From 6bd26f377211aafc174add159bb12258fe0646a7 Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Sun, 21 Apr 2024 20:53:28 +1000 Subject: [PATCH 1/8] Algebra materials moved to core theories (up to monoid) --- examples/algebra/Holmakefile | 2 +- examples/algebra/aat/Holmakefile | 2 +- examples/algebra/aat/aatmonoidScript.sml | 2 +- examples/algebra/field/Holmakefile | 3 +- .../algebra/field/fieldBinomialScript.sml | 22 +- examples/algebra/field/fieldIdealScript.sml | 14 +- .../algebra/field/fieldInstancesScript.sml | 28 +- examples/algebra/field/fieldMapScript.sml | 24 +- examples/algebra/field/fieldOrderScript.sml | 26 +- examples/algebra/field/fieldProductScript.sml | 20 +- examples/algebra/field/fieldRealScript.sml | 6 +- examples/algebra/field/fieldScript.sml | 22 +- examples/algebra/field/fieldUnitScript.sml | 3 - examples/algebra/finitefield/Holmakefile | 3 +- .../algebra/finitefield/ffAdvancedScript.sml | 28 +- .../algebra/finitefield/ffBasicScript.sml | 21 +- .../algebra/finitefield/ffConjugateScript.sml | 28 +- .../algebra/finitefield/ffCycloScript.sml | 30 +- .../algebra/finitefield/ffExistScript.sml | 37 +- .../algebra/finitefield/ffExtendScript.sml | 27 +- .../algebra/finitefield/ffInstancesScript.sml | 31 +- .../algebra/finitefield/ffMasterScript.sml | 40 +- .../algebra/finitefield/ffMinimalScript.sml | 24 +- examples/algebra/finitefield/ffPolyScript.sml | 36 +- .../algebra/finitefield/ffSplitScript.sml | 18 +- .../algebra/finitefield/ffUnityScript.sml | 31 +- examples/algebra/group/Holmakefile | 2 +- examples/algebra/group/congruencesScript.sml | 24 +- examples/algebra/group/corresScript.sml | 14 +- examples/algebra/group/finiteGroupScript.sml | 35 +- examples/algebra/group/groupActionScript.sml | 19 +- examples/algebra/group/groupCyclicScript.sml | 32 +- .../algebra/group/groupInstancesScript.sml | 19 +- examples/algebra/group/groupMapScript.sml | 17 +- examples/algebra/group/groupOrderScript.sml | 20 +- examples/algebra/group/groupProductScript.sml | 15 +- examples/algebra/group/groupRealScript.sml | 7 +- examples/algebra/group/groupScript.sml | 20 +- .../algebra/group/quotientGroupScript.sml | 18 +- examples/algebra/group/subgroupScript.sml | 30 +- examples/algebra/lib/EulerScript.sml | 1226 -- examples/algebra/lib/GaussScript.sml | 2940 --- examples/algebra/lib/MobiusScript.sml | 1112 -- examples/algebra/lib/README.md | 25 - examples/algebra/lib/binomialScript.sml | 1516 -- examples/algebra/lib/files.txt | 89 - examples/algebra/lib/helperFunctionScript.sml | 4570 ----- examples/algebra/lib/helperListScript.sml | 8545 -------- examples/algebra/lib/helperNumScript.sml | 4782 ----- examples/algebra/lib/helperSetScript.sml | 4443 ----- examples/algebra/lib/logPowerScript.sml | 3784 ---- examples/algebra/lib/primePowerScript.sml | 4150 ---- examples/algebra/lib/primesScript.sml | 376 - examples/algebra/lib/sublistScript.sml | 1417 -- examples/algebra/lib/triangleScript.sml | 6622 ------ .../algebra/linear/FiniteVSpaceScript.sml | 14 +- examples/algebra/linear/Holmakefile | 3 +- examples/algebra/linear/LinearIndepScript.sml | 12 +- examples/algebra/linear/SpanSpaceScript.sml | 13 +- examples/algebra/linear/VectorSpaceScript.sml | 13 +- examples/algebra/monoid/Holmakefile | 1 - examples/algebra/monoid/gbagScript.sml | 528 - .../algebra/monoid/monoidInstancesScript.sml | 709 - examples/algebra/monoid/monoidMapScript.sml | 1338 -- examples/algebra/monoid/monoidOrderScript.sml | 1211 -- examples/algebra/monoid/monoidScript.sml | 903 - examples/algebra/monoid/submonoidScript.sml | 673 - examples/algebra/multipoly/Holmakefile | 2 +- .../algebra/multipoly/multipolyScript.sml | 26 +- examples/algebra/polynomial/Holmakefile | 3 +- .../algebra/polynomial/polyBinomialScript.sml | 39 +- .../algebra/polynomial/polyCyclicScript.sml | 27 +- .../polynomial/polyDerivativeScript.sml | 24 +- .../algebra/polynomial/polyDividesScript.sml | 31 +- .../algebra/polynomial/polyDivisionScript.sml | 30 +- .../algebra/polynomial/polyEvalScript.sml | 28 +- .../polynomial/polyFieldDivisionScript.sml | 34 +- .../polynomial/polyFieldModuloScript.sml | 30 +- .../algebra/polynomial/polyFieldScript.sml | 27 +- examples/algebra/polynomial/polyGCDScript.sml | 29 +- .../polynomial/polyIrreducibleScript.sml | 28 +- examples/algebra/polynomial/polyMapScript.sml | 39 +- .../polynomial/polyModuloRingScript.sml | 37 +- .../algebra/polynomial/polyMonicScript.sml | 30 +- .../polynomial/polyMultiplicityScript.sml | 15 +- .../algebra/polynomial/polyProductScript.sml | 28 +- .../polynomial/polyRingModuloScript.sml | 29 +- .../algebra/polynomial/polyRingScript.sml | 32 +- .../algebra/polynomial/polyRootScript.sml | 31 +- .../algebra/polynomial/polyWeakScript.sml | 34 +- .../algebra/polynomial/polynomialScript.sml | 8 +- examples/algebra/ring/Holmakefile | 2 +- .../ring/integralDomainInstancesScript.sml | 22 +- .../algebra/ring/integralDomainScript.sml | 28 +- examples/algebra/ring/quotientRingScript.sml | 20 +- examples/algebra/ring/ringBinomialScript.sml | 21 +- examples/algebra/ring/ringDividesScript.sml | 30 +- examples/algebra/ring/ringIdealScript.sml | 25 +- examples/algebra/ring/ringInstancesScript.sml | 20 +- examples/algebra/ring/ringIntegerScript.sml | 27 +- examples/algebra/ring/ringMapScript.sml | 23 +- examples/algebra/ring/ringRealScript.sml | 12 +- examples/algebra/ring/ringScript.sml | 16 +- examples/algebra/ring/ringUnitScript.sml | 16 +- src/algebra/Holmakefile | 9 + src/algebra/combinatoricsScript.sml | 16640 ++++++++++++++++ .../algebra/lib => src/algebra}/jcLib.sig | 0 .../algebra/lib => src/algebra}/jcLib.sml | 0 src/algebra/monoidScript.sml | 5180 +++++ src/algebra/numberScript.sml | 10245 ++++++++++ src/algebra/primeScript.sml | 11019 ++++++++++ src/bag/bagScript.sml | 19 +- src/bag/primeFactorScript.sml | 10 +- src/list/src/indexedListsScript.sml | 103 +- src/list/src/listRangeScript.sml | 990 +- src/list/src/listScript.sml | 208 +- src/list/src/rich_listScript.sml | 3629 +++- src/num/extra_theories/dividesScript.sml | 628 +- src/num/extra_theories/gcdScript.sml | 1207 +- src/num/extra_theories/logrootScript.sml | 1006 +- src/num/theories/arithmeticScript.sml | 412 +- src/parallel_builds/core/Holmakefile | 3 +- src/pred_set/src/gcdsetScript.sml | 404 +- .../src/more_theories/cardinalScript.sml | 8 +- src/pred_set/src/pred_setScript.sml | 1116 +- src/real/Holmakefile | 2 +- .../real/real_algebraScript.sml | 15 +- 127 files changed, 53027 insertions(+), 52544 deletions(-) delete mode 100644 examples/algebra/lib/EulerScript.sml delete mode 100644 examples/algebra/lib/GaussScript.sml delete mode 100644 examples/algebra/lib/MobiusScript.sml delete mode 100644 examples/algebra/lib/README.md delete mode 100644 examples/algebra/lib/binomialScript.sml delete mode 100644 examples/algebra/lib/files.txt delete mode 100644 examples/algebra/lib/helperFunctionScript.sml delete mode 100644 examples/algebra/lib/helperListScript.sml delete mode 100644 examples/algebra/lib/helperNumScript.sml delete mode 100644 examples/algebra/lib/helperSetScript.sml delete mode 100644 examples/algebra/lib/logPowerScript.sml delete mode 100644 examples/algebra/lib/primePowerScript.sml delete mode 100644 examples/algebra/lib/primesScript.sml delete mode 100644 examples/algebra/lib/sublistScript.sml delete mode 100644 examples/algebra/lib/triangleScript.sml delete mode 100644 examples/algebra/monoid/Holmakefile delete mode 100644 examples/algebra/monoid/gbagScript.sml delete mode 100644 examples/algebra/monoid/monoidInstancesScript.sml delete mode 100644 examples/algebra/monoid/monoidMapScript.sml delete mode 100644 examples/algebra/monoid/monoidOrderScript.sml delete mode 100644 examples/algebra/monoid/monoidScript.sml delete mode 100644 examples/algebra/monoid/submonoidScript.sml create mode 100644 src/algebra/Holmakefile create mode 100644 src/algebra/combinatoricsScript.sml rename {examples/algebra/lib => src/algebra}/jcLib.sig (100%) rename {examples/algebra/lib => src/algebra}/jcLib.sml (100%) create mode 100644 src/algebra/monoidScript.sml create mode 100644 src/algebra/numberScript.sml create mode 100644 src/algebra/primeScript.sml rename examples/algebra/monoid/monoidRealScript.sml => src/real/real_algebraScript.sml (81%) diff --git a/examples/algebra/Holmakefile b/examples/algebra/Holmakefile index 27513b3b48..f48ecd11a2 100644 --- a/examples/algebra/Holmakefile +++ b/examples/algebra/Holmakefile @@ -1,2 +1,2 @@ CLINE_OPTIONS = -r -INCLUDES = lib monoid group ring field polynomial multipoly linear finitefield +INCLUDES = group ring field polynomial multipoly linear finitefield diff --git a/examples/algebra/aat/Holmakefile b/examples/algebra/aat/Holmakefile index 76e0d345d8..30e0860ab8 100644 --- a/examples/algebra/aat/Holmakefile +++ b/examples/algebra/aat/Holmakefile @@ -1 +1 @@ -INCLUDES = ../monoid +INCLUDES = $(HOLDIR)/src/algebra diff --git a/examples/algebra/aat/aatmonoidScript.sml b/examples/algebra/aat/aatmonoidScript.sml index 4eafcd6419..3c9af573b1 100644 --- a/examples/algebra/aat/aatmonoidScript.sml +++ b/examples/algebra/aat/aatmonoidScript.sml @@ -1,6 +1,6 @@ open HolKernel Parse boolLib bossLib; -open monoidTheory monoidOrderTheory transferTheory transferLib +open monoidTheory transferTheory transferLib val _ = new_theory "aatmonoid"; diff --git a/examples/algebra/field/Holmakefile b/examples/algebra/field/Holmakefile index da2c24a48d..592fef6adb 100644 --- a/examples/algebra/field/Holmakefile +++ b/examples/algebra/field/Holmakefile @@ -1,2 +1 @@ -PRE_INCLUDES = ../ring -INCLUDES = ../lib ../monoid ../group +INCLUDES = $(HOLDIR)/src/algebra ../ring ../group diff --git a/examples/algebra/field/fieldBinomialScript.sml b/examples/algebra/field/fieldBinomialScript.sml index c1e558f429..48c6cf6f3e 100644 --- a/examples/algebra/field/fieldBinomialScript.sml +++ b/examples/algebra/field/fieldBinomialScript.sml @@ -12,39 +12,23 @@ val _ = new_theory "fieldBinomial"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* val _ = load "binomialTheory"; *) -open binomialTheory; -open dividesTheory; +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "groupInstancesTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "fieldMapTheory"; *) open fieldTheory; open ringTheory; open groupTheory; open monoidTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupMapTheory ringMapTheory fieldMapTheory; (* val _ = load "ringBinomialTheory"; *) open ringBinomialTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - - (* ------------------------------------------------------------------------- *) (* Field Binomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/field/fieldIdealScript.sml b/examples/algebra/field/fieldIdealScript.sml index 7a9a1bc840..ed31563840 100644 --- a/examples/algebra/field/fieldIdealScript.sml +++ b/examples/algebra/field/fieldIdealScript.sml @@ -12,11 +12,12 @@ val _ = new_theory "fieldIdeal"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; +open pred_setTheory arithmeticTheory gcdsetTheory numberTheory + combinatoricsTheory; + (* Get dependent theories local *) (* (* val _ = load "monoidTheory"; *) *) (* (* val _ = load "groupTheory"; *) *) @@ -33,15 +34,6 @@ open ringIdealTheory quotientRingTheory; (* val _ = load "fieldTheory"; *) open fieldTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory; - - (* ------------------------------------------------------------------------- *) (* Ideals in Field Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/field/fieldInstancesScript.sml b/examples/algebra/field/fieldInstancesScript.sml index 110e11859c..346c765656 100644 --- a/examples/algebra/field/fieldInstancesScript.sml +++ b/examples/algebra/field/fieldInstancesScript.sml @@ -16,37 +16,21 @@ GF(p) -- Galois Field of order prime p. (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; +open pred_setTheory arithmeticTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory; + (* declare new theory at start *) val _ = new_theory "fieldInstances"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* val _ = load "fieldMapTheory"; *) open monoidTheory groupTheory ringTheory fieldTheory; -open groupOrderTheory monoidOrderTheory; - -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; - -(* val _ = load "ringInstancesTheory"; *) -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -open monoidInstancesTheory groupInstancesTheory ringInstancesTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; - +open groupOrderTheory; +open groupMapTheory ringMapTheory fieldMapTheory; +open groupInstancesTheory ringInstancesTheory; (* ------------------------------------------------------------------------- *) (* Field Instances Documentation *) diff --git a/examples/algebra/field/fieldMapScript.sml b/examples/algebra/field/fieldMapScript.sml index d3b426b990..f50686844c 100644 --- a/examples/algebra/field/fieldMapScript.sml +++ b/examples/algebra/field/fieldMapScript.sml @@ -12,17 +12,14 @@ val _ = new_theory "fieldMap"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "fieldOrderTheory"; *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory gcdsetTheory + numberTheory combinatoricsTheory; + open monoidTheory groupTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open ringTheory ringUnitTheory integralDomainTheory; open fieldTheory fieldOrderTheory; @@ -30,20 +27,9 @@ open fieldTheory fieldOrderTheory; open ringDividesTheory; (* val _ = load "ringMapTheory"; *) -open monoidMapTheory groupMapTheory ringMapTheory; +open groupMapTheory ringMapTheory; open quotientGroupTheory subgroupTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Field Maps Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/field/fieldOrderScript.sml b/examples/algebra/field/fieldOrderScript.sml index 450dd5f0e7..95c691e070 100644 --- a/examples/algebra/field/fieldOrderScript.sml +++ b/examples/algebra/field/fieldOrderScript.sml @@ -12,40 +12,20 @@ val _ = new_theory "fieldOrder"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + gcdsetTheory numberTheory combinatoricsTheory primeTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "groupInstancesTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "fieldTheory"; *) open fieldTheory; open integralDomainTheory; open ringTheory; open groupTheory; open monoidTheory; -(* val _ = load "groupOrderTheory"; *) -open monoidOrderTheory groupOrderTheory; -open subgroupTheory; - -(* val _ = load "groupCyclicTheory"; *) -open groupCyclicTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; -open dividesTheory gcdTheory; - -open GaussTheory; - +open groupOrderTheory subgroupTheory groupCyclicTheory; (* ------------------------------------------------------------------------- *) (* Order of Elements in a Field Documentation *) diff --git a/examples/algebra/field/fieldProductScript.sml b/examples/algebra/field/fieldProductScript.sml index 6c43741c1d..feef3a4dfa 100644 --- a/examples/algebra/field/fieldProductScript.sml +++ b/examples/algebra/field/fieldProductScript.sml @@ -12,23 +12,13 @@ val _ = new_theory "fieldProduct"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* val _ = load "binomialTheory"; *) -open binomialTheory; -open dividesTheory; +open pred_setTheory listTheory arithmeticTheory dividesTheory numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "groupInstancesTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "fieldTheory"; *) open fieldTheory; open ringTheory; open groupTheory; @@ -38,12 +28,6 @@ open monoidTheory; open groupProductTheory; open subgroupTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - - (* ------------------------------------------------------------------------- *) (* Product of a set of Field elements Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/field/fieldRealScript.sml b/examples/algebra/field/fieldRealScript.sml index 31485a9768..bba6629842 100644 --- a/examples/algebra/field/fieldRealScript.sml +++ b/examples/algebra/field/fieldRealScript.sml @@ -1,8 +1,10 @@ (* ------------------------------------------------------------------------- *) (* The field of reals. *) (* ------------------------------------------------------------------------- *) -open HolKernel boolLib bossLib Parse - groupTheory fieldTheory ringRealTheory groupRealTheory + +open HolKernel boolLib bossLib Parse; + +open groupTheory fieldTheory ringRealTheory groupRealTheory; val _ = new_theory"fieldReal"; diff --git a/examples/algebra/field/fieldScript.sml b/examples/algebra/field/fieldScript.sml index 2c2a03b244..2ef9eb87a4 100644 --- a/examples/algebra/field/fieldScript.sml +++ b/examples/algebra/field/fieldScript.sml @@ -23,33 +23,19 @@ val _ = new_theory "field"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "integralDomainTheory"; *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory gcdsetTheory + numberTheory combinatoricsTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory integralDomainTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; (* for field subgroups *) (* val _ = load "ringDividesTheory"; *) open ringDividesTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Field Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/field/fieldUnitScript.sml b/examples/algebra/field/fieldUnitScript.sml index 945dec1299..1d12c81dff 100644 --- a/examples/algebra/field/fieldUnitScript.sml +++ b/examples/algebra/field/fieldUnitScript.sml @@ -12,8 +12,6 @@ val _ = new_theory "fieldUnit"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; @@ -26,7 +24,6 @@ open pred_setTheory; open ringTheory fieldTheory; open ringDividesTheory ringUnitTheory; - (* ------------------------------------------------------------------------- *) (* Field Units Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/Holmakefile b/examples/algebra/finitefield/Holmakefile index b3cc85a87c..96a6d042c2 100644 --- a/examples/algebra/finitefield/Holmakefile +++ b/examples/algebra/finitefield/Holmakefile @@ -1,2 +1 @@ -PRE_INCLUDES = ../ring -INCLUDES = ../lib ../monoid ../group ../field ../polynomial ../linear +INCLUDES = ../ring ../group ../field ../polynomial ../linear diff --git a/examples/algebra/finitefield/ffAdvancedScript.sml b/examples/algebra/finitefield/ffAdvancedScript.sml index 92da61334c..5cce473286 100644 --- a/examples/algebra/finitefield/ffAdvancedScript.sml +++ b/examples/algebra/finitefield/ffAdvancedScript.sml @@ -12,11 +12,12 @@ val _ = new_theory "ffAdvanced"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) +(* open dependent theories *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory primeTheory; (* val _ = load "ffBasicTheory"; *) open ffBasicTheory; @@ -24,22 +25,6 @@ open ffBasicTheory; (* val _ = load "FiniteVSpaceTheory"; *) open VectorSpaceTheory FiniteVSpaceTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) -(* (* val _ = load "fieldTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; open groupInstancesTheory ringInstancesTheory; @@ -57,7 +42,7 @@ open polyModuloRingTheory; open polyFieldModuloTheory; open polyIrreducibleTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupMapTheory ringMapTheory fieldMapTheory; open ringDividesTheory; open ringIdealTheory; open ringUnitTheory; @@ -65,14 +50,9 @@ open subgroupTheory; open quotientGroupTheory; open groupCyclicTheory; -open monoidOrderTheory; open groupOrderTheory; open fieldOrderTheory; -(* val _ = load "logPowerTheory"; *) -open logPowerTheory; (* for perfect_power *) - - (* ------------------------------------------------------------------------- *) (* Finite Field Advanced Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffBasicScript.sml b/examples/algebra/finitefield/ffBasicScript.sml index 208ad1cefb..f94b65896b 100644 --- a/examples/algebra/finitefield/ffBasicScript.sml +++ b/examples/algebra/finitefield/ffBasicScript.sml @@ -12,28 +12,18 @@ val _ = new_theory "ffBasic"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* val _ = load "polyFieldModuloTheory"; *) -open polyFieldModuloTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; +open pred_setTheory arithmeticTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; +open polyFieldModuloTheory; (* (* val _ = load "fieldTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupOrderTheory; +open groupMapTheory ringMapTheory fieldMapTheory; (* val _ = load "fieldInstancesTheory"; *) open groupInstancesTheory ringInstancesTheory; @@ -56,7 +46,6 @@ val _ = overload_on ("**", ``(PolyRing r).prod.exp``); Therefore, keep this file clean by not loading any polynomials. *) - (* ------------------------------------------------------------------------- *) (* Finite Field Basic Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffConjugateScript.sml b/examples/algebra/finitefield/ffConjugateScript.sml index b9416c2d6e..d5a44c82a6 100644 --- a/examples/algebra/finitefield/ffConjugateScript.sml +++ b/examples/algebra/finitefield/ffConjugateScript.sml @@ -12,11 +12,12 @@ val _ = new_theory "ffConjugate"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open arithmeticTheory pred_setTheory listTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory gcdsetTheory primeTheory; (* Get dependent theories local *) (* val _ = load "ffMinimalTheory"; *) @@ -28,25 +29,8 @@ open ffUnityTheory; open ffMasterTheory; open ffMinimalTheory; -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -(* (* val _ = load "helperListTheory"; -- in polyRingTheory *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; @@ -77,10 +61,6 @@ open ringDividesTheory; open ringIdealTheory; open ringUnitTheory; -open binomialTheory; -open GaussTheory; - - (* ------------------------------------------------------------------------- *) (* Finite Field Element Conjugates Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffCycloScript.sml b/examples/algebra/finitefield/ffCycloScript.sml index 858097ce94..8f42d3616d 100644 --- a/examples/algebra/finitefield/ffCycloScript.sml +++ b/examples/algebra/finitefield/ffCycloScript.sml @@ -12,41 +12,24 @@ val _ = new_theory "ffCyclo"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open arithmeticTheory pred_setTheory listTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory; -(* Loading theories *) -(* val _ = load "ffPolyTheory"; *) open ffBasicTheory; open ffAdvancedTheory; open ffPolyTheory; -(* Open theories in order *) - -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; -open dividesTheory gcdTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; - -(* Get dependent theories local *) -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open fieldOrderTheory; (* Get polynomial theory of Ring *) -open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory polyBinomialTheory; +open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory + polyBinomialTheory; (* (* val _ = load "polyFieldModuloTheory"; *) *) open polyFieldTheory; @@ -61,7 +44,6 @@ open polyMonicTheory; open polyProductTheory; open polyGCDTheory; - (* ------------------------------------------------------------------------- *) (* Finite Field Cyclotomic Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffExistScript.sml b/examples/algebra/finitefield/ffExistScript.sml index ebb5e63f04..45bf923e68 100644 --- a/examples/algebra/finitefield/ffExistScript.sml +++ b/examples/algebra/finitefield/ffExistScript.sml @@ -12,14 +12,13 @@ val _ = new_theory "ffExist"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open arithmeticTheory pred_setTheory listTheory numberTheory dividesTheory + combinatoricsTheory gcdTheory gcdsetTheory primeTheory cardinalTheory; -(* Get dependent theories local *) -(* val _ = load "ffConjugateTheory"; *) open ffBasicTheory; open ffAdvancedTheory; open ffPolyTheory; @@ -29,25 +28,8 @@ open ffMinimalTheory; open ffMasterTheory; open ffConjugateTheory; -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -(* (* val _ = load "helperListTheory"; -- in polyRingTheory *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory fieldOrderTheory; +open groupOrderTheory fieldOrderTheory; open subgroupTheory; open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; open groupCyclicTheory; @@ -68,7 +50,7 @@ open polyRingModuloTheory; open polyModuloRingTheory; open polyMapTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupMapTheory ringMapTheory fieldMapTheory; (* (* val _ = load "polyGCDTheory"; *) *) open polyGCDTheory; @@ -84,13 +66,6 @@ open ringUnitTheory; (* (* val _ = load "fieldBinomialTheory"; *) *) open fieldBinomialTheory; -(* val _ = load "MobiusTheory"; *) -open MobiusTheory; (* for sigma_eq_perfect_power_bounds_2 *) - -(* val _ = load "cardinalTheory"; *) -open cardinalTheory; (* for helpers: A_LIST_BIJ_A *) - - (* ------------------------------------------------------------------------- *) (* Finite Field Existence and Uniqueness Documentation *) (* ------------------------------------------------------------------------- *) @@ -2570,7 +2545,7 @@ val monoid_bij_image_group = store_thm( rpt strip_tac >> rw_tac std_ss[Group_def] >- rw[monoid_bij_image_monoid] >> - rw[monoidOrderTheory.monoid_invertibles_def, monoid_bij_image_def, EXTENSION, EQ_IMP_THM] >> + rw[monoid_invertibles_def, monoid_bij_image_def, EXTENSION, EQ_IMP_THM] >> `g.inv x' IN G` by rw[] >> qexists_tac `f (g.inv x')` >> metis_tac[group_inv_thm]); diff --git a/examples/algebra/finitefield/ffExtendScript.sml b/examples/algebra/finitefield/ffExtendScript.sml index 2fac55ad86..7b6634528e 100644 --- a/examples/algebra/finitefield/ffExtendScript.sml +++ b/examples/algebra/finitefield/ffExtendScript.sml @@ -12,12 +12,12 @@ val _ = new_theory "ffExtend"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + gcdsetTheory numberTheory combinatoricsTheory cardinalTheory; (* Get dependent theories local *) (* val _ = load "ffExistTheory"; *) @@ -28,24 +28,11 @@ open ffMinimalTheory; open ffConjugateTheory; open ffExistTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperListTheory helperSetTheory; - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "fieldTheory"; *) *) -(* (* val _ = load "ringUnitTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; open subgroupTheory; open ringUnitTheory; open groupOrderTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupMapTheory ringMapTheory fieldMapTheory; (* (* val _ = load "ringBinomialTheory"; *) *) open ringBinomialTheory; @@ -53,8 +40,6 @@ open ringBinomialTheory; (* (* val _ = load "groupCyclicTheory"; *) *) open groupCyclicTheory; -(* (* val _ = load "fieldInstancesTheory"; *) *) -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; @@ -79,10 +64,6 @@ open polyRingModuloTheory; open polyModuloRingTheory; open polyMapTheory; -(* val _ = load "cardinalTheory"; *) -open cardinalTheory; (* for helpers: INJ_FINITE_INFINITE *) - - (* ------------------------------------------------------------------------- *) (* Field Extension Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffInstancesScript.sml b/examples/algebra/finitefield/ffInstancesScript.sml index 2bded74244..cce8b786bf 100644 --- a/examples/algebra/finitefield/ffInstancesScript.sml +++ b/examples/algebra/finitefield/ffInstancesScript.sml @@ -12,43 +12,23 @@ val _ = new_theory "ffInstances"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; +(* open dependent theories *) +open pred_setTheory arithmeticTheory listTheory numberTheory dividesTheory + gcdTheory; + (* Get dependent theories local *) (* val _ = load "ffBasicTheory"; *) open ffBasicTheory; -(* open dependent theories *) -open pred_setTheory arithmeticTheory listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyDivisionTheory"; *) *) -(* (* val _ = load "polyBinomialTheory"; *) *) open polynomialTheory polyWeakTheory polyRingTheory; -(* (* val _ = load "polyFieldTheory"; *) *) -(* (* val _ = load "polyFieldDivisionTheory"; -- has polyDivisionTheory *) *) -(* (* val _ = load "polyFieldModuloTheory"; *) *) open polyFieldTheory polyDivisionTheory polyFieldDivisionTheory; open polyModuloRingTheory polyFieldModuloTheory; @@ -64,7 +44,6 @@ open ringUnitTheory; open subgroupTheory; open quotientGroupTheory; - (* ------------------------------------------------------------------------- *) (* Finite Field Instances Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffMasterScript.sml b/examples/algebra/finitefield/ffMasterScript.sml index de9c2fb55f..1b4eef494c 100644 --- a/examples/algebra/finitefield/ffMasterScript.sml +++ b/examples/algebra/finitefield/ffMasterScript.sml @@ -12,41 +12,19 @@ val _ = new_theory "ffMaster"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open arithmeticTheory pred_setTheory listTheory dividesTheory gcdTheory + gcdsetTheory numberTheory combinatoricsTheory primeTheory; -(* Loading theories *) -(* (* val _ = load "ffCycloTheory"; *) *) -(* val _ = load "ffPolyTheory"; *) open ffBasicTheory; open ffAdvancedTheory; open ffPolyTheory; -(* Open theories in order *) - -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -(* (* val _ = load "helperListTheory"; -- in polyRingTheory *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* Get dependent theories local *) -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; @@ -71,24 +49,16 @@ open polyDividesTheory; open polyGCDTheory; open polyIrreducibleTheory; -(* (* val _ = load "polyMapTheory"; *) *) -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupMapTheory ringMapTheory fieldMapTheory; open polyMapTheory; open polyDerivativeTheory; open polyEvalTheory; open polyRootTheory; -open binomialTheory; - -open GaussTheory; -open EulerTheory; (* val _ = load "fieldBinomialTheory"; *) open fieldBinomialTheory; (* for finite_field_freshman_all *) -(* (* val _ = load "MobiusTheory"; *) *) - - (* ------------------------------------------------------------------------- *) (* Finite Field Master Polynomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffMinimalScript.sml b/examples/algebra/finitefield/ffMinimalScript.sml index 0eae975908..278407036a 100644 --- a/examples/algebra/finitefield/ffMinimalScript.sml +++ b/examples/algebra/finitefield/ffMinimalScript.sml @@ -12,12 +12,11 @@ val _ = new_theory "ffMinimal"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open arithmeticTheory pred_setTheory listTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory gcdsetTheory; (* Get dependent theories local *) (* val _ = load "ffUnityTheory"; *) @@ -31,27 +30,11 @@ open SpanSpaceTheory; open LinearIndepTheory; open FiniteVSpaceTheory; -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory fieldOrderTheory; +open groupOrderTheory fieldOrderTheory; open subgroupTheory; open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyFieldModuloTheory"; *) *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory polyBinomialTheory; open polyMonicTheory polyEvalTheory; open polyDividesTheory; @@ -70,7 +53,6 @@ open ringDividesTheory; open ringIdealTheory; open ringUnitTheory; - (* ------------------------------------------------------------------------- *) (* Finite Field Minimal Polynomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffPolyScript.sml b/examples/algebra/finitefield/ffPolyScript.sml index 8127a4eec5..4253e5a90f 100644 --- a/examples/algebra/finitefield/ffPolyScript.sml +++ b/examples/algebra/finitefield/ffPolyScript.sml @@ -12,52 +12,27 @@ val _ = new_theory "ffPoly"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) -(* val _ = load "ffAdvancedTheory"; *) -open ffAdvancedTheory ffBasicTheory; - (* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; +open arithmeticTheory pred_setTheory listTheory numberTheory dividesTheory + gcdTheory combinatoricsTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; +open ffAdvancedTheory ffBasicTheory; -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; open fieldOrderTheory; open groupCyclicTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyDivisionTheory"; *) *) -(* (* val _ = load "polyBinomialTheory"; *) *) -(* (* val _ = load "polyMapTheory"; *) *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory polyBinomialTheory; (* (* val _ = load "polyEvalTheory"; *) *) open polyMonicTheory polyEvalTheory; -(* (* val _ = load "polyFieldTheory"; *) *) -(* (* val _ = load "polyFieldDivisionTheory"; *) *) -(* (* val _ = load "polyFieldModuloTheory"; *) *) open polyFieldTheory; open polyFieldDivisionTheory; open polyFieldModuloTheory; @@ -79,8 +54,7 @@ open polyIrreducibleTheory; open polyProductTheory; open polyMultiplicityTheory; open polyMapTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; - +open groupMapTheory ringMapTheory fieldMapTheory; (* ------------------------------------------------------------------------- *) (* Finite Field Polynomials of Subfield Documentation *) diff --git a/examples/algebra/finitefield/ffSplitScript.sml b/examples/algebra/finitefield/ffSplitScript.sml index 9dd759e1f7..db77ca613e 100644 --- a/examples/algebra/finitefield/ffSplitScript.sml +++ b/examples/algebra/finitefield/ffSplitScript.sml @@ -12,10 +12,11 @@ val _ = new_theory "ffSplit"; (* ------------------------------------------------------------------------- *) - - open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory numberTheory + combinatoricsTheory dividesTheory gcdTheory gcdsetTheory primeTheory; (* Get dependent theories local *) open ffBasicTheory; @@ -29,17 +30,9 @@ open ffUnityTheory; open ffExistTheory; open ffExtendTheory; -open bagTheory; (* also has MEMBER_NOT_EMPTY *) - -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; - -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -open dividesTheory gcdTheory; +open bagTheory; open monoidTheory groupTheory ringTheory fieldTheory; -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; @@ -71,9 +64,6 @@ open polyMapTheory; open polyProductTheory; (* for PPROD *) -open GaussTheory; (* for divisors *) - - (* ------------------------------------------------------------------------- *) (* Splitting Field Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/finitefield/ffUnityScript.sml b/examples/algebra/finitefield/ffUnityScript.sml index 1acf6a7ca0..6d1c95e391 100644 --- a/examples/algebra/finitefield/ffUnityScript.sml +++ b/examples/algebra/finitefield/ffUnityScript.sml @@ -12,38 +12,20 @@ val _ = new_theory "ffUnity"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open arithmeticTheory pred_setTheory listTheory dividesTheory gcdTheory + gcdsetTheory numberTheory combinatoricsTheory primeTheory; -(* Loading theories *) -(* val _ = load "ffCycloTheory"; *) -(* val _ = load "ffMasterTheory"; *) open ffPolyTheory ffAdvancedTheory ffBasicTheory; open ffCycloTheory; open ffMasterTheory; (* Open theories in order *) - -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; -open dividesTheory gcdTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* Get dependent theories local *) -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) -(* (* val _ = load "fieldOrderTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; @@ -78,14 +60,9 @@ open polyProductTheory; open polyIrreducibleTheory; open polyGCDTheory; -(* (* val _ = load "GaussTheory"; *) *) -open binomialTheory; -open GaussTheory; - (* val _ = load "polyCyclicTheory"; *) open polyCyclicTheory; (* for poly_unity_irreducible_factor_exists *) - (* ------------------------------------------------------------------------- *) (* Finite Field Unity Polynomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/group/Holmakefile b/examples/algebra/group/Holmakefile index 785f364c63..30e0860ab8 100644 --- a/examples/algebra/group/Holmakefile +++ b/examples/algebra/group/Holmakefile @@ -1 +1 @@ -INCLUDES = ../lib ../monoid +INCLUDES = $(HOLDIR)/src/algebra diff --git a/examples/algebra/group/congruencesScript.sml b/examples/algebra/group/congruencesScript.sml index 8c06fcf175..18363e03f4 100644 --- a/examples/algebra/group/congruencesScript.sml +++ b/examples/algebra/group/congruencesScript.sml @@ -19,33 +19,13 @@ val _ = new_theory "congruences"; For mult_mod p, show that MOD_MULT_INV can be evaluted by Fermat's Little Theorem. *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get required theories *) -(* (* val _ = load "groupTheory"; *) *) -(* val _ = load "subgroupTheory"; *) -(* val _ = load "groupInstancesTheory"; *) -open groupTheory subgroupTheory groupInstancesTheory; - -(* val _ = load "groupProductTheory"; *) -open groupProductTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory via groupTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory via groupTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "EulerTheory"; *) *) -open EulerTheory; - (* open dependent theories *) -open arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -open dividesTheory; +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory; +open groupTheory subgroupTheory groupInstancesTheory groupProductTheory; (* ------------------------------------------------------------------------- *) (* Congruences Documentation *) diff --git a/examples/algebra/group/corresScript.sml b/examples/algebra/group/corresScript.sml index 0a17720d35..2c79cd5293 100644 --- a/examples/algebra/group/corresScript.sml +++ b/examples/algebra/group/corresScript.sml @@ -7,25 +7,15 @@ (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; -(* declare new theory at start *) -val _ = new_theory "corres"; - (* ------------------------------------------------------------------------- *) - - -(* val _ = load "quotientGroupTheory"; *) - -(* open HolKernel Parse boolLib bossLib; *) - -open pred_setTheory arithmeticTheory helperSetTheory; +open pred_setTheory arithmeticTheory numberTheory combinatoricsTheory; open groupTheory subgroupTheory; open quotientGroupTheory groupMapTheory; -(* val _ = new_theory "corres"; *) - +val _ = new_theory "corres"; (* ------------------------------------------------------------------------- *) (* Group Correspondence Documentation *) diff --git a/examples/algebra/group/finiteGroupScript.sml b/examples/algebra/group/finiteGroupScript.sml index e9ef6162b1..7e34686268 100644 --- a/examples/algebra/group/finiteGroupScript.sml +++ b/examples/algebra/group/finiteGroupScript.sml @@ -12,41 +12,20 @@ val _ = new_theory "finiteGroup"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory arithmeticTheory; +open pred_setTheory arithmeticTheory dividesTheory numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* val _ = load "groupOrderTheory"; *) -open groupTheory monoidTheory; -open groupOrderTheory monoidOrderTheory; +open groupTheory monoidTheory groupOrderTheory; -(* val _ = load "subgroupTheory"; *) -open submonoidTheory; open subgroupTheory; (* val _ = load "groupProductTheory"; *) open groupProductTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; loaded by monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; loaded by monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* val _ = load "helperListTheory"; *) -open helperListTheory; - -(* val _ = load "helperFunctionTheory"; *) -open helperFunctionTheory; - -(* Load dependent theories *) -(* val _ = load "Satisfysimps"; *) - - (* ------------------------------------------------------------------------- *) (* Finite Group Theory Documentation *) (* ------------------------------------------------------------------------- *) @@ -805,7 +784,7 @@ val subset_cross_to_preimage_cross_bij = store_thm( Note s1 SUBSET G /\ s2 SUBSET G by subgroup_carrier_subset so FINITE s1 /\ FINITE s2 by SUBSET_FINITE, FINITE G ==> FINITE (s1 INTER s2) by FINITE_INTER - Thus CARD t = CARD (s1 INTER s2) by FINITE_BIJ_PROPERTY + Thus CARD t = CARD (s1 INTER s2) by FINITE_BIJ *) val subset_cross_partition_property = store_thm( "subset_cross_partition_property", @@ -820,7 +799,7 @@ val subset_cross_partition_property = store_thm( `?m. BIJ m (s1 INTER s2) t` by metis_tac[subset_cross_to_preimage_cross_bij] >> `FINITE s1 /\ FINITE s2` by metis_tac[subgroup_carrier_subset, SUBSET_FINITE] >> `FINITE (s1 INTER s2)` by rw[] >> - metis_tac[FINITE_BIJ_PROPERTY]); + metis_tac[FINITE_BIJ]); (* Theorem: h1 <= g /\ h2 <= g /\ FINITE G ==> let (s1 = h1.carrier) in let (s2 = h2.carrier) in let (f = (\(x, y). x * y)) in @@ -831,7 +810,7 @@ val subset_cross_partition_property = store_thm( Note s1 SUBSET G /\ s2 SUBSET G by subgroup_carrier_subset so FINITE s1 /\ FINITE s2 by SUBSET_FINITE, FINITE G ==> FINITE (s1 INTER s2) by FINITE_INTER - Thus CARD (preimage f s z) = CARD (s1 INTER s2) by FINITE_BIJ_PROPERTY + Thus CARD (preimage f s z) = CARD (s1 INTER s2) by FINITE_BIJ *) val subset_cross_element_preimage_card = store_thm( "subset_cross_element_preimage_card", @@ -839,7 +818,7 @@ val subset_cross_element_preimage_card = store_thm( let (s1 = h1.carrier) in let (s2 = h2.carrier) in let (f = (\(x, y). x * y)) in !z. z IN (s1 o s2) ==> (CARD (preimage f (s1 CROSS s2) z) = CARD (s1 INTER s2))``, metis_tac[subset_cross_to_preimage_cross_bij, subgroup_carrier_subset, - SUBSET_FINITE, FINITE_INTER, FINITE_BIJ_PROPERTY]); + SUBSET_FINITE, FINITE_INTER, FINITE_BIJ]); (* Theorem: INJ (preimage (\(x, y). x * y) (s1 CROSS s2)) (s1 o s2) univ(:('a # 'a -> bool)) *) (* Proof: diff --git a/examples/algebra/group/groupActionScript.sml b/examples/algebra/group/groupActionScript.sml index a286caa2ff..6510b8dae6 100644 --- a/examples/algebra/group/groupActionScript.sml +++ b/examples/algebra/group/groupActionScript.sml @@ -12,30 +12,15 @@ val _ = new_theory "groupAction"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) -(* val _ = load "groupOrderTheory"; *) open monoidTheory groupTheory; open subgroupTheory groupOrderTheory; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - +open pred_setTheory listTheory arithmeticTheory numberTheory dividesTheory + gcdTheory combinatoricsTheory; (*===========================================================================*) diff --git a/examples/algebra/group/groupCyclicScript.sml b/examples/algebra/group/groupCyclicScript.sml index 7ad1115339..fd06f95bac 100644 --- a/examples/algebra/group/groupCyclicScript.sml +++ b/examples/algebra/group/groupCyclicScript.sml @@ -12,41 +12,17 @@ val _ = new_theory "groupCyclic"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "subgroupTheory"; *) *) -(* val _ = load "groupOrderTheory"; *) -open monoidTheory monoidOrderTheory; +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory gcdsetTheory primeTheory; + +open monoidTheory; open groupTheory subgroupTheory groupOrderTheory; open groupMapTheory; - -(* val _ = load "groupInstancesTheory"; *) open groupInstancesTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* val _ = load "helperFunctionTheory"; *) -open helperFunctionTheory; - -(* val _ = load "GaussTheory"; *) -open GaussTheory; -open EulerTheory; - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Cyclic Group Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/group/groupInstancesScript.sml b/examples/algebra/group/groupInstancesScript.sml index 4a28f43b22..a44bd9ddb2 100644 --- a/examples/algebra/group/groupInstancesScript.sml +++ b/examples/algebra/group/groupInstancesScript.sml @@ -24,27 +24,16 @@ val _ = new_theory "groupInstances"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* val _ = load "groupOrderTheory"; (* loads monoidTheory implicitly *) *) -open monoidTheory groupTheory groupOrderTheory; -open subgroupTheory; - -(* val _ = load "groupProductTheory"; *) -open groupProductTheory; - -open helperNumTheory helperSetTheory helperFunctionTheory; - (* open dependent theories *) -open prim_recTheory pred_setTheory arithmeticTheory dividesTheory gcdTheory; +open prim_recTheory pred_setTheory arithmeticTheory dividesTheory gcdTheory + numberTheory primeTheory; -(* val _ = load "GaussTheory"; *) -open EulerTheory GaussTheory; (* for residue *) +open monoidTheory groupTheory groupOrderTheory subgroupTheory; +open groupProductTheory; (* ------------------------------------------------------------------------- *) (* Group Instances Documentation *) diff --git a/examples/algebra/group/groupMapScript.sml b/examples/algebra/group/groupMapScript.sml index d89c2be7a1..f7b661ca0d 100644 --- a/examples/algebra/group/groupMapScript.sml +++ b/examples/algebra/group/groupMapScript.sml @@ -12,25 +12,16 @@ val _ = new_theory "groupMap"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* val _ = load "monoidMapTheory"; *) -open monoidTheory monoidOrderTheory monoidMapTheory; - -(* val _ = load "groupTheory"; *) -open groupTheory; - (* open dependent theories *) -open pred_setTheory arithmeticTheory; +open pred_setTheory arithmeticTheory gcdsetTheory numberTheory combinatoricsTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; +open monoidTheory; +(* val _ = load "groupTheory"; *) +open groupTheory; (* ------------------------------------------------------------------------- *) (* Group Maps Documentation *) diff --git a/examples/algebra/group/groupOrderScript.sml b/examples/algebra/group/groupOrderScript.sml index 834ee6092e..9c548c024c 100644 --- a/examples/algebra/group/groupOrderScript.sml +++ b/examples/algebra/group/groupOrderScript.sml @@ -12,29 +12,17 @@ val _ = new_theory "groupOrder"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory; -(* Get dependent theories local *) -(* val _ = load "monoidOrderTheory"; *) -open monoidTheory monoidOrderTheory; +open monoidTheory; -(* (* val _ = load "groupTheory"; *) *) -(* val _ = load "subgroupTheory"; *) open groupTheory groupMapTheory subgroupTheory; - (* ------------------------------------------------------------------------- *) (* Finite Group Order Documentation *) (* ------------------------------------------------------------------------- *) @@ -387,7 +375,7 @@ local val gim = group_is_monoid |> SPEC_ALL |> UNDISCH in fun lift_monoid_order_thm suffix = let - val mth = DB.fetch "monoidOrder" ("monoid_order_" ^ suffix) + val mth = DB.fetch "monoid" ("monoid_order_" ^ suffix) val mth' = mth |> SPEC_ALL in save_thm("group_order_" ^ suffix, gim |> MP mth' |> DISCH_ALL |> GEN_ALL) diff --git a/examples/algebra/group/groupProductScript.sml b/examples/algebra/group/groupProductScript.sml index 5d922ad708..782e576220 100644 --- a/examples/algebra/group/groupProductScript.sml +++ b/examples/algebra/group/groupProductScript.sml @@ -12,31 +12,18 @@ val _ = new_theory "groupProduct"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory arithmeticTheory; +open pred_setTheory arithmeticTheory numberTheory combinatoricsTheory; (* Get dependent theories local *) (* val _ = load "groupTheory"; *) open groupTheory monoidTheory; -open monoidOrderTheory; -(* val _ = load "subgroupTheory"; *) -open submonoidTheory; open subgroupTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperSetTheory"; loaded by monoidTheory *) *) -open helperSetTheory; - -(* Load dependent theories *) -(* val _ = load "Satisfysimps"; *) -(* used in coset_id_eq_subgroup: srw_tac[SatisfySimps.SATISFY_ss] *) - - (* ------------------------------------------------------------------------- *) (* Iterated Product Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/group/groupRealScript.sml b/examples/algebra/group/groupRealScript.sml index 4c6a4c32e1..b4462789ce 100644 --- a/examples/algebra/group/groupRealScript.sml +++ b/examples/algebra/group/groupRealScript.sml @@ -1,8 +1,9 @@ (* ------------------------------------------------------------------------- *) -(* The groups of addition and multiplication of real numbers. *) +(* The groups of addition and multiplication of real numbers. *) (* ------------------------------------------------------------------------- *) -open HolKernel boolLib bossLib Parse pred_setTheory - groupTheory monoidTheory monoidOrderTheory monoidRealTheory +open HolKernel boolLib bossLib Parse; + +open pred_setTheory groupTheory monoidTheory real_algebraTheory; val _ = new_theory"groupReal"; diff --git a/examples/algebra/group/groupScript.sml b/examples/algebra/group/groupScript.sml index c629111d7b..a39d880b92 100644 --- a/examples/algebra/group/groupScript.sml +++ b/examples/algebra/group/groupScript.sml @@ -25,22 +25,13 @@ val _ = new_theory "group"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory arithmeticTheory; - -(* Get dependent theories local *) -(* val _ = load "monoidOrderTheory"; *) -open monoidTheory monoidOrderTheory; (* for G*, monoid_invertibles_is_monoid *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; +open pred_setTheory arithmeticTheory numberTheory combinatoricsTheory; +open monoidTheory; (* for G*, monoid_invertibles_is_monoid *) (* ------------------------------------------------------------------------- *) (* Group Documentation *) @@ -205,7 +196,12 @@ val _ = Hol_datatype` *) val _ = type_abbrev ("group", Type `:'a monoid`); -(* Define Group by Monoid *) +(* Define Group by Monoid + + NOTE: +val _ = overload_on ("G", ``g.carrier``); +val _ = overload_on ("G*", ``monoid_invertibles g``); + *) val Group_def = Define` Group (g:'a group) <=> Monoid g /\ (G* = G) diff --git a/examples/algebra/group/quotientGroupScript.sml b/examples/algebra/group/quotientGroupScript.sml index 20d1953cc3..49651ea3d3 100644 --- a/examples/algebra/group/quotientGroupScript.sml +++ b/examples/algebra/group/quotientGroupScript.sml @@ -29,25 +29,13 @@ val _ = new_theory "quotientGroup"; open jcLib; (* open dependent theories *) -open pred_setTheory; +open pred_setTheory numberTheory combinatoricsTheory; -(* -(* val _ = load "helperFunctionTheory"; *) -open helperFunctionTheory; -*) - -(* Get dependent theories local *) -(* val _ = load "monoidOrderTheory"; *) -open monoidTheory monoidOrderTheory; +open monoidTheory; (* val _ = load "subgroupTheory"; *) open groupTheory subgroupTheory; -open monoidMapTheory groupMapTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperSetTheory"; loaded by monoidTheory *) *) -open helperSetTheory; - +open groupMapTheory; (* ------------------------------------------------------------------------- *) (* Quotient Group Documentation *) diff --git a/examples/algebra/group/subgroupScript.sml b/examples/algebra/group/subgroupScript.sml index 77148eaf2b..e38ca9ab8e 100644 --- a/examples/algebra/group/subgroupScript.sml +++ b/examples/algebra/group/subgroupScript.sml @@ -21,30 +21,14 @@ val _ = new_theory "subgroup"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory arithmeticTheory; +open pred_setTheory arithmeticTheory numberTheory combinatoricsTheory; -(* Get dependent theories local *) -(* val _ = load "groupMapTheory"; *) open groupMapTheory; open groupTheory monoidTheory; -open monoidOrderTheory; - -(* val _ = load "submonoidTheory"; *) -open submonoidTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperSetTheory"; loaded by monoidTheory *) *) -open helperSetTheory; - -(* Load dependent theories *) -(* val _ = load "Satisfysimps"; *) -(* used in coset_id_eq_subgroup: srw_tac[SatisfySimps.SATISFY_ss] *) - (* ------------------------------------------------------------------------- *) (* Subgroup Documentation *) @@ -1558,14 +1542,14 @@ val subgroup_big_intersect_subset = store_thm( Since (sgbINTER g).op x y IN (sgbINTER g).carrier by subgroup_big_intersect_op_element and (sgbINTER g).op y z IN (sgbINTER g).carrier by subgroup_big_intersect_op_element So this is to show: (x * y) * z = x * (y * z) by subgroup_big_intersect_property - Since x IN G, y IN G and z IN G by IN_SUBSET + Since x IN G, y IN G and z IN G by SUBSET_DEF This follows by group_assoc. (3) (sgbINTER g).id IN (sgbINTER g).carrier This is true by subgroup_big_intersect_has_id. (4) x IN (sgbINTER g).carrier ==> (sgbINTER g).op (sgbINTER g).id x = x Since (sgbINTER g).id IN (sgbINTER g).carrier by subgroup_big_intersect_op_element and (sgbINTER g).id = #e by subgroup_big_intersect_property - also x IN G by IN_SUBSET + also x IN G by SUBSET_DEF (sgbINTER g).op (sgbINTER g).id x = #e * x by subgroup_big_intersect_property = x by group_id @@ -1574,7 +1558,7 @@ val subgroup_big_intersect_subset = store_thm( Since |/ x IN (sgbINTER g).carrier by subgroup_big_intersect_has_inv and (sgbINTER g).id IN (sgbINTER g).carrier by subgroup_big_intersect_op_element and (sgbINTER g).id = #e by subgroup_big_intersect_property - also x IN G by IN_SUBSET + also x IN G by SUBSET_DEF Let y = |/ x, then y IN (sgbINTER g).carrier, (sgbINTER g).op y x = |/ x * x by subgroup_big_intersect_property @@ -1590,20 +1574,20 @@ val subgroup_big_intersect_group = store_thm( `(sgbINTER g).op x y IN (sgbINTER g).carrier` by metis_tac[subgroup_big_intersect_op_element] >> `(sgbINTER g).op y z IN (sgbINTER g).carrier` by metis_tac[subgroup_big_intersect_op_element] >> `(x * y) * z = x * (y * z)` suffices_by rw[subgroup_big_intersect_property] >> - `x IN G /\ y IN G /\ z IN G` by metis_tac[IN_SUBSET] >> + `x IN G /\ y IN G /\ z IN G` by metis_tac[SUBSET_DEF] >> rw[group_assoc], metis_tac[subgroup_big_intersect_has_id], `(sgbINTER g).id = #e` by rw[subgroup_big_intersect_property] >> `(sgbINTER g).id IN (sgbINTER g).carrier` by metis_tac[subgroup_big_intersect_has_id] >> `#e * x = x` suffices_by rw[subgroup_big_intersect_property] >> - `x IN G` by metis_tac[IN_SUBSET] >> + `x IN G` by metis_tac[SUBSET_DEF] >> rw[], `|/ x IN (sgbINTER g).carrier` by rw[subgroup_big_intersect_has_inv] >> `(sgbINTER g).id = #e` by rw[subgroup_big_intersect_property] >> `(sgbINTER g).id IN (sgbINTER g).carrier` by rw[subgroup_big_intersect_has_id] >> qexists_tac `|/ x` >> `|/ x * x = #e` suffices_by rw[subgroup_big_intersect_property] >> - `x IN G` by metis_tac[IN_SUBSET] >> + `x IN G` by metis_tac[SUBSET_DEF] >> rw[] ]); diff --git a/examples/algebra/lib/EulerScript.sml b/examples/algebra/lib/EulerScript.sml deleted file mode 100644 index c3cf291c6a..0000000000 --- a/examples/algebra/lib/EulerScript.sml +++ /dev/null @@ -1,1226 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Euler Set and Totient Function *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "Euler"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* open dependent theories *) -open pred_setTheory listTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open arithmeticTheory dividesTheory gcdTheory; - - -(* ------------------------------------------------------------------------- *) -(* Euler Set and Totient Function Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - natural n = IMAGE SUC (count n) - upto n = count (SUC n) -*) -(* Definitions and Theorems (# are exported, ! in computeLib): - - Residues: - residue_def |- !n. residue n = {i | 0 < i /\ i < n} - residue_element |- !n j. j IN residue n ==> 0 < j /\ j < n - residue_0 |- residue 0 = {} - residue_1 |- residue 1 = {} - residue_nonempty |- !n. 1 < n ==> residue n <> {} - residue_no_zero |- !n. 0 NOTIN residue n - residue_no_self |- !n. n NOTIN residue n -! residue_thm |- !n. residue n = count n DIFF {0} - residue_insert |- !n. 0 < n ==> (residue (SUC n) = n INSERT residue n) - residue_delete |- !n. 0 < n ==> (residue n DELETE n = residue n) - residue_suc |- !n. 0 < n ==> (residue (SUC n) = n INSERT residue n) - residue_count |- !n. 0 < n ==> (count n = 0 INSERT residue n) - residue_finite |- !n. FINITE (residue n) - residue_card |- !n. 0 < n ==> (CARD (residue n) = n - 1) - residue_prime_neq |- !p a n. prime p /\ a IN residue p /\ n <= p ==> - !x. x IN residue n ==> (a * n) MOD p <> (a * x) MOD p - prod_set_residue |- !n. PROD_SET (residue n) = FACT (n - 1) - - Naturals: - natural_element |- !n j. j IN natural n <=> 0 < j /\ j <= n - natural_property |- !n. natural n = {j | 0 < j /\ j <= n} - natural_finite |- !n. FINITE (natural n) - natural_card |- !n. CARD (natural n) = n - natural_not_0 |- !n. 0 NOTIN natural n - natural_0 |- natural 0 = {} - natural_1 |- natural 1 = {1} - natural_has_1 |- !n. 0 < n ==> 1 IN natural n - natural_has_last |- !n. 0 < n ==> n IN natural n - natural_suc |- !n. natural (SUC n) = SUC n INSERT natural n - natural_thm |- !n. natural n = set (GENLIST SUC n) - natural_divisor_natural |- !n a b. 0 < n /\ a IN natural n /\ b divides a ==> b IN natural n - natural_cofactor_natural |- !n a b. 0 < n /\ 0 < a /\ b IN natural n /\ a divides b ==> - b DIV a IN natural n - natural_cofactor_natural_reduced - |- !n a b. 0 < n /\ a divides n /\ b IN natural n /\ a divides b ==> - b DIV a IN natural (n DIV a) - - Uptos: - upto_finite |- !n. FINITE (upto n) - upto_card |- !n. CARD (upto n) = SUC n - upto_has_last |- !n. n IN upto n - upto_delete |- !n. upto n DELETE n = count n - upto_split_first |- !n. upto n = {0} UNION natural n - upto_split_last |- !n. upto n = count n UNION {n} - upto_by_count |- !n. upto n = n INSERT count n - upto_by_natural |- !n. upto n = 0 INSERT natural n - natural_by_upto |- !n. natural n = upto n DELETE 0 - - Euler Set and Totient Function: - Euler_def |- !n. Euler n = {i | 0 < i /\ i < n /\ coprime n i} - totient_def |- !n. totient n = CARD (Euler n) - Euler_element |- !n x. x IN Euler n <=> 0 < x /\ x < n /\ coprime n x -! Euler_thm |- !n. Euler n = residue n INTER {j | coprime j n} - Euler_finite |- !n. FINITE (Euler n) - Euler_0 |- Euler 0 = {} - Euler_1 |- Euler 1 = {} - Euler_has_1 |- !n. 1 < n ==> 1 IN Euler n - Euler_nonempty |- !n. 1 < n ==> Euler n <> {} - Euler_empty |- !n. (Euler n = {}) <=> (n = 0 \/ n = 1) - Euler_card_upper_le |- !n. totient n <= n - Euler_card_upper_lt |- !n. 1 < n ==> totient n < n - Euler_card_bounds |- !n. totient n <= n /\ (1 < n ==> 0 < totient n /\ totient n < n) - Euler_prime |- !p. prime p ==> (Euler p = residue p) - Euler_card_prime |- !p. prime p ==> (totient p = p - 1) - - Summation of Geometric Sequence: - sigma_geometric_natural_eqn |- !p. 0 < p ==> - !n. (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) - sigma_geometric_natural |- !p. 1 < p ==> - !n. SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) DIV (p - 1) - - Chinese Remainder Theorem: - mod_mod_prod_eq |- !m n a b. 0 < m /\ 0 < n /\ a MOD (m * n) = b MOD (m * n) ==> - a MOD m = b MOD m /\ a MOD n = b MOD n - coprime_mod_mod_prod_eq - |- !m n a b. 0 < m /\ 0 < n /\ coprime m n /\ - a MOD m = b MOD m /\ a MOD n = b MOD n ==> - a MOD (m * n) = b MOD (m * n) - coprime_mod_mod_prod_eq_iff - |- !m n. 0 < m /\ 0 < n /\ coprime m n ==> - !a b. a MOD (m * n) = b MOD (m * n) <=> - a MOD m = b MOD m /\ a MOD n = b MOD n - coprime_mod_mod_solve - |- !m n a b. 0 < m /\ 0 < n /\ coprime m n ==> - ?!x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n - - Useful Theorems: - PROD_SET_IMAGE_EXP_NONZERO |- !n m. PROD_SET (IMAGE (\j. n ** j) (count m)) = - PROD_SET (IMAGE (\j. n ** j) (residue m)) -*) - -(* ------------------------------------------------------------------------- *) -(* Count-based Sets *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Residues -- close-relative of COUNT *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of residues = nonzero remainders *) -val residue_def = zDefine `residue n = { i | (0 < i) /\ (i < n) }`; -(* use zDefine as this is not computationally effective. *) - -(* Theorem: j IN residue n ==> 0 < j /\ j < n *) -(* Proof: by residue_def. *) -val residue_element = store_thm( - "residue_element", - ``!n j. j IN residue n ==> 0 < j /\ j < n``, - rw[residue_def]); - -(* Theorem: residue 0 = EMPTY *) -(* Proof: by residue_def *) -Theorem residue_0: - residue 0 = {} -Proof - simp[residue_def] -QED - -(* Theorem: residue 1 = EMPTY *) -(* Proof: by residue_def. *) -Theorem residue_1: - residue 1 = {} -Proof - simp[residue_def] -QED - -(* Theorem: 1 < n ==> residue n <> {} *) -(* Proof: - By residue_def, this is to show: 1 < n ==> ?x. x <> 0 /\ x < n - Take x = 1, this is true. -*) -val residue_nonempty = store_thm( - "residue_nonempty", - ``!n. 1 < n ==> residue n <> {}``, - rw[residue_def, EXTENSION] >> - metis_tac[DECIDE``1 <> 0``]); - -(* Theorem: 0 NOTIN residue n *) -(* Proof: by residue_def *) -Theorem residue_no_zero: - !n. 0 NOTIN residue n -Proof - simp[residue_def] -QED - -(* Theorem: n NOTIN residue n *) -(* Proof: by residue_def *) -Theorem residue_no_self: - !n. n NOTIN residue n -Proof - simp[residue_def] -QED - -(* Theorem: residue n = (count n) DIFF {0} *) -(* Proof: - residue n - = {i | 0 < i /\ i < n} by residue_def - = {i | i < n /\ i <> 0} by NOT_ZERO_LT_ZERO - = {i | i < n} DIFF {0} by IN_DIFF - = (count n) DIFF {0} by count_def -*) -val residue_thm = store_thm( - "residue_thm[compute]", - ``!n. residue n = (count n) DIFF {0}``, - rw[residue_def, EXTENSION]); -(* This is effective, put in computeLib. *) - -(* -> EVAL ``residue 10``; -val it = |- residue 10 = {9; 8; 7; 6; 5; 4; 3; 2; 1}: thm -*) - -(* Theorem: For n > 0, residue (SUC n) = n INSERT residue n *) -(* Proof: - residue (SUC n) - = {1, 2, ..., n} - = n INSERT {1, 2, ..., (n-1) } - = n INSERT residue n -*) -val residue_insert = store_thm( - "residue_insert", - ``!n. 0 < n ==> (residue (SUC n) = n INSERT residue n)``, - srw_tac[ARITH_ss][residue_def, EXTENSION]); - -(* Theorem: (residue n) DELETE n = residue n *) -(* Proof: Because n is not in (residue n). *) -val residue_delete = store_thm( - "residue_delete", - ``!n. 0 < n ==> ((residue n) DELETE n = residue n)``, - rpt strip_tac >> - `n NOTIN (residue n)` by rw[residue_def] >> - metis_tac[DELETE_NON_ELEMENT]); - -(* Theorem alias: rename *) -val residue_suc = save_thm("residue_suc", residue_insert); -(* val residue_suc = |- !n. 0 < n ==> (residue (SUC n) = n INSERT residue n): thm *) - -(* Theorem: count n = 0 INSERT (residue n) *) -(* Proof: by definition. *) -val residue_count = store_thm( - "residue_count", - ``!n. 0 < n ==> (count n = 0 INSERT (residue n))``, - srw_tac[ARITH_ss][residue_def, EXTENSION]); - -(* Theorem: FINITE (residue n) *) -(* Proof: by FINITE_COUNT. - If n = 0, residue 0 = {}, hence FINITE. - If n > 0, count n = 0 INSERT (residue n) by residue_count - hence true by FINITE_COUNT and FINITE_INSERT. -*) -val residue_finite = store_thm( - "residue_finite", - ``!n. FINITE (residue n)``, - Cases >- - rw[residue_def] >> - metis_tac[residue_count, FINITE_INSERT, count_def, FINITE_COUNT, DECIDE ``0 < SUC n``]); - -(* Theorem: For n > 0, CARD (residue n) = n-1 *) -(* Proof: - Since 0 INSERT (residue n) = count n by residue_count - the result follows by CARD_COUNT. -*) -val residue_card = store_thm( - "residue_card", - ``!n. 0 < n ==> (CARD (residue n) = n-1)``, - rpt strip_tac >> - `0 NOTIN (residue n)` by rw[residue_def] >> - `0 INSERT (residue n) = count n` by rw[residue_count] >> - `SUC (CARD (residue n)) = n` by metis_tac[residue_finite, CARD_INSERT, CARD_COUNT] >> - decide_tac); - -(* Theorem: For prime m, a in residue m, n <= m, a*n MOD m <> a*x MOD m for all x in residue n *) -(* Proof: - Assume the contrary, that a*n MOD m = a*x MOD m - Since a in residue m and m is prime, MOD_MULT_LCANCEL gives: n MOD m = x MOD m - If n = m, n MOD m = 0, but x MOD m <> 0, hence contradiction. - If n < m, then since x < n <= m, n = x, contradicting x < n. -*) -val residue_prime_neq = store_thm( - "residue_prime_neq", - ``!p a n. prime p /\ a IN (residue p) /\ n <= p ==> !x. x IN (residue n) ==> (a*n) MOD p <> (a*x) MOD p``, - rw[residue_def] >> - spose_not_then strip_assume_tac >> - `0 < p` by rw[PRIME_POS] >> - `(a MOD p <> 0) /\ (x MOD p <> 0)` by rw_tac arith_ss[] >> - `n MOD p = x MOD p` by metis_tac[MOD_MULT_LCANCEL] >> - Cases_on `n = p` >- - metis_tac [DIVMOD_ID] >> - `n < p` by decide_tac >> - `(n MOD p = n) /\ (x MOD p = x)` by rw_tac arith_ss[] >> - decide_tac); - -(* Idea: the product of residues is a factorial. *) - -(* Theorem: PROD_SET (residue n) = FACT (n - 1) *) -(* Proof: - By induction on n. - Base: PROD_SET (residue 0) = FACT (0 - 1) - PROD_SET (residue 0) - = PROD_SET {} by residue_0 - = 1 by PROD_SET_EMPTY - = FACT 0 by FACT_0 - = FACT (0 - 1) by arithmetic - Step: PROD_SET (residue n) = FACT (n - 1) ==> - PROD_SET (residue (SUC n)) = FACT (SUC n - 1) - If n = 0, - PROD_SET (residue (SUC 0)) - = PROD_SET (residue 1) by ONE - = PROD_SET {} by residue_1 - = 1 by PROD_SET_EMPTY - = FACT 0 by FACT_0 - - If n <> 0, then 0 < n. - Note FINITE (residue n) by residue_finite - PROD_SET (residue (SUC n)) - = PROD_SET (n INSERT residue n) by residue_insert - = n * PROD_SET ((residue n) DELETE n) by PROD_SET_THM - = n * PROD_SET (residue n) by residue_delete - = n * FACT (n - 1) by induction hypothesis - = FACT (SUC (n - 1)) by FACT - = FACT (SUC n - 1) by arithmetic -*) -Theorem prod_set_residue: - !n. PROD_SET (residue n) = FACT (n - 1) -Proof - Induct >- - simp[residue_0, PROD_SET_EMPTY, FACT_0] >> - Cases_on `n = 0` >- - simp[residue_1, PROD_SET_EMPTY, FACT_0] >> - `FINITE (residue n)` by rw[residue_finite] >> - `n = SUC (n - 1)` by decide_tac >> - `SUC (n - 1) = SUC n - 1` by decide_tac >> - `PROD_SET (residue (SUC n)) = PROD_SET (n INSERT residue n)` by rw[residue_insert] >> - `_ = n * PROD_SET ((residue n) DELETE n)` by rw[PROD_SET_THM] >> - `_ = n * PROD_SET (residue n)` by rw[residue_delete] >> - `_ = n * FACT (n - 1)` by rw[] >> - metis_tac[FACT] -QED - -(* ------------------------------------------------------------------------- *) -(* Naturals -- counting from 1 rather than 0, and inclusive. *) -(* ------------------------------------------------------------------------- *) - -(* Overload the set of natural numbers (like count) *) -val _ = overload_on("natural", ``\n. IMAGE SUC (count n)``); - -(* Theorem: j IN (natural n) <=> 0 < j /\ j <= n *) -(* Proof: - Note j <> 0 by natural_not_0 - j IN (natural n) - ==> j IN IMAGE SUC (count n) by notation - ==> ?x. x < n /\ (j = SUC x) by IN_IMAGE - Since SUC x <> 0 by numTheory.NOT_SUC - Hence j <> 0, - and x < n ==> SUC x < SUC n by LESS_MONO_EQ - or j < SUC n by above, j = SUC x - thus j <= n by prim_recTheory.LESS_THM -*) -val natural_element = store_thm( - "natural_element", - ``!n j. j IN (natural n) <=> 0 < j /\ j <= n``, - rw[EQ_IMP_THM] >> - `j <> 0` by decide_tac >> - `?m. j = SUC m` by metis_tac[num_CASES] >> - `m < n` by decide_tac >> - metis_tac[]); - -(* Theorem: natural n = {j| 0 < j /\ j <= n} *) -(* Proof: by natural_element, IN_IMAGE *) -val natural_property = store_thm( - "natural_property", - ``!n. natural n = {j| 0 < j /\ j <= n}``, - rw[EXTENSION, natural_element]); - -(* Theorem: FINITE (natural n) *) -(* Proof: FINITE_COUNT, IMAGE_FINITE *) -val natural_finite = store_thm( - "natural_finite", - ``!n. FINITE (natural n)``, - rw[]); - -(* Theorem: CARD (natural n) = n *) -(* Proof: - CARD (natural n) - = CARD (IMAGE SUC (count n)) by notation - = CARD (count n) by CARD_IMAGE_SUC - = n by CARD_COUNT -*) -val natural_card = store_thm( - "natural_card", - ``!n. CARD (natural n) = n``, - rw[CARD_IMAGE_SUC]); - -(* Theorem: 0 NOTIN (natural n) *) -(* Proof: by NOT_SUC *) -val natural_not_0 = store_thm( - "natural_not_0", - ``!n. 0 NOTIN (natural n)``, - rw[]); - -(* Theorem: natural 0 = {} *) -(* Proof: - natural 0 - = IMAGE SUC (count 0) by notation - = IMAGE SUC {} by COUNT_ZERO - = {} by IMAGE_EMPTY -*) -val natural_0 = store_thm( - "natural_0", - ``natural 0 = {}``, - rw[]); - -(* Theorem: natural 1 = {1} *) -(* Proof: - natural 1 - = IMAGE SUC (count 1) by notation - = IMAGE SUC {0} by count_add1 - = {SUC 0} by IMAGE_DEF - = {1} by ONE -*) -val natural_1 = store_thm( - "natural_1", - ``natural 1 = {1}``, - rw[EXTENSION, EQ_IMP_THM]); - -(* Theorem: 0 < n ==> 1 IN (natural n) *) -(* Proof: by natural_element, LESS_OR, ONE *) -val natural_has_1 = store_thm( - "natural_has_1", - ``!n. 0 < n ==> 1 IN (natural n)``, - rw[natural_element]); - -(* Theorem: 0 < n ==> n IN (natural n) *) -(* Proof: by natural_element *) -val natural_has_last = store_thm( - "natural_has_last", - ``!n. 0 < n ==> n IN (natural n)``, - rw[natural_element]); - -(* Theorem: natural (SUC n) = (SUC n) INSERT (natural n) *) -(* Proof: - natural (SUC n) - <=> {j | 0 < j /\ j <= (SUC n)} by natural_property - <=> {j | 0 < j /\ (j <= n \/ (j = SUC n))} by LE - <=> {j | j IN (natural n) \/ (j = SUC n)} by natural_property - <=> (SUC n) INSERT (natural n) by INSERT_DEF -*) -val natural_suc = store_thm( - "natural_suc", - ``!n. natural (SUC n) = (SUC n) INSERT (natural n)``, - rw[EXTENSION, EQ_IMP_THM]); - -(* Theorem: natural n = set (GENLIST SUC n) *) -(* Proof: - By induction on n. - Base: natural 0 = set (GENLIST SUC 0) - LHS = natural 0 = {} by natural_0 - RHS = set (GENLIST SUC 0) - = set [] by GENLIST_0 - = {} by LIST_TO_SET - Step: natural n = set (GENLIST SUC n) ==> - natural (SUC n) = set (GENLIST SUC (SUC n)) - natural (SUC n) - = SUC n INSERT natural n by natural_suc - = SUC n INSERT (set (GENLIST SUC n)) by induction hypothesis - = set (SNOC (SUC n) (GENLIST SUC n)) by LIST_TO_SET_SNOC - = set (GENLIST SUC (SUC n)) by GENLIST -*) -val natural_thm = store_thm( - "natural_thm", - ``!n. natural n = set (GENLIST SUC n)``, - Induct >- - rw[] >> - rw[natural_suc, LIST_TO_SET_SNOC, GENLIST]); - -(* Theorem: 0 < n /\ a IN (natural n) /\ b divides a ==> b IN (natural n) *) -(* Proof: - By natural_element, this is to show: - (1) 0 < a /\ b divides a ==> 0 < b - True by divisor_pos - (2) 0 < a /\ b divides a ==> b <= n - Since b divides a - ==> b <= a by DIVIDES_LE, 0 < a - ==> b <= n by LESS_EQ_TRANS -*) -val natural_divisor_natural = store_thm( - "natural_divisor_natural", - ``!n a b. 0 < n /\ a IN (natural n) /\ b divides a ==> b IN (natural n)``, - rw[natural_element] >- - metis_tac[divisor_pos] >> - metis_tac[DIVIDES_LE, LESS_EQ_TRANS]); - -(* Theorem: 0 < n /\ 0 < a /\ b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural n) *) -(* Proof: - Let c = b DIV a. - By natural_element, this is to show: - 0 < a /\ 0 < b /\ b <= n /\ a divides b ==> 0 < c /\ c <= n - Since a divides b ==> b = c * a by DIVIDES_EQN, 0 < a - so b = a * c by MULT_COMM - or c divides b by divides_def - Thus 0 < c /\ c <= b by divides_pos - or c <= n by LESS_EQ_TRANS -*) -val natural_cofactor_natural = store_thm( - "natural_cofactor_natural", - ``!n a b. 0 < n /\ 0 < a /\ b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural n)``, - rewrite_tac[natural_element] >> - ntac 4 strip_tac >> - qabbrev_tac `c = b DIV a` >> - `b = c * a` by rw[GSYM DIVIDES_EQN, Abbr`c`] >> - `c divides b` by metis_tac[divides_def, MULT_COMM] >> - `0 < c /\ c <= b` by metis_tac[divides_pos] >> - decide_tac); - -(* Theorem: 0 < n /\ a divides n /\ b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural (n DIV a)) *) -(* Proof: - Let c = b DIV a. - By natural_element, this is to show: - 0 < n /\ a divides b /\ 0 < b /\ b <= n /\ a divides b ==> 0 < c /\ c <= n DIV a - Note 0 < a by ZERO_DIVIES, 0 < n - Since a divides b ==> b = c * a by DIVIDES_EQN, 0 < a [1] - or c divides b by divides_def, MULT_COMM - Thus 0 < c, since 0 divides b means b = 0. by ZERO_DIVIDES, b <> 0 - Now n = (n DIV a) * a by DIVIDES_EQN, 0 < a [2] - With b <= n, c * a <= (n DIV a) * a by [1], [2] - Hence c <= n DIV a by LE_MULT_RCANCEL, a <> 0 -*) -val natural_cofactor_natural_reduced = store_thm( - "natural_cofactor_natural_reduced", - ``!n a b. 0 < n /\ a divides n /\ - b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural (n DIV a))``, - rewrite_tac[natural_element] >> - ntac 4 strip_tac >> - qabbrev_tac `c = b DIV a` >> - `a <> 0` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> - `(b = c * a) /\ (n = (n DIV a) * a)` by rw[GSYM DIVIDES_EQN, Abbr`c`] >> - `c divides b` by metis_tac[divides_def, MULT_COMM] >> - `0 < c` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> - metis_tac[LE_MULT_RCANCEL]); - -(* ------------------------------------------------------------------------- *) -(* Uptos -- counting from 0 and inclusive. *) -(* ------------------------------------------------------------------------- *) - -(* Overload on another count-related set *) -val _ = overload_on("upto", ``\n. count (SUC n)``); - -(* Theorem: FINITE (upto n) *) -(* Proof: by FINITE_COUNT *) -val upto_finite = store_thm( - "upto_finite", - ``!n. FINITE (upto n)``, - rw[]); - -(* Theorem: CARD (upto n) = SUC n *) -(* Proof: by CARD_COUNT *) -val upto_card = store_thm( - "upto_card", - ``!n. CARD (upto n) = SUC n``, - rw[]); - -(* Theorem: n IN (upto n) *) -(* Proof: byLESS_SUC_REFL *) -val upto_has_last = store_thm( - "upto_has_last", - ``!n. n IN (upto n)``, - rw[]); - -(* Theorem: (upto n) DELETE n = count n *) -(* Proof: - (upto n) DELETE n - = (count (SUC n)) DELETE n by notation - = (n INSERT count n) DELETE n by COUNT_SUC - = count n DELETE n by DELETE_INSERT - = count n by DELETE_NON_ELEMENT, COUNT_NOT_SELF -*) -Theorem upto_delete: - !n. (upto n) DELETE n = count n -Proof - metis_tac[COUNT_SUC, COUNT_NOT_SELF, DELETE_INSERT, DELETE_NON_ELEMENT] -QED - -(* Theorem: upto n = {0} UNION (natural n) *) -(* Proof: - By UNION_DEF, EXTENSION, this is to show: - (1) x < SUC n ==> (x = 0) \/ ?x'. (x = SUC x') /\ x' < n - If x = 0, trivially true. - If x <> 0, x = SUC m. - Take x' = m, - then SUC m = x < SUC n ==> m < n by LESS_MONO_EQ - (2) (x = 0) \/ ?x'. (x = SUC x') /\ x' < n ==> x < SUC n - If x = 0, 0 < SUC n by SUC_POS - If ?x'. (x = SUC x') /\ x' < n, - x' < n ==> SUC x' = x < SUC n by LESS_MONO_EQ -*) -val upto_split_first = store_thm( - "upto_split_first", - ``!n. upto n = {0} UNION (natural n)``, - rw[EXTENSION, EQ_IMP_THM] >> - Cases_on `x` >- - rw[] >> - metis_tac[LESS_MONO_EQ]); - -(* Theorem: upto n = (count n) UNION {n} *) -(* Proof: - By UNION_DEF, EXTENSION, this is to show: - (1) x < SUC n ==> x < n \/ (x = n) - True by LESS_THM. - (2) x < n \/ (x = n) ==> x < SUC n - True by LESS_THM. -*) -val upto_split_last = store_thm( - "upto_split_last", - ``!n. upto n = (count n) UNION {n}``, - rw[EXTENSION, EQ_IMP_THM]); - -(* Theorem: upto n = n INSERT (count n) *) -(* Proof: - upto n - = count (SUC n) by notation - = {x | x < SUC n} by count_def - = {x | (x = n) \/ (x < n)} by prim_recTheory.LESS_THM - = x INSERT {x| x < n} by INSERT_DEF - = x INSERT (count n) by count_def -*) -val upto_by_count = store_thm( - "upto_by_count", - ``!n. upto n = n INSERT (count n)``, - rw[EXTENSION]); - -(* Theorem: upto n = 0 INSERT (natural n) *) -(* Proof: - upto n - = count (SUC n) by notation - = {x | x < SUC n} by count_def - = {x | ((x = 0) \/ (?m. x = SUC m)) /\ x < SUC n)} by num_CASES - = {x | (x = 0 /\ x < SUC n) \/ (?m. x = SUC m /\ x < SUC n)} by SUC_POS - = 0 INSERT {SUC m | SUC m < SUC n} by INSERT_DEF - = 0 INSERT {SUC m | m < n} by LESS_MONO_EQ - = 0 INSERT (IMAGE SUC (count n)) by IMAGE_DEF - = 0 INSERT (natural n) by notation -*) -val upto_by_natural = store_thm( - "upto_by_natural", - ``!n. upto n = 0 INSERT (natural n)``, - rw[EXTENSION] >> - metis_tac[num_CASES, LESS_MONO_EQ, SUC_POS]); - -(* Theorem: natural n = count (SUC n) DELETE 0 *) -(* Proof: - count (SUC n) DELETE 0 - = {x | x < SUC n} DELETE 0 by count_def - = {x | x < SUC n} DIFF {0} by DELETE_DEF - = {x | x < SUC n /\ x <> 0} by DIFF_DEF - = {SUC m | SUC m < SUC n} by num_CASES - = {SUC m | m < n} by LESS_MONO_EQ - = IMAGE SUC (count n) by IMAGE_DEF - = natural n by notation -*) -val natural_by_upto = store_thm( - "natural_by_upto", - ``!n. natural n = count (SUC n) DELETE 0``, - (rw[EXTENSION, EQ_IMP_THM] >> metis_tac[num_CASES, LESS_MONO_EQ])); - -(* ------------------------------------------------------------------------- *) -(* Euler Set and Totient Function *) -(* ------------------------------------------------------------------------- *) - -(* Euler's totient function *) -val Euler_def = zDefine` - Euler n = { i | 0 < i /\ i < n /\ (gcd n i = 1) } -`; -(* that is, Euler n = { i | i in (residue n) /\ (gcd n i = 1) }; *) -(* use zDefine as this is not computationally effective. *) - -val totient_def = Define` - totient n = CARD (Euler n) -`; - -(* Theorem: x IN (Euler n) <=> 0 < x /\ x < n /\ coprime n x *) -(* Proof: by Euler_def. *) -val Euler_element = store_thm( - "Euler_element", - ``!n x. x IN (Euler n) <=> 0 < x /\ x < n /\ coprime n x``, - rw[Euler_def]); - -(* Theorem: Euler n = (residue n) INTER {j | coprime j n} *) -(* Proof: by Euler_def, residue_def, EXTENSION, IN_INTER *) -val Euler_thm = store_thm( - "Euler_thm[compute]", - ``!n. Euler n = (residue n) INTER {j | coprime j n}``, - rw[Euler_def, residue_def, GCD_SYM, EXTENSION]); -(* This is effective, put in computeLib. *) - -(* -> EVAL ``Euler 10``; -val it = |- Euler 10 = {9; 7; 3; 1}: thm -> EVAL ``totient 10``; -val it = |- totient 10 = 4: thm -*) - -(* Theorem: FINITE (Euler n) *) -(* Proof: - Since (Euler n) SUBSET count n by Euler_def, SUBSET_DEF - and FINITE (count n) by FINITE_COUNT - ==> FINITE (Euler n) by SUBSET_FINITE -*) -val Euler_finite = store_thm( - "Euler_finite", - ``!n. FINITE (Euler n)``, - rpt strip_tac >> - `(Euler n) SUBSET count n` by rw[Euler_def, SUBSET_DEF] >> - metis_tac[FINITE_COUNT, SUBSET_FINITE]); - -(* Theorem: Euler 0 = {} *) -(* Proof: by Euler_def *) -val Euler_0 = store_thm( - "Euler_0", - ``Euler 0 = {}``, - rw[Euler_def]); - -(* Theorem: Euler 1 = {} *) -(* Proof: by Euler_def *) -val Euler_1 = store_thm( - "Euler_1", - ``Euler 1 = {}``, - rw[Euler_def]); - -(* Theorem: 1 < n ==> 1 IN (Euler n) *) -(* Proof: by Euler_def *) -val Euler_has_1 = store_thm( - "Euler_has_1", - ``!n. 1 < n ==> 1 IN (Euler n)``, - rw[Euler_def]); - -(* Theorem: 1 < n ==> (Euler n) <> {} *) -(* Proof: by Euler_has_1, MEMBER_NOT_EMPTY *) -val Euler_nonempty = store_thm( - "Euler_nonempty", - ``!n. 1 < n ==> (Euler n) <> {}``, - metis_tac[Euler_has_1, MEMBER_NOT_EMPTY]); - -(* Theorem: (Euler n = {}) <=> ((n = 0) \/ (n = 1)) *) -(* Proof: - If part: Euler n = {} ==> n = 0 \/ n = 1 - By contradiction, suppose ~(n = 0 \/ n = 1). - Then 1 < n, but Euler n <> {} by Euler_nonempty - This contradicts Euler n = {}. - Only-if part: n = 0 \/ n = 1 ==> Euler n = {} - Note Euler 0 = {} by Euler_0 - and Euler 1 = {} by Euler_1 -*) -val Euler_empty = store_thm( - "Euler_empty", - ``!n. (Euler n = {}) <=> ((n = 0) \/ (n = 1))``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `1 < n` by decide_tac >> - metis_tac[Euler_nonempty], - rw[Euler_0], - rw[Euler_1] - ]); - -(* Theorem: totient n <= n *) -(* Proof: - Since (Euler n) SUBSET count n by Euler_def, SUBSET_DEF - and FINITE (count n) by FINITE_COUNT - and (CARD (count n) = n by CARD_COUNT - Hence CARD (Euler n) <= n by CARD_SUBSET - or totient n <= n by totient_def -*) -val Euler_card_upper_le = store_thm( - "Euler_card_upper_le", - ``!n. totient n <= n``, - rpt strip_tac >> - `(Euler n) SUBSET count n` by rw[Euler_def, SUBSET_DEF] >> - metis_tac[totient_def, CARD_SUBSET, FINITE_COUNT, CARD_COUNT]); - -(* Theorem: 1 < n ==> totient n < n *) -(* Proof: - First, (Euler n) SUBSET count n by Euler_def, SUBSET_DEF - Now, ~(coprime 0 n) by coprime_0L, n <> 1 - so 0 NOTIN (Euler n) by Euler_def - but 0 IN (count n) by IN_COUNT, 0 < n - Thus (Euler n) <> (count n) by EXTENSION - and (Euler n) PSUBSET (count n) by PSUBSET_DEF - Since FINITE (count n) by FINITE_COUNT - and (CARD (count n) = n by CARD_COUNT - Hence CARD (Euler n) < n by CARD_PSUBSET - or totient n < n by totient_def -*) -val Euler_card_upper_lt = store_thm( - "Euler_card_upper_lt", - ``!n. 1 < n ==> totient n < n``, - rpt strip_tac >> - `(Euler n) SUBSET count n` by rw[Euler_def, SUBSET_DEF] >> - `0 < n /\ n <> 1` by decide_tac >> - `~(coprime 0 n)` by metis_tac[coprime_0L] >> - `0 NOTIN (Euler n)` by rw[Euler_def] >> - `0 IN (count n)` by rw[] >> - `(Euler n) <> (count n)` by metis_tac[EXTENSION] >> - `(Euler n) PSUBSET (count n)` by rw[PSUBSET_DEF] >> - metis_tac[totient_def, CARD_PSUBSET, FINITE_COUNT, CARD_COUNT]); - -(* Theorem: (totient n <= n) /\ (1 < n ==> 0 < totient n /\ totient n < n) *) -(* Proof: - This is to show: - (1) totient n <= n, - True by Euler_card_upper_le. - (2) 1 < n ==> 0 < totient n - Since (Euler n) <> {} by Euler_nonempty - Also FINITE (Euler n) by Euler_finite - Hence CARD (Euler n) <> 0 by CARD_EQ_0 - or 0 < totient n by totient_def - (3) 1 < n ==> totient n < n - True by Euler_card_upper_lt. -*) -val Euler_card_bounds = store_thm( - "Euler_card_bounds", - ``!n. (totient n <= n) /\ (1 < n ==> 0 < totient n /\ totient n < n)``, - rw[] >- - rw[Euler_card_upper_le] >- - (`(Euler n) <> {}` by rw[Euler_nonempty] >> - `FINITE (Euler n)` by rw[Euler_finite] >> - `totient n <> 0` by metis_tac[totient_def, CARD_EQ_0] >> - decide_tac) >> - rw[Euler_card_upper_lt]); - -(* Theorem: For prime p, (Euler p = residue p) *) -(* Proof: - By Euler_def, residue_def, this is to show: - For prime p, gcd p x = 1 for 0 < x < p. - Since x < p, x does not divide p, result follows by PRIME_GCD. - or, this is true by prime_coprime_all_lt -*) -val Euler_prime = store_thm( - "Euler_prime", - ``!p. prime p ==> (Euler p = residue p)``, - rw[Euler_def, residue_def, EXTENSION, EQ_IMP_THM] >> - rw[prime_coprime_all_lt]); - -(* Theorem: For prime p, totient p = p - 1 *) -(* Proof: - totient p - = CARD (Euler p) by totient_def - = CARD (residue p) by Euler_prime - = p - 1 by residue_card, and prime p > 0. -*) -val Euler_card_prime = store_thm( - "Euler_card_prime", - ``!p. prime p ==> (totient p = p - 1)``, - rw[totient_def, Euler_prime, residue_card, PRIME_POS]); - -(* ------------------------------------------------------------------------- *) -(* Summation of Geometric Sequence *) -(* ------------------------------------------------------------------------- *) - -(* Geometric Series: - Let s = p + p ** 2 + p ** 3 - p * s = p ** 2 + p ** 3 + p ** 4 - p * s - s = p ** 4 - p - (p - 1) * s = p * (p ** 3 - 1) -*) - -(* Theorem: 0 < p ==> !n. (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) *) -(* Proof: - By induction on n. - Base: (p - 1) * SIGMA (\j. p ** j) (natural 0) = p * (p ** 0 - 1) - LHS = (p - 1) * SIGMA (\j. p ** j) (natural 0) - = (p - 1) * SIGMA (\j. p ** j) {} by natural_0 - = (p - 1) * 0 by SUM_IMAGE_EMPTY - = 0 by MULT_0 - RHS = p * (p ** 0 - 1) - = p * (1 - 1) by EXP - = p * 0 by SUB_EQUAL_0 - = 0 = LHS by MULT_0 - Step: (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) ==> - (p - 1) * SIGMA (\j. p ** j) (natural (SUC n)) = p * (p ** SUC n - 1) - Note FINITE (natural n) by natural_finite - and (SUC n) NOTIN (natural n) by natural_element - Also p <= p ** (SUC n) by X_LE_X_EXP, SUC_POS - and 1 <= p by 0 < p - thus p ** (SUC n) <> 0 by EXP_POS, 0 < p - so p ** (SUC n) <= p * p ** (SUC n) by LE_MULT_LCANCEL, p ** (SUC n) <> 0 - (p - 1) * SIGMA (\j. p ** j) (natural (SUC n)) - = (p - 1) * SIGMA (\j. p ** j) ((SUC n) INSERT (natural n)) by natural_suc - = (p - 1) * ((p ** SUC n) + SIGMA (\j. p ** j) ((natural n) DELETE (SUC n))) by SUM_IMAGE_THM - = (p - 1) * ((p ** SUC n) + SIGMA (\j. p ** j) (natural n)) by DELETE_NON_ELEMENT - = (p - 1) * (p ** SUC n) + (p - 1) * SIGMA (\j. p ** j) (natural n) by LEFT_ADD_DISTRIB - = (p - 1) * (p ** SUC n) + p * (p ** n - 1) by induction hypothesis - = (p - 1) * (p ** SUC n) + (p * p ** n - p) by LEFT_SUB_DISTRIB - = (p - 1) * (p ** SUC n) + (p ** (SUC n) - p) by EXP - = (p * p ** SUC n - p ** SUC n) + (p ** SUC n - p) by RIGHT_SUB_DISTRIB - = (p * p ** SUC n - p ** SUC n + p ** SUC n - p by LESS_EQ_ADD_SUB, p <= p ** (SUC n) - = p ** p ** SUC n - p by SUB_ADD, p ** (SUC n) <= p * p ** (SUC n) - = p * (p ** SUC n - 1) by LEFT_SUB_DISTRIB - *) -val sigma_geometric_natural_eqn = store_thm( - "sigma_geometric_natural_eqn", - ``!p. 0 < p ==> !n. (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1)``, - rpt strip_tac >> - Induct_on `n` >- - rw_tac std_ss[natural_0, SUM_IMAGE_EMPTY, EXP, MULT_0] >> - `FINITE (natural n)` by rw[natural_finite] >> - `(SUC n) NOTIN (natural n)` by rw[natural_element] >> - qabbrev_tac `q = p ** SUC n` >> - `p <= q` by rw[X_LE_X_EXP, Abbr`q`] >> - `1 <= p` by decide_tac >> - `q <> 0` by rw[EXP_POS, Abbr`q`] >> - `q <= p * q` by rw[LE_MULT_LCANCEL] >> - `(p - 1) * SIGMA (\j. p ** j) (natural (SUC n)) - = (p - 1) * SIGMA (\j. p ** j) ((SUC n) INSERT (natural n))` by rw[natural_suc] >> - `_ = (p - 1) * (q + SIGMA (\j. p ** j) ((natural n) DELETE (SUC n)))` by rw[SUM_IMAGE_THM, Abbr`q`] >> - `_ = (p - 1) * (q + SIGMA (\j. p ** j) (natural n))` by metis_tac[DELETE_NON_ELEMENT] >> - `_ = (p - 1) * q + (p - 1) * SIGMA (\j. p ** j) (natural n)` by rw[LEFT_ADD_DISTRIB] >> - `_ = (p - 1) * q + p * (p ** n - 1)` by rw[] >> - `_ = (p - 1) * q + (p * p ** n - p)` by rw[LEFT_SUB_DISTRIB] >> - `_ = (p - 1) * q + (q - p)` by rw[EXP, Abbr`q`] >> - `_ = (p * q - q) + (q - p)` by rw[RIGHT_SUB_DISTRIB] >> - `_ = (p * q - q + q) - p` by rw[LESS_EQ_ADD_SUB] >> - `_ = p * q - p` by rw[SUB_ADD] >> - `_ = p * (q - 1)` by rw[LEFT_SUB_DISTRIB] >> - rw[]); - -(* Theorem: 1 < p ==> !n. SIGMA (\j. p ** j) (natural n) = (p * (p ** n - 1)) DIV (p - 1) *) -(* Proof: - Since 1 < p, - ==> 0 < p - 1, and 0 < p by arithmetic - Let t = SIGMA (\j. p ** j) (natural n) - With 0 < p, - (p - 1) * t = p * (p ** n - 1) by sigma_geometric_natural_eqn, 0 < p - Hence t = (p * (p ** n - 1)) DIV (p - 1) by DIV_SOLVE, 0 < (p - 1) -*) -val sigma_geometric_natural = store_thm( - "sigma_geometric_natural", - ``!p. 1 < p ==> !n. SIGMA (\j. p ** j) (natural n) = (p * (p ** n - 1)) DIV (p - 1)``, - rpt strip_tac >> - `0 < p - 1 /\ 0 < p` by decide_tac >> - rw[sigma_geometric_natural_eqn, DIV_SOLVE]); - -(* ------------------------------------------------------------------------- *) -(* Chinese Remainder Theorem. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: when a MOD (m * n) = b MOD (m * n), break up modulus m * n. *) - -(* Theorem: 0 < m /\ 0 < n /\ a MOD (m * n) = b MOD (m * n) ==> - a MOD m = b MOD m /\ a MOD n = b MOD n *) -(* Proof: - Either b <= a, or a < b, which implies a <= b. - The statement is symmetrical in a and b, - so proceed by lemma with b <= a, without loss of generality. - Note 0 < m * n by MULT_POS - so ?c. a = b + c * (m * n) by MOD_MOD_EQN, 0 < m * n - Thus a = b + (c * m) * n by arithmetic - and a = b + (c * n) * m by arithmetic - ==> a MOD m = b MOD m by MOD_MOD_EQN, 0 < m - and a MOD n = b MOD n by MOD_MOD_EQN, 0 < n -*) -Theorem mod_mod_prod_eq: - !m n a b. 0 < m /\ 0 < n /\ a MOD (m * n) = b MOD (m * n) ==> - a MOD m = b MOD m /\ a MOD n = b MOD n -Proof - ntac 5 strip_tac >> - `!a b. b <= a /\ a MOD (m * n) = b MOD (m * n) ==> - a MOD m = b MOD m /\ a MOD n = b MOD n` by - (ntac 3 strip_tac >> - `0 < m * n` by fs[] >> - `?c. a' = b' + c * (m * n)` by metis_tac[MOD_MOD_EQN] >> - `a' = b' + (c * m) * n` by decide_tac >> - `a' = b' + (c * n) * m` by decide_tac >> - metis_tac[MOD_MOD_EQN]) >> - (Cases_on `b <= a` >> simp[]) -QED - -(* Idea: converse of mod_mod_prod_eq when coprime. *) - -(* Theorem: 0 < m /\ 0 < n /\ coprime m n /\ - a MOD m = b MOD m /\ a MOD n = b MOD n ==> - a MOD (m * n) = b MOD (m * n) *) -(* Proof: - Either b <= a, or a < b, which implies a <= b. - The statement is symmetrical in a and b, - so proceed by lemma with b <= a, without loss of generality. - Note 0 < m * n by MULT_POS - and ?h. a = b + h * m by MOD_MOD_EQN, 0 < m - and ?k. a = b + k * n by MOD_MOD_EQN, 0 < n - ==> h * m = k * n by EQ_ADD_LCANCEL - Thus n divides (h * m) by divides_def - or n divides h by euclid_coprime, coprime m n - ==> ?c. h = c * n by divides_def - so a = b + c * (m * n) by above - Thus a MOD (m * n) = b MOD (m * n) - by MOD_MOD_EQN, 0 < m * n -*) -Theorem coprime_mod_mod_prod_eq: - !m n a b. 0 < m /\ 0 < n /\ coprime m n /\ - a MOD m = b MOD m /\ a MOD n = b MOD n ==> - a MOD (m * n) = b MOD (m * n) -Proof - rpt strip_tac >> - `!a b. b <= a /\ a MOD m = b MOD m /\ a MOD n = b MOD n ==> - a MOD (m * n) = b MOD (m * n)` by - (rpt strip_tac >> - `0 < m * n` by fs[] >> - `?h. a' = b' + h * m` by metis_tac[MOD_MOD_EQN] >> - `?k. a' = b' + k * n` by metis_tac[MOD_MOD_EQN] >> - `h * m = k * n` by decide_tac >> - `n divides (h * m)` by metis_tac[divides_def] >> - `n divides h` by metis_tac[euclid_coprime, MULT_COMM] >> - `?c. h = c * n` by rw[GSYM divides_def] >> - `a' = b' + c * (m * n)` by fs[] >> - metis_tac[MOD_MOD_EQN]) >> - (Cases_on `b <= a` >> simp[]) -QED - -(* Idea: combine both parts for a MOD (m * n) = b MOD (m * n). *) - -(* Theorem: 0 < m /\ 0 < n /\ coprime m n ==> - !a b. a MOD (m * n) = b MOD (m * n) <=> a MOD m = b MOD m /\ a MOD n = b MOD n *) -(* Proof: - If part is true by mod_mod_prod_eq - Only-if part is true by coprime_mod_mod_prod_eq -*) -Theorem coprime_mod_mod_prod_eq_iff: - !m n. 0 < m /\ 0 < n /\ coprime m n ==> - !a b. a MOD (m * n) = b MOD (m * n) <=> a MOD m = b MOD m /\ a MOD n = b MOD n -Proof - metis_tac[mod_mod_prod_eq, coprime_mod_mod_prod_eq] -QED - -(* Idea: application, the Chinese Remainder Theorem for two coprime moduli. *) - -(* Theorem: 0 < m /\ 0 < n /\ coprime m n ==> - ?!x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n *) -(* Proof: - By EXISTS_UNIQUE_THM, this is to show: - (1) Existence: ?x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n - Note ?p q. (p * m + q * n) MOD (m * n) = 1 MOD (m * n) - by coprime_linear_mod_prod - so (p * m + q * n) MOD m = 1 MOD m - and (p * m + q * n) MOD n = 1 MOD n by mod_mod_prod_eq - or (q * n) MOD m = 1 MOD m by MOD_TIMES - and (p * m) MOD n = 1 MOD n by MOD_TIMES - Let z = b * p * m + a * q * n. - z MOD m - = (b * p * m + a * q * n) MOD m - = (a * q * n) MOD m by MOD_TIMES - = ((a MOD m) * (q * n) MOD m) MOD m by MOD_TIMES2 - = a MOD m by MOD_TIMES, above - and z MOD n - = (b * p * m + a * q * n) MDO n - = (b * p * m) MOD n by MOD_TIMES - = ((b MOD n) * (p * m) MOD n) MOD n by MOD_TIMES2 - = b MOD n by MOD_TIMES, above - Take x = z MOD (m * n). - Then x < m * n by MOD_LESS - and x MOD m = z MOD m = a MOD m by MOD_MULT_MOD - and x MOD n = z MOD n = b MOD n by MOD_MULT_MOD - (2) Uniqueness: - x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n /\ - y < m * n /\ y MOD m = a MOD m /\ y MOD n = b MOD n ==> x = y - Note x MOD m = y MOD m by both equal to a MOD m - and x MOD n = y MOD n by both equal to b MOD n - Thus x MOD (m * n) = y MOD (m * n) by coprime_mod_mod_prod_eq - so x = y by LESS_MOD, both < m * n -*) -Theorem coprime_mod_mod_solve: - !m n a b. 0 < m /\ 0 < n /\ coprime m n ==> - ?!x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n -Proof - rw[EXISTS_UNIQUE_THM] >| [ - `?p q. (p * m + q * n) MOD (m * n) = 1 MOD (m * n)` by rw[coprime_linear_mod_prod] >> - qabbrev_tac `u = p * m + q * n` >> - `u MOD m = 1 MOD m /\ u MOD n = 1 MOD n` by metis_tac[mod_mod_prod_eq] >> - `(q * n) MOD m = 1 MOD m /\ (p * m) MOD n = 1 MOD n` by rfs[MOD_TIMES, Abbr`u`] >> - qabbrev_tac `z = b * p * m + a * q * n` >> - qexists_tac `z MOD (m * n)` >> - rw[] >| [ - `z MOD (m * n) MOD m = z MOD m` by rw[MOD_MULT_MOD] >> - `_ = (a * q * n) MOD m` by rw[Abbr`z`] >> - `_ = ((a MOD m) * (q * n) MOD m) MOD m` by rw[MOD_TIMES2] >> - `_ = a MOD m` by fs[] >> - decide_tac, - `z MOD (m * n) MOD n = z MOD n` by metis_tac[MOD_MULT_MOD, MULT_COMM] >> - `_ = (b * p * m) MOD n` by rw[Abbr`z`] >> - `_ = ((b MOD n) * (p * m) MOD n) MOD n` by rw[MOD_TIMES2] >> - `_ = b MOD n` by fs[] >> - decide_tac - ], - metis_tac[coprime_mod_mod_prod_eq, LESS_MOD] - ] -QED - -(* Yes! The Chinese Remainder Theorem with two modular equations. *) - -(* -For an algorithm: -* define bezout, input pair (m, n), output pair (p, q) -* define a dot-product: - (p, q) dot (m, n) = p * m + q * n - with (p, q) dot (m, n) MOD (m * n) = (gcd m n) MOD (m * n) -* define a scale-product: - (a, b) scale (p, q) = (a * p, b * q) - with z = ((a, b) scale (p, q)) dot (m, n) - and x = z MOD (m * n) - = (((a, b) scale (p, q)) dot (m, n)) MOD (m * n) - = (((a, b) scale (bezout (m, n))) dot (m, n)) MOD (m * n) - -Note that bezout (m, n) is the extended Euclidean algorithm. - -*) - -(* ------------------------------------------------------------------------- *) -(* Useful Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Note: - count m = {i | i < m} defined in pred_set - residue m = {i | 0 < i /\ i < m} defined in Euler - The difference i = 0 gives n ** 0 = 1, which does not make a difference for PROD_SET. -*) - -(* Theorem: PROD_SET (IMAGE (\j. n ** j) (count m)) = - PROD_SET (IMAGE (\j. n ** j) (residue m)) *) -(* Proof: - Let f = \j. n ** j. - When m = 0, - Note count 0 = {} by COUNT_0 - and residue 0 = {} by residue_0 - Thus LHS = RHS. - When m = 1, - Note count 1 = {0} by COUNT_1 - and residue 1 = {} by residue_1 - Thus LHS = PROD_SET (IMAGE f {0}) - = PROD_SET {f 0} by IMAGE_SING - = f 0 by PROD_SET_SING - = n ** 0 = 1 by EXP_0 - RHS = PROD_SET (IMAGE f {}) - = PROD_SET {} by IMAGE_EMPTY - = 1 by PROD_SET_EMPTY - = LHS - For m <> 0, m <> 1, - When n = 0, - Note !j. f j = f j = 0 then 1 else 0 by ZERO_EXP - Thus IMAGE f (count m) = {0; 1} by count_def, EXTENSION, 1 < m - and IMAGE f (residue m) = {0} by residue_def, EXTENSION, 1 < m - Thus LHS = PROD_SET {0; 1} - = 0 * 1 = 0 by PROD_SET_THM - RHS = PROD_SET {0} - = 0 = LHS by PROD_SET_SING - When n = 1, - Note f = K 1 by EXP_1, FUN_EQ_THM - and count m <> {} by COUNT_NOT_EMPTY, 0 < m - and residue m <> {} by residue_nonempty, 1 < m - Thus LHS = PROD_SET (IMAGE (K 1) (count m)) - = PROD_SET {1} by IMAGE_K - = PROD_SET (IMAGE (K 1) (residue m)) by IMAGE_K - = RHS - For 1 < m, and 1 < n, - Note 0 IN count m by IN_COUNT, 0 < m - also (IMAGE f (count m)) DELETE 1 - = IMAGE f (residue m) by IMAGE_DEF, EXP_EQ_1, EXP, 1 < n - PROD_SET (IMAGE f (count m)) - = PROD_SET (IMAGE f (0 INSERT count m)) by ABSORPTION - = PROD_SET (f 0 INSERT IMAGE f (count m)) by IMAGE_INSERT - = n ** 0 * PROD_SET ((IMAGE f (count m)) DELETE n ** 0) by PROD_SET_THM - = PROD_SET ((IMAGE f (count m)) DELETE 1) by EXP_0 - = PROD_SET ((IMAGE f (residue m))) by above -*) -Theorem PROD_SET_IMAGE_EXP_NONZERO: - !n m. PROD_SET (IMAGE (\j. n ** j) (count m)) = - PROD_SET (IMAGE (\j. n ** j) (residue m)) -Proof - rpt strip_tac >> - qabbrev_tac `f = \j. n ** j` >> - Cases_on `m = 0` >- - simp[residue_0] >> - Cases_on `m = 1` >- - simp[residue_1, COUNT_1, Abbr`f`, PROD_SET_THM] >> - `0 < m /\ 1 < m` by decide_tac >> - Cases_on `n = 0` >| [ - `!j. f j = if j = 0 then 1 else 0` by rw[Abbr`f`] >> - `IMAGE f (count m) = {0; 1}` by - (rw[EXTENSION, EQ_IMP_THM] >- - metis_tac[ONE_NOT_ZERO] >> - metis_tac[] - ) >> - `IMAGE f (residue m) = {0}` by - (rw[residue_def, EXTENSION, EQ_IMP_THM] >> - `0 < 1` by decide_tac >> - metis_tac[]) >> - simp[PROD_SET_THM], - Cases_on `n = 1` >| [ - `f = K 1` by rw[FUN_EQ_THM, Abbr`f`] >> - `count m <> {}` by fs[COUNT_NOT_EMPTY] >> - `residue m <> {}` by fs[residue_nonempty] >> - simp[IMAGE_K], - `0 < n /\ 1 < n` by decide_tac >> - `0 IN count m` by rw[] >> - `FINITE (IMAGE f (count m))` by rw[] >> - `(IMAGE f (count m)) DELETE 1 = IMAGE f (residue m)` by - (rw[residue_def, IMAGE_DEF, Abbr`f`, EXTENSION, EQ_IMP_THM] >- - metis_tac[EXP, NOT_ZERO] >- - metis_tac[] >> - `j <> 0` by decide_tac >> - metis_tac[EXP_EQ_1] - ) >> - `PROD_SET (IMAGE f (count m)) = PROD_SET (IMAGE f (0 INSERT count m))` by metis_tac[ABSORPTION] >> - `_ = PROD_SET (f 0 INSERT IMAGE f (count m))` by rw[] >> - `_ = n ** 0 * PROD_SET ((IMAGE f (count m)) DELETE n ** 0)` by rw[PROD_SET_THM, Abbr`f`] >> - `_ = 1 * PROD_SET ((IMAGE f (count m)) DELETE 1)` by metis_tac[EXP_0] >> - `_ = PROD_SET ((IMAGE f (residue m)))` by rw[] >> - decide_tac - ] - ] -QED - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/GaussScript.sml b/examples/algebra/lib/GaussScript.sml deleted file mode 100644 index 56ed6ca567..0000000000 --- a/examples/algebra/lib/GaussScript.sml +++ /dev/null @@ -1,2940 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Gauss' Little Theorem. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "Gauss"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* Get dependent theories in lib *) -(* val _ = load "helperListTheory"; *) -open helperListTheory; - -(* val _ = load "EulerTheory"; *) -open EulerTheory; -(* (* val _ = load "helperFunctionTheory"; -- in EulerTheory *) *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* for SQRT and related theorems *) -(* val _ = load "logPowerTheory"; *) -open logrootTheory logPowerTheory; - - -(* ------------------------------------------------------------------------- *) -(* Gauss' Little Theorem *) -(* ------------------------------------------------------------------------- *) -(* Overloading: -*) -(* Definitions and Theorems (# are exported, ! in computeLib): - - Helper Theorems: - - Coprimes: - coprimes_def |- !n. coprimes n = {j | j IN natural n /\ coprime j n} - coprimes_element |- !n j. j IN coprimes n <=> 0 < j /\ j <= n /\ coprime j n -! coprimes_alt |- !n. coprimes n = natural n INTER {j | coprime j n} - coprimes_thm |- !n. coprimes n = set (FILTER (\j. coprime j n) (GENLIST SUC n)) - coprimes_subset |- !n. coprimes n SUBSET natural n - coprimes_finite |- !n. FINITE (coprimes n) - coprimes_0 |- coprimes 0 = {} - coprimes_1 |- coprimes 1 = {1} - coprimes_has_1 |- !n. 0 < n ==> 1 IN coprimes n - coprimes_eq_empty |- !n. (coprimes n = {}) <=> (n = 0) - coprimes_no_0 |- !n. 0 NOTIN coprimes n - coprimes_without_last |- !n. 1 < n ==> n NOTIN coprimes n - coprimes_with_last |- !n. n IN coprimes n <=> (n = 1) - coprimes_has_last_but_1 |- !n. 1 < n ==> n - 1 IN coprimes n - coprimes_element_less |- !n. 1 < n ==> !j. j IN coprimes n ==> j < n - coprimes_element_alt |- !n. 1 < n ==> !j. j IN coprimes n <=> j < n /\ coprime j n - coprimes_max |- !n. 1 < n ==> (MAX_SET (coprimes n) = n - 1) - coprimes_eq_Euler |- !n. 1 < n ==> (coprimes n = Euler n) - coprimes_prime |- !n. prime n ==> (coprimes n = residue n) - - Coprimes by a divisor: - coprimes_by_def |- !n d. coprimes_by n d = if 0 < n /\ d divides n then coprimes (n DIV d) else {} - coprimes_by_element |- !n d j. j IN coprimes_by n d <=> 0 < n /\ d divides n /\ j IN coprimes (n DIV d) - coprimes_by_finite |- !n d. FINITE (coprimes_by n d) - coprimes_by_0 |- !d. coprimes_by 0 d = {} - coprimes_by_by_0 |- !n. coprimes_by n 0 = {} - coprimes_by_by_1 |- !n. 0 < n ==> (coprimes_by n 1 = coprimes n) - coprimes_by_by_last |- !n. 0 < n ==> (coprimes_by n n = {1}) - coprimes_by_by_divisor |- !n d. 0 < n /\ d divides n ==> (coprimes_by n d = coprimes (n DIV d)) - coprimes_by_eq_empty |- !n d. 0 < n ==> ((coprimes_by n d = {}) <=> ~(d divides n)) - - GCD Equivalence Class: - gcd_matches_def |- !n d. gcd_matches n d = {j | j IN natural n /\ (gcd j n = d)} -! gcd_matches_alt |- !n d. gcd_matches n d = natural n INTER {j | gcd j n = d} - gcd_matches_element |- !n d j. j IN gcd_matches n d <=> 0 < j /\ j <= n /\ (gcd j n = d) - gcd_matches_subset |- !n d. gcd_matches n d SUBSET natural n - gcd_matches_finite |- !n d. FINITE (gcd_matches n d) - gcd_matches_0 |- !d. gcd_matches 0 d = {} - gcd_matches_with_0 |- !n. gcd_matches n 0 = {} - gcd_matches_1 |- !d. gcd_matches 1 d = if d = 1 then {1} else {} - gcd_matches_has_divisor |- !n d. 0 < n /\ d divides n ==> d IN gcd_matches n d - gcd_matches_element_divides |- !n d j. j IN gcd_matches n d ==> d divides j /\ d divides n - gcd_matches_eq_empty |- !n d. 0 < n ==> ((gcd_matches n d = {}) <=> ~(d divides n)) - - Phi Function: - phi_def |- !n. phi n = CARD (coprimes n) - phi_thm |- !n. phi n = LENGTH (FILTER (\j. coprime j n) (GENLIST SUC n)) - phi_fun |- phi = CARD o coprimes - phi_pos |- !n. 0 < n ==> 0 < phi n - phi_0 |- phi 0 = 0 - phi_eq_0 |- !n. (phi n = 0) <=> (n = 0) - phi_1 |- phi 1 = 1 - phi_eq_totient |- !n. 1 < n ==> (phi n = totient n) - phi_prime |- !n. prime n ==> (phi n = n - 1) - phi_2 |- phi 2 = 1 - phi_gt_1 |- !n. 2 < n ==> 1 < phi n - phi_le |- !n. phi n <= n - phi_lt |- !n. 1 < n ==> phi n < n - - Divisors: - divisors_def |- !n. divisors n = {d | 0 < d /\ d <= n /\ d divides n} - divisors_element |- !n d. d IN divisors n <=> 0 < d /\ d <= n /\ d divides n - divisors_element_alt |- !n. 0 < n ==> !d. d IN divisors n <=> d divides n - divisors_has_element |- !n d. d IN divisors n ==> 0 < n - divisors_has_1 |- !n. 0 < n ==> 1 IN divisors n - divisors_has_last |- !n. 0 < n ==> n IN divisors n - divisors_not_empty |- !n. 0 < n ==> divisors n <> {} - divisors_0 |- divisors 0 = {} - divisors_1 |- divisors 1 = {1} - divisors_eq_empty |- !n. divisors n = {} <=> n = 0 -! divisors_eqn |- !n. divisors n = - IMAGE (\j. if j + 1 divides n then j + 1 else 1) (count n) - divisors_has_factor |- !n p q. 0 < n /\ n = p * q ==> p IN divisors n /\ q IN divisors n - divisors_has_cofactor |- !n d. d IN divisors n ==> n DIV d IN divisors n - divisors_delete_last |- !n. divisors n DELETE n = {m | 0 < m /\ m < n /\ m divides n} - divisors_nonzero |- !n d. d IN divisors n ==> 0 < d - divisors_subset_natural |- !n. divisors n SUBSET natural n - divisors_finite |- !n. FINITE (divisors n) - divisors_divisors_bij |- !n. (\d. n DIV d) PERMUTES divisors n - - An upper bound for divisors: - divisor_le_cofactor_ge |- !n p. 0 < p /\ p divides n /\ p <= SQRT n ==> SQRT n <= n DIV p - divisor_gt_cofactor_le |- !n p. 0 < p /\ p divides n /\ SQRT n < p ==> n DIV p <= SQRT n - divisors_cofactor_inj |- !n. INJ (\j. n DIV j) (divisors n) univ(:num) - divisors_card_upper |- !n. CARD (divisors n) <= TWICE (SQRT n) - - Gauss' Little Theorem: - gcd_matches_divisor_element |- !n d. d divides n ==> - !j. j IN gcd_matches n d ==> j DIV d IN coprimes_by n d - gcd_matches_bij_coprimes_by |- !n d. d divides n ==> - BIJ (\j. j DIV d) (gcd_matches n d) (coprimes_by n d) - gcd_matches_bij_coprimes |- !n d. 0 < n /\ d divides n ==> - BIJ (\j. j DIV d) (gcd_matches n d) (coprimes (n DIV d)) - divisors_eq_gcd_image |- !n. divisors n = IMAGE (gcd n) (natural n) - gcd_eq_equiv_class |- !n d. feq_class (gcd n) (natural n) d = gcd_matches n d - gcd_eq_equiv_class_fun |- !n. feq_class (gcd n) (natural n) = gcd_matches n - gcd_eq_partition_by_divisors |- !n. partition (feq (gcd n)) (natural n) = - IMAGE (gcd_matches n) (divisors n) - gcd_eq_equiv_on_natural |- !n. feq (gcd n) equiv_on natural n - sum_over_natural_by_gcd_partition - |- !f n. SIGMA f (natural n) = - SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) - sum_over_natural_by_divisors |- !f n. SIGMA f (natural n) = - SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) - gcd_matches_from_divisors_inj |- !n. INJ (gcd_matches n) (divisors n) univ(:num -> bool) - gcd_matches_and_coprimes_by_same_size |- !n. CARD o gcd_matches n = CARD o coprimes_by n - coprimes_by_with_card |- !n. 0 < n ==> CARD o coprimes_by n = - (\d. phi (if d IN divisors n then n DIV d else 0)) - coprimes_by_divisors_card |- !n x. x IN divisors n ==> - (CARD o coprimes_by n) x = (\d. phi (n DIV d)) x - Gauss_little_thm |- !n. SIGMA phi (divisors n) = n - - Euler phi function is multiplicative for coprimes: - coprimes_mult_by_image - |- !m n. coprime m n ==> - coprimes (m * n) = - IMAGE (\(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)) - (coprimes m CROSS coprimes n) - coprimes_map_cross_inj - |- !m n. coprime m n ==> - INJ (\(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)) - (coprimes m CROSS coprimes n) univ(:num) - phi_mult |- !m n. coprime m n ==> phi (m * n) = phi m * phi n - phi_primes_distinct |- !p q. prime p /\ prime q /\ p <> q ==> phi (p * q) = (p - 1) * (q - 1) - - Euler phi function for prime powers: - multiples_upto_def |- !m n. m multiples_upto n = {x | m divides x /\ 0 < x /\ x <= n} - multiples_upto_element - |- !m n x. x IN m multiples_upto n <=> m divides x /\ 0 < x /\ x <= n - multiples_upto_alt |- !m n. m multiples_upto n = {x | ?k. x = k * m /\ 0 < x /\ x <= n} - multiples_upto_element_alt - |- !m n x. x IN m multiples_upto n <=> ?k. x = k * m /\ 0 < x /\ x <= n - multiples_upto_eqn |- !m n. m multiples_upto n = {x | m divides x /\ x IN natural n} - multiples_upto_0_n |- !n. 0 multiples_upto n = {} - multiples_upto_1_n |- !n. 1 multiples_upto n = natural n - multiples_upto_m_0 |- !m. m multiples_upto 0 = {} - multiples_upto_m_1 |- !m. m multiples_upto 1 = if m = 1 then {1} else {} - multiples_upto_thm |- !m n. m multiples_upto n = - if m = 0 then {} else IMAGE ($* m) (natural (n DIV m)) - multiples_upto_subset - |- !m n. m multiples_upto n SUBSET natural n - multiples_upto_finite - |- !m n. FINITE (m multiples_upto n) - multiples_upto_card |- !m n. CARD (m multiples_upto n) = if m = 0 then 0 else n DIV m - coprimes_prime_power|- !p n. prime p ==> - coprimes (p ** n) = natural (p ** n) DIFF p multiples_upto p ** n - phi_prime_power |- !p n. prime p ==> phi (p ** SUC n) = (p - 1) * p ** n - phi_prime_sq |- !p. prime p ==> phi (p * p) = p * (p - 1) - phi_primes |- !p q. prime p /\ prime q ==> - phi (p * q) = if p = q then p * (p - 1) else (p - 1) * (q - 1) - - Recursive definition of phi: - rec_phi_def |- !n. rec_phi n = if n = 0 then 0 - else if n = 1 then 1 - else n - SIGMA (\a. rec_phi a) {m | m < n /\ m divides n} - rec_phi_0 |- rec_phi 0 = 0 - rec_phi_1 |- rec_phi 1 = 1 - rec_phi_eq_phi |- !n. rec_phi n = phi n - - Useful Theorems: - coprimes_from_not_1_inj |- INJ coprimes (univ(:num) DIFF {1}) univ(:num -> bool) - divisors_eq_image_gcd_upto |- !n. 0 < n ==> divisors n = IMAGE (gcd n) (upto n) - gcd_eq_equiv_on_upto |- !n. feq (gcd n) equiv_on upto n - gcd_eq_upto_partition_by_divisors - |- !n. 0 < n ==> - partition (feq (gcd n)) (upto n) = - IMAGE (preimage (gcd n) (upto n)) (divisors n) - sum_over_upto_by_gcd_partition - |- !f n. SIGMA f (upto n) = - SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n)) - sum_over_upto_by_divisors |- !f n. 0 < n ==> - SIGMA f (upto n) = - SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n)) - - divisors_eq_image_gcd_count |- !n. divisors n = IMAGE (gcd n) (count n) - gcd_eq_equiv_on_count |- !n. feq (gcd n) equiv_on count n - gcd_eq_count_partition_by_divisors - |- !n. partition (feq (gcd n)) (count n) = - IMAGE (preimage (gcd n) (count n)) (divisors n) - sum_over_count_by_gcd_partition - |- !f n. SIGMA f (count n) = - SIGMA (SIGMA f) (partition (feq (gcd n)) (count n)) - sum_over_count_by_divisors |- !f n. SIGMA f (count n) = - SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) - - divisors_eq_image_gcd_natural - |- !n. divisors n = IMAGE (gcd n) (natural n) - gcd_eq_natural_partition_by_divisors - |- !n. partition (feq (gcd n)) (natural n) = - IMAGE (preimage (gcd n) (natural n)) (divisors n) - sum_over_natural_by_preimage_divisors - |- !f n. SIGMA f (natural n) = - SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) - sum_image_divisors_cong |- !f g. f 0 = g 0 /\ (!n. SIGMA f (divisors n) = SIGMA g (divisors n)) ==> f = g -*) - -(* Theory: - -Given the set natural 6 = {1, 2, 3, 4, 5, 6} -Every element has a gcd with 6: IMAGE (gcd 6) (natural 6) = {1, 2, 3, 2, 1, 6} = {1, 2, 3, 6}. -Thus the original set is partitioned by gcd: {{1, 5}, {2, 4}, {3}, {6}} -Since (gcd 6) j is a divisor of 6, and they run through all possible divisors of 6, - SIGMA f (natural 6) -= f 1 + f 2 + f 3 + f 4 + f 5 + f 6 -= (f 1 + f 5) + (f 2 + f 4) + f 3 + f 6 -= (SIGMA f {1, 5}) + (SIGMA f {2, 4}) + (SIGMA f {3}) + (SIGMA f {6}) -= SIGMA (SIGMA f) {{1, 5}, {2, 4}, {3}, {6}} -= SIGMA (SIGMA f) (partition (feq (natural 6) (gcd 6)) (natural 6)) - -SIGMA:('a -> num) -> ('a -> bool) -> num -SIGMA (f:num -> num):(num -> bool) -> num -SIGMA (SIGMA (f:num -> num)) (s:(num -> bool) -> bool):num - -How to relate this to (divisors n) ? -First, observe IMAGE (gcd 6) (natural 6) = divisors 6 -and partition {{1, 5}, {2, 4}, {3}, {6}} = IMAGE (preimage (gcd 6) (natural 6)) (divisors 6) - - SIGMA f (natural 6) -= SIGMA (SIGMA f) (partition (feq (natural 6) (gcd 6)) (natural 6)) -= SIGMA (SIGMA f) (IMAGE (preimage (gcd 6) (natural 6)) (divisors 6)) - -divisors n:num -> bool -preimage (gcd n):(num -> bool) -> num -> num -> bool -preimage (gcd n) (natural n):num -> num -> bool -IMAGE (preimage (gcd n) (natural n)) (divisors n):(num -> bool) -> bool - -How to relate this to (coprimes d), where d divides n ? -Note {1, 5} with (gcd 6) j = 1, equals to (coprimes (6 DIV 1)) = coprimes 6 - {2, 4} with (gcd 6) j = 2, BIJ to {2/2, 4/2} with gcd (6/2) (j/2) = 1, i.e {1, 2} = coprimes 3 - {3} with (gcd 6) j = 3, BIJ to {3/3} with gcd (6/3) (j/3) = 1, i.e. {1} = coprimes 2 - {6} with (gcd 6) j = 6, BIJ to {6/6} with gcd (6/6) (j/6) = 1, i.e. {1} = coprimes 1 -Hence CARD {{1, 5}, {2, 4}, {3}, {6}} = CARD (partition) - = CARD {{1, 5}/1, {2,4}/2, {3}/3, {6}/6} = CARD (reduced-partition) - = CARD {(coprimes 6/1) (coprimes 6/2) (coprimes 6/3) (coprimes 6/6)} - = CARD {(coprimes 6) (coprimes 3) (coprimes 2) (coprimes 1)} - = SIGMA (CARD (coprimes d)), over d divides 6) - = SIGMA (phi d), over d divides 6. -*) - - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Coprimes *) -(* ------------------------------------------------------------------------- *) - -(* Define the coprimes set: integers from 1 to n that are coprime to n *) -(* -val coprimes_def = zDefine ` - coprimes n = {j | 0 < j /\ j <= n /\ coprime j n} -`; -*) -(* Note: j <= n ensures that coprimes n is finite. *) -(* Note: 0 < j is only to ensure coprimes 1 = {1} *) -val coprimes_def = zDefine ` - coprimes n = {j | j IN (natural n) /\ coprime j n} -`; -(* use zDefine as this is not computationally effective. *) - -(* Theorem: j IN coprimes n <=> 0 < j /\ j <= n /\ coprime j n *) -(* Proof: by coprimes_def, natural_element *) -val coprimes_element = store_thm( - "coprimes_element", - ``!n j. j IN coprimes n <=> 0 < j /\ j <= n /\ coprime j n``, - (rw[coprimes_def, natural_element] >> metis_tac[])); - -(* Theorem: coprimes n = (natural n) INTER {j | coprime j n} *) -(* Proof: by coprimes_def, EXTENSION, IN_INTER *) -val coprimes_alt = store_thm( - "coprimes_alt[compute]", - ``!n. coprimes n = (natural n) INTER {j | coprime j n}``, - rw[coprimes_def, EXTENSION]); -(* This is effective, put in computeLib. *) - -(* -> EVAL ``coprimes 10``; -val it = |- coprimes 10 = {9; 7; 3; 1}: thm -*) - -(* Theorem: coprimes n = set (FILTER (\j. coprime j n) (GENLIST SUC n)) *) -(* Proof: - coprimes n - = (natural n) INTER {j | coprime j n} by coprimes_alt - = (set (GENLIST SUC n)) INTER {j | coprime j n} by natural_thm - = {j | coprime j n} INTER (set (GENLIST SUC n)) by INTER_COMM - = set (FILTER (\j. coprime j n) (GENLIST SUC n)) by LIST_TO_SET_FILTER -*) -val coprimes_thm = store_thm( - "coprimes_thm", - ``!n. coprimes n = set (FILTER (\j. coprime j n) (GENLIST SUC n))``, - rw[coprimes_alt, natural_thm, INTER_COMM, LIST_TO_SET_FILTER]); - -(* Theorem: coprimes n SUBSET natural n *) -(* Proof: by coprimes_def, SUBSET_DEF *) -val coprimes_subset = store_thm( - "coprimes_subset", - ``!n. coprimes n SUBSET natural n``, - rw[coprimes_def, SUBSET_DEF]); - -(* Theorem: FINITE (coprimes n) *) -(* Proof: - Since (coprimes n) SUBSET (natural n) by coprimes_subset - and !n. FINITE (natural n) by natural_finite - so FINITE (coprimes n) by SUBSET_FINITE -*) -val coprimes_finite = store_thm( - "coprimes_finite", - ``!n. FINITE (coprimes n)``, - metis_tac[coprimes_subset, natural_finite, SUBSET_FINITE]); - -(* Theorem: coprimes 0 = {} *) -(* Proof: - By coprimes_element, 0 < j /\ j <= 0, - which is impossible, hence empty. -*) -val coprimes_0 = store_thm( - "coprimes_0", - ``coprimes 0 = {}``, - rw[coprimes_element, EXTENSION]); - -(* Theorem: coprimes 1 = {1} *) -(* Proof: - By coprimes_element, 0 < j /\ j <= 1, - Only possible j is 1, and gcd 1 1 = 1. - *) -val coprimes_1 = store_thm( - "coprimes_1", - ``coprimes 1 = {1}``, - rw[coprimes_element, EXTENSION]); - -(* Theorem: 0 < n ==> 1 IN (coprimes n) *) -(* Proof: by coprimes_element, GCD_1 *) -val coprimes_has_1 = store_thm( - "coprimes_has_1", - ``!n. 0 < n ==> 1 IN (coprimes n)``, - rw[coprimes_element]); - -(* Theorem: (coprimes n = {}) <=> (n = 0) *) -(* Proof: - If part: coprimes n = {} ==> n = 0 - By contradiction. - Suppose n <> 0, then 0 < n. - Then 1 IN (coprimes n) by coprimes_has_1 - hence (coprimes n) <> {} by MEMBER_NOT_EMPTY - This contradicts (coprimes n) = {}. - Only-if part: n = 0 ==> coprimes n = {} - True by coprimes_0 -*) -val coprimes_eq_empty = store_thm( - "coprimes_eq_empty", - ``!n. (coprimes n = {}) <=> (n = 0)``, - rw[EQ_IMP_THM] >- - metis_tac[coprimes_has_1, MEMBER_NOT_EMPTY, NOT_ZERO_LT_ZERO] >> - rw[coprimes_0]); - -(* Theorem: 0 NOTIN (coprimes n) *) -(* Proof: - By coprimes_element, 0 < j /\ j <= n, - Hence j <> 0, or 0 NOTIN (coprimes n) -*) -val coprimes_no_0 = store_thm( - "coprimes_no_0", - ``!n. 0 NOTIN (coprimes n)``, - rw[coprimes_element]); - -(* Theorem: 1 < n ==> n NOTIN coprimes n *) -(* Proof: - By coprimes_element, 0 < j /\ j <= n /\ gcd j n = 1 - If j = n, 1 = gcd j n = gcd n n = n by GCD_REF - which is excluded by 1 < n, so j <> n. -*) -val coprimes_without_last = store_thm( - "coprimes_without_last", - ``!n. 1 < n ==> n NOTIN coprimes n``, - rw[coprimes_element]); - -(* Theorem: n IN coprimes n <=> (n = 1) *) -(* Proof: - By coprimes_element, 0 < j /\ j <= n /\ gcd j n = 1 - If n IN coprimes n, 1 = gcd j n = gcd n n = n by GCD_REF - If n = 1, 0 < n, n <= n, and gcd n n = n = 1 by GCD_REF -*) -val coprimes_with_last = store_thm( - "coprimes_with_last", - ``!n. n IN coprimes n <=> (n = 1)``, - rw[coprimes_element]); - -(* Theorem: 1 < n ==> (n - 1) IN (coprimes n) *) -(* Proof: by coprimes_element, coprime_PRE, GCD_SYM *) -val coprimes_has_last_but_1 = store_thm( - "coprimes_has_last_but_1", - ``!n. 1 < n ==> (n - 1) IN (coprimes n)``, - rpt strip_tac >> - `0 < n /\ 0 < n - 1` by decide_tac >> - rw[coprimes_element, coprime_PRE, GCD_SYM]); - -(* Theorem: 1 < n ==> !j. j IN coprimes n ==> j < n *) -(* Proof: - Since j IN coprimes n ==> j <= n by coprimes_element - If j = n, then gcd n n = n <> 1 by GCD_REF - Thus j <> n, or j < n. or by coprimes_without_last -*) -val coprimes_element_less = store_thm( - "coprimes_element_less", - ``!n. 1 < n ==> !j. j IN coprimes n ==> j < n``, - metis_tac[coprimes_element, coprimes_without_last, LESS_OR_EQ]); - -(* Theorem: 1 < n ==> !j. j IN coprimes n <=> j < n /\ coprime j n *) -(* Proof: - If part: j IN coprimes n ==> j < n /\ coprime j n - Note 0 < j /\ j <= n /\ coprime j n by coprimes_element - By contradiction, suppose n <= j. - Then j = n, but gcd n n = n <> 1 by GCD_REF - Only-if part: j < n /\ coprime j n ==> j IN coprimes n - This is to show: - 0 < j /\ j <= n /\ coprime j n by coprimes_element - By contradiction, suppose ~(0 < j). - Then j = 0, but gcd 0 n = n <> 1 by GCD_0L -*) -val coprimes_element_alt = store_thm( - "coprimes_element_alt", - ``!n. 1 < n ==> !j. j IN coprimes n <=> j < n /\ coprime j n``, - rw[coprimes_element] >> - `n <> 1` by decide_tac >> - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `j = n` by decide_tac >> - metis_tac[GCD_REF], - spose_not_then strip_assume_tac >> - `j = 0` by decide_tac >> - metis_tac[GCD_0L] - ]); - -(* Theorem: 1 < n ==> (MAX_SET (coprimes n) = n - 1) *) -(* Proof: - Let s = coprimes n, m = MAX_SET s. - Note (n - 1) IN s by coprimes_has_last_but_1, 1 < n - Hence s <> {} by MEMBER_NOT_EMPTY - and FINITE s by coprimes_finite - Since !x. x IN s ==> x < n by coprimes_element_less, 1 < n - also !x. x < n ==> x <= (n - 1) by SUB_LESS_OR - Therefore MAX_SET s = n - 1 by MAX_SET_TEST -*) -val coprimes_max = store_thm( - "coprimes_max", - ``!n. 1 < n ==> (MAX_SET (coprimes n) = n - 1)``, - rpt strip_tac >> - qabbrev_tac `s = coprimes n` >> - `(n - 1) IN s` by rw[coprimes_has_last_but_1, Abbr`s`] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - `FINITE s` by rw[coprimes_finite, Abbr`s`] >> - `!x. x IN s ==> x < n` by rw[coprimes_element_less, Abbr`s`] >> - `!x. x < n ==> x <= (n - 1)` by decide_tac >> - metis_tac[MAX_SET_TEST]); - -(* Relate coprimes to Euler totient *) - -(* Theorem: 1 < n ==> (coprimes n = Euler n) *) -(* Proof: - By Euler_def, this is to show: - (1) x IN coprimes n ==> 0 < x, true by coprimes_element - (2) x IN coprimes n ==> x < n, true by coprimes_element_less - (3) x IN coprimes n ==> coprime n x, true by coprimes_element, GCD_SYM - (4) 0 < x /\ x < n /\ coprime n x ==> x IN coprimes n - That is, to show: 0 < x /\ x <= n /\ coprime x n. - Since x < n ==> x <= n by LESS_IMP_LESS_OR_EQ - Hence true by GCD_SYM -*) -val coprimes_eq_Euler = store_thm( - "coprimes_eq_Euler", - ``!n. 1 < n ==> (coprimes n = Euler n)``, - rw[Euler_def, EXTENSION, EQ_IMP_THM] >- - metis_tac[coprimes_element] >- - rw[coprimes_element_less] >- - metis_tac[coprimes_element, GCD_SYM] >> - metis_tac[coprimes_element, GCD_SYM, LESS_IMP_LESS_OR_EQ]); - -(* Theorem: prime n ==> (coprimes n = residue n) *) -(* Proof: - Since prime n ==> 1 < n by ONE_LT_PRIME - Hence coprimes n - = Euler n by coprimes_eq_Euler - = residue n by Euler_prime -*) -val coprimes_prime = store_thm( - "coprimes_prime", - ``!n. prime n ==> (coprimes n = residue n)``, - rw[ONE_LT_PRIME, coprimes_eq_Euler, Euler_prime]); - -(* ------------------------------------------------------------------------- *) -(* Coprimes by a divisor *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of coprimes by a divisor of n *) -val coprimes_by_def = Define ` - coprimes_by n d = if (0 < n /\ d divides n) then coprimes (n DIV d) else {} -`; - -(* -EVAL ``coprimes_by 10 2``; = {4; 3; 2; 1} -EVAL ``coprimes_by 10 5``; = {1} -*) - -(* Theorem: j IN (coprimes_by n d) <=> (0 < n /\ d divides n /\ j IN coprimes (n DIV d)) *) -(* Proof: by coprimes_by_def, MEMBER_NOT_EMPTY *) -val coprimes_by_element = store_thm( - "coprimes_by_element", - ``!n d j. j IN (coprimes_by n d) <=> (0 < n /\ d divides n /\ j IN coprimes (n DIV d))``, - metis_tac[coprimes_by_def, MEMBER_NOT_EMPTY]); - -(* Theorem: FINITE (coprimes_by n d) *) -(* Proof: - From coprimes_by_def, this follows by: - (1) !k. FINITE (coprimes k) by coprimes_finite - (2) FINITE {} by FINITE_EMPTY -*) -val coprimes_by_finite = store_thm( - "coprimes_by_finite", - ``!n d. FINITE (coprimes_by n d)``, - rw[coprimes_by_def, coprimes_finite]); - -(* Theorem: coprimes_by 0 d = {} *) -(* Proof: by coprimes_by_def *) -val coprimes_by_0 = store_thm( - "coprimes_by_0", - ``!d. coprimes_by 0 d = {}``, - rw[coprimes_by_def]); - -(* Theorem: coprimes_by n 0 = {} *) -(* Proof: - coprimes_by n 0 - = if 0 < n /\ 0 divides n then coprimes (n DIV 0) else {} - = 0 < 0 then coprimes (n DIV 0) else {} by ZERO_DIVIDES - = {} by prim_recTheory.LESS_REFL -*) -val coprimes_by_by_0 = store_thm( - "coprimes_by_by_0", - ``!n. coprimes_by n 0 = {}``, - rw[coprimes_by_def]); - -(* Theorem: 0 < n ==> (coprimes_by n 1 = coprimes n) *) -(* Proof: - Since 1 divides n by ONE_DIVIDES_ALL - coprimes_by n 1 - = coprimes (n DIV 1) by coprimes_by_def - = coprimes n by DIV_ONE, ONE -*) -val coprimes_by_by_1 = store_thm( - "coprimes_by_by_1", - ``!n. 0 < n ==> (coprimes_by n 1 = coprimes n)``, - rw[coprimes_by_def]); - -(* Theorem: 0 < n ==> (coprimes_by n n = {1}) *) -(* Proof: - Since n divides n by DIVIDES_REFL - coprimes_by n n - = coprimes (n DIV n) by coprimes_by_def - = coprimes 1 by DIVMOD_ID, 0 < n - = {1} by coprimes_1 -*) -val coprimes_by_by_last = store_thm( - "coprimes_by_by_last", - ``!n. 0 < n ==> (coprimes_by n n = {1})``, - rw[coprimes_by_def, coprimes_1]); - -(* Theorem: 0 < n /\ d divides n ==> (coprimes_by n d = coprimes (n DIV d)) *) -(* Proof: by coprimes_by_def *) -val coprimes_by_by_divisor = store_thm( - "coprimes_by_by_divisor", - ``!n d. 0 < n /\ d divides n ==> (coprimes_by n d = coprimes (n DIV d))``, - rw[coprimes_by_def]); - -(* Theorem: 0 < n ==> ((coprimes_by n d = {}) <=> ~(d divides n)) *) -(* Proof: - If part: 0 < n /\ coprimes_by n d = {} ==> ~(d divides n) - By contradiction. Suppose d divides n. - Then d divides n and 0 < n means - 0 < d /\ d <= n by divides_pos, 0 < n - Also coprimes_by n d = coprimes (n DIV d) by coprimes_by_def - so coprimes (n DIV d) = {} <=> n DIV d = 0 by coprimes_eq_empty - Thus n < d by DIV_EQUAL_0 - which contradicts d <= n. - Only-if part: 0 < n /\ ~(d divides n) ==> coprimes n d = {} - This follows by coprimes_by_def -*) -val coprimes_by_eq_empty = store_thm( - "coprimes_by_eq_empty", - ``!n d. 0 < n ==> ((coprimes_by n d = {}) <=> ~(d divides n))``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `0 < d /\ d <= n` by metis_tac[divides_pos] >> - `n DIV d = 0` by metis_tac[coprimes_by_def, coprimes_eq_empty] >> - `n < d` by rw[GSYM DIV_EQUAL_0] >> - decide_tac, - rw[coprimes_by_def] - ]); - -(* ------------------------------------------------------------------------- *) -(* GCD Equivalence Class *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of values with the same gcd *) -val gcd_matches_def = zDefine ` - gcd_matches n d = {j| j IN (natural n) /\ (gcd j n = d)} -`; -(* use zDefine as this is not computationally effective. *) - -(* Theorem: gcd_matches n d = (natural n) INTER {j | gcd j n = d} *) -(* Proof: by gcd_matches_def *) -Theorem gcd_matches_alt[compute]: - !n d. gcd_matches n d = (natural n) INTER {j | gcd j n = d} -Proof - simp[gcd_matches_def, EXTENSION] -QED - -(* -EVAL ``gcd_matches 10 2``; = {8; 6; 4; 2} -EVAL ``gcd_matches 10 5``; = {5} -*) - -(* Theorem: j IN gcd_matches n d <=> 0 < j /\ j <= n /\ (gcd j n = d) *) -(* Proof: by gcd_matches_def *) -val gcd_matches_element = store_thm( - "gcd_matches_element", - ``!n d j. j IN gcd_matches n d <=> 0 < j /\ j <= n /\ (gcd j n = d)``, - rw[gcd_matches_def, natural_element]); - -(* Theorem: (gcd_matches n d) SUBSET (natural n) *) -(* Proof: by gcd_matches_def, SUBSET_DEF *) -val gcd_matches_subset = store_thm( - "gcd_matches_subset", - ``!n d. (gcd_matches n d) SUBSET (natural n)``, - rw[gcd_matches_def, SUBSET_DEF]); - -(* Theorem: FINITE (gcd_matches n d) *) -(* Proof: - Since (gcd_matches n d) SUBSET (natural n) by coprimes_subset - and !n. FINITE (natural n) by natural_finite - so FINITE (gcd_matches n d) by SUBSET_FINITE -*) -val gcd_matches_finite = store_thm( - "gcd_matches_finite", - ``!n d. FINITE (gcd_matches n d)``, - metis_tac[gcd_matches_subset, natural_finite, SUBSET_FINITE]); - -(* Theorem: gcd_matches 0 d = {} *) -(* Proof: - j IN gcd_matches 0 d - <=> 0 < j /\ j <= 0 /\ (gcd j 0 = d) by gcd_matches_element - Since no j can satisfy this, the set is empty. -*) -val gcd_matches_0 = store_thm( - "gcd_matches_0", - ``!d. gcd_matches 0 d = {}``, - rw[gcd_matches_element, EXTENSION]); - -(* Theorem: gcd_matches n 0 = {} *) -(* Proof: - x IN gcd_matches n 0 - <=> 0 < x /\ x <= n /\ (gcd x n = 0) by gcd_matches_element - <=> 0 < x /\ x <= n /\ (x = 0) /\ (n = 0) by GCD_EQ_0 - <=> F by 0 < x, x = 0 - Hence gcd_matches n 0 = {} by EXTENSION -*) -val gcd_matches_with_0 = store_thm( - "gcd_matches_with_0", - ``!n. gcd_matches n 0 = {}``, - rw[EXTENSION, gcd_matches_element]); - -(* Theorem: gcd_matches 1 d = if d = 1 then {1} else {} *) -(* Proof: - j IN gcd_matches 1 d - <=> 0 < j /\ j <= 1 /\ (gcd j 1 = d) by gcd_matches_element - Only j to satisfy this is j = 1. - and d = gcd 1 1 = 1 by GCD_REF - If d = 1, j = 1 is the only element. - If d <> 1, the only element is taken out, set is empty. -*) -val gcd_matches_1 = store_thm( - "gcd_matches_1", - ``!d. gcd_matches 1 d = if d = 1 then {1} else {}``, - rw[gcd_matches_element, EXTENSION]); - -(* Theorem: 0 < n /\ d divides n ==> d IN (gcd_matches n d) *) -(* Proof: - Note 0 < n /\ d divides n - ==> 0 < d, and d <= n by divides_pos - and gcd d n = d by divides_iff_gcd_fix - Hence d IN (gcd_matches n d) by gcd_matches_element -*) -val gcd_matches_has_divisor = store_thm( - "gcd_matches_has_divisor", - ``!n d. 0 < n /\ d divides n ==> d IN (gcd_matches n d)``, - rw[gcd_matches_element] >- - metis_tac[divisor_pos] >- - rw[DIVIDES_LE] >> - rw[GSYM divides_iff_gcd_fix]); - -(* Theorem: j IN (gcd_matches n d) ==> d divides j /\ d divides n *) -(* Proof: - If j IN (gcd_matches n d), gcd j n = d by gcd_matches_element - This means d divides j /\ d divides n by GCD_IS_GREATEST_COMMON_DIVISOR -*) -val gcd_matches_element_divides = store_thm( - "gcd_matches_element_divides", - ``!n d j. j IN (gcd_matches n d) ==> d divides j /\ d divides n``, - metis_tac[gcd_matches_element, GCD_IS_GREATEST_COMMON_DIVISOR]); - -(* Theorem: 0 < n ==> ((gcd_matches n d = {}) <=> ~(d divides n)) *) -(* Proof: - If part: 0 < n /\ (gcd_matches n d = {}) ==> ~(d divides n) - By contradiction, suppose d divides n. - Then d IN gcd_matches n d by gcd_matches_has_divisor - This contradicts gcd_matches n d = {} by MEMBER_NOT_EMPTY - Only-if part: 0 < n /\ ~(d divides n) ==> (gcd_matches n d = {}) - By contradiction, suppose gcd_matches n d <> {}. - Then ?j. j IN (gcd_matches n d) by MEMBER_NOT_EMPTY - Giving d divides j /\ d divides n by gcd_matches_element_divides - This contradicts ~(d divides n). -*) -val gcd_matches_eq_empty = store_thm( - "gcd_matches_eq_empty", - ``!n d. 0 < n ==> ((gcd_matches n d = {}) <=> ~(d divides n))``, - rw[EQ_IMP_THM] >- - metis_tac[gcd_matches_has_divisor, MEMBER_NOT_EMPTY] >> - metis_tac[gcd_matches_element_divides, MEMBER_NOT_EMPTY]); - -(* ------------------------------------------------------------------------- *) -(* Phi Function *) -(* ------------------------------------------------------------------------- *) - -(* Define the Euler phi function from coprime set *) -val phi_def = Define ` - phi n = CARD (coprimes n) -`; -(* Since (coprimes n) is computable, phi n is now computable *) - -(* -> EVAL ``phi 10``; -val it = |- phi 10 = 4: thm -*) - -(* Theorem: phi n = LENGTH (FILTER (\j. coprime j n) (GENLIST SUC n)) *) -(* Proof: - Let ls = FILTER (\j. coprime j n) (GENLIST SUC n). - Note ALL_DISTINCT (GENLIST SUC n) by ALL_DISTINCT_GENLIST, SUC_EQ - Thus ALL_DISTINCT ls by FILTER_ALL_DISTINCT - phi n = CARD (coprimes n) by phi_def - = CARD (set ls) by coprimes_thm - = LENGTH ls by ALL_DISTINCT_CARD_LIST_TO_SET -*) -val phi_thm = store_thm( - "phi_thm", - ``!n. phi n = LENGTH (FILTER (\j. coprime j n) (GENLIST SUC n))``, - rpt strip_tac >> - qabbrev_tac `ls = FILTER (\j. coprime j n) (GENLIST SUC n)` >> - `ALL_DISTINCT ls` by rw[ALL_DISTINCT_GENLIST, FILTER_ALL_DISTINCT, Abbr`ls`] >> - `phi n = CARD (coprimes n)` by rw[phi_def] >> - `_ = CARD (set ls)` by rw[coprimes_thm, Abbr`ls`] >> - `_ = LENGTH ls` by rw[ALL_DISTINCT_CARD_LIST_TO_SET] >> - decide_tac); - -(* Theorem: phi = CARD o coprimes *) -(* Proof: by phi_def, FUN_EQ_THM *) -val phi_fun = store_thm( - "phi_fun", - ``phi = CARD o coprimes``, - rw[phi_def, FUN_EQ_THM]); - -(* Theorem: 0 < n ==> 0 < phi n *) -(* Proof: - Since 1 IN coprimes n by coprimes_has_1 - so coprimes n <> {} by MEMBER_NOT_EMPTY - and FINITE (coprimes n) by coprimes_finite - hence phi n <> 0 by CARD_EQ_0 - or 0 < phi n -*) -val phi_pos = store_thm( - "phi_pos", - ``!n. 0 < n ==> 0 < phi n``, - rpt strip_tac >> - `coprimes n <> {}` by metis_tac[coprimes_has_1, MEMBER_NOT_EMPTY] >> - `FINITE (coprimes n)` by rw[coprimes_finite] >> - `phi n <> 0` by rw[phi_def, CARD_EQ_0] >> - decide_tac); - -(* Theorem: phi 0 = 0 *) -(* Proof: - phi 0 - = CARD (coprimes 0) by phi_def - = CARD {} by coprimes_0 - = 0 by CARD_EMPTY -*) -val phi_0 = store_thm( - "phi_0", - ``phi 0 = 0``, - rw[phi_def, coprimes_0]); - -(* Theorem: (phi n = 0) <=> (n = 0) *) -(* Proof: - If part: (phi n = 0) ==> (n = 0) by phi_pos, NOT_ZERO_LT_ZERO - Only-if part: phi 0 = 0 by phi_0 -*) -val phi_eq_0 = store_thm( - "phi_eq_0", - ``!n. (phi n = 0) <=> (n = 0)``, - metis_tac[phi_0, phi_pos, NOT_ZERO_LT_ZERO]); - -(* Theorem: phi 1 = 1 *) -(* Proof: - phi 1 - = CARD (coprimes 1) by phi_def - = CARD {1} by coprimes_1 - = 1 by CARD_SING -*) -val phi_1 = store_thm( - "phi_1", - ``phi 1 = 1``, - rw[phi_def, coprimes_1]); - -(* Theorem: 1 < n ==> (phi n = totient n) *) -(* Proof: - phi n - = CARD (coprimes n) by phi_def - = CARD (Euler n ) by coprimes_eq_Euler - = totient n by totient_def -*) -val phi_eq_totient = store_thm( - "phi_eq_totient", - ``!n. 1 < n ==> (phi n = totient n)``, - rw[phi_def, totient_def, coprimes_eq_Euler]); - -(* Theorem: prime n ==> (phi n = n - 1) *) -(* Proof: - Since prime n ==> 1 < n by ONE_LT_PRIME - Hence phi n - = totient n by phi_eq_totient - = n - 1 by Euler_card_prime -*) -val phi_prime = store_thm( - "phi_prime", - ``!n. prime n ==> (phi n = n - 1)``, - rw[ONE_LT_PRIME, phi_eq_totient, Euler_card_prime]); - -(* Theorem: phi 2 = 1 *) -(* Proof: - Since prime 2 by PRIME_2 - so phi 2 = 2 - 1 = 1 by phi_prime -*) -val phi_2 = store_thm( - "phi_2", - ``phi 2 = 1``, - rw[phi_prime, PRIME_2]); - -(* Theorem: 2 < n ==> 1 < phi n *) -(* Proof: - Note 1 IN (coprimes n) by coprimes_has_1, 0 < n - and (n - 1) IN (coprimes n) by coprimes_has_last_but_1, 1 < n - and n - 1 <> 1 by 2 < n - Now FINITE (coprimes n) by coprimes_finite] - and {1; (n-1)} SUBSET (coprimes n) by SUBSET_DEF, above - Note CARD {1; (n-1)} = 2 by CARD_INSERT, CARD_EMPTY, TWO - thus 2 <= CARD (coprimes n) by CARD_SUBSET - or 1 < phi n by phi_def -*) -val phi_gt_1 = store_thm( - "phi_gt_1", - ``!n. 2 < n ==> 1 < phi n``, - rw[phi_def] >> - `0 < n /\ 1 < n /\ n - 1 <> 1` by decide_tac >> - `1 IN (coprimes n)` by rw[coprimes_has_1] >> - `(n - 1) IN (coprimes n)` by rw[coprimes_has_last_but_1] >> - `FINITE (coprimes n)` by rw[coprimes_finite] >> - `{1; (n-1)} SUBSET (coprimes n)` by rw[SUBSET_DEF] >> - `CARD {1; (n-1)} = 2` by rw[] >> - `2 <= CARD (coprimes n)` by metis_tac[CARD_SUBSET] >> - decide_tac); - -(* Theorem: phi n <= n *) -(* Proof: - Note phi n = CARD (coprimes n) by phi_def - and coprimes n SUBSET natural n by coprimes_subset - Now FINITE (natural n) by natural_finite - and CARD (natural n) = n by natural_card - so CARD (coprimes n) <= n by CARD_SUBSET -*) -val phi_le = store_thm( - "phi_le", - ``!n. phi n <= n``, - metis_tac[phi_def, coprimes_subset, natural_finite, natural_card, CARD_SUBSET]); - -(* Theorem: 1 < n ==> phi n < n *) -(* Proof: - Note phi n = CARD (coprimes n) by phi_def - and 1 < n ==> !j. j IN coprimes n ==> j < n by coprimes_element_less - but 0 NOTIN coprimes n by coprimes_no_0 - or coprimes n SUBSET (count n) DIFF {0} by SUBSET_DEF, IN_DIFF - Let s = (count n) DIFF {0}. - Note {0} SUBSET count n by SUBSET_DEF]); - so count n INTER {0} = {0} by SUBSET_INTER_ABSORPTION - Now FINITE s by FINITE_COUNT, FINITE_DIFF - and CARD s = n - 1 by CARD_COUNT, CARD_DIFF, CARD_SING - so CARD (coprimes n) <= n - 1 by CARD_SUBSET - or phi n < n by arithmetic -*) -val phi_lt = store_thm( - "phi_lt", - ``!n. 1 < n ==> phi n < n``, - rw[phi_def] >> - `!j. j IN coprimes n ==> j < n` by rw[coprimes_element_less] >> - `!j. j IN coprimes n ==> j <> 0` by metis_tac[coprimes_no_0] >> - qabbrev_tac `s = (count n) DIFF {0}` >> - `coprimes n SUBSET s` by rw[SUBSET_DEF, Abbr`s`] >> - `{0} SUBSET count n` by rw[SUBSET_DEF] >> - `count n INTER {0} = {0}` by metis_tac[SUBSET_INTER_ABSORPTION, INTER_COMM] >> - `FINITE s` by rw[Abbr`s`] >> - `CARD s = n - 1` by rw[Abbr`s`] >> - `CARD (coprimes n) <= n - 1` by metis_tac[CARD_SUBSET] >> - decide_tac); - -(* ------------------------------------------------------------------------- *) -(* Divisors *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of divisors of a number. *) -Definition divisors_def[nocompute]: - divisors n = {d | 0 < d /\ d <= n /\ d divides n} -End -(* use [nocompute] as this is not computationally effective. *) -(* Note: use of 0 < d to have positive divisors, as only 0 divides 0. *) -(* Note: use of d <= n to give divisors_0 = {}, since ALL_DIVIDES_0. *) -(* Note: for 0 < n, d <= n is redundant, as DIVIDES_LE implies it. *) - -(* Theorem: d IN divisors n <=> 0 < d /\ d <= n /\ d divides n *) -(* Proof: by divisors_def *) -Theorem divisors_element: - !n d. d IN divisors n <=> 0 < d /\ d <= n /\ d divides n -Proof - rw[divisors_def] -QED - -(* Theorem: 0 < n ==> !d. d IN divisors n <=> d divides n *) -(* Proof: - If part: d IN divisors n ==> d divides n - This is true by divisors_element - Only-if part: 0 < n /\ d divides n ==> d IN divisors n - Since 0 < n /\ d divides n - ==> 0 < d /\ d <= n by divides_pos - Hence d IN divisors n by divisors_element -*) -Theorem divisors_element_alt: - !n. 0 < n ==> !d. d IN divisors n <=> d divides n -Proof - metis_tac[divisors_element, divides_pos] -QED - -(* Theorem: d IN divisors n ==> 0 < n *) -(* Proof: - Note 0 < d /\ d <= n /\ d divides n by divisors_def - so 0 < n by inequality -*) -Theorem divisors_has_element: - !n d. d IN divisors n ==> 0 < n -Proof - simp[divisors_def] -QED - -(* Theorem: 0 < n ==> 1 IN (divisors n) *) -(* Proof: - Note 1 divides n by ONE_DIVIDES_ALL - Hence 1 IN (divisors n) by divisors_element_alt -*) -Theorem divisors_has_1: - !n. 0 < n ==> 1 IN (divisors n) -Proof - simp[divisors_element_alt] -QED - -(* Theorem: 0 < n ==> n IN (divisors n) *) -(* Proof: - Note n divides n by DIVIDES_REFL - Hence n IN (divisors n) by divisors_element_alt -*) -Theorem divisors_has_last: - !n. 0 < n ==> n IN (divisors n) -Proof - simp[divisors_element_alt] -QED - -(* Theorem: 0 < n ==> divisors n <> {} *) -(* Proof: by divisors_has_last, MEMBER_NOT_EMPTY *) -Theorem divisors_not_empty: - !n. 0 < n ==> divisors n <> {} -Proof - metis_tac[divisors_has_last, MEMBER_NOT_EMPTY] -QED - -(* Theorem: divisors 0 = {} *) -(* Proof: by divisors_def, 0 < d /\ d <= 0 is impossible. *) -Theorem divisors_0: - divisors 0 = {} -Proof - simp[divisors_def] -QED - -(* Theorem: divisors 1 = {1} *) -(* Proof: by divisors_def, 0 < d /\ d <= 1 ==> d = 1. *) -Theorem divisors_1: - divisors 1 = {1} -Proof - rw[divisors_def, EXTENSION] -QED - -(* Theorem: divisors n = {} <=> n = 0 *) -(* Proof: - By EXTENSION, this is to show: - (1) divisors n = {} ==> n = 0 - By contradiction, suppose n <> 0. - Then 1 IN (divisors n) by divisors_has_1 - This contradicts divisors n = {} by MEMBER_NOT_EMPTY - (2) n = 0 ==> divisors n = {} - This is true by divisors_0 -*) -Theorem divisors_eq_empty: - !n. divisors n = {} <=> n = 0 -Proof - rw[EQ_IMP_THM] >- - metis_tac[divisors_has_1, MEMBER_NOT_EMPTY, NOT_ZERO] >> - simp[divisors_0] -QED - -(* Idea: a method to evaluate divisors. *) - -(* Theorem: divisors n = IMAGE (\j. if (j + 1) divides n then j + 1 else 1) (count n) *) -(* Proof: - Let f = \j. if (j + 1) divides n then j + 1 else 1. - If n = 0, - divisors 0 - = {d | 0 < d /\ d <= 0 /\ d divides 0} by divisors_def - = {} by 0 < d /\ d <= 0 - = IMAGE f {} by IMAGE_EMPTY - = IMAGE f (count 0) by COUNT_0 - If n <> 0, - divisors n - = {d | 0 < d /\ d <= n /\ d divides n} by divisors_def - = {d | d <> 0 /\ d <= n /\ d divides n} by 0 < d - = {k + 1 | (k + 1) <= n /\ (k + 1) divides n} - by num_CASES, d <> 0 - = {k + 1 | k < n /\ (k + 1) divides n} by arithmetic - = IMAGE f {k | k < n} by IMAGE_DEF - = IMAGE f (count n) by count_def -*) -Theorem divisors_eqn[compute]: - !n. divisors n = IMAGE (\j. if (j + 1) divides n then j + 1 else 1) (count n) -Proof - (rw[divisors_def, EXTENSION, EQ_IMP_THM] >> rw[]) >> - `?k. x = SUC k` by metis_tac[num_CASES, NOT_ZERO] >> - qexists_tac `k` >> - fs[ADD1] -QED - -(* -> EVAL ``divisors 3``; = {3; 1}: thm -> EVAL ``divisors 4``; = {4; 2; 1}: thm -> EVAL ``divisors 5``; = {5; 1}: thm -> EVAL ``divisors 6``; = {6; 3; 2; 1}: thm -> EVAL ``divisors 7``; = {7; 1}: thm -> EVAL ``divisors 8``; = {8; 4; 2; 1}: thm -> EVAL ``divisors 9``; = {9; 3; 1}: thm -*) - -(* Idea: each factor of a product divides the product. *) - -(* Theorem: 0 < n /\ n = p * q ==> p IN divisors n /\ q IN divisors n *) -(* Proof: - Note 0 < p /\ 0 < q by MULT_EQ_0 - so p <= n /\ q <= n by arithmetic - and p divides n by divides_def - and q divides n by divides_def, MULT_COMM - ==> p IN divisors n /\ - q IN divisors n by divisors_element_alt, 0 < n -*) -Theorem divisors_has_factor: - !n p q. 0 < n /\ n = p * q ==> p IN divisors n /\ q IN divisors n -Proof - (rw[divisors_element_alt] >> metis_tac[MULT_EQ_0, NOT_ZERO]) -QED - -(* Idea: when factor divides, its cofactor also divides. *) - -(* Theorem: d IN divisors n ==> (n DIV d) IN divisors n *) -(* Proof: - Note 0 < d /\ d <= n /\ d divides n by divisors_def - and 0 < n by 0 < d /\ d <= n - so 0 < n DIV d by DIV_POS, 0 < n - and n DIV d <= n by DIV_LESS_EQ, 0 < d - and n DIV d divides n by DIVIDES_COFACTOR, 0 < d - so (n DIV d) IN divisors n by divisors_def -*) -Theorem divisors_has_cofactor: - !n d. d IN divisors n ==> (n DIV d) IN divisors n -Proof - simp [divisors_def] >> - ntac 3 strip_tac >> - `0 < n` by decide_tac >> - rw[DIV_POS, DIV_LESS_EQ, DIVIDES_COFACTOR] -QED - -(* Theorem: (divisors n) DELETE n = {m | 0 < m /\ m < n /\ m divides n} *) -(* Proof: - (divisors n) DELETE n - = {m | 0 < m /\ m <= n /\ m divides n} DELETE n by divisors_def - = {m | 0 < m /\ m <= n /\ m divides n} DIFF {n} by DELETE_DEF - = {m | 0 < m /\ m <> n /\ m <= n /\ m divides n} by IN_DIFF - = {m | 0 < m /\ m < n /\ m divides n} by LESS_OR_EQ -*) -Theorem divisors_delete_last: - !n. (divisors n) DELETE n = {m | 0 < m /\ m < n /\ m divides n} -Proof - rw[divisors_def, EXTENSION, EQ_IMP_THM] -QED - -(* Theorem: d IN (divisors n) ==> 0 < d *) -(* Proof: by divisors_def. *) -Theorem divisors_nonzero: - !n d. d IN (divisors n) ==> 0 < d -Proof - simp[divisors_def] -QED - -(* Theorem: (divisors n) SUBSET (natural n) *) -(* Proof: - By SUBSET_DEF, this is to show: - x IN (divisors n) ==> x IN (natural n) - x IN (divisors n) - ==> 0 < x /\ x <= n /\ x divides n by divisors_element - ==> 0 < x /\ x <= n - ==> x IN (natural n) by natural_element -*) -Theorem divisors_subset_natural: - !n. (divisors n) SUBSET (natural n) -Proof - rw[divisors_element, natural_element, SUBSET_DEF] -QED - -(* Theorem: FINITE (divisors n) *) -(* Proof: - Since (divisors n) SUBSET (natural n) by divisors_subset_natural - and FINITE (naturnal n) by natural_finite - so FINITE (divisors n) by SUBSET_FINITE -*) -Theorem divisors_finite: - !n. FINITE (divisors n) -Proof - metis_tac[divisors_subset_natural, natural_finite, SUBSET_FINITE] -QED - -(* Theorem: BIJ (\d. n DIV d) (divisors n) (divisors n) *) -(* Proof: - By BIJ_DEF, INJ_DEF, SURJ_DEF, this is to show: - (1) d IN divisors n ==> n DIV d IN divisors n - This is true by divisors_has_cofactor - (2) d IN divisors n /\ d' IN divisors n /\ n DIV d = n DIV d' ==> d = d' - d IN divisors n ==> d divides n /\ 0 < d by divisors_element - d' IN divisors n ==> d' divides n /\ 0 < d' by divisors_element - Also d IN divisors n ==> 0 < n by divisors_has_element - Hence n = (n DIV d) * d and n = (n DIV d') * d' by DIVIDES_EQN - giving (n DIV d) * d = (n DIV d') * d' - Now (n DIV d) <> 0, otherwise contradicts n <> 0 by MULT - Hence d = d' by EQ_MULT_LCANCEL - (3) same as (1), true by divisors_has_cofactor - (4) x IN divisors n ==> ?d. d IN divisors n /\ (n DIV d = x) - Note x IN divisors n ==> x divides n by divisors_element - and 0 < n by divisors_has_element - Let d = n DIV x. - Then d IN divisors n by divisors_has_cofactor - and n DIV d = n DIV (n DIV x) = x by divide_by_cofactor, 0 < n -*) -Theorem divisors_divisors_bij: - !n. (\d. n DIV d) PERMUTES divisors n -Proof - rw[BIJ_DEF, INJ_DEF, SURJ_DEF] >- - rw[divisors_has_cofactor] >- - (`n = (n DIV d) * d` by metis_tac[DIVIDES_EQN, divisors_element] >> - `n = (n DIV d') * d'` by metis_tac[DIVIDES_EQN, divisors_element] >> - `0 < n` by metis_tac[divisors_has_element] >> - `n DIV d <> 0` by metis_tac[MULT, NOT_ZERO] >> - metis_tac[EQ_MULT_LCANCEL]) >- - rw[divisors_has_cofactor] >> - `0 < n` by metis_tac[divisors_has_element] >> - metis_tac[divisors_element, divisors_has_cofactor, divide_by_cofactor] -QED - -(* ------------------------------------------------------------------------- *) -(* An upper bound for divisors. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: if a divisor of n is less or equal to (SQRT n), its cofactor is more or equal to (SQRT n) *) - -(* Theorem: 0 < p /\ p divides n /\ p <= SQRT n ==> SQRT n <= (n DIV p) *) -(* Proof: - Let m = SQRT n, then p <= m. - By contradiction, suppose (n DIV p) < m. - Then n = (n DIV p) * p by DIVIDES_EQN, 0 < p - <= (n DIV p) * m by p <= m - < m * m by (n DIV p) < m - <= n by SQ_SQRT_LE - giving n < n, which is a contradiction. -*) -Theorem divisor_le_cofactor_ge: - !n p. 0 < p /\ p divides n /\ p <= SQRT n ==> SQRT n <= (n DIV p) -Proof - rpt strip_tac >> - qabbrev_tac `m = SQRT n` >> - spose_not_then strip_assume_tac >> - `n = (n DIV p) * p` by rfs[DIVIDES_EQN] >> - `(n DIV p) * p <= (n DIV p) * m` by fs[] >> - `(n DIV p) * m < m * m` by fs[] >> - `m * m <= n` by simp[SQ_SQRT_LE, Abbr`m`] >> - decide_tac -QED - -(* Idea: if a divisor of n is greater than (SQRT n), its cofactor is less or equal to (SQRT n) *) - -(* Theorem: 0 < p /\ p divides n /\ SQRT n < p ==> (n DIV p) <= SQRT n *) -(* Proof: - Let m = SQRT n, then m < p. - By contradiction, suppose m < (n DIV p). - Let q = (n DIV p). - Then SUC m <= p, SUC m <= q by m < p, m < q - and n = q * p by DIVIDES_EQN, 0 < p - >= (SUC m) * (SUC m) by LESS_MONO_MULT2 - = (SUC m) ** 2 by EXP_2 - > n by SQRT_PROPERTY - which is a contradiction. -*) -Theorem divisor_gt_cofactor_le: - !n p. 0 < p /\ p divides n /\ SQRT n < p ==> (n DIV p) <= SQRT n -Proof - rpt strip_tac >> - qabbrev_tac `m = SQRT n` >> - spose_not_then strip_assume_tac >> - `n = (n DIV p) * p` by rfs[DIVIDES_EQN] >> - qabbrev_tac `q = n DIV p` >> - `SUC m <= p /\ SUC m <= q` by decide_tac >> - `(SUC m) * (SUC m) <= q * p` by simp[LESS_MONO_MULT2] >> - `n < (SUC m) * (SUC m)` by metis_tac[SQRT_PROPERTY, EXP_2] >> - decide_tac -QED - -(* Idea: for (divisors n), the map (\j. n DIV j) is injective. *) - -(* Theorem: INJ (\j. n DIV j) (divisors n) univ(:num) *) -(* Proof: - By INJ_DEF, this is to show: - (1) !x. x IN (divisors n) ==> (\j. n DIV j) x IN univ(:num) - True by types, n DIV j is a number, with type :num. - (2) !x y. x IN (divisors n) /\ y IN (divisors n) /\ n DIV x = n DIV y ==> x = y - Note x divides n /\ 0 < x /\ x <= n by divisors_def - and y divides n /\ 0 < y /\ x <= n by divisors_def - Let p = n DIV x, q = n DIV y. - Note 0 < n by divisors_has_element - then 0 < p, 0 < q by DIV_POS, 0 < n - Then n = p * x = q * y by DIVIDES_EQN, 0 < x, 0 < y - But p = q by given - so x = y by EQ_MULT_LCANCEL -*) -Theorem divisors_cofactor_inj: - !n. INJ (\j. n DIV j) (divisors n) univ(:num) -Proof - rw[INJ_DEF, divisors_def] >> - `n = n DIV j * j` by fs[GSYM DIVIDES_EQN] >> - `n = n DIV j' * j'` by fs[GSYM DIVIDES_EQN] >> - `0 < n` by fs[GSYM divisors_has_element] >> - metis_tac[EQ_MULT_LCANCEL, DIV_POS, NOT_ZERO] -QED - -(* Idea: an upper bound for CARD (divisors n). - -To prove: 0 < n ==> CARD (divisors n) <= 2 * SQRT n -Idea of proof: - Consider the two sets, - s = {x | x IN divisors n /\ x <= SQRT n} - t = {x | x IN divisors n /\ SQRT n <= x} - Note s SUBSET (natural (SQRT n)), so CARD s <= SQRT n. - Also t SUBSET (natural (SQRT n)), so CARD t <= SQRT n. - There is a bijection between the two parts: - BIJ (\j. n DIV j) s t - Now divisors n = s UNION t - CARD (divisors n) - = CARD s + CARD t - CARD (s INTER t) - <= CARD s + CARD t - <= SQRT n + SQRT n - = 2 * SQRT n - - The BIJ part will be quite difficult. - So the actual proof is a bit different. -*) - -(* Theorem: CARD (divisors n) <= 2 * SQRT n *) -(* Proof: - Let m = SQRT n, - d = divisors n, - s = {x | x IN d /\ x <= m}, - f = \j. n DIV j, - t = IMAGE f s. - - Claim: s SUBSET natural m - Proof: By SUBSET_DEF, this is to show: - x IN d /\ x <= m ==> ?y. x = SUC y /\ y < m - Note 0 < x by divisors_nonzero - Let y = PRE x. - Then x = SUC (PRE x) by SUC_PRE - and PRE x < x by PRE_LESS - so PRE x < m by inequality, x <= m - - Claim: BIJ f s t - Proof: Note s SUBSET d by SUBSET_DEF - and INJ f d univ(:num) by divisors_cofactor_inj - so INJ f s univ(:num) by INJ_SUBSET, SUBSET_REFL - ==> BIJ f s t by INJ_IMAGE_BIJ_ALT - - Claim: d = s UNION t - Proof: By EXTENSION, EQ_IMP_THM, this is to show: - (1) x IN divisors n ==> x <= m \/ ?j. x = n DIV j /\ j IN divisors n /\ j <= m - If x <= m, this is trivial. - Otherwise, m < x. - Let j = n DIV x. - Then x = n DIV (n DIV x) by divide_by_cofactor - and (n DIV j) IN divisors n by divisors_has_cofactor - and (n DIV j) <= m by divisor_gt_cofactor_le - (2) j IN divisors n ==> n DIV j IN divisors n - This is true by divisors_has_cofactor - - Now FINITE (natural m) by natural_finite - so FINITE s by SUBSET_FINITE - and FINITE t by IMAGE_FINITE - so CARD s <= m by CARD_SUBSET, natural_card - Also CARD t = CARD s by FINITE_BIJ_CARD - - CARD d <= CARD s + CARD t by CARD_UNION_LE, d = s UNION t - <= m + m by above - = 2 * m by arithmetic -*) -Theorem divisors_card_upper: - !n. CARD (divisors n) <= 2 * SQRT n -Proof - rpt strip_tac >> - qabbrev_tac `m = SQRT n` >> - qabbrev_tac `d = divisors n` >> - qabbrev_tac `s = {x | x IN d /\ x <= m}` >> - qabbrev_tac `f = \j. n DIV j` >> - qabbrev_tac `t = (IMAGE f s)` >> - `s SUBSET (natural m)` by - (rw[SUBSET_DEF, Abbr`s`] >> - `0 < x` by metis_tac[divisors_nonzero] >> - qexists_tac `PRE x` >> - simp[]) >> - `BIJ f s t` by - (simp[Abbr`t`] >> - irule INJ_IMAGE_BIJ_ALT >> - `s SUBSET d` by rw[SUBSET_DEF, Abbr`s`] >> - `INJ f d univ(:num)` by metis_tac[divisors_cofactor_inj] >> - metis_tac[INJ_SUBSET, SUBSET_REFL]) >> - `d = s UNION t` by - (rw[EXTENSION, Abbr`d`, Abbr`s`, Abbr`t`, Abbr`f`, EQ_IMP_THM] >| [ - (Cases_on `x <= m` >> simp[]) >> - qexists_tac `n DIV x` >> - `0 < x /\ x <= n /\ x divides n` by fs[divisors_element] >> - simp[divide_by_cofactor, divisors_has_cofactor] >> - `m < x` by decide_tac >> - simp[divisor_gt_cofactor_le, Abbr`m`], - simp[divisors_has_cofactor] - ]) >> - `FINITE (natural m)` by simp[natural_finite] >> - `FINITE s /\ FINITE t` by metis_tac[SUBSET_FINITE, IMAGE_FINITE] >> - `CARD s <= m` by metis_tac[CARD_SUBSET, natural_card] >> - `CARD t = CARD s` by metis_tac[FINITE_BIJ_CARD] >> - `CARD d <= CARD s + CARD t` by metis_tac[CARD_UNION_LE] >> - decide_tac -QED - -(* This is a remarkable result! *) - - -(* ------------------------------------------------------------------------- *) -(* Gauss' Little Theorem *) -(* ------------------------------------------------------------------------- *) -(* ------------------------------------------------------------------------- *) -(* Gauss' Little Theorem: sum of phi over divisors *) -(* ------------------------------------------------------------------------- *) -(* ------------------------------------------------------------------------- *) -(* Gauss' Little Theorem: A general theory on sum over divisors *) -(* ------------------------------------------------------------------------- *) - -(* -Let n = 6. (divisors 6) = {1, 2, 3, 6} - IMAGE coprimes (divisors 6) -= {coprimes 1, coprimes 2, coprimes 3, coprimes 6} -= {{1}, {1}, {1, 2}, {1, 5}} <-- will collapse - IMAGE (preimage (gcd 6) (count 6)) (divisors 6) -= {{preimage in count 6 of those gcd 6 j = 1}, - {preimage in count 6 of those gcd 6 j = 2}, - {preimage in count 6 of those gcd 6 j = 3}, - {preimage in count 6 of those gcd 6 j = 6}} -= {{1, 5}, {2, 4}, {3}, {6}} -= {1x{1, 5}, 2x{1, 2}, 3x{1}, 6x{1}} -!s. s IN (IMAGE (preimage (gcd n) (count n)) (divisors n)) -==> ?d. d divides n /\ d < n /\ s = preimage (gcd n) (count n) d -==> ?d. d divides n /\ d < n /\ s = IMAGE (TIMES d) (coprimes ((gcd n d) DIV d)) - - IMAGE (feq_class (count 6) (gcd 6)) (divisors 6) -= {{feq_class in count 6 of those gcd 6 j = 1}, - {feq_class in count 6 of those gcd 6 j = 2}, - {feq_class in count 6 of those gcd 6 j = 3}, - {feq_class in count 6 of those gcd 6 j = 6}} -= {{1, 5}, {2, 4}, {3}, {6}} -= {1x{1, 5}, 2x{1, 2}, 3x{1}, 6x{1}} -That is: CARD {1, 5} = CARD (coprime 6) = CARD (coprime (6 DIV 1)) - CARD {2, 4} = CARD (coprime 3) = CARD (coprime (6 DIV 2)) - CARD {3} = CARD (coprime 2) = CARD (coprime (6 DIV 3))) - CARD {6} = CARD (coprime 1) = CARD (coprime (6 DIV 6))) - -*) -(* Note: - In general, what is the condition for: SIGMA f s = SIGMA g t ? - Conceptually, - SIGMA f s = f s1 + f s2 + f s3 + ... + f sn - and SIGMA g t = g t1 + g t2 + g t3 + ... + g tm - -SUM_IMAGE_eq_SUM_MAP_SET_TO_LIST - -Use disjoint_bigunion_card -|- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P) -If a partition P = {s | condition on s} the element s = IMAGE g t -e.g. P = {{1, 5} {2, 4} {3} {6}} - = {IMAGE (TIMES 1) (coprimes 6/1), - IMAGE (TIMES 2) (coprimes 6/2), - IMAGE (TIMES 3) (coprimes 6/3), - IMAGE (TIMES 6) (coprimes 6/6)} - = IMAGE (\d. TIMES d o coprimes (6/d)) {1, 2, 3, 6} - -*) - -(* Theorem: d divides n ==> !j. j IN gcd_matches n d ==> j DIV d IN coprimes_by n d *) -(* Proof: - When n = 0, gcd_matches 0 d = {} by gcd_matches_0, hence trivially true. - Otherwise, - By coprimes_by_def, this is to show: - 0 < n /\ d divides n ==> !j. j IN gcd_matches n d ==> j DIV d IN coprimes (n DIV d) - Note j IN gcd_matches n d - ==> d divides j by gcd_matches_element_divides - Also d IN gcd_matches n d by gcd_matches_has_divisor - so 0 < d /\ (d = gcd j n) by gcd_matches_element - or d <> 0 /\ (d = gcd n j) by GCD_SYM - With the given d divides n, - j = d * (j DIV d) by DIVIDES_EQN, MULT_COMM, 0 < d - n = d * (n DIV d) by DIVIDES_EQN, MULT_COMM, 0 < d - Hence d = d * gcd (n DIV d) (j DIV d) by GCD_COMMON_FACTOR - or d * 1 = d * gcd (n DIV d) (j DIV d) by MULT_RIGHT_1 - giving 1 = gcd (n DIV d) (j DIV d) by EQ_MULT_LCANCEL, d <> 0 - or coprime (j DIV d) (n DIV d) by GCD_SYM - Also j IN natural n by gcd_matches_subset, SUBSET_DEF - Hence 0 < j DIV d /\ j DIV d <= n DIV d by natural_cofactor_natural_reduced - or j DIV d IN coprimes (n DIV d) by coprimes_element -*) -val gcd_matches_divisor_element = store_thm( - "gcd_matches_divisor_element", - ``!n d. d divides n ==> !j. j IN gcd_matches n d ==> j DIV d IN coprimes_by n d``, - rpt strip_tac >> - Cases_on `n = 0` >- - metis_tac[gcd_matches_0, NOT_IN_EMPTY] >> - `0 < n` by decide_tac >> - rw[coprimes_by_def] >> - `d divides j` by metis_tac[gcd_matches_element_divides] >> - `0 < d /\ 0 < j /\ j <= n /\ (d = gcd n j)` by metis_tac[gcd_matches_has_divisor, gcd_matches_element, GCD_SYM] >> - `d <> 0` by decide_tac >> - `(j = d * (j DIV d)) /\ (n = d * (n DIV d))` by metis_tac[DIVIDES_EQN, MULT_COMM] >> - `coprime (n DIV d) (j DIV d)` by metis_tac[GCD_COMMON_FACTOR, MULT_RIGHT_1, EQ_MULT_LCANCEL] >> - `0 < j DIV d /\ j DIV d <= n DIV d` by metis_tac[natural_cofactor_natural_reduced, natural_element] >> - metis_tac[coprimes_element, GCD_SYM]); - -(* Theorem: d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes_by n d) *) -(* Proof: - When n = 0, gcd_matches 0 d = {} by gcd_matches_0 - and coprimes_by 0 d = {} by coprimes_by_0, hence trivially true. - Otherwise, - By definitions, this is to show: - (1) j IN gcd_matches n d ==> j DIV d IN coprimes_by n d - True by gcd_matches_divisor_element. - (2) j IN gcd_matches n d /\ j' IN gcd_matches n d /\ j DIV d = j' DIV d ==> j = j' - Note j IN gcd_matches n d /\ j' IN gcd_matches n d - ==> d divides j /\ d divides j' by gcd_matches_element_divides - Also d IN (gcd_matches n d) by gcd_matches_has_divisor - so 0 < d by gcd_matches_element - Thus j = (j DIV d) * d by DIVIDES_EQN, 0 < d - and j' = (j' DIV d) * d by DIVIDES_EQN, 0 < d - Since j DIV d = j' DIV d, j = j'. - (3) same as (1), true by gcd_matches_divisor_element, - (4) d divides n /\ x IN coprimes_by n d ==> ?j. j IN gcd_matches n d /\ (j DIV d = x) - Note x IN coprimes (n DIV d) by coprimes_by_def - ==> 0 < x /\ x <= n DIV d /\ (coprime x (n DIV d)) by coprimes_element - And d divides n /\ 0 < n - ==> 0 < d /\ d <> 0 by ZERO_DIVIDES, 0 < n - Giving (x * d) DIV d = x by MULT_DIV, 0 < d - Let j = x * d. so j DIV d = x by above - Then d divides j by divides_def - ==> j = (j DIV d) * d by DIVIDES_EQN, 0 < d - Note d divides n - ==> n = (n DIV d) * d by DIVIDES_EQN, 0 < d - Hence gcd j n - = gcd (d * (j DIV d)) (d * (n DIV d)) by MULT_COMM - = d * gcd (j DIV d) (n DIV d) by GCD_COMMON_FACTOR - = d * gcd x (n DIV d) by x = j DIV d - = d * 1 by coprime x (n DIV d) - = d by MULT_RIGHT_1 - Since j = x * d, 0 < j by MULT_EQ_0, 0 < x, 0 < d - Also x <= n DIV d - means j DIV d <= n DIV d by x = j DIV d - so (j DIV d) * d <= (n DIV d) * d by LE_MULT_RCANCEL, d <> 0 - or j <= n by above - Hence j IN gcd_matches n d by gcd_matches_element -*) -val gcd_matches_bij_coprimes_by = store_thm( - "gcd_matches_bij_coprimes_by", - ``!n d. d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes_by n d)``, - rpt strip_tac >> - Cases_on `n = 0` >| [ - `gcd_matches n d = {}` by rw[gcd_matches_0] >> - `coprimes_by n d = {}` by rw[coprimes_by_0] >> - rw[], - `0 < n` by decide_tac >> - rw[BIJ_DEF, INJ_DEF, SURJ_DEF, EQ_IMP_THM] >- - rw[GSYM gcd_matches_divisor_element] >- - metis_tac[gcd_matches_element_divides, gcd_matches_has_divisor, gcd_matches_element, DIVIDES_EQN] >- - rw[GSYM gcd_matches_divisor_element] >> - `0 < x /\ x <= n DIV d /\ (coprime x (n DIV d))` by metis_tac[coprimes_by_def, coprimes_element] >> - `0 < d /\ d <> 0` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> - `(x * d) DIV d = x` by rw[MULT_DIV] >> - qabbrev_tac `j = x * d` >> - `d divides j` by metis_tac[divides_def] >> - `(n = (n DIV d) * d) /\ (j = (j DIV d) * d)` by rw[GSYM DIVIDES_EQN] >> - `gcd j n = d` by metis_tac[GCD_COMMON_FACTOR, MULT_COMM, MULT_RIGHT_1] >> - `0 < j` by metis_tac[MULT_EQ_0, NOT_ZERO] >> - `j <= n` by metis_tac[LE_MULT_RCANCEL] >> - metis_tac[gcd_matches_element] - ]); - -(* Theorem: 0 < n /\ d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes (n DIV d)) *) -(* Proof: by gcd_matches_bij_coprimes_by, coprimes_by_by_divisor *) -val gcd_matches_bij_coprimes = store_thm( - "gcd_matches_bij_coprimes", - ``!n d. 0 < n /\ d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes (n DIV d))``, - metis_tac[gcd_matches_bij_coprimes_by, coprimes_by_by_divisor]); - -(* Note: it is not useful to show: - CARD o (gcd_matches n) = CARD o coprimes, - as FUN_EQ_THM will demand: CARD (gcd_matches n x) = CARD (coprimes x), - which is not possible. -*) - -(* Theorem: divisors n = IMAGE (gcd n) (natural n) *) -(* Proof: - divisors n - = {d | 0 < d /\ d <= n /\ d divides n} by divisors_def - = {d | d IN (natural n) /\ d divides n} by natural_element - = {d | d IN (natural n) /\ (gcd d n = d)} by divides_iff_gcd_fix - = {d | d IN (natural n) /\ (gcd n d = d)} by GCD_SYM - = {gcd n d | d | d IN (natural n)} by replacemnt - = IMAGE (gcd n) (natural n) by IMAGE_DEF - The replacemnt requires: - d IN (natural n) ==> gcd n d IN (natural n) - d IN (natural n) ==> gcd n (gcd n d) = gcd n d - which are given below. - - Or, by divisors_def, natuarl_elemnt, IN_IMAGE, this is to show: - (1) 0 < x /\ x <= n /\ x divides n ==> ?y. (x = gcd n y) /\ 0 < y /\ y <= n - Note x divides n ==> gcd x n = x by divides_iff_gcd_fix - or gcd n x = x by GCD_SYM - Take this x, and the result follows. - (2) 0 < y /\ y <= n ==> 0 < gcd n y /\ gcd n y <= n /\ gcd n y divides n - Note 0 < n by arithmetic - and gcd n y divides n by GCD_IS_GREATEST_COMMON_DIVISOR, 0 < n - and 0 < gcd n y by GCD_EQ_0, n <> 0 - and gcd n y <= n by DIVIDES_LE, 0 < n -*) -Theorem divisors_eq_gcd_image: - !n. divisors n = IMAGE (gcd n) (natural n) -Proof - rw_tac std_ss[divisors_def, GSPECIFICATION, EXTENSION, IN_IMAGE, natural_element, EQ_IMP_THM] >| [ - `0 < n` by decide_tac >> - metis_tac[divides_iff_gcd_fix, GCD_SYM], - metis_tac[GCD_EQ_0, NOT_ZERO], - `0 < n` by decide_tac >> - metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_LE], - metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] - ] -QED - -(* Theorem: feq_class (gcd n) (natural n) d = gcd_matches n d *) -(* Proof: - feq_class (gcd n) (natural n) d - = {x | x IN natural n /\ (gcd n x = d)} by feq_class_def - = {j | j IN natural n /\ (gcd j n = d)} by GCD_SYM - = gcd_matches n d by gcd_matches_def -*) -val gcd_eq_equiv_class = store_thm( - "gcd_eq_equiv_class", - ``!n d. feq_class (gcd n) (natural n) d = gcd_matches n d``, - rewrite_tac[gcd_matches_def] >> - rw[EXTENSION, GCD_SYM, in_preimage]); - -(* Theorem: feq_class (gcd n) (natural n) = gcd_matches n *) -(* Proof: by FUN_EQ_THM, gcd_eq_equiv_class *) -val gcd_eq_equiv_class_fun = store_thm( - "gcd_eq_equiv_class_fun", - ``!n. feq_class (gcd n) (natural n) = gcd_matches n``, - rw[FUN_EQ_THM, gcd_eq_equiv_class]); - -(* Theorem: partition (feq (gcd n)) (natural n) = IMAGE (gcd_matches n) (divisors n) *) -(* Proof: - partition (feq (gcd n)) (natural n) - = IMAGE (equiv_class (feq (gcd n)) (natural n)) (natural n) by partition_elements - = IMAGE ((feq_class (gcd n) (natural n)) o (gcd n)) (natural n) by feq_class_fun - = IMAGE ((gcd_matches n) o (gcd n)) (natural n) by gcd_eq_equiv_class_fun - = IMAGE (gcd_matches n) (IMAGE (gcd n) (natural n)) by IMAGE_COMPOSE - = IMAGE (gcd_matches n) (divisors n) by divisors_eq_gcd_image, 0 < n -*) -Theorem gcd_eq_partition_by_divisors: - !n. partition (feq (gcd n)) (natural n) = IMAGE (gcd_matches n) (divisors n) -Proof - rpt strip_tac >> - qabbrev_tac `f = gcd n` >> - qabbrev_tac `s = natural n` >> - `partition (feq f) s = IMAGE (equiv_class (feq f) s) s` by rw[partition_elements] >> - `_ = IMAGE ((feq_class f s) o f) s` by rw[feq_class_fun] >> - `_ = IMAGE ((gcd_matches n) o f) s` by rw[gcd_eq_equiv_class_fun, Abbr`f`, Abbr`s`] >> - `_ = IMAGE (gcd_matches n) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> - `_ = IMAGE (gcd_matches n) (divisors n)` by rw[divisors_eq_gcd_image, Abbr`f`, Abbr`s`] >> - simp[] -QED - -(* Theorem: (feq (gcd n)) equiv_on (natural n) *) -(* Proof: - By feq_equiv |- !s f. feq f equiv_on s - Taking s = upto n, f = natural n. -*) -val gcd_eq_equiv_on_natural = store_thm( - "gcd_eq_equiv_on_natural", - ``!n. (feq (gcd n)) equiv_on (natural n)``, - rw[feq_equiv]); - -(* Theorem: SIGMA f (natural n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) *) -(* Proof: - Let g = gcd n, s = natural n. - Since FINITE s by natural_finite - and (feq g) equiv_on s by feq_equiv - The result follows by set_sigma_by_partition -*) -val sum_over_natural_by_gcd_partition = store_thm( - "sum_over_natural_by_gcd_partition", - ``!f n. SIGMA f (natural n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n))``, - rw[feq_equiv, natural_finite, set_sigma_by_partition]); - -(* Theorem: SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) *) -(* Proof: - SIGMA f (natural n) - = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) by sum_over_natural_by_gcd_partition - = SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) by gcd_eq_partition_by_divisors -*) -Theorem sum_over_natural_by_divisors: - !f n. SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) -Proof - simp[sum_over_natural_by_gcd_partition, gcd_eq_partition_by_divisors] -QED - -(* Theorem: INJ (gcd_matches n) (divisors n) univ(num) *) -(* Proof: - By INJ_DEF, this is to show: - x IN divisors n /\ y IN divisors n /\ gcd_matches n x = gcd_matches n y ==> x = y - Note 0 < x /\ x <= n /\ x divides n by divisors_def - also 0 < y /\ y <= n /\ y divides n by divisors_def - Hence (gcd x n = x) /\ (gcd y n = y) by divides_iff_gcd_fix - ==> x IN gcd_matches n x by gcd_matches_element - so x IN gcd_matches n y by gcd_matches n x = gcd_matches n y - with gcd x n = y by gcd_matches_element - Therefore y = gcd x n = x. -*) -Theorem gcd_matches_from_divisors_inj: - !n. INJ (gcd_matches n) (divisors n) univ(:num -> bool) -Proof - rw[INJ_DEF] >> - fs[divisors_def] >> - `(gcd x n = x) /\ (gcd y n = y)` by rw[GSYM divides_iff_gcd_fix] >> - metis_tac[gcd_matches_element] -QED - -(* Theorem: CARD o (gcd_matches n) = CARD o (coprimes_by n) *) -(* Proof: - By composition and FUN_EQ_THM, this is to show: - !x. CARD (gcd_matches n x) = CARD (coprimes_by n x) - If x divides n, - Then BIJ (\j. j DIV x) (gcd_matches n x) (coprimes_by n x) by gcd_matches_bij_coprimes_by - Also FINITE (gcd_matches n x) by gcd_matches_finite - and FINITE (coprimes_by n x) by coprimes_by_finite - Hence CARD (gcd_matches n x) = CARD (coprimes_by n x) by FINITE_BIJ_CARD_EQ - If ~(x divides n), - If n = 0, - then gcd_matches 0 x = {} by gcd_matches_0 - and coprimes_by 0 x = {} by coprimes_by_0 - Hence true. - If n <> 0, - then gcd_matches n x = {} by gcd_matches_eq_empty, 0 < n - and coprimes_by n x = {} by coprimes_by_eq_empty, 0 < n - Hence CARD {} = CARD {}. -*) -val gcd_matches_and_coprimes_by_same_size = store_thm( - "gcd_matches_and_coprimes_by_same_size", - ``!n. CARD o (gcd_matches n) = CARD o (coprimes_by n)``, - rw[FUN_EQ_THM] >> - Cases_on `x divides n` >| [ - `BIJ (\j. j DIV x) (gcd_matches n x) (coprimes_by n x)` by rw[gcd_matches_bij_coprimes_by] >> - `FINITE (gcd_matches n x)` by rw[gcd_matches_finite] >> - `FINITE (coprimes_by n x)` by rw[coprimes_by_finite] >> - metis_tac[FINITE_BIJ_CARD_EQ], - Cases_on `n = 0` >- - rw[gcd_matches_0, coprimes_by_0] >> - `gcd_matches n x = {}` by rw[gcd_matches_eq_empty] >> - `coprimes_by n x = {}` by rw[coprimes_by_eq_empty] >> - rw[] - ]); - -(* Theorem: 0 < n ==> (CARD o (coprimes_by n) = \d. phi (if d IN (divisors n) then n DIV d else 0)) *) -(* Proof: - By FUN_EQ_THM, - CARD o (coprimes_by n) x - = CARD (coprimes_by n x) by composition, combinTheory.o_THM - = CARD (if x divides n then coprimes (n DIV x) else {}) by coprimes_by_def, 0 < n - If x divides n, - then x <= n by DIVIDES_LE - and 0 < x by divisor_pos, 0 < n - so x IN (divisors n) by divisors_element - CARD o (coprimes_by n) x - = CARD (coprimes (n DIV x)) - = phi (n DIV x) by phi_def - If ~(x divides n), - x NOTIN (divisors n) by divisors_element - CARD o (coprimes_by n) x - = CARD {} - = 0 by CARD_EMPTY - = phi 0 by phi_0 - Hence the same function as: - \d. phi (if d IN (divisors n) then n DIV d else 0) -*) -Theorem coprimes_by_with_card: - !n. 0 < n ==> (CARD o (coprimes_by n) = \d. phi (if d IN (divisors n) then n DIV d else 0)) -Proof - rw[coprimes_by_def, phi_def, divisors_def, FUN_EQ_THM] >> - metis_tac[DIVIDES_LE, divisor_pos, coprimes_0] -QED - -(* Theorem: x IN (divisors n) ==> (CARD o (coprimes_by n)) x = (\d. phi (n DIV d)) x *) -(* Proof: - Since x IN (divisors n) ==> x divides n by divisors_element - CARD o (coprimes_by n) x - = CARD (coprimes (n DIV x)) by coprimes_by_def - = phi (n DIV x) by phi_def -*) -Theorem coprimes_by_divisors_card: - !n x. x IN (divisors n) ==> (CARD o (coprimes_by n)) x = (\d. phi (n DIV d)) x -Proof - rw[coprimes_by_def, phi_def, divisors_def] -QED - -(* -SUM_IMAGE_CONG |- (s1 = s2) /\ (!x. x IN s2 ==> (f1 x = f2 x)) ==> (SIGMA f1 s1 = SIGMA f2 s2) -*) - -(* Theorem: SIGMA phi (divisors n) = n *) -(* Proof: - Note INJ (gcd_matches n) (divisors n) univ(:num -> bool) by gcd_matches_from_divisors_inj - and (\d. n DIV d) PERMUTES (divisors n) by divisors_divisors_bij - n = CARD (natural n) by natural_card - = SIGMA CARD (partition (feq (gcd n)) (natural n)) by partition_CARD - = SIGMA CARD (IMAGE (gcd_matches n) (divisors n)) by gcd_eq_partition_by_divisors - = SIGMA (CARD o (gcd_matches n)) (divisors n) by sum_image_by_composition - = SIGMA (CARD o (coprimes_by n)) (divisors n) by gcd_matches_and_coprimes_by_same_size - = SIGMA (\d. phi (n DIV d)) (divisors n) by SUM_IMAGE_CONG, coprimes_by_divisors_card - = SIGMA phi (divisors n) by sum_image_by_permutation -*) -Theorem Gauss_little_thm: - !n. SIGMA phi (divisors n) = n -Proof - rpt strip_tac >> - `FINITE (natural n)` by rw[natural_finite] >> - `(feq (gcd n)) equiv_on (natural n)` by rw[gcd_eq_equiv_on_natural] >> - `INJ (gcd_matches n) (divisors n) univ(:num -> bool)` by rw[gcd_matches_from_divisors_inj] >> - `(\d. n DIV d) PERMUTES (divisors n)` by rw[divisors_divisors_bij] >> - `FINITE (divisors n)` by rw[divisors_finite] >> - `n = CARD (natural n)` by rw[natural_card] >> - `_ = SIGMA CARD (partition (feq (gcd n)) (natural n))` by rw[partition_CARD] >> - `_ = SIGMA CARD (IMAGE (gcd_matches n) (divisors n))` by rw[gcd_eq_partition_by_divisors] >> - `_ = SIGMA (CARD o (gcd_matches n)) (divisors n)` by prove_tac[sum_image_by_composition] >> - `_ = SIGMA (CARD o (coprimes_by n)) (divisors n)` by rw[gcd_matches_and_coprimes_by_same_size] >> - `_ = SIGMA (\d. phi (n DIV d)) (divisors n)` by rw[SUM_IMAGE_CONG, coprimes_by_divisors_card] >> - `_ = SIGMA phi (divisors n)` by metis_tac[sum_image_by_permutation] >> - decide_tac -QED - -(* This is a milestone theorem. *) - -(* ------------------------------------------------------------------------- *) -(* Euler phi function is multiplicative for coprimes. *) -(* ------------------------------------------------------------------------- *) - -(* -EVAL ``coprimes 2``; = {1} -EVAL ``coprimes 3``; = {2; 1} -EVAL ``coprimes 6``; = {5; 1} - -Let ϕ(n) = the set of remainders coprime to n and not exceeding n. -Then ϕ(2) = {1}, ϕ(3) = {1,2} -We shall show ϕ(6) = {z = (3 * x + 2 * y) mod 6 | x ∈ ϕ(2), y ∈ ϕ(3)}. -(1,1) corresponds to z = (3 * 1 + 2 * 1) mod 6 = 5, right! -(1,2) corresponds to z = (3 * 1 + 2 * 2) mod 6 = 1, right! -*) - -(* Idea: give an expression for coprimes (m * n). *) - -(* Theorem: coprime m n ==> - coprimes (m * n) = - IMAGE (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) - ((coprimes m) CROSS (coprimes n)) *) -(* Proof: - Let f = \(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n). - If m = 0 or n = 0, - When m = 0, to show: - coprimes 0 = IMAGE f ((coprimes 0) CROSS (coprimes n)) - RHS - = IMAGE f ({} CROSS (coprimes n)) by coprimes_0 - = IMAGE f {} by CROSS_EMPTY - = {} by IMAGE_EMPTY - = LHS by coprimes_0 - When n = 0, to show: - coprimes 0 = IMAGE f ((coprimes m) CROSS (coprimes 0)) - RHS - = IMAGE f ((coprimes n) CROSS {}) by coprimes_0 - = IMAGE f {} by CROSS_EMPTY - = {} by IMAGE_EMPTY - = LHS by coprimes_0 - - If m = 1, or n = 1, - When m = 1, to show: - coprimes n = IMAGE f ((coprimes 1) CROSS (coprimes n)) - RHS - = IMAGE f ({1} CROSS (coprimes n)) by coprimes_1 - = IMAGE f {(1,y) | y IN coprimes n} by IN_CROSS - = {if n = 1 then 1 else (n + y) MOD n | y IN coprimes n} - by IN_IMAGE - = {1} if n = 1, or {y MOD n | y IN coprimes n} if 1 < n - = {1} if n = 1, or {y | y IN coprimes n} if 1 < n - by coprimes_element_alt, LESS_MOD, y < n - = LHS by coprimes_1 - When n = 1, to show: - coprimes m = IMAGE f ((coprimes m) CROSS (coprimes 1)) - RHS - = IMAGE f ((coprimes m) CROSS {1}) by coprimes_1 - = IMAGE f {(x,1) | x IN coprimes m} by IN_CROSS - = {if m = 1 then 1 else (x + m) MOD m | x IN coprimes m} - by IN_IMAGE - = {1} if m = 1, or {x MOD m | x IN coprimes m} if 1 < m - = {1} if m = 1, or {x | x IN coprimes m} if 1 < m - by coprimes_element_alt, LESS_MOD, x < m - = LHS by coprimes_1 - - Now, 1 < m, 1 < n, and 0 < m, 0 < n. - Therefore 1 < m * n, and 0 < m * n. by MULT_EQ_1, MULT_EQ_0 - and function f = \(x,y). (x * n + y * m) MOD (m * n). - If part: z IN coprimes (m * n) ==> - ?x y. z = (x * n + y * m) MOD (m * n) /\ x IN coprimes m /\ y IN coprimes n - Note z < m * n /\ coprime z (m * n) by coprimes_element_alt, 1 < m * n - for x < m /\ coprime x m, and y < n /\ coprime y n - by coprimes_element_alt, 1 < m, 1 < n - Now ?p q. (p * m + q * n) MOD (m * n) - = z MOD (m * n) by coprime_multiple_linear_mod_prod - = z by LESS_MOD, z < m * n - Note ?h x. p = h * n + x /\ x < n by DA, 0 < n - and ?k y. q = k * m + y /\ y < m by DA, 0 < m - z - = (p * m + q * n) MOD (m * n) by above - = (h * n * m + x * m + k * m * n + y * n) MOD (m * n) - = ((x * m + y * n) + (h + k) * (m * n)) MOD (m * n) - = (x * m + y * n) MOD (m * n) by MOD_PLUS2, MOD_EQ_0 - Take these x and y, but need to show: - (1) coprime x n - Let g = gcd x n, - Then g divides x /\ g divides n by GCD_PROPERTY - so g divides (m * n) by DIVIDES_MULTIPLE - so g divides z by divides_linear, mod_divides_divides - ==> g = 1, or coprime x n by coprime_common_factor - (2) coprime y m - Let g = gcd y m, - Then g divides y /\ g divides m by GCD_PROPERTY - so g divides (m * n) by DIVIDES_MULTIPLE - so g divides z by divides_linear, mod_divides_divides - ==> g = 1, or coprime y m by coprime_common_factor - - Only-if part: coprime m n /\ x IN coprimes m /\ y IN coprimes n ==> - (x * n + y * m) MOD (m * n) IN coprimes (m * n) - Note x < m /\ coprime x m by coprimes_element_alt, 1 < m - and y < n /\ coprime y n by coprimes_element_alt, 1 < n - Let z = x * m + y * n. - Then coprime z (m * n) by coprime_linear_mult - so coprime (z MOD (m * n)) (m * n) by GCD_MOD_COMM - and z MOD (m * n) < m * n by MOD_LESS, 0 < m * n -*) -Theorem coprimes_mult_by_image: - !m n. coprime m n ==> - coprimes (m * n) = - IMAGE (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) - ((coprimes m) CROSS (coprimes n)) -Proof - rpt strip_tac >> - Cases_on `m = 0 \/ n = 0` >- - fs[coprimes_0] >> - Cases_on `m = 1 \/ n = 1` >| [ - fs[coprimes_1] >| [ - rw[EXTENSION, pairTheory.EXISTS_PROD] >> - Cases_on `n = 1` >- - simp[coprimes_1] >> - fs[coprimes_element_alt] >> - metis_tac[LESS_MOD], - rw[EXTENSION, pairTheory.EXISTS_PROD] >> - Cases_on `m = 1` >- - simp[coprimes_1] >> - fs[coprimes_element_alt] >> - metis_tac[LESS_MOD] - ], - `m * n <> 0 /\ m * n <> 1` by rw[] >> - `1 < m /\ 1 < n /\ 1 < m * n` by decide_tac >> - rw[EXTENSION, pairTheory.EXISTS_PROD] >> - rw[EQ_IMP_THM] >| [ - rfs[coprimes_element_alt] >> - `1 < m /\ 1 < n /\ 0 < m /\ 0 < n /\ 0 < m * n` by decide_tac >> - `?p q. (p * m + q * n) MOD (m * n) = x MOD (m * n)` by rw[coprime_multiple_linear_mod_prod] >> - `?h u. p = h * n + u /\ u < n` by metis_tac[DA] >> - `?k v. q = k * m + v /\ v < m` by metis_tac[DA] >> - `p * m + q * n = h * n * m + u * m + k * m * n + v * n` by simp[] >> - `_ = (u * m + v * n) + (h + k) * (m * n)` by simp[] >> - `(u * m + v * n) MOD (m * n) = x MOD (m * n)` by metis_tac[MOD_PLUS2, MOD_EQ_0, ADD_0] >> - `_ = x` by rw[] >> - `coprime u n` by - (qabbrev_tac `g = gcd u n` >> - `0 < g` by rw[GCD_POS, Abbr`g`] >> - `g divides u /\ g divides n` by metis_tac[GCD_PROPERTY] >> - `g divides (m * n)` by rw[DIVIDES_MULTIPLE] >> - `g divides x` by metis_tac[divides_linear, MULT_COMM, mod_divides_divides] >> - metis_tac[coprime_common_factor]) >> - `coprime v m` by - (qabbrev_tac `g = gcd v m` >> - `0 < g` by rw[GCD_POS, Abbr`g`] >> - `g divides v /\ g divides m` by metis_tac[GCD_PROPERTY] >> - `g divides (m * n)` by metis_tac[DIVIDES_MULTIPLE, MULT_COMM] >> - `g divides x` by metis_tac[divides_linear, MULT_COMM, mod_divides_divides] >> - metis_tac[coprime_common_factor]) >> - metis_tac[MULT_COMM], - rfs[coprimes_element_alt] >> - `0 < m * n` by decide_tac >> - `coprime (m * p_2 + n * p_1) (m * n)` by metis_tac[coprime_linear_mult, MULT_COMM] >> - metis_tac[GCD_MOD_COMM] - ] - ] -QED - -(* Yes! a milestone theorem. *) - -(* Idea: in coprimes (m * n), the image map is injective. *) - -(* Theorem: coprime m n ==> - INJ (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) - ((coprimes m) CROSS (coprimes n)) univ(:num) *) -(* Proof: - Let f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n). - To show: coprime m n ==> INJ f ((coprimes m) CROSS (coprimes n)) univ(:num) - If m = 0, or n = 0, - When m = 0, - INJ f ((coprimes 0) CROSS (coprimes n)) univ(:num) - <=> INJ f ({} CROSS (coprimes n)) univ(:num) by coprimes_0 - <=> INJ f {} univ(:num) by CROSS_EMPTY - <=> T by INJ_EMPTY - When n = 0, - INJ f ((coprimes m) CROSS (coprimes 0)) univ(:num) - <=> INJ f ((coprimes m) CROSS {}) univ(:num) by coprimes_0 - <=> INJ f {} univ(:num) by CROSS_EMPTY - <=> T by INJ_EMPTY - - If m = 1, or n = 1, - When m = 1, - INJ f ((coprimes 1) CROSS (coprimes n)) univ(:num) - <=> INJ f ({1} CROSS (coprimes n)) univ(:num) by coprimes_1 - If n = 1, this is - INJ f ({1} CROSS {1}) univ(:num) by coprimes_1 - <=> INJ f {(1,1)} univ(:num) by CROSS_SINGS - <=> T by INJ_DEF - If n <> 1, this is by INJ_DEF: - to show: !p q. p IN coprimes n /\ q IN coprimes n ==> p MOD n = q MOD n ==> p = q - Now p < n /\ q < n by coprimes_element_alt, 1 < n - With p MOD n = q MOD n, so p = q by LESS_MOD - When n = 1, - INJ f ((coprimes m) CROSS (coprimes 1)) univ(:num) - <=> INJ f ((coprimes m) CROSS {1}) univ(:num) by coprimes_1 - If m = 1, this is - INJ f ({1} CROSS {1}) univ(:num) by coprimes_1 - <=> INJ f {(1,1)} univ(:num) by CROSS_SINGS - <=> T by INJ_DEF - If m <> 1, this is by INJ_DEF: - to show: !p q. p IN coprimes m /\ q IN coprimes m ==> p MOD m = q MOD m ==> p = q - Now p < m /\ q < m by coprimes_element_alt, 1 < m - With p MOD m = q MOD m, so p = q by LESS_MOD - - Now 1 < m and 1 < n, so 1 < m * n by MULT_EQ_1, MULT_EQ_0 - By INJ_DEF, coprimes_element_alt, this is to show: - !x y u v. x < m /\ coprime x m /\ y < n /\ coprime y n /\ - u < m /\ coprime u m /\ v < n /\ coprime v n /\ - (x * n + y * m) MOD (m * n) = (u * n + v * m) MOD (m * n) - ==> x = u /\ y = v - Note x * n < n * m by LT_MULT_RCANCEL, 0 < n, x < m - and v * m < n * m by LT_MULT_RCANCEL, 0 < m, v < n - Thus (y * m + (n * m - v * m)) MOD (n * m) - = (u * n + (n * m - x * n)) MOD (n * m) by mod_add_eq_sub - Now y * m + (n * m - v * m) = m * (n + y - v) by arithmetic - and u * n + (n * m - x * n) = n * (m + u - x) by arithmetic - and 0 < n + y - v /\ n + y - v < 2 * n by y < n, v < n - and 0 < m + u - x /\ m + u - x < 2 * m by x < m, u < m - ==> n + y - v = n /\ m + u - x = m by mod_mult_eq_mult - ==> n + y = n + v /\ m + u = m + x by arithmetic - ==> y = v /\ x = u by EQ_ADD_LCANCEL -*) -Theorem coprimes_map_cross_inj: - !m n. coprime m n ==> - INJ (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) - ((coprimes m) CROSS (coprimes n)) univ(:num) -Proof - rpt strip_tac >> - qabbrev_tac `f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)` >> - Cases_on `m = 0 \/ n = 0` >- - fs[coprimes_0] >> - Cases_on `m = 1 \/ n = 1` >| [ - fs[coprimes_1, INJ_DEF, pairTheory.FORALL_PROD, Abbr`f`] >| [ - (Cases_on `n = 1` >> simp[coprimes_1]) >> - fs[coprimes_element_alt], - (Cases_on `m = 1` >> simp[coprimes_1]) >> - fs[coprimes_element_alt] - ], - `m * n <> 0 /\ m * n <> 1` by rw[] >> - `1 < m /\ 1 < n /\ 1 < m * n` by decide_tac >> - simp[INJ_DEF, pairTheory.FORALL_PROD] >> - ntac 6 strip_tac >> - rfs[coprimes_element_alt, Abbr`f`] >> - `0 < m /\ 0 < n /\ 0 < m * n` by decide_tac >> - `n * p_1 < n * m /\ m * p_2' < n * m` by simp[] >> - `(m * p_2 + (n * m - m * p_2')) MOD (n * m) = - (n * p_1' + (n * m - n * p_1)) MOD (n * m)` by simp[GSYM mod_add_eq_sub] >> - `m * p_2 + (n * m - m * p_2') = m * (n + p_2 - p_2')` by decide_tac >> - `n * p_1' + (n * m - n * p_1) = n * (m + p_1' - p_1)` by decide_tac >> - `0 < n + p_2 - p_2' /\ n + p_2 - p_2' < 2 * n` by decide_tac >> - `0 < m + p_1' - p_1 /\ m + p_1' - p_1 < 2 * m` by decide_tac >> - `n + p_2 - p_2' = n /\ m + p_1' - p_1 = m` by metis_tac[mod_mult_eq_mult, MULT_COMM] >> - simp[] - ] -QED - -(* Another milestone theorem! *) - -(* Idea: Euler phi function is multiplicative for coprimes. *) - -(* Theorem: coprime m n ==> phi (m * n) = phi m * phi n *) -(* Proof: - Let f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n), - u = coprimes m, - v = coprimes n. - Then coprimes (m * n) = IMAGE f (u CROSS v) by coprimes_mult_by_image - and INJ f (u CROSS v) univ(:num) by coprimes_map_cross_inj - Note FINITE u /\ FINITE v by coprimes_finite - so FINITE (u CROSS v) by FINITE_CROSS - phi (m * n) - = CARD (coprimes (m * n)) by phi_def - = CARD (IMAGE f (u CROSS v)) by above - = CARD (u CROSS v) by INJ_CARD_IMAGE - = (CARD u) * (CARD v) by CARD_CROSS - = phi m * phi n by phi_def -*) -Theorem phi_mult: - !m n. coprime m n ==> phi (m * n) = phi m * phi n -Proof - rw[phi_def] >> - imp_res_tac coprimes_mult_by_image >> - imp_res_tac coprimes_map_cross_inj >> - qabbrev_tac `f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)` >> - qabbrev_tac `u = coprimes m` >> - qabbrev_tac `v = coprimes n` >> - `FINITE u /\ FINITE v` by rw[coprimes_finite, Abbr`u`, Abbr`v`] >> - `FINITE (u CROSS v)` by rw[] >> - metis_tac[INJ_CARD_IMAGE, CARD_CROSS] -QED - -(* This is the ultimate goal! *) - -(* Idea: an expression for phi (p * q) with distinct primes p and q. *) - -(* Theorem: prime p /\ prime q /\ p <> q ==> phi (p * q) = (p - 1) * (q - 1) *) -(* Proof: - Note coprime p q by primes_coprime - phi (p * q) - = phi p * phi q by phi_mult - = (p - 1) * (q - 1) by phi_prime -*) -Theorem phi_primes_distinct: - !p q. prime p /\ prime q /\ p <> q ==> phi (p * q) = (p - 1) * (q - 1) -Proof - simp[primes_coprime, phi_mult, phi_prime] -QED - -(* ------------------------------------------------------------------------- *) -(* Euler phi function for prime powers. *) -(* ------------------------------------------------------------------------- *) - -(* -EVAL ``coprimes 9``; = {8; 7; 5; 4; 2; 1} -EVAL ``divisors 9``; = {9; 3; 1} -EVAL ``IMAGE (\x. 3 * x) (natural 3)``; = {9; 6; 3} -EVAL ``IMAGE (\x. 3 * x) (natural 9)``; = {27; 24; 21; 18; 15; 12; 9; 6; 3} - -> EVAL ``IMAGE ($* 3) (natural (8 DIV 3))``; = {6; 3} -> EVAL ``IMAGE ($* 3) (natural (9 DIV 3))``; = {9; 6; 3} -> EVAL ``IMAGE ($* 3) (natural (10 DIV 3))``; = {9; 6; 3} -> EVAL ``IMAGE ($* 3) (natural (12 DIV 3))``; = {12; 9; 6; 3} -*) - -(* Idea: develop a special set in anticipation for counting. *) - -(* Define the set of positive multiples of m, up to n *) -val multiples_upto_def = zDefine` - multiples_upto m n = {x | m divides x /\ 0 < x /\ x <= n} -`; -(* use zDefine as this is not effective for evalutaion. *) -(* make this an infix operator *) -val _ = set_fixity "multiples_upto" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) - -(* -> multiples_upto_def; -val it = |- !m n. m multiples_upto n = {x | m divides x /\ 0 < x /\ x <= n}: thm -*) - -(* Theorem: x IN m multiples_upto n <=> m divides x /\ 0 < x /\ x <= n *) -(* Proof: by multiples_upto_def. *) -Theorem multiples_upto_element: - !m n x. x IN m multiples_upto n <=> m divides x /\ 0 < x /\ x <= n -Proof - simp[multiples_upto_def] -QED - -(* Theorem: m multiples_upto n = {x | ?k. x = k * m /\ 0 < x /\ x <= n} *) -(* Proof: - m multiples_upto n - = {x | m divides x /\ 0 < x /\ x <= n} by multiples_upto_def - = {x | ?k. x = k * m /\ 0 < x /\ x <= n} by divides_def -*) -Theorem multiples_upto_alt: - !m n. m multiples_upto n = {x | ?k. x = k * m /\ 0 < x /\ x <= n} -Proof - rw[multiples_upto_def, EXTENSION] >> - metis_tac[divides_def] -QED - -(* Theorem: x IN m multiples_upto n <=> ?k. x = k * m /\ 0 < x /\ x <= n *) -(* Proof: by multiples_upto_alt. *) -Theorem multiples_upto_element_alt: - !m n x. x IN m multiples_upto n <=> ?k. x = k * m /\ 0 < x /\ x <= n -Proof - simp[multiples_upto_alt] -QED - -(* Theorem: m multiples_upto n = {x | m divides x /\ x IN natural n} *) -(* Proof: - m multiples_upto n - = {x | m divides x /\ 0 < x /\ x <= n} by multiples_upto_def - = {x | m divides x /\ x IN natural n} by natural_element -*) -Theorem multiples_upto_eqn: - !m n. m multiples_upto n = {x | m divides x /\ x IN natural n} -Proof - simp[multiples_upto_def, natural_element, EXTENSION] -QED - -(* Theorem: 0 multiples_upto n = {} *) -(* Proof: - 0 multiples_upto n - = {x | 0 divides x /\ 0 < x /\ x <= n} by multiples_upto_def - = {x | x = 0 /\ 0 < x /\ x <= n} by ZERO_DIVIDES - = {} by contradiction -*) -Theorem multiples_upto_0_n: - !n. 0 multiples_upto n = {} -Proof - simp[multiples_upto_def, EXTENSION] -QED - -(* Theorem: 1 multiples_upto n = natural n *) -(* Proof: - 1 multiples_upto n - = {x | 1 divides x /\ x IN natural n} by multiples_upto_eqn - = {x | T /\ x IN natural n} by ONE_DIVIDES_ALL - = natural n by EXTENSION -*) -Theorem multiples_upto_1_n: - !n. 1 multiples_upto n = natural n -Proof - simp[multiples_upto_eqn, EXTENSION] -QED - -(* Theorem: m multiples_upto 0 = {} *) -(* Proof: - m multiples_upto 0 - = {x | m divides x /\ 0 < x /\ x <= 0} by multiples_upto_def - = {x | m divides x /\ F} by arithmetic - = {} by contradiction -*) -Theorem multiples_upto_m_0: - !m. m multiples_upto 0 = {} -Proof - simp[multiples_upto_def, EXTENSION] -QED - -(* Theorem: m multiples_upto 1 = if m = 1 then {1} else {} *) -(* Proof: - m multiples_upto 1 - = {x | m divides x /\ 0 < x /\ x <= 1} by multiples_upto_def - = {x | m divides x /\ x = 1} by arithmetic - = {1} if m = 1, {} otherwise by DIVIDES_ONE -*) -Theorem multiples_upto_m_1: - !m. m multiples_upto 1 = if m = 1 then {1} else {} -Proof - rw[multiples_upto_def, EXTENSION] >> - spose_not_then strip_assume_tac >> - `x = 1` by decide_tac >> - fs[] -QED - -(* Idea: an expression for (m multiples_upto n), for direct evaluation. *) - -(* Theorem: m multiples_upto n = - if m = 0 then {} - else IMAGE ($* m) (natural (n DIV m)) *) -(* Proof: - If m = 0, - Then 0 multiples_upto n = {} by multiples_upto_0_n - If m <> 0. - By multiples_upto_alt, EXTENSION, this is to show: - (1) 0 < k * m /\ k * m <= n ==> - ?y. k * m = m * y /\ ?x. y = SUC x /\ x < n DIV m - Note k <> 0 by MULT_EQ_0 - and k <= n DIV m by X_LE_DIV, 0 < m - so k - 1 < n DIV m by arithmetic - Let y = k, x = k - 1. - Note SUC x = SUC (k - 1) = k = y. - (2) x < n DIV m ==> ?k. m * SUC x = k * m /\ 0 < m * SUC x /\ m * SUC x <= n - Note SUC x <= n DIV m by arithmetic - so m * SUC x <= n by X_LE_DIV, 0 < m - and 0 < m * SUC x by MULT_EQ_0 - Take k = SUC x, true by MULT_COMM -*) -Theorem multiples_upto_thm[compute]: - !m n. m multiples_upto n = - if m = 0 then {} - else IMAGE ($* m) (natural (n DIV m)) -Proof - rpt strip_tac >> - Cases_on `m = 0` >- - fs[multiples_upto_0_n] >> - fs[multiples_upto_alt, EXTENSION] >> - rw[EQ_IMP_THM] >| [ - qexists_tac `k` >> - simp[] >> - `0 < k /\ 0 < m` by metis_tac[MULT_EQ_0, NOT_ZERO] >> - `k <= n DIV m` by rw[X_LE_DIV] >> - `k - 1 < n DIV m` by decide_tac >> - qexists_tac `k - 1` >> - simp[], - `SUC x'' <= n DIV m` by decide_tac >> - `m * SUC x'' <= n` by rfs[X_LE_DIV] >> - simp[] >> - metis_tac[MULT_COMM] - ] -QED - -(* -EVAL ``3 multiples_upto 9``; = {9; 6; 3} -EVAL ``3 multiples_upto 11``; = {9; 6; 3} -EVAL ``3 multiples_upto 12``; = {12; 9; 6; 3} -EVAL ``3 multiples_upto 13``; = {12; 9; 6; 3} -*) - -(* Theorem: m multiples_upto n SUBSET natural n *) -(* Proof: by multiples_upto_eqn, SUBSET_DEF. *) -Theorem multiples_upto_subset: - !m n. m multiples_upto n SUBSET natural n -Proof - simp[multiples_upto_eqn, SUBSET_DEF] -QED - -(* Theorem: FINITE (m multiples_upto n) *) -(* Proof: - Let s = m multiples_upto n - Note s SUBSET natural n by multiples_upto_subset - and FINITE natural n by natural_finite - so FINITE s by SUBSET_FINITE -*) -Theorem multiples_upto_finite: - !m n. FINITE (m multiples_upto n) -Proof - metis_tac[multiples_upto_subset, natural_finite, SUBSET_FINITE] -QED - -(* Theorem: CARD (m multiples_upto n) = if m = 0 then 0 else n DIV m *) -(* Proof: - If m = 0, - CARD (0 multiples_upto n) - = CARD {} by multiples_upto_0_n - = 0 by CARD_EMPTY - If m <> 0, - Claim: INJ ($* m) (natural (n DIV m)) univ(:num) - Proof: By INJ_DEF, this is to show: - !x. x IN (natural (n DIV m)) /\ - m * x = m * y ==> x = y, true by EQ_MULT_LCANCEL, m <> 0 - Note FINITE (natural (n DIV m)) by natural_finite - CARD (m multiples_upto n) - = CARD (IMAGE ($* m) (natural (n DIV m))) by multiples_upto_thm, m <> 0 - = CARD (natural (n DIV m)) by INJ_CARD_IMAGE - = n DIV m by natural_card -*) -Theorem multiples_upto_card: - !m n. CARD (m multiples_upto n) = if m = 0 then 0 else n DIV m -Proof - rpt strip_tac >> - Cases_on `m = 0` >- - simp[multiples_upto_0_n] >> - simp[multiples_upto_thm] >> - `INJ ($* m) (natural (n DIV m)) univ(:num)` by rw[INJ_DEF] >> - metis_tac[INJ_CARD_IMAGE, natural_finite, natural_card] -QED - -(* Idea: an expression for the set of coprimes of a prime power. *) - -(* Theorem: prime p ==> - coprimes (p ** n) = natural (p ** n) DIFF p multiples_upto (p ** n) *) -(* Proof: - If n = 0, - LHS = coprimes (p ** 0) - = coprimes 1 by EXP_0 - = {1} by coprimes_1 - RHS = natural (p ** 0) DIFF p multiples_upto (p ** 0) - = natural 1 DIFF p multiples_upto 1 - = natural 1 DIFF {} by multiples_upto_m_1, NOT_PRIME_1 - = {1} DIFF {} by natural_1 - = {1} = LHS by DIFF_EMPTY - If n <> 0, - By coprimes_def, multiples_upto_def, EXTENSION, this is to show: - coprime (SUC x) (p ** n) <=> ~(p divides SUC x) - This is true by coprime_prime_power -*) -Theorem coprimes_prime_power: - !p n. prime p ==> - coprimes (p ** n) = natural (p ** n) DIFF p multiples_upto (p ** n) -Proof - rpt strip_tac >> - Cases_on `n = 0` >| [ - `p <> 1` by metis_tac[NOT_PRIME_1] >> - simp[coprimes_1, multiples_upto_m_1, natural_1, EXP_0], - rw[coprimes_def, multiples_upto_def, EXTENSION] >> - (rw[EQ_IMP_THM] >> rfs[coprime_prime_power]) - ] -QED - -(* Idea: an expression for phi of a prime power. *) - -(* Theorem: prime p ==> phi (p ** SUC n) = (p - 1) * p ** n *) -(* Proof: - Let m = SUC n, - u = natural (p ** m), - v = p multiples_upto (p ** m). - Note 0 < p by PRIME_POS - and FINITE u by natural_finite - and v SUBSET u by multiples_upto_subset - - phi (p ** m) - = CARD (coprimes (p ** m)) by phi_def - = CARD (u DIFF v) by coprimes_prime_power - = CARD u - CARD v by SUBSET_DIFF_CARD - = p ** m - CARD v by natural_card - = p ** m - (p ** m DIV p) by multiples_upto_card, p <> 0 - = p ** m - p ** n by EXP_SUC_DIV, 0 < p - = p * p ** n - p ** n by EXP - = (p - 1) * p ** n by RIGHT_SUB_DISTRIB -*) -Theorem phi_prime_power: - !p n. prime p ==> phi (p ** SUC n) = (p - 1) * p ** n -Proof - rpt strip_tac >> - qabbrev_tac `m = SUC n` >> - qabbrev_tac `u = natural (p ** m)` >> - qabbrev_tac `v = p multiples_upto (p ** m)` >> - `0 < p` by rw[PRIME_POS] >> - `FINITE u` by rw[natural_finite, Abbr`u`] >> - `v SUBSET u` by rw[multiples_upto_subset, Abbr`v`, Abbr`u`] >> - `phi (p ** m) = CARD (coprimes (p ** m))` by rw[phi_def] >> - `_ = CARD (u DIFF v)` by rw[coprimes_prime_power, Abbr`u`, Abbr`v`] >> - `_ = CARD u - CARD v` by rw[SUBSET_DIFF_CARD] >> - `_ = p ** m - (p ** m DIV p)` by rw[natural_card, multiples_upto_card, Abbr`u`, Abbr`v`] >> - `_ = p ** m - p ** n` by rw[EXP_SUC_DIV, Abbr`m`] >> - `_ = p * p ** n - p ** n` by rw[GSYM EXP] >> - `_ = (p - 1) * p ** n` by decide_tac >> - simp[] -QED - -(* Yes, a spectacular theorem! *) - -(* Idea: specialise phi_prime_power for prime squared. *) - -(* Theorem: prime p ==> phi (p * p) = p * (p - 1) *) -(* Proof: - phi (p * p) - = phi (p ** 2) by EXP_2 - = phi (p ** SUC 1) by TWO - = (p - 1) * p ** 1 by phi_prime_power - = p * (p - 1) by EXP_1 -*) -Theorem phi_prime_sq: - !p. prime p ==> phi (p * p) = p * (p - 1) -Proof - rpt strip_tac >> - `phi (p * p) = phi (p ** SUC 1)` by rw[] >> - simp[phi_prime_power] -QED - -(* Idea: Euler phi function for a product of primes. *) - -(* Theorem: prime p /\ prime q ==> - phi (p * q) = if p = q then p * (p - 1) else (p - 1) * (q - 1) *) -(* Proof: - If p = q, phi (p * p) = p * (p - 1) by phi_prime_sq - If p <> q, phi (p * q) = (p - 1) * (q - 1) by phi_primes_distinct -*) -Theorem phi_primes: - !p q. prime p /\ prime q ==> - phi (p * q) = if p = q then p * (p - 1) else (p - 1) * (q - 1) -Proof - metis_tac[phi_prime_sq, phi_primes_distinct] -QED - -(* Finally, another nice result. *) - -(* ------------------------------------------------------------------------- *) -(* Recursive definition of phi *) -(* ------------------------------------------------------------------------- *) - -(* Define phi by recursion *) -Definition rec_phi_def: - rec_phi n = if n = 0 then 0 - else if n = 1 then 1 - else n - SIGMA rec_phi { m | m < n /\ m divides n} -Termination - WF_REL_TAC `$< : num -> num -> bool` >> srw_tac[][] -End -(* This is the recursive form of Gauss' Little Theorem: n = SUM phi m, m divides n *) - -(* Just using Define without any condition will trigger: - -Initial goal: - -?R. WF R /\ !n a. n <> 0 /\ n <> 1 /\ a IN {m | m < n /\ m divides n} ==> R a n - -Unable to prove termination! - -Try using "TotalDefn.tDefine ". - -The termination goal has been set up using Defn.tgoal . - -So one can set up: -g `?R. WF R /\ !n a. n <> 0 /\ n <> 1 /\ a IN {m | m < n /\ m divides n} ==> R a n`; -e (WF_REL_TAC `$< : num -> num -> bool`); -e (srw[][]); - -which gives the tDefine solution. -*) - -(* Theorem: rec_phi 0 = 0 *) -(* Proof: by rec_phi_def *) -val rec_phi_0 = store_thm( - "rec_phi_0", - ``rec_phi 0 = 0``, - rw[rec_phi_def]); - -(* Theorem: rec_phi 1 = 1 *) -(* Proof: by rec_phi_def *) -val rec_phi_1 = store_thm( - "rec_phi_1", - ``rec_phi 1 = 1``, - rw[Once rec_phi_def]); - -(* Theorem: rec_phi n = phi n *) -(* Proof: - By complete induction on n. - If n = 0, - rec_phi 0 = 0 by rec_phi_0 - = phi 0 by phi_0 - If n = 1, - rec_phi 1 = 1 by rec_phi_1 - = phi 1 by phi_1 - Othewise, 0 < n, 1 < n. - Let s = {m | m < n /\ m divides n}. - Note s SUBSET (count n) by SUBSET_DEF - thus FINITE s by SUBSET_FINITE, FINITE_COUNT - Hence !m. m IN s - ==> (rec_phi m = phi m) by induction hypothesis - Also n NOTIN s by EXTENSION - and n INSERT s - = {m | m <= n /\ m divides n} - = {m | 0 < m /\ m <= n /\ m divides n} by divisor_pos, 0 < n - = divisors n by divisors_def, EXTENSION, LESS_OR_EQ - - rec_phi n - = n - (SIGMA rec_phi s) by rec_phi_def - = n - (SIGMA phi s) by SUM_IMAGE_CONG, rec_phi m = phi m - = (SIGMA phi (divisors n)) - (SIGMA phi s) by Gauss' Little Theorem - = (SIGMA phi (n INSERT s)) - (SIGMA phi s) by divisors n = n INSERT s - = (phi n + SIGMA phi (s DELETE n)) - (SIGMA phi s) by SUM_IMAGE_THM - = (phi n + SIGMA phi s) - (SIGMA phi s) by DELETE_NON_ELEMENT - = phi n by ADD_SUB -*) -Theorem rec_phi_eq_phi: - !n. rec_phi n = phi n -Proof - completeInduct_on `n` >> - Cases_on `n = 0` >- - rw[rec_phi_0, phi_0] >> - Cases_on `n = 1` >- - rw[rec_phi_1, phi_1] >> - `0 < n /\ 1 < n` by decide_tac >> - qabbrev_tac `s = {m | m < n /\ m divides n}` >> - qabbrev_tac `t = SIGMA rec_phi s` >> - `!m. m IN s <=> m < n /\ m divides n` by rw[Abbr`s`] >> - `!m. m IN s ==> (rec_phi m = phi m)` by rw[] >> - `t = SIGMA phi s` by rw[SUM_IMAGE_CONG, Abbr`t`] >> - `s SUBSET (count n)` by rw[SUBSET_DEF] >> - `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - `n NOTIN s` by rw[] >> - (`n INSERT s = divisors n` by (rw[divisors_def, EXTENSION] >> metis_tac[divisor_pos, LESS_OR_EQ, DIVIDES_REFL])) >> - `n = SIGMA phi (divisors n)` by rw[Gauss_little_thm] >> - `_ = phi n + SIGMA phi (s DELETE n)` by rw[GSYM SUM_IMAGE_THM] >> - `_ = phi n + t` by metis_tac[DELETE_NON_ELEMENT] >> - `rec_phi n = n - t` by metis_tac[rec_phi_def] >> - decide_tac -QED - - -(* ------------------------------------------------------------------------- *) -(* Useful Theorems (not used). *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: INJ (coprimes) (univ(:num) DIFF {1}) univ(:num -> bool) *) -(* Proof: - By INJ_DEF, this is to show: - x <> 1 /\ y <> 1 /\ coprimes x = coprimes y ==> x = y - If x = 0, then y = 0 by coprimes_eq_empty - If y = 0, then x = 0 by coprimes_eq_empty - If x <> 0 and y <> 0, - with x <> 1 and y <> 1 by given - then 1 < x and 1 < y. - Since MAX_SET (coprimes x) = x - 1 by coprimes_max, 1 < x - and MAX_SET (coprimes y) = y - 1 by coprimes_max, 1 < y - If coprimes x = coprimes y, - x - 1 = y - 1 by above - Hence x = y by CANCEL_SUB -*) -val coprimes_from_not_1_inj = store_thm( - "coprimes_from_not_1_inj", - ``INJ (coprimes) (univ(:num) DIFF {1}) univ(:num -> bool)``, - rw[INJ_DEF] >> - Cases_on `x = 0` >- - metis_tac[coprimes_eq_empty] >> - Cases_on `y = 0` >- - metis_tac[coprimes_eq_empty] >> - `1 < x /\ 1 < y` by decide_tac >> - `x - 1 = y - 1` by metis_tac[coprimes_max] >> - decide_tac); -(* Not very useful. *) - -(* Here is group of related theorems for (divisors n): - divisors_eq_image_gcd_upto - divisors_eq_image_gcd_count - divisors_eq_image_gcd_natural - - This first one is proved independently, then the second and third are derived. - Of course, the best is the third one, which is now divisors_eq_gcd_image (above) - Here, I rework all proofs of these three from divisors_eq_gcd_image, - so divisors_eq_image_gcd_natural = divisors_eq_gcd_image. -*) - -(* Theorem: 0 < n ==> divisors n = IMAGE (gcd n) (upto n) *) -(* Proof: - Note gcd n 0 = n by GCD_0 - and n IN divisors n by divisors_has_last, 0 < n - divisors n - = (gcd n 0) INSERT (divisors n) by ABSORPTION - = (gcd n 0) INSERT (IMAGE (gcd n) (natural n)) by divisors_eq_gcd_image - = IMAGE (gcd n) (0 INSERT (natural n)) by IMAGE_INSERT - = IMAGE (gcd n) (upto n) by upto_by_natural -*) -Theorem divisors_eq_image_gcd_upto: - !n. 0 < n ==> divisors n = IMAGE (gcd n) (upto n) -Proof - rpt strip_tac >> - `IMAGE (gcd n) (upto n) = IMAGE (gcd n) (0 INSERT natural n)` by simp[upto_by_natural] >> - `_ = (gcd n 0) INSERT (IMAGE (gcd n) (natural n))` by fs[] >> - `_ = n INSERT (divisors n)` by fs[divisors_eq_gcd_image] >> - metis_tac[divisors_has_last, ABSORPTION] -QED - -(* Theorem: (feq (gcd n)) equiv_on (upto n) *) -(* Proof: - By feq_equiv |- !s f. feq f equiv_on s - Taking s = upto n, f = gcd n. -*) -val gcd_eq_equiv_on_upto = store_thm( - "gcd_eq_equiv_on_upto", - ``!n. (feq (gcd n)) equiv_on (upto n)``, - rw[feq_equiv]); - -(* Theorem: 0 < n ==> partition (feq (gcd n)) (upto n) = IMAGE (preimage (gcd n) (upto n)) (divisors n) *) -(* Proof: - Let f = gcd n, s = upto n. - partition (feq f) s - = IMAGE (preimage f s o f) s by feq_partition - = IMAGE (preimage f s) (IMAGE f s) by IMAGE_COMPOSE - = IMAGE (preimage f s) (IMAGE (gcd n) (upto n)) by expansion - = IMAGE (preimage f s) (divisors n) by divisors_eq_image_gcd_upto, 0 < n -*) -val gcd_eq_upto_partition_by_divisors = store_thm( - "gcd_eq_upto_partition_by_divisors", - ``!n. 0 < n ==> partition (feq (gcd n)) (upto n) = IMAGE (preimage (gcd n) (upto n)) (divisors n)``, - rpt strip_tac >> - qabbrev_tac `f = gcd n` >> - qabbrev_tac `s = upto n` >> - `partition (feq f) s = IMAGE (preimage f s o f) s` by rw[feq_partition] >> - `_ = IMAGE (preimage f s) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> - rw[divisors_eq_image_gcd_upto, Abbr`f`, Abbr`s`]); - -(* Theorem: SIGMA f (upto n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n)) *) -(* Proof: - Let g = gcd n, s = upto n. - Since FINITE s by upto_finite - and (feq g) equiv_on s by feq_equiv - The result follows by set_sigma_by_partition -*) -val sum_over_upto_by_gcd_partition = store_thm( - "sum_over_upto_by_gcd_partition", - ``!f n. SIGMA f (upto n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n))``, - rw[feq_equiv, set_sigma_by_partition]); - -(* Theorem: 0 < n ==> SIGMA f (upto n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n)) *) -(* Proof: - SIGMA f (upto n) - = SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n)) by sum_over_upto_by_gcd_partition - = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n)) by gcd_eq_upto_partition_by_divisors, 0 < n -*) -val sum_over_upto_by_divisors = store_thm( - "sum_over_upto_by_divisors", - ``!f n. 0 < n ==> SIGMA f (upto n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n))``, - rw[sum_over_upto_by_gcd_partition, gcd_eq_upto_partition_by_divisors]); - -(* Similar results based on count *) - -(* Theorem: divisors n = IMAGE (gcd n) (count n) *) -(* Proof: - If n = 0, - LHS = divisors 0 = {} by divisors_0 - RHS = IMAGE (gcd 0) (count 0) - = IMAGE (gcd 0) {} by COUNT_0 - = {} = LHS by IMAGE_EMPTY - If n <> 0, 0 < n. - divisors n - = IMAGE (gcd n) (upto n) by divisors_eq_image_gcd_upto, 0 < n - = IMAGE (gcd n) (n INSERT (count n)) by upto_by_count - = (gcd n n) INSERT (IMAGE (gcd n) (count n)) by IMAGE_INSERT - = n INSERT (IMAGE (gcd n) (count n)) by GCD_REF - = (gcd n 0) INSERT (IMAGE (gcd n) (count n)) by GCD_0R - = IMAGE (gcd n) (0 INSERT (count n)) by IMAGE_INSERT - = IMAGE (gcd n) (count n) by IN_COUNT, ABSORPTION, 0 < n. -*) -Theorem divisors_eq_image_gcd_count: - !n. divisors n = IMAGE (gcd n) (count n) -Proof - rpt strip_tac >> - Cases_on `n = 0` >- - simp[divisors_0] >> - `0 < n` by decide_tac >> - `divisors n = IMAGE (gcd n) (upto n)` by rw[divisors_eq_image_gcd_upto] >> - `_ = IMAGE (gcd n) (n INSERT (count n))` by rw[upto_by_count] >> - `_ = n INSERT (IMAGE (gcd n) (count n))` by rw[GCD_REF] >> - `_ = (gcd n 0) INSERT (IMAGE (gcd n) (count n))` by rw[GCD_0R] >> - `_ = IMAGE (gcd n) (0 INSERT (count n))` by rw[] >> - metis_tac[IN_COUNT, ABSORPTION] -QED - -(* Theorem: (feq (gcd n)) equiv_on (count n) *) -(* Proof: - By feq_equiv |- !s f. feq f equiv_on s - Taking s = upto n, f = count n. -*) -val gcd_eq_equiv_on_count = store_thm( - "gcd_eq_equiv_on_count", - ``!n. (feq (gcd n)) equiv_on (count n)``, - rw[feq_equiv]); - -(* Theorem: partition (feq (gcd n)) (count n) = IMAGE (preimage (gcd n) (count n)) (divisors n) *) -(* Proof: - Let f = gcd n, s = count n. - partition (feq f) s - = IMAGE (preimage f s o f) s by feq_partition - = IMAGE (preimage f s) (IMAGE f s) by IMAGE_COMPOSE - = IMAGE (preimage f s) (IMAGE (gcd n) (count n)) by expansion - = IMAGE (preimage f s) (divisors n) by divisors_eq_image_gcd_count -*) -Theorem gcd_eq_count_partition_by_divisors: - !n. partition (feq (gcd n)) (count n) = IMAGE (preimage (gcd n) (count n)) (divisors n) -Proof - rpt strip_tac >> - qabbrev_tac `f = gcd n` >> - qabbrev_tac `s = count n` >> - `partition (feq f) s = IMAGE (preimage f s o f) s` by rw[feq_partition] >> - `_ = IMAGE (preimage f s) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> - rw[divisors_eq_image_gcd_count, Abbr`f`, Abbr`s`] -QED - -(* Theorem: SIGMA f (count n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (count n)) *) -(* Proof: - Let g = gcd n, s = count n. - Since FINITE s by FINITE_COUNT - and (feq g) equiv_on s by feq_equiv - The result follows by set_sigma_by_partition -*) -val sum_over_count_by_gcd_partition = store_thm( - "sum_over_count_by_gcd_partition", - ``!f n. SIGMA f (count n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (count n))``, - rw[feq_equiv, set_sigma_by_partition]); - -(* Theorem: SIGMA f (count n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) *) -(* Proof: - SIGMA f (count n) - = SIGMA (SIGMA f) (partition (feq (gcd n)) (count n)) by sum_over_count_by_gcd_partition - = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) by gcd_eq_count_partition_by_divisors -*) -Theorem sum_over_count_by_divisors: - !f n. SIGMA f (count n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) -Proof - rw[sum_over_count_by_gcd_partition, gcd_eq_count_partition_by_divisors] -QED - -(* Similar results based on natural *) - -(* Theorem: divisors n = IMAGE (gcd n) (natural n) *) -(* Proof: - If n = 0, - LHS = divisors 0 = {} by divisors_0 - RHS = IMAGE (gcd 0) (natural 0) - = IMAGE (gcd 0) {} by natural_0 - = {} = LHS by IMAGE_EMPTY - If n <> 0, 0 < n. - divisors n - = IMAGE (gcd n) (upto n) by divisors_eq_image_gcd_upto, 0 < n - = IMAGE (gcd n) (0 INSERT natural n) by upto_by_natural - = (gcd 0 n) INSERT (IMAGE (gcd n) (natural n)) by IMAGE_INSERT - = n INSERT (IMAGE (gcd n) (natural n)) by GCD_0L - = (gcd n n) INSERT (IMAGE (gcd n) (natural n)) by GCD_REF - = IMAGE (gcd n) (n INSERT (natural n)) by IMAGE_INSERT - = IMAGE (gcd n) (natural n) by natural_has_last, ABSORPTION, 0 < n. -*) -Theorem divisors_eq_image_gcd_natural: - !n. divisors n = IMAGE (gcd n) (natural n) -Proof - rpt strip_tac >> - Cases_on `n = 0` >- - simp[divisors_0, natural_0] >> - `0 < n` by decide_tac >> - `divisors n = IMAGE (gcd n) (upto n)` by rw[divisors_eq_image_gcd_upto] >> - `_ = IMAGE (gcd n) (0 INSERT (natural n))` by rw[upto_by_natural] >> - `_ = n INSERT (IMAGE (gcd n) (natural n))` by rw[GCD_0L] >> - `_ = (gcd n n) INSERT (IMAGE (gcd n) (natural n))` by rw[GCD_REF] >> - `_ = IMAGE (gcd n) (n INSERT (natural n))` by rw[] >> - metis_tac[natural_has_last, ABSORPTION] -QED -(* This is the same as divisors_eq_gcd_image *) - -(* Theorem: partition (feq (gcd n)) (natural n) = IMAGE (preimage (gcd n) (natural n)) (divisors n) *) -(* Proof: - Let f = gcd n, s = natural n. - partition (feq f) s - = IMAGE (preimage f s o f) s by feq_partition - = IMAGE (preimage f s) (IMAGE f s) by IMAGE_COMPOSE - = IMAGE (preimage f s) (IMAGE (gcd n) (natural n)) by expansion - = IMAGE (preimage f s) (divisors n) by divisors_eq_image_gcd_natural -*) -Theorem gcd_eq_natural_partition_by_divisors: - !n. partition (feq (gcd n)) (natural n) = IMAGE (preimage (gcd n) (natural n)) (divisors n) -Proof - rpt strip_tac >> - qabbrev_tac `f = gcd n` >> - qabbrev_tac `s = natural n` >> - `partition (feq f) s = IMAGE (preimage f s o f) s` by rw[feq_partition] >> - `_ = IMAGE (preimage f s) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> - rw[divisors_eq_image_gcd_natural, Abbr`f`, Abbr`s`] -QED - -(* Theorem: SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) *) -(* Proof: - SIGMA f (natural n) - = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) by sum_over_natural_by_gcd_partition - = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) by gcd_eq_natural_partition_by_divisors -*) -Theorem sum_over_natural_by_preimage_divisors: - !f n. SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) -Proof - rw[sum_over_natural_by_gcd_partition, gcd_eq_natural_partition_by_divisors] -QED - -(* Theorem: (f 0 = g 0) /\ (!n. SIGMA f (divisors n) = SIGMA g (divisors n)) ==> (f = g) *) -(* Proof: - By FUN_EQ_THM, this is to show: !x. f x = g x. - By complete induction on x. - Let s = divisors x, t = s DELETE x. - If x = 0, f 0 = g 0 is true by given - Otherwise x <> 0. - Then x IN s by divisors_has_last, 0 < x - and s = x INSERT t /\ x NOTIN t by INSERT_DELETE, IN_DELETE - Note FINITE s by divisors_finite - so FINITE t by FINITE_DELETE - - Claim: SIGMA f t = SIGMA g t - Proof: By SUM_IMAGE_CONG, this is to show: - !z. z IN t ==> (f z = g z) - But z IN s <=> 0 < z /\ z <= x /\ z divides x by divisors_element - so z IN t <=> 0 < z /\ z < x /\ z divides x by IN_DELETE - ==> f z = g z by induction hypothesis, [1] - - Now SIGMA f s = SIGMA g s by implication - or f x + SIGMA f t = g x + SIGMA g t by SUM_IMAGE_INSERT - or f x = g x by [1], SIGMA f t = SIGMA g t -*) -Theorem sum_image_divisors_cong: - !f g. (f 0 = g 0) /\ (!n. SIGMA f (divisors n) = SIGMA g (divisors n)) ==> (f = g) -Proof - rw[FUN_EQ_THM] >> - completeInduct_on `x` >> - qabbrev_tac `s = divisors x` >> - qabbrev_tac `t = s DELETE x` >> - (Cases_on `x = 0` >> simp[]) >> - `x IN s` by rw[divisors_has_last, Abbr`s`] >> - `s = x INSERT t /\ x NOTIN t` by rw[Abbr`t`] >> - `SIGMA f t = SIGMA g t` by - ((irule SUM_IMAGE_CONG >> simp[]) >> - rw[divisors_element, Abbr`t`, Abbr`s`]) >> - `FINITE t` by rw[divisors_finite, Abbr`t`, Abbr`s`] >> - `SIGMA f s = f x + SIGMA f t` by rw[SUM_IMAGE_INSERT] >> - `SIGMA g s = g x + SIGMA g t` by rw[SUM_IMAGE_INSERT] >> - `SIGMA f s = SIGMA g s` by metis_tac[] >> - decide_tac -QED -(* But this is not very useful! *) - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/MobiusScript.sml b/examples/algebra/lib/MobiusScript.sml deleted file mode 100644 index d6ee80d8d6..0000000000 --- a/examples/algebra/lib/MobiusScript.sml +++ /dev/null @@ -1,1112 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Mobius Function and Inversion. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "Mobius"; - -(* ------------------------------------------------------------------------- *) - - - -open jcLib; - -(* open dependent theories *) -open pred_setTheory listTheory; -open prim_recTheory arithmeticTheory dividesTheory gcdTheory; - -open helperNumTheory helperSetTheory helperListTheory; - -open GaussTheory; -open EulerTheory; - - -(* ------------------------------------------------------------------------- *) -(* Mobius Function and Inversion Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - sq_free s = {n | n IN s /\ square_free n} - non_sq_free s = {n | n IN s /\ ~(square_free n)} - even_sq_free s = {n | n IN (sq_free s) /\ EVEN (CARD (prime_factors n))} - odd_sq_free s = {n | n IN (sq_free s) /\ ODD (CARD (prime_factors n))} - less_divisors n = {x | x IN (divisors n) /\ x <> n} - proper_divisors n = {x | x IN (divisors n) /\ x <> 1 /\ x <> n} -*) -(* Definitions and Theorems (# are exported): - - Helper Theorems: - - Square-free Number and Square-free Sets: - square_free_def |- !n. square_free n <=> !p. prime p /\ p divides n ==> ~(p * p divides n) - square_free_1 |- square_free 1 - square_free_prime |- !n. prime n ==> square_free n - - sq_free_element |- !s n. n IN sq_free s <=> n IN s /\ square_free n - sq_free_subset |- !s. sq_free s SUBSET s - sq_free_finite |- !s. FINITE s ==> FINITE (sq_free s) - non_sq_free_element |- !s n. n IN non_sq_free s <=> n IN s /\ ~square_free n - non_sq_free_subset |- !s. non_sq_free s SUBSET s - non_sq_free_finite |- !s. FINITE s ==> FINITE (non_sq_free s) - sq_free_split |- !s. (s = sq_free s UNION non_sq_free s) /\ - (sq_free s INTER non_sq_free s = {}) - sq_free_union |- !s. s = sq_free s UNION non_sq_free s - sq_free_inter |- !s. sq_free s INTER non_sq_free s = {} - sq_free_disjoint |- !s. DISJOINT (sq_free s) (non_sq_free s) - - Prime Divisors of a Number and Partitions of Square-free Set: - prime_factors_def |- !n. prime_factors n = {p | prime p /\ p IN divisors n} - prime_factors_element |- !n p. p IN prime_factors n <=> prime p /\ p <= n /\ p divides n - prime_factors_subset |- !n. prime_factors n SUBSET divisors n - prime_factors_finite |- !n. FINITE (prime_factors n) - - even_sq_free_element |- !s n. n IN even_sq_free s <=> n IN s /\ square_free n /\ EVEN (CARD (prime_factors n)) - even_sq_free_subset |- !s. even_sq_free s SUBSET s - even_sq_free_finite |- !s. FINITE s ==> FINITE (even_sq_free s) - odd_sq_free_element |- !s n. n IN odd_sq_free s <=> n IN s /\ square_free n /\ ODD (CARD (prime_factors n)) - odd_sq_free_subset |- !s. odd_sq_free s SUBSET s - odd_sq_free_finite |- !s. FINITE s ==> FINITE (odd_sq_free s) - sq_free_split_even_odd |- !s. (sq_free s = even_sq_free s UNION odd_sq_free s) /\ - (even_sq_free s INTER odd_sq_free s = {}) - sq_free_union_even_odd |- !s. sq_free s = even_sq_free s UNION odd_sq_free s - sq_free_inter_even_odd |- !s. even_sq_free s INTER odd_sq_free s = {} - sq_free_disjoint_even_odd |- !s. DISJOINT (even_sq_free s) (odd_sq_free s) - - Less Divisors of a number: - less_divisors_element |- !n x. x IN less_divisors n <=> 0 < x /\ x < n /\ x divides n - less_divisors_0 |- less_divisors 0 = {} - less_divisors_1 |- less_divisors 1 = {} - less_divisors_subset_divisors - |- !n. less_divisors n SUBSET divisors n - less_divisors_finite |- !n. FINITE (less_divisors n) - less_divisors_prime |- !n. prime n ==> (less_divisors n = {1}) - less_divisors_has_1 |- !n. 1 < n ==> 1 IN less_divisors n - less_divisors_nonzero |- !n x. x IN less_divisors n ==> 0 < x - less_divisors_has_cofactor |- !n d. 1 < d /\ d IN less_divisors n ==> n DIV d IN less_divisors n - - Proper Divisors of a number: - proper_divisors_element |- !n x. x IN proper_divisors n <=> 1 < x /\ x < n /\ x divides n - proper_divisors_0 |- proper_divisors 0 = {} - proper_divisors_1 |- proper_divisors 1 = {} - proper_divisors_subset |- !n. proper_divisors n SUBSET less_divisors n - proper_divisors_finite |- !n. FINITE (proper_divisors n) - proper_divisors_not_1 |- !n. 1 NOTIN proper_divisors n - proper_divisors_by_less_divisors - |- !n. proper_divisors n = less_divisors n DELETE 1 - proper_divisors_prime |- !n. prime n ==> (proper_divisors n = {}) - proper_divisors_has_cofactor|- !n d. d IN proper_divisors n ==> n DIV d IN proper_divisors n - proper_divisors_min_gt_1 |- !n. proper_divisors n <> {} ==> 1 < MIN_SET (proper_divisors n) - proper_divisors_max_min |- !n. proper_divisors n <> {} ==> - (MAX_SET (proper_divisors n) = n DIV MIN_SET (proper_divisors n)) /\ - (MIN_SET (proper_divisors n) = n DIV MAX_SET (proper_divisors n)) - - Useful Properties of Less Divisors: - less_divisors_min |- !n. 1 < n ==> (MIN_SET (less_divisors n) = 1) - less_divisors_max |- !n. MAX_SET (less_divisors n) <= n DIV 2 - less_divisors_subset_natural |- !n. less_divisors n SUBSET natural (n DIV 2) - - Properties of Summation equals Perfect Power: - perfect_power_special_inequality |- !p. 1 < p ==> !n. p * (p ** n - 1) < (p - 1) * (2 * p ** n) - perfect_power_half_inequality_1 |- !p n. 1 < p /\ 0 < n ==> 2 * p ** (n DIV 2) <= p ** n - perfect_power_half_inequality_2 |- !p n. 1 < p /\ 0 < n ==> - (p ** (n DIV 2) - 2) * p ** (n DIV 2) <= p ** n - 2 * p ** (n DIV 2) - sigma_eq_perfect_power_bounds_1 |- !p. 1 < p ==> - !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> - (!n. 0 < n ==> n * f n <= p ** n) /\ - !n. 0 < n ==> p ** n - 2 * p ** (n DIV 2) < n * f n - sigma_eq_perfect_power_bounds_2 |- !p. 1 < p ==> - !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> - (!n. 0 < n ==> n * f n <= p ** n) /\ - !n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * f n - -*) - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Mobius Function and Inversion *) -(* ------------------------------------------------------------------------- *) - - -(* ------------------------------------------------------------------------- *) -(* Square-free Number and Square-free Sets *) -(* ------------------------------------------------------------------------- *) - -(* Define square-free number *) -val square_free_def = Define` - square_free n = !p. prime p /\ p divides n ==> ~(p * p divides n) -`; - -(* Theorem: square_free 1 *) -(* Proof: - square_free 1 - <=> !p. prime p /\ p divides 1 ==> ~(p * p divides 1) by square_free_def - <=> prime 1 ==> ~(1 * 1 divides 1) by DIVIDES_ONE - <=> F ==> ~(1 * 1 divides 1) by NOT_PRIME_1 - <=> T by false assumption -*) -val square_free_1 = store_thm( - "square_free_1", - ``square_free 1``, - rw[square_free_def]); - -(* Theorem: prime n ==> square_free n *) -(* Proof: - square_free n - <=> !p. prime p /\ p divides n ==> ~(p * p divides n) by square_free_def - By contradiction, suppose (p * p divides n). - Since p divides n ==> (p = n) \/ (p = 1) by prime_def - and p * p divides ==> (p * p = n) \/ (p * p = 1) by prime_def - but p <> 1 by prime_def - so p * p <> 1 by MULT_EQ_1 - Thus p * p = n = p, - or p = 0 \/ p = 1 by SQ_EQ_SELF - But p <> 0 by NOT_PRIME_0 - and p <> 1 by NOT_PRIME_1 - Thus there is a contradiction. -*) -val square_free_prime = store_thm( - "square_free_prime", - ``!n. prime n ==> square_free n``, - rw_tac std_ss[square_free_def] >> - spose_not_then strip_assume_tac >> - `p * p = p` by metis_tac[prime_def, MULT_EQ_1] >> - metis_tac[SQ_EQ_SELF, NOT_PRIME_0, NOT_PRIME_1]); - -(* Overload square-free filter of a set *) -val _ = overload_on("sq_free", ``\s. {n | n IN s /\ square_free n}``); - -(* Overload non-square-free filter of a set *) -val _ = overload_on("non_sq_free", ``\s. {n | n IN s /\ ~(square_free n)}``); - -(* Theorem: n IN sq_free s <=> n IN s /\ square_free n *) -(* Proof: by notation. *) -val sq_free_element = store_thm( - "sq_free_element", - ``!s n. n IN sq_free s <=> n IN s /\ square_free n``, - rw[]); - -(* Theorem: sq_free s SUBSET s *) -(* Proof: by SUBSET_DEF *) -val sq_free_subset = store_thm( - "sq_free_subset", - ``!s. sq_free s SUBSET s``, - rw[SUBSET_DEF]); - -(* Theorem: FINITE s ==> FINITE (sq_free s) *) -(* Proof: by sq_free_subset, SUBSET_FINITE *) -val sq_free_finite = store_thm( - "sq_free_finite", - ``!s. FINITE s ==> FINITE (sq_free s)``, - metis_tac[sq_free_subset, SUBSET_FINITE]); - -(* Theorem: n IN non_sq_free s <=> n IN s /\ ~(square_free n) *) -(* Proof: by notation. *) -val non_sq_free_element = store_thm( - "non_sq_free_element", - ``!s n. n IN non_sq_free s <=> n IN s /\ ~(square_free n)``, - rw[]); - -(* Theorem: non_sq_free s SUBSET s *) -(* Proof: by SUBSET_DEF *) -val non_sq_free_subset = store_thm( - "non_sq_free_subset", - ``!s. non_sq_free s SUBSET s``, - rw[SUBSET_DEF]); - -(* Theorem: FINITE s ==> FINITE (non_sq_free s) *) -(* Proof: by non_sq_free_subset, SUBSET_FINITE *) -val non_sq_free_finite = store_thm( - "non_sq_free_finite", - ``!s. FINITE s ==> FINITE (non_sq_free s)``, - metis_tac[non_sq_free_subset, SUBSET_FINITE]); - -(* Theorem: (s = (sq_free s) UNION (non_sq_free s)) /\ ((sq_free s) INTER (non_sq_free s) = {}) *) -(* Proof: - This is to show: - (1) s = (sq_free s) UNION (non_sq_free s) - True by EXTENSION, IN_UNION. - (2) (sq_free s) INTER (non_sq_free s) = {} - True by EXTENSION, IN_INTER -*) -val sq_free_split = store_thm( - "sq_free_split", - ``!s. (s = (sq_free s) UNION (non_sq_free s)) /\ ((sq_free s) INTER (non_sq_free s) = {})``, - (rw[EXTENSION] >> metis_tac[])); - -(* Theorem: s = (sq_free s) UNION (non_sq_free s) *) -(* Proof: extract from sq_free_split. *) -val sq_free_union = save_thm("sq_free_union", sq_free_split |> SPEC_ALL |> CONJUNCT1 |> GEN_ALL); -(* val sq_free_union = |- !s. s = sq_free s UNION non_sq_free s: thm *) - -(* Theorem: (sq_free s) INTER (non_sq_free s) = {} *) -(* Proof: extract from sq_free_split. *) -val sq_free_inter = save_thm("sq_free_inter", sq_free_split |> SPEC_ALL |> CONJUNCT2 |> GEN_ALL); -(* val sq_free_inter = |- !s. sq_free s INTER non_sq_free s = {}: thm *) - -(* Theorem: DISJOINT (sq_free s) (non_sq_free s) *) -(* Proof: by DISJOINT_DEF, sq_free_inter. *) -val sq_free_disjoint = store_thm( - "sq_free_disjoint", - ``!s. DISJOINT (sq_free s) (non_sq_free s)``, - rw_tac std_ss[DISJOINT_DEF, sq_free_inter]); - -(* ------------------------------------------------------------------------- *) -(* Prime Divisors of a Number and Partitions of Square-free Set *) -(* ------------------------------------------------------------------------- *) - -(* Define the prime divisors of a number *) -val prime_factors_def = zDefine` - prime_factors n = {p | prime p /\ p IN (divisors n)} -`; -(* use zDefine as this cannot be computed. *) -(* prime_divisors is used in triangle.hol *) - -(* Theorem: p IN prime_factors n <=> prime p /\ p <= n /\ p divides n *) -(* Proof: - p IN prime_factors n - <=> prime p /\ p IN (divisors n) by prime_factors_def - <=> prime p /\ 0 < p /\ p <= n /\ p divides n by divisors_def - <=> prime p /\ p <= n /\ p divides n by PRIME_POS -*) -Theorem prime_factors_element: - !n p. p IN prime_factors n <=> prime p /\ p <= n /\ p divides n -Proof - rw[prime_factors_def, divisors_def] >> - metis_tac[PRIME_POS] -QED - -(* Theorem: (prime_factors n) SUBSET (divisors n) *) -(* Proof: - p IN (prime_factors n) - ==> p IN (divisors n) by prime_factors_def - Hence (prime_factors n) SUBSET (divisors n) by SUBSET_DEF -*) -val prime_factors_subset = store_thm( - "prime_factors_subset", - ``!n. (prime_factors n) SUBSET (divisors n)``, - rw[prime_factors_def, SUBSET_DEF]); - -(* Theorem: FINITE (prime_factors n) *) -(* Proof: - Since (prime_factors n) SUBSET (divisors n) by prime_factors_subset - and FINITE (divisors n) by divisors_finite - Thus FINITE (prime_factors n) by SUBSET_FINITE -*) -val prime_factors_finite = store_thm( - "prime_factors_finite", - ``!n. FINITE (prime_factors n)``, - metis_tac[prime_factors_subset, divisors_finite, SUBSET_FINITE]); - -(* Overload even square-free filter of a set *) -val _ = overload_on("even_sq_free", ``\s. {n | n IN (sq_free s) /\ EVEN (CARD (prime_factors n))}``); - -(* Overload odd square-free filter of a set *) -val _ = overload_on("odd_sq_free", ``\s. {n | n IN (sq_free s) /\ ODD (CARD (prime_factors n))}``); - -(* Theorem: n IN even_sq_free s <=> n IN s /\ square_free n /\ EVEN (CARD (prime_factors n)) *) -(* Proof: by notation. *) -val even_sq_free_element = store_thm( - "even_sq_free_element", - ``!s n. n IN even_sq_free s <=> n IN s /\ square_free n /\ EVEN (CARD (prime_factors n))``, - (rw[] >> metis_tac[])); - -(* Theorem: even_sq_free s SUBSET s *) -(* Proof: by SUBSET_DEF *) -val even_sq_free_subset = store_thm( - "even_sq_free_subset", - ``!s. even_sq_free s SUBSET s``, - rw[SUBSET_DEF]); - -(* Theorem: FINITE s ==> FINITE (even_sq_free s) *) -(* Proof: by even_sq_free_subset, SUBSET_FINITE *) -val even_sq_free_finite = store_thm( - "even_sq_free_finite", - ``!s. FINITE s ==> FINITE (even_sq_free s)``, - metis_tac[even_sq_free_subset, SUBSET_FINITE]); - -(* Theorem: n IN odd_sq_free s <=> n IN s /\ square_free n /\ ODD (CARD (prime_factors n)) *) -(* Proof: by notation. *) -val odd_sq_free_element = store_thm( - "odd_sq_free_element", - ``!s n. n IN odd_sq_free s <=> n IN s /\ square_free n /\ ODD (CARD (prime_factors n))``, - (rw[] >> metis_tac[])); - -(* Theorem: odd_sq_free s SUBSET s *) -(* Proof: by SUBSET_DEF *) -val odd_sq_free_subset = store_thm( - "odd_sq_free_subset", - ``!s. odd_sq_free s SUBSET s``, - rw[SUBSET_DEF]); - -(* Theorem: FINITE s ==> FINITE (odd_sq_free s) *) -(* Proof: by odd_sq_free_subset, SUBSET_FINITE *) -val odd_sq_free_finite = store_thm( - "odd_sq_free_finite", - ``!s. FINITE s ==> FINITE (odd_sq_free s)``, - metis_tac[odd_sq_free_subset, SUBSET_FINITE]); - -(* Theorem: (sq_free s = (even_sq_free s) UNION (odd_sq_free s)) /\ - ((even_sq_free s) INTER (odd_sq_free s) = {}) *) -(* Proof: - This is to show: - (1) sq_free s = even_sq_free s UNION odd_sq_free s - True by EXTENSION, IN_UNION, EVEN_ODD. - (2) even_sq_free s INTER odd_sq_free s = {} - True by EXTENSION, IN_INTER, EVEN_ODD. -*) -val sq_free_split_even_odd = store_thm( - "sq_free_split_even_odd", - ``!s. (sq_free s = (even_sq_free s) UNION (odd_sq_free s)) /\ - ((even_sq_free s) INTER (odd_sq_free s) = {})``, - (rw[EXTENSION] >> metis_tac[EVEN_ODD])); - -(* Theorem: sq_free s = (even_sq_free s) UNION (odd_sq_free s) *) -(* Proof: extract from sq_free_split_even_odd. *) -val sq_free_union_even_odd = - save_thm("sq_free_union_even_odd", sq_free_split_even_odd |> SPEC_ALL |> CONJUNCT1 |> GEN_ALL); -(* val sq_free_union_even_odd = - |- !s. sq_free s = even_sq_free s UNION odd_sq_free s: thm *) - -(* Theorem: (even_sq_free s) INTER (odd_sq_free s) = {} *) -(* Proof: extract from sq_free_split_even_odd. *) -val sq_free_inter_even_odd = - save_thm("sq_free_inter_even_odd", sq_free_split_even_odd |> SPEC_ALL |> CONJUNCT2 |> GEN_ALL); -(* val sq_free_inter_even_odd = - |- !s. even_sq_free s INTER odd_sq_free s = {}: thm *) - -(* Theorem: DISJOINT (even_sq_free s) (odd_sq_free s) *) -(* Proof: by DISJOINT_DEF, sq_free_inter_even_odd. *) -val sq_free_disjoint_even_odd = store_thm( - "sq_free_disjoint_even_odd", - ``!s. DISJOINT (even_sq_free s) (odd_sq_free s)``, - rw_tac std_ss[DISJOINT_DEF, sq_free_inter_even_odd]); - -(* ------------------------------------------------------------------------- *) -(* Less Divisors of a number. *) -(* ------------------------------------------------------------------------- *) - -(* Overload the set of divisors less than n *) -val _ = overload_on("less_divisors", ``\n. {x | x IN (divisors n) /\ x <> n}``); - -(* Theorem: x IN (less_divisors n) <=> (0 < x /\ x < n /\ x divides n) *) -(* Proof: by divisors_element. *) -val less_divisors_element = store_thm( - "less_divisors_element", - ``!n x. x IN (less_divisors n) <=> (0 < x /\ x < n /\ x divides n)``, - rw[divisors_element, EQ_IMP_THM]); - -(* Theorem: less_divisors 0 = {} *) -(* Proof: by divisors_element. *) -val less_divisors_0 = store_thm( - "less_divisors_0", - ``less_divisors 0 = {}``, - rw[divisors_element]); - -(* Theorem: less_divisors 1 = {} *) -(* Proof: by divisors_element. *) -val less_divisors_1 = store_thm( - "less_divisors_1", - ``less_divisors 1 = {}``, - rw[divisors_element]); - -(* Theorem: (less_divisors n) SUBSET (divisors n) *) -(* Proof: by SUBSET_DEF *) -val less_divisors_subset_divisors = store_thm( - "less_divisors_subset_divisors", - ``!n. (less_divisors n) SUBSET (divisors n)``, - rw[SUBSET_DEF]); - -(* Theorem: FINITE (less_divisors n) *) -(* Proof: - Since (less_divisors n) SUBSET (divisors n) by less_divisors_subset_divisors - and FINITE (divisors n) by divisors_finite - so FINITE (proper_divisors n) by SUBSET_FINITE -*) -val less_divisors_finite = store_thm( - "less_divisors_finite", - ``!n. FINITE (less_divisors n)``, - metis_tac[divisors_finite, less_divisors_subset_divisors, SUBSET_FINITE]); - -(* Theorem: prime n ==> (less_divisors n = {1}) *) -(* Proof: - Since prime n - ==> !b. b divides n ==> (b = n) \/ (b = 1) by prime_def - But (less_divisors n) excludes n by less_divisors_element - and 1 < n by ONE_LT_PRIME - Hence less_divisors n = {1} -*) -val less_divisors_prime = store_thm( - "less_divisors_prime", - ``!n. prime n ==> (less_divisors n = {1})``, - rpt strip_tac >> - `!b. b divides n ==> (b = n) \/ (b = 1)` by metis_tac[prime_def] >> - rw[less_divisors_element, EXTENSION, EQ_IMP_THM] >| [ - `x <> n` by decide_tac >> - metis_tac[], - rw[ONE_LT_PRIME] - ]); - -(* Theorem: 1 < n ==> 1 IN (less_divisors n) *) -(* Proof: - 1 IN (less_divisors n) - <=> 1 < n /\ 1 divides n by less_divisors_element - <=> T by ONE_DIVIDES_ALL -*) -val less_divisors_has_1 = store_thm( - "less_divisors_has_1", - ``!n. 1 < n ==> 1 IN (less_divisors n)``, - rw[less_divisors_element]); - -(* Theorem: x IN (less_divisors n) ==> 0 < x *) -(* Proof: by less_divisors_element. *) -val less_divisors_nonzero = store_thm( - "less_divisors_nonzero", - ``!n x. x IN (less_divisors n) ==> 0 < x``, - rw[less_divisors_element]); - -(* Theorem: 1 < d /\ d IN (less_divisors n) ==> (n DIV d) IN (less_divisors n) *) -(* Proof: - d IN (less_divisors n) - ==> d IN (divisors n) by less_divisors_subset_divisors - ==> (n DIV d) IN (divisors n) by divisors_has_cofactor - Note 0 < d /\ d <= n ==> 0 < n by divisors_element - Also n DIV d < n by DIV_LESS, 0 < n /\ 1 < d - thus n DIV d <> n by LESS_NOT_EQ - Hence (n DIV d) IN (less_divisors n) by notation -*) -val less_divisors_has_cofactor = store_thm( - "less_divisors_has_cofactor", - ``!n d. 1 < d /\ d IN (less_divisors n) ==> (n DIV d) IN (less_divisors n)``, - rw[divisors_has_cofactor, divisors_element, DIV_LESS, LESS_NOT_EQ]); - -(* ------------------------------------------------------------------------- *) -(* Proper Divisors of a number. *) -(* ------------------------------------------------------------------------- *) - -(* Overload the set of proper divisors of n *) -val _ = overload_on("proper_divisors", ``\n. {x | x IN (divisors n) /\ x <> 1 /\ x <> n}``); - -(* Theorem: x IN (proper_divisors n) <=> (1 < x /\ x < n /\ x divides n) *) -(* Proof: - Since x IN (divisors n) - ==> 0 < x /\ x <= n /\ x divides n by divisors_element - Since x <= n but x <> n, x < n. - With x <> 0 /\ x <> 1 ==> 1 < x. -*) -val proper_divisors_element = store_thm( - "proper_divisors_element", - ``!n x. x IN (proper_divisors n) <=> (1 < x /\ x < n /\ x divides n)``, - rw[divisors_element, EQ_IMP_THM]); - -(* Theorem: proper_divisors 0 = {} *) -(* Proof: by proper_divisors_element. *) -val proper_divisors_0 = store_thm( - "proper_divisors_0", - ``proper_divisors 0 = {}``, - rw[proper_divisors_element, EXTENSION]); - -(* Theorem: proper_divisors 1 = {} *) -(* Proof: by proper_divisors_element. *) -val proper_divisors_1 = store_thm( - "proper_divisors_1", - ``proper_divisors 1 = {}``, - rw[proper_divisors_element, EXTENSION]); - -(* Theorem: (proper_divisors n) SUBSET (less_divisors n) *) -(* Proof: by SUBSET_DEF *) -val proper_divisors_subset = store_thm( - "proper_divisors_subset", - ``!n. (proper_divisors n) SUBSET (less_divisors n)``, - rw[SUBSET_DEF]); - -(* Theorem: FINITE (proper_divisors n) *) -(* Proof: - Since (proper_divisors n) SUBSET (less_divisors n) by proper_divisors_subset - and FINITE (less_divisors n) by less_divisors_finite - so FINITE (proper_divisors n) by SUBSET_FINITE -*) -val proper_divisors_finite = store_thm( - "proper_divisors_finite", - ``!n. FINITE (proper_divisors n)``, - metis_tac[less_divisors_finite, proper_divisors_subset, SUBSET_FINITE]); - -(* Theorem: 1 NOTIN (proper_divisors n) *) -(* Proof: proper_divisors_element *) -val proper_divisors_not_1 = store_thm( - "proper_divisors_not_1", - ``!n. 1 NOTIN (proper_divisors n)``, - rw[proper_divisors_element]); - -(* Theorem: proper_divisors n = (less_divisors n) DELETE 1 *) -(* Proof: - proper_divisors n - = {x | x IN (divisors n) /\ x <> 1 /\ x <> n} by notation - = {x | x IN (divisors n) /\ x <> n} DELETE 1 by IN_DELETE - = (less_divisors n) DELETE 1 -*) -val proper_divisors_by_less_divisors = store_thm( - "proper_divisors_by_less_divisors", - ``!n. proper_divisors n = (less_divisors n) DELETE 1``, - rw[divisors_element, EXTENSION, EQ_IMP_THM]); - -(* Theorem: prime n ==> (proper_divisors n = {}) *) -(* Proof: - proper_divisors n - = (less_divisors n) DELETE 1 by proper_divisors_by_less_divisors - = {1} DELETE 1 by less_divisors_prime, prime n - = {} by SING_DELETE -*) -val proper_divisors_prime = store_thm( - "proper_divisors_prime", - ``!n. prime n ==> (proper_divisors n = {})``, - rw[proper_divisors_by_less_divisors, less_divisors_prime]); - -(* Theorem: d IN (proper_divisors n) ==> (n DIV d) IN (proper_divisors n) *) -(* Proof: - Let e = n DIV d. - Since d IN (proper_divisors n) - ==> 1 < d /\ d < n by proper_divisors_element - and d IN (less_divisors n) by proper_divisors_subset - so e IN (less_divisors n) by less_divisors_has_cofactor - and 0 < e by less_divisors_nonzero - Since d divides n by less_divisors_element - so n = e * d by DIV_MULT_EQ, 0 < d - thus e <> 1 since n <> d by MULT_LEFT_1 - With 0 < e /\ e <> 1 - ==> e IN (proper_divisors n) by proper_divisors_by_less_divisors, IN_DELETE -*) -val proper_divisors_has_cofactor = store_thm( - "proper_divisors_has_cofactor", - ``!n d. d IN (proper_divisors n) ==> (n DIV d) IN (proper_divisors n)``, - rpt strip_tac >> - qabbrev_tac `e = n DIV d` >> - `1 < d /\ d < n` by metis_tac[proper_divisors_element] >> - `d IN (less_divisors n)` by metis_tac[proper_divisors_subset, SUBSET_DEF] >> - `e IN (less_divisors n)` by rw[less_divisors_has_cofactor, Abbr`e`] >> - `0 < e` by metis_tac[less_divisors_nonzero] >> - `0 < d /\ n <> d` by decide_tac >> - `e <> 1` by metis_tac[less_divisors_element, DIV_MULT_EQ, MULT_LEFT_1] >> - metis_tac[proper_divisors_by_less_divisors, IN_DELETE]); - -(* Theorem: (proper_divisors n) <> {} ==> 1 < MIN_SET (proper_divisors n) *) -(* Proof: - Let s = proper_divisors n. - Since !x. x IN s ==> 1 < x by proper_divisors_element - But MIN_SET s IN s by MIN_SET_IN_SET - Hence 1 < MIN_SET s by above -*) -val proper_divisors_min_gt_1 = store_thm( - "proper_divisors_min_gt_1", - ``!n. (proper_divisors n) <> {} ==> 1 < MIN_SET (proper_divisors n)``, - metis_tac[MIN_SET_IN_SET, proper_divisors_element]); - -(* Theorem: (proper_divisors n) <> {} ==> - (MAX_SET (proper_divisors n) = n DIV (MIN_SET (proper_divisors n))) /\ - (MIN_SET (proper_divisors n) = n DIV (MAX_SET (proper_divisors n))) *) -(* Proof: - Let s = proper_divisors n, b = MIN_SET s. - By MAX_SET_ELIM, this is to show: - (1) FINITE s, true by proper_divisors_finite - (2) s <> {} /\ x IN s /\ !y. y IN s ==> y <= x ==> x = n DIV b /\ b = n DIV x - Note s <> {} ==> n <> 0 by proper_divisors_0 - Let m = n DIV b. - Note n DIV x IN s by proper_divisors_has_cofactor, 0 < n, 1 < b. - Also b IN s /\ b <= x by MIN_SET_IN_SET, s <> {} - thus 1 < b by proper_divisors_min_gt_1 - so m IN s by proper_divisors_has_cofactor, 0 < n, 1 < x. - or 1 < m by proper_divisors_nonzero - and m <= x by implication, x = MAX_SET s. - Thus n DIV x <= n DIV m by DIV_LE_MONOTONE_REVERSE [1], 0 < x, 0 < m. - But n DIV m - = n DIV (n DIV b) = b by divide_by_cofactor, b divides n. - so n DIV x <= b by [1] - Since b <= n DIV x by MIN_SET_PROPERTY, b = MIN_SET s, n DIV x IN s. - so n DIV x = b by LESS_EQUAL_ANTISYM (gives second subgoal) - Hence m = n DIV b - = n DIV (n DIV x) = x by divide_by_cofactor, x divides n (gives first subgoal) -*) -val proper_divisors_max_min = store_thm( - "proper_divisors_max_min", - ``!n. (proper_divisors n) <> {} ==> - (MAX_SET (proper_divisors n) = n DIV (MIN_SET (proper_divisors n))) /\ - (MIN_SET (proper_divisors n) = n DIV (MAX_SET (proper_divisors n)))``, - ntac 2 strip_tac >> - qabbrev_tac `s = proper_divisors n` >> - qabbrev_tac `b = MIN_SET s` >> - DEEP_INTRO_TAC MAX_SET_ELIM >> - strip_tac >- - rw[proper_divisors_finite, Abbr`s`] >> - ntac 3 strip_tac >> - `n <> 0` by metis_tac[proper_divisors_0] >> - `b IN s /\ b <= x` by rw[MIN_SET_IN_SET, Abbr`b`] >> - `1 < b` by rw[proper_divisors_min_gt_1, Abbr`s`, Abbr`b`] >> - `0 < n /\ 1 < x` by decide_tac >> - qabbrev_tac `m = n DIV b` >> - `m IN s /\ (n DIV x) IN s` by rw[proper_divisors_has_cofactor, Abbr`m`, Abbr`s`] >> - `1 < m` by metis_tac[proper_divisors_element] >> - `0 < x /\ 0 < m` by decide_tac >> - `n DIV x <= n DIV m` by rw[DIV_LE_MONOTONE_REVERSE] >> - `b divides n /\ x divides n` by metis_tac[proper_divisors_element] >> - `n DIV m = b` by rw[divide_by_cofactor, Abbr`m`] >> - `b <= n DIV x` by rw[MIN_SET_PROPERTY, Abbr`b`] >> - `b = n DIV x` by rw[LESS_EQUAL_ANTISYM] >> - `m = x` by rw[divide_by_cofactor, Abbr`m`] >> - decide_tac); - -(* This is a milestone theorem. *) - -(* ------------------------------------------------------------------------- *) -(* Useful Properties of Less Divisors *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 1 < n ==> (MIN_SET (less_divisors n) = 1) *) -(* Proof: - Let s = less_divisors n. - Since 1 < n ==> 1 IN s by less_divisors_has_1 - so s <> {} by MEMBER_NOT_EMPTY - and !y. y IN s ==> 0 < y by less_divisors_nonzero - or !y. y IN s ==> 1 <= y by LESS_EQ - Hence 1 = MIN_SET s by MIN_SET_TEST -*) -val less_divisors_min = store_thm( - "less_divisors_min", - ``!n. 1 < n ==> (MIN_SET (less_divisors n) = 1)``, - metis_tac[less_divisors_has_1, MEMBER_NOT_EMPTY, - MIN_SET_TEST, less_divisors_nonzero, LESS_EQ, ONE]); - -(* Theorem: MAX_SET (less_divisors n) <= n DIV 2 *) -(* Proof: - Let s = less_divisors n, m = MAX_SET s. - If s = {}, - Then m = MAX_SET {} = 0 by MAX_SET_EMPTY - and 0 <= n DIV 2 is trivial. - If s <> {}, - Then n <> 0 /\ n <> 1 by less_divisors_0, less_divisors_1 - Note 1 IN s by less_divisors_has_1 - Consider t = s DELETE 1. - Then t = proper_divisors n by proper_divisors_by_less_divisors - If t = {}, - Then s = {1} by DELETE_EQ_SING - and m = 1 by SING_DEF, IN_SING (same as MAX_SET_SING) - Since 2 <= n by 1 < n - thus n DIV n <= n DIV 2 by DIV_LE_MONOTONE_REVERSE - or n DIV n = 1 = m <= n DIV 2 by DIVMOD_ID, 0 < n - If t <> {}, - Let b = MIN_SET t - Then MAX_SET t = n DIV b by proper_divisors_max_min, t <> {} - Since MIN_SET s = 1 by less_divisors_min, 1 < n - and FINITE s by less_divisors_finite - and s <> {1} by DELETE_EQ_SING - thus m = MAX_SET t by MAX_SET_DELETE, s <> {1} - - Now 1 < b by proper_divisors_min_gt_1 - so 2 <= b by LESS_EQ, 1 < b - Hence n DIV b <= n DIV 2 by DIV_LE_MONOTONE_REVERSE - or m <= n DIV 2 by m = MAX_SET t = n DIV b -*) - -Theorem less_divisors_max: - !n. MAX_SET (less_divisors n) <= n DIV 2 -Proof - rpt strip_tac >> - qabbrev_tac `s = less_divisors n` >> - qabbrev_tac `m = MAX_SET s` >> - Cases_on `s = {}` >- rw[MAX_SET_EMPTY, Abbr`m`] >> - `n <> 0 /\ n <> 1` by metis_tac[less_divisors_0, less_divisors_1] >> - `1 < n` by decide_tac >> - `1 IN s` by rw[less_divisors_has_1, Abbr`s`] >> - qabbrev_tac `t = proper_divisors n` >> - `t = s DELETE 1` by rw[proper_divisors_by_less_divisors, Abbr`t`, Abbr`s`] >> - Cases_on `t = {}` >| [ - `s = {1}` by rfs[] >> - `m = 1` by rw[MAX_SET_SING, Abbr`m`] >> - `(2 <= n) /\ (0 < 2) /\ (0 < n) /\ (n DIV n = 1)` by rw[] >> - metis_tac[DIV_LE_MONOTONE_REVERSE], - qabbrev_tac `b = MIN_SET t` >> - `MAX_SET t = n DIV b` by metis_tac[proper_divisors_max_min] >> - `MIN_SET s = 1` by rw[less_divisors_min, Abbr`s`] >> - `FINITE s` by rw[less_divisors_finite, Abbr`s`] >> - `s <> {1}` by metis_tac[DELETE_EQ_SING] >> - `m = MAX_SET t` by metis_tac[MAX_SET_DELETE] >> - `1 < b` by rw[proper_divisors_min_gt_1, Abbr`b`, Abbr`t`] >> - `2 <= b /\ (0 < b) /\ (0 < 2)` by decide_tac >> - `n DIV b <= n DIV 2` by rw[DIV_LE_MONOTONE_REVERSE] >> - decide_tac - ] -QED - -(* Theorem: (less_divisors n) SUBSET (natural (n DIV 2)) *) -(* Proof: - Let s = less_divisors n - If n = 0 or n - 1, - Then s = {} by less_divisors_0, less_divisors_1 - and {} SUBSET t, for any t. by EMPTY_SUBSET - If n <> 0 and n <> 1, 1 < n. - Note FINITE s by less_divisors_finite - and x IN s ==> x <= MAX_SET s by MAX_SET_PROPERTY, FINITE s - But MAX_SET s <= n DIV 2 by less_divisors_max - Thus x IN s ==> x <= n DIV 2 by LESS_EQ_TRANS - Note s <> {} by MEMBER_NOT_EMPTY, x IN s - and x IN s ==> MIN_SET s <= x by MIN_SET_PROPERTY, s <> {} - Since 1 = MIN_SET s, 1 <= x by less_divisors_min, 1 < n - Thus 0 < x <= n DIV 2 by LESS_EQ - or x IN (natural (n DIV 2)) by natural_element -*) -val less_divisors_subset_natural = store_thm( - "less_divisors_subset_natural", - ``!n. (less_divisors n) SUBSET (natural (n DIV 2))``, - rpt strip_tac >> - qabbrev_tac `s = less_divisors n` >> - qabbrev_tac `m = n DIV 2` >> - Cases_on `(n = 0) \/ (n = 1)` >- - metis_tac[less_divisors_0, less_divisors_1, EMPTY_SUBSET] >> - `1 < n` by decide_tac >> - rw_tac std_ss[SUBSET_DEF] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - `FINITE s` by rw[less_divisors_finite, Abbr`s`] >> - `x <= MAX_SET s` by rw[MAX_SET_PROPERTY] >> - `MIN_SET s <= x` by rw[MIN_SET_PROPERTY] >> - `MAX_SET s <= m` by rw[less_divisors_max, Abbr`s`, Abbr`m`] >> - `MIN_SET s = 1` by rw[less_divisors_min, Abbr`s`] >> - `0 < x /\ x <= m` by decide_tac >> - rw[natural_element]); - -(* ------------------------------------------------------------------------- *) -(* Properties of Summation equals Perfect Power *) -(* ------------------------------------------------------------------------- *) - -(* Idea for the theorem below (for m = n DIV 2 when applied in bounds): - p * (p ** m - 1) / (p - 1) - < p * p ** m / (p - 1) discard subtraction - <= p * p ** m / (p / 2) replace by smaller denominator - = 2 * p ** m double division and cancel p - or p * (p ** m - 1) < (p - 1) * 2 * p ** m -*) - -(* Theorem: 1 < p ==> !n. p * (p ** n - 1) < (p - 1) * (2 * p ** n) *) -(* Proof: - Let q = p ** n - Then 1 <= q by ONE_LE_EXP, 0 < p - so p <= p * q by LE_MULT_LCANCEL, p <> 0 - Also 1 < p ==> 2 <= p by LESS_EQ - so 2 * q <= p * q by LE_MULT_RCANCEL, q <> 0 - Thus LHS - = p * (q - 1) - = p * q - p by LEFT_SUB_DISTRIB - And RHS - = (p - 1) * (2 * q) - = p * (2 * q) - 2 * q by RIGHT_SUB_DISTRIB - = 2 * (p * q) - 2 * q by MULT_ASSOC, MULT_COMM - = (p * q + p * q) - 2 * q by TIMES2 - = (p * q - p + p + p * q) - 2 * q by SUB_ADD, p <= p * q - = LHS + p + p * q - 2 * q by above - = LHS + p + (p * q - 2 * q) by LESS_EQ_ADD_SUB, 2 * q <= p * q - = LHS + p + (p - 2) * q by RIGHT_SUB_DISTRIB - - Since 0 < p by 1 < p - and 0 <= (p - 2) * q by 2 <= p - Hence LHS < RHS by discarding positive terms -*) -val perfect_power_special_inequality = store_thm( - "perfect_power_special_inequality", - ``!p. 1 < p ==> !n. p * (p ** n - 1) < (p - 1) * (2 * p ** n)``, - rpt strip_tac >> - qabbrev_tac `q = p ** n` >> - `p <> 0 /\ 2 <= p` by decide_tac >> - `1 <= q` by rw[ONE_LE_EXP, Abbr`q`] >> - `p <= p * q` by rw[] >> - `2 * q <= p * q` by rw[] >> - qabbrev_tac `l = p * (q - 1)` >> - qabbrev_tac `r = (p - 1) * (2 * q)` >> - `l = p * q - p` by rw[Abbr`l`] >> - `r = p * (2 * q) - 2 * q` by rw[Abbr`r`] >> - `_ = 2 * (p * q) - 2 * q` by rw[] >> - `_ = (p * q + p * q) - 2 * q` by rw[] >> - `_ = (p * q - p + p + p * q) - 2 * q` by rw[] >> - `_ = l + p + p * q - 2 * q` by rw[] >> - `_ = l + p + (p * q - 2 * q)` by rw[] >> - `_ = l + p + (p - 2) * q` by rw[] >> - decide_tac); - -(* Theorem: 1 < p /\ 1 < n ==> - p ** (n DIV 2) * p ** (n DIV 2) <= p ** n /\ - 2 * p ** (n DIV 2) <= p ** (n DIV 2) * p ** (n DIV 2) *) -(* Proof: - Let m = n DIV 2, q = p ** m. - The goal becomes: q * q <= p ** n /\ 2 * q <= q * q. - Note 1 < p ==> 0 < p. - First goal: q * q <= p ** n - Then 0 < q by EXP_POS, 0 < p - and 2 * m <= n by DIV_MULT_LE, 0 < 2. - thus p ** (2 * m) <= p ** n by EXP_BASE_LE_MONO, 1 < p. - Since p ** (2 * m) - = p ** (m + m) by TIMES2 - = q * q by EXP_ADD - Thus q * q <= p ** n by above - - Second goal: 2 * q <= q * q - Since 1 < n, so 2 <= n by LESS_EQ - so 2 DIV 2 <= n DIV 2 by DIV_LE_MONOTONE, 0 < 2. - or 1 <= m, i.e. 0 < m by DIVMOD_ID, 0 < 2. - Thus 1 < q by ONE_LT_EXP, 1 < p, 0 < m. - so 2 <= q by LESS_EQ - and 2 * q <= q * q by MULT_RIGHT_CANCEL, q <> 0. - Hence 2 * q <= p ** n by LESS_EQ_TRANS -*) -val perfect_power_half_inequality_lemma = prove( - ``!p n. 1 < p /\ 1 < n ==> - p ** (n DIV 2) * p ** (n DIV 2) <= p ** n /\ - 2 * p ** (n DIV 2) <= p ** (n DIV 2) * p ** (n DIV 2)``, - ntac 3 strip_tac >> - qabbrev_tac `m = n DIV 2` >> - qabbrev_tac `q = p ** m` >> - strip_tac >| [ - `0 < p /\ 0 < 2` by decide_tac >> - `0 < q /\ q <> 0` by rw[EXP_POS, Abbr`q`] >> - `2 * m <= n` by metis_tac[DIV_MULT_LE, MULT_COMM] >> - `p ** (2 * m) <= p ** n` by rw[EXP_BASE_LE_MONO] >> - `p ** (2 * m) = p ** (m + m)` by rw[] >> - `_ = q * q` by rw[EXP_ADD, Abbr`q`] >> - decide_tac, - `2 <= n /\ 0 < 2` by decide_tac >> - `1 <= m` by metis_tac[DIV_LE_MONOTONE, DIVMOD_ID] >> - `0 < m` by decide_tac >> - `1 < q` by rw[ONE_LT_EXP, Abbr`q`] >> - rw[] - ]); - -(* Theorem: 1 < p /\ 0 < n ==> 2 * p ** (n DIV 2) <= p ** n *) -(* Proof: - Let m = n DIV 2, q = p ** m. - The goal becomes: 2 * q <= p ** n - If n = 1, - Then m = 0 by ONE_DIV, 0 < 2. - and q = 1 by EXP - and p ** n = p by EXP_1 - Since 1 < p ==> 2 <= p by LESS_EQ - Hence 2 * q <= p = p ** n by MULT_RIGHT_1 - If n <> 1, 1 < n. - Then q * q <= p ** n /\ - 2 * q <= q * q by perfect_power_half_inequality_lemma - Hence 2 * q <= p ** n by LESS_EQ_TRANS -*) -val perfect_power_half_inequality_1 = store_thm( - "perfect_power_half_inequality_1", - ``!p n. 1 < p /\ 0 < n ==> 2 * p ** (n DIV 2) <= p ** n``, - rpt strip_tac >> - qabbrev_tac `m = n DIV 2` >> - qabbrev_tac `q = p ** m` >> - Cases_on `n = 1` >| [ - `m = 0` by rw[Abbr`m`] >> - `(q = 1) /\ (p ** n = p)` by rw[Abbr`q`] >> - `2 <= p` by decide_tac >> - rw[], - `1 < n` by decide_tac >> - `q * q <= p ** n /\ 2 * q <= q * q` by rw[perfect_power_half_inequality_lemma, Abbr`q`, Abbr`m`] >> - decide_tac - ]); - -(* Theorem: 1 < p /\ 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) <= p ** n - 2 * p ** (n DIV 2) *) -(* Proof: - Let m = n DIV 2, q = p ** m. - The goal becomes: (q - 2) * q <= p ** n - 2 * q - If n = 1, - Then m = 0 by ONE_DIV, 0 < 2. - and q = 1 by EXP - and p ** n = p by EXP_1 - Since 1 < p ==> 2 <= p by LESS_EQ - or 0 <= p - 2 by SUB_LEFT_LESS_EQ - Hence (q - 2) * q = 0 <= p - 2 - If n <> 1, 1 < n. - Then q * q <= p ** n /\ 2 * q <= q * q by perfect_power_half_inequality_lemma - Thus q * q - 2 * q <= p ** n - 2 * q by LE_SUB_RCANCEL, 2 * q <= q * q - or (q - 2) * q <= p ** n - 2 * q by RIGHT_SUB_DISTRIB -*) -val perfect_power_half_inequality_2 = store_thm( - "perfect_power_half_inequality_2", - ``!p n. 1 < p /\ 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) <= p ** n - 2 * p ** (n DIV 2)``, - rpt strip_tac >> - qabbrev_tac `m = n DIV 2` >> - qabbrev_tac `q = p ** m` >> - Cases_on `n = 1` >| [ - `m = 0` by rw[Abbr`m`] >> - `(q = 1) /\ (p ** n = p)` by rw[Abbr`q`] >> - `0 <= p - 2 /\ (1 - 2 = 0)` by decide_tac >> - rw[], - `1 < n` by decide_tac >> - `q * q <= p ** n /\ 2 * q <= q * q` by rw[perfect_power_half_inequality_lemma, Abbr`q`, Abbr`m`] >> - decide_tac - ]); - -(* Already in pred_setTheory: -SUM_IMAGE_SUBSET_LE; -!f s t. FINITE s /\ t SUBSET s ==> SIGMA f t <= SIGMA f s: thm -SUM_IMAGE_MONO_LESS_EQ; -|- !s. FINITE s ==> (!x. x IN s ==> f x <= g x) ==> SIGMA f s <= SIGMA g s: thm -*) - -(* Theorem: 1 < p ==> !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> - (!n. 0 < n ==> n * (f n) <= p ** n) /\ - (!n. 0 < n ==> p ** n - 2 * p ** (n DIV 2) < n * (f n)) *) -(* Proof: - Step 1: prove a specific lemma for sum decomposition - Claim: !n. 0 < n ==> (divisors n DIFF {n}) SUBSET (natural (n DIV 2)) /\ - (p ** n = SIGMA (\d. d * f d) (divisors n)) ==> - (p ** n = n * f n + SIGMA (\d. d * f d) (divisors n DIFF {n})) - Proof: Let s = divisors n, a = {n}, b = s DIFF a, m = n DIV 2. - Then b = less_divisors n by EXTENSION,IN_DIFF - and b SUBSET (natural m) by less_divisors_subset_natural - This gives the first part. - For the second part: - Note a SUBSET s by divisors_has_last, SUBSET_DEF - and b SUBSET s by DIFF_SUBSET - Thus s = b UNION a by UNION_DIFF, a SUBSET s - and DISJOINT b a by DISJOINT_DEF, EXTENSION - Now FINITE s by divisors_finite - so FINITE a /\ FINITE b by SUBSET_FINITE, by a SUBSEt s /\ b SUBSET s - - p ** n - = SIGMA (\d. d * f d) s by implication - = SIGMA (\d. d * f d) (b UNION a) by above, s = b UNION a - = SIGMA (\d. d * f d) b + SIGMA (\d. d * f d) a by SUM_IMAGE_DISJOINT, FINITE a /\ FINITE b - = SIGMA (\d. d * f d) b + n * f n by SUM_IMAGE_SING - = n * f n + SIGMA (\d. d * f d) b by ADD_COMM - This gives the second part. - - Step 2: Upper bound, to show: !n. 0 < n ==> n * f n <= p ** n - Let b = divisors n DIFF {n} - Since n * f n + SIGMA (\d. d * f d) b = p ** n by lemma - Hence n * f n <= p ** n by 0 <= SIGMA (\d. d * f d) b - - Step 3: Lower bound, to show: !n. 0 < n ==> p ** n - p ** (n DIV 2) <= n * f n - Let s = divisors n, a = {n}, b = s DIFF a, m = n DIV 2. - Note b SUBSET (natural m) /\ - (p ** n = n * f n + SIGMA (\d. d * f d) b) by lemma - Since FINITE (natural m) by natural_finite - thus SIGMA (\d. d * f d) b - <= SIGMA (\d. d * f d) (natural m) by SUM_IMAGE_SUBSET_LE [1] - Also !d. d IN (natural m) ==> 0 < d by natural_element - and !d. 0 < d ==> d * f d <= p ** d by upper bound (Step 2) - thus !d. d IN (natural m) ==> d * f d <= p ** d by implication - Hence SIGMA (\d. d * f d) (natural m) - <= SIGMA (\d. p ** d) (natural m) by SUM_IMAGE_MONO_LESS_EQ [2] - Now 1 < p ==> 0 < p /\ (p - 1) <> 0 by arithmetic - - (p - 1) * SIGMA (\d. d * f d) b - <= (p - 1) * SIGMA (\d. d * f d) (natural m) by LE_MULT_LCANCEL, [1] - <= (p - 1) * SIGMA (\d. p ** d) (natural m) by LE_MULT_LCANCEL, [2] - = p * (p ** m - 1) by sigma_geometric_natural_eqn - < (p - 1) * (2 * p ** m) by perfect_power_special_inequality - - (p - 1) * SIGMA (\d. d * f d) b < (p - 1) * (2 * p ** m) by LESS_EQ_LESS_TRANS - or SIGMA (\d. d * f d) b < 2 * p ** m by LT_MULT_LCANCEL, (p - 1) <> 0 - - But 2 * p ** m <= p ** n by perfect_power_half_inequality_1, 1 < p, 0 < n - Thus p ** n = p ** n - 2 * p ** m + 2 * p ** m by SUB_ADD, 2 * p ** m <= p ** n - Combinig with lemma, - p ** n - 2 * p ** m + 2 * p ** m < n * f n + 2 * p ** m - or p ** n - 2 * p ** m < n * f n by LESS_MONO_ADD_EQ, no condition -*) -Theorem sigma_eq_perfect_power_bounds_1: - !p. - 1 < p ==> - !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> - (!n. 0 < n ==> n * (f n) <= p ** n) /\ - (!n. 0 < n ==> p ** n - 2 * p ** (n DIV 2) < n * (f n)) -Proof - ntac 4 strip_tac >> - ‘∀n. 0 < n ==> - (divisors n DIFF {n}) SUBSET (natural (n DIV 2)) /\ - (p ** n = SIGMA (\d. d * f d) (divisors n) ==> - p ** n = n * f n + SIGMA (\d. d * f d) (divisors n DIFF {n}))’ - by (ntac 2 strip_tac >> - qabbrev_tac `s = divisors n` >> - qabbrev_tac `a = {n}` >> - qabbrev_tac `b = s DIFF a` >> - qabbrev_tac `m = n DIV 2` >> - `b = less_divisors n` by rw[EXTENSION, Abbr`b`, Abbr`a`, Abbr`s`] >> - `b SUBSET (natural m)` by metis_tac[less_divisors_subset_natural] >> - strip_tac >- rw[] >> - `a SUBSET s` by rw[divisors_has_last, SUBSET_DEF, Abbr`s`, Abbr`a`] >> - `b SUBSET s` by rw[Abbr`b`] >> - `s = b UNION a` by rw[UNION_DIFF, Abbr`b`] >> - `DISJOINT b a` - by (rw[DISJOINT_DEF, Abbr`b`, EXTENSION] >> metis_tac[]) >> - `FINITE s` by rw[divisors_finite, Abbr`s`] >> - `FINITE a /\ FINITE b` by metis_tac[SUBSET_FINITE] >> - strip_tac >> - `_ = SIGMA (\d. d * f d) (b UNION a)` by metis_tac[Abbr`s`] >> - `_ = SIGMA (\d. d * f d) b + SIGMA (\d. d * f d) a` - by rw[SUM_IMAGE_DISJOINT] >> - `_ = SIGMA (\d. d * f d) b + n * f n` by rw[SUM_IMAGE_SING, Abbr`a`] >> - rw[]) >> - conj_asm1_tac >| [ - rpt strip_tac >> - `p ** n = n * f n + SIGMA (\d. d * f d) (divisors n DIFF {n})` by rw[] >> - decide_tac, - rpt strip_tac >> - qabbrev_tac `s = divisors n` >> - qabbrev_tac `a = {n}` >> - qabbrev_tac `b = s DIFF a` >> - qabbrev_tac `m = n DIV 2` >> - `b SUBSET (natural m) /\ (p ** n = n * f n + SIGMA (\d. d * f d) b)` - by rw[Abbr`s`, Abbr`a`, Abbr`b`, Abbr`m`] >> - `FINITE (natural m)` by rw[natural_finite] >> - `SIGMA (\d. d * f d) b <= SIGMA (\d. d * f d) (natural m)` - by rw[SUM_IMAGE_SUBSET_LE] >> - `!d. d IN (natural m) ==> 0 < d` by rw[natural_element] >> - `SIGMA (\d. d * f d) (natural m) <= SIGMA (\d. p ** d) (natural m)` - by rw[SUM_IMAGE_MONO_LESS_EQ] >> - `0 < p /\ (p - 1) <> 0` by decide_tac >> - `(p - 1) * SIGMA (\d. p ** d) (natural m) = p * (p ** m - 1)` - by rw[sigma_geometric_natural_eqn] >> - `p * (p ** m - 1) < (p - 1) * (2 * p ** m)` - by rw[perfect_power_special_inequality] >> - `SIGMA (\d. d * f d) b < 2 * p ** m` - by metis_tac[LE_MULT_LCANCEL, LESS_EQ_TRANS, LESS_EQ_LESS_TRANS, - LT_MULT_LCANCEL] >> - `p ** n < n * f n + 2 * p ** m` by decide_tac >> - `2 * p ** m <= p ** n` by rw[perfect_power_half_inequality_1, Abbr`m`] >> - decide_tac - ] -QED - -(* Theorem: 1 < p ==> !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> - (!n. 0 < n ==> n * (f n) <= p ** n) /\ - (!n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n)) *) -(* Proof: - For the first goal: (!n. 0 < n ==> n * (f n) <= p ** n) - True by sigma_eq_perfect_power_bounds_1. - For the second goal: (!n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n)) - Let m = n DIV 2. - Then p ** n - 2 * p ** m < n * (f n) by sigma_eq_perfect_power_bounds_1 - and (p ** m - 2) * p ** m <= p ** n - 2 * p ** m by perfect_power_half_inequality_2 - Hence (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n) by LESS_EQ_LESS_TRANS -*) -val sigma_eq_perfect_power_bounds_2 = store_thm( - "sigma_eq_perfect_power_bounds_2", - ``!p. 1 < p ==> !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> - (!n. 0 < n ==> n * (f n) <= p ** n) /\ - (!n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n))``, - rpt strip_tac >- - rw[sigma_eq_perfect_power_bounds_1] >> - qabbrev_tac `m = n DIV 2` >> - `p ** n - 2 * p ** m < n * (f n)` by rw[sigma_eq_perfect_power_bounds_1, Abbr`m`] >> - `(p ** m - 2) * p ** m <= p ** n - 2 * p ** m` by rw[perfect_power_half_inequality_2, Abbr`m`] >> - decide_tac); - -(* This is a milestone theorem. *) - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/README.md b/examples/algebra/lib/README.md deleted file mode 100644 index e0092cc4e8..0000000000 --- a/examples/algebra/lib/README.md +++ /dev/null @@ -1,25 +0,0 @@ - -# Library - -These scripts develop basic theories to support various libraries. - -## Enhancement -* __helperNum__, more theorems on parity, divisibility, inequalities, `MAX` and `MIN`. -* __helperSet__, more theormes on function images, `SUM_SET` and `PROD_SET`. -* __helperList__, enhance listTheory with `SUM`, `PROD`, `MAX_LIST`, `MIN_LIST`, `rotate`, and `turn`. -* __helperFunction__, collect theorems for integer functions: `SQRT`, `ROOT`, `FACT`, `GCD`, and `LCM`. - -## Number Theory -* __Euler__, number-theoretic sets, and Euler's φ function. -* __Gauss__, coprimes, properties of φ; function, and Gauss' Little Theorem. -* __Mobius__, work on Möbius inversion. - -## Number Patterns -* __binomial__, properties of binomial coefficients in Pascal's Triangle. -* __triangle__, properties of Leibniz's Denominator Triangle, relating to consecutive LCM. -* __primes__, properties of two-factors, and a primality test. -* __primePower__, properties of prime powers and divisors, an investigation on consecutive LCM. -* __logPower__, properties of perfect power, power free, and upper logarithm. - -## Applications -* __sublist__, order-preserving sublist and properties. \ No newline at end of file diff --git a/examples/algebra/lib/binomialScript.sml b/examples/algebra/lib/binomialScript.sml deleted file mode 100644 index e5419dbf38..0000000000 --- a/examples/algebra/lib/binomialScript.sml +++ /dev/null @@ -1,1516 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Binomial coefficients and expansion. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "binomial"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* val _ = load "helperListTheory"; *) -open helperListTheory; - -(* open dependent theories *) -open pred_setTheory listTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open arithmeticTheory dividesTheory gcdTheory; - - -(* ------------------------------------------------------------------------- *) -(* Binomial scripts in HOL: -C:\jc\www\ml\hol\info\Hol\examples\miller\RSA\summationScript.sml -C:\jc\www\ml\hol\info\Hol\examples\miller\RSA\powerScript.sml -C:\jc\www\ml\hol\info\Hol\examples\miller\RSA\binomialScript.sml -*) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Binomial Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: -*) -(* Definitions and Theorems (# are exported): - - Binomial Coefficients: - binomial_def |- (binomial 0 0 = 1) /\ (!n. binomial (SUC n) 0 = 1) /\ - (!k. binomial 0 (SUC k) = 0) /\ - !n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k) - binomial_alt |- !n k. binomial n 0 = 1 /\ binomial 0 (k + 1) = 0 /\ - binomial (n + 1) (k + 1) = binomial n k + binomial n (k + 1) - binomial_less_0 |- !n k. n < k ==> (binomial n k = 0) - binomial_n_0 |- !n. binomial n 0 = 1 - binomial_n_n |- !n. binomial n n = 1 - binomial_0_n |- !n. binomial 0 n = if n = 0 then 1 else 0 - binomial_recurrence |- !n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k) - binomial_formula |- !n k. binomial (n + k) k * (FACT n * FACT k) = FACT (n + k) - binomial_formula2 |- !n k. k <= n ==> (FACT n = binomial n k * (FACT (n - k) * FACT k)) - binomial_formula3 |- !n k. k <= n ==> (binomial n k = FACT n DIV (FACT k * FACT (n - k))) - binomial_fact |- !n k. k <= n ==> (binomial n k = FACT n DIV (FACT k * FACT (n - k))) - binomial_n_k |- !n k. k <= n ==> (binomial n k = FACT n DIV FACT k DIV FACT (n - k) - binomial_n_1 |- !n. binomial n 1 = n - binomial_sym |- !n k. k <= n ==> (binomial n k = binomial n (n - k)) - binomial_is_integer |- !n k. k <= n ==> (FACT k * FACT (n - k)) divides (FACT n) - binomial_pos |- !n k. k <= n ==> 0 < binomial n k - binomial_eq_0 |- !n k. (binomial n k = 0) <=> n < k - binomial_1_n |- !n. binomial 1 n = if 1 < n then 0 else 1 - binomial_up_eqn |- !n. 0 < n ==> !k. n * binomial (n - 1) k = (n - k) * binomial n k - binomial_up |- !n. 0 < n ==> !k. binomial (n - 1) k = (n - k) * binomial n k DIV n - binomial_right_eqn |- !n. 0 < n ==> !k. (k + 1) * binomial n (k + 1) = (n - k) * binomial n k - binomial_right |- !n. 0 < n ==> !k. binomial n (k + 1) = (n - k) * binomial n k DIV (k + 1) - binomial_monotone |- !n k. k < HALF n ==> binomial n k < binomial n (k + 1) - binomial_max |- !n k. binomial n k <= binomial n (HALF n) - binomial_iff |- !f. f = binomial <=> - !n k. f n 0 = 1 /\ f 0 (k + 1) = 0 /\ - f (n + 1) (k + 1) = f n k + f n (k + 1) - - Primes and Binomial Coefficients: - prime_divides_binomials |- !n. prime n ==> 1 < n /\ !k. 0 < k /\ k < n ==> n divides (binomial n k) - prime_divides_binomials_alt |- !n k. prime n /\ 0 < k /\ k < n ==> n divides binomial n k - prime_divisor_property |- !n p. 1 < n /\ p < n /\ prime p /\ p divides n ==> ~(p divides (FACT (n - 1) DIV FACT (n - p))) - divides_binomials_imp_prime |- !n. 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k)) ==> prime n - prime_iff_divides_binomials |- !n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> n divides (binomial n k) - prime_iff_divides_binomials_alt - |- !n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> binomial n k MOD n = 0 - - Binomial Theorem: - GENLIST_binomial_index_shift |- !n x y. GENLIST ((\k. binomial n k * x ** SUC (n - k) * y ** k) o SUC) n = - GENLIST (\k. binomial n (SUC k) * x ** (n - k) * y ** SUC k) n - binomial_index_shift |- !n x y. (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) o SUC = - (\k. binomial (SUC n) (SUC k) * x ** (n - k) * y ** SUC k) - binomial_term_merge_x |- !n x y. (\k. x * k) o (\k. binomial n k * x ** (n - k) * y ** k) = - (\k. binomial n k * x ** SUC (n - k) * y ** k) - binomial_term_merge_y |- !n x y. (\k. y * k) o (\k. binomial n k * x ** (n - k) * y ** k) = - (\k. binomial n k * x ** (n - k) * y ** SUC k) - binomial_thm |- !n x y. (x + y) ** n = SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n)) - binomial_thm_alt |- !n x y. (x + y) ** n = SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (n + 1)) - binomial_sum |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n - binomial_sum_alt |- !n. SUM (GENLIST (binomial n) (n + 1)) = 2 ** n - - Binomial Horizontal List: - binomial_horizontal_0 |- binomial_horizontal 0 = [1] - binomial_horizontal_len |- !n. LENGTH (binomial_horizontal n) = n + 1 - binomial_horizontal_mem |- !n k. k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n) - binomial_horizontal_mem_iff |- !n k. MEM (binomial n k) (binomial_horizontal n) <=> k <= n - binomial_horizontal_member |- !n x. MEM x (binomial_horizontal n) <=> ?k. k <= n /\ (x = binomial n k) - binomial_horizontal_element |- !n k. k <= n ==> (EL k (binomial_horizontal n) = binomial n k) - binomial_horizontal_pos |- !n. EVERY (\x. 0 < x) (binomial_horizontal n) - binomial_horizontal_pos_alt |- !n x. MEM x (binomial_horizontal n) ==> 0 < x - binomial_horizontal_sum |- !n. SUM (binomial_horizontal n) = 2 ** n - binomial_horizontal_max |- !n. MAX_LIST (binomial_horizontal n) = binomial n (HALF n) - binomial_row_max |- !n. MAX_SET (IMAGE (binomial n) (count (n + 1))) = binomial n (HALF n) - binomial_product_identity |- !m n k. k <= m /\ m <= n ==> - (binomial m k * binomial n m = binomial n k * binomial (n - k) (m - k)) - binomial_middle_upper_bound |- !n. binomial n (HALF n) <= 4 ** HALF n - - Stirling's Approximation: - Stirling = (!n. FACT n = (SQRT (2 * pi * n)) * (n DIV e) ** n) /\ - (!n. SQRT n = n ** h) /\ (2 * h = 1) /\ (0 < pi) /\ (0 < e) /\ - (!a b x y. (a * b) DIV (x * y) = (a DIV x) * (b DIV y)) /\ - (!a b c. (a DIV c) DIV (b DIV c) = a DIV b) - binomial_middle_by_stirling |- Stirling ==> - !n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = 2 ** (n + 1) DIV SQRT (2 * pi * n)) - - Useful theorems for Binomial: - binomial_range_shift |- !n . 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> - (!h. h < PRE n ==> ((binomial n (SUC h)) MOD n = 0))) - binomial_mod_zero |- !n. 0 < n ==> !k. (binomial n k MOD n = 0) <=> - (!x y. (binomial n k * x ** (n-k) * y ** k) MOD n = 0) - binomial_range_shift_alt |- !n . 0 < n ==> ((!k. 0 < k /\ k < n ==> - (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0))) <=> - (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0)))) - binomial_mod_zero_alt |- !n. 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> - !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0) - - Binomial Theorem with prime exponent: - binomial_thm_prime |- !p. prime p ==> (!x y. (x + y) ** p MOD p = (x ** p + y ** p) MOD p) -*) - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Binomial Coefficients *) -(* ------------------------------------------------------------------------- *) - -(* Define Binomials: - C(n,0) = 1 - C(0,k) = 0 if k > 0 - C(n+1,k+1) = C(n,k) + C(n,k+1) -*) -val binomial_def = Define` - (binomial 0 0 = 1) /\ - (binomial (SUC n) 0 = 1) /\ - (binomial 0 (SUC k) = 0) /\ - (binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k)) -`; - -(* Theorem: alternative definition of C(n,k). *) -(* Proof: by binomial_def. *) -Theorem binomial_alt: - !n k. (binomial n 0 = 1) /\ - (binomial 0 (k + 1) = 0) /\ - (binomial (n + 1) (k + 1) = binomial n k + binomial n (k + 1)) -Proof - rewrite_tac[binomial_def, GSYM ADD1] >> - (Cases_on `n` >> simp[binomial_def]) -QED - -(* Basic properties *) - -(* Theorem: C(n,k) = 0 if n < k *) -(* Proof: - By induction on n. - Base case: C(0,k) = 0 if 0 < k, by definition. - Step case: assume C(n,k) = 0 if n < k. - then for SUC n < k, - C(SUC n, k) - = C(SUC n, SUC h) where k = SUC h - = C(n,h) + C(n,SUC h) h < SUC h = k - = 0 + 0 by induction hypothesis - = 0 -*) -val binomial_less_0 = store_thm( - "binomial_less_0", - ``!n k. n < k ==> (binomial n k = 0)``, - Induct_on `n` >- - metis_tac[binomial_def, num_CASES, NOT_ZERO] >> - rw[binomial_def] >> - `?h. k = SUC h` by metis_tac[SUC_NOT, NOT_ZERO, SUC_EXISTS, LESS_TRANS] >> - metis_tac[binomial_def, LESS_MONO_EQ, LESS_TRANS, LESS_SUC, ADD_0]); - -(* Theorem: C(n,0) = 1 *) -(* Proof: - If n = 0, C(n, 0) = C(0, 0) = 1 by binomial_def - If n <> 0, n = SUC m, and C(SUC m, 0) = 1 by binomial_def -*) -val binomial_n_0 = store_thm( - "binomial_n_0", - ``!n. binomial n 0 = 1``, - metis_tac[binomial_def, num_CASES]); - -(* Theorem: C(n,n) = 1 *) -(* Proof: - By induction on n. - Base case: C(0,0) = 1, true by binomial_def. - Step case: assume C(n,n) = 1 - C(SUC n, SUC n) - = C(n,n) + C(n,SUC n) - = 1 + C(n,SUC n) by induction hypothesis - = 1 + 0 by binomial_less_0 - = 1 -*) -val binomial_n_n = store_thm( - "binomial_n_n", - ``!n. binomial n n = 1``, - Induct_on `n` >- - metis_tac[binomial_def] >> - metis_tac[binomial_def, LESS_SUC, binomial_less_0, ADD_0]); - -(* Theorem: binomial 0 n = if n = 0 then 1 else 0 *) -(* Proof: - If n = 0, - binomial 0 0 = 1 by binomial_n_0 - If n <> 0, then 0 < n. - binomial 0 n = 0 by binomial_less_0 -*) -val binomial_0_n = store_thm( - "binomial_0_n", - ``!n. binomial 0 n = if n = 0 then 1 else 0``, - rw[binomial_n_0, binomial_less_0]); - -(* Theorem: C(n+1,k+1) = C(n,k) + C(n,k+1) *) -(* Proof: by definition. *) -val binomial_recurrence = store_thm( - "binomial_recurrence", - ``!n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k)``, - rw[binomial_def]); - -(* Theorem: C(n+k,k) = (n+k)!/n!k! *) -(* Proof: - By induction on k. - Base case: C(n,0) = n!n! = 1 by binomial_n_0 - Step case: assume C(n+k,k) = (n+k)!/n!k! - To prove C(n+SUC k, SUC k) = (n+SUC k)!/n!(SUC k)! - By induction on n. - Base case: C(SUC k, SUC k) = (SUC k)!/(SUC k)! = 1 by binomial_n_n - Step case: assume C(n+SUC k, SUC k) = (n +SUC k)!/n!(SUC k)! - To prove C(SUC n + SUC k, SUC k) = (SUC n + SUC k)!/(SUC n)!(SUC k)! - C(SUC n + SUC k, SUC k) - = C(SUC SUC (n+k), SUC k) - = C(SUC (n+k),k) + C(SUC (n+k), SUC k) - = C(SUC n + k, k) + C(n + SUC k, SUC k) - = (SUC n + k)!/(SUC n)!k! + (n + SUC k)!/n!(SUC k)! by two induction hypothesis - = ((SUC n + k)!(SUC k) + (n + SUC k)(SUC n))/(SUC n)!(SUC k)! - = (SUC n + SUC k)!/(SUC n)!(SUC k)! -*) -val binomial_formula = store_thm( - "binomial_formula", - ``!n k. binomial (n+k) k * (FACT n * FACT k) = FACT (n+k)``, - Induct_on `k` >- - metis_tac[binomial_n_0, FACT, MULT_CLAUSES, ADD_0] >> - Induct_on `n` >- - metis_tac[binomial_n_n, FACT, MULT_CLAUSES, ADD_CLAUSES] >> - `SUC n + SUC k = SUC (SUC (n+k))` by decide_tac >> - `SUC (n + k) = SUC n + k` by decide_tac >> - `binomial (SUC n + SUC k) (SUC k) * (FACT (SUC n) * FACT (SUC k)) = - (binomial (SUC (n + k)) k + - binomial (SUC (n + k)) (SUC k)) * (FACT (SUC n) * FACT (SUC k))` - by metis_tac[binomial_recurrence] >> - `_ = binomial (SUC (n + k)) k * (FACT (SUC n) * FACT (SUC k)) + - binomial (SUC (n + k)) (SUC k) * (FACT (SUC n) * FACT (SUC k))` - by metis_tac[RIGHT_ADD_DISTRIB] >> - `_ = binomial (SUC n + k) k * (FACT (SUC n) * ((SUC k) * FACT k)) + - binomial (n + SUC k) (SUC k) * ((SUC n) * FACT n * FACT (SUC k))` - by metis_tac[ADD_COMM, SUC_ADD_SYM, FACT] >> - `_ = binomial (SUC n + k) k * FACT (SUC n) * FACT k * (SUC k) + - binomial (n + SUC k) (SUC k) * FACT n * FACT (SUC k) * (SUC n)` - by metis_tac[MULT_COMM, MULT_ASSOC] >> - `_ = FACT (SUC n + k) * SUC k + FACT (n + SUC k) * SUC n` - by metis_tac[MULT_COMM, MULT_ASSOC] >> - `_ = FACT (SUC (n+k)) * SUC k + FACT (SUC (n+k)) * SUC n` - by metis_tac[ADD_COMM, SUC_ADD_SYM] >> - `_ = FACT (SUC (n+k)) * (SUC k + SUC n)` by metis_tac[LEFT_ADD_DISTRIB] >> - `_ = (SUC n + SUC k) * FACT (SUC (n+k))` by metis_tac[MULT_COMM, ADD_COMM] >> - metis_tac[FACT]); - -(* Theorem: C(n,k) = n!/k!(n-k)! for 0 <= k <= n *) -(* Proof: - FACT n - = FACT ((n-k)+k) by SUB_ADD, k <= n. - = binomial ((n-k)+k) k * (FACT (n-k) * FACT k) by binomial_formula - = binomial n k * (FACT (n-k) * FACT k)) by SUB_ADD, k <= n. -*) -val binomial_formula2 = store_thm( - "binomial_formula2", - ``!n k. k <= n ==> (FACT n = binomial n k * (FACT (n-k) * FACT k))``, - metis_tac[binomial_formula, SUB_ADD]); - -(* Theorem: k <= n ==> binomial n k = (FACT n) DIV ((FACT k) * (FACT (n - k))) *) -(* Proof: - binomial n k - = (binomial n k * (FACT (n - k) * FACT k)) DIV ((FACT (n - k) * FACT k)) by MULT_DIV - = (FACT n) DIV ((FACT (n - k) * FACT k)) by binomial_formula2 - = (FACT n) DIV ((FACT k * FACT (n - k))) by MULT_COMM -*) -val binomial_formula3 = store_thm( - "binomial_formula3", - ``!n k. k <= n ==> (binomial n k = (FACT n) DIV ((FACT k) * (FACT (n - k))))``, - metis_tac[binomial_formula2, MULT_COMM, MULT_DIV, MULT_EQ_0, FACT_LESS, NOT_ZERO]); - -(* Theorem alias. *) -val binomial_fact = save_thm("binomial_fact", binomial_formula3); -(* val binomial_fact = |- !n k. k <= n ==> (binomial n k = FACT n DIV (FACT k * FACT (n - k))): thm *) - -(* Theorem: k <= n ==> binomial n k = (FACT n) DIV (FACT k) DIV (FACT (n - k)) *) -(* Proof: - binomial n k - = (FACT n) DIV ((FACT k * FACT (n - k))) by binomial_formula3 - = (FACT n) DIV (FACT k) DIV (FACT (n - k)) by DIV_DIV_DIV_MULT -*) -val binomial_n_k = store_thm( - "binomial_n_k", - ``!n k. k <= n ==> (binomial n k = (FACT n) DIV (FACT k) DIV (FACT (n - k)))``, - metis_tac[DIV_DIV_DIV_MULT, binomial_formula3, MULT_EQ_0, FACT_LESS, NOT_ZERO]); - -(* Theorem: binomial n 1 = n *) -(* Proof: - If n = 0, - binomial 0 1 - = if 1 = 0 then 1 else 0 by binomial_0_n - = 0 by 1 = 0 = F - If n <> 0, then 0 < n. - Thus 1 <= n, and n = SUC (n-1) by 0 < n - binomial n 1 - = FACT n DIV FACT 1 DIV FACT (n - 1) by binomial_n_k, 1 <= n - = FACT n DIV 1 DIV (FACT (n-1)) by FACT, ONE - = FACT n DIV (FACT (n-1)) by DIV_1 - = (n * FACT (n-1)) DIV (FACT (n-1)) by FACT - = n by MULT_DIV, FACT_LESS -*) -val binomial_n_1 = store_thm( - "binomial_n_1", - ``!n. binomial n 1 = n``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[binomial_0_n] >> - `1 <= n /\ (n = SUC (n-1))` by decide_tac >> - `binomial n 1 = FACT n DIV FACT 1 DIV FACT (n - 1)` by rw[binomial_n_k] >> - `_ = FACT n DIV 1 DIV (FACT (n-1))` by EVAL_TAC >> - `_ = FACT n DIV (FACT (n-1))` by rw[] >> - `_ = (n * FACT (n-1)) DIV (FACT (n-1))` by metis_tac[FACT] >> - `_ = n` by rw[MULT_DIV, FACT_LESS] >> - rw[]); - -(* Theorem: k <= n ==> (binomial n k = binomial n (n-k)) *) -(* Proof: - Note (n-k) <= n always. - binomial n k - = (FACT n) DIV (FACT k * FACT (n - k)) by binomial_formula3, k <= n. - = (FACT n) DIV (FACT (n - k) * FACT k) by MULT_COMM - = (FACT n) DIV (FACT (n - k) * FACT (n-(n-k))) by n - (n-k) = k - = binomial n (n-k) by binomial_formula3, (n-k) <= n. -*) -val binomial_sym = store_thm( - "binomial_sym", - ``!n k. k <= n ==> (binomial n k = binomial n (n-k))``, - rpt strip_tac >> - `n - (n-k) = k` by decide_tac >> - `(n-k) <= n` by decide_tac >> - rw[binomial_formula3, MULT_COMM]); - -(* Theorem: k <= n ==> (FACT k * FACT (n-k)) divides (FACT n) *) -(* Proof: - Since FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2 - = binomial n k * (FACT k * FACT (n - k)) by MULT_COMM - Hence (FACT k * FACT (n-k)) divides (FACT n) by divides_def -*) -val binomial_is_integer = store_thm( - "binomial_is_integer", - ``!n k. k <= n ==> (FACT k * FACT (n-k)) divides (FACT n)``, - metis_tac[binomial_formula2, MULT_COMM, divides_def]); - -(* Theorem: k <= n ==> 0 < binomial n k *) -(* Proof: - Since FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2 - and 0 < FACT n, 0 < FACT (n-k), 0 < FACT k by FACT_LESS - Hence 0 < binomial n k by ZERO_LESS_MULT -*) -val binomial_pos = store_thm( - "binomial_pos", - ``!n k. k <= n ==> 0 < binomial n k``, - metis_tac[binomial_formula2, FACT_LESS, ZERO_LESS_MULT]); - -(* Theorem: (binomial n k = 0) <=> n < k *) -(* Proof: - If part: (binomial n k = 0) ==> n < k - By contradiction, suppose k <= n. - Then 0 < binomial n k by binomial_pos - This contradicts binomial n k = 0 by NOT_ZERO - Only-if part: n < k ==> (binomial n k = 0) - This is true by binomial_less_0 -*) -val binomial_eq_0 = store_thm( - "binomial_eq_0", - ``!n k. (binomial n k = 0) <=> n < k``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `k <= n` by decide_tac >> - metis_tac[binomial_pos, NOT_ZERO], - rw[binomial_less_0] - ]); - -(* Theorem: binomial 1 n = if 1 < n then 0 else 1 *) -(* Proof: - If n = 0, binomial 1 0 = 1 by binomial_n_0 - If n = 1, binomial 1 1 = 1 by binomial_n_1 - Otherwise, binomial 1 n = 0 by binomial_eq_0, 1 < n -*) -Theorem binomial_1_n: - !n. binomial 1 n = if 1 < n then 0 else 1 -Proof - rw[binomial_eq_0] >> - `n = 0 \/ n = 1` by decide_tac >- - simp[binomial_n_0] >> - simp[binomial_n_1] -QED - -(* Relating Binomial to its up-entry: - - binomial n k = (n, k, n-k) = n! / k! (n-k)! - binomial (n-1) k = (n-1, k, n-1-k) = (n-1)! / k! (n-1-k)! - = (n!/n) / k! ((n-k)!/(n-k)) - = (n-k) * binomial n k / n -*) - -(* Theorem: 0 < n ==> !k. n * binomial (n-1) k = (n-k) * (binomial n k) *) -(* Proof: - If n <= k, that is n-1 < k. - So binomial (n-1) k = 0 by binomial_less_0 - and n - k = 0 by arithmetic - Hence true by MULT_EQ_0 - Otherwise k < n, - or k <= n, 1 <= n-k, k <= n-1 - Therefore, - FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2, k <= n. - = binomial n k * ((n - k) * FACT (n-1-k) * FACT k) by FACT - = binomial n k * (n - k) * (FACT (n-1-k) * FACT k) by MULT_ASSOC - = (n - k) * binomial n k * (FACT (n-1-k) * FACT k) by MULT_COMM - FACT n = n * FACT (n-1) by FACT - = n * (binomial (n-1) k * (FACT (n-1-k) * FACT k)) by binomial_formula2, k <= n-1. - = (n * binomial (n-1) k) * (FACT (n-1-k) * FACT k) by MULT_ASSOC - Since 0 < FACT (n-1-k) * FACT k by FACT_LESS, MULT_EQ_0 - n * binomial (n-1) k = (n-k) * (binomial n k) by MULT_RIGHT_CANCEL -*) -val binomial_up_eqn = store_thm( - "binomial_up_eqn", - ``!n. 0 < n ==> !k. n * binomial (n-1) k = (n-k) * (binomial n k)``, - rpt strip_tac >> - `!n. n <> 0 <=> 0 < n` by decide_tac >> - Cases_on `n <= k` >| [ - `n-1 < k /\ (n - k = 0)` by decide_tac >> - `binomial (n - 1) k = 0` by rw[binomial_less_0] >> - metis_tac[MULT_EQ_0], - `k < n /\ k <= n /\ 1 <= n-k /\ k <= n-1` by decide_tac >> - `SUC (n-1) = n` by decide_tac >> - `SUC (n-1-k) = n - k` by metis_tac[SUB_PLUS, ADD_COMM, ADD1, SUB_ADD] >> - `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> - `_ = binomial n k * ((n - k) * FACT (n-1-k) * FACT k)` by metis_tac[FACT] >> - `_ = binomial n k * (n - k) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> - `_ = (n - k) * binomial n k * (FACT (n-1-k) * FACT k)` by rw_tac std_ss[MULT_COMM] >> - `FACT n = n * FACT (n-1)` by metis_tac[FACT] >> - `_ = n * (binomial (n-1) k * (FACT (n-1-k) * FACT k))` by rw_tac std_ss[GSYM binomial_formula2] >> - `_ = (n * binomial (n-1) k) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> - metis_tac[FACT_LESS, MULT_EQ_0, MULT_RIGHT_CANCEL] - ]); - -(* Theorem: 0 < n ==> !k. binomial (n-1) k = ((n-k) * (binomial n k)) DIV n *) -(* Proof: - Since n * binomial (n-1) k = (n-k) * (binomial n k) by binomial_up_eqn - binomial (n-1) k = (n-k) * (binomial n k) DIV n by DIV_SOLVE, 0 < n. -*) -val binomial_up = store_thm( - "binomial_up", - ``!n. 0 < n ==> !k. binomial (n-1) k = ((n-k) * (binomial n k)) DIV n``, - rw[binomial_up_eqn, DIV_SOLVE]); - -(* Relating Binomial to its right-entry: - - binomial n k = (n, k, n-k) = n! / k! (n-k)! - binomial n (k+1) = (n, k+1, n-k-1) = n! / (k+1)! (n-k-1)! - = n! / (k+1) * k! ((n-k)!/(n-k)) - = (n-k) * binomial n k / (k+1) -*) - -(* Theorem: 0 < n ==> !k. (k + 1) * binomial n (k+1) = (n - k) * binomial n k *) -(* Proof: - If n <= k, that is n < k+1. - So binomial n (k+1) = 0 by binomial_less_0 - and n - k = 0 by arithmetic - Hence true by MULT_EQ_0 - Otherwise k < n, - or k <= n, 1 <= n-k, k+1 <= n - Therefore, - FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2, k <= n. - = binomial n k * ((n - k) * FACT (n-1-k) * FACT k) by FACT - = binomial n k * (n - k) * (FACT (n-1-k) * FACT k) by MULT_ASSOC - = (n - k) * binomial n k * (FACT (n-1-k) * FACT k) by MULT_COMM - FACT n = binomial n (k+1) * (FACT (n-(k+1)) * FACT (k+1)) by binomial_formula2, k+1 <= n. - = binomial n (k+1) * (FACT (n-1-k) * FACT (k+1)) by SUB_PLUS, ADD_COMM - = binomial n (k+1) * (FACT (n-1-k) * ((k+1) * FACT k)) by FACT - = binomial n (k+1) * ((k+1) * (FACT (n-1-k) * FACT k)) by MULT_ASSOC, MULT_COMM - = (k+1) * binomial n (k+1) * (FACT (n-1-k) * FACT k) by MULT_COMM, MULT_ASSOC - Since 0 < FACT (n-1-k) * FACT k by FACT_LESS, MULT_EQ_0 - (k+1) * binomial n (k+1) = (n-k) * (binomial n k) by MULT_RIGHT_CANCEL -*) -val binomial_right_eqn = store_thm( - "binomial_right_eqn", - ``!n. 0 < n ==> !k. (k + 1) * binomial n (k+1) = (n - k) * binomial n k``, - rpt strip_tac >> - `!n. n <> 0 <=> 0 < n` by decide_tac >> - Cases_on `n <= k` >| [ - `n < k+1` by decide_tac >> - `binomial n (k+1) = 0` by rw[binomial_less_0] >> - `n - k = 0` by decide_tac >> - metis_tac[MULT_EQ_0], - `k < n /\ k <= n /\ 1 <= n-k /\ k+1 <= n` by decide_tac >> - `SUC k = k + 1` by decide_tac >> - `SUC (n-1-k) = n - k` by metis_tac[SUB_PLUS, ADD_COMM, ADD1, SUB_ADD] >> - `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> - `_ = binomial n k * ((n - k) * FACT (n-1-k) * FACT k)` by metis_tac[FACT] >> - `_ = binomial n k * (n - k) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> - `_ = (n - k) * binomial n k * (FACT (n-1-k) * FACT k)` by rw_tac std_ss[MULT_COMM] >> - `FACT n = binomial n (k+1) * (FACT (n-(k+1)) * FACT (k+1))` by rw[binomial_formula2] >> - `_ = binomial n (k+1) * (FACT (n-1-k) * FACT (k+1))` by metis_tac[SUB_PLUS, ADD_COMM] >> - `_ = binomial n (k+1) * (FACT (n-1-k) * ((k+1) * FACT k))` by metis_tac[FACT] >> - `_ = binomial n (k+1) * ((FACT (n-1-k) * (k+1)) * FACT k)` by rw[MULT_ASSOC] >> - `_ = binomial n (k+1) * ((k+1) * (FACT (n-1-k)) * FACT k)` by rw_tac std_ss[MULT_COMM] >> - `_ = (binomial n (k+1) * (k+1)) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> - `_ = (k+1) * binomial n (k+1) * (FACT (n-1-k) * FACT k)` by rw_tac std_ss[MULT_COMM] >> - metis_tac[FACT_LESS, MULT_EQ_0, MULT_RIGHT_CANCEL] - ]); - -(* Theorem: 0 < n ==> !k. binomial n (k+1) = (n - k) * binomial n k DIV (k+1) *) -(* Proof: - Since (k + 1) * binomial n (k+1) = (n - k) * binomial n k by binomial_right_eqn - binomial n (k+1) = (n - k) * binomial n k DIV (k+1) by DIV_SOLVE, 0 < k+1. -*) -val binomial_right = store_thm( - "binomial_right", - ``!n. 0 < n ==> !k. binomial n (k+1) = (n - k) * binomial n k DIV (k+1)``, - rw[binomial_right_eqn, DIV_SOLVE, DECIDE ``!k. 0 < k+1``]); - -(* - k < HALF n <=> k + 1 <= n - k -n = 5, HALF n = 2, binomial 5 k: 1, 5, 10, 10, 5, 1 - k= 0, 1, 2, 3, 4, 5 - k < 2 <=> k + 1 <= 5 - k - k = 0 1 <= 5 binomial 5 1 >= binomial 5 0 - k = 1 2 <= 4 binomial 5 2 >= binomial 5 1 -n = 6, HALF n = 3, binomial 6 k: 1, 6, 15, 20, 15, 6, 1 - k= 0, 1, 2, 3, 4, 5, 6 - k < 3 <=> k + 1 <= 6 - k - k = 0 1 <= 6 binomial 6 1 >= binomial 6 0 - k = 1 2 <= 5 binomial 6 2 >= binomial 6 1 - k = 2 3 <= 4 binomial 6 3 >= binomial 6 2 -*) - -(* Theorem: k < HALF n ==> binomial n k < binomial n (k + 1) *) -(* Proof: - Note k < HALF n ==> 0 < n by ZERO_DIV, 0 < 2 - also k < HALF n ==> k + 1 < n - k by LESS_HALF_IFF - so 0 < k + 1 /\ 0 < n - k by arithmetic - Now (k + 1) * binomial n (k + 1) = (n - k) * binomial n k by binomial_right_eqn, 0 < n - Note HALF n <= n by DIV_LESS_EQ, 0 < 2 - so k < HALF n <= n by above - Thus 0 < binomial n k by binomial_pos, k <= n - and 0 < binomial n (k + 1) by MULT_0, MULT_EQ_0 - Hence binomial n k < binomial n (k + 1) by MULT_EQ_LESS_TO_MORE -*) -val binomial_monotone = store_thm( - "binomial_monotone", - ``!n k. k < HALF n ==> binomial n k < binomial n (k + 1)``, - rpt strip_tac >> - `k + 1 < n - k` by rw[GSYM LESS_HALF_IFF] >> - `0 < k + 1 /\ 0 < n - k` by decide_tac >> - `(k + 1) * binomial n (k + 1) = (n - k) * binomial n k` by rw[binomial_right_eqn] >> - `HALF n <= n` by rw[DIV_LESS_EQ] >> - `0 < binomial n k` by rw[binomial_pos] >> - `0 < binomial n (k + 1)` by metis_tac[MULT_0, MULT_EQ_0, NOT_ZERO] >> - metis_tac[MULT_EQ_LESS_TO_MORE]); - -(* Theorem: binomial n k <= binomial n (HALF n) *) -(* Proof: - Since (k + 1) * binomial n (k + 1) = (n - k) * binomial n k by binomial_right_eqn - binomial n (k + 1) / binomial n k = (n - k) / (k + 1) - As k varies from 0, 1, to (n-1), n - the ratio varies from n/1, (n-1)/2, (n-2)/3, ...., 1/n, 0/(n+1). - The ratio is greater than 1 when (n - k) / (k + 1) > 1 - or n - k > k + 1 - or n > 2 * k + 1 - or HALF n >= k + (HALF 1) - or k <= HALF n - Thus (binomial n (HALF n)) is greater than all preceding coefficients. - For k > HALF n, note that (binomial n k = binomial n (n - k)) by binomial_sym - Hence (binomial n (HALF n)) is greater than all succeeding coefficients, too. - - If n = 0, - binomial 0 k = 1 or 0 by binomial_0_n - binomial 0 (HALF 0) = 1 by binomial_0_n, ZERO_DIV - Hence true. - If n <> 0, - If k = HALF n, trivially true. - If k < HALF n, - Then binomial n k < binomial n (HALF n) by binomial_monotone, MONOTONE_MAX - Hence true. - If ~(k < HALF n), HALF n < k. - Then n - k <= HALF n by MORE_HALF_IMP - If k > n, - Then binomial n k = 0, hence true by binomial_less_0 - If ~(k > n), then k <= n. - Then binomial n k = binomial n (n - k) by binomial_sym, k <= n - If n - k = HALF n, trivially true. - Otherwise, n - k < HALF n, - Thus binomial n (n - k) < binomial n (HALF n) by binomial_monotone, MONOTONE_MAX - Hence true. -*) -val binomial_max = store_thm( - "binomial_max", - ``!n k. binomial n k <= binomial n (HALF n)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[binomial_0_n] >> - Cases_on `k = HALF n` >- - rw[] >> - Cases_on `k < HALF n` >| [ - `binomial n k < binomial n (HALF n)` by rw[binomial_monotone, MONOTONE_MAX] >> - decide_tac, - `HALF n < k` by decide_tac >> - `n - k <= HALF n` by rw[MORE_HALF_IMP] >> - Cases_on `k > n` >- - rw[binomial_less_0] >> - `k <= n` by decide_tac >> - `binomial n k = binomial n (n - k)` by rw[GSYM binomial_sym] >> - Cases_on `n - k = HALF n` >- - rw[] >> - `n - k < HALF n` by decide_tac >> - `binomial n (n - k) < binomial n (HALF n)` by rw[binomial_monotone, MONOTONE_MAX] >> - decide_tac - ]); - -(* Idea: the recurrence relation for binomial defines itself. *) - -(* Theorem: f = binomial <=> - !n k. f n 0 = 1 /\ f 0 (k + 1) = 0 /\ - f (n + 1) (k + 1) = f n k + f n (k + 1) *) -(* Proof: - If part: f = binomial ==> recurrence, true by binomial_alt - Only-if part: recurrence ==> f = binomial - By FUN_EQ_THM, this is to show: - !n k. f n k = binomial n k - By double induction, first induct on k. - Base: !n. f n 0 = binomial n 0, true by binomial_n_0 - Step: !n. f n k = binomial n k ==> - !n. f n (SUC k) = binomial n (SUC k) - By induction on n. - Base: f 0 (SUC k) = binomial 0 (SUC k) - This is true by binomial_0_n, ADD1 - Step: f n (SUC k) = binomial n (SUC k) ==> - f (SUC n) (SUC k) = binomial (SUC n) (SUC k) - - f (SUC n) (SUC k) - = f (n + 1) (k + 1) by ADD1 - = f n k + f n (k + 1) by given - = binomial n k + binomial n (k + 1) by induction hypothesis - = binomial (n + 1) (k + 1) by binomial_alt - = binomial (SUC n) (SUC k) by ADD1 -*) -Theorem binomial_iff: - !f. f = binomial <=> - !n k. f n 0 = 1 /\ f 0 (k + 1) = 0 /\ f (n + 1) (k + 1) = f n k + f n (k + 1) -Proof - rw[binomial_alt, EQ_IMP_THM] >> - simp[FUN_EQ_THM] >> - Induct_on `x'` >- - simp[binomial_n_0] >> - Induct_on `x` >- - fs[binomial_0_n, ADD1] >> - fs[binomial_alt, ADD1] -QED - -(* ------------------------------------------------------------------------- *) -(* Primes and Binomial Coefficients *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: n is prime ==> n divides C(n,k) for all 0 < k < n *) -(* Proof: - C(n,k) = n!/k!/(n-k)! - or n! = C(n,k) k! (n-k)! - n divides n!, so n divides the product C(n,k) k!(n-k)! - For a prime n, n cannot divide k!(n-k)!, all factors less than prime n. - By Euclid's lemma, a prime divides a product must divide a factor. - So p divides C(n,k). -*) -val prime_divides_binomials = store_thm( - "prime_divides_binomials", - ``!n. prime n ==> 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k))``, - rpt strip_tac >- - metis_tac[ONE_LT_PRIME] >> - `(n = n-k + k) /\ (n-k) < n` by decide_tac >> - `FACT n = (binomial n k) * (FACT (n-k) * FACT k)` by metis_tac[binomial_formula] >> - `~(n divides (FACT k)) /\ ~(n divides (FACT (n-k)))` by metis_tac[PRIME_BIG_NOT_DIVIDES_FACT] >> - `n divides (FACT n)` by metis_tac[DIVIDES_FACT, LESS_TRANS] >> - metis_tac[P_EUCLIDES]); - -(* Theorem: n is prime ==> n divides C(n,k) for all 0 < k < n *) -(* Proof: by prime_divides_binomials *) -val prime_divides_binomials_alt = store_thm( - "prime_divides_binomials_alt", - ``!n k. prime n /\ 0 < k /\ k < n ==> n divides (binomial n k)``, - rw[prime_divides_binomials]); - -(* Theorem: If prime p divides n, p does not divide (n-1)!/(n-p)! *) -(* Proof: - By contradiction. - (n-1)...(n-p+1)/p cannot be an integer - as p cannot divide any of the numerator. - Note: when p divides n, the nearest multiples for p are n+/-p. -*) -val prime_divisor_property = store_thm( - "prime_divisor_property", - ``!n p. 1 < n /\ p < n /\ prime p /\ p divides n ==> - ~(p divides ((FACT (n-1)) DIV (FACT (n-p))))``, - spose_not_then strip_assume_tac >> - `1 < p` by metis_tac[ONE_LT_PRIME] >> - `n-p < n-1` by decide_tac >> - `(FACT (n-1)) DIV (FACT (n-p)) = PROD_SET (IMAGE SUC ((count (n-1)) DIFF (count (n-p))))` - by metis_tac[FACT_REDUCTION, MULT_DIV, FACT_LESS] >> - `(count (n-1)) DIFF (count (n-p)) = {x | (n-p) <= x /\ x < (n-1)}` - by srw_tac[ARITH_ss][EXTENSION, EQ_IMP_THM] >> - `IMAGE SUC {x | (n-p) <= x /\ x < (n-1)} = {x | (n-p) < x /\ x < n}` by - (srw_tac[ARITH_ss][EXTENSION, EQ_IMP_THM] >> - qexists_tac `x-1` >> - decide_tac) >> - `FINITE (count (n - 1) DIFF count (n - p))` by rw[] >> - `?y. y IN {x| n - p < x /\ x < n} /\ p divides y` by metis_tac[PROD_SET_EUCLID, IMAGE_FINITE] >> - `!m n y. y IN {x | m < x /\ x < n} ==> m < y /\ y < n` by rw[] >> - `n-p < y /\ y < n` by metis_tac[] >> - `y < n + p` by decide_tac >> - `y = n` by metis_tac[MULTIPLE_INTERVAL] >> - decide_tac); - -(* Theorem: n divides C(n,k) for all 0 < k < n ==> n is prime *) -(* Proof: - By contradiction. Let p be a proper factor of n, 1 < p < n. - Then C(n,p) = n(n-1)...(n-p+1)/p(p-1)..1 - is divisible by n/p, but not n, since - C(n,p)/n = (n-1)...(n-p+1)/p(p-1)...1 - cannot be an integer as p cannot divide any of the numerator. - Note: when p divides n, the nearest multiples for p are n+/-p. -*) -val divides_binomials_imp_prime = store_thm( - "divides_binomials_imp_prime", - ``!n. 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k)) ==> prime n``, - (spose_not_then strip_assume_tac) >> - `?p. prime p /\ p < n /\ p divides n` by metis_tac[PRIME_FACTOR_PROPER] >> - `n divides (binomial n p)` by metis_tac[PRIME_POS] >> - `0 < p` by metis_tac[PRIME_POS] >> - `(n = n-p + p) /\ (n-p) < n` by decide_tac >> - `FACT n = (binomial n p) * (FACT (n-p) * FACT p)` by metis_tac[binomial_formula] >> - `(n = SUC (n-1)) /\ (p = SUC (p-1))` by decide_tac >> - `(FACT n = n * FACT (n-1)) /\ (FACT p = p * FACT (p-1))` by metis_tac[FACT] >> - `n * FACT (n-1) = (binomial n p) * (FACT (n-p) * (p * FACT (p-1)))` by metis_tac[] >> - `0 < n` by decide_tac >> - `?q. binomial n p = n * q` by metis_tac[divides_def, MULT_COMM] >> - `0 <> n` by decide_tac >> - `FACT (n-1) = q * (FACT (n-p) * (p * FACT (p-1)))` - by metis_tac[EQ_MULT_LCANCEL, MULT_ASSOC] >> - `_ = q * ((FACT (p-1) * p)* FACT (n-p))` by metis_tac[MULT_COMM] >> - `_ = q * FACT (p-1) * p * FACT (n-p)` by metis_tac[MULT_ASSOC] >> - `FACT (n-1) DIV FACT (n-p) = q * FACT (p-1) * p` by metis_tac[MULT_DIV, FACT_LESS] >> - metis_tac[divides_def, prime_divisor_property]); - -(* Theorem: n is prime iff n divides C(n,k) for all 0 < k < n *) -(* Proof: - By prime_divides_binomials and - divides_binomials_imp_prime. -*) -val prime_iff_divides_binomials = store_thm( - "prime_iff_divides_binomials", - ``!n. prime n <=> 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k))``, - metis_tac[prime_divides_binomials, divides_binomials_imp_prime]); - -(* Theorem: prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0) *) -(* Proof: by prime_iff_divides_binomials *) -val prime_iff_divides_binomials_alt = store_thm( - "prime_iff_divides_binomials_alt", - ``!n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)``, - rw[prime_iff_divides_binomials, DIVIDES_MOD_0]); - -(* ------------------------------------------------------------------------- *) -(* Binomial Theorem *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: Binomial Index Shifting, for - SUM (k=1..n) C(n,k)x^(n+1-k)y^k - = SUM (k=0..n-1) C(n,k+1)x^(n-k)y^(k+1) - *) -(* Proof: -SUM (k=1..n) C(n,k)x^(n+1-k)y^k -= SUM (MAP (\k. (binomial n k)* x**(n+1-k) * y**k) (GENLIST SUC n)) -= SUM (GENLIST (\k. (binomial n k)* x**(n+1-k) * y**k) o SUC n) - -SUM (k=0..n-1) C(n,k+1)x^(n-k)y^(k+1) -= SUM (MAP (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) (GENLIST I n)) -= SUM (GENLIST (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) o I n) -= SUM (GENLIST (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) n) - -i.e. - -(\k. (binomial n k)* x**(n-k+1) * y**k) o SUC -= (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) -*) -(* Theorem: Binomial index shift for GENLIST *) -val GENLIST_binomial_index_shift = store_thm( - "GENLIST_binomial_index_shift", - ``!n x y. GENLIST ((\k. binomial n k * x ** SUC(n - k) * y ** k) o SUC) n = - GENLIST (\k. binomial n (SUC k) * x ** (n-k) * y**(SUC k)) n``, - rw_tac std_ss[GENLIST_FUN_EQ] >> - `SUC (n - SUC k) = n - k` by decide_tac >> - rw_tac std_ss[]); - -(* This is closely related to above, with (SUC n) replacing (n), - but does not require k < n. *) -(* Proof: by function equality. *) -val binomial_index_shift = store_thm( - "binomial_index_shift", - ``!n x y. (\k. binomial (SUC n) k * x ** ((SUC n) - k) * y ** k) o SUC = - (\k. binomial (SUC n) (SUC k) * x ** (n-k) * y ** (SUC k))``, - rw_tac std_ss[FUN_EQ_THM]); - -(* Pattern for binomial expansion: - - (x+y)(x^3 + 3x^2y + 3xy^2 + y^3) - = x(x^3) + 3x(x^2y) + 3x(xy^2) + x(y^3) + - y(x^3) + 3y(x^2y) + 3y(xy^2) + y(y^3) - = x^4 + (3+1)x^3y + (3+3)(x^2y^2) + (1+3)(xy^3) + y^4 - = x^4 + 4x^3y + 6x^2y^2 + 4xy^3 + y^4 - -*) - -(* Theorem: multiply x into a binomial term *) -(* Proof: by function equality and EXP. *) -val binomial_term_merge_x = store_thm( - "binomial_term_merge_x", - ``!n x y. (\k. x * k) o (\k. binomial n k * x ** (n - k) * y ** k) = - (\k. binomial n k * x ** (SUC(n - k)) * y ** k)``, - rw_tac std_ss[FUN_EQ_THM] >> - `x * (binomial n k * x ** (n - k) * y ** k) = - binomial n k * (x * x ** (n - k)) * y ** k` by decide_tac >> - metis_tac[EXP]); - -(* Theorem: multiply y into a binomial term *) -(* Proof: by functional equality and EXP. *) -val binomial_term_merge_y = store_thm( - "binomial_term_merge_y", - ``!n x y. (\k. y * k) o (\k. binomial n k * x ** (n - k) * y ** k) = - (\k. binomial n k * x ** (n - k) * y ** (SUC k))``, - rw_tac std_ss[FUN_EQ_THM] >> - `y * (binomial n k * x ** (n - k) * y ** k) = - binomial n k * x ** (n - k) * (y * y ** k)` by decide_tac >> - metis_tac[EXP]); - -(* Theorem: [Binomial Theorem] (x + y)^n = SUM (k=0..n) C(n,k)x^(n-k)y^k *) -(* Proof: - By induction on n. - Base case: to prove (x + y)^0 = SUM (k=0..0) C(0,k)x^(0-k)y^k - (x + y)^0 = 1 by EXP - SUM (k=0..0) C(0,k)x^(n-k)y^k = C(0,0)x^(0-0)y^0 = C(0,0) = 1 by EXP, binomial_def - Step case: assume (x + y)^n = SUM (k=0..n) C(n,k)x^(n-k)y^k - to prove: (x + y)^SUC n = SUM (k=0..(SUC n)) C(SUC n,k)x^((SUC n)-k)y^k - (x + y)^SUC n - = (x + y)(x + y)^n by EXP - = (x + y) SUM (k=0..n) C(n,k)x^(n-k)y^k by induction hypothesis - = x (SUM (k=0..n) C(n,k)x^(n-k)y^k) + - y (SUM (k=0..n) C(n,k)x^(n-k)y^k) by RIGHT_ADD_DISTRIB - = SUM (k=0..n) C(n,k)x^(n+1-k)y^k + - SUM (k=0..n) C(n,k)x^(n-k)y^(k+1) by moving factor into SUM - = C(n,0)x^(n+1) + SUM (k=1..n) C(n,k)x^(n+1-k)y^k + - SUM (k=0..n-1) C(n,k)x^(n-k)y^(k+1) + C(n,n)y^(n+1) - by breaking sum - - = C(n,0)x^(n+1) + SUM (k=0..n-1) C(n,k+1)x^(n-k)y^(k+1) + - SUM (k=0..n-1) C(n,k)x^(n-k)y^(k+1) + C(n,n)y^(n+1) - by index shifting - = C(n,0)x^(n+1) + - SUM (k=0..n-1) [C(n,k+1) + C(n,k)] x^(n-k)y^(k+1) + - C(n,n)y^(n+1) by merging sums - = C(n,0)x^(n+1) + - SUM (k=0..n-1) C(n+1,k+1) x^(n-k)y^(k+1) + - C(n,n)y^(n+1) by binomial recurrence - = C(n,0)x^(n+1) + - SUM (k=1..n) C(n+1,k) x^(n+1-k)y^k + - C(n,n)y^(n+1) by index shifting again - = C(n+1,0)x^(n+1) + - SUM (k=1..n) C(n+1,k) x^(n+1-k)y^k + - C(n+1,n+1)y^(n+1) by binomial identities - = SUM (k=0..(SUC n))C(SUC n,k) x^((SUC n)-k)y^k - by synthesis of sum -*) -val binomial_thm = store_thm( - "binomial_thm", - ``!n x y. (x + y) ** n = SUM (GENLIST (\k. (binomial n k) * x ** (n-k) * y ** k) (SUC n))``, - Induct_on `n` >- - rw[EXP, binomial_n_n] >> - rw_tac std_ss[EXP] >> - `(x + y) * SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n)) = - x * SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n)) + - y * SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n))` - by metis_tac[RIGHT_ADD_DISTRIB] >> - `_ = SUM (GENLIST ((\k. x * k) o (\k. binomial n k * x ** (n - k) * y ** k)) (SUC n)) + - SUM (GENLIST ((\k. y * k) o (\k. binomial n k * x ** (n - k) * y ** k)) (SUC n))` - by metis_tac[SUM_MULT, MAP_GENLIST] >> - `_ = SUM (GENLIST (\k. binomial n k * x ** SUC(n - k) * y ** k) (SUC n)) + - SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) (SUC n))` - by rw[binomial_term_merge_x, binomial_term_merge_y] >> - `_ = (\k. binomial n k * x ** SUC (n - k) * y ** k) 0 + - SUM (GENLIST ((\k. binomial n k * x ** SUC (n - k) * y ** k) o SUC) n) + - SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) (SUC n))` - by rw[SUM_DECOMPOSE_FIRST] >> - `_ = (\k. binomial n k * x ** SUC (n - k) * y ** k) 0 + - SUM (GENLIST ((\k. binomial n k * x ** SUC (n - k) * y ** k) o SUC) n) + - (SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n) + - (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n )` - by rw[SUM_DECOMPOSE_LAST] >> - `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + - SUM (GENLIST (\k. binomial n (SUC k) * x ** (n - k) * y ** (SUC k)) n) + - (SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n) + - (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n )` - by metis_tac[GENLIST_binomial_index_shift] >> - `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + - (SUM (GENLIST (\k. binomial n (SUC k) * x ** (n - k) * y ** (SUC k)) n) + - SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n)) + - (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` - by decide_tac >> - `_ = (\k. binomial n k * x ** SUC (n - k) * y ** k) 0 + - SUM (GENLIST (\k. (binomial n (SUC k) * x ** (n - k) * y ** (SUC k) + - binomial n k * x ** (n - k) * y ** (SUC k))) n) + - (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` - by metis_tac[SUM_ADD_GENLIST] >> - `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + - SUM (GENLIST (\k. (binomial n (SUC k) + binomial n k) * x ** (n - k) * y ** (SUC k)) n) + - (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` - by rw[RIGHT_ADD_DISTRIB, MULT_ASSOC] >> - `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + - SUM (GENLIST (\k. binomial (SUC n) (SUC k) * x ** (n - k) * y ** (SUC k)) n) + - (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` - by rw[binomial_recurrence, ADD_COMM] >> - `_ = binomial (SUC n) 0 * x ** (SUC n) * y ** 0 + - SUM (GENLIST (\k. binomial (SUC n) (SUC k) * x ** (n - k) * y ** (SUC k)) n) + - binomial (SUC n) (SUC n) * x ** 0 * y ** (SUC n)` - by rw[binomial_n_0, binomial_n_n] >> - `_ = binomial (SUC n) 0 * x ** (SUC n) * y ** 0 + - SUM (GENLIST ((\k. binomial (SUC n) k * x ** ((SUC n) - k) * y ** k) o SUC) n) + - binomial (SUC n) (SUC n) * x ** 0 * y ** (SUC n)` - by rw[binomial_index_shift] >> - `_ = SUM (GENLIST (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) (SUC n)) + - (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) (SUC n)` - by rw[SUM_DECOMPOSE_FIRST] >> - `_ = SUM (GENLIST (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) (SUC (SUC n)))` - by rw[SUM_DECOMPOSE_LAST] >> - decide_tac); - -(* This is a milestone theorem. *) - -(* Derive an alternative form. *) -val binomial_thm_alt = save_thm("binomial_thm_alt", - binomial_thm |> SIMP_RULE bool_ss [ADD1]); -(* val binomial_thm_alt = - |- !n x y. (x + y) ** n = - SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (n + 1)): thm *) - -(* Theorem: SUM (GENLIST (binomial n) (SUC n)) = 2 ** n *) -(* Proof: by binomial_sum_alt and function equality. *) -(* Proof: - Put x = 1, y = 1 in binomial_thm, - (1 + 1) ** n = SUM (GENLIST (\k. binomial n k * 1 ** (n - k) * 1 ** k) (SUC n)) - (1 + 1) ** n = SUM (GENLIST (\k. binomial n k) (SUC n)) by EXP_1 - or 2 ** n = SUM (GENLIST (binomial n) (SUC n)) by FUN_EQ_THM -*) -Theorem binomial_sum: - !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n -Proof - rpt strip_tac >> - `!n. (\k. binomial n k * 1 ** (n - k) * 1 ** k) = binomial n` by rw[FUN_EQ_THM] >> - `SUM (GENLIST (binomial n) (SUC n)) = - SUM (GENLIST (\k. binomial n k * 1 ** (n - k) * 1 ** k) (SUC n))` by fs[] >> - `_ = (1 + 1) ** n` by rw[GSYM binomial_thm] >> - simp[] -QED - -(* Derive an alternative form. *) -val binomial_sum_alt = save_thm("binomial_sum_alt", - binomial_sum |> SIMP_RULE bool_ss [ADD1]); -(* val binomial_sum_alt = |- !n. SUM (GENLIST (binomial n) (n + 1)) = 2 ** n: thm *) - -(* ------------------------------------------------------------------------- *) -(* Binomial Horizontal List *) -(* ------------------------------------------------------------------------- *) - -(* Define Horizontal List in Pascal Triangle *) -(* -val binomial_horizontal_def = Define ` - binomial_horizontal n = GENLIST (binomial n) (SUC n) -`; -*) - -(* Use overloading for binomial_horizontal n. *) -val _ = overload_on("binomial_horizontal", ``\n. GENLIST (binomial n) (n + 1)``); - -(* Theorem: binomial_horizontal 0 = [1] *) -(* Proof: - binomial_horizontal 0 - = GENLIST (binomial 0) (0 + 1) by notation - = SNOC (binomial 0 0) [] by GENLIST, ONE - = [binomial 0 0] by SNOC - = [1] by binomial_n_0 -*) -val binomial_horizontal_0 = store_thm( - "binomial_horizontal_0", - ``binomial_horizontal 0 = [1]``, - rw[binomial_n_0]); - -(* Theorem: LENGTH (binomial_horizontal n) = n + 1 *) -(* Proof: - LENGTH (binomial_horizontal n) - = LENGTH (GENLIST (binomial n) (n + 1)) by notation - = n + 1 by LENGTH_GENLIST -*) -val binomial_horizontal_len = store_thm( - "binomial_horizontal_len", - ``!n. LENGTH (binomial_horizontal n) = n + 1``, - rw[]); - -(* Theorem: k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n) *) -(* Proof: by MEM_GENLIST *) -val binomial_horizontal_mem = store_thm( - "binomial_horizontal_mem", - ``!n k. k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n)``, - metis_tac[MEM_GENLIST]); - -(* Theorem: MEM (binomial n k) (binomial_horizontal n) <=> k <= n *) -(* Proof: - If part: MEM (binomial n k) (binomial_horizontal n) ==> k <= n - By contradiction, suppose n < k. - Then binomial n k = 0 by binomial_less_0, ~(k <= n) - But ?m. m < n + 1 ==> 0 = binomial n m by MEM_GENLIST - or m <= n ==> binomial n m = 0 by m < n + 1 - Yet binomial n m <> 0 by binomial_eq_0 - This is a contradiction. - Only-if part: k <= n ==> MEM (binomial n k) (binomial_horizontal n) - By MEM_GENLIST, this is to show: - ?m. m < n + 1 /\ (binomial n k = binomial n m) - Note k <= n ==> k < n + 1, - Take m = k, the result follows. -*) -val binomial_horizontal_mem_iff = store_thm( - "binomial_horizontal_mem_iff", - ``!n k. MEM (binomial n k) (binomial_horizontal n) <=> k <= n``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `binomial n k = 0` by rw[binomial_less_0] >> - fs[MEM_GENLIST] >> - `m <= n` by decide_tac >> - fs[binomial_eq_0], - rw[MEM_GENLIST] >> - `k < n + 1` by decide_tac >> - metis_tac[] - ]); - -(* Theorem: MEM x (binomial_horizontal n) <=> ?k. k <= n /\ (x = binomial n k) *) -(* Proof: - By MEM_GENLIST, this is to show: - (?m. m < n + 1 /\ (x = binomial n m)) <=> ?k. k <= n /\ (x = binomial n k) - Since m < n + 1 <=> m <= n by LE_LT1 - This is trivially true. -*) -val binomial_horizontal_member = store_thm( - "binomial_horizontal_member", - ``!n x. MEM x (binomial_horizontal n) <=> ?k. k <= n /\ (x = binomial n k)``, - metis_tac[MEM_GENLIST, LE_LT1]); - -(* Theorem: k <= n ==> (EL k (binomial_horizontal n) = binomial n k) *) -(* Proof: by EL_GENLIST *) -val binomial_horizontal_element = store_thm( - "binomial_horizontal_element", - ``!n k. k <= n ==> (EL k (binomial_horizontal n) = binomial n k)``, - rw[EL_GENLIST]); - -(* Theorem: EVERY (\x. 0 < x) (binomial_horizontal n) *) -(* Proof: - EVERY (\x. 0 < x) (binomial_horizontal n) - <=> EVERY (\x. 0 < x) (GENLIST (binomial n) (n + 1)) by notation - <=> !k. k < n + 1 ==> 0 < binomial n k by EVERY_GENLIST - <=> !k. k <= n ==> 0 < binomial n k by arithmetic - <=> T by binomial_pos -*) -val binomial_horizontal_pos = store_thm( - "binomial_horizontal_pos", - ``!n. EVERY (\x. 0 < x) (binomial_horizontal n)``, - rpt strip_tac >> - `!k n. k < n + 1 <=> k <= n` by decide_tac >> - rw_tac std_ss[EVERY_GENLIST, LESS_EQ_IFF_LESS_SUC, binomial_pos]); - -(* Theorem: MEM x (binomial_horizontal n) ==> 0 < x *) -(* Proof: by binomial_horizontal_pos, EVERY_MEM *) -val binomial_horizontal_pos_alt = store_thm( - "binomial_horizontal_pos_alt", - ``!n x. MEM x (binomial_horizontal n) ==> 0 < x``, - metis_tac[binomial_horizontal_pos, EVERY_MEM]); - -(* Theorem: SUM (binomial_horizontal n) = 2 ** n *) -(* Proof: - SUM (binomial_horizontal n) - = SUM (GENLIST (binomial n) (n + 1)) by notation - = 2 ** n by binomial_sum, ADD1 -*) -val binomial_horizontal_sum = store_thm( - "binomial_horizontal_sum", - ``!n. SUM (binomial_horizontal n) = 2 ** n``, - rw_tac std_ss[binomial_sum, GSYM ADD1]); - -(* Theorem: MAX_LIST (binomial_horizontal n) = binomial n (HALF n) *) -(* Proof: - Let l = binomial_horizontal n, m = binomial n (HALF n). - Then l <> [] by binomial_horizontal_len, LENGTH_NIL - and HALF n <= n by DIV_LESS_EQ, 0 < 2 - or HALF n < n + 1 by arithmetic - Also MEM m l by binomial_horizontal_mem - and !x. MEM x l ==> x <= m by binomial_max, MEM_GENLIST - Thus m = MAX_LIST l by MAX_LIST_TEST -*) -val binomial_horizontal_max = store_thm( - "binomial_horizontal_max", - ``!n. MAX_LIST (binomial_horizontal n) = binomial n (HALF n)``, - rpt strip_tac >> - qabbrev_tac `l = binomial_horizontal n` >> - qabbrev_tac `m = binomial n (HALF n)` >> - `l <> []` by metis_tac[binomial_horizontal_len, LENGTH_NIL, DECIDE``n + 1 <> 0``] >> - `HALF n <= n` by rw[DIV_LESS_EQ] >> - `HALF n < n + 1` by decide_tac >> - `MEM m l` by rw[binomial_horizontal_mem, Abbr`l`, Abbr`m`] >> - metis_tac[binomial_max, MEM_GENLIST, MAX_LIST_TEST]); - -(* Theorem: MAX_SET (IMAGE (binomial n) (count (n + 1))) = binomial n (HALF n) *) -(* Proof: - Let f = binomial n, s = IMAGE f (count (n + 1)). - Note FINITE (count (n + 1)) by FINITE_COUNT - so FINITE s by IMAGE_FINITE - Also count (n + 1) <> {} by COUNT_EQ_EMPTY, n + 1 <> 0 - so s <> {} by IMAGE_EQ_EMPTY - Now !k. k IN (count (n + 1)) ==> f k <= f (HALF n) by binomial_max - ==> !x. x IN s ==> x <= f (HALF n) by IN_IMAGE - Also HALF n <= n by DIV_LESS_EQ, 0 < 2 - so HALF n IN (count (n + 1)) by IN_COUNT - ==> f (HALF n) IN s by IN_IMAGE - Thus MAX_SET s = f (HALF n) by MAX_SET_TEST -*) -val binomial_row_max = store_thm( - "binomial_row_max", - ``!n. MAX_SET (IMAGE (binomial n) (count (n + 1))) = binomial n (HALF n)``, - rpt strip_tac >> - qabbrev_tac `f = binomial n` >> - qabbrev_tac `s = IMAGE f (count (n + 1))` >> - `FINITE s` by rw[Abbr`s`] >> - `s <> {}` by rw[COUNT_EQ_EMPTY, Abbr`s`] >> - `!k. k IN (count (n + 1)) ==> f k <= f (HALF n)` by rw[binomial_max, Abbr`f`] >> - `!x. x IN s ==> x <= f (HALF n)` by metis_tac[IN_IMAGE] >> - `HALF n <= n` by rw[DIV_LESS_EQ] >> - `HALF n IN (count (n + 1))` by rw[] >> - `f (HALF n) IN s` by metis_tac[IN_IMAGE] >> - rw[MAX_SET_TEST]); - -(* Theorem: k <= m /\ m <= n ==> - ((binomial m k) * (binomial n m) = (binomial n k) * (binomial (n - k) (m - k))) *) -(* Proof: - Using binomial_formula2, - - (binomial m k) * (binomial n m) - n! m! - = ----------- * ------------------ binomial formula - m! (n - m)! k! (m - k)! - n! m! - = ----------- * ------------------ cancel m! - k! m! (m - k)! (n - m)! - n! (n - k)! - = ----------- * ------------------ replace by (n - k)! - k! (n - k)! (m - k)! (n - m)! - - = (binomial n k) * (binomial (n - k) (m - k)) binomial formula -*) -val binomial_product_identity = store_thm( - "binomial_product_identity", - ``!m n k. k <= m /\ m <= n ==> - ((binomial m k) * (binomial n m) = (binomial n k) * (binomial (n - k) (m - k)))``, - rpt strip_tac >> - `m - k <= n - k` by decide_tac >> - `(n - k) - (m - k) = n - m` by decide_tac >> - `FACT m = binomial m k * (FACT (m - k) * FACT k)` by rw[binomial_formula2] >> - `FACT n = binomial n m * (FACT (n - m) * FACT m)` by rw[binomial_formula2] >> - `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> - `FACT (n - k) = binomial (n - k) (m - k) * (FACT (n - m) * FACT (m - k))` by metis_tac[binomial_formula2] >> - `FACT n = FACT (n - m) * (FACT k * (FACT (m - k) * ((binomial m k) * (binomial n m))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> - `FACT n = FACT (n - m) * (FACT k * (FACT (m - k) * ((binomial n k) * (binomial (n - k) (m - k)))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> - metis_tac[MULT_LEFT_CANCEL, FACT_LESS, NOT_ZERO]); - -(* Theorem: binomial n (HALF n) <= 4 ** (HALF n) *) -(* Proof: - Let m = HALF n, l = binomial_horizontal n - Note LENGTH l = n + 1 by binomial_horizontal_len - If EVEN n, - Then n = 2 * m by EVEN_HALF - and m <= n by m <= 2 * m - Note EL m l <= SUM l by SUM_LE_EL, m < n + 1 - Now EL m l = binomial n m by binomial_horizontal_element, m <= n - and SUM l - = 2 ** n by binomial_horizontal_sum - = 4 ** m by EXP_EXP_MULT - Hence binomial n m <= 4 ** m. - If ~EVEN n, - Then ODD n by EVEN_ODD - and n = 2 * m + 1 by ODD_HALF - so m + 1 <= n by m + 1 <= 2 * m + 1 - with m <= n by m + 1 <= n - Note EL m l = binomial n m by binomial_horizontal_element, m <= n - and EL (m + 1) l = binomial n (m + 1) by binomial_horizontal_element, m + 1 <= n - Note binomial n (m + 1) = binomial n m by binomial_sym - Thus 2 * binomial n m - = binomial n m + binomial n (m + 1) by above - = EL m l + EL (m + 1) l - <= SUM l by SUM_LE_SUM_EL, m < m + 1, m + 1 < n + 1 - and SUM l - = 2 ** n by binomial_horizontal_sum - = 2 * 2 ** (2 * m) by EXP, ADD1 - = 2 * 4 ** m by EXP_EXP_MULT - Hence binomial n m <= 4 ** m. -*) -val binomial_middle_upper_bound = store_thm( - "binomial_middle_upper_bound", - ``!n. binomial n (HALF n) <= 4 ** (HALF n)``, - rpt strip_tac >> - qabbrev_tac `m = HALF n` >> - qabbrev_tac `l = binomial_horizontal n` >> - `LENGTH l = n + 1` by rw[binomial_horizontal_len, Abbr`l`] >> - Cases_on `EVEN n` >| [ - `n = 2 * m` by rw[EVEN_HALF, Abbr`m`] >> - `m < n + 1` by decide_tac >> - `EL m l <= SUM l` by rw[SUM_LE_EL] >> - `EL m l = binomial n m` by rw[binomial_horizontal_element, Abbr`l`] >> - `SUM l = 2 ** n` by rw[binomial_horizontal_sum, Abbr`l`] >> - `_ = 4 ** m` by rw[EXP_EXP_MULT] >> - decide_tac, - `ODD n` by metis_tac[EVEN_ODD] >> - `n = 2 * m + 1` by rw[ODD_HALF, Abbr`m`] >> - `EL m l = binomial n m` by rw[binomial_horizontal_element, Abbr`l`] >> - `EL (m + 1) l = binomial n (m + 1)` by rw[binomial_horizontal_element, Abbr`l`] >> - `binomial n (m + 1) = binomial n m` by rw[Once binomial_sym] >> - `EL m l + EL (m + 1) l <= SUM l` by rw[SUM_LE_SUM_EL] >> - `SUM l = 2 ** n` by rw[binomial_horizontal_sum, Abbr`l`] >> - `_ = 2 * 2 ** (2 * m)` by metis_tac[EXP, ADD1] >> - `_ = 2 * 4 ** m` by rw[EXP_EXP_MULT] >> - decide_tac - ]); - -(* ------------------------------------------------------------------------- *) -(* Stirling's Approximation *) -(* ------------------------------------------------------------------------- *) - -(* Stirling's formula: n! ~ sqrt(2 pi n) (n/e)^n. *) -val _ = overload_on("Stirling", - ``(!n. FACT n = (SQRT (2 * pi * n)) * (n DIV e) ** n) /\ - (!n. SQRT n = n ** h) /\ (2 * h = 1) /\ (0 < pi) /\ (0 < e) /\ - (!a b x y. (a * b) DIV (x * y) = (a DIV x) * (b DIV y)) /\ - (!a b c. (a DIV c) DIV (b DIV c) = a DIV b)``); - -(* Theorem: Stirling ==> - !n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = (2 ** (n + 1)) DIV (SQRT (2 * pi * n))) *) -(* Proof: - Note HALF n <= n by DIV_LESS_EQ, 0 < 2 - Let k = HALF n, then n = 2 * k by EVEN_HALF - Note 0 < k by 0 < n = 2 * k - so (k * 2) DIV k = 2 by MULT_TO_DIV, 0 < k - or n DIV k = 2 by MULT_COMM - Also 0 < pi * n by MULT_EQ_0, 0 < pi, 0 < n - so 0 < 2 * pi * n by arithmetic - - Some theorems on the fly: - Claim: !a b j. (a ** j) DIV (b ** j) = (a DIV b) ** j [1] - Proof: By induction on j. - Base: (a ** 0) DIV (b ** 0) = (a DIV b) ** 0 - (a ** 0) DIV (b ** 0) - = 1 DIV 1 = 1 by EXP, DIVMOD_ID, 0 < 1 - = (a DIV b) ** 0 by EXP - Step: (a ** j) DIV (b ** j) = (a DIV b) ** j ==> - (a ** (SUC j)) DIV (b ** (SUC j)) = (a DIV b) ** (SUC j) - (a ** (SUC j)) DIV (b ** (SUC j)) - = (a * a ** j) DIV (b * b ** j) by EXP - = (a DIV b) * ((a ** j) DIV (b ** j)) by assumption - = (a DIV b) * (a DIV b) ** j by induction hypothesis - = (a DIV b) ** (SUC j) by EXP - - Claim: !a b c. (a DIV b) * c = (a * c) DIV b [2] - Proof: (a DIV b) * c - = (a DIV b) * (c DIV 1) by DIV_1 - = (a * c) DIV (b * 1) by assumption - = (a * c) DIV b by MULT_RIGHT_1 - - Claim: !a b. a DIV b = 2 * (a DIV (2 * b)) [3] - Proof: a DIV b - = 1 * (a DIV b) by MULT_LEFT_1 - = (n DIV n) * (a DIV b) by DIVMOD_ID, 0 < n - = (n * a) DIV (n * b) by assumption - = (n * a) DIV (k * (2 * b)) by arithmetic, n = 2 * k - = (n DIV k) * (a DIV (2 * b)) by assumption - = 2 * (a DIV (2 * b)) by n DIV k = 2 - - Claim: !a b. 0 < b ==> (a * (b ** h DIV b) = a DIV (b ** h)) [4] - Proof: Let c = b ** h. - Then b = c * c by EXP_EXP_MULT - so 0 < c by MULT_EQ_0, 0 < b - a * (c DIV b) - = (c DIV b) * a by MULT_COMM - = (a * c) DIV b by [2] - = (a * c) DIV (c * c) by b = c * c - = (a DIV c) * (c DIV c) by assumption - = a DIV c by DIVMOD_ID, c DIV c = 1, 0 < c - - Note (FACT k) ** 2 - = (SQRT (2 * pi * k)) ** 2 * ((k DIV e) ** k) ** 2 by EXP_BASE_MULT - = (SQRT (2 * pi * k)) ** 2 * (k DIV e) ** n by EXP_EXP_MULT, n = 2 * k - = (SQRT (pi * n)) ** 2 * (k DIV e) ** n by MULT_ASSOC, 2 * k = n - = ((pi * n) ** h) ** 2 * (k DIV e) ** n by assumption - = (pi * n) * (k DIV e) ** n by EXP_EXP_MULT, h * 2 = 1 - - binomial n (HALF n) - = binomial n k by k = HALF n - = FACT n DIV (FACT k * FACT (n - k)) by binomial_formula3, k <= n - = FACT n DIV (FACT k * FACT k) by arithmetic, n - k = 2 * k - k = k - = FACT n DIV ((FACT k) ** 2) by EXP_2 - = FACT n DIV ((pi * n) * (k DIV e) ** n) by above - = ((2 * pi * n) ** h * (n DIV e) ** n) DIV ((pi * n) * (k DIV e) ** n) by assumption - = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) ** n DIV ((k DIV e) ** n)) by (a * b) DIV (x * y) = (a DIV x) * (b DIV y) - = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) DIV (k DIV e)) ** n by (a ** n) DIV (b ** n) = (a DIV b) ** n) - = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * ((n DIV e) DIV (k DIV e)) ** n by MULT_ASSOC, a DIV b = 2 * a DIV (2 * b) - = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * (n DIV k) ** n by assumption, apply DIV_DIV_DIV_MULT - = 2 DIV (2 * pi * n) ** h * (n DIV k) ** n by 2 * x ** h DIV x = 2 DIV (x ** h) - = 2 DIV (2 * pi * n) ** h * 2 ** n by n DIV k = 2 - = 2 * 2 ** n DIV (2 * pi * n) ** h by (a DIV b) * c = a * c DIV b - = 2 ** (SUC n) DIV (2 * pi * n) ** h by EXP - = 2 ** (n + 1)) DIV (SQRT (2 * pi * n)) by ADD1, assumption -*) -val binomial_middle_by_stirling = store_thm( - "binomial_middle_by_stirling", - ``Stirling ==> !n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = (2 ** (n + 1)) DIV (SQRT (2 * pi * n)))``, - rpt strip_tac >> - `HALF n <= n /\ (n = 2 * HALF n)` by rw[DIV_LESS_EQ, EVEN_HALF] >> - qabbrev_tac `k = HALF n` >> - `0 < k` by decide_tac >> - `n DIV k = 2` by metis_tac[MULT_TO_DIV, MULT_COMM] >> - `0 < pi * n` by metis_tac[MULT_EQ_0, NOT_ZERO] >> - `0 < 2 * pi * n` by decide_tac >> - `(FACT k) ** 2 = (SQRT (2 * pi * k)) ** 2 * ((k DIV e) ** k) ** 2` by rw[EXP_BASE_MULT] >> - `_ = (SQRT (2 * pi * k)) ** 2 * (k DIV e) ** n` by rw[GSYM EXP_EXP_MULT] >> - `_ = (pi * n) * (k DIV e) ** n` by rw[GSYM EXP_EXP_MULT] >> - (`!a b j. (a ** j) DIV (b ** j) = (a DIV b) ** j` by (Induct_on `j` >> rw[EXP])) >> - `!a b c. (a DIV b) * c = (a * c) DIV b` by metis_tac[DIV_1, MULT_RIGHT_1] >> - `!a b. a DIV b = 2 * (a DIV (2 * b))` by metis_tac[DIVMOD_ID, MULT_LEFT_1] >> - `!a b. 0 < b ==> (a * (b ** h DIV b) = a DIV (b ** h))` by - (rpt strip_tac >> - qabbrev_tac `c = b ** h` >> - `b = c * c` by rw[GSYM EXP_EXP_MULT, Abbr`c`] >> - `0 < c` by metis_tac[MULT_EQ_0, NOT_ZERO] >> - `a * (c DIV b) = (a * c) DIV (c * c)` by metis_tac[MULT_COMM] >> - `_ = (a DIV c) * (c DIV c)` by metis_tac[] >> - metis_tac[DIVMOD_ID, MULT_RIGHT_1]) >> - `binomial n k = (FACT n) DIV (FACT k * FACT (n - k))` by metis_tac[binomial_formula3] >> - `_ = (FACT n) DIV (FACT k) ** 2` by metis_tac[EXP_2, DECIDE``2 * k - k = k``] >> - `_ = ((2 * pi * n) ** h * (n DIV e) ** n) DIV ((pi * n) * (k DIV e) ** n)` by prove_tac[] >> - `_ = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) ** n DIV ((k DIV e) ** n))` by metis_tac[] >> - `_ = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) DIV (k DIV e)) ** n` by metis_tac[] >> - `_ = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * ((n DIV e) DIV (k DIV e)) ** n` by metis_tac[MULT_ASSOC] >> - `_ = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * (n DIV k) ** n` by metis_tac[] >> - `_ = 2 DIV (2 * pi * n) ** h * (n DIV k) ** n` by metis_tac[] >> - `_ = 2 DIV (2 * pi * n) ** h * 2 ** n` by metis_tac[] >> - `_ = (2 * 2 ** n DIV (2 * pi * n) ** h)` by metis_tac[] >> - metis_tac[EXP, ADD1]); - -(* ------------------------------------------------------------------------- *) -(* Useful theorems for Binomial *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: !k. 0 < k /\ k < n ==> (binomial n k MOD n = 0) <=> - !h. 0 <= h /\ h < PRE n ==> (binomial n (SUC h) MOD n = 0) *) -(* Proof: by h = PRE k, or k = SUC h. - If part: put k = SUC h, - then 0 < SUC h ==> 0 <= h, - and SUC h < n ==> PRE (SUC h) = h < PRE n by prim_recTheory.PRE - Only-if part: put h = PRE k, - then 0 <= PRE k ==> 0 < k - and PRE k < PRE n ==> k < n by INV_PRE_LESS -*) -val binomial_range_shift = store_thm( - "binomial_range_shift", - ``!n . 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> - (!h. h < PRE n ==> ((binomial n (SUC h)) MOD n = 0)))``, - rw_tac std_ss[EQ_IMP_THM] >| [ - `0 < SUC h /\ SUC h < n` by decide_tac >> - rw_tac std_ss[], - `k <> 0` by decide_tac >> - `?h. k = SUC h` by metis_tac[num_CASES] >> - `h < PRE n` by decide_tac >> - rw_tac std_ss[] - ]); - -(* Theorem: binomial n k MOD n = 0 <=> (binomial n k * x ** (n-k) * y ** k) MOD n = 0 *) -(* Proof: - (binomial n k * x ** (n-k) * y ** k) MOD n = 0 - <=> (binomial n k * (x ** (n-k) * y ** k)) MOD n = 0 by MULT_ASSOC - <=> (((binomial n k) MOD n) * ((x ** (n - k) * y ** k) MOD n)) MOD n = 0 by MOD_TIMES2 - If part, apply 0 * z = 0 by MULT. - Only-if part, pick x = 1, y = 1, apply EXP_1. -*) -val binomial_mod_zero = store_thm( - "binomial_mod_zero", - ``!n. 0 < n ==> !k. (binomial n k MOD n = 0) <=> (!x y. (binomial n k * x ** (n-k) * y ** k) MOD n = 0)``, - rw_tac std_ss[EQ_IMP_THM] >- - metis_tac[MOD_TIMES2, ZERO_MOD, MULT] >> - metis_tac[EXP_1, MULT_RIGHT_1]); - - -(* Theorem: (!k. 0 < k /\ k < n ==> (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0))) <=> - (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0))) *) -(* Proof: by h = PRE k, or k = SUC h. *) -val binomial_range_shift_alt = store_thm( - "binomial_range_shift_alt", - ``!n . 0 < n ==> ((!k. 0 < k /\ k < n ==> (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0))) <=> - (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0))))``, - rw_tac std_ss[EQ_IMP_THM] >| [ - `0 < SUC h /\ SUC h < n` by decide_tac >> - rw_tac std_ss[], - `k <> 0` by decide_tac >> - `?h. k = SUC h` by metis_tac[num_CASES] >> - `h < PRE n` by decide_tac >> - rw_tac std_ss[] - ]); - -(* Theorem: !k. 0 < k /\ k < n ==> (binomial n k) MOD n = 0 <=> - !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0 *) -(* Proof: - !k. 0 < k /\ k < n ==> (binomial n k) MOD n = 0 - <=> !k. 0 < k /\ k < n ==> !x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0) by binomial_mod_zero - <=> !h. h < PRE n ==> !x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0) by binomial_range_shift_alt - <=> !x y. EVERY (\z. z = 0) (GENLIST (\k. (binomial n (SUC k) * x ** (n - (SUC k)) * y ** (SUC k)) MOD n) (PRE n)) by EVERY_GENLIST - <=> !x y. EVERY (\x. x = 0) (GENLIST ((\k. binomial n k * x ** (n - k) * y ** k) o SUC) (PRE n) by FUN_EQ_THM - <=> !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0 by SUM_EQ_0 -*) -val binomial_mod_zero_alt = store_thm( - "binomial_mod_zero_alt", - ``!n. 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> - !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0)``, - rpt strip_tac >> - `!x y. (\k. (binomial n (SUC k) * x ** (n - SUC k) * y ** (SUC k)) MOD n) = (\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC` by rw_tac std_ss[FUN_EQ_THM] >> - `(!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> - (!k. 0 < k /\ k < n ==> (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0)))` by rw_tac std_ss[binomial_mod_zero] >> - `_ = (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0)))` by rw_tac std_ss[binomial_range_shift_alt] >> - `_ = !x y h. h < PRE n ==> (((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0))` by metis_tac[] >> - rw_tac std_ss[EVERY_GENLIST, SUM_EQ_0]); - - -(* ------------------------------------------------------------------------- *) -(* Binomial Theorem with prime exponent *) -(* ------------------------------------------------------------------------- *) - - -(* Theorem: [Binomial Expansion for prime exponent] (x + y)^p = x^p + y^p (mod p) *) -(* Proof: - (x+y)^p (mod p) - = SUM (k=0..p) C(p,k)x^(p-k)y^k (mod p) by binomial theorem - = (C(p,0)x^py^0 + SUM (k=1..(p-1)) C(p,k)x^(p-k)y^k + C(p,p)x^0y^p) (mod p) by breaking sum - = (x^p + SUM (k=1..(p-1)) C(p,k)x^(p-k)y^k + y^k) (mod p) by binomial_n_0, binomial_n_n - = ((x^p mod p) + (SUM (k=1..(p-1)) C(p,k)x^(p-k)y^k) (mod p) + (y^p mod p)) mod p by MOD_PLUS - = ((x^p mod p) + (SUM (k=1..(p-1)) (C(p,k)x^(p-k)y^k) (mod p)) + (y^p mod p)) mod p - = (x^p mod p + 0 + y^p mod p) mod p by prime_iff_divides_binomials - = (x^p + y^p) (mod p) by MOD_PLUS -*) -val binomial_thm_prime = store_thm( - "binomial_thm_prime", - ``!p. prime p ==> (!x y. (x + y) ** p MOD p = (x ** p + y ** p) MOD p)``, - rpt strip_tac >> - `0 < p` by rw_tac std_ss[PRIME_POS] >> - `!k. 0 < k /\ k < p ==> ((binomial p k) MOD p = 0)` by metis_tac[prime_iff_divides_binomials, DIVIDES_MOD_0] >> - `SUM (GENLIST ((\k. binomial p k * x ** (p - k) * y ** k) o SUC) (PRE p)) MOD p = 0` by metis_tac[SUM_GENLIST_MOD, binomial_mod_zero_alt, ZERO_MOD] >> - `(x + y) ** p MOD p = (x ** p + SUM (GENLIST ((\k. binomial p k * x ** (p - k) * y ** k) o SUC) (PRE p)) + y ** p) MOD p` by rw_tac std_ss[binomial_thm, SUM_DECOMPOSE_FIRST_LAST, binomial_n_0, binomial_n_n, EXP] >> - metis_tac[MOD_PLUS3, ADD_0, MOD_PLUS]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/files.txt b/examples/algebra/lib/files.txt deleted file mode 100644 index d3c9daf8ba..0000000000 --- a/examples/algebra/lib/files.txt +++ /dev/null @@ -1,89 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Hierarchy of Tools Library *) -(* *) -(* Author: Joseph Chan *) -(* Date: December, 2014 *) -(* ------------------------------------------------------------------------- *) - -0 helperNum -- extends HOL library on numbers. -* divides -* gcd - -1 helperSet -- extends HOL library on sets. -* pred_set -* 0 helperNum - -2 helperList -- extends HOL library on lists. -* pred_set -* list -* rich_list -* listRange -* 0 helperNum -* 1 helperSet - -3 helperFunction -- useful theorems on functions. -* 0 helperNum -* 1 helperSet -* 2 helperList - -3 sublist -- order-preserving sublist and properties. -* 0 listRange -* 2 helperList - -4 logPower -- properties of perfect power, power free, and upper logarithm. -* logroot -* 0 helperNum -* 1 helperSet -* 3 helperFunction - -4 binomial -- properties of binomial coefficients in Pascal's Triangle. -* 0 helperNum -* 1 helperSet -* 2 helperList -* 3 helperFunction - -4 Euler -- number-theoretic sets, and Euler's phi function. -* 0 helperNum -* 1 helperSet -* 3 helperFunction - -5 Gauss -- coprimes, properties of phi function, and Gauss' Little Theorem. -* 0 helperNum -* 1 helperSet -* 2 helperList -* 3 helperFunction -* 4 logPower -* 4 Euler - -5 primes -- properties of two-factors, and a primality test. -* 0 helperNum -* 3 helperFunction -* 4 logPower - -5 triangle -- properties of Leibniz's Denominator Triangle, relating to consecutive LCM. -* listRange -* relation -* 0 helperNum -* 1 helperSet -* 2 helperList -* 3 helperFunction -* 4 binomial -* 4 Euler - -6 primePower -- properties of prime powers and divisors, an investigation on consecutive LCM. -* listRange -* option -* 0 helperNum -* 1 helperSet -* 2 helperList -* 3 helperFunction -* 4 logPower -* 4 Euler -* 5 triangle - -6 Mobius -- work on Mobius Inversion. -* 0 helperNum -* 1 helperSet -* 2 helperList -* 4 Euler -* 5 Gauss diff --git a/examples/algebra/lib/helperFunctionScript.sml b/examples/algebra/lib/helperFunctionScript.sml deleted file mode 100644 index 87af347769..0000000000 --- a/examples/algebra/lib/helperFunctionScript.sml +++ /dev/null @@ -1,4570 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Helper Theorems - a collection of useful results -- for Functions. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "helperFunction"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperListTheory; - -(* val _ = load "helperSetTheory"; *) -open helperSetTheory; - -(* open dependent theories *) -open pred_setTheory prim_recTheory arithmeticTheory; -open listTheory rich_listTheory listRangeTheory; -open dividesTheory gcdTheory; - - -(* ------------------------------------------------------------------------- *) -(* Helper Function Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading/syntax: - (x == y) f = fequiv x y f - feq = flip (flip o fequiv) - RISING f = !x:num. x <= f x - FALLING f = !x:num. f x <= x - SQRT n = ROOT 2 n - LOG2 n = LOG 2 n - PAIRWISE_COPRIME s = !x y. x IN s /\ y IN s /\ x <> y ==> coprime x y - tops b n = b ** n - 1 - nines n = tops 10 n -*) -(* Definitions and Theorems (# are exported): - - Function Equivalence as Relation: - fequiv_def |- !x y f. fequiv x y f <=> (f x = f y) - fequiv_refl |- !f x. (x == x) f - fequiv_sym |- !f x y. (x == y) f ==> (y == x) f - fequiv_trans |- !f x y z. (x == y) f /\ (y == z) f ==> (x == z) f - fequiv_equiv_class |- !f. (\x y. (x == y) f) equiv_on univ(:'a) - - Function-based Equivalence: - feq_class_def |- !s f n. feq_class f s n = {x | x IN s /\ (f x = n)} - feq_class_element |- !f s n x. x IN feq_class f s n <=> x IN s /\ (f x = n) - feq_class_property |- !f s x. feq_class f s (f x) = {y | y IN s /\ feq f x y} - feq_class_fun |- !f s. feq_class f s o f = (\x. {y | y IN s /\ feq f x y}) - - - feq_equiv |- !s f. feq f equiv_on s - feq_partition |- !s f. partition (feq f) s = IMAGE (feq_class f s o f) s - feq_partition_element |- !s f t. t IN partition (feq f) s <=> - ?z. z IN s /\ !x. x IN t <=> x IN s /\ (f x = f z) - feq_partition_element_exists - |- !f s x. x IN s <=> ?e. e IN partition (feq f) s /\ x IN e - feq_partition_element_not_empty - |- !f s e. e IN partition (feq f) s ==> e <> {} - feq_class_eq_preimage |- !f s. feq_class f s = preimage f s - feq_partition_by_preimage |- !f s. partition (feq f) s = IMAGE (preimage f s o f) s - feq_sum_over_partition |- !s. FINITE s ==> !f g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s) - - finite_card_by_feq_partition |- !s. FINITE s ==> !f. CARD s = SIGMA CARD (partition (feq f) s) - finite_card_by_image_preimage |- !s. FINITE s ==> !f. CARD s = SIGMA CARD (IMAGE (preimage f s o f) s) - finite_card_surj_by_image_preimage - |- !f s t. FINITE s /\ SURJ f s t ==> - CARD s = SIGMA CARD (IMAGE (preimage f s) t) - preimage_image_bij |- !f s. BIJ (preimage f s) (IMAGE f s) (partition (feq f) s): - - Condition for surjection to be a bijection: - inj_iff_partition_element_sing - |- !f s. INJ f s (IMAGE f s) <=> - !e. e IN partition (feq f) s ==> SING e - inj_iff_partition_element_card_1 - |- !f s. FINITE s ==> - (INJ f s (IMAGE f s) <=> - !e. e IN partition (feq f) s ==> CARD e = 1) - FINITE_SURJ_IS_INJ |- !f s t. FINITE s /\ - CARD s = CARD t /\ SURJ f s t ==> INJ f s t - FINITE_SURJ_IS_BIJ |- !f s t. FINITE s /\ - CARD s = CARD t /\ SURJ f s t ==> BIJ f s t - - Function Iteration: - FUNPOW_2 |- !f x. FUNPOW f 2 x = f (f x) - FUNPOW_K |- !n x c. FUNPOW (K c) n x = if n = 0 then x else c - FUNPOW_MULTIPLE |- !f k e. 0 < k /\ (FUNPOW f k e = e) ==> !n. FUNPOW f (n * k) e = e - FUNPOW_MOD |- !f k e. 0 < k /\ (FUNPOW f k e = e) ==> !n. FUNPOW f n e = FUNPOW f (n MOD k) e - FUNPOW_COMM |- !f m n x. FUNPOW f m (FUNPOW f n x) = FUNPOW f n (FUNPOW f m x) - FUNPOW_LE_RISING |- !f m n. RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x - FUNPOW_LE_FALLING |- !f m n. FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x - FUNPOW_LE_MONO |- !f g. (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x - FUNPOW_GE_MONO |- !f g. (!x. f x <= g x) /\ MONO f ==> !n x. FUNPOW f n x <= FUNPOW g n x - FUNPOW_SQ |- !m n. FUNPOW (\n. SQ n) n m = m ** 2 ** n - FUNPOW_SQ_MOD |- !m n k. 0 < m /\ 0 < n ==> (FUNPOW (\n. SQ n MOD m) n k = k ** 2 ** n MOD m) - FUNPOW_ADD1 |- !m n. FUNPOW SUC n m = m + n - FUNPOW_SUB1 |- !m n. FUNPOW PRE n m = m - n - FUNPOW_MUL |- !b m n. FUNPOW ($* b) n m = m * b ** n - FUNPOW_DIV |- !b m n. 0 < b ==> FUNPOW (combin$C $DIV b) n m = m DIV b ** n - FUNPOW_MAX |- !m n k. 0 < n ==> (FUNPOW (\x. MAX x m) n k = MAX k m) - FUNPOW_MIN |- !m n k. 0 < n ==> (FUNPOW (\x. MIN x m) n k = MIN k m) - FUNPOW_PAIR |- !f g n x y. FUNPOW (\(x,y). (f x,g y)) n (x,y) = (FUNPOW f n x,FUNPOW g n y) - FUNPOW_TRIPLE |- !f g h n x y z. FUNPOW (\(x,y,z). (f x,g y,h z)) n (x,y,z) = - (FUNPOW f n x,FUNPOW g n y,FUNPOW h n z) - - More FUNPOW Theorems: - LINV_permutes |- !f s. f PERMUTES s ==> LINV f s PERMUTES s - FUNPOW_permutes |- !f s n. f PERMUTES s ==> FUNPOW f n PERMUTES s - FUNPOW_closure |- !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s - FUNPOW_LINV_permutes|- !f s n. f PERMUTES s ==> FUNPOW (LINV f s) n PERMUTES s - FUNPOW_LINV_closure |- !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n x IN s - FUNPOW_LINV_EQ |- !f s x n. f PERMUTES s /\ x IN s ==> - FUNPOW f n (FUNPOW (LINV f s) n x) = x - FUNPOW_EQ_LINV |- !f s x n. f PERMUTES s /\ x IN s ==> - FUNPOW (LINV f s) n (FUNPOW f n x) = x - FUNPOW_SUB_LINV1 |- !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW f (n - m) x = FUNPOW f n (FUNPOW (LINV f s) m x) - FUNPOW_SUB_LINV2 |- !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW f (n - m) x = FUNPOW (LINV f s) m (FUNPOW f n x) - FUNPOW_LINV_SUB1 |- !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW (LINV f s) (n - m) x = FUNPOW (LINV f s) n (FUNPOW f m x) - FUNPOW_LINV_SUB2 |- !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW (LINV f s) (n - m) x = FUNPOW f m (FUNPOW (LINV f s) n x) - FUNPOW_LINV_INV |- !f s x y n. f PERMUTES s /\ x IN s /\ y IN s ==> - (x = FUNPOW f n y <=> y = FUNPOW (LINV f s) n x) - - FUNPOW with incremental cons: - FUNPOW_cons_head |- !f n ls. HD (FUNPOW (\ls. f (HD ls)::ls) n ls) = FUNPOW f n (HD ls) - FUNPOW_cons_eq_map_0|- !f u n. FUNPOW (\ls. f (HD ls)::ls) n [u] = - MAP (\j. FUNPOW f j u) (n downto 0) - FUNPOW_cons_eq_map_1|- !f u n. 0 < n ==> - FUNPOW (\ls. f (HD ls)::ls) (n - 1) [f u] = - MAP (\j. FUNPOW f j u) (n downto 1) - - Factorial: - FACT_0 |- FACT 0 = 1 - FACT_1 |- FACT 1 = 1 - FACT_2 |- FACT 2 = 2 - FACT_EQ_1 |- !n. (FACT n = 1) <=> n <= 1 - FACT_GE_1 |- !n. 1 <= FACT n - FACT_EQ_SELF |- !n. (FACT n = n) <=> (n = 1) \/ (n = 2) - FACT_GE_SELF |- !n. 0 < n ==> n <= FACT n - FACT_DIV |- !n. 0 < n ==> (FACT (n - 1) = FACT n DIV n) - FACT_EQ_PROD |- !n. FACT n = PROD_SET (IMAGE SUC (count n)) - FACT_REDUCTION |- !n m. m < n ==> (FACT n = PROD_SET (IMAGE SUC ((count n) DIFF (count m))) * (FACT m)) - PRIME_BIG_NOT_DIVIDES_FACT |- !p k. prime p /\ k < p ==> ~(p divides (FACT k)) - FACT_iff |- !f. f = FACT <=> f 0 = 1 /\ !n. f (SUC n) = SUC n * f n - - Basic GCD, LCM Theorems: - GCD_COMM |- !a b. gcd a b = gcd b a - LCM_SYM |- !a b. lcm a b = lcm b a - GCD_0 |- !x. (gcd 0 x = x) /\ (gcd x 0 = x) - GCD_DIVIDES |- !m n. 0 < n /\ 0 < m ==> 0 < gcd n m /\ (n MOD gcd n m = 0) /\ (m MOD gcd n m = 0) - GCD_GCD |- !m n. gcd n (gcd n m) = gcd n m - GCD_LCM |- !m n. gcd m n * lcm m n = m * n - LCM_DIVISORS |- !m n. m divides lcm m n /\ n divides lcm m n - LCM_IS_LCM |- !m n p. m divides p /\ n divides p ==> lcm m n divides p - LCM_EQ_0 |- !m n. (lcm m n = 0) <=> (m = 0) \/ (n = 0) - LCM_REF |- !a. lcm a a = a - LCM_DIVIDES |- !n a b. a divides n /\ b divides n ==> lcm a b divides n - GCD_POS |- !m n. 0 < m \/ 0 < n ==> 0 < gcd m n - LCM_POS |- !m n. 0 < m /\ 0 < n ==> 0 < lcm m n - divides_iff_gcd_fix |- !m n. n divides m <=> (gcd n m = n) - divides_iff_lcm_fix |- !m n. n divides m <=> (lcm n m = m) - FACTOR_OUT_PRIME |- !n p. 0 < n /\ prime p /\ p divides n ==> - ?m. 0 < m /\ (p ** m) divides n /\ !k. coprime (p ** k) (n DIV p ** m) - - Consequences of Coprime: - MOD_NONZERO_WHEN_GCD_ONE |- !n. 1 < n ==> !x. coprime n x ==> 0 < x /\ 0 < x MOD n - PRODUCT_WITH_GCD_ONE |- !n x y. coprime n x /\ coprime n y ==> coprime n (x * y) - MOD_WITH_GCD_ONE |- !n x. 0 < n /\ coprime n x ==> coprime n (x MOD n) - GCD_ONE_PROPERTY |- !n x. 1 < n /\ coprime n x ==> ?k. ((k * x) MOD n = 1) /\ coprime n k - GCD_MOD_MULT_INV |- !n x. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> - ?y. 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1) - GEN_MULT_INV_DEF |- !n x. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> - 0 < GCD_MOD_MUL_INV n x /\ GCD_MOD_MUL_INV n x < n /\ - coprime n (GCD_MOD_MUL_INV n x) /\ - ((GCD_MOD_MUL_INV n x * x) MOD n = 1) - - More GCD and LCM Theorems: - GCD_PROPERTY |- !a b c. (c = gcd a b) <=> c divides a /\ c divides b /\ - !x. x divides a /\ x divides b ==> x divides c - GCD_ASSOC |- !a b c. gcd a (gcd b c) = gcd (gcd a b) c - GCD_ASSOC_COMM |- !a b c. gcd a (gcd b c) = gcd b (gcd a c) - LCM_PROPERTY |- !a b c. (c = lcm a b) <=> a divides c /\ b divides c /\ - !x. a divides x /\ b divides x ==> c divides x - LCM_ASSOC |- !a b c. lcm a (lcm b c) = lcm (lcm a b) - LCM_ASSOC_COMM |- !a b c. lcm a (lcm b c) = lcm b (lcm a c) - GCD_SUB_L |- !a b. b <= a ==> (gcd (a - b) b = gcd a b) - GCD_SUB_R |- !a b. a <= b ==> (gcd a (b - a) = gcd a b) - LCM_EXCHANGE |- !a b c. (a * b = c * (a - b)) ==> (lcm a b = lcm a c) - LCM_COPRIME |- !m n. coprime m n ==> (lcm m n = m * n) - LCM_COMMON_FACTOR |- !m n k. lcm (k * m) (k * n) = k * lcm m n - LCM_COMMON_COPRIME |- !a b. coprime a b ==> !c. lcm (a * c) (b * c) = a * b * c - GCD_MULTIPLE |- !m n. 0 < n /\ (m MOD n = 0) ==> (gcd m n = n) - GCD_MULTIPLE_ALT |- !m n. gcd (m * n) n = n - GCD_SUB_MULTIPLE |- !a b k. k * a <= b ==> (gcd a b = gcd a (b - k * a)) - GCD_SUB_MULTIPLE_COMM - |- !a b k. k * a <= b ==> (gcd b a = gcd a (b - k * a)) - GCD_MOD |- !m n. 0 < m ==> (gcd m n = gcd m (n MOD m)) - GCD_MOD_COMM |- !m n. 0 < m ==> (gcd n m = gcd (n MOD m) m) - GCD_EUCLID |- !a b c. gcd a (b * a + c) = gcd a c - GCD_REDUCE |- !a b c. gcd (b * a + c) a = gcd a c - GCD_REDUCE_BY_COPRIME - |- !m n k. coprime m k ==> gcd m (k * n) = gcd m n - gcd_le |- !m n. 0 < m /\ 0 < n ==> gcd m n <= m /\ gcd m n <= n - gcd_divides_iff |- !a b c. 0 < a ==> (gcd a b divides c <=> ?p q. p * a = q * b + c) - gcd_linear_thm |- !a b c. 0 < a ==> (gcd a b divides c <=> ?p q. p * a = q * b + c) - gcd_linear_mod_thm |- !n a b. 0 < n /\ 0 < a ==> ?p q. (p * a + q * b) MOD n = gcd a b MOD n - gcd_linear_mod_1 |- !a b. 0 < a ==> ?q. (q * b) MOD a = gcd a b MOD a - gcd_linear_mod_2 |- !a b. 0 < b ==> ?p. (p * a) MOD b = gcd a b MOD b - gcd_linear_mod_prod |- !a b. 0 < a /\ 0 < b ==> - ?p q. (p * a + q * b) MOD (a * b) = gcd a b MOD (a * b) - coprime_linear_mod_prod - |- !a b. 0 < a /\ 0 < b /\ coprime a b ==> - ?p q. (p * a + q * b) MOD (a * b) = 1 MOD (a * b) - gcd_multiple_linear_mod_thm - |- !n a b c. 0 < n /\ 0 < a /\ gcd a b divides c ==> - ?p q. (p * a + q * b) MOD n = c MOD n - gcd_multiple_linear_mod_prod - |- !a b c. 0 < a /\ 0 < b /\ gcd a b divides c ==> - ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) - coprime_multiple_linear_mod_prod - |- !a b c. 0 < a /\ 0 < b /\ coprime a b ==> - ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) - - Coprime Theorems: - coprime_SUC |- !n. coprime n (n + 1) - coprime_PRE |- !n. 0 < n ==> coprime (n - 1) n - coprime_0L |- !n. coprime 0 n <=> (n = 1) - coprime_0R |- !n. coprime n 0 <=> (n = 1) - coprime_0 |- !n. (coprime 0 n <=> n = 1) /\ (coprime n 0 <=> n = 1) - coprime_sym |- !x y. coprime x y <=> coprime y x - coprime_neq_1 |- !n k. coprime k n /\ n <> 1 ==> k <> 0 - coprime_gt_1 |- !n k. coprime k n /\ 1 < n ==> 0 < k - coprime_exp |- !c m. coprime c m ==> !n. coprime (c ** n) m - coprime_exp_comm |- !a b. coprime a b ==> !n. coprime a (b ** n) - coprime_iff_coprime_exp |- !n. 0 < n ==> !a b. coprime a b <=> coprime a (b ** n) - coprime_product_coprime |- !x y z. coprime x z /\ coprime y z ==> coprime (x * y) z - coprime_product_coprime_sym |- !x y z. coprime z x /\ coprime z y ==> coprime z (x * y) - coprime_product_coprime_iff |- !x y z. coprime x z ==> (coprime y z <=> coprime (x * y) z) - coprime_product_divides |- !n a b. a divides n /\ b divides n /\ coprime a b ==> a * b divides n - coprime_mod |- !m n. 0 < m /\ coprime m n ==> coprime m (n MOD m) - coprime_mod_iff |- !m n. 0 < m ==> (coprime m n <=> coprime m (n MOD m)) - coprime_not_divides |- !m n. 1 < n /\ coprime n m ==> ~(n divides m) - coprime_factor_not_divides |- !n k. 1 < n /\ coprime n k ==> - !p. 1 < p /\ p divides n ==> ~(p divides k) - coprime_factor_coprime |- !m n. m divides n ==> !k. coprime n k ==> coprime m k - coprime_common_factor |- !a b c. coprime a b /\ c divides a /\ c divides b ==> c = 1 - prime_not_divides_coprime |- !n p. prime p /\ ~(p divides n) ==> coprime p n - prime_not_coprime_divides |- !n p. prime p /\ ~(coprime p n) ==> p divides n - coprime_prime_factor_coprime |- !n p. 1 < n /\ prime p /\ p divides n ==> - !k. coprime n k ==> coprime p k - coprime_all_le_imp_lt |- !n. 1 < n ==> !m. (!j. 0 < j /\ j <= m ==> coprime n j) ==> m < n - coprime_condition |- !m n. (!j. 1 < j /\ j <= m ==> ~(j divides n)) <=> - !j. 1 < j /\ j <= m ==> coprime j n - coprime_by_le_not_divides |- !m n. 1 < m /\ (!j. 1 < j /\ j <= m ==> ~(j divides n)) ==> coprime m n - coprime_by_prime_factor |- !m n. coprime m n <=> !p. prime p ==> ~(p divides m /\ p divides n) - coprime_by_prime_factor_le|- !m n. 0 < m /\ 0 < n ==> - (coprime m n <=> - !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m /\ p divides n)) - coprime_linear_mult |- !a b p q. coprime a b /\ coprime p b /\ coprime q a ==> - coprime (p * a + q * b) (a * b) - coprime_linear_mult_iff |- !a b p q. coprime a b ==> - (coprime p b /\ coprime q a <=> coprime (p * a + q * b) (a * b)) - coprime_prime_power |- !p n. prime p /\ 0 < n ==> !q. coprime q (p ** n) <=> ~(p divides q) - prime_coprime_all_lt |- !n. prime n ==> !m. 0 < m /\ m < n ==> coprime n m - prime_coprime_all_less |- !m n. prime n /\ m < n ==> !j. 0 < j /\ j <= m ==> coprime n j - prime_iff_coprime_all_lt |- !n. prime n <=> 1 < n /\ !j. 0 < j /\ j < n ==> coprime n j - prime_iff_no_proper_factor|- !n. prime n <=> 1 < n /\ !j. 1 < j /\ j < n ==> ~(j divides n) - prime_always_bigger |- !n. ?p. prime p /\ n < p - divides_imp_coprime_with_successor |- !m n. n divides m ==> coprime n (SUC m) - divides_imp_coprime_with_predecessor |- !m n. 0 < m /\ n divides m ==> coprime n (PRE m) - gcd_coprime_cancel |- !m n p. coprime p n ==> (gcd (p * m) n = gcd m n) - primes_coprime |- !p q. prime p /\ prime q /\ p <> q ==> coprime p q - every_coprime_prod_set_coprime |- !s. FINITE s ==> - !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s) - - Pairwise Coprime Property: - pairwise_coprime_insert |- !s e. e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> - (!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s - pairwise_coprime_prod_set_subset_divides - |- !s. FINITE s /\ PAIRWISE_COPRIME s ==> - !t. t SUBSET s ==> PROD_SET t divides PROD_SET s - pairwise_coprime_partition_coprime |- !s. FINITE s /\ PAIRWISE_COPRIME s ==> - !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v) - pairwise_coprime_prod_set_partition |- !s. FINITE s /\ PAIRWISE_COPRIME s ==> - !u v. (s = u UNION v) /\ DISJOINT u v ==> - (PROD_SET s = PROD_SET u * PROD_SET v) /\ coprime (PROD_SET u) (PROD_SET v) - - GCD divisibility condition of Power Predecessors: - power_predecessor_division_eqn |- !t m n. 0 < t /\ m <= n ==> - tops t n = t ** (n - m) * tops t m + tops t (n - m) - power_predecessor_division_alt |- !t m n. 0 < t /\ m <= n ==> - tops t n - t ** (n - m) * tops t m = tops t (n - m) - power_predecessor_gcd_reduction |- !t n m. m <= n ==> - (gcd (tops t n) (tops t m) = gcd (tops t m) (tops t (n - m))) - power_predecessor_gcd_identity |- !t n m. gcd (tops t n) (tops t m) = tops t (gcd n m) - power_predecessor_divisibility |- !t n m. 1 < t ==> (tops t n divides tops t m <=> n divides m) - power_predecessor_divisor |- !t n. t - 1 divides tops t n - - nines_division_eqn |- !m n. m <= n ==> nines n = 10 ** (n - m) * nines m + nines (n - m): thm - nines_division_alt |- !m n. m <= n ==> nines n - 10 ** (n - m) * nines m = nines (n - m): thm - nines_gcd_reduction |- !n m. m <= n ==> gcd (nines n) (nines m) = gcd (nines m) (nines (n - m)): thm - nines_gcd_identity |- !n m. gcd (nines n) (nines m) = nines (gcd n m): thm - nines_divisibility |- !n m. nines n divides nines m <=> n divides m: thm - nines_divisor |- !n. 9 divides nines n: thm - - GCD involving Powers: - prime_divides_prime_power |- !m n k. prime m /\ prime n /\ m divides n ** k ==> (m = n) - prime_power_factor |- !n p. 0 < n /\ prime p ==> ?q m. (n = p ** m * q) /\ coprime p q - prime_power_divisor |- !p n a. prime p /\ a divides p ** n ==> ?j. j <= n /\ (a = p ** j) - prime_powers_eq |- !p q. prime p /\ prime q ==> !m n. 0 < m /\ (p ** m = q ** n) ==> (p = q) /\ (m = n) - prime_powers_coprime |- !p q. prime p /\ prime q /\ p <> q ==> !m n. coprime (p ** m) (q ** n) - prime_powers_divide |- !p q. prime p /\ prime q ==> - !m n. 0 < m ==> (p ** m divides q ** n <=> (p = q) /\ m <= n) - gcd_powers |- !b m n. gcd (b ** m) (b ** n) = b ** MIN m n - lcm_powers |- !b m n. lcm (b ** m) (b ** n) = b ** MAX m n - coprime_power_and_power_predecessor |- !b m n. 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m - 1) - coprime_power_and_power_successor |- !b m n. 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m + 1) - - Useful Theorems: - PRIME_EXP_FACTOR |- !p q n. prime p /\ q divides p ** n ==> (q = 1) \/ p divides q - FACT_MOD_PRIME |- !p n. prime p /\ n < p ==> FACT n MOD p <> 0: -*) - -(* ------------------------------------------------------------------------- *) -(* Function Equivalence as Relation *) -(* ------------------------------------------------------------------------- *) - -(* For function f on a domain D, x, y in D are "equal" if f x = f y. *) -Definition fequiv_def: - fequiv x y f <=> (f x = f y) -End -Overload "==" = ``fequiv`` -val _ = set_fixity "==" (Infix(NONASSOC, 450)); - -(* Theorem: [Reflexive] (x == x) f *) -(* Proof: by definition, - and f x = f x. -*) -Theorem fequiv_refl[simp]: !f x. (x == x) f -Proof rw_tac std_ss[fequiv_def] -QED - -(* Theorem: [Symmetric] (x == y) f ==> (y == x) f *) -(* Proof: by defintion, - and f x = f y means the same as f y = f x. -*) -val fequiv_sym = store_thm( - "fequiv_sym", - ``!f x y. (x == y) f ==> (y == x) f``, - rw_tac std_ss[fequiv_def]); - -(* no export of commutativity *) - -(* Theorem: [Transitive] (x == y) f /\ (y == z) f ==> (x == z) f *) -(* Proof: by defintion, - and f x = f y - and f y = f z - implies f x = f z. -*) -val fequiv_trans = store_thm( - "fequiv_trans", - ``!f x y z. (x == y) f /\ (y == z) f ==> (x == z) f``, - rw_tac std_ss[fequiv_def]); - -(* Theorem: fequiv (==) is an equivalence relation on the domain. *) -(* Proof: by reflexive, symmetric and transitive. *) -val fequiv_equiv_class = store_thm( - "fequiv_equiv_class", - ``!f. (\x y. (x == y) f) equiv_on univ(:'a)``, - rw_tac std_ss[equiv_on_def, fequiv_def, EQ_IMP_THM]); - -(* ------------------------------------------------------------------------- *) -(* Function-based Equivalence *) -(* ------------------------------------------------------------------------- *) - -Overload feq = “flip (flip o fequiv)” -Overload feq_class[inferior] = “preimage” - -(* Theorem: x IN feq_class f s n <=> x IN s /\ (f x = n) *) -(* Proof: by feq_class_def *) -Theorem feq_class_element = in_preimage - -(* Note: - y IN equiv_class (feq f) s x -<=> y IN s /\ (feq f x y) by equiv_class_element -<=> y IN s /\ (f x = f y) by feq_def -*) - -(* Theorem: feq_class f s (f x) = equiv_class (feq f) s x *) -(* Proof: - feq_class f s (f x) - = {y | y IN s /\ (f y = f x)} by feq_class_def - = {y | y IN s /\ (f x = f y)} - = {y | y IN s /\ (feq f x y)} by feq_def - = equiv_class (feq f) s x by notation -*) -val feq_class_property = store_thm( - "feq_class_property", - ``!f s x. feq_class f s (f x) = equiv_class (feq f) s x``, - rw[in_preimage, EXTENSION, fequiv_def] >> metis_tac[]); - -(* Theorem: (feq_class f s) o f = equiv_class (feq f) s *) -(* Proof: by FUN_EQ_THM, feq_class_property *) -val feq_class_fun = store_thm( - "feq_class_fun", - ``!f s. (feq_class f s) o f = equiv_class (feq f) s``, - rw[FUN_EQ_THM, feq_class_property]); - -(* Theorem: feq f equiv_on s *) -(* Proof: by equiv_on_def, feq_def *) -val feq_equiv = store_thm( - "feq_equiv", - ``!s f. feq f equiv_on s``, - rw[equiv_on_def, fequiv_def] >> - metis_tac[]); - -(* Theorem: partition (feq f) s = IMAGE ((feq_class f s) o f) s *) -(* Proof: - Use partition_def |> ISPEC ``feq f`` |> ISPEC ``(s:'a -> bool)``; - - partition (feq f) s - = {t | ?x. x IN s /\ (t = {y | y IN s /\ feq f x y})} by partition_def - = {t | ?x. x IN s /\ (t = {y | y IN s /\ (f x = f y)})} by feq_def - = {t | ?x. x IN s /\ (t = feq_class f s (f x))} by feq_class_def - = {feq_class f s (f x) | x | x IN s } by rewriting - = IMAGE (feq_class f s) (IMAGE f s) by IN_IMAGE - = IMAGE ((feq_class f s) o f) s by IMAGE_COMPOSE -*) -val feq_partition = store_thm( - "feq_partition", - ``!s f. partition (feq f) s = IMAGE ((feq_class f s) o f) s``, - rw[partition_def, fequiv_def, in_preimage, EXTENSION, EQ_IMP_THM] >> - metis_tac[]); - -(* Theorem: t IN partition (feq f) s <=> ?z. z IN s /\ (!x. x IN t <=> x IN s /\ (f x = f z)) *) -(* Proof: by feq_partition, feq_class_def, EXTENSION *) -Theorem feq_partition_element: - !s f t. t IN partition (feq f) s <=> - ?z. z IN s /\ (!x. x IN t <=> x IN s /\ (f x = f z)) -Proof - rw[feq_partition, in_preimage, EXTENSION] >> metis_tac[] -QED - -(* Theorem: x IN s <=> ?e. e IN partition (feq f) s /\ x IN e *) -(* Proof: - Note (feq f) equiv_on s by feq_equiv - This result follows by partition_element_exists -*) -Theorem feq_partition_element_exists: - !f s x. x IN s <=> ?e. e IN partition (feq f) s /\ x IN e -Proof - simp[feq_equiv, partition_element_exists] -QED - -(* Theorem: e IN partition (feq f) s ==> e <> {} *) -(* Proof: - Note (feq f) equiv_on s by feq_equiv - so e <> {} by partition_element_not_empty -*) -Theorem feq_partition_element_not_empty: - !f s e. e IN partition (feq f) s ==> e <> {} -Proof - metis_tac[feq_equiv, partition_element_not_empty] -QED - -(* Theorem: partition (feq f) s = IMAGE (preimage f s o f) s *) -(* Proof: - x IN partition (feq f) s - <=> ?z. z IN s /\ !j. j IN x <=> j IN s /\ (f j = f z) by feq_partition_element - <=> ?z. z IN s /\ !j. j IN x <=> j IN (preimage f s (f z)) by preimage_element - <=> ?z. z IN s /\ (x = preimage f s (f z)) by EXTENSION - <=> ?z. z IN s /\ (x = (preimage f s o f) z) by composition (o_THM) - <=> x IN IMAGE (preimage f s o f) s by IN_IMAGE - Hence partition (feq f) s = IMAGE (preimage f s o f) s by EXTENSION - - or, - partition (feq f) s - = IMAGE (feq_class f s o f) s by feq_partition - = IMAGE (preiamge f s o f) s by feq_class_eq_preimage -*) -val feq_partition_by_preimage = feq_partition - -(* Theorem: FINITE s ==> !f g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s) *) -(* Proof: - Since (feq f) equiv_on s by feq_equiv - Hence !g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s) by set_sigma_by_partition -*) -val feq_sum_over_partition = store_thm( - "feq_sum_over_partition", - ``!s. FINITE s ==> !f g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s)``, - rw[feq_equiv, set_sigma_by_partition]); - -(* Theorem: FINITE s ==> !f. CARD s = SIGMA CARD (partition (feq f) s) *) -(* Proof: - Note feq equiv_on s by feq_equiv - The result follows by partition_CARD -*) -val finite_card_by_feq_partition = store_thm( - "finite_card_by_feq_partition", - ``!s. FINITE s ==> !f. CARD s = SIGMA CARD (partition (feq f) s)``, - rw[feq_equiv, partition_CARD]); - -(* Theorem: FINITE s ==> !f. CARD s = SIGMA CARD (IMAGE ((preimage f s) o f) s) *) -(* Proof: - Note (feq f) equiv_on s by feq_equiv - CARD s - = SIGMA CARD (partition (feq f) s) by partition_CARD - = SIGMA CARD (IMAGE (preimage f s o f) s) by feq_partition_by_preimage -*) -val finite_card_by_image_preimage = store_thm( - "finite_card_by_image_preimage", - ``!s. FINITE s ==> !f. CARD s = SIGMA CARD (IMAGE ((preimage f s) o f) s)``, - rw[feq_equiv, partition_CARD, GSYM feq_partition]); - -(* Theorem: FINITE s /\ SURJ f s t ==> - CARD s = SIGMA CARD (IMAGE (preimage f s) t) *) -(* Proof: - CARD s - = SIGMA CARD (IMAGE (preimage f s o f) s) by finite_card_by_image_preimage - = SIGMA CARD (IMAGE (preimage f s) (IMAGE f s)) by IMAGE_COMPOSE - = SIGMA CARD (IMAGE (preimage f s) t) by IMAGE_SURJ -*) -Theorem finite_card_surj_by_image_preimage: - !f s t. FINITE s /\ SURJ f s t ==> - CARD s = SIGMA CARD (IMAGE (preimage f s) t) -Proof - rpt strip_tac >> - `CARD s = SIGMA CARD (IMAGE (preimage f s o f) s)` by rw[finite_card_by_image_preimage] >> - `_ = SIGMA CARD (IMAGE (preimage f s) (IMAGE f s))` by rw[IMAGE_COMPOSE] >> - `_ = SIGMA CARD (IMAGE (preimage f s) t)` by fs[IMAGE_SURJ] >> - simp[] -QED - -(* Theorem: BIJ (preimage f s) (IMAGE f s) (partition (feq f) s) *) -(* Proof: - Let g = preimage f s, t = IMAGE f s. - Note INJ g t (POW s) by preimage_image_inj - so BIJ g t (IMAGE g t) by INJ_IMAGE_BIJ - But IMAGE g t - = IMAGE (preimage f s) (IMAGE f s) by notation - = IMAGE (preimage f s o f) s by IMAGE_COMPOSE - = partition (feq f) s by feq_partition_by_preimage - Thus BIJ g t (partition (feq f) s) by above -*) -Theorem preimage_image_bij: - !f s. BIJ (preimage f s) (IMAGE f s) (partition (feq f) s) -Proof - rpt strip_tac >> - qabbrev_tac `g = preimage f s` >> - qabbrev_tac `t = IMAGE f s` >> - `BIJ g t (IMAGE g t)` by metis_tac[preimage_image_inj, INJ_IMAGE_BIJ] >> - simp[IMAGE_COMPOSE, feq_partition, Abbr`g`, Abbr`t`] -QED - -(* ------------------------------------------------------------------------- *) -(* Condition for surjection to be a bijection. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> SING e *) -(* Proof: - If part: e IN partition (feq f) s ==> SING e - e IN partition (feq f) s - <=> ?z. z IN s /\ !x. x IN e <=> x IN s /\ f x = f z - by feq_partition_element - Thus z IN e, so e <> {} by MEMBER_NOT_EMPTY - and !x. x IN e ==> x = z by INJ_DEF - so SING e by SING_ONE_ELEMENT - Only-if part: !e. e IN partition (feq f) s ==> SING e ==> INJ f s (IMAGE f s) - By INJ_DEF, IN_IMAGE, this is to show: - !x y. x IN s /\ y IN s /\ f x = f y ==> x = y - Note ?e. e IN (partition (feq f) s) /\ x IN e - by feq_partition_element_exists - and y IN e by feq_partition_element - then SING e by implication - so x = y by IN_SING -*) -Theorem inj_iff_partition_element_sing: - !f s. INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> SING e -Proof - rw[EQ_IMP_THM] >| [ - fs[feq_partition_element, INJ_DEF] >> - `e <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - simp[SING_ONE_ELEMENT], - rw[INJ_DEF] >> - `?e. e IN (partition (feq f) s) /\ x IN e` by fs[GSYM feq_partition_element_exists] >> - `y IN e` by metis_tac[feq_partition_element] >> - metis_tac[SING_DEF, IN_SING] - ] -QED - -(* Theorem: FINITE s ==> - (INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> CARD e = 1) *) -(* Proof: - INJ f s (IMAGE f s) - <=> !e. e IN (partition (feq f) s) ==> SING e by inj_iff_partition_element_sing - <=> !e. e IN (partition (feq f) s) ==> CARD e = 1 by FINITE_partition, CARD_EQ_1 -*) -Theorem inj_iff_partition_element_card_1: - !f s. FINITE s ==> - (INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> CARD e = 1) -Proof - metis_tac[inj_iff_partition_element_sing, FINITE_partition, CARD_EQ_1] -QED - -(* Idea: for a finite domain, with target same size, surjection means injection. *) - -(* Theorem: FINITE s /\ CARD s = CARD t /\ SURJ f s t ==> INJ f s t *) -(* Proof: - Let p = partition (feq f) s. - Note IMAGE f s = t by IMAGE_SURJ - so FINITE t by IMAGE_FINITE - and CARD s = SIGMA CARD p by finite_card_by_feq_partition - and CARD t = CARD p by preimage_image_bij, bij_eq_card - Thus CARD p = SIGMA CARD p by given CARD s = CARD t - Now FINITE p by FINITE_partition - and !e. e IN p ==> FINITE e by FINITE_partition - and !e. e IN p ==> e <> {} by feq_partition_element_not_empty - so !e. e IN p ==> CARD e <> 0 by CARD_EQ_0 - Thus !e. e IN p ==> CARD e = 1 by card_eq_sigma_card - or INJ f s (IMAGE f s) by inj_iff_partition_element_card_1 - so INJ f s t by IMAGE f s = t -*) -Theorem FINITE_SURJ_IS_INJ: - !f s t. FINITE s /\ CARD s = CARD t /\ SURJ f s t ==> INJ f s t -Proof - rpt strip_tac >> - imp_res_tac finite_card_by_feq_partition >> - first_x_assum (qspec_then `f` strip_assume_tac) >> - qabbrev_tac `p = partition (feq f) s` >> - `IMAGE f s = t` by fs[IMAGE_SURJ] >> - `FINITE t` by rw[] >> - `CARD t = CARD p` by metis_tac[preimage_image_bij, FINITE_BIJ_CARD] >> - `FINITE p /\ !e. e IN p ==> FINITE e` by metis_tac[FINITE_partition] >> - `!e. e IN p ==> CARD e <> 0` by metis_tac[feq_partition_element_not_empty, CARD_EQ_0] >> - `!e. e IN p ==> CARD e = 1` by metis_tac[card_eq_sigma_card] >> - metis_tac[inj_iff_partition_element_card_1] -QED - -(* Finally! show that SURJ can imply BIJ. *) - -(* Theorem: FINITE s /\ CARD s = CARD t /\ SURJ f s t ==> BIJ f s t *) -(* Proof: - Note INJ f s t by FINITE_SURJ_IS_INJ - so BIJ f s t by BIJ_DEF, SURJ f s t -*) -Theorem FINITE_SURJ_IS_BIJ: - !f s t. FINITE s /\ CARD s = CARD t /\ SURJ f s t ==> BIJ f s t -Proof - simp[FINITE_SURJ_IS_INJ, BIJ_DEF] -QED - -(* ------------------------------------------------------------------------- *) -(* Function Iteration *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: FUNPOW f 2 x = f (f x) *) -(* Proof: by definition. *) -val FUNPOW_2 = store_thm( - "FUNPOW_2", - ``!f x. FUNPOW f 2 x = f (f x)``, - simp_tac bool_ss [FUNPOW, TWO, ONE]); - -(* Theorem: FUNPOW (K c) n x = if n = 0 then x else c *) -(* Proof: - By induction on n. - Base: !x c. FUNPOW (K c) 0 x = if 0 = 0 then x else c - FUNPOW (K c) 0 x - = x by FUNPOW - = if 0 = 0 then x else c by 0 = 0 is true - Step: !x c. FUNPOW (K c) n x = if n = 0 then x else c ==> - !x c. FUNPOW (K c) (SUC n) x = if SUC n = 0 then x else c - FUNPOW (K c) (SUC n) x - = FUNPOW (K c) n ((K c) x) by FUNPOW - = if n = 0 then ((K c) c) else c by induction hypothesis - = if n = 0 then c else c by K_THM - = c by either case - = if SUC n = 0 then x else c by SUC n = 0 is false -*) -val FUNPOW_K = store_thm( - "FUNPOW_K", - ``!n x c. FUNPOW (K c) n x = if n = 0 then x else c``, - Induct >- - rw[] >> - metis_tac[FUNPOW, combinTheory.K_THM, SUC_NOT_ZERO]); - -(* Theorem: 0 < k /\ FUNPOW f k e = e ==> !n. FUNPOW f (n*k) e = e *) -(* Proof: - By induction on n: - Base case: FUNPOW f (0 * k) e = e - FUNPOW f (0 * k) e - = FUNPOW f 0 e by arithmetic - = e by FUNPOW_0 - Step case: FUNPOW f (n * k) e = e ==> FUNPOW f (SUC n * k) e = e - FUNPOW f (SUC n * k) e - = FUNPOW f (k + n * k) e by arithmetic - = FUNPOW f k (FUNPOW (n * k) e) by FUNPOW_ADD. - = FUNPOW f k e by induction hypothesis - = e by given -*) -val FUNPOW_MULTIPLE = store_thm( - "FUNPOW_MULTIPLE", - ``!f k e. 0 < k /\ (FUNPOW f k e = e) ==> !n. FUNPOW f (n*k) e = e``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - metis_tac[MULT_COMM, MULT_SUC, FUNPOW_ADD]); - -(* Theorem: 0 < k /\ FUNPOW f k e = e ==> !n. FUNPOW f n e = FUNPOW f (n MOD k) e *) -(* Proof: - FUNPOW f n e - = FUNPOW f ((n DIV k) * k + (n MOD k)) e by division algorithm - = FUNPOW f ((n MOD k) + (n DIV k) * k) e by arithmetic - = FUNPOW f (n MOD k) (FUNPOW (n DIV k) * k e) by FUNPOW_ADD - = FUNPOW f (n MOD k) e by FUNPOW_MULTIPLE -*) -val FUNPOW_MOD = store_thm( - "FUNPOW_MOD", - ``!f k e. 0 < k /\ (FUNPOW f k e = e) ==> !n. FUNPOW f n e = FUNPOW f (n MOD k) e``, - rpt strip_tac >> - `n = (n MOD k) + (n DIV k) * k` by metis_tac[DIVISION, ADD_COMM] >> - metis_tac[FUNPOW_ADD, FUNPOW_MULTIPLE]); - -(* Theorem: FUNPOW f m (FUNPOW f n x) = FUNPOW f n (FUNPOW f m x) *) -(* Proof: by FUNPOW_ADD, ADD_COMM *) -Theorem FUNPOW_COMM: - !f m n x. FUNPOW f m (FUNPOW f n x) = FUNPOW f n (FUNPOW f m x) -Proof - metis_tac[FUNPOW_ADD, ADD_COMM] -QED - -(* Overload a RISING function *) -val _ = overload_on ("RISING", ``\f. !x:num. x <= f x``); - -(* Overload a FALLING function *) -val _ = overload_on ("FALLING", ``\f. !x:num. f x <= x``); - -(* Theorem: RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x *) -(* Proof: - By induction on n. - Base: !m. m <= 0 ==> !x. FUNPOW f m x <= FUNPOW f 0 x - Note m = 0, and FUNPOW f 0 x <= FUNPOW f 0 x. - Step: !m. RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x ==> - !m. m <= SUC n ==> FUNPOW f m x <= FUNPOW f (SUC n) x - Note m <= n or m = SUC n. - If m = SUC n, this is trivial. - If m <= n, - FUNPOW f m x - <= FUNPOW f n x by induction hypothesis - <= f (FUNPOW f n x) by RISING f - = FUNPOW f (SUC n) x by FUNPOW_SUC -*) -val FUNPOW_LE_RISING = store_thm( - "FUNPOW_LE_RISING", - ``!f m n. RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x``, - strip_tac >> - Induct_on `n` >- - rw[] >> - rpt strip_tac >> - `(m <= n) \/ (m = SUC n)` by decide_tac >| [ - `FUNPOW f m x <= FUNPOW f n x` by rw[] >> - `FUNPOW f n x <= f (FUNPOW f n x)` by rw[] >> - `f (FUNPOW f n x) = FUNPOW f (SUC n) x` by rw[FUNPOW_SUC] >> - decide_tac, - rw[] - ]); - -(* Theorem: FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x *) -(* Proof: - By induction on n. - Base: !m. m <= 0 ==> !x. FUNPOW f 0 x <= FUNPOW f m x - Note m = 0, and FUNPOW f 0 x <= FUNPOW f 0 x. - Step: !m. FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x ==> - !m. m <= SUC n ==> FUNPOW f (SUC n) x <= FUNPOW f m x - Note m <= n or m = SUC n. - If m = SUC n, this is trivial. - If m <= n, - FUNPOW f (SUC n) x - = f (FUNPOW f n x) by FUNPOW_SUC - <= FUNPOW f n x by FALLING f - <= FUNPOW f m x by induction hypothesis -*) -val FUNPOW_LE_FALLING = store_thm( - "FUNPOW_LE_FALLING", - ``!f m n. FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x``, - strip_tac >> - Induct_on `n` >- - rw[] >> - rpt strip_tac >> - `(m <= n) \/ (m = SUC n)` by decide_tac >| [ - `FUNPOW f (SUC n) x = f (FUNPOW f n x)` by rw[FUNPOW_SUC] >> - `f (FUNPOW f n x) <= FUNPOW f n x` by rw[] >> - `FUNPOW f n x <= FUNPOW f m x` by rw[] >> - decide_tac, - rw[] - ]); - -(* Theorem: (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x *) -(* Proof: - By induction on n. - Base: FUNPOW f 0 x <= FUNPOW g 0 x - FUNPOW f 0 x by FUNPOW_0 - = x - <= x = FUNPOW g 0 x by FUNPOW_0 - Step: FUNPOW f n x <= FUNPOW g n x ==> FUNPOW f (SUC n) x <= FUNPOW g (SUC n) x - FUNPOW f (SUC n) x - = f (FUNPOW f n x) by FUNPOW_SUC - <= g (FUNPOW f n x) by !x. f x <= g x - <= g (FUNPOW g n x) by induction hypothesis, MONO g - = FUNPOW g (SUC n) x by FUNPOW_SUC -*) -val FUNPOW_LE_MONO = store_thm( - "FUNPOW_LE_MONO", - ``!f g. (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - rw[FUNPOW_SUC] >> - `f (FUNPOW f n x) <= g (FUNPOW f n x)` by rw[] >> - `g (FUNPOW f n x) <= g (FUNPOW g n x)` by rw[] >> - decide_tac); - -(* Note: -There is no FUNPOW_LE_RMONO. FUNPOW_LE_MONO says: -|- !f g. (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x -To compare the terms in these two sequences: - x, f x, f (f x), f (f (f x)), ...... - x, g x, g (g x), g (g (g x)), ...... -For the first pair: x <= x. -For the second pair: f x <= g x, as g is cover. -For the third pair: f (f x) <= g (f x) by g is cover, - <= g (g x) by MONO g, and will not work if RMONO g. -*) - -(* Theorem: (!x. f x <= g x) /\ MONO f ==> !n x. FUNPOW f n x <= FUNPOW g n x *) -(* Proof: - By induction on n. - Base: FUNPOW f 0 x <= FUNPOW g 0 x - FUNPOW f 0 x by FUNPOW_0 - = x - <= x = FUNPOW g 0 x by FUNPOW_0 - Step: FUNPOW f n x <= FUNPOW g n x ==> FUNPOW f (SUC n) x <= FUNPOW g (SUC n) x - FUNPOW f (SUC n) x - = f (FUNPOW f n x) by FUNPOW_SUC - <= f (FUNPOW g n x) by induction hypothesis, MONO f - <= g (FUNPOW g n x) by !x. f x <= g x - = FUNPOW g (SUC n) x by FUNPOW_SUC -*) -val FUNPOW_GE_MONO = store_thm( - "FUNPOW_GE_MONO", - ``!f g. (!x. f x <= g x) /\ MONO f ==> !n x. FUNPOW f n x <= FUNPOW g n x``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - rw[FUNPOW_SUC] >> - `f (FUNPOW f n x) <= f (FUNPOW g n x)` by rw[] >> - `f (FUNPOW g n x) <= g (FUNPOW g n x)` by rw[] >> - decide_tac); - -(* Note: the name FUNPOW_SUC is taken: -FUNPOW_SUC |- !f n x. FUNPOW f (SUC n) x = f (FUNPOW f n x) -*) - -(* Theorem: FUNPOW SUC n m = m + n *) -(* Proof: - By induction on n. - Base: !m. FUNPOW SUC 0 m = m + 0 - LHS = FUNPOW SUC 0 m - = m by FUNPOW_0 - = m + 0 = RHS by ADD_0 - Step: !m. FUNPOW SUC n m = m + n ==> - !m. FUNPOW SUC (SUC n) m = m + SUC n - FUNPOW SUC (SUC n) m - = FUNPOW SUC n (SUC m) by FUNPOW - = (SUC m) + n by induction hypothesis - = m + SUC n by arithmetic -*) -val FUNPOW_ADD1 = store_thm( - "FUNPOW_ADD1", - ``!m n. FUNPOW SUC n m = m + n``, - Induct_on `n` >> - rw[FUNPOW]); - -(* Theorem: FUNPOW PRE n m = m - n *) -(* Proof: - By induction on n. - Base: !m. FUNPOW PRE 0 m = m - 0 - LHS = FUNPOW PRE 0 m - = m by FUNPOW_0 - = m + 0 = RHS by ADD_0 - Step: !m. FUNPOW PRE n m = m - n ==> - !m. FUNPOW PRE (SUC n) m = m - SUC n - FUNPOW PRE (SUC n) m - = FUNPOW PRE n (PRE m) by FUNPOW - = (PRE m) - n by induction hypothesis - = m - PRE n by arithmetic -*) -val FUNPOW_SUB1 = store_thm( - "FUNPOW_SUB1", - ``!m n. FUNPOW PRE n m = m - n``, - Induct_on `n` >- - rw[] >> - rw[FUNPOW]); - -(* Theorem: FUNPOW ($* b) n m = m * b ** n *) -(* Proof: - By induction on n. - Base: !m. !m. FUNPOW ($* b) 0 m = m * b ** 0 - LHS = FUNPOW ($* b) 0 m - = m by FUNPOW_0 - = m * 1 by MULT_RIGHT_1 - = m * b ** 0 = RHS by EXP_0 - Step: !m. FUNPOW ($* b) n m = m * b ** n ==> - !m. FUNPOW ($* b) (SUC n) m = m * b ** SUC n - FUNPOW ($* b) (SUC n) m - = FUNPOW ($* b) n (b * m) by FUNPOW - = b * m * b ** n by induction hypothesis - = m * (b * b ** n) by arithmetic - = m * b ** SUC n by EXP -*) -val FUNPOW_MUL = store_thm( - "FUNPOW_MUL", - ``!b m n. FUNPOW ($* b) n m = m * b ** n``, - strip_tac >> - Induct_on `n` >- - rw[] >> - rw[FUNPOW, EXP]); - -(* Theorem: 0 < b ==> (FUNPOW (combin$C $DIV b) n m = m DIV (b ** n)) *) -(* Proof: - By induction on n. - Let f = combin$C $DIV b. - Base: !m. FUNPOW f 0 m = m DIV b ** 0 - LHS = FUNPOW f 0 m - = m by FUNPOW_0 - = m DIV 1 by DIV_1 - = m DIV (b ** 0) = RHS by EXP_0 - Step: !m. FUNPOW f n m = m DIV b ** n ==> - !m. FUNPOW f (SUC n) m = m DIV b ** SUC n - FUNPOW f (SUC n) m - = FUNPOW f n (f m) by FUNPOW - = FUNPOW f n (m DIV b) by C_THM - = (m DIV b) DIV (b ** n) by induction hypothesis - = m DIV (b * b ** n) by DIV_DIV_DIV_MULT, 0 < b, 0 < b ** n - = m DIV b ** SUC n by EXP -*) -val FUNPOW_DIV = store_thm( - "FUNPOW_DIV", - ``!b m n. 0 < b ==> (FUNPOW (combin$C $DIV b) n m = m DIV (b ** n))``, - strip_tac >> - qabbrev_tac `f = combin$C $DIV b` >> - Induct_on `n` >- - rw[EXP_0] >> - rpt strip_tac >> - `FUNPOW f (SUC n) m = FUNPOW f n (m DIV b)` by rw[FUNPOW, Abbr`f`] >> - `_ = (m DIV b) DIV (b ** n)` by rw[] >> - `_ = m DIV (b * b ** n)` by rw[DIV_DIV_DIV_MULT] >> - `_ = m DIV b ** SUC n` by rw[EXP] >> - decide_tac); - -(* Theorem: FUNPOW SQ n m = m ** (2 ** n) *) -(* Proof: - By induction on n. - Base: !m. FUNPOW (\n. SQ n) 0 m = m ** 2 ** 0 - FUNPOW SQ 0 m - = m by FUNPOW_0 - = m ** 1 by EXP_1 - = m ** 2 ** 0 by EXP_0 - Step: !m. FUNPOW (\n. SQ n) n m = m ** 2 ** n ==> - !m. FUNPOW (\n. SQ n) (SUC n) m = m ** 2 ** SUC n - FUNPOW (\n. SQ n) (SUC n) m - = SQ (FUNPOW (\n. SQ n) n m) by FUNPOW_SUC - = SQ (m ** 2 ** n) by induction hypothesis - = (m ** 2 ** n) ** 2 by EXP_2 - = m ** (2 * 2 ** n) by EXP_EXP_MULT - = m ** 2 ** SUC n by EXP -*) -val FUNPOW_SQ = store_thm( - "FUNPOW_SQ", - ``!m n. FUNPOW SQ n m = m ** (2 ** n)``, - Induct_on `n` >- - rw[] >> - rw[FUNPOW_SUC, GSYM EXP_EXP_MULT, EXP]); - -(* Theorem: 0 < m /\ 0 < n ==> (FUNPOW (\n. (n * n) MOD m) n k = (k ** 2 ** n) MOD m) *) -(* Proof: - Lef f = (\n. SQ n MOD m). - By induction on n. - Base: !k. 0 < m /\ 0 < 0 ==> FUNPOW f 0 k = k ** 2 ** 0 MOD m - True since 0 < 0 = F. - Step: !k. 0 < m /\ 0 < n ==> FUNPOW f n k = k ** 2 ** n MOD m ==> - !k. 0 < m /\ 0 < SUC n ==> FUNPOW f (SUC n) k = k ** 2 ** SUC n MOD m - If n = 1, - FUNPOW f (SUC 0) k - = FUNPOW f 1 k by ONE - = f k by FUNPOW_1 - = SQ k MOD m by notation - = (k ** 2) MOD m by EXP_2 - = (k ** (2 ** 1)) MOD m by EXP_1 - If n <> 0, - FUNPOW f (SUC n) k - = f (FUNPOW f n k) by FUNPOW_SUC - = f (k ** 2 ** n MOD m) by induction hypothesis - = (k ** 2 ** n MOD m) * (k ** 2 ** n MOD m) MOD m by notation - = (k ** 2 ** n * k ** 2 ** n) MOD m by MOD_TIMES2 - = (k ** (2 ** n + 2 ** n)) MOD m by EXP_BASE_MULT - = (k ** (2 * 2 ** n)) MOD m by arithmetic - = (k ** 2 ** SUC n) MOD m by EXP -*) -val FUNPOW_SQ_MOD = store_thm( - "FUNPOW_SQ_MOD", - ``!m n k. 0 < m /\ 0 < n ==> (FUNPOW (\n. (n * n) MOD m) n k = (k ** 2 ** n) MOD m)``, - strip_tac >> - qabbrev_tac `f = \n. SQ n MOD m` >> - Induct >> - simp[] >> - rpt strip_tac >> - Cases_on `n = 0` >- - simp[Abbr`f`] >> - rw[FUNPOW_SUC, Abbr`f`] >> - `(k ** 2 ** n) ** 2 = k ** (2 * 2 ** n)` by rw[GSYM EXP_EXP_MULT] >> - `_ = k ** 2 ** SUC n` by rw[EXP] >> - rw[]); - -(* Theorem: 0 < n ==> (FUNPOW (\x. MAX x m) n k = MAX k m) *) -(* Proof: - By induction on n. - Base: !m k. 0 < 0 ==> FUNPOW (\x. MAX x m) 0 k = MAX k m - True by 0 < 0 = F. - Step: !m k. 0 < n ==> FUNPOW (\x. MAX x m) n k = MAX k m ==> - !m k. 0 < SUC n ==> FUNPOW (\x. MAX x m) (SUC n) k = MAX k m - If n = 0, - FUNPOW (\x. MAX x m) (SUC 0) k - = FUNPOW (\x. MAX x m) 1 k by ONE - = (\x. MAX x m) k by FUNPOW_1 - = MAX k m by function application - If n <> 0, - FUNPOW (\x. MAX x m) (SUC n) k - = f (FUNPOW (\x. MAX x m) n k) by FUNPOW_SUC - = (\x. MAX x m) (MAX k m) by induction hypothesis - = MAX (MAX k m) m by function application - = MAX k m by MAX_IS_MAX, m <= MAX k m -*) -val FUNPOW_MAX = store_thm( - "FUNPOW_MAX", - ``!m n k. 0 < n ==> (FUNPOW (\x. MAX x m) n k = MAX k m)``, - Induct_on `n` >- - simp[] >> - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - rw[FUNPOW_SUC] >> - `m <= MAX k m` by rw[] >> - rw[MAX_DEF]); - -(* Theorem: 0 < n ==> (FUNPOW (\x. MIN x m) n k = MIN k m) *) -(* Proof: - By induction on n. - Base: !m k. 0 < 0 ==> FUNPOW (\x. MIN x m) 0 k = MIN k m - True by 0 < 0 = F. - Step: !m k. 0 < n ==> FUNPOW (\x. MIN x m) n k = MIN k m ==> - !m k. 0 < SUC n ==> FUNPOW (\x. MIN x m) (SUC n) k = MIN k m - If n = 0, - FUNPOW (\x. MIN x m) (SUC 0) k - = FUNPOW (\x. MIN x m) 1 k by ONE - = (\x. MIN x m) k by FUNPOW_1 - = MIN k m by function application - If n <> 0, - FUNPOW (\x. MIN x m) (SUC n) k - = f (FUNPOW (\x. MIN x m) n k) by FUNPOW_SUC - = (\x. MIN x m) (MIN k m) by induction hypothesis - = MIN (MIN k m) m by function application - = MIN k m by MIN_IS_MIN, MIN k m <= m -*) -val FUNPOW_MIN = store_thm( - "FUNPOW_MIN", - ``!m n k. 0 < n ==> (FUNPOW (\x. MIN x m) n k = MIN k m)``, - Induct_on `n` >- - simp[] >> - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - rw[FUNPOW_SUC] >> - `MIN k m <= m` by rw[] >> - rw[MIN_DEF]); - -(* Theorem: FUNPOW (\(x,y). (f x, g y)) n (x,y) = (FUNPOW f n x, FUNPOW g n y) *) -(* Proof: - By induction on n. - Base: FUNPOW (\(x,y). (f x,g y)) 0 (x,y) = (FUNPOW f 0 x,FUNPOW g 0 y) - FUNPOW (\(x,y). (f x,g y)) 0 (x,y) - = (x,y) by FUNPOW_0 - = (FUNPOW f 0 x, FUNPOW g 0 y) by FUNPOW_0 - Step: FUNPOW (\(x,y). (f x,g y)) n (x,y) = (FUNPOW f n x,FUNPOW g n y) ==> - FUNPOW (\(x,y). (f x,g y)) (SUC n) (x,y) = (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y) - FUNPOW (\(x,y). (f x,g y)) (SUC n) (x,y) - = (\(x,y). (f x,g y)) (FUNPOW (\(x,y). (f x,g y)) n (x,y)) by FUNPOW_SUC - = (\(x,y). (f x,g y)) (FUNPOW f n x,FUNPOW g n y) by induction hypothesis - = (f (FUNPOW f n x),g (FUNPOW g n y)) by function application - = (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y) by FUNPOW_SUC -*) -val FUNPOW_PAIR = store_thm( - "FUNPOW_PAIR", - ``!f g n x y. FUNPOW (\(x,y). (f x, g y)) n (x,y) = (FUNPOW f n x, FUNPOW g n y)``, - rpt strip_tac >> - Induct_on `n` >> - rw[FUNPOW_SUC]); - -(* Theorem: FUNPOW (\(x,y,z). (f x, g y, h z)) n (x,y,z) = (FUNPOW f n x, FUNPOW g n y, FUNPOW h n z) *) -(* Proof: - By induction on n. - Base: FUNPOW (\(x,y,z). (f x,g y,h z)) 0 (x,y,z) = (FUNPOW f 0 x,FUNPOW g 0 y,FUNPOW h 0 z) - FUNPOW (\(x,y,z). (f x,g y,h z)) 0 (x,y,z) - = (x,y) by FUNPOW_0 - = (FUNPOW f 0 x, FUNPOW g 0 y, FUNPOW h 0 z) by FUNPOW_0 - Step: FUNPOW (\(x,y,z). (f x,g y,h z)) n (x,y,z) = - (FUNPOW f n x,FUNPOW g n y,FUNPOW h n z) ==> - FUNPOW (\(x,y,z). (f x,g y,h z)) (SUC n) (x,y,z) = - (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y,FUNPOW h (SUC n) z) - Let fun = (\(x,y,z). (f x,g y,h z)). - FUNPOW fun (SUC n) (x,y, z) - = fun (FUNPOW fun n (x,y,z)) by FUNPOW_SUC - = fun (FUNPOW f n x,FUNPOW g n y, FUNPOW h n z) by induction hypothesis - = (f (FUNPOW f n x),g (FUNPOW g n y), h (FUNPOW h n z)) by function application - = (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y, FUNPOW h (SUC n) z) by FUNPOW_SUC -*) -val FUNPOW_TRIPLE = store_thm( - "FUNPOW_TRIPLE", - ``!f g h n x y z. FUNPOW (\(x,y,z). (f x, g y, h z)) n (x,y,z) = - (FUNPOW f n x, FUNPOW g n y, FUNPOW h n z)``, - rpt strip_tac >> - Induct_on `n` >> - rw[FUNPOW_SUC]); - -(* ------------------------------------------------------------------------- *) -(* More FUNPOW Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: f PERMUTES s ==> (LINV f s) PERMUTES s *) -(* Proof: by BIJ_LINV_BIJ *) -Theorem LINV_permutes: - !f s. f PERMUTES s ==> (LINV f s) PERMUTES s -Proof - rw[BIJ_LINV_BIJ] -QED - -(* Theorem: f PERMUTES s ==> (FUNPOW f n) PERMUTES s *) -(* Proof: - By induction on n. - Base: FUNPOW f 0 PERMUTES s - Note FUNPOW f 0 = I by FUN_EQ_THM, FUNPOW_0 - and I PERMUTES s by BIJ_I_SAME - thus true. - Step: f PERMUTES s /\ FUNPOW f n PERMUTES s ==> - FUNPOW f (SUC n) PERMUTES s - Note FUNPOW f (SUC n) - = f o (FUNPOW f n) by FUN_EQ_THM, FUNPOW_SUC - Thus true by BIJ_COMPOSE -*) -Theorem FUNPOW_permutes: - !f s n. f PERMUTES s ==> (FUNPOW f n) PERMUTES s -Proof - rpt strip_tac >> - Induct_on `n` >| [ - `FUNPOW f 0 = I` by rw[FUN_EQ_THM] >> - simp[BIJ_I_SAME], - `FUNPOW f (SUC n) = f o (FUNPOW f n)` by rw[FUN_EQ_THM, FUNPOW_SUC] >> - metis_tac[BIJ_COMPOSE] - ] -QED - -(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s *) -(* Proof: - By induction on n. - Base: FUNPOW f 0 x IN s - Since FUNPOW f 0 x = x by FUNPOW_0 - This is trivially true. - Step: FUNPOW f n x IN s ==> FUNPOW f (SUC n) x IN s - FUNPOW f (SUC n) x - = f (FUNPOW f n x) by FUNPOW_SUC - But FUNPOW f n x IN s by induction hypothesis - so f (FUNPOW f n x) IN s by BIJ_ELEMENT, f PERMUTES s -*) -Theorem FUNPOW_closure: - !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s -Proof - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - metis_tac[FUNPOW_SUC, BIJ_ELEMENT] -QED - -(* Theorem: f PERMUTES s ==> FUNPOW (LINV f s) n PERMUTES s *) -(* Proof: by LINV_permutes, FUNPOW_permutes *) -Theorem FUNPOW_LINV_permutes: - !f s n. f PERMUTES s ==> FUNPOW (LINV f s) n PERMUTES s -Proof - simp[LINV_permutes, FUNPOW_permutes] -QED - -(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s *) -(* Proof: - By induction on n. - Base: FUNPOW (LINV f s) 0 x IN s - Since FUNPOW (LINV f s) 0 x = x by FUNPOW_0 - This is trivially true. - Step: FUNPOW (LINV f s) n x IN s ==> FUNPOW (LINV f s) (SUC n) x IN s - FUNPOW (LINV f s) (SUC n) x - = (LINV f s) (FUNPOW (LINV f s) n x) by FUNPOW_SUC - But FUNPOW (LINV f s) n x IN s by induction hypothesis - and (LINV f s) PERMUTES s by LINV_permutes - so (LINV f s) (FUNPOW (LINV f s) n x) IN s - by BIJ_ELEMENT -*) -Theorem FUNPOW_LINV_closure: - !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n x IN s -Proof - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - `(LINV f s) PERMUTES s` by rw[LINV_permutes] >> - prove_tac[FUNPOW_SUC, BIJ_ELEMENT] -QED - -(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW f n (FUNPOW (LINV f s) n x) = x *) -(* Proof: - By induction on n. - Base: FUNPOW f 0 (FUNPOW (LINV f s) 0 x) = x - FUNPOW f 0 (FUNPOW (LINV f s) 0 x) - = FUNPOW f 0 x by FUNPOW_0 - = x by FUNPOW_0 - Step: FUNPOW f n (FUNPOW (LINV f s) n x) = x ==> - FUNPOW f (SUC n) (FUNPOW (LINV f s) (SUC n) x) = x - Note (FUNPOW (LINV f s) n x) IN s by FUNPOW_LINV_closure - FUNPOW f (SUC n) (FUNPOW (LINV f s) (SUC n) x) - = FUNPOW f (SUC n) ((LINV f s) (FUNPOW (LINV f s) n x)) by FUNPOW_SUC - = FUNPOW f n (f ((LINV f s) (FUNPOW (LINV f s) n x))) by FUNPOW - = FUNPOW f n (FUNPOW (LINV f s) n x) by BIJ_LINV_THM - = x by induction hypothesis -*) -Theorem FUNPOW_LINV_EQ: - !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW f n (FUNPOW (LINV f s) n x) = x -Proof - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - `FUNPOW f (SUC n) (FUNPOW (LINV f s) (SUC n) x) - = FUNPOW f (SUC n) ((LINV f s) (FUNPOW (LINV f s) n x))` by rw[FUNPOW_SUC] >> - `_ = FUNPOW f n (f ((LINV f s) (FUNPOW (LINV f s) n x)))` by rw[FUNPOW] >> - `_ = FUNPOW f n (FUNPOW (LINV f s) n x)` by metis_tac[BIJ_LINV_THM, FUNPOW_LINV_closure] >> - simp[] -QED - -(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n (FUNPOW f n x) = x *) -(* Proof: - By induction on n. - Base: FUNPOW (LINV f s) 0 (FUNPOW f 0 x) = x - FUNPOW (LINV f s) 0 (FUNPOW f 0 x) - = FUNPOW (LINV f s) 0 x by FUNPOW_0 - = x by FUNPOW_0 - Step: FUNPOW (LINV f s) n (FUNPOW f n x) = x ==> - FUNPOW (LINV f s) (SUC n) (FUNPOW f (SUC n) x) = x - Note (FUNPOW f n x) IN s by FUNPOW_closure - FUNPOW (LINV f s) (SUC n) (FUNPOW f (SUC n) x) - = FUNPOW (LINV f s) (SUC n) (f (FUNPOW f n x)) by FUNPOW_SUC - = FUNPOW (LINV f s) n ((LINV f s) (f (FUNPOW f n x))) by FUNPOW - = FUNPOW (LINV f s) n (FUNPOW f n x) by BIJ_LINV_THM - = x by induction hypothesis -*) -Theorem FUNPOW_EQ_LINV: - !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n (FUNPOW f n x) = x -Proof - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - `FUNPOW (LINV f s) (SUC n) (FUNPOW f (SUC n) x) - = FUNPOW (LINV f s) (SUC n) (f (FUNPOW f n x))` by rw[FUNPOW_SUC] >> - `_ = FUNPOW (LINV f s) n ((LINV f s) (f (FUNPOW f n x)))` by rw[FUNPOW] >> - `_ = FUNPOW (LINV f s) n (FUNPOW f n x)` by metis_tac[BIJ_LINV_THM, FUNPOW_closure] >> - simp[] -QED - -(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW f (n - m) x = FUNPOW f n (FUNPOW (LINV f s) m x) *) -(* Proof: - FUNPOW f n (FUNPOW (LINV f s) m x) - = FUNPOW f (n - m + m) (FUNPOW (LINV f s) m x) by SUB_ADD, m <= n - = FUNPOW f (n - m) (FUNPOW f m (FUNPOW (LINV f s) m x)) by FUNPOW_ADD - = FUNPOW f (n - m) x by FUNPOW_LINV_EQ -*) -Theorem FUNPOW_SUB_LINV1: - !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW f (n - m) x = FUNPOW f n (FUNPOW (LINV f s) m x) -Proof - rpt strip_tac >> - `FUNPOW f n (FUNPOW (LINV f s) m x) - = FUNPOW f (n - m + m) (FUNPOW (LINV f s) m x)` by simp[] >> - `_ = FUNPOW f (n - m) (FUNPOW f m (FUNPOW (LINV f s) m x))` by rw[FUNPOW_ADD] >> - `_ = FUNPOW f (n - m) x` by rw[FUNPOW_LINV_EQ] >> - simp[] -QED - -(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW f (n - m) x = FUNPOW (LINV f s) m (FUNPOW f n x) *) -(* Proof: - Note FUNPOW f (n - m) x IN s by FUNPOW_closure - FUNPOW (LINV f s) m (FUNPOW f n x) - = FUNPOW (LINV f s) m (FUNPOW f (n - m + m) x) by SUB_ADD, m <= n - = FUNPOW (LINV f s) m (FUNPOW f (m + (n - m)) x) by ADD_COMM - = FUNPOW (LINV f s) m (FUNPOW f m (FUNPOW f (n - m) x)) by FUNPOW_ADD - = FUNPOW f (n - m) x by FUNPOW_EQ_LINV -*) -Theorem FUNPOW_SUB_LINV2: - !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW f (n - m) x = FUNPOW (LINV f s) m (FUNPOW f n x) -Proof - rpt strip_tac >> - `FUNPOW (LINV f s) m (FUNPOW f n x) - = FUNPOW (LINV f s) m (FUNPOW f (n - m + m) x)` by simp[] >> - `_ = FUNPOW (LINV f s) m (FUNPOW f (m + (n - m)) x)` by metis_tac[ADD_COMM] >> - `_ = FUNPOW (LINV f s) m (FUNPOW f m (FUNPOW f (n - m) x))` by rw[FUNPOW_ADD] >> - `_ = FUNPOW f (n - m) x` by rw[FUNPOW_EQ_LINV, FUNPOW_closure] >> - simp[] -QED - -(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW (LINV f s) (n - m) x = FUNPOW (LINV f s) n (FUNPOW f m x) *) -(* Proof: - FUNPOW (LINV f s) n (FUNPOW f m x) - = FUNPOW (LINV f s) (n - m + m) (FUNPOW f m x) by SUB_ADD, m <= n - = FUNPOW (LINV f s) (n - m) (FUNPOW (LINV f s) m (FUNPOW f m x)) by FUNPOW_ADD - = FUNPOW (LINV f s) (n - m) x by FUNPOW_EQ_LINV -*) -Theorem FUNPOW_LINV_SUB1: - !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW (LINV f s) (n - m) x = FUNPOW (LINV f s) n (FUNPOW f m x) -Proof - rpt strip_tac >> - `FUNPOW (LINV f s) n (FUNPOW f m x) - = FUNPOW (LINV f s) (n - m + m) (FUNPOW f m x)` by simp[] >> - `_ = FUNPOW (LINV f s) (n - m) (FUNPOW (LINV f s) m (FUNPOW f m x))` by rw[FUNPOW_ADD] >> - `_ = FUNPOW (LINV f s) (n - m) x` by rw[FUNPOW_EQ_LINV] >> - simp[] -QED - -(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW (LINV f s) (n - m) x = FUNPOW f m (FUNPOW (LINV f s) n x) *) -(* Proof: - Note FUNPOW (LINV f s) (n - m) x IN s by FUNPOW_LINV_closure - FUNPOW f m (FUNPOW (LINV f s) n x) - = FUNPOW f m (FUNPOW (LINV f s) (n - m + m) x) by SUB_ADD, m <= n - = FUNPOW f m (FUNPOW (LINV f s) (m + (n - m)) x) by ADD_COMM - = FUNPOW f m (FUNPOW (LINV f s) m (FUNPOW (LINV f s) (n - m) x)) by FUNPOW_ADD - = FUNPOW (LINV f s) (n - m) x by FUNPOW_LINV_EQ -*) -Theorem FUNPOW_LINV_SUB2: - !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> - FUNPOW (LINV f s) (n - m) x = FUNPOW f m (FUNPOW (LINV f s) n x) -Proof - rpt strip_tac >> - `FUNPOW f m (FUNPOW (LINV f s) n x) - = FUNPOW f m (FUNPOW (LINV f s) (n - m + m) x)` by simp[] >> - `_ = FUNPOW f m (FUNPOW (LINV f s) (m + (n - m)) x)` by metis_tac[ADD_COMM] >> - `_ = FUNPOW f m (FUNPOW (LINV f s) m (FUNPOW (LINV f s) (n - m) x))` by rw[FUNPOW_ADD] >> - `_ = FUNPOW (LINV f s) (n - m) x` by rw[FUNPOW_LINV_EQ, FUNPOW_LINV_closure] >> - simp[] -QED - -(* Theorem: f PERMUTES s /\ x IN s /\ y IN s ==> - (x = FUNPOW f n y <=> y = FUNPOW (LINV f s) n x) *) -(* Proof: - If part: x = FUNPOW f n y ==> y = FUNPOW (LINV f s) n x) - FUNPOW (LINV f s) n x) - = FUNPOW (LINV f s) n (FUNPOW f n y)) by x = FUNPOW f n y - = y by FUNPOW_EQ_LINV - Only-if part: y = FUNPOW (LINV f s) n x) ==> x = FUNPOW f n y - FUNPOW f n y - = FUNPOW f n (FUNPOW (LINV f s) n x)) by y = FUNPOW (LINV f s) n x) - = x by FUNPOW_LINV_EQ -*) -Theorem FUNPOW_LINV_INV: - !f s x y n. f PERMUTES s /\ x IN s /\ y IN s ==> - (x = FUNPOW f n y <=> y = FUNPOW (LINV f s) n x) -Proof - rw[EQ_IMP_THM] >- - rw[FUNPOW_EQ_LINV] >> - rw[FUNPOW_LINV_EQ] -QED - -(* ------------------------------------------------------------------------- *) -(* FUNPOW with incremental cons. *) -(* ------------------------------------------------------------------------- *) - -(* Note from HelperList: m downto n = REVERSE [m .. n] *) - -(* Idea: when applying incremental cons (f head) to a list for n times, - head of the result is f^n (head of list). *) - -(* Theorem: HD (FUNPOW (\ls. f (HD ls)::ls) n ls) = FUNPOW f n (HD ls) *) -(* Proof: - Let h = (\ls. f (HD ls)::ls). - By induction on n. - Base: !ls. HD (FUNPOW h 0 ls) = FUNPOW f 0 (HD ls) - HD (FUNPOW h 0 ls) - = HD ls by FUNPOW_0 - = FUNPOW f 0 (HD ls) by FUNPOW_0 - Step: !ls. HD (FUNPOW h n ls) = FUNPOW f n (HD ls) ==> - !ls. HD (FUNPOW h (SUC n) ls) = FUNPOW f (SUC n) (HD ls) - HD (FUNPOW h (SUC n) ls) - = HD (FUNPOW h n (h ls)) by FUNPOW - = FUNPOW f n (HD (h ls)) by induction hypothesis - = FUNPOW f n (f (HD ls)) by definition of h - = FUNPOW f (SUC n) (HD ls) by FUNPOW -*) -Theorem FUNPOW_cons_head: - !f n ls. HD (FUNPOW (\ls. f (HD ls)::ls) n ls) = FUNPOW f n (HD ls) -Proof - strip_tac >> - qabbrev_tac `h = \ls. f (HD ls)::ls` >> - Induct >- - simp[] >> - rw[FUNPOW, Abbr`h`] -QED - -(* Idea: when applying incremental cons (f head) to a singleton [u] for n times, - the result is the list [f^n(u), .... f(u), u]. *) - -(* Theorem: FUNPOW (\ls. f (HD ls)::ls) n [u] = - MAP (\j. FUNPOW f j u) (n downto 0) *) -(* Proof: - Let g = (\ls. f (HD ls)::ls), - h = (\j. FUNPOW f j u). - By induction on n. - Base: FUNPOW g 0 [u] = MAP h (0 downto 0) - FUNPOW g 0 [u] - = [u] by FUNPOW_0 - = [FUNPOW f 0 u] by FUNPOW_0 - = MAP h [0] by MAP - = MAP h (0 downto 0) by REVERSE - Step: FUNPOW g n [u] = MAP h (n downto 0) ==> - FUNPOW g (SUC n) [u] = MAP h (SUC n downto 0) - FUNPOW g (SUC n) [u] - = g (FUNPOW g n [u]) by FUNPOW_SUC - = g (MAP h (n downto 0)) by induction hypothesis - = f (HD (MAP h (n downto 0))) :: - MAP h (n downto 0) by definition of g - Now f (HD (MAP h (n downto 0))) - = f (HD (MAP h (MAP (\x. n - x) [0 .. n]))) by listRangeINC_REVERSE - = f (HD (MAP h o (\x. n - x) [0 .. n])) by MAP_COMPOSE - = f ((h o (\x. n - x)) 0) by MAP - = f (h n) - = f (FUNPOW f n u) by definition of h - = FUNPOW (n + 1) u by FUNPOW_SUC - = h (n + 1) by definition of h - so h (n + 1) :: MAP h (n downto 0) - = MAP h ((n + 1) :: (n downto 0)) by MAP - = MAP h (REVERSE (SNOC (n+1) [0 .. n])) by REVERSE_SNOC - = MAP h (SUC n downto 0) by listRangeINC_SNOC -*) -Theorem FUNPOW_cons_eq_map_0: - !f u n. FUNPOW (\ls. f (HD ls)::ls) n [u] = - MAP (\j. FUNPOW f j u) (n downto 0) -Proof - ntac 2 strip_tac >> - Induct >- - rw[] >> - qabbrev_tac `g = \ls. f (HD ls)::ls` >> - qabbrev_tac `h = \j. FUNPOW f j u` >> - rw[] >> - `f (HD (MAP h (n downto 0))) = h (n + 1)` by - (`[0 .. n] = 0 :: [1 .. n]` by rw[listRangeINC_CONS] >> - fs[listRangeINC_REVERSE, MAP_COMPOSE, GSYM FUNPOW_SUC, ADD1, Abbr`h`]) >> - `FUNPOW g (SUC n) [u] = g (FUNPOW g n [u])` by rw[FUNPOW_SUC] >> - `_ = g (MAP h (n downto 0))` by fs[] >> - `_ = h (n + 1) :: MAP h (n downto 0)` by rw[Abbr`g`] >> - `_ = MAP h ((n + 1) :: (n downto 0))` by rw[] >> - `_ = MAP h (REVERSE (SNOC (n+1) [0 .. n]))` by rw[REVERSE_SNOC] >> - rw[listRangeINC_SNOC, ADD1] -QED - -(* Idea: when applying incremental cons (f head) to a singleton [f(u)] for (n-1) times, - the result is the list [f^n(u), .... f(u)]. *) - -(* Theorem: 0 < n ==> (FUNPOW (\ls. f (HD ls)::ls) (n - 1) [f u] = - MAP (\j. FUNPOW f j u) (n downto 1)) *) -(* Proof: - Let g = (\ls. f (HD ls)::ls), - h = (\j. FUNPOW f j u). - By induction on n. - Base: FUNPOW g 0 [f u] = MAP h (REVERSE [1 .. 1]) - FUNPOW g 0 [f u] - = [f u] by FUNPOW_0 - = [FUNPOW f 1 u] by FUNPOW_1 - = MAP h [1] by MAP - = MAP h (REVERSE [1 .. 1]) by REVERSE - Step: 0 < n ==> FUNPOW g (n-1) [f u] = MAP h (n downto 1) ==> - FUNPOW g n [f u] = MAP h (REVERSE [1 .. SUC n]) - The case n = 0 is the base case. For n <> 0, - FUNPOW g n [f u] - = g (FUNPOW g (n-1) [f u]) by FUNPOW_SUC - = g (MAP h (n downto 1)) by induction hypothesis - = f (HD (MAP h (n downto 1))) :: - MAP h (n downto 1) by definition of g - Now f (HD (MAP h (n downto 1))) - = f (HD (MAP h (MAP (\x. n + 1 - x) [1 .. n]))) by listRangeINC_REVERSE - = f (HD (MAP h o (\x. n + 1 - x) [1 .. n])) by MAP_COMPOSE - = f ((h o (\x. n + 1 - x)) 1) by MAP - = f (h n) - = f (FUNPOW f n u) by definition of h - = FUNPOW (n + 1) u by FUNPOW_SUC - = h (n + 1) by definition of h - so h (n + 1) :: MAP h (n downto 1) - = MAP h ((n + 1) :: (n downto 1)) by MAP - = MAP h (REVERSE (SNOC (n+1) [1 .. n])) by REVERSE_SNOC - = MAP h (REVERSE [1 .. SUC n]) by listRangeINC_SNOC -*) -Theorem FUNPOW_cons_eq_map_1: - !f u n. 0 < n ==> (FUNPOW (\ls. f (HD ls)::ls) (n - 1) [f u] = - MAP (\j. FUNPOW f j u) (n downto 1)) -Proof - ntac 2 strip_tac >> - Induct >- - simp[] >> - rw[] >> - qabbrev_tac `g = \ls. f (HD ls)::ls` >> - qabbrev_tac `h = \j. FUNPOW f j u` >> - Cases_on `n = 0` >- - rw[Abbr`g`, Abbr`h`] >> - `f (HD (MAP h (n downto 1))) = h (n + 1)` by - (`[1 .. n] = 1 :: [2 .. n]` by rw[listRangeINC_CONS] >> - fs[listRangeINC_REVERSE, MAP_COMPOSE, GSYM FUNPOW_SUC, ADD1, Abbr`h`]) >> - `n = SUC (n-1)` by decide_tac >> - `FUNPOW g n [f u] = g (FUNPOW g (n - 1) [f u])` by metis_tac[FUNPOW_SUC] >> - `_ = g (MAP h (n downto 1))` by fs[] >> - `_ = h (n + 1) :: MAP h (n downto 1)` by rw[Abbr`g`] >> - `_ = MAP h ((n + 1) :: (n downto 1))` by rw[] >> - `_ = MAP h (REVERSE (SNOC (n+1) [1 .. n]))` by rw[REVERSE_SNOC] >> - rw[listRangeINC_SNOC, ADD1] -QED - -(* ------------------------------------------------------------------------- *) -(* Integer Functions. *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Factorial *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: FACT 0 = 1 *) -(* Proof: by FACT *) -val FACT_0 = store_thm( - "FACT_0", - ``FACT 0 = 1``, - EVAL_TAC); - -(* Theorem: FACT 1 = 1 *) -(* Proof: - FACT 1 - = FACT (SUC 0) by ONE - = (SUC 0) * FACT 0 by FACT - = (SUC 0) * 1 by FACT - = 1 by ONE -*) -val FACT_1 = store_thm( - "FACT_1", - ``FACT 1 = 1``, - EVAL_TAC); - -(* Theorem: FACT 2 = 2 *) -(* Proof: - FACT 2 - = FACT (SUC 1) by TWO - = (SUC 1) * FACT 1 by FACT - = (SUC 1) * 1 by FACT_1 - = 2 by TWO -*) -val FACT_2 = store_thm( - "FACT_2", - ``FACT 2 = 2``, - EVAL_TAC); - -(* Theorem: (FACT n = 1) <=> n <= 1 *) -(* Proof: - If n = 0, - LHS = (FACT 0 = 1) = T by FACT_0 - RHS = 0 <= 1 = T by arithmetic - If n <> 0, n = SUC m by num_CASES - LHS = FACT (SUC m) = 1 - <=> (SUC m) * FACT m = 1 by FACT - <=> SUC m = 1 /\ FACT m = 1 by MULT_EQ_1 - <=> m = 0 /\ FACT m = 1 by m = PRE 1 = 0 - <=> m = 0 by FACT_0 - RHS = SUC m <= 1 - <=> ~(1 <= m) by NOT_LEQ - <=> m < 1 by NOT_LESS_EQUAL - <=> m = 0 by arithmetic -*) -val FACT_EQ_1 = store_thm( - "FACT_EQ_1", - ``!n. (FACT n = 1) <=> n <= 1``, - rpt strip_tac >> - Cases_on `n` >> - rw[FACT_0] >> - rw[FACT] >> - `!m. SUC m <= 1 <=> (m = 0)` by decide_tac >> - metis_tac[FACT_0]); - -(* Theorem: 1 <= FACT n *) -(* Proof: - Note 0 < FACT n by FACT_LESS - so 1 <= FACT n by arithmetic -*) -val FACT_GE_1 = store_thm( - "FACT_GE_1", - ``!n. 1 <= FACT n``, - metis_tac[FACT_LESS, LESS_OR, ONE]); - -(* Theorem: (FACT n = n) <=> (n = 1) \/ (n = 2) *) -(* Proof: - If part: (FACT n = n) ==> (n = 1) \/ (n = 2) - Note n <> 0 by FACT_0: FACT 0 = 1 - ==> ?m. n = SUC m by num_CASES - Thus SUC m * FACT m = SUC m by FACT - = SUC m * 1 by MULT_RIGHT_1 - ==> FACT m = 1 by EQ_MULT_LCANCEL, SUC_NOT - or m <= 1 by FACT_EQ_1 - Thus m = 0 or 1 by arithmetic - or n = 1 or 2 by ONE, TWO - - Only-if part: (FACT 1 = 1) /\ (FACT 2 = 2) - Note FACT 1 = 1 by FACT_1 - and FACT 2 = 2 by FACT_2 -*) -val FACT_EQ_SELF = store_thm( - "FACT_EQ_SELF", - ``!n. (FACT n = n) <=> (n = 1) \/ (n = 2)``, - rw[EQ_IMP_THM] >| [ - `n <> 0` by metis_tac[FACT_0, DECIDE``1 <> 0``] >> - `?m. n = SUC m` by metis_tac[num_CASES] >> - fs[FACT] >> - `FACT m = 1` by metis_tac[MULT_LEFT_1, EQ_MULT_RCANCEL, SUC_NOT] >> - `m <= 1` by rw[GSYM FACT_EQ_1] >> - decide_tac, - rw[FACT_1], - rw[FACT_2] - ]); - -(* Theorem: 0 < n ==> n <= FACT n *) -(* Proof: - Note n <> 0 by 0 < n - ==> ?m. n = SUC m by num_CASES - Thus FACT n - = FACT (SUC m) by n = SUC m - = (SUC m) * FACT m by FACT_LESS: 0 < FACT m - >= (SUC m) by LE_MULT_CANCEL_LBARE - >= n by n = SUC m -*) -val FACT_GE_SELF = store_thm( - "FACT_GE_SELF", - ``!n. 0 < n ==> n <= FACT n``, - rpt strip_tac >> - `?m. n = SUC m` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> - rw[FACT] >> - rw[FACT_LESS]); - -(* Theorem: 0 < n ==> (FACT (n-1) = FACT n DIV n) *) -(* Proof: - Since n = SUC(n-1) by SUC_PRE, 0 < n. - and FACT n = n * FACT (n-1) by FACT - = FACT (n-1) * n by MULT_COMM - = FACT (n-1) * n + 0 by ADD_0 - Hence FACT (n-1) = FACT n DIV n by DIV_UNIQUE, 0 < n. -*) -val FACT_DIV = store_thm( - "FACT_DIV", - ``!n. 0 < n ==> (FACT (n-1) = FACT n DIV n)``, - rpt strip_tac >> - `n = SUC(n-1)` by decide_tac >> - `FACT n = n * FACT (n-1)` by metis_tac[FACT] >> - `_ = FACT (n-1) * n + 0` by rw[MULT_COMM] >> - metis_tac[DIV_UNIQUE]); - -(* Theorem: n! = PROD_SET (count (n+1)) *) -(* Proof: by induction on n. - Base case: FACT 0 = PROD_SET (IMAGE SUC (count 0)) - LHS = FACT 0 - = 1 by FACT - = PROD_SET {} by PROD_SET_THM - = PROD_SET (IMAGE SUC {}) by IMAGE_EMPTY - = PROD_SET (IMAGE SUC (count 0)) by COUNT_ZERO - = RHS - Step case: FACT n = PROD_SET (IMAGE SUC (count n)) ==> - FACT (SUC n) = PROD_SET (IMAGE SUC (count (SUC n))) - Note: (SUC n) NOTIN (IMAGE SUC (count n)) by IN_IMAGE, IN_COUNT [1] - LHS = FACT (SUC n) - = (SUC n) * (FACT n) by FACT - = (SUC n) * (PROD_SET (IMAGE SUC (count n))) by induction hypothesis - = (SUC n) * (PROD_SET (IMAGE SUC (count n)) DELETE (SUC n)) by DELETE_NON_ELEMENT, [1] - = PROD_SET ((SUC n) INSERT ((IMAGE SUC (count n)) DELETE (SUC n))) by PROD_SET_THM - = PROD_SET (IMAGE SUC (n INSERT (count n))) by IMAGE_INSERT - = PROD_SET (IMAGE SUC (count (SUC n))) by COUNT_SUC - = RHS -*) -val FACT_EQ_PROD = store_thm( - "FACT_EQ_PROD", - ``!n. FACT n = PROD_SET (IMAGE SUC (count n))``, - Induct_on `n` >- - rw[PROD_SET_THM, FACT] >> - rw[PROD_SET_THM, FACT, COUNT_SUC] >> - `(SUC n) NOTIN (IMAGE SUC (count n))` by rw[] >> - metis_tac[DELETE_NON_ELEMENT]); - -(* Theorem: n!/m! = product of (m+1) to n. - m < n ==> (FACT n = PROD_SET (IMAGE SUC ((count n) DIFF (count m))) * (FACT m)) *) -(* Proof: by factorial formula. - By induction on n. - Base case: m < 0 ==> ... - True since m < 0 = F. - Step case: !m. m < n ==> - (FACT n = PROD_SET (IMAGE SUC (count n DIFF count m)) * FACT m) ==> - !m. m < SUC n ==> - (FACT (SUC n) = PROD_SET (IMAGE SUC (count (SUC n) DIFF count m)) * FACT m) - Note that m < SUC n ==> m <= n. - and FACT (SUC n) = (SUC n) * FACT n by FACT - If m = n, - PROD_SET (IMAGE SUC (count (SUC n) DIFF count n)) * FACT n - = PROD_SET (IMAGE SUC {n}) * FACT n by IN_DIFF, IN_COUNT - = PROD_SET {SUC n} * FACT n by IN_IMAGE - = (SUC n) * FACT n by PROD_SET_THM - If m < n, - n NOTIN (count m) by IN_COUNT - so n INSERT ((count n) DIFF (count m)) - = (n INSERT (count n)) DIFF (count m) by INSERT_DIFF - = count (SUC n) DIFF (count m) by EXTENSION - Since (SUC n) NOTIN (IMAGE SUC ((count n) DIFF (count m))) by IN_IMAGE, IN_DIFF, IN_COUNT - and FINITE (IMAGE SUC ((count n) DIFF (count m))) by IMAGE_FINITE, FINITE_DIFF, FINITE_COUNT - Hence PROD_SET (IMAGE SUC (count (SUC n) DIFF count m)) * FACT m - = ((SUC n) * PROD_SET (IMAGE SUC (count n DIFF count m))) * FACT m by PROD_SET_IMAGE_REDUCTION - = (SUC n) * (PROD_SET (IMAGE SUC (count n DIFF count m))) * FACT m) by MULT_ASSOC - = (SUC n) * FACT n by induction hypothesis - = FACT (SUC n) by FACT -*) -val FACT_REDUCTION = store_thm( - "FACT_REDUCTION", - ``!n m. m < n ==> (FACT n = PROD_SET (IMAGE SUC ((count n) DIFF (count m))) * (FACT m))``, - Induct_on `n` >- - rw[] >> - rw_tac std_ss[FACT] >> - `m <= n` by decide_tac >> - Cases_on `m = n` >| [ - rw_tac std_ss[] >> - `count (SUC m) DIFF count m = {m}` by - (rw[DIFF_DEF] >> - rw[EXTENSION, EQ_IMP_THM]) >> - `PROD_SET (IMAGE SUC {m}) = SUC m` by rw[PROD_SET_THM] >> - metis_tac[], - `m < n` by decide_tac >> - `n NOTIN (count m)` by srw_tac[ARITH_ss][] >> - `n INSERT ((count n) DIFF (count m)) = (n INSERT (count n)) DIFF (count m)` by rw[] >> - `_ = count (SUC n) DIFF (count m)` by srw_tac[ARITH_ss][EXTENSION] >> - `(SUC n) NOTIN (IMAGE SUC ((count n) DIFF (count m)))` by rw[] >> - `FINITE (IMAGE SUC ((count n) DIFF (count m)))` by rw[] >> - metis_tac[PROD_SET_IMAGE_REDUCTION, MULT_ASSOC] - ]); - -(* Theorem: prime p ==> p cannot divide k! for p > k. - prime p /\ k < p ==> ~(p divides (FACT k)) *) -(* Proof: - Since all terms of k! are less than p, and p has only 1 and p as factor. - By contradiction, and induction on k. - Base case: prime p ==> 0 < p ==> p divides (FACT 0) ==> F - Since FACT 0 = 1 by FACT - and p divides 1 <=> p = 1 by DIVIDES_ONE - but prime p ==> 1 < p by ONE_LT_PRIME - so this is a contradiction. - Step case: prime p /\ k < p ==> p divides (FACT k) ==> F ==> - SUC k < p ==> p divides (FACT (SUC k)) ==> F - Since FACT (SUC k) = SUC k * FACT k by FACT - and prime p /\ p divides (FACT (SUC k)) - ==> p divides (SUC k), - or p divides (FACT k) by P_EUCLIDES - But SUC k < p, so ~(p divides (SUC k)) by NOT_LT_DIVIDES - Hence p divides (FACT k) ==> F by induction hypothesis -*) -val PRIME_BIG_NOT_DIVIDES_FACT = store_thm( - "PRIME_BIG_NOT_DIVIDES_FACT", - ``!p k. prime p /\ k < p ==> ~(p divides (FACT k))``, - (spose_not_then strip_assume_tac) >> - Induct_on `k` >| [ - rw[FACT] >> - metis_tac[ONE_LT_PRIME, LESS_NOT_EQ], - rw[FACT] >> - (spose_not_then strip_assume_tac) >> - `k < p /\ 0 < SUC k` by decide_tac >> - metis_tac[P_EUCLIDES, NOT_LT_DIVIDES] - ]); - -(* Idea: test if a function f is factorial. *) - -(* Theorem: f = FACT <=> f 0 = 1 /\ !n. f (SUC n) = SUC n * f n *) -(* Proof: - If part is true by FACT - Only-if part, apply FUN_EQ_THM, this is to show: - !n. f n = FACT n. - By induction on n. - Base: f 0 = FACT 0 - f 0 - = 1 by given - = FACT 0 by FACT_0 - Step: f n = FACT n ==> f (SUC n) = FACT (SUC n) - f (SUC n) - = SUC n * f n by given - = SUC n * FACT n by induction hypothesis - = FACT (SUC n) by FACT -*) -Theorem FACT_iff: - !f. f = FACT <=> f 0 = 1 /\ !n. f (SUC n) = SUC n * f n -Proof - rw[FACT, EQ_IMP_THM] >> - rw[FUN_EQ_THM] >> - Induct_on `x` >> - simp[FACT] -QED - -(* ------------------------------------------------------------------------- *) -(* Basic GCD, LCM Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Note: gcd Theory has: GCD_SYM |- !a b. gcd a b = gcd b a - but: LCM_COMM |- lcm a b = lcm b a -*) -val GCD_COMM = save_thm("GCD_COMM", GCD_SYM); -(* val GCD_COMM = |- !a b. gcd a b = gcd b a: thm *) -val LCM_SYM = save_thm("LCM_SYM", LCM_COMM |> GEN ``b:num`` |> GEN ``a:num``); -(* val val LCM_SYM = |- !a b. lcm a b = lcm b a: thm *) - -(* Note: -gcdTheory.LCM_0 |- (lcm 0 x = 0) /\ (lcm x 0 = 0) -gcdTheory.LCM_1 |- (lcm 1 x = x) /\ (lcm x 1 = x) -gcdTheory.GCD_1 |- coprime 1 x /\ coprime x 1 -but only GCD_0L, GCD_0R -gcdTheory.GCD_EQ_0 |- !n m. (gcd n m = 0) <=> (n = 0) /\ (m = 0) -*) - -(* Theorem: (gcd 0 x = x) /\ (gcd x 0 = x) *) -(* Proof: by GCD_0L, GCD_0R *) -val GCD_0 = store_thm( - "GCD_0", - ``!x. (gcd 0 x = x) /\ (gcd x 0 = x)``, - rw_tac std_ss[GCD_0L, GCD_0R]); - -(* Theorem: gcd(n, m) = 1 ==> n divides (c * m) ==> n divides c *) -(* Proof: - This is L_EUCLIDES: (Euclid's Lemma) -> val it = |- !a b c. coprime a b /\ divides b (a * c) ==> b divides c : thm -*) - -(* Theorem: If 0 < n, 0 < m, let g = gcd n m, then 0 < g and n MOD g = 0 and m MOD g = 0 *) -(* Proof: - 0 < n ==> n <> 0, 0 < m ==> m <> 0, by NOT_ZERO_LT_ZERO - hence g = gcd n m <> 0, or 0 < g. by GCD_EQ_0 - g = gcd n m ==> (g divides n) /\ (g divides m) by GCD_IS_GCD, is_gcd_def - ==> (n MOD g = 0) /\ (m MOD g = 0) by DIVIDES_MOD_0 -*) -val GCD_DIVIDES = store_thm( - "GCD_DIVIDES", - ``!m n. 0 < n /\ 0 < m ==> 0 < (gcd n m) /\ (n MOD (gcd n m) = 0) /\ (m MOD (gcd n m) = 0)``, - ntac 3 strip_tac >> - conj_asm1_tac >- - metis_tac[GCD_EQ_0, NOT_ZERO_LT_ZERO] >> - metis_tac[GCD_IS_GCD, is_gcd_def, DIVIDES_MOD_0]); - -(* Theorem: gcd n (gcd n m) = gcd n m *) -(* Proof: - If n = 0, - gcd 0 (gcd n m) = gcd n m by GCD_0L - If m = 0, - gcd n (gcd n 0) - = gcd n n by GCD_0R - = n = gcd n 0 by GCD_REF - If n <> 0, m <> 0, d <> 0 by GCD_EQ_0 - Since d divides n, n MOD d = 0 - gcd n d - = gcd d n by GCD_SYM - = gcd (n MOD d) d by GCD_EFFICIENTLY, d <> 0 - = gcd 0 d by GCD_DIVIDES - = d by GCD_0L -*) -val GCD_GCD = store_thm( - "GCD_GCD", - ``!m n. gcd n (gcd n m) = gcd n m``, - rpt strip_tac >> - Cases_on `n = 0` >- rw[] >> - Cases_on `m = 0` >- rw[] >> - `0 < n /\ 0 < m` by decide_tac >> - metis_tac[GCD_SYM, GCD_EFFICIENTLY, GCD_DIVIDES, GCD_EQ_0, GCD_0L]); - -(* Theorem: GCD m n * LCM m n = m * n *) -(* Proof: - By lcm_def: - lcm m n = if (m = 0) \/ (n = 0) then 0 else m * n DIV gcd m n - If m = 0, - gcd 0 n * lcm 0 n = n * 0 = 0 = 0 * n, hence true. - If n = 0, - gcd m 0 * lcm m 0 = m * 0 = 0 = m * 0, hence true. - If m <> 0, n <> 0, - gcd m n * lcm m n = gcd m n * (m * n DIV gcd m n) = m * n. -*) -val GCD_LCM = store_thm( - "GCD_LCM", - ``!m n. gcd m n * lcm m n = m * n``, - rw[lcm_def] >> - `0 < m /\ 0 < n` by decide_tac >> - `0 < gcd m n /\ (n MOD gcd m n = 0)` by rw[GCD_DIVIDES] >> - qabbrev_tac `d = gcd m n` >> - `m * n = (m * n) DIV d * d + (m * n) MOD d` by rw[DIVISION] >> - `(m * n) MOD d = 0` by metis_tac[MOD_TIMES2, ZERO_MOD, MULT_0] >> - metis_tac[ADD_0, MULT_COMM]); - -(* Theorem: m divides (lcm m n) /\ n divides (lcm m n) *) -(* Proof: by LCM_IS_LEAST_COMMON_MULTIPLE *) -val LCM_DIVISORS = store_thm( - "LCM_DIVISORS", - ``!m n. m divides (lcm m n) /\ n divides (lcm m n)``, - rw[LCM_IS_LEAST_COMMON_MULTIPLE]); - -(* Theorem: m divides p /\ n divides p ==> (lcm m n) divides p *) -(* Proof: by LCM_IS_LEAST_COMMON_MULTIPLE *) -val LCM_IS_LCM = store_thm( - "LCM_IS_LCM", - ``!m n p. m divides p /\ n divides p ==> (lcm m n) divides p``, - rw[LCM_IS_LEAST_COMMON_MULTIPLE]); - -(* Theorem: (lcm m n = 0) <=> ((m = 0) \/ (n = 0)) *) -(* Proof: - If part: lcm m n = 0 ==> m = 0 \/ n = 0 - By contradiction, suppse m = 0 /\ n = 0. - Then gcd m n = 0 by GCD_EQ_0 - and m * n = 0 by MULT_EQ_0 - but (gcd m n) * (lcm m n) = m * n by GCD_LCM - so RHS <> 0, but LHS = 0 by MULT_0 - This is a contradiction. - Only-if part: m = 0 \/ n = 0 ==> lcm m n = 0 - True by LCM_0 -*) -val LCM_EQ_0 = store_thm( - "LCM_EQ_0", - ``!m n. (lcm m n = 0) <=> ((m = 0) \/ (n = 0))``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `(gcd m n) * (lcm m n) = m * n` by rw[GCD_LCM] >> - `gcd m n <> 0` by rw[GCD_EQ_0] >> - `m * n <> 0` by rw[MULT_EQ_0] >> - metis_tac[MULT_0], - rw[LCM_0], - rw[LCM_0] - ]); - -(* Note: gcdTheory.GCD_REF |- !a. gcd a a = a *) - -(* Theorem: lcm a a = a *) -(* Proof: - If a = 0, - lcm 0 0 = 0 by LCM_0 - If a <> 0, - (gcd a a) * (lcm a a) = a * a by GCD_LCM - a * (lcm a a) = a * a by GCD_REF - lcm a a = a by MULT_LEFT_CANCEL, a <> 0 -*) -val LCM_REF = store_thm( - "LCM_REF", - ``!a. lcm a a = a``, - metis_tac[num_CASES, LCM_0, GCD_LCM, GCD_REF, MULT_LEFT_CANCEL]); - -(* Theorem: a divides n /\ b divides n ==> (lcm a b) divides n *) -(* Proof: same as LCM_IS_LCM *) -val LCM_DIVIDES = store_thm( - "LCM_DIVIDES", - ``!n a b. a divides n /\ b divides n ==> (lcm a b) divides n``, - rw[LCM_IS_LCM]); -(* -> LCM_IS_LCM |> ISPEC ``a:num`` |> ISPEC ``b:num`` |> ISPEC ``n:num`` |> GEN_ALL; -val it = |- !n b a. a divides n /\ b divides n ==> lcm a b divides n: thm -*) - -(* Theorem: 0 < m \/ 0 < n ==> 0 < gcd m n *) -(* Proof: by GCD_EQ_0, NOT_ZERO_LT_ZERO *) -val GCD_POS = store_thm( - "GCD_POS", - ``!m n. 0 < m \/ 0 < n ==> 0 < gcd m n``, - metis_tac[GCD_EQ_0, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < m /\ 0 < n ==> 0 < lcm m n *) -(* Proof: by LCM_EQ_0, NOT_ZERO_LT_ZERO *) -val LCM_POS = store_thm( - "LCM_POS", - ``!m n. 0 < m /\ 0 < n ==> 0 < lcm m n``, - metis_tac[LCM_EQ_0, NOT_ZERO_LT_ZERO]); - -(* Theorem: n divides m <=> gcd n m = n *) -(* Proof: - If part: - n divides m ==> ?k. m = k * n by divides_def - gcd n m - = gcd n (k * n) - = gcd (n * 1) (n * k) by MULT_RIGHT_1, MULT_COMM - = n * gcd 1 k by GCD_COMMON_FACTOR - = n * 1 by GCD_1 - = n by MULT_RIGHT_1 - Only-if part: gcd n m = n ==> n divides m - If n = 0, gcd 0 m = m by GCD_0L - But gcd n m = n = 0 by givien - hence m = 0, - and 0 divides 0 is true by DIVIDES_REFL - If n <> 0, - If m = 0, LHS true by GCD_0R - RHS true by ALL_DIVIDES_0 - If m <> 0, - then 0 < n and 0 < m, - gcd n m = gcd (m MOD n) n by GCD_EFFICIENTLY - if (m MOD n) = 0 - then n divides m by DIVIDES_MOD_0 - If (m MOD n) <> 0, - so (m MOD n) MOD (gcd n m) = 0 by GCD_DIVIDES - or (m MOD n) MOD n = 0 by gcd n m = n, given - or m MOD n = 0 by MOD_MOD - contradicting (m MOD n) <> 0, hence true. -*) -val divides_iff_gcd_fix = store_thm( - "divides_iff_gcd_fix", - ``!m n. n divides m <=> (gcd n m = n)``, - rw[EQ_IMP_THM] >| [ - `?k. m = k * n` by rw[GSYM divides_def] >> - `gcd n m = gcd (n * 1) (n * k)` by rw[MULT_COMM] >> - rw[GCD_COMMON_FACTOR, GCD_1], - Cases_on `n = 0` >- - metis_tac[GCD_0L, DIVIDES_REFL] >> - Cases_on `m = 0` >- - metis_tac[GCD_0R, ALL_DIVIDES_0] >> - `0 < n /\ 0 < m` by decide_tac >> - Cases_on `m MOD n = 0` >- - metis_tac[DIVIDES_MOD_0] >> - `0 < m MOD n` by decide_tac >> - metis_tac[GCD_EFFICIENTLY, GCD_DIVIDES, MOD_MOD] - ]); - -(* Theorem: !m n. n divides m <=> (lcm n m = m) *) -(* Proof: - If n = 0, - n divides m <=> m = 0 by ZERO_DIVIDES - and lcm 0 0 = 0 = m by LCM_0 - If n <> 0, - gcd n m * lcm n m = n * m by GCD_LCM - If part: n divides m ==> (lcm n m = m) - Then gcd n m = n by divides_iff_gcd_fix - so n * lcm n m = n * m by above - lcm n m = m by MULT_LEFT_CANCEL, n <> 0 - Only-if part: lcm n m = m ==> n divides m - If m = 0, n divdes 0 = true by ALL_DIVIDES_0 - If m <> 0, - Then gcd n m * m = n * m by above - or gcd n m = n by MULT_RIGHT_CANCEL, m <> 0 - so n divides m by divides_iff_gcd_fix -*) -val divides_iff_lcm_fix = store_thm( - "divides_iff_lcm_fix", - ``!m n. n divides m <=> (lcm n m = m)``, - rpt strip_tac >> - Cases_on `n = 0` >- - metis_tac[ZERO_DIVIDES, LCM_0] >> - metis_tac[GCD_LCM, MULT_LEFT_CANCEL, MULT_RIGHT_CANCEL, divides_iff_gcd_fix, ALL_DIVIDES_0]); - -(* Theorem: If prime p divides n, ?m. 0 < m /\ (p ** m) divides n /\ n DIV (p ** m) has no p *) -(* Proof: - Let s = {j | (p ** j) divides n } - Since p ** 1 = p, 1 IN s, so s <> {}. - (p ** j) divides n - ==> p ** j <= n by DIVIDES_LE - ==> p ** j <= p ** z by EXP_ALWAYS_BIG_ENOUGH - ==> j <= z by EXP_BASE_LE_MONO - ==> s SUBSET count (SUC z), - so FINITE s by FINITE_COUNT, SUBSET_FINITE - Let m = MAX_SET s, - m IN s, so (p ** m) divides n by MAX_SET_DEF - 1 <= m, or 0 < m. - ?q. n = q * (p ** m) by divides_def - To prove: !k. gcd (p ** k) (n DIV (p ** m)) = 1 - By contradiction, suppose there is a k such that - gcd (p ** k) (n DIV (p ** m)) <> 1 - So there is a prime pp that divides this gcd, by PRIME_FACTOR - but pp | p ** k, a pure prime, so pp = p by DIVIDES_EXP_BASE, prime_divides_only_self - pp | n DIV (p ** m) - or pp * p ** m | n - p * SUC m | n, making m not MAX_SET s. -*) -val FACTOR_OUT_PRIME = store_thm( - "FACTOR_OUT_PRIME", - ``!n p. 0 < n /\ prime p /\ p divides n ==> ?m. 0 < m /\ (p ** m) divides n /\ !k. gcd (p ** k) (n DIV (p ** m)) = 1``, - rpt strip_tac >> - qabbrev_tac `s = {j | (p ** j) divides n }` >> - `!j. j IN s <=> (p ** j) divides n` by rw[Abbr`s`] >> - `p ** 1 = p` by rw[] >> - `1 IN s` by metis_tac[] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `?z. n <= p ** z` by rw[EXP_ALWAYS_BIG_ENOUGH] >> - `!j. j IN s ==> p ** j <= n` by metis_tac[DIVIDES_LE] >> - `!j. j IN s ==> p ** j <= p ** z` by metis_tac[LESS_EQ_TRANS] >> - `!j. j IN s ==> j <= z` by metis_tac[EXP_BASE_LE_MONO] >> - `!j. j <= z <=> j < SUC z` by decide_tac >> - `!j. j < SUC z <=> j IN count (SUC z)` by rw[] >> - `s SUBSET count (SUC z)` by metis_tac[SUBSET_DEF] >> - `FINITE s` by metis_tac[FINITE_COUNT, SUBSET_FINITE] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - qabbrev_tac `m = MAX_SET s` >> - `m IN s /\ !y. y IN s ==> y <= m`by rw[MAX_SET_DEF, Abbr`m`] >> - qexists_tac `m` >> - CONJ_ASM1_TAC >| [ - `1 <= m` by metis_tac[] >> - decide_tac, - CONJ_ASM1_TAC >- - metis_tac[] >> - qabbrev_tac `pm = p ** m` >> - `0 < p` by decide_tac >> - `0 < pm` by rw[ZERO_LT_EXP, Abbr`pm`] >> - `n MOD pm = 0` by metis_tac[DIVIDES_MOD_0] >> - `n = n DIV pm * pm` by metis_tac[DIVISION, ADD_0] >> - qabbrev_tac `qm = n DIV pm` >> - spose_not_then strip_assume_tac >> - `?q. prime q /\ q divides (gcd (p ** k) qm)` by rw[PRIME_FACTOR] >> - `0 <> pm /\ n <> 0` by decide_tac >> - `qm <> 0` by metis_tac[MULT] >> - `0 < qm` by decide_tac >> - qabbrev_tac `pk = p ** k` >> - `0 < pk` by rw[ZERO_LT_EXP, Abbr`pk`] >> - `(gcd pk qm) divides pk /\ (gcd pk qm) divides qm` by metis_tac[GCD_DIVIDES, DIVIDES_MOD_0] >> - `q divides pk /\ q divides qm` by metis_tac[DIVIDES_TRANS] >> - `k <> 0` by metis_tac[EXP, GCD_1] >> - `0 < k` by decide_tac >> - `q divides p` by metis_tac[DIVIDES_EXP_BASE] >> - `q = p` by rw[prime_divides_only_self] >> - `?x. qm = x * q` by rw[GSYM divides_def] >> - `n = x * p * pm` by metis_tac[] >> - `_ = x * (p * pm)` by rw_tac arith_ss[] >> - `_ = x * (p ** SUC m)` by rw[EXP, Abbr`pm`] >> - `(p ** SUC m) divides n` by metis_tac[divides_def] >> - `SUC m <= m` by metis_tac[] >> - decide_tac - ]); - -(* ------------------------------------------------------------------------- *) -(* Consequences of Coprime. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: If 1 < n, !x. coprime n x ==> 0 < x /\ 0 < x MOD n *) -(* Proof: - If x = 0, gcd n x = n. But n <> 1, hence x <> 0, or 0 < x. - x MOD n = 0 ==> x a multiple of n ==> gcd n x = n <> 1 if n <> 1. - Hence if 1 < n, coprime n x ==> x MOD n <> 0, or 0 < x MOD n. -*) -val MOD_NONZERO_WHEN_GCD_ONE = store_thm( - "MOD_NONZERO_WHEN_GCD_ONE", - ``!n. 1 < n ==> !x. coprime n x ==> 0 < x /\ 0 < x MOD n``, - ntac 4 strip_tac >> - conj_asm1_tac >| [ - `1 <> n` by decide_tac >> - `x <> 0` by metis_tac[GCD_0R] >> - decide_tac, - `1 <> n /\ x <> 0` by decide_tac >> - `?k q. k * x = q * n + 1` by metis_tac[LINEAR_GCD] >> - `(k*x) MOD n = 1` by rw_tac std_ss[MOD_MULT] >> - spose_not_then strip_assume_tac >> - `(x MOD n = 0) /\ 0 < n /\ 1 <> 0` by decide_tac >> - metis_tac[MOD_MULITPLE_ZERO, MULT_COMM] - ]); - -(* Theorem: coprime n x /\ coprime n y ==> coprime n (x * y) *) -(* Proof: - gcd n x = 1 ==> no common factor between x and n - gcd n y = 1 ==> no common factor between y and n - Hence there is no common factor between (x * y) and n, or gcd n (x * y) = 1 - - gcd n (x * y) = gcd n y by GCD_CANCEL_MULT, since coprime n x. - = 1 by given -*) -val PRODUCT_WITH_GCD_ONE = store_thm( - "PRODUCT_WITH_GCD_ONE", - ``!n x y. coprime n x /\ coprime n y ==> coprime n (x * y)``, - metis_tac[GCD_CANCEL_MULT]); - -(* Theorem: For 0 < n, coprime n x ==> coprime n (x MOD n) *) -(* Proof: - Since n <> 0, - 1 = gcd n x by given - = gcd (x MOD n) n by GCD_EFFICIENTLY - = gcd n (x MOD n) by GCD_SYM -*) -val MOD_WITH_GCD_ONE = store_thm( - "MOD_WITH_GCD_ONE", - ``!n x. 0 < n /\ coprime n x ==> coprime n (x MOD n)``, - rpt strip_tac >> - `0 <> n` by decide_tac >> - metis_tac[GCD_EFFICIENTLY, GCD_SYM]); - -(* Theorem: If 1 < n, coprime n x ==> ?k. ((k * x) MOD n = 1) /\ coprime n k *) -(* Proof: - gcd n x = 1 ==> x <> 0 by GCD_0R - Also, - gcd n x = 1 - ==> ?k q. k * x = q * n + 1 by LINEAR_GCD - ==> (k * x) MOD n = (q * n + 1) MOD n by arithmetic - ==> (k * x) MOD n = 1 by MOD_MULT, 1 < n. - - Let g = gcd n k. - Since 1 < n, 0 < n. - Since q * n+1 <> 0, x <> 0, k <> 0, hence 0 < k. - Hence 0 < g /\ (n MOD g = 0) /\ (k MOD g = 0) by GCD_DIVIDES. - Or n = a * g /\ k = b * g for some a, b. - Therefore: - (b * g) * x = q * (a * g) + 1 - (b * x) * g = (q * a) * g + 1 by arithmetic - Hence g divides 1, or g = 1 since 0 < g. -*) -val GCD_ONE_PROPERTY = store_thm( - "GCD_ONE_PROPERTY", - ``!n x. 1 < n /\ coprime n x ==> ?k. ((k * x) MOD n = 1) /\ coprime n k``, - rpt strip_tac >> - `n <> 1` by decide_tac >> - `x <> 0` by metis_tac[GCD_0R] >> - `?k q. k * x = q * n + 1` by metis_tac[LINEAR_GCD] >> - `(k * x) MOD n = 1` by rw_tac std_ss[MOD_MULT] >> - `?g. g = gcd n k` by rw[] >> - `n <> 0 /\ q*n + 1 <> 0` by decide_tac >> - `k <> 0` by metis_tac[MULT_EQ_0] >> - `0 < g /\ (n MOD g = 0) /\ (k MOD g = 0)` by metis_tac[GCD_DIVIDES, NOT_ZERO_LT_ZERO] >> - `g divides n /\ g divides k` by rw[DIVIDES_MOD_0] >> - `g divides (n * q) /\ g divides (k*x)` by rw[DIVIDES_MULT] >> - `g divides (n * q + 1)` by metis_tac [MULT_COMM] >> - `g divides 1` by metis_tac[DIVIDES_ADD_2] >> - metis_tac[DIVIDES_ONE]); - -(* Theorem: For 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> - ?y. 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1) *) -(* Proof: - gcd n x = 1 - ==> ?k. (k * x) MOD n = 1 /\ coprime n k by GCD_ONE_PROPERTY - (k * x) MOD n = 1 - ==> (k MOD n * x MOD n) MOD n = 1 by MOD_TIMES2 - ==> ((k MOD n) * x) MOD n = 1 by LESS_MOD, x < n. - - Now k MOD n < n by MOD_LESS - and 0 < k MOD n by MOD_MULITPLE_ZERO and 1 <> 0. - - Hence take y = k MOD n, then 0 < y < n. - and gcd n k = 1 ==> gcd n (k MOD n) = 1 by MOD_WITH_GCD_ONE. -*) -val GCD_MOD_MULT_INV = store_thm( - "GCD_MOD_MULT_INV", - ``!n x. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> - ?y. 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1)``, - rpt strip_tac >> - `?k. ((k * x) MOD n = 1) /\ coprime n k` by rw_tac std_ss[GCD_ONE_PROPERTY] >> - `0 < n` by decide_tac >> - `(k MOD n * x MOD n) MOD n = 1` by rw_tac std_ss[MOD_TIMES2] >> - `((k MOD n) * x) MOD n = 1` by metis_tac[LESS_MOD] >> - `k MOD n < n` by rw_tac std_ss[MOD_LESS] >> - `1 <> 0` by decide_tac >> - `0 <> k MOD n` by metis_tac[MOD_MULITPLE_ZERO] >> - `0 < k MOD n` by decide_tac >> - metis_tac[MOD_WITH_GCD_ONE]); - -(* Convert this into an existence definition *) -val lemma = prove( - ``!n x. ?y. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> - 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1)``, - metis_tac[GCD_MOD_MULT_INV]); - -val GEN_MULT_INV_DEF = new_specification( - "GEN_MULT_INV_DEF", - ["GCD_MOD_MUL_INV"], - SIMP_RULE (srw_ss()) [SKOLEM_THM] lemma); -(* > val GEN_MULT_INV_DEF = - |- !n x. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> - 0 < GCD_MOD_MUL_INV n x /\ GCD_MOD_MUL_INV n x < n /\ coprime n (GCD_MOD_MUL_INV n x) /\ - ((GCD_MOD_MUL_INV n x * x) MOD n = 1) : thm *) - -(* ------------------------------------------------------------------------- *) -(* More GCD and LCM Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Note: -gcdTheory.LCM_IS_LEAST_COMMON_MULTIPLE -|- m divides lcm m n /\ n divides lcm m n /\ !p. m divides p /\ n divides p ==> lcm m n divides p -gcdTheory.GCD_IS_GREATEST_COMMON_DIVISOR -|- !a b. gcd a b divides a /\ gcd a b divides b /\ !d. d divides a /\ d divides b ==> d divides gcd a b -*) - -(* Theorem: (c = gcd a b) <=> - c divides a /\ c divides b /\ !x. x divides a /\ x divides b ==> x divides c *) -(* Proof: - By GCD_IS_GREATEST_COMMON_DIVISOR - (gcd a b) divides a [1] - and (gcd a b) divides b [2] - and !p. p divides a /\ p divides b ==> p divides (gcd a b) [3] - Hence if part is true, and for the only-if part, - We have c divides (gcd a b) by [3] above, - and (gcd a b) divides c by [1], [2] and the given implication - Therefore c = gcd a b by DIVIDES_ANTISYM -*) -val GCD_PROPERTY = store_thm( - "GCD_PROPERTY", - ``!a b c. (c = gcd a b) <=> - c divides a /\ c divides b /\ !x. x divides a /\ x divides b ==> x divides c``, - rw[GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_ANTISYM, EQ_IMP_THM]); - -(* Theorem: gcd a (gcd b c) = gcd (gcd a b) c *) -(* Proof: - Since (gcd a (gcd b c)) divides a by GCD_PROPERTY - (gcd a (gcd b c)) divides b by GCD_PROPERTY, DIVIDES_TRANS - (gcd a (gcd b c)) divides c by GCD_PROPERTY, DIVIDES_TRANS - (gcd (gcd a b) c) divides a by GCD_PROPERTY, DIVIDES_TRANS - (gcd (gcd a b) c) divides b by GCD_PROPERTY, DIVIDES_TRANS - (gcd (gcd a b) c) divides c by GCD_PROPERTY - We have - (gcd (gcd a b) c) divides (gcd b c) by GCD_PROPERTY - and (gcd (gcd a b) c) divides (gcd a (gcd b c)) by GCD_PROPERTY - Also (gcd a (gcd b c)) divides (gcd a b) by GCD_PROPERTY - and (gcd a (gcd b c)) divides (gcd (gcd a b) c) by GCD_PROPERTY - Therefore gcd a (gcd b c) = gcd (gcd a b) c by DIVIDES_ANTISYM -*) -val GCD_ASSOC = store_thm( - "GCD_ASSOC", - ``!a b c. gcd a (gcd b c) = gcd (gcd a b) c``, - rpt strip_tac >> - `(gcd a (gcd b c)) divides a` by metis_tac[GCD_PROPERTY] >> - `(gcd a (gcd b c)) divides b` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> - `(gcd a (gcd b c)) divides c` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> - `(gcd (gcd a b) c) divides a` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> - `(gcd (gcd a b) c) divides b` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> - `(gcd (gcd a b) c) divides c` by metis_tac[GCD_PROPERTY] >> - `(gcd (gcd a b) c) divides (gcd a (gcd b c))` by metis_tac[GCD_PROPERTY] >> - `(gcd a (gcd b c)) divides (gcd (gcd a b) c)` by metis_tac[GCD_PROPERTY] >> - rw[DIVIDES_ANTISYM]); - -(* Note: - With identity by GCD_1: (gcd 1 x = 1) /\ (gcd x 1 = 1) - GCD forms a monoid in numbers. -*) - -(* Theorem: gcd a (gcd b c) = gcd b (gcd a c) *) -(* Proof: - gcd a (gcd b c) - = gcd (gcd a b) c by GCD_ASSOC - = gcd (gcd b a) c by GCD_SYM - = gcd b (gcd a c) by GCD_ASSOC -*) -val GCD_ASSOC_COMM = store_thm( - "GCD_ASSOC_COMM", - ``!a b c. gcd a (gcd b c) = gcd b (gcd a c)``, - metis_tac[GCD_ASSOC, GCD_SYM]); - -(* Theorem: (c = lcm a b) <=> - a divides c /\ b divides c /\ !x. a divides x /\ b divides x ==> c divides x *) -(* Proof: - By LCM_IS_LEAST_COMMON_MULTIPLE - a divides (lcm a b) [1] - and b divides (lcm a b) [2] - and !p. a divides p /\ divides b p ==> divides (lcm a b) p [3] - Hence if part is true, and for the only-if part, - We have c divides (lcm a b) by implication and [1], [2] - and (lcm a b) divides c by [3] - Therefore c = lcm a b by DIVIDES_ANTISYM -*) -val LCM_PROPERTY = store_thm( - "LCM_PROPERTY", - ``!a b c. (c = lcm a b) <=> - a divides c /\ b divides c /\ !x. a divides x /\ b divides x ==> c divides x``, - rw[LCM_IS_LEAST_COMMON_MULTIPLE, DIVIDES_ANTISYM, EQ_IMP_THM]); - -(* Theorem: lcm a (lcm b c) = lcm (lcm a b) c *) -(* Proof: - Since a divides (lcm a (lcm b c)) by LCM_PROPERTY - b divides (lcm a (lcm b c)) by LCM_PROPERTY, DIVIDES_TRANS - c divides (lcm a (lcm b c)) by LCM_PROPERTY, DIVIDES_TRANS - a divides (lcm (lcm a b) c) by LCM_PROPERTY, DIVIDES_TRANS - b divides (lcm (lcm a b) c) by LCM_PROPERTY, DIVIDES_TRANS - c divides (lcm (lcm a b) c) by LCM_PROPERTY - We have - (lcm b c) divides (lcm (lcm a b) c) by LCM_PROPERTY - and (lcm a (lcm b c)) divides (lcm (lcm a b) c) by LCM_PROPERTY - Also (lcm a b) divides (lcm a (lcm b c)) by LCM_PROPERTY - and (lcm (lcm a b) c) divides (lcm a (lcm b c)) by LCM_PROPERTY - Therefore lcm a (lcm b c) = lcm (lcm a b) c by DIVIDES_ANTISYM -*) -val LCM_ASSOC = store_thm( - "LCM_ASSOC", - ``!a b c. lcm a (lcm b c) = lcm (lcm a b) c``, - rpt strip_tac >> - `a divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY] >> - `b divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> - `c divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> - `a divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> - `b divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> - `c divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY] >> - `(lcm a (lcm b c)) divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY] >> - `(lcm (lcm a b) c) divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY] >> - rw[DIVIDES_ANTISYM]); - -(* Note: - With the identity by LCM_0: (lcm 0 x = 0) /\ (lcm x 0 = 0) - LCM forms a monoid in numbers. -*) - -(* Theorem: lcm a (lcm b c) = lcm b (lcm a c) *) -(* Proof: - lcm a (lcm b c) - = lcm (lcm a b) c by LCM_ASSOC - = lcm (lcm b a) c by LCM_COMM - = lcm b (lcm a c) by LCM_ASSOC -*) -val LCM_ASSOC_COMM = store_thm( - "LCM_ASSOC_COMM", - ``!a b c. lcm a (lcm b c) = lcm b (lcm a c)``, - metis_tac[LCM_ASSOC, LCM_COMM]); - -(* Theorem: b <= a ==> gcd (a - b) b = gcd a b *) -(* Proof: - gcd (a - b) b - = gcd b (a - b) by GCD_SYM - = gcd (b + (a - b)) b by GCD_ADD_L - = gcd (a - b + b) b by ADD_COMM - = gcd a b by SUB_ADD, b <= a. - -Note: If a < b, a - b = 0 for num, hence gcd (a - b) b = gcd 0 b = b. -*) -val GCD_SUB_L = store_thm( - "GCD_SUB_L", - ``!a b. b <= a ==> (gcd (a - b) b = gcd a b)``, - metis_tac[GCD_SYM, GCD_ADD_L, ADD_COMM, SUB_ADD]); - -(* Theorem: a <= b ==> gcd a (b - a) = gcd a b *) -(* Proof: - gcd a (b - a) - = gcd (b - a) a by GCD_SYM - = gcd b a by GCD_SUB_L - = gcd a b by GCD_SYM -*) -val GCD_SUB_R = store_thm( - "GCD_SUB_R", - ``!a b. a <= b ==> (gcd a (b - a) = gcd a b)``, - metis_tac[GCD_SYM, GCD_SUB_L]); - -(* Theorem: If 1/c = 1/b - 1/a, then lcm a b = lcm a c. - a * b = c * (a - b) ==> lcm a b = lcm a c *) -(* Proof: - Idea: - lcm a c - = (a * c) DIV (gcd a c) by lcm_def - = (a * b * c) DIV (gcd a c) DIV b by MULT_DIV - = (a * b * c) DIV b * (gcd a c) by DIV_DIV_DIV_MULT - = (a * b * c) DIV gcd b*a b*c by GCD_COMMON_FACTOR - = (a * b * c) DIV gcd c*(a-b) c*b by given - = (a * b * c) DIV c * gcd (a-b) b by GCD_COMMON_FACTOR - = (a * b * c) DIV c * gcd a b by GCD_SUB_L - = (a * b * c) DIV c DIV gcd a b by DIV_DIV_DIV_MULT - = a * b DIV gcd a b by MULT_DIV - = lcm a b by lcm_def - - Details: - If a = 0, - lcm 0 b = 0 = lcm 0 c by LCM_0 - If a <> 0, - If b = 0, a * b = 0 = c * a by MULT_0, SUB_0 - Hence c = 0, hence true by MULT_EQ_0 - If b <> 0, c <> 0. by MULT_EQ_0 - So 0 < gcd a c, 0 < gcd a b by GCD_EQ_0 - and (gcd a c) divides a by GCD_IS_GREATEST_COMMON_DIVISOR - thus (gcd a c) divides (a * c) by DIVIDES_MULT - Note (a - b) <> 0 by MULT_EQ_0 - so ~(a <= b) by SUB_EQ_0 - or b < a, or b <= a for GCD_SUB_L later. - Now, - lcm a c - = (a * c) DIV (gcd a c) by lcm_def - = (b * ((a * c) DIV (gcd a c))) DIV b by MULT_COMM, MULT_DIV - = ((b * (a * c)) DIV (gcd a c)) DIV b by MULTIPLY_DIV - = (b * (a * c)) DIV ((gcd a c) * b) by DIV_DIV_DIV_MULT - = (b * a * c) DIV ((gcd a c) * b) by MULT_ASSOC - = c * (a * b) DIV (b * (gcd a c)) by MULT_COMM - = c * (a * b) DIV (gcd (b * a) (b * c)) by GCD_COMMON_FACTOR - = c * (a * b) DIV (gcd (a * b) (c * b)) by MULT_COMM - = c * (a * b) DIV (gcd (c * (a-b)) (c * b)) by a * b = c * (a - b) - = c * (a * b) DIV (c * gcd (a-b) b) by GCD_COMMON_FACTOR - = c * (a * b) DIV (c * gcd a b) by GCD_SUB_L - = c * (a * b) DIV c DIV (gcd a b) by DIV_DIV_DIV_MULT - = a * b DIV gcd a b by MULT_COMM, MULT_DIV - = lcm a b by lcm_def -*) -val LCM_EXCHANGE = store_thm( - "LCM_EXCHANGE", - ``!a b c. (a * b = c * (a - b)) ==> (lcm a b = lcm a c)``, - rpt strip_tac >> - Cases_on `a = 0` >- - rw[] >> - Cases_on `b = 0` >| [ - `c = 0` by metis_tac[MULT_EQ_0, SUB_0] >> - rw[], - `c <> 0` by metis_tac[MULT_EQ_0] >> - `0 < b /\ 0 < c` by decide_tac >> - `(gcd a c) divides a` by rw[GCD_IS_GREATEST_COMMON_DIVISOR] >> - `(gcd a c) divides (a * c)` by rw[DIVIDES_MULT] >> - `0 < gcd a c /\ 0 < gcd a b` by metis_tac[GCD_EQ_0, NOT_ZERO_LT_ZERO] >> - `~(a <= b)` by metis_tac[SUB_EQ_0, MULT_EQ_0] >> - `b <= a` by decide_tac >> - `lcm a c = (a * c) DIV (gcd a c)` by rw[lcm_def] >> - `_ = (b * ((a * c) DIV (gcd a c))) DIV b` by metis_tac[MULT_COMM, MULT_DIV] >> - `_ = ((b * (a * c)) DIV (gcd a c)) DIV b` by rw[MULTIPLY_DIV] >> - `_ = (b * (a * c)) DIV ((gcd a c) * b)` by rw[DIV_DIV_DIV_MULT] >> - `_ = (b * a * c) DIV ((gcd a c) * b)` by rw[MULT_ASSOC] >> - `_ = c * (a * b) DIV (b * (gcd a c))` by rw_tac std_ss[MULT_COMM] >> - `_ = c * (a * b) DIV (gcd (b * a) (b * c))` by rw[GCD_COMMON_FACTOR] >> - `_ = c * (a * b) DIV (gcd (a * b) (c * b))` by rw_tac std_ss[MULT_COMM] >> - `_ = c * (a * b) DIV (gcd (c * (a-b)) (c * b))` by rw[] >> - `_ = c * (a * b) DIV (c * gcd (a-b) b)` by rw[GCD_COMMON_FACTOR] >> - `_ = c * (a * b) DIV (c * gcd a b)` by rw[GCD_SUB_L] >> - `_ = c * (a * b) DIV c DIV (gcd a b)` by rw[DIV_DIV_DIV_MULT] >> - `_ = a * b DIV gcd a b` by metis_tac[MULT_COMM, MULT_DIV] >> - `_ = lcm a b` by rw[lcm_def] >> - decide_tac - ]); - -(* Theorem: coprime m n ==> LCM m n = m * n *) -(* Proof: - By GCD_LCM, with gcd m n = 1. -*) -val LCM_COPRIME = store_thm( - "LCM_COPRIME", - ``!m n. coprime m n ==> (lcm m n = m * n)``, - metis_tac[GCD_LCM, MULT_LEFT_1]); - -(* Theorem: LCM (k * m) (k * n) = k * LCM m n *) -(* Proof: - If m = 0 or n = 0, LHS = 0 = RHS. - If m <> 0 and n <> 0, - lcm (k * m) (k * n) - = (k * m) * (k * n) / gcd (k * m) (k * n) by GCD_LCM - = (k * m) * (k * n) / k * (gcd m n) by GCD_COMMON_FACTOR - = k * m * n / (gcd m n) - = k * LCM m n by GCD_LCM -*) -val LCM_COMMON_FACTOR = store_thm( - "LCM_COMMON_FACTOR", - ``!m n k. lcm (k * m) (k * n) = k * lcm m n``, - rpt strip_tac >> - `k * (k * (m * n)) = (k * m) * (k * n)` by rw_tac arith_ss[] >> - `_ = gcd (k * m) (k * n) * lcm (k * m) (k * n) ` by rw[GCD_LCM] >> - `_ = k * (gcd m n) * lcm (k * m) (k * n)` by rw[GCD_COMMON_FACTOR] >> - `_ = k * ((gcd m n) * lcm (k * m) (k * n))` by rw_tac arith_ss[] >> - Cases_on `k = 0` >- - rw[] >> - `(gcd m n) * lcm (k * m) (k * n) = k * (m * n)` by metis_tac[MULT_LEFT_CANCEL] >> - `_ = k * ((gcd m n) * (lcm m n))` by rw_tac std_ss[GCD_LCM] >> - `_ = (gcd m n) * (k * (lcm m n))` by rw_tac arith_ss[] >> - Cases_on `n = 0` >- - rw[] >> - metis_tac[MULT_LEFT_CANCEL, GCD_EQ_0]); - -(* Theorem: coprime a b ==> !c. lcm (a * c) (b * c) = a * b * c *) -(* Proof: - lcm (a * c) (b * c) - = lcm (c * a) (c * b) by MULT_COMM - = c * (lcm a b) by LCM_COMMON_FACTOR - = (lcm a b) * c by MULT_COMM - = a * b * c by LCM_COPRIME -*) -val LCM_COMMON_COPRIME = store_thm( - "LCM_COMMON_COPRIME", - ``!a b. coprime a b ==> !c. lcm (a * c) (b * c) = a * b * c``, - metis_tac[LCM_COMMON_FACTOR, LCM_COPRIME, MULT_COMM]); - -(* Theorem: 0 < n /\ m MOD n = 0 ==> gcd m n = n *) -(* Proof: - Since m MOD n = 0 - ==> n divides m by DIVIDES_MOD_0 - Hence gcd m n = gcd n m by GCD_SYM - = n by divides_iff_gcd_fix -*) -val GCD_MULTIPLE = store_thm( - "GCD_MULTIPLE", - ``!m n. 0 < n /\ (m MOD n = 0) ==> (gcd m n = n)``, - metis_tac[DIVIDES_MOD_0, divides_iff_gcd_fix, GCD_SYM]); - -(* Theorem: gcd (m * n) n = n *) -(* Proof: - gcd (m * n) n - = gcd (n * m) n by MULT_COMM - = gcd (n * m) (n * 1) by MULT_RIGHT_1 - = n * (gcd m 1) by GCD_COMMON_FACTOR - = n * 1 by GCD_1 - = n by MULT_RIGHT_1 -*) -val GCD_MULTIPLE_ALT = store_thm( - "GCD_MULTIPLE_ALT", - ``!m n. gcd (m * n) n = n``, - rpt strip_tac >> - `gcd (m * n) n = gcd (n * m) n` by rw[MULT_COMM] >> - `_ = gcd (n * m) (n * 1)` by rw[] >> - rw[GCD_COMMON_FACTOR]); - -(* Theorem: k * a <= b ==> gcd a b = gcd a (b - k * a) *) -(* Proof: - By induction on k. - Base case: 0 * a <= b ==> gcd a b = gcd a (b - 0 * a) - True since b - 0 * a = b by MULT, SUB_0 - Step case: k * a <= b ==> (gcd a b = gcd a (b - k * a)) ==> - SUC k * a <= b ==> (gcd a b = gcd a (b - SUC k * a)) - SUC k * a <= b - ==> k * a + a <= b by MULT - so a <= b - k * a by arithmetic [1] - and k * a <= b by 0 <= b - k * a, [2] - gcd a (b - SUC k * a) - = gcd a (b - (k * a + a)) by MULT - = gcd a (b - k * a - a) by arithmetic - = gcd a (b - k * a - a + a) by GCD_ADD_L, ADD_COMM - = gcd a (b - k * a) by SUB_ADD, a <= b - k * a [1] - = gcd a b by induction hypothesis, k * a <= b [2] -*) -val GCD_SUB_MULTIPLE = store_thm( - "GCD_SUB_MULTIPLE", - ``!a b k. k * a <= b ==> (gcd a b = gcd a (b - k * a))``, - rpt strip_tac >> - Induct_on `k` >- - rw[] >> - rw_tac std_ss[] >> - `k * a + a <= b` by metis_tac[MULT] >> - `a <= b - k * a` by decide_tac >> - `k * a <= b` by decide_tac >> - `gcd a (b - SUC k * a) = gcd a (b - (k * a + a))` by rw[MULT] >> - `_ = gcd a (b - k * a - a)` by rw_tac arith_ss[] >> - `_ = gcd a (b - k * a - a + a)` by rw[GCD_ADD_L, ADD_COMM] >> - rw_tac std_ss[SUB_ADD]); - -(* Theorem: k * a <= b ==> (gcd b a = gcd a (b - k * a)) *) -(* Proof: by GCD_SUB_MULTIPLE, GCD_SYM *) -val GCD_SUB_MULTIPLE_COMM = store_thm( - "GCD_SUB_MULTIPLE_COMM", - ``!a b k. k * a <= b ==> (gcd b a = gcd a (b - k * a))``, - metis_tac[GCD_SUB_MULTIPLE, GCD_SYM]); - -(* Theorem: 0 < m ==> (gcd m n = gcd m (n MOD m)) *) -(* Proof: - gcd m n - = gcd (n MOD m) m by GCD_EFFICIENTLY, m <> 0 - = gcd m (n MOD m) by GCD_SYM -*) -val GCD_MOD = store_thm( - "GCD_MOD", - ``!m n. 0 < m ==> (gcd m n = gcd m (n MOD m))``, - rw[Once GCD_EFFICIENTLY, GCD_SYM]); - -(* Theorem: 0 < m ==> (gcd n m = gcd (n MOD m) m) *) -(* Proof: by GCD_MOD, GCD_COMM *) -val GCD_MOD_COMM = store_thm( - "GCD_MOD_COMM", - ``!m n. 0 < m ==> (gcd n m = gcd (n MOD m) m)``, - metis_tac[GCD_MOD, GCD_COMM]); - -(* Theorem: gcd a (b * a + c) = gcd a c *) -(* Proof: - If a = 0, - Then b * 0 + c = c by arithmetic - Hence trivially true. - If a <> 0, - gcd a (b * a + c) - = gcd ((b * a + c) MOD a) a by GCD_EFFICIENTLY, 0 < a - = gcd (c MOD a) a by MOD_TIMES, 0 < a - = gcd a c by GCD_EFFICIENTLY, 0 < a -*) -val GCD_EUCLID = store_thm( - "GCD_EUCLID", - ``!a b c. gcd a (b * a + c) = gcd a c``, - rpt strip_tac >> - Cases_on `a = 0` >- - rw[] >> - metis_tac[GCD_EFFICIENTLY, MOD_TIMES, NOT_ZERO_LT_ZERO]); - -(* Theorem: gcd (b * a + c) a = gcd a c *) -(* Proof: by GCD_EUCLID, GCD_SYM *) -val GCD_REDUCE = store_thm( - "GCD_REDUCE", - ``!a b c. gcd (b * a + c) a = gcd a c``, - rw[GCD_EUCLID, GCD_SYM]); - -(* Theorem alias *) -Theorem GCD_REDUCE_BY_COPRIME = GCD_CANCEL_MULT; -(* val GCD_REDUCE_BY_COPRIME = - |- !m n k. coprime m k ==> gcd m (k * n) = gcd m n: thm *) - -(* Idea: a crude upper bound for greatest common divisor. - A better upper bound is: gcd m n <= MIN m n, by MIN_LE *) - -(* Theorem: 0 < m /\ 0 < n ==> gcd m n <= m /\ gcd m n <= n *) -(* Proof: - Let g = gcd m n. - Then g divides m /\ g divides n by GCD_PROPERTY - so g <= m /\ g <= n by DIVIDES_LE, 0 < m, 0 < n -*) -Theorem gcd_le: - !m n. 0 < m /\ 0 < n ==> gcd m n <= m /\ gcd m n <= n -Proof - ntac 3 strip_tac >> - qabbrev_tac `g = gcd m n` >> - `g divides m /\ g divides n` by metis_tac[GCD_PROPERTY] >> - simp[DIVIDES_LE] -QED - -(* Idea: a generalisation of GCD_LINEAR: -|- !j k. 0 < j ==> ?p q. p * j = q * k + gcd j k - This imposes a condition for (gcd a b) divides c. -*) - -(* Theorem: 0 < a ==> ((gcd a b) divides c <=> ?p q. p * a = q * b + c) *) -(* Proof: - Let d = gcd a b. - If part: d divides c ==> ?p q. p * a = q * b + c - Note ?k. c = k * d by divides_def - and ?u v. u * a = v * b + d by GCD_LINEAR, 0 < a - so (k * u) * a = (k * v) * b + (k * d) - Take p = k * u, q = k * v, - Then p * q = q * b + c - Only-if part: p * a = q * b + c ==> d divides c - Note d divides a /\ d divides b by GCD_PROPERTY - so d divides c by divides_linear_sub -*) -Theorem gcd_divides_iff: - !a b c. 0 < a ==> ((gcd a b) divides c <=> ?p q. p * a = q * b + c) -Proof - rpt strip_tac >> - qabbrev_tac `d = gcd a b` >> - rw_tac bool_ss[EQ_IMP_THM] >| [ - `?k. c = k * d` by rw[GSYM divides_def] >> - `?p q. p * a = q * b + d` by rw[GCD_LINEAR, Abbr`d`] >> - `k * (p * a) = k * (q * b + d)` by fs[] >> - `_ = k * (q * b) + k * d` by decide_tac >> - metis_tac[MULT_ASSOC], - `d divides a /\ d divides b` by metis_tac[GCD_PROPERTY] >> - metis_tac[divides_linear_sub] - ] -QED - -(* Theorem alias *) -Theorem gcd_linear_thm = gcd_divides_iff; -(* val gcd_linear_thm = -|- !a b c. 0 < a ==> (gcd a b divides c <=> ?p q. p * a = q * b + c): thm *) - -(* Idea: a version of GCD_LINEAR for MOD, without negatives. - That is: in MOD n. gcd (a b) can be expressed as a linear combination of a b. *) - -(* Theorem: 0 < n /\ 0 < a ==> ?p q. (p * a + q * b) MOD n = gcd a b MOD n *) -(* Proof: - Let d = gcd a b. - Then ?h k. h * a = k * b + d by GCD_LINEAR, 0 < a - Let p = h, q = k * n - k. - Then q + k = k * n. - (p * a) MOD n = (k * b + d) MOD n - <=> (p * a + q * b) MOD n = (q * b + k * b + d) MOD n by ADD_MOD - <=> (p * a + q * b) MOD n = (k * b * n + d) MOD n by above - <=> (p * a + q * b) MOD n = d MOD n by MOD_TIMES -*) -Theorem gcd_linear_mod_thm: - !n a b. 0 < n /\ 0 < a ==> ?p q. (p * a + q * b) MOD n = gcd a b MOD n -Proof - rpt strip_tac >> - qabbrev_tac `d = gcd a b` >> - `?p k. p * a = k * b + d` by rw[GCD_LINEAR, Abbr`d`] >> - `k <= k * n` by fs[] >> - `k * n - k + k = k * n` by decide_tac >> - qabbrev_tac `q = k * n - k` >> - qexists_tac `p` >> - qexists_tac `q` >> - `(p * a + q * b) MOD n = (q * b + k * b + d) MOD n` by rw[ADD_MOD] >> - `_ = ((q + k) * b + d) MOD n` by decide_tac >> - `_ = (k * b * n + d) MOD n` by rfs[] >> - simp[MOD_TIMES] -QED - -(* Idea: a simplification of gcd_linear_mod_thm when n = a. *) - -(* Theorem: 0 < a ==> ?q. (q * b) MOD a = (gcd a b) MOD a *) -(* Proof: - Let g = gcd a b. - Then ?p q. (p * a + q * b) MOD a = g MOD a by gcd_linear_mod_thm, n = a - so (q * b) MOD a = g MOD a by MOD_TIMES -*) -Theorem gcd_linear_mod_1: - !a b. 0 < a ==> ?q. (q * b) MOD a = (gcd a b) MOD a -Proof - metis_tac[gcd_linear_mod_thm, MOD_TIMES] -QED - -(* Idea: symmetric version of of gcd_linear_mod_1. *) - -(* Theorem: 0 < b ==> ?p. (p * a) MOD b = (gcd a b) MOD b *) -(* Proof: - Note ?p. (p * a) MOD b = (gcd b a) MOD b by gcd_linear_mod_1 - or = (gcd a b) MOD b by GCD_SYM -*) -Theorem gcd_linear_mod_2: - !a b. 0 < b ==> ?p. (p * a) MOD b = (gcd a b) MOD b -Proof - metis_tac[gcd_linear_mod_1, GCD_SYM] -QED - -(* Idea: replacing n = a * b in gcd_linear_mod_thm. *) - -(* Theorem: 0 < a /\ 0 < b ==> ?p q. (p * a + q * b) MOD (a * b) = (gcd a b) MOD (a * b) *) -(* Proof: by gcd_linear_mod_thm, n = a * b. *) -Theorem gcd_linear_mod_prod: - !a b. 0 < a /\ 0 < b ==> ?p q. (p * a + q * b) MOD (a * b) = (gcd a b) MOD (a * b) -Proof - simp[gcd_linear_mod_thm] -QED - -(* Idea: specialise gcd_linear_mod_prod for coprime a b. *) - -(* Theorem: 0 < a /\ 0 < b /\ coprime a b ==> - ?p q. (p * a + q * b) MOD (a * b) = 1 MOD (a * b) *) -(* Proof: by gcd_linear_mod_prod. *) -Theorem coprime_linear_mod_prod: - !a b. 0 < a /\ 0 < b /\ coprime a b ==> - ?p q. (p * a + q * b) MOD (a * b) = 1 MOD (a * b) -Proof - metis_tac[gcd_linear_mod_prod] -QED - -(* Idea: generalise gcd_linear_mod_thm for multiple of gcd a b. *) - -(* Theorem: 0 < n /\ 0 < a /\ gcd a b divides c ==> - ?p q. (p * a + q * b) MOD n = c MOD n *) -(* Proof: - Let d = gcd a b. - Note k. c = k * d by divides_def - and ?p q. (p * a + q * b) MOD n = d MOD n by gcd_linear_mod_thm - Thus (k * d) MOD n - = (k * (p * a + q * b)) MOD n by MOD_TIMES2, 0 < n - = (k * p * a + k * q * b) MOD n by LEFT_ADD_DISTRIB - Take (k * p) and (k * q) for the eventual p and q. -*) -Theorem gcd_multiple_linear_mod_thm: - !n a b c. 0 < n /\ 0 < a /\ gcd a b divides c ==> - ?p q. (p * a + q * b) MOD n = c MOD n -Proof - rpt strip_tac >> - qabbrev_tac `d = gcd a b` >> - `?k. c = k * d` by rw[GSYM divides_def] >> - `?p q. (p * a + q * b) MOD n = d MOD n` by metis_tac[gcd_linear_mod_thm] >> - `(k * (p * a + q * b)) MOD n = (k * d) MOD n` by metis_tac[MOD_TIMES2] >> - `k * (p * a + q * b) = k * p * a + k * q * b` by decide_tac >> - metis_tac[] -QED - -(* Idea: specialise gcd_multiple_linear_mod_thm for n = a * b. *) - -(* Theorem: 0 < a /\ 0 < b /\ gcd a b divides c ==> - ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b)) *) -(* Proof: by gcd_multiple_linear_mod_thm. *) -Theorem gcd_multiple_linear_mod_prod: - !a b c. 0 < a /\ 0 < b /\ gcd a b divides c ==> - ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) -Proof - simp[gcd_multiple_linear_mod_thm] -QED - -(* Idea: specialise gcd_multiple_linear_mod_prod for coprime a b. *) - -(* Theorem: 0 < a /\ 0 < b /\ coprime a b ==> - ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) *) -(* Proof: - Note coprime a b means gcd a b = 1 by notation - and 1 divides c by ONE_DIVIDES_ALL - so the result follows by gcd_multiple_linear_mod_prod -*) -Theorem coprime_multiple_linear_mod_prod: - !a b c. 0 < a /\ 0 < b /\ coprime a b ==> - ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) -Proof - metis_tac[gcd_multiple_linear_mod_prod, ONE_DIVIDES_ALL] -QED - -(* ------------------------------------------------------------------------- *) -(* Coprime Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: coprime n (n + 1) *) -(* Proof: - Since n < n + 1 ==> n <= n + 1, - gcd n (n + 1) - = gcd n (n + 1 - n) by GCD_SUB_R - = gcd n 1 by arithmetic - = 1 by GCD_1 -*) -val coprime_SUC = store_thm( - "coprime_SUC", - ``!n. coprime n (n + 1)``, - rw[GCD_SUB_R]); - -(* Theorem: 0 < n ==> coprime n (n - 1) *) -(* Proof: - gcd n (n - 1) - = gcd (n - 1) n by GCD_SYM - = gcd (n - 1) (n - 1 + 1) by SUB_ADD, 0 <= n - = 1 by coprime_SUC -*) -val coprime_PRE = store_thm( - "coprime_PRE", - ``!n. 0 < n ==> coprime n (n - 1)``, - metis_tac[GCD_SYM, coprime_SUC, DECIDE``!n. 0 < n ==> (n - 1 + 1 = n)``]); - -(* Theorem: coprime 0 n ==> n = 1 *) -(* Proof: - gcd 0 n = n by GCD_0L - = 1 by coprime 0 n -*) -val coprime_0L = store_thm( - "coprime_0L", - ``!n. coprime 0 n <=> (n = 1)``, - rw[GCD_0L]); - -(* Theorem: coprime n 0 ==> n = 1 *) -(* Proof: - gcd n 0 = n by GCD_0L - = 1 by coprime n 0 -*) -val coprime_0R = store_thm( - "coprime_0R", - ``!n. coprime n 0 <=> (n = 1)``, - rw[GCD_0R]); - -(* Theorem: (coprime 0 n <=> n = 1) /\ (coprime n 0 <=> n = 1) *) -(* Proof: by coprime_0L, coprime_0R *) -Theorem coprime_0: - !n. (coprime 0 n <=> n = 1) /\ (coprime n 0 <=> n = 1) -Proof - simp[coprime_0L, coprime_0R] -QED - -(* Theorem: coprime x y = coprime y x *) -(* Proof: - coprime x y - means gcd x y = 1 - so gcd y x = 1 by GCD_SYM - thus coprime y x -*) -val coprime_sym = store_thm( - "coprime_sym", - ``!x y. coprime x y = coprime y x``, - rw[GCD_SYM]); - -(* Theorem: coprime k n /\ n <> 1 ==> k <> 0 *) -(* Proof: by coprime_0L *) -val coprime_neq_1 = store_thm( - "coprime_neq_1", - ``!n k. coprime k n /\ n <> 1 ==> k <> 0``, - fs[coprime_0L]); - -(* Theorem: coprime k n /\ 1 < n ==> 0 < k *) -(* Proof: by coprime_neq_1 *) -val coprime_gt_1 = store_thm( - "coprime_gt_1", - ``!n k. coprime k n /\ 1 < n ==> 0 < k``, - metis_tac[coprime_neq_1, NOT_ZERO_LT_ZERO, DECIDE``~(1 < 1)``]); - -(* Note: gcd (c ** n) m = gcd c m is false when n = 0, where c ** 0 = 1. *) - -(* Theorem: coprime c m ==> !n. coprime (c ** n) m *) -(* Proof: by induction on n. - Base case: coprime (c ** 0) m - Since c ** 0 = 1 by EXP - and coprime 1 m is true by GCD_1 - Step case: coprime c m /\ coprime (c ** n) m ==> coprime (c ** SUC n) m - coprime c m means - coprime m c by GCD_SYM - - gcd m (c ** SUC n) - = gcd m (c * c ** n) by EXP - = gcd m (c ** n) by GCD_CANCEL_MULT, coprime m c - = 1 by induction hypothesis - Hence coprime m (c ** SUC n) - or coprime (c ** SUC n) m by GCD_SYM -*) -val coprime_exp = store_thm( - "coprime_exp", - ``!c m. coprime c m ==> !n. coprime (c ** n) m``, - rpt strip_tac >> - Induct_on `n` >- - rw[EXP, GCD_1] >> - metis_tac[EXP, GCD_CANCEL_MULT, GCD_SYM]); - -(* Theorem: coprime a b ==> !n. coprime a (b ** n) *) -(* Proof: by coprime_exp, GCD_SYM *) -val coprime_exp_comm = store_thm( - "coprime_exp_comm", - ``!a b. coprime a b ==> !n. coprime a (b ** n)``, - metis_tac[coprime_exp, GCD_SYM]); - -(* Theorem: 0 < n ==> !a b. coprime a b <=> coprime a (b ** n) *) -(* Proof: - If part: coprime a b ==> coprime a (b ** n) - True by coprime_exp_comm. - Only-if part: coprime a (b ** n) ==> coprime a b - If a = 0, - then b ** n = 1 by GCD_0L - and b = 1 by EXP_EQ_1, n <> 0 - Hence coprime 0 1 by GCD_0L - If a <> 0, - Since coprime a (b ** n) means - ?h k. h * a = k * b ** n + 1 by LINEAR_GCD, GCD_SYM - Let d = gcd a b. - Since d divides a and d divides b by GCD_IS_GREATEST_COMMON_DIVISOR - and d divides b ** n by divides_exp, 0 < n - so d divides 1 by divides_linear_sub - Thus d = 1 by DIVIDES_ONE - or coprime a b by notation -*) -val coprime_iff_coprime_exp = store_thm( - "coprime_iff_coprime_exp", - ``!n. 0 < n ==> !a b. coprime a b <=> coprime a (b ** n)``, - rw[EQ_IMP_THM] >- - rw[coprime_exp_comm] >> - `n <> 0` by decide_tac >> - Cases_on `a = 0` >- - metis_tac[GCD_0L, EXP_EQ_1] >> - `?h k. h * a = k * b ** n + 1` by metis_tac[LINEAR_GCD, GCD_SYM] >> - qabbrev_tac `d = gcd a b` >> - `d divides a /\ d divides b` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> - `d divides (b ** n)` by rw[divides_exp] >> - `d divides 1` by metis_tac[divides_linear_sub] >> - rw[GSYM DIVIDES_ONE]); - -(* Theorem: coprime x z /\ coprime y z ==> coprime (x * y) z *) -(* Proof: - By GCD_CANCEL_MULT: - |- !m n k. coprime m k ==> (gcd m (k * n) = gcd m n) - Hence follows by coprime_sym. -*) -val coprime_product_coprime = store_thm( - "coprime_product_coprime", - ``!x y z. coprime x z /\ coprime y z ==> coprime (x * y) z``, - metis_tac[GCD_CANCEL_MULT, GCD_SYM]); - -(* Theorem: coprime z x /\ coprime z y ==> coprime z (x * y) *) -(* Proof: - Note gcd z x = 1 by given - ==> gcd z (x * y) - = gcd z y by GCD_CANCEL_MULT - = 1 by given -*) -val coprime_product_coprime_sym = store_thm( - "coprime_product_coprime_sym", - ``!x y z. coprime z x /\ coprime z y ==> coprime z (x * y)``, - rw[GCD_CANCEL_MULT]); -(* This is the same as PRODUCT_WITH_GCD_ONE *) - -(* Theorem: coprime x z ==> (coprime y z <=> coprime (x * y) z) *) -(* Proof: - If part: coprime x z /\ coprime y z ==> coprime (x * y) z - True by coprime_product_coprime - Only-if part: coprime x z /\ coprime (x * y) z ==> coprime y z - Let d = gcd y z. - Then d divides z /\ d divides y by GCD_PROPERTY - so d divides (x * y) by DIVIDES_MULT, MULT_COMM - or d divides (gcd (x * y) z) by GCD_PROPERTY - d divides 1 by coprime (x * y) z - ==> d = 1 by DIVIDES_ONE - or coprime y z by notation -*) -val coprime_product_coprime_iff = store_thm( - "coprime_product_coprime_iff", - ``!x y z. coprime x z ==> (coprime y z <=> coprime (x * y) z)``, - rw[EQ_IMP_THM] >- - rw[coprime_product_coprime] >> - qabbrev_tac `d = gcd y z` >> - metis_tac[GCD_PROPERTY, DIVIDES_MULT, MULT_COMM, DIVIDES_ONE]); - -(* Theorem: a divides n /\ b divides n /\ coprime a b ==> (a * b) divides n *) -(* Proof: by LCM_COPRIME, LCM_DIVIDES *) -val coprime_product_divides = store_thm( - "coprime_product_divides", - ``!n a b. a divides n /\ b divides n /\ coprime a b ==> (a * b) divides n``, - metis_tac[LCM_COPRIME, LCM_DIVIDES]); - -(* Theorem: 0 < m /\ coprime m n ==> coprime m (n MOD m) *) -(* Proof: - gcd m n - = if m = 0 then n else gcd (n MOD m) m by GCD_EFFICIENTLY - = gcd (n MOD m) m by decide_tac, m <> 0 - = gcd m (n MOD m) by GCD_SYM - Hence true since coprime m n <=> gcd m n = 1. -*) -val coprime_mod = store_thm( - "coprime_mod", - ``!m n. 0 < m /\ coprime m n ==> coprime m (n MOD m)``, - metis_tac[GCD_EFFICIENTLY, GCD_SYM, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < m ==> (coprime m n = coprime m (n MOD m)) *) -(* Proof: by GCD_MOD *) -val coprime_mod_iff = store_thm( - "coprime_mod_iff", - ``!m n. 0 < m ==> (coprime m n = coprime m (n MOD m))``, - rw[Once GCD_MOD]); - -(* Theorem: 1 < n /\ coprime n m ==> ~(n divides m) *) -(* Proof: - coprime n m - ==> gcd n m = 1 by notation - ==> n MOD m <> 0 by MOD_NONZERO_WHEN_GCD_ONE, with 1 < n - ==> ~(n divides m) by DIVIDES_MOD_0, with 0 < n -*) -val coprime_not_divides = store_thm( - "coprime_not_divides", - ``!m n. 1 < n /\ coprime n m ==> ~(n divides m)``, - metis_tac[MOD_NONZERO_WHEN_GCD_ONE, DIVIDES_MOD_0, ONE_LT_POS, NOT_ZERO_LT_ZERO]); - -(* Theorem: 1 < n /\ coprime n k /\ 1 < p /\ p divides n ==> ~(p divides k) *) -(* Proof: - First, 1 < n ==> n <> 0 and n <> 1 - If k = 0, gcd n k = n by GCD_0R - But coprime n k means gcd n k = 1, so k <> 0. - By contradiction. - If p divides k, and given p divides n, - then p divides gcd n k = 1 by GCD_IS_GREATEST_COMMON_DIVISOR, n <> 0 and k <> 0 - or p = 1 by DIVIDES_ONE - which contradicts 1 < p. -*) -val coprime_factor_not_divides = store_thm( - "coprime_factor_not_divides", - ``!n k. 1 < n /\ coprime n k ==> !p. 1 < p /\ p divides n ==> ~(p divides k)``, - rpt strip_tac >> - `n <> 0 /\ n <> 1 /\ p <> 1` by decide_tac >> - metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_ONE, GCD_0R]); - -(* Theorem: m divides n ==> !k. coprime n k ==> coprime m k *) -(* Proof: - Let d = gcd m k. - Then d divides m /\ d divides k by GCD_IS_GREATEST_COMMON_DIVISOR - ==> d divides n by DIVIDES_TRANS - so d divides 1 by GCD_IS_GREATEST_COMMON_DIVISOR, coprime n k - ==> d = 1 by DIVIDES_ONE -*) -val coprime_factor_coprime = store_thm( - "coprime_factor_coprime", - ``!m n. m divides n ==> !k. coprime n k ==> coprime m k``, - rpt strip_tac >> - qabbrev_tac `d = gcd m k` >> - `d divides m /\ d divides k` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> - `d divides n` by metis_tac[DIVIDES_TRANS] >> - `d divides 1` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] >> - rw[GSYM DIVIDES_ONE]); - -(* Idea: common factor of two coprime numbers. *) - -(* Theorem: coprime a b /\ c divides a /\ c divides b ==> c = 1 *) -(* Proof: - Note c divides gcd a b by GCD_PROPERTY - or c divides 1 by coprime a b - so c = 1 by DIVIDES_ONE -*) -Theorem coprime_common_factor: - !a b c. coprime a b /\ c divides a /\ c divides b ==> c = 1 -Proof - metis_tac[GCD_PROPERTY, DIVIDES_ONE] -QED - - -(* Theorem: prime p /\ ~(p divides n) ==> coprime p n *) -(* Proof: - Since divides p 0, so n <> 0. by ALL_DIVIDES_0 - If n = 1, certainly coprime p n by GCD_1 - If n <> 1, - Let gcd p n = d. - Since d divides p by GCD_IS_GREATEST_COMMON_DIVISOR - and prime p by given - so d = 1 or d = p by prime_def - but d <> p by divides_iff_gcd_fix - Hence d = 1, or coprime p n. -*) -val prime_not_divides_coprime = store_thm( - "prime_not_divides_coprime", - ``!n p. prime p /\ ~(p divides n) ==> coprime p n``, - rpt strip_tac >> - `n <> 0` by metis_tac[ALL_DIVIDES_0] >> - Cases_on `n = 1` >- - rw[] >> - `0 < p` by rw[PRIME_POS] >> - `p <> 0` by decide_tac >> - metis_tac[prime_def, divides_iff_gcd_fix, GCD_IS_GREATEST_COMMON_DIVISOR]); - -(* Theorem: prime p /\ ~(coprime p n) ==> p divides n *) -(* Proof: - Let d = gcd p n. - Then d divides p by GCD_IS_GREATEST_COMMON_DIVISOR - ==> d = p by prime_def - Thus p divides n by divides_iff_gcd_fix - - Or: this is just the inverse of prime_not_divides_coprime. -*) -val prime_not_coprime_divides = store_thm( - "prime_not_coprime_divides", - ``!n p. prime p /\ ~(coprime p n) ==> p divides n``, - metis_tac[prime_not_divides_coprime]); - -(* Theorem: 1 < n /\ prime p /\ p divides n ==> !k. coprime n k ==> coprime p k *) -(* Proof: - Since coprime n k /\ p divides n - ==> ~(p divides k) by coprime_factor_not_divides - Then prime p /\ ~(p divides k) - ==> coprime p k by prime_not_divides_coprime -*) -val coprime_prime_factor_coprime = store_thm( - "coprime_prime_factor_coprime", - ``!n p. 1 < n /\ prime p /\ p divides n ==> !k. coprime n k ==> coprime p k``, - metis_tac[coprime_factor_not_divides, prime_not_divides_coprime, ONE_LT_PRIME]); - -(* This is better: -coprime_factor_coprime -|- !m n. m divides n ==> !k. coprime n k ==> coprime m k -*) - -(* Theorem: 1 < n ==> (!j. 0 < j /\ j <= m ==> coprime n j) ==> m < n *) -(* Proof: - By contradiction. Suppose n <= m. - Since 1 < n means 0 < n and n <> 1, - The implication shows - coprime n n, or n = 1 by notation - But gcd n n = n by GCD_REF - This contradicts n <> 1. -*) -val coprime_all_le_imp_lt = store_thm( - "coprime_all_le_imp_lt", - ``!n. 1 < n ==> !m. (!j. 0 < j /\ j <= m ==> coprime n j) ==> m < n``, - spose_not_then strip_assume_tac >> - `n <= m` by decide_tac >> - `0 < n /\ n <> 1` by decide_tac >> - metis_tac[GCD_REF]); - -(* Theorem: (!j. 1 < j /\ j <= m ==> ~(j divides n)) <=> (!j. 1 < j /\ j <= m ==> coprime j n) *) -(* Proof: - If part: (!j. 1 < j /\ j <= m ==> ~(j divides n)) /\ 1 < j /\ j <= m ==> coprime j n - Let d = gcd j n. - Then d divides j /\ d divides n by GCD_IS_GREATEST_COMMON_DIVISOR - Now 1 < j ==> 0 < j /\ j <> 0 - so d <= j by DIVIDES_LE, 0 < j - and d <> 0 by GCD_EQ_0, j <> 0 - By contradiction, suppose d <> 1. - Then 1 < d /\ d <= m by d <> 1, d <= j /\ j <= m - so ~(d divides n), a contradiction by implication - - Only-if part: (!j. 1 < j /\ j <= m ==> coprime j n) /\ 1 < j /\ j <= m ==> ~(j divides n) - Since coprime j n by implication - so ~(j divides n) by coprime_not_divides -*) -val coprime_condition = store_thm( - "coprime_condition", - ``!m n. (!j. 1 < j /\ j <= m ==> ~(j divides n)) <=> (!j. 1 < j /\ j <= m ==> coprime j n)``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - qabbrev_tac `d = gcd j n` >> - `d divides j /\ d divides n` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> - `0 < j /\ j <> 0` by decide_tac >> - `d <= j` by rw[DIVIDES_LE] >> - `d <> 0` by metis_tac[GCD_EQ_0] >> - `1 < d /\ d <= m` by decide_tac >> - metis_tac[], - metis_tac[coprime_not_divides] - ]); - -(* Note: -The above is the generalization of this observation: -- a prime n has all 1 < j < n coprime to n. Therefore, -- a number n has all 1 < j < m coprime to n, where m is the first non-trivial factor of n. - Of course, the first non-trivial factor of n must be a prime. -*) - -(* Theorem: 1 < m /\ (!j. 1 < j /\ j <= m ==> ~(j divides n)) ==> coprime m n *) -(* Proof: by coprime_condition, taking j = m. *) -val coprime_by_le_not_divides = store_thm( - "coprime_by_le_not_divides", - ``!m n. 1 < m /\ (!j. 1 < j /\ j <= m ==> ~(j divides n)) ==> coprime m n``, - rw[coprime_condition]); - -(* Idea: a characterisation of the coprime property of two numbers. *) - -(* Theorem: coprime m n <=> !p. prime p ==> ~(p divides m /\ p divides n) *) -(* Proof: - If part: coprime m n /\ prime p ==> ~(p divides m) \/ ~(p divides n) - By contradiction, suppose p divides m /\ p divides n. - Then p = 1 by coprime_common_factor - This contradicts prime p by NOT_PRIME_1 - Only-if part: !p. prime p ==> ~(p divides m) \/ ~(p divides m) ==> coprime m n - Let d = gcd m n. - By contradiction, suppose d <> 1. - Then ?p. prime p /\ p divides d by PRIME_FACTOR, d <> 1. - Now d divides m /\ d divides n by GCD_PROPERTY - so p divides m /\ p divides n by DIVIDES_TRANS - This contradicts the assumption. -*) -Theorem coprime_by_prime_factor: - !m n. coprime m n <=> !p. prime p ==> ~(p divides m /\ p divides n) -Proof - rw[EQ_IMP_THM] >- - metis_tac[coprime_common_factor, NOT_PRIME_1] >> - qabbrev_tac `d = gcd m n` >> - spose_not_then strip_assume_tac >> - `?p. prime p /\ p divides d` by rw[PRIME_FACTOR] >> - `d divides m /\ d divides n` by metis_tac[GCD_PROPERTY] >> - metis_tac[DIVIDES_TRANS] -QED - -(* Idea: coprime_by_prime_factor with reduced testing of primes, useful in practice. *) - -(* Theorem: 0 < m /\ 0 < n ==> - (coprime m n <=> - !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m /\ p divides n)) *) -(* Proof: - If part: coprime m n /\ prime p /\ ... ==> ~(p divides m) \/ ~(p divides n) - By contradiction, suppose p divides m /\ p divides n. - Then p = 1 by coprime_common_factor - This contradicts prime p by NOT_PRIME_1 - Only-if part: !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m) \/ ~(p divides m) ==> coprime m n - Let d = gcd m n. - By contradiction, suppose d <> 1. - Then ?p. prime p /\ p divides d by PRIME_FACTOR, d <> 1. - Now d divides m /\ d divides n by GCD_PROPERTY - so p divides m /\ p divides n by DIVIDES_TRANS - Thus p <= m /\ p <= n by DIVIDES_LE, 0 < m, 0 < n - This contradicts the assumption. -*) -Theorem coprime_by_prime_factor_le: - !m n. 0 < m /\ 0 < n ==> - (coprime m n <=> - !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m /\ p divides n)) -Proof - rw[EQ_IMP_THM] >- - metis_tac[coprime_common_factor, NOT_PRIME_1] >> - qabbrev_tac `d = gcd m n` >> - spose_not_then strip_assume_tac >> - `?p. prime p /\ p divides d` by rw[PRIME_FACTOR] >> - `d divides m /\ d divides n` by metis_tac[GCD_PROPERTY] >> - `0 < p` by rw[PRIME_POS] >> - metis_tac[DIVIDES_TRANS, DIVIDES_LE] -QED - -(* Idea: establish coprime (p * a + q * b) (a * b). *) -(* Note: the key is to apply coprime_by_prime_factor. *) - -(* Theorem: coprime a b /\ coprime p b /\ coprime q a ==> coprime (p * a + q * b) (a * b) *) -(* Proof: - Let z = p * a + q * b, c = a * b, d = gcd z c. - Then d divides z /\ d divides c by GCD_PROPERTY - By coprime_by_prime_factor, we need to show: - !t. prime t ==> ~(t divides z /\ t divides c) - By contradiction, suppose t divides z /\ t divides c. - Then t divides d by GCD_PROPERTY - or t divides c where c = a * b by DIVIDES_TRANS - so t divides a or p divides b by P_EUCLIDES - - If t divides a, - Then t divides (q * b) by divides_linear_sub - and ~(t divides b) by coprime_common_factor, NOT_PRIME_1 - so t divides q by P_EUCLIDES - ==> t = 1 by coprime_common_factor - This contradicts prime t by NOT_PRIME_1 - If t divides b, - Then t divides (p * a) by divides_linear_sub - and ~(t divides a) by coprime_common_factor, NOT_PRIME_1 - so t divides p by P_EUCLIDES - ==> t = 1 by coprime_common_factor - This contradicts prime t by NOT_PRIME_1 - Since all lead to contradiction, we have shown: - !t. prime t ==> ~(t divides z /\ t divides c) - Thus coprime z c by coprime_by_prime_factor -*) -Theorem coprime_linear_mult: - !a b p q. coprime a b /\ coprime p b /\ coprime q a ==> coprime (p * a + q * b) (a * b) -Proof - rpt strip_tac >> - qabbrev_tac `z = p * a + q * b` >> - qabbrev_tac `c = a * b` >> - irule (coprime_by_prime_factor |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> - rpt strip_tac >> - `p' divides a \/ p' divides b` by metis_tac[P_EUCLIDES] >| [ - `p' divides (q * b)` by metis_tac[divides_linear_sub, MULT_LEFT_1] >> - `~(p' divides b)` by metis_tac[coprime_common_factor, NOT_PRIME_1] >> - `p' divides q` by metis_tac[P_EUCLIDES] >> - metis_tac[coprime_common_factor, NOT_PRIME_1], - `p' divides (p * a)` by metis_tac[divides_linear_sub, MULT_LEFT_1, ADD_COMM] >> - `~(p' divides a)` by metis_tac[coprime_common_factor, NOT_PRIME_1, MULT_COMM] >> - `p' divides p` by metis_tac[P_EUCLIDES] >> - metis_tac[coprime_common_factor, NOT_PRIME_1] - ] -QED - -(* Idea: include converse of coprime_linear_mult. *) - -(* Theorem: coprime a b ==> - ((coprime p b /\ coprime q a) <=> coprime (p * a + q * b) (a * b)) *) -(* Proof: - If part: coprime p b /\ coprime q a ==> coprime (p * a + q * b) (a * b) - This is true by coprime_linear_mult. - Only-if: coprime (p * a + q * b) (a * b) ==> coprime p b /\ coprime q a - Let z = p * a + q * b. Consider a prime t. - For coprime p b. - If t divides p /\ t divides b, - Then t divides z by divides_linear - and t divides (a * b) by DIVIDES_MULTIPLE - so t = 1 by coprime_common_factor - This contradicts prime t by NOT_PRIME_1 - Thus coprime p b by coprime_by_prime_factor - For coprime q a. - If t divides q /\ t divides a, - Then t divides z by divides_linear - and t divides (a * b) by DIVIDES_MULTIPLE - so t = 1 by coprime_common_factor - This contradicts prime t by NOT_PRIME_1 - Thus coprime q a by coprime_by_prime_factor -*) -Theorem coprime_linear_mult_iff: - !a b p q. coprime a b ==> - ((coprime p b /\ coprime q a) <=> coprime (p * a + q * b) (a * b)) -Proof - rw_tac std_ss[EQ_IMP_THM] >- - simp[coprime_linear_mult] >- - (irule (coprime_by_prime_factor |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> - rpt strip_tac >> - `p' divides (p * a + q * b)` by metis_tac[divides_linear, MULT_COMM] >> - `p' divides (a * b)` by rw[DIVIDES_MULTIPLE] >> - metis_tac[coprime_common_factor, NOT_PRIME_1]) >> - irule (coprime_by_prime_factor |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> - rpt strip_tac >> - `p' divides (p * a + q * b)` by metis_tac[divides_linear, MULT_COMM] >> - `p' divides (a * b)` by metis_tac[DIVIDES_MULTIPLE, MULT_COMM] >> - metis_tac[coprime_common_factor, NOT_PRIME_1] -QED - -(* Idea: condition for a number to be coprime with prime power. *) - -(* Theorem: prime p /\ 0 < n ==> !q. coprime q (p ** n) <=> ~(p divides q) *) -(* Proof: - If part: prime p /\ 0 < n /\ coprime q (p ** n) ==> ~(p divides q) - By contradiction, suppose p divides q. - Note p divides (p ** n) by prime_divides_self_power, 0 < n - Thus p = 1 by coprime_common_factor - This contradicts p <> 1 by NOT_PRIME_1 - Only-if part: prime p /\ 0 < n /\ ~(p divides q) ==> coprime q (p ** n) - Note coprime q p by prime_not_divides_coprime, GCD_SYM - Thus coprime q (p ** n) by coprime_iff_coprime_exp, 0 < n -*) -Theorem coprime_prime_power: - !p n. prime p /\ 0 < n ==> !q. coprime q (p ** n) <=> ~(p divides q) -Proof - rw[EQ_IMP_THM] >- - metis_tac[prime_divides_self_power, coprime_common_factor, NOT_PRIME_1] >> - metis_tac[prime_not_divides_coprime, coprime_iff_coprime_exp, GCD_SYM] -QED - -(* Theorem: prime n ==> !m. 0 < m /\ m < n ==> coprime n m *) -(* Proof: - By contradiction. Let d = gcd n m, and d <> 1. - Since prime n, 0 < n by PRIME_POS - Thus d divides n, and d m divides by GCD_IS_GREATEST_COMMON_DIVISOR, n <> 0, m <> 0. - ==> d = n by prime_def, d <> 1. - ==> n divides m by d divides m - ==> n <= m by DIVIDES_LE - which contradicts m < n. -*) -val prime_coprime_all_lt = store_thm( - "prime_coprime_all_lt", - ``!n. prime n ==> !m. 0 < m /\ m < n ==> coprime n m``, - rpt strip_tac >> - spose_not_then strip_assume_tac >> - qabbrev_tac `d = gcd n m` >> - `0 < n` by rw[PRIME_POS] >> - `n <> 0 /\ m <> 0` by decide_tac >> - `d divides n /\ d divides m` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> - `d = n` by metis_tac[prime_def] >> - `n <= m` by rw[DIVIDES_LE] >> - decide_tac); - -(* Theorem: prime n /\ m < n ==> (!j. 0 < j /\ j <= m ==> coprime n j) *) -(* Proof: - Since m < n, all j < n. - Hence true by prime_coprime_all_lt -*) -val prime_coprime_all_less = store_thm( - "prime_coprime_all_less", - ``!m n. prime n /\ m < n ==> (!j. 0 < j /\ j <= m ==> coprime n j)``, - rpt strip_tac >> - `j < n` by decide_tac >> - rw[prime_coprime_all_lt]); - -(* Theorem: prime n <=> 1 < n /\ (!j. 0 < j /\ j < n ==> coprime n j)) *) -(* Proof: - If part: prime n ==> 1 < n /\ !j. 0 < j /\ j < n ==> coprime n j - (1) prime n ==> 1 < n by ONE_LT_PRIME - (2) prime n /\ 0 < j /\ j < n ==> coprime n j by prime_coprime_all_lt - Only-if part: !j. 0 < j /\ j < n ==> coprime n j ==> prime n - By contradiction, assume ~prime n. - Now, 1 < n /\ ~prime n - ==> ?p. prime p /\ p < n /\ p divides n by PRIME_FACTOR_PROPER - and prime p ==> 0 < p and 1 < p by PRIME_POS, ONE_LT_PRIME - Hence ~coprime p n by coprime_not_divides, 1 < p - But 0 < p < n ==> coprime n p by given implication - This is a contradiction by coprime_sym -*) -val prime_iff_coprime_all_lt = store_thm( - "prime_iff_coprime_all_lt", - ``!n. prime n <=> 1 < n /\ (!j. 0 < j /\ j < n ==> coprime n j)``, - rw[EQ_IMP_THM, ONE_LT_PRIME] >- - rw[prime_coprime_all_lt] >> - spose_not_then strip_assume_tac >> - `?p. prime p /\ p < n /\ p divides n` by rw[PRIME_FACTOR_PROPER] >> - `0 < p` by rw[PRIME_POS] >> - `1 < p` by rw[ONE_LT_PRIME] >> - metis_tac[coprime_not_divides, coprime_sym]); - -(* Theorem: prime n <=> (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n))) *) -(* Proof: - If part: prime n ==> (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n))) - Note 1 < n by ONE_LT_PRIME - By contradiction, suppose j divides n. - Then j = 1 or j = n by prime_def - This contradicts 1 < j /\ j < n. - Only-if part: (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n))) ==> prime n - This is to show: - !b. b divides n ==> b = 1 or b = n by prime_def - Since 1 < n, so n <> 0 by arithmetic - Thus b <= n by DIVIDES_LE - and b <> 0 by ZERO_DIVIDES - By contradiction, suppose b <> 1 and b <> n, but b divides n. - Then 1 < b /\ b < n by above - giving ~(b divides n) by implication - This contradicts with b divides n. -*) -val prime_iff_no_proper_factor = store_thm( - "prime_iff_no_proper_factor", - ``!n. prime n <=> (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n)))``, - rw_tac std_ss[EQ_IMP_THM] >- - rw[ONE_LT_PRIME] >- - metis_tac[prime_def, LESS_NOT_EQ] >> - rw[prime_def] >> - `b <= n` by rw[DIVIDES_LE] >> - `n <> 0` by decide_tac >> - `b <> 0` by metis_tac[ZERO_DIVIDES] >> - spose_not_then strip_assume_tac >> - `1 < b /\ b < n` by decide_tac >> - metis_tac[]); - -(* Theorem: !n. ?p. prime p /\ n < p *) -(* Proof: - Since ?i. n < PRIMES i by NEXT_LARGER_PRIME - and prime (PRIMES i) by primePRIMES - Take p = PRIMES i. -*) -val prime_always_bigger = store_thm( - "prime_always_bigger", - ``!n. ?p. prime p /\ n < p``, - metis_tac[NEXT_LARGER_PRIME, primePRIMES]); - -(* Theorem: n divides m ==> coprime n (SUC m) *) -(* Proof: - If n = 0, - then m = 0 by ZERO_DIVIDES - gcd 0 (SUC 0) - = SUC 0 by GCD_0L - = 1 by ONE - If n = 1, - gcd 1 (SUC m) = 1 by GCD_1 - If n <> 0, - gcd n (SUC m) - = gcd ((SUC m) MOD n) n by GCD_EFFICIENTLY - = gcd 1 n by n divides m - = 1 by GCD_1 -*) -val divides_imp_coprime_with_successor = store_thm( - "divides_imp_coprime_with_successor", - ``!m n. n divides m ==> coprime n (SUC m)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[GSYM ZERO_DIVIDES] >> - Cases_on `n = 1` >- - rw[] >> - `0 < n /\ 1 < n` by decide_tac >> - `m MOD n = 0` by rw[GSYM DIVIDES_MOD_0] >> - `(SUC m) MOD n = (m + 1) MOD n` by rw[ADD1] >> - `_ = (m MOD n + 1 MOD n) MOD n` by rw[MOD_PLUS] >> - `_ = (0 + 1) MOD n` by rw[ONE_MOD] >> - `_ = 1` by rw[ONE_MOD] >> - metis_tac[GCD_EFFICIENTLY, GCD_1]); - -(* Note: counter-example for converse: gcd 3 11 = 1, but ~(3 divides 10). *) - -(* Theorem: 0 < m /\ n divides m ==> coprime n (PRE m) *) -(* Proof: - Since n divides m - ==> ?q. m = q * n by divides_def - Also 0 < m means m <> 0, - ==> ?k. m = SUC k by num_CASES - = k + 1 by ADD1 - so m - k = 1, k = PRE m. - Let d = gcd n k. - Then d divides n /\ d divides k by GCD_IS_GREATEST_COMMON_DIVISOR - and d divides n ==> d divides m by DIVIDES_MULTIPLE, m = q * n - so d divides (m - k) by DIVIDES_SUB - or d divides 1 by m - k = 1 - ==> d = 1 by DIVIDES_ONE -*) -val divides_imp_coprime_with_predecessor = store_thm( - "divides_imp_coprime_with_predecessor", - ``!m n. 0 < m /\ n divides m ==> coprime n (PRE m)``, - rpt strip_tac >> - `?q. m = q * n` by rw[GSYM divides_def] >> - `m <> 0` by decide_tac >> - `?k. m = k + 1` by metis_tac[num_CASES, ADD1] >> - `(k = PRE m) /\ (m - k = 1)` by decide_tac >> - qabbrev_tac `d = gcd n k` >> - `d divides n /\ d divides k` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> - `d divides m` by rw[DIVIDES_MULTIPLE] >> - `d divides (m - k)` by rw[DIVIDES_SUB] >> - metis_tac[DIVIDES_ONE]); - -(* Theorem: coprime p n ==> (gcd (p * m) n = gcd m n) *) -(* Proof: - Note coprime p n means coprime n p by GCD_SYM - gcd (p * m) n - = gcd n (p * m) by GCD_SYM - = gcd n p by GCD_CANCEL_MULT -*) -val gcd_coprime_cancel = store_thm( - "gcd_coprime_cancel", - ``!m n p. coprime p n ==> (gcd (p * m) n = gcd m n)``, - rw[GCD_CANCEL_MULT, GCD_SYM]); - -(* The following is a direct, but tricky, proof of the above result *) - -(* Theorem: coprime p n ==> (gcd (p * m) n = gcd m n) *) -(* Proof: - gcd (p * m) n - = gcd (p * m) (n * 1) by MULT_RIGHT_1 - = gcd (p * m) (n * (gcd m 1)) by GCD_1 - = gcd (p * m) (gcd (n * m) n) by GCD_COMMON_FACTOR - = gcd (gcd (p * m) (n * m)) n by GCD_ASSOC - = gcd (m * (gcd p n)) n by GCD_COMMON_FACTOR, MULT_COMM - = gcd (m * 1) n by coprime p n - = gcd m n by MULT_RIGHT_1 - - Simple proof of GCD_CANCEL_MULT: - (a*c, b) = (a*c , b*1) = (a * c, b * (c, 1)) = (a * c, b * c, b) = ((a, b) * c, b) = (c, b) since (a,b) = 1. -*) -Theorem gcd_coprime_cancel[allow_rebind]: - !m n p. coprime p n ==> (gcd (p * m) n = gcd m n) -Proof - rpt strip_tac >> - ‘gcd (p * m) n = gcd (p * m) (n * (gcd m 1))’ by rw[GCD_1] >> - ‘_ = gcd (p * m) (gcd (n * m) n)’ by rw[GSYM GCD_COMMON_FACTOR] >> - ‘_ = gcd (gcd (p * m) (n * m)) n’ by rw[GCD_ASSOC] >> - ‘_ = gcd m n’ by rw[GCD_COMMON_FACTOR, MULT_COMM] >> - rw[] -QED - -(* Theorem: prime p /\ prime q /\ p <> q ==> coprime p q *) -(* Proof: - Let d = gcd p q. - By contradiction, suppose d <> 1. - Then d divides p /\ d divides q by GCD_PROPERTY - so d = 1 or d = p by prime_def - and d = 1 or d = q by prime_def - But p <> q by given - so d = 1, contradicts d <> 1. -*) -val primes_coprime = store_thm( - "primes_coprime", - ``!p q. prime p /\ prime q /\ p <> q ==> coprime p q``, - spose_not_then strip_assume_tac >> - qabbrev_tac `d = gcd p q` >> - `d divides p /\ d divides q` by metis_tac[GCD_PROPERTY] >> - metis_tac[prime_def]); - -(* Theorem: FINITE s ==> !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s) *) -(* Proof: - By finite induction on s. - Base: coprime x (PROD_SET {}) - Note PROD_SET {} = 1 by PROD_SET_EMPTY - and coprime x 1 = T by GCD_1 - Step: !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s) ==> - e NOTIN s /\ x NOTIN e INSERT s /\ !z. z IN e INSERT s ==> coprime x z ==> - coprime x (PROD_SET (e INSERT s)) - Note coprime x e by IN_INSERT - and coprime x (PROD_SET s) by induction hypothesis - Thus coprime x (e * PROD_SET s) by coprime_product_coprime_sym - or coprime x PROD_SET (e INSERT s) by PROD_SET_INSERT -*) -val every_coprime_prod_set_coprime = store_thm( - "every_coprime_prod_set_coprime", - ``!s. FINITE s ==> !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_SET_EMPTY] >> - fs[] >> - rw[PROD_SET_INSERT, coprime_product_coprime_sym]); - -(* ------------------------------------------------------------------------- *) -(* Pairwise Coprime Property *) -(* ------------------------------------------------------------------------- *) - -(* Overload pairwise coprime set *) -val _ = overload_on("PAIRWISE_COPRIME", ``\s. !x y. x IN s /\ y IN s /\ x <> y ==> coprime x y``); - -(* Theorem: e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> - (!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s *) -(* Proof: by IN_INSERT *) -val pairwise_coprime_insert = store_thm( - "pairwise_coprime_insert", - ``!s e. e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> - (!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s``, - metis_tac[IN_INSERT]); - -(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> - !t. t SUBSET s ==> (PROD_SET t) divides (PROD_SET s) *) -(* Proof: - Note FINITE t by SUBSET_FINITE - By finite induction on t. - Base case: PROD_SET {} divides PROD_SET s - Note PROD_SET {} = 1 by PROD_SET_EMPTY - and 1 divides (PROD_SET s) by ONE_DIVIDES_ALL - Step case: t SUBSET s ==> PROD_SET t divides PROD_SET s ==> - e NOTIN t /\ e INSERT t SUBSET s ==> PROD_SET (e INSERT t) divides PROD_SET s - Let m = PROD_SET s. - Note e IN s /\ t SUBSET s by INSERT_SUBSET - Thus e divides m by PROD_SET_ELEMENT_DIVIDES - and (PROD_SET t) divides m by induction hypothesis - Also coprime e (PROD_SET t) by every_coprime_prod_set_coprime, SUBSET_DEF - Note PROD_SET (e INSERT t) = e * PROD_SET t by PROD_SET_INSERT - ==> e * PROD_SET t divides m by coprime_product_divides -*) -val pairwise_coprime_prod_set_subset_divides = store_thm( - "pairwise_coprime_prod_set_subset_divides", - ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> - !t. t SUBSET s ==> (PROD_SET t) divides (PROD_SET s)``, - rpt strip_tac >> - `FINITE t` by metis_tac[SUBSET_FINITE] >> - qpat_x_assum `t SUBSET s` mp_tac >> - qpat_x_assum `FINITE t` mp_tac >> - qid_spec_tac `t` >> - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_SET_EMPTY] >> - fs[] >> - `e divides PROD_SET s` by rw[PROD_SET_ELEMENT_DIVIDES] >> - `coprime e (PROD_SET t)` by prove_tac[every_coprime_prod_set_coprime, SUBSET_DEF] >> - rw[PROD_SET_INSERT, coprime_product_divides]); - -(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> - !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v) *) -(* Proof: - By finite induction on s. - Base: {} = u UNION v ==> coprime (PROD_SET u) (PROD_SET v) - Note u = {} and v = {} by EMPTY_UNION - and PROD_SET {} = 1 by PROD_SET_EMPTY - Hence true by GCD_1 - Step: PAIRWISE_COPRIME s ==> - !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v) ==> - e NOTIN s /\ e INSERT s = u UNION v ==> coprime (PROD_SET u) (PROD_SET v) - Note (!x. x IN s ==> coprime e x) /\ - PAIRWISE_COPRIME s by IN_INSERT - Note e IN u \/ e IN v by IN_INSERT, IN_UNION - If e IN u, - Then e NOTIN v by IN_DISJOINT - Let w = u DELETE e. - Then e NOTIN w by IN_DELETE - and u = e INSERT w by INSERT_DELETE - Note s = w UNION v by EXTENSION, IN_INSERT, IN_UNION - ==> FINITE w by FINITE_UNION - and DISJOINT w v by DISJOINT_INSERT - - Note coprime (PROD_SET w) (PROD_SET v) by induction hypothesis - and !x. x IN v ==> coprime e x by v SUBSET s - Also FINITE v by FINITE_UNION - so coprime e (PROD_SET v) by every_coprime_prod_set_coprime, FINITE v - ==> coprime (e * PROD_SET w) PROD_SET v by coprime_product_coprime - or coprime PROD_SET (e INSERT w) PROD_SET v by PROD_SET_INSERT - = coprime PROD_SET u PROD_SET v by above - - Similarly for e IN v. -*) -val pairwise_coprime_partition_coprime = store_thm( - "pairwise_coprime_partition_coprime", - ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> - !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v)``, - ntac 2 strip_tac >> - qpat_x_assum `PAIRWISE_COPRIME s` mp_tac >> - qpat_x_assum `FINITE s` mp_tac >> - qid_spec_tac `s` >> - Induct_on `FINITE` >> - rpt strip_tac >- - fs[PROD_SET_EMPTY] >> - `(!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s` by metis_tac[IN_INSERT] >> - `e IN u \/ e IN v` by metis_tac[IN_INSERT, IN_UNION] >| [ - qabbrev_tac `w = u DELETE e` >> - `u = e INSERT w` by rw[Abbr`w`] >> - `e NOTIN w` by rw[Abbr`w`] >> - `e NOTIN v` by metis_tac[IN_DISJOINT] >> - `s = w UNION v` by - (rw[EXTENSION] >> - metis_tac[IN_INSERT, IN_UNION]) >> - `FINITE w` by metis_tac[FINITE_UNION] >> - `DISJOINT w v` by metis_tac[DISJOINT_INSERT] >> - `coprime (PROD_SET w) (PROD_SET v)` by rw[] >> - `(!x. x IN v ==> coprime e x)` by rw[] >> - `FINITE v` by metis_tac[FINITE_UNION] >> - `coprime e (PROD_SET v)` by rw[every_coprime_prod_set_coprime] >> - metis_tac[coprime_product_coprime, PROD_SET_INSERT], - qabbrev_tac `w = v DELETE e` >> - `v = e INSERT w` by rw[Abbr`w`] >> - `e NOTIN w` by rw[Abbr`w`] >> - `e NOTIN u` by metis_tac[IN_DISJOINT] >> - `s = u UNION w` by - (rw[EXTENSION] >> - metis_tac[IN_INSERT, IN_UNION]) >> - `FINITE w` by metis_tac[FINITE_UNION] >> - `DISJOINT u w` by metis_tac[DISJOINT_INSERT, DISJOINT_SYM] >> - `coprime (PROD_SET u) (PROD_SET w)` by rw[] >> - `(!x. x IN u ==> coprime e x)` by rw[] >> - `FINITE u` by metis_tac[FINITE_UNION] >> - `coprime (PROD_SET u) e` by rw[every_coprime_prod_set_coprime, coprime_sym] >> - metis_tac[coprime_product_coprime_sym, PROD_SET_INSERT] - ]); - -(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> !u v. (s = u UNION v) /\ DISJOINT u v ==> - (PROD_SET s = PROD_SET u * PROD_SET v) /\ (coprime (PROD_SET u) (PROD_SET v)) *) -(* Proof: by PROD_SET_PRODUCT_BY_PARTITION, pairwise_coprime_partition_coprime *) -val pairwise_coprime_prod_set_partition = store_thm( - "pairwise_coprime_prod_set_partition", - ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> !u v. (s = u UNION v) /\ DISJOINT u v ==> - (PROD_SET s = PROD_SET u * PROD_SET v) /\ (coprime (PROD_SET u) (PROD_SET v))``, - metis_tac[PROD_SET_PRODUCT_BY_PARTITION, pairwise_coprime_partition_coprime]); - -(* ------------------------------------------------------------------------- *) -(* GCD divisibility condition of Power Predecessors *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < t /\ m <= n ==> - (t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1)) *) -(* Proof: - Note !n. 1 <= t ** n by ONE_LE_EXP, 0 < t, [1] - - Claim: t ** (n - m) - 1 <= t ** n - 1, because: - Proof: Note n - m <= n always - so t ** (n - m) <= t ** n by EXP_BASE_LEQ_MONO_IMP, 0 < t - Now 1 <= t ** (n - m) and - 1 <= t ** n by [1] - Hence t ** (n - m) - 1 <= t ** n - 1. - - t ** (n - m) * (t ** m - 1) + t ** (n - m) - 1 - = (t ** (n - m) * t ** m - t ** (n - m)) + t ** (n - m) - 1 by LEFT_SUB_DISTRIB - = (t ** (n - m + m) - t ** (n - m)) + t ** (n - m) - 1 by EXP_ADD - = (t ** n - t ** (n - m)) + t ** (n - m) - 1 by SUB_ADD, m <= n - = (t ** n - (t ** (n - m) - 1 + 1)) + t ** (n - m) - 1 by SUB_ADD, 1 <= t ** (n - m) - = (t ** n - (1 + (t ** (n - m) - 1))) + t ** (n - m) - 1 by ADD_COMM - = (t ** n - 1 - (t ** (n - m) - 1)) + t ** (n - m) - 1 by SUB_PLUS, no condition - = t ** n - 1 by SUB_ADD, t ** (n - m) - 1 <= t ** n - 1 -*) -val power_predecessor_division_eqn = store_thm( - "power_predecessor_division_eqn", - ``!t m n. 0 < t /\ m <= n ==> - (t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1))``, - rpt strip_tac >> - `1 <= t ** n /\ 1 <= t ** (n - m)` by rw[ONE_LE_EXP] >> - `n - m <= n` by decide_tac >> - `t ** (n - m) <= t ** n` by rw[EXP_BASE_LEQ_MONO_IMP] >> - `t ** (n - m) - 1 <= t ** n - 1` by decide_tac >> - qabbrev_tac `z = t ** (n - m) - 1` >> - `t ** (n - m) * (t ** m - 1) + z = - t ** (n - m) * t ** m - t ** (n - m) + z` by decide_tac >> - `_ = t ** (n - m + m) - t ** (n - m) + z` by rw_tac std_ss[EXP_ADD] >> - `_ = t ** n - t ** (n - m) + z` by rw_tac std_ss[SUB_ADD] >> - `_ = t ** n - (z + 1) + z` by rw_tac std_ss[SUB_ADD, Abbr`z`] >> - `_ = t ** n + z - (z + 1)` by decide_tac >> - `_ = t ** n - 1` by decide_tac >> - decide_tac); - -(* This shows the pattern: - 1000000 so 9999999999 = 1000000 * 9999 + 999999 - ------------ or (b ** 10 - 1) = b ** 6 * (b ** 4 - 1) + (b ** 6 - 1) - 9999 | 9999999999 where b = 10. - 9999 - ---------- - 999999 -*) - -(* Theorem: 0 < t /\ m <= n ==> - (t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1) *) -(* Proof: by power_predecessor_division_eqn *) -val power_predecessor_division_alt = store_thm( - "power_predecessor_division_alt", - ``!t m n. 0 < t /\ m <= n ==> - (t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1)``, - rpt strip_tac >> - imp_res_tac power_predecessor_division_eqn >> - fs[]); - -(* Theorem: m < n ==> (gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** m - 1)) (t ** (n - m) - 1)) *) -(* Proof: - Case t = 0, - If n = 0, t ** 0 = 1 by ZERO_EXP - LHS = gcd 0 x = 0 by GCD_0L - = gcd 0 y = RHS by ZERO_EXP - If n <> 0, 0 ** n = 0 by ZERO_EXP - LHS = gcd (0 - 1) x - = gcd 0 x = 0 by GCD_0L - = gcd 0 y = RHS by ZERO_EXP - Case t <> 0, - Note t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1) - by power_predecessor_division_eqn - so t ** (n - m) * (t ** m - 1) <= t ** n - 1 by above, [1] - and t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1, [2] - gcd (t ** n - 1) (t ** m - 1) - = gcd (t ** m - 1) (t ** n - 1) by GCD_SYM - = gcd (t ** m - 1) ((t ** n - 1) - t ** (n - m) * (t ** m - 1)) - by GCD_SUB_MULTIPLE, [1] - = gcd (t ** m - 1)) (t ** (n - m) - 1) by [2] -*) -val power_predecessor_gcd_reduction = store_thm( - "power_predecessor_gcd_reduction", - ``!t n m. m <= n ==> (gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** m - 1)) (t ** (n - m) - 1))``, - rpt strip_tac >> - Cases_on `t = 0` >- - rw[ZERO_EXP] >> - `t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1)` by rw[power_predecessor_division_eqn] >> - `t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1` by fs[] >> - `gcd (t ** n - 1) (t ** m - 1) = gcd (t ** m - 1) (t ** n - 1)` by rw_tac std_ss[GCD_SYM] >> - `_ = gcd (t ** m - 1) ((t ** n - 1) - t ** (n - m) * (t ** m - 1))` by rw_tac std_ss[GCD_SUB_MULTIPLE] >> - rw_tac std_ss[]); - -(* Theorem: gcd (t ** n - 1) (t ** m - 1) = t ** (gcd n m) - 1 *) -(* Proof: - By complete induction on (n + m): - Induction hypothesis: !m'. m' < n + m ==> - !n m. (m' = n + m) ==> (gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1) - Idea: if 0 < m, n < n + m. Put last n = m, m = n - m. That is m' = m + (n - m) = n. - Also if 0 < n, m < n + m. Put last n = n, m = m - n. That is m' = n + (m - n) = m. - - Thus to apply induction hypothesis, need 0 < n or 0 < m. - So take care of these special cases first. - - Case: n = 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 - LHS = gcd (t ** 0 - 1) (t ** m - 1) - = gcd 0 (t ** m - 1) by EXP - = t ** m - 1 by GCD_0L - = t ** (gcd 0 m) - 1 = RHS by GCD_0L - Case: m = 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 - LHS = gcd (t ** n - 1) (t ** 0 - 1) - = gcd (t ** n - 1) 0 by EXP - = t ** n - 1 by GCD_0R - = t ** (gcd n 0) - 1 = RHS by GCD_0R - - Case: m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 - That is, 0 < n, and 0 < m - also n < n + m, and m < n + m by arithmetic - - Use trichotomy of numbers: by LESS_LESS_CASES - Case: n = m /\ m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 - LHS = gcd (t ** m - 1) (t ** m - 1) - = t ** m - 1 by GCD_REF - = t ** (gcd m m) - 1 = RHS by GCD_REF - - Case: m < n /\ m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 - Since n < n + m by 0 < m - and m + (n - m) = (n - m) + m by ADD_COMM - = n by SUB_ADD, m <= n - gcd (t ** n - 1) (t ** m - 1) - = gcd ((t ** m - 1)) (t ** (n - m) - 1) by power_predecessor_gcd_reduction - = t ** gcd m (n - m) - 1 by induction hypothesis, m + (n - m) = n - = t ** gcd m n - 1 by GCD_SUB_R, m <= n - = t ** gcd n m - 1 by GCD_SYM - - Case: n < m /\ m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 - Since m < n + m by 0 < n - and n + (m - n) = (m - n) + n by ADD_COMM - = m by SUB_ADD, n <= m - gcd (t ** n - 1) (t ** m - 1) - = gcd (t ** m - 1) (t ** n - 1) by GCD_SYM - = gcd ((t ** n - 1)) (t ** (m - n) - 1) by power_predecessor_gcd_reduction - = t ** gcd n (m - n) - 1 by induction hypothesis, n + (m - n) = m - = t ** gcd n m by GCD_SUB_R, n <= m -*) -val power_predecessor_gcd_identity = store_thm( - "power_predecessor_gcd_identity", - ``!t n m. gcd (t ** n - 1) (t ** m - 1) = t ** (gcd n m) - 1``, - rpt strip_tac >> - completeInduct_on `n + m` >> - rpt strip_tac >> - Cases_on `n = 0` >- - rw[EXP] >> - Cases_on `m = 0` >- - rw[EXP] >> - `(n = m) \/ (m < n) \/ (n < m)` by metis_tac[LESS_LESS_CASES] >- - rw[GCD_REF] >- - (`0 < m /\ n < n + m` by decide_tac >> - `m <= n` by decide_tac >> - `m + (n - m) = n` by metis_tac[SUB_ADD, ADD_COMM] >> - `gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** m - 1)) (t ** (n - m) - 1)` by rw[power_predecessor_gcd_reduction] >> - `_ = t ** gcd m (n - m) - 1` by metis_tac[] >> - metis_tac[GCD_SUB_R, GCD_SYM]) >> - `0 < n /\ m < n + m` by decide_tac >> - `n <= m` by decide_tac >> - `n + (m - n) = m` by metis_tac[SUB_ADD, ADD_COMM] >> - `gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** n - 1)) (t ** (m - n) - 1)` by rw[power_predecessor_gcd_reduction, GCD_SYM] >> - `_ = t ** gcd n (m - n) - 1` by metis_tac[] >> - metis_tac[GCD_SUB_R]); - -(* Above is the formal proof of the following pattern: - For any base - gcd(999999,9999) = gcd(6 9s, 4 9s) = gcd(6,4) 9s = 2 9s = 99 - or 999999 MOD 9999 = (6 9s) MOD (4 9s) = 2 9s = 99 - Thus in general, - (m 9s) MOD (n 9s) = (m MOD n) 9s - Repeating the use of Euclidean algorithm then gives: - gcd (m 9s, n 9s) = (gcd m n) 9s - -Reference: A Mathematical Tapestry (by Jean Pedersen and Peter Hilton) -Chapter 4: A number-theory thread -- Folding numbers, a number trick, and some tidbits. -*) - -(* Theorem: 1 < t ==> ((t ** n - 1) divides (t ** m - 1) <=> n divides m) *) -(* Proof: - (t ** n - 1) divides (t ** m - 1) - <=> gcd (t ** n - 1) (t ** m - 1) = t ** n - 1 by divides_iff_gcd_fix - <=> t ** (gcd n m) - 1 = t ** n - 1 by power_predecessor_gcd_identity - <=> t ** (gcd n m) = t ** n by PRE_SUB1, INV_PRE_EQ, EXP_POS, 0 < t - <=> gcd n m = n by EXP_BASE_INJECTIVE, 1 < t - <=> n divides m by divides_iff_gcd_fix -*) -val power_predecessor_divisibility = store_thm( - "power_predecessor_divisibility", - ``!t n m. 1 < t ==> ((t ** n - 1) divides (t ** m - 1) <=> n divides m)``, - rpt strip_tac >> - `0 < t` by decide_tac >> - `!n. 0 < t ** n` by rw[EXP_POS] >> - `!x y. 0 < x /\ 0 < y ==> ((x - 1 = y - 1) <=> (x = y))` by decide_tac >> - `(t ** n - 1) divides (t ** m - 1) <=> ((gcd (t ** n - 1) (t ** m - 1) = t ** n - 1))` by rw[divides_iff_gcd_fix] >> - `_ = (t ** (gcd n m) - 1 = t ** n - 1)` by rw[power_predecessor_gcd_identity] >> - `_ = (t ** (gcd n m) = t ** n)` by rw[] >> - `_ = (gcd n m = n)` by rw[EXP_BASE_INJECTIVE] >> - rw[divides_iff_gcd_fix]); - -(* Theorem: t - 1 divides t ** n - 1 *) -(* Proof: - If t = 0, - Then t - 1 = 0 by integer subtraction - and t ** n - 1 = 0 by ZERO_EXP, either case of n. - Thus 0 divides 0 by ZERO_DIVIDES - If t = 1, - Then t - 1 = 0 by arithmetic - and t ** n - 1 = 0 by EXP_1 - Thus 0 divides 0 by ZERO_DIVIDES - Otherwise, 1 < t - and 1 divides n by ONE_DIVIDES_ALL - ==> t ** 1 - 1 divides t ** n - 1 by power_predecessor_divisibility - or t - 1 divides t ** n - 1 by EXP_1 -*) -Theorem power_predecessor_divisor: - !t n. t - 1 divides t ** n - 1 -Proof - rpt strip_tac >> - Cases_on `t = 0` >- - simp[ZERO_EXP] >> - Cases_on `t = 1` >- - simp[] >> - `1 < t` by decide_tac >> - metis_tac[power_predecessor_divisibility, EXP_1, ONE_DIVIDES_ALL] -QED - -(* Overload power predecessor *) -Overload tops = “\b:num n. b ** n - 1” - -(* - power_predecessor_division_eqn - |- !t m n. 0 < t /\ m <= n ==> tops t n = t ** (n - m) * tops t m + tops t (n - m) - power_predecessor_division_alt - |- !t m n. 0 < t /\ m <= n ==> tops t n - t ** (n - m) * tops t m = tops t (n - m) - power_predecessor_gcd_reduction - |- !t n m. m <= n ==> (gcd (tops t n) (tops t m) = gcd (tops t m) (tops t (n - m))) - power_predecessor_gcd_identity - |- !t n m. gcd (tops t n) (tops t m) = tops t (gcd n m) - power_predecessor_divisibility - |- !t n m. 1 < t ==> (tops t n divides tops t m <=> n divides m) - power_predecessor_divisor - |- !t n. t - 1 divides tops t n -*) - -(* Overload power predecessor base 10 *) -val _ = overload_on("nines", ``\n. tops 10 n``); - -(* Obtain corollaries *) - -val nines_division_eqn = save_thm("nines_division_eqn", - power_predecessor_division_eqn |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); -val nines_division_alt = save_thm("nines_division_alt", - power_predecessor_division_alt |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); -val nines_gcd_reduction = save_thm("nines_gcd_reduction", - power_predecessor_gcd_reduction |> ISPEC ``10``); -val nines_gcd_identity = save_thm("nines_gcd_identity", - power_predecessor_gcd_identity |> ISPEC ``10``); -val nines_divisibility = save_thm("nines_divisibility", - power_predecessor_divisibility |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); -val nines_divisor = save_thm("nines_divisor", - power_predecessor_divisor |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); -(* -val nines_division_eqn = - |- !m n. m <= n ==> nines n = 10 ** (n - m) * nines m + nines (n - m): thm -val nines_division_alt = - |- !m n. m <= n ==> nines n - 10 ** (n - m) * nines m = nines (n - m): thm -val nines_gcd_reduction = - |- !n m. m <= n ==> gcd (nines n) (nines m) = gcd (nines m) (nines (n - m)): thm -val nines_gcd_identity = |- !n m. gcd (nines n) (nines m) = nines (gcd n m): thm -val nines_divisibility = |- !n m. nines n divides nines m <=> n divides m: thm -val nines_divisor = |- !n. 9 divides nines n: thm -*) - -(* ------------------------------------------------------------------------- *) -(* GCD involving Powers *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: prime m /\ prime n /\ m divides (n ** k) ==> (m = n) *) -(* Proof: - By induction on k. - Base: m divides n ** 0 ==> (m = n) - Since n ** 0 = 1 by EXP - and m divides 1 ==> m = 1 by DIVIDES_ONE - This contradicts 1 < m by ONE_LT_PRIME - Step: m divides n ** k ==> (m = n) ==> m divides n ** SUC k ==> (m = n) - Since n ** SUC k = n * n ** k by EXP - Also m divides n \/ m divides n ** k by P_EUCLIDES - If m divides n, then m = n by prime_divides_only_self - If m divides n ** k, then m = n by induction hypothesis -*) -val prime_divides_prime_power = store_thm( - "prime_divides_prime_power", - ``!m n k. prime m /\ prime n /\ m divides (n ** k) ==> (m = n)``, - rpt strip_tac >> - Induct_on `k` >| [ - rpt strip_tac >> - `1 < m` by rw[ONE_LT_PRIME] >> - `m = 1` by metis_tac[EXP, DIVIDES_ONE] >> - decide_tac, - metis_tac[EXP, P_EUCLIDES, prime_divides_only_self] - ]); - -(* This is better than FACTOR_OUT_PRIME *) - -(* Theorem: 0 < n /\ prime p ==> ?q m. (n = (p ** m) * q) /\ coprime p q *) -(* Proof: - If p divides n, - Then ?m. 0 < m /\ p ** m divides n /\ - !k. coprime (p ** k) (n DIV p ** m) by FACTOR_OUT_PRIME - Let q = n DIV (p ** m). - Note 0 < p by PRIME_POS - so 0 < p ** m by EXP_POS, 0 < p - Take this q and m, - Then n = (p ** m) * q by DIVIDES_EQN_COMM - and coprime p q by taking k = 1, EXP_1 - - If ~(p divides n), - Then coprime p n by prime_not_divides_coprime - Let q = n, m = 0. - Then n = 1 * q by EXP, MULT_LEFT_1 - and coprime p q. -*) -val prime_power_factor = store_thm( - "prime_power_factor", - ``!n p. 0 < n /\ prime p ==> ?q m. (n = (p ** m) * q) /\ coprime p q``, - rpt strip_tac >> - Cases_on `p divides n` >| [ - `?m. 0 < m /\ p ** m divides n /\ !k. coprime (p ** k) (n DIV p ** m)` by rw[FACTOR_OUT_PRIME] >> - qabbrev_tac `q = n DIV (p ** m)` >> - `0 < p` by rw[PRIME_POS] >> - `0 < p ** m` by rw[EXP_POS] >> - metis_tac[DIVIDES_EQN_COMM, EXP_1], - `coprime p n` by rw[prime_not_divides_coprime] >> - metis_tac[EXP, MULT_LEFT_1] - ]); - -(* Even this simple theorem is quite difficult to prove, why? *) -(* Because this needs a typical detective-style proof! *) - -(* Theorem: prime p /\ a divides (p ** n) ==> ?j. j <= n /\ (a = p ** j) *) -(* Proof: - Note 0 < p by PRIME_POS - so 0 < p ** n by EXP_POS - Thus 0 < a by ZERO_DIVIDES - ==> ?q m. (a = (p ** m) * q) /\ coprime p q by prime_power_factor - - Claim: q = 1 - Proof: By contradiction, suppose q <> 1. - Then ?t. prime t /\ t divides q by PRIME_FACTOR, q <> 1 - Now q divides a by divides_def - so t divides (p ** n) by DIVIDES_TRANS - ==> t = p by prime_divides_prime_power - But gcd t q = t by divides_iff_gcd_fix - or gcd p q = p by t = p - Yet p <> 1 by NOT_PRIME_1 - so this contradicts coprime p q. - - Thus a = p ** m by q = 1, Claim. - Note p ** m <= p ** n by DIVIDES_LE, 0 < p - and 1 < p by ONE_LT_PRIME - ==> m <= n by EXP_BASE_LE_MONO, 1 < p - Take j = m, and the result follows. -*) -val prime_power_divisor = store_thm( - "prime_power_divisor", - ``!p n a. prime p /\ a divides (p ** n) ==> ?j. j <= n /\ (a = p ** j)``, - rpt strip_tac >> - `0 < p` by rw[PRIME_POS] >> - `0 < p ** n` by rw[EXP_POS] >> - `0 < a` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - `?q m. (a = (p ** m) * q) /\ coprime p q` by rw[prime_power_factor] >> - `q = 1` by - (spose_not_then strip_assume_tac >> - `?t. prime t /\ t divides q` by rw[PRIME_FACTOR] >> - `q divides a` by metis_tac[divides_def] >> - `t divides (p ** n)` by metis_tac[DIVIDES_TRANS] >> - `t = p` by metis_tac[prime_divides_prime_power] >> - `gcd t q = t` by rw[GSYM divides_iff_gcd_fix] >> - metis_tac[NOT_PRIME_1]) >> - `a = p ** m` by rw[] >> - metis_tac[DIVIDES_LE, EXP_BASE_LE_MONO, ONE_LT_PRIME]); - -(* Theorem: prime p /\ prime q ==> - !m n. 0 < m /\ (p ** m = q ** n) ==> (p = q) /\ (m = n) *) -(* Proof: - First goal: p = q. - Since p divides p by DIVIDES_REFL - ==> p divides p ** m by divides_exp, 0 < m. - so p divides q ** n by given, p ** m = q ** n - Hence p = q by prime_divides_prime_power - Second goal: m = n. - Note p = q by first goal. - Since 1 < p by ONE_LT_PRIME - Hence m = n by EXP_BASE_INJECTIVE, 1 < p -*) -val prime_powers_eq = store_thm( - "prime_powers_eq", - ``!p q. prime p /\ prime q ==> - !m n. 0 < m /\ (p ** m = q ** n) ==> (p = q) /\ (m = n)``, - ntac 6 strip_tac >> - conj_asm1_tac >- - metis_tac[divides_exp, prime_divides_prime_power, DIVIDES_REFL] >> - metis_tac[EXP_BASE_INJECTIVE, ONE_LT_PRIME]); - -(* Theorem: prime p /\ prime q /\ p <> q ==> !m n. coprime (p ** m) (q ** n) *) -(* Proof: - Let d = gcd (p ** m) (q ** n). - By contradiction, d <> 1. - Then d divides (p ** m) /\ d divides (q ** n) by GCD_PROPERTY - ==> ?j. j <= m /\ (d = p ** j) by prime_power_divisor, prime p - and ?k. k <= n /\ (d = q ** k) by prime_power_divisor, prime q - Note j <> 0 /\ k <> 0 by EXP_0 - or 0 < j /\ 0 < k by arithmetic - ==> p = q, which contradicts p <> q by prime_powers_eq -*) -val prime_powers_coprime = store_thm( - "prime_powers_coprime", - ``!p q. prime p /\ prime q /\ p <> q ==> !m n. coprime (p ** m) (q ** n)``, - spose_not_then strip_assume_tac >> - qabbrev_tac `d = gcd (p ** m) (q ** n)` >> - `d divides (p ** m) /\ d divides (q ** n)` by metis_tac[GCD_PROPERTY] >> - metis_tac[prime_power_divisor, prime_powers_eq, EXP_0, NOT_ZERO_LT_ZERO]); - -(* -val prime_powers_eq = |- !p q. prime p /\ prime q ==> !m n. 0 < m /\ (p ** m = q ** n) ==> (p = q) /\ (m = n): thm -*) - -(* Theorem: prime p /\ prime q ==> !m n. 0 < m ==> ((p ** m divides q ** n) <=> (p = q) /\ (m <= n)) *) -(* Proof: - If part: p ** m divides q ** n ==> (p = q) /\ m <= n - Note p divides (p ** m) by prime_divides_self_power, 0 < m - so p divides (q ** n) by DIVIDES_TRANS - Thus p = q by prime_divides_prime_power - Note 1 < p by ONE_LT_PRIME - Thus m <= n by power_divides_iff - Only-if part: (p = q) /\ m <= n ==> p ** m divides q ** n - Note 1 < p by ONE_LT_PRIME - Thus p ** m divides q ** n by power_divides_iff -*) -val prime_powers_divide = store_thm( - "prime_powers_divide", - ``!p q. prime p /\ prime q ==> !m n. 0 < m ==> ((p ** m divides q ** n) <=> (p = q) /\ (m <= n))``, - metis_tac[ONE_LT_PRIME, divides_self_power, prime_divides_prime_power, power_divides_iff, DIVIDES_TRANS]); - -(* Theorem: gcd (b ** m) (b ** n) = b ** (MIN m n) *) -(* Proof: - If m = n, - LHS = gcd (b ** n) (b ** n) - = b ** n by GCD_REF - RHS = b ** (MIN n n) - = b ** n by MIN_IDEM - If m < n, - b ** n = b ** (n - m + m) by arithmetic - = b ** (n - m) * b ** m by EXP_ADD - so (b ** m) divides (b ** n) by divides_def - or gcd (b ** m) (b ** n) - = b ** m by divides_iff_gcd_fix - = b ** (MIN m n) by MIN_DEF - If ~(m < n), n < m. - Similar argument as m < n, with m n exchanged, use GCD_SYM. -*) -val gcd_powers = store_thm( - "gcd_powers", - ``!b m n. gcd (b ** m) (b ** n) = b ** (MIN m n)``, - rpt strip_tac >> - Cases_on `m = n` >- - rw[] >> - Cases_on `m < n` >| [ - `b ** n = b ** (n - m + m)` by rw[] >> - `_ = b ** (n - m) * b ** m` by rw[EXP_ADD] >> - `(b ** m) divides (b ** n)` by metis_tac[divides_def] >> - metis_tac[divides_iff_gcd_fix, MIN_DEF], - `n < m` by decide_tac >> - `b ** m = b ** (m - n + n)` by rw[] >> - `_ = b ** (m - n) * b ** n` by rw[EXP_ADD] >> - `(b ** n) divides (b ** m)` by metis_tac[divides_def] >> - metis_tac[divides_iff_gcd_fix, GCD_SYM, MIN_DEF] - ]); - -(* Theorem: lcm (b ** m) (b ** n) = b ** (MAX m n) *) -(* Proof: - If m = n, - LHS = lcm (b ** n) (b ** n) - = b ** n by LCM_REF - RHS = b ** (MAX n n) - = b ** n by MAX_IDEM - If m < n, - b ** n = b ** (n - m + m) by arithmetic - = b ** (n - m) * b ** m by EXP_ADD - so (b ** m) divides (b ** n) by divides_def - or lcm (b ** m) (b ** n) - = b ** n by divides_iff_lcm_fix - = b ** (MAX m n) by MAX_DEF - If ~(m < n), n < m. - Similar argument as m < n, with m n exchanged, use LCM_COMM. -*) -val lcm_powers = store_thm( - "lcm_powers", - ``!b m n. lcm (b ** m) (b ** n) = b ** (MAX m n)``, - rpt strip_tac >> - Cases_on `m = n` >- - rw[LCM_REF] >> - Cases_on `m < n` >| [ - `b ** n = b ** (n - m + m)` by rw[] >> - `_ = b ** (n - m) * b ** m` by rw[EXP_ADD] >> - `(b ** m) divides (b ** n)` by metis_tac[divides_def] >> - metis_tac[divides_iff_lcm_fix, MAX_DEF], - `n < m` by decide_tac >> - `b ** m = b ** (m - n + n)` by rw[] >> - `_ = b ** (m - n) * b ** n` by rw[EXP_ADD] >> - `(b ** n) divides (b ** m)` by metis_tac[divides_def] >> - metis_tac[divides_iff_lcm_fix, LCM_COMM, MAX_DEF] - ]); - -(* Theorem: 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m - 1) *) -(* Proof: - If m = n, - coprime (b ** n) (b ** n - 1) - <=> T by coprime_PRE - - Claim: !j. j < m ==> coprime (b ** j) (b ** m - 1) - Proof: b ** m - = b ** (m - j + j) by SUB_ADD - = b ** (m - j) * b ** j by EXP_ADD - Thus (b ** j) divides (b ** m) by divides_def - Now 0 < b ** m by EXP_POS - so coprime (b ** j) (PRE (b ** m)) by divides_imp_coprime_with_predecessor - or coprime (b ** j) (b ** m - 1) by PRE_SUB1 - - Given 0 < m, - b ** n - = b ** ((n DIV m) * m + n MOD m) by DIVISION - = b ** (m * (n DIV m) + n MOD m) by MULT_COMM - = b ** (m * (n DIV m)) * b ** (n MOD m) by EXP_ADD - = (b ** m) ** (n DIV m) * b ** (n MOD m) by EXP_EXP_MULT - Let z = b ** m, - Then b ** n = z ** (n DIV m) * b ** (n MOD m) - and 0 < z by EXP_POS - Since coprime z (z - 1) by coprime_PRE - ==> coprime (z ** (n DIV m)) (z - 1) by coprime_exp - gcd (b ** n) (b ** m - 1) - = gcd (z ** (n DIV m) * b ** (n MOD m)) (z - 1) - = gcd (b ** (n MOD m)) (z - 1) by GCD_SYM, GCD_CANCEL_MULT - Now (n MOD m) < m by MOD_LESS - so apply the claim to deduce the result. -*) -val coprime_power_and_power_predecessor = store_thm( - "coprime_power_and_power_predecessor", - ``!b m n. 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m - 1)``, - rpt strip_tac >> - `0 < b ** n /\ 0 < b ** m` by rw[EXP_POS] >> - Cases_on `m = n` >- - rw[coprime_PRE] >> - `!j. j < m ==> coprime (b ** j) (b ** m - 1)` by - (rpt strip_tac >> - `b ** m = b ** (m - j + j)` by rw[] >> - `_ = b ** (m - j) * b ** j` by rw[EXP_ADD] >> - `(b ** j) divides (b ** m)` by metis_tac[divides_def] >> - metis_tac[divides_imp_coprime_with_predecessor, PRE_SUB1]) >> - `b ** n = b ** ((n DIV m) * m + n MOD m)` by rw[GSYM DIVISION] >> - `_ = b ** (m * (n DIV m) + n MOD m)` by rw[MULT_COMM] >> - `_ = b ** (m * (n DIV m)) * b ** (n MOD m)` by rw[EXP_ADD] >> - `_ = (b ** m) ** (n DIV m) * b ** (n MOD m)` by rw[EXP_EXP_MULT] >> - qabbrev_tac `z = b ** m` >> - `coprime z (z - 1)` by rw[coprime_PRE] >> - `coprime (z ** (n DIV m)) (z - 1)` by rw[coprime_exp] >> - metis_tac[GCD_SYM, GCD_CANCEL_MULT, MOD_LESS]); - -(* Any counter-example? Theorem proved, no counter-example! *) -(* This is a most unexpected theorem. - At first I thought it only holds for prime base b, - but in HOL4 using the EVAL function shows it seems to hold for any base b. - As for the proof, I don't have a clue initially. - I try this idea: - For a prime base b, most likely ODD b, then ODD (b ** n) and ODD (b ** m). - But then EVEN (b ** m - 1), maybe ODD and EVEN will give coprime. - If base b is EVEN, then EVEN (b ** n) but ODD (b ** m - 1), so this can work. - However, in general ODD and EVEN do not give coprime: gcd 6 9 = 3. - Of course, if ODD and EVEN arise from powers of same base, like this theorem, then true! - Actually this follows from divides_imp_coprime_with_predecessor, sort of. - This success inspires the following theorem. -*) - -(* Theorem: 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m + 1) *) -(* Proof: - If m = n, - coprime (b ** n) (b ** n + 1) - <=> T by coprime_SUC - - Claim: !j. j < m ==> coprime (b ** j) (b ** m + 1) - Proof: b ** m - = b ** (m - j + j) by SUB_ADD - = b ** (m - j) * b ** j by EXP_ADD - Thus (b ** j) divides (b ** m) by divides_def - Now 0 < b ** m by EXP_POS - so coprime (b ** j) (SUC (b ** m)) by divides_imp_coprime_with_successor - or coprime (b ** j) (b ** m + 1) by ADD1 - - Given 0 < m, - b ** n - = b ** ((n DIV m) * m + n MOD m) by DIVISION - = b ** (m * (n DIV m) + n MOD m) by MULT_COMM - = b ** (m * (n DIV m)) * b ** (n MOD m) by EXP_ADD - = (b ** m) ** (n DIV m) * b ** (n MOD m) by EXP_EXP_MULT - Let z = b ** m, - Then b ** n = z ** (n DIV m) * b ** (n MOD m) - and 0 < z by EXP_POS - Since coprime z (z + 1) by coprime_SUC - ==> coprime (z ** (n DIV m)) (z + 1) by coprime_exp - gcd (b ** n) (b ** m + 1) - = gcd (z ** (n DIV m) * b ** (n MOD m)) (z + 1) - = gcd (b ** (n MOD m)) (z + 1) by GCD_SYM, GCD_CANCEL_MULT - Now (n MOD m) < m by MOD_LESS - so apply the claim to deduce the result. -*) -val coprime_power_and_power_successor = store_thm( - "coprime_power_and_power_successor", - ``!b m n. 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m + 1)``, - rpt strip_tac >> - `0 < b ** n /\ 0 < b ** m` by rw[EXP_POS] >> - Cases_on `m = n` >- - rw[coprime_SUC] >> - `!j. j < m ==> coprime (b ** j) (b ** m + 1)` by - (rpt strip_tac >> - `b ** m = b ** (m - j + j)` by rw[] >> - `_ = b ** (m - j) * b ** j` by rw[EXP_ADD] >> - `(b ** j) divides (b ** m)` by metis_tac[divides_def] >> - metis_tac[divides_imp_coprime_with_successor, ADD1]) >> - `b ** n = b ** ((n DIV m) * m + n MOD m)` by rw[GSYM DIVISION] >> - `_ = b ** (m * (n DIV m) + n MOD m)` by rw[MULT_COMM] >> - `_ = b ** (m * (n DIV m)) * b ** (n MOD m)` by rw[EXP_ADD] >> - `_ = (b ** m) ** (n DIV m) * b ** (n MOD m)` by rw[EXP_EXP_MULT] >> - qabbrev_tac `z = b ** m` >> - `coprime z (z + 1)` by rw[coprime_SUC] >> - `coprime (z ** (n DIV m)) (z + 1)` by rw[coprime_exp] >> - metis_tac[GCD_SYM, GCD_CANCEL_MULT, MOD_LESS]); - -(* ------------------------------------------------------------------------- *) -(* Useful Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: prime p /\ q divides (p ** n) ==> (q = 1) \/ (p divides q) *) -(* Proof: - By contradiction, suppose q <> 1 /\ ~(p divides q). - Note ?j. j <= n /\ (q = p ** j) by prime_power_divisor - and 0 < j by EXP_0, q <> 1 - then p divides q by prime_divides_self_power, 0 < j - This contradicts ~(p divides q). -*) -Theorem PRIME_EXP_FACTOR: - !p q n. prime p /\ q divides (p ** n) ==> (q = 1) \/ (p divides q) -Proof - spose_not_then strip_assume_tac >> - `?j. j <= n /\ (q = p ** j)` by rw[prime_power_divisor] >> - `0 < j` by fs[] >> - metis_tac[prime_divides_self_power] -QED - -(* Idea: For prime p, FACT (p-1) MOD p <> 0 *) - -(* Theorem: prime p /\ n < p ==> FACT n MOD p <> 0 *) -(* Proof: - Note 1 < p by ONE_LT_PRIME - By induction on n. - Base: 0 < p ==> (FACT 0 MOD p = 0) ==> F - Note FACT 0 = 1 by FACT_0 - and 1 MOD p = 1 by LESS_MOD, 1 < p - and 1 = 0 is F. - Step: n < p ==> (FACT n MOD p = 0) ==> F ==> - SUC n < p ==> (FACT (SUC n) MOD p = 0) ==> F - If n = 0, SUC 0 = 1 by ONE - Note FACT 1 = 1 by FACT_1 - and 1 MOD p = 1 by LESS_MOD, 1 < p - and 1 = 0 is F. - If n <> 0, 0 < n. - (FACT (SUC n)) MOD p = 0 - <=> (SUC n * FACT n) MOD p = 0 by FACT - Note (SUC n) MOD p <> 0 by MOD_LESS, SUC n < p - and (FACT n) MOD p <> 0 by induction hypothesis - so (SUC n * FACT n) MOD p <> 0 by EUCLID_LEMMA - This is a contradiction. -*) -Theorem FACT_MOD_PRIME: - !p n. prime p /\ n < p ==> FACT n MOD p <> 0 -Proof - rpt strip_tac >> - `1 < p` by rw[ONE_LT_PRIME] >> - Induct_on `n` >- - simp[FACT_0] >> - Cases_on `n = 0` >- - simp[FACT_1] >> - rw[FACT] >> - `n < p` by decide_tac >> - `(SUC n) MOD p <> 0` by fs[] >> - metis_tac[EUCLID_LEMMA] -QED - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/helperListScript.sml b/examples/algebra/lib/helperListScript.sml deleted file mode 100644 index d3816d4630..0000000000 --- a/examples/algebra/lib/helperListScript.sml +++ /dev/null @@ -1,8545 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Helper Theorems - a collection of useful results -- for Lists. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "helperList"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* open dependent theories *) -open pred_setTheory listTheory rich_listTheory; - -(* val _ = load "helperNumTheory"; *) -open helperNumTheory; - -(* val _ = load "helperSetTheory"; *) -open helperSetTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open arithmeticTheory dividesTheory gcdTheory; - -(* use listRange: [1 .. 3] = [1; 2; 3], [1 ..< 3] = [1; 2] *) -(* val _ = load "listRangeTheory"; *) -open listRangeTheory; -open rich_listTheory; (* for EVERY_REVERSE *) -open indexedListsTheory; (* for findi_def *) - - -(* ------------------------------------------------------------------------- *) -(* HelperList Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - m downto n = REVERSE [m .. n] - turn_exp l n = FUNPOW turn n l - POSITIVE l = !x. MEM x l ==> 0 < x - EVERY_POSITIVE l = EVERY (\k. 0 < k) l - MONO f = !x y. x <= y ==> f x <= f y - MONO2 f = !x1 y1 x2 y2. x1 <= x2 /\ y1 <= y2 ==> f x1 y1 <= f x2 y2 - MONO3 f = !x1 y1 z1 x2 y2 z2. x1 <= x2 /\ y1 <= y2 /\ z1 <= z2 ==> f x1 y1 z1 <= f x2 y2 z2 - RMONO f = !x y. x <= y ==> f y <= f x - RMONO2 f = !x1 y1 x2 y2. x1 <= x2 /\ y1 <= y2 ==> f x2 y2 <= f x1 y1 - RMONO3 f = !x1 y1 z1 x2 y2 z2. x1 <= x2 /\ y1 <= y2 /\ z1 <= z2 ==> f x2 y2 z2 <= f x1 y1 z1 - MONO_INC ls = !m n. m <= n /\ n < LENGTH ls ==> EL m ls <= EL n ls - MONO_DEC ls = !m n. m <= n /\ n < LENGTH ls ==> EL n ls <= EL m ls -*) - -(* Definitions and Theorems (# are exported): - - List Theorems: - LIST_NOT_NIL |- !ls. ls <> [] <=> (ls = HD ls::TL ls) - LIST_HEAD_TAIL |- !ls. 0 < LENGTH ls <=> (ls = HD ls::TL ls) - LIST_EQ_HEAD_TAIL|- !p q. p <> [] /\ q <> [] ==> ((p = q) <=> (HD p = HD q) /\ (TL p = TL q)) - LIST_SING_EQ |- !x y. ([x] = [y]) <=> (x = y) - LENGTH_NON_NIL |- !l. 0 < LENGTH l <=> l <> [] - LENGTH_EQ_0 |- !l. (LENGTH l = 0) <=> (l = []) - LENGTH_EQ_1 |- !l. (LENGTH l = 1) <=> ?x. l = [x] - LENGTH_SING |- !x. LENGTH [x] = 1 - LENGTH_TL_LT |- !ls. ls <> [] ==> LENGTH (TL ls) < LENGTH ls - SNOC_NIL |- !x. SNOC x [] = [x] - SNOC_CONS |- !x x' l. SNOC x (x'::l) = x'::SNOC x l - SNOC_LAST_FRONT |- !l. l <> [] ==> (l = SNOC (LAST l) (FRONT l)) - MAP_COMPOSE |- !f g l. MAP f (MAP g l) = MAP (f o g) l - MAP_SING |- !f x. MAP f [x] = [f x] - MAP_HD |- !f ls. ls <> [] ==> HD (MAP f ls) = f (HD ls) - LAST_EL_CONS |- !h t. t <> [] ==> LAST t = EL (LENGTH t) (h::t) - FRONT_LENGTH |- !l. l <> [] ==> (LENGTH (FRONT l) = PRE (LENGTH l)) - FRONT_EL |- !l n. l <> [] /\ n < LENGTH (FRONT l) ==> (EL n (FRONT l) = EL n l) - FRONT_EQ_NIL |- !l. LENGTH l = 1 ==> FRONT l = [] - FRONT_NON_NIL |- !l. 1 < LENGTH l ==> FRONT l <> [] - HEAD_MEM |- !ls. ls <> [] ==> MEM (HD ls) ls - LAST_MEM |- !ls. ls <> [] ==> MEM (LAST ls) ls - LAST_EQ_HD |- !h t. ~MEM h t /\ LAST (h::t) = h <=> t = [] - MEM_FRONT_NOT_LAST|- !ls. ls <> [] /\ ALL_DISTINCT ls ==> ~MEM (LAST ls) (FRONT ls) - NIL_NO_MEM |- !ls. ls = [] <=> !x. ~MEM x ls - MEM_APPEND_3 |- !l1 x l2 h. MEM h (l1 ++ [x] ++ l2) <=> MEM h (x::(l1 ++ l2)) - DROP_1 |- !h t. DROP 1 (h::t) = t - FRONT_SING |- !x. FRONT [x] = [] - TAIL_BY_DROP |- !ls. ls <> [] ==> TL ls = DROP 1 ls - FRONT_BY_TAKE |- !ls. ls <> [] ==> FRONT ls = TAKE (LENGTH ls - 1) ls - HD_APPEND |- !h t ls. HD (h::t ++ ls) = h - EL_TAIL |- !h t n. 0 <> n ==> (EL (n - 1) t = EL n (h::t)) - MONOLIST_SET_SING|- !c ls. ls <> [] /\ EVERY ($= c) ls ==> SING (set ls) -! LIST_TO_SET_EVAL |- !t h. set [] = {} /\ set (h::t) = h INSERT set t - set_list_eq_count|- !ls n. set ls = count n ==> !j. j < LENGTH ls ==> EL j ls < n - list_to_set_eq_el_image - |- !ls. set ls = IMAGE (\j. EL j ls) (count (LENGTH ls)) - all_distinct_list_el_inj - |- !ls. ALL_DISTINCT ls ==> INJ (\j. EL j ls) (count (LENGTH ls)) univ(:'a) - - List Reversal: - REVERSE_SING |- !x. REVERSE [x] = [x] - REVERSE_HD |- !ls. ls <> [] ==> (HD (REVERSE ls) = LAST ls) - REVERSE_TL |- !ls. ls <> [] ==> (TL (REVERSE ls) = REVERSE (FRONT ls)) - - List Index: - findi_nil |- !x. findi x [] = 0 - findi_cons |- !x h t. findi x (h::t) = if x = h then 0 else 1 + findi x t - findi_none |- !ls x. ~MEM x ls ==> findi x ls = LENGTH ls - findi_APPEND |- !l1 l2 x. findi x (l1 ++ l2) = - if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 - findi_EL_iff |- !ls x n. ALL_DISTINCT ls /\ MEM x ls /\ n < LENGTH ls ==> - (x = EL n ls <=> findi x ls = n) - - Extra List Theorems: - EVERY_ELEMENT_PROPERTY |- !p R. EVERY (\c. c IN R) p ==> !k. k < LENGTH p ==> EL k p IN R - EVERY_MONOTONIC_MAP |- !l f P Q. (!x. P x ==> (Q o f) x) /\ EVERY P l ==> EVERY Q (MAP f l) - EVERY_LT_IMP_EVERY_LE |- !ls n. EVERY (\j. j < n) ls ==> EVERY (\j. j <= n) ls - ZIP_SNOC |- !x1 x2 l1 l2. (LENGTH l1 = LENGTH l2) ==> - (ZIP (SNOC x1 l1,SNOC x2 l2) = SNOC (x1,x2) (ZIP (l1,l2))) - ZIP_MAP_MAP |- !ls f g. ZIP (MAP f ls,MAP g ls) = MAP (\x. (f x,g x)) ls - MAP2_MAP_MAP |- !ls f g1 g2. MAP2 f (MAP g1 ls) (MAP g2 ls) = MAP (\x. f (g1 x) (g2 x)) ls - EL_APPEND |- !n l1 l2. EL n (l1 ++ l2) = if n < LENGTH l1 then EL n l1 else EL (n - LENGTH l1) l2 - EL_SPLIT |- !ls j. j < LENGTH ls ==> ?l1 l2. ls = l1 ++ EL j ls::l2 - EL_SPLIT_2 |- !ls j k. j < k /\ k < LENGTH ls ==> ?l1 l2 l3. ls = l1 ++ EL j ls::l2 ++ EL k ls::l3 - EL_ALL_PROPERTY |- !h1 t1 h2 t2 P. (LENGTH (h1::t1) = LENGTH (h2::t2)) /\ - (!k. k < LENGTH (h1::t1) ==> P (EL k (h1::t1)) (EL k (h2::t2))) ==> - P h1 h2 /\ !k. k < LENGTH t1 ==> P (EL k t1) (EL k t2) - APPEND_EQ_APPEND_EQ |- !l1 l2 m1 m2. - (l1 ++ l2 = m1 ++ m2) /\ (LENGTH l1 = LENGTH m1) <=> (l1 = m1) /\ (l2 = m2) - LUPDATE_LEN |- !e n l. LENGTH (LUPDATE e n l) = LENGTH l - LUPDATE_EL |- !e n l p. p < LENGTH l ==> EL p (LUPDATE e n l) = if p = n then e else EL p l - LUPDATE_SAME_SPOT |- !ls n p q. LUPDATE q n (LUPDATE p n ls) = LUPDATE q n ls - LUPDATE_DIFF_SPOT |- !ls m n p q. m <> n ==> - LUPDATE q n (LUPDATE p m ls) = LUPDATE p m (LUPDATE q n ls) - EL_LENGTH_APPEND_0 |- !ls h t. EL (LENGTH ls) (ls ++ h::t) = h - EL_LENGTH_APPEND_1 |- !ls h k t. EL (LENGTH ls + 1) (ls ++ h::k::t) = k - LUPDATE_APPEND_0 |- !ls a h t. LUPDATE a (LENGTH ls) (ls ++ h::t) = ls ++ a::t - LUPDATE_APPEND_1 |- !ls b h k t. LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = ls ++ h::b::t - LUPDATE_APPEND_0_1 |- !ls a b h k t. LUPDATE b (LENGTH ls + 1) - (LUPDATE a (LENGTH ls) (ls ++ h::k::t)) = ls ++ a::b::t - - DROP and TAKE: - DROP_LENGTH_NIL |- !l. DROP (LENGTH l) l = [] - TL_DROP |- !ls n. n < LENGTH ls ==> TL (DROP n ls) = DROP n (TL ls) - TAKE_1_APPEND |- !x y. x <> [] ==> (TAKE 1 (x ++ y) = TAKE 1 x) - DROP_1_APPEND |- !x y. x <> [] ==> (DROP 1 (x ++ y) = DROP 1 x ++ y) - DROP_SUC |- !n x. DROP (SUC n) x = DROP 1 (DROP n x) - TAKE_SUC |- !n x. TAKE (SUC n) x = TAKE n x ++ TAKE 1 (DROP n x) - TAKE_SUC_BY_TAKE |- !k x. k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x)) - DROP_BY_DROP_SUC |- !k x. k < LENGTH x ==> (DROP k x = EL k x::DROP (SUC k) x) - DROP_HEAD_ELEMENT |- !ls n. n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u - DROP_TAKE_EQ_NIL |- !ls n. DROP n (TAKE n ls) = [] - TAKE_DROP_SWAP |- !ls m n. TAKE m (DROP n ls) = DROP n (TAKE (n + m) ls) - TAKE_LENGTH_APPEND2 |- !l1 l2 x k. TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) = l1 - LENGTH_TAKE_LE |- !n l. LENGTH (TAKE n l) <= LENGTH l - ALL_DISTINCT_TAKE |- !ls n. ALL_DISTINCT ls ==> ALL_DISTINCT (TAKE n ls) - ALL_DISTINCT_TAKE_DROP|- !ls. ALL_DISTINCT ls ==> - !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F - ALL_DISTINCT_SWAP |- !ls x y. ALL_DISTINCT (x::y::ls) <=> ALL_DISTINCT (y::x::ls) - ALL_DISTINCT_LAST_EL_IFF - |- !ls j. ALL_DISTINCT ls /\ ls <> [] /\ j < LENGTH ls ==> - (EL j ls = LAST ls <=> j + 1 = LENGTH ls) - ALL_DISTINCT_FRONT |- !ls. ls <> [] /\ ALL_DISTINCT ls ==> ALL_DISTINCT (FRONT ls) - ALL_DISTINCT_EL_APPEND - |- !ls l1 l2 j. ALL_DISTINCT ls /\ j < LENGTH ls /\ - ls = l1 ++ [EL j ls] ++ l2 ==> j = LENGTH l1 - ALL_DISTINCT_APPEND_3 |- !l1 x l2. ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) - MEM_SPLIT_APPEND_distinct - |- !l. ALL_DISTINCT l ==> - !x. MEM x l <=> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 - MEM_SPLIT_TAKE_DROP_first - |- !ls x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k + 1) ls /\ ~MEM x (TAKE k ls) - MEM_SPLIT_TAKE_DROP_last - |- !ls x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k + 1) ls /\ ~MEM x (DROP (k + 1) ls) - MEM_SPLIT_TAKE_DROP_distinct - |- !ls. ALL_DISTINCT ls ==> - !x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k + 1) ls /\ - ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k + 1) ls) - - List Filter: - FILTER_EL_IMP |- !P ls l1 l2 x. (let fs = FILTER P ls - in ls = l1 ++ x::l2 /\ P x ==> x = EL (LENGTH (FILTER P l1)) fs) - FILTER_EL_IFF |- !P ls l1 l2 x j. (let fs = FILTER P ls - in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ j < LENGTH fs ==> - (x = EL j fs <=> P x /\ j = LENGTH (FILTER P l1))) - FILTER_HD |- !P ls l1 l2 x. ls = l1 ++ x::l2 /\ P x /\ FILTER P l1 = [] ==> x = HD (FILTER P ls) - FILTER_HD_IFF |- !P ls l1 l2 x. ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> - (x = HD (FILTER P ls) <=> FILTER P l1 = []) - FILTER_LAST |- !P ls l1 l2 x. ls = l1 ++ x::l2 /\ P x /\ FILTER P l2 = [] ==> x = LAST (FILTER P ls) - FILTER_LAST_IFF |- !P ls l1 l2 x. ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> - (x = LAST (FILTER P ls) <=> FILTER P l2 = []) - FILTER_EL_NEXT |- !P ls l1 l2 l3 x y. (let fs = FILTER P ls; j = LENGTH (FILTER P l1) - in ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y /\ FILTER P l2 = [] ==> - x = EL j fs /\ y = EL (j + 1) fs) - FILTER_EL_NEXT_IFF |- !P ls l1 l2 l3 x y. (let fs = FILTER P ls; j = LENGTH (FILTER P l1) - in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> - (x = EL j fs /\ y = EL (j + 1) fs <=> FILTER P l2 = [])) - FILTER_EL_NEXT_IDX |- !P ls l1 l2 l3 x y. (let fs = FILTER P ls - in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> - (findi y fs = 1 + findi x fs <=> FILTER P l2 = [])) - - List Rotation: - rotate_def |- !n l. rotate n l = DROP n l ++ TAKE n l - rotate_shift_element |- !l n. n < LENGTH l ==> (rotate n l = EL n l::(DROP (SUC n) l ++ TAKE n l)) - rotate_0 |- !l. rotate 0 l = l - rotate_nil |- !n. rotate n [] = [] - rotate_full |- !l. rotate (LENGTH l) l = l - rotate_suc |- !l n. n < LENGTH l ==> (rotate (SUC n) l = rotate 1 (rotate n l)) - rotate_same_length |- !l n. LENGTH (rotate n l) = LENGTH l - rotate_same_set |- !l n. set (rotate n l) = set l - rotate_add |- !n m l. n + m <= LENGTH l ==> (rotate n (rotate m l) = rotate (n + m) l) - rotate_lcancel |- !k l. k < LENGTH l ==> (rotate (LENGTH l - k) (rotate k l) = l) - rotate_rcancel |- !k l. k < LENGTH l ==> (rotate k (rotate (LENGTH l - k) l) = l) - - List Turn: - turn_def |- !l. turn l = if l = [] then [] else LAST l::FRONT l - turn_nil |- turn [] = [] - turn_not_nil |- !l. l <> [] ==> (turn l = LAST l::FRONT l) - turn_length |- !l. LENGTH (turn l) = LENGTH l - turn_eq_nil |- !p. (turn p = []) <=> (p = []) - head_turn |- !ls. ls <> [] ==> HD (turn ls) = LAST ls - tail_turn |- !ls. ls <> [] ==> (TL (turn ls) = FRONT ls) - turn_snoc |- !ls x. turn (SNOC x ls) = x::ls - turn_exp_0 |- !l. turn_exp l 0 = l - turn_exp_1 |- !l. turn_exp l 1 = turn l - turn_exp_2 |- !l. turn_exp l 2 = turn (turn l) - turn_exp_SUC |- !l n. turn_exp l (SUC n) = turn_exp (turn l) n - turn_exp_suc |- !l n. turn_exp l (SUC n) = turn (turn_exp l n) - turn_exp_length |- !l n. LENGTH (turn_exp l n) = LENGTH l - head_turn_exp |- !ls n. n < LENGTH ls ==> - HD (turn_exp ls n) = EL (if n = 0 then 0 else (LENGTH ls - n)) ls - - Unit-List and Mono-List: - LIST_TO_SET_SING |- !l. (LENGTH l = 1) ==> SING (set l) - MONOLIST_EQ |- !l1 l2. SING (set l1) /\ SING (set l2) ==> - ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) - NON_MONO_TAIL_PROPERTY |- !l. ~SING (set (h::t)) ==> ?h'. MEM h' t /\ h' <> h - - GENLIST Theorems: - GENLIST_0 |- !f. GENLIST f 0 = [] - GENLIST_1 |- !f. GENLIST f 1 = [f 0] - GENLIST_EQ |- !f1 f2 n. (!m. m < n ==> f1 m = f2 m) ==> GENLIST f1 n = GENLIST f2 n - GENLIST_EQ_NIL |- !f n. (GENLIST f n = []) <=> (n = 0) - GENLIST_LAST |- !f n. LAST (GENLIST f (SUC n)) = f n - GENLIST_CONSTANT |- !f n c. (!k. k < n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f n) - GENLIST_K_CONS |- !e n. GENLIST (K e) (SUC n) = e::GENLIST (K e) n - GENLIST_K_ADD |- !e n m. GENLIST (K e) (n + m) = GENLIST (K e) m ++ GENLIST (K e) n - GENLIST_K_LESS |- !f e n. (!k. k < n ==> (f k = e)) ==> (GENLIST f n = GENLIST (K e) n) - GENLIST_K_RANGE |- !f e n. (!k. 0 < k /\ k <= n ==> (f k = e)) ==> (GENLIST (f o SUC) n = GENLIST (K e) n) - GENLIST_K_APPEND |- !a b c. GENLIST (K c) a ++ GENLIST (K c) b = GENLIST (K c) (a + b) - GENLIST_K_APPEND_K |- !c n. GENLIST (K c) n ++ [c] = [c] ++ GENLIST (K c) n - GENLIST_K_MEM |- !x c n. 0 < n ==> (MEM x (GENLIST (K c) n) <=> (x = c)) - GENLIST_K_SET |- !c n. 0 < n ==> (set (GENLIST (K c) n) = {c}) - LIST_TO_SET_SING_IFF|- !ls. ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) - - SUM Theorems: - SUM_NIL |- SUM [] = 0 - SUM_CONS |- !h t. SUM (h::t) = h + SUM t - SUM_SING |- !n. SUM [n] = n - SUM_MULT |- !s k. k * SUM s = SUM (MAP ($* k) s) - SUM_RIGHT_ADD_DISTRIB |- !s m n. (m + n) * SUM s = SUM (MAP ($* m) s) + SUM (MAP ($* n) s) - SUM_LEFT_ADD_DISTRIB |- !s m n. SUM s * (m + n) = SUM (MAP ($* m) s) + SUM (MAP ($* n) s) - - SUM_GENLIST |- !f n. SUM (GENLIST f n) = SIGMA f (count n) - SUM_DECOMPOSE_FIRST |- !f n. SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) n) - SUM_DECOMPOSE_LAST |- !f n. SUM (GENLIST f (SUC n)) = SUM (GENLIST f n) + f n - SUM_ADD_GENLIST |- !a b n. SUM (GENLIST a n) + SUM (GENLIST b n) = - SUM (GENLIST (\k. a k + b k) n) - SUM_GENLIST_APPEND |- !a b n. SUM (GENLIST a n ++ GENLIST b n) = SUM (GENLIST (\k. a k + b k) n) - SUM_DECOMPOSE_FIRST_LAST |- !f n. 0 < n ==> - (SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n) - SUM_MOD |- !n. 0 < n ==> !l. (SUM l) MOD n = (SUM (MAP (\x. x MOD n) l)) MOD n - SUM_EQ_0 |- !l. (SUM l = 0) <=> EVERY (\x. x = 0) l - SUM_GENLIST_MOD |- !n. 0 < n ==> !f. SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = - SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n - SUM_CONSTANT |- !n x. SUM (GENLIST (\j. x) n) = n * x - SUM_GENLIST_K |- !m n. SUM (GENLIST (K m) n) = m * n - SUM_LE |- !l1 l2. (LENGTH l1 = LENGTH l2) /\ - (!k. k < LENGTH l1 ==> EL k l1 <= EL k l2) ==> SUM l1 <= SUM l2 - SUM_LE_MEM |- !l x. MEM x l ==> x <= SUM l: - SUM_LE_EL |- !l n. n < LENGTH l ==> EL n l <= SUM l - SUM_LE_SUM_EL |- !l m n. m < n /\ n < LENGTH l ==> EL m l + EL n l <= SUM l - SUM_DOUBLING_LIST |- !m n. SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1) - list_length_le_sum|- !ls. EVERY_POSITIVE ls ==> LENGTH ls <= SUM ls - list_length_eq_sum|- !ls. EVERY_POSITIVE ls /\ LENGTH ls = SUM ls ==> EVERY (\x. x = 1) ls - - Maximum of a List: - MAX_LIST_def |- (MAX_LIST [] = 0) /\ !h t. MAX_LIST (h::t) = MAX h (MAX_LIST t) -# MAX_LIST_NIL |- MAX_LIST [] = 0 -# MAX_LIST_CONS |- !h t. MAX_LIST (h::t) = MAX h (MAX_LIST t) - MAX_LIST_SING |- !x. MAX_LIST [x] = x - MAX_LIST_EQ_0 |- !l. (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l - MAX_LIST_MEM |- !l. l <> [] ==> MEM (MAX_LIST l) l - MAX_LIST_PROPERTY |- !l x. MEM x l ==> x <= MAX_LIST l - MAX_LIST_TEST |- !l. l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> y <= x) ==> (x = MAX_LIST l) - MAX_LIST_LE |- !h t. MAX_LIST t <= MAX_LIST (h::t) - MAX_LIST_MONO_MAP |- !f. (!x y. x <= y ==> f x <= f y) ==> - !ls. ls <> [] ==> MAX_LIST (MAP f ls) = f (MAX_LIST ls) - - Minimum of a List: - MIN_LIST_def |- !h t. MIN_LIST (h::t) = if t = [] then h else MIN h (MIN_LIST t) -# MIN_LIST_SING |- !x. MIN_LIST [x] = x -# MIN_LIST_CONS |- !h t. t <> [] ==> (MIN_LIST (h::t) = MIN h (MIN_LIST t)) - MIN_LIST_MEM |- !l. l <> [] ==> MEM (MIN_LIST l) l - MIN_LIST_PROPERTY |- !l. l <> [] ==> !x. MEM x l ==> MIN_LIST l <= x - MIN_LIST_TEST |- !l. l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> x <= y) ==> (x = MIN_LIST l) - MIN_LIST_LE_MAX_LIST |- !l. l <> [] ==> MIN_LIST l <= MAX_LIST l - MIN_LIST_LE |- !h t. t <> [] ==> MIN_LIST (h::t) <= MIN_LIST t - MIN_LIST_MONO_MAP |- !f. (!x y. x <= y ==> f x <= f y) ==> - !ls. ls <> [] ==> MIN_LIST (MAP f ls) = f (MIN_LIST ls) - - List Nub and Set: - nub_nil |- nub [] = [] - nub_cons |- !x l. nub (x::l) = if MEM x l then nub l else x::nub l - nub_sing |- !x. nub [x] = [x] - nub_all_distinct |- !l. ALL_DISTINCT (nub l) - CARD_LIST_TO_SET_EQ |- !l. CARD (set l) = LENGTH (nub l) - MONO_LIST_TO_SET |- !x. set [x] = {x} - DISTINCT_LIST_TO_SET_EQ_SING |- !l x. ALL_DISTINCT l /\ (set l = {x}) <=> (l = [x]) - LIST_TO_SET_REDUCTION |- !l1 l2 h. ~MEM h l1 /\ (set (h::l1) = set l2) ==> - ?p1 p2. ~MEM h p1 /\ ~MEM h p2 /\ (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2)) - - Constant List and Padding: - PAD_LEFT_NIL |- !n c. PAD_LEFT c n [] = GENLIST (K c) n - PAD_RIGHT_NIL |- !n c. PAD_RIGHT c n [] = GENLIST (K c) n - PAD_LEFT_LENGTH |- !n c s. LENGTH (PAD_LEFT c n s) = MAX n (LENGTH s) - PAD_RIGHT_LENGTH |- !n c s. LENGTH (PAD_RIGHT c n s) = MAX n (LENGTH s) - PAD_LEFT_ID |- !l c n. n <= LENGTH l ==> (PAD_LEFT c n l = l) - PAD_RIGHT_ID |- !l c n. n <= LENGTH l ==> (PAD_RIGHT c n l = l) - PAD_LEFT_0 |- !l c. PAD_LEFT c 0 l = l - PAD_RIGHT_0 |- !l c. PAD_RIGHT c 0 l = l - PAD_LEFT_CONS |- !l n. LENGTH l <= n ==> !c. PAD_LEFT c (SUC n) l = c::PAD_LEFT c n l - PAD_RIGHT_SNOC |- !l n. LENGTH l <= n ==> !c. PAD_RIGHT c (SUC n) l = SNOC c (PAD_RIGHT c n l) - PAD_RIGHT_CONS |- !h t c n. h::PAD_RIGHT c n t = PAD_RIGHT c (SUC n) (h::t) - PAD_LEFT_LAST |- !l c n. l <> [] ==> (LAST (PAD_LEFT c n l) = LAST l) - PAD_LEFT_EQ_NIL |- !l c n. (PAD_LEFT c n l = []) <=> (l = []) /\ (n = 0) - PAD_RIGHT_EQ_NIL |- !l c n. (PAD_RIGHT c n l = []) <=> (l = []) /\ (n = 0) - PAD_LEFT_NIL_EQ |- !n c. 0 < n ==> (PAD_LEFT c n [] = PAD_LEFT c n [c]) - PAD_RIGHT_NIL_EQ |- !n c. 0 < n ==> (PAD_RIGHT c n [] = PAD_RIGHT c n [c]) - PAD_RIGHT_BY_RIGHT|- !ls c n. PAD_RIGHT c n ls = ls ++ PAD_RIGHT c (n - LENGTH ls) [] - PAD_RIGHT_BY_LEFT |- !ls c n. PAD_RIGHT c n ls = ls ++ PAD_LEFT c (n - LENGTH ls) [] - PAD_LEFT_BY_RIGHT |- !ls c n. PAD_LEFT c n ls = PAD_RIGHT c (n - LENGTH ls) [] ++ ls - PAD_LEFT_BY_LEFT |- !ls c n. PAD_LEFT c n ls = PAD_LEFT c (n - LENGTH ls) [] ++ ls - - PROD for List, similar to SUM for List: - POSITIVE_THM |- !ls. EVERY_POSITIVE ls <=> POSITIVE ls -# PROD |- (PROD [] = 1) /\ !h t. PROD (h::t) = h * PROD t - PROD_NIL |- PROD [] = 1 - PROD_CONS |- !h t. PROD (h::t) = h * PROD t - PROD_SING |- !n. PROD [n] = n - PROD_eval |- !ls. PROD ls = if ls = [] then 1 else HD ls * PROD (TL ls) - PROD_eq_1 |- !ls. (PROD ls = 1) <=> !x. MEM x ls ==> (x = 1) - PROD_SNOC |- !x l. PROD (SNOC x l) = PROD l * x - PROD_APPEND |- !l1 l2. PROD (l1 ++ l2) = PROD l1 * PROD l2 - PROD_MAP_FOLDL |- !ls f. PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls - PROD_IMAGE_eq_PROD_MAP_SET_TO_LIST |- !s. FINITE s ==> !f. PI f s = PROD (MAP f (SET_TO_LIST s)) - PROD_ACC_DEF |- (!acc. PROD_ACC [] acc = acc) /\ - !h t acc. PROD_ACC (h::t) acc = PROD_ACC t (h * acc) - PROD_ACC_PROD_LEM |- !L n. PROD_ACC L n = PROD L * n - PROD_PROD_ACC |- !L. PROD L = PROD_ACC L 1 - PROD_GENLIST_K |- !m n. PROD (GENLIST (K m) n) = m ** n - PROD_CONSTANT |- !n x. PROD (GENLIST (\j. x) n) = x ** n - PROD_EQ_0 |- !l. (PROD l = 0) <=> MEM 0 l - PROD_POS |- !l. EVERY_POSITIVE l ==> 0 < PROD l - PROD_POS_ALT |- !l. POSITIVE l ==> 0 < PROD l - PROD_SQUARING_LIST|- !m n. PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1) - - Range Conjunction and Disjunction: - every_range_sing |- !a j. a <= j /\ j <= a <=> (j = a) - every_range_cons |- !f a b. a <= b ==> - ((!j. a <= j /\ j <= b ==> f j) <=> - f a /\ !j. a + 1 <= j /\ j <= b ==> f j) - every_range_split_head - |- !f a b. a <= b ==> - ((!j. PRE a <= j /\ j <= b ==> f j) <=> - f (PRE a) /\ !j. a <= j /\ j <= b ==> f j) - every_range_split_last - |- !f a b. a <= b ==> - ((!j. a <= j /\ j <= SUC b ==> f j) <=> - f (SUC b) /\ !j. a <= j /\ j <= b ==> f j) - every_range_less_ends - |- !f a b. a <= b ==> - ((!j. a <= j /\ j <= b ==> f j) <=> - f a /\ f b /\ !j. a < j /\ j < b ==> f j) - every_range_span_max|- !f a b. a < b /\ f a /\ ~f b ==> - ?m. a <= m /\ m < b /\ - (!j. a <= j /\ j <= m ==> f j) /\ ~f (SUC m) - every_range_span_min|- !f a b. a < b /\ ~f a /\ f b ==> - ?m. a < m /\ m <= b /\ - (!j. m <= j /\ j <= b ==> f j) /\ ~f (PRE m) - exists_range_sing |- !a. ?j. a <= j /\ j <= a <=> (j = a) - exists_range_cons |- !f a b. a <= b ==> - ((?j. a <= j /\ j <= b /\ f j) <=> - f a \/ ?j. a + 1 <= j /\ j <= b /\ f j) - - List Range: - listRangeINC_to_LHI |- !m n. [m .. n] = [m ..< SUC n] - listRangeINC_SET |- !n. set [1 .. n] = IMAGE SUC (count n) - listRangeINC_LEN |- !m n. LENGTH [m .. n] = n + 1 - m - listRangeINC_NIL |- !m n. ([m .. n] = []) <=> n + 1 <= m - listRangeINC_MEM |- !m n x. MEM x [m .. n] <=> m <= x /\ x <= n - listRangeINC_EL |- !m n i. m + i <= n ==> EL i [m .. n] = m + i - listRangeINC_EVERY |- !P m n. EVERY P [m .. n] <=> !x. m <= x /\ x <= n ==> P x - listRangeINC_EXISTS |- !P m n. EXISTS P [m .. n] <=> ?x. m <= x /\ x <= n /\ P x - listRangeINC_EVERY_EXISTS |- !P m n. EVERY P [m .. n] <=> ~EXISTS ($~ o P) [m .. n] - listRangeINC_EXISTS_EVERY |- !P m n. EXISTS P [m .. n] <=> ~EVERY ($~ o P) [m .. n] - listRangeINC_SNOC |- !m n. m <= n + 1 ==> ([m .. n + 1] = SNOC (n + 1) [m .. n]) - listRangeINC_FRONT |- !m n. m <= n + 1 ==> (FRONT [m .. n + 1] = [m .. n]) - listRangeINC_LAST |- !m n. m <= n ==> (LAST [m .. n] = n) - listRangeINC_REVERSE |- !m n. REVERSE [m .. n] = MAP (\x. n - x + m) [m .. n] - listRangeINC_REVERSE_MAP |- !f m n. REVERSE (MAP f [m .. n]) = MAP (f o (\x. n - x + m)) [m .. n] - listRangeINC_MAP_SUC |- !f m n. MAP f [m + 1 .. n + 1] = MAP (f o SUC) [m .. n] - listRangeINC_APPEND |- !a b c. a <= b /\ b <= c ==> ([a .. b] ++ [b + 1 .. c] = [a .. c]) - listRangeINC_SUM |- !m n. SUM [m .. n] = SUM [1 .. n] - SUM [1 .. m - 1] - listRangeINC_PROD_pos |- !m n. 0 < m ==> 0 < PROD [m .. n] - listRangeINC_PROD |- !m n. 0 < m /\ m <= n ==> (PROD [m .. n] = PROD [1 .. n] DIV PROD [1 .. m - 1]) - listRangeINC_has_divisors |- !m n x. 0 < n /\ m <= x /\ x divides n ==> MEM x [m .. n] - listRangeINC_1_n |- !n. [1 .. n] = GENLIST SUC n - listRangeINC_MAP |- !f n. MAP f [1 .. n] = GENLIST (f o SUC) n - listRangeINC_SUM_MAP |- !f n. SUM (MAP f [1 .. SUC n]) = f (SUC n) + SUM (MAP f [1 .. n]) - listRangeINC_SPLIT |- !m n j. m < j /\ j <= n ==> [m .. n] = [m .. j - 1] ++ j::[j + 1 .. n] - - listRangeLHI_to_INC |- !m n. [m ..< n + 1] = [m .. n] - listRangeLHI_SET |- !n. set [0 ..< n] = count n - listRangeLHI_LEN |- !m n. LENGTH [m ..< n] = n - m - listRangeLHI_NIL |- !m n. [m ..< n] = [] <=> n <= m - listRangeLHI_MEM |- !m n x. MEM x [m ..< n] <=> m <= x /\ x < n - listRangeLHI_EL |- !m n i. m + i < n ==> EL i [m ..< n] = m + i - listRangeLHI_EVERY |- !P m n. EVERY P [m ..< n] <=> !x. m <= x /\ x < n ==> P x - listRangeLHI_SNOC |- !m n. m <= n ==> [m ..< n + 1] = SNOC n [m ..< n] - listRangeLHI_FRONT |- !m n. m <= n ==> (FRONT [m ..< n + 1] = [m ..< n]) - listRangeLHI_LAST |- !m n. m <= n ==> (LAST [m ..< n + 1] = n) - listRangeLHI_REVERSE |- !m n. REVERSE [m ..< n] = MAP (\x. n - 1 - x + m) [m ..< n] - listRangeLHI_REVERSE_MAP |- !f m n. REVERSE (MAP f [m ..< n]) = MAP (f o (\x. n - 1 - x + m)) [m ..< n] - listRangeLHI_MAP_SUC |- !f m n. MAP f [m + 1 ..< n + 1] = MAP (f o SUC) [m ..< n] - listRangeLHI_APPEND |- !a b c. a <= b /\ b <= c ==> [a ..< b] ++ [b ..< c] = [a ..< c] - listRangeLHI_SUM |- !m n. SUM [m ..< n] = SUM [1 ..< n] - SUM [1 ..< m] - listRangeLHI_PROD_pos |- !m n. 0 < m ==> 0 < PROD [m ..< n] - listRangeLHI_PROD |- !m n. 0 < m /\ m <= n ==> PROD [m ..< n] = PROD [1 ..< n] DIV PROD [1 ..< m] - listRangeLHI_has_divisors |- !m n x. 0 < n /\ m <= x /\ x divides n ==> MEM x [m ..< n + 1] - listRangeLHI_0_n |- !n. [0 ..< n] = GENLIST I n - listRangeLHI_MAP |- !f n. MAP f [0 ..< n] = GENLIST f n - listRangeLHI_SUM_MAP |- !f n. SUM (MAP f [0 ..< SUC n]) = f n + SUM (MAP f [0 ..< n]) - listRangeLHI_SPLIT |- !m n j. m <= j /\ j < n ==> [m ..< n] = [m ..< j] ++ j::[j + 1 ..< n] - - listRangeINC_ALL_DISTINCT |- !m n. ALL_DISTINCT [m .. n] - listRangeINC_EVERY_split_head |- !P m n. m <= n ==> (EVERY P [m - 1 .. n] <=> P (m - 1) /\ EVERY P [m .. n]) - listRangeINC_EVERY_split_last |- !P m n. m <= n ==> (EVERY P [m .. n + 1] <=> P (n + 1) /\ EVERY P [m .. n]) - listRangeINC_EVERY_less_last |- !P m n. m <= n ==> (EVERY P [m .. n] <=> P n /\ EVERY P [m ..< n]) - listRangeINC_EVERY_span_max |- !P m n. m < n /\ P m /\ ~P n ==> - ?k. m <= k /\ k < n /\ EVERY P [m .. k] /\ ~P (SUC k) - listRangeINC_EVERY_span_min |- !P m n. m < n /\ ~P m /\ P n ==> - ?k. m < k /\ k <= n /\ EVERY P [k .. n] /\ ~P (PRE k) - - List Summation and Product: - sum_1_to_n_eq_tri_n |- !n. SUM [1 .. n] = tri n - sum_1_to_n_eqn |- !n. SUM [1 .. n] = HALF (n * (n + 1)) - sum_1_to_n_double |- !n. TWICE (SUM [1 .. n]) = n * (n + 1) - prod_1_to_n_eq_fact_n |- !n. PROD [1 .. n] = FACT n - power_predecessor_eqn |- !t n. t ** n - 1 = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n]) - geometric_sum_eqn |- !t n. 1 < t ==> SUM (MAP (\j. t ** j) [0 ..< n]) = (t ** n - 1) DIV (t - 1) - geometric_sum_eqn_alt |- !t n. 1 < t ==> SUM (MAP (\j. t ** j) [0 .. n]) = (t ** (n + 1) - 1) DIV (t - 1) - arithmetic_sum_eqn |- !n. SUM [1 ..< n] = HALF (n * (n - 1)) - arithmetic_sum_eqn_alt |- !n. SUM [1 .. n] = HALF (n * (n + 1)) - SUM_GENLIST_REVERSE |- !f n. SUM (GENLIST (\j. f (n - j)) n) = SUM (MAP f [1 .. n]) - SUM_IMAGE_count |- !f n. SIGMA f (count n) = SUM (MAP f [0 ..< n]) - SUM_IMAGE_upto |- !f n. SIGMA f (count (SUC n)) = SUM (MAP f [0 .. n]) - - MAP of function with 3 list arguments: - MAP3_DEF |- (!t3 t2 t1 h3 h2 h1 f. - MAP3 f (h1::t1) (h2::t2) (h3::t3) = f h1 h2 h3::MAP3 f t1 t2 t3) /\ - (!z y f. MAP3 f [] y z = []) /\ - (!z v5 v4 f. MAP3 f (v4::v5) [] z = []) /\ - !v5 v4 v13 v12 f. MAP3 f (v4::v5) (v12::v13) [] = [] - MAP3 |- (!f. MAP3 f [] [] [] = []) /\ - !f h1 t1 h2 t2 h3 t3. - MAP3 f (h1::t1) (h2::t2) (h3::t3) = f h1 h2 h3::MAP3 f t1 t2 t3 - LENGTH_MAP3 |- !lx ly lz f. LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) - EL_MAP3 |- !lx ly lz n. n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz) - MEM_MAP2 |- !f x l1 l2. MEM x (MAP2 f l1 l2) ==> - ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 l2 - MEM_MAP3 |- !f x l1 l2 l3. MEM x (MAP3 f l1 l2 l3) ==> - ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3 - SUM_MAP_K |- !ls c. SUM (MAP (K c) ls) = c * LENGTH ls - SUM_MAP_K_LE|- !ls a b. a <= b ==> SUM (MAP (K a) ls) <= SUM (MAP (K b) ls) - SUM_MAP2_K |- !lx ly c. SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) - SUM_MAP3_K |- !lx ly lz c. SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) - - Bounds on Lists: - SUM_UPPER |- !ls. SUM ls <= MAX_LIST ls * LENGTH ls - SUM_LOWER |- !ls. MIN_LIST ls * LENGTH ls <= SUM ls - SUM_MAP_LE |- !f g ls. EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls) - SUM_MAP_LT |- !f g ls. EVERY (\x. f x < g x) ls /\ ls <> [] ==> SUM (MAP f ls) < SUM (MAP g ls) - MEM_MAP_UPPER |- !f. MONO f ==> !ls e. MEM e (MAP f ls) ==> e <= f (MAX_LIST ls) - MEM_MAP2_UPPER |- !f. MONO2 f ==>!lx ly e. MEM e (MAP2 f lx ly) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) - MEM_MAP3_UPPER |- !f. MONO3 f ==> - !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) - MEM_MAP_LOWER |- !f. MONO f ==> !ls e. MEM e (MAP f ls) ==> f (MIN_LIST ls) <= e - MEM_MAP2_LOWER |- !f. MONO2 f ==> !lx ly e. MEM e (MAP2 f lx ly) ==> f (MIN_LIST lx) (MIN_LIST ly) <= e - MEM_MAP3_LOWER |- !f. MONO3 f ==> - !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> f (MIN_LIST lx) (MIN_LIST ly) (MIN_LIST lz) <= e - MAX_LIST_MAP_LE |- !f g. (!x. f x <= g x) ==> - !ls. MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls) - MIN_LIST_MAP_LE |- !f g. (!x. f x <= g x) ==> - !ls. MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls) - MAP_LE |- !f g. (!x. f x <= g x) ==> !ls n. EL n (MAP f ls) <= EL n (MAP g ls) - MAP2_LE |- !f g. (!x y. f x y <= g x y) ==> - !lx ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly) - MAP3_LE |- !f g. (!x y z. f x y z <= g x y z) ==> - !lx ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz) - SUM_MONO_MAP |- !f1 f2. (!x. f1 x <= f2 x) ==> !ls. SUM (MAP f1 ls) <= SUM (MAP f2 ls) - SUM_MONO_MAP2 |- !f1 f2. (!x y. f1 x y <= f2 x y) ==> - !lx ly. SUM (MAP2 f1 lx ly) <= SUM (MAP2 f2 lx ly) - SUM_MONO_MAP3 |- !f1 f2. (!x y z. f1 x y z <= f2 x y z) ==> - !lx ly lz. SUM (MAP3 f1 lx ly lz) <= SUM (MAP3 f2 lx ly lz) - SUM_MAP_UPPER |- !f. MONO f ==> !ls. SUM (MAP f ls) <= f (MAX_LIST ls) * LENGTH ls - SUM_MAP2_UPPER |- !f. MONO2 f ==> - !lx ly. SUM (MAP2 f lx ly) <= f (MAX_LIST lx) (MAX_LIST ly) * LENGTH (MAP2 f lx ly) - SUM_MAP3_UPPER |- !f. MONO3 f ==> - !lx ly lz. SUM (MAP3 f lx ly lz) <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) * LENGTH (MAP3 f lx ly lz) - - Increasing and decreasing list bounds: - MONO_INC_NIL |- MONO_INC [] - MONO_INC_CONS |- !h t. MONO_INC (h::t) ==> MONO_INC t - MONO_INC_HD |- !h t x. MONO_INC (h::t) /\ MEM x t ==> h <= x - MONO_DEC_NIL |- MONO_DEC [] - MONO_DEC_CONS |- !h t. MONO_DEC (h::t) ==> MONO_DEC t - MONO_DEC_HD |- !h t x. MONO_DEC (h::t) /\ MEM x t ==> x <= h - GENLIST_MONO_INC |- !f n. MONO f ==> MONO_INC (GENLIST f n) - GENLIST_MONO_DEC |- !f n. RMONO f ==> MONO_DEC (GENLIST f n) - MAX_LIST_MONO_INC |- !ls. ls <> [] /\ MONO_INC ls ==> MAX_LIST ls = LAST ls - MAX_LIST_MONO_DEC |- !ls. ls <> [] /\ MONO_DEC ls ==> MAX_LIST ls = HD ls - MIN_LIST_MONO_INC |- !ls. ls <> [] /\ MONO_INC ls ==> MIN_LIST ls = HD ls - MIN_LIST_MONO_DEC |- !ls. ls <> [] /\ MONO_DEC ls ==> MIN_LIST ls = LAST ls - listRangeINC_MONO_INC |- !m n. MONO_INC [m .. n] - listRangeLHI_MONO_INC |- !m n. MONO_INC [m ..< n] - - List Dilation: - - List Dilation (Multiplicative): - MDILATE_def |- (!e n. MDILATE e n [] = []) /\ - !e n h t. MDILATE e n (h::t) = if t = [] then [h] else h::GENLIST (K e) (PRE n) ++ MDILATE e n t -# MDILATE_NIL |- !e n. MDILATE e n [] = [] -# MDILATE_SING |- !e n x. MDILATE e n [x] = [x] - MDILATE_CONS |- !e n h t. MDILATE e n (h::t) = - if t = [] then [h] else h::GENLIST (K e) (PRE n) ++ MDILATE e n t - MDILATE_1 |- !l e. MDILATE e 1 l = l - MDILATE_0 |- !l e. MDILATE e 0 l = l - MDILATE_LENGTH |- !l e n. LENGTH (MDILATE e n l) = - if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l)) - MDILATE_LENGTH_LOWER |- !l e n. LENGTH l <= LENGTH (MDILATE e n l) - MDILATE_LENGTH_UPPER |- !l e n. 0 < n ==> LENGTH (MDILATE e n l) <= SUC (n * PRE (LENGTH l)) - MDILATE_EL |- !l e n k. k < LENGTH (MDILATE e n l) ==> - (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e) - MDILATE_EQ_NIL |- !l e n. (MDILATE e n l = []) <=> (l = []) - MDILATE_LAST |- !l e n. LAST (MDILATE e n l) = LAST l - - List Dilation (Additive): - DILATE_def |- (!n m e. DILATE e n m [] = []) /\ - (!n m h e. DILATE e n m [h] = [h]) /\ - !v9 v8 n m h e. DILATE e n m (h::v8::v9) = - h:: (TAKE n (v8::v9) ++ GENLIST (K e) m ++ DILATE e n m (DROP n (v8::v9))) -# DILATE_NIL |- !n m e. DILATE e n m [] = [] -# DILATE_SING |- !n m h e. DILATE e n m [h] = [h] - DILATE_CONS |- !n m h t e. DILATE e n m (h::t) = - if t = [] then [h] else h::(TAKE n t ++ GENLIST (K e) m ++ DILATE e n m (DROP n t)) - DILATE_0_CONS |- !n h t e. DILATE e 0 n (h::t) = - if t = [] then [h] else h::(GENLIST (K e) n ++ DILATE e 0 n t) - DILATE_0_0 |- !l e. DILATE e 0 0 l = l - DILATE_0_SUC |- !l e n. DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l) - DILATE_0_LENGTH |- !l e n. LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l)) - DILATE_0_LENGTH_LOWER |- !l e n. LENGTH l <= LENGTH (DILATE e 0 n l) - DILATE_0_LENGTH_UPPER |- !l e n. LENGTH (DILATE e 0 n l) <= SUC (SUC n * PRE (LENGTH l)) - DILATE_0_EL |- !l e n k. k < LENGTH (DILATE e 0 n l) ==> - (EL k (DILATE e 0 n l) = if k MOD SUC n = 0 then EL (k DIV SUC n) l else e) - DILATE_0_EQ_NIL |- !l e n. (DILATE e 0 n l = []) <=> (l = []) - DILATE_0_LAST |- !l e n. LAST (DILATE e 0 n l) = LAST l - -*) - -(* ------------------------------------------------------------------------- *) -(* List Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: ls <> [] <=> (ls = HD ls::TL ls) *) -(* Proof: - If part: ls <> [] ==> (ls = HD ls::TL ls) - ls <> [] - ==> ?h t. ls = h::t by list_CASES - ==> ls = (HD ls)::(TL ls) by HD, TL - Only-if part: (ls = HD ls::TL ls) ==> ls <> [] - This is true by NOT_NIL_CONS -*) -val LIST_NOT_NIL = store_thm( - "LIST_NOT_NIL", - ``!ls. ls <> [] <=> (ls = HD ls::TL ls)``, - metis_tac[list_CASES, HD, TL, NOT_NIL_CONS]); - -(* NOT_NIL_EQ_LENGTH_NOT_0 |- x <> [] <=> 0 < LENGTH x *) - -(* Theorem: 0 < LENGTH ls <=> (ls = HD ls::TL ls) *) -(* Proof: - If part: 0 < LENGTH ls ==> (ls = HD ls::TL ls) - Note LENGTH ls <> 0 by arithmetic - so ~(NULL l) by NULL_LENGTH - or ls = HD ls :: TL ls by CONS - Only-if part: (ls = HD ls::TL ls) ==> 0 < LENGTH ls - Note LENGTH ls = SUC (LENGTH (TL ls)) by LENGTH - but 0 < SUC (LENGTH (TL ls)) by SUC_POS -*) -val LIST_HEAD_TAIL = store_thm( - "LIST_HEAD_TAIL", - ``!ls. 0 < LENGTH ls <=> (ls = HD ls::TL ls)``, - metis_tac[LIST_NOT_NIL, NOT_NIL_EQ_LENGTH_NOT_0]); - -(* Theorem: p <> [] /\ q <> [] ==> ((p = q) <=> ((HD p = HD q) /\ (TL p = TL q))) *) -(* Proof: by cases on p and cases on q, CONS_11 *) -val LIST_EQ_HEAD_TAIL = store_thm( - "LIST_EQ_HEAD_TAIL", - ``!p q. p <> [] /\ q <> [] ==> - ((p = q) <=> ((HD p = HD q) /\ (TL p = TL q)))``, - (Cases_on `p` >> Cases_on `q` >> fs[])); - -(* Theorem: [x] = [y] <=> x = y *) -(* Proof: by EQ_LIST and notation. *) -val LIST_SING_EQ = store_thm( - "LIST_SING_EQ", - ``!x y. ([x] = [y]) <=> (x = y)``, - rw_tac bool_ss[]); - -(* Note: There is LENGTH_NIL, but no LENGTH_NON_NIL *) - -(* Theorem: 0 < LENGTH l <=> l <> [] *) -(* Proof: - Since (LENGTH l = 0) <=> (l = []) by LENGTH_NIL - l <> [] <=> LENGTH l <> 0, - or 0 < LENGTH l by NOT_ZERO_LT_ZERO -*) -val LENGTH_NON_NIL = store_thm( - "LENGTH_NON_NIL", - ``!l. 0 < LENGTH l <=> l <> []``, - metis_tac[LENGTH_NIL, NOT_ZERO_LT_ZERO]); - -(* val LENGTH_EQ_0 = save_thm("LENGTH_EQ_0", LENGTH_EQ_NUM |> CONJUNCT1); *) -val LENGTH_EQ_0 = save_thm("LENGTH_EQ_0", LENGTH_NIL); -(* > val LENGTH_EQ_0 = |- !l. (LENGTH l = 0) <=> (l = []): thm *) - -(* Theorem: (LENGTH l = 1) <=> ?x. l = [x] *) -(* Proof: - If part: (LENGTH l = 1) ==> ?x. l = [x] - Since LENGTH l <> 0, l <> [] by LENGTH_NIL - or ?h t. l = h::t by list_CASES - and LENGTH t = 0 by LENGTH - so t = [] by LENGTH_NIL - Hence l = [x] - Only-if part: (l = [x]) ==> (LENGTH l = 1) - True by LENGTH. -*) -val LENGTH_EQ_1 = store_thm( - "LENGTH_EQ_1", - ``!l. (LENGTH l = 1) <=> ?x. l = [x]``, - rw[EQ_IMP_THM] >| [ - `LENGTH l <> 0` by decide_tac >> - `?h t. l = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - `SUC (LENGTH t) = 1` by metis_tac[LENGTH] >> - `LENGTH t = 0` by decide_tac >> - metis_tac[LENGTH_NIL], - rw[] - ]); - -(* Theorem: LENGTH [x] = 1 *) -(* Proof: by LENGTH, ONE. *) -val LENGTH_SING = store_thm( - "LENGTH_SING", - ``!x. LENGTH [x] = 1``, - rw_tac bool_ss[LENGTH, ONE]); - -(* Theorem: ls <> [] ==> LENGTH (TL ls) < LENGTH ls *) -(* Proof: by LENGTH_TL, LENGTH_EQ_0 *) -val LENGTH_TL_LT = store_thm( - "LENGTH_TL_LT", - ``!ls. ls <> [] ==> LENGTH (TL ls) < LENGTH ls``, - metis_tac[LENGTH_TL, LENGTH_EQ_0, NOT_ZERO_LT_ZERO, DECIDE``n <> 0 ==> n - 1 < n``]); - -val SNOC_NIL = save_thm("SNOC_NIL", SNOC |> CONJUNCT1); -(* > val SNOC_NIL = |- !x. SNOC x [] = [x]: thm *) -val SNOC_CONS = save_thm("SNOC_CONS", SNOC |> CONJUNCT2); -(* > val SNOC_CONS = |- !x x' l. SNOC x (x'::l) = x'::SNOC x l: thm *) - -(* Theorem: l <> [] ==> (l = SNOC (LAST l) (FRONT l)) *) -(* Proof: - l - = FRONT l ++ [LAST l] by APPEND_FRONT_LAST, l <> [] - = SNOC (LAST l) (FRONT l) by SNOC_APPEND -*) -val SNOC_LAST_FRONT = store_thm( - "SNOC_LAST_FRONT", - ``!l. l <> [] ==> (l = SNOC (LAST l) (FRONT l))``, - rw[APPEND_FRONT_LAST]); - -(* Theorem alias *) -val MAP_COMPOSE = save_thm("MAP_COMPOSE", MAP_MAP_o); -(* val MAP_COMPOSE = |- !f g l. MAP f (MAP g l) = MAP (f o g) l: thm *) - -(* Theorem: MAP f [x] = [f x] *) -(* Proof: by MAP *) -val MAP_SING = store_thm( - "MAP_SING", - ``!f x. MAP f [x] = [f x]``, - rw[]); - -(* listTheory.MAP_TL |- !l f. MAP f (TL l) = TL (MAP f l) *) - -(* Theorem: ls <> [] ==> HD (MAP f ls) = f (HD ls) *) -(* Proof: - Note 0 < LENGTH ls by LENGTH_NON_NIL - HD (MAP f ls) - = EL 0 (MAP f ls) by EL - = f (EL 0 ls) by EL_MAP, 0 < LENGTH ls - = f (HD ls) by EL -*) -Theorem MAP_HD: - !ls f. ls <> [] ==> HD (MAP f ls) = f (HD ls) -Proof - metis_tac[EL_MAP, EL, LENGTH_NON_NIL] -QED - - -(* -LAST_EL |- !ls. ls <> [] ==> LAST ls = EL (PRE (LENGTH ls)) ls -*) - -(* Theorem: t <> [] ==> (LAST t = EL (LENGTH t) (h::t)) *) -(* Proof: - Note LENGTH t <> 0 by LENGTH_EQ_0 - or 0 < LENGTH t - LAST t - = EL (PRE (LENGTH t)) t by LAST_EL - = EL (SUC (PRE (LENGTH t))) (h::t) by EL - = EL (LENGTH t) (h::t) bu SUC_PRE, 0 < LENGTH t -*) -val LAST_EL_CONS = store_thm( - "LAST_EL_CONS", - ``!h t. t <> [] ==> (LAST t = EL (LENGTH t) (h::t))``, - rpt strip_tac >> - `0 < LENGTH t` by metis_tac[LENGTH_EQ_0, NOT_ZERO_LT_ZERO] >> - `LAST t = EL (PRE (LENGTH t)) t` by rw[LAST_EL] >> - `_ = EL (SUC (PRE (LENGTH t))) (h::t)` by rw[] >> - metis_tac[SUC_PRE]); - -(* Theorem alias *) -val FRONT_LENGTH = save_thm ("FRONT_LENGTH", LENGTH_FRONT); -(* val FRONT_LENGTH = |- !l. l <> [] ==> (LENGTH (FRONT l) = PRE (LENGTH l)): thm *) - -(* Theorem: l <> [] /\ n < LENGTH (FRONT l) ==> (EL n (FRONT l) = EL n l) *) -(* Proof: by EL_FRONT, NULL *) -val FRONT_EL = store_thm( - "FRONT_EL", - ``!l n. l <> [] /\ n < LENGTH (FRONT l) ==> (EL n (FRONT l) = EL n l)``, - metis_tac[EL_FRONT, NULL, list_CASES]); - -(* Theorem: (LENGTH l = 1) ==> (FRONT l = []) *) -(* Proof: - Note ?x. l = [x] by LENGTH_EQ_1 - FRONT l - = FRONT [x] by above - = [] by FRONT_DEF -*) -val FRONT_EQ_NIL = store_thm( - "FRONT_EQ_NIL", - ``!l. (LENGTH l = 1) ==> (FRONT l = [])``, - rw[LENGTH_EQ_1] >> - rw[FRONT_DEF]); - -(* Theorem: 1 < LENGTH l ==> FRONT l <> [] *) -(* Proof: - Note LENGTH l <> 0 by 1 < LENGTH l - Thus ?h s. l = h::s by list_CASES - or 1 < 1 + LENGTH s - so 0 < LENGTH s by arithmetic - Thus ?k t. s = k::t by list_CASES - FRONT l - = FRONT (h::k::t) - = h::FRONT (k::t) by FRONT_CONS - <> [] by list_CASES -*) -val FRONT_NON_NIL = store_thm( - "FRONT_NON_NIL", - ``!l. 1 < LENGTH l ==> FRONT l <> []``, - rpt strip_tac >> - `LENGTH l <> 0` by decide_tac >> - `?h s. l = h::s` by metis_tac[list_CASES, LENGTH_EQ_0] >> - `LENGTH l = 1 + LENGTH s` by rw[] >> - `LENGTH s <> 0` by decide_tac >> - `?k t. s = k::t` by metis_tac[list_CASES, LENGTH_EQ_0] >> - `FRONT l = h::FRONT (k::t)` by fs[FRONT_CONS] >> - fs[]); - -(* Theorem: ls <> [] ==> MEM (HD ls) ls *) -(* Proof: - Note ls = h::t by list_CASES - MEM (HD (h::t)) (h::t) - <=> MEM h (h::t) by HD - <=> T by MEM -*) -val HEAD_MEM = store_thm( - "HEAD_MEM", - ``!ls. ls <> [] ==> MEM (HD ls) ls``, - (Cases_on `ls` >> simp[])); - -(* Theorem: ls <> [] ==> MEM (LAST ls) ls *) -(* Proof: - By induction on ls. - Base: [] <> [] ==> MEM (LAST []) [] - True by [] <> [] = F. - Step: ls <> [] ==> MEM (LAST ls) ls ==> - !h. h::ls <> [] ==> MEM (LAST (h::ls)) (h::ls) - If ls = [], - MEM (LAST [h]) [h] - <=> MEM h [h] by LAST_DEF - <=> T by MEM - If ls <> [], - MEM (LAST [h::ls]) (h::ls) - <=> MEM (LAST ls) (h::ls) by LAST_DEF - <=> LAST ls = h \/ MEM (LAST ls) ls by MEM - <=> LAST ls = h \/ T by induction hypothesis - <=> T by logical or -*) -val LAST_MEM = store_thm( - "LAST_MEM", - ``!ls. ls <> [] ==> MEM (LAST ls) ls``, - Induct >- - decide_tac >> - (Cases_on `ls = []` >> rw[LAST_DEF])); - -(* Idea: the last equals the head when there is no tail. *) - -(* Theorem: ~MEM h t /\ LAST (h::t) = h <=> t = [] *) -(* Proof: - If part: ~MEM h t /\ LAST (h::t) = h ==> t = [] - By contradiction, suppose t <> []. - Then h = LAST (h::t) = LAST t by LAST_CONS_cond, t <> [] - so MEM h t by LAST_MEM - This contradicts ~MEM h t. - Only-if part: t = [] ==> ~MEM h t /\ LAST (h::t) = h - Note MEM h [] = F, so ~MEM h [] = T by MEM - and LAST [h] = h by LAST_CONS -*) -Theorem LAST_EQ_HD: - !h t. ~MEM h t /\ LAST (h::t) = h <=> t = [] -Proof - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - metis_tac[LAST_CONS_cond, LAST_MEM] -QED - -(* Theorem: ls <> [] /\ ALL_DISTINCT ls ==> ~MEM (LAST ls) (FRONT ls) *) -(* Proof: - Let k = LENGTH ls. - Then 0 < k by LENGTH_EQ_0, NOT_ZERO - and LENGTH (FRONT ls) = PRE k by LENGTH_FRONT, ls <> [] - so ?n. n < PRE k /\ - LAST ls = EL n (FRONT ls) by MEM_EL - = EL n ls by FRONT_EL, ls <> [] - but LAST ls = EL (PRE k) ls by LAST_EL, ls <> [] - Thus n = PRE k by ALL_DISTINCT_EL_IMP - This contradicts n < PRE k by arithmetic -*) -Theorem MEM_FRONT_NOT_LAST: - !ls. ls <> [] /\ ALL_DISTINCT ls ==> ~MEM (LAST ls) (FRONT ls) -Proof - rpt strip_tac >> - qabbrev_tac `k = LENGTH ls` >> - `0 < k` by metis_tac[LENGTH_EQ_0, NOT_ZERO] >> - `LENGTH (FRONT ls) = PRE k` by fs[LENGTH_FRONT, Abbr`k`] >> - fs[MEM_EL] >> - `LAST ls = EL n ls` by fs[FRONT_EL] >> - `LAST ls = EL (PRE k) ls` by rfs[LAST_EL, Abbr`k`] >> - `n < k /\ PRE k < k` by decide_tac >> - `n = PRE k` by metis_tac[ALL_DISTINCT_EL_IMP] >> - decide_tac -QED - -(* Theorem: ls = [] <=> !x. ~MEM x ls *) -(* Proof: - If part: !x. ~MEM x [], true by MEM - Only-if part: !x. ~MEM x ls ==> ls = [] - By contradiction, suppose ls <> []. - Then ?h t. ls = h::t by list_CASES - and MEM h ls by MEM - which contradicts !x. ~MEM x ls. -*) -Theorem NIL_NO_MEM: - !ls. ls = [] <=> !x. ~MEM x ls -Proof - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - metis_tac[list_CASES, MEM] -QED - -(* -el_append3 -|- !l1 x l2. EL (LENGTH l1) (l1 ++ [x] ++ l2) = x -*) - -(* Theorem: MEM h (l1 ++ [x] ++ l2) <=> MEM h (x::(l1 ++ l2)) *) -(* Proof: - MEM h (l1 ++ [x] ++ l2) - <=> MEM h l1 \/ h = x \/ MEM h l2 by MEM, MEM_APPEND - <=> h = x \/ MEM h l1 \/ MEM h l2 - <=> h = x \/ MEM h (l1 ++ l2) by MEM_APPEND - <=> MEM h (x::(l1 + l2)) by MEM -*) -Theorem MEM_APPEND_3: - !l1 x l2 h. MEM h (l1 ++ [x] ++ l2) <=> MEM h (x::(l1 ++ l2)) -Proof - rw[] >> - metis_tac[] -QED - -(* Theorem: DROP 1 (h::t) = t *) -(* Proof: DROP_def *) -val DROP_1 = store_thm( - "DROP_1", - ``!h t. DROP 1 (h::t) = t``, - rw[]); - -(* Theorem: FRONT [x] = [] *) -(* Proof: FRONT_def *) -val FRONT_SING = store_thm( - "FRONT_SING", - ``!x. FRONT [x] = []``, - rw[]); - -(* Theorem: ls <> [] ==> (TL ls = DROP 1 ls) *) -(* Proof: - Note ls = h::t by list_CASES - so TL (h::t) - = t by TL - = DROP 1 (h::t) by DROP_def -*) -val TAIL_BY_DROP = store_thm( - "TAIL_BY_DROP", - ``!ls. ls <> [] ==> (TL ls = DROP 1 ls)``, - Cases_on `ls` >- - decide_tac >> - rw[]); - -(* Theorem: ls <> [] ==> (FRONT ls = TAKE (LENGTH ls - 1) ls) *) -(* Proof: - By induction on ls. - Base: [] <> [] ==> FRONT [] = TAKE (LENGTH [] - 1) [] - True by [] <> [] = F. - Step: ls <> [] ==> FRONT ls = TAKE (LENGTH ls - 1) ls ==> - !h. h::ls <> [] ==> FRONT (h::ls) = TAKE (LENGTH (h::ls) - 1) (h::ls) - If ls = [], - FRONT [h] - = [] by FRONT_SING - = TAKE 0 [h] by TAKE_0 - = TAKE (LENGTH [h] - 1) [h] by LENGTH_SING - If ls <> [], - FRONT (h::ls) - = h::FRONT ls by FRONT_DEF - = h::TAKE (LENGTH ls - 1) ls by induction hypothesis - = TAKE (LENGTH (h::ls) - 1) (h::ls) by TAKE_def -*) -val FRONT_BY_TAKE = store_thm( - "FRONT_BY_TAKE", - ``!ls. ls <> [] ==> (FRONT ls = TAKE (LENGTH ls - 1) ls)``, - Induct >- - decide_tac >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - `LENGTH ls <> 0` by rw[] >> - rw[FRONT_DEF]); - -(* Theorem: HD (h::t ++ ls) = h *) -(* Proof: - HD (h::t ++ ls) - = HD (h::(t ++ ls)) by APPEND - = h by HD -*) -Theorem HD_APPEND: - !h t ls. HD (h::t ++ ls) = h -Proof - simp[] -QED - -(* Theorem: 0 <> n ==> (EL (n-1) t = EL n (h::t)) *) -(* Proof: - Note n = SUC k for some k by num_CASES - so EL k t = EL (SUC k) (h::t) by EL_restricted -*) -Theorem EL_TAIL: - !h t n. 0 <> n ==> (EL (n-1) t = EL n (h::t)) -Proof - rpt strip_tac >> - `n = SUC (n - 1)` by decide_tac >> - metis_tac[EL_restricted] -QED - -(* Idea: If all elements are the same, the set is SING. *) - -(* Theorem: ls <> [] /\ EVERY ($= c) ls ==> SING (set ls) *) -(* Proof: - Note set ls = {c} by LIST_TO_SET_EQ_SING - thus SING (set ls) by SING_DEF -*) -Theorem MONOLIST_SET_SING: - !c ls. ls <> [] /\ EVERY ($= c) ls ==> SING (set ls) -Proof - metis_tac[LIST_TO_SET_EQ_SING, SING_DEF] -QED - -(* -> EVAL ``set [3;3;3]``; -val it = |- set [3; 3; 3] = set [3; 3; 3]: thm -*) - -(* Put LIST_TO_SET into compute *) -(* Near: put to helperList *) -Theorem LIST_TO_SET_EVAL[compute] = LIST_TO_SET |> GEN_ALL; -(* val LIST_TO_SET_EVAL = |- !t h. set [] = {} /\ set (h::t) = h INSERT set t: thm *) -(* cannot add to computeLib directly LIST_TO_SET, which is not in current theory. *) - -(* -> EVAL ``set [3;3;3]``; -val it = |- set [3; 3; 3] = {3}: thm -*) - -(* Theorem: set ls = count n ==> !j. j < LENGTH ls ==> EL j ls < n *) -(* Proof: - Note MEM (EL j ls) ls by EL_MEM - so EL j ls IN (count n) by set ls = count n - or EL j ls < n by IN_COUNT -*) -Theorem set_list_eq_count: - !ls n. set ls = count n ==> !j. j < LENGTH ls ==> EL j ls < n -Proof - metis_tac[EL_MEM, IN_COUNT] -QED - -(* Theorem: set ls = IMAGE (\j. EL j ls) (count (LENGTH ls)) *) -(* Proof: - Let f = \j. EL j ls, n = LENGTH ls. - x IN IMAGE f (count n) - <=> ?j. x = f j /\ j IN (count n) by IN_IMAGE - <=> ?j. x = EL j ls /\ j < n by notation, IN_COUNT - <=> MEM x ls by MEM_EL - <=> x IN set ls by notation - Thus set ls = IMAGE f (count n) by EXTENSION -*) -Theorem list_to_set_eq_el_image: - !ls. set ls = IMAGE (\j. EL j ls) (count (LENGTH ls)) -Proof - rw[EXTENSION] >> - metis_tac[MEM_EL] -QED - -(* Theorem: ALL_DISTINCT ls ==> INJ (\j. EL j ls) (count (LENGTH ls)) univ(:num) *) -(* Proof: - By INJ_DEF this is to show: - (1) EL j ls IN univ(:'a), true by IN_UNIV, function type - (2) !x y. x < LENGTH ls /\ y < LENGTH ls /\ EL x ls = EL y ls ==> x = y - This is true by ALL_DISTINCT_EL_IMP, ALL_DISTINCT ls -*) -Theorem all_distinct_list_el_inj: - !ls. ALL_DISTINCT ls ==> INJ (\j. EL j ls) (count (LENGTH ls)) univ(:'a) -Proof - rw[INJ_DEF, ALL_DISTINCT_EL_IMP] -QED - -(* ------------------------------------------------------------------------- *) -(* List Reversal. *) -(* ------------------------------------------------------------------------- *) - -(* Overload for REVERSE [m .. n] *) -val _ = overload_on ("downto", ``\n m. REVERSE [m .. n]``); -val _ = set_fixity "downto" (Infix(NONASSOC, 450)); (* same as relation *) - -(* Theorem: REVERSE [x] = [x] *) -(* Proof: - REVERSE [x] - = [] ++ [x] by REVERSE_DEF - = [x] by APPEND -*) -val REVERSE_SING = store_thm( - "REVERSE_SING", - ``!x. REVERSE [x] = [x]``, - rw[]); - -(* Theorem: ls <> [] ==> (HD (REVERSE ls) = LAST ls) *) -(* Proof: - HD (REVERSE ls) - = HD (REVERSE (SNOC (LAST ls) (FRONT ls))) by SNOC_LAST_FRONT - = HD (LAST ls :: (REVERSE (FRONT ls)) by REVERSE_SNOC - = LAST ls by HD -*) -Theorem REVERSE_HD: - !ls. ls <> [] ==> (HD (REVERSE ls) = LAST ls) -Proof - metis_tac[SNOC_LAST_FRONT, REVERSE_SNOC, HD] -QED - -(* Theorem: ls <> [] ==> (TL (REVERSE ls) = REVERSE (FRONT ls)) *) -(* Proof: - TL (REVERSE ls) - = TL (REVERSE (SNOC (LAST ls) (FRONT ls))) by SNOC_LAST_FRONT - = TL (LAST ls :: (REVERSE (FRONT ls)) by REVERSE_SNOC - = REVERSE (FRONT ls) by TL -*) -Theorem REVERSE_TL: - !ls. ls <> [] ==> (TL (REVERSE ls) = REVERSE (FRONT ls)) -Proof - metis_tac[SNOC_LAST_FRONT, REVERSE_SNOC, TL] -QED - -(* ------------------------------------------------------------------------- *) -(* List Index. *) -(* ------------------------------------------------------------------------- *) - -(* Extract theorems for findi *) - -Theorem findi_nil = findi_def |> CONJUNCT1; -(* val findi_nil = |- !x. findi x [] = 0: thm *) - -Theorem findi_cons = findi_def |> CONJUNCT2; -(* val findi_cons = |- !x h t. findi x (h::t) = if x = h then 0 else 1 + findi x t: thm *) - -(* Theorem: ~MEM x ls ==> findi x ls = LENGTH ls *) -(* Proof: - By induction on ls. - Base: ~MEM x [] ==> findi x [] = LENGTH [] - findi x [] - = 0 by findi_nil - = LENGTH [] by LENGTH - Step: ~MEM x ls ==> findi x ls = LENGTH ls ==> - !h. ~MEM x (h::ls) ==> findi x (h::ls) = LENGTH (h::ls) - Note ~MEM x (h::ls) - ==> x <> h /\ ~MEM x ls by MEM - Thus findi x (h::ls) - = 1 + findi x ls by findi_cons - = 1 + LENGTH ls by induction hypothesis - = SUC (LENGTH ls) by ADD1 - = LENGTH (h::ls) by LENGTH -*) -Theorem findi_none: - !ls x. ~MEM x ls ==> findi x ls = LENGTH ls -Proof - rpt strip_tac >> - Induct_on `ls` >- - simp[findi_nil] >> - simp[findi_cons] -QED - -(* Theorem: findi x (l1 ++ l2) = if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 *) -(* Proof: - By induction on l1. - Base: findi x ([] ++ l2) = if MEM x [] then findi x [] else LENGTH [] + findi x l2 - Note MEM x [] = F by MEM - so findi x ([] ++ l2) - = findi x l2 by APPEND - = 0 + findi x l2 by ADD - = LENGTH [] + findi x l2 by LENGTH - Step: findi x (l1 ++ l2) = if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 ==> - !h. findi x (h::l1 ++ l2) = if MEM x (h::l1) then findi x (h::l1) - else LENGTH (h::l1) + findi x l2 - - Note findi x (h::l1 ++ l2) - = if x = h then 0 else 1 + findi x (l1 ++ l2) by findi_cons - - Case: MEM x (h::l1). - To show: findi x (h::l1 ++ l2) = findi x (h::l1). - Note MEM x (h::l1) - <=> x = h \/ MEM x l1 by MEM - If x = h, - findi x (h::l1 ++ l2) - = 0 = findi x (h::l1) by findi_cons - If x <> h, then MEM x l1. - findi x (h::l1 ++ l2) - = 1 + findi x (l1 ++ l2) by x <> h - = 1 + findi x l1 by induction hypothesis - = findi x (h::l1) by findi_cons - - Case: ~MEM x (h::l1). - To show: findi x (h::l1 ++ l2) = LENGTH (h::l1) + findi x l2. - Note ~MEM x (h::l1) - <=> x <> h /\ ~MEM x l1 by MEM - findi x (h::l1 ++ l2) - = 1 + findi x (l1 ++ l2) by x <> h - = 1 + (LENGTH l1 + findi x l2) by induction hypothesis - = (1 + LENGTH l1) + findi x l2 by arithmetic - = LENGTH (h::l1) + findi x l2 by LENGTH -*) -Theorem findi_APPEND: - !l1 l2 x. findi x (l1 ++ l2) = if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 -Proof - rpt strip_tac >> - Induct_on `l1` >- - simp[] >> - (rw[findi_cons] >> fs[]) -QED - -(* Theorem: ALL_DISTINCT ls /\ MEM x ls /\ n < LENGTH ls ==> (x = EL n ls <=> findi x ls = n) *) -(* Proof: - If part: x = EL n ls ==> findi x ls = n - Given ALL_DISTINCT ls /\ n < LENGTH ls - This is true by findi_EL - Only-if part: findi x ls = n ==> x = EL n ls - Given MEM x ls - This is true by EL_findi -*) -Theorem findi_EL_iff: - !ls x n. ALL_DISTINCT ls /\ MEM x ls /\ n < LENGTH ls ==> (x = EL n ls <=> findi x ls = n) -Proof - metis_tac[findi_EL, EL_findi] -QED - -(* ------------------------------------------------------------------------- *) -(* Extra List Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: EVERY (\c. c IN R) p ==> !k. k < LENGTH p ==> EL k p IN R *) -(* Proof: by EVERY_EL. *) -val EVERY_ELEMENT_PROPERTY = store_thm( - "EVERY_ELEMENT_PROPERTY", - ``!p R. EVERY (\c. c IN R) p ==> !k. k < LENGTH p ==> EL k p IN R``, - rw[EVERY_EL]); - -(* Theorem: (!x. P x ==> (Q o f) x) /\ EVERY P l ==> EVERY Q (MAP f l) *) -(* Proof: - Since !x. P x ==> (Q o f) x, - EVERY P l - ==> EVERY Q o f l by EVERY_MONOTONIC - ==> EVERY Q (MAP f l) by EVERY_MAP -*) -val EVERY_MONOTONIC_MAP = store_thm( - "EVERY_MONOTONIC_MAP", - ``!l f P Q. (!x. P x ==> (Q o f) x) /\ EVERY P l ==> EVERY Q (MAP f l)``, - metis_tac[EVERY_MONOTONIC, EVERY_MAP]); - -(* Theorem: EVERY (\j. j < n) ls ==> EVERY (\j. j <= n) ls *) -(* Proof: by EVERY_EL, arithmetic. *) -val EVERY_LT_IMP_EVERY_LE = store_thm( - "EVERY_LT_IMP_EVERY_LE", - ``!ls n. EVERY (\j. j < n) ls ==> EVERY (\j. j <= n) ls``, - simp[EVERY_EL, LESS_IMP_LESS_OR_EQ]); - -(* Theorem: LENGTH l1 = LENGTH l2 ==> ZIP (SNOC x1 l1, SNOC x2 l2) = SNOC (x1, x2) ZIP (l1, l2) *) -(* Proof: - By induction on l1, - Base case: !l2. (LENGTH [] = LENGTH l2) ==> (ZIP (SNOC x1 [],SNOC x2 l2) = SNOC (x1,x2) (ZIP ([],l2))) - Since LENGTH l2 = LENGTH [] = 0, l2 = [] by LENGTH_NIL - ZIP (SNOC x1 [],SNOC x2 []) - = ZIP ([x1], [x2]) by SNOC - = ([x1], [x2]) by ZIP - = SNOC ([x1], [x2]) [] by SNOC - = SNOC ([x1], [x2]) ZIP ([][]) by ZIP - Step case: !h l2. (LENGTH (h::l1) = LENGTH l2) ==> (ZIP (SNOC x1 (h::l1),SNOC x2 l2) = SNOC (x1,x2) (ZIP (h::l1,l2))) - Expand by LENGTH_CONS, this is to show: - ZIP (h::(l1 ++ [x1]),h'::l' ++ [x2]) = ZIP (h::l1,h'::l') ++ [(x1,x2)] - ZIP (h::(l1 ++ [x1]),h'::l' ++ [x2]) - = (h, h') :: ZIP (l1 ++ [x1],l' ++ [x2]) by ZIP - = (h, h') :: ZIP (SNOC x1 l1, SNOC x2 l') by SNOC - = (h, h') :: SNOC (x1, x2) (ZIP (l1, l')) by induction hypothesis - = (h, h') :: ZIP (l1, l') ++ [(x1, x2)] by SNOC - = ZIP (h::l1, h'::l') ++ [(x1, x2)] by ZIP -*) -val ZIP_SNOC = store_thm( - "ZIP_SNOC", - ``!x1 x2 l1 l2. (LENGTH l1 = LENGTH l2) ==> (ZIP (SNOC x1 l1, SNOC x2 l2) = SNOC (x1, x2) (ZIP (l1, l2)))``, - ntac 2 strip_tac >> - Induct_on `l1` >- - rw[] >> - rw[LENGTH_CONS] >> - `ZIP (h::(l1 ++ [x1]),h'::l' ++ [x2]) = (h, h') :: ZIP (l1 ++ [x1],l' ++ [x2])` by rw[] >> - `_ = (h, h') :: ZIP (SNOC x1 l1, SNOC x2 l')` by rw[] >> - `_ = (h, h') :: SNOC (x1, x2) (ZIP (l1, l'))` by metis_tac[] >> - `_ = (h, h') :: ZIP (l1, l') ++ [(x1, x2)]` by rw[] >> - `_ = ZIP (h::l1, h'::l') ++ [(x1, x2)]` by rw[] >> - metis_tac[]); - -(* MAP_ZIP_SAME |- !ls f. MAP f (ZIP (ls,ls)) = MAP (\x. f (x,x)) ls *) - -(* Theorem: ZIP ((MAP f ls), (MAP g ls)) = MAP (\x. (f x, g x)) ls *) -(* Proof: - ZIP ((MAP f ls), (MAP g ls)) - = MAP (\(x, y). (f x, y)) (ZIP (ls, (MAP g ls))) by ZIP_MAP - = MAP (\(x, y). (f x, y)) (MAP (\(x, y). (x, g y)) (ZIP (ls, ls))) by ZIP_MAP - = MAP (\(x, y). (f x, y)) (MAP (\j. (\(x, y). (x, g y)) (j,j)) ls) by MAP_ZIP_SAME - = MAP (\(x, y). (f x, y)) o (\j. (\(x, y). (x, g y)) (j,j)) ls by MAP_COMPOSE - = MAP (\x. (f x, g x)) ls by FUN_EQ_THM -*) -val ZIP_MAP_MAP = store_thm( - "ZIP_MAP_MAP", - ``!ls f g. ZIP ((MAP f ls), (MAP g ls)) = MAP (\x. (f x, g x)) ls``, - rw[ZIP_MAP, MAP_COMPOSE] >> - qabbrev_tac `f1 = \p. (f (FST p),SND p)` >> - qabbrev_tac `f2 = \x. (x,g x)` >> - qabbrev_tac `f3 = \x. (f x,g x)` >> - `f1 o f2 = f3` by rw[FUN_EQ_THM, Abbr`f1`, Abbr`f2`, Abbr`f3`] >> - rw[]); - -(* Theorem: MAP2 f (MAP g1 ls) (MAP g2 ls) = MAP (\x. f (g1 x) (g2 x)) ls *) -(* Proof: - Let k = LENGTH ls. - Note LENGTH (MAP g1 ls) = k by LENGTH_MAP - and LENGTH (MAP g2 ls) = k by LENGTH_MAP - MAP2 f (MAP g1 ls) (MAP g2 ls) - = MAP (UNCURRY f) (ZIP ((MAP g1 ls), (MAP g2 ls))) by MAP2_MAP - = MAP (UNCURRY f) (MAP (\x. (g1 x, g2 x)) ls) by ZIP_MAP_MAP - = MAP ((UNCURRY f) o (\x. (g1 x, g2 x))) ls by MAP_COMPOSE - = MAP (\x. f (g1 x) (g2 y)) ls by FUN_EQ_THM -*) -val MAP2_MAP_MAP = store_thm( - "MAP2_MAP_MAP", - ``!ls f g1 g2. MAP2 f (MAP g1 ls) (MAP g2 ls) = MAP (\x. f (g1 x) (g2 x)) ls``, - rw[MAP2_MAP, ZIP_MAP_MAP, MAP_COMPOSE] >> - qabbrev_tac `f1 = UNCURRY f o (\x. (g1 x,g2 x))` >> - qabbrev_tac `f2 = \x. f (g1 x) (g2 x)` >> - `f1 = f2` by rw[FUN_EQ_THM, Abbr`f1`, Abbr`f2`] >> - rw[]); - -(* Theorem: EL n (l1 ++ l2) = if n < LENGTH l1 then EL n l1 else EL (n - LENGTH l1) l2 *) -(* Proof: by EL_APPEND1, EL_APPEND2 *) -val EL_APPEND = store_thm( - "EL_APPEND", - ``!n l1 l2. EL n (l1 ++ l2) = if n < LENGTH l1 then EL n l1 else EL (n - LENGTH l1) l2``, - rw[EL_APPEND1, EL_APPEND2]); - -(* Theorem: j < LENGTH ls ==> ?l1 l2. ls = l1 ++ (EL j ls)::l2 *) -(* Proof: - Let x = EL j ls. - Then MEM x ls by EL_MEM, j < LENGTH ls - so ?l1 l2. l = l1 ++ x::l2 by MEM_SPLIT - Pick these l1 and l2. -*) -Theorem EL_SPLIT: - !ls j. j < LENGTH ls ==> ?l1 l2. ls = l1 ++ (EL j ls)::l2 -Proof - metis_tac[EL_MEM, MEM_SPLIT] -QED - -(* Theorem: j < k /\ k < LENGTH ls ==> - ?l1 l2 l3. ls = l1 ++ (EL j ls)::l2 ++ (EL k ls)::l3 *) -(* Proof: - Let a = EL j ls, - b = EL k ls. - Note j < LENGTH ls by j < k, k < LENGTH ls - so MEM a ls /\ MEM b ls by MEM_EL - - Now ls - = TAKE k ls ++ DROP k ls by TAKE_DROP - = TAKE k ls ++ b::(DROP (k+1) ls) by DROP_EL_CONS - Let lt = TAKE k ls. - Then LENGTH lt = k by LENGTH_TAKE - and a = EL j lt by EL_TAKE - and lt - = TAKE j lt ++ DROP j lt by TAKE_DROP - = TAKE j lt ++ a::(DROP (j+1) lt) by DROP_EL_CONS - Pick l1 = TAKE j lt, l2 = DROP (j+1) lt, l3 = DROP (k+1) ls. -*) -Theorem EL_SPLIT_2: - !ls j k. j < k /\ k < LENGTH ls ==> - ?l1 l2 l3. ls = l1 ++ (EL j ls)::l2 ++ (EL k ls)::l3 -Proof - rpt strip_tac >> - qabbrev_tac `a = EL j ls` >> - qabbrev_tac `b = EL k ls` >> - `j < LENGTH ls` by decide_tac >> - `MEM a ls /\ MEM b ls` by metis_tac[MEM_EL] >> - `ls = TAKE k ls ++ b::(DROP (k+1) ls)` by metis_tac[TAKE_DROP, DROP_EL_CONS] >> - qabbrev_tac `lt = TAKE k ls` >> - `LENGTH lt = k` by simp[Abbr`lt`] >> - `a = EL j lt` by simp[EL_TAKE, Abbr`a`, Abbr`lt`] >> - `lt = TAKE j lt ++ a::(DROP (j+1) lt)` by metis_tac[TAKE_DROP, DROP_EL_CONS] >> - metis_tac[] -QED - -(* Theorem: (LENGTH (h1::t1) = LENGTH (h2::t2)) /\ - (!k. k < LENGTH (h1::t1) ==> P (EL k (h1::t1)) (EL k (h2::t2))) ==> - (P h1 h2) /\ (!k. k < LENGTH t1 ==> P (EL k t1) (EL k t2)) *) -(* Proof: - Put k = 0, - Then LENGTH (h1::t1) = SUC (LENGTH t1) by LENGTH - > 0 by SUC_POS - and P (EL 0 (h1::t1)) (EL 0 (h2::t2)) by implication, 0 < LENGTH (h1::t1) - or P HD (h1::t1) HD (h2::t2) by EL - or P h1 h2 by HD - Note k < LENGTH t1 - ==> k + 1 < SUC (LENGTH t1) by ADD1 - = LENGTH (h1::t1) by LENGTH - Thus P (EL (k + 1) (h1::t1)) (EL (k + 1) (h2::t2)) by implication - or P (EL (PRE (k + 1) t1)) (EL (PRE (k + 1)) t2) by EL_CONS - or P (EL k t1) (EL k t2) by PRE, ADD1 -*) -val EL_ALL_PROPERTY = store_thm( - "EL_ALL_PROPERTY", - ``!h1 t1 h2 t2 P. (LENGTH (h1::t1) = LENGTH (h2::t2)) /\ - (!k. k < LENGTH (h1::t1) ==> P (EL k (h1::t1)) (EL k (h2::t2))) ==> - (P h1 h2) /\ (!k. k < LENGTH t1 ==> P (EL k t1) (EL k t2))``, - rpt strip_tac >| [ - `0 < LENGTH (h1::t1)` by metis_tac[LENGTH, SUC_POS] >> - metis_tac[EL, HD], - `k + 1 < SUC (LENGTH t1)` by decide_tac >> - `k + 1 < LENGTH (h1::t1)` by metis_tac[LENGTH] >> - `0 < k + 1 /\ (PRE (k + 1) = k)` by decide_tac >> - metis_tac[EL_CONS] - ]); - -(* Theorem: (l1 ++ l2 = m1 ++ m2) /\ (LENGTH l1 = LENGTH m1) <=> (l1 = m1) /\ (l2 = m2) *) -(* Proof: - By APPEND_EQ_APPEND, - ?l. (l1 = m1 ++ l) /\ (m2 = l ++ l2) \/ ?l. (m1 = l1 ++ l) /\ (l2 = l ++ m2). - Thus this is to show: - (1) LENGTH (m1 ++ l) = LENGTH m1 ==> m1 ++ l = m1, true since l = [] by LENGTH_APPEND, LENGTH_NIL - (2) LENGTH (m1 ++ l) = LENGTH m1 ==> l2 = l ++ l2, true since l = [] by LENGTH_APPEND, LENGTH_NIL - (3) LENGTH l1 = LENGTH (l1 ++ l) ==> l1 = l1 ++ l, true since l = [] by LENGTH_APPEND, LENGTH_NIL - (4) LENGTH l1 = LENGTH (l1 ++ l) ==> l ++ m2 = m2, true since l = [] by LENGTH_APPEND, LENGTH_NIL -*) -val APPEND_EQ_APPEND_EQ = store_thm( - "APPEND_EQ_APPEND_EQ", - ``!l1 l2 m1 m2. (l1 ++ l2 = m1 ++ m2) /\ (LENGTH l1 = LENGTH m1) <=> (l1 = m1) /\ (l2 = m2)``, - rw[APPEND_EQ_APPEND] >> - rw[EQ_IMP_THM] >- - fs[] >- - fs[] >- - (fs[] >> - `LENGTH l = 0` by decide_tac >> - fs[]) >> - fs[] >> - `LENGTH l = 0` by decide_tac >> - fs[]); - -(* -LUPDATE_SEM |- (!e n l. LENGTH (LUPDATE e n l) = LENGTH l) /\ - !e n l p. p < LENGTH l ==> EL p (LUPDATE e n l) = if p = n then e else EL p l -EL_LUPDATE |- !ys x i k. EL i (LUPDATE x k ys) = if i = k /\ k < LENGTH ys then x else EL i ys -LENGTH_LUPDATE |- !x n ys. LENGTH (LUPDATE x n ys) = LENGTH ys -*) - -(* Extract useful theorem from LUPDATE semantics *) -val LUPDATE_LEN = save_thm("LUPDATE_LEN", LUPDATE_SEM |> CONJUNCT1); -(* val LUPDATE_LEN = |- !e n l. LENGTH (LUPDATE e n l) = LENGTH l: thm *) -val LUPDATE_EL = save_thm("LUPDATE_EL", LUPDATE_SEM |> CONJUNCT2); -(* val LUPDATE_EL = |- !e n l p. p < LENGTH l ==> EL p (LUPDATE e n l) = if p = n then e else EL p l: thm *) - -(* Theorem: LUPDATE q n (LUPDATE p n ls) = LUPDATE q n ls *) -(* Proof: - Let l1 = LUPDATE q n (LUPDATE p n ls), l2 = LUPDATE q n ls. - By LIST_EQ, this is to show: - (1) LENGTH l1 = LENGTH l2 - LENGTH l1 - = LENGTH (LUPDATE q n (LUPDATE p n ls)) by notation - = LENGTH (LUPDATE p n ls) by LUPDATE_LEN - = ls by LUPDATE_LEN - = LENGTH (LUPDATE q n ls) by LUPDATE_LEN - = LENGTH l2 by notation - (2) !x. x < LENGTH l1 ==> EL x l1 = EL x l2 - EL x l1 - = EL x (LUPDATE q n (LUPDATE p n ls)) by notation - = if x = n then q else EL x (LUPDATE p n ls) by LUPDATE_EL - = if x = n then q else (if x = n then p else EL x ls) by LUPDATE_EL - = if x = n then q else EL x ls by simplification - = EL x (LUPDATE q n ls) by LUPDATE_EL - = EL x l2 by notation -*) -val LUPDATE_SAME_SPOT = store_thm( - "LUPDATE_SAME_SPOT", - ``!ls n p q. LUPDATE q n (LUPDATE p n ls) = LUPDATE q n ls``, - rpt strip_tac >> - qabbrev_tac `l1 = LUPDATE q n (LUPDATE p n ls)` >> - qabbrev_tac `l2 = LUPDATE q n ls` >> - `LENGTH l1 = LENGTH l2` by rw[LUPDATE_LEN, Abbr`l1`, Abbr`l2`] >> - `!x. x < LENGTH l1 ==> (EL x l1 = EL x l2)` by fs[LUPDATE_EL, Abbr`l1`, Abbr`l2`] >> - rw[LIST_EQ]); - -(* Theorem: m <> n ==> - (LUPDATE q n (LUPDATE p m ls) = LUPDATE p m (LUPDATE q n ls)) *) -(* Proof: - Let l1 = LUPDATE q n (LUPDATE p m ls), - l2 = LUPDATE p m (LUPDATE q n ls). - LENGTH l1 - = LENGTH (LUPDATE q n (LUPDATE p m ls)) by notation - = LENGTH (LUPDATE p m ls) by LUPDATE_LEN - = LENGTH ls by LUPDATE_LEN - = LENGTH (LUPDATE q n ls) by LUPDATE_LEN - = LENGTH (LUPDATE p m (LUPDATE q n ls)) by LUPDATE_LEN - = LENGTH l2 by notation - !x. x < LENGTH l1 ==> - EL x l1 - = EL x ((LUPDATE q n (LUPDATE p m ls)) by notation - = EL x ls if x <> n, x <> m, or p if x = m, q if x = n - by LUPDATE_EL - EL x l2 - = EL x ((LUPDATE p m (LUPDATE q n ls)) by notation - = EL x ls if x <> m, x <> n, or q if x = n, p if x = m - by LUPDATE_EL - = EL x l1 - Hence l1 = l2 by LIST_EQ -*) -val LUPDATE_DIFF_SPOT = store_thm( - "LUPDATE_DIFF_SPOT", - `` !ls m n p q. m <> n ==> - (LUPDATE q n (LUPDATE p m ls) = LUPDATE p m (LUPDATE q n ls))``, - rpt strip_tac >> - qabbrev_tac `l1 = LUPDATE q n (LUPDATE p m ls)` >> - qabbrev_tac `l2 = LUPDATE p m (LUPDATE q n ls)` >> - irule LIST_EQ >> - rw[LUPDATE_EL, Abbr`l1`, Abbr`l2`]); - -(* Theorem: EL (LENGTH ls) (ls ++ h::t) = h *) -(* Proof: - Let l2 = h::t. - Note ~NULL l2 by NULL - so EL (LENGTH ls) (ls ++ h::t) - = EL (LENGTH ls) (ls ++ l2) by notation - = HD l2 by EL_LENGTH_APPEND - = HD (h::t) = h by notation -*) -val EL_LENGTH_APPEND_0 = store_thm( - "EL_LENGTH_APPEND_0", - ``!ls h t. EL (LENGTH ls) (ls ++ h::t) = h``, - rw[EL_LENGTH_APPEND]); - -(* Theorem: EL (LENGTH ls + 1) (ls ++ h::k::t) = k *) -(* Proof: - Let l1 = ls ++ [h]. - Then LENGTH l1 = LENGTH ls + 1 by LENGTH - Note ls ++ h::k::t = l1 ++ k::t by APPEND - EL (LENGTH ls + 1) (ls ++ h::k::t) - = EL (LENGTH l1) (l1 ++ k::t) by above - = k by EL_LENGTH_APPEND_0 -*) -val EL_LENGTH_APPEND_1 = store_thm( - "EL_LENGTH_APPEND_1", - ``!ls h k t. EL (LENGTH ls + 1) (ls ++ h::k::t) = k``, - rpt strip_tac >> - qabbrev_tac `l1 = ls ++ [h]` >> - `LENGTH l1 = LENGTH ls + 1` by rw[Abbr`l1`] >> - `ls ++ h::k::t = l1 ++ k::t` by rw[Abbr`l1`] >> - metis_tac[EL_LENGTH_APPEND_0]); - -(* Theorem: LUPDATE a (LENGTH ls) (ls ++ (h::t)) = ls ++ (a::t) *) -(* Proof: - LUPDATE a (LENGTH ls) (ls ++ h::t) - = ls ++ LUPDATE a (LENGTH ls - LENGTH ls) (h::t) by LUPDATE_APPEND2 - = ls ++ LUPDATE a 0 (h::t) by arithmetic - = ls ++ (a::t) by LUPDATE_def -*) -val LUPDATE_APPEND_0 = store_thm( - "LUPDATE_APPEND_0", - ``!ls a h t. LUPDATE a (LENGTH ls) (ls ++ (h::t)) = ls ++ (a::t)``, - rw_tac std_ss[LUPDATE_APPEND2, LUPDATE_def]); - -(* Theorem: LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = ls ++ h::b::t *) -(* Proof: - LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) - = ls ++ LUPDATE b (LENGTH ls + 1 - LENGTH ls) (h::k::t) by LUPDATE_APPEND2 - = ls ++ LUPDATE b 1 (h::k::t) by arithmetic - = ls ++ (h::b::t) by LUPDATE_def -*) -val LUPDATE_APPEND_1 = store_thm( - "LUPDATE_APPEND_1", - ``!ls b h k t. LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = ls ++ h::b::t``, - rpt strip_tac >> - `LUPDATE b 1 (h::k::t) = h::LUPDATE b 0 (k::t)` by rw[GSYM LUPDATE_def] >> - `_ = h::b::t` by rw[LUPDATE_def] >> - `LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = - ls ++ LUPDATE b (LENGTH ls + 1 - LENGTH ls) (h::k::t)` by metis_tac[LUPDATE_APPEND2, DECIDE``n <= n + 1``] >> - fs[]); - -(* Theorem: LUPDATE b (LENGTH ls + 1) - (LUPDATE a (LENGTH ls) (ls ++ h::k::t)) = ls ++ a::b::t *) -(* Proof: - Let l1 = LUPDATE a (LENGTH ls) (ls ++ h::k::t) - = ls ++ a::k::t by LUPDATE_APPEND_0 - LUPDATE b (LENGTH ls + 1) l1 - = LUPDATE b (LENGTH ls + 1) (ls ++ a::k::t) - = ls ++ a::b::t by LUPDATE_APPEND2_1 -*) -val LUPDATE_APPEND_0_1 = store_thm( - "LUPDATE_APPEND_0_1", - ``!ls a b h k t. - LUPDATE b (LENGTH ls + 1) - (LUPDATE a (LENGTH ls) (ls ++ h::k::t)) = ls ++ a::b::t``, - rw_tac std_ss[LUPDATE_APPEND_0, LUPDATE_APPEND_1]); - -(* ------------------------------------------------------------------------- *) -(* DROP and TAKE *) -(* ------------------------------------------------------------------------- *) - -(* Note: There is TAKE_LENGTH_ID, but no DROP_LENGTH_NIL, now have DROP_LENGTH_TOO_LONG *) - -(* Theorem: DROP (LENGTH l) l = [] *) -(* Proof: - By induction on l. - Base case: DROP (LENGTH []) [] = [] - True by DROP_def: DROP n [] = []. - Step case: DROP (LENGTH l) l = [] ==> - !h. DROP (LENGTH (h::l)) (h::l) = [] - Since LENGTH (h::l) = SUC (LENGTH l) by LENGTH - so LENGTH (h::l) <> 0 by NOT_SUC - and SUC (LENGTH l) - 1 = LENGTH l by SUC_SUB1 - DROP (LENGTH (h::l) (h::l) - = DROP (LENGTH l) l by DROP_def - = [] by induction hypothesis -*) -val DROP_LENGTH_NIL = store_thm( - "DROP_LENGTH_NIL", - ``!l. DROP (LENGTH l) l = []``, - Induct >> rw[]); - -(* listTheory.HD_DROP |- !n l. n < LENGTH l ==> HD (DROP n l) = EL n l *) - -(* Theorem: n < LENGTH ls ==> TL (DROP n ls) = DROP n (TL ls) *) -(* Proof: - Note 0 < LENGTH ls, so ls <> [] by LENGTH_NON_NIL - so ?h t. ls = h::t by NOT_NIL_CONS - TL (DROP n ls) - = TL (EL n ls::DROP (SUC n) ls) by DROP_CONS_EL - = DROP (SUC n) ls by TL - = DROP (SUC n) (h::t) by above - = DROP n t by DROP - = DROP n (TL ls) by TL -*) -Theorem TL_DROP: - !ls n. n < LENGTH ls ==> TL (DROP n ls) = DROP n (TL ls) -Proof - rpt strip_tac >> - `0 < LENGTH ls` by decide_tac >> - `TL (DROP n ls) = TL (EL n ls::DROP (SUC n) ls)` by simp[DROP_CONS_EL] >> - `_ = DROP (SUC n) ls` by simp[] >> - `_ = DROP (SUC n) (HD ls::TL ls)` by metis_tac[LIST_HEAD_TAIL] >> - simp[] -QED - -(* Theorem: x <> [] ==> (TAKE 1 (x ++ y) = TAKE 1 x) *) -(* Proof: - x <> [] means ?h t. x = h::t by list_CASES - TAKE 1 (x ++ y) - = TAKE 1 ((h::t) ++ y) - = TAKE 1 (h:: t ++ y) by APPEND - = h::TAKE 0 (t ++ y) by TAKE_def - = h::TAKE 0 t by TAKE_0 - = TAKE 1 (h::t) by TAKE_def -*) -val TAKE_1_APPEND = store_thm( - "TAKE_1_APPEND", - ``!x y. x <> [] ==> (TAKE 1 (x ++ y) = TAKE 1 x)``, - Cases_on `x`>> rw[]); - -(* Theorem: x <> [] ==> (DROP 1 (x ++ y) = (DROP 1 x) ++ y) *) -(* Proof: - x <> [] means ?h t. x = h::t by list_CASES - DROP 1 (x ++ y) - = DROP 1 ((h::t) ++ y) - = DROP 1 (h:: t ++ y) by APPEND - = DROP 0 (t ++ y) by DROP_def - = t ++ y by DROP_0 - = (DROP 1 (h::t)) ++ y by DROP_def -*) -val DROP_1_APPEND = store_thm( - "DROP_1_APPEND", - ``!x y. x <> [] ==> (DROP 1 (x ++ y) = (DROP 1 x) ++ y)``, - Cases_on `x` >> rw[]); - -(* Theorem: DROP (SUC n) x = DROP 1 (DROP n x) *) -(* Proof: - By induction on x. - Base case: !n. DROP (SUC n) [] = DROP 1 (DROP n []) - LHS = DROP (SUC n) [] = [] by DROP_def - RHS = DROP 1 (DROP n []) - = DROP 1 [] by DROP_def - = [] = LHS by DROP_def - Step case: !n. DROP (SUC n) x = DROP 1 (DROP n x) ==> - !h n. DROP (SUC n) (h::x) = DROP 1 (DROP n (h::x)) - If n = 0, - LHS = DROP (SUC 0) (h::x) - = DROP 1 (h::x) by ONE - RHS = DROP 1 (DROP 0 (h::x)) - = DROP 1 (h::x) = LHS by DROP_0 - If n <> 0, - LHS = DROP (SUC n) (h::x) - = DROP n x by DROP_def - RHS = DROP 1 (DROP n (h::x) - = DROP 1 (DROP (n-1) x) by DROP_def - = DROP (SUC (n-1)) x by induction hypothesis - = DROP n x = LHS by SUC (n-1) = n, n <> 0. -*) -val DROP_SUC = store_thm( - "DROP_SUC", - ``!n x. DROP (SUC n) x = DROP 1 (DROP n x)``, - Induct_on `x` >> - rw[DROP_def] >> - `n = SUC (n-1)` by decide_tac >> - metis_tac[]); - -(* Theorem: TAKE (SUC n) x = (TAKE n x) ++ (TAKE 1 (DROP n x)) *) -(* Proof: - By induction on x. - Base case: !n. TAKE (SUC n) [] = TAKE n [] ++ TAKE 1 (DROP n []) - LHS = TAKE (SUC n) [] = [] by TAKE_def - RHS = TAKE n [] ++ TAKE 1 (DROP n []) - = [] ++ TAKE 1 [] by TAKE_def, DROP_def - = TAKE 1 [] by APPEND - = [] = LHS by TAKE_def - Step case: !n. TAKE (SUC n) x = TAKE n x ++ TAKE 1 (DROP n x) ==> - !h n. TAKE (SUC n) (h::x) = TAKE n (h::x) ++ TAKE 1 (DROP n (h::x)) - If n = 0, - LHS = TAKE (SUC 0) (h::x) - = TAKE 1 (h::x) by ONE - RHS = TAKE 0 (h::x) ++ TAKE 1 (DROP 0 (h::x)) - = [] ++ TAKE 1 (h::x) by TAKE_def, DROP_def - = TAKE 1 (h::x) = LHS by APPEND - If n <> 0, - LHS = TAKE (SUC n) (h::x) - = h :: TAKE n x by TAKE_def - RHS = TAKE n (h::x) ++ TAKE 1 (DROP n (h::x)) - = (h:: TAKE (n-1) x) ++ TAKE 1 (DROP (n-1) x) by TAKE_def, DROP_def, n <> 0. - = h :: (TAKE (n-1) x ++ TAKE 1 (DROP (n-1) x)) by APPEND - = h :: TAKE (SUC (n-1)) x by induction hypothesis - = h :: TAKE n x by SUC (n-1) = n, n <> 0. -*) -val TAKE_SUC = store_thm( - "TAKE_SUC", - ``!n x. TAKE (SUC n) x = (TAKE n x) ++ (TAKE 1 (DROP n x))``, - Induct_on `x` >> - rw[TAKE_def, DROP_def] >> - `n = SUC (n-1)` by decide_tac >> - metis_tac[]); - -(* Theorem: k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x)) *) -(* Proof: - By induction on k. - Base case: !x. 0 < LENGTH x ==> (TAKE (SUC 0) x = SNOC (EL 0 x) (TAKE 0 x)) - 0 < LENGTH x - ==> ?h t. x = h::t by LENGTH_NIL, list_CASES - LHS = TAKE (SUC 0) x - = TAKE 1 (h::t) by ONE - = h::TAKE 0 t by TAKE_def - = h::[] by TAKE_0 - = [h] - = SNOC h [] by SNOC - = SNOC h (TAKE 0 (h::t)) by TAKE_0 - = SNOC (EL 0 (h::t)) (TAKE 0 (h::t)) by EL - = RHS - Step case: !x. k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x)) ==> - !x. SUC k < LENGTH x ==> (TAKE (SUC (SUC k)) x = SNOC (EL (SUC k) x) (TAKE (SUC k) x)) - Since 0 < SUC k by prim_recTheory.LESS_0 - 0 < LENGTH x by LESS_TRANS - ==> ?h t. x = h::t by LENGTH_NIL, list_CASES - and LENGTH (h::t) = SUC (LENGTH t) by LENGTH - hence k < LENGTH t by LESS_MONO_EQ - LHS = TAKE (SUC (SUC k)) (h::t) - = h :: TAKE (SUC k) t by TAKE_def - = h :: SNOC (EL k t) (TAKE k t) by induction hypothesis, k < LENGTH t. - = SNOC (EL k t) (h :: TAKE k t) by SNOC - = SNOC (EL (SUC k) (h::t)) (h :: TAKE k t) by EL_restricted - = SNOC (EL (SUC k) (h::t)) (TAKE (SUC k) (h::t)) by TAKE_def - = RHS -*) -val TAKE_SUC_BY_TAKE = store_thm( - "TAKE_SUC_BY_TAKE", - ``!k x. k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x))``, - Induct_on `k` >| [ - rpt strip_tac >> - `LENGTH x <> 0` by decide_tac >> - `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - rw[], - rpt strip_tac >> - `LENGTH x <> 0` by decide_tac >> - `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - `k < LENGTH t` by metis_tac[LENGTH, LESS_MONO_EQ] >> - rw_tac std_ss[TAKE_def, SNOC, EL_restricted] - ]); - -(* Theorem: k < LENGTH x ==> (DROP k x = (EL k x) :: (DROP (SUC k) x)) *) -(* Proof: - By induction on k. - Base case: !x. 0 < LENGTH x ==> (DROP 0 x = EL 0 x::DROP (SUC 0) x) - 0 < LENGTH x - ==> ?h t. x = h::t by LENGTH_NIL, list_CASES - LHS = DROP 0 (h::t) - = h::t by DROP_0 - = (EL 0 (h::t))::t by EL - = (EL 0 (h::t))::(DROP 1 (h::t)) by DROP_def - = EL 0 x::DROP (SUC 0) x by ONE - = RHS - Step case: !x. k < LENGTH x ==> (DROP k x = EL k x::DROP (SUC k) x) ==> - !x. SUC k < LENGTH x ==> (DROP (SUC k) x = EL (SUC k) x::DROP (SUC (SUC k)) x) - Since 0 < SUC k by prim_recTheory.LESS_0 - 0 < LENGTH x by LESS_TRANS - ==> ?h t. x = h::t by LENGTH_NIL, list_CASES - and LENGTH (h::t) = SUC (LENGTH t) by LENGTH - hence k < LENGTH t by LESS_MONO_EQ - LHS = DROP (SUC k) (h::t) - = DROP k t by DROP_def - = EL k x::DROP (SUC k) x by induction hypothesis - = EL k t :: DROP (SUC (SUC k)) (h::t) by DROP_def - = EL (SUC k) (h::t)::DROP (SUC (SUC k)) (h::t) by EL - = RHS -*) -val DROP_BY_DROP_SUC = store_thm( - "DROP_BY_DROP_SUC", - ``!k x. k < LENGTH x ==> (DROP k x = (EL k x) :: (DROP (SUC k) x))``, - Induct_on `k` >| [ - rpt strip_tac >> - `LENGTH x <> 0` by decide_tac >> - `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - rw[], - rpt strip_tac >> - `LENGTH x <> 0` by decide_tac >> - `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - `k < LENGTH t` by metis_tac[LENGTH, LESS_MONO_EQ] >> - rw[] - ]); - -(* Theorem: n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u *) -(* Proof: - By induction on n. - Base: !ls. 0 < LENGTH ls ==> ?u. DROP 0 ls = [EL 0 ls] ++ u - Note LENGTH ls <> 0 by 0 < LENGTH ls - ==> ls <> [] by LENGTH_NIL - ==> ?h t. ls = h::t by list_CASES - DROP 0 ls - = ls by DROP_0 - = [h] ++ t by ls = h::t, CONS_APPEND - = [EL 0 ls] ++ t by EL - Take u = t. - Step: !ls. n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u ==> - !ls. SUC n < LENGTH ls ==> ?u. DROP (SUC n) ls = [EL (SUC n) ls] ++ u - Note LENGTH ls <> 0 by SUC n < LENGTH ls - ==> ?h t. ls = h::t by list_CASES, LENGTH_NIL - Now LENGTH ls = SUC (LENGTH t) by LENGTH - ==> n < LENGTH t by SUC n < SUC (LENGTH t) - Thus ?u. DROP n t = [EL n t] ++ u by induction hypothesis - - DROP (SUC n) ls - = DROP (SUC n) (h::t) by ls = h::t - = DROP n t by DROP_def - = [EL n t] ++ u by above - = [EL (SUC n) (h::t)] ++ u by EL_restricted - Take this u. -*) -val DROP_HEAD_ELEMENT = store_thm( - "DROP_HEAD_ELEMENT", - ``!ls n. n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u``, - Induct_on `n` >| [ - rpt strip_tac >> - `LENGTH ls <> 0` by decide_tac >> - `?h t. ls = h::t` by metis_tac[list_CASES, LENGTH_NIL] >> - rw[], - rw[] >> - `LENGTH ls <> 0` by decide_tac >> - `?h t. ls = h::t` by metis_tac[list_CASES, LENGTH_NIL] >> - `LENGTH ls = SUC (LENGTH t)` by rw[] >> - `n < LENGTH t` by decide_tac >> - `?u. DROP n t = [EL n t] ++ u` by rw[] >> - rw[] - ]); - -(* Theorem: DROP n (TAKE n ls) = [] *) -(* Proof: - If n <= LENGTH ls, - Then LENGTH (TAKE n ls) = n by LENGTH_TAKE_EQ - Thus DROP n (TAKE n ls) = [] by DROP_LENGTH_TOO_LONG - If LENGTH ls < n - Then LENGTH (TAKE n ls) = LENGTH ls by LENGTH_TAKE_EQ - Thus DROP n (TAKE n ls) = [] by DROP_LENGTH_TOO_LONG -*) -val DROP_TAKE_EQ_NIL = store_thm( - "DROP_TAKE_EQ_NIL", - ``!ls n. DROP n (TAKE n ls) = []``, - rw[LENGTH_TAKE_EQ, DROP_LENGTH_TOO_LONG]); - -(* Theorem: TAKE m (DROP n ls) = DROP n (TAKE (n + m) ls) *) -(* Proof: - If n <= LENGTH ls, - Then LENGTH (TAKE n ls) = n by LENGTH_TAKE_EQ, n <= LENGTH ls - DROP n (TAKE (n + m) ls) - = DROP n (TAKE n ls ++ TAKE m (DROP n ls)) by TAKE_SUM - = DROP n (TAKE n ls) ++ DROP (n - LENGTH (TAKE n ls)) (TAKE m (DROP n ls)) by DROP_APPEND - = [] ++ DROP (n - LENGTH (TAKE n ls)) (TAKE m (DROP n ls)) by DROP_TAKE_EQ_NIL - = DROP (n - LENGTH (TAKE n ls)) (TAKE m (DROP n ls)) by APPEND - = DROP 0 (TAKE m (DROP n ls)) by above - = TAKE m (DROP n ls) by DROP_0 - If LENGTH ls < n, - Then DROP n ls = [] by DROP_LENGTH_TOO_LONG - and TAKE (n + m) ls = ls by TAKE_LENGTH_TOO_LONG - DROP n (TAKE (n + m) ls) - = DROP n ls by TAKE_LENGTH_TOO_LONG - = [] by DROP_LENGTH_TOO_LONG - = TAKE m [] by TAKE_nil - = TAKE m (DROP n ls) by DROP_LENGTH_TOO_LONG -*) -val TAKE_DROP_SWAP = store_thm( - "TAKE_DROP_SWAP", - ``!ls m n. TAKE m (DROP n ls) = DROP n (TAKE (n + m) ls)``, - rpt strip_tac >> - Cases_on `n <= LENGTH ls` >| [ - qabbrev_tac `x = TAKE m (DROP n ls)` >> - `DROP n (TAKE (n + m) ls) = DROP n (TAKE n ls ++ x)` by rw[TAKE_SUM, Abbr`x`] >> - `_ = DROP n (TAKE n ls) ++ DROP (n - LENGTH (TAKE n ls)) x` by rw[DROP_APPEND] >> - `_ = DROP (n - LENGTH (TAKE n ls)) x` by rw[DROP_TAKE_EQ_NIL] >> - `_ = DROP 0 x` by rw[LENGTH_TAKE_EQ] >> - rw[], - `DROP n ls = []` by rw[DROP_LENGTH_TOO_LONG] >> - `TAKE (n + m) ls = ls` by rw[TAKE_LENGTH_TOO_LONG] >> - rw[] - ]); - -(* Theorem: TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) = l1 *) -(* Proof: - TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) - = TAKE (LENGTH l1) (l1 ++ LUPDATE x k l2) by LUPDATE_APPEND2 - = l1 by TAKE_LENGTH_APPEND -*) -val TAKE_LENGTH_APPEND2 = store_thm( - "TAKE_LENGTH_APPEND2", - ``!l1 l2 x k. TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) = l1``, - rw_tac std_ss[LUPDATE_APPEND2, TAKE_LENGTH_APPEND]); - -(* Theorem: LENGTH (TAKE n l) <= LENGTH l *) -(* Proof: by LENGTH_TAKE_EQ *) -val LENGTH_TAKE_LE = store_thm( - "LENGTH_TAKE_LE", - ``!n l. LENGTH (TAKE n l) <= LENGTH l``, - rw[LENGTH_TAKE_EQ]); - -(* Theorem: ALL_DISTINCT ls ==> ALL_DISTINCT (TAKE n ls) *) -(* Proof: - By induction on ls. - Base: !n. ALL_DISTINCT [] ==> ALL_DISTINCT (TAKE n []) - ALL_DISTINCT (TAKE n []) - <=> ALL_DISTINCT [] by TAKE_nil - <=> T by ALL_DISTINCT - Step: !n. ALL_DISTINCT ls ==> ALL_DISTINCT (TAKE n ls) ==> - !h n. ALL_DISTINCT (h::ls) ==> ALL_DISTINCT (TAKE n (h::ls)) - If n = 0, - ALL_DISTINCT (TAKE 0 (h::ls)) - <=> ALL_DISTINCT [] by TAKE_0 - <=> T by ALL_DISTINCT - If n <> 0, - ALL_DISTINCT (TAKE n (h::ls)) - <=> ALL_DISTINCT (h::TAKE (n - 1) ls) by TAKE_def - <=> ~MEM h (TAKE (n - 1) ls) /\ ALL_DISTINCT (TAKE (n - 1) ls) - by ALL_DISTINCT - <=> ~MEM h (TAKE (n - 1) ls) /\ T by induction hypothesis - <=> T by MEM_TAKE, ALL_DISTINCT -*) -Theorem ALL_DISTINCT_TAKE: - !ls n. ALL_DISTINCT ls ==> ALL_DISTINCT (TAKE n ls) -Proof - Induct >- - simp[] >> - rw[] >> - (Cases_on `n = 0` >> simp[]) >> - metis_tac[MEM_TAKE] -QED - -(* Theorem: ALL_DISTINCT ls ==> - !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F *) -(* Proof: - By induction on ls. - Base: ALL_DISTINCT [] ==> !k e. MEM e (TAKE k []) /\ MEM e (DROP k []) ==> F - MEM e (TAKE k []) = MEM e [] = F by TAKE_nil, MEM - MEM e (DROP k []) = MEM e [] = F by DROP_nil, MEM - Step: ALL_DISTINCT ls ==> - !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F ==> - !h. ALL_DISTINCT (h::ls) ==> - !k e. MEM e (TAKE k (h::ls)) /\ MEM e (DROP k (h::ls)) ==> F - Note ~MEM h ls /\ ALL_DISTINCT ls by ALL_DISTINCT - If k = 0, - MEM e (TAKE 0 (h::ls)) - <=> MEM e [] = F by TAKE_0, MEM - hence true. - If k <> 0, - MEM e (TAKE k (h::ls)) - <=> MEM e (h::TAKE (k - 1) ls) by TAKE_def, k <> 0 - <=> e = h \/ MEM e (TAKE (k - 1) ls) by MEM - MEM e (DROP k (h::ls)) - <=> MEM e (DROP (k - 1) ls) by DROP_def, k <> 0 - ==> MEM e ls by MEM_DROP_IMP - If e = h, - this contradicts ~MEM h ls. - If MEM e (TAKE (k - 1) ls) - this contradicts the induction hypothesis. -*) -Theorem ALL_DISTINCT_TAKE_DROP: - !ls. ALL_DISTINCT ls ==> - !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F -Proof - Induct >- - simp[] >> - rw[] >> - Cases_on `k = 0` >- - fs[] >> - spose_not_then strip_assume_tac >> - rfs[] >- - metis_tac[MEM_DROP_IMP] >> - metis_tac[] -QED - -(* Theorem: ALL_DISTINCT (x::y::ls) <=> ALL_DISTINCT (y::x::ls) *) -(* Proof: - If x = y, this is trivial. - If x <> y, - ALL_DISTINCT (x::y::ls) - <=> (x <> y /\ ~MEM x ls) /\ ~MEM y ls /\ ALL_DISTINCT ls by ALL_DISTINCT - <=> (y <> x /\ ~MEM y ls) /\ ~MEM x ls /\ ALL_DISTINCT ls - <=> ALL_DISTINCT (y::x::ls) by ALL_DISTINCT -*) -Theorem ALL_DISTINCT_SWAP: - !ls x y. ALL_DISTINCT (x::y::ls) <=> ALL_DISTINCT (y::x::ls) -Proof - rw[] >> - metis_tac[] -QED - -(* Theorem: ALL_DISTINCT ls /\ ls <> [] /\ j < LENGTH ls ==> (EL j ls = LAST ls <=> j + 1 = LENGTH ls) *) -(* Proof: - Note 0 < LENGTH ls by LENGTH_EQ_0 - EL j ls = LAST ls - <=> EL j ls = EL (PRE (LENGTH ls)) ls by LAST_EL - <=> j = PRE (LENGTH ls) by ALL_DISTINCT_EL_IMP, j < LENGTH ls - <=> j + 1 = LENGTH ls by SUC_PRE, ADD1, 0 < LENGTH ls -*) -Theorem ALL_DISTINCT_LAST_EL_IFF: - !ls j. ALL_DISTINCT ls /\ ls <> [] /\ j < LENGTH ls ==> (EL j ls = LAST ls <=> j + 1 = LENGTH ls) -Proof - rw[LAST_EL] >> - `0 < LENGTH ls` by metis_tac[LENGTH_EQ_0, NOT_ZERO] >> - `PRE (LENGTH ls) + 1 = LENGTH ls` by decide_tac >> - `EL j ls = EL (PRE (LENGTH ls)) ls <=> j = PRE (LENGTH ls)` by fs[ALL_DISTINCT_EL_IMP] >> - simp[] -QED - -(* Theorem: ls <> [] /\ ALL_DISTINCT ls ==> ALL_DISTINCT (FRONT ls) *) -(* Proof: - Let k = LENGTH ls. - ALL_DISTINCT ls - ==> ALL_DISTINCT (TAKE (k - 1) ls) by ALL_DISTINCT_TAKE - ==> ALL_DISTINCT (FRONT ls) by FRONT_BY_TAKE, ls <> [] -*) -Theorem ALL_DISTINCT_FRONT: - !ls. ls <> [] /\ ALL_DISTINCT ls ==> ALL_DISTINCT (FRONT ls) -Proof - simp[ALL_DISTINCT_TAKE, FRONT_BY_TAKE] -QED - -(* Theorem: ALL_DISTINCT ls /\ j < LENGTH ls /\ ls = l1 ++ [EL j ls] ++ l2 ==> j = LENGTH l1 *) -(* Proof: - Note EL j ls = EL (LENGTH l1) ls by el_append3 - and LENGTH l1 < LENGTH ls by LENGTH_APPEND - so j = LENGTH l1 by ALL_DISTINCT_EL_IMP -*) -Theorem ALL_DISTINCT_EL_APPEND: - !ls l1 l2 j. ALL_DISTINCT ls /\ j < LENGTH ls /\ ls = l1 ++ [EL j ls] ++ l2 ==> j = LENGTH l1 -Proof - rpt strip_tac >> - `EL j ls = EL (LENGTH l1) ls` by metis_tac[el_append3] >> - `LENGTH ls = LENGTH l1 + 1 + LENGTH l2` by metis_tac[LENGTH_APPEND, LENGTH_SING] >> - `LENGTH l1 < LENGTH ls` by decide_tac >> - metis_tac[ALL_DISTINCT_EL_IMP] -QED - -(* Theorem: ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) *) -(* Proof: - By induction on l1. - Base: ALL_DISTINCT ([] ++ [x] ++ l2) <=> ALL_DISTINCT (x::([] ++ l2)) - ALL_DISTINCT ([] ++ [x] ++ l2) - <=> ALL_DISTINCT (x::l2) by APPEND_NIL - <=> ALL_DISTINCT (x::([] ++ l2)) by APPEND_NIL - Step: ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) ==> - !h. ALL_DISTINCT (h::l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(h::l1 ++ l2)) - - ALL_DISTINCT (h::l1 ++ [x] ++ l2) - <=> ALL_DISTINCT (h::(l1 ++ [x] ++ l2)) by APPEND - <=> ~MEM h (l1 ++ [x] ++ l2) /\ - ALL_DISTINCT (l1 ++ [x] ++ l2) by ALL_DISTINCT - <=> ~MEM h (l1 ++ [x] ++ l2) /\ - ALL_DISTINCT (x::(l1 ++ l2)) by induction hypothesis - <=> ~MEM h (x::(l1 ++ l2)) /\ - ALL_DISTINCT (x::(l1 ++ l2)) by MEM_APPEND_3 - <=> ALL_DISTINCT (h::x::(l1 ++ l2)) by ALL_DISTINCT - <=> ALL_DISTINCT (x::h::(l1 ++ l2)) by ALL_DISTINCT_SWAP - <=> ALL_DISTINCT (x::(h::l1 ++ l2)) by APPEND -*) -Theorem ALL_DISTINCT_APPEND_3: - !l1 x l2. ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) -Proof - rpt strip_tac >> - Induct_on `l1` >- - simp[] >> - rpt strip_tac >> - `ALL_DISTINCT (h::l1 ++ [x] ++ l2) <=> ALL_DISTINCT (h::(l1 ++ [x] ++ l2))` by rw[] >> - `_ = (~MEM h (l1 ++ [x] ++ l2) /\ ALL_DISTINCT (l1 ++ [x] ++ l2))` by rw[] >> - `_ = (~MEM h (l1 ++ [x] ++ l2) /\ ALL_DISTINCT (x::(l1 ++ l2)))` by rw[] >> - `_ = (~MEM h (x::(l1 ++ l2)) /\ ALL_DISTINCT (x::(l1 ++ l2)))` by rw[MEM_APPEND_3] >> - `_ = ALL_DISTINCT (h::x::(l1 ++ l2))` by rw[] >> - `_ = ALL_DISTINCT (x::h::(l1 ++ l2))` by rw[ALL_DISTINCT_SWAP] >> - `_ = ALL_DISTINCT (x::(h::l1 ++ l2))` by metis_tac[APPEND] >> - simp[] -QED - -(* Theorem: ALL_DISTINCT l ==> !x. MEM x l <=> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 *) -(* Proof: - If part: MEM x l ==> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 - Note ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p2 by MEM_SPLIT_APPEND_last - Now ALL_DISTINCT (p1 ++ [x]) by ALL_DISTINCT_APPEND, ALL_DISTINCT l - But MEM x [x] by MEM - so ~MEM x p1 by ALL_DISTINCT_APPEND - - Only-if part: MEM x (p1 ++ [x] ++ p2), true by MEM_APPEND -*) -Theorem MEM_SPLIT_APPEND_distinct: - !l. ALL_DISTINCT l ==> !x. MEM x l <=> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 -Proof - rw[EQ_IMP_THM] >- - metis_tac[MEM_SPLIT_APPEND_last, ALL_DISTINCT_APPEND, MEM] >> - rw[] -QED - -(* Theorem: MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (TAKE k ls) *) -(* Proof: - If part: MEM x ls ==> ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (TAKE k ls) - Note ?pfx sfx. ls = pfx ++ [x] ++ sfx /\ ~MEM x pfx - by MEM_SPLIT_APPEND_first - Take k = LENGTH pfx. - Then k < LENGTH ls by LENGTH_APPEND - and EL k ls - = EL k (pfx ++ [x] ++ sfx) - = x by el_append3 - and TAKE k ls ++ x::DROP (k+1) ls - = TAKE k (pfx ++ [x] ++ sfx) ++ - [x] ++ - DROP (k+1) ((pfx ++ [x] ++ sfx)) - = pfx ++ [x] ++ by TAKE_APPEND1 - (DROP (k+1)(pfx + [x]) - ++ sfx by DROP_APPEND1 - = pfx ++ [x] ++ sfx by DROP_LENGTH_NIL - = ls - and TAKE k ls = pfx by TAKE_APPEND1 - Only-if part: k < LENGTH ls /\ ls = TAKE k ls ++ [EL k ls] ++ DROP (k + 1) ls /\ - ~MEM (EL k ls) (TAKE k ls) ==> MEM (EL k ls) ls - This is true by EL_MEM, just need k < LENGTH ls -*) -Theorem MEM_SPLIT_TAKE_DROP_first: - !ls x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (TAKE k ls) -Proof - rw[EQ_IMP_THM] >| [ - imp_res_tac MEM_SPLIT_APPEND_first >> - qexists_tac `LENGTH pfx` >> - rpt strip_tac >- - fs[] >- - fs[el_append3] >- - fs[TAKE_APPEND1, DROP_APPEND1] >> - `TAKE (LENGTH pfx) ls = pfx` by rw[TAKE_APPEND1] >> - fs[], - fs[EL_MEM] - ] -QED - -(* Theorem: MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (DROP (k+1) ls) *) -(* Proof: - If part: MEM x ls ==> ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (DROP (k+1) ls) - Note ?pfx sfx. ls = pfx ++ [x] ++ sfx /\ ~MEM x sfx - by MEM_SPLIT_APPEND_last - Take k = LENGTH pfx. - Then k < LENGTH ls by LENGTH_APPEND - and EL k ls - = EL k (pfx ++ [x] ++ sfx) - = x by el_append3 - and TAKE k ls ++ x::DROP (k+1) ls - = TAKE k (pfx ++ [x] ++ sfx) ++ - [x] ++ - DROP (k+1) ((pfx ++ [x] ++ sfx)) - = pfx ++ [x] ++ by TAKE_APPEND1 - (DROP (k+1)(pfx + [x]) - ++ sfx by DROP_APPEND1 - = pfx ++ [x] ++ sfx by DROP_LENGTH_NIL - = ls - and DROP (k + 1) ls) = sfx by DROP_APPEND1, DROP_LENGTH_NIL - Only-if part: k < LENGTH ls /\ ls = TAKE k ls ++ [EL k ls] ++ DROP (k + 1) ls /\ - ~MEM (EL k ls) (DROP (k+1) ls)) ==> MEM (EL k ls) ls - This is true by EL_MEM, just need k < LENGTH ls -*) -Theorem MEM_SPLIT_TAKE_DROP_last: - !ls x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (DROP (k+1) ls) -Proof - rw[EQ_IMP_THM] >| [ - imp_res_tac MEM_SPLIT_APPEND_last >> - qexists_tac `LENGTH pfx` >> - rpt strip_tac >- - fs[] >- - fs[el_append3] >- - fs[TAKE_APPEND1, DROP_APPEND1] >> - `DROP (LENGTH pfx + 1) ls = sfx` by rw[DROP_APPEND1] >> - fs[], - fs[EL_MEM] - ] -QED - -(* Theorem: ALL_DISTINCT ls ==> - !x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ - ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k+1) ls) *) -(* Proof: - If part: MEM x ls ==> ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ - ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k+1) ls) - Note ?p1 p2. ls = p1 ++ [x] ++ p2 /\ ~MEM x p1 /\ ~MEM x p2 - by MEM_SPLIT_APPEND_distinct - Take k = LENGTH p1. - Then k < LENGTH ls by LENGTH_APPEND - and EL k ls - = EL k (p1 ++ [x] ++ p2) - = x by el_append3 - and TAKE k ls ++ x::DROP (k+1) ls - = TAKE k (p1 ++ [x] ++ p2) ++ - [x] ++ - DROP (k+1) ((p1 ++ [x] ++ p2)) - = p1 ++ [x] ++ by TAKE_APPEND1 - (DROP (k+1)(p1 + [x]) - ++ p2 by DROP_APPEND1 - = p1 ++ [x] ++ p2 by DROP_LENGTH_NIL - = ls - and TAKE k ls = p1 by TAKE_APPEND1 - and DROP (k + 1) ls) = p2 by DROP_APPEND1, DROP_LENGTH_NIL - Only-if part: k < LENGTH ls /\ ls = TAKE k ls ++ [EL k ls] ++ DROP (k + 1) ls /\ - ~MEM (EL k ls) (TAKE k ls) /\ ~MEM (EL k ls) (DROP (k+1) ls)) ==> MEM (EL k ls) ls - This is true by EL_MEM, just need k < LENGTH ls -*) -Theorem MEM_SPLIT_TAKE_DROP_distinct: - !ls. ALL_DISTINCT ls ==> - !x. MEM x ls <=> - ?k. k < LENGTH ls /\ x = EL k ls /\ - ls = TAKE k ls ++ x::DROP (k+1) ls /\ - ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k+1) ls) -Proof - rw[EQ_IMP_THM] >| [ - `?p1 p2. ls = p1 ++ [x] ++ p2 /\ ~MEM x p1 /\ ~MEM x p2` by rw[GSYM MEM_SPLIT_APPEND_distinct] >> - qexists_tac `LENGTH p1` >> - rpt strip_tac >- - fs[] >- - fs[el_append3] >- - fs[TAKE_APPEND1, DROP_APPEND1] >- - rfs[TAKE_APPEND1] >> - `DROP (LENGTH p1 + 1) ls = p2` by rw[DROP_APPEND1] >> - fs[], - fs[EL_MEM] - ] -QED - -(* ------------------------------------------------------------------------- *) -(* List Filter. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: the j-th element of FILTER must have j elements filtered beforehand. *) - -(* Theorem: let fs = FILTER P ls in ls = l1 ++ x::l2 /\ P x ==> - x = EL (LENGTH (FILTER P l1)) fs *) -(* Proof: - Let l3 = x::l2, then ls = l1 ++ l3. - Let j = LENGTH (FILTER P l1). - EL j fs - = EL j (FILTER P ls) by given - = EL j (FILTER P l1 ++ FILTER P l3) by FILTER_APPEND_DISTRIB - = EL 0 (FILTER P l3) by EL_APPEND, j = LENGTH (FILTER P l1) - = EL 0 (FILTER P (x::l2)) by notation - = EL 0 (x::FILTER P l2) by FILTER, P x - = x by HD -*) -Theorem FILTER_EL_IMP: - !P ls l1 l2 x. let fs = FILTER P ls in ls = l1 ++ x::l2 /\ P x ==> - x = EL (LENGTH (FILTER P l1)) fs -Proof - rw_tac std_ss[] >> - qabbrev_tac `l3 = x::l2` >> - qabbrev_tac `j = LENGTH (FILTER P l1)` >> - `EL j fs = EL j (FILTER P l1 ++ FILTER P l3)` by simp[FILTER_APPEND_DISTRIB, Abbr`fs`] >> - `_ = EL 0 (FILTER P (x::l2))` by simp[EL_APPEND, Abbr`j`, Abbr`l3`] >> - fs[] -QED - -(* Theorem: let fs = FILTER P ls in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ j < LENGTH fs ==> - (x = EL j fs <=> P x /\ j = LENGTH (FILTER P l1)) *) -(* Proof: - Let k = LENGTH (FILTER P l1). - If part: j < LENGTH fs /\ x = EL j fs ==> P x /\ j = k - Note j < LENGTH fs /\ x = EL j fs by given - ==> MEM x fs by MEM_EL - ==> P x by MEM_FILTER - Thus x = EL k fs by FILTER_EL_IMP - Let l3 = x::l2, then ls = l1 ++ l3. - Then FILTER P l3 = x :: FILTER P l2 by FILTER - or FILTER P l3 <> [] by NOT_NIL_CONS - or LENGTH (FILTER P l3) <> 0 by LENGTH_EQ_0, [1] - - LENGTH fs - = LENGTH (FILTER P ls) by notation - = LENGTH (FILTER P l1 ++ FILTER P l3) by FILTER_APPEND_DISTRIB - = k + LENGTH (FILTER P l3) by LENGTH_APPEND - Thus k < LENGTH fs by [1] - - Note ALL_DISTINCT ls - ==> ALL_DISTINCT fs by FILTER_ALL_DISTINCT - With x = EL j fs = EL k fs by above - and j < LENGTH fs /\ k < LENGTH fs by above - ==> j = k by ALL_DISTINCT_EL_IMP - - Only-if part: j < LENGTH fs /\ P x /\ j = k ==> x = EL j fs - This is true by FILTER_EL_IMP -*) -Theorem FILTER_EL_IFF: - !P ls l1 l2 x j. let fs = FILTER P ls in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ j < LENGTH fs ==> - (x = EL j fs <=> P x /\ j = LENGTH (FILTER P l1)) -Proof - rw_tac std_ss[] >> - qabbrev_tac `k = LENGTH (FILTER P l1)` >> - simp[EQ_IMP_THM] >> - ntac 2 strip_tac >| [ - `MEM x fs` by metis_tac[MEM_EL] >> - `P x` by fs[MEM_FILTER, Abbr`fs`] >> - qabbrev_tac `ls = l1 ++ x::l2` >> - `EL j fs = EL k fs` by metis_tac[FILTER_EL_IMP] >> - qabbrev_tac `l3 = x::l2` >> - `FILTER P l3 = x :: FILTER P l2` by simp[Abbr`l3`] >> - `LENGTH (FILTER P l3) <> 0` by fs[] >> - `fs = FILTER P l1 ++ FILTER P l3` by fs[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`ls`] >> - `LENGTH fs = k + LENGTH (FILTER P l3)` by fs[Abbr`k`] >> - `k < LENGTH fs` by decide_tac >> - `ALL_DISTINCT fs` by simp[FILTER_ALL_DISTINCT, Abbr`fs`] >> - metis_tac[ALL_DISTINCT_EL_IMP], - metis_tac[FILTER_EL_IMP] - ] -QED - -(* Derive theorems for head = (EL 0 fs) *) - -(* Theorem: ls = l1 ++ x::l2 /\ P x /\ FILTER P l1 = [] ==> x = HD (FILTER P ls) *) -(* Proof: - Note FILTER P l1 = [] by given - ==> LENGTH (FILTER P l1) = 0 by LENGTH - Thus x = EL 0 (FILTER P ls) by FILTER_EL_IMP - = HD (FILTER P ls) by EL -*) -Theorem FILTER_HD: - !P ls l1 l2 x. ls = l1 ++ x::l2 /\ P x /\ FILTER P l1 = [] ==> x = HD (FILTER P ls) -Proof - metis_tac[LENGTH, FILTER_EL_IMP, EL] -QED - -(* Theorem: ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> - (x = HD (FILTER P ls) <=> FILTER P l1 = []) *) -(* Proof: - Let fs = FILTER P ls. - Note MEM x ls by MEM_APPEND, MEM - and P x ==> fs <> [] by MEM_FILTER, NIL_NO_MEM - so 0 < LENGTH fs by LENGTH_EQ_0 - Thus x = HD fs - = EL 0 fs by EL - <=> LENGTH (FILTER P l1) = 0 by FILTER_EL_IFF - <=> FILTER P l1 = [] by LENGTH_EQ_0 -*) -Theorem FILTER_HD_IFF: - !P ls l1 l2 x. ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> - (x = HD (FILTER P ls) <=> FILTER P l1 = []) -Proof - rpt strip_tac >> - qabbrev_tac `fs = FILTER P ls` >> - `MEM x ls` by metis_tac[MEM_APPEND, MEM] >> - `MEM x fs` by fs[MEM_FILTER, Abbr`fs`] >> - `0 < LENGTH fs` by metis_tac[NIL_NO_MEM, LENGTH_EQ_0, NOT_ZERO] >> - metis_tac[FILTER_EL_IFF, EL, LENGTH_EQ_0] -QED - -(* Derive theorems for last = (EL (LENGTH fs - 1) fs) *) - -(* Theorem: ls = l1 ++ x::l2 /\ P x /\ FILTER P l2 = [] ==> - x = LAST (FILTER P ls) *) -(* Proof: - Let fs = FILTER P ls, - k = LENGTH fs. - Note MEM x ls by MEM_APPEND, MEM - and P x ==> fs <> [] by MEM_FILTER, NIL_NO_MEM - so 0 < LENGTH fs = k by LENGTH_EQ_0 - - Note FILTER P l2 = [] by given - ==> LENGTH (FILTER P l2) = 0 by LENGTH - k = LENGTH fs - = LENGTH (FILTER P ls) by notation - = LENGTH (FILTER P l1) + 1 by FILTER_APPEND_DISTRIB, ONE - or LENGTH (FILTER P l1) = PRE k - Thus x = EL (PRE k) fs by FILTER_EL_IMP - = LAST fs by LAST_EL, fs <> [] -*) -Theorem FILTER_LAST: - !P ls l1 l2 x. ls = l1 ++ x::l2 /\ P x /\ FILTER P l2 = [] ==> - x = LAST (FILTER P ls) -Proof - rpt strip_tac >> - qabbrev_tac `fs = FILTER P ls` >> - qabbrev_tac `k = LENGTH fs` >> - `MEM x ls` by metis_tac[MEM_APPEND, MEM] >> - `MEM x fs` by fs[MEM_FILTER, Abbr`fs`] >> - `fs <> [] /\ 0 < k` by metis_tac[NIL_NO_MEM, LENGTH_EQ_0, NOT_ZERO] >> - `k = LENGTH (FILTER P l1) + 1` by fs[FILTER_APPEND_DISTRIB, Abbr`k`, Abbr`fs`] >> - `LENGTH (FILTER P l1) = PRE k` by decide_tac >> - metis_tac[FILTER_EL_IMP, LAST_EL] -QED - -(* Theorem: ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> - (x = LAST (FILTER P ls) <=> FILTER P l2 = []) *) -(* Proof: - Let fs = FILTER P ls, - k = LENGTH fs, - j = LENGTH (FILTER P l1). - Note MEM x ls by MEM_APPEND, MEM - and P x ==> fs <> [] by MEM_FILTER, NIL_NO_MEM - so 0 < LENGTH fs = k by LENGTH_EQ_0 - and PRE k < k by arithmetic - - k = LENGTH fs - = LENGTH (FILTER P ls) by notation - = j + 1 + LENGTH (FILTER P l2) by FILTER_APPEND_DISTRIB, ONE - so j = PRE k <=> LENGTH (FILTER P l2) = 0 by arithmetic - - Thus x = LAST fs - = EL (PRE k) fs by LAST_EL - <=> PRE k = j by FILTER_EL_IFF - <=> LENGTH (FILTER P l2) = 0 by above - <=> FILTER P l2 = [] by LENGTH_EQ_0 -*) -Theorem FILTER_LAST_IFF: - !P ls l1 l2 x. ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> - (x = LAST (FILTER P ls) <=> FILTER P l2 = []) -Proof - rpt strip_tac >> - qabbrev_tac `fs = FILTER P ls` >> - qabbrev_tac `k = LENGTH fs` >> - qabbrev_tac `j = LENGTH (FILTER P l1)` >> - `MEM x ls` by metis_tac[MEM_APPEND, MEM] >> - `MEM x fs` by fs[MEM_FILTER, Abbr`fs`] >> - `fs <> [] /\ 0 < k` by metis_tac[NIL_NO_MEM, LENGTH_EQ_0, NOT_ZERO] >> - `k = j + 1 + LENGTH (FILTER P l2)` by fs[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`k`, Abbr`j`] >> - `PRE k < k /\ (j = PRE k <=> LENGTH (FILTER P l2) = 0)` by decide_tac >> - metis_tac[FILTER_EL_IFF, LAST_EL, LENGTH_EQ_0] -QED - -(* Idea: for FILTER over a range, the range between successive filter elements is filtered. *) - -(* Theorem: let fs = FILTER P ls; j = LENGTH (FILTER P l1) in - ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y /\ FILTER P l2 = [] ==> - x = EL j fs /\ y = EL (j + 1) fs *) -(* Proof: - Let l4 = y::l3, then - ls = l1 ++ x::l2 ++ l4 - = l1 ++ x::(l2 ++ l4) by APPEND_ASSOC_CONS - Thus x = EL j fs by FILTER_EL_IMP - - Now let l5 = l1 ++ x::l2, - k = LENGTH (FILTER P l5). - Then ls = l5 ++ y::l3 by APPEND_ASSOC - and y = EL k fs by FILTER_EL_IMP - - Note FILTER P l5 - = FILTER P l1 ++ FILTER P (x::l2) by FILTER_APPEND_DISTRIB - = FILTER P l1 ++ x :: FILTER P l2 by FILTER - = FILTER P l1 ++ [x] by FILTER P l2 = [] - and k = LENGTH (FILTER P l5) - = LENGTH (FILTER P l1 ++ [x]) by above - = j + 1 by LENGTH_APPEND -*) -Theorem FILTER_EL_NEXT: - !P ls l1 l2 l3 x y. let fs = FILTER P ls; j = LENGTH (FILTER P l1) in - ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y /\ FILTER P l2 = [] ==> - x = EL j fs /\ y = EL (j + 1) fs -Proof - rw_tac std_ss[] >| [ - qabbrev_tac `l4 = y::l3` >> - qabbrev_tac `ls = l1 ++ x::l2 ++ l4` >> - `ls = l1 ++ x::(l2 ++ l4)` by simp[Abbr`ls`] >> - metis_tac[FILTER_EL_IMP], - qabbrev_tac `l5 = l1 ++ x::l2` >> - qabbrev_tac `ls = l5 ++ y::l3` >> - `FILTER P l5 = FILTER P l1 ++ [x]` by fs[FILTER_APPEND_DISTRIB, Abbr`l5`] >> - `LENGTH (FILTER P l5) = j + 1` by fs[Abbr`j`] >> - metis_tac[FILTER_EL_IMP] - ] -QED - -(* Theorem: let fs = FILTER P ls; j = LENGTH (FILTER P l1) in - ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> - (x = EL j fs /\ y = EL (j + 1) fs <=> FILTER P l2 = []) *) -(* Proof: - Note fs = FILTER P ls - = FILTER P (l1 ++ x::l2 ++ y::l3) by given - = FILTER P l1 ++ - x :: FILTER P l2 ++ - y :: FILTER P l3 by FILTER_APPEND_DISTRIB, FILTER - Thus LENGTH fs - = j + SUC (LENGTH (FILTER P l2)) - + SUC (LENGTH (FILTER P l3)) by LENGTH_APPEND - or j + 2 <= LENGTH fs by arithmetic - or j < LENGTH fs, j + 1 < LENGTH fs by inequality - - Let l4 = y::l3, then - ls = l1 ++ x::l2 ++ l4 - = l1 ++ x::(l2 ++ l4) by APPEND_ASSOC_CONS - Thus x = EL j fs by FILTER_EL_IFF, j < LENGTH fs - - Now let l5 = l1 ++ x::l2, - k = LENGTH (FILTER P l5). - Then ls = l5 ++ y::l3 by APPEND_ASSOC - and fs = FILTER P l5 ++ - y :: FILTER P l3 by FILTER_APPEND_DISTRIB, FILTER - so LENGTH fs = k + SUC (LENGTH P l3) by LENGTH_APPEND - Thus k < LENGTH fs - and y = EL k fs by FILTER_EL_IFF - - Also FILTER P l5 = FILTER P l1 ++ - x :: FILTER P l2 by FILTER_APPEND_DISTRIB, FILTER - so k = j + SUC (LENGTH (FILTER P l2)) by LENGTH_APPEND - Thus k = j + 1 - <=> LENGTH (FILTER P l2) = 0 by arithmetic - - Note ALL_DISTINCT fs by FILTER_ALL_DISTINCT - so EL k fs = EL (j + 1) fs - <=> k = j + 1 - <=> LENGTH (FILTER P l2) = 0 by above - <=> FILTER P l2 = [] by LENGTH_EQ_0 -*) -Theorem FILTER_EL_NEXT_IFF: - !P ls l1 l2 l3 x y. let fs = FILTER P ls; j = LENGTH (FILTER P l1) in - ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> - (x = EL j fs /\ y = EL (j + 1) fs <=> FILTER P l2 = []) -Proof - rw_tac std_ss[] >> - qabbrev_tac `ls = l1 ++ x::l2 ++ y::l3` >> - `j + 2 <= LENGTH fs` by - (`fs = FILTER P l1 ++ x::FILTER P l2 ++ y::FILTER P l3` by simp[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`ls`] >> - `LENGTH fs = j + SUC (LENGTH (FILTER P l2)) + SUC (LENGTH (FILTER P l3))` by fs[Abbr`j`] >> - decide_tac) >> - `j < LENGTH fs` by decide_tac >> - qabbrev_tac `l4 = y::l3` >> - `ls = l1 ++ x::(l2 ++ l4)` by simp[Abbr`ls`] >> - `x = EL j fs` by metis_tac[FILTER_EL_IFF] >> - qabbrev_tac `l5 = l1 ++ x::l2` >> - qabbrev_tac `k = LENGTH (FILTER P l5)` >> - `ls = l5 ++ y::l3` by simp[Abbr`l5`, Abbr`ls`] >> - `k < LENGTH fs /\ (k = j + 1 <=> FILTER P l2 = [])` by - (`fs = FILTER P l5 ++ y::FILTER P l3` by rfs[FILTER_APPEND_DISTRIB, Abbr`fs`] >> - `LENGTH fs = k + SUC (LENGTH (FILTER P l3))` by fs[Abbr`k`] >> - `FILTER P l5 = FILTER P l1 ++ x :: FILTER P l2` by rfs[FILTER_APPEND_DISTRIB, Abbr`l5`] >> - `k = j + SUC (LENGTH (FILTER P l2))` by fs[Abbr`k`, Abbr`j`] >> - simp[]) >> - `y = EL k fs` by metis_tac[FILTER_EL_IFF] >> - `j + 1 < LENGTH fs` by decide_tac >> - `ALL_DISTINCT fs` by simp[FILTER_ALL_DISTINCT, Abbr`fs`] >> - metis_tac[ALL_DISTINCT_EL_IMP] -QED - -(* Theorem: let fs = FILTER P ls in - ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> - (findi y fs = 1 + findi x fs <=> FILTER P l2 = []) *) -(* Proof: - Let j = LENGTH (FILTER P l1). - - Note fs = FILTER P l1 ++ x::FILTER P l2 ++ - y::FILTER P l3 by FILTER_APPEND_DISTRIB - Thus LENGTH fs = j + - SUC (LENGTH (FILTER P l2)) + - SUC (LENGTH (FILTER P l3)) by LENGTH_APPEND - or j + 2 <= LENGTH fs by arithmetic - or j < LENGTH fs /\ j + 1 < LENGTH fs by j + 2 <= LENGTH fs - - Let l4 = y::l3, - Then ls = l1 ++ x::l2 ++ l4 - = l1 ++ x::(l2 ++ l4) by APPEND_ASSOC_CONS - ==> x = EL j fs by FILTER_EL_IMP - - Note ALL_DISTINCT fs by FILTER_ALL_DISTINCT - and MEM x ls /\ MEM y ls by MEM_APPEND - so MEM x fs /\ MEM y fs by MEM_FILTER - and x = EL j fs <=> findi x fs = j by findi_EL_iff - and y = EL (j + 1) fs <=> findi y fs = j + 1 by findi_EL_iff - - FILTER P l2 = [] - <=> x = EL j fs /\ y = EL (j + 1) fs by FILTER_EL_NEXT_IFF - <=> findi y fs = 1 + findi x fs by above -*) -Theorem FILTER_EL_NEXT_IDX: - !P ls l1 l2 l3 x y. let fs = FILTER P ls in - ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> - (findi y fs = 1 + findi x fs <=> FILTER P l2 = []) -Proof - rw_tac std_ss[] >> - qabbrev_tac `ls = l1 ++ x::l2 ++ y::l3` >> - qabbrev_tac `j = LENGTH (FILTER P l1)` >> - `j + 2 <= LENGTH fs` by - (`fs = FILTER P l1 ++ x::FILTER P l2 ++ y::FILTER P l3` by simp[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`ls`] >> - `LENGTH fs = j + SUC (LENGTH (FILTER P l2)) + SUC (LENGTH (FILTER P l3))` by fs[Abbr`j`] >> - decide_tac) >> - `j < LENGTH fs /\ j + 1 < LENGTH fs` by decide_tac >> - `x = EL j fs` by - (qabbrev_tac `l4 = y::l3` >> - `ls = l1 ++ x::(l2 ++ l4)` by simp[Abbr`ls`] >> - metis_tac[FILTER_EL_IMP]) >> - `MEM x ls /\ MEM y ls` by fs[Abbr`ls`] >> - `MEM x fs /\ MEM y fs` by fs[MEM_FILTER, Abbr`fs`] >> - `ALL_DISTINCT fs` by simp[FILTER_ALL_DISTINCT, Abbr`fs`] >> - `x = EL j fs <=> findi x fs = j` by fs[findi_EL_iff] >> - `y = EL (j + 1) fs <=> findi y fs = 1 + j` by fs[findi_EL_iff] >> - metis_tac[FILTER_EL_NEXT_IFF] -QED - -(* ------------------------------------------------------------------------- *) -(* List Rotation. *) -(* ------------------------------------------------------------------------- *) - -(* Define rotation of a list *) -val rotate_def = Define ` - rotate n l = DROP n l ++ TAKE n l -`; - -(* Theorem: Rotate shifts element - rotate n l = EL n l::(DROP (SUC n) l ++ TAKE n l) *) -(* Proof: - h h t t t t t t --> t t t t t h h - k k - TAKE 2 x = h h - DROP 2 x = t t t t t t - k - DROP 2 x ++ TAKE 2 x has element k at front. - - Proof: by induction on l. - Base case: !n. n < LENGTH [] ==> (DROP n [] = EL n []::DROP (SUC n) []) - Since n < LENGTH [] = 0 is F, this is true. - Step case: !h n. n < LENGTH (h::l) ==> (DROP n (h::l) = EL n (h::l)::DROP (SUC n) (h::l)) - i.e. n <> 0 /\ n < SUC (LENGTH l) ==> DROP (n - 1) l = EL n (h::l)::DROP n l by DROP_def - n <> 0 means ?j. n = SUC j < SUC (LENGTH l), so j < LENGTH l. - LHS = DROP (SUC j - 1) l - = DROP j l by SUC j - 1 = j - = EL j l :: DROP (SUC j) l by induction hypothesis - RHS = EL (SUC j) (h::l) :: DROP (SUC (SUC j)) (h::l) - = EL j l :: DROP (SUC j) l by EL, DROP_def - = LHS -*) -Theorem rotate_shift_element: - !l n. n < LENGTH l ==> (rotate n l = EL n l::(DROP (SUC n) l ++ TAKE n l)) -Proof - rw[rotate_def] >> - pop_assum mp_tac >> - qid_spec_tac `n` >> - Induct_on `l` >- rw[] >> - rw[DROP_def] >> Cases_on `n` >> fs[] -QED - -(* Theorem: rotate 0 l = l *) -(* Proof: - rotate 0 l - = DROP 0 l ++ TAKE 0 l by rotate_def - = l ++ [] by DROP_def, TAKE_def - = l by APPEND -*) -val rotate_0 = store_thm( - "rotate_0", - ``!l. rotate 0 l = l``, - rw[rotate_def]); - -(* Theorem: rotate n [] = [] *) -(* Proof: - rotate n [] - = DROP n [] ++ TAKE n [] by rotate_def - = [] ++ [] by DROP_def, TAKE_def - = [] by APPEND -*) -val rotate_nil = store_thm( - "rotate_nil", - ``!n. rotate n [] = []``, - rw[rotate_def]); - -(* Theorem: rotate (LENGTH l) l = l *) -(* Proof: - rotate (LENGTH l) l - = DROP (LENGTH l) l ++ TAKE (LENGTH l) l by rotate_def - = [] ++ TAKE (LENGTH l) l by DROP_LENGTH_NIL - = [] ++ l by TAKE_LENGTH_ID - = l -*) -val rotate_full = store_thm( - "rotate_full", - ``!l. rotate (LENGTH l) l = l``, - rw[rotate_def, DROP_LENGTH_NIL]); - -(* Theorem: n < LENGTH l ==> rotate (SUC n) l = rotate 1 (rotate n l) *) -(* Proof: - Since n < LENGTH l, l <> [] by LENGTH_NIL. - Thus DROP n l <> [] by DROP_EQ_NIL (need n < LENGTH l) - Expand by rotate_def, this is to show: - DROP (SUC n) l ++ TAKE (SUC n) l = DROP 1 (DROP n l ++ TAKE n l) ++ TAKE 1 (DROP n l ++ TAKE n l) - LHS = DROP (SUC n) l ++ TAKE (SUC n) l - = DROP 1 (DROP n l) ++ (TAKE n l ++ TAKE 1 (DROP n l)) by DROP_SUC, TAKE_SUC - Since DROP n l <> [] from above, - RHS = DROP 1 (DROP n l ++ TAKE n l) ++ TAKE 1 (DROP n l ++ TAKE n l) - = DROP 1 (DROP n l) ++ (TAKE n l ++ TAKE 1 (DROP n l)) by DROP_1_APPEND, TAKE_1_APPEND - = LHS -*) -val rotate_suc = store_thm( - "rotate_suc", - ``!l n. n < LENGTH l ==> (rotate (SUC n) l = rotate 1 (rotate n l))``, - rpt strip_tac >> - `LENGTH l <> 0` by decide_tac >> - `l <> []` by metis_tac[LENGTH_NIL] >> - `DROP n l <> []` by simp[DROP_EQ_NIL] >> - rw[rotate_def, DROP_1_APPEND, TAKE_1_APPEND, DROP_SUC, TAKE_SUC]); - -(* Theorem: Rotate keeps LENGTH (of necklace): LENGTH (rotate n l) = LENGTH l *) -(* Proof: - LENGTH (rotate n l) - = LENGTH (DROP n l ++ TAKE n l) by rotate_def - = LENGTH (DROP n l) + LENGTH (TAKE n l) by LENGTH_APPEND - = LENGTH (TAKE n l) + LENGTH (DROP n l) by arithmetic - = LENGTH (TAKE n l ++ DROP n l) by LENGTH_APPEND - = LENGTH l by TAKE_DROP -*) -val rotate_same_length = store_thm( - "rotate_same_length", - ``!l n. LENGTH (rotate n l) = LENGTH l``, - rpt strip_tac >> - `LENGTH (rotate n l) = LENGTH (DROP n l ++ TAKE n l)` by rw[rotate_def] >> - `_ = LENGTH (DROP n l) + LENGTH (TAKE n l)` by rw[] >> - `_ = LENGTH (TAKE n l) + LENGTH (DROP n l)` by rw[ADD_COMM] >> - `_ = LENGTH (TAKE n l ++ DROP n l)` by rw[] >> - rw_tac std_ss[TAKE_DROP]); - -(* Theorem: Rotate keeps SET (of elements): set (rotate n l) = set l *) -(* Proof: - set (rotate n l) - = set (DROP n l ++ TAKE n l) by rotate_def - = set (DROP n l) UNION set (TAKE n l) by LIST_TO_SET_APPEND - = set (TAKE n l) UNION set (DROP n l) by UNION_COMM - = set (TAKE n l ++ DROP n l) by LIST_TO_SET_APPEND - = set l by TAKE_DROP -*) -val rotate_same_set = store_thm( - "rotate_same_set", - ``!l n. set (rotate n l) = set l``, - rpt strip_tac >> - `set (rotate n l) = set (DROP n l ++ TAKE n l)` by rw[rotate_def] >> - `_ = set (DROP n l) UNION set (TAKE n l)` by rw[] >> - `_ = set (TAKE n l) UNION set (DROP n l)` by rw[UNION_COMM] >> - `_ = set (TAKE n l ++ DROP n l)` by rw[] >> - rw_tac std_ss[TAKE_DROP]); - -(* Theorem: n + m <= LENGTH l ==> rotate n (rotate m l) = rotate (n + m) l *) -(* Proof: - By induction on n. - Base case: !m l. 0 + m <= LENGTH l ==> (rotate 0 (rotate m l) = rotate (0 + m) l) - rotate 0 (rotate m l) - = rotate m l by rotate_0 - = rotate (0 + m) l by ADD - Step case: !m l. SUC n + m <= LENGTH l ==> (rotate (SUC n) (rotate m l) = rotate (SUC n + m) l) - rotate (SUC n) (rotate m l) - = rotate 1 (rotate n (rotate m l)) by rotate_suc - = rotate 1 (rotate (n + m) l) by induction hypothesis - = rotate (SUC (n + m)) l by rotate_suc - = rotate (SUC n + m) l by ADD_CLAUSES -*) -val rotate_add = store_thm( - "rotate_add", - ``!n m l. n + m <= LENGTH l ==> (rotate n (rotate m l) = rotate (n + m) l)``, - Induct >- - rw[rotate_0] >> - rw[] >> - `LENGTH (rotate m l) = LENGTH l` by rw[rotate_same_length] >> - `LENGTH (rotate (n + m) l) = LENGTH l` by rw[rotate_same_length] >> - `n < LENGTH l /\ n + m < LENGTH l /\ n + m <= LENGTH l` by decide_tac >> - rw[rotate_suc, ADD_CLAUSES]); - -(* Theorem: !k. k < LENGTH l ==> rotate (LENGTH l - k) (rotate k l) = l *) -(* Proof: - Since k < LENGTH l - LENGTH 1 - k + k = LENGTH l <= LENGTH l by EQ_LESS_EQ - rotate (LENGTH l - k) (rotate k l) - = rotate (LENGTH l - k + k) l by rotate_add - = rotate (LENGTH l) l by arithmetic - = l by rotate_full -*) -val rotate_lcancel = store_thm( - "rotate_lcancel", - ``!k l. k < LENGTH l ==> (rotate (LENGTH l - k) (rotate k l) = l)``, - rpt strip_tac >> - `LENGTH l - k + k = LENGTH l` by decide_tac >> - `LENGTH l <= LENGTH l` by rw[] >> - rw[rotate_add, rotate_full]); - -(* Theorem: !k. k < LENGTH l ==> rotate k (rotate (LENGTH l - k) l) = l *) -(* Proof: - Since k < LENGTH l - k + (LENGTH 1 - k) = LENGTH l <= LENGTH l by EQ_LESS_EQ - rotate k (rotate (LENGTH l - k) l) - = rotate (k + (LENGTH l - k)) l by rotate_add - = rotate (LENGTH l) l by arithmetic - = l by rotate_full -*) -val rotate_rcancel = store_thm( - "rotate_rcancel", - ``!k l. k < LENGTH l ==> (rotate k (rotate (LENGTH l - k) l) = l)``, - rpt strip_tac >> - `k + (LENGTH l - k) = LENGTH l` by decide_tac >> - `LENGTH l <= LENGTH l` by rw[] >> - rw[rotate_add, rotate_full]); - -(* ------------------------------------------------------------------------- *) -(* List Turn *) -(* ------------------------------------------------------------------------- *) - -(* Define a rotation turn of a list (like a turnstile) *) -val turn_def = Define` - turn l = if l = [] then [] else ((LAST l) :: (FRONT l)) -`; - -(* Theorem: turn [] = [] *) -(* Proof: by turn_def *) -val turn_nil = store_thm( - "turn_nil", - ``turn [] = []``, - rw[turn_def]); - -(* Theorem: l <> [] ==> (turn l = (LAST l) :: (FRONT l)) *) -(* Proof: by turn_def *) -val turn_not_nil = store_thm( - "turn_not_nil", - ``!l. l <> [] ==> (turn l = (LAST l) :: (FRONT l))``, - rw[turn_def]); - -(* Theorem: LENGTH (turn l) = LENGTH l *) -(* Proof: - If l = [], - LENGTH (turn []) = LENGTH [] by turn_def - If l <> [], - Then LENGTH l <> 0 by LENGTH_NIL - LENGTH (turn l) - = LENGTH ((LAST l) :: (FRONT l)) by turn_def - = SUC (LENGTH (FRONT l)) by LENGTH - = SUC (PRE (LENGTH l)) by LENGTH_FRONT - = LENGTH l by SUC_PRE, 0 < LENGTH l -*) -val turn_length = store_thm( - "turn_length", - ``!l. LENGTH (turn l) = LENGTH l``, - metis_tac[turn_def, list_CASES, LENGTH, LENGTH_FRONT_CONS, SUC_PRE, NOT_ZERO_LT_ZERO]); - -(* Theorem: (turn p = []) <=> (p = []) *) -(* Proof: - turn p = [] - <=> LENGTH (turn p) = 0 by LENGTH_NIL - <=> LENGTH p = 0 by turn_length - <=> p = [] by LENGTH_NIL -*) -val turn_eq_nil = store_thm( - "turn_eq_nil", - ``!p. (turn p = []) <=> (p = [])``, - metis_tac[turn_length, LENGTH_NIL]); - -(* Theorem: ls <> [] ==> (HD (turn ls) = LAST ls) *) -(* Proof: - HD (turn ls) - = HD (LAST ls :: FRONT ls) by turn_def, ls <> [] - = LAST ls by HD -*) -val head_turn = store_thm( - "head_turn", - ``!ls. ls <> [] ==> (HD (turn ls) = LAST ls)``, - rw[turn_def]); - -(* Theorem: ls <> [] ==> (TL (turn ls) = FRONT ls) *) -(* Proof: - TL (turn ls) - = TL (LAST ls :: FRONT ls) by turn_def, ls <> [] - = FRONT ls by TL -*) -Theorem tail_turn: - !ls. ls <> [] ==> (TL (turn ls) = FRONT ls) -Proof - rw[turn_def] -QED - -(* Theorem: turn (SNOC x ls) = x :: ls *) -(* Proof: - Note (SNOC x ls) <> [] by NOT_SNOC_NIL - turn (SNOC x ls) - = LAST (SNOC x ls) :: FRONT (SNOC x ls) by turn_def - = x :: FRONT (SNOC x ls) by LAST_SNOC - = x :: ls by FRONT_SNOC -*) -Theorem turn_snoc: - !ls x. turn (SNOC x ls) = x :: ls -Proof - metis_tac[NOT_SNOC_NIL, turn_def, LAST_SNOC, FRONT_SNOC] -QED - -(* Overload repeated turns *) -val _ = overload_on("turn_exp", ``\l n. FUNPOW turn n l``); - -(* Theorem: turn_exp l 0 = l *) -(* Proof: - turn_exp l 0 - = FUNPOW turn 0 l by notation - = l by FUNPOW -*) -val turn_exp_0 = store_thm( - "turn_exp_0", - ``!l. turn_exp l 0 = l``, - rw[]); - -(* Theorem: turn_exp l 1 = turn l *) -(* Proof: - turn_exp l 1 - = FUNPOW turn 1 l by notation - = turn l by FUNPOW -*) -val turn_exp_1 = store_thm( - "turn_exp_1", - ``!l. turn_exp l 1 = turn l``, - rw[]); - -(* Theorem: turn_exp l 2 = turn (turn l) *) -(* Proof: - turn_exp l 2 - = FUNPOW turn 2 l by notation - = turn (FUNPOW turn 1 l) by FUNPOW_SUC - = turn (turn_exp l 1) by notation - = turn (turn l) by turn_exp_1 -*) -val turn_exp_2 = store_thm( - "turn_exp_2", - ``!l. turn_exp l 2 = turn (turn l)``, - metis_tac[FUNPOW_SUC, turn_exp_1, TWO]); - -(* Theorem: turn_exp l (SUC n) = turn_exp (turn l) n *) -(* Proof: - turn_exp l (SUC n) - = FUNPOW turn (SUC n) l by notation - = FUNPOW turn n (turn l) by FUNPOW - = turn_exp (turn l) n by notation -*) -val turn_exp_SUC = store_thm( - "turn_exp_SUC", - ``!l n. turn_exp l (SUC n) = turn_exp (turn l) n``, - rw[FUNPOW]); - -(* Theorem: turn_exp l (SUC n) = turn (turn_exp l n) *) -(* Proof: - turn_exp l (SUC n) - = FUNPOW turn (SUC n) l by notation - = turn (FUNPOW turn n l) by FUNPOW_SUC - = turn (turn_exp l n) by notation -*) -val turn_exp_suc = store_thm( - "turn_exp_suc", - ``!l n. turn_exp l (SUC n) = turn (turn_exp l n)``, - rw[FUNPOW_SUC]); - -(* Theorem: LENGTH (turn_exp l n) = LENGTH l *) -(* Proof: - By induction on n. - Base: LENGTH (turn_exp l 0) = LENGTH l - True by turn_exp l 0 = l by turn_exp_0 - Step: LENGTH (turn_exp l n) = LENGTH l ==> LENGTH (turn_exp l (SUC n)) = LENGTH l - LENGTH (turn_exp l (SUC n)) - = LENGTH (turn (turn_exp l n)) by turn_exp_suc - = LENGTH (turn_exp l n) by turn_length - = LENGTH l by induction hypothesis -*) -val turn_exp_length = store_thm( - "turn_exp_length", - ``!l n. LENGTH (turn_exp l n) = LENGTH l``, - strip_tac >> - Induct >- - rw[] >> - rw[turn_exp_suc, turn_length]); - -(* Theorem: n < LENGTH ls ==> - (HD (turn_exp ls n) = EL (if n = 0 then 0 else LENGTH ls - n) ls) *) -(* Proof: - By induction on n. - Base: !ls. 0 < LENGTH ls ==> - HD (turn_exp ls 0) = EL 0 ls - HD (turn_exp ls 0) - = HD ls by FUNPOW_0 - = EL 0 ls by EL - Step: !ls. n < LENGTH ls ==> HD (turn_exp ls n) = EL (if n = 0 then 0 else (LENGTH ls - n)) ls ==> - !ls. SUC n < LENGTH ls ==> HD (turn_exp ls (SUC n)) = EL (LENGTH ls - SUC n) ls - Let k = LENGTH ls, then SUC n < k - Note LENGTH (FRONT ls) = PRE k by FRONT_LENGTH - and n < PRE k by SUC n < k - Also LENGTH (turn ls) = k by turn_length - so n < k by n < SUC n, SUC n < k - Note ls <> [] by k <> 0 - - HD (turn_exp ls (SUC n)) - = HD (turn_exp (turn ls) n) by turn_exp_SUC - = EL (if n = 0 then 0 else (LENGTH (turn ls) - n)) (turn ls) - by induction hypothesis, apply to (turn ls) - = EL (if n = 0 then 0 else (k - n) (turn ls)) by above - - If n = 0, - = EL 0 (turn ls) - = LAST ls by turn_def - = EL (PRE k) ls by LAST_EL - = EL (k - SUC 0) ls by ONE - If n <> 0 - = EL (k - n) (turn ls) - = EL (k - n) (LAST ls :: FRONT ls) by turn_def - = EL (k - n - 1) (FRONT ls) by EL - = EL (k - n - 1) ls by FRONT_EL, k - n - 1 < PRE k, n <> 0 - = EL (k - SUC n) ls by arithmetic -*) -val head_turn_exp = store_thm( - "head_turn_exp", - ``!ls n. n < LENGTH ls ==> - (HD (turn_exp ls n) = EL (if n = 0 then 0 else LENGTH ls - n) ls)``, - (Induct_on `n` >> simp[]) >> - rpt strip_tac >> - qabbrev_tac `k = LENGTH ls` >> - `n < k` by rw[Abbr`k`] >> - `LENGTH (turn ls) = k` by rw[turn_length, Abbr`k`] >> - `HD (turn_exp ls (SUC n)) = HD (turn_exp (turn ls) n)` by rw[turn_exp_SUC] >> - `_ = EL (if n = 0 then 0 else (k - n)) (turn ls)` by rw[] >> - `k <> 0` by decide_tac >> - `ls <> []` by metis_tac[LENGTH_NIL] >> - (Cases_on `n = 0` >> fs[]) >| [ - `PRE k = k - 1` by decide_tac >> - rw[head_turn, LAST_EL], - `k - n = SUC (k - SUC n)` by decide_tac >> - rw[turn_def, Abbr`k`] >> - `LENGTH (FRONT ls) = PRE (LENGTH ls)` by rw[FRONT_LENGTH] >> - `n < PRE (LENGTH ls)` by decide_tac >> - rw[FRONT_EL] - ]); - -(* ------------------------------------------------------------------------- *) -(* Unit-List and Mono-List *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: (LENGTH l = 1) ==> SING (set l) *) -(* Proof: - Since ?x. l = [x] by LENGTH_EQ_1 - set l = {x} by LIST_TO_SET_DEF - or SING (set l) by SING_DEF -*) -val LIST_TO_SET_SING = store_thm( - "LIST_TO_SET_SING", - ``!l. (LENGTH l = 1) ==> SING (set l)``, - rw[LENGTH_EQ_1, SING_DEF] >> - `set [x] = {x}` by rw[] >> - metis_tac[]); - -(* Mono-list Theory: a mono-list is a list l with SING (set l) *) - -(* Theorem: Two mono-lists are equal if their lengths and sets are equal. - SING (set l1) /\ SING (set l2) ==> - ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) *) -(* Proof: - By induction on l1. - Base case: !l2. SING (set []) /\ SING (set l2) ==> - (([] = l2) <=> (LENGTH [] = LENGTH l2) /\ (set [] = set l2)) - True by SING (set []) is False, by SING_EMPTY. - Step case: !l2. SING (set l1) /\ SING (set l2) ==> - ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) ==> - !h l2. SING (set (h::l1)) /\ SING (set l2) ==> - ((h::l1 = l2) <=> (LENGTH (h::l1) = LENGTH l2) /\ (set (h::l1) = set l2)) - This is to show: - (1) 1 = LENGTH l2 /\ {h} = set l2 ==> - ([h] = l2) <=> (SUC (LENGTH []) = LENGTH l2) /\ (h INSERT set [] = set l2) - If-part, l2 = [h], - LENGTH l2 = 1 = SUC 0 = SUC (LENGTH []) by LENGTH, ONE - and set l2 = set [h] = {h} = h INSERT set [] by LIST_TO_SET - Only-if part, LENGTH l2 = SUC 0 = 1 by ONE - Then ?x. l2 = [x] by LENGTH_EQ_1 - so set l2 = {x} = {h} by LIST_TO_SET - or x = h, hence l2 = [h] by EQUAL_SING - (2) set l1 = {h} /\ SING (set l2) ==> - (h::l1 = l2) <=> (SUC (LENGTH l1) = LENGTH l2) /\ (h INSERT set l1 = set l2) - If part, h::l1 = l2. - Then LENGTH l2 = LENGTH (h::l1) = SUC (LENGTH l1) by LENGTH - and set l2 = set (h::l1) = h INSERT set l1 by LIST_TO_SET - Only-if part, SUC (LENGTH l1) = LENGTH l2. - Since 0 < SUC (LENGTH l1) by prim_recTheory.LESS_0 - 0 < LENGTH l2 by LESS_TRANS - so ?k t. l2 = k::t by LENGTH_NON_NIL, list_CASES - Since LENGTH l2 = SUC (LENGTH t) by LENGTH - LENGTH l1 = LENGTH t by prim_recTheory.INV_SUC_EQ - and set l2 = k INSERT set t by LIST_TO_SET - Given SING (set l2), - either (set t = {}), or (set t = {k}) by SING_INSERT - If set t = {}, - then t = [] by LIST_TO_SET_EQ_EMPTY - and l1 = [] by LENGTH_NIL, LENGTH l1 = LENGTH t. - so set l1 = {} by LIST_TO_SET_EQ_EMPTY - contradicting set l1 = {h} by NOT_SING_EMPTY - If set t = {k}, - then set l2 = set t by ABSORPTION, set l2 = k INSERT set {k}. - or k = h by IN_SING - so l1 = t by induction hypothesis - giving l2 = h::l1 -*) -Theorem MONOLIST_EQ: - !l1 l2. SING (set l1) /\ SING (set l2) ==> - ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) -Proof - Induct >> rw[NOT_SING_EMPTY, SING_INSERT] >| [ - Cases_on `l2` >> rw[] >> - full_simp_tac (srw_ss()) [SING_INSERT, EQUAL_SING] >> - rw[LENGTH_NIL, NOT_SING_EMPTY, EQUAL_SING] >> metis_tac[], - Cases_on `l2` >> rw[] >> - full_simp_tac (srw_ss()) [SING_INSERT, LENGTH_NIL, NOT_SING_EMPTY, - EQUAL_SING] >> - metis_tac[] - ] -QED - -(* Theorem: A non-mono-list has at least one element in tail that is distinct from its head. - ~SING (set (h::t)) ==> ?h'. h' IN set t /\ h' <> h *) -(* Proof: - By SING_INSERT, this is to show: - t <> [] /\ set t <> {h} ==> ?h'. MEM h' t /\ h' <> h - Now, t <> [] ==> set t <> {} by LIST_TO_SET_EQ_EMPTY - so ?e. e IN set t by MEMBER_NOT_EMPTY - hence MEM e t, - and MEM x t <=/=> (x = h) by EXTENSION - Therefore, e <> h, so take h' = e. -*) -val NON_MONO_TAIL_PROPERTY = store_thm( - "NON_MONO_TAIL_PROPERTY", - ``!l. ~SING (set (h::t)) ==> ?h'. h' IN set t /\ h' <> h``, - rw[SING_INSERT] >> - `set t <> {}` by metis_tac[LIST_TO_SET_EQ_EMPTY] >> - `?e. e IN set t` by metis_tac[MEMBER_NOT_EMPTY] >> - full_simp_tac (srw_ss())[EXTENSION] >> - metis_tac[]); - -(* ------------------------------------------------------------------------- *) -(* GENLIST Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: GENLIST f 0 = [] *) -(* Proof: by GENLIST *) -val GENLIST_0 = store_thm( - "GENLIST_0", - ``!f. GENLIST f 0 = []``, - rw[]); - -(* Theorem: GENLIST f 1 = [f 0] *) -(* Proof: - GENLIST f 1 - = GENLIST f (SUC 0) by ONE - = SNOC (f 0) (GENLIST f 0) by GENLIST - = SNOC (f 0) [] by GENLIST - = [f 0] by SNOC -*) -val GENLIST_1 = store_thm( - "GENLIST_1", - ``!f. GENLIST f 1 = [f 0]``, - rw[]); - -(* Theorem alias *) -Theorem GENLIST_EQ = - listTheory.GENLIST_CONG |> GEN ``n:num`` |> GEN ``f2:num -> 'a`` - |> GEN ``f1:num -> 'a``; -(* -val GENLIST_EQ = |- !f1 f2 n. (!m. m < n ==> f1 m = f2 m) ==> GENLIST f1 n = GENLIST f2 n: thm -*) - -(* Theorem: (GENLIST f n = []) <=> (n = 0) *) -(* Proof: - If part: GENLIST f n = [] ==> n = 0 - By contradiction, suppose n <> 0. - Then LENGTH (GENLIST f n) = n <> 0 by LENGTH_GENLIST - This contradicts LENGTH [] = 0. - Only-if part: GENLIST f 0 = [], true by GENLIST_0 -*) -val GENLIST_EQ_NIL = store_thm( - "GENLIST_EQ_NIL", - ``!f n. (GENLIST f n = []) <=> (n = 0)``, - rw[EQ_IMP_THM] >> - metis_tac[LENGTH_GENLIST, LENGTH_NIL]); - -(* Theorem: LAST (GENLIST f (SUC n)) = f n *) -(* Proof: - LAST (GENLIST f (SUC n)) - = LAST (SNOC (f n) (GENLIST f n)) by GENLIST - = f n by LAST_SNOC -*) -val GENLIST_LAST = store_thm( - "GENLIST_LAST", - ``!f n. LAST (GENLIST f (SUC n)) = f n``, - rw[GENLIST]); - -(* Note: - -- EVERY_MAP; -> val it = |- !P f l. EVERY P (MAP f l) <=> EVERY (\x. P (f x)) l : thm -- EVERY_GENLIST; -> val it = |- !n. EVERY P (GENLIST f n) <=> !i. i < n ==> P (f i) : thm -- MAP_GENLIST; -> val it = |- !f g n. MAP f (GENLIST g n) = GENLIST (f o g) n : thm -*) - -(* Note: the following can use EVERY_GENLIST. *) - -(* Theorem: !k. (k < n ==> f k = c) <=> EVERY (\x. x = c) (GENLIST f n) *) -(* Proof: by induction on n. - Base case: !c. (!k. k < 0 ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f 0) - Since GENLIST f 0 = [], this is true as no k < 0. - Step case: (!k. k < n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f n) ==> - (!k. k < SUC n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f (SUC n)) - EVERY (\x. x = c) (GENLIST f (SUC n)) - <=> EVERY (\x. x = c) (SNOC (f n) (GENLIST f n)) by GENLIST - <=> EVERY (\x. x = c) (GENLIST f n) /\ (f n = c) by EVERY_SNOC - <=> (!k. k < n ==> (f k = c)) /\ (f n = c) by induction hypothesis - <=> !k. k < SUC n ==> (f k = c) -*) -val GENLIST_CONSTANT = store_thm( - "GENLIST_CONSTANT", - ``!f n c. (!k. k < n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f n)``, - strip_tac >> - Induct >- - rw[] >> - rw_tac std_ss[EVERY_DEF, GENLIST, EVERY_SNOC, EQ_IMP_THM] >- - metis_tac[prim_recTheory.LESS_SUC] >> - Cases_on `k = n` >- - rw_tac std_ss[] >> - metis_tac[prim_recTheory.LESS_THM]); - -(* Theorem: GENLIST (K e) (SUC n) = e :: GENLIST (K e) n *) -(* Proof: - GENLIST (K e) (SUC n) - = (K e) 0::GENLIST ((K e) o SUC) n by GENLIST_CONS - = e :: GENLIST ((K e) o SUC) n by K_THM - = e :: GENLIST (K e) n by K_o_THM -*) -val GENLIST_K_CONS = save_thm("GENLIST_K_CONS", - SIMP_CONV (srw_ss()) [GENLIST_CONS] ``GENLIST (K e) (SUC n)`` |> GEN ``n:num`` |> GEN ``e``); -(* val GENLIST_K_CONS = |- !e n. GENLIST (K e) (SUC n) = e::GENLIST (K e) n: thm *) - -(* Theorem: GENLIST (K e) (n + m) = GENLIST (K e) m ++ GENLIST (K e) n *) -(* Proof: - Note (\t. e) = K e by FUN_EQ_THM - GENLIST (K e) (n + m) - = GENLIST (K e) m ++ GENLIST (\t. (K e) (t + m)) n by GENLIST_APPEND - = GENLIST (K e) m ++ GENLIST (\t. e) n by K_THM - = GENLIST (K e) m ++ GENLIST (K e) n by above -*) -val GENLIST_K_ADD = store_thm( - "GENLIST_K_ADD", - ``!e n m. GENLIST (K e) (n + m) = GENLIST (K e) m ++ GENLIST (K e) n``, - rpt strip_tac >> - `(\t. e) = K e` by rw[FUN_EQ_THM] >> - rw[GENLIST_APPEND] >> - metis_tac[]); - -(* Theorem: (!k. k < n ==> (f k = e)) ==> (GENLIST f n = GENLIST (K e) n) *) -(* Proof: - By induction on n. - Base: GENLIST f 0 = GENLIST (K e) 0 - GENLIST f 0 - = [] by GENLIST_0 - = GENLIST (K e) 0 by GENLIST_0 - Step: GENLIST f n = GENLIST (K e) n ==> - GENLIST f (SUC n) = GENLIST (K e) (SUC n) - GENLIST f (SUC n) - = SNOC (f n) (GENLIST f n) by GENLIST - = SNOC e (GENLIST f n) by applying f to n - = SNOC e (GENLIST (K e) n) by induction hypothesis - = GENLIST (K e) (SUC n) by GENLIST -*) -val GENLIST_K_LESS = store_thm( - "GENLIST_K_LESS", - ``!f e n. (!k. k < n ==> (f k = e)) ==> (GENLIST f n = GENLIST (K e) n)``, - rpt strip_tac >> - Induct_on `n` >> - rw[GENLIST]); - -(* Theorem: (!k. 0 < k /\ k <= n ==> (f k = e)) ==> (GENLIST (f o SUC) n = GENLIST (K e) n) *) -(* Proof: - Base: GENLIST (f o SUC) 0 = GENLIST (K e) 0 - GENLIST (f o SUC) 0 - = [] by GENLIST_0 - = GENLIST (K e) 0 by GENLIST_0 - Step: GENLIST (f o SUC) n = GENLIST (K e) n ==> - GENLIST (f o SUC) (SUC n) = GENLIST (K e) (SUC n) - GENLIST (f o SUC) (SUC n) - = SNOC (f n) (GENLIST (f o SUC) n) by GENLIST - = SNOC e (GENLIST (f o SUC) n) by applying f to n - = SNOC e GENLIST (K e) n by induction hypothesis - = GENLIST (K e) (SUC n) by GENLIST -*) -val GENLIST_K_RANGE = store_thm( - "GENLIST_K_RANGE", - ``!f e n. (!k. 0 < k /\ k <= n ==> (f k = e)) ==> (GENLIST (f o SUC) n = GENLIST (K e) n)``, - rpt strip_tac >> - Induct_on `n` >> - rw[GENLIST]); - -(* Theorem: GENLIST (K c) a ++ GENLIST (K c) b = GENLIST (K c) (a + b) *) -(* Proof: - Note (\t. c) = K c by FUN_EQ_THM - GENLIST (K c) (a + b) - = GENLIST (K c) (b + a) by ADD_COMM - = GENLIST (K c) a ++ GENLIST (\t. (K c) (t + a)) b by GENLIST_APPEND - = GENLIST (K c) a ++ GENLIST (\t. c) b by applying constant function - = GENLIST (K c) a ++ GENLIST (K c) b by GENLIST_FUN_EQ -*) -val GENLIST_K_APPEND = store_thm( - "GENLIST_K_APPEND", - ``!a b c. GENLIST (K c) a ++ GENLIST (K c) b = GENLIST (K c) (a + b)``, - rpt strip_tac >> - `(\t. c) = K c` by rw[FUN_EQ_THM] >> - `GENLIST (K c) (a + b) = GENLIST (K c) (b + a)` by rw[] >> - `_ = GENLIST (K c) a ++ GENLIST (\t. (K c) (t + a)) b` by rw[GENLIST_APPEND] >> - rw[GENLIST_FUN_EQ]); - -(* Theorem: GENLIST (K c) n ++ [c] = [c] ++ GENLIST (K c) n *) -(* Proof: - GENLIST (K c) n ++ [c] - = GENLIST (K c) n ++ GENLIST (K c) 1 by GENLIST_1 - = GENLIST (K c) (n + 1) by GENLIST_K_APPEND - = GENLIST (K c) (1 + n) by ADD_COMM - = GENLIST (K c) 1 ++ GENLIST (K c) n by GENLIST_K_APPEND - = [c] ++ GENLIST (K c) n by GENLIST_1 -*) -val GENLIST_K_APPEND_K = store_thm( - "GENLIST_K_APPEND_K", - ``!c n. GENLIST (K c) n ++ [c] = [c] ++ GENLIST (K c) n``, - metis_tac[GENLIST_K_APPEND, GENLIST_1, ADD_COMM, combinTheory.K_THM]); - -(* Theorem: 0 < n ==> (MEM x (GENLIST (K c) n) <=> (x = c)) *) -(* Proof: - MEM x (GENLIST (K c) n - <=> ?m. m < n /\ (x = (K c) m) by MEM_GENLIST - <=> ?m. m < n /\ (x = c) by K_THM - <=> (x = c) by taking m = 0, 0 < n -*) -Theorem GENLIST_K_MEM: - !x c n. 0 < n ==> (MEM x (GENLIST (K c) n) <=> (x = c)) -Proof - metis_tac[MEM_GENLIST, combinTheory.K_THM] -QED - -(* Theorem: 0 < n ==> (set (GENLIST (K c) n) = {c}) *) -(* Proof: - By induction on n. - Base: 0 < 0 ==> (set (GENLIST (K c) 0) = {c}) - Since 0 < 0 = F, hence true. - Step: 0 < n ==> (set (GENLIST (K c) n) = {c}) ==> - 0 < SUC n ==> (set (GENLIST (K c) (SUC n)) = {c}) - If n = 0, - set (GENLIST (K c) (SUC 0) - = set (GENLIST (K c) 1 by ONE - = set [(K c) 0] by GENLIST_1 - = set [c] by K_THM - = {c} by LIST_TO_SET - If n <> 0, 0 < n. - set (GENLIST (K c) (SUC n) - = set (SNOC ((K c) n) (GENLIST (K c) n)) by GENLIST - = set (SNOC c (GENLIST (K c) n) by K_THM - = c INSERT set (GENLIST (K c) n) by LIST_TO_SET_SNOC - = c INSERT {c} by induction hypothesis - = {c} by IN_INSERT - *) -Theorem GENLIST_K_SET: - !c n. 0 < n ==> (set (GENLIST (K c) n) = {c}) -Proof - rpt strip_tac >> - Induct_on `n` >- - simp[] >> - (Cases_on `n = 0` >> simp[]) >> - `0 < n` by decide_tac >> - simp[GENLIST, LIST_TO_SET_SNOC] -QED - -(* Theorem: ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) *) -(* Proof: - By induction on ls. - Base: [] <> [] ==> (SING (set []) <=> ?c. [] = GENLIST (K c) (LENGTH [])) - Since [] <> [] = F, hence true. - Step: ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) ==> - !h. h::ls <> [] ==> - (SING (set (h::ls)) <=> ?c. h::ls = GENLIST (K c) (LENGTH (h::ls))) - Note h::ls <> [] = T. - If part: SING (set (h::ls)) ==> ?c. h::ls = GENLIST (K c) (LENGTH (h::ls)) - Note SING (set (h::ls)) means - set ls = {h} by LIST_TO_SET_DEF, IN_SING - Let n = LENGTH ls, 0 < n by LENGTH_NON_NIL - Note ls <> [] by LIST_TO_SET, IN_SING, MEMBER_NOT_EMPTY - and SING (set ls) by SING_DEF - ==> ?c. ls = GENLIST (K c) n by induction hypothesis - so set ls = {c} by GENLIST_K_SET, 0 < n - ==> h = c by IN_SING - GENLIST (K c) (LENGTH (h::ls) - = (K c) h :: ls by GENLIST_K_CONS - = c :: ls by K_THM - = h::ls by h = c - Only-if part: ?c. h::ls = GENLIST (K c) (LENGTH (h::ls)) ==> SING (set (h::ls)) - set (h::ls) - = set (GENLIST (K c) (LENGTH (h::ls))) by given - = set ((K c) h :: GENLIST (K c) (LENGTH ls)) by GENLIST_K_CONS - = set (c :: GENLIST (K c) (LENGTH ls)) by K_THM - = c INSERT set (GENLIST (K c) (LENGTH ls)) by LIST_TO_SET - = c INSERT {c} by GENLIST_K_SET - = {c} by IN_INSERT - Hence SING (set (h::ls)) by SING_DEF -*) -Theorem LIST_TO_SET_SING_IFF: - !ls. ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) -Proof - Induct >- - simp[] >> - (rw[EQ_IMP_THM] >> simp[]) >| [ - qexists_tac `h` >> - qabbrev_tac `n = LENGTH ls` >> - `ls <> []` by metis_tac[LIST_TO_SET, IN_SING, MEMBER_NOT_EMPTY] >> - `SING (set ls)` by fs[SING_DEF] >> - fs[] >> - `0 < n` by metis_tac[LENGTH_NON_NIL] >> - `h = c` by metis_tac[GENLIST_K_SET, IN_SING] >> - simp[GENLIST_K_CONS], - spose_not_then strip_assume_tac >> - fs[GENLIST_K_CONS] >> - `0 < LENGTH ls` by metis_tac[LENGTH_NON_NIL] >> - metis_tac[GENLIST_K_SET] - ] -QED - -(* ------------------------------------------------------------------------- *) -(* SUM Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Defined: SUM for summation of list = sequence *) - -(* Theorem: SUM [] = 0 *) -(* Proof: by definition. *) -val SUM_NIL = save_thm("SUM_NIL", SUM |> CONJUNCT1); -(* > val SUM_NIL = |- SUM [] = 0 : thm *) - -(* Theorem: SUM h::t = h + SUM t *) -(* Proof: by definition. *) -val SUM_CONS = save_thm("SUM_CONS", SUM |> CONJUNCT2); -(* val SUM_CONS = |- !h t. SUM (h::t) = h + SUM t: thm *) - -(* Theorem: SUM [n] = n *) -(* Proof: by SUM *) -val SUM_SING = store_thm( - "SUM_SING", - ``!n. SUM [n] = n``, - rw[]); - -(* Theorem: SUM (s ++ t) = SUM s + SUM t *) -(* Proof: by induction on s *) -(* -val SUM_APPEND = store_thm( - "SUM_APPEND", - ``!s t. SUM (s ++ t) = SUM s + SUM t``, - Induct_on `s` >- - rw[] >> - rw[ADD_ASSOC]); -*) -(* There is already a SUM_APPEND in up-to-date listTheory *) - -(* Theorem: constant multiplication: k * SUM s = SUM (k * s) *) -(* Proof: by induction on s. - Base case: !k. k * SUM [] = SUM (MAP ($* k) []) - LHS = k * SUM [] = k * 0 = 0 by SUM_NIL, MULT_0 - = SUM [] by SUM_NIL - = SUM (MAP ($* k) []) = RHS by MAP - Step case: !k. k * SUM s = SUM (MAP ($* k) s) ==> - !h k. k * SUM (h::s) = SUM (MAP ($* k) (h::s)) - LHS = k * SUM (h::s) - = k * (h + SUM s) by SUM_CONS - = k * h + k * SUM s by LEFT_ADD_DISTRIB - = k * h + SUM (MAP ($* k) s) by induction hypothesis - = SUM (k * h :: (MAP ($* k) s)) by SUM_CONS - = SUM (MAP ($* k) (h::s)) by MAP - = RHS -*) -val SUM_MULT = store_thm( - "SUM_MULT", - ``!s k. k * SUM s = SUM (MAP ($* k) s)``, - Induct_on `s` >- - metis_tac[SUM, MAP, MULT_0] >> - metis_tac[SUM, MAP, LEFT_ADD_DISTRIB]); - -(* Theorem: (m + n) * SUM s = SUM (m * s) + SUM (n * s) *) -(* Proof: generalization of -- RIGHT_ADD_DISTRIB; -> val it = |- !m n p. (m + n) * p = m * p + n * p : thm - (m + n) * SUM s - = m * SUM s + n * SUM s by RIGHT_ADD_DISTRIB - = SUM (MAP (\x. m * x) s) + SUM (MAP (\x. n * x) s) by SUM_MULT -*) -val SUM_RIGHT_ADD_DISTRIB = store_thm( - "SUM_RIGHT_ADD_DISTRIB", - ``!s m n. (m + n) * SUM s = SUM (MAP ($* m) s) + SUM (MAP ($* n) s)``, - metis_tac[RIGHT_ADD_DISTRIB, SUM_MULT]); - -(* Theorem: (SUM s) * (m + n) = SUM (m * s) + SUM (n * s) *) -(* Proof: generalization of -- LEFT_ADD_DISTRIB; -> val it = |- !m n p. p * (m + n) = p * m + p * n : thm - (SUM s) * (m + n) - = (m + n) * SUM s by MULT_COMM - = SUM (MAP ($* m) s) + SUM (MAP ($* n) s) by SUM_RIGHT_ADD_DISTRIB -*) -val SUM_LEFT_ADD_DISTRIB = store_thm( - "SUM_LEFT_ADD_DISTRIB", - ``!s m n. (SUM s) * (m + n) = SUM (MAP ($* m) s) + SUM (MAP ($* n) s)``, - metis_tac[SUM_RIGHT_ADD_DISTRIB, MULT_COMM]); - - -(* -- EVAL ``GENLIST I 4``; -> val it = |- GENLIST I 4 = [0; 1; 2; 3] : thm -- EVAL ``GENLIST SUC 4``; -> val it = |- GENLIST SUC 4 = [1; 2; 3; 4] : thm -- EVAL ``GENLIST (\k. binomial 4 k) 5``; -> val it = |- GENLIST (\k. binomial 4 k) 5 = [1; 4; 6; 4; 1] : thm -- EVAL ``GENLIST (\k. binomial 5 k) 6``; -> val it = |- GENLIST (\k. binomial 5 k) 6 = [1; 5; 10; 10; 5; 1] : thm -- EVAL ``GENLIST (\k. binomial 10 k) 11``; -> val it = |- GENLIST (\k. binomial 10 k) 11 = [1; 10; 45; 120; 210; 252; 210; 120; 45; 10; 1] : thm -*) - -(* Theorems on GENLIST: - -- GENLIST; -> val it = |- (!f. GENLIST f 0 = []) /\ - !f n. GENLIST f (SUC n) = SNOC (f n) (GENLIST f n) : thm -- NULL_GENLIST; -> val it = |- !n f. NULL (GENLIST f n) <=> (n = 0) : thm -- GENLIST_CONS; -> val it = |- GENLIST f (SUC n) = f 0::GENLIST (f o SUC) n : thm -- EL_GENLIST; -> val it = |- !f n x. x < n ==> (EL x (GENLIST f n) = f x) : thm -- EXISTS_GENLIST; -> val it = |- !n. EXISTS P (GENLIST f n) <=> ?i. i < n /\ P (f i) : thm -- EVERY_GENLIST; -> val it = |- !n. EVERY P (GENLIST f n) <=> !i. i < n ==> P (f i) : thm -- MAP_GENLIST; -> val it = |- !f g n. MAP f (GENLIST g n) = GENLIST (f o g) n : thm -- GENLIST_APPEND; -> val it = |- !f a b. GENLIST f (a + b) = GENLIST f b ++ GENLIST (\t. f (t + b)) a : thm -- HD_GENLIST; -> val it = |- HD (GENLIST f (SUC n)) = f 0 : thm -- TL_GENLIST; -> val it = |- !f n. TL (GENLIST f (SUC n)) = GENLIST (f o SUC) n : thm -- HD_GENLIST_COR; -> val it = |- !n f. 0 < n ==> (HD (GENLIST f n) = f 0) : thm -- GENLIST_FUN_EQ; -> val it = |- !n f g. (GENLIST f n = GENLIST g n) <=> !x. x < n ==> (f x = g x) : thm - -*) - -(* Theorem: SUM (GENLIST f n) = SIGMA f (count n) *) -(* Proof: - By induction on n. - Base: SUM (GENLIST f 0) = SIGMA f (count 0) - - SUM (GENLIST f 0) - = SUM [] by GENLIST_0 - = 0 by SUM_NIL - = SIGMA f {} by SUM_IMAGE_THM - = SIGMA f (count 0) by COUNT_0 - - Step: SUM (GENLIST f n) = SIGMA f (count n) ==> - SUM (GENLIST f (SUC n)) = SIGMA f (count (SUC n)) - - SUM (GENLIST f (SUC n)) - = SUM (SNOC (f n) (GENLIST f n)) by GENLIST - = f n + SUM (GENLIST f n) by SUM_SNOC - = f n + SIGMA f (count n) by induction hypothesis - = f n + SIGMA f (count n DELETE n) by IN_COUNT, DELETE_NON_ELEMENT - = SIGMA f (n INSERT count n) by SUM_IMAGE_THM, FINITE_COUNT - = SIGMA f (count (SUC n)) by COUNT_SUC -*) -val SUM_GENLIST = store_thm( - "SUM_GENLIST", - ``!f n. SUM (GENLIST f n) = SIGMA f (count n)``, - strip_tac >> - Induct >- - rw[SUM_IMAGE_THM] >> - `SUM (GENLIST f (SUC n)) = SUM (SNOC (f n) (GENLIST f n))` by rw[GENLIST] >> - `_ = f n + SUM (GENLIST f n)` by rw[SUM_SNOC] >> - `_ = f n + SIGMA f (count n)` by rw[] >> - `_ = f n + SIGMA f (count n DELETE n)` - by metis_tac[IN_COUNT, prim_recTheory.LESS_REFL, DELETE_NON_ELEMENT] >> - `_ = SIGMA f (n INSERT count n)` by rw[SUM_IMAGE_THM] >> - `_ = SIGMA f (count (SUC n))` by rw[COUNT_SUC] >> - decide_tac); - -(* Theorem: SUM (k=0..n) f(k) = f(0) + SUM (k=1..n) f(k) *) -(* Proof: - SUM (GENLIST f (SUC n)) - = SUM (f 0 :: GENLIST (f o SUC) n) by GENLIST_CONS - = f 0 + SUM (GENLIST (f o SUC) n) by SUM definition. -*) -val SUM_DECOMPOSE_FIRST = store_thm( - "SUM_DECOMPOSE_FIRST", - ``!f n. SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) n)``, - metis_tac[GENLIST_CONS, SUM]); - -(* Theorem: SUM (k=0..n) f(k) = SUM (k=0..(n-1)) f(k) + f n *) -(* Proof: - SUM (GENLIST f (SUC n)) - = SUM (SNOC (f n) (GENLIST f n)) by GENLIST definition - = SUM ((GENLIST f n) ++ [f n]) by SNOC_APPEND - = SUM (GENLIST f n) + SUM [f n] by SUM_APPEND - = SUM (GENLIST f n) + f n by SUM definition: SUM (h::t) = h + SUM t, and SUM [] = 0. -*) -val SUM_DECOMPOSE_LAST = store_thm( - "SUM_DECOMPOSE_LAST", - ``!f n. SUM (GENLIST f (SUC n)) = SUM (GENLIST f n) + f n``, - rpt strip_tac >> - `SUM (GENLIST f (SUC n)) = SUM (SNOC (f n) (GENLIST f n))` by metis_tac[GENLIST] >> - `_ = SUM ((GENLIST f n) ++ [f n])` by metis_tac[SNOC_APPEND] >> - `_ = SUM (GENLIST f n) + SUM [f n]` by metis_tac[SUM_APPEND] >> - rw[SUM]); - -(* Theorem: SUM (GENLIST a n) + SUM (GENLIST b n) = SUM (GENLIST (\k. a k + b k) n) *) -(* Proof: by induction on n. - Base case: !a b. SUM (GENLIST a 0) + SUM (GENLIST b 0) = SUM (GENLIST (\k. a k + b k) 0) - Since GENLIST f 0 = [] by GENLIST - and SUM [] = 0 by SUM_NIL - This is just 0 + 0 = 0, true by arithmetic. - Step case: !a b. SUM (GENLIST a n) + SUM (GENLIST b n) = - SUM (GENLIST (\k. a k + b k) n) ==> - !a b. SUM (GENLIST a (SUC n)) + SUM (GENLIST b (SUC n)) = - SUM (GENLIST (\k. a k + b k) (SUC n)) - SUM (GENLIST a (SUC n)) + SUM (GENLIST b (SUC n) - = (SUM (GENLIST a n) + a n) + (SUM (GENLIST b n) + b n) by SUM_DECOMPOSE_LAST - = SUM (GENLIST a n) + SUM (GENLIST b n) + (a n + b n) by arithmetic - = SUM (GENLIST (\k. a k + b k) n) + (a n + b n) by induction hypothesis - = SUM (GENLIST (\k. a k + b k) (SUC n)) by SUM_DECOMPOSE_LAST -*) -val SUM_ADD_GENLIST = store_thm( - "SUM_ADD_GENLIST", - ``!a b n. SUM (GENLIST a n) + SUM (GENLIST b n) = SUM (GENLIST (\k. a k + b k) n)``, - Induct_on `n` >- - rw[] >> - rw[SUM_DECOMPOSE_LAST]); - -(* Theorem: SUM (GENLIST a n ++ GENLIST b n) = SUM (GENLIST (\k. a k + b k) n) *) -(* Proof: - SUM (GENLIST a n ++ GENLIST b n) - = SUM (GENLIST a n) + SUM (GENLIST b n) by SUM_APPEND - = SUM (GENLIST (\k. a k + b k) n) by SUM_ADD_GENLIST -*) -val SUM_GENLIST_APPEND = store_thm( - "SUM_GENLIST_APPEND", - ``!a b n. SUM (GENLIST a n ++ GENLIST b n) = SUM (GENLIST (\k. a k + b k) n)``, - metis_tac[SUM_APPEND, SUM_ADD_GENLIST]); - -(* Theorem: 0 < n ==> SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n *) -(* Proof: - SUM (GENLIST f (SUC n)) - = SUM (GENLIST f n) + f n by SUM_DECOMPOSE_LAST - = SUM (GENLIST f (SUC m)) + f n by n = SUC m, 0 < n - = f 0 + SUM (GENLIST (f o SUC) m) + f n by SUM_DECOMPOSE_FIRST - = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n by PRE_SUC_EQ -*) -val SUM_DECOMPOSE_FIRST_LAST = store_thm( - "SUM_DECOMPOSE_FIRST_LAST", - ``!f n. 0 < n ==> (SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n)``, - metis_tac[SUM_DECOMPOSE_LAST, SUM_DECOMPOSE_FIRST, SUC_EXISTS, PRE_SUC_EQ]); - -(* Theorem: (SUM l) MOD n = (SUM (MAP (\x. x MOD n) l)) MOD n *) -(* Proof: by list induction. - Base case: SUM [] MOD n = SUM (MAP (\x. x MOD n) []) MOD n - true by SUM [] = 0, MAP f [] = 0, and 0 MOD n = 0. - Step case: SUM l MOD n = SUM (MAP (\x. x MOD n) l) MOD n ==> - !h. SUM (h::l) MOD n = SUM (MAP (\x. x MOD n) (h::l)) MOD n - SUM (h::l) MOD n - = (h + SUM l) MOD n by SUM - = (h MOD n + (SUM l) MOD n) MOD n by MOD_PLUS - = (h MOD n + SUM (MAP (\x. x MOD n) l) MOD n) MOD n by induction hypothesis - = ((h MOD n) MOD n + SUM (MAP (\x. x MOD n) l) MOD n) MOD n by MOD_MOD - = ((h MOD n + SUM (MAP (\x. x MOD n) l)) MOD n) MOD n by MOD_PLUS - = (h MOD n + SUM (MAP (\x. x MOD n) l)) MOD n by MOD_MOD - = (SUM (h MOD n ::(MAP (\x. x MOD n) l))) MOD n by SUM - = (SUM (MAP (\x. x MOD n) (h::l))) MOD n by MAP -*) -val SUM_MOD = store_thm( - "SUM_MOD", - ``!n. 0 < n ==> !l. (SUM l) MOD n = (SUM (MAP (\x. x MOD n) l)) MOD n``, - rpt strip_tac >> - Induct_on `l` >- - rw[] >> - rpt strip_tac >> - `SUM (h::l) MOD n = (h MOD n + (SUM l) MOD n) MOD n` by rw_tac std_ss[SUM, MOD_PLUS] >> - `_ = ((h MOD n) MOD n + SUM (MAP (\x. x MOD n) l) MOD n) MOD n` by rw_tac std_ss[MOD_MOD] >> - rw[MOD_PLUS]); - -(* Theorem: SUM l = 0 <=> l = EVERY (\x. x = 0) l *) -(* Proof: by induction on l. - Base case: (SUM [] = 0) <=> EVERY (\x. x = 0) [] - true by SUM [] = 0 and GENLIST f 0 = []. - Step case: (SUM l = 0) <=> EVERY (\x. x = 0) l ==> - !h. (SUM (h::l) = 0) <=> EVERY (\x. x = 0) (h::l) - SUM (h::l) = 0 - <=> h + SUM l = 0 by SUM - <=> h = 0 /\ SUM l = 0 by ADD_EQ_0 - <=> h = 0 /\ EVERY (\x. x = 0) l by induction hypothesis - <=> EVERY (\x. x = 0) (h::l) by EVERY_DEF -*) -val SUM_EQ_0 = store_thm( - "SUM_EQ_0", - ``!l. (SUM l = 0) <=> EVERY (\x. x = 0) l``, - Induct >> - rw[]); - -(* Theorem: SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = - SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n *) -(* Proof: - SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n - = SUM (MAP (\x. x MOD n) (GENLIST ((\k. f k) o SUC) (PRE n))) MOD n by SUM_MOD - = SUM (GENLIST ((\x. x MOD n) o ((\k. f k) o SUC)) (PRE n)) MOD n by MAP_GENLIST - = SUM (GENLIST ((\x. x MOD n) o (\k. f k) o SUC) (PRE n)) MOD n by composition associative - = SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n by composition -*) -val SUM_GENLIST_MOD = store_thm( - "SUM_GENLIST_MOD", - ``!n. 0 < n ==> !f. SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n``, - rpt strip_tac >> - `SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = - SUM (MAP (\x. x MOD n) (GENLIST ((\k. f k) o SUC) (PRE n))) MOD n` by metis_tac[SUM_MOD] >> - rw_tac std_ss[MAP_GENLIST, combinTheory.o_ASSOC, combinTheory.o_ABS_L]); - -(* Theorem: SUM (GENLIST (\j. x) n) = n * x *) -(* Proof: - By induction on n. - Base case: !x. SUM (GENLIST (\j. x) 0) = 0 * x - SUM (GENLIST (\j. x) 0) - = SUM [] by GENLIST - = 0 by SUM - = 0 * x by MULT - Step case: !x. SUM (GENLIST (\j. x) n) = n * x ==> - !x. SUM (GENLIST (\j. x) (SUC n)) = SUC n * x - SUM (GENLIST (\j. x) (SUC n)) - = SUM (SNOC x (GENLIST (\j. x) n)) by GENLIST - = SUM (GENLIST (\j. x) n) + x by SUM_SNOC - = n * x + x by induction hypothesis - = SUC n * x by MULT -*) -val SUM_CONSTANT = store_thm( - "SUM_CONSTANT", - ``!n x. SUM (GENLIST (\j. x) n) = n * x``, - Induct >- - rw[] >> - rw_tac std_ss[GENLIST, SUM_SNOC, MULT]); - -(* Theorem: SUM (GENLIST (K m) n) = m * n *) -(* Proof: - By induction on n. - Base: SUM (GENLIST (K m) 0) = m * 0 - SUM (GENLIST (K m) 0) - = SUM [] by GENLIST - = 0 by SUM - = m * 0 by MULT_0 - Step: SUM (GENLIST (K m) n) = m * n ==> SUM (GENLIST (K m) (SUC n)) = m * SUC n - SUM (GENLIST (K m) (SUC n)) - = SUM (SNOC m (GENLIST (K m) n)) by GENLIST - = SUM (GENLIST (K m) n) + m by SUM_SNOC - = m * n + m by induction hypothesis - = m + m * n by ADD_COMM - = m * SUC n by MULT_SUC -*) -val SUM_GENLIST_K = store_thm( - "SUM_GENLIST_K", - ``!m n. SUM (GENLIST (K m) n) = m * n``, - strip_tac >> - Induct >- - rw[] >> - rw[GENLIST, SUM_SNOC, MULT_SUC]); - -(* Theorem: (LENGTH l1 = LENGTH l2) /\ (!k. k <= LENGTH l1 ==> EL k l1 <= EL k l2) ==> SUM l1 <= SUM l2 *) -(* Proof: - By induction on l1. - Base: LENGTH [] = LENGTH l2 ==> SUM [] <= SUM l2 - Note l2 = [] by LENGTH_EQ_0 - so SUM [] = SUM [] - or SUM [] <= SUM l2 by EQ_LESS_EQ - Step: !l2. (LENGTH l1 = LENGTH l2) /\ ... ==> SUM l1 <= SUM l2 ==> - (LENGTH (h::l1) = LENGTH l2) /\ ... ==> SUM h::l1 <= SUM l2 - Note l2 <> [] by LENGTH_EQ_0 - so ?h1 t2. l2 = h1::t1 by list_CASES - and LENGTH l1 = LENGTH t1 by LENGTH - SUM (h::l1) - = h + SUM l1 by SUM_CONS - <= h1 + SUM t1 by EL_ALL_PROPERTY, induction hypothesis - = SUM l2 by SUM_CONS -*) -val SUM_LE = store_thm( - "SUM_LE", - ``!l1 l2. (LENGTH l1 = LENGTH l2) /\ (!k. k < LENGTH l1 ==> EL k l1 <= EL k l2) ==> - SUM l1 <= SUM l2``, - Induct >- - metis_tac[LENGTH_EQ_0, EQ_LESS_EQ] >> - rpt strip_tac >> - `?h1 t1. l2 = h1::t1` by metis_tac[LENGTH_EQ_0, list_CASES] >> - `LENGTH l1 = LENGTH t1` by metis_tac[LENGTH, SUC_EQ] >> - `SUM (h::l1) = h + SUM l1` by rw[SUM_CONS] >> - `SUM l2 = h1 + SUM t1` by rw[SUM_CONS] >> - `(h <= h1) /\ SUM l1 <= SUM t1` by metis_tac[EL_ALL_PROPERTY] >> - decide_tac); - -(* Theorem: MEM x l ==> x <= SUM l *) -(* Proof: - By induction on l. - Base: !x. MEM x [] ==> x <= SUM [] - True since MEM x [] = F by MEM - Step: !x. MEM x l ==> x <= SUM l ==> !h x. MEM x (h::l) ==> x <= SUM (h::l) - If x = h, - Then h <= h + SUM l = SUM (h::l) by SUM - If x <> h, - Then MEM x l by MEM - ==> x <= SUM l by induction hypothesis - or x <= h + SUM l = SUM (h::l) by SUM -*) -val SUM_LE_MEM = store_thm( - "SUM_LE_MEM", - ``!l x. MEM x l ==> x <= SUM l``, - Induct >- - rw[] >> - rw[] >- - decide_tac >> - `x <= SUM l` by rw[] >> - decide_tac); - -(* Theorem: n < LENGTH l ==> (EL n l) <= SUM l *) -(* Proof: by SUM_LE_MEM, MEM_EL *) -val SUM_LE_EL = store_thm( - "SUM_LE_EL", - ``!l n. n < LENGTH l ==> (EL n l) <= SUM l``, - metis_tac[SUM_LE_MEM, MEM_EL]); - -(* Theorem: m < n /\ n < LENGTH l ==> (EL m l) + (EL n l) <= SUM l *) -(* Proof: - By induction on l. - Base: !m n. m < n /\ n < LENGTH [] ==> EL m [] + EL n [] <= SUM [] - True since n < LENGTH [] = F by LENGTH - Step: !m n. m < LENGTH l /\ n < LENGTH l ==> EL m l + EL n l <= SUM l ==> - !h m n. m < LENGTH (h::l) /\ n < LENGTH (h::l) ==> EL m (h::l) + EL n (h::l) <= SUM (h::l) - Note 0 < n, or n <> 0 by m < n - so ?k. n = SUC k by num_CASES - and k < LENGTH l by SUC k < SUC (LENGTH l) - and EL n (h::l) = EL k l by EL_restricted - If m = 0, - Then EL m (h::l) = h by EL_restricted - and EL k l <= SUM l by SUM_LE_EL - Thus EL m (h::l) + EL n (h::l) - = h + SUM l - = SUM (h::l) by SUM - If m <> 0, - Then ?j. m = SUC j by num_CASES - and j < k by SUC j < SUC k - and EL m (h::l) = EL j l by EL_restricted - Thus EL m (h::l) + EL n (h::l) - = EL j l + EL k l by above - <= SUM l by induction hypothesis - <= h + SUM l by arithmetic - = SUM (h::l) by SUM -*) -val SUM_LE_SUM_EL = store_thm( - "SUM_LE_SUM_EL", - ``!l m n. m < n /\ n < LENGTH l ==> (EL m l) + (EL n l) <= SUM l``, - Induct >- - rw[] >> - rw[] >> - `n <> 0` by decide_tac >> - `?k. n = SUC k` by metis_tac[num_CASES] >> - `k < LENGTH l` by decide_tac >> - `EL n (h::l) = EL k l` by rw[] >> - Cases_on `m = 0` >| [ - `EL m (h::l) = h` by rw[] >> - `EL k l <= SUM l` by rw[SUM_LE_EL] >> - decide_tac, - `?j. m = SUC j` by metis_tac[num_CASES] >> - `j < k` by decide_tac >> - `EL m (h::l) = EL j l` by rw[] >> - `EL j l + EL k l <= SUM l` by rw[] >> - decide_tac - ]); - -(* Theorem: SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1) *) -(* Proof: - The computation is: - n + (n * 2) + (n * 4) + ... + (n * (2 ** (m - 1))) - = n * (1 + 2 + 4 + ... + 2 ** (m - 1)) - = n * (2 ** m - 1) - - By induction on m. - Base: SUM (GENLIST (\j. n * 2 ** j) 0) = n * (2 ** 0 - 1) - LHS = SUM (GENLIST (\j. n * 2 ** j) 0) - = SUM [] by GENLIST_0 - = 0 by PROD - RHS = n * (1 - 1) by EXP_0 - = n * 0 = 0 = LHS by MULT_0 - Step: SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1) ==> - SUM (GENLIST (\j. n * 2 ** j) (SUC m)) = n * (2 ** SUC m - 1) - SUM (GENLIST (\j. n * 2 ** j) (SUC m)) - = SUM (SNOC (n * 2 ** m) (GENLIST (\j. n * 2 ** j) m)) by GENLIST - = SUM (GENLIST (\j. n * 2 ** j) m) + (n * 2 ** m) by SUM_SNOC - = n * (2 ** m - 1) + n * 2 ** m by induction hypothesis - = n * (2 ** m - 1 + 2 ** m) by LEFT_ADD_DISTRIB - = n * (2 * 2 ** m - 1) by arithmetic - = n * (2 ** SUC m - 1) by EXP -*) -val SUM_DOUBLING_LIST = store_thm( - "SUM_DOUBLING_LIST", - ``!m n. SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1)``, - rpt strip_tac >> - Induct_on `m` >- - rw[] >> - qabbrev_tac `f = \j. n * 2 ** j` >> - `SUM (GENLIST f (SUC m)) = SUM (SNOC (n * 2 ** m) (GENLIST f m))` by rw[GENLIST, Abbr`f`] >> - `_ = SUM (GENLIST f m) + (n * 2 ** m)` by rw[SUM_SNOC] >> - `_ = n * (2 ** m - 1) + n * 2 ** m` by rw[] >> - `_ = n * (2 ** m - 1 + 2 ** m)` by rw[LEFT_ADD_DISTRIB] >> - rw[EXP]); - - -(* Idea: key theorem, almost like pigeonhole principle. *) - -(* List equivalent sum theorems. This is an example of digging out theorems. *) - -(* Theorem: EVERY (\x. 0 < x) ls ==> LENGTH ls <= SUM ls *) -(* Proof: - Let P = (\x. 0 < x). - By induction on list ls. - Base: EVERY P [] ==> LENGTH [] <= SUM [] - Note EVERY P [] = T by EVERY_DEF - and LENGTH [] = 0 by LENGTH - and SUM [] = 0 by SUM - Hence true. - Step: EVERY P ls ==> LENGTH ls <= SUM ls ==> - !h. EVERY P (h::ls) ==> LENGTH (h::ls) <= SUM (h::ls) - Note 0 < h /\ EVERY P ls by EVERY_DEF - LENGTH (h::ls) - = 1 + LENGTH ls by LENGTH - <= 1 + SUM ls by induction hypothesis - <= h + SUM ls by 0 < h - = SUM (h::ls) by SUM -*) -Theorem list_length_le_sum: - !ls. EVERY (\x. 0 < x) ls ==> LENGTH ls <= SUM ls -Proof - Induct >- - rw[] >> - rw[] >> - `1 <= h` by decide_tac >> - fs[] -QED - -(* Theorem: EVERY (\x. 0 < x) ls /\ LENGTH ls = SUM ls ==> EVERY (\x. x = 1) ls *) -(* Proof: - Let P = (\x. 0 < x), Q = (\x. x = 1). - By induction on list ls. - Base: EVERY P [] /\ LENGTH [] = SUM [] ==> EVERY Q [] - Note EVERY Q [] = T by EVERY_DEF - Hence true. - Step: EVERY P ls /\ LENGTH ls = SUM ls ==> EVERY Q ls ==> - !h. EVERY P (h::ls) /\ LENGTH (h::ls) = SUM (h::ls) ==> EVERY Q (h::ls) - Note 0 < h /\ EVERY P ls by EVERY_DEF - LHS = LENGTH (h::ls) - = 1 + LENGTH ls by LENGTH - <= 1 + SUM ls by list_length_le_sum - RHS = SUM (h::ls) - = h + SUM ls by SUM - Thus h + SUM ls <= 1 + SUM ls - or h <= 1 by arithmetic - giving h = 1 by 0 < h - Thus LENGTH ls = SUM ls by arithmetic - and EVERY Q ls by induction hypothesis - or EVERY Q (h::ls) by EVERY_DEF, h = 1 -*) -Theorem list_length_eq_sum: - !ls. EVERY (\x. 0 < x) ls /\ LENGTH ls = SUM ls ==> EVERY (\x. x = 1) ls -Proof - Induct >- - rw[] >> - rpt strip_tac >> - fs[] >> - `LENGTH ls <= SUM ls` by rw[list_length_le_sum] >> - `h + LENGTH ls <= SUC (LENGTH ls)` by fs[] >> - `h = 1` by decide_tac >> - `SUM ls = LENGTH ls` by fs[] >> - simp[] -QED - -(* ------------------------------------------------------------------------- *) -(* Maximum of a List *) -(* ------------------------------------------------------------------------- *) - -(* Define MAX of a list *) -val MAX_LIST_def = Define` - (MAX_LIST [] = 0) /\ - (MAX_LIST (h::t) = MAX h (MAX_LIST t)) -`; - -(* export simple recursive definition *) -(* val _ = export_rewrites["MAX_LIST_def"]; *) - -(* Theorem: MAX_LIST [] = 0 *) -(* Proof: by MAX_LIST_def *) -val MAX_LIST_NIL = save_thm("MAX_LIST_NIL", MAX_LIST_def |> CONJUNCT1); -(* val MAX_LIST_NIL = |- MAX_LIST [] = 0: thm *) - -(* Theorem: MAX_LIST (h::t) = MAX h (MAX_LIST t) *) -(* Proof: by MAX_LIST_def *) -val MAX_LIST_CONS = save_thm("MAX_LIST_CONS", MAX_LIST_def |> CONJUNCT2); -(* val MAX_LIST_CONS = |- !h t. MAX_LIST (h::t) = MAX h (MAX_LIST t): thm *) - -(* export simple results *) -val _ = export_rewrites["MAX_LIST_NIL", "MAX_LIST_CONS"]; - -(* Theorem: MAX_LIST [x] = x *) -(* Proof: - MAX_LIST [x] - = MAX x (MAX_LIST []) by MAX_LIST_CONS - = MAX x 0 by MAX_LIST_NIL - = x by MAX_0 -*) -val MAX_LIST_SING = store_thm( - "MAX_LIST_SING", - ``!x. MAX_LIST [x] = x``, - rw[]); - -(* Theorem: (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l *) -(* Proof: - By induction on l. - Base: (MAX_LIST [] = 0) <=> EVERY (\x. x = 0) [] - LHS: MAX_LIST [] = 0, true by MAX_LIST_NIL - RHS: EVERY (\x. x = 0) [], true by EVERY_DEF - Step: (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l ==> - !h. (MAX_LIST (h::l) = 0) <=> EVERY (\x. x = 0) (h::l) - MAX_LIST (h::l) = 0 - <=> MAX h (MAX_LIST l) = 0 by MAX_LIST_CONS - <=> (h = 0) /\ (MAX_LIST l = 0) by MAX_EQ_0 - <=> (h = 0) /\ EVERY (\x. x = 0) l by induction hypothesis - <=> EVERY (\x. x = 0) (h::l) by EVERY_DEF -*) -val MAX_LIST_EQ_0 = store_thm( - "MAX_LIST_EQ_0", - ``!l. (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l``, - Induct >> - rw[MAX_EQ_0]); - -(* Theorem: l <> [] ==> MEM (MAX_LIST l) l *) -(* Proof: - By induction on l. - Base: [] <> [] ==> MEM (MAX_LIST []) [] - Trivially true by [] <> [] = F. - Step: l <> [] ==> MEM (MAX_LIST l) l ==> - !h. h::l <> [] ==> MEM (MAX_LIST (h::l)) (h::l) - If l = [], - Note MAX_LIST [h] = h by MAX_LIST_SING - and MEM h [h] by MEM - Hence true. - If l <> [], - Let x = MAX_LIST (h::l) - = MAX h (MAX_LIST l) by MAX_LIST_CONS - ==> x = h \/ x = MAX_LIST l by MAX_CASES - If x = h, MEM x (h::l) by MEM - If x = MAX_LIST l, MEM x l by induction hypothesis -*) -val MAX_LIST_MEM = store_thm( - "MAX_LIST_MEM", - ``!l. l <> [] ==> MEM (MAX_LIST l) l``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `l = []` >- - rw[] >> - rw[] >> - metis_tac[MAX_CASES]); - -(* Theorem: MEM x l ==> x <= MAX_LIST l *) -(* Proof: - By induction on l. - Base: !x. MEM x [] ==> x <= MAX_LIST [] - Trivially true since MEM x [] = F by MEM - Step: !x. MEM x l ==> x <= MAX_LIST l ==> !h x. MEM x (h::l) ==> x <= MAX_LIST (h::l) - Note MEM x (h::l) means (x = h) \/ MEM x l by MEM - and MAX_LIST (h::l) = MAX h (MAX_LIST l) by MAX_LIST_CONS - If x = h, x <= MAX h (MAX_LIST l) by MAX_LE - If MEM x l, x <= MAX_LIST l by induction hypothesis - or x <= MAX h (MAX_LIST l) by MAX_LE, LESS_EQ_TRANS -*) -val MAX_LIST_PROPERTY = store_thm( - "MAX_LIST_PROPERTY", - ``!l x. MEM x l ==> x <= MAX_LIST l``, - Induct >- - rw[] >> - rw[MAX_LIST_CONS] >- - decide_tac >> - rw[]); - -(* Theorem: l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> y <= x) ==> (x = MAX_LIST l) *) -(* Proof: - Let m = MAX_LIST l. - Since MEM x l /\ x <= m by MAX_LIST_PROPERTY - and MEM m l ==> m <= x by MAX_LIST_MEM, implication, l <> [] - Hence x = m by EQ_LESS_EQ -*) -val MAX_LIST_TEST = store_thm( - "MAX_LIST_TEST", - ``!l. l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> y <= x) ==> (x = MAX_LIST l)``, - metis_tac[MAX_LIST_MEM, MAX_LIST_PROPERTY, EQ_LESS_EQ]); - -(* Theorem: MAX_LIST t <= MAX_LIST (h::t) *) -(* Proof: - Note MAX_LIST (h::t) = MAX h (MAX_LIST t) by MAX_LIST_def - and MAX_LIST t <= MAX h (MAX_LIST t) by MAX_IS_MAX - Thus MAX_LIST t <= MAX_LIST (h::t) -*) -val MAX_LIST_LE = store_thm( - "MAX_LIST_LE", - ``!h t. MAX_LIST t <= MAX_LIST (h::t)``, - rw_tac std_ss[MAX_LIST_def]); - -(* Theorem: (!x y. x <= y ==> f x <= f y) ==> - !ls. ls <> [] ==> (MAX_LIST (MAP f ls) = f (MAX_LIST ls)) *) -(* Proof: - By induction on ls. - Base: [] <> [] ==> MAX_LIST (MAP f []) = f (MAX_LIST []) - True by [] <> [] = F. - Step: ls <> [] ==> MAX_LIST (MAP f ls) = f (MAX_LIST ls) ==> - !h. h::ls <> [] ==> MAX_LIST (MAP f (h::ls)) = f (MAX_LIST (h::ls)) - If ls = [], - MAX_LIST (MAP f [h]) - = MAX_LIST [f h] by MAP - = f h by MAX_LIST_def - = f (MAX_LIST [h]) by MAX_LIST_def - If ls <> [], - MAX_LIST (MAP f (h::ls)) - = MAX_LIST (f h::MAP f ls) by MAP - = MAX (f h) MAX_LIST (MAP f ls) by MAX_LIST_def - = MAX (f h) (f (MAX_LIST ls)) by induction hypothesis - = f (MAX h (MAX_LIST ls)) by MAX_SWAP - = f (MAX_LIST (h::ls)) by MAX_LIST_def -*) -val MAX_LIST_MONO_MAP = store_thm( - "MAX_LIST_MONO_MAP", - ``!f. (!x y. x <= y ==> f x <= f y) ==> - !ls. ls <> [] ==> (MAX_LIST (MAP f ls) = f (MAX_LIST ls))``, - rpt strip_tac >> - Induct_on `ls` >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - rw[MAX_SWAP]); - -(* ------------------------------------------------------------------------- *) -(* Minimum of a List *) -(* ------------------------------------------------------------------------- *) - -(* Define MIN of a list *) -val MIN_LIST_def = Define` - MIN_LIST (h::t) = if t = [] then h else MIN h (MIN_LIST t) -`; - -(* Theorem: MIN_LIST [x] = x *) -(* Proof: by MIN_LIST_def *) -val MIN_LIST_SING = store_thm( - "MIN_LIST_SING", - ``!x. MIN_LIST [x] = x``, - rw[MIN_LIST_def]); - -(* Theorem: t <> [] ==> (MIN_LIST (h::t) = MIN h (MIN_LIST t)) *) -(* Proof: by MIN_LIST_def *) -val MIN_LIST_CONS = store_thm( - "MIN_LIST_CONS", - ``!h t. t <> [] ==> (MIN_LIST (h::t) = MIN h (MIN_LIST t))``, - rw[MIN_LIST_def]); - -(* export simple results *) -val _ = export_rewrites["MIN_LIST_SING", "MIN_LIST_CONS"]; - -(* Theorem: l <> [] ==> MEM (MIN_LIST l) l *) -(* Proof: - By induction on l. - Base: [] <> [] ==> MEM (MIN_LIST []) [] - Trivially true by [] <> [] = F. - Step: l <> [] ==> MEM (MIN_LIST l) l ==> - !h. h::l <> [] ==> MEM (MIN_LIST (h::l)) (h::l) - If l = [], - Note MIN_LIST [h] = h by MIN_LIST_SING - and MEM h [h] by MEM - Hence true. - If l <> [], - Let x = MIN_LIST (h::l) - = MIN h (MIN_LIST l) by MIN_LIST_CONS - ==> x = h \/ x = MIN_LIST l by MIN_CASES - If x = h, MEM x (h::l) by MEM - If x = MIN_LIST l, MEM x l by induction hypothesis -*) -val MIN_LIST_MEM = store_thm( - "MIN_LIST_MEM", - ``!l. l <> [] ==> MEM (MIN_LIST l) l``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `l = []` >- - rw[] >> - rw[] >> - metis_tac[MIN_CASES]); - -(* Theorem: l <> [] ==> !x. MEM x l ==> (MIN_LIST l) <= x *) -(* Proof: - By induction on l. - Base: [] <> [] ==> ... - Trivially true since [] <> [] = F - Step: l <> [] ==> !x. MEM x l ==> MIN_LIST l <= x ==> - !h. h::l <> [] ==> !x. MEM x (h::l) ==> MIN_LIST (h::l) <= x - Note MEM x (h::l) means (x = h) \/ MEM x l by MEM - If l = [], - MEM x [h] means x = h by MEM - and MIN_LIST [h] = h, hence true by MIN_LIST_SING - If l <> [], - MIN_LIST (h::l) = MIN h (MIN_LIST l) by MIN_LIST_CONS - If x = h, MIN h (MIN_LIST l) <= x by MIN_LE - If MEM x l, MIN_LIST l <= x by induction hypothesis - or MIN h (MIN_LIST l) <= x by MIN_LE, LESS_EQ_TRANS -*) -val MIN_LIST_PROPERTY = store_thm( - "MIN_LIST_PROPERTY", - ``!l. l <> [] ==> !x. MEM x l ==> (MIN_LIST l) <= x``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `l = []` >- - fs[MIN_LIST_SING, MEM] >> - fs[MIN_LIST_CONS, MEM]); - -(* Theorem: l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> x <= y) ==> (x = MIN_LIST l) *) -(* Proof: - Let m = MIN_LIST l. - Since MEM x l /\ m <= x by MIN_LIST_PROPERTY - and MEM m l ==> x <= m by MIN_LIST_MEM, implication, l <> [] - Hence x = m by EQ_LESS_EQ -*) -val MIN_LIST_TEST = store_thm( - "MIN_LIST_TEST", - ``!l. l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> x <= y) ==> (x = MIN_LIST l)``, - metis_tac[MIN_LIST_MEM, MIN_LIST_PROPERTY, EQ_LESS_EQ]); - -(* Theorem: l <> [] ==> MIN_LIST l <= MAX_LIST l *) -(* Proof: - Since MEM (MIN_LIST l) l by MIN_LIST_MEM - so MIN_LIST l <= MAX_LIST l by MAX_LIST_PROPERTY -*) -val MIN_LIST_LE_MAX_LIST = store_thm( - "MIN_LIST_LE_MAX_LIST", - ``!l. l <> [] ==> MIN_LIST l <= MAX_LIST l``, - rw[MIN_LIST_MEM, MAX_LIST_PROPERTY]); - -(* Theorem: t <> [] ==> MIN_LIST (h::t) <= MIN_LIST t *) -(* Proof: - Note MIN_LIST (h::t) = MIN h (MIN_LIST t) by MIN_LIST_def, t <> [] - and MIN h (MIN_LIST t) <= MIN_LIST t by MIN_IS_MIN - Thus MIN_LIST (h::t) <= MIN_LIST t -*) -val MIN_LIST_LE = store_thm( - "MIN_LIST_LE", - ``!h t. t <> [] ==> MIN_LIST (h::t) <= MIN_LIST t``, - rw_tac std_ss[MIN_LIST_def]); - -(* Theorem: (!x y. x <= y ==> f x <= f y) ==> - !ls. ls <> [] ==> (MIN_LIST (MAP f ls) = f (MIN_LIST ls)) *) -(* Proof: - By induction on ls. - Base: [] <> [] ==> MIN_LIST (MAP f []) = f (MIN_LIST []) - True by [] <> [] = F. - Step: ls <> [] ==> MIN_LIST (MAP f ls) = f (MIN_LIST ls) ==> - !h. h::ls <> [] ==> MIN_LIST (MAP f (h::ls)) = f (MIN_LIST (h::ls)) - If ls = [], - MIN_LIST (MAP f [h]) - = MIN_LIST [f h] by MAP - = f h by MIN_LIST_def - = f (MIN_LIST [h]) by MIN_LIST_def - If ls <> [], - MIN_LIST (MAP f (h::ls)) - = MIN_LIST (f h::MAP f ls) by MAP - = MIN (f h) MIN_LIST (MAP f ls) by MIN_LIST_def - = MIN (f h) (f (MIN_LIST ls)) by induction hypothesis - = f (MIN h (MIN_LIST ls)) by MIN_SWAP - = f (MIN_LIST (h::ls)) by MIN_LIST_def -*) -val MIN_LIST_MONO_MAP = store_thm( - "MIN_LIST_MONO_MAP", - ``!f. (!x y. x <= y ==> f x <= f y) ==> - !ls. ls <> [] ==> (MIN_LIST (MAP f ls) = f (MIN_LIST ls))``, - rpt strip_tac >> - Induct_on `ls` >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - rw[MIN_SWAP]); - -(* ------------------------------------------------------------------------- *) -(* List Nub and Set *) -(* ------------------------------------------------------------------------- *) - -(* Note: -> nub_def; -|- (nub [] = []) /\ !x l. nub (x::l) = if MEM x l then nub l else x::nub l -*) - -(* Theorem: nub [] = [] *) -(* Proof: by nub_def *) -val nub_nil = save_thm("nub_nil", nub_def |> CONJUNCT1); -(* val nub_nil = |- nub [] = []: thm *) - -(* Theorem: nub (x::l) = if MEM x l then nub l else x::nub l *) -(* Proof: by nub_def *) -val nub_cons = save_thm("nub_cons", nub_def |> CONJUNCT2); -(* val nub_cons = |- !x l. nub (x::l) = if MEM x l then nub l else x::nub l: thm *) - -(* Theorem: nub [x] = [x] *) -(* Proof: - nub [x] - = nub (x::[]) by notation - = x :: nub [] by nub_cons, MEM x [] = F - = x ::[] by nub_nil - = [x] by notation -*) -val nub_sing = store_thm( - "nub_sing", - ``!x. nub [x] = [x]``, - rw[nub_def]); - -(* Theorem: ALL_DISTINCT (nub l) *) -(* Proof: - By induction on l. - Base: ALL_DISTINCT (nub []) - ALL_DISTINCT (nub []) - <=> ALL_DISTINCT [] by nub_nil - <=> T by ALL_DISTINCT - Step: ALL_DISTINCT (nub l) ==> !h. ALL_DISTINCT (nub (h::l)) - If MEM h l, - Then nub (h::l) = nub l by nub_cons - Thus ALL_DISTINCT (nub l) by induction hypothesis - ==> ALL_DISTINCT (nub (h::l)) - If ~(MEM h l), - Then nub (h::l) = h:nub l by nub_cons - With ALL_DISTINCT (nub l) by induction hypothesis - ==> ALL_DISTINCT (h::nub l) by ALL_DISTINCT, ~(MEM h l) - or ALL_DISTINCT (nub (h::l)) -*) -val nub_all_distinct = store_thm( - "nub_all_distinct", - ``!l. ALL_DISTINCT (nub l)``, - Induct >- - rw[nub_nil] >> - rw[nub_cons]); - -(* Theorem: CARD (set l) = LENGTH (nub l) *) -(* Proof: - Note set (nub l) = set l by nub_set - and ALL_DISTINCT (nub l) by nub_all_distinct - CARD (set l) - = CARD (set (nub l)) by above - = LENGTH (nub l) by ALL_DISTINCT_CARD_LIST_TO_SET, ALL_DISTINCT (nub l) -*) -val CARD_LIST_TO_SET_EQ = store_thm( - "CARD_LIST_TO_SET_EQ", - ``!l. CARD (set l) = LENGTH (nub l)``, - rpt strip_tac >> - `set (nub l) = set l` by rw[nub_set] >> - `ALL_DISTINCT (nub l)` by rw[nub_all_distinct] >> - rw[GSYM ALL_DISTINCT_CARD_LIST_TO_SET]); - -(* Theorem: set [x] = {x} *) -(* Proof: - set [x] - = x INSERT set [] by LIST_TO_SET - = x INSERT {} by LIST_TO_SET - = {x} by INSERT_DEF -*) -val MONO_LIST_TO_SET = store_thm( - "MONO_LIST_TO_SET", - ``!x. set [x] = {x}``, - rw[]); - -(* Theorem: ALL_DISTINCT l /\ (set l = {x}) <=> (l = [x]) *) -(* Proof: - If part: ALL_DISTINCT l /\ set l = {x} ==> l = [x] - Note set l = {x} - ==> l <> [] /\ EVERY ($= x) l by LIST_TO_SET_EQ_SING - Let P = (S= x). - Note l <> [] ==> ?h t. l = h::t by list_CASES - so h = x /\ EVERY P t by EVERY_DEF - and ~(MEM h t) /\ ALL_DISTINCT t by ALL_DISTINCT - By contradiction, suppose l <> [x]. - Then t <> [] ==> ?u v. t = u::v by list_CASES - and MEM u t by MEM - but u = h by EVERY_DEF - ==> MEM h t, which contradicts ~(MEM h t). - - Only-if part: l = [x] ==> ALL_DISTINCT l /\ set l = {x} - Note ALL_DISTINCT [x] = T by ALL_DISTINCT_SING - and set [x] = {x} by MONO_LIST_TO_SET -*) -val DISTINCT_LIST_TO_SET_EQ_SING = store_thm( - "DISTINCT_LIST_TO_SET_EQ_SING", - ``!l x. ALL_DISTINCT l /\ (set l = {x}) <=> (l = [x])``, - rw[EQ_IMP_THM] >> - qabbrev_tac `P = ($= x)` >> - `!y. P y ==> (y = x)` by rw[Abbr`P`] >> - `l <> [] /\ EVERY P l` by metis_tac[LIST_TO_SET_EQ_SING, Abbr`P`] >> - `?h t. l = h::t` by metis_tac[list_CASES] >> - `(h = x) /\ (EVERY P t)` by metis_tac[EVERY_DEF] >> - `~(MEM h t) /\ ALL_DISTINCT t` by metis_tac[ALL_DISTINCT] >> - spose_not_then strip_assume_tac >> - `t <> []` by rw[] >> - `?u v. t = u::v` by metis_tac[list_CASES] >> - `MEM u t` by rw[] >> - metis_tac[EVERY_DEF]); - -(* Theorem: ~(MEM h l1) /\ (set (h::l1) = set l2) ==> - ?p1 p2. ~(MEM h p1) /\ ~(MEM h p2) /\ (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2)) *) -(* Proof: - Note MEM h (h::l1) by MEM - or h IN set (h::l1) by notation - so h IN set l2 by given - or h IN set (nub l2) by nub_set - so MEM h (nub l2) by notation - or ?p1 p2. nub l2 = p1 ++ [h] ++ h2 - and ~(MEM h p1) /\ ~(MEM h p2) by MEM_SPLIT_APPEND_distinct - Remaining goal: set l1 = set (p1 ++ p2) - - Step 1: show set l1 SUBSET set (p1 ++ p2) - Let x IN set l1. - Then MEM x l1 ==> MEM x (h::l1) by MEM - so x IN set (h::l1) - or x IN set l2 by given - or x IN set (nub l2) by nub_set - or MEM x (nub l2) by notation - But h <> x since MEM x l1 but ~MEM h l1 - so MEM x (p1 ++ p2) by MEM, MEM_APPEND - or x IN set (p1 ++ p2) by notation - Thus l1 SUBSET set (p1 ++ p2) by SUBSET_DEF - - Step 2: show set (p1 ++ p2) SUBSET set l1 - Let x IN set (p1 ++ p2) - or MEM x (p1 ++ p2) by notation - so MEM x (nub l2) by MEM, MEM_APPEND - or x IN set (nub l2) by notation - ==> x IN set l2 by nub_set - or x IN set (h::l1) by given - or MEM x (h::l1) by notation - But x <> h by MEM_APPEND, MEM x (p1 ++ p2) but ~(MEM h p1) /\ ~(MEM h p2) - ==> MEM x l1 by MEM - or x IN set l1 by notation - Thus set (p1 ++ p2) SUBSET set l1 by SUBSET_DEF - - Thus set l1 = set (p1 ++ p2) by SUBSET_ANTISYM -*) -val LIST_TO_SET_REDUCTION = store_thm( - "LIST_TO_SET_REDUCTION", - ``!l1 l2 h. ~(MEM h l1) /\ (set (h::l1) = set l2) ==> - ?p1 p2. ~(MEM h p1) /\ ~(MEM h p2) /\ (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2))``, - rpt strip_tac >> - `MEM h (nub l2)` by metis_tac[MEM, nub_set] >> - qabbrev_tac `l = nub l2` >> - `?n. n < LENGTH l /\ (h = EL n l)` by rw[GSYM MEM_EL] >> - `ALL_DISTINCT l` by rw[nub_all_distinct, Abbr`l`] >> - `?p1 p2. (l = p1 ++ [h] ++ p2) /\ ~MEM h p1 /\ ~MEM h p2` by rw[GSYM MEM_SPLIT_APPEND_distinct] >> - qexists_tac `p1` >> - qexists_tac `p2` >> - rpt strip_tac >- - rw[] >> - `set l1 SUBSET set (p1 ++ p2) /\ set (p1 ++ p2) SUBSET set l1` suffices_by metis_tac[SUBSET_ANTISYM] >> - rewrite_tac[SUBSET_DEF] >> - rpt strip_tac >- - metis_tac[MEM_APPEND, MEM, nub_set] >> - metis_tac[MEM_APPEND, MEM, nub_set]); - -(* ------------------------------------------------------------------------- *) -(* List Padding *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: PAD_LEFT c n [] = GENLIST (K c) n *) -(* Proof: by PAD_LEFT *) -val PAD_LEFT_NIL = store_thm( - "PAD_LEFT_NIL", - ``!n c. PAD_LEFT c n [] = GENLIST (K c) n``, - rw[PAD_LEFT]); - -(* Theorem: PAD_RIGHT c n [] = GENLIST (K c) n *) -(* Proof: by PAD_RIGHT *) -val PAD_RIGHT_NIL = store_thm( - "PAD_RIGHT_NIL", - ``!n c. PAD_RIGHT c n [] = GENLIST (K c) n``, - rw[PAD_RIGHT]); - -(* Theorem: LENGTH (PAD_LEFT c n s) = MAX n (LENGTH s) *) -(* Proof: - LENGTH (PAD_LEFT c n s) - = LENGTH (GENLIST (K c) (n - LENGTH s) ++ s) by PAD_LEFT - = LENGTH (GENLIST (K c) (n - LENGTH s)) + LENGTH s by LENGTH_APPEND - = n - LENGTH s + LENGTH s by LENGTH_GENLIST - = MAX n (LENGTH s) by MAX_DEF -*) -val PAD_LEFT_LENGTH = store_thm( - "PAD_LEFT_LENGTH", - ``!n c s. LENGTH (PAD_LEFT c n s) = MAX n (LENGTH s)``, - rw[PAD_LEFT, MAX_DEF]); - -(* Theorem: LENGTH (PAD_RIGHT c n s) = MAX n (LENGTH s) *) -(* Proof: - LENGTH (PAD_LEFT c n s) - = LENGTH (s ++ GENLIST (K c) (n - LENGTH s)) by PAD_RIGHT - = LENGTH s + LENGTH (GENLIST (K c) (n - LENGTH s)) by LENGTH_APPEND - = LENGTH s + (n - LENGTH s) by LENGTH_GENLIST - = MAX n (LENGTH s) by MAX_DEF -*) -val PAD_RIGHT_LENGTH = store_thm( - "PAD_RIGHT_LENGTH", - ``!n c s. LENGTH (PAD_RIGHT c n s) = MAX n (LENGTH s)``, - rw[PAD_RIGHT, MAX_DEF]); - -(* Theorem: n <= LENGTH l ==> (PAD_LEFT c n l = l) *) -(* Proof: - Note n - LENGTH l = 0 by n <= LENGTH l - PAD_LEFT c (LENGTH l) l - = GENLIST (K c) 0 ++ l by PAD_LEFT - = [] ++ l by GENLIST - = l by APPEND -*) -val PAD_LEFT_ID = store_thm( - "PAD_LEFT_ID", - ``!l c n. n <= LENGTH l ==> (PAD_LEFT c n l = l)``, - rpt strip_tac >> - `n - LENGTH l = 0` by decide_tac >> - rw[PAD_LEFT]); - -(* Theorem: n <= LENGTH l ==> (PAD_RIGHT c n l = l) *) -(* Proof: - Note n - LENGTH l = 0 by n <= LENGTH l - PAD_RIGHT c (LENGTH l) l - = ll ++ GENLIST (K c) 0 by PAD_RIGHT - = [] ++ l by GENLIST - = l by APPEND_NIL -*) -val PAD_RIGHT_ID = store_thm( - "PAD_RIGHT_ID", - ``!l c n. n <= LENGTH l ==> (PAD_RIGHT c n l = l)``, - rpt strip_tac >> - `n - LENGTH l = 0` by decide_tac >> - rw[PAD_RIGHT]); - -(* Theorem: PAD_LEFT c 0 l = l *) -(* Proof: by PAD_LEFT_ID *) -val PAD_LEFT_0 = store_thm( - "PAD_LEFT_0", - ``!l c. PAD_LEFT c 0 l = l``, - rw_tac std_ss[PAD_LEFT_ID]); - -(* Theorem: PAD_RIGHT c 0 l = l *) -(* Proof: by PAD_RIGHT_ID *) -val PAD_RIGHT_0 = store_thm( - "PAD_RIGHT_0", - ``!l c. PAD_RIGHT c 0 l = l``, - rw_tac std_ss[PAD_RIGHT_ID]); - -(* Theorem: LENGTH l <= n ==> !c. PAD_LEFT c (SUC n) l = c:: PAD_LEFT c n l *) -(* Proof: - PAD_LEFT c (SUC n) l - = GENLIST (K c) (SUC n - LENGTH l) ++ l by PAD_LEFT - = GENLIST (K c) (SUC (n - LENGTH l)) ++ l by LENGTH l <= n - = SNOC c (GENLIST (K c) (n - LENGTH l)) ++ l by GENLIST - = (GENLIST (K c) (n - LENGTH l)) ++ [c] ++ l by SNOC_APPEND - = [c] ++ (GENLIST (K c) (n - LENGTH l)) ++ l by GENLIST_K_APPEND_K - = [c] ++ ((GENLIST (K c) (n - LENGTH l)) ++ l) by APPEND_ASSOC - = [c] ++ PAD_LEFT c n l by PAD_LEFT - = c :: PAD_LEFT c n l by CONS_APPEND -*) -val PAD_LEFT_CONS = store_thm( - "PAD_LEFT_CONS", - ``!l n. LENGTH l <= n ==> !c. PAD_LEFT c (SUC n) l = c:: PAD_LEFT c n l``, - rpt strip_tac >> - qabbrev_tac `m = LENGTH l` >> - `SUC n - m = SUC (n - m)` by decide_tac >> - `PAD_LEFT c (SUC n) l = GENLIST (K c) (SUC n - m) ++ l` by rw[PAD_LEFT, Abbr`m`] >> - `_ = SNOC c (GENLIST (K c) (n - m)) ++ l` by rw[GENLIST] >> - `_ = (GENLIST (K c) (n - m)) ++ [c] ++ l` by rw[SNOC_APPEND] >> - `_ = [c] ++ (GENLIST (K c) (n - m)) ++ l` by rw[GENLIST_K_APPEND_K] >> - `_ = [c] ++ ((GENLIST (K c) (n - m)) ++ l)` by rw[APPEND_ASSOC] >> - `_ = [c] ++ PAD_LEFT c n l` by rw[PAD_LEFT] >> - `_ = c :: PAD_LEFT c n l` by rw[] >> - rw[]); - -(* Theorem: LENGTH l <= n ==> !c. PAD_RIGHT c (SUC n) l = SNOC c (PAD_RIGHT c n l) *) -(* Proof: - PAD_RIGHT c (SUC n) l - = l ++ GENLIST (K c) (SUC n - LENGTH l) by PAD_RIGHT - = l ++ GENLIST (K c) (SUC (n - LENGTH l)) by LENGTH l <= n - = l ++ SNOC c (GENLIST (K c) (n - LENGTH l)) by GENLIST - = SNOC c (l ++ (GENLIST (K c) (n - LENGTH l))) by APPEND_SNOC - = SNOC c (PAD_RIGHT c n l) by PAD_RIGHT -*) -val PAD_RIGHT_SNOC = store_thm( - "PAD_RIGHT_SNOC", - ``!l n. LENGTH l <= n ==> !c. PAD_RIGHT c (SUC n) l = SNOC c (PAD_RIGHT c n l)``, - rpt strip_tac >> - qabbrev_tac `m = LENGTH l` >> - `SUC n - m = SUC (n - m)` by decide_tac >> - rw[PAD_RIGHT, GENLIST, APPEND_SNOC]); - -(* Theorem: h :: PAD_RIGHT c n t = PAD_RIGHT c (SUC n) (h::t) *) -(* Proof: - h :: PAD_RIGHT c n t - = h :: (t ++ GENLIST (K c) (n - LENGTH t)) by PAD_RIGHT - = (h::t) ++ GENLIST (K c) (n - LENGTH t) by APPEND - = (h::t) ++ GENLIST (K c) (SUC n - LENGTH (h::t)) by LENGTH - = PAD_RIGHT c (SUC n) (h::t) by PAD_RIGHT -*) -val PAD_RIGHT_CONS = store_thm( - "PAD_RIGHT_CONS", - ``!h t c n. h :: PAD_RIGHT c n t = PAD_RIGHT c (SUC n) (h::t)``, - rw[PAD_RIGHT]); - -(* Theorem: l <> [] ==> (LAST (PAD_LEFT c n l) = LAST l) *) -(* Proof: - Note ?h t. l = h::t by list_CASES - LAST (PAD_LEFT c n l) - = LAST (GENLIST (K c) (n - LENGTH (h::t)) ++ (h::t)) by PAD_LEFT - = LAST (h::t) by LAST_APPEND_CONS - = LAST l by notation -*) -val PAD_LEFT_LAST = store_thm( - "PAD_LEFT_LAST", - ``!l c n. l <> [] ==> (LAST (PAD_LEFT c n l) = LAST l)``, - rpt strip_tac >> - `?h t. l = h::t` by metis_tac[list_CASES] >> - rw[PAD_LEFT, LAST_APPEND_CONS]); - -(* Theorem: (PAD_LEFT c n l = []) <=> ((l = []) /\ (n = 0)) *) -(* Proof: - PAD_LEFT c n l = [] - <=> GENLIST (K c) (n - LENGTH l) ++ l = [] by PAD_LEFT - <=> GENLIST (K c) (n - LENGTH l) = [] /\ l = [] by APPEND_eq_NIL - <=> GENLIST (K c) n = [] /\ l = [] by LENGTH l = 0 - <=> n = 0 /\ l = [] by GENLIST_EQ_NIL -*) -val PAD_LEFT_EQ_NIL = store_thm( - "PAD_LEFT_EQ_NIL", - ``!l c n. (PAD_LEFT c n l = []) <=> ((l = []) /\ (n = 0))``, - rw[PAD_LEFT, EQ_IMP_THM] >> - fs[GENLIST_EQ_NIL]); - -(* Theorem: (PAD_RIGHT c n l = []) <=> ((l = []) /\ (n = 0)) *) -(* Proof: - PAD_RIGHT c n l = [] - <=> l ++ GENLIST (K c) (n - LENGTH l) = [] by PAD_RIGHT - <=> l = [] /\ GENLIST (K c) (n - LENGTH l) = [] by APPEND_eq_NIL - <=> l = [] /\ GENLIST (K c) n = [] by LENGTH l = 0 - <=> l = [] /\ n = 0 by GENLIST_EQ_NIL -*) -val PAD_RIGHT_EQ_NIL = store_thm( - "PAD_RIGHT_EQ_NIL", - ``!l c n. (PAD_RIGHT c n l = []) <=> ((l = []) /\ (n = 0))``, - rw[PAD_RIGHT, EQ_IMP_THM] >> - fs[GENLIST_EQ_NIL]); - -(* Theorem: 0 < n ==> (PAD_LEFT c n [] = PAD_LEFT c n [c]) *) -(* Proof: - PAD_LEFT c n [] - = GENLIST (K c) n by PAD_LEFT, APPEND_NIL - = GENLIST (K c) (SUC k) by n = SUC k, 0 < n - = SNOC c (GENLIST (K c) k) by GENLIST, (K c) k = c - = GENLIST (K c) k ++ [c] by SNOC_APPEND - = PAD_LEFT c n [c] by PAD_LEFT -*) -val PAD_LEFT_NIL_EQ = store_thm( - "PAD_LEFT_NIL_EQ", - ``!n c. 0 < n ==> (PAD_LEFT c n [] = PAD_LEFT c n [c])``, - rw[PAD_LEFT] >> - `SUC (n - 1) = n` by decide_tac >> - qabbrev_tac `f = (K c):num -> 'a` >> - `f (n - 1) = c` by rw[Abbr`f`] >> - metis_tac[SNOC_APPEND, GENLIST]); - -(* Theorem: 0 < n ==> (PAD_RIGHT c n [] = PAD_RIGHT c n [c]) *) -(* Proof: - PAD_RIGHT c n [] - = GENLIST (K c) n by PAD_RIGHT - = GENLIST (K c) (SUC (n - 1)) by 0 < n - = c :: GENLIST (K c) (n - 1) by GENLIST_K_CONS - = [c] ++ GENLIST (K c) (n - 1) by CONS_APPEND - = PAD_RIGHT c (SUC (n - 1)) [c] by PAD_RIGHT - = PAD_RIGHT c n [c] by 0 < n -*) -val PAD_RIGHT_NIL_EQ = store_thm( - "PAD_RIGHT_NIL_EQ", - ``!n c. 0 < n ==> (PAD_RIGHT c n [] = PAD_RIGHT c n [c])``, - rw[PAD_RIGHT] >> - `SUC (n - 1) = n` by decide_tac >> - metis_tac[GENLIST_K_CONS]); - -(* Theorem: PAD_RIGHT c n ls = ls ++ PAD_RIGHT c (n - LENGTH ls) [] *) -(* Proof: - PAD_RIGHT c n ls - = ls ++ GENLIST (K c) (n - LENGTH ls) by PAD_RIGHT - = ls ++ ([] ++ GENLIST (K c) ((n - LENGTH ls) - 0) by APPEND_NIL, LENGTH - = ls ++ PAD_RIGHT c (n - LENGTH ls) [] by PAD_RIGHT -*) -val PAD_RIGHT_BY_RIGHT = store_thm( - "PAD_RIGHT_BY_RIGHT", - ``!ls c n. PAD_RIGHT c n ls = ls ++ PAD_RIGHT c (n - LENGTH ls) []``, - rw[PAD_RIGHT]); - -(* Theorem: PAD_RIGHT c n ls = ls ++ PAD_LEFT c (n - LENGTH ls) [] *) -(* Proof: - PAD_RIGHT c n ls - = ls ++ GENLIST (K c) (n - LENGTH ls) by PAD_RIGHT - = ls ++ (GENLIST (K c) ((n - LENGTH ls) - 0) ++ []) by APPEND_NIL, LENGTH - = ls ++ PAD_LEFT c (n - LENGTH ls) [] by PAD_LEFT -*) -val PAD_RIGHT_BY_LEFT = store_thm( - "PAD_RIGHT_BY_LEFT", - ``!ls c n. PAD_RIGHT c n ls = ls ++ PAD_LEFT c (n - LENGTH ls) []``, - rw[PAD_RIGHT, PAD_LEFT]); - -(* Theorem: PAD_LEFT c n ls = (PAD_RIGHT c (n - LENGTH ls) []) ++ ls *) -(* Proof: - PAD_LEFT c n ls - = GENLIST (K c) (n - LENGTH ls) ++ ls by PAD_LEFT - = ([] ++ GENLIST (K c) ((n - LENGTH ls) - 0) ++ ls by APPEND_NIL, LENGTH - = (PAD_RIGHT c (n - LENGTH ls) []) ++ ls by PAD_RIGHT -*) -val PAD_LEFT_BY_RIGHT = store_thm( - "PAD_LEFT_BY_RIGHT", - ``!ls c n. PAD_LEFT c n ls = (PAD_RIGHT c (n - LENGTH ls) []) ++ ls``, - rw[PAD_RIGHT, PAD_LEFT]); - -(* Theorem: PAD_LEFT c n ls = (PAD_LEFT c (n - LENGTH ls) []) ++ ls *) -(* Proof: - PAD_LEFT c n ls - = GENLIST (K c) (n - LENGTH ls) ++ ls by PAD_LEFT - = ((GENLIST (K c) ((n - LENGTH ls) - 0) ++ []) ++ ls by APPEND_NIL, LENGTH - = (PAD_LEFT c (n - LENGTH ls) []) ++ ls by PAD_LEFT -*) -val PAD_LEFT_BY_LEFT = store_thm( - "PAD_LEFT_BY_LEFT", - ``!ls c n. PAD_LEFT c n ls = (PAD_LEFT c (n - LENGTH ls) []) ++ ls``, - rw[PAD_LEFT]); - -(* ------------------------------------------------------------------------- *) -(* PROD for List, similar to SUM for List *) -(* ------------------------------------------------------------------------- *) - -(* Overload a positive list *) -val _ = overload_on("POSITIVE", ``\l. !x. MEM x l ==> 0 < x``); -val _ = overload_on("EVERY_POSITIVE", ``\l. EVERY (\k. 0 < k) l``); - -(* Theorem: EVERY_POSITIVE ls <=> POSITIVE ls *) -(* Proof: by EVERY_MEM *) -val POSITIVE_THM = store_thm( - "POSITIVE_THM", - ``!ls. EVERY_POSITIVE ls <=> POSITIVE ls``, - rw[EVERY_MEM]); - -(* Note: For product of a number list, any zero element will make the product 0. *) - -(* Define PROD, similar to SUM *) -val PROD = new_recursive_definition - {name = "PROD", - rec_axiom = list_Axiom, - def = ``(PROD [] = 1) /\ - (!h t. PROD (h::t) = h * PROD t)``}; - -(* export simple definition *) -val _ = export_rewrites["PROD"]; - -(* Extract theorems from definition *) -val PROD_NIL = save_thm("PROD_NIL", PROD |> CONJUNCT1); -(* val PROD_NIL = |- PROD [] = 1: thm *) - -val PROD_CONS = save_thm("PROD_CONS", PROD |> CONJUNCT2); -(* val PROD_CONS = |- !h t. PROD (h::t) = h * PROD t: thm *) - -(* Theorem: PROD [n] = n *) -(* Proof: by PROD *) -val PROD_SING = store_thm( - "PROD_SING", - ``!n. PROD [n] = n``, - rw[]); - -(* Theorem: PROD ls = if ls = [] then 1 else (HD ls) * PROD (TL ls) *) -(* Proof: by PROD *) -val PROD_eval = store_thm( - "PROD_eval[compute]", (* put in computeLib *) - ``!ls. PROD ls = if ls = [] then 1 else (HD ls) * PROD (TL ls)``, - metis_tac[PROD, list_CASES, HD, TL]); - -(* enable PROD computation -- use [compute] above. *) -(* val _ = computeLib.add_persistent_funs ["PROD_eval"]; *) - -(* Theorem: (PROD ls = 1) = !x. MEM x ls ==> (x = 1) *) -(* Proof: - By induction on ls. - Base: (PROD [] = 1) <=> !x. MEM x [] ==> (x = 1) - LHS: PROD [] = 1 is true by PROD - RHS: is true since MEM x [] = F by MEM - Step: (PROD ls = 1) <=> !x. MEM x ls ==> (x = 1) ==> - !h. (PROD (h::ls) = 1) <=> !x. MEM x (h::ls) ==> (x = 1) - Note 1 = PROD (h::ls) by given - = h * PROD ls by PROD - Thus h = 1 /\ PROD ls = 1 by MULT_EQ_1 - or h = 1 /\ !x. MEM x ls ==> (x = 1) by induction hypothesis - or !x. MEM x (h::ls) ==> (x = 1) by MEM -*) -val PROD_eq_1 = store_thm( - "PROD_eq_1", - ``!ls. (PROD ls = 1) = !x. MEM x ls ==> (x = 1)``, - Induct >> - rw[] >> - metis_tac[]); - -(* Theorem: PROD (SNOC x l) = (PROD l) * x *) -(* Proof: - By induction on l. - Base: PROD (SNOC x []) = PROD [] * x - PROD (SNOC x []) - = PROD [x] by SNOC - = x by PROD - = 1 * x by MULT_LEFT_1 - = PROD [] * x by PROD - Step: PROD (SNOC x l) = PROD l * x ==> !h. PROD (SNOC x (h::l)) = PROD (h::l) * x - PROD (SNOC x (h::l)) - = PROD (h:: SNOC x l) by SNOC - = h * PROD (SNOC x l) by PROD - = h * (PROD l * x) by induction hypothesis - = (h * PROD l) * x by MULT_ASSOC - = PROD (h::l) * x by PROD -*) -val PROD_SNOC = store_thm( - "PROD_SNOC", - ``!x l. PROD (SNOC x l) = (PROD l) * x``, - strip_tac >> - Induct >> - rw[]); - -(* Theorem: PROD (APPEND l1 l2) = PROD l1 * PROD l2 *) -(* Proof: - By induction on l1. - Base: PROD ([] ++ l2) = PROD [] * PROD l2 - PROD ([] ++ l2) - = PROD l2 by APPEND - = 1 * PROD l2 by MULT_LEFT_1 - = PROD [] * PROD l2 by PROD - Step: !l2. PROD (l1 ++ l2) = PROD l1 * PROD l2 ==> !h l2. PROD (h::l1 ++ l2) = PROD (h::l1) * PROD l2 - PROD (h::l1 ++ l2) - = PROD (h::(l1 ++ l2)) by APPEND - = h * PROD (l1 ++ l2) by PROD - = h * (PROD l1 * PROD l2) by induction hypothesis - = (h * PROD l1) * PROD l2 by MULT_ASSOC - = PROD (h::l1) * PROD l2 by PROD -*) -val PROD_APPEND = store_thm( - "PROD_APPEND", - ``!l1 l2. PROD (APPEND l1 l2) = PROD l1 * PROD l2``, - Induct >> rw[]); - -(* Theorem: PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls *) -(* Proof: - By SNOC_INDUCT |- !P. P [] /\ (!l. P l ==> !x. P (SNOC x l)) ==> !l. P l - Base: PROD (MAP f []) = FOLDL (\a e. a * f e) 1 [] - PROD (MAP f []) - = PROD [] by MAP - = 1 by PROD - = FOLDL (\a e. a * f e) 1 [] by FOLDL - Step: !f. PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls ==> - PROD (MAP f (SNOC x ls)) = FOLDL (\a e. a * f e) 1 (SNOC x ls) - PROD (MAP f (SNOC x ls)) - = PROD (SNOC (f x) (MAP f ls)) by MAP_SNOC - = PROD (MAP f ls) * (f x) by PROD_SNOC - = (FOLDL (\a e. a * f e) 1 ls) * (f x) by induction hypothesis - = (\a e. a * f e) (FOLDL (\a e. a * f e) 1 ls) x by function application - = FOLDL (\a e. a * f e) 1 (SNOC x ls) by FOLDL_SNOC -*) -val PROD_MAP_FOLDL = store_thm( - "PROD_MAP_FOLDL", - ``!ls f. PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls``, - HO_MATCH_MP_TAC SNOC_INDUCT >> - rpt strip_tac >- - rw[] >> - rw[MAP_SNOC, PROD_SNOC, FOLDL_SNOC]); - -(* Theorem: FINITE s ==> !f. PI f s = PROD (MAP f (SET_TO_LIST s)) *) -(* Proof: - PI f s - = ITSET (\e acc. f e * acc) s 1 by PROD_IMAGE_DEF - = FOLDL (combin$C (\e acc. f e * acc)) 1 (SET_TO_LIST s) by ITSET_eq_FOLDL_SET_TO_LIST, FINITE s - = FOLDL (\a e. a * f e) 1 (SET_TO_LIST s) by FUN_EQ_THM - = PROD (MAP f (SET_TO_LIST s)) by PROD_MAP_FOLDL -*) -val PROD_IMAGE_eq_PROD_MAP_SET_TO_LIST = store_thm( - "PROD_IMAGE_eq_PROD_MAP_SET_TO_LIST", - ``!s. FINITE s ==> !f. PI f s = PROD (MAP f (SET_TO_LIST s))``, - rw[PROD_IMAGE_DEF] >> - rw[ITSET_eq_FOLDL_SET_TO_LIST, PROD_MAP_FOLDL] >> - rpt AP_THM_TAC >> - AP_TERM_TAC >> - rw[FUN_EQ_THM]); - -(* Define PROD using accumulator *) -val PROD_ACC_DEF = Lib.with_flag (Defn.def_suffix, "_DEF") Define - `(PROD_ACC [] acc = acc) /\ - (PROD_ACC (h::t) acc = PROD_ACC t (h * acc))`; - -(* Theorem: PROD_ACC L n = PROD L * n *) -(* Proof: - By induction on L. - Base: !n. PROD_ACC [] n = PROD [] * n - PROD_ACC [] n - = n by PROD_ACC_DEF - = 1 * n by MULT_LEFT_1 - = PROD [] * n by PROD - Step: !n. PROD_ACC L n = PROD L * n ==> !h n. PROD_ACC (h::L) n = PROD (h::L) * n - PROD_ACC (h::L) n - = PROD_ACC L (h * n) by PROD_ACC_DEF - = PROD L * (h * n) by induction hypothesis - = (PROD L * h) * n by MULT_ASSOC - = (h * PROD L) * n by MULT_COMM - = PROD (h::L) * n by PROD -*) -val PROD_ACC_PROD_LEM = store_thm( - "PROD_ACC_PROD_LEM", - ``!L n. PROD_ACC L n = PROD L * n``, - Induct >> - rw[PROD_ACC_DEF]); -(* proof SUM_ACC_SUM_LEM *) -val PROD_ACC_PROD_LEM = store_thm -("PROD_ACC_SUM_LEM", - ``!L n. PROD_ACC L n = PROD L * n``, - Induct THEN RW_TAC arith_ss [PROD_ACC_DEF, PROD]); - -(* Theorem: PROD L = PROD_ACC L 1 *) -(* Proof: Put n = 1 in PROD_ACC_PROD_LEM *) -Theorem PROD_PROD_ACC[compute]: - !L. PROD L = PROD_ACC L 1 -Proof - rw[PROD_ACC_PROD_LEM] -QED - -(* EVAL ``PROD [1; 2; 3; 4]``; --> 24 *) - -(* Theorem: PROD (GENLIST (K m) n) = m ** n *) -(* Proof: - By induction on n. - Base: PROD (GENLIST (K m) 0) = m ** 0 - PROD (GENLIST (K m) 0) - = PROD [] by GENLIST - = 1 by PROD - = m ** 0 by EXP - Step: PROD (GENLIST (K m) n) = m ** n ==> PROD (GENLIST (K m) (SUC n)) = m ** SUC n - PROD (GENLIST (K m) (SUC n)) - = PROD (SNOC m (GENLIST (K m) n)) by GENLIST - = PROD (GENLIST (K m) n) * m by PROD_SNOC - = m ** n * m by induction hypothesis - = m * m ** n by MULT_COMM - = m * SUC n by EXP -*) -val PROD_GENLIST_K = store_thm( - "PROD_GENLIST_K", - ``!m n. PROD (GENLIST (K m) n) = m ** n``, - strip_tac >> - Induct >- - rw[] >> - rw[GENLIST, PROD_SNOC, EXP]); - -(* Same as PROD_GENLIST_K, formulated slightly different. *) - -(* Theorem: PPROD (GENLIST (\j. x) n) = x ** n *) -(* Proof: - Note (\j. x) = K x by FUN_EQ_THM - PROD (GENLIST (\j. x) n) - = PROD (GENLIST (K x) n) by GENLIST_FUN_EQ - = x ** n by PROD_GENLIST_K -*) -val PROD_CONSTANT = store_thm( - "PROD_CONSTANT", - ``!n x. PROD (GENLIST (\j. x) n) = x ** n``, - rpt strip_tac >> - `(\j. x) = K x` by rw[FUN_EQ_THM] >> - metis_tac[PROD_GENLIST_K, GENLIST_FUN_EQ]); - -(* Theorem: (PROD l = 0) <=> MEM 0 l *) -(* Proof: - By induction on l. - Base: (PROD [] = 0) <=> MEM 0 [] - LHS = F by PROD_NIL, 1 <> 0 - RHS = F by MEM - Step: (PROD l = 0) <=> MEM 0 l ==> !h. (PROD (h::l) = 0) <=> MEM 0 (h::l) - Note PROD (h::l) = h * PROD l by PROD_CONS - Thus PROD (h::l) = 0 - ==> h = 0 \/ PROD l = 0 by MULT_EQ_0 - If h = 0, then MEM 0 (h::l) by MEM - If PROD l = 0, then MEM 0 l by induction hypothesis - or MEM 0 (h::l) by MEM -*) -val PROD_EQ_0 = store_thm( - "PROD_EQ_0", - ``!l. (PROD l = 0) <=> MEM 0 l``, - Induct >- - rw[] >> - metis_tac[PROD_CONS, MULT_EQ_0, MEM]); - -(* Theorem: EVERY (\x. 0 < x) l ==> 0 < PROD l *) -(* Proof: - By contradiction, suppose PROD l = 0. - Then MEM 0 l by PROD_EQ_0 - or 0 < 0 = F by EVERY_MEM -*) -val PROD_POS = store_thm( - "PROD_POS", - ``!l. EVERY (\x. 0 < x) l ==> 0 < PROD l``, - metis_tac[EVERY_MEM, PROD_EQ_0, NOT_ZERO_LT_ZERO]); - -(* Theorem: POSITIVE l ==> 0 < PROD l *) -(* Proof: PROD_POS, EVERY_MEM *) -val PROD_POS_ALT = store_thm( - "PROD_POS_ALT", - ``!l. POSITIVE l ==> 0 < PROD l``, - rw[PROD_POS, EVERY_MEM]); - -(* Theorem: PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1) *) -(* Proof: - The computation is: - n * (n ** 2) * (n ** 4) * ... * (n ** (2 ** (m - 1))) - = n ** (1 + 2 + 4 + ... + 2 ** (m - 1)) - = n ** (2 ** m - 1) - - By induction on m. - Base: PROD (GENLIST (\j. n ** 2 ** j) 0) = n ** (2 ** 0 - 1) - LHS = PROD (GENLIST (\j. n ** 2 ** j) 0) - = PROD [] by GENLIST_0 - = 1 by PROD - RHS = n ** (1 - 1) by EXP_0 - = n ** 0 = 1 = LHS by EXP_0 - Step: PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1) ==> - PROD (GENLIST (\j. n ** 2 ** j) (SUC m)) = n ** (2 ** SUC m - 1) - PROD (GENLIST (\j. n ** 2 ** j) (SUC m)) - = PROD (SNOC (n ** 2 ** m) (GENLIST (\j. n ** 2 ** j) m)) by GENLIST - = PROD (GENLIST (\j. n ** 2 ** j) m) * (n ** 2 ** m) by PROD_SNOC - = n ** (2 ** m - 1) * n ** 2 ** m by induction hypothesis - = n ** (2 ** m - 1 + 2 ** m) by EXP_ADD - = n ** (2 * 2 ** m - 1) by arithmetic - = n ** (2 ** SUC m - 1) by EXP -*) -val PROD_SQUARING_LIST = store_thm( - "PROD_SQUARING_LIST", - ``!m n. PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1)``, - rpt strip_tac >> - Induct_on `m` >- - rw[] >> - qabbrev_tac `f = \j. n ** 2 ** j` >> - `PROD (GENLIST f (SUC m)) = PROD (SNOC (n ** 2 ** m) (GENLIST f m))` by rw[GENLIST, Abbr`f`] >> - `_ = PROD (GENLIST f m) * (n ** 2 ** m)` by rw[PROD_SNOC] >> - `_ = n ** (2 ** m - 1) * n ** 2 ** m` by rw[] >> - `_ = n ** (2 ** m - 1 + 2 ** m)` by rw[EXP_ADD] >> - rw[EXP]); - -(* ------------------------------------------------------------------------- *) -(* Range Conjunction and Disjunction *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: a <= j /\ j <= a <=> (j = a) *) -(* Proof: trivial by arithmetic. *) -val every_range_sing = store_thm( - "every_range_sing", - ``!a j. a <= j /\ j <= a <=> (j = a)``, - decide_tac); - -(* Theorem: a <= b ==> - ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ !j. a + 1 <= j /\ j <= b ==> f j)) *) -(* Proof: - If part: !j. a <= j /\ j <= b ==> f j ==> - f a /\ !j. a + 1 <= j /\ j <= b ==> f j - This is trivial since a + 1 = SUC a. - Only-if part: f a /\ !j. a + 1 <= j /\ j <= b ==> f j ==> - !j. a <= j /\ j <= b ==> f j - Note a <= j <=> a = j or a < j by arithmetic - If a = j, this is trivial. - If a < j, then a + 1 <= j, also trivial. -*) -val every_range_cons = store_thm( - "every_range_cons", - ``!f a b. a <= b ==> - ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ !j. a + 1 <= j /\ j <= b ==> f j))``, - rw[EQ_IMP_THM] >> - `(a = j) \/ (a < j)` by decide_tac >- - fs[] >> - fs[]); - -(* Theorem: a <= b ==> ((!j. PRE a <= j /\ j <= b ==> f j) <=> (f (PRE a) /\ !j. a <= j /\ j <= b ==> f j)) *) -(* Proof: - !j. PRE a <= j /\ j <= b ==> f j - <=> !j. (PRE a = j \/ a <= j) /\ j <= b ==> f j by arithmetic - <=> !j. (j = PRE a ==> f j) /\ a <= j /\ j <= b ==> f j by RIGHT_AND_OVER_OR, DISJ_IMP_THM - <=> !j. a <= j /\ j <= b ==> f j /\ f (PRE a) -*) -Theorem every_range_split_head: - !f a b. a <= b ==> - ((!j. PRE a <= j /\ j <= b ==> f j) <=> (f (PRE a) /\ !j. a <= j /\ j <= b ==> f j)) -Proof - rpt strip_tac >> - `!j. PRE a <= j <=> PRE a = j \/ a <= j` by decide_tac >> - metis_tac[] -QED - -(* Theorem: a <= b ==> ((!j. a <= j /\ j <= SUC b ==> f j) <=> (f (SUC b) /\ !j. a <= j /\ j <= b ==> f j)) *) -(* Proof: - !j. a <= j /\ j <= SUC b ==> f j - <=> !j. a <= j /\ (j <= b \/ j = SUC b) ==> f j by arithmetic - <=> !j. a <= j /\ j <= b ==> f j /\ (j = SUC b ==> f j) by LEFT_AND_OVER_OR, DISJ_IMP_THM - <=> !j. a <= j /\ j <= b ==> f j /\ f (SUC b) -*) -Theorem every_range_split_last: - !f a b. a <= b ==> - ((!j. a <= j /\ j <= SUC b ==> f j) <=> (f (SUC b) /\ !j. a <= j /\ j <= b ==> f j)) -Proof - rpt strip_tac >> - `!j. j <= SUC b <=> j <= b \/ j = SUC b` by decide_tac >> - metis_tac[] -QED - -(* Theorem: a <= b ==> ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ f b /\ !j. a < j /\ j < b ==> f j)) *) -(* Proof: - !j. a <= j /\ j <= b ==> f j - <=> !j. (a < j \/ a = j) /\ (j < b \/ j = b) ==> f j by arithmetic - <=> !j. a = j ==> f j /\ j = b ==> f j /\ !j. a < j /\ j < b ==> f j by LEFT_AND_OVER_OR, DISJ_IMP_THM - <=> f a /\ f b /\ !j. a < j /\ j < b ==> f j -*) -Theorem every_range_less_ends: - !f a b. a <= b ==> - ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ f b /\ !j. a < j /\ j < b ==> f j)) -Proof - rpt strip_tac >> - `!m n. m <= n <=> m < n \/ m = n` by decide_tac >> - metis_tac[] -QED - -(* Theorem: a < b /\ f a /\ ~f b ==> - ?m. a <= m /\ m < b /\ (!j. a <= j /\ j <= m ==> f j) /\ ~f (SUC m) *) -(* Proof: - Let s = {p | a <= p /\ p < b /\ (!j. a <= j /\ j <= p ==> f j)} - Pick m = MAX_SET s. - Note f a ==> a IN s by every_range_sing - so s <> {} by MEMBER_NOT_EMPTY - Also s SUBSET (count b) by SUBSET_DEF - so FINITE s by FINITE_COUNT, SUBSET_FINITE - ==> m IN s by MAX_SET_IN_SET - Thus a <= m /\ m < b /\ (!j. a <= j /\ j <= m ==> f j) - It remains to show: ~f (SUC m). - By contradiction, suppose f (SUC m). - Since m < b, SUC m <= b. - But ~f b, so SUC m <> b by given - Thus a <= m < SUC m, and SUC m < b, - and !j. a <= j /\ j <= SUC m ==> f j) - ==> SUC m IN s by every_range_split_last - Then SUC m <= m by X_LE_MAX_SET - which is impossible by LESS_SUC -*) -Theorem every_range_span_max: - !f a b. a < b /\ f a /\ ~f b ==> - ?m. a <= m /\ m < b /\ (!j. a <= j /\ j <= m ==> f j) /\ ~f (SUC m) -Proof - rpt strip_tac >> - qabbrev_tac `s = {p | a <= p /\ p < b /\ (!j. a <= j /\ j <= p ==> f j)}` >> - qabbrev_tac `m = MAX_SET s` >> - qexists_tac `m` >> - `a IN s` by fs[every_range_sing, Abbr`s`] >> - `s SUBSET (count b)` by fs[SUBSET_DEF, Abbr`s`] >> - `FINITE s /\ s <> {}` by metis_tac[FINITE_COUNT, SUBSET_FINITE, MEMBER_NOT_EMPTY] >> - `m IN s` by fs[MAX_SET_IN_SET, Abbr`m`] >> - rfs[Abbr`s`] >> - spose_not_then strip_assume_tac >> - qabbrev_tac `s = {p | a <= p /\ p < b /\ (!j. a <= j /\ j <= p ==> f j)}` >> - `SUC m <> b` by metis_tac[] >> - `a <= SUC m /\ SUC m < b` by decide_tac >> - `SUC m IN s` by fs[every_range_split_last, Abbr`s`] >> - `SUC m <= m` by simp[X_LE_MAX_SET, Abbr`m`] >> - decide_tac -QED - -(* Theorem: a < b /\ ~f a /\ f b ==> - ?m. a < m /\ m <= b /\ (!j. m <= j /\ j <= b ==> f j) /\ ~f (PRE m) *) -(* Proof: - Let s = {p | a < p /\ p <= b /\ (!j. p <= j /\ j <= b ==> f j)} - Pick m = MIN_SET s. - Note f b ==> b IN s by every_range_sing - so s <> {} by MEMBER_NOT_EMPTY - ==> m IN s by MIN_SET_IN_SET - Thus a < m /\ m <= b /\ (!j. m <= j /\ j <= b ==> f j) - It remains to show: ~f (PRE m). - By contradiction, suppose f (PRE m). - Since a < m, a <= PRE m. - But ~f a, so PRE m <> a by given - Thus a < PRE m, and PRE m <= b, - and !j. PRE m <= j /\ j <= b ==> f j) - ==> PRE m IN s by every_range_split_head - Then m <= PRE m by MIN_SET_PROPERTY - which is impossible by PRE_LESS, a < m ==> 0 < m -*) -Theorem every_range_span_min: - !f a b. a < b /\ ~f a /\ f b ==> - ?m. a < m /\ m <= b /\ (!j. m <= j /\ j <= b ==> f j) /\ ~f (PRE m) -Proof - rpt strip_tac >> - qabbrev_tac `s = {p | a < p /\ p <= b /\ (!j. p <= j /\ j <= b ==> f j)}` >> - qabbrev_tac `m = MIN_SET s` >> - qexists_tac `m` >> - `b IN s` by fs[every_range_sing, Abbr`s`] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - `m IN s` by fs[MIN_SET_IN_SET, Abbr`m`] >> - rfs[Abbr`s`] >> - spose_not_then strip_assume_tac >> - qabbrev_tac `s = {p | a < p /\ p <= b /\ (!j. p <= j /\ j <= b ==> f j)}` >> - `PRE m <> a` by metis_tac[] >> - `a < PRE m /\ PRE m <= b` by decide_tac >> - `PRE m IN s` by fs[every_range_split_head, Abbr`s`] >> - `m <= PRE m` by simp[MIN_SET_PROPERTY, Abbr`m`] >> - decide_tac -QED - -(* Theorem: ?j. a <= j /\ j <= a <=> (j = a) *) -(* Proof: trivial by arithmetic. *) -val exists_range_sing = store_thm( - "exists_range_sing", - ``!a. ?j. a <= j /\ j <= a <=> (j = a)``, - metis_tac[LESS_EQ_REFL]); - -(* Theorem: a <= b ==> - ((?j. a <= j /\ j <= b /\ f j) <=> (f a \/ ?j. a + 1 <= j /\ j <= b /\ f j)) *) -(* Proof: - If part: ?j. a <= j /\ j <= b /\ f j ==> - f a \/ ?j. a + 1 <= j /\ j <= b /\ f j - This is trivial since a + 1 = SUC a. - Only-if part: f a /\ ?j. a + 1 <= j /\ j <= b /\ f j ==> - ?j. a <= j /\ j <= b /\ f j - Note a <= j <=> a = j or a < j by arithmetic - If a = j, this is trivial. - If a < j, then a + 1 <= j, also trivial. -*) -val exists_range_cons = store_thm( - "exists_range_cons", - ``!f a b. a <= b ==> - ((?j. a <= j /\ j <= b /\ f j) <=> (f a \/ ?j. a + 1 <= j /\ j <= b /\ f j))``, - rw[EQ_IMP_THM] >| [ - `(a = j) \/ (a < j)` by decide_tac >- - fs[] >> - `a + 1 <= j` by decide_tac >> - metis_tac[], - metis_tac[LESS_EQ_REFL], - `a <= j` by decide_tac >> - metis_tac[] - ]); - -(* ------------------------------------------------------------------------- *) -(* List Range *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: [m .. n] = [m ..< SUC n] *) -(* Proof: - = [m .. n] - = GENLIST (\i. m + i) (n + 1 - m) by listRangeINC_def - = [m ..< (n + 1)] by listRangeLHI_def - = [m ..< SUC n] by ADD1 -*) -Theorem listRangeINC_to_LHI: - !m n. [m .. n] = [m ..< SUC n] -Proof - rw[listRangeLHI_def, listRangeINC_def, ADD1] -QED - -(* Theorem: set [1 .. n] = IMAGE SUC (count n) *) -(* Proof: - x IN set [1 .. n] - <=> 1 <= x /\ x <= n by listRangeINC_MEM - <=> 0 < x /\ PRE x < n by arithmetic - <=> 0 < SUC (PRE x) /\ PRE x < n by SUC_PRE, 0 < x - <=> x IN IMAGE SUC (count n) by IN_COUNT, IN_IMAGE -*) -Theorem listRangeINC_SET: - !n. set [1 .. n] = IMAGE SUC (count n) -Proof - rw[EXTENSION, EQ_IMP_THM] >> - `0 < x /\ PRE x < n` by decide_tac >> - metis_tac[SUC_PRE] -QED - -(* Theorem: LENGTH [m .. n] = n + 1 - m *) -(* Proof: - LENGTH [m .. n] - = LENGTH (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def - = n + 1 - m by LENGTH_GENLIST -*) -val listRangeINC_LEN = store_thm( - "listRangeINC_LEN", - ``!m n. LENGTH [m .. n] = n + 1 - m``, - rw[listRangeINC_def]); - -(* Theorem: ([m .. n] = []) <=> (n + 1 <= m) *) -(* Proof: - [m .. n] = [] - <=> LENGTH [m .. n] = 0 by LENGTH_NIL - <=> n + 1 - m = 0 by listRangeINC_LEN - <=> n + 1 <= m by arithmetic -*) -val listRangeINC_NIL = store_thm( - "listRangeINC_NIL", - ``!m n. ([m .. n] = []) <=> (n + 1 <= m)``, - metis_tac[listRangeINC_LEN, LENGTH_NIL, DECIDE``(n + 1 - m = 0) <=> (n + 1 <= m)``]); - -(* Rename a theorem *) -val listRangeINC_MEM = save_thm("listRangeINC_MEM", - MEM_listRangeINC |> GEN ``x:num`` |> GEN ``n:num`` |> GEN ``m:num``); -(* -val listRangeINC_MEM = |- !m n x. MEM x [m .. n] <=> m <= x /\ x <= n: thm -*) - -(* -EL_listRangeLHI -|- lo + i < hi ==> EL i [lo ..< hi] = lo + i -*) - -(* Theorem: m + i <= n ==> (EL i [m .. n] = m + i) *) -(* Proof: by listRangeINC_def *) -val listRangeINC_EL = store_thm( - "listRangeINC_EL", - ``!m n i. m + i <= n ==> (EL i [m .. n] = m + i)``, - rw[listRangeINC_def]); - -(* Theorem: EVERY P [m .. n] <=> !x. m <= x /\ x <= n ==> P x *) -(* Proof: - EVERY P [m .. n] - <=> !x. MEM x [m .. n] ==> P x by EVERY_MEM - <=> !x. m <= x /\ x <= n ==> P x by MEM_listRangeINC -*) -val listRangeINC_EVERY = store_thm( - "listRangeINC_EVERY", - ``!P m n. EVERY P [m .. n] <=> !x. m <= x /\ x <= n ==> P x``, - rw[EVERY_MEM, MEM_listRangeINC]); - -(* Theorem: EXISTS P [m .. n] <=> ?x. m <= x /\ x <= n /\ P x *) -(* Proof: - EXISTS P [m .. n] - <=> ?x. MEM x [m .. n] /\ P x by EXISTS_MEM - <=> ?x. m <= x /\ x <= n /\ P e by MEM_listRangeINC -*) -val listRangeINC_EXISTS = store_thm( - "listRangeINC_EXISTS", - ``!P m n. EXISTS P [m .. n] <=> ?x. m <= x /\ x <= n /\ P x``, - metis_tac[EXISTS_MEM, MEM_listRangeINC]); - -(* Theorem: EVERY P [m .. n] <=> ~(EXISTS ($~ o P) [m .. n]) *) -(* Proof: - EVERY P [m .. n] - <=> !x. m <= x /\ x <= n ==> P x by listRangeINC_EVERY - <=> ~(?x. m <= x /\ x <= n /\ ~(P x)) by negation - <=> ~(EXISTS ($~ o P) [m .. m]) by listRangeINC_EXISTS -*) -val listRangeINC_EVERY_EXISTS = store_thm( - "listRangeINC_EVERY_EXISTS", - ``!P m n. EVERY P [m .. n] <=> ~(EXISTS ($~ o P) [m .. n])``, - rw[listRangeINC_EVERY, listRangeINC_EXISTS]); - -(* Theorem: EXISTS P [m .. n] <=> ~(EVERY ($~ o P) [m .. n]) *) -(* Proof: - EXISTS P [m .. n] - <=> ?x. m <= x /\ x <= m /\ P x by listRangeINC_EXISTS - <=> ~(!x. m <= x /\ x <= n ==> ~(P x)) by negation - <=> ~(EVERY ($~ o P) [m .. n]) by listRangeINC_EVERY -*) -val listRangeINC_EXISTS_EVERY = store_thm( - "listRangeINC_EXISTS_EVERY", - ``!P m n. EXISTS P [m .. n] <=> ~(EVERY ($~ o P) [m .. n])``, - rw[listRangeINC_EXISTS, listRangeINC_EVERY]); - -(* Theorem: m <= n + 1 ==> ([m .. (n + 1)] = SNOC (n + 1) [m .. n]) *) -(* Proof: - [m .. (n + 1)] - = GENLIST (\i. m + i) ((n + 1) + 1 - m) by listRangeINC_def - = GENLIST (\i. m + i) (1 + (n + 1 - m)) by arithmetic - = GENLIST (\i. m + i) (n + 1 - m) ++ GENLIST (\t. (\i. m + i) (t + n + 1 - m)) 1 by GENLIST_APPEND - = [m .. n] ++ GENLIST (\t. (\i. m + i) (t + n + 1 - m)) 1 by listRangeINC_def - = [m .. n] ++ [(\t. (\i. m + i) (t + n + 1 - m)) 0] by GENLIST_1 - = [m .. n] ++ [m + n + 1 - m] by function evaluation - = [m .. n] ++ [n + 1] by arithmetic - = SNOC (n + 1) [m .. n] by SNOC_APPEND -*) -val listRangeINC_SNOC = store_thm( - "listRangeINC_SNOC", - ``!m n. m <= n + 1 ==> ([m .. (n + 1)] = SNOC (n + 1) [m .. n])``, - rw[listRangeINC_def] >> - `(n + 2 - m = 1 + (n + 1 - m)) /\ (n + 1 - m + m = n + 1)` by decide_tac >> - rw_tac std_ss[GENLIST_APPEND, GENLIST_1]); - -(* Theorem: m <= n + 1 ==> (FRONT [m .. (n + 1)] = [m .. n]) *) -(* Proof: - FRONT [m .. (n + 1)] - = FRONT (SNOC (n + 1) [m .. n])) by listRangeINC_SNOC - = [m .. n] by FRONT_SNOC -*) -Theorem listRangeINC_FRONT: - !m n. m <= n + 1 ==> (FRONT [m .. (n + 1)] = [m .. n]) -Proof - simp[listRangeINC_SNOC, FRONT_SNOC] -QED - -(* Theorem: m <= n ==> (LAST [m .. n] = n) *) -(* Proof: - Let ls = [m .. n] - Note ls <> [] by listRangeINC_NIL - so LAST ls - = EL (PRE (LENGTH ls)) ls by LAST_EL - = EL (PRE (n + 1 - m)) ls by listRangeINC_LEN - = EL (n - m) ls by arithmetic - = n by listRangeINC_EL - Or - LAST [m .. n] - = LAST (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def - = LAST (GENLIST (\i. m + i) (SUC (n - m)) by arithmetic, m <= n - = (\i. m + i) (n - m) by GENLIST_LAST - = m + (n - m) by function application - = n by m <= n - Or - If n = 0, then m <= 0 means m = 0. - LAST [0 .. 0] = LAST [0] = 0 = n by LAST_DEF - Otherwise n = SUC k. - LAST [m .. n] - = LAST (SNOC n [m .. k]) by listRangeINC_SNOC, ADD1 - = n by LAST_SNOC -*) -Theorem listRangeINC_LAST: - !m n. m <= n ==> (LAST [m .. n] = n) -Proof - rpt strip_tac >> - Cases_on `n` >- - fs[] >> - metis_tac[listRangeINC_SNOC, LAST_SNOC, ADD1] -QED - -(* Theorem: REVERSE [m .. n] = MAP (\x. n - x + m) [m .. n] *) -(* Proof: - REVERSE [m .. n] - = REVERSE (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def - = GENLIST (\x. (\i. m + i) (PRE (n + 1 - m) - x)) (n + 1 - m) by REVERSE_GENLIST - = GENLIST (\x. (\i. m + i) (n - m - x)) (n + 1 - m) by PRE - = GENLIST (\x. (m + n - m - x) (n + 1 - m) by function application - = GENLIST (\x. n - x) (n + 1 - m) by arithmetic - - MAP (\x. n - x + m) [m .. n] - = MAP (\x. n - x + m) (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def - = GENLIST ((\x. n - x + m) o (\i. m + i)) (n + 1 - m) by MAP_GENLIST - = GENLIST (\i. n - (m + i) + m) (n + 1 - m) by function composition - = GENLIST (\i. n - i) (n + 1 - m) by arithmetic -*) -val listRangeINC_REVERSE = store_thm( - "listRangeINC_REVERSE", - ``!m n. REVERSE [m .. n] = MAP (\x. n - x + m) [m .. n]``, - rpt strip_tac >> - `(\m'. PRE (n + 1 - m) - m' + m) = ((\x. n - x + m) o (\i. i + m))` by rw[FUN_EQ_THM, combinTheory.o_THM] >> - rw[listRangeINC_def, REVERSE_GENLIST, MAP_GENLIST]); - -(* Theorem: REVERSE (MAP f [m .. n]) = MAP (f o (\x. n - x + m)) [m .. n] *) -(* Proof: - REVERSE (MAP f [m .. n]) - = MAP f (REVERSE [m .. n]) by MAP_REVERSE - = MAP f (MAP (\x. n - x + m) [m .. n]) by listRangeINC_REVERSE - = MAP (f o (\x. n - x + m)) [m .. n] by MAP_MAP_o -*) -val listRangeINC_REVERSE_MAP = store_thm( - "listRangeINC_REVERSE_MAP", - ``!f m n. REVERSE (MAP f [m .. n]) = MAP (f o (\x. n - x + m)) [m .. n]``, - metis_tac[MAP_REVERSE, listRangeINC_REVERSE, MAP_MAP_o]); - -(* Theorem: MAP f [(m + 1) .. (n + 1)] = MAP (f o SUC) [m .. n] *) -(* Proof: - Note (\i. (m + 1) + i) = SUC o (\i. (m + i)) by FUN_EQ_THM - MAP f [(m + 1) .. (n + 1)] - = MAP f (GENLIST (\i. (m + 1) + i) ((n + 1) + 1 - (m + 1))) by listRangeINC_def - = MAP f (GENLIST (\i. (m + 1) + i) (n + 1 - m)) by arithmetic - = MAP f (GENLIST (SUC o (\i. (m + i))) (n + 1 - m)) by above - = MAP (f o SUC) (GENLIST (\i. (m + i)) (n + 1 - m)) by MAP_GENLIST - = MAP (f o SUC) [m .. n] by listRangeINC_def -*) -val listRangeINC_MAP_SUC = store_thm( - "listRangeINC_MAP_SUC", - ``!f m n. MAP f [(m + 1) .. (n + 1)] = MAP (f o SUC) [m .. n]``, - rpt strip_tac >> - `(\i. (m + 1) + i) = SUC o (\i. (m + i))` by rw[FUN_EQ_THM] >> - rw[listRangeINC_def, MAP_GENLIST]); - -(* Theorem: a <= b /\ b <= c ==> ([a .. b] ++ [(b + 1) .. c] = [a .. c]) *) -(* Proof: - By listRangeINC_def, this is to show: - GENLIST (\i. a + i) (b + 1 - a) ++ GENLIST (\i. b + (i + 1)) (c - b) = GENLIST (\i. a + i) (c + 1 - a) - Let f = \i. a + i. - Note (\t. f (t + (b + 1 - a))) = (\i. b + (i + 1)) by FUN_EQ_THM - Thus GENLIST (\i. b + (i + 1)) (c - b) = GENLIST (\t. f (t + (b + 1 - a))) (c - b) by GENLIST_FUN_EQ - Now (c - b) + (b + 1 - a) = c + 1 - a by a <= b /\ b <= c - The result follows by GENLIST_APPEND -*) -val listRangeINC_APPEND = store_thm( - "listRangeINC_APPEND", - ``!a b c. a <= b /\ b <= c ==> ([a .. b] ++ [(b + 1) .. c] = [a .. c])``, - rw[listRangeINC_def] >> - qabbrev_tac `f = \i. a + i` >> - `(\t. f (t + (b + 1 - a))) = (\i. b + (i + 1))` by rw[FUN_EQ_THM, Abbr`f`] >> - `GENLIST (\i. b + (i + 1)) (c - b) = GENLIST (\t. f (t + (b + 1 - a))) (c - b)` by rw[GSYM GENLIST_FUN_EQ] >> - `(c - b) + (b + 1 - a) = c + 1 - a` by decide_tac >> - metis_tac[GENLIST_APPEND]); - -(* Theorem: SUM [m .. n] = SUM [1 .. n] - SUM [1 .. (m - 1)] *) -(* Proof: - If m = 0, - Then [1 .. (m-1)] = [1 .. 0] = [] by listRangeINC_EMPTY - SUM [0 .. n] - = SUM (0::[1 .. n]) by listRangeINC_CONS - = 0 + SUM [1 .. n] by SUM_CONS - = SUM [1 .. n] - 0 by arithmetic - = SUM [1 .. n] - SUM [] by SUM_NIL - If m = 1, - Then [1 .. (m-1)] = [1 .. 0] = [] by listRangeINC_EMPTY - SUM [1 .. n] - = SUM [1 .. n] - 0 by arithmetic - = SUM [1 .. n] - SUM [] by SUM_NIL - Otherwise 1 < m, or 1 <= m - 1. - If n < m, - Then SUM [m .. n] = 0 by listRangeINC_EMPTY - If n = 0, - Then SUM [1 .. 0] = 0 by listRangeINC_EMPTY - and 0 - SUM [1 .. (m - 1)] = 0 by integer subtraction - If n <> 0, - Then 1 <= n /\ n <= m - 1 by arithmetic - ==> [1 .. m - 1] = - [1 .. n] ++ [(n + 1) .. (m - 1)] by listRangeINC_APPEND - or SUM [1 .. m - 1] - = SUM [1 .. n] + SUM [(n + 1) .. (m - 1)] by SUM_APPEND - Thus SUM [1 .. n] - SUM [1 .. m - 1] = 0 by subtraction - If ~(n < m), then m <= n. - Note m - 1 < n /\ (m - 1 + 1 = m) by arithmetic - Thus [1 .. n] = [1 .. (m - 1)] ++ [m .. n] by listRangeINC_APPEND - or SUM [1 .. n] - = SUM [1 .. (m - 1)] + SUM [m .. n] by SUM_APPEND - The result follows by subtraction -*) -val listRangeINC_SUM = store_thm( - "listRangeINC_SUM", - ``!m n. SUM [m .. n] = SUM [1 .. n] - SUM [1 .. (m - 1)]``, - rpt strip_tac >> - Cases_on `m = 0` >- - rw[listRangeINC_EMPTY, listRangeINC_CONS] >> - Cases_on `m = 1` >- - rw[listRangeINC_EMPTY] >> - Cases_on `n < m` >| [ - Cases_on `n = 0` >- - rw[listRangeINC_EMPTY] >> - `1 <= n /\ n <= m - 1` by decide_tac >> - `[1 .. m - 1] = [1 .. n] ++ [(n + 1) .. (m - 1)]` by rw[listRangeINC_APPEND] >> - `SUM [1 .. m - 1] = SUM [1 .. n] + SUM [(n + 1) .. (m - 1)]` by rw[GSYM SUM_APPEND] >> - `SUM [m .. n] = 0` by rw[listRangeINC_EMPTY] >> - decide_tac, - `1 <= m - 1 /\ m - 1 <= n /\ (m - 1 + 1 = m)` by decide_tac >> - `[1 .. n] = [1 .. (m - 1)] ++ [m .. n]` by metis_tac[listRangeINC_APPEND] >> - `SUM [1 .. n] = SUM [1 .. (m - 1)] + SUM [m .. n]` by rw[GSYM SUM_APPEND] >> - decide_tac - ]); - -(* Theorem: 0 < m ==> 0 < PROD [m .. n] *) -(* Proof: - Note MEM 0 [m .. n] = F by MEM_listRangeINC - Thus PROD [m .. n] <> 0 by PROD_EQ_0 - The result follows. - or - Note EVERY_POSITIVE [m .. n] by listRangeINC_EVERY - Thus 0 < PROD [m .. n] by PROD_POS -*) -val listRangeINC_PROD_pos = store_thm( - "listRangeINC_PROD_pos", - ``!m n. 0 < m ==> 0 < PROD [m .. n]``, - rw[PROD_POS, listRangeINC_EVERY]); - -(* Theorem: 0 < m /\ m <= n ==> (PROD [m .. n] = PROD [1 .. n] DIV PROD [1 .. (m - 1)]) *) -(* Proof: - If m = 1, - Then [1 .. (m-1)] = [1 .. 0] = [] by listRangeINC_EMPTY - PROD [1 .. n] - = PROD [1 .. n] DIV 1 by DIV_ONE - = PROD [1 .. n] DIV PROD [] by PROD_NIL - If m <> 1, then 1 <= m by m <> 0, m <> 1 - Note 1 <= m - 1 /\ m - 1 < n /\ (m - 1 + 1 = m) by arithmetic - Thus [1 .. n] = [1 .. (m - 1)] ++ [m .. n] by listRangeINC_APPEND - or PROD [1 .. n] = PROD [1 .. (m - 1)] * PROD [m .. n] by PROD_POS - Now 0 < PROD [1 .. (m - 1)] by listRangeINC_PROD_pos - The result follows by MULT_TO_DIV -*) -val listRangeINC_PROD = store_thm( - "listRangeINC_PROD", - ``!m n. 0 < m /\ m <= n ==> (PROD [m .. n] = PROD [1 .. n] DIV PROD [1 .. (m - 1)])``, - rpt strip_tac >> - Cases_on `m = 1` >- - rw[listRangeINC_EMPTY] >> - `1 <= m - 1 /\ m - 1 <= n /\ (m - 1 + 1 = m)` by decide_tac >> - `[1 .. n] = [1 .. (m - 1)] ++ [m .. n]` by metis_tac[listRangeINC_APPEND] >> - `PROD [1 .. n] = PROD [1 .. (m - 1)] * PROD [m .. n]` by rw[GSYM PROD_APPEND] >> - `0 < PROD [1 .. (m - 1)]` by rw[listRangeINC_PROD_pos] >> - metis_tac[MULT_TO_DIV]); - -(* Theorem: 0 < n /\ m <= x /\ x divides n ==> MEM x [m .. n] *) -(* Proof: - Note x divdes n ==> x <= n by DIVIDES_LE, 0 < n - so MEM x [m .. n] by listRangeINC_MEM -*) -val listRangeINC_has_divisors = store_thm( - "listRangeINC_has_divisors", - ``!m n x. 0 < n /\ m <= x /\ x divides n ==> MEM x [m .. n]``, - rw[listRangeINC_MEM, DIVIDES_LE]); - -(* Theorem: [1 .. n] = GENLIST SUC n *) -(* Proof: by listRangeINC_def *) -val listRangeINC_1_n = store_thm( - "listRangeINC_1_n", - ``!n. [1 .. n] = GENLIST SUC n``, - rpt strip_tac >> - `(\i. i + 1) = SUC` by rw[FUN_EQ_THM] >> - rw[listRangeINC_def]); - -(* Theorem: MAP f [1 .. n] = GENLIST (f o SUC) n *) -(* Proof: - MAP f [1 .. n] - = MAP f (GENLIST SUC n) by listRangeINC_1_n - = GENLIST (f o SUC) n by MAP_GENLIST -*) -val listRangeINC_MAP = store_thm( - "listRangeINC_MAP", - ``!f n. MAP f [1 .. n] = GENLIST (f o SUC) n``, - rw[listRangeINC_1_n, MAP_GENLIST]); - -(* Theorem: SUM (MAP f [1 .. (SUC n)]) = f (SUC n) + SUM (MAP f [1 .. n]) *) -(* Proof: - SUM (MAP f [1 .. (SUC n)]) - = SUM (MAP f (SNOC (SUC n) [1 .. n])) by listRangeINC_SNOC - = SUM (SNOC (f (SUC n)) (MAP f [1 .. n])) by MAP_SNOC - = f (SUC n) + SUM (MAP f [1 .. n]) by SUM_SNOC -*) -val listRangeINC_SUM_MAP = store_thm( - "listRangeINC_SUM_MAP", - ``!f n. SUM (MAP f [1 .. (SUC n)]) = f (SUC n) + SUM (MAP f [1 .. n])``, - rw[listRangeINC_SNOC, MAP_SNOC, SUM_SNOC, ADD1]); - -(* Theorem: m < j /\ j <= n ==> [m .. n] = [m .. j-1] ++ j::[j+1 .. n] *) -(* Proof: - Note m < j implies m <= j-1. - [m .. n] - = [m .. j-1] ++ [j .. n] by listRangeINC_APPEND, m <= j-1 - = [m .. j-1] ++ j::[j+1 .. n] by listRangeINC_CONS, j <= n -*) -Theorem listRangeINC_SPLIT: - !m n j. m < j /\ j <= n ==> [m .. n] = [m .. j-1] ++ j::[j+1 .. n] -Proof - rpt strip_tac >> - `m <= j - 1 /\ j - 1 <= n /\ (j - 1) + 1 = j` by decide_tac >> - `[m .. n] = [m .. j-1] ++ [j .. n]` by metis_tac[listRangeINC_APPEND] >> - simp[listRangeINC_CONS] -QED - -(* Theorem: [m ..< (n + 1)] = [m .. n] *) -(* Proof: - [m ..< (n + 1)] - = GENLIST (\i. m + i) (n + 1 - m) by listRangeLHI_def - = [m .. n] by listRangeINC_def -*) -Theorem listRangeLHI_to_INC: - !m n. [m ..< (n + 1)] = [m .. n] -Proof - rw[listRangeLHI_def, listRangeINC_def] -QED - -(* Theorem: set [0 ..< n] = count n *) -(* Proof: - x IN set [0 ..< n] - <=> 0 <= x /\ x < n by listRangeLHI_MEM - <=> x < n by arithmetic - <=> x IN count n by IN_COUNT -*) -Theorem listRangeLHI_SET: - !n. set [0 ..< n] = count n -Proof - simp[EXTENSION] -QED - -(* Theorem alias *) -Theorem listRangeLHI_LEN = LENGTH_listRangeLHI |> GEN_ALL |> SPEC ``m:num`` |> SPEC ``n:num`` |> GEN_ALL; -(* val listRangeLHI_LEN = |- !n m. LENGTH [m ..< n] = n - m: thm *) - -(* Theorem: ([m ..< n] = []) <=> n <= m *) -(* Proof: - If n = 0, LHS = T, RHS = T hence true. - If n <> 0, then n = SUC k by num_CASES - [m ..< n] = [] - <=> [m ..< SUC k] = [] by n = SUC k - <=> [m .. k] = [] by listRangeLHI_to_INC - <=> k + 1 <= m by listRangeINC_NIL - <=> n <= m by ADD1 -*) -val listRangeLHI_NIL = store_thm( - "listRangeLHI_NIL", - ``!m n. ([m ..< n] = []) <=> n <= m``, - rpt strip_tac >> - Cases_on `n` >- - rw[listRangeLHI_def] >> - rw[listRangeLHI_to_INC, listRangeINC_NIL, ADD1]); - -(* Theorem: MEM x [m ..< n] <=> m <= x /\ x < n *) -(* Proof: by MEM_listRangeLHI *) -val listRangeLHI_MEM = store_thm( - "listRangeLHI_MEM", - ``!m n x. MEM x [m ..< n] <=> m <= x /\ x < n``, - rw[MEM_listRangeLHI]); - -(* Theorem: m + i < n ==> EL i [m ..< n] = m + i *) -(* Proof: EL_listRangeLHI *) -val listRangeLHI_EL = store_thm( - "listRangeLHI_EL", - ``!m n i. m + i < n ==> (EL i [m ..< n] = m + i)``, - rw[EL_listRangeLHI]); - -(* Theorem: EVERY P [m ..< n] <=> !x. m <= x /\ x < n ==> P x *) -(* Proof: - EVERY P [m ..< n] - <=> !x. MEM x [m ..< n] ==> P e by EVERY_MEM - <=> !x. m <= x /\ x < n ==> P e by MEM_listRangeLHI -*) -val listRangeLHI_EVERY = store_thm( - "listRangeLHI_EVERY", - ``!P m n. EVERY P [m ..< n] <=> !x. m <= x /\ x < n ==> P x``, - rw[EVERY_MEM, MEM_listRangeLHI]); - -(* Theorem: m <= n ==> ([m ..< n + 1] = SNOC n [m ..< n]) *) -(* Proof: - If n = 0, - Then m = 0 by m <= n - LHS = [0 ..< 1] = [0] - RHS = SNOC 0 [0 ..< 0] - = SNOC 0 [] by listRangeLHI_def - = [0] = LHS by SNOC - If n <> 0, - Then n = (n - 1) + 1 by arithmetic - [m ..< n + 1] - = [m .. n] by listRangeLHI_to_INC - = SNOC n [m .. n - 1] by listRangeINC_SNOC, m <= (n - 1) + 1 - = SNOC n [m ..< n] by listRangeLHI_to_INC -*) -val listRangeLHI_SNOC = store_thm( - "listRangeLHI_SNOC", - ``!m n. m <= n ==> ([m ..< n + 1] = SNOC n [m ..< n])``, - rpt strip_tac >> - Cases_on `n = 0` >| [ - `m = 0` by decide_tac >> - rw[listRangeLHI_def], - `n = (n - 1) + 1` by decide_tac >> - `[m ..< n + 1] = [m .. n]` by rw[listRangeLHI_to_INC] >> - `_ = SNOC n [m .. n - 1]` by metis_tac[listRangeINC_SNOC] >> - `_ = SNOC n [m ..< n]` by rw[GSYM listRangeLHI_to_INC] >> - rw[] - ]); - -(* Theorem: m <= n ==> (FRONT [m .. < n + 1] = [m .. (FRONT [m ..< n + 1] = [m ..< n]) -Proof - simp[listRangeLHI_SNOC, FRONT_SNOC] -QED - -(* Theorem: m <= n ==> (LAST [m ..< n + 1] = n) *) -(* Proof: - LAST [m ..< n + 1] - = LAST (SNOC n [m ..< n]) by listRangeLHI_SNOC - = n by LAST_SNOC -*) -Theorem listRangeLHI_LAST: - !m n. m <= n ==> (LAST [m ..< n + 1] = n) -Proof - simp[listRangeLHI_SNOC, LAST_SNOC] -QED - -(* Theorem: REVERSE [m ..< n] = MAP (\x. n - 1 - x + m) [m ..< n] *) -(* Proof: - If n = 0, - LHS = REVERSE [] by listRangeLHI_def - = [] by REVERSE_DEF - = MAP f [] = RHS by MAP - If n <> 0, - Then n = k + 1 for some k by num_CASES, ADD1 - REVERSE [m ..< n] - = REVERSE [m .. k] by listRangeLHI_to_INC - = MAP (\x. k - x + m) [m .. k] by listRangeINC_REVERSE - = MAP (\x. n - 1 - x + m) [m ..< n] by listRangeLHI_to_INC -*) -val listRangeLHI_REVERSE = store_thm( - "listRangeLHI_REVERSE", - ``!m n. REVERSE [m ..< n] = MAP (\x. n - 1 - x + m) [m ..< n]``, - rpt strip_tac >> - Cases_on `n` >- - rw[listRangeLHI_def] >> - `REVERSE [m ..< SUC n'] = REVERSE [m .. n']` by rw[listRangeLHI_to_INC, ADD1] >> - `_ = MAP (\x. n' - x + m) [m .. n']` by rw[listRangeINC_REVERSE] >> - `_ = MAP (\x. n' - x + m) [m ..< (SUC n')]` by rw[GSYM listRangeLHI_to_INC, ADD1] >> - rw[]); - -(* Theorem: REVERSE (MAP f [m ..< n]) = MAP (f o (\x. n - 1 - x + m)) [m ..< n] *) -(* Proof: - REVERSE (MAP f [m ..< n]) - = MAP f (REVERSE [m ..< n]) by MAP_REVERSE - = MAP f (MAP (\x. n - 1 - x + m) [m ..< n]) by listRangeLHI_REVERSE - = MAP (f o (\x. n - 1 - x + m)) [m ..< n] by MAP_MAP_o -*) -val listRangeLHI_REVERSE_MAP = store_thm( - "listRangeLHI_REVERSE_MAP", - ``!f m n. REVERSE (MAP f [m ..< n]) = MAP (f o (\x. n - 1 - x + m)) [m ..< n]``, - metis_tac[MAP_REVERSE, listRangeLHI_REVERSE, MAP_MAP_o]); - -(* Theorem: MAP f [(m + 1) ..< (n + 1)] = MAP (f o SUC) [m ..< n] *) -(* Proof: - Note (\i. (m + 1) + i) = SUC o (\i. (m + i)) by FUN_EQ_THM - MAP f [(m + 1) ..< (n + 1)] - = MAP f (GENLIST (\i. (m + 1) + i) ((n + 1) - (m + 1))) by listRangeLHI_def - = MAP f (GENLIST (\i. (m + 1) + i) (n - m)) by arithmetic - = MAP f (GENLIST (SUC o (\i. (m + i))) (n - m)) by above - = MAP (f o SUC) (GENLIST (\i. (m + i)) (n - m)) by MAP_GENLIST - = MAP (f o SUC) [m ..< n] by listRangeLHI_def -*) -val listRangeLHI_MAP_SUC = store_thm( - "listRangeLHI_MAP_SUC", - ``!f m n. MAP f [(m + 1) ..< (n + 1)] = MAP (f o SUC) [m ..< n]``, - rpt strip_tac >> - `(\i. (m + 1) + i) = SUC o (\i. (m + i))` by rw[FUN_EQ_THM] >> - rw[listRangeLHI_def, MAP_GENLIST]); - - -(* Theorem: a <= b /\ b <= c ==> [a ..< b] ++ [b ..< c] = [a ..< c] *) -(* Proof: - If a = b, - LHS = [a ..< a] ++ [a ..< c] - = [] ++ [a ..< c] by listRangeLHI_def - = [a ..< c] = RHS by APPEND - If a <> b, - Then a < b, by a <= b - so b <> 0, and c <> 0 by b <= c - Let b = b' + 1, c = c' + 1 by num_CASES, ADD1 - Then a <= b' /\ b' <= c. - [a ..< b] ++ [b ..< c] - = [a .. b'] ++ [b' + 1 .. c'] by listRangeLHI_to_INC - = [a .. c'] by listRangeINC_APPEND - = [a ..< c] by listRangeLHI_to_INC -*) -val listRangeLHI_APPEND = store_thm( - "listRangeLHI_APPEND", - ``!a b c. a <= b /\ b <= c ==> ([a ..< b] ++ [b ..< c] = [a ..< c])``, - rpt strip_tac >> - `(a = b) \/ (a < b)` by decide_tac >- - rw[listRangeLHI_def] >> - `b <> 0 /\ c <> 0` by decide_tac >> - `?b' c'. (b = b' + 1) /\ (c = c' + 1)` by metis_tac[num_CASES, ADD1] >> - `a <= b' /\ b' <= c` by decide_tac >> - `[a ..< b] ++ [b ..< c] = [a .. b'] ++ [b' + 1 .. c']` by rw[listRangeLHI_to_INC] >> - `_ = [a .. c']` by rw[listRangeINC_APPEND] >> - `_ = [a ..< c]` by rw[GSYM listRangeLHI_to_INC] >> - rw[]); - -(* Theorem: SUM [m ..< n] = SUM [1 ..< n] - SUM [1 ..< m] *) -(* Proof: - If n = 0, - LHS = SUM [m ..< 0] = SUM [] = 0 by listRangeLHI_EMPTY - RHS = SUM [1 ..< 0] - SUM [1 ..< m] - = SUM [] - SUM [1 ..< m] by listRangeLHI_EMPTY - = 0 - SUM [1 ..< m] = 0 = LHS by integer subtraction - If m = 0, - LHS = SUM [0 ..< n] - = SUM (0 :: [1 ..< n]) by listRangeLHI_CONS - = 0 + SUM [1 ..< n] by SUM - = SUM [1 ..< n] by arithmetic - RHS = SUM [1 ..< n] - SUM [1 ..< 0] by integer subtraction - = SUM [1 ..< n] - SUM [] by listRangeLHI_EMPTY - = SUM [1 ..< n] - 0 by SUM - = LHS - Otherwise, - n <> 0, and m <> 0. - Let n = n' + 1, m = m' + 1 by num_CASES, ADD1 - SUM [m ..< n] - = SUM [m .. n'] by listRangeLHI_to_INC - = SUM [1 .. n'] - SUM [1 .. m - 1] by listRangeINC_SUM - = SUM [1 .. n'] - SUM [1 .. m'] by m' = m - 1 - = SUM [1 ..< n] - SUM [1 ..< m] by listRangeLHI_to_INC -*) -val listRangeLHI_SUM = store_thm( - "listRangeLHI_SUM", - ``!m n. SUM [m ..< n] = SUM [1 ..< n] - SUM [1 ..< m]``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[listRangeLHI_EMPTY] >> - Cases_on `m = 0` >- - rw[listRangeLHI_EMPTY, listRangeLHI_CONS] >> - `?n' m'. (n = n' + 1) /\ (m = m' + 1)` by metis_tac[num_CASES, ADD1] >> - `SUM [m ..< n] = SUM [m .. n']` by rw[listRangeLHI_to_INC] >> - `_ = SUM [1 .. n'] - SUM [1 .. m - 1]` by rw[GSYM listRangeINC_SUM] >> - `_ = SUM [1 .. n'] - SUM [1 .. m']` by rw[] >> - `_ = SUM [1 ..< n] - SUM [1 ..< m]` by rw[GSYM listRangeLHI_to_INC] >> - rw[]); - -(* Theorem: 0 < m ==> 0 < PROD [m ..< n] *) -(* Proof: - Note MEM 0 [m ..< n] = F by MEM_listRangeLHI - Thus PROD [m ..< n] <> 0 by PROD_EQ_0 - The result follows. - or, - Note EVERY_POSITIVE [m ..< n] by listRangeLHI_EVERY - Thus 0 < PROD [m ..< n] by PROD_POS -*) -val listRangeLHI_PROD_pos = store_thm( - "listRangeLHI_PROD_pos", - ``!m n. 0 < m ==> 0 < PROD [m ..< n]``, - rw[PROD_POS, listRangeLHI_EVERY]); - -(* Theorem: 0 < m /\ m <= n ==> (PROD [m ..< n] = PROD [1 ..< n] DIV PROD [1 ..< m]) *) -(* Proof: - Note n <> 0 by 0 < m /\ m <= n - Let m = m' + 1, n = n' + 1 by num_CASES, ADD1 - If m = n, - Note 0 < PROD [1 ..< n] by listRangeLHI_PROD_pos - LHS = PROD [n ..< n] - = PROD [] = 1 by listRangeLHI_EMPTY - RHS = PROD [1 ..< n] DIV PROD [1 ..< n] - = 1 by DIVMOD_ID, 0 < PROD [1 ..< n] - If m <> n, - Then m < n, or m <= n' by arithmetic - PROD [m ..< n] - = PROD [m .. n'] by listRangeLHI_to_INC - = PROD [1 .. n'] DIV PROD [1 .. m - 1] by listRangeINC_PROD, m <= n' - = PROD [1 .. n'] DIV PROD [1 .. m'] by m' = m - 1 - = PROD [1 ..< n] DIV PROD [1 ..< m] by listRangeLHI_to_INC -*) -val listRangeLHI_PROD = store_thm( - "listRangeLHI_PROD", - ``!m n. 0 < m /\ m <= n ==> (PROD [m ..< n] = PROD [1 ..< n] DIV PROD [1 ..< m])``, - rpt strip_tac >> - `m <> 0 /\ n <> 0` by decide_tac >> - `?n' m'. (n = n' + 1) /\ (m = m' + 1)` by metis_tac[num_CASES, ADD1] >> - Cases_on `m = n` >| [ - `0 < PROD [1 ..< n]` by rw[listRangeLHI_PROD_pos] >> - rfs[listRangeLHI_EMPTY, DIVMOD_ID], - `m <= n'` by decide_tac >> - `PROD [m ..< n] = PROD [m .. n']` by rw[listRangeLHI_to_INC] >> - `_ = PROD [1 .. n'] DIV PROD [1 .. m - 1]` by rw[GSYM listRangeINC_PROD] >> - `_ = PROD [1 .. n'] DIV PROD [1 .. m']` by rw[] >> - `_ = PROD [1 ..< n] DIV PROD [1 ..< m]` by rw[GSYM listRangeLHI_to_INC] >> - rw[] - ]); - -(* Theorem: 0 < n /\ m <= x /\ x divides n ==> MEM x [m ..< n + 1] *) -(* Proof: - Note the condition implies: - MEM x [m .. n] by listRangeINC_has_divisors - = MEM x [m ..< n + 1] by listRangeLHI_to_INC -*) -val listRangeLHI_has_divisors = store_thm( - "listRangeLHI_has_divisors", - ``!m n x. 0 < n /\ m <= x /\ x divides n ==> MEM x [m ..< n + 1]``, - metis_tac[listRangeINC_has_divisors, listRangeLHI_to_INC]); - -(* Theorem: [0 ..< n] = GENLIST I n *) -(* Proof: by listRangeINC_def *) -val listRangeLHI_0_n = store_thm( - "listRangeLHI_0_n", - ``!n. [0 ..< n] = GENLIST I n``, - rpt strip_tac >> - `(\i:num. i) = I` by rw[FUN_EQ_THM] >> - rw[listRangeLHI_def]); - -(* Theorem: MAP f [0 ..< n] = GENLIST f n *) -(* Proof: - MAP f [0 ..< n] - = MAP f (GENLIST I n) by listRangeLHI_0_n - = GENLIST (f o I) n by MAP_GENLIST - = GENLIST f n by I_THM -*) -val listRangeLHI_MAP = store_thm( - "listRangeLHI_MAP", - ``!f n. MAP f [0 ..< n] = GENLIST f n``, - rw[listRangeLHI_0_n, MAP_GENLIST]); - -(* Theorem: SUM (MAP f [0 ..< (SUC n)]) = f n + SUM (MAP f [0 ..< n]) *) -(* Proof: - SUM (MAP f [0 ..< (SUC n)]) - = SUM (MAP f (SNOC n [0 ..< n])) by listRangeLHI_SNOC - = SUM (SNOC (f n) (MAP f [0 ..< n])) by MAP_SNOC - = f n + SUM (MAP f [0 ..< n]) by SUM_SNOC -*) -val listRangeLHI_SUM_MAP = store_thm( - "listRangeLHI_SUM_MAP", - ``!f n. SUM (MAP f [0 ..< (SUC n)]) = f n + SUM (MAP f [0 ..< n])``, - rw[listRangeLHI_SNOC, MAP_SNOC, SUM_SNOC, ADD1]); - -(* Theorem: m <= j /\ j < n ==> [m ..< n] = [m ..< j] ++ j::[j+1 ..< n] *) -(* Proof: - Note j < n implies j <= n. - [m ..< n] - = [m ..< j] ++ [j ..< n] by listRangeLHI_APPEND, j <= n - = [m ..< j] ++ j::[j+1 ..< n] by listRangeLHI_CONS, j < n -*) -Theorem listRangeLHI_SPLIT: - !m n j. m <= j /\ j < n ==> [m ..< n] = [m ..< j] ++ j::[j+1 ..< n] -Proof - rpt strip_tac >> - `[m ..< n] = [m ..< j] ++ [j ..< n]` by simp[listRangeLHI_APPEND] >> - simp[listRangeLHI_CONS] -QED - -(* listRangeTheory.listRangeLHI_ALL_DISTINCT |- ALL_DISTINCT [lo ..< hi] *) - -(* Theorem: ALL_DISTINCT [m .. n] *) -(* Proof: - ALL_DISTINCT [m .. n] - <=> ALL_DISTINCT [m ..< n + 1] by listRangeLHI_to_INC - <=> T by listRangeLHI_ALL_DISTINCT -*) -Theorem listRangeINC_ALL_DISTINCT: - !m n. ALL_DISTINCT [m .. n] -Proof - metis_tac[listRangeLHI_to_INC, listRangeLHI_ALL_DISTINCT] -QED - -(* Theorem: m <= n ==> EVERY P [m - 1 .. n] <=> (P (m - 1) /\ EVERY P [m ..n]) *) -(* Proof: - EVERY P [m - 1 .. n] - <=> !x. m - 1 <= x /\ x <= n ==> P x by listRangeINC_EVERY - <=> !x. (m - 1 = x \/ m <= x) /\ x <= n ==> P x by arithmetic - <=> !x. (x = m - 1 ==> P x) /\ m <= x /\ x <= n ==> P x - by RIGHT_AND_OVER_OR, DISJ_IMP_THM - <=> P (m - 1) /\ EVERY P [m .. n] by listRangeINC_EVERY -*) -Theorem listRangeINC_EVERY_split_head: - !P m n. m <= n ==> (EVERY P [m - 1 .. n] <=> P (m - 1) /\ EVERY P [m ..n]) -Proof - rw[listRangeINC_EVERY] >> - `!x. m <= x + 1 <=> m - 1 = x \/ m <= x` by decide_tac >> - (rw[EQ_IMP_THM] >> metis_tac[]) -QED - -(* Theorem: m <= n ==> (EVERY P [m .. (n + 1)] <=> P (n + 1) /\ EVERY P [m .. n]) *) -(* Proof: - EVERY P [m .. (n + 1)] - <=> !x. m <= x /\ x <= n + 1 ==> P x by listRangeINC_EVERY - <=> !x. m <= x /\ (x <= n \/ x = n + 1) ==> P x by arithmetic - <=> !x. m <= x /\ x <= n ==> P x /\ P (n + 1) by LEFT_AND_OVER_OR, DISJ_IMP_THM - <=> P (n + 1) /\ EVERY P [m .. n] by listRangeINC_EVERY -*) -Theorem listRangeINC_EVERY_split_last: - !P m n. m <= n ==> (EVERY P [m .. (n + 1)] <=> P (n + 1) /\ EVERY P [m .. n]) -Proof - rw[listRangeINC_EVERY] >> - `!x. x <= n + 1 <=> x <= n \/ x = n + 1` by decide_tac >> - metis_tac[] -QED - -(* Theorem: m <= n ==> (EVERY P [m .. n] <=> P n /\ EVERY P [m ..< n]) *) -(* Proof: - EVERY P [m .. n] - <=> !x. m <= x /\ x <= n ==> P x by listRangeINC_EVERY - <=> !x. m <= x /\ (x < n \/ x = n) ==> P x by arithmetic - <=> !x. m <= x /\ x < n ==> P x /\ P n by LEFT_AND_OVER_OR, DISJ_IMP_THM - <=> P n /\ EVERY P [m ..< n] by listRangeLHI_EVERY -*) -Theorem listRangeINC_EVERY_less_last: - !P m n. m <= n ==> (EVERY P [m .. n] <=> P n /\ EVERY P [m ..< n]) -Proof - rw[listRangeINC_EVERY, listRangeLHI_EVERY] >> - `!x. x <= n <=> x < n \/ x = n` by decide_tac >> - metis_tac[] -QED - -(* Theorem: m < n /\ P m /\ ~P n ==> - ?k. m <= k /\ k < n /\ EVERY P [m .. k] /\ ~P (SUC k) *) -(* Proof: - m < n /\ P m /\ ~P n - ==> ?k. m <= k /\ k < m /\ - (!j. m <= j /\ j <= k ==> P j) /\ ~P (SUC k) by every_range_span_max - ==> ?k. m <= k /\ k < m /\ - EVERY P [m .. k] /\ ~P (SUC k) by listRangeINC_EVERY -*) -Theorem listRangeINC_EVERY_span_max: - !P m n. m < n /\ P m /\ ~P n ==> - ?k. m <= k /\ k < n /\ EVERY P [m .. k] /\ ~P (SUC k) -Proof - simp[listRangeINC_EVERY, every_range_span_max] -QED - -(* Theorem: m < n /\ ~P m /\ P n ==> - ?k. m < k /\ k <= n /\ EVERY P [k .. n] /\ ~P (PRE k) *) -(* Proof: - m < n /\ P m /\ ~P n - ==> ?k. m < k /\ k <= n /\ - (!j. k <= j /\ j <= n ==> P j) /\ ~P (PRE k) by every_range_span_min - ==> ?k. m < k /\ k <= n /\ - EVERY P [k .. n] /\ ~P (PRE k) by listRangeINC_EVERY -*) -Theorem listRangeINC_EVERY_span_min: - !P m n. m < n /\ ~P m /\ P n ==> - ?k. m < k /\ k <= n /\ EVERY P [k .. n] /\ ~P (PRE k) -Proof - simp[listRangeINC_EVERY, every_range_span_min] -QED - -(* ------------------------------------------------------------------------- *) -(* List Summation and Product *) -(* ------------------------------------------------------------------------- *) - -(* -> numpairTheory.tri_def; -val it = |- tri 0 = 0 /\ !n. tri (SUC n) = SUC n + tri n: thm -*) - -(* Theorem: SUM [1 .. n] = tri n *) -(* Proof: - By induction on n, - Base: SUM [1 .. 0] = tri 0 - SUM [1 .. 0] - = SUM [] by listRangeINC_EMPTY - = 0 by SUM_NIL - = tri 0 by tri_def - Step: SUM [1 .. n] = tri n ==> SUM [1 .. SUC n] = tri (SUC n) - SUM [1 .. SUC n] - = SUM (SNOC (SUC n) [1 .. n]) by listRangeINC_SNOC, 1 < n - = SUM [1 .. n] + (SUC n) by SUM_SNOC - = tri n + (SUC n) by induction hypothesis - = tri (SUC n) by tri_def -*) -val sum_1_to_n_eq_tri_n = store_thm( - "sum_1_to_n_eq_tri_n", - ``!n. SUM [1 .. n] = tri n``, - Induct >- - rw[listRangeINC_EMPTY, SUM_NIL, numpairTheory.tri_def] >> - rw[listRangeINC_SNOC, ADD1, SUM_SNOC, numpairTheory.tri_def]); - -(* Theorem: SUM [1 .. n] = HALF (n * (n + 1)) *) -(* Proof: - SUM [1 .. n] - = tri n by sum_1_to_n_eq_tri_n - = HALF (n * (n + 1)) by tri_formula -*) -val sum_1_to_n_eqn = store_thm( - "sum_1_to_n_eqn", - ``!n. SUM [1 .. n] = HALF (n * (n + 1))``, - rw[sum_1_to_n_eq_tri_n, numpairTheory.tri_formula]); - -(* Theorem: 2 * SUM [1 .. n] = n * (n + 1) *) -(* Proof: - Note EVEN (n * (n + 1)) by EVEN_PARTNERS - or 2 divides (n * (n + 1)) by EVEN_ALT - Thus n * (n + 1) - = ((n * (n + 1)) DIV 2) * 2 by DIV_MULT_EQ - = (SUM [1 .. n]) * 2 by sum_1_to_n_eqn - = 2 * SUM [1 .. n] by MULT_COMM -*) -val sum_1_to_n_double = store_thm( - "sum_1_to_n_double", - ``!n. 2 * SUM [1 .. n] = n * (n + 1)``, - rpt strip_tac >> - `2 divides (n * (n + 1))` by rw[EVEN_PARTNERS, GSYM EVEN_ALT] >> - metis_tac[sum_1_to_n_eqn, DIV_MULT_EQ, MULT_COMM, DECIDE``0 < 2``]); - -(* Theorem: PROD [1 .. n] = FACT n *) -(* Proof: - By induction on n, - Base: PROD [1 .. 0] = FACT 0 - PROD [1 .. 0] - = PROD [] by listRangeINC_EMPTY - = 1 by PROD_NIL - = FACT 0 by FACT - Step: PROD [1 .. n] = FACT n ==> PROD [1 .. SUC n] = FACT (SUC n) - PROD [1 .. SUC n] = FACT (SUC n) - = PROD (SNOC (SUC n) [1 .. n]) by listRangeINC_SNOC, 1 < n - = PROD [1 .. n] * (SUC n) by PROD_SNOC - = (FACT n) * (SUC n) by induction hypothesis - = FACT (SUC n) by FACT -*) -val prod_1_to_n_eq_fact_n = store_thm( - "prod_1_to_n_eq_fact_n", - ``!n. PROD [1 .. n] = FACT n``, - Induct >- - rw[listRangeINC_EMPTY, PROD_NIL, FACT] >> - rw[listRangeINC_SNOC, ADD1, PROD_SNOC, FACT]); - -(* This is numerical version of: -poly_cyclic_cofactor |- !r. Ring r /\ #1 <> #0 ==> !n. unity n = unity 1 * cyclic n -*) -(* Theorem: (t ** n - 1 = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n])) *) -(* Proof: - Let f = (\j. t ** j). - By induction on n. - Base: t ** 0 - 1 = (t - 1) * SUM (MAP f [0 ..< 0]) - LHS = t ** 0 - 1 = 0 by EXP_0 - RHS = (t - 1) * SUM (MAP f [0 ..< 0]) - = (t - 1) * SUM [] by listRangeLHI_EMPTY - = (t - 1) * 0 = 0 by SUM - Step: t ** n - 1 = (t - 1) * SUM (MAP f [0 ..< n]) ==> - t ** SUC n - 1 = (t - 1) * SUM (MAP f [0 ..< SUC n]) - If t = 0, - LHS = 0 ** SUC n - 1 = 0 by EXP_0 - RHS = (0 - 1) * SUM (MAP f [0 ..< SUC n]) - = 0 * SUM (MAP f [0 ..< SUC n]) by integer subtraction - = 0 = LHS - If t <> 0, - Then 0 < t ** n by EXP_POS - or 1 <= t ** n by arithmetic - so (t ** n - 1) + (t * t ** n - t ** n) = t * t ** n - 1 - (t - 1) * SUM (MAP (\j. t ** j) [0 ..< (SUC n)]) - = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n + 1]) by ADD1 - = (t - 1) * SUM (MAP (\j. t ** j) (SNOC n [0 ..< n])) by listRangeLHI_SNOC - = (t - 1) * SUM (SNOC (t ** n) (MAP f [0 ..< n])) by MAP_SNOC - = (t - 1) * (SUM (MAP f [0 ..< n]) + t ** n) by SUM_SNOC - = (t - 1) * SUM (MAP f [0 ..< n]) + (t - 1) * t ** n by RIGHT_ADD_DISTRIB - = (t ** n - 1) + (t - 1) * t ** n by induction hypothesis - = t ** SUC n - 1 by EXP -*) -val power_predecessor_eqn = store_thm( - "power_predecessor_eqn", - ``!t n. t ** n - 1 = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n])``, - rpt strip_tac >> - qabbrev_tac `f = \j. t ** j` >> - Induct_on `n` >- - rw[EXP_0, Abbr`f`] >> - Cases_on `t = 0` >- - rw[ZERO_EXP, Abbr`f`] >> - `(t ** n - 1) + (t * t ** n - t ** n) = t * t ** n - 1` by - (`0 < t` by decide_tac >> - `0 < t ** n` by rw[EXP_POS] >> - `1 <= t ** n` by decide_tac >> - `t ** n <= t * t ** n` by rw[] >> - decide_tac) >> - `(t - 1) * SUM (MAP f [0 ..< (SUC n)]) = (t - 1) * SUM (MAP f [0 ..< n + 1])` by rw[ADD1] >> - `_ = (t - 1) * SUM (MAP f (SNOC n [0 ..< n]))` by rw[listRangeLHI_SNOC] >> - `_ = (t - 1) * SUM (SNOC (t ** n) (MAP f [0 ..< n]))` by rw[MAP_SNOC, Abbr`f`] >> - `_ = (t - 1) * (SUM (MAP f [0 ..< n]) + t ** n)` by rw[SUM_SNOC] >> - `_ = (t - 1) * SUM (MAP f [0 ..< n]) + (t - 1) * t ** n` by rw[RIGHT_ADD_DISTRIB] >> - `_ = (t ** n - 1) + (t - 1) * t ** n` by rw[] >> - `_ = (t ** n - 1) + (t * t ** n - t ** n)` by rw[LEFT_SUB_DISTRIB] >> - `_ = t * t ** n - 1` by rw[] >> - `_ = t ** SUC n - 1 ` by rw[GSYM EXP] >> - rw[]); - -(* Above is the formal proof of the following observation for any base: - 9 = 9 * 1 - 99 = 9 * 11 - 999 = 9 * 111 - 9999 = 9 * 1111 - 99999 = 8 * 11111 - etc. - - This asserts: - (t ** n - 1) = (t - 1) * (1 + t + t ** 2 + ... + t ** (n-1)) - or 1 + t + t ** 2 + ... + t ** (n - 1) = (t ** n - 1) DIV (t - 1), - which is the sum of the geometric series. -*) - -(* Theorem: 1 < t ==> (SUM (MAP (\j. t ** j) [0 ..< n]) = (t ** n - 1) DIV (t - 1)) *) -(* Proof: - Note 0 < t - 1 by 1 < t - Let s = SUM (MAP (\j. t ** j) [0 ..< n]). - Then (t ** n - 1) = (t - 1) * s by power_predecessor_eqn - Thus s = (t ** n - 1) DIV (t - 1) by MULT_TO_DIV, 0 < t - 1 -*) -val geometric_sum_eqn = store_thm( - "geometric_sum_eqn", - ``!t n. 1 < t ==> (SUM (MAP (\j. t ** j) [0 ..< n]) = (t ** n - 1) DIV (t - 1))``, - rpt strip_tac >> - `0 < t - 1` by decide_tac >> - rw_tac std_ss[power_predecessor_eqn, MULT_TO_DIV]); - -(* Theorem: 1 < t ==> (SUM (MAP (\j. t ** j) [0 .. n]) = (t ** (n + 1) - 1) DIV (t - 1)) *) -(* Proof: - SUM (MAP (\j. t ** j) [0 .. n]) - = SUM (MAP (\j. t ** j) [0 ..< n + 1]) by listRangeLHI_to_INC - = (t ** (n + 1) - 1) DIV (t - 1) by geometric_sum_eqn -*) -val geometric_sum_eqn_alt = store_thm( - "geometric_sum_eqn_alt", - ``!t n. 1 < t ==> (SUM (MAP (\j. t ** j) [0 .. n]) = (t ** (n + 1) - 1) DIV (t - 1))``, - rw_tac std_ss[GSYM listRangeLHI_to_INC, geometric_sum_eqn]); - -(* Theorem: SUM [1 ..< n] = HALF (n * (n - 1)) *) -(* Proof: - If n = 0, - LHS = SUM [1 ..< 0] - = SUM [] = 0 by listRangeLHI_EMPTY - RHS = HALF (0 * (0 - 1)) - = 0 = LHS by arithmetic - If n <> 0, - Then n = (n - 1) + 1 by arithmetic, n <> 0 - SUM [1 ..< n] - = SUM [1 .. n - 1] by listRangeLHI_to_INC - = HALF ((n - 1) * (n - 1 + 1)) by sum_1_to_n_eqn - = HALF (n * (n - 1)) by arithmetic -*) -val arithmetic_sum_eqn = store_thm( - "arithmetic_sum_eqn", - ``!n. SUM [1 ..< n] = HALF (n * (n - 1))``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[listRangeLHI_EMPTY] >> - `n = (n - 1) + 1` by decide_tac >> - `SUM [1 ..< n] = SUM [1 .. n - 1]` by rw[GSYM listRangeLHI_to_INC] >> - `_ = HALF ((n - 1) * (n - 1 + 1))` by rw[sum_1_to_n_eqn] >> - `_ = HALF (n * (n - 1))` by rw[] >> - rw[]); - -(* Theorem alias *) -val arithmetic_sum_eqn_alt = save_thm("arithmetic_sum_eqn_alt", sum_1_to_n_eqn); -(* val arithmetic_sum_eqn_alt = |- !n. SUM [1 .. n] = HALF (n * (n + 1)): thm *) - -(* Theorem: SUM (GENLIST (\j. f (n - j)) n) = SUM (MAP f [1 .. n]) *) -(* Proof: - SUM (GENLIST (\j. f (n - j)) n) - = SUM (REVERSE (GENLIST (\j. f (n - j)) n)) by SUM_REVERSE - = SUM (GENLIST (\j. f (n - (PRE n - j))) n) by REVERSE_GENLIST - = SUM (GENLIST (\j. f (1 + j)) n) by LIST_EQ, SUB_SUB - = SUM (GENLIST (f o SUC) n) by FUN_EQ_THM - = SUM (MAP f [1 .. n]) by listRangeINC_MAP -*) -val SUM_GENLIST_REVERSE = store_thm( - "SUM_GENLIST_REVERSE", - ``!f n. SUM (GENLIST (\j. f (n - j)) n) = SUM (MAP f [1 .. n])``, - rpt strip_tac >> - `GENLIST (\j. f (n - (PRE n - j))) n = GENLIST (f o SUC) n` by - (irule LIST_EQ >> - rw[] >> - `n + x - PRE n = SUC x` by decide_tac >> - simp[]) >> - qabbrev_tac `g = \j. f (n - j)` >> - `SUM (GENLIST g n) = SUM (REVERSE (GENLIST g n))` by rw[SUM_REVERSE] >> - `_ = SUM (GENLIST (\j. g (PRE n - j)) n)` by rw[REVERSE_GENLIST] >> - `_ = SUM (GENLIST (f o SUC) n)` by rw[Abbr`g`] >> - `_ = SUM (MAP f [1 .. n])` by rw[listRangeINC_MAP] >> - decide_tac); -(* Note: locate here due to use of listRangeINC_MAP *) - -(* Theorem: SIGMA f (count n) = SUM (MAP f [0 ..< n]) *) -(* Proof: - SIGMA f (count n) - = SUM (GENLIST f n) by SUM_GENLIST - = SUM (MAP f [0 ..< n]) by listRangeLHI_MAP -*) -Theorem SUM_IMAGE_count: - !f n. SIGMA f (count n) = SUM (MAP f [0 ..< n]) -Proof - simp[SUM_GENLIST, listRangeLHI_MAP] -QED -(* Note: locate here due to use of listRangeINC_MAP *) - -(* Theorem: SIGMA f (count (SUC n)) = SUM (MAP f [0 .. n]) *) -(* Proof: - SIGMA f (count (SUC n)) - = SUM (GENLIST f (SUC n)) by SUM_GENLIST - = SUM (MAP f [0 ..< (SUC n)]) by SUM_IMAGE_count - = SUM (MAP f [0 .. n]) by listRangeINC_to_LHI -*) -Theorem SUM_IMAGE_upto: - !f n. SIGMA f (count (SUC n)) = SUM (MAP f [0 .. n]) -Proof - simp[SUM_GENLIST, SUM_IMAGE_count, listRangeINC_to_LHI] -QED - -(* ------------------------------------------------------------------------- *) -(* MAP of function with 3 list arguments *) -(* ------------------------------------------------------------------------- *) - -(* Define MAP3 similar to MAP2 in listTheory. *) -val dDefine = Lib.with_flag (Defn.def_suffix, "_DEF") Define; -val MAP3_DEF = dDefine` - (MAP3 f (h1::t1) (h2::t2) (h3::t3) = f h1 h2 h3::MAP3 f t1 t2 t3) /\ - (MAP3 f x y z = [])`; -val _ = export_rewrites["MAP3_DEF"]; -val MAP3 = store_thm ("MAP3", -``(!f. MAP3 f [] [] [] = []) /\ - (!f h1 t1 h2 t2 h3 t3. MAP3 f (h1::t1) (h2::t2) (h3::t3) = f h1 h2 h3::MAP3 f t1 t2 t3)``, - METIS_TAC[MAP3_DEF]); - -(* -LENGTH_MAP |- !l f. LENGTH (MAP f l) = LENGTH l -LENGTH_MAP2 |- !xs ys. LENGTH (MAP2 f xs ys) = MIN (LENGTH xs) (LENGTH ys) -*) - -(* Theorem: LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) *) -(* Proof: - By induction on lx. - Base: !ly lz f. LENGTH (MAP3 f [] ly lz) = MIN (MIN (LENGTH []) (LENGTH ly)) (LENGTH lz) - LHS = LENGTH [] = 0 by MAP3, LENGTH - RHS = MIN (MIN 0 (LENGTH ly)) (LENGTH lz) by LENGTH - = MIN 0 (LENGTH lz) = 0 = LHS by MIN_DEF - Step: !ly lz f. LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !h ly lz f. LENGTH (MAP3 f (h::lx) ly lz) = MIN (MIN (LENGTH (h::lx)) (LENGTH ly)) (LENGTH lz) - If ly = [], - LHS = LENGTH (MAP3 f (h::lx) [] lz) = 0 by MAP3, LENGTH - RHS = MIN (MIN (LENGTH (h::lx)) (LENGTH [])) (LENGTH lz) - = MIN 0 (LENGTH lz) = 0 = LHS by MIN_DEF - Otherwise, ly = h'::t. - If lz = [], - LHS = LENGTH (MAP3 f (h::lx) (h'::t) []) = 0 by MAP3, LENGTH - RHS = MIN (MIN (LENGTH (h::lx)) (LENGTH (h'::t))) (LENGTH []) - = 0 = LHS by MIN_DEF - Otherwise, lz = h''::t'. - LHS = LENGTH (MAP3 f (h::lx) (h'::t) (h''::t')) - = LENGTH (f h' h''::MAP3 lx t t'') by MAP3 - = SUC (LENGTH MAP3 lx t t'') by LENGTH - = SUC (MIN (MIN (LENGTH lx) (LENGTH t)) (LENGTH t'')) by induction hypothesis - RHS = MIN (MIN (LENGTH (h::lx)) (LENGTH (h'::t))) (LENGTH (h''::t')) - = MIN (MIN (SUC (LENGTH lx)) (SUC (LENGTH t))) (SUC (LENGTH t')) by LENGTH - = MIN (SUC (MIN (LENGTH lx) (LENGTH t))) (SUC (LESS_TWICE t')) by MIN_DEF - = SUC (MIN (MIN (LENGTH lx) (LENGTH t)) (LENGTH t'')) = LHS by MIN_DEF -*) -val LENGTH_MAP3 = store_thm( - "LENGTH_MAP3", - ``!lx ly lz f. LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz)``, - Induct_on `lx` >- - rw[] >> - rpt strip_tac >> - Cases_on `ly` >- - rw[] >> - Cases_on `lz` >- - rw[] >> - rw[MIN_DEF]); - -(* -EL_MAP |- !n l. n < LENGTH l ==> !f. EL n (MAP f l) = f (EL n l) -EL_MAP2 |- !ts tt n. n < MIN (LENGTH ts) (LENGTH tt) ==> (EL n (MAP2 f ts tt) = f (EL n ts) (EL n tt)) -*) - -(* Theorem: n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz) *) -(* Proof: - By induction on n. - Base: !lx ly lz. 0 < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !f. EL 0 (MAP3 f lx ly lz) = f (EL 0 lx) (EL 0 ly) (EL 0 lz) - Note ?x tx. lx = x::tx by LENGTH_EQ_0, list_CASES - and ?y ty. ly = y::ty by LENGTH_EQ_0, list_CASES - and ?z tz. lz = z::tz by LENGTH_EQ_0, list_CASES - EL 0 (MAP3 f lx ly lz) - = EL 0 (MAP3 f (x::lx) (y::ty) (z::tz)) - = EL 0 (f x y z::MAP3 f tx ty tz) by MAP3 - = f x y z by EL - = f (EL 0 lx) (EL 0 ly) (EL 0 lz) by EL - Step: !lx ly lz. n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz) ==> - !lx ly lz. SUC n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !f. EL (SUC n) (MAP3 f lx ly lz) = f (EL (SUC n) lx) (EL (SUC n) ly) (EL (SUC n) lz) - Note ?x tx. lx = x::tx by LENGTH_EQ_0, list_CASES - and ?y ty. ly = y::ty by LENGTH_EQ_0, list_CASES - and ?z tz. lz = z::tz by LENGTH_EQ_0, list_CASES - Also n < LENGTH tx /\ n < LENGTH ty /\ n < LENGTH tz by LENGTH - Thus n < MIN (MIN (LENGTH tx) (LENGTH ty)) (LENGTH tz) by MIN_DEF - EL (SUC n) (MAP3 f lx ly lz) - = EL (SUC n) (MAP3 f (x::lx) (y::ty) (z::tz)) - = EL (SUC n) (f x y z::MAP3 f tx ty tz) by MAP3 - = EL n (MAP3 f tx ty tz) by EL - = f (EL n tx) (EL n ty) (EL n tz) by induction hypothesis - = f (EL (SUC n) lx) (EL (SUC n) ly) (EL (SUC n) lz) - by EL -*) -val EL_MAP3 = store_thm( - "EL_MAP3", - ``!lx ly lz n. n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> - !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz)``, - Induct_on `n` >| [ - rw[] >> - `?x tx. lx = x::tx` by metis_tac[LENGTH_EQ_0, list_CASES, NOT_ZERO_LT_ZERO] >> - `?y ty. ly = y::ty` by metis_tac[LENGTH_EQ_0, list_CASES, NOT_ZERO_LT_ZERO] >> - `?z tz. lz = z::tz` by metis_tac[LENGTH_EQ_0, list_CASES, NOT_ZERO_LT_ZERO] >> - rw[], - rw[] >> - `!a. SUC n < a ==> a <> 0` by decide_tac >> - `?x tx. lx = x::tx` by metis_tac[LENGTH_EQ_0, list_CASES] >> - `?y ty. ly = y::ty` by metis_tac[LENGTH_EQ_0, list_CASES] >> - `?z tz. lz = z::tz` by metis_tac[LENGTH_EQ_0, list_CASES] >> - `n < LENGTH tx /\ n < LENGTH ty /\ n < LENGTH tz` by fs[] >> - rw[] - ]); - -(* -MEM_MAP |- !l f x. MEM x (MAP f l) <=> ?y. x = f y /\ MEM y l -*) - -(* Theorem: MEM x (MAP2 f l1 l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 l2 *) -(* Proof: - By induction on l1. - Base: !l2. MEM x (MAP2 f [] l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 [] /\ MEM y2 l2 - Note MAP2 f [] l2 = [] by MAP2_DEF - and MEM x [] = F, hence true by MEM - Step: !l2. MEM x (MAP2 f l1 l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 l2 ==> - !h l2. MEM x (MAP2 f (h::l1) l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 (h::l1) /\ MEM y2 l2 - If l2 = [], - Then MEM x (MAP2 f (h::l1) []) = F, hence true by MEM - Otherwise, l2 = h'::t, - to show: MEM x (MAP2 f (h::l1) (h'::t)) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 (h::l1) /\ MEM y2 (h'::t) - Note MAP2 f (h::l1) (h'::t) - = (f h h')::MAP2 f l1 t by MAP2 - Thus x = f h h' or MEM x (MAP2 f l1 t) by MEM - If x = f h h', - Take y1 = h, y2 = h', and the result follows by MEM - If MEM x (MAP2 f l1 t) - Then ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 t by induction hypothesis - Take this y1 and y2, the result follows by MEM -*) -val MEM_MAP2 = store_thm( - "MEM_MAP2", - ``!f x l1 l2. MEM x (MAP2 f l1 l2) ==> ?y1 y2. (x = f y1 y2) /\ MEM y1 l1 /\ MEM y2 l2``, - ntac 2 strip_tac >> - Induct_on `l1` >- - rw[] >> - rpt strip_tac >> - Cases_on `l2` >- - fs[] >> - fs[] >- - metis_tac[] >> - metis_tac[MEM]); - -(* Theorem: MEM x (MAP3 f l1 l2 l3) ==> ?y1 y2 y3. (x = f y1 y2 y3) /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3 *) -(* Proof: - By induction on l1. - Base: !l2 l3. MEM x (MAP3 f [] l2 l3) ==> ... - Note MAP3 f [] l2 l3 = [], and MEM x [] = F, hence true. - Step: !l2 l3. MEM x (MAP3 f l1 l2 l3) ==> - ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3 ==> - !h l2 l3. MEM x (MAP3 f (h::l1) l2 l3) ==> - ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 (h::l1) /\ MEM y2 l2 /\ MEM y3 l3 - If l2 = [], - Then MEM x (MAP3 f (h::l1) [] l3) = MEM x [] = F, hence true by MAP3_DEF - Otherwise, l2 = h'::t, - to show: MEM x (MAP3 f (h::l1) (h'::t) l3) ==> - ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 (h::l1) /\ MEM y2 (h'::t) /\ MEM y3 l3 - If l3 = [], - Then MEM x (MAP3 f (h::l1) l2 []) = MEM x [] = F, hence true by MAP3_DEF - Otherwise, l3 = h''::t', - to show: MEM x (MAP3 f (h::l1) (h'::t) (h''::t')) ==> - ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 (h::l1) /\ MEM y2 (h'::t) /\ MEM y3 (h''::t') - - Note MAP3 f (h::l1) (h'::t) (h''::t') - = (f h h' h'')::MAP3 f l1 t t' by MAP3 - Thus x = f h h' h'' or MEM x (MAP3 f l1 t t') by MEM - If x = f h h' h'', - Take y1 = h, y2 = h', y3 = h'' and the result follows by MEM - If MEM x (MAP3 f l1 t t') - Then ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 t /\ MEM y2 l2 /\ MEM y3 t' - by induction hypothesis - Take this y1, y2 and y3, the result follows by MEM -*) -val MEM_MAP3 = store_thm( - "MEM_MAP3", - ``!f x l1 l2 l3. MEM x (MAP3 f l1 l2 l3) ==> - ?y1 y2 y3. (x = f y1 y2 y3) /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3``, - ntac 2 strip_tac >> - Induct_on `l1` >- - rw[] >> - rpt strip_tac >> - Cases_on `l2` >- - fs[] >> - Cases_on `l3` >- - fs[] >> - fs[] >- - metis_tac[] >> - metis_tac[MEM]); - -(* Theorem: SUM (MAP (K c) ls) = c * LENGTH ls *) -(* Proof: - By induction on ls. - Base: !c. SUM (MAP (K c) []) = c * LENGTH [] - LHS = SUM (MAP (K c) []) - = SUM [] = 0 by MAP, SUM - RHS = c * LENGTH [] - = c * 0 = 0 = LHS by LENGTH - Step: !c. SUM (MAP (K c) ls) = c * LENGTH ls ==> - !h c. SUM (MAP (K c) (h::ls)) = c * LENGTH (h::ls) - SUM (MAP (K c) (h::ls)) - = SUM (c :: MAP (K c) ls) by MAP - = c + SUM (MAP (K c) ls) by SUM - = c + c * LENGTH ls by induction hypothesis - = c * (1 + LENGTH ls) by RIGHT_ADD_DISTRIB - = c * (SUC (LENGTH ls)) by ADD1 - = c * LENGTH (h::ls) by LENGTH -*) -val SUM_MAP_K = store_thm( - "SUM_MAP_K", - ``!ls c. SUM (MAP (K c) ls) = c * LENGTH ls``, - Induct >- - rw[] >> - rw[ADD1]); - -(* Theorem: a <= b ==> SUM (MAP (K a) ls) <= SUM (MAP (K b) ls) *) -(* Proof: - SUM (MAP (K a) ls) - = a * LENGTH ls by SUM_MAP_K - <= b * LENGTH ls by a <= b - = SUM (MAP (K b) ls) by SUM_MAP_K -*) -val SUM_MAP_K_LE = store_thm( - "SUM_MAP_K_LE", - ``!ls a b. a <= b ==> SUM (MAP (K a) ls) <= SUM (MAP (K b) ls)``, - rw[SUM_MAP_K]); - -(* Theorem: SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) *) -(* Proof: - By induction on lx. - Base: !ly c. SUM (MAP2 (\x y. c) [] ly) = c * LENGTH (MAP2 (\x y. c) [] ly) - LHS = SUM (MAP2 (\x y. c) [] ly) - = SUM [] = 0 by MAP2_DEF, SUM - RHS = c * LENGTH (MAP2 (\x y. c) [] ly) - = c * 0 = 0 = LHS by MAP2_DEF, LENGTH - Step: !ly c. SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) ==> - !h ly c. SUM (MAP2 (\x y. c) (h::lx) ly) = c * LENGTH (MAP2 (\x y. c) (h::lx) ly) - If ly = [], - to show: SUM (MAP2 (\x y. c) (h::lx) []) = c * LENGTH (MAP2 (\x y. c) (h::lx) []) - LHS = SUM (MAP2 (\x y. c) (h::lx) []) - = SUM [] = 0 by MAP2_DEF, SUM - RHS = c * LENGTH (MAP2 (\x y. c) (h::lx) []) - = c * 0 = 0 = LHS by MAP2_DEF, LENGTH - Otherwise, ly = h'::t, - to show: SUM (MAP2 (\x y. c) (h::lx) (h'::t)) = c * LENGTH (MAP2 (\x y. c) (h::lx) (h'::t)) - - SUM (MAP2 (\x y. c) (h::lx) (h'::t)) - = SUM (c :: MAP2 (\x y. c) lx t) by MAP2_DEF - = c + SUM (MAP2 (\x y. c) lx t) by SUM - = c + c * LENGTH (MAP2 (\x y. c) lx t) by induction hypothesis - = c * (1 + LENGTH (MAP2 (\x y. c) lx t) by RIGHT_ADD_DISTRIB - = c * (SUC (LENGTH (MAP2 (\x y. c) lx t)) by ADD1 - = c * LENGTH (MAP2 (\x y. c) (h::lx) (h'::t)) by LENGTH -*) -val SUM_MAP2_K = store_thm( - "SUM_MAP2_K", - ``!lx ly c. SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly)``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `ly` >- - rw[] >> - rw[ADD1, MIN_DEF]); - -(* Theorem: SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) *) -(* Proof: - By induction on lx. - Base: !ly lz c. SUM (MAP3 (\x y z. c) [] ly lz) = c * LENGTH (MAP3 (\x y z. c) [] ly lz) - LHS = SUM (MAP3 (\x y z. c) [] ly lz) - = SUM [] = 0 by MAP3_DEF, SUM - RHS = c * LENGTH (MAP3 (\x y z. c) [] ly lz) - = c * 0 = 0 = LHS by MAP3_DEF, LENGTH - Step: !ly lz c. SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) ==> - !h ly lz c. SUM (MAP3 (\x y z. c) (h::lx) ly lz) = c * LENGTH (MAP3 (\x y z. c) (h::lx) ly lz) - If ly = [], - to show: SUM (MAP3 (\x y z. c) (h::lx) [] lz) = c * LENGTH (MAP3 (\x y z. c) (h::lx) [] lz) - LHS = SUM (MAP3 (\x y z. c) (h::lx) [] lz) - = SUM [] = 0 by MAP3_DEF, SUM - RHS = c * LENGTH (MAP3 (\x y z. c) (h::lx) [] lz) - = c * 0 = 0 = LHS by MAP3_DEF, LENGTH - Otherwise, ly = h'::t, - to show: SUM (MAP3 (\x y z. c) (h::lx) (h'::t) lz) = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) lz) - If lz = [], - to show: SUM (MAP3 (\x y z. c) (h::lx) (h'::t) []) = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) []) - LHS = SUM (MAP3 (\x y z. c) (h::lx) (h'::t) []) - = SUM [] = 0 by MAP3_DEF, SUM - RHS = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) []) - = c * 0 = 0 by MAP3_DEF, LENGTH - Otherwise, lz = h''::t', - to show: SUM (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) - SUM (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) - = SUM (c :: MAP3 (\x y z. c) lx t t') by MAP3_DEF - = c + SUM (MAP3 (\x y z. c) lx t t') by SUM - = c + c * LENGTH (MAP3 (\x y z. c) lx t t') by induction hypothesis - = c * (1 + LENGTH (MAP3 (\x y z. c) lx t t') by RIGHT_ADD_DISTRIB - = c * (SUC (LENGTH (MAP3 (\x y z. c) lx t t')) by ADD1 - = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) by LENGTH -*) -val SUM_MAP3_K = store_thm( - "SUM_MAP3_K", - ``!lx ly lz c. SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz)``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `ly` >- - rw[] >> - Cases_on `lz` >- - rw[] >> - rw[ADD1]); - -(* ------------------------------------------------------------------------- *) -(* Bounds on Lists *) -(* ------------------------------------------------------------------------- *) - -(* Overload non-decreasing functions with different arity. *) -val _ = overload_on("MONO", ``\f:num -> num. !x y. x <= y ==> f x <= f y``); -val _ = overload_on("MONO2", ``\f:num -> num -> num. !x1 y1 x2 y2. x1 <= x2 /\ y1 <= y2 ==> f x1 y1 <= f x2 y2``); -val _ = overload_on("MONO3", ``\f:num -> num -> num -> num. !x1 y1 z1 x2 y2 z2. x1 <= x2 /\ y1 <= y2 /\ z1 <= z2 ==> f x1 y1 z1 <= f x2 y2 z2``); - -(* Overload non-increasing functions with different arity. *) -val _ = overload_on("RMONO", ``\f:num -> num. !x y. x <= y ==> f y <= f x``); -val _ = overload_on("RMONO2", ``\f:num -> num -> num. !x1 y1 x2 y2. x1 <= x2 /\ y1 <= y2 ==> f x2 y2 <= f x1 y1``); -val _ = overload_on("RMONO3", ``\f:num -> num -> num -> num. !x1 y1 z1 x2 y2 z2. x1 <= x2 /\ y1 <= y2 /\ z1 <= z2 ==> f x2 y2 z2 <= f x1 y1 z1``); - - -(* Theorem: SUM ls <= (MAX_LIST ls) * LENGTH ls *) -(* Proof: - By induction on ls. - Base: SUM [] <= MAX_LIST [] * LENGTH [] - LHS = SUM [] = 0 by SUM - RHS = MAX_LIST [] * LENGTH [] - = 0 * 0 = 0 by MAX_LIST, LENGTH - Hence true. - Step: SUM ls <= MAX_LIST ls * LENGTH ls ==> - !h. SUM (h::ls) <= MAX_LIST (h::ls) * LENGTH (h::ls) - SUM (h::ls) - = h + SUM ls by SUM - <= h + MAX_LIST ls * LENGTH ls by induction hypothesis - <= MAX_LIST (h::ls) + MAX_LIST ls * LENGTH ls by MAX_LIST_PROPERTY - <= MAX_LIST (h::ls) + MAX_LIST (h::ls) * LENGTH ls by MAX_LIST_LE - = MAX_LIST (h::ls) * (1 + LENGTH ls) by LEFT_ADD_DISTRIB - = MAX_LIST (h::ls) * LENGTH (h::ls) by LENGTH -*) -val SUM_UPPER = store_thm( - "SUM_UPPER", - ``!ls. SUM ls <= (MAX_LIST ls) * LENGTH ls``, - Induct_on `ls` >- - rw[] >> - strip_tac >> - `SUM (h::ls) <= h + MAX_LIST ls * LENGTH ls` by rw[] >> - `h + MAX_LIST ls * LENGTH ls <= MAX_LIST (h::ls) + MAX_LIST ls * LENGTH ls` by rw[] >> - `MAX_LIST (h::ls) + MAX_LIST ls * LENGTH ls <= MAX_LIST (h::ls) + MAX_LIST (h::ls) * LENGTH ls` by rw[] >> - `MAX_LIST (h::ls) + MAX_LIST (h::ls) * LENGTH ls = MAX_LIST (h::ls) * (1 + LENGTH ls)` by rw[] >> - `_ = MAX_LIST (h::ls) * LENGTH (h::ls)` by rw[] >> - decide_tac); - -(* Theorem: (MIN_LIST ls) * LENGTH ls <= SUM ls *) -(* Proof: - By induction on ls. - Base: MIN_LIST [] * LENGTH [] <= SUM [] - LHS = (MIN_LIST []) * LENGTH [] = 0 by LENGTH - RHS = SUM [] = 0 by SUM - Hence true. - Step: MIN_LIST ls * LENGTH ls <= SUM ls ==> - !h. MIN_LIST (h::ls) * LENGTH (h::ls) <= SUM (h::ls) - If ls = [], - LHS = (MIN_LIST [h]) * LENGTH [h] - = h * 1 = h by MIN_LIST_def, LENGTH - RHS = SUM [h] = h by SUM - Hence true. - If ls <> [], - MIN_LIST (h::ls) * LENGTH (h::ls) - = (MIN h (MIN_LIST ls)) * (1 + LENGTH ls) by MIN_LIST_def, LENGTH - = (MIN h (MIN_LIST ls)) + (MIN h (MIN_LIST ls)) * LENGTH ls - by RIGHT_ADD_DISTRIB - <= h + (MIN_LIST ls) * LENGTH ls by MIN_IS_MIN - <= h + SUM ls by induction hypothesis - = SUM (h::ls) by SUM -*) -val SUM_LOWER = store_thm( - "SUM_LOWER", - ``!ls. (MIN_LIST ls) * LENGTH ls <= SUM ls``, - Induct_on `ls` >- - rw[] >> - strip_tac >> - Cases_on `ls = []` >- - rw[] >> - `MIN_LIST (h::ls) * LENGTH (h::ls) = (MIN h (MIN_LIST ls)) * (1 + LENGTH ls)` by rw[] >> - `_ = (MIN h (MIN_LIST ls)) + (MIN h (MIN_LIST ls)) * LENGTH ls` by rw[] >> - `(MIN h (MIN_LIST ls)) <= h` by rw[] >> - `(MIN h (MIN_LIST ls)) * LENGTH ls <= (MIN_LIST ls) * LENGTH ls` by rw[] >> - rw[]); - -(* Theorem: EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls) *) -(* Proof: - By induction on ls. - Base: EVERY (\x. f x <= g x) [] ==> SUM (MAP f []) <= SUM (MAP g []) - EVERY (\x. f x <= g x) [] = T by EVERY_DEF - SUM (MAP f []) - = SUM [] by MAP - = SUM (MAP g []) by MAP - Step: EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls) ==> - !h. EVERY (\x. f x <= g x) (h::ls) ==> SUM (MAP f (h::ls)) <= SUM (MAP g (h::ls)) - Note f h <= g h /\ - EVERY (\x. f x <= g x) ls by EVERY_DEF - SUM (MAP f (h::ls)) - = SUM (f h :: MAP f ls) by MAP - = f h + SUM (MAP f ls) by SUM - <= g h + SUM (MAP g ls) by above, induction hypothesis - = SUM (g h :: MAP g ls) by SUM - = SUM (MAP g (h::ls)) by MAP -*) -val SUM_MAP_LE = store_thm( - "SUM_MAP_LE", - ``!f g ls. EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls)``, - rpt strip_tac >> - Induct_on `ls` >> - rw[] >> - rw[] >> - fs[]); - -(* Theorem: EVERY (\x. f x < g x) ls /\ ls <> [] ==> SUM (MAP f ls) < SUM (MAP g ls) *) -(* Proof: - By induction on ls. - Base: EVERY (\x. f x <= g x) [] /\ [] <> [] ==> SUM (MAP f []) <= SUM (MAP g []) - True since [] <> [] = F. - Step: EVERY (\x. f x <= g x) ls ==> ls <> [] ==> SUM (MAP f ls) <= SUM (MAP g ls) ==> - !h. EVERY (\x. f x <= g x) (h::ls) ==> h::ls <> [] ==> SUM (MAP f (h::ls)) <= SUM (MAP g (h::ls)) - Note f h < g h /\ - EVERY (\x. f x < g x) ls by EVERY_DEF - - If ls = [], - SUM (MAP f [h]) - = SUM (f h) by MAP - = f h by SUM - < g h by above - = SUM (g h) by SUM - = SUM (MAP g [h]) by MAP - - If ls <> [], - SUM (MAP f (h::ls)) - = SUM (f h :: MAP f ls) by MAP - = f h + SUM (MAP f ls) by SUM - < g h + SUM (MAP g ls) by induction hypothesis - = SUM (g h :: MAP g ls) by SUM - = SUM (MAP g (h::ls)) by MAP -*) -val SUM_MAP_LT = store_thm( - "SUM_MAP_LT", - ``!f g ls. EVERY (\x. f x < g x) ls /\ ls <> [] ==> SUM (MAP f ls) < SUM (MAP g ls)``, - rpt strip_tac >> - Induct_on `ls` >> - rw[] >> - rw[] >> - (Cases_on `ls = []` >> fs[])); - -(* -MAX_LIST_PROPERTY |- !l x. MEM x l ==> x <= MAX_LIST l -MIN_LIST_PROPERTY |- !l. l <> [] ==> !x. MEM x l ==> MIN_LIST l <= x -*) - -(* Theorem: MONO f ==> !ls e. MEM e (MAP f ls) ==> e <= f (MAX_LIST ls) *) -(* Proof: - Note ?y. (e = f y) /\ MEM y ls by MEM_MAP - and y <= MAX_LIST ls by MAX_LIST_PROPERTY - Thus f y <= f (MAX_LIST ls) by given - or e <= f (MAX_LIST ls) by e = f y -*) -val MEM_MAP_UPPER = store_thm( - "MEM_MAP_UPPER", - ``!f. MONO f ==> !ls e. MEM e (MAP f ls) ==> e <= f (MAX_LIST ls)``, - rpt strip_tac >> - `?y. (e = f y) /\ MEM y ls` by rw[GSYM MEM_MAP] >> - `y <= MAX_LIST ls` by rw[MAX_LIST_PROPERTY] >> - rw[]); - -(* Theorem: MONO2 f ==> !lx ly e. MEM e (MAP2 f lx ly) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) *) -(* Proof: - Note ?ex ey. (e = f ex ey) /\ - MEM ex lx /\ MEM ey ly by MEM_MAP2 - and ex <= MAX_LIST lx by MAX_LIST_PROPERTY - and ey <= MAX_LIST ly by MAX_LIST_PROPERTY - The result follows by the non-decreasing condition on f. -*) -val MEM_MAP2_UPPER = store_thm( - "MEM_MAP2_UPPER", - ``!f. MONO2 f ==> !lx ly e. MEM e (MAP2 f lx ly) ==> e <= f (MAX_LIST lx) (MAX_LIST ly)``, - metis_tac[MEM_MAP2, MAX_LIST_PROPERTY]); - -(* Theorem: MONO3 f ==> - !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) *) -(* Proof: - Note ?ex ey ez. (e = f ex ey ez) /\ - MEM ex lx /\ MEM ey ly /\ MEM ez lz by MEM_MAP3 - and ex <= MAX_LIST lx by MAX_LIST_PROPERTY - and ey <= MAX_LIST ly by MAX_LIST_PROPERTY - and ez <= MAX_LIST lz by MAX_LIST_PROPERTY - The result follows by the non-decreasing condition on f. -*) -val MEM_MAP3_UPPER = store_thm( - "MEM_MAP3_UPPER", - ``!f. MONO3 f ==> - !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz)``, - metis_tac[MEM_MAP3, MAX_LIST_PROPERTY]); - -(* Theorem: MONO f ==> !ls e. MEM e (MAP f ls) ==> f (MIN_LIST ls) <= e *) -(* Proof: - Note ?y. (e = f y) /\ MEM y ls by MEM_MAP - and ls <> [] by MEM, MEM y ls - then MIN_LIST ls <= y by MIN_LIST_PROPERTY, ls <> [] - Thus f (MIN_LIST ls) <= f y by given - or f (MIN_LIST ls) <= e by e = f y -*) -val MEM_MAP_LOWER = store_thm( - "MEM_MAP_LOWER", - ``!f. MONO f ==> !ls e. MEM e (MAP f ls) ==> f (MIN_LIST ls) <= e``, - rpt strip_tac >> - `?y. (e = f y) /\ MEM y ls` by rw[GSYM MEM_MAP] >> - `ls <> []` by metis_tac[MEM] >> - `MIN_LIST ls <= y` by rw[MIN_LIST_PROPERTY] >> - rw[]); - -(* Theorem: MONO2 f ==> - !lx ly e. MEM e (MAP2 f lx ly) ==> f (MIN_LIST lx) (MIN_LIST ly) <= e *) -(* Proof: - Note ?ex ey. (e = f ex ey) /\ - MEM ex lx /\ MEM ey ly by MEM_MAP2 - and lx <> [] /\ ly <> [] by MEM - and MIN_LIST lx <= ex by MIN_LIST_PROPERTY - and MIN_LIST ly <= ey by MIN_LIST_PROPERTY - The result follows by the non-decreasing condition on f. -*) -val MEM_MAP2_LOWER = store_thm( - "MEM_MAP2_LOWER", - ``!f. MONO2 f ==> - !lx ly e. MEM e (MAP2 f lx ly) ==> f (MIN_LIST lx) (MIN_LIST ly) <= e``, - metis_tac[MEM_MAP2, MEM, MIN_LIST_PROPERTY]); - -(* Theorem: MONO3 f ==> - !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> f (MIN_LIST lx) (MIN_LIST ly) (MIN_LIST lz) <= e *) -(* Proof: - Note ?ex ey ez. (e = f ex ey ez) /\ - MEM ex lx /\ MEM ey ly /\ MEM ez lz by MEM_MAP3 - and lx <> [] /\ ly <> [] /\ lz <> [] by MEM - and MIN_LIST lx <= ex by MIN_LIST_PROPERTY - and MIN_LIST ly <= ey by MIN_LIST_PROPERTY - and MIN_LIST lz <= ez by MIN_LIST_PROPERTY - The result follows by the non-decreasing condition on f. -*) -val MEM_MAP3_LOWER = store_thm( - "MEM_MAP3_LOWER", - ``!f. MONO3 f ==> - !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> f (MIN_LIST lx) (MIN_LIST ly) (MIN_LIST lz) <= e``, - rpt strip_tac >> - `?ex ey ez. (e = f ex ey ez) /\ MEM ex lx /\ MEM ey ly /\ MEM ez lz` by rw[MEM_MAP3] >> - `lx <> [] /\ ly <> [] /\ lz <> []` by metis_tac[MEM] >> - rw[MIN_LIST_PROPERTY]); - -(* Theorem: (!x. f x <= g x) ==> !ls. MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls) *) -(* Proof: - By induction on ls. - Base: MAX_LIST (MAP f []) <= MAX_LIST (MAP g []) - LHS = MAX_LIST (MAP f []) = MAX_LIST [] by MAP - RHS = MAX_LIST (MAP g []) = MAX_LIST [] by MAP - Hence true. - Step: MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls) ==> - !h. MAX_LIST (MAP f (h::ls)) <= MAX_LIST (MAP g (h::ls)) - MAX_LIST (MAP f (h::ls)) - = MAX_LIST (f h::MAP f ls) by MAP - = MAX (f h) (MAX_LIST (MAP f ls)) by MAX_LIST_def - <= MAX (f h) (MAX_LIST (MAP g ls)) by induction hypothesis - <= MAX (g h) (MAX_LIST (MAP g ls)) by properties of f, g - = MAX_LIST (g h::MAP g ls) by MAX_LIST_def - = MAX_LIST (MAP g (h::ls)) by MAP -*) -val MAX_LIST_MAP_LE = store_thm( - "MAX_LIST_MAP_LE", - ``!f g. (!x. f x <= g x) ==> !ls. MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls)``, - rpt strip_tac >> - Induct_on `ls` >- - rw[] >> - rw[]); - -(* Theorem: (!x. f x <= g x) ==> !ls. MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls) *) -(* Proof: - By induction on ls. - Base: MIN_LIST (MAP f []) <= MIN_LIST (MAP g []) - LHS = MIN_LIST (MAP f []) = MIN_LIST [] by MAP - RHS = MIN_LIST (MAP g []) = MIN_LIST [] by MAP - Hence true. - Step: MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls) ==> - !h. MIN_LIST (MAP f (h::ls)) <= MIN_LIST (MAP g (h::ls)) - If ls = [], - MIN_LIST (MAP f [h]) - = MIN_LIST [f h] by MAP - = f h by MIN_LIST_def - <= g h by properties of f, g - = MIN_LIST [g h] by MIN_LIST_def - = MIN_LIST (MAP g [h]) by MAP - Otherwise ls <> [], - MIN_LIST (MAP f (h::ls)) - = MIN_LIST (f h::MAP f ls) by MAP - = MIN (f h) (MIN_LIST (MAP f ls)) by MIN_LIST_def - <= MIN (g h) (MIN_LIST (MAP g ls)) by MIN_LE_PAIR, induction hypothesis - = MIN_LIST (g h::MAP g ls) by MIN_LIST_def - = MIN_LIST (MAP g (h::ls)) by MAP -*) -val MIN_LIST_MAP_LE = store_thm( - "MIN_LIST_MAP_LE", - ``!f g. (!x. f x <= g x) ==> !ls. MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls)``, - rpt strip_tac >> - Induct_on `ls` >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[MIN_LIST_def] >> - rw[MIN_LIST_def, MIN_LE_PAIR]); - -(* Theorem: (!x. f x <= g x) ==> !ls n. EL n (MAP f ls) <= EL n (MAP g ls) *) -(* Proof: - By induction on ls. - Base: !n. EL n (MAP f []) <= EL n (MAP g []) - LHS = EL n [] = RHS by MAP - Step: !n. EL n (MAP f ls) <= EL n (MAP g ls) ==> - !h n. EL n (MAP f (h::ls)) <= EL n (MAP g (h::ls)) - If n = 0, - EL 0 (MAP f (h::ls)) - = EL 0 (f h::MAP f ls) by MAP - = f h by EL - <= g h by given - = EL 0 (g h::MAP g ls) by EL - = EL 0 (MAP g (h::ls)) by MAP - If n <> 0, then n = SUC k by num_CASES - EL n (MAP f (h::ls)) - = EL (SUC k) (f h::MAP f ls) by MAP - = EL k (MAP f ls) by EL - <= EL k (MAP g ls) by induction hypothesis - = EL (SUC k) (g h::MAP g ls) by EL - = EL n (MAP g (h::ls)) by MAP -*) -val MAP_LE = store_thm( - "MAP_LE", - ``!(f:num -> num) g. (!x. f x <= g x) ==> !ls n. EL n (MAP f ls) <= EL n (MAP g ls)``, - ntac 3 strip_tac >> - Induct_on `ls` >- - rw[] >> - Cases_on `n` >- - rw[] >> - rw[]); - -(* Theorem: (!x y. f x y <= g x y) ==> !lx ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly) *) -(* Proof: - By induction on lx. - Base: !ly n. EL n (MAP2 f [] ly) <= EL n (MAP2 g [] ly) - LHS = EL n [] = RHS by MAP2_DEF - Step: !ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly) ==> - !h ly n. EL n (MAP2 f (h::lx) ly) <= EL n (MAP2 g (h::lx) ly) - If ly = [], - to show: EL n (MAP2 f (h::lx) []) <= EL n (MAP2 g (h::lx) []) - True since LHS = EL n [] = RHS by MAP2_DEF - Otherwise, ly = h'::t. - to show: EL n (MAP2 f (h::lx) (h'::t)) <= EL n (MAP2 g (h::lx) (h'::t)) - If n = 0, - EL 0 (MAP2 f (h::lx) (h'::t)) - = EL 0 (f h h'::MAP2 f lx t) by MAP2 - = f h h' by EL - <= g h h' by given - = EL 0 (g h h'::MAP2 g lx t) by EL - = EL 0 (MAP2 g (h::lx) (h'::t)) by MAP2 - If n <> 0, then n = SUC k by num_CASES - EL n (MAP2 f (h::lx) (h'::t)) - = EL (SUC k) (f h h'::MAP2 f lx t) by MAP2 - = EL k (MAP2 f lx t) by EL - <= EL k (MAP2 g lx t) by induction hypothesis - = EL (SUC k) (g h h'::MAP2 g lx t) by EL - = EL n (MAP2 g (h::lx) (h'::t)) by MAP2 -*) -val MAP2_LE = store_thm( - "MAP2_LE", - ``!(f:num -> num -> num) g. (!x y. f x y <= g x y) ==> - !lx ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly)``, - ntac 3 strip_tac >> - Induct_on `lx` >- - rw[] >> - rpt strip_tac >> - Cases_on `ly` >- - rw[] >> - Cases_on `n` >- - rw[] >> - rw[]); - -(* Theorem: (!x y z. f x y z <= g x y z) ==> - !lx ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz) *) -(* Proof: - By induction on lx. - Base: !ly lz n. EL n (MAP3 f [] ly lz) <= EL n (MAP3 g [] ly lz) - LHS = EL n [] = RHS by MAP3_DEF - Step: !ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz) ==> - !h ly lz n. EL n (MAP3 f (h::lx) ly lz) <= EL n (MAP3 g (h::lx) ly lz) - If ly = [], - to show: EL n (MAP3 f (h::lx) [] lz) <= EL n (MAP3 g (h::lx) [] lz) - True since LHS = EL n [] = RHS by MAP3_DEF - Otherwise, ly = h'::t. - to show: EL n (MAP3 f (h::lx) (h'::t) lz) <= EL n (MAP3 g (h::lx) (h'::t) lz) - If lz = [], - to show: EL n (MAP3 f (h::lx) (h'::t) []) <= EL n (MAP3 g (h::lx) (h'::t) []) - True since LHS = EL n [] = RHS by MAP3_DEF - Otherwise, lz = h''::t'. - to show: EL n (MAP3 f (h::lx) (h'::t) (h''::t')) <= EL n (MAP3 g (h::lx) (h'::t) (h''::t')) - If n = 0, - EL 0 (MAP3 f (h::lx) (h'::t) (h''::t')) - = EL 0 (f h h' h''::MAP3 f lx t t') by MAP3 - = f h h' h'' by EL - <= g h h' h'' by given - = EL 0 (g h h' h''::MAP3 g lx t t') by EL - = EL 0 (MAP3 g (h::lx) (h'::t) (h''::t')) by MAP3 - If n <> 0, then n = SUC k by num_CASES - EL n (MAP3 f (h::lx) (h'::t) (h''::t')) - = EL (SUC k) (f h h' h''::MAP3 f lx t t') by MAP3 - = EL k (MAP3 f lx t t') by EL - <= EL k (MAP3 g lx t t') by induction hypothesis - = EL (SUC k) (g h h' h''::MAP3 g lx t t') by EL - = EL n (MAP3 g (h::lx) (h'::t) (h''::t')) by MAP3 -*) -val MAP3_LE = store_thm( - "MAP3_LE", - ``!(f:num -> num -> num -> num) g. (!x y z. f x y z <= g x y z) ==> - !lx ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz)``, - ntac 3 strip_tac >> - Induct_on `lx` >- - rw[] >> - rpt strip_tac >> - Cases_on `ly` >- - rw[] >> - Cases_on `lz` >- - rw[] >> - Cases_on `n` >- - rw[] >> - rw[]); - -(* -SUM_MAP_PLUS |- !f g ls. SUM (MAP (\x. f x + g x) ls) = SUM (MAP f ls) + SUM (MAP g ls) -SUM_MAP_PLUS_ZIP |- !ls1 ls2. LENGTH ls1 = LENGTH ls2 /\ (!x y. f (x,y) = g x + h y) ==> - SUM (MAP f (ZIP (ls1,ls2))) = SUM (MAP g ls1) + SUM (MAP h ls2) -*) - -(* Theorem: (!x. f1 x <= f2 x) ==> !ls. SUM (MAP f1 ls) <= SUM (MAP f2 ls) *) -(* Proof: - By SUM_LE, this is to show: - (1) !k. k < LENGTH (MAP f1 ls) ==> EL k (MAP f1 ls) <= EL k (MAP f2 ls) - This is true by EL_MAP - (2) LENGTH (MAP f1 ls) = LENGTH (MAP f2 ls) - This is true by LENGTH_MAP -*) -val SUM_MONO_MAP = store_thm( - "SUM_MONO_MAP", - ``!f1 f2. (!x. f1 x <= f2 x) ==> !ls. SUM (MAP f1 ls) <= SUM (MAP f2 ls)``, - rpt strip_tac >> - irule SUM_LE >> - rw[EL_MAP]); - -(* Theorem: (!x y. f1 x y <= f2 x y) ==> !lx ly. SUM (MAP2 f1 lx ly) <= SUM (MAP2 f2 lx ly) *) -(* Proof: - By SUM_LE, this is to show: - (1) !k. k < LENGTH (MAP2 f1 lx ly) ==> EL k (MAP2 f1 lx ly) <= EL k (MAP2 f2 lx ly) - This is true by EL_MAP2, LENGTH_MAP2 - (2) LENGTH (MAP2 f1 lx ly) = LENGTH (MAP2 f2 lx ly) - This is true by LENGTH_MAP2 -*) -val SUM_MONO_MAP2 = store_thm( - "SUM_MONO_MAP2", - ``!f1 f2. (!x y. f1 x y <= f2 x y) ==> !lx ly. SUM (MAP2 f1 lx ly) <= SUM (MAP2 f2 lx ly)``, - rpt strip_tac >> - irule SUM_LE >> - rw[EL_MAP2]); - -(* Theorem: (!x y z. f1 x y z <= f2 x y z) ==> !lx ly lz. SUM (MAP3 f1 lx ly lz) <= SUM (MAP3 f2 lx ly lz) *) -(* Proof: - By SUM_LE, this is to show: - (1) !k. k < LENGTH (MAP3 f1 lx ly lz) ==> EL k (MAP3 f1 lx ly lz) <= EL k (MAP3 f2 lx ly lz) - This is true by EL_MAP3, LENGTH_MAP3 - (2)LENGTH (MAP3 f1 lx ly lz) = LENGTH (MAP3 f2 lx ly lz) - This is true by LENGTH_MAP3 -*) -val SUM_MONO_MAP3 = store_thm( - "SUM_MONO_MAP3", - ``!f1 f2. (!x y z. f1 x y z <= f2 x y z) ==> - !lx ly lz. SUM (MAP3 f1 lx ly lz) <= SUM (MAP3 f2 lx ly lz)``, - rpt strip_tac >> - irule SUM_LE >> - rw[EL_MAP3, LENGTH_MAP3]); - -(* Theorem: MONO f ==> !ls. SUM (MAP f ls) <= f (MAX_LIST ls) * LENGTH ls *) -(* Proof: - Let c = f (MAX_LIST ls). - - Claim: SUM (MAP f ls) <= SUM (MAP (K c) ls) - Proof: By SUM_LE, this is to show: - (1) LENGTH (MAP f ls) = LENGTH (MAP (K c) ls) - This is true by LENGTH_MAP - (2) !k. k < LENGTH (MAP f ls) ==> EL k (MAP f ls) <= EL k (MAP (K c) ls) - Note EL k (MAP f ls) = f (EL k ls) by EL_MAP - and EL k (MAP (K c) ls) - = (K c) (EL k ls) by EL_MAP - = c by K_THM - Now MEM (EL k ls) ls by EL_MEM - so EL k ls <= MAX_LIST ls by MAX_LIST_PROPERTY - Thus f (EL k ls) <= c by property of f - - Note SUM (MAP (K c) ls) = c * LENGTH ls by SUM_MAP_K - Thus SUM (MAP f ls) <= c * LENGTH ls by Claim -*) -val SUM_MAP_UPPER = store_thm( - "SUM_MAP_UPPER", - ``!f. MONO f ==> !ls. SUM (MAP f ls) <= f (MAX_LIST ls) * LENGTH ls``, - rpt strip_tac >> - qabbrev_tac `c = f (MAX_LIST ls)` >> - `SUM (MAP f ls) <= SUM (MAP (K c) ls)` by - ((irule SUM_LE >> rw[]) >> - rw[EL_MAP, EL_MEM, MAX_LIST_PROPERTY, Abbr`c`]) >> - `SUM (MAP (K c) ls) = c * LENGTH ls` by rw[SUM_MAP_K] >> - decide_tac); - -(* Theorem: MONO2 f ==> - !lx ly. SUM (MAP2 f lx ly) <= (f (MAX_LIST lx) (MAX_LIST ly)) * LENGTH (MAP2 f lx ly) *) -(* Proof: - Let c = f (MAX_LIST lx) (MAX_LIST ly). - - Claim: SUM (MAP2 f lx ly) <= SUM (MAP2 (\x y. c) lx ly) - Proof: By SUM_LE, this is to show: - (1) LENGTH (MAP2 f lx ly) = LENGTH (MAP2 (\x y. c) lx ly) - This is true by LENGTH_MAP2 - (2) !k. k < LENGTH (MAP2 f lx ly) ==> EL k (MAP2 f lx ly) <= EL k (MAP2 (\x y. c) lx ly) - Note EL k (MAP2 f lx ly) - = f (EL k lx) (EL k ly) by EL_MAP2 - and EL k (MAP2 (\x y. c) lx ly) - = (\x y. c) (EL k lx) (EL k ly) by EL_MAP2 - = c by function application - Note k < LENGTH lx, k < LENGTH ly by LENGTH_MAP2 - Now MEM (EL k lx) lx by EL_MEM - and MEM (EL k ly) ly by EL_MEM - so EL k lx <= MAX_LIST lx by MAX_LIST_PROPERTY - and EL k ly <= MAX_LIST ly by MAX_LIST_PROPERTY - Thus f (EL k lx) (EL k ly) <= c by property of f - - Note SUM (MAP (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) by SUM_MAP2_K - and LENGTH (MAP2 (\x y. c) lx ly) = LENGTH (MAP2 f lx ly) by LENGTH_MAP2 - Thus SUM (MAP f lx ly) <= c * LENGTH (MAP2 f lx ly) by Claim -*) -val SUM_MAP2_UPPER = store_thm( - "SUM_MAP2_UPPER", - ``!f. MONO2 f ==> - !lx ly. SUM (MAP2 f lx ly) <= (f (MAX_LIST lx) (MAX_LIST ly)) * LENGTH (MAP2 f lx ly)``, - rpt strip_tac >> - qabbrev_tac `c = f (MAX_LIST lx) (MAX_LIST ly)` >> - `SUM (MAP2 f lx ly) <= SUM (MAP2 (\x y. c) lx ly)` by - ((irule SUM_LE >> rw[]) >> - rw[EL_MAP2, EL_MEM, MAX_LIST_PROPERTY, Abbr`c`]) >> - `SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly)` by rw[SUM_MAP2_K, Abbr`c`] >> - `c * LENGTH (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 f lx ly)` by rw[] >> - decide_tac); - -(* Theorem: MONO3 f ==> - !lx ly lz. SUM (MAP3 f lx ly lz) <= - f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) * LENGTH (MAP3 f lx ly lz) *) -(* Proof: - Let c = f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz). - - Claim: SUM (MAP3 f lx ly lz) <= SUM (MAP3 (\x y z. c) lx ly lz) - Proof: By SUM_LE, this is to show: - (1) LENGTH (MAP3 f lx ly lz) = LENGTH (MAP3 (\x y z. c) lx ly lz) - This is true by LENGTH_MAP3 - (2) !k. k < LENGTH (MAP3 f lx ly lz) ==> EL k (MAP3 f lx ly lz) <= EL k (MAP3 (\x y z. c) lx ly lz) - Note EL k (MAP3 f lx ly lz) - = f (EL k lx) (EL k ly) (EL k lz) by EL_MAP3 - and EL k (MAP3 (\x y z. c) lx ly lz) - = (\x y z. c) (EL k lx) (EL k ly) (EL k lz) by EL_MAP3 - = c by function application - Note k < LENGTH lx, k < LENGTH ly, k < LENGTH lz - by LENGTH_MAP3 - Now MEM (EL k lx) lx by EL_MEM - and MEM (EL k ly) ly by EL_MEM - and MEM (EL k lz) lz by EL_MEM - so EL k lx <= MAX_LIST lx by MAX_LIST_PROPERTY - and EL k ly <= MAX_LIST ly by MAX_LIST_PROPERTY - and EL k lz <= MAX_LIST lz by MAX_LIST_PROPERTY - Thus f (EL k lx) (EL k ly) (EL k lz) <= c by property of f - - Note SUM (MAP (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) by SUM_MAP3_K - and LENGTH (MAP3 (\x y z. c) lx ly lz) = LENGTH (MAP3 f lx ly lz) by LENGTH_MAP3 - Thus SUM (MAP f lx ly lz) <= c * LENGTH (MAP3 f lx ly lz) by Claim -*) -val SUM_MAP3_UPPER = store_thm( - "SUM_MAP3_UPPER", - ``!f. MONO3 f ==> - !lx ly lz. SUM (MAP3 f lx ly lz) <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) * LENGTH (MAP3 f lx ly lz)``, - rpt strip_tac >> - qabbrev_tac `c = f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz)` >> - `SUM (MAP3 f lx ly lz) <= SUM (MAP3 (\x y z. c) lx ly lz)` by - (`LENGTH (MAP3 f lx ly lz) = LENGTH (MAP3 (\x y z. c) lx ly lz)` by rw[LENGTH_MAP3] >> - (irule SUM_LE >> rw[]) >> - fs[LENGTH_MAP3] >> - rw[EL_MAP3, EL_MEM, MAX_LIST_PROPERTY, Abbr`c`]) >> - `SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz)` by rw[SUM_MAP3_K] >> - `c * LENGTH (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 f lx ly lz)` by rw[LENGTH_MAP3] >> - decide_tac); - -(* ------------------------------------------------------------------------- *) -(* Increasing and decreasing list bounds *) -(* ------------------------------------------------------------------------- *) - -(* Overload increasing list and decreasing list *) -val _ = overload_on("MONO_INC", - ``\ls:num list. !m n. m <= n /\ n < LENGTH ls ==> EL m ls <= EL n ls``); -val _ = overload_on("MONO_DEC", - ``\ls:num list. !m n. m <= n /\ n < LENGTH ls ==> EL n ls <= EL m ls``); - -(* Theorem: MONO_INC []*) -(* Proof: no member to falsify. *) -Theorem MONO_INC_NIL: - MONO_INC [] -Proof - simp[] -QED - -(* Theorem: MONO_INC (h::t) ==> MONO_INC t *) -(* Proof: - This is to show: m <= n /\ n < LENGTH t ==> EL m t <= EL n t - Note m <= n <=> SUC m <= SUC n by arithmetic - and n < LENGTH t <=> SUC n < LENGTH (h::t) by LENGTH - Thus EL (SUC m) (h::t) <= EL (SUC n) (h::t) by MONO_INC (h::t) - or EL m t <= EL n t by EL -*) -Theorem MONO_INC_CONS: - !h t. MONO_INC (h::t) ==> MONO_INC t -Proof - rw[] >> - first_x_assum (qspecl_then [`SUC m`, `SUC n`] strip_assume_tac) >> - rfs[] -QED - -(* Theorem: MONO_INC (h::t) /\ MEM x t ==> h <= x *) -(* Proof: - Note MEM x t - ==> ?n. n < LENGTH t /\ x = EL n t by MEM_EL - or SUC n < SUC (LENGTH t) by inequality - Now 0 < SUC n, or 0 <= SUC n, - and SUC n < SUC (LENGTH t) = LENGTH (h::t) by LENGTH - so EL 0 (h::t) <= EL (SUC n) (h::t) by MONO_INC (h::t) - or h <= EL n t = x by EL -*) -Theorem MONO_INC_HD: - !h t x. MONO_INC (h::t) /\ MEM x t ==> h <= x -Proof - rpt strip_tac >> - fs[MEM_EL] >> - last_x_assum (qspecl_then [`0`,`SUC n`] strip_assume_tac) >> - rfs[] -QED - -(* Theorem: MONO_DEC []*) -(* Proof: no member to falsify. *) -Theorem MONO_DEC_NIL: - MONO_DEC [] -Proof - simp[] -QED - -(* Theorem: MONO_DEC (h::t) ==> MONO_DEC t *) -(* Proof: - This is to show: m <= n /\ n < LENGTH t ==> EL n t <= EL m t - Note m <= n <=> SUC m <= SUC n by arithmetic - and n < LENGTH t <=> SUC n < LENGTH (h::t) by LENGTH - Thus EL (SUC n) (h::t) <= EL (SUC m) (h::t) by MONO_DEC (h::t) - or EL n t <= EL m t by EL -*) -Theorem MONO_DEC_CONS: - !h t. MONO_DEC (h::t) ==> MONO_DEC t -Proof - rw[] >> - first_x_assum (qspecl_then [`SUC m`, `SUC n`] strip_assume_tac) >> - rfs[] -QED - -(* Theorem: MONO_DEC (h::t) /\ MEM x t ==> x <= h *) -(* Proof: - Note MEM x t - ==> ?n. n < LENGTH t /\ x = EL n t by MEM_EL - or SUC n < SUC (LENGTH t) by inequality - Now 0 < SUC n, or 0 <= SUC n, - and SUC n < SUC (LENGTH t) = LENGTH (h::t) by LENGTH - so EL (SUC n) (h::t) <= EL 0 (h::t) by MONO_DEC (h::t) - or x = EL n t <= h by EL -*) -Theorem MONO_DEC_HD: - !h t x. MONO_DEC (h::t) /\ MEM x t ==> x <= h -Proof - rpt strip_tac >> - fs[MEM_EL] >> - last_x_assum (qspecl_then [`0`,`SUC n`] strip_assume_tac) >> - rfs[] -QED - -(* Theorem: MONO f ==> MONO_INC (GENLIST f n) *) -(* Proof: - Let ls = GENLIST f n. - Then LENGTH ls = n by LENGTH_GENLIST - and !k. k < n ==> EL k ls = f k by EL_GENLIST - Thus MONO_INC ls -*) -val GENLIST_MONO_INC = store_thm( - "GENLIST_MONO_INC", - ``!f:num -> num n. MONO f ==> MONO_INC (GENLIST f n)``, - rw[]); - -(* Theorem: RMONO f ==> MONO_DEC (GENLIST f n) *) -(* Proof: - Let ls = GENLIST f n. - Then LENGTH ls = n by LENGTH_GENLIST - and !k. k < n ==> EL k ls = f k by EL_GENLIST - Thus MONO_DEC ls -*) -val GENLIST_MONO_DEC = store_thm( - "GENLIST_MONO_DEC", - ``!f:num -> num n. RMONO f ==> MONO_DEC (GENLIST f n)``, - rw[]); - -(* Theorem: ls <> [] /\ (!m n. m <= n ==> EL m ls <= EL n ls) ==> (MAX_LIST ls = LAST ls) *) -(* Proof: - By induction on ls. - Base: [] <> [] /\ MONO_INC [] ==> MAX_LIST [] = LAST [] - Note [] <> [] = F, hence true. - Step: ls <> [] /\ MONO_INC ls ==> MAX_LIST ls = LAST ls ==> - !h. h::ls <> [] /\ MONO_INC (h::ls) ==> MAX_LIST (h::ls) = LAST (h::ls) - If ls = [], - LHS = MAX_LIST [h] = h by MAX_LIST_def - RHS = LAST [h] = h = LHS by LAST_DEF - If ls <> [], - Note h <= LAST ls by LAST_EL_CONS, increasing property - and MONO_INC ls by EL, m <= n ==> SUC m <= SUC n - MAX_LIST (h::ls) - = MAX h (MAX_LIST ls) by MAX_LIST_def - = MAX h (LAST ls) by induction hypothesis - = LAST ls by MAX_DEF, h <= LAST ls - = LAST (h::ls) by LAST_DEF -*) -val MAX_LIST_MONO_INC = store_thm( - "MAX_LIST_MONO_INC", - ``!ls. ls <> [] /\ MONO_INC ls ==> (MAX_LIST ls = LAST ls)``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - `h <= LAST ls` by - (`LAST ls = EL (LENGTH ls) (h::ls)` by rw[LAST_EL_CONS] >> - `h = EL 0 (h::ls)` by rw[] >> - `LENGTH ls < LENGTH (h::ls)` by rw[] >> - metis_tac[DECIDE``0 <= n``]) >> - `MONO_INC ls` by - (rpt strip_tac >> - `SUC m <= SUC n` by decide_tac >> - `EL (SUC m) (h::ls) <= EL (SUC n) (h::ls)` by rw[] >> - fs[]) >> - rw[MAX_DEF, LAST_DEF]); - -(* Theorem: ls <> [] /\ MONO_DEC ls ==> (MAX_LIST ls = HD ls) *) -(* Proof: - By induction on ls. - Base: [] <> [] /\ MONO_DEC [] ==> MAX_LIST [] = HD [] - Note [] <> [] = F, hence true. - Step: ls <> [] /\ MONO_DEC ls ==> MAX_LIST ls = HD ls ==> - !h. h::ls <> [] /\ MONO_DEC (h::ls) ==> MAX_LIST (h::ls) = HD (h::ls) - If ls = [], - LHS = MAX_LIST [h] = h by MAX_LIST_def - RHS = HD [h] = h = LHS by HD - If ls <> [], - Note HD ls <= h by HD, decreasing property - and MONO_DEC ls by EL, m <= n ==> SUC m <= SUC n - MAX_LIST (h::ls) - = MAX h (MAX_LIST ls) by MAX_LIST_def - = MAX h (HD ls) by induction hypothesis - = h by MAX_DEF, HD ls <= h - = HD (h::ls) by HD -*) -val MAX_LIST_MONO_DEC = store_thm( - "MAX_LIST_MONO_DEC", - ``!ls. ls <> [] /\ MONO_DEC ls ==> (MAX_LIST ls = HD ls)``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - `HD ls <= h` by - (`HD ls = EL 1 (h::ls)` by rw[] >> - `h = EL 0 (h::ls)` by rw[] >> - `0 < LENGTH ls` by metis_tac[LENGTH_EQ_0, NOT_ZERO_LT_ZERO] >> - `1 < LENGTH (h::ls)` by rw[] >> - metis_tac[DECIDE``0 <= 1``]) >> - `MONO_DEC ls` by - (rpt strip_tac >> - `SUC m <= SUC n` by decide_tac >> - `EL (SUC n) (h::ls) <= EL (SUC m) (h::ls)` by rw[] >> - fs[]) >> - rw[MAX_DEF]); - -(* Theorem: ls <> [] /\ MONO_INC ls ==> (MIN_LIST ls = HD ls) *) -(* Proof: - By induction on ls. - Base: [] <> [] /\ MONO_INC [] ==> MIN_LIST [] = HD [] - Note [] <> [] = F, hence true. - Step: ls <> [] /\ MONO_INC ls ==> MIN_LIST ls = HD ls ==> - !h. h::ls <> [] /\ MONO_INC (h::ls) ==> MIN_LIST (h::ls) = HD (h::ls) - If ls = [], - LHS = MIN_LIST [h] = h by MIN_LIST_def - RHS = HD [h] = h = LHS by HD - If ls <> [], - Note h <= HD ls by HD, increasing property - and MONO_INC ls by EL, m <= n ==> SUC m <= SUC n - MIN_LIST (h::ls) - = MIN h (MIN_LIST ls) by MIN_LIST_def - = MIN h (HD ls) by induction hypothesis - = h by MIN_DEF, h <= HD ls - = HD (h::ls) by HD -*) -val MIN_LIST_MONO_INC = store_thm( - "MIN_LIST_MONO_INC", - ``!ls. ls <> [] /\ MONO_INC ls ==> (MIN_LIST ls = HD ls)``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - `h <= HD ls` by - (`HD ls = EL 1 (h::ls)` by rw[] >> - `h = EL 0 (h::ls)` by rw[] >> - `0 < LENGTH ls` by metis_tac[LENGTH_EQ_0, NOT_ZERO_LT_ZERO] >> - `1 < LENGTH (h::ls)` by rw[] >> - metis_tac[DECIDE``0 <= 1``]) >> - `MONO_INC ls` by - (rpt strip_tac >> - `SUC m <= SUC n` by decide_tac >> - `EL (SUC m) (h::ls) <= EL (SUC n) (h::ls)` by rw[] >> - fs[]) >> - rw[MIN_DEF]); - -(* Theorem: ls <> [] /\ MONO_DEC ls ==> (MIN_LIST ls = LAST ls) *) -(* Proof: - By induction on ls. - Base: [] <> [] /\ MONO_DEC [] ==> MIN_LIST [] = LAST [] - Note [] <> [] = F, hence true. - Step: ls <> [] /\ MONO_DEC ls ==> MIN_LIST ls = LAST ls ==> - !h. h::ls <> [] /\ MONO_DEC (h::ls) ==> MAX_LIST (h::ls) = LAST (h::ls) - If ls = [], - LHS = MIN_LIST [h] = h by MIN_LIST_def - RHS = LAST [h] = h = LHS by LAST_DEF - If ls <> [], - Note LAST ls <= h by LAST_EL_CONS, decreasing property - and MONO_DEC ls by EL, m <= n ==> SUC m <= SUC n - MIN_LIST (h::ls) - = MIN h (MIN_LIST ls) by MIN_LIST_def - = MIN h (LAST ls) by induction hypothesis - = LAST ls by MIN_DEF, LAST ls <= h - = LAST (h::ls) by LAST_DEF -*) -val MIN_LIST_MONO_DEC = store_thm( - "MIN_LIST_MONO_DEC", - ``!ls. ls <> [] /\ MONO_DEC ls ==> (MIN_LIST ls = LAST ls)``, - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `ls = []` >- - rw[] >> - `LAST ls <= h` by - (`LAST ls = EL (LENGTH ls) (h::ls)` by rw[LAST_EL_CONS] >> - `h = EL 0 (h::ls)` by rw[] >> - `LENGTH ls < LENGTH (h::ls)` by rw[] >> - metis_tac[DECIDE``0 <= n``]) >> - `MONO_DEC ls` by - (rpt strip_tac >> - `SUC m <= SUC n` by decide_tac >> - `EL (SUC n) (h::ls) <= EL (SUC m) (h::ls)` by rw[] >> - fs[]) >> - rw[MIN_DEF, LAST_DEF]); - -(* Theorem: MONO_INC [m .. n] *) -(* Proof: - This is to show: - !j k. j <= k /\ k < LENGTH [m .. n] ==> EL j [m .. n] <= EL k [m .. n] - Note LENGTH [m .. n] = n + 1 - m by listRangeINC_LEN - so m + j <= n by j < LENGTH [m .. n] - ==> EL j [m .. n] = m + j by listRangeINC_EL - also m + k <= n by k < LENGTH [m .. n] - ==> EL k [m .. n] = m + k by listRangeINC_EL - Thus EL j [m .. n] <= EL k [m .. n] by arithmetic -*) -Theorem listRangeINC_MONO_INC: - !m n. MONO_INC [m .. n] -Proof - simp[listRangeINC_EL, listRangeINC_LEN] -QED - -(* Theorem: MONO_INC [m ..< n] *) -(* Proof: - This is to show: - !j k. j <= k /\ k < LENGTH [m ..< n] ==> EL j [m ..< n] <= EL k [m ..< n] - Note LENGTH [m ..< n] = n - m by listRangeLHI_LEN - so m + j < n by j < LENGTH [m ..< n] - ==> EL j [m ..< n] = m + j by listRangeLHI_EL - also m + k < n by k < LENGTH [m ..< n] - ==> EL k [m ..< n] = m + k by listRangeLHI_EL - Thus EL j [m ..< n] <= EL k [m ..< n] by arithmetic -*) -Theorem listRangeLHI_MONO_INC: - !m n. MONO_INC [m ..< n] -Proof - simp[listRangeLHI_EL] -QED - -(* ------------------------------------------------------------------------- *) -(* List Dilation *) -(* ------------------------------------------------------------------------- *) - -(* -Use the concept of dilating a list. - -Let p = [1;2;3], that is, p = 1 + 2x + 3x^2. -Then q = peval p (x^3) is just q = 1 + 2(x^3) + 3(x^3)^2 = [1;0;0;2;0;0;3] - -DILATE 3 [] = [] -DILATE 3 (h::t) = [h;0;0] ++ MDILATE 3 t - -val DILATE_3_DEF = Define` - (DILATE_3 [] = []) /\ - (DILATE_3 (h::t) = [h;0;0] ++ (MDILATE_3 t)) -`; -> EVAL ``DILATE_3 [1;2;3]``; -val it = |- MDILATE_3 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3; 0; 0]: thm - -val DILATE_3_DEF = Define` - (DILATE_3 [] = []) /\ - (DILATE_3 [h] = [h]) /\ - (DILATE_3 (h::t) = [h;0;0] ++ (MDILATE_3 t)) -`; -> EVAL ``DILATE_3 [1;2;3]``; -val it = |- MDILATE_3 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3]: thm -*) - -(* ------------------------------------------------------------------------- *) -(* List Dilation (Multiplicative) *) -(* ------------------------------------------------------------------------- *) - -(* Note: - It would be better to define: MDILATE e n l = inserting n (e)'s, - that is, using GENLIST (K e) n, so that only MDILATE e 0 l = l. - However, the intention is to have later, for polynomials: - peval p (X ** n) = pdilate n p - and since X ** 1 = X, and peval p X = p, - it is desirable to have MDILATE e 1 l = l, with the definition below. - - However, the multiplicative feature at the end destroys such an application. -*) - -(* Dilate a list with an element e, for a factor n (n <> 0) *) -val MDILATE_def = Define` - (MDILATE e n [] = []) /\ - (MDILATE e n (h::t) = if t = [] then [h] else (h:: GENLIST (K e) (PRE n)) ++ (MDILATE e n t)) -`; -(* -> EVAL ``MDILATE 0 2 [1;2;3]``; -val it = |- MDILATE 0 2 [1; 2; 3] = [1; 0; 2; 0; 3]: thm -> EVAL ``MDILATE 0 3 [1;2;3]``; -val it = |- MDILATE 0 3 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3]: thm -> EVAL ``MDILATE #0 3 [a;b;#1]``; -val it = |- MDILATE #0 3 [a; b; #1] = [a; #0; #0; b; #0; #0; #1]: thm -*) - -(* Theorem: MDILATE e n [] = [] *) -(* Proof: by MDILATE_def *) -val MDILATE_NIL = store_thm( - "MDILATE_NIL", - ``!e n. MDILATE e n [] = []``, - rw[MDILATE_def]); - -(* export simple result *) -val _ = export_rewrites["MDILATE_NIL"]; - -(* Theorem: MDILATE e n [x] = [x] *) -(* Proof: by MDILATE_def *) -val MDILATE_SING = store_thm( - "MDILATE_SING", - ``!e n x. MDILATE e n [x] = [x]``, - rw[MDILATE_def]); - -(* export simple result *) -val _ = export_rewrites["MDILATE_SING"]; - -(* Theorem: MDILATE e n (h::t) = - if t = [] then [h] else (h:: GENLIST (K e) (PRE n)) ++ (MDILATE e n t) *) -(* Proof: by MDILATE_def *) -val MDILATE_CONS = store_thm( - "MDILATE_CONS", - ``!e n h t. MDILATE e n (h::t) = - if t = [] then [h] else (h:: GENLIST (K e) (PRE n)) ++ (MDILATE e n t)``, - rw[MDILATE_def]); - -(* Theorem: MDILATE e 1 l = l *) -(* Proof: - By induction on l. - Base: !e. MDILATE e 1 [] = [], true by MDILATE_NIL - Step: !e. MDILATE e 1 l = l ==> !h e. MDILATE e 1 (h::l) = h::l - If l = [], - MDILATE e 1 [h] - = [h] by MDILATE_SING - If l <> [], - MDILATE e 1 (h::l) - = (h:: GENLIST (K e) (PRE 1)) ++ (MDILATE e n l) by MDILATE_CONS - = (h:: GENLIST (K e) (PRE 1)) ++ l by induction hypothesis - = (h:: GENLIST (K e) 0) ++ l by PRE - = [h] ++ l by GENLIST_0 - = h::l by CONS_APPEND -*) -val MDILATE_1 = store_thm( - "MDILATE_1", - ``!l e. MDILATE e 1 l = l``, - Induct_on `l` >> - rw[MDILATE_def]); - -(* Theorem: MDILATE e 0 l = l *) -(* Proof: - By induction on l, and note GENLIST (K e) (PRE 0) = GENLIST (K e) 0 = []. -*) -val MDILATE_0 = store_thm( - "MDILATE_0", - ``!l e. MDILATE e 0 l = l``, - Induct_on `l` >> rw[MDILATE_def]); - -(* Theorem: LENGTH (MDILATE e n l) = - if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l)) *) -(* Proof: - If n = 0, - Then MDILATE e 0 l = l by MDILATE_0 - Hence true. - If n <> 0, - Then 0 < n by NOT_ZERO_LT_ZERO - By induction on l. - Base: LENGTH (MDILATE e n []) = if n = 0 then LENGTH [] else if [] = [] then 0 else SUC (n * PRE (LENGTH [])) - LENGTH (MDILATE e n []) - = LENGTH [] by MDILATE_NIL - = 0 by LENGTH_NIL - Step: LENGTH (MDILATE e n l) = if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l)) ==> - !h. LENGTH (MDILATE e n (h::l)) = if n = 0 then LENGTH (h::l) else if h::l = [] then 0 else SUC (n * PRE (LENGTH (h::l))) - Note h::l = [] <=> F by NOT_CONS_NIL - If l = [], - LENGTH (MDILATE e n [h]) - = LENGTH [h] by MDILATE_SING - = 1 by LENGTH_EQ_1 - = SUC 0 by ONE - = SUC (n * 0) by MULT_0 - = SUC (n * (PRE (LENGTH [h]))) by LENGTH_EQ_1, PRE_SUC_EQ - If l <> [], - Then LENGTH l <> 0 by LENGTH_NIL - LENGTH (MDILATE e n (h::l)) - = LENGTH (h:: GENLIST (K e) (PRE n) ++ MDILATE e n l) by MDILATE_CONS - = LENGTH (h:: GENLIST (K e) (PRE n)) + LENGTH (MDILATE e n l) by LENGTH_APPEND - = n + LENGTH (MDILATE e n l) by LENGTH_GENLIST - = n + SUC (n * PRE (LENGTH l)) by induction hypothesis - = SUC (n + n * PRE (LENGTH l)) by ADD_SUC - = SUC (n * SUC (PRE (LENGTH l))) by MULT_SUC - = SUC (n * LENGTH l) by SUC_PRE, 0 < LENGTH l - = SUC (n * PRE (LENGTH (h::l))) by LENGTH, PRE_SUC_EQ -*) -val MDILATE_LENGTH = store_thm( - "MDILATE_LENGTH", - ``!l e n. LENGTH (MDILATE e n l) = - if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l))``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[MDILATE_0] >> - `0 < n` by decide_tac >> - Induct_on `l` >- - rw[] >> - rw[MDILATE_def] >> - `LENGTH l <> 0` by metis_tac[LENGTH_NIL] >> - `0 < LENGTH l` by decide_tac >> - `PRE n + SUC (n * PRE (LENGTH l)) = SUC (PRE n) + n * PRE (LENGTH l)` by rw[] >> - `_ = n + n * PRE (LENGTH l)` by decide_tac >> - `_ = n * SUC (PRE (LENGTH l))` by rw[MULT_SUC] >> - `_ = n * LENGTH l` by metis_tac[SUC_PRE] >> - decide_tac); - -(* Theorem: LENGTH l <= LENGTH (MDILATE e n l) *) -(* Proof: - If n = 0, - LENGTH (MDILATE e 0 l) - = LENGTH l by MDILATE_LENGTH - >= LENGTH l - If l = [], - LENGTH (MDILATE e n []) - = LENGTH [] by MDILATE_NIL - >= LENGTH [] - If l <> [], - Then ?h t. l = h::t by list_CASES - LENGTH (MDILATE e n (h::t)) - = SUC (n * PRE (LENGTH (h::t))) by MDILATE_LENGTH - = SUC (n * PRE (SUC (LENGTH t))) by LENGTH - = SUC (n * LENGTH t) by PRE - = n * LENGTH t + 1 by ADD1 - >= LENGTH t + 1 by LE_MULT_CANCEL_LBARE, 0 < n - = SUC (LENGTH t) by ADD1 - = LENGTH (h::t) by LENGTH -*) -val MDILATE_LENGTH_LOWER = store_thm( - "MDILATE_LENGTH_LOWER", - ``!l e n. LENGTH l <= LENGTH (MDILATE e n l)``, - rw[MDILATE_LENGTH] >> - `?h t. l = h::t` by metis_tac[list_CASES] >> - rw[]); - -(* Theorem: 0 < n ==> LENGTH (MDILATE e n l) <= SUC (n * PRE (LENGTH l)) *) -(* Proof: - Since n <> 0, - If l = [], - LENGTH (MDILATE e n []) - = LENGTH [] by MDILATE_NIL - = 0 by LENGTH_NIL - SUC (n * PRE (LENGTH [])) - = SUC (n * PRE 0) by LENGTH_NIL - = SUC 0 by PRE, MULT_0 - > 0 by LESS_SUC - If l <> [], - LENGTH (MDILATE e n l) - = SUC (n * PRE (LENGTH l)) by MDILATE_LENGTH, n <> 0 -*) -val MDILATE_LENGTH_UPPER = store_thm( - "MDILATE_LENGTH_UPPER", - ``!l e n. 0 < n ==> LENGTH (MDILATE e n l) <= SUC (n * PRE (LENGTH l))``, - rw[MDILATE_LENGTH]); - -(* Theorem: k < LENGTH (MDILATE e n l) ==> - (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e) *) -(* Proof: - If n = 0, - Then MDILATE e 0 l = l by MDILATE_0 - Hence true trivially. - If n <> 0, - Then 0 < n by NOT_ZERO_LT_ZERO - By induction on l. - Base: !k. k < LENGTH (MDILATE e n []) ==> - (EL k (MDILATE e n []) = if n = 0 then EL k [] else if k MOD n = 0 then EL (k DIV n) [] else e) - Note LENGTH (MDILATE e n []) - = LENGTH [] by MDILATE_NIL - = 0 by LENGTH_NIL - Thus k < 0 <=> F by NOT_ZERO_LT_ZERO - Step: !k. k < LENGTH (MDILATE e n l) ==> (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e) ==> - !h k. k < LENGTH (MDILATE e n (h::l)) ==> (EL k (MDILATE e n (h::l)) = if n = 0 then EL k (h::l) else if k MOD n = 0 then EL (k DIV n) (h::l) else e) - Note LENGTH (MDILATE e n [h]) = 1 by MDILATE_SING - and LENGTH (MDILATE e n (h::l)) - = SUC (n * PRE (LENGTH (h::l))) by MDILATE_LENGTH, n <> 0 - = SUC (n * PRE (SUC (LENGTH l))) by LENGTH - = SUC (n * LENGTH l) by PRE - - If l = [], - Then MDILATE e n [h] = [h] by MDILATE_SING - and LENGTH (MDILATE e n [h]) = 1 by LENGTH - so k < 1 means k = 0. - and 0 DIV n = 0 by ZERO_DIV, 0 < n - and 0 MOD n = 0 by ZERO_MOD, 0 < n - Thus EL k [h] = EL (k DIV n) [h]. - - If l <> [], - Let t = h::GENLIST (K e) (PRE n) - Note LENGTH t = n by LENGTH_GENLIST - If k < n, - Then k MOD n = k by LESS_MOD, k < n - EL k (MDILATE e n (h::l)) - = EL k (t ++ MDILATE e n l) by MDILATE_CONS - = EL k t by EL_APPEND, k < LENGTH t - If k = 0, - EL 0 t - = EL 0 (h:: GENLIST (K e) (PRE n)) by notation of t - = h - = EL (0 DIV n) (h::l) by EL, HD - If k <> 0, - EL k t - = EL k (h:: GENLIST (K e) (PRE n)) by notation of t - = EL (PRE k) (GENLIST (K e) (PRE n)) by EL_CONS - = (K e) (PRE k) by EL_GENLIST, PRE k < PRE n - = e by application of K - If ~(k < n), n <= k. - Given k < LENGTH (MDILATE e n (h::l)) - or k < SUC (n * LENGTH l) by above - ==> k - n < SUC (n * LENGTH l) - n by n <= k - = SUC (n * LENGTH l - n) by SUB - = SUC (n * (LENGTH l - 1)) by LEFT_SUB_DISTRIB - = SUC (n * PRE (LENGTH l)) by PRE_SUB1 - or k - n < LENGTH (MDILATE e n l) by MDILATE_LENGTH - Thus (k - n) MOD n = k MOD n by SUB_MOD - and (k - n) DIV n = k DIV n - 1 by SUB_DIV - If k MOD n = 0, - Note 0 < k DIV n by DIVIDES_MOD_0, DIV_POS - EL k (t ++ MDILATE e n l) - = EL (k - n) (MDILATE e n l) by EL_APPEND, n <= k - = EL (k DIV n - 1) l by induction hypothesis, (k - n) MOD n = 0 - = EL (PRE (k DIV n)) l by PRE_SUB1 - = EL (k DIV n) (h::l) by EL_CONS, 0 < k DIV n - If k MOD n <> 0, - EL k (t ++ MDILATE e n l) - = EL (k - n) (MDILATE e n l) by EL_APPEND, n <= k - = e by induction hypothesis, (k - n) MOD n <> 0 -*) -val MDILATE_EL = store_thm( - "MDILATE_EL", - ``!l e n k. k < LENGTH (MDILATE e n l) ==> - (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e)``, - ntac 3 strip_tac >> - Cases_on `n = 0` >- - rw[MDILATE_0] >> - `0 < n` by decide_tac >> - Induct_on `l` >- - rw[] >> - rpt strip_tac >> - `LENGTH (MDILATE e n [h]) = 1` by rw[MDILATE_SING] >> - `LENGTH (MDILATE e n (h::l)) = SUC (n * LENGTH l)` by rw[MDILATE_LENGTH] >> - qabbrev_tac `t = h:: GENLIST (K e) (PRE n)` >> - `!k. k < 1 <=> (k = 0)` by decide_tac >> - rw_tac std_ss[MDILATE_def] >- - metis_tac[ZERO_DIV] >- - metis_tac[ZERO_MOD] >- - (rw_tac std_ss[EL_APPEND] >| [ - `LENGTH t = n` by rw[Abbr`t`] >> - `k MOD n = k` by rw[LESS_MOD] >> - `!x. EL 0 (h::x) = h` by rw[] >> - metis_tac[ZERO_DIV], - `LENGTH t = n` by rw[Abbr`t`] >> - `k - n < LENGTH (MDILATE e n l)` by rw[MDILATE_LENGTH] >> - `(k - n) MOD n = k MOD n` by rw[SUB_MOD] >> - `(k - n) DIV n = k DIV n - 1` by rw[GSYM SUB_DIV] >> - `0 < k DIV n` by rw[DIVIDES_MOD_0, DIV_POS] >> - `EL (k - n) (MDILATE e n l) = EL (k DIV n - 1) l` by rw[] >> - `_ = EL (PRE (k DIV n)) l` by rw[PRE_SUB1] >> - `_ = EL (k DIV n) (h::l)` by rw[EL_CONS] >> - rw[] - ]) >> - rw_tac std_ss[EL_APPEND] >| [ - `LENGTH t = n` by rw[Abbr`t`] >> - `k MOD n = k` by rw[LESS_MOD] >> - `0 < k /\ PRE k < PRE n` by decide_tac >> - `EL k t = EL (PRE k) (GENLIST (K e) (PRE n))` by rw[EL_CONS, Abbr`t`] >> - `_ = e` by rw[] >> - rw[], - `LENGTH t = n` by rw[Abbr`t`] >> - `k - n < LENGTH (MDILATE e n l)` by rw[MDILATE_LENGTH] >> - `n <= k` by decide_tac >> - `(k - n) MOD n = k MOD n` by rw[SUB_MOD] >> - `EL (k - n) (MDILATE e n l) = e` by rw[] >> - rw[] - ]); - -(* This is a milestone theorem. *) - -(* Theorem: (MDILATE e n l = []) <=> (l = []) *) -(* Proof: - If part: MDILATE e n l = [] ==> l = [] - By contradiction, suppose l <> []. - If n = 0, - Then MDILATE e 0 l = l by MDILATE_0 - This contradicts MDILATE e 0 l = []. - If n <> 0, - Then LENGTH (MDILATE e n l) - = SUC (n * PRE (LENGTH l)) by MDILATE_LENGTH - <> 0 by SUC_NOT - So (MDILATE e n l) <> [] by LENGTH_NIL - This contradicts MDILATE e n l = [] - Only-if part: l = [] ==> MDILATE e n l = [] - True by MDILATE_NIL -*) -val MDILATE_EQ_NIL = store_thm( - "MDILATE_EQ_NIL", - ``!l e n. (MDILATE e n l = []) <=> (l = [])``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - Cases_on `n = 0` >| [ - `MDILATE e 0 l = l` by rw[GSYM MDILATE_0] >> - metis_tac[], - `LENGTH (MDILATE e n l) = SUC (n * PRE (LENGTH l))` by rw[MDILATE_LENGTH] >> - `LENGTH (MDILATE e n l) <> 0` by decide_tac >> - metis_tac[LENGTH_EQ_0] - ]); - -(* Theorem: LAST (MDILATE e n l) = LAST l *) -(* Proof: - If l = [], - LAST (MDILATE e n []) - = LAST [] by MDILATE_NIL - If l <> [], - If n = 0, - LAST (MDILATE e 0 l) - = LAST l by MDILATE_0 - If n <> 0, then 0 < m by LESS_0 - Then MDILATE e n l <> [] by MDILATE_EQ_NIL - or LENGTH (MDILATE e n l) <> 0 by LENGTH_NIL - Note PRE (LENGTH (MDILATE e n l)) - = PRE (SUC (n * PRE (LENGTH l))) by MDILATE_LENGTH - = n * PRE (LENGTH l) by PRE - Let k = PRE (LENGTH (MDILATE e n l)). - Then k < LENGTH (MDILATE e n l) by PRE x < x - and k MOD n = 0 by MOD_EQ_0, MULT_COMM, 0 < n - and k DIV n = PRE (LENGTH l) by MULT_DIV, MULT_COMM - - LAST (MDILATE e n l) - = EL k (MDILATE e n l) by LAST_EL - = EL (k DIV n) l by MDILATE_EL - = EL (PRE (LENGTH l)) l by above - = LAST l by LAST_EL -*) -val MDILATE_LAST = store_thm( - "MDILATE_LAST", - ``!l e n. LAST (MDILATE e n l) = LAST l``, - rpt strip_tac >> - Cases_on `l = []` >- - rw[] >> - Cases_on `n = 0` >- - rw[MDILATE_0] >> - `0 < n` by decide_tac >> - `MDILATE e n l <> []` by rw[MDILATE_EQ_NIL] >> - `LENGTH (MDILATE e n l) <> 0` by metis_tac[LENGTH_NIL] >> - qabbrev_tac `k = PRE (LENGTH (MDILATE e n l))` >> - rw[LAST_EL] >> - `k = n * PRE (LENGTH l)` by rw[MDILATE_LENGTH, Abbr`k`] >> - `k MOD n = 0` by metis_tac[MOD_EQ_0, MULT_COMM] >> - `k DIV n = PRE (LENGTH l)` by metis_tac[MULT_DIV, MULT_COMM] >> - `k < LENGTH (MDILATE e n l)` by rw[Abbr`k`] >> - rw[MDILATE_EL]); - -(* -Succesive dilation: - -> EVAL ``MDILATE #0 3 [a; b; c]``; -val it = |- MDILATE #0 3 [a; b; c] = [a; #0; #0; b; #0; #0; c]: thm -> EVAL ``MDILATE #0 4 [a; b; c]``; -val it = |- MDILATE #0 4 [a; b; c] = [a; #0; #0; #0; b; #0; #0; #0; c]: thm -> EVAL ``MDILATE #0 1 (MDILATE #0 3 [a; b; c])``; -val it = |- MDILATE #0 1 (MDILATE #0 3 [a; b; c]) = [a; #0; #0; b; #0; #0; c]: thm -> EVAL ``MDILATE #0 2 (MDILATE #0 3 [a; b; c])``; -val it = |- MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = [a; #0; #0; #0; #0; #0; b; #0; #0; #0; #0; #0; c]: thm -> EVAL ``MDILATE #0 2 (MDILATE #0 2 [a; b; c])``; -val it = |- MDILATE #0 2 (MDILATE #0 2 [a; b; c]) = [a; #0; #0; #0; b; #0; #0; #0; c]: thm -> EVAL ``MDILATE #0 2 (MDILATE #0 2 [a; b; c]) = MDILATE #0 4 [a; b; c]``; -val it = |- (MDILATE #0 2 (MDILATE #0 2 [a; b; c]) = MDILATE #0 4 [a; b; c]) <=> T: thm -> EVAL ``MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 5 [a; b; c]``; -val it = |- (MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 5 [a; b; c]) <=> F: thm -> EVAL ``MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 6 [a; b; c]``; -val it = |- (MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 6 [a; b; c]) <=> T: thm - -So successive dilation is related to product, or factorisation, or primes: -MDILATE e m (MDILATE e n l) = MDILATE e (m * n) l, for 0 < m, 0 < n. - -*) - -(* ------------------------------------------------------------------------- *) -(* List Dilation (Additive) *) -(* ------------------------------------------------------------------------- *) - -(* Dilate by inserting m zeroes, at position n of tail *) -Definition DILATE_def: - (DILATE e n m [] = []) /\ - (DILATE e n m [h] = [h]) /\ - (DILATE e n m (h::t) = h:: (TAKE n t ++ (GENLIST (K e) m) ++ DILATE e n m (DROP n t))) -Termination - WF_REL_TAC `measure (λ(a,b,c,d). LENGTH d)` >> - rw[LENGTH_DROP] -End - -(* -> EVAL ``DILATE 0 0 1 [1;2;3]``; -val it = |- DILATE 0 0 1 [1; 2; 3] = [1; 0; 2; 0; 3]: thm -> EVAL ``DILATE 0 0 2 [1;2;3]``; -val it = |- DILATE 0 0 2 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3]: thm -> EVAL ``DILATE 0 1 1 [1;2;3]``; -val it = |- DILATE 0 1 1 [1; 2; 3] = [1; 2; 0; 3]: thm -> EVAL ``DILATE 0 1 1 (DILATE 0 0 1 [1;2;3])``; -val it = |- DILATE 0 1 1 (DILATE 0 0 1 [1; 2; 3]) = [1; 0; 0; 2; 0; 0; 3]: thm -> EVAL ``DILATE 0 0 3 [1;2;3]``; -val it = |- DILATE 0 0 3 [1; 2; 3] = [1; 0; 0; 0; 2; 0; 0; 0; 3]: thm -> EVAL ``DILATE 0 1 1 (DILATE 0 0 2 [1;2;3])``; -val it = |- DILATE 0 1 1 (DILATE 0 0 2 [1; 2; 3]) = [1; 0; 0; 0; 2; 0; 0; 0; 0; 3]: thm -> EVAL ``DILATE 0 0 3 [1;2;3] = DILATE 0 2 1 (DILATE 0 0 2 [1;2;3])``; -val it = |- (DILATE 0 0 3 [1; 2; 3] = DILATE 0 2 1 (DILATE 0 0 2 [1; 2; 3])) <=> T: thm - -> EVAL ``DILATE 0 0 0 [1;2;3]``; -val it = |- DILATE 0 0 0 [1; 2; 3] = [1; 2; 3]: thm -> EVAL ``DILATE 1 0 0 [1;2;3]``; -val it = |- DILATE 1 0 0 [1; 2; 3] = [1; 2; 3]: thm -> EVAL ``DILATE 1 0 1 [1;2;3]``; -val it = |- DILATE 1 0 1 [1; 2; 3] = [1; 1; 2; 1; 3]: thm -> EVAL ``DILATE 1 1 1 [1;2;3]``; -val it = |- DILATE 1 1 1 [1; 2; 3] = [1; 2; 1; 3]: thm -> EVAL ``DILATE 1 1 2 [1;2;3]``; -val it = |- DILATE 1 1 2 [1; 2; 3] = [1; 2; 1; 1; 3]: thm -> EVAL ``DILATE 1 1 3 [1;2;3]``; -val it = |- DILATE 1 1 3 [1; 2; 3] = [1; 2; 1; 1; 1; 3]: thm -*) - -(* Theorem: DILATE e n m [] = [] *) -(* Proof: by DILATE_def *) -val DILATE_NIL = save_thm("DILATE_NIL", DILATE_def |> CONJUNCT1); -(* val DILATE_NIL = |- !n m e. DILATE e n m [] = []: thm *) - -(* export simple result *) -val _ = export_rewrites["DILATE_NIL"]; - -(* Theorem: DILATE e n m [h] = [h] *) -(* Proof: by DILATE_def *) -val DILATE_SING = save_thm("DILATE_SING", DILATE_def |> CONJUNCT2 |> CONJUNCT1); -(* val DILATE_SING = |- !n m h e. DILATE e n m [h] = [h]: thm *) - -(* export simple result *) -val _ = export_rewrites["DILATE_SING"]; - -(* Theorem: DILATE e n m (h::t) = - if t = [] then [h] else h:: (TAKE n t ++ (GENLIST (K e) m) ++ DILATE e n m (DROP n t)) *) -(* Proof: by DILATE_def, list_CASES *) -val DILATE_CONS = store_thm( - "DILATE_CONS", - ``!n m h t e. DILATE e n m (h::t) = - if t = [] then [h] else h:: (TAKE n t ++ (GENLIST (K e) m) ++ DILATE e n m (DROP n t))``, - metis_tac[DILATE_def, list_CASES]); - -(* Theorem: DILATE e 0 n (h::t) = if t = [] then [h] else h::(GENLIST (K e) n ++ DILATE e 0 n t) *) -(* Proof: - If t = [], - DILATE e 0 n (h::t) = [h] by DILATE_CONS - If t <> [], - DILATE e 0 n (h::t) - = h:: (TAKE 0 t ++ (GENLIST (K e) n) ++ DILATE e 0 n (DROP 0 t)) by DILATE_CONS - = h:: ([] ++ (GENLIST (K e) n) ++ DILATE e 0 n t) by TAKE_0, DROP_0 - = h:: (GENLIST (K e) n ++ DILATE e 0 n t) by APPEND -*) -val DILATE_0_CONS = store_thm( - "DILATE_0_CONS", - ``!n h t e. DILATE e 0 n (h::t) = if t = [] then [h] else h::(GENLIST (K e) n ++ DILATE e 0 n t)``, - rw[DILATE_CONS]); - -(* Theorem: DILATE e 0 0 l = l *) -(* Proof: - By induction on l. - Base: DILATE e 0 0 [] = [], true by DILATE_NIL - Step: DILATE e 0 0 l = l ==> !h. DILATE e 0 0 (h::l) = h::l - If l = [], - DILATE e 0 0 [h] = [h] by DILATE_SING - If l <> [], - DILATE e 0 0 (h::l) - = h::(GENLIST (K e) 0 ++ DILATE e 0 0 l) by DILATE_0_CONS - = h::([] ++ DILATE e 0 0 l) by GENLIST_0 - = h:: DILATE e 0 0 l by APPEND - = h::l by induction hypothesis -*) -val DILATE_0_0 = store_thm( - "DILATE_0_0", - ``!l e. DILATE e 0 0 l = l``, - Induct >> - rw[DILATE_0_CONS]); - -(* Theorem: DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l) *) -(* Proof: - If n = 0, - DILATE e 0 1 l = DILATE e 0 1 (DILATE e 0 0 l) by DILATE_0_0 - If n <> 0, - GENLIST (K e) n <> [] by LENGTH_GENLIST, LENGTH_NIL - By induction on l. - Base: DILATE e 0 (SUC n) [] = DILATE e n 1 (DILATE e 0 n []) - DILATE e 0 (SUC n) [] = [] by DILATE_NIL - DILATE e n 1 (DILATE e 0 n []) - = DILATE e n 1 [] = [] by DILATE_NIL - Step: DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l) ==> - !h. DILATE e 0 (SUC n) (h::l) = DILATE e n 1 (DILATE e 0 n (h::l)) - If l = [], - DILATE e 0 (SUC n) [h] = [h] by DILATE_SING - DILATE e n 1 (DILATE e 0 n [h]) - = DILATE e n 1 [h] = [h] by DILATE_SING - If l <> [], - DILATE e 0 (SUC n) (h::l) - = h::(GENLIST (K e) (SUC n) ++ DILATE e 0 (SUC n) l) by DILATE_0_CONS - = h::(GENLIST (K e) (SUC n) ++ DILATE e n 1 (DILATE e 0 n l)) by induction hypothesis - - Note LENGTH (GENLIST (K e) n) = n by LENGTH_GENLIST - so (GENLIST (K e) n ++ DILATE e 0 n l) <> [] by APPEND_eq_NIL, LENGTH_NIL [1] - and TAKE n (GENLIST (K e) n ++ DILATE e 0 n l) = GENLIST (K e) n by TAKE_LENGTH_APPEND [2] - and DROP n (GENLIST (K e) n ++ DILATE e 0 n l) = DILATE e 0 n l by DROP_LENGTH_APPEND [3] - and GENLIST (K e) (SUC n) - = GENLIST (K e) (1 + n) by SUC_ONE_ADD - = GENLIST (K e) n ++ GENLIST (K e) 1 by GENLIST_K_ADD [4] - - DILATE e n 1 (DILATE e 0 n (h::l)) - = DILATE e n 1 (h::(GENLIST (K e) n ++ DILATE e 0 n l)) by DILATE_0_CONS - = h::(TAKE n (GENLIST (K e) n ++ DILATE e 0 n l) ++ GENLIST (K e) 1 ++ - DILATE e n 1 (DROP n (GENLIST (K e) n ++ DILATE e 0 n l))) by DILATE_CONS, [1] - = h::(GENLIST (K e) n ++ GENLIST (K e) 1 ++ DILATE e n 1 (DILATE e 0 n l)) by above [2], [3] - = h::(GENLIST (K e) (SUC n) ++ DILATE e n 1 (DILATE e 0 n l)) by above [4] -*) -val DILATE_0_SUC = store_thm( - "DILATE_0_SUC", - ``!l e n. DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[DILATE_0_0] >> - Induct_on `l` >- - rw[] >> - rpt strip_tac >> - Cases_on `l = []` >- - rw[DILATE_SING] >> - qabbrev_tac `a = GENLIST (K e) n ++ DILATE e 0 n l` >> - `LENGTH (GENLIST (K e) n) = n` by rw[] >> - `a <> []` by metis_tac[APPEND_eq_NIL, LENGTH_NIL] >> - `TAKE n a = GENLIST (K e) n` by metis_tac[TAKE_LENGTH_APPEND] >> - `DROP n a = DILATE e 0 n l` by metis_tac[DROP_LENGTH_APPEND] >> - `GENLIST (K e) (SUC n) = GENLIST (K e) n ++ GENLIST (K e) 1` by rw_tac std_ss[SUC_ONE_ADD, GENLIST_K_ADD] >> - metis_tac[DILATE_0_CONS, DILATE_CONS]); - -(* Theorem: LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l)) *) -(* Proof: - By induction on l. - Base: LENGTH (DILATE e 0 n []) = 0 - LENGTH (DILATE e 0 n []) - = LENGTH [] by DILATE_NIL - = 0 by LENGTH_NIL - Step: LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l)) ==> - !h. LENGTH (DILATE e 0 n (h::l)) = SUC (SUC n * PRE (LENGTH (h::l))) - If l = [], - LENGTH (DILATE e 0 n [h]) - = LENGTH [h] by DILATE_SING - = 1 by LENGTH - SUC (SUC n * PRE (LENGTH [h]) - = SUC (SUC n * PRE 1) by LENGTH - = SUC (SUC n * 0) by PRE_SUB1 - = SUC 0 by MULT_0 - = 1 by ONE - If l <> [], - Note LENGTH l <> 0 by LENGTH_NIL - LENGTH (DILATE e 0 n (h::l)) - = LENGTH (h::(GENLIST (K e) n ++ DILATE e 0 n l)) by DILATE_0_CONS - = SUC (LENGTH (GENLIST (K e) n ++ DILATE e 0 n l)) by LENGTH - = SUC (LENGTH (GENLIST (K e) n) + LENGTH (DILATE e 0 n l)) by LENGTH_APPEND - = SUC (n + LENGTH (DILATE e 0 n l)) by LENGTH_GENLIST - = SUC (n + SUC (SUC n * PRE (LENGTH l))) by induction hypothesis - = SUC (SUC (n + SUC n * PRE (LENGTH l))) by ADD_SUC - = SUC (SUC n + SUC n * PRE (LENGTH l)) by ADD_COMM, ADD_SUC - = SUC (SUC n * SUC (PRE (LENGTH l))) by MULT_SUC - = SUC (SUC n * LENGTH l) by SUC_PRE, 0 < LENGTH l - = SUC (SUC n * PRE (LENGTH (h::l))) by LENGTH, PRE_SUC_EQ -*) -val DILATE_0_LENGTH = store_thm( - "DILATE_0_LENGTH", - ``!l e n. LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l))``, - Induct >- - rw[] >> - rw_tac std_ss[LENGTH] >> - Cases_on `l = []` >- - rw[] >> - `0 < LENGTH l` by metis_tac[LENGTH_NIL, NOT_ZERO_LT_ZERO] >> - `LENGTH (DILATE e 0 n (h::l)) = LENGTH (h::(GENLIST (K e) n ++ DILATE e 0 n l))` by rw[DILATE_0_CONS] >> - `_ = SUC (LENGTH (GENLIST (K e) n ++ DILATE e 0 n l))` by rw[] >> - `_ = SUC (n + LENGTH (DILATE e 0 n l))` by rw[] >> - `_ = SUC (n + SUC (SUC n * PRE (LENGTH l)))` by rw[] >> - `_ = SUC (SUC (n + SUC n * PRE (LENGTH l)))` by rw[] >> - `_ = SUC (SUC n + SUC n * PRE (LENGTH l))` by rw[] >> - `_ = SUC (SUC n * SUC (PRE (LENGTH l)))` by rw[MULT_SUC] >> - `_ = SUC (SUC n * LENGTH l)` by rw[SUC_PRE] >> - rw[]); - -(* Theorem: LENGTH l <= LENGTH (DILATE e 0 n l) *) -(* Proof: - If l = [], - LENGTH (DILATE e 0 n []) - = LENGTH [] by DILATE_NIL - >= LENGTH [] - If l <> [], - Then ?h t. l = h::t by list_CASES - LENGTH (DILATE e 0 n (h::t)) - = SUC (SUC n * PRE (LENGTH (h::t))) by DILATE_0_LENGTH - = SUC (SUC n * PRE (SUC (LENGTH t))) by LENGTH - = SUC (SUC n * LENGTH t) by PRE - = SUC n * LENGTH t + 1 by ADD1 - >= LENGTH t + 1 by LE_MULT_CANCEL_LBARE, 0 < SUC n - = SUC (LENGTH t) by ADD1 - = LENGTH (h::t) by LENGTH -*) -val DILATE_0_LENGTH_LOWER = store_thm( - "DILATE_0_LENGTH_LOWER", - ``!l e n. LENGTH l <= LENGTH (DILATE e 0 n l)``, - rw[DILATE_0_LENGTH] >> - `?h t. l = h::t` by metis_tac[list_CASES] >> - rw[]); - -(* Theorem: LENGTH (DILATE e 0 n l) <= SUC (SUC n * PRE (LENGTH l)) *) -(* Proof: - If l = [], - LENGTH (DILATE e 0 n []) - = LENGTH [] by DILATE_NIL - = 0 by LENGTH_NIL - SUC (SUC n * PRE (LENGTH [])) - = SUC (SUC n * PRE 0) by LENGTH_NIL - = SUC 0 by PRE, MULT_0 - > 0 by LESS_SUC - If l <> [], - LENGTH (DILATE e 0 n l) - = SUC (SUC n * PRE (LENGTH l)) by DILATE_0_LENGTH -*) -val DILATE_0_LENGTH_UPPER = store_thm( - "DILATE_0_LENGTH_UPPER", - ``!l e n. LENGTH (DILATE e 0 n l) <= SUC (SUC n * PRE (LENGTH l))``, - rw[DILATE_0_LENGTH]); - -(* Theorem: k < LENGTH (DILATE e 0 n l) ==> - (EL k (DILATE e 0 n l) = if k MOD (SUC n) = 0 then EL (k DIV (SUC n)) l else e) *) -(* Proof: - Let m = SUC n, then 0 < m. - By induction on l. - Base: !k. k < LENGTH (DILATE e 0 n []) ==> (EL k (DILATE e 0 n []) = if k MOD m = 0 then EL (k DIV m) [] else e) - Note LENGTH (DILATE e 0 n []) - = LENGTH [] by DILATE_NIL - = 0 by LENGTH_NIL - Thus k < 0 <=> F by NOT_ZERO_LT_ZERO - Step: !k. k < LENGTH (DILATE e 0 n l) ==> (EL k (DILATE e 0 n l) = if k MOD m = 0 then EL (k DIV m) l else e) ==> - !h k. k < LENGTH (DILATE e 0 n (h::l)) ==> (EL k (DILATE e 0 n (h::l)) = if k MOD m = 0 then EL (k DIV m) (h::l) else e) - Note LENGTH (DILATE e 0 n [h]) = 1 by DILATE_SING - and LENGTH (DILATE e 0 n (h::l)) - = SUC (m * PRE (LENGTH (h::l))) by DILATE_0_LENGTH, n <> 0 - = SUC (m * PRE (SUC (LENGTH l))) by LENGTH - = SUC (m * LENGTH l) by PRE - - If l = [], - Then DILATE e 0 n [h] = [h] by DILATE_SING - and LENGTH (DILATE e 0 n [h]) = 1 by LENGTH - so k < 1 means k = 0. - and 0 DIV m = 0 by ZERO_DIV, 0 < m - and 0 MOD m = 0 by ZERO_MOD, 0 < m - Thus EL k [h] = EL (k DIV m) [h]. - - If l <> [], - Let t = h:: GENLIST (K e) n. - Note LENGTH t = SUC n = m by LENGTH_GENLIST - If k < m, - Then k MOD m = k by LESS_MOD, k < m - EL k (DILATE e 0 n (h::l)) - = EL k (t ++ DILATE e 0 n l) by DILATE_0_CONS - = EL k t by EL_APPEND, k < LENGTH t - If k = 0, i.e. k MOD m = 0. - EL 0 t - = EL 0 (h:: GENLIST (K e) (PRE n)) by notation of t - = h - = EL (0 DIV m) (h::l) by EL, HD - If k <> 0, i.e. k MOD m <> 0. - EL k t - = EL k (h:: GENLIST (K e) n) by notation of t - = EL (PRE k) (GENLIST (K e) n) by EL_CONS - = (K e) (PRE k) by EL_GENLIST, PRE k < PRE m = n - = e by application of K - If ~(k < m), then m <= k. - Given k < LENGTH (DILATE e 0 n (h::l)) - or k < SUC (m * LENGTH l) by above - ==> k - m < SUC (m * LENGTH l) - m by m <= k - = SUC (m * LENGTH l - m) by SUB - = SUC (m * (LENGTH l - 1)) by LEFT_SUB_DISTRIB - = SUC (m * PRE (LENGTH l)) by PRE_SUB1 - or k - m < LENGTH (MDILATE e n l) by MDILATE_LENGTH - Thus (k - m) MOD m = k MOD m by SUB_MOD - and (k - m) DIV m = k DIV m - 1 by SUB_DIV - If k MOD m = 0, - Note 0 < k DIV m by DIVIDES_MOD_0, DIV_POS - EL k (t ++ DILATE e 0 n l) - = EL (k - m) (DILATE e 0 n l) by EL_APPEND, m <= k - = EL (k DIV m - 1) l by induction hypothesis, (k - m) MOD m = 0 - = EL (PRE (k DIV m)) l by PRE_SUB1 - = EL (k DIV m) (h::l) by EL_CONS, 0 < k DIV m - If k MOD m <> 0, - EL k (t ++ DILATE e 0 n l) - = EL (k - m) (DILATE e 0 n l) by EL_APPEND, n <= k - = e by induction hypothesis, (k - m) MOD n <> 0 -*) -Theorem DILATE_0_EL: - !l e n k. k < LENGTH (DILATE e 0 n l) ==> - (EL k (DILATE e 0 n l) = if k MOD (SUC n) = 0 then EL (k DIV (SUC n)) l else e) -Proof - ntac 3 strip_tac >> - `0 < SUC n` by decide_tac >> - qabbrev_tac `m = SUC n` >> - Induct_on `l` >- - rw[] >> - rpt strip_tac >> - `LENGTH (DILATE e 0 n [h]) = 1` by rw[DILATE_SING] >> - `LENGTH (DILATE e 0 n (h::l)) = SUC (m * LENGTH l)` by rw[DILATE_0_LENGTH, Abbr`m`] >> - Cases_on `l = []` >| [ - `k = 0` by rw[] >> - `k MOD m = 0` by rw[] >> - `k DIV m = 0` by rw[ZERO_DIV] >> - rw_tac std_ss[DILATE_SING], - qabbrev_tac `t = h::GENLIST (K e) n` >> - `DILATE e 0 n (h::l) = t ++ DILATE e 0 n l` by rw[DILATE_0_CONS, Abbr`t`] >> - `m = LENGTH t` by rw[Abbr`t`] >> - Cases_on `k < m` >| [ - `k MOD m = k` by rw[] >> - `EL k (DILATE e 0 n (h::l)) = EL k t` by rw[EL_APPEND] >> - Cases_on `k = 0` >| [ - `EL 0 t = h` by rw[Abbr`t`] >> - rw[ZERO_DIV], - `PRE m = n` by rw[Abbr`m`] >> - `PRE k < n` by decide_tac >> - `EL k t = EL (PRE k) (GENLIST (K e) n)` by rw[EL_CONS, Abbr`t`] >> - `_ = (K e) (PRE k)` by rw[EL_GENLIST] >> - rw[] - ], - `m <= k` by decide_tac >> - `EL k (t ++ DILATE e 0 n l) = EL (k - m) (DILATE e 0 n l)` by simp[EL_APPEND] >> - `k - m < LENGTH (DILATE e 0 n l)` by rw[DILATE_0_LENGTH] >> - `(k - m) MOD m = k MOD m` by simp[SUB_MOD] >> - `(k - m) DIV m = k DIV m - 1` by simp[SUB_DIV] >> - Cases_on `k MOD m = 0` >| [ - `0 < k DIV m` by rw[DIVIDES_MOD_0, DIV_POS] >> - `EL (k - m) (DILATE e 0 n l) = EL (k DIV m - 1) l` by rw[] >> - `_ = EL (PRE (k DIV m)) l` by rw[PRE_SUB1] >> - `_ = EL (k DIV m) (h::l)` by rw[EL_CONS] >> - rw[], - `EL (k - m) (DILATE e 0 n l) = e` by rw[] >> - rw[] - ] - ] - ] -QED - -(* This is a milestone theorem. *) - -(* Theorem: (DILATE e 0 n l = []) <=> (l = []) *) -(* Proof: - If part: DILATE e 0 n l = [] ==> l = [] - By contradiction, suppose l <> []. - If n = 0, - Then DILATE e n 0 l = l by DILATE_0_0 - This contradicts DILATE e n 0 l = []. - If n <> 0, - Then LENGTH (DILATE e 0 n l) - = SUC (SUC n * PRE (LENGTH l)) by DILATE_0_LENGTH - <> 0 by SUC_NOT - So (DILATE e 0 n l) <> [] by LENGTH_NIL - This contradicts DILATE e 0 n l = [] - Only-if part: l = [] ==> DILATE e 0 n l = [] - True by DILATE_NIL -*) -val DILATE_0_EQ_NIL = store_thm( - "DILATE_0_EQ_NIL", - ``!l e n. (DILATE e 0 n l = []) <=> (l = [])``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - Cases_on `n = 0` >| [ - `DILATE e 0 0 l = l` by rw[GSYM DILATE_0_0] >> - metis_tac[], - `LENGTH (DILATE e 0 n l) = SUC (SUC n * PRE (LENGTH l))` by rw[DILATE_0_LENGTH] >> - `LENGTH (DILATE e 0 n l) <> 0` by decide_tac >> - metis_tac[LENGTH_EQ_0] - ]); - -(* Theorem: LAST (DILATE e 0 n l) = LAST l *) -(* Proof: - If l = [], - LAST (DILATE e 0 n []) - = LAST [] by DILATE_NIL - If l <> [], - If n = 0, - LAST (DILATE e 0 0 l) - = LAST l by DILATE_0_0 - If n <> 0, - Then DILATE e 0 n l <> [] by DILATE_0_EQ_NIL - or LENGTH (DILATE e 0 n l) <> 0 by LENGTH_NIL - Let m = SUC n, then 0 < m by LESS_0 - Note PRE (LENGTH (DILATE e 0 n l)) - = PRE (SUC (m * PRE (LENGTH l))) by DILATE_0_LENGTH - = m * PRE (LENGTH l) by PRE - Let k = PRE (LENGTH (DILATE e 0 n l)). - Then k < LENGTH (DILATE e 0 n l) by PRE x < x - and k MOD m = 0 by MOD_EQ_0, MULT_COMM, 0 < m - and k DIV m = PRE (LENGTH l) by MULT_DIV, MULT_COMM - - LAST (DILATE e 0 n l) - = EL k (DILATE e 0 n l) by LAST_EL - = EL (k DIV m) l by DILATE_0_EL - = EL (PRE (LENGTH l)) l by above - = LAST l by LAST_EL -*) -val DILATE_0_LAST = store_thm( - "DILATE_0_LAST", - ``!l e n. LAST (DILATE e 0 n l) = LAST l``, - rpt strip_tac >> - Cases_on `l = []` >- - rw[] >> - Cases_on `n = 0` >- - rw[DILATE_0_0] >> - `0 < n` by decide_tac >> - `DILATE e 0 n l <> []` by rw[DILATE_0_EQ_NIL] >> - `LENGTH (DILATE e 0 n l) <> 0` by metis_tac[LENGTH_NIL] >> - qabbrev_tac `k = PRE (LENGTH (DILATE e 0 n l))` >> - rw[LAST_EL] >> - `0 < SUC n` by decide_tac >> - qabbrev_tac `m = SUC n` >> - `k = m * PRE (LENGTH l)` by rw[DILATE_0_LENGTH, Abbr`k`, Abbr`m`] >> - `k MOD m = 0` by metis_tac[MOD_EQ_0, MULT_COMM] >> - `k DIV m = PRE (LENGTH l)` by metis_tac[MULT_DIV, MULT_COMM] >> - `k < LENGTH (DILATE e 0 n l)` by simp[Abbr`k`] >> - Q.RM_ABBREV_TAC ‘k’ >> - rw[DILATE_0_EL]); - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/helperNumScript.sml b/examples/algebra/lib/helperNumScript.sml deleted file mode 100644 index 9f9806a31d..0000000000 --- a/examples/algebra/lib/helperNumScript.sml +++ /dev/null @@ -1,4782 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Helper Theorems - a collection of useful results -- for Numbers. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "helperNum"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* open dependent theories *) -open pred_setTheory; - -(* val _ = load "dividesTheory"; *) -(* val _ = load "gcdTheory"; *) -open arithmeticTheory dividesTheory -open gcdTheory; (* for P_EUCLIDES *) - - -(* ------------------------------------------------------------------------- *) -(* HelperNum Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - SQ n = n * n - HALF n = n DIV 2 - TWICE n = 2 * n - n divides m = divides n m - coprime x y = gcd x y = 1 -*) -(* Definitions and Theorems (# are exported): - - Arithmetic Theorems: - THREE |- 3 = SUC 2 - FOUR |- 4 = SUC 3 - FIVE |- 5 = SUC 4 - num_nchotomy |- !m n. m = n \/ m < n \/ n < m - ZERO_LE_ALL |- !n. 0 <= n - NOT_ZERO |- !n. n <> 0 <=> 0 < n - ONE_NOT_0 |- 1 <> 0 - ONE_LT_POS |- !n. 1 < n ==> 0 < n - ONE_LT_NONZERO |- !n. 1 < n ==> n <> 0 - NOT_LT_ONE |- !n. ~(1 < n) <=> (n = 0) \/ (n = 1) - NOT_ZERO_GE_ONE |- !n. n <> 0 <=> 1 <= n - LE_ONE |- !n. n <= 1 <=> (n = 0) \/ (n = 1) - LESS_SUC |- !n. n < SUC n - PRE_LESS |- !n. 0 < n ==> PRE n < n - SUC_EXISTS |- !n. 0 < n ==> ?m. n = SUC m - SUC_POS |- !n. 0 < SUC n - SUC_NOT_ZERO |- !n. SUC n <> 0 - ONE_NOT_ZERO |- 1 <> 0 - SUC_ADD_SUC |- !m n. SUC m + SUC n = m + n + 2 - SUC_MULT_SUC |- !m n. SUC m * SUC n = m * n + m + n + 1 - SUC_EQ |- !m n. (SUC m = SUC n) <=> (m = n) - TWICE_EQ_0 |- !n. (TWICE n = 0) <=> (n = 0) - SQ_EQ_0 |- !n. (SQ n = 0) <=> (n = 0) - SQ_EQ_1 |- !n. (SQ n = 1) <=> (n = 1) - MULT3_EQ_0 |- !x y z. (x * y * z = 0) <=> ((x = 0) \/ (y = 0) \/ (z = 0)) - MULT3_EQ_1 |- !x y z. (x * y * z = 1) <=> ((x = 1) /\ (y = 1) /\ (z = 1)) - SQ_0 |- 0 ** 2 = 0 - EXP_2_EQ_0 |- !n. (n ** 2 = 0) <=> (n = 0) - LE_MULT_LCANCEL_IMP |- !m n p. n <= p ==> m * n <= m * p - - Maximum and minimum: - MAX_ALT |- !m n. MAX m n = if m <= n then n else m - MIN_ALT |- !m n. MIN m n = if m <= n then m else n - MAX_SWAP |- !f. (!x y. x <= y ==> f x <= f y) ==> !x y. f (MAX x y) = MAX (f x) (f y) - MIN_SWAP |- !f. (!x y. x <= y ==> f x <= f y) ==> !x y. f (MIN x y) = MIN (f x) (f y) - SUC_MAX |- !m n. SUC (MAX m n) = MAX (SUC m) (SUC n) - SUC_MIN |- !m n. SUC (MIN m n) = MIN (SUC m) (SUC n) - MAX_SUC |- !m n. MAX (SUC m) (SUC n) = SUC (MAX m n) - MIN_SUC |- !m n. MIN (SUC m) (SUC n) = SUC (MIN m n) - MAX_LESS |- !x y n. x < n /\ y < n ==> MAX x y < n - MAX_CASES |- !m n. (MAX n m = n) \/ (MAX n m = m) - MIN_CASES |- !m n. (MIN n m = n) \/ (MIN n m = m) - MAX_EQ_0 |- !m n. (MAX n m = 0) <=> (n = 0) /\ (m = 0) - MIN_EQ_0 |- !m n. (MIN n m = 0) <=> (n = 0) \/ (m = 0) - MAX_IS_MAX |- !m n. m <= MAX m n /\ n <= MAX m n - MIN_IS_MIN |- !m n. MIN m n <= m /\ MIN m n <= n - MAX_ID |- !m n. MAX (MAX m n) n = MAX m n /\ MAX m (MAX m n) = MAX m n - MIN_ID |- !m n. MIN (MIN m n) n = MIN m n /\ MIN m (MIN m n) = MIN m n - MAX_LE_PAIR |- !a b c d. a <= b /\ c <= d ==> MAX a c <= MAX b d - MIN_LE_PAIR |- !a b c d. a <= b /\ c <= d ==> MIN a c <= MIN b d - MAX_ADD |- !a b c. MAX a (b + c) <= MAX a b + MAX a c - MIN_ADD |- !a b c. MIN a (b + c) <= MIN a b + MIN a c - MAX_1_POS |- !n. 0 < n ==> MAX 1 n = n - MIN_1_POS |- !n. 0 < n ==> MIN 1 n = 1 - MAX_LE_SUM |- !m n. MAX m n <= m + n - MIN_LE_SUM |- !m n. MIN m n <= m + n - MAX_1_EXP |- !n m. MAX 1 (m ** n) = MAX 1 m ** n - MIN_1_EXP |- !n m. MIN 1 (m ** n) = MIN 1 m ** n - - Arithmetic Manipulations: - MULT_POS |- !m n. 0 < m /\ 0 < n ==> 0 < m * n - MULT_COMM_ASSOC |- !m n p. m * (n * p) = n * (m * p) - MULT_RIGHT_CANCEL |- !m n p. (n * p = m * p) <=> (p = 0) \/ (n = m) - MULT_LEFT_CANCEL |- !m n p. (p * n = p * m) <=> (p = 0) \/ (n = m) - MULT_TO_DIV |- !m n. 0 < n ==> (n * m DIV n = m) - MULT_ASSOC_COMM |- !m n p. m * (n * p) = m * p * n - MULT_LEFT_ID |- !n. 0 < n ==> !m. (m * n = n) <=> (m = 1) - MULT_RIGHT_ID |- !n. 0 < n ==> !m. (n * m = n) <=> (m = 1) - MULT_EQ_SELF |- !n. 0 < n ==> !m. (n * m = n) <=> (m = 1) - SQ_EQ_SELF |- !n. (n * n = n) <=> (n = 0) \/ (n = 1) - EXP_EXP_BASE_LE |- !b c m n. m <= n /\ 0 < c ==> b ** c ** m <= b ** c ** n - EXP_EXP_LE_MONO_IMP |- !a b n. a <= b ==> a ** n <= b ** n - EXP_BY_ADD_SUB_LE |- !m n. m <= n ==> !p. p ** n = p ** m * p ** (n - m) - EXP_BY_ADD_SUB_LT |- !m n. m < n ==> !p. p ** n = p ** m * p ** (n - m) - EXP_SUC_DIV |- !m n. 0 < m ==> m ** SUC n DIV m = m ** n - SELF_LE_SQ |- !n. n <= n ** 2 - LE_MONO_ADD2 |- !a b c d. a <= b /\ c <= d ==> a + c <= b + d - LT_MONO_ADD2 |- !a b c d. a < b /\ c < d ==> a + c < b + d - LE_MONO_MULT2 |- !a b c d. a <= b /\ c <= d ==> a * c <= b * d - LT_MONO_MULT2 |- !a b c d. a < b /\ c < d ==> a * c < b * d - SUM_LE_PRODUCT |- !m n. 1 < m /\ 1 < n ==> m + n <= m * n - MULTIPLE_SUC_LE |- !n k. 0 < n ==> k * n + 1 <= (k + 1) * n - - Simple Theorems: - ADD_EQ_2 |- !m n. 0 < m /\ 0 < n /\ (m + n = 2) ==> (m = 1) /\ (n = 1) - EVEN_0 |- EVEN 0 - ODD_1 |- ODD 1 - EVEN_2 |- EVEN 2 - EVEN_SQ |- !n. EVEN (n ** 2) <=> EVEN n - ODD_SQ |- !n. ODD (n ** 2) <=> ODD n - EQ_PARITY |- !a b. EVEN (2 * a + b) <=> EVEN b - ODD_MOD2 |- !x. ODD x <=> (x MOD 2 = 1) - EVEN_ODD_SUC |- !n. (EVEN n <=> ODD (SUC n)) /\ (ODD n <=> EVEN (SUC n)) - EVEN_ODD_PRE |- !n. 0 < n ==> (EVEN n <=> ODD (PRE n)) /\ (ODD n <=> EVEN (PRE n)) - EVEN_PARTNERS |- !n. EVEN (n * (n + 1)) - EVEN_HALF |- !n. EVEN n ==> (n = 2 * HALF n) - ODD_HALF |- !n. ODD n ==> (n = 2 * HALF n + 1) - EVEN_SUC_HALF |- !n. EVEN n ==> (HALF (SUC n) = HALF n) - ODD_SUC_HALF |- !n. ODD n ==> (HALF (SUC n) = SUC (HALF n)) - HALF_EQ_0 |- !n. (HALF n = 0) <=> (n = 0) \/ (n = 1) - HALF_EQ_SELF |- !n. (HALF n = n) <=> (n = 0) - HALF_LT |- !n. 0 < n ==> HALF n < n - HALF_ADD1_LT |- !n. 2 < n ==> 1 + HALF n < n - HALF_TWICE |- !n. HALF (TWICE n) = n - HALF_MULT |- !m n. n * HALF m <= HALF (n * m) - TWO_HALF_LE_THM |- !n. 2 * HALF n <= n /\ n <= SUC (2 * HALF n) - TWO_HALF_TIMES_LE |- !m n. TWICE (HALF n * m) <= n * m - HALF_ADD1_LE |- !n. 0 < n ==> 1 + HALF n <= n - HALF_SQ_LE |- !n. HALF n ** 2 <= n ** 2 DIV 4 - HALF_LE |- !n. HALF n <= n - HALF_LE_MONO |- !x y. x <= y ==> HALF x <= HALF y - HALF_SUC |- !n. HALF (SUC n) <= n - HALF_SUC_SUC |- !n. 0 < n ==> HALF (SUC (SUC n)) <= n - HALF_SUC_LE |- !n m. n < HALF (SUC m) ==> 2 * n + 1 <= m - HALF_EVEN_LE |- !n m. 2 * n < m ==> n <= HALF m - HALF_ODD_LT |- !n m. 2 * n + 1 < m ==> n < HALF m - MULT_EVEN |- !n. EVEN n ==> !m. m * n = TWICE m * HALF n - MULT_ODD |- !n. ODD n ==> !m. m * n = m + TWICE m * HALF n - EVEN_MOD_EVEN |- !m. EVEN m /\ m <> 0 ==> !n. EVEN n <=> EVEN (n MOD m) - EVEN_MOD_ODD |- !m. EVEN m /\ m <> 0 ==> !n. ODD n <=> ODD (n MOD m) - - SUB_SUB_SUB |- !a b c. c <= a ==> (a - b - (a - c) = c - b) - ADD_SUB_SUB |- !a b c. c <= a ==> (a + b - (a - c) = c + b) - SUB_EQ_ADD |- !p. 0 < p ==> !m n. (m - n = p) <=> (m = n + p) - MULT_EQ_LESS_TO_MORE |- !a b c d. 0 < a /\ 0 < b /\ a < c /\ (a * b = c * d) ==> d < b - LE_IMP_REVERSE_LT |- !a b c d. 0 < c /\ 0 < d /\ a * b <= c * d /\ d < b ==> a < c - - Exponential Theorems: - EXP_0 |- !n. n ** 0 = 1 - EXP_2 |- !n. n ** 2 = n * n - EXP_NONZERO |- !m n. m <> 0 ==> m ** n <> 0 - EXP_POS |- !m n. 0 < m ==> 0 < m ** n - EXP_EQ_SELF |- !n m. 0 < m ==> (n ** m = n) <=> (m = 1) \/ (n = 0) \/ (n = 1) - EXP_LE |- !n b. 0 < n ==> b <= b ** n - EXP_LT |- !n b. 1 < b /\ 1 < n ==> b < b ** n - EXP_LCANCEL |- !a b c n m. 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c) - EXP_RCANCEL |- !a b c n m. 0 < a /\ n < m /\ (b * a ** n = c * a ** m) ==> ?d. 0 < d /\ (b = c * a ** d) - ONE_LE_EXP |- !m n. 0 < m ==> 1 <= m ** n - EXP_EVEN |- !n. EVEN n ==> !m. m ** n = SQ m ** HALF n - EXP_ODD |- !n. ODD n ==> !m. m ** n = m * SQ m ** HALF n - EXP_THM |- !m n. m ** n = - if n = 0 then 1 else if n = 1 then m - else if EVEN n then SQ m ** HALF n - else m * SQ m ** HALF n - EXP_EQN |- !m n. m ** n = - if n = 0 then 1 - else if EVEN n then SQ m ** HALF n - else m * SQ m ** HALF n - EXP_EQN_ALT |- !m n. m ** n = - if n = 0 then 1 else (if EVEN n then 1 else m) * SQ m ** HALF n - EXP_ALT_EQN |- !m n. m ** n = - if n = 0 then 1 else (if EVEN n then 1 else m) * (m ** 2) ** HALF n - EXP_MOD_EQN |- !b n m. 1 < m ==> - (b ** n MOD m = - if n = 0 then 1 - else (let result = SQ b ** HALF n MOD m - in if EVEN n then result else (b * result) MOD m)) - EXP_MOD_ALT |- !b n m. 1 < m ==> - (b ** n MOD m = - if n = 0 then 1 - else ((if EVEN n then 1 else b) * SQ b ** HALF n MOD m) MOD m) - EXP_EXP_SUC |- !x y n. x ** y ** SUC n = (x ** y) ** y ** n - EXP_LOWER_LE_LOW |- !n m. 1 + n * m <= (1 + m) ** n - EXP_LOWER_LT_LOW |- !n m. 0 < m /\ 1 < n ==> 1 + n * m < (1 + m) ** n - EXP_LOWER_LE_HIGH |- !n m. n * m ** (n - 1) + m ** n <= (1 + m) ** n - SUC_X_LT_2_EXP_X |- !n. 1 < n ==> SUC n < 2 ** n - - DIVIDES Theorems: - DIV_EQUAL_0 |- !m n. 0 < n ==> ((m DIV n = 0) <=> m < n) - DIV_POS |- !m n. 0 < m /\ m <= n ==> 0 < n DIV m - DIV_EQ |- !x y z. 0 < z ==> (x DIV z = y DIV z <=> x - x MOD z = y - y MOD z) - ADD_DIV_EQ |- !n a b. a MOD n + b < n ==> (a + b) DIV n = a DIV n - DIV_LE |- !x y z. 0 < y /\ x <= y * z ==> x DIV y <= z - DIV_SOLVE |- !n. 0 < n ==> !x y. (x * n = y) ==> (x = y DIV n) - DIV_SOLVE_COMM |- !n. 0 < n ==> !x y. (n * x = y) ==> (x = y DIV n) - ONE_DIV |- !n. 1 < n ==> (1 DIV n = 0) - DIVIDES_ODD |- !n. ODD n ==> !m. m divides n ==> ODD m - DIVIDES_EVEN |- !m. EVEN m ==> !n. m divides n ==> EVEN n - EVEN_ALT |- !n. EVEN n <=> 2 divides n - ODD_ALT |- !n. ODD n <=> ~(2 divides n) - - DIV_MULT_LE |- !n. 0 < n ==> !q. q DIV n * n <= q - DIV_MULT_EQ |- !n. 0 < n ==> !q. n divides q <=> (q DIV n * n = q) - DIV_MULT_LESS_EQ |- !m n. 0 < m ==> m * (n DIV m) <= n /\ n < m * SUC (n DIV m) - DIV_LE_MONOTONE_REVERSE |- !x y. 0 < x /\ 0 < y /\ x <= y ==> !n. n DIV y <= n DIV x - DIVIDES_EQN |- !n. 0 < n ==> !m. n divides m <=> (m = m DIV n * n) - DIVIDES_EQN_COMM |- !n. 0 < n ==> !m. n divides m <=> (m = n * (m DIV n)) - SUB_DIV |- !m n. 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1) - SUB_DIV_EQN |- !m n. 0 < n ==> ((m - n) DIV n = if m < n then 0 else m DIV n - 1) - SUB_MOD_EQN |- !m n. 0 < n ==> ((m - n) MOD n = if m < n then 0 else m MOD n) - DIV_EQ_MULT |- !n. 0 < n ==> !k m. (m MOD n = 0) ==> ((k * n = m) <=> (k = m DIV n)) - MULT_LT_DIV |- !n. 0 < n ==> !k m. (m MOD n = 0) ==> (k * n < m <=> k < m DIV n) - LE_MULT_LE_DIV |- !n. 0 < n ==> !k m. (m MOD n = 0) ==> (m <= n * k <=> m DIV n <= k) - DIV_MOD_EQ_0 |- !m n. 0 < m ==> (n DIV m = 0 /\ n MOD m = 0 <=> n = 0) - DIV_LT_SUC |- !m n. 0 < m /\ 0 < n /\ n MOD m = 0 ==> n DIV SUC m < n DIV m - DIV_LT_MONOTONE_REVERSE |- !x y. 0 < x /\ 0 < y /\ x < y ==> - !n. 0 < n /\ n MOD x = 0 ==> n DIV y < n DIV x - - EXP_DIVIDES |- !a b n. 0 < n /\ a ** n divides b ==> a divides b - DIVIDES_EXP_BASE |- !a b n. prime a /\ 0 < n ==> (a divides b <=> a divides (b ** n)) - DIVIDES_MULTIPLE |- !m n. n divides m ==> !k. n divides (k * m) - DIVIDES_MULTIPLE_IFF |- !m n k. k <> 0 ==> (m divides n <=> k * m divides k * n) - DIVIDES_FACTORS |- !m n. 0 < n /\ n divides m ==> (m = n * (m DIV n)) - DIVIDES_COFACTOR |- !m n. 0 < n /\ n divides m ==> (m DIV n) divides m - MULTIPLY_DIV |- !n p q. 0 < n /\ n divides q ==> (p * (q DIV n) = p * q DIV n) - DIVIDES_MOD_MOD |- !m n. 0 < n /\ m divides n ==> !x. x MOD n MOD m = x MOD m - DIVIDES_CANCEL |- !k. 0 < k ==> !m n. m divides n <=> (m * k) divides (n * k) - DIVIDES_CANCEL_COMM |- !a b k. a divides b ==> (k * a) divides (k * b) - DIV_COMMON_FACTOR |- !m n. 0 < n /\ 0 < m ==> !x. n divides x ==> (m * x DIV (m * n) = x DIV n) - DIV_DIV_MULT |- !m n x. 0 < n /\ 0 < m /\ 0 < m DIV n /\ n divides m /\ m divides x /\ - (m DIV n) divides x ==> (x DIV (m DIV n) = n * (x DIV m)) - - Basic Divisibility: - divides_iff_equal |- !m n. 0 < n /\ n < 2 * m ==> (m divides n <=> n = m) - dividend_divides_divisor_multiple - |- !m n. 0 < m /\ n divides m ==> !t. m divides t * n <=> m DIV n divides t - divisor_pos |- !m n. 0 < n /\ m divides n ==> 0 < m - divides_pos |- !m n. 0 < n /\ m divides n ==> 0 < m /\ m <= n - divide_by_cofactor |- !m n. 0 < n /\ m divides n ==> (n DIV (n DIV m) = m) - divides_exp |- !n. 0 < n ==> !a b. a divides b ==> a divides b ** n - divides_linear |- !a b c. c divides a /\ c divides b ==> !h k. c divides h * a + k * b - divides_linear_sub |- !a b c. c divides a /\ c divides b ==> - !h k d. (h * a = k * b + d) ==> c divides d - power_divides_iff |- !p. 1 < p ==> !m n. p ** m divides p ** n <=> m <= n - prime_power_divides_iff |- !p. prime p ==> !m n. p ** m divides p ** n <=> m <= n - divides_self_power |- !n p. 0 < n /\ 1 < p ==> p divides p ** n - divides_eq_thm |- !a b. a divides b /\ 0 < b /\ b < 2 * a ==> (b = a) - factor_eq_cofactor |- !m n. 0 < m /\ m divides n ==> (m = n DIV m <=> n = m ** 2) - euclid_prime |- !p a b. prime p /\ p divides a * b ==> p divides a \/ p divides b - euclid_coprime |- !a b c. coprime a b /\ b divides a * c ==> b divides c - - Modulo Theorems: - MOD_EQN |- !n. 0 < n ==> !a b. (a MOD n = b) <=> ?c. (a = c * n + b) /\ b < n - MOD_MOD_EQN |- !n a b. 0 < n /\ b <= a ==> (a MOD n = b MOD n <=> ?c. a = b + c * n) - MOD_PLUS2 |- !n x y. 0 < n ==> (x + y) MOD n = (x + y MOD n) MOD n - MOD_PLUS3 |- !n. 0 < n ==> !x y z. (x + y + z) MOD n = (x MOD n + y MOD n + z MOD n) MOD n - MOD_ADD_ASSOC |- !n x y z. 0 < n /\ x < n /\ y < n /\ z < n ==> - (((x + y) MOD n + z) MOD n = (x + (y + z) MOD n) MOD n) - MOD_MULT_ASSOC |- !n x y z. 0 < n /\ x < n /\ y < n /\ z < n ==> - (((x * y) MOD n * z) MOD n = (x * (y * z) MOD n) MOD n) - MOD_ADD_INV |- !n x. 0 < n /\ x < n ==> (((n - x) MOD n + x) MOD n = 0) - MOD_MULITPLE_ZERO |- !n k. 0 < n /\ (k MOD n = 0) ==> !x. (k * x) MOD n = 0 - MOD_EQ_DIFF |- !n a b. 0 < n /\ (a MOD n = b MOD n) ==> ((a - b) MOD n = 0) - MOD_EQ |- !n a b. 0 < n /\ b <= a ==> (((a - b) MOD n = 0) <=> (a MOD n = b MOD n)) - MOD_EQ_0_IFF |- !m n. n < m ==> ((n MOD m = 0) <=> (n = 0)) - MOD_EXP |- !n. 0 < n ==> !a m. (a MOD n) ** m MOD n = a ** m MOD n - mod_add_eq_sub |- !n a b c d. b < n /\ c < n ==> - ((a + b) MOD n = (c + d) MOD n <=> - (a + (n - c)) MOD n = (d + (n - b)) MOD n) - mod_add_eq_sub_eq |- !n a b c d. 0 < n ==> - ((a + b) MOD n = (c + d) MOD n <=> - (a + (n - c MOD n)) MOD n = (d + (n - b MOD n)) MOD n) - mod_divides |- !n a b. 0 < n /\ b divides n /\ b divides a MOD n ==> b divides a - mod_divides_iff |- !n a b. 0 < n /\ b divides n ==> (b divides a MOD n <=> b divides a) - mod_divides_divides |- !n a b c. 0 < n /\ a MOD n = b /\ c divides n /\ c divides a ==> c divides b - mod_divides_divides_iff - |- !n a b c. 0 < n /\ a MOD n = b /\ c divides n ==> - (c divides a <=> c divides b) - mod_eq_divides |- !n a b c. 0 < n /\ a MOD n = b MOD n /\ c divides n /\ c divides a ==> - c divides b - mod_eq_divides_iff |- !n a b c. 0 < n /\ a MOD n = b MOD n /\ c divides n ==> - (c divides a <=> c divides b) - mod_mult_eq_mult |- !m n a b. coprime m n /\ 0 < a /\ a < 2 * n /\ 0 < b /\ b < 2 * m /\ - (m * a) MOD (m * n) = (n * b) MOD (m * n) ==> a = n /\ b = m - - Even and Odd Parity: - EVEN_EXP |- !m n. 0 < n /\ EVEN m ==> EVEN (m ** n) - ODD_EXP |- !m n. 0 < n /\ ODD m ==> ODD (m ** n) - power_parity |- !n. 0 < n ==> !m. (EVEN m <=> EVEN (m ** n)) /\ (ODD m <=> ODD (m ** n)) - EXP_2_EVEN |- !n. 0 < n ==> EVEN (2 ** n) - EXP_2_PRE_ODD |- !n. 0 < n ==> ODD (2 ** n - 1) - - Modulo Inverse: - GCD_LINEAR |- !j k. 0 < j ==> ?p q. p * j = q * k + gcd j k - EUCLID_LEMMA |- !p x y. prime p ==> (((x * y) MOD p = 0) <=> (x MOD p = 0) \/ (y MOD p = 0)) - MOD_MULT_LCANCEL |- !p x y z. prime p /\ (x * y) MOD p = (x * z) MOD p /\ x MOD p <> 0 ==> - y MOD p = z MOD p - MOD_MULT_RCANCEL |- !p x y z. prime p /\ (y * x) MOD p = (z * x) MOD p /\ x MOD p <> 0 ==> - y MOD p = z MOD p - MOD_MULT_INV_EXISTS |- !p x. prime p /\ 0 < x /\ x < p ==> ?y. 0 < y /\ y < p /\ ((y * x) MOD p = 1) - MOD_MULT_INV_DEF |- !p x. prime p /\ 0 < x /\ x < p ==> - 0 < MOD_MULT_INV p x /\ MOD_MULT_INV p x < p /\ ((MOD_MULT_INV p x * x) MOD p = 1) - - FACTOR Theorems: - PRIME_FACTOR_PROPER |- !n. 1 < n /\ ~prime n ==> ?p. prime p /\ p < n /\ (p divides n) - FACTOR_OUT_POWER |- !n p. 0 < n /\ 1 < p /\ p divides n ==> - ?m. (p ** m) divides n /\ ~(p divides (n DIV p ** m)) - - Useful Theorems: - binomial_add |- !a b. (a + b) ** 2 = a ** 2 + b ** 2 + 2 * a * b - binomial_sub |- !a b. b <= a ==> ((a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b) - binomial_means |- !a b. 2 * a * b <= a ** 2 + b ** 2 - binomial_sub_sum |- !a b. b <= a ==> (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 - binomial_sub_add |- !a b. b <= a ==> ((a - b) ** 2 + 4 * a * b = (a + b) ** 2) - difference_of_squares|- !a b. a ** 2 - b ** 2 = (a - b) * (a + b) - difference_of_squares_alt - |- !a b. a * a - b * b = (a - b) * (a + b) - binomial_2 |- !m n. (m + n) ** 2 = m ** 2 + n ** 2 + TWICE m * n - SUC_SQ |- !n. SUC n ** 2 = SUC (n ** 2) + TWICE n - SQ_LE |- !m n. m <= n ==> SQ m <= SQ n - EVEN_PRIME |- !n. EVEN n /\ prime n <=> n = 2 - ODD_PRIME |- !n. prime n /\ n <> 2 ==> ODD n - TWO_LE_PRIME |- !p. prime p ==> 2 <= p - NOT_PRIME_4 |- ~prime 4 - prime_divides_prime |- !n m. prime n /\ prime m ==> (n divides m <=> (n = m)) - ALL_PRIME_FACTORS_MOD_EQ_1 |- !m n. 0 < m /\ 1 < n /\ - (!p. prime p /\ p divides m ==> (p MOD n = 1)) ==> (m MOD n = 1) - prime_divides_power |- !p n. prime p /\ 0 < n ==> !b. p divides b ** n <=> p divides b - prime_divides_self_power |- !p. prime p ==> !n. 0 < n ==> p divides p ** n - power_eq_prime_power |- !p. prime p ==> !b n m. 0 < m /\ (b ** n = p ** m) ==> - ?k. (b = p ** k) /\ (k * n = m) - POWER_EQ_SELF |- !n. 1 < n ==> !m. (n ** m = n) <=> (m = 1) - - LESS_HALF_IFF |- !n k. k < HALF n <=> k + 1 < n - k - MORE_HALF_IMP |- !n k. HALF n < k ==> n - k <= HALF n - MONOTONE_MAX |- !f m. (!k. k < m ==> f k < f (k + 1)) ==> !k. k < m ==> f k < f m - MULTIPLE_INTERVAL |- !n m. n divides m ==> !x. m - n < x /\ x < m + n /\ n divides x ==> (x = m) - MOD_SUC_EQN |- !m n. 0 < m ==> (SUC (n MOD m) = SUC n MOD m + (SUC n DIV m - n DIV m) * m) - ONE_LT_HALF_SQ |- !n. 1 < n ==> 1 < HALF (n ** 2) - EXP_2_HALF |- !n. 0 < n ==> (HALF (2 ** n) = 2 ** (n - 1)) - HALF_MULT_EVEN |- !m n. EVEN n ==> (HALF (m * n) = m * HALF n) - MULT_LT_IMP_LT |- !m n k. 0 < k /\ k * m < n ==> m < n - MULT_LE_IMP_LE |- !m n k. 0 < k /\ k * m <= n ==> m <= n - HALF_EXP_5 |- !n. n * HALF (SQ n ** 2) <= HALF (n ** 5) - LE_TWICE_ALT |- !m n. n <= TWICE m <=> n <> 0 ==> HALF (n - 1) < m - HALF_DIV_TWO_POWER |- !m n. HALF n DIV 2 ** m = n DIV 2 ** SUC m - fit_for_10 |- 1 + 2 + 3 + 4 = 10 - fit_for_100 |- 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 = 100 -*) - -(* ------------------------------------------------------------------------- *) -(* Arithmetic Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 3 = SUC 2 *) -(* Proof: by arithmetic *) -val THREE = store_thm( - "THREE", - ``3 = SUC 2``, - decide_tac); - -(* Theorem: 4 = SUC 3 *) -(* Proof: by arithmetic *) -val FOUR = store_thm( - "FOUR", - ``4 = SUC 3``, - decide_tac); - -(* Theorem: 5 = SUC 4 *) -(* Proof: by arithmetic *) -val FIVE = store_thm( - "FIVE", - ``5 = SUC 4``, - decide_tac); - -(* Overload squaring *) -val _ = overload_on("SQ", ``\n. n * n``); (* not n ** 2 *) - -(* Overload half of a number *) -val _ = overload_on("HALF", ``\n. n DIV 2``); - -(* Overload twice of a number *) -val _ = overload_on("TWICE", ``\n. 2 * n``); - -(* make divides infix *) -val _ = set_fixity "divides" (Infixl 480); (* relation is 450, +/- is 500, * is 600. *) - -(* Theorem alias *) -Theorem num_nchotomy = arithmeticTheory.LESS_LESS_CASES; -(* val num_nchotomy = |- !m n. m = n \/ m < n \/ n < m: thm *) - -(* Theorem alias *) -Theorem ZERO_LE_ALL = arithmeticTheory.ZERO_LESS_EQ; -(* val ZERO_LE_ALL = |- !n. 0 <= n: thm *) - -(* Theorem alias *) -Theorem NOT_ZERO = arithmeticTheory.NOT_ZERO_LT_ZERO; -(* val NOT_ZERO = |- !n. n <> 0 <=> 0 < n: thm *) - -(* Extract theorem *) -Theorem ONE_NOT_0 = DECIDE``1 <> 0``; -(* val ONE_NOT_0 = |- 1 <> 0: thm *) - -(* Theorem: !n. 1 < n ==> 0 < n *) -(* Proof: by arithmetic. *) -val ONE_LT_POS = store_thm( - "ONE_LT_POS", - ``!n. 1 < n ==> 0 < n``, - decide_tac); - -(* Theorem: !n. 1 < n ==> n <> 0 *) -(* Proof: by arithmetic. *) -val ONE_LT_NONZERO = store_thm( - "ONE_LT_NONZERO", - ``!n. 1 < n ==> n <> 0``, - decide_tac); - -(* Theorem: ~(1 < n) <=> (n = 0) \/ (n = 1) *) -(* Proof: by arithmetic. *) -val NOT_LT_ONE = store_thm( - "NOT_LT_ONE", - ``!n. ~(1 < n) <=> (n = 0) \/ (n = 1)``, - decide_tac); - -(* Theorem: n <> 0 <=> 1 <= n *) -(* Proof: by arithmetic. *) -val NOT_ZERO_GE_ONE = store_thm( - "NOT_ZERO_GE_ONE", - ``!n. n <> 0 <=> 1 <= n``, - decide_tac); - -(* Theorem: n <= 1 <=> (n = 0) \/ (n = 1) *) -(* Proof: by arithmetic *) -val LE_ONE = store_thm( - "LE_ONE", - ``!n. n <= 1 <=> (n = 0) \/ (n = 1)``, - decide_tac); - -(* arithmeticTheory.LESS_EQ_SUC_REFL |- !m. m <= SUC m *) - -(* Theorem: n < SUC n *) -(* Proof: by arithmetic. *) -val LESS_SUC = store_thm( - "LESS_SUC", - ``!n. n < SUC n``, - decide_tac); - -(* Theorem: 0 < n ==> PRE n < n *) -(* Proof: by arithmetic. *) -val PRE_LESS = store_thm( - "PRE_LESS", - ``!n. 0 < n ==> PRE n < n``, - decide_tac); - -(* Theorem: 0 < n ==> ?m. n = SUC m *) -(* Proof: by NOT_ZERO_LT_ZERO, num_CASES. *) -val SUC_EXISTS = store_thm( - "SUC_EXISTS", - ``!n. 0 < n ==> ?m. n = SUC m``, - metis_tac[NOT_ZERO_LT_ZERO, num_CASES]); - -(* prim_recTheory.LESS_0 |- !n. 0 < SUC n *) -(* Theorem: 0 < SUC n *) -(* Proof: by arithmetic. *) -val SUC_POS = store_thm( - "SUC_POS", - ``!n. 0 < SUC n``, - decide_tac); - -(* numTheory.NOT_SUC |- !n. SUC n <> 0 *) -(* Theorem: 0 < SUC n *) -(* Proof: by arithmetic. *) -val SUC_NOT_ZERO = store_thm( - "SUC_NOT_ZERO", - ``!n. SUC n <> 0``, - decide_tac); - -(* Theorem: 1 <> 0 *) -(* Proof: by ONE, SUC_ID *) -val ONE_NOT_ZERO = store_thm( - "ONE_NOT_ZERO", - ``1 <> 0``, - decide_tac); - -(* Theorem: (SUC m) + (SUC n) = m + n + 2 *) -(* Proof: - (SUC m) + (SUC n) - = (m + 1) + (n + 1) by ADD1 - = m + n + 2 by arithmetic -*) -val SUC_ADD_SUC = store_thm( - "SUC_ADD_SUC", - ``!m n. (SUC m) + (SUC n) = m + n + 2``, - decide_tac); - -(* Theorem: (SUC m) * (SUC n) = m * n + m + n + 1 *) -(* Proof: - (SUC m) * (SUC n) - = SUC m + (SUC m) * n by MULT_SUC - = SUC m + n * (SUC m) by MULT_COMM - = SUC m + (n + n * m) by MULT_SUC - = m * n + m + n + 1 by arithmetic -*) -val SUC_MULT_SUC = store_thm( - "SUC_MULT_SUC", - ``!m n. (SUC m) * (SUC n) = m * n + m + n + 1``, - rw[MULT_SUC]); - -(* Theorem: (SUC m = SUC n) <=> (m = n) *) -(* Proof: by prim_recTheory.INV_SUC_EQ *) -val SUC_EQ = store_thm( - "SUC_EQ", - ``!m n. (SUC m = SUC n) <=> (m = n)``, - rw[]); - -(* Theorem: (TWICE n = 0) <=> (n = 0) *) -(* Proof: MULT_EQ_0 *) -val TWICE_EQ_0 = store_thm( - "TWICE_EQ_0", - ``!n. (TWICE n = 0) <=> (n = 0)``, - rw[]); - -(* Theorem: (SQ n = 0) <=> (n = 0) *) -(* Proof: MULT_EQ_0 *) -val SQ_EQ_0 = store_thm( - "SQ_EQ_0", - ``!n. (SQ n = 0) <=> (n = 0)``, - rw[]); - -(* Theorem: (SQ n = 1) <=> (n = 1) *) -(* Proof: MULT_EQ_1 *) -val SQ_EQ_1 = store_thm( - "SQ_EQ_1", - ``!n. (SQ n = 1) <=> (n = 1)``, - rw[]); - -(* Theorem: (x * y * z = 0) <=> ((x = 0) \/ (y = 0) \/ (z = 0)) *) -(* Proof: by MULT_EQ_0 *) -val MULT3_EQ_0 = store_thm( - "MULT3_EQ_0", - ``!x y z. (x * y * z = 0) <=> ((x = 0) \/ (y = 0) \/ (z = 0))``, - metis_tac[MULT_EQ_0]); - -(* Theorem: (x * y * z = 1) <=> ((x = 1) /\ (y = 1) /\ (z = 1)) *) -(* Proof: by MULT_EQ_1 *) -val MULT3_EQ_1 = store_thm( - "MULT3_EQ_1", - ``!x y z. (x * y * z = 1) <=> ((x = 1) /\ (y = 1) /\ (z = 1))``, - metis_tac[MULT_EQ_1]); - -(* Theorem: 0 ** 2 = 0 *) -(* Proof: by ZERO_EXP *) -Theorem SQ_0: - 0 ** 2 = 0 -Proof - simp[] -QED - -(* Theorem: (n ** 2 = 0) <=> (n = 0) *) -(* Proof: by EXP_2, MULT_EQ_0 *) -Theorem EXP_2_EQ_0: - !n. (n ** 2 = 0) <=> (n = 0) -Proof - simp[] -QED - -(* LE_MULT_LCANCEL |- !m n p. m * n <= m * p <=> m = 0 \/ n <= p *) - -(* Theorem: n <= p ==> m * n <= m * p *) -(* Proof: - If m = 0, this is trivial. - If m <> 0, this is true by LE_MULT_LCANCEL. -*) -Theorem LE_MULT_LCANCEL_IMP: - !m n p. n <= p ==> m * n <= m * p -Proof - simp[] -QED - -(* ------------------------------------------------------------------------- *) -(* Maximum and minimum *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MAX m n = if m <= n then n else m *) -(* Proof: by MAX_DEF *) -val MAX_ALT = store_thm( - "MAX_ALT", - ``!m n. MAX m n = if m <= n then n else m``, - rw[MAX_DEF]); - -(* Theorem: MIN m n = if m <= n then m else n *) -(* Proof: by MIN_DEF *) -val MIN_ALT = store_thm( - "MIN_ALT", - ``!m n. MIN m n = if m <= n then m else n``, - rw[MIN_DEF]); - -(* Theorem: (!x y. x <= y ==> f x <= f y) ==> !x y. f (MAX x y) = MAX (f x) (f y) *) -(* Proof: by MAX_DEF *) -val MAX_SWAP = store_thm( - "MAX_SWAP", - ``!f. (!x y. x <= y ==> f x <= f y) ==> !x y. f (MAX x y) = MAX (f x) (f y)``, - rw[MAX_DEF] >> - Cases_on `x < y` >| [ - `f x <= f y` by rw[] >> - Cases_on `f x = f y` >- - rw[] >> - rw[], - `y <= x` by decide_tac >> - `f y <= f x` by rw[] >> - rw[] - ]); - -(* Theorem: (!x y. x <= y ==> f x <= f y) ==> !x y. f (MIN x y) = MIN (f x) (f y) *) -(* Proof: by MIN_DEF *) -val MIN_SWAP = store_thm( - "MIN_SWAP", - ``!f. (!x y. x <= y ==> f x <= f y) ==> !x y. f (MIN x y) = MIN (f x) (f y)``, - rw[MIN_DEF] >> - Cases_on `x < y` >| [ - `f x <= f y` by rw[] >> - Cases_on `f x = f y` >- - rw[] >> - rw[], - `y <= x` by decide_tac >> - `f y <= f x` by rw[] >> - rw[] - ]); - -(* Theorem: SUC (MAX m n) = MAX (SUC m) (SUC n) *) -(* Proof: - If m < n, then SUC m < SUC n by LESS_MONO_EQ - hence true by MAX_DEF. - If m = n, then true by MAX_IDEM. - If n < m, true by MAX_COMM of the case m < n. -*) -val SUC_MAX = store_thm( - "SUC_MAX", - ``!m n. SUC (MAX m n) = MAX (SUC m) (SUC n)``, - rw[MAX_DEF]); - -(* Theorem: SUC (MIN m n) = MIN (SUC m) (SUC n) *) -(* Proof: by MIN_DEF *) -val SUC_MIN = store_thm( - "SUC_MIN", - ``!m n. SUC (MIN m n) = MIN (SUC m) (SUC n)``, - rw[MIN_DEF]); - -(* Reverse theorems *) -val MAX_SUC = save_thm("MAX_SUC", GSYM SUC_MAX); -(* val MAX_SUC = |- !m n. MAX (SUC m) (SUC n) = SUC (MAX m n): thm *) -val MIN_SUC = save_thm("MIN_SUC", GSYM SUC_MIN); -(* val MIN_SUC = |- !m n. MIN (SUC m) (SUC n) = SUC (MIN m n): thm *) - -(* Theorem: x < n /\ y < n ==> MAX x y < n *) -(* Proof: - MAX x y - = if x < y then y else x by MAX_DEF - = either x or y - < n for either case -*) -val MAX_LESS = store_thm( - "MAX_LESS", - ``!x y n. x < n /\ y < n ==> MAX x y < n``, - rw[]); - -(* Theorem: (MAX n m = n) \/ (MAX n m = m) *) -(* Proof: by MAX_DEF *) -val MAX_CASES = store_thm( - "MAX_CASES", - ``!m n. (MAX n m = n) \/ (MAX n m = m)``, - rw[MAX_DEF]); - -(* Theorem: (MIN n m = n) \/ (MIN n m = m) *) -(* Proof: by MIN_DEF *) -val MIN_CASES = store_thm( - "MIN_CASES", - ``!m n. (MIN n m = n) \/ (MIN n m = m)``, - rw[MIN_DEF]); - -(* Theorem: (MAX n m = 0) <=> ((n = 0) /\ (m = 0)) *) -(* Proof: - If part: MAX n m = 0 ==> n = 0 /\ m = 0 - If n < m, 0 = MAX n m = m, hence m = 0 by MAX_DEF - but n < 0 is F by NOT_LESS_0 - If ~(n < m), 0 = MAX n m = n, hence n = 0 by MAX_DEF - and ~(0 < m) ==> m = 0 by NOT_LESS - Only-if part: n = 0 /\ m = 0 ==> MAX n m = 0 - True since MAX 0 0 = 0 by MAX_0 -*) -val MAX_EQ_0 = store_thm( - "MAX_EQ_0", - ``!m n. (MAX n m = 0) <=> ((n = 0) /\ (m = 0))``, - rw[MAX_DEF]); - -(* Theorem: (MIN n m = 0) <=> ((n = 0) \/ (m = 0)) *) -(* Proof: - If part: MIN n m = 0 ==> n = 0 \/ m = 0 - If n < m, 0 = MIN n m = n, hence n = 0 by MIN_DEF - If ~(n < m), 0 = MAX n m = m, hence m = 0 by MIN_DEF - Only-if part: n = 0 \/ m = 0 ==> MIN n m = 0 - True since MIN 0 0 = 0 by MIN_0 -*) -val MIN_EQ_0 = store_thm( - "MIN_EQ_0", - ``!m n. (MIN n m = 0) <=> ((n = 0) \/ (m = 0))``, - rw[MIN_DEF]); - -(* Theorem: m <= MAX m n /\ n <= MAX m n *) -(* Proof: by MAX_DEF *) -val MAX_IS_MAX = store_thm( - "MAX_IS_MAX", - ``!m n. m <= MAX m n /\ n <= MAX m n``, - rw_tac std_ss[MAX_DEF]); - -(* Theorem: MIN m n <= m /\ MIN m n <= n *) -(* Proof: by MIN_DEF *) -val MIN_IS_MIN = store_thm( - "MIN_IS_MIN", - ``!m n. MIN m n <= m /\ MIN m n <= n``, - rw_tac std_ss[MIN_DEF]); - -(* Theorem: (MAX (MAX m n) n = MAX m n) /\ (MAX m (MAX m n) = MAX m n) *) -(* Proof: by MAX_DEF *) -val MAX_ID = store_thm( - "MAX_ID", - ``!m n. (MAX (MAX m n) n = MAX m n) /\ (MAX m (MAX m n) = MAX m n)``, - rw[MAX_DEF]); - -(* Theorem: (MIN (MIN m n) n = MIN m n) /\ (MIN m (MIN m n) = MIN m n) *) -(* Proof: by MIN_DEF *) -val MIN_ID = store_thm( - "MIN_ID", - ``!m n. (MIN (MIN m n) n = MIN m n) /\ (MIN m (MIN m n) = MIN m n)``, - rw[MIN_DEF]); - -(* Theorem: a <= b /\ c <= d ==> MAX a c <= MAX b d *) -(* Proof: by MAX_DEF *) -val MAX_LE_PAIR = store_thm( - "MAX_LE_PAIR", - ``!a b c d. a <= b /\ c <= d ==> MAX a c <= MAX b d``, - rw[]); - -(* Theorem: a <= b /\ c <= d ==> MIN a c <= MIN b d *) -(* Proof: by MIN_DEF *) -val MIN_LE_PAIR = store_thm( - "MIN_LE_PAIR", - ``!a b c d. a <= b /\ c <= d ==> MIN a c <= MIN b d``, - rw[]); - -(* Theorem: MAX a (b + c) <= MAX a b + MAX a c *) -(* Proof: by MAX_DEF *) -val MAX_ADD = store_thm( - "MAX_ADD", - ``!a b c. MAX a (b + c) <= MAX a b + MAX a c``, - rw[MAX_DEF]); - -(* Theorem: MIN a (b + c) <= MIN a b + MIN a c *) -(* Proof: by MIN_DEF *) -val MIN_ADD = store_thm( - "MIN_ADD", - ``!a b c. MIN a (b + c) <= MIN a b + MIN a c``, - rw[MIN_DEF]); - -(* Theorem: 0 < n ==> (MAX 1 n = n) *) -(* Proof: by MAX_DEF *) -val MAX_1_POS = store_thm( - "MAX_1_POS", - ``!n. 0 < n ==> (MAX 1 n = n)``, - rw[MAX_DEF]); - -(* Theorem: 0 < n ==> (MIN 1 n = 1) *) -(* Proof: by MIN_DEF *) -val MIN_1_POS = store_thm( - "MIN_1_POS", - ``!n. 0 < n ==> (MIN 1 n = 1)``, - rw[MIN_DEF]); - -(* Theorem: MAX m n <= m + n *) -(* Proof: - If m < n, MAX m n = n <= m + n by arithmetic - Otherwise, MAX m n = m <= m + n by arithmetic -*) -val MAX_LE_SUM = store_thm( - "MAX_LE_SUM", - ``!m n. MAX m n <= m + n``, - rw[MAX_DEF]); - -(* Theorem: MIN m n <= m + n *) -(* Proof: - If m < n, MIN m n = m <= m + n by arithmetic - Otherwise, MIN m n = n <= m + n by arithmetic -*) -val MIN_LE_SUM = store_thm( - "MIN_LE_SUM", - ``!m n. MIN m n <= m + n``, - rw[MIN_DEF]); - -(* Theorem: MAX 1 (m ** n) = (MAX 1 m) ** n *) -(* Proof: - If m = 0, - Then 0 ** n = 0 or 1 by ZERO_EXP - Thus MAX 1 (0 ** n) = 1 by MAX_DEF - and (MAX 1 0) ** n = 1 by MAX_DEF, EXP_1 - If m <> 0, - Then 0 < m ** n by EXP_POS - so MAX 1 (m ** n) = m ** n by MAX_DEF - and (MAX 1 m) ** n = m ** n by MAX_DEF, 0 < m -*) -val MAX_1_EXP = store_thm( - "MAX_1_EXP", - ``!n m. MAX 1 (m ** n) = (MAX 1 m) ** n``, - rpt strip_tac >> - Cases_on `m = 0` >- - rw[ZERO_EXP, MAX_DEF] >> - `0 < m /\ 0 < m ** n` by rw[] >> - `MAX 1 (m ** n) = m ** n` by rw[MAX_DEF] >> - `MAX 1 m = m` by rw[MAX_DEF] >> - fs[]); - -(* Theorem: MIN 1 (m ** n) = (MIN 1 m) ** n *) -(* Proof: - If m = 0, - Then 0 ** n = 0 or 1 by ZERO_EXP - Thus MIN 1 (0 ** n) = 0 when n <> 0 or 1 when n = 0 by MIN_DEF - and (MIN 1 0) ** n = 0 ** n by MIN_DEF - If m <> 0, - Then 0 < m ** n by EXP_POS - so MIN 1 (m ** n) = 1 ** n by MIN_DEF - and (MIN 1 m) ** n = 1 ** n by MIN_DEF, 0 < m -*) -val MIN_1_EXP = store_thm( - "MIN_1_EXP", - ``!n m. MIN 1 (m ** n) = (MIN 1 m) ** n``, - rpt strip_tac >> - Cases_on `m = 0` >- - rw[ZERO_EXP, MIN_DEF] >> - `0 < m ** n` by rw[] >> - `MIN 1 (m ** n) = 1` by rw[MIN_DEF] >> - `MIN 1 m = 1` by rw[MIN_DEF] >> - fs[]); - -(* ------------------------------------------------------------------------- *) -(* Arithmetic Manipulations *) -(* ------------------------------------------------------------------------- *) - -(* Rename theorem *) -val MULT_POS = save_thm("MULT_POS", LESS_MULT2); -(* val MULT_POS = |- !m n. 0 < m /\ 0 < n ==> 0 < m * n: thm *) - -(* Theorem: m * (n * p) = n * (m * p) *) -(* Proof: - m * (n * p) - = (m * n) * p by MULT_ASSOC - = (n * m) * p by MULT_COMM - = n * (m * p) by MULT_ASSOC -*) -val MULT_COMM_ASSOC = store_thm( - "MULT_COMM_ASSOC", - ``!m n p. m * (n * p) = n * (m * p)``, - metis_tac[MULT_COMM, MULT_ASSOC]); - -(* Theorem: n * p = m * p <=> p = 0 \/ n = m *) -(* Proof: - n * p = m * p - <=> n * p - m * p = 0 by SUB_EQUAL_0 - <=> (n - m) * p = 0 by RIGHT_SUB_DISTRIB - <=> n - m = 0 or p = 0 by MULT_EQ_0 - <=> n = m or p = 0 by SUB_EQUAL_0 -*) -val MULT_RIGHT_CANCEL = store_thm( - "MULT_RIGHT_CANCEL", - ``!m n p. (n * p = m * p) <=> (p = 0) \/ (n = m)``, - rw[]); - -(* Theorem: p * n = p * m <=> p = 0 \/ n = m *) -(* Proof: by MULT_RIGHT_CANCEL and MULT_COMM. *) -val MULT_LEFT_CANCEL = store_thm( - "MULT_LEFT_CANCEL", - ``!m n p. (p * n = p * m) <=> (p = 0) \/ (n = m)``, - rw[MULT_RIGHT_CANCEL, MULT_COMM]); - -(* Theorem: 0 < n ==> ((n * m) DIV n = m) *) -(* Proof: - Since n * m = m * n by MULT_COMM - = m * n + 0 by ADD_0 - and 0 < n by given - Hence (n * m) DIV n = m by DIV_UNIQUE: - |- !n k q. (?r. (k = q * n + r) /\ r < n) ==> (k DIV n = q) -*) -val MULT_TO_DIV = store_thm( - "MULT_TO_DIV", - ``!m n. 0 < n ==> ((n * m) DIV n = m)``, - metis_tac[MULT_COMM, ADD_0, DIV_UNIQUE]); -(* This is commutative version of: -arithmeticTheory.MULT_DIV |- !n q. 0 < n ==> (q * n DIV n = q) -*) - -(* Theorem: m * (n * p) = m * p * n *) -(* Proof: by MULT_ASSOC, MULT_COMM *) -val MULT_ASSOC_COMM = store_thm( - "MULT_ASSOC_COMM", - ``!m n p. m * (n * p) = m * p * n``, - metis_tac[MULT_ASSOC, MULT_COMM]); - -(* Theorem: 0 < n ==> !m. (m * n = n) <=> (m = 1) *) -(* Proof: by MULT_EQ_ID *) -val MULT_LEFT_ID = store_thm( - "MULT_LEFT_ID", - ``!n. 0 < n ==> !m. (m * n = n) <=> (m = 1)``, - metis_tac[MULT_EQ_ID, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < n ==> !m. (n * m = n) <=> (m = 1) *) -(* Proof: by MULT_EQ_ID *) -val MULT_RIGHT_ID = store_thm( - "MULT_RIGHT_ID", - ``!n. 0 < n ==> !m. (n * m = n) <=> (m = 1)``, - metis_tac[MULT_EQ_ID, MULT_COMM, NOT_ZERO_LT_ZERO]); - -(* Theorem alias *) -Theorem MULT_EQ_SELF = MULT_RIGHT_ID; -(* val MULT_EQ_SELF = |- !n. 0 < n ==> !m. (n * m = n) <=> (m = 1): thm *) - -(* Theorem: (n * n = n) <=> ((n = 0) \/ (n = 1)) *) -(* Proof: - If part: n * n = n ==> (n = 0) \/ (n = 1) - By contradiction, suppose n <> 0 /\ n <> 1. - Since n * n = n = n * 1 by MULT_RIGHT_1 - then n = 1 by MULT_LEFT_CANCEL, n <> 0 - This contradicts n <> 1. - Only-if part: (n = 0) \/ (n = 1) ==> n * n = n - That is, 0 * 0 = 0 by MULT - and 1 * 1 = 1 by MULT_RIGHT_1 -*) -val SQ_EQ_SELF = store_thm( - "SQ_EQ_SELF", - ``!n. (n * n = n) <=> ((n = 0) \/ (n = 1))``, - rw_tac bool_ss[EQ_IMP_THM] >- - metis_tac[MULT_RIGHT_1, MULT_LEFT_CANCEL] >- - rw[] >> - rw[]); - -(* Theorem: m <= n /\ 0 < c ==> b ** c ** m <= b ** c ** n *) -(* Proof: - If b = 0, - Note 0 < c ** m /\ 0 < c ** n by EXP_POS, by 0 < c - Thus 0 ** c ** m = 0 by ZERO_EXP - and 0 ** c ** n = 0 by ZERO_EXP - Hence true. - If b <> 0, - Then c ** m <= c ** n by EXP_BASE_LEQ_MONO_IMP, 0 < c - so b ** c ** m <= b ** c ** n by EXP_BASE_LEQ_MONO_IMP, 0 < b -*) -val EXP_EXP_BASE_LE = store_thm( - "EXP_EXP_BASE_LE", - ``!b c m n. m <= n /\ 0 < c ==> b ** c ** m <= b ** c ** n``, - rpt strip_tac >> - Cases_on `b = 0` >- - rw[ZERO_EXP] >> - rw[EXP_BASE_LEQ_MONO_IMP]); - -(* Theorem: a <= b ==> a ** n <= b ** n *) -(* Proof: - Note a ** n <= b ** n by EXP_EXP_LE_MONO - Thus size (a ** n) <= size (b ** n) by size_monotone_le -*) -val EXP_EXP_LE_MONO_IMP = store_thm( - "EXP_EXP_LE_MONO_IMP", - ``!a b n. a <= b ==> a ** n <= b ** n``, - rw[]); - -(* Theorem: m <= n ==> !p. p ** n = p ** m * p ** (n - m) *) -(* Proof: - Note n = (n - m) + m by m <= n - p ** n - = p ** (n - m) * p ** m by EXP_ADD - = p ** m * p ** (n - m) by MULT_COMM -*) -val EXP_BY_ADD_SUB_LE = store_thm( - "EXP_BY_ADD_SUB_LE", - ``!m n. m <= n ==> !p. p ** n = p ** m * p ** (n - m)``, - rpt strip_tac >> - `n = (n - m) + m` by decide_tac >> - metis_tac[EXP_ADD, MULT_COMM]); - -(* Theorem: m < n ==> (p ** n = p ** m * p ** (n - m)) *) -(* Proof: by EXP_BY_ADD_SUB_LE, LESS_IMP_LESS_OR_EQ *) -val EXP_BY_ADD_SUB_LT = store_thm( - "EXP_BY_ADD_SUB_LT", - ``!m n. m < n ==> !p. p ** n = p ** m * p ** (n - m)``, - rw[EXP_BY_ADD_SUB_LE]); - -(* Theorem: 0 < m ==> m ** (SUC n) DIV m = m ** n *) -(* Proof: - m ** (SUC n) DIV m - = (m * m ** n) DIV m by EXP - = m ** n by MULT_TO_DIV, 0 < m -*) -val EXP_SUC_DIV = store_thm( - "EXP_SUC_DIV", - ``!m n. 0 < m ==> (m ** (SUC n) DIV m = m ** n)``, - simp[EXP, MULT_TO_DIV]); - -(* Theorem: n <= n ** 2 *) -(* Proof: - If n = 0, - Then n ** 2 = 0 >= 0 by ZERO_EXP - If n <> 0, then 0 < n by NOT_ZERO_LT_ZERO - Hence n = n ** 1 by EXP_1 - <= n ** 2 by EXP_BASE_LEQ_MONO_IMP -*) -val SELF_LE_SQ = store_thm( - "SELF_LE_SQ", - ``!n. n <= n ** 2``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - `0 < n /\ 1 <= 2` by decide_tac >> - metis_tac[EXP_BASE_LEQ_MONO_IMP, EXP_1]); - -(* Theorem: a <= b /\ c <= d ==> a + c <= b + d *) -(* Proof: by LESS_EQ_LESS_EQ_MONO, or - Note a <= b ==> a + c <= b + c by LE_ADD_RCANCEL - and c <= d ==> b + c <= b + d by LE_ADD_LCANCEL - Thus a + c <= b + d by LESS_EQ_TRANS -*) -val LE_MONO_ADD2 = store_thm( - "LE_MONO_ADD2", - ``!a b c d. a <= b /\ c <= d ==> a + c <= b + d``, - rw[LESS_EQ_LESS_EQ_MONO]); - -(* Theorem: a < b /\ c < d ==> a + c < b + d *) -(* Proof: - Note a < b ==> a + c < b + c by LT_ADD_RCANCEL - and c < d ==> b + c < b + d by LT_ADD_LCANCEL - Thus a + c < b + d by LESS_TRANS -*) -val LT_MONO_ADD2 = store_thm( - "LT_MONO_ADD2", - ``!a b c d. a < b /\ c < d ==> a + c < b + d``, - rw[LT_ADD_RCANCEL, LT_ADD_LCANCEL]); - -(* Theorem: a <= b /\ c <= d ==> a * c <= b * d *) -(* Proof: by LESS_MONO_MULT2, or - Note a <= b ==> a * c <= b * c by LE_MULT_RCANCEL - and c <= d ==> b * c <= b * d by LE_MULT_LCANCEL - Thus a * c <= b * d by LESS_EQ_TRANS -*) -val LE_MONO_MULT2 = store_thm( - "LE_MONO_MULT2", - ``!a b c d. a <= b /\ c <= d ==> a * c <= b * d``, - rw[LESS_MONO_MULT2]); - -(* Theorem: a < b /\ c < d ==> a * c < b * d *) -(* Proof: - Note 0 < b, by a < b. - and 0 < d, by c < d. - If c = 0, - Then a * c = 0 < b * d by MULT_EQ_0 - If c <> 0, then 0 < c by NOT_ZERO_LT_ZERO - a < b ==> a * c < b * c by LT_MULT_RCANCEL, 0 < c - c < d ==> b * c < b * d by LT_MULT_LCANCEL, 0 < b - Thus a * c < b * d by LESS_TRANS -*) -val LT_MONO_MULT2 = store_thm( - "LT_MONO_MULT2", - ``!a b c d. a < b /\ c < d ==> a * c < b * d``, - rpt strip_tac >> - `0 < b /\ 0 < d` by decide_tac >> - Cases_on `c = 0` >- - metis_tac[MULT_EQ_0, NOT_ZERO_LT_ZERO] >> - metis_tac[LT_MULT_RCANCEL, LT_MULT_LCANCEL, LESS_TRANS, NOT_ZERO_LT_ZERO]); - -(* Theorem: 1 < m /\ 1 < n ==> (m + n <= m * n) *) -(* Proof: - Let m = m' + 1, n = n' + 1. - Note m' <> 0 /\ n' <> 0. - Thus m' * n' <> 0 by MULT_EQ_0 - or 1 <= m' * n' - m * n - = (m' + 1) * (n' + 1) - = m' * n' + m' + n' + 1 by arithmetic - >= 1 + m' + n' + 1 by 1 <= m' * n' - = m + n -*) -val SUM_LE_PRODUCT = store_thm( - "SUM_LE_PRODUCT", - ``!m n. 1 < m /\ 1 < n ==> (m + n <= m * n)``, - rpt strip_tac >> - `m <> 0 /\ n <> 0` by decide_tac >> - `?m' n'. (m = m' + 1) /\ (n = n' + 1)` by metis_tac[num_CASES, ADD1] >> - `m * n = (m' + 1) * n' + (m' + 1)` by rw[LEFT_ADD_DISTRIB] >> - `_ = m' * n' + n' + (m' + 1)` by rw[RIGHT_ADD_DISTRIB] >> - `_ = m + (n' + m' * n')` by decide_tac >> - `m' * n' <> 0` by fs[] >> - decide_tac); - -(* Theorem: 0 < n ==> k * n + 1 <= (k + 1) * n *) -(* Proof: - k * n + 1 - <= k * n + n by 1 <= n - <= (k + 1) * n by RIGHT_ADD_DISTRIB -*) -val MULTIPLE_SUC_LE = store_thm( - "MULTIPLE_SUC_LE", - ``!n k. 0 < n ==> k * n + 1 <= (k + 1) * n``, - decide_tac); - -(* ------------------------------------------------------------------------- *) -(* Simple Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < m /\ 0 < n /\ (m + n = 2) ==> m = 1 /\ n = 1 *) -(* Proof: by arithmetic. *) -val ADD_EQ_2 = store_thm( - "ADD_EQ_2", - ``!m n. 0 < m /\ 0 < n /\ (m + n = 2) ==> (m = 1) /\ (n = 1)``, - rw_tac arith_ss[]); - -(* Theorem: EVEN 0 *) -(* Proof: by EVEN. *) -val EVEN_0 = store_thm( - "EVEN_0", - ``EVEN 0``, - simp[]); - -(* Theorem: ODD 1 *) -(* Proof: by ODD. *) -val ODD_1 = store_thm( - "ODD_1", - ``ODD 1``, - simp[]); - -(* Theorem: EVEN 2 *) -(* Proof: by EVEN_MOD2. *) -val EVEN_2 = store_thm( - "EVEN_2", - ``EVEN 2``, - EVAL_TAC); - -(* -EVEN_ADD |- !m n. EVEN (m + n) <=> (EVEN m <=> EVEN n) -ODD_ADD |- !m n. ODD (m + n) <=> (ODD m <=/=> ODD n) -EVEN_MULT |- !m n. EVEN (m * n) <=> EVEN m \/ EVEN n -ODD_MULT |- !m n. ODD (m * n) <=> ODD m /\ ODD n -*) - -(* Derive theorems. *) -val EVEN_SQ = save_thm("EVEN_SQ", - EVEN_MULT |> SPEC ``n:num`` |> SPEC ``n:num`` |> SIMP_RULE arith_ss[] |> GEN_ALL); -(* val EVEN_SQ = |- !n. EVEN (n ** 2) <=> EVEN n: thm *) -val ODD_SQ = save_thm("ODD_SQ", - ODD_MULT |> SPEC ``n:num`` |> SPEC ``n:num`` |> SIMP_RULE arith_ss[] |> GEN_ALL); -(* val ODD_SQ = |- !n. ODD (n ** 2) <=> ODD n: thm *) - -(* Theorem: EVEN (2 * a + b) <=> EVEN b *) -(* Proof: - EVEN (2 * a + b) - <=> EVEN (2 * a) /\ EVEN b by EVEN_ADD - <=> T /\ EVEN b by EVEN_DOUBLE - <=> EVEN b -*) -Theorem EQ_PARITY: - !a b. EVEN (2 * a + b) <=> EVEN b -Proof - rw[EVEN_ADD, EVEN_DOUBLE] -QED - -(* Theorem: ODD x <=> (x MOD 2 = 1) *) -(* Proof: - If part: ODD x ==> x MOD 2 = 1 - Since ODD x - <=> ~EVEN x by ODD_EVEN - <=> ~(x MOD 2 = 0) by EVEN_MOD2 - But x MOD 2 < 2 by MOD_LESS, 0 < 2 - so x MOD 2 = 1 by arithmetic - Only-if part: x MOD 2 = 1 ==> ODD x - By contradiction, suppose ~ODD x. - Then EVEN x by ODD_EVEN - and x MOD 2 = 0 by EVEN_MOD2 - This contradicts x MOD 2 = 1. -*) -val ODD_MOD2 = store_thm( - "ODD_MOD2", - ``!x. ODD x <=> (x MOD 2 = 1)``, - metis_tac[EVEN_MOD2, ODD_EVEN, MOD_LESS, - DECIDE``0 <> 1 /\ 0 < 2 /\ !n. n < 2 /\ n <> 1 ==> (n = 0)``]); - -(* Theorem: (EVEN n <=> ODD (SUC n)) /\ (ODD n <=> EVEN (SUC n)) *) -(* Proof: by EVEN, ODD, EVEN_OR_ODD *) -val EVEN_ODD_SUC = store_thm( - "EVEN_ODD_SUC", - ``!n. (EVEN n <=> ODD (SUC n)) /\ (ODD n <=> EVEN (SUC n))``, - metis_tac[EVEN, ODD, EVEN_OR_ODD]); - -(* Theorem: 0 < n ==> (EVEN n <=> ODD (PRE n)) /\ (ODD n <=> EVEN (PRE n)) *) -(* Proof: by EVEN, ODD, EVEN_OR_ODD, PRE_SUC_EQ *) -val EVEN_ODD_PRE = store_thm( - "EVEN_ODD_PRE", - ``!n. 0 < n ==> (EVEN n <=> ODD (PRE n)) /\ (ODD n <=> EVEN (PRE n))``, - metis_tac[EVEN, ODD, EVEN_OR_ODD, PRE_SUC_EQ]); - -(* Theorem: EVEN (n * (n + 1)) *) -(* Proof: - If EVEN n, true by EVEN_MULT - If ~(EVEN n), - Then EVEN (SUC n) by EVEN - or EVEN (n + 1) by ADD1 - Thus true by EVEN_MULT -*) -val EVEN_PARTNERS = store_thm( - "EVEN_PARTNERS", - ``!n. EVEN (n * (n + 1))``, - metis_tac[EVEN, EVEN_MULT, ADD1]); - -(* Theorem: EVEN n ==> (n = 2 * HALF n) *) -(* Proof: - Note EVEN n ==> ?m. n = 2 * m by EVEN_EXISTS - and HALF n = HALF (2 * m) by above - = m by MULT_TO_DIV, 0 < 2 - Thus n = 2 * m = 2 * HALF n by above -*) -val EVEN_HALF = store_thm( - "EVEN_HALF", - ``!n. EVEN n ==> (n = 2 * HALF n)``, - metis_tac[EVEN_EXISTS, MULT_TO_DIV, DECIDE``0 < 2``]); - -(* Theorem: ODD n ==> (n = 2 * HALF n + 1 *) -(* Proof: - Since n = HALF n * 2 + n MOD 2 by DIVISION, 0 < 2 - = 2 * HALF n + n MOD 2 by MULT_COMM - = 2 * HALF n + 1 by ODD_MOD2 -*) -val ODD_HALF = store_thm( - "ODD_HALF", - ``!n. ODD n ==> (n = 2 * HALF n + 1)``, - metis_tac[DIVISION, MULT_COMM, ODD_MOD2, DECIDE``0 < 2``]); - -(* Theorem: EVEN n ==> (HALF (SUC n) = HALF n) *) -(* Proof: - Note n = (HALF n) * 2 + (n MOD 2) by DIVISION, 0 < 2 - = (HALF n) * 2 by EVEN_MOD2 - Now SUC n - = n + 1 by ADD1 - = (HALF n) * 2 + 1 by above - Thus HALF (SUC n) - = ((HALF n) * 2 + 1) DIV 2 by above - = HALF n by DIV_MULT, 1 < 2 -*) -val EVEN_SUC_HALF = store_thm( - "EVEN_SUC_HALF", - ``!n. EVEN n ==> (HALF (SUC n) = HALF n)``, - rpt strip_tac >> - `n MOD 2 = 0` by rw[GSYM EVEN_MOD2] >> - `n = HALF n * 2 + n MOD 2` by rw[DIVISION] >> - `SUC n = HALF n * 2 + 1` by rw[] >> - metis_tac[DIV_MULT, DECIDE``1 < 2``]); - -(* Theorem: ODD n ==> (HALF (SUC n) = SUC (HALF n)) *) -(* Proof: - SUC n - = SUC (2 * HALF n + 1) by ODD_HALF - = 2 * HALF n + 1 + 1 by ADD1 - = 2 * HALF n + 2 by arithmetic - = 2 * (HALF n + 1) by LEFT_ADD_DISTRIB - = 2 * SUC (HALF n) by ADD1 - = SUC (HALF n) * 2 + 0 by MULT_COMM, ADD_0 - Hence HALF (SUC n) = SUC (HALF n) by DIV_UNIQUE, 0 < 2 -*) -val ODD_SUC_HALF = store_thm( - "ODD_SUC_HALF", - ``!n. ODD n ==> (HALF (SUC n) = SUC (HALF n))``, - rpt strip_tac >> - `SUC n = SUC (2 * HALF n + 1)` by rw[ODD_HALF] >> - `_ = SUC (HALF n) * 2 + 0` by rw[] >> - metis_tac[DIV_UNIQUE, DECIDE``0 < 2``]); - -(* Theorem: (HALF n = 0) <=> ((n = 0) \/ (n = 1)) *) -(* Proof: - If part: (HALF n = 0) ==> ((n = 0) \/ (n = 1)) - Note n = (HALF n) * 2 + (n MOD 2) by DIVISION, 0 < 2 - = n MOD 2 by HALF n = 0 - and n MOD 2 < 2 by MOD_LESS, 0 < 2 - so n < 2, or n = 0 or n = 1 by arithmetic - Only-if part: HALF 0 = 0, HALF 1 = 0. - True since both 0 or 1 < 2 by LESS_DIV_EQ_ZERO, 0 < 2 -*) -val HALF_EQ_0 = store_thm( - "HALF_EQ_0", - ``!n. (HALF n = 0) <=> ((n = 0) \/ (n = 1))``, - rw[LESS_DIV_EQ_ZERO, EQ_IMP_THM] >> - `n = (HALF n) * 2 + (n MOD 2)` by rw[DIVISION] >> - `n MOD 2 < 2` by rw[MOD_LESS] >> - decide_tac); - -(* Theorem: (HALF n = n) <=> (n = 0) *) -(* Proof: - If part: HALF n = n ==> n = 0 - Note n = 2 * HALF n + (n MOD 2) by DIVISION, MULT_COMM - so n = 2 * n + (n MOD 2) by HALF n = n - or 0 = n + (n MOD 2) by arithmetic - Thus n = 0 and (n MOD 2 = 0) by ADD_EQ_0 - Only-if part: HALF 0 = 0, true by ZERO_DIV, 0 < 2 -*) -val HALF_EQ_SELF = store_thm( - "HALF_EQ_SELF", - ``!n. (HALF n = n) <=> (n = 0)``, - rw[EQ_IMP_THM] >> - `n = 2 * HALF n + (n MOD 2)` by metis_tac[DIVISION, MULT_COMM, DECIDE``0 < 2``] >> - rw[ADD_EQ_0]); - -(* Theorem: 0 < n ==> HALF n < n *) -(* Proof: - Note HALF n <= n by DIV_LESS_EQ, 0 < 2 - and HALF n <> n by HALF_EQ_SELF, n <> 0 - so HALF n < n by arithmetic -*) -val HALF_LT = store_thm( - "HALF_LT", - ``!n. 0 < n ==> HALF n < n``, - rpt strip_tac >> - `HALF n <= n` by rw[DIV_LESS_EQ] >> - `HALF n <> n` by rw[HALF_EQ_SELF] >> - decide_tac); - -(* Theorem: 2 < n ==> (1 + HALF n < n) *) -(* Proof: - If EVEN n, - then 2 * HALF n = n by EVEN_HALF - so 2 + 2 * HALF n < n + n by 2 < n - or 1 + HALF n < n by arithmetic - If ~EVEN n, then ODD n by ODD_EVEN - then 1 + 2 * HALF n = 2 by ODD_HALF - so 1 + 2 * HALF n < n by 2 < n - also 2 + 2 * HALF n < n + n by 1 < n - or 1 + HALF n < n by arithmetic -*) -Theorem HALF_ADD1_LT: - !n. 2 < n ==> 1 + HALF n < n -Proof - rpt strip_tac >> - Cases_on `EVEN n` >| [ - `2 * HALF n = n` by rw[EVEN_HALF] >> - decide_tac, - `1 + 2 * HALF n = n` by rw[ODD_HALF, ODD_EVEN] >> - decide_tac - ] -QED - -(* Theorem alias *) -Theorem HALF_TWICE = arithmeticTheory.MULT_DIV_2; -(* val HALF_TWICE = |- !n. HALF (2 * n) = n: thm *) - -(* Theorem: n * HALF m <= HALF (n * m) *) -(* Proof: - Let k = HALF m. - If EVEN m, - Then m = 2 * k by EVEN_HALF - HALF (n * m) - = HALF (n * (2 * k)) by above - = HALF (2 * (n * k)) by arithmetic - = n * k by HALF_TWICE - If ~EVEN m, then ODD m by ODD_EVEN - Then m = 2 * k + 1 by ODD_HALF - so HALF (n * m) - = HALF (n * (2 * k + 1)) by above - = HALF (2 * (n * k) + n) by LEFT_ADD_DISTRIB - = HALF (2 * (n * k)) + HALF n by ADD_DIV_ADD_DIV - = n * k + HALF n by HALF_TWICE - >= n * k by arithmetic -*) -Theorem HALF_MULT: !m n. n * (m DIV 2) <= (n * m) DIV 2 -Proof - rpt strip_tac >> - qabbrev_tac `k = m DIV 2` >> - Cases_on `EVEN m` - >- (`m = 2 * k` by rw[EVEN_HALF, Abbr`k`] >> - simp[]) >> - `ODD m` by rw[ODD_EVEN] >> - `m = 2 * k + 1` by rw[ODD_HALF, Abbr`k`] >> - simp[LEFT_ADD_DISTRIB] -QED - -(* Theorem: 2 * HALF n <= n /\ n <= SUC (2 * HALF n) *) -(* Proof: - If EVEN n, - Then n = 2 * HALF n by EVEN_HALF - and n = n < SUC n by LESS_SUC - or n <= n <= SUC n, - Giving 2 * HALF n <= n /\ n <= SUC (2 * HALF n) - If ~(EVEN n), then ODD n by EVEN_ODD - Then n = 2 * HALF n + 1 by ODD_HALF - = SUC (2 * HALF n) by ADD1 - or n - 1 < n = n - or n - 1 <= n <= n, - Giving 2 * HALF n <= n /\ n <= SUC (2 * HALF n) -*) -val TWO_HALF_LE_THM = store_thm( - "TWO_HALF_LE_THM", - ``!n. 2 * HALF n <= n /\ n <= SUC (2 * HALF n)``, - strip_tac >> - Cases_on `EVEN n` >- - rw[GSYM EVEN_HALF] >> - `ODD n` by rw[ODD_EVEN] >> - `n <> 0` by metis_tac[ODD] >> - `n = SUC (2 * HALF n)` by rw[ODD_HALF, ADD1] >> - `2 * HALF n = PRE n` by rw[] >> - rw[]); - -(* Theorem: 2 * ((HALF n) * m) <= n * m *) -(* Proof: - 2 * ((HALF n) * m) - = 2 * (m * HALF n) by MULT_COMM - <= 2 * (HALF (m * n)) by HALF_MULT - <= m * n by TWO_HALF_LE_THM - = n * m by MULT_COMM -*) -val TWO_HALF_TIMES_LE = store_thm( - "TWO_HALF_TIMES_LE", - ``!m n. 2 * ((HALF n) * m) <= n * m``, - rpt strip_tac >> - `2 * (m * HALF n) <= 2 * (HALF (m * n))` by rw[HALF_MULT] >> - `2 * (HALF (m * n)) <= m * n` by rw[TWO_HALF_LE_THM] >> - fs[]); - -(* Theorem: 0 < n ==> 1 + HALF n <= n *) -(* Proof: - If n = 1, - HALF 1 = 0, hence true. - If n <> 1, - Then HALF n <> 0 by HALF_EQ_0, n <> 0, n <> 1 - Thus 1 + HALF n - <= HALF n + HALF n by 1 <= HALF n - = 2 * HALF n - <= n by TWO_HALF_LE_THM -*) -val HALF_ADD1_LE = store_thm( - "HALF_ADD1_LE", - ``!n. 0 < n ==> 1 + HALF n <= n``, - rpt strip_tac >> - (Cases_on `n = 1` >> simp[]) >> - `HALF n <> 0` by metis_tac[HALF_EQ_0, NOT_ZERO] >> - `1 + HALF n <= 2 * HALF n` by decide_tac >> - `2 * HALF n <= n` by rw[TWO_HALF_LE_THM] >> - decide_tac); - -(* Theorem: (HALF n) ** 2 <= (n ** 2) DIV 4 *) -(* Proof: - Let k = HALF n. - Then 2 * k <= n by TWO_HALF_LE_THM - so (2 * k) ** 2 <= n ** 2 by EXP_EXP_LE_MONO - and (2 * k) ** 2 DIV 4 <= n ** 2 DIV 4 by DIV_LE_MONOTONE, 0 < 4 - But (2 * k) ** 2 DIV 4 - = 4 * k ** 2 DIV 4 by EXP_BASE_MULT - = k ** 2 by MULT_TO_DIV, 0 < 4 - Thus k ** 2 <= n ** 2 DIV 4. -*) -val HALF_SQ_LE = store_thm( - "HALF_SQ_LE", - ``!n. (HALF n) ** 2 <= (n ** 2) DIV 4``, - rpt strip_tac >> - qabbrev_tac `k = HALF n` >> - `2 * k <= n` by rw[TWO_HALF_LE_THM, Abbr`k`] >> - `(2 * k) ** 2 <= n ** 2` by rw[] >> - `(2 * k) ** 2 DIV 4 <= n ** 2 DIV 4` by rw[DIV_LE_MONOTONE] >> - `(2 * k) ** 2 DIV 4 = 4 * k ** 2 DIV 4` by rw[EXP_BASE_MULT] >> - `_ = k ** 2` by rw[MULT_TO_DIV] >> - decide_tac); - -(* Obtain theorems *) -val HALF_LE = save_thm("HALF_LE", - DIV_LESS_EQ |> SPEC ``2`` |> SIMP_RULE (arith_ss) [] |> SPEC ``n:num`` |> GEN_ALL); -(* val HALF_LE = |- !n. HALF n <= n: thm *) -val HALF_LE_MONO = save_thm("HALF_LE_MONO", - DIV_LE_MONOTONE |> SPEC ``2`` |> SIMP_RULE (arith_ss) []); -(* val HALF_LE_MONO = |- !x y. x <= y ==> HALF x <= HALF y: thm *) - -(* Theorem: HALF (SUC n) <= n *) -(* Proof: - If EVEN n, - Then ?k. n = 2 * k by EVEN_EXISTS - and SUC n = 2 * k + 1 - so HALF (SUC n) = k <= k + k = n by ineqaulities - Otherwise ODD n, by ODD_EVEN - Then ?k. n = 2 * k + 1 by ODD_EXISTS - and SUC n = 2 * k + 2 - so HALF (SUC n) = k + 1 <= k + k + 1 = n -*) -Theorem HALF_SUC: - !n. HALF (SUC n) <= n -Proof - rpt strip_tac >> - Cases_on `EVEN n` >| [ - `?k. n = 2 * k` by metis_tac[EVEN_EXISTS] >> - `HALF (SUC n) = k` by simp[ADD1] >> - decide_tac, - `?k. n = 2 * k + 1` by metis_tac[ODD_EXISTS, ODD_EVEN, ADD1] >> - `HALF (SUC n) = k + 1` by simp[ADD1] >> - decide_tac - ] -QED - -(* Theorem: 0 < n ==> HALF (SUC (SUC n)) <= n *) -(* Proof: - Note SUC (SUC n) = n + 2 by ADD1 - If EVEN n, - then ?k. n = 2 * k by EVEN_EXISTS - Since n = 2 * k <> 0 by NOT_ZERO, 0 < n - so k <> 0, or 1 <= k by MULT_EQ_0 - HALF (n + 2) - = k + 1 by arithmetic - <= k + k by above - = n - Otherwise ODD n, by ODD_EVEN - then ?k. n = 2 * k + 1 by ODD_EXISTS - HALF (n + 2) - = HALF (2 * k + 3) by arithmetic - = k + 1 by arithmetic - <= k + k + 1 by ineqaulities - = n -*) -Theorem HALF_SUC_SUC: - !n. 0 < n ==> HALF (SUC (SUC n)) <= n -Proof - rpt strip_tac >> - Cases_on `EVEN n` >| [ - `?k. n = 2 * k` by metis_tac[EVEN_EXISTS] >> - `0 < k` by metis_tac[MULT_EQ_0, NOT_ZERO] >> - `1 <= k` by decide_tac >> - `HALF (SUC (SUC n)) = k + 1` by simp[ADD1] >> - fs[], - `?k. n = 2 * k + 1` by metis_tac[ODD_EXISTS, ODD_EVEN, ADD1] >> - `HALF (SUC (SUC n)) = k + 1` by simp[ADD1] >> - fs[] - ] -QED - -(* Theorem: n < HALF (SUC m) ==> 2 * n + 1 <= m *) -(* Proof: - If EVEN m, - Then m = 2 * HALF m by EVEN_HALF - and SUC m = 2 * HALF m + 1 by ADD1 - so n < (2 * HALF m + 1) DIV 2 by given - or n < HALF m by arithmetic - 2 * n < 2 * HALF m by LT_MULT_LCANCEL - 2 * n < m by above - 2 * n + 1 <= m by arithmetic - Otherwise, ODD m by ODD_EVEN - Then m = 2 * HALF m + 1 by ODD_HALF - and SUC m = 2 * HALF m + 2 by ADD1 - so n < (2 * HALF m + 2) DIV 2 by given - or n < HALF m + 1 by arithmetic - 2 * n + 1 < 2 * HALF m + 1 by LT_MULT_LCANCEL, LT_ADD_RCANCEL - or 2 * n + 1 < m by above - Overall, 2 * n + 1 <= m. -*) -Theorem HALF_SUC_LE: - !n m. n < HALF (SUC m) ==> 2 * n + 1 <= m -Proof - rpt strip_tac >> - Cases_on `EVEN m` >| [ - `m = 2 * HALF m` by simp[EVEN_HALF] >> - `HALF (SUC m) = HALF (2 * HALF m + 1)` by metis_tac[ADD1] >> - `_ = HALF m` by simp[] >> - simp[], - `m = 2 * HALF m + 1` by simp[ODD_HALF, ODD_EVEN] >> - `HALF (SUC m) = HALF (2 * HALF m + 1 + 1)` by metis_tac[ADD1] >> - `_ = HALF m + 1` by simp[] >> - simp[] - ] -QED - -(* Theorem: 2 * n < m ==> n <= HALF m *) -(* Proof: - If EVEN m, - Then m = 2 * HALF m by EVEN_HALF - so 2 * n < 2 * HALF m by above - or n < HALF m by LT_MULT_LCANCEL - Otherwise, ODD m by ODD_EVEN - Then m = 2 * HALF m + 1 by ODD_HALF - so 2 * n < 2 * HALF m + 1 by above - so 2 * n <= 2 * HALF m by removing 1 - or n <= HALF m by LE_MULT_LCANCEL - Overall, n <= HALF m. -*) -Theorem HALF_EVEN_LE: - !n m. 2 * n < m ==> n <= HALF m -Proof - rpt strip_tac >> - Cases_on `EVEN m` >| [ - `2 * n < 2 * HALF m` by metis_tac[EVEN_HALF] >> - simp[], - `2 * n < 2 * HALF m + 1` by metis_tac[ODD_HALF, ODD_EVEN] >> - simp[] - ] -QED - -(* Theorem: 2 * n + 1 < m ==> n < HALF m *) -(* Proof: - If EVEN m, - Then m = 2 * HALF m by EVEN_HALF - so 2 * n + 1 < 2 * HALF m by above - or 2 * n < 2 * HALF m by removing 1 - or n < HALF m by LT_MULT_LCANCEL - Otherwise, ODD m by ODD_EVEN - Then m = 2 * HALF m + 1 by ODD_HALF - so 2 * n + 1 < 2 * HALF m + 1 by above - or 2 * n < 2 * HALF m by LT_ADD_RCANCEL - or n < HALF m by LT_MULT_LCANCEL - Overall, n < HALF m. -*) -Theorem HALF_ODD_LT: - !n m. 2 * n + 1 < m ==> n < HALF m -Proof - rpt strip_tac >> - Cases_on `EVEN m` >| [ - `2 * n + 1 < 2 * HALF m` by metis_tac[EVEN_HALF] >> - simp[], - `2 * n + 1 < 2 * HALF m + 1` by metis_tac[ODD_HALF, ODD_EVEN] >> - simp[] - ] -QED - -(* Theorem: EVEN n ==> !m. m * n = (TWICE m) * (HALF n) *) -(* Proof: - (TWICE m) * (HALF n) - = (2 * m) * (HALF n) by notation - = m * TWICE (HALF n) by MULT_COMM, MULT_ASSOC - = m * n by EVEN_HALF -*) -val MULT_EVEN = store_thm( - "MULT_EVEN", - ``!n. EVEN n ==> !m. m * n = (TWICE m) * (HALF n)``, - metis_tac[MULT_COMM, MULT_ASSOC, EVEN_HALF]); - -(* Theorem: ODD n ==> !m. m * n = m + (TWICE m) * (HALF n) *) -(* Proof: - m + (TWICE m) * (HALF n) - = m + (2 * m) * (HALF n) by notation - = m + m * (TWICE (HALF n)) by MULT_COMM, MULT_ASSOC - = m * (SUC (TWICE (HALF n))) by MULT_SUC - = m * (TWICE (HALF n) + 1) by ADD1 - = m * n by ODD_HALF -*) -val MULT_ODD = store_thm( - "MULT_ODD", - ``!n. ODD n ==> !m. m * n = m + (TWICE m) * (HALF n)``, - metis_tac[MULT_COMM, MULT_ASSOC, ODD_HALF, MULT_SUC, ADD1]); - -(* Theorem: EVEN m /\ m <> 0 ==> !n. EVEN n <=> EVEN (n MOD m) *) -(* Proof: - Note ?k. m = 2 * k by EVEN_EXISTS, EVEN m - and k <> 0 by MULT_EQ_0, m <> 0 - ==> (n MOD m) MOD 2 = n MOD 2 by MOD_MULT_MOD - The result follows by EVEN_MOD2 -*) -val EVEN_MOD_EVEN = store_thm( - "EVEN_MOD_EVEN", - ``!m. EVEN m /\ m <> 0 ==> !n. EVEN n <=> EVEN (n MOD m)``, - rpt strip_tac >> - `?k. m = 2 * k` by rw[GSYM EVEN_EXISTS] >> - `(n MOD m) MOD 2 = n MOD 2` by rw[MOD_MULT_MOD] >> - metis_tac[EVEN_MOD2]); - -(* Theorem: EVEN m /\ m <> 0 ==> !n. ODD n <=> ODD (n MOD m) *) -(* Proof: by EVEN_MOD_EVEN, ODD_EVEN *) -val EVEN_MOD_ODD = store_thm( - "EVEN_MOD_ODD", - ``!m. EVEN m /\ m <> 0 ==> !n. ODD n <=> ODD (n MOD m)``, - rw_tac std_ss[EVEN_MOD_EVEN, ODD_EVEN]); - -(* Theorem: c <= a ==> ((a - b) - (a - c) = c - b) *) -(* Proof: - a - b - (a - c) - = a - (b + (a - c)) by SUB_RIGHT_SUB, no condition - = a - ((a - c) + b) by ADD_COMM, no condition - = a - (a - c) - b by SUB_RIGHT_SUB, no condition - = a + c - a - b by SUB_SUB, c <= a - = c + a - a - b by ADD_COMM, no condition - = c + (a - a) - b by LESS_EQ_ADD_SUB, a <= a - = c + 0 - b by SUB_EQUAL_0 - = c - b -*) -val SUB_SUB_SUB = store_thm( - "SUB_SUB_SUB", - ``!a b c. c <= a ==> ((a - b) - (a - c) = c - b)``, - decide_tac); - -(* Theorem: c <= a ==> (a + b - (a - c) = c + b) *) -(* Proof: - a + b - (a - c) - = a + b + c - a by SUB_SUB, a <= c - = a + (b + c) - a by ADD_ASSOC - = (b + c) + a - a by ADD_COMM - = b + c - (a - a) by SUB_SUB, a <= a - = b + c - 0 by SUB_EQUAL_0 - = b + c by SUB_0 -*) -val ADD_SUB_SUB = store_thm( - "ADD_SUB_SUB", - ``!a b c. c <= a ==> (a + b - (a - c) = c + b)``, - decide_tac); - -(* Theorem: 0 < p ==> !m n. (m - n = p) <=> (m = n + p) *) -(* Proof: - If part: m - n = p ==> m = n + p - Note 0 < m - n by 0 < p - so n < m by LESS_MONO_ADD - or m = m - n + n by SUB_ADD, n <= m - = p + n by m - n = p - = n + p by ADD_COMM - Only-if part: m = n + p ==> m - n = p - m - n - = (n + p) - n by m = n + p - = p + n - n by ADD_COMM - = p by ADD_SUB -*) -val SUB_EQ_ADD = store_thm( - "SUB_EQ_ADD", - ``!p. 0 < p ==> !m n. (m - n = p) <=> (m = n + p)``, - decide_tac); - -(* Note: ADD_EQ_SUB |- !m n p. n <= p ==> ((m + n = p) <=> (m = p - n)) *) - -(* Theorem: 0 < a /\ 0 < b /\ a < c /\ (a * b = c * d) ==> (d < b) *) -(* Proof: - By contradiction, suppose b <= d. - Since a * b <> 0 by MULT_EQ_0, 0 < a, 0 < b - so d <> 0, or 0 < d by MULT_EQ_0, a * b <> 0 - Now a * b <= a * d by LE_MULT_LCANCEL, b <= d, a <> 0 - and a * d < c * d by LT_MULT_LCANCEL, a < c, d <> 0 - so a * b < c * d by LESS_EQ_LESS_TRANS - This contradicts a * b = c * d. -*) -val MULT_EQ_LESS_TO_MORE = store_thm( - "MULT_EQ_LESS_TO_MORE", - ``!a b c d. 0 < a /\ 0 < b /\ a < c /\ (a * b = c * d) ==> (d < b)``, - spose_not_then strip_assume_tac >> - `b <= d` by decide_tac >> - `0 < d` by decide_tac >> - `a * b <= a * d` by rw[LE_MULT_LCANCEL] >> - `a * d < c * d` by rw[LT_MULT_LCANCEL] >> - decide_tac); - -(* Theorem: 0 < c /\ 0 < d /\ a * b <= c * d /\ d < b ==> a < c *) -(* Proof: - By contradiction, suppose c <= a. - With d < b, which gives d <= b by LESS_IMP_LESS_OR_EQ - Thus c * d <= a * b by LE_MONO_MULT2 - or a * b = c * d by a * b <= c * d - Note 0 < c /\ 0 < d by given - ==> a < c by MULT_EQ_LESS_TO_MORE - This contradicts c <= a. - -MULT_EQ_LESS_TO_MORE -|- !a b c d. 0 < a /\ 0 < b /\ a < c /\ a * b = c * d ==> d < b - 0 < d /\ 0 < c /\ d < b /\ d * c = b * a ==> a < c -*) -val LE_IMP_REVERSE_LT = store_thm( - "LE_IMP_REVERSE_LT", - ``!a b c d. 0 < c /\ 0 < d /\ a * b <= c * d /\ d < b ==> a < c``, - spose_not_then strip_assume_tac >> - `c <= a` by decide_tac >> - `c * d <= a * b` by rw[LE_MONO_MULT2] >> - `a * b = c * d` by decide_tac >> - `a < c` by metis_tac[MULT_EQ_LESS_TO_MORE, MULT_COMM]); - -(* ------------------------------------------------------------------------- *) -(* Exponential Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: n ** 0 = 1 *) -(* Proof: by EXP *) -val EXP_0 = store_thm( - "EXP_0", - ``!n. n ** 0 = 1``, - rw_tac std_ss[EXP]); - -(* Theorem: n ** 2 = n * n *) -(* Proof: - n ** 2 = n * (n ** 1) = n * (n * (n ** 0)) = n * (n * 1) = n * n - or n ** 2 = n * (n ** 1) = n * n by EXP_1: !n. (1 ** n = 1) /\ (n ** 1 = n) -*) -val EXP_2 = store_thm( - "EXP_2", - ``!n. n ** 2 = n * n``, - metis_tac[EXP, TWO, EXP_1]); - -(* Theorem: m <> 0 ==> m ** n <> 0 *) -(* Proof: by EXP_EQ_0 *) -val EXP_NONZERO = store_thm( - "EXP_NONZERO", - ``!m n. m <> 0 ==> m ** n <> 0``, - metis_tac[EXP_EQ_0]); - -(* Theorem: 0 < m ==> 0 < m ** n *) -(* Proof: by EXP_NONZERO *) -val EXP_POS = store_thm( - "EXP_POS", - ``!m n. 0 < m ==> 0 < m ** n``, - rw[EXP_NONZERO]); - -(* Theorem: 0 < m ==> ((n ** m = n) <=> ((m = 1) \/ (n = 0) \/ (n = 1))) *) -(* Proof: - If part: n ** m = n ==> n = 0 \/ n = 1 - By contradiction, assume n <> 0 /\ n <> 1. - Then ?k. m = SUC k by num_CASES, 0 < m - so n ** SUC k = n by n ** m = n - or n * n ** k = n by EXP - ==> n ** k = 1 by MULT_EQ_SELF, 0 < n - ==> n = 1 or k = 0 by EXP_EQ_1 - ==> n = 1 or m = 1, - These contradict n <> 1 and m <> 1. - Only-if part: n ** 1 = n /\ 0 ** m = 0 /\ 1 ** m = 1 - These are true by EXP_1, ZERO_EXP. -*) -val EXP_EQ_SELF = store_thm( - "EXP_EQ_SELF", - ``!n m. 0 < m ==> ((n ** m = n) <=> ((m = 1) \/ (n = 0) \/ (n = 1)))``, - rw_tac std_ss[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `m <> 0` by decide_tac >> - `?k. m = SUC k` by metis_tac[num_CASES] >> - `n * n ** k = n` by fs[EXP] >> - `n ** k = 1` by metis_tac[MULT_EQ_SELF, NOT_ZERO_LT_ZERO] >> - fs[EXP_EQ_1], - rw[], - rw[], - rw[] - ]); - -(* Obtain a theorem *) -val EXP_LE = save_thm("EXP_LE", X_LE_X_EXP |> GEN ``x:num`` |> SPEC ``b:num`` |> GEN_ALL); -(* val EXP_LE = |- !n b. 0 < n ==> b <= b ** n: thm *) - -(* Theorem: 1 < b /\ 1 < n ==> b < b ** n *) -(* Proof: - By contradiction, assume ~(b < b ** n). - Then b ** n <= b by arithmetic - But b <= b ** n by EXP_LE, 0 < n - ==> b ** n = b by EQ_LESS_EQ - ==> b = 1 or n = 0 or n = 1. - All these contradict 1 < b and 1 < n. -*) -val EXP_LT = store_thm( - "EXP_LT", - ``!n b. 1 < b /\ 1 < n ==> b < b ** n``, - spose_not_then strip_assume_tac >> - `b <= b ** n` by rw[EXP_LE] >> - `b ** n = b` by decide_tac >> - rfs[EXP_EQ_SELF]); - -(* Theorem: 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c) *) -(* Proof: - Let d = m - n. - Then 0 < d, and m = n + d by arithmetic - and 0 < a ==> a ** n <> 0 by EXP_EQ_0 - a ** n * b - = a ** (n + d) * c by m = n + d - = (a ** n * a ** d) * c by EXP_ADD - = a ** n * (a ** d * c) by MULT_ASSOC - The result follows by MULT_LEFT_CANCEL -*) -val EXP_LCANCEL = store_thm( - "EXP_LCANCEL", - ``!a b c n m. 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c)``, - rpt strip_tac >> - `0 < m - n /\ (m = n + (m - n))` by decide_tac >> - qabbrev_tac `d = m - n` >> - `a ** n <> 0` by metis_tac[EXP_EQ_0, NOT_ZERO_LT_ZERO] >> - metis_tac[EXP_ADD, MULT_ASSOC, MULT_LEFT_CANCEL]); - -(* Theorem: 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c) *) -(* Proof: by EXP_LCANCEL, MULT_COMM. *) -val EXP_RCANCEL = store_thm( - "EXP_RCANCEL", - ``!a b c n m. 0 < a /\ n < m /\ (b * a ** n = c * a ** m) ==> ?d. 0 < d /\ (b = c * a ** d)``, - metis_tac[EXP_LCANCEL, MULT_COMM]); - -(* -EXP_POS |- !m n. 0 < m ==> 0 < m ** n -ONE_LT_EXP |- !x y. 1 < x ** y <=> 1 < x /\ 0 < y -ZERO_LT_EXP |- 0 < x ** y <=> 0 < x \/ (y = 0) -*) - -(* Theorem: 0 < m ==> 1 <= m ** n *) -(* Proof: - 0 < m ==> 0 < m ** n by EXP_POS - or 1 <= m ** n by arithmetic -*) -val ONE_LE_EXP = store_thm( - "ONE_LE_EXP", - ``!m n. 0 < m ==> 1 <= m ** n``, - metis_tac[EXP_POS, DECIDE``!x. 0 < x <=> 1 <= x``]); - -(* Theorem: EVEN n ==> !m. m ** n = (SQ m) ** (HALF n) *) -(* Proof: - (SQ m) ** (HALF n) - = (m ** 2) ** (HALF n) by notation - = m ** (2 * HALF n) by EXP_EXP_MULT - = m ** n by EVEN_HALF -*) -val EXP_EVEN = store_thm( - "EXP_EVEN", - ``!n. EVEN n ==> !m. m ** n = (SQ m) ** (HALF n)``, - rpt strip_tac >> - `(SQ m) ** (HALF n) = m ** (2 * HALF n)` by rw[EXP_EXP_MULT] >> - `_ = m ** n` by rw[GSYM EVEN_HALF] >> - rw[]); - -(* Theorem: ODD n ==> !m. m ** n = m * (SQ m) ** (HALF n) *) -(* Proof: - m * (SQ m) ** (HALF n) - = m * (m ** 2) ** (HALF n) by notation - = m * m ** (2 * HALF n) by EXP_EXP_MULT - = m ** (SUC (2 * HALF n)) by EXP - = m ** (2 * HALF n + 1) by ADD1 - = m ** n by ODD_HALF -*) -val EXP_ODD = store_thm( - "EXP_ODD", - ``!n. ODD n ==> !m. m ** n = m * (SQ m) ** (HALF n)``, - rpt strip_tac >> - `m * (SQ m) ** (HALF n) = m * m ** (2 * HALF n)` by rw[EXP_EXP_MULT] >> - `_ = m ** (2 * HALF n + 1)` by rw[GSYM EXP, ADD1] >> - `_ = m ** n` by rw[GSYM ODD_HALF] >> - rw[]); - -(* An exponentiation identity *) -(* val EXP_THM = save_thm("EXP_THM", CONJ EXP_EVEN EXP_ODD); *) -(* -val EXP_THM = |- (!n. EVEN n ==> !m. m ** n = SQ m ** HALF n) /\ - !n. ODD n ==> !m. m ** n = m * SQ m ** HALF n: thm -*) -(* Next is better *) - -(* Theorem: m ** n = if n = 0 then 1 else if n = 1 then m else - if EVEN n then (m * m) ** HALF n else m * ((m * m) ** (HALF n)) *) -(* Proof: mainly by EXP_EVEN, EXP_ODD. *) -Theorem EXP_THM: - !m n. m ** n = if n = 0 then 1 else if n = 1 then m - else if EVEN n then (m * m) ** HALF n - else m * ((m * m) ** (HALF n)) -Proof - metis_tac[EXP_0, EXP_1, EXP_EVEN, EXP_ODD, EVEN_ODD] -QED - -(* Theorem: m ** n = - if n = 0 then 1 - else if EVEN n then (m * m) ** (HALF n) else m * (m * m) ** (HALF n) *) -(* Proof: - If n = 0, to show: - m ** 0 = 1, true by EXP_0 - If EVEN n, to show: - m ** n = (m * m) ** (HALF n), true by EXP_EVEN - If ~EVEN n, ODD n, to show: by EVEN_ODD - m ** n = m * (m * m) ** HALF n, true by EXP_ODD -*) -val EXP_EQN = store_thm( - "EXP_EQN", - ``!m n. m ** n = - if n = 0 then 1 - else if EVEN n then (m * m) ** (HALF n) else m * (m * m) ** (HALF n)``, - rw[] >- - rw[Once EXP_EVEN] >> - `ODD n` by metis_tac[EVEN_ODD] >> - rw[Once EXP_ODD]); - -(* Theorem: m ** n = if n = 0 then 1 else (if EVEN n then 1 else m) * (m * m) ** (HALF n) *) -(* Proof: by EXP_EQN *) -val EXP_EQN_ALT = store_thm( - "EXP_EQN_ALT", - ``!m n. m ** n = - if n = 0 then 1 - else (if EVEN n then 1 else m) * (m * m) ** (HALF n)``, - rw[Once EXP_EQN]); - -(* Theorem: m ** n = (if n = 0 then 1 else (if EVEN n then 1 else m) * (m ** 2) ** HALF n) *) -(* Proof: by EXP_EQN_ALT, EXP_2 *) -val EXP_ALT_EQN = store_thm( - "EXP_ALT_EQN", - ``!m n. m ** n = (if n = 0 then 1 else (if EVEN n then 1 else m) * (m ** 2) ** HALF n)``, - metis_tac[EXP_EQN_ALT, EXP_2]); - -(* Theorem: 1 < m ==> - (b ** n) MOD m = if (n = 0) then 1 - else let result = (b * b) ** (HALF n) MOD m - in if EVEN n then result else (b * result) MOD m *) -(* Proof: - This is to show: - (1) 1 < m ==> b ** 0 MOD m = 1 - b ** 0 MOD m - = 1 MOD m by EXP_0 - = 1 by ONE_MOD, 1 < m - (2) EVEN n ==> b ** n MOD m = (b ** 2) ** HALF n MOD m - b ** n MOD m - = (b * b) ** HALF n MOD m by EXP_EVEN - = (b ** 2) ** HALF n MOD m by EXP_2 - (3) ~EVEN n ==> b ** n MOD m = (b * (b ** 2) ** HALF n) MOD m - b ** n MOD m - = (b * (b * b) ** HALF n) MOD m by EXP_ODD, EVEN_ODD - = (b * (b ** 2) ** HALF n) MOD m by EXP_2 -*) -Theorem EXP_MOD_EQN: - !b n m. 1 < m ==> - ((b ** n) MOD m = - if (n = 0) then 1 - else let result = (b * b) ** (HALF n) MOD m - in if EVEN n then result else (b * result) MOD m) -Proof - rw[] - >- metis_tac[EXP_EVEN, EXP_2] >> - metis_tac[EXP_ODD, EXP_2, EVEN_ODD] -QED - -(* Pretty version of EXP_MOD_EQN, same pattern as EXP_EQN_ALT. *) - -(* Theorem: 1 < m ==> b ** n MOD m = - if n = 0 then 1 else - ((if EVEN n then 1 else b) * ((SQ b ** HALF n) MOD m)) MOD m *) -(* Proof: by EXP_MOD_EQN *) -val EXP_MOD_ALT = store_thm( - "EXP_MOD_ALT", - ``!b n m. 1 < m ==> b ** n MOD m = - if n = 0 then 1 else - ((if EVEN n then 1 else b) * ((SQ b ** HALF n) MOD m)) MOD m``, - rpt strip_tac >> - imp_res_tac EXP_MOD_EQN >> - last_x_assum (qspecl_then [`n`, `b`] strip_assume_tac) >> - rw[]); - -(* Theorem: x ** (y ** SUC n) = (x ** y) ** y ** n *) -(* Proof: - x ** (y ** SUC n) - = x ** (y * y ** n) by EXP - = (x ** y) ** (y ** n) by EXP_EXP_MULT -*) -val EXP_EXP_SUC = store_thm( - "EXP_EXP_SUC", - ``!x y n. x ** (y ** SUC n) = (x ** y) ** y ** n``, - rw[EXP, EXP_EXP_MULT]); - -(* Theorem: 1 + n * m <= (1 + m) ** n *) -(* Proof: - By induction on n. - Base: 1 + 0 * m <= (1 + m) ** 0 - LHS = 1 + 0 * m = 1 by arithmetic - RHS = (1 + m) ** 0 = 1 by EXP_0 - Hence true. - Step: 1 + n * m <= (1 + m) ** n ==> - 1 + SUC n * m <= (1 + m) ** SUC n - 1 + SUC n * m - = 1 + n * m + m by MULT - <= (1 + m) ** n + m by induction hypothesis - <= (1 + m) ** n + m * (1 + m) ** n by EXP_POS - <= (1 + m) * (1 + m) ** n by RIGHT_ADD_DISTRIB - = (1 + m) ** SUC n by EXP -*) -val EXP_LOWER_LE_LOW = store_thm( - "EXP_LOWER_LE_LOW", - ``!n m. 1 + n * m <= (1 + m) ** n``, - rpt strip_tac >> - Induct_on `n` >- - rw[EXP_0] >> - `0 < (1 + m) ** n` by rw[] >> - `1 + SUC n * m = 1 + (n * m + m)` by rw[MULT] >> - `_ = 1 + n * m + m` by decide_tac >> - `m <= m * (1 + m) ** n` by rw[] >> - `1 + SUC n * m <= (1 + m) ** n + m * (1 + m) ** n` by decide_tac >> - `(1 + m) ** n + m * (1 + m) ** n = (1 + m) * (1 + m) ** n` by decide_tac >> - `_ = (1 + m) ** SUC n` by rw[EXP] >> - decide_tac); - -(* Theorem: 0 < m /\ 1 < n ==> 1 + n * m < (1 + m) ** n *) -(* Proof: - By induction on n. - Base: 1 < 0 ==> 1 + 0 * m <= (1 + m) ** 0 - True since 1 < 0 = F. - Step: 1 < n ==> 1 + n * m < (1 + m) ** n ==> - 1 < SUC n ==> 1 + SUC n * m < (1 + m) ** SUC n - Note n <> 0, since SUC 0 = 1 by ONE - If n = 1, - Note m * m <> 0 by MULT_EQ_0, m <> 0 - (1 + m) ** SUC 1 - = (1 + m) ** 2 by TWO - = 1 + 2 * m + m * m by expansion - > 1 + 2 * m by 0 < m * m - = 1 + (SUC 1) * m - If n <> 1, then 1 < n. - 1 + SUC n * m - = 1 + n * m + m by MULT - < (1 + m) ** n + m by induction hypothesis, 1 < n - <= (1 + m) ** n + m * (1 + m) ** n by EXP_POS - <= (1 + m) * (1 + m) ** n by RIGHT_ADD_DISTRIB - = (1 + m) ** SUC n by EXP -*) -val EXP_LOWER_LT_LOW = store_thm( - "EXP_LOWER_LT_LOW", - ``!n m. 0 < m /\ 1 < n ==> 1 + n * m < (1 + m) ** n``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - rpt strip_tac >> - `n <> 0` by fs[] >> - Cases_on `n = 1` >| [ - simp[] >> - `(m + 1) ** 2 = (m + 1) * (m + 1)` by rw[GSYM EXP_2] >> - `_ = m * m + 2 * m + 1` by decide_tac >> - `0 < SQ m` by metis_tac[SQ_EQ_0, NOT_ZERO_LT_ZERO] >> - decide_tac, - `1 < n` by decide_tac >> - `0 < (1 + m) ** n` by rw[] >> - `1 + SUC n * m = 1 + (n * m + m)` by rw[MULT] >> - `_ = 1 + n * m + m` by decide_tac >> - `m <= m * (1 + m) ** n` by rw[] >> - `1 + SUC n * m < (1 + m) ** n + m * (1 + m) ** n` by decide_tac >> - `(1 + m) ** n + m * (1 + m) ** n = (1 + m) * (1 + m) ** n` by decide_tac >> - `_ = (1 + m) ** SUC n` by rw[EXP] >> - decide_tac - ]); - -(* -Note: EXP_LOWER_LE_LOW collects the first two terms of binomial expansion. - but EXP_LOWER_LE_HIGH collects the last two terms of binomial expansion. -*) - -(* Theorem: n * m ** (n - 1) + m ** n <= (1 + m) ** n *) -(* Proof: - By induction on n. - Base: 0 * m ** (0 - 1) + m ** 0 <= (1 + m) ** 0 - LHS = 0 * m ** (0 - 1) + m ** 0 - = 0 + 1 by EXP_0 - = 1 - <= 1 = (1 + m) ** 0 = RHS by EXP_0 - Step: n * m ** (n - 1) + m ** n <= (1 + m) ** n ==> - SUC n * m ** (SUC n - 1) + m ** SUC n <= (1 + m) ** SUC n - If n = 0, - LHS = 1 * m ** 0 + m ** 1 - = 1 + m by EXP_0, EXP_1 - = (1 + m) ** 1 = RHS by EXP_1 - If n <> 0, - Then SUC (n - 1) = n by n <> 0. - LHS = SUC n * m ** (SUC n - 1) + m ** SUC n - = (n + 1) * m ** n + m * m ** n by EXP, ADD1 - = (n + 1 + m) * m ** n by arithmetic - = n * m ** n + (1 + m) * m ** n by arithmetic - = n * m ** SUC (n - 1) + (1 + m) * m ** n by SUC (n - 1) = n - = n * m * m ** (n - 1) + (1 + m) * m ** n by EXP - = m * (n * m ** (n - 1)) + (1 + m) * m ** n by arithmetic - <= (1 + m) * (n * m ** (n - 1)) + (1 + m) * m ** n by m < 1 + m - = (1 + m) * (n * m ** (n - 1) + m ** n) by LEFT_ADD_DISTRIB - <= (1 + m) * (1 + m) ** n by induction hypothesis - = (1 + m) ** SUC n by EXP -*) -val EXP_LOWER_LE_HIGH = store_thm( - "EXP_LOWER_LE_HIGH", - ``!n m. n * m ** (n - 1) + m ** n <= (1 + m) ** n``, - rpt strip_tac >> - Induct_on `n` >- - simp[] >> - Cases_on `n = 0` >- - simp[EXP_0] >> - `SUC (n - 1) = n` by decide_tac >> - simp[EXP] >> - simp[ADD1] >> - `m * m ** n + (n + 1) * m ** n = (m + (n + 1)) * m ** n` by rw[LEFT_ADD_DISTRIB] >> - `_ = (n + (m + 1)) * m ** n` by decide_tac >> - `_ = n * m ** n + (m + 1) * m ** n` by rw[LEFT_ADD_DISTRIB] >> - `_ = n * m ** SUC (n - 1) + (m + 1) * m ** n` by rw[] >> - `_ = n * (m * m ** (n - 1)) + (m + 1) * m ** n` by rw[EXP] >> - `_ = m * (n * m ** (n - 1)) + (m + 1) * m ** n` by decide_tac >> - `m * (n * m ** (n - 1)) + (m + 1) * m ** n <= (m + 1) * (n * m ** (n - 1)) + (m + 1) * m ** n` by decide_tac >> - qabbrev_tac `t = n * m ** (n - 1) + m ** n` >> - `(m + 1) * (n * m ** (n - 1)) + (m + 1) * m ** n = (m + 1) * t` by rw[LEFT_ADD_DISTRIB, Abbr`t`] >> - `t <= (m + 1) ** n` by metis_tac[ADD_COMM] >> - `(m + 1) * t <= (m + 1) * (m + 1) ** n` by rw[] >> - decide_tac); - -(* Theorem: 1 < n ==> SUC n < 2 ** n *) -(* Proof: - Note 1 + n < (1 + 1) ** n by EXP_LOWER_LT_LOW, m = 1 - or SUC n < SUC 1 ** n by ADD1 - or SUC n < 2 ** n by TWO -*) -val SUC_X_LT_2_EXP_X = store_thm( - "SUC_X_LT_2_EXP_X", - ``!n. 1 < n ==> SUC n < 2 ** n``, - rpt strip_tac >> - `1 + n * 1 < (1 + 1) ** n` by rw[EXP_LOWER_LT_LOW] >> - fs[]); - -(* ------------------------------------------------------------------------- *) -(* DIVIDES Theorems *) -(* ------------------------------------------------------------------------- *) - -(* arithmeticTheory.LESS_DIV_EQ_ZERO = |- !r n. r < n ==> (r DIV n = 0) *) - -(* Theorem: 0 < n ==> ((m DIV n = 0) <=> m < n) *) -(* Proof: - If part: 0 < n /\ m DIV n = 0 ==> m < n - Since m = m DIV n * n + m MOD n) /\ (m MOD n < n) by DIVISION, 0 < n - so m = 0 * n + m MOD n by m DIV n = 0 - = 0 + m MOD n by MULT - = m MOD n by ADD - Since m MOD n < n, m < n. - Only-if part: 0 < n /\ m < n ==> m DIV n = 0 - True by LESS_DIV_EQ_ZERO. -*) -val DIV_EQUAL_0 = store_thm( - "DIV_EQUAL_0", - ``!m n. 0 < n ==> ((m DIV n = 0) <=> m < n)``, - rw[EQ_IMP_THM] >- - metis_tac[DIVISION, MULT, ADD] >> - rw[LESS_DIV_EQ_ZERO]); -(* This is an improvement of - arithmeticTheory.DIV_EQ_0 = |- 1 < b ==> (n DIV b = 0 <=> n < b) *) - -(* Theorem: 0 < m /\ m <= n ==> 0 < n DIV m *) -(* Proof: - Note n = (n DIV m) * m + n MOD m /\ - n MDO m < m by DIVISION, 0 < m - ==> n MOD m < n by m <= n - Thus 0 < (n DIV m) * m by inequality - so 0 < n DIV m by ZERO_LESS_MULT -*) -Theorem DIV_POS: - !m n. 0 < m /\ m <= n ==> 0 < n DIV m -Proof - rpt strip_tac >> - imp_res_tac (DIVISION |> SPEC_ALL) >> - first_x_assum (qspec_then `n` strip_assume_tac) >> - first_x_assum (qspec_then `n` strip_assume_tac) >> - `0 < (n DIV m) * m` by decide_tac >> - metis_tac[ZERO_LESS_MULT] -QED - -(* Theorem: 0 < z ==> (x DIV z = y DIV z <=> x - x MOD z = y - y MOD z) *) -(* Proof: - Note x = (x DIV z) * z + x MOD z by DIVISION - and y = (y DIV z) * z + y MDO z by DIVISION - x DIV z = y DIV z - <=> (x DIV z) * z = (y DIV z) * z by EQ_MULT_RCANCEL - <=> x - x MOD z = y - y MOD z by arithmetic -*) -Theorem DIV_EQ: - !x y z. 0 < z ==> (x DIV z = y DIV z <=> x - x MOD z = y - y MOD z) -Proof - rpt strip_tac >> - `x = (x DIV z) * z + x MOD z` by simp[DIVISION] >> - `y = (y DIV z) * z + y MOD z` by simp[DIVISION] >> - `x DIV z = y DIV z <=> (x DIV z) * z = (y DIV z) * z` by simp[] >> - decide_tac -QED - -(* Theorem: a MOD n + b < n ==> (a + b) DIV n = a DIV n *) -(* Proof: - Note 0 < n by a MOD n + b < n - a + b - = ((a DIV n) * n + a MOD n) + b by DIVISION, 0 < n - = (a DIV n) * n + (a MOD n + b) by ADD_ASSOC - - If a MOD n + b < n, - Then (a + b) DIV n = a DIV n /\ - (a + b) MOD n = a MOD n + b by DIVMOD_UNIQ -*) -Theorem ADD_DIV_EQ: - !n a b. a MOD n + b < n ==> (a + b) DIV n = a DIV n -Proof - rpt strip_tac >> - `0 < n` by decide_tac >> - `a = (a DIV n) * n + a MOD n` by simp[DIVISION] >> - `a + b = (a DIV n) * n + (a MOD n + b)` by decide_tac >> - metis_tac[DIVMOD_UNIQ] -QED - -(* -DIV_LE_MONOTONE |- !n x y. 0 < n /\ x <= y ==> x DIV n <= y DIV n -DIV_LE_X |- !x y z. 0 < z ==> (y DIV z <= x <=> y < (x + 1) * z) -*) - -(* Theorem: 0 < y /\ x <= y * z ==> x DIV y <= z *) -(* Proof: - x <= y * z - ==> x DIV y <= (y * z) DIV y by DIV_LE_MONOTONE, 0 < y - = z by MULT_TO_DIV -*) -val DIV_LE = store_thm( - "DIV_LE", - ``!x y z. 0 < y /\ x <= y * z ==> x DIV y <= z``, - metis_tac[DIV_LE_MONOTONE, MULT_TO_DIV]); - -(* Theorem: 0 < n ==> !x y. (x * n = y) ==> (x = y DIV n) *) -(* Proof: - x = (x * n + 0) DIV n by DIV_MULT, 0 < n - = (x * n) DIV n by ADD_0 -*) -val DIV_SOLVE = store_thm( - "DIV_SOLVE", - ``!n. 0 < n ==> !x y. (x * n = y) ==> (x = y DIV n)``, - metis_tac[DIV_MULT, ADD_0]); - -(* Theorem: 0 < n ==> !x y. (n * x = y) ==> (x = y DIV n) *) -(* Proof: by DIV_SOLVE, MULT_COMM *) -val DIV_SOLVE_COMM = store_thm( - "DIV_SOLVE_COMM", - ``!n. 0 < n ==> !x y. (n * x = y) ==> (x = y DIV n)``, - rw[DIV_SOLVE]); - -(* Theorem: 1 < n ==> (1 DIV n = 0) *) -(* Proof: - Since 1 = (1 DIV n) * n + (1 MOD n) by DIVISION, 0 < n. - and 1 MOD n = 1 by ONE_MOD, 1 < n. - thus (1 DIV n) * n = 0 by arithmetic - or 1 DIV n = 0 since n <> 0 by MULT_EQ_0 -*) -val ONE_DIV = store_thm( - "ONE_DIV", - ``!n. 1 < n ==> (1 DIV n = 0)``, - rpt strip_tac >> - `0 < n /\ n <> 0` by decide_tac >> - `1 = (1 DIV n) * n + (1 MOD n)` by rw[DIVISION] >> - `_ = (1 DIV n) * n + 1` by rw[ONE_MOD] >> - `(1 DIV n) * n = 0` by decide_tac >> - metis_tac[MULT_EQ_0]); - -(* Theorem: ODD n ==> !m. m divides n ==> ODD m *) -(* Proof: - Since m divides n - ==> ?q. n = q * m by divides_def - By contradiction, suppose ~ODD m. - Then EVEN m by ODD_EVEN - and EVEN (q * m) = EVEN n by EVEN_MULT - or ~ODD n by ODD_EVEN - This contradicts with ODD n. -*) -val DIVIDES_ODD = store_thm( - "DIVIDES_ODD", - ``!n. ODD n ==> !m. m divides n ==> ODD m``, - metis_tac[divides_def, EVEN_MULT, EVEN_ODD]); - -(* Note: For EVEN n, m divides n cannot conclude EVEN m. -Example: EVEN 2 or ODD 3 both divides EVEN 6. -*) - -(* Theorem: EVEN m ==> !n. m divides n ==> EVEN n*) -(* Proof: - Since m divides n - ==> ?q. n = q * m by divides_def - Given EVEN m - Then EVEN (q * m) = n by EVEN_MULT -*) -val DIVIDES_EVEN = store_thm( - "DIVIDES_EVEN", - ``!m. EVEN m ==> !n. m divides n ==> EVEN n``, - metis_tac[divides_def, EVEN_MULT]); - -(* Theorem: EVEN n = 2 divides n *) -(* Proof: - EVEN n - <=> n MOD 2 = 0 by EVEN_MOD2 - <=> 2 divides n by DIVIDES_MOD_0, 0 < 2 -*) -val EVEN_ALT = store_thm( - "EVEN_ALT", - ``!n. EVEN n = 2 divides n``, - rw[EVEN_MOD2, DIVIDES_MOD_0]); - -(* Theorem: ODD n = ~(2 divides n) *) -(* Proof: - Note n MOD 2 < 2 by MOD_LESS - and !x. x < 2 <=> (x = 0) \/ (x = 1) by arithmetic - ODD n - <=> n MOD 2 = 1 by ODD_MOD2 - <=> ~(2 divides n) by DIVIDES_MOD_0, 0 < 2 - Or, - ODD n = ~(EVEN n) by ODD_EVEN - = ~(2 divides n) by EVEN_ALT -*) -val ODD_ALT = store_thm( - "ODD_ALT", - ``!n. ODD n = ~(2 divides n)``, - metis_tac[EVEN_ODD, EVEN_ALT]); - -(* Theorem: 0 < n ==> !q. (q DIV n) * n <= q *) -(* Proof: - Since q = (q DIV n) * n + q MOD n by DIVISION - Thus (q DIV n) * n <= q by discarding remainder -*) -val DIV_MULT_LE = store_thm( - "DIV_MULT_LE", - ``!n. 0 < n ==> !q. (q DIV n) * n <= q``, - rpt strip_tac >> - `q = (q DIV n) * n + q MOD n` by rw[DIVISION] >> - decide_tac); - -(* Theorem: 0 < n ==> !q. n divides q <=> ((q DIV n) * n = q) *) -(* Proof: - If part: n divides q ==> q DIV n * n = q - q = (q DIV n) * n + q MOD n by DIVISION - = (q DIV n) * n + 0 by MOD_EQ_0_DIVISOR, divides_def - = (q DIV n) * n by ADD_0 - Only-if part: q DIV n * n = q ==> n divides q - True by divides_def -*) -val DIV_MULT_EQ = store_thm( - "DIV_MULT_EQ", - ``!n. 0 < n ==> !q. n divides q <=> ((q DIV n) * n = q)``, - metis_tac[divides_def, DIVISION, MOD_EQ_0_DIVISOR, ADD_0]); -(* same as DIVIDES_EQN below *) - -(* Theorem: 0 < m ==> m * (n DIV m) <= n /\ n < m * SUC (n DIV m) *) -(* Proof: - Note n = n DIV m * m + n MOD m /\ - n MOD m < m by DIVISION - Thus m * (n DIV m) <= n by MULT_COMM - and n < m * (n DIV m) + m - = m * (n DIV m + 1) by LEFT_ADD_DISTRIB - = m * SUC (n DIV m) by ADD1 -*) -val DIV_MULT_LESS_EQ = store_thm( - "DIV_MULT_LESS_EQ", - ``!m n. 0 < m ==> m * (n DIV m) <= n /\ n < m * SUC (n DIV m)``, - ntac 3 strip_tac >> - `(n = n DIV m * m + n MOD m) /\ n MOD m < m` by rw[DIVISION] >> - `n < m * (n DIV m) + m` by decide_tac >> - `m * (n DIV m) + m = m * (SUC (n DIV m))` by rw[ADD1] >> - decide_tac); - -(* Theorem: 0 < x /\ 0 < y /\ x <= y ==> !n. n DIV y <= n DIV x *) -(* Proof: - If n DIV y = 0, - Then 0 <= n DIV x is trivially true. - If n DIV y <> 0, - (n DIV y) * x <= (n DIV y) * y by LE_MULT_LCANCEL, x <= y, n DIV y <> 0 - <= n by DIV_MULT_LE - Hence (n DIV y) * x <= n by LESS_EQ_TRANS - Then ((n DIV y) * x) DIV x <= n DIV x by DIV_LE_MONOTONE - or n DIV y <= n DIV x by MULT_DIV -*) -val DIV_LE_MONOTONE_REVERSE = store_thm( - "DIV_LE_MONOTONE_REVERSE", - ``!x y. 0 < x /\ 0 < y /\ x <= y ==> !n. n DIV y <= n DIV x``, - rpt strip_tac >> - Cases_on `n DIV y = 0` >- - decide_tac >> - `(n DIV y) * x <= (n DIV y) * y` by rw[LE_MULT_LCANCEL] >> - `(n DIV y) * y <= n` by rw[DIV_MULT_LE] >> - `(n DIV y) * x <= n` by decide_tac >> - `((n DIV y) * x) DIV x <= n DIV x` by rw[DIV_LE_MONOTONE] >> - metis_tac[MULT_DIV]); - -(* Theorem: n divides m <=> (m = (m DIV n) * n) *) -(* Proof: - Since n divides m <=> m MOD n = 0 by DIVIDES_MOD_0 - and m = (m DIV n) * n + (m MOD n) by DIVISION - If part: n divides m ==> m = m DIV n * n - This is true by ADD_0 - Only-if part: m = m DIV n * n ==> n divides m - Since !x y. x + y = x <=> y = 0 by ADD_INV_0 - The result follows. -*) -val DIVIDES_EQN = store_thm( - "DIVIDES_EQN", - ``!n. 0 < n ==> !m. n divides m <=> (m = (m DIV n) * n)``, - metis_tac[DIVISION, DIVIDES_MOD_0, ADD_0, ADD_INV_0]); - -(* Theorem: 0 < n ==> !m. n divides m <=> (m = n * (m DIV n)) *) -(* Proof: vy DIVIDES_EQN, MULT_COMM *) -val DIVIDES_EQN_COMM = store_thm( - "DIVIDES_EQN_COMM", - ``!n. 0 < n ==> !m. n divides m <=> (m = n * (m DIV n))``, - rw_tac std_ss[DIVIDES_EQN, MULT_COMM]); - -(* Theorem: 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1) *) -(* Proof: - Apply DIV_SUB |> GEN_ALL |> SPEC ``1`` |> REWRITE_RULE[MULT_RIGHT_1]; - val it = |- !n m. 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1): thm -*) -val SUB_DIV = save_thm("SUB_DIV", - DIV_SUB |> GEN ``n:num`` |> GEN ``m:num`` |> GEN ``q:num`` |> SPEC ``1`` |> REWRITE_RULE[MULT_RIGHT_1]); -(* val SUB_DIV = |- !m n. 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1): thm *) - - -(* Note: -SUB_DIV |- !m n. 0 < n /\ n <= m ==> (m - n) DIV n = m DIV n - 1 -SUB_MOD |- !m n. 0 < n /\ n <= m ==> (m - n) MOD n = m MOD n -*) - -(* Theorem: 0 < n ==> (m - n) DIV n = if m < n then 0 else (m DIV n - 1) *) -(* Proof: - If m < n, then m - n = 0, so (m - n) DIV n = 0 by ZERO_DIV - Otherwise, n <= m, and (m - n) DIV n = m DIV n - 1 by SUB_DIV -*) -val SUB_DIV_EQN = store_thm( - "SUB_DIV_EQN", - ``!m n. 0 < n ==> ((m - n) DIV n = if m < n then 0 else (m DIV n - 1))``, - rw[SUB_DIV] >> - `m - n = 0` by decide_tac >> - rw[ZERO_DIV]); - -(* Theorem: 0 < n ==> (m - n) MOD n = if m < n then 0 else m MOD n *) -(* Proof: - If m < n, then m - n = 0, so (m - n) MOD n = 0 by ZERO_MOD - Otherwise, n <= m, and (m - n) MOD n = m MOD n by SUB_MOD -*) -val SUB_MOD_EQN = store_thm( - "SUB_MOD_EQN", - ``!m n. 0 < n ==> ((m - n) MOD n = if m < n then 0 else m MOD n)``, - rw[SUB_MOD]); - -(* -Note: !n. 0 < n ==> !k m. (m MOD n = 0) ==> ((k * n = m) <=> (k = m DIV n)) is almost MULT_EQ_DIV, - but actually DIVIDES_EQN, DIVIDES_MOD_0, EQ_MULT_RCANCEL. See below. -Note: !n. 0 < n ==> !k m. (m MOD n = 0) ==> (k * n <= m <=> k <= m DIV n) is X_LE_DIV, no m MOD n = 0. -Note: !n. 0 < n ==> !k m. (m MOD n = 0) ==> ((k + 1) * n <= m <=> k < m DIV n) is X_LT_DIV, no m MOD n = 0. -Note: !n. 0 < n ==> !k m. (m MOD n = 0) ==> (k * n < m <=> k < m DIV n) is below next. -*) - -(* Theorem: 0 < n ==> !k m. (m MOD n = 0) ==> ((k * n = m) <=> (k = m DIV n)) *) -(* Proof: - Note m MOD n = 0 - ==> n divides m by DIVIDES_MOD_0, 0 < n - ==> m = (m DIV n) * n by DIVIDES_EQN, 0 < n - k * n = m - <=> k * n = (m DIV n) * n by above - <=> k = (m DIV n) by EQ_MULT_RCANCEL, n <> 0. -*) -val DIV_EQ_MULT = store_thm( - "DIV_EQ_MULT", - ``!n. 0 < n ==> !k m. (m MOD n = 0) ==> ((k * n = m) <=> (k = m DIV n))``, - rpt strip_tac >> - `n <> 0` by decide_tac >> - `m = (m DIV n) * n` by rw[GSYM DIVIDES_EQN, DIVIDES_MOD_0] >> - metis_tac[EQ_MULT_RCANCEL]); - -(* Theorem: 0 < n ==> !k m. (m MOD n = 0) ==> (k * n < m <=> k < m DIV n) *) -(* Proof: - k * n < m - <=> k * n < (m DIV n) * n by DIVIDES_EQN, DIVIDES_MOD_0, 0 < n - <=> k < m DIV n by LT_MULT_RCANCEL, n <> 0 -*) -val MULT_LT_DIV = store_thm( - "MULT_LT_DIV", - ``!n. 0 < n ==> !k m. (m MOD n = 0) ==> (k * n < m <=> k < m DIV n)``, - metis_tac[DIVIDES_EQN, DIVIDES_MOD_0, LT_MULT_RCANCEL, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < n ==> !k m. (m MOD n = 0) ==> (m <= n * k <=> m DIV n <= k) *) -(* Proof: - m <= n * k - <=> (m DIV n) * n <= n * k by DIVIDES_EQN, DIVIDES_MOD_0, 0 < n - <=> (m DIV n) * n <= k * n by MULT_COMM - <=> m DIV n <= k by LE_MULT_RCANCEL, n <> 0 -*) -val LE_MULT_LE_DIV = store_thm( - "LE_MULT_LE_DIV", - ``!n. 0 < n ==> !k m. (m MOD n = 0) ==> (m <= n * k <=> m DIV n <= k)``, - metis_tac[DIVIDES_EQN, DIVIDES_MOD_0, MULT_COMM, LE_MULT_RCANCEL, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < m ==> ((n DIV m = 0) /\ (n MOD m = 0) <=> (n = 0)) *) -(* Proof: - If part: (n DIV m = 0) /\ (n MOD m = 0) ==> (n = 0) - Note n DIV m = 0 ==> n < m by DIV_EQUAL_0 - Thus n MOD m = n by LESS_MOD - or n = 0 - Only-if part: 0 DIV m = 0 by ZERO_DIV - 0 MOD m = 0 by ZERO_MOD -*) -Theorem DIV_MOD_EQ_0: - !m n. 0 < m ==> ((n DIV m = 0) /\ (n MOD m = 0) <=> (n = 0)) -Proof - rpt strip_tac >> - rw[EQ_IMP_THM] >> - metis_tac[DIV_EQUAL_0, LESS_MOD] -QED - -(* Theorem: 0 < m /\ 0 < n /\ (n MOD m = 0) ==> n DIV (SUC m) < n DIV m *) -(* Proof: - Note n = n DIV (SUC m) * (SUC m) + n MOD (SUC m) by DIVISION - = n DIV m * m + n MOD m by DIVISION - = n DIV m * m by n MOD m = 0 - Thus n DIV SUC m * SUC m <= n DIV m * m by arithmetic - Note m < SUC m by LESS_SUC - and n DIV m <> 0, or 0 < n DIV m by DIV_MOD_EQ_0 - Thus n DIV (SUC m) < n DIV m by LE_IMP_REVERSE_LT -*) -val DIV_LT_SUC = store_thm( - "DIV_LT_SUC", - ``!m n. 0 < m /\ 0 < n /\ (n MOD m = 0) ==> n DIV (SUC m) < n DIV m``, - rpt strip_tac >> - `n DIV m * m = n` by metis_tac[DIVISION, ADD_0] >> - `_ = n DIV (SUC m) * (SUC m) + n MOD (SUC m)` by metis_tac[DIVISION, SUC_POS] >> - `n DIV SUC m * SUC m <= n DIV m * m` by decide_tac >> - `m < SUC m` by decide_tac >> - `0 < n DIV m` by metis_tac[DIV_MOD_EQ_0, NOT_ZERO_LT_ZERO] >> - metis_tac[LE_IMP_REVERSE_LT]); - -(* Theorem: 0 < x /\ 0 < y /\ x < y ==> !n. 0 < n /\ (n MOD x = 0) ==> n DIV y < n DIV x *) -(* Proof: - Note x < y ==> SUC x <= y by arithmetic - Thus n DIV y <= n DIV (SUC x) by DIV_LE_MONOTONE_REVERSE - But 0 < x /\ 0 < n /\ (n MOD x = 0) by given - ==> n DIV (SUC x) < n DIV x by DIV_LT_SUC - Hence n DIV y < n DIV x by inequalities -*) -val DIV_LT_MONOTONE_REVERSE = store_thm( - "DIV_LT_MONOTONE_REVERSE", - ``!x y. 0 < x /\ 0 < y /\ x < y ==> !n. 0 < n /\ (n MOD x = 0) ==> n DIV y < n DIV x``, - rpt strip_tac >> - `SUC x <= y` by decide_tac >> - `n DIV y <= n DIV (SUC x)` by rw[DIV_LE_MONOTONE_REVERSE] >> - `n DIV (SUC x) < n DIV x` by rw[DIV_LT_SUC] >> - decide_tac); - -(* Theorem: 0 < n /\ a ** n divides b ==> a divides b *) -(* Proof: - Note ?k. n = SUC k by num_CASES, n <> 0 - and ?q. b = q * (a ** n) by divides_def - = q * (a * a ** k) by EXP - = (q * a ** k) * a by arithmetic - Thus a divides b by divides_def -*) -val EXP_DIVIDES = store_thm( - "EXP_DIVIDES", - ``!a b n. 0 < n /\ a ** n divides b ==> a divides b``, - rpt strip_tac >> - `?k. n = SUC k` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> - `?q. b = q * a ** n` by rw[GSYM divides_def] >> - `_ = q * (a * a ** k)` by rw[EXP] >> - `_ = (q * a ** k) * a` by decide_tac >> - metis_tac[divides_def]); - -(* Theorem: prime a ==> a divides b iff a divides b ** n for any n *) -(* Proof: - by induction on n. - Base case: 0 < 0 ==> (a divides b <=> a divides (b ** 0)) - True since 0 < 0 is False. - Step case: 0 < n ==> (a divides b <=> a divides (b ** n)) ==> - 0 < SUC n ==> (a divides b <=> a divides (b ** SUC n)) - i.e. 0 < n ==> (a divides b <=> a divides (b ** n))==> - a divides b <=> a divides (b * b ** n) by EXP - If n = 0, b ** 0 = 1 by EXP - Hence true. - If n <> 0, 0 < n, - If part: a divides b /\ 0 < n ==> (a divides b <=> a divides (b ** n)) ==> a divides (b ** n) - True by DIVIDES_MULT. - Only-if part: a divides (b * b ** n) /\ 0 < n ==> (a divides b <=> a divides (b ** n)) ==> a divides (b ** n) - Since prime a, a divides b, or a divides (b ** n) by P_EUCLIDES - Either case is true. -*) -val DIVIDES_EXP_BASE = store_thm( - "DIVIDES_EXP_BASE", - ``!a b n. prime a /\ 0 < n ==> (a divides b <=> a divides (b ** n))``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - rw[EXP] >> - Cases_on `n = 0` >- - rw[EXP] >> - `0 < n` by decide_tac >> - rw[EQ_IMP_THM] >- - metis_tac[DIVIDES_MULT] >> - `a divides b \/ a divides (b ** n)` by rw[P_EUCLIDES] >> - metis_tac[]); - -(* Theorem: n divides m ==> !k. n divides (k * m) *) -(* Proof: - n divides m ==> ?q. m = q * n by divides_def - Hence k * m = k * (q * n) - = (k * q) * n by MULT_ASSOC - or n divides (k * m) by divides_def -*) -val DIVIDES_MULTIPLE = store_thm( - "DIVIDES_MULTIPLE", - ``!m n. n divides m ==> !k. n divides (k * m)``, - metis_tac[divides_def, MULT_ASSOC]); - -(* Theorem: k <> 0 ==> (m divides n <=> (k * m) divides (k * n)) *) -(* Proof: - m divides n - <=> ?q. n = q * m by divides_def - <=> ?q. k * n = k * (q * m) by EQ_MULT_LCANCEL, k <> 0 - <=> ?q. k * n = q * (k * m) by MULT_ASSOC, MULT_COMM - <=> (k * m) divides (k * n) by divides_def -*) -val DIVIDES_MULTIPLE_IFF = store_thm( - "DIVIDES_MULTIPLE_IFF", - ``!m n k. k <> 0 ==> (m divides n <=> (k * m) divides (k * n))``, - rpt strip_tac >> - `m divides n <=> ?q. n = q * m` by rw[GSYM divides_def] >> - `_ = ?q. (k * n = k * (q * m))` by rw[EQ_MULT_LCANCEL] >> - metis_tac[divides_def, MULT_COMM, MULT_ASSOC]); - -(* Theorem: 0 < n /\ n divides m ==> m = n * (m DIV n) *) -(* Proof: - n divides m <=> m MOD n = 0 by DIVIDES_MOD_0 - m = (m DIV n) * n + (m MOD n) by DIVISION - = (m DIV n) * n by above - = n * (m DIV n) by MULT_COMM -*) -val DIVIDES_FACTORS = store_thm( - "DIVIDES_FACTORS", - ``!m n. 0 < n /\ n divides m ==> (m = n * (m DIV n))``, - metis_tac[DIVISION, DIVIDES_MOD_0, ADD_0, MULT_COMM]); - -(* Theorem: 0 < n /\ n divides m ==> (m DIV n) divides m *) -(* Proof: - By DIVIDES_FACTORS: m = (m DIV n) * n - Hence (m DIV n) | m by divides_def -*) -val DIVIDES_COFACTOR = store_thm( - "DIVIDES_COFACTOR", - ``!m n. 0 < n /\ n divides m ==> (m DIV n) divides m``, - metis_tac[DIVIDES_FACTORS, divides_def]); - -(* Theorem: n divides q ==> p * (q DIV n) = (p * q) DIV n *) -(* Proof: - n divides q ==> q MOD n = 0 by DIVIDES_MOD_0 - p * q = p * ((q DIV n) * n + q MOD n) by DIVISION - = p * ((q DIV n) * n) by ADD_0 - = p * (q DIV n) * n by MULT_ASSOC - = p * (q DIV n) * n + 0 by ADD_0 - Hence (p * q) DIV n = p * (q DIV n) by DIV_UNIQUE, 0 < n: - |- !n k q. (?r. (k = q * n + r) /\ r < n) ==> (k DIV n = q) -*) -val MULTIPLY_DIV = store_thm( - "MULTIPLY_DIV", - ``!n p q. 0 < n /\ n divides q ==> (p * (q DIV n) = (p * q) DIV n)``, - rpt strip_tac >> - `q MOD n = 0` by rw[GSYM DIVIDES_MOD_0] >> - `p * q = p * ((q DIV n) * n)` by metis_tac[DIVISION, ADD_0] >> - `_ = p * (q DIV n) * n + 0` by rw[MULT_ASSOC] >> - metis_tac[DIV_UNIQUE]); - -(* Note: The condition: n divides q is important: -> EVAL ``5 * (10 DIV 3)``; -val it = |- 5 * (10 DIV 3) = 15: thm -> EVAL ``(5 * 10) DIV 3``; -val it = |- 5 * 10 DIV 3 = 16: thm -*) - -(* Theorem: 0 < n /\ m divides n ==> !x. (x MOD n) MOD m = x MOD m *) -(* Proof: - Note 0 < m by ZERO_DIVIDES, 0 < n - Given divides m n ==> ?q. n = q * m by divides_def - Since x = (x DIV n) * n + (x MOD n) by DIVISION - = (x DIV n) * (q * m) + (x MOD n) by above - = ((x DIV n) * q) * m + (x MOD n) by MULT_ASSOC - Hence x MOD m - = ((x DIV n) * q) * m + (x MOD n)) MOD m by above - = (((x DIV n) * q * m) MOD m + (x MOD n) MOD m) MOD m by MOD_PLUS - = (0 + (x MOD n) MOD m) MOD m by MOD_EQ_0 - = (x MOD n) MOD m by ADD, MOD_MOD -*) -val DIVIDES_MOD_MOD = store_thm( - "DIVIDES_MOD_MOD", - ``!m n. 0 < n /\ m divides n ==> !x. (x MOD n) MOD m = x MOD m``, - rpt strip_tac >> - `0 < m` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> - `?q. n = q * m` by rw[GSYM divides_def] >> - `x MOD m = ((x DIV n) * n + (x MOD n)) MOD m` by rw[GSYM DIVISION] >> - `_ = (((x DIV n) * q) * m + (x MOD n)) MOD m` by rw[MULT_ASSOC] >> - `_ = (((x DIV n) * q * m) MOD m + (x MOD n) MOD m) MOD m` by rw[MOD_PLUS] >> - rw[MOD_EQ_0, MOD_MOD]); - -(* Theorem: m divides n <=> (m * k) divides (n * k) *) -(* Proof: by divides_def and EQ_MULT_LCANCEL. *) -val DIVIDES_CANCEL = store_thm( - "DIVIDES_CANCEL", - ``!k. 0 < k ==> !m n. m divides n <=> (m * k) divides (n * k)``, - rw[divides_def] >> - `k <> 0` by decide_tac >> - `!q. (q * m) * k = q * (m * k)` by rw_tac arith_ss[] >> - metis_tac[EQ_MULT_LCANCEL, MULT_COMM]); - -(* Theorem: m divides n ==> (k * m) divides (k * n) *) -(* Proof: - m divides n - ==> ?q. n = q * m by divides_def - So k * n = k * (q * m) - = (k * q) * m by MULT_ASSOC - = (q * k) * m by MULT_COMM - = q * (k * m) by MULT_ASSOC - Hence (k * m) divides (k * n) by divides_def -*) -val DIVIDES_CANCEL_COMM = store_thm( - "DIVIDES_CANCEL_COMM", - ``!m n k. m divides n ==> (k * m) divides (k * n)``, - metis_tac[MULT_ASSOC, MULT_COMM, divides_def]); - -(* Theorem: 0 < n /\ 0 < m ==> !x. n divides x ==> ((m * x) DIV (m * n) = x DIV n) *) -(* Proof: - n divides x ==> x = n * (x DIV n) by DIVIDES_FACTORS - or m * x = (m * n) * (x DIV n) by MULT_ASSOC - n divides x - ==> divides (m * n) (m * x) by DIVIDES_CANCEL_COMM - ==> m * x = (m * n) * ((m * x) DIV (m * n)) by DIVIDES_FACTORS - Equating expressions for m * x, - (m * n) * (x DIV n) = (m * n) * ((m * x) DIV (m * n)) - or x DIV n = (m * x) DIV (m * n) by MULT_LEFT_CANCEL -*) -val DIV_COMMON_FACTOR = store_thm( - "DIV_COMMON_FACTOR", - ``!m n. 0 < n /\ 0 < m ==> !x. n divides x ==> ((m * x) DIV (m * n) = x DIV n)``, - rpt strip_tac >> - `!n. n <> 0 <=> 0 < n` by decide_tac >> - `0 < m * n` by metis_tac[MULT_EQ_0] >> - metis_tac[DIVIDES_CANCEL_COMM, DIVIDES_FACTORS, MULT_ASSOC, MULT_LEFT_CANCEL]); - -(* Theorem: 0 < n /\ 0 < m /\ 0 < m DIV n /\ - n divides m /\ m divides x /\ (m DIV n) divides x ==> - (x DIV (m DIV n) = n * (x DIV m)) *) -(* Proof: - x DIV (m DIV n) - = (n * x) DIV (n * (m DIV n)) by DIV_COMMON_FACTOR, (m DIV n) divides x, 0 < m DIV n. - = (n * x) DIV m by DIVIDES_FACTORS, n divides m, 0 < n. - = n * (x DIV m) by MULTIPLY_DIV, m divides x, 0 < m. -*) -val DIV_DIV_MULT = store_thm( - "DIV_DIV_MULT", - ``!m n x. 0 < n /\ 0 < m /\ 0 < m DIV n /\ - n divides m /\ m divides x /\ (m DIV n) divides x ==> - (x DIV (m DIV n) = n * (x DIV m))``, - metis_tac[DIV_COMMON_FACTOR, DIVIDES_FACTORS, MULTIPLY_DIV]); - -(* ------------------------------------------------------------------------- *) -(* Basic Divisibility *) -(* ------------------------------------------------------------------------- *) - -(* Overload on coprime for GCD equals 1 *) -val _ = overload_on ("coprime", ``\x y. gcd x y = 1``); - -(* Idea: a little trick to make divisibility to mean equality. *) - -(* Theorem: 0 < n /\ n < 2 * m ==> (m divides n <=> n = m) *) -(* Proof: - If part: 0 < n /\ n < 2 * m /\ m divides n ==> n = m - Note ?k. n = k * m by divides_def - Now k * m < 2 * m by n < 2 * m - so 0 < m /\ k < 2 by LT_MULT_LCANCEL - and 0 < k by MULT - so 1 <= k by LE_MULT_LCANCEL, 0 < m - Thus k = 1, or n = m. - Only-if part: true by DIVIDES_REFL -*) -Theorem divides_iff_equal: - !m n. 0 < n /\ n < 2 * m ==> (m divides n <=> n = m) -Proof - rw[EQ_IMP_THM] >> - `?k. n = k * m` by rw[GSYM divides_def] >> - `0 < m /\ k < 2` by fs[LT_MULT_LCANCEL] >> - `0 < k` by fs[] >> - `k = 1` by decide_tac >> - simp[] -QED - -(* Theorem: 0 < m /\ n divides m ==> !t. m divides (t * n) <=> (m DIV n) divides t *) -(* Proof: - Let k = m DIV n. - Since m <> 0, n divides m ==> n <> 0 by ZERO_DIVIDES - Thus m = k * n by DIVIDES_EQN, 0 < n - so 0 < k by MULT, NOT_ZERO_LT_ZERO - Hence k * n divides t * n <=> k divides t by DIVIDES_CANCEL, 0 < k -*) -val dividend_divides_divisor_multiple = store_thm( - "dividend_divides_divisor_multiple", - ``!m n. 0 < m /\ n divides m ==> !t. m divides (t * n) <=> (m DIV n) divides t``, - rpt strip_tac >> - qabbrev_tac `k = m DIV n` >> - `0 < n` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - `m = k * n` by rw[GSYM DIVIDES_EQN, Abbr`k`] >> - `0 < k` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> - metis_tac[DIVIDES_CANCEL]); - -(* Theorem: 0 < n /\ m divides n ==> 0 < m *) -(* Proof: - Since 0 < n means n <> 0, - then m divides n ==> m <> 0 by ZERO_DIVIDES - or 0 < m by NOT_ZERO_LT_ZERO -*) -val divisor_pos = store_thm( - "divisor_pos", - ``!m n. 0 < n /\ m divides n ==> 0 < m``, - metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < n /\ m divides n ==> 0 < m /\ m <= n *) -(* Proof: - Since 0 < n /\ m divides n, - then 0 < m by divisor_pos - and m <= n by DIVIDES_LE -*) -val divides_pos = store_thm( - "divides_pos", - ``!m n. 0 < n /\ m divides n ==> 0 < m /\ m <= n``, - metis_tac[divisor_pos, DIVIDES_LE]); - -(* Theorem: 0 < n /\ m divides n ==> (n DIV (n DIV m) = m) *) -(* Proof: - Since 0 < n /\ m divides n, 0 < m by divisor_pos - Hence n = (n DIV m) * m by DIVIDES_EQN, 0 < m - Note 0 < n DIV m, otherwise contradicts 0 < n by MULT - Now n = m * (n DIV m) by MULT_COMM - = m * (n DIV m) + 0 by ADD_0 - Therefore n DIV (n DIV m) = m by DIV_UNIQUE -*) -val divide_by_cofactor = store_thm( - "divide_by_cofactor", - ``!m n. 0 < n /\ m divides n ==> (n DIV (n DIV m) = m)``, - rpt strip_tac >> - `0 < m` by metis_tac[divisor_pos] >> - `n = (n DIV m) * m` by rw[GSYM DIVIDES_EQN] >> - `0 < n DIV m` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> - `n = m * (n DIV m) + 0` by metis_tac[MULT_COMM, ADD_0] >> - metis_tac[DIV_UNIQUE]); - -(* Theorem: 0 < n ==> !a b. a divides b ==> a divides b ** n *) -(* Proof: - Since 0 < n, n = SUC m for some m. - thus b ** n = b ** (SUC m) - = b * b ** m by EXP - Now a divides b means - ?k. b = k * a by divides_def - so b ** n - = k * a * b ** m - = (k * b ** m) * a by MULT_COMM, MULT_ASSOC - Hence a divides (b ** n) by divides_def -*) -val divides_exp = store_thm( - "divides_exp", - ``!n. 0 < n ==> !a b. a divides b ==> a divides b ** n``, - rw_tac std_ss[divides_def] >> - `n <> 0` by decide_tac >> - `?m. n = SUC m` by metis_tac[num_CASES] >> - `(q * a) ** n = q * a * (q * a) ** m` by rw[EXP] >> - `_ = q * (q * a) ** m * a` by rw[MULT_COMM, MULT_ASSOC] >> - metis_tac[]); - -(* Note; converse need prime divisor: -DIVIDES_EXP_BASE |- !a b n. prime a /\ 0 < n ==> (a divides b <=> a divides b ** n) -Counter-example for a general base: 12 divides 36 = 6^2, but ~(12 divides 6) -*) - -(* Better than: DIVIDES_ADD_1 |- !a b c. a divides b /\ a divides c ==> a divides b + c *) - -(* Theorem: c divides a /\ c divides b ==> !h k. c divides (h * a + k * b) *) -(* Proof: - Since c divides a, ?u. a = u * c by divides_def - and c divides b, ?v. b = v * c by divides_def - h * a + k * b - = h * (u * c) + k * (v * c) by above - = h * u * c + k * v * c by MULT_ASSOC - = (h * u + k * v) * c by RIGHT_ADD_DISTRIB - Hence c divides (h * a + k * b) by divides_def -*) -val divides_linear = store_thm( - "divides_linear", - ``!a b c. c divides a /\ c divides b ==> !h k. c divides (h * a + k * b)``, - rw_tac std_ss[divides_def] >> - metis_tac[RIGHT_ADD_DISTRIB, MULT_ASSOC]); - -(* Theorem: c divides a /\ c divides b ==> !h k d. (h * a = k * b + d) ==> c divides d *) -(* Proof: - If c = 0, - 0 divides a ==> a = 0 by ZERO_DIVIDES - 0 divides b ==> b = 0 by ZERO_DIVIDES - Thus d = 0 by arithmetic - and 0 divides 0 by ZERO_DIVIDES - If c <> 0, 0 < c. - c divides a ==> (a MOD c = 0) by DIVIDES_MOD_0 - c divides b ==> (b MOD c = 0) by DIVIDES_MOD_0 - Hence 0 = (h * a) MOD c by MOD_TIMES2, ZERO_MOD - = (0 + d MOD c) MOD c by MOD_PLUS, MOD_TIMES2, ZERO_MOD - = d MOD c by MOD_MOD - or c divides d by DIVIDES_MOD_0 -*) -val divides_linear_sub = store_thm( - "divides_linear_sub", - ``!a b c. c divides a /\ c divides b ==> !h k d. (h * a = k * b + d) ==> c divides d``, - rpt strip_tac >> - Cases_on `c = 0` >| [ - `(a = 0) /\ (b = 0)` by metis_tac[ZERO_DIVIDES] >> - `d = 0` by rw_tac arith_ss[] >> - rw[], - `0 < c` by decide_tac >> - `(a MOD c = 0) /\ (b MOD c = 0)` by rw[GSYM DIVIDES_MOD_0] >> - `0 = (h * a) MOD c` by metis_tac[MOD_TIMES2, ZERO_MOD, MULT_0] >> - `_ = (0 + d MOD c) MOD c` by metis_tac[MOD_PLUS, MOD_TIMES2, ZERO_MOD, MULT_0] >> - `_ = d MOD c` by rw[MOD_MOD] >> - rw[DIVIDES_MOD_0] - ]); - -(* Theorem: 1 < p ==> !m n. p ** m divides p ** n <=> m <= n *) -(* Proof: - Note p <> 0 /\ p <> 1 by 1 < p - - If-part: p ** m divides p ** n ==> m <= n - By contradiction, suppose n < m. - Let d = m - n, then d <> 0 by n < m - Note p ** m = p ** n * p ** d by EXP_BY_ADD_SUB_LT - and p ** n <> 0 by EXP_EQ_0, p <> 0 - Now ?q. p ** n = q * p ** m by divides_def - = q * p ** d * p ** n by MULT_ASSOC_COMM - Thus 1 * p ** n = q * p ** d * p ** n by MULT_LEFT_1 - or 1 = q * p ** d by MULT_RIGHT_CANCEL - ==> p ** d = 1 by MULT_EQ_1 - or d = 0 by EXP_EQ_1, p <> 1 - This contradicts d <> 0. - - Only-if part: m <= n ==> p ** m divides p ** n - Note p ** n = p ** m * p ** (n - m) by EXP_BY_ADD_SUB_LE - Thus p ** m divides p ** n by divides_def, MULT_COMM -*) -val power_divides_iff = store_thm( - "power_divides_iff", - ``!p. 1 < p ==> !m n. p ** m divides p ** n <=> m <= n``, - rpt strip_tac >> - `p <> 0 /\ p <> 1` by decide_tac >> - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `n < m /\ m - n <> 0` by decide_tac >> - qabbrev_tac `d = m - n` >> - `p ** m = p ** n * p ** d` by rw[EXP_BY_ADD_SUB_LT, Abbr`d`] >> - `p ** n <> 0` by rw[EXP_EQ_0] >> - `?q. p ** n = q * p ** m` by rw[GSYM divides_def] >> - `_ = q * p ** d * p ** n` by metis_tac[MULT_ASSOC_COMM] >> - `1 = q * p ** d` by metis_tac[MULT_RIGHT_CANCEL, MULT_LEFT_1] >> - `p ** d = 1` by metis_tac[MULT_EQ_1] >> - metis_tac[EXP_EQ_1], - `p ** n = p ** m * p ** (n - m)` by rw[EXP_BY_ADD_SUB_LE] >> - metis_tac[divides_def, MULT_COMM] - ]); - -(* Theorem: prime p ==> !m n. p ** m divides p ** n <=> m <= n *) -(* Proof: by power_divides_iff, ONE_LT_PRIME *) -val prime_power_divides_iff = store_thm( - "prime_power_divides_iff", - ``!p. prime p ==> !m n. p ** m divides p ** n <=> m <= n``, - rw[power_divides_iff, ONE_LT_PRIME]); - -(* Theorem: 0 < n /\ 1 < p ==> p divides p ** n *) -(* Proof: - Note 0 < n <=> 1 <= n by arithmetic - so p ** 1 divides p ** n by power_divides_iff - or p divides p ** n by EXP_1 -*) -val divides_self_power = store_thm( - "divides_self_power", - ``!n p. 0 < n /\ 1 < p ==> p divides p ** n``, - metis_tac[power_divides_iff, EXP_1, DECIDE``0 < n <=> 1 <= n``]); - -(* Theorem: a divides b /\ 0 < b /\ b < 2 * a ==> (b = a) *) -(* Proof: - Note ?k. b = k * a by divides_def - and 0 < k by MULT_EQ_0, 0 < b - and k < 2 by LT_MULT_RCANCEL, k * a < 2 * a - Thus k = 1 by 0 < k < 2 - or b = k * a = a by arithmetic -*) -Theorem divides_eq_thm: - !a b. a divides b /\ 0 < b /\ b < 2 * a ==> (b = a) -Proof - rpt strip_tac >> - `?k. b = k * a` by rw[GSYM divides_def] >> - `0 < k` by metis_tac[MULT_EQ_0, NOT_ZERO] >> - `k < 2` by metis_tac[LT_MULT_RCANCEL] >> - `k = 1` by decide_tac >> - simp[] -QED - -(* Idea: factor equals cofactor iff the number is a square of the factor. *) - -(* Theorem: 0 < m /\ m divides n ==> (m = n DIV m <=> n = m ** 2) *) -(* Proof: - n - = n DIV m * m + n MOD m by DIVISION, 0 < m - = n DIV m * m + 0 by DIVIDES_MOD_0, m divides n - = n DIV m * m by ADD_0 - If m = n DIV m, - then n = m * m = m ** 2 by EXP_2 - If n = m ** 2, - then n = m * m by EXP_2 - so m = n DIV m by EQ_MULT_RCANCEL -*) -Theorem factor_eq_cofactor: - !m n. 0 < m /\ m divides n ==> (m = n DIV m <=> n = m ** 2) -Proof - rw[EQ_IMP_THM] >> - `n = n DIV m * m + n MOD m` by rw[DIVISION] >> - `_ = m * m + 0` by metis_tac[DIVIDES_MOD_0] >> - simp[] -QED - -(* Theorem alias *) -Theorem euclid_prime = gcdTheory.P_EUCLIDES; -(* |- !p a b. prime p /\ p divides a * b ==> p divides a \/ p divides b *) - -(* Theorem alias *) -Theorem euclid_coprime = gcdTheory.L_EUCLIDES; -(* |- !a b c. coprime a b /\ b divides a * c ==> b divides c *) - -(* ------------------------------------------------------------------------- *) -(* Modulo Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < n ==> !a b. (a MOD n = b) <=> ?c. (a = c * n + b) /\ (b < n) *) -(* Proof: - If part: (a MOD n = b) ==> ?c. (a = c * n + b) /\ (b < n) - Or to show: ?c. (a = c * n + a MOD n) /\ a MOD n < n - Taking c = a DIV n, this is true by DIVISION - Only-if part: (a = c * n + b) /\ (b < n) ==> (a MOD n = b) - Or to show: b < n ==> (c * n + b) MOD n = b - (c * n + b) MOD n - = ((c * n) MOD n + b MOD n) MOD n by MOD_PLUS - = (0 + b MOD n) MOD n by MOD_EQ_0 - = b MOD n by MOD_MOD - = b by LESS_MOD, b < n -*) -val MOD_EQN = store_thm( - "MOD_EQN", - ``!n. 0 < n ==> !a b. (a MOD n = b) <=> ?c. (a = c * n + b) /\ (b < n)``, - rw_tac std_ss[EQ_IMP_THM] >- - metis_tac[DIVISION] >> - metis_tac[MOD_PLUS, MOD_EQ_0, ADD, MOD_MOD, LESS_MOD]); - -(* Idea: eliminate modulus n when a MOD n = b MOD n. *) - -(* Theorem: 0 < n /\ b <= a ==> (a MOD n = b MOD n <=> ?c. a = b + c * n) *) -(* Proof: - If part: a MOD n = b MOD n ==> ?c. a = b + c * n - Note ?c. a = c * n + b MOD n by MOD_EQN - and b = (b DIV n) * n + b MOD n by DIVISION - Let q = b DIV n, - Then q * n <= c * n by LE_ADD_RCANCEL, b <= a - a - = c * n + (b - q * n) by above - = b + (c * n - q * n) by arithmetic, q * n <= c * n - = b + (c - q) * n by RIGHT_SUB_DISTRIB - Take (c - q) as c. - Only-if part: (b + c * n) MOD n = b MOD n - This is true by MOD_TIMES -*) -Theorem MOD_MOD_EQN: - !n a b. 0 < n /\ b <= a ==> (a MOD n = b MOD n <=> ?c. a = b + c * n) -Proof - rw[EQ_IMP_THM] >| [ - `?c. a = c * n + b MOD n` by metis_tac[MOD_EQN] >> - `b = (b DIV n) * n + b MOD n` by rw[DIVISION] >> - qabbrev_tac `q = b DIV n` >> - `q * n <= c * n` by metis_tac[LE_ADD_RCANCEL] >> - `a = b + (c * n - q * n)` by decide_tac >> - `_ = b + (c - q) * n` by decide_tac >> - metis_tac[], - simp[] - ] -QED - -(* Idea: a convenient form of MOD_PLUS. *) - -(* Theorem: 0 < n ==> (x + y) MOD n = (x + y MOD n) MOD n *) -(* Proof: - Let q = y DIV n, r = y MOD n. - Then y = q * n + r by DIVISION, 0 < n - (x + y) MOD n - = (x + (q * n + r)) MOD n by above - = (q * n + (x + r)) MOD n by arithmetic - = (x + r) MOD n by MOD_PLUS, MOD_EQ_0 -*) -Theorem MOD_PLUS2: - !n x y. 0 < n ==> (x + y) MOD n = (x + y MOD n) MOD n -Proof - rpt strip_tac >> - `y = (y DIV n) * n + y MOD n` by metis_tac[DIVISION] >> - simp[] -QED - -(* Theorem: (x + y + z) MOD n = (x MOD n + y MOD n + z MOD n) MOD n *) -(* Proof: - (x + y + z) MOD n - = ((x + y) MOD n + z MOD n) MOD n by MOD_PLUS - = ((x MOD n + y MOD n) MOD n + z MOD n) MOD n by MOD_PLUS - = (x MOD n + y MOD n + z MOD n) MOD n by MOD_MOD -*) -val MOD_PLUS3 = store_thm( - "MOD_PLUS3", - ``!n. 0 < n ==> !x y z. (x + y + z) MOD n = (x MOD n + y MOD n + z MOD n) MOD n``, - metis_tac[MOD_PLUS, MOD_MOD]); - -(* Theorem: Addition is associative in MOD: if x, y, z all < n, - ((x + y) MOD n + z) MOD n = (x + (y + z) MOD n) MOD n. *) -(* Proof: - ((x * y) MOD n * z) MOD n - = (((x * y) MOD n) MOD n * z MOD n) MOD n by MOD_TIMES2 - = ((x * y) MOD n * z MOD n) MOD n by MOD_MOD - = (x * y * z) MOD n by MOD_TIMES2 - = (x * (y * z)) MOD n by MULT_ASSOC - = (x MOD n * (y * z) MOD n) MOD n by MOD_TIMES2 - = (x MOD n * ((y * z) MOD n) MOD n) MOD n by MOD_MOD - = (x * (y * z) MOD n) MOD n by MOD_TIMES2 - or - ((x + y) MOD n + z) MOD n - = ((x + y) MOD n + z MOD n) MOD n by LESS_MOD, z < n - = (x + y + z) MOD n by MOD_PLUS - = (x + (y + z)) MOD n by ADD_ASSOC - = (x MOD n + (y + z) MOD n) MOD n by MOD_PLUS - = (x + (y + z) MOD n) MOD n by LESS_MOD, x < n -*) -val MOD_ADD_ASSOC = store_thm( - "MOD_ADD_ASSOC", - ``!n x y z. 0 < n /\ x < n /\ y < n /\ z < n ==> (((x + y) MOD n + z) MOD n = (x + (y + z) MOD n) MOD n)``, - metis_tac[LESS_MOD, MOD_PLUS, ADD_ASSOC]); - -(* Theorem: mutliplication is associative in MOD: - (x*y MOD n * z) MOD n = (x * y*Z MOD n) MOD n *) -(* Proof: - ((x * y) MOD n * z) MOD n - = (((x * y) MOD n) MOD n * z MOD n) MOD n by MOD_TIMES2 - = ((x * y) MOD n * z MOD n) MOD n by MOD_MOD - = (x * y * z) MOD n by MOD_TIMES2 - = (x * (y * z)) MOD n by MULT_ASSOC - = (x MOD n * (y * z) MOD n) MOD n by MOD_TIMES2 - = (x MOD n * ((y * z) MOD n) MOD n) MOD n by MOD_MOD - = (x * (y * z) MOD n) MOD n by MOD_TIMES2 - or - ((x * y) MOD n * z) MOD n - = ((x * y) MOD n * z MOD n) MOD n by LESS_MOD, z < n - = (((x * y) * z) MOD n) MOD n by MOD_TIMES2 - = ((x * (y * z)) MOD n) MOD n by MULT_ASSOC - = (x MOD n * (y * z) MOD n) MOD n by MOD_TIMES2 - = (x * (y * z) MOD n) MOD n by LESS_MOD, x < n -*) -val MOD_MULT_ASSOC = store_thm( - "MOD_MULT_ASSOC", - ``!n x y z. 0 < n /\ x < n /\ y < n /\ z < n ==> (((x * y) MOD n * z) MOD n = (x * (y * z) MOD n) MOD n)``, - metis_tac[LESS_MOD, MOD_TIMES2, MULT_ASSOC]); - -(* Theorem: If n > 0, ((n - x) MOD n + x) MOD n = 0 for x < n. *) -(* Proof: - ((n - x) MOD n + x) MOD n - = ((n - x) MOD n + x MOD n) MOD n by LESS_MOD - = (n - x + x) MOD n by MOD_PLUS - = n MOD n by SUB_ADD and 0 <= n - = (1*n) MOD n by MULT_LEFT_1 - = 0 by MOD_EQ_0 -*) -val MOD_ADD_INV = store_thm( - "MOD_ADD_INV", - ``!n x. 0 < n /\ x < n ==> (((n - x) MOD n + x) MOD n = 0)``, - metis_tac[LESS_MOD, MOD_PLUS, SUB_ADD, LESS_IMP_LESS_OR_EQ, MOD_EQ_0, MULT_LEFT_1]); - -(* Theorem: If n > 0, k MOD n = 0 ==> !x. (k*x) MOD n = 0 *) -(* Proof: - (k*x) MOD n = (k MOD n * x MOD n) MOD n by MOD_TIMES2 - = (0 * x MOD n) MOD n by given - = 0 MOD n by MULT_0 and MULT_COMM - = 0 by ZERO_MOD -*) -val MOD_MULITPLE_ZERO = store_thm( - "MOD_MULITPLE_ZERO", - ``!n k. 0 < n /\ (k MOD n = 0) ==> !x. ((k*x) MOD n = 0)``, - metis_tac[MOD_TIMES2, MULT_0, MULT_COMM, ZERO_MOD]); - -(* Theorem: If n > 0, a MOD n = b MOD n ==> (a - b) MOD n = 0 *) -(* Proof: - a = (a DIV n)*n + (a MOD n) by DIVISION - b = (b DIV n)*n + (b MOD n) by DIVISION - Hence a - b = ((a DIV n) - (b DIV n))* n - = a multiple of n - Therefore (a - b) MOD n = 0. -*) -val MOD_EQ_DIFF = store_thm( - "MOD_EQ_DIFF", - ``!n a b. 0 < n /\ (a MOD n = b MOD n) ==> ((a - b) MOD n = 0)``, - rpt strip_tac >> - `a = a DIV n * n + a MOD n` by metis_tac[DIVISION] >> - `b = b DIV n * n + b MOD n` by metis_tac[DIVISION] >> - `a - b = (a DIV n - b DIV n) * n` by rw_tac arith_ss[] >> - metis_tac[MOD_EQ_0]); -(* Note: The reverse is true only when a >= b: - (a-b) MOD n = 0 cannot imply a MOD n = b MOD n *) - -(* Theorem: if n > 0, a >= b, then (a - b) MOD n = 0 <=> a MOD n = b MOD n *) -(* Proof: - (a-b) MOD n = 0 - ==> n divides (a-b) by MOD_0_DIVIDES - ==> (a-b) = k*n for some k by divides_def - ==> a = b + k*n need b <= a to apply arithmeticTheory.SUB_ADD - ==> a MOD n = b MOD n by arithmeticTheory.MOD_TIMES - - The converse is given by MOD_EQ_DIFF. -*) -val MOD_EQ = store_thm( - "MOD_EQ", - ``!n a b. 0 < n /\ b <= a ==> (((a - b) MOD n = 0) <=> (a MOD n = b MOD n))``, - rw[EQ_IMP_THM] >| [ - `?k. a - b = k * n` by metis_tac[DIVIDES_MOD_0, divides_def] >> - `a = k*n + b` by rw_tac arith_ss[] >> - metis_tac[MOD_TIMES], - metis_tac[MOD_EQ_DIFF] - ]); -(* Both MOD_EQ_DIFF and MOD_EQ are required in MOD_MULT_LCANCEL *) - -(* Theorem: n < m ==> ((n MOD m = 0) <=> (n = 0)) *) -(* Proof: - Note n < m ==> (n MOD m = n) by LESS_MOD - Thus (n MOD m = 0) <=> (n = 0) by above -*) -val MOD_EQ_0_IFF = store_thm( - "MOD_EQ_0_IFF", - ``!m n. n < m ==> ((n MOD m = 0) <=> (n = 0))``, - rw_tac bool_ss[LESS_MOD]); - -(* Theorem: ((a MOD n) ** m) MOD n = (a ** m) MOD n *) -(* Proof: by induction on m. - Base case: (a MOD n) ** 0 MOD n = a ** 0 MOD n - (a MOD n) ** 0 MOD n - = 1 MOD n by EXP - = a ** 0 MOD n by EXP - Step case: (a MOD n) ** m MOD n = a ** m MOD n ==> (a MOD n) ** SUC m MOD n = a ** SUC m MOD n - (a MOD n) ** SUC m MOD n - = ((a MOD n) * (a MOD n) ** m) MOD n by EXP - = ((a MOD n) * (((a MOD n) ** m) MOD n)) MOD n by MOD_TIMES2, MOD_MOD - = ((a MOD n) * (a ** m MOD n)) MOD n by induction hypothesis - = (a * a ** m) MOD n by MOD_TIMES2 - = a ** SUC m MOD n by EXP -*) -val MOD_EXP = store_thm( - "MOD_EXP", - ``!n. 0 < n ==> !a m. ((a MOD n) ** m) MOD n = (a ** m) MOD n``, - rpt strip_tac >> - Induct_on `m` >- - rw[EXP] >> - `(a MOD n) ** SUC m MOD n = ((a MOD n) * (a MOD n) ** m) MOD n` by rw[EXP] >> - `_ = ((a MOD n) * (((a MOD n) ** m) MOD n)) MOD n` by metis_tac[MOD_TIMES2, MOD_MOD] >> - `_ = ((a MOD n) * (a ** m MOD n)) MOD n` by rw[] >> - `_ = (a * a ** m) MOD n` by rw[MOD_TIMES2] >> - rw[EXP]); - - -(* Idea: equality exchange for MOD without negative. *) - -(* Theorem: b < n /\ c < n ==> - ((a + b) MOD n = (c + d) MOD n <=> - (a + (n - c)) MOD n = (d + (n - b)) MOD n) *) -(* Proof: - Note 0 < n by b < n or c < n - Let x = n - b, y = n - c. - The goal is: (a + b) MOD n = (c + d) MOD n <=> - (a + y) MOD n = (d + x) MOD n - Note n = b + x, n = c + y by arithmetic - (a + b) MOD n = (c + d) MOD n - <=> (a + b + x + y) MOD n = (c + d + x + y) MOD n by ADD_MOD - <=> (a + y + n) MOD n = (d + x + n) MOD n by above - <=> (a + y) MOD n = (d + x) MOD n by ADD_MOD - - For integers, this is simply: a + b = c + d <=> a - c = b - d. -*) -Theorem mod_add_eq_sub: - !n a b c d. b < n /\ c < n ==> - ((a + b) MOD n = (c + d) MOD n <=> - (a + (n - c)) MOD n = (d + (n - b)) MOD n) -Proof - rpt strip_tac >> - `0 < n` by decide_tac >> - `n = b + (n - b)` by decide_tac >> - `n = c + (n - c)` by decide_tac >> - qabbrev_tac `x = n - b` >> - qabbrev_tac `y = n - c` >> - `a + b + x + y = a + y + n` by decide_tac >> - `c + d + x + y = d + x + n` by decide_tac >> - `(a + b) MOD n = (c + d) MOD n <=> - (a + b + x + y) MOD n = (c + d + x + y) MOD n` by simp[ADD_MOD] >> - fs[ADD_MOD] -QED - -(* Idea: generalise above equality exchange for MOD. *) - -(* Theorem: 0 < n ==> - ((a + b) MOD n = (c + d) MOD n <=> - (a + (n - c MOD n)) MOD n = (d + (n - b MOD n)) MOD n) *) -(* Proof: - Let b' = b MOD n, c' = c MOD n. - Note b' < n by MOD_LESS, 0 < n - and c' < n by MOD_LESS, 0 < n - (a + b) MOD n = (c + d) MOD n - <=> (a + b') MOD n = (d + c') MOD n by MOD_PLUS2 - <=> (a + (n - c')) MOD n = (d + (n - b')) MOD n by mod_add_eq_sub -*) -Theorem mod_add_eq_sub_eq: - !n a b c d. 0 < n ==> - ((a + b) MOD n = (c + d) MOD n <=> - (a + (n - c MOD n)) MOD n = (d + (n - b MOD n)) MOD n) -Proof - rpt strip_tac >> - `b MOD n < n /\ c MOD n < n` by rw[] >> - `(a + b) MOD n = (a + b MOD n) MOD n` by simp[Once MOD_PLUS2] >> - `(c + d) MOD n = (d + c MOD n) MOD n` by simp[Once MOD_PLUS2] >> - prove_tac[mod_add_eq_sub] -QED - -(* -MOD_EQN is a trick to eliminate MOD: -|- !n. 0 < n ==> !a b. a MOD n = b <=> ?c. a = c * n + b /\ b < n -*) - -(* Idea: remove MOD for divides: need b divides (a MOD n) ==> b divides a. *) - -(* Theorem: 0 < n /\ b divides n /\ b divides (a MOD n) ==> b divides a *) -(* Proof: - Note ?k. n = k * b by divides_def, b divides n - and ?h. a MOD n = h * b by divides_def, b divides (a MOD n) - and ?c. a = c * n + h * b by MOD_EQN, 0 < n - = c * (k * b) + h * b by above - = (c * k + h) * b by RIGHT_ADD_DISTRIB - Thus b divides a by divides_def -*) -Theorem mod_divides: - !n a b. 0 < n /\ b divides n /\ b divides (a MOD n) ==> b divides a -Proof - rpt strip_tac >> - `?k. n = k * b` by rw[GSYM divides_def] >> - `?h. a MOD n = h * b` by rw[GSYM divides_def] >> - `?c. a = c * n + h * b` by metis_tac[MOD_EQN] >> - `_ = (c * k + h) * b` by simp[] >> - metis_tac[divides_def] -QED - -(* Idea: include converse of mod_divides. *) - -(* Theorem: 0 < n /\ b divides n ==> (b divides (a MOD n) <=> b divides a) *) -(* Proof: - If part: b divides n /\ b divides a MOD n ==> b divides a - This is true by mod_divides - Only-if part: b divides n /\ b divides a ==> b divides a MOD n - Note ?c. a = c * n + a MOD n by MOD_EQN, 0 < n - = c * n + 1 * a MOD n by MULT_LEFT_1 - Thus b divides (a MOD n) by divides_linear_sub -*) -Theorem mod_divides_iff: - !n a b. 0 < n /\ b divides n ==> (b divides (a MOD n) <=> b divides a) -Proof - rw[EQ_IMP_THM] >- - metis_tac[mod_divides] >> - `?c. a = c * n + a MOD n` by metis_tac[MOD_EQN] >> - metis_tac[divides_linear_sub, MULT_LEFT_1] -QED - -(* An application of -DIVIDES_MOD_MOD: -|- !m n. 0 < n /\ m divides n ==> !x. x MOD n MOD m = x MOD m -Let x = a linear combination. -(linear) MOD n MOD m = linear MOD m -change n to a product m * n, for z = linear MOD (m * n). -(linear) MOD (m * n) MOD g = linear MOD g -z MOD g = linear MOD g -requires: g divides (m * n) -*) - -(* Idea: generalise for MOD equation: a MOD n = b. Need c divides a ==> c divides b. *) - -(* Theorem: 0 < n /\ a MOD n = b /\ c divides n /\ c divides a ==> c divides b *) -(* Proof: - Note 0 < c by ZERO_DIVIDES, c divides n, 0 < n. - a MOD n = b - ==> (a MOD n) MOD c = b MOD c - ==> a MOD c = b MOD c by DIVIDES_MOD_MOD, 0 < n, c divides n - But a MOD c = 0 by DIVIDES_MOD_0, c divides a - so b MOD c = 0, or c divides b by DIVIDES_MOD_0, 0 < c -*) -Theorem mod_divides_divides: - !n a b c. 0 < n /\ a MOD n = b /\ c divides n /\ c divides a ==> c divides b -Proof - simp[mod_divides_iff] -QED - -(* Idea: include converse of mod_divides_divides. *) - -(* Theorem: 0 < n /\ a MOD n = b /\ c divides n ==> (c divides a <=> c divides b) *) -(* Proof: - If part: c divides a ==> c divides b, true by mod_divides_divides - Only-if part: c divides b ==> c divides a - Note b = a MOD n, so this is true by mod_divides -*) -Theorem mod_divides_divides_iff: - !n a b c. 0 < n /\ a MOD n = b /\ c divides n ==> (c divides a <=> c divides b) -Proof - simp[mod_divides_iff] -QED - -(* Idea: divides across MOD: from a MOD n = b MOD n to c divides a ==> c divides b. *) - -(* Theorem: 0 < n /\ a MOD n = b MOD n /\ c divides n /\ c divides a ==> c divides b *) -(* Proof: - Note c divides (b MOD n) by mod_divides_divides - so c divides b by mod_divides - Or, simply have both by mod_divides_iff -*) -Theorem mod_eq_divides: - !n a b c. 0 < n /\ a MOD n = b MOD n /\ c divides n /\ c divides a ==> c divides b -Proof - metis_tac[mod_divides_iff] -QED - -(* Idea: include converse of mod_eq_divides. *) - -(* Theorem: 0 < n /\ a MOD n = b MOD n /\ c divides n ==> (c divides a <=> c divides b) *) -(* Proof: - If part: c divides a ==> c divides b, true by mod_eq_divides, a MOD n = b MOD n - Only-if: c divides b ==> c divides a, true by mod_eq_divides, b MOD n = a MOD n -*) -Theorem mod_eq_divides_iff: - !n a b c. 0 < n /\ a MOD n = b MOD n /\ c divides n ==> (c divides a <=> c divides b) -Proof - metis_tac[mod_eq_divides] -QED - -(* Idea: special cross-multiply equality of MOD (m * n) implies pair equality: - from (m * a) MOD (m * n) = (n * b) MOD (m * n) to a = n /\ b = m. *) - -(* Theorem: coprime m n /\ 0 < a /\ a < 2 * n /\ 0 < b /\ b < 2 * m /\ - (m * a) MOD (m * n) = (n * b) MOD (m * n) ==> (a = n /\ b = m) *) -(* Proof: - Given (m * a) MOD (m * n) = (n * b) MOD (m * n) - Note n divides (n * b) by factor_divides - and n divides (m * n) by factor_divides - so n divides (m * a) by mod_eq_divides - ==> n divides a by euclid_coprime, MULT_COMM - Thus a = n by divides_iff_equal - Also m divides (m * a) by factor_divides - and m divides (m * n) by factor_divides - so m divides (n * b) by mod_eq_divides - ==> m divides b by euclid_coprime, GCD_SYM - Thus b = m by divides_iff_equal -*) -Theorem mod_mult_eq_mult: - !m n a b. coprime m n /\ 0 < a /\ a < 2 * n /\ 0 < b /\ b < 2 * m /\ - (m * a) MOD (m * n) = (n * b) MOD (m * n) ==> (a = n /\ b = m) -Proof - ntac 5 strip_tac >> - `0 < m /\ 0 < n` by decide_tac >> - `0 < m * n` by rw[] >> - strip_tac >| [ - `n divides (n * b)` by rw[DIVIDES_MULTIPLE] >> - `n divides (m * n)` by rw[DIVIDES_MULTIPLE] >> - `n divides (m * a)` by metis_tac[mod_eq_divides] >> - `n divides a` by metis_tac[euclid_coprime, MULT_COMM] >> - metis_tac[divides_iff_equal], - `m divides (m * a)` by rw[DIVIDES_MULTIPLE] >> - `m divides (m * n)` by metis_tac[DIVIDES_REFL, DIVIDES_MULTIPLE, MULT_COMM] >> - `m divides (n * b)` by metis_tac[mod_eq_divides] >> - `m divides b` by metis_tac[euclid_coprime, GCD_SYM] >> - metis_tac[divides_iff_equal] - ] -QED - -(* ------------------------------------------------------------------------- *) -(* Even and Odd Parity. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < n /\ EVEN m ==> EVEN (m ** n) *) -(* Proof: - Since EVEN m, m MOD 2 = 0 by EVEN_MOD2 - EVEN (m ** n) - <=> (m ** n) MOD 2 = 0 by EVEN_MOD2 - <=> (m MOD 2) ** n MOD 2 = 0 by EXP_MOD, 0 < 2 - ==> 0 ** n MOD 2 = 0 by above - <=> 0 MOD 2 = 0 by ZERO_EXP, n <> 0 - <=> 0 = 0 by ZERO_MOD - <=> T -*) -(* Note: arithmeticTheory.EVEN_EXP |- !m n. 0 < n /\ EVEN m ==> EVEN (m ** n) *) - -(* Theorem: !m n. 0 < n /\ ODD m ==> ODD (m ** n) *) -(* Proof: - Since ODD m, m MOD 2 = 1 by ODD_MOD2 - ODD (m ** n) - <=> (m ** n) MOD 2 = 1 by ODD_MOD2 - <=> (m MOD 2) ** n MOD 2 = 1 by EXP_MOD, 0 < 2 - ==> 1 ** n MOD 2 = 1 by above - <=> 1 MOD 2 = 1 by EXP_1, n <> 0 - <=> 1 = 1 by ONE_MOD, 1 < 2 - <=> T -*) -val ODD_EXP = store_thm( - "ODD_EXP", - ``!m n. 0 < n /\ ODD m ==> ODD (m ** n)``, - rw[ODD_MOD2] >> - `n <> 0 /\ 0 < 2` by decide_tac >> - metis_tac[EXP_MOD, EXP_1, ONE_MOD]); - -(* Theorem: 0 < n ==> !m. (EVEN m <=> EVEN (m ** n)) /\ (ODD m <=> ODD (m ** n)) *) -(* Proof: - First goal: EVEN m <=> EVEN (m ** n) - If part: EVEN m ==> EVEN (m ** n), true by EVEN_EXP - Only-if part: EVEN (m ** n) ==> EVEN m. - By contradiction, suppose ~EVEN m. - Then ODD m by EVEN_ODD - and ODD (m ** n) by ODD_EXP - or ~EVEN (m ** n) by EVEN_ODD - This contradicts EVEN (m ** n). - Second goal: ODD m <=> ODD (m ** n) - Just mirror the reasoning of first goal. -*) -val power_parity = store_thm( - "power_parity", - ``!n. 0 < n ==> !m. (EVEN m <=> EVEN (m ** n)) /\ (ODD m <=> ODD (m ** n))``, - metis_tac[EVEN_EXP, ODD_EXP, ODD_EVEN]); - -(* Theorem: 0 < n ==> EVEN (2 ** n) *) -(* Proof: - EVEN (2 ** n) - <=> (2 ** n) MOD 2 = 0 by EVEN_MOD2 - <=> (2 MOD 2) ** n MOD 2 = 0 by EXP_MOD - <=> 0 ** n MOD 2 = 0 by DIVMOD_ID, 0 < 2 - <=> 0 MOD 2 = 0 by ZERO_EXP, n <> 0 - <=> 0 = 0 by ZERO_MOD - <=> T -*) -Theorem EXP_2_EVEN: !n. 0 < n ==> EVEN (2 ** n) -Proof rw[EVEN_MOD2, ZERO_EXP] -QED -(* Michael's proof by induction -val EXP_2_EVEN = store_thm( - "EXP_2_EVEN", - ``!n. 0 < n ==> EVEN (2 ** n)``, - Induct >> rw[EXP, EVEN_DOUBLE]); - *) - -(* Theorem: 0 < n ==> ODD (2 ** n - 1) *) -(* Proof: - Since 0 < 2 ** n by EXP_POS, 0 < 2 - so 1 <= 2 ** n by LESS_EQ - thus SUC (2 ** n - 1) - = 2 ** n - 1 + 1 by ADD1 - = 2 ** n by SUB_ADD, 1 <= 2 ** n - and EVEN (2 ** n) by EXP_2_EVEN - Hence ODD (2 ** n - 1) by EVEN_ODD_SUC -*) -val EXP_2_PRE_ODD = store_thm( - "EXP_2_PRE_ODD", - ``!n. 0 < n ==> ODD (2 ** n - 1)``, - rpt strip_tac >> - `0 < 2 ** n` by rw[EXP_POS] >> - `SUC (2 ** n - 1) = 2 ** n` by decide_tac >> - metis_tac[EXP_2_EVEN, EVEN_ODD_SUC]); - -(* ------------------------------------------------------------------------- *) -(* Modulo Inverse *) -(* ------------------------------------------------------------------------- *) - -(* -> LINEAR_GCD |> SPEC ``j:num`` |> SPEC ``k:num``; -val it = |- j <> 0 ==> ?p q. p * j = q * k + gcd k j: thm -*) - -(* Theorem: 0 < j ==> ?p q. p * j = q * k + gcd j k *) -(* Proof: by LINEAR_GCD, GCD_SYM *) -val GCD_LINEAR = store_thm( - "GCD_LINEAR", - ``!j k. 0 < j ==> ?p q. p * j = q * k + gcd j k``, - metis_tac[LINEAR_GCD, GCD_SYM, NOT_ZERO]); - -(* Theorem: [Euclid's Lemma] A prime a divides product iff the prime a divides factor. - [in MOD notation] For prime p, x*y MOD p = 0 <=> x MOD p = 0 or y MOD p = 0 *) -(* Proof: - The if part is already in P_EUCLIDES: - !p a b. prime p /\ divides p (a * b) ==> p divides a \/ p divides b - Convert the divides to MOD by DIVIDES_MOD_0. - The only-if part is: - (1) divides p x ==> divides p (x * y) - (2) divides p y ==> divides p (x * y) - Both are true by DIVIDES_MULT: !a b c. a divides b ==> a divides (b * c). - The symmetry of x and y can be taken care of by MULT_COMM. -*) -val EUCLID_LEMMA = store_thm( - "EUCLID_LEMMA", - ``!p x y. prime p ==> (((x * y) MOD p = 0) <=> (x MOD p = 0) \/ (y MOD p = 0))``, - rpt strip_tac >> - `0 < p` by rw[PRIME_POS] >> - rw[GSYM DIVIDES_MOD_0, EQ_IMP_THM] >> - metis_tac[P_EUCLIDES, DIVIDES_MULT, MULT_COMM]); - -(* Theorem: [Cancellation Law for MOD p] - For prime p, if x MOD p <> 0, - (x*y) MOD p = (x*z) MOD p ==> y MOD p = z MOD p *) -(* Proof: - (x*y) MOD p = (x*z) MOD p - ==> ((x*y) - (x*z)) MOD p = 0 by MOD_EQ_DIFF - ==> (x*(y-z)) MOD p = 0 by arithmetic LEFT_SUB_DISTRIB - ==> (y-z) MOD p = 0 by EUCLID_LEMMA, x MOD p <> 0 - ==> y MOD p = z MOD p if z <= y - - Since this theorem is symmetric in y, z, - First prove the theorem assuming z <= y, - then use the same proof for y <= z. -*) -Theorem MOD_MULT_LCANCEL: - !p x y z. prime p /\ (x * y) MOD p = (x * z) MOD p /\ x MOD p <> 0 ==> y MOD p = z MOD p -Proof - rpt strip_tac >> - `!a b c. c <= b /\ (a * b) MOD p = (a * c) MOD p /\ a MOD p <> 0 ==> b MOD p = c MOD p` by - (rpt strip_tac >> - `0 < p` by rw[PRIME_POS] >> - `(a * b - a * c) MOD p = 0` by rw[MOD_EQ_DIFF] >> - `(a * (b - c)) MOD p = 0` by rw[LEFT_SUB_DISTRIB] >> - metis_tac[EUCLID_LEMMA, MOD_EQ]) >> - Cases_on `z <= y` >- - metis_tac[] >> - `y <= z` by decide_tac >> - metis_tac[] -QED - -(* Theorem: prime p /\ (y * x) MOD p = (z * x) MOD p /\ x MOD p <> 0 ==> - y MOD p = z MOD p *) -(* Proof: by MOD_MULT_LCANCEL, MULT_COMM *) -Theorem MOD_MULT_RCANCEL: - !p x y z. prime p /\ (y * x) MOD p = (z * x) MOD p /\ x MOD p <> 0 ==> - y MOD p = z MOD p -Proof - metis_tac[MOD_MULT_LCANCEL, MULT_COMM] -QED - -(* Theorem: For prime p, 0 < x < p ==> ?y. 0 < y /\ y < p /\ (y*x) MOD p = 1 *) -(* Proof: - 0 < x < p - ==> ~ divides p x by NOT_LT_DIVIDES - ==> gcd p x = 1 by gcdTheory.PRIME_GCD - ==> ?k q. k * x = q * p + 1 by gcdTheory.LINEAR_GCD - ==> k*x MOD p = (q*p + 1) MOD p by arithmetic - ==> k*x MOD p = 1 by MOD_MULT, 1 < p. - ==> (k MOD p)*(x MOD p) MOD p = 1 by MOD_TIMES2 - ==> ((k MOD p) * x) MOD p = 1 by LESS_MOD, x < p. - Now k MOD p < p by MOD_LESS - and 0 < k MOD p since (k*x) MOD p <> 0 (by 1 <> 0) - and x MOD p <> 0 (by ~ divides p x) - by EUCLID_LEMMA - Hence take y = k MOD p, then 0 < y < p. -*) -val MOD_MULT_INV_EXISTS = store_thm( - "MOD_MULT_INV_EXISTS", - ``!p x. prime p /\ 0 < x /\ x < p ==> ?y. 0 < y /\ y < p /\ ((y * x) MOD p = 1)``, - rpt strip_tac >> - `0 < p /\ 1 < p` by metis_tac[PRIME_POS, ONE_LT_PRIME] >> - `gcd p x = 1` by metis_tac[PRIME_GCD, NOT_LT_DIVIDES] >> - `?k q. k * x = q * p + 1` by metis_tac[LINEAR_GCD, NOT_ZERO_LT_ZERO] >> - `1 = (k * x) MOD p` by metis_tac[MOD_MULT] >> - `_ = ((k MOD p) * (x MOD p)) MOD p` by metis_tac[MOD_TIMES2] >> - `0 < k MOD p` by - (`1 <> 0` by decide_tac >> - `x MOD p <> 0` by metis_tac[DIVIDES_MOD_0, NOT_LT_DIVIDES] >> - `k MOD p <> 0` by metis_tac[EUCLID_LEMMA, MOD_MOD] >> - decide_tac) >> - metis_tac[MOD_LESS, LESS_MOD]); - -(* Convert this theorem into MUL_INV_DEF *) - -(* Step 1: move ?y forward by collecting quantifiers *) -val lemma = prove( - ``!p x. ?y. prime p /\ 0 < x /\ x < p ==> 0 < y /\ y < p /\ ((y * x) MOD p = 1)``, - metis_tac[MOD_MULT_INV_EXISTS]); - -(* Step 2: apply SKOLEM_THM *) -(* -- SKOLEM_THM; -> val it = |- !P. (!x. ?y. P x y) <=> ?f. !x. P x (f x) : thm -*) -val MOD_MULT_INV_DEF = new_specification( - "MOD_MULT_INV_DEF", - ["MOD_MULT_INV"], (* avoid MOD_MULT_INV_EXISTS: thm *) - SIMP_RULE (srw_ss()) [SKOLEM_THM] lemma); -(* -> val MOD_MULT_INV_DEF = - |- !p x. - prime p /\ 0 < x /\ x < p ==> - 0 < MOD_MULT_INV p x /\ MOD_MULT_INV p x < p /\ - ((MOD_MULT_INV p x * x) MOD p = 1) : thm -*) - -(* ------------------------------------------------------------------------- *) -(* FACTOR Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: ~ prime n ==> n has a proper prime factor p *) -(* Proof: apply PRIME_FACTOR: - !n. n <> 1 ==> ?p. prime p /\ p divides n : thm -*) -val PRIME_FACTOR_PROPER = store_thm( - "PRIME_FACTOR_PROPER", - ``!n. 1 < n /\ ~prime n ==> ?p. prime p /\ p < n /\ (p divides n)``, - rpt strip_tac >> - `0 < n /\ n <> 1` by decide_tac >> - `?p. prime p /\ p divides n` by metis_tac[PRIME_FACTOR] >> - `~(n < p)` by metis_tac[NOT_LT_DIVIDES] >> - Cases_on `n = p` >- - full_simp_tac std_ss[] >> - `p < n` by decide_tac >> - metis_tac[]); - -(* Theorem: If p divides n, then there is a (p ** m) that maximally divides n. *) -(* Proof: - Consider the set s = {k | p ** k divides n} - Since p IN s, s <> {} by MEMBER_NOT_EMPTY - For k IN s, p ** k n divides ==> p ** k <= n by DIVIDES_LE - Since ?z. n <= p ** z by EXP_ALWAYS_BIG_ENOUGH - p ** k <= p ** z - k <= z by EXP_BASE_LE_MONO - or k < SUC z - Hence s SUBSET count (SUC z) by SUBSET_DEF - and FINITE s by FINITE_COUNT, SUBSET_FINITE - Let m = MAX_SET s - Then p ** m n divides by MAX_SET_DEF - Let q = n DIV (p ** m) - i.e. n = q * (p ** m) - If p divides q, then q = t * p - or n = t * p * (p ** m) - = t * (p * p ** m) by MULT_ASSOC - = t * p ** SUC m by EXP - i.e. p ** SUC m divides n, or SUC m IN s. - Since m < SUC m by LESS_SUC - This contradicts the maximal property of m. -*) -val FACTOR_OUT_POWER = store_thm( - "FACTOR_OUT_POWER", - ``!n p. 0 < n /\ 1 < p /\ p divides n ==> ?m. (p ** m) divides n /\ ~(p divides (n DIV (p ** m)))``, - rpt strip_tac >> - `p <= n` by rw[DIVIDES_LE] >> - `1 < n` by decide_tac >> - qabbrev_tac `s = {k | (p ** k) divides n }` >> - qexists_tac `MAX_SET s` >> - qabbrev_tac `m = MAX_SET s` >> - `!k. k IN s <=> (p ** k) divides n` by rw[Abbr`s`] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY, EXP_1] >> - `?z. n <= p ** z` by rw[EXP_ALWAYS_BIG_ENOUGH] >> - `!k. k IN s ==> k <= z` by metis_tac[DIVIDES_LE, EXP_BASE_LE_MONO, LESS_EQ_TRANS] >> - `!k. k <= z ==> k < SUC z` by decide_tac >> - `s SUBSET (count (SUC z))` by metis_tac[IN_COUNT, SUBSET_DEF, LESS_EQ_TRANS] >> - `FINITE s` by metis_tac[FINITE_COUNT, SUBSET_FINITE] >> - `m IN s /\ !y. y IN s ==> y <= m` by metis_tac[MAX_SET_DEF] >> - `(p ** m) divides n` by metis_tac[] >> - rw[] >> - spose_not_then strip_assume_tac >> - `0 < p` by decide_tac >> - `0 < p ** m` by rw[EXP_POS] >> - `n = (p ** m) * (n DIV (p ** m))` by rw[DIVIDES_FACTORS] >> - `?q. n DIV (p ** m) = q * p` by rw[GSYM divides_def] >> - `n = q * p ** SUC m` by metis_tac[MULT_COMM, MULT_ASSOC, EXP] >> - `SUC m <= m` by metis_tac[divides_def] >> - decide_tac); - -(* ------------------------------------------------------------------------- *) -(* Useful Theorems. *) -(* ------------------------------------------------------------------------- *) - -(* binomial_add: same as SUM_SQUARED *) - -(* Theorem: (a + b) ** 2 = a ** 2 + b ** 2 + 2 * a * b *) -(* Proof: - (a + b) ** 2 - = (a + b) * (a + b) by EXP_2 - = a * (a + b) + b * (a + b) by RIGHT_ADD_DISTRIB - = (a * a + a * b) + (b * a + b * b) by LEFT_ADD_DISTRIB - = a * a + b * b + 2 * a * b by arithmetic - = a ** 2 + b ** 2 + 2 * a * b by EXP_2 -*) -Theorem binomial_add: - !a b. (a + b) ** 2 = a ** 2 + b ** 2 + 2 * a * b -Proof - rpt strip_tac >> - `(a + b) ** 2 = (a + b) * (a + b)` by simp[] >> - `_ = a * a + b * b + 2 * a * b` by decide_tac >> - simp[] -QED - -(* Theorem: b <= a ==> ((a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b) *) -(* Proof: - If b = 0, - RHS = a ** 2 + 0 ** 2 - 2 * a * 0 - = a ** 2 + 0 - 0 - = a ** 2 - = (a - 0) ** 2 - = LHS - If b <> 0, - Then b * b <= a * b by LE_MULT_RCANCEL, b <> 0 - and b * b <= 2 * a * b - - LHS = (a - b) ** 2 - = (a - b) * (a - b) by EXP_2 - = a * (a - b) - b * (a - b) by RIGHT_SUB_DISTRIB - = (a * a - a * b) - (b * a - b * b) by LEFT_SUB_DISTRIB - = a * a - (a * b + (a * b - b * b)) by SUB_PLUS - = a * a - (a * b + a * b - b * b) by LESS_EQ_ADD_SUB, b * b <= a * b - = a * a - (2 * a * b - b * b) - = a * a + b * b - 2 * a * b by SUB_SUB, b * b <= 2 * a * b - = a ** 2 + b ** 2 - 2 * a * b by EXP_2 - = RHS -*) -Theorem binomial_sub: - !a b. b <= a ==> ((a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b) -Proof - rpt strip_tac >> - Cases_on `b = 0` >- - simp[] >> - `b * b <= a * b` by rw[] >> - `b * b <= 2 * a * b` by decide_tac >> - `(a - b) ** 2 = (a - b) * (a - b)` by simp[] >> - `_ = a * a + b * b - 2 * a * b` by decide_tac >> - rw[] -QED - -(* Theorem: 2 * a * b <= a ** 2 + b ** 2 *) -(* Proof: - If a = b, - LHS = 2 * a * a - = a * a + a * a - = a ** 2 + a ** 2 by EXP_2 - = RHS - If a < b, then 0 < b - a. - Thus 0 < (b - a) * (b - a) by MULT_EQ_0 - or 0 < (b - a) ** 2 by EXP_2 - so 0 < b ** 2 + a ** 2 - 2 * b * a by binomial_sub, a <= b - ==> 2 * a * b < a ** 2 + b ** 2 due to 0 < RHS. - If b < a, then 0 < a - b. - Thus 0 < (a - b) * (a - b) by MULT_EQ_0 - or 0 < (a - b) ** 2 by EXP_2 - so 0 < a ** 2 + b ** 2 - 2 * a * b by binomial_sub, b <= a - ==> 2 * a * b < a ** 2 + b ** 2 due to 0 < RHS. -*) -Theorem binomial_means: - !a b. 2 * a * b <= a ** 2 + b ** 2 -Proof - rpt strip_tac >> - Cases_on `a = b` >- - simp[] >> - Cases_on `a < b` >| [ - `b - a <> 0` by decide_tac >> - `(b - a) * (b - a) <> 0` by metis_tac[MULT_EQ_0] >> - `(b - a) * (b - a) = (b - a) ** 2` by simp[] >> - `_ = b ** 2 + a ** 2 - 2 * b * a` by rw[binomial_sub] >> - decide_tac, - `a - b <> 0` by decide_tac >> - `(a - b) * (a - b) <> 0` by metis_tac[MULT_EQ_0] >> - `(a - b) * (a - b) = (a - b) ** 2` by simp[] >> - `_ = a ** 2 + b ** 2 - 2 * a * b` by rw[binomial_sub] >> - decide_tac - ] -QED - -(* Theorem: b <= a ==> (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 *) -(* Proof: - Note (a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b by binomial_sub - and 2 * a * b <= a ** 2 + b ** 2 by binomial_means - Thus (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 -*) -Theorem binomial_sub_sum: - !a b. b <= a ==> (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 -Proof - rpt strip_tac >> - imp_res_tac binomial_sub >> - assume_tac (binomial_means |> SPEC_ALL) >> - decide_tac -QED - -(* Theorem: b <= a ==> ((a - b) ** 2 + 4 * a * b = (a + b) ** 2) *) -(* Proof: - Note: 2 * a * b <= a ** 2 + b ** 2 by binomial_means, as [1] - (a - b) ** 2 + 4 * a * b - = a ** 2 + b ** 2 - 2 * a * b + 4 * a * b by binomial_sub, b <= a - = a ** 2 + b ** 2 + 4 * a * b - 2 * a * b by SUB_ADD, [1] - = a ** 2 + b ** 2 + 2 * a * b - = (a + b) ** 2 by binomial_add -*) -Theorem binomial_sub_add: - !a b. b <= a ==> ((a - b) ** 2 + 4 * a * b = (a + b) ** 2) -Proof - rpt strip_tac >> - `2 * a * b <= a ** 2 + b ** 2` by rw[binomial_means] >> - `(a - b) ** 2 + 4 * a * b = a ** 2 + b ** 2 - 2 * a * b + 4 * a * b` by rw[binomial_sub] >> - `_ = a ** 2 + b ** 2 + 4 * a * b - 2 * a * b` by decide_tac >> - `_ = a ** 2 + b ** 2 + 2 * a * b` by decide_tac >> - `_ = (a + b) ** 2` by rw[binomial_add] >> - decide_tac -QED - -(* Theorem: a ** 2 - b ** 2 = (a - b) * (a + b) *) -(* Proof: - a ** 2 - b ** 2 - = a * a - b * b by EXP_2 - = a * a + a * b - a * b - b * b by ADD_SUB - = a * a + a * b - (b * a + b * b) by SUB_PLUS - = a * (a + b) - b * (a + b) by LEFT_ADD_DISTRIB - = (a - b) * (a + b) by RIGHT_SUB_DISTRIB -*) -Theorem difference_of_squares: - !a b. a ** 2 - b ** 2 = (a - b) * (a + b) -Proof - rpt strip_tac >> - `a ** 2 - b ** 2 = a * a - b * b` by simp[] >> - `_ = a * a + a * b - a * b - b * b` by decide_tac >> - decide_tac -QED - -(* Theorem: a * a - b * b = (a - b) * (a + b) *) -(* Proof: - a * a - b * b - = a ** 2 - b ** 2 by EXP_2 - = (a + b) * (a - b) by difference_of_squares -*) -Theorem difference_of_squares_alt: - !a b. a * a - b * b = (a - b) * (a + b) -Proof - rw[difference_of_squares] -QED - -(* binomial_2: same as binomial_add, or SUM_SQUARED *) - -(* Theorem: (m + n) ** 2 = m ** 2 + n ** 2 + 2 * m * n *) -(* Proof: - (m + n) ** 2 - = (m + n) * (m + n) by EXP_2 - = m * m + n * m + m * n + n * n by LEFT_ADD_DISTRIB, RIGHT_ADD_DISTRIB - = m ** 2 + n ** 2 + 2 * m * n by EXP_2 -*) -val binomial_2 = store_thm( - "binomial_2", - ``!m n. (m + n) ** 2 = m ** 2 + n ** 2 + 2 * m * n``, - rpt strip_tac >> - `(m + n) ** 2 = (m + n) * (m + n)` by rw[] >> - `_ = m * m + n * m + m * n + n * n` by decide_tac >> - `_ = m ** 2 + n ** 2 + 2 * m * n` by rw[] >> - decide_tac); - -(* Obtain a corollary *) -val SUC_SQ = save_thm("SUC_SQ", - binomial_2 |> SPEC ``1`` |> SIMP_RULE (srw_ss()) [GSYM SUC_ONE_ADD]); -(* val SUC_SQ = |- !n. SUC n ** 2 = SUC (n ** 2) + TWICE n: thm *) - -(* Theorem: m <= n ==> SQ m <= SQ n *) -(* Proof: - Since m * m <= n * n by LE_MONO_MULT2 - so SQ m <= SQ n by notation -*) -val SQ_LE = store_thm( - "SQ_LE", - ``!m n. m <= n ==> SQ m <= SQ n``, - rw[]); - -(* Theorem: EVEN n /\ prime n <=> n = 2 *) -(* Proof: - If part: EVEN n /\ prime n ==> n = 2 - EVEN n ==> n MOD 2 = 0 by EVEN_MOD2 - ==> 2 divides n by DIVIDES_MOD_0, 0 < 2 - ==> n = 2 by prime_def, 2 <> 1 - Only-if part: n = 2 ==> EVEN n /\ prime n - Note EVEN 2 by EVEN_2 - and prime 2 by prime_2 -*) -(* Proof: - EVEN n ==> n MOD 2 = 0 by EVEN_MOD2 - ==> 2 divides n by DIVIDES_MOD_0, 0 < 2 - ==> n = 2 by prime_def, 2 <> 1 -*) -Theorem EVEN_PRIME: - !n. EVEN n /\ prime n <=> n = 2 -Proof - rw[EQ_IMP_THM] >> - `0 < 2 /\ 2 <> 1` by decide_tac >> - `2 divides n` by rw[DIVIDES_MOD_0, GSYM EVEN_MOD2] >> - metis_tac[prime_def] -QED - -(* Theorem: prime n /\ n <> 2 ==> ODD n *) -(* Proof: - By contradiction, suppose ~ODD n. - Then EVEN n by EVEN_ODD - but EVEN n /\ prime n ==> n = 2 by EVEN_PRIME - This contradicts n <> 2. -*) -val ODD_PRIME = store_thm( - "ODD_PRIME", - ``!n. prime n /\ n <> 2 ==> ODD n``, - metis_tac[EVEN_PRIME, EVEN_ODD]); - -(* Theorem: prime p ==> 2 <= p *) -(* Proof: by ONE_LT_PRIME *) -val TWO_LE_PRIME = store_thm( - "TWO_LE_PRIME", - ``!p. prime p ==> 2 <= p``, - metis_tac[ONE_LT_PRIME, DECIDE``1 < n <=> 2 <= n``]); - -(* Theorem: ~prime 4 *) -(* Proof: - Note 4 = 2 * 2 by arithmetic - so 2 divides 4 by divides_def - thus ~prime 4 by primes_def -*) -Theorem NOT_PRIME_4: - ~prime 4 -Proof - rpt strip_tac >> - `4 = 2 * 2` by decide_tac >> - `4 <> 2 /\ 4 <> 1 /\ 2 <> 1` by decide_tac >> - metis_tac[prime_def, divides_def] -QED - -(* Theorem: prime n /\ prime m ==> (n divides m <=> (n = m)) *) -(* Proof: - If part: prime n /\ prime m /\ n divides m ==> (n = m) - Note prime n - ==> n <> 1 by NOT_PRIME_1 - With n divides m by given - and prime m by given - Thus n = m by prime_def - Only-if part; prime n /\ prime m /\ (n = m) ==> n divides m - True as m divides m by DIVIDES_REFL -*) -val prime_divides_prime = store_thm( - "prime_divides_prime", - ``!n m. prime n /\ prime m ==> (n divides m <=> (n = m))``, - rw[EQ_IMP_THM] >> - `n <> 1` by metis_tac[NOT_PRIME_1] >> - metis_tac[prime_def]); -(* This is: dividesTheory.prime_divides_only_self; -|- !m n. prime m /\ prime n /\ m divides n ==> (m = n) -*) - -(* Theorem: 0 < m /\ 1 < n /\ (!p. prime p /\ p divides m ==> (p MOD n = 1)) ==> (m MOD n = 1) *) -(* Proof: - By complete induction on m. - If m = 1, trivially true by ONE_MOD - If m <> 1, - Then ?p. prime p /\ p divides m by PRIME_FACTOR, m <> 1 - and ?q. m = q * p by divides_def - and q divides m by divides_def, MULT_COMM - In order to apply induction hypothesis, - Show: q < m - Note q <= m by DIVIDES_LE, 0 < m - But p <> 1 by NOT_PRIME_1 - Thus q <> m by MULT_RIGHT_1, EQ_MULT_LCANCEL, m <> 0 - ==> q < m - Show: 0 < q - Since m = q * p and m <> 0 by above - Thus q <> 0, or 0 < q by MULT - Show: !p. prime p /\ p divides q ==> (p MOD n = 1) - If p divides q, and q divides m, - Then p divides m by DIVIDES_TRANS - ==> p MOD n = 1 by implication - - Hence q MOD n = 1 by induction hypothesis - and p MOD n = 1 by implication - Now 0 < n by 1 < n - m MDO n - = (q * p) MOD n by m = q * p - = (q MOD n * p MOD n) MOD n by MOD_TIMES2, 0 < n - = (1 * 1) MOD n by above - = 1 by MULT_RIGHT_1, ONE_MOD -*) -val ALL_PRIME_FACTORS_MOD_EQ_1 = store_thm( - "ALL_PRIME_FACTORS_MOD_EQ_1", - ``!m n. 0 < m /\ 1 < n /\ (!p. prime p /\ p divides m ==> (p MOD n = 1)) ==> (m MOD n = 1)``, - completeInduct_on `m` >> - rpt strip_tac >> - Cases_on `m = 1` >- - rw[] >> - `?p. prime p /\ p divides m` by rw[PRIME_FACTOR] >> - `?q. m = q * p` by rw[GSYM divides_def] >> - `q divides m` by metis_tac[divides_def, MULT_COMM] >> - `p <> 1` by metis_tac[NOT_PRIME_1] >> - `m <> 0` by decide_tac >> - `q <> m` by metis_tac[MULT_RIGHT_1, EQ_MULT_LCANCEL] >> - `q <= m` by metis_tac[DIVIDES_LE] >> - `q < m` by decide_tac >> - `q <> 0` by metis_tac[MULT] >> - `0 < q` by decide_tac >> - `!p. prime p /\ p divides q ==> (p MOD n = 1)` by metis_tac[DIVIDES_TRANS] >> - `q MOD n = 1` by rw[] >> - `p MOD n = 1` by rw[] >> - `0 < n` by decide_tac >> - metis_tac[MOD_TIMES2, MULT_RIGHT_1, ONE_MOD]); - -(* Theorem: prime p /\ 0 < n ==> !b. p divides (b ** n) <=> p divides b *) -(* Proof: - If part: p divides b ** n ==> p divides b - By induction on n. - Base: 0 < 0 ==> p divides b ** 0 ==> p divides b - True by 0 < 0 = F. - Step: 0 < n ==> p divides b ** n ==> p divides b ==> - 0 < SUC n ==> p divides b ** SUC n ==> p divides b - If n = 0, - b ** SUC 0 - = b ** 1 by ONE - = b by EXP_1 - so p divides b. - If n <> 0, 0 < n. - b ** SUC n - = b * b ** n by EXP - Thus p divides b, - or p divides (b ** n) by P_EUCLIDES - For the latter case, - p divides b by induction hypothesis, 0 < n - - Only-if part: p divides b ==> p divides b ** n - Since n <> 0, ?m. n = SUC m by num_CASES - and b ** n - = b ** SUC m - = b * b ** m by EXP - Thus p divides b ** n by DIVIDES_MULTIPLE, MULT_COMM -*) -val prime_divides_power = store_thm( - "prime_divides_power", - ``!p n. prime p /\ 0 < n ==> !b. p divides (b ** n) <=> p divides b``, - rw[EQ_IMP_THM] >| [ - Induct_on `n` >- - rw[] >> - rpt strip_tac >> - Cases_on `n = 0` >- - metis_tac[ONE, EXP_1] >> - `0 < n` by decide_tac >> - `b ** SUC n = b * b ** n` by rw[EXP] >> - metis_tac[P_EUCLIDES], - `n <> 0` by decide_tac >> - `?m. n = SUC m` by metis_tac[num_CASES] >> - `b ** SUC m = b * b ** m` by rw[EXP] >> - metis_tac[DIVIDES_MULTIPLE, MULT_COMM] - ]); - -(* Theorem: prime p ==> !n. 0 < n ==> p divides p ** n *) -(* Proof: - Since p divides p by DIVIDES_REFL - so p divides p ** n by prime_divides_power, 0 < n -*) -val prime_divides_self_power = store_thm( - "prime_divides_self_power", - ``!p. prime p ==> !n. 0 < n ==> p divides p ** n``, - rw[prime_divides_power, DIVIDES_REFL]); - -(* Theorem: prime p ==> !b n m. 0 < m /\ (b ** n = p ** m) ==> ?k. (b = p ** k) /\ (k * n = m) *) -(* Proof: - Note 1 < p by ONE_LT_PRIME - so p <> 0, 0 < p, p <> 1 by arithmetic - also m <> 0 by 0 < m - Thus p ** m <> 0 by EXP_EQ_0, p <> 0 - and p ** m <> 1 by EXP_EQ_1, p <> 1, m <> 0 - ==> n <> 0, 0 < n by EXP, b ** n = p ** m <> 1 - also b <> 0, 0 < b by EXP_EQ_0, b ** n = p ** m <> 0, 0 < n - - Step 1: show p divides b. - Note p divides (p ** m) by prime_divides_self_power, 0 < m - so p divides (b ** n) by given, b ** n = p ** m - or p divides b by prime_divides_power, 0 < b - - Step 2: express b = q * p ** u where ~(p divides q). - Note 1 < p /\ 0 < b /\ p divides b - ==> ?u. p ** u divides b /\ ~(p divides b DIV p ** u) by FACTOR_OUT_POWER - Let q = b DIV p ** u, v = u * n. - Since p ** u <> 0 by EXP_EQ_0, p <> 0 - so b = q * p ** u by DIVIDES_EQN, 0 < p ** u - p ** m - = (q * p ** u) ** n by b = q * p ** u - = q ** n * (p ** u) ** n by EXP_BASE_MULT - = q ** n * p ** (u * n) by EXP_EXP_MULT - = q ** n * p ** v by v = u * n - - Step 3: split cases - If v = m, - Then q ** n * p ** m = 1 * p ** m by above - or q ** n = 1 by EQ_MULT_RCANCEL, p ** m <> 0 - giving q = 1 by EXP_EQ_1, 0 < n - Thus b = p ** u by b = q * p ** u - Take k = u, the result follows. - - If v < m, - Let d = m - v. - Then 0 < d /\ (m = d + v) by arithmetic - and p ** m = p ** d * p ** v by EXP_ADD - Note p ** v <> 0 by EXP_EQ_0, p <> 0 - q ** n * p ** v = p ** d * p ** v - ==> q ** n = p ** d by EQ_MULT_RCANCEL, p ** v <> 0 - Now p divides p ** d by prime_divides_self_power, 0 < d - so p divides q ** n by above, q ** n = p ** d - ==> p divides q by prime_divides_power, 0 < n - This contradicts ~(p divides q) - - If m < v, - Let d = v - m. - Then 0 < d /\ (v = d + m) by arithmetic - and q ** n * p ** v - = q ** n * (p ** d * p ** m) by EXP_ADD - = q ** n * p ** d * p ** m by MULT_ASSOC - = 1 * p ** m by arithmetic, b ** n = p ** m - Hence q ** n * p ** d = 1 by EQ_MULT_RCANCEL, p ** m <> 0 - ==> (q ** n = 1) /\ (p ** d = 1) by MULT_EQ_1 - But p ** d <> 1 by EXP_EQ_1, 0 < d - This contradicts p ** d = 1. -*) -Theorem power_eq_prime_power: - !p. prime p ==> - !b n m. 0 < m /\ (b ** n = p ** m) ==> ?k. (b = p ** k) /\ (k * n = m) -Proof - rpt strip_tac >> - `1 < p` by rw[ONE_LT_PRIME] >> - `m <> 0 /\ 0 < p /\ p <> 0 /\ p <> 1` by decide_tac >> - `p ** m <> 0` by rw[EXP_EQ_0] >> - `p ** m <> 1` by rw[EXP_EQ_1] >> - `n <> 0` by metis_tac[EXP] >> - `0 < n /\ 0 < p ** m` by decide_tac >> - `b <> 0` by metis_tac[EXP_EQ_0] >> - `0 < b` by decide_tac >> - `p divides (p ** m)` by rw[prime_divides_self_power] >> - `p divides b` by metis_tac[prime_divides_power] >> - `?u. p ** u divides b /\ ~(p divides b DIV p ** u)` by metis_tac[FACTOR_OUT_POWER] >> - qabbrev_tac `q = b DIV p ** u` >> - `p ** u <> 0` by rw[EXP_EQ_0] >> - `0 < p ** u` by decide_tac >> - `b = q * p ** u` by rw[GSYM DIVIDES_EQN, Abbr`q`] >> - `q ** n * p ** (u * n) = p ** m` by metis_tac[EXP_BASE_MULT, EXP_EXP_MULT] >> - qabbrev_tac `v = u * n` >> - Cases_on `v = m` >| [ - `p ** m = 1 * p ** m` by simp[] >> - `q ** n = 1` by metis_tac[EQ_MULT_RCANCEL] >> - `q = 1` by metis_tac[EXP_EQ_1] >> - `b = p ** u` by simp[] >> - metis_tac[], - Cases_on `v < m` >| [ - `?d. d = m - v` by rw[] >> - `0 < d /\ (m = d + v)` by rw[] >> - `p ** m = p ** d * p ** v` by rw[EXP_ADD] >> - `p ** v <> 0` by metis_tac[EXP_EQ_0] >> - `q ** n = p ** d` by metis_tac[EQ_MULT_RCANCEL] >> - `p divides p ** d` by metis_tac[prime_divides_self_power] >> - metis_tac[prime_divides_power], - `m < v` by decide_tac >> - `?d. d = v - m` by rw[] >> - `0 < d /\ (v = d + m)` by rw[] >> - `d <> 0` by decide_tac >> - `q ** n * p ** d * p ** m = p ** m` by metis_tac[EXP_ADD, MULT_ASSOC] >> - `_ = 1 * p ** m` by rw[] >> - `q ** n * p ** d = 1` by metis_tac[EQ_MULT_RCANCEL] >> - `(q ** n = 1) /\ (p ** d = 1)` by metis_tac[MULT_EQ_1] >> - metis_tac[EXP_EQ_1] - ] - ] -QED - -(* Theorem: 1 < n ==> !m. (n ** m = n) <=> (m = 1) *) -(* Proof: - If part: n ** m = n ==> m = 1 - Note n = n ** 1 by EXP_1 - so n ** m = n ** 1 by given - or m = 1 by EXP_BASE_INJECTIVE, 1 < n - Only-if part: m = 1 ==> n ** m = n - This is true by EXP_1 -*) -val POWER_EQ_SELF = store_thm( - "POWER_EQ_SELF", - ``!n. 1 < n ==> !m. (n ** m = n) <=> (m = 1)``, - metis_tac[EXP_BASE_INJECTIVE, EXP_1]); - -(* Theorem: k < HALF n <=> k + 1 < n - k *) -(* Proof: - If part: k < HALF n ==> k + 1 < n - k - Claim: 1 < n - 2 * k. - Proof: If EVEN n, - Claim: n - 2 * k <> 0 - Proof: By contradiction, assume n - 2 * k = 0. - Then 2 * k = n = 2 * HALF n by EVEN_HALF - or k = HALF n by MULT_LEFT_CANCEL, 2 <> 0 - but this contradicts k < HALF n. - Claim: n - 2 * k <> 1 - Proof: By contradiction, assume n - 2 * k = 1. - Then n = 2 * k + 1 by SUB_EQ_ADD, 0 < 1 - or ODD n by ODD_EXISTS, ADD1 - but this contradicts EVEN n by EVEN_ODD - Thus n - 2 * k <> 1, or 1 < n - 2 * k by above claims. - Since 1 < n - 2 * k by above - so 2 * k + 1 < n by arithmetic - or k + k + 1 < n by TIMES2 - or k + 1 < n - k by arithmetic - - Only-if part: k + 1 < n - k ==> k < HALF n - Since k + 1 < n - k - so 2 * k + 1 < n by arithmetic - But n = 2 * HALF n + (n MOD 2) by DIVISION, MULT_COMM, 0 < 2 - and n MOD 2 < 2 by MOD_LESS, 0 < 2 - so n <= 2 * HALF n + 1 by arithmetic - Thus 2 * k + 1 < 2 * HALF n + 1 by LESS_LESS_EQ_TRANS - or k < HALF by LT_MULT_LCANCEL -*) -val LESS_HALF_IFF = store_thm( - "LESS_HALF_IFF", - ``!n k. k < HALF n <=> k + 1 < n - k``, - rw[EQ_IMP_THM] >| [ - `1 < n - 2 * k` by - (Cases_on `EVEN n` >| [ - `n - 2 * k <> 0` by - (spose_not_then strip_assume_tac >> - `2 * HALF n = n` by metis_tac[EVEN_HALF] >> - decide_tac) >> - `n - 2 * k <> 1` by - (spose_not_then strip_assume_tac >> - `n = 2 * k + 1` by decide_tac >> - `ODD n` by metis_tac[ODD_EXISTS, ADD1] >> - metis_tac[EVEN_ODD]) >> - decide_tac, - `n MOD 2 = 1` by metis_tac[EVEN_ODD, ODD_MOD2] >> - `n = 2 * HALF n + (n MOD 2)` by metis_tac[DIVISION, MULT_COMM, DECIDE``0 < 2``] >> - decide_tac - ]) >> - decide_tac, - `2 * k + 1 < n` by decide_tac >> - `n = 2 * HALF n + (n MOD 2)` by metis_tac[DIVISION, MULT_COMM, DECIDE``0 < 2``] >> - `n MOD 2 < 2` by rw[] >> - decide_tac - ]); - -(* Theorem: HALF n < k ==> n - k <= HALF n *) -(* Proof: - If k < n, - If EVEN n, - Note HALF n + HALF n < k + HALF n by HALF n < k - or 2 * HALF n < k + HALF n by TIMES2 - or n < k + HALF n by EVEN_HALF, EVEN n - or n - k < HALF n by LESS_EQ_SUB_LESS, k <= n - Hence true. - If ~EVEN n, then ODD n by EVEN_ODD - Note HALF n + HALF n + 1 < k + HALF n + 1 by HALF n < k - or 2 * HALF n + 1 < k + HALF n + 1 by TIMES2 - or n < k + HALF n + 1 by ODD_HALF - or n <= k + HALF n by arithmetic - so n - k <= HALF n by SUB_LESS_EQ_ADD, k <= n - If ~(k < n), then n <= k. - Thus n - k = 0, hence n - k <= HALF n by arithmetic -*) -val MORE_HALF_IMP = store_thm( - "MORE_HALF_IMP", - ``!n k. HALF n < k ==> n - k <= HALF n``, - rpt strip_tac >> - Cases_on `k < n` >| [ - Cases_on `EVEN n` >| [ - `n = 2 * HALF n` by rw[EVEN_HALF] >> - `n < k + HALF n` by decide_tac >> - `n - k < HALF n` by decide_tac >> - decide_tac, - `ODD n` by rw[ODD_EVEN] >> - `n = 2 * HALF n + 1` by rw[ODD_HALF] >> - decide_tac - ], - decide_tac - ]); - -(* Theorem: (!k. k < m ==> f k < f (k + 1)) ==> !k. k < m ==> f k < f m *) -(* Proof: - By induction on the difference (m - k): - Base: 0 = m - k /\ k < m ==> f k < f m - Note m = k and k < m contradicts, hence true. - Step: !m k. (v = m - k) ==> k < m ==> f k < f m ==> - SUC v = m - k /\ k < m ==> f k < f m - Note v + 1 = m - k by ADD1 - so v = m - (k + 1) by arithmetic - If v = 0, - Then m = k + 1 - so f k < f (k + 1) by implication - or f k < f m by m = k + 1 - If v <> 0, then 0 < v. - Then 0 < m - (k + 1) by v = m - (k + 1) - or k + 1 < m by arithmetic - Now f k < f (k + 1) by implication, k < m - and f (k + 1) < f m by induction hypothesis, put k = k + 1 - so f k < f m by LESS_TRANS -*) -val MONOTONE_MAX = store_thm( - "MONOTONE_MAX", - ``!f m. (!k. k < m ==> f k < f (k + 1)) ==> !k. k < m ==> f k < f m``, - rpt strip_tac >> - Induct_on `m - k` >| [ - rpt strip_tac >> - decide_tac, - rpt strip_tac >> - `v + 1 = m - k` by rw[] >> - `v = m - (k + 1)` by decide_tac >> - Cases_on `v = 0` >| [ - `m = k + 1` by decide_tac >> - rw[], - `k + 1 < m` by decide_tac >> - `f k < f (k + 1)` by rw[] >> - `f (k + 1) < f m` by rw[] >> - decide_tac - ] - ]); - -(* Theorem: (multiple gap) - If n divides m, n cannot divide any x: m - n < x < m, or m < x < m + n - n divides m ==> !x. m - n < x /\ x < m + n /\ n divides x ==> (x = m) *) -(* Proof: - All these x, when divided by n, have non-zero remainders. - Since n divides m and n divides x - ==> ?h. m = h * n, and ?k. x = k * n by divides_def - Hence m - n < x - ==> (h-1) * n < k * n by RIGHT_SUB_DISTRIB, MULT_LEFT_1 - and x < m + n - ==> k * n < (h+1) * n by RIGHT_ADD_DISTRIB, MULT_LEFT_1 - so 0 < n, and h-1 < k, and k < h+1 by LT_MULT_RCANCEL - that is, h <= k, and k <= h - Therefore h = k, or m = h * n = k * n = x. -*) -val MULTIPLE_INTERVAL = store_thm( - "MULTIPLE_INTERVAL", - ``!n m. n divides m ==> !x. m - n < x /\ x < m + n /\ n divides x ==> (x = m)``, - rpt strip_tac >> - `(?h. m = h*n) /\ (?k. x = k * n)` by metis_tac[divides_def] >> - `(h-1) * n < k * n` by metis_tac[RIGHT_SUB_DISTRIB, MULT_LEFT_1] >> - `k * n < (h+1) * n` by metis_tac[RIGHT_ADD_DISTRIB, MULT_LEFT_1] >> - `0 < n /\ h-1 < k /\ k < h+1` by metis_tac[LT_MULT_RCANCEL] >> - `h = k` by decide_tac >> - metis_tac[]); - -(* Theorem: 0 < m ==> (SUC (n MOD m) = SUC n MOD m + (SUC n DIV m - n DIV m) * m) *) -(* Proof: - Let x = n DIV m, y = (SUC n) DIV m. - Let a = SUC (n MOD m), b = (SUC n) MOD m. - Note SUC n = y * m + b by DIVISION, 0 < m, for (SUC n), [1] - and n = x * m + (n MOD m) by DIVISION, 0 < m, for n - so SUC n = SUC (x * m + (n MOD m)) by above - = x * m + a by ADD_SUC, [2] - Equating, x * m + a = y * m + b by [1], [2] - Now n < SUC n by SUC_POS - so n DIV m <= (SUC n) DIV m by DIV_LE_MONOTONE, n <= SUC n - or x <= y - so x * m <= y * m by LE_MULT_RCANCEL, m <> 0 - - Thus a = b + (y * m - x * m) by arithmetic - = b + (y - x) * m by RIGHT_SUB_DISTRIB -*) -val MOD_SUC_EQN = store_thm( - "MOD_SUC_EQN", - ``!m n. 0 < m ==> (SUC (n MOD m) = SUC n MOD m + (SUC n DIV m - n DIV m) * m)``, - rpt strip_tac >> - qabbrev_tac `x = n DIV m` >> - qabbrev_tac `y = (SUC n) DIV m` >> - qabbrev_tac `a = SUC (n MOD m)` >> - qabbrev_tac `b = (SUC n) MOD m` >> - `SUC n = y * m + b` by rw[DIVISION, Abbr`y`, Abbr`b`] >> - `n = x * m + (n MOD m)` by rw[DIVISION, Abbr`x`] >> - `SUC n = x * m + a` by rw[Abbr`a`] >> - `n < SUC n` by rw[] >> - `x <= y` by rw[DIV_LE_MONOTONE, Abbr`x`, Abbr`y`] >> - `x * m <= y * m` by rw[] >> - `a = b + (y * m - x * m)` by decide_tac >> - `_ = b + (y - x) * m` by rw[] >> - rw[]); - -(* Note: Compare this result with these in arithmeticTheory: -MOD_SUC |- 0 < y /\ SUC x <> SUC (x DIV y) * y ==> (SUC x MOD y = SUC (x MOD y)) -MOD_SUC_IFF |- 0 < y ==> ((SUC x MOD y = SUC (x MOD y)) <=> SUC x <> SUC (x DIV y) * y) -*) - -(* Theorem: 1 < n ==> 1 < HALF (n ** 2) *) -(* Proof: - 1 < n - ==> 2 <= n by arithmetic - ==> 2 ** 2 <= n ** 2 by EXP_EXP_LE_MONO - ==> (2 ** 2) DIV 2 <= (n ** 2) DIV 2 by DIV_LE_MONOTONE - ==> 2 <= (n ** 2) DIV 2 by arithmetic - ==> 1 < (n ** 2) DIV 2 by arithmetic -*) -val ONE_LT_HALF_SQ = store_thm( - "ONE_LT_HALF_SQ", - ``!n. 1 < n ==> 1 < HALF (n ** 2)``, - rpt strip_tac >> - `2 <= n` by decide_tac >> - `2 ** 2 <= n ** 2` by rw[] >> - `(2 ** 2) DIV 2 <= (n ** 2) DIV 2` by rw[DIV_LE_MONOTONE] >> - `(2 ** 2) DIV 2 = 2` by EVAL_TAC >> - decide_tac); - -(* Theorem: 0 < n ==> (HALF (2 ** n) = 2 ** (n - 1)) *) -(* Proof - By induction on n. - Base: 0 < 0 ==> 2 ** 0 DIV 2 = 2 ** (0 - 1) - This is trivially true as 0 < 0 = F. - Step: 0 < n ==> HALF (2 ** n) = 2 ** (n - 1) - ==> 0 < SUC n ==> HALF (2 ** SUC n) = 2 ** (SUC n - 1) - HALF (2 ** SUC n) - = HALF (2 * 2 ** n) by EXP - = 2 ** n by MULT_TO_DIV - = 2 ** (SUC n - 1) by SUC_SUB1 -*) -Theorem EXP_2_HALF: - !n. 0 < n ==> (HALF (2 ** n) = 2 ** (n - 1)) -Proof - Induct >> simp[EXP, MULT_TO_DIV] -QED - -(* -There is EVEN_MULT |- !m n. EVEN (m * n) <=> EVEN m \/ EVEN n -There is EVEN_DOUBLE |- !n. EVEN (TWICE n) -*) - -(* Theorem: EVEN n ==> (HALF (m * n) = m * HALF n) *) -(* Proof: - Note n = TWICE (HALF n) by EVEN_HALF - Let k = HALF n. - HALF (m * n) - = HALF (m * (2 * k)) by above - = HALF (2 * (m * k)) by MULT_COMM_ASSOC - = m * k by HALF_TWICE - = m * HALF n by notation -*) -val HALF_MULT_EVEN = store_thm( - "HALF_MULT_EVEN", - ``!m n. EVEN n ==> (HALF (m * n) = m * HALF n)``, - metis_tac[EVEN_HALF, MULT_COMM_ASSOC, HALF_TWICE]); - -(* Theorem: 0 < k /\ k * m < n ==> m < n *) -(* Proof: - Note ?h. k = SUC h by num_CASES, k <> 0 - k * m - = SUC h * m by above - = (h + 1) * m by ADD1 - = h * m + 1 * m by LEFT_ADD_DISTRIB - = h * m + m by MULT_LEFT_1 - Since 0 <= h * m, - so k * m < n ==> m < n. -*) -val MULT_LT_IMP_LT = store_thm( - "MULT_LT_IMP_LT", - ``!m n k. 0 < k /\ k * m < n ==> m < n``, - rpt strip_tac >> - `k <> 0` by decide_tac >> - `?h. k = SUC h` by metis_tac[num_CASES] >> - `k * m = h * m + m` by rw[ADD1] >> - decide_tac); - -(* Theorem: 0 < k /\ k * m <= n ==> m <= n *) -(* Proof: - Note 1 <= k by 0 < k - so 1 * m <= k * m by LE_MULT_RCANCEL - or m <= k * m <= n by inequalities -*) -Theorem MULT_LE_IMP_LE: - !m n k. 0 < k /\ k * m <= n ==> m <= n -Proof - rpt strip_tac >> - `1 <= k` by decide_tac >> - `1 * m <= k * m` by simp[] >> - decide_tac -QED - -(* Theorem: n * HALF ((SQ n) ** 2) <= HALF (n ** 5) *) -(* Proof: - n * HALF ((SQ n) ** 2) - <= HALF (n * (SQ n) ** 2) by HALF_MULT - = HALF (n * (n ** 2) ** 2) by EXP_2 - = HALF (n * n ** 4) by EXP_EXP_MULT - = HALF (n ** 5) by EXP -*) -val HALF_EXP_5 = store_thm( - "HALF_EXP_5", - ``!n. n * HALF ((SQ n) ** 2) <= HALF (n ** 5)``, - rpt strip_tac >> - `n * ((SQ n) ** 2) = n * n ** 4` by rw[EXP_2, EXP_EXP_MULT] >> - `_ = n ** 5` by rw[EXP] >> - metis_tac[HALF_MULT]); - -(* Theorem: n <= 2 * m <=> (n <> 0 ==> HALF (n - 1) < m) *) -(* Proof: - Let k = n - 1, then n = SUC k. - If part: n <= TWICE m /\ n <> 0 ==> HALF k < m - Note HALF (SUC k) <= m by DIV_LE_MONOTONE, HALF_TWICE - If EVEN n, - Then ODD k by EVEN_ODD_SUC - ==> HALF (SUC k) = SUC (HALF k) by ODD_SUC_HALF - Thus SUC (HALF k) <= m by above - or HALF k < m by LESS_EQ - If ~EVEN n, then ODD n by EVEN_ODD - Thus EVEN k by EVEN_ODD_SUC - ==> HALF (SUC k) = HALF k by EVEN_SUC_HALF - But k <> TWICE m by k = n - 1, n <= TWICE m - ==> HALF k <> m by EVEN_HALF - Thus HALF k < m by HALF k <= m, HALF k <> m - - Only-if part: n <> 0 ==> HALF k < m ==> n <= TWICE m - If n = 0, trivially true. - If n <> 0, has HALF k < m. - If EVEN n, - Then ODD k by EVEN_ODD_SUC - ==> HALF (SUC k) = SUC (HALF k) by ODD_SUC_HALF - But SUC (HALF k) <= m by HALF k < m - Thus HALF n <= m by n = SUC k - ==> TWICE (HALF n) <= TWICE m by LE_MULT_LCANCEL - or n <= TWICE m by EVEN_HALF - If ~EVEN n, then ODD n by EVEN_ODD - Then EVEN k by EVEN_ODD_SUC - ==> TWICE (HALF k) < TWICE m by LT_MULT_LCANCEL - or k < TWICE m by EVEN_HALF - or n <= TWICE m by n = k + 1 -*) -val LE_TWICE_ALT = store_thm( - "LE_TWICE_ALT", - ``!m n. n <= 2 * m <=> (n <> 0 ==> HALF (n - 1) < m)``, - rw[EQ_IMP_THM] >| [ - `n = SUC (n - 1)` by decide_tac >> - qabbrev_tac `k = n - 1` >> - `HALF (SUC k) <= m` by metis_tac[DIV_LE_MONOTONE, HALF_TWICE, DECIDE``0 < 2``] >> - Cases_on `EVEN n` >| [ - `ODD k` by rw[EVEN_ODD_SUC] >> - `HALF (SUC k) = SUC (HALF k)` by rw[ODD_SUC_HALF] >> - decide_tac, - `ODD n` by metis_tac[EVEN_ODD] >> - `EVEN k` by rw[EVEN_ODD_SUC] >> - `HALF (SUC k) = HALF k` by rw[EVEN_SUC_HALF] >> - `k <> TWICE m` by rw[Abbr`k`] >> - `HALF k <> m` by metis_tac[EVEN_HALF] >> - decide_tac - ], - Cases_on `n = 0` >- - rw[] >> - `n = SUC (n - 1)` by decide_tac >> - qabbrev_tac `k = n - 1` >> - Cases_on `EVEN n` >| [ - `ODD k` by rw[EVEN_ODD_SUC] >> - `HALF (SUC k) = SUC (HALF k)` by rw[ODD_SUC_HALF] >> - `HALF n <= m` by rw[] >> - metis_tac[LE_MULT_LCANCEL, EVEN_HALF, DECIDE``2 <> 0``], - `ODD n` by metis_tac[EVEN_ODD] >> - `EVEN k` by rw[EVEN_ODD_SUC] >> - `k < TWICE m` by metis_tac[LT_MULT_LCANCEL, EVEN_HALF, DECIDE``0 < 2``] >> - rw[Abbr`k`] - ] - ]); - -(* Theorem: (HALF n) DIV 2 ** m = n DIV (2 ** SUC m) *) -(* Proof: - (HALF n) DIV 2 ** m - = (n DIV 2) DIV (2 ** m) by notation - = n DIV (2 * 2 ** m) by DIV_DIV_DIV_MULT, 0 < 2, 0 < 2 ** m - = n DIV (2 ** (SUC m)) by EXP -*) -val HALF_DIV_TWO_POWER = store_thm( - "HALF_DIV_TWO_POWER", - ``!m n. (HALF n) DIV 2 ** m = n DIV (2 ** SUC m)``, - rw[DIV_DIV_DIV_MULT, EXP]); - -(* Theorem: 1 + 2 + 3 + 4 = 10 *) -(* Proof: by calculation. *) -Theorem fit_for_10: - 1 + 2 + 3 + 4 = 10 -Proof - decide_tac -QED - -(* Theorem: 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 = 100 *) -(* Proof: by calculation. *) -Theorem fit_for_100: - 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 = 100 -Proof - decide_tac -QED - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/helperSetScript.sml b/examples/algebra/lib/helperSetScript.sml deleted file mode 100644 index 39aba066be..0000000000 --- a/examples/algebra/lib/helperSetScript.sml +++ /dev/null @@ -1,4443 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Helper Theorems - a collection of useful results -- for Sets. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "helperSet"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* open dependent theories *) -open pred_setTheory; - -(* val _ = load "helperNumTheory"; *) -open helperNumTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open arithmeticTheory dividesTheory; -open gcdTheory; (* for P_EUCLIDES *) - - -(* ------------------------------------------------------------------------- *) -(* HelperSet Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - countFrom m n = IMAGE ($+ m) (count n) - s =|= u # v = split s u v - EVERY_FINITE P = !s. s IN P ==> FINITE s - PAIR_DISJOINT P = !s t. s IN P /\ t IN P /\ ~(s = t) ==> DISJOINT s t - SET_ADDITIVE f = (f {} = 0) /\ - (!s t. FINITE s /\ FINITE t ==> (f (s UNION t) + f (s INTER t) = f s + f t)) - SET_MULTIPLICATIVE f = (f {} = 1) /\ - (!s t. FINITE s /\ FINITE t ==> (f (s UNION t) * f (s INTER t) = f s * f t)) - f PERMUTES s = BIJ f s s - PPOW s = (POW s) DIFF {s} -*) -(* Definitions and Theorems (# are exported): - - Logic Theorems: - EQ_IMP2_THM |- !A B. (A <=> B) <=> (A ==> B) /\ ((A ==> B) ==> B ==> A) - BOOL_EQ |- !b1 b2 f. (b1 <=> b2) ==> (f b1 = f b2) - IMP_IMP |- !b c d. b /\ (c ==> d) ==> (b ==> c) ==> d - AND_IMP_OR_NEG |- !p q. p /\ q ==> p \/ ~q - OR_IMP_IMP |- !p q r. (p \/ q ==> r) ==> p /\ ~q ==> r - PUSH_IN_INTO_IF |- !b x s t. x IN (if b then s else t) <=> if b then x IN s else x IN t - - Set Theorems: - IN_SUBSET |- !s t. s SUBSET t <=> !x. x IN s ==> x IN t - DISJOINT_DIFF |- !s t. DISJOINT (s DIFF t) t /\ DISJOINT t (s DIFF t) - DISJOINT_DIFF_IFF |- !s t. DISJOINT s t <=> (s DIFF t = s) - UNION_DIFF_EQ_UNION |- !s t. s UNION (t DIFF s) = s UNION t - INTER_DIFF |- !s t. (s INTER (t DIFF s) = {}) /\ ((t DIFF s) INTER s = {}) - SUBSET_SING |- !s x. {x} SUBSET s /\ SING s <=> (s = {x}) - INTER_SING |- !s x. x IN s ==> (s INTER {x} = {x}) - ONE_ELEMENT_SING |- !s a. s <> {} /\ (!k. k IN s ==> (k = a)) ==> (s = {a}) - SING_ONE_ELEMENT |- !s. s <> {} ==> (SING s <=> !x y. x IN s /\ y IN s ==> (x = y)) - SING_ELEMENT |- !s. SING s ==> !x y. x IN s /\ y IN s ==> (x = y) - SING_TEST |- !s. SING s <=> s <> {} /\ !x y. x IN s /\ y IN s ==> (x = y) - SING_INTER |- !s x. {x} INTER s = if x IN s then {x} else {} - IN_SING_OR_EMPTY |- !b x y. x IN (if b then {y} else {}) ==> (x = y) - SING_CARD_1 |- !s. SING s ==> (CARD s = 1) - CARD_EQ_1 |- !s. FINITE s ==> ((CARD s = 1) <=> SING s) - INSERT_DELETE_COMM |- !s x y. x <> y ==> ((x INSERT s) DELETE y = x INSERT s DELETE y) - INSERT_DELETE_NON_ELEMENT - |- !x s. x NOTIN s ==> (x INSERT s) DELETE x = s - SUBSET_INTER_SUBSET |- !s t u. s SUBSET u ==> s INTER t SUBSET u - DIFF_DIFF_EQ_INTER |- !s t. s DIFF (s DIFF t) = s INTER t - SET_EQ_BY_DIFF |- !s t. (s = t) <=> s SUBSET t /\ (t DIFF s = {}) - SUBSET_INSERT_BOTH |- !s1 s2 x. s1 SUBSET s2 ==> x INSERT s1 SUBSET x INSERT s2 - INSERT_SUBSET_SUBSET|- !s t x. x NOTIN s /\ x INSERT s SUBSET t ==> s SUBSET t DELETE x - DIFF_DELETE |- !s t x. s DIFF t DELETE x = s DIFF (x INSERT t) - SUBSET_DIFF_CARD |- !a b. FINITE a /\ b SUBSET a ==> (CARD (a DIFF b) = CARD a - CARD b) - SUBSET_SING_IFF |- !s x. s SUBSET {x} <=> (s = {}) \/ (s = {x}) - SUBSET_CARD_EQ |- !s t. FINITE t /\ s SUBSET t ==> (CARD s = CARD t <=> s = t) - IMAGE_SUBSET_TARGET |- !f s t. (!x. x IN s ==> f x IN t) <=> IMAGE f s SUBSET t - SURJ_CARD_IMAGE |- !f s t. SURJ f s t ==> CARD (IMAGE f s) = CARD t - - Image and Bijection: - INJ_CONG |- !f g s t. (!x. x IN s ==> (f x = g x)) ==> (INJ f s t <=> INJ g s t) - SURJ_CONG |- !f g s t. (!x. x IN s ==> (f x = g x)) ==> (SURJ f s t <=> SURJ g s t) - BIJ_CONG |- !f g s t. (!x. x IN s ==> (f x = g x)) ==> (BIJ f s t <=> BIJ g s t) - INJ_ELEMENT |- !f s t x. INJ f s t /\ x IN s ==> f x IN t - SURJ_ELEMENT |- !f s t x. SURJ f s t /\ x IN s ==> f x IN t - BIJ_ELEMENT |- !f s t x. BIJ f s t /\ x IN s ==> f x IN t - INJ_UNIV |- !f s t. INJ f s t ==> INJ f s univ(:'b) - INJ_SUBSET_UNIV |- !f s. INJ f univ(:'a) univ(:'b) ==> INJ f s univ(:'b) - INJ_IMAGE_BIJ_ALT |- !f s. INJ f s univ(:'b) ==> BIJ f s (IMAGE f s) - INJ_IMAGE_EQ |- !P f. INJ f P univ(:'b) ==> !s t. s SUBSET P /\ t SUBSET P ==> - ((IMAGE f s = IMAGE f t) <=> (s = t)) - INJ_IMAGE_INTER |- !P f. INJ f P univ(:'b) ==> !s t. s SUBSET P /\ t SUBSET P ==> - (IMAGE f (s INTER t) = IMAGE f s INTER IMAGE f t) - INJ_IMAGE_DISJOINT |- !P f. INJ f P univ(:'b) ==> !s t. s SUBSET P /\ t SUBSET P ==> - (DISJOINT s t <=> DISJOINT (IMAGE f s) (IMAGE f t)) - INJ_I |- !s. INJ I s univ(:'a) - INJ_I_IMAGE |- !s f. INJ I (IMAGE f s) univ(:'b) - BIJ_THM |- !f s t. BIJ f s t <=> - (!x. x IN s ==> f x IN t) /\ !y. y IN t ==> ?!x. x IN s /\ (f x = y) - BIJ_IS_INJ |- !f s t. BIJ f s t ==> - !x y. x IN s /\ y IN s /\ (f x = f y) ==> (x = y) - BIJ_IS_SURJ |- !f s t. BIJ f s t ==> !x. x IN t ==> ?y. y IN s /\ f y = x - BIJ_FINITE_IFF |- !f s t. BIJ f s t ==> (FINITE s <=> FINITE t) - INJ_EQ_11 |- !f s x y. INJ f s s /\ x IN s /\ y IN s ==> ((f x = f y) <=> (x = y)) - INJ_IMP_11 |- !f. INJ f univ(:'a) univ(:'b) ==> !x y. f x = f y <=> x = y - BIJ_I_SAME |- !s. BIJ I s s - IMAGE_K |- !s. s <> {} ==> !e. IMAGE (K e) s = {e} - IMAGE_ELEMENT_CONDITION |- !f. (!x y. (f x = f y) ==> (x = y)) ==> !s e. e IN s <=> f e IN IMAGE f s - BIGUNION_ELEMENTS_SING |- !s. BIGUNION (IMAGE (\x. {x}) s) = s - IMAGE_DIFF |- !s t f. s SUBSET t /\ INJ f t univ(:'b) ==> - (IMAGE f (t DIFF s) = IMAGE f t DIFF IMAGE f s) - - More Theorems and Sets for Counting: - COUNT_0 |- count 0 = {} - COUNT_1 |- count 1 = {0} - COUNT_NOT_SELF |- !n. n NOTIN count n - COUNT_EQ_EMPTY |- !n. (count n = {}) <=> (n = 0) - COUNT_SUBSET |- !m n. m <= n ==> count m SUBSET count n - COUNT_SUC_SUBSET |- !n t. count (SUC n) SUBSET t <=> count n SUBSET t /\ n IN t - DIFF_COUNT_SUC |- !n t. t DIFF count (SUC n) = t DIFF count n DELETE n - COUNT_SUC_BY_SUC |- !n. count (SUC n) = 0 INSERT IMAGE SUC (count n) - IMAGE_COUNT_SUC |- !f n. IMAGE f (count (SUC n)) = f n INSERT IMAGE f (count n) - IMAGE_COUNT_SUC_BY_SUC - |- !f n. IMAGE f (count (SUC n)) = f 0 INSERT IMAGE (f o SUC) (count n) - countFrom_0 |- !m. countFrom m 0 = {} - countFrom_SUC |- !m n m n. countFrom m (SUC n) = m INSERT countFrom (m + 1) n - countFrom_first |- !m n. 0 < n ==> m IN countFrom m n - countFrom_less |- !m n x. x < m ==> x NOTIN countFrom m n - count_by_countFrom |- !n. count n = countFrom 0 n - count_SUC_by_countFrom |- !n. count (SUC n) = 0 INSERT countFrom 1 n - - CARD_UNION_3_EQN |- !a b c. FINITE a /\ FINITE b /\ FINITE c ==> - (CARD (a UNION b UNION c) = - CARD a + CARD b + CARD c + CARD (a INTER b INTER c) - - CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c)) - CARD_UNION_3_DISJOINT - |- !a b c. FINITE a /\ FINITE b /\ FINITE c /\ - DISJOINT a b /\ DISJOINT b c /\ DISJOINT a c ==> - (CARD (a UNION b UNION c) = CARD a + CARD b + CARD c) - - Maximum and Minimum of a Set: - MAX_SET_LESS |- !s n. FINITE s /\ MAX_SET s < n ==> !x. x IN s ==> x < n - MAX_SET_TEST |- !s. FINITE s /\ s <> {} ==> - !x. x IN s /\ (!y. y IN s ==> y <= x) ==> (x = MAX_SET s) - MIN_SET_TEST |- !s. s <> {} ==> - !x. x IN s /\ (!y. y IN s ==> x <= y) ==> (x = MIN_SET s) - MAX_SET_TEST_IFF |- !s. FINITE s /\ s <> {} ==> !x. x IN s ==> - ((MAX_SET s = x) <=> !y. y IN s ==> y <= x) - MIN_SET_TEST_IFF |- !s. s <> {} ==> !x. x IN s ==> - ((MIN_SET s = x) <=> !y. y IN s ==> x <= y) - MAX_SET_EMPTY |- MAX_SET {} = 0 - MAX_SET_SING |- !e. MAX_SET {e} = e - MAX_SET_IN_SET |- !s. FINITE s /\ s <> {} ==> MAX_SET s IN s - MAX_SET_PROPERTY |- !s. FINITE s ==> !x. x IN s ==> x <= MAX_SET s - MIN_SET_SING |- !e. MIN_SET {e} = e - MIN_SET_IN_SET |- !s. s <> {} ==> MIN_SET s IN s - MIN_SET_PROPERTY |- !s. s <> {} ==> !x. x IN s ==> MIN_SET s <= x - MAX_SET_DELETE |- !s. FINITE s /\ s <> {} /\ s <> {MIN_SET s} ==> - (MAX_SET (s DELETE MIN_SET s) = MAX_SET s) - MAX_SET_EQ_0 |- !s. FINITE s ==> ((MAX_SET s = 0) <=> (s = {}) \/ (s = {0})) - MIN_SET_EQ_0 |- !s. s <> {} ==> ((MIN_SET s = 0) <=> 0 IN s) - MAX_SET_IMAGE_SUC_COUNT |- !n. MAX_SET (IMAGE SUC (count n)) = n - MAX_SET_IMAGE_with_HALF |- !f c x. HALF x <= c ==> f x <= MAX_SET {f x | HALF x <= c} - MAX_SET_IMAGE_with_DIV |- !f b c x. 0 < b /\ x DIV b <= c ==> f x <= MAX_SET {f x | x DIV b <= c} - MAX_SET_IMAGE_with_DEC |- !f b c x. x - b <= c ==> f x <= MAX_SET {f x | x - b <= c} - - Finite and Cardinality Theorems: - INJ_CARD_IMAGE_EQN |- !f s. INJ f s univ(:'b) /\ FINITE s ==> (CARD (IMAGE f s) = CARD s) - FINITE_INJ_AS_SURJ |- !f s t. INJ f s t /\ FINITE s /\ FINITE t /\ (CARD s = CARD t) ==> SURJ f s t - FINITE_INJ_IS_SURJ |- !f s t. FINITE s /\ FINITE t /\ - CARD s = CARD t /\ INJ f s t ==> SURJ f s t - FINITE_INJ_IS_BIJ |- !f s t. FINITE s /\ FINITE t /\ - CARD s = CARD t /\ INJ f s t ==> BIJ f s t - FINITE_COUNT_IMAGE |- !P n. FINITE {P x | x < n} - FINITE_BIJ_PROPERTY |- !f s t. FINITE s /\ BIJ f s t ==> FINITE t /\ (CARD s = CARD t) - FINITE_CARD_IMAGE |- !s f. (!x y. (f x = f y) <=> (x = y)) /\ FINITE s ==> (CARD (IMAGE f s) = CARD s) - CARD_IMAGE_SUC |- !s. FINITE s ==> (CARD (IMAGE SUC s) = CARD s) - CARD_UNION_DISJOINT |- !s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> (CARD (s UNION t) = CARD s + CARD t) - FINITE_BIJ_COUNT_CARD |- !s. FINITE s ==> ?f. BIJ f (count (CARD s)) s - image_mod_subset_count |- !s n. 0 < n ==> IMAGE (\x. x MOD n) s SUBSET count n - card_mod_image |- !s n. 0 < n ==> CARD (IMAGE (\x. x MOD n) s) <= n - card_mod_image_nonzero |- !s n. 0 < n /\ 0 NOTIN IMAGE (\x. x MOD n) s ==> - CARD (IMAGE (\x. x MOD n) s) < n - - Partition Property: - finite_partition_property |- !s. FINITE s ==> !u v. s =|= u # v ==> ((u = {}) <=> (v = s)) - finite_partition_by_predicate |- !s. FINITE s ==> !P. (let u = {x | x IN s /\ P x} in - let v = {x | x IN s /\ ~P x} in - FINITE u /\ FINITE v /\ s =|= u # v) - partition_by_subset |- !s u. u SUBSET s ==> (let v = s DIFF u in s =|= u # v) - partition_by_element |- !s x. x IN s ==> s =|= {x} # s DELETE x - - Splitting of a set: - SPLIT_EMPTY |- !s t. s =|= {} # t <=> s = t - SPLIT_UNION |- !s u v a b. s =|= u # v /\ v =|= a # b ==> s =|= u UNION a # b /\ u UNION a =|= u # a - SPLIT_EQ |- !s u v. s =|= u # v <=> u SUBSET s /\ v = s DIFF u - SPLIT_SYM |- !s u v. s =|= u # v <=> s =|= v # u - SPLIT_SYM_IMP |- !s u v. s =|= u # v ==> s =|= v # u - SPLIT_SING |- !s v x. s =|= {x} # v <=> x IN s /\ v = s DELETE x - SPLIT_SUBSETS |- !s u v. s =|= u # v ==> u SUBSET s /\ v SUBSET s - SPLIT_FINITE |- !s u v. FINITE s /\ s =|= u # v ==> FINITE u /\ FINITE v - SPLIT_CARD |- !s u v. FINITE s /\ s =|= u # v ==> (CARD s = CARD u + CARD v) - SPLIT_EQ_DIFF |- !s u v. s =|= u # v <=> (u = s DIFF v) /\ (v = s DIFF u) - SPLIT_BY_SUBSET |- !s u. u SUBSET s ==> (let v = s DIFF u in s =|= u # v) - SUBSET_DIFF_DIFF |- !s t. t SUBSET s ==> (s DIFF (s DIFF t) = t) - SUBSET_DIFF_EQ |- !s1 s2 t. s1 SUBSET t /\ s2 SUBSET t /\ (t DIFF s1 = t DIFF s2) ==> (s1 = s2) - - Bijective Inverses: - BIJ_LINV_ELEMENT |- !f s t. BIJ f s t ==> !x. x IN t ==> LINV f s x IN s - BIJ_LINV_THM |- !f s t. BIJ f s t ==> - (!x. x IN s ==> (LINV f s (f x) = x)) /\ !x. x IN t ==> (f (LINV f s x) = x) - BIJ_RINV_INV |- !f s t. BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> - !x. x IN s ==> (RINV f s (f x) = x) - BIJ_RINV_BIJ |- !f s t. BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> BIJ (RINV f s) t s - LINV_SUBSET |- !f s t. INJ f t univ(:'b) /\ s SUBSET t ==> !x. x IN s ==> (LINV f t (f x) = x) - - Iteration, Summation and Product: - ITSET_SING |- !f x b. ITSET f {x} b = f x b - ITSET_PROPERTY |- !s f b. FINITE s /\ s <> {} ==> - (ITSET f s b = ITSET f (REST s) (f (CHOICE s) b)) - ITSET_CONG |- !f g. (f = g) ==> (ITSET f = ITSET g) - ITSET_REDUCTION |- !f. (!x y z. f x (f y z) = f y (f x z)) ==> - !s x b. FINITE s /\ x NOTIN s ==> (ITSET f (x INSERT s) b = f x (ITSET f s b)) - - Rework of ITSET Theorems: - closure_comm_assoc_fun_def |- !f s. closure_comm_assoc_fun f s <=> - (!x y. x IN s /\ y IN s ==> f x y IN s) /\ - !x y z. x IN s /\ y IN s /\ z IN s ==> (f x (f y z) = f y (f x z)) - SUBSET_COMMUTING_ITSET_INSERT |- !f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !x b::t. ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) - SUBSET_COMMUTING_ITSET_REDUCTION |- !f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !x b::t. ITSET f s (f x b) = f x (ITSET f s b) - SUBSET_COMMUTING_ITSET_RECURSES |- !f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !x b::t. ITSET f (x INSERT s) b = f x (ITSET f (s DELETE x) b) - - SUM_IMAGE and PROD_IMAGE Theorems: - SUM_IMAGE_EMPTY |- !f. SIGMA f {} = 0 - SUM_IMAGE_INSERT |- !f s. FINITE s ==> !e. e NOTIN s ==> (SIGMA f (e INSERT s) = f e + SIGMA f s) - SUM_IMAGE_AS_SUM_SET |- !s. FINITE s ==> !f. (!x y. (f x = f y) ==> (x = y)) ==> - (SIGMA f s = SUM_SET (IMAGE f s)) - SUM_IMAGE_DOUBLET |- !f x y. x <> y ==> SIGMA f {x; y} = f x + f y - SUM_IMAGE_TRIPLET |- !f x y z. x <> y /\ y <> z /\ z <> x ==> SIGMA f {x; y; z} = f x + f y + f z - SIGMA_CONSTANT |- !s. FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (SIGMA f s = k * CARD s) - SUM_IMAGE_CONSTANT |- !s. FINITE s ==> !c. SIGMA (K c) s = c * CARD s - SIGMA_CARD_CONSTANT |- !n s. FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s - SIGMA_CARD_SAME_SIZE_SETS - |- !n s. FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s - SIGMA_CARD_FACTOR |- !n s. FINITE s /\ (!e. e IN s ==> n divides CARD e) ==> n divides SIGMA CARD s - SIGMA_CONG |- !s f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (SIGMA f1 s = SIGMA f2 s) - CARD_AS_SIGMA |- !s. FINITE s ==> (CARD s = SIGMA (\x. 1) s) - CARD_EQ_SIGMA |- !s. FINITE s ==> (CARD s = SIGMA (K 1) s) - SIGMA_LE_SIGMA |- !s. FINITE s ==> !f g. (!x. x IN s ==> f x <= g x) ==> SIGMA f s <= SIGMA g s - SUM_IMAGE_UNION_EQN |- !s t. FINITE s /\ FINITE t ==> - !f. SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t - SUM_IMAGE_DISJOINT |- !s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> - !f. SIGMA f (s UNION t) = SIGMA f s + SIGMA f t - SUM_IMAGE_MONO_LT |- !s. FINITE s /\ s <> {} ==> - !f g. (!x. x IN s ==> f x < g x) ==> SIGMA f s < SIGMA g s - SUM_IMAGE_PSUBSET_LT |- !f s t. FINITE s /\ t PSUBSET s /\ (!x. x IN s ==> f x <> 0) ==> - SIGMA f t < SIGMA f s - card_le_sigma_card |- !s. FINITE s /\ (!e. e IN s ==> CARD e <> 0) ==> - CARD s <= SIGMA CARD s - card_eq_sigma_card |- !s. FINITE s /\ (!e. e IN s ==> CARD e <> 0) /\ - CARD s = SIGMA CARD s ==> !e. e IN s ==> CARD e = 1 - - PROD_IMAGE_EMPTY |- !f. PI f {} = 1 - PROD_IMAGE_INSERT |- !s. FINITE s ==> !f e. e NOTIN s ==> (PI f (e INSERT s) = f e * PI f s) - PROD_IMAGE_DELETE |- !s. FINITE s ==> !f e. 0 < f e ==> - (PI f (s DELETE e) = if e IN s then PI f s DIV f e else PI f s) - PROD_IMAGE_CONG |- !s f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s) - PI_CONSTANT |- !s. FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s) - PROD_IMAGE_CONSTANT |- !s. FINITE s ==> !c. PI (K c) s = c ** CARD s - - SUM_SET and PROD_SET Theorems: - SUM_SET_INSERT |- !s x. FINITE s /\ x NOTIN s ==> (SUM_SET (x INSERT s) = x + SUM_SET s) - SUM_SET_IMAGE_EQN |- !s. FINITE s ==> !f. INJ f s univ(:num) ==> (SUM_SET (IMAGE f s) = SIGMA f s) - SUM_SET_COUNT |- !n. SUM_SET (count n) = n * (n - 1) DIV 2 - - PROD_SET_SING |- !x. PROD_SET {x} = x - PROD_SET_NONZERO |- !s. FINITE s /\ 0 NOTIN s ==> 0 < PROD_SET s - PROD_SET_LESS |- !s. FINITE s /\ s <> {} /\ 0 NOTIN s ==> - !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s) - PROD_SET_LESS_SUC |- !s. FINITE s /\ s <> {} /\ 0 NOTIN s ==> PROD_SET s < PROD_SET (IMAGE SUC s) - PROD_SET_DIVISORS |- !s. FINITE s ==> !n x. x IN s /\ n divides x ==> n divides (PROD_SET s) - PROD_SET_INSERT |- !x s. FINITE s /\ x NOTIN s ==> (PROD_SET (x INSERT s) = x * PROD_SET s) - PROD_SET_IMAGE_EQN |- !s. FINITE s ==> !f. INJ f s univ(:num) ==> (PROD_SET (IMAGE f s) = PI f s) - PROD_SET_IMAGE_EXP |- !n. 1 < n ==> !m. PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** SUM_SET (count m) - PROD_SET_ELEMENT_DIVIDES |- !s x. FINITE s /\ x IN s ==> x divides PROD_SET s - PROD_SET_LESS_EQ |- !s. FINITE s ==> !f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ - (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s) - PROD_SET_LE_CONSTANT |- !s. FINITE s ==> !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s - PROD_SET_PRODUCT_GE_CONSTANT |- !s. FINITE s ==> !n f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ - (!x. x IN s ==> n <= f x * g x) ==> - n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s) - PROD_SET_PRODUCT_BY_PARTITION |- !s. FINITE s ==> - !u v. s =|= u # v ==> (PROD_SET s = PROD_SET u * PROD_SET v) - - Partition and Equivalent Class: - equiv_class_element |- !R s x y. y IN equiv_class R s x <=> y IN s /\ R x y - partition_on_empty |- !R. partition R {} = {} - partition_element |- !R s t. t IN partition R s <=> ?x. x IN s /\ (t = equiv_class R s x) - partition_elements |- !R s. partition R s = IMAGE (\x. equiv_class R s x) s - partition_as_image |- !R s. partition R s = IMAGE (\x. equiv_class R s x) s - partition_cong |- !R1 R2 s1 s2. (R1 = R2) /\ (s1 = s2) ==> (partition R1 s1 = partition R2 s2) - partition_element_not_empty - |- !R s e. R equiv_on s /\ e IN partition R s ==> e <> {} - equiv_class_not_empty |- !R s x. R equiv_on s /\ x IN s ==> equiv_class R s x <> {} - partition_element_exists - |- !R s x. R equiv_on s ==> (x IN s <=> ?e. e IN partition R s /\ x IN e) - equal_partition_card |- !R s n. FINITE s /\ R equiv_on s /\ - (!e. e IN partition R s ==> (CARD e = n)) ==> (CARD s = n * CARD (partition R s)) - equal_partition_factor |- !R s n. FINITE s /\ R equiv_on s /\ - (!e. e IN partition R s ==> CARD e = n) ==> n divides CARD s - factor_partition_card |- !R s n. FINITE s /\ R equiv_on s /\ - (!e. e IN partition R s ==> n divides CARD e) ==> n divides CARD s - - pair_disjoint_subset |- !s t. t SUBSET s /\ PAIR_DISJOINT s ==> PAIR_DISJOINT t - disjoint_bigunion_add_fun |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P) - set_additive_card |- SET_ADDITIVE CARD - disjoint_bigunion_card |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - (CARD (BIGUNION P) = SIGMA CARD P) - CARD_BIGUNION_PAIR_DISJOINT |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - CARD (BIGUNION P) = SIGMA CARD P - set_additive_sigma |- !f. SET_ADDITIVE (SIGMA f) - disjoint_bigunion_sigma |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SIGMA f (BIGUNION P) = SIGMA (SIGMA f) P - set_add_fun_by_partition |- !R s. R equiv_on s /\ FINITE s ==> - !f. SET_ADDITIVE f ==> (f s = SIGMA f (partition R s)) - set_card_by_partition |- !R s. R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s)) - set_sigma_by_partition |- !R s. R equiv_on s /\ FINITE s ==> - !f. SIGMA f s = SIGMA (SIGMA f) (partition R s) - disjoint_bigunion_mult_fun |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P) - set_mult_fun_by_partition |- !R s. R equiv_on s /\ FINITE s ==> - !f. SET_MULTIPLICATIVE f ==> (f s = PI f (partition R s)) - sum_image_by_composition |- !s. FINITE s ==> !g. INJ g s univ(:'a) ==> - !f. SIGMA f (IMAGE g s) = SIGMA (f o g) s - sum_image_by_permutation |- !s. FINITE s ==> !g. g PERMUTES s ==> !f. SIGMA (f o g) s = SIGMA f s - sum_image_by_composition_with_partial_inj - |- !s. FINITE s ==> !f. (f {} = 0) ==> - !g. (!t. FINITE t /\ (!x. x IN t ==> g x <> {}) ==> INJ g t univ(:'b -> bool)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s) - sum_image_by_composition_without_inj - |- !s. FINITE s ==> - !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s) - - Pre-image Theorems: - preimage_def |- !f s y. preimage f s y = {x | x IN s /\ (f x = y)} - preimage_element |- !f s x y. x IN preimage f s y <=> x IN s /\ (f x = y) - in_preimage |- !f s x y. x IN preimage f s y <=> x IN s /\ (f x = y) - preimage_subset |- !f s y. preimage f s y SUBSET s - preimage_finite |- !f s y. FINITE s ==> FINITE (preimage f s y) - preimage_property |- !f s y x. x IN preimage f s y ==> (f x = y) - preimage_of_image |- !f s x. x IN s ==> x IN preimage f s (f x) - preimage_choice_property |- !f s y. y IN IMAGE f s ==> - CHOICE (preimage f s y) IN s /\ (f (CHOICE (preimage f s y)) = y) - preimage_inj |- !f s. INJ f s univ(:'b) ==> !x. x IN s ==> (preimage f s (f x) = {x}) - preimage_inj_choice |- !f s. INJ f s univ(:'b) ==> !x. x IN s ==> (CHOICE (preimage f s (f x)) = x) - preimage_image_inj |- !f s. INJ (preimage f s) (IMAGE f s) (POW s) - - Set of Proper Subsets: - IN_PPOW |- !s e. e IN PPOW s ==> e PSUBSET s - FINITE_PPOW |- !s. FINITE s ==> FINITE (PPOW s) - CARD_PPOW |- !s. FINITE s ==> (CARD (PPOW s) = PRE (2 ** CARD s) - CARD_PPOW_EQN |- !s. FINITE s ==> (CARD (PPOW s) = 2 ** CARD s - 1) - - Useful Theorems: - in_prime |- !p. p IN prime <=> prime p - PROD_SET_EUCLID |- !s. FINITE s ==> !p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b - -*) - -(* ------------------------------------------------------------------------- *) -(* Logic Theorems. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: (A <=> B) <=> (A ==> B) /\ ((A ==> B) ==> (B ==> A)) *) -(* Proof: by logic. *) -val EQ_IMP2_THM = store_thm( - "EQ_IMP2_THM", - ``!A B. (A <=> B) <=> (A ==> B) /\ ((A ==> B) ==> (B ==> A))``, - metis_tac[]); - -(* Theorem: (b1 = b2) ==> (f b1 = f b2) *) -(* Proof: by substitution. *) -val BOOL_EQ = store_thm( - "BOOL_EQ", - ``!b1:bool b2:bool f. (b1 = b2) ==> (f b1 = f b2)``, - simp[]); - -(* Theorem: b /\ (c ==> d) ==> ((b ==> c) ==> d) *) -(* Proof: by logical implication. *) -val IMP_IMP = store_thm( - "IMP_IMP", - ``!b c d. b /\ (c ==> d) ==> ((b ==> c) ==> d)``, - metis_tac[]); - -(* Theorem: p /\ q ==> p \/ ~q *) -(* Proof: - Note p /\ q ==> p by AND1_THM - and p ==> p \/ ~q by OR_INTRO_THM1 - Thus p /\ q ==> p \/ ~q -*) -val AND_IMP_OR_NEG = store_thm( - "AND_IMP_OR_NEG", - ``!p q. p /\ q ==> p \/ ~q``, - metis_tac[]); - -(* Theorem: (p \/ q ==> r) ==> (p /\ ~q ==> r) *) -(* Proof: - (p \/ q) ==> r - = ~(p \/ q) \/ r by IMP_DISJ_THM - = (~p /\ ~q) \/ r by DE_MORGAN_THM - ==> (~p \/ q) \/ r by AND_IMP_OR_NEG - = ~(p /\ ~q) \/ r by DE_MORGAN_THM - = (p /\ ~q) ==> r by IMP_DISJ_THM -*) -val OR_IMP_IMP = store_thm( - "OR_IMP_IMP", - ``!p q r. ((p \/ q) ==> r) ==> ((p /\ ~q) ==> r)``, - metis_tac[]); - -(* Theorem: x IN (if b then s else t) <=> if b then x IN s else x IN t *) -(* Proof: by boolTheory.COND_RAND: - |- !f b x y. f (if b then x else y) = if b then f x else f y -*) -val PUSH_IN_INTO_IF = store_thm( - "PUSH_IN_INTO_IF", - ``!b x s t. x IN (if b then s else t) <=> if b then x IN s else x IN t``, - rw_tac std_ss[]); - -(* ------------------------------------------------------------------------- *) -(* Set Theorems. *) -(* ------------------------------------------------------------------------- *) - -(* use of IN_SUBSET *) -val IN_SUBSET = save_thm("IN_SUBSET", SUBSET_DEF); -(* -val IN_SUBSET = |- !s t. s SUBSET t <=> !x. x IN s ==> x IN t: thm -*) - -(* Theorem: DISJOINT (s DIFF t) t /\ DISJOINT t (s DIFF t) *) -(* Proof: - DISJOINT (s DIFF t) t - <=> (s DIFF t) INTER t = {} by DISJOINT_DEF - <=> !x. x IN (s DIFF t) INTER t <=> F by MEMBER_NOT_EMPTY - x IN (s DIFF t) INTER t - <=> x IN (s DIFF t) /\ x IN t by IN_INTER - <=> (x IN s /\ x NOTIN t) /\ x IN t by IN_DIFF - <=> x IN s /\ (x NOTIN t /\ x IN t) - <=> x IN s /\ F - <=> F - Similarly for DISJOINT t (s DIFF t) -*) -val DISJOINT_DIFF = store_thm( - "DISJOINT_DIFF", - ``!s t. (DISJOINT (s DIFF t) t) /\ (DISJOINT t (s DIFF t))``, - (rw[DISJOINT_DEF, EXTENSION] >> metis_tac[])); - -(* Theorem: DISJOINT s t <=> ((s DIFF t) = s) *) -(* Proof: by DISJOINT_DEF, DIFF_DEF, EXTENSION *) -val DISJOINT_DIFF_IFF = store_thm( - "DISJOINT_DIFF_IFF", - ``!s t. DISJOINT s t <=> ((s DIFF t) = s)``, - rw[DISJOINT_DEF, DIFF_DEF, EXTENSION] >> - metis_tac[]); - -(* Theorem: s UNION (t DIFF s) = s UNION t *) -(* Proof: - By EXTENSION, - x IN (s UNION (t DIFF s)) - = x IN s \/ x IN (t DIFF s) by IN_UNION - = x IN s \/ (x IN t /\ x NOTIN s) by IN_DIFF - = (x IN s \/ x IN t) /\ (x IN s \/ x NOTIN s) by LEFT_OR_OVER_AND - = (x IN s \/ x IN t) /\ T by EXCLUDED_MIDDLE - = x IN (s UNION t) by IN_UNION -*) -val UNION_DIFF_EQ_UNION = store_thm( - "UNION_DIFF_EQ_UNION", - ``!s t. s UNION (t DIFF s) = s UNION t``, - rw_tac std_ss[EXTENSION, IN_UNION, IN_DIFF] >> - metis_tac[]); - -(* Theorem: (s INTER (t DIFF s) = {}) /\ ((t DIFF s) INTER s = {}) *) -(* Proof: by DISJOINT_DIFF, GSYM DISJOINT_DEF *) -val INTER_DIFF = store_thm( - "INTER_DIFF", - ``!s t. (s INTER (t DIFF s) = {}) /\ ((t DIFF s) INTER s = {})``, - rw[DISJOINT_DIFF, GSYM DISJOINT_DEF]); - -(* Theorem: {x} SUBSET s /\ SING s <=> (s = {x}) *) -(* Proof: - Note {x} SUBSET s ==> x IN s by SUBSET_DEF - and SING s ==> ?y. s = {y} by SING_DEF - Thus x IN {y} ==> x = y by IN_SING -*) -val SUBSET_SING = store_thm( - "SUBSET_SING", - ``!s x. {x} SUBSET s /\ SING s <=> (s = {x})``, - metis_tac[SING_DEF, IN_SING, SUBSET_DEF]); - -(* Theorem: !x. x IN s ==> s INTER {x} = {x} *) -(* Proof: - s INTER {x} - = {x | x IN s /\ x IN {x}} by INTER_DEF - = {x' | x' IN s /\ x' = x} by IN_SING - = {x} by EXTENSION -*) -val INTER_SING = store_thm( - "INTER_SING", - ``!s x. x IN s ==> (s INTER {x} = {x})``, - rw[INTER_DEF, EXTENSION, EQ_IMP_THM]); - -(* Theorem: A non-empty set with all elements equal to a is the singleton {a} *) -(* Proof: by singleton definition. *) -val ONE_ELEMENT_SING = store_thm( - "ONE_ELEMENT_SING", - ``!s a. s <> {} /\ (!k. k IN s ==> (k = a)) ==> (s = {a})``, - rw[EXTENSION, EQ_IMP_THM] >> - metis_tac[]); - -(* Theorem: s <> {} ==> (SING s <=> !x y. x IN s /\ y IN s ==> (x = y)) *) -(* Proof: - If part: SING s ==> !x y. x IN s /\ y IN s ==> (x = y)) - SING s ==> ?t. s = {t} by SING_DEF - x IN s ==> x = t by IN_SING - y IN s ==> y = t by IN_SING - Hence x = y - Only-if part: !x y. x IN s /\ y IN s ==> (x = y)) ==> SING s - True by ONE_ELEMENT_SING -*) -val SING_ONE_ELEMENT = store_thm( - "SING_ONE_ELEMENT", - ``!s. s <> {} ==> (SING s <=> !x y. x IN s /\ y IN s ==> (x = y))``, - metis_tac[SING_DEF, IN_SING, ONE_ELEMENT_SING]); - -(* Theorem: SING s ==> (!x y. x IN s /\ y IN s ==> (x = y)) *) -(* Proof: - Note SING s <=> ?z. s = {z} by SING_DEF - and x IN {z} <=> x = z by IN_SING - and y IN {z} <=> y = z by IN_SING - Thus x = y -*) -val SING_ELEMENT = store_thm( - "SING_ELEMENT", - ``!s. SING s ==> (!x y. x IN s /\ y IN s ==> (x = y))``, - metis_tac[SING_DEF, IN_SING]); -(* Note: the converse really needs s <> {} *) - -(* Theorem: SING s <=> s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y)) *) -(* Proof: - If part: SING s ==> s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y)) - True by SING_EMPTY, SING_ELEMENT. - Only-if part: s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y)) ==> SING s - True by SING_ONE_ELEMENT. -*) -val SING_TEST = store_thm( - "SING_TEST", - ``!s. SING s <=> s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y))``, - metis_tac[SING_EMPTY, SING_ELEMENT, SING_ONE_ELEMENT]); - -(* Theorem: x IN (if b then {y} else {}) ==> (x = y) *) -(* Proof: by IN_SING, MEMBER_NOT_EMPTY *) -val IN_SING_OR_EMPTY = store_thm( - "IN_SING_OR_EMPTY", - ``!b x y. x IN (if b then {y} else {}) ==> (x = y)``, - rw[]); - -(* Theorem: {x} INTER s = if x IN s then {x} else {} *) -(* Proof: by EXTENSION *) -val SING_INTER = store_thm( - "SING_INTER", - ``!s x. {x} INTER s = if x IN s then {x} else {}``, - rw[EXTENSION] >> - metis_tac[]); - -(* Theorem: SING s ==> (CARD s = 1) *) -(* Proof: - Note s = {x} for some x by SING_DEF - so CARD s = 1 by CARD_SING -*) -Theorem SING_CARD_1: - !s. SING s ==> (CARD s = 1) -Proof - metis_tac[SING_DEF, CARD_SING] -QED - -(* Note: SING s <=> (CARD s = 1) cannot be proved. -Only SING_IFF_CARD1 |- !s. SING s <=> (CARD s = 1) /\ FINITE s -That is: FINITE s /\ (CARD s = 1) ==> SING s -*) - -(* Theorem: FINITE s ==> ((CARD s = 1) <=> SING s) *) -(* Proof: - If part: CARD s = 1 ==> SING s - Since CARD s = 1 - ==> s <> {} by CARD_EMPTY - ==> ?x. x IN s by MEMBER_NOT_EMPTY - Claim: !y . y IN s ==> y = x - Proof: By contradiction, suppose y <> x. - Then y NOTIN {x} by EXTENSION - so CARD {y; x} = 2 by CARD_DEF - and {y; x} SUBSET s by SUBSET_DEF - thus CARD {y; x} <= CARD s by CARD_SUBSET - This contradicts CARD s = 1. - Hence SING s by SING_ONE_ELEMENT (or EXTENSION, SING_DEF) - Or, - With x IN s, {x} SUBSET s by SUBSET_DEF - If s <> {x}, then {x} PSUBSET s by PSUBSET_DEF - so CARD {x} < CARD s by CARD_PSUBSET - But CARD {x} = 1 by CARD_SING - and this contradicts CARD s = 1. - - Only-if part: SING s ==> CARD s = 1 - Since SING s - <=> ?x. s = {x} by SING_DEF - ==> CARD {x} = 1 by CARD_SING -*) -val CARD_EQ_1 = store_thm( - "CARD_EQ_1", - ``!s. FINITE s ==> ((CARD s = 1) <=> SING s)``, - rw[SING_DEF, EQ_IMP_THM] >| [ - `1 <> 0` by decide_tac >> - `s <> {} /\ ?x. x IN s` by metis_tac[CARD_EMPTY, MEMBER_NOT_EMPTY] >> - qexists_tac `x` >> - spose_not_then strip_assume_tac >> - `{x} PSUBSET s` by rw[PSUBSET_DEF] >> - `CARD {x} < CARD s` by rw[CARD_PSUBSET] >> - `CARD {x} = 1` by rw[CARD_SING] >> - decide_tac, - rw[CARD_SING] - ]); - -(* Theorem: x <> y ==> ((x INSERT s) DELETE y = x INSERT (s DELETE y)) *) -(* Proof: - z IN (x INSERT s) DELETE y - <=> z IN (x INSERT s) /\ z <> y by IN_DELETE - <=> (z = x \/ z IN s) /\ z <> y by IN_INSERT - <=> (z = x /\ z <> y) \/ (z IN s /\ z <> y) by RIGHT_AND_OVER_OR - <=> (z = x) \/ (z IN s /\ z <> y) by x <> y - <=> (z = x) \/ (z IN DELETE y) by IN_DELETE - <=> z IN x INSERT (s DELETE y) by IN_INSERT -*) -val INSERT_DELETE_COMM = store_thm( - "INSERT_DELETE_COMM", - ``!s x y. x <> y ==> ((x INSERT s) DELETE y = x INSERT (s DELETE y))``, - (rw[EXTENSION] >> metis_tac[])); - -(* Theorem: x NOTIN s ==> (x INSERT s) DELETE x = s *) -(* Proof: - (x INSERT s) DELETE x - = s DELETE x by DELETE_INSERT - = s by DELETE_NON_ELEMENT -*) -Theorem INSERT_DELETE_NON_ELEMENT: - !x s. x NOTIN s ==> (x INSERT s) DELETE x = s -Proof - simp[DELETE_INSERT, DELETE_NON_ELEMENT] -QED - -(* Theorem: s SUBSET u ==> (s INTER t) SUBSET u *) -(* Proof: - Note (s INTER t) SUBSET s by INTER_SUBSET - ==> (s INTER t) SUBSET u by SUBSET_TRANS -*) -val SUBSET_INTER_SUBSET = store_thm( - "SUBSET_INTER_SUBSET", - ``!s t u. s SUBSET u ==> (s INTER t) SUBSET u``, - metis_tac[INTER_SUBSET, SUBSET_TRANS]); - -(* Theorem: s DIFF (s DIFF t) = s INTER t *) -(* Proof: by IN_DIFF, IN_INTER *) -val DIFF_DIFF_EQ_INTER = store_thm( - "DIFF_DIFF_EQ_INTER", - ``!s t. s DIFF (s DIFF t) = s INTER t``, - rw[EXTENSION] >> - metis_tac[]); - -(* Theorem: (s = t) <=> (s SUBSET t /\ (t DIFF s = {})) *) -(* Proof: - s = t - <=> s SUBSET t /\ t SUBSET s by SET_EQ_SUBSET - <=> s SUBSET t /\ (t DIFF s = {}) by SUBSET_DIFF_EMPTY -*) -val SET_EQ_BY_DIFF = store_thm( - "SET_EQ_BY_DIFF", - ``!s t. (s = t) <=> (s SUBSET t /\ (t DIFF s = {}))``, - rw[SET_EQ_SUBSET, SUBSET_DIFF_EMPTY]); - -(* in pred_setTheory: -SUBSET_DELETE_BOTH |- !s1 s2 x. s1 SUBSET s2 ==> s1 DELETE x SUBSET s2 DELETE x -*) - -(* Theorem: s1 SUBSET s2 ==> x INSERT s1 SUBSET x INSERT s2 *) -(* Proof: by SUBSET_DEF *) -Theorem SUBSET_INSERT_BOTH: - !s1 s2 x. s1 SUBSET s2 ==> x INSERT s1 SUBSET x INSERT s2 -Proof - simp[SUBSET_DEF] -QED - -(* Theorem: x NOTIN s /\ (x INSERT s) SUBSET t ==> s SUBSET (t DELETE x) *) -(* Proof: by SUBSET_DEF *) -val INSERT_SUBSET_SUBSET = store_thm( - "INSERT_SUBSET_SUBSET", - ``!s t x. x NOTIN s /\ (x INSERT s) SUBSET t ==> s SUBSET (t DELETE x)``, - rw[SUBSET_DEF]); - -(* DIFF_INSERT |- !s t x. s DIFF (x INSERT t) = s DELETE x DIFF t *) - -(* Theorem: (s DIFF t) DELETE x = s DIFF (x INSERT t) *) -(* Proof: by EXTENSION *) -val DIFF_DELETE = store_thm( - "DIFF_DELETE", - ``!s t x. (s DIFF t) DELETE x = s DIFF (x INSERT t)``, - (rw[EXTENSION] >> metis_tac[])); - -(* Theorem: FINITE a /\ b SUBSET a ==> (CARD (a DIFF b) = CARD a - CARD b) *) -(* Proof: - Note FINITE b by SUBSET_FINITE - so a INTER b = b by SUBSET_INTER2 - CARD (a DIFF b) - = CARD a - CARD (a INTER b) by CARD_DIFF - = CARD a - CARD b by above -*) -Theorem SUBSET_DIFF_CARD: - !a b. FINITE a /\ b SUBSET a ==> (CARD (a DIFF b) = CARD a - CARD b) -Proof - metis_tac[CARD_DIFF, SUBSET_FINITE, SUBSET_INTER2] -QED - -(* Theorem: s SUBSET {x} <=> ((s = {}) \/ (s = {x})) *) -(* Proof: - Note !y. y IN s ==> y = x by SUBSET_DEF, IN_SING - If s = {}, then trivially true. - If s <> {}, - then ?y. y IN s by MEMBER_NOT_EMPTY, s <> {} - so y = x by above - ==> s = {x} by EXTENSION -*) -Theorem SUBSET_SING_IFF: - !s x. s SUBSET {x} <=> ((s = {}) \/ (s = {x})) -Proof - rw[SUBSET_DEF, EXTENSION] >> - metis_tac[] -QED - -(* Theorem: FINITE t /\ s SUBSET t ==> (CARD s = CARD t <=> s = t) *) -(* Proof: - If part: CARD s = CARD t ==> s = t - By contradiction, suppose s <> t. - Then s PSUBSET t by PSUBSET_DEF - so CARD s < CARD t by CARD_PSUBSET, FINITE t - This contradicts CARD s = CARD t. - Only-if part is trivial. -*) -Theorem SUBSET_CARD_EQ: - !s t. FINITE t /\ s SUBSET t ==> (CARD s = CARD t <=> s = t) -Proof - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `s PSUBSET t` by rw[PSUBSET_DEF] >> - `CARD s < CARD t` by rw[CARD_PSUBSET] >> - decide_tac -QED - -(* Theorem: (!x. x IN s ==> f x IN t) <=> (IMAGE f s) SUBSET t *) -(* Proof: - If part: (!x. x IN s ==> f x IN t) ==> (IMAGE f s) SUBSET t - y IN (IMAGE f s) - ==> ?x. (y = f x) /\ x IN s by IN_IMAGE - ==> f x = y IN t by given - hence (IMAGE f s) SUBSET t by SUBSET_DEF - Only-if part: (IMAGE f s) SUBSET t ==> (!x. x IN s ==> f x IN t) - x IN s - ==> f x IN (IMAGE f s) by IN_IMAGE - ==> f x IN t by SUBSET_DEF -*) -val IMAGE_SUBSET_TARGET = store_thm( - "IMAGE_SUBSET_TARGET", - ``!f s t. (!x. x IN s ==> f x IN t) <=> (IMAGE f s) SUBSET t``, - metis_tac[IN_IMAGE, SUBSET_DEF]); - -(* Theorem: SURJ f s t ==> CARD (IMAGE f s) = CARD t *) -(* Proof: - Note IMAGE f s = t by IMAGE_SURJ - Thus CARD (IMAGE f s) = CARD t by above -*) -Theorem SURJ_CARD_IMAGE: - !f s t. SURJ f s t ==> CARD (IMAGE f s) = CARD t -Proof - simp[IMAGE_SURJ] -QED - -(* ------------------------------------------------------------------------- *) -(* Image and Bijection *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: (!x. x IN s ==> (f x = g x)) ==> (INJ f s t <=> INJ g s t) *) -(* Proof: by INJ_DEF *) -val INJ_CONG = store_thm( - "INJ_CONG", - ``!f g s t. (!x. x IN s ==> (f x = g x)) ==> (INJ f s t <=> INJ g s t)``, - rw[INJ_DEF]); - -(* Theorem: (!x. x IN s ==> (f x = g x)) ==> (SURJ f s t <=> SURJ g s t) *) -(* Proof: by SURJ_DEF *) -val SURJ_CONG = store_thm( - "SURJ_CONG", - ``!f g s t. (!x. x IN s ==> (f x = g x)) ==> (SURJ f s t <=> SURJ g s t)``, - rw[SURJ_DEF] >> - metis_tac[]); - -(* Theorem: (!x. x IN s ==> (f x = g x)) ==> (BIJ f s t <=> BIJ g s t) *) -(* Proof: by BIJ_DEF, INJ_CONG, SURJ_CONG *) -val BIJ_CONG = store_thm( - "BIJ_CONG", - ``!f g s t. (!x. x IN s ==> (f x = g x)) ==> (BIJ f s t <=> BIJ g s t)``, - rw[BIJ_DEF] >> - metis_tac[INJ_CONG, SURJ_CONG]); - -(* -BIJ_LINV_BIJ |- !f s t. BIJ f s t ==> BIJ (LINV f s) t s -Cannot prove |- !f s t. BIJ f s t <=> BIJ (LINV f s) t s -because LINV f s depends on f! -*) - -(* Theorem: INJ f s t /\ x IN s ==> f x IN t *) -(* Proof: by INJ_DEF *) -val INJ_ELEMENT = store_thm( - "INJ_ELEMENT", - ``!f s t x. INJ f s t /\ x IN s ==> f x IN t``, - rw_tac std_ss[INJ_DEF]); - -(* Theorem: SURJ f s t /\ x IN s ==> f x IN t *) -(* Proof: by SURJ_DEF *) -val SURJ_ELEMENT = store_thm( - "SURJ_ELEMENT", - ``!f s t x. SURJ f s t /\ x IN s ==> f x IN t``, - rw_tac std_ss[SURJ_DEF]); - -(* Theorem: BIJ f s t /\ x IN s ==> f x IN t *) -(* Proof: by BIJ_DEF *) -val BIJ_ELEMENT = store_thm( - "BIJ_ELEMENT", - ``!f s t x. BIJ f s t /\ x IN s ==> f x IN t``, - rw_tac std_ss[BIJ_DEF, INJ_DEF]); - -(* Theorem: INJ f s t ==> INJ f s UNIV *) -(* Proof: - Note s SUBSET s by SUBSET_REFL - and t SUBSET univ(:'b) by SUBSET_UNIV - so INJ f s t ==> INJ f s univ(:'b) by INJ_SUBSET -*) -val INJ_UNIV = store_thm( - "INJ_UNIV", - ``!f s t. INJ f s t ==> INJ f s UNIV``, - metis_tac[INJ_SUBSET, SUBSET_REFL, SUBSET_UNIV]); - -(* Theorem: INJ f UNIV UNIV ==> INJ f s UNIV *) -(* Proof: - Note s SUBSET univ(:'a) by SUBSET_UNIV - and univ(:'b) SUBSET univ('b) by SUBSET_REFL - so INJ f univ(:'a) univ(:'b) ==> INJ f s univ(:'b) by INJ_SUBSET -*) -val INJ_SUBSET_UNIV = store_thm( - "INJ_SUBSET_UNIV", - ``!(f:'a -> 'b) (s:'a -> bool). INJ f UNIV UNIV ==> INJ f s UNIV``, - metis_tac[INJ_SUBSET, SUBSET_UNIV, SUBSET_REFL]); - -(* Theorem: INJ f s UNIV ==> BIJ f s (IMAGE f s) *) -(* Proof: by definitions. *) -val INJ_IMAGE_BIJ_ALT = store_thm( - "INJ_IMAGE_BIJ_ALT", - ``!f s. INJ f s UNIV ==> BIJ f s (IMAGE f s)``, - rw[BIJ_DEF, INJ_DEF, SURJ_DEF]); - -(* Theorem: INJ f P univ(:'b) ==> - !s t. s SUBSET P /\ t SUBSET P ==> ((IMAGE f s = IMAGE f t) <=> (s = t)) *) -(* Proof: - If part: IMAGE f s = IMAGE f t ==> s = t - Claim: s SUBSET t - Proof: by SUBSET_DEF, this is to show: x IN s ==> x IN t - x IN s - ==> f x IN (IMAGE f s) by INJ_DEF, IN_IMAGE - or f x IN (IMAGE f t) by given - ==> ?x'. x' IN t /\ (f x' = f x) by IN_IMAGE - But x IN P /\ x' IN P by SUBSET_DEF - Thus f x' = f x ==> x' = x by INJ_DEF - - Claim: t SUBSET s - Proof: similar to above by INJ_DEF, IN_IMAGE, SUBSET_DEF - - Hence s = t by SUBSET_ANTISYM - - Only-if part: s = t ==> IMAGE f s = IMAGE f t - This is trivially true. -*) -val INJ_IMAGE_EQ = store_thm( - "INJ_IMAGE_EQ", - ``!P f. INJ f P univ(:'b) ==> - !s t. s SUBSET P /\ t SUBSET P ==> ((IMAGE f s = IMAGE f t) <=> (s = t))``, - rw[EQ_IMP_THM] >> - (irule SUBSET_ANTISYM >> rpt conj_tac) >| [ - rw[SUBSET_DEF] >> - `?x'. x' IN t /\ (f x' = f x)` by metis_tac[IMAGE_IN, IN_IMAGE] >> - metis_tac[INJ_DEF, SUBSET_DEF], - rw[SUBSET_DEF] >> - `?x'. x' IN s /\ (f x' = f x)` by metis_tac[IMAGE_IN, IN_IMAGE] >> - metis_tac[INJ_DEF, SUBSET_DEF] - ]); - -(* Note: pred_setTheory.IMAGE_INTER -|- !f s t. IMAGE f (s INTER t) SUBSET IMAGE f s INTER IMAGE f t -*) - -(* Theorem: INJ f P univ(:'b) ==> - !s t. s SUBSET P /\ t SUBSET P ==> (IMAGE f (s INTER t) = (IMAGE f s) INTER (IMAGE f t)) *) -(* Proof: by EXTENSION, INJ_DEF, SUBSET_DEF *) -val INJ_IMAGE_INTER = store_thm( - "INJ_IMAGE_INTER", - ``!P f. INJ f P univ(:'b) ==> - !s t. s SUBSET P /\ t SUBSET P ==> (IMAGE f (s INTER t) = (IMAGE f s) INTER (IMAGE f t))``, - rw[EXTENSION] >> - metis_tac[INJ_DEF, SUBSET_DEF]); - -(* tobe: helperSet, change P to P *) - -(* Theorem: INJ f P univ(:'b) ==> - !s t. s SUBSET P /\ t SUBSET P ==> (DISJOINT s t <=> DISJOINT (IMAGE f s) (IMAGE f t)) *) -(* Proof: - DISJOINT (IMAGE f s) (IMAGE f t) - <=> (IMAGE f s) INTER (IMAGE f t) = {} by DISJOINT_DEF - <=> IMAGE f (s INTER t) = {} by INJ_IMAGE_INTER, INJ f P univ(:'b) - <=> s INTER t = {} by IMAGE_EQ_EMPTY - <=> DISJOINT s t by DISJOINT_DEF -*) -val INJ_IMAGE_DISJOINT = store_thm( - "INJ_IMAGE_DISJOINT", - ``!P f. INJ f P univ(:'b) ==> - !s t. s SUBSET P /\ t SUBSET P ==> (DISJOINT s t <=> DISJOINT (IMAGE f s) (IMAGE f t))``, - metis_tac[DISJOINT_DEF, INJ_IMAGE_INTER, IMAGE_EQ_EMPTY]); - -(* Theorem: INJ I s univ(:'a) *) -(* Proof: - Note !x. I x = x by I_THM - so !x. x IN s ==> I x IN univ(:'a) by IN_UNIV - and !x y. x IN s /\ y IN s ==> (I x = I y) ==> (x = y) by above - Hence INJ I s univ(:'b) by INJ_DEF -*) -val INJ_I = store_thm( - "INJ_I", - ``!s:'a -> bool. INJ I s univ(:'a)``, - rw[INJ_DEF]); - -(* Theorem: INJ I (IMAGE f s) univ(:'b) *) -(* Proof: - Since !x. x IN (IMAGE f s) ==> x IN univ(:'b) by IN_UNIV - and !x y. x IN (IMAGE f s) /\ y IN (IMAGE f s) ==> - (I x = I y) ==> (x = y) by I_THM - Hence INJ I (IMAGE f s) univ(:'b) by INJ_DEF -*) -val INJ_I_IMAGE = store_thm( - "INJ_I_IMAGE", - ``!s f. INJ I (IMAGE f s) univ(:'b)``, - rw[INJ_DEF]); - -(* Theorem: BIJ f s t <=> (!x. x IN s ==> f x IN t) /\ (!y. y IN t ==> ?!x. x IN s /\ (f x = y)) *) -(* Proof: - This is to prove: - (1) y IN t ==> ?!x. x IN s /\ (f x = y) - x exists by SURJ_DEF, and x is unique by INJ_DEF. - (2) x IN s /\ y IN s /\ f x = f y ==> x = y - true by INJ_DEF. - (3) x IN t ==> ?y. y IN s /\ (f y = x) - true by SURJ_DEF. -*) -val BIJ_THM = store_thm( - "BIJ_THM", - ``!f s t. BIJ f s t <=> (!x. x IN s ==> f x IN t) /\ (!y. y IN t ==> ?!x. x IN s /\ (f x = y))``, - rw_tac std_ss [BIJ_DEF, INJ_DEF, SURJ_DEF, EQ_IMP_THM] >> metis_tac[]); - -(* Theorem: BIJ f s t ==> !x y. x IN s /\ y IN s /\ (f x = f y) ==> (x = y) *) -(* Proof: by BIJ_DEF, INJ_DEF *) -Theorem BIJ_IS_INJ: - !f s t. BIJ f s t ==> !x y. x IN s /\ y IN s /\ (f x = f y) ==> (x = y) -Proof - rw[BIJ_DEF, INJ_DEF] -QED - -(* Theorem: BIJ f s t ==> !x. x IN t ==> ?y. y IN s /\ f y = x *) -(* Proof: by BIJ_DEF, SURJ_DEF. *) -Theorem BIJ_IS_SURJ: - !f s t. BIJ f s t ==> !x. x IN t ==> ?y. y IN s /\ f y = x -Proof - simp[BIJ_DEF, SURJ_DEF] -QED - -(* Can remove in helperSet: FINITE_BIJ_PROPERTY -|- !f s t. FINITE s /\ BIJ f s t ==> FINITE t /\ CARD s = CARD t -pred_setTheory.FINITE_BIJ -|- !f s t. FINITE s /\ BIJ f s t ==> FINITE t /\ CARD s = CARD t -*) - -(* Idea: improve FINITE_BIJ with iff of finiteness of s and t. *) - -(* Theorem: BIJ f s t ==> (FINITE s <=> FINITE t) *) -(* Proof: - If part: FINITE s ==> FINITE t - This is true by FINITE_BIJ - Only-if part: FINITE t ==> FINITE s - Note BIJ (LINV f s) t s by BIJ_LINV_BIJ - Thus FINITE s by FINITE_BIJ -*) -Theorem BIJ_FINITE_IFF: - !f s t. BIJ f s t ==> (FINITE s <=> FINITE t) -Proof - metis_tac[FINITE_BIJ, BIJ_LINV_BIJ] -QED - -(* Theorem: INJ f s s /\ x IN s /\ y IN s ==> ((f x = f y) <=> (x = y)) *) -(* Proof: by INJ_DEF *) -Theorem INJ_EQ_11: - !f s x y. INJ f s s /\ x IN s /\ y IN s ==> ((f x = f y) <=> (x = y)) -Proof - metis_tac[INJ_DEF] -QED - -(* Theorem: INJ f univ(:'a) univ(:'b) ==> !x y. f x = f y <=> x = y *) -(* Proof: by INJ_DEF, IN_UNIV. *) -Theorem INJ_IMP_11: - !f. INJ f univ(:'a) univ(:'b) ==> !x y. f x = f y <=> x = y -Proof - metis_tac[INJ_DEF, IN_UNIV] -QED -(* This is better than INJ_EQ_11 above. *) - -(* Theorem: BIJ I s s *) -(* Proof: by definitions. *) -val BIJ_I_SAME = store_thm( - "BIJ_I_SAME", - ``!s. BIJ I s s``, - rw[BIJ_DEF, INJ_DEF, SURJ_DEF]); - -(* Theorem: s <> {} ==> !e. IMAGE (K e) s = {e} *) -(* Proof: - IMAGE (K e) s - <=> {(K e) x | x IN s} by IMAGE_DEF - <=> {e | x IN s} by K_THM - <=> {e} by EXTENSION, if s <> {} -*) -val IMAGE_K = store_thm( - "IMAGE_K", - ``!s. s <> {} ==> !e. IMAGE (K e) s = {e}``, - rw[EXTENSION, EQ_IMP_THM]); - -(* Theorem: (!x y. (f x = f y) ==> (x = y)) ==> (!s e. e IN s <=> f e IN IMAGE f s) *) -(* Proof: - If part: e IN s ==> f e IN IMAGE f s - True by IMAGE_IN. - Only-if part: f e IN IMAGE f s ==> e IN s - ?x. (f e = f x) /\ x IN s by IN_IMAGE - f e = f x ==> e = x by given implication - Hence x IN s -*) -val IMAGE_ELEMENT_CONDITION = store_thm( - "IMAGE_ELEMENT_CONDITION", - ``!f:'a -> 'b. (!x y. (f x = f y) ==> (x = y)) ==> (!s e. e IN s <=> f e IN IMAGE f s)``, - rw[EQ_IMP_THM] >> - metis_tac[]); - -(* Theorem: BIGUNION (IMAGE (\x. {x}) s) = s *) -(* Proof: - z IN BIGUNION (IMAGE (\x. {x}) s) - <=> ?t. z IN t /\ t IN (IMAGE (\x. {x}) s) by IN_BIGUNION - <=> ?t. z IN t /\ (?y. y IN s /\ (t = {y})) by IN_IMAGE - <=> z IN {z} /\ (?y. y IN s /\ {z} = {y}) by picking t = {z} - <=> T /\ z IN s by picking y = z, IN_SING - Hence BIGUNION (IMAGE (\x. {x}) s) = s by EXTENSION -*) -val BIGUNION_ELEMENTS_SING = store_thm( - "BIGUNION_ELEMENTS_SING", - ``!s. BIGUNION (IMAGE (\x. {x}) s) = s``, - rw[EXTENSION, EQ_IMP_THM] >- - metis_tac[] >> - qexists_tac `{x}` >> - metis_tac[IN_SING]); - -(* Theorem: s SUBSET t /\ INJ f t UNIV ==> (IMAGE f (t DIFF s) = (IMAGE f t) DIFF (IMAGE f s)) *) -(* Proof: by SUBSET_DEF, INJ_DEF, EXTENSION, IN_IMAGE, IN_DIFF *) -Theorem IMAGE_DIFF: - !s t f. s SUBSET t /\ INJ f t UNIV ==> (IMAGE f (t DIFF s) = (IMAGE f t) DIFF (IMAGE f s)) -Proof - rw[SUBSET_DEF, INJ_DEF, EXTENSION] >> - metis_tac[] -QED - -(* ------------------------------------------------------------------------- *) -(* More Theorems and Sets for Counting *) -(* ------------------------------------------------------------------------- *) - -(* Have simple (count n) *) - -(* Theorem: count 1 = {0} *) -(* Proof: rename COUNT_ZERO *) -val COUNT_0 = save_thm("COUNT_0", COUNT_ZERO); -(* val COUNT_0 = |- count 0 = {}: thm *) - -(* Theorem: count 1 = {0} *) -(* Proof: by count_def, EXTENSION *) -val COUNT_1 = store_thm( - "COUNT_1", - ``count 1 = {0}``, - rw[count_def, EXTENSION]); - -(* Theorem: n NOTIN (count n) *) -(* Proof: by IN_COUNT *) -val COUNT_NOT_SELF = store_thm( - "COUNT_NOT_SELF", - ``!n. n NOTIN (count n)``, - rw[]); - -(* Theorem: (count n = {}) <=> (n = 0) *) -(* Proof: - Since FINITE (count n) by FINITE_COUNT - and CARD (count n) = n by CARD_COUNT - so count n = {} <=> n = 0 by CARD_EQ_0 -*) -val COUNT_EQ_EMPTY = store_thm( - "COUNT_EQ_EMPTY", - ``!n. (count n = {}) <=> (n = 0)``, - metis_tac[FINITE_COUNT, CARD_COUNT, CARD_EQ_0]); - -(* Theorem: m <= n ==> count m SUBSET count n *) -(* Proof: by LENGTH_TAKE_EQ *) -val COUNT_SUBSET = store_thm( - "COUNT_SUBSET", - ``!m n. m <= n ==> count m SUBSET count n``, - rw[SUBSET_DEF]); - -(* Theorem: count (SUC n) SUBSET t <=> count n SUBSET t /\ n IN t *) -(* Proof: - count (SUC n) SUBSET t - <=> (n INSERT count n) SUBSET t by COUNT_SUC - <=> n IN t /\ (count n) SUBSET t by INSERT_SUBSET - <=> (count n) SUBSET t /\ n IN t by CONJ_COMM -*) -val COUNT_SUC_SUBSET = store_thm( - "COUNT_SUC_SUBSET", - ``!n t. count (SUC n) SUBSET t <=> count n SUBSET t /\ n IN t``, - metis_tac[COUNT_SUC, INSERT_SUBSET]); - -(* Theorem: t DIFF (count (SUC n)) = t DIFF (count n) DELETE n *) -(* Proof: - t DIFF (count (SUC n)) - = t DIFF (n INSERT count n) by COUNT_SUC - = t DIFF (count n) DELETE n by EXTENSION -*) -val DIFF_COUNT_SUC = store_thm( - "DIFF_COUNT_SUC", - ``!n t. t DIFF (count (SUC n)) = t DIFF (count n) DELETE n``, - rw[EXTENSION, EQ_IMP_THM]); - -(* COUNT_SUC |- !n. count (SUC n) = n INSERT count n *) - -(* Theorem: count (SUC n) = 0 INSERT (IMAGE SUC (count n)) *) -(* Proof: by EXTENSION *) -val COUNT_SUC_BY_SUC = store_thm( - "COUNT_SUC_BY_SUC", - ``!n. count (SUC n) = 0 INSERT (IMAGE SUC (count n))``, - rw[EXTENSION, EQ_IMP_THM] >> - (Cases_on `x` >> simp[])); - -(* Theorem: IMAGE f (count (SUC n)) = (f n) INSERT IMAGE f (count n) *) -(* Proof: - IMAGE f (count (SUC n)) - = IMAGE f (n INSERT (count n)) by COUNT_SUC - = f n INSERT IMAGE f (count n) by IMAGE_INSERT -*) -val IMAGE_COUNT_SUC = store_thm( - "IMAGE_COUNT_SUC", - ``!f n. IMAGE f (count (SUC n)) = (f n) INSERT IMAGE f (count n)``, - rw[COUNT_SUC]); - -(* Theorem: IMAGE f (count (SUC n)) = (f 0) INSERT IMAGE (f o SUC) (count n) *) -(* Proof: - IMAGE f (count (SUC n)) - = IMAGE f (0 INSERT (IMAGE SUC (count n))) by COUNT_SUC_BY_SUC - = f 0 INSERT IMAGE f (IMAGE SUC (count n)) by IMAGE_INSERT - = f 0 INSERT IMAGE (f o SUC) (count n) by IMAGE_COMPOSE -*) -val IMAGE_COUNT_SUC_BY_SUC = store_thm( - "IMAGE_COUNT_SUC_BY_SUC", - ``!f n. IMAGE f (count (SUC n)) = (f 0) INSERT IMAGE (f o SUC) (count n)``, - rw[COUNT_SUC_BY_SUC, IMAGE_COMPOSE]); - -(* Introduce countFrom m n, the set {m, m + 1, m + 2, ...., m + n - 1} *) -val _ = overload_on("countFrom", ``\m n. IMAGE ($+ m) (count n)``); - -(* Theorem: countFrom m 0 = {} *) -(* Proof: - countFrom m 0 - = IMAGE ($+ m) (count 0) by notation - = IMAGE ($+ m) {} by COUNT_0 - = {} by IMAGE_EMPTY -*) -val countFrom_0 = store_thm( - "countFrom_0", - ``!m. countFrom m 0 = {}``, - rw[]); - -(* Theorem: countFrom m (SUC n) = m INSERT countFrom (m + 1) n *) -(* Proof: by IMAGE_COUNT_SUC_BY_SUC *) -val countFrom_SUC = store_thm( - "countFrom_SUC", - ``!m n. !m n. countFrom m (SUC n) = m INSERT countFrom (m + 1) n``, - rpt strip_tac >> - `$+ m o SUC = $+ (m + 1)` by rw[FUN_EQ_THM] >> - rw[IMAGE_COUNT_SUC_BY_SUC]); - -(* Theorem: 0 < n ==> m IN countFrom m n *) -(* Proof: by IN_COUNT *) -val countFrom_first = store_thm( - "countFrom_first", - ``!m n. 0 < n ==> m IN countFrom m n``, - rw[] >> - metis_tac[ADD_0]); - -(* Theorem: x < m ==> x NOTIN countFrom m n *) -(* Proof: by IN_COUNT *) -val countFrom_less = store_thm( - "countFrom_less", - ``!m n x. x < m ==> x NOTIN countFrom m n``, - rw[]); - -(* Theorem: count n = countFrom 0 n *) -(* Proof: by EXTENSION *) -val count_by_countFrom = store_thm( - "count_by_countFrom", - ``!n. count n = countFrom 0 n``, - rw[EXTENSION]); - -(* Theorem: count (SUC n) = 0 INSERT countFrom 1 n *) -(* Proof: - count (SUC n) - = 0 INSERT IMAGE SUC (count n) by COUNT_SUC_BY_SUC - = 0 INSERT IMAGE ($+ 1) (count n) by FUN_EQ_THM - = 0 INSERT countFrom 1 n by notation -*) -val count_SUC_by_countFrom = store_thm( - "count_SUC_by_countFrom", - ``!n. count (SUC n) = 0 INSERT countFrom 1 n``, - rpt strip_tac >> - `SUC = $+ 1` by rw[FUN_EQ_THM] >> - rw[COUNT_SUC_BY_SUC]); - -(* Inclusion-Exclusion for two sets: - -CARD_UNION -|- !s. FINITE s ==> !t. FINITE t ==> - (CARD (s UNION t) + CARD (s INTER t) = CARD s + CARD t) -CARD_UNION_EQN -|- !s t. FINITE s /\ FINITE t ==> - (CARD (s UNION t) = CARD s + CARD t - CARD (s INTER t)) -CARD_UNION_DISJOINT -|- !s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> - (CARD (s UNION t) = CARD s + CARD t) -*) - -(* Inclusion-Exclusion for three sets. *) - -(* Theorem: FINITE a /\ FINITE b /\ FINITE c ==> - (CARD (a UNION b UNION c) = - CARD a + CARD b + CARD c + CARD (a INTER b INTER c) - - CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c)) *) -(* Proof: - Note FINITE (a UNION b) by FINITE_UNION - and FINITE (a INTER c) by FINITE_INTER - and FINITE (b INTER c) by FINITE_INTER - Also (a INTER c) INTER (b INTER c) - = a INTER b INTER c by EXTENSION - and CARD (a INTER b) <= CARD a by CARD_INTER_LESS_EQ - and CARD (a INTER b INTER c) <= CARD (b INTER c) by CARD_INTER_LESS_EQ, INTER_COMM - - CARD (a UNION b UNION c) - = CARD (a UNION b) + CARD c - CARD ((a UNION b) INTER c) - by CARD_UNION_EQN - = (CARD a + CARD b - CARD (a INTER b)) + - CARD c - CARD ((a UNION b) INTER c) by CARD_UNION_EQN - = (CARD a + CARD b - CARD (a INTER b)) + - CARD c - CARD ((a INTER c) UNION (b INTER c)) - by UNION_OVER_INTER - = (CARD a + CARD b - CARD (a INTER b)) + CARD c - - (CARD (a INTER c) + CARD (b INTER c) - CARD ((a INTER c) INTER (b INTER c))) - by CARD_UNION_EQN - = CARD a + CARD b + CARD c - CARD (a INTER b) - - (CARD (a INTER c) + CARD (b INTER c) - CARD (a INTER b INTER c)) - by CARD (a INTER b) <= CARD a - = CARD a + CARD b + CARD c - CARD (a INTER b) - - (CARD (b INTER c) + CARD (a INTER c) - CARD (a INTER b INTER c)) - by ADD_COMM - = CARD a + CARD b + CARD c - CARD (a INTER b) - + CARD (a INTER b INTER c) - CARD (b INTER c) - CARD (a INTER c) - by CARD (a INTER b INTER c) <= CARD (b INTER c) - = CARD a + CARD b + CARD c + CARD (a INTER b INTER c) - - CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c) - by arithmetic -*) -Theorem CARD_UNION_3_EQN: - !a b c. FINITE a /\ FINITE b /\ FINITE c ==> - (CARD (a UNION b UNION c) = - CARD a + CARD b + CARD c + CARD (a INTER b INTER c) - - CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c)) -Proof - rpt strip_tac >> - `FINITE (a UNION b) /\ FINITE (a INTER c) /\ FINITE (b INTER c)` by rw[] >> - (`(a INTER c) INTER (b INTER c) = a INTER b INTER c` by (rw[EXTENSION] >> metis_tac[])) >> - `CARD (a INTER b) <= CARD a` by rw[CARD_INTER_LESS_EQ] >> - `CARD (a INTER b INTER c) <= CARD (b INTER c)` by metis_tac[INTER_COMM, CARD_INTER_LESS_EQ] >> - `CARD (a UNION b UNION c) - = CARD (a UNION b) + CARD c - CARD ((a UNION b) INTER c)` by rw[CARD_UNION_EQN] >> - `_ = (CARD a + CARD b - CARD (a INTER b)) + - CARD c - CARD ((a UNION b) INTER c)` by rw[CARD_UNION_EQN] >> - `_ = (CARD a + CARD b - CARD (a INTER b)) + - CARD c - CARD ((a INTER c) UNION (b INTER c))` by fs[UNION_OVER_INTER, INTER_COMM] >> - `_ = (CARD a + CARD b - CARD (a INTER b)) + CARD c - - (CARD (a INTER c) + CARD (b INTER c) - CARD (a INTER b INTER c))` by metis_tac[CARD_UNION_EQN] >> - decide_tac -QED - -(* Simplification of the above result for 3 disjoint sets. *) - -(* Theorem: FINITE a /\ FINITE b /\ FINITE c /\ - DISJOINT a b /\ DISJOINT b c /\ DISJOINT a c ==> - (CARD (a UNION b UNION c) = CARD a + CARD b + CARD c) *) -(* Proof: by DISJOINT_DEF, CARD_UNION_3_EQN *) -Theorem CARD_UNION_3_DISJOINT: - !a b c. FINITE a /\ FINITE b /\ FINITE c /\ - DISJOINT a b /\ DISJOINT b c /\ DISJOINT a c ==> - (CARD (a UNION b UNION c) = CARD a + CARD b + CARD c) -Proof - rw[DISJOINT_DEF] >> - rw[CARD_UNION_3_EQN] -QED - -(* ------------------------------------------------------------------------- *) -(* Maximum and Minimum of a Set *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: FINITE s /\ MAX_SET s < n ==> !x. x IN s ==> x < n *) -(* Proof: - Since x IN s, s <> {} by MEMBER_NOT_EMPTY - Hence x <= MAX_SET s by MAX_SET_DEF - Thus x < n by LESS_EQ_LESS_TRANS -*) -val MAX_SET_LESS = store_thm( - "MAX_SET_LESS", - ``!s n. FINITE s /\ MAX_SET s < n ==> !x. x IN s ==> x < n``, - metis_tac[MEMBER_NOT_EMPTY, MAX_SET_DEF, LESS_EQ_LESS_TRANS]); - -(* Theorem: FINITE s /\ s <> {} ==> !x. x IN s /\ (!y. y IN s ==> y <= x) ==> (x = MAX_SET s) *) -(* Proof: - Let m = MAX_SET s. - Since m IN s /\ x <= m by MAX_SET_DEF - and m IN s ==> m <= x by implication - Hence x = m. -*) -val MAX_SET_TEST = store_thm( - "MAX_SET_TEST", - ``!s. FINITE s /\ s <> {} ==> !x. x IN s /\ (!y. y IN s ==> y <= x) ==> (x = MAX_SET s)``, - rpt strip_tac >> - qabbrev_tac `m = MAX_SET s` >> - `m IN s /\ x <= m` by rw[MAX_SET_DEF, Abbr`m`] >> - `m <= x` by rw[] >> - decide_tac); - -(* Theorem: s <> {} ==> !x. x IN s /\ (!y. y IN s ==> x <= y) ==> (x = MIN_SET s) *) -(* Proof: - Let m = MIN_SET s. - Since m IN s /\ m <= x by MIN_SET_LEM - and m IN s ==> x <= m by implication - Hence x = m. -*) -val MIN_SET_TEST = store_thm( - "MIN_SET_TEST", - ``!s. s <> {} ==> !x. x IN s /\ (!y. y IN s ==> x <= y) ==> (x = MIN_SET s)``, - rpt strip_tac >> - qabbrev_tac `m = MIN_SET s` >> - `m IN s /\ m <= x` by rw[MIN_SET_LEM, Abbr`m`] >> - `x <= m` by rw[] >> - decide_tac); - -(* Theorem: FINITE s /\ s <> {} ==> !x. x IN s ==> ((MAX_SET s = x) <=> (!y. y IN s ==> y <= x)) *) -(* Proof: - Let m = MAX_SET s. - If part: y IN s ==> y <= m, true by MAX_SET_DEF - Only-if part: !y. y IN s ==> y <= x ==> m = x - Note m IN s /\ x <= m by MAX_SET_DEF - and m IN s ==> m <= x by implication - Hence x = m. -*) -Theorem MAX_SET_TEST_IFF: - !s. FINITE s /\ s <> {} ==> - !x. x IN s ==> ((MAX_SET s = x) <=> (!y. y IN s ==> y <= x)) -Proof - rpt strip_tac >> - qabbrev_tac `m = MAX_SET s` >> - rw[EQ_IMP_THM] >- rw[MAX_SET_DEF, Abbr‘m’] >> - `m IN s /\ x <= m` by rw[MAX_SET_DEF, Abbr`m`] >> - `m <= x` by rw[] >> - decide_tac -QED - -(* Theorem: s <> {} ==> !x. x IN s ==> ((MIN_SET s = x) <=> (!y. y IN s ==> x <= y)) *) -(* Proof: - Let m = MIN_SET s. - If part: y IN s ==> m <= y, true by MIN_SET_LEM - Only-if part: !y. y IN s ==> x <= y ==> m = x - Note m IN s /\ m <= x by MIN_SET_LEM - and m IN s ==> x <= m by implication - Hence x = m. -*) -Theorem MIN_SET_TEST_IFF: - !s. s <> {} ==> !x. x IN s ==> ((MIN_SET s = x) <=> (!y. y IN s ==> x <= y)) -Proof - rpt strip_tac >> - qabbrev_tac `m = MIN_SET s` >> - rw[EQ_IMP_THM] >- rw[MIN_SET_LEM, Abbr‘m’] >> - `m IN s /\ m <= x` by rw[MIN_SET_LEM, Abbr`m`] >> - `x <= m` by rw[] >> decide_tac -QED - -(* Theorem: MAX_SET {} = 0 *) -(* Proof: by MAX_SET_REWRITES *) -val MAX_SET_EMPTY = save_thm("MAX_SET_EMPTY", MAX_SET_REWRITES |> CONJUNCT1); -(* val MAX_SET_EMPTY = |- MAX_SET {} = 0: thm *) - -(* Theorem: MAX_SET {e} = e *) -(* Proof: by MAX_SET_REWRITES *) -val MAX_SET_SING = save_thm("MAX_SET_SING", MAX_SET_REWRITES |> CONJUNCT2 |> GEN_ALL); -(* val MAX_SET_SING = |- !e. MAX_SET {e} = e: thm *) - -(* Theorem: FINITE s /\ s <> {} ==> MAX_SET s IN s *) -(* Proof: by MAX_SET_DEF *) -val MAX_SET_IN_SET = store_thm( - "MAX_SET_IN_SET", - ``!s. FINITE s /\ s <> {} ==> MAX_SET s IN s``, - rw[MAX_SET_DEF]); - -(* Theorem: FINITE s ==> !x. x IN s ==> x <= MAX_SET s *) -(* Proof: by in_max_set *) -val MAX_SET_PROPERTY = save_thm("MAX_SET_PROPERTY", in_max_set); -(* val MAX_SET_PROPERTY = |- !s. FINITE s ==> !x. x IN s ==> x <= MAX_SET s: thm *) - -(* Note: MIN_SET {} is undefined. *) - -(* Theorem: MIN_SET {e} = e *) -(* Proof: by MIN_SET_THM *) -val MIN_SET_SING = save_thm("MIN_SET_SING", MIN_SET_THM |> CONJUNCT1); -(* val MIN_SET_SING = |- !e. MIN_SET {e} = e: thm *) - -(* Theorem: s <> {} ==> MIN_SET s IN s *) -(* Proof: by MIN_SET_LEM *) -val MIN_SET_IN_SET = save_thm("MIN_SET_IN_SET", - MIN_SET_LEM |> SPEC_ALL |> UNDISCH |> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); -(* val MIN_SET_IN_SET = |- !s. s <> {} ==> MIN_SET s IN s: thm *) - -(* Theorem: s <> {} ==> !x. x IN s ==> MIN_SET s <= x *) -(* Proof: by MIN_SET_LEM *) -val MIN_SET_PROPERTY = save_thm("MIN_SET_PROPERTY", - MIN_SET_LEM |> SPEC_ALL |> UNDISCH |> CONJUNCT2 |> DISCH_ALL |> GEN_ALL); -(* val MIN_SET_PROPERTY =|- !s. s <> {} ==> !x. x IN s ==> MIN_SET s <= x: thm *) - - -(* Theorem: FINITE s /\ s <> {} /\ s <> {MIN_SET s} ==> (MAX_SET (s DELETE (MIN_SET s)) = MAX_SET s) *) -(* Proof: - Let m = MIN_SET s, t = s DELETE m. - Then t SUBSET s by DELETE_SUBSET - so FINITE t by SUBSET_FINITE]); - Since m IN s by MIN_SET_IN_SET - so t <> {} by DELETE_EQ_SING, s <> {m} - ==> ?x. x IN t by MEMBER_NOT_EMPTY - and x IN s /\ x <> m by IN_DELETE - From x IN s ==> m <= x by MIN_SET_PROPERTY - With x <> m ==> m < x by LESS_OR_EQ - Also x <= MAX_SET s by MAX_SET_PROPERTY - Thus MAX_SET s <> m since m < x <= MAX_SET s - But MAX_SET s IN s by MAX_SET_IN_SET - Thus MAX_SET s IN t by IN_DELETE - Now !y. y IN t ==> - y IN s by SUBSET_DEF - or y <= MAX_SET s by MAX_SET_PROPERTY - Hence MAX_SET s = MAX_SET t by MAX_SET_TEST -*) -val MAX_SET_DELETE = store_thm( - "MAX_SET_DELETE", - ``!s. FINITE s /\ s <> {} /\ s <> {MIN_SET s} ==> (MAX_SET (s DELETE (MIN_SET s)) = MAX_SET s)``, - rpt strip_tac >> - qabbrev_tac `m = MIN_SET s` >> - qabbrev_tac `t = s DELETE m` >> - `t SUBSET s` by rw[Abbr`t`] >> - `FINITE t` by metis_tac[SUBSET_FINITE] >> - `t <> {}` by metis_tac[MIN_SET_IN_SET, DELETE_EQ_SING] >> - `?x. x IN t /\ x IN s /\ x <> m` by metis_tac[IN_DELETE, MEMBER_NOT_EMPTY] >> - `m <= x` by rw[MIN_SET_PROPERTY, Abbr`m`] >> - `m < x` by decide_tac >> - `x <= MAX_SET s` by rw[MAX_SET_PROPERTY] >> - `MAX_SET s <> m` by decide_tac >> - `MAX_SET s IN t` by rw[MAX_SET_IN_SET, Abbr`t`] >> - metis_tac[SUBSET_DEF, MAX_SET_PROPERTY, MAX_SET_TEST]); - -(* Theorem: FINITE s ==> ((MAX_SET s = 0) <=> (s = {}) \/ (s = {0})) *) -(* Proof: - If part: MAX_SET s = 0 ==> (s = {}) \/ (s = {0}) - By contradiction, suppose s <> {} /\ s <> {0}. - Then ?x. x IN s /\ x <> 0 by ONE_ELEMENT_SING - Thus x <= MAX_SET s by in_max_set - so MAX_SET s <> 0 by x <> 0 - This contradicts MAX_SET s = 0. - Only-if part: (s = {}) \/ (s = {0}) ==> MAX_SET s = 0 - If s = {}, MAX_SET s = 0 by MAX_SET_EMPTY - If s = {0}, MAX_SET s = 0 by MAX_SET_SING -*) -val MAX_SET_EQ_0 = store_thm( - "MAX_SET_EQ_0", - ``!s. FINITE s ==> ((MAX_SET s = 0) <=> (s = {}) \/ (s = {0}))``, - (rw[EQ_IMP_THM] >> simp[]) >> - CCONTR_TAC >> - `s <> {} /\ s <> {0}` by metis_tac[] >> - `?x. x IN s /\ x <> 0` by metis_tac[ONE_ELEMENT_SING] >> - `x <= MAX_SET s` by rw[in_max_set] >> - decide_tac); - -(* Theorem: s <> {} ==> ((MIN_SET s = 0) <=> 0 IN s) *) -(* Proof: - If part: MIN_SET s = 0 ==> 0 IN s - This is true by MIN_SET_IN_SET. - Only-if part: 0 IN s ==> MIN_SET s = 0 - Note MIN_SET s <= 0 by MIN_SET_LEM, 0 IN s - Thus MIN_SET s = 0 by arithmetic -*) -val MIN_SET_EQ_0 = store_thm( - "MIN_SET_EQ_0", - ``!s. s <> {} ==> ((MIN_SET s = 0) <=> 0 IN s)``, - rw[EQ_IMP_THM] >- - metis_tac[MIN_SET_IN_SET] >> - `MIN_SET s <= 0` by rw[MIN_SET_LEM] >> - decide_tac); - -(* Theorem: MAX_SET (IMAGE SUC (count n)) = n *) -(* Proof: - By induction on n. - Base case: MAX_SET (IMAGE SUC (count 0)) = 0 - LHS = MAX_SET (IMAGE SUC (count 0)) - = MAX_SET (IMAGE SUC {}) by COUNT_ZERO - = MAX_SET {} by IMAGE_EMPTY - = 0 by MAX_SET_THM - = RHS - Step case: MAX_SET (IMAGE SUC (count n)) = n ==> - MAX_SET (IMAGE SUC (count (SUC n))) = SUC n - LHS = MAX_SET (IMAGE SUC (count (SUC n))) - = MAX_SET (IMAGE SUC (n INSERT count n)) by COUNT_SUC - = MAX_SET ((SUC n) INSERT (IMAGE SUC (count n))) by IMAGE_INSERT - = MAX (SUC n) (MAX_SET (IMAGE SUC (count n))) by MAX_SET_THM - = MAX (SUC n) n by induction hypothesis - = SUC n by LESS_SUC, MAX_DEF - = RHS -*) -Theorem MAX_SET_IMAGE_SUC_COUNT: - !n. MAX_SET (IMAGE SUC (count n)) = n -Proof - Induct_on ‘n’ >- - rw[] >> - ‘MAX_SET (IMAGE SUC (count (SUC n))) = - MAX_SET (IMAGE SUC (n INSERT count n))’ by rw[COUNT_SUC] >> - ‘_ = MAX_SET ((SUC n) INSERT (IMAGE SUC (count n)))’ by rw[] >> - ‘_ = MAX (SUC n) (MAX_SET (IMAGE SUC (count n)))’ by rw[MAX_SET_THM] >> - ‘_ = MAX (SUC n) n’ by rw[] >> - ‘_ = SUC n’ by metis_tac[LESS_SUC, MAX_DEF, MAX_COMM] >> - rw[] -QED - -(* Theorem: HALF x <= c ==> f x <= MAX_SET {f x | HALF x <= c} *) -(* Proof: - Let s = {f x | HALF x <= c} - Note !x. HALF x <= c - ==> SUC (2 * HALF x) <= SUC (2 * c) by arithmetic - and x <= SUC (2 * HALF x) by TWO_HALF_LE_THM - so x <= SUC (2 * c) < 2 * c + 2 by arithmetic - Thus s SUBSET (IMAGE f (count (2 * c + 2))) by SUBSET_DEF - Note FINITE (count (2 * c + 2)) by FINITE_COUNT - so FINITE s by IMAGE_FINITE, SUBSET_FINITE - and f x IN s by HALF x <= c - Thus f x <= MAX_SET s by MAX_SET_PROPERTY -*) -val MAX_SET_IMAGE_with_HALF = store_thm( - "MAX_SET_IMAGE_with_HALF", - ``!f c x. HALF x <= c ==> f x <= MAX_SET {f x | HALF x <= c}``, - rpt strip_tac >> - qabbrev_tac `s = {f x | HALF x <= c}` >> - `s SUBSET (IMAGE f (count (2 * c + 2)))` by - (rw[SUBSET_DEF, Abbr`s`] >> - `SUC (2 * HALF x'') <= SUC (2 * c)` by rw[] >> - `x'' <= SUC (2 * HALF x'')` by rw[TWO_HALF_LE_THM] >> - `x'' < 2 * c + 2` by decide_tac >> - metis_tac[]) >> - `FINITE s` by metis_tac[FINITE_COUNT, IMAGE_FINITE, SUBSET_FINITE] >> - (`f x IN s` by (rw[Abbr`s`] >> metis_tac[])) >> - rw[MAX_SET_PROPERTY]); - -(* -Note: A more general version, replacing HALF x by g x, would be desirable. -However, there is no way to show FINITE s for arbitrary g x. -*) - -(* Theorem: 0 < b /\ x DIV b <= c ==> f x <= MAX_SET {f x | x DIV b <= c} *) -(* Proof: - Let s = {f x | x DIV b <= c}. - Note !x. x DIV b <= c - ==> b * SUC (x DIV b) <= b * SUC c by arithmetic - and x < b * SUC (x DIV b) by DIV_MULT_LESS_EQ, 0 < b - so x < b * SUC c by arithmetic - Thus s SUBSET (IMAGE f (count (b * SUC c))) by SUBSET_DEF - Note FINITE (count (b * SUC c)) by FINITE_COUNT - so FINITE s by IMAGE_FINITE, SUBSET_FINITE - and f x IN s by HALF x <= c - Thus f x <= MAX_SET s by MAX_SET_PROPERTY -*) -val MAX_SET_IMAGE_with_DIV = store_thm( - "MAX_SET_IMAGE_with_DIV", - ``!f b c x. 0 < b /\ x DIV b <= c ==> f x <= MAX_SET {f x | x DIV b <= c}``, - rpt strip_tac >> - qabbrev_tac `s = {f x | x DIV b <= c}` >> - `s SUBSET (IMAGE f (count (b * SUC c)))` by - (rw[SUBSET_DEF, Abbr`s`] >> - `b * SUC (x'' DIV b) <= b * SUC c` by rw[] >> - `x'' < b * SUC (x'' DIV b)` by rw[DIV_MULT_LESS_EQ] >> - `x'' < b * SUC c` by decide_tac >> - metis_tac[]) >> - `FINITE s` by metis_tac[FINITE_COUNT, IMAGE_FINITE, SUBSET_FINITE] >> - (`f x IN s` by (rw[Abbr`s`] >> metis_tac[])) >> - rw[MAX_SET_PROPERTY]); - -(* Theorem: x - b <= c ==> f x <= MAX_SET {f x | x - b <= c} *) -(* Proof: - Let s = {f x | x - b <= c} - Note !x. x - b <= c ==> x <= b + c by arithmetic - so x <= 1 + b + c by arithmetic - Thus s SUBSET (IMAGE f (count (1 + b + c))) by SUBSET_DEF - Note FINITE (count (1 + b + c)) by FINITE_COUNT - so FINITE s by IMAGE_FINITE, SUBSET_FINITE - and f x IN s by x - b <= c - Thus f x <= MAX_SET s by MAX_SET_PROPERTY -*) -val MAX_SET_IMAGE_with_DEC = store_thm( - "MAX_SET_IMAGE_with_DEC", - ``!f b c x. x - b <= c ==> f x <= MAX_SET {f x | x - b <= c}``, - rpt strip_tac >> - qabbrev_tac `s = {f x | x - b <= c}` >> - `s SUBSET (IMAGE f (count (1 + b + c)))` by - (rw[SUBSET_DEF, Abbr`s`] >> - `x'' < b + (c + 1)` by decide_tac >> - metis_tac[]) >> - `FINITE s` by metis_tac[FINITE_COUNT, IMAGE_FINITE, SUBSET_FINITE] >> - `f x IN s` by - (rw[Abbr`s`] >> - `x <= b + c` by decide_tac >> - metis_tac[]) >> - rw[MAX_SET_PROPERTY]); - -(* ------------------------------------------------------------------------- *) -(* Finite and Cardinality Theorems *) -(* ------------------------------------------------------------------------- *) - - -(* Theorem: INJ f s UNIV /\ FINITE s ==> CARD (IMAGE f s) = CARD s *) -(* Proof: - !x y. x IN s /\ y IN s /\ f x = f y == x = y by INJ_DEF - FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE - Therefore BIJ f s (IMAGE f s) by BIJ_DEF, INJ_DEF, SURJ_DEF - Hence CARD (IMAGE f s) = CARD s by FINITE_BIJ_CARD_EQ -*) -val INJ_CARD_IMAGE_EQN = store_thm( - "INJ_CARD_IMAGE_EQN", - ``!f s. INJ f s UNIV /\ FINITE s ==> (CARD (IMAGE f s) = CARD s)``, - rw[INJ_DEF] >> - `FINITE (IMAGE f s)` by rw[IMAGE_FINITE] >> - `BIJ f s (IMAGE f s)` by rw[BIJ_DEF, INJ_DEF, SURJ_DEF] >> - metis_tac[FINITE_BIJ_CARD_EQ]); - - -(* Theorem: FINTIE s /\ FINITE t /\ CARD s = CARD t /\ INJ f s t ==> SURJ f s t *) -(* Proof: - For any map f from s to t, - (IMAGE f s) SUBSET t by IMAGE_SUBSET_TARGET - FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE - CARD (IMAGE f s) = CARD s by INJ_CARD_IMAGE - = CARD t by given - Hence (IMAGE f s) = t by SUBSET_EQ_CARD, FINITE t - or SURJ f s t by IMAGE_SURJ -*) -val FINITE_INJ_AS_SURJ = store_thm( - "FINITE_INJ_AS_SURJ", - ``!f s t. INJ f s t /\ FINITE s /\ FINITE t /\ (CARD s = CARD t) ==> SURJ f s t``, - rw[INJ_DEF] >> - `(IMAGE f s) SUBSET t` by rw[GSYM IMAGE_SUBSET_TARGET] >> - `FINITE (IMAGE f s)` by rw[IMAGE_FINITE] >> - `CARD (IMAGE f s) = CARD t` by metis_tac[INJ_DEF, INJ_CARD_IMAGE, INJ_SUBSET, SUBSET_REFL, SUBSET_UNIV] >> - rw[SUBSET_EQ_CARD, IMAGE_SURJ]); - -(* Reformulate theorem *) - -(* Theorem: FINITE s /\ FINITE t /\ CARD s = CARD t /\ - INJ f s t ==> SURJ f s t *) -(* Proof: by FINITE_INJ_AS_SURJ *) -Theorem FINITE_INJ_IS_SURJ: - !f s t. FINITE s /\ FINITE t /\ CARD s = CARD t /\ - INJ f s t ==> SURJ f s t -Proof - simp[FINITE_INJ_AS_SURJ] -QED - -(* Theorem: FINITE s /\ FINITE t /\ CARD s = CARD t /\ INJ f s t ==> BIJ f s t *) -(* Proof: - Note SURJ f s t by FINITE_INJ_IS_SURJ - so BIJ f s t by BIJ_DEF, INJ f s t -*) -Theorem FINITE_INJ_IS_BIJ: - !f s t. FINITE s /\ FINITE t /\ CARD s = CARD t /\ INJ f s t ==> BIJ f s t -Proof - simp[FINITE_INJ_IS_SURJ, BIJ_DEF] -QED - -(* Note: FINITE_SURJ_IS_BIJ is not easy, see helperFunction. *) - -(* Theorem: FINITE {P x | x < n} *) -(* Proof: - Since IMAGE (\i. P i) (count n) = {P x | x < n}, - this follows by - IMAGE_FINITE |- !s. FINITE s ==> !f. FINITE (IMAGE f s) : thm - FINITE_COUNT |- !n. FINITE (count n) : thm -*) -val FINITE_COUNT_IMAGE = store_thm( - "FINITE_COUNT_IMAGE", - ``!P n. FINITE {P x | x < n }``, - rpt strip_tac >> - `IMAGE (\i. P i) (count n) = {P x | x < n}` by rw[IMAGE_DEF] >> - metis_tac[IMAGE_FINITE, FINITE_COUNT]); - -(* Note: no need for CARD_IMAGE: - -CARD_INJ_IMAGE |- !f s. (!x y. (f x = f y) <=> (x = y)) /\ FINITE s ==> (CARD (IMAGE f s) = CARD s) -FINITE_BIJ_CARD_EQ |- !S. FINITE S ==> !t f. BIJ f S t /\ FINITE t ==> (CARD S = CARD t) -BIJ_LINV_BIJ |- !f s t. BIJ f s t ==> BIJ (LINV f s) t s -*) - -(* Theorem: FINITE s /\ BIJ f s t ==> FINITE t /\ (CARD s = CARD t) *) -(* Proof: - First, FINITE s /\ BIJ f s t ==> FINITE t - BIJ f s t ==> SURJ f s t by BIJ_DEF - ==> IMAGE f s = t by IMAGE_SURJ - FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE - Hence FINITE t. - Next, FINITE s /\ FINITE t /\ BIJ f s t ==> CARD s = CARD t - by FINITE_BIJ_CARD_EQ. -*) -val FINITE_BIJ_PROPERTY = store_thm( - "FINITE_BIJ_PROPERTY", - ``!f s t. FINITE s /\ BIJ f s t ==> FINITE t /\ (CARD s = CARD t)``, - ntac 4 strip_tac >> - CONJ_ASM1_TAC >| [ - `SURJ f s t` by metis_tac[BIJ_DEF] >> - `IMAGE f s = t` by rw[GSYM IMAGE_SURJ] >> - rw[], - metis_tac[FINITE_BIJ_CARD_EQ] - ]); - -(* Note: -> pred_setTheory.CARD_IMAGE; -val it = |- !s. FINITE s ==> CARD (IMAGE f s) <= CARD s: thm -*) - -(* Theorem: For a 1-1 map f: s -> s, s and (IMAGE f s) are of the same size. *) -(* Proof: - By finite induction on the set s: - Base case: CARD (IMAGE f {}) = CARD {} - True by IMAGE f {} = {} by IMAGE_EMPTY - Step case: !s. FINITE s /\ (CARD (IMAGE f s) = CARD s) ==> !e. e NOTIN s ==> (CARD (IMAGE f (e INSERT s)) = CARD (e INSERT s)) - CARD (IMAGE f (e INSERT s)) - = CARD (f e INSERT IMAGE f s) by IMAGE_INSERT - = SUC (CARD (IMAGE f s)) by CARD_INSERT: e NOTIN s, f e NOTIN s, for 1-1 map - = SUC (CARD s) by induction hypothesis - = CARD (e INSERT s) by CARD_INSERT: e NOTIN s. -*) -Theorem FINITE_CARD_IMAGE: - !s f. (!x y. (f x = f y) <=> (x = y)) /\ FINITE s ==> - (CARD (IMAGE f s) = CARD s) -Proof - Induct_on ‘FINITE’ >> rw[] -QED - -(* Theorem: !s. FINITE s ==> CARD (IMAGE SUC s)) = CARD s *) -(* Proof: - Since !n m. SUC n = SUC m <=> n = m by numTheory.INV_SUC - This is true by FINITE_CARD_IMAGE. -*) -val CARD_IMAGE_SUC = store_thm( - "CARD_IMAGE_SUC", - ``!s. FINITE s ==> (CARD (IMAGE SUC s) = CARD s)``, - rw[FINITE_CARD_IMAGE]); - -(* Theorem: FINITE s /\ FINITE t /\ DISJOINT s t ==> (CARD (s UNION t) = CARD s + CARD t) *) -(* Proof: by CARD_UNION_EQN, DISJOINT_DEF, CARD_EMPTY *) -val CARD_UNION_DISJOINT = store_thm( - "CARD_UNION_DISJOINT", - ``!s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> (CARD (s UNION t) = CARD s + CARD t)``, - rw_tac std_ss[CARD_UNION_EQN, DISJOINT_DEF, CARD_EMPTY]); - -(* Idea: improve FINITE_BIJ_COUNT to include CARD information. *) - -(* Theorem: FINITE s ==> ?f. BIJ f (count (CARD s)) s *) -(* Proof: - Note ?f b. BIJ f (count b) s by FINITE_BIJ_COUNT - and FINITE (count b) by FINITE_COUNT - so CARD s - = CARD (count b) by FINITE_BIJ - = b by CARD_COUNT -*) -Theorem FINITE_BIJ_COUNT_CARD: - !s. FINITE s ==> ?f. BIJ f (count (CARD s)) s -Proof - rpt strip_tac >> - imp_res_tac FINITE_BIJ_COUNT >> - metis_tac[FINITE_COUNT, CARD_COUNT, FINITE_BIJ] -QED - -(* Theorem: !n. 0 < n ==> IMAGE (\x. x MOD n) s SUBSET (count n) *) -(* Proof: by SUBSET_DEF, MOD_LESS. *) -val image_mod_subset_count = store_thm( - "image_mod_subset_count", - ``!s n. 0 < n ==> IMAGE (\x. x MOD n) s SUBSET (count n)``, - rw[SUBSET_DEF] >> - rw[MOD_LESS]); - -(* Theorem: !n. 0 < n ==> CARD (IMAGE (\x. x MOD n) s) <= n *) -(* Proof: - Let t = IMAGE (\x. x MOD n) s - Since t SUBSET count n by SUBSET_DEF, MOD_LESS - Now FINITE (count n) by FINITE_COUNT - and CARD (count n) = n by CARD_COUNT - So CARD t <= n by CARD_SUBSET -*) -val card_mod_image = store_thm( - "card_mod_image", - ``!s n. 0 < n ==> CARD (IMAGE (\x. x MOD n) s) <= n``, - rpt strip_tac >> - qabbrev_tac `t = IMAGE (\x. x MOD n) s` >> - (`t SUBSET count n` by (rw[SUBSET_DEF, Abbr`t`] >> metis_tac[MOD_LESS])) >> - metis_tac[CARD_SUBSET, FINITE_COUNT, CARD_COUNT]); -(* not used *) - -(* Theorem: !n. 0 < n /\ 0 NOTIN (IMAGE (\x. x MOD n) s) ==> CARD (IMAGE (\x. x MOD n) s) < n *) -(* Proof: - Let t = IMAGE (\x. x MOD n) s - Since t SUBSET count n by SUBSET_DEF, MOD_LESS - Now FINITE (count n) by FINITE_COUNT - and CARD (count n) = n by CARD_COUNT - So CARD t <= n by CARD_SUBSET - and FINITE t by SUBSET_FINITE - But 0 IN (count n) by IN_COUNT - yet 0 NOTIN t by given - Hence t <> (count n) by NOT_EQUAL_SETS - So CARD t <> n by SUBSET_EQ_CARD - Thus CARD t < n -*) -val card_mod_image_nonzero = store_thm( - "card_mod_image_nonzero", - ``!s n. 0 < n /\ 0 NOTIN (IMAGE (\x. x MOD n) s) ==> CARD (IMAGE (\x. x MOD n) s) < n``, - rpt strip_tac >> - qabbrev_tac `t = IMAGE (\x. x MOD n) s` >> - (`t SUBSET count n` by (rw[SUBSET_DEF, Abbr`t`] >> metis_tac[MOD_LESS])) >> - `FINITE (count n) /\ (CARD (count n) = n) ` by rw[] >> - `CARD t <= n` by metis_tac[CARD_SUBSET] >> - `0 IN (count n)` by rw[] >> - `t <> (count n)` by metis_tac[NOT_EQUAL_SETS] >> - `CARD t <> n` by metis_tac[SUBSET_EQ_CARD, SUBSET_FINITE] >> - decide_tac); -(* not used *) - -(* ------------------------------------------------------------------------- *) -(* Partition Property *) -(* ------------------------------------------------------------------------- *) - -(* Overload partition by split *) -val _ = overload_on("split", ``\s u v. (s = u UNION v) /\ (DISJOINT u v)``); - -(* Pretty printing of partition by split *) -val _ = add_rule {block_style = (AroundEachPhrase, (PP.CONSISTENT, 2)), - fixity = Infix(NONASSOC, 450), - paren_style = OnlyIfNecessary, - term_name = "split", - pp_elements = [HardSpace 1, TOK "=|=", HardSpace 1, TM, - BreakSpace(1,1), TOK "#", BreakSpace(1,1)]}; - -(* Theorem: FINITE s ==> !u v. s =|= u # v ==> ((u = {}) <=> (v = s)) *) -(* Proof: - If part: u = {} ==> v = s - Note s = {} UNION v by given, u = {} - = v by UNION_EMPTY - Only-if part: v = s ==> u = {} - Note FINITE u /\ FINITE v by FINITE_UNION - ==> CARD v = CARD u + CARD v by CARD_UNION_DISJOINT - ==> 0 = CARD u by arithmetic - Thus u = {} by CARD_EQ_0 -*) -val finite_partition_property = store_thm( - "finite_partition_property", - ``!s. FINITE s ==> !u v. s =|= u # v ==> ((u = {}) <=> (v = s))``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `FINITE u /\ FINITE v` by metis_tac[FINITE_UNION] >> - `CARD v = CARD u + CARD v` by metis_tac[CARD_UNION_DISJOINT] >> - `CARD u <> 0` by rw[CARD_EQ_0] >> - decide_tac); - -(* Theorem: FINITE s ==> !P. let u = {x | x IN s /\ P x} in let v = {x | x IN s /\ ~P x} in - FINITE u /\ FINITE v /\ s =|= u # v *) -(* Proof: - This is to show: - (1) FINITE u, true by SUBSET_DEF, SUBSET_FINITE - (2) FINITE v, true by SUBSET_DEF, SUBSET_FINITE - (3) u UNION v = s by IN_UNION - (4) DISJOINT u v, true by IN_DISJOINT, MEMBER_NOT_EMPTY -*) -Theorem finite_partition_by_predicate: - !s. FINITE s ==> - !P. let u = {x | x IN s /\ P x} ; - v = {x | x IN s /\ ~P x} - in - FINITE u /\ FINITE v /\ s =|= u # v -Proof - rw_tac std_ss[] >| [ - `u SUBSET s` by rw[SUBSET_DEF, Abbr`u`] >> - metis_tac[SUBSET_FINITE], - `v SUBSET s` by rw[SUBSET_DEF, Abbr`v`] >> - metis_tac[SUBSET_FINITE], - simp[EXTENSION, Abbr‘u’, Abbr‘v’] >> - metis_tac[], - simp[Abbr‘u’, Abbr‘v’, DISJOINT_DEF, EXTENSION] >> metis_tac[] - ] -QED - -(* Theorem: u SUBSET s ==> let v = s DIFF u in s =|= u # v *) -(* Proof: - This is to show: - (1) u SUBSET s ==> s = u UNION (s DIFF u), true by UNION_DIFF - (2) u SUBSET s ==> DISJOINT u (s DIFF u), true by DISJOINT_DIFF -*) -val partition_by_subset = store_thm( - "partition_by_subset", - ``!s u. u SUBSET s ==> let v = s DIFF u in s =|= u # v``, - rw[UNION_DIFF, DISJOINT_DIFF]); - -(* Theorem: x IN s ==> s =|= {x} # (s DELETE x) *) -(* Proof: - Note x IN s ==> {x} SUBSET s by SUBSET_DEF, IN_SING - and s DELETE x = s DIFF {x} by DELETE_DEF - Thus s =|= {x} # (s DELETE x) by partition_by_subset -*) -val partition_by_element = store_thm( - "partition_by_element", - ``!s x. x IN s ==> s =|= {x} # (s DELETE x)``, - metis_tac[partition_by_subset, DELETE_DEF, SUBSET_DEF, IN_SING]); - -(* ------------------------------------------------------------------------- *) -(* Splitting of a set *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: s =|= {} # t <=> (s = t) *) -(* Proof: - s =|= {} # t - <=> (s = {} UNION t) /\ (DISJOINT {} t) by notation - <=> (s = {} UNION t) /\ T by DISJOINT_EMPTY - <=> s = t by UNION_EMPTY -*) -val SPLIT_EMPTY = store_thm( - "SPLIT_EMPTY", - ``!s t. s =|= {} # t <=> (s = t)``, - rw[]); - -(* Theorem: s =|= u # v /\ v =|= a # b ==> s =|= u UNION a # b /\ u UNION a =|= u # a *) -(* Proof: - Note s =|= u # v <=> (s = u UNION v) /\ (DISJOINT u v) by notation - and v =|= a # b <=> (v = a UNION b) /\ (DISJOINT a b) by notation - Let c = u UNION a. - Then s = u UNION v by above - = u UNION (a UNION b) by above - = (u UNION a) UNION b by UNION_ASSOC - = c UNION b - Note DISJOINT u v - <=> DISJOINT u (a UNION b) - <=> DISJOINT (a UNION b) u by DISJOINT_SYM - <=> DISJOINT a u /\ DISJOINT b u by DISJOINT_UNION - <=> DISJOINT u a /\ DISJOINT u b by DISJOINT_SYM - - Thus DISJOINT c b - <=> DISJOINT (u UNION a) b by above - <=> DISJOINT u b /\ DISJOINT a b by DISJOINT_UNION - <=> T /\ T by above - <=> T - Therefore, - s =|= c # b by s = c UNION b /\ DISJOINT c b - and c =|= u # a by c = u UNION a /\ DISJOINT u a -*) -val SPLIT_UNION = store_thm( - "SPLIT_UNION", - ``!s u v a b. s =|= u # v /\ v =|= a # b ==> s =|= u UNION a # b /\ u UNION a =|= u # a``, - metis_tac[DISJOINT_UNION, DISJOINT_SYM, UNION_ASSOC]); - -(* Theorem: s =|= u # v <=> u SUBSET s /\ (v = s DIFF u) *) -(* Proof: - Note s =|= u # v <=> (s = u UNION v) /\ (DISJOINT u v) by notation - If part: s =|= u # v ==> u SUBSET s /\ (v = s DIFF u) - Note u SUBSET (u UNION v) by SUBSET_UNION - s DIFF u - = (u UNION v) DIFF u by s = u UNION v - = v DIFF u by DIFF_SAME_UNION - = v by DISJOINT_DIFF_IFF, DISJOINT_SYM - - Only-if part: u SUBSET s /\ (v = s DIFF u) ==> s =|= u # v - Note s = u UNION (s DIFF u) by UNION_DIFF, u SUBSET s - and DISJOINT u (s DIFF u) by DISJOINT_DIFF - Thus s =|= u # v by notation -*) -val SPLIT_EQ = store_thm( - "SPLIT_EQ", - ``!s u v. s =|= u # v <=> u SUBSET s /\ (v = s DIFF u)``, - rw[DISJOINT_DEF, SUBSET_DEF, EXTENSION] >> - metis_tac[]); - -(* Theorem: (s =|= u # v) = (s =|= v # u) *) -(* Proof: - s =|= u # v - = (s = u UNION v) /\ DISJOINT u v by notation - = (s = v UNION u) /\ DISJOINT u v by UNION_COMM - = (s = v UNION u) /\ DISJOINT v u by DISJOINT_SYM - = s =|= v # u by notation -*) -val SPLIT_SYM = store_thm( - "SPLIT_SYM", - ``!s u v. (s =|= u # v) = (s =|= v # u)``, - rw_tac std_ss[UNION_COMM, DISJOINT_SYM]); - -(* Theorem: (s =|= u # v) ==> (s =|= v # u) *) -(* Proof: by SPLIT_SYM *) -val SPLIT_SYM_IMP = store_thm( - "SPLIT_SYM_IMP", - ``!s u v. (s =|= u # v) ==> (s =|= v # u)``, - rw_tac std_ss[SPLIT_SYM]); - -(* Theorem: s =|= {x} # v <=> (x IN s /\ (v = s DELETE x)) *) -(* Proof: - s =|= {x} # v - <=> {x} SUBSET s /\ (v = s DIFF {x}) by SPLIT_EQ - <=> x IN s /\ (v = s DIFF {x}) by SUBSET_DEF - <=> x IN s /\ (v = s DELETE x) by DELETE_DEF -*) -val SPLIT_SING = store_thm( - "SPLIT_SING", - ``!s v x. s =|= {x} # v <=> (x IN s /\ (v = s DELETE x))``, - rw[SPLIT_EQ, SUBSET_DEF, DELETE_DEF]); - -(* Theorem: s =|= u # v ==> u SUBSET s /\ v SUBSET s *) -(* Proof: by SUBSET_UNION *) -Theorem SPLIT_SUBSETS: - !s u v. s =|= u # v ==> u SUBSET s /\ v SUBSET s -Proof - rw[] -QED - -(* Theorem: FINITE s /\ s =|= u # v ==> FINITE u /\ FINITE v *) -(* Proof: by SPLIT_SUBSETS, SUBSET_FINITE *) -Theorem SPLIT_FINITE: - !s u v. FINITE s /\ s =|= u # v ==> FINITE u /\ FINITE v -Proof - simp[SPLIT_SUBSETS, SUBSET_FINITE] -QED - -(* Theorem: FINITE s /\ s =|= u # v ==> (CARD s = CARD u + CARD v) *) -(* Proof: - Note FINITE u /\ FINITE v by SPLIT_FINITE - CARD s - = CARD (u UNION v) by notation - = CARD u + CARD v by CARD_UNION_DISJOINT -*) -Theorem SPLIT_CARD: - !s u v. FINITE s /\ s =|= u # v ==> (CARD s = CARD u + CARD v) -Proof - metis_tac[CARD_UNION_DISJOINT, SPLIT_FINITE] -QED - -(* Theorem: s =|= u # v <=> (u = s DIFF v) /\ (v = s DIFF u) *) -(* Proof: - If part: s =|= u # v ==> (u = s DIFF v) /\ (v = s DIFF u) - True by EXTENSION, IN_UNION, IN_DISJOINT, IN_DIFF. - Only-if part: (u = s DIFF v) /\ (v = s DIFF u) ==> s =|= u # v - True by EXTENSION, IN_UNION, IN_DISJOINT, IN_DIFF. -*) -val SPLIT_EQ_DIFF = store_thm( - "SPLIT_EQ_DIFF", - ``!s u v. s =|= u # v <=> (u = s DIFF v) /\ (v = s DIFF u)``, - rpt strip_tac >> - eq_tac >| [ - rpt strip_tac >| [ - rw[EXTENSION] >> - metis_tac[IN_UNION, IN_DISJOINT, IN_DIFF], - rw[EXTENSION] >> - metis_tac[IN_UNION, IN_DISJOINT, IN_DIFF] - ], - rpt strip_tac >| [ - rw[EXTENSION] >> - metis_tac[IN_UNION, IN_DIFF], - metis_tac[IN_DISJOINT, IN_DIFF] - ] - ]); - -(* Theorem alias *) -val SPLIT_BY_SUBSET = save_thm("SPLIT_BY_SUBSET", partition_by_subset); -(* val SPLIT_BY_SUBSET = |- !s u. u SUBSET s ==> (let v = s DIFF u in s =|= u # v): thm *) - -(* Theorem alias *) -val SUBSET_DIFF_DIFF = save_thm("SUBSET_DIFF_DIFF", DIFF_DIFF_SUBSET); -(* val SUBSET_DIFF_DIFF = |- !s t. t SUBSET s ==> (s DIFF (s DIFF t) = t) *) - -(* Theorem: s1 SUBSET t /\ s2 SUBSET t /\ (t DIFF s1 = t DIFF s2) ==> (s1 = s2) *) -(* Proof: - Let u = t DIFF s2. - Then u = t DIFF s1 by given - ==> t =|= u # s1 by SPLIT_BY_SUBSET, s1 SUBSET t - Thus s1 = t DIFF u by SPLIT_EQ - = t DIFF (t DIFF s2) by notation - = s2 by SUBSET_DIFF_DIFF, s2 SUBSET t -*) -val SUBSET_DIFF_EQ = store_thm( - "SUBSET_DIFF_EQ", - ``!s1 s2 t. s1 SUBSET t /\ s2 SUBSET t /\ (t DIFF s1 = t DIFF s2) ==> (s1 = s2)``, - metis_tac[SPLIT_BY_SUBSET, SPLIT_EQ, SUBSET_DIFF_DIFF]); - -(* ------------------------------------------------------------------------- *) -(* Bijective Inverses. *) -(* ------------------------------------------------------------------------- *) - -(* In pred_setTheory: -LINV_DEF |- !f s t. INJ f s t ==> !x. x IN s ==> (LINV f s (f x) = x) -BIJ_LINV_INV |- !f s t. BIJ f s t ==> !x. x IN t ==> (f (LINV f s x) = x) -BIJ_LINV_BIJ |- !f s t. BIJ f s t ==> BIJ (LINV f s) t s -RINV_DEF |- !f s t. SURJ f s t ==> !x. x IN t ==> (f (RINV f s x) = x) - -That's it, must be missing some! -Must assume: !y. y IN t ==> RINV f s y IN s -*) - -(* Theorem: BIJ f s t ==> !x. x IN t ==> (LINV f s x) IN s *) -(* Proof: - Since BIJ f s t ==> BIJ (LINV f s) t s by BIJ_LINV_BIJ - so x IN t ==> (LINV f s x) IN s by BIJ_DEF, INJ_DEF -*) -val BIJ_LINV_ELEMENT = store_thm( - "BIJ_LINV_ELEMENT", - ``!f s t. BIJ f s t ==> !x. x IN t ==> (LINV f s x) IN s``, - metis_tac[BIJ_LINV_BIJ, BIJ_DEF, INJ_DEF]); - -(* Theorem: (!x. x IN s ==> (LINV f s (f x) = x)) /\ (!x. x IN t ==> (f (LINV f s x) = x)) *) -(* Proof: - Since BIJ f s t ==> INJ f s t by BIJ_DEF - and INJ f s t ==> !x. x IN s ==> (LINV f s (f x) = x) by LINV_DEF - also BIJ f s t ==> !x. x IN t ==> (f (LINV f s x) = x) by BIJ_LINV_INV -*) -val BIJ_LINV_THM = store_thm( - "BIJ_LINV_THM", - ``!(f:'a -> 'b) s t. BIJ f s t ==> - (!x. x IN s ==> (LINV f s (f x) = x)) /\ (!x. x IN t ==> (f (LINV f s x) = x))``, - metis_tac[BIJ_DEF, LINV_DEF, BIJ_LINV_INV]); - -(* Theorem: BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> - !x. x IN s ==> (RINV f s (f x) = x) *) -(* Proof: - BIJ f s t means INJ f s t /\ SURJ f s t by BIJ_DEF - Hence x IN s ==> f x IN t by INJ_DEF or SURJ_DEF - ==> f (RINV f s (f x)) = f x by RINV_DEF, SURJ f s t - ==> RINV f s (f x) = x by INJ_DEF -*) -val BIJ_RINV_INV = store_thm( - "BIJ_RINV_INV", - ``!(f:'a -> 'b) s t. BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> - !x. x IN s ==> (RINV f s (f x) = x)``, - rw[BIJ_DEF] >> - `f x IN t` by metis_tac[INJ_DEF] >> - `f (RINV f s (f x)) = f x` by metis_tac[RINV_DEF] >> - metis_tac[INJ_DEF]); - -(* Theorem: BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> BIJ (RINV f s) t s *) -(* Proof: - By BIJ_DEF, this is to show: - (1) INJ (RINV f s) t s - By INJ_DEF, this is to show: - x IN t /\ y IN t /\ RINV f s x = RINV f s y ==> x = y - But SURJ f s t by BIJ_DEF - so f (RINV f s x) = x by RINV_DEF, SURJ f s t - and f (RINV f s y) = y by RINV_DEF, SURJ f s t - Thus x = y. - (2) SURJ (RINV f s) t s - By SURJ_DEF, this is to show: - x IN s ==> ?y. y IN t /\ (RINV f s y = x) - But INJ f s t by BIJ_DEF - so f x IN t by INJ_DEF - and RINV f s (f x) = x by BIJ_RINV_INV - Take y = f x to meet the criteria. -*) -val BIJ_RINV_BIJ = store_thm( - "BIJ_RINV_BIJ", - ``!(f:'a -> 'b) s t. BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> BIJ (RINV f s) t s``, - rpt strip_tac >> - rw[BIJ_DEF] >- - metis_tac[INJ_DEF, BIJ_DEF, RINV_DEF] >> - rw[SURJ_DEF] >> - metis_tac[INJ_DEF, BIJ_DEF, BIJ_RINV_INV]); - -(* Theorem: INJ f t univ(:'b) /\ s SUBSET t ==> !x. x IN s ==> (LINV f t (f x) = x) *) -(* Proof: by LINV_DEF, SUBSET_DEF *) -Theorem LINV_SUBSET: - !(f:'a -> 'b) s t. INJ f t univ(:'b) /\ s SUBSET t ==> !x. x IN s ==> (LINV f t (f x) = x) -Proof - metis_tac[LINV_DEF, SUBSET_DEF] -QED - -(* ------------------------------------------------------------------------- *) -(* Iteration, Summation and Product *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: ITSET f {x} b = f x b *) -(* Proof: - Since FINITE {x} by FINITE_SING - ITSET f {x} b - = ITSET f (REST {x}) (f (CHOICE {x} b) by ITSET_THM - = ITSET f {} (f x b) by CHOICE_SING, REST_SING - = f x b by ITSET_EMPTY -*) -val ITSET_SING = store_thm( - "ITSET_SING", - ``!f x b. ITSET f {x} b = f x b``, - rw[ITSET_THM]); - -(* Theorem: FINITE s /\ s <> {} ==> (ITSET f s b = ITSET f (REST s) (f (CHOICE s) b)) *) -(* Proof: by ITSET_THM. *) -val ITSET_PROPERTY = store_thm( - "ITSET_PROPERTY", - ``!s f b. FINITE s /\ s <> {} ==> (ITSET f s b = ITSET f (REST s) (f (CHOICE s) b))``, - rw[ITSET_THM]); - -(* Theorem: (f = g) ==> (ITSET f = ITSET g) *) -(* Proof: by congruence rule *) -val ITSET_CONG = store_thm( - "ITSET_CONG", - ``!f g. (f = g) ==> (ITSET f = ITSET g)``, - rw[]); - -(* Reduction of ITSET *) - -(* Theorem: (!x y z. f x (f y z) = f y (f x z)) ==> - !s x b. FINITE s /\ x NOTIN s ==> (ITSET f (x INSERT s) b = f x (ITSET f s b)) *) -(* Proof: - Since x NOTIN s ==> s DELETE x = s by DELETE_NON_ELEMENT - The result is true by COMMUTING_ITSET_RECURSES -*) -val ITSET_REDUCTION = store_thm( - "ITSET_REDUCTION", - ``!f. (!x y z. f x (f y z) = f y (f x z)) ==> - !s x b. FINITE s /\ x NOTIN s ==> (ITSET f (x INSERT s) b = f x (ITSET f s b))``, - rw[COMMUTING_ITSET_RECURSES, DELETE_NON_ELEMENT]); - -(* ------------------------------------------------------------------------- *) -(* Rework of ITSET Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Define a function that gives closure and is commute_associative *) -val closure_comm_assoc_fun_def = Define` - closure_comm_assoc_fun f s <=> - (!x y. x IN s /\ y IN s ==> f x y IN s) /\ (* closure *) - (!x y z. x IN s /\ y IN s /\ z IN s ==> (f x (f y z) = f y (f x z))) (* comm_assoc *) -`; - -(* Theorem: FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !(x b):: t. ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) *) -(* Proof: - By complete induction on CARD s. - The goal is to show: - ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) [1] - Applying ITSET_INSERT to LHS, this is to prove: - ITSET f z (f y b) = ITSET f (s DELETE x) (f x b) - | | - | y = CHOICE (x INSERT s) - +--- z = REST (x INSERT s) - Note y NOTIN z by CHOICE_NOT_IN_REST - If x IN s, - then x INSERT s = s by ABSORPTION - thus y = CHOICE s, z = REST s by x INSERT s = s - - If x = y, - Since s = CHOICE s INSERT REST s by CHOICE_INSERT_REST - = y INSERT z by above y, z - Hence z = s DELETE y by DELETE_INSERT - Since CARD z < CARD s, apply induction: - ITSET f (y INSERT z) b = ITSET f (z DELETE y) (f y b) [2a] - For the original goal [1], - LHS = ITSET f (x INSERT s) b - = ITSET f s b by x INSERT s = s - = ITSET f (y INSERT z) b by s = y INSERT z - = ITSET f (z DELETE y) (f y b) by induction hypothesis [2a] - = ITSET f z (f y b) by DELETE_NON_ELEMENT, y NOTIN z - = ITSET f (s DELETE y) (f y b) by z = s DELETE y - = ITSET f (s DELETE x) (f x b) by x = y - = RHS - - If x <> y, let u = z DELETE x. - Note REST s = z = x INSERT u by INSERT_DELETE - Now s = x INSERT (y INSERT u) - = x INSERT v, where v = y INSERT u, and x NOTIN z. - Therefore (s DELETE x) = v by DELETE_INSERT - Since CARD v < CARD s, apply induction: - ITSET f (x INSERT v) b = ITSET f (v DELETE x) (f x b) [2b] - For the original goal [1], - LHS = ITSET f (x INSERT s) b - = ITSET f s b by x INSERT s = s - = ITSET f (x INSERT v) b by s = x INSERT v - = ITSET f (v DELETE x) (f x b) by induction hypothesis [2b] - = ITSET f v (f x b) by x NOTIN v - = ITSET f (s DELETE x) (f x b) by v = s DELETE x - = RHS - - If x NOTIN s, - then s DELETE x = x by DELETE_NON_ELEMENT - To prove: ITSET f (x INSERT s) b = ITSET f s (f x b) by [1] - The CHOICE/REST of (x INSERT s) cannot be simplified, but can be replaced by: - Note (x INSERT s) <> {} by NOT_EMPTY_INSERT - y INSERT z - = CHOICE (x INSERT s) INSERT (REST (x INSERT s)) by y = CHOICE (x INSERT s), z = REST (x INSERT s) - = x INSERT s by CHOICE_INSERT_REST - - If y = x, - Then z = s by DELETE_INSERT, y INSERT z = x INSERT s, y = x. - because s = s DELETE x by DELETE_NON_ELEMENT, x NOTIN s. - = (x INSERT s) DELETE x by DELETE_INSERT - = (y INSERT z) DELETE x by above - = (y INSERT z) DELETE y by y = x - = z DELETE y by DELETE_INSERT - = z by DELETE_NON_ELEMENT, y NOTIN z. - For the modified goal [1], - LHS = ITSET f (x INSERT s) b - = ITSET f (REST (x INSERT s)) (f (CHOICE (x INSERT s)) b) by ITSET_PROPERTY - = ITSET f z (f y b) by y = CHOICE (x INSERT s), z = REST (x INSERT s) - = ITSET f s (f x b) by z = s, y = x - = RHS - - If y <> x, - Then x IN z and y IN s by IN_INSERT, x INSERT s = y INSERT z and x <> y. - and s = y INSERT (s DELETE y) by INSERT_DELETE, y IN s - = y INSERT u where u = s DELETE y - Then y NOTIN u by IN_DELETE - and z = x INSERT u, - because x INSERT u - = x INSERT (s DELETE y) by u = s DELETE y - = (x INSERT s) DELETE y by DELETE_INSERT, x <> y - = (y INSERT z) DELETE y by x INSERT s = y INSERT z - = z by INSERT_DELETE - and x NOTIN u by IN_DELETE, u = s DELETE y, but x NOTIN s. - Thus CARD u < CARD s by CARD_INSERT, s = y INSERT u. - Apply induction: - !x b. ITSET f (x INSERT u) b = ITSET f (u DELETE x) (f x b) [2c] - - For the modified goal [1], - LHS = ITSET f (x INSERT s) b - = ITSET f (REST (x INSERT s)) (f (CHOICE (x INSERT s)) b) by ITSET_PROPERTY - = ITSET f z (f y b) by z = REST (x INSERT s), y = CHOICE (x INSERT s) - = ITSET f (x INSERT u) (f y b) by z = x INSERT u - = ITSET f (u DELETE x) (f x (f y b)) by induction hypothesis, [2c] - = ITSET f u (f x (f y b)) by x NOTIN u - RHS = ITSET f s (f x b) - = ITSET f (y INSERT u) (f x b) by s = y INSERT u - = ITSET f (u DELETE y) (f y (f x b)) by induction hypothesis, [2c] - = ITSET f u (f y (f x b)) by y NOTIN u - Applying the commute_associativity of f, LHS = RHS. -*) -Theorem SUBSET_COMMUTING_ITSET_INSERT: - !f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !(x b)::t. ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) -Proof - completeInduct_on `CARD s` >> - rule_assum_tac(SIMP_RULE bool_ss[GSYM RIGHT_FORALL_IMP_THM, AND_IMP_INTRO]) >> - rw[RES_FORALL_THM] >> - rw[ITSET_INSERT] >> - qabbrev_tac `y = CHOICE (x INSERT s)` >> - qabbrev_tac `z = REST (x INSERT s)` >> - `y NOTIN z` by metis_tac[CHOICE_NOT_IN_REST] >> - `!x s. x IN s ==> (x INSERT s = s)` by rw[ABSORPTION] >> - `!x s. x NOTIN s ==> (s DELETE x = s)` by rw[DELETE_NON_ELEMENT] >> - Cases_on `x IN s` >| [ - `s = y INSERT z` by metis_tac[NOT_IN_EMPTY, CHOICE_INSERT_REST] >> - `FINITE z` by metis_tac[REST_SUBSET, SUBSET_FINITE] >> - `CARD s = SUC (CARD z)` by rw[] >> - `CARD z < CARD s` by decide_tac >> - `z = s DELETE y` by metis_tac[DELETE_INSERT] >> - `z SUBSET t` by metis_tac[DELETE_SUBSET, SUBSET_TRANS] >> - Cases_on `x = y` >- metis_tac[] >> - `x IN z` by metis_tac[IN_INSERT] >> - qabbrev_tac `u = z DELETE x` >> - `z = x INSERT u` by rw[INSERT_DELETE, Abbr`u`] >> - `x NOTIN u` by metis_tac[IN_DELETE] >> - qabbrev_tac `v = y INSERT u` >> - `s = x INSERT v` by simp[INSERT_COMM, Abbr `v`] >> - `x NOTIN v` by rw[Abbr `v`] >> - `FINITE v` by metis_tac[FINITE_INSERT] >> - `CARD s = SUC (CARD v)` by metis_tac[CARD_INSERT] >> - `CARD v < CARD s` by decide_tac >> - `v SUBSET t` by metis_tac[INSERT_SUBSET, SUBSET_TRANS] >> - `s DELETE x = v` by rw[DELETE_INSERT, Abbr `v`] >> - `v = s DELETE x` by rw[] >> - `y IN t` by metis_tac[NOT_INSERT_EMPTY, CHOICE_DEF, SUBSET_DEF] >> - metis_tac[], - `x INSERT s <> {}` by rw[] >> - `y INSERT z = x INSERT s` by rw[CHOICE_INSERT_REST, Abbr`y`, Abbr`z`] >> - Cases_on `x = y` >- metis_tac[DELETE_INSERT, ITSET_PROPERTY] >> - `x IN z /\ y IN s` by metis_tac[IN_INSERT] >> - qabbrev_tac `u = s DELETE y` >> - `s = y INSERT u` by rw[INSERT_DELETE, Abbr`u`] >> - `y NOTIN u` by metis_tac[IN_DELETE] >> - `z = x INSERT u` by metis_tac[DELETE_INSERT, INSERT_DELETE] >> - `x NOTIN u` by metis_tac[IN_DELETE] >> - `FINITE u` by metis_tac[FINITE_DELETE, SUBSET_FINITE] >> - `CARD u < CARD s` by rw[] >> - `u SUBSET t` by metis_tac[DELETE_SUBSET, SUBSET_TRANS] >> - `y IN t` by metis_tac[CHOICE_DEF, SUBSET_DEF] >> - `f y b IN t /\ f x b IN t` by prove_tac[closure_comm_assoc_fun_def] >> - `ITSET f z (f y b) = ITSET f (x INSERT u) (f y b)` by rw[] >> - `_ = ITSET f (u DELETE x) (f x (f y b))` by metis_tac[] >> - `_ = ITSET f u (f x (f y b))` by rw[] >> - `ITSET f s (f x b) = ITSET f (y INSERT u) (f x b)` by rw[] >> - `_ = ITSET f (u DELETE y) (f y (f x b))` by metis_tac[] >> - `_ = ITSET f u (f y (f x b))` by rw[] >> - `f x (f y b) = f y (f x b)` by prove_tac[closure_comm_assoc_fun_def] >> - metis_tac[] - ] -QED - -(* This is a generalisation of COMMUTING_ITSET_INSERT, removing the requirement of commuting everywhere. *) - -(* Theorem: FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !(x b)::t. ITSET f s (f x b) = f x (ITSET f s b) *) -(* Proof: - By complete induction on CARD s. - The goal is to show: ITSET f s (f x b) = f x (ITSET f s b) - Base: s = {}, - LHS = ITSET f {} (f x b) - = f x b by ITSET_EMPTY - = f x (ITSET f {} b) by ITSET_EMPTY - = RHS - Step: s <> {}, - Let s = y INSERT z, where y = CHOICE s, z = REST s. - Then y NOTIN z by CHOICE_NOT_IN_REST - But y IN t by CHOICE_DEF, SUBSET_DEF - and z SUBSET t by REST_SUBSET, SUBSET_TRANS - Also FINITE z by REST_SUBSET, SUBSET_FINITE - Thus CARD s = SUC (CARD z) by CARD_INSERT - or CARD z < CARD s - Note f x b IN t /\ f y b IN t by closure_comm_assoc_fun_def - - LHS = ITSET f s (f x b) - = ITSET f (y INSERT z) (f x b) by s = y INSERT z - = ITSET f (z DELETE y) (f y (f x b)) by SUBSET_COMMUTING_ITSET_INSERT, y, f x b IN t - = ITSET f z (f y (f x b)) by DELETE_NON_ELEMENT, y NOTIN z - = ITSET f z (f x (f y b)) by closure_comm_assoc_fun_def, x, y, b IN t - = f x (ITSET f z (f y b)) by inductive hypothesis, CARD z < CARD s, x, f y b IN t - = f x (ITSET f (z DELETE y) (f y b)) by DELETE_NON_ELEMENT, y NOTIN z - = f x (ITSET f (y INSERT z) b) by SUBSET_COMMUTING_ITSET_INSERT, y, f y b IN t - = f x (ITSET f s b) by s = y INSERT z - = RHS -*) -val SUBSET_COMMUTING_ITSET_REDUCTION = store_thm( - "SUBSET_COMMUTING_ITSET_REDUCTION", - ``!f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !(x b)::t. ITSET f s (f x b) = f x (ITSET f s b)``, - completeInduct_on `CARD s` >> - rule_assum_tac(SIMP_RULE bool_ss [GSYM RIGHT_FORALL_IMP_THM, AND_IMP_INTRO]) >> - rw[RES_FORALL_THM] >> - Cases_on `s = {}` >- - rw[ITSET_EMPTY] >> - `?y z. (y = CHOICE s) /\ (z = REST s) /\ (s = y INSERT z)` by rw[CHOICE_INSERT_REST] >> - `y NOTIN z` by metis_tac[CHOICE_NOT_IN_REST] >> - `y IN t` by metis_tac[CHOICE_DEF, SUBSET_DEF] >> - `z SUBSET t` by metis_tac[REST_SUBSET, SUBSET_TRANS] >> - `FINITE z` by metis_tac[REST_SUBSET, SUBSET_FINITE] >> - `CARD s = SUC (CARD z)` by rw[] >> - `CARD z < CARD s` by decide_tac >> - `f x b IN t /\ f y b IN t /\ (f y (f x b) = f x (f y b))` by prove_tac[closure_comm_assoc_fun_def] >> - metis_tac[SUBSET_COMMUTING_ITSET_INSERT, DELETE_NON_ELEMENT]); - -(* This helps to prove the next generalisation. *) - -(* Theorem: FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !(x b):: t. ITSET f (x INSERT s) b = f x (ITSET f (s DELETE x) b) *) -(* Proof: - Note (s DELETE x) SUBSET t by DELETE_SUBSET, SUBSET_TRANS - and FINITE (s DELETE x) by FINITE_DELETE, FINITE s - ITSET f (x INSERT s) b - = ITSET f (s DELETE x) (f x b) by SUBSET_COMMUTING_ITSET_INSERT - = f x (ITSET f (s DELETE x) b) by SUBSET_COMMUTING_ITSET_REDUCTION, (s DELETE x) SUBSET t -*) -val SUBSET_COMMUTING_ITSET_RECURSES = store_thm( - "SUBSET_COMMUTING_ITSET_RECURSES", - ``!f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> - !(x b):: t. ITSET f (x INSERT s) b = f x (ITSET f (s DELETE x) b)``, - rw[RES_FORALL_THM] >> - `(s DELETE x) SUBSET t` by metis_tac[DELETE_SUBSET, SUBSET_TRANS] >> - `FINITE (s DELETE x)` by rw[] >> - metis_tac[SUBSET_COMMUTING_ITSET_INSERT, SUBSET_COMMUTING_ITSET_REDUCTION]); - -(* ------------------------------------------------------------------------- *) -(* SUM_IMAGE and PROD_IMAGE Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: SIGMA f {} = 0 *) -(* Proof: by SUM_IMAGE_THM *) -val SUM_IMAGE_EMPTY = store_thm( - "SUM_IMAGE_EMPTY", - ``!f. SIGMA f {} = 0``, - rw[SUM_IMAGE_THM]); - -(* Theorem: FINITE s ==> !e. e NOTIN s ==> (SIGMA f (e INSERT s) = f e + (SIGMA f s)) *) -(* Proof: - SIGMA f (e INSERT s) - = f e + SIGMA f (s DELETE e) by SUM_IMAGE_THM - = f e + SIGMA f s by DELETE_NON_ELEMENT -*) -val SUM_IMAGE_INSERT = store_thm( - "SUM_IMAGE_INSERT", - ``!f s. FINITE s ==> !e. e NOTIN s ==> (SIGMA f (e INSERT s) = f e + (SIGMA f s))``, - rw[SUM_IMAGE_THM, DELETE_NON_ELEMENT]); - -(* Theorem: FINITE s ==> !f. (!x y. (f x = f y) ==> (x = y)) ==> (SIGMA f s = SIGMA I (IMAGE f s)) *) -(* Proof: - By finite induction on s. - Base case: SIGMA f {} = SIGMA I {} - SIGMA f {} - = 0 by SUM_IMAGE_THM - = SIGMA I {} by SUM_IMAGE_THM - = SUM_SET {} by SUM_SET_DEF - Step case: !f. (!x y. (f x = f y) ==> (x = y)) ==> (SIGMA f s = SUM_SET (IMAGE f s)) ==> - e NOTIN s ==> SIGMA f (e INSERT s) = SUM_SET (f e INSERT IMAGE f s) - Note FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE - and e NOTIN s ==> f e NOTIN f s by the injective property - SIGMA f (e INSERT s) - = f e + SIGMA f (s DELETE e)) by SUM_IMAGE_THM - = f e + SIGMA f s by DELETE_NON_ELEMENT - = f e + SUM_SET (IMAGE f s)) by induction hypothesis - = f e + SUM_SET ((IMAGE f s) DELETE (f e)) by DELETE_NON_ELEMENT, f e NOTIN f s - = SUM_SET (f e INSERT IMAGE f s) by SUM_SET_THM -*) -val SUM_IMAGE_AS_SUM_SET = store_thm( - "SUM_IMAGE_AS_SUM_SET", - ``!s. FINITE s ==> !f. (!x y. (f x = f y) ==> (x = y)) ==> (SIGMA f s = SUM_SET (IMAGE f s))``, - HO_MATCH_MP_TAC FINITE_INDUCT >> - rw[SUM_SET_DEF] >- - rw[SUM_IMAGE_THM] >> - rw[SUM_IMAGE_THM, SUM_IMAGE_DELETE] >> - metis_tac[]); - -(* Theorem: x <> y ==> SIGMA f {x; y} = f x + f y *) -(* Proof: - Let s = {x; y}. - Then FINITE s by FINITE_UNION, FINITE_SING - and x INSERT s by INSERT_DEF - and s DELETE x = {y} by DELETE_DEF - SIGMA f s - = SIGMA f (x INSERT s) by above - = f x + SIGMA f (s DELETE x) by SUM_IMAGE_THM - = f x + SIGMA f {y} by above - = f x + f y by SUM_IMAGE_SING -*) -Theorem SUM_IMAGE_DOUBLET: - !f x y. x <> y ==> SIGMA f {x; y} = f x + f y -Proof - rpt strip_tac >> - qabbrev_tac `s = {x; y}` >> - `FINITE s` by fs[Abbr`s`] >> - `x INSERT s = s` by fs[Abbr`s`] >> - `s DELETE x = {x; y} DELETE x` by simp[Abbr`s`] >> - `_ = if y = x then {} else {y}` by EVAL_TAC >> - `_ = {y}` by simp[] >> - metis_tac[SUM_IMAGE_THM, SUM_IMAGE_SING] -QED - -(* Theorem: x <> y /\ y <> z /\ z <> x ==> SIGMA f {x; y; z} = f x + f y + f z *) -(* Proof: - Let s = {x; y; z}. - Then FINITE s by FINITE_UNION, FINITE_SING - and x INSERT s by INSERT_DEF - and s DELETE x = {y; z} by DELETE_DEF - SIGMA f s - = SIGMA f (x INSERT s) by above - = f x + SIGMA f (s DELETE x) by SUM_IMAGE_THM - = f x + SIGMA f {y; z} by above - = f x + f y + f z by SUM_IMAGE_DOUBLET -*) -Theorem SUM_IMAGE_TRIPLET: - !f x y z. x <> y /\ y <> z /\ z <> x ==> SIGMA f {x; y; z} = f x + f y + f z -Proof - rpt strip_tac >> - qabbrev_tac `s = {x; y; z}` >> - `FINITE s` by fs[Abbr`s`] >> - `x INSERT s = s` by fs[Abbr`s`] >> - `s DELETE x = {x; y; z} DELETE x` by simp[Abbr`s`] >> - `_ = if y = x then if z = x then {} else {z} - else y INSERT if z = x then {} else {z}` by EVAL_TAC >> - `_ = {y; z}` by simp[] >> - `SIGMA f s = f x + (f y + f z)` by metis_tac[SUM_IMAGE_THM, SUM_IMAGE_DOUBLET, SUM_IMAGE_SING] >> - decide_tac -QED - -(* Theorem: FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (SIGMA f s = k * CARD s) *) -(* Proof: - By finite induction on s. - Base: SIGMA f {} = k * CARD {} - SIGMA f {} - = 0 by SUM_IMAGE_EMPTY - = k * 0 by MULT_0 - = k * CARD {} by CARD_EMPTY - Step: SIGMA f s = k * CARD s /\ e NOTIN s /\ !x. x IN e INSERT s /\ f x = k ==> - SIGMA f (e INSERT s) = k * CARD (e INSERT s) - Note f e = k /\ !x. x IN s ==> f x = k by IN_INSERT - SIGMA f (e INSERT s) - = f e + SIGMA f (s DELETE e) by SUM_IMAGE_THM - = k + SIGMA f s by DELETE_NON_ELEMENT, f e = k - = k + k * CARD s by induction hypothesis - = k * (1 + CARD s) by LEFT_ADD_DISTRIB - = k * SUC (CARD s) by SUC_ONE_ADD - = k * CARD (e INSERT s) by CARD_INSERT -*) -val SIGMA_CONSTANT = store_thm( - "SIGMA_CONSTANT", - ``!s. FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (SIGMA f s = k * CARD s)``, - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - rw[SUM_IMAGE_EMPTY] >> - `(f e = k) /\ !x. x IN s ==> (f x = k)` by rw[] >> - `SIGMA f (e INSERT s) = f e + SIGMA f (s DELETE e)` by rw[SUM_IMAGE_THM] >> - `_ = k + SIGMA f s` by metis_tac[DELETE_NON_ELEMENT] >> - `_ = k + k * CARD s` by rw[] >> - `_ = k * (1 + CARD s)` by rw[] >> - `_ = k * SUC (CARD s)` by rw[ADD1] >> - `_ = k * CARD (e INSERT s)` by rw[CARD_INSERT] >> - rw[]); - -(* Theorem: FINITE s ==> !c. SIGMA (K c) s = c * CARD s *) -(* Proof: by SIGMA_CONSTANT. *) -val SUM_IMAGE_CONSTANT = store_thm( - "SUM_IMAGE_CONSTANT", - ``!s. FINITE s ==> !c. SIGMA (K c) s = c * CARD s``, - rw[SIGMA_CONSTANT]); - -(* Idea: If !e. e IN s, CARD e = n, SIGMA CARD s = n * CARD s. *) - -(* Theorem: FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s *) -(* Proof: by SIGMA_CONSTANT, take f = CARD. *) -Theorem SIGMA_CARD_CONSTANT: - !n s. FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s -Proof - simp[SIGMA_CONSTANT] -QED - -(* Theorem alias, or rename SIGMA_CARD_CONSTANT *) -Theorem SIGMA_CARD_SAME_SIZE_SETS = SIGMA_CARD_CONSTANT; -(* val SIGMA_CARD_SAME_SIZE_SETS = - |- !n s. FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s: thm *) -(* -CARD_BIGUNION_SAME_SIZED_SETS -|- !n s. FINITE s /\ (!e. e IN s ==> FINITE e /\ CARD e = n) /\ - PAIR_DISJOINT s ==> CARD (BIGUNION s) = CARD s * n -*) - -(* Theorem: If n divides CARD e for all e in s, then n divides SIGMA CARD s. - FINITE s /\ (!e. e IN s ==> n divides (CARD e)) ==> n divides (SIGMA CARD s) *) -(* Proof: - Use finite induction and SUM_IMAGE_THM. - Base: n divides SIGMA CARD {} - Note SIGMA CARD {} = 0 by SUM_IMAGE_THM - and n divides 0 by ALL_DIVIDES_0 - Step: e NOTIN s /\ n divides (CARD e) ==> n divides SIGMA CARD (e INSERT s) - SIGMA CARD (e INSERT s) - = CARD e + SIGMA CARD (s DELETE e) by SUM_IMAGE_THM - = CARD e + SIGMA CARD s by DELETE_NON_ELEMENT - Note n divides (CARD e) by given - and n divides SIGMA CARD s by induction hypothesis - Thus n divides SIGMA CARD (e INSERT s) by DIVIDES_ADD_1 -*) -Theorem SIGMA_CARD_FACTOR: - !n s. FINITE s /\ (!e. e IN s ==> n divides (CARD e)) ==> n divides (SIGMA CARD s) -Proof - strip_tac >> - Induct_on `FINITE` >> - rw[] >- - rw[SUM_IMAGE_THM] >> - metis_tac[SUM_IMAGE_THM, DELETE_NON_ELEMENT, DIVIDES_ADD_1] -QED - -(* Theorem: (!x. x IN s ==> (f1 x = f2 x)) ==> (SIGMA f1 s = SIGMA f2 s) *) -val SIGMA_CONG = store_thm( - "SIGMA_CONG", - ``!s f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (SIGMA f1 s = SIGMA f2 s)``, - rw[SUM_IMAGE_CONG]); - -(* Theorem: FINITE s ==> (CARD s = SIGMA (\x. 1) s) *) -(* Proof: - By finite induction: - Base case: CARD {} = SIGMA (\x. 1) {} - LHS = CARD {} - = 0 by CARD_EMPTY - RHS = SIGMA (\x. 1) {} - = 0 = LHS by SUM_IMAGE_THM - Step case: (CARD s = SIGMA (\x. 1) s) ==> - !e. e NOTIN s ==> (CARD (e INSERT s) = SIGMA (\x. 1) (e INSERT s)) - CARD (e INSERT s) - = SUC (CARD s) by CARD_DEF - = SUC (SIGMA (\x. 1) s) by induction hypothesis - = 1 + SIGMA (\x. 1) s by ADD1, ADD_COMM - = (\x. 1) e + SIGMA (\x. 1) s by function application - = (\x. 1) e + SIGMA (\x. 1) (s DELETE e) by DELETE_NON_ELEMENT - = SIGMA (\x. 1) (e INSERT s) by SUM_IMAGE_THM -*) -val CARD_AS_SIGMA = store_thm( - "CARD_AS_SIGMA", - ``!s. FINITE s ==> (CARD s = SIGMA (\x. 1) s)``, - ho_match_mp_tac FINITE_INDUCT >> - conj_tac >- - rw[SUM_IMAGE_THM] >> - rpt strip_tac >> - `CARD (e INSERT s) = SUC (SIGMA (\x. 1) s)` by rw[] >> - `_ = 1 + SIGMA (\x. 1) s` by rw_tac std_ss[ADD1, ADD_COMM] >> - `_ = (\x. 1) e + SIGMA (\x. 1) s` by rw[] >> - `_ = (\x. 1) e + SIGMA (\x. 1) (s DELETE e)` by metis_tac[DELETE_NON_ELEMENT] >> - `_ = SIGMA (\x. 1) (e INSERT s)` by rw[SUM_IMAGE_THM] >> - decide_tac); - -(* Theorem: FINITE s ==> (CARD s = SIGMA (K 1) s) *) -(* Proof: by CARD_AS_SIGMA, SIGMA_CONG *) -val CARD_EQ_SIGMA = store_thm( - "CARD_EQ_SIGMA", - ``!s. FINITE s ==> (CARD s = SIGMA (K 1) s)``, - rw[CARD_AS_SIGMA, SIGMA_CONG]); - -(* Theorem: FINITE s ==> !f g. (!x. x IN s ==> f x <= g x) ==> (SIGMA f s <= SIGMA g s) *) -(* Proof: - By finite induction: - Base case: !f g. (!x. x IN {} ==> f x <= g x) ==> SIGMA f {} <= SIGMA g {} - True since SIGMA f {} = 0 by SUM_IMAGE_THM - Step case: !f g. (!x. x IN s ==> f x <= g x) ==> SIGMA f s <= SIGMA g s ==> - e NOTIN s ==> - !x. x IN e INSERT s ==> f x <= g x ==> !f g. SIGMA f (e INSERT s) <= SIGMA g (e INSERT s) - SIGMA f (e INSERT s) <= SIGMA g (e INSERT s) - means f e + SIGMA f (s DELETE e) <= g e + SIGMA g (s DELETE e) by SUM_IMAGE_THM - or f e + SIGMA f s <= g e + SIGMA g s by DELETE_NON_ELEMENT - Now, x IN e INSERT s ==> (x = e) or (x IN s) by IN_INSERT - Therefore f e <= g e, and !x IN s, f x <= g x by x IN (e INSERT s) implication - or f e <= g e, and SIGMA f s <= SIGMA g s by induction hypothesis - Hence f e + SIGMA f s <= g e + SIGMA g s by arithmetic -*) -val SIGMA_LE_SIGMA = store_thm( - "SIGMA_LE_SIGMA", - ``!s. FINITE s ==> !f g. (!x. x IN s ==> f x <= g x) ==> (SIGMA f s <= SIGMA g s)``, - ho_match_mp_tac FINITE_INDUCT >> - conj_tac >- - rw[SUM_IMAGE_THM] >> - rw[SUM_IMAGE_THM, DELETE_NON_ELEMENT] >> - `f e <= g e /\ SIGMA f s <= SIGMA g s` by rw[] >> - decide_tac); - -(* Theorem: FINITE s /\ FINITE t ==> !f. SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t *) -(* Proof: - Note SIGMA f (s UNION t) - = SIGMA f s + SIGMA f t - SIGMA f (s INTER t) by SUM_IMAGE_UNION - now s INTER t SUBSET s /\ s INTER t SUBSET t by INTER_SUBSET - so SIGMA f (s INTER t) <= SIGMA f s by SUM_IMAGE_SUBSET_LE - and SIGMA f (s INTER t) <= SIGMA f t by SUM_IMAGE_SUBSET_LE - thus SIGMA f (s INTER t) <= SIGMA f s + SIGMA f t by arithmetic - The result follows by ADD_EQ_SUB -*) -val SUM_IMAGE_UNION_EQN = store_thm( - "SUM_IMAGE_UNION_EQN", - ``!s t. FINITE s /\ FINITE t ==> !f. SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t``, - rpt strip_tac >> - `SIGMA f (s UNION t) = SIGMA f s + SIGMA f t - SIGMA f (s INTER t)` by rw[SUM_IMAGE_UNION] >> - `SIGMA f (s INTER t) <= SIGMA f s` by rw[SUM_IMAGE_SUBSET_LE, INTER_SUBSET] >> - `SIGMA f (s INTER t) <= SIGMA f t` by rw[SUM_IMAGE_SUBSET_LE, INTER_SUBSET] >> - `SIGMA f (s INTER t) <= SIGMA f s + SIGMA f t` by decide_tac >> - rw[ADD_EQ_SUB]); - -(* Theorem: FINITE s /\ FINITE t /\ DISJOINT s t ==> !f. SIGMA f (s UNION t) = SIGMA f s + SIGMA f t *) -(* Proof: - SIGMA f (s UNION t) - = SIGMA f s + SIGMA f t - SIGMA f (s INTER t) by SUM_IMAGE_UNION - = SIGMA f s + SIGMA f t - SIGMA f {} by DISJOINT_DEF - = SIGMA f s + SIGMA f t - 0 by SUM_IMAGE_EMPTY - = SIGMA f s + SIGMA f t by arithmetic -*) -val SUM_IMAGE_DISJOINT = store_thm( - "SUM_IMAGE_DISJOINT", - ``!s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> !f. SIGMA f (s UNION t) = SIGMA f s + SIGMA f t``, - rw_tac std_ss[SUM_IMAGE_UNION, DISJOINT_DEF, SUM_IMAGE_EMPTY]); - -(* Theorem: FINITE s /\ s <> {} ==> !f g. (!x. x IN s ==> f x < g x) ==> SIGMA f s < SIGMA g s *) -(* Proof: - Note s <> {} ==> ?x. x IN s by MEMBER_NOT_EMPTY - Thus ?x. x IN s /\ f x < g x by implication - and !x. x IN s ==> f x <= g x by LESS_IMP_LESS_OR_EQ - ==> SIGMA f s < SIGMA g s by SUM_IMAGE_MONO_LESS -*) -val SUM_IMAGE_MONO_LT = store_thm( - "SUM_IMAGE_MONO_LT", - ``!s. FINITE s /\ s <> {} ==> !f g. (!x. x IN s ==> f x < g x) ==> SIGMA f s < SIGMA g s``, - metis_tac[SUM_IMAGE_MONO_LESS, LESS_IMP_LESS_OR_EQ, MEMBER_NOT_EMPTY]); - -(* Theorem: FINITE s /\ t PSUBSET s /\ (!x. x IN s ==> f x <> 0) ==> SIGMA f t < SIGMA f s *) -(* Proof: - Note t SUBSET s /\ t <> s by PSUBSET_DEF - Thus SIGMA f t <= SIGMA f s by SUM_IMAGE_SUBSET_LE - By contradiction, suppose ~(SIGMA f t < SIGMA f s). - Then SIGMA f t = SIGMA f s by arithmetic [1] - - Let u = s DIFF t. - Then DISJOINT u t by DISJOINT_DIFF - and u UNION t = s by UNION_DIFF - Note FINITE u /\ FINITE t by FINITE_UNION - ==> SIGMA f s = SIGMA f u + SIGMA f t by SUM_IMAGE_DISJOINT - Thus SIGMA f u = 0 by arithmetic, [1] - - Now u SUBSET s by SUBSET_UNION - and u <> {} by finite_partition_property, t <> s - Thus ?x. x IN u by MEMBER_NOT_EMPTY - and f x <> 0 by SUBSET_DEF, implication - This contradicts f x = 0 by SUM_IMAGE_ZERO -*) -val SUM_IMAGE_PSUBSET_LT = store_thm( - "SUM_IMAGE_PSUBSET_LT", - ``!f s t. FINITE s /\ t PSUBSET s /\ (!x. x IN s ==> f x <> 0) ==> SIGMA f t < SIGMA f s``, - rw[PSUBSET_DEF] >> - `SIGMA f t <= SIGMA f s` by rw[SUM_IMAGE_SUBSET_LE] >> - spose_not_then strip_assume_tac >> - `SIGMA f t = SIGMA f s` by decide_tac >> - qabbrev_tac `u = s DIFF t` >> - `DISJOINT u t` by rw[DISJOINT_DIFF, Abbr`u`] >> - `u UNION t = s` by rw[UNION_DIFF, Abbr`u`] >> - `FINITE u /\ FINITE t` by metis_tac[FINITE_UNION] >> - `SIGMA f s = SIGMA f u + SIGMA f t` by rw[GSYM SUM_IMAGE_DISJOINT] >> - `SIGMA f u = 0` by decide_tac >> - `u SUBSET s` by rw[] >> - `u <> {}` by metis_tac[finite_partition_property] >> - metis_tac[SUM_IMAGE_ZERO, SUBSET_DEF, MEMBER_NOT_EMPTY]); - -(* Idea: Let s be a set of sets. If CARD s = SIGMA CARD s, - and all elements in s are non-empty, then all elements in s are SING. *) - -(* Theorem: FINITE s /\ (!e. e IN s ==> CARD e <> 0) ==> CARD s <= SIGMA CARD s *) -(* Proof: - By finite induction on set s. - Base: (!e. e IN {} ==> CARD e <> 0) ==> CARD {} <= SIGMA CARD {} - LHS = CARD {} = 0 by CARD_EMPTY - RHS = SIGMA CARD {} = 0 by SUM_IMAGE_EMPTY - Hence true. - Step: FINITE s /\ ((!e. e IN s ==> CARD e <> 0) ==> CARD s <= SIGMA CARD s) ==> - !e. e NOTIN s ==> - (!e'. e' IN e INSERT s ==> CARD e' <> 0) ==> - CARD (e INSERT s) <= SIGMA CARD (e INSERT s) - - Note !e'. e' IN s - ==> e' IN e INSERT s by IN_INSERT, e NOTIN s - ==> CARD e' <> 0 by implication, so induction hypothesis applies. - and CARD e <> 0 by e IN e INSERT s - CARD (e INSERT s) - = SUC (CARD s) by CARD_INSERT, e NOTIN s - = 1 + CARD s by SUC_ONE_ADD - - <= 1 + SIGMA CARD s by induction hypothesis - <= CARD e + SIGMA CARD s by 1 <= CARD e - = SIGMA (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s. -*) -Theorem card_le_sigma_card: - !s. FINITE s /\ (!e. e IN s ==> CARD e <> 0) ==> CARD s <= SIGMA CARD s -Proof - Induct_on `FINITE` >> - rw[] >> - `CARD e <> 0` by fs[] >> - `1 <= CARD e` by decide_tac >> - fs[] >> - simp[SUM_IMAGE_INSERT] -QED - -(* Theorem: FINITE s /\ (!e. e IN s ==> CARD e <> 0) /\ - CARD s = SIGMA CARD s ==> !e. e IN s ==> CARD e = 1 *) -(* Proof: - By finite induction on set s. - Base: (!e. e IN {} ==> CARD e <> 0) /\ CARD {} = SIGMA CARD {} ==> - !e. e IN {} ==> CARD e = 1 - Since e IN {} = F, this is trivially true. - Step: !s. FINITE s /\ - ((!e. e IN s ==> CARD e <> 0) /\ CARD s = SIGMA CARD s ==> - !e. e IN s ==> CARD e = 1) ==> - !e. e NOTIN s ==> - (!e'. e' IN e INSERT s ==> CARD e' <> 0) /\ - CARD (e INSERT s) = SIGMA CARD (e INSERT s) ==> - !e'. e' IN e INSERT s ==> CARD e' = 1 - Note !e'. e' IN s - ==> e' IN e INSERT s by IN_INSERT, e NOTIN s - ==> CARD e' <> 0 by implication, helps in induction hypothesis - Also e IN e INSERT s by IN_INSERT - so CARD e <> 0 by implication - - CARD e + CARD s - <= CARD e + SIGMA CARD s by card_le_sigma_card - = SIGMA CARD (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s - = CARD (e INSERT s) by given - = SUC (CARD s) by CARD_INSERT, e NOTIN s - = 1 + CARD s by SUC_ONE_ADD - Thus CARD e <= 1 by arithmetic - or CARD e = 1 by CARD e <> 0 - ==> CARD s = SIGMA CARD s by arithmetic, helps in induction hypothesis - Thus !e. e IN s ==> CARD e = 1 by induction hypothesis - and !e'. e' IN e INSERT s ==> CARD e' = 1 by CARD e = 1 -*) -Theorem card_eq_sigma_card: - !s. FINITE s /\ (!e. e IN s ==> CARD e <> 0) /\ - CARD s = SIGMA CARD s ==> !e. e IN s ==> CARD e = 1 -Proof - Induct_on `FINITE` >> - simp[] >> - ntac 6 strip_tac >> - `CARD e <> 0 /\ !e. e IN s ==> CARD e <> 0` by fs[] >> - imp_res_tac card_le_sigma_card >> - `CARD e + CARD s <= CARD e + SIGMA CARD s` by decide_tac >> - `CARD e + SIGMA CARD s = SIGMA CARD (e INSERT s)` by fs[SUM_IMAGE_INSERT] >> - `_ = 1 + CARD s` by rw[] >> - `CARD e <= 1` by fs[] >> - `CARD e = 1` by decide_tac >> - `CARD s = SIGMA CARD s` by fs[] >> - metis_tac[] -QED - -(* ------------------------------------------------------------------------- *) - -(* Theorem: PI f {} = 1 *) -(* Proof: by PROD_IMAGE_THM *) -val PROD_IMAGE_EMPTY = store_thm( - "PROD_IMAGE_EMPTY", - ``!f. PI f {} = 1``, - rw[PROD_IMAGE_THM]); - -(* Theorem: FINITE s ==> !f e. e NOTIN s ==> (PI f (e INSERT s) = (f e) * PI f s) *) -(* Proof: by PROD_IMAGE_THM, DELETE_NON_ELEMENT *) -val PROD_IMAGE_INSERT = store_thm( - "PROD_IMAGE_INSERT", - ``!s. FINITE s ==> !f e. e NOTIN s ==> (PI f (e INSERT s) = (f e) * PI f s)``, - rw[PROD_IMAGE_THM, DELETE_NON_ELEMENT]); - -(* Theorem: FINITE s ==> !f e. 0 < f e ==> - (PI f (s DELETE e) = if e IN s then ((PI f s) DIV (f e)) else PI f s) *) -(* Proof: - If e IN s, - Note PI f (e INSERT s) = (f e) * PI f (s DELETE e) by PROD_IMAGE_THM - Thus PI f (s DELETE e) = PI f (e INSERT s) DIV (f e) by DIV_SOLVE_COMM, 0 < f e - = (PI f s) DIV (f e) by ABSORPTION, e IN s. - If e NOTIN s, - PI f (s DELETE e) = PI f e by DELETE_NON_ELEMENT -*) -val PROD_IMAGE_DELETE = store_thm( - "PROD_IMAGE_DELETE", - ``!s. FINITE s ==> !f e. 0 < f e ==> - (PI f (s DELETE e) = if e IN s then ((PI f s) DIV (f e)) else PI f s)``, - rpt strip_tac >> - rw_tac std_ss[] >- - metis_tac[PROD_IMAGE_THM, DIV_SOLVE_COMM, ABSORPTION] >> - metis_tac[DELETE_NON_ELEMENT]); -(* The original proof of SUM_IMAGE_DELETE is clumsy. *) - -(* Theorem: (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s) *) -(* Proof: - If INFINITE s, - PI f1 s - = ITSET (\e acc. f e * acc) s 1 by PROD_IMAGE_DEF - = ARB by ITSET_def - Similarly, PI f2 s = ARB = PI f1 s. - If FINITE s, - Apply finite induction on s. - Base: PI f1 {} = PI f2 {}, true by PROD_IMAGE_EMPTY - Step: !f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s) ==> - e NOTIN s /\ !x. x IN e INSERT s ==> (f1 x = f2 x) ==> PI f1 (e INSERT s) = PI f2 (e INSERT s) - Note !x. x IN e INSERT s ==> (f1 x = f2 x) - ==> (f1 e = f2 e) \/ !x. s IN s ==> (f1 x = f2 x) by IN_INSERT - PI f1 (e INSERT s) - = (f1 e) * (PI f1 s) by PROD_IMAGE_INSERT, e NOTIN s - = (f1 e) * (PI f2 s) by induction hypothesis - = (f2 e) * (PI f2 s) by f1 e = f2 e - = PI f2 (e INSERT s) by PROD_IMAGE_INSERT, e NOTIN s -*) -val PROD_IMAGE_CONG = store_thm( - "PROD_IMAGE_CONG", - ``!s f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s)``, - rpt strip_tac >> - reverse (Cases_on `FINITE s`) >| [ - rw[PROD_IMAGE_DEF, Once ITSET_def] >> - rw[Once ITSET_def], - pop_assum mp_tac >> - pop_assum mp_tac >> - qid_spec_tac `s` >> - `!s. FINITE s ==> !f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s)` suffices_by rw[] >> - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_IMAGE_EMPTY] >> - metis_tac[PROD_IMAGE_INSERT, IN_INSERT] - ]); - -(* Theorem: FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s) *) -(* Proof: - By finite induction on s. - Base: PI f {} = k ** CARD {} - PI f {} - = 1 by PROD_IMAGE_THM - = c ** 0 by EXP - = c ** CARD {} by CARD_DEF - Step: !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s) ==> - e NOTIN s ==> PI f (e INSERT s) = k ** CARD (e INSERT s) - PI f (e INSERT s) - = ((f e) * PI (K c) (s DELETE e) by PROD_IMAGE_THM - = c * PI (K c) (s DELETE e) by function application - = c * PI (K c) s by DELETE_NON_ELEMENT - = c * c ** CARD s by induction hypothesis - = c ** (SUC (CARD s)) by EXP - = c ** CARD (e INSERT s) by CARD_INSERT, e NOTIN s -*) -val PI_CONSTANT = store_thm( - "PI_CONSTANT", - ``!s. FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_IMAGE_THM] >> - rw[PROD_IMAGE_THM, CARD_INSERT] >> - fs[] >> - metis_tac[DELETE_NON_ELEMENT, EXP]); - -(* Theorem: FINITE s ==> !c. PI (K c) s = c ** (CARD s) *) -(* Proof: by PI_CONSTANT. *) -val PROD_IMAGE_CONSTANT = store_thm( - "PROD_IMAGE_CONSTANT", - ``!s. FINITE s ==> !c. PI (K c) s = c ** (CARD s)``, - rw[PI_CONSTANT]); - -(* ------------------------------------------------------------------------- *) -(* SUM_SET and PROD_SET Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: FINITE s /\ x NOTIN s ==> (SUM_SET (x INSERT s) = x + SUM_SET s) *) -(* Proof: - SUM_SET (x INSERT s) - = x + SUM_SET (s DELETE x) by SUM_SET_THM - = x + SUM_SET s by DELETE_NON_ELEMENT -*) -val SUM_SET_INSERT = store_thm( - "SUM_SET_INSERT", - ``!s x. FINITE s /\ x NOTIN s ==> (SUM_SET (x INSERT s) = x + SUM_SET s)``, - rw[SUM_SET_THM, DELETE_NON_ELEMENT]); - -(* Theorem: FINITE s ==> !f. INJ f s UNIV ==> (SUM_SET (IMAGE f s) = SIGMA f s) *) -(* Proof: - By finite induction on s. - Base: SUM_SET (IMAGE f {}) = SIGMA f {} - SUM_SET (IMAGE f {}) - = SUM_SET {} by IMAGE_EMPTY - = 1 by SUM_SET_EMPTY - = SIGMA f {} by SUM_IMAGE_EMPTY - Step: !f. INJ f s univ(:num) ==> (SUM_SET (IMAGE f s) = SIGMA f s) ==> - e NOTIN s /\ INJ f (e INSERT s) univ(:num) ==> SUM_SET (IMAGE f (e INSERT s)) = SIGMA f (e INSERT s) - Note INJ f s univ(:num) by INJ_INSERT - and f e NOTIN (IMAGE f s) by IN_IMAGE - SUM_SET (IMAGE f (e INSERT s)) - = SUM_SET (f e INSERT (IMAGE f s)) by IMAGE_INSERT - = f e * SUM_SET (IMAGE f s) by SUM_SET_INSERT - = f e * SIGMA f s by induction hypothesis - = SIGMA f (e INSERT s) by SUM_IMAGE_INSERT -*) -val SUM_SET_IMAGE_EQN = store_thm( - "SUM_SET_IMAGE_EQN", - ``!s. FINITE s ==> !f. INJ f s UNIV ==> (SUM_SET (IMAGE f s) = SIGMA f s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[SUM_SET_EMPTY, SUM_IMAGE_EMPTY] >> - fs[INJ_INSERT] >> - `f e NOTIN (IMAGE f s)` by metis_tac[IN_IMAGE] >> - rw[SUM_SET_INSERT, SUM_IMAGE_INSERT]); - -(* Theorem: SUM_SET (count n) = (n * (n - 1)) DIV 2*) -(* Proof: - By induction on n. - Base case: SUM_SET (count 0) = 0 * (0 - 1) DIV 2 - LHS = SUM_SET (count 0) - = SUM_SET {} by COUNT_ZERO - = 0 by SUM_SET_THM - = 0 DIV 2 by ZERO_DIV - = 0 * (0 - 1) DIV 2 by MULT - = RHS - Step case: SUM_SET (count n) = n * (n - 1) DIV 2 ==> - SUM_SET (count (SUC n)) = SUC n * (SUC n - 1) DIV 2 - If n = 0, to show: SUM_SET (count 1) = 0 - SUM_SET (count 1) = SUM_SET {0} = 0 by SUM_SET_SING - If n <> 0, 0 < n. - First, FINITE (count n) by FINITE_COUNT - n NOTIN (count n) by IN_COUNT - LHS = SUM_SET (count (SUC n)) - = SUM_SET (n INSERT count n) by COUNT_SUC - = n + SUM_SET (count n DELETE n) by SUM_SET_THM - = n + SUM_SET (count n) by DELETE_NON_ELEMENT - = n + n * (n - 1) DIV 2 by induction hypothesis - = (n * 2 + n * (n - 1)) DIV 2 by ADD_DIV_ADD_DIV - = (n * (2 + (n - 1))) DIV 2 by LEFT_ADD_DISTRIB - = n * (n + 1) DIV 2 by arithmetic, 0 < n - = (SUC n - 1) * (SUC n) DIV 2 by ADD1, SUC_SUB1 - = SUC n * (SUC n - 1) DIV 2 by MULT_COMM - = RHS -*) -val SUM_SET_COUNT = store_thm( - "SUM_SET_COUNT", - ``!n. SUM_SET (count n) = (n * (n - 1)) DIV 2``, - Induct_on `n` >- - rw[] >> - Cases_on `n = 0` >| [ - rw[] >> - EVAL_TAC, - `0 < n` by decide_tac >> - `FINITE (count n)` by rw[] >> - `n NOTIN (count n)` by rw[] >> - `SUM_SET (count (SUC n)) = SUM_SET (n INSERT count n)` by rw[COUNT_SUC] >> - `_ = n + SUM_SET (count n DELETE n)` by rw[SUM_SET_THM] >> - `_ = n + SUM_SET (count n)` by metis_tac[DELETE_NON_ELEMENT] >> - `_ = n + n * (n - 1) DIV 2` by rw[] >> - `_ = (n * 2 + n * (n - 1)) DIV 2` by rw[ADD_DIV_ADD_DIV] >> - `_ = (n * (2 + (n - 1))) DIV 2` by rw[LEFT_ADD_DISTRIB] >> - `_ = n * (n + 1) DIV 2` by rw_tac arith_ss[] >> - `_ = (SUC n - 1) * SUC n DIV 2` by rw[ADD1, SUC_SUB1] >> - `_ = SUC n * (SUC n - 1) DIV 2 ` by rw[MULT_COMM] >> - decide_tac - ]); - -(* ------------------------------------------------------------------------- *) - -(* Theorem: PROD_SET {x} = x *) -(* Proof: - Since FINITE {x} by FINITE_SING - PROD_SET {x} - = PROD_SET (x INSERT {}) by SING_INSERT - = x * PROD_SET {} by PROD_SET_THM - = x by PROD_SET_EMPTY -*) -val PROD_SET_SING = store_thm( - "PROD_SET_SING", - ``!x. PROD_SET {x} = x``, - rw[PROD_SET_THM, FINITE_SING]); - -(* Theorem: FINITE s /\ 0 NOTIN s ==> 0 < PROD_SET s *) -(* Proof: - By FINITE_INDUCT on s. - Base case: 0 NOTIN {} ==> 0 < PROD_SET {} - Since PROD_SET {} = 1 by PROD_SET_THM - Hence true. - Step case: 0 NOTIN s ==> 0 < PROD_SET s ==> - e NOTIN s /\ 0 NOTIN e INSERT s ==> 0 < PROD_SET (e INSERT s) - PROD_SET (e INSERT s) - = e * PROD_SET (s DELETE e) by PROD_SET_THM - = e * PROD_SET s by DELETE_NON_ELEMENT - But e IN e INSERT s by COMPONENT - Hence e <> 0, or 0 < e by implication - and !x. x IN s ==> x IN (e INSERT s) by IN_INSERT - Thus 0 < PROD_SET s by induction hypothesis - Henec 0 < e * PROD_SET s by ZERO_LESS_MULT -*) -val PROD_SET_NONZERO = store_thm( - "PROD_SET_NONZERO", - ``!s. FINITE s /\ 0 NOTIN s ==> 0 < PROD_SET s``, - `!s. FINITE s ==> 0 NOTIN s ==> 0 < PROD_SET s` suffices_by rw[] >> - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - rw[PROD_SET_THM] >> - fs[] >> - `0 < e` by decide_tac >> - `PROD_SET (e INSERT s) = e * PROD_SET (s DELETE e)` by rw[PROD_SET_THM] >> - `_ = e * PROD_SET s` by metis_tac[DELETE_NON_ELEMENT] >> - rw[ZERO_LESS_MULT]); - -(* Theorem: FINITE s /\ s <> {} /\ 0 NOTIN s ==> - !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s) *) -(* Proof: - By FINITE_INDUCT on s. - Base case: {} <> {} ==> PROD_SET {} < PROD_SET (IMAGE f {}) - True since {} <> {} is false. - Step case: s <> {} /\ 0 NOTIN s ==> !f. INJ f s univ(:num) ==> PROD_SET s < PROD_SET (IMAGE f s) ==> - e NOTIN s /\ e INSERT s <> {} /\ 0 NOTIN e INSERT s /\ INJ f (e INSERT s) univ(:num) ==> - PROD_SET (e INSERT s) < PROD_SET (IMAGE f (e INSERT s)) - Note INJ f (e INSERT s) univ(:num) - ==> INJ f s univ(:num) /\ - !y. y IN s /\ (f e = f y) ==> (e = y) by INJ_INSERT - First, - PROD_SET (e INSERT s) - = e * PROD_SET (s DELETE e) by PROD_SET_THM - = e * PROD_SET s by DELETE_NON_ELEMENT - Next, - FINITE (IMAGE f s) by IMAGE_FINITE - f e NOTIN IMAGE f s by IN_IMAGE, e NOTIN s - PROD_SET (IMAGE f (e INSERT s)) - = f e * PROD_SET (IMAGE f s) by PROD_SET_IMAGE_REDUCTION - - If s = {}, - to show: e * PROD_SET {} < f e * PROD_SET {} by IMAGE_EMPTY - which is true since PROD_SET {} = 1 by PROD_SET_THM - and e < f e by given - If s <> {}, - Since e IN e INSERT s by COMPONENT - Hence 0 < e by e <> 0 - and !x. x IN s ==> x IN (e INSERT s) by IN_INSERT - Thus PROD_SET s < PROD_SET (IMAGE f s) by induction hypothesis - or e * PROD_SET s < e * PROD_SET (IMAGE f s) by LT_MULT_LCANCEL, 0 < e - Note 0 < PROD_SET (IMAGE f s) by IN_IMAGE, !x. x < f x /\ x <> 0 - so e * PROD_SET (IMAGE f s) < f e * PROD_SET (IMAGE f s) by LT_MULT_LCANCEL, e < f e - Hence PROD_SET (e INSERT s) < PROD_SET (IMAGE f (e INSERT s)) -*) -val PROD_SET_LESS = store_thm( - "PROD_SET_LESS", - ``!s. FINITE s /\ s <> {} /\ 0 NOTIN s ==> - !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s)``, - `!s. FINITE s ==> s <> {} /\ 0 NOTIN s ==> - !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s)` suffices_by rw[] >> - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - rw[] >> - `PROD_SET (e INSERT s) = e * PROD_SET (s DELETE e)` by rw[PROD_SET_THM] >> - `_ = e * PROD_SET s` by metis_tac[DELETE_NON_ELEMENT] >> - fs[INJ_INSERT] >> - `FINITE (IMAGE f s)` by rw[] >> - `f e NOTIN IMAGE f s` by metis_tac[IN_IMAGE] >> - `PROD_SET (IMAGE f (e INSERT s)) = f e * PROD_SET (IMAGE f s)` by rw[PROD_SET_IMAGE_REDUCTION] >> - Cases_on `s = {}` >- - rw[PROD_SET_SING, PROD_SET_THM] >> - `0 < e` by decide_tac >> - `PROD_SET s < PROD_SET (IMAGE f s)` by rw[] >> - `e * PROD_SET s < e * PROD_SET (IMAGE f s)` by rw[] >> - `e * PROD_SET (IMAGE f s) < (f e) * PROD_SET (IMAGE f s)` by rw[] >> - `(IMAGE f (e INSERT s)) = (f e INSERT IMAGE f s)` by rw[] >> - metis_tac[LESS_TRANS]); - -(* Theorem: FINITE s /\ s <> {} /\ 0 NOTIN s ==> PROD_SET s < PROD_SET (IMAGE SUC s) *) -(* Proof: - Since !m n. SUC m = SUC n <=> m = n by INV_SUC - thus INJ INJ SUC s univ(:num) by INJ_DEF - Hence the result follows by PROD_SET_LESS -*) -val PROD_SET_LESS_SUC = store_thm( - "PROD_SET_LESS_SUC", - ``!s. FINITE s /\ s <> {} /\ 0 NOTIN s ==> PROD_SET s < PROD_SET (IMAGE SUC s)``, - rpt strip_tac >> - (irule PROD_SET_LESS >> simp[]) >> - rw[INJ_DEF]); - -(* Theorem: FINITE s ==> !n x. x IN s /\ n divides x ==> n divides (PROD_SET s) *) -(* Proof: - By FINITE_INDUCT on s. - Base case: x IN {} /\ n divides x ==> n divides (PROD_SET {}) - True since x IN {} is false by NOT_IN_EMPTY - Step case: !n x. x IN s /\ n divides x ==> n divides (PROD_SET s) ==> - e NOTIN s /\ x IN e INSERT s /\ n divides x ==> n divides (PROD_SET (e INSERT s)) - PROD_SET (e INSERT s) - = e * PROD_SET (s DELETE e) by PROD_SET_THM - = e * PROD_SET s by DELETE_NON_ELEMENT - If x = e, - n divides x - means n divides e - hence n divides PROD_SET (e INSERT s) by DIVIDES_MULTIPLE, MULT_COMM - If x <> e, x IN s by IN_INSERT - n divides (PROD_SET s) by induction hypothesis - hence n divides PROD_SET (e INSERT s) by DIVIDES_MULTIPLE -*) -val PROD_SET_DIVISORS = store_thm( - "PROD_SET_DIVISORS", - ``!s. FINITE s ==> !n x. x IN s /\ n divides x ==> n divides (PROD_SET s)``, - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - metis_tac[NOT_IN_EMPTY] >> - `PROD_SET (e INSERT s) = e * PROD_SET (s DELETE e)` by rw[PROD_SET_THM] >> - `_ = e * PROD_SET s` by metis_tac[DELETE_NON_ELEMENT] >> - `(x = e) \/ (x IN s)` by rw[GSYM IN_INSERT] >- - metis_tac[DIVIDES_MULTIPLE, MULT_COMM] >> - metis_tac[DIVIDES_MULTIPLE]); - -(* PROD_SET_IMAGE_REDUCTION |> ISPEC ``I:num -> num``; *) - -(* Theorem: FINITE s /\ x NOTIN s ==> (PROD_SET (x INSERT s) = x * PROD_SET s) *) -(* Proof: - Since !x. I x = x by I_THM - and !s. IMAGE I s = s by IMAGE_I - thus the result follows by PROD_SET_IMAGE_REDUCTION -*) -val PROD_SET_INSERT = store_thm( - "PROD_SET_INSERT", - ``!x s. FINITE s /\ x NOTIN s ==> (PROD_SET (x INSERT s) = x * PROD_SET s)``, - metis_tac[PROD_SET_IMAGE_REDUCTION, combinTheory.I_THM, IMAGE_I]); - -(* Theorem: FINITE s ==> !f. INJ f s UNIV ==> (PROD_SET (IMAGE f s) = PI f s) *) -(* Proof: - By finite induction on s. - Base: PROD_SET (IMAGE f {}) = PI f {} - PROD_SET (IMAGE f {}) - = PROD_SET {} by IMAGE_EMPTY - = 1 by PROD_SET_EMPTY - = PI f {} by PROD_IMAGE_EMPTY - Step: !f. INJ f s univ(:num) ==> (PROD_SET (IMAGE f s) = PI f s) ==> - e NOTIN s /\ INJ f (e INSERT s) univ(:num) ==> PROD_SET (IMAGE f (e INSERT s)) = PI f (e INSERT s) - Note INJ f s univ(:num) by INJ_INSERT - and f e NOTIN (IMAGE f s) by IN_IMAGE - PROD_SET (IMAGE f (e INSERT s)) - = PROD_SET (f e INSERT (IMAGE f s)) by IMAGE_INSERT - = f e * PROD_SET (IMAGE f s) by PROD_SET_INSERT - = f e * PI f s by induction hypothesis - = PI f (e INSERT s) by PROD_IMAGE_INSERT -*) -val PROD_SET_IMAGE_EQN = store_thm( - "PROD_SET_IMAGE_EQN", - ``!s. FINITE s ==> !f. INJ f s UNIV ==> (PROD_SET (IMAGE f s) = PI f s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_SET_EMPTY, PROD_IMAGE_EMPTY] >> - fs[INJ_INSERT] >> - `f e NOTIN (IMAGE f s)` by metis_tac[IN_IMAGE] >> - rw[PROD_SET_INSERT, PROD_IMAGE_INSERT]); - -(* Theorem: PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** (SUM_SET (count m)) *) -(* Proof: - By induction on m. - Base case: PROD_SET (IMAGE (\j. n ** j) (count 0)) = n ** SUM_SET (count 0) - LHS = PROD_SET (IMAGE (\j. n ** j) (count 0)) - = PROD_SET (IMAGE (\j. n ** j) {}) by COUNT_ZERO - = PROD_SET {} by IMAGE_EMPTY - = 1 by PROD_SET_THM - RHS = n ** SUM_SET (count 0) - = n ** SUM_SET {} by COUNT_ZERO - = n ** 0 by SUM_SET_THM - = 1 by EXP - = LHS - Step case: PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** SUM_SET (count m) ==> - PROD_SET (IMAGE (\j. n ** j) (count (SUC m))) = n ** SUM_SET (count (SUC m)) - First, - FINITE (count m) by FINITE_COUNT - FINITE (IMAGE (\j. n ** j) (count m)) by IMAGE_FINITE - m NOTIN count m by IN_COUNT - and (\j. n ** j) m NOTIN IMAGE (\j. n ** j) (count m) by EXP_BASE_INJECTIVE, 1 < n - - LHS = PROD_SET (IMAGE (\j. n ** j) (count (SUC m))) - = PROD_SET (IMAGE (\j. n ** j) (m INSERT count m)) by COUNT_SUC - = n ** m * PROD_SET (IMAGE (\j. n ** j) (count m)) by PROD_SET_IMAGE_REDUCTION - = n ** m * n ** SUM_SET (count m) by induction hypothesis - = n ** (m + SUM_SET (count m)) by EXP_ADD - = n ** SUM_SET (m INSERT count m) by SUM_SET_INSERT - = n ** SUM_SET (count (SUC m)) by COUNT_SUC - = RHS -*) -val PROD_SET_IMAGE_EXP = store_thm( - "PROD_SET_IMAGE_EXP", - ``!n. 1 < n ==> !m. PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** (SUM_SET (count m))``, - rpt strip_tac >> - Induct_on `m` >- - rw[PROD_SET_THM] >> - `FINITE (IMAGE (\j. n ** j) (count m))` by rw[] >> - `(\j. n ** j) m NOTIN IMAGE (\j. n ** j) (count m)` by rw[] >> - `m NOTIN count m` by rw[] >> - `PROD_SET (IMAGE (\j. n ** j) (count (SUC m))) = - PROD_SET (IMAGE (\j. n ** j) (m INSERT count m))` by rw[COUNT_SUC] >> - `_ = n ** m * PROD_SET (IMAGE (\j. n ** j) (count m))` by rw[PROD_SET_IMAGE_REDUCTION] >> - `_ = n ** m * n ** SUM_SET (count m)` by rw[] >> - `_ = n ** (m + SUM_SET (count m))` by rw[EXP_ADD] >> - `_ = n ** SUM_SET (m INSERT count m)` by rw[SUM_SET_INSERT] >> - `_ = n ** SUM_SET (count (SUC m))` by rw[COUNT_SUC] >> - decide_tac); - -(* Theorem: FINITE s /\ x IN s ==> x divides PROD_SET s *) -(* Proof: - Note !n x. x IN s /\ n divides x - ==> n divides PROD_SET s by PROD_SET_DIVISORS - Put n = x, and x divides x = T by DIVIDES_REFL - and the result follows. -*) -val PROD_SET_ELEMENT_DIVIDES = store_thm( - "PROD_SET_ELEMENT_DIVIDES", - ``!s x. FINITE s /\ x IN s ==> x divides PROD_SET s``, - metis_tac[PROD_SET_DIVISORS, DIVIDES_REFL]); - -(* Theorem: FINITE s ==> !f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ - (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s) *) -(* Proof: - By finite induction on s. - Base: PROD_SET (IMAGE f {}) <= PROD_SET (IMAGE g {}) - Note PROD_SET (IMAGE f {}) - = PROD_SET {} by IMAGE_EMPTY - = 1 by PROD_SET_EMPTY - Thus true. - Step: !f g. (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s) ==> - e NOTIN s /\ !x. x IN e INSERT s ==> f x <= g x ==> - PROD_SET (IMAGE f (e INSERT s)) <= PROD_SET (IMAGE g (e INSERT s)) - Note INJ f s univ(:num) by INJ_INSERT - and INJ g s univ(:num) by INJ_INSERT - and f e NOTIN (IMAGE f s) by IN_IMAGE - and g e NOTIN (IMAGE g s) by IN_IMAGE - Applying LE_MONO_MULT2, - PROD_SET (IMAGE f (e INSERT s)) - = PROD_SET (f e INSERT IMAGE f s) by INSERT_IMAGE - = f e * PROD_SET (IMAGE f s) by PROD_SET_INSERT - <= g e * PROD_SET (IMAGE f s) by f e <= g e - <= g e * PROD_SET (IMAGE g s) by induction hypothesis - = PROD_SET (g e INSERT IMAGE g s) by PROD_SET_INSERT - = PROD_SET (IMAGE g (e INSERT s)) by INSERT_IMAGE -*) -val PROD_SET_LESS_EQ = store_thm( - "PROD_SET_LESS_EQ", - ``!s. FINITE s ==> !f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ - (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_SET_EMPTY] >> - fs[INJ_INSERT] >> - `f e NOTIN (IMAGE f s)` by metis_tac[IN_IMAGE] >> - `g e NOTIN (IMAGE g s)` by metis_tac[IN_IMAGE] >> - `f e <= g e` by rw[] >> - `PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s)` by rw[] >> - rw[PROD_SET_INSERT, LE_MONO_MULT2]); - -(* Theorem: FINITE s ==> !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s *) -(* Proof: - By finite induction on s. - Base: PROD_SET {} <= n ** CARD {} - Note PROD_SET {} - = 1 by PROD_SET_EMPTY - = n ** 0 by EXP_0 - = n ** CARD {} by CARD_EMPTY - Step: !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s ==> - e NOTIN s /\ !x. x IN e INSERT s ==> x <= n ==> PROD_SET (e INSERT s) <= n ** CARD (e INSERT s) - Note !x. (x = e) \/ x IN s ==> x <= n by IN_INSERT - PROD_SET (e INSERT s) - = e * PROD_SET s by PROD_SET_INSERT - <= n * PROD_SET s by e <= n - <= n * (n ** CARD s) by induction hypothesis - = n ** (SUC (CARD s)) by EXP - = n ** CARD (e INSERT s) by CARD_INSERT, e NOTIN s -*) -val PROD_SET_LE_CONSTANT = store_thm( - "PROD_SET_LE_CONSTANT", - ``!s. FINITE s ==> !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_SET_EMPTY, EXP_0] >> - fs[] >> - `e <= n /\ PROD_SET s <= n ** CARD s` by rw[] >> - rw[PROD_SET_INSERT, EXP, CARD_INSERT, LE_MONO_MULT2]); - -(* Theorem: FINITE s ==> !n f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ (!x. x IN s ==> n <= f x * g x) ==> - n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s) *) -(* Proof: - By finite induction on s. - Base: n ** CARD {} <= PROD_SET (IMAGE f {}) * PROD_SET (IMAGE g {}) - Note n ** CARD {} - = n ** 0 by CARD_EMPTY - = 1 by EXP_0 - and PROD_SET (IMAGE f {}) - = PROD_SET {} by IMAGE_EMPTY - = 1 by PROD_SET_EMPTY - Step: !n f. INJ f s univ(:num) /\ INJ g s univ(:num) /\ - (!x. x IN s ==> n <= f x * g x) ==> - n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s) ==> - e NOTIN s /\ INJ f (e INSERT s) univ(:num) /\ INJ g (e INSERT s) univ(:num) /\ - !x. x IN e INSERT s ==> n <= f x * g x ==> - n ** CARD (e INSERT s) <= PROD_SET (IMAGE f (e INSERT s)) * PROD_SET (IMAGE g (e INSERT s)) - Note INJ f s univ(:num) /\ INJ g s univ(:num) by INJ_INSERT - and f e NOTIN (IMAGE f s) /\ g e NOTIN (IMAGE g s) by IN_IMAGE - PROD_SET (IMAGE f (e INSERT s)) * PROD_SET (IMAGE g (e INSERT s)) - = PROD_SET (f e INSERT (IMAGE f s)) * PROD_SET (g e INSERT (IMAGE g s)) by INSERT_IMAGE - = (f e * PROD_SET (IMAGE f s)) * (g e * PROD_SET (IMAGE g s)) by PROD_SET_INSERT - = (f e * g e) * (PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)) by MULT_ASSOC, MULT_COMM - >= n * (PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)) by n <= f e * g e - >= n * n ** CARD s by induction hypothesis - = n ** (SUC (CARD s)) by EXP - = n ** (CARD (e INSERT s)) by CARD_INSERT -*) -val PROD_SET_PRODUCT_GE_CONSTANT = store_thm( - "PROD_SET_PRODUCT_GE_CONSTANT", - ``!s. FINITE s ==> !n f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ (!x. x IN s ==> n <= f x * g x) ==> - n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[PROD_SET_EMPTY, EXP_0] >> - fs[INJ_INSERT] >> - `f e NOTIN (IMAGE f s) /\ g e NOTIN (IMAGE g s)` by metis_tac[IN_IMAGE] >> - `n <= f e * g e /\ n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)` by rw[] >> - `PROD_SET (f e INSERT IMAGE f s) * PROD_SET (g e INSERT IMAGE g s) = - (f e * PROD_SET (IMAGE f s)) * (g e * PROD_SET (IMAGE g s))` by rw[PROD_SET_INSERT] >> - `_ = (f e * g e) * (PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s))` by metis_tac[MULT_ASSOC, MULT_COMM] >> - metis_tac[EXP, CARD_INSERT, LE_MONO_MULT2]); - -(* Theorem: FINITE s ==> !u v. s =|= u # v ==> (PROD_SET s = PROD_SET u * PROD_SET v) *) -(* Proof: - By finite induction on s. - Base: {} = u UNION v ==> PROD_SET {} = PROD_SET u * PROD_SET v - Note u = {} and v = {} by EMPTY_UNION - and PROD_SET {} = 1 by PROD_SET_EMPTY - Hence true. - Step: !u v. (s = u UNION v) /\ DISJOINT u v ==> (PROD_SET s = PROD_SET u * PROD_SET v) ==> - e NOTIN s /\ e INSERT s = u UNION v ==> PROD_SET (e INSERT s) = PROD_SET u * PROD_SET v - Note e IN u \/ e IN v by IN_INSERT, IN_UNION - If e IN u, - Then e NOTIN v by IN_DISJOINT - Let w = u DELETE e. - Then e NOTIN w by IN_DELETE - and u = e INSERT w by INSERT_DELETE - Note s = w UNION v by EXTENSION, IN_INSERT, IN_UNION - ==> FINITE w by FINITE_UNION - and DISJOINT w v by DISJOINT_INSERT - PROD_SET (e INSERT s) - = e * PROD_SET s by PROD_SET_INSERT, FINITE s - = e * (PROD_SET w * PROD_SET v) by induction hypothesis - = (e * PROD_SET w) * PROD_SET v by MULT_ASSOC - = PROD_SET (e INSERT w) * PROD_SET v by PROD_SET_INSERT, FINITE w - = PROD_SET u * PROD_SET v - - Similarly for e IN v. -*) -val PROD_SET_PRODUCT_BY_PARTITION = store_thm( - "PROD_SET_PRODUCT_BY_PARTITION", - ``!s. FINITE s ==> !u v. s =|= u # v ==> (PROD_SET s = PROD_SET u * PROD_SET v)``, - Induct_on `FINITE` >> - rpt strip_tac >- - fs[PROD_SET_EMPTY] >> - `e IN u \/ e IN v` by metis_tac[IN_INSERT, IN_UNION] >| [ - qabbrev_tac `w = u DELETE e` >> - `u = e INSERT w` by rw[Abbr`w`] >> - `e NOTIN w` by rw[Abbr`w`] >> - `e NOTIN v` by metis_tac[IN_DISJOINT] >> - `s = w UNION v` by - (rw[EXTENSION] >> - metis_tac[IN_INSERT, IN_UNION]) >> - `FINITE w` by metis_tac[FINITE_UNION] >> - `DISJOINT w v` by metis_tac[DISJOINT_INSERT] >> - `PROD_SET (e INSERT s) = e * PROD_SET s` by rw[PROD_SET_INSERT] >> - `_ = e * (PROD_SET w * PROD_SET v)` by rw[] >> - `_ = (e * PROD_SET w) * PROD_SET v` by rw[] >> - `_ = PROD_SET u * PROD_SET v` by rw[PROD_SET_INSERT] >> - rw[], - qabbrev_tac `w = v DELETE e` >> - `v = e INSERT w` by rw[Abbr`w`] >> - `e NOTIN w` by rw[Abbr`w`] >> - `e NOTIN u` by metis_tac[IN_DISJOINT] >> - `s = u UNION w` by - (rw[EXTENSION] >> - metis_tac[IN_INSERT, IN_UNION]) >> - `FINITE w` by metis_tac[FINITE_UNION] >> - `DISJOINT u w` by metis_tac[DISJOINT_INSERT, DISJOINT_SYM] >> - `PROD_SET (e INSERT s) = e * PROD_SET s` by rw[PROD_SET_INSERT] >> - `_ = e * (PROD_SET u * PROD_SET w)` by rw[] >> - `_ = PROD_SET u * (e * PROD_SET w)` by rw[] >> - `_ = PROD_SET u * PROD_SET v` by rw[PROD_SET_INSERT] >> - rw[] - ]); - -(* ------------------------------------------------------------------------- *) -(* Partition and Equivalent Class *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: y IN equiv_class R s x <=> y IN s /\ R x y *) -(* Proof: by GSPECIFICATION *) -val equiv_class_element = store_thm( - "equiv_class_element", - ``!R s x y. y IN equiv_class R s x <=> y IN s /\ R x y``, - rw[]); - -(* Theorem: partition R {} = {} *) -(* Proof: by partition_def *) -val partition_on_empty = store_thm( - "partition_on_empty", - ``!R. partition R {} = {}``, - rw[partition_def]); - -(* -> partition_def; -val it = |- !R s. partition R s = {t | ?x. x IN s /\ (t = equiv_class R s x)}: thm -*) - -(* Theorem: t IN partition R s <=> ?x. x IN s /\ (t = equiv_class R s x) *) -(* Proof: by partition_def *) -Theorem partition_element: - !R s t. t IN partition R s <=> ?x. x IN s /\ (t = equiv_class R s x) -Proof - rw[partition_def] -QED - -(* Theorem: partition R s = IMAGE (equiv_class R s) s *) -(* Proof: - partition R s - = {t | ?x. x IN s /\ (t = {y | y IN s /\ R x y})} by partition_def - = {t | ?x. x IN s /\ (t = equiv_class R s x)} by notation - = IMAGE (equiv_class R s) s by IN_IMAGE -*) -val partition_elements = store_thm( - "partition_elements", - ``!R s. partition R s = IMAGE (equiv_class R s) s``, - rw[partition_def, EXTENSION] >> - metis_tac[]); - -(* Theorem alias *) -val partition_as_image = save_thm("partition_as_image", partition_elements); -(* val partition_as_image = - |- !R s. partition R s = IMAGE (\x. equiv_class R s x) s: thm *) - -(* Theorem: (R1 = R2) /\ (s1 = s2) ==> (partition R1 s1 = partition R2 s2) *) -(* Proof: by identity *) -val partition_cong = store_thm( - "partition_cong", - ``!R1 R2 s1 s2. (R1 = R2) /\ (s1 = s2) ==> (partition R1 s1 = partition R2 s2)``, - rw[]); -(* Just in case this is needed. *) - -(* -EMPTY_NOT_IN_partition -val it = |- R equiv_on s ==> {} NOTIN partition R s: thm -*) - -(* Theorem: R equiv_on s /\ e IN partition R s ==> e <> {} *) -(* Proof: by EMPTY_NOT_IN_partition. *) -Theorem partition_element_not_empty: - !R s e. R equiv_on s /\ e IN partition R s ==> e <> {} -Proof - metis_tac[EMPTY_NOT_IN_partition] -QED - -(* Theorem: R equiv_on s /\ x IN s ==> equiv_class R s x <> {} *) -(* Proof: - Note equiv_class R s x IN partition_element R s by partition_element - so equiv_class R s x <> {} by partition_element_not_empty -*) -Theorem equiv_class_not_empty: - !R s x. R equiv_on s /\ x IN s ==> equiv_class R s x <> {} -Proof - metis_tac[partition_element, partition_element_not_empty] -QED - -(* Theorem: R equiv_on s ==> (x IN s <=> ?e. e IN partition R s /\ x IN e) *) -(* Proof: - x IN s - <=> x IN (BIGUNION (partition R s)) by BIGUNION_partition - <=> ?e. e IN partition R s /\ x IN e by IN_BIGUNION -*) -Theorem partition_element_exists: - !R s x. R equiv_on s ==> (x IN s <=> ?e. e IN partition R s /\ x IN e) -Proof - rpt strip_tac >> - imp_res_tac BIGUNION_partition >> - metis_tac[IN_BIGUNION] -QED - -(* Theorem: When the partitions are equal size of n, CARD s = n * CARD (partition of s). - FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> - (CARD s = n * CARD (partition R s)) *) -(* Proof: - Note FINITE (partition R s) by FINITE_partition - so CARD s = SIGMA CARD (partition R s) by partition_CARD - = n * CARD (partition R s) by SIGMA_CARD_CONSTANT -*) -val equal_partition_card = store_thm( - "equal_partition_card", - ``!R s n. FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> - (CARD s = n * CARD (partition R s))``, - rw_tac std_ss[partition_CARD, FINITE_partition, GSYM SIGMA_CARD_CONSTANT]); - -(* Theorem: When the partitions are equal size of n, CARD s = n * CARD (partition of s). - FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> - n divides (CARD s) *) -(* Proof: by equal_partition_card, divides_def. *) -Theorem equal_partition_factor: - !R s n. FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> - n divides (CARD s) -Proof - metis_tac[equal_partition_card, divides_def, MULT_COMM] -QED - -(* Theorem: When the partition size has a factor n, then n divides CARD s. - FINITE s /\ R equiv_on s /\ - (!e. e IN partition R s ==> n divides (CARD e)) ==> n divides (CARD s) *) -(* Proof: - Note FINITE (partition R s) by FINITE_partition - Thus CARD s = SIGMA CARD (partition R s) by partition_CARD - But !e. e IN partition R s ==> n divides (CARD e) - ==> n divides SIGMA CARD (partition R s) by SIGMA_CARD_FACTOR - Hence n divdes CARD s by above -*) -val factor_partition_card = store_thm( - "factor_partition_card", - ``!R s n. FINITE s /\ R equiv_on s /\ - (!e. e IN partition R s ==> n divides (CARD e)) ==> n divides (CARD s)``, - metis_tac[FINITE_partition, partition_CARD, SIGMA_CARD_FACTOR]); - -(* Note: -When a set s is partitioned by an equivalence relation R, -partition_CARD |- !R s. R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s)) -Can this be generalized to: f s = SIGMA f (partition R s) ? -If so, we can have (SIGMA f) s = SIGMA (SIGMA f) (partition R s) -Sort of yes, but have to use BIGUNION, and for a set_additive function f. -*) - -(* Overload every element finite of a superset *) -val _ = overload_on("EVERY_FINITE", ``\P. (!s. s IN P ==> FINITE s)``); - -(* -> FINITE_BIGUNION; -val it = |- !P. FINITE P /\ EVERY_FINITE P ==> FINITE (BIGUNION P): thm -*) - -(* Overload pairwise disjoint of a superset *) -val _ = overload_on("PAIR_DISJOINT", ``\P. (!s t. s IN P /\ t IN P /\ ~(s = t) ==> DISJOINT s t)``); - -(* -> partition_elements_disjoint; -val it = |- R equiv_on s ==> PAIR_DISJOINT (partition R s): thm -*) - -(* Theorem: t SUBSET s /\ PAIR_DISJOINT s ==> PAIR_DISJOINT t *) -(* Proof: by SUBSET_DEF *) -Theorem pair_disjoint_subset: - !s t. t SUBSET s /\ PAIR_DISJOINT s ==> PAIR_DISJOINT t -Proof - rw[SUBSET_DEF] -QED - -(* Overload an additive set function *) -val _ = overload_on("SET_ADDITIVE", - ``\f. (f {} = 0) /\ (!s t. FINITE s /\ FINITE t ==> (f (s UNION t) + f (s INTER t) = f s + f t))``); - -(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P) *) -(* Proof: - By finite induction on P. - Base: f (BIGUNION {}) = SIGMA f {} - f (BIGUNION {}) - = f {} by BIGUNION_EMPTY - = 0 by SET_ADDITIVE f - = SIGMA f {} = RHS by SUM_IMAGE_EMPTY - Step: e NOTIN P ==> f (BIGUNION (e INSERT P)) = SIGMA f (e INSERT P) - Given !s. s IN e INSERT P ==> FINITE s - thus !s. (s = e) \/ s IN P ==> FINITE s by IN_INSERT - hence FINITE e by implication - and EVERY_FINITE P by IN_INSERT - and FINITE (BIGUNION P) by FINITE_BIGUNION - Given PAIR_DISJOINT (e INSERT P) - so PAIR_DISJOINT P by IN_INSERT - and !s. s IN P ==> DISJOINT e s by IN_INSERT - hence DISJOINT e (BIGUNION P) by DISJOINT_BIGUNION - so e INTER (BIGUNION P) = {} by DISJOINT_DEF - and f (e INTER (BIGUNION P)) = 0 by SET_ADDITIVE f - f (BIGUNION (e INSERT P) - = f (BIGUNION (e INSERT P)) + f (e INTER (BIGUNION P)) by ADD_0 - = f e + f (BIGUNION P) by SET_ADDITIVE f - = f e + SIGMA f P by induction hypothesis - = f e + SIGMA f (P DELETE e) by DELETE_NON_ELEMENT - = SIGMA f (e INSERT P) by SUM_IMAGE_THM -*) -val disjoint_bigunion_add_fun = store_thm( - "disjoint_bigunion_add_fun", - ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P)``, - `!P. FINITE P ==> EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P)` suffices_by rw[] >> - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - rw_tac std_ss[BIGUNION_EMPTY, SUM_IMAGE_EMPTY] >> - rw_tac std_ss[BIGUNION_INSERT, SUM_IMAGE_THM] >> - `FINITE e /\ FINITE (BIGUNION P)` by rw[FINITE_BIGUNION] >> - `EVERY_FINITE P /\ PAIR_DISJOINT P` by rw[] >> - `!s. s IN P ==> DISJOINT e s` by metis_tac[IN_INSERT] >> - `f (e INTER (BIGUNION P)) = 0` by metis_tac[DISJOINT_DEF, DISJOINT_BIGUNION] >> - `f (e UNION BIGUNION P) = f (e UNION BIGUNION P) + f (e INTER (BIGUNION P))` by decide_tac >> - `_ = f e + f (BIGUNION P)` by metis_tac[] >> - `_ = f e + SIGMA f P` by prove_tac[] >> - metis_tac[DELETE_NON_ELEMENT]); - -(* Theorem: SET_ADDITIVE CARD *) -(* Proof: - Since CARD {} = 0 by CARD_EMPTY - and !s t. FINITE s /\ FINITE t - ==> CARD (s UNION t) + CARD (s INTER t) = CARD s + CARD t by CARD_UNION - Hence SET_ADDITIVE CARD by notation -*) -val set_additive_card = store_thm( - "set_additive_card", - ``SET_ADDITIVE CARD``, - rw[FUN_EQ_THM, CARD_UNION]); - -(* Note: DISJ_BIGUNION_CARD is only a prove_thm in pred_setTheoryScript.sml *) -(* -g `!P. FINITE P ==> EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P)` -e (PSet_ind.SET_INDUCT_TAC FINITE_INDUCT); -Q. use of this changes P to s, s to s', how? -*) - -(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P) *) -(* Proof: by disjoint_bigunion_add_fun, set_additive_card *) -val disjoint_bigunion_card = store_thm( - "disjoint_bigunion_card", - ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P)``, - rw[disjoint_bigunion_add_fun, set_additive_card]); - -(* Theorem alias *) -Theorem CARD_BIGUNION_PAIR_DISJOINT = disjoint_bigunion_card; -(* -val CARD_BIGUNION_PAIR_DISJOINT = - |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - CARD (BIGUNION P) = SIGMA CARD P: thm -*) - -(* Theorem: SET_ADDITIVE (SIGMA f) *) -(* Proof: - Since SIGMA f {} = 0 by SUM_IMAGE_EMPTY - and !s t. FINITE s /\ FINITE t - ==> SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t by SUM_IMAGE_UNION_EQN - Hence SET_ADDITIVE (SIGMA f) -*) -val set_additive_sigma = store_thm( - "set_additive_sigma", - ``!f. SET_ADDITIVE (SIGMA f)``, - rw[SUM_IMAGE_EMPTY, SUM_IMAGE_UNION_EQN]); - -(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> !f. SIGMA f (BIGUNION P) = SIGMA (SIGMA f) P *) -(* Proof: by disjoint_bigunion_add_fun, set_additive_sigma *) -val disjoint_bigunion_sigma = store_thm( - "disjoint_bigunion_sigma", - ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> !f. SIGMA f (BIGUNION P) = SIGMA (SIGMA f) P``, - rw[disjoint_bigunion_add_fun, set_additive_sigma]); - -(* Theorem: R equiv_on s /\ FINITE s ==> !f. SET_ADDITIVE f ==> (f s = SIGMA f (partition R s)) *) -(* Proof: - Let P = partition R s. - Then FINITE s - ==> FINITE P /\ !t. t IN P ==> FINITE t by FINITE_partition - and R equiv_on s - ==> BIGUNION P = s by BIGUNION_partition - ==> PAIR_DISJOINT P by partition_elements_disjoint - Hence f (BIGUNION P) = SIGMA f P by disjoint_bigunion_add_fun - or f s = SIGMA f P by above, BIGUNION_partition -*) -val set_add_fun_by_partition = store_thm( - "set_add_fun_by_partition", - ``!R s. R equiv_on s /\ FINITE s ==> !f. SET_ADDITIVE f ==> (f s = SIGMA f (partition R s))``, - rpt strip_tac >> - qabbrev_tac `P = partition R s` >> - `FINITE P /\ !t. t IN P ==> FINITE t` by metis_tac[FINITE_partition] >> - `BIGUNION P = s` by rw[BIGUNION_partition, Abbr`P`] >> - `PAIR_DISJOINT P` by metis_tac[partition_elements_disjoint] >> - rw[disjoint_bigunion_add_fun]); - -(* Theorem: R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s)) *) -(* Proof: by set_add_fun_by_partition, set_additive_card *) -val set_card_by_partition = store_thm( - "set_card_by_partition", - ``!R s. R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s))``, - rw[set_add_fun_by_partition, set_additive_card]); - -(* This is pred_setTheory.partition_CARD *) - -(* Theorem: R equiv_on s /\ FINITE s ==> !f. SIGMA f s = SIGMA (SIGMA f) (partition R s) *) -(* Proof: by set_add_fun_by_partition, set_additive_sigma *) -val set_sigma_by_partition = store_thm( - "set_sigma_by_partition", - ``!R s. R equiv_on s /\ FINITE s ==> !f. SIGMA f s = SIGMA (SIGMA f) (partition R s)``, - rw[set_add_fun_by_partition, set_additive_sigma]); - -(* Overload a multiplicative set function *) -val _ = overload_on("SET_MULTIPLICATIVE", - ``\f. (f {} = 1) /\ (!s t. FINITE s /\ FINITE t ==> (f (s UNION t) * f (s INTER t) = f s * f t))``); - -(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P) *) -(* Proof: - By finite induction on P. - Base: f (BIGUNION {}) = PI f {} - f (BIGUNION {}) - = f {} by BIGUNION_EMPTY - = 1 by SET_MULTIPLICATIVE f - = PI f {} = RHS by PROD_IMAGE_EMPTY - Step: e NOTIN P ==> f (BIGUNION (e INSERT P)) = PI f (e INSERT P) - Given !s. s IN e INSERT P ==> FINITE s - thus !s. (s = e) \/ s IN P ==> FINITE s by IN_INSERT - hence FINITE e by implication - and EVERY_FINITE P by IN_INSERT - and FINITE (BIGUNION P) by FINITE_BIGUNION - Given PAIR_DISJOINT (e INSERT P) - so PAIR_DISJOINT P by IN_INSERT - and !s. s IN P ==> DISJOINT e s by IN_INSERT - hence DISJOINT e (BIGUNION P) by DISJOINT_BIGUNION - so e INTER (BIGUNION P) = {} by DISJOINT_DEF - and f (e INTER (BIGUNION P)) = 1 by SET_MULTIPLICATIVE f - f (BIGUNION (e INSERT P) - = f (BIGUNION (e INSERT P)) * f (e INTER (BIGUNION P)) by MULT_RIGHT_1 - = f e * f (BIGUNION P) by SET_MULTIPLICATIVE f - = f e * PI f P by induction hypothesis - = f e * PI f (P DELETE e) by DELETE_NON_ELEMENT - = PI f (e INSERT P) by PROD_IMAGE_THM -*) -val disjoint_bigunion_mult_fun = store_thm( - "disjoint_bigunion_mult_fun", - ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P)``, - `!P. FINITE P ==> EVERY_FINITE P /\ PAIR_DISJOINT P ==> - !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P)` suffices_by rw[] >> - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - rw_tac std_ss[BIGUNION_EMPTY, PROD_IMAGE_EMPTY] >> - rw_tac std_ss[BIGUNION_INSERT, PROD_IMAGE_THM] >> - `FINITE e /\ FINITE (BIGUNION P)` by rw[FINITE_BIGUNION] >> - `EVERY_FINITE P /\ PAIR_DISJOINT P` by rw[] >> - `!s. s IN P ==> DISJOINT e s` by metis_tac[IN_INSERT] >> - `f (e INTER (BIGUNION P)) = 1` by metis_tac[DISJOINT_DEF, DISJOINT_BIGUNION] >> - `f (e UNION BIGUNION P) = f (e UNION BIGUNION P) * f (e INTER (BIGUNION P))` by metis_tac[MULT_RIGHT_1] >> - `_ = f e * f (BIGUNION P)` by metis_tac[] >> - `_ = f e * PI f P` by prove_tac[] >> - metis_tac[DELETE_NON_ELEMENT]); - -(* Theorem: R equiv_on s /\ FINITE s ==> !f. SET_MULTIPLICATIVE f ==> (f s = PI f (partition R s)) *) -(* Proof: - Let P = partition R s. - Then FINITE s - ==> FINITE P /\ EVERY_FINITE P by FINITE_partition - and R equiv_on s - ==> BIGUNION P = s by BIGUNION_partition - ==> PAIR_DISJOINT P by partition_elements_disjoint - Hence f (BIGUNION P) = PI f P by disjoint_bigunion_mult_fun - or f s = PI f P by above, BIGUNION_partition -*) -val set_mult_fun_by_partition = store_thm( - "set_mult_fun_by_partition", - ``!R s. R equiv_on s /\ FINITE s ==> !f. SET_MULTIPLICATIVE f ==> (f s = PI f (partition R s))``, - rpt strip_tac >> - qabbrev_tac `P = partition R s` >> - `FINITE P /\ !t. t IN P ==> FINITE t` by metis_tac[FINITE_partition] >> - `BIGUNION P = s` by rw[BIGUNION_partition, Abbr`P`] >> - `PAIR_DISJOINT P` by metis_tac[partition_elements_disjoint] >> - rw[disjoint_bigunion_mult_fun]); - -(* Theorem: FINITE s ==> !g. INJ g s univ(:'a) ==> !f. SIGMA f (IMAGE g s) = SIGMA (f o g) s *) -(* Proof: - By finite induction on s. - Base: SIGMA f (IMAGE g {}) = SIGMA (f o g) {} - LHS = SIGMA f (IMAGE g {}) - = SIGMA f {} by IMAGE_EMPTY - = 0 by SUM_IMAGE_EMPTY - = SIGMA (f o g) {} = RHS by SUM_IMAGE_EMPTY - Step: e NOTIN s ==> SIGMA f (IMAGE g (e INSERT s)) = SIGMA (f o g) (e INSERT s) - Note INJ g (e INSERT s) univ(:'a) - ==> INJ g s univ(:'a) /\ g e IN univ(:'a) /\ - !y. y IN s /\ (g e = g y) ==> (e = y) by INJ_INSERT - Thus g e NOTIN (IMAGE g s) by IN_IMAGE - SIGMA f (IMAGE g (e INSERT s)) - = SIGMA f (g e INSERT IMAGE g s) by IMAGE_INSERT - = f (g e) + SIGMA f (IMAGE g s) by SUM_IMAGE_THM, g e NOTIN (IMAGE g s) - = f (g e) + SIGMA (f o g) s by induction hypothesis - = (f o g) e + SIGMA (f o g) s by composition - = SIGMA (f o g) (e INSERT s) by SUM_IMAGE_THM, e NOTIN s -*) -val sum_image_by_composition = store_thm( - "sum_image_by_composition", - ``!s. FINITE s ==> !g. INJ g s univ(:'a) ==> !f. SIGMA f (IMAGE g s) = SIGMA (f o g) s``, - ho_match_mp_tac FINITE_INDUCT >> - rpt strip_tac >- - rw[SUM_IMAGE_EMPTY] >> - `INJ g s univ(:'a) /\ g e IN univ(:'a) /\ !y. y IN s /\ (g e = g y) ==> (e = y)` by metis_tac[INJ_INSERT] >> - `g e NOTIN (IMAGE g s)` by metis_tac[IN_IMAGE] >> - `(s DELETE e = s) /\ (IMAGE g s DELETE g e = IMAGE g s)` by metis_tac[DELETE_NON_ELEMENT] >> - rw[SUM_IMAGE_THM]); - -(* Overload on permutation *) -val _ = overload_on("PERMUTES", ``\f s. BIJ f s s``); -val _ = set_fixity "PERMUTES" (Infix(NONASSOC, 450)); (* same as relation *) - -(* Theorem: FINITE s ==> !g. g PERMUTES s ==> !f. SIGMA (f o g) s = SIGMA f s *) -(* Proof: - Given permutate g s = BIJ g s s by notation - ==> INJ g s s /\ SURJ g s s by BIJ_DEF - Now SURJ g s s ==> IMAGE g s = s by IMAGE_SURJ - Also s SUBSET univ(:'a) by SUBSET_UNIV - and s SUBSET s by SUBSET_REFL - Hence INJ g s univ(:'a) by INJ_SUBSET - With FINITE s, - SIGMA (f o g) s - = SIGMA f (IMAGE g s) by sum_image_by_composition - = SIGMA f s by above -*) -val sum_image_by_permutation = store_thm( - "sum_image_by_permutation", - ``!s. FINITE s ==> !g. g PERMUTES s ==> !f. SIGMA (f o g) s = SIGMA f s``, - rpt strip_tac >> - `INJ g s s /\ SURJ g s s` by metis_tac[BIJ_DEF] >> - `IMAGE g s = s` by rw[GSYM IMAGE_SURJ] >> - `s SUBSET univ(:'a)` by rw[SUBSET_UNIV] >> - `INJ g s univ(:'a)` by metis_tac[INJ_SUBSET, SUBSET_REFL] >> - `SIGMA (f o g) s = SIGMA f (IMAGE g s)` by rw[sum_image_by_composition] >> - rw[]); - -(* Theorem: FINITE s ==> !f:('b -> bool) -> num. (f {} = 0) ==> - !g. (!t. FINITE t /\ (!x. x IN t ==> g x <> {}) ==> INJ g t univ(:num -> bool)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s) *) -(* Proof: - Let s1 = {x | x IN s /\ (g x = {})}, - s2 = {x | x IN s /\ (g x <> {})}. - Then s = s1 UNION s2 by EXTENSION - and DISJOINT s1 s2 by EXTENSION, DISJOINT_DEF - and DISJOINT (IMAGE g s1) (IMAGE g s2) by EXTENSION, DISJOINT_DEF - Now s1 SUBSET s /\ s1 SUBSET s by SUBSET_DEF - Since FINITE s by given - thus FINITE s1 /\ FINITE s2 by SUBSET_FINITE - and FINITE (IMAGE g s1) /\ FINITE (IMAGE g s2) by IMAGE_FINITE - - Step 1: decompose left summation - SIGMA f (IMAGE g s) - = SIGMA f (IMAGE g (s1 UNION s2)) by above, s = s1 UNION s2 - = SIGMA f ((IMAGE g s1) UNION (IMAGE g s2)) by IMAGE_UNION - = SIGMA f (IMAGE g s1) + SIGMA f (IMAGE g s2) by SUM_IMAGE_DISJOINT - - Claim: SIGMA f (IMAGE g s1) = 0 - Proof: If s1 = {}, - SIGMA f (IMAGE g {}) - = SIGMA f {} by IMAGE_EMPTY - = 0 by SUM_IMAGE_EMPTY - If s1 <> {}, - Note !x. x IN s1 ==> (g x = {}) by definition of s1 - Thus !y. y IN (IMAGE g s1) ==> (y = {}) by IN_IMAGE, IMAGE_EMPTY - Since s1 <> {}, IMAGE g s1 = {{}} by SING_DEF, IN_SING, SING_ONE_ELEMENT - SIGMA f (IMAGE g {}) - = SIGMA f {{}} by above - = f {} by SUM_IMAGE_SING - = 0 by given - - Step 2: decompose right summation - Also SIGMA (f o g) s - = SIGMA (f o g) (s1 UNION s2) by above, s = s1 UNION s2 - = SIGMA (f o g) s1 + SIGMA (f o g) s2 by SUM_IMAGE_DISJOINT - - Claim: SIGMA (f o g) s1 = 0 - Proof: Note !x. x IN s1 ==> (g x = {}) by definition of s1 - (f o g) x - = f (g x) by function application - = f {} by above - = 0 by given - Hence SIGMA (f o g) s1 - = 0 * CARD s1 by SIGMA_CONSTANT - = 0 by MULT - - Claim: SIGMA f (IMAGE g s2) = SIGMA (f o g) s2 - Proof: Note !x. x IN s2 ==> g x <> {} by definition of s2 - Thus INJ g s2 univ(:'b -> bool) by given - Hence SIGMA f (IMAGE g s2) - = SIGMA (f o g) (s2) by sum_image_by_composition - - Result follows by combining all the claims and arithmetic. -*) -val sum_image_by_composition_with_partial_inj = store_thm( - "sum_image_by_composition_with_partial_inj", - ``!s. FINITE s ==> !f:('b -> bool) -> num. (f {} = 0) ==> - !g. (!t. FINITE t /\ (!x. x IN t ==> g x <> {}) ==> INJ g t univ(:'b -> bool)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s)``, - rpt strip_tac >> - qabbrev_tac `s1 = {x | x IN s /\ (g x = {})}` >> - qabbrev_tac `s2 = {x | x IN s /\ (g x <> {})}` >> - (`s = s1 UNION s2` by (rw[Abbr`s1`, Abbr`s2`, EXTENSION] >> metis_tac[])) >> - (`DISJOINT s1 s2` by (rw[Abbr`s1`, Abbr`s2`, EXTENSION, DISJOINT_DEF] >> metis_tac[])) >> - (`DISJOINT (IMAGE g s1) (IMAGE g s2)` by (rw[Abbr`s1`, Abbr`s2`, EXTENSION, DISJOINT_DEF] >> metis_tac[])) >> - `s1 SUBSET s /\ s2 SUBSET s` by rw[Abbr`s1`, Abbr`s2`, SUBSET_DEF] >> - `FINITE s1 /\ FINITE s2` by metis_tac[SUBSET_FINITE] >> - `FINITE (IMAGE g s1) /\ FINITE (IMAGE g s2)` by rw[] >> - `SIGMA f (IMAGE g s) = SIGMA f ((IMAGE g s1) UNION (IMAGE g s2))` by rw[] >> - `_ = SIGMA f (IMAGE g s1) + SIGMA f (IMAGE g s2)` by rw[SUM_IMAGE_DISJOINT] >> - `SIGMA f (IMAGE g s1) = 0` by - (Cases_on `s1 = {}` >- - rw[SUM_IMAGE_EMPTY] >> - `!x. x IN s1 ==> (g x = {})` by rw[Abbr`s1`] >> - `!y. y IN (IMAGE g s1) ==> (y = {})` by metis_tac[IN_IMAGE, IMAGE_EMPTY] >> - `{} IN {{}} /\ IMAGE g s1 <> {}` by rw[] >> - `IMAGE g s1 = {{}}` by metis_tac[SING_DEF, IN_SING, SING_ONE_ELEMENT] >> - `SIGMA f (IMAGE g s1) = f {}` by rw[SUM_IMAGE_SING] >> - rw[] - ) >> - `SIGMA (f o g) s = SIGMA (f o g) s1 + SIGMA (f o g) s2` by rw[SUM_IMAGE_DISJOINT] >> - `SIGMA (f o g) s1 = 0` by - (`!x. x IN s1 ==> (g x = {})` by rw[Abbr`s1`] >> - `!x. x IN s1 ==> ((f o g) x = 0)` by rw[] >> - metis_tac[SIGMA_CONSTANT, MULT]) >> - `SIGMA f (IMAGE g s2) = SIGMA (f o g) s2` by - (`!x. x IN s2 ==> g x <> {}` by rw[Abbr`s2`] >> - `INJ g s2 univ(:'b -> bool)` by rw[] >> - rw[sum_image_by_composition]) >> - decide_tac); - -(* Theorem: FINITE s ==> !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s) *) -(* Proof: - By finite induction on s. - Base: SIGMA f (IMAGE g {}) = SIGMA (f o g) {} - SIGMA f (IMAGE g {}) - = SIGMA f {} by IMAGE_EMPTY - = 0 by SUM_IMAGE_EMPTY - = SIGMA (f o g) {} by SUM_IMAGE_EMPTY - Step: !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s) ==> - e NOTIN s /\ !x y. x IN e INSERT s /\ y IN e INSERT s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0) - ==> SIGMA f (IMAGE g (e INSERT s)) = SIGMA (f o g) (e INSERT s) - Note !x y. ((x = e) \/ x IN s) /\ ((y = e) \/ y IN s) /\ (g x = g y) ==> - (x = y) \/ (f (g y) = 0) by IN_INSERT - If g e IN IMAGE g s, - Then ?x. x IN s /\ (g x = g e) by IN_IMAGE - and x <> e /\ (f (g e) = 0) by implication - SIGMA f (g e INSERT IMAGE g s) - = SIGMA f (IMAGE g s) by ABSORPTION, g e IN IMAGE g s - = SIGMA (f o g) s by induction hypothesis - = f (g x) + SIGMA (f o g) s by ADD - = (f o g) e + SIGMA (f o g) s by o_THM - = SIGMA (f o g) (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s - If g e NOTIN IMAGE g s, - SIGMA f (g e INSERT IMAGE g s) - = f (g e) + SIGMA f (IMAGE g s) by SUM_IMAGE_INSERT, g e NOTIN IMAGE g s - = f (g e) + SIGMA (f o g) s by induction hypothesis - = (f o g) e + SIGMA (f o g) s by o_THM - = SIGMA (f o g) (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s -*) -val sum_image_by_composition_without_inj = store_thm( - "sum_image_by_composition_without_inj", - ``!s. FINITE s ==> !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> - (SIGMA f (IMAGE g s) = SIGMA (f o g) s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[SUM_IMAGE_EMPTY] >> - fs[] >> - Cases_on `g e IN IMAGE g s` >| [ - `?x. x IN s /\ (g x = g e)` by metis_tac[IN_IMAGE] >> - `x <> e /\ (f (g e) = 0)` by metis_tac[] >> - `SIGMA f (g e INSERT IMAGE g s) = SIGMA f (IMAGE g s)` by metis_tac[ABSORPTION] >> - `_ = SIGMA (f o g) s` by rw[] >> - `_ = (f o g) e + SIGMA (f o g) s` by rw[] >> - `_ = SIGMA (f o g) (e INSERT s)` by rw[SUM_IMAGE_INSERT] >> - rw[], - `SIGMA f (g e INSERT IMAGE g s) = f (g e) + SIGMA f (IMAGE g s)` by rw[SUM_IMAGE_INSERT] >> - `_ = f (g e) + SIGMA (f o g) s` by rw[] >> - `_ = (f o g) e + SIGMA (f o g) s` by rw[] >> - `_ = SIGMA (f o g) (e INSERT s)` by rw[SUM_IMAGE_INSERT] >> - rw[] - ]); - -(* ------------------------------------------------------------------------- *) -(* Pre-image Theorems. *) -(* ------------------------------------------------------------------------- *) - -(* -- IN_IMAGE; -> val it = |- !y s f. y IN IMAGE f s <=> ?x. (y = f x) /\ x IN s : thm -*) - -(* Define preimage *) -val preimage_def = Define `preimage f s y = { x | x IN s /\ (f x = y) }`; - -(* Theorem: x IN (preimage f s y) <=> x IN s /\ (f x = y) *) -(* Proof: by preimage_def *) -val preimage_element = store_thm( - "preimage_element", - ``!f s x y. x IN (preimage f s y) <=> x IN s /\ (f x = y)``, - rw[preimage_def]); - -(* Theorem: x IN preimage f s y <=> (x IN s /\ (f x = y)) *) -(* Proof: by preimage_def *) -val in_preimage = store_thm( - "in_preimage", - ``!f s x y. x IN preimage f s y <=> (x IN s /\ (f x = y))``, - rw[preimage_def]); -(* same as theorem above. *) - -(* Theorem: (preimage f s y) SUBSET s *) -(* Proof: - x IN preimage f s y - <=> x IN s /\ f x = y by in_preimage - ==> x IN s - Thus (preimage f s y) SUBSET s by SUBSET_DEF -*) -Theorem preimage_subset: - !f s y. (preimage f s y) SUBSET s -Proof - simp[preimage_def, SUBSET_DEF] -QED - -(* Theorem: FINITE s ==> FINITE (preimage f s y) *) -(* Proof: - Note (preimage f s y) SUBSET s by preimage_subset - Thus FINITE (preimage f s y) by SUBSET_FINITE -*) -Theorem preimage_finite: - !f s y. FINITE s ==> FINITE (preimage f s y) -Proof - metis_tac[preimage_subset, SUBSET_FINITE] -QED - -(* Theorem: !x. x IN preimage f s y ==> f x = y *) -(* Proof: by definition. *) -val preimage_property = store_thm( - "preimage_property", - ``!f s y. !x. x IN preimage f s y ==> (f x = y)``, - rw[preimage_def]); - -(* This is bad: every pattern of f x = y (i.e. practically every equality!) will invoke the check: x IN preimage f s y! *) -(* val _ = export_rewrites ["preimage_property"]; *) - -(* Theorem: x IN s ==> x IN preimage f s (f x) *) -(* Proof: by IN_IMAGE. preimage_def. *) -val preimage_of_image = store_thm( - "preimage_of_image", - ``!f s x. x IN s ==> x IN preimage f s (f x)``, - rw[preimage_def]); - -(* Theorem: y IN (IMAGE f s) ==> CHOICE (preimage f s y) IN s /\ f (CHOICE (preimage f s y)) = y *) -(* Proof: - (1) prove: y IN IMAGE f s ==> CHOICE (preimage f s y) IN s - By IN_IMAGE, this is to show: - x IN s ==> CHOICE (preimage f s (f x)) IN s - Now, preimage f s (f x) <> {} since x is a pre-image. - hence CHOICE (preimage f s (f x)) IN preimage f s (f x) by CHOICE_DEF - hence CHOICE (preimage f s (f x)) IN s by preimage_def - (2) prove: y IN IMAGE f s /\ CHOICE (preimage f s y) IN s ==> f (CHOICE (preimage f s y)) = y - By IN_IMAGE, this is to show: x IN s ==> f (CHOICE (preimage f s (f x))) = f x - Now, x IN preimage f s (f x) by preimage_of_image - hence preimage f s (f x) <> {} by MEMBER_NOT_EMPTY - thus CHOICE (preimage f s (f x)) IN (preimage f s (f x)) by CHOICE_DEF - hence f (CHOICE (preimage f s (f x))) = f x by preimage_def -*) -val preimage_choice_property = store_thm( - "preimage_choice_property", - ``!f s y. y IN (IMAGE f s) ==> CHOICE (preimage f s y) IN s /\ (f (CHOICE (preimage f s y)) = y)``, - rpt gen_tac >> - strip_tac >> - conj_asm1_tac >| [ - full_simp_tac std_ss [IN_IMAGE] >> - `CHOICE (preimage f s (f x)) IN preimage f s (f x)` suffices_by rw[preimage_def] >> - metis_tac[CHOICE_DEF, preimage_of_image, MEMBER_NOT_EMPTY], - full_simp_tac std_ss [IN_IMAGE] >> - `x IN preimage f s (f x)` by rw_tac std_ss[preimage_of_image] >> - `CHOICE (preimage f s (f x)) IN (preimage f s (f x))` by metis_tac[CHOICE_DEF, MEMBER_NOT_EMPTY] >> - full_simp_tac std_ss [preimage_def, GSPECIFICATION] - ]); - -(* Theorem: INJ f s univ(:'b) ==> !x. x IN s ==> (preimage f s (f x) = {x}) *) -(* Proof: - preimage f s (f x) - = {x' | x' IN s /\ (f x' = f x)} by preimage_def - = {x' | x' IN s /\ (x' = x)} by INJ_DEF - = {x} by EXTENSION -*) -val preimage_inj = store_thm( - "preimage_inj", - ``!f s. INJ f s univ(:'b) ==> !x. x IN s ==> (preimage f s (f x) = {x})``, - rw[preimage_def, EXTENSION] >> - metis_tac[INJ_DEF]); - -(* Theorem: INJ f s univ(:'b) ==> !x. x IN s ==> (CHOICE (preimage f s (f x)) = x) *) -(* Proof: - CHOICE (preimage f s (f x)) - = CHOICE {x} by preimage_inj, INJ f s univ(:'b) - = x by CHOICE_SING -*) -val preimage_inj_choice = store_thm( - "preimage_inj_choice", - ``!f s. INJ f s univ(:'b) ==> !x. x IN s ==> (CHOICE (preimage f s (f x)) = x)``, - rw[preimage_inj]); - -(* Theorem: INJ (preimage f s) (IMAGE f s) (POW s) *) -(* Proof: - By INJ_DEF, this is to show: - (1) x IN s ==> preimage f s (f x) IN POW s - Let y = preimage f s (f x). - Then y SUBSET s by preimage_subset - so y IN (POW s) by IN_POW - (2) x IN s /\ y IN s /\ preimage f s (f x) = preimage f s (f y) ==> f x = f y - Note (f x) IN preimage f s (f x) by in_preimage - so (f y) IN preimage f s (f y) by given - Thus f x = f y by in_preimage -*) -Theorem preimage_image_inj: - !f s. INJ (preimage f s) (IMAGE f s) (POW s) -Proof - rw[INJ_DEF] >- - simp[preimage_subset, IN_POW] >> - metis_tac[in_preimage] -QED - -(* ------------------------------------------------------------------------- *) -(* Set of Proper Subsets *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of all proper subsets of a set *) -val _ = overload_on ("PPOW", ``\s. (POW s) DIFF {s}``); - -(* Theorem: !s e. e IN PPOW s ==> e PSUBSET s *) -(* Proof: - e IN PPOW s - = e IN ((POW s) DIFF {s}) by notation - = (e IN POW s) /\ e NOTIN {s} by IN_DIFF - = (e SUBSET s) /\ e NOTIN {s} by IN_POW - = (e SUBSET s) /\ e <> s by IN_SING - = e PSUBSET s by PSUBSET_DEF -*) -val IN_PPOW = store_thm( - "IN_PPOW", - ``!s e. e IN PPOW s ==> e PSUBSET s``, - rw[PSUBSET_DEF, IN_POW]); - -(* Theorem: FINITE (PPOW s) *) -(* Proof: - Since PPOW s = (POW s) DIFF {s}, - FINITE s - ==> FINITE (POW s) by FINITE_POW - ==> FINITE ((POW s) DIFF {s}) by FINITE_DIFF - ==> FINITE (PPOW s) by above -*) -val FINITE_PPOW = store_thm( - "FINITE_PPOW", - ``!s. FINITE s ==> FINITE (PPOW s)``, - rw[FINITE_POW]); - -(* Theorem: FINITE s ==> CARD (PPOW s) = PRE (2 ** CARD s) *) -(* Proof: - CARD (PPOW s) - = CARD ((POW s) DIFF {s}) by notation - = CARD (POW s) - CARD ((POW s) INTER {s}) by CARD_DIFF - = CARD (POW s) - CARD {s} by INTER_SING, since s IN POW s - = 2 ** CARD s - CARD {s} by CARD_POW - = 2 ** CARD s - 1 by CARD_SING - = PRE (2 ** CARD s) by PRE_SUB1 -*) -val CARD_PPOW = store_thm( - "CARD_PPOW", - ``!s. FINITE s ==> (CARD (PPOW s) = PRE (2 ** CARD s))``, - rpt strip_tac >> - `FINITE {s}` by rw[FINITE_SING] >> - `FINITE (POW s)` by rw[FINITE_POW] >> - `s IN (POW s)` by rw[IN_POW, SUBSET_REFL] >> - `CARD (PPOW s) = CARD (POW s) - CARD ((POW s) INTER {s})` by rw[CARD_DIFF] >> - `_ = CARD (POW s) - CARD {s}` by rw[INTER_SING] >> - `_ = 2 ** CARD s - CARD {s}` by rw[CARD_POW] >> - `_ = 2 ** CARD s - 1` by rw[CARD_SING] >> - `_ = PRE (2 ** CARD s)` by rw[PRE_SUB1] >> - rw[]); - -(* Theorem: FINITE s ==> CARD (PPOW s) = PRE (2 ** CARD s) *) -(* Proof: by CARD_PPOW *) -val CARD_PPOW_EQN = store_thm( - "CARD_PPOW_EQN", - ``!s. FINITE s ==> (CARD (PPOW s) = (2 ** CARD s) - 1)``, - rw[CARD_PPOW]); - -(* ------------------------------------------------------------------------- *) -(* Useful Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Note: -> type_of ``prime``; -val it = ":num -> bool": hol_type - -Thus prime is also a set, or prime = {p | prime p} -*) - -(* Theorem: p IN prime <=> prime p *) -(* Proof: by IN_DEF *) -val in_prime = store_thm( - "in_prime", - ``!p. p IN prime <=> prime p``, - rw[IN_DEF]); - -(* Theorem: (Generalized Euclid's Lemma) - If prime p divides a PROD_SET, it divides a member of the PROD_SET. - FINITE s ==> !p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b *) -(* Proof: by induction of the PROD_SET, apply Euclid's Lemma. -- P_EUCLIDES; -> val it = - |- !p a b. prime p /\ p divides (a * b) ==> p divides a \/ p divides b : thm - By finite induction on s. - Base case: prime p /\ p divides (PROD_SET {}) ==> F - Since PROD_SET {} = 1 by PROD_SET_THM - and p divides 1 <=> p = 1 by DIVIDES_ONE - but prime p ==> p <> 1 by NOT_PRIME_1 - This gives the contradiction. - Step case: FINITE s /\ (!p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b) /\ - e NOTIN s /\ prime p /\ p divides (PROD_SET (e INSERT s)) ==> - ?b. ((b = e) \/ b IN s) /\ p divides b - Note PROD_SET (e INSERT s) = e * PROD_SET s by PROD_SET_THM, DELETE_NON_ELEMENT, e NOTIN s. - So prime p /\ p divides (PROD_SET (e INSERT s)) - ==> p divides e, or p divides (PROD_SET s) by P_EUCLIDES - If p divides e, just take b = e. - If p divides (PROD_SET s), there is such b by induction hypothesis -*) -val PROD_SET_EUCLID = store_thm( - "PROD_SET_EUCLID", - ``!s. FINITE s ==> !p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b``, - ho_match_mp_tac FINITE_INDUCT >> - rw[] >- - metis_tac[PROD_SET_EMPTY, DIVIDES_ONE, NOT_PRIME_1] >> - `PROD_SET (e INSERT s) = e * PROD_SET s` - by metis_tac[PROD_SET_THM, DELETE_NON_ELEMENT] >> - Cases_on `p divides e` >- - metis_tac[] >> - metis_tac[P_EUCLIDES]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/logPowerScript.sml b/examples/algebra/lib/logPowerScript.sml deleted file mode 100644 index 9d1ae42b45..0000000000 --- a/examples/algebra/lib/logPowerScript.sml +++ /dev/null @@ -1,3784 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Integer Functions Computation. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "logPower"; - -(* ------------------------------------------------------------------------- *) - - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* Get dependent theories in lib *) -(* val _ = load "helperNumTheory"; *) -open helperNumTheory; - -(* val _ = load "helperFunctionTheory"; *) -open helperFunctionTheory; -open pred_setTheory; -open helperSetTheory; - -(* open dependent theories *) -open arithmeticTheory; -open dividesTheory gcdTheory; - -(* val _ = load "logrootTheory"; *) -open logrootTheory; (* for ROOT *) - - -(* ------------------------------------------------------------------------- *) -(* Integer Functions Computation Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - SQRT n = ROOT 2 n - LOG2 n = LOG 2 n - n power_of b = perfect_power n b -*) - -(* Definitions and Theorems (# are exported): - - ROOT computation: - ROOT_POWER |- !a n. 1 < a /\ 0 < n ==> (ROOT n (a ** n) = a) - ROOT_FROM_POWER |- !m n b. 0 < m /\ (b ** m = n) ==> (b = ROOT m n) -# ROOT_OF_0 |- !m. 0 < m ==> (ROOT m 0 = 0) -# ROOT_OF_1 |- !m. 0 < m ==> (ROOT m 1 = 1) - ROOT_EQ_0 |- !m. 0 < m ==> !n. (ROOT m n = 0) <=> (n = 0) -# ROOT_1 |- !n. ROOT 1 n = n - ROOT_THM |- !r. 0 < r ==> !n p. (ROOT r n = p) <=> p ** r <= n /\ n < SUC p ** r - ROOT_EQN |- !r n. 0 < r ==> (ROOT r n = - (let m = TWICE (ROOT r (n DIV 2 ** r)) - in m + if (m + 1) ** r <= n then 1 else 0)) - ROOT_SUC |- !r n. 0 < r ==> - ROOT r (SUC n) = ROOT r n + - if SUC n = SUC (ROOT r n) ** r then 1 else 0 - ROOT_EQ_1 |- !m. 0 < m ==> !n. (ROOT m n = 1) <=> 0 < n /\ n < 2 ** m - ROOT_LE_SELF |- !m n. 0 < m ==> ROOT m n <= n - ROOT_EQ_SELF |- !m n. 0 < m ==> (ROOT m n = n) <=> (m = 1) \/ (n = 0) \/ (n = 1)) - ROOT_GE_SELF |- !m n. 0 < m ==> (n <= ROOT m n) <=> (m = 1) \/ (n = 0) \/ (n = 1)) - ROOT_LE_REVERSE |- !a b n. 0 < a /\ a <= b ==> ROOT b n <= ROOT a n - - Square Root: - SQRT_PROPERTY |- !n. 0 < n ==> SQRT n ** 2 <= n /\ n < SUC (SQRT n) ** 2 - SQRT_UNIQUE |- !n p. p ** 2 <= n /\ n < SUC p ** 2 ==> SQRT n = p - SQRT_THM |- !n p. (SQRT n = p) <=> p ** 2 <= n /\ n < SUC p ** 2 - SQ_SQRT_LE |- !n. SQ (SQRT n) <= n - SQ_SQRT_LE_alt |- !n. SQRT n ** 2 <= n - SQRT_LE |- !n m. n <= m ==> SQRT n <= SQRT m - SQRT_LT |- !n m. n < m ==> SQRT n <= SQRT m -# SQRT_0 |- SQRT 0 = 0 -# SQRT_1 |- SQRT 1 = 1 - SQRT_EQ_0 |- !n. (SQRT n = 0) <=> (n = 0) - SQRT_EQ_1 |- !n. (SQRT n = 1) <=> (n = 1) \/ (n = 2) \/ (n = 3) - SQRT_EXP_2 |- !n. SQRT (n ** 2) = n - SQRT_OF_SQ |- !n. SQRT (n ** 2) = n - SQRT_SQ |- !n. SQRT (SQ n) = n - SQRT_LE_SELF |- !n. SQRT n <= n - SQRT_GE_SELF |- !n. n <= SQRT n <=> (n = 0) \/ (n = 1) - SQRT_EQ_SELF |- !n. (SQRT n = n) <=> (n = 0) \/ (n = 1) - SQRT_LE_IMP |- !n m. SQRT n <= m ==> n <= 3 * m ** 2 - SQRT_MULT_LE |- !n m. SQRT n * SQRT m <= SQRT (n * m) - SQRT_LT_IMP |- !n m. SQRT n < m ==> n < m ** 2 - LT_SQRT_IMP |- !n m. n < SQRT m ==> n ** 2 < m - SQRT_LT_SQRT |- !n m. SQRT n < SQRT m ==> n < m - - Square predicate: - square_def |- !n. square n <=> ?k. n = k * k - square_alt |- !n. square n <=> ?k. n = k ** 2 -! square_eqn |- !n. square n <=> SQRT n ** 2 = n - square_0 |- square 0 - square_1 |- square 1 - prime_non_square |- !p. prime p ==> ~square p - SQ_SQRT_LT |- !n. ~square n ==> SQRT n * SQRT n < n - SQ_SQRT_LT_alt |- !n. ~square n ==> SQRT n ** 2 < n - odd_square_lt |- !n m. ~square n ==> ((2 * m + 1) ** 2 < n <=> m < HALF (1 + SQRT n)) - - Logarithm: - LOG_EXACT_EXP |- !a. 1 < a ==> !n. LOG a (a ** n) = n - EXP_TO_LOG |- !a b n. 1 < a /\ 0 < b /\ b <= a ** n ==> LOG a b <= n - LOG_THM |- !a n. 1 < a /\ 0 < n ==> - !p. (LOG a n = p) <=> a ** p <= n /\ n < a ** SUC p - LOG_EVAL |- !m n. LOG m n = if m <= 1 \/ n = 0 then LOG m n - else if n < m then 0 else SUC (LOG m (n DIV m)) - LOG_TEST |- !a n. 1 < a /\ 0 < n ==> - !p. (LOG a n = p) <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n - LOG_POWER |- !b x n. 1 < b /\ 0 < x /\ 0 < n ==> - n * LOG b x <= LOG b (x ** n) /\ - LOG b (x ** n) < n * SUC (LOG b x) - LOG_LE_REVERSE |- !a b n. 1 < a /\ 0 < n /\ a <= b ==> LOG b n <= LOG a n - -# LOG2_1 |- LOG2 1 = 0 -# LOG2_2 |- LOG2 2 = 1 - LOG2_THM |- !n. 0 < n ==> !p. (LOG2 n = p) <=> 2 ** p <= n /\ n < 2 ** SUC p - LOG2_PROPERTY |- !n. 0 < n ==> 2 ** LOG2 n <= n /\ n < 2 ** SUC (LOG2 n) - TWO_EXP_LOG2_LE |- !n. 0 < n ==> 2 ** LOG2 n <= n - LOG2_UNIQUE |- !n m. 2 ** m <= n /\ n < 2 ** SUC m ==> (LOG2 n = m) - LOG2_EQ_0 |- !n. 0 < n ==> (LOG2 n = 0 <=> n = 1) - LOG2_EQ_1 |- !n. 0 < n ==> ((LOG2 n = 1) <=> (n = 2) \/ (n = 3)) - LOG2_LE_MONO |- !n m. 0 < n ==> n <= m ==> LOG2 n <= LOG2 m - LOG2_LE |- !n m. 0 < n /\ n <= m ==> LOG2 n <= LOG2 m - LOG2_LT |- !n m. 0 < n /\ n < m ==> LOG2 n <= LOG2 m - LOG2_LT_SELF |- !n. 0 < n ==> LOG2 n < n - LOG2_NEQ_SELF |- !n. 0 < n ==> LOG2 n <> n - LOG2_EQ_SELF |- !n. (LOG2 n = n) ==> (n = 0) -# LOG2_POS |- !n. 1 < n ==> 0 < LOG2 n - LOG2_TWICE_LT |- !n. 1 < n ==> 1 < 2 * LOG2 n - LOG2_TWICE_SQ |- !n. 1 < n ==> 4 <= (2 * LOG2 n) ** 2 - LOG2_SUC_TWICE_SQ |- !n. 0 < n ==> 4 <= (2 * SUC (LOG2 n)) ** 2 - LOG2_SUC_SQ |- !n. 1 < n ==> 1 < SUC (LOG2 n) ** 2 - LOG2_SUC_TIMES_SQ_DIV_2_POS |- !n m. 1 < m ==> 0 < SUC (LOG2 n) * (m ** 2 DIV 2) - LOG2_2_EXP |- !n. LOG2 (2 ** n) = n - LOG2_EXACT_EXP |- !n. (2 ** LOG2 n = n) <=> ?k. n = 2 ** k - LOG2_MULT_EXP |- !n m. 0 < n ==> (LOG2 (n * 2 ** m) = LOG2 n + m) - LOG2_TWICE |- !n. 0 < n ==> (LOG2 (TWICE n) = 1 + LOG2 n) - LOG2_HALF |- !n. 1 < n ==> (LOG2 (HALF n) = LOG2 n - 1) - LOG2_BY_HALF |- !n. 1 < n ==> (LOG2 n = 1 + LOG2 (HALF n)) - LOG2_DIV_EXP |- !n m. 2 ** m < n ==> (LOG2 (n DIV 2 ** m) = LOG2 n - m) - - LOG2 Computation: - halves_def |- !n. halves n = if n = 0 then 0 else SUC (halves (HALF n)) - halves_alt |- !n. halves n = if n = 0 then 0 else 1 + halves (HALF n) -# halves_0 |- halves 0 = 0 -# halves_1 |- halves 1 = 1 -# halves_2 |- halves 2 = 2 -# halves_pos |- !n. 0 < n ==> 0 < halves n - halves_by_LOG2 |- !n. 0 < n ==> (halves n = 1 + LOG2 n) - LOG2_compute |- !n. LOG2 n = if n = 0 then LOG2 0 else halves n - 1 - halves_le |- !m n. m <= n ==> halves m <= halves n - halves_eq_0 |- !n. (halves n = 0) <=> (n = 0) - halves_eq_1 |- !n. (halves n = 1) <=> (n = 1) - - Perfect Power and Power Free: - perfect_power_def |- !n m. perfect_power n m <=> ?e. n = m ** e - perfect_power_self |- !n. perfect_power n n - perfect_power_0_m |- !m. perfect_power 0 m <=> (m = 0) - perfect_power_1_m |- !m. perfect_power 1 m - perfect_power_n_0 |- !n. perfect_power n 0 <=> (n = 0) \/ (n = 1) - perfect_power_n_1 |- !n. perfect_power n 1 <=> (n = 1) - perfect_power_mod_eq_0 |- !n m. 0 < m /\ 1 < n /\ n MOD m = 0 ==> - (perfect_power n m <=> perfect_power (n DIV m) m) - perfect_power_mod_ne_0 |- !n m. 0 < m /\ 1 < n /\ n MOD m <> 0 ==> ~perfect_power n m - perfect_power_test |- !n m. perfect_power n m <=> - if n = 0 then m = 0 - else if n = 1 then T - else if m = 0 then n <= 1 - else if m = 1 then n = 1 - else if n MOD m = 0 then perfect_power (n DIV m) m - else F - perfect_power_suc |- !m n. 1 < m /\ perfect_power n m /\ perfect_power (SUC n) m ==> - (m = 2) /\ (n = 1) - perfect_power_not_suc |- !m n. 1 < m /\ 1 < n /\ perfect_power n m ==> ~perfect_power (SUC n) m - LOG_SUC |- !b n. 1 < b /\ 0 < n ==> - LOG b (SUC n) = LOG b n + - if perfect_power (SUC n) b then 1 else 0 - perfect_power_bound_LOG2 |- !n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= LOG2 n /\ (n = m ** k) - perfect_power_condition |- !p q. prime p /\ (?x y. 0 < x /\ (p ** x = q ** y)) ==> perfect_power q p - perfect_power_cofactor |- !n p. 0 < p /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) - perfect_power_cofactor_alt - |- !n p. 0 < n /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) - perfect_power_2_odd |- !n. perfect_power n 2 ==> (ODD n <=> (n = 1)) - - Power Free: - power_free_def |- !n. power_free n <=> !m e. (n = m ** e) ==> (m = n) /\ (e = 1) - power_free_0 |- power_free 0 <=> F - power_free_1 |- power_free 1 <=> F - power_free_gt_1 |- !n. power_free n ==> 1 < n - power_free_alt |- !n. power_free n <=> 1 < n /\ !m. perfect_power n m ==> (n = m) - prime_is_power_free |- !n. prime n ==> power_free n - power_free_perfect_power |- !m n. power_free n /\ perfect_power n m ==> (n = m) - power_free_property |- !n. power_free n ==> !j. 1 < j ==> ROOT j n ** j <> n - power_free_check_all |- !n. power_free n <=> 1 < n /\ !j. 1 < j ==> ROOT j n ** j <> n - - Upper Logarithm: - count_up_def |- !n m k. count_up n m k = if m = 0 then 0 - else if n <= m then k - else count_up n (2 * m) (SUC k) - ulog_def |- !n. ulog n = count_up n 1 0 -# ulog_0 |- ulog 0 = 0 -# ulog_1 |- ulog 1 = 0 -# ulog_2 |- ulog 2 = 1 - - count_up_exit |- !m n. m <> 0 /\ n <= m ==> !k. count_up n m k = k - count_up_suc |- !m n. m <> 0 /\ m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k) - count_up_suc_eqn |- !m. m <> 0 ==> !n t. 2 ** t * m < n ==> - !k. count_up n m k = count_up n (2 ** SUC t * m) (SUC k + t) - count_up_exit_eqn |- !m. m <> 0 ==> !n t. 2 ** t * m < 2 * n /\ n <= 2 ** t * m ==> - !k. count_up n m k = k + t - ulog_unique |- !m n. 2 ** m < 2 * n /\ n <= 2 ** m ==> (ulog n = m) - ulog_eqn |- !n. ulog n = if 1 < n then SUC (LOG2 (n - 1)) else 0 - ulog_suc |- !n. 0 < n ==> (ulog (SUC n) = SUC (LOG2 n)) - ulog_property |- !n. 0 < n ==> 2 ** ulog n < 2 * n /\ n <= 2 ** ulog n - ulog_thm |- !n. 0 < n ==> !m. (ulog n = m) <=> 2 ** m < 2 * n /\ n <= 2 ** m - ulog_def_alt |- (ulog 0 = 0) /\ - !n. 0 < n ==> !m. (ulog n = m) <=> n <= 2 ** m /\ 2 ** m < TWICE n - ulog_eq_0 |- !n. (ulog n = 0) <=> (n = 0) \/ (n = 1) - ulog_eq_1 |- !n. (ulog n = 1) <=> (n = 2) - ulog_le_1 |- !n. ulog n <= 1 <=> n <= 2 - ulog_le |- !m n. n <= m ==> ulog n <= ulog m - ulog_lt |- !m n. n < m ==> ulog n <= ulog m - ulog_2_exp |- !n. ulog (2 ** n) = n - ulog_le_self |- !n. ulog n <= n - ulog_eq_self |- !n. (ulog n = n) <=> (n = 0) - ulog_lt_self |- !n. 0 < n ==> ulog n < n - ulog_exp_exact |- !n. (2 ** ulog n = n) <=> perfect_power n 2 - ulog_exp_not_exact |- !n. ~perfect_power n 2 ==> 2 ** ulog n <> n - ulog_property_not_exact |- !n. 0 < n /\ ~perfect_power n 2 ==> n < 2 ** ulog n - ulog_property_odd |- !n. 1 < n /\ ODD n ==> n < 2 ** ulog n - exp_to_ulog |- !m n. n <= 2 ** m ==> ulog n <= m -# ulog_pos |- !n. 1 < n ==> 0 < ulog n - ulog_ge_1 |- !n. 1 < n ==> 1 <= ulog n - ulog_sq_gt_1 |- !n. 2 < n ==> 1 < ulog n ** 2 - ulog_twice_sq |- !n. 1 < n ==> 4 <= TWICE (ulog n) ** 2 - ulog_alt |- !n. ulog n = if n = 0 then 0 - else if perfect_power n 2 then LOG2 n else SUC (LOG2 n) - ulog_LOG2 |- !n. 0 < n ==> LOG2 n <= ulog n /\ ulog n <= 1 + LOG2 n - perfect_power_bound_ulog - |- !n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= ulog n /\ (n = m ** k) - - Upper Log Theorems: - ulog_mult |- !m n. ulog (m * n) <= ulog m + ulog n - ulog_exp |- !m n. ulog (m ** n) <= n * ulog m - ulog_even |- !n. 0 < n /\ EVEN n ==> (ulog n = 1 + ulog (HALF n)) - ulog_odd |- !n. 1 < n /\ ODD n ==> ulog (HALF n) + 1 <= ulog n - ulog_half |- !n. 1 < n ==> ulog (HALF n) + 1 <= ulog n - sqrt_upper |- !n. SQRT n <= 2 ** ulog n - - Power Free up to a limit: - power_free_upto_def |- !n k. n power_free_upto k <=> !j. 1 < j /\ j <= k ==> ROOT j n ** j <> n - power_free_upto_0 |- !n. n power_free_upto 0 <=> T - power_free_upto_1 |- !n. n power_free_upto 1 <=> T - power_free_upto_suc |- !n k. 0 < k /\ n power_free_upto k ==> - (n power_free_upto k + 1 <=> ROOT (k + 1) n ** (k + 1) <> n) - power_free_check_upto |- !n b. LOG2 n <= b ==> (power_free n <=> 1 < n /\ n power_free_upto b) - power_free_check_upto_LOG2 |- !n. power_free n <=> 1 < n /\ n power_free_upto LOG2 n - power_free_check_upto_ulog |- !n. power_free n <=> 1 < n /\ n power_free_upto ulog n - power_free_2 |- power_free 2 - power_free_3 |- power_free 3 - power_free_test_def |- !n. power_free_test n <=> 1 < n /\ n power_free_upto ulog n - power_free_test_eqn |- !n. power_free_test n <=> power_free n - power_free_test_upto_LOG2 |- !n. power_free n <=> - 1 < n /\ !j. 1 < j /\ j <= LOG2 n ==> ROOT j n ** j <> n - power_free_test_upto_ulog |- !n. power_free n <=> - 1 < n /\ !j. 1 < j /\ j <= ulog n ==> ROOT j n ** j <> n - - Another Characterisation of Power Free: - power_index_def |- !n k. power_index n k = - if k <= 1 then 1 - else if ROOT k n ** k = n then k - else power_index n (k - 1) - power_index_0 |- !n. power_index n 0 = 1 - power_index_1 |- !n. power_index n 1 = 1 - power_index_eqn |- !n k. ROOT (power_index n k) n ** power_index n k = n - power_index_root |- !n k. perfect_power n (ROOT (power_index n k) n) - power_index_of_1 |- !k. power_index 1 k = if k = 0 then 1 else k - power_index_exact_root |- !n k. 0 < k /\ (ROOT k n ** k = n) ==> (power_index n k = k) - power_index_not_exact_root |- !n k. ROOT k n ** k <> n ==> (power_index n k = power_index n (k - 1)) - power_index_no_exact_roots |- !m n k. k <= m /\ (!j. k < j /\ j <= m ==> ROOT j n ** j <> n) ==> - (power_index n m = power_index n k) - power_index_lower |- !m n k. k <= m /\ (ROOT k n ** k = n) ==> k <= power_index n m - power_index_pos |- !n k. 0 < power_index n k - power_index_upper |- !n k. 0 < k ==> power_index n k <= k - power_index_equal |- !m n k. 0 < k /\ k <= m ==> - ((power_index n m = power_index n k) <=> !j. k < j /\ j <= m ==> ROOT j n ** j <> n) - power_index_property |- !m n k. (power_index n m = k) ==> !j. k < j /\ j <= m ==> ROOT j n ** j <> n - - power_free_by_power_index_LOG2 - |- !n. power_free n <=> 1 < n /\ (power_index n (LOG2 n) = 1) - power_free_by_power_index_ulog - |- !n. power_free n <=> 1 < n /\ (power_index n (ulog n) = 1) - -*) - -(* ------------------------------------------------------------------------- *) -(* ROOT Computation *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: ROOT n (a ** n) = a *) -(* Proof: - Since a < SUC a by LESS_SUC - a ** n < (SUC a) ** n by EXP_BASE_LT_MONO - Let b = a ** n, - Then a ** n <= b by LESS_EQ_REFL - and b < (SUC a) ** n by above - Hence a = ROOT n b by ROOT_UNIQUE -*) -val ROOT_POWER = store_thm( - "ROOT_POWER", - ``!a n. 1 < a /\ 0 < n ==> (ROOT n (a ** n) = a)``, - rw[EXP_BASE_LT_MONO, ROOT_UNIQUE]); - -(* Theorem: 0 < m /\ (b ** m = n) ==> (b = ROOT m n) *) -(* Proof: - Note n <= n by LESS_EQ_REFL - so b ** m <= n by b ** m = n - Also b < SUC b by LESS_SUC - so b ** m < (SUC b) ** m by EXP_EXP_LT_MONO, 0 < m - so n < (SUC b) ** m by b ** m = n - Thus b = ROOT m n by ROOT_UNIQUE -*) -val ROOT_FROM_POWER = store_thm( - "ROOT_FROM_POWER", - ``!m n b. 0 < m /\ (b ** m = n) ==> (b = ROOT m n)``, - rpt strip_tac >> - rw[ROOT_UNIQUE]); - -(* Theorem: 0 < m ==> (ROOT m 0 = 0) *) -(* Proof: - Note 0 ** m = 0 by EXP_0 - Thus 0 = ROOT m 0 by ROOT_FROM_POWER -*) -val ROOT_OF_0 = store_thm( - "ROOT_OF_0[simp]", - ``!m. 0 < m ==> (ROOT m 0 = 0)``, - rw[ROOT_FROM_POWER]); - -(* Theorem: 0 < m ==> (ROOT m 1 = 1) *) -(* Proof: - Note 1 ** m = 1 by EXP_1 - Thus 1 = ROOT m 1 by ROOT_FROM_POWER -*) -val ROOT_OF_1 = store_thm( - "ROOT_OF_1[simp]", - ``!m. 0 < m ==> (ROOT m 1 = 1)``, - rw[ROOT_FROM_POWER]); - -(* Theorem: 0 < r ==> !n p. (ROOT r n = p) <=> (p ** r <= n /\ n < SUC p ** r) *) -(* Proof: - If part: 0 < r ==> ROOT r n ** r <= n /\ n < SUC (ROOT r n) ** r - This is true by ROOT, 0 < r - Only-if part: p ** r <= n /\ n < SUC p ** r ==> ROOT r n = p - This is true by ROOT_UNIQUE -*) -val ROOT_THM = store_thm( - "ROOT_THM", - ``!r. 0 < r ==> !n p. (ROOT r n = p) <=> (p ** r <= n /\ n < SUC p ** r)``, - metis_tac[ROOT, ROOT_UNIQUE]); - -(* Rework proof of ROOT_COMPUTE in logroot theory. *) -(* ./num/extra_theories/logrootScript.sml *) - -(* ROOT r n = r-th root of n. - -Make use of indentity: -n ^ (1/r) = 2 (n/ 2^r) ^(1/r) - -if n = 0 then 0 -else (* precompute *) let x = 2 * r-th root of (n DIV (2 ** r)) - (* apply *) in if n < (SUC x) ** r then x else (SUC x) -*) - -(* - -val lem = prove(``0 < r ==> (0 ** r = 0)``, RW_TAC arith_ss [EXP_EQ_0]); - -val ROOT_COMPUTE = Q.store_thm("ROOT_COMPUTE", - `!r n. - 0 < r ==> - (ROOT r 0 = 0) /\ - (ROOT r n = let x = 2 * ROOT r (n DIV 2 ** r) in - if n < SUC x ** r then x else SUC x)`, - RW_TAC (arith_ss ++ boolSimps.LET_ss) [] - THEN MATCH_MP_TAC ROOT_UNIQUE - THEN CONJ_TAC - THEN FULL_SIMP_TAC arith_ss [NOT_LESS, EXP_1, lem] - THEN MAP_FIRST MATCH_MP_TAC - [LESS_EQ_TRANS, DECIDE ``!a b c. a < b /\ b <= c ==> a < c``] - THENL [ - Q.EXISTS_TAC `ROOT r n ** r`, - Q.EXISTS_TAC `SUC (ROOT r n) ** r`] - THEN RW_TAC arith_ss - [ROOT, GSYM EXP_LE_ISO, GSYM ROOT_DIV, DECIDE ``0 < 2n``] - THEN METIS_TAC - [DIVISION, MULT_COMM, DECIDE ``0 < 2n``, - DECIDE ``(a = b + c) ==> b <= a:num``, ADD1, LE_ADD_LCANCEL, - DECIDE ``a <= 1 = a < 2n``]); - -> ROOT_COMPUTE; -val it = - |- !r n. - 0 < r ==> - (ROOT r 0 = 0) /\ - (ROOT r n = - (let x = 2 * ROOT r (n DIV 2 ** r) - in - if n < SUC x ** r then x else SUC x)): - thm -*) - -(* Theorem: 0 < m ==> !n. (ROOT m n = 0) <=> (n = 0) *) -(* Proof: - If part: ROOT m n = 0 ==> n = 0 - Note n < SUC (ROOT m n) ** r by ROOT - or n < SUC 0 ** m by ROOT m n = 0 - so n < 1 by ONE, EXP_1 - or n = 0 by arithmetic - Only-if part: ROOT m 0 = 0, true by ROOT_OF_0 -*) -val ROOT_EQ_0 = store_thm( - "ROOT_EQ_0", - ``!m. 0 < m ==> !n. (ROOT m n = 0) <=> (n = 0)``, - rw[EQ_IMP_THM] >> - `n < 1` by metis_tac[ROOT, EXP_1, ONE] >> - decide_tac); - -(* Theorem: ROOT 1 n = n *) -(* Proof: - Note n ** 1 = n by EXP_1 - so n ** 1 <= n - Also n < SUC n by LESS_SUC - so n < SUC n ** 1 by EXP_1 - Thus ROOT 1 n = n by ROOT_UNIQUE -*) -val ROOT_1 = store_thm( - "ROOT_1[simp]", - ``!n. ROOT 1 n = n``, - rw[ROOT_UNIQUE]); - - -(* Theorem: 0 < r ==> (ROOT r n = - let m = 2 * ROOT r (n DIV 2 ** r) in m + if (m + 1) ** r <= n then 1 else 0) *) -(* Proof: - ROOT k n - = if n < SUC m ** k then m else SUC m by ROOT_COMPUTE - = if SUC m ** k <= n then SUC m else m by logic - = if (m + 1) ** k <= n then (m + 1) else m by ADD1 - = m + if (m + 1) ** k <= n then 1 else 0 by arithmetic -*) -val ROOT_EQN = store_thm( - "ROOT_EQN", - ``!r n. 0 < r ==> (ROOT r n = - let m = 2 * ROOT r (n DIV 2 ** r) in m + if (m + 1) ** r <= n then 1 else 0)``, - rw_tac std_ss[] >> - Cases_on `(m + 1) ** r <= n` >- - rw[ROOT_COMPUTE, ADD1] >> - rw[ROOT_COMPUTE, ADD1]); - - -(* Theorem: 0 < r ==> - (ROOT r (SUC n) = ROOT r n + if SUC n = (SUC (ROOT r n)) ** r then 1 else 0) *) -(* Proof: - Let x = ROOT r n, y = ROOT r (SUC n). x <= y. - Note n < (SUC x) ** r /\ x ** r <= n by ROOT_THM - and SUC n < (SUC y) ** r /\ y ** r <= SUC n by ROOT_THM - Since n < (SUC x) ** r, - SUC n <= (SUC x) ** r. - If SUC n = (SUC x) ** r, - Then y = ROOT r (SUC n) - = ROOT r ((SUC x) ** r) - = SUC x by ROOT_POWER - If SUC n < (SUC x) ** r, - Then x ** r <= n < SUC n by LESS_SUC - Thus x = y by ROOT_THM -*) -val ROOT_SUC = store_thm( - "ROOT_SUC", - ``!r n. 0 < r ==> - (ROOT r (SUC n) = ROOT r n + if SUC n = (SUC (ROOT r n)) ** r then 1 else 0)``, - rpt strip_tac >> - qabbrev_tac `x = ROOT r n` >> - qabbrev_tac `y = ROOT r (SUC n)` >> - Cases_on `n = 0` >| [ - `x = 0` by rw[ROOT_OF_0, Abbr`x`] >> - `y = 1` by rw[ROOT_OF_1, Abbr`y`] >> - simp[], - `x <> 0` by rw[ROOT_EQ_0, Abbr`x`] >> - `n < (SUC x) ** r /\ x ** r <= n` by metis_tac[ROOT_THM] >> - `SUC n < (SUC y) ** r /\ y ** r <= SUC n` by metis_tac[ROOT_THM] >> - `(SUC n = (SUC x) ** r) \/ SUC n < (SUC x) ** r` by decide_tac >| [ - `1 < SUC x` by decide_tac >> - `y = SUC x` by metis_tac[ROOT_POWER] >> - simp[], - `x ** r <= SUC n` by decide_tac >> - `x = y` by metis_tac[ROOT_THM] >> - simp[] - ] - ]); - -(* -ROOT_SUC; -|- !r n. 0 < r ==> ROOT r (SUC n) = ROOT r n + if SUC n = SUC (ROOT r n) ** r then 1 else 0 -Let z = ROOT r n. - - z(n) - ------------------------------------------------- - n (n+1=(z+1)**r) - -> EVAL ``MAP (ROOT 2) [1 .. 20]``; -val it = |- MAP (ROOT 2) [1 .. 20] = - [1; 1; 1; 2; 2; 2; 2; 2; 3; 3; 3; 3; 3; 3; 3; 4; 4; 4; 4; 4]: thm - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -*) - -(* Theorem: 0 < m ==> !n. (ROOT m n = 1) <=> (0 < n /\ n < 2 ** m) *) -(* Proof: - ROOT m n = 1 - <=> 1 ** m <= n /\ n < (SUC 1) ** m by ROOT_THM, 0 < m - <=> 1 <= n /\ n < 2 ** m by TWO, EXP_1 - <=> 0 < n /\ n < 2 ** m by arithmetic -*) -val ROOT_EQ_1 = store_thm( - "ROOT_EQ_1", - ``!m. 0 < m ==> !n. (ROOT m n = 1) <=> (0 < n /\ n < 2 ** m)``, - rpt strip_tac >> - `!n. 0 < n <=> 1 <= n` by decide_tac >> - metis_tac[ROOT_THM, TWO, EXP_1]); - -(* Theorem: 0 < m ==> ROOT m n <= n *) -(* Proof: - Let r = ROOT m n. - Note r <= r ** m by X_LE_X_EXP, 0 < m - <= n by ROOT -*) -val ROOT_LE_SELF = store_thm( - "ROOT_LE_SELF", - ``!m n. 0 < m ==> ROOT m n <= n``, - metis_tac[X_LE_X_EXP, ROOT, LESS_EQ_TRANS]); - -(* Theorem: 0 < m ==> ((ROOT m n = n) <=> ((m = 1) \/ (n = 0) \/ (n = 1))) *) -(* Proof: - If part: ROOT m n = n ==> m = 1 \/ n = 0 \/ n = 1 - Note n ** m <= n by ROOT, 0 < r - But n <= n ** m by X_LE_X_EXP, 0 < m - so n ** m = n by EQ_LESS_EQ - ==> m = 1 or n = 0 or n = 1 by EXP_EQ_SELF - Only-if part: ROOT 1 n = n /\ ROOT m 0 = 0 /\ ROOT m 1 = 1 - True by ROOT_1, ROOT_OF_0, ROOT_OF_1. -*) -Theorem ROOT_EQ_SELF: - !m n. 0 < m ==> (ROOT m n = n <=> m = 1 \/ n = 0 \/ n = 1) -Proof - rw_tac std_ss[EQ_IMP_THM] >> rw[] >> - `n ** m <= n` by metis_tac[ROOT] >> - `n <= n ** m` by rw[X_LE_X_EXP] >> - `n ** m = n` by decide_tac >> - gs[] -QED - -(* Theorem: 0 < m ==> (n <= ROOT m n <=> ((m = 1) \/ (n = 0) \/ (n = 1))) *) -(* Proof: - Note ROOT m n <= n by ROOT_LE_SELF - Thus n <= ROOT m n <=> ROOT m n = n by EQ_LESS_EQ - The result follows by ROOT_EQ_SELF -*) -val ROOT_GE_SELF = store_thm( - "ROOT_GE_SELF", - ``!m n. 0 < m ==> (n <= ROOT m n <=> ((m = 1) \/ (n = 0) \/ (n = 1)))``, - metis_tac[ROOT_LE_SELF, ROOT_EQ_SELF, EQ_LESS_EQ]); - -(* -EVAL ``MAP (\k. ROOT k 100) [1 .. 10]``; = [100; 10; 4; 3; 2; 2; 1; 1; 1; 1]: thm - -This shows (ROOT k) is a decreasing function of k, -but this is very hard to prove without some real number theory. -Even this is hard to prove: ROOT 3 n <= ROOT 2 n - -No! -- this can be proved, see below. -*) - -(* Theorem: 0 < a /\ a <= b ==> ROOT b n <= ROOT a n *) -(* Proof: - Let x = ROOT a n, y = ROOT b n. To show: y <= x. - By contradiction, suppose x < y. - Then SUC x <= y. - Note x ** a <= n /\ n < (SUC x) ** a by ROOT - and y ** b <= n /\ n < (SUC y) ** b by ROOT - But a <= b - (SUC x) ** a - <= (SUC x) ** b by EXP_BASE_LEQ_MONO_IMP, 0 < SUC x, a <= b - <= y ** b by EXP_EXP_LE_MONO, 0 < b - This leads to n < (SUC x) ** a <= y ** b <= n, a contradiction. -*) -val ROOT_LE_REVERSE = store_thm( - "ROOT_LE_REVERSE", - ``!a b n. 0 < a /\ a <= b ==> ROOT b n <= ROOT a n``, - rpt strip_tac >> - qabbrev_tac `x = ROOT a n` >> - qabbrev_tac `y = ROOT b n` >> - spose_not_then strip_assume_tac >> - `0 < b /\ SUC x <= y` by decide_tac >> - `x ** a <= n /\ n < (SUC x) ** a` by rw[ROOT, Abbr`x`] >> - `y ** b <= n /\ n < (SUC y) ** b` by rw[ROOT, Abbr`y`] >> - `(SUC x) ** a <= (SUC x) ** b` by rw[EXP_BASE_LEQ_MONO_IMP] >> - `(SUC x) ** b <= y ** b` by rw[EXP_EXP_LE_MONO] >> - decide_tac); - - -(* ------------------------------------------------------------------------- *) -(* Square Root *) -(* ------------------------------------------------------------------------- *) -(* Use overload for SQRT *) -val _ = overload_on ("SQRT", ``\n. ROOT 2 n``); - -(* -> EVAL ``SQRT 4``; -val it = |- SQRT 4 = 2: thm -> EVAL ``(SQRT 4) ** 2``; -val it = |- SQRT 4 ** 2 = 4: thm -> EVAL ``(SQRT 5) ** 2``; -val it = |- SQRT 5 ** 2 = 4: thm -> EVAL ``(SQRT 8) ** 2``; -val it = |- SQRT 8 ** 2 = 4: thm -> EVAL ``(SQRT 9) ** 2``; -val it = |- SQRT 9 ** 2 = 9: thm - -> EVAL ``LOG2 4``; -val it = |- LOG2 4 = 2: thm -> EVAL ``2 ** (LOG2 4)``; -val it = |- 2 ** LOG2 4 = 4: thm -> EVAL ``2 ** (LOG2 5)``; -val it = |- 2 ** LOG2 5 = 4: thm -> EVAL ``2 ** (LOG2 6)``; -val it = |- 2 ** LOG2 6 = 4: thm -> EVAL ``2 ** (LOG2 7)``; -val it = |- 2 ** LOG2 7 = 4: thm -> EVAL ``2 ** (LOG2 8)``; -val it = |- 2 ** LOG2 8 = 8: thm - -> EVAL ``SQRT 9``; -val it = |- SQRT 9 = 3: thm -> EVAL ``SQRT 8``; -val it = |- SQRT 8 = 2: thm -> EVAL ``SQRT 7``; -val it = |- SQRT 7 = 2: thm -> EVAL ``SQRT 6``; -val it = |- SQRT 6 = 2: thm -> EVAL ``SQRT 5``; -val it = |- SQRT 5 = 2: thm -> EVAL ``SQRT 4``; -val it = |- SQRT 4 = 2: thm -> EVAL ``SQRT 3``; -val it = |- SQRT 3 = 1: thm -*) - -(* -EXP_BASE_LT_MONO |- !b. 1 < b ==> !n m. b ** m < b ** n <=> m < n -LT_EXP_ISO |- !e a b. 1 < e ==> (a < b <=> e ** a < e ** b) - -ROOT_exists |- !r n. 0 < r ==> ?rt. rt ** r <= n /\ n < SUC rt ** r -ROOT_UNIQUE |- !r n p. p ** r <= n /\ n < SUC p ** r ==> (ROOT r n = p) -ROOT_LE_MONO |- !r x y. 0 < r ==> x <= y ==> ROOT r x <= ROOT r y - -LOG_exists |- ?f. !a n. 1 < a /\ 0 < n ==> a ** f a n <= n /\ n < a ** SUC (f a n) -LOG_UNIQUE |- !a n p. a ** p <= n /\ n < a ** SUC p ==> (LOG a n = p) -LOG_LE_MONO |- !a x y. 1 < a /\ 0 < x ==> x <= y ==> LOG a x <= LOG a y - -LOG_EXP |- !n a b. 1 < a /\ 0 < b ==> (LOG a (a ** n * b) = n + LOG a b) -LOG |- !a n. 1 < a /\ 0 < n ==> a ** LOG a n <= n /\ n < a ** SUC (LOG a n) -*) - -(* Theorem: 0 < n ==> (SQRT n) ** 2 <= n /\ n < SUC (SQRT n) ** 2 *) -(* Proof: by ROOT: - |- !r n. 0 < r ==> ROOT r n ** r <= n /\ n < SUC (ROOT r n) ** r -*) -val SQRT_PROPERTY = store_thm( - "SQRT_PROPERTY", - ``!n. (SQRT n) ** 2 <= n /\ n < SUC (SQRT n) ** 2``, - rw[ROOT]); - -(* Get a useful theorem *) -Theorem SQRT_UNIQUE = logrootTheory.ROOT_UNIQUE |> SPEC ``2``; -(* val SQRT_UNIQUE = |- !n p. p ** 2 <= n /\ n < SUC p ** 2 ==> SQRT n = p: thm *) - -(* Obtain a theorem *) -val SQRT_THM = save_thm("SQRT_THM", - ROOT_THM |> SPEC ``2`` |> SIMP_RULE (srw_ss())[]); -(* val SQRT_THM = |- !n p. (SQRT n = p) <=> p ** 2 <= n /\ n < SUC p ** 2: thm *) - -(* Theorem: SQ (SQRT n) <= n *) -(* Proof: by SQRT_PROPERTY, EXP_2 *) -val SQ_SQRT_LE = store_thm( - "SQ_SQRT_LE", - ``!n. SQ (SQRT n) <= n``, - metis_tac[SQRT_PROPERTY, EXP_2]); - -(* Theorem: n <= m ==> SQRT n <= SQRT m *) -(* Proof: by ROOT_LE_MONO *) -val SQRT_LE = store_thm( - "SQRT_LE", - ``!n m. n <= m ==> SQRT n <= SQRT m``, - rw[ROOT_LE_MONO]); - -(* Theorem: n < m ==> SQRT n <= SQRT m *) -(* Proof: - Since n < m ==> n <= m by LESS_IMP_LESS_OR_EQ - This is true by ROOT_LE_MONO -*) -val SQRT_LT = store_thm( - "SQRT_LT", - ``!n m. n < m ==> SQRT n <= SQRT m``, - rw[ROOT_LE_MONO, LESS_IMP_LESS_OR_EQ]); - -(* Extract theorem *) -Theorem SQ_SQRT_LE_alt = SQRT_PROPERTY |> SPEC_ALL |> CONJUNCT1 |> GEN_ALL; -(* val SQ_SQRT_LE_alt = |- !n. SQRT n ** 2 <= n: thm *) - -(* Theorem: SQRT 0 = 0 *) -(* Proof: by ROOT_OF_0 *) -val SQRT_0 = store_thm( - "SQRT_0[simp]", - ``SQRT 0 = 0``, - rw[]); - -(* Theorem: SQRT 1 = 1 *) -(* Proof: by ROOT_OF_1 *) -val SQRT_1 = store_thm( - "SQRT_1[simp]", - ``SQRT 1 = 1``, - rw[]); - -(* Theorem: SQRT n = 0 <=> n = 0 *) -(* Proof: - If part: SQRT n = 0 ==> n = 0. - By contradiction, suppose n <> 0. - This means 1 <= n - Hence SQRT 1 <= SQRT n by SQRT_LE - so 1 <= SQRT n by SQRT_1 - This contradicts SQRT n = 0. - Only-if part: n = 0 ==> SQRT n = 0 - True since SQRT 0 = 0 by SQRT_0 -*) -val SQRT_EQ_0 = store_thm( - "SQRT_EQ_0", - ``!n. (SQRT n = 0) <=> (n = 0)``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `1 <= n` by decide_tac >> - `SQRT 1 <= SQRT n` by rw[SQRT_LE] >> - `SQRT 1 = 1` by rw[] >> - decide_tac); - -(* Theorem: SQRT n = 1 <=> n = 1 \/ n = 2 \/ n = 3 *) -(* Proof: - If part: SQRT n = 1 ==> (n = 1) \/ (n = 2) \/ (n = 3). - By contradiction, suppose n <> 1 /\ n <> 2 /\ n <> 3. - Note n <> 0 by SQRT_EQ_0 - This means 4 <= n - Hence SQRT 4 <= SQRT n by SQRT_LE - so 2 <= SQRT n by EVAL_TAC, SQRT 4 = 2 - This contradicts SQRT n = 1. - Only-if part: n = 1 \/ n = 2 \/ n = 3 ==> SQRT n = 1 - All these are true by EVAL_TAC -*) -val SQRT_EQ_1 = store_thm( - "SQRT_EQ_1", - ``!n. (SQRT n = 1) <=> ((n = 1) \/ (n = 2) \/ (n = 3))``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `n <> 0` by metis_tac[SQRT_EQ_0] >> - `4 <= n` by decide_tac >> - `SQRT 4 <= SQRT n` by rw[SQRT_LE] >> - `SQRT 4 = 2` by EVAL_TAC >> - decide_tac, - EVAL_TAC, - EVAL_TAC, - EVAL_TAC - ]); - -(* Theorem: SQRT (n ** 2) = n *) -(* Proof: - If 1 < n, true by ROOT_POWER, 0 < 2 - Otherwise, n = 0 or n = 1. - When n = 0, - SQRT (0 ** 2) = SQRT 0 = 0 by SQRT_0 - When n = 1, - SQRT (1 ** 2) = SQRT 1 = 1 by SQRT_1 -*) -val SQRT_EXP_2 = store_thm( - "SQRT_EXP_2", - ``!n. SQRT (n ** 2) = n``, - rpt strip_tac >> - Cases_on `1 < n` >- - fs[ROOT_POWER] >> - `(n = 0) \/ (n = 1)` by decide_tac >> - rw[]); - -(* Theorem alias *) -val SQRT_OF_SQ = save_thm("SQRT_OF_SQ", SQRT_EXP_2); -(* val SQRT_OF_SQ = |- !n. SQRT (n ** 2) = n: thm *) - -(* Theorem: SQRT (SQ n) = n *) -(* Proof: - SQRT (SQ n) - = SQRT (n ** 2) by EXP_2 - = n by SQRT_EXP_2 -*) -val SQRT_SQ = store_thm( - "SQRT_SQ", - ``!n. SQRT (SQ n) = n``, - metis_tac[SQRT_EXP_2, EXP_2]); - -(* Theorem: SQRT n <= n *) -(* Proof: - Note n <= n ** 2 by SELF_LE_SQ - Thus SQRT n <= SQRT (n ** 2) by SQRT_LE - or SQRT n <= n by SQRT_EXP_2 -*) -val SQRT_LE_SELF = store_thm( - "SQRT_LE_SELF", - ``!n. SQRT n <= n``, - metis_tac[SELF_LE_SQ, SQRT_LE, SQRT_EXP_2]); - -(* Theorem: (n <= SQRT n) <=> ((n = 0) \/ (n = 1)) *) -(* Proof: - If part: (n <= SQRT n) ==> ((n = 0) \/ (n = 1)) - By contradiction, suppose n <> 0 /\ n <> 1. - Then 1 < n, implying n ** 2 <= SQRT n ** 2 by EXP_BASE_LE_MONO - but SQRT n ** 2 <= n by SQRT_PROPERTY - so n ** 2 <= n by LESS_EQ_TRANS - or n * n <= n * 1 by EXP_2 - or n <= 1 by LE_MULT_LCANCEL, n <> 0. - This contradicts 1 < n. - Only-if part: ((n = 0) \/ (n = 1)) ==> (n <= SQRT n) - This is to show: - (1) 0 <= SQRT 0, true by SQRT 0 = 0 by SQRT_0 - (2) 1 <= SQRT 1, true by SQRT 1 = 1 by SQRT_1 -*) -val SQRT_GE_SELF = store_thm( - "SQRT_GE_SELF", - ``!n. (n <= SQRT n) <=> ((n = 0) \/ (n = 1))``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `1 < n` by decide_tac >> - `n ** 2 <= SQRT n ** 2` by rw[EXP_BASE_LE_MONO] >> - `SQRT n ** 2 <= n` by rw[SQRT_PROPERTY] >> - `n ** 2 <= n` by metis_tac[LESS_EQ_TRANS] >> - `n * n <= n * 1` by metis_tac[EXP_2, MULT_RIGHT_1] >> - `n <= 1` by metis_tac[LE_MULT_LCANCEL] >> - decide_tac, - rw[], - rw[] - ]); - -(* Theorem: (SQRT n = n) <=> ((n = 0) \/ (n = 1)) *) -(* Proof: by ROOT_EQ_SELF, 0 < 2 *) -val SQRT_EQ_SELF = store_thm( - "SQRT_EQ_SELF", - ``!n. (SQRT n = n) <=> ((n = 0) \/ (n = 1))``, - rw[ROOT_EQ_SELF]); - -(* Theorem: SQRT n <= m ==> n <= 3 * (m ** 2) *) -(* Proof: - Note n < (SUC (SQRT n)) ** 2 by SQRT_PROPERTY - = SUC ((SQRT n) ** 2) + 2 * SQRT n by SUC_SQ - Thus n <= m ** 2 + 2 * m by SQRT n <= m - <= m ** 2 + 2 * m ** 2 by arithmetic - = 3 * m ** 2 -*) -val SQRT_LE_IMP = store_thm( - "SQRT_LE_IMP", - ``!n m. SQRT n <= m ==> n <= 3 * (m ** 2)``, - rpt strip_tac >> - `n < (SUC (SQRT n)) ** 2` by rw[SQRT_PROPERTY] >> - `SUC (SQRT n) ** 2 = SUC ((SQRT n) ** 2) + 2 * SQRT n` by rw[SUC_SQ] >> - `SQRT n ** 2 <= m ** 2` by rw[] >> - `2 * SQRT n <= 2 * m` by rw[] >> - `2 * m <= 2 * m * m` by rw[] >> - `2 * m * m = 2 * m ** 2` by rw[] >> - decide_tac); - -(* Theorem: (SQRT n) * (SQRT m) <= SQRT (n * m) *) -(* Proof: - Note (SQRT n) ** 2 <= n by SQRT_PROPERTY - and (SQRT m) ** 2 <= m by SQRT_PROPERTY - so (SQRT n) ** 2 * (SQRT m) ** 2 <= n * m by LE_MONO_MULT2 - or ((SQRT n) * (SQRT m)) ** 2 <= n * m by EXP_BASE_MULT - ==> (SQRT n) * (SQRT m) <= SQRT (n * m) by SQRT_LE, SQRT_OF_SQ -*) -Theorem SQRT_MULT_LE: - !n m. (SQRT n) * (SQRT m) <= SQRT (n * m) -Proof - rpt strip_tac >> - qabbrev_tac `h = SQRT n` >> - qabbrev_tac `k = SQRT m` >> - `h ** 2 <= n` by simp[SQRT_PROPERTY, Abbr`h`] >> - `k ** 2 <= m` by simp[SQRT_PROPERTY, Abbr`k`] >> - `(h * k) ** 2 <= n * m` by metis_tac[LE_MONO_MULT2, EXP_BASE_MULT] >> - metis_tac[SQRT_LE, SQRT_OF_SQ] -QED - -(* Theorem: SQRT n < m ==> n < m ** 2 *) -(* Proof: - SQRT n < m - ==> SUC (SQRT n) <= m by arithmetic - ==> (SUC (SQRT m)) ** 2 <= m ** 2 by EXP_EXP_LE_MONO - But n < (SUC (SQRT n)) ** 2 by SQRT_PROPERTY - Thus n < m ** 2 by inequality -*) -Theorem SQRT_LT_IMP: - !n m. SQRT n < m ==> n < m ** 2 -Proof - rpt strip_tac >> - `SUC (SQRT n) <= m` by decide_tac >> - `SUC (SQRT n) ** 2 <= m ** 2` by simp[EXP_EXP_LE_MONO] >> - `n < SUC (SQRT n) ** 2` by simp[SQRT_PROPERTY] >> - decide_tac -QED - -(* Theorem: n < SQRT m ==> n ** 2 < m *) -(* Proof: - n < SQRT m - ==> n ** 2 < (SQRT m) ** 2 by EXP_EXP_LT_MONO - But (SQRT m) ** 2 <= m by SQRT_PROPERTY - Thus n ** 2 < m by inequality -*) -Theorem LT_SQRT_IMP: - !n m. n < SQRT m ==> n ** 2 < m -Proof - rpt strip_tac >> - `n ** 2 < (SQRT m) ** 2` by simp[EXP_EXP_LT_MONO] >> - `(SQRT m) ** 2 <= m` by simp[SQRT_PROPERTY] >> - decide_tac -QED - -(* Theorem: SQRT n < SQRT m ==> n < m *) -(* Proof: - SQRT n < SQRT m - ==> n < (SQRT m) ** 2 by SQRT_LT_IMP - and (SQRT m) ** 2 <= m by SQRT_PROPERTY - so n < m by inequality -*) -Theorem SQRT_LT_SQRT: - !n m. SQRT n < SQRT m ==> n < m -Proof - rpt strip_tac >> - imp_res_tac SQRT_LT_IMP >> - `(SQRT m) ** 2 <= m` by simp[SQRT_PROPERTY] >> - decide_tac -QED - -(* Non-theorems: - SQRT n <= SQRT m ==> n <= m - counter-example: SQRT 5 = 2 = SQRT 4, but 5 > 4. - - n < m ==> SQRT n < SQRT m - counter-example: 4 < 5, but SQRT 4 = 2 = SQRT 5. -*) - -(* ------------------------------------------------------------------------- *) -(* Square predicate *) -(* ------------------------------------------------------------------------- *) - -(* Define square predicate. *) - -Definition square_def[nocompute]: - square (n:num) = ?k. n = k * k -End -(* use [nocompute] as this is not effective. *) - -(* Theorem: square n = ?k. n = k ** 2 *) -(* Proof: by square_def. *) -Theorem square_alt: - !n. square n = ?k. n = k ** 2 -Proof - simp[square_def] -QED - -(* Theorem: square n <=> (SQRT n) ** 2 = n *) -(* Proof: - If part: square n ==> (SQRT n) ** 2 = n - This is true by SQRT_SQ, EXP_2 - Only-if part: (SQRT n) ** 2 = n ==> square n - Take k = SQRT n for n = k ** 2. -*) -Theorem square_eqn[compute]: - !n. square n <=> (SQRT n) ** 2 = n -Proof - metis_tac[square_def, SQRT_SQ, EXP_2] -QED - -(* -EVAL ``square 10``; F -EVAL ``square 16``; T -*) - -(* Theorem: square 0 *) -(* Proof: by 0 = 0 * 0. *) -Theorem square_0: - square 0 -Proof - simp[square_def] -QED - -(* Theorem: square 1 *) -(* Proof: by 1 = 1 * 1. *) -Theorem square_1: - square 1 -Proof - simp[square_def] -QED - -(* Theorem: prime p ==> ~square p *) -(* Proof: - By contradiction, suppose (square p). - Then p = k * k by square_def - thus k divides p by divides_def - so k = 1 or k = p by prime_def - If k = 1, - then p = 1 * 1 = 1 by arithmetic - but p <> 1 by NOT_PRIME_1 - If k = p, - then p * 1 = p * p by arithmetic - or 1 = p by EQ_MULT_LCANCEL, NOT_PRIME_0 - but p <> 1 by NOT_PRIME_1 -*) -Theorem prime_non_square: - !p. prime p ==> ~square p -Proof - rpt strip_tac >> - `?k. p = k * k` by rw[GSYM square_def] >> - `k divides p` by metis_tac[divides_def] >> - `(k = 1) \/ (k = p)` by metis_tac[prime_def] >- - fs[NOT_PRIME_1] >> - `p * 1 = p * p` by metis_tac[MULT_RIGHT_1] >> - `1 = p` by metis_tac[EQ_MULT_LCANCEL, NOT_PRIME_0] >> - metis_tac[NOT_PRIME_1] -QED - -(* Theorem: ~square n ==> (SQRT n) * (SQRT n) < n *) -(* Proof: - Note (SQRT n) * (SQRT n) <= n by SQ_SQRT_LE - but (SQRT n) * (SQRT n) <> n by square_def - so (SQRT n) * (SQRT n) < n by inequality -*) -Theorem SQ_SQRT_LT: - !n. ~square n ==> (SQRT n) * (SQRT n) < n -Proof - rpt strip_tac >> - `(SQRT n) * (SQRT n) <= n` by simp[SQ_SQRT_LE] >> - `(SQRT n) * (SQRT n) <> n` by metis_tac[square_def] >> - decide_tac -QED - -(* Theorem: ~square n ==> SQRT n ** 2 < n *) -(* Proof: by SQ_SQRT_LT, EXP_2. *) -Theorem SQ_SQRT_LT_alt: - !n. ~square n ==> SQRT n ** 2 < n -Proof - metis_tac[SQ_SQRT_LT, EXP_2] -QED - -(* Theorem: ~square n ==> ((2 * m + 1) ** 2 < n <=> m < HALF (1 + SQRT n)) *) -(* Proof: - If part: (2 * m + 1) ** 2 < n ==> m < HALF (1 + SQRT n) - (2 * m + 1) ** 2 < n - ==> 2 * m + 1 <= SQRT n by SQRT_LT, SQRT_OF_SQ - ==> 2 * (m + 1) <= 1 + SQRT n by arithmetic - ==> m < HALF (1 + SQRT n) by X_LT_DIV - Only-if part: m < HALF (1 + SQRT n) ==> (2 * m + 1) ** 2 < n - m < HALF (1 + SQRT n) - <=> 2 * (m + 1) <= 1 + SQRT n by X_LT_DIV - <=> 2 * m + 1 <= SQRT n by arithmetic - <=> (2 * m + 1) ** 2 <= (SQRT n) ** 2 by EXP_EXP_LE_MONO - ==> (2 * m + 1) ** 2 <= n by SQ_SQRT_LE_alt - But n <> (2 * m + 1) ** 2 by ~square n - so (2 * m + 1) ** 2 < n -*) -Theorem odd_square_lt: - !n m. ~square n ==> ((2 * m + 1) ** 2 < n <=> m < HALF (1 + SQRT n)) -Proof - rw[EQ_IMP_THM] >| [ - `2 * m + 1 <= SQRT n` by metis_tac[SQRT_LT, SQRT_OF_SQ] >> - `2 * (m + 1) <= 1 + SQRT n` by decide_tac >> - fs[X_LT_DIV], - `2 * (m + 1) <= 1 + SQRT n` by fs[X_LT_DIV] >> - `2 * m + 1 <= SQRT n` by decide_tac >> - `(2 * m + 1) ** 2 <= (SQRT n) ** 2` by simp[] >> - `(SQRT n) ** 2 <= n` by fs[SQ_SQRT_LE_alt] >> - `n <> (2 * m + 1) ** 2` by metis_tac[square_alt] >> - decide_tac - ] -QED - -(* ------------------------------------------------------------------------- *) -(* Logarithm *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 1 < a ==> LOG a (a ** n) = n *) -(* Proof: - LOG a (a ** n) - = LOG a ((a ** n) * 1) by MULT_RIGHT_1 - = n + LOG a 1 by LOG_EXP - = n + 0 by LOG_1 - = n by ADD_0 -*) -val LOG_EXACT_EXP = store_thm( - "LOG_EXACT_EXP", - ``!a. 1 < a ==> !n. LOG a (a ** n) = n``, - metis_tac[MULT_RIGHT_1, LOG_EXP, LOG_1, ADD_0, DECIDE``0 < 1``]); - -(* Theorem: 1 < a /\ 0 < b /\ b <= a ** n ==> LOG a b <= n *) -(* Proof: - Given b <= a ** n - LOG a b <= LOG a (a ** n) by LOG_LE_MONO - = n by LOG_EXACT_EXP -*) -val EXP_TO_LOG = store_thm( - "EXP_TO_LOG", - ``!a b n. 1 < a /\ 0 < b /\ b <= a ** n ==> LOG a b <= n``, - metis_tac[LOG_LE_MONO, LOG_EXACT_EXP]); - -(* Theorem: 1 < a /\ 0 < n ==> !p. (LOG a n = p) <=> (a ** p <= n /\ n < a ** SUC p) *) -(* Proof: - If part: 1 < a /\ 0 < n ==> a ** LOG a n <= n /\ n < a ** SUC (LOG a n) - This is true by LOG. - Only-if part: a ** p <= n /\ n < a ** SUC p ==> LOG a n = p - This is true by LOG_UNIQUE -*) -val LOG_THM = store_thm( - "LOG_THM", - ``!a n. 1 < a /\ 0 < n ==> !p. (LOG a n = p) <=> (a ** p <= n /\ n < a ** SUC p)``, - metis_tac[LOG, LOG_UNIQUE]); - -(* Theorem: LOG m n = if m <= 1 \/ (n = 0) then LOG m n - else if n < m then 0 else SUC (LOG m (n DIV m)) *) -(* Proof: by LOG_RWT *) -val LOG_EVAL = store_thm( - "LOG_EVAL[compute]", - ``!m n. LOG m n = if m <= 1 \/ (n = 0) then LOG m n - else if n < m then 0 else SUC (LOG m (n DIV m))``, - rw[LOG_RWT]); -(* Put to computeLib for LOG evaluation of any base *) - -(* -> EVAL ``MAP (LOG 3) [1 .. 20]``; = - [0; 0; 1; 1; 1; 1; 1; 1; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2]: thm -> EVAL ``MAP (LOG 3) [1 .. 30]``; = - [0; 0; 1; 1; 1; 1; 1; 1; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 3; 3; 3; 3]: thm -*) - -(* Theorem: 1 < a /\ 0 < n ==> - !p. (LOG a n = p) <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n *) -(* Proof: - Note !p. LOG a n = p - <=> n < a ** SUC p /\ a ** p <= n by LOG_THM - <=> n < a ** SUC p /\ a * a ** p <= a * n by LE_MULT_LCANCEL - <=> n < a ** SUC p /\ a ** SUC p <= a * n by EXP - <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n by arithmetic -*) -val LOG_TEST = store_thm( - "LOG_TEST", - ``!a n. 1 < a /\ 0 < n ==> - !p. (LOG a n = p) <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n``, - rw[EQ_IMP_THM] >| [ - `n < a ** SUC (LOG a n)` by metis_tac[LOG_THM] >> - decide_tac, - `a ** (LOG a n) <= n` by metis_tac[LOG_THM] >> - rw[EXP], - `a * a ** p <= a * n` by fs[EXP] >> - `a ** p <= n` by fs[] >> - `n < a ** SUC p` by decide_tac >> - metis_tac[LOG_THM] - ]); - -(* For continuous functions, log_b (x ** y) = y * log_b x. *) - -(* Theorem: 1 < b /\ 0 < x /\ 0 < n ==> - n * LOG b x <= LOG b (x ** n) /\ LOG b (x ** n) < n * SUC (LOG b x) *) -(* Proof: - Note: -> LOG_THM |> SPEC ``b:num`` |> SPEC ``x:num``; -val it = |- 1 < b /\ 0 < x ==> !p. LOG b x = p <=> b ** p <= x /\ x < b ** SUC p: thm -> LOG_THM |> SPEC ``b:num`` |> SPEC ``(x:num) ** n``; -val it = |- 1 < b /\ 0 < x ** n ==> - !p. LOG b (x ** n) = p <=> b ** p <= x ** n /\ x ** n < b ** SUC p: thm - - Let y = LOG b x, z = LOG b (x ** n). - Then b ** y <= x /\ x < b ** SUC y by LOG_THM, (1) - and b ** z <= x ** n /\ x ** n < b ** SUC z by LOG_THM, (2) - From (1), - b ** (n * y) <= x ** n /\ by EXP_EXP_LE_MONO, EXP_EXP_MULT - x ** n < b ** (n * SUC y) by EXP_EXP_LT_MONO, EXP_EXP_MULT, 0 < n - Cross combine with (2), - b ** (n * y) <= x ** n < b ** SUC z - and b ** z <= x ** n < b ** (n * y) - ==> n * y < SUC z /\ z < n * SUC y by EXP_BASE_LT_MONO - or n * y <= z /\ z < n * SUC y -*) -val LOG_POWER = store_thm( - "LOG_POWER", - ``!b x n. 1 < b /\ 0 < x /\ 0 < n ==> - n * LOG b x <= LOG b (x ** n) /\ LOG b (x ** n) < n * SUC (LOG b x)``, - ntac 4 strip_tac >> - `0 < x ** n` by rw[] >> - qabbrev_tac `y = LOG b x` >> - qabbrev_tac `z = LOG b (x ** n)` >> - `b ** y <= x /\ x < b ** SUC y` by metis_tac[LOG_THM] >> - `b ** z <= x ** n /\ x ** n < b ** SUC z` by metis_tac[LOG_THM] >> - `b ** (y * n) <= x ** n` by rw[EXP_EXP_MULT] >> - `x ** n < b ** ((SUC y) * n)` by rw[EXP_EXP_MULT] >> - `b ** (y * n) < b ** SUC z` by decide_tac >> - `b ** z < b ** (SUC y * n)` by decide_tac >> - `y * n < SUC z` by metis_tac[EXP_BASE_LT_MONO] >> - `z < SUC y * n` by metis_tac[EXP_BASE_LT_MONO] >> - decide_tac); - -(* Theorem: 1 < a /\ 0 < n /\ a <= b ==> LOG b n <= LOG a n *) -(* Proof: - Let x = LOG a n, y = LOG b n. To show: y <= x. - By contradiction, suppose x < y. - Then SUC x <= y. - Note a ** x <= n /\ n < a ** SUC x by LOG_THM - and b ** y <= n /\ n < b ** SUC y by LOG_THM - But a <= b - a ** SUC x - <= b ** SUC x by EXP_EXP_LE_MONO, 0 < SUC x - <= b ** y by EXP_BASE_LEQ_MONO_IMP, SUC x <= y - This leads to n < a ** SUC x <= b ** y <= n, a contradiction. -*) -val LOG_LE_REVERSE = store_thm( - "LOG_LE_REVERSE", - ``!a b n. 1 < a /\ 0 < n /\ a <= b ==> LOG b n <= LOG a n``, - rpt strip_tac >> - qabbrev_tac `x = LOG a n` >> - qabbrev_tac `y = LOG b n` >> - spose_not_then strip_assume_tac >> - `1 < b /\ SUC x <= y` by decide_tac >> - `a ** x <= n /\ n < a ** SUC x` by metis_tac[LOG_THM] >> - `b ** y <= n /\ n < b ** SUC y` by metis_tac[LOG_THM] >> - `a ** SUC x <= b ** SUC x` by rw[EXP_EXP_LE_MONO] >> - `b ** SUC x <= b ** y` by rw[EXP_BASE_LEQ_MONO_IMP] >> - decide_tac); - -(* Overload LOG base 2 *) -val _ = overload_on ("LOG2", ``\n. LOG 2 n``); - -(* Theorem: LOG2 1 = 0 *) -(* Proof: - LOG_1 |> SPEC ``2``; - val it = |- 1 < 2 ==> LOG2 1 = 0: thm -*) -val LOG2_1 = store_thm( - "LOG2_1[simp]", - ``LOG2 1 = 0``, - rw[LOG_1]); - -(* Theorem: LOG2 2 = 1 *) -(* Proof: - LOG_BASE |> SPEC ``2``; - val it = |- 1 < 2 ==> LOG2 2 = 1: thm -*) -val LOG2_2 = store_thm( - "LOG2_2[simp]", - ``LOG2 2 = 1``, - rw[LOG_BASE]); - -(* Obtain a theorem *) -val LOG2_THM = save_thm("LOG2_THM", - LOG_THM |> SPEC ``2`` |> SIMP_RULE (srw_ss())[]); -(* val LOG2_THM = |- !n. 0 < n ==> !p. (LOG2 n = p) <=> 2 ** p <= n /\ n < 2 ** SUC p: thm *) - -(* Obtain a theorem *) -Theorem LOG2_PROPERTY = logrootTheory.LOG |> SPEC ``2`` |> SIMP_RULE (srw_ss())[]; -(* val LOG2_PROPERTY = |- !n. 0 < n ==> 2 ** LOG2 n <= n /\ n < 2 ** SUC (LOG2 n): thm *) - -(* Theorem: 0 < n ==> 2 ** LOG2 n <= n) *) -(* Proof: by LOG2_PROPERTY *) -val TWO_EXP_LOG2_LE = store_thm( - "TWO_EXP_LOG2_LE", - ``!n. 0 < n ==> 2 ** LOG2 n <= n``, - rw[LOG2_PROPERTY]); - -(* Obtain a theorem *) -val LOG2_UNIQUE = save_thm("LOG2_UNIQUE", - LOG_UNIQUE |> SPEC ``2`` |> SPEC ``n:num`` |> SPEC ``m:num`` |> GEN_ALL); -(* val LOG2_UNIQUE = |- !n m. 2 ** m <= n /\ n < 2 ** SUC m ==> LOG2 n = m: thm *) - -(* Theorem: 0 < n ==> ((LOG2 n = 0) <=> (n = 1)) *) -(* Proof: - LOG_EQ_0 |> SPEC ``2``; - |- !b. 1 < 2 /\ 0 < b ==> (LOG2 b = 0 <=> b < 2) -*) -val LOG2_EQ_0 = store_thm( - "LOG2_EQ_0", - ``!n. 0 < n ==> ((LOG2 n = 0) <=> (n = 1))``, - rw[LOG_EQ_0]); - -(* Theorem: 0 < n ==> LOG2 n = 1 <=> (n = 2) \/ (n = 3) *) -(* Proof: - If part: LOG2 n = 1 ==> n = 2 \/ n = 3 - Note 2 ** 1 <= n /\ n < 2 ** SUC 1 by LOG2_PROPERTY - or 2 <= n /\ n < 4 by arithmetic - Thus n = 2 or n = 3. - Only-if part: LOG2 2 = 1 /\ LOG2 3 = 1 - Note LOG2 2 = 1 by LOG2_2 - and LOG2 3 = 1 by LOG2_UNIQUE - since 2 ** 1 <= 3 /\ 3 < 2 ** SUC 1 ==> (LOG2 3 = 1) -*) -val LOG2_EQ_1 = store_thm( - "LOG2_EQ_1", - ``!n. 0 < n ==> ((LOG2 n = 1) <=> ((n = 2) \/ (n = 3)))``, - rw_tac std_ss[EQ_IMP_THM] >| [ - imp_res_tac LOG2_PROPERTY >> - rfs[], - rw[], - irule LOG2_UNIQUE >> - simp[] - ]); - -(* Obtain theorem *) -val LOG2_LE_MONO = save_thm("LOG2_LE_MONO", - LOG_LE_MONO |> SPEC ``2`` |> SPEC ``n:num`` |> SPEC ``m:num`` - |> SIMP_RULE (srw_ss())[] |> GEN_ALL); -(* val LOG2_LE_MONO = |- !n m. 0 < n ==> n <= m ==> LOG2 n <= LOG2 m: thm *) - -(* Theorem: 0 < n /\ n <= m ==> LOG2 n <= LOG2 m *) -(* Proof: by LOG_LE_MONO *) -val LOG2_LE = store_thm( - "LOG2_LE", - ``!n m. 0 < n /\ n <= m ==> LOG2 n <= LOG2 m``, - rw[LOG_LE_MONO, DECIDE``1 < 2``]); - -(* Note: next is not LOG2_LT_MONO! *) - -(* Theorem: 0 < n /\ n < m ==> LOG2 n <= LOG2 m *) -(* Proof: - Since n < m ==> n <= m by LESS_IMP_LESS_OR_EQ - This is true by LOG_LE_MONO -*) -val LOG2_LT = store_thm( - "LOG2_LT", - ``!n m. 0 < n /\ n < m ==> LOG2 n <= LOG2 m``, - rw[LOG_LE_MONO, LESS_IMP_LESS_OR_EQ, DECIDE``1 < 2``]); - -(* Theorem: 0 < n ==> LOG2 n < n *) -(* Proof: - LOG2 n - < 2 ** (LOG2 n) by X_LT_EXP_X, 1 < 2 - <= n by LOG2_PROPERTY, 0 < n -*) -val LOG2_LT_SELF = store_thm( - "LOG2_LT_SELF", - ``!n. 0 < n ==> LOG2 n < n``, - rpt strip_tac >> - `LOG2 n < 2 ** (LOG2 n)` by rw[X_LT_EXP_X] >> - `2 ** LOG2 n <= n` by rw[LOG2_PROPERTY] >> - decide_tac); - -(* Theorem: 0 < n ==> LOG2 n <> n *) -(* Proof: - Note n < LOG2 n by LOG2_LT_SELF - Thus n <> LOG2 n by arithmetic -*) -val LOG2_NEQ_SELF = store_thm( - "LOG2_NEQ_SELF", - ``!n. 0 < n ==> LOG2 n <> n``, - rpt strip_tac >> - `LOG2 n < n` by rw[LOG2_LT_SELF] >> - decide_tac); - -(* Theorem: LOG2 n = n ==> n = 0 *) -(* Proof: by LOG2_NEQ_SELF *) -val LOG2_EQ_SELF = store_thm( - "LOG2_EQ_SELF", - ``!n. (LOG2 n = n) ==> (n = 0)``, - metis_tac[LOG2_NEQ_SELF, DECIDE``~(0 < n) <=> (n = 0)``]); - -(* Theorem: 1 < n ==> 0 < LOG2 n *) -(* Proof: - 1 < n - ==> 2 <= n - ==> LOG2 2 <= LOG2 n by LOG2_LE - ==> 1 <= LOG2 n by LOG_BASE, LOG2 2 = 1 - or 0 < LOG2 n -*) -val LOG2_POS = store_thm( - "LOG2_POS[simp]", - ``!n. 1 < n ==> 0 < LOG2 n``, - rpt strip_tac >> - `LOG2 2 = 1` by rw[LOG_BASE, DECIDE``1 < 2``] >> - `2 <= n` by decide_tac >> - `LOG2 2 <= LOG2 n` by rw[LOG2_LE] >> - decide_tac); - -(* Theorem: 1 < n ==> 1 < 2 * LOG2 n *) -(* Proof: - 1 < n - ==> 2 <= n - ==> LOG2 2 <= LOG2 n by LOG2_LE - ==> 1 <= LOG2 n by LOG_BASE, LOG2 2 = 1 - ==> 2 * 1 <= 2 * LOG2 n by LE_MULT_LCANCEL - or 1 < 2 * LOG2 n -*) -val LOG2_TWICE_LT = store_thm( - "LOG2_TWICE_LT", - ``!n. 1 < n ==> 1 < 2 * (LOG2 n)``, - rpt strip_tac >> - `LOG2 2 = 1` by rw[LOG_BASE, DECIDE``1 < 2``] >> - `2 <= n` by decide_tac >> - `LOG2 2 <= LOG2 n` by rw[LOG2_LE] >> - `1 <= LOG2 n` by decide_tac >> - `2 <= 2 * LOG2 n` by rw_tac arith_ss[LE_MULT_LCANCEL, DECIDE``0 < 2``] >> - decide_tac); - -(* Theorem: 1 < n ==> 4 <= (2 * (LOG2 n)) ** 2 *) -(* Proof: - 1 < n - ==> 2 <= n - ==> LOG2 2 <= LOG2 n by LOG2_LE - ==> 1 <= LOG2 n by LOG2_2, or LOG_BASE, LOG2 2 = 1 - ==> 2 * 1 <= 2 * LOG2 n by LE_MULT_LCANCEL - ==> 2 ** 2 <= (2 * LOG2 n) ** 2 by EXP_EXP_LE_MONO - ==> 4 <= (2 * LOG2 n) ** 2 -*) -val LOG2_TWICE_SQ = store_thm( - "LOG2_TWICE_SQ", - ``!n. 1 < n ==> 4 <= (2 * (LOG2 n)) ** 2``, - rpt strip_tac >> - `LOG2 2 = 1` by rw[] >> - `2 <= n` by decide_tac >> - `LOG2 2 <= LOG2 n` by rw[LOG2_LE] >> - `1 <= LOG2 n` by decide_tac >> - `2 <= 2 * LOG2 n` by rw_tac arith_ss[LE_MULT_LCANCEL, DECIDE``0 < 2``] >> - `2 ** 2 <= (2 * LOG2 n) ** 2` by rw[EXP_EXP_LE_MONO, DECIDE``0 < 2``] >> - `2 ** 2 = 4` by rw_tac arith_ss[] >> - decide_tac); - -(* Theorem: 0 < n ==> 4 <= (2 * SUC (LOG2 n)) ** 2 *) -(* Proof: - 0 < n - ==> 1 <= n - ==> LOG2 1 <= LOG2 n by LOG2_LE - ==> 0 <= LOG2 n by LOG2_1, or LOG_BASE, LOG2 1 = 0 - ==> 1 <= SUC (LOG2 n) by LESS_EQ_MONO - ==> 2 * 1 <= 2 * SUC (LOG2 n) by LE_MULT_LCANCEL - ==> 2 ** 2 <= (2 * SUC (LOG2 n)) ** 2 by EXP_EXP_LE_MONO - ==> 4 <= (2 * SUC (LOG2 n)) ** 2 -*) -val LOG2_SUC_TWICE_SQ = store_thm( - "LOG2_SUC_TWICE_SQ", - ``!n. 0 < n ==> 4 <= (2 * SUC (LOG2 n)) ** 2``, - rpt strip_tac >> - `LOG2 1 = 0` by rw[] >> - `1 <= n` by decide_tac >> - `LOG2 1 <= LOG2 n` by rw[LOG2_LE] >> - `1 <= SUC (LOG2 n)` by decide_tac >> - `2 <= 2 * SUC (LOG2 n)` by rw_tac arith_ss[LE_MULT_LCANCEL, DECIDE``0 < 2``] >> - `2 ** 2 <= (2 * SUC (LOG2 n)) ** 2` by rw[EXP_EXP_LE_MONO, DECIDE``0 < 2``] >> - `2 ** 2 = 4` by rw_tac arith_ss[] >> - decide_tac); - -(* Theorem: 1 < n ==> 1 < (SUC (LOG2 n)) ** 2 *) -(* Proof: - Note 0 < LOG2 n by LOG2_POS, 1 < n - so 1 < SUC (LOG2 n) by arithmetic - ==> 1 < (SUC (LOG2 n)) ** 2 by ONE_LT_EXP, 0 < 2 -*) -val LOG2_SUC_SQ = store_thm( - "LOG2_SUC_SQ", - ``!n. 1 < n ==> 1 < (SUC (LOG2 n)) ** 2``, - rpt strip_tac >> - `0 < LOG2 n` by rw[] >> - `1 < SUC (LOG2 n)` by decide_tac >> - rw[ONE_LT_EXP]); - -(* Theorem: 1 < m ==> 0 < SUC (LOG2 n) * (m ** 2 DIV 2) *) -(* Proof: - Since 1 < m ==> 1 < m ** 2 DIV 2 by ONE_LT_HALF_SQ - Hence 0 < m ** 2 DIV 2 - and 0 < 0 < SUC (LOG2 n) by prim_recTheory.LESS_0 - Therefore 0 < SUC (LOG2 n) * (m ** 2 DIV 2) by ZERO_LESS_MULT -*) -val LOG2_SUC_TIMES_SQ_DIV_2_POS = store_thm( - "LOG2_SUC_TIMES_SQ_DIV_2_POS", - ``!n m. 1 < m ==> 0 < SUC (LOG2 n) * (m ** 2 DIV 2)``, - rpt strip_tac >> - `1 < m ** 2 DIV 2` by rw[ONE_LT_HALF_SQ] >> - `0 < m ** 2 DIV 2 /\ 0 < SUC (LOG2 n)` by decide_tac >> - rw[ZERO_LESS_MULT]); - -(* Theorem: LOG2 (2 ** n) = n *) -(* Proof: by LOG_EXACT_EXP *) -val LOG2_2_EXP = store_thm( - "LOG2_2_EXP", - ``!n. LOG2 (2 ** n) = n``, - rw[LOG_EXACT_EXP]); - -(* Theorem: (2 ** (LOG2 n) = n) <=> ?k. n = 2 ** k *) -(* Proof: - If part: 2 ** LOG2 n = n ==> ?k. n = 2 ** k - True by taking k = LOG2 n. - Only-if part: 2 ** LOG2 (2 ** k) = 2 ** k - Note LOG2 n = k by LOG_EXACT_EXP, 1 < 2 - or n = 2 ** k = 2 ** LOG2 n. -*) -val LOG2_EXACT_EXP = store_thm( - "LOG2_EXACT_EXP", - ``!n. (2 ** (LOG2 n) = n) <=> ?k. n = 2 ** k``, - metis_tac[LOG2_2_EXP]); - -(* Theorem: 0 < n ==> LOG2 (n * 2 ** m) = (LOG2 n) + m *) -(* Proof: - LOG_EXP |> SPEC ``m:num`` |> SPEC ``2`` |> SPEC ``n:num``; - val it = |- 1 < 2 /\ 0 < n ==> LOG2 (2 ** m * n) = m + LOG2 n: thm -*) -val LOG2_MULT_EXP = store_thm( - "LOG2_MULT_EXP", - ``!n m. 0 < n ==> (LOG2 (n * 2 ** m) = (LOG2 n) + m)``, - rw[GSYM LOG_EXP]); - -(* Theorem: 0 < n ==> (LOG2 (2 * n) = 1 + LOG2 n) *) -(* Proof: - LOG_MULT |> SPEC ``2`` |> SPEC ``n:num``; - val it = |- 1 < 2 /\ 0 < n ==> LOG2 (TWICE n) = SUC (LOG2 n): thm -*) -val LOG2_TWICE = store_thm( - "LOG2_TWICE", - ``!n. 0 < n ==> (LOG2 (2 * n) = 1 + LOG2 n)``, - rw[LOG_MULT]); - -(* Theorem: 1 < n ==> LOG2 (HALF n) = (LOG2 n) - 1 *) -(* Proof: - Note: > LOG_DIV |> SPEC ``2`` |> SPEC ``n:num``; - val it = |- 1 < 2 /\ 2 <= n ==> LOG2 n = 1 + LOG2 (HALF n): thm - Hence the result. -*) -val LOG2_HALF = store_thm( - "LOG2_HALF", - ``!n. 1 < n ==> (LOG2 (HALF n) = (LOG2 n) - 1)``, - rpt strip_tac >> - `LOG2 n = 1 + LOG2 (HALF n)` by rw[LOG_DIV] >> - decide_tac); - -(* Theorem: 1 < n ==> (LOG2 n = 1 + LOG2 (HALF n)) *) -(* Proof: by LOG_DIV: -> LOG_DIV |> SPEC ``2``; -val it = |- !x. 1 < 2 /\ 2 <= x ==> (LOG2 x = 1 + LOG2 (HALF x)): thm -*) -val LOG2_BY_HALF = store_thm( - "LOG2_BY_HALF", - ``!n. 1 < n ==> (LOG2 n = 1 + LOG2 (HALF n))``, - rw[LOG_DIV]); - -(* Theorem: 2 ** m < n ==> LOG2 (n DIV 2 ** m) = (LOG2 n) - m *) -(* Proof: - By induction on m. - Base: !n. 2 ** 0 < n ==> LOG2 (n DIV 2 ** 0) = LOG2 n - 0 - LOG2 (n DIV 2 ** 0) - = LOG2 (n DIV 1) by EXP_0 - = LOG2 n by DIV_1 - = LOG2 n - 0 by SUB_0 - Step: !n. 2 ** m < n ==> LOG2 (n DIV 2 ** m) = LOG2 n - m ==> - !n. 2 ** SUC m < n ==> LOG2 (n DIV 2 ** SUC m) = LOG2 n - SUC m - Note 2 ** SUC m = 2 * 2 ** m by EXP, [1] - Thus HALF (2 * 2 ** m) <= HALF n by DIV_LE_MONOTONE - or 2 ** m <= HALF n by HALF_TWICE - If 2 ** m < HALF n, - LOG2 (n DIV 2 ** SUC m) - = LOG2 (n DIV (2 * 2 ** m)) by [1] - = LOG2 ((HALF n) DIV 2 ** m) by DIV_DIV_DIV_MULT - = LOG2 (HALF n) - m by induction hypothesis, 2 ** m < HALF n - = (LOG2 n - 1) - m by LOG2_HALF, 1 < n - = LOG2 n - (1 + m) by arithmetic - = LOG2 n - SUC m by ADD1 - Otherwise 2 ** m = HALF n, - LOG2 (n DIV 2 ** SUC m) - = LOG2 (n DIV (2 * 2 ** m)) by [1] - = LOG2 ((HALF n) DIV 2 ** m) by DIV_DIV_DIV_MULT - = LOG2 ((HALF n) DIV (HALF n)) by 2 ** m = HALF n - = LOG2 1 by DIVMOD_ID, 0 < HALF n - = 0 by LOG2_1 - LOG2 n - = 1 + LOG2 (HALF n) by LOG_DIV - = 1 + LOG2 (2 ** m) by 2 ** m = HALF n - = 1 + m by LOG2_2_EXP - = SUC m by SUC_ONE_ADD - Thus RHS = LOG2 n - SUC m = 0 = LHS. -*) - -Theorem LOG2_DIV_EXP: - !n m. 2 ** m < n ==> LOG2 (n DIV 2 ** m) = LOG2 n - m -Proof - Induct_on ‘m’ >- rw[] >> - rpt strip_tac >> - ‘1 < 2 ** SUC m’ by rw[ONE_LT_EXP] >> - ‘1 < n’ by decide_tac >> - fs[EXP] >> - ‘2 ** m <= HALF n’ - by metis_tac[DIV_LE_MONOTONE, HALF_TWICE, LESS_IMP_LESS_OR_EQ, - DECIDE “0 < 2”] >> - ‘LOG2 (n DIV (TWICE (2 ** m))) = LOG2 ((HALF n) DIV 2 ** m)’ - by rw[DIV_DIV_DIV_MULT] >> - fs[LESS_OR_EQ] >- rw[LOG2_HALF] >> - ‘LOG2 n = 1 + LOG2 (HALF n)’ by rw[LOG_DIV] >> - ‘_ = 1 + m’ by metis_tac[LOG2_2_EXP] >> - ‘_ = SUC m’ by rw[] >> - ‘0 < HALF n’ suffices_by rw[] >> - metis_tac[DECIDE “0 < 2”, ZERO_LT_EXP] -QED - -(* ------------------------------------------------------------------------- *) -(* LOG2 Computation *) -(* ------------------------------------------------------------------------- *) - -(* Define halves n = count of HALFs of n to 0, recursively. *) -val halves_def = Define` - halves n = if n = 0 then 0 else SUC (halves (HALF n)) -`; - -(* Theorem: halves n = if n = 0 then 0 else 1 + (halves (HALF n)) *) -(* Proof: by halves_def, ADD1 *) -val halves_alt = store_thm( - "halves_alt", - ``!n. halves n = if n = 0 then 0 else 1 + (halves (HALF n))``, - rw[Once halves_def, ADD1]); - -(* Extract theorems from definition *) -val halves_0 = save_thm("halves_0[simp]", halves_def |> SPEC ``0`` |> SIMP_RULE arith_ss[]); -(* val halves_0 = |- halves 0 = 0: thm *) -val halves_1 = save_thm("halves_1[simp]", halves_def |> SPEC ``1`` |> SIMP_RULE arith_ss[]); -(* val halves_1 = |- halves 1 = 1: thm *) -val halves_2 = save_thm("halves_2[simp]", halves_def |> SPEC ``2`` |> SIMP_RULE arith_ss[halves_1]); -(* val halves_2 = |- halves 2 = 2: thm *) - -(* Theorem: 0 < n ==> 0 < halves n *) -(* Proof: by halves_def *) -val halves_pos = store_thm( - "halves_pos[simp]", - ``!n. 0 < n ==> 0 < halves n``, - rw[Once halves_def]); - -(* Theorem: 0 < n ==> (halves n = 1 + LOG2 n) *) -(* Proof: - By complete induction on n. - Assume: !m. m < n ==> 0 < m ==> (halves m = 1 + LOG2 m) - To show: 0 < n ==> (halves n = 1 + LOG2 n) - Note HALF n < n by HALF_LT, 0 < n - Need 0 < HALF n to apply induction hypothesis. - If HALF n = 0, - Then n = 1 by HALF_EQ_0 - halves 1 - = SUC (halves 0) by halves_def - = 1 by halves_def - = 1 + LOG2 1 by LOG2_1 - If HALF n <> 0, - Then n <> 1 by HALF_EQ_0 - so 1 < n by n <> 0, n <> 1. - halves n - = SUC (halves (HALF n)) by halves_def - = SUC (1 + LOG2 (HALF n)) by induction hypothesis - = SUC (LOG2 n) by LOG2_BY_HALF - = 1 + LOG2 n by ADD1 -*) -val halves_by_LOG2 = store_thm( - "halves_by_LOG2", - ``!n. 0 < n ==> (halves n = 1 + LOG2 n)``, - completeInduct_on `n` >> - strip_tac >> - rw[Once halves_def] >> - Cases_on `n = 1` >- - simp[Once halves_def] >> - `HALF n < n` by rw[HALF_LT] >> - `HALF n <> 0` by fs[HALF_EQ_0] >> - simp[LOG2_BY_HALF]); - -(* Theorem: LOG2 n = if n = 0 then LOG2 0 else (halves n - 1) *) -(* Proof: - If 0 < n, - Note 0 < halves n by halves_pos - and halves n = 1 + LOG2 n by halves_by_LOG2 - or LOG2 n = halves - 1. - If n = 0, make it an infinite loop. -*) -val LOG2_compute = store_thm( - "LOG2_compute[compute]", - ``!n. LOG2 n = if n = 0 then LOG2 0 else (halves n - 1)``, - rpt strip_tac >> - (Cases_on `n = 0` >> simp[]) >> - `0 < halves n` by rw[] >> - `halves n = 1 + LOG2 n` by rw[halves_by_LOG2] >> - decide_tac); - -(* Put this to computeLib *) -(* val _ = computeLib.add_persistent_funs ["LOG2_compute"]; *) - -(* -EVAL ``LOG2 16``; --> 4 -EVAL ``LOG2 17``; --> 4 -EVAL ``LOG2 32``; --> 5 -EVAL ``LOG2 1024``; --> 10 -EVAL ``LOG2 1023``; --> 9 -*) - -(* Michael's method *) -(* -Define `count_divs n = if 2 <= n then 1 + count_divs (n DIV 2) else 0`; - -g `0 < n ==> (LOG2 n = count_divs n)`; -e (completeInduct_on `n`); -e strip_tac; -e (ONCE_REWRITE_TAC [theorm "count_divs_def"]); -e (Cases_on `2 <= n`); -e (mp_tac (Q.SPECL [`2`, `n`] LOG_DIV)); -e (simp[]); -(* prove on-the-fly *) -e (`0 < n DIV 2` suffices_by simp[]); -(* DB.match [] ``x < k DIV n``; *) -e (simp[arithmeticTheory.X_LT_DIV]); -e (`n = 1` by simp[]); -LOG_1; -e (simp[it]); -val foo = top_thm(); - -g `!n. LOG2 n = if 0 < n then count_divs n else LOG2 n`; - -e (rw[]); -e (simp[foo]); -e (lfs[]); ??? - -val bar = top_thm(); -var bar = save_thm("bar", bar); -computeLib.add_persistent_funs ["bar"]; -EVAL ``LOG2 16``; -EVAL ``LOG2 17``; -EVAL ``LOG2 32``; -EVAL ``LOG2 1024``; -EVAL ``LOG2 1023``; -EVAL ``LOG2 0``; -- loops! - -So for n = 97, -EVAL ``LOG2 97``; --> 6 -EVAL ``4 * LOG2 97 * LOG2 97``; --> 4 * 6 * 6 = 4 * 36 = 144 - -Need ord_r (97) > 144, r < 97, not possible ??? - -val count_divs_def = Define `count_divs n = if 1 < n then 1 + count_divs (n DIV 2) else 0`; - -val LOG2_by_count_divs = store_thm( - "LOG2_by_count_divs", - ``!n. 0 < n ==> (LOG2 n = count_divs n)``, - completeInduct_on `n` >> - strip_tac >> - ONCE_REWRITE_TAC[count_divs_def] >> - rw[] >| [ - mp_tac (Q.SPECL [`2`, `n`] LOG_DIV) >> - `2 <= n` by decide_tac >> - `0 < n DIV 2` by rw[X_LT_DIV] >> - simp[], - `n = 1` by decide_tac >> - simp[LOG_1] - ]); - -val LOG2_compute = store_thm( - "LOG2_compute[compute]", - ``!n. LOG2 n = if 0 < n then count_divs n else LOG2 n``, - rw_tac std_ss[LOG2_by_count_divs]); - -*) - -(* Theorem: m <= n ==> halves m <= halves n *) -(* Proof: - If m = 0, - Then halves m = 0 by halves_0 - Thus halves m <= halves n by 0 <= halves n - If m <> 0, - Then 0 < m and 0 < n by m <= n - so halves m = 1 + LOG2 m by halves_by_LOG2 - and halves n = 1 + LOG2 n by halves_by_LOG2 - and LOG2 m <= LOG2 n by LOG2_LE - ==> halves m <= halves n by arithmetic -*) -val halves_le = store_thm( - "halves_le", - ``!m n. m <= n ==> halves m <= halves n``, - rpt strip_tac >> - Cases_on `m = 0` >- - rw[] >> - `0 < m /\ 0 < n` by decide_tac >> - `LOG2 m <= LOG2 n` by rw[LOG2_LE] >> - rw[halves_by_LOG2]); - -(* Theorem: (halves n = 0) <=> (n = 0) *) -(* Proof: by halves_pos, halves_0 *) -val halves_eq_0 = store_thm( - "halves_eq_0", - ``!n. (halves n = 0) <=> (n = 0)``, - metis_tac[halves_pos, halves_0, NOT_ZERO_LT_ZERO]); - -(* Theorem: (halves n = 1) <=> (n = 1) *) -(* Proof: - If part: halves n = 1 ==> n = 1 - By contradiction, assume n <> 1. - Note n <> 0 by halves_eq_0 - so 2 <= n by n <> 0, n <> 1 - or halves 2 <= halves n by halves_le - But halves 2 = 2 by halves_2 - This gives 2 <= 1, a contradiction. - Only-if part: halves 1 = 1, true by halves_1 -*) -val halves_eq_1 = store_thm( - "halves_eq_1", - ``!n. (halves n = 1) <=> (n = 1)``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `n <> 0` by metis_tac[halves_eq_0, DECIDE``1 <> 0``] >> - `2 <= n` by decide_tac >> - `halves 2 <= halves n` by rw[halves_le] >> - fs[]); - -(* ------------------------------------------------------------------------- *) -(* Perfect Power *) -(* ------------------------------------------------------------------------- *) - -(* Define a PerfectPower number *) -val perfect_power_def = Define` - perfect_power (n:num) (m:num) <=> ?e. (n = m ** e) -`; - -(* Overload perfect_power *) -val _ = overload_on("power_of", ``perfect_power``); -val _ = set_fixity "power_of" (Infix(NONASSOC, 450)); (* same as relation *) -(* from pretty-printing, a good idea. *) - -(* Theorem: perfect_power n n *) -(* Proof: - True since n = n ** 1 by EXP_1 -*) -val perfect_power_self = store_thm( - "perfect_power_self", - ``!n. perfect_power n n``, - metis_tac[perfect_power_def, EXP_1]); - -(* Theorem: perfect_power 0 m <=> (m = 0) *) -(* Proof: by perfect_power_def, EXP_EQ_0 *) -val perfect_power_0_m = store_thm( - "perfect_power_0_m", - ``!m. perfect_power 0 m <=> (m = 0)``, - rw[perfect_power_def, EQ_IMP_THM]); - -(* Theorem: perfect_power 1 m *) -(* Proof: by perfect_power_def, take e = 0 *) -val perfect_power_1_m = store_thm( - "perfect_power_1_m", - ``!m. perfect_power 1 m``, - rw[perfect_power_def] >> - metis_tac[]); - -(* Theorem: perfect_power n 0 <=> ((n = 0) \/ (n = 1)) *) -(* Proof: by perfect_power_def, ZERO_EXP. *) -val perfect_power_n_0 = store_thm( - "perfect_power_n_0", - ``!n. perfect_power n 0 <=> ((n = 0) \/ (n = 1))``, - rw[perfect_power_def] >> - metis_tac[ZERO_EXP]); - -(* Theorem: perfect_power n 1 <=> (n = 1) *) -(* Proof: by perfect_power_def, EXP_1 *) -val perfect_power_n_1 = store_thm( - "perfect_power_n_1", - ``!n. perfect_power n 1 <=> (n = 1)``, - rw[perfect_power_def]); - -(* Theorem: 0 < m /\ 1 < n /\ (n MOD m = 0) ==> - (perfect_power n m) <=> (perfect_power (n DIV m) m) *) -(* Proof: - If part: perfect_power n m ==> perfect_power (n DIV m) m - Note ?e. n = m ** e by perfect_power_def - and e <> 0 by EXP_0, n <> 1 - so ?k. e = SUC k by num_CASES - or n = m ** SUC k - ==> n DIV m = m ** k by EXP_SUC_DIV - Thus perfect_power (n DIV m) m by perfect_power_def - Only-if part: perfect_power (n DIV m) m ==> perfect_power n m - Note ?e. n DIV m = m ** e by perfect_power_def - Now m divides n by DIVIDES_MOD_0, n MOD m = 0, 0 < m - ==> n = m * (n DIV m) by DIVIDES_EQN_COMM, 0 < m - = m * m ** e by above - = m ** (SUC e) by EXP - Thus perfect_power n m by perfect_power_def -*) -val perfect_power_mod_eq_0 = store_thm( - "perfect_power_mod_eq_0", - ``!n m. 0 < m /\ 1 < n /\ (n MOD m = 0) ==> - ((perfect_power n m) <=> (perfect_power (n DIV m) m))``, - rw[perfect_power_def] >> - rw[EQ_IMP_THM] >| [ - `m ** e <> 1` by decide_tac >> - `e <> 0` by metis_tac[EXP_0] >> - `?k. e = SUC k` by metis_tac[num_CASES] >> - qexists_tac `k` >> - rw[EXP_SUC_DIV], - `m divides n` by rw[DIVIDES_MOD_0] >> - `n = m * (n DIV m)` by rw[GSYM DIVIDES_EQN_COMM] >> - metis_tac[EXP] - ]); - -(* Theorem: 0 < m /\ 1 < n /\ (n MOD m <> 0) ==> ~(perfect_power n m) *) -(* Proof: - By contradiction, assume perfect_power n m. - Then ?e. n = m ** e by perfect_power_def - Now e <> 0 by EXP_0, n <> 1 - so ?k. e = SUC k by num_CASES - n = m ** SUC k - = m * (m ** k) by EXP - = (m ** k) * m by MULT_COMM - Thus m divides n by divides_def - ==> n MOD m = 0 by DIVIDES_MOD_0 - This contradicts n MOD m <> 0. -*) -val perfect_power_mod_ne_0 = store_thm( - "perfect_power_mod_ne_0", - ``!n m. 0 < m /\ 1 < n /\ (n MOD m <> 0) ==> ~(perfect_power n m)``, - rpt strip_tac >> - fs[perfect_power_def] >> - `n <> 1` by decide_tac >> - `e <> 0` by metis_tac[EXP_0] >> - `?k. e = SUC k` by metis_tac[num_CASES] >> - `n = m * m ** k` by fs[EXP] >> - `m divides n` by metis_tac[divides_def, MULT_COMM] >> - metis_tac[DIVIDES_MOD_0]); - -(* Theorem: perfect_power n m = - if n = 0 then (m = 0) - else if n = 1 then T - else if m = 0 then (n <= 1) - else if m = 1 then (n = 1) - else if n MOD m = 0 then perfect_power (n DIV m) m else F *) -(* Proof: - If n = 0, to show: - perfect_power 0 m <=> (m = 0), true by perfect_power_0_m - If n = 1, to show: - perfect_power 1 m = T, true by perfect_power_1_m - If m = 0, to show: - perfect_power n 0 <=> (n <= 1), true by perfect_power_n_0 - If m = 1, to show: - perfect_power n 1 <=> (n = 1), true by perfect_power_n_1 - Otherwise, - If n MOD m = 0, to show: - perfect_power (n DIV m) m <=> perfect_power n m, true - by perfect_power_mod_eq_0 - If n MOD m <> 0, to show: - ~perfect_power n m, true by perfect_power_mod_ne_0 -*) -val perfect_power_test = store_thm( - "perfect_power_test", - ``!n m. perfect_power n m = - if n = 0 then (m = 0) - else if n = 1 then T - else if m = 0 then (n <= 1) - else if m = 1 then (n = 1) - else if n MOD m = 0 then perfect_power (n DIV m) m else F``, - rpt strip_tac >> - (Cases_on `n = 0` >> simp[perfect_power_0_m]) >> - (Cases_on `n = 1` >> simp[perfect_power_1_m]) >> - `1 < n` by decide_tac >> - (Cases_on `m = 0` >> simp[perfect_power_n_0]) >> - `0 < m` by decide_tac >> - (Cases_on `m = 1` >> simp[perfect_power_n_1]) >> - (Cases_on `n MOD m = 0` >> simp[]) >- - rw[perfect_power_mod_eq_0] >> - rw[perfect_power_mod_ne_0]); - -(* Theorem: 1 < m /\ perfect_power n m /\ perfect_power (SUC n) m ==> (m = 2) /\ (n = 1) *) -(* Proof: - Note ?x. n = m ** x by perfect_power_def - and ?y. SUC n = m ** y by perfect_power_def - Since n < SUC n by LESS_SUC - ==> x < y by EXP_BASE_LT_MONO - Let d = y - x. - Then 0 < d /\ (y = x + d). - Let v = m ** d - Note 1 < v by ONE_LT_EXP, 1 < m - and m ** y = n * v by EXP_ADD - Let z = v - 1. - Then 0 < z /\ (v = z + 1). - and SUC n = n * v - = n * (z + 1) - = n * z + n * 1 by LEFT_ADD_DISTRIB - = n * z + n - ==> n * z = 1 by ADD1 - ==> n = 1 /\ z = 1 by MULT_EQ_1 - so v = 2 by v = z + 1 - - To show: m = 2. - By contradiction, suppose m <> 2. - Then 2 < m by 1 < m, m <> 2 - ==> 2 ** y < m ** y by EXP_EXP_LT_MONO - = n * v = 2 = 2 ** 1 by EXP_1 - ==> y < 1 by EXP_BASE_LT_MONO - Thus y = 0, but y <> 0 by x < y, - leading to a contradiction. -*) - -Theorem perfect_power_suc: - !m n. 1 < m /\ perfect_power n m /\ perfect_power (SUC n) m ==> - m = 2 /\ n = 1 -Proof - ntac 3 strip_tac >> - `?x. n = m ** x` by fs[perfect_power_def] >> - `?y. SUC n = m ** y` by fs[GSYM perfect_power_def] >> - `n < SUC n` by decide_tac >> - `x < y` by metis_tac[EXP_BASE_LT_MONO] >> - qabbrev_tac `d = y - x` >> - `0 < d /\ (y = x + d)` by fs[Abbr`d`] >> - qabbrev_tac `v = m ** d` >> - `m ** y = n * v` by fs[EXP_ADD, Abbr`v`] >> - `1 < v` by rw[ONE_LT_EXP, Abbr`v`] >> - qabbrev_tac `z = v - 1` >> - `0 < z /\ (v = z + 1)` by fs[Abbr`z`] >> - `n * v = n * z + n * 1` by rw[] >> - `n * z = 1` by decide_tac >> - `n = 1 /\ z = 1` by metis_tac[MULT_EQ_1] >> - `v = 2` by decide_tac >> - simp[] >> - spose_not_then strip_assume_tac >> - `2 < m` by decide_tac >> - `2 ** y < m ** y` by simp[EXP_EXP_LT_MONO] >> - `m ** y = 2` by decide_tac >> - `2 ** y < 2 ** 1` by metis_tac[EXP_1] >> - `y < 1` by fs[EXP_BASE_LT_MONO] >> - decide_tac -QED - -(* Theorem: 1 < m /\ 1 < n /\ perfect_power n m ==> ~perfect_power (SUC n) m *) -(* Proof: - By contradiction, suppose perfect_power (SUC n) m. - Then n = 1 by perfect_power_suc - This contradicts 1 < n. -*) -val perfect_power_not_suc = store_thm( - "perfect_power_not_suc", - ``!m n. 1 < m /\ 1 < n /\ perfect_power n m ==> ~perfect_power (SUC n) m``, - spose_not_then strip_assume_tac >> - `n = 1` by metis_tac[perfect_power_suc] >> - decide_tac); - -(* Theorem: 1 < b /\ 0 < n ==> - (LOG b (SUC n) = LOG b n + if perfect_power (SUC n) b then 1 else 0) *) -(* Proof: - Let x = LOG b n, y = LOG b (SUC n). x <= y - Note SUC n <= b ** SUC x /\ b ** SUC x <= b * n by LOG_TEST - and SUC (SUC n) <= b ** SUC y /\ b ** SUC y <= b * SUC n by LOG_TEST, 0 < SUC n - - If SUC n = b ** SUC x, - Then perfect_power (SUC n) b by perfect_power_def - and y = LOG b (SUC n) - = LOG b (b ** SUC x) - = SUC x by LOG_EXACT_EXP - = x + 1 by ADD1 - hence true. - Otherwise, SUC n < b ** SUC x, - Then SUC (SUC n) <= b ** SUC x by SUC n < b ** SUC x - and b * n < b * SUC n by LT_MULT_LCANCEL, n < SUC n - Thus b ** SUC x <= b * n < b * SUC n - or y = x by LOG_TEST - Claim: ~perfect_power (SUC n) b - Proof: By contradiction, suppose perfect_power (SUC n) b. - Then ?e. SUC n = b ** e. - Thus y = LOG b (SUC n) - = LOG b (b ** e) by LOG_EXACT_EXP - = e - ==> b * n < b * SUC n - = b * b ** e by SUC n = b ** e - = b ** SUC e by EXP - = b ** SUC x by e = y = x - This contradicts b ** SUC x <= b * n - With ~perfect_power (SUC n) b, hence true. -*) - -Theorem LOG_SUC: - !b n. 1 < b /\ 0 < n ==> - (LOG b (SUC n) = LOG b n + if perfect_power (SUC n) b then 1 else 0) -Proof - rpt strip_tac >> - qabbrev_tac ‘x = LOG b n’ >> - qabbrev_tac ‘y = LOG b (SUC n)’ >> - ‘0 < SUC n’ by decide_tac >> - ‘SUC n <= b ** SUC x /\ b ** SUC x <= b * n’ by metis_tac[LOG_TEST] >> - ‘SUC (SUC n) <= b ** SUC y /\ b ** SUC y <= b * SUC n’ - by metis_tac[LOG_TEST] >> - ‘(SUC n = b ** SUC x) \/ (SUC n < b ** SUC x)’ by decide_tac >| [ - ‘perfect_power (SUC n) b’ by metis_tac[perfect_power_def] >> - ‘y = SUC x’ by rw[LOG_EXACT_EXP, Abbr‘y’] >> - simp[], - ‘SUC (SUC n) <= b ** SUC x’ by decide_tac >> - ‘b * n < b * SUC n’ by rw[] >> - ‘b ** SUC x <= b * SUC n’ by decide_tac >> - ‘y = x’ by metis_tac[LOG_TEST] >> - ‘~perfect_power (SUC n) b’ - by (spose_not_then strip_assume_tac >> - `?e. SUC n = b ** e` by fs[perfect_power_def] >> - `y = e` by (simp[Abbr`y`] >> fs[] >> rfs[LOG_EXACT_EXP]) >> - `b * n < b ** SUC x` by metis_tac[EXP] >> - decide_tac) >> - simp[] - ] -QED - -(* -LOG_SUC; -|- !b n. 1 < b /\ 0 < n ==> LOG b (SUC n) = LOG b n + if perfect_power (SUC n) b then 1 else 0 -Let v = LOG b n. - - v v+1. v+2. v+3. - ----------------------------------------------- - b b ** 2 b ** 3 b ** 4 - -> EVAL ``MAP (LOG 2) [1 .. 20]``; -val it = |- MAP (LOG 2) [1 .. 20] = - [0; 1; 1; 2; 2; 2; 2; 3; 3; 3; 3; 3; 3; 3; 3; 4; 4; 4; 4; 4]: thm - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -*) - -(* Theorem: 0 < n ==> !m. perfect_power n m <=> ?k. k <= LOG2 n /\ (n = m ** k) *) -(* Proof: - If part: perfect_power n m ==> ?k. k <= LOG2 n /\ (n = m ** k) - Given perfect_power n m, ?e. (n = m ** e) by perfect_power_def - If n = 1, - Then LOG2 1 = 0 by LOG2_1 - Take k = 0, then 1 = m ** 0 by EXP_0 - If n <> 1, so e <> 0 by EXP - and m <> 1 by EXP_1 - also n <> 0, so m <> 0 by ZERO_EXP - Therefore 2 <= m - ==> 2 ** e <= m ** e by EXP_BASE_LE_MONO, 1 < 2 - But n < 2 ** (SUC (LOG2 n)) by LOG2_PROPERTY, 0 < n - or 2 ** e < 2 ** (SUC (LOG2 n)) - hence e < SUC (LOG2 n) by EXP_BASE_LT_MONO, 1 < 2 - i.e. e <= LOG2 n - Only-if part: ?k. k <= LOG2 n /\ (n = m ** k) ==> perfect_power n m - True by perfect_power_def. -*) -val perfect_power_bound_LOG2 = store_thm( - "perfect_power_bound_LOG2", - ``!n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= LOG2 n /\ (n = m ** k)``, - rw[EQ_IMP_THM] >| [ - Cases_on `n = 1` >- - simp[] >> - `?e. (n = m ** e)` by rw[GSYM perfect_power_def] >> - `n <> 0 /\ 1 < n /\ 1 < 2` by decide_tac >> - `e <> 0` by metis_tac[EXP] >> - `m <> 1` by metis_tac[EXP_1] >> - `m <> 0` by metis_tac[ZERO_EXP] >> - `2 <= m` by decide_tac >> - `2 ** e <= n` by rw[EXP_BASE_LE_MONO] >> - `n < 2 ** (SUC (LOG2 n))` by rw[LOG2_PROPERTY] >> - `e < SUC (LOG2 n)` by metis_tac[EXP_BASE_LT_MONO, LESS_EQ_LESS_TRANS] >> - `e <= LOG2 n` by decide_tac >> - metis_tac[], - metis_tac[perfect_power_def] - ]); - -(* Theorem: prime p /\ (?x y. 0 < x /\ (p ** x = q ** y)) ==> perfect_power q p *) -(* Proof: - Note ?k. (q = p ** k) by power_eq_prime_power, prime p, 0 < x - Thus perfect_power q p by perfect_power_def -*) -val perfect_power_condition = store_thm( - "perfect_power_condition", - ``!p q. prime p /\ (?x y. 0 < x /\ (p ** x = q ** y)) ==> perfect_power q p``, - metis_tac[power_eq_prime_power, perfect_power_def]); - -(* Theorem: 0 < p /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) *) -(* Proof: - Let q = n DIV p. - Then n = p * q by DIVIDES_EQN_COMM, 0 < p - If part: perfect_power n p ==> perfect_power q p - Note ?k. n = p ** k by perfect_power_def - If k = 0, - Then p * q = p ** 0 = 1 by EXP - ==> p = 1 and q = 1 by MULT_EQ_1 - so perfect_power q p by perfect_power_self - If k <> 0, k = SUC h for some h. - Then p * q = p ** SUC h - = p * p ** h by EXP - or q = p ** h by MULT_LEFT_CANCEL, p <> 0 - so perfect_power q p by perfect_power_self - - Only-if part: perfect_power q p ==> perfect_power n p - Note ?k. q = p ** k by perfect_power_def - so n = p * q = p ** SUC k by EXP - thus perfect_power n p by perfect_power_def -*) -val perfect_power_cofactor = store_thm( - "perfect_power_cofactor", - ``!n p. 0 < p /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p)``, - rpt strip_tac >> - qabbrev_tac `q = n DIV p` >> - `n = p * q` by rw[GSYM DIVIDES_EQN_COMM, Abbr`q`] >> - simp[EQ_IMP_THM] >> - rpt strip_tac >| [ - `?k. p * q = p ** k` by rw[GSYM perfect_power_def] >> - Cases_on `k` >| [ - `(p = 1) /\ (q = 1)` by metis_tac[MULT_EQ_1, EXP] >> - metis_tac[perfect_power_self], - `q = p ** n'` by metis_tac[EXP, MULT_LEFT_CANCEL, NOT_ZERO_LT_ZERO] >> - metis_tac[perfect_power_def] - ], - `?k. q = p ** k` by rw[GSYM perfect_power_def] >> - `p * q = p ** SUC k` by rw[EXP] >> - metis_tac[perfect_power_def] - ]); - -(* Theorem: 0 < n /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) *) -(* Proof: - Note 0 < p by ZERO_DIVIDES, 0 < n - The result follows by perfect_power_cofactor -*) -val perfect_power_cofactor_alt = store_thm( - "perfect_power_cofactor_alt", - ``!n p. 0 < n /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p)``, - rpt strip_tac >> - `0 < p` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> - qabbrev_tac `q = n DIV p` >> - rw[perfect_power_cofactor]); - -(* Theorem: perfect_power n 2 ==> (ODD n <=> (n = 1)) *) -(* Proof: - If part: perfect_power n 2 /\ ODD n ==> n = 1 - By contradiction, suppose n <> 1. - Note ?k. n = 2 ** k by perfect_power_def - Thus k <> 0 by EXP - so ?h. k = SUC h by num_CASES - n = 2 ** (SUC h) by above - = 2 * 2 ** h by EXP - ==> EVEN n by EVEN_DOUBLE - This contradicts ODD n by EVEN_ODD - Only-if part: perfect_power n 2 /\ n = 1 ==> ODD n - This is true by ODD_1 -*) -val perfect_power_2_odd = store_thm( - "perfect_power_2_odd", - ``!n. perfect_power n 2 ==> (ODD n <=> (n = 1))``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `?k. n = 2 ** k` by rw[GSYM perfect_power_def] >> - `k <> 0` by metis_tac[EXP] >> - `?h. k = SUC h` by metis_tac[num_CASES] >> - `n = 2 * 2 ** h` by rw[EXP] >> - metis_tac[EVEN_DOUBLE, EVEN_ODD]); - -(* ------------------------------------------------------------------------- *) -(* Power Free *) -(* ------------------------------------------------------------------------- *) - -(* Define a PowerFree number: a trivial perfect power *) -val power_free_def = zDefine` - power_free (n:num) <=> !m e. (n = m ** e) ==> (m = n) /\ (e = 1) -`; -(* Use zDefine as this is not computationally effective. *) - -(* Theorem: power_free 0 = F *) -(* Proof: - Note 0 ** 2 = 0 by ZERO_EXP - Thus power_free 0 = F by power_free_def -*) -val power_free_0 = store_thm( - "power_free_0", - ``power_free 0 = F``, - rw[power_free_def]); - -(* Theorem: power_free 1 = F *) -(* Proof: - Note 0 ** 0 = 1 by ZERO_EXP - Thus power_free 1 = F by power_free_def -*) -val power_free_1 = store_thm( - "power_free_1", - ``power_free 1 = F``, - rw[power_free_def]); - -(* Theorem: power_free n ==> 1 < n *) -(* Proof: - By contradiction, suppose n = 0 or n = 1. - Then power_free 0 = F by power_free_0 - and power_free 1 = F by power_free_1 -*) -val power_free_gt_1 = store_thm( - "power_free_gt_1", - ``!n. power_free n ==> 1 < n``, - metis_tac[power_free_0, power_free_1, DECIDE``1 < n <=> (n <> 0 /\ n <> 1)``]); - -(* Theorem: power_free n <=> 1 < n /\ (!m. perfect_power n m ==> (n = m)) *) -(* Proof: - If part: power_free n ==> 1 < n /\ (!m. perfect_power n m ==> (n = m)) - Note power_free n - ==> 1 < n by power_free_gt_1 - Now ?e. n = m ** e by perfect_power_def - ==> n = m by power_free_def - - Only-if part: 1 < n /\ (!m. perfect_power n m ==> (n = m)) ==> power_free n - By power_free_def, this is to show: - (n = m ** e) ==> (m = n) /\ (e = 1) - Note perfect_power n m by perfect_power_def, ?e. - ==> m = n by implication - so n = n ** e by given, m = n - ==> e = 1 by POWER_EQ_SELF -*) -Theorem power_free_alt: - power_free n <=> 1 < n /\ !m. perfect_power n m ==> n = m -Proof - rw[EQ_IMP_THM] - >- rw[power_free_gt_1] - >- fs[power_free_def, perfect_power_def] >> - fs[power_free_def, perfect_power_def, PULL_EXISTS] >> - rpt strip_tac >> - first_x_assum $ drule_then strip_assume_tac >> gs[] -QED - -(* Theorem: prime n ==> power_free n *) -(* Proof: - Let n = m ** e. To show that n is power_free, - (1) show m = n, by squeezing m as a factor of prime n. - (2) show e = 1, by applying prime_powers_eq - This is a typical detective-style proof. - - Note prime n ==> n <> 1 by NOT_PRIME_1 - - Claim: !m e. n = m ** e ==> m = n - Proof: Note m <> 1 by EXP_1, n <> 1 - and e <> 0 by EXP, n <> 1 - Thus e = SUC k for some k by num_CASES - n = m ** SUC k - = m * (m ** k) by EXP - = (m ** k) * m by MULT_COMM - Thus m divides n, by divides_def - But m <> 1, so m = n by prime_def - - The claim satisfies half of the power_free_def. - With m = n, prime m, - and e <> 0 by EXP, n <> 1 - Thus n = n ** 1 = m ** e by EXP_1 - ==> e = 1 by prime_powers_eq, 0 < e. -*) -val prime_is_power_free = store_thm( - "prime_is_power_free", - ``!n. prime n ==> power_free n``, - rpt strip_tac >> - `n <> 1` by metis_tac[NOT_PRIME_1] >> - `!m e. (n = m ** e) ==> (m = n)` by - (rpt strip_tac >> - `m <> 1` by metis_tac[EXP_1] >> - metis_tac[EXP, num_CASES, MULT_COMM, divides_def, prime_def]) >> - `!m e. (n = m ** e) ==> (e = 1)` by metis_tac[EXP, EXP_1, prime_powers_eq, NOT_ZERO_LT_ZERO] >> - metis_tac[power_free_def]); - -(* Theorem: power_free n /\ perfect_power n m ==> (n = m) *) -(* Proof: - Note ?e. n = m ** e by perfect_power_def - ==> n = m by power_free_def -*) -val power_free_perfect_power = store_thm( - "power_free_perfect_power", - ``!m n. power_free n /\ perfect_power n m ==> (n = m)``, - metis_tac[perfect_power_def, power_free_def]); - -(* Theorem: power_free n ==> (!j. 1 < j ==> (ROOT j n) ** j <> n) *) -(* Proof: - By contradiction, suppose (ROOT j n) ** j = n. - Then j = 1 by power_free_def - This contradicts 1 < j. -*) -val power_free_property = store_thm( - "power_free_property", - ``!n. power_free n ==> (!j. 1 < j ==> (ROOT j n) ** j <> n)``, - spose_not_then strip_assume_tac >> - `j = 1` by metis_tac[power_free_def] >> - decide_tac); - -(* We have: -power_free_0 |- power_free 0 <=> F -power_free_1 |- power_free 1 <=> F -So, given 1 < n, how to check power_free n ? -*) - -(* Theorem: power_free n <=> 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n) *) -(* Proof: - If part: power_free n ==> 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n) - Note 1 < n by power_free_gt_1 - The rest is true by power_free_property. - Only-if part: 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n) ==> power_free n - By contradiction, assume ~(power_free n). - That is, ?m e. n = m ** e /\ (m = m ** e ==> e <> 1) by power_free_def - Note 1 < m /\ 0 < e by ONE_LT_EXP, 1 < n - Thus ROOT e n = m by ROOT_POWER, 1 < m, 0 < e - By the implication, ~(1 < e), or e <= 1. - Since 0 < e, this shows e = 1. - Then m = m ** e by EXP_1 - This gives e <> 1, a contradiction. -*) -val power_free_check_all = store_thm( - "power_free_check_all", - ``!n. power_free n <=> 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n)``, - rw[EQ_IMP_THM] >- - rw[power_free_gt_1] >- - rw[power_free_property] >> - simp[power_free_def] >> - spose_not_then strip_assume_tac >> - `1 < m /\ 0 < e` by metis_tac[ONE_LT_EXP] >> - `ROOT e n = m` by rw[ROOT_POWER] >> - `~(1 < e)` by metis_tac[] >> - `e = 1` by decide_tac >> - rw[]); - -(* However, there is no need to check all the exponents: - just up to (LOG2 n) or (ulog n) is sufficient. - See earlier part with power_free_upto_def. *) - -(* ------------------------------------------------------------------------- *) -(* Upper Logarithm *) -(* ------------------------------------------------------------------------- *) - -(* Find the power of 2 more or equal to n *) -Definition count_up_def: - count_up n m k = - if m = 0 then 0 (* just to provide m <> 0 for the next one *) - else if n <= m then k else count_up n (2 * m) (SUC k) -Termination WF_REL_TAC `measure (λ(n, m, k). n - m)` -End - -(* Define upper LOG2 n by count_up *) -val ulog_def = Define` - ulog n = count_up n 1 0 -`; - -(* -> EVAL ``ulog 1``; --> 0 -> EVAL ``ulog 2``; --> 1 -> EVAL ``ulog 3``; --> 2 -> EVAL ``ulog 4``; --> 2 -> EVAL ``ulog 5``; --> 3 -> EVAL ``ulog 6``; --> 3 -> EVAL ``ulog 7``; --> 3 -> EVAL ``ulog 8``; --> 3 -> EVAL ``ulog 9``; --> 4 -*) - -(* Theorem: ulog 0 = 0 *) -(* Proof: - ulog 0 - = count_up 0 1 0 by ulog_def - = 0 by count_up_def, 0 <= 1 -*) -val ulog_0 = store_thm( - "ulog_0[simp]", - ``ulog 0 = 0``, - rw[ulog_def, Once count_up_def]); - -(* Theorem: ulog 1 = 0 *) -(* Proof: - ulog 1 - = count_up 1 1 0 by ulog_def - = 0 by count_up_def, 1 <= 1 -*) -val ulog_1 = store_thm( - "ulog_1[simp]", - ``ulog 1 = 0``, - rw[ulog_def, Once count_up_def]); - -(* Theorem: ulog 2 = 1 *) -(* Proof: - ulog 2 - = count_up 2 1 0 by ulog_def - = count_up 2 2 1 by count_up_def, ~(1 < 2) - = 1 by count_up_def, 2 <= 2 -*) -val ulog_2 = store_thm( - "ulog_2[simp]", - ``ulog 2 = 1``, - rw[ulog_def, Once count_up_def] >> - rw[Once count_up_def]); - -(* Theorem: m <> 0 /\ n <= m ==> !k. count_up n m k = k *) -(* Proof: by count_up_def *) -val count_up_exit = store_thm( - "count_up_exit", - ``!m n. m <> 0 /\ n <= m ==> !k. count_up n m k = k``, - rw[Once count_up_def]); - -(* Theorem: m <> 0 /\ m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k) *) -(* Proof: by count_up_def *) -val count_up_suc = store_thm( - "count_up_suc", - ``!m n. m <> 0 /\ m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k)``, - rw[Once count_up_def]); - -(* Theorem: m <> 0 ==> - !t. 2 ** t * m < n ==> !k. count_up n m k = count_up n (2 ** (SUC t) * m) ((SUC k) + t) *) -(* Proof: - By induction on t. - Base: 2 ** 0 * m < n ==> !k. count_up n m k = count_up n (2 ** SUC 0 * m) (SUC k + 0) - Simplifying, this is to show: - m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k) - which is true by count_up_suc. - Step: 2 ** t * m < n ==> !k. count_up n m k = count_up n (2 ** SUC t * m) (SUC k + t) ==> - 2 ** SUC t * m < n ==> !k. count_up n m k = count_up n (2 ** SUC (SUC t) * m) (SUC k + SUC t) - Note 2 ** SUC t <> 0 by EXP_EQ_0, 2 <> 0 - so 2 ** SUC t * m <> 0 by MULT_EQ_0, m <> 0 - and 2 ** SUC t * m - = 2 * 2 ** t * m by EXP - = 2 * (2 ** t * m) by MULT_ASSOC - Thus (2 ** t * m) < n by MULT_LT_IMP_LT, 0 < 2 - count_up n m k - = count_up n (2 ** SUC t * m) (SUC k + t) by induction hypothesis - = count_up n (2 * (2 ** SUC t * m)) (SUC (SUC k + t)) by count_up_suc - = count_up n (2 ** SUC (SUC t) * m) (SUC k + SUC t) by EXP, ADD1 -*) -val count_up_suc_eqn = store_thm( - "count_up_suc_eqn", - ``!m. m <> 0 ==> - !n t. 2 ** t * m < n ==> !k. count_up n m k = count_up n (2 ** (SUC t) * m) ((SUC k) + t)``, - ntac 3 strip_tac >> - Induct >- - rw[count_up_suc] >> - rpt strip_tac >> - qabbrev_tac `q = 2 ** t * m` >> - `2 ** SUC t <> 0` by metis_tac[EXP_EQ_0, DECIDE``2 <> 0``] >> - `2 ** SUC t * m <> 0` by metis_tac[MULT_EQ_0] >> - `2 ** SUC t * m = 2 * q` by rw_tac std_ss[EXP, MULT_ASSOC, Abbr`q`] >> - `q < n` by rw[MULT_LT_IMP_LT] >> - rw[count_up_suc, EXP, ADD1]); - -(* Theorem: m <> 0 ==> !n t. 2 ** t * m < 2 * n /\ n <= 2 ** t * m ==> !k. count_up n m k = k + t *) -(* Proof: - If t = 0, - Then n <= m by EXP - so count_up n m k - = k by count_up_exit - = k + 0 by ADD_0 - If t <> 0, - Then ?s. t = SUC s by num_CASES - Note 2 ** t * m - = 2 ** SUC s * m by above - = 2 * 2 ** s * m by EXP - = 2 * (2 ** s * m) by MULT_ASSOC - Note 2 ** SUC s * m < 2 * n by given - so (2 ** s * m) < n by LT_MULT_RCANCEL, 2 <> 0 - - count_up n m k - = count_up n (2 ** t * m) ((SUC k) + t) by count_up_suc_eqn - = (SUC k) + t by count_up_exit -*) -val count_up_exit_eqn = store_thm( - "count_up_exit_eqn", - ``!m. m <> 0 ==> !n t. 2 ** t * m < 2 * n /\ n <= 2 ** t * m ==> !k. count_up n m k = k + t``, - rpt strip_tac >> - Cases_on `t` >- - fs[count_up_exit] >> - qabbrev_tac `q = 2 ** n' * m` >> - `2 ** SUC n' * m = 2 * q` by rw_tac std_ss[EXP, MULT_ASSOC, Abbr`q`] >> - `q < n` by decide_tac >> - `count_up n m k = count_up n (2 ** (SUC n') * m) ((SUC k) + n')` by rw[count_up_suc_eqn, Abbr`q`] >> - `_ = (SUC k) + n'` by rw[count_up_exit] >> - rw[]); - -(* Theorem: 2 ** m < 2 * n /\ n <= 2 ** m ==> (ulog n = m) *) -(* Proof: - Put m = 1 in count_up_exit_eqn: - 2 ** t * 1 < 2 * n /\ n <= 2 ** t * 1 ==> !k. count_up n 1 k = k + t - Put k = 0, and apply MULT_RIGHT_1, ADD: - 2 ** t * 1 < 2 * n /\ n <= 2 ** t * 1 ==> count_up n 1 0 = t - Then apply ulog_def to get the result, and rename t by m. -*) -val ulog_unique = store_thm( - "ulog_unique", - ``!m n. 2 ** m < 2 * n /\ n <= 2 ** m ==> (ulog n = m)``, - metis_tac[ulog_def, count_up_exit_eqn, MULT_RIGHT_1, ADD, DECIDE``1 <> 0``]); - -(* Theorem: ulog n = if 1 < n then SUC (LOG2 (n - 1)) else 0 *) -(* Proof: - If 1 < n, - Then 0 < n - 1 by 1 < n - ==> 2 ** LOG2 (n - 1) <= (n - 1) /\ - (n - 1) < 2 ** SUC (LOG2 (n - 1)) by LOG2_PROPERTY - or 2 ** LOG2 (n - 1) < n /\ - n <= 2 ** SUC (LOG2 (n - 1)) by shifting inequalities - Let t = SUC (LOG2 (n - 1)). - Then 2 ** t = 2 * 2 ** (LOG2 (n - 1)) by EXP - < 2 * n by LT_MULT_LCANCEL, 2 ** LOG2 (n - 1) < n - Thus ulog n = t by ulog_unique. - If ~(1 < n), - Then n <= 1, or n = 0 or n = 1. - If n = 0, ulog n = 0 by ulog_0 - If n = 1, ulog n = 0 by ulog_1 -*) -val ulog_eqn = store_thm( - "ulog_eqn", - ``!n. ulog n = if 1 < n then SUC (LOG2 (n - 1)) else 0``, - rw[] >| [ - `0 < n - 1` by decide_tac >> - `2 ** LOG2 (n - 1) <= (n - 1) /\ (n - 1) < 2 ** SUC (LOG2 (n - 1))` by metis_tac[LOG2_PROPERTY] >> - `2 * 2 ** LOG2 (n - 1) < 2 * n /\ n <= 2 ** SUC (LOG2 (n - 1))` by decide_tac >> - rw[EXP, ulog_unique], - metis_tac[ulog_0, ulog_1, DECIDE``~(1 < n) <=> (n = 0) \/ (n = 1)``] - ]); - -(* Theorem: 0 < n ==> (ulog (SUC n) = SUC (LOG2 n)) *) -(* Proof: - Note 0 < n ==> 1 < SUC n by LT_ADD_RCANCEL, ADD1 - Thus ulog (SUC n) - = SUC (LOG2 (SUC n - 1)) by ulog_eqn - = SUC (LOG2 n) by SUC_SUB1 -*) -val ulog_suc = store_thm( - "ulog_suc", - ``!n. 0 < n ==> (ulog (SUC n) = SUC (LOG2 n))``, - rpt strip_tac >> - `1 < SUC n` by decide_tac >> - rw[ulog_eqn]); - -(* Theorem: 0 < n ==> 2 ** (ulog n) < 2 * n /\ n <= 2 ** (ulog n) *) -(* Proof: - Apply ulog_eqn, this is to show: - (1) 1 < n ==> 2 ** SUC (LOG2 (n - 1)) < 2 * n - Let m = n - 1. - Note 0 < m by 1 < n - ==> 2 ** LOG2 m <= m by TWO_EXP_LOG2_LE, 0 < m - or <= n - 1 by notation - Thus 2 ** LOG2 m < n by inequality [1] - and 2 ** SUC (LOG2 m) - = 2 * 2 ** (LOG2 m) by EXP - < 2 * n by LT_MULT_LCANCEL, [1] - (2) 1 < n ==> n <= 2 ** SUC (LOG2 (n - 1)) - Let m = n - 1. - Note 0 < m by 1 < n - ==> m < 2 ** SUC (LOG2 m) by LOG2_PROPERTY, 0 < m - n - 1 < 2 ** SUC (LOG2 m) by notation - n <= 2 ** SUC (LOG2 m) by inequality [2] - or n <= 2 ** SUC (LOG2 (n - 1)) by notation -*) -val ulog_property = store_thm( - "ulog_property", - ``!n. 0 < n ==> 2 ** (ulog n) < 2 * n /\ n <= 2 ** (ulog n)``, - rw[ulog_eqn] >| [ - `0 < n - 1` by decide_tac >> - qabbrev_tac `m = n - 1` >> - `2 ** SUC (LOG2 m) = 2 * 2 ** (LOG2 m)` by rw[EXP] >> - `2 ** LOG2 m <= n - 1` by rw[TWO_EXP_LOG2_LE, Abbr`m`] >> - decide_tac, - `0 < n - 1` by decide_tac >> - qabbrev_tac `m = n - 1` >> - `2 ** SUC (LOG2 m) = 2 * 2 ** (LOG2 m)` by rw[EXP] >> - `n - 1 < 2 ** SUC (LOG2 m)` by metis_tac[LOG2_PROPERTY] >> - decide_tac - ]); - -(* Theorem: 0 < n ==> !m. (ulog n = m) <=> 2 ** m < 2 * n /\ n <= 2 ** m *) -(* Proof: - If part: 0 < n ==> 2 ** (ulog n) < 2 * n /\ n <= 2 ** (ulog n) - True by ulog_property, 0 < n - Only-if part: 2 ** m < 2 * n /\ n <= 2 ** m ==> ulog n = m - True by ulog_unique -*) -val ulog_thm = store_thm( - "ulog_thm", - ``!n. 0 < n ==> !m. (ulog n = m) <=> (2 ** m < 2 * n /\ n <= 2 ** m)``, - metis_tac[ulog_property, ulog_unique]); - -(* Theorem: (ulog 0 = 0) /\ !n. 0 < n ==> !m. (ulog n = m) <=> (n <= 2 ** m /\ 2 ** m < 2 * n) *) -(* Proof: by ulog_0 ulog_thm *) -Theorem ulog_def_alt: - (ulog 0 = 0) /\ - !n. 0 < n ==> !m. (ulog n = m) <=> (n <= 2 ** m /\ 2 ** m < 2 * n) -Proof rw[ulog_0, ulog_thm] -QED - -(* Theorem: (ulog n = 0) <=> ((n = 0) \/ (n = 1)) *) -(* Proof: - Note !n. SUC n <> 0 by NOT_SUC - so if 1 < n, ulog n <> 0 by ulog_eqn - Thus ulog n = 0 <=> ~(1 < n) by above - or <=> n <= 1 by negation - or <=> n = 0 or n = 1 by range -*) -val ulog_eq_0 = store_thm( - "ulog_eq_0", - ``!n. (ulog n = 0) <=> ((n = 0) \/ (n = 1))``, - rw[ulog_eqn]); - -(* Theorem: (ulog n = 1) <=> (n = 2) *) -(* Proof: - If part: ulog n = 1 ==> n = 2 - Note n <> 0 and n <> 1 by ulog_eq_0 - Thus 1 < n, or 0 < n - 1 by arithmetic - ==> SUC (LOG2 (n - 1)) = 1 by ulog_eqn, 1 < n - or LOG2 (n - 1) = 0 by SUC_EQ, ONE - ==> n - 1 < 2 by LOG_EQ_0, 0 < n - 1 - or n <= 2 by inequality - Combine with 1 < n, n = 2. - Only-if part: ulog 2 = 1 - ulog 2 - = ulog (SUC 1) by TWO - = SUC (LOG2 1) by ulog_suc - = SUC 0 by LOG_1, 0 < 2 - = 1 by ONE -*) -val ulog_eq_1 = store_thm( - "ulog_eq_1", - ``!n. (ulog n = 1) <=> (n = 2)``, - rw[EQ_IMP_THM] >> - `n <> 0 /\ n <> 1` by metis_tac[ulog_eq_0, DECIDE``1 <> 0``] >> - `1 < n /\ 0 < n - 1` by decide_tac >> - `SUC (LOG2 (n - 1)) = 1` by metis_tac[ulog_eqn] >> - `LOG2 (n - 1) = 0` by decide_tac >> - `n - 1 < 2` by metis_tac[LOG_EQ_0, DECIDE``1 < 2``] >> - decide_tac); - -(* Theorem: ulog n <= 1 <=> n <= 2 *) -(* Proof: - ulog n <= 1 - <=> ulog n = 0 \/ ulog n = 1 by arithmetic - <=> n = 0 \/ n = 1 \/ n = 2 by ulog_eq_0, ulog_eq_1 - <=> n <= 2 by arithmetic - -*) -val ulog_le_1 = store_thm( - "ulog_le_1", - ``!n. ulog n <= 1 <=> n <= 2``, - rpt strip_tac >> - `ulog n <= 1 <=> ((ulog n = 0) \/ (ulog n = 1))` by decide_tac >> - rw[ulog_eq_0, ulog_eq_1]); - -(* Theorem: n <= m ==> ulog n <= ulog m *) -(* Proof: - If n = 0, - Note ulog 0 = 0 by ulog_0 - and 0 <= ulog m for anything - If n = 1, - Note ulog 1 = 0 by ulog_1 - Thus 0 <= ulog m by arithmetic - If n <> 1, then 1 < n - Note n <= m, so 1 < m - Thus 0 < n - 1 by arithmetic - and n - 1 <= m - 1 by arithmetic - ==> LOG2 (n - 1) <= LOG2 (m - 1) by LOG2_LE - ==> SUC (LOG2 (n - 1)) <= SUC (LOG2 (m - 1)) by LESS_EQ_MONO - or ulog n <= ulog m by ulog_eqn, 1 < n, 1 < m -*) -val ulog_le = store_thm( - "ulog_le", - ``!m n. n <= m ==> ulog n <= ulog m``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - Cases_on `n = 1` >- - rw[] >> - rw[ulog_eqn, LOG2_LE]); - -(* Theorem: n < m ==> ulog n <= ulog m *) -(* Proof: by ulog_le *) -val ulog_lt = store_thm( - "ulog_lt", - ``!m n. n < m ==> ulog n <= ulog m``, - rw[ulog_le]); - -(* Theorem: ulog (2 ** n) = n *) -(* Proof: - Note 0 < 2 ** n by EXP_POS, 0 < 2 - From 1 < 2 by arithmetic - ==> 2 ** n < 2 * 2 ** n by LT_MULT_RCANCEL, 0 < 2 ** n - Now 2 ** n <= 2 ** n by LESS_EQ_REFL - Thus ulog (2 ** n) = n by ulog_unique -*) -val ulog_2_exp = store_thm( - "ulog_2_exp", - ``!n. ulog (2 ** n) = n``, - rpt strip_tac >> - `0 < 2 ** n` by rw[EXP_POS] >> - `2 ** n < 2 * 2 ** n` by decide_tac >> - `2 ** n <= 2 ** n` by decide_tac >> - rw[ulog_unique]); - -(* Theorem: ulog n <= n *) -(* Proof: - Note n < 2 ** n by X_LT_EXP_X - Thus ulog n <= ulog (2 ** n) by ulog_lt - or ulog n <= n by ulog_2_exp -*) -val ulog_le_self = store_thm( - "ulog_le_self", - ``!n. ulog n <= n``, - metis_tac[X_LT_EXP_X, ulog_lt, ulog_2_exp, DECIDE``1 < 2n``]); - -(* Theorem: ulog n = n <=> n = 0 *) -(* Proof: - If part: ulog n = n ==> n = 0 - By contradiction, assume n <> 0 - Then ?k. n = SUC k by num_CASES, n < 0 - so 2 ** SUC k < 2 * SUC k by ulog_property - or 2 * 2 ** k < 2 * SUC k by EXP - ==> 2 ** k < SUC k by arithmetic - or 2 ** k <= k by arithmetic - This contradicts k < 2 ** k by X_LT_EXP_X, 0 < 2 - Only-if part: ulog 0 = 0 - This is true by ulog_0 -*) -val ulog_eq_self = store_thm( - "ulog_eq_self", - ``!n. (ulog n = n) <=> (n = 0)``, - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `?k. n = SUC k` by metis_tac[num_CASES] >> - `2 * (2 ** k) = 2 ** SUC k` by rw[EXP] >> - `0 < n` by decide_tac >> - `2 ** SUC k < 2 * SUC k` by metis_tac[ulog_property] >> - `2 ** k <= k` by decide_tac >> - `k < 2 ** k` by rw[X_LT_EXP_X] >> - decide_tac); - -(* Theorem: 0 < n ==> ulog n < n *) -(* Proof: - By contradiction, assume ~(ulog n < n). - Then n <= ulog n by ~(ulog n < n) - But ulog n <= n by ulog_le_self - ==> ulog n = n by arithmetic - so n = 0 by ulog_eq_self - This contradicts 0 < n. -*) -val ulog_lt_self = store_thm( - "ulog_lt_self", - ``!n. 0 < n ==> ulog n < n``, - rpt strip_tac >> - spose_not_then strip_assume_tac >> - `ulog n <= n` by rw[ulog_le_self] >> - `ulog n = n` by decide_tac >> - `n = 0` by rw[GSYM ulog_eq_self] >> - decide_tac); - -(* Theorem: (2 ** (ulog n) = n) <=> perfect_power n 2 *) -(* Proof: - Using perfect_power_def, - If part: 2 ** (ulog n) = n ==> ?e. n = 2 ** e - True by taking e = ulog n. - Only-if part: 2 ** ulog (2 ** e) = 2 ** e - This is true by ulog_2_exp -*) -val ulog_exp_exact = store_thm( - "ulog_exp_exact", - ``!n. (2 ** (ulog n) = n) <=> perfect_power n 2``, - rw[perfect_power_def, EQ_IMP_THM] >- - metis_tac[] >> - rw[ulog_2_exp]); - -(* Theorem: ~(perfect_power n 2) ==> 2 ** ulog n <> n *) -(* Proof: by ulog_exp_exact. *) -val ulog_exp_not_exact = store_thm( - "ulog_exp_not_exact", - ``!n. ~(perfect_power n 2) ==> 2 ** ulog n <> n``, - rw[ulog_exp_exact]); - -(* Theorem: 0 < n /\ ~(perfect_power n 2) ==> n < 2 ** ulog n *) -(* Proof: - Note n <= 2 ** ulog n by ulog_property, 0 < n - But n <> 2 ** ulog n by ulog_exp_not_exact, ~(perfect_power n 2) - Thus n < 2 ** ulog n by LESS_OR_EQ -*) -val ulog_property_not_exact = store_thm( - "ulog_property_not_exact", - ``!n. 0 < n /\ ~(perfect_power n 2) ==> n < 2 ** ulog n``, - metis_tac[ulog_property, ulog_exp_not_exact, LESS_OR_EQ]); - -(* Theorem: 1 < n /\ ODD n ==> n < 2 ** ulog n *) -(* Proof: - Note 0 < n /\ n <> 1 by 1 < n - Thus n <= 2 ** ulog n by ulog_property, 0 < n - But ~(perfect_power n 2) by perfect_power_2_odd, n <> 1 - ==> n <> 2 ** ulog n by ulog_exp_not_exact, ~(perfect_power n 2) - Thus n < 2 ** ulog n by LESS_OR_EQ -*) -val ulog_property_odd = store_thm( - "ulog_property_odd", - ``!n. 1 < n /\ ODD n ==> n < 2 ** ulog n``, - rpt strip_tac >> - `0 < n /\ n <> 1` by decide_tac >> - `n <= 2 ** ulog n` by metis_tac[ulog_property] >> - `~(perfect_power n 2)` by metis_tac[perfect_power_2_odd] >> - `2 ** ulog n <> n` by rw[ulog_exp_not_exact] >> - decide_tac); - -(* Theorem: n <= 2 ** m ==> ulog n <= m *) -(* Proof: - n <= 2 ** m - ==> ulog n <= ulog (2 ** m) by ulog_le - ==> ulog n <= m by ulog_2_exp -*) -val exp_to_ulog = store_thm( - "exp_to_ulog", - ``!m n. n <= 2 ** m ==> ulog n <= m``, - metis_tac[ulog_le, ulog_2_exp]); - -(* Theorem: 1 < n ==> 0 < ulog n *) -(* Proof: - Note 1 < n ==> n <> 0 /\ n <> 1 by arithmetic - so ulog n <> 0 by ulog_eq_0 - or 0 < ulog n by NOT_ZERO_LT_ZERO -*) -val ulog_pos = store_thm( - "ulog_pos[simp]", - ``!n. 1 < n ==> 0 < ulog n``, - metis_tac[ulog_eq_0, NOT_ZERO, DECIDE``1 < n <=> n <> 0 /\ n <> 1``]); - -(* Theorem: 1 < n ==> 1 <= ulog n *) -(* Proof: - Note 0 < ulog n by ulog_pos - Thus 1 <= ulog n by arithmetic -*) -val ulog_ge_1 = store_thm( - "ulog_ge_1", - ``!n. 1 < n ==> 1 <= ulog n``, - metis_tac[ulog_pos, DECIDE``0 < n ==> 1 <= n``]); - -(* Theorem: 2 < n ==> 1 < (ulog n) ** 2 *) -(* Proof: - Note 1 < n /\ n <> 2 by 2 < n - so 0 < ulog n by ulog_pos, 1 < n - and ulog n <> 1 by ulog_eq_1, n <> 2 - Thus 1 < ulog n by ulog n <> 0, ulog n <> 1 - so 1 < (ulog n) ** 2 by ONE_LT_EXP, 0 < 2 -*) -val ulog_sq_gt_1 = store_thm( - "ulog_sq_gt_1", - ``!n. 2 < n ==> 1 < (ulog n) ** 2``, - rpt strip_tac >> - `1 < n /\ n <> 2` by decide_tac >> - `0 < ulog n` by rw[] >> - `ulog n <> 1` by rw[ulog_eq_1] >> - `1 < ulog n` by decide_tac >> - rw[ONE_LT_EXP]); - -(* Theorem: 1 < n ==> 4 <= (2 * ulog n) ** 2 *) -(* Proof: - Note 0 < ulog n by ulog_pos, 1 < n - Thus 2 <= 2 * ulog n by arithmetic - or 4 <= (2 * ulog n) ** 2 by EXP_BASE_LE_MONO -*) -val ulog_twice_sq = store_thm( - "ulog_twice_sq", - ``!n. 1 < n ==> 4 <= (2 * ulog n) ** 2``, - rpt strip_tac >> - `0 < ulog n` by rw[ulog_pos] >> - `2 <= 2 * ulog n` by decide_tac >> - `2 ** 2 <= (2 * ulog n) ** 2` by rw[EXP_BASE_LE_MONO] >> - `2 ** 2 = 4` by rw[] >> - decide_tac); - -(* Theorem: ulog n = if n = 0 then 0 - else if (perfect_power n 2) then (LOG2 n) else SUC (LOG2 n) *) -(* Proof: - This is to show: - (1) ulog 0 = 0, true by ulog_0 - (2) perfect_power n 2 ==> ulog n = LOG2 n - Note ?k. n = 2 ** k by perfect_power_def - Thus ulog n = k by ulog_exp_exact - and LOG2 n = k by LOG_EXACT_EXP, 1 < 2 - (3) ~(perfect_power n 2) ==> ulog n = SUC (LOG2 n) - Let m = SUC (LOG2 n). - Then 2 ** m - = 2 * 2 ** (LOG2 n) by EXP - <= 2 * n by TWO_EXP_LOG2_LE - But n <> LOG2 n by LOG2_EXACT_EXP, ~(perfect_power n 2) - Thus 2 ** m < 2 * n [1] - - Also n < 2 ** m by LOG2_PROPERTY - Thus n <= 2 ** m, [2] - giving ulog n = m by ulog_unique, [1] [2] -*) -val ulog_alt = store_thm( - "ulog_alt", - ``!n. ulog n = if n = 0 then 0 - else if (perfect_power n 2) then (LOG2 n) else SUC (LOG2 n)``, - rw[] >- - metis_tac[perfect_power_def, ulog_exp_exact, LOG_EXACT_EXP, DECIDE``1 < 2``] >> - qabbrev_tac `m = SUC (LOG2 n)` >> - (irule ulog_unique >> rpt conj_tac) >| [ - `2 ** m = 2 * 2 ** (LOG2 n)` by rw[EXP, Abbr`m`] >> - `2 ** (LOG2 n) <= n` by rw[TWO_EXP_LOG2_LE] >> - `2 ** (LOG2 n) <> n` by rw[LOG2_EXACT_EXP, GSYM perfect_power_def] >> - decide_tac, - `n < 2 ** m` by rw[LOG2_PROPERTY, Abbr`m`] >> - decide_tac - ]); - -(* -Thus, for 0 < n, (ulog n) and SUC (LOG2 n) differ only for (perfect_power n 2). -This means that replacing AKS bounds of SUC (LOG2 n) by (ulog n) -only affect calculations involving (perfect_power n 2), -which are irrelevant for primality testing ! -However, in display, (ulog n) is better, while SUC (LOG2 n) is a bit ugly. -*) - -(* Theorem: 0 < n ==> (LOG2 n <= ulog n /\ ulog n <= 1 + LOG2 n) *) -(* Proof: by ulog_alt *) -val ulog_LOG2 = store_thm( - "ulog_LOG2", - ``!n. 0 < n ==> (LOG2 n <= ulog n /\ ulog n <= 1 + LOG2 n)``, - rw[ulog_alt]); - -(* Theorem: 0 < n ==> !m. perfect_power n m <=> ?k. k <= ulog n /\ (n = m ** k) *) -(* Proof: by perfect_power_bound_LOG2, ulog_LOG2 *) -val perfect_power_bound_ulog = store_thm( - "perfect_power_bound_ulog", - ``!n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= ulog n /\ (n = m ** k)``, - rw[EQ_IMP_THM] >| [ - `LOG2 n <= ulog n` by rw[ulog_LOG2] >> - metis_tac[perfect_power_bound_LOG2, LESS_EQ_TRANS], - metis_tac[perfect_power_def] - ]); - -(* ------------------------------------------------------------------------- *) -(* Upper Log Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: ulog (m * n) <= ulog m + ulog n *) -(* Proof: - Let x = ulog (m * n), y = ulog m + ulog n. - Note m * n <= 2 ** x < 2 * m * n by ulog_thm - and m <= 2 ** ulog m < 2 * m by ulog_thm - and n <= 2 ** ulog n < 2 * n by ulog_thm - Note that 2 ** ulog m * 2 ** ulog n = 2 ** y by EXP_ADD - Multiplying inequalities, - m * n <= 2 ** y by LE_MONO_MULT2 - 2 ** y < 4 * m * n by LT_MONO_MULT2 - The picture is: - m * n ....... 2 * m * n ....... 4 * m * n - 2 ** x somewhere - 2 ** y somewhere - If 2 ** y < 2 * m * n, - Then x = y by ulog_unique - Otherwise, - 2 ** y is in the second range. - Then 2 ** x < 2 ** y since 2 ** x in the first - or x < y by EXP_BASE_LT_MONO - Combining these two cases: x <= y. -*) -val ulog_mult = store_thm( - "ulog_mult", - ``!m n. ulog (m * n) <= ulog m + ulog n``, - rpt strip_tac >> - Cases_on `(m = 0) \/ (n = 0)` >- - fs[] >> - `m * n <> 0` by rw[] >> - `0 < m /\ 0 < n /\ 0 < m * n` by decide_tac >> - qabbrev_tac `x = ulog (m * n)` >> - qabbrev_tac `y = ulog m + ulog n` >> - `m * n <= 2 ** x /\ 2 ** x < TWICE (m * n)` by metis_tac[ulog_thm] >> - `m * n <= 2 ** y /\ 2 ** y < (TWICE m) * (TWICE n)` by metis_tac[ulog_thm, LE_MONO_MULT2, LT_MONO_MULT2, EXP_ADD] >> - Cases_on `2 ** y < TWICE (m * n)` >| [ - `y = x` by metis_tac[ulog_unique] >> - decide_tac, - `2 ** x < 2 ** y /\ 1 < 2` by decide_tac >> - `x < y` by metis_tac[EXP_BASE_LT_MONO] >> - decide_tac - ]); - -(* Theorem: ulog (m ** n) <= n * ulog m *) -(* Proof: - By induction on n. - Base: ulog (m ** 0) <= 0 * ulog m - LHS = ulog (m ** 0) - = ulog 1 by EXP_0 - = 0 by ulog_1 - <= 0 * ulog m by MULT - = RHS - Step: ulog (m ** n) <= n * ulog m ==> ulog (m ** SUC n) <= SUC n * ulog m - LHS = ulog (m ** SUC n) - = ulog (m * m ** n) by EXP - <= ulog m + ulog (m ** n) by ulog_mult - <= ulog m + n * ulog m by induction hypothesis - = (1 + n) * ulog m by RIGHT_ADD_DISTRIB - = SUC n * ulog m by ADD1, ADD_COMM - = RHS -*) -val ulog_exp = store_thm( - "ulog_exp", - ``!m n. ulog (m ** n) <= n * ulog m``, - rpt strip_tac >> - Induct_on `n` >> - rw[EXP_0] >> - `ulog (m ** SUC n) <= ulog m + ulog (m ** n)` by rw[EXP, ulog_mult] >> - `ulog m + ulog (m ** n) <= ulog m + n * ulog m` by rw[] >> - `ulog m + n * ulog m = SUC n * ulog m` by rw[ADD1] >> - decide_tac); - -(* Theorem: 0 < n /\ EVEN n ==> (ulog n = 1 + ulog (HALF n)) *) -(* Proof: - Let k = HALF n. - Then 0 < k by HALF_EQ_0, EVEN n - and EVEN n ==> n = TWICE k by EVEN_HALF - Note n <= 2 ** ulog n < 2 * n by ulog_thm, by 0 < n - and k <= 2 ** ulog k < 2 * k by ulog_thm, by 0 < k - so 2 * k <= 2 * 2 ** ulog k < 2 * 2 * k by multiplying 2 - or n <= 2 ** (1 + ulog k) < 2 * n by EXP - Thus ulog n = 1 + ulog k by ulog_unique -*) -val ulog_even = store_thm( - "ulog_even", - ``!n. 0 < n /\ EVEN n ==> (ulog n = 1 + ulog (HALF n))``, - rpt strip_tac >> - qabbrev_tac `k = HALF n` >> - `n = TWICE k` by rw[EVEN_HALF, Abbr`k`] >> - `0 < k` by rw[Abbr`k`] >> - `n <= 2 ** ulog n /\ 2 ** ulog n < 2 * n` by metis_tac[ulog_thm] >> - `k <= 2 ** ulog k /\ 2 ** ulog k < 2 * k` by metis_tac[ulog_thm] >> - `2 <> 0` by decide_tac >> - `n <= 2 * 2 ** ulog k` by rw[LE_MULT_LCANCEL] >> - `2 * 2 ** ulog k < 2 * n` by rw[LT_MULT_LCANCEL] >> - `2 * 2 ** ulog k = 2 ** (1 + ulog k)` by metis_tac[EXP, ADD1, ADD_COMM] >> - metis_tac[ulog_unique]); - -(* Theorem: 1 < n /\ ODD n ==> ulog (HALF n) + 1 <= ulog n *) -(* Proof: - Let k = HALF n. - Then 0 < k by HALF_EQ_0, 1 < n - and ODD n ==> n = TWICE k + 1 by ODD_HALF - Note n <= 2 ** ulog n < 2 * n by ulog_thm, by 0 < n - and k <= 2 ** ulog k < 2 * k by ulog_thm, by 0 < k - so 2 * k <= 2 * 2 ** ulog k < 2 * 2 * k by multiplying 2 - or (2 * k) <= 2 ** (1 + ulog k) < 2 * (2 * k) by EXP - Since 2 * k < n, so 2 * (2 * k) < 2 * n, - the picture is: - 2 * k ... n ...... 2 * (2 * k) ... 2 * n - <--- 2 ** ulog n ----> - <--- 2 ** (1 + ulog k) --> - If n <= 2 ** (1 + ulog k), then ulog n = 1 + ulog k by ulog_unique - Otherwise, 2 ** (1 + ulog k) < 2 ** ulog n - so 1 + ulog k < ulog n by EXP_BASE_LT_MONO, 1 < 2 - Combining, 1 + ulog k <= ulog n. -*) -val ulog_odd = store_thm( - "ulog_odd", - ``!n. 1 < n /\ ODD n ==> ulog (HALF n) + 1 <= ulog n``, - rpt strip_tac >> - qabbrev_tac `k = HALF n` >> - `(n <> 0) /\ (n <> 1)` by decide_tac >> - `0 < n /\ 0 < k` by metis_tac[HALF_EQ_0, NOT_ZERO_LT_ZERO] >> - `n = TWICE k + 1` by rw[ODD_HALF, Abbr`k`] >> - `n <= 2 ** ulog n /\ 2 ** ulog n < 2 * n` by metis_tac[ulog_thm] >> - `k <= 2 ** ulog k /\ 2 ** ulog k < 2 * k` by metis_tac[ulog_thm] >> - `2 <> 0 /\ 1 < 2` by decide_tac >> - `2 * k <= 2 * 2 ** ulog k` by rw[LE_MULT_LCANCEL] >> - `2 * 2 ** ulog k < 2 * (2 * k)` by rw[LT_MULT_LCANCEL] >> - `2 * 2 ** ulog k = 2 ** (1 + ulog k)` by metis_tac[EXP, ADD1, ADD_COMM] >> - Cases_on `n <= 2 ** (1 + ulog k)` >| [ - `2 * k < n` by decide_tac >> - `2 * (2 * k) < 2 * n` by rw[LT_MULT_LCANCEL] >> - `2 ** (1 + ulog k) < TWICE n` by decide_tac >> - `1 + ulog k = ulog n` by metis_tac[ulog_unique] >> - decide_tac, - `2 ** (1 + ulog k) < 2 ** ulog n` by decide_tac >> - `1 + ulog k < ulog n` by metis_tac[EXP_BASE_LT_MONO] >> - decide_tac - ]); - -(* -EVAL ``let n = 13 in [ulog (HALF n) + 1; ulog n]``; -|- (let n = 13 in [ulog (HALF n) + 1; ulog n]) = [4; 4]: -|- (let n = 17 in [ulog (HALF n) + 1; ulog n]) = [4; 5]: -*) - -(* Theorem: 1 < n ==> ulog (HALF n) + 1 <= ulog n *) -(* Proof: - Note 1 < n ==> 0 < n by arithmetic - If EVEN n, true by ulog_even, 0 < n - If ODD n, true by ulog_odd, 1 < n, ODD_EVEN. -*) -val ulog_half = store_thm( - "ulog_half", - ``!n. 1 < n ==> ulog (HALF n) + 1 <= ulog n``, - rpt strip_tac >> - Cases_on `EVEN n` >- - rw[ulog_even] >> - rw[ODD_EVEN, ulog_odd]); - -(* Theorem: SQRT n <= 2 ** (ulog n) *) -(* Proof: - Note n <= 2 ** ulog n by ulog_property - and SQRT n <= n by SQRT_LE_SELF - Thus SQRT n <= 2 ** ulog n by LESS_EQ_TRANS - or SQRT n <= -*) -val sqrt_upper = store_thm( - "sqrt_upper", - ``!n. SQRT n <= 2 ** (ulog n)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - `n <= 2 ** ulog n` by rw[ulog_property] >> - `SQRT n <= n` by rw[SQRT_LE_SELF] >> - decide_tac); - -(* ------------------------------------------------------------------------- *) -(* Power Free up to a limit *) -(* ------------------------------------------------------------------------- *) - -(* Define a power free property of a number *) -val power_free_upto_def = Define` - power_free_upto n k <=> !j. 1 < j /\ j <= k ==> (ROOT j n) ** j <> n -`; -(* make this an infix relation. *) -val _ = set_fixity "power_free_upto" (Infix(NONASSOC, 450)); (* same as relation *) - -(* Theorem: (n power_free_upto 0) = T *) -(* Proof: by power_free_upto_def, no counter-example. *) -val power_free_upto_0 = store_thm( - "power_free_upto_0", - ``!n. (n power_free_upto 0) = T``, - rw[power_free_upto_def]); - -(* Theorem: (n power_free_upto 1) = T *) -(* Proof: by power_free_upto_def, no counter-example. *) -val power_free_upto_1 = store_thm( - "power_free_upto_1", - ``!n. (n power_free_upto 1) = T``, - rw[power_free_upto_def]); - -(* Theorem: 0 < k /\ (n power_free_upto k) ==> - ((n power_free_upto (k + 1)) <=> ROOT (k + 1) n ** (k + 1) <> n) *) -(* Proof: by power_free_upto_def *) -val power_free_upto_suc = store_thm( - "power_free_upto_suc", - ``!n k. 0 < k /\ (n power_free_upto k) ==> - ((n power_free_upto (k + 1)) <=> ROOT (k + 1) n ** (k + 1) <> n)``, - rw[power_free_upto_def] >> - rw[EQ_IMP_THM] >> - metis_tac[LESS_OR_EQ, DECIDE``k < n + 1 ==> k <= n``]); - -(* Theorem: LOG2 n <= b ==> (power_free n <=> (1 < n /\ n power_free_upto b)) *) -(* Proof: - If part: LOG2 n <= b /\ power_free n ==> 1 < n /\ n power_free_upto b - (1) 1 < n, - By contradiction, suppose n <= 1. - Then n = 0, but power_free 0 = F by power_free_0 - or n = 1, but power_free 1 = F by power_free_1 - (2) n power_free_upto b, - By power_free_upto_def, this is to show: - 1 < j /\ j <= b ==> ROOT j n ** j <> n - By contradiction, suppose ROOT j n ** j = n. - Then n = m ** j where m = ROOT j n, with j <> 1. - This contradicts power_free n by power_free_def - - Only-if part: 1 < n /\ LOG2 n <= b /\ n power_free_upto b ==> power_free n - By contradiction, suppose ~(power_free n). - Then ?e. n = m ** e with n = m ==> e <> 1 by power_free_def - ==> perfect_power n m by perfect_power_def - Thus ?k. k <= LOG2 n /\ (n = m ** k) by perfect_power_bound_LOG2, 0 < n - Now k <> 0 by EXP_0, n <> 1 - so m = ROOT k n by ROOT_FROM_POWER, k <> 0 - - Claim: k <> 1 - Proof: Note m <> 0 by ROOT_EQ_0, n <> 0 - and m <> 1 by EXP_1, k <> 0, n <> 1 - ==> 1 < m by m <> 0, m <> 1 - Thus n = m ** e = m ** k ==> k = e by EXP_BASE_INJECTIVE - But e <> 1 - since e = 1 ==> n <> m, by n = m ==> e <> 1 - yet n = m ** 1 ==> n = m by EXP_1 - Since k = e, k <> 1. - - Therefore 1 < k by k <> 0, k <> 1 - and k <= LOG2 n /\ LOG2 n <= b ==> k <= b by arithmetic - With 1 < k /\ k <= b /\ m = ROOT k n /\ m ** k = n, - These will give a contradiction by power_free_upto_def -*) -val power_free_check_upto = store_thm( - "power_free_check_upto", - ``!n b. LOG2 n <= b ==> (power_free n <=> (1 < n /\ n power_free_upto b))``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `(n = 0) \/ (n = 1)` by decide_tac >- - fs[power_free_0] >> - fs[power_free_1], - rw[power_free_upto_def] >> - spose_not_then strip_assume_tac >> - `j <> 1` by decide_tac >> - metis_tac[power_free_def], - simp[power_free_def] >> - spose_not_then strip_assume_tac >> - `perfect_power n m` by metis_tac[perfect_power_def] >> - `0 < n /\ n <> 1` by decide_tac >> - `?k. k <= LOG2 n /\ (n = m ** k)` by rw[GSYM perfect_power_bound_LOG2] >> - `k <> 0` by metis_tac[EXP_0] >> - `m = ROOT k n` by rw[ROOT_FROM_POWER] >> - `k <> 1` by - (`m <> 0` by rw[ROOT_EQ_0] >> - `m <> 1 /\ e <> 1` by metis_tac[EXP_1] >> - `1 < m` by decide_tac >> - metis_tac[EXP_BASE_INJECTIVE]) >> - `1 < k` by decide_tac >> - `k <= b` by decide_tac >> - metis_tac[power_free_upto_def] - ]); - -(* Theorem: power_free n <=> (1 < n /\ n power_free_upto LOG2 n) *) -(* Proof: by power_free_check_upto, LOG2 n <= LOG2 n *) -val power_free_check_upto_LOG2 = store_thm( - "power_free_check_upto_LOG2", - ``!n. power_free n <=> (1 < n /\ n power_free_upto LOG2 n)``, - rw[power_free_check_upto]); - -(* Theorem: power_free n <=> (1 < n /\ n power_free_upto ulog n) *) -(* Proof: - If n = 0, - LHS = power_free 0 = F by power_free_0 - = RHS, as 1 < 0 = F - If n <> 0, - Then LOG2 n <= ulog n by ulog_LOG2, 0 < n - The result follows by power_free_check_upto -*) -val power_free_check_upto_ulog = store_thm( - "power_free_check_upto_ulog", - ``!n. power_free n <=> (1 < n /\ n power_free_upto ulog n)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[power_free_0] >> - rw[power_free_check_upto, ulog_LOG2]); - -(* Theorem: power_free 2 *) -(* Proof: - power_free 2 - <=> 2 power_free_upto (LOG2 2) by power_free_check_upto_LOG2 - <=> 2 power_free_upto 1 by LOG2_2 - <=> T by power_free_upto_1 -*) -val power_free_2 = store_thm( - "power_free_2", - ``power_free 2``, - rw[power_free_check_upto_LOG2, power_free_upto_1]); - -(* Theorem: power_free 3 *) -(* Proof: - Note 3 power_free_upto 1 by power_free_upto_1 - power_free 3 - <=> 3 power_free_upto (ulog 3) by power_free_check_upto_ulog - <=> 3 power_free_upto 2 by evaluation - <=> ROOT 2 3 ** 2 <> 3 by power_free_upto_suc, 0 < 1 - <=> T by evaluation -*) -val power_free_3 = store_thm( - "power_free_3", - ``power_free 3``, - `3 power_free_upto 1` by rw[power_free_upto_1] >> - `ulog 3 = 2` by EVAL_TAC >> - `ROOT 2 3 ** 2 <> 3` by EVAL_TAC >> - `power_free 3 <=> 3 power_free_upto 2` by rw[power_free_check_upto_ulog] >> - metis_tac[power_free_upto_suc, DECIDE``0 < 1 /\ (1 + 1 = 2)``]); - -(* Define a power free test, based on (ulog n), for computation. *) -val power_free_test_def = Define` - power_free_test n <=>(1 < n /\ n power_free_upto (ulog n)) -`; - -(* Theorem: power_free_test n = power_free n *) -(* Proof: by power_free_test_def, power_free_check_upto_ulog *) -val power_free_test_eqn = store_thm( - "power_free_test_eqn", - ``!n. power_free_test n = power_free n``, - rw[power_free_test_def, power_free_check_upto_ulog]); - -(* Theorem: power_free n <=> - (1 < n /\ !j. 1 < j /\ j <= (LOG2 n) ==> ROOT j n ** j <> n) *) -(* Proof: by power_free_check_upto_ulog, power_free_upto_def *) -val power_free_test_upto_LOG2 = store_thm( - "power_free_test_upto_LOG2", - ``!n. power_free n <=> - (1 < n /\ !j. 1 < j /\ j <= (LOG2 n) ==> ROOT j n ** j <> n)``, - rw[power_free_check_upto_LOG2, power_free_upto_def]); - -(* Theorem: power_free n <=> - (1 < n /\ !j. 1 < j /\ j <= (ulog n) ==> ROOT j n ** j <> n) *) -(* Proof: by power_free_check_upto_ulog, power_free_upto_def *) -val power_free_test_upto_ulog = store_thm( - "power_free_test_upto_ulog", - ``!n. power_free n <=> - (1 < n /\ !j. 1 < j /\ j <= (ulog n) ==> ROOT j n ** j <> n)``, - rw[power_free_check_upto_ulog, power_free_upto_def]); - -(* ------------------------------------------------------------------------- *) -(* Another Characterisation of Power Free *) -(* ------------------------------------------------------------------------- *) - -(* Define power index of n, the highest index of n in power form by descending from k *) -val power_index_def = Define ` - power_index n k <=> - if k <= 1 then 1 - else if (ROOT k n) ** k = n then k - else power_index n (k - 1) -`; - -(* Theorem: power_index n 0 = 1 *) -(* Proof: by power_index_def *) -val power_index_0 = store_thm( - "power_index_0", - ``!n. power_index n 0 = 1``, - rw[Once power_index_def]); - -(* Theorem: power_index n 1 = 1 *) -(* Proof: by power_index_def *) -val power_index_1 = store_thm( - "power_index_1", - ``!n. power_index n 1 = 1``, - rw[Once power_index_def]); - -(* Theorem: (ROOT (power_index n k) n) ** (power_index n k) = n *) -(* Proof: - By induction on k. - Base: ROOT (power_index n 0) n ** power_index n 0 = n - ROOT (power_index n 0) n ** power_index n 0 - = (ROOT 1 n) ** 1 by power_index_0 - = n ** 1 by ROOT_1 - = n by EXP_1 - Step: ROOT (power_index n k) n ** power_index n k = n ==> - ROOT (power_index n (SUC k)) n ** power_index n (SUC k) = n - If k = 0, - ROOT (power_index n (SUC 0)) n ** power_index n (SUC 0) - = ROOT (power_index n 1) n ** power_index n 1 by ONE - = (ROOT 1 n) ** 1 by power_index_1 - = n ** 1 by ROOT_1 - = n by EXP_1 - If k <> 0, - Then ~(SUC k <= 1) by 0 < k - If ROOT (SUC k) n ** SUC k = n, - Then power_index n (SUC k) = SUC k by power_index_def - so ROOT (power_index n (SUC k)) n ** power_index n (SUC k) - = ROOT (SUC k) n ** SUC k by above - = n by condition - If ROOT (SUC k) n ** SUC k <> n, - Then power_index n (SUC k) = power_index n k by power_index_def - so ROOT (power_index n (SUC k)) n ** power_index n (SUC k) - = ROOT (power_index n k) n ** power_index n k by above - = n by induction hypothesis -*) -val power_index_eqn = store_thm( - "power_index_eqn", - ``!n k. (ROOT (power_index n k) n) ** (power_index n k) = n``, - rpt strip_tac >> - Induct_on `k` >- - rw[power_index_0] >> - Cases_on `k = 0` >- - rw[power_index_1] >> - `~(SUC k <= 1)` by decide_tac >> - rw_tac std_ss[Once power_index_def] >- - rw[Once power_index_def] >> - `power_index n (SUC k) = power_index n k` by rw[Once power_index_def] >> - rw[]); - -(* Theorem: perfect_power n (ROOT (power_index n k) n) *) -(* Proof: - Let m = ROOT (power_index n k) n. - By perfect_power_def, this is to show: - ?e. n = m ** e - Take e = power_index n k. - m ** e - = (ROOT (power_index n k) n) ** (power_index n k) by root_compute_eqn - = n by power_index_eqn -*) -val power_index_root = store_thm( - "power_index_root", - ``!n k. perfect_power n (ROOT (power_index n k) n)``, - metis_tac[perfect_power_def, power_index_eqn]); - -(* Theorem: power_index 1 k = if k = 0 then 1 else k *) -(* Proof: - If k = 0, - power_index 1 0 = 1 by power_index_0 - If k <> 0, then 0 < k. - If k = 1, - Then power_index 1 1 = 1 = k by power_index_1 - If k <> 1, 1 < k. - Note ROOT k 1 = 1 by ROOT_OF_1, 0 < k. - so power_index 1 k = k by power_index_def -*) -val power_index_of_1 = store_thm( - "power_index_of_1", - ``!k. power_index 1 k = if k = 0 then 1 else k``, - rw[Once power_index_def]); - -(* Theorem: 0 < k /\ ((ROOT k n) ** k = n) ==> (power_index n k = k) *) -(* Proof: - If k = 1, - True since power_index n 1 = 1 by power_index_1 - If k <> 1, then 1 < k by 0 < k - True by power_index_def -*) -val power_index_exact_root = store_thm( - "power_index_exact_root", - ``!n k. 0 < k /\ ((ROOT k n) ** k = n) ==> (power_index n k = k)``, - rpt strip_tac >> - Cases_on `k = 1` >- - rw[power_index_1] >> - `1 < k` by decide_tac >> - rw[Once power_index_def]); - -(* Theorem: (ROOT k n) ** k <> n ==> (power_index n k = power_index n (k - 1)) *) -(* Proof: - If k = 0, - Then k = k - 1 by k = 0 - Thus true trivially. - If k = 1, - Note power_index n 1 = 1 by power_index_1 - and power_index n 0 = 1 by power_index_0 - Thus true. - If k <> 0 /\ k <> 1, then 1 < k by arithmetic - True by power_index_def -*) -val power_index_not_exact_root = store_thm( - "power_index_not_exact_root", - ``!n k. (ROOT k n) ** k <> n ==> (power_index n k = power_index n (k - 1))``, - rpt strip_tac >> - Cases_on `k = 0` >| [ - `k = k - 1` by decide_tac >> - rw[], - Cases_on `k = 1` >- - rw[power_index_0, power_index_1] >> - `1 < k` by decide_tac >> - rw[Once power_index_def] - ]); - -(* Theorem: k <= m /\ (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n) ==> (power_index n m = power_index n k) *) -(* Proof: - By induction on (m - k). - Base: k <= m /\ 0 = m - k ==> power_index n m = power_index n k - Note m <= k by 0 = m - k - so m = k by k <= m - Thus true trivially. - Step: !m'. v = m' - k /\ k <= m' /\ ... ==> power_index n m' = power_index n k ==> - SUC v = m - k ==> power_index n m = power_index n k - If m = k, true trivially. - If m <> k, then k < m. - Thus k <= (m - 1), and v = (m - 1) - k. - Note ROOT m n ** m <> n by j = m in implication - Thus power_index n m - = power_index n (m - 1) by power_index_not_exact_root - = power_index n k by induction hypothesis, m' = m - 1. -*) -val power_index_no_exact_roots = store_thm( - "power_index_no_exact_roots", - ``!m n k. k <= m /\ (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n) ==> (power_index n m = power_index n k)``, - rpt strip_tac >> - Induct_on `m - k` >| [ - rpt strip_tac >> - `m = k` by decide_tac >> - rw[], - rpt strip_tac >> - Cases_on `m = k` >- - rw[] >> - `ROOT m n ** m <> n` by rw[] >> - `k <= m - 1` by decide_tac >> - `power_index n (m - 1) = power_index n k` by rw[] >> - rw[power_index_not_exact_root] - ]); - -(* The theorem power_index_equal requires a detective-style proof, based on these lemma. *) - -(* Theorem: k <= m /\ ((ROOT k n) ** k = n) ==> k <= power_index n m *) -(* Proof: - If k = 0, - Then n = 1 by EXP - If m = 0, - Then power_index 1 0 = 1 by power_index_of_1 - But k <= 0, so k = 0 by arithmetic - Hence k <= power_index n m - If m <> 0, - Then power_index 1 m = m by power_index_of_1 - Hence k <= power_index 1 m = m by given - - If k <> 0, then 0 < k. - Let s = {j | j <= m /\ ((ROOT j n) ** j = n)} - Then s SUBSET (count (SUC m)) by SUBSET_DEF - ==> FINITE s by SUBSET_FINITE, FINITE_COUNT - Note k IN s by given - ==> s <> {} by MEMBER_NOT_EMPTY - Let t = MAX_SET s. - - Claim: !x. t < x /\ x <= m ==> (ROOT x n) ** x <> n - Proof: By contradiction, suppose (ROOT x n) ** x = n - Then x IN s, so x <= t by MAX_SET_PROPERTY - This contradicts t < x. - - Note t IN s by MAX_SET_IN_SET - so t <= m /\ (ROOT t n) ** t = n by above - Thus power_index n m = power_index n t by power_index_no_exact_roots, t <= m - and power_index n t = t by power_index_exact_root, (ROOT t n) ** t = n - But k <= t by MAX_SET_PROPERTY - Thus k <= t = power_index n m -*) -val power_index_lower = store_thm( - "power_index_lower", - ``!m n k. k <= m /\ ((ROOT k n) ** k = n) ==> k <= power_index n m``, - rpt strip_tac >> - Cases_on `k = 0` >| [ - `n = 1` by fs[EXP] >> - rw[power_index_of_1], - `0 < k` by decide_tac >> - qabbrev_tac `s = {j | j <= m /\ ((ROOT j n) ** j = n)}` >> - `!j. j IN s <=> j <= m /\ ((ROOT j n) ** j = n)` by rw[Abbr`s`] >> - `s SUBSET (count (SUC m))` by rw[SUBSET_DEF] >> - `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - `k IN s` by rw[] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - qabbrev_tac `t = MAX_SET s` >> - `!x. t < x /\ x <= m ==> (ROOT x n) ** x <> n` by - (spose_not_then strip_assume_tac >> - `x IN s` by rw[] >> - `x <= t` by rw[MAX_SET_PROPERTY, Abbr`t`] >> - decide_tac) >> - `t IN s` by rw[MAX_SET_IN_SET, Abbr`t`] >> - `power_index n m = power_index n t` by metis_tac[power_index_no_exact_roots] >> - `k <= t` by rw[MAX_SET_PROPERTY, Abbr`t`] >> - `(ROOT t n) ** t = n` by metis_tac[] >> - `power_index n t = t` by rw[power_index_exact_root] >> - decide_tac - ]); - -(* Theorem: 0 < power_index n k *) -(* Proof: - If k = 0, - True since power_index n 0 = 1 by power_index_0 - If k <> 0, - Then 1 <= k. - Note (ROOT 1 n) ** 1 = n ** 1 = n by ROOT_1, EXP_1 - Thus 1 <= power_index n k by power_index_lower - or 0 < power_index n k -*) -val power_index_pos = store_thm( - "power_index_pos", - ``!n k. 0 < power_index n k``, - rpt strip_tac >> - Cases_on `k = 0` >- - rw[power_index_0] >> - `1 <= power_index n k` by rw[power_index_lower, EXP_1] >> - decide_tac); - -(* Theorem: 0 < k ==> power_index n k <= k *) -(* Proof: - By induction on k. - Base: 0 < 0 ==> power_index n 0 <= 0 - True by 0 < 0 = F. - Step: 0 < k ==> power_index n k <= k ==> - 0 < SUC k ==> power_index n (SUC k) <= SUC k - If k = 0, - Then SUC k = 1 by ONE - True since power_index n 1 = 1 by power_index_1 - If k <> 0, - Let m = SUC k, or k = m - 1. - Then 1 < m by arithmetic - If (ROOT m n) ** m = n, - Then power_index n m - = m <= m by power_index_exact_root - If (ROOT m n) ** m <> n, - Then power_index n m - = power_index n (m - 1) by power_index_not_exact_root - = power_index n k by m - 1 = k - <= k by induction hypothesis - But k < SUC k = m by LESS_SUC - Thus power_index n m < m by LESS_EQ_LESS_TRANS - or power_index n m <= m by LESS_IMP_LESS_OR_EQ -*) -val power_index_upper = store_thm( - "power_index_upper", - ``!n k. 0 < k ==> power_index n k <= k``, - strip_tac >> - Induct >- - rw[] >> - rpt strip_tac >> - Cases_on `k = 0` >- - rw[power_index_1] >> - `1 < SUC k` by decide_tac >> - qabbrev_tac `m = SUC k` >> - Cases_on `(ROOT m n) ** m = n` >- - rw[power_index_exact_root] >> - rw[power_index_not_exact_root, Abbr`m`]); - -(* Theorem: 0 < k /\ k <= m ==> - ((power_index n m = power_index n k) <=> (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n)) *) -(* Proof: - If part: 0 < k /\ k <= m /\ power_index n m = power_index n k /\ k < j /\ j <= m ==> ROOT j n ** j <> n - By contradiction, suppose ROOT j n ** j = n. - Then j <= power_index n m by power_index_lower - But power_index n k <= k by power_index_upper, 0 < k - Thus j <= k by LESS_EQ_TRANS - This contradicts k < j. - Only-if part: 0 < k /\ k <= m /\ !j. k < j /\ j <= m ==> ROOT j n ** j <> n ==> - power_index n m = power_index n k - True by power_index_no_exact_roots -*) -val power_index_equal = store_thm( - "power_index_equal", - ``!m n k. 0 < k /\ k <= m ==> - ((power_index n m = power_index n k) <=> (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n))``, - rpt strip_tac >> - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `j <= power_index n m` by rw[power_index_lower] >> - `power_index n k <= k` by rw[power_index_upper] >> - decide_tac, - rw[power_index_no_exact_roots] - ]); - -(* Theorem: (power_index n m = k) ==> !j. k < j /\ j <= m ==> (ROOT j n) ** j <> n *) -(* Proof: - By contradiction, suppose k < j /\ j <= m /\ (ROOT j n) ** j = n. - Then j <= power_index n m by power_index_lower - This contradicts power_index n m = k < j by given -*) -val power_index_property = store_thm( - "power_index_property", - ``!m n k. (power_index n m = k) ==> !j. k < j /\ j <= m ==> (ROOT j n) ** j <> n``, - spose_not_then strip_assume_tac >> - `j <= power_index n m` by rw[power_index_lower] >> - decide_tac); - -(* Theorem: power_free n <=> (1 < n) /\ (power_index n (LOG2 n) = 1) *) -(* Proof: - By power_free_check_upto_LOG2, power_free_upto_def, this is to show: - 1 < n /\ (!j. 1 < j /\ j <= LOG2 n ==> ROOT j n ** j <> n) <=> - 1 < n /\ (power_index n (LOG2 n) = 1) - If part: - Note 0 < LOG2 n by LOG2_POS, 1 < n - power_index n (LOG2 n) - = power_index n 1 by power_index_no_exact_roots, 1 <= LOG2 n - = 1 by power_index_1 - Only-if part, true by power_index_property -*) -val power_free_by_power_index_LOG2 = store_thm( - "power_free_by_power_index_LOG2", - ``!n. power_free n <=> (1 < n) /\ (power_index n (LOG2 n) = 1)``, - rw[power_free_check_upto_LOG2, power_free_upto_def] >> - rw[EQ_IMP_THM] >| [ - `0 < LOG2 n` by rw[] >> - `1 <= LOG2 n` by decide_tac >> - `power_index n (LOG2 n) = power_index n 1` by rw[power_index_no_exact_roots] >> - rw[power_index_1], - metis_tac[power_index_property] - ]); - -(* Theorem: power_free n <=> (1 < n) /\ (power_index n (ulog n) = 1) *) -(* Proof: - By power_free_check_upto_ulog, power_free_upto_def, this is to show: - 1 < n /\ (!j. 1 < j /\ j <= ulog n ==> ROOT j n ** j <> n) <=> - 1 < n /\ (power_index n (ulog n) = 1) - If part: - Note 0 < ulog n by ulog_POS, 1 < n - power_index n (ulog n) - = power_index n 1 by power_index_no_exact_roots, 1 <= ulog n - = 1 by power_index_1 - Only-if part, true by power_index_property -*) -val power_free_by_power_index_ulog = store_thm( - "power_free_by_power_index_ulog", - ``!n. power_free n <=> (1 < n) /\ (power_index n (ulog n) = 1)``, - rw[power_free_check_upto_ulog, power_free_upto_def] >> - rw[EQ_IMP_THM] >| [ - `0 < ulog n` by rw[] >> - `1 <= ulog n` by decide_tac >> - `power_index n (ulog n) = power_index n 1` by rw[power_index_no_exact_roots] >> - rw[power_index_1], - metis_tac[power_index_property] - ]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/primePowerScript.sml b/examples/algebra/lib/primePowerScript.sml deleted file mode 100644 index fcb25f16be..0000000000 --- a/examples/algebra/lib/primePowerScript.sml +++ /dev/null @@ -1,4150 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Prime Power *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "primePower"; - -(* ------------------------------------------------------------------------- *) - - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories in lib *) -(* val _ = load "logPowerTheory"; *) -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open logPowerTheory; - -(* val _ = load "triangleTheory"; *) -open triangleTheory; (* for list_lcm, set_lcm *) -open helperListTheory; -open listRangeTheory; -open rich_listTheory; - -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -open EulerTheory; (* for natural_finite *) -open logrootTheory; (* for LOG *) -open optionTheory; (* for Consecutive LCM Function *) - - -(* ------------------------------------------------------------------------- *) -(* Prime Power Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - ppidx n = prime_power_index p n - common_prime_divisors m n = (prime_divisors m) INTER (prime_divisors n) - total_prime_divisors m n = (prime_divisors m) UNION (prime_divisors n) - park_on m n = {p | p IN common_prime_divisors m n /\ ppidx m <= ppidx n} - park_off m n = {p | p IN common_prime_divisors m n /\ ppidx n < ppidx m} - park m n = PROD_SET (IMAGE (\p. p ** ppidx m) (park_on m n)) -*) -(* Definitions and Theorems (# are exported): - - Helper Theorem: - self_to_log_index_member |- !n x. MEM x [1 .. n] ==> MEM (x ** LOG x n) [1 .. n] - - Prime Power or Coprime Factors: - prime_power_or_coprime_factors |- !n. 1 < n ==> (?p k. 0 < k /\ prime p /\ (n = p ** k)) \/ - ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n /\ b < n - non_prime_power_coprime_factors |- !n. 1 < n /\ ~(?p k. 0 < k /\ prime p /\ (n = p ** k)) ==> - ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ a < n /\ 1 < b /\ b < n - pairwise_coprime_for_prime_powers |- !s f. s SUBSET prime ==> PAIRWISE_COPRIME (IMAGE (\p. p ** f p) s) - - Prime Power Index: - prime_power_index_exists |- !n p. 0 < n /\ prime p ==> ?m. p ** m divides n /\ coprime p (n DIV p ** m) - prime_power_index_def |- !p n. 0 < n /\ prime p ==> - p ** ppidx n divides n /\ coprime p (n DIV p ** ppidx n) - prime_power_factor_divides |- !n p. prime p ==> p ** ppidx n divides n - prime_power_cofactor_coprime |- !n p. 0 < n /\ prime p ==> coprime p (n DIV p ** ppidx n) - prime_power_eqn |- !n p. 0 < n /\ prime p ==> (n = p ** ppidx n * (n DIV p ** ppidx n)) - prime_power_divisibility |- !n p. 0 < n /\ prime p ==> !k. p ** k divides n <=> k <= ppidx n - prime_power_index_maximal |- !n p. 0 < n /\ prime p ==> !k. k > ppidx n ==> ~(p ** k divides n) - prime_power_index_of_divisor |- !m n. 0 < n /\ m divides n ==> !p. prime p ==> ppidx m <= ppidx n - prime_power_index_test |- !n p. 0 < n /\ prime p ==> - !k. (k = ppidx n) <=> ?q. (n = p ** k * q) /\ coprime p q: - prime_power_index_1 |- !p. prime p ==> (ppidx 1 = 0) - prime_power_index_eq_0 |- !n p. 0 < n /\ prime p /\ ~(p divides n) ==> (ppidx n = 0) - prime_power_index_prime_power |- !p. prime p ==> !k. ppidx (p ** k) = k - prime_power_index_prime |- !p. prime p ==> (ppidx p = 1) - prime_power_index_eqn |- !n p. 0 < n /\ prime p ==> (let q = n DIV p ** ppidx n in - (n = p ** ppidx n * q) /\ coprime p q) - prime_power_index_pos |- !n p. 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n - - Prime Power and GCD, LCM: - gcd_prime_power_factor |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - (gcd a b = p ** MIN (ppidx a) (ppidx b) * gcd (a DIV p ** ppidx a) (b DIV p ** ppidx b)) - gcd_prime_power_factor_divides_gcd - |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - p ** MIN (ppidx a) (ppidx b) divides gcd a b - gcd_prime_power_cofactor_coprime - |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - coprime p (gcd (a DIV p ** ppidx a) (b DIV p ** ppidx b)) - gcd_prime_power_index |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - (ppidx (gcd a b) = MIN (ppidx a) (ppidx b)) - gcd_prime_power_divisibility |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - !k. p ** k divides gcd a b ==> k <= MIN (ppidx a) (ppidx b) - - lcm_prime_power_factor |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - (lcm a b = p ** MAX (ppidx a) (ppidx b) * lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b)) - lcm_prime_power_factor_divides_lcm - |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - p ** MAX (ppidx a) (ppidx b) divides lcm a b - lcm_prime_power_cofactor_coprime - |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - coprime p (lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b)) - lcm_prime_power_index |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - (ppidx (lcm a b) = MAX (ppidx a) (ppidx b)) - lcm_prime_power_divisibility |- !a b p. 0 < a /\ 0 < b /\ prime p ==> - !k. p ** k divides lcm a b ==> k <= MAX (ppidx a) (ppidx b) - - Prime Powers and List LCM: - list_lcm_prime_power_factor_divides |- !l p. prime p ==> p ** MAX_LIST (MAP ppidx l) divides list_lcm l - list_lcm_prime_power_index |- !l p. prime p /\ POSITIVE l ==> - (ppidx (list_lcm l) = MAX_LIST (MAP ppidx l)) - list_lcm_prime_power_divisibility |- !l p. prime p /\ POSITIVE l ==> - !k. p ** k divides list_lcm l ==> k <= MAX_LIST (MAP ppidx l) - list_lcm_prime_power_factor_member |- !l p. prime p /\ l <> [] /\ POSITIVE l ==> - !k. p ** k divides list_lcm l ==> ?x. MEM x l /\ p ** k divides x - lcm_special_for_prime_power |- !p. prime p ==> !m n. (n = p ** SUC (ppidx m)) ==> (lcm n m = p * m) - lcm_special_for_coprime_factors |- !n a b. (n = a * b) /\ coprime a b ==> - !m. a divides m /\ b divides m ==> (lcm n m = m) - - Prime Divisors: - prime_divisors_def |- !n. prime_divisors n = {p | prime p /\ p divides n} - prime_divisors_element |- !n p. p IN prime_divisors n <=> prime p /\ p divides n - prime_divisors_subset_natural |- !n. 0 < n ==> prime_divisors n SUBSET natural n - prime_divisors_finite |- !n. 0 < n ==> FINITE (prime_divisors n) - prime_divisors_0 |- prime_divisors 0 = prime - prime_divisors_1 |- prime_divisors 1 = {} - prime_divisors_subset_prime |- !n. prime_divisors n SUBSET prime - prime_divisors_nonempty |- !n. 1 < n ==> prime_divisors n <> {} - prime_divisors_empty_iff |- !n. (prime_divisors n = {}) <=> (n = 1) - prime_divisors_0_not_sing |- ~SING (prime_divisors 0) - prime_divisors_prime |- !n. prime n ==> (prime_divisors n = {n}) - prime_divisors_prime_power |- !n. prime n ==> !k. 0 < k ==> (prime_divisors (n ** k) = {n}) - prime_divisors_sing |- !n. SING (prime_divisors n) <=> ?p k. prime p /\ 0 < k /\ (n = p ** k) - prime_divisors_sing_alt |- !n p. (prime_divisors n = {p}) <=> ?k. prime p /\ 0 < k /\ (n = p ** k) - prime_divisors_sing_property |- !n. SING (prime_divisors n) ==> (let p = CHOICE (prime_divisors n) in - prime p /\ (n = p ** ppidx n)) - prime_divisors_divisor_subset |- !m n. m divides n ==> prime_divisors m SUBSET prime_divisors n - prime_divisors_common_divisor |- !n m x. x divides m /\ x divides n ==> - prime_divisors x SUBSET prime_divisors m INTER prime_divisors n - prime_divisors_common_multiple |- !n m x. m divides x /\ n divides x ==> - prime_divisors m UNION prime_divisors n SUBSET prime_divisors x - prime_power_index_common_divisor |- !n m x. 0 < m /\ 0 < n /\ x divides m /\ x divides n ==> - !p. prime p ==> ppidx x <= MIN (ppidx m) (ppidx n) - prime_power_index_common_multiple |- !n m x. 0 < x /\ m divides x /\ n divides x ==> - !p. prime p ==> MAX (ppidx m) (ppidx n) <= ppidx x - prime_power_index_le_log_index |- !n p. 0 < n /\ prime p ==> ppidx n <= LOG p n - - Prime-related Sets: - primes_upto_def |- !n. primes_upto n = {p | prime p /\ p <= n} - prime_powers_upto_def |- !n. prime_powers_upto n = IMAGE (\p. p ** LOG p n) (primes_upto n) - prime_power_divisors_def |- !n. prime_power_divisors n = IMAGE (\p. p ** ppidx n) (prime_divisors n) - - primes_upto_element |- !n p. p IN primes_upto n <=> prime p /\ p <= n - primes_upto_subset_natural |- !n. primes_upto n SUBSET natural n - primes_upto_finite |- !n. FINITE (primes_upto n) - primes_upto_pairwise_coprime |- !n. PAIRWISE_COPRIME (primes_upto n) - primes_upto_0 |- primes_upto 0 = {} - primes_count_0 |- primes_count 0 = 0 - primes_upto_1 |- primes_upto 1 = {} - primes_count_1 |- primes_count 1 = 0 - - prime_powers_upto_element |- !n x. x IN prime_powers_upto n <=> - ?p. (x = p ** LOG p n) /\ prime p /\ p <= n - prime_powers_upto_element_alt |- !p n. prime p /\ p <= n ==> p ** LOG p n IN prime_powers_upto n - prime_powers_upto_finite |- !n. FINITE (prime_powers_upto n) - prime_powers_upto_pairwise_coprime |- !n. PAIRWISE_COPRIME (prime_powers_upto n) - prime_powers_upto_0 |- prime_powers_upto 0 = {} - prime_powers_upto_1 |- prime_powers_upto 1 = {} - - prime_power_divisors_element |- !n x. x IN prime_power_divisors n <=> - ?p. (x = p ** ppidx n) /\ prime p /\ p divides n - prime_power_divisors_element_alt |- !p n. prime p /\ p divides n ==> - p ** ppidx n IN prime_power_divisors n - prime_power_divisors_finite |- !n. 0 < n ==> FINITE (prime_power_divisors n) - prime_power_divisors_pairwise_coprime |- !n. PAIRWISE_COPRIME (prime_power_divisors n) - prime_power_divisors_1 |- prime_power_divisors 1 = {} - - Factorisations: - prime_factorisation |- !n. 0 < n ==> (n = PROD_SET (prime_power_divisors n)) - basic_prime_factorisation |- !n. 0 < n ==> - (n = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n))) - divisor_prime_factorisation |- !m n. 0 < n /\ m divides n ==> - (m = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors n))) - gcd_prime_factorisation |- !m n. 0 < m /\ 0 < n ==> - (gcd m n = PROD_SET (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) - (prime_divisors m INTER prime_divisors n))) - lcm_prime_factorisation |- !m n. 0 < m /\ 0 < n ==> - (lcm m n = PROD_SET (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) - (prime_divisors m UNION prime_divisors n))) - - GCD and LCM special coprime decompositions: - common_prime_divisors_element |- !m n p. p IN common_prime_divisors m n <=> - p IN prime_divisors m /\ p IN prime_divisors n - common_prime_divisors_finite |- !m n. 0 < m /\ 0 < n ==> FINITE (common_prime_divisors m n) - common_prime_divisors_pairwise_coprime |- !m n. PAIRWISE_COPRIME (common_prime_divisors m n) - common_prime_divisors_min_image_pairwise_coprime - |- !m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n)) - total_prime_divisors_element |- !m n p. p IN total_prime_divisors m n <=> - p IN prime_divisors m \/ p IN prime_divisors n - total_prime_divisors_finite |- !m n. 0 < m /\ 0 < n ==> FINITE (total_prime_divisors m n) - total_prime_divisors_pairwise_coprime |- !m n. PAIRWISE_COPRIME (total_prime_divisors m n) - total_prime_divisors_max_image_pairwise_coprime - |- !m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n)) - - park_on_element |- !m n p. p IN park_on m n <=> - p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n - park_off_element |- !m n p. p IN park_off m n <=> - p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m - park_off_alt |- !m n. park_off m n = common_prime_divisors m n DIFF park_on m n - park_on_subset_common |- !m n. park_on m n SUBSET common_prime_divisors m n - park_off_subset_common |- !m n. park_off m n SUBSET common_prime_divisors m n - park_on_subset_total |- !m n. park_on m n SUBSET total_prime_divisors m n - park_off_subset_total |- !m n. park_off m n SUBSET total_prime_divisors m n - park_on_off_partition |- !m n. common_prime_divisors m n =|= park_on m n # park_off m n - park_off_image_has_not_1 |- !m n. 1 NOTIN IMAGE (\p. p ** ppidx m) (park_off m n) - - park_on_off_common_image_partition - |- !m n. (let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n) in - let u = IMAGE (\p. p ** ppidx m) (park_on m n) in - let v = IMAGE (\p. p ** ppidx n) (park_off m n) in - 0 < m ==> s =|= u # v) - gcd_park_decomposition |- !m n. 0 < m /\ 0 < n ==> - (let a = mypark m n in let b = gcd m n DIV a in - (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ - (gcd m n = a * b) /\ coprime a b) - gcd_park_decompose |- !m n. 0 < m /\ 0 < n ==> - (let a = mypark m n in let b = gcd m n DIV a in - (gcd m n = a * b) /\ coprime a b) - - park_on_off_total_image_partition - |- !m n. (let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n) in - let u = IMAGE (\p. p ** ppidx m) (prime_divisors m DIFF park_on m n) in - let v = IMAGE (\p. p ** ppidx n) (prime_divisors n DIFF park_off m n) in - 0 < m /\ 0 < n ==> s =|= u # v) - lcm_park_decomposition |- !m n. 0 < m /\ 0 < n ==> - (let a = park m n in let b = gcd m n DIV a in - let p = m DIV a in let q = a * n DIV gcd m n in - (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ - (p = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors m DIFF park_on m n))) /\ - (q = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n DIFF park_off m n))) /\ - (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q)) - lcm_park_decompose |- !m n. 0 < m /\ 0 < n ==> - (let a = park m n in let p = m DIV a in let q = a * n DIV gcd m n in - (lcm m n = p * q) /\ coprime p q) - lcm_gcd_park_decompose |- !m n. 0 < m /\ 0 < n ==> - (let a = park m n in let b = gcd m n DIV a in - let p = m DIV a in let q = a * n DIV gcd m n in - (lcm m n = p * q) /\ coprime p q /\ - (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q)) - - Consecutive LCM Recurrence: - lcm_fun_def |- (lcm_fun 0 = 1) /\ - !n. lcm_fun (SUC n) = if n = 0 then 1 else - case some p. ?k. 0 < k /\ prime p /\ (SUC n = p ** k) of - NONE => lcm_fun n - | SOME p => p * lcm_fun n - lcm_fun_0 |- lcm_fun 0 = 1 - lcm_fun_SUC |- !n. lcm_fun (SUC n) = if n = 0 then 1 else - case some p. ?k. 0 < k /\ prime p /\ (SUC n = p ** k) of - NONE => lcm_fun n - | SOME p => p * lcm_fun n - lcm_fun_1 |- lcm_fun 1 = 1 - lcm_fun_2 |- lcm_fun 2 = 2 - lcm_fun_suc_some |- !n p. prime p /\ (?k. 0 < k /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = p * lcm_fun n) - lcm_fun_suc_none |- !n. ~(?p k. 0 < k /\ prime p /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = lcm_fun n) - list_lcm_prime_power_index_lower |- !l p. prime p /\ l <> [] /\ POSITIVE l ==> - !x. MEM x l ==> ppidx x <= ppidx (list_lcm l) - list_lcm_with_last_prime_power |- !n p k. prime p /\ (n + 2 = p ** k) ==> - (list_lcm [1 .. n + 2] = p * list_lcm (leibniz_vertical n)) - list_lcm_with_last_non_prime_power |- !n. (!p k. (k = 0) \/ ~prime p \/ n + 2 <> p ** k) ==> - (list_lcm [1 .. n + 2] = list_lcm (leibniz_vertical n)) - list_lcm_eq_lcm_fun |- !n. list_lcm (leibniz_vertical n) = lcm_fun (n + 1) - lcm_fun_lower_bound |- !n. 2 ** n <= lcm_fun (n + 1) - lcm_fun_lower_bound_alt |- !n. 0 < n ==> 2 ** (n - 1) <= lcm_fun n - prime_power_index_suc_special |- !n p. 0 < n /\ prime p /\ (SUC n = p ** ppidx (SUC n)) ==> - (ppidx (SUC n) = SUC (ppidx (list_lcm [1 .. n]))) - prime_power_index_suc_property |- !n p. 0 < n /\ prime p /\ (n + 1 = p ** ppidx (n + 1)) ==> - (ppidx (n + 1) = 1 + ppidx (list_lcm [1 .. n])) - - Consecutive LCM Recurrence - Rework: - list_lcm_by_last_prime_power |- !n. SING (prime_divisors (n + 1)) ==> - (list_lcm [1 .. (n + 1)] = CHOICE (prime_divisors (n + 1)) * list_lcm [1 .. n]) - list_lcm_by_last_non_prime_power |- !n. ~SING (prime_divisors (n + 1)) ==> - (list_lcm (leibniz_vertical n) = list_lcm [1 .. n]) - list_lcm_recurrence |- !n. list_lcm (leibniz_vertical n) = (let s = prime_divisors (n + 1) in - if SING s then CHOICE s * list_lcm [1 .. n] else list_lcm [1 .. n]) - list_lcm_option_last_prime_power |- !n p. (prime_divisors (n + 1) = {p}) ==> - (list_lcm (leibniz_vertical n) = p * list_lcm [1 .. n]) - list_lcm_option_last_non_prime_power |- !n. (!p. prime_divisors (n + 1) <> {p}) ==> - (list_lcm (leibniz_vertical n) = list_lcm [1 .. n]) - list_lcm_option_recurrence |- !n. list_lcm (leibniz_vertical n) = - case some p. prime_divisors (n + 1) = {p} of - NONE => list_lcm [1 .. n] - | SOME p => p * list_lcm [1 .. n] - - Relating Consecutive LCM to Prime Functions: - Theorems on Prime-related Sets: - prime_powers_upto_list_mem |- !n x. MEM x (SET_TO_LIST (prime_powers_upto n)) <=> - ?p. (x = p ** LOG p n) /\ prime p /\ p <= n - prime_powers_upto_lcm_prime_to_log_divisor - |- !n p. prime p /\ p <= n ==> - p ** LOG p n divides set_lcm (prime_powers_upto n) - prime_powers_upto_lcm_prime_divisor - |- !n p. prime p /\ p <= n ==> p divides set_lcm (prime_powers_upto n) - prime_powers_upto_lcm_prime_to_power_divisor - |- !n p. prime p /\ p <= n ==> - p ** ppidx n divides set_lcm (prime_powers_upto n) - prime_powers_upto_lcm_divisor |- !n x. 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n) - - Consecutive LCM and Prime-related Sets: - lcm_run_eq_set_lcm_prime_powers |- !n. lcm_run n = set_lcm (prime_powers_upto n) - set_lcm_prime_powers_upto_eqn |- !n. set_lcm (prime_powers_upto n) = PROD_SET (prime_powers_upto n) - lcm_run_eq_prod_set_prime_powers |- !n. lcm_run n = PROD_SET (prime_powers_upto n) - prime_powers_upto_prod_set_le |- !n. PROD_SET (prime_powers_upto n) <= n ** primes_count n - lcm_run_upper_by_primes_count |- !n. lcm_run n <= n ** primes_count n - prime_powers_upto_prod_set_ge |- !n. PROD_SET (primes_upto n) <= PROD_SET (prime_powers_upto n) - lcm_run_lower_by_primes_product |- !n. PROD_SET (primes_upto n) <= lcm_run n - prime_powers_upto_prod_set_mix_ge |- !n. n ** primes_count n <= - PROD_SET (primes_upto n) * PROD_SET (prime_powers_upto n) - primes_count_upper_by_product |- !n. n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n - primes_count_upper_by_lcm_run |- !n. n ** primes_count n <= lcm_run n ** 2 - lcm_run_lower_by_primes_count |- !n. SQRT (n ** primes_count n) <= lcm_run n -*) - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MEM x [1 .. n] ==> MEM (x ** LOG x n) [1 .. n] *) -(* Proof: - By listRangeINC_MEM, this is to show: - (1) 1 <= x /\ x <= n ==> 1 <= x ** LOG x n - Note 0 < x by 1 <= x - so 0 < x ** LOG x n by EXP_POS, 0 < x - or 1 <= x ** LOG x n by arithmetic - (2) 1 <= x /\ x <= n ==> x ** LOG x n <= n - Note 1 <= n /\ 0 < n by arithmetic - If x = 1, - Then true by EXP_1 - If x <> 1, - Then 1 < x, so true by LOG -*) -Theorem self_to_log_index_member: - !n x. MEM x [1 .. n] ==> MEM (x ** LOG x n) [1 .. n] -Proof - rw[listRangeINC_MEM] >> - ‘0 < n /\ 1 <= n’ by decide_tac >> - Cases_on ‘x = 1’ >- - rw[EXP_1] >> rw[LOG] -QED - -(* ------------------------------------------------------------------------- *) -(* Prime Power or Coprime Factors *) -(* ------------------------------------------------------------------------- *) - -(* -The concept of a prime number goes like this: -* Given a number n > 1, it has factors n = a * b. - Either there are several possibilities, or there is just the trivial: 1 * n and n * 1. - A number with only trivial factors is a prime, otherwise it is a composite. -The concept of a prime power number goes like this: -* Given a number n > 1, it has factors n = a * b. - Either a and b can be chosen to be coprime, or this choice is impossible. - A number that cannot have coprime factors is a prime power, otherwise a coprime product. -*) - -(* Theorem: 1 < n ==> (?p k. 0 < k /\ prime p /\ (n = p ** k)) \/ - (?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n /\ b < n) *) -(* Proof: - Note 1 < n ==> 0 < n /\ n <> 0 /\ n <> 1 by arithmetic - Now ?p. prime p /\ p divides n by PRIME_FACTOR, n <> 1 - so ?k. 0 < k /\ p ** k divides n /\ - coprime p (n DIV p ** k) by FACTOR_OUT_PRIME, EXP_1, 0 < n - Note 0 < p by PRIME_POS - so 0 < p ** k by EXP_POS - Let b = n DIV p ** k. - Then n = (p ** k) * b by DIVIDES_EQN_COMM, 0 < p ** m - - If b = 1, - Then n = p ** k by MULT_RIGHT_1 - Take k for the first assertion. - If b <> 1, - Let a = p ** k. - Then coprime a b by coprime_exp, coprime p b - Since p <> 1 by NOT_PRIME_1 - so a <> 1 by EXP_EQ_1 - and b <> 0 by MULT_0 - Now a divides n /\ b divides n by divides_def, MULT_COMM - so a <= n /\ b <= n by DIVIDES_LE, 0 < n - but a <> n /\ b <> n by MULT_LEFT_ID, MULT_RIGHT_ID - Thus 1 < a /\ 1 < b /\ a < n /\ b < n by arithmetic - Take a, b for the second assertion. -*) -val prime_power_or_coprime_factors = store_thm( - "prime_power_or_coprime_factors", - ``!n. 1 < n ==> (?p k. 0 < k /\ prime p /\ (n = p ** k)) \/ - (?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n /\ b < n)``, - rpt strip_tac >> - `0 < n /\ n <> 0 /\ n <> 1` by decide_tac >> - `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> - `?k. 0 < k /\ p ** k divides n /\ coprime p (n DIV p ** k)` by metis_tac[FACTOR_OUT_PRIME, EXP_1] >> - `0 < p ** k` by rw[PRIME_POS, EXP_POS] >> - qabbrev_tac `b = n DIV p ** k` >> - `n = (p ** k) * b` by rw[GSYM DIVIDES_EQN_COMM, Abbr`b`] >> - Cases_on `b = 1` >- - metis_tac[MULT_RIGHT_1] >> - qabbrev_tac `a = p ** k` >> - `coprime a b` by rw[coprime_exp, Abbr`a`] >> - `a <> 1` by metis_tac[EXP_EQ_1, NOT_PRIME_1, NOT_ZERO_LT_ZERO] >> - `b <> 0` by metis_tac[MULT_0] >> - `a divides n /\ b divides n` by metis_tac[divides_def, MULT_COMM] >> - `a <= n /\ b <= n` by rw[DIVIDES_LE] >> - `a <> n /\ b <> n` by metis_tac[MULT_LEFT_ID, MULT_RIGHT_ID] >> - `1 < a /\ 1 < b /\ a < n /\ b < n` by decide_tac >> - metis_tac[]); - -(* The following is the more powerful version of this: - Simple theorem: If n is not a prime, then ?a b. (n = a * b) /\ 1 < a /\ a < n /\ 1 < b /\ b < n - Powerful theorem: If n is not a prime power, then ?a b. (n = a * b) /\ 1 < a /\ a < n /\ 1 < b /\ b < n -*) - -(* Theorem: 1 < n /\ ~(?p k. 0 < k /\ prime p /\ (n = p ** k)) ==> - ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ a < n /\ 1 < b /\ b < n *) -(* Proof: - Since 1 < n, n <> 1 and 0 < n by arithmetic - Note ?p. prime p /\ p divides n by PRIME_FACTOR, n <> 1 - and ?m. 0 < m /\ p ** m divides n /\ - !k. coprime (p ** k) (n DIV p ** m) by FACTOR_OUT_PRIME, 0 < n - - Let a = p ** m, b = n DIV a. - Since 0 < p by PRIME_POS - so 0 < a by EXP_POS, 0 < p - Thus n = a * b by DIVIDES_EQN_COMM, 0 < a - - Also 1 < p by ONE_LT_PRIME - so 1 < a by ONE_LT_EXP, 1 < p, 0 < m - and b < n by DIV_LESS, Abbr, 0 < n - Now b <> 1 by MULT_RIGHT_1, n not perfect power of any prime - and b <> 0 by MULT_0, n = a * b, 0 < n - ==> 1 < b by b <> 0 /\ b <> 1 - Also a <= n by DIVIDES_LE - and a <> n by MULT_RIGHT_1 - ==> a < n by arithmetic - Take these a and b, the result follows. -*) -val non_prime_power_coprime_factors = store_thm( - "non_prime_power_coprime_factors", - ``!n. 1 < n /\ ~(?p k. 0 < k /\ prime p /\ (n = p ** k)) ==> - ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ a < n /\ 1 < b /\ b < n``, - rpt strip_tac >> - `0 < n` by decide_tac >> - `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> - `?m. 0 < m /\ p ** m divides n /\ !k. coprime (p ** k) (n DIV p ** m)` by rw[FACTOR_OUT_PRIME] >> - qabbrev_tac `a = p ** m` >> - qabbrev_tac `b = n DIV a` >> - `0 < a` by rw[PRIME_POS, EXP_POS, Abbr`a`] >> - `n = a * b` by metis_tac[DIVIDES_EQN_COMM] >> - `1 < a` by rw[ONE_LT_EXP, ONE_LT_PRIME, Abbr`a`] >> - `b < n` by rw[DIV_LESS, Abbr`b`] >> - `b <> 1` by metis_tac[MULT_RIGHT_1] >> - `b <> 0` by metis_tac[MULT_0, NOT_ZERO_LT_ZERO] >> - `1 < b` by decide_tac >> - `a <= n` by rw[DIVIDES_LE] >> - `a <> n` by metis_tac[MULT_RIGHT_1] >> - `a < n` by decide_tac >> - metis_tac[]); - -(* Theorem: s SUBSET prime ==> PAIRWISE_COPRIME (IMAGE (\p. p ** f p) s) *) -(* Proof: - By SUBSET_DEF, this is to show: - (!x. x IN s ==> prime x) /\ p IN s /\ p' IN s /\ p ** f <> p' ** f ==> coprime (p ** f) (p' ** f) - Note prime p /\ prime p' by in_prime - and p <> p' by p ** f <> p' ** f - Thus coprime (p ** f) (p' ** f) by prime_powers_coprime -*) -val pairwise_coprime_for_prime_powers = store_thm( - "pairwise_coprime_for_prime_powers", - ``!s f. s SUBSET prime ==> PAIRWISE_COPRIME (IMAGE (\p. p ** f p) s)``, - rw[SUBSET_DEF] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[in_prime] >> - rw[prime_powers_coprime]); - -(* ------------------------------------------------------------------------- *) -(* Prime Power Index *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < n /\ prime p ==> ?m. (p ** m) divides n /\ coprime p (n DIV (p ** m)) *) -(* Proof: - Note ?q m. (n = (p ** m) * q) /\ coprime p q by prime_power_factor - Let t = p ** m. - Then t divides n by divides_def, MULT_COMM - Now 0 < p by PRIME_POS - so 0 < t by EXP_POS - ==> n = t * (n DIV t) by DIVIDES_EQN_COMM - Thus q = n DIV t by MULT_LEFT_CANCEL - Take this m, and the result follows. -*) -val prime_power_index_exists = store_thm( - "prime_power_index_exists", - ``!n p. 0 < n /\ prime p ==> ?m. (p ** m) divides n /\ coprime p (n DIV (p ** m))``, - rpt strip_tac >> - `?q m. (n = (p ** m) * q) /\ coprime p q` by rw[prime_power_factor] >> - qabbrev_tac `t = p ** m` >> - `t divides n` by metis_tac[divides_def, MULT_COMM] >> - `0 < t` by rw[PRIME_POS, EXP_POS, Abbr`t`] >> - metis_tac[DIVIDES_EQN_COMM, MULT_LEFT_CANCEL, NOT_ZERO_LT_ZERO]); - -(* Apply Skolemization *) -val lemma = prove( - ``!p n. ?m. 0 < n /\ prime p ==> (p ** m) divides n /\ coprime p (n DIV (p ** m))``, - metis_tac[prime_power_index_exists]); -(* Note !p n, for first parameter p, second parameter n. *) -(* -- SKOLEM_THM; -> val it = |- !P. (!x. ?y. P x y) <=> ?f. !x. P x (f x) : thm -*) -(* Define prime power index *) -(* -- SIMP_RULE bool_ss [SKOLEM_THM] lemma; -> val it = |- ?f. !p n. 0 < n /\ prime p ==> p ** f p n divides n /\ coprime p (n DIV p ** f p n): thm -*) -val prime_power_index_def = new_specification( - "prime_power_index_def", - ["prime_power_index"], - SIMP_RULE bool_ss [SKOLEM_THM] lemma); -(* -> val prime_power_index_def = |- !p n. 0 < n /\ prime p ==> - p ** prime_power_index p n divides n /\ coprime p (n DIV p ** prime_power_index p n): thm -*) - -(* Overload on prime_power_index of prime p *) -val _ = overload_on("ppidx", ``prime_power_index p``); - -(* -> prime_power_index_def; -val it = |- !p n. 0 < n /\ prime p ==> p ** ppidx n divides n /\ coprime p (n DIV p ** ppidx n): thm -*) - -(* Theorem: prime p ==> (p ** (ppidx n)) divides n *) -(* Proof: by prime_power_index_def, and ALL_DIVIDES_0 *) -val prime_power_factor_divides = store_thm( - "prime_power_factor_divides", - ``!n p. prime p ==> (p ** (ppidx n)) divides n``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - rw[prime_power_index_def]); - -(* Theorem: 0 < n /\ prime p ==> coprime p (n DIV p ** ppidx n) *) -(* Proof: by prime_power_index_def *) -val prime_power_cofactor_coprime = store_thm( - "prime_power_cofactor_coprime", - ``!n p. 0 < n /\ prime p ==> coprime p (n DIV p ** ppidx n)``, - rw[prime_power_index_def]); - -(* Theorem: 0 < n /\ prime p ==> (n = p ** (ppidx n) * (n DIV p ** (ppidx n))) *) -(* Proof: - Let q = p ** (ppidx n). - Then q divides n by prime_power_factor_divides - But 0 < n ==> n <> 0, - so q <> 0, or 0 < q by ZERO_DIVIDES - Thus n = q * (n DIV q) by DIVIDES_EQN_COMM, 0 < q -*) -val prime_power_eqn = store_thm( - "prime_power_eqn", - ``!n p. 0 < n /\ prime p ==> (n = p ** (ppidx n) * (n DIV p ** (ppidx n)))``, - rpt strip_tac >> - qabbrev_tac `q = p ** (ppidx n)` >> - `q divides n` by rw[prime_power_factor_divides, Abbr`q`] >> - `0 < q` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - rw[GSYM DIVIDES_EQN_COMM]); - -(* Theorem: 0 < n /\ prime p ==> !k. (p ** k) divides n <=> k <= (ppidx n) *) -(* Proof: - Let m = ppidx n. - Then p ** m divides n by prime_power_factor_divides - If part: p ** k divides n ==> k <= m - By contradiction, suppose m < k. - Let q = n DIV p ** m. - Then n = p ** m * q by prime_power_eqn - ==> ?t. n = p ** k * t by divides_def, MULT_COMM - Let d = k - m. - Then 0 < d by m < k - ==> p ** k = p ** m * p ** d by EXP_BY_ADD_SUB_LT - But 0 < p ** m by PRIME_POS, EXP_POS - so p ** m <> 0 by arithmetic - Thus q = p ** d * t by MULT_LEFT_CANCEL, MULT_ASSOC - Since p divides p ** d by prime_divides_self_power, 0 < d - so p divides q by DIVIDES_MULT - or gcd p q = p by divides_iff_gcd_fix - But coprime p q by prime_power_cofactor_coprime - This is a contradiction since p <> 1 by NOT_PRIME_1 - - Only-if part: k <= m ==> p ** k divides n - Note p ** m = p ** d * p ** k by EXP_BY_ADD_SUB_LE, MULT_COMM - Thus p ** k divides p ** m by divides_def - ==> p ** k divides n by DIVIDES_TRANS -*) - -Theorem prime_power_divisibility: - !n p. 0 < n /\ prime p ==> !k. (p ** k) divides n <=> k <= (ppidx n) -Proof - rpt strip_tac >> - qabbrev_tac `m = ppidx n` >> - `p ** m divides n` by rw[prime_power_factor_divides, Abbr`m`] >> - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `m < k` by decide_tac >> - qabbrev_tac `q = n DIV p ** m` >> - `n = p ** m * q` by rw[prime_power_eqn, Abbr`m`, Abbr`q`] >> - `?t. n = p ** k * t` by metis_tac[divides_def, MULT_COMM] >> - `p ** k = p ** m * p ** (k - m)` by rw[EXP_BY_ADD_SUB_LT] >> - `0 < k - m` by decide_tac >> - qabbrev_tac `d = k - m` >> - `0 < p ** m` by rw[PRIME_POS, EXP_POS] >> - `p ** m <> 0` by decide_tac >> - `q = p ** d * t` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> - `p divides p ** d` by rw[prime_divides_self_power] >> - `p divides q` by simp[DIVIDES_MULTIPLE] >> - `gcd p q = p` by rw[GSYM divides_iff_gcd_fix] >> - `coprime p q` by rw[GSYM prime_power_cofactor_coprime, Abbr`m`, Abbr`q`] >> - metis_tac[NOT_PRIME_1], - `p ** m = p ** (m - k) * p ** k` by rw[EXP_BY_ADD_SUB_LE, MULT_COMM] >> - `p ** k divides p ** m` by metis_tac[divides_def] >> - metis_tac[DIVIDES_TRANS] - ] -QED - -(* Theorem: 0 < n /\ prime p ==> !k. k > ppidx n ==> ~(p ** k divides n) *) -(* Proof: by prime_power_divisibility *) -val prime_power_index_maximal = store_thm( - "prime_power_index_maximal", - ``!n p. 0 < n /\ prime p ==> !k. k > ppidx n ==> ~(p ** k divides n)``, - rw[prime_power_divisibility]); - -(* Theorem: 0 < n /\ m divides n ==> !p. prime p ==> ppidx m <= ppidx n *) -(* Proof: - Note 0 < m by ZERO_DIVIDES, 0 < n - Thus p ** ppidx m divides m by prime_power_factor_divides, 0 < m - ==> p ** ppidx m divides n by DIVIDES_TRANS - or ppidx m <= ppidx n by prime_power_divisibility, 0 < n -*) -val prime_power_index_of_divisor = store_thm( - "prime_power_index_of_divisor", - ``!m n. 0 < n /\ m divides n ==> !p. prime p ==> ppidx m <= ppidx n``, - rpt strip_tac >> - `0 < m` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - `p ** ppidx m divides m` by rw[prime_power_factor_divides] >> - `p ** ppidx m divides n` by metis_tac[DIVIDES_TRANS] >> - rw[GSYM prime_power_divisibility]); - -(* Theorem: 0 < n /\ prime p ==> !k. (k = ppidx n) <=> (?q. (n = p ** k * q) /\ coprime p q) *) -(* Proof: - If part: k = ppidx n ==> ?q. (n = p ** k * q) /\ coprime p q - Let q = n DIV p ** k, where k = ppidx n. - Then n = p ** k * q by prime_power_eqn - and coprime p q by prime_power_cofactor_coprime - Only-if part: n = p ** k * q /\ coprime p q ==> k = ppidx n - Note n = p ** (ppidx n) * q by prime_power_eqn - - Thus p ** k divides n by divides_def, MULT_COMM - ==> k <= ppidx n by prime_power_divisibility - - Claim: ppidx n <= k - Proof: By contradiction, suppose k < ppidx n. - Let d = ppidx n - k, then 0 < d. - Let nq = n DIV p ** (ppidx n). - Then n = p ** (ppidx n) * nq by prime_power_eqn - Note p ** ppidx n = p ** k * p ** d by EXP_BY_ADD_SUB_LT - Now 0 < p ** k by PRIME_POS, EXP_POS - so q = p ** d * nq by MULT_LEFT_CANCEL, MULT_ASSOC, p ** k <> 0 - But p divides p ** d by prime_divides_self_power, 0 < d - and p ** d divides q by divides_def, MULT_COMM - ==> p divides q by DIVIDES_TRANS - or gcd p q = p by divides_iff_gcd_fix - This contradicts coprime p q as p <> 1 by NOT_PRIME_1 - - With k <= ppidx n and ppidx n <= k, k = ppidx n by LESS_EQUAL_ANTISYM -*) -val prime_power_index_test = store_thm( - "prime_power_index_test", - ``!n p. 0 < n /\ prime p ==> !k. (k = ppidx n) <=> (?q. (n = p ** k * q) /\ coprime p q)``, - rw_tac std_ss[EQ_IMP_THM] >- - metis_tac[prime_power_eqn, prime_power_cofactor_coprime] >> - qabbrev_tac `n = p ** k * q` >> - `p ** k divides n` by metis_tac[divides_def, MULT_COMM] >> - `k <= ppidx n` by rw[GSYM prime_power_divisibility] >> - `ppidx n <= k` by - (spose_not_then strip_assume_tac >> - `k < ppidx n /\ 0 < ppidx n - k` by decide_tac >> - `p ** ppidx n = p ** k * p ** (ppidx n - k)` by rw[EXP_BY_ADD_SUB_LT] >> - qabbrev_tac `d = ppidx n - k` >> - qabbrev_tac `nq = n DIV p ** (ppidx n)` >> - `n = p ** (ppidx n) * nq` by rw[prime_power_eqn, Abbr`nq`] >> - `0 < p ** k` by rw[PRIME_POS, EXP_POS] >> - `q = p ** d * nq` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC, NOT_ZERO_LT_ZERO] >> - `p divides p ** d` by rw[prime_divides_self_power] >> - `p ** d divides q` by metis_tac[divides_def, MULT_COMM] >> - `p divides q` by metis_tac[DIVIDES_TRANS] >> - `gcd p q = p` by rw[GSYM divides_iff_gcd_fix] >> - metis_tac[NOT_PRIME_1]) >> - decide_tac); - -(* Theorem: prime p ==> (ppidx 1 = 0) *) -(* Proof: - Note 1 = p ** 0 * 1 by EXP, MULT_RIGHT_1 - and coprime p 1 by GCD_1 - so ppidx 1 = 0 by prime_power_index_test, 0 < 1 -*) -val prime_power_index_1 = store_thm( - "prime_power_index_1", - ``!p. prime p ==> (ppidx 1 = 0)``, - rpt strip_tac >> - `1 = p ** 0 * 1` by rw[] >> - `coprime p 1` by rw[GCD_1] >> - metis_tac[prime_power_index_test, DECIDE``0 < 1``]); - -(* Theorem: 0 < n /\ prime p /\ ~(p divides n) ==> (ppidx n = 0) *) -(* Proof: - By contradiction, suppose ppidx n <> 0. - Then 0 < ppidx n by NOT_ZERO_LT_ZERO - Note p ** ppidx n divides n by prime_power_index_def, 0 < n - and 1 < p by ONE_LT_PRIME - so p divides p ** ppidx n by divides_self_power, 0 < n, 1 < p - ==> p divides n by DIVIDES_TRANS - This contradicts ~(p divides n). -*) -val prime_power_index_eq_0 = store_thm( - "prime_power_index_eq_0", - ``!n p. 0 < n /\ prime p /\ ~(p divides n) ==> (ppidx n = 0)``, - spose_not_then strip_assume_tac >> - `p ** ppidx n divides n` by rw[prime_power_index_def] >> - `p divides p ** ppidx n` by rw[divides_self_power, ONE_LT_PRIME] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: prime p ==> (ppidx (p ** k) = k) *) -(* Proof: - Note p ** k = p ** k * 1 by EXP, MULT_RIGHT_1 - and coprime p 1 by GCD_1 - Now 0 < p ** k by PRIME_POS, EXP_POS - so ppidx (p ** k) = k by prime_power_index_test, 0 < p ** k -*) -val prime_power_index_prime_power = store_thm( - "prime_power_index_prime_power", - ``!p. prime p ==> !k. ppidx (p ** k) = k``, - rpt strip_tac >> - `p ** k = p ** k * 1` by rw[] >> - `coprime p 1` by rw[GCD_1] >> - `0 < p ** k` by rw[PRIME_POS, EXP_POS] >> - metis_tac[prime_power_index_test]); - -(* Theorem: prime p ==> (ppidx p = 1) *) -(* Proof: - Note 0 < p by PRIME_POS - and p = p ** 1 * 1 by EXP_1, MULT_RIGHT_1 - and coprime p 1 by GCD_1 - so ppidx p = 1 by prime_power_index_test -*) -val prime_power_index_prime = store_thm( - "prime_power_index_prime", - ``!p. prime p ==> (ppidx p = 1)``, - rpt strip_tac >> - `0 < p` by rw[PRIME_POS] >> - `p = p ** 1 * 1` by rw[] >> - metis_tac[prime_power_index_test, GCD_1]); - -(* Theorem: 0 < n /\ prime p ==> let q = n DIV (p ** ppidx n) in (n = p ** ppidx n * q) /\ (coprime p q) *) -(* Proof: - This is to show: - (1) n = p ** ppidx n * q - Note p ** ppidx n divides n by prime_power_index_def - Now 0 < p by PRIME_POS - so 0 < p ** ppidx n by EXP_POS - ==> n = p ** ppidx n * q by DIVIDES_EQN_COMM, 0 < p ** ppidx n - (2) coprime p q, true by prime_power_index_def -*) -val prime_power_index_eqn = store_thm( - "prime_power_index_eqn", - ``!n p. 0 < n /\ prime p ==> let q = n DIV (p ** ppidx n) in (n = p ** ppidx n * q) /\ (coprime p q)``, - metis_tac[prime_power_index_def, PRIME_POS, EXP_POS, DIVIDES_EQN_COMM]); - -(* Theorem: 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n *) -(* Proof: - By contradiction, suppose ~(0 < ppidx n). - Then ppidx n = 0 by NOT_ZERO_LT_ZERO - Note ?q. coprime p q /\ - n = p ** ppidx n * q by prime_power_index_eqn - = p ** 0 * q by ppidx n = 0 - = 1 * q by EXP_0 - = q by MULT_LEFT_1 - But 1 < p by ONE_LT_PRIME - and coprime p q ==> ~(p divides q) by coprime_not_divides - This contradicts p divides q by p divides n, n = q -*) -val prime_power_index_pos = store_thm( - "prime_power_index_pos", - ``!n p. 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n``, - spose_not_then strip_assume_tac >> - `ppidx n = 0` by decide_tac >> - `?q. coprime p q /\ (n = p ** ppidx n * q)` by metis_tac[prime_power_index_eqn] >> - `_ = q` by rw[] >> - metis_tac[coprime_not_divides, ONE_LT_PRIME]); - -(* ------------------------------------------------------------------------- *) -(* Prime Power and GCD, LCM *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> - (gcd a b = p ** MIN (ppidx a) (ppidx b) * gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b))) *) -(* Proof: - Let ma = ppidx a, qa = a DIV p ** ma. - Let mb = ppidx b, qb = b DIV p ** mb. - Then coprime p qa by prime_power_cofactor_coprime - and coprime p qb by prime_power_cofactor_coprime - Also a = p ** ma * qa by prime_power_eqn - and b = p ** mb * qb by prime_power_eqn - - If ma < mb, let d = mb - ma. - Then p ** mb = p ** ma * p ** d by EXP_BY_ADD_SUB_LT - and coprime (p ** d) qa by coprime_exp - gcd a b - = p ** ma * gcd qa (p ** d * qb) by GCD_COMMON_FACTOR, MULT_ASSOC - = p ** ma * gcd qa qb by gcd_coprime_cancel, GCD_SYM, coprime (p ** d) qa - = p ** (MIN ma mb) * gcd qa qb by MIN_DEF - - If ~(ma < mb), let d = ma - mb. - Then p ** ma = p ** mb * p ** d by EXP_BY_ADD_SUB_LE - and coprime (p ** d) qb by coprime_exp - gcd a b - = p ** mb * gcd (p ** d * qa) qb by GCD_COMMON_FACTOR, MULT_ASSOC - = p ** mb * gcd qa qb by gcd_coprime_cancel, coprime (p ** d) qb - = p ** (MIN ma mb) * gcd qa qb by MIN_DEF -*) -val gcd_prime_power_factor = store_thm( - "gcd_prime_power_factor", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> - (gcd a b = p ** MIN (ppidx a) (ppidx b) * gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b)))``, - rpt strip_tac >> - qabbrev_tac `ma = ppidx a` >> - qabbrev_tac `qa = a DIV p ** ma` >> - qabbrev_tac `mb = ppidx b` >> - qabbrev_tac `qb = b DIV p ** mb` >> - `coprime p qa` by rw[prime_power_cofactor_coprime, Abbr`ma`, Abbr`qa`] >> - `coprime p qb` by rw[prime_power_cofactor_coprime, Abbr`mb`, Abbr`qb`] >> - `a = p ** ma * qa` by rw[prime_power_eqn, Abbr`ma`, Abbr`qa`] >> - `b = p ** mb * qb` by rw[prime_power_eqn, Abbr`mb`, Abbr`qb`] >> - Cases_on `ma < mb` >| [ - `p ** mb = p ** ma * p ** (mb - ma)` by rw[EXP_BY_ADD_SUB_LT] >> - qabbrev_tac `d = mb - ma` >> - `coprime (p ** d) qa` by rw[coprime_exp] >> - `gcd a b = p ** ma * gcd qa (p ** d * qb)` by metis_tac[GCD_COMMON_FACTOR, MULT_ASSOC] >> - `_ = p ** ma * gcd qa qb` by metis_tac[gcd_coprime_cancel, GCD_SYM] >> - rw[MIN_DEF], - `p ** ma = p ** mb * p ** (ma - mb)` by rw[EXP_BY_ADD_SUB_LE] >> - qabbrev_tac `d = ma - mb` >> - `coprime (p ** d) qb` by rw[coprime_exp] >> - `gcd a b = p ** mb * gcd (p ** d * qa) qb` by metis_tac[GCD_COMMON_FACTOR, MULT_ASSOC] >> - `_ = p ** mb * gcd qa qb` by rw[gcd_coprime_cancel] >> - rw[MIN_DEF] - ]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (p ** MIN (ppidx a) (ppidx b)) divides (gcd a b) *) -(* Proof: by gcd_prime_power_factor, divides_def *) -val gcd_prime_power_factor_divides_gcd = store_thm( - "gcd_prime_power_factor_divides_gcd", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (p ** MIN (ppidx a) (ppidx b)) divides (gcd a b)``, - prove_tac[gcd_prime_power_factor, divides_def, MULT_COMM]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> coprime p (gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b))) *) -(* Proof: - Let ma = ppidx a, qa = a DIV p ** ma. - Let mb = ppidx b, qb = b DIV p ** mb. - Then coprime p qa by prime_power_cofactor_coprime - gcd p (gcd qa qb) - = gcd (gcd p qa) qb by GCD_ASSOC - = gcd 1 qb by coprime p qa - = 1 by GCD_1 -*) -val gcd_prime_power_cofactor_coprime = store_thm( - "gcd_prime_power_cofactor_coprime", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> coprime p (gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b)))``, - rw[prime_power_cofactor_coprime, GCD_ASSOC, GCD_1]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (ppidx (gcd a b) = MIN (ppidx a) (ppidx b)) *) -(* Proof: - Let ma = ppidx a, qa = a DIV p ** ma. - Let mb = ppidx b, qb = b DIV p ** mb. - Let m = MIN ma mb. - Then gcd a b = p ** m * (gcd qa qb) by gcd_prime_power_factor - Note 0 < gcd a b by GCD_POS - and coprime p (gcd qa qb) by gcd_prime_power_cofactor_coprime - Thus ppidx (gcd a b) = m by prime_power_index_test -*) -val gcd_prime_power_index = store_thm( - "gcd_prime_power_index", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (ppidx (gcd a b) = MIN (ppidx a) (ppidx b))``, - metis_tac[gcd_prime_power_factor, GCD_POS, prime_power_index_test, gcd_prime_power_cofactor_coprime]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides gcd a b ==> k <= MIN (ppidx a) (ppidx b) *) -(* Proof: - Note 0 < gcd a b by GCD_POS - Thus k <= ppidx (gcd a b) by prime_power_divisibility - or k <= MIN (ppidx a) (ppidx b) by gcd_prime_power_index -*) -val gcd_prime_power_divisibility = store_thm( - "gcd_prime_power_divisibility", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides gcd a b ==> k <= MIN (ppidx a) (ppidx b)``, - metis_tac[GCD_POS, prime_power_divisibility, gcd_prime_power_index]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> - (lcm a b = p ** MAX (ppidx a) (ppidx b) * lcm (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b))) *) -(* Proof: - Let ma = ppidx a, qa = a DIV p ** ma. - Let mb = ppidx b, qb = b DIV p ** mb. - Then coprime p qa by prime_power_cofactor_coprime - and coprime p qb by prime_power_cofactor_coprime - Also a = p ** ma * qa by prime_power_eqn - and b = p ** mb * qb by prime_power_eqn - Note (gcd a b) * (lcm a b) = a * b by GCD_LCM - and gcd qa qb <> 0 by GCD_EQ_0, MULT_0, 0 < a, 0 < b. - - If ma < mb, - Then gcd a b = p ** ma * gcd qa qb by gcd_prime_power_factor, MIN_DEF - and a * b = (p ** ma * qa) * (p ** mb * qb) by above - Note p ** ma <> 0 by MULT, 0 < a = p ** ma * qa - gcd qa qb * lcm a b - = qa * (p ** mb * qb) by MULT_LEFT_CANCEL, MULT_ASSOC - = qa * qb * (p ** mb) by MULT_ASSOC_COMM - = gcd qa qb * lcm qa qb * (p ** mb) by GCD_LCM - Thus lcm a b = lcm qa qb * p ** mb by MULT_LEFT_CANCEL, MULT_ASSOC - = p ** mb * lcm qa qb by MULT_COMM - = p ** (MAX ma mb) * lcm qa qb by MAX_DEF - - If ~(ma < mb), - Then gcd a b = p ** mb * gcd qa qb by gcd_prime_power_factor, MIN_DEF - and a * b = (p ** mb * qb) * (p ** ma * qa) by MULT_COMM - Note p ** mb <> 0 by MULT, 0 < b = p ** mb * qb - gcd qa qb * lcm a b - = qb * (p ** ma * qa) by MULT_LEFT_CANCEL, MULT_ASSOC - = qa * qb * (p ** ma) by MULT_ASSOC_COMM, MULT_COMM - = gcd qa qb * lcm qa qb * (p ** ma) by GCD_LCM - Thus lcm a b = lcm qa qb * p ** ma by MULT_LEFT_CANCEL, MULT_ASSOC - = p ** ma * lcm qa qb by MULT_COMM - = p ** (MAX ma mb) * lcm qa qb by MAX_DEF -*) -val lcm_prime_power_factor = store_thm( - "lcm_prime_power_factor", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> - (lcm a b = p ** MAX (ppidx a) (ppidx b) * lcm (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b)))``, - rpt strip_tac >> - qabbrev_tac `ma = ppidx a` >> - qabbrev_tac `qa = a DIV p ** ma` >> - qabbrev_tac `mb = ppidx b` >> - qabbrev_tac `qb = b DIV p ** mb` >> - `coprime p qa` by rw[prime_power_cofactor_coprime, Abbr`ma`, Abbr`qa`] >> - `coprime p qb` by rw[prime_power_cofactor_coprime, Abbr`mb`, Abbr`qb`] >> - `a = p ** ma * qa` by rw[prime_power_eqn, Abbr`ma`, Abbr`qa`] >> - `b = p ** mb * qb` by rw[prime_power_eqn, Abbr`mb`, Abbr`qb`] >> - `(gcd a b) * (lcm a b) = a * b` by rw[GCD_LCM] >> - `gcd qa qb <> 0` by metis_tac[GCD_EQ_0, MULT_0, NOT_ZERO_LT_ZERO] >> - Cases_on `ma < mb` >| [ - `gcd a b = p ** ma * gcd qa qb` by metis_tac[gcd_prime_power_factor, MIN_DEF] >> - `a * b = (p ** ma * qa) * (p ** mb * qb)` by rw[] >> - `p ** ma <> 0` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> - `gcd qa qb * lcm a b = qa * (p ** mb * qb)` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> - `_ = qa * qb * (p ** mb)` by rw[MULT_ASSOC_COMM] >> - `_ = gcd qa qb * lcm qa qb * (p ** mb)` by metis_tac[GCD_LCM] >> - `lcm a b = lcm qa qb * p ** mb` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> - rw[MAX_DEF, Once MULT_COMM], - `gcd a b = p ** mb * gcd qa qb` by metis_tac[gcd_prime_power_factor, MIN_DEF] >> - `a * b = (p ** mb * qb) * (p ** ma * qa)` by rw[Once MULT_COMM] >> - `p ** mb <> 0` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> - `gcd qa qb * lcm a b = qb * (p ** ma * qa)` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> - `_ = qa * qb * (p ** ma)` by rw[MULT_ASSOC_COMM, Once MULT_COMM] >> - `_ = gcd qa qb * lcm qa qb * (p ** ma)` by metis_tac[GCD_LCM] >> - `lcm a b = lcm qa qb * p ** ma` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> - rw[MAX_DEF, Once MULT_COMM] - ]); - -(* The following is the two-number version of prime_power_factor_divides *) - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (p ** MAX (ppidx a) (ppidx b)) divides (lcm a b) *) -(* Proof: by lcm_prime_power_factor, divides_def *) -val lcm_prime_power_factor_divides_lcm = store_thm( - "lcm_prime_power_factor_divides_lcm", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (p ** MAX (ppidx a) (ppidx b)) divides (lcm a b)``, - prove_tac[lcm_prime_power_factor, divides_def, MULT_COMM]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> coprime p (lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b)) *) -(* Proof: - Let ma = ppidx a, qa = a DIV p ** ma. - Let mb = ppidx b, qb = b DIV p ** mb. - Then coprime p qa by prime_power_cofactor_coprime - and coprime p qb by prime_power_cofactor_coprime - - Simple if we have: gcd_over_lcm: gcd x (lcm y z) = lcm (gcd x y) (gcd x z) - But we don't, so use another approach. - - Note 1 < p by ONE_LT_PRIME - Let d = gcd p (lcm qa qb). - By contradiction, assume d <> 1. - Note d divides p by GCD_IS_GREATEST_COMMON_DIVISOR - so d = p by prime_def, d <> 1 - or p divides lcm qa qb by divides_iff_gcd_fix, gcd p (lcm qa qb) = d = p - But (lcm qa qb) divides (qa * qb) by GCD_LCM, divides_def - so p divides qa * qb by DIVIDES_TRANS - ==> p divides qa or p divides qb by P_EUCLIDES - This contradicts coprime p qa - and coprime p qb by coprime_not_divides, 1 < p -*) -val lcm_prime_power_cofactor_coprime = store_thm( - "lcm_prime_power_cofactor_coprime", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> coprime p (lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b))``, - rpt strip_tac >> - qabbrev_tac `ma = ppidx a` >> - qabbrev_tac `mb = ppidx b` >> - qabbrev_tac `qa = a DIV p ** ma` >> - qabbrev_tac `qb = b DIV p ** mb` >> - `coprime p qa` by rw[prime_power_cofactor_coprime, Abbr`ma`, Abbr`qa`] >> - `coprime p qb` by rw[prime_power_cofactor_coprime, Abbr`mb`, Abbr`qb`] >> - spose_not_then strip_assume_tac >> - qabbrev_tac `d = gcd p (lcm qa qb)` >> - `d divides p` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> - `d = p` by metis_tac[prime_def] >> - `p divides lcm qa qb` by rw[divides_iff_gcd_fix, Abbr`d`] >> - `(lcm qa qb) divides (qa * qb)` by metis_tac[GCD_LCM, divides_def] >> - `p divides qa * qb` by metis_tac[DIVIDES_TRANS] >> - `1 < p` by rw[ONE_LT_PRIME] >> - metis_tac[P_EUCLIDES, coprime_not_divides]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (ppidx (lcm a b) = MAX (ppidx a) (ppidx b)) *) -(* Proof: - Let ma = ppidx a, qa = a DIV p ** ma. - Let mb = ppidx b, qb = b DIV p ** mb. - Let m = MAX ma mb. - Then lcm a b = p ** m * (lcm qa qb) by lcm_prime_power_factor - Note 0 < lcm a b by LCM_POS - and coprime p (lcm qa qb) by lcm_prime_power_cofactor_coprime - so ppidx (lcm a b) = m by prime_power_index_test -*) -val lcm_prime_power_index = store_thm( - "lcm_prime_power_index", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (ppidx (lcm a b) = MAX (ppidx a) (ppidx b))``, - metis_tac[lcm_prime_power_factor, LCM_POS, lcm_prime_power_cofactor_coprime, prime_power_index_test]); - -(* Theorem: 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides lcm a b ==> k <= MAX (ppidx a) (ppidx b) *) -(* Proof: - Note 0 < lcm a b by LCM_POS - so k <= ppidx (lcm a b) by prime_power_divisibility - or k <= MAX (ppidx a) (ppidx b) by lcm_prime_power_index -*) -val lcm_prime_power_divisibility = store_thm( - "lcm_prime_power_divisibility", - ``!a b p. 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides lcm a b ==> k <= MAX (ppidx a) (ppidx b)``, - metis_tac[LCM_POS, prime_power_divisibility, lcm_prime_power_index]); - -(* ------------------------------------------------------------------------- *) -(* Prime Powers and List LCM *) -(* ------------------------------------------------------------------------- *) - -(* -If a prime-power divides a list_lcm, the prime-power must divides some element in the list for list_lcm. -Note: this is not true for non-prime-power. -*) - -(* Theorem: prime p ==> p ** (MAX_LIST (MAP (ppidx) l)) divides list_lcm l *) -(* Proof: - If l = [], - p ** MAX_LIST (MAP ppidx []) - = p ** MAX_LIST [] by MAP - = p ** 0 by MAX_LIST_NIL - = 1 - Hence true by ONE_DIVIDES_ALL - In fact, list_lcm [] = 1 by list_lcm_nil - If l <> [], - Let ml = MAP ppidx l. - Then ml <> [] by MAP_EQ_NIL - ==> MEM (MAX_LIST ml) ml by MAX_LIST_MEM, ml <> [] - so ?x. (MAX_LIST ml = ppidx x) /\ MEM x l by MEM_MAP - Thus p ** ppidx x divides x by prime_power_factor_divides - Now x divides list_lcm l by list_lcm_is_common_multiple - so p ** (ppidx x) - = p ** (MAX_LIST ml) divides list_lcm l by DIVIDES_TRANS -*) -val list_lcm_prime_power_factor_divides = store_thm( - "list_lcm_prime_power_factor_divides", - ``!l p. prime p ==> p ** (MAX_LIST (MAP (ppidx) l)) divides list_lcm l``, - rpt strip_tac >> - Cases_on `l = []` >- - rw[MAX_LIST_NIL] >> - qabbrev_tac `ml = MAP ppidx l` >> - `ml <> []` by rw[Abbr`ml`] >> - `MEM (MAX_LIST ml) ml` by rw[MAX_LIST_MEM] >> - `?x. (MAX_LIST ml = ppidx x) /\ MEM x l` by metis_tac[MEM_MAP] >> - `p ** ppidx x divides x` by rw[prime_power_factor_divides] >> - `x divides list_lcm l` by rw[list_lcm_is_common_multiple] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: prime p /\ POSITIVE l ==> (ppidx (list_lcm l) = MAX_LIST (MAP ppidx l)) *) -(* Proof: - By induction on l. - Base: ppidx (list_lcm []) = MAX_LIST (MAP ppidx []) - ppidx (list_lcm []) - = ppidx 1 by list_lcm_nil - = 0 by prime_power_index_1 - = MAX_LIST [] by MAX_LIST_NIL - = MAX_LIST (MAP ppidx []) by MAP - - Step: ppidx (list_lcm l) = MAX_LIST (MAP ppidx l) ==> - ppidx (list_lcm (h::l)) = MAX_LIST (MAP ppidx (h::l)) - Note 0 < list_lcm l by list_lcm_pos, EVERY_MEM - ppidx (list_lcm (h::l)) - = ppidx (lcm h (list_lcm l)) by list_lcm_cons - = MAX (ppidx h) (ppidx (list_lcm l)) by lcm_prime_power_index, 0 < list_lcm l - = MAX (ppidx h) (MAX_LIST (MAP ppidx l)) by induction hypothesis - = MAX_LIST (ppidx h :: MAP ppidx l) by MAX_LIST_CONS - = MAX_LIST (MAP ppidx (h::l)) by MAP -*) -val list_lcm_prime_power_index = store_thm( - "list_lcm_prime_power_index", - ``!l p. prime p /\ POSITIVE l ==> (ppidx (list_lcm l) = MAX_LIST (MAP ppidx l))``, - Induct >- - rw[prime_power_index_1] >> - rpt strip_tac >> - `0 < list_lcm l` by rw[list_lcm_pos, EVERY_MEM] >> - `ppidx (list_lcm (h::l)) = ppidx (lcm h (list_lcm l))` by rw[list_lcm_cons] >> - `_ = MAX (ppidx h) (ppidx (list_lcm l))` by rw[lcm_prime_power_index] >> - `_ = MAX (ppidx h) (MAX_LIST (MAP ppidx l))` by rw[] >> - `_ = MAX_LIST (ppidx h :: MAP ppidx l)` by rw[MAX_LIST_CONS] >> - `_ = MAX_LIST (MAP ppidx (h::l))` by rw[] >> - rw[]); - -(* Theorem: prime p /\ POSITIVE l ==> - !k. p ** k divides list_lcm l ==> k <= MAX_LIST (MAP ppidx l) *) -(* Proof: - Note 0 < list_lcm l by list_lcm_pos, EVERY_MEM - so k <= ppidx (list_lcm l) by prime_power_divisibility - or k <= MAX_LIST (MAP ppidx l) by list_lcm_prime_power_index -*) -val list_lcm_prime_power_divisibility = store_thm( - "list_lcm_prime_power_divisibility", - ``!l p. prime p /\ POSITIVE l ==> - !k. p ** k divides list_lcm l ==> k <= MAX_LIST (MAP ppidx l)``, - rpt strip_tac >> - `0 < list_lcm l` by rw[list_lcm_pos, EVERY_MEM] >> - metis_tac[prime_power_divisibility, list_lcm_prime_power_index]); - -(* Theorem: prime p /\ l <> [] /\ POSITIVE l ==> - !k. p ** k divides list_lcm l ==> ?x. MEM x l /\ p ** k divides x *) -(* Proof: - Let ml = MAP ppidx l. - - Step 1: Get member x that attains ppidx x = MAX_LIST ml - Note ml <> [] by MAP_EQ_NIL - Then MEM (MAX_LIST ml) ml by MAX_LIST_MEM, ml <> [] - ==> ?x. (MAX_LIST ml = ppidx x) /\ MEM x l by MEM_MAP - - Step 2: Show that this is a suitable x - Note p ** k divides list_lcm l by given - ==> k <= MAX_LIST ml by list_lcm_prime_power_divisibility - Now 1 < p by ONE_LT_PRIME - so p ** k divides p ** (MAX_LIST ml) by power_divides_iff, 1 < p - and p ** (ppidx x) divides x by prime_power_factor_divides - Thus p ** k divides x by DIVIDES_TRANS - - Take this x, and the result follows. -*) -val list_lcm_prime_power_factor_member = store_thm( - "list_lcm_prime_power_factor_member", - ``!l p. prime p /\ l <> [] /\ POSITIVE l ==> - !k. p ** k divides list_lcm l ==> ?x. MEM x l /\ p ** k divides x``, - rpt strip_tac >> - qabbrev_tac `ml = MAP ppidx l` >> - `ml <> []` by rw[Abbr`ml`] >> - `MEM (MAX_LIST ml) ml` by rw[MAX_LIST_MEM] >> - `?x. (MAX_LIST ml = ppidx x) /\ MEM x l` by metis_tac[MEM_MAP] >> - `k <= MAX_LIST ml` by rw[list_lcm_prime_power_divisibility, Abbr`ml`] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `p ** k divides p ** (MAX_LIST ml)` by rw[power_divides_iff] >> - `p ** (ppidx x) divides x` by rw[prime_power_factor_divides] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: prime p ==> !m n. (n = p ** SUC (ppidx m)) ==> (lcm n m = p * m) *) -(* Proof: - If m = 0, - lcm n 0 = 0 by LCM_0 - = p * 0 by MULT_0 - If m <> 0, then 0 < m. - Note 0 < n by PRIME_POS, EXP_POS - Let nq = n DIV p ** (ppidx n), mq = m DIV p ** (ppidx m). - Let k = ppidx m. - Note ppidx n = SUC k by prime_power_index_prime_power - and nq = 1 by DIVMOD_ID - Now MAX (ppidx n) (ppidx m) - = MAX (SUC k) k by above - = SUC k by MAX_DEF - - lcm n m - = p ** MAX (ppidx n) (ppidx m) * (lcm nq mq) by lcm_prime_power_factor - = p ** (SUC k) * (lcm 1 mq) by above - = p ** (SUC k) * mq by LCM_1 - = p * p ** k * mq by EXP - = p * (p ** k * mq) by MULT_ASSOC - = p * m by prime_power_eqn -*) -val lcm_special_for_prime_power = store_thm( - "lcm_special_for_prime_power", - ``!p. prime p ==> !m n. (n = p ** SUC (ppidx m)) ==> (lcm n m = p * m)``, - rpt strip_tac >> - Cases_on `m = 0` >- - rw[] >> - `0 < m` by decide_tac >> - `0 < n` by rw[PRIME_POS, EXP_POS] >> - qabbrev_tac `k = ppidx m` >> - `ppidx n = SUC k` by rw[prime_power_index_prime_power] >> - `MAX (SUC k) k = SUC k` by rw[MAX_DEF] >> - qabbrev_tac `mq = m DIV p ** (ppidx m)` >> - qabbrev_tac `nq = n DIV p ** (ppidx n)` >> - `nq = 1` by rw[DIVMOD_ID, Abbr`nq`] >> - `lcm n m = p ** (SUC k) * (lcm nq mq)` by metis_tac[lcm_prime_power_factor] >> - metis_tac[LCM_1, EXP, MULT_ASSOC, prime_power_eqn]); - -(* Theorem: (n = a * b) /\ coprime a b ==> !m. a divides m /\ b divides m ==> (lcm n m = m) *) -(* Proof: - If n = 0, - Then a * b = 0 ==> a = 0 or b = 0 by MULT_EQ_0 - so a divides m /\ b divides m ==> m = 0 by ZERO_DIVIDES - Since lcm 0 m = 0 by LCM_0 - so lcm n m = m by above - If n <> 0, - Note (a * b) divides m by coprime_product_divides - or n divides m by n = a * b - so lcm n m = m by divides_iff_lcm_fix -*) -Theorem lcm_special_for_coprime_factors: - !n a b. n = a * b /\ coprime a b ==> - !m. a divides m /\ b divides m ==> lcm n m = m -Proof - rpt strip_tac >> Cases_on `n = 0` >| [ - `m = 0` by metis_tac[MULT_EQ_0, ZERO_DIVIDES] >> - simp[LCM_0], - `n divides m` by rw[coprime_product_divides] >> - rw[GSYM divides_iff_lcm_fix] - ] -QED - -(* ------------------------------------------------------------------------- *) -(* Prime Divisors *) -(* ------------------------------------------------------------------------- *) - -(* Define the prime divisors of a number *) -val prime_divisors_def = zDefine` - prime_divisors n = {p | prime p /\ p divides n} -`; -(* use zDefine as this is not effective. *) - -(* Theorem: p IN prime_divisors n <=> prime p /\ p divides n *) -(* Proof: by prime_divisors_def *) -val prime_divisors_element = store_thm( - "prime_divisors_element", - ``!n p. p IN prime_divisors n <=> prime p /\ p divides n``, - rw[prime_divisors_def]); - -(* Theorem: 0 < n ==> (prime_divisors n) SUBSET (natural n) *) -(* Proof: - By prime_divisors_element, SUBSET_DEF, - this is to show: ?x'. (x = SUC x') /\ x' < n - Note prime x /\ x divides n - ==> 0 < x /\ x <= n by PRIME_POS, DIVIDES_LE, 0 < n - ==> 0 < x /\ PRE x < n by arithmetic - Take x' = PRE x, - Then SUC x' = SUC (PRE x) = x by SUC_PRE, 0 < x -*) -val prime_divisors_subset_natural = store_thm( - "prime_divisors_subset_natural", - ``!n. 0 < n ==> (prime_divisors n) SUBSET (natural n)``, - rw[prime_divisors_element, SUBSET_DEF] >> - `x <= n` by rw[DIVIDES_LE] >> - `PRE x < n` by decide_tac >> - `0 < x` by rw[PRIME_POS] >> - metis_tac[SUC_PRE]); - -(* Theorem: 0 < n ==> FINITE (prime_divisors n) *) -(* Proof: - Note (prime_divisors n) SUBSET (natural n) by prime_divisors_subset_natural, 0 < n - and FINITE (natural n) by natural_finite - so FINITE (prime_divisors n) by SUBSET_FINITE -*) -val prime_divisors_finite = store_thm( - "prime_divisors_finite", - ``!n. 0 < n ==> FINITE (prime_divisors n)``, - metis_tac[prime_divisors_subset_natural, natural_finite, SUBSET_FINITE]); - -(* Theorem: prime_divisors 0 = {p | prime p} *) -(* Proof: by prime_divisors_def, ALL_DIVIDES_0 *) -Theorem prime_divisors_0: prime_divisors 0 = {p | prime p} -Proof rw[prime_divisors_def] -QED - -(* Note: prime: num -> bool is also a set, so prime = {p | prime p} *) - -(* Theorem: prime_divisors n = {} *) -(* Proof: by prime_divisors_def, DIVIDES_ONE, NOT_PRIME_1 *) -val prime_divisors_1 = store_thm( - "prime_divisors_1", - ``prime_divisors 1 = {}``, - rw[prime_divisors_def, EXTENSION]); - -(* Theorem: (prime_divisors n) SUBSET prime *) -(* Proof: by prime_divisors_element, SUBSET_DEF, IN_DEF *) -val prime_divisors_subset_prime = store_thm( - "prime_divisors_subset_prime", - ``!n. (prime_divisors n) SUBSET prime``, - rw[prime_divisors_element, SUBSET_DEF, IN_DEF]); - -(* Theorem: 1 < n ==> prime_divisors n <> {} *) -(* Proof: - Note n <> 1 by 1 < n - so ?p. prime p /\ p divides n by PRIME_FACTOR - or p IN prime_divisors n by prime_divisors_element - ==> prime_divisors n <> {} by MEMBER_NOT_EMPTY -*) -val prime_divisors_nonempty = store_thm( - "prime_divisors_nonempty", - ``!n. 1 < n ==> prime_divisors n <> {}``, - metis_tac[PRIME_FACTOR, prime_divisors_element, MEMBER_NOT_EMPTY, DECIDE``1 < n ==> n <> 1``]); - -(* Theorem: (prime_divisors n = {}) <=> (n = 1) *) -(* Proof: by prime_divisors_def, DIVIDES_ONE, NOT_PRIME_1, PRIME_FACTOR *) -val prime_divisors_empty_iff = store_thm( - "prime_divisors_empty_iff", - ``!n. (prime_divisors n = {}) <=> (n = 1)``, - rw[prime_divisors_def, EXTENSION] >> - metis_tac[DIVIDES_ONE, NOT_PRIME_1, PRIME_FACTOR]); - -(* Theorem: ~ SING (prime_divisors 0) *) -(* Proof: - Let s = prime_divisors 0. - By contradiction, suppose SING s. - Note prime 2 by PRIME_2 - and prime 3 by PRIME_3 - so 2 IN s /\ 3 IN s by prime_divisors_0 - This contradicts SING s by SING_ELEMENT -*) -val prime_divisors_0_not_sing = store_thm( - "prime_divisors_0_not_sing", - ``~ SING (prime_divisors 0)``, - rpt strip_tac >> - qabbrev_tac `s = prime_divisors 0` >> - `2 IN s /\ 3 IN s` by rw[PRIME_2, PRIME_3, prime_divisors_0, Abbr`s`] >> - metis_tac[SING_ELEMENT, DECIDE``2 <> 3``]); - -(* Theorem: prime n ==> (prime_divisors n = {n}) *) -(* Proof: - By prime_divisors_def, EXTENSION, this is to show: - prime x /\ x divides n <=> (x = n) - This is true by prime_divides_prime -*) -val prime_divisors_prime = store_thm( - "prime_divisors_prime", - ``!n. prime n ==> (prime_divisors n = {n})``, - rw[prime_divisors_def, EXTENSION] >> - metis_tac[prime_divides_prime]); - -(* Theorem: prime n ==> (prime_divisors n = {n}) *) -(* Proof: - By prime_divisors_def, EXTENSION, this is to show: - prime x /\ x divides n ** k <=> (x = n) - If part: prime x /\ x divides n ** k ==> (x = n) - This is true by prime_divides_prime_power - Only-if part: prime n /\ 0 < k ==> n divides n ** k - This is true by prime_divides_power, DIVIDES_REFL -*) -val prime_divisors_prime_power = store_thm( - "prime_divisors_prime_power", - ``!n. prime n ==> !k. 0 < k ==> (prime_divisors (n ** k) = {n})``, - rw[prime_divisors_def, EXTENSION] >> - rw[EQ_IMP_THM] >- - metis_tac[prime_divides_prime_power] >> - metis_tac[prime_divides_power, DIVIDES_REFL]); - -(* Theorem: SING (prime_divisors n) <=> ?p k. prime p /\ 0 < k /\ (n = p ** k) *) -(* Proof: - If part: SING (prime_divisors n) ==> ?p k. prime p /\ 0 < k /\ (n = p ** k) - Note n <> 0 by prime_divisors_0_not_sing - Claim: n <> 1 - Proof: By contradiction, suppose n = 1. - Then prime_divisors 1 = {} by prime_divisors_1 - but SING {} = F by SING_EMPTY - - Thus 1 < n by n <> 0, n <> 1 - ==> ?p. prime p /\ p divides n by PRIME_FACTOR - also ?q m. (n = p ** m * q) /\ (coprime p q) by prime_power_factor, 0 < n - Note q <> 0 by MULT_EQ_0 - Claim: q = 1 - Proof: By contradiction, suppose q <> 1. - Then 1 < q by q <> 0, q <> 1 - ==> ?z. prime z /\ z divides q by PRIME_FACTOR - Now 1 < p by ONE_LT_PRIME - so ~(p divides q) by coprime_not_divides, 1 < p, coprime p q - or p <> z by z divides q, but ~(p divides q) - But q divides n by divides_def, n = p ** m * q - Thus z divides n by DIVIDES_TRANS - so p IN (prime_divisors n) by prime_divisors_element - and z IN (prime_divisors n) by prime_divisors_element - This contradicts SING (prime_divisors n) by SING_ELEMENT - - Thus q = 1, - ==> n = p ** m by MULT_RIGHT_1 - and m <> 0 by EXP_0, n <> 1 - Thus take this prime p, and exponent m, and 0 < m by NOT_ZERO_LT_ZERO - - Only-if part: ?p k. prime p /\ 0 < k /\ (n = p ** k) ==> SING (prime_divisors n) - Note (prime_divisors p ** k) = {p} by prime_divisors_prime_power - so SING (prime_divisors n) by SING_DEF -*) -val prime_divisors_sing = store_thm( - "prime_divisors_sing", - ``!n. SING (prime_divisors n) <=> ?p k. prime p /\ 0 < k /\ (n = p ** k)``, - rw[EQ_IMP_THM] >| [ - `n <> 0` by metis_tac[prime_divisors_0_not_sing] >> - `n <> 1` by metis_tac[prime_divisors_1, SING_EMPTY] >> - `0 < n /\ 1 < n` by decide_tac >> - `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> - `?q m. (n = p ** m * q) /\ (coprime p q)` by rw[prime_power_factor] >> - `q <> 0` by metis_tac[MULT_EQ_0] >> - Cases_on `q = 1` >- - metis_tac[MULT_RIGHT_1, EXP_0, NOT_ZERO_LT_ZERO] >> - `?z. prime z /\ z divides q` by rw[PRIME_FACTOR] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `p <> z` by metis_tac[coprime_not_divides] >> - `z divides n` by metis_tac[divides_def, DIVIDES_TRANS] >> - metis_tac[prime_divisors_element, SING_ELEMENT], - metis_tac[prime_divisors_prime_power, SING_DEF] - ]); - -(* Theorem: (prime_divisors n = {p}) <=> ?k. prime p /\ 0 < k /\ (n = p ** k) *) -(* Proof: - If part: prime_divisors n = {p} ==> ?k. prime p /\ 0 < k /\ (n = p ** k) - Note prime p by prime_divisors_element, IN_SING - and SING (prime_divisors n) by SING_DEF - ==> ?q k. prime q /\ 0 < k /\ (n = q ** k) by prime_divisors_sing - Take this k, then q = p by prime_divisors_prime_power, IN_SING - Only-if part: prime p ==> prime_divisors (p ** k) = {p} - This is true by prime_divisors_prime_power -*) -val prime_divisors_sing_alt = store_thm( - "prime_divisors_sing_alt", - ``!n p. (prime_divisors n = {p}) <=> ?k. prime p /\ 0 < k /\ (n = p ** k)``, - metis_tac[prime_divisors_sing, SING_DEF, IN_SING, prime_divisors_element, prime_divisors_prime_power]); - -(* Theorem: SING (prime_divisors n) ==> - let p = CHOICE (prime_divisors n) in prime p /\ (n = p ** ppidx n) *) -(* Proof: - Let s = prime_divisors n. - Note n <> 0 by prime_divisors_0_not_sing - and n <> 1 by prime_divisors_1, SING_EMPTY - ==> s <> {} by prime_divisors_empty_iff, n <> 1 - Note p = CHOICE s IN s by CHOICE_DEF - so prime p /\ p divides n by prime_divisors_element - Thus need only to show: n = p ** ppidx n - Note ?q. (n = p ** ppidx n * q) /\ - coprime p q by prime_power_factor, prime_power_index_test, 0 < n - Claim: q = 1 - Proof: By contradiction, suppose q <> 1. - Note 1 < p by ONE_LT_PRIME, prime p - and q <> 0 by MULT_EQ_0 - ==> ?z. prime z /\ z divides q by PRIME_FACTOR, 1 < q - Note ~(p divides q) by coprime_not_divides, 1 < p - ==> z <> p by z divides q - Also q divides n by divides_def, n = p ** ppidx n * q - ==> z divides n by DIVIDES_TRANS - Thus p IN s /\ z IN s by prime_divisors_element - or p = z, contradicts z <> p by SING_ELEMENT - - Thus q = 1, and n = p ** ppidx n by MULT_RIGHT_1 -*) -val prime_divisors_sing_property = store_thm( - "prime_divisors_sing_property", - ``!n. SING (prime_divisors n) ==> - let p = CHOICE (prime_divisors n) in prime p /\ (n = p ** ppidx n)``, - ntac 2 strip_tac >> - qabbrev_tac `s = prime_divisors n` >> - `n <> 0` by metis_tac[prime_divisors_0_not_sing] >> - `n <> 1` by metis_tac[prime_divisors_1, SING_EMPTY] >> - `s <> {}` by rw[prime_divisors_empty_iff, Abbr`s`] >> - `prime (CHOICE s) /\ (CHOICE s) divides n` by metis_tac[CHOICE_DEF, prime_divisors_element] >> - rw_tac std_ss[] >> - rw[] >> - `0 < n` by decide_tac >> - `?q. (n = p ** ppidx n * q) /\ coprime p q` by metis_tac[prime_power_factor, prime_power_index_test] >> - reverse (Cases_on `q = 1`) >| [ - `q <> 0` by metis_tac[MULT_EQ_0] >> - `?z. prime z /\ z divides q` by rw[PRIME_FACTOR] >> - `z <> p` by metis_tac[coprime_not_divides, ONE_LT_PRIME] >> - `z divides n` by metis_tac[divides_def, DIVIDES_TRANS] >> - metis_tac[prime_divisors_element, SING_ELEMENT], - rw[] - ]); - -(* Theorem: m divides n ==> (prime_divisors m) SUBSET (prime_divisors n) *) -(* Proof: - Note !x. x IN prime_divisors m - ==> prime x /\ x divides m by prime_divisors_element - ==> primx x /\ x divides n by DIVIDES_TRANS - ==> x IN prime_divisors n by prime_divisors_element - or (prime_divisors m) SUBSET (prime_divisors n) by SUBSET_DEF -*) -val prime_divisors_divisor_subset = store_thm( - "prime_divisors_divisor_subset", - ``!m n. m divides n ==> (prime_divisors m) SUBSET (prime_divisors n)``, - rw[prime_divisors_element, SUBSET_DEF] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: x divides m /\ x divides n ==> - (prime_divisors x SUBSET (prime_divisors m) INTER (prime_divisors n)) *) -(* Proof: - By prime_divisors_element, SUBSET_DEF, this is to show: - (1) x' divides x /\ x divides m ==> x' divides m, true by DIVIDES_TRANS - (2) x' divides x /\ x divides n ==> x' divides n, true by DIVIDES_TRANS -*) -val prime_divisors_common_divisor = store_thm( - "prime_divisors_common_divisor", - ``!n m x. x divides m /\ x divides n ==> - (prime_divisors x SUBSET (prime_divisors m) INTER (prime_divisors n))``, - rw[prime_divisors_element, SUBSET_DEF] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: m divides x /\ n divides x ==> - (prime_divisors m UNION prime_divisors n) SUBSET prime_divisors x *) -(* Proof: - By prime_divisors_element, SUBSET_DEF, this is to show: - (1) x' divides m /\ m divides x ==> x' divides x, true by DIVIDES_TRANS - (2) x' divides n /\ n divides x ==> x' divides x, true by DIVIDES_TRANS -*) -val prime_divisors_common_multiple = store_thm( - "prime_divisors_common_multiple", - ``!n m x. m divides x /\ n divides x ==> - (prime_divisors m UNION prime_divisors n) SUBSET prime_divisors x``, - rw[prime_divisors_element, SUBSET_DEF] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: 0 < m /\ 0 < n /\ x divides m /\ x divides n ==> - !p. prime p ==> ppidx x <= MIN (ppidx m) (ppidx n) *) -(* Proof: - Note ppidx x <= ppidx m by prime_power_index_of_divisor, 0 < m - and ppidx x <= ppidx n by prime_power_index_of_divisor, 0 < n - ==> ppidx x <= MIN (ppidx m) (ppidx n) by MIN_LE -*) -val prime_power_index_common_divisor = store_thm( - "prime_power_index_common_divisor", - ``!n m x. 0 < m /\ 0 < n /\ x divides m /\ x divides n ==> - !p. prime p ==> ppidx x <= MIN (ppidx m) (ppidx n)``, - rw[MIN_LE, prime_power_index_of_divisor]); - -(* Theorem: 0 < x /\ m divides x /\ n divides x ==> - !p. prime p ==> MAX (ppidx m) (ppidx n) <= ppidx x *) -(* Proof: - Note ppidx m <= ppidx x by prime_power_index_of_divisor, 0 < x - and ppidx n <= ppidx x by prime_power_index_of_divisor, 0 < x - ==> MAX (ppidx m) (ppidx n) <= ppidx x by MAX_LE -*) -val prime_power_index_common_multiple = store_thm( - "prime_power_index_common_multiple", - ``!n m x. 0 < x /\ m divides x /\ n divides x ==> - !p. prime p ==> MAX (ppidx m) (ppidx n) <= ppidx x``, - rw[MAX_LE, prime_power_index_of_divisor]); - -(* -prime p = 2, n = 10, LOG 2 10 = 3, but ppidx 10 = 1, since 4 cannot divide 10. -10 = 2^1 * 5^1 -*) - -(* Theorem: 0 < n /\ prime p ==> ppidx n <= LOG p n *) -(* Proof: - By contradiction, suppose LOG p n < ppidx n. - Then SUC (LOG p n) <= ppidx n by arithmetic - Note 1 < p by ONE_LT_PRIME - so p ** (SUC (LOG p n)) divides p ** ppidx n by power_divides_iff, 1 < p - But p ** ppidx n divides n by prime_power_index_def - ==> p ** SUC (LOG p n) divides n by DIVIDES_TRANS - or p ** SUC (LOG p n) <= n by DIVIDES_LE, 0 < n - This contradicts n < p ** SUC (LOG p n) by LOG, 0 < n, 1 < p -*) -val prime_power_index_le_log_index = store_thm( - "prime_power_index_le_log_index", - ``!n p. 0 < n /\ prime p ==> ppidx n <= LOG p n``, - spose_not_then strip_assume_tac >> - `SUC (LOG p n) <= ppidx n` by decide_tac >> - `1 < p` by rw[ONE_LT_PRIME] >> - `p ** (SUC (LOG p n)) divides p ** ppidx n` by rw[power_divides_iff] >> - `p ** ppidx n divides n` by rw[prime_power_index_def] >> - `p ** SUC (LOG p n) divides n` by metis_tac[DIVIDES_TRANS] >> - `p ** SUC (LOG p n) <= n` by rw[DIVIDES_LE] >> - `n < p ** SUC (LOG p n)` by rw[LOG] >> - decide_tac); - -(* ------------------------------------------------------------------------- *) -(* Prime-related Sets *) -(* ------------------------------------------------------------------------- *) - -(* -Example: Take n = 10. -primes_upto 10 = {2; 3; 5; 7} -prime_powers_upto 10 = {8; 9; 5; 7} -SET_TO_LIST (prime_powers_upto 10) = [8; 9; 5; 7] -set_lcm (prime_powers_upto 10) = 2520 -lcm_run 10 = 2520 - -Given n, -First get (primes_upto n) = {p | prime p /\ p <= n} -For each prime p, map to p ** LOG p n. - -logroot.LOG |- !a n. 1 < a /\ 0 < n ==> a ** LOG a n <= n /\ n < a ** SUC (LOG a n) -*) - -(* val _ = clear_overloads_on "pd"; in Mobius theory *) -(* open primePowerTheory; *) - -(* -> prime_power_index_def; -val it = |- !p n. 0 < n /\ prime p ==> p ** ppidx n divides n /\ coprime p (n DIV p ** ppidx n): thm -*) - -(* Define the set of primes up to n *) -val primes_upto_def = Define` - primes_upto n = {p | prime p /\ p <= n} -`; - -(* Overload the counts of primes up to n *) -val _ = overload_on("primes_count", ``\n. CARD (primes_upto n)``); - -(* Define the prime powers up to n *) -val prime_powers_upto_def = Define` - prime_powers_upto n = IMAGE (\p. p ** LOG p n) (primes_upto n) -`; - -(* Define the prime power divisors of n *) -val prime_power_divisors_def = Define` - prime_power_divisors n = IMAGE (\p. p ** ppidx n) (prime_divisors n) -`; - -(* Theorem: p IN primes_upto n <=> prime p /\ p <= n *) -(* Proof: by primes_upto_def *) -val primes_upto_element = store_thm( - "primes_upto_element", - ``!n p. p IN primes_upto n <=> prime p /\ p <= n``, - rw[primes_upto_def]); - -(* Theorem: (primes_upto n) SUBSET (natural n) *) -(* Proof: - By primes_upto_def, SUBSET_DEF, - this is to show: prime x /\ x <= n ==> ?x'. (x = SUC x') /\ x' < n - Note 0 < x by PRIME_POS, prime x - so PRE x < n by x <= n - and SUC (PRE x) = x by SUC_PRE, 0 < x - Take x' = PRE x, and the result follows. -*) -val primes_upto_subset_natural = store_thm( - "primes_upto_subset_natural", - ``!n. (primes_upto n) SUBSET (natural n)``, - rw[primes_upto_def, SUBSET_DEF] >> - `0 < x` by rw[PRIME_POS] >> - `PRE x < n` by decide_tac >> - metis_tac[SUC_PRE]); - -(* Theorem: FINITE (primes_upto n) *) -(* Proof: - Note (primes_upto n) SUBSET (natural n) by primes_upto_subset_natural - and FINITE (natural n) by natural_finite - ==> FINITE (primes_upto n) by SUBSET_FINITE -*) -val primes_upto_finite = store_thm( - "primes_upto_finite", - ``!n. FINITE (primes_upto n)``, - metis_tac[primes_upto_subset_natural, natural_finite, SUBSET_FINITE]); - -(* Theorem: PAIRWISE_COPRIME (primes_upto n) *) -(* Proof: - Let s = prime_power_divisors n - This is to show: prime x /\ prime y /\ x <> y ==> coprime x y - This is true by primes_coprime -*) -val primes_upto_pairwise_coprime = store_thm( - "primes_upto_pairwise_coprime", - ``!n. PAIRWISE_COPRIME (primes_upto n)``, - metis_tac[primes_upto_element, primes_coprime]); - -(* Theorem: primes_upto 0 = {} *) -(* Proof: - p IN primes_upto 0 - <=> prime p /\ p <= 0 by primes_upto_element - <=> prime 0 by p <= 0 - <=> F by NOT_PRIME_0 -*) -val primes_upto_0 = store_thm( - "primes_upto_0", - ``primes_upto 0 = {}``, - rw[primes_upto_element, EXTENSION]); - -(* Theorem: primes_count 0 = 0 *) -(* Proof: - primes_count 0 - = CARD (primes_upto 0) by notation - = CARD {} by primes_upto_0 - = 0 by CARD_EMPTY -*) -val primes_count_0 = store_thm( - "primes_count_0", - ``primes_count 0 = 0``, - rw[primes_upto_0]); - -(* Theorem: primes_upto 1 = {} *) -(* Proof: - p IN primes_upto 1 - <=> prime p /\ p <= 1 by primes_upto_element - <=> prime 0 or prime 1 by p <= 1 - <=> F by NOT_PRIME_0, NOT_PRIME_1 -*) -val primes_upto_1 = store_thm( - "primes_upto_1", - ``primes_upto 1 = {}``, - rw[primes_upto_element, EXTENSION, DECIDE``x <= 1 <=> (x = 0) \/ (x = 1)``] >> - metis_tac[NOT_PRIME_0, NOT_PRIME_1]); - -(* Theorem: primes_count 1 = 0 *) -(* Proof: - primes_count 1 - = CARD (primes_upto 1) by notation - = CARD {} by primes_upto_1 - = 0 by CARD_EMPTY -*) -val primes_count_1 = store_thm( - "primes_count_1", - ``primes_count 1 = 0``, - rw[primes_upto_1]); - -(* Theorem: x IN prime_powers_upto n <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n *) -(* Proof: by prime_powers_upto_def, primes_upto_element *) -val prime_powers_upto_element = store_thm( - "prime_powers_upto_element", - ``!n x. x IN prime_powers_upto n <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n``, - rw[prime_powers_upto_def, primes_upto_element]); - -(* Theorem: prime p /\ p <= n ==> (p ** LOG p n) IN (prime_powers_upto n) *) -(* Proof: by prime_powers_upto_element *) -val prime_powers_upto_element_alt = store_thm( - "prime_powers_upto_element_alt", - ``!p n. prime p /\ p <= n ==> (p ** LOG p n) IN (prime_powers_upto n)``, - metis_tac[prime_powers_upto_element]); - -(* Theorem: FINITE (prime_powers_upto n) *) -(* Proof: - Note prime_powers_upto n = - IMAGE (\p. p ** LOG p n) (primes_upto n) by prime_powers_upto_def - and FINITE (primes_upto n) by primes_upto_finite - ==> FINITE (prime_powers_upto n) by IMAGE_FINITE -*) -val prime_powers_upto_finite = store_thm( - "prime_powers_upto_finite", - ``!n. FINITE (prime_powers_upto n)``, - rw[prime_powers_upto_def, primes_upto_finite]); - -(* Theorem: PAIRWISE_COPRIME (prime_powers_upto n) *) -(* Proof: - Let s = prime_power_divisors n - This is to show: x IN s /\ y IN s /\ x <> y ==> coprime x y - Note ?p1. prime p1 /\ (x = p1 ** LOG p1 n) /\ p1 <= n by prime_powers_upto_element - and ?p2. prime p2 /\ (y = p2 ** LOG p2 n) /\ p2 <= n by prime_powers_upto_element - and p1 <> p2 by prime_powers_eq - Thus coprime x y by prime_powers_coprime -*) -val prime_powers_upto_pairwise_coprime = store_thm( - "prime_powers_upto_pairwise_coprime", - ``!n. PAIRWISE_COPRIME (prime_powers_upto n)``, - metis_tac[prime_powers_upto_element, prime_powers_eq, prime_powers_coprime]); - -(* Theorem: prime_powers_upto 0 = {} *) -(* Proof: - x IN prime_powers_upto 0 - <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= 0 by prime_powers_upto_element - <=> ?p. (x = p ** LOG p n) /\ prime 0 by p <= 0 - <=> F by NOT_PRIME_0 -*) -val prime_powers_upto_0 = store_thm( - "prime_powers_upto_0", - ``prime_powers_upto 0 = {}``, - rw[prime_powers_upto_element, EXTENSION]); - -(* Theorem: prime_powers_upto 1 = {} *) -(* Proof: - x IN prime_powers_upto 1 - <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= 1 by prime_powers_upto_element - <=> ?p. (x = p ** LOG p n) /\ prime 0 or prime 1 by p <= 0 - <=> F by NOT_PRIME_0, NOT_PRIME_1 -*) -val prime_powers_upto_1 = store_thm( - "prime_powers_upto_1", - ``prime_powers_upto 1 = {}``, - rw[prime_powers_upto_element, EXTENSION, DECIDE``x <= 1 <=> (x = 0) \/ (x = 1)``] >> - metis_tac[NOT_PRIME_0, NOT_PRIME_1]); - -(* Theorem: x IN prime_power_divisors n <=> ?p. (x = p ** ppidx n) /\ prime p /\ p divides n *) -(* Proof: by prime_power_divisors_def, prime_divisors_element *) -val prime_power_divisors_element = store_thm( - "prime_power_divisors_element", - ``!n x. x IN prime_power_divisors n <=> ?p. (x = p ** ppidx n) /\ prime p /\ p divides n``, - rw[prime_power_divisors_def, prime_divisors_element]); - -(* Theorem: prime p /\ p divides n ==> (p ** ppidx n) IN (prime_power_divisors n) *) -(* Proof: by prime_power_divisors_element *) -val prime_power_divisors_element_alt = store_thm( - "prime_power_divisors_element_alt", - ``!p n. prime p /\ p divides n ==> (p ** ppidx n) IN (prime_power_divisors n)``, - metis_tac[prime_power_divisors_element]); - -(* Theorem: 0 < n ==> FINITE (prime_power_divisors n) *) -(* Proof: - Note prime_power_divisors n = - IMAGE (\p. p ** ppidx n) (prime_divisors n) by prime_power_divisors_def - and FINITE (prime_divisors n) by prime_divisors_finite, 0 < n - ==> FINITE (prime_power_divisors n) by IMAGE_FINITE -*) -val prime_power_divisors_finite = store_thm( - "prime_power_divisors_finite", - ``!n. 0 < n ==> FINITE (prime_power_divisors n)``, - rw[prime_power_divisors_def, prime_divisors_finite]); - -(* Theorem: PAIRWISE_COPRIME (prime_power_divisors n) *) -(* Proof: - Let s = prime_power_divisors n - This is to show: x IN s /\ y IN s /\ x <> y ==> coprime x y - Note ?p1. prime p1 /\ - (x = p1 ** prime_power_index p1 n) /\ p1 divides n by prime_power_divisors_element - and ?p2. prime p2 /\ - (y = p2 ** prime_power_index p2 n) /\ p2 divides n by prime_power_divisors_element - and p1 <> p2 by prime_powers_eq - Thus coprime x y by prime_powers_coprime -*) -val prime_power_divisors_pairwise_coprime = store_thm( - "prime_power_divisors_pairwise_coprime", - ``!n. PAIRWISE_COPRIME (prime_power_divisors n)``, - metis_tac[prime_power_divisors_element, prime_powers_eq, prime_powers_coprime]); - -(* Theorem: prime_power_divisors 1 = {} *) -(* Proof: - x IN prime_power_divisors 1 - <=> ?p. (x = p ** ppidx n) /\ prime p /\ p divides 1 by prime_power_divisors_element - <=> ?p. (x = p ** ppidx n) /\ prime 1 by DIVIDES_ONE - <=> F by NOT_PRIME_1 -*) -val prime_power_divisors_1 = store_thm( - "prime_power_divisors_1", - ``prime_power_divisors 1 = {}``, - rw[prime_power_divisors_element, EXTENSION]); - -(* ------------------------------------------------------------------------- *) -(* Factorisations *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 0 < n ==> (n = PROD_SET (prime_power_divisors n)) *) -(* Proof: - Let s = prime_power_divisors n. - The goal becomes: n = PROD_SET s - Note FINITE s by prime_power_divisors_finite - - Claim: (PROD_SET s) divides n - Proof: Note !z. z IN s <=> - ?p. (z = p ** ppidx n) /\ prime p /\ p divides n by prime_power_divisors_element - ==> !z. z IN s ==> z divides n by prime_power_index_def - - Note PAIRWISE_COPRIME s by prime_power_divisors_pairwise_coprime - Thus set_lcm s = PROD_SET s by pairwise_coprime_prod_set_eq_set_lcm - But (set_lcm s) divides n by set_lcm_is_least_common_multiple - ==> PROD_SET s divides n by above - - Therefore, ?q. n = q * PROD_SET s by divides_def, Claim. - Claim: q = 1 - Proof: By contradiction, suppose q <> 1. - Then ?p. prime p /\ p divides q by PRIME_FACTOR - Let u = p ** ppidx n, v = n DIV u. - Then u divides n /\ coprime p v by prime_power_index_def, 0 < n, prime p - Note 0 < p by PRIME_POS - ==> 0 < u by EXP_POS, 0 < p - Thus n = v * u by DIVIDES_EQN, 0 < u - - Claim: u divides (PROD_SET s) - Proof: Note q divides n by divides_def, MULT_COMM - ==> p divides n by DIVIDES_TRANS - ==> p IN (prime_divisors n) by prime_divisors_element - ==> u IN s by prime_power_divisors_element_alt - Thus u divides (PROD_SET s) by PROD_SET_ELEMENT_DIVIDES, FINITE s - - Hence ?t. PROD_SET s = t * u by divides_def, u divides (PROD_SET s) - or v * u = n = q * (t * u) by above - = (q * t) * u by MULT_ASSOC - ==> v = q * t by MULT_RIGHT_CANCEL, NOT_ZERO_LT_ZERO - But p divideq q by above - ==> p divides v by DIVIDES_MULT - Note 1 < p by ONE_LT_PRIME - ==> ~(coprime p v) by coprime_not_divides - This contradicts coprime p v. - - Thus n = q * PROD_SET s, and q = 1 by Claim - or n = PROD_SET s by MULT_LEFT_1 -*) -val prime_factorisation = store_thm( - "prime_factorisation", - ``!n. 0 < n ==> (n = PROD_SET (prime_power_divisors n))``, - rpt strip_tac >> - qabbrev_tac `s = prime_power_divisors n` >> - `FINITE s` by rw[prime_power_divisors_finite, Abbr`s`] >> - `(PROD_SET s) divides n` by - (`!z. z IN s ==> z divides n` by metis_tac[prime_power_divisors_element, prime_power_index_def] >> - `PAIRWISE_COPRIME s` by metis_tac[prime_power_divisors_pairwise_coprime, Abbr`s`] >> - metis_tac[pairwise_coprime_prod_set_eq_set_lcm, set_lcm_is_least_common_multiple]) >> - `?q. n = q * PROD_SET s` by rw[GSYM divides_def] >> - `q = 1` by - (spose_not_then strip_assume_tac >> - `?p. prime p /\ p divides q` by rw[PRIME_FACTOR] >> - qabbrev_tac `u = p ** ppidx n` >> - qabbrev_tac `v = n DIV u` >> - `u divides n /\ coprime p v` by rw[prime_power_index_def, Abbr`u`, Abbr`v`] >> - `0 < u` by rw[EXP_POS, PRIME_POS, Abbr`u`] >> - `n = v * u` by rw[GSYM DIVIDES_EQN, Abbr`v`] >> - `u divides (PROD_SET s)` by - (`p divides n` by metis_tac[divides_def, MULT_COMM, DIVIDES_TRANS] >> - `p IN (prime_divisors n)` by rw[prime_divisors_element] >> - `u IN s` by metis_tac[prime_power_divisors_element_alt] >> - rw[PROD_SET_ELEMENT_DIVIDES]) >> - `?t. PROD_SET s = t * u` by rw[GSYM divides_def] >> - `v = q * t` by metis_tac[MULT_RIGHT_CANCEL, MULT_ASSOC, NOT_ZERO_LT_ZERO] >> - `p divides v` by rw[DIVIDES_MULT] >> - `1 < p` by rw[ONE_LT_PRIME] >> - metis_tac[coprime_not_divides]) >> - rw[]); - -(* This is a little milestone theorem. *) - -(* Theorem: 0 < n ==> (n = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n))) *) -(* Proof: by prime_factorisation, prime_power_divisors_def *) -val basic_prime_factorisation = store_thm( - "basic_prime_factorisation", - ``!n. 0 < n ==> (n = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n)))``, - rw[prime_factorisation, GSYM prime_power_divisors_def]); - -(* Theorem: 0 < n /\ m divides n ==> (m = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors n))) *) -(* Proof: - Note 0 < m by ZERO_DIVIDES, 0 < n - Let s = prime_divisors n, t = IMAGE (\p. p ** ppidx m) s. - The goal is: m = PROD_SET t - - Note FINITE s by prime_divisors_finite - ==> FINITE t by IMAGE_FINITE - and PAIRWISE_COPRIME t by prime_divisors_element, prime_powers_coprime - - By DIVIDES_ANTISYM, this is to show: - (1) m divides PROD_SET t - Let r = prime_divisors m - Then m = PROD_SET (IMAGE (\p. p ** ppidx m) r) by basic_prime_factorisation - and r SUBSET s by prime_divisors_divisor_subset - ==> (IMAGE (\p. p ** ppidx m) r) SUBSET t by IMAGE_SUBSET - ==> m divides PROD_SET t by pairwise_coprime_prod_set_subset_divides - (2) PROD_SET t divides m - Claim: !x. x IN t ==> x divides m - Proof: Note ?p. p IN s /\ (x = p ** (ppidx m)) by IN_IMAGE - and prime p by prime_divisors_element - so 1 < p by ONE_LT_PRIME - Now p ** ppidx m divides m by prime_power_factor_divides - or x divides m by above - Hence PROD_SET t divides m by pairwise_coprime_prod_set_divides -*) -val divisor_prime_factorisation = store_thm( - "divisor_prime_factorisation", - ``!m n. 0 < n /\ m divides n ==> (m = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors n)))``, - rpt strip_tac >> - `0 < m` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - qabbrev_tac `s = prime_divisors n` >> - qabbrev_tac `t = IMAGE (\p. p ** ppidx m) s` >> - `FINITE s` by rw[prime_divisors_finite, Abbr`s`] >> - `FINITE t` by rw[Abbr`t`] >> - `PAIRWISE_COPRIME t` by - (rw[Abbr`t`] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> - rw[prime_powers_coprime]) >> - (irule DIVIDES_ANTISYM >> rpt conj_tac) >| [ - qabbrev_tac `r = prime_divisors m` >> - `m = PROD_SET (IMAGE (\p. p ** ppidx m) r)` by rw[basic_prime_factorisation, Abbr`r`] >> - `r SUBSET s` by rw[prime_divisors_divisor_subset, Abbr`r`, Abbr`s`] >> - metis_tac[pairwise_coprime_prod_set_subset_divides, IMAGE_SUBSET], - `!x. x IN t ==> x divides m` by - (rpt strip_tac >> - qabbrev_tac `f = \p:num. p ** (ppidx m)` >> - `?p. p IN s /\ (x = p ** (ppidx m))` by metis_tac[IN_IMAGE] >> - `prime p` by metis_tac[prime_divisors_element] >> - rw[prime_power_factor_divides]) >> - rw[pairwise_coprime_prod_set_divides] - ]); - -(* Theorem: 0 < m /\ 0 < n ==> - (gcd m n = PROD_SET (IMAGE (\p. p ** (MIN (ppidx m) (ppidx n))) - ((prime_divisors m) INTER (prime_divisors n)))) *) -(* Proof: - Let sm = prime_divisors m, sn = prime_divisors n, s = sm INTER sn. - Let tm = IMAGE (\p. p ** ppidx m) sm, tn = IMAGE (\p. p ** ppidx m) sn, - t = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) s. - The goal is: gcd m n = PROD_SET t - - By GCD_PROPERTY, this is to show: - (1) PROD_SET t divides m /\ PROD_SET t divides n - Note FINITE sm /\ FINITE sn by prime_divisors_finite - ==> FINITE s by FINITE_INTER - and FINITE tm /\ FINITE tn /\ FINITE t by IMAGE_FINITE - Also PAIRWISE_COPRIME t by IN_INTER, prime_divisors_element, prime_powers_coprime - - Claim: !x. x IN t ==> x divides m /\ x divides n - Prood: Note x IN t - ==> ?p. p IN s /\ x = p ** MIN (ppidx m) (ppidx n) by IN_IMAGE - ==> p IN sm /\ p IN sn by IN_INTER - Note prime p by prime_divisors_element - ==> p ** ppidx m divides m by prime_power_factor_divides - and p ** ppidx n divides n by prime_power_factor_divides - Note MIN (ppidx m) (ppidx n) <= ppidx m by MIN_DEF - and MIN (ppidx m) (ppidx n) <= ppidx n by MIN_DEF - ==> x divides p ** ppidx m by prime_power_divides_iff - and x divides p ** ppidx n by prime_power_divides_iff - or x divides m /\ x divides n by DIVIDES_TRANS - - Therefore, PROD_SET t divides m by pairwise_coprime_prod_set_divides, Claim - and PROD_SET t divides n by pairwise_coprime_prod_set_divides, Claim - - (2) !x. x divides m /\ x divides n ==> x divides PROD_SET t - Let k = PROD_SET t, sx = prime_divisors x, tx = IMAGE (\p. p ** ppidx x) sx. - Note 0 < x by ZERO_DIVIDES, 0 < m - and x = PROD_SET tx by basic_prime_factorisation, 0 < x - The aim is to show: PROD_SET tx divides k - - Note FINITE sx by prime_divisors_finite - ==> FINITE tx by IMAGE_FINITE - and PAIRWISE_COPRIME tx by prime_divisors_element, prime_powers_coprime - - Claim: !z. z IN tx ==> z divides k - Proof: Note z IN tx - ==> ?p. p IN sx /\ (z = p ** ppidx x) by IN_IMAGE - Note prime p by prime_divisors_element - and sx SUBSET sm /\ sx SUBSET sn by prime_divisors_divisor_subset, x | m, x | n - ==> p IN sm /\ p IN sn by SUBSET_DEF - or p IN s by IN_INTER - Also ppidx x <= MIN (ppidx m) (ppidx n) by prime_power_index_common_divisor - ==> z divides p ** MIN (ppidx m) (ppidx n) by prime_power_divides_iff - But p ** MIN (ppidx m) (ppidx n) IN t by IN_IMAGE - ==> p ** MIN (ppidx m) (ppidx n) divides k by PROD_SET_ELEMENT_DIVIDES - or z divides k by DIVIDES_TRANS - - Therefore, PROD_SET tx divides k by pairwise_coprime_prod_set_divides -*) -val gcd_prime_factorisation = store_thm( - "gcd_prime_factorisation", - ``!m n. 0 < m /\ 0 < n ==> - (gcd m n = PROD_SET (IMAGE (\p. p ** (MIN (ppidx m) (ppidx n))) - ((prime_divisors m) INTER (prime_divisors n))))``, - rpt strip_tac >> - qabbrev_tac `sm = prime_divisors m` >> - qabbrev_tac `sn = prime_divisors n` >> - qabbrev_tac `s = sm INTER sn` >> - qabbrev_tac `tm = IMAGE (\p. p ** ppidx m) sm` >> - qabbrev_tac `tn = IMAGE (\p. p ** ppidx m) sn` >> - qabbrev_tac `t = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) s` >> - `FINITE sm /\ FINITE sn /\ FINITE s` by rw[prime_divisors_finite, Abbr`sm`, Abbr`sn`, Abbr`s`] >> - `FINITE tm /\ FINITE tn /\ FINITE t` by rw[Abbr`tm`, Abbr`tn`, Abbr`t`] >> - `PAIRWISE_COPRIME t` by - (rw[Abbr`t`] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element, IN_INTER] >> - rw[prime_powers_coprime]) >> - `!x. x IN t ==> x divides m /\ x divides n` by - (ntac 2 strip_tac >> - qabbrev_tac `f = \p:num. p ** MIN (ppidx m) (ppidx n)` >> - `?p. p IN s /\ p IN sm /\ p IN sn /\ (x = p ** MIN (ppidx m) (ppidx n))` by metis_tac[IN_IMAGE, IN_INTER] >> - `prime p` by metis_tac[prime_divisors_element] >> - `p ** ppidx m divides m /\ p ** ppidx n divides n` by rw[prime_power_factor_divides] >> - `MIN (ppidx m) (ppidx n) <= ppidx m /\ MIN (ppidx m) (ppidx n) <= ppidx n` by rw[] >> - metis_tac[prime_power_divides_iff, DIVIDES_TRANS]) >> - rw[GCD_PROPERTY] >- - rw[pairwise_coprime_prod_set_divides] >- - rw[pairwise_coprime_prod_set_divides] >> - qabbrev_tac `k = PROD_SET t` >> - qabbrev_tac `sx = prime_divisors x` >> - qabbrev_tac `tx = IMAGE (\p. p ** ppidx x) sx` >> - `0 < x` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - `x = PROD_SET tx` by rw[basic_prime_factorisation, Abbr`tx`, Abbr`sx`] >> - `FINITE sx` by rw[prime_divisors_finite, Abbr`sx`] >> - `FINITE tx` by rw[Abbr`tx`] >> - `PAIRWISE_COPRIME tx` by - (rw[Abbr`tx`] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> - rw[prime_powers_coprime]) >> - `!z. z IN tx ==> z divides k` by - (rw[Abbr`tx`] >> - `prime p` by metis_tac[prime_divisors_element] >> - `p IN sm /\ p IN sn` by metis_tac[prime_divisors_divisor_subset, SUBSET_DEF] >> - `p IN s` by metis_tac[IN_INTER] >> - `ppidx x <= MIN (ppidx m) (ppidx n)` by rw[prime_power_index_common_divisor] >> - `(p ** ppidx x) divides p ** MIN (ppidx m) (ppidx n)` by rw[prime_power_divides_iff] >> - qabbrev_tac `f = \p:num. p ** MIN (ppidx m) (ppidx n)` >> - `p ** MIN (ppidx m) (ppidx n) IN t` by metis_tac[IN_IMAGE] >> - metis_tac[PROD_SET_ELEMENT_DIVIDES, DIVIDES_TRANS]) >> - rw[pairwise_coprime_prod_set_divides]); - -(* This is a major milestone theorem. *) - -(* Theorem: 0 < m /\ 0 < n ==> - (lcm m n = PROD_SET (IMAGE (\p. p ** (MAX (ppidx m) (ppidx n))) - ((prime_divisors m) UNION (prime_divisors n)))) *) -(* Proof: - Let sm = prime_divisors m, sn = prime_divisors n, s = sm UNION sn. - Let tm = IMAGE (\p. p ** ppidx m) sm, tn = IMAGE (\p. p ** ppidx m) sn, - t = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) s. - The goal is: lcm m n = PROD_SET t - - By LCM_PROPERTY, this is to show: - (1) m divides PROD_SET t /\ n divides PROD_SET t - Let k = PROD_SET t. - Note m = PROD_SET tm by basic_prime_factorisation, 0 < m - and n = PROD_SET tn by basic_prime_factorisation, 0 < n - Also PAIRWISE_COPRIME tm by prime_divisors_element, prime_powers_coprime - and PAIRWISE_COPRIME tn by prime_divisors_element, prime_powers_coprime - - Claim: !z. z IN tm ==> z divides k - Proof: Note z IN tm - ==> ?p. p IN sm /\ (z = p ** ppidx m) by IN_IMAGE - ==> p IN s by IN_UNION - and prime p by prime_divisors_element - Note ppidx m <= MAX (ppidx m) (ppidx n) by MAX_DEF - ==> z divides p ** MAX (ppidx m) (ppidx n) by prime_power_divides_iff - But p ** MAX (ppidx m) (ppidx n) IN t by IN_IMAGE - and p ** MAX (ppidx m) (ppidx n) divides k by PROD_SET_ELEMENT_DIVIDES - Thus z divides k by DIVIDES_TRANS - - Similarly, !z. z IN tn ==> z divides k - Hence (PROD_SET tm) divides k /\ (PROD_SET tn) divides k by pairwise_coprime_prod_set_divides - or m divides k /\ n divides k by above - - (2) m divides x /\ n divides x ==> PROD_SET t divides x - If x = 0, trivially true by ALL_DIVIDES_ZERO - If x <> 0, then 0 < x. - Note PAIRWISE_COPRIME t by prime_divisors_element, prime_powers_coprimem IN_UNION - - Claim: !z. z IN t ==> z divides x - Proof: Note z IN t - ==> ?p. p IN s /\ (z = p ** MAX (ppidx m) (ppidx n)) by IN_IMAGE - or prime p by prime_divisors_element, IN_UNION - Note MAX (ppidx m) (ppidx n) <= ppidx x by prime_power_index_common_multiple, 0 < x - so z divides p ** ppidx x by prime_power_divides_iff - But p ** ppidx x divides x by prime_power_factor_divides - Thus z divides x by DIVIDES_TRANS - Hence PROD_SET t divides x by pairwise_coprime_prod_set_divides -*) -val lcm_prime_factorisation = store_thm( - "lcm_prime_factorisation", - ``!m n. 0 < m /\ 0 < n ==> - (lcm m n = PROD_SET (IMAGE (\p. p ** (MAX (ppidx m) (ppidx n))) - ((prime_divisors m) UNION (prime_divisors n))))``, - rpt strip_tac >> - qabbrev_tac `sm = prime_divisors m` >> - qabbrev_tac `sn = prime_divisors n` >> - qabbrev_tac `s = sm UNION sn` >> - qabbrev_tac `tm = IMAGE (\p. p ** ppidx m) sm` >> - qabbrev_tac `tn = IMAGE (\p. p ** ppidx n) sn` >> - qabbrev_tac `t = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) s` >> - `FINITE sm /\ FINITE sn /\ FINITE s` by rw[prime_divisors_finite, Abbr`sm`, Abbr`sn`, Abbr`s`] >> - `FINITE tm /\ FINITE tn /\ FINITE t` by rw[Abbr`tm`, Abbr`tn`, Abbr`t`] >> - rw[LCM_PROPERTY] >| [ - qabbrev_tac `k = PROD_SET t` >> - `m = PROD_SET tm` by rw[basic_prime_factorisation, Abbr`tm`, Abbr`sm`] >> - `PAIRWISE_COPRIME tm` by - (rw[Abbr`tm`] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> - rw[prime_powers_coprime]) >> - `!z. z IN tm ==> z divides k` by - (rw[Abbr`tm`] >> - `prime p` by metis_tac[prime_divisors_element] >> - `p IN s` by metis_tac[IN_UNION] >> - `ppidx m <= MAX (ppidx m) (ppidx n)` by rw[] >> - `(p ** ppidx m) divides p ** MAX (ppidx m) (ppidx n)` by rw[prime_power_divides_iff] >> - qabbrev_tac `f = \p:num. p ** MAX (ppidx m) (ppidx n)` >> - `p ** MAX (ppidx m) (ppidx n) IN t` by metis_tac[IN_IMAGE] >> - metis_tac[PROD_SET_ELEMENT_DIVIDES, DIVIDES_TRANS]) >> - rw[pairwise_coprime_prod_set_divides], - qabbrev_tac `k = PROD_SET t` >> - `n = PROD_SET tn` by rw[basic_prime_factorisation, Abbr`tn`, Abbr`sn`] >> - `PAIRWISE_COPRIME tn` by - (rw[Abbr`tn`] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> - rw[prime_powers_coprime]) >> - `!z. z IN tn ==> z divides k` by - (rw[Abbr`tn`] >> - `prime p` by metis_tac[prime_divisors_element] >> - `p IN s` by metis_tac[IN_UNION] >> - `ppidx n <= MAX (ppidx m) (ppidx n)` by rw[] >> - `(p ** ppidx n) divides p ** MAX (ppidx m) (ppidx n)` by rw[prime_power_divides_iff] >> - qabbrev_tac `f = \p:num. p ** MAX (ppidx m) (ppidx n)` >> - `p ** MAX (ppidx m) (ppidx n) IN t` by metis_tac[IN_IMAGE] >> - metis_tac[PROD_SET_ELEMENT_DIVIDES, DIVIDES_TRANS]) >> - rw[pairwise_coprime_prod_set_divides], - Cases_on `x = 0` >- - rw[] >> - `0 < x` by decide_tac >> - `PAIRWISE_COPRIME t` by - (rw[Abbr`t`] >> - `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element, IN_UNION] >> - rw[prime_powers_coprime]) >> - `!z. z IN t ==> z divides x` by - (rw[Abbr`t`] >> - `prime p` by metis_tac[prime_divisors_element, IN_UNION] >> - `MAX (ppidx m) (ppidx n) <= ppidx x` by rw[prime_power_index_common_multiple] >> - `p ** MAX (ppidx m) (ppidx n) divides p ** ppidx x` by rw[prime_power_divides_iff] >> - `p ** ppidx x divides x` by rw[prime_power_factor_divides] >> - metis_tac[DIVIDES_TRANS]) >> - rw[pairwise_coprime_prod_set_divides] - ]); - -(* Another major milestone theorem. *) - -(* ------------------------------------------------------------------------- *) -(* GCD and LCM special coprime decompositions *) -(* ------------------------------------------------------------------------- *) - -(* -Notes -=|=== -Given two numbers m and n, with d = gcd m n, and cofactors a = m/d, b = n/d. -Is it true that gcd a n = 1 ? - -Take m = 2^3 * 3^2 = 8 * 9 = 72, n = 2^2 * 3^3 = 4 * 27 = 108 -Then gcd m n = d = 2^2 * 3^2 = 4 * 9 = 36, with cofactors a = 2, b = 3. -In this case, gcd a n = gcd 2 108 <> 1. -But lcm m n = 2^3 * 3^3 = 8 * 27 = 216 - -Ryan Vinroot's method: -Take m = 2^7 * 3^5 * 5^4 * 7^4 n = 2^6 * 3*7 * 5^4 * 11^4 -Then m = a b c d = (7^4) (5^4) (2^7) (3^5) - and n = x y z t = (11^4) (5^4) (3^7) (2^6) -Note b = y always, and lcm m n = a b c x z, gcd m n = d y z -Define P = a b c, Q = x z, then coprime P Q, and lcm P Q = a b c x z = lcm m n = P * Q - -a = PROD (all prime factors of m which are not prime factors of n) = 7^4 -b = PROD (all prime factors of m common with m and equal powers in both) = 5^4 -c = PROD (all prime factors of m common with m but more powers in m) = 2^7 -d = PROD (all prime factors of m common with m but more powers in n) = 3^5 - -x = PROD (all prime factors of n which are not prime factors of m) = 11^4 -y = PROD (all prime factors of n common with n and equal powers in both) = 5^4 -z = PROD (all prime factors of n common with n but more powers in n) = 3^7 -t = PROD (all prime factors of n common with n but more powers in m) = 2^6 - -Let d = gcd m n. If d <> 1, then it consists of prime powers, e.g. 2^3 * 3^2 * 5 -Since d is to take the minimal of prime powers common to both m n, -each prime power in d must occur fully in either m or n. -e.g. it may be the case that: m = 2^3 * 3 * 5 * a, n = 2 * 3^2 * 5 * b -where a, b don't have prime factors 2, 3, 5, and coprime a b. -and lcm m n = a * b * 2^3 * 3^2 * 5, taking the maximal of prime powers common to both. - = (a * 2^3) * (b * 3^2 * 5) = P * Q with coprime P Q. - -Ryan Vinroot's method (again): -Take m = 2^7 * 3^5 * 5^4 * 7^4 n = 2^6 * 3*7 * 5^4 * 11^4 -Then gcd m n = 2^6 * 3^5 * 5^4, lcm m n = 2^7 * 3^7 * 5^4 * 7^4 * 11^4 -Take d = 3^5 * 5^4 (compare m to gcd n m, take the full factors of gcd in m ) - e = gcd n m / d = 2^6 (take what is left over) -Then P = m / d = 2^7 * 7^4 - Q = n / e = 3^7 * 5^4 * 11^4 - so P | m, there is ord p = P. -and Q | n, there is ord q = Q. -and coprime P Q, so ord (p * q) = P * Q = lcm m n. - -d = PROD {p ** ppidx m | p | prime p /\ p | (gcd m n) /\ (ppidx (gcd n m) = ppidx m)} -e = gcd n m / d - -prime_factorisation |- !n. 0 < n ==> (n = PROD_SET (prime_power_divisors n) - -This is because: m = 2^7 * 3^5 * 5^4 * 7^4 * 11^0 - n = 2^6 * 3^7 * 5^4 * 7^0 * 11^4 -we know that gcd m n = 2^6 * 3^5 * 5^4 * 7^0 * 11^0 taking minimum - lcm m n = 2^7 * 3^7 * 5^4 * 7^4 * 11^4 taking maximum -Thus, using gcd m n as a guide, -pick d = 2^0 * 3^5 * 5^4 , taking common minimum, -Then P = m / d will remove these common minimum from m, -but Q = n / e = n / (gcd m n / d) = n * d / gcd m n will include their common maximum -This separation of prime factors keep coprime P Q, but P * Q = lcm m n. - -*) - -(* Overload the park sets *) -val _ = overload_on ("common_prime_divisors", - ``\m n. (prime_divisors m) INTER (prime_divisors n)``); -val _ = overload_on ("total_prime_divisors", - ``\m n. (prime_divisors m) UNION (prime_divisors n)``); -val _ = overload_on ("park_on", - ``\m n. {p | p IN common_prime_divisors m n /\ ppidx m <= ppidx n}``); -val _ = overload_on ("park_off", - ``\m n. {p | p IN common_prime_divisors m n /\ ppidx n < ppidx m}``); -(* Overload the parking divisor of GCD *) -val _ = overload_on("park", ``\m n. PROD_SET (IMAGE (\p. p ** ppidx m) (park_on m n))``); - -(* Note: -The basic one is park_on m n, defined only for 0 < m and 0 < n. -*) - -(* Theorem: p IN common_prime_divisors m n <=> p IN prime_divisors m /\ p IN prime_divisors n *) -(* Proof: by IN_INTER *) -val common_prime_divisors_element = store_thm( - "common_prime_divisors_element", - ``!m n p. p IN common_prime_divisors m n <=> p IN prime_divisors m /\ p IN prime_divisors n``, - rw[]); - -(* Theorem: 0 < m /\ 0 < n ==> FINITE (common_prime_divisors m n) *) -(* Proof: by prime_divisors_finite, FINITE_INTER *) -val common_prime_divisors_finite = store_thm( - "common_prime_divisors_finite", - ``!m n. 0 < m /\ 0 < n ==> FINITE (common_prime_divisors m n)``, - rw[prime_divisors_finite]); - -(* Theorem: PAIRWISE_COPRIME (common_prime_divisors m n) *) -(* Proof: - Note x IN prime_divisors m ==> prime x by prime_divisors_element - and y IN prime_divisors n ==> prime y by prime_divisors_element - and x <> y ==> coprime x y by primes_coprime -*) -val common_prime_divisors_pairwise_coprime = store_thm( - "common_prime_divisors_pairwise_coprime", - ``!m n. PAIRWISE_COPRIME (common_prime_divisors m n)``, - metis_tac[prime_divisors_element, primes_coprime, IN_INTER]); - -(* Theorem: PAIRWISE_COPRIME (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n)) *) -(* Proof: - Note (prime_divisors m) SUBSET prime by prime_divisors_subset_prime - so (common_prime_divisors m n) SUBSET prime by SUBSET_INTER_SUBSET - Thus true by pairwise_coprime_for_prime_powers -*) -val common_prime_divisors_min_image_pairwise_coprime = store_thm( - "common_prime_divisors_min_image_pairwise_coprime", - ``!m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n))``, - metis_tac[prime_divisors_subset_prime, SUBSET_INTER_SUBSET, pairwise_coprime_for_prime_powers]); - -(* Theorem: p IN total_prime_divisors m n <=> p IN prime_divisors m \/ p IN prime_divisors n *) -(* Proof: by IN_UNION *) -val total_prime_divisors_element = store_thm( - "total_prime_divisors_element", - ``!m n p. p IN total_prime_divisors m n <=> p IN prime_divisors m \/ p IN prime_divisors n``, - rw[]); - -(* Theorem: 0 < m /\ 0 < n ==> FINITE (total_prime_divisors m n) *) -(* Proof: by prime_divisors_finite, FINITE_UNION *) -val total_prime_divisors_finite = store_thm( - "total_prime_divisors_finite", - ``!m n. 0 < m /\ 0 < n ==> FINITE (total_prime_divisors m n)``, - rw[prime_divisors_finite]); - -(* Theorem: PAIRWISE_COPRIME (total_prime_divisors m n) *) -(* Proof: - Note x IN prime_divisors m ==> prime x by prime_divisors_element - and y IN prime_divisors n ==> prime y by prime_divisors_element - and x <> y ==> coprime x y by primes_coprime -*) -val total_prime_divisors_pairwise_coprime = store_thm( - "total_prime_divisors_pairwise_coprime", - ``!m n. PAIRWISE_COPRIME (total_prime_divisors m n)``, - metis_tac[prime_divisors_element, primes_coprime, IN_UNION]); - -(* Theorem: PAIRWISE_COPRIME (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n)) *) -(* Proof: - Note prime_divisors m SUBSET prime by prime_divisors_subset_prime - and prime_divisors n SUBSET prime by prime_divisors_subset_prime - so (total_prime_divisors m n) SUBSET prime by UNION_SUBSET - Thus true by pairwise_coprime_for_prime_powers -*) -val total_prime_divisors_max_image_pairwise_coprime = store_thm( - "total_prime_divisors_max_image_pairwise_coprime", - ``!m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n))``, - metis_tac[prime_divisors_subset_prime, UNION_SUBSET, pairwise_coprime_for_prime_powers]); - -(* Theorem: p IN park_on m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n *) -(* Proof: by IN_INTER *) -val park_on_element = store_thm( - "park_on_element", - ``!m n p. p IN park_on m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n``, - rw[] >> - metis_tac[]); - -(* Theorem: p IN park_off m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m *) -(* Proof: by IN_INTER *) -val park_off_element = store_thm( - "park_off_element", - ``!m n p. p IN park_off m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m``, - rw[] >> - metis_tac[]); - -(* Theorem: park_off m n = (common_prime_divisors m n) DIFF (park_on m n) *) -(* Proof: by EXTENSION, NOT_LESS_EQUAL *) -val park_off_alt = store_thm( - "park_off_alt", - ``!m n. park_off m n = (common_prime_divisors m n) DIFF (park_on m n)``, - rw[EXTENSION] >> - metis_tac[NOT_LESS_EQUAL]); - -(* Theorem: park_on m n SUBSET common_prime_divisors m n *) -(* Proof: by SUBSET_DEF *) -val park_on_subset_common = store_thm( - "park_on_subset_common", - ``!m n. park_on m n SUBSET common_prime_divisors m n``, - rw[SUBSET_DEF]); - -(* Theorem: park_off m n SUBSET common_prime_divisors m n *) -(* Proof: by SUBSET_DEF *) -val park_off_subset_common = store_thm( - "park_off_subset_common", - ``!m n. park_off m n SUBSET common_prime_divisors m n``, - rw[SUBSET_DEF]); - -(* Theorem: park_on m n SUBSET total_prime_divisors m n *) -(* Proof: by SUBSET_DEF *) -val park_on_subset_total = store_thm( - "park_on_subset_total", - ``!m n. park_on m n SUBSET total_prime_divisors m n``, - rw[SUBSET_DEF]); - -(* Theorem: park_off m n SUBSET total_prime_divisors m n *) -(* Proof: by SUBSET_DEF *) -val park_off_subset_total = store_thm( - "park_off_subset_total", - ``!m n. park_off m n SUBSET total_prime_divisors m n``, - rw[SUBSET_DEF]); - -(* Theorem: common_prime_divisors m n =|= park_on m n # park_off m n *) -(* Proof: - Let s = common_prime_divisors m n. - Note park_on m n SUBSET s by park_on_subset_common - and park_off m n = s DIFF (park_on m n) by park_off_alt - so s = park_on m n UNION park_off m n /\ - DISJOINT (park_on m n) (park_off m n) by partition_by_subset -*) -val park_on_off_partition = store_thm( - "park_on_off_partition", - ``!m n. common_prime_divisors m n =|= park_on m n # park_off m n``, - metis_tac[partition_by_subset, park_on_subset_common, park_off_alt]); - -(* Theorem: 1 NOTIN (IMAGE (\p. p ** ppidx m) (park_off m n)) *) -(* Proof: - By contradiction, suppse 1 IN (IMAGE (\p. p ** ppidx m) (park_off m n)). - Then ?p. p IN park_off m n /\ (1 = p ** ppidx m) by IN_IMAGE - or p IN prime_divisors m /\ - p IN prime_divisors n /\ ppidx n < ppidx m by park_off_element - But prime p by prime_divisors_element - and p <> 1 by NOT_PRIME_1 - Thus ppidx m = 0 by EXP_EQ_1 - or ppidx n < 0, which is F by NOT_LESS_0 -*) -val park_off_image_has_not_1 = store_thm( - "park_off_image_has_not_1", - ``!m n. 1 NOTIN (IMAGE (\p. p ** ppidx m) (park_off m n))``, - rw[] >> - spose_not_then strip_assume_tac >> - `prime p` by metis_tac[prime_divisors_element] >> - `p <> 1` by metis_tac[NOT_PRIME_1] >> - decide_tac); - -(* -For the example, -This is because: m = 2^7 * 3^5 * 5^4 * 7^4 * 11^0 - n = 2^6 * 3^7 * 5^4 * 7^0 * 11^4 -we know that gcd m n = 2^6 * 3^5 * 5^4 * 7^0 * 11^0 taking minimum - lcm m n = 2^7 * 3^7 * 5^4 * 7^4 * 11^4 taking maximum -Thus, using gcd m n as a guide, -pick d = 2^0 * 3^5 * 5^4 , taking common minimum, -Then P = m / d will remove these common minimum from m, -but Q = n / e = n / (gcd m n / d) = n * d / gcd m n will include their common maximum -This separation of prime factors keep coprime P Q, but P * Q = lcm m n. -common_prime_divisors m n = {2; 3; 5} s = {2^6; 3^5; 5^4} with MIN -park_on m n = {3; 5} u = IMAGE (\p. p ** ppidx m) (park_on m n) = {3^5; 5^4} -park_off m n = {2} v = IMAGE (\p. p ** ppidx n) (park_off m n) = {2^6} -Note IMAGE (\p. p ** ppidx m) (park_off m n) = {2^7} -and IMAGE (\p. p ** ppidx n) (park_on m n) = {3^7; 5^4} - -total_prime_divisors m n = {2; 3; 5; 7; 11} s = {2^7; 3^7; 5^4; 7^4; 11^4} with MAX -sm = (prime_divisors m) DIFF (park_on m n) = {2; 7}, u = IMAGE (\p. p ** ppidx m) sm = {2^7; 7^4} -sn = (prime_divisors n) DIFF (park_off m n) = {3; 5; 11}, v = IMAGE (\p. p ** ppidx n) sn = {3^7; 5^4; 11^4} - -park_on_element -|- !m n p. p IN park_on m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n -park_off_element -|- !m n p. p IN park_off m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m -*) - -(* Theorem: let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n) in - let u = IMAGE (\p. p ** ppidx m) (park_on m n) in - let v = IMAGE (\p. p ** ppidx n) (park_off m n) in - 0 < m ==> s =|= u # v *) -(* Proof: - This is to show: - (1) s = u UNION v - By EXTENSION, this is to show: - (a) !x. x IN s ==> x IN u \/ x IN v by IN_UNION - Note x IN s - ==> ?p. (x = p ** MIN (ppidx m) (ppidx n)) /\ - p IN common_prime_divisors m n by IN_IMAGE - If ppidx m <= ppidx n - Then MIN (ppidx m) (ppidx n) = ppidx m by MIN_DEF - and p IN park_on m n by common_prime_divisors_element, park_on_element - ==> x IN u by IN_IMAGE - If ~(ppidx m <= ppidx n), - so ppidx n < ppidx m by NOT_LESS_EQUAL - Then MIN (ppidx m) (ppidx n) = ppidx n by MIN_DEF - and p IN park_off m n by common_prime_divisors_element, park_off_element - ==> x IN v by IN_IMAGE - (b) x IN u ==> x IN s - Note x IN u - ==> ?p. (x = p ** ppidx m) /\ - p IN park_on m n by IN_IMAGE - ==> ppidx m <= ppidx n by park_on_element - Thus MIN (ppidx m) (ppidx n) = ppidx m by MIN_DEF - so p IN common_prime_divisors m n by park_on_subset_common, SUBSET_DEF - ==> x IN s by IN_IMAGE - (c) x IN v ==> x IN s - Note x IN v - ==> ?p. (x = p ** ppidx n) /\ - p IN park_off m n by IN_IMAGE - ==> ppidx n < ppidx m by park_off_element - Thus MIN (ppidx m) (ppidx n) = ppidx n by MIN_DEF - so p IN common_prime_divisors m n by park_off_subset_common, SUBSET_DEF - ==> x IN s by IN_IMAGE - (2) DISJOINT u v - This is to show: u INTER v = {} by DISJOINT_DEF - By contradiction, suppse u INTER v <> {}. - Then ?x. x IN u /\ x IN v by MEMBER_NOT_EMPTY, IN_INTER - Thus ?p. p IN park_on m n /\ (x = p ** ppidx m) by IN_IMAGE - and ?q. q IN park_off m n /\ (x = q ** prime_power_index q n) by IN_IMAGE - ==> prime p /\ prime q /\ p divides m by park_on_element, park_off_element - prime_divisors_element - Also 0 < ppidx m by prime_power_index_pos, p divides m, 0 < m - ==> p = q by prime_powers_eq - Thus p IN (park_on m n) INTER (park_off m n) by IN_INTER - But DISJOINT (park_on m n) (park_off m n) by park_on_off_partition - so there is a contradiction by IN_DISJOINT -*) -val park_on_off_common_image_partition = store_thm( - "park_on_off_common_image_partition", - ``!m n. let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n) in - let u = IMAGE (\p. p ** ppidx m) (park_on m n) in - let v = IMAGE (\p. p ** ppidx n) (park_off m n) in - 0 < m ==> s =|= u # v``, - rpt strip_tac >> - qabbrev_tac `f = \p:num. p ** MIN (ppidx m) (ppidx n)` >> - qabbrev_tac `f1 = \p:num. p ** ppidx m` >> - qabbrev_tac `f2 = \p:num. p ** ppidx n` >> - rw_tac std_ss[] >| [ - rw[EXTENSION, EQ_IMP_THM] >| [ - `?p. (x = p ** MIN (ppidx m) (ppidx n)) /\ p IN common_prime_divisors m n` by metis_tac[IN_IMAGE] >> - Cases_on `ppidx m <= ppidx n` >| [ - `MIN (ppidx m) (ppidx n) = ppidx m` by rw[MIN_DEF] >> - `p IN park_on m n` by metis_tac[common_prime_divisors_element, park_on_element] >> - metis_tac[IN_IMAGE], - `MIN (ppidx m) (ppidx n) = ppidx n` by rw[MIN_DEF] >> - `p IN park_off m n` by metis_tac[common_prime_divisors_element, park_off_element, NOT_LESS_EQUAL] >> - metis_tac[IN_IMAGE] - ], - `?p. (x = p ** ppidx m) /\ p IN park_on m n` by metis_tac[IN_IMAGE] >> - `ppidx m <= ppidx n` by metis_tac[park_on_element] >> - `MIN (ppidx m) (ppidx n) = ppidx m` by rw[MIN_DEF] >> - `p IN common_prime_divisors m n` by metis_tac[park_on_subset_common, SUBSET_DEF] >> - metis_tac[IN_IMAGE], - `?p. (x = p ** ppidx n) /\ p IN park_off m n` by metis_tac[IN_IMAGE] >> - `ppidx n < ppidx m` by metis_tac[park_off_element] >> - `MIN (ppidx m) (ppidx n) = ppidx n` by rw[MIN_DEF] >> - `p IN common_prime_divisors m n` by metis_tac[park_off_subset_common, SUBSET_DEF] >> - metis_tac[IN_IMAGE] - ], - rw[DISJOINT_DEF] >> - spose_not_then strip_assume_tac >> - `?x. x IN u /\ x IN v` by metis_tac[MEMBER_NOT_EMPTY, IN_INTER] >> - `?p. p IN park_on m n /\ (x = p ** ppidx m)` by prove_tac[IN_IMAGE] >> - `?q. q IN park_off m n /\ (x = q ** prime_power_index q n)` by prove_tac[IN_IMAGE] >> - `prime p /\ prime q /\ p divides m` by metis_tac[park_on_element, park_off_element, prime_divisors_element] >> - `0 < ppidx m` by rw[prime_power_index_pos] >> - `p = q` by metis_tac[prime_powers_eq] >> - metis_tac[park_on_off_partition, IN_DISJOINT] - ]); - -(* Theorem: 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in - (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ (gcd m n = a * b) /\ coprime a b *) -(* Proof: - Let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n), - u = IMAGE (\p. p ** ppidx m) (park_on m n), - v = IMAGE (\p. p ** ppidx n) (park_off m n). - Then s =|= u # v by park_on_off_common_image_partition - Let a = PROD_SET u, b = PROD_SET v, c = PROD_SET s. - Then FINITE s by common_prime_divisors_finite, IMAGE_FINITE, 0 < m, 0 < n - and PAIRWISE_COPRIME s by common_prime_divisors_min_image_pairwise_coprime - ==> (c = a * b) /\ coprime a b by pairwise_coprime_prod_set_partition - Note c = gcd m n by gcd_prime_factorisation - and a = park m n by notation - Note c <> 0 by GCD_EQ_0, 0 < m, 0 < n - Thus a <> 0, or 0 < a by MULT_EQ_0 - so b = c DIV a by DIV_SOLVE_COMM, 0 < a - Therefore, - b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n)) /\ - gcd m n = a * b /\ coprime a b by above -*) - -Theorem gcd_park_decomposition: - !m n. 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in - b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n)) /\ - gcd m n = a * b /\ coprime a b -Proof - rpt strip_tac >> - qabbrev_tac `s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n)` >> - qabbrev_tac `u = IMAGE (\p. p ** ppidx m) (park_on m n)` >> - qabbrev_tac `v = IMAGE (\p. p ** ppidx n) (park_off m n)` >> - `s =|= u # v` by metis_tac[park_on_off_common_image_partition] >> - qabbrev_tac `a = PROD_SET u` >> - qabbrev_tac `b = PROD_SET v` >> - qabbrev_tac `c = PROD_SET s` >> - `FINITE s` by rw[common_prime_divisors_finite, Abbr`s`] >> - `PAIRWISE_COPRIME s` by metis_tac[common_prime_divisors_min_image_pairwise_coprime] >> - `(c = a * b) /\ coprime a b` - by (simp[Abbr`a`, Abbr`b`, Abbr`c`] >> - metis_tac[pairwise_coprime_prod_set_partition]) >> - metis_tac[gcd_prime_factorisation, GCD_EQ_0, MULT_EQ_0, DIV_SOLVE_COMM, - NOT_ZERO_LT_ZERO] -QED - -(* Theorem: 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in - (gcd m n = a * b) /\ coprime a b *) -(* Proof: by gcd_park_decomposition *) -val gcd_park_decompose = store_thm( - "gcd_park_decompose", - ``!m n. 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in - (gcd m n = a * b) /\ coprime a b``, - metis_tac[gcd_park_decomposition]); - -(* -For the example: -total_prime_divisors m n = {2; 3; 5; 7; 11} s = {2^7; 3^7; 5^4; 7^4; 11^4} with MAX -sm = (prime_divisors m) DIFF (park_on m n) = {2; 7}, u = IMAGE (\p. p ** ppidx m) sm = {2^7; 7^4} -sn = (prime_divisors n) DIFF (park_off m n) = {3; 5; 11}, v = IMAGE (\p. p ** ppidx n) sn = {3^7; 5^4; 11^4} -*) - -(* Theorem: let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n) in - let u = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)) in - let v = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)) in - 0 < m /\ 0 < n ==> s =|= u # v *) -(* Proof: - This is to show: - (1) s = u UNION v - By EXTENSION, this is to show: - (a) x IN s ==> x IN u \/ x IN v - Note x IN s - ==> ?p. p IN total_prime_divisors m n /\ - (x = p ** MAX (ppidx m) (ppidx n)) by IN_IMAGE - By total_prime_divisors_element, - - If p IN prime_divisors m, - Then prime p /\ p divides m by prime_divisors_element - If p IN park_on m n, - Then p IN prime_divisors n /\ - ppidx m <= ppidx n by park_on_element - ==> MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF - Note DISJOINT (park_on m n) (park_off m n) by park_on_off_partition - Thus p NOTIN park_off m n by IN_DISJOINT - ==> p IN prime_divisors n DIFF park_off m n by IN_DIFF - Therefore x IN v by IN_IMAGE - If p NOTIN park_on m n, - Then p IN prime_divisors m DIFF park_on m n by IN_DIFF - By park_on_element, either [1] or [2]: - [1] p NOTIN prime_divisors n - Then ppidx n = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < n - ==> MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF - Therefore x IN u by IN_IMAGE - [2] ~(ppidx m <= ppidx n) - Then MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF - Therefore x IN u by IN_IMAGE - - If p IN prime_divisors n, - Then prime p /\ p divides n by prime_divisors_element - If p IN park_off m n, - Then p IN prime_divisors m /\ - ppidx n < ppidx m by park_off_element - ==> MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF - Note DISJOINT (park_on m n) (park_off m n) by park_on_off_partition - Thus p NOTIN park_on m n by IN_DISJOINT - ==> p IN prime_divisors m DIFF park_on m n by IN_DIFF - Therefore x IN u by IN_IMAGE - If p NOTIN park_off m n, - Then p IN prime_divisors n DIFF park_off m n by IN_DIFF - By park_off_element, either [1] or [2]: - [1] p NOTIN prime_divisors m - Then ppidx m = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < m - ==> MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF - Therefore x IN v by IN_IMAGE - [2] ~(ppidx n < ppidx m) - Then MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF - Therefore x IN v by IN_IMAGE - - (b) x IN u ==> x IN s - Note x IN u - ==> ?p. p IN prime_divisors m DIFF park_on m n /\ - (x = p ** ppidx m) by IN_IMAGE - Thus p IN prime_divisors m /\ p NOTIN park_on m n by IN_DIFF - Note p IN total_prime_divisors m n by total_prime_divisors_element - By park_on_element, either [1] or [2]: - [1] p NOTIN prime_divisors n - Then ppidx n = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < n - ==> MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF - Therefore x IN u by IN_IMAGE - [2] ~(ppidx m <= ppidx n) - Then MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF - Therefore x IN u by IN_IMAGE - - (c) x IN v ==> x IN s - Note x IN v - ==> ?p. p IN prime_divisors n DIFF park_off m n /\ - (x = p ** ppidx n) by IN_IMAGE - Thus p IN prime_divisors n /\ p NOTIN park_off m n by IN_DIFF - Note p IN total_prime_divisors m n by total_prime_divisors_element - By park_off_element, either [1] or [2]: - [1] p NOTIN prime_divisors m - Then ppidx m = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < m - ==> MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF - Therefore x IN v by IN_IMAGE - [2] ~(ppidx n < ppidx m) - Then MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF - Therefore x IN v by IN_IMAGE - - (2) DISJOINT u v - This is to show: u INTER v = {} by DISJOINT_DEF - By contradiction, suppse u INTER v <> {}. - Then ?x. x IN u /\ x IN v by MEMBER_NOT_EMPTY, IN_INTER - Note x IN u - ==> ?p. p IN prime_divisors m DIFF park_on m n /\ - (x = p ** ppidx m) by IN_IMAGE - and x IN v - ==> ?q. q IN prime_divisors n DIFF park_off m n /\ - (x = q ** prime_power_index q n) by IN_IMAGE - Thus p IN prime_divisors m /\ p NOTIN park_on m n by IN_DIFF - and q IN prime_divisors n /\ q NOTIN park_off m n by IN_DIFF [1] - Now prime p /\ prime q /\ p divides m by prime_divisors_element - and 0 < ppidx m by prime_power_index_pos, p divides m, 0 < m - ==> p = q by prime_powers_eq - Thus p IN common_prime_divisors m n by common_prime_divisors_element, [1] - ==> p IN park_on m n \/ p IN park_off m n by park_on_off_partition, IN_UNION - This is a contradiction with [1]. -*) -val park_on_off_total_image_partition = store_thm( - "park_on_off_total_image_partition", - ``!m n. let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n) in - let u = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)) in - let v = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)) in - 0 < m /\ 0 < n ==> s =|= u # v``, - rpt strip_tac >> - qabbrev_tac `f = \p:num. p ** MAX (ppidx m) (ppidx n)` >> - qabbrev_tac `f1 = \p:num. p ** ppidx m` >> - qabbrev_tac `f2 = \p:num. p ** ppidx n` >> - rw_tac std_ss[] >| [ - rw[EXTENSION, EQ_IMP_THM] >| [ - `?p. p IN total_prime_divisors m n /\ (x = p ** MAX (ppidx m) (ppidx n))` by metis_tac[IN_IMAGE] >> - `p IN prime_divisors m \/ p IN prime_divisors n` by rw[GSYM total_prime_divisors_element] >| [ - `prime p /\ p divides m` by metis_tac[prime_divisors_element] >> - Cases_on `p IN park_on m n` >| [ - `p IN prime_divisors n /\ ppidx m <= ppidx n` by metis_tac[park_on_element] >> - `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> - `p NOTIN park_off m n` by metis_tac[park_on_off_partition, IN_DISJOINT] >> - `p IN prime_divisors n DIFF park_off m n` by rw[] >> - metis_tac[IN_IMAGE], - `p IN prime_divisors m DIFF park_on m n` by rw[] >> - `p NOTIN prime_divisors n \/ ~(ppidx m <= ppidx n)` by metis_tac[park_on_element] >| [ - `ppidx n = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> - `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE], - `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE] - ] - ], - `prime p /\ p divides n` by metis_tac[prime_divisors_element] >> - Cases_on `p IN park_off m n` >| [ - `p IN prime_divisors m /\ ppidx n < ppidx m` by metis_tac[park_off_element] >> - `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> - `p NOTIN park_on m n` by metis_tac[park_on_off_partition, IN_DISJOINT] >> - `p IN prime_divisors m DIFF park_on m n` by rw[] >> - metis_tac[IN_IMAGE], - `p IN prime_divisors n DIFF park_off m n` by rw[] >> - `p NOTIN prime_divisors m \/ ~(ppidx n < ppidx m)` by metis_tac[park_off_element] >| [ - `ppidx m = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> - `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE], - `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE] - ] - ] - ], - `?p. p IN prime_divisors m DIFF park_on m n /\ (x = p ** ppidx m)` by prove_tac[IN_IMAGE] >> - `p IN prime_divisors m /\ p NOTIN park_on m n` by metis_tac[IN_DIFF] >> - `p IN total_prime_divisors m n` by rw[total_prime_divisors_element] >> - `p NOTIN prime_divisors n \/ ~(ppidx m <= ppidx n)` by metis_tac[park_on_element] >| [ - `ppidx n = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> - `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE], - `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE] - ], - `?p. p IN prime_divisors n DIFF park_off m n /\ (x = p ** ppidx n)` by prove_tac[IN_IMAGE] >> - `p IN prime_divisors n /\ p NOTIN park_off m n` by metis_tac[IN_DIFF] >> - `p IN total_prime_divisors m n` by rw[total_prime_divisors_element] >> - `p NOTIN prime_divisors m \/ ~(ppidx n < ppidx m)` by metis_tac[park_off_element] >| [ - `ppidx m = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> - `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE], - `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> - metis_tac[IN_IMAGE] - ] - ], - rw[DISJOINT_DEF] >> - spose_not_then strip_assume_tac >> - `?x. x IN u /\ x IN v` by metis_tac[MEMBER_NOT_EMPTY, IN_INTER] >> - `?p. p IN prime_divisors m DIFF park_on m n /\ (x = p ** ppidx m)` by prove_tac[IN_IMAGE] >> - `?q. q IN prime_divisors n DIFF park_off m n /\ (x = q ** prime_power_index q n)` by prove_tac[IN_IMAGE] >> - `p IN prime_divisors m /\ p NOTIN park_on m n` by metis_tac[IN_DIFF] >> - `q IN prime_divisors n /\ q NOTIN park_off m n` by metis_tac[IN_DIFF] >> - `prime p /\ prime q /\ p divides m` by metis_tac[prime_divisors_element] >> - `0 < ppidx m` by rw[prime_power_index_pos] >> - `p = q` by metis_tac[prime_powers_eq] >> - `p IN common_prime_divisors m n` by rw[common_prime_divisors_element] >> - metis_tac[park_on_off_partition, IN_UNION] - ]); - -(* Theorem: 0 < m /\ 0 < n ==> - let a = park m n in let b = gcd m n DIV a in - let p = m DIV a in let q = (a * n) DIV (gcd m n) in - (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ - (p = PROD_SET (IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)))) /\ - (q = PROD_SET (IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)))) /\ - (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q) *) -(* Proof: - Let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n), - u = IMAGE (\p. p ** ppidx m) (park_on m n), - v = IMAGE (\p. p ** ppidx n) (park_off m n), - h = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)), - k = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)), - a = PROD_SET u, b = PROD_SET v, c = PROD_SET s, p = PROD_SET h, q = PROD_SET k - x = IMAGE (\p. p ** ppidx m) (prime_divisors m), - y = IMAGE (\p. p ** ppidx n) (prime_divisors n), - Let g = gcd m n. - - Step 1: GCD - Note a = park m n by notation - and g = a * b by gcd_park_decomposition - - Step 2: LCM - Note c = lcm m n by lcm_prime_factorisation - Note s =|= h # k by park_on_off_total_image_partition - and FINITE (total_prime_divisors m n) by total_prime_divisors_finite, 0 < m, 0 < n - ==> FINITE s by IMAGE_FINITE - also PAIRWISE_COPRIME s by total_prime_divisors_max_image_pairwise_coprime - Thus (c = p * q) /\ coprime p q by pairwise_coprime_prod_set_partition - - Step 3: Identities - Note m = PROD_SET x by basic_prime_factorisation - n = PROD_SET y by basic_prime_factorisation - - For the identity: m = a * p - We need: PROD_SET x = PROD_SET u * PROD_SET h - This requires: x = u UNION h /\ DISJOINT u h, i.e. x =|= u # h - or partition: (prime_divisors m) --> (park_on m n) and (prime_divisors m) DIFF (park_on m n) - - Claim: m = a * p - Proof: Claim: h = x DIFF u - Proof: Let pk = park_on m n, pm = prime_divisors m, f = \p. p ** ppidx m. - Note pk SUBSET pm by park_on_element, prime_divisors_element, SUBSET_DEF - ==> INJ f pm UNIV by INJ_DEF, prime_divisors_element, - prime_power_index_pos, prime_powers_eq - x DIFF u - = (IMAGE f pm) DIFF (IMAGE f pk) by notation - = IMAGE f (pm DIFF pk) by IMAGE_DIFF - = h by notation - Note FINITE x by prime_divisors_finite, IMAGE_FINITE - and u SUBSET x by SUBSET_DEF, IMAGE_SUBSET - Thus x =|= u # h by partition_by_subset - ==> m = a * p by PROD_SET_PRODUCT_BY_PARTITION - - For the identity: n = b * q - We need: PROD_SET y = PROD_SET v * PROD_SET k - This requires: y = v UNION k /\ DISJOINT v k, i.e y =|= v # k - or partition: (prime_divisors n) --> (park_off m n) and (prime_divisors n) DIFF (park_off m n) - - Claim: n = b * q - Proof: Claim: k = y DIFF v - Proof: Let pk = park_off m n, pn = prime_divisors n, f = \p. p ** ppidx n. - Note pk SUBSET pn by park_off_element, prime_divisors_element, SUBSET_DEF - ==> INJ f pn UNIV by INJ_DEF, prime_divisors_element, - prime_power_index_pos, prime_powers_eq - y DIFF v - = (IMAGE f pn) DIFF (IMAGE f pk) by notation - = IMAGE f (pn DIFF pk) by IMAGE_DIFF - = k by notation - Note FINITE y by prime_divisors_finite, IMAGE_FINITE - and v SUBSET y by SUBSET_DEF, IMAGE_SUBSET - Thus y =|= v # k by partition_by_subset - ==> n = b * q by PROD_SET_PRODUCT_BY_PARTITION - - Proof better: - Note m * n = g * c by GCD_LCM - = (a * b) * (p * q) by above - = (a * p) * (b * q) by MULT_COMM, MULT_ASSOC - = m * (b * q) by m = a * p - Thus n = b * q by MULT_LEFT_CANCEL, 0 < m - - Thus g <> 0 /\ c <> 0 by GCD_EQ_0, LCM_EQ_0, m <> 0, n <> 0 - ==> p <> 0 /\ a <> 0 by MULT_EQ_0 - ==> b = g DIV a by DIV_SOLVE_COMM, 0 < a - ==> p = m DIV a by DIV_SOLVE_COMM, 0 < a - and q = c DIV p by DIV_SOLVE_COMM, 0 < p - Note g divides n by GCD_IS_GREATEST_COMMON_DIVISOR - so g divides a * n by DIVIDES_MULTIPLE - or a * n = a * (b * q) by n = b * q from Claim 2 - = (a * b) * q by MULT_ASSOC - = g * q by g = a * b - = q * g by MULT_COMM - so g divides a * n by divides_def - Thus q = c DIV p by above - = ((m * n) DIV g) DIV p by lcm_def, m <> 0, n <> 0 - = (m * n) DIV (g * p) by DIV_DIV_DIV_MULT, 0 < g, 0 < p - = ((a * p) * n) DIV (g * p) by m = a * p, Claim 1 - = (p * (a * n)) DIV (p * g) by MULT_COMM, MULT_ASSOC - = (a * n) DIV g by DIV_COMMON_FACTOR, 0 < p, g divides a * n - - This gives all the assertions: - (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ - (m = a * p) /\ (n = b * q) by MULT_COMM -*) - -Theorem lcm_park_decomposition: - !m n. - 0 < m /\ 0 < n ==> - let a = park m n ; b = gcd m n DIV a ; - p = m DIV a ; q = (a * n) DIV (gcd m n) - in - b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n)) /\ - p = PROD_SET (IMAGE (\p. p ** ppidx m) - ((prime_divisors m) DIFF (park_on m n))) /\ - q = PROD_SET (IMAGE (\p. p ** ppidx n) - ((prime_divisors n) DIFF (park_off m n))) /\ - lcm m n = p * q /\ coprime p q /\ gcd m n = a * b /\ m = a * p /\ - n = b * q -Proof - rpt strip_tac >> - qabbrev_tac ‘s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n)’ >> - qabbrev_tac ‘u = IMAGE (\p. p ** ppidx m) (park_on m n)’ >> - qabbrev_tac ‘v = IMAGE (\p. p ** ppidx n) (park_off m n)’ >> - qabbrev_tac ‘h = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n))’ >> - qabbrev_tac ‘k = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n))’ >> - qabbrev_tac ‘a = PROD_SET u’ >> - qabbrev_tac ‘b = PROD_SET v’ >> - qabbrev_tac ‘c = PROD_SET s’ >> - qabbrev_tac ‘p = PROD_SET h’ >> - qabbrev_tac ‘q = PROD_SET k’ >> - qabbrev_tac ‘x = IMAGE (\p. p ** ppidx m) (prime_divisors m)’ >> - qabbrev_tac ‘y = IMAGE (\p. p ** ppidx n) (prime_divisors n)’ >> - qabbrev_tac ‘g = gcd m n’ >> - ‘a = park m n’ by rw[Abbr‘a’] >> - ‘g = a * b’ by metis_tac[gcd_park_decomposition] >> - ‘c = lcm m n’ by rw[lcm_prime_factorisation, Abbr‘c’, Abbr‘s’] >> - ‘s =|= h # k’ by metis_tac[park_on_off_total_image_partition] >> - ‘FINITE s’ by rw[total_prime_divisors_finite, Abbr‘s’] >> - ‘PAIRWISE_COPRIME s’ - by metis_tac[total_prime_divisors_max_image_pairwise_coprime] >> - ‘(c = p * q) /\ coprime p q’ - by (simp[Abbr‘p’, Abbr‘q’, Abbr‘c’] >> - metis_tac[pairwise_coprime_prod_set_partition]) >> - ‘m = PROD_SET x’ by rw[basic_prime_factorisation, Abbr‘x’] >> - ‘n = PROD_SET y’ by rw[basic_prime_factorisation, Abbr‘y’] >> - ‘m = a * p’ - by (‘h = x DIFF u’ - by (‘park_on m n SUBSET prime_divisors m’ - by metis_tac[park_on_element,prime_divisors_element,SUBSET_DEF] >> - ‘INJ (\p. p ** ppidx m) (prime_divisors m) UNIV’ - by (rw[INJ_DEF] >> - metis_tac[prime_divisors_element, prime_power_index_pos, - prime_powers_eq]) >> - metis_tac[IMAGE_DIFF]) >> - ‘FINITE x’ by rw[prime_divisors_finite, Abbr‘x’] >> - ‘u SUBSET x’ by rw[SUBSET_DEF, Abbr‘u’, Abbr‘x’] >> - ‘x =|= u # h’ by metis_tac[partition_by_subset] >> - metis_tac[PROD_SET_PRODUCT_BY_PARTITION]) >> - ‘n = b * q’ - by (‘m * n = g * c’ by metis_tac[GCD_LCM] >> - ‘_ = (a * p) * (b * q)’ by rw[] >> - ‘_ = m * (b * q)’ by rw[] >> - metis_tac[MULT_LEFT_CANCEL, NOT_ZERO_LT_ZERO]) >> - ‘m <> 0 /\ n <> 0’ by decide_tac >> - ‘g <> 0 /\ c <> 0’ by metis_tac[GCD_EQ_0, LCM_EQ_0] >> - ‘p <> 0 /\ a <> 0’ by metis_tac[MULT_EQ_0] >> - ‘b = g DIV a’ by metis_tac[DIV_SOLVE_COMM, NOT_ZERO_LT_ZERO] >> - ‘p = m DIV a’ by metis_tac[DIV_SOLVE_COMM, NOT_ZERO_LT_ZERO] >> - ‘q = c DIV p’ by metis_tac[DIV_SOLVE_COMM, NOT_ZERO_LT_ZERO] >> - ‘g divides a * n’ by metis_tac[divides_def, MULT_ASSOC, MULT_COMM] >> - ‘c = (m * n) DIV g’ by metis_tac[lcm_def] >> - ‘q = (m * n) DIV (g * p)’ by metis_tac[DIV_DIV_DIV_MULT, NOT_ZERO_LT_ZERO] >> - ‘_ = (p * (a * n)) DIV (p * g)’ by metis_tac[MULT_COMM, MULT_ASSOC] >> - ‘_ = (a * n) DIV g’ by metis_tac[DIV_COMMON_FACTOR, NOT_ZERO_LT_ZERO] >> - metis_tac[] -QED - -(* Theorem: 0 < m /\ 0 < n ==> let a = park m n in let p = m DIV a in let q = (a * n) DIV (gcd m n) in - (lcm m n = p * q) /\ coprime p q *) -(* Proof: by lcm_park_decomposition *) -val lcm_park_decompose = store_thm( - "lcm_park_decompose", - ``!m n. 0 < m /\ 0 < n ==> let a = park m n in let p = m DIV a in let q = (a * n) DIV (gcd m n) in - (lcm m n = p * q) /\ coprime p q``, - metis_tac[lcm_park_decomposition]); - -(* Theorem: 0 < m /\ 0 < n ==> - let a = park m n in let b = gcd m n DIV a in - let p = m DIV a in let q = (a * n) DIV (gcd m n) in - (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q) *) -(* Proof: by lcm_park_decomposition *) -val lcm_gcd_park_decompose = store_thm( - "lcm_gcd_park_decompose", - ``!m n. 0 < m /\ 0 < n ==> - let a = park m n in let b = gcd m n DIV a in - let p = m DIV a in let q = (a * n) DIV (gcd m n) in - (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q)``, - metis_tac[lcm_park_decomposition]); - -(* ------------------------------------------------------------------------- *) -(* Consecutive LCM Recurrence *) -(* ------------------------------------------------------------------------- *) - -(* -> optionTheory.some_def; -val it = |- !P. $some P = if ?x. P x then SOME (@x. P x) else NONE: thm -*) - -(* -Cannot do this: Definition is schematic in the following variables: p - -val lcm_fun_def = Define` - lcm_fun n = if n = 0 then 1 - else if n = 1 then 1 - else if ?p k. 0 < k /\ prime p /\ (n = p ** k) then p * lcm_fun (n - 1) - else lcm_fun (n - 1) -`; -*) - -(* NOT this: -val lcm_fun_def = Define` - (lcm_fun 1 = 1) /\ - (lcm_fun (SUC n) = case some p. ?k. (SUC n = p ** k) of - SOME p => p * (lcm_fun n) - | NONE => lcm_fun n) -`; -*) - -(* -Question: don't know how to prove termination -(* Define the B(n) function *) -val lcm_fun_def = Define` - (lcm_fun 1 = 1) /\ - (lcm_fun n = case some p. ?k. 0 < k /\ prime p /\ (n = p ** k) of - SOME p => p * (lcm_fun (n - 1)) - | NONE => lcm_fun (n - 1)) -`; - -(* use a measure that is decreasing *) -e (WF_REL_TAC `measure (\n k. k * n)`); -e (rpt strip_tac); -*) - -(* Define the Consecutive LCM Function *) -val lcm_fun_def = Define` - (lcm_fun 0 = 1) /\ - (lcm_fun (SUC n) = if n = 0 then 1 else - case some p. ?k. 0 < k /\ prime p /\ (SUC n = p ** k) of - SOME p => p * (lcm_fun n) - | NONE => lcm_fun n) -`; - -(* Another possible definition -- but need to work with pairs: - -val lcm_fun_def = Define` - (lcm_fun 0 = 1) /\ - (lcm_fun (SUC n) = if n = 0 then 1 else - case some (p, k). 0 < k /\ prime p /\ (SUC n = p ** k) of - SOME (p, k) => p * (lcm_fun n) - | NONE => lcm_fun n) -`; - -By prime_powers_eq, when SOME, such (p, k) exists uniquely, or NONE. -*) - -(* Get components of definition *) -val lcm_fun_0 = save_thm("lcm_fun_0", lcm_fun_def |> CONJUNCT1); -(* val lcm_fun_0 = |- lcm_fun 0 = 1: thm *) -val lcm_fun_SUC = save_thm("lcm_fun_SUC", lcm_fun_def |> CONJUNCT2); -(* val lcm_fun_SUC = |- !n. lcm_fun (SUC n) = if n = 0 then 1 else - case some p. ?k. SUC n = p ** k of - NONE => lcm_fun n | SOME p => p * lcm_fun n: thm *) - -(* Theorem: lcm_fun 1 = 1 *) -(* Proof: - lcm_fun 1 - = lcm_fun (SUC 0) by ONE - = 1 by lcm_fun_def -*) -val lcm_fun_1 = store_thm( - "lcm_fun_1", - ``lcm_fun 1 = 1``, - rw_tac bool_ss[lcm_fun_def, ONE]); - -(* Theorem: lcm_fun 2 = 2 *) -(* Proof: - Note 2 = 2 ** 1 by EXP_1 - and prime 2 by PRIME_2 - and 0 < k /\ prime p /\ (2 ** 1 = p ** k) - ==> (p = 2) /\ (k = 1) by prime_powers_eq - - lcm_fun 2 - = lcm_fun (SUC 1) by TWO - = case some p. ?k. 0 < k /\ prime p /\ (SUC 1 = p ** k) of - SOME p => p * (lcm_fun 1) - | NONE => lcm_fun 1) by lcm_fun_def - = SOME 2 by some_intro, above - = 2 * (lcm_fun 1) by definition - = 2 * 1 by lcm_fun_1 - = 2 by arithmetic -*) -Theorem lcm_fun_2: - lcm_fun 2 = 2 -Proof - simp_tac bool_ss[lcm_fun_def, lcm_fun_1, TWO] >> - `prime 2 /\ (2 = 2 ** 1)` by rw[PRIME_2] >> - DEEP_INTRO_TAC some_intro >> - rw_tac std_ss[] - >- metis_tac[prime_powers_eq] >> - metis_tac[DECIDE``0 <> 1``] -QED - -(* Theorem: prime p /\ (?k. 0 < k /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = p * lcm_fun n) *) -(* Proof: by lcm_fun_def, prime_powers_eq *) -Theorem lcm_fun_suc_some: - !n p. prime p /\ (?k. 0 < k /\ (SUC n = p ** k)) ==> - lcm_fun (SUC n) = p * lcm_fun n -Proof - rw[lcm_fun_def] >> - DEEP_INTRO_TAC some_intro >> - rw_tac std_ss[] >> - metis_tac[prime_powers_eq, DECIDE “~(0 < 0)”] -QED - -(* Theorem: ~(?p k. 0 < k /\ prime p /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = lcm_fun n) *) -(* Proof: by lcm_fun_def *) -val lcm_fun_suc_none = store_thm( - "lcm_fun_suc_none", - ``!n. ~(?p k. 0 < k /\ prime p /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = lcm_fun n)``, - rw[lcm_fun_def] >> - DEEP_INTRO_TAC some_intro >> - rw_tac std_ss[] >> - `k <> 0` by decide_tac >> - metis_tac[]); - -(* Theorem: prime p /\ l <> [] /\ POSITIVE l ==> !x. MEM x l ==> ppidx x <= ppidx (list_lcm l) *) -(* Proof: - Note ppidx (list_lcm l) = MAX_LIST (MAP ppidx l) by list_lcm_prime_power_index - and MEM (ppidx x) (MAP ppidx l) by MEM_MAP, MEM x l - Thus ppidx x <= ppidx (list_lcm l) by MAX_LIST_PROPERTY -*) -val list_lcm_prime_power_index_lower = store_thm( - "list_lcm_prime_power_index_lower", - ``!l p. prime p /\ l <> [] /\ POSITIVE l ==> !x. MEM x l ==> ppidx x <= ppidx (list_lcm l)``, - rpt strip_tac >> - `ppidx (list_lcm l) = MAX_LIST (MAP ppidx l)` by rw[list_lcm_prime_power_index] >> - `MEM (ppidx x) (MAP ppidx l)` by metis_tac[MEM_MAP] >> - rw[MAX_LIST_PROPERTY]); - -(* -The keys to show list_lcm_eq_lcm_fun are: -(1) Given a number n and a prime p that divides n, you can extract all the p's in n, - giving n = (p ** k) * q for some k, and coprime p q. This is FACTOR_OUT_PRIME, or FACTOR_OUT_POWER. -(2) To figure out the LCM, use the GCD_LCM identity, i.e. figure out first the GCD. - -Therefore, let m = consecutive LCM. -Consider given two number m, n; and a prime p with p divides n. -By (1), n = (p ** k) * q, with coprime p q. -If q > 1, then n = a * b where a, b are both less than n, and coprime a b: take a = p ** k, b = q. - Now, if a divides m, and b divides m --- which is the case when m = consecutive LCM, - By coprime a b, (a * b) divides m, or n divides m, - or gcd m n = n by divides_iff_gcd_fix - or lcm m n = (m * n) DIV (gcd m n) = (m * n) DIV n = m (or directly by divides_iff_lcm_fix) -If q = 1, then n is a pure prime p power: n = p ** k, with k > 0. - Now, m = (p ** j) * t with coprime p t, although it may be that j = 0. - For list LCM, j <= k, since the numbers are consecutive. In fact, j = k - 1 - Thus n = (p ** j) * p, and gcd m n = (p ** j) gcd p t = (p ** j) by GCD_COMMON_FACTOR - or lcm m n = (m * n) DIV (gcd m n) - = m * (n DIV (p ** j)) - = m * ((p ** j) * p) DIV (p ** j) - = m * p = p * m -*) - -(* Theorem: prime p /\ (n + 2 = p ** k) ==> (list_lcm [1 .. (n + 2)] = p * list_lcm [1 .. (n + 1)]) *) -(* Proof: - Note n + 2 = SUC (SUC n) <> 1 by ADD1, TWO - Thus p ** k <> 1, or k <> 0 by EXP_EQ_1 - ==> ?h. k = SUC h by num_CASES - and n + 2 = x ** SUC h by above - - Let l = [1 .. (n + 1)], m = list_lcm l. - Note POSITIVE l by leibniz_vertical_pos, EVERY_MEM - Now h < SUC h = k by LESS_SUC - so p ** h < p ** k = n + 2 by EXP_BASE_LT_MONO, 1 < p - ==> MEM (p ** h) l by leibniz_vertical_mem - Note l <> [] by leibniz_vertical_not_nil - so ppidx (p ** h) <= ppidx m by list_lcm_prime_power_index_lower - or h <= ppidx m by prime_power_index_prime_power - - Claim: ppidx m <= h - Proof: By contradiction, suppose h < ppidx m. - Then k <= ppidx m by k = SUC h - and p ** k divides p ** (ppidx m) by power_divides_iff - But p ** (ppidx m) divides m by prime_power_factor_divides - so p ** k divides m by DIVIDES_TRANS - ==> ?z. MEM z l /\ (n + 2) divides z by list_lcm_prime_power_factor_member - or (n + 2) <= z by DIVIDES_LE, 0 < z, all members are positive - Now z <= n + 1 by leibniz_vertical_mem - This leads to a contradiction: n + 2 <= n + 1. - - Therefore ppidx m = h by h <= ppidx m /\ ppidx m <= h, by Claim. - - list_lcm [1 .. (n + 2)] - = list_lcm (SNOC (n + 2) l) by leibniz_vertical_snoc, n + 2 = SUC (n + 1) - = lcm (n + 2) m by list_lcm_snoc - = p * m by lcm_special_for_prime_power -*) -val list_lcm_with_last_prime_power = store_thm( - "list_lcm_with_last_prime_power", - ``!n p k. prime p /\ (n + 2 = p ** k) ==> (list_lcm [1 .. (n + 2)] = p * list_lcm [1 .. (n + 1)])``, - rpt strip_tac >> - `n + 2 <> 1` by decide_tac >> - `0 <> k` by metis_tac[EXP_EQ_1] >> - `?h. k = SUC h` by metis_tac[num_CASES] >> - qabbrev_tac `l = leibniz_vertical n` >> - qabbrev_tac `m = list_lcm l` >> - `POSITIVE l` by rw[leibniz_vertical_pos, EVERY_MEM, Abbr`l`] >> - `h < k` by rw[] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `p ** h < p ** k` by rw[EXP_BASE_LT_MONO] >> - `0 < p ** h` by rw[PRIME_POS, EXP_POS] >> - `p ** h <= n + 1` by decide_tac >> - `MEM (p ** h) l` by rw[leibniz_vertical_mem, Abbr`l`] >> - `ppidx (p ** h) = h` by rw[prime_power_index_prime_power] >> - `l <> []` by rw[leibniz_vertical_not_nil, Abbr`l`] >> - `h <= ppidx m` by metis_tac[list_lcm_prime_power_index_lower] >> - `ppidx m <= h` by - (spose_not_then strip_assume_tac >> - `k <= ppidx m` by decide_tac >> - `p ** k divides p ** (ppidx m)` by rw[power_divides_iff] >> - `p ** (ppidx m) divides m` by rw[prime_power_factor_divides] >> - `p ** k divides m` by metis_tac[DIVIDES_TRANS] >> - `?z. MEM z l /\ (n + 2) divides z` by metis_tac[list_lcm_prime_power_factor_member] >> - `(n + 2) <= z` by rw[DIVIDES_LE] >> - `z <= n + 1` by metis_tac[leibniz_vertical_mem, Abbr`l`] >> - decide_tac) >> - `h = ppidx m` by decide_tac >> - `list_lcm [1 .. (n + 2)] = list_lcm (SNOC (n + 2) l)` by rw[GSYM leibniz_vertical_snoc, Abbr`l`] >> - `_ = lcm (n + 2) m` by rw[list_lcm_snoc, Abbr`m`] >> - `_ = p * m` by rw[lcm_special_for_prime_power] >> - rw[]); - -(* Theorem: (!p k. (k = 0) \/ ~prime p \/ n + 2 <> p ** k) ==> - (list_lcm [1 .. (n + 2)] = list_lcm [1 .. (n + 1)]) *) -(* Proof: - Note 1 < n + 2, - ==> ?a b. (n + 2 = a * b) /\ coprime a b /\ - 1 < a /\ 1 < b /\ a < n + 2 /\ b < n + 2 by prime_power_or_coprime_factors - or 0 < a /\ 0 < b /\ a <= n + 1 /\ b <= n + 1 by arithmetic - Let l = leibniz_vertical n, m = list_lcm l. - Then MEM a l and MEM b l by leibniz_vertical_mem - and a divides m /\ b divides m by list_lcm_is_common_multiple - ==> (n + 2) divides m by coprime_product_divides, coprime a b - - list_lcm (leibniz_vertical (n + 1)) - = list_lcm (SNOC (n + 2) l) by leibniz_vertical_snoc - = lcm (n + 2) m by list_lcm_snoc - = m by divides_iff_lcm_fix -*) -val list_lcm_with_last_non_prime_power = store_thm( - "list_lcm_with_last_non_prime_power", - ``!n. (!p k. (k = 0) \/ ~prime p \/ n + 2 <> p ** k) ==> - (list_lcm [1 .. (n + 2)] = list_lcm [1 .. (n + 1)])``, - rpt strip_tac >> - `1 < n + 2` by decide_tac >> - `!k. ~(0 < k) = (k = 0)` by decide_tac >> - `?a b. (n + 2 = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n + 2 /\ b < n + 2` by metis_tac[prime_power_or_coprime_factors] >> - `0 < a /\ 0 < b /\ a <= n + 1 /\ b <= n + 1` by decide_tac >> - qabbrev_tac `l = leibniz_vertical n` >> - qabbrev_tac `m = list_lcm l` >> - `MEM a l /\ MEM b l` by rw[leibniz_vertical_mem, Abbr`l`] >> - `a divides m /\ b divides m` by rw[list_lcm_is_common_multiple, Abbr`m`] >> - `(n + 2) divides m` by rw[coprime_product_divides] >> - `list_lcm [1 .. (n + 2)] = list_lcm (SNOC (n + 2) l)` by rw[GSYM leibniz_vertical_snoc, Abbr`l`] >> - `_ = lcm (n + 2) m` by rw[list_lcm_snoc, Abbr`m`] >> - `_ = m` by rw[GSYM divides_iff_lcm_fix] >> - rw[]); - -(* Theorem: list_lcm [1 .. (n + 1)] = lcm_fun (n + 1) *) -(* Proof: - By induction on n. - Base: list_lcm [1 .. 0 + 1] = lcm_fun (0 + 1) - LHS = list_lcm [1 .. 0 + 1] - = list_lcm [1] by leibniz_vertical_0 - = 1 by list_lcm_sing - RHS = lcm_fun (0 + 1) - = lcm_fun 1 by ADD - = 1 = LHS by lcm_fun_1 - Step: list_lcm [1 .. n + 1] = lcm_fun (n + 1) ==> - list_lcm [1 .. SUC n + 1] = lcm_fun (SUC n + 1) - Note (SUC n) <> 0 by SUC_NOT_ZERO - and n + 2 = SUC (SUC n) by ADD1, TWO - By lcm_fun_def, this is to show: - list_lcm [1 .. SUC n + 1] = case some p. ?k. 0 < k /\ prime p /\ (SUC (SUC n) = p ** k) of - NONE => lcm_fun (SUC n) - | SOME p => p * lcm_fun (SUC n) - - If SOME, - Then 0 < k /\ prime p /\ SUC (SUC n) = p ** k - This is the case of perfect prime power. - list_lcm (leibniz_vertical (SUC n)) - = list_lcm (leibniz_vertical (n + 1)) by ADD1 - = p * list_lcm (leibniz_vertical n) by list_lcm_with_last_prime_power - = p * lcm_fun (SUC n) by induction hypothesis - If NONE, - Then !x k. ~(0 < k) \/ ~prime x \/ SUC (SUC n) <> x ** k - This is the case of non-perfect prime power. - list_lcm (leibniz_vertical (SUC n)) - = list_lcm (leibniz_vertical (n + 1)) by ADD1 - = list_lcm (leibniz_vertical n) by list_lcm_with_last_non_prime_power - = lcm_fun (SUC n) by induction hypothesis -*) -val list_lcm_eq_lcm_fun = store_thm( - "list_lcm_eq_lcm_fun", - ``!n. list_lcm [1 .. (n + 1)] = lcm_fun (n + 1)``, - Induct >- - rw[leibniz_vertical_0, list_lcm_sing, lcm_fun_1] >> - `(SUC n) + 1 = SUC (SUC n)` by rw[] >> - `list_lcm [1 .. SUC n + 1] = case some p. ?k. 0 < k /\ prime p /\ ((SUC n) + 1 = p ** k) of - NONE => lcm_fun (SUC n) - | SOME p => p * lcm_fun (SUC n)` suffices_by rw[lcm_fun_def] >> - `n + 2 = (SUC n) + 1` by rw[] >> - DEEP_INTRO_TAC some_intro >> - rw[] >- - metis_tac[list_lcm_with_last_prime_power, ADD1] >> - metis_tac[list_lcm_with_last_non_prime_power, ADD1]); - -(* This is a major milestone theorem! *) - -(* Theorem: 2 ** n <= lcm_fun (SUC n) *) -(* Proof: - Note 2 ** n <= list_lcm (leibniz_vertical n) by lcm_lower_bound - and list_lcm (leibniz_vertical n) = lcm_fun (SUC n) by list_lcm_eq_lcm_fun\ - so 2 ** n <= lcm_fun (SUC n) -*) -val lcm_fun_lower_bound = store_thm( - "lcm_fun_lower_bound", - ``!n. 2 ** n <= lcm_fun (n + 1)``, - rw[GSYM list_lcm_eq_lcm_fun, lcm_lower_bound]); - -(* Theorem: 0 < n ==> 2 ** (n - 1) <= lcm_fun n *) -(* Proof: - Note 0 < n ==> ?m. n = SUC m by num_CASES - or m = n - 1 by SUC_SUB1 - Apply lcm_fun_lower_bound, - put n = SUC m, and the result follows. -*) -val lcm_fun_lower_bound_alt = store_thm( - "lcm_fun_lower_bound_alt", - ``!n. 0 < n ==> 2 ** (n - 1) <= lcm_fun n``, - rpt strip_tac >> - `n <> 0` by decide_tac >> - `?m. n = SUC m` by metis_tac[num_CASES] >> - `(n - 1 = m) /\ (n = m + 1)` by decide_tac >> - metis_tac[lcm_fun_lower_bound]); - -(* Theorem: 0 < n /\ prime p /\ (SUC n = p ** ppidx (SUC n)) ==> - (ppidx (SUC n) = SUC (ppidx (list_lcm [1 .. n]))) *) -(* Proof: - Let z = SUC n, - Then z = p ** ppidx z by given - Note n <> 0 /\ z <> 1 by 0 < n - ==> ppidx z <> 0 by EXP_EQ_1, z <> 1 - ==> ?h. ppidx z = SUC h by num_CASES - - Let l = [1 .. n], m = list_lcm l, j = ppidx m. - Current goal is to show: SUC h = SUC j - which only need to show: h = j by INV_SUC_EQ - Note l <> [] by listRangeINC_NIL - and POSITIVE l by listRangeINC_MEM, [1] - Also 1 < p by ONE_LT_PRIME - - Claim: h <= j - Proof: Note h < SUC h by LESS_SUC - Thus p ** h < z = p ** SUC h by EXP_BASE_LT_MONO, 1 < p - ==> p ** h <= n by z = SUC n - Also 0 < p ** h by EXP_POS, 0 < p - ==> MEM (p ** h) l by listRangeINC_MEM, 0 < p ** h /\ p ** h <= n - Note ppidx (p ** h) = h by prime_power_index_prime_power - Thus h <= j = ppidx m by list_lcm_prime_power_index_lower, l <> [] - - Claim: j <= h - Proof: By contradiction, suppose h < j. - Then SUC h <= j by arithmetic - ==> z divides p ** j by power_divides_iff, 1 < p, z = p ** SUC h, SUC h <= j - But p ** j divides m by prime_power_factor_divides - ==> z divides m by DIVIDES_TRANS - Thus ?y. MEM y l /\ z divides y by list_lcm_prime_power_factor_member, l <> [] - Note 0 < y by all members of l, [1] - so z <= y by DIVIDES_LE, 0 < y - or SUC n <= y by z = SUC n - But ?u. n = u + 1 by num_CASES, ADD1, n <> 0 - so y <= n by listRangeINC_MEM, MEM y l - This leads to SUC n <= n, a contradiction. - - By these two claims, h = j. -*) -val prime_power_index_suc_special = store_thm( - "prime_power_index_suc_special", - ``!n p. 0 < n /\ prime p /\ (SUC n = p ** ppidx (SUC n)) ==> - (ppidx (SUC n) = SUC (ppidx (list_lcm [1 .. n])))``, - rpt strip_tac >> - qabbrev_tac `z = SUC n` >> - `n <> 0 /\ z <> 1` by rw[Abbr`z`] >> - `?h. ppidx z = SUC h` by metis_tac[EXP_EQ_1, num_CASES] >> - qabbrev_tac `l = [1 .. n]` >> - qabbrev_tac `m = list_lcm l` >> - qabbrev_tac `j = ppidx m` >> - `h <= j /\ j <= h` suffices_by rw[] >> - `l <> []` by rw[listRangeINC_NIL, Abbr`l`] >> - `POSITIVE l` by rw[Abbr`l`] >> - `1 < p` by rw[ONE_LT_PRIME] >> - rpt strip_tac >| [ - `h < SUC h` by rw[] >> - `p ** h < z` by metis_tac[EXP_BASE_LT_MONO] >> - `p ** h <= n` by rw[Abbr`z`] >> - `0 < p ** h` by rw[EXP_POS] >> - `MEM (p ** h) l` by rw[Abbr`l`] >> - metis_tac[prime_power_index_prime_power, list_lcm_prime_power_index_lower], - spose_not_then strip_assume_tac >> - `SUC h <= j` by decide_tac >> - `z divides p ** j` by metis_tac[power_divides_iff] >> - `p ** j divides m` by rw[prime_power_factor_divides, Abbr`j`] >> - `z divides m` by metis_tac[DIVIDES_TRANS] >> - `?y. MEM y l /\ z divides y` by metis_tac[list_lcm_prime_power_factor_member] >> - `SUC n <= y` by rw[DIVIDES_LE, Abbr`z`] >> - `y <= n` by metis_tac[listRangeINC_MEM] >> - decide_tac - ]); - -(* Theorem: 0 < n /\ prime p /\ (n + 1 = p ** ppidx (n + 1)) ==> - (ppidx (n + 1) = 1 + (ppidx (list_lcm [1 .. n]))) *) -(* Proof: by prime_power_index_suc_special, ADD1, ADD_COMM *) -val prime_power_index_suc_property = store_thm( - "prime_power_index_suc_property", - ``!n p. 0 < n /\ prime p /\ (n + 1 = p ** ppidx (n + 1)) ==> - (ppidx (n + 1) = 1 + (ppidx (list_lcm [1 .. n])))``, - metis_tac[prime_power_index_suc_special, ADD1, ADD_COMM]); - -(* ------------------------------------------------------------------------- *) -(* Consecutive LCM Recurrence - Rework *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: SING (prime_divisors (n + 1)) ==> - (list_lcm [1 .. (n + 1)] = CHOICE (prime_divisors (n + 1)) * list_lcm [1 .. n]) *) -(* Proof: - Let z = n + 1. - Then ?p. prime_divisors z = {p} by SING_DEF - By CHOICE_SING, this is to show: list_lcm [1 .. z] = p * list_lcm [1 .. n] - - Note prime p /\ (z = p ** ppidx z) by prime_divisors_sing_property, CHOICE_SING - and z <> 1 /\ n <> 0 by prime_divisors_1, NOT_SING_EMPTY, ADD - Note ppidx z <> 0 by EXP_EQ_1, z <> 1 - ==> ?h. ppidx z = SUC h by num_CASES, EXP - Thus z = p ** SUC h = p ** h * p by EXP, MULT_COMM - - Let m = list_lcm [1 .. n], j = ppidx m. - Note EVERY_POSITIVE l by listRangeINC_MEM, EVERY_MEM - so 0 < m by list_lcm_pos - ==> ?q. (m = p ** j * q) /\ - coprime p q by prime_power_index_eqn - Note 0 < n by n <> 0 - Thus SUC h = SUC j by prime_power_index_suc_special, ADD1, 0 < n - or h = j by INV_SUC_EQ - - list_lcm [1 .. z] - = lcm z m by list_lcm_suc - = p * m by lcm_special_for_prime_power -*) -Theorem list_lcm_by_last_prime_power: - !n. - SING (prime_divisors (n + 1)) ==> - list_lcm [1 .. (n + 1)] = - CHOICE (prime_divisors (n + 1)) * list_lcm [1 .. n] -Proof - rpt strip_tac >> - qabbrev_tac ‘z = n + 1’ >> - ‘?p. prime_divisors z = {p}’ by rw[GSYM SING_DEF] >> - rw[] >> - ‘prime p /\ (z = p ** ppidx z)’ by metis_tac[prime_divisors_sing_property, CHOICE_SING] >> - ‘z <> 1 /\ n <> 0’ by metis_tac[prime_divisors_1, NOT_SING_EMPTY, ADD] >> - ‘?h. ppidx z = SUC h’ by metis_tac[EXP_EQ_1, num_CASES] >> - qabbrev_tac ‘m = list_lcm [1 .. n]’ >> - qabbrev_tac ‘j = ppidx m’ >> - ‘0 < m’ by rw[list_lcm_pos, EVERY_MEM, Abbr‘m’] >> - ‘?q. (m = p ** j * q) /\ coprime p q’ by metis_tac[prime_power_index_eqn] >> - ‘0 < n’ by decide_tac >> - ‘SUC h = SUC j’ by metis_tac[prime_power_index_suc_special, ADD1] >> - ‘h = j’ by decide_tac >> - ‘list_lcm [1 .. z] = lcm z m’ by rw[list_lcm_suc, Abbr‘z’, Abbr‘m’] >> - ‘_ = p * m’ by metis_tac[lcm_special_for_prime_power] >> - rw[] -QED - -(* Theorem: ~ SING (prime_divisors (n + 1)) ==> (list_lcm [1 .. (n + 1)] = list_lcm [1 .. n]) *) -(* Proof: - Let z = n + 1, l = [1 .. n], m = list_lcm l. - The goal is to show: list_lcm [1 .. z] = m. - - If z = 1, - Then n = 0 by 1 = n + 1 - LHS = list_lcm [1 .. z] - = list_lcm [1 .. 1] by z = 1 - = list_lcm [1] by listRangeINC_SING - = 1 by list_lcm_sing - RHS = list_lcm [1 .. n] - = list_lcm [1 .. 0] by n = 0 - = list_lcm [] by listRangeINC_EMPTY - = 1 = LHS by list_lcm_nil - If z <> 1, - Note z <> 0, or 0 < z by z = n + 1 - ==> ?p. prime p /\ p divides z by PRIME_FACTOR, z <> 1 - and 0 < ppidx z by prime_power_index_pos, 0 < z - Let t = p ** ppidx z. - Then ?q. (z = t * q) /\ coprime p q by prime_power_index_eqn, 0 < z - ==> coprime t q by coprime_exp - Thus t <> 0 /\ q <> 0 by MULT_EQ_0, z <> 0 - and q <> 1 by prime_divisors_sing, MULT_RIGHT_1, ~SING (prime_divisors z) - Note p <> 1 by NOT_PRIME_1 - and t <> 1 by EXP_EQ_1, ppidx z <> 0 - Thus 0 < q /\ q < n + 1 by z = t * q = n + 1 - and 0 < t /\ t < n + 1 by z = t * q = n + 1 - - Then MEM q l by listRangeINC_MEM, 1 <= q <= n - and MEM t l by listRangeINC_MEM, 1 <= t <= n - ==> q divides m /\ t divides m by list_lcm_is_common_multiple - ==> q * t = z divides m by coprime_product_divides, coprime t q - - list_lcm [1 .. z] - = lcm z m by list_lcm_suc - = m by divides_iff_lcm_fix -*) - -Theorem list_lcm_by_last_non_prime_power: - !n. ~ SING (prime_divisors (n + 1)) ==> - list_lcm [1 .. (n + 1)] = list_lcm [1 .. n] -Proof - rpt strip_tac >> - qabbrev_tac `z = n + 1` >> - Cases_on `z = 1` >| [ - `n = 0` by rw[Abbr`z`] >> - `([1 .. z] = [1]) /\ ([1 .. n] = [])` by rw[listRangeINC_EMPTY] >> - rw[list_lcm_sing, list_lcm_nil], - `z <> 0 /\ 0 < z` by rw[Abbr`z`] >> - `?p. prime p /\ p divides z` by rw[PRIME_FACTOR] >> - `0 < ppidx z` by rw[prime_power_index_pos] >> - qabbrev_tac `t = p ** ppidx z` >> - `?q. (z = t * q) /\ coprime p q /\ coprime t q` - by metis_tac[prime_power_index_eqn, coprime_exp] >> - `t <> 0 /\ q <> 0` by metis_tac[MULT_EQ_0] >> - `q <> 1` by metis_tac[prime_divisors_sing, MULT_RIGHT_1] >> - `t <> 1` by metis_tac[EXP_EQ_1, NOT_PRIME_1, NOT_ZERO_LT_ZERO] >> - `0 < q /\ q < n + 1` by rw[Abbr`z`] >> - `0 < t /\ t < n + 1` by rw[Abbr`z`] >> - qabbrev_tac `l = [1 .. n]` >> - qabbrev_tac `m = list_lcm l` >> - `MEM q l /\ MEM t l` by rw[Abbr`l`] >> - `q divides m /\ t divides m` - by simp[list_lcm_is_common_multiple, Abbr`m`] >> - `z divides m` - by (simp[] >> metis_tac[coprime_sym, coprime_product_divides]) >> - `list_lcm [1 .. z] = lcm z m` by rw[list_lcm_suc, Abbr`z`, Abbr`m`] >> - `_ = m` by rw[GSYM divides_iff_lcm_fix] >> - rw[] - ] -QED - -(* Theorem: list_lcm [1 .. (n + 1)] = let s = prime_divisors (n + 1) in - if SING s then CHOICE s * list_lcm [1 .. n] else list_lcm [1 .. n] *) -(* Proof: by list_lcm_with_last_prime_power, list_lcm_with_last_non_prime_power *) -val list_lcm_recurrence = store_thm( - "list_lcm_recurrence", - ``!n. list_lcm [1 .. (n + 1)] = let s = prime_divisors (n + 1) in - if SING s then CHOICE s * list_lcm [1 .. n] else list_lcm [1 .. n]``, - rw[list_lcm_by_last_prime_power, list_lcm_by_last_non_prime_power]); - -(* Theorem: (prime_divisors (n + 1) = {p}) ==> (list_lcm [1 .. (n + 1)] = p * list_lcm [1 .. n]) *) -(* Proof: by list_lcm_by_last_prime_power, SING_DEF *) -val list_lcm_option_last_prime_power = store_thm( - "list_lcm_option_last_prime_power", - ``!n p. (prime_divisors (n + 1) = {p}) ==> (list_lcm [1 .. (n + 1)] = p * list_lcm [1 .. n])``, - rw[list_lcm_by_last_prime_power, SING_DEF]); - -(* Theorem: (!p. prime_divisors (n + 1) <> {p}) ==> (list_lcm [1 .. (n + 1)] = list_lcm [1 .. n]) *) -(* Proof: by ist_lcm_by_last_non_prime_power, SING_DEF *) -val list_lcm_option_last_non_prime_power = store_thm( - "list_lcm_option_last_non_prime_power", - ``!n. (!p. prime_divisors (n + 1) <> {p}) ==> (list_lcm [1 .. (n + 1)] = list_lcm [1 .. n])``, - rw[list_lcm_by_last_non_prime_power, SING_DEF]); - -(* Theorem: list_lcm [1 .. (n + 1)] = case some p. (prime_divisors (n + 1)) = {p} of - NONE => list_lcm [1 .. n] - | SOME p => p * list_lcm [1 .. n] *) -(* Proof: - For SOME p, true by list_lcm_option_last_prime_power - For NONE, true by list_lcm_option_last_non_prime_power -*) -val list_lcm_option_recurrence = store_thm( - "list_lcm_option_recurrence", - ``!n. list_lcm [1 .. (n + 1)] = case some p. (prime_divisors (n + 1)) = {p} of - NONE => list_lcm [1 .. n] - | SOME p => p * list_lcm [1 .. n]``, - rpt strip_tac >> - DEEP_INTRO_TAC optionTheory.some_intro >> - rw[list_lcm_option_last_prime_power, list_lcm_option_last_non_prime_power]); - -(* ------------------------------------------------------------------------- *) -(* Relating Consecutive LCM to Prime Functions *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MEM x (SET_TO_LIST (prime_powers_upto n)) <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n *) -(* Proof: - Let s = prime_powers_upto n. - Then FINITE s by prime_powers_upto_finite - and !x. x IN s <=> MEM x (SET_TO_LIST s) by MEM_SET_TO_LIST - The result follows by prime_powers_upto_element -*) -val prime_powers_upto_list_mem = store_thm( - "prime_powers_upto_list_mem", - ``!n x. MEM x (SET_TO_LIST (prime_powers_upto n)) <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n``, - rw[MEM_SET_TO_LIST, prime_powers_upto_element, prime_powers_upto_finite]); - -(* -LOG_EQ_0 |- !a b. 1 < a /\ 0 < b ==> ((LOG a b = 0) <=> b < a) -*) - -(* Theorem: prime p /\ p <= n ==> p ** LOG p n divides set_lcm (prime_powers_upto n) *) -(* Proof: - Let s = prime_powers_upto n. - Note FINITE s by prime_powers_upto_finite - and p ** LOG p n IN s by prime_powers_upto_element_alt - ==> p ** LOG p n divides set_lcm s by set_lcm_is_common_multiple -*) -val prime_powers_upto_lcm_prime_to_log_divisor = store_thm( - "prime_powers_upto_lcm_prime_to_log_divisor", - ``!n p. prime p /\ p <= n ==> p ** LOG p n divides set_lcm (prime_powers_upto n)``, - rpt strip_tac >> - `FINITE (prime_powers_upto n)` by rw[prime_powers_upto_finite] >> - `p ** LOG p n IN prime_powers_upto n` by rw[prime_powers_upto_element_alt] >> - rw[set_lcm_is_common_multiple]); - -(* Theorem: prime p /\ p <= n ==> p divides set_lcm (prime_powers_upto n) *) -(* Proof: - Note 1 < p by ONE_LT_PRIME - so LOG p n <> 0 by LOG_EQ_0, 1 < p - ==> p divides p ** LOG p n by divides_self_power, 1 < p - - Note p ** LOG p n divides set_lcm s by prime_powers_upto_lcm_prime_to_log_divisor - Thus p divides set_lcm s by DIVIDES_TRANS -*) -val prime_powers_upto_lcm_prime_divisor = store_thm( - "prime_powers_upto_lcm_prime_divisor", - ``!n p. prime p /\ p <= n ==> p divides set_lcm (prime_powers_upto n)``, - rpt strip_tac >> - `1 < p` by rw[ONE_LT_PRIME] >> - `LOG p n <> 0` by rw[LOG_EQ_0] >> - `p divides p ** LOG p n` by rw[divides_self_power] >> - `p ** LOG p n divides set_lcm (prime_powers_upto n)` by rw[prime_powers_upto_lcm_prime_to_log_divisor] >> - metis_tac[DIVIDES_TRANS]); - -(* Theorem: prime p /\ p <= n ==> p ** ppidx n divides set_lcm (prime_powers_upto n) *) -(* Proof: - Note 1 < p by ONE_LT_PRIME - and 0 < n by p <= n - ==> ppidx n <= LOG p n by prime_power_index_le_log_index, 0 < n - Thus p ** ppidx n divides p ** LOG p n by power_divides_iff, 1 < p - and p ** LOG p n divides set_lcm (prime_powers_upto n) by prime_powers_upto_lcm_prime_to_log_divisor - or p ** ppidx n divides set_lcm (prime_powers_upto n) by DIVIDES_TRANS -*) -val prime_powers_upto_lcm_prime_to_power_divisor = store_thm( - "prime_powers_upto_lcm_prime_to_power_divisor", - ``!n p. prime p /\ p <= n ==> p ** ppidx n divides set_lcm (prime_powers_upto n)``, - rpt strip_tac >> - `1 < p` by rw[ONE_LT_PRIME] >> - `0 < n` by decide_tac >> - `ppidx n <= LOG p n` by rw[prime_power_index_le_log_index] >> - `p ** ppidx n divides p ** LOG p n` by rw[power_divides_iff] >> - `p ** LOG p n divides set_lcm (prime_powers_upto n)` by rw[prime_powers_upto_lcm_prime_to_log_divisor] >> - metis_tac[DIVIDES_TRANS]); - -(* The next theorem is based on this example: -Take n = 10, -prime_powers_upto 10 = {2^3; 3^2; 5^1; 7^1} = {8; 9; 5; 7} -set_lcm (prime_powers_upto 10) = 2520 -For any 1 <= x <= 10, e.g. x = 6. -6 <= 10, 6 divides set_lcm (prime_powers_upto 10). - -The reason is that: -6 = PROD_SET (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) by prime_factorisation -prime_divisors 6 = {2; 3} -Because 2, 3 <= 6, 6 <= 10, the divisors 2,3 <= 10 by DIVIDES_LE -Thus 2^(LOG 2 10) = 2^3, 3^(LOG 3 10) = 3^2 IN prime_powers_upto 10) by prime_powers_upto_element_alt -But 2^(ppidx 6) = 2^1 = 2 divides 6, 3^(ppidx 6) = 3^1 = 3 divides 6 by prime_power_index_def - so 2^(ppidx 6) <= 10 and 3^(ppidx 6) <= 10. - -In this example, 2^1 < 2^3 3^1 < 3^2 how to compare (ppidx x) with (LOG p n) in general? ## -Due to this, 2^(ppidx 6) divides 2^(LOG 2 10), by prime_powers_divide - and 3^(ppidx 6) divides 3^(LOG 3 10), -And 2^(LOG 2 10) divides set_lcm (prime_powers_upto 10) by prime_powers_upto_lcm_prime_to_log_divisor -and 3^(LOG 3 10) divides set_lcm (prime_powers_upto 10) by prime_powers_upto_lcm_prime_to_log_divisor -or !z. z IN (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) - ==> z divides set_lcm (prime_powers_upto 10) by verification -Hence set_lcm (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) divides set_lcm (prime_powers_upto 10) - by set_lcm_is_least_common_multiple -But PAIRWISE_COPRIME (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)), -Thus set_lcm (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) - = PROD_SET (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) by pairwise_coprime_prod_set_eq_set_lcm - = 6 by above -Hence x divides set_lcm (prime_powers_upto 10) - -## maybe: - ppidx x <= LOG p x by prime_power_index_le_log_index - LOG p x <= LOG p n by LOG_LE_MONO -*) - -(* Theorem: 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n) *) -(* Proof: - Note 0 < n by 0 < x /\ x <= n - Let m = set_lcm (prime_powers_upto n). - The goal becomes: x divides m. - - Let s = prime_power_divisors x. - Then x = PROD_SET s by prime_factorisation, 0 < x - - Claim: !z. z IN s ==> z divides m - Proof: By prime_power_divisors_element, this is to show: - prime p /\ p divides x ==> p ** ppidx x divides m - Note p <= x by DIVIDES_LE, 0 < x - Thus p <= n by p <= x, x <= n - ==> p ** LOG p n IN prime_powers_upto n by prime_powers_upto_element_alt, b <= n - ==> p ** LOG p n divides m by prime_powers_upto_lcm_prime_to_log_divisor - Note 1 < p by ONE_LT_PRIME - and ppidx x <= LOG p x by prime_power_index_le_log_index, 0 < n - also LOG p x <= LOG p n by LOG_LE_MONO, 1 < p - ==> ppidx x <= LOG p n by arithmetic - ==> p ** ppidx x divides p ** LOG p n by power_divides_iff, 1 < p - Thus p ** ppidx x divides m by DIVIDES_TRANS - - Note FINITE s by prime_power_divisors_finite - and set_lcm s divides m by set_lcm_is_least_common_multiple, FINITE s - Also PAIRWISE_COPRIME s by prime_power_divisors_pairwise_coprime - ==> PROD_SET s = set_lcm s by pairwise_coprime_prod_set_eq_set_lcm - Thus x divides m by set_lcm s divides m -*) -val prime_powers_upto_lcm_divisor = store_thm( - "prime_powers_upto_lcm_divisor", - ``!n x. 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n)``, - rpt strip_tac >> - `0 < n` by decide_tac >> - qabbrev_tac `m = set_lcm (prime_powers_upto n)` >> - qabbrev_tac `s = prime_power_divisors x` >> - `x = PROD_SET s` by rw[prime_factorisation, Abbr`s`] >> - `!z. z IN s ==> z divides m` by - (rw[prime_power_divisors_element, Abbr`s`] >> - `p <= x` by rw[DIVIDES_LE] >> - `p <= n` by decide_tac >> - `p ** LOG p n IN prime_powers_upto n` by rw[prime_powers_upto_element_alt] >> - `p ** LOG p n divides m` by rw[prime_powers_upto_lcm_prime_to_log_divisor, Abbr`m`] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `ppidx x <= LOG p x` by rw[prime_power_index_le_log_index] >> - `LOG p x <= LOG p n` by rw[LOG_LE_MONO] >> - `ppidx x <= LOG p n` by decide_tac >> - `p ** ppidx x divides p ** LOG p n` by rw[power_divides_iff] >> - metis_tac[DIVIDES_TRANS]) >> - `FINITE s` by rw[prime_power_divisors_finite, Abbr`s`] >> - `set_lcm s divides m` by rw[set_lcm_is_least_common_multiple] >> - metis_tac[prime_power_divisors_pairwise_coprime, pairwise_coprime_prod_set_eq_set_lcm]); - -(* This is a key result. *) - -(* ------------------------------------------------------------------------- *) -(* Consecutive LCM and Prime-related Sets *) -(* ------------------------------------------------------------------------- *) - -(* -Useful: -list_lcm_is_common_multiple |- !x l. MEM x l ==> x divides list_lcm l -list_lcm_prime_factor |- !p l. prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) -list_lcm_prime_factor_member |- !p l. prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x -prime_power_index_pos |- !n p. 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n -*) - -(* Theorem: lcm_run n = set_lcm (prime_powers_upto n) *) -(* Proof: - By DIVIDES_ANTISYM, this is to show: - (1) lcm_run n divides set_lcm (prime_powers_upto n) - Let m = set_lcm (prime_powers_upto n) - Note !x. MEM x [1 .. n] <=> 0 < x /\ x <= n by listRangeINC_MEM - and !x. 0 < x /\ x <= n ==> x divides m by prime_powers_upto_lcm_divisor - Thus lcm_run n divides m by list_lcm_is_least_common_multiple - (2) set_lcm (prime_powers_upto n) divides lcm_run n - Let s = prime_powers_upto n, m = lcm_run n - Claim: !z. z IN s ==> z divides m - Proof: Note ?p. (z = p ** LOG p n) /\ - prime p /\ p <= n by prime_powers_upto_element - Now 0 < p by PRIME_POS - so MEM p [1 .. n] by listRangeINC_MEM - ==> MEM z [1 .. n] by self_to_log_index_member - Thus z divides m by list_lcm_is_common_multiple - - Note FINITE s by prime_powers_upto_finite - Thus set_lcm s divides m by set_lcm_is_least_common_multiple, Claim -*) -val lcm_run_eq_set_lcm_prime_powers = store_thm( - "lcm_run_eq_set_lcm_prime_powers", - ``!n. lcm_run n = set_lcm (prime_powers_upto n)``, - rpt strip_tac >> - (irule DIVIDES_ANTISYM >> rpt conj_tac) >| [ - `!x. MEM x [1 .. n] <=> 0 < x /\ x <= n` by rw[listRangeINC_MEM] >> - `!x. 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n)` by rw[prime_powers_upto_lcm_divisor] >> - rw[list_lcm_is_least_common_multiple], - qabbrev_tac `s = prime_powers_upto n` >> - qabbrev_tac `m = lcm_run n` >> - `!z. z IN s ==> z divides m` by - (rw[prime_powers_upto_element, Abbr`s`] >> - `0 < p` by rw[PRIME_POS] >> - `MEM p [1 .. n]` by rw[listRangeINC_MEM] >> - `MEM (p ** LOG p n) [1 .. n]` by rw[self_to_log_index_member] >> - rw[list_lcm_is_common_multiple, Abbr`m`]) >> - `FINITE s` by rw[prime_powers_upto_finite, Abbr`s`] >> - rw[set_lcm_is_least_common_multiple] - ]); - -(* Theorem: set_lcm (prime_powers_upto n) = PROD_SET (prime_powers_upto n) *) -(* Proof: - Let s = prime_powers_upto n. - Note FINITE s by prime_powers_upto_finite - and PAIRWISE_COPRIME s by prime_powers_upto_pairwise_coprime - Thus set_lcm s = PROD_SET s by pairwise_coprime_prod_set_eq_set_lcm -*) -val set_lcm_prime_powers_upto_eqn = store_thm( - "set_lcm_prime_powers_upto_eqn", - ``!n. set_lcm (prime_powers_upto n) = PROD_SET (prime_powers_upto n)``, - metis_tac[prime_powers_upto_finite, prime_powers_upto_pairwise_coprime, pairwise_coprime_prod_set_eq_set_lcm]); - -(* Theorem: lcm_run n = PROD_SET (prime_powers_upto n) *) -(* Proof: - lcm_run n - = set_lcm (prime_powers_upto n) - = PROD_SET (prime_powers_upto n) -*) -val lcm_run_eq_prod_set_prime_powers = store_thm( - "lcm_run_eq_prod_set_prime_powers", - ``!n. lcm_run n = PROD_SET (prime_powers_upto n)``, - rw[lcm_run_eq_set_lcm_prime_powers, set_lcm_prime_powers_upto_eqn]); - -(* Theorem: PROD_SET (prime_powers_upto n) <= n ** (primes_count n) *) -(* Proof: - Let s = (primes_upto n), f = \p. p ** LOG p n, t = prime_powers_upto n. - Then IMAGE f s = t by prime_powers_upto_def - and FINITE s by primes_upto_finite - and FINITE t by IMAGE_FINITE - - Claim: !x. x IN t ==> x <= n - Proof: Note x IN t ==> - ?p. (x = p ** LOG p n) /\ prime p /\ p <= n by prime_powers_upto_element - Now 1 < p by ONE_LT_PRIME - so 0 < n by 1 < p, p <= n - and p ** LOG p n <= n by LOG - or x <= n - - Thus PROD_SET t <= n ** CARD t by PROD_SET_LE_CONSTANT, Claim - - Claim: INJ f s t - Proof: By prime_powers_upto_element_alt, primes_upto_element, INJ_DEF, - This is to show: prime p /\ prime p' /\ p ** LOG p n = p' ** LOG p' n ==> p = p' - Note 1 < p by ONE_LT_PRIME - so 0 < n by 1 < p, p <= n - and LOG p n <> 0 by LOG_EQ_0, p <= n - or 0 < LOG p n by NOT_ZERO_LT_ZERO - ==> p = p' by prime_powers_eq - - Thus CARD (IMAGE f s) = CARD s by INJ_CARD_IMAGE, Claim - or PROD_SET t <= n ** CARD s by above -*) - -Theorem prime_powers_upto_prod_set_le: - !n. PROD_SET (prime_powers_upto n) <= n ** (primes_count n) -Proof - rpt strip_tac >> - qabbrev_tac ‘s = (primes_upto n)’ >> - qabbrev_tac ‘f = \p. p ** LOG p n’ >> - qabbrev_tac ‘t = prime_powers_upto n’ >> - ‘IMAGE f s = t’ by simp[prime_powers_upto_def, Abbr‘f’, Abbr‘s’, Abbr‘t’] >> - ‘FINITE s’ by rw[primes_upto_finite, Abbr‘s’] >> - ‘FINITE t’ by metis_tac[IMAGE_FINITE] >> - ‘!x. x IN t ==> x <= n’ - by (rw[prime_powers_upto_element, Abbr‘t’, Abbr‘f’] >> - ‘1 < p’ by rw[ONE_LT_PRIME] >> - rw[LOG]) >> - ‘PROD_SET t <= n ** CARD t’ by rw[PROD_SET_LE_CONSTANT] >> - ‘INJ f s t’ - by (rw[prime_powers_upto_element_alt, primes_upto_element, INJ_DEF, Abbr‘f’, - Abbr‘s’, Abbr‘t’] >> - ‘1 < p’ by rw[ONE_LT_PRIME] >> - ‘0 < n’ by decide_tac >> - ‘LOG p n <> 0’ by rw[LOG_EQ_0] >> - metis_tac[prime_powers_eq, NOT_ZERO_LT_ZERO]) >> - metis_tac[INJ_CARD_IMAGE] -QED - -(* Theorem: lcm_run n <= n ** (primes_count n) *) -(* Proof: - lcm_run n - = PROD_SET (prime_powers_upto n) by lcm_run_eq_prod_set_prime_powers - <= n ** (primes_count n) by prime_powers_upto_prod_set_le -*) -val lcm_run_upper_by_primes_count = store_thm( - "lcm_run_upper_by_primes_count", - ``!n. lcm_run n <= n ** (primes_count n)``, - rw[lcm_run_eq_prod_set_prime_powers, prime_powers_upto_prod_set_le]); - -(* This is a significant result. *) - -(* Theorem: PROD_SET (primes_upto n) <= PROD_SET (prime_powers_upto n) *) -(* Proof: - Let s = primes_upto n, f = \p. p ** LOG p n, t = prime_powers_upto n. - The goal becomes: PROD_SET s <= PROD_SET t - Note IMAGE f s = t by prime_powers_upto_def - and FINITE s by primes_upto_finite - - Claim: INJ f s univ(:num) - Proof: By primes_upto_element, INJ_DEF, - This is to show: prime p /\ prime p' /\ p ** LOG p n = p' ** LOG p' n ==> p = p' - Note 1 < p by ONE_LT_PRIME - so 0 < n by 1 < p, p <= n - Thus LOG p n <> 0 by LOG_EQ_0, p <= n - or 0 < LOG p n by NOT_ZERO_LT_ZERO - ==> p = p' by prime_powers_eq - - Also INJ I s univ(:num) by primes_upto_element, INJ_DEF - and IMAGE I s = s by IMAGE_I - - Claim: !x. x IN s ==> I x <= f x - Proof: By primes_upto_element, - This is to show: prime x /\ x <= n ==> x <= x ** LOG x n - Note 1 < x by ONE_LT_PRIME - so 0 < n by 1 < x, x <= n - Thus LOG x n <> 0 by LOG_EQ_0 - or 1 <= LOG x n by LOG x n <> 0 - ==> x ** 1 <= x ** LOG x n by EXP_BASE_LE_MONO - or x <= x ** LOG x n by EXP_1 - - Hence PROD_SET s <= PROD_SET t by PROD_SET_LESS_EQ -*) -val prime_powers_upto_prod_set_ge = store_thm( - "prime_powers_upto_prod_set_ge", - ``!n. PROD_SET (primes_upto n) <= PROD_SET (prime_powers_upto n)``, - rpt strip_tac >> - qabbrev_tac `s = primes_upto n` >> - qabbrev_tac `f = \p. p ** LOG p n` >> - qabbrev_tac `t = prime_powers_upto n` >> - `IMAGE f s = t` by rw[prime_powers_upto_def, Abbr`f`, Abbr`s`, Abbr`t`] >> - `FINITE s` by rw[primes_upto_finite, Abbr`s`] >> - `INJ f s univ(:num)` by - (rw[primes_upto_element, INJ_DEF, Abbr`f`, Abbr`s`] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `LOG p n <> 0` by rw[LOG_EQ_0] >> - metis_tac[prime_powers_eq, NOT_ZERO_LT_ZERO]) >> - `INJ I s univ(:num)` by rw[primes_upto_element, INJ_DEF, Abbr`s`] >> - `IMAGE I s = s` by rw[] >> - `!x. x IN s ==> I x <= f x` by - (rw[primes_upto_element, Abbr`f`, Abbr`s`] >> - `1 < x` by rw[ONE_LT_PRIME] >> - `LOG x n <> 0` by rw[LOG_EQ_0] >> - `1 <= LOG x n` by decide_tac >> - metis_tac[EXP_BASE_LE_MONO, EXP_1]) >> - metis_tac[PROD_SET_LESS_EQ]); - -(* Theorem: PROD_SET (primes_upto n) <= lcm_run n *) -(* Proof: - lcm_run n - = set_lcm (prime_powers_upto n) by lcm_run_eq_set_lcm_prime_powers - = PROD_SET (prime_powers_upto n) by set_lcm_prime_powers_upto_eqn - >= PROD_SET (primes_upto n) by prime_powers_upto_prod_set_ge -*) -val lcm_run_lower_by_primes_product = store_thm( - "lcm_run_lower_by_primes_product", - ``!n. PROD_SET (primes_upto n) <= lcm_run n``, - rpt strip_tac >> - `lcm_run n = set_lcm (prime_powers_upto n)` by rw[lcm_run_eq_set_lcm_prime_powers] >> - `_ = PROD_SET (prime_powers_upto n)` by rw[set_lcm_prime_powers_upto_eqn] >> - rw[prime_powers_upto_prod_set_ge]); - -(* This is another significant result. *) - -(* These are essentially Chebyshev functions. *) - -(* Theorem: n ** primes_count n <= PROD_SET (primes_upto n) * (PROD_SET (prime_powers_upto n)) *) -(* Proof: - Let s = (primes_upto n), f = \p. p ** LOG p n, t = prime_powers_upto n. - The goal becomes: n ** CARD s <= PROD_SET s * PROD_SET t - - Note IMAGE f s = t by prime_powers_upto_def - and FINITE s by primes_upto_finite - and FINITE t by IMAGE_FINITE - - Claim: !p. p IN s ==> n <= I p * f p - Proof: By primes_upto_element, - This is to show: prime p /\ p <= n ==> n < p * p ** LOG p n - Note 1 < p by ONE_LT_PRIME - so 0 < n by 1 < p, p <= n - ==> n < p ** (SUC (LOG p n)) by LOG - = p * p ** (LOG p n) by EXP - or n <= p * p ** (LOG p n) by LESS_IMP_LESS_OR_EQ - - Note INJ I s univ(:num) by primes_upto_element, INJ_DEF, - and IMAGE I s = s by IMAGE_I - - Claim: INJ f s univ(:num) - Proof: By primes_upto_element, INJ_DEF, - This is to show: prime p /\ prime p' /\ p ** LOG p n = p' ** LOG p' n ==> p = p' - Note 1 < p by ONE_LT_PRIME - so 0 < n by 1 < p, p <= n - ==> LOG p n <> 0 by LOG_EQ_0 - or 0 < LOG p n by NOT_ZERO_LT_ZERO - Thus p = p' by prime_powers_eq - - Therefore, - n ** CARD s <= PROD_SET (IMAGE I s) * PROD_SET (IMAGE f s) - by PROD_SET_PRODUCT_GE_CONSTANT - or n ** CARD s <= PROD_SET s * PROD_SET t by above -*) - -Theorem prime_powers_upto_prod_set_mix_ge: - !n. n ** primes_count n <= - PROD_SET (primes_upto n) * (PROD_SET (prime_powers_upto n)) -Proof - rpt strip_tac >> - qabbrev_tac ‘s = (primes_upto n)’ >> - qabbrev_tac ‘f = \p. p ** LOG p n’ >> - qabbrev_tac ‘t = prime_powers_upto n’ >> - ‘IMAGE f s = t’ by rw[prime_powers_upto_def, Abbr‘f’, Abbr‘s’, Abbr‘t’] >> - ‘FINITE s’ by rw[primes_upto_finite, Abbr‘s’] >> - ‘FINITE t’ by rw[] >> - ‘!p. p IN s ==> n <= I p * f p’ by - (rw[primes_upto_element, Abbr‘s’, Abbr‘f’] >> - ‘1 < p’ by rw[ONE_LT_PRIME] >> - rw[LOG, GSYM EXP, LESS_IMP_LESS_OR_EQ]) >> - ‘INJ I s univ(:num)’ by rw[primes_upto_element, INJ_DEF, Abbr‘s’] >> - ‘IMAGE I s = s’ by rw[] >> - ‘INJ f s univ(:num)’ by - (rw[primes_upto_element, INJ_DEF, Abbr‘f’, Abbr‘s’] >> - ‘1 < p’ by rw[ONE_LT_PRIME] >> - ‘LOG p n <> 0’ by rw[LOG_EQ_0] >> - metis_tac[prime_powers_eq, NOT_ZERO_LT_ZERO]) >> - metis_tac[PROD_SET_PRODUCT_GE_CONSTANT] -QED - -(* Another significant result. *) - -(* Theorem: n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n *) -(* Proof: - n ** primes_count n - <= PROD_SET (primes_upto n) * (PROD_SET (prime_powers_upto n)) by prime_powers_upto_prod_set_mix_ge - = PROD_SET (primes_upto n) * lcm_run n by lcm_run_eq_prod_set_prime_powers -*) -val primes_count_upper_by_product = store_thm( - "primes_count_upper_by_product", - ``!n. n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n``, - metis_tac[prime_powers_upto_prod_set_mix_ge, lcm_run_eq_prod_set_prime_powers]); - -(* Theorem: n ** primes_count n <= (lcm_run n) ** 2 *) -(* Proof: - n ** primes_count n - <= PROD_SET (primes_upto n) * lcm_run n by primes_count_upper_by_product - <= lcm_run n * lcm_run n by lcm_run_lower_by_primes_product - = (lcm_run n) ** 2 by EXP_2 -*) -val primes_count_upper_by_lcm_run = store_thm( - "primes_count_upper_by_lcm_run", - ``!n. n ** primes_count n <= (lcm_run n) ** 2``, - rpt strip_tac >> - `n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n` by rw[primes_count_upper_by_product] >> - `PROD_SET (primes_upto n) <= lcm_run n` by rw[lcm_run_lower_by_primes_product] >> - metis_tac[LESS_MONO_MULT, LESS_EQ_TRANS, EXP_2]); - -(* Theorem: SQRT (n ** (primes_count n)) <= lcm_run n *) -(* Proof: - Note n ** primes_count n <= (lcm_run n) ** 2 by primes_count_upper_by_lcm_run - ==> SQRT (n ** primes_count n) <= SQRT ((lcm_run n) ** 2) by ROOT_LE_MONO, 0 < 2 - But SQRT ((lcm_run n) ** 2) = lcm_run n by ROOT_UNIQUE - Thus SQRT (n ** (primes_count n)) <= lcm_run n -*) -val lcm_run_lower_by_primes_count = store_thm( - "lcm_run_lower_by_primes_count", - ``!n. SQRT (n ** (primes_count n)) <= lcm_run n``, - rpt strip_tac >> - `n ** primes_count n <= (lcm_run n) ** 2` by rw[primes_count_upper_by_lcm_run] >> - `SQRT (n ** primes_count n) <= SQRT ((lcm_run n) ** 2)` by rw[ROOT_LE_MONO] >> - `SQRT ((lcm_run n) ** 2) = lcm_run n` by rw[ROOT_UNIQUE] >> - decide_tac); - -(* Therefore: - L(n) <= n ** pi(n) by lcm_run_upper_by_primes_count - PI(n) <= L(n) by lcm_run_lower_by_primes_product - n ** pi(n) <= PI(n) * L(n) by primes_count_upper_by_product - - giving: L(n) <= n ** pi(n) <= L(n) ** 2 by primes_count_upper_by_lcm_run - and: SQRT (n ** pi(n)) <= L(n) <= (n ** pi(n)) by lcm_run_lower_by_primes_count -*) - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/primesScript.sml b/examples/algebra/lib/primesScript.sml deleted file mode 100644 index 0ccfeedadb..0000000000 --- a/examples/algebra/lib/primesScript.sml +++ /dev/null @@ -1,376 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Primality Tests *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "primes"; - -(* ------------------------------------------------------------------------- *) - - - -(* open dependent theories *) -(* val _ = load "logPowerTheory"; *) -open logPowerTheory; (* for SQRT *) -open helperNumTheory helperFunctionTheory; -open arithmeticTheory dividesTheory; - - -(* ------------------------------------------------------------------------- *) -(* Primality Tests Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: -*) -(* - - Two Factors Properties: - two_factors_property_1 |- !n a b. (n = a * b) /\ a < SQRT n ==> SQRT n <= b - two_factors_property_2 |- !n a b. (n = a * b) /\ SQRT n < a ==> b <= SQRT n - two_factors_property |- !n a b. (n = a * b) ==> a <= SQRT n \/ b <= SQRT n - - Primality or Compositeness based on SQRT: - prime_by_sqrt_factors |- !p. prime p <=> - 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) - prime_factor_estimate |- !n. 1 < n ==> - (~prime n <=> ?p. prime p /\ p divides n /\ p <= SQRT n) - - Primality Testing Algorithm: - factor_seek_def |- !q n c. factor_seek n c q = - if c <= q then n - else if 1 < q /\ (n MOD q = 0) then q - else factor_seek n c (q + 1) - prime_test_def |- !n. prime_test n <=> - if n <= 1 then F else factor_seek n (1 + SQRT n) 2 = n - factor_seek_bound |- !n c q. 0 < n ==> factor_seek n c q <= n - factor_seek_thm |- !n c q. 1 < q /\ q <= c /\ c <= n ==> - (factor_seek n c q = n <=> !p. q <= p /\ p < c ==> ~(p divides n)) - prime_test_thm |- !n. prime n <=> prime_test n - -*) - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Two Factors Properties *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: (n = a * b) /\ a < SQRT n ==> SQRT n <= b *) -(* Proof: - If n = 0, then a = 0 or b = 0 by MULT_EQ_0 - But SQRT 0 = 0 by SQRT_0 - so a <> 0, making b = 0, and SQRT n <= b true. - If n <> 0, a <> 0 and b <> 0 by MULT_EQ_0 - By contradiction, suppose b < SQRT n. - Then n = a * b < a * SQRT n by LT_MULT_LCANCEL, 0 < a. - and a * SQRT n < SQRT n * SQRT n by LT_MULT_RCANCEL, 0 < SQRT n. - making n < (SQRT n) ** 2 by LESS_TRANS, EXP_2 - This contradicts (SQRT n) ** 2 <= n by SQRT_PROPERTY -*) -val two_factors_property_1 = store_thm( - "two_factors_property_1", - ``!n a b. (n = a * b) /\ a < SQRT n ==> SQRT n <= b``, - rpt strip_tac >> - Cases_on `n = 0` >| [ - `a <> 0 /\ (b = 0) /\ (SQRT n = 0)` by metis_tac[MULT_EQ_0, SQRT_0, DECIDE``~(0 < 0)``] >> - decide_tac, - `a <> 0 /\ b <> 0` by metis_tac[MULT_EQ_0] >> - spose_not_then strip_assume_tac >> - `b < SQRT n` by decide_tac >> - `0 < a /\ 0 < b /\ 0 < SQRT n` by decide_tac >> - `n < a * SQRT n` by rw[] >> - `a * SQRT n < SQRT n * SQRT n` by rw[] >> - `n < (SQRT n) ** 2` by metis_tac[LESS_TRANS, EXP_2] >> - `(SQRT n) ** 2 <= n` by rw[SQRT_PROPERTY] >> - decide_tac - ]); - -(* Theorem: (n = a * b) /\ SQRT n < a ==> b <= SQRT n *) -(* Proof: - If n = 0, then a = 0 or b = 0 by MULT_EQ_0 - But SQRT 0 = 0 by SQRT_0 - so a <> 0, making b = 0, and b <= SQRT n true. - If n <> 0, a <> 0 and b <> 0 by MULT_EQ_0 - By contradiction, suppose SQRT n < b. - Then SUC (SQRT n) ** 2 - = SUC (SQRT n) * SUC (SQRT n) by EXP_2 - <= a * SUC (SQRT n) by LT_MULT_RCANCEL, 0 < SUC (SQRT n). - <= a * b = n by LT_MULT_LCANCEL, 0 < a. - Giving (SUC (SQRT n)) ** 2 <= n by LESS_EQ_TRANS - or SQRT ((SUC (SQRT n)) ** 2) <= SQRT n by SQRT_LE - or SUC (SQRT n) <= SQRT n by SQRT_OF_SQ - which is a contradiction to !m. SUC m > m by LESS_SUC_REFL - *) -val two_factors_property_2 = store_thm( - "two_factors_property_2", - ``!n a b. (n = a * b) /\ SQRT n < a ==> b <= SQRT n``, - rpt strip_tac >> - Cases_on `n = 0` >| [ - `a <> 0 /\ (b = 0) /\ (SQRT 0 = 0)` by metis_tac[MULT_EQ_0, SQRT_0, DECIDE``~(0 < 0)``] >> - decide_tac, - `a <> 0 /\ b <> 0` by metis_tac[MULT_EQ_0] >> - spose_not_then strip_assume_tac >> - `SQRT n < b` by decide_tac >> - `SUC (SQRT n) <= a /\ SUC (SQRT n) <= b` by decide_tac >> - `SUC (SQRT n) * SUC (SQRT n) <= a * SUC (SQRT n)` by rw[] >> - `a * SUC (SQRT n) <= n` by rw[] >> - `SUC (SQRT n) ** 2 <= n` by metis_tac[LESS_EQ_TRANS, EXP_2] >> - `SUC (SQRT n) <= SQRT n` by metis_tac[SQRT_LE, SQRT_OF_SQ] >> - decide_tac - ]); - -(* Theorem: (n = a * b) ==> a <= SQRT n \/ b <= SQRT n *) -(* Proof: - By contradiction, suppose SQRT n < a /\ SQRT n < b. - Then (n = a * b) /\ SQRT n < a ==> b <= SQRT n by two_factors_property_2 - which contradicts SQRT n < b. - *) -val two_factors_property = store_thm( - "two_factors_property", - ``!n a b. (n = a * b) ==> a <= SQRT n \/ b <= SQRT n``, - rpt strip_tac >> - spose_not_then strip_assume_tac >> - `SQRT n < a` by decide_tac >> - metis_tac[two_factors_property_2]); - -(* ------------------------------------------------------------------------- *) -(* Primality or Compositeness based on SQRT *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: prime p <=> 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) *) -(* Proof: - If part: prime p ==> 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) - First one: prime p ==> 1 < p is true by ONE_LT_PRIME - Second one: by contradiction, suppose q divides p. - But prime p /\ q divides p ==> (q = p) or (q = 1) by prime_def - Given 1 < q, q <> 1, hence q = p. - This means p <= SQRT p, giving p = 0 or p = 1 by SQRT_GE_SELF - which contradicts p <> 0 and p <> 1 by PRIME_POS, prime_def - Only-if part: 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) ==> prime p - By prime_def, this is to show: - (1) p <> 1, true since 1 < p. - (2) b divides p ==> (b = p) \/ (b = 1) - By contradiction, suppose b <> p /\ b <> 1. - b divides p ==> ?a. p = a * b by divides_def - which means a <= SQRT p \/ b <= SQRT p by two_factors_property - If a <= SQRT p, - 1 < p ==> p <> 0, so a <> 0 by MULT - also b <> p ==> a <> 1 by MULT_LEFT_1 - so 1 < a, and a divides p by prime_def, MULT_COMM - The implication gives ~(a divides p), a contradiction. - If b <= SQRT p, - 1 < p ==> p <> 0, so b <> 0 by MULT_0 - also b <> 1, so 1 < b, and b divides p. - The implication gives ~(b divides p), a contradiction. - *) -val prime_by_sqrt_factors = store_thm( - "prime_by_sqrt_factors", - ``!p. prime p <=> 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p)``, - rw[EQ_IMP_THM] >- - rw[ONE_LT_PRIME] >- - (spose_not_then strip_assume_tac >> - `0 < p` by rw[PRIME_POS] >> - `p <> 0 /\ q <> 1` by decide_tac >> - `(q = p) /\ p <> 1` by metis_tac[prime_def] >> - metis_tac[SQRT_GE_SELF]) >> - rw[prime_def] >> - spose_not_then strip_assume_tac >> - `?a. p = a * b` by rw[GSYM divides_def] >> - `a <= SQRT p \/ b <= SQRT p` by rw[two_factors_property] >| [ - `a <> 1` by metis_tac[MULT_LEFT_1] >> - `p <> 0` by decide_tac >> - `a <> 0` by metis_tac[MULT] >> - `1 < a` by decide_tac >> - metis_tac[divides_def, MULT_COMM], - `p <> 0` by decide_tac >> - `b <> 0` by metis_tac[MULT_0] >> - `1 < b` by decide_tac >> - metis_tac[] - ]); - -(* Theorem: 1 < n ==> (~prime n <=> ?p. prime p /\ p divides n /\ p <= SQRT n) *) -(* Proof: - If part ~prime n ==> ?p. prime p /\ p divides n /\ p <= SQRT n - Given n <> 1, ?p. prime p /\ p divides n by PRIME_FACTOR - If p <= SQRT n, take this p. - If ~(p <= SQRT n), SQRT n < p. - Since p divides n, ?q. n = p * q by divides_def, MULT_COMM - Hence q <= SQRT n by two_factors_property_2 - Since prime p but ~prime n, q <> 1 by MULT_RIGHT_1 - so ?t. prime t /\ t divides q by PRIME_FACTOR - Since 1 < n, n <> 0, so q <> 0 by MULT_0 - so t divides q ==> t <= q by DIVIDES_LE, 0 < q. - Take t, then t divides n by DIVIDES_TRANS - and t <= SQRT n by LESS_EQ_TRANS - Only-if part: ?p. prime p /\ p divides n /\ p <= SQRT n ==> ~prime n - By contradiction, assume prime n. - Then p divides n ==> p = 1 or p = n by prime_def - But prime p ==> p <> 1, so p = n by ONE_LT_PRIME - Giving p <= SQRT p, - thus forcing p = 0 or p = 1 by SQRT_GE_SELF - Both are impossible for prime p. -*) -val prime_factor_estimate = store_thm( - "prime_factor_estimate", - ``!n. 1 < n ==> (~prime n <=> ?p. prime p /\ p divides n /\ p <= SQRT n)``, - rpt strip_tac >> - `n <> 1` by decide_tac >> - rw[EQ_IMP_THM] >| [ - `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> - Cases_on `p <= SQRT n` >- - metis_tac[] >> - `SQRT n < p` by decide_tac >> - `?q. n = q * p` by rw[GSYM divides_def] >> - `_ = p * q` by rw[MULT_COMM] >> - `q <= SQRT n` by metis_tac[two_factors_property_2] >> - `q <> 1` by metis_tac[MULT_RIGHT_1] >> - `?t. prime t /\ t divides q` by rw[PRIME_FACTOR] >> - `n <> 0` by decide_tac >> - `q <> 0` by metis_tac[MULT_0] >> - `0 < q ` by decide_tac >> - `t <= q` by rw[DIVIDES_LE] >> - `q divides n` by metis_tac[divides_def] >> - metis_tac[DIVIDES_TRANS, LESS_EQ_TRANS], - spose_not_then strip_assume_tac >> - `1 < p` by rw[ONE_LT_PRIME] >> - `p <> 1 /\ p <> 0` by decide_tac >> - `p = n` by metis_tac[prime_def] >> - metis_tac[SQRT_GE_SELF] - ]); - -(* ------------------------------------------------------------------------- *) -(* Primality Testing Algorithm *) -(* ------------------------------------------------------------------------- *) - -(* Seek the prime factor of number n, starting with q, up to cutoff c. *) -Definition factor_seek_def: - factor_seek n c q = - if c <= q then n - else if 1 < q /\ (n MOD q = 0) then q - else factor_seek n c (q + 1) -Termination - WF_REL_TAC ‘measure (λ(n,c,q). c - q)’ >> simp[] -End -(* Use 1 < q so that, for prime n, it gives a result n for any initial q, including q = 1. *) - -(* Primality test by seeking a factor exceeding (SQRT n). *) -val prime_test_def = Define` - prime_test n = - if n <= 1 then F - else factor_seek n (1 + SQRT n) 2 = n -`; - -(* -EVAL ``MAP prime_test [1 .. 15]``; = [F; T; T; F; T; F; T; F; F; F; T; F; T; F; F]: thm -*) - -(* Theorem: 0 < n ==> factor_seek n c q <= n *) -(* Proof: - By induction from factor_seek_def. - If c <= q, - Then factor_seek n c q = n, hence true by factor_seek_def - If q = 0, - Then factor_seek n c 0 = 0, hence true by factor_seek_def - If n MOD q = 0, - Then factor_seek n c q = q by factor_seek_def - Thus q divides n by DIVIDES_MOD_0, q <> 0 - hence q <= n by DIVIDES_LE, 0 < n - Otherwise, - factor_seek n c q - = factor_seek n c (q + 1) by factor_seek_def - <= n by induction hypothesis -*) -val factor_seek_bound = store_thm( - "factor_seek_bound", - ``!n c q. 0 < n ==> factor_seek n c q <= n``, - ho_match_mp_tac (theorem "factor_seek_ind") >> - rw[] >> - rw[Once factor_seek_def] >> - `q divides n` by rw[DIVIDES_MOD_0] >> - rw[DIVIDES_LE]); - -(* Theorem: 1 < q /\ q <= c /\ c <= n ==> - ((factor_seek n c q = n) <=> (!p. q <= p /\ p < c ==> ~(p divides n))) *) -(* Proof: - By induction from factor_seek_def, this is to show: - (1) n MOD q = 0 ==> ?p. (q <= p /\ p < c) /\ p divides n - Take p = q, then n MOD q = 0 ==> q divides n by DIVIDES_MOD_0, 0 < q - (2) n MOD q <> 0 ==> factor_seek n c (q + 1) = n <=> - !p. q <= p /\ p < c ==> ~(p divides n) - factor_seek n c (q + 1) = n - <=> !p. q + 1 <= p /\ p < c ==> ~(p divides n)) by induction hypothesis - or !p. q < p /\ p < c ==> ~(p divides n)) - But n MOD q <> 0 gives ~(q divides n) by DIVIDES_MOD_0, 0 < q - Thus !p. q <= p /\ p < c ==> ~(p divides n)) -*) -val factor_seek_thm = store_thm( - "factor_seek_thm", - ``!n c q. 1 < q /\ q <= c /\ c <= n ==> - ((factor_seek n c q = n) <=> (!p. q <= p /\ p < c ==> ~(p divides n)))``, - ho_match_mp_tac (theorem "factor_seek_ind") >> - rw[] >> - rw[Once factor_seek_def] >| [ - qexists_tac `q` >> - rw[DIVIDES_MOD_0], - rw[EQ_IMP_THM] >> - spose_not_then strip_assume_tac >> - `0 < q` by decide_tac >> - `p <> q` by metis_tac[DIVIDES_MOD_0] >> - `q + 1 <= p` by decide_tac >> - metis_tac[] - ]); - -(* Theorem: prime n = prime_test n *) -(* Proof: - prime n - <=> 1 < n /\ !q. 1 < q /\ n <= SQRT n ==> ~(n divides p) by prime_by_sqrt_factors - <=> 1 < n /\ !q. 2 <= q /\ n < c ==> ~(n divides p) where c = 1 + SQRT n - Under 1 < n, - Note SQRT n <> 0 by SQRT_EQ_0, n <> 0 - so 1 < 1 + SQRT n = c, or 2 <= c. - Also SQRT n <= n by SQRT_LE_SELF - but SQRT n <> n by SQRT_EQ_SELF, 1 < n - so SQRT n < n, or c <= n. - Thus 1 < n /\ !q. 2 <= q /\ n < c ==> ~(n divides p) - <=> factor_seek n c q = n by factor_seek_thm - <=> prime_test n by prime_test_def -*) -val prime_test_thm = store_thm( - "prime_test_thm", - ``!n. prime n = prime_test n``, - rw[prime_test_def, prime_by_sqrt_factors] >> - rw[EQ_IMP_THM] >| [ - qabbrev_tac `c = SQRT n + 1` >> - `0 < 2` by decide_tac >> - `SQRT n <> 0` by rw[SQRT_EQ_0] >> - `2 <= c` by rw[Abbr`c`] >> - `SQRT n <= n` by rw[SQRT_LE_SELF] >> - `SQRT n <> n` by rw[SQRT_EQ_SELF] >> - `c <= n` by rw[Abbr`c`] >> - `!q. 2 <= q /\ q < c ==> ~(q divides n)` by fs[Abbr`c`] >> - rw[factor_seek_thm], - qabbrev_tac `c = SQRT n + 1` >> - `0 < 2` by decide_tac >> - `SQRT n <> 0` by rw[SQRT_EQ_0] >> - `2 <= c` by rw[Abbr`c`] >> - `SQRT n <= n` by rw[SQRT_LE_SELF] >> - `SQRT n <> n` by rw[SQRT_EQ_SELF] >> - `c <= n` by rw[Abbr`c`] >> - fs[factor_seek_thm] >> - `!p. 1 < p /\ p <= SQRT n ==> ~(p divides n)` by fs[Abbr`c`] >> - rw[] - ]); - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/sublistScript.sml b/examples/algebra/lib/sublistScript.sml deleted file mode 100644 index 4963db90e8..0000000000 --- a/examples/algebra/lib/sublistScript.sml +++ /dev/null @@ -1,1417 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Sublist Theory *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "sublist"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories *) -(* val _ = load "helperListTheory"; *) -open helperListTheory; - -(* open dependent theories *) -open pred_setTheory listTheory rich_listTheory arithmeticTheory; -open listRangeTheory; (* for listRangeINC_def *) -open indexedListsTheory; (* for MEM_findi *) - - -(* ------------------------------------------------------------------------- *) -(* Sublist Theory Documentation *) -(* ------------------------------------------------------------------------- *) -(* Datatypes and overloads: - l1 <= l2 = sublist l1 l2 -*) -(* Definitions and Theorems (# are exported): - - Sublist: - sublist_def |- (!x. [] <= x <=> T) /\ (!t1 h1. h1::t1 <= [] <=> F) /\ - !t2 t1 h2 h1. h1::t1 <= h2::t2 <=> - (h1 = h2) /\ t1 <= t2 \/ h1 <> h2 /\ h1::t1 <= t2 - sublist_nil |- !p. [] <= p - sublist_cons |- !h p q. p <= q <=> h::p <= h::q - sublist_of_nil |- !p. p <= [] <=> (p = []) - sublist_cons_eq |- !h. (!p q. h::p <= q ==> p <= q) <=> !p q. p <= q ==> p <= h::q - sublist_cons_remove |- !h p q. h::p <= q ==> p <= q - sublist_cons_include |- !h p q. p <= q ==> p <= h::q - sublist_length |- !p q. p <= q ==> LENGTH p <= LENGTH q - sublist_refl |- !p. p <= p - sublist_antisym |- !p q. p <= q /\ q <= p ==> (p = q) - sublist_trans |- !p q r. p <= q /\ q <= r ==> p <= r - sublist_snoc |- !h p q. p <= q ==> SNOC h p <= SNOC h q - sublist_member_sing |- !ls x. MEM x ls ==> [x] <= ls - sublist_take |- !ls n. TAKE n ls <= ls - sublist_drop |- !ls n. DROP n ls <= ls - sublist_tail |- !ls. ls <> [] ==> TL ls <= ls - sublist_front |- !ls. ls <> [] ==> FRONT ls <= ls - sublist_head_sing |- !ls. ls <> [] ==> [HD ls] <= ls - sublist_last_sing |- !ls. ls <> [] ==> [LAST ls] <= ls - sublist_every |- !l ls. l <= ls ==> !P. EVERY P ls ==> EVERY P l - - More sublists: - sublist_induct |- !P. (!y. P [] y) /\ - (!h x y. P x y /\ x <= y ==> P (h::x) (h::y)) /\ - (!h x y. P x y /\ x <= y ==> P x (h::y)) ==> !x y. x <= y ==> P x y - sublist_mem |- !p q x. p <= q /\ MEM x p ==> MEM x q - sublist_subset |- !ls sl. sl <= ls ==> set sl SUBSET set ls - sublist_ALL_DISTINCT |- !p q. p <= q /\ ALL_DISTINCT q ==> ALL_DISTINCT p - sublist_append_remove |- !p q x. x ++ p <= q ==> p <= q - sublist_append_include |- !p q x. p <= q ==> p <= x ++ q - sublist_append_suffix |- !p q. p <= p ++ q - sublist_append_prefix |- !p q. p <= q ++ p - sublist_prefix |- !x p q. p <= q <=> x ++ p <= x ++ q - sublist_prefix_nil |- !p q. p ++ q <= q ==> (p = []) - sublist_append_if |- !p q. p <= q ==> !h. p ++ [h] <= q ++ [h] - sublist_append_only_if |- !p q h. p ++ [h] <= q ++ [h] ==> p <= q - sublist_append_iff |- !p q h. p <= q <=> p ++ [h] <= q ++ [h] - sublist_suffix |- !x p q. p <= q <=> p ++ x <= q ++ x - sublist_append_pair |- !a b c d. a <= b /\ c <= d ==> a ++ c <= b ++ d - sublist_append_extend |- !h t q. h::t <= q <=> ?x y. (q = x ++ h::y) /\ t <= y - - Applications of sublist: - MAP_SUBLIST |- !f p q. p <= q ==> MAP f p <= MAP f q - SUM_SUBLIST |- !p q. p <= q ==> SUM p <= SUM q - listRangeINC_sublist |- !m n. m < n ==> [m; n] <= [m .. n] - listRangeLHI_sublist |- !m n. m + 1 < n ==> [m; n - 1] <= [m ..< n] - sublist_order |- !ls sl x. sl <= ls /\ MEM x sl ==> - ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ - l3 <= l1 /\ l4 <= l2 - sublist_element_order |- !ls sl j h. sl <= ls /\ ALL_DISTINCT ls /\ j < h /\ h < LENGTH sl ==> - findi (EL j sl) ls < findi (EL h sl) ls - sublist_MONO_INC |- !ls sl. sl <= ls /\ MONO_INC ls ==> MONO_INC sl - sublist_MONO_DEC |- !ls sl. sl <= ls /\ MONO_DEC ls ==> MONO_DEC sl - - FILTER as sublist: - FILTER_sublist |- !P ls. FILTER P ls <= ls - FILTER_element_order |- !P ls j h. (let fs = FILTER P ls - in ALL_DISTINCT ls /\ j < h /\ h < LENGTH fs ==> - findi (EL j fs) ls < findi (EL h fs) ls) - FILTER_MONO_INC |- !P ls. MONO_INC ls ==> MONO_INC (FILTER P ls) - FILTER_MONO_DEC |- !P ls. MONO_DEC ls ==> MONO_DEC (FILTER P ls) - -*) - -(* ------------------------------------------------------------------------- *) -(* Sublist: an order-preserving portion of a list *) -(* ------------------------------------------------------------------------- *) - -(* Definition of sublist *) -val sublist_def = Define` - (sublist [] x = T) /\ - (sublist (h1::t1) [] = F) /\ - (sublist (h1::t1) (h2::t2) <=> - ((h1 = h2) /\ sublist t1 t2 \/ ~(h1 = h2) /\ sublist (h1::t1) t2)) -`; - -(* Overload sublist by infix operator *) -val _ = overload_on ("<=", ``sublist``); -(* -> sublist_def; -val it = |- (!x. [] <= x <=> T) /\ (!t1 h1. h1::t1 <= [] <=> F) /\ - !t2 t1 h2 h1. h1::t1 <= h2::t2 <=> - (h1 = h2) /\ t1 <= t2 \/ h1 <> h2 /\ h1::t1 <= t2: thm -*) - -(* Theorem: [] <= p *) -(* Proof: by sublist_def *) -val sublist_nil = store_thm( - "sublist_nil", - ``!p. [] <= p``, - rw[sublist_def]); - -(* Theorem: p <= q <=> h::p <= h::q *) -(* Proof: by sublist_def *) -val sublist_cons = store_thm( - "sublist_cons", - ``!h p q. p <= q <=> h::p <= h::q``, - rw[sublist_def]); - -(* Theorem: p <= [] <=> (p = []) *) -(* Proof: - If p = [], then [] <= [] by sublist_nil - If p = h::t, then h::t <= [] = F by sublist_def -*) -val sublist_of_nil = store_thm( - "sublist_of_nil", - ``!p. p <= [] <=> (p = [])``, - (Cases_on `p` >> rw[sublist_def])); - -(* Theorem: (!p q. (h::p) <= q ==> p <= q) = (!p q. p <= q ==> p <= (h::q)) *) -(* Proof: - If part: (!p q. (h::p) <= q ==> p <= q) ==> (!p q. p <= q ==> p <= (h::q)) - p <= q - ==> h::p <= h::q by sublist_cons - ==> p <= h::q by implication - Only-if part: (!p q. p <= q ==> p <= (h::q)) ==> (!p q. (h::p) <= q ==> p <= q) - (h::p) <= q - ==> (h::p) <= (h::q) by implication - ==> p <= q by sublist_cons -*) -val sublist_cons_eq = store_thm( - "sublist_cons_eq", - ``!h. (!p q. (h::p) <= q ==> p <= q) = (!p q. p <= q ==> p <= (h::q))``, - rw[EQ_IMP_THM] >> - metis_tac[sublist_cons]); - -(* Theorem: h::p <= q ==> p <= q *) -(* Proof: - By induction on q. - Base: !h p. h::p <= [] ==> p <= [] - True since h::p <= [] = F by sublist_def - - Step: !h p. h::p <= q ==> p <= q ==> - !h h' p. h'::p <= h::q ==> p <= h::q - If p = [], true by sublist_nil - If p = h''::t, - p <= h::q - <=> (h'' = h) /\ t <= q \/ h'' <> h /\ h''::t <= q by sublist_def - If h'' = h, then t <= q by sublist_def, induction hypothesis - If h'' <> h, then h''::t <= q by sublist_def, induction hypothesis -*) -val sublist_cons_remove = store_thm( - "sublist_cons_remove", - ``!h p q. h::p <= q ==> p <= q``, - Induct_on `q` >- - rw[sublist_def] >> - rpt strip_tac >> - (Cases_on `p` >> simp[sublist_def]) >> - (Cases_on `h'' = h` >> metis_tac[sublist_def])); - -(* Theorem: p <= q ==> p <= h::q *) -(* Proof: by sublist_cons_eq, sublist_cons_remove *) -val sublist_cons_include = store_thm( - "sublist_cons_include", - ``!h p q. p <= q ==> p <= h::q``, - metis_tac[sublist_cons_eq, sublist_cons_remove]); - -(* Theorem: p <= q ==> LENGTH p <= LENGTH q *) -(* Proof: - By induction on q. - Base: !p. p <= [] ==> LENGTH p <= LENGTH [] - Note p = [] by sublist_of_nil - Thus true by arithemtic - Step: !p. p <= q ==> LENGTH p <= LENGTH q ==> - !h p. p <= h::q ==> LENGTH p <= LENGTH (h::q) - If p = [], LENGTH p = 0 by LENGTH - and 0 <= LENGTH (h::q). - If p = h'::t, - If h = h', - (h::t) <= (h::q) - <=> t <= q by sublist_def, same heads - ==> LENGTH t <= LENGTH q by inductive hypothesis - ==> SUC(LENGTH t) <= SUC(LENGTH q) - = LENGTH (h::t) <= LENGTH (h::q) - If ~(h = h'), - (h'::t) <= (h::q) - <=> (h'::t) <= q by sublist_def, different heads - ==> LENGTH (h'::t) <= LENGTH q by inductive hypothesis - ==> LENGTH (h'::t) <= SUC(LENGTH q) by arithemtic - = LENGTH (h'::t) <= LENGTH (h::q) -*) -val sublist_length = store_thm( - "sublist_length", - ``!p q. p <= q ==> LENGTH p <= LENGTH q``, - Induct_on `q` >- - rw[sublist_of_nil] >> - rpt strip_tac >> - (Cases_on `p` >> simp[]) >> - (Cases_on `h = h'` >> fs[sublist_def]) >> - `LENGTH (h'::t) <= LENGTH q` by rw[] >> - `LENGTH t < LENGTH (h'::t)` by rw[LENGTH_TL_LT] >> - decide_tac); - -(* Theorem: [Reflexive] p <= p *) -(* Proof: - By induction on p. - Base: [] <= [], true by sublist_nil - Step: p <= p ==> !h. h::p <= h::p, true by sublist_cons -*) -val sublist_refl = store_thm( - "sublist_refl", - ``!p:'a list. p <= p``, - Induct >> rw[sublist_def]); - -(* Theorem: [Anti-symmetric] !p q. (p <= q) /\ (q <= p) ==> (p = q) *) -(* Proof: - By induction on q. - Base: !p. p <= [] /\ [] <= p ==> (p = []) - Note p <= [] already gives p = [] by sublist_of_nil - Step: !p. p <= q /\ q <= p ==> (p = q) ==> - !h p. p <= h::q /\ h::q <= p ==> (p = h::q) - If p = [], h::q <= [] = F by sublist_def - If p = (h'::t), - If h = h', - ((h::t) <= (h::q)) /\ ((h::q) <= (h::t)) - <=> (t <= q) and (q <= t) by sublist_def, same heads - ==> t = q by inductive hypothesis - <=> (h::t) = (h::q) by list equality - If ~(h = h'), - ((h'::t) <= (h::q)) /\ ((h::q) <= (h'::t)) - <=> ((h'::t) <= q) /\ ((h::q) <= t) by sublist_def, different heads - ==> (LENGTH (h'::t) <= LENGTH q) /\ - (LENGTH (h::q) <= LENGTH t) by sublist_length - ==> (LENGTh t < LENGTH q) /\ - (LENGTH q < LENGTH t) by LENGTH_TL_LT - = F by arithmetic - Hence the implication is T. -*) -val sublist_antisym = store_thm( - "sublist_antisym", - ``!p q:'a list. p <= q /\ q <= p ==> (p = q)``, - Induct_on `q` >- - rw[sublist_of_nil] >> - rpt strip_tac >> - Cases_on `p` >- - fs[sublist_def] >> - (Cases_on `h' = h` >> fs[sublist_def]) >> - `LENGTH (h'::t) <= LENGTH q /\ LENGTH (h::q) <= LENGTH t` by rw[sublist_length] >> - fs[LENGTH_TL_LT]); - -(* Theorem [Transitive]: for all lists p, q, r; (p <= q) /\ (q <= r) ==> (p <= r) *) -(* Proof: - By induction on list r and taking note of cases. - By induction on r. - Base: !p q. p <= q /\ q <= [] ==> p <= [] - Note q = [] by sublist_of_nil - so p = [] by sublist_of_nil - Step: !p q. p <= q /\ q <= r ==> p <= r ==> - !h p q. p <= q /\ q <= h::r ==> p <= h::r - If p = [], true by sublist_nil - If p = h'::t, to show: - h'::t <= q /\ q <= h::r ==> - (h' = h) /\ t <= r \/ - h' <> h /\ h'::t <= r by sublist_def - If q = [], [] <= h::r = F by sublist_def - If q = h''::t', this reduces to: - (1) h' = h'' /\ t <= t' /\ h'' = h /\ t' <= r ==> t <= r - Note t <= t' /\ t' <= r ==> t <= r by induction hypothesis - (2) h' = h'' /\ t <= t' /\ h'' <> h /\ h''::t' <= r ==> h''::t <= r - Note t <= t' ==> h''::t <= h''::t' by sublist_cons - With h''::t' <= r ==> h''::t <= r by induction hypothesis - (3) h' <> h'' /\ h'::t <= t' /\ h'' = h /\ t' <= r ==> - (h' = h) /\ t <= r \/ h' <> h /\ h'::t <= r - Note h'::t <= t' ==> t <= t' by sublist_cons_remove - With t' <= r ==> t <= r by induction hypothesis - Or simply h'::t <= t' /\ t' <= r - ==> h'::t <= r by induction hypothesis - Hence this is true. - (4) h' <> h'' /\ h'::t <= t' /\ h'' <> h /\ h''::t' <= r ==> - (h' = h) /\ t <= r \/ h' <> h /\ h'::t <= r - Same reasoning as (3). -*) -val sublist_trans = store_thm( - "sublist_trans", - ``!p q r:'a list. p <= q /\ q <= r ==> p <= r``, - Induct_on `r` >- - fs[sublist_of_nil] >> - rpt strip_tac >> - (Cases_on `p` >> fs[sublist_def]) >> - (Cases_on `q` >> fs[sublist_def]) >- - metis_tac[] >- - metis_tac[sublist_cons] >- - metis_tac[sublist_cons_remove] >> - metis_tac[sublist_cons_remove]); - -(* The above theorems show that sublist is a partial ordering. *) - -(* Theorem: p <= q ==> SNOC h p <= SNOC h q *) -(* Proof: - By induction on q. - Base: !h p. p <= [] ==> SNOC h p <= SNOC h [] - Note p = [] by sublist_of_nil - Thus SNOC h [] <= SNOC h [] by sublist_refl - Step: !h p. p <= q ==> SNOC h p <= SNOC h q ==> - !h h' p. p <= h::q ==> SNOC h' p <= SNOC h' (h::q) - If p = [], - Note [] <= q by sublist_nil - Thus SNOC h' [] - <= SNOC h' q by induction hypothesis - <= h::SNOC h' q by sublist_cons_include - = SNOC h' (h::q) by SNOC - If p = h''::t, - If h = h'', - Then t <= q by sublist_def, same heads - ==> SNOC h' t <= SNOC h' q by induction hypothesis - ==> h::SNOC h' t <= h::SNOC h' q by rw[sublist_cons - or SNOC h' (h::t) <= SNOC h' (h::q) by SNOC - or SNOC h' p <= SNOC h' (h::q) by p = h::t - If h <> h'', - Then p <= q by sublist_def, different heads - ==> SNOC h' p <= SNOC h' q by induction hypothesis - ==> SNOC h' p <= h::SNOC h' q by sublist_cons_include -*) -val sublist_snoc = store_thm( - "sublist_snoc", - ``!h p q. p <= q ==> SNOC h p <= SNOC h q``, - Induct_on `q` >- - rw[sublist_of_nil, sublist_refl] >> - rw[sublist_def] >> - Cases_on `p` >- - rw[sublist_nil, sublist_cons_include] >> - metis_tac[sublist_def, sublist_cons, SNOC]); - -(* Theorem: MEM x ls ==> [x] <= ls *) -(* Proof: - By induction on ls. - Base: !x. MEM x [] ==> [x] <= [] - True since MEM x [] = F. - Step: !x. MEM x ls ==> [x] <= ls ==> - !h x. MEM x (h::ls) ==> [x] <= h::ls - If x = h, - Then [h] <= h::ls by sublist_nil, sublist_cons - If x <> h, - Then MEM h ls by MEM x (h::ls) - ==> [x] <= ls by induction hypothesis - ==> [x] <= h::ls by sublist_cons_include -*) -val sublist_member_sing = store_thm( - "sublist_member_sing", - ``!ls x. MEM x ls ==> [x] <= ls``, - Induct >- - rw[] >> - rw[] >- - rw[sublist_nil, GSYM sublist_cons] >> - rw[sublist_cons_include]); - -(* Theorem: TAKE n ls <= ls *) -(* Proof: - By induction on ls. - Base: !n. TAKE n [] <= [] - LHS = TAKE n [] = [] by TAKE_def - <= [] = RHS by sublist_nil - Step: !n. TAKE n ls <= ls ==> !h n. TAKE n (h::ls) <= h::ls - If n = 0, - TAKE 0 (h::ls) - = [] by TAKE_def - <= h::ls by sublist_nil - If n <> 0, - TAKE n (h::ls) - = h::TAKE (n - 1) ls by TAKE_def - Now TAKE (n - 1) ls <= ls by induction hypothesis - Thus h::TAKE (n - 1) ls <= h::ls by sublist_cons -*) -val sublist_take = store_thm( - "sublist_take", - ``!ls n. TAKE n ls <= ls``, - Induct >- - rw[sublist_nil] >> - rpt strip_tac >> - Cases_on `n = 0` >- - rw[sublist_nil] >> - rw[GSYM sublist_cons]); - -(* Theorem: DROP n ls <= ls *) -(* Proof: - By induction on ls. - Base: !n. DROP n [] <= [] - LHS = DROP n [] = [] by DROP_def - <= [] = RHS by sublist_nil - Step: !n. DROP n ls <= ls ==> !h n. DROP n (h::ls) <= h::ls - If n = 0, - DROP 0 (h::ls) - = h::ls by DROP_def - <= h::ls by sublist_refl - If n <> 0, - DROP n (h::ls) - = DROP n ls by DROP_def - <= ls by induction hypothesis - <= h::ls by sublist_cons_include -*) -val sublist_drop = store_thm( - "sublist_drop", - ``!ls n. DROP n ls <= ls``, - Induct >- - rw[sublist_nil] >> - rpt strip_tac >> - Cases_on `n = 0` >- - rw[sublist_refl] >> - rw[sublist_cons_include]); - -(* Theorem: ls <> [] ==> TL ls <= ls *) -(* Proof: - Note TL ls = DROP 1 ls by TAIL_BY_DROP, ls <> [] - Thus TL ls <= ls by sublist_drop -*) -val sublist_tail = store_thm( - "sublist_tail", - ``!ls. ls <> [] ==> TL ls <= ls``, - rw[TAIL_BY_DROP, sublist_drop]); - -(* Theorem: ls <> [] ==> FRONT ls <= ls *) -(* Proof: - Note FRONT ls = TAKE (LENGTH ls - 1) ls by FRONT_BY_TAKE - so FRONT ls <= ls by sublist_take -*) -val sublist_front = store_thm( - "sublist_front", - ``!ls. ls <> [] ==> FRONT ls <= ls``, - rw[FRONT_BY_TAKE, sublist_take]); - -(* Theorem: ls <> [] ==> [HD ls] <= ls *) -(* Proof: HEAD_MEM, sublist_member_sing *) -val sublist_head_sing = store_thm( - "sublist_head_sing", - ``!ls. ls <> [] ==> [HD ls] <= ls``, - rw[HEAD_MEM, sublist_member_sing]); - -(* Theorem: ls <> [] ==> [LAST ls] <= ls *) -(* Proof: LAST_MEM, sublist_member_sing *) -val sublist_last_sing = store_thm( - "sublist_last_sing", - ``!ls. ls <> [] ==> [LAST ls] <= ls``, - rw[LAST_MEM, sublist_member_sing]); - -(* Theorem: l <= ls ==> !P. EVERY P ls ==> EVERY P l *) -(* Proof: - By induction on ls. - Base: !l. l <= [] ==> !P. EVERY P [] ==> EVERY P l - Note l <= [] ==> l = [] by sublist_of_nil - and EVERY P [] = T by implication, or EVERY_DEF - Step: !l. l <= ls ==> !P. EVERY P ls ==> EVERY P l ==> - !h l. l <= h::ls ==> !P. EVERY P (h::ls) ==> EVERY P l - l <= h::ls - If l = [], then EVERY P [] = T by EVERY_DEF - Otherwise, let l = k::t by list_CASES - Note EVERY P (h::ls) - ==> P h /\ EVERY P ls by EVERY_DEF - Then k::t <= h::ls - ==> k = h /\ t <= ls - or k <> h /\ k::t <= ls by sublist_def - For the first case, h = k, - P k /\ EVERY P t by induction hypothesis - ==> EVERY P (k::t) = EVERY P l by EVERY_DEF - For the second case, h <> k, - EVERY P (k::t) = EVERY P l by induction hypothesis -*) -val sublist_every = store_thm( - "sublist_every", - ``!l ls. l <= ls ==> !P. EVERY P ls ==> EVERY P l``, - Induct_on `ls` >- - rw[sublist_of_nil] >> - (Cases_on `l` >> simp[]) >> - metis_tac[sublist_def, EVERY_DEF]); - -(* ------------------------------------------------------------------------- *) -(* More sublists, applying partial order properties *) -(* ------------------------------------------------------------------------- *) - -(* Observation: -When doing induction proofs on sublists about p <= q, -Always induct on q, then take cases on p. -*) - -(* The following induction theorem is already present during definition: -> theorem "sublist_ind"; -val it = |- !P. (!x. P [] x) /\ (!h1 t1. P (h1::t1) []) /\ - (!h1 t1 h2 t2. P t1 t2 /\ P (h1::t1) t2 ==> P (h1::t1) (h2::t2)) ==> - !v v1. P v v1: thm - -Just prove it as an exercise. -*) - -(* Theorem: [Induction] For any property P satisfying, - (a) !y. P [] y = T - (b) !h x y. P x y /\ sublist x y ==> P (h::x) (h::y) - (c) !h x y. P x y /\ sublist x y ==> P x (h::y) - then !x y. sublist x y ==> P x y. -*) -(* Proof: - By induction on y. - Base: !x. x <= [] ==> P x [] - Note x = [] by sublist_of_nil - and P [] [] = T by given - Step: !x. x <= y ==> P x y ==> - !h x. x <= h::y ==> P x (h::y) - If x = [], then [] <= h::y = F by sublist_def - If x = h'::t, - If h' = h, t <= y by sublist_def, same heads - Thus P t y by induction hypothesis - and P t y /\ t <= y ==> P (h::t) (h::y) = P x (h::y) - If h' <> h, x <= y by sublist_def, different heads - Thus P x y by induction hypothesis - and P x y /\ x <= y ==> P x (h::y). -*) -val sublist_induct = store_thm( - "sublist_induct", - ``!P. (!y. P [] y) /\ - (!h x y. P x y /\ x <= y ==> P (h::x) (h::y)) /\ - (!h x y. P x y /\ x <= y ==> P x (h::y)) ==> - !x y. x <= y ==> P x y``, - ntac 2 strip_tac >> - Induct_on `y` >- - rw[sublist_of_nil] >> - rpt strip_tac >> - (Cases_on `x` >> fs[sublist_def])); - -(* -Note that from definition: -sublist_ind -|- !P. (!x. P [] x) /\ (!h1 t1. P (h1::t1) []) /\ - (!h1 t1 h2 t2. P t1 t2 /\ P (h1::t1) t2 ==> P (h1::t1) (h2::t2)) ==> - !v v1. P v v1 - -sublist_induct -|- !P. (!y. P [] y) /\ (!h x y. P x y /\ x <= y ==> P (h::x) (h::y)) /\ - (!h x y. P x y /\ x <= y ==> P x (h::y)) ==> - !x y. x <= y ==> P x y - -The second is better. -*) - -(* Theorem: p <= q /\ MEM x p ==> MEM x q *) -(* Proof: - By sublist_induct, this is to show: - (1) MEM x [] ==> MEM x q - Note MEM x [] = F by MEM - Hence true. - (2) MEM x p ==> MEM x q /\ p <= q /\ MEM x (h::p) ==> MEM x (h::q) - If x = h, then MEM h (h::q) = T by MEM - If x <> h, MEM x (h::p) - ==> MEM x p by MEM, x <> h - ==> MEM x q by induction hypothesis - ==> MEM x (h::q) by MEM, x <> h - (3) MEM x p ==> MEM x q /\ p <= q /\ MEM x p ==> MEM x (h::q) - If x = h, then MEM h (h::q) = T by MEM - If x <> h, MEM x p - ==> MEM x q by induction hypothesis - ==> MEM x (h::q) by MEM, x <> h -*) -Theorem sublist_mem: - !p q x. p <= q /\ MEM x p ==> MEM x q -Proof - rpt strip_tac >> - pop_assum mp_tac >> - pop_assum mp_tac >> - qid_spec_tac `q` >> - qid_spec_tac `p` >> - ho_match_mp_tac sublist_induct >> - rpt strip_tac >- - fs[] >- - (Cases_on `x = h` >> fs[]) >> - (Cases_on `x = h` >> fs[]) -QED - -(* Theorem: sl <= ls ==> set sl SUBSET set ls *) -(* Proof: - set sl SUBSET set ls - <=> !x. x IN set (sl) ==> x IN set ls by SUBSET_DEF - <=> !x. MEM x sl ==> MEM x ls by notation - ==> T by sublist_mem -*) -Theorem sublist_subset: - !ls sl. sl <= ls ==> set sl SUBSET set ls -Proof - metis_tac[SUBSET_DEF, sublist_mem] -QED - -(* Theorem: p <= q /\ ALL_DISTINCT q ==> ALL_DISTINCT p *) -(* Proof: - By sublist_induct, this is to show: - (1) ALL_DISTINCT q ==> ALL_DISTINCT [] - Note ALL_DISTINCT [] = T by ALL_DISTINCT - (2) ALL_DISTINCT q ==> ALL_DISTINCT p /\ p <= q /\ ALL_DISTINCT (h::q) ==> ALL_DISTINCT (h::p) - ALL_DISTINCT (h::q) - <=> ~MEM h q /\ ALL_DISTINCT q by ALL_DISTINCT - ==> ~MEM h q /\ ALL_DISTINCT p by induction hypothesis - ==> ~MEM h p /\ ALL_DISTINCT p by sublist_mem - <=> ALL_DISTINCT (h::p) by ALL_DISTINCT - (3) ALL_DISTINCT q ==> ALL_DISTINCT p /\ p <= q /\ ALL_DISTINCT (h::q) ==> ALL_DISTINCT p - ALL_DISTINCT (h::q) - ==> ALL_DISTINCT q by ALL_DISTINCT - ==> ALL_DISTINCT p by induction hypothesis -*) -Theorem sublist_ALL_DISTINCT: - !p q. p <= q /\ ALL_DISTINCT q ==> ALL_DISTINCT p -Proof - rpt strip_tac >> - pop_assum mp_tac >> - pop_assum mp_tac >> - qid_spec_tac `q` >> - qid_spec_tac `p` >> - ho_match_mp_tac sublist_induct >> - rpt strip_tac >- - simp[] >- - (fs[] >> metis_tac[sublist_mem]) >> - fs[] -QED - -(* Theorem: [Eliminate append from left]: (x ++ p) <= q ==> sublist p <= q *) -(* Proof: - By induction on the extra list x. - The induction step follows from sublist_cons_remove. - - By induction on x. - Base: !p q. [] ++ p <= q ==> p <= q - True since [] ++ p = p by APPEND - Step: !p q. x ++ p <= q ==> p <= q ==> - !h p q. h::x ++ p <= q ==> p <= q - h::x ++ p <= q - = h::(x ++ p) <= q by APPEND - ==> (x ++ p) <= q by sublist_cons_remove - ==> p <= q by induction hypothesis -*) -val sublist_append_remove = store_thm( - "sublist_append_remove", - ``!p q x. x ++ p <= q ==> p <= q``, - Induct_on `x` >> metis_tac[sublist_cons_remove, APPEND]); - -(* Theorem: [Include append to right] p <= q ==> p <= (x ++ q) *) -(* Proof: - By induction on list x. - The induction step follows by sublist_cons_include. - - By induction on x. - Base: !p q. p <= q ==> p <= [] ++ q - True since [] ++ q = q by APPEND - Step: !p q. p <= q ==> p <= x ++ q ==> - !h p q. p <= q ==> p <= h::x ++ q - p <= q - ==> p <= x ++ q by induction hypothesis - ==> p <= h::(x ++ q) by sublist_cons_include - = p <= h::x ++ q by APPEND -*) -val sublist_append_include = store_thm( - "sublist_append_include", - ``!p q x. p <= q ==> p <= x ++ q``, - Induct_on `x` >> metis_tac[sublist_cons_include, APPEND]); - -(* Theorem: [append after] p <= (p ++ q) *) -(* Proof: - By induction on list p, and note that p and (p ++ q) have the same head. - Base: !q. [] <= [] ++ q, true by sublist_nil - Step: !q. p <= p ++ q ==> !h q. h::p <= h::p ++ q - p <= p ++ q by induction hypothesis - ==> h::p <= h::(p ++ q) by sublist_cons - ==> h::p <= h::p ++ q by APPEND -*) -val sublist_append_suffix = store_thm( - "sublist_append_suffix", - ``!p q. p <= p ++ q``, - Induct_on `p` >> rw[sublist_def]); - -(* Theorem: [append before] p <= (q ++ p) *) -(* Proof: - By induction on q. - Base: !p. p <= [] ++ p - Note [] ++ p = p by APPEND - and p <= p by sublist_refl - Step: !p. p <= q ++ p ==> !h p. p <= h::q ++ p - p <= q ++ p by induction hypothesis - ==> p <= h::(q ++ p) by sublist_cons_include - = p <= h::q ++ p by APPEND -*) -val sublist_append_prefix = store_thm( - "sublist_append_prefix", - ``!p q. p <= q ++ p``, - Induct_on `q` >- - rw[sublist_refl] >> - rw[sublist_cons_include]); - -(* Theorem: [prefix append] p <= q <=> (x ++ p) <= (x ++ q) *) -(* Proof: - By induction on x. - Base: !p q. p <= q <=> [] ++ p <= [] ++ q - Since [] ++ p = p /\ [] ++ q = q by APPEND - This is trivially true. - Step: !p q. p <= q <=> x ++ p <= x ++ q ==> - !h p q. p <= q <=> h::x ++ p <= h::x ++ q - p <= q <=> x ++ p <= x ++ q by induction hypothesis - <=> h::(x ++ p) <= h::(x ++ q) by sublist_cons - <=> h::x ++ p <= h::x ++ q by APPEND -*) -val sublist_prefix = store_thm( - "sublist_prefix", - ``!x p q. p <= q <=> (x ++ p) <= (x ++ q)``, - Induct_on `x` >> metis_tac[sublist_cons, APPEND]); - -(* Theorem: [no append to left] !p q. (p ++ q) <= q ==> p = [] *) -(* Proof: - By induction on q. - Base: !p. p ++ [] <= [] ==> (p = []) - Note p ++ [] = p by APPEND - and p <= [] ==> p = [] by sublist_of_nil - Step: !p. p ++ q <= q ==> (p = []) ==> - !h p. p ++ h::q <= h::q ==> (p = []) - If p = [], true trivially. - If p = h'::t, - (h'::t) ++ (h::q) <= h::q - = h'::(t ++ h::q) <= h::q by APPEND - If h' = h, - Then t ++ h::q <= q by sublist_def, same heads - or (t ++ [h]) ++ q <= q by APPEND - ==> t ++ [h] = [] by induction hypothesis - which is F, hence h' <> h. - If h' <> h, - Then p ++ h::q <= q by sublist_def, different heads - or (p ++ [h]) ++ q <= q by APPEND - ==> (p ++ [h]) = [] by induction hypothesis - which is F, hence neither h' <> h. - All these shows that p = h'::t is impossible. -*) -val sublist_prefix_nil = store_thm( - "sublist_prefix_nil", - ``!p q. (p ++ q) <= q ==> (p = [])``, - Induct_on `q` >- - rw[sublist_of_nil] >> - rpt strip_tac >> - (Cases_on `p` >> fs[sublist_def]) >| [ - `t ++ h::q = (t ++ [h])++ q` by rw[] >> - `t ++ [h] <> []` by rw[] >> - metis_tac[], - `(t ++ h::q) <= q` by metis_tac[sublist_cons_remove] >> - `t ++ h::q = (t ++ [h])++ q` by rw[] >> - `t ++ [h] <> []` by rw[] >> - metis_tac[] - ]); - -(* Theorem: [tail append - if] p <= q ==> (p ++ [h]) <= (q ++ [h]) *) -(* Proof: - p <= q - ==> SNOC h p <= SNOC h q by sublist_snoc - ==> (p ++ [h]) <= (q ++ [h]) by SNOC_APPEND -*) -Theorem sublist_append_if: - !p q h. p <= q ==> (p ++ [h]) <= (q ++ [h]) -Proof - rw[sublist_snoc, GSYM SNOC_APPEND] -QED - -(* Theorem: [tail append - only if] p ++ [h] <= q ++ [h] ==> p <= q *) -(* Proof: - By induction on q. - Base: !p h. p ++ [h] <= [] ++ [h] ==> p <= [] - Note [] ++ [h] = [h] by APPEND - and p ++ [h] <= [h] ==> p = [] by sublist_prefix_nil - and [] <= [] by sublist_nil - Step: !p h. p ++ [h] <= q ++ [h] ==> p <= q ==> - !h p h'. p ++ [h'] <= h::q ++ [h'] ==> p <= h::q - If p = [], [h'] <= h::q ++ [h'] = F by sublist_def - If p = h''::t, - h''::t ++ [h'] = h''::(t ++ [h']) by APPEND - If h'' = h', - Then t ++ [h'] <= q ++ [h'] by sublist_def, same heads - ==> t <= q by induction hypothesis - If h'' <> h', - Then h''::t ++ [h'] <= q ++ [h'] by sublist_def, different heads - ==> h''::t <= q by induction hypothesis -*) -val sublist_append_only_if = store_thm( - "sublist_append_only_if", - ``!p q h. (p ++ [h]) <= (q ++ [h]) ==> p <= q``, - Induct_on `q` >- - metis_tac[sublist_prefix_nil, sublist_nil, APPEND] >> - rpt strip_tac >> - (Cases_on `p` >> fs[sublist_def]) >- - metis_tac[] >> - `h''::(t ++ [h']) = (h''::t) ++ [h']` by rw[] >> - metis_tac[]); - -(* Theorem: [tail append] p <= q <=> p ++ [h] <= q ++ [h] *) -(* Proof: by sublist_append_if, sublist_append_only_if *) -val sublist_append_iff = store_thm( - "sublist_append_iff", - ``!p q h. p <= q <=> (p ++ [h]) <= (q ++ [h])``, - metis_tac[sublist_append_if, sublist_append_only_if]); - -(* Theorem: [suffix append] sublist p q ==> sublist (p ++ x) (q ++ x) *) -(* Proof: - By induction on x. - Base: !p q. p <= q <=> p ++ [] <= q ++ [] - True by p ++ [] = p, q ++ [] = q by APPEND - Step: !p q. p <= q <=> p ++ x <= q ++ x ==> - !h p q. p <= q <=> p ++ h::x <= q ++ h::x - p <= q - <=> (p ++ [h]) <= (q ++ [h]) by sublist_append_iff - <=> (p ++ [h]) ++ x <= (q ++ [h]) ++ x by induction hypothesis - <=> p ++ (h::x) <= q ++ (h::x) by APPEND -*) -val sublist_suffix = store_thm( - "sublist_suffix", - ``!x p q. p <= q <=> (p ++ x) <= (q ++ x)``, - Induct >- - rw[] >> - rpt strip_tac >> - `!z. z ++ h::x = (z ++ [h]) ++ x` by rw[] >> - metis_tac[sublist_append_iff]); - -(* Theorem : (a <= b) /\ (c <= d) ==> (a ++ c) <= (b ++ d) *) -(* Proof: - Note a ++ c <= a ++ d by sublist_prefix - and a ++ d <= b ++ d by sublist_suffix - ==> a ++ c <= b ++ d by sublist_trans -*) -val sublist_append_pair = store_thm( - "sublist_append_pair", - ``!a b c d. (a <= b) /\ (c <= d) ==> (a ++ c) <= (b ++ d)``, - metis_tac[sublist_prefix, sublist_suffix, sublist_trans]); - -(* Theorem: [Extended Append, or Decomposition] (h::t) <= q <=> ?x y. (q = x ++ (h::y)) /\ (t <= y) *) -(* Proof: - By induction on list q. - Base case is to prove: - sublist (h::t) [] <=> ?x y. ([] = x ++ (h::y)) /\ (sublist t y) - Hypothesis sublist (h::t) [] is F by SUBLIST_NIL. - In the conclusion, [] cannot be decomposed, hence implication is valid. - Step case is to prove: - (sublist (h::t) q <=> ?x y. (q = x ++ (h::y)) /\ (sublist t y)) ==> - (sublist (h::t) (h'::q) <=> ?x y. (h'::q = x ++ (h::y)) /\ (sublist t y)) - Applying SUBLIST definition and split the if-and-only-if parts, there are 4 cases: - When h = h', if part: - sublist (h::t) (h::q) ==> ?x y. (h::q = x ++ (h::y)) /\ (sublist t y) - For this case, choose x=[], y=q, and sublist (h::t) (h::q) = sublist t q, by SUBLIST same head. - When h = h', only-if part: - ?x y. (h::q = x ++ (h::y)) /\ (sublist t y) ==> sublist (h::t) (h::q) - Take cases on x. - When x = [], - h::q = h::y ==> q = y, - hence sublist t y = sublist t q ==> sublist (h::t) (h::q) by SUBLIST same head. - When x = h''::t', - h::q = (h''::t') ++ (h::y) = h''::(t' ++ (h::y)) ==> - q = t' ++ (h::y), - hence sublist t y ==> sublist t q (by SUBLIST_APPENDR_I) ==> sublist (h::t) (h::q). - When ~(h=h'), if part: - sublist (h::t) (h'::q) ==> ?x y. (h'::q = x ++ (h::y)) /\ (sublist t y) - From hypothesis, - sublist (h::t) (h'::q) - = sublist (h::t) q when ~(h=h'), by SUBLIST definition - ==> ?x1 y1. (q = x1 ++ (h::y1)) /\ (sublist t y1)) by inductive hypothesis - Now (h'::q) = (h'::(x1 ++ (h::y1)) = (h'::x1) ++ (h::y1) by APPEND associativity - So taking x = h'::x1, y = y1, this gives the conclusion. - When ~(h=h'), only-if part: - ?x y. (h'::q = x ++ (h::y)) /\ (sublist t y) ==> sublist (h::t) (h'::q) - The case x = [] is impossible by list equality, since ~(h=h'). - Hence x = h'::t', giving: - q = t'++(h::y) /\ (sublist t y) - = sublist (h::t) q by inductive hypothesis (only-if) - ==> sublist (h::t) (h'::q) by SUBLIST, different head. -*) -val sublist_append_extend = store_thm( - "sublist_append_extend", - ``!h t q. h::t <= q <=> ?x y. (q = x ++ (h::y)) /\ (t <= y)``, - ntac 2 strip_tac >> - Induct >- - rw[sublist_of_nil] >> - rpt strip_tac >> - (Cases_on `h = h'` >> rw[EQ_IMP_THM]) >| [ - `h::q = [] ++ [h] ++ q` by rw[] >> - metis_tac[sublist_cons], - `h::t <= h::y` by rw[GSYM sublist_cons] >> - `x ++ [h] ++ y = x ++ (h::y)` by rw[] >> - metis_tac[sublist_append_include], - `h::t <= q` by metis_tac[sublist_def] >> - metis_tac[APPEND, APPEND_ASSOC], - `h::t <= h::y` by rw[GSYM sublist_cons] >> - `x ++ [h] ++ y = x ++ (h::y)` by rw[] >> - metis_tac[sublist_append_include] - ]); - - -(* ------------------------------------------------------------------------- *) -(* Applications of sublist. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: p <= q ==> (MAP f p) <= (MAP f q) *) -(* Proof: - By induction on q. - Base: !p. p <= [] ==> MAP f p <= MAP f [] - Note p = [] by sublist_of_nil - and MAP f [] by MAP - so [] <= [] by sublist_nil - Step: !p. p <= q ==> MAP f p <= MAP f q ==> - !h p. p <= h::q ==> MAP f p <= MAP f (h::q) - If p = [], [] <= h::q = F by sublist_def - If p = h'::t, - If h' = h, - Then t <= q by sublist_def, same heads - ==> MAP f t <= MAP f q by induction hypothesis - ==> h::MAP f t <= h::MAP f q by sublist_cons - ==> MAP f (h::t) <= MAP f (h::q) by MAP - or MAP f p <= MAP f (h::q) by p = h::t - If h' <> h, - Then p <= q by sublist_def, different heads - ==> MAP f p <= MAP f q by induction hypothesis - ==> MAP f p <= h::MAP f q by sublist_cons_include - or MAP f p <= MAP f (h::q) by MAP -*) -val MAP_SUBLIST = store_thm( - "MAP_SUBLIST", - ``!f p q. p <= q ==> (MAP f p) <= (MAP f q)``, - strip_tac >> - Induct_on `q` >- - rw[sublist_of_nil, sublist_nil] >> - rpt strip_tac >> - (Cases_on `p` >> simp[sublist_def]) >> - metis_tac[sublist_def, sublist_cons_include, MAP]); - -(* Theorem: l1 <= l2 ==> SUM l1 <= SUM l2 *) -(* Proof: - By induction on q. - Base: !p. p <= [] ==> SUM p <= SUM [] - Note p = [] by sublist_of_nil - and SUM [] = 0 by SUM - Hence true. - Step: !p. p <= q ==> SUM p <= SUM q ==> - !h p. p <= h::q ==> SUM p <= SUM (h::q) - If p = [], [] <= h::q = F by sublist_def - If p = h'::t, - If h' = h, - Then t <= q by sublist_def, same heads - ==> SUM t <= SUM q by induction hypothesis - ==> h + SUM t <= h + SUM q by arithmetic - ==> SUM (h::t) <= SUM (h::q) by SUM - or SUM p <= SUM (h::q) by p = h::t - If h' <> h, - Then p <= q by sublist_def, different heads - ==> SUM p <= SUM q by induction hypothesis - ==> SUM p <= h + SUM q by arithmetic - ==> SUM p <= SUM (h::q) by SUM -*) -val SUM_SUBLIST = store_thm( - "SUM_SUBLIST", - ``!p q. p <= q ==> SUM p <= SUM q``, - Induct_on `q` >- - rw[sublist_of_nil] >> - rpt strip_tac >> - (Cases_on `p` >> fs[sublist_def]) >> - `h' + SUM t <= SUM q` by metis_tac[SUM] >> - decide_tac); - -(* Theorem: m < n ==> [m; n] <= [m .. n] *) -(* Proof: - By induction on n. - Base: !m. m < 0 ==> [m; 0] <= [m .. 0], true by m < 0 = F. - Step: !m. m < n ==> [m; n] <= [m .. n] ==> - !m. m < SUC n ==> [m; SUC n] <= [m .. SUC n] - Note m < SUC n means m <= n. - If m = n, LHS = [n; SUC n] - RHS = [n .. (n + 1)] = [n; SUC n] by ADD1 - = LHS, thus true by sublist_refl - If m < n, [m; n] <= [m .. n] by induction hypothesis - SNOC (SUC n) [m; n] <= SNOC (SUC n) [m .. n] by sublist_snoc - [m; n; SUC n] <= [m .. SUC n] by SNOC, listRangeINC_SNOC - But [m; SUC n] <= [m; n; SUC n] by sublist_def - Thus [m; SUC n] <= [m .. SUC n] by sublist_trans -*) -val listRangeINC_sublist = store_thm( - "listRangeINC_sublist", - ``!m n. m < n ==> [m; n] <= [m .. n]``, - Induct_on `n` >- - rw[] >> - rpt strip_tac >> - `(m = n) \/ m < n` by decide_tac >| [ - rw[listRangeINC_def, ADD1] >> - rw[sublist_refl], - `[m; n] <= [m .. n]` by rw[] >> - `SNOC (SUC n) [m; n] <= SNOC (SUC n) [m .. n]` by rw[sublist_snoc] >> - `SNOC (SUC n) [m; n] = [m; n; SUC n]` by rw[] >> - `SNOC (SUC n) [m .. n] = [m .. SUC n]` by rw[listRangeINC_SNOC, ADD1] >> - `[m; SUC n] <= [m; n; SUC n]` by rw[sublist_def] >> - metis_tac[sublist_trans] - ]); - -(* Theorem: m + 1 < n ==> [m; (n - 1)] <= [m ..< n] *) -(* Proof: - By induction on n. - Base: !m. m + 1 < 0 ==> [m; 0 - 1] <= [m ..< 0], true by m + 1 < 0 = F. - Step: !m. m + 1 < n ==> [m; n - 1] <= [m ..< n] ==> - !m. m + 1 < SUC n ==> [m; SUC n - 1] <= [m ..< SUC n] - Note m + 1 < SUC n means m + 1 <= n. - If m + 1 = n, LHS = [m; SUC n - 1] = [m; n] - RHS = [m ..< (n + 1)] = [m; n] by ADD1 - = LHS, thus true by sublist_refl - If m + 1 < n, [m; n - 1] <= [m ..< n] by induction hypothesis - SNOC n [m; n - 1] <= SNOC n [m ..< n] by sublist_snoc - [m; n - 1; n] <= [m ..< SUC n] by SNOC, listRangeLHI_SNOC, ADD1 - But [m; SUC n - 1] <= [m; n] <= [m; n - 1; n] by sublist_def - Thus [m; SUC n - 1] <= [m ..< SUC n] by sublist_trans -*) -val listRangeLHI_sublist = store_thm( - "listRangeLHI_sublist", - ``!m n. m + 1 < n ==> [m; (n - 1)] <= [m ..< n]``, - Induct_on `n` >- - rw[] >> - rpt strip_tac >> - `SUC n - 1 = n` by decide_tac >> - `(m + 1 = n) \/ m + 1 < n` by decide_tac >| [ - rw[listRangeLHI_def, ADD1] >> - rw[sublist_refl], - `[m; n - 1] <= [m ..< n]` by rw[] >> - `SNOC n [m; n - 1] <= SNOC n [m ..< n]` by rw[sublist_snoc] >> - `SNOC n [m; n - 1] = [m; n - 1; n]` by rw[] >> - `SNOC n [m ..< n] = [m ..< SUC n]` by rw[listRangeLHI_SNOC, ADD1] >> - `[m; SUC n - 1] <= [m; n - 1; n]` by rw[sublist_def] >> - metis_tac[sublist_trans] - ]); - -(* Idea: express order-preserving in sublist *) - -(* Note: -A simple statement of order-preserving: - -g `p <= q /\ MEM x p /\ MEM y p /\ findi x p <= findi y p ==> findi x q <= findi y q`; - -This simple statement has a counter-example: -q = [1;2;3;4;3;5;1] -p = [2;4;1] -MEM 4 p /\ MEM 1 p /\ findi 4 p = 1 <= findi 1 p = 2, but findi 4 q = 3, yet findi 1 q = 0. -This is because findi gives the first appearance of the member. -This can be fixed by ALL_DISTINCT, but the idea of order-preserving should not depend on ALL_DISTINCT. -*) - -(* Theorem: sl <= ls /\ MEM x sl ==> - ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 *) -(* Proof: - By sublist_induct, this is to show: - (1) MEM x [] ==> ?l1 l2 l3 l4... - Note MEM x [] = F by MEM - hence true. - (2) MEM x sl ==> ?l1 l2 l3 l4... /\ sl <= ls /\ MEM x (h::sl) ==> - ?l1 l2 l3 l4. h::ls = l1 ++ [x] ++ l2 /\ h::sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 - Note MEM x (h::sl) - ==> x = h \/ MEM x sl by MEM - If x = h, - Then h::ls = [h] ++ ls by CONS_APPEND - and h::sl = [h] ++ sl by CONS_APPEND - Pick l1 = [], l2 = ls, l3 = [], l4 = sl. - Then l3 <= l1 since by sublist_nil - and l4 <= l2 since sl <= ls by induction hypothesis - Otherwise, MEM x sl, - Note ?l1 l2 l3 l4. - ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 - by induction hypothesis - Then h::ls = h::(l1 ++ [x] ++ l2) - = h::l1 ++ [x] ++ l2 by APPEND - and h::sl = h::(l3 ++ [x] ++ l4) - = h::l3 ++ [x] ++ l4 by APPEND - Pick new l1 = h::l1, l2 = l2, l3 = h::l3, l4 = l4. - Then l3 <= l1 ==> h::l3 <= h::l1 by sublist_cons - (3) MEM x sl ==> ?l1 l2 l3 l4... /\ sl <= ls /\ MEM x sl ==> - ?l1 l2 l3 l4. h::ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 - Note ?l1 l2 l3 l4. - ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 - by induction hypothesis - Then h::ls = h::(l1 ++ [x] ++ l2) - = h::l1 ++ [x] ++ l2 by APPEND - Pick new l1 = h::l1, l2 = l2, l3 = l3, l4 = l4. - Then l3 <= l1 ==> l3 <= h::l1 by sublist_cons_include -*) -Theorem sublist_order: - !ls sl x. sl <= ls /\ MEM x sl ==> - ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 -Proof - rpt strip_tac >> - pop_assum mp_tac >> - pop_assum mp_tac >> - qid_spec_tac `ls` >> - qid_spec_tac `sl` >> - ho_match_mp_tac sublist_induct >> - rpt strip_tac >- - fs[] >- - (fs[] >| [ - map_every qexists_tac [`[]`, `ls`, `[]`, `sl`] >> - simp[sublist_nil], - fs[] >> - map_every qexists_tac [`h::l1`, `l2`, `h::l3`, `l4`] >> - simp[GSYM sublist_cons] - ]) >> - fs[] >> - map_every qexists_tac [`h::l1`, `l2`, `l3`, `l4`] >> - simp[sublist_cons_include] -QED - -(* Theorem: sl <= ls /\ ALL_DISTINCT ls /\ j < h /\ h < LENGTH sl ==> - findi (EL j sl) ls < findi (EL h sl) ls *) -(* Proof: - Let x = EL j sl, - y = EL h sl, - p = findi x ls, - q = findi y ls. - Then MEM x sl /\ MEM y sl by EL_MEM - and ALL_DISTINCT sl by sublist_ALL_DISTINCT - - With MEM x sl, - Note ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ - sl = l3 ++ [x] ++ l4 /\ - l3 <= l1 /\ l4 <= l2 by sublist_order, sl <= ls - Thus j = LENGTH l3 by ALL_DISTINCT_EL_APPEND, j < LENGTH sl - - Claim: MEM y l4 - Proof: By contradiction, suppose ~MEM y l4. - Note y <> x by ALL_DISTINCT_EL_IMP, j <> h - ==> MEM y l3 by MEM_APPEND - ==> ?k. k < LENGTH l3 /\ y = EL k l3 by MEM_EL - But LENGTH l3 < LENGTH sl by LENGTH_APPEND - and y = EL k sl by EL_APPEND1 - Thus k = h by ALL_DISTINCT_EL_IMP, k < LENGTH sl - or h < j, contradicting j < h by j = LENGTH l3 - - Thus ?l5 l6 l7 l8. l2 = l5 ++ [x] ++ l6 /\ - l4 = l7 ++ [x] ++ l8 /\ - l7 <= l5 /\ l8 <= l6 by sublist_order, l4 <= l2 - - Hence, ls = l1 ++ [x] ++ l5 ++ [y] ++ l6. - Now p < LENGTH ls /\ q < LENGTH ls by MEM_findi - so x = EL p ls /\ y = EL q ls by findi_EL_iff - and p = LENGTH l1 by ALL_DISTINCT_EL_APPEND - and q = LENGTH (l1 ++ [x] ++ l5) by ALL_DISTINCT_EL_APPEND - Thus p < q by LENGTH_APPEND -*) -Theorem sublist_element_order: - !ls sl j h. sl <= ls /\ ALL_DISTINCT ls /\ j < h /\ h < LENGTH sl ==> - findi (EL j sl) ls < findi (EL h sl) ls -Proof - rpt strip_tac >> - qabbrev_tac `x = EL j sl` >> - qabbrev_tac `y = EL h sl` >> - qabbrev_tac `p = findi x ls` >> - qabbrev_tac `q = findi y ls` >> - `MEM x sl /\ MEM y sl` by fs[EL_MEM, Abbr`x`, Abbr`y`] >> - assume_tac sublist_order >> - last_x_assum (qspecl_then [`ls`, `sl`, `x`] strip_assume_tac) >> - rfs[] >> - `ALL_DISTINCT sl` by metis_tac[sublist_ALL_DISTINCT] >> - `j = LENGTH l3` by metis_tac[ALL_DISTINCT_EL_APPEND, LESS_TRANS] >> - `MEM y l4` by - (spose_not_then strip_assume_tac >> - `y <> x` by fs[ALL_DISTINCT_EL_IMP, Abbr`x`, Abbr`y`] >> - `MEM y l3` by fs[] >> - `?k. k < LENGTH l3 /\ y = EL k l3` by simp[GSYM MEM_EL] >> - `LENGTH l3 < LENGTH sl` by fs[] >> - `y = EL k sl` by fs[EL_APPEND1] >> - `k = h` by metis_tac[ALL_DISTINCT_EL_IMP, LESS_TRANS] >> - decide_tac) >> - assume_tac sublist_order >> - last_x_assum (qspecl_then [`l2`, `l4`, `y`] strip_assume_tac) >> - rfs[] >> - rename1 `l2 = l5 ++ [y] ++ l6` >> - `p < LENGTH ls /\ q < LENGTH ls` by fs[MEM_findi, Abbr`p`, Abbr`q`] >> - `x = EL p ls /\ y = EL q ls` by fs[findi_EL_iff, Abbr`p`, Abbr`q`] >> - `p = LENGTH l1` by metis_tac[ALL_DISTINCT_EL_APPEND] >> - `ls = l1 ++ [x] ++ l5 ++ [y] ++ l6` by fs[] >> - `q = LENGTH (l1 ++ [x] ++ l5)` by metis_tac[ALL_DISTINCT_EL_APPEND] >> - fs[] -QED - -(* Theorem: sl <= ls /\ MONO_INC ls ==> MONO_INC sl *) -(* Proof: - By sublist induction, this is to show: - (1) n < LENGTH [] /\ m <= n ==> EL m [] <= EL n [] - Note LENGTH [] = 0 by LENGTH - so assumption is F, hence T. - (2) MONO_INC ls ==> MONO_INC sl /\ sl <= ls /\ - MONO_INC (h::ls) /\ m <= n /\ n < LENGTH (h::sl) ==> EL m (h::sl) <= EL n (h::sl) - Note MONO_INC (h::ls) ==> MONO_INC ls by MONO_INC_CONS - If m = 0, - If n = 0, - Then EL 0 (h::sl) = h, hence T. - If 0 < n, - Then 0 <= PRE n, - so EL n (h::sl) = EL (PRE n) sl - Let x = EL 0 sl. - Then x <= EL (PRE n) sl by MONO_INC sl - But MEM x sl by EL_MEM - ==> MEM x ls by sublist_mem - so h <= x by MONO_INC (h::ls) - Thus h <= EL n (h::sl) by inequality - If 0 < m, - Then m <= n means 0 < n. - Thus PRE m <= PRE n - EL m (h::sl) - = EL (PRE m) sl by EL_CONS, 0 < m - <= EL (PRE n) sl by induction hypothesis - = EL n (h::sl) by EL_CONS, 0 < n - - (3) MONO_INC ls ==> MONO_INC sl /\ sl <= ls /\ - MONO_INC (h::ls) /\ m <= n /\ n < LENGTH sl ==> EL m sl <= EL n sl - Note MONO_INC (h::ls) ==> MONO_INC ls by MONO_INC_CONS - Thus MONO_INC sl by induction hypothesis - so m <= n ==> EL m sl <= EL n sl by MONO_INC sl -*) -Theorem sublist_MONO_INC: - !ls sl. sl <= ls /\ MONO_INC ls ==> MONO_INC sl -Proof - ntac 3 strip_tac >> - pop_assum mp_tac >> - pop_assum mp_tac >> - qid_spec_tac `ls` >> - qid_spec_tac `sl` >> - ho_match_mp_tac sublist_induct >> - rpt strip_tac >- - fs[] >- - (`MONO_INC ls` by metis_tac[MONO_INC_CONS] >> - `m = 0 \/ 0 < m` by decide_tac >| [ - `n = 0 \/ 0 < n` by decide_tac >- - simp[] >> - `0 <= PRE n` by decide_tac >> - qabbrev_tac `x = EL 0 sl` >> - `x <= EL (PRE n) sl` by fs[Abbr`x`] >> - `MEM x sl` by fs[EL_MEM, Abbr`x`] >> - `h <= x` by metis_tac[MONO_INC_HD, sublist_mem] >> - simp[EL_CONS], - `0 < n /\ PRE m <= PRE n` by decide_tac >> - `EL (PRE m) sl <= EL (PRE n) sl` by fs[] >> - simp[EL_CONS] - ]) >> - `MONO_INC ls` by metis_tac[MONO_INC_CONS] >> - fs[] -QED - -(* Theorem: sl <= ls /\ MONO_DEC ls ==> MONO_DEC sl *) -(* Proof: - By sublist induction, this is to show: - (1) n < LENGTH [] /\ m <= n ==> EL n [] <= EL m [] - Note LENGTH [] = 0 by LENGTH - so assumption is F, hence T. - (2) MONO_DEC ls ==> MONO_DEC sl /\ sl <= ls /\ - MONO_DEC (h::ls) /\ m <= n /\ n < LENGTH (h::sl) ==> EL n (h::sl) <= EL m (h::sl) - Note MONO_DEC (h::ls) ==> MONO_DEC ls by MONO_DEC_CONS - If m = 0, - If n = 0, - Then EL 0 (h::sl) = h, hence T. - If 0 < n, - Then 0 <= PRE n, - so EL n (h::sl) = EL (PRE n) sl - Let x = EL 0 sl. - Then EL (PRE n) sl <= x by MONO_DEC sl - But MEM x sl by EL_MEM - ==> MEM x ls by sublist_mem - so x <= h by MONO_DEC (h::ls) - Thus EL n (h::sl) <= h by inequality - If 0 < m, - Then m <= n means 0 < n. - Thus PRE m <= PRE n - EL n (h::sl) - = EL (PRE n) sl by EL_CONS, 0 < n - <= EL (PRE m) sl by induction hypothesis - = EL m (h::sl) by EL_CONS, 0 < m - - (3) MONO_DEC ls ==> MONO_DEC sl /\ sl <= ls /\ - MONO_DEC (h::ls) /\ m <= n /\ n < LENGTH sl ==> EL n sl <= EL m sl - Note MONO_DEC (h::ls) ==> MONO_DEC ls by MONO_DEC_CONS - Thus MONO_DEC sl by induction hypothesis - so m <= n ==> EL n sl <= EL m sl by MONO_DEC sl -*) -Theorem sublist_MONO_DEC: - !ls sl. sl <= ls /\ MONO_DEC ls ==> MONO_DEC sl -Proof - ntac 3 strip_tac >> - pop_assum mp_tac >> - pop_assum mp_tac >> - qid_spec_tac `ls` >> - qid_spec_tac `sl` >> - ho_match_mp_tac sublist_induct >> - rpt strip_tac >- - fs[] >- - (`MONO_DEC ls` by metis_tac[MONO_DEC_CONS] >> - `m = 0 \/ 0 < m` by decide_tac >| [ - `n = 0 \/ 0 < n` by decide_tac >- - simp[] >> - `0 <= PRE n` by decide_tac >> - qabbrev_tac `x = EL 0 sl` >> - `EL (PRE n) sl <= x` by fs[Abbr`x`] >> - `MEM x sl` by fs[EL_MEM, Abbr`x`] >> - `x <= h` by metis_tac[MONO_DEC_HD, sublist_mem] >> - simp[EL_CONS], - `0 < n /\ PRE m <= PRE n` by decide_tac >> - `EL (PRE n) sl <= EL (PRE m) sl` by fs[] >> - simp[EL_CONS] - ]) >> - `MONO_DEC ls` by metis_tac[MONO_DEC_CONS] >> - fs[] -QED - -(* Yes, finally! *) - -(* ------------------------------------------------------------------------- *) -(* FILTER as sublist. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: FILTER P ls <= ls *) -(* Proof: - By induction on ls. - Base: FILTER P [] <= [], - Note FILTER P [] = [] by FILTER - and [] <= [] by sublist_refl - Step: FILTER P ls <= ls ==> - !h. FILTER P (h::ls) <= h::ls - If P h, - FILTER P ls <= ls by induction hypothesis - ==> h::FILTER P ls <= h::ls by sublist_cons - ==> FILTER P (h::ls) <= h::ls by FILTER, P h. - - If ~P h, - FILTER P ls <= ls by induction hypothesis - ==> FILTER P ls <= h::ls by sublist_cons_include - ==> FILTER P (h::ls) <= h::ls by FILTER, ~P h. -*) -Theorem FILTER_sublist: - !P ls. FILTER P ls <= ls -Proof - strip_tac >> - Induct >- - simp[sublist_refl] >> - rpt strip_tac >> - Cases_on `P h` >- - metis_tac[FILTER, sublist_cons] >> - metis_tac[FILTER, sublist_cons_include] -QED - -(* Theorem: let fs = FILTER P ls in ALL_DISTINCT ls /\ j < h /\ h < LENGTH fs ==> - findi (EL j fs) ls < findi (EL h fs) l *) -(* Proof: - Let fs = FILTER P ls. - Then fs <= ls by FILTER_sublist - Thus findi (EL j fs) ls - < findi (EL h fs) ls by sublist_element_order -*) -Theorem FILTER_element_order: - !P ls j h. let fs = FILTER P ls in ALL_DISTINCT ls /\ j < h /\ h < LENGTH fs ==> - findi (EL j fs) ls < findi (EL h fs) ls -Proof - rw_tac std_ss[] >> - `fs <= ls` by simp[FILTER_sublist, Abbr`fs`] >> - fs[sublist_element_order] -QED - -(* Theorem: MONO_INC ls ==> MONO_INC (FILTER P ls) *) -(* Proof: - Note (FILTER P ls) <= ls by FILTER_sublist - With MONO_INC ls - ==> MONO_INC (FILTER P ls) by sublist_MONO_INC -*) -Theorem FILTER_MONO_INC: - !P ls. MONO_INC ls ==> MONO_INC (FILTER P ls) -Proof - metis_tac[FILTER_sublist, sublist_MONO_INC] -QED - -(* Theorem: MONO_DEC ls ==> MONO_DEC (FILTER P ls) *) -(* Proof: - Note (FILTER P ls) <= ls by FILTER_sublist - With MONO_DEC ls - ==> MONO_DEC (FILTER P ls) by sublist_MONO_DEC -*) -Theorem FILTER_MONO_DEC: - !P ls. MONO_DEC ls ==> MONO_DEC (FILTER P ls) -Proof - metis_tac[FILTER_sublist, sublist_MONO_DEC] -QED - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/lib/triangleScript.sml b/examples/algebra/lib/triangleScript.sml deleted file mode 100644 index d8525aafc3..0000000000 --- a/examples/algebra/lib/triangleScript.sml +++ /dev/null @@ -1,6622 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Leibniz Harmonic Triangle *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "triangle"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* Get dependent theories local *) - -(* Get dependent theories in lib *) -(* val _ = load "EulerTheory"; *) -open EulerTheory; (* for upto_by_count *) -(* (* val _ = load "helperFunctionTheory"; -- in EulerTheory *) *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* val _ = load "helperListTheory"; *) -open helperListTheory; - -(* open dependent theories *) -open arithmeticTheory; -open pred_setTheory; -open listTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* val _ = load "binomialTheory"; *) -open binomialTheory; - -(* use listRange: [1 .. 3] = [1; 2; 3], [1 ..< 3] = [1; 2] *) -(* val _ = load "listRangeTheory"; *) -open listRangeTheory; -open rich_listTheory; (* for EVERY_REVERSE *) -open relationTheory; (* for RTC *) - - -(* ------------------------------------------------------------------------- *) -(* Leibniz Harmonic Triangle Documentation *) -(* ------------------------------------------------------------------------- *) -(* Type: (# are temp) - triple = <| a: num; b: num; c: num |> -# path = :num list - Overloading: - leibniz_vertical n = [1 .. (n+1)] - leibniz_up n = REVERSE (leibniz_vertical n) - leibniz_horizontal n = GENLIST (leibniz n) (n + 1) - binomial_horizontal n = GENLIST (binomial n) (n + 1) -# ta = (triplet n k).a -# tb = (triplet n k).b -# tc = (triplet n k).c - p1 zigzag p2 = leibniz_zigzag p1 p2 - p1 wriggle p2 = RTC leibniz_zigzag p1 p2 - leibniz_col_arm a b n = MAP (\x. leibniz (a - x) b) [0 ..< n] - leibniz_seg_arm a b n = MAP (\x. leibniz a (b + x)) [0 ..< n] - - leibniz_seg n k h = IMAGE (\j. leibniz n (k + j)) (count h) - leibniz_row n h = IMAGE (leibniz n) (count h) - leibniz_col h = IMAGE (\i. leibniz i 0) (count h) - lcm_run n = list_lcm [1 .. n] -# beta n k = k * binomial n k -# beta_horizontal n = GENLIST (beta n o SUC) n -*) -(* Definitions and Theorems (# are exported): - - Helper Theorems: - RTC_TRANS |- R^* x y /\ R^* y z ==> R^* x z - - Leibniz Triangle (Denominator form): -# leibniz_def |- !n k. leibniz n k = (n + 1) * binomial n k - leibniz_0_n |- !n. leibniz 0 n = if n = 0 then 1 else 0 - leibniz_n_0 |- !n. leibniz n 0 = n + 1 - leibniz_n_n |- !n. leibniz n n = n + 1 - leibniz_less_0 |- !n k. n < k ==> (leibniz n k = 0) - leibniz_sym |- !n k. k <= n ==> (leibniz n k = leibniz n (n - k)) - leibniz_monotone |- !n k. k < HALF n ==> leibniz n k < leibniz n (k + 1) - leibniz_pos |- !n k. k <= n ==> 0 < leibniz n k - leibniz_eq_0 |- !n k. (leibniz n k = 0) <=> n < k - leibniz_alt |- !n. leibniz n = (\j. (n + 1) * j) o binomial n - leibniz_def_alt |- !n k. leibniz n k = (\j. (n + 1) * j) (binomial n k) - leibniz_up_eqn |- !n. 0 < n ==> !k. (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k - leibniz_up |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1) - leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k - leibniz_right_eqn |- !n. 0 < n ==> !k. (k + 1) * leibniz n (k + 1) = (n - k) * leibniz n k - leibniz_right |- !n. 0 < n ==> !k. leibniz n (k + 1) = (n - k) * leibniz n k DIV (k + 1) - leibniz_property |- !n. 0 < n ==> !k. leibniz n k * leibniz (n - 1) k = - leibniz n (k + 1) * (leibniz n k - leibniz (n - 1) k) - leibniz_formula |- !n k. k <= n ==> (leibniz n k = (n + 1) * FACT n DIV (FACT k * FACT (n - k))) - leibniz_recurrence |- !n. 0 < n ==> !k. k < n ==> (leibniz n (k + 1) = leibniz n k * - leibniz (n - 1) k DIV (leibniz n k - leibniz (n - 1) k)) - leibniz_n_k |- !n k. 0 < k /\ k <= n ==> (leibniz n k = - leibniz n (k - 1) * leibniz (n - 1) (k - 1) - DIV (leibniz n (k - 1) - leibniz (n - 1) (k - 1))) - leibniz_lcm_exchange |- !n. 0 < n ==> !k. lcm (leibniz n k) (leibniz (n - 1) k) = - lcm (leibniz n k) (leibniz n (k + 1)) - leibniz_middle_lower |- !n. 4 ** n <= leibniz (TWICE n) n - - LCM of a list of numbers: -# list_lcm_def |- (list_lcm [] = 1) /\ !h t. list_lcm (h::t) = lcm h (list_lcm t) - list_lcm_nil |- list_lcm [] = 1 - list_lcm_cons |- !h t. list_lcm (h::t) = lcm h (list_lcm t) - list_lcm_sing |- !x. list_lcm [x] = x - list_lcm_snoc |- !x l. list_lcm (SNOC x l) = lcm x (list_lcm l) - list_lcm_map_times |- !n l. list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l - list_lcm_pos |- !l. EVERY_POSITIVE l ==> 0 < list_lcm l - list_lcm_pos_alt |- !l. POSITIVE l ==> 0 < list_lcm l - list_lcm_lower_bound |- !l. EVERY_POSITIVE l ==> SUM l <= LENGTH l * list_lcm l - list_lcm_lower_bound_alt |- !l. POSITIVE l ==> SUM l <= LENGTH l * list_lcm l - list_lcm_is_common_multiple |- !x l. MEM x l ==> x divides (list_lcm l) - list_lcm_is_least_common_multiple |- !l m. (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m - list_lcm_append |- !l1 l2. list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2) - list_lcm_append_3 |- !l1 l2 l3. list_lcm (l1 ++ l2 ++ l3) = list_lcm [list_lcm l1; list_lcm l2; list_lcm l3] - list_lcm_reverse |- !l. list_lcm (REVERSE l) = list_lcm l - list_lcm_suc |- !n. list_lcm [1 .. n + 1] = lcm (n + 1) (list_lcm [1 .. n]) - list_lcm_nonempty_lower |- !l. l <> [] /\ EVERY_POSITIVE l ==> SUM l DIV LENGTH l <= list_lcm l - list_lcm_nonempty_lower_alt |- !l. l <> [] /\ POSITIVE l ==> SUM l DIV LENGTH l <= list_lcm l - list_lcm_divisor_lcm_pair |- !l x y. MEM x l /\ MEM y l ==> lcm x y divides list_lcm l - list_lcm_lower_by_lcm_pair |- !l x y. POSITIVE l /\ MEM x l /\ MEM y l ==> lcm x y <= list_lcm l - list_lcm_upper_by_common_multiple - |- !l m. 0 < m /\ (!x. MEM x l ==> x divides m) ==> list_lcm l <= m - list_lcm_by_FOLDR |- !ls. list_lcm ls = FOLDR lcm 1 ls - list_lcm_by_FOLDL |- !ls. list_lcm ls = FOLDL lcm 1 ls - - Lists in Leibniz Triangle: - - Veritcal Lists in Leibniz Triangle - leibniz_vertical_alt |- !n. leibniz_vertical n = GENLIST (\i. 1 + i) (n + 1) - leibniz_vertical_0 |- leibniz_vertical 0 = [1] - leibniz_vertical_len |- !n. LENGTH (leibniz_vertical n) = n + 1 - leibniz_vertical_not_nil |- !n. leibniz_vertical n <> [] - leibniz_vertical_pos |- !n. EVERY_POSITIVE (leibniz_vertical n) - leibniz_vertical_pos_alt |- !n. POSITIVE (leibniz_vertical n) - leibniz_vertical_mem |- !n x. 0 < x /\ x <= n + 1 <=> MEM x (leibniz_vertical n) - leibniz_vertical_snoc |- !n. leibniz_vertical (n + 1) = SNOC (n + 2) (leibniz_vertical n) - - leibniz_up_0 |- leibniz_up 0 = [1] - leibniz_up_len |- !n. LENGTH (leibniz_up n) = n + 1 - leibniz_up_pos |- !n. EVERY_POSITIVE (leibniz_up n) - leibniz_up_mem |- !n x. 0 < x /\ x <= n + 1 <=> MEM x (leibniz_up n) - leibniz_up_cons |- !n. leibniz_up (n + 1) = n + 2::leibniz_up n - - leibniz_horizontal_0 |- leibniz_horizontal 0 = [1] - leibniz_horizontal_len |- !n. LENGTH (leibniz_horizontal n) = n + 1 - leibniz_horizontal_el |- !n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k) - leibniz_horizontal_mem |- !n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) - leibniz_horizontal_mem_iff |- !n k. MEM (leibniz n k) (leibniz_horizontal n) <=> k <= n - leibniz_horizontal_member |- !n x. MEM x (leibniz_horizontal n) <=> ?k. k <= n /\ (x = leibniz n k) - leibniz_horizontal_element |- !n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k) - leibniz_horizontal_head |- !n. TAKE 1 (leibniz_horizontal (n + 1)) = [n + 2] - leibniz_horizontal_divisor|- !n k. k <= n ==> leibniz n k divides list_lcm (leibniz_horizontal n) - leibniz_horizontal_pos |- !n. EVERY_POSITIVE (leibniz_horizontal n) - leibniz_horizontal_pos_alt|- !n. POSITIVE (leibniz_horizontal n) - leibniz_horizontal_alt |- !n. leibniz_horizontal n = MAP (\j. (n + 1) * j) (binomial_horizontal n) - leibniz_horizontal_lcm_alt|- !n. list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n) - leibniz_horizontal_sum |- !n. SUM (leibniz_horizontal n) = (n + 1) * SUM (binomial_horizontal n) - leibniz_horizontal_sum_eqn |- !n. SUM (leibniz_horizontal n) = (n + 1) * 2 ** n: - leibniz_horizontal_average |- !n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = - SUM (binomial_horizontal n) - leibniz_horizontal_average_eqn |- !n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = 2 ** n - - Using Triplet and Paths: - triplet_def |- !n k. triplet n k = - <|a := leibniz n k; - b := leibniz (n + 1) k; - c := leibniz (n + 1) (k + 1) - |> - leibniz_triplet_member |- !n k. (ta = leibniz n k) /\ - (tb = leibniz (n + 1) k) /\ (tc = leibniz (n + 1) (k + 1)) - leibniz_right_entry |- !n k. (k + 1) * tc = (n + 1 - k) * tb - leibniz_up_entry |- !n k. (n + 2) * ta = (n + 1 - k) * tb - leibniz_triplet_property |- !n k. ta * tb = tc * (tb - ta) - leibniz_triplet_lcm |- !n k. lcm tb ta = lcm tb tc - - Zigzag Path in Leibniz Triangle: - leibniz_zigzag_def |- !p1 p2. p1 zigzag p2 <=> - ?n k x y. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) - list_lcm_zigzag |- !p1 p2. p1 zigzag p2 ==> (list_lcm p1 = list_lcm p2) - leibniz_zigzag_tail |- !p1 p2. p1 zigzag p2 ==> !x. [x] ++ p1 zigzag [x] ++ p2 - leibniz_horizontal_zigzag |- !n k. k <= n ==> - TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) zigzag - TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) - leibniz_triplet_0 |- leibniz_up 1 zigzag leibniz_horizontal 1 - - Wriggle Paths in Leibniz Triangle: - list_lcm_wriggle |- !p1 p2. p1 wriggle p2 ==> (list_lcm p1 = list_lcm p2) - leibniz_zigzag_wriggle |- !p1 p2. p1 zigzag p2 ==> p1 wriggle p2 - leibniz_wriggle_tail |- !p1 p2. p1 wriggle p2 ==> !x. [x] ++ p1 wriggle [x] ++ p2 - leibniz_wriggle_refl |- !p1. p1 wriggle p1 - leibniz_wriggle_trans |- !p1 p2 p3. p1 wriggle p2 /\ p2 wriggle p3 ==> p1 wriggle p3 - leibniz_horizontal_wriggle_step |- !n k. k <= n + 1 ==> - TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle leibniz_horizontal (n + 1) - leibniz_horizontal_wriggle |- !n. [leibniz (n + 1) 0] ++ leibniz_horizontal n wriggle leibniz_horizontal (n + 1) - - Path Transform keeping LCM: - leibniz_up_wriggle_horizontal |- !n. leibniz_up n wriggle leibniz_horizontal n - leibniz_lcm_property |- !n. list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n) - leibniz_vertical_divisor |- !n k. k <= n ==> leibniz n k divides list_lcm (leibniz_vertical n) - - Lower Bound of Leibniz LCM: - leibniz_horizontal_lcm_lower |- !n. 2 ** n <= list_lcm (leibniz_horizontal n) - leibniz_vertical_lcm_lower |- !n. 2 ** n <= list_lcm (leibniz_vertical n) - lcm_lower_bound |- !n. 2 ** n <= list_lcm [1 .. (n + 1)] - - Leibniz LCM Invariance: - leibniz_col_arm_0 |- !a b. leibniz_col_arm a b 0 = [] - leibniz_seg_arm_0 |- !a b. leibniz_seg_arm a b 0 = [] - leibniz_col_arm_1 |- !a b. leibniz_col_arm a b 1 = [leibniz a b] - leibniz_seg_arm_1 |- !a b. leibniz_seg_arm a b 1 = [leibniz a b] - leibniz_col_arm_len |- !a b n. LENGTH (leibniz_col_arm a b n) = n - leibniz_seg_arm_len |- !a b n. LENGTH (leibniz_seg_arm a b n) = n - leibniz_col_arm_el |- !n k. k < n ==> !a b. EL k (leibniz_col_arm a b n) = leibniz (a - k) b - leibniz_seg_arm_el |- !n k. k < n ==> !a b. EL k (leibniz_seg_arm a b n) = leibniz a (b + k) - leibniz_seg_arm_head |- !a b n. TAKE 1 (leibniz_seg_arm a b (n + 1)) = [leibniz a b] - leibniz_col_arm_cons |- !a b n. leibniz_col_arm (a + 1) b (n + 1) = leibniz (a + 1) b::leibniz_col_arm a b n - - leibniz_seg_arm_zigzag_step |- !n k. k < n ==> !a b. - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) zigzag - TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) - leibniz_seg_arm_wriggle_step |- !n k. k < n + 1 ==> !a b. - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle - leibniz_seg_arm (a + 1) b (n + 1) - leibniz_seg_arm_wriggle_row_arm |- !a b n. [leibniz (a + 1) b] ++ leibniz_seg_arm a b n wriggle - leibniz_seg_arm (a + 1) b (n + 1) - leibniz_col_arm_wriggle_row_arm |- !a b n. b <= a /\ n <= a + 1 - b ==> - leibniz_col_arm a b n wriggle leibniz_seg_arm a b n - leibniz_lcm_invariance |- !a b n. b <= a /\ n <= a + 1 - b ==> - (list_lcm (leibniz_col_arm a b n) = list_lcm (leibniz_seg_arm a b n)) - leibniz_col_arm_n_0 |- !n. leibniz_col_arm n 0 (n + 1) = leibniz_up n - leibniz_seg_arm_n_0 |- !n. leibniz_seg_arm n 0 (n + 1) = leibniz_horizontal n - leibniz_up_wriggle_horizontal_alt |- !n. leibniz_up n wriggle leibniz_horizontal n - leibniz_up_lcm_eq_horizontal_lcm |- !n. list_lcm (leibniz_up n) = list_lcm (leibniz_horizontal n) - - Set GCD as Big Operator: - big_gcd_def |- !s. big_gcd s = ITSET gcd s 0 - big_gcd_empty |- big_gcd {} = 0 - big_gcd_sing |- !x. big_gcd {x} = x - big_gcd_reduction |- !s x. FINITE s /\ x NOTIN s ==> (big_gcd (x INSERT s) = gcd x (big_gcd s)) - big_gcd_is_common_divisor |- !s. FINITE s ==> !x. x IN s ==> big_gcd s divides x - big_gcd_is_greatest_common_divisor - |- !s. FINITE s ==> !m. (!x. x IN s ==> m divides x) ==> m divides big_gcd s - big_gcd_insert |- !s. FINITE s ==> !x. big_gcd (x INSERT s) = gcd x (big_gcd s) - big_gcd_two |- !x y. big_gcd {x; y} = gcd x y - big_gcd_positive |- !s. FINITE s /\ s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s - big_gcd_map_times |- !s. FINITE s /\ s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s - - Set LCM as Big Operator: - big_lcm_def |- !s. big_lcm s = ITSET lcm s 1 - big_lcm_empty |- big_lcm {} = 1 - big_lcm_sing |- !x. big_lcm {x} = x - big_lcm_reduction |- !s x. FINITE s /\ x NOTIN s ==> (big_lcm (x INSERT s) = lcm x (big_lcm s)) - big_lcm_is_common_multiple |- !s. FINITE s ==> !x. x IN s ==> x divides big_lcm s - big_lcm_is_least_common_multiple - |- !s. FINITE s ==> !m. (!x. x IN s ==> x divides m) ==> big_lcm s divides m - big_lcm_insert |- !s. FINITE s ==> !x. big_lcm (x INSERT s) = lcm x (big_lcm s) - big_lcm_two |- !x y. big_lcm {x; y} = lcm x y - big_lcm_positive |- !s. FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s - big_lcm_map_times |- !s. FINITE s /\ s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s - - LCM Lower bound using big LCM: - leibniz_seg_def |- !n k h. leibniz_seg n k h = {leibniz n (k + j) | j IN count h} - leibniz_row_def |- !n h. leibniz_row n h = {leibniz n j | j IN count h} - leibniz_col_def |- !h. leibniz_col h = {leibniz j 0 | j IN count h} - leibniz_col_eq_natural |- !n. leibniz_col n = natural n - big_lcm_seg_transform |- !n k h. lcm (leibniz (n + 1) k) (big_lcm (leibniz_seg n k h)) = - big_lcm (leibniz_seg (n + 1) k (h + 1)) - big_lcm_row_transform |- !n h. lcm (leibniz (n + 1) 0) (big_lcm (leibniz_row n h)) = - big_lcm (leibniz_row (n + 1) (h + 1)) - big_lcm_corner_transform |- !n. big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1)) - big_lcm_count_lower_bound |- !f n. (!x. x IN count (n + 1) ==> 0 < f x) ==> - SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1))) - big_lcm_natural_eqn |- !n. big_lcm (natural (n + 1)) = - (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) - big_lcm_lower_bound |- !n. 2 ** n <= big_lcm (natural (n + 1)) - big_lcm_eq_list_lcm |- !l. big_lcm (set l) = list_lcm l - - List LCM depends only on its set of elements: - list_lcm_absorption |- !x l. MEM x l ==> (list_lcm (x::l) = list_lcm l) - list_lcm_nub |- !l. list_lcm (nub l) = list_lcm l - list_lcm_nub_eq_if_set_eq |- !l1 l2. (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2)) - list_lcm_eq_if_set_eq |- !l1 l2. (set l1 = set l2) ==> (list_lcm l1 = list_lcm l2) - - Set LCM by List LCM: - set_lcm_def |- !s. set_lcm s = list_lcm (SET_TO_LIST s) - set_lcm_empty |- set_lcm {} = 1 - set_lcm_nonempty |- !s. FINITE s /\ s <> {} ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s))) - set_lcm_sing |- !x. set_lcm {x} = x - set_lcm_eq_list_lcm |- !l. set_lcm (set l) = list_lcm l - set_lcm_eq_big_lcm |- !s. FINITE s ==> (set_lcm s = big_lcm s) - set_lcm_insert |- !s. FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm s) - set_lcm_is_common_multiple |- !x s. FINITE s /\ x IN s ==> x divides set_lcm s - set_lcm_is_least_common_multiple |- !s m. FINITE s /\ (!x. x IN s ==> x divides m) ==> set_lcm s divides m - pairwise_coprime_prod_set_eq_set_lcm - |- !s. FINITE s /\ PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s) - pairwise_coprime_prod_set_divides - |- !s m. FINITE s /\ PAIRWISE_COPRIME s /\ - (!x. x IN s ==> x divides m) ==> PROD_SET s divides m - - Nair's Trick (direct): - lcm_run_by_FOLDL |- !n. lcm_run n = FOLDL lcm 1 [1 .. n] - lcm_run_by_FOLDR |- !n. lcm_run n = FOLDR lcm 1 [1 .. n] - lcm_run_0 |- lcm_run 0 = 1 - lcm_run_1 |- lcm_run 1 = 1 - lcm_run_suc |- !n. lcm_run (n + 1) = lcm (n + 1) (lcm_run n) - lcm_run_pos |- !n. 0 < lcm_run n - lcm_run_small |- (lcm_run 2 = 2) /\ (lcm_run 3 = 6) /\ (lcm_run 4 = 12) /\ - (lcm_run 5 = 60) /\ (lcm_run 6 = 60) /\ (lcm_run 7 = 420) /\ - (lcm_run 8 = 840) /\ (lcm_run 9 = 2520) - lcm_run_divisors |- !n. n + 1 divides lcm_run (n + 1) /\ lcm_run n divides lcm_run (n + 1) - lcm_run_monotone |- !n. lcm_run n <= lcm_run (n + 1) - lcm_run_lower |- !n. 2 ** n <= lcm_run (n + 1) - lcm_run_leibniz_divisor |- !n k. k <= n ==> leibniz n k divides lcm_run (n + 1) - lcm_run_lower_odd |- !n. n * 4 ** n <= lcm_run (TWICE n + 1) - lcm_run_lower_even |- !n. n * 4 ** n <= lcm_run (TWICE (n + 1)) - - lcm_run_odd_lower |- !n. ODD n ==> HALF n * HALF (2 ** n) <= lcm_run n - lcm_run_even_lower |- !n. EVEN n ==> HALF (n - 2) * HALF (HALF (2 ** n)) <= lcm_run n - lcm_run_odd_lower_alt |- !n. ODD n /\ 5 <= n ==> 2 ** n <= lcm_run n - lcm_run_even_lower_alt |- !n. EVEN n /\ 8 <= n ==> 2 ** n <= lcm_run n - lcm_run_lower_better |- !n. 7 <= n ==> 2 ** n <= lcm_run n - - Nair's Trick (rework): - lcm_run_odd_factor |- !n. 0 < n ==> n * leibniz (TWICE n) n divides lcm_run (TWICE n + 1) - lcm_run_lower_odd |- !n. n * 4 ** n <= lcm_run (TWICE n + 1) - lcm_run_lower_odd_iff |- !n. ODD n ==> (2 ** n <= lcm_run n <=> 5 <= n) - lcm_run_lower_even_iff |- !n. EVEN n ==> (2 ** n <= lcm_run n <=> (n = 0) \/ 8 <= n) - lcm_run_lower_better_iff |- !n. 2 ** n <= lcm_run n <=> (n = 0) \/ (n = 5) \/ 7 <= n - - Nair's Trick (consecutive): - lcm_upto_def |- (lcm_upto 0 = 1) /\ !n. lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n) - lcm_upto_0 |- lcm_upto 0 = 1 - lcm_upto_SUC |- !n. lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n) - lcm_upto_alt |- (lcm_upto 0 = 1) /\ !n. lcm_upto (n + 1) = lcm (n + 1) (lcm_upto n) - lcm_upto_1 |- lcm_upto 1 = 1 - lcm_upto_small |- (lcm_upto 2 = 2) /\ (lcm_upto 3 = 6) /\ (lcm_upto 4 = 12) /\ - (lcm_upto 5 = 60) /\ (lcm_upto 6 = 60) /\ (lcm_upto 7 = 420) /\ - (lcm_upto 8 = 840) /\ (lcm_upto 9 = 2520) /\ (lcm_upto 10 = 2520) - lcm_upto_eq_list_lcm |- !n. lcm_upto n = list_lcm [1 .. n] - lcm_upto_lower |- !n. 2 ** n <= lcm_upto (n + 1) - lcm_upto_divisors |- !n. n + 1 divides lcm_upto (n + 1) /\ lcm_upto n divides lcm_upto (n + 1) - lcm_upto_monotone |- !n. lcm_upto n <= lcm_upto (n + 1) - lcm_upto_leibniz_divisor |- !n k. k <= n ==> leibniz n k divides lcm_upto (n + 1) - lcm_upto_lower_odd |- !n. n * 4 ** n <= lcm_upto (TWICE n + 1) - lcm_upto_lower_even |- !n. n * 4 ** n <= lcm_upto (TWICE (n + 1)) - lcm_upto_lower_better |- !n. 7 <= n ==> 2 ** n <= lcm_upto n - - Simple LCM lower bounds: - lcm_run_lower_simple |- !n. HALF (n + 1) <= lcm_run n - lcm_run_alt |- !n. lcm_run n = lcm_run (n - 1 + 1) - lcm_run_lower_good |- !n. 2 ** (n - 1) <= lcm_run n - - Upper Bound by Leibniz Triangle: - leibniz_eqn |- !n k. leibniz n k = (n + 1 - k) * binomial (n + 1) k - leibniz_right_alt |- !n k. leibniz n (k + 1) = (n - k) * binomial (n + 1) (k + 1) - leibniz_binomial_identity |- !m n k. k <= m /\ m <= n ==> - (leibniz n k * binomial (n - k) (m - k) = leibniz m k * binomial (n + 1) (m + 1)) - leibniz_divides_leibniz_factor |- !m n k. k <= m /\ m <= n ==> - leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) - leibniz_horizontal_member_divides |- !m n x. n <= TWICE m + 1 /\ m <= n /\ - MEM x (leibniz_horizontal n) ==> - x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) - lcm_run_divides_property |- !m n. n <= TWICE m /\ m <= n ==> - lcm_run n divides lcm_run m * binomial n m - lcm_run_bound_recurrence |- !m n. n <= TWICE m /\ m <= n ==> lcm_run n <= lcm_run m * binomial n m - lcm_run_upper_bound |- !n. lcm_run n <= 4 ** n - - Beta Triangle: - beta_0_n |- !n. beta 0 n = 0 - beta_n_0 |- !n. beta n 0 = 0 - beta_less_0 |- !n k. n < k ==> (beta n k = 0) - beta_eqn |- !n k. beta (n + 1) (k + 1) = leibniz n k - beta_alt |- !n k. 0 < n /\ 0 < k ==> (beta n k = leibniz (n - 1) (k - 1)) - beta_pos |- !n k. 0 < k /\ k <= n ==> 0 < beta n k - beta_eq_0 |- !n k. (beta n k = 0) <=> (k = 0) \/ n < k - beta_sym |- !n k. k <= n ==> (beta n k = beta n (n - k + 1)) - - Beta Horizontal List: - beta_horizontal_0 |- beta_horizontal 0 = [] - beta_horizontal_len |- !n. LENGTH (beta_horizontal n) = n - beta_horizontal_eqn |- !n. beta_horizontal (n + 1) = leibniz_horizontal n - beta_horizontal_alt |- !n. 0 < n ==> (beta_horizontal n = leibniz_horizontal (n - 1)) - beta_horizontal_mem |- !n k. 0 < k /\ k <= n ==> MEM (beta n k) (beta_horizontal n) - beta_horizontal_mem_iff |- !n k. MEM (beta n k) (beta_horizontal n) <=> 0 < k /\ k <= n - beta_horizontal_member |- !n x. MEM x (beta_horizontal n) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k) - beta_horizontal_element |- !n k. k < n ==> (EL k (beta_horizontal n) = beta n (k + 1)) - lcm_run_by_beta_horizontal |- !n. 0 < n ==> (lcm_run n = list_lcm (beta_horizontal n)) - lcm_run_beta_divisor |- !n k. 0 < k /\ k <= n ==> beta n k divides lcm_run n - beta_divides_beta_factor |- !m n k. k <= m /\ m <= n ==> beta n k divides beta m k * binomial n m - lcm_run_divides_property_alt |- !m n. n <= TWICE m /\ m <= n ==> lcm_run n divides binomial n m * lcm_run m - lcm_run_upper_bound |- !n. lcm_run n <= 4 ** n - - LCM Lower Bound using Maximum: - list_lcm_ge_max |- !l. POSITIVE l ==> MAX_LIST l <= list_lcm l - lcm_lower_bound_by_list_lcm |- !n. (n + 1) * binomial n (HALF n) <= list_lcm [1 .. (n + 1)] - big_lcm_ge_max |- !s. FINITE s /\ (!x. x IN s ==> 0 < x) ==> MAX_SET s <= big_lcm s - lcm_lower_bound_by_big_lcm |- !n. (n + 1) * binomial n (HALF n) <= big_lcm (natural (n + 1)) - - Consecutive LCM function: - lcm_lower_bound_by_list_lcm_stirling |- Stirling /\ (!n c. n DIV SQRT (c * (n - 1)) = SQRT (n DIV c)) ==> - !n. ODD n ==> SQRT (n DIV (2 * pi)) * 2 ** n <= list_lcm [1 .. n] - big_lcm_non_decreasing |- !n. big_lcm (natural n) <= big_lcm (natural (n + 1)) - lcm_lower_bound_by_big_lcm_stirling |- Stirling /\ (!n c. n DIV SQRT (c * (n - 1)) = SQRT (n DIV c)) ==> - !n. ODD n ==> SQRT (n DIV (2 * pi)) * 2 ** n <= big_lcm (natural n) - - Extra Theorems: - gcd_prime_product_property |- !p m n. prime p /\ m divides n /\ ~(p * m divides n) ==> (gcd (p * m) n = m) - lcm_prime_product_property |- !p m n. prime p /\ m divides n /\ ~(p * m divides n) ==> (lcm (p * m) n = p * n) - list_lcm_prime_factor |- !p l. prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) - list_lcm_prime_factor_member |- !p l. prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x - -*) - -(* ------------------------------------------------------------------------- *) -(* Leibniz Harmonic Triangle *) -(* ------------------------------------------------------------------------- *) - -(* - -Leibniz Harmonic Triangle (fraction form) - - c <= r -r = 1 1 -r = 2 1/2 1/2 -r = 3 1/3 1/6 1/3 -r = 4 1/4 1/12 1/12 1/4 -r = 5 1/5 1/10 1/20 1/10 1/5 - -In general, L(r,1) = 1/r, L(r,c) = |L(r-1,c-1) - L(r,c-1)| - -Solving, L(r,c) = 1/(r C(r-1,c-1)) = 1/(c C(r,c)) -where C(n,m) is the binomial coefficient of Pascal Triangle. - -c = 1 are the 1/(1 * natural numbers -c = 2 are the 1/(2 * triangular numbers) -c = 3 are the 1/(3 * tetrahedral numbers) - -Sum of denominators of n-th row = n 2**(n-1). - -Note that L(r,c) = Integral(0,1) x ** (c-1) * (1-x) ** (r-c) dx - -Another form: L(n,1) = 1/n, L(n,k) = L(n-1,k-1) - L(n,k-1) -Solving, L(n,k) = 1/ k C(n,k) = 1/ n C(n-1,k-1) - -Still another notation H(n,r) = 1/ (n+1)C(n,r) = (n-r)!r!/(n+1)! for 0 <= r <= n - -Harmonic Denominator Number Triangle (integer form) -g(d,n) = 1/H(d,n) where H(d,h) is the Leibniz Harmonic Triangle -g(d,n) = (n+d)C(d,n) where C(d,h) is the Pascal's Triangle. -g(d,n) = n(n+1)...(n+d)/d! - -(k+1)-th row of Pascal's triangle: x^4 + 4x^3 + 6x^2 + 4x + 1 -Perform differentiation, d/dx -> 4x^3 + 12x^2 + 12x + 4 -which is k-th row of Harmonic Denominator Number Triangle. - -(k+1)-th row of Pascal's triangle: (x+1)^(k+1) -k-th row of Harmonic Denominator Number Triangle: d/dx[(x+1)^(k+1)] - - d/dx[(x+1)^(k+1)] -= d/dx[SUM C(k+1,j) x^j] j = 0...(k+1) -= SUM C(k+1,j) d/dx[x^j] -= SUM C(k+1,j) j x^(j-1) j = 1...(k+1) -= SUM C(k+1,j+1) (j+1) x^j j = 0...k -= SUM D(k,j) x^j with D(k,j) = (j+1) C(k+1,j+1) ??? - -*) - -(* Another presentation of triangles: - -The harmonic triangle of Leibniz - 1/1 1/2 1/3 1/4 1/5 .... harmonic fractions - 1/2 1/6 1/12 1/20 .... successive difference - 1/3 1/12 1/30 ... - 1/4 1/20 ... ... - 1/5 ... ... ... - -Pascal's triangle - 1 1 1 1 1 1 1 .... units - 1 2 3 4 5 6 .... sum left and above - 1 3 6 10 15 21 - 1 4 10 20 35 - 1 5 15 35 - 1 6 21 - - -*) - -(* LCM Lemma - -(n+1) lcm (C(n,0) to C(n,n)) = lcm (1 to (n+1)) - -m-th number in the n-th row of Leibniz triangle is: 1/ (n+1)C(n,m) - -LHS = (n+1) LCM (C(n,0), C(n,1), ..., C(n,n)) = lcd of fractions in n-th row of Leibniz triangle. - -Any such number is an integer linear combination of fractions on triangle’s sides -1/1, 1/2, 1/3, ... 1/n, and vice versa. - -So LHS = lcd (1/1, 1/2, 1/3, ..., 1/n) = RHS = lcm (1,2,3, ..., (n+1)). - -0-th row: 1 -1-st row: 1/2 1/2 -2-nd row: 1/3 1/6 1/3 -3-rd row: 1/4 1/12 1/12 1/4 -4-th row: 1/5 1/20 1/30 1/20 1/5 - -4-th row: 1/5 C(4,m), C(4,m) = 1 4 6 4 1, hence 1/5 1/20 1/30 1/20 1/5 - lcd (1/5 1/20 1/30 1/20 1/5) -= lcm (5, 20, 30, 20, 5) -= lcm (5 C(4,0), 5 C(4,1), 5 C(4,2), 5 C(4,3), 5 C(4,4)) -= 5 lcm (C(4,0), C(4,1), C(4,2), C(4,3), C(4,4)) - -But 1/5 = harmonic - 1/20 = 1/4 - 1/5 = combination of harmonic - 1/30 = 1/12 - 1/20 = (1/3 - 1/4) - (1/4 - 1/5) = combination of harmonic - - lcd (1/5 1/20 1/30 1/20 1/5) -= lcd (combination of harmonic from 1/1 to 1/5) -= lcd (1/1 to 1/5) -= lcm (1 to 5) - -Theorem: lcd (1/x 1/y 1/z) = lcm (x y z) -Theorem: lcm (kx ky kz) = k lcm (x y z) -Theorem: lcd (combination of harmonic from 1/1 to 1/n) = lcd (1/1 to 1/n) -Then apply first theorem, lcd (1/1 to 1/n) = lcm (1 to n) -*) - -(* LCM Bound - 0 < n ==> 2^(n-1) < lcm (1 to n) - - lcm (1 to n) -= n lcm (C(n-1,0) to C(n-1,n-1)) by LCM Lemma ->= n max (0 <= j <= n-1) C(n-1,j) ->= SUM (0 <= j <= n-1) C(n-1,j) -= 2^(n-1) - - lcm (1 to 5) -= 5 lcm (C(4,0), C(4,1), C(4,2), C(4,3), C(4,4)) - - ->= C(4,0) + C(4,1) + C(4,2) + C(4,3) + C(4,4) -= (1 + 1)^4 -= 2^4 - - lcm (1 to 5) = 1x2x3x4x5/2 = 60 -= 5 lcm (1 4 6 4 1) = 5 x 12 -= lcm (1 4 6 4 1) --> unfold 5x to add 5 times - + lcm (1 4 6 4 1) - + lcm (1 4 6 4 1) - + lcm (1 4 6 4 1) - + lcm (1 4 6 4 1) ->= 1 + 4 + 6 + 4 + 1 --> pick one of each 5 C(n,m), i.e. diagonal -= (1 + 1)^4 --> fold back binomial -= 2^4 = 16 - -Actually, can take 5 lcm (1 4 6 4 1) >= 5 x 6 = 30, -but this will need estimation of C(n, n/2), or C(2n,n), involving Stirling's formula. - -Theorem: lcm (x y z) >= x or lcm (x y z) >= y or lcm (x y z) >= z - -*) - -(* - -More generally, there is an identity for 0 <= k <= n: - -(n+1) lcm (C(n,0), C(n,1), ..., C(n,k)) = lcm (n+1, n, n-1, ..., n+1-k) - -This is simply that fact that any integer linear combination of -f(x), delta f(x), delta^2 f(x), ..., delta^k f(x) -is an integer linear combination of f(x), f(x-1), f(x-2), ..., f(x-k) -where delta is the difference operator, f(x) = 1/x, and x = n+1. - -BTW, Leibnitz harmonic triangle too gives this identity. - -That's correct, but the use of absolute values in the Leibniz triangle and -its specialized definition somewhat obscures the generic, linear nature of the identity. - - f(x) = f(n+1) = 1/(n+1) -f(x-1) = f(n) = 1/n -f(x-2) = f(n-1) = 1/(n-1) -f(x-k) = f(n+1-k) = 1/(n+1-k) - - f(x) = f(n+1) = 1/(n+1) = 1/(n+1)C(n,0) - delta f(x) = f(x-1) - f(x) = 1/n - 1/(n+1) = 1/n(n+1) = 1/(n+1)C(n,1) - = C(1,0) f(x-1) - C(1,1) f(x) -delta^2 f(x) = delta f(x-1) - delta f(x) = 1/(n-1)n - 1/n(n+1) - = (n(n+1) - n(n-1))/(n)(n+1)(n)(n-1) - = 2n/n(n+1)n(n-1) = 1/(n+1)(n(n-1)/2) = 1/(n+1)C(n,2) -delta^2 f(x) = delta f(x-1) - delta f(x) - = (f(x-2) - f(x-1)) - (f(x-1) - f(x)) - = f(x-2) - 2 f(x-1) + f(x) - = C(2,0) f(x-2) - C(2,1) f(x-1) + C(2,2) f(x) -delta^3 f(x) = delta^2 f(x-1) - delta^2 f(x) - = (f(x-3) - 2 f(x-2) + f(x-1)) - (f(x-2) - 2 f(x-1) + f(x)) - = f(x-3) - 3 f(x-2) + 3 f(x-1) - f(x) - = C(3,0) f(x-3) - C(3,1) f(x-2) + C(3,2) f(x-2) - C(3,3) f(x) - -delta^k f(x) = C(k,0) f(x-k) - C(k,1) f(x-k+1) + ... + (-1)^k C(k,k) f(x) - = SUM(0 <= j <= k) (-1)^k C(k,j) f(x-k+j) -Also, - f(x) = 1/(n+1)C(n,0) - delta f(x) = 1/(n+1)C(n,1) -delta^2 f(x) = 1/(n+1)C(n,2) -delta^k f(x) = 1/(n+1)C(n,k) - -so lcd (f(x), df(x), d^2f(x), ..., d^kf(x)) - = lcm ((n+1)C(n,0),(n+1)C(n,1),...,(n+1)C(n,k)) by lcd-to-lcm - = lcd (f(x), f(x-1), f(x-2), ..., f(x-k)) by linear combination - = lcm ((n+1), n, (n-1), ..., (n+1-k)) by lcd-to-lcm - -How to formalize: -lcd (f(x), df(x), d^2f(x), ..., d^kf(x)) = lcd (f(x), f(x-1), f(x-2), ..., f(x-k)) - -Simple case: lcd (f(x), df(x)) = lcd (f(x), f(x-1)) - - lcd (f(x), df(x)) -= lcd (f(x), f(x-1) - f(x)) -= lcd (f(x), f(x-1)) - -Can we have - LCD {f(x), df(x)} -= LCD {f(x), f(x-1) - f(x)} = LCD {1/x, 1/(x-1) - 1/x} -= LCD {f(x), f(x-1), f(x)} = lcm {x, x(x-1)} -= LCD {f(x), f(x-1)} = x(x-1) = lcm {x, x-1} = LCD {1/x, 1/(x-1)} - -*) - -(* Step 1: From Pascal's Triangle to Leibniz's Triangle - -Pascal's Triangle: - -row 0 1 -row 1 1 1 -row 2 1 2 1 -row 3 1 3 3 1 -row 4 1 4 6 4 1 -row 5 1 5 10 10 5 1 - -The rule is: boundary = 1, entry = up + left-up - or: C(n,0) = 1, C(n,k) = C(n-1,k) + C(n-1,k-1) - -Multiple each row by successor of its index, i.e. row n -> (n + 1) (row n): -Multiples Triangle (or Modified Triangle): - -1 * row 0 1 -2 * row 1 2 2 -3 * row 2 3 6 3 -4 * row 3 4 12 12 4 -5 * row 4 5 20 30 20 5 -6 * row 5 6 30 60 60 30 6 - -The rule is: boundary = n, entry = left * left-up / (left - left-up) - or: L(n,0) = n, L(n,k) = L(n,k-1) * L(n-1,k-1) / (L(n,k-1) - L(n-1,k-1)) - -Then lcm(1, 2) - = lcm(2) - = lcm(2, 2) - - lcm(1, 2, 3) - = lcm(lcm(1,2), 3) using lcm(1,2,...,n,n+1) = lcm(lcm(1,2,...,n), n+1) - = lcm(2, 3) using lcm(1,2) - = lcm(2*3/1, 3) using lcm(L(n,k-1), L(n-1,k-1)) = lcm(L(n,k-1), L(n-1,k-1)/(L(n,k-1), L(n-1,k-1)), L(n-1,k-1)) - = lcm(6, 3) - = lcm(3, 6, 3) - - lcm(1, 2, 3, 4) - = lcm(lcm(1,2,3), 4) - = lcm(lcm(6,3), 4) - = lcm(6, 3, 4) - = lcm(6, 3*4/1, 4) - = lcm(6, 12, 4) - = lcm(6*12/6, 12, 4) - = lcm(12, 12, 4) - = lcm(4, 12, 12, 4) - - lcm(1, 2, 3, 4, 5) - = lcm(lcm(2,3,4), 5) - = lcm(lcm(12,4), 5) - = lcm(12, 4, 5) - = lcm(12, 4*5/1, 5) - = lcm(12, 20, 5) - = lcm(12*20/8, 20, 5) - = lcm(30, 20, 5) - = lcm(5, 20, 30, 20, 5) - - lcm(1, 2, 3, 4, 5, 6) - = lcm(lcm(1, 2, 3, 4, 5), 6) - = lcm(lcm(30,20,5), 6) - = lcm(30, 20, 5, 6) - = lcm(30, 20, 5*6/1, 6) - = lcm(30, 20, 30, 6) - = lcm(30, 20*30/10, 30, 6) - = lcm(20, 60, 30, 6) - = lcm(20*60/40, 60, 30, 6) - = lcm(30, 60, 30, 6) - = lcm(6, 30, 60, 30, 6) - -Invert each entry of Multiples Triangle into a unit fraction: -Leibniz's Triangle: - -1/(1 * row 0) 1/1 -1/(2 * row 1) 1/2 1/2 -1/(3 * row 2) 1/3 1/6 1/3 -1/(4 * row 3) 1/4 1/12 1/12 1/4 -1/(5 * row 4) 1/5 1/20 1/30 1/20 1/5 -1/(6 * row 5) 1/6 1/30 1/60 1/60 1/30 1/6 - -Theorem: In the Multiples Triangle, the vertical-lcm = horizontal-lcm. -i.e. lcm (1, 2, 3) = lcm (3, 6, 3) = 6 - lcm (1, 2, 3, 4) = lcm (4, 12, 12, 4) = 12 - lcm (1, 2, 3, 4, 5) = lcm (5, 20, 30, 20, 5) = 60 - lcm (1, 2, 3, 4, 5, 6) = lcm (6, 30, 60, 60, 30, 6) = 60 -Proof: With reference to Leibniz's Triangle, note: term = left-up - left - lcm (5, 20, 30, 20, 5) -= lcm (5, 20, 30) by reduce repetition -= lcm (5, d(1/20), d(1/30)) by denominator of fraction -= lcm (5, d(1/4 - 1/5), d(1/30)) by term = left-up - left -= lcm (5, lcm(4, 5), d(1/12 - 1/20)) by denominator of fraction subtraction -= lcm (5, 4, lcm(12, 20)) by lcm (a, lcm (a, b)) = lcm (a, b) -= lcm (5, 4, lcm(d(1/12), d(1/20))) to fraction again -= lcm (5, 4, lcm(d(1/3 - 1/4), d(1/4 - 1/5))) by Leibniz's Triangle -= lcm (5, 4, lcm(lcm(3,4), lcm(4,5))) by fraction subtraction denominator -= lcm (5, 4, lcm(3, 4, 5)) by lcm merge -= lcm (5, 4, 3) merge again -= lcm (5, 4, 3, 2) by lcm include factor (!!!) -= lcm (5, 4, 3, 2, 1) by lcm include 1 - -Note: to make 30, need 12, 20 - to make 12, need 3, 4; to make 20, need 4, 5 - lcm (1, 2, 3, 4, 5) -= lcm (1, 2, lcm(3,4), lcm(4,5), 5) -= lcm (1, 2, d(1/3 - 1/4), d(1/4 - 1/5), 5) -= lcm (1, 2, d(1/12), d(1/20), 5) -= lcm (1, 2, 12, 20, 5) -= lcm (1, 2, lcm(12, 20), 20, 5) -= lcm (1, 2, d(1/12 - 1/20), 20, 5) -= lcm (1, 2, d(1/30), 20, 5) -= lcm (1, 2, 30, 20, 5) -= lcm (1, 30, 20, 5) can drop factor !! -= lcm (30, 20, 5) can drop 1 -= lcm (5, 20, 30, 20, 5) - - lcm (1, 2, 3, 4, 5, 6) -= lcm (lcm (1, 2, 3, 4, 5), lcm(5,6), 6) -= lcm (lcm (5, 20, 30, 20, 5), d(1/5 - 1/6), 6) -= lcm (lcm (5, 20, 30, 20, 5), d(1/30), 6) -= lcm (lcm (5, 20, 30, 20, 5), 30, 6) -= lcm (lcm (5, 20, 30, 20, 5), 30, 6) -= lcm (5, 30, 20, 6) -= lcm (30, 20, 6) can drop factor !! -= lcm (lcm(20, 30), 30, 6) -= lcm (d(1/20 - 1/30), 30, 6) -= lcm (d(1/60), 30, 6) -= lcm (60, 30, 6) -= lcm (6, 30, 60, 30, 6) - - lcm (1, 2) -= lcm (lcm(1,2), 2) -= lcm (2, 2) - - lcm (1, 2, 3) -= lcm (lcm(1, 2), 3) -= lcm (2, 3) --> lcm (2x3/(3-2), 3) = lcm (6, 3) -= lcm (lcm(2, 3), 3) --> lcm (6, 3) = lcm (3, 6, 3) -= lcm (d(1/2 - 1/3), 3) -= lcm (d(1/6), 3) -= lcm (6, 3) = lcm (3, 6, 3) - - lcm (1, 2, 3, 4) -= lcm (lcm(1, 2, 3), 4) -= lcm (lcm(6, 3), 4) -= lcm (6, 3, 4) -= lcm (6, lcm(3, 4), 4) --> lcm (6, 12, 4) = lcm (6x12/(12-6), 12, 4) -= lcm (6, d(1/3 - 1/4), 4) = lcm (12, 12, 4) = lcm (4, 12, 12, 4) -= lcm (6, d(1/12), 4) -= lcm (6, 12, 4) -= lcm (lcm(6, 12), 4) -= lcm (d(1/6 - 1/12), 4) -= lcm (d(1/12), 4) -= lcm (12, 4) = lcm (4, 12, 12, 4) - - lcm (1, 2, 3, 4, 5) -= lcm (lcm(1, 2, 3, 4), 5) -= lcm (lcm(12, 4), 5) -= lcm (12, 4, 5) -= lcm (12, lcm(4,5), 5) --> lcm (12, 20, 5) = lcm (12x20/(20-12), 20, 5) -= lcm (12, d(1/4 - 1/5), 5) = lcm (240/8, 20, 5) but lcm(12,20) != 30 -= lcm (12, d(1/20), 5) = lcm (30, 20, 5) use lcm(a,b,c) = lcm(ab/(b-a), b, c) -= lcm (12, 20, 5) -= lcm (lcm(12,20), 20, 5) -= lcm (d(1/12 - 1/20), 20, 5) -= lcm (d(1/30), 20, 5) -= lcm (30, 20, 5) = lcm (5, 20, 30, 20, 5) - - lcm (1, 2, 3, 4, 5, 6) -= lcm (lcm(1, 2, 3, 4, 5), 6) -= lcm (lcm(30, 20, 5), 6) -= lcm (30, 20, 5, 6) -= lcm (30, 20, lcm(5,6), 6) --> lcm (30, 20, 30, 6) = lcm (30, 20x30/(30-20), 30, 6) -= lcm (30, 20, d(1/5 - 1/6), 6) = lcm (30, 60, 30, 6) -= lcm (30, 20, d(1/30), 6) = lcm (30x60/(60-30), 60, 30, 6) -= lcm (30, 20, 30, 6) = lcm (60, 60, 30, 6) -= lcm (30, lcm(20,30), 30, 6) -= lcm (30, d(1/20 - 1/30), 30, 6) -= lcm (30, d(1/60), 30, 6) -= lcm (30, 60, 30, 6) -= lcm (lcm(30, 60), 60, 30, 6) -= lcm (d(1/30 - 1/60), 60, 30, 6) -= lcm (d(1/60), 60, 30, 6) -= lcm (60, 60, 30, 6) -= lcm (60, 30, 6) = lcm (6, 30, 60, 60, 30, 6) - -*) - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: R^* x y /\ R^* y z ==> R^* x z *) -(* Proof: by RTC_TRANSITIVE, transitive_def *) -val RTC_TRANS = store_thm( - "RTC_TRANS", - ``R^* x y /\ R^* y z ==> R^* x z``, - metis_tac[RTC_TRANSITIVE, transitive_def]); - -(* ------------------------------------------------------------------------- *) -(* Leibniz Triangle (Denominator form) *) -(* ------------------------------------------------------------------------- *) - -(* Define Leibniz Triangle *) -val leibniz_def = Define` - leibniz n k = (n + 1) * binomial n k -`; - -(* export simple definition *) -val _ = export_rewrites["leibniz_def"]; - -(* Theorem: leibniz 0 n = if n = 0 then 1 else 0 *) -(* Proof: - leibniz 0 n - = (0 + 1) * binomial 0 n by leibniz_def - = if n = 0 then 1 else 0 by binomial_n_0 -*) -val leibniz_0_n = store_thm( - "leibniz_0_n", - ``!n. leibniz 0 n = if n = 0 then 1 else 0``, - rw[binomial_0_n]); - -(* Theorem: leibniz n 0 = n + 1 *) -(* Proof: - leibniz n 0 - = (n + 1) * binomial n 0 by leibniz_def - = (n + 1) * 1 by binomial_n_0 - = n + 1 -*) -val leibniz_n_0 = store_thm( - "leibniz_n_0", - ``!n. leibniz n 0 = n + 1``, - rw[binomial_n_0]); - -(* Theorem: leibniz n n = n + 1 *) -(* Proof: - leibniz n n - = (n + 1) * binomial n n by leibniz_def - = (n + 1) * 1 by binomial_n_n - = n + 1 -*) -val leibniz_n_n = store_thm( - "leibniz_n_n", - ``!n. leibniz n n = n + 1``, - rw[binomial_n_n]); - -(* Theorem: n < k ==> leibniz n k = 0 *) -(* Proof: - leibniz n k - = (n + 1) * binomial n k by leibniz_def - = (n + 1) * 0 by binomial_less_0 - = 0 -*) -val leibniz_less_0 = store_thm( - "leibniz_less_0", - ``!n k. n < k ==> (leibniz n k = 0)``, - rw[binomial_less_0]); - -(* Theorem: k <= n ==> (leibniz n k = leibniz n (n-k)) *) -(* Proof: - leibniz n k - = (n + 1) * binomial n k by leibniz_def - = (n + 1) * binomial n (n-k) by binomial_sym - = leibniz n (n-k) by leibniz_def -*) -val leibniz_sym = store_thm( - "leibniz_sym", - ``!n k. k <= n ==> (leibniz n k = leibniz n (n-k))``, - rw[leibniz_def, GSYM binomial_sym]); - -(* Theorem: k < HALF n ==> leibniz n k < leibniz n (k + 1) *) -(* Proof: - Assume k < HALF n, and note that 0 < (n + 1). - leibniz n k < leibniz n (k + 1) - <=> (n + 1) * binomial n k < (n + 1) * binomial n (k + 1) by leibniz_def - <=> binomial n k < binomial n (k + 1) by LT_MULT_LCANCEL - <=> T by binomial_monotone -*) -val leibniz_monotone = store_thm( - "leibniz_monotone", - ``!n k. k < HALF n ==> leibniz n k < leibniz n (k + 1)``, - rw[leibniz_def, binomial_monotone]); - -(* Theorem: k <= n ==> 0 < leibniz n k *) -(* Proof: - Since leibniz n k = (n + 1) * binomial n k by leibniz_def - and 0 < n + 1, 0 < binomial n k by binomial_pos - Hence 0 < leibniz n k by ZERO_LESS_MULT -*) -val leibniz_pos = store_thm( - "leibniz_pos", - ``!n k. k <= n ==> 0 < leibniz n k``, - rw[leibniz_def, binomial_pos, ZERO_LESS_MULT, DECIDE``!n. 0 < n + 1``]); - -(* Theorem: (leibniz n k = 0) <=> n < k *) -(* Proof: - leibniz n k = 0 - <=> (n + 1) * (binomial n k = 0) by leibniz_def - <=> binomial n k = 0 by MULT_EQ_0, n + 1 <> 0 - <=> n < k by binomial_eq_0 -*) -val leibniz_eq_0 = store_thm( - "leibniz_eq_0", - ``!n k. (leibniz n k = 0) <=> n < k``, - rw[leibniz_def, binomial_eq_0]); - -(* Theorem: leibniz n = (\j. (n + 1) * j) o (binomial n) *) -(* Proof: by leibniz_def and function equality. *) -val leibniz_alt = store_thm( - "leibniz_alt", - ``!n. leibniz n = (\j. (n + 1) * j) o (binomial n)``, - rw[leibniz_def, FUN_EQ_THM]); - -(* Theorem: leibniz n k = (\j. (n + 1) * j) (binomial n k) *) -(* Proof: by leibniz_def *) -val leibniz_def_alt = store_thm( - "leibniz_def_alt", - ``!n k. leibniz n k = (\j. (n + 1) * j) (binomial n k)``, - rw_tac std_ss[leibniz_def]); - -(* -Picture of Leibniz Triangle L-corner: - b = L (n-1) k - a = L n k c = L n (k+1) - -a = L n k = (n+1) * (n, k, n-k) = (n+1, k, n-k) = (n+1)! / k! (n-k)! -b = L (n-1) k = n * (n-1, k, n-1-k) = (n , k, n-k-1) = n! / k! (n-k-1)! = a * (n-k)/(n+1) -c = L n (k+1) = (n+1) * (n, k+1, n-(k+1)) = (n+1, k+1, n-k-1) = (n+1)! / (k+1)! (n-k-1)! = a * (n-k)/(k+1) - -a * b = a * a * (n-k)/(n+1) -a - b = a - a * (n-k)/(n+1) = a * (1 - (n-k)/(n+1)) = a * (n+1 - n+k)/(n+1) = a * (k+1)/(n+1) -Hence - a * b /(a - b) -= [a * a * (n-k)/(n+1)] / [a * (k+1)/(n+1)] -= a * (n-k)/(k+1) -= c -or a * b = c * (a - b) -*) - -(* Theorem: 0 < n ==> !k. (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k *) -(* Proof: - (n + 1) * leibniz (n - 1) k - = (n + 1) * ((n-1 + 1) * binomial (n-1) k) by leibniz_def - = (n + 1) * (n * binomial (n-1) k) by SUB_ADD, 1 <= n. - = (n + 1) * ((n - k) * (binomial n k)) by binomial_up_eqn - = ((n + 1) * (n - k)) * binomial n k by MULT_ASSOC - = ((n - k) * (n + 1)) * binomial n k by MULT_COMM - = (n - k) * ((n + 1) * binomial n k) by MULT_ASSOC - = (n - k) * leibniz n k by leibniz_def -*) -val leibniz_up_eqn = store_thm( - "leibniz_up_eqn", - ``!n. 0 < n ==> !k. (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k``, - rw[leibniz_def] >> - `1 <= n` by decide_tac >> - metis_tac[SUB_ADD, binomial_up_eqn, MULT_ASSOC, MULT_COMM]); - -(* Theorem: 0 < n ==> !k. leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1) *) -(* Proof: - Since (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k by leibniz_up_eqn - leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1) by DIV_SOLVE, 0 < n+1. -*) -val leibniz_up = store_thm( - "leibniz_up", - ``!n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1)``, - rw[leibniz_up_eqn, DIV_SOLVE]); - -(* Theorem: 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k *) -(* Proof: - leibniz (n - 1) k - = (n - k) * leibniz n k DIV (n + 1) by leibniz_up, 0 < n - = (n - k) * ((n + 1) * binomial n k) DIV (n + 1) by leibniz_def - = (n + 1) * ((n - k) * binomial n k) DIV (n + 1) by MULT_ASSOC, MULT_COMM - = (n - k) * binomial n k by MULT_DIV, 0 < n + 1 -*) -val leibniz_up_alt = store_thm( - "leibniz_up_alt", - ``!n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k``, - metis_tac[leibniz_up, leibniz_def, MULT_DIV, MULT_ASSOC, MULT_COMM, DECIDE``0 < x + 1``]); - -(* Theorem: 0 < n ==> !k. (k + 1) * leibniz n (k+1) = (n - k) * leibniz n k *) -(* Proof: - (k + 1) * leibniz n (k+1) - = (k + 1) * ((n + 1) * binomial n (k+1)) by leibniz_def - = (k + 1) * (n + 1) * binomial n (k+1) by MULT_ASSOC - = (n + 1) * (k + 1) * binomial n (k+1) by MULT_COMM - = (n + 1) * ((k + 1) * binomial n (k+1)) by MULT_ASSOC - = (n + 1) * ((n - k) * (binomial n k)) by binomial_right_eqn - = ((n + 1) * (n - k)) * binomial n k by MULT_ASSOC - = ((n - k) * (n + 1)) * binomial n k by MULT_COMM - = (n - k) * ((n + 1) * binomial n k) by MULT_ASSOC - = (n - k) * leibniz n k by leibniz_def -*) -val leibniz_right_eqn = store_thm( - "leibniz_right_eqn", - ``!n. 0 < n ==> !k. (k + 1) * leibniz n (k+1) = (n - k) * leibniz n k``, - metis_tac[leibniz_def, MULT_COMM, MULT_ASSOC, binomial_right_eqn]); - -(* Theorem: 0 < n ==> !k. leibniz n (k+1) = (n - k) * (leibniz n k) DIV (k + 1) *) -(* Proof: - Since (k + 1) * leibniz n (k+1) = (n - k) * leibniz n k by leibniz_right_eqn - leibniz n (k+1) = (n - k) * (leibniz n k) DIV (k+1) by DIV_SOLVE, 0 < k+1. -*) -val leibniz_right = store_thm( - "leibniz_right", - ``!n. 0 < n ==> !k. leibniz n (k+1) = (n - k) * (leibniz n k) DIV (k+1)``, - rw[leibniz_right_eqn, DIV_SOLVE]); - -(* Note: Following is the property from Leibniz Harmonic Triangle: - 1 / leibniz n (k+1) = 1 / leibniz (n-1) k - 1 / leibniz n k - = (leibniz n k - leibniz (n-1) k) / leibniz n k * leibniz (n-1) k -*) - -(* The Idea: - b -Actually, lcm a b = lcm b c = lcm c a for a c in Leibniz Triangle. -The only relationship is: c = ab/(a - b), or ab = c(a - b). - -Is this a theorem: ab = c(a - b) ==> lcm a b = lcm b c = lcm c a -Or in fractions, 1/c = 1/b - 1/a ==> lcm a b = lcm b c = lcm c a ? - -lcm a b -= a b / (gcd a b) -= c(a - b) / (gcd a (a - b)) -= ac(a - b) / gcd a (a-b) / a -= lcm (a (a-b)) c / a -= lcm (ca c(a-b)) / a -= lcm (ca ab) / a -= lcm (b c) - -lcm a b = a b / gcd a b = a b / gcd a (a-b) = a b c / gcd ca c(a-b) -= c (a-b) c / gcd ca c(a-b) = lcm ca c(a-b) / a = lcm ca ab / a = lcm b c - - lcm b c -= b c / gcd b c -= a b c / gcd a*b a*c -= a b c / gcd c*(a-b) c*a -= a b / gcd (a-b) a -= a b / gcd b a -= lcm (a b) -= lcm a b - - lcm a c -= a c / gcd a c -= a b c / gcd b*a b*c -= a b c / gcd c*(a-b) b*c -= a b / gcd (a-b) b -= a b / gcd a b -= lcm a b - -Yes! - -This is now in LCM_EXCHANGE: -val it = |- !a b c. (a * b = c * (a - b)) ==> (lcm a b = lcm a c): thm -*) - -(* Theorem: 0 < n ==> - !k. leibniz n k * leibniz (n-1) k = leibniz n (k+1) * (leibniz n k - leibniz (n-1) k) *) -(* Proof: - If n <= k, - then n-1 < k, and n < k+1. - so leibniz (n-1) k = 0 by leibniz_less_0, n-1 < k. - and leibniz n (k+1) = 0 by leibniz_less_0, n < k+1. - Hence true by MULT_EQ_0 - Otherwise, k < n, or k <= n. - then (n+1) - (n-k) = k+1. - - (k + 1) * (c * (a - b)) - = (k + 1) * c * (a - b) by MULT_ASSOC - = ((n+1) - (n-k)) * c * (a - b) by above - = (n - k) * a * (a - b) by leibniz_right_eqn - = (n - k) * a * a - (n - k) * a * b by LEFT_SUB_DISTRIB - = (n + 1) * b * a - (n - k) * a * b by leibniz_up_eqn - = (n + 1) * (a * b) - (n - k) * (a * b) by MULT_ASSOC, MULT_COMM - = ((n+1) - (n-k)) * (a * b) by RIGHT_SUB_DISTRIB - = (k + 1) * (a * b) by above - - Since (k+1) <> 0, the result follows by MULT_LEFT_CANCEL -*) -val leibniz_property = store_thm( - "leibniz_property", - ``!n. 0 < n ==> - !k. leibniz n k * leibniz (n-1) k = leibniz n (k+1) * (leibniz n k - leibniz (n-1) k)``, - rpt strip_tac >> - Cases_on `n <= k` >- - rw[leibniz_less_0] >> - `(n+1) - (n-k) = k+1` by decide_tac >> - `(k+1) <> 0` by decide_tac >> - qabbrev_tac `a = leibniz n k` >> - qabbrev_tac `b = leibniz (n - 1) k` >> - qabbrev_tac `c = leibniz n (k + 1)` >> - `(k + 1) * (c * (a - b)) = ((n+1) - (n-k)) * c * (a - b)` by rw_tac std_ss[MULT_ASSOC] >> - `_ = (n - k) * a * (a - b)` by rw_tac std_ss[leibniz_right_eqn, Abbr`c`, Abbr`a`] >> - `_ = (n - k) * a * a - (n - k) * a * b` by rw_tac std_ss[LEFT_SUB_DISTRIB] >> - `_ = (n + 1) * b * a - (n - k) * a * b` by rw_tac std_ss[leibniz_up_eqn, Abbr`b`, Abbr`a`] >> - `_ = (n + 1) * (a * b) - (n - k) * (a * b)` by metis_tac[MULT_ASSOC, MULT_COMM] >> - `_ = ((n+1) - (n-k)) * (a * b)` by rw_tac std_ss[RIGHT_SUB_DISTRIB] >> - `_ = (k + 1) * (a * b)` by rw_tac std_ss[] >> - metis_tac[MULT_LEFT_CANCEL]); - -(* Theorem: k <= n ==> (leibniz n k = (n + 1) * FACT n DIV (FACT k * FACT (n - k))) *) -(* Proof: - Note (FACT k * FACT (n - k)) divides (FACT n) by binomial_is_integer - and 0 < FACT k * FACT (n - k) by FACT_LESS, ZERO_LESS_MULT - leibniz n k - = (n + 1) * binomial n k by leibniz_def - = (n + 1) * (FACT n DIV (FACT k * FACT (n - k))) by binomial_formula3 - = (n + 1) * FACT n DIV (FACT k * FACT (n - k)) by MULTIPLY_DIV -*) -val leibniz_formula = store_thm( - "leibniz_formula", - ``!n k. k <= n ==> (leibniz n k = (n + 1) * FACT n DIV (FACT k * FACT (n - k)))``, - metis_tac[leibniz_def, binomial_formula3, binomial_is_integer, FACT_LESS, MULTIPLY_DIV, ZERO_LESS_MULT]); - -(* Theorem: 0 < n ==> - !k. k < n ==> leibniz n (k+1) = leibniz n k * leibniz (n-1) k DIV (leibniz n k - leibniz (n-1) k) *) -(* Proof: - By leibniz_property, - leibniz n (k+1) * (leibniz n k - leibniz (n-1) k) = leibniz n k * leibniz (n-1) k - Since 0 < leibniz n k and 0 < leibniz (n-1) k by leibniz_pos - so 0 < (leibniz n k - leibniz (n-1) k) by MULT_EQ_0 - Hence by MULT_COMM, DIV_SOLVE, 0 < (leibniz n k - leibniz (n-1) k), - leibniz n (k+1) = leibniz n k * leibniz (n-1) k DIV (leibniz n k - leibniz (n-1) k) -*) -val leibniz_recurrence = store_thm( - "leibniz_recurrence", - ``!n. 0 < n ==> - !k. k < n ==> (leibniz n (k+1) = leibniz n k * leibniz (n-1) k DIV (leibniz n k - leibniz (n-1) k))``, - rpt strip_tac >> - `k <= n /\ k <= (n-1)` by decide_tac >> - `leibniz n (k+1) * (leibniz n k - leibniz (n-1) k) = leibniz n k * leibniz (n-1) k` by rw[leibniz_property] >> - `0 < leibniz n k /\ 0 < leibniz (n-1) k` by rw[leibniz_pos] >> - `0 < (leibniz n k - leibniz (n-1) k)` by metis_tac[MULT_EQ_0, NOT_ZERO_LT_ZERO] >> - rw_tac std_ss[DIV_SOLVE, MULT_COMM]); - -(* Theorem: 0 < k /\ k <= n ==> - (leibniz n k = leibniz n (k-1) * leibniz (n-1) (k-1) DIV (leibniz n (k-1) - leibniz (n-1) (k-1))) *) -(* Proof: - Since 0 < k, k = SUC h for some h - or k = h + 1 by ADD1 - and h = k - 1 by arithmetic - Since 0 < k and k <= n, - 0 < n and h < n. - Hence true by leibniz_recurrence. -*) -val leibniz_n_k = store_thm( - "leibniz_n_k", - ``!n k. 0 < k /\ k <= n ==> - (leibniz n k = leibniz n (k-1) * leibniz (n-1) (k-1) DIV (leibniz n (k-1) - leibniz (n-1) (k-1)))``, - rpt strip_tac >> - `?h. k = h + 1` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO, ADD1] >> - `(h = k - 1) /\ h < n /\ 0 < n` by decide_tac >> - metis_tac[leibniz_recurrence]); - -(* Theorem: 0 < n ==> - !k. lcm (leibniz n k) (leibniz (n-1) k) = lcm (leibniz n k) (leibniz n (k+1)) *) -(* Proof: - By leibniz_property, - leibniz n k * leibniz (n - 1) k = leibniz n (k + 1) * (leibniz n k - leibniz (n - 1) k) - Hence true by LCM_EXCHANGE. -*) -val leibniz_lcm_exchange = store_thm( - "leibniz_lcm_exchange", - ``!n. 0 < n ==> !k. lcm (leibniz n k) (leibniz (n-1) k) = lcm (leibniz n k) (leibniz n (k+1))``, - rw[leibniz_property, LCM_EXCHANGE]); - -(* Theorem: 4 ** n <= leibniz (2 * n) n *) -(* Proof: - Let m = 2 * n. - Then n = HALF m by HALF_TWICE - Let l1 = GENLIST (K (binomial m n)) (m + 1) - and l2 = GENLIST (binomial m) (m + 1) - Note LENGTH l1 = LENGTH l2 = m + 1 by LENGTH_GENLIST - - Claim: !k. k < m + 1 ==> EL k l2 <= EL k l1 - Proof: Note EL k l1 = binomial m n by EL_GENLIST - and EL k l2 = binomial m k by EL_GENLIST - Apply binomial m k <= binomial m n by binomial_max - The result follows - - leibniz m n - = (m + 1) * binomial m n by leibniz_def - = SUM (GENLIST (K (binomial m n)) (m + 1)) by SUM_GENLIST_K - >= SUM (GENLIST (\k. binomial m k) (m + 1)) by SUM_LE, above - = SUM (GENLIST (binomial m) (SUC m)) by ADD1 - = 2 ** m by binomial_sum - = 2 ** (2 * n) by notation - = (2 ** 2) ** n by EXP_EXP_MULT - = 4 ** n by arithmetic -*) -val leibniz_middle_lower = store_thm( - "leibniz_middle_lower", - ``!n. 4 ** n <= leibniz (2 * n) n``, - rpt strip_tac >> - qabbrev_tac `m = 2 * n` >> - `n = HALF m` by rw[HALF_TWICE, Abbr`m`] >> - qabbrev_tac `l1 = GENLIST (K (binomial m n)) (m + 1)` >> - qabbrev_tac `l2 = GENLIST (binomial m) (m + 1)` >> - `!k. k < m + 1 ==> EL k l2 <= EL k l1` by rw[binomial_max, EL_GENLIST, Abbr`l1`, Abbr`l2`] >> - `leibniz m n = (m + 1) * binomial m n` by rw[leibniz_def] >> - `_ = SUM l1` by rw[SUM_GENLIST_K, Abbr`l1`] >> - `SUM l2 = SUM (GENLIST (binomial m) (SUC m))` by rw[ADD1, Abbr`l2`] >> - `_ = 2 ** m` by rw[binomial_sum] >> - `_ = 4 ** n` by rw[EXP_EXP_MULT, Abbr`m`] >> - metis_tac[SUM_LE, LENGTH_GENLIST]); - -(* ------------------------------------------------------------------------- *) -(* Property of Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* -binomial_recurrence |- !n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k) -This means: - B n k + B n k* - v - B n* k* -However, for the Leibniz Triangle, the recurrence is: - L n k - L n* k -> L n* k* = (L n* k)(L n k) / (L n* k - L n k) -That is, it takes a different style, and has the property: - 1 / L n* k* = 1 / L n k - 1 / L n* k -Why? -First, some verification. -Pascal: [1] 3 3 - [4] 6 = 3 + 3 = 6 -Leibniz: 12 12 - [20] 30 = 20 * 12 / (20 - 12) = 20 * 12 / 8 = 30 -Now, the 20 comes from 4 = 3 + 1. -Originally, 30 = 5 * 6 by definition based on multiple - = 5 * (3 + 3) by Pascal - = 4 * (3 + 3) + (3 + 3) - = 12 + 12 + 6 -In terms of factorials, 30 = 5 * 6 = 5 * B(4,2) = 5 * 4!/2!2! - 20 = 5 * 4 = 5 * B(4,1) = 5 * 4!/1!3! - 12 = 4 * 3 = 4 * B(3,1) = 4 * 3!/1!2! -So 1/30 = (2!2!)/(5 4!) 1 / n** B n* k* = k*! (n* - k* )! / n** n*! = (n - k)! k*! / n**! - 1/20 = (1!3!)/(5 4!) 1 / n** B n* k - 1/12 = (1!2!)/(4 3!) 1 / n* B n k - 1/12 - 1/20 - = (1!2!)/(4 3!) - (1!3!)/(5 4!) - = (1!2!)/4! - (1!3!)/5! - = 5(1!2!)/5! - (1!3!)/5! - = (5(1!2!) - (1!3!))/5! - = (5 1! - 3 1!) 2!/5! - = (5 - 3)1! 2!/5! - = 2! 2! / 5! - - 1 / n B n k - 1 / n** B n* k - = k! (n-k)! / n* n! - k! (n* - k)! / n** n*! - = k! (n-k)! / n*! - k!(n* - k)! / n** n*! - = (n** (n-k)! - (n* - k)!) k! / n** n*! - = (n** - (n* - k)) (n - k)! k! / n** n*! - = (k+1) (n - k)! k! / n** n*! - = (n* - k* )! k*! / n** n*! - = 1 / n** B n* k* - -Direct without using unit fractions, - -L n k = n* B n k = n* n! / k! (n-k)! = n*! / k! (n-k)! -L n* k = n** B n* k = n** n*! / k! (n* - k)! = n**! / k! (n* - k)! -L n* k* = n** B n* k* = n** n*! / k*! (n* - k* )! = n**! / k*! (n-k)! - -(L n* k) * (L n k) = n**! n*! / k! (n* - k)! k! (n-k)! -(L n* k) - (L n k) = n**! / k! (n* - k)! - n*! / k! (n-k)! - = n**! / k! (n-k)!( 1/(n* - k) - 1/ n** ) - = n**! / k! (n-k)! (n** - n* + k)/(n* - k)(n** ) - = n**! / k! (n-k)! k* / (n* - k) n** - = n*! k* / k! (n* - k)! -(L n* k) * (L n k) / (L n* k) - (L n k) -= n**! /k! (n-k)! k* -= n**! /k*! (n-k)! -= L n* k* -So: L n k - L n* k --> L n* k* - -Can the LCM be shown directly? -lcm (L n* k, L n k) = lcm (L n* k, L n* k* ) -To prove this, need to show: -both have the same common multiples, and least is the same -- probably yes due to common L n* k. - -In general, what is the condition for lcm a b = lcm a c ? -Well, lcm a b = a b / gcd a b, lcm a c = a c / gcd a c -So it must be a b gcd a c = a c gcd a b, or b * gcd a c = c * gcd a b. - -It this true for Leibniz triangle? -Let a = 5, b = 4, c = 20. b * gcd a c = 4 * gcd 5 20 = 4 * 5 = 20 - c * gcd a b = 20 * gcd 5 4 = 20 -Verify lcm a b = lcm 5 4 = 20 = 5 * 4 / gcd 5 4 - lcm a c = lcm 5 20 = 20 = 5 * 20 / gcd 5 20 - 5 * 4 / gcd 5 4 = 5 * 20 / gcd 5 20 -or 4 * gcd 5 20 = 20 * gcd 5 4 - -(L n k) * gcd (L n* k, L n* k* ) = (L n* k* ) * gcd (L n* k, L n k) - -or n* B n k * gcd (n** B n* k, n** B n* k* ) = (n** B n* k* ) * gcd (n** B n* k, n* B n k) -By GCD_COMMON_FACTOR, !m n k. gcd (k * m) (k * n) = k * gcd m n - n** n* B n k gcd (B n* k, B n* k* ) = (n** B n* k* ) * gcd (n** B n* k, n* B n k) -*) - -(* Special Property of Leibniz Triangle -For: L n k - L n+ k --> L n+ k+ - -L n k = n+! / k! (n-k)! -L n+ k = n++! / k! (n+ - k)! = n++ n+! / k! (n+ - k) k! = (n++ / n+ - k) L n k -L n+ k+ = n++! / k+! (n-k)! = (L n+ k) * (L n k) / (L n+ k - L n k) = (n++ / k+) L n k -Let g = gcd (L n+ k) (L n k), then L n+ k+ = lcm (L n+ k) (L n k) / (co n+ k - co n k) -where co n+ k = L n+ k / g, co n k = L n k / g. - - L n+ k = (n++ / n+ - k) L n k, -and L n+ k+ = (n++ / k+) L n k -e.g. L 3 1 = 12 - L 4 1 = 20, or (3++ / 3+ - 1) L 3 1 = (5/3) 12 = 20. - L 4 2 = 30, or (3++ / 1+) L 3 1 = (5/2) 12 = 30. -so lcm (L 4 1) (L 3 1) = lcm (5/3)*12 12 = 12 * 5 = 60 since 3 must divide 12. - lcm (L 4 1) (L 4 2) = lcm (5/3)*12 (5/2)*12 = 12 * 5 = 60 since 3, 2 must divide 12. - -By LCM_COMMON_FACTOR |- !m n k. lcm (k * m) (k * n) = k * lcm m n -lcm a (a * b DIV c) = a * b - -So the picture is: (L n k) - (L n k) * (n+2)/(n-k+1) (L n k) * (n+2)/(k+1) - -A better picture: -Pascal: (B n-1 k) = (n-1, k, n-k-1) - (B n k) = (n, k, n-k) (B n k+1) = (n, k+1, n-k-1) -Leibniz: (L n-1 k) = (n, k, n-k-1) = (L n k) / (n+1) * (n-k-1) - (L n k) = (n+1, k, n-k) (L n k+1) = (n+1, k+1, n-k-1) = (L n k) / (n-k-1) * (k+1) -And we want: - LCM (L, (n-k-1) * L DIV (n+1)) = LCM (L, (k+1) * L DIV (n-k-1)). - -Theorem: lcm a ((a * b) DIV c) = (a * b) DIV (gcd b c) -Assume this theorem, -LHS = L * (n-k-1) DIV gcd (n-k-1, n+1) -RHS = L * (k+1) DIV gcd (k+1, n-k-1) -Still no hope to show LHS = RHS ! - -LCM of fractions: -lcm (a/c, b/c) = lcm(a, b)/c -lcm (a/c, b/d) = ... = lcm(a, b)/gcd(c, d) -Hence lcm (a, a*b/c) = lcm(a*b/b, a*b/c) = a * b / gcd (b, c) -*) - -(* Special Property of Leibniz Triangle -- another go -Leibniz: L(5,1) = 30 = b - L(6,1) = 42 = a L(6,2) = 105 = c, c = ab/(a - b), or ab = c(a - b) -Why is LCM 42 30 = LCM 42 105 = 210 = 2x3x5x7? -First, b = L(5,1) = 30 = (6,1,4) = 6!/1!4! = 7!/1!5! * (5/7) = a * (5/7) = 2x3x5 - a = L(6,1) = 42 = (7,1,5) = 7!/1!5! = 2x3x7 = b * (7/5) = c * (2/5) - c = L(6,2) = 105 = (7,2,4) = 7!/2!4! = 7!/1!5! * (5/2) = a * (5/2) = 3x5x7 -Any common multiple of a, b must have 5, 7 as factor, also with factor 2 (by common k = 1) -Any common multiple of a, c must have 5, 2 as factor, also with factor 7 (by common n = 6) -Also n = 5 implies a factor 6, k = 2 imples a factor 2. -LCM a b = a b / GCD a b - = c (a - b) / GCD a b - = (m c') (m a' - (m-1)b') / GCD (m a') (m-1 b') -LCM a c = a c / GCD a c - = (m a') (m c') / GCD (m a') (m c') where c' = a' + b' from Pascal triangle - = m a' (a' + b') / GCD a' (a' + b') - = m a' (a' + b') / GCD a' b' - = a' c / GCD a' b' -Can we prove: c(a - b) / GCD a b = c a' / GCD a' b' -or (a - b) GCD a' b' = a' GCD a b ? -or a GCD a' b' = a' GCD a b + b GCD a' b' ? -or ab GCD a' b' = c a' GCD a b? -or m (b GCD a' b') = c GCD a b? -or b GCD a' b' = c' GCD a b? -b = (a DIV 7) * 5 -c = (a DIV 2) * 5 -lcm (a, b) = lcm (a, (a DIV 7) * 5) = lcm (a, 5) -lcm (a, c) = lcm (a, (a DIV 2) * 5) = lcm (a, 5) -Is this a theorem: lcm (a, (a DIV p) * b) = lcm (a, b) if p | a ? -Let c = lcm (a, b). Then a | c, b | c. -Since a = (a DIV p) * p, (a DIV p) * p | c. -Hence ((a DIV p) * b) * p | b * c. -How to conclude ((a DIV p) * b) | c? - -A counter-example: -lcm (42, 9) = 126 = 2x3x3x7. -lcm (42, (42 DIV 3) * 9) = 126 = 2x3x3x7. -lcm (42, (42 DIV 6) * 9) = 126 = 2x3x3x7. -lcm (42, (42 DIV 2) * 9) = 378 = 2x3x3x3x7. -lcm (42, (42 DIV 7) * 9) = 378 = 2x3x3x3x7. - -LCM a c -= LCM a (ab/(a-b)) let g = GCD(a,b), a = gA, b=gB, coprime A,B. -= LCM gA gAB/(A-B) -= g LCM A AB/(A-B) -= (ab/LCM a b) LCM A AB/(A-B) -*) - -(* ------------------------------------------------------------------------- *) -(* LCM of a list of numbers *) -(* ------------------------------------------------------------------------- *) - -(* Define LCM of a list of numbers *) -val list_lcm_def = Define` - (list_lcm [] = 1) /\ - (list_lcm (h::t) = lcm h (list_lcm t)) -`; - -(* export simple definition *) -val _ = export_rewrites["list_lcm_def"]; - -(* Theorem: list_lcm [] = 1 *) -(* Proof: by list_lcm_def. *) -val list_lcm_nil = store_thm( - "list_lcm_nil", - ``list_lcm [] = 1``, - rw[]); - -(* Theorem: list_lcm (h::t) = lcm h (list_lcm t) *) -(* Proof: by list_lcm_def. *) -val list_lcm_cons = store_thm( - "list_lcm_cons", - ``!h t. list_lcm (h::t) = lcm h (list_lcm t)``, - rw[]); - -(* Theorem: list_lcm [x] = x *) -(* Proof: - list_lcm [x] - = lcm x (list_lcm []) by list_lcm_cons - = lcm x 1 by list_lcm_nil - = x by LCM_1 -*) -val list_lcm_sing = store_thm( - "list_lcm_sing", - ``!x. list_lcm [x] = x``, - rw[]); - -(* Theorem: list_lcm (SNOC x l) = list_lcm (x::l) *) -(* Proof: - By induction on l. - Base case: list_lcm (SNOC x []) = lcm x (list_lcm []) - list_lcm (SNOC x []) - = list_lcm [x] by SNOC - = lcm x (list_lcm []) by list_lcm_def - Step case: list_lcm (SNOC x l) = lcm x (list_lcm l) ==> - !h. list_lcm (SNOC x (h::l)) = lcm x (list_lcm (h::l)) - list_lcm (SNOC x (h::l)) - = list_lcm (h::SNOC x l) by SNOC - = lcm h (list_lcm (SNOC x l)) by list_lcm_def - = lcm h (lcm x (list_lcm l)) by induction hypothesis - = lcm x (lcm h (list_lcm l)) by LCM_ASSOC_COMM - = lcm x (list_lcm h::l) by list_lcm_def -*) -val list_lcm_snoc = store_thm( - "list_lcm_snoc", - ``!x l. list_lcm (SNOC x l) = lcm x (list_lcm l)``, - strip_tac >> - Induct >- - rw[] >> - rw[LCM_ASSOC_COMM]); - -(* Theorem: list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l *) -(* Proof: - By induction on l. - Base case: !n. list_lcm (MAP (\k. n * k) []) = if [] = [] then 1 else n * list_lcm [] - list_lcm (MAP (\k. n * k) []) - = list_lcm [] by MAP - = 1 by list_lcm_nil - Step case: !n. list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l ==> - !h n. list_lcm (MAP (\k. n * k) (h::l)) = if h::l = [] then 1 else n * list_lcm (h::l) - Note h::l <> [] by NOT_NIL_CONS - If l = [], h::l = [h] - list_lcm (MAP (\k. n * k) [h]) - = list_lcm [n * h] by MAP - = n * h by list_lcm_sing - = n * list_lcm [h] by list_lcm_sing - If l <> [], - list_lcm (MAP (\k. n * k) (h::l)) - = list_lcm ((n * h) :: MAP (\k. n * k) l) by MAP - = lcm (n * h) (list_lcm (MAP (\k. n * k) l)) by list_lcm_cons - = lcm (n * h) (n * list_lcm l) by induction hypothesis - = n * (lcm h (list_lcm l)) by LCM_COMMON_FACTOR - = n * list_lcm (h::l) by list_lcm_cons -*) -val list_lcm_map_times = store_thm( - "list_lcm_map_times", - ``!n l. list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l``, - Induct_on `l` >- - rw[] >> - rpt strip_tac >> - Cases_on `l = []` >- - rw[] >> - rw_tac std_ss[LCM_COMMON_FACTOR, MAP, list_lcm_cons]); - -(* Theorem: EVERY_POSITIVE l ==> 0 < list_lcm l *) -(* Proof: - By induction on l. - Base case: EVERY_POSITIVE [] ==> 0 < list_lcm [] - Note EVERY_POSITIVE [] = T by EVERY_DEF - Since list_lcm [] = 1 by list_lcm_nil - Hence true since 0 < 1 by SUC_POS, ONE. - Step case: EVERY_POSITIVE l ==> 0 < list_lcm l ==> - !h. EVERY_POSITIVE (h::l) ==> 0 < list_lcm (h::l) - Note EVERY_POSITIVE (h::l) - ==> 0 < h and EVERY_POSITIVE l by EVERY_DEF - Since list_lcm (h::l) = lcm h (list_lcm l) by list_lcm_cons - and 0 < list_lcm l by induction hypothesis - so h <= lcm h (list_lcm l) by LCM_LE, 0 < h. - Hence 0 < list_lcm (h::l) by LESS_LESS_EQ_TRANS -*) -val list_lcm_pos = store_thm( - "list_lcm_pos", - ``!l. EVERY_POSITIVE l ==> 0 < list_lcm l``, - Induct >- - rw[] >> - metis_tac[EVERY_DEF, list_lcm_cons, LCM_LE, LESS_LESS_EQ_TRANS]); - -(* Theorem: POSITIVE l ==> 0 < list_lcm l *) -(* Proof: by list_lcm_pos, EVERY_MEM *) -val list_lcm_pos_alt = store_thm( - "list_lcm_pos_alt", - ``!l. POSITIVE l ==> 0 < list_lcm l``, - rw[list_lcm_pos, EVERY_MEM]); - -(* Theorem: EVERY_POSITIVE l ==> SUM l <= (LENGTH l) * list_lcm l *) -(* Proof: - By induction on l. - Base case: EVERY_POSITIVE [] ==> SUM [] <= LENGTH [] * list_lcm [] - Note EVERY_POSITIVE [] = T by EVERY_DEF - Since SUM [] = 0 by SUM - and LENGTH [] = 0 by LENGTH_NIL - Hence true by MULT, as 0 <= 0 by LESS_EQ_REFL - Step case: EVERY_POSITIVE l ==> SUM l <= LENGTH l * list_lcm l ==> - !h. EVERY_POSITIVE (h::l) ==> SUM (h::l) <= LENGTH (h::l) * list_lcm (h::l) - Note EVERY_POSITIVE (h::l) - ==> 0 < h and EVERY_POSITIVE l by EVERY_DEF - ==> 0 < h and 0 < list_lcm l by list_lcm_pos - If l = [], LENGTH l = 0. - SUM (h::[]) = SUM [h] = h by SUM - LENGTH (h::[]) * list_lcm (h::[]) - = 1 * list_lcm [h] by ONE - = 1 * h by list_lcm_sing - = h by MULT_LEFT_1 - If l <> [], LENGTH l <> 0 by LENGTH_NIL ... [1] - SUM (h::l) - = h + SUM l by SUM - <= h + LENGTH l * list_lcm l by induction hypothesis - <= lcm h (list_lcm l) + LENGTH l * list_lcm l by LCM_LE, 0 < h - <= lcm h (list_lcm l) + LENGTH l * (lcm h (list_lcm l)) by LCM_LE, 0 < list_lcm l, [1] - = (1 + LENGTH l) * (lcm h (list_lcm l)) by RIGHT_ADD_DISTRIB - = SUC (LENGTH l) * (lcm h (list_lcm l)) by SUC_ONE_ADD - = LENGTH (h::l) * (lcm h (list_lcm l)) by LENGTH - = LENGTH (h::l) * list_lcm (h::l) by list_lcm_cons -*) -val list_lcm_lower_bound = store_thm( - "list_lcm_lower_bound", - ``!l. EVERY_POSITIVE l ==> SUM l <= (LENGTH l) * list_lcm l``, - Induct >> - rw[] >> - Cases_on `l = []` >- - rw[] >> - `lcm h (list_lcm l) + LENGTH l * (lcm h (list_lcm l)) = SUC (LENGTH l) * (lcm h (list_lcm l))` by rw[RIGHT_ADD_DISTRIB, SUC_ONE_ADD] >> - `LENGTH l <> 0` by metis_tac[LENGTH_NIL] >> - `0 < list_lcm l` by rw[list_lcm_pos] >> - `h <= lcm h (list_lcm l) /\ list_lcm l <= lcm h (list_lcm l)` by rw[LCM_LE] >> - `LENGTH l * list_lcm l <= LENGTH l * (lcm h (list_lcm l))` by rw[LE_MULT_LCANCEL] >> - `h + SUM l <= h + LENGTH l * list_lcm l` by rw[] >> - decide_tac); - -(* Another version to eliminate EVERY by MEM. *) -val list_lcm_lower_bound_alt = save_thm("list_lcm_lower_bound_alt", - list_lcm_lower_bound |> SIMP_RULE (srw_ss()) [EVERY_MEM]); -(* > list_lcm_lower_bound_alt; -val it = |- !l. POSITIVE l ==> SUM l <= LENGTH l * list_lcm l: thm -*) - -(* Theorem: list_lcm l is a common multiple of its members. - MEM x l ==> x divides (list_lcm l) *) -(* Proof: - By induction on l. - Base case: !x. MEM x [] ==> x divides (list_lcm []) - True since MEM x [] = F by MEM - Step case: !x. MEM x l ==> x divides (list_lcm l) ==> - !h x. MEM x (h::l) ==> x divides (list_lcm (h::l)) - Note MEM x (h::l) <=> x = h, or MEM x l by MEM - and list_lcm (h::l) = lcm h (list_lcm l) by list_lcm_cons - If x = h, - divides h (lcm h (list_lcm l)) is true by LCM_IS_LEAST_COMMON_MULTIPLE - If MEM x l, - x divides (list_lcm l) by induction hypothesis - (list_lcm l) divides (lcm h (list_lcm l)) by LCM_IS_LEAST_COMMON_MULTIPLE - Hence x divides (lcm h (list_lcm l)) by DIVIDES_TRANS -*) -val list_lcm_is_common_multiple = store_thm( - "list_lcm_is_common_multiple", - ``!x l. MEM x l ==> x divides (list_lcm l)``, - Induct_on `l` >> - rw[] >> - metis_tac[LCM_IS_LEAST_COMMON_MULTIPLE, DIVIDES_TRANS]); - -(* Theorem: If m is a common multiple of members of l, (list_lcm l) divides m. - (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m *) -(* Proof: - By induction on l. - Base case: !m. (!x. MEM x [] ==> x divides m) ==> divides (list_lcm []) m - Since list_lcm [] = 1 by list_lcm_nil - and divides 1 m is true by ONE_DIVIDES_ALL - Step case: !m. (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m ==> - !h m. (!x. MEM x (h::l) ==> x divides m) ==> divides (list_lcm (h::l)) m - Note MEM x (h::l) <=> x = h, or MEM x l by MEM - and list_lcm (h::l) = lcm h (list_lcm l) by list_lcm_cons - Put x = h, divides h m by MEM h (h::l) = T - Put MEM x l, x divides m by MEM x (h::l) = T - giving (list_lcm l) divides m by induction hypothesis - Hence divides (lcm h (list_lcm l)) m by LCM_IS_LEAST_COMMON_MULTIPLE -*) -val list_lcm_is_least_common_multiple = store_thm( - "list_lcm_is_least_common_multiple", - ``!l m. (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m``, - Induct >- - rw[] >> - rw[LCM_IS_LEAST_COMMON_MULTIPLE]); - -(* -> EVAL ``list_lcm []``; -val it = |- list_lcm [] = 1: thm -> EVAL ``list_lcm [1; 2; 3]``; -val it = |- list_lcm [1; 2; 3] = 6: thm -> EVAL ``list_lcm [1; 2; 3; 4; 5]``; -val it = |- list_lcm [1; 2; 3; 4; 5] = 60: thm -> EVAL ``list_lcm (GENLIST SUC 5)``; -val it = |- list_lcm (GENLIST SUC 5) = 60: thm -> EVAL ``list_lcm (GENLIST SUC 4)``; -val it = |- list_lcm (GENLIST SUC 4) = 12: thm -> EVAL ``lcm 5 (list_lcm (GENLIST SUC 4))``; -val it = |- lcm 5 (list_lcm (GENLIST SUC 4)) = 60: thm -> EVAL ``SNOC 5 (GENLIST SUC 4)``; -val it = |- SNOC 5 (GENLIST SUC 4) = [1; 2; 3; 4; 5]: thm -> EVAL ``list_lcm (SNOC 5 (GENLIST SUC 4))``; -val it = |- list_lcm (SNOC 5 (GENLIST SUC 4)) = 60: thm -> EVAL ``GENLIST (\k. leibniz 5 k) (SUC 5)``; -val it = |- GENLIST (\k. leibniz 5 k) (SUC 5) = [6; 30; 60; 60; 30; 6]: thm -> EVAL ``list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5))``; -val it = |- list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5)) = 60: thm -> EVAL ``list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5))``; -val it = |- (list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5))) <=> T: thm -> EVAL ``list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (leibniz 5) (SUC 5))``; -val it = |- (list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (leibniz 5) (SUC 5))) <=> T: thm -*) - -(* Theorem: list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2) *) -(* Proof: - By induction on l1. - Base: !l2. list_lcm ([] ++ l2) = lcm (list_lcm []) (list_lcm l2) - LHS = list_lcm ([] ++ l2) - = list_lcm l2 by APPEND - = lcm 1 (list_lcm l2) by LCM_1 - = lcm (list_lcm []) (list_lcm l2) by list_lcm_nil - = RHS - Step: !l2. list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2) ==> - !h l2. list_lcm (h::l1 ++ l2) = lcm (list_lcm (h::l1)) (list_lcm l2) - list_lcm (h::l1 ++ l2) - = list_lcm (h::(l1 ++ l2)) by APPEND - = lcm h (list_lcm (l1 ++ l2)) by list_lcm_cons - = lcm h (lcm (list_lcm l1) (list_lcm l2)) by induction hypothesis - = lcm (lcm h (list_lcm l1)) (list_lcm l2) by LCM_ASSOC - = lcm (list_lcm (h::l1)) (list_lcm l2) by list_lcm_cons -*) -val list_lcm_append = store_thm( - "list_lcm_append", - ``!l1 l2. list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2)``, - Induct >- - rw[] >> - rw[LCM_ASSOC]); - -(* Theorem: list_lcm (l1 ++ l2 ++ l3) = list_lcm [(list_lcm l1); (list_lcm l2); (list_lcm l3)] *) -(* Proof: - list_lcm (l1 ++ l2 ++ l3) - = lcm (list_lcm (l1 ++ l2)) (list_lcm l3) by list_lcm_append - = lcm (lcm (list_lcm l1) (list_lcm l2)) (list_lcm l3) by list_lcm_append - = lcm (list_lcm l1) (lcm (list_lcm l2) (list_lcm l3)) by LCM_ASSOC - = lcm (list_lcm l1) (list_lcm [(list_lcm l2); list_lcm l3]) by list_lcm_cons - = list_lcm [list_lcm l1; list_lcm l2; list_lcm l3] by list_lcm_cons -*) -val list_lcm_append_3 = store_thm( - "list_lcm_append_3", - ``!l1 l2 l3. list_lcm (l1 ++ l2 ++ l3) = list_lcm [(list_lcm l1); (list_lcm l2); (list_lcm l3)]``, - rw[list_lcm_append, LCM_ASSOC, list_lcm_cons]); - -(* Theorem: list_lcm (REVERSE l) = list_lcm l *) -(* Proof: - By induction on l. - Base: list_lcm (REVERSE []) = list_lcm [] - True since REVERSE [] = [] by REVERSE_DEF - Step: list_lcm (REVERSE l) = list_lcm l ==> - !h. list_lcm (REVERSE (h::l)) = list_lcm (h::l) - list_lcm (REVERSE (h::l)) - = list_lcm (REVERSE l ++ [h]) by REVERSE_DEF - = lcm (list_lcm (REVERSE l)) (list_lcm [h]) by list_lcm_append - = lcm (list_lcm l) (list_lcm [h]) by induction hypothesis - = lcm (list_lcm [h]) (list_lcm l) by LCM_COMM - = list_lcm ([h] ++ l) by list_lcm_append - = list_lcm (h::l) by CONS_APPEND -*) -val list_lcm_reverse = store_thm( - "list_lcm_reverse", - ``!l. list_lcm (REVERSE l) = list_lcm l``, - Induct >- - rw[] >> - rpt strip_tac >> - `list_lcm (REVERSE (h::l)) = list_lcm (REVERSE l ++ [h])` by rw[] >> - `_ = lcm (list_lcm (REVERSE l)) (list_lcm [h])` by rw[list_lcm_append] >> - `_ = lcm (list_lcm l) (list_lcm [h])` by rw[] >> - `_ = lcm (list_lcm [h]) (list_lcm l)` by rw[LCM_COMM] >> - `_ = list_lcm ([h] ++ l)` by rw[list_lcm_append] >> - `_ = list_lcm (h::l)` by rw[] >> - decide_tac); - -(* Theorem: list_lcm [1 .. (n + 1)] = lcm (n + 1) (list_lcm [1 .. n])) *) -(* Proof: - list_lcm [1 .. (n + 1)] - = list_lcm (SONC (n + 1) [1 .. n]) by listRangeINC_SNOC, 1 <= n + 1 - = lcm (n + 1) (list_lcm [1 .. n]) by list_lcm_snoc -*) -val list_lcm_suc = store_thm( - "list_lcm_suc", - ``!n. list_lcm [1 .. (n + 1)] = lcm (n + 1) (list_lcm [1 .. n])``, - rw[listRangeINC_SNOC, list_lcm_snoc]); - -(* Theorem: l <> [] /\ EVERY_POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l *) -(* Proof: - Note LENGTH l <> 0 by LENGTH_NIL - and SUM l <= LENGTH l * list_lcm l by list_lcm_lower_bound - so (SUM l) DIV (LENGTH l) <= list_lcm l by DIV_LE -*) -val list_lcm_nonempty_lower = store_thm( - "list_lcm_nonempty_lower", - ``!l. l <> [] /\ EVERY_POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l``, - metis_tac[list_lcm_lower_bound, DIV_LE, LENGTH_NIL, NOT_ZERO_LT_ZERO]); - -(* Theorem: l <> [] /\ POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l *) -(* Proof: - Note LENGTH l <> 0 by LENGTH_NIL - and SUM l <= LENGTH l * list_lcm l by list_lcm_lower_bound_alt - so (SUM l) DIV (LENGTH l) <= list_lcm l by DIV_LE -*) -val list_lcm_nonempty_lower_alt = store_thm( - "list_lcm_nonempty_lower_alt", - ``!l. l <> [] /\ POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l``, - metis_tac[list_lcm_lower_bound_alt, DIV_LE, LENGTH_NIL, NOT_ZERO_LT_ZERO]); - -(* Theorem: MEM x l /\ MEM y l ==> (lcm x y) <= list_lcm l *) -(* Proof: - Note x divides (list_lcm l) by list_lcm_is_common_multiple - and y divides (list_lcm l) by list_lcm_is_common_multiple - ==> (lcm x y) divides (list_lcm l) by LCM_IS_LEAST_COMMON_MULTIPLE -*) -val list_lcm_divisor_lcm_pair = store_thm( - "list_lcm_divisor_lcm_pair", - ``!l x y. MEM x l /\ MEM y l ==> (lcm x y) divides list_lcm l``, - rw[list_lcm_is_common_multiple, LCM_IS_LEAST_COMMON_MULTIPLE]); - -(* Theorem: POSITIVE l /\ MEM x l /\ MEM y l ==> (lcm x y) <= list_lcm l *) -(* Proof: - Note (lcm x y) divides (list_lcm l) by list_lcm_divisor_lcm_pair - Now 0 < list_lcm l by list_lcm_pos_alt - Thus (lcm x y) <= list_lcm l by DIVIDES_LE -*) -val list_lcm_lower_by_lcm_pair = store_thm( - "list_lcm_lower_by_lcm_pair", - ``!l x y. POSITIVE l /\ MEM x l /\ MEM y l ==> (lcm x y) <= list_lcm l``, - rw[list_lcm_divisor_lcm_pair, list_lcm_pos_alt, DIVIDES_LE]); - -(* Theorem: 0 < m /\ (!x. MEM x l ==> x divides m) ==> list_lcm l <= m *) -(* Proof: - Note list_lcm l divides m by list_lcm_is_least_common_multiple - Thus list_lcm l <= m by DIVIDES_LE, 0 < m -*) -val list_lcm_upper_by_common_multiple = store_thm( - "list_lcm_upper_by_common_multiple", - ``!l m. 0 < m /\ (!x. MEM x l ==> x divides m) ==> list_lcm l <= m``, - rw[list_lcm_is_least_common_multiple, DIVIDES_LE]); - -(* Theorem: list_lcm ls = FOLDR lcm 1 ls *) -(* Proof: - By induction on ls. - Base: list_lcm [] = FOLDR lcm 1 [] - list_lcm [] - = 1 by list_lcm_nil - = FOLDR lcm 1 [] by FOLDR - Step: list_lcm ls = FOLDR lcm 1 ls ==> - !h. list_lcm (h::ls) = FOLDR lcm 1 (h::ls) - list_lcm (h::ls) - = lcm h (list_lcm ls) by list_lcm_def - = lcm h (FOLDR lcm 1 ls) by induction hypothesis - = FOLDR lcm 1 (h::ls) by FOLDR -*) -val list_lcm_by_FOLDR = store_thm( - "list_lcm_by_FOLDR", - ``!ls. list_lcm ls = FOLDR lcm 1 ls``, - Induct >> rw[]); - -(* Theorem: list_lcm ls = FOLDL lcm 1 ls *) -(* Proof: - Note COMM lcm since !x y. lcm x y = lcm y x by LCM_COMM - and ASSOC lcm since !x y z. lcm x (lcm y z) = lcm (lcm x y) z by LCM_ASSOC - Now list_lcm ls - = FOLDR lcm 1 ls by list_lcm_by FOLDR - = FOLDL lcm 1 ls by FOLDL_EQ_FOLDR, COMM lcm, ASSOC lcm -*) -val list_lcm_by_FOLDL = store_thm( - "list_lcm_by_FOLDL", - ``!ls. list_lcm ls = FOLDL lcm 1 ls``, - simp[list_lcm_by_FOLDR] >> - irule (GSYM FOLDL_EQ_FOLDR) >> - rpt strip_tac >- - rw[LCM_ASSOC, combinTheory.ASSOC_DEF] >> - rw[LCM_COMM, combinTheory.COMM_DEF]); - -(* ------------------------------------------------------------------------- *) -(* Lists in Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Veritcal Lists in Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* Define Vertical List in Leibniz Triangle *) -(* -val leibniz_vertical_def = Define ` - leibniz_vertical n = GENLIST SUC (SUC n) -`; - -(* Use overloading for leibniz_vertical n. *) -val _ = overload_on("leibniz_vertical", ``\n. GENLIST ((+) 1) (n + 1)``); -*) - -(* Define Vertical (downward list) in Leibniz Triangle *) - -(* Use overloading for leibniz_vertical n. *) -val _ = overload_on("leibniz_vertical", ``\n. [1 .. (n+1)]``); - -(* Theorem: leibniz_vertical n = GENLIST (\i. 1 + i) (n + 1) *) -(* Proof: - leibniz_vertical n - = [1 .. (n+1)] by notation - = GENLIST (\i. 1 + i) (n+1 + 1 - 1) by listRangeINC_def - = GENLIST (\i. 1 + i) (n + 1) by arithmetic -*) -val leibniz_vertical_alt = store_thm( - "leibniz_vertical_alt", - ``!n. leibniz_vertical n = GENLIST (\i. 1 + i) (n + 1)``, - rw[listRangeINC_def]); - -(* Theorem: leibniz_vertical 0 = [1] *) -(* Proof: - leibniz_vertical 0 - = [1 .. (0+1)] by notation - = [1 .. 1] by arithmetic - = [1] by listRangeINC_SING -*) -val leibniz_vertical_0 = store_thm( - "leibniz_vertical_0", - ``leibniz_vertical 0 = [1]``, - rw[]); - -(* Theorem: LENGTH (leibniz_vertical n) = n + 1 *) -(* Proof: - LENGTH (leibniz_vertical n) - = LENGTH [1 .. (n+1)] by notation - = n + 1 + 1 - 1 by listRangeINC_LEN - = n + 1 by arithmetic -*) -val leibniz_vertical_len = store_thm( - "leibniz_vertical_len", - ``!n. LENGTH (leibniz_vertical n) = n + 1``, - rw[listRangeINC_LEN]); - -(* Theorem: leibniz_vertical n <> [] *) -(* Proof: - LENGTH (leibniz_vertical n) - = n + 1 by leibniz_vertical_len - <> 0 by ADD1, SUC_NOT_ZERO - Thus leibniz_vertical n <> [] by LENGTH_EQ_0 -*) -val leibniz_vertical_not_nil = store_thm( - "leibniz_vertical_not_nil", - ``!n. leibniz_vertical n <> []``, - metis_tac[leibniz_vertical_len, LENGTH_EQ_0, DECIDE``!n. n + 1 <> 0``]); - -(* Theorem: EVERY_POSITIVE (leibniz_vertical n) *) -(* Proof: - EVERY_POSITIVE (leibniz_vertical n) - <=> EVERY_POSITIVE GENLIST (\i. 1 + i) (n+1) by leibniz_vertical_alt - <=> !i. i < n + 1 ==> 0 < 1 + i by EVERY_GENLIST - <=> !i. i < n + 1 ==> T by arithmetic - <=> T -*) -val leibniz_vertical_pos = store_thm( - "leibniz_vertical_pos", - ``!n. EVERY_POSITIVE (leibniz_vertical n)``, - rw[leibniz_vertical_alt, EVERY_GENLIST]); - -(* Theorem: POSITIVE (leibniz_vertical n) *) -(* Proof: by leibniz_vertical_pos, EVERY_MEM *) -val leibniz_vertical_pos_alt = store_thm( - "leibniz_vertical_pos_alt", - ``!n. POSITIVE (leibniz_vertical n)``, - rw[leibniz_vertical_pos, EVERY_MEM]); - -(* Theorem: 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_vertical n) *) -(* Proof: - Note: (leibniz_vertical n) has 1 to (n+1), inclusive: - MEM x (leibniz_vertical n) - <=> MEM x [1 .. (n+1)] by notation - <=> 1 <= x /\ x <= n + 1 by listRangeINC_MEM - <=> 0 < x /\ x <= n + 1 by num_CASES, LESS_EQ_MONO -*) -val leibniz_vertical_mem = store_thm( - "leibniz_vertical_mem", - ``!n x. 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_vertical n)``, - rw[]); - -(* Theorem: leibniz_vertical (n + 1) = SNOC (n + 2) (leibniz_vertical n) *) -(* Proof: - leibniz_vertical (n + 1) - = [1 .. (n+1 +1)] by notation - = SNOC (n+1 + 1) [1 .. (n+1)] by listRangeINC_SNOC - = SNOC (n + 2) (leibniz_vertical n) by notation -*) -val leibniz_vertical_snoc = store_thm( - "leibniz_vertical_snoc", - ``!n. leibniz_vertical (n + 1) = SNOC (n + 2) (leibniz_vertical n)``, - rw[listRangeINC_SNOC]);; - -(* Use overloading for leibniz_up n. *) -val _ = overload_on("leibniz_up", ``\n. REVERSE (leibniz_vertical n)``); - -(* Theorem: leibniz_up 0 = [1] *) -(* Proof: - leibniz_up 0 - = REVERSE (leibniz_vertical 0) by notation - = REVERSE [1] by leibniz_vertical_0 - = [1] by REVERSE_SING -*) -val leibniz_up_0 = store_thm( - "leibniz_up_0", - ``leibniz_up 0 = [1]``, - rw[]); - -(* Theorem: LENGTH (leibniz_up n) = n + 1 *) -(* Proof: - LENGTH (leibniz_up n) - = LENGTH (REVERSE (leibniz_vertical n)) by notation - = LENGTH (leibniz_vertical n) by LENGTH_REVERSE - = n + 1 by leibniz_vertical_len -*) -val leibniz_up_len = store_thm( - "leibniz_up_len", - ``!n. LENGTH (leibniz_up n) = n + 1``, - rw[leibniz_vertical_len]); - -(* Theorem: EVERY_POSITIVE (leibniz_up n) *) -(* Proof: - EVERY_POSITIVE (leibniz_up n) - <=> EVERY_POSITIVE (REVERSE (leibniz_vertical n)) by notation - <=> EVERY_POSITIVE (leibniz_vertical n) by EVERY_REVERSE - <=> T by leibniz_vertical_pos -*) -val leibniz_up_pos = store_thm( - "leibniz_up_pos", - ``!n. EVERY_POSITIVE (leibniz_up n)``, - rw[leibniz_vertical_pos, EVERY_REVERSE]); - -(* Theorem: 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_up n) *) -(* Proof: - Note: (leibniz_up n) has (n+1) downto 1, inclusive: - MEM x (leibniz_up n) - <=> MEM x (REVERSE (leibniz_vertical n)) by notation - <=> MEM x (leibniz_vertical n) by MEM_REVERSE - <=> T by leibniz_vertical_mem -*) -val leibniz_up_mem = store_thm( - "leibniz_up_mem", - ``!n x. 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_up n)``, - rw[]); - -(* Theorem: leibniz_up (n + 1) = (n + 2) :: (leibniz_up n) *) -(* Proof: - leibniz_up (n + 1) - = REVERSE (leibniz_vertical (n + 1)) by notation - = REVERSE (SNOC (n + 2) (leibniz_vertical n)) by leibniz_vertical_snoc - = (n + 2) :: (leibniz_up n) by REVERSE_SNOC -*) -val leibniz_up_cons = store_thm( - "leibniz_up_cons", - ``!n. leibniz_up (n + 1) = (n + 2) :: (leibniz_up n)``, - rw[leibniz_vertical_snoc, REVERSE_SNOC]); - -(* ------------------------------------------------------------------------- *) -(* Horizontal List in Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* Define row (horizontal list) in Leibniz Triangle *) -(* -val leibniz_horizontal_def = Define ` - leibniz_horizontal n = GENLIST (leibniz n) (SUC n) -`; - -(* Use overloading for leibniz_horizontal n. *) -val _ = overload_on("leibniz_horizontal", ``\n. GENLIST (leibniz n) (n + 1)``); -*) - -(* Use overloading for leibniz_horizontal n. *) -val _ = overload_on("leibniz_horizontal", ``\n. GENLIST (leibniz n) (n + 1)``); - -(* -> EVAL ``leibniz_horizontal 0``; -val it = |- leibniz_horizontal 0 = [1]: thm -> EVAL ``leibniz_horizontal 1``; -val it = |- leibniz_horizontal 1 = [2; 2]: thm -> EVAL ``leibniz_horizontal 2``; -val it = |- leibniz_horizontal 2 = [3; 6; 3]: thm -> EVAL ``leibniz_horizontal 3``; -val it = |- leibniz_horizontal 3 = [4; 12; 12; 4]: thm -> EVAL ``leibniz_horizontal 4``; -val it = |- leibniz_horizontal 4 = [5; 20; 30; 20; 5]: thm -> EVAL ``leibniz_horizontal 5``; -val it = |- leibniz_horizontal 5 = [6; 30; 60; 60; 30; 6]: thm -> EVAL ``leibniz_horizontal 6``; -val it = |- leibniz_horizontal 6 = [7; 42; 105; 140; 105; 42; 7]: thm -> EVAL ``leibniz_horizontal 7``; -val it = |- leibniz_horizontal 7 = [8; 56; 168; 280; 280; 168; 56; 8]: thm -> EVAL ``leibniz_horizontal 8``; -val it = |- leibniz_horizontal 8 = [9; 72; 252; 504; 630; 504; 252; 72; 9]: thm -*) - -(* Theorem: leibniz_horizontal 0 = [1] *) -(* Proof: - leibniz_horizontal 0 - = GENLIST (leibniz 0) (0 + 1) by notation - = GENLIST (leibniz 0) 1 by arithmetic - = [leibniz 0 0] by GENLIST - = [1] by leibniz_n_0 -*) -val leibniz_horizontal_0 = store_thm( - "leibniz_horizontal_0", - ``leibniz_horizontal 0 = [1]``, - rw_tac std_ss[GENLIST_1, leibniz_n_0]); - -(* Theorem: LENGTH (leibniz_horizontal n) = n + 1 *) -(* Proof: - LENGTH (leibniz_horizontal n) - = LENGTH (GENLIST (leibniz n) (n + 1)) by notation - = n + 1 by LENGTH_GENLIST -*) -val leibniz_horizontal_len = store_thm( - "leibniz_horizontal_len", - ``!n. LENGTH (leibniz_horizontal n) = n + 1``, - rw[]); - -(* Theorem: k <= n ==> EL k (leibniz_horizontal n) = leibniz n k *) -(* Proof: - Note k <= n means k < SUC n. - EL k (leibniz_horizontal n) - = EL k (GENLIST (leibniz n) (n + 1)) by notation - = EL k (GENLIST (leibniz n) (SUC n)) by ADD1 - = leibniz n k by EL_GENLIST, k < SUC n. -*) -val leibniz_horizontal_el = store_thm( - "leibniz_horizontal_el", - ``!n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k)``, - rw[LESS_EQ_IMP_LESS_SUC]); - -(* Theorem: k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) *) -(* Proof: - Note k <= n ==> k < (n + 1) - Thus MEM (leibniz n k) (GENLIST (leibniz n) (n + 1)) by MEM_GENLIST - or MEM (leibniz n k) (leibniz_horizontal n) by notation -*) -val leibniz_horizontal_mem = store_thm( - "leibniz_horizontal_mem", - ``!n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n)``, - metis_tac[MEM_GENLIST, DECIDE``k <= n ==> k < n + 1``]); - -(* Theorem: MEM (leibniz n k) (leibniz_horizontal n) <=> k <= n *) -(* Proof: - If part: (leibniz n k) (leibniz_horizontal n) ==> k <= n - By contradiction, suppose n < k. - Then leibniz n k = 0 by binomial_less_0, ~(k <= n) - But ?m. m < n + 1 ==> 0 = leibniz n m by MEM_GENLIST - or m <= n ==> leibniz n m = 0 by m < n + 1 - Yet leibniz n m <> 0 by leibniz_eq_0 - This is a contradiction. - Only-if part: k <= n ==> (leibniz n k) (leibniz_horizontal n) - By MEM_GENLIST, this is to show: - ?m. m < n + 1 /\ (leibniz n k = leibniz n m) - Note k <= n ==> k < n + 1, - Take m = k, the result follows. -*) -val leibniz_horizontal_mem_iff = store_thm( - "leibniz_horizontal_mem_iff", - ``!n k. MEM (leibniz n k) (leibniz_horizontal n) <=> k <= n``, - rw_tac bool_ss[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `leibniz n k = 0` by rw[leibniz_less_0] >> - fs[MEM_GENLIST] >> - `m <= n` by decide_tac >> - fs[binomial_eq_0], - rw[MEM_GENLIST] >> - `k < n + 1` by decide_tac >> - metis_tac[] - ]); - -(* Theorem: MEM x (leibniz_horizontal n) <=> ?k. k <= n /\ (x = leibniz n k) *) -(* Proof: - By MEM_GENLIST, this is to show: - (?m. m < n + 1 /\ (x = (n + 1) * binomial n m)) <=> ?k. k <= n /\ (x = (n + 1) * binomial n k) - Since m < n + 1 <=> m <= n by LE_LT1 - This is trivially true. -*) -val leibniz_horizontal_member = store_thm( - "leibniz_horizontal_member", - ``!n x. MEM x (leibniz_horizontal n) <=> ?k. k <= n /\ (x = leibniz n k)``, - metis_tac[MEM_GENLIST, LE_LT1]); - -(* Theorem: k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k) *) -(* Proof: by EL_GENLIST *) -val leibniz_horizontal_element = store_thm( - "leibniz_horizontal_element", - ``!n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k)``, - rw[EL_GENLIST]); - -(* Theorem: TAKE 1 (leibniz_horizontal (n + 1)) = [n + 2] *) -(* Proof: - TAKE 1 (leibniz_horizontal (n + 1)) - = TAKE 1 (GENLIST (leibniz (n + 1)) (n + 1 + 1)) by notation - = TAKE 1 (GENLIST (leibniz (SUC n)) (SUC (SUC n))) by ADD1 - = TAKE 1 ((leibniz (SUC n) 0) :: GENLIST ((leibniz (SUC n)) o SUC) n) by GENLIST_CONS - = (leibniz (SUC n) 0):: TAKE 0 (GENLIST ((leibniz (SUC n)) o SUC) n) by TAKE_def - = [leibniz (SUC n) 0]:: [] by TAKE_0 - = [SUC n + 1] by leibniz_n_0 - = [n + 2] by ADD1 -*) -val leibniz_horizontal_head = store_thm( - "leibniz_horizontal_head", - ``!n. TAKE 1 (leibniz_horizontal (n + 1)) = [n + 2]``, - rpt strip_tac >> - `(!n. n + 1 = SUC n) /\ (!n. n + 2 = SUC (SUC n))` by decide_tac >> - rw[GENLIST_CONS, leibniz_n_0]); - -(* Theorem: k <= n ==> (leibniz n k) divides list_lcm (leibniz_horizontal n) *) -(* Proof: - Note MEM (leibniz n k) (leibniz_horizontal n) by leibniz_horizontal_mem - so (leibniz n k) divides list_lcm (leibniz_horizontal n) by list_lcm_is_common_multiple -*) -val leibniz_horizontal_divisor = store_thm( - "leibniz_horizontal_divisor", - ``!n k. k <= n ==> (leibniz n k) divides list_lcm (leibniz_horizontal n)``, - rw[leibniz_horizontal_mem, list_lcm_is_common_multiple]); - -(* Theorem: EVERY_POSITIVE (leibniz_horizontal n) *) -(* Proof: - Let l = leibniz_horizontal n - Then LENGTH l = n + 1 by leibniz_horizontal_len - EVERY_POSITIVE l - <=> !k. k < LENGTH l ==> 0 < (EL k l) by EVERY_EL - <=> !k. k < n + 1 ==> 0 < (EL k l) by above - <=> !k. k <= n ==> 0 < EL k l by arithmetic - <=> !k. k <= n ==> 0 < leibniz n k by leibniz_horizontal_el - <=> T by leibniz_pos -*) -Theorem leibniz_horizontal_pos: - !n. EVERY_POSITIVE (leibniz_horizontal n) -Proof - simp[EVERY_EL, binomial_pos] -QED - -(* Theorem: POSITIVE (leibniz_horizontal n) *) -(* Proof: by leibniz_horizontal_pos, EVERY_MEM *) -val leibniz_horizontal_pos_alt = store_thm( - "leibniz_horizontal_pos_alt", - ``!n. POSITIVE (leibniz_horizontal n)``, - metis_tac[leibniz_horizontal_pos, EVERY_MEM]); - -(* Theorem: leibniz_horizontal n = MAP (\j. (n+1) * j) (binomial_horizontal n) *) -(* Proof: - leibniz_horizontal n - = GENLIST (leibniz n) (n + 1) by notation - = GENLIST ((\j. (n + 1) * j) o (binomial n)) (n + 1) by leibniz_alt - = MAP (\j. (n + 1) * j) (GENLIST (binomial n) (n + 1)) by MAP_GENLIST - = MAP (\j. (n + 1) * j) (binomial_horizontal n) by notation -*) -val leibniz_horizontal_alt = store_thm( - "leibniz_horizontal_alt", - ``!n. leibniz_horizontal n = MAP (\j. (n+1) * j) (binomial_horizontal n)``, - rw_tac std_ss[leibniz_alt, MAP_GENLIST]); - -(* Theorem: list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n) *) -(* Proof: - Since LENGTH (binomial_horizontal n) = n + 1 by binomial_horizontal_len - binomial_horizontal n <> [] by LENGTH_NIL ... [1] - list_lcm (leibniz_horizontal n) - = list_lcm (MAP (\j (n+1) * j) (binomial_horizontal n)) by leibniz_horizontal_alt - = (n + 1) * list_lcm (binomial_horizontal n) by list_lcm_map_times, [1] -*) -val leibniz_horizontal_lcm_alt = store_thm( - "leibniz_horizontal_lcm_alt", - ``!n. list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n)``, - rpt strip_tac >> - `LENGTH (binomial_horizontal n) = n + 1` by rw[binomial_horizontal_len] >> - `n + 1 <> 0` by decide_tac >> - `binomial_horizontal n <> []` by metis_tac[LENGTH_NIL] >> - rw_tac std_ss[leibniz_horizontal_alt, list_lcm_map_times]); - -(* Theorem: SUM (leibniz_horizontal n) = (n + 1) * SUM (binomial_horizontal n) *) -(* Proof: - SUM (leibniz_horizontal n) - = SUM (MAP (\j. (n + 1) * j) (binomial_horizontal n)) by leibniz_horizontal_alt - = (n + 1) * SUM (binomial_horizontal n) by SUM_MULT -*) -val leibniz_horizontal_sum = store_thm( - "leibniz_horizontal_sum", - ``!n. SUM (leibniz_horizontal n) = (n + 1) * SUM (binomial_horizontal n)``, - rw[leibniz_horizontal_alt, SUM_MULT] >> - `(\j. j * (n + 1)) = $* (n + 1)` by rw[FUN_EQ_THM] >> - rw[]); - -(* Theorem: SUM (leibniz_horizontal n) = (n + 1) * 2 ** n *) -(* Proof: - SUM (leibniz_horizontal n) - = (n + 1) * SUM (binomial_horizontal n) by leibniz_horizontal_sum - = (n + 1) * 2 ** n by binomial_horizontal_sum -*) -val leibniz_horizontal_sum_eqn = store_thm( - "leibniz_horizontal_sum_eqn", - ``!n. SUM (leibniz_horizontal n) = (n + 1) * 2 ** n``, - rw[leibniz_horizontal_sum, binomial_horizontal_sum]); - -(* Theorem: SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = SUM (binomial_horizontal n) *) -(* Proof: - Note LENGTH (leibniz_horizontal n) = n + 1 by leibniz_horizontal_len - so 0 < LENGTH (leibniz_horizontal n) by 0 < n + 1 - - SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) - = ((n + 1) * SUM (binomial_horizontal n)) DIV (n + 1) by leibniz_horizontal_sum - = SUM (binomial_horizontal n) by MULT_TO_DIV, 0 < n + 1 -*) -val leibniz_horizontal_average = store_thm( - "leibniz_horizontal_average", - ``!n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = SUM (binomial_horizontal n)``, - metis_tac[leibniz_horizontal_sum, leibniz_horizontal_len, MULT_TO_DIV, DECIDE``0 < n + 1``]); - -(* Theorem: SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = 2 ** n *) -(* Proof: - SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) - = SUM (binomial_horizontal n) by leibniz_horizontal_average - = 2 ** n by binomial_horizontal_sum -*) -val leibniz_horizontal_average_eqn = store_thm( - "leibniz_horizontal_average_eqn", - ``!n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = 2 ** n``, - rw[leibniz_horizontal_average, binomial_horizontal_sum]); - -(* ------------------------------------------------------------------------- *) -(* Transform from Vertical LCM to Horizontal LCM. *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Using Triplet and Paths *) -(* ------------------------------------------------------------------------- *) - -(* Define a triple type *) -val _ = Hol_datatype` - triple = <| a: num; - b: num; - c: num - |> -`; - -(* A triplet is a triple composed of Leibniz node and children. *) -val triplet_def = Define` - (triplet n k):triple = - <| a := leibniz n k; - b := leibniz (n + 1) k; - c := leibniz (n + 1) (k + 1) - |> -`; - -(* can even do this after definition of triple type: - -val triple_def = Define` - triple n k = - <| a := leibniz n k; - b := leibniz (n + 1) k; - c := leibniz (n + 1) (k + 1) - |> -`; -*) - -(* Overload elements of a triplet *) -(* -val _ = overload_on("tri_a", ``leibniz n k``); -val _ = overload_on("tri_b", ``leibniz (SUC n) k``); -val _ = overload_on("tri_c", ``leibniz (SUC n) (SUC k)``); - -val _ = overload_on("tri_a", ``(triple n k).a``); -val _ = overload_on("tri_b", ``(triple n k).b``); -val _ = overload_on("tri_c", ``(triple n k).c``); -*) -val _ = temp_overload_on("ta", ``(triplet n k).a``); -val _ = temp_overload_on("tb", ``(triplet n k).b``); -val _ = temp_overload_on("tc", ``(triplet n k).c``); - -(* Theorem: (ta = leibniz n k) /\ (tb = leibniz (n + 1) k) /\ (tc = leibniz (n + 1) (k + 1)) *) -(* Proof: by triplet_def *) -val leibniz_triplet_member = store_thm( - "leibniz_triplet_member", - ``!n k. (ta = leibniz n k) /\ (tb = leibniz (n + 1) k) /\ (tc = leibniz (n + 1) (k + 1))``, - rw[triplet_def]); - -(* Theorem: (k + 1) * tc = (n + 1 - k) * tb *) -(* Proof: - Apply: > leibniz_right_eqn |> SPEC ``n+1``; - val it = |- 0 < n + 1 ==> !k. (k + 1) * leibniz (n + 1) (k + 1) = (n + 1 - k) * leibniz (n + 1) k: thm -*) -val leibniz_right_entry = store_thm( - "leibniz_right_entry", - ``!(n k):num. (k + 1) * tc = (n + 1 - k) * tb``, - rw_tac arith_ss[triplet_def, leibniz_right_eqn]); - -(* Theorem: (n + 2) * ta = (n + 1 - k) * tb *) -(* Proof: - Apply: > leibniz_up_eqn |> SPEC ``n+1``; - val it = |- 0 < n + 1 ==> !k. (n + 1 + 1) * leibniz (n + 1 - 1) k = (n + 1 - k) * leibniz (n + 1) k: thm -*) -val leibniz_up_entry = store_thm( - "leibniz_up_entry", - ``!(n k):num. (n + 2) * ta = (n + 1 - k) * tb``, - rw_tac std_ss[triplet_def, leibniz_up_eqn |> SPEC ``n+1`` |> SIMP_RULE arith_ss[]]); - -(* Theorem: ta * tb = tc * (tb - ta) *) -(* Proof: - Apply > leibniz_property |> SPEC ``n+1``; - val it = |- 0 < n + 1 ==> !k. !k. leibniz (n + 1) k * leibniz (n + 1 - 1) k = - leibniz (n + 1) (k + 1) * (leibniz (n + 1) k - leibniz (n + 1 - 1) k): thm -*) -val leibniz_triplet_property = store_thm( - "leibniz_triplet_property", - ``!(n k):num. ta * tb = tc * (tb - ta)``, - rw_tac std_ss[triplet_def, MULT_COMM, leibniz_property |> SPEC ``n+1`` |> SIMP_RULE arith_ss[]]); - -(* Direct proof of same result, for the paper. *) - -(* Theorem: ta * tb = tc * (tb - ta) *) -(* Proof: - If n < k, - Note n < k ==> ta = 0 by triplet_def, leibniz_less_0 - also n + 1 < k + 1 ==> tc = 0 by triplet_def, leibniz_less_0 - Thus ta * tb = 0 = tc * (tb - ta) by MULT_EQ_0 - If ~(n < k), - Then (n + 2) - (n + 1 - k) = k + 1 by arithmetic, k <= n. - - (k + 1) * ta * tb - = (n + 2 - (n + 1 - k)) * ta * tb - = (n + 2) * ta * tb - (n + 1 - k) * ta * tb by RIGHT_SUB_DISTRIB - = (n + 1 - k) * tb * tb - (n + 1 - k) * ta * tb by leibniz_up_entry - = (n + 1 - k) * tb * tb - (n + 1 - k) * tb * ta by MULT_ASSOC, MULT_COMM - = (n + 1 - k) * tb * (tb - ta) by LEFT_SUB_DISTRIB - = (k + 1) * tc * (tb - ta) by leibniz_right_entry - - Since k + 1 <> 0, the result follows by MULT_LEFT_CANCEL -*) -Theorem leibniz_triplet_property[allow_rebind]: - !n k:num. ta * tb = tc * (tb - ta) -Proof - rpt strip_tac >> - Cases_on ‘n < k’ >- - rw[triplet_def, leibniz_less_0] >> - ‘(n + 2) - (n + 1 - k) = k + 1’ by decide_tac >> - ‘(k + 1) * ta * tb = (n + 2 - (n + 1 - k)) * ta * tb’ by rw[] >> - ‘_ = (n + 2) * ta * tb - (n + 1 - k) * ta * tb’ by rw_tac std_ss[RIGHT_SUB_DISTRIB] >> - ‘_ = (n + 1 - k) * tb * tb - (n + 1 - k) * ta * tb’ by rw_tac std_ss[leibniz_up_entry] >> - ‘_ = (n + 1 - k) * tb * tb - (n + 1 - k) * tb * ta’ by metis_tac[MULT_ASSOC, MULT_COMM] >> - ‘_ = (n + 1 - k) * tb * (tb - ta)’ by rw_tac std_ss[LEFT_SUB_DISTRIB] >> - ‘_ = (k + 1) * tc * (tb - ta)’ by rw_tac std_ss[leibniz_right_entry] >> - ‘k + 1 <> 0’ by decide_tac >> - metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] -QED - -(* Theorem: lcm tb ta = lcm tb tc *) -(* Proof: - Apply: > leibniz_lcm_exchange |> SPEC ``n+1``; - val it = |- 0 < n + 1 ==> - !k. lcm (leibniz (n + 1) k) (leibniz (n + 1 - 1) k) = - lcm (leibniz (n + 1) k) (leibniz (n + 1) (k + 1)): thm -*) -val leibniz_triplet_lcm = store_thm( - "leibniz_triplet_lcm", - ``!(n k):num. lcm tb ta = lcm tb tc``, - rw_tac std_ss[triplet_def, leibniz_lcm_exchange |> SPEC ``n+1`` |> SIMP_RULE arith_ss[]]); - -(* ------------------------------------------------------------------------- *) -(* Zigzag Path in Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* Define a path type *) -val _ = temp_type_abbrev("path", Type `:num list`); - -(* Define paths reachable by one zigzag *) -val leibniz_zigzag_def = Define` - leibniz_zigzag (p1: path) (p2: path) <=> - ?(n k):num (x y):path. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) -`; -val _ = overload_on("zigzag", ``leibniz_zigzag``); -val _ = set_fixity "zigzag" (Infix(NONASSOC, 450)); (* same as relation *) - -(* Theorem: p1 zigzag p2 ==> (list_lcm p1 = list_lcm p2) *) -(* Proof: - Given p1 zigzag p2, - ==> ?n k x y. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) by leibniz_zigzag_def - - list_lcm p1 - = list_lcm (x ++ [tb; ta] ++ y) by above - = lcm (list_lcm (x ++ [tb; ta])) (list_lcm y) by list_lcm_append - = lcm (list_lcm (x ++ ([tb; ta]))) (list_lcm y) by APPEND_ASSOC - = lcm (lcm (list_lcm x) (list_lcm ([tb; ta]))) (list_lcm y) by list_lcm_append - = lcm (lcm (list_lcm x) (lcm tb ta)) (list_lcm y) by list_lcm_append, list_lcm_sing - = lcm (lcm (list_lcm x) (lcm tb tc)) (list_lcm y) by leibniz_triplet_lcm - = lcm (lcm (list_lcm x) (list_lcm ([tb; tc]))) (list_lcm y) by list_lcm_append, list_lcm_sing - = lcm (list_lcm (x ++ ([tb; tc]))) (list_lcm y) by list_lcm_append - = lcm (list_lcm (x ++ [tb; tc])) (list_lcm y) by APPEND_ASSOC - = list_lcm (x ++ [tb; tc] ++ y) by list_lcm_append - = list_lcm p2 by above -*) -val list_lcm_zigzag = store_thm( - "list_lcm_zigzag", - ``!p1 p2. p1 zigzag p2 ==> (list_lcm p1 = list_lcm p2)``, - rw_tac std_ss[leibniz_zigzag_def] >> - `list_lcm (x ++ [tb; ta] ++ y) = lcm (list_lcm (x ++ [tb; ta])) (list_lcm y)` by rw[list_lcm_append] >> - `_ = lcm (list_lcm (x ++ ([tb; ta]))) (list_lcm y)` by rw[] >> - `_ = lcm (lcm (list_lcm x) (lcm tb ta)) (list_lcm y)` by rw[list_lcm_append] >> - `_ = lcm (lcm (list_lcm x) (lcm tb tc)) (list_lcm y)` by rw[leibniz_triplet_lcm] >> - `_ = lcm (list_lcm (x ++ ([tb; tc]))) (list_lcm y)` by rw[list_lcm_append] >> - `_ = lcm (list_lcm (x ++ [tb; tc])) (list_lcm y)` by rw[] >> - `_ = list_lcm (x ++ [tb; tc] ++ y)` by rw[list_lcm_append] >> - rw[]); - -(* Theorem: p1 zigzag p2 ==> !x. ([x] ++ p1) zigzag ([x] ++ p2) *) -(* Proof: - Since p1 zigzag p2 - ==> ?n k x y. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) by leibniz_zigzag_def - - [x] ++ p1 - = [x] ++ (x ++ [tb; ta] ++ y) by above - = [x] ++ x ++ [tb; ta] ++ y by APPEND - [x] ++ p2 - = [x] ++ (x ++ [tb; tc] ++ y) by above - = [x] ++ x ++ [tb; tc] ++ y by APPEND - Take new x = [x] ++ x, new y = y. - Then ([x] ++ p1) zigzag ([x] ++ p2) by leibniz_zigzag_def -*) -val leibniz_zigzag_tail = store_thm( - "leibniz_zigzag_tail", - ``!p1 p2. p1 zigzag p2 ==> !x. ([x] ++ p1) zigzag ([x] ++ p2)``, - metis_tac[leibniz_zigzag_def, APPEND]); - -(* Theorem: k <= n ==> - TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) zigzag - TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) *) -(* Proof: - Since k <= n, k < n + 1, and k + 1 < n + 2. - Hence k < LENGTH (leibniz_horizontal (n + 1)), - - Let x = TAKE k (leibniz_horizontal (n + 1)) - and y = DROP (k + 1) (leibniz_horizontal n) - TAKE (k + 1) (leibniz_horizontal (n + 1)) - = TAKE (SUC k) (leibniz_horizontal (SUC n)) by ADD1 - = SNOC tb x by TAKE_SUC_BY_TAKE, k < LENGTH (leibniz_horizontal (n + 1)) - = x ++ [tb] by SNOC_APPEND - TAKE (k + 2) (leibniz_horizontal (n + 1)) - = TAKE (SUC (SUC k)) (leibniz_horizontal (SUC n)) by ADD1 - = SNOC tc (SNOC tb x) by TAKE_SUC_BY_TAKE, k + 1 < LENGTH (leibniz_horizontal (n + 1)) - = x ++ [tb; tc] by SNOC_APPEND - DROP k (leibniz_horizontal n) - = ta :: y by DROP_BY_DROP_SUC, k < LENGTH (leibniz_horizontal n) - = [ta] ++ y by CONS_APPEND - Hence - Let p1 = TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) - = x ++ [tb] ++ [ta] ++ y - = x ++ [tb; ta] ++ y by APPEND - Let p2 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) - = x ++ [tb; tc] ++ y - Therefore p1 zigzag p2 by leibniz_zigzag_def -*) -val leibniz_horizontal_zigzag = store_thm( - "leibniz_horizontal_zigzag", - ``!n k. k <= n ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) zigzag - TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n)``, - rpt strip_tac >> - qabbrev_tac `x = TAKE k (leibniz_horizontal (n + 1))` >> - qabbrev_tac `y = DROP (k + 1) (leibniz_horizontal n)` >> - `k <= n + 1` by decide_tac >> - `EL k (leibniz_horizontal n) = ta` by rw_tac std_ss[triplet_def, leibniz_horizontal_el] >> - `EL k (leibniz_horizontal (n + 1)) = tb` by rw_tac std_ss[triplet_def, leibniz_horizontal_el] >> - `EL (k + 1) (leibniz_horizontal (n + 1)) = tc` by rw_tac std_ss[triplet_def, leibniz_horizontal_el] >> - `k < n + 1` by decide_tac >> - `k < LENGTH (leibniz_horizontal (n + 1))` by rw[leibniz_horizontal_len] >> - `TAKE (k + 1) (leibniz_horizontal (n + 1)) = TAKE (SUC k) (leibniz_horizontal (n + 1))` by rw[ADD1] >> - `_ = SNOC tb x` by rw[TAKE_SUC_BY_TAKE, Abbr`x`] >> - `_ = x ++ [tb]` by rw[] >> - `SUC k < n + 2` by decide_tac >> - `SUC k < LENGTH (leibniz_horizontal (n + 1))` by rw[leibniz_horizontal_len] >> - `TAKE (k + 2) (leibniz_horizontal (n + 1)) = TAKE (SUC (SUC k)) (leibniz_horizontal (n + 1))` by rw[ADD1] >> - `_ = SNOC tc (SNOC tb x)` by rw_tac std_ss[TAKE_SUC_BY_TAKE, ADD1, Abbr`x`] >> - `_ = x ++ [tb; tc]` by rw[] >> - `DROP k (leibniz_horizontal n) = [ta] ++ y` by rw[DROP_BY_DROP_SUC, ADD1, Abbr`y`] >> - qabbrev_tac `p1 = TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n)` >> - qabbrev_tac `p2 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ y` >> - `p1 = x ++ [tb; ta] ++ y` by rw[Abbr`p1`, Abbr`x`, Abbr`y`] >> - `p2 = x ++ [tb; tc] ++ y` by rw[Abbr`p2`, Abbr`x`] >> - metis_tac[leibniz_zigzag_def]); - -(* Theorem: (leibniz_up 1) zigzag (leibniz_horizontal 1) *) -(* Proof: - Since leibniz_up 1 - = [2; 1] by EVAL_TAC - = [] ++ [2; 1] ++ [] by EVAL_TAC - and leibniz_horizontal 1 - = [2; 2] by EVAL_TAC - = [] ++ [2; 2] ++ [] by EVAL_TAC - Now the first Leibniz triplet is: - (triplet 0 0).a = 1 by EVAL_TAC - (triplet 0 0).b = 2 by EVAL_TAC - (triplet 0 0).c = 2 by EVAL_TAC - Hence (leibniz_up 1) zigzag (leibniz_horizontal 1) by leibniz_zigzag_def -*) -val leibniz_triplet_0 = store_thm( - "leibniz_triplet_0", - ``(leibniz_up 1) zigzag (leibniz_horizontal 1)``, - `leibniz_up 1 = [] ++ [2; 1] ++ []` by EVAL_TAC >> - `leibniz_horizontal 1 = [] ++ [2; 2] ++ []` by EVAL_TAC >> - `((triplet 0 0).a = 1) /\ ((triplet 0 0).b = 2) /\ ((triplet 0 0).c = 2)` by EVAL_TAC >> - metis_tac[leibniz_zigzag_def]); - -(* ------------------------------------------------------------------------- *) -(* Wriggle Paths in Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* Define paths reachable by many zigzags *) -(* -val leibniz_wriggle_def = Define` - leibniz_wriggle (p1: path) (p2: path) <=> - ?(m:num) (f:num -> path). - (p1 = f 0) /\ - (p2 = f m) /\ - (!k. k < m ==> (f k) zigzag (f (SUC k))) -`; -*) - -(* Define paths reachable by many zigzags by closure *) -val _ = overload_on("wriggle", ``RTC leibniz_zigzag``); (* RTC = reflexive transitive closure *) -val _ = set_fixity "wriggle" (Infix(NONASSOC, 450)); (* same as relation *) - -(* Theorem: p1 wriggle p2 ==> (list_lcm p1 = list_lcm p2) *) -(* Proof: - By RTC_STRONG_INDUCT. - Base: list_lcm p1 = list_lcm p1, trivially true. - Step: p1 zigzag p1' /\ p1' wriggle p2 /\ list_lcm p1' = list_lcm p2 ==> list_lcm p1 = list_lcm p2 - list_lcm p1 - = list_lcm p1' by list_lcm_zigzag - = list_lcm p2 by induction hypothesis -*) -val list_lcm_wriggle = store_thm( - "list_lcm_wriggle", - ``!p1 p2. p1 wriggle p2 ==> (list_lcm p1 = list_lcm p2)``, - ho_match_mp_tac RTC_STRONG_INDUCT >> - rpt strip_tac >- - rw[] >> - metis_tac[list_lcm_zigzag]); - -(* Theorem: p1 zigzag p2 ==> p1 wriggle p2 *) -(* Proof: - p1 wriggle p2 - = p1 (RTC zigzag) p2 by notation - = p1 zigzag p2 by RTC_SINGLE -*) -val leibniz_zigzag_wriggle = store_thm( - "leibniz_zigzag_wriggle", - ``!p1 p2. p1 zigzag p2 ==> p1 wriggle p2``, - rw[]); - -(* Theorem: p1 wriggle p2 ==> !x. ([x] ++ p1) wriggle ([x] ++ p2) *) -(* Proof: - By RTC_STRONG_INDUCT. - Base: [x] ++ p1 wriggle [x] ++ p1 - True by RTC_REFL. - Step: p1 zigzag p1' /\ p1' wriggle p2 /\ !x. [x] ++ p1' wriggle [x] ++ p2 ==> - [x] ++ p1 wriggle [x] ++ p2 - Since p1 zigzag p1', - so [x] ++ p1 zigzag [x] ++ p1' by leibniz_zigzag_tail - or [x] ++ p1 wriggle [x] ++ p1' by leibniz_zigzag_wriggle - With [x] ++ p1' wriggle [x] ++ p2 by induction hypothesis - Hence [x] ++ p1 wriggle [x] ++ p2 by RTC_TRANS -*) -val leibniz_wriggle_tail = store_thm( - "leibniz_wriggle_tail", - ``!p1 p2. p1 wriggle p2 ==> !x. ([x] ++ p1) wriggle ([x] ++ p2)``, - ho_match_mp_tac RTC_STRONG_INDUCT >> - rpt strip_tac >- - rw[] >> - metis_tac[leibniz_zigzag_tail, leibniz_zigzag_wriggle, RTC_TRANS]); - -(* Theorem: p1 wriggle p1 *) -(* Proof: by RTC_REFL *) -val leibniz_wriggle_refl = store_thm( - "leibniz_wriggle_refl", - ``!p1. p1 wriggle p1``, - metis_tac[RTC_REFL]); - -(* Theorem: p1 wriggle p2 /\ p2 wriggle p3 ==> p1 wriggle p3 *) -(* Proof: by RTC_TRANS *) -val leibniz_wriggle_trans = store_thm( - "leibniz_wriggle_trans", - ``!p1 p2 p3. p1 wriggle p2 /\ p2 wriggle p3 ==> p1 wriggle p3``, - metis_tac[RTC_TRANS]); - -(* Theorem: k <= n + 1 ==> - TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle - leibniz_horizontal (n + 1) *) -(* Proof: - By induction on the difference: n + 1 - k. - Base: k = n + 1 ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle - leibniz_horizontal (n + 1) - TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) - = TAKE (n + 2) (leibniz_horizontal (n + 1)) ++ DROP (n + 1) (leibniz_horizontal n) by k = n + 1 - = leibniz_horizontal (n + 1) ++ [] by TAKE_LENGTH_ID, DROP_LENGTH_NIL - = leibniz_horizontal (n + 1) by APPEND_NIL - Hence they wriggle to each other by RTC_REFL - Step: k <= n + 1 ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle - leibniz_horizontal (n + 1) - Let p1 = leibniz_horizontal (n + 1) - p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_horizontal n) - p3 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) - Then p2 zigzag p3 by leibniz_horizontal_zigzag - and p3 wriggle p1 by induction hypothesis - Hence p2 wriggle p1 by RTC_RULES -*) -val leibniz_horizontal_wriggle_step = store_thm( - "leibniz_horizontal_wriggle_step", - ``!n k. k <= n + 1 ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle - leibniz_horizontal (n + 1)``, - Induct_on `n + 1 - k` >| [ - rpt strip_tac >> - rw_tac arith_ss[] >> - `n + 1 = k` by decide_tac >> - rw[TAKE_LENGTH_ID_rwt, DROP_LENGTH_NIL_rwt], - rpt strip_tac >> - `v = n - k` by decide_tac >> - `v = (n + 1) - (k + 1)` by decide_tac >> - `k <= n` by decide_tac >> - `k + 1 <= n + 1` by decide_tac >> - `k + 1 + 1 = k + 2` by decide_tac >> - qabbrev_tac `p1 = leibniz_horizontal (n + 1)` >> - qabbrev_tac `p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_horizontal n)` >> - qabbrev_tac `p3 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n)` >> - `p2 zigzag p3` by rw[leibniz_horizontal_zigzag, Abbr`p1`, Abbr`p2`, Abbr`p3`] >> - metis_tac[RTC_RULES] - ]); - -(* Theorem: ([leibniz (n + 1) 0] ++ leibniz_horizontal n) wriggle leibniz_horizontal (n + 1) *) -(* Proof: - Apply > leibniz_horizontal_wriggle_step |> SPEC ``n:num`` |> SPEC ``0`` |> SIMP_RULE std_ss[DROP_0]; - val it = |- TAKE 1 (leibniz_horizontal (n + 1)) ++ leibniz_horizontal n wriggle leibniz_horizontal (n + 1): thm -*) -val leibniz_horizontal_wriggle = store_thm( - "leibniz_horizontal_wriggle", - ``!n. ([leibniz (n + 1) 0] ++ leibniz_horizontal n) wriggle leibniz_horizontal (n + 1)``, - rpt strip_tac >> - `TAKE 1 (leibniz_horizontal (n + 1)) = [leibniz (n + 1) 0]` by rw[leibniz_horizontal_head, binomial_n_0] >> - metis_tac[leibniz_horizontal_wriggle_step |> SPEC ``n:num`` |> SPEC ``0`` |> SIMP_RULE std_ss[DROP_0]]); - -(* ------------------------------------------------------------------------- *) -(* Path Transform keeping LCM *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: (leibniz_up n) wriggle (leibniz_horizontal n) *) -(* Proof: - By induction on n. - Base: leibniz_up 0 wriggle leibniz_horizontal 0 - Since leibniz_up 0 = [1] by leibniz_up_0 - and leibniz_horizontal 0 = [1] by leibniz_horizontal_0 - Hence leibniz_up 0 wriggle leibniz_horizontal 0 by leibniz_wriggle_refl - Step: leibniz_up n wriggle leibniz_horizontal n ==> - leibniz_up (SUC n) wriggle leibniz_horizontal (SUC n) - Let x = leibniz (n + 1) 0. - Then x = n + 2 by leibniz_n_0 - Now leibniz_up (n + 1) = [x] ++ (leibniz_up n) by leibniz_up_cons - Since leibniz_up n wriggle leibniz_horizontal n by induction hypothesis - so ([x] ++ (leibniz_up n)) wriggle - ([x] ++ (leibniz_horizontal n)) by leibniz_wriggle_tail - and ([x] ++ (leibniz_horizontal n)) wriggle - (leibniz_horizontal (n + 1)) by leibniz_horizontal_wriggle - Hence leibniz_up (SUC n) wriggle - leibniz_horizontal (SUC n) by leibniz_wriggle_trans, ADD1 -*) -val leibniz_up_wriggle_horizontal = store_thm( - "leibniz_up_wriggle_horizontal", - ``!n. (leibniz_up n) wriggle (leibniz_horizontal n)``, - Induct >- - rw[leibniz_up_0, leibniz_horizontal_0] >> - qabbrev_tac `x = leibniz (n + 1) 0` >> - `x = n + 2` by rw[leibniz_n_0, Abbr`x`] >> - `leibniz_up (n + 1) = [x] ++ (leibniz_up n)` by rw[leibniz_up_cons, Abbr`x`] >> - `([x] ++ (leibniz_up n)) wriggle ([x] ++ (leibniz_horizontal n))` by rw[leibniz_wriggle_tail] >> - `([x] ++ (leibniz_horizontal n)) wriggle (leibniz_horizontal (n + 1))` by rw[leibniz_horizontal_wriggle, Abbr`x`] >> - metis_tac[leibniz_wriggle_trans, ADD1]); - -(* Theorem: list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n) *) -(* Proof: - Since leibniz_up n = REVERSE (leibniz_vertical n) by notation - and leibniz_up n wriggle leibniz_horizontal n by leibniz_up_wriggle_horizontal - list_lcm (leibniz_vertical n) - = list_lcm (leibniz_up n) by list_lcm_reverse - = list_lcm (leibniz_horizontal n) by list_lcm_wriggle -*) -val leibniz_lcm_property = store_thm( - "leibniz_lcm_property", - ``!n. list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n)``, - metis_tac[leibniz_up_wriggle_horizontal, list_lcm_wriggle, list_lcm_reverse]); - -(* This is a milestone theorem. *) - -(* Theorem: k <= n ==> (leibniz n k) divides list_lcm (leibniz_vertical n) *) -(* Proof: - Note (leibniz n k) divides list_lcm (leibniz_horizontal n) by leibniz_horizontal_divisor - ==> (leibniz n k) divides list_lcm (leibniz_vertical n) by leibniz_lcm_property -*) -val leibniz_vertical_divisor = store_thm( - "leibniz_vertical_divisor", - ``!n k. k <= n ==> (leibniz n k) divides list_lcm (leibniz_vertical n)``, - metis_tac[leibniz_horizontal_divisor, leibniz_lcm_property]); - -(* ------------------------------------------------------------------------- *) -(* Lower Bound of Leibniz LCM *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 2 ** n <= list_lcm (leibniz_horizontal n) *) -(* Proof: - Note LENGTH (binomail_horizontal n) = n + 1 by binomial_horizontal_len - and EVERY_POSITIVE (binomial_horizontal n) by binomial_horizontal_pos .. [1] - list_lcm (leibniz_horizontal n) - = (n + 1) * list_lcm (binomial_horizontal n) by leibniz_horizontal_lcm_alt - >= SUM (binomial_horizontal n) by list_lcm_lower_bound, [1] - = 2 ** n by binomial_horizontal_sum -*) -val leibniz_horizontal_lcm_lower = store_thm( - "leibniz_horizontal_lcm_lower", - ``!n. 2 ** n <= list_lcm (leibniz_horizontal n)``, - rpt strip_tac >> - `LENGTH (binomial_horizontal n) = n + 1` by rw[binomial_horizontal_len] >> - `EVERY_POSITIVE (binomial_horizontal n)` by rw[binomial_horizontal_pos] >> - `list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n)` by rw[leibniz_horizontal_lcm_alt] >> - `SUM (binomial_horizontal n) = 2 ** n` by rw[binomial_horizontal_sum] >> - metis_tac[list_lcm_lower_bound]); - -(* Theorem: 2 ** n <= list_lcm (leibniz_vertical n) *) -(* Proof: - list_lcm (leibniz_vertical n) - = list_lcm (leibniz_horizontal n) by leibniz_lcm_property - >= 2 ** n by leibniz_horizontal_lcm_lower -*) -val leibniz_vertical_lcm_lower = store_thm( - "leibniz_vertical_lcm_lower", - ``!n. 2 ** n <= list_lcm (leibniz_vertical n)``, - rw_tac std_ss[leibniz_horizontal_lcm_lower, leibniz_lcm_property]); - -(* Theorem: 2 ** n <= list_lcm [1 .. (n + 1)] *) -(* Proof: by leibniz_vertical_lcm_lower. *) -val lcm_lower_bound = store_thm( - "lcm_lower_bound", - ``!n. 2 ** n <= list_lcm [1 .. (n + 1)]``, - rw[leibniz_vertical_lcm_lower]); - -(* ------------------------------------------------------------------------- *) -(* Leibniz LCM Invariance *) -(* ------------------------------------------------------------------------- *) - -(* Use overloading for leibniz_col_arm rooted at leibniz a b, of length n. *) -val _ = overload_on("leibniz_col_arm", ``\a b n. MAP (\x. leibniz (a - x) b) [0 ..< n]``); - -(* Use overloading for leibniz_seg_arm rooted at leibniz a b, of length n. *) -val _ = overload_on("leibniz_seg_arm", ``\a b n. MAP (\x. leibniz a (b + x)) [0 ..< n]``); - -(* -> EVAL ``leibniz_col_arm 5 1 4``; -val it = |- leibniz_col_arm 5 1 4 = [30; 20; 12; 6]: thm -> EVAL ``leibniz_seg_arm 5 1 4``; -val it = |- leibniz_seg_arm 5 1 4 = [30; 60; 60; 30]: thm -> EVAL ``list_lcm (leibniz_col_arm 5 1 4)``; -val it = |- list_lcm (leibniz_col_arm 5 1 4) = 60: thm -> EVAL ``list_lcm (leibniz_seg_arm 5 1 4)``; -val it = |- list_lcm (leibniz_seg_arm 5 1 4) = 60: thm -*) - -(* Theorem: leibniz_col_arm a b 0 = [] *) -(* Proof: - leibniz_col_arm a b 0 - = MAP (\x. leibniz (a - x) b) [0 ..< 0] by notation - = MAP (\x. leibniz (a - x) b) [] by listRangeLHI_def - = [] by MAP -*) -val leibniz_col_arm_0 = store_thm( - "leibniz_col_arm_0", - ``!a b. leibniz_col_arm a b 0 = []``, - rw[]); - -(* Theorem: leibniz_seg_arm a b 0 = [] *) -(* Proof: - leibniz_seg_arm a b 0 - = MAP (\x. leibniz a (b + x)) [0 ..< 0] by notation - = MAP (\x. leibniz a (b + x)) [] by listRangeLHI_def - = [] by MAP -*) -val leibniz_seg_arm_0 = store_thm( - "leibniz_seg_arm_0", - ``!a b. leibniz_seg_arm a b 0 = []``, - rw[]); - -(* Theorem: leibniz_col_arm a b 1 = [leibniz a b] *) -(* Proof: - leibniz_col_arm a b 1 - = MAP (\x. leibniz (a - x) b) [0 ..< 1] by notation - = MAP (\x. leibniz (a - x) b) [0] by listRangeLHI_def - = (\x. leibniz (a - x) b) 0 ::[] by MAP - = [leibniz a b] by function application -*) -val leibniz_col_arm_1 = store_thm( - "leibniz_col_arm_1", - ``!a b. leibniz_col_arm a b 1 = [leibniz a b]``, - rw[listRangeLHI_def]); - -(* Theorem: leibniz_seg_arm a b 1 = [leibniz a b] *) -(* Proof: - leibniz_seg_arm a b 1 - = MAP (\x. leibniz a (b + x)) [0 ..< 1] by notation - = MAP (\x. leibniz a (b + x)) [0] by listRangeLHI_def - = (\x. leibniz a (b + x)) 0 :: [] by MAP - = [leibniz a b] by function application -*) -val leibniz_seg_arm_1 = store_thm( - "leibniz_seg_arm_1", - ``!a b. leibniz_seg_arm a b 1 = [leibniz a b]``, - rw[listRangeLHI_def]); - -(* Theorem: LENGTH (leibniz_col_arm a b n) = n *) -(* Proof: - LENGTH (leibniz_col_arm a b n) - = LENGTH (MAP (\x. leibniz (a - x) b) [0 ..< n]) by notation - = LENGTH [0 ..< n] by LENGTH_MAP - = LENGTH (GENLIST (\i. i) n) by listRangeLHI_def - = m by LENGTH_GENLIST -*) -val leibniz_col_arm_len = store_thm( - "leibniz_col_arm_len", - ``!a b n. LENGTH (leibniz_col_arm a b n) = n``, - rw[]); - -(* Theorem: LENGTH (leibniz_seg_arm a b n) = n *) -(* Proof: - LENGTH (leibniz_seg_arm a b n) - = LENGTH (MAP (\x. leibniz a (b + x)) [0 ..< n]) by notation - = LENGTH [0 ..< n] by LENGTH_MAP - = LENGTH (GENLIST (\i. i) n) by listRangeLHI_def - = m by LENGTH_GENLIST -*) -val leibniz_seg_arm_len = store_thm( - "leibniz_seg_arm_len", - ``!a b n. LENGTH (leibniz_seg_arm a b n) = n``, - rw[]); - -(* Theorem: k < n ==> !a b. EL k (leibniz_col_arm a b n) = leibniz (a - k) b *) -(* Proof: - Note LENGTH [0 ..< n] = n by LENGTH_listRangeLHI - EL k (leibniz_col_arm a b n) - = EL k (MAP (\x. leibniz (a - x) b) [0 ..< n]) by notation - = (\x. leibniz (a - x) b) (EL k [0 ..< n]) by EL_MAP - = (\x. leibniz (a - x) b) k by EL_listRangeLHI - = leibniz (a - k) b -*) -val leibniz_col_arm_el = store_thm( - "leibniz_col_arm_el", - ``!n k. k < n ==> !a b. EL k (leibniz_col_arm a b n) = leibniz (a - k) b``, - rw[EL_MAP, EL_listRangeLHI]); - -(* Theorem: k < n ==> !a b. EL k (leibniz_seg_arm a b n) = leibniz a (b + k) *) -(* Proof: - Note LENGTH [0 ..< n] = n by LENGTH_listRangeLHI - EL k (leibniz_seg_arm a b n) - = EL k (MAP (\x. leibniz a (b + x)) [0 ..< n]) by notation - = (\x. leibniz a (b + x)) (EL k [0 ..< n]) by EL_MAP - = (\x. leibniz a (b + x)) k by EL_listRangeLHI - = leibniz a (b + k) -*) -val leibniz_seg_arm_el = store_thm( - "leibniz_seg_arm_el", - ``!n k. k < n ==> !a b. EL k (leibniz_seg_arm a b n) = leibniz a (b + k)``, - rw[EL_MAP, EL_listRangeLHI]); - -(* Theorem: TAKE 1 (leibniz_seg_arm a b (n + 1)) = [leibniz a b] *) -(* Proof: - Note LENGTH (leibniz_seg_arm a b (n + 1)) = n + 1 by leibniz_seg_arm_len - and 0 < n + 1 by ADD1, SUC_POS - TAKE 1 (leibniz_seg_arm a b (n + 1)) - = TAKE (SUC 0) (leibniz_seg_arm a b (n + 1)) by ONE - = SNOC (EL 0 (leibniz_seg_arm a b (n + 1))) [] by TAKE_SUC_BY_TAKE, TAKE_0 - = [EL 0 (leibniz_seg_arm a b (n + 1))] by SNOC_NIL - = leibniz a b by leibniz_seg_arm_el -*) -val leibniz_seg_arm_head = store_thm( - "leibniz_seg_arm_head", - ``!a b n. TAKE 1 (leibniz_seg_arm a b (n + 1)) = [leibniz a b]``, - metis_tac[leibniz_seg_arm_len, leibniz_seg_arm_el, - ONE, TAKE_SUC_BY_TAKE, TAKE_0, SNOC_NIL, DECIDE``!n. 0 < n + 1 /\ (n + 0 = n)``]); - -(* Theorem: leibniz_col_arm (a + 1) b (n + 1) = leibniz (a + 1) b :: leibniz_col_arm a b n *) -(* Proof: - Note (\x. leibniz (a + 1 - x) b) o SUC - = (\x. leibniz (a + 1 - (x + 1)) b) by FUN_EQ_THM - = (\x. leibniz (a - x) b) by arithmetic - - leibniz_col_arm (a + 1) b (n + 1) - = MAP (\x. leibniz (a + 1 - x) b) [0 ..< (n + 1)] by notation - = MAP (\x. leibniz (a + 1 - x) b) (0::[1 ..< (n+1)]) by listRangeLHI_CONS, 0 < n + 1 - = (\x. leibniz (a + 1 - x) b) 0 :: MAP (\x. leibniz (a + 1 - x) b) [1 ..< (n+1)] by MAP - = leibniz (a + 1) b :: MAP (\x. leibniz (a + 1 - x) b) [1 ..< (n+1)] by function application - = leibniz (a + 1) b :: MAP ((\x. leibniz (a + 1 - x) b) o SUC) [0 ..< n] by listRangeLHI_MAP_SUC - = leibniz (a + 1) b :: MAP (\x. leibniz (a - x) b) [0 ..< n] by above - = leibniz (a + 1) b :: leibniz_col_arm a b n by notation -*) -val leibniz_col_arm_cons = store_thm( - "leibniz_col_arm_cons", - ``!a b n. leibniz_col_arm (a + 1) b (n + 1) = leibniz (a + 1) b :: leibniz_col_arm a b n``, - rpt strip_tac >> - `!a x. a + 1 - SUC x + 1 = a - x + 1` by decide_tac >> - `!a x. a + 1 - SUC x = a - x` by decide_tac >> - `(\x. leibniz (a + 1 - x) b) o SUC = (\x. leibniz (a + 1 - (x + 1)) b)` by rw[FUN_EQ_THM] >> - `0 < n + 1` by decide_tac >> - `leibniz_col_arm (a + 1) b (n + 1) = MAP (\x. leibniz (a + 1 - x) b) (0::[1 ..< (n+1)])` by rw[listRangeLHI_CONS] >> - `_ = leibniz (a + 1) b :: MAP (\x. leibniz (a + 1 - x) b) [0+1 ..< (n+1)]` by rw[] >> - `_ = leibniz (a + 1) b :: MAP ((\x. leibniz (a + 1 - x) b) o SUC) [0 ..< n]` by rw[listRangeLHI_MAP_SUC] >> - `_ = leibniz (a + 1) b :: leibniz_col_arm a b n` by rw[] >> - rw[]); - -(* Theorem: k < n ==> !a b. - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) zigzag - TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) *) -(* Proof: - Since k <= n, k < n + 1, and k + 1 < n + 2. - Hence k < LENGTH (leibniz_seg_arm a b (n + 1)), - - Let x = TAKE k (leibniz_seg_arm a b (n + 1)) - and y = DROP (k + 1) (leibniz_seg_arm a b n) - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) - = TAKE (SUC k) (leibniz_seg_arm (a + 1) b (n + 1)) by ADD1 - = SNOC t.b x by TAKE_SUC_BY_TAKE, k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1)) - = x ++ [t.b] by SNOC_APPEND - TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) - = TAKE (SUC (SUC k)) (leibniz_seg_arm (a + 1) b (SUC n)) by ADD1 - = SNOC t.c (SNOC t.b x) by TAKE_SUC_BY_TAKE, SUC k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1)) - = x ++ [t.b; t.c] by SNOC_APPEND - DROP k (leibniz_seg_arm a b n) - = t.a :: y by DROP_BY_DROP_SUC, k < LENGTH (leibniz_seg_arm a b n) - = [t.a] ++ y by CONS_APPEND - Hence - Let p1 = TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) - = x ++ [t.b] ++ [t.a] ++ y - = x ++ [t.b; t.a] ++ y by APPEND - Let p2 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) - = x ++ [t.b; t.c] ++ y - Therefore p1 zigzag p2 by leibniz_zigzag_def -*) -val leibniz_seg_arm_zigzag_step = store_thm( - "leibniz_seg_arm_zigzag_step", - ``!n k. k < n ==> !a b. - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) zigzag - TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n)``, - rpt strip_tac >> - qabbrev_tac `x = TAKE k (leibniz_seg_arm (a + 1) b (n + 1))` >> - qabbrev_tac `y = DROP (k + 1) (leibniz_seg_arm a b n)` >> - qabbrev_tac `t = triplet a (b + k)` >> - `k < n + 1 /\ k + 1 < n + 1` by decide_tac >> - `EL k (leibniz_seg_arm a b n) = t.a` by rw[triplet_def, leibniz_seg_arm_el, Abbr`t`] >> - `EL k (leibniz_seg_arm (a + 1) b (n + 1)) = t.b` by rw[triplet_def, leibniz_seg_arm_el, Abbr`t`] >> - `EL (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) = t.c` by rw[triplet_def, leibniz_seg_arm_el, Abbr`t`] >> - `k < LENGTH (leibniz_seg_arm a b (n + 1))` by rw[leibniz_seg_arm_len] >> - `TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) = TAKE (SUC k) (leibniz_seg_arm (a + 1) b (n + 1))` by rw[ADD1] >> - `_ = SNOC t.b x` by rw[TAKE_SUC_BY_TAKE, Abbr`x`] >> - `_ = x ++ [t.b]` by rw[] >> - `SUC k < n + 1` by decide_tac >> - `SUC k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1))` by rw[leibniz_seg_arm_len] >> - `k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1))` by decide_tac >> - `TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) = TAKE (SUC (SUC k)) (leibniz_seg_arm (a + 1) b (n + 1))` by rw[ADD1] >> - `_ = SNOC t.c (SNOC t.b x)` by metis_tac[TAKE_SUC_BY_TAKE, ADD1] >> - `_ = x ++ [t.b; t.c]` by rw[] >> - `DROP k (leibniz_seg_arm a b n) = [t.a] ++ y` by rw[DROP_BY_DROP_SUC, ADD1, Abbr`y`] >> - qabbrev_tac `p1 = TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n)` >> - qabbrev_tac `p2 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ y` >> - `p1 = x ++ [t.b; t.a] ++ y` by rw[Abbr`p1`, Abbr`x`, Abbr`y`] >> - `p2 = x ++ [t.b; t.c] ++ y` by rw[Abbr`p2`, Abbr`x`] >> - metis_tac[leibniz_zigzag_def]); - -(* Theorem: k < n + 1 ==> !a b. - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle - leibniz_seg_arm (a + 1) b (n + 1) *) -(* Proof: - By induction on the difference: n - k. - Base: k = n ==> TAKE (k + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle - leibniz_seg_arm a b (n + 1) - Note LENGTH (leibniz_seg_arm (a + 1) b (n + 1)) = n + 1 by leibniz_seg_arm_len - and LENGTH (leibniz_seg_arm a b n) = n by leibniz_seg_arm_len - TAKE (k + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) - = TAKE (n + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP n (leibniz_seg_arm a b n) by k = n - = leibniz_seg_arm a b n ++ [] by TAKE_LENGTH_ID, DROP_LENGTH_NIL - = leibniz_seg_arm a b n by APPEND_NIL - Hence they wriggle to each other by RTC_REFL - Step: k < n + 1 ==> TAKE (k + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle - leibniz_seg_arm a b (n + 1) - Let p1 = leibniz_seg_arm (a + 1) b (n + 1) - p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_seg_arm a b n) - p3 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) - Then p2 zigzag p3 by leibniz_seg_arm_zigzag_step - and p3 wriggle p1 by induction hypothesis - Hence p2 wriggle p1 by RTC_RULES -*) -val leibniz_seg_arm_wriggle_step = store_thm( - "leibniz_seg_arm_wriggle_step", - ``!n k. k < n + 1 ==> !a b. - TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle - leibniz_seg_arm (a + 1) b (n + 1)``, - Induct_on `n - k` >| [ - rpt strip_tac >> - `k = n` by decide_tac >> - metis_tac[leibniz_seg_arm_len, TAKE_LENGTH_ID, DROP_LENGTH_NIL, APPEND_NIL, RTC_REFL], - rpt strip_tac >> - qabbrev_tac `p1 = leibniz_seg_arm (a + 1) b (n + 1)` >> - qabbrev_tac `p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_seg_arm a b n)` >> - qabbrev_tac `p3 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n)` >> - `p2 zigzag p3` by rw[leibniz_seg_arm_zigzag_step, Abbr`p1`, Abbr`p2`, Abbr`p3`] >> - `v = n - (k + 1)` by decide_tac >> - `k + 1 < n + 1` by decide_tac >> - `k + 1 + 1 = k + 2` by decide_tac >> - metis_tac[RTC_RULES] - ]); - -(* Theorem: ([leibniz (a + 1) b] ++ leibniz_seg_arm a b n) wriggle leibniz_seg_arm (a + 1) b (n + 1) *) -(* Proof: - Apply > leibniz_seg_arm_wriggle_step |> SPEC ``n:num`` |> SPEC ``0`` |> SIMP_RULE std_ss[DROP_0]; - val it = - |- 0 < n + 1 ==> !a b. - TAKE 1 (leibniz_seg_arm (a + 1) b (n + 1)) ++ leibniz_seg_arm a b n wriggle - leibniz_seg_arm (a + 1) b (n + 1): - thm - - Note 0 < n + 1 by ADD1, SUC_POS - [leibniz (a + 1) b] ++ leibniz_seg_arm a b n - = TAKE 1 (leibniz_seg_arm (a + 1) b (n + 1)) ++ leibniz_seg_arm a b n by leibniz_seg_arm_head - = TAKE 1 (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP 0 (leibniz_seg_arm a b n) by DROP_0 - wriggle leibniz_seg_arm (a + 1) b (n + 1) by leibniz_seg_arm_wriggle_step, put k = 0 -*) -val leibniz_seg_arm_wriggle_row_arm = store_thm( - "leibniz_seg_arm_wriggle_row_arm", - ``!a b n. ([leibniz (a + 1) b] ++ leibniz_seg_arm a b n) wriggle leibniz_seg_arm (a + 1) b (n + 1)``, - rpt strip_tac >> - `0 < n + 1 /\ (0 + 1 = 1)` by decide_tac >> - metis_tac[leibniz_seg_arm_head, leibniz_seg_arm_wriggle_step, DROP_0]); - -(* Theorem: b <= a /\ n <= a + 1 - b ==> (leibniz_col_arm a b n) wriggle (leibniz_seg_arm a b n) *) -(* Proof: - By induction on n. - Base: leibniz_col_arm a b 0 wriggle leibniz_seg_arm a b 0 - Since leibniz_col_arm a b 0 = [] by leibniz_col_arm_0 - and leibniz_seg_arm a b 0 = [] by leibniz_seg_arm_0 - Hence leibniz_col_arm a b 0 wriggle leibniz_seg_arm a b 0 by leibniz_wriggle_refl - Step: !a b. leibniz_col_arm a b n wriggle leibniz_seg_arm a b n ==> - leibniz_col_arm a b (SUC n) wriggle leibniz_seg_arm a b (SUC n) - Induct_on a. - Base: b <= 0 /\ SUC n <= 0 + 1 - b ==> leibniz_col_arm 0 b (SUC n) wriggle leibniz_seg_arm 0 b (SUC n) - Note SUC n <= 1 - b ==> n = 0, since 0 <= b. - leibniz_col_arm 0 b (SUC 0) - = leibniz_col_arm 0 b 1 by ONE - = [leibniz 0 b] by leibniz_col_arm_1 - leibniz_seg_arm 0 b (SUC 0) - = leibniz_seg_arm 0 b 1 by ONE - = [leibniz 0 b] by leibniz_seg_arm_1 - Hence leibniz_col_arm 0 b 1 wriggle - leibniz_seg_arm 0 b 1 by leibniz_wriggle_refl - Step: b <= SUC a /\ SUC n <= SUC a + 1 - b ==> leibniz_col_arm (SUC a) b (SUC n) wriggle leibniz_seg_arm (SUC a) b (SUC n) - Note n <= a + 1 - b - If a + 1 = b, - Then n = 0, - leibniz_col_arm (SUC a) b (SUC 0) - = leibniz_col_arm (SUC a) b 1 by ONE - = [leibniz (SUC a) b] by leibniz_col_arm_1 - = leibniz_seg_arm (SUC a) b 1 by leibniz_seg_arm_1 - = leibniz_seg_arm (SUC a) b (SUC 0) by ONE - Hence leibniz_col_arm (SUC a) b 1 wriggle - leibniz_seg_arm (SUC a) b 1 by leibniz_wriggle_refl - If a + 1 <> b, - Then b <= a, and induction hypothesis applies. - Let x = leibniz (a + 1) b. - Then leibniz_col_arm (a + 1) b (n + 1) - = [x] ++ (leibniz_col_arm a b n) by leibniz_col_arm_cons - Since leibniz_col_arm a b n - wriggle leibniz_seg_arm a b n by induction hypothesis - so ([x] ++ (leibniz_col_arm a b n)) wriggle - ([x] ++ (leibniz_seg_arm a b n)) by leibniz_wriggle_tail - and ([x] ++ (leibniz_seg_arm a b n)) wriggle - (leibniz_seg_arm (a + 1) b (n + 1)) by leibniz_seg_arm_wriggle_row_arm - Hence leibniz_col_arm a b (SUC n) wriggle - leibniz_seg_arm a b (SUC n) by leibniz_wriggle_trans, ADD1 -*) -val leibniz_col_arm_wriggle_row_arm = store_thm( - "leibniz_col_arm_wriggle_row_arm", - ``!a b n. b <= a /\ n <= a + 1 - b ==> (leibniz_col_arm a b n) wriggle (leibniz_seg_arm a b n)``, - Induct_on `n` >- - rw[leibniz_col_arm_0, leibniz_seg_arm_0] >> - rpt strip_tac >> - Induct_on `a` >| [ - rpt strip_tac >> - `n = 0` by decide_tac >> - metis_tac[leibniz_col_arm_1, leibniz_seg_arm_1, ONE, leibniz_wriggle_refl], - rpt strip_tac >> - `n <= a + 1 - b` by decide_tac >> - Cases_on `a + 1 = b` >| [ - `n = 0` by decide_tac >> - metis_tac[leibniz_col_arm_1, leibniz_seg_arm_1, ONE, leibniz_wriggle_refl], - `b <= a` by decide_tac >> - qabbrev_tac `x = leibniz (a + 1) b` >> - `leibniz_col_arm (a + 1) b (n + 1) = [x] ++ (leibniz_col_arm a b n)` by rw[leibniz_col_arm_cons, Abbr`x`] >> - `([x] ++ (leibniz_col_arm a b n)) wriggle ([x] ++ (leibniz_seg_arm a b n))` by rw[leibniz_wriggle_tail] >> - `([x] ++ (leibniz_seg_arm a b n)) wriggle (leibniz_seg_arm (a + 1) b (n + 1))` by rw[leibniz_seg_arm_wriggle_row_arm, Abbr`x`] >> - metis_tac[leibniz_wriggle_trans, ADD1] - ] - ]); - -(* Theorem: b <= a /\ n <= a + 1 - b ==> (list_lcm (leibniz_col_arm a b n) = list_lcm (leibniz_seg_arm a b n)) *) -(* Proof: - Since (leibniz_col_arm a b n) wriggle (leibniz_seg_arm a b n) by leibniz_col_arm_wriggle_row_arm - the result follows by list_lcm_wriggle -*) -val leibniz_lcm_invariance = store_thm( - "leibniz_lcm_invariance", - ``!a b n. b <= a /\ n <= a + 1 - b ==> (list_lcm (leibniz_col_arm a b n) = list_lcm (leibniz_seg_arm a b n))``, - rw[leibniz_col_arm_wriggle_row_arm, list_lcm_wriggle]); - -(* This is a milestone theorem. *) - -(* This is used to give another proof of leibniz_up_wriggle_horizontal *) - -(* Theorem: leibniz_col_arm n 0 (n + 1) = leibniz_up n *) -(* Proof: - leibniz_col_arm n 0 (n + 1) - = MAP (\x. leibniz (n - x) 0) [0 ..< (n + 1)] by notation - = MAP (\x. leibniz (n - x) 0) [0 .. n] by listRangeLHI_to_INC - = MAP ((\x. leibniz x 0) o (\x. n - x)) [0 .. n] by function composition - = REVERSE (MAP (\x. leibniz x 0) [0 .. n]) by listRangeINC_REVERSE_MAP - = REVERSE (MAP (\x. x + 1) [0 .. n]) by leibniz_n_0 - = REVERSE (MAP SUC [0 .. n]) by ADD1 - = REVERSE (MAP (I o SUC) [0 .. n]) by I_THM - = REVERSE [1 .. (n+1)] by listRangeINC_MAP_SUC - = REVERSE (leibniz_vertical n) by notation - = leibniz_up n by notation -*) -val leibniz_col_arm_n_0 = store_thm( - "leibniz_col_arm_n_0", - ``!n. leibniz_col_arm n 0 (n + 1) = leibniz_up n``, - rpt strip_tac >> - `(\x. x + 1) = SUC` by rw[FUN_EQ_THM] >> - `(\x. leibniz x 0) o (\x. n - x + 0) = (\x. leibniz (n - x) 0)` by rw[FUN_EQ_THM] >> - `leibniz_col_arm n 0 (n + 1) = MAP (\x. leibniz (n - x) 0) [0 .. n]` by rw[listRangeLHI_to_INC] >> - `_ = MAP ((\x. leibniz x 0) o (\x. n - x + 0)) [0 .. n]` by rw[] >> - `_ = REVERSE (MAP (\x. leibniz x 0) [0 .. n])` by rw[listRangeINC_REVERSE_MAP] >> - `_ = REVERSE (MAP (\x. x + 1) [0 .. n])` by rw[leibniz_n_0] >> - `_ = REVERSE (MAP SUC [0 .. n])` by rw[ADD1] >> - `_ = REVERSE (MAP (I o SUC) [0 .. n])` by rw[] >> - `_ = REVERSE [1 .. (n+1)]` by rw[GSYM listRangeINC_MAP_SUC] >> - rw[]); - -(* Theorem: leibniz_seg_arm n 0 (n + 1) = leibniz_horizontal n *) -(* Proof: - leibniz_seg_arm n 0 (n + 1) - = MAP (\x. leibniz n x) [0 ..< (n + 1)] by notation - = MAP (\x. leibniz n x) [0 .. n] by listRangeLHI_to_INC - = MAP (leibniz n) [0 .. n] by FUN_EQ_THM - = MAP (leibniz n) (GENLIST (\i. i) (n + 1)) by listRangeINC_def - = GENLIST ((leibniz n) o I) (n + 1) by MAP_GENLIST - = GENLIST (leibniz n) (n + 1) by I_THM - = leibniz_horizontal n by notation -*) -val leibniz_seg_arm_n_0 = store_thm( - "leibniz_seg_arm_n_0", - ``!n. leibniz_seg_arm n 0 (n + 1) = leibniz_horizontal n``, - rpt strip_tac >> - `(\x. x) = I` by rw[FUN_EQ_THM] >> - `(\x. leibniz n x) = leibniz n` by rw[FUN_EQ_THM] >> - `leibniz_seg_arm n 0 (n + 1) = MAP (leibniz n) [0 .. n]` by rw_tac std_ss[listRangeLHI_to_INC] >> - `_ = MAP (leibniz n) (GENLIST (\i. i) (n + 1))` by rw[listRangeINC_def] >> - `_ = MAP (leibniz n) (GENLIST I (n + 1))` by metis_tac[] >> - `_ = GENLIST ((leibniz n) o I) (n + 1)` by rw[MAP_GENLIST] >> - `_ = GENLIST (leibniz n) (n + 1)` by rw[] >> - rw[]); - -(* Theorem: (leibniz_up n) wriggle (leibniz_horizontal n) *) -(* Proof: - Note 0 <= n /\ n + 1 <= n + 1 - 0, so leibniz_col_arm_wriggle_row_arm applies. - leibniz_up n - = leibniz_col_arm n 0 (n + 1) by leibniz_col_arm_n_0 - wriggle leibniz_seg_arm n 0 (n + 1) by leibniz_col_arm_wriggle_row_arm - = leibniz_horizontal n by leibniz_seg_arm_n_0 -*) -val leibniz_up_wriggle_horizontal_alt = store_thm( - "leibniz_up_wriggle_horizontal_alt", - ``!n. (leibniz_up n) wriggle (leibniz_horizontal n)``, - rpt strip_tac >> - `0 <= n /\ n + 1 <= n + 1 - 0` by decide_tac >> - metis_tac[leibniz_col_arm_wriggle_row_arm, leibniz_col_arm_n_0, leibniz_seg_arm_n_0]); - -(* Theorem: list_lcm (leibniz_up n) = list_lcm (leibniz_horizontal n) *) -(* Proof: by leibniz_up_wriggle_horizontal_alt, list_lcm_wriggle *) -val leibniz_up_lcm_eq_horizontal_lcm = store_thm( - "leibniz_up_lcm_eq_horizontal_lcm", - ``!n. list_lcm (leibniz_up n) = list_lcm (leibniz_horizontal n)``, - rw[leibniz_up_wriggle_horizontal_alt, list_lcm_wriggle]); - -(* This is another proof of the milestone theorem. *) - -(* ------------------------------------------------------------------------- *) -(* Set GCD as Big Operator *) -(* ------------------------------------------------------------------------- *) - -(* Big Operators: -SUM_IMAGE_DEF |- !f s. SIGMA f s = ITSET (\e acc. f e + acc) s 0: thm -PROD_IMAGE_DEF |- !f s. PI f s = ITSET (\e acc. f e * acc) s 1: thm -*) - -(* Define big_gcd for a set *) -val big_gcd_def = Define` - big_gcd s = ITSET gcd s 0 -`; - -(* Theorem: big_gcd {} = 0 *) -(* Proof: - big_gcd {} - = ITSET gcd {} 0 by big_gcd_def - = 0 by ITSET_EMPTY -*) -val big_gcd_empty = store_thm( - "big_gcd_empty", - ``big_gcd {} = 0``, - rw[big_gcd_def, ITSET_EMPTY]); - -(* Theorem: big_gcd {x} = x *) -(* Proof: - big_gcd {x} - = ITSET gcd {x} 0 by big_gcd_def - = gcd x 0 by ITSET_SING - = x by GCD_0R -*) -val big_gcd_sing = store_thm( - "big_gcd_sing", - ``!x. big_gcd {x} = x``, - rw[big_gcd_def, ITSET_SING]); - -(* Theorem: FINITE s /\ x NOTIN s ==> (big_gcd (x INSERT s) = gcd x (big_gcd s)) *) -(* Proof: - Note big_gcd s = ITSET gcd s 0 by big_lcm_def - Since !x y z. gcd x (gcd y z) = gcd y (gcd x z) by GCD_ASSOC_COMM - The result follows by ITSET_REDUCTION -*) -val big_gcd_reduction = store_thm( - "big_gcd_reduction", - ``!s x. FINITE s /\ x NOTIN s ==> (big_gcd (x INSERT s) = gcd x (big_gcd s))``, - rw[big_gcd_def, ITSET_REDUCTION, GCD_ASSOC_COMM]); - -(* Theorem: FINITE s ==> !x. x IN s ==> (big_gcd s) divides x *) -(* Proof: - By finite induction on s. - Base: x IN {} ==> big_gcd {} divides x - True since x IN {} = F by MEMBER_NOT_EMPTY - Step: !x. x IN s ==> big_gcd s divides x ==> - e NOTIN s /\ x IN (e INSERT s) ==> big_gcd (e INSERT s) divides x - Since e NOTIN s, - so big_gcd (e INSERT s) = gcd e (big_gcd s) by big_gcd_reduction - By IN_INSERT, - If x = e, - to show: gcd e (big_gcd s) divides e, true by GCD_IS_GREATEST_COMMON_DIVISOR - If x <> e, x IN s, - to show gcd e (big_gcd s) divides x, - Since (big_gcd s) divides x by induction hypothesis, x IN s - and (big_gcd s) divides gcd e (big_gcd s) by GCD_IS_GREATEST_COMMON_DIVISOR - so gcd e (big_gcd s) divides x by DIVIDES_TRANS -*) -val big_gcd_is_common_divisor = store_thm( - "big_gcd_is_common_divisor", - ``!s. FINITE s ==> !x. x IN s ==> (big_gcd s) divides x``, - Induct_on `FINITE` >> - rpt strip_tac >- - metis_tac[MEMBER_NOT_EMPTY] >> - metis_tac[big_gcd_reduction, IN_INSERT, GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_TRANS]); - -(* Theorem: FINITE s ==> !m. (!x. x IN s ==> m divides x) ==> m divides (big_gcd s) *) -(* Proof: - By finite induction on s. - Base: m divides big_gcd {} - Since big_gcd {} = 0 by big_gcd_empty - Hence true by ALL_DIVIDES_0 - Step: !m. (!x. x IN s ==> m divides x) ==> m divides big_gcd s ==> - e NOTIN s /\ !x. x IN e INSERT s ==> m divides x ==> m divides big_gcd (e INSERT s) - Note x IN e INSERT s ==> x = e \/ x IN s by IN_INSERT - Put x = e, then m divides e by x divides m, x = e - Put x IN s, then m divides big_gcd s by induction hypothesis - Therefore, m divides gcd e (big_gcd s) by GCD_IS_GREATEST_COMMON_DIVISOR - or m divides big_gcd (e INSERT s) by big_gcd_reduction, e NOTIN s -*) -val big_gcd_is_greatest_common_divisor = store_thm( - "big_gcd_is_greatest_common_divisor", - ``!s. FINITE s ==> !m. (!x. x IN s ==> m divides x) ==> m divides (big_gcd s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[big_gcd_empty] >> - metis_tac[big_gcd_reduction, GCD_IS_GREATEST_COMMON_DIVISOR, IN_INSERT]); - -(* Theorem: FINITE s ==> !x. big_gcd (x INSERT s) = gcd x (big_gcd s) *) -(* Proof: - If x IN s, - Then (big_gcd s) divides x by big_gcd_is_common_divisor - gcd x (big_gcd s) - = gcd (big_gcd s) x by GCD_SYM - = big_gcd s by divides_iff_gcd_fix - = big_gcd (x INSERT s) by ABSORPTION - If x NOTIN s, result is true by big_gcd_reduction -*) -val big_gcd_insert = store_thm( - "big_gcd_insert", - ``!s. FINITE s ==> !x. big_gcd (x INSERT s) = gcd x (big_gcd s)``, - rpt strip_tac >> - Cases_on `x IN s` >- - metis_tac[big_gcd_is_common_divisor, divides_iff_gcd_fix, ABSORPTION, GCD_SYM] >> - rw[big_gcd_reduction]); - -(* Theorem: big_gcd {x; y} = gcd x y *) -(* Proof: - big_gcd {x; y} - = big_gcd (x INSERT y) by notation - = gcd x (big_gcd {y}) by big_gcd_insert - = gcd x (big_gcd {y INSERT {}}) by notation - = gcd x (gcd y (big_gcd {})) by big_gcd_insert - = gcd x (gcd y 0) by big_gcd_empty - = gcd x y by gcd_0R -*) -val big_gcd_two = store_thm( - "big_gcd_two", - ``!x y. big_gcd {x; y} = gcd x y``, - rw[big_gcd_insert, big_gcd_empty]); - -(* Theorem: FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s *) -(* Proof: - By finite induction on s. - Base: {} <> {} /\ !x. x IN {} ==> 0 < x ==> 0 < big_gcd {} - True since {} <> {} = F - Step: s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s ==> - e NOTIN s /\ e INSERT s <> {} /\ !x. x IN e INSERT s ==> 0 < x ==> 0 < big_gcd (e INSERT s) - Note 0 < e /\ !x. x IN s ==> 0 < x by IN_INSERT - If s = {}, - big_gcd (e INSERT {}) - = big_gcd {e} by IN_INSERT - = e > 0 by big_gcd_sing - If s <> {}, - so 0 < big_gcd s by induction hypothesis - ==> 0 < gcd e (big_gcd s) by GCD_EQ_0 - or 0 < big_gcd (e INSERT s) by big_gcd_insert -*) -val big_gcd_positive = store_thm( - "big_gcd_positive", - ``!s. FINITE s /\ s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s``, - `!s. FINITE s ==> s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s` suffices_by rw[] >> - Induct_on `FINITE` >> - rpt strip_tac >- - rw[] >> - `0 < e /\ (!x. x IN s ==> 0 < x)` by rw[] >> - Cases_on `s = {}` >- - rw[big_gcd_sing] >> - metis_tac[big_gcd_insert, GCD_EQ_0, NOT_ZERO_LT_ZERO]); - -(* Theorem: FINITE s /\ s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s *) -(* Proof: - By finite induction on s. - Base: {} <> {} ==> ..., must be true. - Step: s <> {} ==> !!k. big_gcd (IMAGE ($* k) s) = k * big_gcd s ==> - e NOTIN s ==> big_gcd (IMAGE ($* k) (e INSERT s)) = k * big_gcd (e INSERT s) - If s = {}, - big_gcd (IMAGE ($* k) (e INSERT {})) - = big_gcd (IMAGE ($* k) {e}) by IN_INSERT, s = {} - = big_gcd {k * e} by IMAGE_SING - = k * e by big_gcd_sing - = k * big_gcd {e} by big_gcd_sing - = k * big_gcd (e INSERT {}) by IN_INSERT, s = {} - If s <> {}, - big_gcd (IMAGE ($* k) (e INSERT s)) - = big_gcd ((k * e) INSERT (IMAGE ($* k) s)) by IMAGE_INSERT - = gcd (k * e) (big_gcd (IMAGE ($* k) s)) by big_gcd_insert - = gcd (k * e) (k * big_gcd s) by induction hypothesis - = k * gcd e (big_gcd s) by GCD_COMMON_FACTOR - = k * big_gcd (e INSERT s) by big_gcd_insert -*) -val big_gcd_map_times = store_thm( - "big_gcd_map_times", - ``!s. FINITE s /\ s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s``, - `!s. FINITE s ==> s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s` suffices_by rw[] >> - Induct_on `FINITE` >> - rpt strip_tac >- - rw[] >> - Cases_on `s = {}` >- - rw[big_gcd_sing] >> - `big_gcd (IMAGE ($* k) (e INSERT s)) = gcd (k * e) (k * big_gcd s)` by rw[big_gcd_insert] >> - `_ = k * gcd e (big_gcd s)` by rw[GCD_COMMON_FACTOR] >> - `_ = k * big_gcd (e INSERT s)` by rw[big_gcd_insert] >> - rw[]); - -(* ------------------------------------------------------------------------- *) -(* Set LCM as Big Operator *) -(* ------------------------------------------------------------------------- *) - -(* big_lcm s = ITSET (\e x. lcm e x) s 1 = ITSET lcm s 1, of course! *) -val big_lcm_def = Define` - big_lcm s = ITSET lcm s 1 -`; - -(* Theorem: big_lcm {} = 1 *) -(* Proof: - big_lcm {} - = ITSET lcm {} 1 by big_lcm_def - = 1 by ITSET_EMPTY -*) -val big_lcm_empty = store_thm( - "big_lcm_empty", - ``big_lcm {} = 1``, - rw[big_lcm_def, ITSET_EMPTY]); - -(* Theorem: big_lcm {x} = x *) -(* Proof: - big_lcm {x} - = ITSET lcm {x} 1 by big_lcm_def - = lcm x 1 by ITSET_SING - = x by LCM_1 -*) -val big_lcm_sing = store_thm( - "big_lcm_sing", - ``!x. big_lcm {x} = x``, - rw[big_lcm_def, ITSET_SING]); - -(* Theorem: FINITE s /\ x NOTIN s ==> (big_lcm (x INSERT s) = lcm x (big_lcm s)) *) -(* Proof: - Note big_lcm s = ITSET lcm s 1 by big_lcm_def - Since !x y z. lcm x (lcm y z) = lcm y (lcm x z) by LCM_ASSOC_COMM - The result follows by ITSET_REDUCTION -*) -val big_lcm_reduction = store_thm( - "big_lcm_reduction", - ``!s x. FINITE s /\ x NOTIN s ==> (big_lcm (x INSERT s) = lcm x (big_lcm s))``, - rw[big_lcm_def, ITSET_REDUCTION, LCM_ASSOC_COMM]); - -(* Theorem: FINITE s ==> !x. x IN s ==> x divides (big_lcm s) *) -(* Proof: - By finite induction on s. - Base: x IN {} ==> x divides big_lcm {} - True since x IN {} = F by MEMBER_NOT_EMPTY - Step: !x. x IN s ==> x divides big_lcm s ==> - e NOTIN s /\ x IN (e INSERT s) ==> x divides big_lcm (e INSERT s) - Since e NOTIN s, - so big_lcm (e INSERT s) = lcm e (big_lcm s) by big_lcm_reduction - By IN_INSERT, - If x = e, - to show: e divides lcm e (big_lcm s), true by LCM_DIVISORS - If x <> e, x IN s, - to show x divides lcm e (big_lcm s), - Since x divides (big_lcm s) by induction hypothesis, x IN s - and (big_lcm s) divides lcm e (big_lcm s) by LCM_DIVISORS - so x divides lcm e (big_lcm s) by DIVIDES_TRANS -*) -val big_lcm_is_common_multiple = store_thm( - "big_lcm_is_common_multiple", - ``!s. FINITE s ==> !x. x IN s ==> x divides (big_lcm s)``, - Induct_on `FINITE` >> - rpt strip_tac >- - metis_tac[MEMBER_NOT_EMPTY] >> - metis_tac[big_lcm_reduction, IN_INSERT, LCM_DIVISORS, DIVIDES_TRANS]); - -(* Theorem: FINITE s ==> !m. (!x. x IN s ==> x divides m) ==> (big_lcm s) divides m *) -(* Proof: - By finite induction on s. - Base: big_lcm {} divides m - Since big_lcm {} = 1 by big_lcm_empty - Hence true by ONE_DIVIDES_ALL - Step: !m. (!x. x IN s ==> x divides m) ==> big_lcm s divides m ==> - e NOTIN s /\ !x. x IN e INSERT s ==> x divides m ==> big_lcm (e INSERT s) divides m - Note x IN e INSERT s ==> x = e \/ x IN s by IN_INSERT - Put x = e, then e divides m by x divides m, x = e - Put x IN s, then big_lcm s divides m by induction hypothesis - Therefore, lcm e (big_lcm s) divides m by LCM_IS_LEAST_COMMON_MULTIPLE - or big_lcm (e INSERT s) divides m by big_lcm_reduction, e NOTIN s -*) -val big_lcm_is_least_common_multiple = store_thm( - "big_lcm_is_least_common_multiple", - ``!s. FINITE s ==> !m. (!x. x IN s ==> x divides m) ==> (big_lcm s) divides m``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[big_lcm_empty] >> - metis_tac[big_lcm_reduction, LCM_IS_LEAST_COMMON_MULTIPLE, IN_INSERT]); - -(* Theorem: FINITE s ==> !x. big_lcm (x INSERT s) = lcm x (big_lcm s) *) -(* Proof: - If x IN s, - Then x divides (big_lcm s) by big_lcm_is_common_multiple - lcm x (big_lcm s) - = big_lcm s by divides_iff_lcm_fix - = big_lcm (x INSERT s) by ABSORPTION - If x NOTIN s, result is true by big_lcm_reduction -*) -val big_lcm_insert = store_thm( - "big_lcm_insert", - ``!s. FINITE s ==> !x. big_lcm (x INSERT s) = lcm x (big_lcm s)``, - rpt strip_tac >> - Cases_on `x IN s` >- - metis_tac[big_lcm_is_common_multiple, divides_iff_lcm_fix, ABSORPTION] >> - rw[big_lcm_reduction]); - -(* Theorem: big_lcm {x; y} = lcm x y *) -(* Proof: - big_lcm {x; y} - = big_lcm (x INSERT y) by notation - = lcm x (big_lcm {y}) by big_lcm_insert - = lcm x (big_lcm {y INSERT {}}) by notation - = lcm x (lcm y (big_lcm {})) by big_lcm_insert - = lcm x (lcm y 1) by big_lcm_empty - = lcm x y by LCM_1 -*) -val big_lcm_two = store_thm( - "big_lcm_two", - ``!x y. big_lcm {x; y} = lcm x y``, - rw[big_lcm_insert, big_lcm_empty]); - -(* Theorem: FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s *) -(* Proof: - By finite induction on s. - Base: !x. x IN {} ==> 0 < x ==> 0 < big_lcm {} - big_lcm {} = 1 > 0 by big_lcm_empty - Step: (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s ==> - e NOTIN s /\ !x. x IN e INSERT s ==> 0 < x ==> 0 < big_lcm (e INSERT s) - Note 0 < e /\ !x. x IN s ==> 0 < x by IN_INSERT - so 0 < big_lcm s by induction hypothesis - ==> 0 < lcm e (big_lcm s) by LCM_EQ_0 - or 0 < big_lcm (e INSERT s) by big_lcm_insert -*) -val big_lcm_positive = store_thm( - "big_lcm_positive", - ``!s. FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s``, - Induct_on `FINITE` >> - rpt strip_tac >- - rw[big_lcm_empty] >> - `0 < e /\ (!x. x IN s ==> 0 < x)` by rw[] >> - metis_tac[big_lcm_insert, LCM_EQ_0, NOT_ZERO_LT_ZERO]); - -(* Theorem: FINITE s /\ s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s *) -(* Proof: - By finite induction on s. - Base: {} <> {} ==> ..., must be true. - Step: s <> {} ==> !!k. big_lcm (IMAGE ($* k) s) = k * big_lcm s ==> - e NOTIN s ==> big_lcm (IMAGE ($* k) (e INSERT s)) = k * big_lcm (e INSERT s) - If s = {}, - big_lcm (IMAGE ($* k) (e INSERT {})) - = big_lcm (IMAGE ($* k) {e}) by IN_INSERT, s = {} - = big_lcm {k * e} by IMAGE_SING - = k * e by big_lcm_sing - = k * big_lcm {e} by big_lcm_sing - = k * big_lcm (e INSERT {}) by IN_INSERT, s = {} - If s <> {}, - big_lcm (IMAGE ($* k) (e INSERT s)) - = big_lcm ((k * e) INSERT (IMAGE ($* k) s)) by IMAGE_INSERT - = lcm (k * e) (big_lcm (IMAGE ($* k) s)) by big_lcm_insert - = lcm (k * e) (k * big_lcm s) by induction hypothesis - = k * lcm e (big_lcm s) by LCM_COMMON_FACTOR - = k * big_lcm (e INSERT s) by big_lcm_insert -*) -val big_lcm_map_times = store_thm( - "big_lcm_map_times", - ``!s. FINITE s /\ s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s``, - `!s. FINITE s ==> s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s` suffices_by rw[] >> - Induct_on `FINITE` >> - rpt strip_tac >- - rw[] >> - Cases_on `s = {}` >- - rw[big_lcm_sing] >> - `big_lcm (IMAGE ($* k) (e INSERT s)) = lcm (k * e) (k * big_lcm s)` by rw[big_lcm_insert] >> - `_ = k * lcm e (big_lcm s)` by rw[LCM_COMMON_FACTOR] >> - `_ = k * big_lcm (e INSERT s)` by rw[big_lcm_insert] >> - rw[]); - -(* ------------------------------------------------------------------------- *) -(* LCM Lower bound using big LCM *) -(* ------------------------------------------------------------------------- *) - -(* Laurent's leib.v and leib.html - -Lemma leibn_lcm_swap m n : - lcmn 'L(m.+1, n) 'L(m, n) = lcmn 'L(m.+1, n) 'L(m.+1, n.+1). -Proof. -rewrite ![lcmn 'L(m.+1, n) _]lcmnC. -by apply/lcmn_swap/leibnS. -Qed. - -Notation "\lcm_ ( i < n ) F" := - (\big[lcmn/1%N]_(i < n ) F%N) - (at level 41, F at level 41, i, n at level 50, - format "'[' \lcm_ ( i < n ) '/ ' F ']'") : nat_scope. - -Canonical Structure lcmn_moid : Monoid.law 1 := - Monoid.Law lcmnA lcm1n lcmn1. -Canonical lcmn_comoid := Monoid.ComLaw lcmnC. - -Lemma lieb_line n i k : lcmn 'L(n.+1, i) (\lcm_(j < k) 'L(n, i + j)) = - \lcm_(j < k.+1) 'L(n.+1, i + j). -Proof. -elim: k i => [i|k1 IH i]. - by rewrite big_ord_recr !big_ord0 /= lcmn1 lcm1n addn0. -rewrite big_ord_recl /= addn0. -rewrite lcmnA leibn_lcm_swap. -rewrite (eq_bigr (fun j : 'I_k1 => 'L(n, i.+1 + j))). -rewrite -lcmnA. -rewrite IH. -rewrite [RHS]big_ord_recl. -rewrite addn0; congr (lcmn _ _). -by apply: eq_bigr => j _; rewrite addnS. -move=> j _. -by rewrite addnS. -Qed. - -Lemma leib_corner n : \lcm_(i < n.+1) 'L(i, 0) = \lcm_(i < n.+1) 'L(n, i). -Proof. -elim: n => [|n IH]; first by rewrite !big_ord_recr !big_ord0 /=. -rewrite big_ord_recr /= IH lcmnC. -rewrite (eq_bigr (fun i : 'I_n.+1 => 'L(n, 0 + i))) //. -by rewrite lieb_line. -Qed. - -Lemma main_result n : 2^n.-1 <= \lcm_(i < n) i.+1. -Proof. -case: n => [|n /=]; first by rewrite big_ord0. -have <-: \lcm_(i < n.+1) 'L(i, 0) = \lcm_(i < n.+1) i.+1. - by apply: eq_bigr => i _; rewrite leibn0. -rewrite leib_corner. -have -> : forall j, \lcm_(i < j.+1) 'L(n, i) = n.+1 * \lcm_(i < j.+1) 'C(n, i). - elim=> [|j IH]; first by rewrite !big_ord_recr !big_ord0 /= !lcm1n. - by rewrite big_ord_recr [in RHS]big_ord_recr /= IH muln_lcmr. -rewrite (expnDn 1 1) /= (eq_bigr (fun i : 'I_n.+1 => 'C(n, i))) => - [|i _]; last by rewrite !exp1n !muln1. -have <- : forall n m, \sum_(i < n) m = n * m. - by move=> m1 n1; rewrite sum_nat_const card_ord. -apply: leq_sum => i _. -apply: dvdn_leq; last by rewrite (bigD1 i) //= dvdn_lcml. -apply big_ind => // [x y Hx Hy|x H]; first by rewrite lcmn_gt0 Hx. -by rewrite bin_gt0 -ltnS. -Qed. - -*) - -(* -Lemma lieb_line n i k : lcmn 'L(n.+1, i) (\lcm_(j < k) 'L(n, i + j)) = \lcm_(j < k.+1) 'L(n.+1, i + j). - -translates to: - !n i k. lcm (leibniz (n + 1) i) (big_lcm {leibniz n (i + j) | j | j < k}) = - big_lcm {leibniz (n+1) (i + j) | j | j < k + 1}; - -The picture is: - - n-th row: L n i L n (i+1) .... L n (i + (k-1)) -(n+1)-th row: L (n+1) i - -(n+1)-th row: L (n+1) i L (n+1) (i+1) .... L (n+1) (i + (k-1)) L (n+1) (i + k) - -If k = 1, this is: L n i transform to: - L (n+1) i L (n+1) i L (n+1) (i+1) -which is Leibniz triplet. - -In general, if true for k, then for the next (k+1) - - n-th row: L n i L n (i+1) .... L n (i + (k-1)) L n (i + k) -(n+1)-th row: L (n+1) i -= L n (i + k) -(n+1)-th row: L (n+1) i L (n+1) (i+1) .... L (n+1) (i + (k-1)) L (n+1) (i + k) -by induction hypothesis -= -(n+1)-th row: L (n+1) i L (n+1) (i+1) .... L (n+1) (i + (k-1)) L (n+1) (i + k) L (n+1) (i + (k+1)) -by Leibniz triplet. - -*) - -(* Introduce a segment, a partial horizontal row, in Leibniz Denominator Triangle *) -val _ = overload_on("leibniz_seg", ``\n k h. IMAGE (\j. leibniz n (k + j)) (count h)``); -(* This is a segment starting at leibniz n k, of length h *) - -(* Introduce a horizontal row in Leibniz Denominator Triangle *) -val _ = overload_on("leibniz_row", ``\n h. IMAGE (leibniz n) (count h)``); -(* This is a row starting at leibniz n 0, of length h *) - -(* Introduce a vertical column in Leibniz Denominator Triangle *) -val _ = overload_on("leibniz_col", ``\h. IMAGE (\i. leibniz i 0) (count h)``); -(* This is a column starting at leibniz 0 0, descending for a length h *) - -(* Representations of paths based on indexed sets *) - -(* Theorem: leibniz_seg n k h = {leibniz n (k + j) | j | j IN (count h)} *) -(* Proof: by notation *) -val leibniz_seg_def = store_thm( - "leibniz_seg_def", - ``!n k h. leibniz_seg n k h = {leibniz n (k + j) | j | j IN (count h)}``, - rw[EXTENSION]); - -(* Theorem: leibniz_row n h = {leibniz n j | j | j IN (count h)} *) -(* Proof: by notation *) -val leibniz_row_def = store_thm( - "leibniz_row_def", - ``!n h. leibniz_row n h = {leibniz n j | j | j IN (count h)}``, - rw[EXTENSION]); - -(* Theorem: leibniz_col h = {leibniz j 0 | j | j IN (count h)} *) -(* Proof: by notation *) -val leibniz_col_def = store_thm( - "leibniz_col_def", - ``!h. leibniz_col h = {leibniz j 0 | j | j IN (count h)}``, - rw[EXTENSION]); - -(* Theorem: leibniz_col n = natural n *) -(* Proof: - leibniz_col n - = IMAGE (\i. leibniz i 0) (count n) by notation - = IMAGE (\i. i + 1) (count n) by leibniz_n_0 - = IMAGE (\i. SUC i) (count n) by ADD1 - = IMAGE SUC (count n) by FUN_EQ_THM - = natural n by notation -*) -val leibniz_col_eq_natural = store_thm( - "leibniz_col_eq_natural", - ``!n. leibniz_col n = natural n``, - rw[leibniz_n_0, ADD1, FUN_EQ_THM]); - -(* The following can be taken as a generalisation of the Leibniz Triplet LCM exchange. *) -(* When length h = 1, the top row is a singleton, and the next row is a duplet, altogether a triplet. *) - -(* Theorem: lcm (leibniz (n + 1) k) (big_lcm (leibniz_seg n k h)) = big_lcm (leibniz_seg (n + 1) k (h + 1)) *) -(* Proof: - Let p = (\j. leibniz n (k + j)), q = (\j. leibniz (n + 1) (k + j)). - Note q 0 = (leibniz (n + 1) k) by function application [1] - The goal is: lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count h))) = big_lcm (IMAGE q (count (h + 1))) - - By induction on h, length of the row. - Base case: lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count 0))) = big_lcm (IMAGE q (count (0 + 1))) - lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count 0))) - = lcm (q 0) (big_lcm (IMAGE p (count 0))) by [1] - = lcm (q 0) (big_lcm (IMAGE p {})) by COUNT_ZERO - = lcm (q 0) (big_lcm {}) by IMAGE_EMPTY - = lcm (q 0) 1 by big_lcm_empty - = q 0 by LCM_1 - = big_lcm {q 0} by big_lcm_sing - = big_lcm (IMAEG q {0}) by IMAGE_SING - = big_lcm (IMAGE q (count 1)) by count_def, EXTENSION - - Step case: lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count h))) = big_lcm (IMAGE q (count (h + 1))) ==> - lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (upto h))) = big_lcm (IMAGE q (count (SUC h + 1))) - Note !n. FINITE (count n) by FINITE_COUNT - and !s. FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE - Also p h = (triplet n (k + h)).a by leibniz_triplet_member - q h = (triplet n (k + h)).b by leibniz_triplet_member - q (h + 1) = (triplet n (k + h)).c by leibniz_triplet_member - Thus lcm (q h) (p h) = lcm (q h) (q (h + 1)) by leibniz_triplet_lcm - - lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (upto h))) - = lcm (q 0) (big_lcm (IMAGE p (count (SUC h)))) by [1], notation - = lcm (q 0) (big_lcm (IMAGE p (h INSERT count h))) by upto_by_count - = lcm (q 0) (big_lcm ((p h) INSERT (IMAGE p (count h)))) by IMAGE_INSERT - = lcm (q 0) (lcm (p h) (big_lcm (IMAGE p (count h)))) by big_lcm_insert - = lcm (p h) (lcm (q 0) (big_lcm (IMAGE p (count h)))) by LCM_ASSOC_COMM - = lcm (p h) (big_lcm (IMAGE q (count (h + 1)))) by induction hypothesis - = lcm (p h) (big_lcm (IMAGE q (count (SUC h)))) by ADD1 - = lcm (p h) (big_lcm (IMAGE q (h INSERT (count h))) by upto_by_count - = lcm (p h) (big_lcm ((q h) INSERT IMAGE q (count h))) by IMAGE_INSERT - = lcm (p h) (lcm (q h) (big_lcm (IMAGE q (count h)))) by big_lcm_insert - = lcm (lcm (p h) (q h)) (big_lcm (IMAGE q (count h))) by LCM_ASSOC - = lcm (lcm (q h) (p h)) (big_lcm (IMAGE q (count h))) by LCM_COM - = lcm (lcm (q h) (q (h + 1))) (big_lcm (IMAGE q (count h))) by leibniz_triplet_lcm - = lcm (q (h + 1)) (lcm (q h) (big_lcm (IMAGE q (count h)))) by LCM_ASSOC, LCM_COMM - = lcm (q (h + 1)) (big_lcm ((q h) INSERT IMAGE q (count h))) by big_lcm_insert - = lcm (q (h + 1)) (big_lcm (IMAGE q (h INSERT count h)) by IMAGE_INSERT - = lcm (q (h + 1)) (big_lcm (IMAGE q (count (h + 1)))) by upto_by_count, ADD1 - = big_lcm ((q (h + 1)) INSERT (IMAGE q (count (h + 1)))) by big_lcm_insert - = big_lcm IMAGE q ((h + 1) INSERT (count (h + 1))) by IMAGE_INSERT - = big_lcm (IMAGE q (count (SUC (h + 1)))) by upto_by_count - = big_lcm (IMAGE q (count (SUC h + 1))) by ADD -*) -val big_lcm_seg_transform = store_thm( - "big_lcm_seg_transform", - ``!n k h. lcm (leibniz (n + 1) k) (big_lcm (leibniz_seg n k h)) = - big_lcm (leibniz_seg (n + 1) k (h + 1))``, - rpt strip_tac >> - qabbrev_tac `p = (\j. leibniz n (k + j))` >> - qabbrev_tac `q = (\j. leibniz (n + 1) (k + j))` >> - Induct_on `h` >| [ - `count 0 = {}` by rw[] >> - `count 1 = {0}` by rw[COUNT_1] >> - rw_tac std_ss[IMAGE_EMPTY, big_lcm_empty, IMAGE_SING, LCM_1, big_lcm_sing, Abbr`p`, Abbr`q`], - `leibniz (n + 1) k = q 0` by rw[Abbr`q`] >> - simp[] >> - `lcm (q h) (p h) = lcm (q h) (q (h + 1))` by - (`p h = (triplet n (k + h)).a` by rw[leibniz_triplet_member, Abbr`p`] >> - `q h = (triplet n (k + h)).b` by rw[leibniz_triplet_member, Abbr`q`] >> - `q (h + 1) = (triplet n (k + h)).c` by rw[leibniz_triplet_member, Abbr`q`] >> - rw[leibniz_triplet_lcm]) >> - `lcm (q 0) (big_lcm (IMAGE p (count (SUC h)))) = lcm (q 0) (lcm (p h) (big_lcm (IMAGE p (count h))))` by rw[upto_by_count, big_lcm_insert] >> - `_ = lcm (p h) (lcm (q 0) (big_lcm (IMAGE p (count h))))` by rw[LCM_ASSOC_COMM] >> - `_ = lcm (p h) (big_lcm (IMAGE q (count (SUC h))))` by metis_tac[ADD1] >> - `_ = lcm (p h) (lcm (q h) (big_lcm (IMAGE q (count h))))` by rw[upto_by_count, big_lcm_insert] >> - `_ = lcm (q (h + 1)) (lcm (q h) (big_lcm (IMAGE q (count h))))` by metis_tac[LCM_ASSOC, LCM_COMM] >> - `_ = lcm (q (h + 1)) (big_lcm (IMAGE q (count (SUC h))))` by rw[upto_by_count, big_lcm_insert] >> - `_ = lcm (q (h + 1)) (big_lcm (IMAGE q (count (h + 1))))` by rw[ADD1] >> - `_ = big_lcm (IMAGE q (count (SUC (h + 1))))` by rw[upto_by_count, big_lcm_insert] >> - metis_tac[ADD] - ]); - -(* Theorem: lcm (leibniz (n + 1) 0) (big_lcm (leibniz_row n h)) = big_lcm (leibniz_row (n + 1) (h + 1)) *) -(* Proof: - Note !n h. leibniz_row n h = leibniz_seg n 0 h by FUN_EQ_THM - Take k = 0 in big_lcm_seg_transform, the result follows. -*) -val big_lcm_row_transform = store_thm( - "big_lcm_row_transform", - ``!n h. lcm (leibniz (n + 1) 0) (big_lcm (leibniz_row n h)) = big_lcm (leibniz_row (n + 1) (h + 1))``, - rpt strip_tac >> - `!n h. leibniz_row n h = leibniz_seg n 0 h` by rw[FUN_EQ_THM] >> - metis_tac[big_lcm_seg_transform]); - -(* Theorem: big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1)) *) -(* Proof: - Let f = \i. leibniz i 0, then f 0 = leibniz 0 0. - By induction on n. - Base: big_lcm (leibniz_col (0 + 1)) = big_lcm (leibniz_row 0 (0 + 1)) - big_lcm (leibniz_col (0 + 1)) - = big_lcm (IMAGE f (count 1)) by notation - = big_lcm (IMAGE f) {0}) by COUNT_1 - = big_lcm {f 0} by IMAGE_SING - = big_lcm {leibniz 0 0} by f 0 - = big_lcm (IMAGE (leibniz 0) {0}) by IMAGE_SING - = big_lcm (IMAGE (leibniz 0) (count 1)) by COUNT_1 - - Step: big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1)) ==> - big_lcm (leibniz_col (SUC n + 1)) = big_lcm (leibniz_row (SUC n) (SUC n + 1)) - big_lcm (leibniz_col (SUC n + 1)) - = big_lcm (IMAGE f (count (SUC n + 1))) by notation - = big_lcm (IMAGE f (count (SUC (n + 1)))) by ADD - = big_lcm (IMAGE f ((n + 1) INSERT (count (n + 1)))) by upto_by_count - = big_lcm ((f (n + 1)) INSERT (IMAGE f (count (n + 1)))) by IMAGE_INSERT - = lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by big_lcm_insert - = lcm (f (n + 1)) (big_lcm (IMAGE (leibniz n) (count (n + 1)))) by induction hypothesis - = lcm (leibniz (n + 1) 0) (big_lcm (IMAGE (leibniz n) (count (n + 1)))) by f (n + 1) - = big_lcm (IMAGE (leibniz (n + 1)) (count (n + 1 + 1))) by big_lcm_line_transform - = big_lcm (IMAGE (leibniz (SUC n)) (count (SUC n + 1))) by ADD1 -*) -val big_lcm_corner_transform = store_thm( - "big_lcm_corner_transform", - ``!n. big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1))``, - Induct >- - rw[COUNT_1, IMAGE_SING] >> - qabbrev_tac `f = \i. leibniz i 0` >> - `big_lcm (IMAGE f (count (SUC n + 1))) = big_lcm (IMAGE f (count (SUC (n + 1))))` by rw[ADD] >> - `_ = lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[upto_by_count, big_lcm_insert] >> - `_ = lcm (leibniz (n + 1) 0) (big_lcm (IMAGE (leibniz n) (count (n + 1))))` by rw[Abbr`f`] >> - `_ = big_lcm (IMAGE (leibniz (n + 1)) (count (n + 1 + 1)))` by rw[big_lcm_row_transform] >> - `_ = big_lcm (IMAGE (leibniz (SUC n)) (count (SUC n + 1)))` by rw[ADD1] >> - rw[]); - -(* Theorem: (!x. x IN (count (n + 1)) ==> 0 < f x) ==> - SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1))) *) -(* Proof: - By induction on n. - Base: SUM (GENLIST f (0 + 1)) <= (0 + 1) * big_lcm (IMAGE f (count (0 + 1))) - LHS = SUM (GENLIST f 1) - = SUM [f 0] by GENLIST_1 - = f 0 by SUM - RHS = 1 * big_lcm (IMAGE f (count 1)) - = big_lcm (IMAGE f {0}) by COUNT_1 - = big_lcm (f 0) by IMAGE_SING - = f 0 by big_lcm_sing - Thus LHS <= RHS by arithmetic - Step: SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1))) ==> - SUM (GENLIST f (SUC n + 1)) <= (SUC n + 1) * big_lcm (IMAGE f (count (SUC n + 1))) - Note 0 < f (n + 1) by (n + 1) IN count (SUC n + 1) - and !y. y IN count (n + 1) ==> y IN count (SUC n + 1) by IN_COUNT - and !x. x IN IMAGE f (count (n + 1)) ==> 0 < x by IN_IMAGE, above - so 0 < big_lcm (IMAGE f (count (n + 1))) by big_lcm_positive - and 0 < SUC n by SUC_POS - Thus f (n + 1) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by LCM_LE - and big_lcm (IMAGE f (count (n + 1))) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by LCM_LE - - LHS = SUM (GENLIST f (SUC n + 1)) - = SUM (GENLIST f (SUC (n + 1))) by ADD - = SUM (SNOC (f (n + 1)) (GENLIST f (n + 1))) by GENLIST - = SUM (GENLIST f (n + 1)) + f (n + 1) by SUM_SNOC - RHS = (SUC n + 1) * big_lcm (IMAGE f (count (SUC n + 1))) - = (SUC n + 1) * big_lcm (IMAGE f (count (SUC (n + 1)))) by ADD - = (SUC n + 1) * big_lcm (IMAGE f ((n + 1) INSERT (count (n + 1)))) by upto_by_count - = (SUC n + 1) * big_lcm ((f (n + 1)) INSERT (IMAGE f (count (n + 1)))) by IMAGE_INSERT - = (SUC n + 1) * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by big_lcm_insert - = SUC n * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) - + 1 * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by RIGHT_ADD_DISTRIB - >= SUC n * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) by LCM_LE - = (n + 1) * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) by ADD1 - >= SUM (GENLIST f (n + 1)) + f (n + 1) by induction hypothesis - = LHS by above -*) -val big_lcm_count_lower_bound = store_thm( - "big_lcm_count_lower_bound", - ``!f n. (!x. x IN (count (n + 1)) ==> 0 < f x) ==> - SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1)))``, - rpt strip_tac >> - Induct_on `n` >| [ - rpt strip_tac >> - `SUM (GENLIST f 1) = f 0` by rw[] >> - `1 * big_lcm (IMAGE f (count 1)) = f 0` by rw[COUNT_1, big_lcm_sing] >> - rw[], - rpt strip_tac >> - `big_lcm (IMAGE f (count (SUC n + 1))) = big_lcm (IMAGE f (count (SUC (n + 1))))` by rw[ADD] >> - `_ = lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[upto_by_count, big_lcm_insert] >> - `!x. (SUC n + 1) * x = SUC n * x + x` by rw[] >> - `0 < f (n + 1)` by rw[] >> - `!y. y IN count (n + 1) ==> y IN count (SUC n + 1)` by rw[] >> - `!x. x IN IMAGE f (count (n + 1)) ==> 0 < x` by metis_tac[IN_IMAGE] >> - `0 < big_lcm (IMAGE f (count (n + 1)))` by rw[big_lcm_positive] >> - `0 < SUC n` by rw[] >> - `f (n + 1) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[LCM_LE] >> - `big_lcm (IMAGE f (count (n + 1))) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[LCM_LE] >> - `!a b c x. 0 < a /\ 0 < b /\ 0 < c /\ a <= x /\ b <= x ==> c * a + b <= c * x + x` by - (rpt strip_tac >> - `c * a <= c * x` by rw[] >> - decide_tac) >> - `SUC n * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) <= (SUC n + 1) * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by metis_tac[] >> - `SUC n * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) = (n + 1) * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1)` by rw[ADD1] >> - `SUM (GENLIST f (SUC n + 1)) = SUM (GENLIST f (SUC (n + 1)))` by rw[ADD] >> - `_ = SUM (GENLIST f (n + 1)) + f (n + 1)` by rw[GENLIST, SUM_SNOC] >> - metis_tac[LESS_EQ_TRANS, DECIDE``!a x y. 0 < a /\ x <= y ==> x + a <= y + a``] - ]); - -(* Theorem: big_lcm (natural (n + 1)) = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) *) -(* Proof: - Note SUC = \i. i + 1 by ADD1, FUN_EQ_THM - = \i. leibniz i 0 by leibniz_n_0 - and leibniz n = \j. (n + 1) * binomial n j by leibniz_def, FUN_EQ_THM - so !s. IMAGE SUC s = IMAGE (\i. leibniz i 0) s by IMAGE_CONG - and !s. IMAGE (leibniz n) s = IMAGE (\j. (n + 1) * binomial n j) s by IMAGE_CONG - also !s. IMAGE (binomial n) s = IMAGE (\j. binomial n j) s by FUN_EQ_THM, IMAGE_CONG - and count (n + 1) <> {} by COUNT_EQ_EMPTY, n + 1 <> 0 [1] - - big_lcm (IMAGE SUC (count (n + 1))) - = big_lcm (IMAGE (\i. leibniz i 0) (count (n + 1))) by above - = big_lcm (IMAGE (leibniz n) (count (n + 1))) by big_lcm_corner_transform - = big_lcm (IMAGE (\j. (n + 1) * binomial n j) (count (n + 1))) by leibniz_def - = big_lcm (IMAGE ($* (n + 1)) (IMAGE (binomial n) (count (n + 1)))) by IMAGE_COMPOSE, o_DEF - = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) by big_lcm_map_times, FINITE_COUNT, [1] -*) -val big_lcm_natural_eqn = store_thm( - "big_lcm_natural_eqn", - ``!n. big_lcm (natural (n + 1)) = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1)))``, - rpt strip_tac >> - `SUC = \i. leibniz i 0` by rw[leibniz_n_0, FUN_EQ_THM] >> - `leibniz n = \j. (n + 1) * binomial n j` by rw[leibniz_def, FUN_EQ_THM] >> - `!s. IMAGE SUC s = IMAGE (\i. leibniz i 0) s` by rw[IMAGE_CONG] >> - `!s. IMAGE (leibniz n) s = IMAGE (\j. (n + 1) * binomial n j) s` by rw[IMAGE_CONG] >> - `!s. IMAGE (binomial n) s = IMAGE (\j. binomial n j) s` by rw[FUN_EQ_THM, IMAGE_CONG] >> - `count (n + 1) <> {}` by rw[COUNT_EQ_EMPTY] >> - `big_lcm (IMAGE SUC (count (n + 1))) = big_lcm (IMAGE (leibniz n) (count (n + 1)))` by rw[GSYM big_lcm_corner_transform] >> - `_ = big_lcm (IMAGE (\j. (n + 1) * binomial n j) (count (n + 1)))` by rw[] >> - `_ = big_lcm (IMAGE ($* (n + 1)) (IMAGE (binomial n) (count (n + 1))))` by rw[GSYM IMAGE_COMPOSE, combinTheory.o_DEF] >> - `_ = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1)))` by rw[big_lcm_map_times] >> - rw[]); - -(* Theorem: 2 ** n <= big_lcm (natural (n + 1)) *) -(* Proof: - Note !x. x IN (count (n + 1)) ==> 0 < (binomial n) x by binomial_pos, IN_COUNT [1] - big_lcm (natural (n + 1)) - = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) by big_lcm_natural_eqn - >= SUM (GENLIST (binomial n) (n + 1)) by big_lcm_count_lower_bound, [1] - = SUM (GENLIST (binomial n) (SUC n)) by ADD1 - = 2 ** n by binomial_sum -*) -val big_lcm_lower_bound = store_thm( - "big_lcm_lower_bound", - ``!n. 2 ** n <= big_lcm (natural (n + 1))``, - rpt strip_tac >> - `!x. x IN (count (n + 1)) ==> 0 < (binomial n) x` by rw[binomial_pos] >> - `big_lcm (IMAGE SUC (count (n + 1))) = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1)))` by rw[big_lcm_natural_eqn] >> - `SUM (GENLIST (binomial n) (n + 1)) = 2 ** n` by rw[GSYM binomial_sum, ADD1] >> - metis_tac[big_lcm_count_lower_bound]); - -(* Another proof of the milestone theorem. *) - -(* Theorem: big_lcm (set l) = list_lcm l *) -(* Proof: - By induction on l. - Base: big_lcm (set []) = list_lcm [] - big_lcm (set []) - = big_lcm {} by LIST_TO_SET - = 1 by big_lcm_empty - = list_lcm [] by list_lcm_nil - Step: big_lcm (set l) = list_lcm l ==> !h. big_lcm (set (h::l)) = list_lcm (h::l) - Note FINITE (set l) by FINITE_LIST_TO_SET - big_lcm (set (h::l)) - = big_lcm (h INSERT set l) by LIST_TO_SET - = lcm h (big_lcm (set l)) by big_lcm_insert, FINITE (set t) - = lcm h (list_lcm l) by induction hypothesis - = list_lcm (h::l) by list_lcm_cons -*) -val big_lcm_eq_list_lcm = store_thm( - "big_lcm_eq_list_lcm", - ``!l. big_lcm (set l) = list_lcm l``, - Induct >- - rw[big_lcm_empty] >> - rw[big_lcm_insert]); - -(* ------------------------------------------------------------------------- *) -(* List LCM depends only on its set of elements *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MEM x l ==> (list_lcm (x::l) = list_lcm l) *) -(* Proof: - By induction on l. - Base: MEM x [] ==> (list_lcm [x] = list_lcm []) - True by MEM x [] = F by MEM - Step: MEM x l ==> (list_lcm (x::l) = list_lcm l) ==> - !h. MEM x (h::l) ==> (list_lcm (x::h::l) = list_lcm (h::l)) - Note MEM x (h::l) ==> (x = h) \/ (MEM x l) by MEM - If x = h, - list_lcm (h::h::l) - = lcm h (lcm h (list_lcm l)) by list_lcm_cons - = lcm (lcm h h) (list_lcm l) by LCM_ASSOC - = lcm h (list_lcm l) by LCM_REF - = list_lcm (h::l) by list_lcm_cons - If x <> h, MEM x l - list_lcm (x::h::l) - = lcm x (lcm h (list_lcm l)) by list_lcm_cons - = lcm h (lcm x (list_lcm l)) by LCM_ASSOC_COMM - = lcm h (list_lcm (x::l)) by list_lcm_cons - = lcm h (list_lcm l) by induction hypothesis, MEM x l - = list_lcm (h::l) by list_lcm_cons -*) -val list_lcm_absorption = store_thm( - "list_lcm_absorption", - ``!x l. MEM x l ==> (list_lcm (x::l) = list_lcm l)``, - rpt strip_tac >> - Induct_on `l` >- - metis_tac[MEM] >> - rw[MEM] >| [ - `lcm h (lcm h (list_lcm l)) = lcm (lcm h h) (list_lcm l)` by rw[LCM_ASSOC] >> - rw[LCM_REF], - `lcm x (lcm h (list_lcm l)) = lcm h (lcm x (list_lcm l))` by rw[LCM_ASSOC_COMM] >> - `_ = lcm h (list_lcm (x::l))` by metis_tac[list_lcm_cons] >> - rw[] - ]); - -(* Theorem: list_lcm (nub l) = list_lcm l *) -(* Proof: - By induction on l. - Base: list_lcm (nub []) = list_lcm [] - True since nub [] = [] by nub_nil - Step: list_lcm (nub l) = list_lcm l ==> !h. list_lcm (nub (h::l)) = list_lcm (h::l) - If MEM h l, - list_lcm (nub (h::l)) - = list_lcm (nub l) by nub_cons, MEM h l - = list_lcm l by induction hypothesis - = list_lcm (h::l) by list_lcm_absorption, MEM h l - If ~(MEM h l), - list_lcm (nub (h::l)) - = list_lcm (h::nub l) by nub_cons, ~(MEM h l) - = lcm h (list_lcm (nub l)) by list_lcm_cons - = lcm h (list_lcm l) by induction hypothesis - = list_lcm (h::l) by list_lcm_cons -*) -val list_lcm_nub = store_thm( - "list_lcm_nub", - ``!l. list_lcm (nub l) = list_lcm l``, - Induct >- - rw[nub_nil] >> - metis_tac[nub_cons, list_lcm_cons, list_lcm_absorption]); - -(* Theorem: (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2)) *) -(* Proof: - By induction on l1. - Base: !l2. (set [] = set l2) ==> (list_lcm (nub []) = list_lcm (nub l2)) - Note set [] = set l2 ==> l2 = [] by LIST_TO_SET_EQ_EMPTY - Hence true. - Step: !l2. (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2)) ==> - !h l2. (set (h::l1) = set l2) ==> (list_lcm (nub (h::l1)) = list_lcm (nub l2)) - If MEM h l1, - Then h IN (set l1) by notation - set (h::l1) - = h INSERT set l1 by LIST_TO_SET - = set l1 by ABSORPTION_RWT - Thus set l1 = set l2, - so list_lcm (nub (h::l1)) - = list_lcm (nub l1) by nub_cons, MEM h l1 - = list_lcm (nub l2) by induction hypothesis, set l1 = set l2 - - If ~(MEM h l1), - Then set (h::l1) = set l2 - ==> ?p1 p2. nub l2 = p1 ++ [h] ++ p2 - and set l1 = set (p1 ++ p2) by LIST_TO_SET_REDUCTION - - list_lcm (nub (h::l1)) - = list_lcm (h::nub l1) by nub_cons, ~(MEM h l1) - = lcm h (list_lcm (nub l1)) by list_lcm_cons - = lcm h (list_lcm (nub (p1 ++ p2))) by induction hypothesis - = lcm h (list_lcm (p1 ++ p2)) by list_lcm_nub - = lcm h (lcm (list_lcm p1) (list_lcm p2)) by list_lcm_append - = lcm (list_lcm p1) (lcm h (list_lcm p2)) by LCM_ASSOC_COMM - = lcm (list_lcm p1) (list_lcm (h::p2)) by list_lcm_append - = lcm (list_lcm p1) (list_lcm ([h] ++ p2)) by CONS_APPEND - = list_lcm (p1 ++ ([h] ++ p2)) by list_lcm_append - = list_lcm (p1 ++ [h] ++ p2) by APPEND_ASSOC - = list_lcm (nub l2) by above -*) -val list_lcm_nub_eq_if_set_eq = store_thm( - "list_lcm_nub_eq_if_set_eq", - ``!l1 l2. (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2))``, - Induct >- - rw[LIST_TO_SET_EQ_EMPTY] >> - rpt strip_tac >> - Cases_on `MEM h l1` >- - metis_tac[LIST_TO_SET, ABSORPTION_RWT, nub_cons] >> - `?p1 p2. (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2))` by metis_tac[LIST_TO_SET_REDUCTION] >> - `list_lcm (nub (h::l1)) = list_lcm (h::nub l1)` by rw[nub_cons] >> - `_ = lcm h (list_lcm (nub l1))` by rw[list_lcm_cons] >> - `_ = lcm h (list_lcm (nub (p1 ++ p2)))` by metis_tac[] >> - `_ = lcm h (list_lcm (p1 ++ p2))` by rw[list_lcm_nub] >> - `_ = lcm h (lcm (list_lcm p1) (list_lcm p2))` by rw[list_lcm_append] >> - `_ = lcm (list_lcm p1) (lcm h (list_lcm p2))` by rw[LCM_ASSOC_COMM] >> - `_ = lcm (list_lcm p1) (list_lcm ([h] ++ p2))` by rw[list_lcm_cons] >> - metis_tac[list_lcm_append, APPEND_ASSOC]); - -(* Theorem: (set l1 = set l2) ==> (list_lcm l1 = list_lcm l2) *) -(* Proof: - set l1 = set l2 - ==> list_lcm (nub l1) = list_lcm (nub l2) by list_lcm_nub_eq_if_set_eq - ==> list_lcm l1 = list_lcm l2 by list_lcm_nub -*) -val list_lcm_eq_if_set_eq = store_thm( - "list_lcm_eq_if_set_eq", - ``!l1 l2. (set l1 = set l2) ==> (list_lcm l1 = list_lcm l2)``, - metis_tac[list_lcm_nub_eq_if_set_eq, list_lcm_nub]); - -(* ------------------------------------------------------------------------- *) -(* Set LCM by List LCM *) -(* ------------------------------------------------------------------------- *) - -(* Define LCM of a set *) -(* none works! -val set_lcm_def = Define` - (set_lcm {} = 1) /\ - !s. FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm (s DELETE x)) -`; -val set_lcm_def = Define` - (set_lcm {} = 1) /\ - (!s. FINITE s ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s)))) -`; -val set_lcm_def = Define` - set_lcm s = if s = {} then 1 else lcm (CHOICE s) (set_lcm (REST s)) -`; -*) -val set_lcm_def = Define` - set_lcm s = list_lcm (SET_TO_LIST s) -`; - -(* Theorem: set_lcm {} = 1 *) -(* Proof: - set_lcm {} - = lcm_list (SET_TO_LIST {}) by set_lcm_def - = lcm_list [] by SET_TO_LIST_EMPTY - = 1 by list_lcm_nil -*) -val set_lcm_empty = store_thm( - "set_lcm_empty", - ``set_lcm {} = 1``, - rw[set_lcm_def]); - -(* Theorem: FINITE s /\ s <> {} ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s))) *) -(* Proof: - set_lcm s - = list_lcm (SET_TO_LIST s) by set_lcm_def - = list_lcm (CHOICE s::SET_TO_LIST (REST s)) by SET_TO_LIST_THM - = lcm (CHOICE s) (list_lcm (SET_TO_LIST (REST s))) by list_lcm_cons - = lcm (CHOICE s) (set_lcm (REST s)) by set_lcm_def -*) -val set_lcm_nonempty = store_thm( - "set_lcm_nonempty", - ``!s. FINITE s /\ s <> {} ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s)))``, - rw[set_lcm_def, SET_TO_LIST_THM, list_lcm_cons]); - -(* Theorem: set_lcm {x} = x *) -(* Proof: - set_lcm {x} - = list_lcm (SET_TO_LIST {x}) by set_lcm_def - = list_lcm [x] by SET_TO_LIST_SING - = x by list_lcm_sing -*) -val set_lcm_sing = store_thm( - "set_lcm_sing", - ``!x. set_lcm {x} = x``, - rw_tac std_ss[set_lcm_def, SET_TO_LIST_SING, list_lcm_sing]); - -(* Theorem: set_lcm (set l) = list_lcm l *) -(* Proof: - Let t = SET_TO_LIST (set l) - Note FINITE (set l) by FINITE_LIST_TO_SET - Then set t - = set (SET_TO_LIST (set l)) by notation - = set l by SET_TO_LIST_INV, FINITE (set l) - - set_lcm (set l) - = list_lcm (SET_TO_LIST (set l)) by set_lcm_def - = list_lcm t by notation - = list_lcm l by list_lcm_eq_if_set_eq, set t = set l -*) -val set_lcm_eq_list_lcm = store_thm( - "set_lcm_eq_list_lcm", - ``!l. set_lcm (set l) = list_lcm l``, - rw[FINITE_LIST_TO_SET, SET_TO_LIST_INV, set_lcm_def, list_lcm_eq_if_set_eq]); - -(* Theorem: FINITE s ==> (set_lcm s = big_lcm s) *) -(* Proof: - set_lcm s - = list_lcm (SET_TO_LIST s) by set_lcm_def - = big_lcm (set (SET_TO_LIST s)) by big_lcm_eq_list_lcm - = big_lcm s by SET_TO_LIST_INV, FINITE s -*) -val set_lcm_eq_big_lcm = store_thm( - "set_lcm_eq_big_lcm", - ``!s. FINITE s ==> (big_lcm s = set_lcm s)``, - metis_tac[set_lcm_def, big_lcm_eq_list_lcm, SET_TO_LIST_INV]); - -(* Theorem: FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm s) *) -(* Proof: by big_lcm_insert, set_lcm_eq_big_lcm *) -val set_lcm_insert = store_thm( - "set_lcm_insert", - ``!s. FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm s)``, - rw[big_lcm_insert, GSYM set_lcm_eq_big_lcm]); - -(* Theorem: FINITE s /\ x IN s ==> x divides (set_lcm s) *) -(* Proof: - Note FINITE s /\ x IN s - ==> MEM x (SET_TO_LIST s) by MEM_SET_TO_LIST - ==> x divides list_lcm (SET_TO_LIST s) by list_lcm_is_common_multiple - or x divides (set_lcm s) by set_lcm_def -*) -val set_lcm_is_common_multiple = store_thm( - "set_lcm_is_common_multiple", - ``!x s. FINITE s /\ x IN s ==> x divides (set_lcm s)``, - rw[set_lcm_def] >> - `MEM x (SET_TO_LIST s)` by rw[MEM_SET_TO_LIST] >> - rw[list_lcm_is_common_multiple]); - -(* Theorem: FINITE s /\ (!x. x IN s ==> x divides m) ==> set_lcm s divides m *) -(* Proof: - Note FINITE s - ==> !x. x IN s <=> MEM x (SET_TO_LIST s) by MEM_SET_TO_LIST - Thus list_lcm (SET_TO_LIST s) divides m by list_lcm_is_least_common_multiple - or set_lcm s divides m by set_lcm_def -*) -val set_lcm_is_least_common_multiple = store_thm( - "set_lcm_is_least_common_multiple", - ``!s m. FINITE s /\ (!x. x IN s ==> x divides m) ==> set_lcm s divides m``, - metis_tac[set_lcm_def, MEM_SET_TO_LIST, list_lcm_is_least_common_multiple]); - -(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s) *) -(* Proof: - By finite induction on s. - Base: set_lcm {} = PROD_SET {} - set_lcm {} - = 1 by set_lcm_empty - = PROD_SET {} by PROD_SET_EMPTY - Step: PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s) ==> - e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> set_lcm (e INSERT s) = PROD_SET (e INSERT s) - Note !z. z IN s ==> coprime e z by IN_INSERT - Thus coprime e (PROD_SET s) by every_coprime_prod_set_coprime - set_lcm (e INSERT s) - = lcm e (set_lcm s) by set_lcm_insert - = lcm e (PROD_SET s) by induction hypothesis - = e * (PROD_SET s) by LCM_COPRIME - = PROD_SET (e INSERT s) by PROD_SET_INSERT, e NOTIN s -*) -val pairwise_coprime_prod_set_eq_set_lcm = store_thm( - "pairwise_coprime_prod_set_eq_set_lcm", - ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s)``, - `!s. FINITE s ==> PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s)` suffices_by rw[] >> - Induct_on `FINITE` >> - rpt strip_tac >- - rw[set_lcm_empty, PROD_SET_EMPTY] >> - fs[] >> - `!z. z IN s ==> coprime e z` by metis_tac[] >> - `coprime e (PROD_SET s)` by rw[every_coprime_prod_set_coprime] >> - `set_lcm (e INSERT s) = lcm e (set_lcm s)` by rw[set_lcm_insert] >> - `_ = lcm e (PROD_SET s)` by rw[] >> - `_ = e * (PROD_SET s)` by rw[LCM_COPRIME] >> - `_ = PROD_SET (e INSERT s)` by rw[PROD_SET_INSERT] >> - rw[]); - -(* This is a generalisation of LCM_COPRIME |- !m n. coprime m n ==> (lcm m n = m * n) *) - -(* Theorem: FINITE s /\ PAIRWISE_COPRIME s /\ (!x. x IN s ==> x divides m) ==> (PROD_SET s) divides m *) -(* Proof: - Note PROD_SET s = set_lcm s by pairwise_coprime_prod_set_eq_set_lcm - and set_lcm s divides m by set_lcm_is_least_common_multiple - ==> (PROD_SET s) divides m -*) -val pairwise_coprime_prod_set_divides = store_thm( - "pairwise_coprime_prod_set_divides", - ``!s m. FINITE s /\ PAIRWISE_COPRIME s /\ (!x. x IN s ==> x divides m) ==> (PROD_SET s) divides m``, - rw[set_lcm_is_least_common_multiple, GSYM pairwise_coprime_prod_set_eq_set_lcm]); - -(* ------------------------------------------------------------------------- *) -(* Nair's Trick - using List LCM directly *) -(* ------------------------------------------------------------------------- *) - -(* Overload on consecutive LCM *) -val _ = overload_on("lcm_run", ``\n. list_lcm [1 .. n]``); - -(* Theorem: lcm_run n = FOLDL lcm 1 [1 .. n] *) -(* Proof: - lcm_run n - = list_lcm [1 .. n] by notation - = FOLDL lcm 1 [1 .. n] by list_lcm_by_FOLDL -*) -val lcm_run_by_FOLDL = store_thm( - "lcm_run_by_FOLDL", - ``!n. lcm_run n = FOLDL lcm 1 [1 .. n]``, - rw[list_lcm_by_FOLDL]); - -(* Theorem: lcm_run n = FOLDL lcm 1 [1 .. n] *) -(* Proof: - lcm_run n - = list_lcm [1 .. n] by notation - = FOLDR lcm 1 [1 .. n] by list_lcm_by_FOLDR -*) -val lcm_run_by_FOLDR = store_thm( - "lcm_run_by_FOLDR", - ``!n. lcm_run n = FOLDR lcm 1 [1 .. n]``, - rw[list_lcm_by_FOLDR]); - -(* Theorem: lcm_run 0 = 1 *) -(* Proof: - lcm_run 0 - = list_lcm [1 .. 0] by notation - = list_lcm [] by listRangeINC_EMPTY, 0 < 1 - = 1 by list_lcm_nil -*) -val lcm_run_0 = store_thm( - "lcm_run_0", - ``lcm_run 0 = 1``, - rw[listRangeINC_EMPTY]); - -(* Theorem: lcm_run 1 = 1 *) -(* Proof: - lcm_run 1 - = list_lcm [1 .. 1] by notation - = list_lcm [1] by leibniz_vertical_0 - = 1 by list_lcm_sing -*) -val lcm_run_1 = store_thm( - "lcm_run_1", - ``lcm_run 1 = 1``, - rw[leibniz_vertical_0, list_lcm_sing]); - -(* Theorem alias *) -val lcm_run_suc = save_thm("lcm_run_suc", list_lcm_suc); -(* val lcm_run_suc = |- !n. lcm_run (n + 1) = lcm (n + 1) (lcm_run n): thm *) - -(* Theorem: 0 < lcm_run n *) -(* Proof: - Note EVERY_POSITIVE [1 .. n] by listRangeINC_EVERY - so lcm_run n - = list_lcm [1 .. n] by notation - > 0 by list_lcm_pos -*) -val lcm_run_pos = store_thm( - "lcm_run_pos", - ``!n. 0 < lcm_run n``, - rw[list_lcm_pos, listRangeINC_EVERY]); - -(* Theorem: (lcm_run 2 = 2) /\ (lcm_run 3 = 6) /\ (lcm_run 4 = 12) /\ (lcm_run 5 = 60) /\ ... *) -(* Proof: by evaluation *) -val lcm_run_small = store_thm( - "lcm_run_small", - ``(lcm_run 2 = 2) /\ (lcm_run 3 = 6) /\ (lcm_run 4 = 12) /\ (lcm_run 5 = 60) /\ - (lcm_run 6 = 60) /\ (lcm_run 7 = 420) /\ (lcm_run 8 = 840) /\ (lcm_run 9 = 2520)``, - EVAL_TAC); - -(* Theorem: (n + 1) divides lcm_run (n + 1) /\ (lcm_run n) divides lcm_run (n + 1) *) -(* Proof: - If n = 0, - Then 0 + 1 = 1 by arithmetic - and lcm_run 0 = 1 by lcm_run_0 - Hence true by ONE_DIVIDES_ALL - If n <> 0, - Then n - 1 + 1 = n by arithmetic, 0 < n - lcm_run (n + 1) - = list_lcm [1 .. (n + 1)] by notation - = list_lcm (SNOC (n + 1) [1 .. n]) by leibniz_vertical_snoc - = lcm (n + 1) (list_lcm [1 .. n]) by list_lcm_snoc] - = lcm (n + 1) (lcm_run n) by notation - Hence true by LCM_DIVISORS -*) -val lcm_run_divisors = store_thm( - "lcm_run_divisors", - ``!n. (n + 1) divides lcm_run (n + 1) /\ (lcm_run n) divides lcm_run (n + 1)``, - strip_tac >> - Cases_on `n = 0` >- - rw[lcm_run_0] >> - `(n - 1 + 1 = n) /\ (n - 1 + 2 = n + 1)` by decide_tac >> - `lcm_run (n + 1) = list_lcm (SNOC (n + 1) [1 .. n])` by metis_tac[leibniz_vertical_snoc] >> - `_ = lcm (n + 1) (lcm_run n)` by rw[list_lcm_snoc] >> - rw[LCM_DIVISORS]); - -(* Theorem: lcm_run n <= lcm_run (n + 1) *) -(* Proof: - Note lcm_run n divides lcm_run (n + 1) by lcm_run_divisors - and 0 < lcm_run (n + 1) ] by lcm_run_pos - so lcm_run n <= lcm_run (n + 1) by DIVIDES_LE -*) -Theorem lcm_run_monotone[allow_rebind]: - !n. lcm_run n <= lcm_run (n + 1) -Proof rw[lcm_run_divisors, lcm_run_pos, DIVIDES_LE] -QED - -(* Theorem: 2 ** n <= lcm_run (n + 1) *) -(* Proof: - lcm_run (n + 1) - = list_lcm [1 .. (n + 1)] by notation - >= 2 ** n by lcm_lower_bound -*) -val lcm_run_lower = save_thm("lcm_run_lower", lcm_lower_bound); -(* -val lcm_run_lower = |- !n. 2 ** n <= lcm_run (n + 1): thm -*) - -(* Theorem: !n k. k <= n ==> leibniz n k divides lcm_run (n + 1) *) -(* Proof: by notation, leibniz_vertical_divisor *) -val lcm_run_leibniz_divisor = save_thm("lcm_run_leibniz_divisor", leibniz_vertical_divisor); -(* -val lcm_run_leibniz_divisor = |- !n k. k <= n ==> leibniz n k divides lcm_run (n + 1): thm -*) - -(* Theorem: n * 4 ** n <= lcm_run (2 * n + 1) *) -(* Proof: - If n = 0, LHS = 0, trivially true. - If n <> 0, 0 < n. - Let m = 2 * n. - - Claim: (m + 1) * binomial m n divides lcm_run (m + 1) [1] - Proof: Note n <= m by LESS_MONO_MULT, 1 <= 2 - ==> (leibniz m n) divides lcm_run (m + 1) by lcm_run_leibniz_divisor, n <= m - or (m + 1) * binomial m n divides lcm_run (m + 1) by leibniz_def - - Claim: n * binomial m n divides lcm_run (m + 1) [2] - Proof: Note 0 < m /\ n <= m - 1 by 0 < n - and m - 1 + 1 = m by 0 < m - Thus (leibniz (m - 1) n) divides lcm_run m by lcm_run_leibniz_divisor, n <= m - 1 - Note (lcm_run m) divides lcm_run (m + 1) by lcm_run_divisors - so (leibniz (m - 1) n) divides lcm_run (m + 1) by DIVIDES_TRANS - and leibniz (m - 1) n - = (m - n) * binomial m n by leibniz_up_alt - = n * binomial m n by m - n = n - - Note coprime n (m + 1) by GCD_EUCLID, GCD_1, 1 < n - Thus lcm (n * binomial m n) ((m + 1) * binomial m n) - = n * (m + 1) * binomial m n by LCM_COMMON_COPRIME - = n * ((m + 1) * binomial m n) by MULT_ASSOC - = n * leibniz m n by leibniz_def - ==> n * leibniz m n divides lcm_run (m + 1) by LCM_DIVIDES, [1], [2] - Note 0 < lcm_run (m + 1) by lcm_run_pos - or n * leibniz m n <= lcm_run (m + 1) by DIVIDES_LE, 0 < lcm_run (m + 1) - Now 4 ** n <= leibniz m n by leibniz_middle_lower - so n * 4 ** n <= n * leibniz m n by LESS_MONO_MULT, MULT_COMM - or n * 4 ** n <= lcm_run (m + 1) by LESS_EQ_TRANS -*) -val lcm_run_lower_odd = store_thm( - "lcm_run_lower_odd", - ``!n. n * 4 ** n <= lcm_run (2 * n + 1)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - `0 < n` by decide_tac >> - qabbrev_tac `m = 2 * n` >> - `(m + 1) * binomial m n divides lcm_run (m + 1)` by - (`n <= m` by rw[Abbr`m`] >> - metis_tac[lcm_run_leibniz_divisor, leibniz_def]) >> - `n * binomial m n divides lcm_run (m + 1)` by - (`0 < m /\ n <= m - 1` by rw[Abbr`m`] >> - `m - 1 + 1 = m` by decide_tac >> - `(leibniz (m - 1) n) divides lcm_run m` by metis_tac[lcm_run_leibniz_divisor] >> - `(lcm_run m) divides lcm_run (m + 1)` by rw[lcm_run_divisors] >> - `leibniz (m - 1) n = (m - n) * binomial m n` by rw[leibniz_up_alt] >> - `_ = n * binomial m n` by rw[Abbr`m`] >> - metis_tac[DIVIDES_TRANS]) >> - `coprime n (m + 1)` by rw[GCD_EUCLID, Abbr`m`] >> - `lcm (n * binomial m n) ((m + 1) * binomial m n) = n * (m + 1) * binomial m n` by rw[LCM_COMMON_COPRIME] >> - `_ = n * leibniz m n` by rw[leibniz_def, MULT_ASSOC] >> - `n * leibniz m n divides lcm_run (m + 1)` by metis_tac[LCM_DIVIDES] >> - `n * leibniz m n <= lcm_run (m + 1)` by rw[DIVIDES_LE, lcm_run_pos] >> - `4 ** n <= leibniz m n` by rw[leibniz_middle_lower, Abbr`m`] >> - metis_tac[LESS_MONO_MULT, MULT_COMM, LESS_EQ_TRANS]); - -(* Theorem: n * 4 ** n <= lcm_run (2 * (n + 1)) *) -(* Proof: - lcm_run (2 * (n + 1)) - = lcm_run (2 * n + 2) by arithmetic - >= lcm_run (2 * n + 1) by lcm_run_monotone - >= n * 4 ** n by lcm_run_lower_odd -*) -val lcm_run_lower_even = store_thm( - "lcm_run_lower_even", - ``!n. n * 4 ** n <= lcm_run (2 * (n + 1))``, - rpt strip_tac >> - `2 * (n + 1) = 2 * n + 1 + 1` by decide_tac >> - metis_tac[lcm_run_monotone, lcm_run_lower_odd, LESS_EQ_TRANS]); - -(* Theorem: ODD n ==> (HALF n) * HALF (2 ** n) <= lcm_run n *) -(* Proof: - Let k = HALF n. - Then n = 2 * k + 1 by ODD_HALF - and HALF (2 ** n) - = HALF (2 ** (2 * k + 1)) by above - = HALF (2 ** (SUC (2 * k))) by ADD1 - = HALF (2 * 2 ** (2 * k)) by EXP - = 2 ** (2 * k) by HALF_TWICE - = 4 ** k by EXP_EXP_MULT - Since k * 4 ** k <= lcm_run (2 * k + 1) by lcm_run_lower_odd - The result follows. -*) -val lcm_run_odd_lower = store_thm( - "lcm_run_odd_lower", - ``!n. ODD n ==> (HALF n) * HALF (2 ** n) <= lcm_run n``, - rpt strip_tac >> - qabbrev_tac `k = HALF n` >> - `n = 2 * k + 1` by rw[ODD_HALF, Abbr`k`] >> - `HALF (2 ** n) = HALF (2 ** (SUC (2 * k)))` by rw[ADD1] >> - `_ = HALF (2 * 2 ** (2 * k))` by rw[EXP] >> - `_ = 2 ** (2 * k)` by rw[HALF_TWICE] >> - `_ = 4 ** k` by rw[EXP_EXP_MULT] >> - metis_tac[lcm_run_lower_odd]); - -Theorem HALF_MULT_EVEN'[local] = ONCE_REWRITE_RULE [MULT_COMM] HALF_MULT_EVEN - -(* Theorem: EVEN n ==> HALF (n - 2) * HALF (HALF (2 ** n)) <= lcm_run n *) -(* Proof: - If n = 0, HALF (n - 2) = 0, so trivially true. - If n <> 0, - Let h = HALF n. - Then n = 2 * h by EVEN_HALF - Note h <> 0 by n <> 0 - so ?k. h = k + 1 by num_CASES, ADD1 - or n = 2 * k + 2 by n = 2 * (k + 1) - and HALF (HALF (2 ** n)) - = HALF (HALF (2 ** (2 * k + 2))) by above - = HALF (HALF (2 ** SUC (SUC (2 * k)))) by ADD1 - = HALF (HALF (2 * (2 * 2 ** (2 * k)))) by EXP - = 2 ** (2 * k) by HALF_TWICE - = 4 ** k by EXP_EXP_MULT - Also n - 2 = 2 * k by 0 < n, n = 2 * k + 2 - so HALF (n - 2) = k by HALF_TWICE - Since k * 4 ** k <= lcm_run (2 * (k + 1)) by lcm_run_lower_even - The result follows. -*) -Theorem lcm_run_even_lower: - !n. EVEN n ==> HALF (n - 2) * HALF (HALF (2 ** n)) <= lcm_run n -Proof - rpt strip_tac >> - Cases_on `n = 0` >- rw[] >> - qabbrev_tac `h = HALF n` >> - `n = 2 * h` by rw[EVEN_HALF, Abbr`h`] >> - `h <> 0` by rw[Abbr`h`] >> - `?k. h = k + 1` by metis_tac[num_CASES, ADD1] >> - `HALF (HALF (2 ** n)) = HALF (HALF (2 ** SUC (SUC (2 * k))))` by simp[ADD1] >> - `_ = HALF (HALF (2 * (2 * 2 ** (2 * k))))` by rw[EXP, HALF_MULT_EVEN'] >> - `_ = 2 ** (2 * k)` by rw[HALF_TWICE] >> - `_ = 4 ** k` by rw[EXP_EXP_MULT] >> - `n - 2 = 2 * k` by decide_tac >> - `HALF (n - 2) = k` by rw[HALF_TWICE] >> - metis_tac[lcm_run_lower_even] -QED - -(* Theorem: ODD n /\ 5 <= n ==> 2 ** n <= lcm_run n *) -(* Proof: - This follows by lcm_run_odd_lower - if we can show: 2 ** n <= HALF n * HALF (2 ** n) - - Note HALF 5 = 2 by arithmetic - and HALF 5 <= HALF n by DIV_LE_MONOTONE, 0 < 2 - Also n <> 0 by 5 <= n - so ?m. n = SUC m by num_CASES - HALF n * HALF (2 ** n) - = HALF n * HALF (2 * 2 ** m) by EXP - = HALF n * 2 ** m by HALF_TWICE - >= 2 * 2 ** m by LESS_MONO_MULT - = 2 ** (SUC m) by EXP - = 2 ** n by n = SUC m -*) -val lcm_run_odd_lower_alt = store_thm( - "lcm_run_odd_lower_alt", - ``!n. ODD n /\ 5 <= n ==> 2 ** n <= lcm_run n``, - rpt strip_tac >> - `2 ** n <= HALF n * HALF (2 ** n)` by - (`HALF 5 = 2` by EVAL_TAC >> - `HALF 5 <= HALF n` by rw[DIV_LE_MONOTONE] >> - `n <> 0` by decide_tac >> - `?m. n = SUC m` by metis_tac[num_CASES] >> - `HALF n * HALF (2 ** n) = HALF n * HALF (2 * 2 ** m)` by rw[EXP] >> - `_ = HALF n * 2 ** m` by rw[HALF_TWICE] >> - `2 * 2 ** m <= HALF n * 2 ** m` by rw[LESS_MONO_MULT] >> - rw[EXP]) >> - metis_tac[lcm_run_odd_lower, LESS_EQ_TRANS]); - -(* Theorem: EVEN n /\ 8 <= n ==> 2 ** n <= lcm_run n *) -(* Proof: - If n = 8, - Then 2 ** 8 = 256 by arithmetic - and lcm_run 8 = 840 by lcm_run_small - Thus true. - If n <> 8, - Note ODD 9 by arithmetic - so n <> 9 by ODD_EVEN - or 10 <= n by 8 <= n, n <> 9 - This follows by lcm_run_even_lower - if we can show: 2 ** n <= HALF (n - 2) * HALF (HALF (2 ** n)) - - Let m = n - 2. - Then 8 <= m by arithmetic - or HALF 8 <= HALF m by DIV_LE_MONOTONE, 0 < 2 - and HALF 8 = 4 = 2 * 2 by arithmetic - Now n = SUC (SUC m) by arithmetic - HALF m * HALF (HALF (2 ** n)) - = HALF m * HALF (HALF (2 ** (SUC (SUC m)))) by above - = HALF m * HALF (HALF (2 * (2 * 2 ** m))) by EXP - = HALF m * 2 ** m by HALF_TWICE - >= 4 * 2 ** m by LESS_MONO_MULT - = 2 * (2 * 2 ** m) by MULT_ASSOC - = 2 ** (SUC (SUC m)) by EXP - = 2 ** n by n = SUC (SUC m) -*) -Theorem lcm_run_even_lower_alt: - !n. EVEN n /\ 8 <= n ==> 2 ** n <= lcm_run n -Proof - rpt strip_tac >> - Cases_on `n = 8` >- rw[lcm_run_small] >> - `2 ** n <= HALF (n - 2) * HALF (HALF (2 ** n))` - by (`ODD 9` by rw[] >> - `n <> 9` by metis_tac[ODD_EVEN] >> - `8 <= n - 2` by decide_tac >> - qabbrev_tac `m = n - 2` >> - `n = SUC (SUC m)` by rw[Abbr`m`] >> - ‘HALF m * HALF (HALF (2 ** n)) = - HALF m * HALF (HALF (2 * (2 * 2 ** m)))’ by rw[EXP, HALF_MULT_EVEN'] >> - `_ = HALF m * 2 ** m` by rw[HALF_TWICE] >> - `HALF 8 <= HALF m` by rw[DIV_LE_MONOTONE] >> - `HALF 8 = 4` by EVAL_TAC >> - `2 * (2 * 2 ** m) <= HALF m * 2 ** m` by rw[LESS_MONO_MULT] >> - rw[EXP]) >> - metis_tac[lcm_run_even_lower, LESS_EQ_TRANS] -QED - -(* Theorem: 7 <= n ==> 2 ** n <= lcm_run n *) -(* Proof: - If EVEN n, - Node ODD 7 by arithmetic - so n <> 7 by EVEN_ODD - or 8 <= n by arithmetic - Hence true by lcm_run_even_lower_alt - If ~EVEN n, then ODD n by EVEN_ODD - Note 7 <= n ==> 5 <= n by arithmetic - Hence true by lcm_run_odd_lower_alt -*) -val lcm_run_lower_better = store_thm( - "lcm_run_lower_better", - ``!n. 7 <= n ==> 2 ** n <= lcm_run n``, - rpt strip_tac >> - `EVEN n \/ ODD n` by rw[EVEN_OR_ODD] >| [ - `ODD 7` by rw[] >> - `n <> 7` by metis_tac[ODD_EVEN] >> - rw[lcm_run_even_lower_alt], - rw[lcm_run_odd_lower_alt] - ]); - - -(* ------------------------------------------------------------------------- *) -(* Nair's Trick -- rework *) -(* ------------------------------------------------------------------------- *) - -(* -Picture: -leibniz_lcm_property |- !n. lcm_run (n + 1) = list_lcm (leibniz_horizontal n) -leibniz_horizontal_mem |- !n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) -so: -lcm_run (2*n + 1) = list_lcm (leibniz_horizontal (2*n)) -and leibniz_horizontal (2*n) has members: 0, 1, 2, ...., n, (n + 1), ....., (2*n) -note: n <= 2*n, always, (n+1) <= 2*n = (n+n) when 1 <= n. -thus: -Both B = (leibniz 2*n n) and C = (leibniz 2*n n+1) divides lcm_run (2*n + 1), - or (lcm B C) divides lcm_run (2*n + 1). -But (lcm B C) = (lcm B A) where A = (leibniz 2*n-1 n). -By leibniz_def |- !n k. leibniz n k = (n + 1) * binomial n k -By leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k - so B = (2*n + 1) * binomial 2*n n -and A = (2*n - n) * binomial 2*n n = n * binomial 2*n n -and lcm B A = lcm ((2*n + 1) * binomial 2*n n) (n * binomial 2*n n) - = (lcm (2*n + 1) n) * binomial 2*n n by LCM_COMMON_FACTOR - = n * (2*n + 1) * binomial 2*n n by coprime (2*n+1) n - = n * (leibniz 2*n n) by leibniz_def -*) - -(* Theorem: 0 < n ==> n * (leibniz (2 * n) n) divides lcm_run (2 * n + 1) *) -(* Proof: - Note 1 <= n by 0 < n - Let m = 2 * n, - Then n <= 2 * n = m, and - n + 1 <= n + n = m by arithmetic - Also coprime n (m + 1) by GCD_EUCLID - - Identify a triplet: - Let t = triplet (m - 1) n - Then t.a = leibniz (m - 1) n by triplet_def - t.b = leibniz m n by triplet_def - t.c = leibniz m (n + 1) by triplet_def - - Note MEM t.b (leibniz_horizontal m) by leibniz_horizontal_mem, n <= m - and MEM t.c (leibniz_horizontal m) by leibniz_horizontal_mem, n + 1 <= m - ==> lcm t.b t.c divides list_lcm (leibniz_horizontal m) by list_lcm_divisor_lcm_pair - = lcm_run (m + 1) by leibniz_lcm_property - - Let k = binomial m n. - lcm t.b t.c - = lcm t.b t.a by leibniz_triplet_lcm - = lcm ((m + 1) * k) t.a by leibniz_def - = lcm ((m + 1) * k) ((m - n) * k) by leibniz_up_alt - = lcm ((m + 1) * k) (n * k) by m = 2 * n - = n * (m + 1) * k by LCM_COMMON_COPRIME, LCM_SYM, coprime n (m + 1) - = n * leibniz m n by leibniz_def - Thus (n * leibniz m n) divides lcm_run (m + 1) -*) -val lcm_run_odd_factor = store_thm( - "lcm_run_odd_factor", - ``!n. 0 < n ==> n * (leibniz (2 * n) n) divides lcm_run (2 * n + 1)``, - rpt strip_tac >> - qabbrev_tac `m = 2 * n` >> - `n <= m /\ n + 1 <= m` by rw[Abbr`m`] >> - `coprime n (m + 1)` by rw[GCD_EUCLID, Abbr`m`] >> - qabbrev_tac `t = triplet (m - 1) n` >> - `t.a = leibniz (m - 1) n` by rw[triplet_def, Abbr`t`] >> - `t.b = leibniz m n` by rw[triplet_def, Abbr`t`] >> - `t.c = leibniz m (n + 1)` by rw[triplet_def, Abbr`t`] >> - `t.b divides lcm_run (m + 1)` by metis_tac[lcm_run_leibniz_divisor] >> - `t.c divides lcm_run (m + 1)` by metis_tac[lcm_run_leibniz_divisor] >> - `lcm t.b t.c divides lcm_run (m + 1)` by rw[LCM_IS_LEAST_COMMON_MULTIPLE] >> - qabbrev_tac `k = binomial m n` >> - `lcm t.b t.c = lcm t.b t.a` by rw[leibniz_triplet_lcm, Abbr`t`] >> - `_ = lcm ((m + 1) * k) ((m - n) * k)` by rw[leibniz_def, leibniz_up_alt, Abbr`k`] >> - `_ = lcm ((m + 1) * k) (n * k)` by rw[Abbr`m`] >> - `_ = n * (m + 1) * k` by rw[LCM_COMMON_COPRIME, LCM_SYM] >> - `_ = n * leibniz m n` by rw[leibniz_def, Abbr`k`] >> - metis_tac[]); - -(* Theorem: n * 4 ** n <= lcm_run (2 * n + 1) *) -(* Proof: - If n = 0, LHS = 0, trivially true. - If n <> 0, 0 < n. - Note 4 ** n <= leibniz (2 * n) n by leibniz_middle_lower - so n * 4 ** n <= n * leibniz (2 * n) n by LESS_MONO_MULT, MULT_COMM - Let k = n * leibniz (2 * n) n. - Then k divides lcm_run (2 * n + 1) by lcm_run_odd_factor - Now 0 < lcm_run (2 * n + 1) by lcm_run_pos - so k <= lcm_run (2 * n + 1) by DIVIDES_LE - Overall n * 4 ** n <= lcm_run (2 * n + 1) by LESS_EQ_TRANS -*) -Theorem lcm_run_lower_odd[allow_rebind]: - !n. n * 4 ** n <= lcm_run (2 * n + 1) -Proof - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - `0 < n` by decide_tac >> - `4 ** n <= leibniz (2 * n) n` by rw[leibniz_middle_lower] >> - `n * 4 ** n <= n * leibniz (2 * n) n` by rw[LESS_MONO_MULT, MULT_COMM] >> - `n * leibniz (2 * n) n <= lcm_run (2 * n + 1)` by rw[lcm_run_odd_factor, lcm_run_pos, DIVIDES_LE] >> - rw[LESS_EQ_TRANS] -QED - -(* Another direct proof of the same theorem *) - -(* Theorem: n * 4 ** n <= lcm_run (2 * n + 1) *) -(* Proof: - If n = 0, LHS = 0, trivially true. - If n <> 0, 0 < n, or 1 <= n by arithmetic - - Let m = 2 * n, - Then n <= 2 * n = m, and - n + 1 <= n + n = m by arithmetic, 1 <= n - Also coprime n (m + 1) by GCD_EUCLID - - Identify a triplet: - Let t = triplet (m - 1) n - Then t.a = leibniz (m - 1) n by triplet_def - t.b = leibniz m n by triplet_def - t.c = leibniz m (n + 1) by triplet_def - - Note MEM t.b (leibniz_horizontal m) by leibniz_horizontal_mem, n <= m - and MEM t.c (leibniz_horizontal m) by leibniz_horizontal_mem, n + 1 <= m - and POSITIVE (leibniz_horizontal m) by leibniz_horizontal_pos_alt - ==> lcm t.b t.c <= list_lcm (leibniz_horizontal m) by list_lcm_lower_by_lcm_pair - = lcm_run (m + 1) by leibniz_lcm_property - - Let k = binomial m n. - lcm t.b t.c - = lcm t.b t.a by leibniz_triplet_lcm - = lcm ((m + 1) * k) t.a by leibniz_def - = lcm ((m + 1) * k) ((m - n) * k) by leibniz_up_alt - = lcm ((m + 1) * k) (n * k) by m = 2 * n - = n * (m + 1) * k by LCM_COMMON_COPRIME, LCM_SYM, coprime n (m + 1) - = n * leibniz m n by leibniz_def - Thus (n * leibniz m n) divides lcm_run (m + 1) - - Note 4 ** n <= leibniz m n by leibniz_middle_lower - so n * 4 ** n <= n * leibniz m n by LESS_MONO_MULT, MULT_COMM - Overall n * 4 ** n <= lcm_run (2 * n + 1) by LESS_EQ_TRANS -*) -Theorem lcm_run_lower_odd[allow_rebind]: - !n. n * 4 ** n <= lcm_run (2 * n + 1) -Proof - rpt strip_tac >> - Cases_on ‘n = 0’ >- - rw[] >> - qabbrev_tac ‘m = 2 * n’ >> - ‘n <= m /\ n + 1 <= m’ by rw[Abbr‘m’] >> - ‘coprime n (m + 1)’ by rw[GCD_EUCLID, Abbr‘m’] >> - qabbrev_tac ‘t = triplet (m - 1) n’ >> - ‘t.a = leibniz (m - 1) n’ by rw[triplet_def, Abbr‘t’] >> - ‘t.b = leibniz m n’ by rw[triplet_def, Abbr‘t’] >> - ‘t.c = leibniz m (n + 1)’ by rw[triplet_def, Abbr‘t’] >> - ‘MEM t.b (leibniz_horizontal m)’ by metis_tac[leibniz_horizontal_mem] >> - ‘MEM t.c (leibniz_horizontal m)’ by metis_tac[leibniz_horizontal_mem] >> - ‘POSITIVE (leibniz_horizontal m)’ by metis_tac[leibniz_horizontal_pos_alt] >> - ‘lcm t.b t.c <= lcm_run (m + 1)’ by metis_tac[leibniz_lcm_property, list_lcm_lower_by_lcm_pair] >> - ‘lcm t.b t.c = n * leibniz m n’ by - (qabbrev_tac ‘k = binomial m n’ >> - ‘lcm t.b t.c = lcm t.b t.a’ by rw[leibniz_triplet_lcm, Abbr‘t’] >> - ‘_ = lcm ((m + 1) * k) ((m - n) * k)’ by rw[leibniz_def, leibniz_up_alt, Abbr‘k’] >> - ‘_ = lcm ((m + 1) * k) (n * k)’ by rw[Abbr‘m’] >> - ‘_ = n * (m + 1) * k’ by rw[LCM_COMMON_COPRIME, LCM_SYM] >> - ‘_ = n * leibniz m n’ by rw[leibniz_def, Abbr‘k’] >> - rw[]) >> - ‘4 ** n <= leibniz m n’ by rw[leibniz_middle_lower, Abbr‘m’] >> - ‘n * 4 ** n <= n * leibniz m n’ by rw[LESS_MONO_MULT] >> - metis_tac[LESS_EQ_TRANS] -QED - -(* Theorem: ODD n ==> (2 ** n <= lcm_run n <=> 5 <= n) *) -(* Proof: - If part: 2 ** n <= lcm_run n ==> 5 <= n - By contradiction, suppose n < 5. - By ODD n, n = 1 or n = 3. - If n = 1, LHS = 2 ** 1 = 2 by arithmetic - RHS = lcm_run 1 = 1 by lcm_run_1 - Hence false. - If n = 3, LHS = 2 ** 3 = 8 by arithmetic - RHS = lcm_run 3 = 6 by lcm_run_small - Hence false. - Only-if part: 5 <= n ==> 2 ** n <= lcm_run n - Let h = HALF n. - Then n = 2 * h + 1 by ODD_HALF - so 4 <= 2 * h by 5 - 1 = 4 - or 2 <= h by arithmetic - ==> 2 * 4 ** h <= h * 4 ** h by LESS_MONO_MULT - But 2 * 4 ** h - = 2 * (2 ** 2) ** h by arithmetic - = 2 * 2 ** (2 * h) by EXP_EXP_MULT - = 2 ** SUC (2 * h) by EXP - = 2 ** n by ADD1, n = 2 * h + 1 - With h * 4 ** h <= lcm_run n by lcm_run_lower_odd - or 2 ** n <= lcm_run n by LESS_EQ_TRANS -*) -val lcm_run_lower_odd_iff = store_thm( - "lcm_run_lower_odd_iff", - ``!n. ODD n ==> (2 ** n <= lcm_run n <=> 5 <= n)``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `n < 5` by decide_tac >> - `EVEN 0 /\ EVEN 2 /\ EVEN 4` by rw[] >> - `n <> 0 /\ n <> 2 /\ n <> 4` by metis_tac[EVEN_ODD] >> - `(n = 1) \/ (n = 3)` by decide_tac >- - fs[] >> - fs[lcm_run_small], - qabbrev_tac `h = HALF n` >> - `n = 2 * h + 1` by rw[ODD_HALF, Abbr`h`] >> - `2 * 4 ** h <= h * 4 ** h` by rw[] >> - `2 * 4 ** h = 2 * 2 ** (2 * h)` by rw[EXP_EXP_MULT] >> - `_ = 2 ** n` by rw[GSYM EXP] >> - `h * 4 ** h <= lcm_run n` by rw[lcm_run_lower_odd] >> - decide_tac - ]); - -(* Theorem: EVEN n ==> (2 ** n <= lcm_run n <=> (n = 0) \/ 8 <= n) *) -(* Proof: - If part: 2 ** n <= lcm_run n ==> (n = 0) \/ 8 <= n - By contradiction, suppose n <> 0 /\ n < 8. - By EVEN n, n = 2 or n = 4 or n = 6. - If n = 2, LHS = 2 ** 2 = 4 by arithmetic - RHS = lcm_run 2 = 2 by lcm_run_small - Hence false. - If n = 4, LHS = 2 ** 4 = 16 by arithmetic - RHS = lcm_run 4 = 12 by lcm_run_small - Hence false. - If n = 6, LHS = 2 ** 6 = 64 by arithmetic - RHS = lcm_run 6 = 60 by lcm_run_small - Hence false. - Only-if part: (n = 0) \/ 8 <= n ==> 2 ** n <= lcm_run n - If n = 0, LHS = 2 ** 0 = 1 by arithmetic - RHS = lcm_run 0 = 1 by lcm_run_0 - Hence true. - If n = 8, LHS = 2 ** 8 = 256 by arithmetic - RHS = lcm_run 8 = 840 by lcm_run_small - Hence true. - Otherwise, 10 <= n, since ODD 9. - Let h = HALF n, k = h - 1. - Then n = 2 * h by EVEN_HALF - = 2 * (k + 1) by k = h - 1 - = 2 * k + 2 by arithmetic - But lcm_run (2 * k + 1) <= lcm_run (2 * k + 2) by lcm_run_monotone - and k * 4 ** k <= lcm_run (2 * k + 1) by lcm_run_lower_odd - - Now 5 <= h by 10 <= h - so 4 <= k by k = h - 1 - ==> 4 * 4 ** k <= k * 4 ** k by LESS_MONO_MULT - - 4 * 4 ** k - = (2 ** 2) * (2 ** 2) ** k by arithmetic - = (2 ** 2) * (2 ** (2 * k)) by EXP_EXP_MULT - = 2 ** (2 * k + 2) by EXP_ADD - = 2 ** n by n = 2 * k + 2 - - Overall 2 ** n <= lcm_run n by LESS_EQ_TRANS -*) -val lcm_run_lower_even_iff = store_thm( - "lcm_run_lower_even_iff", - ``!n. EVEN n ==> (2 ** n <= lcm_run n <=> (n = 0) \/ 8 <= n)``, - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `n < 8` by decide_tac >> - `ODD 1 /\ ODD 3 /\ ODD 5 /\ ODD 7` by rw[] >> - `n <> 1 /\ n <> 3 /\ n <> 5 /\ n <> 7` by metis_tac[EVEN_ODD] >> - `(n = 2) \/ (n = 4) \/ (n = 6)` by decide_tac >- - fs[lcm_run_small] >- - fs[lcm_run_small] >> - fs[lcm_run_small], - fs[lcm_run_0], - Cases_on `n = 8` >- - rw[lcm_run_small] >> - `ODD 9` by rw[] >> - `n <> 9` by metis_tac[EVEN_ODD] >> - `10 <= n` by decide_tac >> - qabbrev_tac `h = HALF n` >> - `n = 2 * h` by rw[EVEN_HALF, Abbr`h`] >> - qabbrev_tac `k = h - 1` >> - `lcm_run (2 * k + 1) <= lcm_run (2 * k + 1 + 1)` by rw[lcm_run_monotone] >> - `2 * k + 1 + 1 = n` by rw[Abbr`k`] >> - `k * 4 ** k <= lcm_run (2 * k + 1)` by rw[lcm_run_lower_odd] >> - `4 * 4 ** k <= k * 4 ** k` by rw[Abbr`k`] >> - `4 * 4 ** k = 2 ** 2 * 2 ** (2 * k)` by rw[EXP_EXP_MULT] >> - `_ = 2 ** (2 * k + 2)` by rw[GSYM EXP_ADD] >> - `_ = 2 ** n` by rw[] >> - metis_tac[LESS_EQ_TRANS] - ]); - -(* Theorem: 2 ** n <= lcm_run n <=> (n = 0) \/ (n = 5) \/ 7 <= n *) -(* Proof: - If EVEN n, - Then n <> 5, n <> 7, so 8 <= n by arithmetic - Thus true by lcm_run_lower_even_iff - If ~EVEN n, then ODD n by EVEN_ODD - Then n <> 0, n <> 6, so 5 <= n by arithmetic - Thus true by lcm_run_lower_odd_iff -*) -val lcm_run_lower_better_iff = store_thm( - "lcm_run_lower_better_iff", - ``!n. 2 ** n <= lcm_run n <=> (n = 0) \/ (n = 5) \/ 7 <= n``, - rpt strip_tac >> - Cases_on `EVEN n` >| [ - `ODD 5 /\ ODD 7` by rw[] >> - `n <> 5 /\ n <> 7` by metis_tac[EVEN_ODD] >> - metis_tac[lcm_run_lower_even_iff, DECIDE``8 <= n <=> (7 <= n /\ n <> 7)``], - `EVEN 0 /\ EVEN 6` by rw[] >> - `ODD n /\ n <> 0 /\ n <> 6` by metis_tac[EVEN_ODD] >> - metis_tac[lcm_run_lower_odd_iff, DECIDE``5 <= n <=> (n = 5) \/ (n = 6) \/ (7 <= n)``] - ]); - -(* This is the ultimate goal! *) - -(* ------------------------------------------------------------------------- *) -(* Nair's Trick - using consecutive LCM *) -(* ------------------------------------------------------------------------- *) - -(* Define the consecutive LCM function *) -val lcm_upto_def = Define` - (lcm_upto 0 = 1) /\ - (lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n)) -`; - -(* Extract theorems from definition *) -val lcm_upto_0 = save_thm("lcm_upto_0", lcm_upto_def |> CONJUNCT1); -(* val lcm_upto_0 = |- lcm_upto 0 = 1: thm *) - -val lcm_upto_SUC = save_thm("lcm_upto_SUC", lcm_upto_def |> CONJUNCT2); -(* val lcm_upto_SUC = |- !n. lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n): thm *) - -(* Theorem: (lcm_upto 0 = 1) /\ (!n. lcm_upto (n+1) = lcm (n+1) (lcm_upto n)) *) -(* Proof: by lcm_upto_def *) -val lcm_upto_alt = store_thm( - "lcm_upto_alt", - ``(lcm_upto 0 = 1) /\ (!n. lcm_upto (n+1) = lcm (n+1) (lcm_upto n))``, - metis_tac[lcm_upto_def, ADD1]); - -(* Theorem: lcm_upto 1 = 1 *) -(* Proof: - lcm_upto 1 - = lcm_upto (SUC 0) by ONE - = lcm (SUC 0) (lcm_upto 0) by lcm_upto_SUC - = lcm (SUC 0) 1 by lcm_upto_0 - = lcm 1 1 by ONE - = 1 by LCM_REF -*) -val lcm_upto_1 = store_thm( - "lcm_upto_1", - ``lcm_upto 1 = 1``, - metis_tac[lcm_upto_def, LCM_REF, ONE]); - -(* Theorem: lcm_upto n for small n *) -(* Proof: by evaluation. *) -val lcm_upto_small = store_thm( - "lcm_upto_small", - ``(lcm_upto 2 = 2) /\ (lcm_upto 3 = 6) /\ (lcm_upto 4 = 12) /\ - (lcm_upto 5 = 60) /\ (lcm_upto 6 = 60) /\ (lcm_upto 7 = 420) /\ - (lcm_upto 8 = 840) /\ (lcm_upto 9 = 2520) /\ (lcm_upto 10 = 2520)``, - EVAL_TAC); - -(* Theorem: lcm_upto n = list_lcm [1 .. n] *) -(* Proof: - By induction on n. - Base: lcm_upto 0 = list_lcm [1 .. 0] - lcm_upto 0 - = 1 by lcm_upto_0 - = list_lcm [] by list_lcm_nil - = list_lcm [1 .. 0] by listRangeINC_EMPTY - Step: lcm_upto n = list_lcm [1 .. n] ==> lcm_upto (SUC n) = list_lcm [1 .. SUC n] - lcm_upto (SUC n) - = lcm (SUC n) (lcm_upto n) by lcm_upto_SUC - = lcm (SUC n) (list_lcm [1 .. n]) by induction hypothesis - = list_lcm (SNOC (SUC n) [1 .. n]) by list_lcm_snoc - = list_lcm [1 .. (SUC n)] by listRangeINC_SNOC, ADD1, 1 <= n + 1 -*) -val lcm_upto_eq_list_lcm = store_thm( - "lcm_upto_eq_list_lcm", - ``!n. lcm_upto n = list_lcm [1 .. n]``, - Induct >- - rw[lcm_upto_0, list_lcm_nil, listRangeINC_EMPTY] >> - rw[lcm_upto_SUC, list_lcm_snoc, listRangeINC_SNOC, ADD1]); - -(* Theorem: 2 ** n <= lcm_upto (n + 1) *) -(* Proof: - lcm_upto (n + 1) - = list_lcm [1 .. (n + 1)] by lcm_upto_eq_list_lcm - >= 2 ** n by lcm_lower_bound -*) -val lcm_upto_lower = store_thm( - "lcm_upto_lower", - ``!n. 2 ** n <= lcm_upto (n + 1)``, - rw[lcm_upto_eq_list_lcm, lcm_lower_bound]); - -(* Theorem: 0 < lcm_upto (n + 1) *) -(* Proof: - lcm_upto (n + 1) - >= 2 ** n by lcm_upto_lower - > 0 by EXP_POS, 0 < 2 -*) -val lcm_upto_pos = store_thm( - "lcm_upto_pos", - ``!n. 0 < lcm_upto (n + 1)``, - metis_tac[lcm_upto_lower, EXP_POS, LESS_LESS_EQ_TRANS, DECIDE``0 < 2``]); - -(* Theorem: (n + 1) divides lcm_upto (n + 1) /\ (lcm_upto n) divides lcm_upto (n + 1) *) -(* Proof: - Note lcm_upto (n + 1) = lcm (n + 1) (lcm_upto n) by lcm_upto_alt - so (n + 1) divides lcm_upto (n + 1) - and (lcm_upto n) divides lcm_upto (n + 1) by LCM_DIVISORS -*) -val lcm_upto_divisors = store_thm( - "lcm_upto_divisors", - ``!n. (n + 1) divides lcm_upto (n + 1) /\ (lcm_upto n) divides lcm_upto (n + 1)``, - rw[lcm_upto_alt, LCM_DIVISORS]); - -(* Theorem: lcm_upto n <= lcm_upto (n + 1) *) -(* Proof: - Note (lcm_upto n) divides lcm_upto (n + 1) by lcm_upto_divisors - and 0 < lcm_upto (n + 1) by lcm_upto_pos - so lcm_upto n <= lcm_upto (n + 1) by DIVIDES_LE -*) -val lcm_upto_monotone = store_thm( - "lcm_upto_monotone", - ``!n. lcm_upto n <= lcm_upto (n + 1)``, - rw[lcm_upto_divisors, lcm_upto_pos, DIVIDES_LE]); - -(* Theorem: k <= n ==> (leibniz n k) divides lcm_upto (n + 1) *) -(* Proof: - Note (leibniz n k) divides list_lcm (leibniz_vertical n) by leibniz_vertical_divisor - ==> (leibniz n k) divides list_lcm [1 .. (n + 1)] by notation - or (leibniz n k) divides lcm_upto (n + 1) by lcm_upto_eq_list_lcm -*) -val lcm_upto_leibniz_divisor = store_thm( - "lcm_upto_leibniz_divisor", - ``!n k. k <= n ==> (leibniz n k) divides lcm_upto (n + 1)``, - metis_tac[leibniz_vertical_divisor, lcm_upto_eq_list_lcm]); - -(* Theorem: n * 4 ** n <= lcm_upto (2 * n + 1) *) -(* Proof: - If n = 0, LHS = 0, trivially true. - If n <> 0, 0 < n. - Let m = 2 * n. - - Claim: (m + 1) * binomial m n divides lcm_upto (m + 1) [1] - Proof: Note n <= m by LESS_MONO_MULT, 1 <= 2 - ==> (leibniz m n) divides lcm_upto (m + 1) by lcm_upto_leibniz_divisor, n <= m - or (m + 1) * binomial m n divides lcm_upto (m + 1) by leibniz_def - - Claim: n * binomial m n divides lcm_upto (m + 1) [2] - Proof: Note (lcm_upto m) divides lcm_upto (m + 1) by lcm_upto_divisors - Also 0 < m /\ n <= m - 1 by 0 < n - and m - 1 + 1 = m by 0 < m - Thus (leibniz (m - 1) n) divides lcm_upto m by lcm_upto_leibniz_divisor, n <= m - 1 - or (leibniz (m - 1) n) divides lcm_upto (m + 1) by DIVIDES_TRANS - and leibniz (m - 1) n - = (m - n) * binomial m n by leibniz_up_alt - = n * binomial m n by m - n = n - - Note coprime n (m + 1) by GCD_EUCLID, GCD_1, 1 < n - Thus lcm (n * binomial m n) ((m + 1) * binomial m n) - = n * (m + 1) * binomial m n by LCM_COMMON_COPRIME - = n * ((m + 1) * binomial m n) by MULT_ASSOC - = n * leibniz m n by leibniz_def - ==> n * leibniz m n divides lcm_upto (m + 1) by LCM_DIVIDES, [1], [2] - Note 0 < lcm_upto (m + 1) by lcm_upto_pos - or n * leibniz m n <= lcm_upto (m + 1) by DIVIDES_LE, 0 < lcm_upto (m + 1) - Now 4 ** n <= leibniz m n by leibniz_middle_lower - so n * 4 ** n <= n * leibniz m n by LESS_MONO_MULT, MULT_COMM - or n * 4 ** n <= lcm_upto (m + 1) by LESS_EQ_TRANS -*) -val lcm_upto_lower_odd = store_thm( - "lcm_upto_lower_odd", - ``!n. n * 4 ** n <= lcm_upto (2 * n + 1)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[] >> - `0 < n` by decide_tac >> - qabbrev_tac `m = 2 * n` >> - `(m + 1) * binomial m n divides lcm_upto (m + 1)` by - (`n <= m` by rw[Abbr`m`] >> - metis_tac[lcm_upto_leibniz_divisor, leibniz_def]) >> - `n * binomial m n divides lcm_upto (m + 1)` by - (`(lcm_upto m) divides lcm_upto (m + 1)` by rw[lcm_upto_divisors] >> - `0 < m /\ n <= m - 1` by rw[Abbr`m`] >> - `m - 1 + 1 = m` by decide_tac >> - `(leibniz (m - 1) n) divides lcm_upto m` by metis_tac[lcm_upto_leibniz_divisor] >> - `(leibniz (m - 1) n) divides lcm_upto (m + 1)` by metis_tac[DIVIDES_TRANS] >> - `leibniz (m - 1) n = (m - n) * binomial m n` by rw[leibniz_up_alt] >> - `_ = n * binomial m n` by rw[Abbr`m`] >> - metis_tac[]) >> - `coprime n (m + 1)` by rw[GCD_EUCLID, Abbr`m`] >> - `lcm (n * binomial m n) ((m + 1) * binomial m n) = n * (m + 1) * binomial m n` by rw[LCM_COMMON_COPRIME] >> - `_ = n * leibniz m n` by rw[leibniz_def, MULT_ASSOC] >> - `n * leibniz m n divides lcm_upto (m + 1)` by metis_tac[LCM_DIVIDES] >> - `n * leibniz m n <= lcm_upto (m + 1)` by rw[DIVIDES_LE, lcm_upto_pos] >> - `4 ** n <= leibniz m n` by rw[leibniz_middle_lower, Abbr`m`] >> - metis_tac[LESS_MONO_MULT, MULT_COMM, LESS_EQ_TRANS]); - -(* Theorem: n * 4 ** n <= lcm_upto (2 * (n + 1)) *) -(* Proof: - lcm_upto (2 * (n + 1)) - = lcm_upto (2 * n + 2) by arithmetic - >= lcm_upto (2 * n + 1) by lcm_upto_monotone - >= n * 4 ** n by lcm_upto_lower_odd -*) -val lcm_upto_lower_even = store_thm( - "lcm_upto_lower_even", - ``!n. n * 4 ** n <= lcm_upto (2 * (n + 1))``, - rpt strip_tac >> - `2 * (n + 1) = 2 * n + 1 + 1` by decide_tac >> - metis_tac[lcm_upto_monotone, lcm_upto_lower_odd, LESS_EQ_TRANS]); - -(* Theorem: 7 <= n ==> 2 ** n <= lcm_upto n *) -(* Proof: - If ODD n, ?k. n = SUC (2 * k) by ODD_EXISTS, - When 5 <= 7 <= n = 2 * k + 1 by ADD1 - 2 <= k by arithmetic - and lcm_upto n - = lcm_upto (2 * k + 1) by notation - >= k * 4 ** k by lcm_upto_lower_odd - >= 2 * 4 ** k by k >= 2, LESS_MONO_MULT - = 2 * 2 ** (2 * k) by EXP_EXP_MULT - = 2 ** SUC (2 * k) by EXP - = 2 ** n by n = SUC (2 * k) - If EVEN n, ?m. n = 2 * m by EVEN_EXISTS - Note ODD 7 /\ ODD 9 by arithmetic - If n = 8, - LHS = 2 ** 8 = 256, - RHS = lcm_upto 8 = 840 by lcm_upto_small - Hence true. - Otherwise, 10 <= n by 7 <= n, n <> 7, n <> 8, n <> 9 - Since 0 < n, 0 < m by MULT_EQ_0 - so ?k. m = SUC k by num_CASES - When 10 <= n = 2 * (k + 1) by ADD1 - 4 <= k by arithmetic - and lcm_upto n - = lcm_upto (2 * (k + 1)) by notation - >= k * 4 ** k by lcm_upto_lower_even - >= 4 * 4 ** k by k >= 4, LESS_MONO_MULT - = 4 ** SUC k by EXP - = 4 ** m by notation - = 2 ** (2 * m) by EXP_EXP_MULT - = 2 ** n by n = 2 * m -*) -val lcm_upto_lower_better = store_thm( - "lcm_upto_lower_better", - ``!n. 7 <= n ==> 2 ** n <= lcm_upto n``, - rpt strip_tac >> - Cases_on `ODD n` >| [ - `?k. n = SUC (2 * k)` by rw[GSYM ODD_EXISTS] >> - `2 <= k` by decide_tac >> - `2 * 4 ** k <= k * 4 ** k` by rw[LESS_MONO_MULT] >> - `lcm_upto n = lcm_upto (2 * k + 1)` by rw[ADD1] >> - `2 ** n = 2 * 2 ** (2 * k)` by rw[EXP] >> - `_ = 2 * 4 ** k` by rw[EXP_EXP_MULT] >> - metis_tac[lcm_upto_lower_odd, LESS_EQ_TRANS], - `ODD 7 /\ ODD 9` by rw[] >> - `EVEN n /\ n <> 7 /\ n <> 9` by metis_tac[ODD_EVEN] >> - `?m. n = 2 * m` by rw[GSYM EVEN_EXISTS] >> - `m <> 0` by decide_tac >> - `?k. m = SUC k` by metis_tac[num_CASES] >> - Cases_on `n = 8` >- - rw[lcm_upto_small] >> - `4 <= k` by decide_tac >> - `4 * 4 ** k <= k * 4 ** k` by rw[LESS_MONO_MULT] >> - `lcm_upto n = lcm_upto (2 * (k + 1))` by rw[ADD1] >> - `2 ** n = 4 ** m` by rw[EXP_EXP_MULT] >> - `_ = 4 * 4 ** k` by rw[EXP] >> - metis_tac[lcm_upto_lower_even, LESS_EQ_TRANS] - ]); - -(* This is a very significant result. *) - -(* ------------------------------------------------------------------------- *) -(* Simple LCM lower bounds -- rework *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: HALF (n + 1) <= lcm_run n *) -(* Proof: - If n = 0, - LHS = HALF 1 = 0 by arithmetic - RHS = lcm_run 0 = 1 by lcm_run_0 - Hence true. - If n <> 0, 0 < n. - Let l = [1 .. n]. - Then l <> [] by listRangeINC_NIL, n <> 0 - so EVERY_POSITIVE l by listRangeINC_EVERY - lcm_run n - = list_lcm l by notation - >= (SUM l) DIV (LENGTH l) by list_lcm_nonempty_lower, l <> [] - = (SUM l) DIV n by listRangeINC_LEN - = (HALF (n * (n + 1))) DIV n by sum_1_to_n_eqn - = HALF ((n * (n + 1)) DIV n) by DIV_DIV_DIV_MULT, 0 < 2, 0 < n - = HALF (n + 1) by MULT_TO_DIV -*) -val lcm_run_lower_simple = store_thm( - "lcm_run_lower_simple", - ``!n. HALF (n + 1) <= lcm_run n``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[lcm_run_0] >> - qabbrev_tac `l = [1 .. n]` >> - `l <> []` by rw[listRangeINC_NIL, Abbr`l`] >> - `EVERY_POSITIVE l` by rw[listRangeINC_EVERY, Abbr`l`] >> - `(SUM l) DIV (LENGTH l) = (SUM l) DIV n` by rw[listRangeINC_LEN, Abbr`l`] >> - `_ = (HALF (n * (n + 1))) DIV n` by rw[sum_1_to_n_eqn, Abbr`l`] >> - `_ = HALF ((n * (n + 1)) DIV n)` by rw[DIV_DIV_DIV_MULT] >> - `_ = HALF (n + 1)` by rw[MULT_TO_DIV] >> - metis_tac[list_lcm_nonempty_lower]); - -(* This is a simple result, good but not very useful. *) - -(* Theorem: lcm_run n = list_lcm (leibniz_vertical (n - 1)) *) -(* Proof: - If n = 0, - Then n - 1 + 1 = 0 - 1 + 1 = 1 - but lcm_run 0 = 1 = lcm_run 1, hence true. - If n <> 0, - Then n - 1 + 1 = n, hence true trivially. -*) -val lcm_run_alt = store_thm( - "lcm_run_alt", - ``!n. lcm_run n = list_lcm (leibniz_vertical (n - 1))``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[lcm_run_0, lcm_run_1] >> - rw[]); - -(* Theorem: 2 ** (n - 1) <= lcm_run n *) -(* Proof: - If n = 0, - LHS = HALF 1 = 0 by arithmetic - RHS = lcm_run 0 = 1 by lcm_run_0 - Hence true. - If n <> 0, 0 < n, or 1 <= n. - Let l = leibniz_horizontal (n - 1). - Then LENGTH l = n by leibniz_horizontal_len - so l <> [] by LENGTH_NIL, n <> 0 - and EVERY_POSITIVE l by leibniz_horizontal_pos - lcm_run n - = list_lcm (leibniz_vertical (n - 1)) by lcm_run_alt - = list_lcm l by leibniz_lcm_property - >= (SUM l) DIV (LENGTH l) by list_lcm_nonempty_lower, l <> [] - = 2 ** (n - 1) by leibniz_horizontal_average_eqn -*) -val lcm_run_lower_good = store_thm( - "lcm_run_lower_good", - ``!n. 2 ** (n - 1) <= lcm_run n``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[lcm_run_0] >> - `0 < n /\ 1 <= n /\ (n - 1 + 1 = n)` by decide_tac >> - qabbrev_tac `l = leibniz_horizontal (n - 1)` >> - `lcm_run n = list_lcm l` by metis_tac[leibniz_lcm_property] >> - `LENGTH l = n` by metis_tac[leibniz_horizontal_len] >> - `l <> []` by metis_tac[LENGTH_NIL] >> - `EVERY_POSITIVE l` by rw[leibniz_horizontal_pos, Abbr`l`] >> - metis_tac[list_lcm_nonempty_lower, leibniz_horizontal_average_eqn]); - -(* ------------------------------------------------------------------------- *) -(* Upper Bound by Leibniz Triangle *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: leibniz n k = (n + 1 - k) * binomial (n + 1) k *) -(* Proof: by leibniz_up_alt: -leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k -*) -val leibniz_eqn = store_thm( - "leibniz_eqn", - ``!n k. leibniz n k = (n + 1 - k) * binomial (n + 1) k``, - rw[GSYM leibniz_up_alt]); - -(* Theorem: leibniz n (k + 1) = (n - k) * binomial (n + 1) (k + 1) *) -(* Proof: by leibniz_up_alt: -leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k -*) -val leibniz_right_alt = store_thm( - "leibniz_right_alt", - ``!n k. leibniz n (k + 1) = (n - k) * binomial (n + 1) (k + 1)``, - metis_tac[leibniz_up_alt, DECIDE``0 < n + 1 /\ (n + 1 - 1 = n) /\ (n + 1 - (k + 1) = n - k)``]); - -(* Leibniz Stack: - \ - \ - \ - \ - (L k k) <-- boundary of Leibniz Triangle - | \ |-- (m - k) = distance - | k <= m <= n <-- m - | \ (n - k) = height, or max distance - | binomial (n+1) (m+1) is at south-east of binomial n m - | \ - | \ - n-th row: ....... (L n k) ................. - -leibniz_binomial_identity -|- !m n k. k <= m /\ m <= n ==> (leibniz n k * binomial (n - k) (m - k) = leibniz m k * binomial (n + 1) (m + 1)) -This says: (leibniz n k) at bottom is related to a stack entry (leibniz m k). -leibniz_divides_leibniz_factor -|- !m n k. k <= m /\ m <= n ==> leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) -This is just a corollary of leibniz_binomial_identity, by divides_def. - -leibniz_horizontal_member_divides -|- !m n x. n <= TWICE m + 1 /\ m <= n /\ MEM x (leibniz_horizontal n) ==> - x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) -This says: for the n-th row, q = list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) - is a common multiple of all members of the n-th row when n <= TWICE m + 1 /\ m <= n -That means, for the n-th row, pick any m-th row for HALF (n - 1) <= m <= n -Compute its list_lcm (leibniz_horizontal m), then multiply by binomial (n + 1) (m + 1) as q. -This value q is a common multiple of all members in n-th row. -The proof goes through all members of n-th row, i.e. (L n k) for k <= n. -To apply leibniz_binomial_identity, the condition is k <= m, not k <= n. -Since m has been picked (between HALF n and n), divide k into two parts: k <= m, m < k <= n. -For the first part, apply leibniz_binomial_identity. -For the second part, use symmetry L n (n - k) = L n k, then apply leibniz_binomial_identity. -With k <= m, m <= n, we apply leibniz_binomial_identity: -(1) Each member x = leibniz n k divides p = leibniz m k * binomial (n + 1) (m + 1), stack member with a factor. -(2) But leibniz m k is a member of (leibniz_horizontal m) -(3) Thus leibniz m k divides list_lcm (leibniz_horizontal m), the stack member divides its row list_lcm - ==> p divides q by multiplying both by binomial (n + 1) (m + 1) -(4) Hence x divides q. -With the other half by symmetry, all members x divides q. -Corollary 1: -lcm_run_divides_property -|- !m n. n <= TWICE m /\ m <= n ==> lcm_run n divides binomial n m * lcm_run m -This follows by list_lcm_is_least_common_multiple and leibniz_lcm_property. -Corollary 2: -lcm_run_bound_recurrence -|- !m n. n <= TWICE m /\ m <= n ==> lcm_run n <= lcm_run m * binomial n m -Then lcm_run_upper_bound |- !n. lcm_run n <= 4 ** n follows by complete induction on n. -*) - -(* Theorem: k <= m /\ m <= n ==> - ((leibniz n k) * (binomial (n - k) (m - k)) = (leibniz m k) * (binomial (n + 1) (m + 1))) *) -(* Proof: - leibniz n k * (binomial (n - k) (m - k)) - = (n + 1) * (binomial n k) * (binomial (n - k) (m - k)) by leibniz_def - n! (n - k)! - = (n + 1) * ------------- * ------------------ binomial formula - k! (n - k)! (m - k)! (n - m)! - n! 1 - = (n + 1) * -------------- * ------------------ cancel (n - k)! - k! 1 (m - k)! (n - m)! - n! (m + 1)! - = (n + 1) * -------------- * ------------------ replace by (m + 1)! - k! (m + 1)! (m - k)! (n - m)! - (n + 1)! m! - = (m + 1) * -------------- * ------------------ merge and split factorials - k! (m + 1)! (m - k)! (n - m)! - m! (n + 1)! - = (m + 1) * -------------- * ------------------ binomial formula - k! (m - k)! (m + 1)! (n - m)! - = leibniz m k * binomial (n + 1) (m + 1) by leibniz_def -*) -val leibniz_binomial_identity = store_thm( - "leibniz_binomial_identity", - ``!m n k. k <= m /\ m <= n ==> - ((leibniz n k) * (binomial (n - k) (m - k)) = (leibniz m k) * (binomial (n + 1) (m + 1)))``, - rw[leibniz_def] >> - `m + 1 <= n + 1` by decide_tac >> - `m - k <= n - k` by decide_tac >> - `(n - k) - (m - k) = n - m` by decide_tac >> - `(n + 1) - (m + 1) = n - m` by decide_tac >> - `FACT m = binomial m k * (FACT (m - k) * FACT k)` by rw[binomial_formula2] >> - `FACT (n + 1) = binomial (n + 1) (m + 1) * (FACT (n - m) * FACT (m + 1))` by metis_tac[binomial_formula2] >> - `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> - `FACT (n - k) = binomial (n - k) (m - k) * (FACT (n - m) * FACT (m - k))` by metis_tac[binomial_formula2] >> - `!n. FACT (n + 1) = (n + 1) * FACT n` by metis_tac[FACT, ADD1] >> - `FACT (n + 1) = FACT (n - m) * (FACT k * (FACT (m - k) * ((m + 1) * (binomial m k) * (binomial (n + 1) (m + 1)))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> - `FACT (n + 1) = FACT (n - m) * (FACT k * (FACT (m - k) * ((n + 1) * (binomial n k) * (binomial (n - k) (m - k)))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> - metis_tac[MULT_LEFT_CANCEL, FACT_LESS, NOT_ZERO_LT_ZERO]); - -(* Theorem: k <= m /\ m <= n ==> leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) *) -(* Proof: - Note leibniz m k * binomial (n + 1) (m + 1) - = leibniz n k * binomial (n - k) (m - k) by leibniz_binomial_identity - Thus leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) - by divides_def, MULT_COMM -*) -val leibniz_divides_leibniz_factor = store_thm( - "leibniz_divides_leibniz_factor", - ``!m n k. k <= m /\ m <= n ==> leibniz n k divides leibniz m k * binomial (n + 1) (m + 1)``, - metis_tac[leibniz_binomial_identity, divides_def, MULT_COMM]); - -(* Theorem: n <= 2 * m + 1 /\ m <= n /\ MEM x (leibniz_horizontal n) ==> - x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) *) -(* Proof: - Let q = list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1). - Note MEM x (leibniz_horizontal n) - ==> ?k. k <= n /\ (x = leibniz n k) by leibniz_horizontal_member - Here the picture is: - HALF n ... m .... n - 0 ........ k .......... n - We need k <= m to get x divides q, by applying leibniz_divides_leibniz_factor. - For m < k <= n, we shall use symmetry to get x divides q. - If k <= m, - Let p = (leibniz m k) * binomial (n + 1) (m + 1). - Then x divides p by leibniz_divides_leibniz_factor, k <= m, m <= n - and MEM (leibniz m k) (leibniz_horizontal m) by leibniz_horizontal_member, k <= m - ==> (leibniz m k) divides list_lcm (leibniz_horizontal m) by list_lcm_is_common_multiple - so (leibniz m k) * binomial (n + 1) (m + 1) - divides - list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) by DIVIDES_CANCEL, binomial_pos - or p divides q by notation - Thus x divides q by DIVIDES_TRANS - If ~(k <= m), then m < k. - Note x = leibniz n (n - k) by leibniz_sym, k <= n - Now n <= m + m + 1 by given n <= 2 * m + 1 - so n - k <= m + m + 1 - k by arithmetic - and m + m + 1 - k <= m by m < k, so m + 1 <= k - or n - k <= m by LESS_EQ_TRANS - Let j = n - k, p = (leibniz m j) * binomial (n + 1) (m + 1). - Then x divides p by leibniz_divides_leibniz_factor, j <= m, m <= n - and MEM (leibniz m j) (leibniz_horizontal m) by leibniz_horizontal_member, j <= m - ==> (leibniz m j) divides list_lcm (leibniz_horizontal m) by list_lcm_is_common_multiple - so (leibniz m j) * binomial (n + 1) (m + 1) - divides - list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) by DIVIDES_CANCEL, binomial_pos - or p divides q by notation - Thus x divides q by DIVIDES_TRANS -*) -val leibniz_horizontal_member_divides = store_thm( - "leibniz_horizontal_member_divides", - ``!m n x. n <= 2 * m + 1 /\ m <= n /\ MEM x (leibniz_horizontal n) ==> - x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1)``, - rpt strip_tac >> - qabbrev_tac `q = list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1)` >> - `?k. k <= n /\ (x = leibniz n k)` by rw[GSYM leibniz_horizontal_member] >> - Cases_on `k <= m` >| [ - qabbrev_tac `p = (leibniz m k) * binomial (n + 1) (m + 1)` >> - `x divides p` by rw[leibniz_divides_leibniz_factor, Abbr`p`] >> - `MEM (leibniz m k) (leibniz_horizontal m)` by metis_tac[leibniz_horizontal_member] >> - `(leibniz m k) divides list_lcm (leibniz_horizontal m)` by rw[list_lcm_is_common_multiple] >> - `p divides q` by rw[GSYM DIVIDES_CANCEL, binomial_pos, Abbr`p`, Abbr`q`] >> - metis_tac[DIVIDES_TRANS], - `n - k <= m` by decide_tac >> - qabbrev_tac `j = n - k` >> - `x = leibniz n j` by rw[Once leibniz_sym, Abbr`j`] >> - qabbrev_tac `p = (leibniz m j) * binomial (n + 1) (m + 1)` >> - `x divides p` by rw[leibniz_divides_leibniz_factor, Abbr`p`] >> - `MEM (leibniz m j) (leibniz_horizontal m)` by metis_tac[leibniz_horizontal_member] >> - `(leibniz m j) divides list_lcm (leibniz_horizontal m)` by rw[list_lcm_is_common_multiple] >> - `p divides q` by rw[GSYM DIVIDES_CANCEL, binomial_pos, Abbr`p`, Abbr`q`] >> - metis_tac[DIVIDES_TRANS] - ]); - -(* Theorem: n <= 2 * m /\ m <= n ==> (lcm_run n) divides (lcm_run m) * binomial n m *) -(* Proof: - If n = 0, - Then lcm_run 0 = 1 by lcm_run_0 - Hence true by ONE_DIVIDES_ALL - If n <> 0, - Then 0 < n, and 0 < m by n <= 2 * m - Thus m - 1 <= n - 1 by m <= n - and n - 1 <= 2 * m - 1 by n <= 2 * m - = 2 * (m - 1) + 1 - Thus !x. MEM x (leibniz_horizontal (n - 1)) ==> - x divides list_lcm (leibniz_horizontal (m - 1)) * binomial n m - by leibniz_horizontal_member_divides - ==> list_lcm (leibniz_horizontal (n - 1)) divides - list_lcm (leibniz_horizontal (m - 1)) * binomial n m - by list_lcm_is_least_common_multiple - But lcm_run n = leibniz_horizontal (n - 1) by leibniz_lcm_property - and lcm_run m = leibniz_horizontal (m - 1) by leibniz_lcm_property - list_lcm (leibniz_horizontal h) divides q by list_lcm_is_least_common_multiple - Thus (lcm_run n) divides (lcm_run m) * binomial n m by above -*) -val lcm_run_divides_property = store_thm( - "lcm_run_divides_property", - ``!m n. n <= 2 * m /\ m <= n ==> (lcm_run n) divides (lcm_run m) * binomial n m``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[lcm_run_0] >> - `0 < n` by decide_tac >> - `0 < m` by decide_tac >> - `m - 1 <= n - 1` by decide_tac >> - `n - 1 <= 2 * (m - 1) + 1` by decide_tac >> - `(n - 1 + 1 = n) /\ (m - 1 + 1 = m)` by decide_tac >> - metis_tac[leibniz_horizontal_member_divides, list_lcm_is_least_common_multiple, leibniz_lcm_property]); - -(* Theorem: n <= 2 * m /\ m <= n ==> (lcm_run n) <= (lcm_run m) * binomial n m *) -(* Proof: - Note 0 < lcm_run m by lcm_run_pos - and 0 < binomial n m by binomial_pos - so 0 < lcm_run m * binomial n m by MULT_EQ_0 - Now (lcm_run n) divides (lcm_run m) * binomial n m by lcm_run_divides_property - Thus (lcm_run n) <= (lcm_run m) * binomial n m by DIVIDES_LE -*) -val lcm_run_bound_recurrence = store_thm( - "lcm_run_bound_recurrence", - ``!m n. n <= 2 * m /\ m <= n ==> (lcm_run n) <= (lcm_run m) * binomial n m``, - rpt strip_tac >> - `0 < lcm_run m * binomial n m` by metis_tac[lcm_run_pos, binomial_pos, MULT_EQ_0, NOT_ZERO_LT_ZERO] >> - rw[lcm_run_divides_property, DIVIDES_LE]); - -(* Theorem: lcm_run n <= 4 ** n *) -(* Proof: - By complete induction on n. - If EVEN n, - Base: n = 0. - LHS = lcm_run 0 = 1 by lcm_run_0 - RHS = 4 ** 0 = 1 by EXP - Hence true. - Step: n <> 0 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n - Let m = HALF n, c = lcm_run m * binomial n m. - Then n = 2 * m by EVEN_HALF - so m <= 2 * m = n by arithmetic - ==> lcm_run n <= c by lcm_run_bound_recurrence, m <= n - But m <> 0 by n <> 0 - so m < n by arithmetic - Now c = lcm_run m * binomial n m by notation - <= 4 ** m * binomial n m by induction hypothesis, m < n - <= 4 ** m * 4 ** m by binomial_middle_upper_bound - = 4 ** (m + m) by EXP_ADD - = 4 ** n by TIMES2, n = 2 * m - Hence lcm_run n <= 4 ** n. - If ~EVEN n, - Then ODD n by EVEN_ODD - Base: n = 1. - LHS = lcm_run 1 = 1 by lcm_run_1 - RHS = 4 ** 1 = 4 by EXP - Hence true. - Step: n <> 1 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n - Let m = HALF n, c = lcm_run (m + 1) * binomial n (m + 1). - Then n = 2 * m + 1 by ODD_HALF - and 0 < m by n <> 1 - and m + 1 <= 2 * m + 1 = n by arithmetic - ==> (lcm_run n) <= c by lcm_run_bound_recurrence, m + 1 <= n - But m + 1 <> n by m <> 0 - so m + 1 < n by m + 1 <> n - Now c = lcm_run (m + 1) * binomial n (m + 1) by notation - <= 4 ** (m + 1) * binomial n (m + 1) by induction hypothesis, m + 1 < n - = 4 ** (m + 1) * binomial n m by binomial_sym, n - (m + 1) = m - <= 4 ** (m + 1) * 4 ** m by binomial_middle_upper_bound - = 4 ** m * 4 ** (m + 1) by arithmetic - = 4 ** (m + (m + 1)) by EXP_ADD - = 4 ** (2 * m + 1) by arithmetic - = 4 ** n by n = 2 * m + 1 - Hence lcm_run n <= 4 ** n. -*) -val lcm_run_upper_bound = store_thm( - "lcm_run_upper_bound", - ``!n. lcm_run n <= 4 ** n``, - completeInduct_on `n` >> - Cases_on `EVEN n` >| [ - Cases_on `n = 0` >- - rw[lcm_run_0] >> - qabbrev_tac `m = HALF n` >> - `n = 2 * m` by rw[EVEN_HALF, Abbr`m`] >> - qabbrev_tac `c = lcm_run m * binomial n m` >> - `lcm_run n <= c` by rw[lcm_run_bound_recurrence, Abbr`c`] >> - `lcm_run m <= 4 ** m` by rw[] >> - `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> - `c <= 4 ** m * 4 ** m` by rw[LESS_MONO_MULT2, Abbr`c`] >> - `4 ** m * 4 ** m = 4 ** n` by metis_tac[EXP_ADD, TIMES2] >> - decide_tac, - `ODD n` by metis_tac[EVEN_ODD] >> - Cases_on `n = 1` >- - rw[lcm_run_1] >> - qabbrev_tac `m = HALF n` >> - `n = 2 * m + 1` by rw[ODD_HALF, Abbr`m`] >> - qabbrev_tac `c = lcm_run (m + 1) * binomial n (m + 1)` >> - `lcm_run n <= c` by rw[lcm_run_bound_recurrence, Abbr`c`] >> - `lcm_run (m + 1) <= 4 ** (m + 1)` by rw[] >> - `binomial n (m + 1) = binomial n m` by rw[Once binomial_sym] >> - `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> - `c <= 4 ** (m + 1) * 4 ** m` by rw[LESS_MONO_MULT2, Abbr`c`] >> - `4 ** (m + 1) * 4 ** m = 4 ** n` by metis_tac[MULT_COMM, EXP_ADD, ADD_ASSOC, TIMES2] >> - decide_tac - ]); - -(* This is a milestone theorem. *) - -(* ------------------------------------------------------------------------- *) -(* Beta Triangle *) -(* ------------------------------------------------------------------------- *) - -(* Define beta triangle *) -(* Use temp_overload so that beta is invisibe outside: -val beta_def = Define` - beta n k = k * (binomial n k) -`; -*) -val _ = temp_overload_on ("beta", ``\n k. k * (binomial n k)``); (* for temporary overloading *) -(* can use overload, but then hard to print and change the appearance of too many theorem? *) - -(* - -Pascal's Triangle (k <= n) -n = 0 1 = binomial 0 0 -n = 1 1 1 -n = 2 1 2 1 -n = 3 1 3 3 1 -n = 4 1 4 6 4 1 -n = 5 1 5 10 10 5 1 -n = 6 1 6 15 20 15 6 1 - -Beta Triangle (0 < k <= n) -n = 1 1 = 1 * (1) = leibniz_horizontal 0 -n = 2 2 2 = 2 * (1 1) = leibniz_horizontal 1 -n = 3 3 6 3 = 3 * (1 2 1) = leibniz_horizontal 2 -n = 4 4 12 12 4 = 4 * (1 3 3 1) = leibniz_horizontal 3 -n = 5 5 20 30 20 5 = 5 * (1 4 6 4 1) = leibniz_horizontal 4 -n = 6 6 30 60 60 30 6 = 6 * (1 5 10 10 5 1) = leibniz_horizontal 5 - -> EVAL ``let n = 10 in let k = 6 in (beta (n+1) (k+1) = leibniz n k)``; --> T -> EVAL ``let n = 10 in let k = 4 in (beta (n+1) (k+1) = leibniz n k)``; --> T -> EVAL ``let n = 10 in let k = 3 in (beta (n+1) (k+1) = leibniz n k)``; --> T - -*) - -(* Theorem: beta 0 n = 0 *) -(* Proof: - beta 0 n - = n * (binomial 0 n) by notation - = n * (if n = 0 then 1 else 0) by binomial_0_n - = 0 -*) -val beta_0_n = store_thm( - "beta_0_n", - ``!n. beta 0 n = 0``, - rw[binomial_0_n]); - -(* Theorem: beta n 0 = 0 *) -(* Proof: by notation *) -val beta_n_0 = store_thm( - "beta_n_0", - ``!n. beta n 0 = 0``, - rw[]); - -(* Theorem: n < k ==> (beta n k = 0) *) -(* Proof: by notation, binomial_less_0 *) -val beta_less_0 = store_thm( - "beta_less_0", - ``!n k. n < k ==> (beta n k = 0)``, - rw[binomial_less_0]); - -(* Theorem: beta (n + 1) (k + 1) = leibniz n k *) -(* Proof: - If k <= n, then k + 1 <= n + 1 by arithmetic - beta (n + 1) (k + 1) - = (k + 1) binomial (n + 1) (k + 1) by notation - = (k + 1) (n + 1)! / (k + 1)! (n - k)! by binomial_formula2 - = (n + 1) n! / k! (n - k)! by factorial composing and decomposing - = (n + 1) * binomial n k by binomial_formula2 - = leibniz_horizontal n k by leibniz_def - If ~(k <= n), then n < k /\ n + 1 < k + 1 by arithmetic - Then beta (n + 1) (k + 1) = 0 by beta_less_0 - and leibniz n k = 0 by leibniz_less_0 - Hence true. -*) -val beta_eqn = store_thm( - "beta_eqn", - ``!n k. beta (n + 1) (k + 1) = leibniz n k``, - rpt strip_tac >> - Cases_on `k <= n` >| [ - `(n + 1) - (k + 1) = n - k` by decide_tac >> - `k + 1 <= n + 1` by decide_tac >> - `FACT (n - k) * FACT k * beta (n + 1) (k + 1) = FACT (n - k) * FACT k * ((k + 1) * binomial (n + 1) (k + 1))` by rw[] >> - `_ = FACT (n - k) * FACT (k + 1) * binomial (n + 1) (k + 1)` by metis_tac[FACT, ADD1, MULT_ASSOC, MULT_COMM] >> - `_ = FACT (n + 1)` by metis_tac[binomial_formula2, MULT_ASSOC, MULT_COMM] >> - `_ = (n + 1) * FACT n` by metis_tac[FACT, ADD1] >> - `_ = FACT (n - k) * FACT k * ((n + 1) * binomial n k)` by metis_tac[binomial_formula2, MULT_ASSOC, MULT_COMM] >> - `_ = FACT (n - k) * FACT k * (leibniz n k)` by rw[leibniz_def] >> - `FACT k <> 0 /\ FACT (n - k) <> 0` by metis_tac[FACT_LESS, NOT_ZERO_LT_ZERO] >> - metis_tac[EQ_MULT_LCANCEL, MULT_ASSOC], - rw[beta_less_0, leibniz_less_0] - ]); - -(* Theorem: 0 < n /\ 0 < k ==> (beta n k = leibniz (n - 1) (k - 1)) *) -(* Proof: by beta_eqn *) -val beta_alt = store_thm( - "beta_alt", - ``!n k. 0 < n /\ 0 < k ==> (beta n k = leibniz (n - 1) (k - 1))``, - rw[GSYM beta_eqn]); - -(* Theorem: 0 < k /\ k <= n ==> 0 < beta n k *) -(* Proof: - 0 < beta n k - <=> beta n k <> 0 by NOT_ZERO_LT_ZERO - <=> k * (binomial n k) <> 0 by notation - <=> k <> 0 /\ binomial n k <> 0 by MULT_EQ_0 - <=> k <> 0 /\ k <= n by binomial_pos - <=> 0 < k /\ k <= n by NOT_ZERO_LT_ZERO -*) -val beta_pos = store_thm( - "beta_pos", - ``!n k. 0 < k /\ k <= n ==> 0 < beta n k``, - metis_tac[MULT_EQ_0, binomial_pos, NOT_ZERO_LT_ZERO]); - -(* Theorem: (beta n k = 0) <=> (k = 0) \/ n < k *) -(* Proof: - beta n k = 0 - <=> k * (binomial n k) = 0 by notation - <=> (k = 0) \/ (binomial n k = 0) by MULT_EQ_0 - <=> (k = 0) \/ (n < k) by binomial_eq_0 -*) -val beta_eq_0 = store_thm( - "beta_eq_0", - ``!n k. (beta n k = 0) <=> (k = 0) \/ n < k``, - rw[binomial_eq_0]); - -(* -binomial_sym |- !n k. k <= n ==> (binomial n k = binomial n (n - k)) -leibniz_sym |- !n k. k <= n ==> (leibniz n k = leibniz n (n - k)) -*) - -(* Theorem: k <= n ==> (beta n k = beta n (n - k + 1)) *) -(* Proof: - If k = 0, - Then beta n 0 = 0 by beta_n_0 - and beta n (n + 1) = 0 by beta_less_0 - Hence true. - If k <> 0, then 0 < k - Thus 0 < n by k <= n - beta n k - = leibniz (n - 1) (k - 1) by beta_alt - = leibniz (n - 1) (n - k) by leibniz_sym - = leibniz (n - 1) (n - k + 1 - 1) by arithmetic - = beta n (n - k + 1) by beta_alt -*) -val beta_sym = store_thm( - "beta_sym", - ``!n k. k <= n ==> (beta n k = beta n (n - k + 1))``, - rpt strip_tac >> - Cases_on `k = 0` >- - rw[beta_n_0, beta_less_0] >> - rw[beta_alt, Once leibniz_sym]); - -(* ------------------------------------------------------------------------- *) -(* Beta Horizontal List *) -(* ------------------------------------------------------------------------- *) - -(* -> EVAL ``leibniz_horizontal 3``; --> [4; 12; 12; 4] -> EVAL ``GENLIST (beta 4) 5``; --> [0; 4; 12; 12; 4] -> EVAL ``TL (GENLIST (beta 4) 5)``; --> [4; 12; 12; 4] -*) - -(* Use overloading for a row of beta n k, k = 1 to n. *) -(* val _ = overload_on("beta_horizontal", ``\n. TL (GENLIST (beta n) (n + 1))``); *) -(* use a direct GENLIST rather than tail of a GENLIST *) -val _ = temp_overload_on("beta_horizontal", ``\n. GENLIST (beta n o SUC) n``); (* for temporary overloading *) - -(* -> EVAL ``leibniz_horizontal 5``; --> [6; 30; 60; 60; 30; 6] -> EVAL ``beta_horizontal 6``; --> [6; 30; 60; 60; 30; 6] -*) - -(* Theorem: beta_horizontal 0 = [] *) -(* Proof: - beta_horizontal 0 - = GENLIST (beta 0 o SUC) 0 by notation - = [] by GENLIST -*) -val beta_horizontal_0 = store_thm( - "beta_horizontal_0", - ``beta_horizontal 0 = []``, - rw[]); - -(* Theorem: LENGTH (beta_horizontal n) = n *) -(* Proof: - LENGTH (beta_horizontal n) - = LENGTH (GENLIST (beta n o SUC) n) by notation - = n by LENGTH_GENLIST -*) -val beta_horizontal_len = store_thm( - "beta_horizontal_len", - ``!n. LENGTH (beta_horizontal n) = n``, - rw[]); - -(* Theorem: beta_horizontal (n + 1) = leibniz_horizontal n *) -(* Proof: - Note beta_horizontal (n + 1) = GENLIST ((beta (n + 1) o SUC)) (n + 1) by notation - and leibniz_horizontal n = GENLIST (leibniz n) (n + 1) by notation - Now (beta (n + 1)) o SUC) k - = beta (n + 1) (k + 1) by ADD1 - = leibniz n k by beta_eqn - Thus beta_horizontal (n + 1) = leibniz_horizontal n by GENLIST_FUN_EQ -*) -val beta_horizontal_eqn = store_thm( - "beta_horizontal_eqn", - ``!n. beta_horizontal (n + 1) = leibniz_horizontal n``, - rw[GENLIST_FUN_EQ, beta_eqn, ADD1]); - -(* Theorem: 0 < n ==> (beta_horizontal n = leibniz_horizontal (n - 1)) *) -(* Proof: by beta_horizontal_eqn *) -val beta_horizontal_alt = store_thm( - "beta_horizontal_alt", - ``!n. 0 < n ==> (beta_horizontal n = leibniz_horizontal (n - 1))``, - metis_tac[beta_horizontal_eqn, DECIDE``0 < n ==> (n - 1 + 1 = n)``]); - -(* Theorem: 0 < k /\ k <= n ==> MEM (beta n k) (beta_horizontal n) *) -(* Proof: - By MEM_GENLIST, this is to show: - ?m. m < n /\ (beta n k = beta n (SUC m)) - Since k <> 0, k = SUC m, - and SUC m = k <= n ==> m < n by arithmetic - So take this m, and the result follows. -*) -val beta_horizontal_mem = store_thm( - "beta_horizontal_mem", - ``!n k. 0 < k /\ k <= n ==> MEM (beta n k) (beta_horizontal n)``, - rpt strip_tac >> - rw[MEM_GENLIST] >> - `?m. k = SUC m` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> - `m < n` by decide_tac >> - metis_tac[]); - -(* too weak: -binomial_horizontal_mem |- !n k. k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n) -leibniz_horizontal_mem |- !n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) -*) - -(* Theorem: MEM (beta n k) (beta_horizontal n) <=> 0 < k /\ k <= n *) -(* Proof: - By MEM_GENLIST, this is to show: - (?m. m < n /\ (beta n k = beta n (SUC m))) <=> 0 < k /\ k <= n - If part: (?m. m < n /\ (beta n k = beta n (SUC m))) ==> 0 < k /\ k <= n - By contradiction, suppose k = 0 \/ n < k - Note SUC m <> 0 /\ ~(n < SUC m) by m < n - Thus beta n (SUC m) <> 0 by beta_eq_0 - or beta n k <> 0 by beta n k = beta n (SUC m) - ==> (k <> 0) /\ ~(n < k) by beta_eq_0 - This contradicts k = 0 \/ n < k. - Only-if part: 0 < k /\ k <= n ==> ?m. m < n /\ (beta n k = beta n (SUC m)) - Note k <> 0, so ?m. k = SUC m by num_CASES - and SUC m <= n <=> m < n by LESS_EQ - so Take this m, and the result follows. -*) -val beta_horizontal_mem_iff = store_thm( - "beta_horizontal_mem_iff", - ``!n k. MEM (beta n k) (beta_horizontal n) <=> 0 < k /\ k <= n``, - rw[MEM_GENLIST] >> - rewrite_tac[EQ_IMP_THM] >> - strip_tac >| [ - spose_not_then strip_assume_tac >> - `SUC m <> 0 /\ ~(n < SUC m)` by decide_tac >> - `(k <> 0) /\ ~(n < k)` by metis_tac[beta_eq_0] >> - decide_tac, - strip_tac >> - `?m. k = SUC m` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> - metis_tac[LESS_EQ] - ]); - -(* Theorem: MEM x (beta_horizontal n) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k) *) -(* Proof: - By MEM_GENLIST, this is to show: - (?m. m < n /\ (x = beta n (SUC m))) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k) - Since 0 < k /\ k <= n <=> ?m. (k = SUC m) /\ m < n by num_CASES, LESS_EQ - This is trivially true. -*) -val beta_horizontal_member = store_thm( - "beta_horizontal_member", - ``!n x. MEM x (beta_horizontal n) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k)``, - rw[MEM_GENLIST] >> - metis_tac[num_CASES, NOT_ZERO_LT_ZERO, SUC_NOT_ZERO, LESS_EQ]); - -(* Theorem: k < n ==> (EL k (beta_horizontal n) = beta n (k + 1)) *) -(* Proof: by EL_GENLIST, ADD1 *) -val beta_horizontal_element = store_thm( - "beta_horizontal_element", - ``!n k. k < n ==> (EL k (beta_horizontal n) = beta n (k + 1))``, - rw[EL_GENLIST, ADD1]); - -(* Theorem: 0 < n ==> (lcm_run n = list_lcm (beta_horizontal n)) *) -(* Proof: - Note n <> 0 - ==> n = SUC k for some k by num_CASES - or n = k + 1 by ADD1 - lcm_run n - = lcm_run (k + 1) - = list_lcm (leibniz_horizontal k) by leibniz_lcm_property - = list_lcm (beta_horizontal n) by beta_horizontal_eqn -*) -val lcm_run_by_beta_horizontal = store_thm( - "lcm_run_by_beta_horizontal", - ``!n. 0 < n ==> (lcm_run n = list_lcm (beta_horizontal n))``, - metis_tac[leibniz_lcm_property, beta_horizontal_eqn, num_CASES, ADD1, NOT_ZERO_LT_ZERO]); - -(* Theorem: 0 < k /\ k <= n ==> (beta n k) divides lcm_run n *) -(* Proof: - Note 0 < n by 0 < k /\ k <= n - and MEM (beta n k) (beta_horizontal n) by beta_horizontal_mem - also lcm_run n = list_lcm (beta_horizontal n) by lcm_run_by_beta_horizontal, 0 < n - Thus (beta n k) divides lcm_run n by list_lcm_is_common_multiple -*) -val lcm_run_beta_divisor = store_thm( - "lcm_run_beta_divisor", - ``!n k. 0 < k /\ k <= n ==> (beta n k) divides lcm_run n``, - rw[beta_horizontal_mem, lcm_run_by_beta_horizontal, list_lcm_is_common_multiple]); - -(* Theorem: k <= m /\ m <= n ==> (beta n k) divides (beta m k) * (binomial n m) *) -(* Proof: - Note (binomial m k) * (binomial n m) - = (binomial n k) * (binomial (n - k) (m - k)) by binomial_product_identity - Thus binomial n k divides binomial m k * binomial n m by divides_def, MULT_COMM - ==> k * binomial n k divides k * (binomial m k * binomial n m) by DIVIDES_CANCEL_COMM - = (k * binomial m k) * binomial n m by MULT_ASSOC - or (beta n k) divides (beta m k) * (binomial n m) by notation -*) -val beta_divides_beta_factor = store_thm( - "beta_divides_beta_factor", - ``!m n k. k <= m /\ m <= n ==> (beta n k) divides (beta m k) * (binomial n m)``, - rw[] >> - `binomial n k divides binomial m k * binomial n m` by metis_tac[binomial_product_identity, divides_def, MULT_COMM] >> - metis_tac[DIVIDES_CANCEL_COMM, MULT_ASSOC]); - -(* Theorem: n <= 2 * m /\ m <= n ==> (lcm_run n) divides (binomial n m) * (lcm_run m) *) -(* Proof: - If n = 0, - Then lcm_run 0 = 1 by lcm_run_0 - Hence true by ONE_DIVIDES_ALL - If n <> 0, then 0 < n. - Let q = (binomial n m) * (lcm_run m) - - Claim: !x. MEM x (beta_horizontal n) ==> x divides q - Proof: Note MEM x (beta_horizontal n) - ==> ?k. 0 < k /\ k <= n /\ (x = beta n k) by beta_horizontal_member - Here the picture is: - HALF n ... m .... n - 0 ........ k ........... n - We need k <= m to get x divides q. - For m < k <= n, we shall use symmetry to get x divides q. - If k <= m, - Let p = (beta m k) * (binomial n m). - Then x divides p by beta_divides_beta_factor, k <= m, m <= n - and (beta m k) divides lcm_run m by lcm_run_beta_divisor, 0 < k /\ k <= m - so (beta m k) * (binomial n m) - divides - (lcm_run m) * (binomial n m) by DIVIDES_CANCEL, binomial_pos - or p divides q by MULT_COMM - Thus x divides q by DIVIDES_TRANS - If ~(k <= m), then m < k. - Note x = beta n (n - k + 1) by beta_sym, k <= n - Now n <= m + m by given - so n - k + 1 <= m + m + 1 - k by arithmetic - and m + m + 1 - k <= m by m < k - ==> n - k + 1 <= m by arithmetic - Let h = n - k + 1, p = (beta m h) * (binomial n m). - Then x divides p by beta_divides_beta_factor, h <= m, m <= n - and (beta m h) divides lcm_run m by lcm_run_beta_divisor, 0 < h /\ h <= m - so (beta m h) * (binomial n m) - divides - (lcm_run m) * (binomial n m) by DIVIDES_CANCEL, binomial_pos - or p divides q by MULT_COMM - Thus x divides q by DIVIDES_TRANS - - Therefore, - (list_lcm (beta_horizontal n)) divides q by list_lcm_is_least_common_multiple, Claim - or (lcm_run n) divides q by lcm_run_by_beta_horizontal, 0 < n -*) -val lcm_run_divides_property_alt = store_thm( - "lcm_run_divides_property_alt", - ``!m n. n <= 2 * m /\ m <= n ==> (lcm_run n) divides (binomial n m) * (lcm_run m)``, - rpt strip_tac >> - Cases_on `n = 0` >- - rw[lcm_run_0] >> - `0 < n` by decide_tac >> - qabbrev_tac `q = (binomial n m) * (lcm_run m)` >> - `!x. MEM x (beta_horizontal n) ==> x divides q` by - (rpt strip_tac >> - `?k. 0 < k /\ k <= n /\ (x = beta n k)` by rw[GSYM beta_horizontal_member] >> - Cases_on `k <= m` >| [ - qabbrev_tac `p = (beta m k) * (binomial n m)` >> - `x divides p` by rw[beta_divides_beta_factor, Abbr`p`] >> - `(beta m k) divides lcm_run m` by rw[lcm_run_beta_divisor] >> - `p divides q` by metis_tac[DIVIDES_CANCEL, MULT_COMM, binomial_pos] >> - metis_tac[DIVIDES_TRANS], - `x = beta n (n - k + 1)` by rw[Once beta_sym] >> - `n - k + 1 <= m` by decide_tac >> - qabbrev_tac `h = n - k + 1` >> - qabbrev_tac `p = (beta m h) * (binomial n m)` >> - `x divides p` by rw[beta_divides_beta_factor, Abbr`p`] >> - `(beta m h) divides lcm_run m` by rw[lcm_run_beta_divisor, Abbr`h`] >> - `p divides q` by metis_tac[DIVIDES_CANCEL, MULT_COMM, binomial_pos] >> - metis_tac[DIVIDES_TRANS] - ]) >> - `(list_lcm (beta_horizontal n)) divides q` by metis_tac[list_lcm_is_least_common_multiple] >> - metis_tac[lcm_run_by_beta_horizontal]); - -(* This is the original lcm_run_divides_property to give lcm_run_upper_bound. *) - -(* Theorem: lcm_run n <= 4 ** n *) -(* Proof: - By complete induction on n. - If EVEN n, - Base: n = 0. - LHS = lcm_run 0 = 1 by lcm_run_0 - RHS = 4 ** 0 = 1 by EXP - Hence true. - Step: n <> 0 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n - Let m = HALF n, c = binomial n m * lcm_run m. - Then n = 2 * m by EVEN_HALF - so m <= 2 * m = n by arithmetic - Note 0 < binomial n m by binomial_pos, m <= n - and 0 < lcm_run m by lcm_run_pos - ==> 0 < c by MULT_EQ_0 - Thus (lcm_run n) divides c by lcm_run_divides_property, m <= n - or lcm_run n - <= c by DIVIDES_LE, 0 < c - = (binomial n m) * lcm_run m by notation - <= (binomial n m) * 4 ** m by induction hypothesis, m < n - <= 4 ** m * 4 ** m by binomial_middle_upper_bound - = 4 ** (m + m) by EXP_ADD - = 4 ** n by TIMES2, n = 2 * m - Hence lcm_run n <= 4 ** n. - If ~EVEN n, - Then ODD n by EVEN_ODD - Base: n = 1. - LHS = lcm_run 1 = 1 by lcm_run_1 - RHS = 4 ** 1 = 4 by EXP - Hence true. - Step: n <> 1 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n - Let m = HALF n, c = binomial n (m + 1) * lcm_run (m + 1). - Then n = 2 * m + 1 by ODD_HALF - and 0 < m by n <> 1 - and m + 1 <= 2 * m + 1 = n by arithmetic - But m + 1 <> n by m <> 0 - so m + 1 < n by m + 1 <> n - Note 0 < binomial n (m + 1) by binomial_pos, m + 1 <= n - and 0 < lcm_run (m + 1) by lcm_run_pos - ==> 0 < c by MULT_EQ_0 - Thus (lcm_run n) divides c by lcm_run_divides_property, 0 < m + 1, m + 1 <= n - or lcm_run n - <= c by DIVIDES_LE, 0 < c - = (binomial n (m + 1)) * lcm_run (m + 1) by notation - <= (binomial n (m + 1)) * 4 ** (m + 1) by induction hypothesis, m + 1 < n - = (binomial n m) * 4 ** (m + 1) by binomial_sym, n - (m + 1) = m - <= 4 ** m * 4 ** (m + 1) by binomial_middle_upper_bound - = 4 ** (m + (m + 1)) by EXP_ADD - = 4 ** (2 * m + 1) by arithmetic - = 4 ** n by n = 2 * m + 1 - Hence lcm_run n <= 4 ** n. -*) -Theorem lcm_run_upper_bound[allow_rebind]: - !n. lcm_run n <= 4 ** n -Proof - completeInduct_on `n` >> - Cases_on `EVEN n` >| [ - Cases_on `n = 0` >- - rw[lcm_run_0] >> - qabbrev_tac `m = HALF n` >> - `n = 2 * m` by rw[EVEN_HALF, Abbr`m`] >> - qabbrev_tac `c = binomial n m * lcm_run m` >> - `m <= n` by decide_tac >> - `0 < c` by metis_tac[binomial_pos, lcm_run_pos, MULT_EQ_0, NOT_ZERO_LT_ZERO] >> - `lcm_run n <= c` by rw[lcm_run_divides_property, DIVIDES_LE, Abbr`c`] >> - `lcm_run m <= 4 ** m` by rw[] >> - `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> - `c <= 4 ** m * 4 ** m` by rw[LESS_MONO_MULT2, Abbr`c`] >> - `4 ** m * 4 ** m = 4 ** n` by metis_tac[EXP_ADD, TIMES2] >> - decide_tac, - `ODD n` by metis_tac[EVEN_ODD] >> - Cases_on `n = 1` >- - rw[lcm_run_1] >> - qabbrev_tac `m = HALF n` >> - `n = 2 * m + 1` by rw[ODD_HALF, Abbr`m`] >> - `0 < m` by rw[] >> - qabbrev_tac `c = binomial n (m + 1) * lcm_run (m + 1)` >> - `m + 1 <= n` by decide_tac >> - `0 < c` by metis_tac[binomial_pos, lcm_run_pos, MULT_EQ_0, NOT_ZERO_LT_ZERO] >> - `lcm_run n <= c` by rw[lcm_run_divides_property, DIVIDES_LE, Abbr`c`] >> - `lcm_run (m + 1) <= 4 ** (m + 1)` by rw[] >> - `binomial n (m + 1) = binomial n m` by rw[Once binomial_sym] >> - `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> - `c <= 4 ** m * 4 ** (m + 1)` by rw[LESS_MONO_MULT2, Abbr`c`] >> - `4 ** m * 4 ** (m + 1) = 4 ** n` by metis_tac[EXP_ADD, ADD_ASSOC, TIMES2] >> - decide_tac - ] -QED - -(* This is the original proof of the upper bound. *) - -(* ------------------------------------------------------------------------- *) -(* LCM Lower Bound using Maximum *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: POSITIVE l ==> MAX_LIST l <= list_lcm l *) -(* Proof: - If l = [], - Note MAX_LIST [] = 0 by MAX_LIST_NIL - and list_lcm [] = 1 by list_lcm_nil - Hence true. - If l <> [], - Let x = MAX_LIST l. - Then MEM x l by MAX_LIST_MEM - and x divides (list_lcm l) by list_lcm_is_common_multiple - Now 0 < list_lcm l by list_lcm_pos, EVERY_MEM - so x <= list_lcm l by DIVIDES_LE, 0 < list_lcm l -*) -val list_lcm_ge_max = store_thm( - "list_lcm_ge_max", - ``!l. POSITIVE l ==> MAX_LIST l <= list_lcm l``, - rpt strip_tac >> - Cases_on `l = []` >- - rw[MAX_LIST_NIL, list_lcm_nil] >> - `MEM (MAX_LIST l) l` by rw[MAX_LIST_MEM] >> - `0 < list_lcm l` by rw[list_lcm_pos, EVERY_MEM] >> - rw[list_lcm_is_common_multiple, DIVIDES_LE]); - -(* Theorem: (n + 1) * binomial n (HALF n) <= list_lcm [1 .. (n + 1)] *) -(* Proof: - Note !k. MEM k (binomial_horizontal n) ==> 0 < k by binomial_horizontal_pos_alt [1] - - list_lcm [1 .. (n + 1)] - = list_lcm (leibniz_vertical n) by notation - = list_lcm (leibniz_horizontal n) by leibniz_lcm_property - = (n + 1) * list_lcm (binomial_horizontal n) by leibniz_horizontal_lcm_alt - >= (n + 1) * MAX_LIST (binomial_horizontal n) by list_lcm_ge_max, [1], LE_MULT_LCANCEL - = (n + 1) * binomial n (HALF n) by binomial_horizontal_max -*) -val lcm_lower_bound_by_list_lcm = store_thm( - "lcm_lower_bound_by_list_lcm", - ``!n. (n + 1) * binomial n (HALF n) <= list_lcm [1 .. (n + 1)]``, - rpt strip_tac >> - `MAX_LIST (binomial_horizontal n) <= list_lcm (binomial_horizontal n)` by - (irule list_lcm_ge_max >> - metis_tac[binomial_horizontal_pos_alt]) >> - `list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n)` by rw[leibniz_lcm_property] >> - `_ = (n + 1) * list_lcm (binomial_horizontal n)` by rw[leibniz_horizontal_lcm_alt] >> - `n + 1 <> 0` by decide_tac >> - metis_tac[LE_MULT_LCANCEL, binomial_horizontal_max]); - -(* Theorem: FINITE s /\ (!x. x IN s ==> 0 < x) ==> MAX_SET s <= big_lcm s *) -(* Proof: - If s = {}, - Note MAX_SET {} = 0 by MAX_SET_EMPTY - and big_lcm {} = 1 by big_lcm_empty - Hence true. - If s <> {}, - Let x = MAX_SET s. - Then x IN s by MAX_SET_IN_SET - and x divides (big_lcm s) by big_lcm_is_common_multiple - Now 0 < big_lcm s by big_lcm_positive - so x <= big_lcm s by DIVIDES_LE, 0 < big_lcm s -*) -val big_lcm_ge_max = store_thm( - "big_lcm_ge_max", - ``!s. FINITE s /\ (!x. x IN s ==> 0 < x) ==> MAX_SET s <= big_lcm s``, - rpt strip_tac >> - Cases_on `s = {}` >- - rw[MAX_SET_EMPTY, big_lcm_empty] >> - `(MAX_SET s) IN s` by rw[MAX_SET_IN_SET] >> - `0 < big_lcm s` by rw[big_lcm_positive] >> - rw[big_lcm_is_common_multiple, DIVIDES_LE]); - -(* Theorem: (n + 1) * binomial n (HALF n) <= big_lcm (natural (n + 1)) *) -(* Proof: - Claim: MAX_SET (IMAGE (binomial n) (count (n + 1))) <= big_lcm (IMAGE (binomial n) count (n + 1)) - Proof: By big_lcm_ge_max, this is to show: - (1) FINITE (IMAGE (binomial n) (count (n + 1))) - This is true by FINITE_COUNT, IMAGE_FINITE - (2) !x. x IN IMAGE (binomial n) (count (n + 1)) ==> 0 < x - This is true by binomial_pos, IN_IMAGE, IN_COUNT - - big_lcm (natural (n + 1)) - = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) by big_lcm_natural_eqn - >= (n + 1) * MAX_SET (IMAGE (binomial n) (count (n + 1))) by claim, LE_MULT_LCANCEL - = (n + 1) * binomial n (HALF n) by binomial_row_max -*) -val lcm_lower_bound_by_big_lcm = store_thm( - "lcm_lower_bound_by_big_lcm", - ``!n. (n + 1) * binomial n (HALF n) <= big_lcm (natural (n + 1))``, - rpt strip_tac >> - `MAX_SET (IMAGE (binomial n) (count (n + 1))) <= - big_lcm (IMAGE (binomial n) (count (n + 1)))` by - ((irule big_lcm_ge_max >> rpt conj_tac) >- - metis_tac[binomial_pos, IN_IMAGE, IN_COUNT, DECIDE``x < n + 1 ==> x <= n``] >> - rw[] - ) >> - metis_tac[big_lcm_natural_eqn, LE_MULT_LCANCEL, binomial_row_max, DECIDE``n + 1 <> 0``]); - -(* ------------------------------------------------------------------------- *) -(* Consecutive LCM function *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> - !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= list_lcm [1 .. n] *) -(* Proof: - Note ODD n ==> n <> 0 by EVEN_0, EVEN_ODD - If n = 1, - Note 1 <= pi by 0 < pi - so 2 <= 2 * pi by LE_MULT_LCANCEL, 2 <> 0 - or 1 < 2 * pi by arithmetic - Thus 1 DIV (2 * pi) = 0 by ONE_DIV, 1 < 2 * pi - and SQRT (1 DIV (2 * pi)) = 0 by ZERO_EXP, 0 ** h, h <> 0 - But list_lcm [1 .. 1] = 1 by list_lcm_sing - so SQRT (1 DIV (2 * pi)) * 2 ** 1 <= list_lcm [1 .. 1] by MULT - If n <> 1, - Then 0 < n - 1. - Let m = n - 1, then n = m + 1 by arithmetic - and n * binomial m (HALF m) <= list_lcm [1 .. n] by lcm_lower_bound_by_list_lcm - Now !a b c. (a DIV b) * c = (a * c) DIV b by DIV_1, MULT_RIGHT_1, c = c DIV 1, b * 1 = b - Note ODD n ==> EVEN m by EVEN_ODD_SUC, ADD1 - n * binomial m (HALF m) - = n * (2 ** n DIV SQRT (2 * pi * m)) by binomial_middle_by_stirling - = (2 ** n DIV SQRT (2 * pi * m)) * n by MULT_COMM - = (2 ** n * n) DIV (SQRT (2 * pi * m)) by above - = (n * 2 ** n) DIV (SQRT (2 * pi * m)) by MULT_COMM - = (n DIV SQRT (2 * pi * m)) * 2 ** n by above - = (SQRT (n DIV (2 * pi)) * 2 ** n by assumption, m = n - 1 - Hence SQRT (n DIV (2 * pi))) * (2 ** n) <= list_lcm [1 .. n] -*) -val lcm_lower_bound_by_list_lcm_stirling = store_thm( - "lcm_lower_bound_by_list_lcm_stirling", - ``Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> - !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= list_lcm [1 .. n]``, - rpt strip_tac >> - `!n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = 2 ** (n + 1) DIV SQRT (2 * pi * n))` by prove_tac[binomial_middle_by_stirling] >> - `n <> 0` by metis_tac[EVEN_0, EVEN_ODD] >> - Cases_on `n = 1` >| [ - `1 <= pi` by decide_tac >> - `1 < 2 * pi` by decide_tac >> - `1 DIV (2 * pi) = 0` by rw[ONE_DIV] >> - `SQRT (1 DIV (2 * pi)) * 2 ** 1 = 0` by rw[] >> - rw[list_lcm_sing], - `0 < n - 1 /\ (n = (n - 1) + 1)` by decide_tac >> - qabbrev_tac `m = n - 1` >> - `n * binomial m (HALF m) <= list_lcm [1 .. n]` by metis_tac[lcm_lower_bound_by_list_lcm] >> - `EVEN m` by metis_tac[EVEN_ODD_SUC, ADD1] >> - `!a b c. (a DIV b) * c = (a * c) DIV b` by metis_tac[DIV_1, MULT_RIGHT_1] >> - `n * binomial m (HALF m) = n * (2 ** n DIV SQRT (2 * pi * m))` by rw[] >> - `_ = (n DIV SQRT (2 * pi * m)) * 2 ** n` by metis_tac[MULT_COMM] >> - metis_tac[] - ]); - -(* Theorem: big_lcm (natural n) <= big_lcm (natural (n + 1)) *) -(* Proof: - Note FINITE (natural n) by natural_finite - and 0 < big_lcm (natural n) by big_lcm_positive, natural_element - big_lcm (natural n) - <= lcm (SUC n) (big_lcm (natural n)) by LCM_LE, 0 < SUC n, 0 < big_lcm (natural n) - = big_lcm ((SUC n) INSERT (natural n)) by big_lcm_insert - = big_lcm (natural (SUC n)) by natural_suc - = big_lcm (natural (n + 1)) by ADD1 -*) -val big_lcm_non_decreasing = store_thm( - "big_lcm_non_decreasing", - ``!n. big_lcm (natural n) <= big_lcm (natural (n + 1))``, - rpt strip_tac >> - `FINITE (natural n)` by rw[natural_finite] >> - `0 < big_lcm (natural n)` by rw[big_lcm_positive, natural_element] >> - `big_lcm (natural (n + 1)) = big_lcm (natural (SUC n))` by rw[ADD1] >> - `_ = big_lcm ((SUC n) INSERT (natural n))` by rw[natural_suc] >> - `_ = lcm (SUC n) (big_lcm (natural n))` by rw[big_lcm_insert] >> - rw[LCM_LE]); - -(* Theorem: Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> - !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= big_lcm (natural n) *) -(* Proof: - Note ODD n ==> n <> 0 by EVEN_0, EVEN_ODD - If n = 1, - Note 1 <= pi by 0 < pi - so 2 <= 2 * pi by LE_MULT_LCANCEL, 2 <> 0 - or 1 < 2 * pi by arithmetic - Thus 1 DIV (2 * pi) = 0 by ONE_DIV, 1 < 2 * pi - and SQRT (1 DIV (2 * pi)) = 0 by ZERO_EXP, 0 ** h, h <> 0 - But big_lcm (natural 1) = 1 by list_lcm_sing, natural_1 - so SQRT (1 DIV (2 * pi)) * 2 ** 1 <= big_lcm (natural 1) by MULT - If n <> 1, - Then 0 < n - 1. - Let m = n - 1, then n = m + 1 by arithmetic - and n * binomial m (HALF m) <= big_lcm (natural n) by lcm_lower_bound_by_big_lcm - Now !a b c. (a DIV b) * c = (a * c) DIV b by DIV_1, MULT_RIGHT_1, c = c DIV 1, b * 1 = b - Note ODD n ==> EVEN m by EVEN_ODD_SUC, ADD1 - n * binomial m (HALF m) - = n * (2 ** n DIV SQRT (2 * pi * m)) by binomial_middle_by_stirling - = (2 ** n DIV SQRT (2 * pi * m)) * n by MULT_COMM - = (2 ** n * n) DIV (SQRT (2 * pi * m)) by above - = (n * 2 ** n) DIV (SQRT (2 * pi * m)) by MULT_COMM - = (n DIV SQRT (2 * pi * m)) * 2 ** n by above - = (SQRT (n DIV (2 * pi)) * 2 ** n by assumption, m = n - 1 - Hence SQRT (n DIV (2 * pi))) * (2 ** n) <= big_lcm (natural n) -*) -val lcm_lower_bound_by_big_lcm_stirling = store_thm( - "lcm_lower_bound_by_big_lcm_stirling", - ``Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> - !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= big_lcm (natural n)``, - rpt strip_tac >> - `!n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = 2 ** (n + 1) DIV SQRT (2 * pi * n))` by prove_tac[binomial_middle_by_stirling] >> - `n <> 0` by metis_tac[EVEN_0, EVEN_ODD] >> - Cases_on `n = 1` >| [ - `1 <= pi` by decide_tac >> - `1 < 2 * pi` by decide_tac >> - `1 DIV (2 * pi) = 0` by rw[ONE_DIV] >> - `SQRT (1 DIV (2 * pi)) * 2 ** 1 = 0` by rw[] >> - rw[big_lcm_sing], - `0 < n - 1 /\ (n = (n - 1) + 1)` by decide_tac >> - qabbrev_tac `m = n - 1` >> - `n * binomial m (HALF m) <= big_lcm (natural n)` by metis_tac[lcm_lower_bound_by_big_lcm] >> - `EVEN m` by metis_tac[EVEN_ODD_SUC, ADD1] >> - `!a b c. (a DIV b) * c = (a * c) DIV b` by metis_tac[DIV_1, MULT_RIGHT_1] >> - `n * binomial m (HALF m) = n * (2 ** n DIV SQRT (2 * pi * m))` by rw[] >> - `_ = (n DIV SQRT (2 * pi * m)) * 2 ** n` by metis_tac[MULT_COMM] >> - metis_tac[] - ]); - -(* ------------------------------------------------------------------------- *) -(* Extra Theorems (not used) *) -(* ------------------------------------------------------------------------- *) - -(* -This is GCD_CANCEL_MULT by coprime p n, and coprime p n ==> coprime (p ** k) n by coprime_exp. -Note prime_not_divides_coprime. -*) - -(* Theorem: prime p /\ m divides n /\ ~((p * m) divides n) ==> (gcd (p * m) n = m) *) -(* Proof: - Note m divides n ==> ?q. n = q * m by divides_def - - Claim: coprime p q - Proof: By contradiction, suppose gcd p q <> 1. - Since (gcd p q) divides p by GCD_IS_GREATEST_COMMON_DIVISOR - so gcd p q = p by prime_def, gcd p q <> 1. - or p divides q by divides_iff_gcd_fix - Now, m <> 0 because - If m = 0, p * m = 0 by MULT_0 - Then m divides n and ~((p * m) divides n) are contradictory. - Thus p * m divides q * m by DIVIDES_MULTIPLE_IFF, MULT_COMM, p divides q, m <> 0 - But q * m = n, contradicting ~((p * m) divides n). - - gcd (p * m) n - = gcd (p * m) (q * m) by n = q * m - = m * gcd p q by GCD_COMMON_FACTOR, MULT_COMM - = m * 1 by coprime p q, from Claim - = m -*) -val gcd_prime_product_property = store_thm( - "gcd_prime_product_property", - ``!p m n. prime p /\ m divides n /\ ~((p * m) divides n) ==> (gcd (p * m) n = m)``, - rpt strip_tac >> - `?q. n = q * m` by rw[GSYM divides_def] >> - `m <> 0` by metis_tac[MULT_0] >> - `coprime p q` by - (spose_not_then strip_assume_tac >> - `(gcd p q) divides p` by rw[GCD_IS_GREATEST_COMMON_DIVISOR] >> - `gcd p q = p` by metis_tac[prime_def] >> - `p divides q` by rw[divides_iff_gcd_fix] >> - metis_tac[DIVIDES_MULTIPLE_IFF, MULT_COMM]) >> - metis_tac[GCD_COMMON_FACTOR, MULT_COMM, MULT_RIGHT_1]); - -(* Theorem: prime p /\ m divides n /\ ~((p * m) divides n) ==>(lcm (p * m) n = p * n) *) -(* Proof: - Note m <> 0 by MULT_0, m divides n /\ ~((p * m) divides n) - and m * lcm (p * m) n - = gcd (p * m) n * lcm (p * m) n by gcd_prime_product_property - = (p * m) * n by GCD_LCM - = (m * p) * n by MULT_COMM - = m * (p * n) by MULT_ASSOC - Thus lcm (p * m) n = p * n by MULT_LEFT_CANCEL -*) -val lcm_prime_product_property = store_thm( - "lcm_prime_product_property", - ``!p m n. prime p /\ m divides n /\ ~((p * m) divides n) ==>(lcm (p * m) n = p * n)``, - rpt strip_tac >> - `m <> 0` by metis_tac[MULT_0] >> - `m * lcm (p * m) n = gcd (p * m) n * lcm (p * m) n` by rw[gcd_prime_product_property] >> - `_ = (p * m) * n` by rw[GCD_LCM] >> - `_ = m * (p * n)` by metis_tac[MULT_COMM, MULT_ASSOC] >> - metis_tac[MULT_LEFT_CANCEL]); - -(* Theorem: prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) *) -(* Proof: - By induction on l. - Base: prime p /\ p divides list_lcm [] ==> p divides PROD_SET (set []) - Note list_lcm [] = 1 by list_lcm_nil - and PROD_SET (set []) - = PROD_SET {} by LIST_TO_SET - = 1 by PROD_SET_EMPTY - Hence conclusion is alredy in predicate, thus true. - Step: prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) ==> - prime p /\ p divides list_lcm (h::l) ==> p divides PROD_SET (set (h::l)) - Note PROD_SET (set (h::l)) - = PROD_SET (h INSERT set l) by LIST_TO_SET - This is to show: p divides PROD_SET (h INSERT set l) - - Let x = list_lcm l. - Since p divides (lcm h x) by given - so p divides (gcd h x) * (lcm h x) by DIVIDES_MULTIPLE - or p divides h * x by GCD_LCM - ==> p divides h or p divides x by P_EUCLIDES - Case: p divides h. - If h IN set l, or MEM h l, - Then h divides x by list_lcm_is_common_multiple - so p divides x by DIVIDES_TRANS - Thus p divides PROD_SET (set l) by induction hypothesis - or p divides PROD_SET (h INSERT set l) by ABSORPTION - If ~(h IN set l), - Then PROD_SET (h INSERT set l) = h * PROD_SET (set l) by PROD_SET_INSERT - or p divides PROD_SET (h INSERT set l) by DIVIDES_MULTIPLE, MULT_COMM - Case: p divides x. - If h IN set l, or MEM h l, - Then p divides PROD_SET (set l) by induction hypothesis - or p divides PROD_SET (h INSERT set l) by ABSORPTION - If ~(h IN set l), - Then PROD_SET (h INSERT set l) = h * PROD_SET (set l) by PROD_SET_INSERT - or p divides PROD_SET (h INSERT set l) by DIVIDES_MULTIPLE -*) -val list_lcm_prime_factor = store_thm( - "list_lcm_prime_factor", - ``!p l. prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l)``, - strip_tac >> - Induct >- - rw[] >> - rw[] >> - qabbrev_tac `x = list_lcm l` >> - `(gcd h x) * (lcm h x) = h * x` by rw[GCD_LCM] >> - `p divides (h * x)` by metis_tac[DIVIDES_MULTIPLE] >> - `p divides h \/ p divides x` by rw[P_EUCLIDES] >| [ - Cases_on `h IN set l` >| [ - `h divides x` by rw[list_lcm_is_common_multiple, Abbr`x`] >> - `p divides x` by metis_tac[DIVIDES_TRANS] >> - fs[ABSORPTION], - rw[PROD_SET_INSERT] >> - metis_tac[DIVIDES_MULTIPLE, MULT_COMM] - ], - Cases_on `h IN set l` >- - fs[ABSORPTION] >> - rw[PROD_SET_INSERT] >> - metis_tac[DIVIDES_MULTIPLE] - ]); - -(* Theorem: prime p /\ p divides PROD_SET (set l) ==> ?x. MEM x l /\ p divides x *) -(* Proof: - By induction on l. - Base: prime p /\ p divides PROD_SET (set []) ==> ?x. MEM x [] /\ p divides x - p divides PROD_SET (set []) - ==> p divides PROD_SET {} by LIST_TO_SET - ==> p divides 1 by PROD_SET_EMPTY - ==> p = 1 by DIVIDES_ONE - This contradicts with 1 < p by ONE_LT_PRIME - Step: prime p /\ p divides PROD_SET (set l) ==> ?x. MEM x l /\ p divides x ==> - !h. prime p /\ p divides PROD_SET (set (h::l)) ==> ?x. MEM x (h::l) /\ p divides x - Note PROD_SET (set (h::l)) - = PROD_SET (h INSERT set l) by LIST_TO_SET - This is to show: ?x. ((x = h) \/ MEM x l) /\ p divides x by MEM - If h IN set l, or MEM h l, - Then h INSERT set l = set l by ABSORPTION - Thus ?x. MEM x l /\ p divides x by induction hypothesis - If ~(h IN set l), - Then PROD_SET (h INSERT set l) = h * PROD_SET (set l) by PROD_SET_INSERT - Thus p divides h \/ p divides (PROD_SET (set l)) by P_EUCLIDES - Case p divides h. - Take x = h, the result is true. - Case p divides PROD_SET (set l). - Then ?x. MEM x l /\ p divides x by induction hypothesis -*) -val list_product_prime_factor = store_thm( - "list_product_prime_factor", - ``!p l. prime p /\ p divides PROD_SET (set l) ==> ?x. MEM x l /\ p divides x``, - strip_tac >> - Induct >| [ - rpt strip_tac >> - `PROD_SET (set []) = 1` by rw[PROD_SET_EMPTY] >> - `1 < p` by rw[ONE_LT_PRIME] >> - `p <> 1` by decide_tac >> - metis_tac[DIVIDES_ONE], - rw[] >> - Cases_on `h IN set l` >- - metis_tac[ABSORPTION] >> - fs[PROD_SET_INSERT] >> - `p divides h \/ p divides (PROD_SET (set l))` by rw[P_EUCLIDES] >- - metis_tac[] >> - metis_tac[] - ]); - -(* Theorem: prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x *) -(* Proof: by list_lcm_prime_factor, list_product_prime_factor *) -val list_lcm_prime_factor_member = store_thm( - "list_lcm_prime_factor_member", - ``!p l. prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x``, - rw[list_lcm_prime_factor, list_product_prime_factor]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/linear/FiniteVSpaceScript.sml b/examples/algebra/linear/FiniteVSpaceScript.sml index 08d6e8d24d..adba178373 100644 --- a/examples/algebra/linear/FiniteVSpaceScript.sml +++ b/examples/algebra/linear/FiniteVSpaceScript.sml @@ -12,22 +12,14 @@ val _ = new_theory "FiniteVSpace"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* val _ = load "LinearIndepTheory"; *) -open VectorSpaceTheory SpanSpaceTheory LinearIndepTheory; -open monoidTheory fieldTheory; - -(* Get dependent theories in lib *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - (* open dependent theories *) -open pred_setTheory arithmeticTheory listTheory; +open pred_setTheory arithmeticTheory listTheory numberTheory combinatoricsTheory; +open VectorSpaceTheory SpanSpaceTheory LinearIndepTheory; +open monoidTheory fieldTheory; (* ------------------------------------------------------------------------- *) (* Finite Vector Space Documentation *) diff --git a/examples/algebra/linear/Holmakefile b/examples/algebra/linear/Holmakefile index 20c8c721f6..3f027627d6 100644 --- a/examples/algebra/linear/Holmakefile +++ b/examples/algebra/linear/Holmakefile @@ -1,2 +1 @@ -PRE_INCLUDES = ../ring -INCLUDES = ../lib ../monoid ../group ../field ../polynomial +INCLUDES = ../ring ../group ../field ../polynomial diff --git a/examples/algebra/linear/LinearIndepScript.sml b/examples/algebra/linear/LinearIndepScript.sml index 5c0dc928e9..e518401e9e 100644 --- a/examples/algebra/linear/LinearIndepScript.sml +++ b/examples/algebra/linear/LinearIndepScript.sml @@ -12,23 +12,17 @@ val _ = new_theory "LinearIndep"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; +(* open dependent theories *) +open pred_setTheory arithmeticTheory listTheory numberTheory combinatoricsTheory; + (* Get dependent theories local *) (* val _ = load "SpanSpaceTheory"; *) open VectorSpaceTheory SpanSpaceTheory; open monoidTheory groupTheory fieldTheory; -(* Get dependent theories in lib *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory listTheory; - - (* ------------------------------------------------------------------------- *) (* Linear Independence Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/linear/SpanSpaceScript.sml b/examples/algebra/linear/SpanSpaceScript.sml index fba5abe54e..94c91262b2 100644 --- a/examples/algebra/linear/SpanSpaceScript.sml +++ b/examples/algebra/linear/SpanSpaceScript.sml @@ -12,24 +12,17 @@ val _ = new_theory "SpanSpace"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; +(* open dependent theories *) +open pred_setTheory arithmeticTheory listTheory numberTheory combinatoricsTheory; + (* Get dependent theories local *) (* val _ = load "VectorSpaceTheory"; *) open VectorSpaceTheory; open monoidTheory groupTheory fieldTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory listTheory; - - (* ------------------------------------------------------------------------- *) (* Span Space Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/linear/VectorSpaceScript.sml b/examples/algebra/linear/VectorSpaceScript.sml index 20cac9c97f..009b79e42a 100644 --- a/examples/algebra/linear/VectorSpaceScript.sml +++ b/examples/algebra/linear/VectorSpaceScript.sml @@ -39,22 +39,13 @@ val _ = new_theory "VectorSpace"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* val _ = load "fieldTheory"; *) -open groupTheory fieldTheory; - -(* Get dependent theories in lib *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory; +open groupTheory fieldTheory; (* ------------------------------------------------------------------------- *) (* Vector Space Documentation *) diff --git a/examples/algebra/monoid/Holmakefile b/examples/algebra/monoid/Holmakefile deleted file mode 100644 index 05bed3d3ac..0000000000 --- a/examples/algebra/monoid/Holmakefile +++ /dev/null @@ -1 +0,0 @@ -INCLUDES = ../lib $(HOLDIR)/src/real diff --git a/examples/algebra/monoid/gbagScript.sml b/examples/algebra/monoid/gbagScript.sml deleted file mode 100644 index 8850fb5e71..0000000000 --- a/examples/algebra/monoid/gbagScript.sml +++ /dev/null @@ -1,528 +0,0 @@ -open HolKernel boolLib bossLib Parse dep_rewrite - pred_setTheory bagTheory helperSetTheory - monoidTheory monoidMapTheory - -(* Theory about folding a monoid (or group) operation over a bag of elements *) - -val _ = new_theory"gbag"; - -(* uses helperSetTheory so cannot move to bagTheory as-is *) -Theorem BAG_OF_SET_IMAGE_INJ: - !f s. - (!x y. x IN s /\ y IN s /\ f x = f y ==> x = y) ==> - BAG_OF_SET (IMAGE f s) = BAG_IMAGE f (BAG_OF_SET s) -Proof - rw[FUN_EQ_THM, BAG_OF_SET, BAG_IMAGE_DEF] - \\ rw[] \\ gs[GSYM BAG_OF_SET] - \\ gs[BAG_FILTER_BAG_OF_SET] - \\ simp[BAG_CARD_BAG_OF_SET] - >- ( - irule SING_CARD_1 - \\ simp[SING_TEST, GSYM pred_setTheory.MEMBER_NOT_EMPTY] - \\ metis_tac[] ) - >- simp[EXTENSION] - \\ qmatch_asmsub_abbrev_tac`INFINITE z` - \\ `z = {}` suffices_by metis_tac[FINITE_EMPTY] - \\ simp[EXTENSION, Abbr`z`] -QED - -Overload GITBAG = ``\(g:'a monoid) s b. ITBAG g.op s b``; - -Theorem GITBAG_THM = - ITBAG_THM |> CONV_RULE SWAP_FORALL_CONV - |> INST_TYPE [beta |-> alpha] |> Q.SPEC`(g:'a monoid).op` - |> GEN_ALL - -Theorem GITBAG_EMPTY[simp]: - !g a. GITBAG g {||} a = a -Proof - rw[ITBAG_EMPTY] -QED - -Theorem GITBAG_INSERT: - !b. FINITE_BAG b ==> - !g x a. GITBAG g (BAG_INSERT x b) a = - GITBAG g (BAG_REST (BAG_INSERT x b)) - (g.op (BAG_CHOICE (BAG_INSERT x b)) a) -Proof - rw[ITBAG_INSERT] -QED - -Theorem SUBSET_COMMUTING_ITBAG_INSERT: - !f b t. - SET_OF_BAG b SUBSET t /\ closure_comm_assoc_fun f t /\ FINITE_BAG b ==> - !x a::t. ITBAG f (BAG_INSERT x b) a = ITBAG f b (f x a) -Proof - simp[RES_FORALL_THM] - \\ rpt gen_tac \\ strip_tac - \\ completeInduct_on `BAG_CARD b` - \\ rw[] - \\ simp[ITBAG_INSERT, BAG_REST_DEF, EL_BAG] - \\ qmatch_goalsub_abbrev_tac`{|c|}` - \\ `BAG_IN c (BAG_INSERT x b)` by PROVE_TAC[BAG_CHOICE_DEF, BAG_INSERT_NOT_EMPTY] - \\ fs[BAG_IN_BAG_INSERT] - \\ `?b0. b = BAG_INSERT c b0` by PROVE_TAC [BAG_IN_BAG_DELETE, BAG_DELETE] - \\ `BAG_DIFF (BAG_INSERT x b) {| c |} = BAG_INSERT x b0` - by SRW_TAC [][BAG_INSERT_commutes] - \\ pop_assum SUBST_ALL_TAC - \\ first_x_assum(qspec_then`BAG_CARD b0`mp_tac) - \\ `FINITE_BAG b0` by FULL_SIMP_TAC (srw_ss()) [] - \\ impl_keep_tac >- SRW_TAC [numSimps.ARITH_ss][BAG_CARD_THM] - \\ disch_then(qspec_then`b0`mp_tac) - \\ impl_tac >- simp[] - \\ impl_tac >- fs[SUBSET_DEF] - \\ impl_tac >- simp[] - \\ strip_tac - \\ first_assum(qspec_then`x`mp_tac) - \\ first_x_assum(qspec_then`c`mp_tac) - \\ impl_keep_tac >- fs[SUBSET_DEF] - \\ disch_then(qspec_then`f x a`mp_tac) - \\ impl_keep_tac >- metis_tac[closure_comm_assoc_fun_def] - \\ strip_tac - \\ impl_tac >- simp[] - \\ disch_then(qspec_then`f c a`mp_tac) - \\ impl_keep_tac >- metis_tac[closure_comm_assoc_fun_def] - \\ disch_then SUBST1_TAC - \\ simp[] - \\ metis_tac[closure_comm_assoc_fun_def] -QED - -Theorem COMMUTING_GITBAG_INSERT: - !g b. AbelianMonoid g /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET G ==> - !x a::(G). GITBAG g (BAG_INSERT x b) a = GITBAG g b (g.op x a) -Proof - rpt strip_tac - \\ irule SUBSET_COMMUTING_ITBAG_INSERT - \\ metis_tac[abelian_monoid_op_closure_comm_assoc_fun] -QED - -Theorem GITBAG_INSERT_THM = - SIMP_RULE(srw_ss())[RES_FORALL_THM, PULL_FORALL, AND_IMP_INTRO] - COMMUTING_GITBAG_INSERT - -Theorem GITBAG_UNION: - !g. AbelianMonoid g ==> - !b1. FINITE_BAG b1 ==> !b2. FINITE_BAG b2 /\ SET_OF_BAG b1 SUBSET G - /\ SET_OF_BAG b2 SUBSET G ==> - !a. a IN G ==> GITBAG g (BAG_UNION b1 b2) a = GITBAG g b2 (GITBAG g b1 a) -Proof - gen_tac \\ strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ rw[] - \\ simp[BAG_UNION_INSERT] - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ gs[SUBSET_DEF] - \\ simp[GSYM CONJ_ASSOC] - \\ conj_tac >- metis_tac[] - \\ first_x_assum irule - \\ simp[] - \\ fs[AbelianMonoid_def] -QED - -Theorem GITBAG_in_carrier: - !g. AbelianMonoid g ==> - !b. FINITE_BAG b ==> !a. SET_OF_BAG b SUBSET G /\ a IN G ==> GITBAG g b a IN G -Proof - ntac 2 strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ simp[] - \\ rpt strip_tac - \\ drule COMMUTING_GITBAG_INSERT - \\ disch_then (qspec_then`b`mp_tac) - \\ fs[SUBSET_DEF] - \\ simp[RES_FORALL_THM, PULL_FORALL] - \\ strip_tac - \\ last_x_assum irule - \\ metis_tac[monoid_op_element, AbelianMonoid_def] -QED - -Overload GBAG = ``\(g:'a monoid) b. GITBAG g b g.id``; - -Theorem GBAG_in_carrier: - !g b. AbelianMonoid g /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET G ==> GBAG g b IN G -Proof - rw[] - \\ irule GITBAG_in_carrier - \\ metis_tac[AbelianMonoid_def, monoid_id_element] -QED - -Theorem GITBAG_GBAG: - !g. AbelianMonoid g ==> - !b. FINITE_BAG b ==> !a. a IN g.carrier /\ SET_OF_BAG b SUBSET g.carrier ==> - GITBAG g b a = g.op a (GITBAG g b g.id) -Proof - ntac 2 strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ rw[] >- fs[AbelianMonoid_def] - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[] - \\ conj_asm1_tac >- fs[SUBSET_DEF, AbelianMonoid_def] - \\ irule EQ_TRANS - \\ qexists_tac`g.op (g.op e a) (GBAG g b)` - \\ conj_tac >- ( - first_x_assum irule - \\ metis_tac[AbelianMonoid_def, monoid_op_element] ) - \\ first_x_assum(qspec_then`e`mp_tac) - \\ simp[] - \\ `g.op e (#e) = e` by metis_tac[AbelianMonoid_def, monoid_id] - \\ pop_assum SUBST1_TAC - \\ disch_then SUBST1_TAC - \\ fs[AbelianMonoid_def] - \\ irule monoid_assoc - \\ simp[] - \\ irule GBAG_in_carrier - \\ simp[AbelianMonoid_def] -QED - -Theorem GBAG_UNION: - AbelianMonoid g /\ FINITE_BAG b1 /\ FINITE_BAG b2 /\ - SET_OF_BAG b1 SUBSET g.carrier /\ SET_OF_BAG b2 SUBSET g.carrier ==> - GBAG g (BAG_UNION b1 b2) = g.op (GBAG g b1) (GBAG g b2) -Proof - rpt strip_tac - \\ DEP_REWRITE_TAC[GITBAG_UNION] - \\ simp[] - \\ conj_tac >- fs[AbelianMonoid_def] - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] - \\ simp[] - \\ irule GBAG_in_carrier - \\ simp[] -QED - -Theorem GITBAG_BAG_IMAGE_op: - !g. AbelianMonoid g ==> - !b. FINITE_BAG b ==> - !p q a. IMAGE p (SET_OF_BAG b) SUBSET g.carrier /\ - IMAGE q (SET_OF_BAG b) SUBSET g.carrier /\ a IN g.carrier ==> - GITBAG g (BAG_IMAGE (\x. g.op (p x) (q x)) b) a = - g.op (GITBAG g (BAG_IMAGE p b) a) (GBAG g (BAG_IMAGE q b)) -Proof - ntac 2 strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ rw[] >- fs[AbelianMonoid_def] - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ conj_asm1_tac - >- ( - gs[SUBSET_DEF, PULL_EXISTS] - \\ gs[AbelianMonoid_def] ) - \\ qmatch_goalsub_abbrev_tac`GITBAG g bb aa` - \\ first_assum(qspecl_then[`p`,`q`,`aa`]mp_tac) - \\ impl_tac >- ( - fs[SUBSET_DEF, PULL_EXISTS, Abbr`aa`] - \\ fs[AbelianMonoid_def] ) - \\ simp[] - \\ disch_then kall_tac - \\ simp[Abbr`aa`] - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] - \\ conj_asm1_tac >- ( - fs[SUBSET_DEF, PULL_EXISTS] - \\ fs[AbelianMonoid_def] ) - \\ irule EQ_SYM - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] - \\ conj_asm1_tac >- fs[AbelianMonoid_def] - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG |> SIMP_RULE(srw_ss())[PULL_FORALL,AND_IMP_INTRO] - |> Q.SPECL[`g`,`b`,`g.op x y`]] - \\ simp[] - \\ fs[AbelianMonoid_def] - \\ qmatch_goalsub_abbrev_tac`_ * _ * gp * ( _ * gq)` - \\ `gp ∈ g.carrier ∧ gq ∈ g.carrier` - by ( - unabbrev_all_tac - \\ conj_tac \\ irule GBAG_in_carrier - \\ fs[AbelianMonoid_def] ) - \\ drule monoid_assoc - \\ strip_tac \\ gs[] -QED - -Theorem IMP_GBAG_EQ_ID: - AbelianMonoid g ==> - !b. BAG_EVERY ((=) g.id) b ==> GBAG g b = g.id -Proof - rw[] - \\ `FINITE_BAG b` - by ( - Cases_on`b = {||}` \\ simp[] - \\ once_rewrite_tac[GSYM unibag_FINITE] - \\ rewrite_tac[FINITE_BAG_OF_SET] - \\ `SET_OF_BAG b = {g.id}` - by ( - rw[SET_OF_BAG, FUN_EQ_THM] - \\ fs[BAG_EVERY] - \\ rw[EQ_IMP_THM] - \\ Cases_on`b` \\ rw[] ) - \\ pop_assum SUBST1_TAC - \\ simp[]) - \\ qpat_x_assum`BAG_EVERY _ _` mp_tac - \\ pop_assum mp_tac - \\ qid_spec_tac`b` - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ rw[] \\ gs[] - \\ drule COMMUTING_GITBAG_INSERT - \\ disch_then drule - \\ impl_keep_tac - >- ( - fs[SUBSET_DEF, BAG_EVERY] - \\ fs[AbelianMonoid_def] - \\ metis_tac[monoid_id_element] ) - \\ simp[RES_FORALL_THM, PULL_FORALL, AND_IMP_INTRO] - \\ disch_then(qspecl_then[`#e`,`#e`]mp_tac) - \\ simp[] - \\ metis_tac[monoid_id_element, monoid_id_id, AbelianMonoid_def] -QED - -Theorem GITBAG_CONG: - !g. AbelianMonoid g ==> - !b. FINITE_BAG b ==> !b' a a'. FINITE_BAG b' /\ - a IN g.carrier /\ SET_OF_BAG b SUBSET g.carrier /\ SET_OF_BAG b' SUBSET g.carrier - /\ (!x. BAG_IN x (BAG_UNION b b') /\ x <> g.id ==> b x = b' x) - ==> - GITBAG g b a = GITBAG g b' a -Proof - ntac 2 strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT \\ rw[] - >- ( - fs[BAG_IN, BAG_INN, EMPTY_BAG] - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] - \\ simp[] - \\ irule EQ_TRANS - \\ qexists_tac`g.op a g.id` - \\ conj_tac >- fs[AbelianMonoid_def] - \\ AP_TERM_TAC - \\ irule EQ_SYM - \\ irule IMP_GBAG_EQ_ID - \\ simp[BAG_EVERY, BAG_IN, BAG_INN] - \\ metis_tac[]) - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[] - \\ fs[SET_OF_BAG_INSERT] - \\ Cases_on`e = g.id` - >- ( - fs[AbelianMonoid_def] - \\ first_x_assum irule - \\ simp[] - \\ fs[BAG_INSERT] - \\ metis_tac[] ) - \\ `BAG_IN e b'` - by ( - simp[BAG_IN, BAG_INN] - \\ fs[BAG_INSERT] - \\ first_x_assum(qspec_then`e`mp_tac) - \\ simp[] ) - \\ drule BAG_DECOMPOSE - \\ disch_then(qx_choose_then`b2`strip_assume_tac) - \\ pop_assum SUBST_ALL_TAC - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[] \\ fs[SET_OF_BAG_INSERT] - \\ first_x_assum irule \\ simp[] - \\ fs[BAG_INSERT, AbelianMonoid_def] - \\ qx_gen_tac`x` - \\ disch_then assume_tac - \\ first_x_assum(qspec_then`x`mp_tac) - \\ impl_tac >- metis_tac[] - \\ IF_CASES_TAC \\ simp[] -QED - -Theorem GITBAG_same_op: - g1.op = g2.op ==> - !b. FINITE_BAG b ==> - !a. GITBAG g1 b a = GITBAG g2 b a -Proof - strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ rw[GITBAG_THM] -QED - -Theorem GBAG_IMAGE_PARTITION: - AbelianMonoid g /\ FINITE s ==> - !b. FINITE_BAG b ==> - IMAGE f (SET_OF_BAG b) SUBSET G /\ - (!x. BAG_IN x b ==> ?P. P IN s /\ P x) /\ - (!x P1 P2. BAG_IN x b /\ P1 IN s /\ P2 IN s /\ P1 x /\ P2 x ==> P1 = P2) - ==> - GBAG g (BAG_IMAGE (λP. GBAG g (BAG_IMAGE f (BAG_FILTER P b))) (BAG_OF_SET s)) = - GBAG g (BAG_IMAGE f b) -Proof - strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ simp[] - \\ conj_tac - >- ( - irule IMP_GBAG_EQ_ID - \\ simp[BAG_EVERY] - \\ rw[] - \\ imp_res_tac BAG_IN_BAG_IMAGE_IMP - \\ fs[] ) - \\ rpt strip_tac - \\ fs[SET_OF_BAG_INSERT] - \\ `∃P. P IN s /\ P e` by metis_tac[] - \\ `∃s0. s = P INSERT s0 /\ P NOTIN s0` by metis_tac[DECOMPOSITION] - \\ BasicProvers.VAR_EQ_TAC - \\ simp[BAG_OF_SET_INSERT_NON_ELEMENT] - \\ DEP_REWRITE_TAC[BAG_IMAGE_FINITE_INSERT] - \\ qpat_x_assum`_ ⇒ _`mp_tac - \\ impl_tac >- metis_tac[] - \\ strip_tac - \\ conj_tac >- metis_tac[FINITE_INSERT, FINITE_BAG_OF_SET] - \\ qmatch_goalsub_abbrev_tac`BAG_IMAGE ff (BAG_OF_SET s0)` - \\ `BAG_IMAGE ff (BAG_OF_SET s0) = - BAG_IMAGE (\P. GBAG g (BAG_IMAGE f (BAG_FILTER P b))) (BAG_OF_SET s0)` - by ( - irule BAG_IMAGE_CONG - \\ simp[Abbr`ff`] - \\ rw[] - \\ metis_tac[IN_INSERT] ) - \\ simp[Abbr`ff`] - \\ pop_assum kall_tac - \\ rpt(first_x_assum(qspec_then`ARB`kall_tac)) - \\ pop_assum mp_tac - \\ simp[BAG_OF_SET_INSERT_NON_ELEMENT] - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ fs[AbelianMonoid_def] - \\ conj_asm1_tac >- fs[SUBSET_DEF, PULL_EXISTS] - \\ conj_asm1_tac >- ( - fs[SUBSET_DEF, PULL_EXISTS] - \\ rw[] \\ irule GITBAG_in_carrier - \\ fs[SUBSET_DEF, PULL_EXISTS, AbelianMonoid_def] ) - \\ simp[] - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[] - \\ conj_asm1_tac - >- ( - simp[AbelianMonoid_def] - \\ irule GITBAG_in_carrier - \\ simp[AbelianMonoid_def] ) - \\ simp[] - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] \\ simp[] \\ strip_tac - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] \\ simp[] - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] \\ simp[] - \\ DEP_REWRITE_TAC[monoid_assoc] - \\ simp[] - \\ conj_tac >- ( irule GBAG_in_carrier \\ simp[] ) - \\ irule EQ_SYM - \\ irule GITBAG_GBAG - \\ simp[] -QED - -Theorem GBAG_PARTITION: - AbelianMonoid g /\ FINITE s /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET G /\ - (!x. BAG_IN x b ==> ?P. P IN s /\ P x) /\ - (!x P1 P2. BAG_IN x b /\ P1 IN s /\ P2 IN s /\ P1 x /\ P2 x ==> P1 = P2) - ==> - GBAG g (BAG_IMAGE (λP. GBAG g (BAG_FILTER P b)) (BAG_OF_SET s)) = GBAG g b -Proof - strip_tac - \\ `!P. FINITE_BAG (BAG_FILTER P b)` by metis_tac[FINITE_BAG_FILTER] - \\ `GBAG g b = GBAG g (BAG_IMAGE I b)` by metis_tac[BAG_IMAGE_FINITE_I] - \\ pop_assum SUBST1_TAC - \\ `(λP. GBAG g (BAG_FILTER P b)) = λP. GBAG g (BAG_IMAGE I (BAG_FILTER P b))` - by simp[FUN_EQ_THM] - \\ pop_assum SUBST1_TAC - \\ irule GBAG_IMAGE_PARTITION - \\ simp[] - \\ metis_tac[] -QED - -Theorem GBAG_IMAGE_FILTER: - AbelianMonoid g ==> - !b. FINITE_BAG b ==> IMAGE f (SET_OF_BAG b ∩ P) SUBSET g.carrier ==> - GBAG g (BAG_IMAGE f (BAG_FILTER P b)) = - GBAG g (BAG_IMAGE (\x. if P x then f x else g.id) b) -Proof - strip_tac - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ rw[] - \\ fs[SUBSET_DEF, PULL_EXISTS] - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[SUBSET_DEF, PULL_EXISTS] - \\ conj_asm1_tac - >- ( - rw[] - \\ fs[monoidTheory.AbelianMonoid_def] - \\ metis_tac[IN_DEF] ) - \\ irule EQ_SYM - \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] - \\ simp[SUBSET_DEF, PULL_EXISTS] - \\ fs[monoidTheory.AbelianMonoid_def] - \\ qmatch_goalsub_abbrev_tac`_ * gg` - \\ `gg IN g.carrier` - by ( - simp[Abbr`gg`] - \\ irule GBAG_in_carrier - \\ simp[monoidTheory.AbelianMonoid_def, SUBSET_DEF, PULL_EXISTS] ) - \\ IF_CASES_TAC \\ gs[] - \\ simp[Abbr`gg`] - \\ irule EQ_SYM - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[PULL_EXISTS, SUBSET_DEF, monoidTheory.AbelianMonoid_def] - \\ conj_tac >- metis_tac[] - \\ qpat_x_assum`_ = _`(assume_tac o SYM) \\ simp[] - \\ irule GITBAG_GBAG - \\ simp[SUBSET_DEF, PULL_EXISTS] - \\ metis_tac[monoidTheory.AbelianMonoid_def] -QED - -Theorem GBAG_INSERT: - AbelianMonoid g /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET g.carrier /\ x IN g.carrier ==> - GBAG g (BAG_INSERT x b) = g.op x (GBAG g b) -Proof - strip_tac - \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] - \\ simp[] - \\ `Monoid g` by fs[monoidTheory.AbelianMonoid_def] \\ simp[] - \\ irule GITBAG_GBAG - \\ simp[] -QED - -Theorem MonoidHomo_GBAG: - AbelianMonoid g /\ AbelianMonoid h /\ - MonoidHomo f g h /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET g.carrier ==> - f (GBAG g b) = GBAG h (BAG_IMAGE f b) -Proof - strip_tac - \\ ntac 2 (pop_assum mp_tac) - \\ qid_spec_tac`b` - \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT - \\ simp[] - \\ fs[MonoidHomo_def] - \\ rpt strip_tac - \\ DEP_REWRITE_TAC[GBAG_INSERT] - \\ simp[] - \\ fs[SUBSET_DEF, PULL_EXISTS] - \\ `GBAG g b IN g.carrier` suffices_by metis_tac[] - \\ irule GBAG_in_carrier - \\ simp[SUBSET_DEF, PULL_EXISTS] -QED - -Theorem IMP_GBAG_EQ_EXP: - AbelianMonoid g /\ x IN g.carrier /\ SET_OF_BAG b SUBSET {x} ==> - GBAG g b = g.exp x (b x) -Proof - Induct_on`b x` \\ rw[] - >- ( - Cases_on`b = {||}` \\ simp[] - \\ fs[SUBSET_DEF] - \\ Cases_on`b` \\ fs[BAG_INSERT] ) - \\ `b = BAG_INSERT x (b - {|x|})` - by ( - simp[BAG_EXTENSION] - \\ simp[BAG_INN, BAG_INSERT, EMPTY_BAG, BAG_DIFF] - \\ rw[] ) - \\ qmatch_asmsub_abbrev_tac`BAG_INSERT x b0` - \\ fs[] - \\ `b0 x = v` by fs[BAG_INSERT] - \\ first_x_assum(qspecl_then[`b0`,`x`]mp_tac) - \\ simp[] - \\ impl_tac >- fs[SUBSET_DEF] - \\ DEP_REWRITE_TAC[GBAG_INSERT] - \\ simp[] - \\ simp[BAG_INSERT] - \\ rewrite_tac[GSYM arithmeticTheory.ADD1] - \\ simp[] - \\ DEP_REWRITE_TAC[GSYM FINITE_SET_OF_BAG] - \\ `SET_OF_BAG b0 SUBSET {x}` by fs[SUBSET_DEF] - \\ `FINITE {x}` by simp[] - \\ reverse conj_tac >- fs[SUBSET_DEF] - \\ metis_tac[SUBSET_FINITE] -QED - -val _ = export_theory(); diff --git a/examples/algebra/monoid/monoidInstancesScript.sml b/examples/algebra/monoid/monoidInstancesScript.sml deleted file mode 100644 index 213dc78524..0000000000 --- a/examples/algebra/monoid/monoidInstancesScript.sml +++ /dev/null @@ -1,709 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Applying Monoid Theory: Monoid Instances *) -(* ------------------------------------------------------------------------- *) - -(* - -Monoid Instances -=============== -The important ones: - - Zn -- Addition Modulo n, n > 0. -Z*n -- Multiplication Modulo n, n > 1. - -*) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "monoidInstances"; - -(* ------------------------------------------------------------------------- *) - - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* Get dependent theories local *) -(* val _ = load "monoidMapTheory"; *) -open monoidTheory; -open monoidMapTheory; (* for MonoidHomo and MonoidIso *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; -open listTheory; (* for list concatenation example *) - -(* val _ = load "logPowerTheory"; *) -open logrootTheory logPowerTheory; (* for LOG_EXACT_EXP *) - - -(* ------------------------------------------------------------------------- *) -(* Monoid Instances Documentation *) -(* ------------------------------------------------------------------------- *) -(* Monoid Data type: - The generic symbol for monoid data is g. - g.carrier = Carrier set of monoid - g.op = Binary operation of monoid - g.id = Identity element of monoid -*) -(* Definitions and Theorems (# are exported, ! in computeLib): - - The trivial monoid: - trivial_monoid_def |- !e. trivial_monoid e = <|carrier := {e}; id := e; op := (\x y. e)|> - trivial_monoid |- !e. FiniteAbelianMonoid (trivial_monoid e) - - The monoid of addition modulo n: - plus_mod_def |- !n. plus_mod n = - <|carrier := count n; - id := 0; - op := (\i j. (i + j) MOD n)|> - plus_mod_property |- !n. ((plus_mod n).carrier = count n) /\ - ((plus_mod n).op = (\i j. (i + j) MOD n)) /\ - ((plus_mod n).id = 0) /\ - (!x. x IN (plus_mod n).carrier ==> x < n) /\ - FINITE (plus_mod n).carrier /\ - (CARD (plus_mod n).carrier = n) - plus_mod_exp |- !n. 0 < n ==> !x k. (plus_mod n).exp x k = (k * x) MOD n - plus_mod_monoid |- !n. 0 < n ==> Monoid (plus_mod n) - plus_mod_abelian_monoid |- !n. 0 < n ==> AbelianMonoid (plus_mod n) - plus_mod_finite |- !n. FINITE (plus_mod n).carrier - plus_mod_finite_monoid |- !n. 0 < n ==> FiniteMonoid (plus_mod n) - plus_mod_finite_abelian_monoid |- !n. 0 < n ==> FiniteAbelianMonoid (plus_mod n) - - The monoid of multiplication modulo n: - times_mod_def |- !n. times_mod n = - <|carrier := count n; - id := if n = 1 then 0 else 1; - op := (\i j. (i * j) MOD n)|> -! times_mod_eval |- !n. ((times_mod n).carrier = count n) /\ - (!x y. (times_mod n).op x y = (x * y) MOD n) /\ - ((times_mod n).id = if n = 1 then 0 else 1) - times_mod_property |- !n. ((times_mod n).carrier = count n) /\ - ((times_mod n).op = (\i j. (i * j) MOD n)) /\ - ((times_mod n).id = if n = 1 then 0 else 1) /\ - (!x. x IN (times_mod n).carrier ==> x < n) /\ - FINITE (times_mod n).carrier /\ - (CARD (times_mod n).carrier = n) - times_mod_exp |- !n. 0 < n ==> !x k. (times_mod n).exp x k = (x MOD n) ** k MOD n - times_mod_monoid |- !n. 0 < n ==> Monoid (times_mod n) - times_mod_abelian_monoid |- !n. 0 < n ==> AbelianMonoid (times_mod n) - times_mod_finite |- !n. FINITE (times_mod n).carrier - times_mod_finite_monoid |- !n. 0 < n ==> FiniteMonoid (times_mod n) - times_mod_finite_abelian_monoid |- !n. 0 < n ==> FiniteAbelianMonoid (times_mod n) - - The Monoid of List concatenation: - lists_def |- lists = <|carrier := univ(:'a list); id := []; op := $++ |> - lists_monoid |- Monoid lists - - The Monoids from Set: - set_inter_def |- set_inter = <|carrier := univ(:'a -> bool); id := univ(:'a); op := $INTER|> - set_inter_monoid |- Monoid set_inter - set_inter_abelian_monoid |- AbelianMonoid set_inter - set_union_def |- set_union = <|carrier := univ(:'a -> bool); id := {}; op := $UNION|> - set_union_monoid |- Monoid set_union - set_union_abelian_monoid |- AbelianMonoid set_union - - Addition of numbers form a Monoid: - addition_monoid_def |- addition_monoid = <|carrier := univ(:num); op := $+; id := 0|> - addition_monoid_property |- (addition_monoid.carrier = univ(:num)) /\ - (addition_monoid.op = $+) /\ (addition_monoid.id = 0) - addition_monoid_abelian_monoid |- AbelianMonoid addition_monoid - addition_monoid_monoid |- Monoid addition_monoid - - Multiplication of numbers form a Monoid: - multiplication_monoid_def |- multiplication_monoid = <|carrier := univ(:num); op := $*; id := 1|> - multiplication_monoid_property |- (multiplication_monoid.carrier = univ(:num)) /\ - (multiplication_monoid.op = $* ) /\ (multiplication_monoid.id = 1) - multiplication_monoid_abelian_monoid |- AbelianMonoid multiplication_monoid - multiplication_monoid_monoid |- Monoid multiplication_monoid - - Powers of a fixed base form a Monoid: - power_monoid_def |- !b. power_monoid b = - <|carrier := {b ** j | j IN univ(:num)}; op := $*; id := 1|> - power_monoid_property |- !b. ((power_monoid b).carrier = {b ** j | j IN univ(:num)}) /\ - ((power_monoid b).op = $* ) /\ ((power_monoid b).id = 1) - power_monoid_abelian_monoid |- !b. AbelianMonoid (power_monoid b) - power_monoid_monoid |- !b. Monoid (power_monoid b) - - Logarithm is an isomorphism: - power_to_addition_homo |- !b. 1 < b ==> MonoidHomo (LOG b) (power_monoid b) addition_monoid - power_to_addition_iso |- !b. 1 < b ==> MonoidIso (LOG b) (power_monoid b) addition_monoid - - -*) -(* ------------------------------------------------------------------------- *) -(* The trivial monoid. *) -(* ------------------------------------------------------------------------- *) - -(* The trivial monoid: {#e} *) -val trivial_monoid_def = Define` - trivial_monoid e :'a monoid = - <| carrier := {e}; - id := e; - op := (\x y. e) - |> -`; - -(* -- type_of ``trivial_monoid e``; -> val it = ``:'a monoid`` : hol_type -> EVAL ``(trivial_monoid T).id``; -val it = |- (trivial_monoid T).id <=> T: thm -> EVAL ``(trivial_monoid 8).id``; -val it = |- (trivial_monoid 8).id = 8: thm -*) - -(* Theorem: {e} is indeed a monoid *) -(* Proof: check by definition. *) -val trivial_monoid = store_thm( - "trivial_monoid", - ``!e. FiniteAbelianMonoid (trivial_monoid e)``, - rw_tac std_ss[FiniteAbelianMonoid_def, AbelianMonoid_def, Monoid_def, trivial_monoid_def, IN_SING, FINITE_SING]); - -(* ------------------------------------------------------------------------- *) -(* The monoid of addition modulo n. *) -(* ------------------------------------------------------------------------- *) - -(* Additive Modulo Monoid *) -val plus_mod_def = Define` - plus_mod n :num monoid = - <| carrier := count n; - id := 0; - op := (\i j. (i + j) MOD n) - |> -`; -(* This monoid should be upgraded to add_mod, the additive group of ZN ring later. *) - -(* -- type_of ``plus_mod n``; -> val it = ``:num monoid`` : hol_type -> EVAL ``(plus_mod 7).op 5 6``; -val it = |- (plus_mod 7).op 5 6 = 4: thm -*) - -(* Theorem: properties of (plus_mod n) *) -(* Proof: by plus_mod_def. *) -val plus_mod_property = store_thm( - "plus_mod_property", - ``!n. ((plus_mod n).carrier = count n) /\ - ((plus_mod n).op = (\i j. (i + j) MOD n)) /\ - ((plus_mod n).id = 0) /\ - (!x. x IN (plus_mod n).carrier ==> x < n) /\ - (FINITE (plus_mod n).carrier) /\ - (CARD (plus_mod n).carrier = n)``, - rw[plus_mod_def]); - -(* Theorem: 0 < n ==> !x k. (plus_mod n).exp x k = (k * x) MOD n *) -(* Proof: - Expanding by definitions, this is to show: - FUNPOW (\j. (x + j) MOD n) k 0 = (k * x) MOD n - Applyy induction on k. - Base case: FUNPOW (\j. (x + j) MOD n) 0 0 = (0 * x) MOD n - LHS = FUNPOW (\j. (x + j) MOD n) 0 0 - = 0 by FUNPOW_0 - = 0 MOD n by ZERO_MOD, 0 < n - = (0 * x) MOD n by MULT - = RHS - Step case: FUNPOW (\j. (x + j) MOD n) (SUC k) 0 = (SUC k * x) MOD n - LHS = FUNPOW (\j. (x + j) MOD n) (SUC k) 0 - = (x + FUNPOW (\j. (x + j) MOD n) k 0) MOD n by FUNPOW_SUC - = (x + (k * x) MOD n) MOD n by induction hypothesis - = (x MOD n + (k * x) MOD n) MOD n by MOD_PLUS, MOD_MOD - = (x + k * x) MOD n by MOD_PLUS, MOD_MOD - = (k * x + x) MOD n by ADD_COMM - = ((SUC k) * x) MOD n by MULT - = RHS -*) -val plus_mod_exp = store_thm( - "plus_mod_exp", - ``!n. 0 < n ==> !x k. (plus_mod n).exp x k = (k * x) MOD n``, - rw_tac std_ss[plus_mod_def, monoid_exp_def] >> - Induct_on `k` >- - rw[] >> - rw_tac std_ss[FUNPOW_SUC] >> - metis_tac[MULT, ADD_COMM, MOD_PLUS, MOD_MOD]); - -(* Theorem: Additive Modulo n is a monoid. *) -(* Proof: check group definitions, use MOD_ADD_ASSOC. -*) -val plus_mod_monoid = store_thm( - "plus_mod_monoid", - ``!n. 0 < n ==> Monoid (plus_mod n)``, - rw_tac std_ss[plus_mod_def, Monoid_def, count_def, GSPECIFICATION, MOD_ADD_ASSOC]); - -(* Theorem: Additive Modulo n is an Abelian monoid. *) -(* Proof: by plus_mod_monoid and ADD_COMM. *) -val plus_mod_abelian_monoid = store_thm( - "plus_mod_abelian_monoid", - ``!n. 0 < n ==> AbelianMonoid (plus_mod n)``, - rw[plus_mod_monoid, plus_mod_def, AbelianMonoid_def, ADD_COMM]); - -(* Theorem: Additive Modulo n carrier is FINITE. *) -(* Proof: by FINITE_COUNT. *) -val plus_mod_finite = store_thm( - "plus_mod_finite", - ``!n. FINITE (plus_mod n).carrier``, - rw[plus_mod_def]); - -(* Theorem: Additive Modulo n is a FINITE monoid. *) -(* Proof: by plus_mod_monoid and plus_mod_finite. *) -val plus_mod_finite_monoid = store_thm( - "plus_mod_finite_monoid", - ``!n. 0 < n ==> FiniteMonoid (plus_mod n)``, - rw[FiniteMonoid_def, plus_mod_monoid, plus_mod_finite]); - -(* Theorem: Additive Modulo n is a FINITE Abelian monoid. *) -(* Proof: by plus_mod_abelian_monoid and plus_mod_finite. *) -val plus_mod_finite_abelian_monoid = store_thm( - "plus_mod_finite_abelian_monoid", - ``!n. 0 < n ==> FiniteAbelianMonoid (plus_mod n)``, - rw[FiniteAbelianMonoid_def, plus_mod_abelian_monoid, plus_mod_finite]); - -(* ------------------------------------------------------------------------- *) -(* The monoid of multiplication modulo n. *) -(* ------------------------------------------------------------------------- *) - -(* Multiplicative Modulo Monoid *) -val times_mod_def = zDefine` - times_mod n :num monoid = - <| carrier := count n; - id := if n = 1 then 0 else 1; - op := (\i j. (i * j) MOD n) - |> -`; -(* This monoid is taken as the multiplicative monoid of ZN ring later. *) -(* Use of zDefine to avoid incorporating into computeLib, by default. *) -(* Evaluation is given later in times_mod_eval. *) - -(* -- type_of ``times_mod n``; -> val it = ``:num monoid`` : hol_type -> EVAL ``(times_mod 7).op 5 6``; -val it = |- (times_mod 7).op 5 6 = 2: thm -*) - -(* Theorem: times_mod evaluation. *) -(* Proof: by times_mod_def. *) -val times_mod_eval = store_thm( - "times_mod_eval[compute]", - ``!n. ((times_mod n).carrier = count n) /\ - (!x y. (times_mod n).op x y = (x * y) MOD n) /\ - ((times_mod n).id = if n = 1 then 0 else 1)``, - rw_tac std_ss[times_mod_def]); - -(* Theorem: properties of (times_mod n) *) -(* Proof: by times_mod_def. *) -val times_mod_property = store_thm( - "times_mod_property", - ``!n. ((times_mod n).carrier = count n) /\ - ((times_mod n).op = (\i j. (i * j) MOD n)) /\ - ((times_mod n).id = if n = 1 then 0 else 1) /\ - (!x. x IN (times_mod n).carrier ==> x < n) /\ - (FINITE (times_mod n).carrier) /\ - (CARD (times_mod n).carrier = n)``, - rw[times_mod_def]); - -(* Theorem: 0 < n ==> !x k. (times_mod n).exp x k = ((x MOD n) ** k) MOD n *) -(* Proof: - Expanding by definitions, this is to show: - (1) n = 1 ==> FUNPOW (\j. (x * j) MOD n) k 0 = (x MOD n) ** k MOD n - or to show: FUNPOW (\j. 0) k 0 = 0 by MOD_1 - Note (\j. 0) = K 0 by FUN_EQ_THM - and FUNPOW (K 0) k 0 = 0 by FUNPOW_K - (2) n <> 1 ==> FUNPOW (\j. (x * j) MOD n) k 1 = (x MOD n) ** k MOD n - Note 1 < n by 0 < n /\ n <> 1 - By induction on k. - Base: FUNPOW (\j. (x * j) MOD n) 0 1 = (x MOD n) ** 0 MOD n - FUNPOW (\j. (x * j) MOD n) 0 1 - = 1 by FUNPOW_0 - = 1 MOD n by ONE_MOD, 1 < n - = ((x MOD n) ** 0) MOD n by EXP - Step: FUNPOW (\j. (x * j) MOD n) (SUC k) 1 = (x MOD n) ** SUC k MOD n - FUNPOW (\j. (x * j) MOD n) (SUC k) 1 - = (x * FUNPOW (\j. (x * j) MOD n) k 1) MOD n by FUNPOW_SUC - = (x * (x MOD n) ** k MOD n) MOD n by induction hypothesis - = ((x MOD n) * (x MOD n) ** k MOD n) MOD n by MOD_TIMES2, MOD_MOD, 0 < n - = ((x MOD n) * (x MOD n) ** k) MOD n by MOD_TIMES2, MOD_MOD, 0 < n - = ((x MOD n) ** SUC k) MOD n by EXP -*) -val times_mod_exp = store_thm( - "times_mod_exp", - ``!n. 0 < n ==> !x k. (times_mod n).exp x k = ((x MOD n) ** k) MOD n``, - rw_tac std_ss[times_mod_def, monoid_exp_def] >| [ - `(\j. 0) = K 0` by rw[FUN_EQ_THM] >> - metis_tac[FUNPOW_K], - `1 < n` by decide_tac >> - Induct_on `k` >- - rw[EXP, ONE_MOD] >> - `FUNPOW (\j. (x * j) MOD n) (SUC k) 1 = (x * FUNPOW (\j. (x * j) MOD n) k 1) MOD n` by rw_tac std_ss[FUNPOW_SUC] >> - metis_tac[EXP, MOD_TIMES2, MOD_MOD] - ]); - -(* Theorem: For n > 0, Multiplication Modulo n is a monoid. *) -(* Proof: check monoid definitions, use MOD_MULT_ASSOC. *) -Theorem times_mod_monoid: - !n. 0 < n ==> Monoid (times_mod n) -Proof - rw_tac std_ss[Monoid_def, times_mod_def, count_def, GSPECIFICATION] >| [ - rw[MOD_MULT_ASSOC], - decide_tac - ] -QED - -(* Theorem: For n > 0, Multiplication Modulo n is an Abelian monoid. *) -(* Proof: by times_mod_monoid and MULT_COMM. *) -val times_mod_abelian_monoid = store_thm( - "times_mod_abelian_monoid", - ``!n. 0 < n ==> AbelianMonoid (times_mod n)``, - rw[AbelianMonoid_def, times_mod_monoid, times_mod_def, MULT_COMM]); - -(* Theorem: Multiplication Modulo n carrier is FINITE. *) -(* Proof: by FINITE_COUNT. *) -val times_mod_finite = store_thm( - "times_mod_finite", - ``!n. FINITE (times_mod n).carrier``, - rw[times_mod_def]); - -(* Theorem: For n > 0, Multiplication Modulo n is a FINITE monoid. *) -(* Proof: by times_mod_monoid and times_mod_finite. *) -val times_mod_finite_monoid = store_thm( - "times_mod_finite_monoid", - ``!n. 0 < n ==> FiniteMonoid (times_mod n)``, - rw[times_mod_monoid, times_mod_finite, FiniteMonoid_def]); - -(* Theorem: For n > 0, Multiplication Modulo n is a FINITE Abelian monoid. *) -(* Proof: by times_mod_abelian_monoid and times_mod_finite. *) -val times_mod_finite_abelian_monoid = store_thm( - "times_mod_finite_abelian_monoid", - ``!n. 0 < n ==> FiniteAbelianMonoid (times_mod n)``, - rw[times_mod_abelian_monoid, times_mod_finite, FiniteAbelianMonoid_def, AbelianMonoid_def]); - -(* - -- EVAL ``(plus_mod 5).op 3 4``; -> val it = |- (plus_mod 5).op 3 4 = 2 : thm -- EVAL ``(plus_mod 5).id``; -> val it = |- (plus_mod 5).id = 0 : thm -- EVAL ``(times_mod 5).op 2 3``; -> val it = |- (times_mod 5).op 2 3 = 1 : thm -- EVAL ``(times_mod 5).op 5 3``; -> val it = |- (times_mod 5).id = 1 : thm -*) - -(* ------------------------------------------------------------------------- *) -(* The Monoid of List concatenation. *) -(* ------------------------------------------------------------------------- *) - -val lists_def = Define` - lists :'a list monoid = - <| carrier := UNIV; - id := []; - op := list$APPEND - |> -`; - -(* -> EVAL ``lists.op [1;2;3] [4;5]``; -val it = |- lists.op [1; 2; 3] [4; 5] = [1; 2; 3; 4; 5]: thm -*) - -(* Theorem: Lists form a Monoid *) -(* Proof: check definition. *) -val lists_monoid = store_thm( - "lists_monoid", - ``Monoid lists``, - rw_tac std_ss[Monoid_def, lists_def, IN_UNIV, GSPECIFICATION, APPEND, APPEND_NIL, APPEND_ASSOC]); - -(* after a long while ... - -val lists_monoid = store_thm( - "lists_monoid", - ``Monoid lists``, - rw[Monoid_def, lists_def]); -*) - -(* ------------------------------------------------------------------------- *) -(* The Monoids from Set. *) -(* ------------------------------------------------------------------------- *) - -(* The Monoid of set intersection *) -val set_inter_def = Define` - set_inter = <| carrier := UNIV; - id := UNIV; - op := (INTER) |> -`; - -(* -> EVAL ``set_inter.op {1;4;5;6} {5;6;8;9}``; -val it = |- set_inter.op {1; 4; 5; 6} {5; 6; 8; 9} = {5; 6}: thm -*) - -(* Theorem: set_inter is a Monoid. *) -(* Proof: check definitions. *) -val set_inter_monoid = store_thm( - "set_inter_monoid", - ``Monoid set_inter``, - rw[Monoid_def, set_inter_def, INTER_ASSOC]); - -val _ = export_rewrites ["set_inter_monoid"]; - -(* Theorem: set_inter is an abelian Monoid. *) -(* Proof: check definitions. *) -val set_inter_abelian_monoid = store_thm( - "set_inter_abelian_monoid", - ``AbelianMonoid set_inter``, - rw[AbelianMonoid_def, set_inter_def, INTER_COMM]); - -val _ = export_rewrites ["set_inter_abelian_monoid"]; - -(* The Monoid of set union *) -val set_union_def = Define` - set_union = <| carrier := UNIV; - id := EMPTY; - op := (UNION) |> -`; - -(* -> EVAL ``set_union.op {1;4;5;6} {5;6;8;9}``; -val it = |- set_union.op {1; 4; 5; 6} {5; 6; 8; 9} = {1; 4; 5; 6; 8; 9}: thm -*) - -(* Theorem: set_union is a Monoid. *) -(* Proof: check definitions. *) -val set_union_monoid = store_thm( - "set_union_monoid", - ``Monoid set_union``, - rw[Monoid_def, set_union_def, UNION_ASSOC]); - -val _ = export_rewrites ["set_union_monoid"]; - -(* Theorem: set_union is an abelian Monoid. *) -(* Proof: check definitions. *) -val set_union_abelian_monoid = store_thm( - "set_union_abelian_monoid", - ``AbelianMonoid set_union``, - rw[AbelianMonoid_def, set_union_def, UNION_COMM]); - -val _ = export_rewrites ["set_union_abelian_monoid"]; - -(* ------------------------------------------------------------------------- *) -(* Addition of numbers form a Monoid *) -(* ------------------------------------------------------------------------- *) - -(* Define the number addition monoid *) -val addition_monoid_def = Define` - addition_monoid = - <| carrier := univ(:num); - op := $+; - id := 0; - |> -`; - -(* -> EVAL ``addition_monoid.op 5 6``; -val it = |- addition_monoid.op 5 6 = 11: thm -*) - -(* Theorem: properties of addition_monoid *) -(* Proof: by addition_monoid_def *) -val addition_monoid_property = store_thm( - "addition_monoid_property", - ``(addition_monoid.carrier = univ(:num)) /\ - (addition_monoid.op = $+ ) /\ - (addition_monoid.id = 0)``, - rw[addition_monoid_def]); - -(* Theorem: AbelianMonoid (addition_monoid) *) -(* Proof: - By AbelianMonoid_def, Monoid_def, addition_monoid_def, this is to show: - (1) ?z. z = x + y. Take z = x + y. - (2) x + y + z = x + (y + z), true by ADD_ASSOC - (3) x + 0 = x /\ 0 + x = x, true by ADD, ADD_0 - (4) x + y = y + x, true by ADD_COMM -*) -val addition_monoid_abelian_monoid = store_thm( - "addition_monoid_abelian_monoid", - ``AbelianMonoid (addition_monoid)``, - rw_tac std_ss[AbelianMonoid_def, Monoid_def, addition_monoid_def, GSPECIFICATION, IN_UNIV] >> - simp[]); - -(* Theorem: Monoid (addition_monoid) *) -(* Proof: by addition_monoid_abelian_monoid, AbelianMonoid_def *) -val addition_monoid_monoid = store_thm( - "addition_monoid_monoid", - ``Monoid (addition_monoid)``, - metis_tac[addition_monoid_abelian_monoid, AbelianMonoid_def]); - -(* ------------------------------------------------------------------------- *) -(* Multiplication of numbers form a Monoid *) -(* ------------------------------------------------------------------------- *) - -(* Define the number multiplication monoid *) -val multiplication_monoid_def = Define` - multiplication_monoid = - <| carrier := univ(:num); - op := $*; - id := 1; - |> -`; - -(* -> EVAL ``multiplication_monoid.op 5 6``; -val it = |- multiplication_monoid.op 5 6 = 30: thm -*) - -(* Theorem: properties of multiplication_monoid *) -(* Proof: by multiplication_monoid_def *) -val multiplication_monoid_property = store_thm( - "multiplication_monoid_property", - ``(multiplication_monoid.carrier = univ(:num)) /\ - (multiplication_monoid.op = $* ) /\ - (multiplication_monoid.id = 1)``, - rw[multiplication_monoid_def]); - -(* Theorem: AbelianMonoid (multiplication_monoid) *) -(* Proof: - By AbelianMonoid_def, Monoid_def, multiplication_monoid_def, this is to show: - (1) ?z. z = x * y. Take z = x * y. - (2) x * y * z = x * (y * z), true by MULT_ASSOC - (3) x * 1 = x /\ 1 * x = x, true by MULT, MULT_1 - (4) x * y = y * x, true by MULT_COMM -*) -val multiplication_monoid_abelian_monoid = store_thm( - "multiplication_monoid_abelian_monoid", - ``AbelianMonoid (multiplication_monoid)``, - rw_tac std_ss[AbelianMonoid_def, Monoid_def, multiplication_monoid_def, GSPECIFICATION, IN_UNIV] >- - simp[] >> - simp[]); - -(* Theorem: Monoid (multiplication_monoid) *) -(* Proof: by multiplication_monoid_abelian_monoid, AbelianMonoid_def *) -val multiplication_monoid_monoid = store_thm( - "multiplication_monoid_monoid", - ``Monoid (multiplication_monoid)``, - metis_tac[multiplication_monoid_abelian_monoid, AbelianMonoid_def]); - -(* ------------------------------------------------------------------------- *) -(* Powers of a fixed base form a Monoid *) -(* ------------------------------------------------------------------------- *) - -(* Define the power monoid *) -val power_monoid_def = Define` - power_monoid (b:num) = - <| carrier := {b ** j | j IN univ(:num)}; - op := $*; - id := 1; - |> -`; - -(* -> EVAL ``(power_monoid 2).op (2 ** 3) (2 ** 4)``; -val it = |- (power_monoid 2).op (2 ** 3) (2 ** 4) = 128: thm -*) - -(* Theorem: properties of power monoid *) -(* Proof: by power_monoid_def *) -val power_monoid_property = store_thm( - "power_monoid_property", - ``!b. ((power_monoid b).carrier = {b ** j | j IN univ(:num)}) /\ - ((power_monoid b).op = $* ) /\ - ((power_monoid b).id = 1)``, - rw[power_monoid_def]); - - -(* Theorem: AbelianMonoid (power_monoid b) *) -(* Proof: - By AbelianMonoid_def, Monoid_def, power_monoid_def, this is to show: - (1) ?j''. b ** j * b ** j' = b ** j'' - Take j'' = j + j', true by EXP_ADD - (2) b ** j * b ** j' * b ** j'' = b ** j * (b ** j' * b ** j'') - True by EXP_ADD, ADD_ASSOC - (3) ?j. b ** j = 1 - or ?j. (b = 1) \/ (j = 0), true by j = 0. - (4) b ** j * b ** j' = b ** j' * b ** j - True by EXP_ADD, ADD_COMM -*) -val power_monoid_abelian_monoid = store_thm( - "power_monoid_abelian_monoid", - ``!b. AbelianMonoid (power_monoid b)``, - rw_tac std_ss[AbelianMonoid_def, Monoid_def, power_monoid_def, GSPECIFICATION, IN_UNIV] >- - metis_tac[EXP_ADD] >- - rw[EXP_ADD] >- - metis_tac[] >> - rw[EXP_ADD]); - -(* Theorem: Monoid (power_monoid b) *) -(* Proof: by power_monoid_abelian_monoid, AbelianMonoid_def *) -val power_monoid_monoid = store_thm( - "power_monoid_monoid", - ``!b. Monoid (power_monoid b)``, - metis_tac[power_monoid_abelian_monoid, AbelianMonoid_def]); - -(* ------------------------------------------------------------------------- *) -(* Logarithm is an isomorphism from Power Monoid to Addition Monoid *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: 1 < b ==> MonoidHomo (LOG b) (power_monoid b) (addition_monoid) *) -(* Proof: - By MonoidHomo_def, power_monoid_def, addition_monoid_def, this is to show: - (1) LOG b (b ** j * b ** j') = LOG b (b ** j) + LOG b (b ** j') - LOG b (b ** j * b ** j') - = LOG b (b ** (j + j')) by EXP_ADD - = j + j' by LOG_EXACT_EXP - = LOG b (b ** j) + LOG b (b ** j') by LOG_EXACT_EXP - (2) LOG b 1 = 0, true by LOG_1 -*) -val power_to_addition_homo = store_thm( - "power_to_addition_homo", - ``!b. 1 < b ==> MonoidHomo (LOG b) (power_monoid b) (addition_monoid)``, - rw[MonoidHomo_def, power_monoid_def, addition_monoid_def] >- - rw[LOG_EXACT_EXP, GSYM EXP_ADD] >> - rw[LOG_1]); - -(* Theorem: 1 < b ==> MonoidIso (LOG b) (power_monoid b) (addition_monoid) *) -(* Proof: - By MonoidIso_def, this is to show: - (1) MonoidHomo (LOG b) (power_monoid b) addition_monoid - This is true by power_to_addition_homo - (2) BIJ (LOG b) (power_monoid b).carrier addition_monoid.carrier - By BIJ_DEF, this is to show: - (1) INJ (LOG b) {b ** j | j IN univ(:num)} univ(:num) - By INJ_DEF, this is to show: - LOG b (b ** j) = LOG b (b ** j') ==> b ** j = b ** j' - LOG b (b ** j) = LOG b (b ** j') - ==> j = j' by LOG_EXACT_EXP - ==> b ** j = b ** j' - (2) SURJ (LOG b) {b ** j | j IN univ(:num)} univ(:num) - By SURJ_DEF, this is to show: - ?y. (?j. y = b ** j) /\ (LOG b y = x) - Let j = x, y = b ** x, then true by LOG_EXACT_EXP -*) -val power_to_addition_iso = store_thm( - "power_to_addition_iso", - ``!b. 1 < b ==> MonoidIso (LOG b) (power_monoid b) (addition_monoid)``, - rw[MonoidIso_def] >- - rw[power_to_addition_homo] >> - rw_tac std_ss[BIJ_DEF, power_monoid_def, addition_monoid_def] >| [ - rw[INJ_DEF] >> - rfs[LOG_EXACT_EXP], - rw[SURJ_DEF] >> - metis_tac[LOG_EXACT_EXP] - ]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/monoid/monoidMapScript.sml b/examples/algebra/monoid/monoidMapScript.sml deleted file mode 100644 index 923138244a..0000000000 --- a/examples/algebra/monoid/monoidMapScript.sml +++ /dev/null @@ -1,1338 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Monoid Maps *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "monoidMap"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* Get dependent theories local *) -(* val _ = load "monoidOrderTheory"; *) -open monoidTheory monoidOrderTheory; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- from monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- from monoidTheory *) *) -open helperNumTheory helperSetTheory; - - -(* ------------------------------------------------------------------------- *) -(* Monoid Maps Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - H = h.carrier - o = binary operation of homo_monoid: (homo_monoid g f).op - #i = identity of homo_monoid: (homo_monoid g f).id - fG = carrier of homo_monoid: (homo_monoid g f).carrier -*) -(* Definitions and Theorems (# are exported): - - Homomorphism, isomorphism, endomorphism, automorphism and submonoid: - MonoidHomo_def |- !f g h. MonoidHomo f g h <=> - (!x. x IN G ==> f x IN h.carrier) /\ (f #e = h.id) /\ - !x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y)) - MonoidIso_def |- !f g h. MonoidIso f g h <=> MonoidHomo f g h /\ BIJ f G h.carrier - MonoidEndo_def |- !f g. MonoidEndo f g <=> MonoidHomo f g g - MonoidAuto_def |- !f g. MonoidAuto f g <=> MonoidIso f g g - submonoid_def |- !h g. submonoid h g <=> MonoidHomo I h g - - Monoid Homomorphisms: - monoid_homo_id |- !f g h. MonoidHomo f g h ==> (f #e = h.id) - monoid_homo_element |- !f g h. MonoidHomo f g h ==> !x. x IN G ==> f x IN h.carrier - monoid_homo_cong |- !g h f1 f2. Monoid g /\ Monoid h /\ (!x. x IN G ==> (f1 x = f2 x)) ==> - (MonoidHomo f1 g h <=> MonoidHomo f2 g h) - monoid_homo_I_refl |- !g. MonoidHomo I g g - monoid_homo_trans |- !g h k f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k - monoid_homo_sym |- !g h f. Monoid g /\ MonoidHomo f g h /\ BIJ f G h.carrier ==> MonoidHomo (LINV f G) h g - monoid_homo_compose |- !g h k f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k - monoid_homo_exp |- !g h f. Monoid g /\ MonoidHomo f g h ==> - !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n - - Monoid Isomorphisms: - monoid_iso_property |- !f g h. MonoidIso f g h <=> - MonoidHomo f g h /\ !y. y IN h.carrier ==> ?!x. x IN G /\ (f x = y) - monoid_iso_id |- !f g h. MonoidIso f g h ==> (f #e = h.id) - monoid_iso_element |- !f g h. MonoidIso f g h ==> !x. x IN G ==> f x IN h.carrier - monoid_iso_monoid |- !g h f. Monoid g /\ MonoidIso f g h ==> Monoid h - monoid_iso_I_refl |- !g. MonoidIso I g g - monoid_iso_trans |- !g h k f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k - monoid_iso_sym |- !g h f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g - monoid_iso_compose |- !g h k f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k - monoid_iso_exp |- !g h f. Monoid g /\ MonoidIso f g h ==> - !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n - monoid_iso_linv_iso |- !g h f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g - monoid_iso_bij |- !g h f. MonoidIso f g h ==> BIJ f G h.carrier - monoid_iso_eq_id |- !g h f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> - !x. x IN G ==> ((f x = h.id) <=> (x = #e)) - monoid_iso_order |- !g h f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> - !x. x IN G ==> (order h (f x) = ord x) - monoid_iso_card_eq |- !g h f. MonoidIso f g h /\ FINITE G ==> (CARD G = CARD h.carrier) - - Monoid Automorphisms: - monoid_auto_id |- !f g. MonoidAuto f g ==> (f #e = #e) - monoid_auto_element |- !f g. MonoidAuto f g ==> !x. x IN G ==> f x IN G - monoid_auto_compose |- !g f1 f2. MonoidAuto f1 g /\ MonoidAuto f2 g ==> MonoidAuto (f1 o f2) g - monoid_auto_exp |- !g f. Monoid g /\ MonoidAuto f g ==> - !x. x IN G ==> !n. f (x ** n) = f x ** n - monoid_auto_I |- !g. MonoidAuto I g - monoid_auto_linv_auto|- !g f. Monoid g /\ MonoidAuto f g ==> MonoidAuto (LINV f G) g - monoid_auto_bij |- !g f. MonoidAuto f g ==> f PERMUTES G - monoid_auto_order |- !g f. Monoid g /\ MonoidAuto f g ==> - !x. x IN G ==> (ord (f x) = ord x) - - Submonoids: - submonoid_eqn |- !g h. submonoid h g <=> H SUBSET G /\ - (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ (h.id = #e) - submonoid_subset |- !g h. submonoid h g ==> H SUBSET G - submonoid_homo_homo |- !g h k f. submonoid h g /\ MonoidHomo f g k ==> MonoidHomo f h k - submonoid_refl |- !g. submonoid g g - submonoid_trans |- !g h k. submonoid g h /\ submonoid h k ==> submonoid g k - submonoid_I_antisym |- !g h. submonoid h g /\ submonoid g h ==> MonoidIso I h g - submonoid_carrier_antisym |- !g h. submonoid h g /\ G SUBSET H ==> MonoidIso I h g - submonoid_order_eqn |- !g h. Monoid g /\ Monoid h /\ submonoid h g ==> - !x. x IN H ==> (order h x = ord x) - - Homomorphic Image of Monoid: - image_op_def |- !g f x y. image_op g f x y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)) - image_op_inj |- !g f. INJ f G univ(:'b) ==> - !x y. x IN G /\ y IN G ==> (image_op g f (f x) (f y) = f (x * y)) - homo_monoid_def |- !g f. homo_monoid g f = <|carrier := IMAGE f G; op := image_op g f; id := f #e|> - homo_monoid_property |- !g f. (fG = IMAGE f G) /\ - (!x y. x IN fG /\ y IN fG ==> - (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))) /\ - (#i = f #e) - homo_monoid_element |- !g f x. x IN G ==> f x IN fG - homo_monoid_id |- !g f. f #e = #i - homo_monoid_op_inj |- !g f. INJ f G univ(:'b) ==> !x y. x IN G /\ y IN G ==> (f (x * y) = f x o f y) - homo_monoid_I |- !g. MonoidIso I (homo_group g I) g - - homo_monoid_closure |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> - !x y. x IN fG /\ y IN fG ==> x o y IN fG - homo_monoid_assoc |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> - !x y z. x IN fG /\ y IN fG /\ z IN fG ==> ((x o y) o z = x o y o z) - homo_monoid_id_property |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> #i IN fG /\ - !x. x IN fG ==> (#i o x = x) /\ (x o #i = x) - homo_monoid_comm |- !g f. AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> - !x y. x IN fG /\ y IN fG ==> (x o y = y o x) - homo_monoid_monoid |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> Monoid (homo_monoid g f) - homo_monoid_abelian_monoid |- !g f. AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> - AbelianMonoid (homo_monoid g f) - homo_monoid_by_inj |- !g f. Monoid g /\ INJ f G univ(:'b) ==> MonoidHomo f g (homo_monoid g f) - - Weaker form of Homomorphic of Monoid and image of identity: - WeakHomo_def |- !f g h. WeakHomo f g h <=> - (!x. x IN G ==> f x IN h.carrier) /\ - !x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y)) - WeakIso_def |- !f g h. WeakIso f g h <=> WeakHomo f g h /\ BIJ f G h.carrier - monoid_weak_iso_id |- !f g h. Monoid g /\ Monoid h /\ WeakIso f g h ==> (f #e = h.id) - - Injective Image of Monoid: - monoid_inj_image_def |- !g f. monoid_inj_image g f = - <|carrier := IMAGE f G; - op := (let t = LINV f G in \x y. f (t x * t y)); - id := f #e - |> - monoid_inj_image_monoid |- !g f. Monoid g /\ INJ f G univ(:'b) ==> Monoid (monoid_inj_image g f) - monoid_inj_image_abelian_monoid |- !g f. AbelianMonoid g /\ INJ f G univ(:'b) ==> AbelianMonoid (monoid_inj_image g f) - monoid_inj_image_monoid_homo |- !g f. INJ f G univ(:'b) ==> MonoidHomo f g (monoid_inj_image g f) -*) - -(* ------------------------------------------------------------------------- *) -(* Homomorphism, isomorphism, endomorphism, automorphism and submonoid. *) -(* ------------------------------------------------------------------------- *) - -(* val _ = overload_on ("H", ``h.carrier``); - -- type_of ``H``; -> val it = ``:'a -> bool`` : hol_type - -then MonoidIso f g h = MonoidHomo f g h /\ BIJ f G H -will make MonoidIso apply only for f: 'a -> 'a. - -will need val _ = overload_on ("H", ``(h:'b monoid).carrier``); -*) - -(* A function f from g to h is a homomorphism if monoid properties are preserved. *) -(* For monoids, need to ensure that identity is preserved, too. See: monoid_weak_iso_id. *) -val MonoidHomo_def = Define` - MonoidHomo (f:'a -> 'b) (g:'a monoid) (h:'b monoid) <=> - (!x. x IN G ==> f x IN h.carrier) /\ - (!x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y))) /\ - (f #e = h.id) -`; -(* -If MonoidHomo_def uses the condition: !x y. f (x * y) = h.op (f x) (f y) -this will mean a corresponding change in GroupHomo_def, but then -in quotientGroup <> will give a goal: -h <= g ==> x * y * H = (x * H) o (y * H) with no qualification on x, y! -*) - -(* A function f from g to h is an isomorphism if f is a bijective homomorphism. *) -val MonoidIso_def = Define` - MonoidIso f g h <=> MonoidHomo f g h /\ BIJ f G h.carrier -`; - -(* A monoid homomorphism from g to g is an endomorphism. *) -val MonoidEndo_def = Define `MonoidEndo f g <=> MonoidHomo f g g`; - -(* A monoid isomorphism from g to g is an automorphism. *) -val MonoidAuto_def = Define `MonoidAuto f g <=> MonoidIso f g g`; - -(* A submonoid h of g if identity is a homomorphism from h to g *) -val submonoid_def = Define `submonoid h g <=> MonoidHomo I h g`; - -(* ------------------------------------------------------------------------- *) -(* Monoid Homomorphisms *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MonoidHomo f g h ==> (f #e = h.id) *) -(* Proof: by MonoidHomo_def. *) -val monoid_homo_id = store_thm( - "monoid_homo_id", - ``!f g h. MonoidHomo f g h ==> (f #e = h.id)``, - rw[MonoidHomo_def]); - -(* Theorem: MonoidHomo f g h ==> !x. x IN G ==> f x IN h.carrier *) -(* Proof: by MonoidHomo_def *) -val monoid_homo_element = store_thm( - "monoid_homo_element", - ``!f g h. MonoidHomo f g h ==> !x. x IN G ==> f x IN h.carrier``, - rw_tac std_ss[MonoidHomo_def]); - -(* Theorem: Monoid g /\ Monoid h /\ (!x. x IN G ==> (f1 x = f2 x)) ==> (MonoidHomo f1 g h = MonoidHomo f2 g h) *) -(* Proof: by MonoidHomo_def, monoid_op_element, monoid_id_element *) -val monoid_homo_cong = store_thm( - "monoid_homo_cong", - ``!g h f1 f2. Monoid g /\ Monoid h /\ (!x. x IN G ==> (f1 x = f2 x)) ==> - (MonoidHomo f1 g h = MonoidHomo f2 g h)``, - rw_tac std_ss[MonoidHomo_def, EQ_IMP_THM] >- - metis_tac[monoid_op_element] >- - metis_tac[monoid_id_element] >- - metis_tac[monoid_op_element] >> - metis_tac[monoid_id_element]); - -(* Theorem: MonoidHomo I g g *) -(* Proof: by MonoidHomo_def. *) -val monoid_homo_I_refl = store_thm( - "monoid_homo_I_refl", - ``!g:'a monoid. MonoidHomo I g g``, - rw[MonoidHomo_def]); - -(* Theorem: MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo f2 o f1 g k *) -(* Proof: true by MonoidHomo_def. *) -val monoid_homo_trans = store_thm( - "monoid_homo_trans", - ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). - !f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k``, - rw[MonoidHomo_def]); - -(* Theorem: Monoid g /\ MonoidHomo f g h /\ BIJ f G h.carrier ==> MonoidHomo (LINV f G) h g *) -(* Proof: - Note BIJ f G h.carrier - ==> BIJ (LINV f G) h.carrier G by BIJ_LINV_BIJ - By MonoidHomo_def, this is to show: - (1) x IN h.carrier ==> LINV f G x IN G - With BIJ (LINV f G) h.carrier G - ==> INJ (LINV f G) h.carrier G by BIJ_DEF - ==> x IN h.carrier ==> LINV f G x IN G by INJ_DEF - (2) x IN h.carrier /\ y IN h.carrier ==> LINV f G (h.op x y) = LINV f G x * LINV f G y - With x IN h.carrier - ==> ?x1. (x = f x1) /\ x1 IN G by BIJ_DEF, SURJ_DEF - With y IN h.carrier - ==> ?y1. (y = f y1) /\ y1 IN G by BIJ_DEF, SURJ_DEF - and x1 * y1 IN G by monoid_op_element - LINV f G (h.op x y) - = LINV f G (f (x1 * y1)) by MonoidHomo_def - = x1 * y1 by BIJ_LINV_THM, x1 * y1 IN G - = (LINV f G (f x1)) * (LINV f G (f y1)) by BIJ_LINV_THM, x1 IN G, y1 IN G - = (LINV f G x) * (LINV f G y) by x = f x1, y = f y1. - (3) LINV f G h.id = #e - Since #e IN G by monoid_id_element - LINV f G h.id - = LINV f G (f #e) by MonoidHomo_def - = #e by BIJ_LINV_THM -*) -val monoid_homo_sym = store_thm( - "monoid_homo_sym", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidHomo f g h /\ BIJ f G h.carrier ==> - MonoidHomo (LINV f G) h g``, - rpt strip_tac >> - `BIJ (LINV f G) h.carrier G` by rw[BIJ_LINV_BIJ] >> - fs[MonoidHomo_def] >> - rpt strip_tac >- - metis_tac[BIJ_DEF, INJ_DEF] >- - (`?x1. (x = f x1) /\ x1 IN G` by metis_tac[BIJ_DEF, SURJ_DEF] >> - `?y1. (y = f y1) /\ y1 IN G` by metis_tac[BIJ_DEF, SURJ_DEF] >> - `g.op x1 y1 IN G` by rw[] >> - metis_tac[BIJ_LINV_THM]) >> - `#e IN G` by rw[] >> - metis_tac[BIJ_LINV_THM]); - -Theorem monoid_homo_sym_any: - Monoid g /\ MonoidHomo f g h /\ - (!x. x IN h.carrier ==> i x IN g.carrier /\ f (i x) = x) /\ - (!x. x IN g.carrier ==> i (f x) = x) - ==> - MonoidHomo i h g -Proof - rpt strip_tac >> fs[MonoidHomo_def] - \\ metis_tac[Monoid_def] -QED - -(* Theorem: MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k *) -(* Proof: by MonoidHomo_def *) -val monoid_homo_compose = store_thm( - "monoid_homo_compose", - ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). - !f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k``, - rw_tac std_ss[MonoidHomo_def]); -(* This is the same as monoid_homo_trans *) - -(* Theorem: Monoid g /\ MonoidHomo f g h ==> !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n *) -(* Proof: - By induction on n. - Base: f (x ** 0) = h.exp (f x) 0 - f (x ** 0) - = f #e by monoid_exp_0 - = h.id by monoid_homo_id - = h.exp (f x) 0 by monoid_exp_0 - Step: f (x ** SUC n) = h.exp (f x) (SUC n) - Note x ** n IN G by monoid_exp_element - f (x ** SUC n) - = f (x * x ** n) by monoid_exp_SUC - = h.op (f x) (f (x ** n)) by MonoidHomo_def - = h.op (f x) (h.exp (f x) n) by induction hypothesis - = h.exp (f x) (SUC n) by monoid_exp_SUC -*) -val monoid_homo_exp = store_thm( - "monoid_homo_exp", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidHomo f g h ==> - !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n``, - rpt strip_tac >> - Induct_on `n` >- - rw[monoid_exp_0, monoid_homo_id] >> - fs[monoid_exp_SUC, MonoidHomo_def]); - -(* ------------------------------------------------------------------------- *) -(* Monoid Isomorphisms *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MonoidIso f g h <=> MonoidHomo f g h /\ (!y. y IN h.carrier ==> ?!x. x IN G /\ (f x = y)) *) -(* Proof: - This is to prove: - (1) BIJ f G H /\ y IN H ==> ?!x. x IN G /\ (f x = y) - true by INJ_DEF and SURJ_DEF. - (2) !y. y IN H /\ MonoidHomo f g h ==> ?!x. x IN G /\ (f x = y) ==> BIJ f G H - true by INJ_DEF and SURJ_DEF, and - x IN G /\ GroupHomo f g h ==> f x IN H by MonoidHomo_def -*) -val monoid_iso_property = store_thm( - "monoid_iso_property", - ``!f g h. MonoidIso f g h <=> MonoidHomo f g h /\ (!y. y IN h.carrier ==> ?!x. x IN G /\ (f x = y))``, - rw_tac std_ss[MonoidIso_def, EQ_IMP_THM] >- - metis_tac[BIJ_THM] >> - rw[BIJ_THM] >> - metis_tac[MonoidHomo_def]); - -(* Note: all these proofs so far do not require the condition: f #e = h.id in MonoidHomo_def, - but evetually it should, as this is included in definitions of all resources. *) - -(* Theorem: MonoidIso f g h ==> (f #e = h.id) *) -(* Proof: by MonoidIso_def, monoid_homo_id. *) -val monoid_iso_id = store_thm( - "monoid_iso_id", - ``!f g h. MonoidIso f g h ==> (f #e = h.id)``, - rw_tac std_ss[MonoidIso_def, monoid_homo_id]); - -(* Theorem: MonoidIso f g h ==> !x. x IN G ==> f x IN h.carrier *) -(* Proof: by MonoidIso_def, monoid_homo_element *) -val monoid_iso_element = store_thm( - "monoid_iso_element", - ``!f g h. MonoidIso f g h ==> !x. x IN G ==> f x IN h.carrier``, - metis_tac[MonoidIso_def, monoid_homo_element]); - -(* Theorem: Monoid g /\ MonoidIso f g h ==> Monoid h *) -(* Proof: - This is to show: - (1) x IN h.carrier /\ y IN h.carrier ==> h.op x y IN h.carrier - Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property - ?y'. y' IN G /\ (f y' = y) by monoid_iso_property - h.op x y = f (x' * y') by MonoidHomo_def - As x' * y' IN G by monoid_op_element - hence f (x' * y') IN h.carrier by MonoidHomo_def - (2) x IN h.carrier /\ y IN h.carrier /\ z IN h.carrier ==> h.op (h.op x y) z = h.op x (h.op y z) - Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property - ?y'. y' IN G /\ (f y' = y) by monoid_iso_property - ?z'. z' IN G /\ (f z' = z) by monoid_iso_property - as x' * y' IN G by monoid_op_element - and f (x' * y') IN h.carrier by MonoidHomo_def - ?!t. t IN G /\ f t = f (x' * y') by monoid_iso_property - i.e. t = x' * y' by uniqueness - hence h.op (h.op x y) z = f (x' * y' * z') by MonoidHomo_def - Similary, - as y' * z' IN G by monoid_op_element - and f (y' * z') IN h.carrier by MonoidHomo_def - ?!s. s IN G /\ f s = f (y' * z') by monoid_iso_property - i.e. s = y' * z' by uniqueness - and h.op x (h.op y z) = f (x' * (y' * z')) by MonoidHomo_def - hence true by monoid_assoc. - (3) h.id IN h.carrier - Since #e IN G by monoid_id_element - (f #e) = h.id IN h.carrier by MonoidHomo_def - (4) x IN h.carrier ==> h.op h.id x = x - Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property - h.id IN h.carrier by monoid_id_element - ?!e. e IN G /\ f e = h.id = f #e by monoid_iso_property - i.e. e = #e by uniqueness - hence h.op h.id x = f (e * x') by MonoidHomo_def - = f (#e * x') - = f x' by monoid_lid - = x - (5) x IN h.carrier ==> h.op x h.id = x - Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property - h.id IN h.carrier by monoid_id_element - ?!e. e IN G /\ f e = h.id = f #e by monoid_iso_property - i.e. e = #e by uniqueness - hence h.op x h.id = f (x' * e) by MonoidHomo_def - = f (x' * #e) - = f x' by monoid_rid - = x -*) -val monoid_iso_monoid = store_thm( - "monoid_iso_monoid", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> Monoid h``, - rw[monoid_iso_property] >> - `(!x. x IN G ==> f x IN h.carrier) /\ - (!x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y))) /\ - (f #e = h.id)` by metis_tac[MonoidHomo_def] >> - rw_tac std_ss[Monoid_def] >- - metis_tac[monoid_op_element] >- - (`?x'. x' IN G /\ (f x' = x)` by metis_tac[] >> - `?y'. y' IN G /\ (f y' = y)` by metis_tac[] >> - `?z'. z' IN G /\ (f z' = z)` by metis_tac[] >> - `?t. t IN G /\ (t = x' * y')` by metis_tac[monoid_op_element] >> - `h.op (h.op x y) z = f (x' * y' * z')` by metis_tac[] >> - `?s. s IN G /\ (s = y' * z')` by metis_tac[monoid_op_element] >> - `h.op x (h.op y z) = f (x' * (y' * z'))` by metis_tac[] >> - `x' * y' * z' = x' * (y' * z')` by rw[monoid_assoc] >> - metis_tac[]) >- - metis_tac[monoid_id_element, MonoidHomo_def] >- - metis_tac[monoid_lid, monoid_id_element] >> - metis_tac[monoid_rid, monoid_id_element]); - -(* Theorem: MonoidIso I g g *) -(* Proof: - By MonoidIso_def, this is to show: - (1) MonoidHomo I g g, true by monoid_homo_I_refl - (2) BIJ I R R, true by BIJ_I_SAME -*) -val monoid_iso_I_refl = store_thm( - "monoid_iso_I_refl", - ``!g:'a monoid. MonoidIso I g g``, - rw[MonoidIso_def, monoid_homo_I_refl, BIJ_I_SAME]); - -(* Theorem: MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k *) -(* Proof: - By MonoidIso_def, this is to show: - (1) MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k - True by monoid_homo_trans. - (2) BIJ f1 G h.carrier /\ BIJ f2 h.carrier k.carrier ==> BIJ (f2 o f1) G k.carrier - True by BIJ_COMPOSE. -*) -val monoid_iso_trans = store_thm( - "monoid_iso_trans", - ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). - !f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k``, - rw[MonoidIso_def] >- - metis_tac[monoid_homo_trans] >> - metis_tac[BIJ_COMPOSE]); - -(* Theorem: Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g *) -(* Proof: - By MonoidIso_def, this is to show: - (1) MonoidHomo f g h /\ BIJ f G h.carrier ==> MonoidHomo (LINV f G) h g - True by monoid_homo_sym. - (2) BIJ f G h.carrier ==> BIJ (LINV f G) h.carrier G - True by BIJ_LINV_BIJ -*) -val monoid_iso_sym = store_thm( - "monoid_iso_sym", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g``, - rw[MonoidIso_def, monoid_homo_sym, BIJ_LINV_BIJ]); - -(* Theorem: MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k *) -(* Proof: - By MonoidIso_def, this is to show: - (1) MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k - True by monoid_homo_compose. - (2) BIJ f1 G h.carrier /\ BIJ f2 h.carrier k.carrier ==> BIJ (f2 o f1) G k.carrier - True by BIJ_COMPOSE -*) -val monoid_iso_compose = store_thm( - "monoid_iso_compose", - ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). - !f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k``, - rw_tac std_ss[MonoidIso_def] >- - metis_tac[monoid_homo_compose] >> - metis_tac[BIJ_COMPOSE]); -(* This is the same as monoid_iso_trans. *) - -(* Theorem: Monoid g /\ MonoidIso f g h ==> !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n *) -(* Proof: by MonoidIso_def, monoid_homo_exp *) -val monoid_iso_exp = store_thm( - "monoid_iso_exp", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> - !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n``, - rw[MonoidIso_def, monoid_homo_exp]); - -(* Theorem: Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g *) -(* Proof: - By MonoidIso_def, MonoidHomo_def, this is to show: - (1) BIJ f G h.carrier /\ x IN h.carrier ==> LINV f G x IN G - True by BIJ_LINV_ELEMENT - (2) BIJ f G h.carrier /\ x IN h.carrier /\ y IN h.carrier ==> LINV f G (h.op x y) = LINV f G x * LINV f G y - Let x' = LINV f G x, y' = LINV f G y. - Then x' IN G /\ y' IN G by BIJ_LINV_ELEMENT - so x' * y' IN G by monoid_op_element - ==> f (x' * y') = h.op (f x') (f y') by MonoidHomo_def - = h.op x y by BIJ_LINV_THM - Thus LINV f G (h.op x y) - = LINV f G (f (x' * y')) by above - = x' * y' by BIJ_LINV_THM - (3) BIJ f G h.carrier ==> LINV f G h.id = #e - Note #e IN G by monoid_id_element - and f #e = h.id by MonoidHomo_def - ==> LINV f G h.id = #e by BIJ_LINV_THM - (4) BIJ f G h.carrier ==> BIJ (LINV f G) h.carrier G - True by BIJ_LINV_BIJ -*) -val monoid_iso_linv_iso = store_thm( - "monoid_iso_linv_iso", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g``, - rw_tac std_ss[MonoidIso_def, MonoidHomo_def] >- - metis_tac[BIJ_LINV_ELEMENT] >- - (qabbrev_tac `x' = LINV f G x` >> - qabbrev_tac `y' = LINV f G y` >> - metis_tac[BIJ_LINV_THM, BIJ_LINV_ELEMENT, monoid_op_element]) >- - metis_tac[BIJ_LINV_THM, monoid_id_element] >> - rw_tac std_ss[BIJ_LINV_BIJ]); -(* This is the same as monoid_iso_sym, just a direct proof. *) - -(* Theorem: MonoidIso f g h ==> BIJ f G h.carrier *) -(* Proof: by MonoidIso_def *) -val monoid_iso_bij = store_thm( - "monoid_iso_bij", - ``!(g:'a monoid) (h:'b monoid) f. MonoidIso f g h ==> BIJ f G h.carrier``, - rw_tac std_ss[MonoidIso_def]); - -(* Theorem: Monoid g /\ Monoid h /\ MonoidIso f g h ==> - !x. x IN G ==> ((f x = h.id) <=> (x = #e)) *) -(* Proof: - Note MonoidHomo f g h /\ BIJ f G h.carrier by MonoidIso_def - If part: x IN G /\ f x = h.id ==> x = #e - By monoid_id_unique, this is to show: - (1) !y. y IN G ==> y * x = y - Let z = y * x. - Then z IN G by monoid_op_element - f z = h.op (f y) (f x) by MonoidHomo_def - = h.op (f y) h.id by f x = h.id - = f y by monoid_homo_element, monoid_id - Thus z = y by BIJ_DEF, INJ_DEF - (2) !y. y IN G ==> x * y = y - Let z = x * y. - Then z IN G by monoid_op_element - f z = h.op (f x) (f y) by MonoidHomo_def - = h.op h.id (f y) by f x = h.id - = f y by monoid_homo_element, monoid_id - Thus z = y by BIJ_DEF, INJ_DEF - - Only-if part: f #e = h.id, true by monoid_homo_id -*) -val monoid_iso_eq_id = store_thm( - "monoid_iso_eq_id", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> - !x. x IN G ==> ((f x = h.id) <=> (x = #e))``, - rw[MonoidIso_def] >> - rw[EQ_IMP_THM] >| [ - rw[GSYM monoid_id_unique] >| [ - qabbrev_tac `z = x' * x` >> - `z IN G` by rw[Abbr`z`] >> - `f z = h.op (f x') (f x)` by fs[MonoidHomo_def, Abbr`z`] >> - `_ = f x'` by metis_tac[monoid_homo_element, monoid_id] >> - metis_tac[BIJ_DEF, INJ_DEF], - qabbrev_tac `z = x * x'` >> - `z IN G` by rw[Abbr`z`] >> - `f z = h.op (f x) (f x')` by fs[MonoidHomo_def, Abbr`z`] >> - `_ = f x'` by metis_tac[monoid_homo_element, monoid_id] >> - metis_tac[BIJ_DEF, INJ_DEF] - ], - rw[monoid_homo_id] - ]); - -(* Theorem: Monoid g /\ Monoid h /\ MonoidIso f g h ==> !x. x IN G ==> (order h (f x) = ord x) *) -(* Proof: - Let n = ord x, y = f x. - If n = 0, to show: order h y = 0. - By contradiction, suppose order h y <> 0. - Let m = order h y. - Note h.id = h.exp y m by order_property - = f (x ** m) by monoid_iso_exp - Thus x ** m = #e by monoid_iso_eq_id, monoid_exp_element - But x ** m <> #e by order_eq_0, ord x = 0 - This is a contradiction. - - If n <> 0, to show: order h y = n. - Note ord x = n - ==> (x ** n = #e) /\ - !m. 0 < m /\ m < n ==> x ** m <> #e by order_thm, 0 < n - Note h.exp y n = f (x ** n) by monoid_iso_exp - = f #e by x ** n = #e - = h.id by monoid_iso_id, [1] - Claim: !m. 0 < m /\ m < n ==> h.exp y m <> h.id - Proof: By contradiction, suppose h.exp y m = h.id. - Then f (x ** m) = h.exp y m by monoid_iso_exp - = h.id by above - or x ** m = #e by monoid_iso_eq_id, monoid_exp_element - But !m. 0 < m /\ m < n ==> x ** m <> #e by above - This is a contradiction. - - Thus by [1] and claim, order h y = n by order_thm -*) -val monoid_iso_order = store_thm( - "monoid_iso_order", - ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> - !x. x IN G ==> (order h (f x) = ord x)``, - rpt strip_tac >> - qabbrev_tac `n = ord x` >> - qabbrev_tac `y = f x` >> - Cases_on `n = 0` >| [ - spose_not_then strip_assume_tac >> - qabbrev_tac `m = order h y` >> - `0 < m` by decide_tac >> - `h.exp y m = h.id` by metis_tac[order_property] >> - `f (x ** m) = h.id` by metis_tac[monoid_iso_exp] >> - `x ** m = #e` by metis_tac[monoid_iso_eq_id, monoid_exp_element] >> - metis_tac[order_eq_0], - `0 < n` by decide_tac >> - `(x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e` by metis_tac[order_thm] >> - `h.exp y n = h.id` by metis_tac[monoid_iso_exp, monoid_iso_id] >> - `!m. 0 < m /\ m < n ==> h.exp y m <> h.id` by - (spose_not_then strip_assume_tac >> - `f (x ** m) = h.id` by metis_tac[monoid_iso_exp] >> - `x ** m = #e` by metis_tac[monoid_iso_eq_id, monoid_exp_element] >> - metis_tac[]) >> - metis_tac[order_thm] - ]); - -(* Theorem: MonoidIso f g h /\ FINITE G ==> (CARD G = CARD h.carrier) *) -(* Proof: by MonoidIso_def, FINITE_BIJ_CARD. *) -val monoid_iso_card_eq = store_thm( - "monoid_iso_card_eq", - ``!g:'a monoid h:'b monoid f. MonoidIso f g h /\ FINITE G ==> (CARD G = CARD h.carrier)``, - metis_tac[MonoidIso_def, FINITE_BIJ_CARD]); - -(* ------------------------------------------------------------------------- *) -(* Monoid Automorphisms *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: MonoidAuto f g ==> (f #e = #e) *) -(* Proof: by MonoidAuto_def, monoid_iso_id. *) -val monoid_auto_id = store_thm( - "monoid_auto_id", - ``!f g. MonoidAuto f g ==> (f #e = #e)``, - rw_tac std_ss[MonoidAuto_def, monoid_iso_id]); - -(* Theorem: MonoidAuto f g ==> !x. x IN G ==> f x IN G *) -(* Proof: by MonoidAuto_def, monoid_iso_element *) -val monoid_auto_element = store_thm( - "monoid_auto_element", - ``!f g. MonoidAuto f g ==> !x. x IN G ==> f x IN G``, - metis_tac[MonoidAuto_def, monoid_iso_element]); - -(* Theorem: MonoidAuto f1 g /\ MonoidAuto f2 g ==> MonoidAuto (f1 o f2) g *) -(* Proof: by MonoidAuto_def, monoid_iso_compose *) -val monoid_auto_compose = store_thm( - "monoid_auto_compose", - ``!(g:'a monoid). !f1 f2. MonoidAuto f1 g /\ MonoidAuto f2 g ==> MonoidAuto (f1 o f2) g``, - metis_tac[MonoidAuto_def, monoid_iso_compose]); - -(* Theorem: Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> !n. f (x ** n) = (f x) ** n *) -(* Proof: by MonoidAuto_def, monoid_iso_exp *) -val monoid_auto_exp = store_thm( - "monoid_auto_exp", - ``!f g. Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> !n. f (x ** n) = (f x) ** n``, - rw[MonoidAuto_def, monoid_iso_exp]); - -(* Theorem: MonoidAuto I g *) -(* Proof: - MonoidAuto I g - <=> MonoidIso I g g by MonoidAuto_def - <=> MonoidHomo I g g /\ BIJ f G G by MonoidIso_def - <=> T /\ BIJ f G G by MonoidHomo_def, I_THM - <=> T /\ T by BIJ_I_SAME -*) -val monoid_auto_I = store_thm( - "monoid_auto_I", - ``!(g:'a monoid). MonoidAuto I g``, - rw_tac std_ss[MonoidAuto_def, MonoidIso_def, MonoidHomo_def, BIJ_I_SAME]); - -(* Theorem: Monoid g /\ MonoidAuto f g ==> MonoidAuto (LINV f G) g *) -(* Proof: - MonoidAuto I g - ==> MonoidIso I g g by MonoidAuto_def - ==> MonoidIso (LINV f G) g by monoid_iso_linv_iso - ==> MonoidAuto (LINV f G) g by MonoidAuto_def -*) -val monoid_auto_linv_auto = store_thm( - "monoid_auto_linv_auto", - ``!(g:'a monoid) f. Monoid g /\ MonoidAuto f g ==> MonoidAuto (LINV f G) g``, - rw_tac std_ss[MonoidAuto_def, monoid_iso_linv_iso]); - -(* Theorem: MonoidAuto f g ==> f PERMUTES G *) -(* Proof: by MonoidAuto_def, MonoidIso_def *) -val monoid_auto_bij = store_thm( - "monoid_auto_bij", - ``!g:'a monoid. !f. MonoidAuto f g ==> f PERMUTES G``, - rw_tac std_ss[MonoidAuto_def, MonoidIso_def]); - -(* Theorem: Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> (ord (f x) = ord x) *) -(* Proof: by MonoidAuto_def, monoid_iso_order. *) -val monoid_auto_order = store_thm( - "monoid_auto_order", - ``!(g:'a monoid) f. Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> (ord (f x) = ord x)``, - rw[MonoidAuto_def, monoid_iso_order]); - -(* ------------------------------------------------------------------------- *) -(* Submonoids. *) -(* ------------------------------------------------------------------------- *) - -(* Use H to denote h.carrier *) -val _ = overload_on ("H", ``(h:'a monoid).carrier``); - -(* Theorem: submonoid h g ==> H SUBSET G /\ (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ (h.id = #e) *) -(* Proof: - submonoid h g - <=> MonoidHomo I h g by submonoid_def - <=> (!x. x IN H ==> I x IN G) /\ - (!x y. x IN H /\ y IN H ==> (I (h.op x y) = (I x) * (I y))) /\ - (h.id = I #e) by MonoidHomo_def - <=> (!x. x IN H ==> x IN G) /\ - (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ - (h.id = #e) by I_THM - <=> H SUBSET G - (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ - (h.id = #e) by SUBSET_DEF -*) -val submonoid_eqn = store_thm( - "submonoid_eqn", - ``!(g:'a monoid) (h:'a monoid). submonoid h g <=> - H SUBSET G /\ (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ (h.id = #e)``, - rw_tac std_ss[submonoid_def, MonoidHomo_def, SUBSET_DEF]); - -(* Theorem: submonoid h g ==> H SUBSET G *) -(* Proof: by submonoid_eqn *) -val submonoid_subset = store_thm( - "submonoid_subset", - ``!(g:'a monoid) (h:'a monoid). submonoid h g ==> H SUBSET G``, - rw_tac std_ss[submonoid_eqn]); - -(* Theorem: submonoid h g /\ MonoidHomo f g k ==> MonoidHomo f h k *) -(* Proof: - Note H SUBSET G by submonoid_subset - or !x. x IN H ==> x IN G by SUBSET_DEF - By MonoidHomo_def, this is to show: - (1) x IN H ==> f x IN k.carrier - True by MonoidHomo_def, MonoidHomo f g k - (2) x IN H /\ y IN H /\ f (h.op x y) = k.op (f x) (f y) - Note x IN H ==> x IN G by above - and y IN H ==> y IN G by above - f (h.op x y) - = f (x * y) by submonoid_eqn - = k.op (f x) (f y) by MonoidHomo_def - (3) f h.id = k.id - f (h.id) - = f #e by submonoid_eqn - = k.id by MonoidHomo_def -*) -val submonoid_homo_homo = store_thm( - "submonoid_homo_homo", - ``!(g:'a monoid) (h:'a monoid) (k:'b monoid) f. submonoid h g /\ MonoidHomo f g k ==> MonoidHomo f h k``, - rw_tac std_ss[submonoid_def, MonoidHomo_def]); - -(* Theorem: submonoid g g *) -(* Proof: - By submonoid_def, this is to show: - MonoidHomo I g g, true by monoid_homo_I_refl. -*) -val submonoid_refl = store_thm( - "submonoid_refl", - ``!g:'a monoid. submonoid g g``, - rw[submonoid_def, monoid_homo_I_refl]); - -(* Theorem: submonoid g h /\ submonoid h k ==> submonoid g k *) -(* Proof: - By submonoid_def, this is to show: - MonoidHomo I g h /\ MonoidHomo I h k ==> MonoidHomo I g k - Since I o I = I by combinTheory.I_o_ID - This is true by monoid_homo_trans -*) -val submonoid_trans = store_thm( - "submonoid_trans", - ``!(g h k):'a monoid. submonoid g h /\ submonoid h k ==> submonoid g k``, - prove_tac[submonoid_def, combinTheory.I_o_ID, monoid_homo_trans]); - -(* Theorem: submonoid h g /\ submonoid g h ==> MonoidIso I h g *) -(* Proof: - By submonoid_def, MonoidIso_def, this is to show: - MonoidHomo I h g /\ MonoidHomo I g h ==> BIJ I H G - By BIJ_DEF, INJ_DEF, SURJ_DEF, this is to show: - (1) x IN H ==> x IN G, true by submonoid_subset, submonoid h g - (2) x IN G ==> x IN H, true by submonoid_subset, submonoid g h -*) -val submonoid_I_antisym = store_thm( - "submonoid_I_antisym", - ``!(g:'a monoid) h. submonoid h g /\ submonoid g h ==> MonoidIso I h g``, - rw_tac std_ss[submonoid_def, MonoidIso_def] >> - fs[MonoidHomo_def] >> - rw_tac std_ss[BIJ_DEF, INJ_DEF, SURJ_DEF]); - -(* Theorem: submonoid h g /\ G SUBSET H ==> MonoidIso I h g *) -(* Proof: - By submonoid_def, MonoidIso_def, this is to show: - MonoidHomo I h g /\ G SUBSET H ==> BIJ I H G - By BIJ_DEF, INJ_DEF, SURJ_DEF, this is to show: - (1) x IN H ==> x IN G, true by submonoid_subset, submonoid h g - (2) x IN G ==> x IN H, true by G SUBSET H, given -*) -val submonoid_carrier_antisym = store_thm( - "submonoid_carrier_antisym", - ``!(g:'a monoid) h. submonoid h g /\ G SUBSET H ==> MonoidIso I h g``, - rpt (stripDup[submonoid_def]) >> - rw_tac std_ss[MonoidIso_def] >> - `H SUBSET G` by rw[submonoid_subset] >> - fs[MonoidHomo_def, SUBSET_DEF] >> - rw_tac std_ss[BIJ_DEF, INJ_DEF, SURJ_DEF]); - -(* Theorem: Monoid g /\ Monoid h /\ submonoid h g ==> !x. x IN H ==> (order h x = ord x) *) -(* Proof: - Note MonoidHomo I h g by submonoid_def - If ord x = 0, to show: order h x = 0. - By contradiction, suppose order h x <> 0. - Let n = order h x. - Then 0 < n by n <> 0 - and h.exp x n = h.id by order_property - or x ** n = #e by monoid_homo_exp, monoid_homo_id, I_THM - But x ** n <> #e by order_eq_0, ord x = 0 - This is a contradiction. - If ord x <> 0, to show: order h x = ord x. - Note 0 < ord x by ord x <> 0 - By order_thm, this is to show: - (1) h.exp x (ord x) = h.id - h.exp x (ord x) - = I (h.exp x (ord x)) by I_THM - = (I x) ** ord x by monoid_homo_exp - = x ** ord x by I_THM - = #e by order_property - = I (h.id) by monoid_homo_id - = h.id by I_THM - (2) 0 < m /\ m < ord x ==> h.exp x m <> h.id - By contradiction, suppose h.exp x m = h.id - h.exp x m - = I (h.exp x m) by I_THM - = (I x) ** m by monoid_homo_exp - = x ** m by I_THM - h.id = I (h.id) by I_THM - = #e by monoid_homo_id - Thus x ** m = #e by h.exp x m = h.id - But x ** m <> #e by order_thm - This is a contradiction. -*) -val submonoid_order_eqn = store_thm( - "submonoid_order_eqn", - ``!(g:'a monoid) h. Monoid g /\ Monoid h /\ submonoid h g ==> - !x. x IN H ==> (order h x = ord x)``, - rw[submonoid_def] >> - `!x. I x = x` by rw[] >> - Cases_on `ord x = 0` >| [ - spose_not_then strip_assume_tac >> - qabbrev_tac `n = order h x` >> - `0 < n` by decide_tac >> - `h.exp x n = h.id` by rw[order_property, Abbr`n`] >> - `x ** n = #e` by metis_tac[monoid_homo_exp, monoid_homo_id] >> - metis_tac[order_eq_0], - `0 < ord x` by decide_tac >> - rw[order_thm] >- - metis_tac[monoid_homo_exp, order_property, monoid_homo_id] >> - spose_not_then strip_assume_tac >> - `x ** m = #e` by metis_tac[monoid_homo_exp, monoid_homo_id] >> - metis_tac[order_thm] - ]); - -(* ------------------------------------------------------------------------- *) -(* Homomorphic Image of Monoid. *) -(* ------------------------------------------------------------------------- *) - -(* Define an operation on images *) -val image_op_def = Define` - image_op (g:'a monoid) (f:'a -> 'b) x y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)) -`; - -(* Theorem: INJ f G univ(:'b) ==> !x y. x IN G /\ y IN G ==> (image_op g f (f x) (f y) = f (x * y)) *) -(* Proof: - Note x = CHOICE (preimage f G (f x)) by preimage_inj_choice - and y = CHOICE (preimage f G (f y)) by preimage_inj_choice - image_op g f (f x) (f y) - = f (CHOICE (preimage f G (f x)) * CHOICE (preimage f G (f y)) by image_op_def - = f (x * y) by above -*) -val image_op_inj = store_thm( - "image_op_inj", - ``!g:'a monoid f. INJ f G univ(:'b) ==> - !x y. x IN G /\ y IN G ==> (image_op g f (f x) (f y) = f (g.op x y))``, - rw[image_op_def, preimage_inj_choice]); - -(* Define the homomorphic image of a monoid. *) -val homo_monoid_def = Define` - homo_monoid (g:'a monoid) (f:'a -> 'b) = - <| carrier := IMAGE f G; - op := image_op g f; - id := f #e - |> -`; - -(* set overloading *) -val _ = overload_on ("o", ``(homo_monoid (g:'a monoid) (f:'a -> 'b)).op``); -val _ = overload_on ("#i", ``(homo_monoid (g:'a monoid) (f:'a -> 'b)).id``); -val _ = overload_on ("fG", ``(homo_monoid (g:'a monoid) (f:'a -> 'b)).carrier``); - -(* Theorem: Properties of homo_monoid. *) -(* Proof: by homo_monoid_def and image_op_def. *) -val homo_monoid_property = store_thm( - "homo_monoid_property", - ``!(g:'a monoid) (f:'a -> 'b). (fG = IMAGE f G) /\ - (!x y. x IN fG /\ y IN fG ==> (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))) /\ - (#i = f #e)``, - rw[homo_monoid_def, image_op_def]); - -(* Theorem: !x. x IN G ==> f x IN fG *) -(* Proof: by homo_monoid_def, IN_IMAGE *) -val homo_monoid_element = store_thm( - "homo_monoid_element", - ``!(g:'a monoid) (f:'a -> 'b). !x. x IN G ==> f x IN fG``, - rw[homo_monoid_def]); - -(* Theorem: f #e = #i *) -(* Proof: by homo_monoid_def *) -val homo_monoid_id = store_thm( - "homo_monoid_id", - ``!(g:'a monoid) (f:'a -> 'b). f #e = #i``, - rw[homo_monoid_def]); - -(* Theorem: INJ f G univ(:'b) ==> - !x y. x IN G /\ y IN G ==> (f (x * y) = (f x) o (f y)) *) -(* Proof: - Note x = CHOICE (preimage f G (f x)) by preimage_inj_choice - and y = CHOICE (preimage f G (f y)) by preimage_inj_choice - f (x * y) - = f (CHOICE (preimage f G (f x)) * CHOICE (preimage f G (f y)) by above - = (f x) o (f y) by homo_monoid_property -*) -val homo_monoid_op_inj = store_thm( - "homo_monoid_op_inj", - ``!(g:'a monoid) (f:'a -> 'b). INJ f G univ(:'b) ==> - !x y. x IN G /\ y IN G ==> (f (x * y) = (f x) o (f y))``, - rw[homo_monoid_property, preimage_inj_choice]); - -(* Theorem: MonoidIso I (homo_monoid g I) g *) -(* Proof: - Note IMAGE I G = G by IMAGE_I - and INJ I G univ(:'a) by INJ_I - and !x. I x = x by I_THM - By MonoidIso_def, this is to show: - (1) MonoidHomo I (homo_monoid g I) g - By MonoidHomo_def, homo_monoid_def, this is to show: - x IN G /\ y IN G ==> image_op g I x y = x * y - This is true by image_op_inj - (2) BIJ I (homo_group g I).carrier G - (homo_group g I).carrier - = IMAGE I G by homo_monoid_property - = G by above, IMAGE_I - This is true by BIJ_I_SAME -*) -val homo_monoid_I = store_thm( - "homo_monoid_I", - ``!g:'a monoid. MonoidIso I (homo_monoid g I) g``, - rpt strip_tac >> - `IMAGE I G = G` by rw[] >> - `INJ I G univ(:'a)` by rw[INJ_I] >> - `!x. I x = x` by rw[] >> - rw_tac std_ss[MonoidIso_def] >| [ - rw_tac std_ss[MonoidHomo_def, homo_monoid_def] >> - metis_tac[image_op_inj], - rw[homo_monoid_property, BIJ_I_SAME] - ]); - -(* Theorem: [Closure] Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> x IN fG /\ y IN fG ==> x o y IN fG *) -(* Proof: - Let a = CHOICE (preimage f G x), - b = CHOICE (preimage f G y). - Then a IN G /\ x = f a by preimage_choice_property - b IN G /\ y = f b by preimage_choice_property - x o y - = (f a) o (f b) - = f (a * b) by homo_monoid_property - Note a * b IN G by monoid_op_element - so f (a * b) IN fG by homo_monoid_element -*) -val homo_monoid_closure = store_thm( - "homo_monoid_closure", - ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> - !x y. x IN fG /\ y IN fG ==> x o y IN fG``, - rw_tac std_ss[homo_monoid_property] >> - rw[preimage_choice_property]); - -(* Theorem: [Associative] Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> - x IN fG /\ y IN fG /\ z IN fG ==> (x o y) o z = x o (y o z) *) -(* Proof: - By MonoidHomo_def, - !x. x IN G ==> f x IN fG - !x y. x IN G /\ y IN G ==> (f (x * y) = f x o f y) - Let a = CHOICE (preimage f G x), - b = CHOICE (preimage f G y), - c = CHOICE (preimage f G z). - Then a IN G /\ x = f a by preimage_choice_property - b IN G /\ y = f b by preimage_choice_property - c IN G /\ z = f c by preimage_choice_property - (x o y) o z - = ((f a) o (f b)) o (f c) by expanding x, y, z - = f (a * b) o (f c) by homo_monoid_property - = f (a * b * c) by homo_monoid_property - = f (a * (b * c)) by monoid_assoc - = (f a) o f (b * c) by homo_monoid_property - = (f a) o ((f b) o (f c)) by homo_monoid_property - = x o (y o z) by contracting x, y, z -*) -val homo_monoid_assoc = store_thm( - "homo_monoid_assoc", - ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> - !x y z. x IN fG /\ y IN fG /\ z IN fG ==> ((x o y) o z = x o (y o z))``, - rw_tac std_ss[MonoidHomo_def] >> - `(fG = IMAGE f G) /\ !x y. x IN fG /\ y IN fG ==> (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))` by rw[homo_monoid_property] >> - qabbrev_tac `a = CHOICE (preimage f G x)` >> - qabbrev_tac `b = CHOICE (preimage f G y)` >> - qabbrev_tac `c = CHOICE (preimage f G z)` >> - `a IN G /\ (f a = x)` by metis_tac[preimage_choice_property] >> - `b IN G /\ (f b = y)` by metis_tac[preimage_choice_property] >> - `c IN G /\ (f c = z)` by metis_tac[preimage_choice_property] >> - `a * b IN G /\ b * c IN G ` by rw[] >> - `a * b * c = a * (b * c)` by rw[monoid_assoc] >> - metis_tac[]); - -(* Theorem: [Identity] Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> #i IN fG /\ #i o x = x /\ x o #i = x. *) -(* Proof: - By homo_monoid_property, #i = f #e, and #i IN fG. - Since CHOICE (preimage f G x) IN G /\ x = f (CHOICE (preimage f G x)) by preimage_choice_property - hence #i o x - = (f #e) o f (preimage f G x) - = f (#e * preimage f G x) by homo_monoid_property - = f (preimage f G x) by monoid_lid - = x - similarly for x o #i = x by monoid_rid -*) -val homo_monoid_id_property = store_thm( - "homo_monoid_id_property", - ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> - #i IN fG /\ (!x. x IN fG ==> (#i o x = x) /\ (x o #i = x))``, - rw[MonoidHomo_def, homo_monoid_property] >- - metis_tac[monoid_lid, monoid_id_element, preimage_choice_property] >> - metis_tac[monoid_rid, monoid_id_element, preimage_choice_property]); - -(* Theorem: [Commutative] AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> - x IN fG /\ y IN fG ==> (x o y = y o x) *) -(* Proof: - Note AbelianMonoid g ==> Monoid g and - !x y. x IN G /\ y IN G ==> (x * y = y * x) by AbelianMonoid_def - By MonoidHomo_def, - !x. x IN G ==> f x IN fG - !x y. x IN G /\ y IN G ==> (f (x * y) = f x o f y) - Let a = CHOICE (preimage f G x), - b = CHOICE (preimage f G y). - Then a IN G /\ x = f a by preimage_choice_property - b IN G /\ y = f b by preimage_choice_property - x o y - = (f a) o (f b) by expanding x, y - = f (a * b) by homo_monoid_property - = f (b * a) by AbelianMonoid_def, above - = (f b) o (f a) by homo_monoid_property - = y o x by contracting x, y -*) -val homo_monoid_comm = store_thm( - "homo_monoid_comm", - ``!(g:'a monoid) (f:'a -> 'b). AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> - !x y. x IN fG /\ y IN fG ==> (x o y = y o x)``, - rw_tac std_ss[AbelianMonoid_def, MonoidHomo_def] >> - `(fG = IMAGE f G) /\ !x y. x IN fG /\ y IN fG ==> (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))` by rw[homo_monoid_property] >> - qabbrev_tac `a = CHOICE (preimage f G x)` >> - qabbrev_tac `b = CHOICE (preimage f G y)` >> - `a IN G /\ (f a = x)` by metis_tac[preimage_choice_property] >> - `b IN G /\ (f b = y)` by metis_tac[preimage_choice_property] >> - `a * b = b * a` by rw[] >> - metis_tac[]); - -(* Theorem: Homomorphic image of a monoid is a monoid. - Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> Monoid (homo_monoid g f) *) -(* Proof: - This is to show each of these: - (1) x IN fG /\ y IN fG ==> x o y IN fG true by homo_monoid_closure - (2) x IN fG /\ y IN fG /\ z IN fG ==> (x o y) o z = (x o y) o z true by homo_monoid_assoc - (3) #i IN fG, true by homo_monoid_id_property - (4) x IN fG ==> #i o x = x, true by homo_monoid_id_property - (5) x IN fG ==> x o #i = x, true by homo_monoid_id_property -*) -val homo_monoid_monoid = store_thm( - "homo_monoid_monoid", - ``!(g:'a monoid) f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> Monoid (homo_monoid g f)``, - rpt strip_tac >> - rw_tac std_ss[Monoid_def] >| [ - rw[homo_monoid_closure], - rw[homo_monoid_assoc], - rw[homo_monoid_id_property], - rw[homo_monoid_id_property], - rw[homo_monoid_id_property] - ]); - -(* Theorem: Homomorphic image of an Abelian monoid is an Abelian monoid. - AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> AbelianMonoid (homo_monoid g f) *) -(* Proof: - Note AbelianMonoid g ==> Monoid g by AbelianMonoid_def - By AbelianMonoid_def, this is to show: - (1) Monoid (homo_monoid g f), true by homo_monoid_monoid, Monoid g - (2) x IN fG /\ y IN fG ==> x o y = y o x, true by homo_monoid_comm, AbelianMonoid g -*) -val homo_monoid_abelian_monoid = store_thm( - "homo_monoid_abelian_monoid", - ``!(g:'a monoid) f. AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> AbelianMonoid (homo_monoid g f)``, - metis_tac[homo_monoid_monoid, AbelianMonoid_def, homo_monoid_comm]); - -(* Theorem: Monoid g /\ INJ f G UNIV ==> MonoidHomo f g (homo_monoid g f) *) -(* Proof: - By MonoidHomo_def, homo_monoid_property, this is to show: - (1) x IN G ==> f x IN IMAGE f G, true by IN_IMAGE - (2) x IN G /\ y IN G ==> f (x * y) = f x o f y, true by homo_monoid_op_inj -*) -val homo_monoid_by_inj = store_thm( - "homo_monoid_by_inj", - ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ INJ f G UNIV ==> MonoidHomo f g (homo_monoid g f)``, - rw_tac std_ss[MonoidHomo_def, homo_monoid_property] >- - rw[] >> - rw[homo_monoid_op_inj]); - -(* ------------------------------------------------------------------------- *) -(* Weaker form of Homomorphic of Monoid and image of identity. *) -(* ------------------------------------------------------------------------- *) - -(* Let us take out the image of identity requirement *) -val WeakHomo_def = Define` - WeakHomo (f:'a -> 'b) (g:'a monoid) (h:'b monoid) <=> - (!x. x IN G ==> f x IN h.carrier) /\ - (!x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y))) - (* no requirement for: f #e = h.id *) -`; - -(* A function f from g to h is an isomorphism if f is a bijective homomorphism. *) -val WeakIso_def = Define` - WeakIso f g h <=> WeakHomo f g h /\ BIJ f G h.carrier -`; - -(* Then the best we can prove about monoid identities is the following: - Monoid g /\ Monoid h /\ WeakIso f g h ==> f #e = h.id - which is NOT: - Monoid g /\ Monoid h /\ WeakHomo f g h ==> f #e = h.id -*) - -(* Theorem: Monoid g /\ Monoid h /\ WeakIso f g h ==> f #e = h.id *) -(* Proof: - By monoid_id_unique: - |- !g. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x) /\ (e * x = x)) <=> (e = #e)) : thm - Hence this is to show: !x. x IN h.carrier ==> (h.op x (f #e) = x) /\ (h.op (f #e) x = x) - Note that WeakIso f g h = WeakHomo f g h /\ BIJ f G h.carrier. - (1) x IN h.carrier /\ f #e IN h.carrier ==> h.op x (f #e) = x - Since x = f y for some y IN G, by BIJ_THM - h.op x (f #e) = h.op (f y) (f #e) - = f (y * #e) by WeakHomo_def - = f y = x by monoid_rid - (2) x IN h.carrier /\ f #e IN h.carrier ==> h.op (f #e) x = x - Similar to above, - h.op (f #e) x = h.op (f #e) (f y) = f (#e * y) = f y = x by monoid_lid. -*) -val monoid_weak_iso_id = store_thm( - "monoid_weak_iso_id", - ``!f g h. Monoid g /\ Monoid h /\ WeakIso f g h ==> (f #e = h.id)``, - rw_tac std_ss[WeakIso_def] >> - `#e IN G /\ f #e IN h.carrier` by metis_tac[WeakHomo_def, monoid_id_element] >> - `!x. x IN h.carrier ==> (h.op x (f #e) = x) /\ (h.op (f #e) x = x)` suffices_by rw_tac std_ss[monoid_id_unique] >> - rpt strip_tac>| [ - `?y. y IN G /\ (f y = x)` by metis_tac[BIJ_THM] >> - metis_tac[WeakHomo_def, monoid_rid], - `?y. y IN G /\ (f y = x)` by metis_tac[BIJ_THM] >> - metis_tac[WeakHomo_def, monoid_lid] - ]); - -(* ------------------------------------------------------------------------- *) -(* Injective Image of Monoid. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: Given a Monoid g, and an injective function f, - then the image (f G) is a Monoid, with an induced binary operator: - op := (\x y. f (f^-1 x * f^-1 y)) *) - -(* Define a monoid injective image for an injective f, with LINV f G. *) -Definition monoid_inj_image_def: - monoid_inj_image (g:'a monoid) (f:'a -> 'b) = - <|carrier := IMAGE f G; - op := let t = LINV f G in (\x y. f (t x * t y)); - id := f #e - |> -End - -(* Theorem: Monoid g /\ INJ f G univ(:'b) ==> Monoid (monoid_inj_image g f) *) -(* Proof: - Note INJ f G univ(:'b) ==> BIJ f G (IMAGE f G) by INJ_IMAGE_BIJ_ALT - so !x. x IN G ==> t (f x) = x where t = LINV f G - and !x. x IN (IMAGE f G) ==> f (t x) = x by BIJ_LINV_THM - By Monoid_def, monoid_inj_image_def, this is to show: - (1) x IN IMAGE f G /\ y IN IMAGE f G ==> f (t x * t y) IN IMAGE f G - Note ?a. (x = f a) /\ a IN G by IN_IMAGE - ?b. (y = f b) /\ b IN G by IN_IMAGE - Hence f (t x * t y) - = f (t (f a) * t (f b)) by x = f a, y = f b - = f (a * b) by !y. t (f y) = y - Since a * b IN G by monoid_op_element - f (a * b) IN IMAGE f G by IN_IMAGE - (2) x IN IMAGE f G /\ y IN IMAGE f G /\ z IN IMAGE f G ==> f (t (f (t x * t y)) * t z) = f (t x * t (f (t y * t z))) - Note ?a. (x = f a) /\ a IN G by IN_IMAGE - ?b. (y = f b) /\ b IN G by IN_IMAGE - ?c. (z = f c) /\ c IN G by IN_IMAGE - LHS = f (t (f (t x * t y)) * t z)) - = f (t (f (t (f a) * t (f b))) * t (f c)) by x = f a, y = f b, z = f c - = f (t (f (a * b)) * c) by !y. t (f y) = y - = f ((a * b) * c) by !y. t (f y) = y, monoid_op_element - RHS = f (t x * t (f (t y * t z))) - = f (t (f a) * t (f (t (f b) * t (f c)))) by x = f a, y = f b, z = f c - = f (a * t (f (b * c))) by !y. t (f y) = y - = f (a * (b * c)) by !y. t (f y) = y - = LHS by monoid_assoc - (3) f #e IN IMAGE f G - Since #e IN G by monoid_id_element - so f #e IN IMAGE f G by IN_IMAGE - (4) x IN IMAGE f G ==> f (t (f #e) * t x) = x - Note ?a. (x = f a) /\ a IN G by IN_IMAGE - and #e IN G by monoid_id_element - Hence f (t (f #e) * t x) - = f (#e * t x) by !y. t (f y) = y - = f (#e * t (f a)) by x = f a - = f (#e * a) by !y. t (f y) = y - = f a by monoid_id - = x by x = f a - (5) x IN IMAGE f G ==> f (t x * t (f #e)) = x - Note ?a. (x = f a) /\ a IN G by IN_IMAGE - and #e IN G by monoid_id_element - Hence f (t x * t (f #e)) - = f (t x * #e) by !y. t (f y) = y - = f (t (f a) * #e) by x = f a - = f (a * #e) by !y. t (f y) = y - = f a by monoid_id - = x by x = f a -*) -Theorem monoid_inj_image_monoid: - !(g:'a monoid) (f:'a -> 'b). Monoid g /\ INJ f G univ(:'b) ==> Monoid (monoid_inj_image g f) -Proof - rpt strip_tac >> - `BIJ f G (IMAGE f G)` by rw[INJ_IMAGE_BIJ_ALT] >> - `(!x. x IN G ==> (LINV f G (f x) = x)) /\ (!x. x IN (IMAGE f G) ==> (f (LINV f G x) = x))` by metis_tac[BIJ_LINV_THM] >> - rw_tac std_ss[Monoid_def, monoid_inj_image_def] >| [ - `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> - `?b. (y = f b) /\ b IN G` by rw[GSYM IN_IMAGE] >> - rw[], - `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> - `?b. (y = f b) /\ b IN G` by rw[GSYM IN_IMAGE] >> - `?c. (z = f c) /\ c IN G` by rw[GSYM IN_IMAGE] >> - rw[monoid_assoc], - rw[], - `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> - rw[], - `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> - rw[] - ] -QED - -(* Theorem: AbelianMonoid g /\ INJ f G univ(:'b) ==> AbelianMonoid (monoid_inj_image g f) *) -(* Proof: - By AbelianMonoid_def, this is to show: - (1) Monoid g ==> Monoid (monoid_inj_image g f) - True by monoid_inj_image_monoid. - (2) x IN G /\ y IN G ==> (monoid_inj_image g f).op x y = (monoid_inj_image g f).op y x - By monoid_inj_image_def, this is to show: - f (t (f x) * t (f y)) = f (t (f y) * t (f x)) where t = LINV f G - Note INJ f G univ(:'b) ==> BIJ f G (IMAGE f G) by INJ_IMAGE_BIJ_ALT - so !x. x IN G ==> t (f x) = x - and !x. x IN (IMAGE f G) ==> f (t x) = x by BIJ_LINV_THM - f (t (f x) * t (f y)) - = f (x * y) by !y. t (f y) = y - = f (y * x) by commutativity condition - = f (t (f y) * t (f x)) by !y. t (f y) = y -*) -Theorem monoid_inj_image_abelian_monoid: - !(g:'a monoid) (f:'a -> 'b). AbelianMonoid g /\ INJ f G univ(:'b) ==> - AbelianMonoid (monoid_inj_image g f) -Proof - rw[AbelianMonoid_def] >- - rw[monoid_inj_image_monoid] >> - pop_assum mp_tac >> - pop_assum mp_tac >> - rw[monoid_inj_image_def] >> - metis_tac[INJ_IMAGE_BIJ_ALT, BIJ_LINV_THM] -QED - -(* Theorem: INJ f G univ(:'b) ==> MonoidHomo f g (monoid_inj_image g f) *) -(* Proof: - Let s = IMAGE f G. - Then BIJ f G s by INJ_IMAGE_BIJ_ALT - so INJ f G s by BIJ_DEF - - By MonoidHomo_def, monoid_inj_image_def, this is to show: - (1) x IN G ==> f x IN IMAGE f G, true by IN_IMAGE - (2) x IN R /\ y IN R ==> f (x * y) = f (LINV f R (f x) * LINV f R (f y)) - Since LINV f R (f x) = x by BIJ_LINV_THM - and LINV f R (f y) = y by BIJ_LINV_THM - The result is true. -*) -Theorem monoid_inj_image_monoid_homo: - !(g:'a monoid) f. INJ f G univ(:'b) ==> MonoidHomo f g (monoid_inj_image g f) -Proof - rw[MonoidHomo_def, monoid_inj_image_def] >> - qabbrev_tac `s = IMAGE f G` >> - `BIJ f G s` by rw[INJ_IMAGE_BIJ_ALT, Abbr`s`] >> - `INJ f G s` by metis_tac[BIJ_DEF] >> - metis_tac[BIJ_LINV_THM] -QED - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/monoid/monoidOrderScript.sml b/examples/algebra/monoid/monoidOrderScript.sml deleted file mode 100644 index 2b47ecd152..0000000000 --- a/examples/algebra/monoid/monoidOrderScript.sml +++ /dev/null @@ -1,1211 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Monoid Order and Invertibles *) -(* ------------------------------------------------------------------------- *) - -(* - -Monoid Order -============ -This is an investigation of the equation x ** n = #e. -Given x IN G, x ** 0 = #e by monoid_exp_0 -But for those having non-trivial n with x ** n = #e, -the least value of n is called the order for the element x. -This is an important property for the element x, -especiallly later for Finite Group. - -Monoid Invertibles -================== -In a monoid M, not all elements are invertible. -But for those elements that are invertible, -they have interesting properties. -Indeed, being invertible, an operation .inv or |/ -can be defined through Skolemization, and later, -the Monoid Invertibles will be shown to be a Group. - -*) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "monoidOrder"; - -(* ------------------------------------------------------------------------- *) - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory; -open dividesTheory gcdTheory; - -(* val _ = load "monoidTheory"; *) -open monoidTheory; - -(* val _ = load "primePowerTheory"; *) -open primePowerTheory; - - -(* ------------------------------------------------------------------------- *) -(* Monoid Order and Invertibles Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: - ord x = order g x - maximal_order g = MAX_SET (IMAGE ord G) - G* = monoid_invertibles g - reciprocal x = monoid_inv g x - |/ = reciprocal -*) -(* Definitions and Theorems (# are exported): - - Definitions: - period_def |- !g x k. period g x k <=> 0 < k /\ (x ** k = #e) - order_def |- !g x. ord x = case OLEAST k. period g x k of NONE => 0 | SOME k => k - order_alt |- !g x. ord x = case OLEAST k. 0 < k /\ x ** k = #e of NONE => 0 | SOME k => k - order_property |- !g x. x ** ord x = #e - order_period |- !g x. 0 < ord x ==> period g x (ord x) - order_minimal |- !g x n. 0 < n /\ n < ord x ==> x ** n <> #e - order_eq_0 |- !g x. (ord x = 0) <=> !n. 0 < n ==> x ** n <> #e - order_thm |- !g x n. 0 < n ==> - ((ord x = n) <=> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e) - -# monoid_order_id |- !g. Monoid g ==> (ord #e = 1) - monoid_order_eq_1 |- !g. Monoid g ==> !x. x IN G ==> ((ord x = 1) <=> (x = #e)) - monoid_order_condition |- !g. Monoid g ==> !x. x IN G ==> !m. (x ** m = #e) <=> ord x divides m - monoid_order_divides_exp|- !g. Monoid g ==> !x n. x IN G ==> ((x ** n = #e) <=> ord x divides n) - monoid_order_power_eq_0 |- !g. Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) = 0) <=> 0 < k /\ (ord x = 0) - monoid_order_power |- !g. Monoid g ==> !x. x IN G ==> !k. ord (x ** k) * gcd (ord x) k = ord x - monoid_order_power_eqn |- !g. Monoid g ==> !x k. x IN G /\ 0 < k ==> (ord (x ** k) = ord x DIV gcd k (ord x)) - monoid_order_power_coprime |- !g. Monoid g ==> !x. x IN G ==> - !n. coprime n (ord x) ==> (ord (x ** n) = ord x) - monoid_order_cofactor |- !g. Monoid g ==> !x n. x IN G /\ 0 < ord x /\ n divides (ord x) ==> - (ord (x ** (ord x DIV n)) = n) - monoid_order_divisor |- !g. Monoid g ==> !x m. x IN G /\ 0 < ord x /\ m divides (ord x) ==> - ?y. y IN G /\ (ord y = m) - monoid_order_common |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> - ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) - monoid_order_common_coprime |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) /\ - coprime (ord x) (ord y) ==> ?z. z IN G /\ (ord z = ord x * ord y) - monoid_exp_mod_order |- !g. Monoid g ==> !x. x IN G /\ 0 < ord x ==> !n. x ** n = x ** (n MOD ord x) - abelian_monoid_order_common |- !g. AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> - ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) - abelian_monoid_order_common_coprime - |- !g. AbelianMonoid g ==> !x y. x IN G /\ y IN G /\ - coprime (ord x) (ord y) ==> ?z. z IN G /\ (ord z = ord x * ord y) - abelian_monoid_order_lcm |- !g. AbelianMonoid g ==> - !x y. x IN G /\ y IN G ==> ?z. z IN G /\ (ord z = lcm (ord x) (ord y)) - - Orders of elements: - orders_def |- !g n. orders g n = {x | x IN G /\ (ord x = n)} - orders_element |- !g x n. x IN orders g n <=> x IN G /\ (ord x = n) - orders_subset |- !g n. orders g n SUBSET G - orders_finite |- !g. FINITE G ==> !n. FINITE (orders g n) - orders_eq_1 |- !g. Monoid g ==> (orders g 1 = {#e}) - - Maximal Order: - maximal_order_alt |- !g. maximal_order g = MAX_SET {ord z | z | z IN G} - monoid_order_divides_maximal |- !g. FiniteAbelianMonoid g ==> - !x. x IN G /\ 0 < ord x ==> ord x divides maximal_order g - - Monoid Invertibles: - monoid_invertibles_def |- !g. G* = {x | x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e)} - monoid_invertibles_element |- !g x. x IN G* <=> x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e) - monoid_order_nonzero |- !g x. Monoid g /\ x IN G /\ 0 < ord x ==> x IN G* - - Invertibles_def |- !g. Invertibles g = <|carrier := G*; op := $*; id := #e|> - Invertibles_property |- !g. ((Invertibles g).carrier = G* ) /\ - ((Invertibles g).op = $* ) /\ - ((Invertibles g).id = #e) /\ - ((Invertibles g).exp = $** ) - Invertibles_carrier |- !g. (Invertibles g).carrier = G* - Invertibles_subset |- !g. (Invertibles g).carrier SUBSET G - Invertibles_order |- !g x. order (Invertibles g) x = ord x - - Monoid Inverse as an operation: - monoid_inv_def |- !g x. Monoid g /\ x IN G* ==> |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) - monoid_inv_def_alt |- !g. Monoid g ==> !x. x IN G* <=> - x IN G /\ |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) - monoid_inv_element |- !g. Monoid g ==> !x. x IN G* ==> x IN G -# monoid_id_invertible |- !g. Monoid g ==> #e IN G* -# monoid_inv_op_invertible |- !g. Monoid g ==> !x y. x IN G* /\ y IN G* ==> x * y IN G* -# monoid_inv_invertible |- !g. Monoid g ==> !x. x IN G* ==> |/ x IN G* - monoid_invertibles_is_monoid |- !g. Monoid g ==> Monoid (Invertibles g) - -*) - -(* ------------------------------------------------------------------------- *) -(* Monoid Order Definition. *) -(* ------------------------------------------------------------------------- *) - -(* Define order = optional LEAST period for an element x in Group g *) -val period_def = zDefine` - period (g:'a monoid) (x:'a) k <=> 0 < k /\ (x ** k = #e) -`; -val order_def = zDefine` - order (g:'a monoid) (x:'a) = case OLEAST k. period g x k of - NONE => 0 - | SOME k => k -`; -(* use zDefine here since these are not computationally effective. *) - -(* Expand order_def with period_def. *) -val order_alt = save_thm( - "order_alt", REWRITE_RULE [period_def] order_def); -(* val order_alt = - |- !g x. order g x = - case OLEAST k. 0 < k /\ x ** k = #e of NONE => 0 | SOME k => k: thm *) - -(* overloading on Monoid Order *) -val _ = overload_on ("ord", ``order g``); - -(* Theorem: (x ** (ord x) = #e *) -(* Proof: by definition, and x ** 0 = #e by monoid_exp_0. *) -val order_property = store_thm( - "order_property", - ``!g:'a monoid. !x:'a. (x ** (ord x) = #e)``, - ntac 2 strip_tac >> - simp_tac std_ss[order_def, period_def] >> - DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> - rw[]); - -(* Theorem: 0 < (ord x) ==> period g x (ord x) *) -(* Proof: by order_property, period_def. *) -val order_period = store_thm( - "order_period", - ``!g:'a monoid x:'a. 0 < (ord x) ==> period g x (ord x)``, - rw[order_property, period_def]); - -(* Theorem: !n. 0 < n /\ n < (ord x) ==> x ** n <> #e *) -(* Proof: by definition of OLEAST. *) -Theorem order_minimal: - !g:'a monoid x:'a. !n. 0 < n /\ n < ord x ==> x ** n <> #e -Proof - ntac 3 strip_tac >> - simp_tac std_ss[order_def, period_def] >> - DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> - rw_tac std_ss[] >> - metis_tac[DECIDE “~(0 < 0)”] -QED - -(* Theorem: (ord x = 0) <=> !n. 0 < n ==> x ** n <> #e *) -(* Proof: - Expand by order_def, period_def, this is to show: - (1) 0 < n /\ (!n. ~(0 < n) \/ x ** n <> #e) ==> x ** n <> #e - True by assertion. - (2) 0 < n /\ x ** n = #e /\ (!m. m < 0 ==> ~(0 < m) \/ x ** m <> #e) ==> (n = 0) <=> !n. 0 < n ==> x ** n <> #e - True by assertion. -*) -Theorem order_eq_0: - !g:'a monoid x. ord x = 0 <=> !n. 0 < n ==> x ** n <> #e -Proof - ntac 2 strip_tac >> - simp_tac std_ss[order_def, period_def] >> - DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> - rw_tac std_ss[] >> - metis_tac[DECIDE “~(0 < 0)”] -QED - -val std_ss = std_ss -* ["NOT_LT_ZERO_EQ_ZERO"] - -(* Theorem: 0 < n ==> ((ord x = n) <=> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e) *) -(* Proof: - If part: (ord x = n) ==> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e - This is to show: - (1) (ord x = n) ==> (x ** n = #e), true by order_property - (2) (ord x = n) ==> !m. 0 < m /\ m < n ==> x ** m <> #e, true by order_minimal - Only-if part: (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e ==> (ord x = n) - Expanding by order_def, period_def, this is to show: - (1) 0 < n /\ x ** n = #e /\ !n'. ~(0 < n') \/ x ** n' <> #e ==> 0 = n - Putting n' = n, the assumption is contradictory. - (2) 0 < n /\ 0 < n' /\ x ** n = #e /\ x ** n' = #e /\ ... ==> n' = n - The assumptions implies ~(n' < n), and ~(n < n'), hence n' = n. -*) -val order_thm = store_thm( - "order_thm", - ``!g:'a monoid x:'a. !n. 0 < n ==> - ((ord x = n) <=> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e)``, - rw[EQ_IMP_THM] >- - rw[order_property] >- - rw[order_minimal] >> - simp_tac std_ss[order_def, period_def] >> - DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> - rw_tac std_ss[] >- - metis_tac[] >> - `~(n' < n)` by metis_tac[] >> - `~(n < n')` by metis_tac[] >> - decide_tac); - -(* Theorem: Monoid g ==> (ord #e = 1) *) -(* Proof: - Since #e IN G by monoid_id_element - and #e ** 1 = #e by monoid_exp_1 - Obviously, 0 < 1 and there is no m such that 0 < m < 1 - hence true by order_thm -*) -val monoid_order_id = store_thm( - "monoid_order_id", - ``!g:'a monoid. Monoid g ==> (ord #e = 1)``, - rw[order_thm, DECIDE``!m . ~(0 < m /\ m < 1)``]); - -(* export simple result *) -val _ = export_rewrites ["monoid_order_id"]; - -(* Theorem: Monoid g ==> !x. x IN G ==> ((ord x = 1) <=> (x = #e)) *) -(* Proof: - If part: ord x = 1 ==> x = #e - Since x ** (ord x) = #e by order_property - ==> x ** 1 = #e by given - ==> x = #e by monoid_exp_1 - Only-if part: x = #e ==> ord x = 1 - i.e. to show ord #e = 1. - True by monoid_order_id. -*) -val monoid_order_eq_1 = store_thm( - "monoid_order_eq_1", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> ((ord x = 1) <=> (x = #e))``, - rw[EQ_IMP_THM] >> - `#e = x ** (ord x)` by rw[order_property] >> - rw[]); - -(* Theorem: Monoid g ==> !x. x IN G ==> !m. (x ** m = #e) <=> (ord x) divides m *) -(* Proof: - (ord x) is a period, and so divides all periods. - Let n = ord x. - If part: x^m = #e ==> n divides m - If n = 0, m = 0 by order_eq_0 - Hence true by ZERO_DIVIDES - If n <> 0, - By division algorithm, m = q * n + t for some q, t and t < n. - #e = x^m - = x^(q * n + t) - = (x^n)^q * x^t - = #e * x^t - Thus x^t = #e, but t < n. - If 0 < t, this contradicts order_minimal. - Hence t = 0, or n divides m. - Only-if part: n divides m ==> x^m = #e - By divides_def, ?k. m = k * n - x^m = x^(k * n) = (x^n)^k = #e^k = #e. -*) -val monoid_order_condition = store_thm( - "monoid_order_condition", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !m. (x ** m = #e) <=> (ord x) divides m``, - rpt strip_tac >> - qabbrev_tac `n = ord x` >> - rw[EQ_IMP_THM] >| [ - Cases_on `n = 0` >| [ - `~(0 < m)` by metis_tac[order_eq_0] >> - `m = 0` by decide_tac >> - rw[ZERO_DIVIDES], - `x ** n = #e` by rw[order_property, Abbr`n`] >> - `0 < n` by decide_tac >> - `?q t. (m = q * n + t) /\ t < n` by metis_tac[DIVISION] >> - `x ** m = x ** (n * q + t)` by metis_tac[MULT_COMM] >> - `_ = (x ** (n * q)) * (x ** t)` by rw[] >> - `_ = ((x ** n) ** q) * (x ** t)` by rw[] >> - `_ = x ** t` by rw[] >> - `~(0 < t)` by metis_tac[order_minimal] >> - `t = 0` by decide_tac >> - `m = q * n` by rw[] >> - metis_tac[divides_def] - ], - `x ** n = #e` by rw[order_property, Abbr`n`] >> - `?k. m = k * n` by rw[GSYM divides_def] >> - `x ** m = x ** (n * k)` by metis_tac[MULT_COMM] >> - `_ = (x ** n) ** k` by rw[] >> - rw[] - ]); - -(* Theorem: Monoid g ==> !x n. x IN G ==> (x ** n = #e <=> ord x divides n) *) -(* Proof: by monoid_order_condition *) -val monoid_order_divides_exp = store_thm( - "monoid_order_divides_exp", - ``!g:'a monoid. Monoid g ==> !x n. x IN G ==> ((x ** n = #e) <=> ord x divides n)``, - rw[monoid_order_condition]); - -(* Theorem: Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) = 0) <=> 0 < k /\ (ord x = 0) *) -(* Proof: - By order_eq_0, this is to show: - (1) !n. 0 < n ==> (x ** k) ** n <> #e ==> 0 < k - By contradiction. Assume k = 0. - Then x ** k = #e by monoid_exp_0 - and #e ** n = #e by monoid_id_exp - This contradicts the implication: (x ** k) ** n <> #e. - (2) 0 < n /\ !n. 0 < n ==> (x ** k) ** n <> #e ==> x ** n <> #e - By contradiction. Assume x ** n = #e. - Now, (x ** k) ** n - = x ** (k * n) by monoid_exp_mult - = x ** (n * k) by MULT_COMM - = (x ** n) * k by monoid_exp_mult - = #e ** k by x ** n = #e - = #e by monoid_id_exp - This contradicts the implication: (x ** k) ** n <> #e. - (3) 0 < n /\ !n. 0 < n ==> x ** n <> #e ==> (x ** k) ** n <> #e - By contradiction. Assume (x ** k) ** n = #e. - 0 < k /\ 0 < n ==> 0 < k * n by arithmetic - But (x ** n) ** k = x ** (n * k) by monoid_exp_mult - This contradicts the implication: (x ** k) ** n <> #e. -*) -val monoid_order_power_eq_0 = store_thm( - "monoid_order_power_eq_0", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) = 0) <=> 0 < k /\ (ord x = 0)``, - rw[order_eq_0, EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `k = 0` by decide_tac >> - `x ** k = #e` by rw[monoid_exp_0] >> - metis_tac[monoid_id_exp, DECIDE``0 < 1``], - metis_tac[monoid_exp_mult, MULT_COMM, monoid_id_exp], - `0 < k * n` by rw[LESS_MULT2] >> - metis_tac[monoid_exp_mult] - ]); - -(* Theorem: ord (x ** k) = ord x / gcd(ord x, k) - Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) * (gcd (ord x) k) = ord x) *) -(* Proof: - Let n = ord x, m = ord (x^k), d = gcd(n,k). - This is to show: m = n / d. - If k = 0, - m = ord (x^0) = ord #e = 1 by monoid_order_id - d = gcd(n,0) = n by GCD_0R - henc true. - If k <> 0, - First, show ord (x^k) = m divides n/d. - If n = 0, m = 0 by monoid_order_power_eq_0 - so ord (x^k) = m | (n/d) by ZERO_DIVIDES - If n <> 0, - (x^k)^(n/d) = x^(k * n/d) = x^(n * k/d) = (x^n)^(k/d) = #e, - so ord (x^k) = m | (n/d) by monoid_order_condition. - Second, show (n/d) divides m = ord (x^k), or equivalently: n divides d * m - x^(k * m) = (x^k)^m = #e = x^n, - so ord x = n | k * m by monoid_order_condition - Since d = gcd(k,n), there are integers a and b such that - ka + nb = d by LINEAR_GCD - Multiply by m: k * m * a + n * m * b = d * m. - But since n | k * m, it follows that n | d*m, - i.e. (n/d) | m by DIVIDES_CANCEL. - By DIVIDES_ANTISYM, ord (x^k) = m = n/d. -*) -val monoid_order_power = store_thm( - "monoid_order_power", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) * (gcd (ord x) k) = ord x)``, - rpt strip_tac >> - qabbrev_tac `n = ord x` >> - qabbrev_tac `m = ord (x ** k)` >> - qabbrev_tac `d = gcd n k` >> - Cases_on `k = 0` >| [ - `d = n` by metis_tac[GCD_0R] >> - rw[Abbr`m`], - Cases_on `n = 0` >| [ - `0 < k` by decide_tac >> - `m = 0` by rw[monoid_order_power_eq_0, Abbr`n`, Abbr`m`] >> - rw[], - `x ** n = #e` by rw[order_property, Abbr`n`] >> - `0 < n /\ 0 < k` by decide_tac >> - `?p q. (n = p * d) /\ (k = q * d)` by metis_tac[FACTOR_OUT_GCD] >> - `k * p = n * q` by rw_tac arith_ss[] >> - `(x ** k) ** p = x ** (k * p)` by rw[] >> - `_ = x ** (n * q)` by metis_tac[] >> - `_ = (x ** n) ** q` by rw[] >> - `_ = #e` by rw[] >> - `m divides p` by rw[GSYM monoid_order_condition, Abbr`m`] >> - `x ** (m * k) = x ** (k * m)` by metis_tac[MULT_COMM] >> - `_ = (x ** k) ** m` by rw[] >> - `_ = #e` by rw[order_property, Abbr`m`] >> - `n divides (m * k)` by rw[GSYM monoid_order_condition, Abbr`n`, Abbr`m`] >> - `?u v. u * k = v * n + d` by rw[LINEAR_GCD, Abbr`d`] >> - `m * k * u = m * (u * k)` by rw_tac arith_ss[] >> - `_ = m * (v * n) + m * d` by metis_tac[LEFT_ADD_DISTRIB] >> - `_ = m * v * n + m * d` by rw_tac arith_ss[] >> - `n divides (m * k * u)` by metis_tac[DIVIDES_MULT] >> - `n divides (m * v * n)` by metis_tac[divides_def] >> - `n divides (m * d)` by metis_tac[DIVIDES_ADD_2] >> - `d <> 0` by metis_tac[MULT_EQ_0] >> - `0 < d` by decide_tac >> - `p divides m` by metis_tac[DIVIDES_CANCEL] >> - metis_tac[DIVIDES_ANTISYM] - ] - ]); - -(* Theorem: Monoid g ==> - !x k. x IN G /\ 0 < k ==> (ord (x ** k) = (ord x) DIV (gcd k (ord x))) *) -(* Proof: - Note ord (x ** k) * gcd k (ord x) = ord x by monoid_order_power, GCD_SYM - and 0 < gcd k (ord x) by GCD_EQ_0, 0 < k - ==> ord (x ** k) = (ord x) DIV (gcd k (ord x)) by MULT_EQ_DIV -*) -val monoid_order_power_eqn = store_thm( - "monoid_order_power_eqn", - ``!g:'a monoid. Monoid g ==> - !x k. x IN G /\ 0 < k ==> (ord (x ** k) = (ord x) DIV (gcd k (ord x)))``, - rpt strip_tac >> - `ord (x ** k) * gcd k (ord x) = ord x` by metis_tac[monoid_order_power, GCD_SYM] >> - `0 < gcd k (ord x)` by metis_tac[GCD_EQ_0, NOT_ZERO] >> - fs[MULT_EQ_DIV]); - -(* Theorem: Monoid g ==> !x. x IN G ==> !n. coprime n (ord x) ==> (ord (x ** n) = ord x) *) -(* Proof: - ord x - = ord (x ** n) * gcd (ord x) n by monoid_order_power - = ord (x ** n) * 1 by coprime_sym - = ord (x ** n) by MULT_RIGHT_1 -*) -val monoid_order_power_coprime = store_thm( - "monoid_order_power_coprime", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n. coprime n (ord x) ==> (ord (x ** n) = ord x)``, - metis_tac[monoid_order_power, coprime_sym, MULT_RIGHT_1]); - -(* Theorem: Monoid g ==> - !x n. x IN G /\ 0 < ord x /\ n divides ord x ==> (ord (x ** (ord x DIV n)) = n) *) -(* Proof: - Let m = ord x, k = m DIV n. - Since 0 < m, n <> 0, or 0 < n by ZERO_DIVIDES - Since n divides m, m = k * n by DIVIDES_EQN - Hence k divides m by divisors_def, MULT_COMM - and k <> 0 by MULT, m <> 0 - and gcd k m = k by divides_iff_gcd_fix - Now ord (x ** k) * k - = m by monoid_order_power - = k * n by above - = n * k by MULT_COMM - Hence ord (x ** k) = n by MULT_RIGHT_CANCEL, k <> 0 -*) -val monoid_order_cofactor = store_thm( - "monoid_order_cofactor", - ``!g: 'a monoid. Monoid g ==> - !x n. x IN G /\ 0 < ord x /\ n divides ord x ==> (ord (x ** (ord x DIV n)) = n)``, - rpt strip_tac >> - qabbrev_tac `m = ord x` >> - qabbrev_tac `k = m DIV n` >> - `0 < n` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> - `m = k * n` by rw[GSYM DIVIDES_EQN, Abbr`k`] >> - `k divides m` by metis_tac[divides_def, MULT_COMM] >> - `k <> 0` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> - `gcd k m = k` by rw[GSYM divides_iff_gcd_fix] >> - metis_tac[monoid_order_power, GCD_SYM, MULT_COMM, MULT_RIGHT_CANCEL]); - -(* Theorem: If x IN G with ord x = n > 0, and m divides n, then G contains an element of order m. *) -(* Proof: - m divides n ==> n = k * m for some k, by divides_def. - Then x^k has order m: - (x^k)^m = x^(k * m) = x^n = #e - and for any h < m, - if (x^k)^h = x^(k * h) = #e means x has order k * h < k * m = n, - which is a contradiction with order_minimal. -*) -val monoid_order_divisor = store_thm( - "monoid_order_divisor", - ``!g:'a monoid. Monoid g ==> - !x m. x IN G /\ 0 < ord x /\ m divides (ord x) ==> ?y. y IN G /\ (ord y = m)``, - rpt strip_tac >> - `ord x <> 0` by decide_tac >> - `m <> 0` by metis_tac[ZERO_DIVIDES] >> - `0 < m` by decide_tac >> - `?k. ord x = k * m` by rw[GSYM divides_def] >> - qexists_tac `x ** k` >> - rw[] >> - `x ** (ord x) = #e` by rw[order_property] >> - `(x ** k) ** m = #e` by metis_tac[monoid_exp_mult] >> - `(!h. 0 < h /\ h < m ==> (x ** k) ** h <> #e)` suffices_by metis_tac[order_thm] >> - rpt strip_tac >> - `h <> 0` by decide_tac >> - `k <> 0 /\ k * h <> 0` by metis_tac[MULT, MULT_EQ_0] >> - `0 < k /\ 0 < k * h` by decide_tac >> - `k * h < k * m` by metis_tac[LT_MULT_LCANCEL] >> - `(x ** k) ** h = x ** (k * h)` by rw[] >> - metis_tac[order_minimal]); - -(* Theorem: If x * y = y * x, and n = ord x, m = ord y, - then there exists z IN G such that ord z = (lcm n m) / (gcd n m) *) -(* Proof: - Let n = ord x, m = ord y, d = gcd(n, m). - This is to show: ?z. z IN G /\ (ord z * d = n * m) - If n = 0, take z = x, by LCM_0. - If m = 0, take z = y, by LCM_0. - If n <> 0 and m <> 0, - First, get a pair with coprime orders. - ?p q. (n = p * d) /\ (m = q * d) /\ coprime p q by FACTOR_OUT_GCD - Let u = x^d, v = y^d - then ord u = ord (x^d) = ord x / gcd(n, d) = n/d = p by monoid_order_power - and ord v = ord (y^d) = ord y / gcd(m, d) = m/d = q by monoid_order_power - Now gcd(p,q) = 1, and there exists integers a and b such that - a * p + b * q = 1 by LINEAR_GCD - Let w = u^b * v^a - Then w^p = (u^b * v^a)^p - = (u^b)^p * (v^a)^p by monoid_comm_op_exp - = (u^p)^b * (v^a)^p by monoid_exp_mult_comm - = #e^b * v^(a * p) by p = ord u - = v^(a * p) by monoid_id_exp - = v^(1 - b * q) by LINEAR_GCD condition - = v^1 * |/ v^(b * q) by variant of monoid_exp_add - = v * 1/ (v^q)^b by monoid_exp_mult_comm - = v * 1/ #e^b by q = ord v - = v - Hence ord (w^p) = ord v = q, - Let c = ord w, c <> 0 since p * q <> 0 by GCD_0L - then q = ord (w^p) = c / gcd(c,p) by monoid_order_power - i.e. q * gcd(c,p) = c, or q divides c - Similarly, w^q = u, and p * gcd(c,q) = c, or p divides c. - Since coprime p q, p * q divides c, an order of element w IN G. - Hence there is some z in G such that ord z = p * q by monoid_order_divisor. - i.e. ord z = lcm p q = lcm (n/d) (m/d) = (lcm n m) / d. -*) -val monoid_order_common = store_thm( - "monoid_order_common", - ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> - ?z. z IN G /\ ((ord z) * gcd (ord x) (ord y) = lcm (ord x) (ord y))``, - rpt strip_tac >> - qabbrev_tac `n = ord x` >> - qabbrev_tac `m = ord y` >> - qabbrev_tac `d = gcd n m` >> - Cases_on `n = 0` >- - metis_tac[LCM_0, MULT_EQ_0] >> - Cases_on `m = 0` >- - metis_tac[LCM_0, MULT_EQ_0] >> - `x ** n = #e` by rw[order_property, Abbr`n`] >> - `y ** m = #e` by rw[order_property, Abbr`m`] >> - `d <> 0` by rw[GCD_EQ_0, Abbr`d`] >> - `?p q. (n = p * d) /\ (m = q * d) /\ coprime p q` by rw[FACTOR_OUT_GCD, Abbr`d`] >> - qabbrev_tac `u = x ** d` >> - qabbrev_tac `v = y ** d` >> - `u IN G /\ v IN G` by rw[Abbr`u`, Abbr`v`] >> - `(gcd n d = d) /\ (gcd m d = d)` by rw[GCD_GCD, GCD_SYM, Abbr`d`] >> - `ord u = p` by metis_tac[monoid_order_power, MULT_RIGHT_CANCEL] >> - `ord v = q` by metis_tac[monoid_order_power, MULT_RIGHT_CANCEL] >> - `p <> 0 /\ q <> 0` by metis_tac[MULT_EQ_0] >> - `?a b. a * q = b * p + 1` by metis_tac[LINEAR_GCD] >> - `?h k. h * p = k * q + 1` by metis_tac[LINEAR_GCD, GCD_SYM] >> - qabbrev_tac `ua = u ** a` >> - qabbrev_tac `vh = v ** h` >> - qabbrev_tac `w = ua * vh` >> - `ua IN G /\ vh IN G /\ w IN G` by rw[Abbr`ua`, Abbr`vh`, Abbr`w`] >> - `ua * vh = (x ** d) ** a * (y ** d) ** h` by rw[] >> - `_ = x ** (d * a) * y ** (d * h)` by rw_tac std_ss[GSYM monoid_exp_mult] >> - `_ = y ** (d * h) * x ** (d * a)` by metis_tac[monoid_comm_exp_exp] >> - `_ = vh * ua` by rw[] >> - `w ** p = (ua * vh) ** p` by rw[] >> - `_ = ua ** p * vh ** p` by metis_tac[monoid_comm_op_exp] >> - `_ = (u ** p) ** a * (v ** h) ** p` by rw[monoid_exp_mult_comm] >> - `_ = #e ** a * v ** (h * p)` by rw[order_property] >> - `_ = v ** (h * p)` by rw[] >> - `_ = v ** (k * q + 1)` by rw_tac std_ss[] >> - `_ = v ** (k * q) * v` by rw[] >> - `_ = v ** (q * k) * v` by rw_tac std_ss[MULT_COMM] >> - `_ = (v ** q) ** k * v` by rw[] >> - `_ = #e ** k * v` by rw[order_property] >> - `_ = v` by rw[] >> - `w ** q = (ua * vh) ** q` by rw[] >> - `_ = ua ** q * vh ** q` by metis_tac[monoid_comm_op_exp] >> - `_ = (u ** a) ** q * (v ** q) ** h` by rw[monoid_exp_mult_comm] >> - `_ = u ** (a * q) * #e ** h` by rw[order_property] >> - `_ = u ** (a * q)` by rw[] >> - `_ = u ** (b * p + 1)` by rw_tac std_ss[] >> - `_ = u ** (b * p) * u` by rw[] >> - `_ = u ** (p * b) * u` by rw_tac std_ss[MULT_COMM] >> - `_ = (u ** p) ** b * u` by rw[] >> - `_ = #e ** b * u` by rw[order_property] >> - `_ = u` by rw[] >> - qabbrev_tac `c = ord w` >> - `q * gcd c p = c` by rw[monoid_order_power, Abbr`c`] >> - `p * gcd c q = c` by metis_tac[monoid_order_power] >> - `p divides c /\ q divides c` by metis_tac[divides_def, MULT_COMM] >> - `lcm p q = p * q` by rw[LCM_COPRIME] >> - `(p * q) divides c` by metis_tac[LCM_IS_LEAST_COMMON_MULTIPLE] >> - `p * q <> 0` by rw[MULT_EQ_0] >> - `c <> 0` by metis_tac[GCD_0L] >> - `0 < c` by decide_tac >> - `?z. z IN G /\ (ord z = p * q)` by metis_tac[monoid_order_divisor] >> - `ord z * d = d * (p * q)` by rw_tac arith_ss[] >> - `_ = lcm (d * p) (d * q)` by rw[LCM_COMMON_FACTOR] >> - `_ = lcm n m` by metis_tac[MULT_COMM] >> - metis_tac[]); - -(* This is a milestone. *) - -(* Theorem: If x * y = y * x, and n = ord x, m = ord y, and gcd n m = 1, - then there exists z IN G with ord z = (lcm n m) *) -(* Proof: - By monoid_order_common and gcd n m = 1. -*) -val monoid_order_common_coprime = store_thm( - "monoid_order_common_coprime", - ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) /\ coprime (ord x) (ord y) ==> - ?z. z IN G /\ (ord z = (ord x) * (ord y))``, - metis_tac[monoid_order_common, GCD_LCM, MULT_RIGHT_1, MULT_LEFT_1]); -(* This version can be proved directly using previous technique, then derive the general case: - Let ord x = n, ord y = m. - Let d = gcd(n,m) p = n/d, q = m/d, gcd(p,q) = 1. - By p | n = ord x, there is u with ord u = p by monoid_order_divisor - By q | m = ord y, there is v with ord v = q by monoid_order_divisor - By gcd(ord u, ord v) = gcd(p,q) = 1, - there is z with ord z = lcm(p,q) = p * q = n/d * m/d = lcm(n,m)/gcd(n,m). -*) - -(* Theorem: Monoid g ==> !x. x IN G /\ 0 < ord x ==> !n. x ** n = x ** (n MOD (ord x)) *) -(* Proof: - Let z = ord x, 0 < z by given - Note n = (n DIV z) * z + (n MOD z) by DIVISION, 0 < z. - x ** n - = x ** ((n DIV z) * z + (n MOD z)) by above - = x ** ((n DIV z) * z) * x ** (n MOD z) by monoid_exp_add - = x ** (z * (n DIV z)) * x ** (n MOD z) by MULT_COMM - = (x ** z) ** (n DIV z) * x ** (n MOD z) by monoid_exp_mult - = #e ** (n DIV 2) * x ** (n MOD z) by order_property - = #e * x ** (n MOD z) by monoid_id_exp - = x ** (n MOD z) by monoid_lid -*) -val monoid_exp_mod_order = store_thm( - "monoid_exp_mod_order", - ``!g:'a monoid. Monoid g ==> !x. x IN G /\ 0 < ord x ==> !n. x ** n = x ** (n MOD (ord x))``, - rpt strip_tac >> - qabbrev_tac `z = ord x` >> - `x ** n = x ** ((n DIV z) * z + (n MOD z))` by metis_tac[DIVISION] >> - `_ = x ** ((n DIV z) * z) * x ** (n MOD z)` by rw[monoid_exp_add] >> - `_ = x ** (z * (n DIV z)) * x ** (n MOD z)` by metis_tac[MULT_COMM] >> - rw[monoid_exp_mult, order_property, Abbr`z`]); - -(* Theorem: AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> - ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) *) -(* Proof: by AbelianMonoid_def, monoid_order_common *) -val abelian_monoid_order_common = store_thm( - "abelian_monoid_order_common", - ``!g:'a monoid. AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> - ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y))``, - rw[AbelianMonoid_def, monoid_order_common]); - -(* Theorem: AbelianMonoid g ==> !x y. x IN G /\ y IN G /\ coprime (ord x) (ord y) ==> - ?z. z IN G /\ (ord z = ord x * ord y) *) -(* Proof: by AbelianMonoid_def, monoid_order_common_coprime *) -val abelian_monoid_order_common_coprime = store_thm( - "abelian_monoid_order_common_coprime", - ``!g:'a monoid. AbelianMonoid g ==> !x y. x IN G /\ y IN G /\ coprime (ord x) (ord y) ==> - ?z. z IN G /\ (ord z = ord x * ord y)``, - rw[AbelianMonoid_def, monoid_order_common_coprime]); - -(* Theorem: AbelianMonoid g ==> - !x y. x IN G /\ y IN G ==> ?z. z IN G /\ (ord z = lcm (ord x) (ord y)) *) -(* Proof: - If ord x = 0, - Then lcm 0 (ord y) = 0 = ord x by LCM_0 - Thus take z = x. - If ord y = 0 - lcm (ord x) 0 = 0 = ord y by LCM_0 - Thus take z = y. - Otherwise, 0 < ord x /\ 0 < ord y. - Let m = ord x, n = ord y. - Note ?a b p q. (lcm m n = p * q) /\ coprime p q /\ - (m = a * p) /\ (n = b * q) by lcm_gcd_park_decompose - Thus p divides m /\ q divides n by divides_def - ==> ?u. u IN G /\ (ord u = p) by monoid_order_divisor, p divides m - and ?v. v IN G /\ (ord v = q) by monoid_order_divisor, q divides n - ==> ?z. z IN G /\ (ord z = p * q) by monoid_order_common_coprime, coprime p q - or z IN G /\ (ord z = lcm m n) by above -*) -val abelian_monoid_order_lcm = store_thm( - "abelian_monoid_order_lcm", - ``!g:'a monoid. AbelianMonoid g ==> - !x y. x IN G /\ y IN G ==> ?z. z IN G /\ (ord z = lcm (ord x) (ord y))``, - rw[AbelianMonoid_def] >> - qabbrev_tac `m = ord x` >> - qabbrev_tac `n = ord y` >> - Cases_on `(m = 0) \/ (n = 0)` >- - metis_tac[LCM_0] >> - `0 < m /\ 0 < n` by decide_tac >> - `?a b p q. (lcm m n = p * q) /\ coprime p q /\ (m = a * p) /\ (n = b * q)` by metis_tac[lcm_gcd_park_decompose] >> - `p divides m /\ q divides n` by metis_tac[divides_def] >> - `?u. u IN G /\ (ord u = p)` by metis_tac[monoid_order_divisor] >> - `?v. v IN G /\ (ord v = q)` by metis_tac[monoid_order_divisor] >> - `?z. z IN G /\ (ord z = p * q)` by rw[monoid_order_common_coprime] >> - metis_tac[]); - -(* This is much better than: -abelian_monoid_order_common -|- !g. AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> - ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) -*) - -(* ------------------------------------------------------------------------- *) -(* Orders of elements *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of elements with a given order *) -val orders_def = Define ` - orders (g:'a monoid) n = {x | x IN G /\ (ord x = n)} -`; - -(* Theorem: !x n. x IN orders g n <=> x IN G /\ (ord x = n) *) -(* Proof: by orders_def *) -val orders_element = store_thm( - "orders_element", - ``!g:'a monoid. !x n. x IN orders g n <=> x IN G /\ (ord x = n)``, - rw[orders_def]); - -(* Theorem: !n. (orders g n) SUBSET G *) -(* Proof: by orders_def, SUBSET_DEF *) -val orders_subset = store_thm( - "orders_subset", - ``!g:'a monoid. !n. (orders g n) SUBSET G``, - rw[orders_def, SUBSET_DEF]); - -(* Theorem: FINITE G ==> !n. FINITE (orders g n) *) -(* Proof: by orders_subset, SUBSET_FINITE *) -val orders_finite = store_thm( - "orders_finite", - ``!g:'a monoid. FINITE G ==> !n. FINITE (orders g n)``, - metis_tac[orders_subset, SUBSET_FINITE]); - -(* Theorem: Monoid g ==> (orders g 1 = {#e}) *) -(* Proof: - orders g 1 - = {x | x IN G /\ (ord x = 1)} by orders_def - = {x | x IN G /\ (x = #e)} by monoid_order_eq_1 - = {#e} by monoid_id_elelment -*) -val orders_eq_1 = store_thm( - "orders_eq_1", - ``!g:'a monoid. Monoid g ==> (orders g 1 = {#e})``, - rw[orders_def, EXTENSION, EQ_IMP_THM, GSYM monoid_order_eq_1]); - -(* ------------------------------------------------------------------------- *) -(* Maximal Order *) -(* ------------------------------------------------------------------------- *) - -(* Overload maximal_order of a group *) -val _ = overload_on("maximal_order", ``\g:'a monoid. MAX_SET (IMAGE ord G)``); - -(* Theorem: maximal_order g = MAX_SET {ord z | z | z IN G} *) -(* Proof: by IN_IMAGE *) -val maximal_order_alt = store_thm( - "maximal_order_alt", - ``!g:'a monoid. maximal_order g = MAX_SET {ord z | z | z IN G}``, - rpt strip_tac >> - `IMAGE ord G = {ord z | z | z IN G}` by rw[EXTENSION] >> - rw[]); - -(* Theorem: In an Abelian Monoid, every nonzero order divides the maximal order. - FiniteAbelianMonoid g ==> !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g) *) -(* Proof: - Let m = maximal_order g = MAX_SET {ord x | x IN G} - Choose z IN G so that ord z = m. - Pick x IN G so that ord x = n. Question: will n divide m ? - - We have: ord x = n, ord z = m bigger. - Let d = gcd(n,m), a = n/d, b = m/d. - Since a | n = ord x, there is ord xa = a - Since b | m = ord y, there is ord xb = b - and gcd(a,b) = 1 by FACTOR_OUT_GCD - - If gcd(a,m) <> 1, let prime p divides gcd(a,m) by PRIME_FACTOR - - Since gcd(a,m) | a and gcd(a,m) divides m, - prime p | a, p | m = b * d, a product. - When prime p divides (b * d), p | b or p | d by P_EUCLIDES - But gcd(a,b)=1, they don't share any common factor, so p | a ==> p not divide b. - If p not divide b, so p | d. - But d | n, d | m, so p | n and p | m. - - Let p^i | n for some max i, mi = MAX_SET {i | p^i divides n}, p^mi | n ==> n = nq * p^mi - and p^j | m for some max j, mj = MAX_SET {j | p^j divides m), p^mj | m ==> m = mq * p^mj - If i <= j, - ppppp | n ppppppp | m - d should picks up all i of the p's, leaving a = n/d with no p, p cannot divide a. - But p | a, so i > j, but this will derive a contradiction: - pppppp | n pppp | m - d picks up j of the p's - Let u = p^i (all prime p in n), v = m/p^j (no prime p) - u | n, so there is ord x = u = p^i u = p^mi - v | m, so there is ord x = v = m/p^j v = m/p^mj - gcd(u,v)=1, since u is pure prime p, v has no prime p (possible gcd = 1, p, p^2, etc.) - So there is ord z = u * v = p^i * m /p^j = m * p^(i-j) .... > m, a contradiction! - - This case is impossible for the max order suitation. - - So gcd(a,m) = 1, there is ord z = a * m = n * m /d - But n * m /d <= m, since m is maximal - i.e. n <= d - But d | n, d <= n, - Hence n = d = gcd(m,n), apply divides_iff_gcd_fix: n divides m. -*) -val monoid_order_divides_maximal = store_thm( - "monoid_order_divides_maximal", - ``!g:'a monoid. FiniteAbelianMonoid g ==> - !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g)``, - rw[FiniteAbelianMonoid_def, AbelianMonoid_def] >> - qabbrev_tac `s = IMAGE ord G` >> - qabbrev_tac `m = MAX_SET s` >> - qabbrev_tac `n = ord x` >> - `#e IN G /\ (ord #e = 1)` by rw[] >> - `s <> {}` by metis_tac[IN_IMAGE, MEMBER_NOT_EMPTY] >> - `FINITE s` by metis_tac[IMAGE_FINITE] >> - `m IN s /\ !y. y IN s ==> y <= m` by rw[MAX_SET_DEF, Abbr`m`] >> - `?z. z IN G /\ (ord z = m)` by metis_tac[IN_IMAGE] >> - `!z. 0 < z <=> z <> 0` by decide_tac >> - `1 <= m` by metis_tac[in_max_set, IN_IMAGE] >> - `0 < m` by decide_tac >> - `?a b. (n = a * gcd n m) /\ (m = b * gcd n m) /\ coprime a b` by metis_tac[FACTOR_OUT_GCD] >> - qabbrev_tac `d = gcd n m` >> - `a divides n /\ b divides m` by metis_tac[divides_def, MULT_COMM] >> - `?xa. xa IN G /\ (ord xa = a)` by metis_tac[monoid_order_divisor] >> - `?xb. xb IN G /\ (ord xb = b)` by metis_tac[monoid_order_divisor] >> - Cases_on `coprime a m` >| [ - `?xc. xc IN G /\ (ord xc = a * m)` by metis_tac[monoid_order_common_coprime] >> - `a * m <= m` by metis_tac[IN_IMAGE] >> - `n * m = d * (a * m)` by rw_tac arith_ss[] >> - `n <= d` by metis_tac[LE_MULT_LCANCEL, LE_MULT_RCANCEL] >> - `d <= n` by metis_tac[GCD_DIVIDES, DIVIDES_MOD_0, DIVIDES_LE] >> - `n = d` by decide_tac >> - metis_tac [divides_iff_gcd_fix], - qabbrev_tac `q = gcd a m` >> - `?p. prime p /\ p divides q` by rw[PRIME_FACTOR] >> - `0 < a` by metis_tac[MULT] >> - `q divides a /\ q divides m` by metis_tac[GCD_DIVIDES, DIVIDES_MOD_0] >> - `p divides a /\ p divides m` by metis_tac[DIVIDES_TRANS] >> - `p divides b \/ p divides d` by metis_tac[P_EUCLIDES] >| [ - `p divides 1` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR, MULT] >> - metis_tac[DIVIDES_ONE, NOT_PRIME_1], - `d divides n` by metis_tac[divides_def] >> - `p divides n` by metis_tac[DIVIDES_TRANS] >> - `?i. 0 < i /\ (p ** i) divides n /\ !k. coprime (p ** k) (n DIV p ** i)` by rw[FACTOR_OUT_PRIME] >> - `?j. 0 < j /\ (p ** j) divides m /\ !k. coprime (p ** k) (m DIV p ** j)` by rw[FACTOR_OUT_PRIME] >> - Cases_on `i > j` >| [ - qabbrev_tac `u = p ** i` >> - qabbrev_tac `v = m DIV p ** j` >> - `0 < p` by metis_tac[PRIME_POS] >> - `v divides m` by metis_tac[DIVIDES_COFACTOR, EXP_EQ_0] >> - `?xu. xu IN G /\ (ord xu = u)` by metis_tac[monoid_order_divisor] >> - `?xv. xv IN G /\ (ord xv = v)` by metis_tac[monoid_order_divisor] >> - `coprime u v` by rw[Abbr`u`] >> - `?xz. xz IN G /\ (ord xz = u * v)` by rw[monoid_order_common_coprime] >> - `m = (p ** j) * v` by metis_tac[DIVIDES_FACTORS, EXP_EQ_0] >> - `p ** (i - j) * m = p ** (i - j) * (p ** j) * v` by rw_tac arith_ss[] >> - `j <= i` by decide_tac >> - `p ** (i - j) * (p ** j) = p ** (i - j + j)` by rw[EXP_ADD] >> - `_ = p ** i` by rw[SUB_ADD] >> - `p ** (i - j) * m = u * v` by rw_tac std_ss[Abbr`u`] >> - `0 < i - j` by decide_tac >> - `1 < p ** (i - j)` by rw[ONE_LT_EXP, ONE_LT_PRIME] >> - `m < p ** (i - j) * m` by rw[LT_MULT_RCANCEL] >> - `m < u * v` by metis_tac[] >> - `u * v > m` by decide_tac >> - `u * v <= m` by metis_tac[IN_IMAGE] >> - metis_tac[NOT_GREATER], - `i <= j` by decide_tac >> - `0 < p` by metis_tac[PRIME_POS] >> - `p ** i <> 0 /\ p ** j <> 0` by metis_tac[EXP_EQ_0] >> - `n = (p ** i) * (n DIV p ** i)` by metis_tac[DIVIDES_FACTORS] >> - `m = (p ** j) * (m DIV p ** j)` by metis_tac[DIVIDES_FACTORS] >> - `p ** (j - i) * (p ** i) = p ** (j - i + i)` by rw[EXP_ADD] >> - `_ = p ** j` by rw[SUB_ADD] >> - `m = p ** (j - i) * (p ** i) * (m DIV p ** j)` by rw_tac std_ss[] >> - `_ = (p ** i) * (p ** (j - i) * (m DIV p ** j))` by rw_tac arith_ss[] >> - qabbrev_tac `u = p ** i` >> - qabbrev_tac `v = n DIV u` >> - `u divides m` by metis_tac[divides_def, MULT_COMM] >> - `u divides d` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] >> - `?c. d = c * u` by metis_tac[divides_def] >> - `n = (a * c) * u` by rw_tac arith_ss[] >> - `v = c * a` by metis_tac[MULT_RIGHT_CANCEL, MULT_COMM] >> - `a divides v` by metis_tac[divides_def] >> - `p divides v` by metis_tac[DIVIDES_TRANS] >> - `p divides u` by metis_tac[DIVIDES_EXP_BASE, DIVIDES_REFL] >> - `d <> 0` by metis_tac[MULT_0] >> - `c <> 0` by metis_tac[MULT] >> - `v <> 0` by metis_tac[MULT_EQ_0] >> - `p divides (gcd v u)` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] >> - `coprime u v` by metis_tac[] >> - metis_tac[GCD_SYM, DIVIDES_ONE, NOT_PRIME_1] - ] - ] - ]); - -(* This is a milestone theorem. *) - -(* Another proof based on the following: - -The Multiplicative Group of a Finite Field (Ryan Vinroot) -http://www.math.wm.edu/~vinroot/430S13MultFiniteField.pdf - -*) - -(* Theorem: FiniteAbelianMonoid g ==> - !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g) *) -(* Proof: - Note AbelianMonoid g /\ FINITE G by FiniteAbelianMonoid_def - Let ord z = m = maximal_order g, attained by some z IN G. - Let ord x = n, and n <= m since m is maximal_order, so 0 < m. - Then x IN G /\ z IN G - ==> ?y. y IN G /\ ord y = lcm n m by abelian_monoid_order_lcm - Note lcm n m <= m by m is maximal_order - but m <= lcm n m by LCM_LE, lcm is a common multiple - ==> lcm n m = m by EQ_LESS_EQ - or n divides m by divides_iff_lcm_fix -*) -Theorem monoid_order_divides_maximal[allow_rebind]: - !g:'a monoid. - FiniteAbelianMonoid g ==> - !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g) -Proof - rw[FiniteAbelianMonoid_def] >> - ‘Monoid g’ by metis_tac[AbelianMonoid_def] >> - qabbrev_tac ‘s = IMAGE ord G’ >> - qabbrev_tac ‘m = MAX_SET s’ >> - qabbrev_tac ‘n = ord x’ >> - ‘#e IN G /\ (ord #e = 1)’ by rw[] >> - ‘s <> {}’ by metis_tac[IN_IMAGE, MEMBER_NOT_EMPTY] >> - ‘FINITE s’ by rw[Abbr‘s’] >> - ‘m IN s /\ !y. y IN s ==> y <= m’ by rw[MAX_SET_DEF, Abbr‘m’] >> - ‘?z. z IN G /\ (ord z = m)’ by metis_tac[IN_IMAGE] >> - ‘?y. y IN G /\ (ord y = lcm n m)’ by metis_tac[abelian_monoid_order_lcm] >> - ‘n IN s /\ ord y IN s’ by rw[Abbr‘s’, Abbr‘n’] >> - ‘n <= m /\ lcm n m <= m’ by metis_tac[] >> - ‘0 < m’ by decide_tac >> - ‘m <= lcm n m’ by rw[LCM_LE] >> - rw[divides_iff_lcm_fix] -QED - -(* ------------------------------------------------------------------------- *) -(* Monoid Invertibles *) -(* ------------------------------------------------------------------------- *) - -(* The Invertibles are those with inverses. *) -val monoid_invertibles_def = Define` - monoid_invertibles (g:'a monoid) = - { x | x IN G /\ (?y. y IN G /\ (x * y = #e) /\ (y * x = #e)) } -`; -val _ = overload_on ("G*", ``monoid_invertibles g``); - -(* Theorem: x IN G* <=> x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e) *) -(* Proof: by monoid_invertibles_def. *) -val monoid_invertibles_element = store_thm( - "monoid_invertibles_element", - ``!g:'a monoid x. x IN G* <=> x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e)``, - rw[monoid_invertibles_def]); - -(* Theorem: Monoid g /\ x IN G /\ 0 < ord x ==> x IN G* *) -(* Proof: - By monoid_invertibles_def, this is to show: - ?y. y IN G /\ (x * y = #e) /\ (y * x = #e) - Since x ** (ord x) = #e by order_property - and ord x = SUC n by ord x <> 0 - Now, x ** SUC n = x * x ** n by monoid_exp_SUC - x ** SUC n = x ** n * x by monoid_exp_suc - and x ** n IN G by monoid_exp_element - Hence taking y = x ** n will satisfy the requirements. -*) -val monoid_order_nonzero = store_thm( - "monoid_order_nonzero", - ``!g:'a monoid x. Monoid g /\ x IN G /\ 0 < ord x ==> x IN G*``, - rw[monoid_invertibles_def] >> - `x ** (ord x) = #e` by rw[order_property] >> - `ord x <> 0` by decide_tac >> - metis_tac[num_CASES, monoid_exp_SUC, monoid_exp_suc, monoid_exp_element]); - -(* The Invertibles of a monoid, will be a Group. *) -val Invertibles_def = Define` - Invertibles (g:'a monoid) : 'a monoid = - <| carrier := G*; - op := g.op; - id := g.id - |> -`; -(* -- type_of ``Invertibles g``; -> val it = ``:'a moniod`` : hol_type -*) - -(* Theorem: properties of Invertibles *) -(* Proof: by Invertibles_def. *) -val Invertibles_property = store_thm( - "Invertibles_property", - ``!g:'a monoid. ((Invertibles g).carrier = G*) /\ - ((Invertibles g).op = g.op) /\ - ((Invertibles g).id = #e) /\ - ((Invertibles g).exp = g.exp)``, - rw[Invertibles_def, monoid_exp_def, FUN_EQ_THM]); - -(* Theorem: (Invertibles g).carrier = monoid_invertibles g *) -(* Proof: by Invertibles_def. *) -val Invertibles_carrier = store_thm( - "Invertibles_carrier", - ``!g:'a monoid. (Invertibles g).carrier = monoid_invertibles g``, - rw[Invertibles_def]); - -(* Theorem: (Invertibles g).carrier SUBSET G *) -(* Proof: - (Invertibles g).carrier - = G* by Invertibles_def - = {x | x IN G /\ ... } by monoid_invertibles_def - SUBSET G by SUBSET_DEF -*) -val Invertibles_subset = store_thm( - "Invertibles_subset", - ``!g:'a monoid. (Invertibles g).carrier SUBSET G``, - rw[Invertibles_def, monoid_invertibles_def, SUBSET_DEF]); - -(* Theorem: order (Invertibles g) x = order g x *) -(* Proof: order_def, period_def, Invertibles_property *) -val Invertibles_order = store_thm( - "Invertibles_order", - ``!g:'a monoid. !x. order (Invertibles g) x = order g x``, - rw[order_def, period_def, Invertibles_property]); - -(* ------------------------------------------------------------------------- *) -(* Monoid Inverse as an operation *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: x IN G* means inverse of x exists. *) -(* Proof: by definition of G*. *) -val monoid_inv_from_invertibles = store_thm( - "monoid_inv_from_invertibles", - ``!g:'a monoid. Monoid g ==> !x. x IN G* ==> ?y. y IN G /\ (x * y = #e) /\ (y * x = #e)``, - rw[monoid_invertibles_def]); - -(* Convert this into the form: !g x. ?y. ..... for SKOLEM_THM *) -val lemma = prove(``!(g:'a monoid) x. ?y. Monoid g /\ x IN G* ==> y IN G /\ (x * y = #e) /\ (y * x = #e)``, - metis_tac[monoid_inv_from_invertibles]); - -(* Use Skolemization to generate the monoid_inv_from_invertibles function *) -val monoid_inv_def = new_specification( - "monoid_inv_def", - ["monoid_inv"], (* name of function *) - SIMP_RULE (srw_ss()) [SKOLEM_THM] lemma); -(* > val monoid_inv_def = |- !g x. Monoid g /\ x IN G* ==> - monoid_inv g x IN G /\ (x * monoid_inv g x = #e) /\ (monoid_inv g x * x = #e) : thm *) - -(* -- type_of ``monoid_inv g``; -> val it = ``:'a -> 'a`` : hol_type -*) - -(* Convert inv function to inv field, i.e. m.inv is defined to be monoid_inv. *) -val _ = add_record_field ("inv", ``monoid_inv``); -(* -- type_of ``g.inv``; -> val it = ``:'a -> 'a`` : hol_type -*) -(* val _ = overload_on ("|/", ``g.inv``); *) (* for non-unicode input *) - -(* for unicode dispaly *) -val _ = add_rule{fixity = Suffix 2100, - term_name = "reciprocal", - block_style = (AroundEachPhrase, (PP.CONSISTENT, 0)), - paren_style = OnlyIfNecessary, - pp_elements = [TOK "⁻¹"]}; -val _ = overload_on("reciprocal", ``monoid_inv g``); -val _ = overload_on ("|/", ``reciprocal``); (* for non-unicode input *) - -(* This means: reciprocal will have the display ⁻¹, and here reciprocal is short-name for monoid_inv g *) - -(* - monoid_inv_def; -> val it = |- !g x. Monoid g /\ x IN G* ==> |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) : thm -*) - -(* Theorem: x IN G* <=> x IN G /\ |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) *) -(* Proof: by definition. *) -val monoid_inv_def_alt = store_thm( - "monoid_inv_def_alt", - ``!g:'a monoid. Monoid g ==> (!x. x IN G* <=> x IN G /\ |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e))``, - rw[monoid_invertibles_def, monoid_inv_def, EQ_IMP_THM] >> - metis_tac[]); - -(* In preparation for: The invertibles of a monoid form a group. *) - -(* Theorem: x IN G* ==> x IN G *) -(* Proof: by definition of G*. *) -val monoid_inv_element = store_thm( - "monoid_inv_element", - ``!g:'a monoid. Monoid g ==> !x. x IN G* ==> x IN G``, - rw[monoid_invertibles_def]); - -(* This export will cause rewrites of RHS = x IN G to become proving LHS = x IN G*, which is not useful. *) -(* val _ = export_rewrites ["monoid_inv_element"]; *) - -(* Theorem: #e IN G* *) -(* Proof: by monoid_id and definition. *) -val monoid_id_invertible = store_thm( - "monoid_id_invertible", - ``!g:'a monoid. Monoid g ==> #e IN G*``, - rw[monoid_invertibles_def] >> - qexists_tac `#e` >> - rw[]); - -val _ = export_rewrites ["monoid_id_invertible"]; - -(* This is a direct proof, next one is shorter. *) - -(* Theorem: [Closure for Invertibles] x, y IN G* ==> x * y IN G* *) -(* Proof: inverse of (x * y) = (inverse of y) * (inverse of x) - Note x IN G* ==> - |/x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) by monoid_inv_def - y IN G* ==> - |/y IN G /\ (y * |/ y = #e) /\ ( |/ y * y = #e) by monoid_inv_def - Now x * y IN G and | /y * | / x IN G by monoid_op_element - and (x * y) * ( |/ y * |/ x) = #e by monoid_assoc, monoid_lid - also ( |/ y * |/ x) * (x * y) = #e by monoid_assoc, monoid_lid - Thus x * y IN G*, with ( |/ y * |/ x) as its inverse. -*) -val monoid_inv_op_invertible = store_thm( - "monoid_inv_op_invertible", - ``!g:'a monoid. Monoid g ==> !x y. x IN G* /\ y IN G* ==> x * y IN G*``, - rpt strip_tac>> - `x IN G /\ y IN G` by rw_tac std_ss[monoid_inv_element] >> - `|/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e)` by rw_tac std_ss[monoid_inv_def] >> - `|/ y IN G /\ (y * |/ y = #e) /\ ( |/ y * y = #e)` by rw_tac std_ss[monoid_inv_def] >> - `x * y IN G /\ |/ y * |/ x IN G` by rw_tac std_ss[monoid_op_element] >> - `(x * y) * ( |/ y * |/ x) = x * ((y * |/ y) * |/ x)` by rw_tac std_ss[monoid_assoc, monoid_op_element] >> - `( |/ y * |/ x) * (x * y) = |/ y * (( |/ x * x) * y)` by rw_tac std_ss[monoid_assoc, monoid_op_element] >> - rw_tac std_ss[monoid_invertibles_def, GSPECIFICATION] >> - metis_tac[monoid_lid]); - -(* Better proof of the same theorem. *) - -(* Theorem: [Closure for Invertibles] x, y IN G* ==> x * y IN G* *) -(* Proof: inverse of (x * y) = (inverse of y) * (inverse of x) *) -Theorem monoid_inv_op_invertible[allow_rebind,simp]: - !g:'a monoid. Monoid g ==> !x y. x IN G* /\ y IN G* ==> x * y IN G* -Proof - rw[monoid_invertibles_def] >> - qexists_tac `y'' * y'` >> - rw_tac std_ss[monoid_op_element] >| [ - `x * y * (y'' * y') = x * ((y * y'') * y')` by rw[monoid_assoc], - `y'' * y' * (x * y) = y'' * ((y' * x) * y)` by rw[monoid_assoc] - ] >> rw_tac std_ss[monoid_lid] -QED - -(* Theorem: x IN G* ==> |/ x IN G* *) -(* Proof: by monoid_inv_def. *) -val monoid_inv_invertible = store_thm( - "monoid_inv_invertible", - ``!g:'a monoid. Monoid g ==> !x. x IN G* ==> |/ x IN G*``, - rpt strip_tac >> - rw[monoid_invertibles_def] >- - rw[monoid_inv_def] >> - metis_tac[monoid_inv_def, monoid_inv_element]); - -val _ = export_rewrites ["monoid_inv_invertible"]; - -(* Theorem: The Invertibles of a monoid form a monoid. *) -(* Proof: by checking definition. *) -val monoid_invertibles_is_monoid = store_thm( - "monoid_invertibles_is_monoid", - ``!g. Monoid g ==> Monoid (Invertibles g)``, - rpt strip_tac >> - `!x. x IN G* ==> x IN G` by rw[monoid_inv_element] >> - rw[Invertibles_def] >> - rewrite_tac[Monoid_def] >> - rw[monoid_assoc]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/monoid/monoidScript.sml b/examples/algebra/monoid/monoidScript.sml deleted file mode 100644 index 393dc3129b..0000000000 --- a/examples/algebra/monoid/monoidScript.sml +++ /dev/null @@ -1,903 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Monoid *) -(* ------------------------------------------------------------------------- *) - -(* - -Monoid Theory -============= -A monoid is a semi-group with an identity. -The units of a monoid form a group. -A finite, cancellative monoid is also a group. - -*) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "monoid"; - -(* ------------------------------------------------------------------------- *) - - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* open dependent theories *) -open pred_setTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* val _ = load "helperNumTheory"; *) -(* val _ = load "helperSetTheory"; *) -open helperNumTheory helperSetTheory; - - -(* ------------------------------------------------------------------------- *) -(* Monoid Documentation *) -(* ------------------------------------------------------------------------- *) -(* Data type: - The generic symbol for monoid data is g. - g.carrier = Carrier set of monoid, overloaded as G. - g.op = Binary operation of monoid, overloaded as *. - g.id = Identity element of monoid, overloaded as #e. - - Overloading: - * = g.op - #e = g.id - ** = g.exp - G = g.carrier - GITSET g s b = ITSET g.op s b -*) -(* Definitions and Theorems (# are exported): - - Definitions: - Monoid_def |- !g. Monoid g <=> - (!x y. x IN G /\ y IN G ==> x * y IN G) /\ - (!x y z. x IN G /\ y IN G /\ z IN G ==> ((x * y) * z = x * (y * z))) /\ - #e IN G /\ (!x. x IN G ==> (#e * x = x) /\ (x * #e = x)) - AbelianMonoid_def |- !g. AbelianMonoid g <=> Monoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x) -# FiniteMonoid_def |- !g. FiniteMonoid g <=> Monoid g /\ FINITE G -# FiniteAbelianMonoid_def |- !g. FiniteAbelianMonoid g <=> AbelianMonoid g /\ FINITE G - - Extract from definition: -# monoid_id_element |- !g. Monoid g ==> #e IN G -# monoid_op_element |- !g. Monoid g ==> !x y. x IN G /\ y IN G ==> x * y IN G - monoid_assoc |- !g. Monoid g ==> !x y z. x IN G /\ y IN G /\ z IN G ==> (x * y * z = x * (y * z)) - monoid_id |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) /\ (x * #e = x) -# monoid_lid |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) -# monoid_rid |- !g. Monoid g ==> !x. x IN G ==> (x * #e = x) -# monoid_id_id |- !g. Monoid g ==> (#e * #e = #e) - - Simple theorems: - FiniteAbelianMonoid_def_alt |- !g. FiniteAbelianMonoid g <=> FiniteMonoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x) - monoid_carrier_nonempty |- !g. Monoid g ==> G <> {} - monoid_lid_unique |- !g. Monoid g ==> !e. e IN G ==> (!x. x IN G ==> (e * x = x)) ==> (e = #e) - monoid_rid_unique |- !g. Monoid g ==> !e. e IN G ==> (!x. x IN G ==> (x * e = x)) ==> (e = #e) - monoid_id_unique |- !g. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x) /\ (e * x = x)) <=> (e = #e)) - - Iteration of the binary operation gives exponentiation: - monoid_exp_def |- !g x n. x ** n = FUNPOW ($* x) n #e -# monoid_exp_0 |- !g x. x ** 0 = #e -# monoid_exp_SUC |- !g x n. x ** SUC n = x * x ** n -# monoid_exp_element |- !g. Monoid g ==> !x. x IN G ==> !n. x ** n IN G -# monoid_exp_1 |- !g. Monoid g ==> !x. x IN G ==> (x ** 1 = x) -# monoid_id_exp |- !g. Monoid g ==> !n. #e ** n = #e - - Monoid commutative elements: - monoid_comm_exp |- !g. Monoid g ==> !x y. x IN G /\ y IN G ==> (x * y = y * x) ==> !n. x ** n * y = y * x ** n - monoid_exp_comm |- !g. Monoid g ==> !x. x IN G ==> !n. x ** n * x = x * x ** n -# monoid_exp_suc |- !g. Monoid g ==> !x. x IN G ==> !n. x ** SUC n = x ** n * x - monoid_comm_op_exp |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> - !n. (x * y) ** n = x ** n * y ** n - monoid_comm_exp_exp |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> - !n m. x ** n * y ** m = y ** m * x ** n -# monoid_exp_add |- !g. Monoid g ==> !x. x IN G ==> !n k. x ** (n + k) = x ** n * x ** k -# monoid_exp_mult |- !g. Monoid g ==> !x. x IN G ==> !n k. x ** (n * k) = (x ** n) ** k - monoid_exp_mult_comm |- !g. Monoid g ==> !x. x IN G ==> !m n. (x ** m) ** n = (x ** n) ** m - - - Finite Monoid: - finite_monoid_exp_not_distinct |- !g. FiniteMonoid g ==> !x. x IN G ==> - ?h k. (x ** h = x ** k) /\ h <> k - - Abelian Monoid ITSET Theorems: - GITSET_THM |- !s g b. FINITE s ==> (GITSET g s b = if s = {} then b - else GITSET g (REST s) (CHOICE s * b)) - GITSET_EMPTY |- !g b. GITSET g {} b = b - GITSET_INSERT |- !x. FINITE s ==> - !x g b. (GITSET g (x INSERT s) b = GITSET g (REST (x INSERT s)) (CHOICE (x INSERT s) * b)) - GITSET_PROPERTY |- !g s. FINITE s /\ s <> {} ==> !b. GITSET g s b = GITSET g (REST s) (CHOICE s * b) - - abelian_monoid_op_closure_comm_assoc_fun |- !g. AbelianMonoid g ==> closure_comm_assoc_fun $* G - COMMUTING_GITSET_INSERT |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::G. GITSET g (x INSERT s) b = GITSET g (s DELETE x) (x * b) - COMMUTING_GITSET_REDUCTION |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::G. GITSET g s (x * b) = x * GITSET g s b - COMMUTING_GITSET_RECURSES |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::G. GITSET g (x INSERT s) b = x * GITSET g (s DELETE x) b: - - Abelian Monoid PROD_SET: - GPROD_SET_def |- !g s. GPROD_SET g s = GITSET g s #e - GPROD_SET_THM |- !g s. (GPROD_SET g {} = #e) /\ - (AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !x::(G). GPROD_SET g (x INSERT s) = x * GPROD_SET g (s DELETE x)) - GPROD_SET_EMPTY |- !g s. GPROD_SET g {} = #e - GPROD_SET_SING |- !g. Monoid g ==> !x. x IN G ==> (GPROD_SET g {x} = x) - GPROD_SET_PROPERTY |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> GPROD_SET g s IN G - -*) - -(* ------------------------------------------------------------------------- *) -(* Monoid Definition. *) -(* ------------------------------------------------------------------------- *) - -(* Monoid and Group share the same type *) - -(* Set up monoid type as a record - A Monoid has: - . a carrier set (set = function 'a -> bool, since MEM is a boolean function) - . a binary operation (2-nary function) called multiplication - . an identity element for the binary operation -*) -val _ = Hol_datatype` - monoid = <| carrier: 'a -> bool; - op: 'a -> 'a -> 'a; - id: 'a - |>`; -(* If the field inv: 'a -> 'a; is included, - there will be an implicit monoid_inv accessor generated. - Later, when monoid_inv_def defines another monoid_inv, - the monoid_accessors will ALL be invalidated! - So, do not include the field inv here, - but use add_record_field ("inv", ``monoid_inv``) - to achieve the same effect. -*) - -(* Using symbols m for monoid and g for group - will give excessive overloading for Monoid and Group, - so the generic symbol for both is taken as g. *) -(* set overloading *) -val _ = overload_on ("*", ``g.op``); -val _ = overload_on ("#e", ``g.id``); -val _ = overload_on ("G", ``g.carrier``); - -(* Monoid Definition: - A Monoid is a set with elements of type 'a monoid, such that - . Closure: x * y is in the carrier set - . Associativity: (x * y) * z = x * (y * z)) - . Existence of identity: #e is in the carrier set - . Property of identity: #e * x = x * #e = x -*) -(* Define Monoid by predicate *) -val Monoid_def = Define` - Monoid (g:'a monoid) <=> - (!x y. x IN G /\ y IN G ==> x * y IN G) /\ - (!x y z. x IN G /\ y IN G /\ z IN G ==> ((x * y) * z = x * (y * z))) /\ - #e IN G /\ (!x. x IN G ==> (#e * x = x) /\ (x * #e = x)) -`; -(* export basic definition -- but too many and's. *) -(* val _ = export_rewrites ["Monoid_def"]; *) - -(* ------------------------------------------------------------------------- *) -(* More Monoid Defintions. *) -(* ------------------------------------------------------------------------- *) - -(* Abelian Monoid = a Monoid that is commutative: x * y = y * x. *) -val AbelianMonoid_def = Define` - AbelianMonoid (g:'a monoid) <=> - Monoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x) -`; -(* export simple definition -- but this one has commutativity, so don't. *) -(* val _ = export_rewrites ["AbelianMonoid_def"]; *) - -(* Finite Monoid = a Monoid with a finite carrier set. *) -val FiniteMonoid_def = Define` - FiniteMonoid (g:'a monoid) <=> - Monoid g /\ FINITE G -`; -(* export simple definition. *) -val _ = export_rewrites ["FiniteMonoid_def"]; - -(* Finite Abelian Monoid = a Monoid that is both Finite and Abelian. *) -val FiniteAbelianMonoid_def = Define` - FiniteAbelianMonoid (g:'a monoid) <=> - AbelianMonoid g /\ FINITE G -`; -(* export simple definition. *) -val _ = export_rewrites ["FiniteAbelianMonoid_def"]; - -(* ------------------------------------------------------------------------- *) -(* Basic theorems from definition. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: Finite Abelian Monoid = Finite Monoid /\ commutativity. *) -(* Proof: by definitions. *) -val FiniteAbelianMonoid_def_alt = store_thm( - "FiniteAbelianMonoid_def_alt", - ``!g:'a monoid. FiniteAbelianMonoid g <=> - FiniteMonoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x)``, - rw[AbelianMonoid_def, EQ_IMP_THM]); - -(* Monoid clauses from definition, in implicative form, no for-all, internal use only. *) -val monoid_clauses = Monoid_def |> SPEC_ALL |> #1 o EQ_IMP_RULE; -(* > val monoid_clauses = - |- Monoid g ==> - (!x y. x IN G /\ y IN G ==> x * y IN G) /\ - (!x y z. x IN G /\ y IN G /\ z IN G ==> (x * y * z = x * (y * z))) /\ - #e IN G /\ !x. x IN G ==> (#e * x = x) /\ (x * #e = x) : thm *) - -(* Extract theorems from Monoid clauses. *) -(* No need to export as definition is already exported. *) - -(* Theorem: [Closure] x * y in carrier. *) -val monoid_op_element = save_thm("monoid_op_element", - monoid_clauses |> UNDISCH_ALL |> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); -(* > val monoid_op_element = |- !g. Monoid g ==> !x y. x IN G /\ y IN G ==> x * y IN G : thm*) - -(* Theorem: [Associativity] (x * y) * z = x * (y * z) *) -val monoid_assoc = save_thm("monoid_assoc", - monoid_clauses |> UNDISCH_ALL |> CONJUNCT2|> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); -(* > val monoid_assoc = |- !g. Monoid g ==> !x y z. x IN G /\ y IN G /\ z IN G ==> (x * y * z = x * (y * z)) : thm *) - -(* Theorem: [Identity exists] #e in carrier. *) -val monoid_id_element = save_thm("monoid_id_element", - monoid_clauses |> UNDISCH_ALL |> CONJUNCT2|> CONJUNCT2 |> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); -(* > val monoid_id_element = |- !g. Monoid g ==> #e IN G : thm *) - -(* Theorem: [Identity property] #e * x = x and x * #e = x *) -val monoid_id = save_thm("monoid_id", - monoid_clauses |> UNDISCH_ALL |> CONJUNCT2|> CONJUNCT2 |> CONJUNCT2 |> DISCH_ALL |> GEN_ALL); -(* > val monoid_id = |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) /\ (x * #e = x) : thm *) - -(* Theorem: [Left identity] #e * x = x *) -(* Proof: from monoid_id. *) -val monoid_lid = save_thm("monoid_lid", - monoid_id |> SPEC_ALL |> UNDISCH_ALL |> SPEC_ALL |> UNDISCH_ALL |> CONJUNCT1 - |> DISCH ``x IN G`` |> GEN_ALL |> DISCH_ALL |> GEN_ALL); -(* > val monoid_lid = |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) : thm *) - -(* Theorem: [Right identity] x * #e = x *) -(* Proof: from monoid_id. *) -val monoid_rid = save_thm("monoid_rid", - monoid_id |> SPEC_ALL |> UNDISCH_ALL |> SPEC_ALL |> UNDISCH_ALL |> CONJUNCT2 - |> DISCH ``x IN G`` |> GEN_ALL |> DISCH_ALL |> GEN_ALL); -(* > val monoid_rid = |- !g. Monoid g ==> !x. x IN G ==> (x * #e = x) : thm *) - -(* export simple statements (no complicated and's) *) -val _ = export_rewrites ["monoid_op_element"]; -(* val _ = export_rewrites ["monoid_assoc"]; -- no associativity *) -val _ = export_rewrites ["monoid_id_element"]; -val _ = export_rewrites ["monoid_lid"]; -val _ = export_rewrites ["monoid_rid"]; - -(* Theorem: #e * #e = #e *) -(* Proof: - by monoid_lid and monoid_id_element. -*) -val monoid_id_id = store_thm( - "monoid_id_id", - ``!g:'a monoid. Monoid g ==> (#e * #e = #e)``, - rw[]); - -val _ = export_rewrites ["monoid_id_id"]; - -(* ------------------------------------------------------------------------- *) -(* Theorems in basic Monoid Theory. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: [Monoid carrier nonempty] G <> {} *) -(* Proof: by monoid_id_element. *) -val monoid_carrier_nonempty = store_thm( - "monoid_carrier_nonempty", - ``!g:'a monoid. Monoid g ==> G <> {}``, - metis_tac[monoid_id_element, MEMBER_NOT_EMPTY]); - -(* Theorem: [Left Identity unique] !x. (e * x = x) ==> e = #e *) -(* Proof: - Put x = #e, - then e * #e = #e by given - but e * #e = e by monoid_rid - hence e = #e. -*) -val monoid_lid_unique = store_thm( - "monoid_lid_unique", - ``!g:'a monoid. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (e * x = x)) ==> (e = #e))``, - metis_tac[monoid_id_element, monoid_rid]); - -(* Theorem: [Right Identity unique] !x. (x * e = x) ==> e = #e *) -(* Proof: - Put x = #e, - then #e * e = #e by given - but #e * e = e by monoid_lid - hence e = #e. -*) -val monoid_rid_unique = store_thm( - "monoid_rid_unique", - ``!g:'a monoid. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x)) ==> (e = #e))``, - metis_tac[monoid_id_element, monoid_lid]); - -(* Theorem: [Identity unique] !x. (x * e = x) and (e * x = x) <=> e = #e *) -(* Proof: - If e, #e are two identities, - For e, put x = #e, #e*e = #e and e*#e = #e - For #e, put x = e, e*#e = e and #e*e = e - Therefore e = #e. -*) -val monoid_id_unique = store_thm( - "monoid_id_unique", - ``!g:'a monoid. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x) /\ (e * x = x)) <=> (e = #e))``, - metis_tac[monoid_id_element, monoid_id]); - - -(* ------------------------------------------------------------------------- *) -(* Application of basic Monoid Theory: *) -(* Exponentiation - the FUNPOW version of Monoid operation. *) -(* ------------------------------------------------------------------------- *) - -(* Define exponents of a monoid element: - For x in Monoid g, x ** 0 = #e - x ** (SUC n) = x * (x ** n) -*) -(* -val monoid_exp_def = Define` - (monoid_exp m x 0 = g.id) /\ - (monoid_exp m x (SUC n) = x * (monoid_exp m x n)) -`; -*) -val monoid_exp_def = Define `monoid_exp (g:'a monoid) (x:'a) n = FUNPOW (g.op x) n #e`; -(* val _ = export_rewrites ["monoid_exp_def"]; *) -(* -- monoid_exp_def; -> val it = |- !g x n. x ** n = FUNPOW ($* x) n #e : thm -*) - -(* export simple properties later *) -(* val _ = export_rewrites ["monoid_exp_def"]; *) - -(* Convert exp function to exp field, i.e. g.exp is defined to be monoid_exp. *) -val _ = add_record_field ("exp", ``monoid_exp``); -(* -- type_of ``g.exp``; -> val it = ``:'a -> num -> 'a`` : hol_type -*) -(* overloading *) -(* val _ = clear_overloads_on "**"; *) -(* val _ = overload_on ("**", ``monoid_exp g``); -- not this *) -val _ = overload_on ("**", ``g.exp``); - -(* Theorem: x ** 0 = #e *) -(* Proof: by definition and FUNPOW_0. *) -val monoid_exp_0 = store_thm( - "monoid_exp_0", - ``!g:'a monoid. !x:'a. x ** 0 = #e``, - rw[monoid_exp_def]); - -val _ = export_rewrites ["monoid_exp_0"]; - -(* Theorem: x ** (SUC n) = x * (x ** n) *) -(* Proof: by definition and FUNPOW_SUC. *) -val monoid_exp_SUC = store_thm( - "monoid_exp_SUC", - ``!g:'a monoid. !x:'a. !n. x ** (SUC n) = x * (x ** n)``, - rw[monoid_exp_def, FUNPOW_SUC]); - -(* should this be exported? Only FUNPOW_0 is exported. *) -val _ = export_rewrites ["monoid_exp_SUC"]; - -(* Theorem: (x ** n) in G *) -(* Proof: by induction on n. - Base case: x ** 0 IN G - x ** 0 = #e by monoid_exp_0 - in G by monoid_id_element. - Step case: x ** n IN G ==> x ** SUC n IN G - x ** SUC n - = x * (x ** n) by monoid_exp_SUC - in G by monoid_op_element and induction hypothesis -*) -val monoid_exp_element = store_thm( - "monoid_exp_element", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n. (x ** n) IN G``, - rpt strip_tac>> - Induct_on `n` >> - rw[]); - -val _ = export_rewrites ["monoid_exp_element"]; - -(* Theorem: x ** 1 = x *) -(* Proof: - x ** 1 - = x ** SUC 0 by ONE - = x * x ** 0 by monoid_exp_SUC - = x * #e by monoid_exp_0 - = x by monoid_rid -*) -val monoid_exp_1 = store_thm( - "monoid_exp_1", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> (x ** 1 = x)``, - rewrite_tac[ONE] >> - rw[]); - -val _ = export_rewrites ["monoid_exp_1"]; - -(* Theorem: (#e ** n) = #e *) -(* Proof: by induction on n. - Base case: #e ** 0 = #e - true by monoid_exp_0. - Step case: #e ** n = #e ==> #e ** SUC n = #e - #e ** SUC n - = #e * #e ** n by monoid_exp_SUC, monoid_id_element - = #e ** n by monoid_lid, monoid_exp_element - hence true by induction hypothesis. -*) -val monoid_id_exp = store_thm( - "monoid_id_exp", - ``!g:'a monoid. Monoid g ==> !n. #e ** n = #e``, - rpt strip_tac>> - Induct_on `n` >> - rw[]); - -val _ = export_rewrites ["monoid_id_exp"]; - -(* Theorem: For Abelian Monoid g, (x ** n) * y = y * (x ** n) *) -(* Proof: - Since x ** n IN G by monoid_exp_element - True by abelian property: !z y. z IN G /\ y IN G ==> z * y = y * z -*) -(* This is trivial for AbelianMonoid, since every element commutes. - However, what is needed is just for those elements that commute. *) - -(* Theorem: x * y = y * x ==> (x ** n) * y = y * (x ** n) *) -(* Proof: - By induction on n. - Base case: x ** 0 * y = y * x ** 0 - (x ** 0) * y - = #e * y by monoid_exp_0 - = y * #e by monoid_id - = y * (x ** 0) by monoid_exp_0 - Step case: x ** n * y = y * x ** n ==> x ** SUC n * y = y * x ** SUC n - x ** (SUC n) * y - = (x * x ** n) * y by monoid_exp_SUC - = x * ((x ** n) * y) by monoid_assoc - = x * (y * (x ** n)) by induction hypothesis - = (x * y) * (x ** n) by monoid_assoc - = (y * x) * (x ** n) by abelian property - = y * (x * (x ** n)) by monoid_assoc - = y * x ** (SUC n) by monoid_exp_SUC -*) -val monoid_comm_exp = store_thm( - "monoid_comm_exp", - ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G ==> (x * y = y * x) ==> !n. (x ** n) * y = y * (x ** n)``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - metis_tac[monoid_exp_SUC, monoid_assoc, monoid_exp_element]); - -(* do not export commutativity check *) -(* val _ = export_rewrites ["monoid_comm_exp"]; *) - -(* Theorem: (x ** n) * x = x * (x ** n) *) -(* Proof: - Since x * x = x * x, this is true by monoid_comm_exp. -*) -val monoid_exp_comm = store_thm( - "monoid_exp_comm", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n. (x ** n) * x = x * (x ** n)``, - rw[monoid_comm_exp]); - -(* no export of commutativity *) -(* val _ = export_rewrites ["monoid_exp_comm"]; *) - -(* Theorem: x ** (SUC n) = (x ** n) * x *) -(* Proof: by monoid_exp_SUC and monoid_exp_comm. *) -val monoid_exp_suc = store_thm( - "monoid_exp_suc", - ``!g:'a monoid. Monoid g ==> !x:'a. x IN G ==> !n. x ** (SUC n) = (x ** n) * x``, - rw[monoid_exp_comm]); - -(* no export of commutativity *) -(* val _ = export_rewrites ["monoid_exp_suc"]; *) - -(* Theorem: x * y = y * x ==> (x * y) ** n = (x ** n) * (y ** n) *) -(* Proof: - By induction on n. - Base case: (x * y) ** 0 = x ** 0 * y ** 0 - (x * y) ** 0 - = #e by monoid_exp_0 - = #e * #e by monoid_id_id - = (x ** 0) * (y ** 0) by monoid_exp_0 - Step case: (x * y) ** n = (x ** n) * (y ** n) ==> (x * y) ** SUC n = x ** SUC n * y ** SUC n - (x * y) ** (SUC n) - = (x * y) * ((x * y) ** n) by monoid_exp_SUC - = (x * y) * ((x ** n) * (y ** n)) by induction hypothesis - = x * (y * ((x ** n) * (y ** n))) by monoid_assoc - = x * ((y * (x ** n)) * (y ** n)) by monoid_assoc - = x * (((x ** n) * y) * (y ** n)) by monoid_comm_exp - = x * ((x ** n) * (y * (y ** n))) by monoid_assoc - = (x * (x ** n)) * (y * (y ** n)) by monoid_assoc -*) -val monoid_comm_op_exp = store_thm( - "monoid_comm_op_exp", - ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> !n. (x * y) ** n = (x ** n) * (y ** n)``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - `(x * y) ** SUC n = x * ((y * (x ** n)) * (y ** n))` by rw[monoid_assoc] >> - `_ = x * (((x ** n) * y) * (y ** n))` by metis_tac[monoid_comm_exp] >> - rw[monoid_assoc]); - -(* do not export commutativity check *) -(* val _ = export_rewrites ["monoid_comm_op_exp"]; *) - -(* Theorem: x IN G /\ y IN G /\ x * y = y * x ==> (x ** n) * (y ** m) = (y ** m) * (x ** n) *) -(* Proof: - By inducton on m. - Base case: x ** n * y ** 0 = y ** 0 * x ** n - LHS = x ** n * y ** 0 - = x ** n * #e by monoid_exp_0 - = x ** n by monoid_rid - = #e * x ** n by monoid_lid - = y ** 0 * x ** n by monoid_exp_0 - = RHS - Step case: x ** n * y ** m = y ** m * x ** n ==> x ** n * y ** SUC m = y ** SUC m * x ** n - LHS = x ** n * y ** SUC m - = x ** n * (y * y ** m) by monoid_exp_SUC - = (x ** n * y) * y ** m by monoid_assoc - = (y * x ** n) * y ** m by monoid_comm_exp (with single y) - = y * (x ** n * y ** m) by monoid_assoc - = y * (y ** m * x ** n) by induction hypothesis - = (y * y ** m) * x ** n by monoid_assoc - = y ** SUC m * x ** n by monoid_exp_SUC - = RHS -*) -val monoid_comm_exp_exp = store_thm( - "monoid_comm_exp_exp", - ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> - !n m. x ** n * y ** m = y ** m * x ** n``, - rpt strip_tac >> - Induct_on `m` >- - rw[] >> - `x ** n * y ** SUC m = x ** n * (y * y ** m)` by rw[] >> - `_ = (x ** n * y) * y ** m` by rw[monoid_assoc] >> - `_ = (y * x ** n) * y ** m` by metis_tac[monoid_comm_exp] >> - `_ = y * (x ** n * y ** m)` by rw[monoid_assoc] >> - `_ = y * (y ** m * x ** n)` by metis_tac[] >> - rw[monoid_assoc]); - -(* Theorem: x ** (n + k) = (x ** n) * (x ** k) *) -(* Proof: - By induction on n. - Base case: x ** (0 + k) = x ** 0 * x ** k - x ** (0 + k) - = x ** k by arithmetic - = #e * (x ** k) by monoid_lid - = (x ** 0) * (x ** k) by monoid_exp_0 - Step case: x ** (n + k) = x ** n * x ** k ==> x ** (SUC n + k) = x ** SUC n * x ** k - x ** (SUC n + k) - = x ** (SUC (n + k)) by arithmetic - = x * (x ** (n + k)) by monoid_exp_SUC - = x * ((x ** n) * (x ** k)) by induction hypothesis - = (x * (x ** n)) * (x ** k) by monoid_assoc - = (x ** SUC n) * (x ** k) by monoid_exp_def -*) -val monoid_exp_add = store_thm( - "monoid_exp_add", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n k. x ** (n + k) = (x ** n) * (x ** k)``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - rw_tac std_ss[monoid_exp_SUC, monoid_assoc, monoid_exp_element, DECIDE ``SUC n + k = SUC (n+k)``]); - -(* export simple result *) -val _ = export_rewrites ["monoid_exp_add"]; - -(* Theorem: x ** (n * k) = (x ** n) ** k *) -(* Proof: - By induction on n. - Base case: x ** (0 * k) = (x ** 0) ** k - x ** (0 * k) - = x ** 0 by arithmetic - = #e by monoid_exp_0 - = (#e) ** n by monoid_id_exp - = (x ** 0) ** n by monoid_exp_0 - Step case: x ** (n * k) = (x ** n) ** k ==> x ** (SUC n * k) = (x ** SUC n) ** k - x ** (SUC n * k) - = x ** (n * k + k) by arithmetic - = (x ** (n * k)) * (x ** k) by monoid_exp_add - = ((x ** n) ** k) * (x ** k) by induction hypothesis - = ((x ** n) * x) ** k by monoid_comm_op_exp and monoid_exp_comm - = (x * (x ** n)) ** k by monoid_exp_comm - = (x ** SUC n) ** k by monoid_exp_def -*) -val monoid_exp_mult = store_thm( - "monoid_exp_mult", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n k. x ** (n * k) = (x ** n) ** k``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - `SUC n * k = n * k + k` by metis_tac[MULT] >> - `x ** (SUC n * k) = ((x ** n) * x) ** k` by rw_tac std_ss[monoid_comm_op_exp, monoid_exp_comm, monoid_exp_element, monoid_exp_add] >> - rw[monoid_exp_comm]); - -(* export simple result *) -val _ = export_rewrites ["monoid_exp_mult"]; - -(* Theorem: x IN G ==> (x ** m) ** n = (x ** n) ** m *) -(* Proof: - (x ** m) ** n - = x ** (m * n) by monoid_exp_mult - = x ** (n * m) by MULT_COMM - = (x ** n) ** m by monoid_exp_mult -*) -val monoid_exp_mult_comm = store_thm( - "monoid_exp_mult_comm", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !m n. (x ** m) ** n = (x ** n) ** m``, - metis_tac[monoid_exp_mult, MULT_COMM]); - - -(* ------------------------------------------------------------------------- *) -(* Finite Monoid. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: For FINITE Monoid g and x IN G, x ** k cannot be all distinct. *) -(* Proof: - By contradiction. Assume !k h. x ** k = x ** h ==> k = h, then - Since G is FINITE, let c = CARD G. - The map (count (SUC c)) -> G such that n -> x ** n is: - (1) a map since each x ** n IN G - (2) injective since all x ** n are distinct - But c < SUC c = CARD (count (SUC c)), and this contradicts the Pigeon-hole Principle. -*) -val finite_monoid_exp_not_distinct = store_thm( - "finite_monoid_exp_not_distinct", - ``!g:'a monoid. FiniteMonoid g ==> !x. x IN G ==> ?h k. (x ** h = x ** k) /\ (h <> k)``, - rw[FiniteMonoid_def] >> - spose_not_then strip_assume_tac >> - qabbrev_tac `c = CARD G` >> - `INJ (\n. x ** n) (count (SUC c)) G` by rw[INJ_DEF] >> - `c < SUC c` by decide_tac >> - metis_tac[CARD_COUNT, PHP]); -(* -This theorem implies that, if x ** k are all distinct for a Monoid g, -then its carrier G must be INFINITE. -Otherwise, this is not immediately useful for a Monoid, as the op has no inverse. -However, it is useful for a Group, where the op has inverse, -hence reduce this to x ** (h-k) = #e, if h > k. -Also, it is useful for an Integral Domain, where the prod.op still has no inverse, -but being a Ring, it has subtraction and distribution, giving x ** k * (x ** (h-k) - #1) = #0 -from which the no-zero-divisor property of Integral Domain gives x ** (h-k) = #1. -*) - -(* ------------------------------------------------------------------------- *) -(* Abelian Monoid ITSET Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Define ITSET for Monoid -- fold of g.op, especially for Abelian Monoid (by lifting) *) -val _ = overload_on("GITSET", ``\(g:'a monoid) s b. ITSET g.op s b``); - -(* -> ITSET_def |> ISPEC ``s:'b -> bool`` |> ISPEC ``(g:'a monoid).op`` |> ISPEC ``b:'a``; -val it = |- GITSET g s b = if FINITE s then if s = {} then b else GITSET g (REST s) (CHOICE s * b) - else ARB: thm -*) - -fun gINST th = th |> SPEC_ALL |> INST_TYPE [beta |-> alpha] - |> Q.INST [`f` |-> `g.op`] |> GEN_ALL; -(* val gINST = fn: thm -> thm *) - -val GITSET_THM = save_thm("GITSET_THM", gINST ITSET_THM); -(* > val GITSET_THM = - |- !s g b. FINITE s ==> (GITSET g s b = if s = {} then b else GITSET g (REST s) (CHOICE s * b)) : thm -*) - -(* Theorem: GITSET {} b = b *) -val GITSET_EMPTY = save_thm("GITSET_EMPTY", gINST ITSET_EMPTY); -(* > val GITSET_EMPTY = |- !g b. GITSET g {} b = b : thm *) - -(* Theorem: GITSET g (x INSERT s) b = GITSET g (REST (x INSERT s)) ((CHOICE (x INSERT s)) * b) *) -(* Proof: - By GITSET_THM, since x INSERT s is non-empty. -*) -val GITSET_INSERT = save_thm( - "GITSET_INSERT", - gINST (ITSET_INSERT |> SPEC_ALL |> UNDISCH) |> DISCH_ALL |> GEN_ALL); -(* > val GITSET_INSERT = - |- !s. FINITE s ==> !x g b. (GITSET g (x INSERT s) b = GITSET g (REST (x INSERT s)) (CHOICE (x INSERT s) * b)) : thm -*) - -(* Theorem: [simplified GITSET_INSERT] - FINITE s /\ s <> {} ==> GITSET g s b = GITSET g (REST s) ((CHOICE s) * b) *) -(* Proof: - Replace (x INSERT s) in GITSET_INSERT by s, - GITSET g s b = GITSET g (REST s) ((CHOICE s) * b) - Since CHOICE s IN s by CHOICE_DEF - so (CHOICE s) INSERT s = s by ABSORPTION - and the result follows. -*) -val GITSET_PROPERTY = store_thm( - "GITSET_PROPERTY", - ``!g s. FINITE s /\ s <> {} ==> !b. GITSET g s b = GITSET g (REST s) ((CHOICE s) * b)``, - metis_tac[CHOICE_DEF, ABSORPTION, GITSET_INSERT]); - -(* Theorem: AbelianMonoid g ==> closure_comm_assoc_fun g.op G *) -(* Proof: - Note Monoid g /\ !x y::(G). x * y = y * x by AbelianMonoid_def - and !x y z::(G). x * y * z = y * x * z by monoid_assoc, above gives commutativity - Thus closure_comm_assoc_fun g.op G by closure_comm_assoc_fun_def -*) -val abelian_monoid_op_closure_comm_assoc_fun = store_thm( - "abelian_monoid_op_closure_comm_assoc_fun", - ``!g:'a monoid. AbelianMonoid g ==> closure_comm_assoc_fun g.op G``, - rw[AbelianMonoid_def, closure_comm_assoc_fun_def] >> - metis_tac[monoid_assoc]); - -(* Theorem: AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::(G). GITSET g (x INSERT s) b = GITSET g (s DELETE x) (x * b) *) -(* Proof: - Note closure_comm_assoc_fun g.op G by abelian_monoid_op_closure_comm_assoc_fun - The result follows by SUBSET_COMMUTING_ITSET_INSERT -*) -val COMMUTING_GITSET_INSERT = store_thm( - "COMMUTING_GITSET_INSERT", - ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::(G). GITSET g (x INSERT s) b = GITSET g (s DELETE x) (x * b)``, - metis_tac[abelian_monoid_op_closure_comm_assoc_fun, SUBSET_COMMUTING_ITSET_INSERT]); - -(* Theorem: AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::(G). GITSET g s (x * b) = x * (GITSET g s b) *) -(* Proof: - Note closure_comm_assoc_fun g.op G by abelian_monoid_op_closure_comm_assoc_fun - The result follows by SUBSET_COMMUTING_ITSET_REDUCTION -*) -val COMMUTING_GITSET_REDUCTION = store_thm( - "COMMUTING_GITSET_REDUCTION", - ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::(G). GITSET g s (x * b) = x * (GITSET g s b)``, - metis_tac[abelian_monoid_op_closure_comm_assoc_fun, SUBSET_COMMUTING_ITSET_REDUCTION]); - -(* Theorem: AbelianMonoid g ==> GITSET g (x INSERT s) b = x * (GITSET g (s DELETE x) b) *) -(* Proof: - Note closure_comm_assoc_fun g.op G by abelian_monoid_op_closure_comm_assoc_fun - The result follows by SUBSET_COMMUTING_ITSET_RECURSES -*) -val COMMUTING_GITSET_RECURSES = store_thm( - "COMMUTING_GITSET_RECURSES", - ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - !b x::(G). GITSET g (x INSERT s) b = x * (GITSET g (s DELETE x) b)``, - metis_tac[abelian_monoid_op_closure_comm_assoc_fun, SUBSET_COMMUTING_ITSET_RECURSES]); - -(* ------------------------------------------------------------------------- *) -(* Abelian Monoid PROD_SET *) -(* ------------------------------------------------------------------------- *) - -(* Define GPROD_SET via GITSET *) -val GPROD_SET_def = Define `GPROD_SET g s = GITSET g s #e`; - -(* Theorem: property of GPROD_SET *) -(* Proof: - This is to prove: - (1) GITSET g {} #e = #e - True by GITSET_EMPTY, and monoid_id_element. - (2) GITSET g (x INSERT s) #e = x * GITSET g (s DELETE x) #e - True by COMMUTING_GITSET_RECURSES, and monoid_id_element. -*) -val GPROD_SET_THM = store_thm( - "GPROD_SET_THM", - ``!g s. (GPROD_SET g {} = #e) /\ - (AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> - (!x::(G). GPROD_SET g (x INSERT s) = x * GPROD_SET g (s DELETE x)))``, - rw[GPROD_SET_def, RES_FORALL_THM, GITSET_EMPTY] >> - `Monoid g` by metis_tac[AbelianMonoid_def] >> - metis_tac[COMMUTING_GITSET_RECURSES, monoid_id_element]); - -(* Theorem: GPROD_SET g {} = #e *) -(* Proof: - GPROD_SET g {} - = GITSET g {} #e by GPROD_SET_def - = #e by GITSET_EMPTY - or directly by GPROD_SET_THM -*) -val GPROD_SET_EMPTY = store_thm( - "GPROD_SET_EMPTY", - ``!g s. GPROD_SET g {} = #e``, - rw[GPROD_SET_def, GITSET_EMPTY]); - -(* Theorem: Monoid g ==> !x. x IN G ==> (GPROD_SET g {x} = x) *) -(* Proof: - GPROD_SET g {x} - = GITSET g {x} #e by GPROD_SET_def - = x * #e by ITSET_SING - = x by monoid_rid -*) -val GPROD_SET_SING = store_thm( - "GPROD_SET_SING", - ``!g:'a monoid. Monoid g ==> !x. x IN G ==> (GPROD_SET g {x} = x)``, - rw[GPROD_SET_def, ITSET_SING]); - -(* -> ITSET_SING |> SPEC_ALL |> INST_TYPE[beta |-> alpha] |> Q.INST[`f` |-> `g.op`] |> GEN_ALL; -val it = |- !x g b. GITSET g {x} b = x * b: thm -> ITSET_SING |> SPEC_ALL |> INST_TYPE[beta |-> alpha] |> Q.INST[`f` |-> `g.op`] |> Q.INST[`b` |-> `#e`] |> REWRITE_RULE[GSYM GPROD_SET_def]; -val it = |- GPROD_SET g {x} = x * #e: thm -*) - -(* Theorem: GPROD_SET g s IN G *) -(* Proof: - By complete induction on CARD s. - Case s = {}, - Then GPROD_SET g {} = #e by GPROD_SET_EMPTY - and #e IN G by monoid_id_element - Case s <> {}, - Let x = CHOICE s, t = REST s, s = x INSERT t, x NOTIN t. - GPROD_SET g s - = GPROD_SET g (x INSERT t) by s = x INSERT t - = x * GPROD_SET g (t DELETE x) by GPROD_SET_THM - = x * GPROD_SET g t by DELETE_NON_ELEMENT, x NOTIN t - Hence GPROD_SET g s IN G by induction, and monoid_op_element. -*) -val GPROD_SET_PROPERTY = store_thm( - "GPROD_SET_PROPERTY", - ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> GPROD_SET g s IN G``, - completeInduct_on `CARD s` >> - pop_assum (assume_tac o SIMP_RULE bool_ss[GSYM RIGHT_FORALL_IMP_THM, AND_IMP_INTRO]) >> - rpt strip_tac >> - `Monoid g` by metis_tac[AbelianMonoid_def] >> - Cases_on `s = {}` >- - rw[GPROD_SET_EMPTY] >> - `?x t. (x = CHOICE s) /\ (t = REST s) /\ (s = x INSERT t)` by rw[CHOICE_INSERT_REST] >> - `x IN G` by metis_tac[CHOICE_DEF, SUBSET_DEF] >> - `t SUBSET G /\ FINITE t` by metis_tac[REST_SUBSET, SUBSET_TRANS, SUBSET_FINITE] >> - `x NOTIN t` by metis_tac[CHOICE_NOT_IN_REST] >> - `CARD t < CARD s` by rw[] >> - metis_tac[GPROD_SET_THM, DELETE_NON_ELEMENT, monoid_op_element]); - -(* ---------------------------------------------------------------------- - monoid extension - - lifting a monoid so that its carrier is the whole of the type but the - op is the same on the old carrier set. - ---------------------------------------------------------------------- *) - -Definition extend_def: - extend m = <| carrier := UNIV; id := m.id; - op := λx y. if x ∈ m.carrier then - if y ∈ m.carrier then m.op x y else y - else x |> -End - -Theorem extend_is_monoid[simp]: - ∀m. Monoid m ⇒ Monoid (extend m) -Proof - simp[extend_def, EQ_IMP_THM, Monoid_def] >> rw[] >> rw[] >> - gvs[] -QED - -Theorem extend_carrier[simp]: - (extend m).carrier = UNIV -Proof - simp[extend_def] -QED - -Theorem extend_id[simp]: - (extend m).id = m.id -Proof - simp[extend_def] -QED - -Theorem extend_op: - x ∈ m.carrier ∧ y ∈ m.carrier ⇒ (extend m).op x y = m.op x y -Proof - simp[extend_def] -QED - - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/monoid/submonoidScript.sml b/examples/algebra/monoid/submonoidScript.sml deleted file mode 100644 index d677afcc88..0000000000 --- a/examples/algebra/monoid/submonoidScript.sml +++ /dev/null @@ -1,673 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Submonoid *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "submonoid"; - -(* ------------------------------------------------------------------------- *) - - - -(* val _ = load "jcLib"; *) -open jcLib; - -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) -(* val _ = load "monoidMapTheory"; *) -open monoidTheory monoidMapTheory; - -open pred_setTheory; -open helperSetTheory; - - -(* ------------------------------------------------------------------------- *) -(* Submonoid Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading (# are temporary): -# K = k.carrier -# x o y = h.op x y -# #i = h.id - h << g = Submonoid h g - h mINTER k = monoid_intersect h k - smbINTER g = submonoid_big_intersect g -*) -(* Definitions and Theorems (# are exported): - - Helper Theorems: - - Submonoid of a Monoid: - Submonoid_def |- !h g. h << g <=> - Monoid h /\ Monoid g /\ H SUBSET G /\ ($o = $* ) /\ (#i = #e) - submonoid_property |- !g h. h << g ==> - Monoid h /\ Monoid g /\ H SUBSET G /\ - (!x y. x IN H /\ y IN H ==> (x o y = x * y)) /\ (#i = #e) - submonoid_carrier_subset |- !g h. h << g ==> H SUBSET G -# submonoid_element |- !g h. h << g ==> !x. x IN H ==> x IN G -# submonoid_id |- !g h. h << g ==> (#i = #e) - submonoid_exp |- !g h. h << g ==> !x. x IN H ==> !n. h.exp x n = x ** n - submonoid_homomorphism |- !g h. h << g ==> Monoid h /\ Monoid g /\ submonoid h g - submonoid_order |- !g h. h << g ==> !x. x IN H ==> (order h x = ord x) - submonoid_alt |- !g. Monoid g ==> !h. h << g <=> H SUBSET G /\ - (!x y. x IN H /\ y IN H ==> h.op x y IN H) /\ - h.id IN H /\ (h.op = $* ) /\ (h.id = #e) - - Submonoid Theorems: - submonoid_reflexive |- !g. Monoid g ==> g << g - submonoid_antisymmetric |- !g h. h << g /\ g << h ==> (h = g) - submonoid_transitive |- !g h k. k << h /\ h << g ==> k << g - submonoid_monoid |- !g h. h << g ==> Monoid h - - Submonoid Intersection: - monoid_intersect_def |- !g h. g mINTER h = <|carrier := G INTER H; op := $*; id := #e|> - monoid_intersect_property |- !g h. ((g mINTER h).carrier = G INTER H) /\ - ((g mINTER h).op = $* ) /\ ((g mINTER h).id = #e) - monoid_intersect_element |- !g h x. x IN (g mINTER h).carrier ==> x IN G /\ x IN H - monoid_intersect_id |- !g h. (g mINTER h).id = #e - - submonoid_intersect_property |- !g h k. h << g /\ k << g ==> - ((h mINTER k).carrier = H INTER K) /\ - (!x y. x IN H INTER K /\ y IN H INTER K ==> - ((h mINTER k).op x y = x * y)) /\ ((h mINTER k).id = #e) - submonoid_intersect_monoid |- !g h k. h << g /\ k << g ==> Monoid (h mINTER k) - submonoid_intersect_submonoid |- !g h k. h << g /\ k << g ==> (h mINTER k) << g - - Submonoid Big Intersection: - submonoid_big_intersect_def |- !g. smbINTER g = - <|carrier := BIGINTER (IMAGE (\h. H) {h | h << g}); op := $*; id := #e|> - submonoid_big_intersect_property |- !g. - ((smbINTER g).carrier = BIGINTER (IMAGE (\h. H) {h | h << g})) /\ - (!x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> ((smbINTER g).op x y = x * y)) /\ - ((smbINTER g).id = #e) - submonoid_big_intersect_element |- !g x. x IN (smbINTER g).carrier <=> !h. h << g ==> x IN H - submonoid_big_intersect_op_element |- !g x y. x IN (smbINTER g).carrier /\ - y IN (smbINTER g).carrier ==> - (smbINTER g).op x y IN (smbINTER g).carrier - submonoid_big_intersect_has_id |- !g. (smbINTER g).id IN (smbINTER g).carrier - submonoid_big_intersect_subset |- !g. Monoid g ==> (smbINTER g).carrier SUBSET G - submonoid_big_intersect_monoid |- !g. Monoid g ==> Monoid (smbINTER g) - submonoid_big_intersect_submonoid |- !g. Monoid g ==> smbINTER g << g -*) - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Submonoid of a Monoid *) -(* ------------------------------------------------------------------------- *) - -(* Use K to denote k.carrier *) -val _ = temp_overload_on ("K", ``(k:'a monoid).carrier``); - -(* Use o to denote h.op *) -val _ = temp_overload_on ("o", ``(h:'a monoid).op``); - -(* Use #i to denote h.id *) -val _ = temp_overload_on ("#i", ``(h:'a monoid).id``); - -(* A Submonoid is a subset of a monoid that's a monoid itself, keeping op, id. *) -val Submonoid_def = Define` - Submonoid (h:'a monoid) (g:'a monoid) <=> - Monoid h /\ Monoid g /\ - H SUBSET G /\ ($o = $* ) /\ (#i = #e) -`; - -(* Overload Submonoid *) -val _ = overload_on ("<<", ``Submonoid``); -val _ = set_fixity "<<" (Infix(NONASSOC, 450)); (* same as relation *) - -(* Note: The requirement $o = $* is stronger than the following: -val _ = overload_on ("<<", ``\(h g):'a monoid. Monoid g /\ Monoid h /\ submonoid h g``); -Since submonoid h g is based on MonoidHomo I g h, which only gives -!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) - -This is not enough to satisfy monoid_component_equality, -hence cannot prove: h << g /\ g << h ==> h = g -*) - -(* -val submonoid_property = save_thm( - "submonoid_property", - Submonoid_def - |> SPEC_ALL - |> REWRITE_RULE [ASSUME ``h:'a monoid << g``] - |> CONJUNCTS - |> (fn thl => List.take(thl, 2) @ List.drop(thl, 3)) - |> LIST_CONJ - |> DISCH_ALL - |> Q.GEN `h` |> Q.GEN `g`); -val submonoid_property = |- !g h. h << g ==> Monoid h /\ Monoid g /\ ($o = $* ) /\ (#i = #e) -*) - -(* Theorem: properties of submonoid *) -(* Proof: Assume h << g, then derive all consequences of definition. *) -val submonoid_property = store_thm( - "submonoid_property", - ``!(g:'a monoid) h. h << g ==> Monoid h /\ Monoid g /\ H SUBSET G /\ - (!x y. x IN H /\ y IN H ==> (x o y = x * y)) /\ (#i = #e)``, - rw_tac std_ss[Submonoid_def]); - -(* Theorem: h << g ==> H SUBSET G *) -(* Proof: by Submonoid_def *) -val submonoid_carrier_subset = store_thm( - "submonoid_carrier_subset", - ``!(g:'a monoid) h. Submonoid h g ==> H SUBSET G``, - rw[Submonoid_def]); - -(* Theorem: elements in submonoid are also in monoid. *) -(* Proof: Since h << g ==> H SUBSET G by Submonoid_def. *) -val submonoid_element = store_thm( - "submonoid_element", - ``!(g:'a monoid) h. h << g ==> !x. x IN H ==> x IN G``, - rw_tac std_ss[Submonoid_def, SUBSET_DEF]); - -(* export simple result *) -val _ = export_rewrites ["submonoid_element"]; - -(* Theorem: h << g ==> (h.op = $* ) *) -(* Proof: by Subgroup_def *) -val submonoid_op = store_thm( - "submonoid_op", - ``!(g:'a monoid) h. h << g ==> (h.op = g.op)``, - rw[Submonoid_def]); - -(* Theorem: h << g ==> #i = #e *) -(* Proof: by Submonoid_def. *) -val submonoid_id = store_thm( - "submonoid_id", - ``!(g:'a monoid) h. h << g ==> (#i = #e)``, - rw_tac std_ss[Submonoid_def]); - -(* export simple results *) -val _ = export_rewrites["submonoid_id"]; - -(* Theorem: h << g ==> !x. x IN H ==> !n. h.exp x n = x ** n *) -(* Proof: by induction on n. - Base: h.exp x 0 = x ** 0 - LHS = h.exp x 0 - = h.id by monoid_exp_0 - = #e by submonoid_id - = x ** 0 by monoid_exp_0 - = RHS - Step: h.exp x n = x ** n ==> h.exp x (SUC n) = x ** SUC n - LHS = h.exp x (SUC n) - = h.op x (h.exp x n) by monoid_exp_SUC - = x * (h.exp x n) by submonoid_property - = x * x ** n by induction hypothesis - = x ** SUC n by monoid_exp_SUC - = RHS -*) -val submonoid_exp = store_thm( - "submonoid_exp", - ``!(g:'a monoid) h. h << g ==> !x. x IN H ==> !n. h.exp x n = x ** n``, - rpt strip_tac >> - Induct_on `n` >- - rw[] >> - `h.exp x (SUC n) = h.op x (h.exp x n)` by rw_tac std_ss[monoid_exp_SUC] >> - `_ = x * (h.exp x n)` by metis_tac[submonoid_property, monoid_exp_element] >> - `_ = x * (x ** n)` by rw[] >> - `_ = x ** (SUC n)` by rw_tac std_ss[monoid_exp_SUC] >> - rw[]); - -(* Theorem: A submonoid h of g implies identity is a homomorphism from h to g. - or h << g ==> Monoid h /\ Monoid g /\ submonoid h g *) -(* Proof: - h << g ==> Monoid h /\ Monoid g by Submonoid_def - together with - H SUBSET G /\ ($o = $* ) /\ (#i = #e) by Submonoid_def - ==> !x. x IN H ==> x IN G /\ - !x y. x IN H /\ y IN H ==> (x o y = x * y) /\ - #i = #e by SUBSET_DEF - ==> MonoidHomo I h g by MonoidHomo_def, f = I. - ==> submonoid h g by submonoid_def -*) -val submonoid_homomorphism = store_thm( - "submonoid_homomorphism", - ``!(g:'a monoid) h. h << g ==> Monoid h /\ Monoid g /\ submonoid h g``, - rw_tac std_ss[Submonoid_def, submonoid_def, MonoidHomo_def, SUBSET_DEF]); - -(* original: -g `!(g:'a monoid) h. h << g = Monoid h /\ Monoid g /\ submonoid h g`; -e (rw_tac std_ss[Submonoid_def, submonoid_def, MonoidHomo_def, SUBSET_DEF, EQ_IMP_THM]); - -The only-if part (<==) cannot be proved: -Note Submonoid_def uses h.op = g.op, -but submonoid_def uses homomorphism I, and so cannot show this for any x y. -*) - -(* Theorem: h << g ==> !x. x IN H ==> (order h x = ord x) *) -(* Proof: - Note Monoid g /\ Monoid h /\ submonoid h g by submonoid_homomorphism, h << g - Thus !x. x IN H ==> (order h x = ord x) by submonoid_order_eqn -*) -val submonoid_order = store_thm( - "submonoid_order", - ``!(g:'a monoid) h. h << g ==> !x. x IN H ==> (order h x = ord x)``, - metis_tac[submonoid_homomorphism, submonoid_order_eqn]); - -(* Theorem: Monoid g ==> !h. Submonoid h g <=> - H SUBSET G /\ (!x y. x IN H /\ y IN H ==> h.op x y IN H) /\ (h.id IN H) /\ (h.op = $* ) /\ (h.id = #e) *) -(* Proof: - By Submonoid_def, EQ_IMP_THM, this is to show: - (1) x IN H /\ y IN H ==> x * y IN H, true by monoid_op_element - (2) #e IN H, true by monoid_id_element - (3) Monoid h - By Monoid_def, this is to show: - (1) x IN H /\ y IN H /\ z IN H - ==> x * y * z = x * (y * z), true by monoid_assoc, SUBSET_DEF - (2) x IN H ==> #e * x = x, true by monoid_lid, SUBSET_DEF - (3) x IN H ==> x * #e = x, true by monoid_rid, SUBSET_DEF -*) -val submonoid_alt = store_thm( - "submonoid_alt", - ``!g:'a monoid. Monoid g ==> !h. Submonoid h g <=> - H SUBSET G /\ (* subset *) - (!x y. x IN H /\ y IN H ==> h.op x y IN H) /\ (* closure *) - (h.id IN H) /\ (* has identity *) - (h.op = g.op ) /\ (h.id = #e)``, - rw_tac std_ss[Submonoid_def, EQ_IMP_THM] >- - metis_tac[monoid_op_element] >- - metis_tac[monoid_id_element] >> - rw_tac std_ss[Monoid_def] >- - fs[monoid_assoc, SUBSET_DEF] >- - fs[monoid_lid, SUBSET_DEF] >> - fs[monoid_rid, SUBSET_DEF]); - -(* ------------------------------------------------------------------------- *) -(* Submonoid Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: Monoid g ==> g << g *) -(* Proof: by Submonoid_def, SUBSET_REFL *) -val submonoid_reflexive = store_thm( - "submonoid_reflexive", - ``!g:'a monoid. Monoid g ==> g << g``, - rw_tac std_ss[Submonoid_def, SUBSET_REFL]); - -(* Theorem: h << g /\ g << h ==> (h = g) *) -(* Proof: - Since h << g ==> Monoid h /\ Monoid g /\ H SUBSET G /\ ($o = $* ) /\ (#i = #e) by Submonoid_def - and g << h ==> Monoid g /\ Monoid h /\ G SUBSET H /\ ($* = $o) /\ (#e = #i) by Submonoid_def - Now, H SUBSET G /\ G SUBSET H ==> H = G by SUBSET_ANTISYM - Hence h = g by monoid_component_equality -*) -val submonoid_antisymmetric = store_thm( - "submonoid_antisymmetric", - ``!g h:'a monoid. h << g /\ g << h ==> (h = g)``, - rw_tac std_ss[Submonoid_def] >> - full_simp_tac bool_ss[monoid_component_equality, SUBSET_ANTISYM]); - -(* Theorem: k << h /\ h << g ==> k << g *) -(* Proof: by Submonoid_def and SUBSET_TRANS *) -val submonoid_transitive = store_thm( - "submonoid_transitive", - ``!g h k:'a monoid. k << h /\ h << g ==> k << g``, - rw_tac std_ss[Submonoid_def] >> - metis_tac[SUBSET_TRANS]); - -(* Theorem: h << g ==> Monoid h *) -(* Proof: by Submonoid_def. *) -val submonoid_monoid = store_thm( - "submonoid_monoid", - ``!g h:'a monoid. h << g ==> Monoid h``, - rw[Submonoid_def]); - -(* ------------------------------------------------------------------------- *) -(* Submonoid Intersection *) -(* ------------------------------------------------------------------------- *) - -(* Define intersection of monoids *) -val monoid_intersect_def = Define` - monoid_intersect (g:'a monoid) (h:'a monoid) = - <| carrier := G INTER H; - op := $*; (* g.op *) - id := #e (* g.id *) - |> -`; - -val _ = overload_on ("mINTER", ``monoid_intersect``); -val _ = set_fixity "mINTER" (Infix(NONASSOC, 450)); (* same as relation *) -(* -> monoid_intersect_def; -val it = |- !g h. g mINTER h = <|carrier := G INTER H; op := $*; id := #e|>: thm -*) - -(* Theorem: ((g mINTER h).carrier = G INTER H) /\ - ((g mINTER h).op = $* ) /\ ((g mINTER h).id = #e) *) -(* Proof: by monoid_intersect_def *) -val monoid_intersect_property = store_thm( - "monoid_intersect_property", - ``!g h:'a monoid. ((g mINTER h).carrier = G INTER H) /\ - ((g mINTER h).op = $* ) /\ ((g mINTER h).id = #e)``, - rw[monoid_intersect_def]); - -(* Theorem: !x. x IN (g mINTER h).carrier ==> x IN G /\ x IN H *) -(* Proof: - x IN (g mINTER h).carrier - ==> x IN G INTER H by monoid_intersect_def - ==> x IN G and x IN H by IN_INTER -*) -val monoid_intersect_element = store_thm( - "monoid_intersect_element", - ``!g h:'a monoid. !x. x IN (g mINTER h).carrier ==> x IN G /\ x IN H``, - rw[monoid_intersect_def, IN_INTER]); - -(* Theorem: (g mINTER h).id = #e *) -(* Proof: by monoid_intersect_def. *) -val monoid_intersect_id = store_thm( - "monoid_intersect_id", - ``!g h:'a monoid. (g mINTER h).id = #e``, - rw[monoid_intersect_def]); - -(* Theorem: h << g /\ k << g ==> - ((h mINTER k).carrier = H INTER K) /\ - (!x y. x IN H INTER K /\ y IN H INTER K ==> ((h mINTER k).op x y = x * y)) /\ - ((h mINTER k).id = #e) *) -(* Proof: - (h mINTER k).carrier = H INTER K by monoid_intersect_def - Hence x IN (h mINTER k).carrier ==> x IN H /\ x IN K by IN_INTER - and y IN (h mINTER k).carrier ==> y IN H /\ y IN K by IN_INTER - so (h mINTER k).op x y = x o y by monoid_intersect_def - = x * y by submonoid_property - and (h mINTER k).id = #i by monoid_intersect_def - = #e by submonoid_property -*) -val submonoid_intersect_property = store_thm( - "submonoid_intersect_property", - ``!g h k:'a monoid. h << g /\ k << g ==> - ((h mINTER k).carrier = H INTER K) /\ - (!x y. x IN H INTER K /\ y IN H INTER K ==> ((h mINTER k).op x y = x * y)) /\ - ((h mINTER k).id = #e)``, - rw[monoid_intersect_def, submonoid_property]); - -(* Theorem: h << g /\ k << g ==> Monoid (h mINTER k) *) -(* Proof: - By definitions, this is to show: - (1) x IN H INTER K /\ y IN H INTER K ==> x o y IN H INTER K - x IN H INTER K ==> x IN H /\ x IN K by IN_INTER - y IN H INTER K ==> y IN H /\ y IN K by IN_INTER - x IN H /\ y IN H ==> x o y IN H by monoid_op_element - x IN K /\ y IN K ==> k.op x y IN K by monoid_op_element - x o y = x * y by submonoid_property - k.op x y = x * y by submonoid_property - Hence x o y IN H INTER K by IN_INTER - (2) x IN H INTER K /\ y IN H INTER K /\ z IN H INTER K ==> (x o y) o z = x o y o z - x IN H INTER K ==> x IN H by IN_INTER - y IN H INTER K ==> y IN H by IN_INTER - z IN H INTER K ==> z IN H by IN_INTER - x IN H /\ y IN H ==> x o y IN H by monoid_op_element - y IN H /\ z IN H ==> y o z IN H by monoid_op_element - x, y, z IN H ==> x, y, z IN G by submonoid_element - LHS = (x o y) o z - = (x o y) * z by submonoid_property - = (x * y) * z by submonoid_property - = x * (y * z) by monoid_assoc - = x * (y o z) by submonoid_property - = x o (y o z) = RHS by submonoid_property - (3) #i IN H INTER K - #i IN H and #i = #e by monoid_id_element, submonoid_id - k.id IN K and k.id = #e by monoid_id_element, submonoid_id - Hence #e = #i IN H INTER K by IN_INTER - (4) x IN H INTER K ==> #i o x = x - x IN H INTER K ==> x IN H by IN_INTER - ==> x IN G by submonoid_element - #i IN H and #i = #e by monoid_id_element, submonoid_id - #i o x - = #i * x by submonoid_property - = #e * x by submonoid_id - = x by monoid_id - (5) x IN H INTER K ==> x o #i = x - x IN H INTER K ==> x IN H by IN_INTER - ==> x IN G by submonoid_element - #i IN H and #i = #e by monoid_id_element, submonoid_id - x o #i - = x * #i by submonoid_property - = x * #e by submonoid_id - = x by monoid_id -*) -val submonoid_intersect_monoid = store_thm( - "submonoid_intersect_monoid", - ``!g h k:'a monoid. h << g /\ k << g ==> Monoid (h mINTER k)``, - rpt strip_tac >> - `Monoid h /\ Monoid k /\ Monoid g` by metis_tac[submonoid_property] >> - rw_tac std_ss[Monoid_def, monoid_intersect_def] >| [ - `x IN H /\ x IN K /\ y IN H /\ y IN K` by metis_tac[IN_INTER] >> - `x o y IN H /\ (x o y = x * y)` by metis_tac[submonoid_property, monoid_op_element] >> - `k.op x y IN K /\ (k.op x y = x * y)` by metis_tac[submonoid_property, monoid_op_element] >> - metis_tac[IN_INTER], - `x IN H /\ y IN H /\ z IN H` by metis_tac[IN_INTER] >> - `x IN G /\ y IN G /\ z IN G` by metis_tac[submonoid_element] >> - `x o y IN H /\ y o z IN H` by metis_tac[monoid_op_element] >> - `(x o y) o z = (x * y) * z` by metis_tac[submonoid_property] >> - `x o (y o z) = x * (y * z)` by metis_tac[submonoid_property] >> - rw[monoid_assoc], - metis_tac[IN_INTER, submonoid_id, monoid_id_element], - metis_tac[submonoid_property, monoid_id, submonoid_element, IN_INTER, monoid_id_element], - metis_tac[submonoid_property, monoid_id, submonoid_element, IN_INTER, monoid_id_element] - ]); - -(* Theorem: h << g /\ k << g ==> (h mINTER k) << g *) -(* Proof: - By Submonoid_def, this is to show: - (1) Monoid (h mINTER k), true by submonoid_intersect_monoid - (2) (h mINTER k).carrier SUBSET G - Since (h mINTER k).carrier = H INTER K by submonoid_intersect_property - and (H INTER K) SUBSET H by INTER_SUBSET - and h << g ==> H SUBSET G by submonoid_property - Hence (h mINTER k).carrier SUBSET G by SUBSET_TRANS - (3) (h mINTER k).op = $* - (h mINTER k).op = $o by monoid_intersect_def - = $* by Submonoid_def - (4) (h mINTER k).id = #e - (h mINTER k).id = #i by monoid_intersect_def - = #e by Submonoid_def -*) -val submonoid_intersect_submonoid = store_thm( - "submonoid_intersect_submonoid", - ``!g h k:'a monoid. h << g /\ k << g ==> (h mINTER k) << g``, - rpt strip_tac >> - `Monoid h /\ Monoid k /\ Monoid g` by metis_tac[submonoid_property] >> - rw[Submonoid_def] >| [ - metis_tac[submonoid_intersect_monoid], - `(h mINTER k).carrier = H INTER K` by metis_tac[submonoid_intersect_property] >> - `H SUBSET G` by rw[submonoid_property] >> - metis_tac[INTER_SUBSET, SUBSET_TRANS], - `(h mINTER k).op = $o` by rw[monoid_intersect_def] >> - metis_tac[Submonoid_def], - `(h mINTER k).id = #i` by rw[monoid_intersect_def] >> - metis_tac[Submonoid_def] - ]); - -(* ------------------------------------------------------------------------- *) -(* Submonoid Big Intersection *) -(* ------------------------------------------------------------------------- *) - -(* Define intersection of submonoids of a monoid *) -val submonoid_big_intersect_def = Define` - submonoid_big_intersect (g:'a monoid) = - <| carrier := BIGINTER (IMAGE (\h. H) {h | h << g}); - op := $*; (* g.op *) - id := #e (* g.id *) - |> -`; - -val _ = overload_on ("smbINTER", ``submonoid_big_intersect``); -(* -> submonoid_big_intersect_def; -val it = |- !g. smbINTER g = - <|carrier := BIGINTER (IMAGE (\h. H) {h | h << g}); op := $*; id := #e|>: thm -*) - -(* Theorem: ((smbINTER g).carrier = BIGINTER (IMAGE (\h. H) {h | h << g})) /\ - (!x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> ((smbINTER g).op x y = x * y)) /\ - ((smbINTER g).id = #e) *) -(* Proof: by submonoid_big_intersect_def. *) -val submonoid_big_intersect_property = store_thm( - "submonoid_big_intersect_property", - ``!g:'a monoid. ((smbINTER g).carrier = BIGINTER (IMAGE (\h. H) {h | h << g})) /\ - (!x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> ((smbINTER g).op x y = x * y)) /\ - ((smbINTER g).id = #e)``, - rw[submonoid_big_intersect_def]); - -(* Theorem: x IN (smbINTER g).carrier <=> (!h. h << g ==> x IN H) *) -(* Proof: - x IN (smbINTER g).carrier - <=> x IN BIGINTER (IMAGE (\h. H) {h | h << g}) by submonoid_big_intersect_def - <=> !P. P IN (IMAGE (\h. H) {h | h << g}) ==> x IN P by IN_BIGINTER - <=> !P. ?h. (P = H) /\ h IN {h | h << g}) ==> x IN P by IN_IMAGE - <=> !P. ?h. (P = H) /\ h << g) ==> x IN P by GSPECIFICATION - <=> !h. h << g ==> x IN H -*) -val submonoid_big_intersect_element = store_thm( - "submonoid_big_intersect_element", - ``!g:'a monoid. !x. x IN (smbINTER g).carrier <=> (!h. h << g ==> x IN H)``, - rw[submonoid_big_intersect_def] >> - metis_tac[]); - -(* Theorem: x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> (smbINTER g).op x y IN (smbINTER g).carrier *) -(* Proof: - Since x IN (smbINTER g).carrier, !h. h << g ==> x IN H by submonoid_big_intersect_element - also y IN (smbINTER g).carrier, !h. h << g ==> y IN H by submonoid_big_intersect_element - Now !h. h << g ==> x o y IN H by Submonoid_def, monoid_op_element - ==> x * y IN H by submonoid_property - Now, (smbINTER g).op x y = x * y by submonoid_big_intersect_property - Hence (smbINTER g).op x y IN (smbINTER g).carrier by submonoid_big_intersect_element -*) -val submonoid_big_intersect_op_element = store_thm( - "submonoid_big_intersect_op_element", - ``!g:'a monoid. !x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> - (smbINTER g).op x y IN (smbINTER g).carrier``, - rpt strip_tac >> - `!h. h << g ==> x IN H /\ y IN H` by metis_tac[submonoid_big_intersect_element] >> - `!h. h << g ==> x * y IN H` by metis_tac[Submonoid_def, monoid_op_element, submonoid_property] >> - `(smbINTER g).op x y = x * y` by rw[submonoid_big_intersect_property] >> - metis_tac[submonoid_big_intersect_element]); - -(* Theorem: (smbINTER g).id IN (smbINTER g).carrier *) -(* Proof: - !h. h << g ==> #i = #e by submonoid_id - !h. h << g ==> #i IN H by Submonoid_def, monoid_id_element - Now (smbINTER g).id = #e by submonoid_big_intersect_property - Hence !h. h << g ==> (smbINTER g).id IN H by above - or (smbINTER g).id IN (smbINTER g).carrier by submonoid_big_intersect_element -*) -val submonoid_big_intersect_has_id = store_thm( - "submonoid_big_intersect_has_id", - ``!g:'a monoid. (smbINTER g).id IN (smbINTER g).carrier``, - rpt strip_tac >> - `!h. h << g ==> (#i = #e)` by rw[submonoid_id] >> - `!h. h << g ==> #i IN H` by rw[Submonoid_def] >> - `(smbINTER g).id = #e` by metis_tac[submonoid_big_intersect_property] >> - metis_tac[submonoid_big_intersect_element]); - -(* Theorem: Monoid g ==> (smbINTER g).carrier SUBSET G *) -(* Proof: - By submonoid_big_intersect_def, this is to show: - Monoid g ==> BIGINTER (IMAGE (\h. H) {h | h << g}) SUBSET G - Let P = IMAGE (\h. H) {h | h << g}. - Since g << g by submonoid_reflexive - so G IN P by IN_IMAGE, definition of P. - Thus P <> {} by MEMBER_NOT_EMPTY. - Now h << g ==> H SUBSET G by submonoid_property - Hence P SUBSET G by BIGINTER_SUBSET -*) -val submonoid_big_intersect_subset = store_thm( - "submonoid_big_intersect_subset", - ``!g:'a monoid. Monoid g ==> (smbINTER g).carrier SUBSET G``, - rw[submonoid_big_intersect_def] >> - qabbrev_tac `P = IMAGE (\h. H) {h | h << g}` >> - (`!x. x IN P <=> ?h. (H = x) /\ h << g` by (rw[Abbr`P`] >> metis_tac[])) >> - `g << g` by rw[submonoid_reflexive] >> - `P <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - `!h:'a monoid. h << g ==> H SUBSET G` by rw[submonoid_property] >> - metis_tac[BIGINTER_SUBSET]); - -(* Theorem: Monoid g ==> Monoid (smbINTER g) *) -(* Proof: - Monoid g ==> (smbINTER g).carrier SUBSET G by submonoid_big_intersect_subset - By Monoid_def, this is to show: - (1) x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> (smbINTER g).op x y IN (smbINTER g).carrier - True by submonoid_big_intersect_op_element. - (2) (smbINTER g).op ((smbINTER g).op x y) z = (smbINTER g).op x ((smbINTER g).op y z) - Since (smbINTER g).op x y IN (smbINTER g).carrier by submonoid_big_intersect_op_element - and (smbINTER g).op y z IN (smbINTER g).carrier by submonoid_big_intersect_op_element - So this is to show: (x * y) * z = x * (y * z) by submonoid_big_intersect_property - Since x IN G, y IN G and z IN G by IN_SUBSET - This follows by monoid_assoc. - (3) (smbINTER g).id IN (smbINTER g).carrier - This is true by submonoid_big_intersect_has_id. - (4) x IN (smbINTER g).carrier ==> (smbINTER g).op (smbINTER g).id x = x - Since (smbINTER g).id IN (smbINTER g).carrier by submonoid_big_intersect_op_element - and (smbINTER g).id = #e by submonoid_big_intersect_property - also x IN G by IN_SUBSET - (smbINTER g).op (smbINTER g).id x - = #e * x by submonoid_big_intersect_property - = x by monoid_id - (5) x IN (smbINTER g).carrier ==> (smbINTER g).op x (smbINTER g).id = x - Since (smbINTER g).id IN (smbINTER g).carrier by submonoid_big_intersect_op_element - and (smbINTER g).id = #e by submonoid_big_intersect_property - also x IN G by IN_SUBSET - (smbINTER g).op x (smbINTER g).id - = x * #e by submonoid_big_intersect_property - = x by monoid_id -*) -val submonoid_big_intersect_monoid = store_thm( - "submonoid_big_intersect_monoid", - ``!g:'a monoid. Monoid g ==> Monoid (smbINTER g)``, - rpt strip_tac >> - `(smbINTER g).carrier SUBSET G` by rw[submonoid_big_intersect_subset] >> - rw_tac std_ss[Monoid_def] >| [ - metis_tac[submonoid_big_intersect_op_element], - `(smbINTER g).op x y IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_op_element] >> - `(smbINTER g).op y z IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_op_element] >> - `(x * y) * z = x * (y * z)` suffices_by rw[submonoid_big_intersect_property] >> - `x IN G /\ y IN G /\ z IN G` by metis_tac[IN_SUBSET] >> - rw[monoid_assoc], - metis_tac[submonoid_big_intersect_has_id], - `(smbINTER g).id = #e` by rw[submonoid_big_intersect_property] >> - `(smbINTER g).id IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_has_id] >> - `#e * x = x` suffices_by rw[submonoid_big_intersect_property] >> - `x IN G` by metis_tac[IN_SUBSET] >> - rw[], - `(smbINTER g).id = #e` by rw[submonoid_big_intersect_property] >> - `(smbINTER g).id IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_has_id] >> - `x * #e = x` suffices_by rw[submonoid_big_intersect_property] >> - `x IN G` by metis_tac[IN_SUBSET] >> - rw[] - ]); - -(* Theorem: Monoid g ==> (smbINTER g) << g *) -(* Proof: - By Submonoid_def, this is to show: - (1) Monoid (smbINTER g) - True by submonoid_big_intersect_monoid. - (2) (smbINTER g).carrier SUBSET G - True by submonoid_big_intersect_subset. - (3) (smbINTER g).op = $* - True by submonoid_big_intersect_def - (4) (smbINTER g).id = #e - True by submonoid_big_intersect_def -*) -val submonoid_big_intersect_submonoid = store_thm( - "submonoid_big_intersect_submonoid", - ``!g:'a monoid. Monoid g ==> (smbINTER g) << g``, - rw_tac std_ss[Submonoid_def] >| [ - rw[submonoid_big_intersect_monoid], - rw[submonoid_big_intersect_subset], - rw[submonoid_big_intersect_def], - rw[submonoid_big_intersect_def] - ]); - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/algebra/multipoly/Holmakefile b/examples/algebra/multipoly/Holmakefile index 59279b4b75..4fb3554e01 100644 --- a/examples/algebra/multipoly/Holmakefile +++ b/examples/algebra/multipoly/Holmakefile @@ -1 +1 @@ -INCLUDES = ../lib ../ring ../polynomial +INCLUDES = ../ring ../polynomial diff --git a/examples/algebra/multipoly/multipolyScript.sml b/examples/algebra/multipoly/multipolyScript.sml index bd2424789d..0f75fdb0b2 100644 --- a/examples/algebra/multipoly/multipolyScript.sml +++ b/examples/algebra/multipoly/multipolyScript.sml @@ -1,14 +1,14 @@ -open HolKernel boolLib bossLib Parse dep_rewrite - pairTheory pred_setTheory listTheory helperListTheory bagTheory ringTheory - gbagTheory polynomialTheory polyWeakTheory polyRingTheory polyEvalTheory - polyFieldTheory integralDomainTheory - monoidMapTheory groupMapTheory ringMapTheory +open HolKernel boolLib bossLib Parse; -val _ = new_theory"multipoly" +open dep_rewrite pairTheory pred_setTheory listTheory rich_listTheory bagTheory + gcdsetTheory numberTheory combinatoricsTheory; -(* stuff that should be moved *) +open ringTheory polynomialTheory polyWeakTheory polyRingTheory polyEvalTheory + polyFieldTheory integralDomainTheory groupMapTheory ringMapTheory; -open monoidTheory groupTheory helperSetTheory +open monoidTheory groupTheory; + +val _ = new_theory "multipoly"; Theorem GBAG_IMAGE_GBAG_BAG_OF_SET: AbelianMonoid g ==> @@ -570,12 +570,12 @@ Proof \\ drule BIJ_LINV_BIJ \\ qmatch_goalsub_abbrev_tac`BIJ f s` \\ strip_tac \\ `∀x. x IN s ==> f x = mpoly_of_poly r v x` - suffices_by metis_tac[helperSetTheory.BIJ_CONG] + suffices_by metis_tac[BIJ_CONG] \\ simp[Abbr`f`] \\ rpt strip_tac \\ qmatch_goalsub_abbrev_tac`LINV f t x` \\ `mpoly_of_poly r v x = mpoly_of_poly r v (f (LINV f t x))` - by metis_tac[helperSetTheory.BIJ_LINV_THM] + by metis_tac[BIJ_LINV_THM] \\ pop_assum SUBST1_TAC \\ qunabbrev_tac`f` \\ DEP_REWRITE_TAC[mpoly_of_poly_of_mpoly] @@ -3048,7 +3048,7 @@ Proof \\ `1 <= MAX_SET ls <=> MAX_SET ls <> 0` by simp[] \\ pop_assum SUBST1_TAC \\ `FINITE ls` by simp[Abbr`ls`] - \\ simp[helperSetTheory.MAX_SET_EQ_0, Abbr`ls`] + \\ simp[MAX_SET_EQ_0, Abbr`ls`] \\ simp[IMAGE_EQ_SING] \\ Cases_on`monomials r p = {}` \\ simp[] \\ rw[BAG_IN, BAG_INN] @@ -3064,7 +3064,7 @@ Proof rw[degree_of_def, monomials_mpoly_of_poly] \\ rw[GSYM IMAGE_COMPOSE, combinTheory.o_DEF] \\ rw[polynomialTheory.poly_deg_def] - \\ DEP_REWRITE_TAC[helperSetTheory.MAX_SET_TEST_IFF] + \\ DEP_REWRITE_TAC[MAX_SET_TEST_IFF] \\ simp[] \\ fs[polyRingTheory.poly_def_alt] \\ simp[Once EXTENSION] @@ -3529,7 +3529,7 @@ Proof \\ fs[monomials_def] \\ NO_TAC) \\ metis_tac[] ) \\ simp[] - \\ simp[helperSetTheory.INTER_SING] ) + \\ simp[INTER_SING] ) \\ qmatch_goalsub_abbrev_tac`CARD s` \\ `s = {}` suffices_by rw[] \\ simp[Abbr`s`, Once EXTENSION] diff --git a/examples/algebra/polynomial/Holmakefile b/examples/algebra/polynomial/Holmakefile index 771d7ff5c2..815fd241c2 100644 --- a/examples/algebra/polynomial/Holmakefile +++ b/examples/algebra/polynomial/Holmakefile @@ -1,2 +1 @@ -PRE_INCLUDES = ../ring -INCLUDES = ../lib ../monoid ../group ../field +INCLUDES = ../ring ../group ../field diff --git a/examples/algebra/polynomial/polyBinomialScript.sml b/examples/algebra/polynomial/polyBinomialScript.sml index 5753ccd359..706ff964a5 100644 --- a/examples/algebra/polynomial/polyBinomialScript.sml +++ b/examples/algebra/polynomial/polyBinomialScript.sml @@ -12,59 +12,28 @@ val _ = new_theory "polyBinomial"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* (* val _ = load "fieldTheory"; *) *) +(* open dependent theories *) +open pred_setTheory arithmeticTheory listTheory rich_listTheory numberTheory + combinatoricsTheory dividesTheory gcdTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory; (* val _ = load "fieldInstancesTheory"; *) open fieldTheory fieldInstancesTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* val _ = load "polyDivisionTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory; -(* val _ = load "polyMonicTheory"; *) open polyMonicTheory; - -(* val _ = load "polyEvalTheory"; *) open polyFieldTheory; open polyRootTheory; open polyEvalTheory; -(* open dependent theories *) -open pred_setTheory arithmeticTheory listTheory rich_listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory" -- in ringTheory *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* val _ = load "binomialTheory"; *) -open binomialTheory; - -(* val _ = load "ringBinomialTheory"; *) open ringBinomialTheory; - -(* val _ = load "ringInstancesTheory"; *) open ringInstancesTheory; - (* ------------------------------------------------------------------------- *) (* Polynomial Binomial R[x] Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyCyclicScript.sml b/examples/algebra/polynomial/polyCyclicScript.sml index 7a51625790..63bb94fe1b 100644 --- a/examples/algebra/polynomial/polyCyclicScript.sml +++ b/examples/algebra/polynomial/polyCyclicScript.sml @@ -12,17 +12,14 @@ val _ = new_theory "polyCyclic"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependet theories local *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; *) +(* Get dependet theories local *) open monoidTheory groupTheory ringTheory ringUnitTheory; (* (* val _ = load "integralDomainTheory"; *) *) @@ -30,8 +27,7 @@ open monoidTheory groupTheory ringTheory ringUnitTheory; open integralDomainTheory; open fieldTheory; -(* val _ = load "groupCyclicTheory"; *) -open monoidOrderTheory groupOrderTheory groupCyclicTheory; +open groupOrderTheory groupCyclicTheory; (* Get polynomial theory of Ring *) (* val _ = load "polyIrreducibleTheory"; *) @@ -43,19 +39,6 @@ open polyRootTheory; open polyDividesTheory; open polyIrreducibleTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Cyclic Polynomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyDerivativeScript.sml b/examples/algebra/polynomial/polyDerivativeScript.sml index 6ada70145a..f315bdc3ca 100644 --- a/examples/algebra/polynomial/polyDerivativeScript.sml +++ b/examples/algebra/polynomial/polyDerivativeScript.sml @@ -12,38 +12,18 @@ val _ = new_theory "polyDerivative"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyDivisionTheory"; *) *) -(* val _ = load "polyRootTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory; open polyMonicTheory polyRootTheory; -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) open monoidTheory groupTheory ringTheory; open fieldTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - - (* ------------------------------------------------------------------------- *) (* Formal Derivative of Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyDividesScript.sml b/examples/algebra/polynomial/polyDividesScript.sml index 62d8260047..6f3dcff9f5 100644 --- a/examples/algebra/polynomial/polyDividesScript.sml +++ b/examples/algebra/polynomial/polyDividesScript.sml @@ -12,25 +12,17 @@ val _ = new_theory "polyDivides"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; (* this overloads |/ as r*.inv *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; (* see poly_roots_mult, this overload |/ as (r.prod excluding #0).inv *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory dividesTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory fieldTheory; open subgroupTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* val _ = load "polyDivisionTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory; (* val _ = load "polyRootTheory"; *) @@ -47,21 +39,6 @@ open ringDividesTheory; (* val _ = load "polyEvalTheory"; *) open polyEvalTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory; - - (* ------------------------------------------------------------------------- *) (* Divisibility of Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyDivisionScript.sml b/examples/algebra/polynomial/polyDivisionScript.sml index 0931dcbc3a..14b1628736 100644 --- a/examples/algebra/polynomial/polyDivisionScript.sml +++ b/examples/algebra/polynomial/polyDivisionScript.sml @@ -12,44 +12,22 @@ val _ = new_theory "polyDivision"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; *) -open monoidTheory groupTheory ringTheory ringUnitTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; +open monoidTheory groupTheory ringTheory ringUnitTheory; -(* (* val _ = load "ringIdealTheory"; *) *) -(* val _ = load "quotientRingTheory"; *) open ringIdealTheory quotientRingTheory; open subgroupTheory; open quotientGroupTheory; -open monoidMapTheory groupMapTheory ringMapTheory; - -(* (* val _ = load "polyWeakTheory"; *) *) -(* val _ = load "polyRingTheory"; *) +open groupMapTheory ringMapTheory; open polynomialTheory polyWeakTheory polyRingTheory; - (* ------------------------------------------------------------------------- *) (* Polynomials Division over a Ring R[x] Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyEvalScript.sml b/examples/algebra/polynomial/polyEvalScript.sml index 67b6108478..6f5523875f 100644 --- a/examples/algebra/polynomial/polyEvalScript.sml +++ b/examples/algebra/polynomial/polyEvalScript.sml @@ -12,22 +12,15 @@ val _ = new_theory "polyEval"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* (* val _ = load "fieldTheory"; *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* val _ = load "polyFieldTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; (* val _ = load "polyMonicTheory"; *) @@ -36,19 +29,6 @@ open polyMonicTheory; (* val _ = load "polyRootTheory"; *) open polyRootTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Polynomial Evaluation giving Polynomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyFieldDivisionScript.sml b/examples/algebra/polynomial/polyFieldDivisionScript.sml index 688f3d4629..01f146199a 100644 --- a/examples/algebra/polynomial/polyFieldDivisionScript.sml +++ b/examples/algebra/polynomial/polyFieldDivisionScript.sml @@ -13,50 +13,26 @@ val _ = new_theory "polyFieldDivision"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; (* This takes |/ = (r.prod excluding #0).inv *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; + open monoidTheory groupTheory ringTheory integralDomainTheory fieldTheory; -(* (* val _ = load "ringIdealTheory"; *) *) -(* val _ = load "fieldIdealTheory"; *) open ringIdealTheory fieldIdealTheory; -(* (* val _ = load "groupOrderTheory"; *) *) -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* val _ = load "polyFieldTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; (* val _ = load "polyMonicTheory"; *) open polyMonicTheory; open polyDivisionTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Polynomials Division in F[x] Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyFieldModuloScript.sml b/examples/algebra/polynomial/polyFieldModuloScript.sml index 05d84bcbf6..310459e944 100644 --- a/examples/algebra/polynomial/polyFieldModuloScript.sml +++ b/examples/algebra/polynomial/polyFieldModuloScript.sml @@ -13,16 +13,13 @@ val _ = new_theory "polyFieldModulo"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyFieldTheory"; *) *) -(* (* val _ = load "polyFieldDivisionTheory"; *) *) -(* val _ = load "polyRingModuloTheory"; *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; + open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; open polyDivisionTheory polyFieldDivisionTheory; open polyModuloRingTheory polyRingModuloTheory; @@ -39,11 +36,10 @@ open polyMonicTheory; open polyProductTheory; open monoidTheory groupTheory ringTheory integralDomainTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; -(* val _ = load "fieldMapTheory"; *) -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; +open groupMapTheory ringMapTheory fieldMapTheory; (* (* val _ = load "ringDividesTheory"; *) *) open ringDividesTheory ringUnitTheory; @@ -53,20 +49,6 @@ open ringDividesTheory ringUnitTheory; open ringIdealTheory fieldIdealTheory; (* for EuclideanRing_def, quotient_field_by_maximal_ideal *) -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Field Polynomial Modulo Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyFieldScript.sml b/examples/algebra/polynomial/polyFieldScript.sml index 031840e113..7c14e7a03e 100644 --- a/examples/algebra/polynomial/polyFieldScript.sml +++ b/examples/algebra/polynomial/polyFieldScript.sml @@ -12,38 +12,19 @@ val _ = new_theory "polyField"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; + open monoidTheory groupTheory ringTheory integralDomainTheory fieldTheory; (* (* val _ = load "polyWeakTheory"; *) *) (* val _ = load "polyRingTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory; - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Polynomials over a Field F[x] Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyGCDScript.sml b/examples/algebra/polynomial/polyGCDScript.sml index 4ae838f84c..5b3d6baca2 100644 --- a/examples/algebra/polynomial/polyGCDScript.sml +++ b/examples/algebra/polynomial/polyGCDScript.sml @@ -12,21 +12,17 @@ val _ = new_theory "polyGCD"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; (* this overloads |/ as r*.inv *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; (* see poly_roots_mult, this overload |/ as (r.prod excluding #0).inv *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory gcdsetTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory fieldTheory; open subgroupTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; (* (* val _ = load "polyWeakTheory"; *) *) (* (* val _ = load "polyRingTheory"; *) *) @@ -55,21 +51,6 @@ open polyProductTheory; (* val _ = load "polyDerivativeTheory"; *) open polyDerivativeTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* GCD and LCM of Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyIrreducibleScript.sml b/examples/algebra/polynomial/polyIrreducibleScript.sml index e845b8193e..02a3833bb8 100644 --- a/examples/algebra/polynomial/polyIrreducibleScript.sml +++ b/examples/algebra/polynomial/polyIrreducibleScript.sml @@ -12,25 +12,20 @@ val _ = new_theory "polyIrreducible"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; (* This takes |/ = (r.prod excluding #0).inv *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; + open monoidTheory groupTheory ringTheory integralDomainTheory fieldTheory; (* (* val _ = load "ringIdealTheory"; *) *) (* val _ = load "fieldIdealTheory"; *) open ringIdealTheory fieldIdealTheory; -(* (* val _ = load "groupOrderTheory"; *) *) -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; (* Get polynomial theory of Ring *) (* (* val _ = load "polyWeakTheory"; *) *) @@ -50,19 +45,6 @@ open polyRootTheory; (* val _ = load "polyDividesTheory"; *) open ringDividesTheory polyDividesTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Irreducible Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyMapScript.sml b/examples/algebra/polynomial/polyMapScript.sml index c482b72a3e..27128e792b 100644 --- a/examples/algebra/polynomial/polyMapScript.sml +++ b/examples/algebra/polynomial/polyMapScript.sml @@ -12,18 +12,12 @@ val _ = new_theory "polyMap"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open arithmeticTheory pred_setTheory listTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyDivisionTheory"; *) *) -(* (* val _ = load "polyBinomialTheory"; *) *) -(* val _ = load "polyMultiplicityTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory; (* (* val _ = load "polyEvalTheory"; *) *) @@ -33,9 +27,6 @@ open polyMonicTheory polyEvalTheory; open polyRootTheory; open polyDividesTheory; -(* (* val _ = load "polyFieldTheory"; *) *) -(* (* val _ = load "polyFieldDivisionTheory"; *) *) -(* (* val _ = load "polyFieldModuloTheory"; *) *) open polyFieldTheory; open polyFieldDivisionTheory; open polyFieldModuloTheory; @@ -48,16 +39,11 @@ open polyMultiplicityTheory; open polyBinomialTheory; (* for coefficients *) open monoidTheory groupTheory ringTheory fieldTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; -open monoidMapTheory groupMapTheory ringMapTheory fieldMapTheory; - +open groupMapTheory ringMapTheory fieldMapTheory; -(* (* val _ = load "binomialTheory"; *) *) -open binomialTheory; - -(* (* val _ = load "ringBinomialTheory"; *) *) open ringBinomialTheory; open ringDividesTheory; open ringIdealTheory; @@ -67,23 +53,6 @@ open ringUnitTheory; open fieldOrderTheory; (* for field_order_eqn *) open groupCyclicTheory; (* for orders_def *) -(* (* val _ = load "groupInstancesTheory"; -- in ringInstancesTheory *) *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) -(* open groupInstancesTheory ringInstancesTheory fieldInstancesTheory; *) - -(* open dependent theories *) -open arithmeticTheory pred_setTheory listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Polynomial Maps Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyModuloRingScript.sml b/examples/algebra/polynomial/polyModuloRingScript.sml index 7871d73575..c22c2d66a8 100644 --- a/examples/algebra/polynomial/polyModuloRingScript.sml +++ b/examples/algebra/polynomial/polyModuloRingScript.sml @@ -12,48 +12,27 @@ val _ = new_theory "polyModuloRing"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; *) -open monoidTheory groupTheory ringTheory ringUnitTheory; +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; +open monoidTheory groupTheory ringTheory ringUnitTheory; -(* (* val _ = load "ringIdealTheory"; *) *) -(* val _ = load "quotientRingTheory"; *) open ringIdealTheory quotientRingTheory; open subgroupTheory; open quotientGroupTheory; -open monoidMapTheory groupMapTheory ringMapTheory; +open groupMapTheory ringMapTheory; -(* (* val _ = load "polyWeakTheory"; *) *) -(* val _ = load "polyDivisionTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory; open polyDivisionTheory; (* val _ = load "polyFieldTheory"; *) open polyFieldTheory; - (* ------------------------------------------------------------------------- *) (* Polynomial Quotient Ring by a Modulus Documentation *) (* ------------------------------------------------------------------------- *) @@ -1033,7 +1012,7 @@ val poly_mod_ring_carrier_alt = store_thm( t = {p | poly p /\ ((p = []) \/ deg p < deg z)}. Note BIJ chop s t by weak_poly_poly_bij Now FINITE s by weak_poly_finite - so FINITE t by FINITE_BIJ_PROPERTY + so FINITE t by FINITE_BIJ or FINITE Rz by poly_mod_ring_carrier_alt *) val poly_mod_ring_finite = store_thm( @@ -1041,13 +1020,13 @@ val poly_mod_ring_finite = store_thm( ``!r:'a ring z:'a poly. FiniteRing r ==> FINITE Rz``, rw[FiniteRing_def] >> `#0 IN R` by rw[] >> - metis_tac[weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ_PROPERTY, poly_mod_ring_carrier_alt]); + metis_tac[weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ, poly_mod_ring_carrier_alt]); (* Theorem: FiniteRing r ==> CARD Rz = (CARD R) ** (deg z) *) (* Proof: CARD Rz = CARD { p | poly p /\ ((p = []) \/ deg p < deg z) } by poly_mod_ring_carrier_alt - = CARD { p | weak p /\ (LENGTH p = deg z) } by weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ_PROPERTY + = CARD { p | weak p /\ (LENGTH p = deg z) } by weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ = CARD R ** (deg z) by weak_poly_card *) val poly_mod_ring_card = store_thm( @@ -1055,7 +1034,7 @@ val poly_mod_ring_card = store_thm( ``!r:'a ring z. FiniteRing r ==> (CARD Rz = (CARD R) ** (deg z))``, rw[FiniteRing_def] >> `#0 IN R` by rw[] >> - metis_tac[poly_mod_ring_carrier_alt, weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ_PROPERTY, weak_poly_card]); + metis_tac[poly_mod_ring_carrier_alt, weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ, weak_poly_card]); (* ------------------------------------------------------------------------- *) (* Polynomial Modulo Theorems *) diff --git a/examples/algebra/polynomial/polyMonicScript.sml b/examples/algebra/polynomial/polyMonicScript.sml index c132a965c6..e1c298d5bc 100644 --- a/examples/algebra/polynomial/polyMonicScript.sml +++ b/examples/algebra/polynomial/polyMonicScript.sml @@ -12,23 +12,15 @@ val _ = new_theory "polyMonic"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; *) -open monoidTheory groupTheory ringTheory ringUnitTheory; +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + rich_listTheory dividesTheory gcdTheory; -(* (* val _ = load "integralDomainTheory"; *) *) -(* (* val _ = load "fieldTheory"; *) *) +open monoidTheory groupTheory ringTheory ringUnitTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* val _ = load "polyDivisionTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory; open polyDivisionTheory; (* for ulead, pmonic and poly_mod theorems. *) @@ -36,20 +28,6 @@ open polyDivisionTheory; (* for ulead, pmonic and poly_mod theorems. *) open polyFieldTheory; open fieldTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open rich_listTheory; (* for NOT_SNOC_NIL *) - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Monic Polynomial Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyMultiplicityScript.sml b/examples/algebra/polynomial/polyMultiplicityScript.sml index 78ff4d947c..89a2166b75 100644 --- a/examples/algebra/polynomial/polyMultiplicityScript.sml +++ b/examples/algebra/polynomial/polyMultiplicityScript.sml @@ -12,10 +12,12 @@ val _ = new_theory "polyMultiplicity"; (* ------------------------------------------------------------------------- *) - - open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory numberTheory + combinatoricsTheory dividesTheory gcdTheory gcdsetTheory; + (* Get dependent theories local *) open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; open polyBinomialTheory polyDivisionTheory polyEvalTheory; @@ -30,15 +32,6 @@ open polyGCDTheory; open monoidTheory groupTheory ringTheory; open fieldTheory; -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory; - -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Multiple Roots of Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyProductScript.sml b/examples/algebra/polynomial/polyProductScript.sml index f59227e4ca..f3352876e2 100644 --- a/examples/algebra/polynomial/polyProductScript.sml +++ b/examples/algebra/polynomial/polyProductScript.sml @@ -12,17 +12,12 @@ val _ = new_theory "polyProduct"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; (* this overloads |/ as r*.inv *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; (* see poly_roots_mult, this overload |/ as (r.prod excluding #0).inv *) *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory gcdsetTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory fieldTheory; open subgroupTheory; @@ -31,9 +26,6 @@ open groupOrderTheory; (* val _ = load "groupProductTheory"; *) open groupProductTheory; -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* val _ = load "polyDividesTheory"; *) open polyDividesTheory polyDivisionTheory; open polynomialTheory polyWeakTheory polyRingTheory; @@ -43,20 +35,6 @@ open polyFieldDivisionTheory; open polyEvalTheory; open polyRootTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperListTheory"; *) *) -(* (* val _ = load "helperFunctionTheory"; *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Product of Polynomials Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyRingModuloScript.sml b/examples/algebra/polynomial/polyRingModuloScript.sml index 37caed244c..1ec9c08b29 100644 --- a/examples/algebra/polynomial/polyRingModuloScript.sml +++ b/examples/algebra/polynomial/polyRingModuloScript.sml @@ -12,16 +12,13 @@ val _ = new_theory "polyRingModulo"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyFieldTheory"; *) *) -(* val _ = load "polyDividesTheory"; *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory; + open polynomialTheory polyWeakTheory polyRingTheory; open polyDivisionTheory polyMonicTheory; open polyRootTheory polyEvalTheory; @@ -35,26 +32,12 @@ open polyBinomialTheory; (* Get dependent theories local *) open monoidTheory groupTheory ringTheory -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; open subgroupTheory; -open monoidMapTheory groupMapTheory ringMapTheory; +open groupMapTheory ringMapTheory; open ringUnitTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Ring Polynomial Modulo Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyRingScript.sml b/examples/algebra/polynomial/polyRingScript.sml index 299281e3b4..0f673cdabf 100644 --- a/examples/algebra/polynomial/polyRingScript.sml +++ b/examples/algebra/polynomial/polyRingScript.sml @@ -12,36 +12,22 @@ val _ = new_theory "polyRing"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "polynomialTheory"; *) *) -(* val _ = load "polyWeakTheory"; *) +(* open dependent theories *) +open pred_setTheory arithmeticTheory listTheory rich_listTheory numberTheory + dividesTheory combinatoricsTheory; + open monoidTheory groupTheory ringTheory; open polynomialTheory polyWeakTheory; (* val _ = load "ringUnitTheory"; *) open ringUnitTheory; -(* open dependent theories *) -open pred_setTheory arithmeticTheory listTheory rich_listTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperSetTheory helperListTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory; - +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Polynomials over a Ring R[x] Documentation *) @@ -4824,7 +4810,7 @@ val poly_truncated_by_degree = store_thm( Then {p | poly p /\ deg p < n} SUBSET s by SUBSET_DEF and BIJ chop {p | weak p /\ (LENGTH p = n)} s by weak_poly_poly_bij, FINITE R /\ #0 IN R Now FINITE {p | weak p /\ (LENGTH p = n)} by weak_poly_finite]); - so FINITE s by FINITE_BIJ_PROPERTY + so FINITE s by FINITE_BIJ Hence FINITE {p | poly p /\ deg p < n} by SUBSET_FINITE *) val poly_truncated_by_degree_finite = store_thm( @@ -4832,7 +4818,7 @@ val poly_truncated_by_degree_finite = store_thm( ``!r:'a ring. FINITE R /\ #0 IN R ==> !n. FINITE {p | poly p /\ deg p < n}``, rpt strip_tac >> `{p | poly p /\ deg p < n} SUBSET {p | poly p /\ ((p = []) \/ deg p < n)}` by rw[SUBSET_DEF] >> - metis_tac[weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ_PROPERTY, SUBSET_FINITE]); + metis_tac[weak_poly_poly_bij, weak_poly_finite, FINITE_BIJ, SUBSET_FINITE]); (* ------------------------------------------------------------------------- *) (* Other Useful Theorems *) diff --git a/examples/algebra/polynomial/polyRootScript.sml b/examples/algebra/polynomial/polyRootScript.sml index 7502e90429..9b56d1354d 100644 --- a/examples/algebra/polynomial/polyRootScript.sml +++ b/examples/algebra/polynomial/polyRootScript.sml @@ -12,22 +12,15 @@ val _ = new_theory "polyRoot"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringUnitTheory"; (* this overloads |/ as r*.inv *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* val _ = load "fieldTheory"; (* see poly_roots_mult, this overload |/ as (r.prod excluding #0).inv *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory gcdsetTheory; + open monoidTheory groupTheory ringTheory ringUnitTheory fieldTheory; -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* val _ = load "polyDivisionTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory; (* val _ = load "polyFieldTheory"; *) @@ -36,28 +29,12 @@ open polyFieldTheory; (* val _ = load "fieldOrderTheory"; *) open fieldOrderTheory; open groupOrderTheory; -open monoidOrderTheory; (* val _ = load "polyMonicTheory"; *) open polyMonicTheory; -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - open integralDomainTheory; (* for poly_roots_mult_id *) - (* ------------------------------------------------------------------------- *) (* Polynomials Factors and Roots Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/polynomial/polyWeakScript.sml b/examples/algebra/polynomial/polyWeakScript.sml index 900f5f7d18..d83988eb9d 100644 --- a/examples/algebra/polynomial/polyWeakScript.sml +++ b/examples/algebra/polynomial/polyWeakScript.sml @@ -12,39 +12,17 @@ val _ = new_theory "polyWeak"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "polynomialTheory"; *) -open monoidTheory gbagTheory groupTheory ringTheory polynomialTheory; - -(* Instances for examples. *) -(* (* val _ = load "ringInstancesTheory"; *) *) -(* (* val _ = load "fieldInstancesTheory"; *) *) -(* open ringInstancesTheory fieldInstancesTheory; *) - (* open dependent theories *) -open pairTheory bagTheory pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; *) *) -(* (* val _ = load "gcdTheory"; *) *) -(* open dividesTheory gcdTheory; *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* val _ = load "helperListTheory"; *) -open helperNumTheory helperListTheory; -open rich_listTheory; (* for MEM_LAST *) +open pairTheory bagTheory pred_setTheory listTheory arithmeticTheory + numberTheory rich_listTheory combinatoricsTheory; -(* val _ = load "sublistTheory"; *) -open sublistTheory; (* for sublist_every *) +open monoidTheory groupTheory ringTheory polynomialTheory; +(* Overload sublist by infix operator *) +val _ = temp_overload_on ("<=", ``sublist``); (* ------------------------------------------------------------------------- *) (* Weak Polynomials Documentation *) @@ -2541,7 +2519,7 @@ Proof Induct \\ rw[] \\ fs[] \\ simp[EL_weak_add] - \\ simp[helperSetTheory.COUNT_SUC_BY_SUC] + \\ simp[COUNT_SUC_BY_SUC] \\ simp[Once CROSS_INSERT_LEFT] \\ dep_rewrite.DEP_REWRITE_TAC[BAG_OF_SET_DISJOINT_UNION] \\ conj_tac >- simp[IN_DISJOINT] diff --git a/examples/algebra/polynomial/polynomialScript.sml b/examples/algebra/polynomial/polynomialScript.sml index deb93c8943..010e7b0a25 100644 --- a/examples/algebra/polynomial/polynomialScript.sml +++ b/examples/algebra/polynomial/polynomialScript.sml @@ -12,19 +12,13 @@ val _ = new_theory "polynomial"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) open pred_setTheory listTheory arithmeticTheory; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -open monoidTheory groupTheory ringTheory +open monoidTheory groupTheory ringTheory; (* ------------------------------------------------------------------------- *) (* Basic Polynomials Documentation *) diff --git a/examples/algebra/ring/Holmakefile b/examples/algebra/ring/Holmakefile index 303c8f7e5b..9ec29cbdb7 100644 --- a/examples/algebra/ring/Holmakefile +++ b/examples/algebra/ring/Holmakefile @@ -1 +1 @@ -INCLUDES = ../lib ../monoid ../group +INCLUDES = ../group diff --git a/examples/algebra/ring/integralDomainInstancesScript.sml b/examples/algebra/ring/integralDomainInstancesScript.sml index 258e1ea133..2db01bf547 100644 --- a/examples/algebra/ring/integralDomainInstancesScript.sml +++ b/examples/algebra/ring/integralDomainInstancesScript.sml @@ -22,32 +22,16 @@ val _ = new_theory "integralDomainInstances"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "integralDomainTheory"; *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory numberTheory; + open monoidTheory groupTheory ringTheory integralDomainTheory; -(* val _ = load "monoidInstancesTheory"; *) -open monoidInstancesTheory; + (* val _ = load "groupInstancesTheory"; *) open groupInstancesTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -open helperNumTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Integral Domain Instances Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/ring/integralDomainScript.sml b/examples/algebra/ring/integralDomainScript.sml index 0810e6893e..4f01f59207 100644 --- a/examples/algebra/ring/integralDomainScript.sml +++ b/examples/algebra/ring/integralDomainScript.sml @@ -26,26 +26,15 @@ val _ = new_theory "integralDomain"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "ringUnitTheory"; *) *) -(* val _ = load "ringIdealTheory"; *) -open groupTheory monoidTheory ringTheory ringUnitTheory ringIdealTheory; -open monoidOrderTheory groupOrderTheory; -open monoidMapTheory ringMapTheory ringDividesTheory; - -(* open dependent theories *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory sortingTheory containerTheory gbagTheory - dep_rewrite arithmeticTheory dividesTheory; +open pred_setTheory listTheory sortingTheory containerTheory dep_rewrite + arithmeticTheory dividesTheory; +open groupTheory monoidTheory ringTheory ringUnitTheory ringIdealTheory; +open groupOrderTheory; +open groupMapTheory ringMapTheory ringDividesTheory; (* ------------------------------------------------------------------------- *) (* Integral Domain Documentation *) @@ -271,10 +260,9 @@ Proof \\ simp[RingIso_def, RingHomo_def] \\ strip_tac \\ qmatch_asmsub_abbrev_tac`BIJ g s.carrier r.carrier` - \\ `Group s.sum /\ Group r.sum` by - metis_tac[Ring_def, groupTheory.AbelianGroup_def] - \\ `g s.sum.id = r.sum.id` by metis_tac[groupMapTheory.group_homo_id] - \\ conj_asm1_tac >- metis_tac[monoidMapTheory.monoid_homo_id] + \\ `Group s.sum /\ Group r.sum` by metis_tac[Ring_def, AbelianGroup_def] + \\ `g s.sum.id = r.sum.id` by metis_tac[group_homo_id] + \\ conj_asm1_tac >- metis_tac[monoid_homo_id] \\ rw[] \\ first_x_assum(qspecl_then[`g x`,`g y`]mp_tac) \\ impl_keep_tac >- metis_tac[BIJ_DEF, INJ_DEF] diff --git a/examples/algebra/ring/quotientRingScript.sml b/examples/algebra/ring/quotientRingScript.sml index 837757f7ef..3ca9c75c2c 100644 --- a/examples/algebra/ring/quotientRingScript.sml +++ b/examples/algebra/ring/quotientRingScript.sml @@ -24,33 +24,19 @@ val _ = new_theory "quotientRing"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory; +open arithmeticTheory dividesTheory pred_setTheory numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* val _ = load "ringIdealTheory"; *) open monoidTheory groupTheory ringTheory ringIdealTheory; -open monoidMapTheory groupMapTheory ringMapTheory; +open groupMapTheory ringMapTheory; -(* (* val _ = load "subgroupTheory"; *) *) -(* open subgroupTheory; *) -(* val _ = load "quotientGroupTheory"; *) open subgroupTheory; open quotientGroupTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperSetTheory; - -(* Get arithmetic for Ring characteristics *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -open arithmeticTheory dividesTheory; - - (* ------------------------------------------------------------------------- *) (* Quotient Ring Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/ring/ringBinomialScript.sml b/examples/algebra/ring/ringBinomialScript.sml index aeb810de96..3b60b244d0 100644 --- a/examples/algebra/ring/ringBinomialScript.sml +++ b/examples/algebra/ring/ringBinomialScript.sml @@ -12,32 +12,17 @@ val _ = new_theory "ringBinomial"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* val _ = load "binomialTheory"; *) -open binomialTheory; -open dividesTheory; +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "groupInstancesTheory"; *) *) -(* val _ = load "ringMapTheory"; *) open ringTheory; open groupTheory; open monoidTheory; -open monoidMapTheory groupMapTheory ringMapTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - +open groupMapTheory ringMapTheory; (* ------------------------------------------------------------------------- *) (* Ring Binomial Documentation *) diff --git a/examples/algebra/ring/ringDividesScript.sml b/examples/algebra/ring/ringDividesScript.sml index da0c675f69..cb658998a3 100644 --- a/examples/algebra/ring/ringDividesScript.sml +++ b/examples/algebra/ring/ringDividesScript.sml @@ -12,42 +12,22 @@ val _ = new_theory "ringDivides"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory dep_rewrite; +open pred_setTheory listTheory arithmeticTheory dep_rewrite numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "ringUnitTheory"; *) *) -(* val _ = load "ringIdealTheory"; *) open ringIdealTheory; open ringUnitTheory; open ringTheory; open groupTheory; -open monoidTheory gbagTheory bagTheory containerTheory; -val MEMBER_NOT_EMPTY = pred_setTheory.MEMBER_NOT_EMPTY; - -open ringMapTheory monoidMapTheory groupMapTheory; +open monoidTheory bagTheory containerTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "subgroupTheory"; *) *) -(* val _ = load "quotientGroupTheory"; *) +open ringMapTheory groupMapTheory; open subgroupTheory quotientGroupTheory; -(* Make srw_tac more powerful with SATISFY_ss *) -(* (* val _ = load "SatisfySimps"; *) *) -(* val _ = augment_srw_ss [SatisfySimps.SATISFY_ss]; *) - - (* ------------------------------------------------------------------------- *) (* Divisbility in Ring Documentation *) (* ------------------------------------------------------------------------- *) @@ -966,7 +946,7 @@ Proof \\ impl_tac >- ( `p = LINV f R (f p) /\ x = LINV f R (f x) /\ y = LINV f R (f y)` - by metis_tac[helperSetTheory.BIJ_LINV_THM] + by metis_tac[BIJ_LINV_THM] \\ ntac 3 (pop_assum SUBST1_TAC) \\ `r.prod.op (LINV f R (f x)) (LINV f R (f y)) = LINV f R (r_.prod.op (f x) (f y))` diff --git a/examples/algebra/ring/ringIdealScript.sml b/examples/algebra/ring/ringIdealScript.sml index bae08aa773..a0c3f875b6 100644 --- a/examples/algebra/ring/ringIdealScript.sml +++ b/examples/algebra/ring/ringIdealScript.sml @@ -12,40 +12,21 @@ val _ = new_theory "ringIdeal"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; +open pred_setTheory listTheory arithmeticTheory gcdsetTheory numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* val _ = load "ringMapTheory"; *) open ringTheory; open groupTheory; open monoidTheory; -open monoidMapTheory groupMapTheory ringMapTheory; +open groupMapTheory ringMapTheory; -(* val _ = load "ringUnitTheory"; *) open ringUnitTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "subgroupTheory"; *) *) -(* val _ = load "quotientGroupTheory"; *) open subgroupTheory quotientGroupTheory; -(* Make srw_tac more powerful with SATISFY_ss *) -(* (* val _ = load "SatisfySimps"; *) *) -(* val _ = augment_srw_ss [SatisfySimps.SATISFY_ss]; *) - - (* ------------------------------------------------------------------------- *) (* Ideals in Ring Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/ring/ringInstancesScript.sml b/examples/algebra/ring/ringInstancesScript.sml index 381a446dcb..646917007d 100644 --- a/examples/algebra/ring/ringInstancesScript.sml +++ b/examples/algebra/ring/ringInstancesScript.sml @@ -21,26 +21,16 @@ val _ = new_theory "ringInstances"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; +open prim_recTheory pred_setTheory arithmeticTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory whileTheory primeTheory; + open monoidTheory groupTheory ringTheory; -open monoidInstancesTheory; open groupInstancesTheory; -open monoidOrderTheory groupOrderTheory; - -open monoidMapTheory groupMapTheory ringMapTheory; - -open helperNumTheory helperFunctionTheory; - -open prim_recTheory pred_setTheory arithmeticTheory dividesTheory gcdTheory; -open EulerTheory; - -open GaussTheory; - -open whileTheory; (* for order computation by WHILE loop *) - +open groupOrderTheory; +open groupMapTheory ringMapTheory; (* ------------------------------------------------------------------------- *) (* Ring Instances Documentation *) diff --git a/examples/algebra/ring/ringIntegerScript.sml b/examples/algebra/ring/ringIntegerScript.sml index c4c02cae40..9e3f9850ab 100644 --- a/examples/algebra/ring/ringIntegerScript.sml +++ b/examples/algebra/ring/ringIntegerScript.sml @@ -12,46 +12,25 @@ val _ = new_theory "ringInteger"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; - -(* val _ = load "integerTheory"; *) -open integerTheory; +open pred_setTheory listTheory arithmeticTheory integerTheory numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "ringIdealTheory"; *) *) -(* val _ = load "quotientRingTheory"; *) open quotientRingTheory; open ringIdealTheory; open ringTheory; open groupTheory; open monoidTheory; -open monoidMapTheory groupMapTheory ringMapTheory; +open groupMapTheory ringMapTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* (* val _ = load "subgroupTheory"; *) *) -(* val _ = load "quotientGroupTheory"; *) open subgroupTheory quotientGroupTheory; -(* (* val _ = load "monoidInstancesTheory"; *) *) -(* (* val _ = load "groupInstancesTheory"; *) *) -(* val _ = load "ringInstancesTheory"; *) open groupInstancesTheory; -open monoidInstancesTheory; open ringInstancesTheory; - (* ------------------------------------------------------------------------- *) (* Integer Ring Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/ring/ringMapScript.sml b/examples/algebra/ring/ringMapScript.sml index 904b00a76d..8a144df3d5 100644 --- a/examples/algebra/ring/ringMapScript.sml +++ b/examples/algebra/ring/ringMapScript.sml @@ -12,36 +12,25 @@ val _ = new_theory "ringMap"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories local *) -(* val _ = load "groupOrderTheory"; (* loads monoidTheory implicitly *) *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory gcdsetTheory + numberTheory combinatoricsTheory; + open monoidTheory groupTheory; -open monoidOrderTheory groupOrderTheory; +open groupOrderTheory; (* val _ = load "ringUnitTheory"; *) open ringTheory ringUnitTheory; (* val _ = load "subgroupTheory"; *) -open submonoidTheory subgroupTheory; -open monoidMapTheory groupMapTheory; +open subgroupTheory; +open groupMapTheory; (* val _ = load "quotientGroupTheory"; *) open quotientGroupTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* Get arithmetic for Ring characteristics *) -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Ring Maps Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/algebra/ring/ringRealScript.sml b/examples/algebra/ring/ringRealScript.sml index db80e74848..aa1094a634 100644 --- a/examples/algebra/ring/ringRealScript.sml +++ b/examples/algebra/ring/ringRealScript.sml @@ -1,10 +1,12 @@ (* ------------------------------------------------------------------------- *) (* Reals as a ring. *) (* ------------------------------------------------------------------------- *) -open HolKernel boolLib bossLib Parse dep_rewrite - realTheory ringTheory ringMapTheory ringUnitTheory - ringDividesTheory monoidRealTheory groupRealTheory - pred_setTheory bagTheory gbagTheory real_sigmaTheory iterateTheory +open HolKernel boolLib bossLib Parse; + +open dep_rewrite realTheory pred_setTheory bagTheory real_sigmaTheory + iterateTheory monoidTheory real_algebraTheory; + +open ringTheory ringMapTheory ringUnitTheory ringDividesTheory groupRealTheory; val _ = new_theory"ringReal"; @@ -41,7 +43,7 @@ Proof \\ irule EQ_SYM \\ irule ring_unit_linv_unique \\ simp[] - \\ simp[Reals_def, REAL_MUL_LINV, monoidOrderTheory.Invertibles_carrier] + \\ simp[Reals_def, REAL_MUL_LINV, Invertibles_carrier] QED Theorem ring_divides_Reals: diff --git a/examples/algebra/ring/ringScript.sml b/examples/algebra/ring/ringScript.sml index ade062c311..fe986c3738 100644 --- a/examples/algebra/ring/ringScript.sml +++ b/examples/algebra/ring/ringScript.sml @@ -23,25 +23,15 @@ val _ = new_theory "ring"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; *) -(* (* val _ = load "helperNumTheory"; -- in helperFunctionTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in helperFunctionTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; - (* Get arithmetic for Ring characteristics *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; +open pred_setTheory arithmeticTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* val _ = load "groupOrderTheory"; (* loads monoidTheory implicitly *) *) open monoidTheory groupTheory; -open monoidOrderTheory groupOrderTheory; - +open groupOrderTheory; (* ------------------------------------------------------------------------- *) (* Ring Documentation *) diff --git a/examples/algebra/ring/ringUnitScript.sml b/examples/algebra/ring/ringUnitScript.sml index 40763b1f73..4909a4df39 100644 --- a/examples/algebra/ring/ringUnitScript.sml +++ b/examples/algebra/ring/ringUnitScript.sml @@ -12,28 +12,16 @@ val _ = new_theory "ringUnit"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; (* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory; -(* Get dependent theories local *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "groupInstancesTheory"; *) *) -(* val _ = load "ringTheory"; *) open ringTheory; open groupTheory; open monoidTheory; -open monoidOrderTheory groupOrderTheory; - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - +open groupOrderTheory; (* ------------------------------------------------------------------------- *) (* Ring Units Documentation *) diff --git a/src/algebra/Holmakefile b/src/algebra/Holmakefile new file mode 100644 index 0000000000..21292da5dc --- /dev/null +++ b/src/algebra/Holmakefile @@ -0,0 +1,9 @@ +INCLUDES = ../bag + +ifdef HOLBUILD +.PHONY: link-to-sigobj +all: link-to-sigobj + +link-to-sigobj: $(DEFAULT_TARGETS) + $(HOL_LNSIGOBJ) +endif diff --git a/src/algebra/combinatoricsScript.sml b/src/algebra/combinatoricsScript.sml new file mode 100644 index 0000000000..13566ee8a4 --- /dev/null +++ b/src/algebra/combinatoricsScript.sml @@ -0,0 +1,16640 @@ +(* ------------------------------------------------------------------------- *) +(* Combinatorics Theory *) +(* (Combined theory of Euler, Gauss, Mobius, triangle and binomial, etc., *) +(* originally under "examples/algebra/lib") *) +(* *) +(* Author: (Joseph) Hing-Lun Chan (Australian National University, 2019) *) +(* ------------------------------------------------------------------------- *) + +(* ------------------------------------------------------------------------- *) +(* Necklace Theory - monocoloured and multicoloured. *) +(* ------------------------------------------------------------------------- *) +(* + +Necklace Theory +=============== + +Consider the set N of necklaces of length n (i.e. with number of beads = n) +with a colors (i.e. the number of bead colors = a). A linear picture of such +a necklace is: + ++--+--+--+--+--+--+--+ +|2 |4 |0 |3 |1 |2 |3 | p = 7, with (lots of) beads of a = 5 colors: 01234. ++--+--+--+--+--+--+--+ + +Since a bead can have any of the a colors, and there are n beads in total, + +Number of such necklaces = CARD N = a*a*...*a = a^n. + +There is only 1 necklace of pure color A, 1 necklace with pure color B, etc. + +Number of monocoloured necklaces = a = CARD S, where S = monocoloured necklaces. + +So, N = S UNION M, where M = multicoloured necklaces (i.e. more than one color). + +Since S and M are disjoint, CARD M = CARD N - CARD S = a^n - a. + +*) + +open HolKernel boolLib Parse bossLib; + +open prim_recTheory arithmeticTheory dividesTheory gcdTheory gcdsetTheory + logrootTheory pred_setTheory listTheory rich_listTheory numberTheory + listRangeTheory indexedListsTheory relationTheory; + +val _ = new_theory "combinatorics"; + +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); + +(* ------------------------------------------------------------------------- *) +(* List Reversal. *) +(* ------------------------------------------------------------------------- *) + +(* Overload for REVERSE [m .. n] *) +val _ = overload_on ("downto", ``\n m. REVERSE [m .. n]``); +val _ = set_fixity "downto" (Infix(NONASSOC, 450)); (* same as relation *) + +(* ------------------------------------------------------------------------- *) +(* Extra List Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: EVERY (\c. c IN R) p ==> !k. k < LENGTH p ==> EL k p IN R *) +(* Proof: by EVERY_EL. *) +val EVERY_ELEMENT_PROPERTY = store_thm( + "EVERY_ELEMENT_PROPERTY", + ``!p R. EVERY (\c. c IN R) p ==> !k. k < LENGTH p ==> EL k p IN R``, + rw[EVERY_EL]); + +(* Theorem: (!x. P x ==> (Q o f) x) /\ EVERY P l ==> EVERY Q (MAP f l) *) +(* Proof: + Since !x. P x ==> (Q o f) x, + EVERY P l + ==> EVERY Q o f l by EVERY_MONOTONIC + ==> EVERY Q (MAP f l) by EVERY_MAP +*) +val EVERY_MONOTONIC_MAP = store_thm( + "EVERY_MONOTONIC_MAP", + ``!l f P Q. (!x. P x ==> (Q o f) x) /\ EVERY P l ==> EVERY Q (MAP f l)``, + metis_tac[EVERY_MONOTONIC, EVERY_MAP]); + +(* Theorem: EVERY (\j. j < n) ls ==> EVERY (\j. j <= n) ls *) +(* Proof: by EVERY_EL, arithmetic. *) +val EVERY_LT_IMP_EVERY_LE = store_thm( + "EVERY_LT_IMP_EVERY_LE", + ``!ls n. EVERY (\j. j < n) ls ==> EVERY (\j. j <= n) ls``, + simp[EVERY_EL, LESS_IMP_LESS_OR_EQ]); + +(* Theorem: (LENGTH (h1::t1) = LENGTH (h2::t2)) /\ + (!k. k < LENGTH (h1::t1) ==> P (EL k (h1::t1)) (EL k (h2::t2))) ==> + (P h1 h2) /\ (!k. k < LENGTH t1 ==> P (EL k t1) (EL k t2)) *) +(* Proof: + Put k = 0, + Then LENGTH (h1::t1) = SUC (LENGTH t1) by LENGTH + > 0 by SUC_POS + and P (EL 0 (h1::t1)) (EL 0 (h2::t2)) by implication, 0 < LENGTH (h1::t1) + or P HD (h1::t1) HD (h2::t2) by EL + or P h1 h2 by HD + Note k < LENGTH t1 + ==> k + 1 < SUC (LENGTH t1) by ADD1 + = LENGTH (h1::t1) by LENGTH + Thus P (EL (k + 1) (h1::t1)) (EL (k + 1) (h2::t2)) by implication + or P (EL (PRE (k + 1) t1)) (EL (PRE (k + 1)) t2) by EL_CONS + or P (EL k t1) (EL k t2) by PRE, ADD1 +*) +val EL_ALL_PROPERTY = store_thm( + "EL_ALL_PROPERTY", + ``!h1 t1 h2 t2 P. (LENGTH (h1::t1) = LENGTH (h2::t2)) /\ + (!k. k < LENGTH (h1::t1) ==> P (EL k (h1::t1)) (EL k (h2::t2))) ==> + (P h1 h2) /\ (!k. k < LENGTH t1 ==> P (EL k t1) (EL k t2))``, + rpt strip_tac >| [ + `0 < LENGTH (h1::t1)` by metis_tac[LENGTH, SUC_POS] >> + metis_tac[EL, HD], + `k + 1 < SUC (LENGTH t1)` by decide_tac >> + `k + 1 < LENGTH (h1::t1)` by metis_tac[LENGTH] >> + `0 < k + 1 /\ (PRE (k + 1) = k)` by decide_tac >> + metis_tac[EL_CONS] + ]); + +(* +LUPDATE_SEM |- (!e n l. LENGTH (LUPDATE e n l) = LENGTH l) /\ + !e n l p. p < LENGTH l ==> EL p (LUPDATE e n l) = if p = n then e else EL p l +EL_LUPDATE |- !ys x i k. EL i (LUPDATE x k ys) = if i = k /\ k < LENGTH ys then x else EL i ys +LENGTH_LUPDATE |- !x n ys. LENGTH (LUPDATE x n ys) = LENGTH ys +*) + +(* Extract useful theorem from LUPDATE semantics *) +val LUPDATE_LEN = save_thm("LUPDATE_LEN", LUPDATE_SEM |> CONJUNCT1); +(* val LUPDATE_LEN = |- !e n l. LENGTH (LUPDATE e n l) = LENGTH l: thm *) +val LUPDATE_EL = save_thm("LUPDATE_EL", LUPDATE_SEM |> CONJUNCT2); +(* val LUPDATE_EL = |- !e n l p. p < LENGTH l ==> EL p (LUPDATE e n l) = if p = n then e else EL p l: thm *) + +(* Theorem: LUPDATE q n (LUPDATE p n ls) = LUPDATE q n ls *) +(* Proof: + Let l1 = LUPDATE q n (LUPDATE p n ls), l2 = LUPDATE q n ls. + By LIST_EQ, this is to show: + (1) LENGTH l1 = LENGTH l2 + LENGTH l1 + = LENGTH (LUPDATE q n (LUPDATE p n ls)) by notation + = LENGTH (LUPDATE p n ls) by LUPDATE_LEN + = ls by LUPDATE_LEN + = LENGTH (LUPDATE q n ls) by LUPDATE_LEN + = LENGTH l2 by notation + (2) !x. x < LENGTH l1 ==> EL x l1 = EL x l2 + EL x l1 + = EL x (LUPDATE q n (LUPDATE p n ls)) by notation + = if x = n then q else EL x (LUPDATE p n ls) by LUPDATE_EL + = if x = n then q else (if x = n then p else EL x ls) by LUPDATE_EL + = if x = n then q else EL x ls by simplification + = EL x (LUPDATE q n ls) by LUPDATE_EL + = EL x l2 by notation +*) +val LUPDATE_SAME_SPOT = store_thm( + "LUPDATE_SAME_SPOT", + ``!ls n p q. LUPDATE q n (LUPDATE p n ls) = LUPDATE q n ls``, + rpt strip_tac >> + qabbrev_tac `l1 = LUPDATE q n (LUPDATE p n ls)` >> + qabbrev_tac `l2 = LUPDATE q n ls` >> + `LENGTH l1 = LENGTH l2` by rw[LUPDATE_LEN, Abbr`l1`, Abbr`l2`] >> + `!x. x < LENGTH l1 ==> (EL x l1 = EL x l2)` by fs[LUPDATE_EL, Abbr`l1`, Abbr`l2`] >> + rw[LIST_EQ]); + +(* Theorem: m <> n ==> + (LUPDATE q n (LUPDATE p m ls) = LUPDATE p m (LUPDATE q n ls)) *) +(* Proof: + Let l1 = LUPDATE q n (LUPDATE p m ls), + l2 = LUPDATE p m (LUPDATE q n ls). + LENGTH l1 + = LENGTH (LUPDATE q n (LUPDATE p m ls)) by notation + = LENGTH (LUPDATE p m ls) by LUPDATE_LEN + = LENGTH ls by LUPDATE_LEN + = LENGTH (LUPDATE q n ls) by LUPDATE_LEN + = LENGTH (LUPDATE p m (LUPDATE q n ls)) by LUPDATE_LEN + = LENGTH l2 by notation + !x. x < LENGTH l1 ==> + EL x l1 + = EL x ((LUPDATE q n (LUPDATE p m ls)) by notation + = EL x ls if x <> n, x <> m, or p if x = m, q if x = n + by LUPDATE_EL + EL x l2 + = EL x ((LUPDATE p m (LUPDATE q n ls)) by notation + = EL x ls if x <> m, x <> n, or q if x = n, p if x = m + by LUPDATE_EL + = EL x l1 + Hence l1 = l2 by LIST_EQ +*) +val LUPDATE_DIFF_SPOT = store_thm( + "LUPDATE_DIFF_SPOT", + `` !ls m n p q. m <> n ==> + (LUPDATE q n (LUPDATE p m ls) = LUPDATE p m (LUPDATE q n ls))``, + rpt strip_tac >> + qabbrev_tac `l1 = LUPDATE q n (LUPDATE p m ls)` >> + qabbrev_tac `l2 = LUPDATE p m (LUPDATE q n ls)` >> + irule LIST_EQ >> + rw[LUPDATE_EL, Abbr`l1`, Abbr`l2`]); + +(* Theorem: LUPDATE a (LENGTH ls) (ls ++ (h::t)) = ls ++ (a::t) *) +(* Proof: + LUPDATE a (LENGTH ls) (ls ++ h::t) + = ls ++ LUPDATE a (LENGTH ls - LENGTH ls) (h::t) by LUPDATE_APPEND2 + = ls ++ LUPDATE a 0 (h::t) by arithmetic + = ls ++ (a::t) by LUPDATE_def +*) +val LUPDATE_APPEND_0 = store_thm( + "LUPDATE_APPEND_0", + ``!ls a h t. LUPDATE a (LENGTH ls) (ls ++ (h::t)) = ls ++ (a::t)``, + rw_tac std_ss[LUPDATE_APPEND2, LUPDATE_def]); + +(* Theorem: LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = ls ++ h::b::t *) +(* Proof: + LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) + = ls ++ LUPDATE b (LENGTH ls + 1 - LENGTH ls) (h::k::t) by LUPDATE_APPEND2 + = ls ++ LUPDATE b 1 (h::k::t) by arithmetic + = ls ++ (h::b::t) by LUPDATE_def +*) +val LUPDATE_APPEND_1 = store_thm( + "LUPDATE_APPEND_1", + ``!ls b h k t. LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = ls ++ h::b::t``, + rpt strip_tac >> + `LUPDATE b 1 (h::k::t) = h::LUPDATE b 0 (k::t)` by rw[GSYM LUPDATE_def] >> + `_ = h::b::t` by rw[LUPDATE_def] >> + `LUPDATE b (LENGTH ls + 1) (ls ++ h::k::t) = + ls ++ LUPDATE b (LENGTH ls + 1 - LENGTH ls) (h::k::t)` by metis_tac[LUPDATE_APPEND2, DECIDE``n <= n + 1``] >> + fs[]); + +(* Theorem: LUPDATE b (LENGTH ls + 1) + (LUPDATE a (LENGTH ls) (ls ++ h::k::t)) = ls ++ a::b::t *) +(* Proof: + Let l1 = LUPDATE a (LENGTH ls) (ls ++ h::k::t) + = ls ++ a::k::t by LUPDATE_APPEND_0 + LUPDATE b (LENGTH ls + 1) l1 + = LUPDATE b (LENGTH ls + 1) (ls ++ a::k::t) + = ls ++ a::b::t by LUPDATE_APPEND2_1 +*) +val LUPDATE_APPEND_0_1 = store_thm( + "LUPDATE_APPEND_0_1", + ``!ls a b h k t. + LUPDATE b (LENGTH ls + 1) + (LUPDATE a (LENGTH ls) (ls ++ h::k::t)) = ls ++ a::b::t``, + rw_tac std_ss[LUPDATE_APPEND_0, LUPDATE_APPEND_1]); + +(* Theorem: let fs = FILTER P ls in + ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> + (findi y fs = 1 + findi x fs <=> FILTER P l2 = []) *) +(* Proof: + Let j = LENGTH (FILTER P l1). + + Note fs = FILTER P l1 ++ x::FILTER P l2 ++ + y::FILTER P l3 by FILTER_APPEND_DISTRIB + Thus LENGTH fs = j + + SUC (LENGTH (FILTER P l2)) + + SUC (LENGTH (FILTER P l3)) by LENGTH_APPEND + or j + 2 <= LENGTH fs by arithmetic + or j < LENGTH fs /\ j + 1 < LENGTH fs by j + 2 <= LENGTH fs + + Let l4 = y::l3, + Then ls = l1 ++ x::l2 ++ l4 + = l1 ++ x::(l2 ++ l4) by APPEND_ASSOC_CONS + ==> x = EL j fs by FILTER_EL_IMP + + Note ALL_DISTINCT fs by FILTER_ALL_DISTINCT + and MEM x ls /\ MEM y ls by MEM_APPEND + so MEM x fs /\ MEM y fs by MEM_FILTER + and x = EL j fs <=> findi x fs = j by findi_EL_iff + and y = EL (j + 1) fs <=> findi y fs = j + 1 by findi_EL_iff + + FILTER P l2 = [] + <=> x = EL j fs /\ y = EL (j + 1) fs by FILTER_EL_NEXT_IFF + <=> findi y fs = 1 + findi x fs by above +*) +Theorem FILTER_EL_NEXT_IDX: + !P ls l1 l2 l3 x y. let fs = FILTER P ls in + ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> + (findi y fs = 1 + findi x fs <=> FILTER P l2 = []) +Proof + rw_tac std_ss[] >> + qabbrev_tac `ls = l1 ++ x::l2 ++ y::l3` >> + qabbrev_tac `j = LENGTH (FILTER P l1)` >> + `j + 2 <= LENGTH fs` by + (`fs = FILTER P l1 ++ x::FILTER P l2 ++ y::FILTER P l3` by simp[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`ls`] >> + `LENGTH fs = j + SUC (LENGTH (FILTER P l2)) + SUC (LENGTH (FILTER P l3))` by fs[Abbr`j`] >> + decide_tac) >> + `j < LENGTH fs /\ j + 1 < LENGTH fs` by decide_tac >> + `x = EL j fs` by + (qabbrev_tac `l4 = y::l3` >> + `ls = l1 ++ x::(l2 ++ l4)` by simp[Abbr`ls`] >> + metis_tac[FILTER_EL_IMP]) >> + `MEM x ls /\ MEM y ls` by fs[Abbr`ls`] >> + `MEM x fs /\ MEM y fs` by fs[MEM_FILTER, Abbr`fs`] >> + `ALL_DISTINCT fs` by simp[FILTER_ALL_DISTINCT, Abbr`fs`] >> + `x = EL j fs <=> findi x fs = j` by fs[findi_EL_iff] >> + `y = EL (j + 1) fs <=> findi y fs = 1 + j` by fs[findi_EL_iff] >> + metis_tac[FILTER_EL_NEXT_IFF] +QED + +(* ------------------------------------------------------------------------- *) +(* List Rotation. *) +(* ------------------------------------------------------------------------- *) + +(* Define rotation of a list *) +val rotate_def = Define ` + rotate n l = DROP n l ++ TAKE n l +`; + +(* Theorem: Rotate shifts element + rotate n l = EL n l::(DROP (SUC n) l ++ TAKE n l) *) +(* Proof: + h h t t t t t t --> t t t t t h h + k k + TAKE 2 x = h h + DROP 2 x = t t t t t t + k + DROP 2 x ++ TAKE 2 x has element k at front. + + Proof: by induction on l. + Base case: !n. n < LENGTH [] ==> (DROP n [] = EL n []::DROP (SUC n) []) + Since n < LENGTH [] = 0 is F, this is true. + Step case: !h n. n < LENGTH (h::l) ==> (DROP n (h::l) = EL n (h::l)::DROP (SUC n) (h::l)) + i.e. n <> 0 /\ n < SUC (LENGTH l) ==> DROP (n - 1) l = EL n (h::l)::DROP n l by DROP_def + n <> 0 means ?j. n = SUC j < SUC (LENGTH l), so j < LENGTH l. + LHS = DROP (SUC j - 1) l + = DROP j l by SUC j - 1 = j + = EL j l :: DROP (SUC j) l by induction hypothesis + RHS = EL (SUC j) (h::l) :: DROP (SUC (SUC j)) (h::l) + = EL j l :: DROP (SUC j) l by EL, DROP_def + = LHS +*) +Theorem rotate_shift_element: + !l n. n < LENGTH l ==> (rotate n l = EL n l::(DROP (SUC n) l ++ TAKE n l)) +Proof + rw[rotate_def] >> + pop_assum mp_tac >> + qid_spec_tac `n` >> + Induct_on `l` >- rw[] >> + rw[DROP_def] >> Cases_on `n` >> fs[] +QED + +(* Theorem: rotate 0 l = l *) +(* Proof: + rotate 0 l + = DROP 0 l ++ TAKE 0 l by rotate_def + = l ++ [] by DROP_def, TAKE_def + = l by APPEND +*) +val rotate_0 = store_thm( + "rotate_0", + ``!l. rotate 0 l = l``, + rw[rotate_def]); + +(* Theorem: rotate n [] = [] *) +(* Proof: + rotate n [] + = DROP n [] ++ TAKE n [] by rotate_def + = [] ++ [] by DROP_def, TAKE_def + = [] by APPEND +*) +val rotate_nil = store_thm( + "rotate_nil", + ``!n. rotate n [] = []``, + rw[rotate_def]); + +(* Theorem: rotate (LENGTH l) l = l *) +(* Proof: + rotate (LENGTH l) l + = DROP (LENGTH l) l ++ TAKE (LENGTH l) l by rotate_def + = [] ++ TAKE (LENGTH l) l by DROP_LENGTH_NIL + = [] ++ l by TAKE_LENGTH_ID + = l +*) +val rotate_full = store_thm( + "rotate_full", + ``!l. rotate (LENGTH l) l = l``, + rw[rotate_def, DROP_LENGTH_NIL]); + +(* Theorem: n < LENGTH l ==> rotate (SUC n) l = rotate 1 (rotate n l) *) +(* Proof: + Since n < LENGTH l, l <> [] by LENGTH_NIL. + Thus DROP n l <> [] by DROP_EQ_NIL (need n < LENGTH l) + Expand by rotate_def, this is to show: + DROP (SUC n) l ++ TAKE (SUC n) l = DROP 1 (DROP n l ++ TAKE n l) ++ TAKE 1 (DROP n l ++ TAKE n l) + LHS = DROP (SUC n) l ++ TAKE (SUC n) l + = DROP 1 (DROP n l) ++ (TAKE n l ++ TAKE 1 (DROP n l)) by DROP_SUC, TAKE_SUC + Since DROP n l <> [] from above, + RHS = DROP 1 (DROP n l ++ TAKE n l) ++ TAKE 1 (DROP n l ++ TAKE n l) + = DROP 1 (DROP n l) ++ (TAKE n l ++ TAKE 1 (DROP n l)) by DROP_1_APPEND, TAKE_1_APPEND + = LHS +*) +val rotate_suc = store_thm( + "rotate_suc", + ``!l n. n < LENGTH l ==> (rotate (SUC n) l = rotate 1 (rotate n l))``, + rpt strip_tac >> + `LENGTH l <> 0` by decide_tac >> + `l <> []` by metis_tac[LENGTH_NIL] >> + `DROP n l <> []` by simp[DROP_EQ_NIL] >> + rw[rotate_def, DROP_1_APPEND, TAKE_1_APPEND, DROP_SUC, TAKE_SUC]); + +(* Theorem: Rotate keeps LENGTH (of necklace): LENGTH (rotate n l) = LENGTH l *) +(* Proof: + LENGTH (rotate n l) + = LENGTH (DROP n l ++ TAKE n l) by rotate_def + = LENGTH (DROP n l) + LENGTH (TAKE n l) by LENGTH_APPEND + = LENGTH (TAKE n l) + LENGTH (DROP n l) by arithmetic + = LENGTH (TAKE n l ++ DROP n l) by LENGTH_APPEND + = LENGTH l by TAKE_DROP +*) +val rotate_same_length = store_thm( + "rotate_same_length", + ``!l n. LENGTH (rotate n l) = LENGTH l``, + rpt strip_tac >> + `LENGTH (rotate n l) = LENGTH (DROP n l ++ TAKE n l)` by rw[rotate_def] >> + `_ = LENGTH (DROP n l) + LENGTH (TAKE n l)` by rw[] >> + `_ = LENGTH (TAKE n l) + LENGTH (DROP n l)` by rw[ADD_COMM] >> + `_ = LENGTH (TAKE n l ++ DROP n l)` by rw[] >> + rw_tac std_ss[TAKE_DROP]); + +(* Theorem: Rotate keeps SET (of elements): set (rotate n l) = set l *) +(* Proof: + set (rotate n l) + = set (DROP n l ++ TAKE n l) by rotate_def + = set (DROP n l) UNION set (TAKE n l) by LIST_TO_SET_APPEND + = set (TAKE n l) UNION set (DROP n l) by UNION_COMM + = set (TAKE n l ++ DROP n l) by LIST_TO_SET_APPEND + = set l by TAKE_DROP +*) +val rotate_same_set = store_thm( + "rotate_same_set", + ``!l n. set (rotate n l) = set l``, + rpt strip_tac >> + `set (rotate n l) = set (DROP n l ++ TAKE n l)` by rw[rotate_def] >> + `_ = set (DROP n l) UNION set (TAKE n l)` by rw[] >> + `_ = set (TAKE n l) UNION set (DROP n l)` by rw[UNION_COMM] >> + `_ = set (TAKE n l ++ DROP n l)` by rw[] >> + rw_tac std_ss[TAKE_DROP]); + +(* Theorem: n + m <= LENGTH l ==> rotate n (rotate m l) = rotate (n + m) l *) +(* Proof: + By induction on n. + Base case: !m l. 0 + m <= LENGTH l ==> (rotate 0 (rotate m l) = rotate (0 + m) l) + rotate 0 (rotate m l) + = rotate m l by rotate_0 + = rotate (0 + m) l by ADD + Step case: !m l. SUC n + m <= LENGTH l ==> (rotate (SUC n) (rotate m l) = rotate (SUC n + m) l) + rotate (SUC n) (rotate m l) + = rotate 1 (rotate n (rotate m l)) by rotate_suc + = rotate 1 (rotate (n + m) l) by induction hypothesis + = rotate (SUC (n + m)) l by rotate_suc + = rotate (SUC n + m) l by ADD_CLAUSES +*) +val rotate_add = store_thm( + "rotate_add", + ``!n m l. n + m <= LENGTH l ==> (rotate n (rotate m l) = rotate (n + m) l)``, + Induct >- + rw[rotate_0] >> + rw[] >> + `LENGTH (rotate m l) = LENGTH l` by rw[rotate_same_length] >> + `LENGTH (rotate (n + m) l) = LENGTH l` by rw[rotate_same_length] >> + `n < LENGTH l /\ n + m < LENGTH l /\ n + m <= LENGTH l` by decide_tac >> + rw[rotate_suc, ADD_CLAUSES]); + +(* Theorem: !k. k < LENGTH l ==> rotate (LENGTH l - k) (rotate k l) = l *) +(* Proof: + Since k < LENGTH l + LENGTH 1 - k + k = LENGTH l <= LENGTH l by EQ_LESS_EQ + rotate (LENGTH l - k) (rotate k l) + = rotate (LENGTH l - k + k) l by rotate_add + = rotate (LENGTH l) l by arithmetic + = l by rotate_full +*) +val rotate_lcancel = store_thm( + "rotate_lcancel", + ``!k l. k < LENGTH l ==> (rotate (LENGTH l - k) (rotate k l) = l)``, + rpt strip_tac >> + `LENGTH l - k + k = LENGTH l` by decide_tac >> + `LENGTH l <= LENGTH l` by rw[] >> + rw[rotate_add, rotate_full]); + +(* Theorem: !k. k < LENGTH l ==> rotate k (rotate (LENGTH l - k) l) = l *) +(* Proof: + Since k < LENGTH l + k + (LENGTH 1 - k) = LENGTH l <= LENGTH l by EQ_LESS_EQ + rotate k (rotate (LENGTH l - k) l) + = rotate (k + (LENGTH l - k)) l by rotate_add + = rotate (LENGTH l) l by arithmetic + = l by rotate_full +*) +val rotate_rcancel = store_thm( + "rotate_rcancel", + ``!k l. k < LENGTH l ==> (rotate k (rotate (LENGTH l - k) l) = l)``, + rpt strip_tac >> + `k + (LENGTH l - k) = LENGTH l` by decide_tac >> + `LENGTH l <= LENGTH l` by rw[] >> + rw[rotate_add, rotate_full]); + +(* ------------------------------------------------------------------------- *) +(* List Turn *) +(* ------------------------------------------------------------------------- *) + +(* Define a rotation turn of a list (like a turnstile) *) +val turn_def = Define` + turn l = if l = [] then [] else ((LAST l) :: (FRONT l)) +`; + +(* Theorem: turn [] = [] *) +(* Proof: by turn_def *) +val turn_nil = store_thm( + "turn_nil", + ``turn [] = []``, + rw[turn_def]); + +(* Theorem: l <> [] ==> (turn l = (LAST l) :: (FRONT l)) *) +(* Proof: by turn_def *) +val turn_not_nil = store_thm( + "turn_not_nil", + ``!l. l <> [] ==> (turn l = (LAST l) :: (FRONT l))``, + rw[turn_def]); + +(* Theorem: LENGTH (turn l) = LENGTH l *) +(* Proof: + If l = [], + LENGTH (turn []) = LENGTH [] by turn_def + If l <> [], + Then LENGTH l <> 0 by LENGTH_NIL + LENGTH (turn l) + = LENGTH ((LAST l) :: (FRONT l)) by turn_def + = SUC (LENGTH (FRONT l)) by LENGTH + = SUC (PRE (LENGTH l)) by LENGTH_FRONT + = LENGTH l by SUC_PRE, 0 < LENGTH l +*) +val turn_length = store_thm( + "turn_length", + ``!l. LENGTH (turn l) = LENGTH l``, + metis_tac[turn_def, list_CASES, LENGTH, LENGTH_FRONT_CONS, SUC_PRE, NOT_ZERO_LT_ZERO]); + +(* Theorem: (turn p = []) <=> (p = []) *) +(* Proof: + turn p = [] + <=> LENGTH (turn p) = 0 by LENGTH_NIL + <=> LENGTH p = 0 by turn_length + <=> p = [] by LENGTH_NIL +*) +val turn_eq_nil = store_thm( + "turn_eq_nil", + ``!p. (turn p = []) <=> (p = [])``, + metis_tac[turn_length, LENGTH_NIL]); + +(* Theorem: ls <> [] ==> (HD (turn ls) = LAST ls) *) +(* Proof: + HD (turn ls) + = HD (LAST ls :: FRONT ls) by turn_def, ls <> [] + = LAST ls by HD +*) +val head_turn = store_thm( + "head_turn", + ``!ls. ls <> [] ==> (HD (turn ls) = LAST ls)``, + rw[turn_def]); + +(* Theorem: ls <> [] ==> (TL (turn ls) = FRONT ls) *) +(* Proof: + TL (turn ls) + = TL (LAST ls :: FRONT ls) by turn_def, ls <> [] + = FRONT ls by TL +*) +Theorem tail_turn: + !ls. ls <> [] ==> (TL (turn ls) = FRONT ls) +Proof + rw[turn_def] +QED + +(* Theorem: turn (SNOC x ls) = x :: ls *) +(* Proof: + Note (SNOC x ls) <> [] by NOT_SNOC_NIL + turn (SNOC x ls) + = LAST (SNOC x ls) :: FRONT (SNOC x ls) by turn_def + = x :: FRONT (SNOC x ls) by LAST_SNOC + = x :: ls by FRONT_SNOC +*) +Theorem turn_snoc: + !ls x. turn (SNOC x ls) = x :: ls +Proof + metis_tac[NOT_SNOC_NIL, turn_def, LAST_SNOC, FRONT_SNOC] +QED + +(* Overload repeated turns *) +val _ = overload_on("turn_exp", ``\l n. FUNPOW turn n l``); + +(* Theorem: turn_exp l 0 = l *) +(* Proof: + turn_exp l 0 + = FUNPOW turn 0 l by notation + = l by FUNPOW +*) +val turn_exp_0 = store_thm( + "turn_exp_0", + ``!l. turn_exp l 0 = l``, + rw[]); + +(* Theorem: turn_exp l 1 = turn l *) +(* Proof: + turn_exp l 1 + = FUNPOW turn 1 l by notation + = turn l by FUNPOW +*) +val turn_exp_1 = store_thm( + "turn_exp_1", + ``!l. turn_exp l 1 = turn l``, + rw[]); + +(* Theorem: turn_exp l 2 = turn (turn l) *) +(* Proof: + turn_exp l 2 + = FUNPOW turn 2 l by notation + = turn (FUNPOW turn 1 l) by FUNPOW_SUC + = turn (turn_exp l 1) by notation + = turn (turn l) by turn_exp_1 +*) +val turn_exp_2 = store_thm( + "turn_exp_2", + ``!l. turn_exp l 2 = turn (turn l)``, + metis_tac[FUNPOW_SUC, turn_exp_1, TWO]); + +(* Theorem: turn_exp l (SUC n) = turn_exp (turn l) n *) +(* Proof: + turn_exp l (SUC n) + = FUNPOW turn (SUC n) l by notation + = FUNPOW turn n (turn l) by FUNPOW + = turn_exp (turn l) n by notation +*) +val turn_exp_SUC = store_thm( + "turn_exp_SUC", + ``!l n. turn_exp l (SUC n) = turn_exp (turn l) n``, + rw[FUNPOW]); + +(* Theorem: turn_exp l (SUC n) = turn (turn_exp l n) *) +(* Proof: + turn_exp l (SUC n) + = FUNPOW turn (SUC n) l by notation + = turn (FUNPOW turn n l) by FUNPOW_SUC + = turn (turn_exp l n) by notation +*) +val turn_exp_suc = store_thm( + "turn_exp_suc", + ``!l n. turn_exp l (SUC n) = turn (turn_exp l n)``, + rw[FUNPOW_SUC]); + +(* Theorem: LENGTH (turn_exp l n) = LENGTH l *) +(* Proof: + By induction on n. + Base: LENGTH (turn_exp l 0) = LENGTH l + True by turn_exp l 0 = l by turn_exp_0 + Step: LENGTH (turn_exp l n) = LENGTH l ==> LENGTH (turn_exp l (SUC n)) = LENGTH l + LENGTH (turn_exp l (SUC n)) + = LENGTH (turn (turn_exp l n)) by turn_exp_suc + = LENGTH (turn_exp l n) by turn_length + = LENGTH l by induction hypothesis +*) +val turn_exp_length = store_thm( + "turn_exp_length", + ``!l n. LENGTH (turn_exp l n) = LENGTH l``, + strip_tac >> + Induct >- + rw[] >> + rw[turn_exp_suc, turn_length]); + +(* Theorem: n < LENGTH ls ==> + (HD (turn_exp ls n) = EL (if n = 0 then 0 else LENGTH ls - n) ls) *) +(* Proof: + By induction on n. + Base: !ls. 0 < LENGTH ls ==> + HD (turn_exp ls 0) = EL 0 ls + HD (turn_exp ls 0) + = HD ls by FUNPOW_0 + = EL 0 ls by EL + Step: !ls. n < LENGTH ls ==> HD (turn_exp ls n) = EL (if n = 0 then 0 else (LENGTH ls - n)) ls ==> + !ls. SUC n < LENGTH ls ==> HD (turn_exp ls (SUC n)) = EL (LENGTH ls - SUC n) ls + Let k = LENGTH ls, then SUC n < k + Note LENGTH (FRONT ls) = PRE k by FRONT_LENGTH + and n < PRE k by SUC n < k + Also LENGTH (turn ls) = k by turn_length + so n < k by n < SUC n, SUC n < k + Note ls <> [] by k <> 0 + + HD (turn_exp ls (SUC n)) + = HD (turn_exp (turn ls) n) by turn_exp_SUC + = EL (if n = 0 then 0 else (LENGTH (turn ls) - n)) (turn ls) + by induction hypothesis, apply to (turn ls) + = EL (if n = 0 then 0 else (k - n) (turn ls)) by above + + If n = 0, + = EL 0 (turn ls) + = LAST ls by turn_def + = EL (PRE k) ls by LAST_EL + = EL (k - SUC 0) ls by ONE + If n <> 0 + = EL (k - n) (turn ls) + = EL (k - n) (LAST ls :: FRONT ls) by turn_def + = EL (k - n - 1) (FRONT ls) by EL + = EL (k - n - 1) ls by FRONT_EL, k - n - 1 < PRE k, n <> 0 + = EL (k - SUC n) ls by arithmetic +*) +val head_turn_exp = store_thm( + "head_turn_exp", + ``!ls n. n < LENGTH ls ==> + (HD (turn_exp ls n) = EL (if n = 0 then 0 else LENGTH ls - n) ls)``, + (Induct_on `n` >> simp[]) >> + rpt strip_tac >> + qabbrev_tac `k = LENGTH ls` >> + `n < k` by rw[Abbr`k`] >> + `LENGTH (turn ls) = k` by rw[turn_length, Abbr`k`] >> + `HD (turn_exp ls (SUC n)) = HD (turn_exp (turn ls) n)` by rw[turn_exp_SUC] >> + `_ = EL (if n = 0 then 0 else (k - n)) (turn ls)` by rw[] >> + `k <> 0` by decide_tac >> + `ls <> []` by metis_tac[LENGTH_NIL] >> + (Cases_on `n = 0` >> fs[]) >| [ + `PRE k = k - 1` by decide_tac >> + rw[head_turn, LAST_EL], + `k - n = SUC (k - SUC n)` by decide_tac >> + rw[turn_def, Abbr`k`] >> + `LENGTH (FRONT ls) = PRE (LENGTH ls)` by rw[FRONT_LENGTH] >> + `n < PRE (LENGTH ls)` by decide_tac >> + rw[FRONT_EL] + ]); + +(* ------------------------------------------------------------------------- *) +(* SUM Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Defined: SUM for summation of list = sequence *) + +(* Theorem: SUM [] = 0 *) +(* Proof: by definition. *) +val SUM_NIL = save_thm("SUM_NIL", SUM |> CONJUNCT1); +(* > val SUM_NIL = |- SUM [] = 0 : thm *) + +(* Theorem: SUM h::t = h + SUM t *) +(* Proof: by definition. *) +val SUM_CONS = save_thm("SUM_CONS", SUM |> CONJUNCT2); +(* val SUM_CONS = |- !h t. SUM (h::t) = h + SUM t: thm *) + +(* Theorem: SUM [n] = n *) +(* Proof: by SUM *) +val SUM_SING = store_thm( + "SUM_SING", + ``!n. SUM [n] = n``, + rw[]); + +(* Theorem: SUM (s ++ t) = SUM s + SUM t *) +(* Proof: by induction on s *) +(* +val SUM_APPEND = store_thm( + "SUM_APPEND", + ``!s t. SUM (s ++ t) = SUM s + SUM t``, + Induct_on `s` >- + rw[] >> + rw[ADD_ASSOC]); +*) +(* There is already a SUM_APPEND in up-to-date listTheory *) + +(* Theorem: constant multiplication: k * SUM s = SUM (k * s) *) +(* Proof: by induction on s. + Base case: !k. k * SUM [] = SUM (MAP ($* k) []) + LHS = k * SUM [] = k * 0 = 0 by SUM_NIL, MULT_0 + = SUM [] by SUM_NIL + = SUM (MAP ($* k) []) = RHS by MAP + Step case: !k. k * SUM s = SUM (MAP ($* k) s) ==> + !h k. k * SUM (h::s) = SUM (MAP ($* k) (h::s)) + LHS = k * SUM (h::s) + = k * (h + SUM s) by SUM_CONS + = k * h + k * SUM s by LEFT_ADD_DISTRIB + = k * h + SUM (MAP ($* k) s) by induction hypothesis + = SUM (k * h :: (MAP ($* k) s)) by SUM_CONS + = SUM (MAP ($* k) (h::s)) by MAP + = RHS +*) +val SUM_MULT = store_thm( + "SUM_MULT", + ``!s k. k * SUM s = SUM (MAP ($* k) s)``, + Induct_on `s` >- + metis_tac[SUM, MAP, MULT_0] >> + metis_tac[SUM, MAP, LEFT_ADD_DISTRIB]); + +(* Theorem: (m + n) * SUM s = SUM (m * s) + SUM (n * s) *) +(* Proof: generalization of +- RIGHT_ADD_DISTRIB; +> val it = |- !m n p. (m + n) * p = m * p + n * p : thm + (m + n) * SUM s + = m * SUM s + n * SUM s by RIGHT_ADD_DISTRIB + = SUM (MAP (\x. m * x) s) + SUM (MAP (\x. n * x) s) by SUM_MULT +*) +val SUM_RIGHT_ADD_DISTRIB = store_thm( + "SUM_RIGHT_ADD_DISTRIB", + ``!s m n. (m + n) * SUM s = SUM (MAP ($* m) s) + SUM (MAP ($* n) s)``, + metis_tac[RIGHT_ADD_DISTRIB, SUM_MULT]); + +(* Theorem: (SUM s) * (m + n) = SUM (m * s) + SUM (n * s) *) +(* Proof: generalization of +- LEFT_ADD_DISTRIB; +> val it = |- !m n p. p * (m + n) = p * m + p * n : thm + (SUM s) * (m + n) + = (m + n) * SUM s by MULT_COMM + = SUM (MAP ($* m) s) + SUM (MAP ($* n) s) by SUM_RIGHT_ADD_DISTRIB +*) +val SUM_LEFT_ADD_DISTRIB = store_thm( + "SUM_LEFT_ADD_DISTRIB", + ``!s m n. (SUM s) * (m + n) = SUM (MAP ($* m) s) + SUM (MAP ($* n) s)``, + metis_tac[SUM_RIGHT_ADD_DISTRIB, MULT_COMM]); + + +(* +- EVAL ``GENLIST I 4``; +> val it = |- GENLIST I 4 = [0; 1; 2; 3] : thm +- EVAL ``GENLIST SUC 4``; +> val it = |- GENLIST SUC 4 = [1; 2; 3; 4] : thm +- EVAL ``GENLIST (\k. binomial 4 k) 5``; +> val it = |- GENLIST (\k. binomial 4 k) 5 = [1; 4; 6; 4; 1] : thm +- EVAL ``GENLIST (\k. binomial 5 k) 6``; +> val it = |- GENLIST (\k. binomial 5 k) 6 = [1; 5; 10; 10; 5; 1] : thm +- EVAL ``GENLIST (\k. binomial 10 k) 11``; +> val it = |- GENLIST (\k. binomial 10 k) 11 = [1; 10; 45; 120; 210; 252; 210; 120; 45; 10; 1] : thm +*) + +(* Theorems on GENLIST: + +- GENLIST; +> val it = |- (!f. GENLIST f 0 = []) /\ + !f n. GENLIST f (SUC n) = SNOC (f n) (GENLIST f n) : thm +- NULL_GENLIST; +> val it = |- !n f. NULL (GENLIST f n) <=> (n = 0) : thm +- GENLIST_CONS; +> val it = |- GENLIST f (SUC n) = f 0::GENLIST (f o SUC) n : thm +- EL_GENLIST; +> val it = |- !f n x. x < n ==> (EL x (GENLIST f n) = f x) : thm +- EXISTS_GENLIST; +> val it = |- !n. EXISTS P (GENLIST f n) <=> ?i. i < n /\ P (f i) : thm +- EVERY_GENLIST; +> val it = |- !n. EVERY P (GENLIST f n) <=> !i. i < n ==> P (f i) : thm +- MAP_GENLIST; +> val it = |- !f g n. MAP f (GENLIST g n) = GENLIST (f o g) n : thm +- GENLIST_APPEND; +> val it = |- !f a b. GENLIST f (a + b) = GENLIST f b ++ GENLIST (\t. f (t + b)) a : thm +- HD_GENLIST; +> val it = |- HD (GENLIST f (SUC n)) = f 0 : thm +- TL_GENLIST; +> val it = |- !f n. TL (GENLIST f (SUC n)) = GENLIST (f o SUC) n : thm +- HD_GENLIST_COR; +> val it = |- !n f. 0 < n ==> (HD (GENLIST f n) = f 0) : thm +- GENLIST_FUN_EQ; +> val it = |- !n f g. (GENLIST f n = GENLIST g n) <=> !x. x < n ==> (f x = g x) : thm + +*) + +(* Theorem: SUM (GENLIST f n) = SIGMA f (count n) *) +(* Proof: + By induction on n. + Base: SUM (GENLIST f 0) = SIGMA f (count 0) + + SUM (GENLIST f 0) + = SUM [] by GENLIST_0 + = 0 by SUM_NIL + = SIGMA f {} by SUM_IMAGE_THM + = SIGMA f (count 0) by COUNT_0 + + Step: SUM (GENLIST f n) = SIGMA f (count n) ==> + SUM (GENLIST f (SUC n)) = SIGMA f (count (SUC n)) + + SUM (GENLIST f (SUC n)) + = SUM (SNOC (f n) (GENLIST f n)) by GENLIST + = f n + SUM (GENLIST f n) by SUM_SNOC + = f n + SIGMA f (count n) by induction hypothesis + = f n + SIGMA f (count n DELETE n) by IN_COUNT, DELETE_NON_ELEMENT + = SIGMA f (n INSERT count n) by SUM_IMAGE_THM, FINITE_COUNT + = SIGMA f (count (SUC n)) by COUNT_SUC +*) +val SUM_GENLIST = store_thm( + "SUM_GENLIST", + ``!f n. SUM (GENLIST f n) = SIGMA f (count n)``, + strip_tac >> + Induct >- + rw[SUM_IMAGE_THM] >> + `SUM (GENLIST f (SUC n)) = SUM (SNOC (f n) (GENLIST f n))` by rw[GENLIST] >> + `_ = f n + SUM (GENLIST f n)` by rw[SUM_SNOC] >> + `_ = f n + SIGMA f (count n)` by rw[] >> + `_ = f n + SIGMA f (count n DELETE n)` + by metis_tac[IN_COUNT, prim_recTheory.LESS_REFL, DELETE_NON_ELEMENT] >> + `_ = SIGMA f (n INSERT count n)` by rw[SUM_IMAGE_THM] >> + `_ = SIGMA f (count (SUC n))` by rw[COUNT_SUC] >> + decide_tac); + +(* Theorem: SUM (k=0..n) f(k) = f(0) + SUM (k=1..n) f(k) *) +(* Proof: + SUM (GENLIST f (SUC n)) + = SUM (f 0 :: GENLIST (f o SUC) n) by GENLIST_CONS + = f 0 + SUM (GENLIST (f o SUC) n) by SUM definition. +*) +val SUM_DECOMPOSE_FIRST = store_thm( + "SUM_DECOMPOSE_FIRST", + ``!f n. SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) n)``, + metis_tac[GENLIST_CONS, SUM]); + +(* Theorem: SUM (k=0..n) f(k) = SUM (k=0..(n-1)) f(k) + f n *) +(* Proof: + SUM (GENLIST f (SUC n)) + = SUM (SNOC (f n) (GENLIST f n)) by GENLIST definition + = SUM ((GENLIST f n) ++ [f n]) by SNOC_APPEND + = SUM (GENLIST f n) + SUM [f n] by SUM_APPEND + = SUM (GENLIST f n) + f n by SUM definition: SUM (h::t) = h + SUM t, and SUM [] = 0. +*) +val SUM_DECOMPOSE_LAST = store_thm( + "SUM_DECOMPOSE_LAST", + ``!f n. SUM (GENLIST f (SUC n)) = SUM (GENLIST f n) + f n``, + rpt strip_tac >> + `SUM (GENLIST f (SUC n)) = SUM (SNOC (f n) (GENLIST f n))` by metis_tac[GENLIST] >> + `_ = SUM ((GENLIST f n) ++ [f n])` by metis_tac[SNOC_APPEND] >> + `_ = SUM (GENLIST f n) + SUM [f n]` by metis_tac[SUM_APPEND] >> + rw[SUM]); + +(* Theorem: SUM (GENLIST a n) + SUM (GENLIST b n) = SUM (GENLIST (\k. a k + b k) n) *) +(* Proof: by induction on n. + Base case: !a b. SUM (GENLIST a 0) + SUM (GENLIST b 0) = SUM (GENLIST (\k. a k + b k) 0) + Since GENLIST f 0 = [] by GENLIST + and SUM [] = 0 by SUM_NIL + This is just 0 + 0 = 0, true by arithmetic. + Step case: !a b. SUM (GENLIST a n) + SUM (GENLIST b n) = + SUM (GENLIST (\k. a k + b k) n) ==> + !a b. SUM (GENLIST a (SUC n)) + SUM (GENLIST b (SUC n)) = + SUM (GENLIST (\k. a k + b k) (SUC n)) + SUM (GENLIST a (SUC n)) + SUM (GENLIST b (SUC n) + = (SUM (GENLIST a n) + a n) + (SUM (GENLIST b n) + b n) by SUM_DECOMPOSE_LAST + = SUM (GENLIST a n) + SUM (GENLIST b n) + (a n + b n) by arithmetic + = SUM (GENLIST (\k. a k + b k) n) + (a n + b n) by induction hypothesis + = SUM (GENLIST (\k. a k + b k) (SUC n)) by SUM_DECOMPOSE_LAST +*) +val SUM_ADD_GENLIST = store_thm( + "SUM_ADD_GENLIST", + ``!a b n. SUM (GENLIST a n) + SUM (GENLIST b n) = SUM (GENLIST (\k. a k + b k) n)``, + Induct_on `n` >- + rw[] >> + rw[SUM_DECOMPOSE_LAST]); + +(* Theorem: SUM (GENLIST a n ++ GENLIST b n) = SUM (GENLIST (\k. a k + b k) n) *) +(* Proof: + SUM (GENLIST a n ++ GENLIST b n) + = SUM (GENLIST a n) + SUM (GENLIST b n) by SUM_APPEND + = SUM (GENLIST (\k. a k + b k) n) by SUM_ADD_GENLIST +*) +val SUM_GENLIST_APPEND = store_thm( + "SUM_GENLIST_APPEND", + ``!a b n. SUM (GENLIST a n ++ GENLIST b n) = SUM (GENLIST (\k. a k + b k) n)``, + metis_tac[SUM_APPEND, SUM_ADD_GENLIST]); + +(* Theorem: 0 < n ==> SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n *) +(* Proof: + SUM (GENLIST f (SUC n)) + = SUM (GENLIST f n) + f n by SUM_DECOMPOSE_LAST + = SUM (GENLIST f (SUC m)) + f n by n = SUC m, 0 < n + = f 0 + SUM (GENLIST (f o SUC) m) + f n by SUM_DECOMPOSE_FIRST + = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n by PRE_SUC_EQ +*) +val SUM_DECOMPOSE_FIRST_LAST = store_thm( + "SUM_DECOMPOSE_FIRST_LAST", + ``!f n. 0 < n ==> (SUM (GENLIST f (SUC n)) = f 0 + SUM (GENLIST (f o SUC) (PRE n)) + f n)``, + metis_tac[SUM_DECOMPOSE_LAST, SUM_DECOMPOSE_FIRST, SUC_EXISTS, PRE_SUC_EQ]); + +(* Theorem: (SUM l) MOD n = (SUM (MAP (\x. x MOD n) l)) MOD n *) +(* Proof: by list induction. + Base case: SUM [] MOD n = SUM (MAP (\x. x MOD n) []) MOD n + true by SUM [] = 0, MAP f [] = 0, and 0 MOD n = 0. + Step case: SUM l MOD n = SUM (MAP (\x. x MOD n) l) MOD n ==> + !h. SUM (h::l) MOD n = SUM (MAP (\x. x MOD n) (h::l)) MOD n + SUM (h::l) MOD n + = (h + SUM l) MOD n by SUM + = (h MOD n + (SUM l) MOD n) MOD n by MOD_PLUS + = (h MOD n + SUM (MAP (\x. x MOD n) l) MOD n) MOD n by induction hypothesis + = ((h MOD n) MOD n + SUM (MAP (\x. x MOD n) l) MOD n) MOD n by MOD_MOD + = ((h MOD n + SUM (MAP (\x. x MOD n) l)) MOD n) MOD n by MOD_PLUS + = (h MOD n + SUM (MAP (\x. x MOD n) l)) MOD n by MOD_MOD + = (SUM (h MOD n ::(MAP (\x. x MOD n) l))) MOD n by SUM + = (SUM (MAP (\x. x MOD n) (h::l))) MOD n by MAP +*) +val SUM_MOD = store_thm( + "SUM_MOD", + ``!n. 0 < n ==> !l. (SUM l) MOD n = (SUM (MAP (\x. x MOD n) l)) MOD n``, + rpt strip_tac >> + Induct_on `l` >- + rw[] >> + rpt strip_tac >> + `SUM (h::l) MOD n = (h MOD n + (SUM l) MOD n) MOD n` by rw_tac std_ss[SUM, MOD_PLUS] >> + `_ = ((h MOD n) MOD n + SUM (MAP (\x. x MOD n) l) MOD n) MOD n` by rw_tac std_ss[MOD_MOD] >> + rw[MOD_PLUS]); + +(* Theorem: SUM l = 0 <=> l = EVERY (\x. x = 0) l *) +(* Proof: by induction on l. + Base case: (SUM [] = 0) <=> EVERY (\x. x = 0) [] + true by SUM [] = 0 and GENLIST f 0 = []. + Step case: (SUM l = 0) <=> EVERY (\x. x = 0) l ==> + !h. (SUM (h::l) = 0) <=> EVERY (\x. x = 0) (h::l) + SUM (h::l) = 0 + <=> h + SUM l = 0 by SUM + <=> h = 0 /\ SUM l = 0 by ADD_EQ_0 + <=> h = 0 /\ EVERY (\x. x = 0) l by induction hypothesis + <=> EVERY (\x. x = 0) (h::l) by EVERY_DEF +*) +val SUM_EQ_0 = store_thm( + "SUM_EQ_0", + ``!l. (SUM l = 0) <=> EVERY (\x. x = 0) l``, + Induct >> + rw[]); + +(* Theorem: SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = + SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n *) +(* Proof: + SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n + = SUM (MAP (\x. x MOD n) (GENLIST ((\k. f k) o SUC) (PRE n))) MOD n by SUM_MOD + = SUM (GENLIST ((\x. x MOD n) o ((\k. f k) o SUC)) (PRE n)) MOD n by MAP_GENLIST + = SUM (GENLIST ((\x. x MOD n) o (\k. f k) o SUC) (PRE n)) MOD n by composition associative + = SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n by composition +*) +val SUM_GENLIST_MOD = store_thm( + "SUM_GENLIST_MOD", + ``!n. 0 < n ==> !f. SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = SUM (GENLIST ((\k. f k MOD n) o SUC) (PRE n)) MOD n``, + rpt strip_tac >> + `SUM (GENLIST ((\k. f k) o SUC) (PRE n)) MOD n = + SUM (MAP (\x. x MOD n) (GENLIST ((\k. f k) o SUC) (PRE n))) MOD n` by metis_tac[SUM_MOD] >> + rw_tac std_ss[MAP_GENLIST, combinTheory.o_ASSOC, combinTheory.o_ABS_L]); + +(* Theorem: SUM (GENLIST (\j. x) n) = n * x *) +(* Proof: + By induction on n. + Base case: !x. SUM (GENLIST (\j. x) 0) = 0 * x + SUM (GENLIST (\j. x) 0) + = SUM [] by GENLIST + = 0 by SUM + = 0 * x by MULT + Step case: !x. SUM (GENLIST (\j. x) n) = n * x ==> + !x. SUM (GENLIST (\j. x) (SUC n)) = SUC n * x + SUM (GENLIST (\j. x) (SUC n)) + = SUM (SNOC x (GENLIST (\j. x) n)) by GENLIST + = SUM (GENLIST (\j. x) n) + x by SUM_SNOC + = n * x + x by induction hypothesis + = SUC n * x by MULT +*) +val SUM_CONSTANT = store_thm( + "SUM_CONSTANT", + ``!n x. SUM (GENLIST (\j. x) n) = n * x``, + Induct >- + rw[] >> + rw_tac std_ss[GENLIST, SUM_SNOC, MULT]); + +(* Theorem: SUM (GENLIST (K m) n) = m * n *) +(* Proof: + By induction on n. + Base: SUM (GENLIST (K m) 0) = m * 0 + SUM (GENLIST (K m) 0) + = SUM [] by GENLIST + = 0 by SUM + = m * 0 by MULT_0 + Step: SUM (GENLIST (K m) n) = m * n ==> SUM (GENLIST (K m) (SUC n)) = m * SUC n + SUM (GENLIST (K m) (SUC n)) + = SUM (SNOC m (GENLIST (K m) n)) by GENLIST + = SUM (GENLIST (K m) n) + m by SUM_SNOC + = m * n + m by induction hypothesis + = m + m * n by ADD_COMM + = m * SUC n by MULT_SUC +*) +val SUM_GENLIST_K = store_thm( + "SUM_GENLIST_K", + ``!m n. SUM (GENLIST (K m) n) = m * n``, + strip_tac >> + Induct >- + rw[] >> + rw[GENLIST, SUM_SNOC, MULT_SUC]); + +(* Theorem: (LENGTH l1 = LENGTH l2) /\ (!k. k <= LENGTH l1 ==> EL k l1 <= EL k l2) ==> SUM l1 <= SUM l2 *) +(* Proof: + By induction on l1. + Base: LENGTH [] = LENGTH l2 ==> SUM [] <= SUM l2 + Note l2 = [] by LENGTH_EQ_0 + so SUM [] = SUM [] + or SUM [] <= SUM l2 by EQ_LESS_EQ + Step: !l2. (LENGTH l1 = LENGTH l2) /\ ... ==> SUM l1 <= SUM l2 ==> + (LENGTH (h::l1) = LENGTH l2) /\ ... ==> SUM h::l1 <= SUM l2 + Note l2 <> [] by LENGTH_EQ_0 + so ?h1 t2. l2 = h1::t1 by list_CASES + and LENGTH l1 = LENGTH t1 by LENGTH + SUM (h::l1) + = h + SUM l1 by SUM_CONS + <= h1 + SUM t1 by EL_ALL_PROPERTY, induction hypothesis + = SUM l2 by SUM_CONS +*) +val SUM_LE = store_thm( + "SUM_LE", + ``!l1 l2. (LENGTH l1 = LENGTH l2) /\ (!k. k < LENGTH l1 ==> EL k l1 <= EL k l2) ==> + SUM l1 <= SUM l2``, + Induct >- + metis_tac[LENGTH_EQ_0, EQ_LESS_EQ] >> + rpt strip_tac >> + `?h1 t1. l2 = h1::t1` by metis_tac[LENGTH_EQ_0, list_CASES] >> + `LENGTH l1 = LENGTH t1` by metis_tac[LENGTH, SUC_EQ] >> + `SUM (h::l1) = h + SUM l1` by rw[SUM_CONS] >> + `SUM l2 = h1 + SUM t1` by rw[SUM_CONS] >> + `(h <= h1) /\ SUM l1 <= SUM t1` by metis_tac[EL_ALL_PROPERTY] >> + decide_tac); + +(* Theorem: MEM x l ==> x <= SUM l *) +(* Proof: + By induction on l. + Base: !x. MEM x [] ==> x <= SUM [] + True since MEM x [] = F by MEM + Step: !x. MEM x l ==> x <= SUM l ==> !h x. MEM x (h::l) ==> x <= SUM (h::l) + If x = h, + Then h <= h + SUM l = SUM (h::l) by SUM + If x <> h, + Then MEM x l by MEM + ==> x <= SUM l by induction hypothesis + or x <= h + SUM l = SUM (h::l) by SUM +*) +val SUM_LE_MEM = store_thm( + "SUM_LE_MEM", + ``!l x. MEM x l ==> x <= SUM l``, + Induct >- + rw[] >> + rw[] >- + decide_tac >> + `x <= SUM l` by rw[] >> + decide_tac); + +(* Theorem: n < LENGTH l ==> (EL n l) <= SUM l *) +(* Proof: by SUM_LE_MEM, MEM_EL *) +val SUM_LE_EL = store_thm( + "SUM_LE_EL", + ``!l n. n < LENGTH l ==> (EL n l) <= SUM l``, + metis_tac[SUM_LE_MEM, MEM_EL]); + +(* Theorem: m < n /\ n < LENGTH l ==> (EL m l) + (EL n l) <= SUM l *) +(* Proof: + By induction on l. + Base: !m n. m < n /\ n < LENGTH [] ==> EL m [] + EL n [] <= SUM [] + True since n < LENGTH [] = F by LENGTH + Step: !m n. m < LENGTH l /\ n < LENGTH l ==> EL m l + EL n l <= SUM l ==> + !h m n. m < LENGTH (h::l) /\ n < LENGTH (h::l) ==> EL m (h::l) + EL n (h::l) <= SUM (h::l) + Note 0 < n, or n <> 0 by m < n + so ?k. n = SUC k by num_CASES + and k < LENGTH l by SUC k < SUC (LENGTH l) + and EL n (h::l) = EL k l by EL_restricted + If m = 0, + Then EL m (h::l) = h by EL_restricted + and EL k l <= SUM l by SUM_LE_EL + Thus EL m (h::l) + EL n (h::l) + = h + SUM l + = SUM (h::l) by SUM + If m <> 0, + Then ?j. m = SUC j by num_CASES + and j < k by SUC j < SUC k + and EL m (h::l) = EL j l by EL_restricted + Thus EL m (h::l) + EL n (h::l) + = EL j l + EL k l by above + <= SUM l by induction hypothesis + <= h + SUM l by arithmetic + = SUM (h::l) by SUM +*) +val SUM_LE_SUM_EL = store_thm( + "SUM_LE_SUM_EL", + ``!l m n. m < n /\ n < LENGTH l ==> (EL m l) + (EL n l) <= SUM l``, + Induct >- + rw[] >> + rw[] >> + `n <> 0` by decide_tac >> + `?k. n = SUC k` by metis_tac[num_CASES] >> + `k < LENGTH l` by decide_tac >> + `EL n (h::l) = EL k l` by rw[] >> + Cases_on `m = 0` >| [ + `EL m (h::l) = h` by rw[] >> + `EL k l <= SUM l` by rw[SUM_LE_EL] >> + decide_tac, + `?j. m = SUC j` by metis_tac[num_CASES] >> + `j < k` by decide_tac >> + `EL m (h::l) = EL j l` by rw[] >> + `EL j l + EL k l <= SUM l` by rw[] >> + decide_tac + ]); + +(* Theorem: SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1) *) +(* Proof: + The computation is: + n + (n * 2) + (n * 4) + ... + (n * (2 ** (m - 1))) + = n * (1 + 2 + 4 + ... + 2 ** (m - 1)) + = n * (2 ** m - 1) + + By induction on m. + Base: SUM (GENLIST (\j. n * 2 ** j) 0) = n * (2 ** 0 - 1) + LHS = SUM (GENLIST (\j. n * 2 ** j) 0) + = SUM [] by GENLIST_0 + = 0 by PROD + RHS = n * (1 - 1) by EXP_0 + = n * 0 = 0 = LHS by MULT_0 + Step: SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1) ==> + SUM (GENLIST (\j. n * 2 ** j) (SUC m)) = n * (2 ** SUC m - 1) + SUM (GENLIST (\j. n * 2 ** j) (SUC m)) + = SUM (SNOC (n * 2 ** m) (GENLIST (\j. n * 2 ** j) m)) by GENLIST + = SUM (GENLIST (\j. n * 2 ** j) m) + (n * 2 ** m) by SUM_SNOC + = n * (2 ** m - 1) + n * 2 ** m by induction hypothesis + = n * (2 ** m - 1 + 2 ** m) by LEFT_ADD_DISTRIB + = n * (2 * 2 ** m - 1) by arithmetic + = n * (2 ** SUC m - 1) by EXP +*) +val SUM_DOUBLING_LIST = store_thm( + "SUM_DOUBLING_LIST", + ``!m n. SUM (GENLIST (\j. n * 2 ** j) m) = n * (2 ** m - 1)``, + rpt strip_tac >> + Induct_on `m` >- + rw[] >> + qabbrev_tac `f = \j. n * 2 ** j` >> + `SUM (GENLIST f (SUC m)) = SUM (SNOC (n * 2 ** m) (GENLIST f m))` by rw[GENLIST, Abbr`f`] >> + `_ = SUM (GENLIST f m) + (n * 2 ** m)` by rw[SUM_SNOC] >> + `_ = n * (2 ** m - 1) + n * 2 ** m` by rw[] >> + `_ = n * (2 ** m - 1 + 2 ** m)` by rw[LEFT_ADD_DISTRIB] >> + rw[EXP]); + + +(* Idea: key theorem, almost like pigeonhole principle. *) + +(* List equivalent sum theorems. This is an example of digging out theorems. *) + +(* Theorem: EVERY (\x. 0 < x) ls ==> LENGTH ls <= SUM ls *) +(* Proof: + Let P = (\x. 0 < x). + By induction on list ls. + Base: EVERY P [] ==> LENGTH [] <= SUM [] + Note EVERY P [] = T by EVERY_DEF + and LENGTH [] = 0 by LENGTH + and SUM [] = 0 by SUM + Hence true. + Step: EVERY P ls ==> LENGTH ls <= SUM ls ==> + !h. EVERY P (h::ls) ==> LENGTH (h::ls) <= SUM (h::ls) + Note 0 < h /\ EVERY P ls by EVERY_DEF + LENGTH (h::ls) + = 1 + LENGTH ls by LENGTH + <= 1 + SUM ls by induction hypothesis + <= h + SUM ls by 0 < h + = SUM (h::ls) by SUM +*) +Theorem list_length_le_sum: + !ls. EVERY (\x. 0 < x) ls ==> LENGTH ls <= SUM ls +Proof + Induct >- + rw[] >> + rw[] >> + `1 <= h` by decide_tac >> + fs[] +QED + +(* Theorem: EVERY (\x. 0 < x) ls /\ LENGTH ls = SUM ls ==> EVERY (\x. x = 1) ls *) +(* Proof: + Let P = (\x. 0 < x), Q = (\x. x = 1). + By induction on list ls. + Base: EVERY P [] /\ LENGTH [] = SUM [] ==> EVERY Q [] + Note EVERY Q [] = T by EVERY_DEF + Hence true. + Step: EVERY P ls /\ LENGTH ls = SUM ls ==> EVERY Q ls ==> + !h. EVERY P (h::ls) /\ LENGTH (h::ls) = SUM (h::ls) ==> EVERY Q (h::ls) + Note 0 < h /\ EVERY P ls by EVERY_DEF + LHS = LENGTH (h::ls) + = 1 + LENGTH ls by LENGTH + <= 1 + SUM ls by list_length_le_sum + RHS = SUM (h::ls) + = h + SUM ls by SUM + Thus h + SUM ls <= 1 + SUM ls + or h <= 1 by arithmetic + giving h = 1 by 0 < h + Thus LENGTH ls = SUM ls by arithmetic + and EVERY Q ls by induction hypothesis + or EVERY Q (h::ls) by EVERY_DEF, h = 1 +*) +Theorem list_length_eq_sum: + !ls. EVERY (\x. 0 < x) ls /\ LENGTH ls = SUM ls ==> EVERY (\x. x = 1) ls +Proof + Induct >- + rw[] >> + rpt strip_tac >> + fs[] >> + `LENGTH ls <= SUM ls` by rw[list_length_le_sum] >> + `h + LENGTH ls <= SUC (LENGTH ls)` by fs[] >> + `h = 1` by decide_tac >> + `SUM ls = LENGTH ls` by fs[] >> + simp[] +QED + +(* Theorem: (!x y. x <= y ==> f x <= f y) ==> + !ls. ls <> [] ==> (MAX_LIST (MAP f ls) = f (MAX_LIST ls)) *) +(* Proof: + By induction on ls. + Base: [] <> [] ==> MAX_LIST (MAP f []) = f (MAX_LIST []) + True by [] <> [] = F. + Step: ls <> [] ==> MAX_LIST (MAP f ls) = f (MAX_LIST ls) ==> + !h. h::ls <> [] ==> MAX_LIST (MAP f (h::ls)) = f (MAX_LIST (h::ls)) + If ls = [], + MAX_LIST (MAP f [h]) + = MAX_LIST [f h] by MAP + = f h by MAX_LIST_def + = f (MAX_LIST [h]) by MAX_LIST_def + If ls <> [], + MAX_LIST (MAP f (h::ls)) + = MAX_LIST (f h::MAP f ls) by MAP + = MAX (f h) MAX_LIST (MAP f ls) by MAX_LIST_def + = MAX (f h) (f (MAX_LIST ls)) by induction hypothesis + = f (MAX h (MAX_LIST ls)) by MAX_SWAP + = f (MAX_LIST (h::ls)) by MAX_LIST_def +*) +val MAX_LIST_MONO_MAP = store_thm( + "MAX_LIST_MONO_MAP", + ``!f. (!x y. x <= y ==> f x <= f y) ==> + !ls. ls <> [] ==> (MAX_LIST (MAP f ls) = f (MAX_LIST ls))``, + rpt strip_tac >> + Induct_on `ls` >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + rw[MAX_SWAP]); + +(* Theorem: (!x y. x <= y ==> f x <= f y) ==> + !ls. ls <> [] ==> (MIN_LIST (MAP f ls) = f (MIN_LIST ls)) *) +(* Proof: + By induction on ls. + Base: [] <> [] ==> MIN_LIST (MAP f []) = f (MIN_LIST []) + True by [] <> [] = F. + Step: ls <> [] ==> MIN_LIST (MAP f ls) = f (MIN_LIST ls) ==> + !h. h::ls <> [] ==> MIN_LIST (MAP f (h::ls)) = f (MIN_LIST (h::ls)) + If ls = [], + MIN_LIST (MAP f [h]) + = MIN_LIST [f h] by MAP + = f h by MIN_LIST_def + = f (MIN_LIST [h]) by MIN_LIST_def + If ls <> [], + MIN_LIST (MAP f (h::ls)) + = MIN_LIST (f h::MAP f ls) by MAP + = MIN (f h) MIN_LIST (MAP f ls) by MIN_LIST_def + = MIN (f h) (f (MIN_LIST ls)) by induction hypothesis + = f (MIN h (MIN_LIST ls)) by MIN_SWAP + = f (MIN_LIST (h::ls)) by MIN_LIST_def +*) +val MIN_LIST_MONO_MAP = store_thm( + "MIN_LIST_MONO_MAP", + ``!f. (!x y. x <= y ==> f x <= f y) ==> + !ls. ls <> [] ==> (MIN_LIST (MAP f ls) = f (MIN_LIST ls))``, + rpt strip_tac >> + Induct_on `ls` >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + rw[MIN_SWAP]); + +(* ------------------------------------------------------------------------- *) +(* List Nub and Set *) +(* ------------------------------------------------------------------------- *) + +(* Note: +> nub_def; +|- (nub [] = []) /\ !x l. nub (x::l) = if MEM x l then nub l else x::nub l +*) + +(* Theorem: nub [] = [] *) +(* Proof: by nub_def *) +val nub_nil = save_thm("nub_nil", nub_def |> CONJUNCT1); +(* val nub_nil = |- nub [] = []: thm *) + +(* Theorem: nub (x::l) = if MEM x l then nub l else x::nub l *) +(* Proof: by nub_def *) +val nub_cons = save_thm("nub_cons", nub_def |> CONJUNCT2); +(* val nub_cons = |- !x l. nub (x::l) = if MEM x l then nub l else x::nub l: thm *) + +(* Theorem: nub [x] = [x] *) +(* Proof: + nub [x] + = nub (x::[]) by notation + = x :: nub [] by nub_cons, MEM x [] = F + = x ::[] by nub_nil + = [x] by notation +*) +val nub_sing = store_thm( + "nub_sing", + ``!x. nub [x] = [x]``, + rw[nub_def]); + +(* Theorem: ALL_DISTINCT (nub l) *) +(* Proof: + By induction on l. + Base: ALL_DISTINCT (nub []) + ALL_DISTINCT (nub []) + <=> ALL_DISTINCT [] by nub_nil + <=> T by ALL_DISTINCT + Step: ALL_DISTINCT (nub l) ==> !h. ALL_DISTINCT (nub (h::l)) + If MEM h l, + Then nub (h::l) = nub l by nub_cons + Thus ALL_DISTINCT (nub l) by induction hypothesis + ==> ALL_DISTINCT (nub (h::l)) + If ~(MEM h l), + Then nub (h::l) = h:nub l by nub_cons + With ALL_DISTINCT (nub l) by induction hypothesis + ==> ALL_DISTINCT (h::nub l) by ALL_DISTINCT, ~(MEM h l) + or ALL_DISTINCT (nub (h::l)) +*) +val nub_all_distinct = store_thm( + "nub_all_distinct", + ``!l. ALL_DISTINCT (nub l)``, + Induct >- + rw[nub_nil] >> + rw[nub_cons]); + +(* Theorem: CARD (set l) = LENGTH (nub l) *) +(* Proof: + Note set (nub l) = set l by nub_set + and ALL_DISTINCT (nub l) by nub_all_distinct + CARD (set l) + = CARD (set (nub l)) by above + = LENGTH (nub l) by ALL_DISTINCT_CARD_LIST_TO_SET, ALL_DISTINCT (nub l) +*) +val CARD_LIST_TO_SET_EQ = store_thm( + "CARD_LIST_TO_SET_EQ", + ``!l. CARD (set l) = LENGTH (nub l)``, + rpt strip_tac >> + `set (nub l) = set l` by rw[nub_set] >> + `ALL_DISTINCT (nub l)` by rw[nub_all_distinct] >> + rw[GSYM ALL_DISTINCT_CARD_LIST_TO_SET]); + +(* Theorem: set [x] = {x} *) +(* Proof: + set [x] + = x INSERT set [] by LIST_TO_SET + = x INSERT {} by LIST_TO_SET + = {x} by INSERT_DEF +*) +val MONO_LIST_TO_SET = store_thm( + "MONO_LIST_TO_SET", + ``!x. set [x] = {x}``, + rw[]); + +(* Theorem: ~(MEM h l1) /\ (set (h::l1) = set l2) ==> + ?p1 p2. ~(MEM h p1) /\ ~(MEM h p2) /\ (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2)) *) +(* Proof: + Note MEM h (h::l1) by MEM + or h IN set (h::l1) by notation + so h IN set l2 by given + or h IN set (nub l2) by nub_set + so MEM h (nub l2) by notation + or ?p1 p2. nub l2 = p1 ++ [h] ++ h2 + and ~(MEM h p1) /\ ~(MEM h p2) by MEM_SPLIT_APPEND_distinct + Remaining goal: set l1 = set (p1 ++ p2) + + Step 1: show set l1 SUBSET set (p1 ++ p2) + Let x IN set l1. + Then MEM x l1 ==> MEM x (h::l1) by MEM + so x IN set (h::l1) + or x IN set l2 by given + or x IN set (nub l2) by nub_set + or MEM x (nub l2) by notation + But h <> x since MEM x l1 but ~MEM h l1 + so MEM x (p1 ++ p2) by MEM, MEM_APPEND + or x IN set (p1 ++ p2) by notation + Thus l1 SUBSET set (p1 ++ p2) by SUBSET_DEF + + Step 2: show set (p1 ++ p2) SUBSET set l1 + Let x IN set (p1 ++ p2) + or MEM x (p1 ++ p2) by notation + so MEM x (nub l2) by MEM, MEM_APPEND + or x IN set (nub l2) by notation + ==> x IN set l2 by nub_set + or x IN set (h::l1) by given + or MEM x (h::l1) by notation + But x <> h by MEM_APPEND, MEM x (p1 ++ p2) but ~(MEM h p1) /\ ~(MEM h p2) + ==> MEM x l1 by MEM + or x IN set l1 by notation + Thus set (p1 ++ p2) SUBSET set l1 by SUBSET_DEF + + Thus set l1 = set (p1 ++ p2) by SUBSET_ANTISYM +*) +val LIST_TO_SET_REDUCTION = store_thm( + "LIST_TO_SET_REDUCTION", + ``!l1 l2 h. ~(MEM h l1) /\ (set (h::l1) = set l2) ==> + ?p1 p2. ~(MEM h p1) /\ ~(MEM h p2) /\ (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2))``, + rpt strip_tac >> + `MEM h (nub l2)` by metis_tac[MEM, nub_set] >> + qabbrev_tac `l = nub l2` >> + `?n. n < LENGTH l /\ (h = EL n l)` by rw[GSYM MEM_EL] >> + `ALL_DISTINCT l` by rw[nub_all_distinct, Abbr`l`] >> + `?p1 p2. (l = p1 ++ [h] ++ p2) /\ ~MEM h p1 /\ ~MEM h p2` by rw[GSYM MEM_SPLIT_APPEND_distinct] >> + qexists_tac `p1` >> + qexists_tac `p2` >> + rpt strip_tac >- + rw[] >> + `set l1 SUBSET set (p1 ++ p2) /\ set (p1 ++ p2) SUBSET set l1` suffices_by metis_tac[SUBSET_ANTISYM] >> + rewrite_tac[SUBSET_DEF] >> + rpt strip_tac >- + metis_tac[MEM_APPEND, MEM, nub_set] >> + metis_tac[MEM_APPEND, MEM, nub_set]); + +(* ------------------------------------------------------------------------- *) +(* List Padding *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: PAD_LEFT c n [] = GENLIST (K c) n *) +(* Proof: by PAD_LEFT *) +val PAD_LEFT_NIL = store_thm( + "PAD_LEFT_NIL", + ``!n c. PAD_LEFT c n [] = GENLIST (K c) n``, + rw[PAD_LEFT]); + +(* Theorem: PAD_RIGHT c n [] = GENLIST (K c) n *) +(* Proof: by PAD_RIGHT *) +val PAD_RIGHT_NIL = store_thm( + "PAD_RIGHT_NIL", + ``!n c. PAD_RIGHT c n [] = GENLIST (K c) n``, + rw[PAD_RIGHT]); + +(* Theorem: LENGTH (PAD_LEFT c n s) = MAX n (LENGTH s) *) +(* Proof: + LENGTH (PAD_LEFT c n s) + = LENGTH (GENLIST (K c) (n - LENGTH s) ++ s) by PAD_LEFT + = LENGTH (GENLIST (K c) (n - LENGTH s)) + LENGTH s by LENGTH_APPEND + = n - LENGTH s + LENGTH s by LENGTH_GENLIST + = MAX n (LENGTH s) by MAX_DEF +*) +val PAD_LEFT_LENGTH = store_thm( + "PAD_LEFT_LENGTH", + ``!n c s. LENGTH (PAD_LEFT c n s) = MAX n (LENGTH s)``, + rw[PAD_LEFT, MAX_DEF]); + +(* Theorem: LENGTH (PAD_RIGHT c n s) = MAX n (LENGTH s) *) +(* Proof: + LENGTH (PAD_LEFT c n s) + = LENGTH (s ++ GENLIST (K c) (n - LENGTH s)) by PAD_RIGHT + = LENGTH s + LENGTH (GENLIST (K c) (n - LENGTH s)) by LENGTH_APPEND + = LENGTH s + (n - LENGTH s) by LENGTH_GENLIST + = MAX n (LENGTH s) by MAX_DEF +*) +val PAD_RIGHT_LENGTH = store_thm( + "PAD_RIGHT_LENGTH", + ``!n c s. LENGTH (PAD_RIGHT c n s) = MAX n (LENGTH s)``, + rw[PAD_RIGHT, MAX_DEF]); + +(* Theorem: n <= LENGTH l ==> (PAD_LEFT c n l = l) *) +(* Proof: + Note n - LENGTH l = 0 by n <= LENGTH l + PAD_LEFT c (LENGTH l) l + = GENLIST (K c) 0 ++ l by PAD_LEFT + = [] ++ l by GENLIST + = l by APPEND +*) +val PAD_LEFT_ID = store_thm( + "PAD_LEFT_ID", + ``!l c n. n <= LENGTH l ==> (PAD_LEFT c n l = l)``, + rpt strip_tac >> + `n - LENGTH l = 0` by decide_tac >> + rw[PAD_LEFT]); + +(* Theorem: n <= LENGTH l ==> (PAD_RIGHT c n l = l) *) +(* Proof: + Note n - LENGTH l = 0 by n <= LENGTH l + PAD_RIGHT c (LENGTH l) l + = ll ++ GENLIST (K c) 0 by PAD_RIGHT + = [] ++ l by GENLIST + = l by APPEND_NIL +*) +val PAD_RIGHT_ID = store_thm( + "PAD_RIGHT_ID", + ``!l c n. n <= LENGTH l ==> (PAD_RIGHT c n l = l)``, + rpt strip_tac >> + `n - LENGTH l = 0` by decide_tac >> + rw[PAD_RIGHT]); + +(* Theorem: PAD_LEFT c 0 l = l *) +(* Proof: by PAD_LEFT_ID *) +val PAD_LEFT_0 = store_thm( + "PAD_LEFT_0", + ``!l c. PAD_LEFT c 0 l = l``, + rw_tac std_ss[PAD_LEFT_ID]); + +(* Theorem: PAD_RIGHT c 0 l = l *) +(* Proof: by PAD_RIGHT_ID *) +val PAD_RIGHT_0 = store_thm( + "PAD_RIGHT_0", + ``!l c. PAD_RIGHT c 0 l = l``, + rw_tac std_ss[PAD_RIGHT_ID]); + +(* Theorem: LENGTH l <= n ==> !c. PAD_LEFT c (SUC n) l = c:: PAD_LEFT c n l *) +(* Proof: + PAD_LEFT c (SUC n) l + = GENLIST (K c) (SUC n - LENGTH l) ++ l by PAD_LEFT + = GENLIST (K c) (SUC (n - LENGTH l)) ++ l by LENGTH l <= n + = SNOC c (GENLIST (K c) (n - LENGTH l)) ++ l by GENLIST + = (GENLIST (K c) (n - LENGTH l)) ++ [c] ++ l by SNOC_APPEND + = [c] ++ (GENLIST (K c) (n - LENGTH l)) ++ l by GENLIST_K_APPEND_K + = [c] ++ ((GENLIST (K c) (n - LENGTH l)) ++ l) by APPEND_ASSOC + = [c] ++ PAD_LEFT c n l by PAD_LEFT + = c :: PAD_LEFT c n l by CONS_APPEND +*) +val PAD_LEFT_CONS = store_thm( + "PAD_LEFT_CONS", + ``!l n. LENGTH l <= n ==> !c. PAD_LEFT c (SUC n) l = c:: PAD_LEFT c n l``, + rpt strip_tac >> + qabbrev_tac `m = LENGTH l` >> + `SUC n - m = SUC (n - m)` by decide_tac >> + `PAD_LEFT c (SUC n) l = GENLIST (K c) (SUC n - m) ++ l` by rw[PAD_LEFT, Abbr`m`] >> + `_ = SNOC c (GENLIST (K c) (n - m)) ++ l` by rw[GENLIST] >> + `_ = (GENLIST (K c) (n - m)) ++ [c] ++ l` by rw[SNOC_APPEND] >> + `_ = [c] ++ (GENLIST (K c) (n - m)) ++ l` by rw[GENLIST_K_APPEND_K] >> + `_ = [c] ++ ((GENLIST (K c) (n - m)) ++ l)` by rw[APPEND_ASSOC] >> + `_ = [c] ++ PAD_LEFT c n l` by rw[PAD_LEFT] >> + `_ = c :: PAD_LEFT c n l` by rw[] >> + rw[]); + +(* Theorem: LENGTH l <= n ==> !c. PAD_RIGHT c (SUC n) l = SNOC c (PAD_RIGHT c n l) *) +(* Proof: + PAD_RIGHT c (SUC n) l + = l ++ GENLIST (K c) (SUC n - LENGTH l) by PAD_RIGHT + = l ++ GENLIST (K c) (SUC (n - LENGTH l)) by LENGTH l <= n + = l ++ SNOC c (GENLIST (K c) (n - LENGTH l)) by GENLIST + = SNOC c (l ++ (GENLIST (K c) (n - LENGTH l))) by APPEND_SNOC + = SNOC c (PAD_RIGHT c n l) by PAD_RIGHT +*) +val PAD_RIGHT_SNOC = store_thm( + "PAD_RIGHT_SNOC", + ``!l n. LENGTH l <= n ==> !c. PAD_RIGHT c (SUC n) l = SNOC c (PAD_RIGHT c n l)``, + rpt strip_tac >> + qabbrev_tac `m = LENGTH l` >> + `SUC n - m = SUC (n - m)` by decide_tac >> + rw[PAD_RIGHT, GENLIST, APPEND_SNOC]); + +(* Theorem: h :: PAD_RIGHT c n t = PAD_RIGHT c (SUC n) (h::t) *) +(* Proof: + h :: PAD_RIGHT c n t + = h :: (t ++ GENLIST (K c) (n - LENGTH t)) by PAD_RIGHT + = (h::t) ++ GENLIST (K c) (n - LENGTH t) by APPEND + = (h::t) ++ GENLIST (K c) (SUC n - LENGTH (h::t)) by LENGTH + = PAD_RIGHT c (SUC n) (h::t) by PAD_RIGHT +*) +val PAD_RIGHT_CONS = store_thm( + "PAD_RIGHT_CONS", + ``!h t c n. h :: PAD_RIGHT c n t = PAD_RIGHT c (SUC n) (h::t)``, + rw[PAD_RIGHT]); + +(* Theorem: l <> [] ==> (LAST (PAD_LEFT c n l) = LAST l) *) +(* Proof: + Note ?h t. l = h::t by list_CASES + LAST (PAD_LEFT c n l) + = LAST (GENLIST (K c) (n - LENGTH (h::t)) ++ (h::t)) by PAD_LEFT + = LAST (h::t) by LAST_APPEND_CONS + = LAST l by notation +*) +val PAD_LEFT_LAST = store_thm( + "PAD_LEFT_LAST", + ``!l c n. l <> [] ==> (LAST (PAD_LEFT c n l) = LAST l)``, + rpt strip_tac >> + `?h t. l = h::t` by metis_tac[list_CASES] >> + rw[PAD_LEFT, LAST_APPEND_CONS]); + +(* Theorem: (PAD_LEFT c n l = []) <=> ((l = []) /\ (n = 0)) *) +(* Proof: + PAD_LEFT c n l = [] + <=> GENLIST (K c) (n - LENGTH l) ++ l = [] by PAD_LEFT + <=> GENLIST (K c) (n - LENGTH l) = [] /\ l = [] by APPEND_eq_NIL + <=> GENLIST (K c) n = [] /\ l = [] by LENGTH l = 0 + <=> n = 0 /\ l = [] by GENLIST_EQ_NIL +*) +val PAD_LEFT_EQ_NIL = store_thm( + "PAD_LEFT_EQ_NIL", + ``!l c n. (PAD_LEFT c n l = []) <=> ((l = []) /\ (n = 0))``, + rw[PAD_LEFT, EQ_IMP_THM] >> + fs[GENLIST_EQ_NIL]); + +(* Theorem: (PAD_RIGHT c n l = []) <=> ((l = []) /\ (n = 0)) *) +(* Proof: + PAD_RIGHT c n l = [] + <=> l ++ GENLIST (K c) (n - LENGTH l) = [] by PAD_RIGHT + <=> l = [] /\ GENLIST (K c) (n - LENGTH l) = [] by APPEND_eq_NIL + <=> l = [] /\ GENLIST (K c) n = [] by LENGTH l = 0 + <=> l = [] /\ n = 0 by GENLIST_EQ_NIL +*) +val PAD_RIGHT_EQ_NIL = store_thm( + "PAD_RIGHT_EQ_NIL", + ``!l c n. (PAD_RIGHT c n l = []) <=> ((l = []) /\ (n = 0))``, + rw[PAD_RIGHT, EQ_IMP_THM] >> + fs[GENLIST_EQ_NIL]); + +(* Theorem: 0 < n ==> (PAD_LEFT c n [] = PAD_LEFT c n [c]) *) +(* Proof: + PAD_LEFT c n [] + = GENLIST (K c) n by PAD_LEFT, APPEND_NIL + = GENLIST (K c) (SUC k) by n = SUC k, 0 < n + = SNOC c (GENLIST (K c) k) by GENLIST, (K c) k = c + = GENLIST (K c) k ++ [c] by SNOC_APPEND + = PAD_LEFT c n [c] by PAD_LEFT +*) +val PAD_LEFT_NIL_EQ = store_thm( + "PAD_LEFT_NIL_EQ", + ``!n c. 0 < n ==> (PAD_LEFT c n [] = PAD_LEFT c n [c])``, + rw[PAD_LEFT] >> + `SUC (n - 1) = n` by decide_tac >> + qabbrev_tac `f = (K c):num -> 'a` >> + `f (n - 1) = c` by rw[Abbr`f`] >> + metis_tac[SNOC_APPEND, GENLIST]); + +(* Theorem: 0 < n ==> (PAD_RIGHT c n [] = PAD_RIGHT c n [c]) *) +(* Proof: + PAD_RIGHT c n [] + = GENLIST (K c) n by PAD_RIGHT + = GENLIST (K c) (SUC (n - 1)) by 0 < n + = c :: GENLIST (K c) (n - 1) by GENLIST_K_CONS + = [c] ++ GENLIST (K c) (n - 1) by CONS_APPEND + = PAD_RIGHT c (SUC (n - 1)) [c] by PAD_RIGHT + = PAD_RIGHT c n [c] by 0 < n +*) +val PAD_RIGHT_NIL_EQ = store_thm( + "PAD_RIGHT_NIL_EQ", + ``!n c. 0 < n ==> (PAD_RIGHT c n [] = PAD_RIGHT c n [c])``, + rw[PAD_RIGHT] >> + `SUC (n - 1) = n` by decide_tac >> + metis_tac[GENLIST_K_CONS]); + +(* Theorem: PAD_RIGHT c n ls = ls ++ PAD_RIGHT c (n - LENGTH ls) [] *) +(* Proof: + PAD_RIGHT c n ls + = ls ++ GENLIST (K c) (n - LENGTH ls) by PAD_RIGHT + = ls ++ ([] ++ GENLIST (K c) ((n - LENGTH ls) - 0) by APPEND_NIL, LENGTH + = ls ++ PAD_RIGHT c (n - LENGTH ls) [] by PAD_RIGHT +*) +val PAD_RIGHT_BY_RIGHT = store_thm( + "PAD_RIGHT_BY_RIGHT", + ``!ls c n. PAD_RIGHT c n ls = ls ++ PAD_RIGHT c (n - LENGTH ls) []``, + rw[PAD_RIGHT]); + +(* Theorem: PAD_RIGHT c n ls = ls ++ PAD_LEFT c (n - LENGTH ls) [] *) +(* Proof: + PAD_RIGHT c n ls + = ls ++ GENLIST (K c) (n - LENGTH ls) by PAD_RIGHT + = ls ++ (GENLIST (K c) ((n - LENGTH ls) - 0) ++ []) by APPEND_NIL, LENGTH + = ls ++ PAD_LEFT c (n - LENGTH ls) [] by PAD_LEFT +*) +val PAD_RIGHT_BY_LEFT = store_thm( + "PAD_RIGHT_BY_LEFT", + ``!ls c n. PAD_RIGHT c n ls = ls ++ PAD_LEFT c (n - LENGTH ls) []``, + rw[PAD_RIGHT, PAD_LEFT]); + +(* Theorem: PAD_LEFT c n ls = (PAD_RIGHT c (n - LENGTH ls) []) ++ ls *) +(* Proof: + PAD_LEFT c n ls + = GENLIST (K c) (n - LENGTH ls) ++ ls by PAD_LEFT + = ([] ++ GENLIST (K c) ((n - LENGTH ls) - 0) ++ ls by APPEND_NIL, LENGTH + = (PAD_RIGHT c (n - LENGTH ls) []) ++ ls by PAD_RIGHT +*) +val PAD_LEFT_BY_RIGHT = store_thm( + "PAD_LEFT_BY_RIGHT", + ``!ls c n. PAD_LEFT c n ls = (PAD_RIGHT c (n - LENGTH ls) []) ++ ls``, + rw[PAD_RIGHT, PAD_LEFT]); + +(* Theorem: PAD_LEFT c n ls = (PAD_LEFT c (n - LENGTH ls) []) ++ ls *) +(* Proof: + PAD_LEFT c n ls + = GENLIST (K c) (n - LENGTH ls) ++ ls by PAD_LEFT + = ((GENLIST (K c) ((n - LENGTH ls) - 0) ++ []) ++ ls by APPEND_NIL, LENGTH + = (PAD_LEFT c (n - LENGTH ls) []) ++ ls by PAD_LEFT +*) +val PAD_LEFT_BY_LEFT = store_thm( + "PAD_LEFT_BY_LEFT", + ``!ls c n. PAD_LEFT c n ls = (PAD_LEFT c (n - LENGTH ls) []) ++ ls``, + rw[PAD_LEFT]); + +(* ------------------------------------------------------------------------- *) +(* PROD for List, similar to SUM for List *) +(* ------------------------------------------------------------------------- *) + +(* Overload a positive list *) +val _ = overload_on("POSITIVE", ``\l. !x. MEM x l ==> 0 < x``); +val _ = overload_on("EVERY_POSITIVE", ``\l. EVERY (\k. 0 < k) l``); + +(* Theorem: EVERY_POSITIVE ls <=> POSITIVE ls *) +(* Proof: by EVERY_MEM *) +val POSITIVE_THM = store_thm( + "POSITIVE_THM", + ``!ls. EVERY_POSITIVE ls <=> POSITIVE ls``, + rw[EVERY_MEM]); + +(* Note: For product of a number list, any zero element will make the product 0. *) + +(* Define PROD, similar to SUM *) +val PROD = new_recursive_definition + {name = "PROD", + rec_axiom = list_Axiom, + def = ``(PROD [] = 1) /\ + (!h t. PROD (h::t) = h * PROD t)``}; + +(* export simple definition *) +val _ = export_rewrites["PROD"]; + +(* Extract theorems from definition *) +val PROD_NIL = save_thm("PROD_NIL", PROD |> CONJUNCT1); +(* val PROD_NIL = |- PROD [] = 1: thm *) + +val PROD_CONS = save_thm("PROD_CONS", PROD |> CONJUNCT2); +(* val PROD_CONS = |- !h t. PROD (h::t) = h * PROD t: thm *) + +(* Theorem: PROD [n] = n *) +(* Proof: by PROD *) +val PROD_SING = store_thm( + "PROD_SING", + ``!n. PROD [n] = n``, + rw[]); + +(* Theorem: PROD ls = if ls = [] then 1 else (HD ls) * PROD (TL ls) *) +(* Proof: by PROD *) +val PROD_eval = store_thm( + "PROD_eval[compute]", (* put in computeLib *) + ``!ls. PROD ls = if ls = [] then 1 else (HD ls) * PROD (TL ls)``, + metis_tac[PROD, list_CASES, HD, TL]); + +(* enable PROD computation -- use [compute] above. *) +(* val _ = computeLib.add_persistent_funs ["PROD_eval"]; *) + +(* Theorem: (PROD ls = 1) = !x. MEM x ls ==> (x = 1) *) +(* Proof: + By induction on ls. + Base: (PROD [] = 1) <=> !x. MEM x [] ==> (x = 1) + LHS: PROD [] = 1 is true by PROD + RHS: is true since MEM x [] = F by MEM + Step: (PROD ls = 1) <=> !x. MEM x ls ==> (x = 1) ==> + !h. (PROD (h::ls) = 1) <=> !x. MEM x (h::ls) ==> (x = 1) + Note 1 = PROD (h::ls) by given + = h * PROD ls by PROD + Thus h = 1 /\ PROD ls = 1 by MULT_EQ_1 + or h = 1 /\ !x. MEM x ls ==> (x = 1) by induction hypothesis + or !x. MEM x (h::ls) ==> (x = 1) by MEM +*) +val PROD_eq_1 = store_thm( + "PROD_eq_1", + ``!ls. (PROD ls = 1) = !x. MEM x ls ==> (x = 1)``, + Induct >> + rw[] >> + metis_tac[]); + +(* Theorem: PROD (SNOC x l) = (PROD l) * x *) +(* Proof: + By induction on l. + Base: PROD (SNOC x []) = PROD [] * x + PROD (SNOC x []) + = PROD [x] by SNOC + = x by PROD + = 1 * x by MULT_LEFT_1 + = PROD [] * x by PROD + Step: PROD (SNOC x l) = PROD l * x ==> !h. PROD (SNOC x (h::l)) = PROD (h::l) * x + PROD (SNOC x (h::l)) + = PROD (h:: SNOC x l) by SNOC + = h * PROD (SNOC x l) by PROD + = h * (PROD l * x) by induction hypothesis + = (h * PROD l) * x by MULT_ASSOC + = PROD (h::l) * x by PROD +*) +val PROD_SNOC = store_thm( + "PROD_SNOC", + ``!x l. PROD (SNOC x l) = (PROD l) * x``, + strip_tac >> + Induct >> + rw[]); + +(* Theorem: PROD (APPEND l1 l2) = PROD l1 * PROD l2 *) +(* Proof: + By induction on l1. + Base: PROD ([] ++ l2) = PROD [] * PROD l2 + PROD ([] ++ l2) + = PROD l2 by APPEND + = 1 * PROD l2 by MULT_LEFT_1 + = PROD [] * PROD l2 by PROD + Step: !l2. PROD (l1 ++ l2) = PROD l1 * PROD l2 ==> !h l2. PROD (h::l1 ++ l2) = PROD (h::l1) * PROD l2 + PROD (h::l1 ++ l2) + = PROD (h::(l1 ++ l2)) by APPEND + = h * PROD (l1 ++ l2) by PROD + = h * (PROD l1 * PROD l2) by induction hypothesis + = (h * PROD l1) * PROD l2 by MULT_ASSOC + = PROD (h::l1) * PROD l2 by PROD +*) +val PROD_APPEND = store_thm( + "PROD_APPEND", + ``!l1 l2. PROD (APPEND l1 l2) = PROD l1 * PROD l2``, + Induct >> rw[]); + +(* Theorem: PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls *) +(* Proof: + By SNOC_INDUCT |- !P. P [] /\ (!l. P l ==> !x. P (SNOC x l)) ==> !l. P l + Base: PROD (MAP f []) = FOLDL (\a e. a * f e) 1 [] + PROD (MAP f []) + = PROD [] by MAP + = 1 by PROD + = FOLDL (\a e. a * f e) 1 [] by FOLDL + Step: !f. PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls ==> + PROD (MAP f (SNOC x ls)) = FOLDL (\a e. a * f e) 1 (SNOC x ls) + PROD (MAP f (SNOC x ls)) + = PROD (SNOC (f x) (MAP f ls)) by MAP_SNOC + = PROD (MAP f ls) * (f x) by PROD_SNOC + = (FOLDL (\a e. a * f e) 1 ls) * (f x) by induction hypothesis + = (\a e. a * f e) (FOLDL (\a e. a * f e) 1 ls) x by function application + = FOLDL (\a e. a * f e) 1 (SNOC x ls) by FOLDL_SNOC +*) +val PROD_MAP_FOLDL = store_thm( + "PROD_MAP_FOLDL", + ``!ls f. PROD (MAP f ls) = FOLDL (\a e. a * f e) 1 ls``, + HO_MATCH_MP_TAC SNOC_INDUCT >> + rpt strip_tac >- + rw[] >> + rw[MAP_SNOC, PROD_SNOC, FOLDL_SNOC]); + +(* Theorem: FINITE s ==> !f. PI f s = PROD (MAP f (SET_TO_LIST s)) *) +(* Proof: + PI f s + = ITSET (\e acc. f e * acc) s 1 by PROD_IMAGE_DEF + = FOLDL (combin$C (\e acc. f e * acc)) 1 (SET_TO_LIST s) by ITSET_eq_FOLDL_SET_TO_LIST, FINITE s + = FOLDL (\a e. a * f e) 1 (SET_TO_LIST s) by FUN_EQ_THM + = PROD (MAP f (SET_TO_LIST s)) by PROD_MAP_FOLDL +*) +val PROD_IMAGE_eq_PROD_MAP_SET_TO_LIST = store_thm( + "PROD_IMAGE_eq_PROD_MAP_SET_TO_LIST", + ``!s. FINITE s ==> !f. PI f s = PROD (MAP f (SET_TO_LIST s))``, + rw[PROD_IMAGE_DEF] >> + rw[ITSET_eq_FOLDL_SET_TO_LIST, PROD_MAP_FOLDL] >> + rpt AP_THM_TAC >> + AP_TERM_TAC >> + rw[FUN_EQ_THM]); + +(* Define PROD using accumulator *) +val PROD_ACC_DEF = Lib.with_flag (Defn.def_suffix, "_DEF") Define + `(PROD_ACC [] acc = acc) /\ + (PROD_ACC (h::t) acc = PROD_ACC t (h * acc))`; + +(* Theorem: PROD_ACC L n = PROD L * n *) +(* Proof: + By induction on L. + Base: !n. PROD_ACC [] n = PROD [] * n + PROD_ACC [] n + = n by PROD_ACC_DEF + = 1 * n by MULT_LEFT_1 + = PROD [] * n by PROD + Step: !n. PROD_ACC L n = PROD L * n ==> !h n. PROD_ACC (h::L) n = PROD (h::L) * n + PROD_ACC (h::L) n + = PROD_ACC L (h * n) by PROD_ACC_DEF + = PROD L * (h * n) by induction hypothesis + = (PROD L * h) * n by MULT_ASSOC + = (h * PROD L) * n by MULT_COMM + = PROD (h::L) * n by PROD +*) +val PROD_ACC_PROD_LEM = store_thm( + "PROD_ACC_PROD_LEM", + ``!L n. PROD_ACC L n = PROD L * n``, + Induct >> + rw[PROD_ACC_DEF]); +(* proof SUM_ACC_SUM_LEM *) +val PROD_ACC_PROD_LEM = store_thm +("PROD_ACC_SUM_LEM", + ``!L n. PROD_ACC L n = PROD L * n``, + Induct THEN RW_TAC arith_ss [PROD_ACC_DEF, PROD]); + +(* Theorem: PROD L = PROD_ACC L 1 *) +(* Proof: Put n = 1 in PROD_ACC_PROD_LEM *) +Theorem PROD_PROD_ACC[compute]: + !L. PROD L = PROD_ACC L 1 +Proof + rw[PROD_ACC_PROD_LEM] +QED + +(* EVAL ``PROD [1; 2; 3; 4]``; --> 24 *) + +(* Theorem: PROD (GENLIST (K m) n) = m ** n *) +(* Proof: + By induction on n. + Base: PROD (GENLIST (K m) 0) = m ** 0 + PROD (GENLIST (K m) 0) + = PROD [] by GENLIST + = 1 by PROD + = m ** 0 by EXP + Step: PROD (GENLIST (K m) n) = m ** n ==> PROD (GENLIST (K m) (SUC n)) = m ** SUC n + PROD (GENLIST (K m) (SUC n)) + = PROD (SNOC m (GENLIST (K m) n)) by GENLIST + = PROD (GENLIST (K m) n) * m by PROD_SNOC + = m ** n * m by induction hypothesis + = m * m ** n by MULT_COMM + = m * SUC n by EXP +*) +val PROD_GENLIST_K = store_thm( + "PROD_GENLIST_K", + ``!m n. PROD (GENLIST (K m) n) = m ** n``, + strip_tac >> + Induct >- + rw[] >> + rw[GENLIST, PROD_SNOC, EXP]); + +(* Same as PROD_GENLIST_K, formulated slightly different. *) + +(* Theorem: PPROD (GENLIST (\j. x) n) = x ** n *) +(* Proof: + Note (\j. x) = K x by FUN_EQ_THM + PROD (GENLIST (\j. x) n) + = PROD (GENLIST (K x) n) by GENLIST_FUN_EQ + = x ** n by PROD_GENLIST_K +*) +val PROD_CONSTANT = store_thm( + "PROD_CONSTANT", + ``!n x. PROD (GENLIST (\j. x) n) = x ** n``, + rpt strip_tac >> + `(\j. x) = K x` by rw[FUN_EQ_THM] >> + metis_tac[PROD_GENLIST_K, GENLIST_FUN_EQ]); + +(* Theorem: (PROD l = 0) <=> MEM 0 l *) +(* Proof: + By induction on l. + Base: (PROD [] = 0) <=> MEM 0 [] + LHS = F by PROD_NIL, 1 <> 0 + RHS = F by MEM + Step: (PROD l = 0) <=> MEM 0 l ==> !h. (PROD (h::l) = 0) <=> MEM 0 (h::l) + Note PROD (h::l) = h * PROD l by PROD_CONS + Thus PROD (h::l) = 0 + ==> h = 0 \/ PROD l = 0 by MULT_EQ_0 + If h = 0, then MEM 0 (h::l) by MEM + If PROD l = 0, then MEM 0 l by induction hypothesis + or MEM 0 (h::l) by MEM +*) +val PROD_EQ_0 = store_thm( + "PROD_EQ_0", + ``!l. (PROD l = 0) <=> MEM 0 l``, + Induct >- + rw[] >> + metis_tac[PROD_CONS, MULT_EQ_0, MEM]); + +(* Theorem: EVERY (\x. 0 < x) l ==> 0 < PROD l *) +(* Proof: + By contradiction, suppose PROD l = 0. + Then MEM 0 l by PROD_EQ_0 + or 0 < 0 = F by EVERY_MEM +*) +val PROD_POS = store_thm( + "PROD_POS", + ``!l. EVERY (\x. 0 < x) l ==> 0 < PROD l``, + metis_tac[EVERY_MEM, PROD_EQ_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: POSITIVE l ==> 0 < PROD l *) +(* Proof: PROD_POS, EVERY_MEM *) +val PROD_POS_ALT = store_thm( + "PROD_POS_ALT", + ``!l. POSITIVE l ==> 0 < PROD l``, + rw[PROD_POS, EVERY_MEM]); + +(* Theorem: PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1) *) +(* Proof: + The computation is: + n * (n ** 2) * (n ** 4) * ... * (n ** (2 ** (m - 1))) + = n ** (1 + 2 + 4 + ... + 2 ** (m - 1)) + = n ** (2 ** m - 1) + + By induction on m. + Base: PROD (GENLIST (\j. n ** 2 ** j) 0) = n ** (2 ** 0 - 1) + LHS = PROD (GENLIST (\j. n ** 2 ** j) 0) + = PROD [] by GENLIST_0 + = 1 by PROD + RHS = n ** (1 - 1) by EXP_0 + = n ** 0 = 1 = LHS by EXP_0 + Step: PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1) ==> + PROD (GENLIST (\j. n ** 2 ** j) (SUC m)) = n ** (2 ** SUC m - 1) + PROD (GENLIST (\j. n ** 2 ** j) (SUC m)) + = PROD (SNOC (n ** 2 ** m) (GENLIST (\j. n ** 2 ** j) m)) by GENLIST + = PROD (GENLIST (\j. n ** 2 ** j) m) * (n ** 2 ** m) by PROD_SNOC + = n ** (2 ** m - 1) * n ** 2 ** m by induction hypothesis + = n ** (2 ** m - 1 + 2 ** m) by EXP_ADD + = n ** (2 * 2 ** m - 1) by arithmetic + = n ** (2 ** SUC m - 1) by EXP +*) +val PROD_SQUARING_LIST = store_thm( + "PROD_SQUARING_LIST", + ``!m n. PROD (GENLIST (\j. n ** 2 ** j) m) = n ** (2 ** m - 1)``, + rpt strip_tac >> + Induct_on `m` >- + rw[] >> + qabbrev_tac `f = \j. n ** 2 ** j` >> + `PROD (GENLIST f (SUC m)) = PROD (SNOC (n ** 2 ** m) (GENLIST f m))` by rw[GENLIST, Abbr`f`] >> + `_ = PROD (GENLIST f m) * (n ** 2 ** m)` by rw[PROD_SNOC] >> + `_ = n ** (2 ** m - 1) * n ** 2 ** m` by rw[] >> + `_ = n ** (2 ** m - 1 + 2 ** m)` by rw[EXP_ADD] >> + rw[EXP]); + +(* ------------------------------------------------------------------------- *) +(* List Range *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < m ==> 0 < PROD [m .. n] *) +(* Proof: + Note MEM 0 [m .. n] = F by MEM_listRangeINC + Thus PROD [m .. n] <> 0 by PROD_EQ_0 + The result follows. + or + Note EVERY_POSITIVE [m .. n] by listRangeINC_EVERY + Thus 0 < PROD [m .. n] by PROD_POS +*) +val listRangeINC_PROD_pos = store_thm( + "listRangeINC_PROD_pos", + ``!m n. 0 < m ==> 0 < PROD [m .. n]``, + rw[PROD_POS, listRangeINC_EVERY]); + +(* Theorem: 0 < m /\ m <= n ==> (PROD [m .. n] = PROD [1 .. n] DIV PROD [1 .. (m - 1)]) *) +(* Proof: + If m = 1, + Then [1 .. (m-1)] = [1 .. 0] = [] by listRangeINC_EMPTY + PROD [1 .. n] + = PROD [1 .. n] DIV 1 by DIV_ONE + = PROD [1 .. n] DIV PROD [] by PROD_NIL + If m <> 1, then 1 <= m by m <> 0, m <> 1 + Note 1 <= m - 1 /\ m - 1 < n /\ (m - 1 + 1 = m) by arithmetic + Thus [1 .. n] = [1 .. (m - 1)] ++ [m .. n] by listRangeINC_APPEND + or PROD [1 .. n] = PROD [1 .. (m - 1)] * PROD [m .. n] by PROD_POS + Now 0 < PROD [1 .. (m - 1)] by listRangeINC_PROD_pos + The result follows by MULT_TO_DIV +*) +val listRangeINC_PROD = store_thm( + "listRangeINC_PROD", + ``!m n. 0 < m /\ m <= n ==> (PROD [m .. n] = PROD [1 .. n] DIV PROD [1 .. (m - 1)])``, + rpt strip_tac >> + Cases_on `m = 1` >- + rw[listRangeINC_EMPTY] >> + `1 <= m - 1 /\ m - 1 <= n /\ (m - 1 + 1 = m)` by decide_tac >> + `[1 .. n] = [1 .. (m - 1)] ++ [m .. n]` by metis_tac[listRangeINC_APPEND] >> + `PROD [1 .. n] = PROD [1 .. (m - 1)] * PROD [m .. n]` by rw[GSYM PROD_APPEND] >> + `0 < PROD [1 .. (m - 1)]` by rw[listRangeINC_PROD_pos] >> + metis_tac[MULT_TO_DIV]); + +(* Theorem: 0 < m ==> 0 < PROD [m ..< n] *) +(* Proof: + Note MEM 0 [m ..< n] = F by MEM_listRangeLHI + Thus PROD [m ..< n] <> 0 by PROD_EQ_0 + The result follows. + or, + Note EVERY_POSITIVE [m ..< n] by listRangeLHI_EVERY + Thus 0 < PROD [m ..< n] by PROD_POS +*) +val listRangeLHI_PROD_pos = store_thm( + "listRangeLHI_PROD_pos", + ``!m n. 0 < m ==> 0 < PROD [m ..< n]``, + rw[PROD_POS, listRangeLHI_EVERY]); + +(* Theorem: 0 < m /\ m <= n ==> (PROD [m ..< n] = PROD [1 ..< n] DIV PROD [1 ..< m]) *) +(* Proof: + Note n <> 0 by 0 < m /\ m <= n + Let m = m' + 1, n = n' + 1 by num_CASES, ADD1 + If m = n, + Note 0 < PROD [1 ..< n] by listRangeLHI_PROD_pos + LHS = PROD [n ..< n] + = PROD [] = 1 by listRangeLHI_EMPTY + RHS = PROD [1 ..< n] DIV PROD [1 ..< n] + = 1 by DIVMOD_ID, 0 < PROD [1 ..< n] + If m <> n, + Then m < n, or m <= n' by arithmetic + PROD [m ..< n] + = PROD [m .. n'] by listRangeLHI_to_INC + = PROD [1 .. n'] DIV PROD [1 .. m - 1] by listRangeINC_PROD, m <= n' + = PROD [1 .. n'] DIV PROD [1 .. m'] by m' = m - 1 + = PROD [1 ..< n] DIV PROD [1 ..< m] by listRangeLHI_to_INC +*) +val listRangeLHI_PROD = store_thm( + "listRangeLHI_PROD", + ``!m n. 0 < m /\ m <= n ==> (PROD [m ..< n] = PROD [1 ..< n] DIV PROD [1 ..< m])``, + rpt strip_tac >> + `m <> 0 /\ n <> 0` by decide_tac >> + `?n' m'. (n = n' + 1) /\ (m = m' + 1)` by metis_tac[num_CASES, ADD1] >> + Cases_on `m = n` >| [ + `0 < PROD [1 ..< n]` by rw[listRangeLHI_PROD_pos] >> + rfs[listRangeLHI_EMPTY, DIVMOD_ID], + `m <= n'` by decide_tac >> + `PROD [m ..< n] = PROD [m .. n']` by rw[listRangeLHI_to_INC] >> + `_ = PROD [1 .. n'] DIV PROD [1 .. m - 1]` by rw[GSYM listRangeINC_PROD] >> + `_ = PROD [1 .. n'] DIV PROD [1 .. m']` by rw[] >> + `_ = PROD [1 ..< n] DIV PROD [1 ..< m]` by rw[GSYM listRangeLHI_to_INC] >> + rw[] + ]); + +(* ------------------------------------------------------------------------- *) +(* List Summation and Product *) +(* ------------------------------------------------------------------------- *) + +(* +> numpairTheory.tri_def; +val it = |- tri 0 = 0 /\ !n. tri (SUC n) = SUC n + tri n: thm +*) + +(* Theorem: SUM [1 .. n] = tri n *) +(* Proof: + By induction on n, + Base: SUM [1 .. 0] = tri 0 + SUM [1 .. 0] + = SUM [] by listRangeINC_EMPTY + = 0 by SUM_NIL + = tri 0 by tri_def + Step: SUM [1 .. n] = tri n ==> SUM [1 .. SUC n] = tri (SUC n) + SUM [1 .. SUC n] + = SUM (SNOC (SUC n) [1 .. n]) by listRangeINC_SNOC, 1 < n + = SUM [1 .. n] + (SUC n) by SUM_SNOC + = tri n + (SUC n) by induction hypothesis + = tri (SUC n) by tri_def +*) +val sum_1_to_n_eq_tri_n = store_thm( + "sum_1_to_n_eq_tri_n", + ``!n. SUM [1 .. n] = tri n``, + Induct >- + rw[listRangeINC_EMPTY, SUM_NIL, numpairTheory.tri_def] >> + rw[listRangeINC_SNOC, ADD1, SUM_SNOC, numpairTheory.tri_def]); + +(* Theorem: SUM [1 .. n] = HALF (n * (n + 1)) *) +(* Proof: + SUM [1 .. n] + = tri n by sum_1_to_n_eq_tri_n + = HALF (n * (n + 1)) by tri_formula +*) +val sum_1_to_n_eqn = store_thm( + "sum_1_to_n_eqn", + ``!n. SUM [1 .. n] = HALF (n * (n + 1))``, + rw[sum_1_to_n_eq_tri_n, numpairTheory.tri_formula]); + +(* Theorem: 2 * SUM [1 .. n] = n * (n + 1) *) +(* Proof: + Note EVEN (n * (n + 1)) by EVEN_PARTNERS + or 2 divides (n * (n + 1)) by EVEN_ALT + Thus n * (n + 1) + = ((n * (n + 1)) DIV 2) * 2 by DIV_MULT_EQ + = (SUM [1 .. n]) * 2 by sum_1_to_n_eqn + = 2 * SUM [1 .. n] by MULT_COMM +*) +val sum_1_to_n_double = store_thm( + "sum_1_to_n_double", + ``!n. 2 * SUM [1 .. n] = n * (n + 1)``, + rpt strip_tac >> + `2 divides (n * (n + 1))` by rw[EVEN_PARTNERS, GSYM EVEN_ALT] >> + metis_tac[sum_1_to_n_eqn, DIV_MULT_EQ, MULT_COMM, DECIDE``0 < 2``]); + +(* Theorem: PROD [1 .. n] = FACT n *) +(* Proof: + By induction on n, + Base: PROD [1 .. 0] = FACT 0 + PROD [1 .. 0] + = PROD [] by listRangeINC_EMPTY + = 1 by PROD_NIL + = FACT 0 by FACT + Step: PROD [1 .. n] = FACT n ==> PROD [1 .. SUC n] = FACT (SUC n) + PROD [1 .. SUC n] = FACT (SUC n) + = PROD (SNOC (SUC n) [1 .. n]) by listRangeINC_SNOC, 1 < n + = PROD [1 .. n] * (SUC n) by PROD_SNOC + = (FACT n) * (SUC n) by induction hypothesis + = FACT (SUC n) by FACT +*) +val prod_1_to_n_eq_fact_n = store_thm( + "prod_1_to_n_eq_fact_n", + ``!n. PROD [1 .. n] = FACT n``, + Induct >- + rw[listRangeINC_EMPTY, PROD_NIL, FACT] >> + rw[listRangeINC_SNOC, ADD1, PROD_SNOC, FACT]); + +(* This is numerical version of: +poly_cyclic_cofactor |- !r. Ring r /\ #1 <> #0 ==> !n. unity n = unity 1 * cyclic n +*) +(* Theorem: (t ** n - 1 = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n])) *) +(* Proof: + Let f = (\j. t ** j). + By induction on n. + Base: t ** 0 - 1 = (t - 1) * SUM (MAP f [0 ..< 0]) + LHS = t ** 0 - 1 = 0 by EXP_0 + RHS = (t - 1) * SUM (MAP f [0 ..< 0]) + = (t - 1) * SUM [] by listRangeLHI_EMPTY + = (t - 1) * 0 = 0 by SUM + Step: t ** n - 1 = (t - 1) * SUM (MAP f [0 ..< n]) ==> + t ** SUC n - 1 = (t - 1) * SUM (MAP f [0 ..< SUC n]) + If t = 0, + LHS = 0 ** SUC n - 1 = 0 by EXP_0 + RHS = (0 - 1) * SUM (MAP f [0 ..< SUC n]) + = 0 * SUM (MAP f [0 ..< SUC n]) by integer subtraction + = 0 = LHS + If t <> 0, + Then 0 < t ** n by EXP_POS + or 1 <= t ** n by arithmetic + so (t ** n - 1) + (t * t ** n - t ** n) = t * t ** n - 1 + (t - 1) * SUM (MAP (\j. t ** j) [0 ..< (SUC n)]) + = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n + 1]) by ADD1 + = (t - 1) * SUM (MAP (\j. t ** j) (SNOC n [0 ..< n])) by listRangeLHI_SNOC + = (t - 1) * SUM (SNOC (t ** n) (MAP f [0 ..< n])) by MAP_SNOC + = (t - 1) * (SUM (MAP f [0 ..< n]) + t ** n) by SUM_SNOC + = (t - 1) * SUM (MAP f [0 ..< n]) + (t - 1) * t ** n by RIGHT_ADD_DISTRIB + = (t ** n - 1) + (t - 1) * t ** n by induction hypothesis + = t ** SUC n - 1 by EXP +*) +val power_predecessor_eqn = store_thm( + "power_predecessor_eqn", + ``!t n. t ** n - 1 = (t - 1) * SUM (MAP (\j. t ** j) [0 ..< n])``, + rpt strip_tac >> + qabbrev_tac `f = \j. t ** j` >> + Induct_on `n` >- + rw[EXP_0, Abbr`f`] >> + Cases_on `t = 0` >- + rw[ZERO_EXP, Abbr`f`] >> + `(t ** n - 1) + (t * t ** n - t ** n) = t * t ** n - 1` by + (`0 < t` by decide_tac >> + `0 < t ** n` by rw[EXP_POS] >> + `1 <= t ** n` by decide_tac >> + `t ** n <= t * t ** n` by rw[] >> + decide_tac) >> + `(t - 1) * SUM (MAP f [0 ..< (SUC n)]) = (t - 1) * SUM (MAP f [0 ..< n + 1])` by rw[ADD1] >> + `_ = (t - 1) * SUM (MAP f (SNOC n [0 ..< n]))` by rw[listRangeLHI_SNOC] >> + `_ = (t - 1) * SUM (SNOC (t ** n) (MAP f [0 ..< n]))` by rw[MAP_SNOC, Abbr`f`] >> + `_ = (t - 1) * (SUM (MAP f [0 ..< n]) + t ** n)` by rw[SUM_SNOC] >> + `_ = (t - 1) * SUM (MAP f [0 ..< n]) + (t - 1) * t ** n` by rw[RIGHT_ADD_DISTRIB] >> + `_ = (t ** n - 1) + (t - 1) * t ** n` by rw[] >> + `_ = (t ** n - 1) + (t * t ** n - t ** n)` by rw[LEFT_SUB_DISTRIB] >> + `_ = t * t ** n - 1` by rw[] >> + `_ = t ** SUC n - 1 ` by rw[GSYM EXP] >> + rw[]); + +(* Above is the formal proof of the following observation for any base: + 9 = 9 * 1 + 99 = 9 * 11 + 999 = 9 * 111 + 9999 = 9 * 1111 + 99999 = 8 * 11111 + etc. + + This asserts: + (t ** n - 1) = (t - 1) * (1 + t + t ** 2 + ... + t ** (n-1)) + or 1 + t + t ** 2 + ... + t ** (n - 1) = (t ** n - 1) DIV (t - 1), + which is the sum of the geometric series. +*) + +(* Theorem: 1 < t ==> (SUM (MAP (\j. t ** j) [0 ..< n]) = (t ** n - 1) DIV (t - 1)) *) +(* Proof: + Note 0 < t - 1 by 1 < t + Let s = SUM (MAP (\j. t ** j) [0 ..< n]). + Then (t ** n - 1) = (t - 1) * s by power_predecessor_eqn + Thus s = (t ** n - 1) DIV (t - 1) by MULT_TO_DIV, 0 < t - 1 +*) +val geometric_sum_eqn = store_thm( + "geometric_sum_eqn", + ``!t n. 1 < t ==> (SUM (MAP (\j. t ** j) [0 ..< n]) = (t ** n - 1) DIV (t - 1))``, + rpt strip_tac >> + `0 < t - 1` by decide_tac >> + rw_tac std_ss[power_predecessor_eqn, MULT_TO_DIV]); + +(* Theorem: 1 < t ==> (SUM (MAP (\j. t ** j) [0 .. n]) = (t ** (n + 1) - 1) DIV (t - 1)) *) +(* Proof: + SUM (MAP (\j. t ** j) [0 .. n]) + = SUM (MAP (\j. t ** j) [0 ..< n + 1]) by listRangeLHI_to_INC + = (t ** (n + 1) - 1) DIV (t - 1) by geometric_sum_eqn +*) +val geometric_sum_eqn_alt = store_thm( + "geometric_sum_eqn_alt", + ``!t n. 1 < t ==> (SUM (MAP (\j. t ** j) [0 .. n]) = (t ** (n + 1) - 1) DIV (t - 1))``, + rw_tac std_ss[GSYM listRangeLHI_to_INC, geometric_sum_eqn]); + +(* Theorem: SUM [1 ..< n] = HALF (n * (n - 1)) *) +(* Proof: + If n = 0, + LHS = SUM [1 ..< 0] + = SUM [] = 0 by listRangeLHI_EMPTY + RHS = HALF (0 * (0 - 1)) + = 0 = LHS by arithmetic + If n <> 0, + Then n = (n - 1) + 1 by arithmetic, n <> 0 + SUM [1 ..< n] + = SUM [1 .. n - 1] by listRangeLHI_to_INC + = HALF ((n - 1) * (n - 1 + 1)) by sum_1_to_n_eqn + = HALF (n * (n - 1)) by arithmetic +*) +val arithmetic_sum_eqn = store_thm( + "arithmetic_sum_eqn", + ``!n. SUM [1 ..< n] = HALF (n * (n - 1))``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[listRangeLHI_EMPTY] >> + `n = (n - 1) + 1` by decide_tac >> + `SUM [1 ..< n] = SUM [1 .. n - 1]` by rw[GSYM listRangeLHI_to_INC] >> + `_ = HALF ((n - 1) * (n - 1 + 1))` by rw[sum_1_to_n_eqn] >> + `_ = HALF (n * (n - 1))` by rw[] >> + rw[]); + +(* Theorem alias *) +val arithmetic_sum_eqn_alt = save_thm("arithmetic_sum_eqn_alt", sum_1_to_n_eqn); +(* val arithmetic_sum_eqn_alt = |- !n. SUM [1 .. n] = HALF (n * (n + 1)): thm *) + +(* Theorem: SUM (GENLIST (\j. f (n - j)) n) = SUM (MAP f [1 .. n]) *) +(* Proof: + SUM (GENLIST (\j. f (n - j)) n) + = SUM (REVERSE (GENLIST (\j. f (n - j)) n)) by SUM_REVERSE + = SUM (GENLIST (\j. f (n - (PRE n - j))) n) by REVERSE_GENLIST + = SUM (GENLIST (\j. f (1 + j)) n) by LIST_EQ, SUB_SUB + = SUM (GENLIST (f o SUC) n) by FUN_EQ_THM + = SUM (MAP f [1 .. n]) by listRangeINC_MAP +*) +val SUM_GENLIST_REVERSE = store_thm( + "SUM_GENLIST_REVERSE", + ``!f n. SUM (GENLIST (\j. f (n - j)) n) = SUM (MAP f [1 .. n])``, + rpt strip_tac >> + `GENLIST (\j. f (n - (PRE n - j))) n = GENLIST (f o SUC) n` by + (irule LIST_EQ >> + rw[] >> + `n + x - PRE n = SUC x` by decide_tac >> + simp[]) >> + qabbrev_tac `g = \j. f (n - j)` >> + `SUM (GENLIST g n) = SUM (REVERSE (GENLIST g n))` by rw[SUM_REVERSE] >> + `_ = SUM (GENLIST (\j. g (PRE n - j)) n)` by rw[REVERSE_GENLIST] >> + `_ = SUM (GENLIST (f o SUC) n)` by rw[Abbr`g`] >> + `_ = SUM (MAP f [1 .. n])` by rw[listRangeINC_MAP] >> + decide_tac); +(* Note: locate here due to use of listRangeINC_MAP *) + +(* Theorem: SIGMA f (count n) = SUM (MAP f [0 ..< n]) *) +(* Proof: + SIGMA f (count n) + = SUM (GENLIST f n) by SUM_GENLIST + = SUM (MAP f [0 ..< n]) by listRangeLHI_MAP +*) +Theorem SUM_IMAGE_count: + !f n. SIGMA f (count n) = SUM (MAP f [0 ..< n]) +Proof + simp[SUM_GENLIST, listRangeLHI_MAP] +QED +(* Note: locate here due to use of listRangeINC_MAP *) + +(* Theorem: SIGMA f (count (SUC n)) = SUM (MAP f [0 .. n]) *) +(* Proof: + SIGMA f (count (SUC n)) + = SUM (GENLIST f (SUC n)) by SUM_GENLIST + = SUM (MAP f [0 ..< (SUC n)]) by SUM_IMAGE_count + = SUM (MAP f [0 .. n]) by listRangeINC_to_LHI +*) +Theorem SUM_IMAGE_upto: + !f n. SIGMA f (count (SUC n)) = SUM (MAP f [0 .. n]) +Proof + simp[SUM_GENLIST, SUM_IMAGE_count, listRangeINC_to_LHI] +QED + +(* ------------------------------------------------------------------------- *) +(* MAP of function with 3 list arguments *) +(* ------------------------------------------------------------------------- *) + +(* Define MAP3 similar to MAP2 in listTheory. *) +val dDefine = Lib.with_flag (Defn.def_suffix, "_DEF") Define; +val MAP3_DEF = dDefine` + (MAP3 f (h1::t1) (h2::t2) (h3::t3) = f h1 h2 h3::MAP3 f t1 t2 t3) /\ + (MAP3 f x y z = [])`; +val _ = export_rewrites["MAP3_DEF"]; +val MAP3 = store_thm ("MAP3", +``(!f. MAP3 f [] [] [] = []) /\ + (!f h1 t1 h2 t2 h3 t3. MAP3 f (h1::t1) (h2::t2) (h3::t3) = f h1 h2 h3::MAP3 f t1 t2 t3)``, + METIS_TAC[MAP3_DEF]); + +(* +LENGTH_MAP |- !l f. LENGTH (MAP f l) = LENGTH l +LENGTH_MAP2 |- !xs ys. LENGTH (MAP2 f xs ys) = MIN (LENGTH xs) (LENGTH ys) +*) + +(* Theorem: LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) *) +(* Proof: + By induction on lx. + Base: !ly lz f. LENGTH (MAP3 f [] ly lz) = MIN (MIN (LENGTH []) (LENGTH ly)) (LENGTH lz) + LHS = LENGTH [] = 0 by MAP3, LENGTH + RHS = MIN (MIN 0 (LENGTH ly)) (LENGTH lz) by LENGTH + = MIN 0 (LENGTH lz) = 0 = LHS by MIN_DEF + Step: !ly lz f. LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> + !h ly lz f. LENGTH (MAP3 f (h::lx) ly lz) = MIN (MIN (LENGTH (h::lx)) (LENGTH ly)) (LENGTH lz) + If ly = [], + LHS = LENGTH (MAP3 f (h::lx) [] lz) = 0 by MAP3, LENGTH + RHS = MIN (MIN (LENGTH (h::lx)) (LENGTH [])) (LENGTH lz) + = MIN 0 (LENGTH lz) = 0 = LHS by MIN_DEF + Otherwise, ly = h'::t. + If lz = [], + LHS = LENGTH (MAP3 f (h::lx) (h'::t) []) = 0 by MAP3, LENGTH + RHS = MIN (MIN (LENGTH (h::lx)) (LENGTH (h'::t))) (LENGTH []) + = 0 = LHS by MIN_DEF + Otherwise, lz = h''::t'. + LHS = LENGTH (MAP3 f (h::lx) (h'::t) (h''::t')) + = LENGTH (f h' h''::MAP3 lx t t'') by MAP3 + = SUC (LENGTH MAP3 lx t t'') by LENGTH + = SUC (MIN (MIN (LENGTH lx) (LENGTH t)) (LENGTH t'')) by induction hypothesis + RHS = MIN (MIN (LENGTH (h::lx)) (LENGTH (h'::t))) (LENGTH (h''::t')) + = MIN (MIN (SUC (LENGTH lx)) (SUC (LENGTH t))) (SUC (LENGTH t')) by LENGTH + = MIN (SUC (MIN (LENGTH lx) (LENGTH t))) (SUC (LESS_TWICE t')) by MIN_DEF + = SUC (MIN (MIN (LENGTH lx) (LENGTH t)) (LENGTH t'')) = LHS by MIN_DEF +*) +val LENGTH_MAP3 = store_thm( + "LENGTH_MAP3", + ``!lx ly lz f. LENGTH (MAP3 f lx ly lz) = MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz)``, + Induct_on `lx` >- + rw[] >> + rpt strip_tac >> + Cases_on `ly` >- + rw[] >> + Cases_on `lz` >- + rw[] >> + rw[MIN_DEF]); + +(* +EL_MAP |- !n l. n < LENGTH l ==> !f. EL n (MAP f l) = f (EL n l) +EL_MAP2 |- !ts tt n. n < MIN (LENGTH ts) (LENGTH tt) ==> (EL n (MAP2 f ts tt) = f (EL n ts) (EL n tt)) +*) + +(* Theorem: n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> + !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz) *) +(* Proof: + By induction on n. + Base: !lx ly lz. 0 < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> + !f. EL 0 (MAP3 f lx ly lz) = f (EL 0 lx) (EL 0 ly) (EL 0 lz) + Note ?x tx. lx = x::tx by LENGTH_EQ_0, list_CASES + and ?y ty. ly = y::ty by LENGTH_EQ_0, list_CASES + and ?z tz. lz = z::tz by LENGTH_EQ_0, list_CASES + EL 0 (MAP3 f lx ly lz) + = EL 0 (MAP3 f (x::lx) (y::ty) (z::tz)) + = EL 0 (f x y z::MAP3 f tx ty tz) by MAP3 + = f x y z by EL + = f (EL 0 lx) (EL 0 ly) (EL 0 lz) by EL + Step: !lx ly lz. n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> + !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz) ==> + !lx ly lz. SUC n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> + !f. EL (SUC n) (MAP3 f lx ly lz) = f (EL (SUC n) lx) (EL (SUC n) ly) (EL (SUC n) lz) + Note ?x tx. lx = x::tx by LENGTH_EQ_0, list_CASES + and ?y ty. ly = y::ty by LENGTH_EQ_0, list_CASES + and ?z tz. lz = z::tz by LENGTH_EQ_0, list_CASES + Also n < LENGTH tx /\ n < LENGTH ty /\ n < LENGTH tz by LENGTH + Thus n < MIN (MIN (LENGTH tx) (LENGTH ty)) (LENGTH tz) by MIN_DEF + EL (SUC n) (MAP3 f lx ly lz) + = EL (SUC n) (MAP3 f (x::lx) (y::ty) (z::tz)) + = EL (SUC n) (f x y z::MAP3 f tx ty tz) by MAP3 + = EL n (MAP3 f tx ty tz) by EL + = f (EL n tx) (EL n ty) (EL n tz) by induction hypothesis + = f (EL (SUC n) lx) (EL (SUC n) ly) (EL (SUC n) lz) + by EL +*) +val EL_MAP3 = store_thm( + "EL_MAP3", + ``!lx ly lz n. n < MIN (MIN (LENGTH lx) (LENGTH ly)) (LENGTH lz) ==> + !f. EL n (MAP3 f lx ly lz) = f (EL n lx) (EL n ly) (EL n lz)``, + Induct_on `n` >| [ + rw[] >> + `?x tx. lx = x::tx` by metis_tac[LENGTH_EQ_0, list_CASES, NOT_ZERO_LT_ZERO] >> + `?y ty. ly = y::ty` by metis_tac[LENGTH_EQ_0, list_CASES, NOT_ZERO_LT_ZERO] >> + `?z tz. lz = z::tz` by metis_tac[LENGTH_EQ_0, list_CASES, NOT_ZERO_LT_ZERO] >> + rw[], + rw[] >> + `!a. SUC n < a ==> a <> 0` by decide_tac >> + `?x tx. lx = x::tx` by metis_tac[LENGTH_EQ_0, list_CASES] >> + `?y ty. ly = y::ty` by metis_tac[LENGTH_EQ_0, list_CASES] >> + `?z tz. lz = z::tz` by metis_tac[LENGTH_EQ_0, list_CASES] >> + `n < LENGTH tx /\ n < LENGTH ty /\ n < LENGTH tz` by fs[] >> + rw[] + ]); + +(* +MEM_MAP |- !l f x. MEM x (MAP f l) <=> ?y. x = f y /\ MEM y l +*) + +(* Theorem: MEM x (MAP2 f l1 l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 l2 *) +(* Proof: + By induction on l1. + Base: !l2. MEM x (MAP2 f [] l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 [] /\ MEM y2 l2 + Note MAP2 f [] l2 = [] by MAP2_DEF + and MEM x [] = F, hence true by MEM + Step: !l2. MEM x (MAP2 f l1 l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 l2 ==> + !h l2. MEM x (MAP2 f (h::l1) l2) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 (h::l1) /\ MEM y2 l2 + If l2 = [], + Then MEM x (MAP2 f (h::l1) []) = F, hence true by MEM + Otherwise, l2 = h'::t, + to show: MEM x (MAP2 f (h::l1) (h'::t)) ==> ?y1 y2. x = f y1 y2 /\ MEM y1 (h::l1) /\ MEM y2 (h'::t) + Note MAP2 f (h::l1) (h'::t) + = (f h h')::MAP2 f l1 t by MAP2 + Thus x = f h h' or MEM x (MAP2 f l1 t) by MEM + If x = f h h', + Take y1 = h, y2 = h', and the result follows by MEM + If MEM x (MAP2 f l1 t) + Then ?y1 y2. x = f y1 y2 /\ MEM y1 l1 /\ MEM y2 t by induction hypothesis + Take this y1 and y2, the result follows by MEM +*) +val MEM_MAP2 = store_thm( + "MEM_MAP2", + ``!f x l1 l2. MEM x (MAP2 f l1 l2) ==> ?y1 y2. (x = f y1 y2) /\ MEM y1 l1 /\ MEM y2 l2``, + ntac 2 strip_tac >> + Induct_on `l1` >- + rw[] >> + rpt strip_tac >> + Cases_on `l2` >- + fs[] >> + fs[] >- + metis_tac[] >> + metis_tac[MEM]); + +(* Theorem: MEM x (MAP3 f l1 l2 l3) ==> ?y1 y2 y3. (x = f y1 y2 y3) /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3 *) +(* Proof: + By induction on l1. + Base: !l2 l3. MEM x (MAP3 f [] l2 l3) ==> ... + Note MAP3 f [] l2 l3 = [], and MEM x [] = F, hence true. + Step: !l2 l3. MEM x (MAP3 f l1 l2 l3) ==> + ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3 ==> + !h l2 l3. MEM x (MAP3 f (h::l1) l2 l3) ==> + ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 (h::l1) /\ MEM y2 l2 /\ MEM y3 l3 + If l2 = [], + Then MEM x (MAP3 f (h::l1) [] l3) = MEM x [] = F, hence true by MAP3_DEF + Otherwise, l2 = h'::t, + to show: MEM x (MAP3 f (h::l1) (h'::t) l3) ==> + ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 (h::l1) /\ MEM y2 (h'::t) /\ MEM y3 l3 + If l3 = [], + Then MEM x (MAP3 f (h::l1) l2 []) = MEM x [] = F, hence true by MAP3_DEF + Otherwise, l3 = h''::t', + to show: MEM x (MAP3 f (h::l1) (h'::t) (h''::t')) ==> + ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 (h::l1) /\ MEM y2 (h'::t) /\ MEM y3 (h''::t') + + Note MAP3 f (h::l1) (h'::t) (h''::t') + = (f h h' h'')::MAP3 f l1 t t' by MAP3 + Thus x = f h h' h'' or MEM x (MAP3 f l1 t t') by MEM + If x = f h h' h'', + Take y1 = h, y2 = h', y3 = h'' and the result follows by MEM + If MEM x (MAP3 f l1 t t') + Then ?y1 y2 y3. x = f y1 y2 y3 /\ MEM y1 t /\ MEM y2 l2 /\ MEM y3 t' + by induction hypothesis + Take this y1, y2 and y3, the result follows by MEM +*) +val MEM_MAP3 = store_thm( + "MEM_MAP3", + ``!f x l1 l2 l3. MEM x (MAP3 f l1 l2 l3) ==> + ?y1 y2 y3. (x = f y1 y2 y3) /\ MEM y1 l1 /\ MEM y2 l2 /\ MEM y3 l3``, + ntac 2 strip_tac >> + Induct_on `l1` >- + rw[] >> + rpt strip_tac >> + Cases_on `l2` >- + fs[] >> + Cases_on `l3` >- + fs[] >> + fs[] >- + metis_tac[] >> + metis_tac[MEM]); + +(* Theorem: SUM (MAP (K c) ls) = c * LENGTH ls *) +(* Proof: + By induction on ls. + Base: !c. SUM (MAP (K c) []) = c * LENGTH [] + LHS = SUM (MAP (K c) []) + = SUM [] = 0 by MAP, SUM + RHS = c * LENGTH [] + = c * 0 = 0 = LHS by LENGTH + Step: !c. SUM (MAP (K c) ls) = c * LENGTH ls ==> + !h c. SUM (MAP (K c) (h::ls)) = c * LENGTH (h::ls) + SUM (MAP (K c) (h::ls)) + = SUM (c :: MAP (K c) ls) by MAP + = c + SUM (MAP (K c) ls) by SUM + = c + c * LENGTH ls by induction hypothesis + = c * (1 + LENGTH ls) by RIGHT_ADD_DISTRIB + = c * (SUC (LENGTH ls)) by ADD1 + = c * LENGTH (h::ls) by LENGTH +*) +val SUM_MAP_K = store_thm( + "SUM_MAP_K", + ``!ls c. SUM (MAP (K c) ls) = c * LENGTH ls``, + Induct >- + rw[] >> + rw[ADD1]); + +(* Theorem: a <= b ==> SUM (MAP (K a) ls) <= SUM (MAP (K b) ls) *) +(* Proof: + SUM (MAP (K a) ls) + = a * LENGTH ls by SUM_MAP_K + <= b * LENGTH ls by a <= b + = SUM (MAP (K b) ls) by SUM_MAP_K +*) +val SUM_MAP_K_LE = store_thm( + "SUM_MAP_K_LE", + ``!ls a b. a <= b ==> SUM (MAP (K a) ls) <= SUM (MAP (K b) ls)``, + rw[SUM_MAP_K]); + +(* Theorem: SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) *) +(* Proof: + By induction on lx. + Base: !ly c. SUM (MAP2 (\x y. c) [] ly) = c * LENGTH (MAP2 (\x y. c) [] ly) + LHS = SUM (MAP2 (\x y. c) [] ly) + = SUM [] = 0 by MAP2_DEF, SUM + RHS = c * LENGTH (MAP2 (\x y. c) [] ly) + = c * 0 = 0 = LHS by MAP2_DEF, LENGTH + Step: !ly c. SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) ==> + !h ly c. SUM (MAP2 (\x y. c) (h::lx) ly) = c * LENGTH (MAP2 (\x y. c) (h::lx) ly) + If ly = [], + to show: SUM (MAP2 (\x y. c) (h::lx) []) = c * LENGTH (MAP2 (\x y. c) (h::lx) []) + LHS = SUM (MAP2 (\x y. c) (h::lx) []) + = SUM [] = 0 by MAP2_DEF, SUM + RHS = c * LENGTH (MAP2 (\x y. c) (h::lx) []) + = c * 0 = 0 = LHS by MAP2_DEF, LENGTH + Otherwise, ly = h'::t, + to show: SUM (MAP2 (\x y. c) (h::lx) (h'::t)) = c * LENGTH (MAP2 (\x y. c) (h::lx) (h'::t)) + + SUM (MAP2 (\x y. c) (h::lx) (h'::t)) + = SUM (c :: MAP2 (\x y. c) lx t) by MAP2_DEF + = c + SUM (MAP2 (\x y. c) lx t) by SUM + = c + c * LENGTH (MAP2 (\x y. c) lx t) by induction hypothesis + = c * (1 + LENGTH (MAP2 (\x y. c) lx t) by RIGHT_ADD_DISTRIB + = c * (SUC (LENGTH (MAP2 (\x y. c) lx t)) by ADD1 + = c * LENGTH (MAP2 (\x y. c) (h::lx) (h'::t)) by LENGTH +*) +val SUM_MAP2_K = store_thm( + "SUM_MAP2_K", + ``!lx ly c. SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly)``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `ly` >- + rw[] >> + rw[ADD1, MIN_DEF]); + +(* Theorem: SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) *) +(* Proof: + By induction on lx. + Base: !ly lz c. SUM (MAP3 (\x y z. c) [] ly lz) = c * LENGTH (MAP3 (\x y z. c) [] ly lz) + LHS = SUM (MAP3 (\x y z. c) [] ly lz) + = SUM [] = 0 by MAP3_DEF, SUM + RHS = c * LENGTH (MAP3 (\x y z. c) [] ly lz) + = c * 0 = 0 = LHS by MAP3_DEF, LENGTH + Step: !ly lz c. SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) ==> + !h ly lz c. SUM (MAP3 (\x y z. c) (h::lx) ly lz) = c * LENGTH (MAP3 (\x y z. c) (h::lx) ly lz) + If ly = [], + to show: SUM (MAP3 (\x y z. c) (h::lx) [] lz) = c * LENGTH (MAP3 (\x y z. c) (h::lx) [] lz) + LHS = SUM (MAP3 (\x y z. c) (h::lx) [] lz) + = SUM [] = 0 by MAP3_DEF, SUM + RHS = c * LENGTH (MAP3 (\x y z. c) (h::lx) [] lz) + = c * 0 = 0 = LHS by MAP3_DEF, LENGTH + Otherwise, ly = h'::t, + to show: SUM (MAP3 (\x y z. c) (h::lx) (h'::t) lz) = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) lz) + If lz = [], + to show: SUM (MAP3 (\x y z. c) (h::lx) (h'::t) []) = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) []) + LHS = SUM (MAP3 (\x y z. c) (h::lx) (h'::t) []) + = SUM [] = 0 by MAP3_DEF, SUM + RHS = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) []) + = c * 0 = 0 by MAP3_DEF, LENGTH + Otherwise, lz = h''::t', + to show: SUM (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) + SUM (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) + = SUM (c :: MAP3 (\x y z. c) lx t t') by MAP3_DEF + = c + SUM (MAP3 (\x y z. c) lx t t') by SUM + = c + c * LENGTH (MAP3 (\x y z. c) lx t t') by induction hypothesis + = c * (1 + LENGTH (MAP3 (\x y z. c) lx t t') by RIGHT_ADD_DISTRIB + = c * (SUC (LENGTH (MAP3 (\x y z. c) lx t t')) by ADD1 + = c * LENGTH (MAP3 (\x y z. c) (h::lx) (h'::t) (h''::t')) by LENGTH +*) +val SUM_MAP3_K = store_thm( + "SUM_MAP3_K", + ``!lx ly lz c. SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz)``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `ly` >- + rw[] >> + Cases_on `lz` >- + rw[] >> + rw[ADD1]); + +(* ------------------------------------------------------------------------- *) +(* Bounds on Lists *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: SUM ls <= (MAX_LIST ls) * LENGTH ls *) +(* Proof: + By induction on ls. + Base: SUM [] <= MAX_LIST [] * LENGTH [] + LHS = SUM [] = 0 by SUM + RHS = MAX_LIST [] * LENGTH [] + = 0 * 0 = 0 by MAX_LIST, LENGTH + Hence true. + Step: SUM ls <= MAX_LIST ls * LENGTH ls ==> + !h. SUM (h::ls) <= MAX_LIST (h::ls) * LENGTH (h::ls) + SUM (h::ls) + = h + SUM ls by SUM + <= h + MAX_LIST ls * LENGTH ls by induction hypothesis + <= MAX_LIST (h::ls) + MAX_LIST ls * LENGTH ls by MAX_LIST_PROPERTY + <= MAX_LIST (h::ls) + MAX_LIST (h::ls) * LENGTH ls by MAX_LIST_LE + = MAX_LIST (h::ls) * (1 + LENGTH ls) by LEFT_ADD_DISTRIB + = MAX_LIST (h::ls) * LENGTH (h::ls) by LENGTH +*) +val SUM_UPPER = store_thm( + "SUM_UPPER", + ``!ls. SUM ls <= (MAX_LIST ls) * LENGTH ls``, + Induct_on `ls` >- + rw[] >> + strip_tac >> + `SUM (h::ls) <= h + MAX_LIST ls * LENGTH ls` by rw[] >> + `h + MAX_LIST ls * LENGTH ls <= MAX_LIST (h::ls) + MAX_LIST ls * LENGTH ls` by rw[] >> + `MAX_LIST (h::ls) + MAX_LIST ls * LENGTH ls <= MAX_LIST (h::ls) + MAX_LIST (h::ls) * LENGTH ls` by rw[] >> + `MAX_LIST (h::ls) + MAX_LIST (h::ls) * LENGTH ls = MAX_LIST (h::ls) * (1 + LENGTH ls)` by rw[] >> + `_ = MAX_LIST (h::ls) * LENGTH (h::ls)` by rw[] >> + decide_tac); + +(* Theorem: (MIN_LIST ls) * LENGTH ls <= SUM ls *) +(* Proof: + By induction on ls. + Base: MIN_LIST [] * LENGTH [] <= SUM [] + LHS = (MIN_LIST []) * LENGTH [] = 0 by LENGTH + RHS = SUM [] = 0 by SUM + Hence true. + Step: MIN_LIST ls * LENGTH ls <= SUM ls ==> + !h. MIN_LIST (h::ls) * LENGTH (h::ls) <= SUM (h::ls) + If ls = [], + LHS = (MIN_LIST [h]) * LENGTH [h] + = h * 1 = h by MIN_LIST_def, LENGTH + RHS = SUM [h] = h by SUM + Hence true. + If ls <> [], + MIN_LIST (h::ls) * LENGTH (h::ls) + = (MIN h (MIN_LIST ls)) * (1 + LENGTH ls) by MIN_LIST_def, LENGTH + = (MIN h (MIN_LIST ls)) + (MIN h (MIN_LIST ls)) * LENGTH ls + by RIGHT_ADD_DISTRIB + <= h + (MIN_LIST ls) * LENGTH ls by MIN_IS_MIN + <= h + SUM ls by induction hypothesis + = SUM (h::ls) by SUM +*) +val SUM_LOWER = store_thm( + "SUM_LOWER", + ``!ls. (MIN_LIST ls) * LENGTH ls <= SUM ls``, + Induct_on `ls` >- + rw[] >> + strip_tac >> + Cases_on `ls = []` >- + rw[] >> + `MIN_LIST (h::ls) * LENGTH (h::ls) = (MIN h (MIN_LIST ls)) * (1 + LENGTH ls)` by rw[] >> + `_ = (MIN h (MIN_LIST ls)) + (MIN h (MIN_LIST ls)) * LENGTH ls` by rw[] >> + `(MIN h (MIN_LIST ls)) <= h` by rw[] >> + `(MIN h (MIN_LIST ls)) * LENGTH ls <= (MIN_LIST ls) * LENGTH ls` by rw[] >> + rw[]); + +(* Theorem: EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls) *) +(* Proof: + By induction on ls. + Base: EVERY (\x. f x <= g x) [] ==> SUM (MAP f []) <= SUM (MAP g []) + EVERY (\x. f x <= g x) [] = T by EVERY_DEF + SUM (MAP f []) + = SUM [] by MAP + = SUM (MAP g []) by MAP + Step: EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls) ==> + !h. EVERY (\x. f x <= g x) (h::ls) ==> SUM (MAP f (h::ls)) <= SUM (MAP g (h::ls)) + Note f h <= g h /\ + EVERY (\x. f x <= g x) ls by EVERY_DEF + SUM (MAP f (h::ls)) + = SUM (f h :: MAP f ls) by MAP + = f h + SUM (MAP f ls) by SUM + <= g h + SUM (MAP g ls) by above, induction hypothesis + = SUM (g h :: MAP g ls) by SUM + = SUM (MAP g (h::ls)) by MAP +*) +val SUM_MAP_LE = store_thm( + "SUM_MAP_LE", + ``!f g ls. EVERY (\x. f x <= g x) ls ==> SUM (MAP f ls) <= SUM (MAP g ls)``, + rpt strip_tac >> + Induct_on `ls` >> + rw[] >> + rw[] >> + fs[]); + +(* Theorem: EVERY (\x. f x < g x) ls /\ ls <> [] ==> SUM (MAP f ls) < SUM (MAP g ls) *) +(* Proof: + By induction on ls. + Base: EVERY (\x. f x <= g x) [] /\ [] <> [] ==> SUM (MAP f []) <= SUM (MAP g []) + True since [] <> [] = F. + Step: EVERY (\x. f x <= g x) ls ==> ls <> [] ==> SUM (MAP f ls) <= SUM (MAP g ls) ==> + !h. EVERY (\x. f x <= g x) (h::ls) ==> h::ls <> [] ==> SUM (MAP f (h::ls)) <= SUM (MAP g (h::ls)) + Note f h < g h /\ + EVERY (\x. f x < g x) ls by EVERY_DEF + + If ls = [], + SUM (MAP f [h]) + = SUM (f h) by MAP + = f h by SUM + < g h by above + = SUM (g h) by SUM + = SUM (MAP g [h]) by MAP + + If ls <> [], + SUM (MAP f (h::ls)) + = SUM (f h :: MAP f ls) by MAP + = f h + SUM (MAP f ls) by SUM + < g h + SUM (MAP g ls) by induction hypothesis + = SUM (g h :: MAP g ls) by SUM + = SUM (MAP g (h::ls)) by MAP +*) +val SUM_MAP_LT = store_thm( + "SUM_MAP_LT", + ``!f g ls. EVERY (\x. f x < g x) ls /\ ls <> [] ==> SUM (MAP f ls) < SUM (MAP g ls)``, + rpt strip_tac >> + Induct_on `ls` >> + rw[] >> + rw[] >> + (Cases_on `ls = []` >> fs[])); + +(* +MAX_LIST_PROPERTY |- !l x. MEM x l ==> x <= MAX_LIST l +MIN_LIST_PROPERTY |- !l. l <> [] ==> !x. MEM x l ==> MIN_LIST l <= x +*) + +(* Theorem: MONO f ==> !ls e. MEM e (MAP f ls) ==> e <= f (MAX_LIST ls) *) +(* Proof: + Note ?y. (e = f y) /\ MEM y ls by MEM_MAP + and y <= MAX_LIST ls by MAX_LIST_PROPERTY + Thus f y <= f (MAX_LIST ls) by given + or e <= f (MAX_LIST ls) by e = f y +*) +val MEM_MAP_UPPER = store_thm( + "MEM_MAP_UPPER", + ``!f. MONO f ==> !ls e. MEM e (MAP f ls) ==> e <= f (MAX_LIST ls)``, + rpt strip_tac >> + `?y. (e = f y) /\ MEM y ls` by rw[GSYM MEM_MAP] >> + `y <= MAX_LIST ls` by rw[MAX_LIST_PROPERTY] >> + rw[]); + +(* Theorem: MONO2 f ==> !lx ly e. MEM e (MAP2 f lx ly) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) *) +(* Proof: + Note ?ex ey. (e = f ex ey) /\ + MEM ex lx /\ MEM ey ly by MEM_MAP2 + and ex <= MAX_LIST lx by MAX_LIST_PROPERTY + and ey <= MAX_LIST ly by MAX_LIST_PROPERTY + The result follows by the non-decreasing condition on f. +*) +val MEM_MAP2_UPPER = store_thm( + "MEM_MAP2_UPPER", + ``!f. MONO2 f ==> !lx ly e. MEM e (MAP2 f lx ly) ==> e <= f (MAX_LIST lx) (MAX_LIST ly)``, + metis_tac[MEM_MAP2, MAX_LIST_PROPERTY]); + +(* Theorem: MONO3 f ==> + !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) *) +(* Proof: + Note ?ex ey ez. (e = f ex ey ez) /\ + MEM ex lx /\ MEM ey ly /\ MEM ez lz by MEM_MAP3 + and ex <= MAX_LIST lx by MAX_LIST_PROPERTY + and ey <= MAX_LIST ly by MAX_LIST_PROPERTY + and ez <= MAX_LIST lz by MAX_LIST_PROPERTY + The result follows by the non-decreasing condition on f. +*) +val MEM_MAP3_UPPER = store_thm( + "MEM_MAP3_UPPER", + ``!f. MONO3 f ==> + !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> e <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz)``, + metis_tac[MEM_MAP3, MAX_LIST_PROPERTY]); + +(* Theorem: MONO f ==> !ls e. MEM e (MAP f ls) ==> f (MIN_LIST ls) <= e *) +(* Proof: + Note ?y. (e = f y) /\ MEM y ls by MEM_MAP + and ls <> [] by MEM, MEM y ls + then MIN_LIST ls <= y by MIN_LIST_PROPERTY, ls <> [] + Thus f (MIN_LIST ls) <= f y by given + or f (MIN_LIST ls) <= e by e = f y +*) +val MEM_MAP_LOWER = store_thm( + "MEM_MAP_LOWER", + ``!f. MONO f ==> !ls e. MEM e (MAP f ls) ==> f (MIN_LIST ls) <= e``, + rpt strip_tac >> + `?y. (e = f y) /\ MEM y ls` by rw[GSYM MEM_MAP] >> + `ls <> []` by metis_tac[MEM] >> + `MIN_LIST ls <= y` by rw[MIN_LIST_PROPERTY] >> + rw[]); + +(* Theorem: MONO2 f ==> + !lx ly e. MEM e (MAP2 f lx ly) ==> f (MIN_LIST lx) (MIN_LIST ly) <= e *) +(* Proof: + Note ?ex ey. (e = f ex ey) /\ + MEM ex lx /\ MEM ey ly by MEM_MAP2 + and lx <> [] /\ ly <> [] by MEM + and MIN_LIST lx <= ex by MIN_LIST_PROPERTY + and MIN_LIST ly <= ey by MIN_LIST_PROPERTY + The result follows by the non-decreasing condition on f. +*) +val MEM_MAP2_LOWER = store_thm( + "MEM_MAP2_LOWER", + ``!f. MONO2 f ==> + !lx ly e. MEM e (MAP2 f lx ly) ==> f (MIN_LIST lx) (MIN_LIST ly) <= e``, + metis_tac[MEM_MAP2, MEM, MIN_LIST_PROPERTY]); + +(* Theorem: MONO3 f ==> + !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> f (MIN_LIST lx) (MIN_LIST ly) (MIN_LIST lz) <= e *) +(* Proof: + Note ?ex ey ez. (e = f ex ey ez) /\ + MEM ex lx /\ MEM ey ly /\ MEM ez lz by MEM_MAP3 + and lx <> [] /\ ly <> [] /\ lz <> [] by MEM + and MIN_LIST lx <= ex by MIN_LIST_PROPERTY + and MIN_LIST ly <= ey by MIN_LIST_PROPERTY + and MIN_LIST lz <= ez by MIN_LIST_PROPERTY + The result follows by the non-decreasing condition on f. +*) +val MEM_MAP3_LOWER = store_thm( + "MEM_MAP3_LOWER", + ``!f. MONO3 f ==> + !lx ly lz e. MEM e (MAP3 f lx ly lz) ==> f (MIN_LIST lx) (MIN_LIST ly) (MIN_LIST lz) <= e``, + rpt strip_tac >> + `?ex ey ez. (e = f ex ey ez) /\ MEM ex lx /\ MEM ey ly /\ MEM ez lz` by rw[MEM_MAP3] >> + `lx <> [] /\ ly <> [] /\ lz <> []` by metis_tac[MEM] >> + rw[MIN_LIST_PROPERTY]); + +(* Theorem: (!x. f x <= g x) ==> !ls n. EL n (MAP f ls) <= EL n (MAP g ls) *) +(* Proof: + By induction on ls. + Base: !n. EL n (MAP f []) <= EL n (MAP g []) + LHS = EL n [] = RHS by MAP + Step: !n. EL n (MAP f ls) <= EL n (MAP g ls) ==> + !h n. EL n (MAP f (h::ls)) <= EL n (MAP g (h::ls)) + If n = 0, + EL 0 (MAP f (h::ls)) + = EL 0 (f h::MAP f ls) by MAP + = f h by EL + <= g h by given + = EL 0 (g h::MAP g ls) by EL + = EL 0 (MAP g (h::ls)) by MAP + If n <> 0, then n = SUC k by num_CASES + EL n (MAP f (h::ls)) + = EL (SUC k) (f h::MAP f ls) by MAP + = EL k (MAP f ls) by EL + <= EL k (MAP g ls) by induction hypothesis + = EL (SUC k) (g h::MAP g ls) by EL + = EL n (MAP g (h::ls)) by MAP +*) +val MAP_LE = store_thm( + "MAP_LE", + ``!(f:num -> num) g. (!x. f x <= g x) ==> !ls n. EL n (MAP f ls) <= EL n (MAP g ls)``, + ntac 3 strip_tac >> + Induct_on `ls` >- + rw[] >> + Cases_on `n` >- + rw[] >> + rw[]); + +(* Theorem: (!x y. f x y <= g x y) ==> !lx ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly) *) +(* Proof: + By induction on lx. + Base: !ly n. EL n (MAP2 f [] ly) <= EL n (MAP2 g [] ly) + LHS = EL n [] = RHS by MAP2_DEF + Step: !ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly) ==> + !h ly n. EL n (MAP2 f (h::lx) ly) <= EL n (MAP2 g (h::lx) ly) + If ly = [], + to show: EL n (MAP2 f (h::lx) []) <= EL n (MAP2 g (h::lx) []) + True since LHS = EL n [] = RHS by MAP2_DEF + Otherwise, ly = h'::t. + to show: EL n (MAP2 f (h::lx) (h'::t)) <= EL n (MAP2 g (h::lx) (h'::t)) + If n = 0, + EL 0 (MAP2 f (h::lx) (h'::t)) + = EL 0 (f h h'::MAP2 f lx t) by MAP2 + = f h h' by EL + <= g h h' by given + = EL 0 (g h h'::MAP2 g lx t) by EL + = EL 0 (MAP2 g (h::lx) (h'::t)) by MAP2 + If n <> 0, then n = SUC k by num_CASES + EL n (MAP2 f (h::lx) (h'::t)) + = EL (SUC k) (f h h'::MAP2 f lx t) by MAP2 + = EL k (MAP2 f lx t) by EL + <= EL k (MAP2 g lx t) by induction hypothesis + = EL (SUC k) (g h h'::MAP2 g lx t) by EL + = EL n (MAP2 g (h::lx) (h'::t)) by MAP2 +*) +val MAP2_LE = store_thm( + "MAP2_LE", + ``!(f:num -> num -> num) g. (!x y. f x y <= g x y) ==> + !lx ly n. EL n (MAP2 f lx ly) <= EL n (MAP2 g lx ly)``, + ntac 3 strip_tac >> + Induct_on `lx` >- + rw[] >> + rpt strip_tac >> + Cases_on `ly` >- + rw[] >> + Cases_on `n` >- + rw[] >> + rw[]); + +(* Theorem: (!x y z. f x y z <= g x y z) ==> + !lx ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz) *) +(* Proof: + By induction on lx. + Base: !ly lz n. EL n (MAP3 f [] ly lz) <= EL n (MAP3 g [] ly lz) + LHS = EL n [] = RHS by MAP3_DEF + Step: !ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz) ==> + !h ly lz n. EL n (MAP3 f (h::lx) ly lz) <= EL n (MAP3 g (h::lx) ly lz) + If ly = [], + to show: EL n (MAP3 f (h::lx) [] lz) <= EL n (MAP3 g (h::lx) [] lz) + True since LHS = EL n [] = RHS by MAP3_DEF + Otherwise, ly = h'::t. + to show: EL n (MAP3 f (h::lx) (h'::t) lz) <= EL n (MAP3 g (h::lx) (h'::t) lz) + If lz = [], + to show: EL n (MAP3 f (h::lx) (h'::t) []) <= EL n (MAP3 g (h::lx) (h'::t) []) + True since LHS = EL n [] = RHS by MAP3_DEF + Otherwise, lz = h''::t'. + to show: EL n (MAP3 f (h::lx) (h'::t) (h''::t')) <= EL n (MAP3 g (h::lx) (h'::t) (h''::t')) + If n = 0, + EL 0 (MAP3 f (h::lx) (h'::t) (h''::t')) + = EL 0 (f h h' h''::MAP3 f lx t t') by MAP3 + = f h h' h'' by EL + <= g h h' h'' by given + = EL 0 (g h h' h''::MAP3 g lx t t') by EL + = EL 0 (MAP3 g (h::lx) (h'::t) (h''::t')) by MAP3 + If n <> 0, then n = SUC k by num_CASES + EL n (MAP3 f (h::lx) (h'::t) (h''::t')) + = EL (SUC k) (f h h' h''::MAP3 f lx t t') by MAP3 + = EL k (MAP3 f lx t t') by EL + <= EL k (MAP3 g lx t t') by induction hypothesis + = EL (SUC k) (g h h' h''::MAP3 g lx t t') by EL + = EL n (MAP3 g (h::lx) (h'::t) (h''::t')) by MAP3 +*) +val MAP3_LE = store_thm( + "MAP3_LE", + ``!(f:num -> num -> num -> num) g. (!x y z. f x y z <= g x y z) ==> + !lx ly lz n. EL n (MAP3 f lx ly lz) <= EL n (MAP3 g lx ly lz)``, + ntac 3 strip_tac >> + Induct_on `lx` >- + rw[] >> + rpt strip_tac >> + Cases_on `ly` >- + rw[] >> + Cases_on `lz` >- + rw[] >> + Cases_on `n` >- + rw[] >> + rw[]); + +(* +SUM_MAP_PLUS |- !f g ls. SUM (MAP (\x. f x + g x) ls) = SUM (MAP f ls) + SUM (MAP g ls) +SUM_MAP_PLUS_ZIP |- !ls1 ls2. LENGTH ls1 = LENGTH ls2 /\ (!x y. f (x,y) = g x + h y) ==> + SUM (MAP f (ZIP (ls1,ls2))) = SUM (MAP g ls1) + SUM (MAP h ls2) +*) + +(* Theorem: (!x. f1 x <= f2 x) ==> !ls. SUM (MAP f1 ls) <= SUM (MAP f2 ls) *) +(* Proof: + By SUM_LE, this is to show: + (1) !k. k < LENGTH (MAP f1 ls) ==> EL k (MAP f1 ls) <= EL k (MAP f2 ls) + This is true by EL_MAP + (2) LENGTH (MAP f1 ls) = LENGTH (MAP f2 ls) + This is true by LENGTH_MAP +*) +val SUM_MONO_MAP = store_thm( + "SUM_MONO_MAP", + ``!f1 f2. (!x. f1 x <= f2 x) ==> !ls. SUM (MAP f1 ls) <= SUM (MAP f2 ls)``, + rpt strip_tac >> + irule SUM_LE >> + rw[EL_MAP]); + +(* Theorem: (!x y. f1 x y <= f2 x y) ==> !lx ly. SUM (MAP2 f1 lx ly) <= SUM (MAP2 f2 lx ly) *) +(* Proof: + By SUM_LE, this is to show: + (1) !k. k < LENGTH (MAP2 f1 lx ly) ==> EL k (MAP2 f1 lx ly) <= EL k (MAP2 f2 lx ly) + This is true by EL_MAP2, LENGTH_MAP2 + (2) LENGTH (MAP2 f1 lx ly) = LENGTH (MAP2 f2 lx ly) + This is true by LENGTH_MAP2 +*) +val SUM_MONO_MAP2 = store_thm( + "SUM_MONO_MAP2", + ``!f1 f2. (!x y. f1 x y <= f2 x y) ==> !lx ly. SUM (MAP2 f1 lx ly) <= SUM (MAP2 f2 lx ly)``, + rpt strip_tac >> + irule SUM_LE >> + rw[EL_MAP2]); + +(* Theorem: (!x y z. f1 x y z <= f2 x y z) ==> !lx ly lz. SUM (MAP3 f1 lx ly lz) <= SUM (MAP3 f2 lx ly lz) *) +(* Proof: + By SUM_LE, this is to show: + (1) !k. k < LENGTH (MAP3 f1 lx ly lz) ==> EL k (MAP3 f1 lx ly lz) <= EL k (MAP3 f2 lx ly lz) + This is true by EL_MAP3, LENGTH_MAP3 + (2)LENGTH (MAP3 f1 lx ly lz) = LENGTH (MAP3 f2 lx ly lz) + This is true by LENGTH_MAP3 +*) +val SUM_MONO_MAP3 = store_thm( + "SUM_MONO_MAP3", + ``!f1 f2. (!x y z. f1 x y z <= f2 x y z) ==> + !lx ly lz. SUM (MAP3 f1 lx ly lz) <= SUM (MAP3 f2 lx ly lz)``, + rpt strip_tac >> + irule SUM_LE >> + rw[EL_MAP3, LENGTH_MAP3]); + +(* Theorem: MONO f ==> !ls. SUM (MAP f ls) <= f (MAX_LIST ls) * LENGTH ls *) +(* Proof: + Let c = f (MAX_LIST ls). + + Claim: SUM (MAP f ls) <= SUM (MAP (K c) ls) + Proof: By SUM_LE, this is to show: + (1) LENGTH (MAP f ls) = LENGTH (MAP (K c) ls) + This is true by LENGTH_MAP + (2) !k. k < LENGTH (MAP f ls) ==> EL k (MAP f ls) <= EL k (MAP (K c) ls) + Note EL k (MAP f ls) = f (EL k ls) by EL_MAP + and EL k (MAP (K c) ls) + = (K c) (EL k ls) by EL_MAP + = c by K_THM + Now MEM (EL k ls) ls by EL_MEM + so EL k ls <= MAX_LIST ls by MAX_LIST_PROPERTY + Thus f (EL k ls) <= c by property of f + + Note SUM (MAP (K c) ls) = c * LENGTH ls by SUM_MAP_K + Thus SUM (MAP f ls) <= c * LENGTH ls by Claim +*) +val SUM_MAP_UPPER = store_thm( + "SUM_MAP_UPPER", + ``!f. MONO f ==> !ls. SUM (MAP f ls) <= f (MAX_LIST ls) * LENGTH ls``, + rpt strip_tac >> + qabbrev_tac `c = f (MAX_LIST ls)` >> + `SUM (MAP f ls) <= SUM (MAP (K c) ls)` by + ((irule SUM_LE >> rw[]) >> + rw[EL_MAP, EL_MEM, MAX_LIST_PROPERTY, Abbr`c`]) >> + `SUM (MAP (K c) ls) = c * LENGTH ls` by rw[SUM_MAP_K] >> + decide_tac); + +(* Theorem: MONO2 f ==> + !lx ly. SUM (MAP2 f lx ly) <= (f (MAX_LIST lx) (MAX_LIST ly)) * LENGTH (MAP2 f lx ly) *) +(* Proof: + Let c = f (MAX_LIST lx) (MAX_LIST ly). + + Claim: SUM (MAP2 f lx ly) <= SUM (MAP2 (\x y. c) lx ly) + Proof: By SUM_LE, this is to show: + (1) LENGTH (MAP2 f lx ly) = LENGTH (MAP2 (\x y. c) lx ly) + This is true by LENGTH_MAP2 + (2) !k. k < LENGTH (MAP2 f lx ly) ==> EL k (MAP2 f lx ly) <= EL k (MAP2 (\x y. c) lx ly) + Note EL k (MAP2 f lx ly) + = f (EL k lx) (EL k ly) by EL_MAP2 + and EL k (MAP2 (\x y. c) lx ly) + = (\x y. c) (EL k lx) (EL k ly) by EL_MAP2 + = c by function application + Note k < LENGTH lx, k < LENGTH ly by LENGTH_MAP2 + Now MEM (EL k lx) lx by EL_MEM + and MEM (EL k ly) ly by EL_MEM + so EL k lx <= MAX_LIST lx by MAX_LIST_PROPERTY + and EL k ly <= MAX_LIST ly by MAX_LIST_PROPERTY + Thus f (EL k lx) (EL k ly) <= c by property of f + + Note SUM (MAP (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly) by SUM_MAP2_K + and LENGTH (MAP2 (\x y. c) lx ly) = LENGTH (MAP2 f lx ly) by LENGTH_MAP2 + Thus SUM (MAP f lx ly) <= c * LENGTH (MAP2 f lx ly) by Claim +*) +val SUM_MAP2_UPPER = store_thm( + "SUM_MAP2_UPPER", + ``!f. MONO2 f ==> + !lx ly. SUM (MAP2 f lx ly) <= (f (MAX_LIST lx) (MAX_LIST ly)) * LENGTH (MAP2 f lx ly)``, + rpt strip_tac >> + qabbrev_tac `c = f (MAX_LIST lx) (MAX_LIST ly)` >> + `SUM (MAP2 f lx ly) <= SUM (MAP2 (\x y. c) lx ly)` by + ((irule SUM_LE >> rw[]) >> + rw[EL_MAP2, EL_MEM, MAX_LIST_PROPERTY, Abbr`c`]) >> + `SUM (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 (\x y. c) lx ly)` by rw[SUM_MAP2_K, Abbr`c`] >> + `c * LENGTH (MAP2 (\x y. c) lx ly) = c * LENGTH (MAP2 f lx ly)` by rw[] >> + decide_tac); + +(* Theorem: MONO3 f ==> + !lx ly lz. SUM (MAP3 f lx ly lz) <= + f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) * LENGTH (MAP3 f lx ly lz) *) +(* Proof: + Let c = f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz). + + Claim: SUM (MAP3 f lx ly lz) <= SUM (MAP3 (\x y z. c) lx ly lz) + Proof: By SUM_LE, this is to show: + (1) LENGTH (MAP3 f lx ly lz) = LENGTH (MAP3 (\x y z. c) lx ly lz) + This is true by LENGTH_MAP3 + (2) !k. k < LENGTH (MAP3 f lx ly lz) ==> EL k (MAP3 f lx ly lz) <= EL k (MAP3 (\x y z. c) lx ly lz) + Note EL k (MAP3 f lx ly lz) + = f (EL k lx) (EL k ly) (EL k lz) by EL_MAP3 + and EL k (MAP3 (\x y z. c) lx ly lz) + = (\x y z. c) (EL k lx) (EL k ly) (EL k lz) by EL_MAP3 + = c by function application + Note k < LENGTH lx, k < LENGTH ly, k < LENGTH lz + by LENGTH_MAP3 + Now MEM (EL k lx) lx by EL_MEM + and MEM (EL k ly) ly by EL_MEM + and MEM (EL k lz) lz by EL_MEM + so EL k lx <= MAX_LIST lx by MAX_LIST_PROPERTY + and EL k ly <= MAX_LIST ly by MAX_LIST_PROPERTY + and EL k lz <= MAX_LIST lz by MAX_LIST_PROPERTY + Thus f (EL k lx) (EL k ly) (EL k lz) <= c by property of f + + Note SUM (MAP (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz) by SUM_MAP3_K + and LENGTH (MAP3 (\x y z. c) lx ly lz) = LENGTH (MAP3 f lx ly lz) by LENGTH_MAP3 + Thus SUM (MAP f lx ly lz) <= c * LENGTH (MAP3 f lx ly lz) by Claim +*) +val SUM_MAP3_UPPER = store_thm( + "SUM_MAP3_UPPER", + ``!f. MONO3 f ==> + !lx ly lz. SUM (MAP3 f lx ly lz) <= f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz) * LENGTH (MAP3 f lx ly lz)``, + rpt strip_tac >> + qabbrev_tac `c = f (MAX_LIST lx) (MAX_LIST ly) (MAX_LIST lz)` >> + `SUM (MAP3 f lx ly lz) <= SUM (MAP3 (\x y z. c) lx ly lz)` by + (`LENGTH (MAP3 f lx ly lz) = LENGTH (MAP3 (\x y z. c) lx ly lz)` by rw[LENGTH_MAP3] >> + (irule SUM_LE >> rw[]) >> + fs[LENGTH_MAP3] >> + rw[EL_MAP3, EL_MEM, MAX_LIST_PROPERTY, Abbr`c`]) >> + `SUM (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 (\x y z. c) lx ly lz)` by rw[SUM_MAP3_K] >> + `c * LENGTH (MAP3 (\x y z. c) lx ly lz) = c * LENGTH (MAP3 f lx ly lz)` by rw[LENGTH_MAP3] >> + decide_tac); + +(* Theorem: MONO f ==> MONO_INC (GENLIST f n) *) +(* Proof: + Let ls = GENLIST f n. + Then LENGTH ls = n by LENGTH_GENLIST + and !k. k < n ==> EL k ls = f k by EL_GENLIST + Thus MONO_INC ls +*) +val GENLIST_MONO_INC = store_thm( + "GENLIST_MONO_INC", + ``!f:num -> num n. MONO f ==> MONO_INC (GENLIST f n)``, + rw[]); + +(* Theorem: RMONO f ==> MONO_DEC (GENLIST f n) *) +(* Proof: + Let ls = GENLIST f n. + Then LENGTH ls = n by LENGTH_GENLIST + and !k. k < n ==> EL k ls = f k by EL_GENLIST + Thus MONO_DEC ls +*) +val GENLIST_MONO_DEC = store_thm( + "GENLIST_MONO_DEC", + ``!f:num -> num n. RMONO f ==> MONO_DEC (GENLIST f n)``, + rw[]); + +(* Theorem: MONO_INC [m .. n] *) +(* Proof: + This is to show: + !j k. j <= k /\ k < LENGTH [m .. n] ==> EL j [m .. n] <= EL k [m .. n] + Note LENGTH [m .. n] = n + 1 - m by listRangeINC_LEN + so m + j <= n by j < LENGTH [m .. n] + ==> EL j [m .. n] = m + j by listRangeINC_EL + also m + k <= n by k < LENGTH [m .. n] + ==> EL k [m .. n] = m + k by listRangeINC_EL + Thus EL j [m .. n] <= EL k [m .. n] by arithmetic +*) +Theorem listRangeINC_MONO_INC: + !m n. MONO_INC [m .. n] +Proof + simp[listRangeINC_EL, listRangeINC_LEN] +QED + +(* Theorem: MONO_INC [m ..< n] *) +(* Proof: + This is to show: + !j k. j <= k /\ k < LENGTH [m ..< n] ==> EL j [m ..< n] <= EL k [m ..< n] + Note LENGTH [m ..< n] = n - m by listRangeLHI_LEN + so m + j < n by j < LENGTH [m ..< n] + ==> EL j [m ..< n] = m + j by listRangeLHI_EL + also m + k < n by k < LENGTH [m ..< n] + ==> EL k [m ..< n] = m + k by listRangeLHI_EL + Thus EL j [m ..< n] <= EL k [m ..< n] by arithmetic +*) +Theorem listRangeLHI_MONO_INC: + !m n. MONO_INC [m ..< n] +Proof + simp[listRangeLHI_EL] +QED + +(* ------------------------------------------------------------------------- *) +(* List Dilation *) +(* ------------------------------------------------------------------------- *) + +(* +Use the concept of dilating a list. + +Let p = [1;2;3], that is, p = 1 + 2x + 3x^2. +Then q = peval p (x^3) is just q = 1 + 2(x^3) + 3(x^3)^2 = [1;0;0;2;0;0;3] + +DILATE 3 [] = [] +DILATE 3 (h::t) = [h;0;0] ++ MDILATE 3 t + +val DILATE_3_DEF = Define` + (DILATE_3 [] = []) /\ + (DILATE_3 (h::t) = [h;0;0] ++ (MDILATE_3 t)) +`; +> EVAL ``DILATE_3 [1;2;3]``; +val it = |- MDILATE_3 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3; 0; 0]: thm + +val DILATE_3_DEF = Define` + (DILATE_3 [] = []) /\ + (DILATE_3 [h] = [h]) /\ + (DILATE_3 (h::t) = [h;0;0] ++ (MDILATE_3 t)) +`; +> EVAL ``DILATE_3 [1;2;3]``; +val it = |- MDILATE_3 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3]: thm +*) + +(* ------------------------------------------------------------------------- *) +(* List Dilation (Multiplicative) *) +(* ------------------------------------------------------------------------- *) + +(* Note: + It would be better to define: MDILATE e n l = inserting n (e)'s, + that is, using GENLIST (K e) n, so that only MDILATE e 0 l = l. + However, the intention is to have later, for polynomials: + peval p (X ** n) = pdilate n p + and since X ** 1 = X, and peval p X = p, + it is desirable to have MDILATE e 1 l = l, with the definition below. + + However, the multiplicative feature at the end destroys such an application. +*) + +(* Dilate a list with an element e, for a factor n (n <> 0) *) +val MDILATE_def = Define` + (MDILATE e n [] = []) /\ + (MDILATE e n (h::t) = if t = [] then [h] else (h:: GENLIST (K e) (PRE n)) ++ (MDILATE e n t)) +`; +(* +> EVAL ``MDILATE 0 2 [1;2;3]``; +val it = |- MDILATE 0 2 [1; 2; 3] = [1; 0; 2; 0; 3]: thm +> EVAL ``MDILATE 0 3 [1;2;3]``; +val it = |- MDILATE 0 3 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3]: thm +> EVAL ``MDILATE #0 3 [a;b;#1]``; +val it = |- MDILATE #0 3 [a; b; #1] = [a; #0; #0; b; #0; #0; #1]: thm +*) + +(* Theorem: MDILATE e n [] = [] *) +(* Proof: by MDILATE_def *) +val MDILATE_NIL = store_thm( + "MDILATE_NIL", + ``!e n. MDILATE e n [] = []``, + rw[MDILATE_def]); + +(* export simple result *) +val _ = export_rewrites["MDILATE_NIL"]; + +(* Theorem: MDILATE e n [x] = [x] *) +(* Proof: by MDILATE_def *) +val MDILATE_SING = store_thm( + "MDILATE_SING", + ``!e n x. MDILATE e n [x] = [x]``, + rw[MDILATE_def]); + +(* export simple result *) +val _ = export_rewrites["MDILATE_SING"]; + +(* Theorem: MDILATE e n (h::t) = + if t = [] then [h] else (h:: GENLIST (K e) (PRE n)) ++ (MDILATE e n t) *) +(* Proof: by MDILATE_def *) +val MDILATE_CONS = store_thm( + "MDILATE_CONS", + ``!e n h t. MDILATE e n (h::t) = + if t = [] then [h] else (h:: GENLIST (K e) (PRE n)) ++ (MDILATE e n t)``, + rw[MDILATE_def]); + +(* Theorem: MDILATE e 1 l = l *) +(* Proof: + By induction on l. + Base: !e. MDILATE e 1 [] = [], true by MDILATE_NIL + Step: !e. MDILATE e 1 l = l ==> !h e. MDILATE e 1 (h::l) = h::l + If l = [], + MDILATE e 1 [h] + = [h] by MDILATE_SING + If l <> [], + MDILATE e 1 (h::l) + = (h:: GENLIST (K e) (PRE 1)) ++ (MDILATE e n l) by MDILATE_CONS + = (h:: GENLIST (K e) (PRE 1)) ++ l by induction hypothesis + = (h:: GENLIST (K e) 0) ++ l by PRE + = [h] ++ l by GENLIST_0 + = h::l by CONS_APPEND +*) +val MDILATE_1 = store_thm( + "MDILATE_1", + ``!l e. MDILATE e 1 l = l``, + Induct_on `l` >> + rw[MDILATE_def]); + +(* Theorem: MDILATE e 0 l = l *) +(* Proof: + By induction on l, and note GENLIST (K e) (PRE 0) = GENLIST (K e) 0 = []. +*) +val MDILATE_0 = store_thm( + "MDILATE_0", + ``!l e. MDILATE e 0 l = l``, + Induct_on `l` >> rw[MDILATE_def]); + +(* Theorem: LENGTH (MDILATE e n l) = + if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l)) *) +(* Proof: + If n = 0, + Then MDILATE e 0 l = l by MDILATE_0 + Hence true. + If n <> 0, + Then 0 < n by NOT_ZERO_LT_ZERO + By induction on l. + Base: LENGTH (MDILATE e n []) = if n = 0 then LENGTH [] else if [] = [] then 0 else SUC (n * PRE (LENGTH [])) + LENGTH (MDILATE e n []) + = LENGTH [] by MDILATE_NIL + = 0 by LENGTH_NIL + Step: LENGTH (MDILATE e n l) = if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l)) ==> + !h. LENGTH (MDILATE e n (h::l)) = if n = 0 then LENGTH (h::l) else if h::l = [] then 0 else SUC (n * PRE (LENGTH (h::l))) + Note h::l = [] <=> F by NOT_CONS_NIL + If l = [], + LENGTH (MDILATE e n [h]) + = LENGTH [h] by MDILATE_SING + = 1 by LENGTH_EQ_1 + = SUC 0 by ONE + = SUC (n * 0) by MULT_0 + = SUC (n * (PRE (LENGTH [h]))) by LENGTH_EQ_1, PRE_SUC_EQ + If l <> [], + Then LENGTH l <> 0 by LENGTH_NIL + LENGTH (MDILATE e n (h::l)) + = LENGTH (h:: GENLIST (K e) (PRE n) ++ MDILATE e n l) by MDILATE_CONS + = LENGTH (h:: GENLIST (K e) (PRE n)) + LENGTH (MDILATE e n l) by LENGTH_APPEND + = n + LENGTH (MDILATE e n l) by LENGTH_GENLIST + = n + SUC (n * PRE (LENGTH l)) by induction hypothesis + = SUC (n + n * PRE (LENGTH l)) by ADD_SUC + = SUC (n * SUC (PRE (LENGTH l))) by MULT_SUC + = SUC (n * LENGTH l) by SUC_PRE, 0 < LENGTH l + = SUC (n * PRE (LENGTH (h::l))) by LENGTH, PRE_SUC_EQ +*) +val MDILATE_LENGTH = store_thm( + "MDILATE_LENGTH", + ``!l e n. LENGTH (MDILATE e n l) = + if n = 0 then LENGTH l else if l = [] then 0 else SUC (n * PRE (LENGTH l))``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[MDILATE_0] >> + `0 < n` by decide_tac >> + Induct_on `l` >- + rw[] >> + rw[MDILATE_def] >> + `LENGTH l <> 0` by metis_tac[LENGTH_NIL] >> + `0 < LENGTH l` by decide_tac >> + `PRE n + SUC (n * PRE (LENGTH l)) = SUC (PRE n) + n * PRE (LENGTH l)` by rw[] >> + `_ = n + n * PRE (LENGTH l)` by decide_tac >> + `_ = n * SUC (PRE (LENGTH l))` by rw[MULT_SUC] >> + `_ = n * LENGTH l` by metis_tac[SUC_PRE] >> + decide_tac); + +(* Theorem: LENGTH l <= LENGTH (MDILATE e n l) *) +(* Proof: + If n = 0, + LENGTH (MDILATE e 0 l) + = LENGTH l by MDILATE_LENGTH + >= LENGTH l + If l = [], + LENGTH (MDILATE e n []) + = LENGTH [] by MDILATE_NIL + >= LENGTH [] + If l <> [], + Then ?h t. l = h::t by list_CASES + LENGTH (MDILATE e n (h::t)) + = SUC (n * PRE (LENGTH (h::t))) by MDILATE_LENGTH + = SUC (n * PRE (SUC (LENGTH t))) by LENGTH + = SUC (n * LENGTH t) by PRE + = n * LENGTH t + 1 by ADD1 + >= LENGTH t + 1 by LE_MULT_CANCEL_LBARE, 0 < n + = SUC (LENGTH t) by ADD1 + = LENGTH (h::t) by LENGTH +*) +val MDILATE_LENGTH_LOWER = store_thm( + "MDILATE_LENGTH_LOWER", + ``!l e n. LENGTH l <= LENGTH (MDILATE e n l)``, + rw[MDILATE_LENGTH] >> + `?h t. l = h::t` by metis_tac[list_CASES] >> + rw[]); + +(* Theorem: 0 < n ==> LENGTH (MDILATE e n l) <= SUC (n * PRE (LENGTH l)) *) +(* Proof: + Since n <> 0, + If l = [], + LENGTH (MDILATE e n []) + = LENGTH [] by MDILATE_NIL + = 0 by LENGTH_NIL + SUC (n * PRE (LENGTH [])) + = SUC (n * PRE 0) by LENGTH_NIL + = SUC 0 by PRE, MULT_0 + > 0 by LESS_SUC + If l <> [], + LENGTH (MDILATE e n l) + = SUC (n * PRE (LENGTH l)) by MDILATE_LENGTH, n <> 0 +*) +val MDILATE_LENGTH_UPPER = store_thm( + "MDILATE_LENGTH_UPPER", + ``!l e n. 0 < n ==> LENGTH (MDILATE e n l) <= SUC (n * PRE (LENGTH l))``, + rw[MDILATE_LENGTH]); + +(* Theorem: k < LENGTH (MDILATE e n l) ==> + (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e) *) +(* Proof: + If n = 0, + Then MDILATE e 0 l = l by MDILATE_0 + Hence true trivially. + If n <> 0, + Then 0 < n by NOT_ZERO_LT_ZERO + By induction on l. + Base: !k. k < LENGTH (MDILATE e n []) ==> + (EL k (MDILATE e n []) = if n = 0 then EL k [] else if k MOD n = 0 then EL (k DIV n) [] else e) + Note LENGTH (MDILATE e n []) + = LENGTH [] by MDILATE_NIL + = 0 by LENGTH_NIL + Thus k < 0 <=> F by NOT_ZERO_LT_ZERO + Step: !k. k < LENGTH (MDILATE e n l) ==> (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e) ==> + !h k. k < LENGTH (MDILATE e n (h::l)) ==> (EL k (MDILATE e n (h::l)) = if n = 0 then EL k (h::l) else if k MOD n = 0 then EL (k DIV n) (h::l) else e) + Note LENGTH (MDILATE e n [h]) = 1 by MDILATE_SING + and LENGTH (MDILATE e n (h::l)) + = SUC (n * PRE (LENGTH (h::l))) by MDILATE_LENGTH, n <> 0 + = SUC (n * PRE (SUC (LENGTH l))) by LENGTH + = SUC (n * LENGTH l) by PRE + + If l = [], + Then MDILATE e n [h] = [h] by MDILATE_SING + and LENGTH (MDILATE e n [h]) = 1 by LENGTH + so k < 1 means k = 0. + and 0 DIV n = 0 by ZERO_DIV, 0 < n + and 0 MOD n = 0 by ZERO_MOD, 0 < n + Thus EL k [h] = EL (k DIV n) [h]. + + If l <> [], + Let t = h::GENLIST (K e) (PRE n) + Note LENGTH t = n by LENGTH_GENLIST + If k < n, + Then k MOD n = k by LESS_MOD, k < n + EL k (MDILATE e n (h::l)) + = EL k (t ++ MDILATE e n l) by MDILATE_CONS + = EL k t by EL_APPEND, k < LENGTH t + If k = 0, + EL 0 t + = EL 0 (h:: GENLIST (K e) (PRE n)) by notation of t + = h + = EL (0 DIV n) (h::l) by EL, HD + If k <> 0, + EL k t + = EL k (h:: GENLIST (K e) (PRE n)) by notation of t + = EL (PRE k) (GENLIST (K e) (PRE n)) by EL_CONS + = (K e) (PRE k) by EL_GENLIST, PRE k < PRE n + = e by application of K + If ~(k < n), n <= k. + Given k < LENGTH (MDILATE e n (h::l)) + or k < SUC (n * LENGTH l) by above + ==> k - n < SUC (n * LENGTH l) - n by n <= k + = SUC (n * LENGTH l - n) by SUB + = SUC (n * (LENGTH l - 1)) by LEFT_SUB_DISTRIB + = SUC (n * PRE (LENGTH l)) by PRE_SUB1 + or k - n < LENGTH (MDILATE e n l) by MDILATE_LENGTH + Thus (k - n) MOD n = k MOD n by SUB_MOD + and (k - n) DIV n = k DIV n - 1 by SUB_DIV + If k MOD n = 0, + Note 0 < k DIV n by DIVIDES_MOD_0, DIV_POS + EL k (t ++ MDILATE e n l) + = EL (k - n) (MDILATE e n l) by EL_APPEND, n <= k + = EL (k DIV n - 1) l by induction hypothesis, (k - n) MOD n = 0 + = EL (PRE (k DIV n)) l by PRE_SUB1 + = EL (k DIV n) (h::l) by EL_CONS, 0 < k DIV n + If k MOD n <> 0, + EL k (t ++ MDILATE e n l) + = EL (k - n) (MDILATE e n l) by EL_APPEND, n <= k + = e by induction hypothesis, (k - n) MOD n <> 0 +*) +val MDILATE_EL = store_thm( + "MDILATE_EL", + ``!l e n k. k < LENGTH (MDILATE e n l) ==> + (EL k (MDILATE e n l) = if n = 0 then EL k l else if k MOD n = 0 then EL (k DIV n) l else e)``, + ntac 3 strip_tac >> + Cases_on `n = 0` >- + rw[MDILATE_0] >> + `0 < n` by decide_tac >> + Induct_on `l` >- + rw[] >> + rpt strip_tac >> + `LENGTH (MDILATE e n [h]) = 1` by rw[MDILATE_SING] >> + `LENGTH (MDILATE e n (h::l)) = SUC (n * LENGTH l)` by rw[MDILATE_LENGTH] >> + qabbrev_tac `t = h:: GENLIST (K e) (PRE n)` >> + `!k. k < 1 <=> (k = 0)` by decide_tac >> + rw_tac std_ss[MDILATE_def] >- + metis_tac[ZERO_DIV] >- + metis_tac[ZERO_MOD] >- + (rw_tac std_ss[EL_APPEND] >| [ + `LENGTH t = n` by rw[Abbr`t`] >> + `k MOD n = k` by rw[LESS_MOD] >> + `!x. EL 0 (h::x) = h` by rw[] >> + metis_tac[ZERO_DIV], + `LENGTH t = n` by rw[Abbr`t`] >> + `k - n < LENGTH (MDILATE e n l)` by rw[MDILATE_LENGTH] >> + `(k - n) MOD n = k MOD n` by rw[SUB_MOD] >> + `(k - n) DIV n = k DIV n - 1` by rw[GSYM SUB_DIV] >> + `0 < k DIV n` by rw[DIVIDES_MOD_0, DIV_POS] >> + `EL (k - n) (MDILATE e n l) = EL (k DIV n - 1) l` by rw[] >> + `_ = EL (PRE (k DIV n)) l` by rw[PRE_SUB1] >> + `_ = EL (k DIV n) (h::l)` by rw[EL_CONS] >> + rw[] + ]) >> + rw_tac std_ss[EL_APPEND] >| [ + `LENGTH t = n` by rw[Abbr`t`] >> + `k MOD n = k` by rw[LESS_MOD] >> + `0 < k /\ PRE k < PRE n` by decide_tac >> + `EL k t = EL (PRE k) (GENLIST (K e) (PRE n))` by rw[EL_CONS, Abbr`t`] >> + `_ = e` by rw[] >> + rw[], + `LENGTH t = n` by rw[Abbr`t`] >> + `k - n < LENGTH (MDILATE e n l)` by rw[MDILATE_LENGTH] >> + `n <= k` by decide_tac >> + `(k - n) MOD n = k MOD n` by rw[SUB_MOD] >> + `EL (k - n) (MDILATE e n l) = e` by rw[] >> + rw[] + ]); + +(* This is a milestone theorem. *) + +(* Theorem: (MDILATE e n l = []) <=> (l = []) *) +(* Proof: + If part: MDILATE e n l = [] ==> l = [] + By contradiction, suppose l <> []. + If n = 0, + Then MDILATE e 0 l = l by MDILATE_0 + This contradicts MDILATE e 0 l = []. + If n <> 0, + Then LENGTH (MDILATE e n l) + = SUC (n * PRE (LENGTH l)) by MDILATE_LENGTH + <> 0 by SUC_NOT + So (MDILATE e n l) <> [] by LENGTH_NIL + This contradicts MDILATE e n l = [] + Only-if part: l = [] ==> MDILATE e n l = [] + True by MDILATE_NIL +*) +val MDILATE_EQ_NIL = store_thm( + "MDILATE_EQ_NIL", + ``!l e n. (MDILATE e n l = []) <=> (l = [])``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + Cases_on `n = 0` >| [ + `MDILATE e 0 l = l` by rw[GSYM MDILATE_0] >> + metis_tac[], + `LENGTH (MDILATE e n l) = SUC (n * PRE (LENGTH l))` by rw[MDILATE_LENGTH] >> + `LENGTH (MDILATE e n l) <> 0` by decide_tac >> + metis_tac[LENGTH_EQ_0] + ]); + +(* Theorem: LAST (MDILATE e n l) = LAST l *) +(* Proof: + If l = [], + LAST (MDILATE e n []) + = LAST [] by MDILATE_NIL + If l <> [], + If n = 0, + LAST (MDILATE e 0 l) + = LAST l by MDILATE_0 + If n <> 0, then 0 < m by LESS_0 + Then MDILATE e n l <> [] by MDILATE_EQ_NIL + or LENGTH (MDILATE e n l) <> 0 by LENGTH_NIL + Note PRE (LENGTH (MDILATE e n l)) + = PRE (SUC (n * PRE (LENGTH l))) by MDILATE_LENGTH + = n * PRE (LENGTH l) by PRE + Let k = PRE (LENGTH (MDILATE e n l)). + Then k < LENGTH (MDILATE e n l) by PRE x < x + and k MOD n = 0 by MOD_EQ_0, MULT_COMM, 0 < n + and k DIV n = PRE (LENGTH l) by MULT_DIV, MULT_COMM + + LAST (MDILATE e n l) + = EL k (MDILATE e n l) by LAST_EL + = EL (k DIV n) l by MDILATE_EL + = EL (PRE (LENGTH l)) l by above + = LAST l by LAST_EL +*) +val MDILATE_LAST = store_thm( + "MDILATE_LAST", + ``!l e n. LAST (MDILATE e n l) = LAST l``, + rpt strip_tac >> + Cases_on `l = []` >- + rw[] >> + Cases_on `n = 0` >- + rw[MDILATE_0] >> + `0 < n` by decide_tac >> + `MDILATE e n l <> []` by rw[MDILATE_EQ_NIL] >> + `LENGTH (MDILATE e n l) <> 0` by metis_tac[LENGTH_NIL] >> + qabbrev_tac `k = PRE (LENGTH (MDILATE e n l))` >> + rw[LAST_EL] >> + `k = n * PRE (LENGTH l)` by rw[MDILATE_LENGTH, Abbr`k`] >> + `k MOD n = 0` by metis_tac[MOD_EQ_0, MULT_COMM] >> + `k DIV n = PRE (LENGTH l)` by metis_tac[MULT_DIV, MULT_COMM] >> + `k < LENGTH (MDILATE e n l)` by rw[Abbr`k`] >> + rw[MDILATE_EL]); + +(* +Succesive dilation: + +> EVAL ``MDILATE #0 3 [a; b; c]``; +val it = |- MDILATE #0 3 [a; b; c] = [a; #0; #0; b; #0; #0; c]: thm +> EVAL ``MDILATE #0 4 [a; b; c]``; +val it = |- MDILATE #0 4 [a; b; c] = [a; #0; #0; #0; b; #0; #0; #0; c]: thm +> EVAL ``MDILATE #0 1 (MDILATE #0 3 [a; b; c])``; +val it = |- MDILATE #0 1 (MDILATE #0 3 [a; b; c]) = [a; #0; #0; b; #0; #0; c]: thm +> EVAL ``MDILATE #0 2 (MDILATE #0 3 [a; b; c])``; +val it = |- MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = [a; #0; #0; #0; #0; #0; b; #0; #0; #0; #0; #0; c]: thm +> EVAL ``MDILATE #0 2 (MDILATE #0 2 [a; b; c])``; +val it = |- MDILATE #0 2 (MDILATE #0 2 [a; b; c]) = [a; #0; #0; #0; b; #0; #0; #0; c]: thm +> EVAL ``MDILATE #0 2 (MDILATE #0 2 [a; b; c]) = MDILATE #0 4 [a; b; c]``; +val it = |- (MDILATE #0 2 (MDILATE #0 2 [a; b; c]) = MDILATE #0 4 [a; b; c]) <=> T: thm +> EVAL ``MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 5 [a; b; c]``; +val it = |- (MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 5 [a; b; c]) <=> F: thm +> EVAL ``MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 6 [a; b; c]``; +val it = |- (MDILATE #0 2 (MDILATE #0 3 [a; b; c]) = MDILATE #0 6 [a; b; c]) <=> T: thm + +So successive dilation is related to product, or factorisation, or primes: +MDILATE e m (MDILATE e n l) = MDILATE e (m * n) l, for 0 < m, 0 < n. + +*) + +(* ------------------------------------------------------------------------- *) +(* List Dilation (Additive) *) +(* ------------------------------------------------------------------------- *) + +(* Dilate by inserting m zeroes, at position n of tail *) +Definition DILATE_def: + (DILATE e n m [] = []) /\ + (DILATE e n m [h] = [h]) /\ + (DILATE e n m (h::t) = h:: (TAKE n t ++ (GENLIST (K e) m) ++ DILATE e n m (DROP n t))) +Termination + WF_REL_TAC `measure (λ(a,b,c,d). LENGTH d)` >> + rw[LENGTH_DROP] +End + +(* +> EVAL ``DILATE 0 0 1 [1;2;3]``; +val it = |- DILATE 0 0 1 [1; 2; 3] = [1; 0; 2; 0; 3]: thm +> EVAL ``DILATE 0 0 2 [1;2;3]``; +val it = |- DILATE 0 0 2 [1; 2; 3] = [1; 0; 0; 2; 0; 0; 3]: thm +> EVAL ``DILATE 0 1 1 [1;2;3]``; +val it = |- DILATE 0 1 1 [1; 2; 3] = [1; 2; 0; 3]: thm +> EVAL ``DILATE 0 1 1 (DILATE 0 0 1 [1;2;3])``; +val it = |- DILATE 0 1 1 (DILATE 0 0 1 [1; 2; 3]) = [1; 0; 0; 2; 0; 0; 3]: thm +> EVAL ``DILATE 0 0 3 [1;2;3]``; +val it = |- DILATE 0 0 3 [1; 2; 3] = [1; 0; 0; 0; 2; 0; 0; 0; 3]: thm +> EVAL ``DILATE 0 1 1 (DILATE 0 0 2 [1;2;3])``; +val it = |- DILATE 0 1 1 (DILATE 0 0 2 [1; 2; 3]) = [1; 0; 0; 0; 2; 0; 0; 0; 0; 3]: thm +> EVAL ``DILATE 0 0 3 [1;2;3] = DILATE 0 2 1 (DILATE 0 0 2 [1;2;3])``; +val it = |- (DILATE 0 0 3 [1; 2; 3] = DILATE 0 2 1 (DILATE 0 0 2 [1; 2; 3])) <=> T: thm + +> EVAL ``DILATE 0 0 0 [1;2;3]``; +val it = |- DILATE 0 0 0 [1; 2; 3] = [1; 2; 3]: thm +> EVAL ``DILATE 1 0 0 [1;2;3]``; +val it = |- DILATE 1 0 0 [1; 2; 3] = [1; 2; 3]: thm +> EVAL ``DILATE 1 0 1 [1;2;3]``; +val it = |- DILATE 1 0 1 [1; 2; 3] = [1; 1; 2; 1; 3]: thm +> EVAL ``DILATE 1 1 1 [1;2;3]``; +val it = |- DILATE 1 1 1 [1; 2; 3] = [1; 2; 1; 3]: thm +> EVAL ``DILATE 1 1 2 [1;2;3]``; +val it = |- DILATE 1 1 2 [1; 2; 3] = [1; 2; 1; 1; 3]: thm +> EVAL ``DILATE 1 1 3 [1;2;3]``; +val it = |- DILATE 1 1 3 [1; 2; 3] = [1; 2; 1; 1; 1; 3]: thm +*) + +(* Theorem: DILATE e n m [] = [] *) +(* Proof: by DILATE_def *) +val DILATE_NIL = save_thm("DILATE_NIL", DILATE_def |> CONJUNCT1); +(* val DILATE_NIL = |- !n m e. DILATE e n m [] = []: thm *) + +(* export simple result *) +val _ = export_rewrites["DILATE_NIL"]; + +(* Theorem: DILATE e n m [h] = [h] *) +(* Proof: by DILATE_def *) +val DILATE_SING = save_thm("DILATE_SING", DILATE_def |> CONJUNCT2 |> CONJUNCT1); +(* val DILATE_SING = |- !n m h e. DILATE e n m [h] = [h]: thm *) + +(* export simple result *) +val _ = export_rewrites["DILATE_SING"]; + +(* Theorem: DILATE e n m (h::t) = + if t = [] then [h] else h:: (TAKE n t ++ (GENLIST (K e) m) ++ DILATE e n m (DROP n t)) *) +(* Proof: by DILATE_def, list_CASES *) +val DILATE_CONS = store_thm( + "DILATE_CONS", + ``!n m h t e. DILATE e n m (h::t) = + if t = [] then [h] else h:: (TAKE n t ++ (GENLIST (K e) m) ++ DILATE e n m (DROP n t))``, + metis_tac[DILATE_def, list_CASES]); + +(* Theorem: DILATE e 0 n (h::t) = if t = [] then [h] else h::(GENLIST (K e) n ++ DILATE e 0 n t) *) +(* Proof: + If t = [], + DILATE e 0 n (h::t) = [h] by DILATE_CONS + If t <> [], + DILATE e 0 n (h::t) + = h:: (TAKE 0 t ++ (GENLIST (K e) n) ++ DILATE e 0 n (DROP 0 t)) by DILATE_CONS + = h:: ([] ++ (GENLIST (K e) n) ++ DILATE e 0 n t) by TAKE_0, DROP_0 + = h:: (GENLIST (K e) n ++ DILATE e 0 n t) by APPEND +*) +val DILATE_0_CONS = store_thm( + "DILATE_0_CONS", + ``!n h t e. DILATE e 0 n (h::t) = if t = [] then [h] else h::(GENLIST (K e) n ++ DILATE e 0 n t)``, + rw[DILATE_CONS]); + +(* Theorem: DILATE e 0 0 l = l *) +(* Proof: + By induction on l. + Base: DILATE e 0 0 [] = [], true by DILATE_NIL + Step: DILATE e 0 0 l = l ==> !h. DILATE e 0 0 (h::l) = h::l + If l = [], + DILATE e 0 0 [h] = [h] by DILATE_SING + If l <> [], + DILATE e 0 0 (h::l) + = h::(GENLIST (K e) 0 ++ DILATE e 0 0 l) by DILATE_0_CONS + = h::([] ++ DILATE e 0 0 l) by GENLIST_0 + = h:: DILATE e 0 0 l by APPEND + = h::l by induction hypothesis +*) +val DILATE_0_0 = store_thm( + "DILATE_0_0", + ``!l e. DILATE e 0 0 l = l``, + Induct >> + rw[DILATE_0_CONS]); + +(* Theorem: DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l) *) +(* Proof: + If n = 0, + DILATE e 0 1 l = DILATE e 0 1 (DILATE e 0 0 l) by DILATE_0_0 + If n <> 0, + GENLIST (K e) n <> [] by LENGTH_GENLIST, LENGTH_NIL + By induction on l. + Base: DILATE e 0 (SUC n) [] = DILATE e n 1 (DILATE e 0 n []) + DILATE e 0 (SUC n) [] = [] by DILATE_NIL + DILATE e n 1 (DILATE e 0 n []) + = DILATE e n 1 [] = [] by DILATE_NIL + Step: DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l) ==> + !h. DILATE e 0 (SUC n) (h::l) = DILATE e n 1 (DILATE e 0 n (h::l)) + If l = [], + DILATE e 0 (SUC n) [h] = [h] by DILATE_SING + DILATE e n 1 (DILATE e 0 n [h]) + = DILATE e n 1 [h] = [h] by DILATE_SING + If l <> [], + DILATE e 0 (SUC n) (h::l) + = h::(GENLIST (K e) (SUC n) ++ DILATE e 0 (SUC n) l) by DILATE_0_CONS + = h::(GENLIST (K e) (SUC n) ++ DILATE e n 1 (DILATE e 0 n l)) by induction hypothesis + + Note LENGTH (GENLIST (K e) n) = n by LENGTH_GENLIST + so (GENLIST (K e) n ++ DILATE e 0 n l) <> [] by APPEND_eq_NIL, LENGTH_NIL [1] + and TAKE n (GENLIST (K e) n ++ DILATE e 0 n l) = GENLIST (K e) n by TAKE_LENGTH_APPEND [2] + and DROP n (GENLIST (K e) n ++ DILATE e 0 n l) = DILATE e 0 n l by DROP_LENGTH_APPEND [3] + and GENLIST (K e) (SUC n) + = GENLIST (K e) (1 + n) by SUC_ONE_ADD + = GENLIST (K e) n ++ GENLIST (K e) 1 by GENLIST_K_ADD [4] + + DILATE e n 1 (DILATE e 0 n (h::l)) + = DILATE e n 1 (h::(GENLIST (K e) n ++ DILATE e 0 n l)) by DILATE_0_CONS + = h::(TAKE n (GENLIST (K e) n ++ DILATE e 0 n l) ++ GENLIST (K e) 1 ++ + DILATE e n 1 (DROP n (GENLIST (K e) n ++ DILATE e 0 n l))) by DILATE_CONS, [1] + = h::(GENLIST (K e) n ++ GENLIST (K e) 1 ++ DILATE e n 1 (DILATE e 0 n l)) by above [2], [3] + = h::(GENLIST (K e) (SUC n) ++ DILATE e n 1 (DILATE e 0 n l)) by above [4] +*) +val DILATE_0_SUC = store_thm( + "DILATE_0_SUC", + ``!l e n. DILATE e 0 (SUC n) l = DILATE e n 1 (DILATE e 0 n l)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[DILATE_0_0] >> + Induct_on `l` >- + rw[] >> + rpt strip_tac >> + Cases_on `l = []` >- + rw[DILATE_SING] >> + qabbrev_tac `a = GENLIST (K e) n ++ DILATE e 0 n l` >> + `LENGTH (GENLIST (K e) n) = n` by rw[] >> + `a <> []` by metis_tac[APPEND_eq_NIL, LENGTH_NIL] >> + `TAKE n a = GENLIST (K e) n` by metis_tac[TAKE_LENGTH_APPEND] >> + `DROP n a = DILATE e 0 n l` by metis_tac[DROP_LENGTH_APPEND] >> + `GENLIST (K e) (SUC n) = GENLIST (K e) n ++ GENLIST (K e) 1` by rw_tac std_ss[SUC_ONE_ADD, GENLIST_K_ADD] >> + metis_tac[DILATE_0_CONS, DILATE_CONS]); + +(* Theorem: LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l)) *) +(* Proof: + By induction on l. + Base: LENGTH (DILATE e 0 n []) = 0 + LENGTH (DILATE e 0 n []) + = LENGTH [] by DILATE_NIL + = 0 by LENGTH_NIL + Step: LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l)) ==> + !h. LENGTH (DILATE e 0 n (h::l)) = SUC (SUC n * PRE (LENGTH (h::l))) + If l = [], + LENGTH (DILATE e 0 n [h]) + = LENGTH [h] by DILATE_SING + = 1 by LENGTH + SUC (SUC n * PRE (LENGTH [h]) + = SUC (SUC n * PRE 1) by LENGTH + = SUC (SUC n * 0) by PRE_SUB1 + = SUC 0 by MULT_0 + = 1 by ONE + If l <> [], + Note LENGTH l <> 0 by LENGTH_NIL + LENGTH (DILATE e 0 n (h::l)) + = LENGTH (h::(GENLIST (K e) n ++ DILATE e 0 n l)) by DILATE_0_CONS + = SUC (LENGTH (GENLIST (K e) n ++ DILATE e 0 n l)) by LENGTH + = SUC (LENGTH (GENLIST (K e) n) + LENGTH (DILATE e 0 n l)) by LENGTH_APPEND + = SUC (n + LENGTH (DILATE e 0 n l)) by LENGTH_GENLIST + = SUC (n + SUC (SUC n * PRE (LENGTH l))) by induction hypothesis + = SUC (SUC (n + SUC n * PRE (LENGTH l))) by ADD_SUC + = SUC (SUC n + SUC n * PRE (LENGTH l)) by ADD_COMM, ADD_SUC + = SUC (SUC n * SUC (PRE (LENGTH l))) by MULT_SUC + = SUC (SUC n * LENGTH l) by SUC_PRE, 0 < LENGTH l + = SUC (SUC n * PRE (LENGTH (h::l))) by LENGTH, PRE_SUC_EQ +*) +val DILATE_0_LENGTH = store_thm( + "DILATE_0_LENGTH", + ``!l e n. LENGTH (DILATE e 0 n l) = if l = [] then 0 else SUC (SUC n * PRE (LENGTH l))``, + Induct >- + rw[] >> + rw_tac std_ss[LENGTH] >> + Cases_on `l = []` >- + rw[] >> + `0 < LENGTH l` by metis_tac[LENGTH_NIL, NOT_ZERO_LT_ZERO] >> + `LENGTH (DILATE e 0 n (h::l)) = LENGTH (h::(GENLIST (K e) n ++ DILATE e 0 n l))` by rw[DILATE_0_CONS] >> + `_ = SUC (LENGTH (GENLIST (K e) n ++ DILATE e 0 n l))` by rw[] >> + `_ = SUC (n + LENGTH (DILATE e 0 n l))` by rw[] >> + `_ = SUC (n + SUC (SUC n * PRE (LENGTH l)))` by rw[] >> + `_ = SUC (SUC (n + SUC n * PRE (LENGTH l)))` by rw[] >> + `_ = SUC (SUC n + SUC n * PRE (LENGTH l))` by rw[] >> + `_ = SUC (SUC n * SUC (PRE (LENGTH l)))` by rw[MULT_SUC] >> + `_ = SUC (SUC n * LENGTH l)` by rw[SUC_PRE] >> + rw[]); + +(* Theorem: LENGTH l <= LENGTH (DILATE e 0 n l) *) +(* Proof: + If l = [], + LENGTH (DILATE e 0 n []) + = LENGTH [] by DILATE_NIL + >= LENGTH [] + If l <> [], + Then ?h t. l = h::t by list_CASES + LENGTH (DILATE e 0 n (h::t)) + = SUC (SUC n * PRE (LENGTH (h::t))) by DILATE_0_LENGTH + = SUC (SUC n * PRE (SUC (LENGTH t))) by LENGTH + = SUC (SUC n * LENGTH t) by PRE + = SUC n * LENGTH t + 1 by ADD1 + >= LENGTH t + 1 by LE_MULT_CANCEL_LBARE, 0 < SUC n + = SUC (LENGTH t) by ADD1 + = LENGTH (h::t) by LENGTH +*) +val DILATE_0_LENGTH_LOWER = store_thm( + "DILATE_0_LENGTH_LOWER", + ``!l e n. LENGTH l <= LENGTH (DILATE e 0 n l)``, + rw[DILATE_0_LENGTH] >> + `?h t. l = h::t` by metis_tac[list_CASES] >> + rw[]); + +(* Theorem: LENGTH (DILATE e 0 n l) <= SUC (SUC n * PRE (LENGTH l)) *) +(* Proof: + If l = [], + LENGTH (DILATE e 0 n []) + = LENGTH [] by DILATE_NIL + = 0 by LENGTH_NIL + SUC (SUC n * PRE (LENGTH [])) + = SUC (SUC n * PRE 0) by LENGTH_NIL + = SUC 0 by PRE, MULT_0 + > 0 by LESS_SUC + If l <> [], + LENGTH (DILATE e 0 n l) + = SUC (SUC n * PRE (LENGTH l)) by DILATE_0_LENGTH +*) +val DILATE_0_LENGTH_UPPER = store_thm( + "DILATE_0_LENGTH_UPPER", + ``!l e n. LENGTH (DILATE e 0 n l) <= SUC (SUC n * PRE (LENGTH l))``, + rw[DILATE_0_LENGTH]); + +(* Theorem: k < LENGTH (DILATE e 0 n l) ==> + (EL k (DILATE e 0 n l) = if k MOD (SUC n) = 0 then EL (k DIV (SUC n)) l else e) *) +(* Proof: + Let m = SUC n, then 0 < m. + By induction on l. + Base: !k. k < LENGTH (DILATE e 0 n []) ==> (EL k (DILATE e 0 n []) = if k MOD m = 0 then EL (k DIV m) [] else e) + Note LENGTH (DILATE e 0 n []) + = LENGTH [] by DILATE_NIL + = 0 by LENGTH_NIL + Thus k < 0 <=> F by NOT_ZERO_LT_ZERO + Step: !k. k < LENGTH (DILATE e 0 n l) ==> (EL k (DILATE e 0 n l) = if k MOD m = 0 then EL (k DIV m) l else e) ==> + !h k. k < LENGTH (DILATE e 0 n (h::l)) ==> (EL k (DILATE e 0 n (h::l)) = if k MOD m = 0 then EL (k DIV m) (h::l) else e) + Note LENGTH (DILATE e 0 n [h]) = 1 by DILATE_SING + and LENGTH (DILATE e 0 n (h::l)) + = SUC (m * PRE (LENGTH (h::l))) by DILATE_0_LENGTH, n <> 0 + = SUC (m * PRE (SUC (LENGTH l))) by LENGTH + = SUC (m * LENGTH l) by PRE + + If l = [], + Then DILATE e 0 n [h] = [h] by DILATE_SING + and LENGTH (DILATE e 0 n [h]) = 1 by LENGTH + so k < 1 means k = 0. + and 0 DIV m = 0 by ZERO_DIV, 0 < m + and 0 MOD m = 0 by ZERO_MOD, 0 < m + Thus EL k [h] = EL (k DIV m) [h]. + + If l <> [], + Let t = h:: GENLIST (K e) n. + Note LENGTH t = SUC n = m by LENGTH_GENLIST + If k < m, + Then k MOD m = k by LESS_MOD, k < m + EL k (DILATE e 0 n (h::l)) + = EL k (t ++ DILATE e 0 n l) by DILATE_0_CONS + = EL k t by EL_APPEND, k < LENGTH t + If k = 0, i.e. k MOD m = 0. + EL 0 t + = EL 0 (h:: GENLIST (K e) (PRE n)) by notation of t + = h + = EL (0 DIV m) (h::l) by EL, HD + If k <> 0, i.e. k MOD m <> 0. + EL k t + = EL k (h:: GENLIST (K e) n) by notation of t + = EL (PRE k) (GENLIST (K e) n) by EL_CONS + = (K e) (PRE k) by EL_GENLIST, PRE k < PRE m = n + = e by application of K + If ~(k < m), then m <= k. + Given k < LENGTH (DILATE e 0 n (h::l)) + or k < SUC (m * LENGTH l) by above + ==> k - m < SUC (m * LENGTH l) - m by m <= k + = SUC (m * LENGTH l - m) by SUB + = SUC (m * (LENGTH l - 1)) by LEFT_SUB_DISTRIB + = SUC (m * PRE (LENGTH l)) by PRE_SUB1 + or k - m < LENGTH (MDILATE e n l) by MDILATE_LENGTH + Thus (k - m) MOD m = k MOD m by SUB_MOD + and (k - m) DIV m = k DIV m - 1 by SUB_DIV + If k MOD m = 0, + Note 0 < k DIV m by DIVIDES_MOD_0, DIV_POS + EL k (t ++ DILATE e 0 n l) + = EL (k - m) (DILATE e 0 n l) by EL_APPEND, m <= k + = EL (k DIV m - 1) l by induction hypothesis, (k - m) MOD m = 0 + = EL (PRE (k DIV m)) l by PRE_SUB1 + = EL (k DIV m) (h::l) by EL_CONS, 0 < k DIV m + If k MOD m <> 0, + EL k (t ++ DILATE e 0 n l) + = EL (k - m) (DILATE e 0 n l) by EL_APPEND, n <= k + = e by induction hypothesis, (k - m) MOD n <> 0 +*) +Theorem DILATE_0_EL: + !l e n k. k < LENGTH (DILATE e 0 n l) ==> + (EL k (DILATE e 0 n l) = if k MOD (SUC n) = 0 then EL (k DIV (SUC n)) l else e) +Proof + ntac 3 strip_tac >> + `0 < SUC n` by decide_tac >> + qabbrev_tac `m = SUC n` >> + Induct_on `l` >- + rw[] >> + rpt strip_tac >> + `LENGTH (DILATE e 0 n [h]) = 1` by rw[DILATE_SING] >> + `LENGTH (DILATE e 0 n (h::l)) = SUC (m * LENGTH l)` by rw[DILATE_0_LENGTH, Abbr`m`] >> + Cases_on `l = []` >| [ + `k = 0` by rw[] >> + `k MOD m = 0` by rw[] >> + `k DIV m = 0` by rw[ZERO_DIV] >> + rw_tac std_ss[DILATE_SING], + qabbrev_tac `t = h::GENLIST (K e) n` >> + `DILATE e 0 n (h::l) = t ++ DILATE e 0 n l` by rw[DILATE_0_CONS, Abbr`t`] >> + `m = LENGTH t` by rw[Abbr`t`] >> + Cases_on `k < m` >| [ + `k MOD m = k` by rw[] >> + `EL k (DILATE e 0 n (h::l)) = EL k t` by rw[EL_APPEND] >> + Cases_on `k = 0` >| [ + `EL 0 t = h` by rw[Abbr`t`] >> + rw[ZERO_DIV], + `PRE m = n` by rw[Abbr`m`] >> + `PRE k < n` by decide_tac >> + `EL k t = EL (PRE k) (GENLIST (K e) n)` by rw[EL_CONS, Abbr`t`] >> + `_ = (K e) (PRE k)` by rw[EL_GENLIST] >> + rw[] + ], + `m <= k` by decide_tac >> + `EL k (t ++ DILATE e 0 n l) = EL (k - m) (DILATE e 0 n l)` by simp[EL_APPEND] >> + `k - m < LENGTH (DILATE e 0 n l)` by rw[DILATE_0_LENGTH] >> + `(k - m) MOD m = k MOD m` by simp[SUB_MOD] >> + `(k - m) DIV m = k DIV m - 1` by simp[SUB_DIV] >> + Cases_on `k MOD m = 0` >| [ + `0 < k DIV m` by rw[DIVIDES_MOD_0, DIV_POS] >> + `EL (k - m) (DILATE e 0 n l) = EL (k DIV m - 1) l` by rw[] >> + `_ = EL (PRE (k DIV m)) l` by rw[PRE_SUB1] >> + `_ = EL (k DIV m) (h::l)` by rw[EL_CONS] >> + rw[], + `EL (k - m) (DILATE e 0 n l) = e` by rw[] >> + rw[] + ] + ] + ] +QED + +(* This is a milestone theorem. *) + +(* Theorem: (DILATE e 0 n l = []) <=> (l = []) *) +(* Proof: + If part: DILATE e 0 n l = [] ==> l = [] + By contradiction, suppose l <> []. + If n = 0, + Then DILATE e n 0 l = l by DILATE_0_0 + This contradicts DILATE e n 0 l = []. + If n <> 0, + Then LENGTH (DILATE e 0 n l) + = SUC (SUC n * PRE (LENGTH l)) by DILATE_0_LENGTH + <> 0 by SUC_NOT + So (DILATE e 0 n l) <> [] by LENGTH_NIL + This contradicts DILATE e 0 n l = [] + Only-if part: l = [] ==> DILATE e 0 n l = [] + True by DILATE_NIL +*) +val DILATE_0_EQ_NIL = store_thm( + "DILATE_0_EQ_NIL", + ``!l e n. (DILATE e 0 n l = []) <=> (l = [])``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + Cases_on `n = 0` >| [ + `DILATE e 0 0 l = l` by rw[GSYM DILATE_0_0] >> + metis_tac[], + `LENGTH (DILATE e 0 n l) = SUC (SUC n * PRE (LENGTH l))` by rw[DILATE_0_LENGTH] >> + `LENGTH (DILATE e 0 n l) <> 0` by decide_tac >> + metis_tac[LENGTH_EQ_0] + ]); + +(* Theorem: LAST (DILATE e 0 n l) = LAST l *) +(* Proof: + If l = [], + LAST (DILATE e 0 n []) + = LAST [] by DILATE_NIL + If l <> [], + If n = 0, + LAST (DILATE e 0 0 l) + = LAST l by DILATE_0_0 + If n <> 0, + Then DILATE e 0 n l <> [] by DILATE_0_EQ_NIL + or LENGTH (DILATE e 0 n l) <> 0 by LENGTH_NIL + Let m = SUC n, then 0 < m by LESS_0 + Note PRE (LENGTH (DILATE e 0 n l)) + = PRE (SUC (m * PRE (LENGTH l))) by DILATE_0_LENGTH + = m * PRE (LENGTH l) by PRE + Let k = PRE (LENGTH (DILATE e 0 n l)). + Then k < LENGTH (DILATE e 0 n l) by PRE x < x + and k MOD m = 0 by MOD_EQ_0, MULT_COMM, 0 < m + and k DIV m = PRE (LENGTH l) by MULT_DIV, MULT_COMM + + LAST (DILATE e 0 n l) + = EL k (DILATE e 0 n l) by LAST_EL + = EL (k DIV m) l by DILATE_0_EL + = EL (PRE (LENGTH l)) l by above + = LAST l by LAST_EL +*) +val DILATE_0_LAST = store_thm( + "DILATE_0_LAST", + ``!l e n. LAST (DILATE e 0 n l) = LAST l``, + rpt strip_tac >> + Cases_on `l = []` >- + rw[] >> + Cases_on `n = 0` >- + rw[DILATE_0_0] >> + `0 < n` by decide_tac >> + `DILATE e 0 n l <> []` by rw[DILATE_0_EQ_NIL] >> + `LENGTH (DILATE e 0 n l) <> 0` by metis_tac[LENGTH_NIL] >> + qabbrev_tac `k = PRE (LENGTH (DILATE e 0 n l))` >> + rw[LAST_EL] >> + `0 < SUC n` by decide_tac >> + qabbrev_tac `m = SUC n` >> + `k = m * PRE (LENGTH l)` by rw[DILATE_0_LENGTH, Abbr`k`, Abbr`m`] >> + `k MOD m = 0` by metis_tac[MOD_EQ_0, MULT_COMM] >> + `k DIV m = PRE (LENGTH l)` by metis_tac[MULT_DIV, MULT_COMM] >> + `k < LENGTH (DILATE e 0 n l)` by simp[Abbr`k`] >> + Q.RM_ABBREV_TAC ‘k’ >> + rw[DILATE_0_EL]); + +(* ------------------------------------------------------------------------- *) +(* FUNPOW with incremental cons. *) +(* ------------------------------------------------------------------------- *) + +(* Note from HelperList: m downto n = REVERSE [m .. n] *) + +(* Idea: when applying incremental cons (f head) to a list for n times, + head of the result is f^n (head of list). *) + +(* Theorem: HD (FUNPOW (\ls. f (HD ls)::ls) n ls) = FUNPOW f n (HD ls) *) +(* Proof: + Let h = (\ls. f (HD ls)::ls). + By induction on n. + Base: !ls. HD (FUNPOW h 0 ls) = FUNPOW f 0 (HD ls) + HD (FUNPOW h 0 ls) + = HD ls by FUNPOW_0 + = FUNPOW f 0 (HD ls) by FUNPOW_0 + Step: !ls. HD (FUNPOW h n ls) = FUNPOW f n (HD ls) ==> + !ls. HD (FUNPOW h (SUC n) ls) = FUNPOW f (SUC n) (HD ls) + HD (FUNPOW h (SUC n) ls) + = HD (FUNPOW h n (h ls)) by FUNPOW + = FUNPOW f n (HD (h ls)) by induction hypothesis + = FUNPOW f n (f (HD ls)) by definition of h + = FUNPOW f (SUC n) (HD ls) by FUNPOW +*) +Theorem FUNPOW_cons_head: + !f n ls. HD (FUNPOW (\ls. f (HD ls)::ls) n ls) = FUNPOW f n (HD ls) +Proof + strip_tac >> + qabbrev_tac `h = \ls. f (HD ls)::ls` >> + Induct >- + simp[] >> + rw[FUNPOW, Abbr`h`] +QED + +(* Idea: when applying incremental cons (f head) to a singleton [u] for n times, + the result is the list [f^n(u), .... f(u), u]. *) + +(* Theorem: FUNPOW (\ls. f (HD ls)::ls) n [u] = + MAP (\j. FUNPOW f j u) (n downto 0) *) +(* Proof: + Let g = (\ls. f (HD ls)::ls), + h = (\j. FUNPOW f j u). + By induction on n. + Base: FUNPOW g 0 [u] = MAP h (0 downto 0) + FUNPOW g 0 [u] + = [u] by FUNPOW_0 + = [FUNPOW f 0 u] by FUNPOW_0 + = MAP h [0] by MAP + = MAP h (0 downto 0) by REVERSE + Step: FUNPOW g n [u] = MAP h (n downto 0) ==> + FUNPOW g (SUC n) [u] = MAP h (SUC n downto 0) + FUNPOW g (SUC n) [u] + = g (FUNPOW g n [u]) by FUNPOW_SUC + = g (MAP h (n downto 0)) by induction hypothesis + = f (HD (MAP h (n downto 0))) :: + MAP h (n downto 0) by definition of g + Now f (HD (MAP h (n downto 0))) + = f (HD (MAP h (MAP (\x. n - x) [0 .. n]))) by listRangeINC_REVERSE + = f (HD (MAP h o (\x. n - x) [0 .. n])) by MAP_COMPOSE + = f ((h o (\x. n - x)) 0) by MAP + = f (h n) + = f (FUNPOW f n u) by definition of h + = FUNPOW (n + 1) u by FUNPOW_SUC + = h (n + 1) by definition of h + so h (n + 1) :: MAP h (n downto 0) + = MAP h ((n + 1) :: (n downto 0)) by MAP + = MAP h (REVERSE (SNOC (n+1) [0 .. n])) by REVERSE_SNOC + = MAP h (SUC n downto 0) by listRangeINC_SNOC +*) +Theorem FUNPOW_cons_eq_map_0: + !f u n. FUNPOW (\ls. f (HD ls)::ls) n [u] = + MAP (\j. FUNPOW f j u) (n downto 0) +Proof + ntac 2 strip_tac >> + Induct >- + rw[] >> + qabbrev_tac `g = \ls. f (HD ls)::ls` >> + qabbrev_tac `h = \j. FUNPOW f j u` >> + rw[] >> + `f (HD (MAP h (n downto 0))) = h (n + 1)` by + (`[0 .. n] = 0 :: [1 .. n]` by rw[listRangeINC_CONS] >> + fs[listRangeINC_REVERSE, MAP_COMPOSE, GSYM FUNPOW_SUC, ADD1, Abbr`h`]) >> + `FUNPOW g (SUC n) [u] = g (FUNPOW g n [u])` by rw[FUNPOW_SUC] >> + `_ = g (MAP h (n downto 0))` by fs[] >> + `_ = h (n + 1) :: MAP h (n downto 0)` by rw[Abbr`g`] >> + `_ = MAP h ((n + 1) :: (n downto 0))` by rw[] >> + `_ = MAP h (REVERSE (SNOC (n+1) [0 .. n]))` by rw[REVERSE_SNOC] >> + rw[listRangeINC_SNOC, ADD1] +QED + +(* Idea: when applying incremental cons (f head) to a singleton [f(u)] for (n-1) times, + the result is the list [f^n(u), .... f(u)]. *) + +(* Theorem: 0 < n ==> (FUNPOW (\ls. f (HD ls)::ls) (n - 1) [f u] = + MAP (\j. FUNPOW f j u) (n downto 1)) *) +(* Proof: + Let g = (\ls. f (HD ls)::ls), + h = (\j. FUNPOW f j u). + By induction on n. + Base: FUNPOW g 0 [f u] = MAP h (REVERSE [1 .. 1]) + FUNPOW g 0 [f u] + = [f u] by FUNPOW_0 + = [FUNPOW f 1 u] by FUNPOW_1 + = MAP h [1] by MAP + = MAP h (REVERSE [1 .. 1]) by REVERSE + Step: 0 < n ==> FUNPOW g (n-1) [f u] = MAP h (n downto 1) ==> + FUNPOW g n [f u] = MAP h (REVERSE [1 .. SUC n]) + The case n = 0 is the base case. For n <> 0, + FUNPOW g n [f u] + = g (FUNPOW g (n-1) [f u]) by FUNPOW_SUC + = g (MAP h (n downto 1)) by induction hypothesis + = f (HD (MAP h (n downto 1))) :: + MAP h (n downto 1) by definition of g + Now f (HD (MAP h (n downto 1))) + = f (HD (MAP h (MAP (\x. n + 1 - x) [1 .. n]))) by listRangeINC_REVERSE + = f (HD (MAP h o (\x. n + 1 - x) [1 .. n])) by MAP_COMPOSE + = f ((h o (\x. n + 1 - x)) 1) by MAP + = f (h n) + = f (FUNPOW f n u) by definition of h + = FUNPOW (n + 1) u by FUNPOW_SUC + = h (n + 1) by definition of h + so h (n + 1) :: MAP h (n downto 1) + = MAP h ((n + 1) :: (n downto 1)) by MAP + = MAP h (REVERSE (SNOC (n+1) [1 .. n])) by REVERSE_SNOC + = MAP h (REVERSE [1 .. SUC n]) by listRangeINC_SNOC +*) +Theorem FUNPOW_cons_eq_map_1: + !f u n. 0 < n ==> (FUNPOW (\ls. f (HD ls)::ls) (n - 1) [f u] = + MAP (\j. FUNPOW f j u) (n downto 1)) +Proof + ntac 2 strip_tac >> + Induct >- + simp[] >> + rw[] >> + qabbrev_tac `g = \ls. f (HD ls)::ls` >> + qabbrev_tac `h = \j. FUNPOW f j u` >> + Cases_on `n = 0` >- + rw[Abbr`g`, Abbr`h`] >> + `f (HD (MAP h (n downto 1))) = h (n + 1)` by + (`[1 .. n] = 1 :: [2 .. n]` by rw[listRangeINC_CONS] >> + fs[listRangeINC_REVERSE, MAP_COMPOSE, GSYM FUNPOW_SUC, ADD1, Abbr`h`]) >> + `n = SUC (n-1)` by decide_tac >> + `FUNPOW g n [f u] = g (FUNPOW g (n - 1) [f u])` by metis_tac[FUNPOW_SUC] >> + `_ = g (MAP h (n downto 1))` by fs[] >> + `_ = h (n + 1) :: MAP h (n downto 1)` by rw[Abbr`g`] >> + `_ = MAP h ((n + 1) :: (n downto 1))` by rw[] >> + `_ = MAP h (REVERSE (SNOC (n+1) [1 .. n]))` by rw[REVERSE_SNOC] >> + rw[listRangeINC_SNOC, ADD1] +QED + +(* ------------------------------------------------------------------------- *) +(* Binomial Documentation *) +(* ------------------------------------------------------------------------- *) +(* Definitions and Theorems (# are exported): + + Binomial Coefficients: + binomial_def |- (binomial 0 0 = 1) /\ (!n. binomial (SUC n) 0 = 1) /\ + (!k. binomial 0 (SUC k) = 0) /\ + !n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k) + binomial_alt |- !n k. binomial n 0 = 1 /\ binomial 0 (k + 1) = 0 /\ + binomial (n + 1) (k + 1) = binomial n k + binomial n (k + 1) + binomial_less_0 |- !n k. n < k ==> (binomial n k = 0) + binomial_n_0 |- !n. binomial n 0 = 1 + binomial_n_n |- !n. binomial n n = 1 + binomial_0_n |- !n. binomial 0 n = if n = 0 then 1 else 0 + binomial_recurrence |- !n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k) + binomial_formula |- !n k. binomial (n + k) k * (FACT n * FACT k) = FACT (n + k) + binomial_formula2 |- !n k. k <= n ==> (FACT n = binomial n k * (FACT (n - k) * FACT k)) + binomial_formula3 |- !n k. k <= n ==> (binomial n k = FACT n DIV (FACT k * FACT (n - k))) + binomial_fact |- !n k. k <= n ==> (binomial n k = FACT n DIV (FACT k * FACT (n - k))) + binomial_n_k |- !n k. k <= n ==> (binomial n k = FACT n DIV FACT k DIV FACT (n - k) + binomial_n_1 |- !n. binomial n 1 = n + binomial_sym |- !n k. k <= n ==> (binomial n k = binomial n (n - k)) + binomial_is_integer |- !n k. k <= n ==> (FACT k * FACT (n - k)) divides (FACT n) + binomial_pos |- !n k. k <= n ==> 0 < binomial n k + binomial_eq_0 |- !n k. (binomial n k = 0) <=> n < k + binomial_1_n |- !n. binomial 1 n = if 1 < n then 0 else 1 + binomial_up_eqn |- !n. 0 < n ==> !k. n * binomial (n - 1) k = (n - k) * binomial n k + binomial_up |- !n. 0 < n ==> !k. binomial (n - 1) k = (n - k) * binomial n k DIV n + binomial_right_eqn |- !n. 0 < n ==> !k. (k + 1) * binomial n (k + 1) = (n - k) * binomial n k + binomial_right |- !n. 0 < n ==> !k. binomial n (k + 1) = (n - k) * binomial n k DIV (k + 1) + binomial_monotone |- !n k. k < HALF n ==> binomial n k < binomial n (k + 1) + binomial_max |- !n k. binomial n k <= binomial n (HALF n) + binomial_iff |- !f. f = binomial <=> + !n k. f n 0 = 1 /\ f 0 (k + 1) = 0 /\ + f (n + 1) (k + 1) = f n k + f n (k + 1) + + Primes and Binomial Coefficients: + prime_divides_binomials |- !n. prime n ==> 1 < n /\ !k. 0 < k /\ k < n ==> n divides (binomial n k) + prime_divides_binomials_alt |- !n k. prime n /\ 0 < k /\ k < n ==> n divides binomial n k + prime_divisor_property |- !n p. 1 < n /\ p < n /\ prime p /\ p divides n ==> ~(p divides (FACT (n - 1) DIV FACT (n - p))) + divides_binomials_imp_prime |- !n. 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k)) ==> prime n + prime_iff_divides_binomials |- !n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> n divides (binomial n k) + prime_iff_divides_binomials_alt + |- !n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> binomial n k MOD n = 0 + + Binomial Theorem: + GENLIST_binomial_index_shift |- !n x y. GENLIST ((\k. binomial n k * x ** SUC (n - k) * y ** k) o SUC) n = + GENLIST (\k. binomial n (SUC k) * x ** (n - k) * y ** SUC k) n + binomial_index_shift |- !n x y. (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) o SUC = + (\k. binomial (SUC n) (SUC k) * x ** (n - k) * y ** SUC k) + binomial_term_merge_x |- !n x y. (\k. x * k) o (\k. binomial n k * x ** (n - k) * y ** k) = + (\k. binomial n k * x ** SUC (n - k) * y ** k) + binomial_term_merge_y |- !n x y. (\k. y * k) o (\k. binomial n k * x ** (n - k) * y ** k) = + (\k. binomial n k * x ** (n - k) * y ** SUC k) + binomial_thm |- !n x y. (x + y) ** n = SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n)) + binomial_thm_alt |- !n x y. (x + y) ** n = SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (n + 1)) + binomial_sum |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n + binomial_sum_alt |- !n. SUM (GENLIST (binomial n) (n + 1)) = 2 ** n + + Binomial Horizontal List: + binomial_horizontal_0 |- binomial_horizontal 0 = [1] + binomial_horizontal_len |- !n. LENGTH (binomial_horizontal n) = n + 1 + binomial_horizontal_mem |- !n k. k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n) + binomial_horizontal_mem_iff |- !n k. MEM (binomial n k) (binomial_horizontal n) <=> k <= n + binomial_horizontal_member |- !n x. MEM x (binomial_horizontal n) <=> ?k. k <= n /\ (x = binomial n k) + binomial_horizontal_element |- !n k. k <= n ==> (EL k (binomial_horizontal n) = binomial n k) + binomial_horizontal_pos |- !n. EVERY (\x. 0 < x) (binomial_horizontal n) + binomial_horizontal_pos_alt |- !n x. MEM x (binomial_horizontal n) ==> 0 < x + binomial_horizontal_sum |- !n. SUM (binomial_horizontal n) = 2 ** n + binomial_horizontal_max |- !n. MAX_LIST (binomial_horizontal n) = binomial n (HALF n) + binomial_row_max |- !n. MAX_SET (IMAGE (binomial n) (count (n + 1))) = binomial n (HALF n) + binomial_product_identity |- !m n k. k <= m /\ m <= n ==> + (binomial m k * binomial n m = binomial n k * binomial (n - k) (m - k)) + binomial_middle_upper_bound |- !n. binomial n (HALF n) <= 4 ** HALF n + + Stirling's Approximation: + Stirling = (!n. FACT n = (SQRT (2 * pi * n)) * (n DIV e) ** n) /\ + (!n. SQRT n = n ** h) /\ (2 * h = 1) /\ (0 < pi) /\ (0 < e) /\ + (!a b x y. (a * b) DIV (x * y) = (a DIV x) * (b DIV y)) /\ + (!a b c. (a DIV c) DIV (b DIV c) = a DIV b) + binomial_middle_by_stirling |- Stirling ==> + !n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = 2 ** (n + 1) DIV SQRT (2 * pi * n)) + + Useful theorems for Binomial: + binomial_range_shift |- !n . 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> + (!h. h < PRE n ==> ((binomial n (SUC h)) MOD n = 0))) + binomial_mod_zero |- !n. 0 < n ==> !k. (binomial n k MOD n = 0) <=> + (!x y. (binomial n k * x ** (n-k) * y ** k) MOD n = 0) + binomial_range_shift_alt |- !n . 0 < n ==> ((!k. 0 < k /\ k < n ==> + (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0))) <=> + (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0)))) + binomial_mod_zero_alt |- !n. 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> + !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0) + + Binomial Theorem with prime exponent: + binomial_thm_prime |- !p. prime p ==> (!x y. (x + y) ** p MOD p = (x ** p + y ** p) MOD p) +*) + +(* ------------------------------------------------------------------------- *) +(* Binomial Coefficients *) +(* ------------------------------------------------------------------------- *) + +(* Define Binomials: + C(n,0) = 1 + C(0,k) = 0 if k > 0 + C(n+1,k+1) = C(n,k) + C(n,k+1) +*) +val binomial_def = Define` + (binomial 0 0 = 1) /\ + (binomial (SUC n) 0 = 1) /\ + (binomial 0 (SUC k) = 0) /\ + (binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k)) +`; + +(* Theorem: alternative definition of C(n,k). *) +(* Proof: by binomial_def. *) +Theorem binomial_alt: + !n k. (binomial n 0 = 1) /\ + (binomial 0 (k + 1) = 0) /\ + (binomial (n + 1) (k + 1) = binomial n k + binomial n (k + 1)) +Proof + rewrite_tac[binomial_def, GSYM ADD1] >> + (Cases_on `n` >> simp[binomial_def]) +QED + +(* Basic properties *) + +(* Theorem: C(n,k) = 0 if n < k *) +(* Proof: + By induction on n. + Base case: C(0,k) = 0 if 0 < k, by definition. + Step case: assume C(n,k) = 0 if n < k. + then for SUC n < k, + C(SUC n, k) + = C(SUC n, SUC h) where k = SUC h + = C(n,h) + C(n,SUC h) h < SUC h = k + = 0 + 0 by induction hypothesis + = 0 +*) +val binomial_less_0 = store_thm( + "binomial_less_0", + ``!n k. n < k ==> (binomial n k = 0)``, + Induct_on `n` >- + metis_tac[binomial_def, num_CASES, NOT_ZERO] >> + rw[binomial_def] >> + `?h. k = SUC h` by metis_tac[SUC_NOT, NOT_ZERO, SUC_EXISTS, LESS_TRANS] >> + metis_tac[binomial_def, LESS_MONO_EQ, LESS_TRANS, LESS_SUC, ADD_0]); + +(* Theorem: C(n,0) = 1 *) +(* Proof: + If n = 0, C(n, 0) = C(0, 0) = 1 by binomial_def + If n <> 0, n = SUC m, and C(SUC m, 0) = 1 by binomial_def +*) +val binomial_n_0 = store_thm( + "binomial_n_0", + ``!n. binomial n 0 = 1``, + metis_tac[binomial_def, num_CASES]); + +(* Theorem: C(n,n) = 1 *) +(* Proof: + By induction on n. + Base case: C(0,0) = 1, true by binomial_def. + Step case: assume C(n,n) = 1 + C(SUC n, SUC n) + = C(n,n) + C(n,SUC n) + = 1 + C(n,SUC n) by induction hypothesis + = 1 + 0 by binomial_less_0 + = 1 +*) +val binomial_n_n = store_thm( + "binomial_n_n", + ``!n. binomial n n = 1``, + Induct_on `n` >- + metis_tac[binomial_def] >> + metis_tac[binomial_def, LESS_SUC, binomial_less_0, ADD_0]); + +(* Theorem: binomial 0 n = if n = 0 then 1 else 0 *) +(* Proof: + If n = 0, + binomial 0 0 = 1 by binomial_n_0 + If n <> 0, then 0 < n. + binomial 0 n = 0 by binomial_less_0 +*) +val binomial_0_n = store_thm( + "binomial_0_n", + ``!n. binomial 0 n = if n = 0 then 1 else 0``, + rw[binomial_n_0, binomial_less_0]); + +(* Theorem: C(n+1,k+1) = C(n,k) + C(n,k+1) *) +(* Proof: by definition. *) +val binomial_recurrence = store_thm( + "binomial_recurrence", + ``!n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k)``, + rw[binomial_def]); + +(* Theorem: C(n+k,k) = (n+k)!/n!k! *) +(* Proof: + By induction on k. + Base case: C(n,0) = n!n! = 1 by binomial_n_0 + Step case: assume C(n+k,k) = (n+k)!/n!k! + To prove C(n+SUC k, SUC k) = (n+SUC k)!/n!(SUC k)! + By induction on n. + Base case: C(SUC k, SUC k) = (SUC k)!/(SUC k)! = 1 by binomial_n_n + Step case: assume C(n+SUC k, SUC k) = (n +SUC k)!/n!(SUC k)! + To prove C(SUC n + SUC k, SUC k) = (SUC n + SUC k)!/(SUC n)!(SUC k)! + C(SUC n + SUC k, SUC k) + = C(SUC SUC (n+k), SUC k) + = C(SUC (n+k),k) + C(SUC (n+k), SUC k) + = C(SUC n + k, k) + C(n + SUC k, SUC k) + = (SUC n + k)!/(SUC n)!k! + (n + SUC k)!/n!(SUC k)! by two induction hypothesis + = ((SUC n + k)!(SUC k) + (n + SUC k)(SUC n))/(SUC n)!(SUC k)! + = (SUC n + SUC k)!/(SUC n)!(SUC k)! +*) +val binomial_formula = store_thm( + "binomial_formula", + ``!n k. binomial (n+k) k * (FACT n * FACT k) = FACT (n+k)``, + Induct_on `k` >- + metis_tac[binomial_n_0, FACT, MULT_CLAUSES, ADD_0] >> + Induct_on `n` >- + metis_tac[binomial_n_n, FACT, MULT_CLAUSES, ADD_CLAUSES] >> + `SUC n + SUC k = SUC (SUC (n+k))` by decide_tac >> + `SUC (n + k) = SUC n + k` by decide_tac >> + `binomial (SUC n + SUC k) (SUC k) * (FACT (SUC n) * FACT (SUC k)) = + (binomial (SUC (n + k)) k + + binomial (SUC (n + k)) (SUC k)) * (FACT (SUC n) * FACT (SUC k))` + by metis_tac[binomial_recurrence] >> + `_ = binomial (SUC (n + k)) k * (FACT (SUC n) * FACT (SUC k)) + + binomial (SUC (n + k)) (SUC k) * (FACT (SUC n) * FACT (SUC k))` + by metis_tac[RIGHT_ADD_DISTRIB] >> + `_ = binomial (SUC n + k) k * (FACT (SUC n) * ((SUC k) * FACT k)) + + binomial (n + SUC k) (SUC k) * ((SUC n) * FACT n * FACT (SUC k))` + by metis_tac[ADD_COMM, SUC_ADD_SYM, FACT] >> + `_ = binomial (SUC n + k) k * FACT (SUC n) * FACT k * (SUC k) + + binomial (n + SUC k) (SUC k) * FACT n * FACT (SUC k) * (SUC n)` + by metis_tac[MULT_COMM, MULT_ASSOC] >> + `_ = FACT (SUC n + k) * SUC k + FACT (n + SUC k) * SUC n` + by metis_tac[MULT_COMM, MULT_ASSOC] >> + `_ = FACT (SUC (n+k)) * SUC k + FACT (SUC (n+k)) * SUC n` + by metis_tac[ADD_COMM, SUC_ADD_SYM] >> + `_ = FACT (SUC (n+k)) * (SUC k + SUC n)` by metis_tac[LEFT_ADD_DISTRIB] >> + `_ = (SUC n + SUC k) * FACT (SUC (n+k))` by metis_tac[MULT_COMM, ADD_COMM] >> + metis_tac[FACT]); + +(* Theorem: C(n,k) = n!/k!(n-k)! for 0 <= k <= n *) +(* Proof: + FACT n + = FACT ((n-k)+k) by SUB_ADD, k <= n. + = binomial ((n-k)+k) k * (FACT (n-k) * FACT k) by binomial_formula + = binomial n k * (FACT (n-k) * FACT k)) by SUB_ADD, k <= n. +*) +val binomial_formula2 = store_thm( + "binomial_formula2", + ``!n k. k <= n ==> (FACT n = binomial n k * (FACT (n-k) * FACT k))``, + metis_tac[binomial_formula, SUB_ADD]); + +(* Theorem: k <= n ==> binomial n k = (FACT n) DIV ((FACT k) * (FACT (n - k))) *) +(* Proof: + binomial n k + = (binomial n k * (FACT (n - k) * FACT k)) DIV ((FACT (n - k) * FACT k)) by MULT_DIV + = (FACT n) DIV ((FACT (n - k) * FACT k)) by binomial_formula2 + = (FACT n) DIV ((FACT k * FACT (n - k))) by MULT_COMM +*) +val binomial_formula3 = store_thm( + "binomial_formula3", + ``!n k. k <= n ==> (binomial n k = (FACT n) DIV ((FACT k) * (FACT (n - k))))``, + metis_tac[binomial_formula2, MULT_COMM, MULT_DIV, MULT_EQ_0, FACT_LESS, NOT_ZERO]); + +(* Theorem alias. *) +val binomial_fact = save_thm("binomial_fact", binomial_formula3); +(* val binomial_fact = |- !n k. k <= n ==> (binomial n k = FACT n DIV (FACT k * FACT (n - k))): thm *) + +(* Theorem: k <= n ==> binomial n k = (FACT n) DIV (FACT k) DIV (FACT (n - k)) *) +(* Proof: + binomial n k + = (FACT n) DIV ((FACT k * FACT (n - k))) by binomial_formula3 + = (FACT n) DIV (FACT k) DIV (FACT (n - k)) by DIV_DIV_DIV_MULT +*) +val binomial_n_k = store_thm( + "binomial_n_k", + ``!n k. k <= n ==> (binomial n k = (FACT n) DIV (FACT k) DIV (FACT (n - k)))``, + metis_tac[DIV_DIV_DIV_MULT, binomial_formula3, MULT_EQ_0, FACT_LESS, NOT_ZERO]); + +(* Theorem: binomial n 1 = n *) +(* Proof: + If n = 0, + binomial 0 1 + = if 1 = 0 then 1 else 0 by binomial_0_n + = 0 by 1 = 0 = F + If n <> 0, then 0 < n. + Thus 1 <= n, and n = SUC (n-1) by 0 < n + binomial n 1 + = FACT n DIV FACT 1 DIV FACT (n - 1) by binomial_n_k, 1 <= n + = FACT n DIV 1 DIV (FACT (n-1)) by FACT, ONE + = FACT n DIV (FACT (n-1)) by DIV_1 + = (n * FACT (n-1)) DIV (FACT (n-1)) by FACT + = n by MULT_DIV, FACT_LESS +*) +val binomial_n_1 = store_thm( + "binomial_n_1", + ``!n. binomial n 1 = n``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[binomial_0_n] >> + `1 <= n /\ (n = SUC (n-1))` by decide_tac >> + `binomial n 1 = FACT n DIV FACT 1 DIV FACT (n - 1)` by rw[binomial_n_k] >> + `_ = FACT n DIV 1 DIV (FACT (n-1))` by EVAL_TAC >> + `_ = FACT n DIV (FACT (n-1))` by rw[] >> + `_ = (n * FACT (n-1)) DIV (FACT (n-1))` by metis_tac[FACT] >> + `_ = n` by rw[MULT_DIV, FACT_LESS] >> + rw[]); + +(* Theorem: k <= n ==> (binomial n k = binomial n (n-k)) *) +(* Proof: + Note (n-k) <= n always. + binomial n k + = (FACT n) DIV (FACT k * FACT (n - k)) by binomial_formula3, k <= n. + = (FACT n) DIV (FACT (n - k) * FACT k) by MULT_COMM + = (FACT n) DIV (FACT (n - k) * FACT (n-(n-k))) by n - (n-k) = k + = binomial n (n-k) by binomial_formula3, (n-k) <= n. +*) +val binomial_sym = store_thm( + "binomial_sym", + ``!n k. k <= n ==> (binomial n k = binomial n (n-k))``, + rpt strip_tac >> + `n - (n-k) = k` by decide_tac >> + `(n-k) <= n` by decide_tac >> + rw[binomial_formula3, MULT_COMM]); + +(* Theorem: k <= n ==> (FACT k * FACT (n-k)) divides (FACT n) *) +(* Proof: + Since FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2 + = binomial n k * (FACT k * FACT (n - k)) by MULT_COMM + Hence (FACT k * FACT (n-k)) divides (FACT n) by divides_def +*) +val binomial_is_integer = store_thm( + "binomial_is_integer", + ``!n k. k <= n ==> (FACT k * FACT (n-k)) divides (FACT n)``, + metis_tac[binomial_formula2, MULT_COMM, divides_def]); + +(* Theorem: k <= n ==> 0 < binomial n k *) +(* Proof: + Since FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2 + and 0 < FACT n, 0 < FACT (n-k), 0 < FACT k by FACT_LESS + Hence 0 < binomial n k by ZERO_LESS_MULT +*) +val binomial_pos = store_thm( + "binomial_pos", + ``!n k. k <= n ==> 0 < binomial n k``, + metis_tac[binomial_formula2, FACT_LESS, ZERO_LESS_MULT]); + +(* Theorem: (binomial n k = 0) <=> n < k *) +(* Proof: + If part: (binomial n k = 0) ==> n < k + By contradiction, suppose k <= n. + Then 0 < binomial n k by binomial_pos + This contradicts binomial n k = 0 by NOT_ZERO + Only-if part: n < k ==> (binomial n k = 0) + This is true by binomial_less_0 +*) +val binomial_eq_0 = store_thm( + "binomial_eq_0", + ``!n k. (binomial n k = 0) <=> n < k``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `k <= n` by decide_tac >> + metis_tac[binomial_pos, NOT_ZERO], + rw[binomial_less_0] + ]); + +(* Theorem: binomial 1 n = if 1 < n then 0 else 1 *) +(* Proof: + If n = 0, binomial 1 0 = 1 by binomial_n_0 + If n = 1, binomial 1 1 = 1 by binomial_n_1 + Otherwise, binomial 1 n = 0 by binomial_eq_0, 1 < n +*) +Theorem binomial_1_n: + !n. binomial 1 n = if 1 < n then 0 else 1 +Proof + rw[binomial_eq_0] >> + `n = 0 \/ n = 1` by decide_tac >- + simp[binomial_n_0] >> + simp[binomial_n_1] +QED + +(* Relating Binomial to its up-entry: + + binomial n k = (n, k, n-k) = n! / k! (n-k)! + binomial (n-1) k = (n-1, k, n-1-k) = (n-1)! / k! (n-1-k)! + = (n!/n) / k! ((n-k)!/(n-k)) + = (n-k) * binomial n k / n +*) + +(* Theorem: 0 < n ==> !k. n * binomial (n-1) k = (n-k) * (binomial n k) *) +(* Proof: + If n <= k, that is n-1 < k. + So binomial (n-1) k = 0 by binomial_less_0 + and n - k = 0 by arithmetic + Hence true by MULT_EQ_0 + Otherwise k < n, + or k <= n, 1 <= n-k, k <= n-1 + Therefore, + FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2, k <= n. + = binomial n k * ((n - k) * FACT (n-1-k) * FACT k) by FACT + = binomial n k * (n - k) * (FACT (n-1-k) * FACT k) by MULT_ASSOC + = (n - k) * binomial n k * (FACT (n-1-k) * FACT k) by MULT_COMM + FACT n = n * FACT (n-1) by FACT + = n * (binomial (n-1) k * (FACT (n-1-k) * FACT k)) by binomial_formula2, k <= n-1. + = (n * binomial (n-1) k) * (FACT (n-1-k) * FACT k) by MULT_ASSOC + Since 0 < FACT (n-1-k) * FACT k by FACT_LESS, MULT_EQ_0 + n * binomial (n-1) k = (n-k) * (binomial n k) by MULT_RIGHT_CANCEL +*) +val binomial_up_eqn = store_thm( + "binomial_up_eqn", + ``!n. 0 < n ==> !k. n * binomial (n-1) k = (n-k) * (binomial n k)``, + rpt strip_tac >> + `!n. n <> 0 <=> 0 < n` by decide_tac >> + Cases_on `n <= k` >| [ + `n-1 < k /\ (n - k = 0)` by decide_tac >> + `binomial (n - 1) k = 0` by rw[binomial_less_0] >> + metis_tac[MULT_EQ_0], + `k < n /\ k <= n /\ 1 <= n-k /\ k <= n-1` by decide_tac >> + `SUC (n-1) = n` by decide_tac >> + `SUC (n-1-k) = n - k` by metis_tac[SUB_PLUS, ADD_COMM, ADD1, SUB_ADD] >> + `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> + `_ = binomial n k * ((n - k) * FACT (n-1-k) * FACT k)` by metis_tac[FACT] >> + `_ = binomial n k * (n - k) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> + `_ = (n - k) * binomial n k * (FACT (n-1-k) * FACT k)` by rw_tac std_ss[MULT_COMM] >> + `FACT n = n * FACT (n-1)` by metis_tac[FACT] >> + `_ = n * (binomial (n-1) k * (FACT (n-1-k) * FACT k))` by rw_tac std_ss[GSYM binomial_formula2] >> + `_ = (n * binomial (n-1) k) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> + metis_tac[FACT_LESS, MULT_EQ_0, MULT_RIGHT_CANCEL] + ]); + +(* Theorem: 0 < n ==> !k. binomial (n-1) k = ((n-k) * (binomial n k)) DIV n *) +(* Proof: + Since n * binomial (n-1) k = (n-k) * (binomial n k) by binomial_up_eqn + binomial (n-1) k = (n-k) * (binomial n k) DIV n by DIV_SOLVE, 0 < n. +*) +val binomial_up = store_thm( + "binomial_up", + ``!n. 0 < n ==> !k. binomial (n-1) k = ((n-k) * (binomial n k)) DIV n``, + rw[binomial_up_eqn, DIV_SOLVE]); + +(* Relating Binomial to its right-entry: + + binomial n k = (n, k, n-k) = n! / k! (n-k)! + binomial n (k+1) = (n, k+1, n-k-1) = n! / (k+1)! (n-k-1)! + = n! / (k+1) * k! ((n-k)!/(n-k)) + = (n-k) * binomial n k / (k+1) +*) + +(* Theorem: 0 < n ==> !k. (k + 1) * binomial n (k+1) = (n - k) * binomial n k *) +(* Proof: + If n <= k, that is n < k+1. + So binomial n (k+1) = 0 by binomial_less_0 + and n - k = 0 by arithmetic + Hence true by MULT_EQ_0 + Otherwise k < n, + or k <= n, 1 <= n-k, k+1 <= n + Therefore, + FACT n = binomial n k * (FACT (n - k) * FACT k) by binomial_formula2, k <= n. + = binomial n k * ((n - k) * FACT (n-1-k) * FACT k) by FACT + = binomial n k * (n - k) * (FACT (n-1-k) * FACT k) by MULT_ASSOC + = (n - k) * binomial n k * (FACT (n-1-k) * FACT k) by MULT_COMM + FACT n = binomial n (k+1) * (FACT (n-(k+1)) * FACT (k+1)) by binomial_formula2, k+1 <= n. + = binomial n (k+1) * (FACT (n-1-k) * FACT (k+1)) by SUB_PLUS, ADD_COMM + = binomial n (k+1) * (FACT (n-1-k) * ((k+1) * FACT k)) by FACT + = binomial n (k+1) * ((k+1) * (FACT (n-1-k) * FACT k)) by MULT_ASSOC, MULT_COMM + = (k+1) * binomial n (k+1) * (FACT (n-1-k) * FACT k) by MULT_COMM, MULT_ASSOC + Since 0 < FACT (n-1-k) * FACT k by FACT_LESS, MULT_EQ_0 + (k+1) * binomial n (k+1) = (n-k) * (binomial n k) by MULT_RIGHT_CANCEL +*) +val binomial_right_eqn = store_thm( + "binomial_right_eqn", + ``!n. 0 < n ==> !k. (k + 1) * binomial n (k+1) = (n - k) * binomial n k``, + rpt strip_tac >> + `!n. n <> 0 <=> 0 < n` by decide_tac >> + Cases_on `n <= k` >| [ + `n < k+1` by decide_tac >> + `binomial n (k+1) = 0` by rw[binomial_less_0] >> + `n - k = 0` by decide_tac >> + metis_tac[MULT_EQ_0], + `k < n /\ k <= n /\ 1 <= n-k /\ k+1 <= n` by decide_tac >> + `SUC k = k + 1` by decide_tac >> + `SUC (n-1-k) = n - k` by metis_tac[SUB_PLUS, ADD_COMM, ADD1, SUB_ADD] >> + `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> + `_ = binomial n k * ((n - k) * FACT (n-1-k) * FACT k)` by metis_tac[FACT] >> + `_ = binomial n k * (n - k) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> + `_ = (n - k) * binomial n k * (FACT (n-1-k) * FACT k)` by rw_tac std_ss[MULT_COMM] >> + `FACT n = binomial n (k+1) * (FACT (n-(k+1)) * FACT (k+1))` by rw[binomial_formula2] >> + `_ = binomial n (k+1) * (FACT (n-1-k) * FACT (k+1))` by metis_tac[SUB_PLUS, ADD_COMM] >> + `_ = binomial n (k+1) * (FACT (n-1-k) * ((k+1) * FACT k))` by metis_tac[FACT] >> + `_ = binomial n (k+1) * ((FACT (n-1-k) * (k+1)) * FACT k)` by rw[MULT_ASSOC] >> + `_ = binomial n (k+1) * ((k+1) * (FACT (n-1-k)) * FACT k)` by rw_tac std_ss[MULT_COMM] >> + `_ = (binomial n (k+1) * (k+1)) * (FACT (n-1-k) * FACT k)` by rw[MULT_ASSOC] >> + `_ = (k+1) * binomial n (k+1) * (FACT (n-1-k) * FACT k)` by rw_tac std_ss[MULT_COMM] >> + metis_tac[FACT_LESS, MULT_EQ_0, MULT_RIGHT_CANCEL] + ]); + +(* Theorem: 0 < n ==> !k. binomial n (k+1) = (n - k) * binomial n k DIV (k+1) *) +(* Proof: + Since (k + 1) * binomial n (k+1) = (n - k) * binomial n k by binomial_right_eqn + binomial n (k+1) = (n - k) * binomial n k DIV (k+1) by DIV_SOLVE, 0 < k+1. +*) +val binomial_right = store_thm( + "binomial_right", + ``!n. 0 < n ==> !k. binomial n (k+1) = (n - k) * binomial n k DIV (k+1)``, + rw[binomial_right_eqn, DIV_SOLVE, DECIDE ``!k. 0 < k+1``]); + +(* + k < HALF n <=> k + 1 <= n - k +n = 5, HALF n = 2, binomial 5 k: 1, 5, 10, 10, 5, 1 + k= 0, 1, 2, 3, 4, 5 + k < 2 <=> k + 1 <= 5 - k + k = 0 1 <= 5 binomial 5 1 >= binomial 5 0 + k = 1 2 <= 4 binomial 5 2 >= binomial 5 1 +n = 6, HALF n = 3, binomial 6 k: 1, 6, 15, 20, 15, 6, 1 + k= 0, 1, 2, 3, 4, 5, 6 + k < 3 <=> k + 1 <= 6 - k + k = 0 1 <= 6 binomial 6 1 >= binomial 6 0 + k = 1 2 <= 5 binomial 6 2 >= binomial 6 1 + k = 2 3 <= 4 binomial 6 3 >= binomial 6 2 +*) + +(* Theorem: k < HALF n ==> binomial n k < binomial n (k + 1) *) +(* Proof: + Note k < HALF n ==> 0 < n by ZERO_DIV, 0 < 2 + also k < HALF n ==> k + 1 < n - k by LESS_HALF_IFF + so 0 < k + 1 /\ 0 < n - k by arithmetic + Now (k + 1) * binomial n (k + 1) = (n - k) * binomial n k by binomial_right_eqn, 0 < n + Note HALF n <= n by DIV_LESS_EQ, 0 < 2 + so k < HALF n <= n by above + Thus 0 < binomial n k by binomial_pos, k <= n + and 0 < binomial n (k + 1) by MULT_0, MULT_EQ_0 + Hence binomial n k < binomial n (k + 1) by MULT_EQ_LESS_TO_MORE +*) +val binomial_monotone = store_thm( + "binomial_monotone", + ``!n k. k < HALF n ==> binomial n k < binomial n (k + 1)``, + rpt strip_tac >> + `k + 1 < n - k` by rw[GSYM LESS_HALF_IFF] >> + `0 < k + 1 /\ 0 < n - k` by decide_tac >> + `(k + 1) * binomial n (k + 1) = (n - k) * binomial n k` by rw[binomial_right_eqn] >> + `HALF n <= n` by rw[DIV_LESS_EQ] >> + `0 < binomial n k` by rw[binomial_pos] >> + `0 < binomial n (k + 1)` by metis_tac[MULT_0, MULT_EQ_0, NOT_ZERO] >> + metis_tac[MULT_EQ_LESS_TO_MORE]); + +(* Theorem: binomial n k <= binomial n (HALF n) *) +(* Proof: + Since (k + 1) * binomial n (k + 1) = (n - k) * binomial n k by binomial_right_eqn + binomial n (k + 1) / binomial n k = (n - k) / (k + 1) + As k varies from 0, 1, to (n-1), n + the ratio varies from n/1, (n-1)/2, (n-2)/3, ...., 1/n, 0/(n+1). + The ratio is greater than 1 when (n - k) / (k + 1) > 1 + or n - k > k + 1 + or n > 2 * k + 1 + or HALF n >= k + (HALF 1) + or k <= HALF n + Thus (binomial n (HALF n)) is greater than all preceding coefficients. + For k > HALF n, note that (binomial n k = binomial n (n - k)) by binomial_sym + Hence (binomial n (HALF n)) is greater than all succeeding coefficients, too. + + If n = 0, + binomial 0 k = 1 or 0 by binomial_0_n + binomial 0 (HALF 0) = 1 by binomial_0_n, ZERO_DIV + Hence true. + If n <> 0, + If k = HALF n, trivially true. + If k < HALF n, + Then binomial n k < binomial n (HALF n) by binomial_monotone, MONOTONE_MAX + Hence true. + If ~(k < HALF n), HALF n < k. + Then n - k <= HALF n by MORE_HALF_IMP + If k > n, + Then binomial n k = 0, hence true by binomial_less_0 + If ~(k > n), then k <= n. + Then binomial n k = binomial n (n - k) by binomial_sym, k <= n + If n - k = HALF n, trivially true. + Otherwise, n - k < HALF n, + Thus binomial n (n - k) < binomial n (HALF n) by binomial_monotone, MONOTONE_MAX + Hence true. +*) +val binomial_max = store_thm( + "binomial_max", + ``!n k. binomial n k <= binomial n (HALF n)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[binomial_0_n] >> + Cases_on `k = HALF n` >- + rw[] >> + Cases_on `k < HALF n` >| [ + `binomial n k < binomial n (HALF n)` by rw[binomial_monotone, MONOTONE_MAX] >> + decide_tac, + `HALF n < k` by decide_tac >> + `n - k <= HALF n` by rw[MORE_HALF_IMP] >> + Cases_on `k > n` >- + rw[binomial_less_0] >> + `k <= n` by decide_tac >> + `binomial n k = binomial n (n - k)` by rw[GSYM binomial_sym] >> + Cases_on `n - k = HALF n` >- + rw[] >> + `n - k < HALF n` by decide_tac >> + `binomial n (n - k) < binomial n (HALF n)` by rw[binomial_monotone, MONOTONE_MAX] >> + decide_tac + ]); + +(* Idea: the recurrence relation for binomial defines itself. *) + +(* Theorem: f = binomial <=> + !n k. f n 0 = 1 /\ f 0 (k + 1) = 0 /\ + f (n + 1) (k + 1) = f n k + f n (k + 1) *) +(* Proof: + If part: f = binomial ==> recurrence, true by binomial_alt + Only-if part: recurrence ==> f = binomial + By FUN_EQ_THM, this is to show: + !n k. f n k = binomial n k + By double induction, first induct on k. + Base: !n. f n 0 = binomial n 0, true by binomial_n_0 + Step: !n. f n k = binomial n k ==> + !n. f n (SUC k) = binomial n (SUC k) + By induction on n. + Base: f 0 (SUC k) = binomial 0 (SUC k) + This is true by binomial_0_n, ADD1 + Step: f n (SUC k) = binomial n (SUC k) ==> + f (SUC n) (SUC k) = binomial (SUC n) (SUC k) + + f (SUC n) (SUC k) + = f (n + 1) (k + 1) by ADD1 + = f n k + f n (k + 1) by given + = binomial n k + binomial n (k + 1) by induction hypothesis + = binomial (n + 1) (k + 1) by binomial_alt + = binomial (SUC n) (SUC k) by ADD1 +*) +Theorem binomial_iff: + !f. f = binomial <=> + !n k. f n 0 = 1 /\ f 0 (k + 1) = 0 /\ f (n + 1) (k + 1) = f n k + f n (k + 1) +Proof + rw[binomial_alt, EQ_IMP_THM] >> + simp[FUN_EQ_THM] >> + Induct_on `x'` >- + simp[binomial_n_0] >> + Induct_on `x` >- + fs[binomial_0_n, ADD1] >> + fs[binomial_alt, ADD1] +QED + +(* ------------------------------------------------------------------------- *) +(* Primes and Binomial Coefficients *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: n is prime ==> n divides C(n,k) for all 0 < k < n *) +(* Proof: + C(n,k) = n!/k!/(n-k)! + or n! = C(n,k) k! (n-k)! + n divides n!, so n divides the product C(n,k) k!(n-k)! + For a prime n, n cannot divide k!(n-k)!, all factors less than prime n. + By Euclid's lemma, a prime divides a product must divide a factor. + So p divides C(n,k). +*) +val prime_divides_binomials = store_thm( + "prime_divides_binomials", + ``!n. prime n ==> 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k))``, + rpt strip_tac >- + metis_tac[ONE_LT_PRIME] >> + `(n = n-k + k) /\ (n-k) < n` by decide_tac >> + `FACT n = (binomial n k) * (FACT (n-k) * FACT k)` by metis_tac[binomial_formula] >> + `~(n divides (FACT k)) /\ ~(n divides (FACT (n-k)))` by metis_tac[PRIME_BIG_NOT_DIVIDES_FACT] >> + `n divides (FACT n)` by metis_tac[DIVIDES_FACT, LESS_TRANS] >> + metis_tac[P_EUCLIDES]); + +(* Theorem: n is prime ==> n divides C(n,k) for all 0 < k < n *) +(* Proof: by prime_divides_binomials *) +val prime_divides_binomials_alt = store_thm( + "prime_divides_binomials_alt", + ``!n k. prime n /\ 0 < k /\ k < n ==> n divides (binomial n k)``, + rw[prime_divides_binomials]); + +(* Theorem: If prime p divides n, p does not divide (n-1)!/(n-p)! *) +(* Proof: + By contradiction. + (n-1)...(n-p+1)/p cannot be an integer + as p cannot divide any of the numerator. + Note: when p divides n, the nearest multiples for p are n+/-p. +*) +val prime_divisor_property = store_thm( + "prime_divisor_property", + ``!n p. 1 < n /\ p < n /\ prime p /\ p divides n ==> + ~(p divides ((FACT (n-1)) DIV (FACT (n-p))))``, + spose_not_then strip_assume_tac >> + `1 < p` by metis_tac[ONE_LT_PRIME] >> + `n-p < n-1` by decide_tac >> + `(FACT (n-1)) DIV (FACT (n-p)) = PROD_SET (IMAGE SUC ((count (n-1)) DIFF (count (n-p))))` + by metis_tac[FACT_REDUCTION, MULT_DIV, FACT_LESS] >> + `(count (n-1)) DIFF (count (n-p)) = {x | (n-p) <= x /\ x < (n-1)}` + by srw_tac[ARITH_ss][EXTENSION, EQ_IMP_THM] >> + `IMAGE SUC {x | (n-p) <= x /\ x < (n-1)} = {x | (n-p) < x /\ x < n}` by + (srw_tac[ARITH_ss][EXTENSION, EQ_IMP_THM] >> + qexists_tac `x-1` >> + decide_tac) >> + `FINITE (count (n - 1) DIFF count (n - p))` by rw[] >> + `?y. y IN {x| n - p < x /\ x < n} /\ p divides y` by metis_tac[PROD_SET_EUCLID, IMAGE_FINITE] >> + `!m n y. y IN {x | m < x /\ x < n} ==> m < y /\ y < n` by rw[] >> + `n-p < y /\ y < n` by metis_tac[] >> + `y < n + p` by decide_tac >> + `y = n` by metis_tac[MULTIPLE_INTERVAL] >> + decide_tac); + +(* Theorem: n divides C(n,k) for all 0 < k < n ==> n is prime *) +(* Proof: + By contradiction. Let p be a proper factor of n, 1 < p < n. + Then C(n,p) = n(n-1)...(n-p+1)/p(p-1)..1 + is divisible by n/p, but not n, since + C(n,p)/n = (n-1)...(n-p+1)/p(p-1)...1 + cannot be an integer as p cannot divide any of the numerator. + Note: when p divides n, the nearest multiples for p are n+/-p. +*) +val divides_binomials_imp_prime = store_thm( + "divides_binomials_imp_prime", + ``!n. 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k)) ==> prime n``, + (spose_not_then strip_assume_tac) >> + `?p. prime p /\ p < n /\ p divides n` by metis_tac[PRIME_FACTOR_PROPER] >> + `n divides (binomial n p)` by metis_tac[PRIME_POS] >> + `0 < p` by metis_tac[PRIME_POS] >> + `(n = n-p + p) /\ (n-p) < n` by decide_tac >> + `FACT n = (binomial n p) * (FACT (n-p) * FACT p)` by metis_tac[binomial_formula] >> + `(n = SUC (n-1)) /\ (p = SUC (p-1))` by decide_tac >> + `(FACT n = n * FACT (n-1)) /\ (FACT p = p * FACT (p-1))` by metis_tac[FACT] >> + `n * FACT (n-1) = (binomial n p) * (FACT (n-p) * (p * FACT (p-1)))` by metis_tac[] >> + `0 < n` by decide_tac >> + `?q. binomial n p = n * q` by metis_tac[divides_def, MULT_COMM] >> + `0 <> n` by decide_tac >> + `FACT (n-1) = q * (FACT (n-p) * (p * FACT (p-1)))` + by metis_tac[EQ_MULT_LCANCEL, MULT_ASSOC] >> + `_ = q * ((FACT (p-1) * p)* FACT (n-p))` by metis_tac[MULT_COMM] >> + `_ = q * FACT (p-1) * p * FACT (n-p)` by metis_tac[MULT_ASSOC] >> + `FACT (n-1) DIV FACT (n-p) = q * FACT (p-1) * p` by metis_tac[MULT_DIV, FACT_LESS] >> + metis_tac[divides_def, prime_divisor_property]); + +(* Theorem: n is prime iff n divides C(n,k) for all 0 < k < n *) +(* Proof: + By prime_divides_binomials and + divides_binomials_imp_prime. +*) +val prime_iff_divides_binomials = store_thm( + "prime_iff_divides_binomials", + ``!n. prime n <=> 1 < n /\ (!k. 0 < k /\ k < n ==> n divides (binomial n k))``, + metis_tac[prime_divides_binomials, divides_binomials_imp_prime]); + +(* Theorem: prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0) *) +(* Proof: by prime_iff_divides_binomials *) +val prime_iff_divides_binomials_alt = store_thm( + "prime_iff_divides_binomials_alt", + ``!n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)``, + rw[prime_iff_divides_binomials, DIVIDES_MOD_0]); + +(* ------------------------------------------------------------------------- *) +(* Binomial Theorem *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: Binomial Index Shifting, for + SUM (k=1..n) C(n,k)x^(n+1-k)y^k + = SUM (k=0..n-1) C(n,k+1)x^(n-k)y^(k+1) + *) +(* Proof: +SUM (k=1..n) C(n,k)x^(n+1-k)y^k += SUM (MAP (\k. (binomial n k)* x**(n+1-k) * y**k) (GENLIST SUC n)) += SUM (GENLIST (\k. (binomial n k)* x**(n+1-k) * y**k) o SUC n) + +SUM (k=0..n-1) C(n,k+1)x^(n-k)y^(k+1) += SUM (MAP (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) (GENLIST I n)) += SUM (GENLIST (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) o I n) += SUM (GENLIST (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) n) + +i.e. + +(\k. (binomial n k)* x**(n-k+1) * y**k) o SUC += (\k. (binomial n (k+1)) * x**(n-k) * y**(k+1)) +*) +(* Theorem: Binomial index shift for GENLIST *) +val GENLIST_binomial_index_shift = store_thm( + "GENLIST_binomial_index_shift", + ``!n x y. GENLIST ((\k. binomial n k * x ** SUC(n - k) * y ** k) o SUC) n = + GENLIST (\k. binomial n (SUC k) * x ** (n-k) * y**(SUC k)) n``, + rw_tac std_ss[GENLIST_FUN_EQ] >> + `SUC (n - SUC k) = n - k` by decide_tac >> + rw_tac std_ss[]); + +(* This is closely related to above, with (SUC n) replacing (n), + but does not require k < n. *) +(* Proof: by function equality. *) +val binomial_index_shift = store_thm( + "binomial_index_shift", + ``!n x y. (\k. binomial (SUC n) k * x ** ((SUC n) - k) * y ** k) o SUC = + (\k. binomial (SUC n) (SUC k) * x ** (n-k) * y ** (SUC k))``, + rw_tac std_ss[FUN_EQ_THM]); + +(* Pattern for binomial expansion: + + (x+y)(x^3 + 3x^2y + 3xy^2 + y^3) + = x(x^3) + 3x(x^2y) + 3x(xy^2) + x(y^3) + + y(x^3) + 3y(x^2y) + 3y(xy^2) + y(y^3) + = x^4 + (3+1)x^3y + (3+3)(x^2y^2) + (1+3)(xy^3) + y^4 + = x^4 + 4x^3y + 6x^2y^2 + 4xy^3 + y^4 + +*) + +(* Theorem: multiply x into a binomial term *) +(* Proof: by function equality and EXP. *) +val binomial_term_merge_x = store_thm( + "binomial_term_merge_x", + ``!n x y. (\k. x * k) o (\k. binomial n k * x ** (n - k) * y ** k) = + (\k. binomial n k * x ** (SUC(n - k)) * y ** k)``, + rw_tac std_ss[FUN_EQ_THM] >> + `x * (binomial n k * x ** (n - k) * y ** k) = + binomial n k * (x * x ** (n - k)) * y ** k` by decide_tac >> + metis_tac[EXP]); + +(* Theorem: multiply y into a binomial term *) +(* Proof: by functional equality and EXP. *) +val binomial_term_merge_y = store_thm( + "binomial_term_merge_y", + ``!n x y. (\k. y * k) o (\k. binomial n k * x ** (n - k) * y ** k) = + (\k. binomial n k * x ** (n - k) * y ** (SUC k))``, + rw_tac std_ss[FUN_EQ_THM] >> + `y * (binomial n k * x ** (n - k) * y ** k) = + binomial n k * x ** (n - k) * (y * y ** k)` by decide_tac >> + metis_tac[EXP]); + +(* Theorem: [Binomial Theorem] (x + y)^n = SUM (k=0..n) C(n,k)x^(n-k)y^k *) +(* Proof: + By induction on n. + Base case: to prove (x + y)^0 = SUM (k=0..0) C(0,k)x^(0-k)y^k + (x + y)^0 = 1 by EXP + SUM (k=0..0) C(0,k)x^(n-k)y^k = C(0,0)x^(0-0)y^0 = C(0,0) = 1 by EXP, binomial_def + Step case: assume (x + y)^n = SUM (k=0..n) C(n,k)x^(n-k)y^k + to prove: (x + y)^SUC n = SUM (k=0..(SUC n)) C(SUC n,k)x^((SUC n)-k)y^k + (x + y)^SUC n + = (x + y)(x + y)^n by EXP + = (x + y) SUM (k=0..n) C(n,k)x^(n-k)y^k by induction hypothesis + = x (SUM (k=0..n) C(n,k)x^(n-k)y^k) + + y (SUM (k=0..n) C(n,k)x^(n-k)y^k) by RIGHT_ADD_DISTRIB + = SUM (k=0..n) C(n,k)x^(n+1-k)y^k + + SUM (k=0..n) C(n,k)x^(n-k)y^(k+1) by moving factor into SUM + = C(n,0)x^(n+1) + SUM (k=1..n) C(n,k)x^(n+1-k)y^k + + SUM (k=0..n-1) C(n,k)x^(n-k)y^(k+1) + C(n,n)y^(n+1) + by breaking sum + + = C(n,0)x^(n+1) + SUM (k=0..n-1) C(n,k+1)x^(n-k)y^(k+1) + + SUM (k=0..n-1) C(n,k)x^(n-k)y^(k+1) + C(n,n)y^(n+1) + by index shifting + = C(n,0)x^(n+1) + + SUM (k=0..n-1) [C(n,k+1) + C(n,k)] x^(n-k)y^(k+1) + + C(n,n)y^(n+1) by merging sums + = C(n,0)x^(n+1) + + SUM (k=0..n-1) C(n+1,k+1) x^(n-k)y^(k+1) + + C(n,n)y^(n+1) by binomial recurrence + = C(n,0)x^(n+1) + + SUM (k=1..n) C(n+1,k) x^(n+1-k)y^k + + C(n,n)y^(n+1) by index shifting again + = C(n+1,0)x^(n+1) + + SUM (k=1..n) C(n+1,k) x^(n+1-k)y^k + + C(n+1,n+1)y^(n+1) by binomial identities + = SUM (k=0..(SUC n))C(SUC n,k) x^((SUC n)-k)y^k + by synthesis of sum +*) +val binomial_thm = store_thm( + "binomial_thm", + ``!n x y. (x + y) ** n = SUM (GENLIST (\k. (binomial n k) * x ** (n-k) * y ** k) (SUC n))``, + Induct_on `n` >- + rw[EXP, binomial_n_n] >> + rw_tac std_ss[EXP] >> + `(x + y) * SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n)) = + x * SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n)) + + y * SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (SUC n))` + by metis_tac[RIGHT_ADD_DISTRIB] >> + `_ = SUM (GENLIST ((\k. x * k) o (\k. binomial n k * x ** (n - k) * y ** k)) (SUC n)) + + SUM (GENLIST ((\k. y * k) o (\k. binomial n k * x ** (n - k) * y ** k)) (SUC n))` + by metis_tac[SUM_MULT, MAP_GENLIST] >> + `_ = SUM (GENLIST (\k. binomial n k * x ** SUC(n - k) * y ** k) (SUC n)) + + SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) (SUC n))` + by rw[binomial_term_merge_x, binomial_term_merge_y] >> + `_ = (\k. binomial n k * x ** SUC (n - k) * y ** k) 0 + + SUM (GENLIST ((\k. binomial n k * x ** SUC (n - k) * y ** k) o SUC) n) + + SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) (SUC n))` + by rw[SUM_DECOMPOSE_FIRST] >> + `_ = (\k. binomial n k * x ** SUC (n - k) * y ** k) 0 + + SUM (GENLIST ((\k. binomial n k * x ** SUC (n - k) * y ** k) o SUC) n) + + (SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n) + + (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n )` + by rw[SUM_DECOMPOSE_LAST] >> + `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + + SUM (GENLIST (\k. binomial n (SUC k) * x ** (n - k) * y ** (SUC k)) n) + + (SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n) + + (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n )` + by metis_tac[GENLIST_binomial_index_shift] >> + `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + + (SUM (GENLIST (\k. binomial n (SUC k) * x ** (n - k) * y ** (SUC k)) n) + + SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n)) + + (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` + by decide_tac >> + `_ = (\k. binomial n k * x ** SUC (n - k) * y ** k) 0 + + SUM (GENLIST (\k. (binomial n (SUC k) * x ** (n - k) * y ** (SUC k) + + binomial n k * x ** (n - k) * y ** (SUC k))) n) + + (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` + by metis_tac[SUM_ADD_GENLIST] >> + `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + + SUM (GENLIST (\k. (binomial n (SUC k) + binomial n k) * x ** (n - k) * y ** (SUC k)) n) + + (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` + by rw[RIGHT_ADD_DISTRIB, MULT_ASSOC] >> + `_ = (\k. binomial n k * x ** SUC(n - k) * y ** k) 0 + + SUM (GENLIST (\k. binomial (SUC n) (SUC k) * x ** (n - k) * y ** (SUC k)) n) + + (\k. binomial n k * x ** (n - k) * y ** (SUC k)) n` + by rw[binomial_recurrence, ADD_COMM] >> + `_ = binomial (SUC n) 0 * x ** (SUC n) * y ** 0 + + SUM (GENLIST (\k. binomial (SUC n) (SUC k) * x ** (n - k) * y ** (SUC k)) n) + + binomial (SUC n) (SUC n) * x ** 0 * y ** (SUC n)` + by rw[binomial_n_0, binomial_n_n] >> + `_ = binomial (SUC n) 0 * x ** (SUC n) * y ** 0 + + SUM (GENLIST ((\k. binomial (SUC n) k * x ** ((SUC n) - k) * y ** k) o SUC) n) + + binomial (SUC n) (SUC n) * x ** 0 * y ** (SUC n)` + by rw[binomial_index_shift] >> + `_ = SUM (GENLIST (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) (SUC n)) + + (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) (SUC n)` + by rw[SUM_DECOMPOSE_FIRST] >> + `_ = SUM (GENLIST (\k. binomial (SUC n) k * x ** (SUC n - k) * y ** k) (SUC (SUC n)))` + by rw[SUM_DECOMPOSE_LAST] >> + decide_tac); + +(* This is a milestone theorem. *) + +(* Derive an alternative form. *) +val binomial_thm_alt = save_thm("binomial_thm_alt", + binomial_thm |> SIMP_RULE bool_ss [ADD1]); +(* val binomial_thm_alt = + |- !n x y. (x + y) ** n = + SUM (GENLIST (\k. binomial n k * x ** (n - k) * y ** k) (n + 1)): thm *) + +(* Theorem: SUM (GENLIST (binomial n) (SUC n)) = 2 ** n *) +(* Proof: by binomial_sum_alt and function equality. *) +(* Proof: + Put x = 1, y = 1 in binomial_thm, + (1 + 1) ** n = SUM (GENLIST (\k. binomial n k * 1 ** (n - k) * 1 ** k) (SUC n)) + (1 + 1) ** n = SUM (GENLIST (\k. binomial n k) (SUC n)) by EXP_1 + or 2 ** n = SUM (GENLIST (binomial n) (SUC n)) by FUN_EQ_THM +*) +Theorem binomial_sum: + !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n +Proof + rpt strip_tac >> + `!n. (\k. binomial n k * 1 ** (n - k) * 1 ** k) = binomial n` by rw[FUN_EQ_THM] >> + `SUM (GENLIST (binomial n) (SUC n)) = + SUM (GENLIST (\k. binomial n k * 1 ** (n - k) * 1 ** k) (SUC n))` by fs[] >> + `_ = (1 + 1) ** n` by rw[GSYM binomial_thm] >> + simp[] +QED + +(* Derive an alternative form. *) +val binomial_sum_alt = save_thm("binomial_sum_alt", + binomial_sum |> SIMP_RULE bool_ss [ADD1]); +(* val binomial_sum_alt = |- !n. SUM (GENLIST (binomial n) (n + 1)) = 2 ** n: thm *) + +(* ------------------------------------------------------------------------- *) +(* Binomial Horizontal List *) +(* ------------------------------------------------------------------------- *) + +(* Define Horizontal List in Pascal Triangle *) +(* +val binomial_horizontal_def = Define ` + binomial_horizontal n = GENLIST (binomial n) (SUC n) +`; +*) + +(* Use overloading for binomial_horizontal n. *) +val _ = overload_on("binomial_horizontal", ``\n. GENLIST (binomial n) (n + 1)``); + +(* Theorem: binomial_horizontal 0 = [1] *) +(* Proof: + binomial_horizontal 0 + = GENLIST (binomial 0) (0 + 1) by notation + = SNOC (binomial 0 0) [] by GENLIST, ONE + = [binomial 0 0] by SNOC + = [1] by binomial_n_0 +*) +val binomial_horizontal_0 = store_thm( + "binomial_horizontal_0", + ``binomial_horizontal 0 = [1]``, + rw[binomial_n_0]); + +(* Theorem: LENGTH (binomial_horizontal n) = n + 1 *) +(* Proof: + LENGTH (binomial_horizontal n) + = LENGTH (GENLIST (binomial n) (n + 1)) by notation + = n + 1 by LENGTH_GENLIST +*) +val binomial_horizontal_len = store_thm( + "binomial_horizontal_len", + ``!n. LENGTH (binomial_horizontal n) = n + 1``, + rw[]); + +(* Theorem: k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n) *) +(* Proof: by MEM_GENLIST *) +val binomial_horizontal_mem = store_thm( + "binomial_horizontal_mem", + ``!n k. k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n)``, + metis_tac[MEM_GENLIST]); + +(* Theorem: MEM (binomial n k) (binomial_horizontal n) <=> k <= n *) +(* Proof: + If part: MEM (binomial n k) (binomial_horizontal n) ==> k <= n + By contradiction, suppose n < k. + Then binomial n k = 0 by binomial_less_0, ~(k <= n) + But ?m. m < n + 1 ==> 0 = binomial n m by MEM_GENLIST + or m <= n ==> binomial n m = 0 by m < n + 1 + Yet binomial n m <> 0 by binomial_eq_0 + This is a contradiction. + Only-if part: k <= n ==> MEM (binomial n k) (binomial_horizontal n) + By MEM_GENLIST, this is to show: + ?m. m < n + 1 /\ (binomial n k = binomial n m) + Note k <= n ==> k < n + 1, + Take m = k, the result follows. +*) +val binomial_horizontal_mem_iff = store_thm( + "binomial_horizontal_mem_iff", + ``!n k. MEM (binomial n k) (binomial_horizontal n) <=> k <= n``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `binomial n k = 0` by rw[binomial_less_0] >> + fs[MEM_GENLIST] >> + `m <= n` by decide_tac >> + fs[binomial_eq_0], + rw[MEM_GENLIST] >> + `k < n + 1` by decide_tac >> + metis_tac[] + ]); + +(* Theorem: MEM x (binomial_horizontal n) <=> ?k. k <= n /\ (x = binomial n k) *) +(* Proof: + By MEM_GENLIST, this is to show: + (?m. m < n + 1 /\ (x = binomial n m)) <=> ?k. k <= n /\ (x = binomial n k) + Since m < n + 1 <=> m <= n by LE_LT1 + This is trivially true. +*) +val binomial_horizontal_member = store_thm( + "binomial_horizontal_member", + ``!n x. MEM x (binomial_horizontal n) <=> ?k. k <= n /\ (x = binomial n k)``, + metis_tac[MEM_GENLIST, LE_LT1]); + +(* Theorem: k <= n ==> (EL k (binomial_horizontal n) = binomial n k) *) +(* Proof: by EL_GENLIST *) +val binomial_horizontal_element = store_thm( + "binomial_horizontal_element", + ``!n k. k <= n ==> (EL k (binomial_horizontal n) = binomial n k)``, + rw[EL_GENLIST]); + +(* Theorem: EVERY (\x. 0 < x) (binomial_horizontal n) *) +(* Proof: + EVERY (\x. 0 < x) (binomial_horizontal n) + <=> EVERY (\x. 0 < x) (GENLIST (binomial n) (n + 1)) by notation + <=> !k. k < n + 1 ==> 0 < binomial n k by EVERY_GENLIST + <=> !k. k <= n ==> 0 < binomial n k by arithmetic + <=> T by binomial_pos +*) +val binomial_horizontal_pos = store_thm( + "binomial_horizontal_pos", + ``!n. EVERY (\x. 0 < x) (binomial_horizontal n)``, + rpt strip_tac >> + `!k n. k < n + 1 <=> k <= n` by decide_tac >> + rw_tac std_ss[EVERY_GENLIST, LESS_EQ_IFF_LESS_SUC, binomial_pos]); + +(* Theorem: MEM x (binomial_horizontal n) ==> 0 < x *) +(* Proof: by binomial_horizontal_pos, EVERY_MEM *) +val binomial_horizontal_pos_alt = store_thm( + "binomial_horizontal_pos_alt", + ``!n x. MEM x (binomial_horizontal n) ==> 0 < x``, + metis_tac[binomial_horizontal_pos, EVERY_MEM]); + +(* Theorem: SUM (binomial_horizontal n) = 2 ** n *) +(* Proof: + SUM (binomial_horizontal n) + = SUM (GENLIST (binomial n) (n + 1)) by notation + = 2 ** n by binomial_sum, ADD1 +*) +val binomial_horizontal_sum = store_thm( + "binomial_horizontal_sum", + ``!n. SUM (binomial_horizontal n) = 2 ** n``, + rw_tac std_ss[binomial_sum, GSYM ADD1]); + +(* Theorem: MAX_LIST (binomial_horizontal n) = binomial n (HALF n) *) +(* Proof: + Let l = binomial_horizontal n, m = binomial n (HALF n). + Then l <> [] by binomial_horizontal_len, LENGTH_NIL + and HALF n <= n by DIV_LESS_EQ, 0 < 2 + or HALF n < n + 1 by arithmetic + Also MEM m l by binomial_horizontal_mem + and !x. MEM x l ==> x <= m by binomial_max, MEM_GENLIST + Thus m = MAX_LIST l by MAX_LIST_TEST +*) +val binomial_horizontal_max = store_thm( + "binomial_horizontal_max", + ``!n. MAX_LIST (binomial_horizontal n) = binomial n (HALF n)``, + rpt strip_tac >> + qabbrev_tac `l = binomial_horizontal n` >> + qabbrev_tac `m = binomial n (HALF n)` >> + `l <> []` by metis_tac[binomial_horizontal_len, LENGTH_NIL, DECIDE``n + 1 <> 0``] >> + `HALF n <= n` by rw[DIV_LESS_EQ] >> + `HALF n < n + 1` by decide_tac >> + `MEM m l` by rw[binomial_horizontal_mem, Abbr`l`, Abbr`m`] >> + metis_tac[binomial_max, MEM_GENLIST, MAX_LIST_TEST]); + +(* Theorem: MAX_SET (IMAGE (binomial n) (count (n + 1))) = binomial n (HALF n) *) +(* Proof: + Let f = binomial n, s = IMAGE f (count (n + 1)). + Note FINITE (count (n + 1)) by FINITE_COUNT + so FINITE s by IMAGE_FINITE + Also count (n + 1) <> {} by COUNT_EQ_EMPTY, n + 1 <> 0 + so s <> {} by IMAGE_EQ_EMPTY + Now !k. k IN (count (n + 1)) ==> f k <= f (HALF n) by binomial_max + ==> !x. x IN s ==> x <= f (HALF n) by IN_IMAGE + Also HALF n <= n by DIV_LESS_EQ, 0 < 2 + so HALF n IN (count (n + 1)) by IN_COUNT + ==> f (HALF n) IN s by IN_IMAGE + Thus MAX_SET s = f (HALF n) by MAX_SET_TEST +*) +val binomial_row_max = store_thm( + "binomial_row_max", + ``!n. MAX_SET (IMAGE (binomial n) (count (n + 1))) = binomial n (HALF n)``, + rpt strip_tac >> + qabbrev_tac `f = binomial n` >> + qabbrev_tac `s = IMAGE f (count (n + 1))` >> + `FINITE s` by rw[Abbr`s`] >> + `s <> {}` by rw[COUNT_EQ_EMPTY, Abbr`s`] >> + `!k. k IN (count (n + 1)) ==> f k <= f (HALF n)` by rw[binomial_max, Abbr`f`] >> + `!x. x IN s ==> x <= f (HALF n)` by metis_tac[IN_IMAGE] >> + `HALF n <= n` by rw[DIV_LESS_EQ] >> + `HALF n IN (count (n + 1))` by rw[] >> + `f (HALF n) IN s` by metis_tac[IN_IMAGE] >> + rw[MAX_SET_TEST]); + +(* Theorem: k <= m /\ m <= n ==> + ((binomial m k) * (binomial n m) = (binomial n k) * (binomial (n - k) (m - k))) *) +(* Proof: + Using binomial_formula2, + + (binomial m k) * (binomial n m) + n! m! + = ----------- * ------------------ binomial formula + m! (n - m)! k! (m - k)! + n! m! + = ----------- * ------------------ cancel m! + k! m! (m - k)! (n - m)! + n! (n - k)! + = ----------- * ------------------ replace by (n - k)! + k! (n - k)! (m - k)! (n - m)! + + = (binomial n k) * (binomial (n - k) (m - k)) binomial formula +*) +val binomial_product_identity = store_thm( + "binomial_product_identity", + ``!m n k. k <= m /\ m <= n ==> + ((binomial m k) * (binomial n m) = (binomial n k) * (binomial (n - k) (m - k)))``, + rpt strip_tac >> + `m - k <= n - k` by decide_tac >> + `(n - k) - (m - k) = n - m` by decide_tac >> + `FACT m = binomial m k * (FACT (m - k) * FACT k)` by rw[binomial_formula2] >> + `FACT n = binomial n m * (FACT (n - m) * FACT m)` by rw[binomial_formula2] >> + `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> + `FACT (n - k) = binomial (n - k) (m - k) * (FACT (n - m) * FACT (m - k))` by metis_tac[binomial_formula2] >> + `FACT n = FACT (n - m) * (FACT k * (FACT (m - k) * ((binomial m k) * (binomial n m))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> + `FACT n = FACT (n - m) * (FACT k * (FACT (m - k) * ((binomial n k) * (binomial (n - k) (m - k)))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> + metis_tac[MULT_LEFT_CANCEL, FACT_LESS, NOT_ZERO]); + +(* Theorem: binomial n (HALF n) <= 4 ** (HALF n) *) +(* Proof: + Let m = HALF n, l = binomial_horizontal n + Note LENGTH l = n + 1 by binomial_horizontal_len + If EVEN n, + Then n = 2 * m by EVEN_HALF + and m <= n by m <= 2 * m + Note EL m l <= SUM l by SUM_LE_EL, m < n + 1 + Now EL m l = binomial n m by binomial_horizontal_element, m <= n + and SUM l + = 2 ** n by binomial_horizontal_sum + = 4 ** m by EXP_EXP_MULT + Hence binomial n m <= 4 ** m. + If ~EVEN n, + Then ODD n by EVEN_ODD + and n = 2 * m + 1 by ODD_HALF + so m + 1 <= n by m + 1 <= 2 * m + 1 + with m <= n by m + 1 <= n + Note EL m l = binomial n m by binomial_horizontal_element, m <= n + and EL (m + 1) l = binomial n (m + 1) by binomial_horizontal_element, m + 1 <= n + Note binomial n (m + 1) = binomial n m by binomial_sym + Thus 2 * binomial n m + = binomial n m + binomial n (m + 1) by above + = EL m l + EL (m + 1) l + <= SUM l by SUM_LE_SUM_EL, m < m + 1, m + 1 < n + 1 + and SUM l + = 2 ** n by binomial_horizontal_sum + = 2 * 2 ** (2 * m) by EXP, ADD1 + = 2 * 4 ** m by EXP_EXP_MULT + Hence binomial n m <= 4 ** m. +*) +val binomial_middle_upper_bound = store_thm( + "binomial_middle_upper_bound", + ``!n. binomial n (HALF n) <= 4 ** (HALF n)``, + rpt strip_tac >> + qabbrev_tac `m = HALF n` >> + qabbrev_tac `l = binomial_horizontal n` >> + `LENGTH l = n + 1` by rw[binomial_horizontal_len, Abbr`l`] >> + Cases_on `EVEN n` >| [ + `n = 2 * m` by rw[EVEN_HALF, Abbr`m`] >> + `m < n + 1` by decide_tac >> + `EL m l <= SUM l` by rw[SUM_LE_EL] >> + `EL m l = binomial n m` by rw[binomial_horizontal_element, Abbr`l`] >> + `SUM l = 2 ** n` by rw[binomial_horizontal_sum, Abbr`l`] >> + `_ = 4 ** m` by rw[EXP_EXP_MULT] >> + decide_tac, + `ODD n` by metis_tac[EVEN_ODD] >> + `n = 2 * m + 1` by rw[ODD_HALF, Abbr`m`] >> + `EL m l = binomial n m` by rw[binomial_horizontal_element, Abbr`l`] >> + `EL (m + 1) l = binomial n (m + 1)` by rw[binomial_horizontal_element, Abbr`l`] >> + `binomial n (m + 1) = binomial n m` by rw[Once binomial_sym] >> + `EL m l + EL (m + 1) l <= SUM l` by rw[SUM_LE_SUM_EL] >> + `SUM l = 2 ** n` by rw[binomial_horizontal_sum, Abbr`l`] >> + `_ = 2 * 2 ** (2 * m)` by metis_tac[EXP, ADD1] >> + `_ = 2 * 4 ** m` by rw[EXP_EXP_MULT] >> + decide_tac + ]); + +(* ------------------------------------------------------------------------- *) +(* Stirling's Approximation *) +(* ------------------------------------------------------------------------- *) + +(* Stirling's formula: n! ~ sqrt(2 pi n) (n/e)^n. *) +val _ = overload_on("Stirling", + ``(!n. FACT n = (SQRT (2 * pi * n)) * (n DIV e) ** n) /\ + (!n. SQRT n = n ** h) /\ (2 * h = 1) /\ (0 < pi) /\ (0 < e) /\ + (!a b x y. (a * b) DIV (x * y) = (a DIV x) * (b DIV y)) /\ + (!a b c. (a DIV c) DIV (b DIV c) = a DIV b)``); + +(* Theorem: Stirling ==> + !n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = (2 ** (n + 1)) DIV (SQRT (2 * pi * n))) *) +(* Proof: + Note HALF n <= n by DIV_LESS_EQ, 0 < 2 + Let k = HALF n, then n = 2 * k by EVEN_HALF + Note 0 < k by 0 < n = 2 * k + so (k * 2) DIV k = 2 by MULT_TO_DIV, 0 < k + or n DIV k = 2 by MULT_COMM + Also 0 < pi * n by MULT_EQ_0, 0 < pi, 0 < n + so 0 < 2 * pi * n by arithmetic + + Some theorems on the fly: + Claim: !a b j. (a ** j) DIV (b ** j) = (a DIV b) ** j [1] + Proof: By induction on j. + Base: (a ** 0) DIV (b ** 0) = (a DIV b) ** 0 + (a ** 0) DIV (b ** 0) + = 1 DIV 1 = 1 by EXP, DIVMOD_ID, 0 < 1 + = (a DIV b) ** 0 by EXP + Step: (a ** j) DIV (b ** j) = (a DIV b) ** j ==> + (a ** (SUC j)) DIV (b ** (SUC j)) = (a DIV b) ** (SUC j) + (a ** (SUC j)) DIV (b ** (SUC j)) + = (a * a ** j) DIV (b * b ** j) by EXP + = (a DIV b) * ((a ** j) DIV (b ** j)) by assumption + = (a DIV b) * (a DIV b) ** j by induction hypothesis + = (a DIV b) ** (SUC j) by EXP + + Claim: !a b c. (a DIV b) * c = (a * c) DIV b [2] + Proof: (a DIV b) * c + = (a DIV b) * (c DIV 1) by DIV_1 + = (a * c) DIV (b * 1) by assumption + = (a * c) DIV b by MULT_RIGHT_1 + + Claim: !a b. a DIV b = 2 * (a DIV (2 * b)) [3] + Proof: a DIV b + = 1 * (a DIV b) by MULT_LEFT_1 + = (n DIV n) * (a DIV b) by DIVMOD_ID, 0 < n + = (n * a) DIV (n * b) by assumption + = (n * a) DIV (k * (2 * b)) by arithmetic, n = 2 * k + = (n DIV k) * (a DIV (2 * b)) by assumption + = 2 * (a DIV (2 * b)) by n DIV k = 2 + + Claim: !a b. 0 < b ==> (a * (b ** h DIV b) = a DIV (b ** h)) [4] + Proof: Let c = b ** h. + Then b = c * c by EXP_EXP_MULT + so 0 < c by MULT_EQ_0, 0 < b + a * (c DIV b) + = (c DIV b) * a by MULT_COMM + = (a * c) DIV b by [2] + = (a * c) DIV (c * c) by b = c * c + = (a DIV c) * (c DIV c) by assumption + = a DIV c by DIVMOD_ID, c DIV c = 1, 0 < c + + Note (FACT k) ** 2 + = (SQRT (2 * pi * k)) ** 2 * ((k DIV e) ** k) ** 2 by EXP_BASE_MULT + = (SQRT (2 * pi * k)) ** 2 * (k DIV e) ** n by EXP_EXP_MULT, n = 2 * k + = (SQRT (pi * n)) ** 2 * (k DIV e) ** n by MULT_ASSOC, 2 * k = n + = ((pi * n) ** h) ** 2 * (k DIV e) ** n by assumption + = (pi * n) * (k DIV e) ** n by EXP_EXP_MULT, h * 2 = 1 + + binomial n (HALF n) + = binomial n k by k = HALF n + = FACT n DIV (FACT k * FACT (n - k)) by binomial_formula3, k <= n + = FACT n DIV (FACT k * FACT k) by arithmetic, n - k = 2 * k - k = k + = FACT n DIV ((FACT k) ** 2) by EXP_2 + = FACT n DIV ((pi * n) * (k DIV e) ** n) by above + = ((2 * pi * n) ** h * (n DIV e) ** n) DIV ((pi * n) * (k DIV e) ** n) by assumption + = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) ** n DIV ((k DIV e) ** n)) by (a * b) DIV (x * y) = (a DIV x) * (b DIV y) + = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) DIV (k DIV e)) ** n by (a ** n) DIV (b ** n) = (a DIV b) ** n) + = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * ((n DIV e) DIV (k DIV e)) ** n by MULT_ASSOC, a DIV b = 2 * a DIV (2 * b) + = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * (n DIV k) ** n by assumption, apply DIV_DIV_DIV_MULT + = 2 DIV (2 * pi * n) ** h * (n DIV k) ** n by 2 * x ** h DIV x = 2 DIV (x ** h) + = 2 DIV (2 * pi * n) ** h * 2 ** n by n DIV k = 2 + = 2 * 2 ** n DIV (2 * pi * n) ** h by (a DIV b) * c = a * c DIV b + = 2 ** (SUC n) DIV (2 * pi * n) ** h by EXP + = 2 ** (n + 1)) DIV (SQRT (2 * pi * n)) by ADD1, assumption +*) +val binomial_middle_by_stirling = store_thm( + "binomial_middle_by_stirling", + ``Stirling ==> !n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = (2 ** (n + 1)) DIV (SQRT (2 * pi * n)))``, + rpt strip_tac >> + `HALF n <= n /\ (n = 2 * HALF n)` by rw[DIV_LESS_EQ, EVEN_HALF] >> + qabbrev_tac `k = HALF n` >> + `0 < k` by decide_tac >> + `n DIV k = 2` by metis_tac[MULT_TO_DIV, MULT_COMM] >> + `0 < pi * n` by metis_tac[MULT_EQ_0, NOT_ZERO] >> + `0 < 2 * pi * n` by decide_tac >> + `(FACT k) ** 2 = (SQRT (2 * pi * k)) ** 2 * ((k DIV e) ** k) ** 2` by rw[EXP_BASE_MULT] >> + `_ = (SQRT (2 * pi * k)) ** 2 * (k DIV e) ** n` by rw[GSYM EXP_EXP_MULT] >> + `_ = (pi * n) * (k DIV e) ** n` by rw[GSYM EXP_EXP_MULT] >> + (`!a b j. (a ** j) DIV (b ** j) = (a DIV b) ** j` by (Induct_on `j` >> rw[EXP])) >> + `!a b c. (a DIV b) * c = (a * c) DIV b` by metis_tac[DIV_1, MULT_RIGHT_1] >> + `!a b. a DIV b = 2 * (a DIV (2 * b))` by metis_tac[DIVMOD_ID, MULT_LEFT_1] >> + `!a b. 0 < b ==> (a * (b ** h DIV b) = a DIV (b ** h))` by + (rpt strip_tac >> + qabbrev_tac `c = b ** h` >> + `b = c * c` by rw[GSYM EXP_EXP_MULT, Abbr`c`] >> + `0 < c` by metis_tac[MULT_EQ_0, NOT_ZERO] >> + `a * (c DIV b) = (a * c) DIV (c * c)` by metis_tac[MULT_COMM] >> + `_ = (a DIV c) * (c DIV c)` by metis_tac[] >> + metis_tac[DIVMOD_ID, MULT_RIGHT_1]) >> + `binomial n k = (FACT n) DIV (FACT k * FACT (n - k))` by metis_tac[binomial_formula3] >> + `_ = (FACT n) DIV (FACT k) ** 2` by metis_tac[EXP_2, DECIDE``2 * k - k = k``] >> + `_ = ((2 * pi * n) ** h * (n DIV e) ** n) DIV ((pi * n) * (k DIV e) ** n)` by prove_tac[] >> + `_ = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) ** n DIV ((k DIV e) ** n))` by metis_tac[] >> + `_ = ((2 * pi * n) ** h DIV (pi * n)) * ((n DIV e) DIV (k DIV e)) ** n` by metis_tac[] >> + `_ = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * ((n DIV e) DIV (k DIV e)) ** n` by metis_tac[MULT_ASSOC] >> + `_ = 2 * ((2 * pi * n) ** h DIV (2 * pi * n)) * (n DIV k) ** n` by metis_tac[] >> + `_ = 2 DIV (2 * pi * n) ** h * (n DIV k) ** n` by metis_tac[] >> + `_ = 2 DIV (2 * pi * n) ** h * 2 ** n` by metis_tac[] >> + `_ = (2 * 2 ** n DIV (2 * pi * n) ** h)` by metis_tac[] >> + metis_tac[EXP, ADD1]); + +(* ------------------------------------------------------------------------- *) +(* Useful theorems for Binomial *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: !k. 0 < k /\ k < n ==> (binomial n k MOD n = 0) <=> + !h. 0 <= h /\ h < PRE n ==> (binomial n (SUC h) MOD n = 0) *) +(* Proof: by h = PRE k, or k = SUC h. + If part: put k = SUC h, + then 0 < SUC h ==> 0 <= h, + and SUC h < n ==> PRE (SUC h) = h < PRE n by prim_recTheory.PRE + Only-if part: put h = PRE k, + then 0 <= PRE k ==> 0 < k + and PRE k < PRE n ==> k < n by INV_PRE_LESS +*) +val binomial_range_shift = store_thm( + "binomial_range_shift", + ``!n . 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> + (!h. h < PRE n ==> ((binomial n (SUC h)) MOD n = 0)))``, + rw_tac std_ss[EQ_IMP_THM] >| [ + `0 < SUC h /\ SUC h < n` by decide_tac >> + rw_tac std_ss[], + `k <> 0` by decide_tac >> + `?h. k = SUC h` by metis_tac[num_CASES] >> + `h < PRE n` by decide_tac >> + rw_tac std_ss[] + ]); + +(* Theorem: binomial n k MOD n = 0 <=> (binomial n k * x ** (n-k) * y ** k) MOD n = 0 *) +(* Proof: + (binomial n k * x ** (n-k) * y ** k) MOD n = 0 + <=> (binomial n k * (x ** (n-k) * y ** k)) MOD n = 0 by MULT_ASSOC + <=> (((binomial n k) MOD n) * ((x ** (n - k) * y ** k) MOD n)) MOD n = 0 by MOD_TIMES2 + If part, apply 0 * z = 0 by MULT. + Only-if part, pick x = 1, y = 1, apply EXP_1. +*) +val binomial_mod_zero = store_thm( + "binomial_mod_zero", + ``!n. 0 < n ==> !k. (binomial n k MOD n = 0) <=> (!x y. (binomial n k * x ** (n-k) * y ** k) MOD n = 0)``, + rw_tac std_ss[EQ_IMP_THM] >- + metis_tac[MOD_TIMES2, ZERO_MOD, MULT] >> + metis_tac[EXP_1, MULT_RIGHT_1]); + + +(* Theorem: (!k. 0 < k /\ k < n ==> (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0))) <=> + (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0))) *) +(* Proof: by h = PRE k, or k = SUC h. *) +val binomial_range_shift_alt = store_thm( + "binomial_range_shift_alt", + ``!n . 0 < n ==> ((!k. 0 < k /\ k < n ==> (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0))) <=> + (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0))))``, + rw_tac std_ss[EQ_IMP_THM] >| [ + `0 < SUC h /\ SUC h < n` by decide_tac >> + rw_tac std_ss[], + `k <> 0` by decide_tac >> + `?h. k = SUC h` by metis_tac[num_CASES] >> + `h < PRE n` by decide_tac >> + rw_tac std_ss[] + ]); + +(* Theorem: !k. 0 < k /\ k < n ==> (binomial n k) MOD n = 0 <=> + !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0 *) +(* Proof: + !k. 0 < k /\ k < n ==> (binomial n k) MOD n = 0 + <=> !k. 0 < k /\ k < n ==> !x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0) by binomial_mod_zero + <=> !h. h < PRE n ==> !x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0) by binomial_range_shift_alt + <=> !x y. EVERY (\z. z = 0) (GENLIST (\k. (binomial n (SUC k) * x ** (n - (SUC k)) * y ** (SUC k)) MOD n) (PRE n)) by EVERY_GENLIST + <=> !x y. EVERY (\x. x = 0) (GENLIST ((\k. binomial n k * x ** (n - k) * y ** k) o SUC) (PRE n) by FUN_EQ_THM + <=> !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0 by SUM_EQ_0 +*) +val binomial_mod_zero_alt = store_thm( + "binomial_mod_zero_alt", + ``!n. 0 < n ==> ((!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> + !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0)``, + rpt strip_tac >> + `!x y. (\k. (binomial n (SUC k) * x ** (n - SUC k) * y ** (SUC k)) MOD n) = (\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC` by rw_tac std_ss[FUN_EQ_THM] >> + `(!k. 0 < k /\ k < n ==> ((binomial n k) MOD n = 0)) <=> + (!k. 0 < k /\ k < n ==> (!x y. ((binomial n k * x ** (n - k) * y ** k) MOD n = 0)))` by rw_tac std_ss[binomial_mod_zero] >> + `_ = (!h. h < PRE n ==> (!x y. ((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0)))` by rw_tac std_ss[binomial_range_shift_alt] >> + `_ = !x y h. h < PRE n ==> (((binomial n (SUC h) * x ** (n - (SUC h)) * y ** (SUC h)) MOD n = 0))` by metis_tac[] >> + rw_tac std_ss[EVERY_GENLIST, SUM_EQ_0]); + +(* ------------------------------------------------------------------------- *) +(* Binomial Theorem with prime exponent *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: [Binomial Expansion for prime exponent] (x + y)^p = x^p + y^p (mod p) *) +(* Proof: + (x+y)^p (mod p) + = SUM (k=0..p) C(p,k)x^(p-k)y^k (mod p) by binomial theorem + = (C(p,0)x^py^0 + SUM (k=1..(p-1)) C(p,k)x^(p-k)y^k + C(p,p)x^0y^p) (mod p) by breaking sum + = (x^p + SUM (k=1..(p-1)) C(p,k)x^(p-k)y^k + y^k) (mod p) by binomial_n_0, binomial_n_n + = ((x^p mod p) + (SUM (k=1..(p-1)) C(p,k)x^(p-k)y^k) (mod p) + (y^p mod p)) mod p by MOD_PLUS + = ((x^p mod p) + (SUM (k=1..(p-1)) (C(p,k)x^(p-k)y^k) (mod p)) + (y^p mod p)) mod p + = (x^p mod p + 0 + y^p mod p) mod p by prime_iff_divides_binomials + = (x^p + y^p) (mod p) by MOD_PLUS +*) +val binomial_thm_prime = store_thm( + "binomial_thm_prime", + ``!p. prime p ==> (!x y. (x + y) ** p MOD p = (x ** p + y ** p) MOD p)``, + rpt strip_tac >> + `0 < p` by rw_tac std_ss[PRIME_POS] >> + `!k. 0 < k /\ k < p ==> ((binomial p k) MOD p = 0)` by metis_tac[prime_iff_divides_binomials, DIVIDES_MOD_0] >> + `SUM (GENLIST ((\k. binomial p k * x ** (p - k) * y ** k) o SUC) (PRE p)) MOD p = 0` by metis_tac[SUM_GENLIST_MOD, binomial_mod_zero_alt, ZERO_MOD] >> + `(x + y) ** p MOD p = (x ** p + SUM (GENLIST ((\k. binomial p k * x ** (p - k) * y ** k) o SUC) (PRE p)) + y ** p) MOD p` by rw_tac std_ss[binomial_thm, SUM_DECOMPOSE_FIRST_LAST, binomial_n_0, binomial_n_n, EXP] >> + metis_tac[MOD_PLUS3, ADD_0, MOD_PLUS]); + +(* ------------------------------------------------------------------------- *) +(* Leibniz Harmonic Triangle Documentation *) +(* ------------------------------------------------------------------------- *) +(* Type: (# are temp) + triple = <| a: num; b: num; c: num |> +# path = :num list + Overloading: + leibniz_vertical n = [1 .. (n+1)] + leibniz_up n = REVERSE (leibniz_vertical n) + leibniz_horizontal n = GENLIST (leibniz n) (n + 1) + binomial_horizontal n = GENLIST (binomial n) (n + 1) +# ta = (triplet n k).a +# tb = (triplet n k).b +# tc = (triplet n k).c + p1 zigzag p2 = leibniz_zigzag p1 p2 + p1 wriggle p2 = RTC leibniz_zigzag p1 p2 + leibniz_col_arm a b n = MAP (\x. leibniz (a - x) b) [0 ..< n] + leibniz_seg_arm a b n = MAP (\x. leibniz a (b + x)) [0 ..< n] + + leibniz_seg n k h = IMAGE (\j. leibniz n (k + j)) (count h) + leibniz_row n h = IMAGE (leibniz n) (count h) + leibniz_col h = IMAGE (\i. leibniz i 0) (count h) + lcm_run n = list_lcm [1 .. n] +# beta n k = k * binomial n k +# beta_horizontal n = GENLIST (beta n o SUC) n +*) +(* Definitions and Theorems (# are exported): + + Helper Theorems: + RTC_TRANS |- R^* x y /\ R^* y z ==> R^* x z + + Leibniz Triangle (Denominator form): +# leibniz_def |- !n k. leibniz n k = (n + 1) * binomial n k + leibniz_0_n |- !n. leibniz 0 n = if n = 0 then 1 else 0 + leibniz_n_0 |- !n. leibniz n 0 = n + 1 + leibniz_n_n |- !n. leibniz n n = n + 1 + leibniz_less_0 |- !n k. n < k ==> (leibniz n k = 0) + leibniz_sym |- !n k. k <= n ==> (leibniz n k = leibniz n (n - k)) + leibniz_monotone |- !n k. k < HALF n ==> leibniz n k < leibniz n (k + 1) + leibniz_pos |- !n k. k <= n ==> 0 < leibniz n k + leibniz_eq_0 |- !n k. (leibniz n k = 0) <=> n < k + leibniz_alt |- !n. leibniz n = (\j. (n + 1) * j) o binomial n + leibniz_def_alt |- !n k. leibniz n k = (\j. (n + 1) * j) (binomial n k) + leibniz_up_eqn |- !n. 0 < n ==> !k. (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k + leibniz_up |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1) + leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k + leibniz_right_eqn |- !n. 0 < n ==> !k. (k + 1) * leibniz n (k + 1) = (n - k) * leibniz n k + leibniz_right |- !n. 0 < n ==> !k. leibniz n (k + 1) = (n - k) * leibniz n k DIV (k + 1) + leibniz_property |- !n. 0 < n ==> !k. leibniz n k * leibniz (n - 1) k = + leibniz n (k + 1) * (leibniz n k - leibniz (n - 1) k) + leibniz_formula |- !n k. k <= n ==> (leibniz n k = (n + 1) * FACT n DIV (FACT k * FACT (n - k))) + leibniz_recurrence |- !n. 0 < n ==> !k. k < n ==> (leibniz n (k + 1) = leibniz n k * + leibniz (n - 1) k DIV (leibniz n k - leibniz (n - 1) k)) + leibniz_n_k |- !n k. 0 < k /\ k <= n ==> (leibniz n k = + leibniz n (k - 1) * leibniz (n - 1) (k - 1) + DIV (leibniz n (k - 1) - leibniz (n - 1) (k - 1))) + leibniz_lcm_exchange |- !n. 0 < n ==> !k. lcm (leibniz n k) (leibniz (n - 1) k) = + lcm (leibniz n k) (leibniz n (k + 1)) + leibniz_middle_lower |- !n. 4 ** n <= leibniz (TWICE n) n + + LCM of a list of numbers: +# list_lcm_def |- (list_lcm [] = 1) /\ !h t. list_lcm (h::t) = lcm h (list_lcm t) + list_lcm_nil |- list_lcm [] = 1 + list_lcm_cons |- !h t. list_lcm (h::t) = lcm h (list_lcm t) + list_lcm_sing |- !x. list_lcm [x] = x + list_lcm_snoc |- !x l. list_lcm (SNOC x l) = lcm x (list_lcm l) + list_lcm_map_times |- !n l. list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l + list_lcm_pos |- !l. EVERY_POSITIVE l ==> 0 < list_lcm l + list_lcm_pos_alt |- !l. POSITIVE l ==> 0 < list_lcm l + list_lcm_lower_bound |- !l. EVERY_POSITIVE l ==> SUM l <= LENGTH l * list_lcm l + list_lcm_lower_bound_alt |- !l. POSITIVE l ==> SUM l <= LENGTH l * list_lcm l + list_lcm_is_common_multiple |- !x l. MEM x l ==> x divides (list_lcm l) + list_lcm_is_least_common_multiple |- !l m. (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m + list_lcm_append |- !l1 l2. list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2) + list_lcm_append_3 |- !l1 l2 l3. list_lcm (l1 ++ l2 ++ l3) = list_lcm [list_lcm l1; list_lcm l2; list_lcm l3] + list_lcm_reverse |- !l. list_lcm (REVERSE l) = list_lcm l + list_lcm_suc |- !n. list_lcm [1 .. n + 1] = lcm (n + 1) (list_lcm [1 .. n]) + list_lcm_nonempty_lower |- !l. l <> [] /\ EVERY_POSITIVE l ==> SUM l DIV LENGTH l <= list_lcm l + list_lcm_nonempty_lower_alt |- !l. l <> [] /\ POSITIVE l ==> SUM l DIV LENGTH l <= list_lcm l + list_lcm_divisor_lcm_pair |- !l x y. MEM x l /\ MEM y l ==> lcm x y divides list_lcm l + list_lcm_lower_by_lcm_pair |- !l x y. POSITIVE l /\ MEM x l /\ MEM y l ==> lcm x y <= list_lcm l + list_lcm_upper_by_common_multiple + |- !l m. 0 < m /\ (!x. MEM x l ==> x divides m) ==> list_lcm l <= m + list_lcm_by_FOLDR |- !ls. list_lcm ls = FOLDR lcm 1 ls + list_lcm_by_FOLDL |- !ls. list_lcm ls = FOLDL lcm 1 ls + + Lists in Leibniz Triangle: + + Veritcal Lists in Leibniz Triangle + leibniz_vertical_alt |- !n. leibniz_vertical n = GENLIST (\i. 1 + i) (n + 1) + leibniz_vertical_0 |- leibniz_vertical 0 = [1] + leibniz_vertical_len |- !n. LENGTH (leibniz_vertical n) = n + 1 + leibniz_vertical_not_nil |- !n. leibniz_vertical n <> [] + leibniz_vertical_pos |- !n. EVERY_POSITIVE (leibniz_vertical n) + leibniz_vertical_pos_alt |- !n. POSITIVE (leibniz_vertical n) + leibniz_vertical_mem |- !n x. 0 < x /\ x <= n + 1 <=> MEM x (leibniz_vertical n) + leibniz_vertical_snoc |- !n. leibniz_vertical (n + 1) = SNOC (n + 2) (leibniz_vertical n) + + leibniz_up_0 |- leibniz_up 0 = [1] + leibniz_up_len |- !n. LENGTH (leibniz_up n) = n + 1 + leibniz_up_pos |- !n. EVERY_POSITIVE (leibniz_up n) + leibniz_up_mem |- !n x. 0 < x /\ x <= n + 1 <=> MEM x (leibniz_up n) + leibniz_up_cons |- !n. leibniz_up (n + 1) = n + 2::leibniz_up n + + leibniz_horizontal_0 |- leibniz_horizontal 0 = [1] + leibniz_horizontal_len |- !n. LENGTH (leibniz_horizontal n) = n + 1 + leibniz_horizontal_el |- !n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k) + leibniz_horizontal_mem |- !n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) + leibniz_horizontal_mem_iff |- !n k. MEM (leibniz n k) (leibniz_horizontal n) <=> k <= n + leibniz_horizontal_member |- !n x. MEM x (leibniz_horizontal n) <=> ?k. k <= n /\ (x = leibniz n k) + leibniz_horizontal_element |- !n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k) + leibniz_horizontal_head |- !n. TAKE 1 (leibniz_horizontal (n + 1)) = [n + 2] + leibniz_horizontal_divisor|- !n k. k <= n ==> leibniz n k divides list_lcm (leibniz_horizontal n) + leibniz_horizontal_pos |- !n. EVERY_POSITIVE (leibniz_horizontal n) + leibniz_horizontal_pos_alt|- !n. POSITIVE (leibniz_horizontal n) + leibniz_horizontal_alt |- !n. leibniz_horizontal n = MAP (\j. (n + 1) * j) (binomial_horizontal n) + leibniz_horizontal_lcm_alt|- !n. list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n) + leibniz_horizontal_sum |- !n. SUM (leibniz_horizontal n) = (n + 1) * SUM (binomial_horizontal n) + leibniz_horizontal_sum_eqn |- !n. SUM (leibniz_horizontal n) = (n + 1) * 2 ** n: + leibniz_horizontal_average |- !n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = + SUM (binomial_horizontal n) + leibniz_horizontal_average_eqn |- !n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = 2 ** n + + Using Triplet and Paths: + triplet_def |- !n k. triplet n k = + <|a := leibniz n k; + b := leibniz (n + 1) k; + c := leibniz (n + 1) (k + 1) + |> + leibniz_triplet_member |- !n k. (ta = leibniz n k) /\ + (tb = leibniz (n + 1) k) /\ (tc = leibniz (n + 1) (k + 1)) + leibniz_right_entry |- !n k. (k + 1) * tc = (n + 1 - k) * tb + leibniz_up_entry |- !n k. (n + 2) * ta = (n + 1 - k) * tb + leibniz_triplet_property |- !n k. ta * tb = tc * (tb - ta) + leibniz_triplet_lcm |- !n k. lcm tb ta = lcm tb tc + + Zigzag Path in Leibniz Triangle: + leibniz_zigzag_def |- !p1 p2. p1 zigzag p2 <=> + ?n k x y. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) + list_lcm_zigzag |- !p1 p2. p1 zigzag p2 ==> (list_lcm p1 = list_lcm p2) + leibniz_zigzag_tail |- !p1 p2. p1 zigzag p2 ==> !x. [x] ++ p1 zigzag [x] ++ p2 + leibniz_horizontal_zigzag |- !n k. k <= n ==> + TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) zigzag + TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) + leibniz_triplet_0 |- leibniz_up 1 zigzag leibniz_horizontal 1 + + Wriggle Paths in Leibniz Triangle: + list_lcm_wriggle |- !p1 p2. p1 wriggle p2 ==> (list_lcm p1 = list_lcm p2) + leibniz_zigzag_wriggle |- !p1 p2. p1 zigzag p2 ==> p1 wriggle p2 + leibniz_wriggle_tail |- !p1 p2. p1 wriggle p2 ==> !x. [x] ++ p1 wriggle [x] ++ p2 + leibniz_wriggle_refl |- !p1. p1 wriggle p1 + leibniz_wriggle_trans |- !p1 p2 p3. p1 wriggle p2 /\ p2 wriggle p3 ==> p1 wriggle p3 + leibniz_horizontal_wriggle_step |- !n k. k <= n + 1 ==> + TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle leibniz_horizontal (n + 1) + leibniz_horizontal_wriggle |- !n. [leibniz (n + 1) 0] ++ leibniz_horizontal n wriggle leibniz_horizontal (n + 1) + + Path Transform keeping LCM: + leibniz_up_wriggle_horizontal |- !n. leibniz_up n wriggle leibniz_horizontal n + leibniz_lcm_property |- !n. list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n) + leibniz_vertical_divisor |- !n k. k <= n ==> leibniz n k divides list_lcm (leibniz_vertical n) + + Lower Bound of Leibniz LCM: + leibniz_horizontal_lcm_lower |- !n. 2 ** n <= list_lcm (leibniz_horizontal n) + leibniz_vertical_lcm_lower |- !n. 2 ** n <= list_lcm (leibniz_vertical n) + lcm_lower_bound |- !n. 2 ** n <= list_lcm [1 .. (n + 1)] + + Leibniz LCM Invariance: + leibniz_col_arm_0 |- !a b. leibniz_col_arm a b 0 = [] + leibniz_seg_arm_0 |- !a b. leibniz_seg_arm a b 0 = [] + leibniz_col_arm_1 |- !a b. leibniz_col_arm a b 1 = [leibniz a b] + leibniz_seg_arm_1 |- !a b. leibniz_seg_arm a b 1 = [leibniz a b] + leibniz_col_arm_len |- !a b n. LENGTH (leibniz_col_arm a b n) = n + leibniz_seg_arm_len |- !a b n. LENGTH (leibniz_seg_arm a b n) = n + leibniz_col_arm_el |- !n k. k < n ==> !a b. EL k (leibniz_col_arm a b n) = leibniz (a - k) b + leibniz_seg_arm_el |- !n k. k < n ==> !a b. EL k (leibniz_seg_arm a b n) = leibniz a (b + k) + leibniz_seg_arm_head |- !a b n. TAKE 1 (leibniz_seg_arm a b (n + 1)) = [leibniz a b] + leibniz_col_arm_cons |- !a b n. leibniz_col_arm (a + 1) b (n + 1) = leibniz (a + 1) b::leibniz_col_arm a b n + + leibniz_seg_arm_zigzag_step |- !n k. k < n ==> !a b. + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) zigzag + TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) + leibniz_seg_arm_wriggle_step |- !n k. k < n + 1 ==> !a b. + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle + leibniz_seg_arm (a + 1) b (n + 1) + leibniz_seg_arm_wriggle_row_arm |- !a b n. [leibniz (a + 1) b] ++ leibniz_seg_arm a b n wriggle + leibniz_seg_arm (a + 1) b (n + 1) + leibniz_col_arm_wriggle_row_arm |- !a b n. b <= a /\ n <= a + 1 - b ==> + leibniz_col_arm a b n wriggle leibniz_seg_arm a b n + leibniz_lcm_invariance |- !a b n. b <= a /\ n <= a + 1 - b ==> + (list_lcm (leibniz_col_arm a b n) = list_lcm (leibniz_seg_arm a b n)) + leibniz_col_arm_n_0 |- !n. leibniz_col_arm n 0 (n + 1) = leibniz_up n + leibniz_seg_arm_n_0 |- !n. leibniz_seg_arm n 0 (n + 1) = leibniz_horizontal n + leibniz_up_wriggle_horizontal_alt |- !n. leibniz_up n wriggle leibniz_horizontal n + leibniz_up_lcm_eq_horizontal_lcm |- !n. list_lcm (leibniz_up n) = list_lcm (leibniz_horizontal n) + + Set GCD as Big Operator: + big_gcd_def |- !s. big_gcd s = ITSET gcd s 0 + big_gcd_empty |- big_gcd {} = 0 + big_gcd_sing |- !x. big_gcd {x} = x + big_gcd_reduction |- !s x. FINITE s /\ x NOTIN s ==> (big_gcd (x INSERT s) = gcd x (big_gcd s)) + big_gcd_is_common_divisor |- !s. FINITE s ==> !x. x IN s ==> big_gcd s divides x + big_gcd_is_greatest_common_divisor + |- !s. FINITE s ==> !m. (!x. x IN s ==> m divides x) ==> m divides big_gcd s + big_gcd_insert |- !s. FINITE s ==> !x. big_gcd (x INSERT s) = gcd x (big_gcd s) + big_gcd_two |- !x y. big_gcd {x; y} = gcd x y + big_gcd_positive |- !s. FINITE s /\ s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s + big_gcd_map_times |- !s. FINITE s /\ s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s + + Set LCM as Big Operator: + big_lcm_def |- !s. big_lcm s = ITSET lcm s 1 + big_lcm_empty |- big_lcm {} = 1 + big_lcm_sing |- !x. big_lcm {x} = x + big_lcm_reduction |- !s x. FINITE s /\ x NOTIN s ==> (big_lcm (x INSERT s) = lcm x (big_lcm s)) + big_lcm_is_common_multiple |- !s. FINITE s ==> !x. x IN s ==> x divides big_lcm s + big_lcm_is_least_common_multiple + |- !s. FINITE s ==> !m. (!x. x IN s ==> x divides m) ==> big_lcm s divides m + big_lcm_insert |- !s. FINITE s ==> !x. big_lcm (x INSERT s) = lcm x (big_lcm s) + big_lcm_two |- !x y. big_lcm {x; y} = lcm x y + big_lcm_positive |- !s. FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s + big_lcm_map_times |- !s. FINITE s /\ s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s + + LCM Lower bound using big LCM: + leibniz_seg_def |- !n k h. leibniz_seg n k h = {leibniz n (k + j) | j IN count h} + leibniz_row_def |- !n h. leibniz_row n h = {leibniz n j | j IN count h} + leibniz_col_def |- !h. leibniz_col h = {leibniz j 0 | j IN count h} + leibniz_col_eq_natural |- !n. leibniz_col n = natural n + big_lcm_seg_transform |- !n k h. lcm (leibniz (n + 1) k) (big_lcm (leibniz_seg n k h)) = + big_lcm (leibniz_seg (n + 1) k (h + 1)) + big_lcm_row_transform |- !n h. lcm (leibniz (n + 1) 0) (big_lcm (leibniz_row n h)) = + big_lcm (leibniz_row (n + 1) (h + 1)) + big_lcm_corner_transform |- !n. big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1)) + big_lcm_count_lower_bound |- !f n. (!x. x IN count (n + 1) ==> 0 < f x) ==> + SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1))) + big_lcm_natural_eqn |- !n. big_lcm (natural (n + 1)) = + (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) + big_lcm_lower_bound |- !n. 2 ** n <= big_lcm (natural (n + 1)) + big_lcm_eq_list_lcm |- !l. big_lcm (set l) = list_lcm l + + List LCM depends only on its set of elements: + list_lcm_absorption |- !x l. MEM x l ==> (list_lcm (x::l) = list_lcm l) + list_lcm_nub |- !l. list_lcm (nub l) = list_lcm l + list_lcm_nub_eq_if_set_eq |- !l1 l2. (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2)) + list_lcm_eq_if_set_eq |- !l1 l2. (set l1 = set l2) ==> (list_lcm l1 = list_lcm l2) + + Set LCM by List LCM: + set_lcm_def |- !s. set_lcm s = list_lcm (SET_TO_LIST s) + set_lcm_empty |- set_lcm {} = 1 + set_lcm_nonempty |- !s. FINITE s /\ s <> {} ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s))) + set_lcm_sing |- !x. set_lcm {x} = x + set_lcm_eq_list_lcm |- !l. set_lcm (set l) = list_lcm l + set_lcm_eq_big_lcm |- !s. FINITE s ==> (set_lcm s = big_lcm s) + set_lcm_insert |- !s. FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm s) + set_lcm_is_common_multiple |- !x s. FINITE s /\ x IN s ==> x divides set_lcm s + set_lcm_is_least_common_multiple |- !s m. FINITE s /\ (!x. x IN s ==> x divides m) ==> set_lcm s divides m + pairwise_coprime_prod_set_eq_set_lcm + |- !s. FINITE s /\ PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s) + pairwise_coprime_prod_set_divides + |- !s m. FINITE s /\ PAIRWISE_COPRIME s /\ + (!x. x IN s ==> x divides m) ==> PROD_SET s divides m + + Nair's Trick (direct): + lcm_run_by_FOLDL |- !n. lcm_run n = FOLDL lcm 1 [1 .. n] + lcm_run_by_FOLDR |- !n. lcm_run n = FOLDR lcm 1 [1 .. n] + lcm_run_0 |- lcm_run 0 = 1 + lcm_run_1 |- lcm_run 1 = 1 + lcm_run_suc |- !n. lcm_run (n + 1) = lcm (n + 1) (lcm_run n) + lcm_run_pos |- !n. 0 < lcm_run n + lcm_run_small |- (lcm_run 2 = 2) /\ (lcm_run 3 = 6) /\ (lcm_run 4 = 12) /\ + (lcm_run 5 = 60) /\ (lcm_run 6 = 60) /\ (lcm_run 7 = 420) /\ + (lcm_run 8 = 840) /\ (lcm_run 9 = 2520) + lcm_run_divisors |- !n. n + 1 divides lcm_run (n + 1) /\ lcm_run n divides lcm_run (n + 1) + lcm_run_monotone |- !n. lcm_run n <= lcm_run (n + 1) + lcm_run_lower |- !n. 2 ** n <= lcm_run (n + 1) + lcm_run_leibniz_divisor |- !n k. k <= n ==> leibniz n k divides lcm_run (n + 1) + lcm_run_lower_odd |- !n. n * 4 ** n <= lcm_run (TWICE n + 1) + lcm_run_lower_even |- !n. n * 4 ** n <= lcm_run (TWICE (n + 1)) + + lcm_run_odd_lower |- !n. ODD n ==> HALF n * HALF (2 ** n) <= lcm_run n + lcm_run_even_lower |- !n. EVEN n ==> HALF (n - 2) * HALF (HALF (2 ** n)) <= lcm_run n + lcm_run_odd_lower_alt |- !n. ODD n /\ 5 <= n ==> 2 ** n <= lcm_run n + lcm_run_even_lower_alt |- !n. EVEN n /\ 8 <= n ==> 2 ** n <= lcm_run n + lcm_run_lower_better |- !n. 7 <= n ==> 2 ** n <= lcm_run n + + Nair's Trick (rework): + lcm_run_odd_factor |- !n. 0 < n ==> n * leibniz (TWICE n) n divides lcm_run (TWICE n + 1) + lcm_run_lower_odd |- !n. n * 4 ** n <= lcm_run (TWICE n + 1) + lcm_run_lower_odd_iff |- !n. ODD n ==> (2 ** n <= lcm_run n <=> 5 <= n) + lcm_run_lower_even_iff |- !n. EVEN n ==> (2 ** n <= lcm_run n <=> (n = 0) \/ 8 <= n) + lcm_run_lower_better_iff |- !n. 2 ** n <= lcm_run n <=> (n = 0) \/ (n = 5) \/ 7 <= n + + Nair's Trick (consecutive): + lcm_upto_def |- (lcm_upto 0 = 1) /\ !n. lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n) + lcm_upto_0 |- lcm_upto 0 = 1 + lcm_upto_SUC |- !n. lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n) + lcm_upto_alt |- (lcm_upto 0 = 1) /\ !n. lcm_upto (n + 1) = lcm (n + 1) (lcm_upto n) + lcm_upto_1 |- lcm_upto 1 = 1 + lcm_upto_small |- (lcm_upto 2 = 2) /\ (lcm_upto 3 = 6) /\ (lcm_upto 4 = 12) /\ + (lcm_upto 5 = 60) /\ (lcm_upto 6 = 60) /\ (lcm_upto 7 = 420) /\ + (lcm_upto 8 = 840) /\ (lcm_upto 9 = 2520) /\ (lcm_upto 10 = 2520) + lcm_upto_eq_list_lcm |- !n. lcm_upto n = list_lcm [1 .. n] + lcm_upto_lower |- !n. 2 ** n <= lcm_upto (n + 1) + lcm_upto_divisors |- !n. n + 1 divides lcm_upto (n + 1) /\ lcm_upto n divides lcm_upto (n + 1) + lcm_upto_monotone |- !n. lcm_upto n <= lcm_upto (n + 1) + lcm_upto_leibniz_divisor |- !n k. k <= n ==> leibniz n k divides lcm_upto (n + 1) + lcm_upto_lower_odd |- !n. n * 4 ** n <= lcm_upto (TWICE n + 1) + lcm_upto_lower_even |- !n. n * 4 ** n <= lcm_upto (TWICE (n + 1)) + lcm_upto_lower_better |- !n. 7 <= n ==> 2 ** n <= lcm_upto n + + Simple LCM lower bounds: + lcm_run_lower_simple |- !n. HALF (n + 1) <= lcm_run n + lcm_run_alt |- !n. lcm_run n = lcm_run (n - 1 + 1) + lcm_run_lower_good |- !n. 2 ** (n - 1) <= lcm_run n + + Upper Bound by Leibniz Triangle: + leibniz_eqn |- !n k. leibniz n k = (n + 1 - k) * binomial (n + 1) k + leibniz_right_alt |- !n k. leibniz n (k + 1) = (n - k) * binomial (n + 1) (k + 1) + leibniz_binomial_identity |- !m n k. k <= m /\ m <= n ==> + (leibniz n k * binomial (n - k) (m - k) = leibniz m k * binomial (n + 1) (m + 1)) + leibniz_divides_leibniz_factor |- !m n k. k <= m /\ m <= n ==> + leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) + leibniz_horizontal_member_divides |- !m n x. n <= TWICE m + 1 /\ m <= n /\ + MEM x (leibniz_horizontal n) ==> + x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) + lcm_run_divides_property |- !m n. n <= TWICE m /\ m <= n ==> + lcm_run n divides lcm_run m * binomial n m + lcm_run_bound_recurrence |- !m n. n <= TWICE m /\ m <= n ==> lcm_run n <= lcm_run m * binomial n m + lcm_run_upper_bound |- !n. lcm_run n <= 4 ** n + + Beta Triangle: + beta_0_n |- !n. beta 0 n = 0 + beta_n_0 |- !n. beta n 0 = 0 + beta_less_0 |- !n k. n < k ==> (beta n k = 0) + beta_eqn |- !n k. beta (n + 1) (k + 1) = leibniz n k + beta_alt |- !n k. 0 < n /\ 0 < k ==> (beta n k = leibniz (n - 1) (k - 1)) + beta_pos |- !n k. 0 < k /\ k <= n ==> 0 < beta n k + beta_eq_0 |- !n k. (beta n k = 0) <=> (k = 0) \/ n < k + beta_sym |- !n k. k <= n ==> (beta n k = beta n (n - k + 1)) + + Beta Horizontal List: + beta_horizontal_0 |- beta_horizontal 0 = [] + beta_horizontal_len |- !n. LENGTH (beta_horizontal n) = n + beta_horizontal_eqn |- !n. beta_horizontal (n + 1) = leibniz_horizontal n + beta_horizontal_alt |- !n. 0 < n ==> (beta_horizontal n = leibniz_horizontal (n - 1)) + beta_horizontal_mem |- !n k. 0 < k /\ k <= n ==> MEM (beta n k) (beta_horizontal n) + beta_horizontal_mem_iff |- !n k. MEM (beta n k) (beta_horizontal n) <=> 0 < k /\ k <= n + beta_horizontal_member |- !n x. MEM x (beta_horizontal n) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k) + beta_horizontal_element |- !n k. k < n ==> (EL k (beta_horizontal n) = beta n (k + 1)) + lcm_run_by_beta_horizontal |- !n. 0 < n ==> (lcm_run n = list_lcm (beta_horizontal n)) + lcm_run_beta_divisor |- !n k. 0 < k /\ k <= n ==> beta n k divides lcm_run n + beta_divides_beta_factor |- !m n k. k <= m /\ m <= n ==> beta n k divides beta m k * binomial n m + lcm_run_divides_property_alt |- !m n. n <= TWICE m /\ m <= n ==> lcm_run n divides binomial n m * lcm_run m + lcm_run_upper_bound |- !n. lcm_run n <= 4 ** n + + LCM Lower Bound using Maximum: + list_lcm_ge_max |- !l. POSITIVE l ==> MAX_LIST l <= list_lcm l + lcm_lower_bound_by_list_lcm |- !n. (n + 1) * binomial n (HALF n) <= list_lcm [1 .. (n + 1)] + big_lcm_ge_max |- !s. FINITE s /\ (!x. x IN s ==> 0 < x) ==> MAX_SET s <= big_lcm s + lcm_lower_bound_by_big_lcm |- !n. (n + 1) * binomial n (HALF n) <= big_lcm (natural (n + 1)) + + Consecutive LCM function: + lcm_lower_bound_by_list_lcm_stirling |- Stirling /\ (!n c. n DIV SQRT (c * (n - 1)) = SQRT (n DIV c)) ==> + !n. ODD n ==> SQRT (n DIV (2 * pi)) * 2 ** n <= list_lcm [1 .. n] + big_lcm_non_decreasing |- !n. big_lcm (natural n) <= big_lcm (natural (n + 1)) + lcm_lower_bound_by_big_lcm_stirling |- Stirling /\ (!n c. n DIV SQRT (c * (n - 1)) = SQRT (n DIV c)) ==> + !n. ODD n ==> SQRT (n DIV (2 * pi)) * 2 ** n <= big_lcm (natural n) + + Extra Theorems: + gcd_prime_product_property |- !p m n. prime p /\ m divides n /\ ~(p * m divides n) ==> (gcd (p * m) n = m) + lcm_prime_product_property |- !p m n. prime p /\ m divides n /\ ~(p * m divides n) ==> (lcm (p * m) n = p * n) + list_lcm_prime_factor |- !p l. prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) + list_lcm_prime_factor_member |- !p l. prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x + +*) + +(* ------------------------------------------------------------------------- *) +(* Leibniz Harmonic Triangle *) +(* ------------------------------------------------------------------------- *) + +(* + +Leibniz Harmonic Triangle (fraction form) + + c <= r +r = 1 1 +r = 2 1/2 1/2 +r = 3 1/3 1/6 1/3 +r = 4 1/4 1/12 1/12 1/4 +r = 5 1/5 1/10 1/20 1/10 1/5 + +In general, L(r,1) = 1/r, L(r,c) = |L(r-1,c-1) - L(r,c-1)| + +Solving, L(r,c) = 1/(r C(r-1,c-1)) = 1/(c C(r,c)) +where C(n,m) is the binomial coefficient of Pascal Triangle. + +c = 1 are the 1/(1 * natural numbers +c = 2 are the 1/(2 * triangular numbers) +c = 3 are the 1/(3 * tetrahedral numbers) + +Sum of denominators of n-th row = n 2**(n-1). + +Note that L(r,c) = Integral(0,1) x ** (c-1) * (1-x) ** (r-c) dx + +Another form: L(n,1) = 1/n, L(n,k) = L(n-1,k-1) - L(n,k-1) +Solving, L(n,k) = 1/ k C(n,k) = 1/ n C(n-1,k-1) + +Still another notation H(n,r) = 1/ (n+1)C(n,r) = (n-r)!r!/(n+1)! for 0 <= r <= n + +Harmonic Denominator Number Triangle (integer form) +g(d,n) = 1/H(d,n) where H(d,h) is the Leibniz Harmonic Triangle +g(d,n) = (n+d)C(d,n) where C(d,h) is the Pascal's Triangle. +g(d,n) = n(n+1)...(n+d)/d! + +(k+1)-th row of Pascal's triangle: x^4 + 4x^3 + 6x^2 + 4x + 1 +Perform differentiation, d/dx -> 4x^3 + 12x^2 + 12x + 4 +which is k-th row of Harmonic Denominator Number Triangle. + +(k+1)-th row of Pascal's triangle: (x+1)^(k+1) +k-th row of Harmonic Denominator Number Triangle: d/dx[(x+1)^(k+1)] + + d/dx[(x+1)^(k+1)] += d/dx[SUM C(k+1,j) x^j] j = 0...(k+1) += SUM C(k+1,j) d/dx[x^j] += SUM C(k+1,j) j x^(j-1) j = 1...(k+1) += SUM C(k+1,j+1) (j+1) x^j j = 0...k += SUM D(k,j) x^j with D(k,j) = (j+1) C(k+1,j+1) ??? + +*) + +(* Another presentation of triangles: + +The harmonic triangle of Leibniz + 1/1 1/2 1/3 1/4 1/5 .... harmonic fractions + 1/2 1/6 1/12 1/20 .... successive difference + 1/3 1/12 1/30 ... + 1/4 1/20 ... ... + 1/5 ... ... ... + +Pascal's triangle + 1 1 1 1 1 1 1 .... units + 1 2 3 4 5 6 .... sum left and above + 1 3 6 10 15 21 + 1 4 10 20 35 + 1 5 15 35 + 1 6 21 + + +*) + +(* LCM Lemma + +(n+1) lcm (C(n,0) to C(n,n)) = lcm (1 to (n+1)) + +m-th number in the n-th row of Leibniz triangle is: 1/ (n+1)C(n,m) + +LHS = (n+1) LCM (C(n,0), C(n,1), ..., C(n,n)) = lcd of fractions in n-th row of Leibniz triangle. + +Any such number is an integer linear combination of fractions on triangle’s sides +1/1, 1/2, 1/3, ... 1/n, and vice versa. + +So LHS = lcd (1/1, 1/2, 1/3, ..., 1/n) = RHS = lcm (1,2,3, ..., (n+1)). + +0-th row: 1 +1-st row: 1/2 1/2 +2-nd row: 1/3 1/6 1/3 +3-rd row: 1/4 1/12 1/12 1/4 +4-th row: 1/5 1/20 1/30 1/20 1/5 + +4-th row: 1/5 C(4,m), C(4,m) = 1 4 6 4 1, hence 1/5 1/20 1/30 1/20 1/5 + lcd (1/5 1/20 1/30 1/20 1/5) += lcm (5, 20, 30, 20, 5) += lcm (5 C(4,0), 5 C(4,1), 5 C(4,2), 5 C(4,3), 5 C(4,4)) += 5 lcm (C(4,0), C(4,1), C(4,2), C(4,3), C(4,4)) + +But 1/5 = harmonic + 1/20 = 1/4 - 1/5 = combination of harmonic + 1/30 = 1/12 - 1/20 = (1/3 - 1/4) - (1/4 - 1/5) = combination of harmonic + + lcd (1/5 1/20 1/30 1/20 1/5) += lcd (combination of harmonic from 1/1 to 1/5) += lcd (1/1 to 1/5) += lcm (1 to 5) + +Theorem: lcd (1/x 1/y 1/z) = lcm (x y z) +Theorem: lcm (kx ky kz) = k lcm (x y z) +Theorem: lcd (combination of harmonic from 1/1 to 1/n) = lcd (1/1 to 1/n) +Then apply first theorem, lcd (1/1 to 1/n) = lcm (1 to n) +*) + +(* LCM Bound + 0 < n ==> 2^(n-1) < lcm (1 to n) + + lcm (1 to n) += n lcm (C(n-1,0) to C(n-1,n-1)) by LCM Lemma +>= n max (0 <= j <= n-1) C(n-1,j) +>= SUM (0 <= j <= n-1) C(n-1,j) += 2^(n-1) + + lcm (1 to 5) += 5 lcm (C(4,0), C(4,1), C(4,2), C(4,3), C(4,4)) + + +>= C(4,0) + C(4,1) + C(4,2) + C(4,3) + C(4,4) += (1 + 1)^4 += 2^4 + + lcm (1 to 5) = 1x2x3x4x5/2 = 60 += 5 lcm (1 4 6 4 1) = 5 x 12 += lcm (1 4 6 4 1) --> unfold 5x to add 5 times + + lcm (1 4 6 4 1) + + lcm (1 4 6 4 1) + + lcm (1 4 6 4 1) + + lcm (1 4 6 4 1) +>= 1 + 4 + 6 + 4 + 1 --> pick one of each 5 C(n,m), i.e. diagonal += (1 + 1)^4 --> fold back binomial += 2^4 = 16 + +Actually, can take 5 lcm (1 4 6 4 1) >= 5 x 6 = 30, +but this will need estimation of C(n, n/2), or C(2n,n), involving Stirling's formula. + +Theorem: lcm (x y z) >= x or lcm (x y z) >= y or lcm (x y z) >= z + +*) + +(* + +More generally, there is an identity for 0 <= k <= n: + +(n+1) lcm (C(n,0), C(n,1), ..., C(n,k)) = lcm (n+1, n, n-1, ..., n+1-k) + +This is simply that fact that any integer linear combination of +f(x), delta f(x), delta^2 f(x), ..., delta^k f(x) +is an integer linear combination of f(x), f(x-1), f(x-2), ..., f(x-k) +where delta is the difference operator, f(x) = 1/x, and x = n+1. + +BTW, Leibnitz harmonic triangle too gives this identity. + +That's correct, but the use of absolute values in the Leibniz triangle and +its specialized definition somewhat obscures the generic, linear nature of the identity. + + f(x) = f(n+1) = 1/(n+1) +f(x-1) = f(n) = 1/n +f(x-2) = f(n-1) = 1/(n-1) +f(x-k) = f(n+1-k) = 1/(n+1-k) + + f(x) = f(n+1) = 1/(n+1) = 1/(n+1)C(n,0) + delta f(x) = f(x-1) - f(x) = 1/n - 1/(n+1) = 1/n(n+1) = 1/(n+1)C(n,1) + = C(1,0) f(x-1) - C(1,1) f(x) +delta^2 f(x) = delta f(x-1) - delta f(x) = 1/(n-1)n - 1/n(n+1) + = (n(n+1) - n(n-1))/(n)(n+1)(n)(n-1) + = 2n/n(n+1)n(n-1) = 1/(n+1)(n(n-1)/2) = 1/(n+1)C(n,2) +delta^2 f(x) = delta f(x-1) - delta f(x) + = (f(x-2) - f(x-1)) - (f(x-1) - f(x)) + = f(x-2) - 2 f(x-1) + f(x) + = C(2,0) f(x-2) - C(2,1) f(x-1) + C(2,2) f(x) +delta^3 f(x) = delta^2 f(x-1) - delta^2 f(x) + = (f(x-3) - 2 f(x-2) + f(x-1)) - (f(x-2) - 2 f(x-1) + f(x)) + = f(x-3) - 3 f(x-2) + 3 f(x-1) - f(x) + = C(3,0) f(x-3) - C(3,1) f(x-2) + C(3,2) f(x-2) - C(3,3) f(x) + +delta^k f(x) = C(k,0) f(x-k) - C(k,1) f(x-k+1) + ... + (-1)^k C(k,k) f(x) + = SUM(0 <= j <= k) (-1)^k C(k,j) f(x-k+j) +Also, + f(x) = 1/(n+1)C(n,0) + delta f(x) = 1/(n+1)C(n,1) +delta^2 f(x) = 1/(n+1)C(n,2) +delta^k f(x) = 1/(n+1)C(n,k) + +so lcd (f(x), df(x), d^2f(x), ..., d^kf(x)) + = lcm ((n+1)C(n,0),(n+1)C(n,1),...,(n+1)C(n,k)) by lcd-to-lcm + = lcd (f(x), f(x-1), f(x-2), ..., f(x-k)) by linear combination + = lcm ((n+1), n, (n-1), ..., (n+1-k)) by lcd-to-lcm + +How to formalize: +lcd (f(x), df(x), d^2f(x), ..., d^kf(x)) = lcd (f(x), f(x-1), f(x-2), ..., f(x-k)) + +Simple case: lcd (f(x), df(x)) = lcd (f(x), f(x-1)) + + lcd (f(x), df(x)) += lcd (f(x), f(x-1) - f(x)) += lcd (f(x), f(x-1)) + +Can we have + LCD {f(x), df(x)} += LCD {f(x), f(x-1) - f(x)} = LCD {1/x, 1/(x-1) - 1/x} += LCD {f(x), f(x-1), f(x)} = lcm {x, x(x-1)} += LCD {f(x), f(x-1)} = x(x-1) = lcm {x, x-1} = LCD {1/x, 1/(x-1)} + +*) + +(* Step 1: From Pascal's Triangle to Leibniz's Triangle + +Pascal's Triangle: + +row 0 1 +row 1 1 1 +row 2 1 2 1 +row 3 1 3 3 1 +row 4 1 4 6 4 1 +row 5 1 5 10 10 5 1 + +The rule is: boundary = 1, entry = up + left-up + or: C(n,0) = 1, C(n,k) = C(n-1,k) + C(n-1,k-1) + +Multiple each row by successor of its index, i.e. row n -> (n + 1) (row n): +Multiples Triangle (or Modified Triangle): + +1 * row 0 1 +2 * row 1 2 2 +3 * row 2 3 6 3 +4 * row 3 4 12 12 4 +5 * row 4 5 20 30 20 5 +6 * row 5 6 30 60 60 30 6 + +The rule is: boundary = n, entry = left * left-up / (left - left-up) + or: L(n,0) = n, L(n,k) = L(n,k-1) * L(n-1,k-1) / (L(n,k-1) - L(n-1,k-1)) + +Then lcm(1, 2) + = lcm(2) + = lcm(2, 2) + + lcm(1, 2, 3) + = lcm(lcm(1,2), 3) using lcm(1,2,...,n,n+1) = lcm(lcm(1,2,...,n), n+1) + = lcm(2, 3) using lcm(1,2) + = lcm(2*3/1, 3) using lcm(L(n,k-1), L(n-1,k-1)) = lcm(L(n,k-1), L(n-1,k-1)/(L(n,k-1), L(n-1,k-1)), L(n-1,k-1)) + = lcm(6, 3) + = lcm(3, 6, 3) + + lcm(1, 2, 3, 4) + = lcm(lcm(1,2,3), 4) + = lcm(lcm(6,3), 4) + = lcm(6, 3, 4) + = lcm(6, 3*4/1, 4) + = lcm(6, 12, 4) + = lcm(6*12/6, 12, 4) + = lcm(12, 12, 4) + = lcm(4, 12, 12, 4) + + lcm(1, 2, 3, 4, 5) + = lcm(lcm(2,3,4), 5) + = lcm(lcm(12,4), 5) + = lcm(12, 4, 5) + = lcm(12, 4*5/1, 5) + = lcm(12, 20, 5) + = lcm(12*20/8, 20, 5) + = lcm(30, 20, 5) + = lcm(5, 20, 30, 20, 5) + + lcm(1, 2, 3, 4, 5, 6) + = lcm(lcm(1, 2, 3, 4, 5), 6) + = lcm(lcm(30,20,5), 6) + = lcm(30, 20, 5, 6) + = lcm(30, 20, 5*6/1, 6) + = lcm(30, 20, 30, 6) + = lcm(30, 20*30/10, 30, 6) + = lcm(20, 60, 30, 6) + = lcm(20*60/40, 60, 30, 6) + = lcm(30, 60, 30, 6) + = lcm(6, 30, 60, 30, 6) + +Invert each entry of Multiples Triangle into a unit fraction: +Leibniz's Triangle: + +1/(1 * row 0) 1/1 +1/(2 * row 1) 1/2 1/2 +1/(3 * row 2) 1/3 1/6 1/3 +1/(4 * row 3) 1/4 1/12 1/12 1/4 +1/(5 * row 4) 1/5 1/20 1/30 1/20 1/5 +1/(6 * row 5) 1/6 1/30 1/60 1/60 1/30 1/6 + +Theorem: In the Multiples Triangle, the vertical-lcm = horizontal-lcm. +i.e. lcm (1, 2, 3) = lcm (3, 6, 3) = 6 + lcm (1, 2, 3, 4) = lcm (4, 12, 12, 4) = 12 + lcm (1, 2, 3, 4, 5) = lcm (5, 20, 30, 20, 5) = 60 + lcm (1, 2, 3, 4, 5, 6) = lcm (6, 30, 60, 60, 30, 6) = 60 +Proof: With reference to Leibniz's Triangle, note: term = left-up - left + lcm (5, 20, 30, 20, 5) += lcm (5, 20, 30) by reduce repetition += lcm (5, d(1/20), d(1/30)) by denominator of fraction += lcm (5, d(1/4 - 1/5), d(1/30)) by term = left-up - left += lcm (5, lcm(4, 5), d(1/12 - 1/20)) by denominator of fraction subtraction += lcm (5, 4, lcm(12, 20)) by lcm (a, lcm (a, b)) = lcm (a, b) += lcm (5, 4, lcm(d(1/12), d(1/20))) to fraction again += lcm (5, 4, lcm(d(1/3 - 1/4), d(1/4 - 1/5))) by Leibniz's Triangle += lcm (5, 4, lcm(lcm(3,4), lcm(4,5))) by fraction subtraction denominator += lcm (5, 4, lcm(3, 4, 5)) by lcm merge += lcm (5, 4, 3) merge again += lcm (5, 4, 3, 2) by lcm include factor (!!!) += lcm (5, 4, 3, 2, 1) by lcm include 1 + +Note: to make 30, need 12, 20 + to make 12, need 3, 4; to make 20, need 4, 5 + lcm (1, 2, 3, 4, 5) += lcm (1, 2, lcm(3,4), lcm(4,5), 5) += lcm (1, 2, d(1/3 - 1/4), d(1/4 - 1/5), 5) += lcm (1, 2, d(1/12), d(1/20), 5) += lcm (1, 2, 12, 20, 5) += lcm (1, 2, lcm(12, 20), 20, 5) += lcm (1, 2, d(1/12 - 1/20), 20, 5) += lcm (1, 2, d(1/30), 20, 5) += lcm (1, 2, 30, 20, 5) += lcm (1, 30, 20, 5) can drop factor !! += lcm (30, 20, 5) can drop 1 += lcm (5, 20, 30, 20, 5) + + lcm (1, 2, 3, 4, 5, 6) += lcm (lcm (1, 2, 3, 4, 5), lcm(5,6), 6) += lcm (lcm (5, 20, 30, 20, 5), d(1/5 - 1/6), 6) += lcm (lcm (5, 20, 30, 20, 5), d(1/30), 6) += lcm (lcm (5, 20, 30, 20, 5), 30, 6) += lcm (lcm (5, 20, 30, 20, 5), 30, 6) += lcm (5, 30, 20, 6) += lcm (30, 20, 6) can drop factor !! += lcm (lcm(20, 30), 30, 6) += lcm (d(1/20 - 1/30), 30, 6) += lcm (d(1/60), 30, 6) += lcm (60, 30, 6) += lcm (6, 30, 60, 30, 6) + + lcm (1, 2) += lcm (lcm(1,2), 2) += lcm (2, 2) + + lcm (1, 2, 3) += lcm (lcm(1, 2), 3) += lcm (2, 3) --> lcm (2x3/(3-2), 3) = lcm (6, 3) += lcm (lcm(2, 3), 3) --> lcm (6, 3) = lcm (3, 6, 3) += lcm (d(1/2 - 1/3), 3) += lcm (d(1/6), 3) += lcm (6, 3) = lcm (3, 6, 3) + + lcm (1, 2, 3, 4) += lcm (lcm(1, 2, 3), 4) += lcm (lcm(6, 3), 4) += lcm (6, 3, 4) += lcm (6, lcm(3, 4), 4) --> lcm (6, 12, 4) = lcm (6x12/(12-6), 12, 4) += lcm (6, d(1/3 - 1/4), 4) = lcm (12, 12, 4) = lcm (4, 12, 12, 4) += lcm (6, d(1/12), 4) += lcm (6, 12, 4) += lcm (lcm(6, 12), 4) += lcm (d(1/6 - 1/12), 4) += lcm (d(1/12), 4) += lcm (12, 4) = lcm (4, 12, 12, 4) + + lcm (1, 2, 3, 4, 5) += lcm (lcm(1, 2, 3, 4), 5) += lcm (lcm(12, 4), 5) += lcm (12, 4, 5) += lcm (12, lcm(4,5), 5) --> lcm (12, 20, 5) = lcm (12x20/(20-12), 20, 5) += lcm (12, d(1/4 - 1/5), 5) = lcm (240/8, 20, 5) but lcm(12,20) != 30 += lcm (12, d(1/20), 5) = lcm (30, 20, 5) use lcm(a,b,c) = lcm(ab/(b-a), b, c) += lcm (12, 20, 5) += lcm (lcm(12,20), 20, 5) += lcm (d(1/12 - 1/20), 20, 5) += lcm (d(1/30), 20, 5) += lcm (30, 20, 5) = lcm (5, 20, 30, 20, 5) + + lcm (1, 2, 3, 4, 5, 6) += lcm (lcm(1, 2, 3, 4, 5), 6) += lcm (lcm(30, 20, 5), 6) += lcm (30, 20, 5, 6) += lcm (30, 20, lcm(5,6), 6) --> lcm (30, 20, 30, 6) = lcm (30, 20x30/(30-20), 30, 6) += lcm (30, 20, d(1/5 - 1/6), 6) = lcm (30, 60, 30, 6) += lcm (30, 20, d(1/30), 6) = lcm (30x60/(60-30), 60, 30, 6) += lcm (30, 20, 30, 6) = lcm (60, 60, 30, 6) += lcm (30, lcm(20,30), 30, 6) += lcm (30, d(1/20 - 1/30), 30, 6) += lcm (30, d(1/60), 30, 6) += lcm (30, 60, 30, 6) += lcm (lcm(30, 60), 60, 30, 6) += lcm (d(1/30 - 1/60), 60, 30, 6) += lcm (d(1/60), 60, 30, 6) += lcm (60, 60, 30, 6) += lcm (60, 30, 6) = lcm (6, 30, 60, 60, 30, 6) + +*) + +(* ------------------------------------------------------------------------- *) +(* Helper Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: R^* x y /\ R^* y z ==> R^* x z *) +(* Proof: by RTC_TRANSITIVE, transitive_def *) +val RTC_TRANS = store_thm( + "RTC_TRANS", + ``R^* x y /\ R^* y z ==> R^* x z``, + metis_tac[RTC_TRANSITIVE, transitive_def]); + +(* ------------------------------------------------------------------------- *) +(* Leibniz Triangle (Denominator form) *) +(* ------------------------------------------------------------------------- *) + +(* Define Leibniz Triangle *) +val leibniz_def = Define` + leibniz n k = (n + 1) * binomial n k +`; + +(* export simple definition *) +val _ = export_rewrites["leibniz_def"]; + +(* Theorem: leibniz 0 n = if n = 0 then 1 else 0 *) +(* Proof: + leibniz 0 n + = (0 + 1) * binomial 0 n by leibniz_def + = if n = 0 then 1 else 0 by binomial_n_0 +*) +val leibniz_0_n = store_thm( + "leibniz_0_n", + ``!n. leibniz 0 n = if n = 0 then 1 else 0``, + rw[binomial_0_n]); + +(* Theorem: leibniz n 0 = n + 1 *) +(* Proof: + leibniz n 0 + = (n + 1) * binomial n 0 by leibniz_def + = (n + 1) * 1 by binomial_n_0 + = n + 1 +*) +val leibniz_n_0 = store_thm( + "leibniz_n_0", + ``!n. leibniz n 0 = n + 1``, + rw[binomial_n_0]); + +(* Theorem: leibniz n n = n + 1 *) +(* Proof: + leibniz n n + = (n + 1) * binomial n n by leibniz_def + = (n + 1) * 1 by binomial_n_n + = n + 1 +*) +val leibniz_n_n = store_thm( + "leibniz_n_n", + ``!n. leibniz n n = n + 1``, + rw[binomial_n_n]); + +(* Theorem: n < k ==> leibniz n k = 0 *) +(* Proof: + leibniz n k + = (n + 1) * binomial n k by leibniz_def + = (n + 1) * 0 by binomial_less_0 + = 0 +*) +val leibniz_less_0 = store_thm( + "leibniz_less_0", + ``!n k. n < k ==> (leibniz n k = 0)``, + rw[binomial_less_0]); + +(* Theorem: k <= n ==> (leibniz n k = leibniz n (n-k)) *) +(* Proof: + leibniz n k + = (n + 1) * binomial n k by leibniz_def + = (n + 1) * binomial n (n-k) by binomial_sym + = leibniz n (n-k) by leibniz_def +*) +val leibniz_sym = store_thm( + "leibniz_sym", + ``!n k. k <= n ==> (leibniz n k = leibniz n (n-k))``, + rw[leibniz_def, GSYM binomial_sym]); + +(* Theorem: k < HALF n ==> leibniz n k < leibniz n (k + 1) *) +(* Proof: + Assume k < HALF n, and note that 0 < (n + 1). + leibniz n k < leibniz n (k + 1) + <=> (n + 1) * binomial n k < (n + 1) * binomial n (k + 1) by leibniz_def + <=> binomial n k < binomial n (k + 1) by LT_MULT_LCANCEL + <=> T by binomial_monotone +*) +val leibniz_monotone = store_thm( + "leibniz_monotone", + ``!n k. k < HALF n ==> leibniz n k < leibniz n (k + 1)``, + rw[leibniz_def, binomial_monotone]); + +(* Theorem: k <= n ==> 0 < leibniz n k *) +(* Proof: + Since leibniz n k = (n + 1) * binomial n k by leibniz_def + and 0 < n + 1, 0 < binomial n k by binomial_pos + Hence 0 < leibniz n k by ZERO_LESS_MULT +*) +val leibniz_pos = store_thm( + "leibniz_pos", + ``!n k. k <= n ==> 0 < leibniz n k``, + rw[leibniz_def, binomial_pos, ZERO_LESS_MULT, DECIDE``!n. 0 < n + 1``]); + +(* Theorem: (leibniz n k = 0) <=> n < k *) +(* Proof: + leibniz n k = 0 + <=> (n + 1) * (binomial n k = 0) by leibniz_def + <=> binomial n k = 0 by MULT_EQ_0, n + 1 <> 0 + <=> n < k by binomial_eq_0 +*) +val leibniz_eq_0 = store_thm( + "leibniz_eq_0", + ``!n k. (leibniz n k = 0) <=> n < k``, + rw[leibniz_def, binomial_eq_0]); + +(* Theorem: leibniz n = (\j. (n + 1) * j) o (binomial n) *) +(* Proof: by leibniz_def and function equality. *) +val leibniz_alt = store_thm( + "leibniz_alt", + ``!n. leibniz n = (\j. (n + 1) * j) o (binomial n)``, + rw[leibniz_def, FUN_EQ_THM]); + +(* Theorem: leibniz n k = (\j. (n + 1) * j) (binomial n k) *) +(* Proof: by leibniz_def *) +val leibniz_def_alt = store_thm( + "leibniz_def_alt", + ``!n k. leibniz n k = (\j. (n + 1) * j) (binomial n k)``, + rw_tac std_ss[leibniz_def]); + +(* +Picture of Leibniz Triangle L-corner: + b = L (n-1) k + a = L n k c = L n (k+1) + +a = L n k = (n+1) * (n, k, n-k) = (n+1, k, n-k) = (n+1)! / k! (n-k)! +b = L (n-1) k = n * (n-1, k, n-1-k) = (n , k, n-k-1) = n! / k! (n-k-1)! = a * (n-k)/(n+1) +c = L n (k+1) = (n+1) * (n, k+1, n-(k+1)) = (n+1, k+1, n-k-1) = (n+1)! / (k+1)! (n-k-1)! = a * (n-k)/(k+1) + +a * b = a * a * (n-k)/(n+1) +a - b = a - a * (n-k)/(n+1) = a * (1 - (n-k)/(n+1)) = a * (n+1 - n+k)/(n+1) = a * (k+1)/(n+1) +Hence + a * b /(a - b) += [a * a * (n-k)/(n+1)] / [a * (k+1)/(n+1)] += a * (n-k)/(k+1) += c +or a * b = c * (a - b) +*) + +(* Theorem: 0 < n ==> !k. (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k *) +(* Proof: + (n + 1) * leibniz (n - 1) k + = (n + 1) * ((n-1 + 1) * binomial (n-1) k) by leibniz_def + = (n + 1) * (n * binomial (n-1) k) by SUB_ADD, 1 <= n. + = (n + 1) * ((n - k) * (binomial n k)) by binomial_up_eqn + = ((n + 1) * (n - k)) * binomial n k by MULT_ASSOC + = ((n - k) * (n + 1)) * binomial n k by MULT_COMM + = (n - k) * ((n + 1) * binomial n k) by MULT_ASSOC + = (n - k) * leibniz n k by leibniz_def +*) +val leibniz_up_eqn = store_thm( + "leibniz_up_eqn", + ``!n. 0 < n ==> !k. (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k``, + rw[leibniz_def] >> + `1 <= n` by decide_tac >> + metis_tac[SUB_ADD, binomial_up_eqn, MULT_ASSOC, MULT_COMM]); + +(* Theorem: 0 < n ==> !k. leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1) *) +(* Proof: + Since (n + 1) * leibniz (n - 1) k = (n - k) * leibniz n k by leibniz_up_eqn + leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1) by DIV_SOLVE, 0 < n+1. +*) +val leibniz_up = store_thm( + "leibniz_up", + ``!n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * leibniz n k DIV (n + 1)``, + rw[leibniz_up_eqn, DIV_SOLVE]); + +(* Theorem: 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k *) +(* Proof: + leibniz (n - 1) k + = (n - k) * leibniz n k DIV (n + 1) by leibniz_up, 0 < n + = (n - k) * ((n + 1) * binomial n k) DIV (n + 1) by leibniz_def + = (n + 1) * ((n - k) * binomial n k) DIV (n + 1) by MULT_ASSOC, MULT_COMM + = (n - k) * binomial n k by MULT_DIV, 0 < n + 1 +*) +val leibniz_up_alt = store_thm( + "leibniz_up_alt", + ``!n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k``, + metis_tac[leibniz_up, leibniz_def, MULT_DIV, MULT_ASSOC, MULT_COMM, DECIDE``0 < x + 1``]); + +(* Theorem: 0 < n ==> !k. (k + 1) * leibniz n (k+1) = (n - k) * leibniz n k *) +(* Proof: + (k + 1) * leibniz n (k+1) + = (k + 1) * ((n + 1) * binomial n (k+1)) by leibniz_def + = (k + 1) * (n + 1) * binomial n (k+1) by MULT_ASSOC + = (n + 1) * (k + 1) * binomial n (k+1) by MULT_COMM + = (n + 1) * ((k + 1) * binomial n (k+1)) by MULT_ASSOC + = (n + 1) * ((n - k) * (binomial n k)) by binomial_right_eqn + = ((n + 1) * (n - k)) * binomial n k by MULT_ASSOC + = ((n - k) * (n + 1)) * binomial n k by MULT_COMM + = (n - k) * ((n + 1) * binomial n k) by MULT_ASSOC + = (n - k) * leibniz n k by leibniz_def +*) +val leibniz_right_eqn = store_thm( + "leibniz_right_eqn", + ``!n. 0 < n ==> !k. (k + 1) * leibniz n (k+1) = (n - k) * leibniz n k``, + metis_tac[leibniz_def, MULT_COMM, MULT_ASSOC, binomial_right_eqn]); + +(* Theorem: 0 < n ==> !k. leibniz n (k+1) = (n - k) * (leibniz n k) DIV (k + 1) *) +(* Proof: + Since (k + 1) * leibniz n (k+1) = (n - k) * leibniz n k by leibniz_right_eqn + leibniz n (k+1) = (n - k) * (leibniz n k) DIV (k+1) by DIV_SOLVE, 0 < k+1. +*) +val leibniz_right = store_thm( + "leibniz_right", + ``!n. 0 < n ==> !k. leibniz n (k+1) = (n - k) * (leibniz n k) DIV (k+1)``, + rw[leibniz_right_eqn, DIV_SOLVE]); + +(* Note: Following is the property from Leibniz Harmonic Triangle: + 1 / leibniz n (k+1) = 1 / leibniz (n-1) k - 1 / leibniz n k + = (leibniz n k - leibniz (n-1) k) / leibniz n k * leibniz (n-1) k +*) + +(* The Idea: + b +Actually, lcm a b = lcm b c = lcm c a for a c in Leibniz Triangle. +The only relationship is: c = ab/(a - b), or ab = c(a - b). + +Is this a theorem: ab = c(a - b) ==> lcm a b = lcm b c = lcm c a +Or in fractions, 1/c = 1/b - 1/a ==> lcm a b = lcm b c = lcm c a ? + +lcm a b += a b / (gcd a b) += c(a - b) / (gcd a (a - b)) += ac(a - b) / gcd a (a-b) / a += lcm (a (a-b)) c / a += lcm (ca c(a-b)) / a += lcm (ca ab) / a += lcm (b c) + +lcm a b = a b / gcd a b = a b / gcd a (a-b) = a b c / gcd ca c(a-b) += c (a-b) c / gcd ca c(a-b) = lcm ca c(a-b) / a = lcm ca ab / a = lcm b c + + lcm b c += b c / gcd b c += a b c / gcd a*b a*c += a b c / gcd c*(a-b) c*a += a b / gcd (a-b) a += a b / gcd b a += lcm (a b) += lcm a b + + lcm a c += a c / gcd a c += a b c / gcd b*a b*c += a b c / gcd c*(a-b) b*c += a b / gcd (a-b) b += a b / gcd a b += lcm a b + +Yes! + +This is now in LCM_EXCHANGE: +val it = |- !a b c. (a * b = c * (a - b)) ==> (lcm a b = lcm a c): thm +*) + +(* Theorem: 0 < n ==> + !k. leibniz n k * leibniz (n-1) k = leibniz n (k+1) * (leibniz n k - leibniz (n-1) k) *) +(* Proof: + If n <= k, + then n-1 < k, and n < k+1. + so leibniz (n-1) k = 0 by leibniz_less_0, n-1 < k. + and leibniz n (k+1) = 0 by leibniz_less_0, n < k+1. + Hence true by MULT_EQ_0 + Otherwise, k < n, or k <= n. + then (n+1) - (n-k) = k+1. + + (k + 1) * (c * (a - b)) + = (k + 1) * c * (a - b) by MULT_ASSOC + = ((n+1) - (n-k)) * c * (a - b) by above + = (n - k) * a * (a - b) by leibniz_right_eqn + = (n - k) * a * a - (n - k) * a * b by LEFT_SUB_DISTRIB + = (n + 1) * b * a - (n - k) * a * b by leibniz_up_eqn + = (n + 1) * (a * b) - (n - k) * (a * b) by MULT_ASSOC, MULT_COMM + = ((n+1) - (n-k)) * (a * b) by RIGHT_SUB_DISTRIB + = (k + 1) * (a * b) by above + + Since (k+1) <> 0, the result follows by MULT_LEFT_CANCEL +*) +val leibniz_property = store_thm( + "leibniz_property", + ``!n. 0 < n ==> + !k. leibniz n k * leibniz (n-1) k = leibniz n (k+1) * (leibniz n k - leibniz (n-1) k)``, + rpt strip_tac >> + Cases_on `n <= k` >- + rw[leibniz_less_0] >> + `(n+1) - (n-k) = k+1` by decide_tac >> + `(k+1) <> 0` by decide_tac >> + qabbrev_tac `a = leibniz n k` >> + qabbrev_tac `b = leibniz (n - 1) k` >> + qabbrev_tac `c = leibniz n (k + 1)` >> + `(k + 1) * (c * (a - b)) = ((n+1) - (n-k)) * c * (a - b)` by rw_tac std_ss[MULT_ASSOC] >> + `_ = (n - k) * a * (a - b)` by rw_tac std_ss[leibniz_right_eqn, Abbr`c`, Abbr`a`] >> + `_ = (n - k) * a * a - (n - k) * a * b` by rw_tac std_ss[LEFT_SUB_DISTRIB] >> + `_ = (n + 1) * b * a - (n - k) * a * b` by rw_tac std_ss[leibniz_up_eqn, Abbr`b`, Abbr`a`] >> + `_ = (n + 1) * (a * b) - (n - k) * (a * b)` by metis_tac[MULT_ASSOC, MULT_COMM] >> + `_ = ((n+1) - (n-k)) * (a * b)` by rw_tac std_ss[RIGHT_SUB_DISTRIB] >> + `_ = (k + 1) * (a * b)` by rw_tac std_ss[] >> + metis_tac[MULT_LEFT_CANCEL]); + +(* Theorem: k <= n ==> (leibniz n k = (n + 1) * FACT n DIV (FACT k * FACT (n - k))) *) +(* Proof: + Note (FACT k * FACT (n - k)) divides (FACT n) by binomial_is_integer + and 0 < FACT k * FACT (n - k) by FACT_LESS, ZERO_LESS_MULT + leibniz n k + = (n + 1) * binomial n k by leibniz_def + = (n + 1) * (FACT n DIV (FACT k * FACT (n - k))) by binomial_formula3 + = (n + 1) * FACT n DIV (FACT k * FACT (n - k)) by MULTIPLY_DIV +*) +val leibniz_formula = store_thm( + "leibniz_formula", + ``!n k. k <= n ==> (leibniz n k = (n + 1) * FACT n DIV (FACT k * FACT (n - k)))``, + metis_tac[leibniz_def, binomial_formula3, binomial_is_integer, FACT_LESS, MULTIPLY_DIV, ZERO_LESS_MULT]); + +(* Theorem: 0 < n ==> + !k. k < n ==> leibniz n (k+1) = leibniz n k * leibniz (n-1) k DIV (leibniz n k - leibniz (n-1) k) *) +(* Proof: + By leibniz_property, + leibniz n (k+1) * (leibniz n k - leibniz (n-1) k) = leibniz n k * leibniz (n-1) k + Since 0 < leibniz n k and 0 < leibniz (n-1) k by leibniz_pos + so 0 < (leibniz n k - leibniz (n-1) k) by MULT_EQ_0 + Hence by MULT_COMM, DIV_SOLVE, 0 < (leibniz n k - leibniz (n-1) k), + leibniz n (k+1) = leibniz n k * leibniz (n-1) k DIV (leibniz n k - leibniz (n-1) k) +*) +val leibniz_recurrence = store_thm( + "leibniz_recurrence", + ``!n. 0 < n ==> + !k. k < n ==> (leibniz n (k+1) = leibniz n k * leibniz (n-1) k DIV (leibniz n k - leibniz (n-1) k))``, + rpt strip_tac >> + `k <= n /\ k <= (n-1)` by decide_tac >> + `leibniz n (k+1) * (leibniz n k - leibniz (n-1) k) = leibniz n k * leibniz (n-1) k` by rw[leibniz_property] >> + `0 < leibniz n k /\ 0 < leibniz (n-1) k` by rw[leibniz_pos] >> + `0 < (leibniz n k - leibniz (n-1) k)` by metis_tac[MULT_EQ_0, NOT_ZERO_LT_ZERO] >> + rw_tac std_ss[DIV_SOLVE, MULT_COMM]); + +(* Theorem: 0 < k /\ k <= n ==> + (leibniz n k = leibniz n (k-1) * leibniz (n-1) (k-1) DIV (leibniz n (k-1) - leibniz (n-1) (k-1))) *) +(* Proof: + Since 0 < k, k = SUC h for some h + or k = h + 1 by ADD1 + and h = k - 1 by arithmetic + Since 0 < k and k <= n, + 0 < n and h < n. + Hence true by leibniz_recurrence. +*) +val leibniz_n_k = store_thm( + "leibniz_n_k", + ``!n k. 0 < k /\ k <= n ==> + (leibniz n k = leibniz n (k-1) * leibniz (n-1) (k-1) DIV (leibniz n (k-1) - leibniz (n-1) (k-1)))``, + rpt strip_tac >> + `?h. k = h + 1` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO, ADD1] >> + `(h = k - 1) /\ h < n /\ 0 < n` by decide_tac >> + metis_tac[leibniz_recurrence]); + +(* Theorem: 0 < n ==> + !k. lcm (leibniz n k) (leibniz (n-1) k) = lcm (leibniz n k) (leibniz n (k+1)) *) +(* Proof: + By leibniz_property, + leibniz n k * leibniz (n - 1) k = leibniz n (k + 1) * (leibniz n k - leibniz (n - 1) k) + Hence true by LCM_EXCHANGE. +*) +val leibniz_lcm_exchange = store_thm( + "leibniz_lcm_exchange", + ``!n. 0 < n ==> !k. lcm (leibniz n k) (leibniz (n-1) k) = lcm (leibniz n k) (leibniz n (k+1))``, + rw[leibniz_property, LCM_EXCHANGE]); + +(* Theorem: 4 ** n <= leibniz (2 * n) n *) +(* Proof: + Let m = 2 * n. + Then n = HALF m by HALF_TWICE + Let l1 = GENLIST (K (binomial m n)) (m + 1) + and l2 = GENLIST (binomial m) (m + 1) + Note LENGTH l1 = LENGTH l2 = m + 1 by LENGTH_GENLIST + + Claim: !k. k < m + 1 ==> EL k l2 <= EL k l1 + Proof: Note EL k l1 = binomial m n by EL_GENLIST + and EL k l2 = binomial m k by EL_GENLIST + Apply binomial m k <= binomial m n by binomial_max + The result follows + + leibniz m n + = (m + 1) * binomial m n by leibniz_def + = SUM (GENLIST (K (binomial m n)) (m + 1)) by SUM_GENLIST_K + >= SUM (GENLIST (\k. binomial m k) (m + 1)) by SUM_LE, above + = SUM (GENLIST (binomial m) (SUC m)) by ADD1 + = 2 ** m by binomial_sum + = 2 ** (2 * n) by notation + = (2 ** 2) ** n by EXP_EXP_MULT + = 4 ** n by arithmetic +*) +val leibniz_middle_lower = store_thm( + "leibniz_middle_lower", + ``!n. 4 ** n <= leibniz (2 * n) n``, + rpt strip_tac >> + qabbrev_tac `m = 2 * n` >> + `n = HALF m` by rw[HALF_TWICE, Abbr`m`] >> + qabbrev_tac `l1 = GENLIST (K (binomial m n)) (m + 1)` >> + qabbrev_tac `l2 = GENLIST (binomial m) (m + 1)` >> + `!k. k < m + 1 ==> EL k l2 <= EL k l1` by rw[binomial_max, EL_GENLIST, Abbr`l1`, Abbr`l2`] >> + `leibniz m n = (m + 1) * binomial m n` by rw[leibniz_def] >> + `_ = SUM l1` by rw[SUM_GENLIST_K, Abbr`l1`] >> + `SUM l2 = SUM (GENLIST (binomial m) (SUC m))` by rw[ADD1, Abbr`l2`] >> + `_ = 2 ** m` by rw[binomial_sum] >> + `_ = 4 ** n` by rw[EXP_EXP_MULT, Abbr`m`] >> + metis_tac[SUM_LE, LENGTH_GENLIST]); + +(* ------------------------------------------------------------------------- *) +(* Property of Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* +binomial_recurrence |- !n k. binomial (SUC n) (SUC k) = binomial n k + binomial n (SUC k) +This means: + B n k + B n k* + v + B n* k* +However, for the Leibniz Triangle, the recurrence is: + L n k + L n* k -> L n* k* = (L n* k)(L n k) / (L n* k - L n k) +That is, it takes a different style, and has the property: + 1 / L n* k* = 1 / L n k - 1 / L n* k +Why? +First, some verification. +Pascal: [1] 3 3 + [4] 6 = 3 + 3 = 6 +Leibniz: 12 12 + [20] 30 = 20 * 12 / (20 - 12) = 20 * 12 / 8 = 30 +Now, the 20 comes from 4 = 3 + 1. +Originally, 30 = 5 * 6 by definition based on multiple + = 5 * (3 + 3) by Pascal + = 4 * (3 + 3) + (3 + 3) + = 12 + 12 + 6 +In terms of factorials, 30 = 5 * 6 = 5 * B(4,2) = 5 * 4!/2!2! + 20 = 5 * 4 = 5 * B(4,1) = 5 * 4!/1!3! + 12 = 4 * 3 = 4 * B(3,1) = 4 * 3!/1!2! +So 1/30 = (2!2!)/(5 4!) 1 / n** B n* k* = k*! (n* - k* )! / n** n*! = (n - k)! k*! / n**! + 1/20 = (1!3!)/(5 4!) 1 / n** B n* k + 1/12 = (1!2!)/(4 3!) 1 / n* B n k + 1/12 - 1/20 + = (1!2!)/(4 3!) - (1!3!)/(5 4!) + = (1!2!)/4! - (1!3!)/5! + = 5(1!2!)/5! - (1!3!)/5! + = (5(1!2!) - (1!3!))/5! + = (5 1! - 3 1!) 2!/5! + = (5 - 3)1! 2!/5! + = 2! 2! / 5! + + 1 / n B n k - 1 / n** B n* k + = k! (n-k)! / n* n! - k! (n* - k)! / n** n*! + = k! (n-k)! / n*! - k!(n* - k)! / n** n*! + = (n** (n-k)! - (n* - k)!) k! / n** n*! + = (n** - (n* - k)) (n - k)! k! / n** n*! + = (k+1) (n - k)! k! / n** n*! + = (n* - k* )! k*! / n** n*! + = 1 / n** B n* k* + +Direct without using unit fractions, + +L n k = n* B n k = n* n! / k! (n-k)! = n*! / k! (n-k)! +L n* k = n** B n* k = n** n*! / k! (n* - k)! = n**! / k! (n* - k)! +L n* k* = n** B n* k* = n** n*! / k*! (n* - k* )! = n**! / k*! (n-k)! + +(L n* k) * (L n k) = n**! n*! / k! (n* - k)! k! (n-k)! +(L n* k) - (L n k) = n**! / k! (n* - k)! - n*! / k! (n-k)! + = n**! / k! (n-k)!( 1/(n* - k) - 1/ n** ) + = n**! / k! (n-k)! (n** - n* + k)/(n* - k)(n** ) + = n**! / k! (n-k)! k* / (n* - k) n** + = n*! k* / k! (n* - k)! +(L n* k) * (L n k) / (L n* k) - (L n k) += n**! /k! (n-k)! k* += n**! /k*! (n-k)! += L n* k* +So: L n k + L n* k --> L n* k* + +Can the LCM be shown directly? +lcm (L n* k, L n k) = lcm (L n* k, L n* k* ) +To prove this, need to show: +both have the same common multiples, and least is the same -- probably yes due to common L n* k. + +In general, what is the condition for lcm a b = lcm a c ? +Well, lcm a b = a b / gcd a b, lcm a c = a c / gcd a c +So it must be a b gcd a c = a c gcd a b, or b * gcd a c = c * gcd a b. + +It this true for Leibniz triangle? +Let a = 5, b = 4, c = 20. b * gcd a c = 4 * gcd 5 20 = 4 * 5 = 20 + c * gcd a b = 20 * gcd 5 4 = 20 +Verify lcm a b = lcm 5 4 = 20 = 5 * 4 / gcd 5 4 + lcm a c = lcm 5 20 = 20 = 5 * 20 / gcd 5 20 + 5 * 4 / gcd 5 4 = 5 * 20 / gcd 5 20 +or 4 * gcd 5 20 = 20 * gcd 5 4 + +(L n k) * gcd (L n* k, L n* k* ) = (L n* k* ) * gcd (L n* k, L n k) + +or n* B n k * gcd (n** B n* k, n** B n* k* ) = (n** B n* k* ) * gcd (n** B n* k, n* B n k) +By GCD_COMMON_FACTOR, !m n k. gcd (k * m) (k * n) = k * gcd m n + n** n* B n k gcd (B n* k, B n* k* ) = (n** B n* k* ) * gcd (n** B n* k, n* B n k) +*) + +(* Special Property of Leibniz Triangle +For: L n k + L n+ k --> L n+ k+ + +L n k = n+! / k! (n-k)! +L n+ k = n++! / k! (n+ - k)! = n++ n+! / k! (n+ - k) k! = (n++ / n+ - k) L n k +L n+ k+ = n++! / k+! (n-k)! = (L n+ k) * (L n k) / (L n+ k - L n k) = (n++ / k+) L n k +Let g = gcd (L n+ k) (L n k), then L n+ k+ = lcm (L n+ k) (L n k) / (co n+ k - co n k) +where co n+ k = L n+ k / g, co n k = L n k / g. + + L n+ k = (n++ / n+ - k) L n k, +and L n+ k+ = (n++ / k+) L n k +e.g. L 3 1 = 12 + L 4 1 = 20, or (3++ / 3+ - 1) L 3 1 = (5/3) 12 = 20. + L 4 2 = 30, or (3++ / 1+) L 3 1 = (5/2) 12 = 30. +so lcm (L 4 1) (L 3 1) = lcm (5/3)*12 12 = 12 * 5 = 60 since 3 must divide 12. + lcm (L 4 1) (L 4 2) = lcm (5/3)*12 (5/2)*12 = 12 * 5 = 60 since 3, 2 must divide 12. + +By LCM_COMMON_FACTOR |- !m n k. lcm (k * m) (k * n) = k * lcm m n +lcm a (a * b DIV c) = a * b + +So the picture is: (L n k) + (L n k) * (n+2)/(n-k+1) (L n k) * (n+2)/(k+1) + +A better picture: +Pascal: (B n-1 k) = (n-1, k, n-k-1) + (B n k) = (n, k, n-k) (B n k+1) = (n, k+1, n-k-1) +Leibniz: (L n-1 k) = (n, k, n-k-1) = (L n k) / (n+1) * (n-k-1) + (L n k) = (n+1, k, n-k) (L n k+1) = (n+1, k+1, n-k-1) = (L n k) / (n-k-1) * (k+1) +And we want: + LCM (L, (n-k-1) * L DIV (n+1)) = LCM (L, (k+1) * L DIV (n-k-1)). + +Theorem: lcm a ((a * b) DIV c) = (a * b) DIV (gcd b c) +Assume this theorem, +LHS = L * (n-k-1) DIV gcd (n-k-1, n+1) +RHS = L * (k+1) DIV gcd (k+1, n-k-1) +Still no hope to show LHS = RHS ! + +LCM of fractions: +lcm (a/c, b/c) = lcm(a, b)/c +lcm (a/c, b/d) = ... = lcm(a, b)/gcd(c, d) +Hence lcm (a, a*b/c) = lcm(a*b/b, a*b/c) = a * b / gcd (b, c) +*) + +(* Special Property of Leibniz Triangle -- another go +Leibniz: L(5,1) = 30 = b + L(6,1) = 42 = a L(6,2) = 105 = c, c = ab/(a - b), or ab = c(a - b) +Why is LCM 42 30 = LCM 42 105 = 210 = 2x3x5x7? +First, b = L(5,1) = 30 = (6,1,4) = 6!/1!4! = 7!/1!5! * (5/7) = a * (5/7) = 2x3x5 + a = L(6,1) = 42 = (7,1,5) = 7!/1!5! = 2x3x7 = b * (7/5) = c * (2/5) + c = L(6,2) = 105 = (7,2,4) = 7!/2!4! = 7!/1!5! * (5/2) = a * (5/2) = 3x5x7 +Any common multiple of a, b must have 5, 7 as factor, also with factor 2 (by common k = 1) +Any common multiple of a, c must have 5, 2 as factor, also with factor 7 (by common n = 6) +Also n = 5 implies a factor 6, k = 2 imples a factor 2. +LCM a b = a b / GCD a b + = c (a - b) / GCD a b + = (m c') (m a' - (m-1)b') / GCD (m a') (m-1 b') +LCM a c = a c / GCD a c + = (m a') (m c') / GCD (m a') (m c') where c' = a' + b' from Pascal triangle + = m a' (a' + b') / GCD a' (a' + b') + = m a' (a' + b') / GCD a' b' + = a' c / GCD a' b' +Can we prove: c(a - b) / GCD a b = c a' / GCD a' b' +or (a - b) GCD a' b' = a' GCD a b ? +or a GCD a' b' = a' GCD a b + b GCD a' b' ? +or ab GCD a' b' = c a' GCD a b? +or m (b GCD a' b') = c GCD a b? +or b GCD a' b' = c' GCD a b? +b = (a DIV 7) * 5 +c = (a DIV 2) * 5 +lcm (a, b) = lcm (a, (a DIV 7) * 5) = lcm (a, 5) +lcm (a, c) = lcm (a, (a DIV 2) * 5) = lcm (a, 5) +Is this a theorem: lcm (a, (a DIV p) * b) = lcm (a, b) if p | a ? +Let c = lcm (a, b). Then a | c, b | c. +Since a = (a DIV p) * p, (a DIV p) * p | c. +Hence ((a DIV p) * b) * p | b * c. +How to conclude ((a DIV p) * b) | c? + +A counter-example: +lcm (42, 9) = 126 = 2x3x3x7. +lcm (42, (42 DIV 3) * 9) = 126 = 2x3x3x7. +lcm (42, (42 DIV 6) * 9) = 126 = 2x3x3x7. +lcm (42, (42 DIV 2) * 9) = 378 = 2x3x3x3x7. +lcm (42, (42 DIV 7) * 9) = 378 = 2x3x3x3x7. + +LCM a c += LCM a (ab/(a-b)) let g = GCD(a,b), a = gA, b=gB, coprime A,B. += LCM gA gAB/(A-B) += g LCM A AB/(A-B) += (ab/LCM a b) LCM A AB/(A-B) +*) + +(* ------------------------------------------------------------------------- *) +(* LCM of a list of numbers *) +(* ------------------------------------------------------------------------- *) + +(* Define LCM of a list of numbers *) +val list_lcm_def = Define` + (list_lcm [] = 1) /\ + (list_lcm (h::t) = lcm h (list_lcm t)) +`; + +(* export simple definition *) +val _ = export_rewrites["list_lcm_def"]; + +(* Theorem: list_lcm [] = 1 *) +(* Proof: by list_lcm_def. *) +val list_lcm_nil = store_thm( + "list_lcm_nil", + ``list_lcm [] = 1``, + rw[]); + +(* Theorem: list_lcm (h::t) = lcm h (list_lcm t) *) +(* Proof: by list_lcm_def. *) +val list_lcm_cons = store_thm( + "list_lcm_cons", + ``!h t. list_lcm (h::t) = lcm h (list_lcm t)``, + rw[]); + +(* Theorem: list_lcm [x] = x *) +(* Proof: + list_lcm [x] + = lcm x (list_lcm []) by list_lcm_cons + = lcm x 1 by list_lcm_nil + = x by LCM_1 +*) +val list_lcm_sing = store_thm( + "list_lcm_sing", + ``!x. list_lcm [x] = x``, + rw[]); + +(* Theorem: list_lcm (SNOC x l) = list_lcm (x::l) *) +(* Proof: + By induction on l. + Base case: list_lcm (SNOC x []) = lcm x (list_lcm []) + list_lcm (SNOC x []) + = list_lcm [x] by SNOC + = lcm x (list_lcm []) by list_lcm_def + Step case: list_lcm (SNOC x l) = lcm x (list_lcm l) ==> + !h. list_lcm (SNOC x (h::l)) = lcm x (list_lcm (h::l)) + list_lcm (SNOC x (h::l)) + = list_lcm (h::SNOC x l) by SNOC + = lcm h (list_lcm (SNOC x l)) by list_lcm_def + = lcm h (lcm x (list_lcm l)) by induction hypothesis + = lcm x (lcm h (list_lcm l)) by LCM_ASSOC_COMM + = lcm x (list_lcm h::l) by list_lcm_def +*) +val list_lcm_snoc = store_thm( + "list_lcm_snoc", + ``!x l. list_lcm (SNOC x l) = lcm x (list_lcm l)``, + strip_tac >> + Induct >- + rw[] >> + rw[LCM_ASSOC_COMM]); + +(* Theorem: list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l *) +(* Proof: + By induction on l. + Base case: !n. list_lcm (MAP (\k. n * k) []) = if [] = [] then 1 else n * list_lcm [] + list_lcm (MAP (\k. n * k) []) + = list_lcm [] by MAP + = 1 by list_lcm_nil + Step case: !n. list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l ==> + !h n. list_lcm (MAP (\k. n * k) (h::l)) = if h::l = [] then 1 else n * list_lcm (h::l) + Note h::l <> [] by NOT_NIL_CONS + If l = [], h::l = [h] + list_lcm (MAP (\k. n * k) [h]) + = list_lcm [n * h] by MAP + = n * h by list_lcm_sing + = n * list_lcm [h] by list_lcm_sing + If l <> [], + list_lcm (MAP (\k. n * k) (h::l)) + = list_lcm ((n * h) :: MAP (\k. n * k) l) by MAP + = lcm (n * h) (list_lcm (MAP (\k. n * k) l)) by list_lcm_cons + = lcm (n * h) (n * list_lcm l) by induction hypothesis + = n * (lcm h (list_lcm l)) by LCM_COMMON_FACTOR + = n * list_lcm (h::l) by list_lcm_cons +*) +val list_lcm_map_times = store_thm( + "list_lcm_map_times", + ``!n l. list_lcm (MAP (\k. n * k) l) = if l = [] then 1 else n * list_lcm l``, + Induct_on `l` >- + rw[] >> + rpt strip_tac >> + Cases_on `l = []` >- + rw[] >> + rw_tac std_ss[LCM_COMMON_FACTOR, MAP, list_lcm_cons]); + +(* Theorem: EVERY_POSITIVE l ==> 0 < list_lcm l *) +(* Proof: + By induction on l. + Base case: EVERY_POSITIVE [] ==> 0 < list_lcm [] + Note EVERY_POSITIVE [] = T by EVERY_DEF + Since list_lcm [] = 1 by list_lcm_nil + Hence true since 0 < 1 by SUC_POS, ONE. + Step case: EVERY_POSITIVE l ==> 0 < list_lcm l ==> + !h. EVERY_POSITIVE (h::l) ==> 0 < list_lcm (h::l) + Note EVERY_POSITIVE (h::l) + ==> 0 < h and EVERY_POSITIVE l by EVERY_DEF + Since list_lcm (h::l) = lcm h (list_lcm l) by list_lcm_cons + and 0 < list_lcm l by induction hypothesis + so h <= lcm h (list_lcm l) by LCM_LE, 0 < h. + Hence 0 < list_lcm (h::l) by LESS_LESS_EQ_TRANS +*) +val list_lcm_pos = store_thm( + "list_lcm_pos", + ``!l. EVERY_POSITIVE l ==> 0 < list_lcm l``, + Induct >- + rw[] >> + metis_tac[EVERY_DEF, list_lcm_cons, LCM_LE, LESS_LESS_EQ_TRANS]); + +(* Theorem: POSITIVE l ==> 0 < list_lcm l *) +(* Proof: by list_lcm_pos, EVERY_MEM *) +val list_lcm_pos_alt = store_thm( + "list_lcm_pos_alt", + ``!l. POSITIVE l ==> 0 < list_lcm l``, + rw[list_lcm_pos, EVERY_MEM]); + +(* Theorem: EVERY_POSITIVE l ==> SUM l <= (LENGTH l) * list_lcm l *) +(* Proof: + By induction on l. + Base case: EVERY_POSITIVE [] ==> SUM [] <= LENGTH [] * list_lcm [] + Note EVERY_POSITIVE [] = T by EVERY_DEF + Since SUM [] = 0 by SUM + and LENGTH [] = 0 by LENGTH_NIL + Hence true by MULT, as 0 <= 0 by LESS_EQ_REFL + Step case: EVERY_POSITIVE l ==> SUM l <= LENGTH l * list_lcm l ==> + !h. EVERY_POSITIVE (h::l) ==> SUM (h::l) <= LENGTH (h::l) * list_lcm (h::l) + Note EVERY_POSITIVE (h::l) + ==> 0 < h and EVERY_POSITIVE l by EVERY_DEF + ==> 0 < h and 0 < list_lcm l by list_lcm_pos + If l = [], LENGTH l = 0. + SUM (h::[]) = SUM [h] = h by SUM + LENGTH (h::[]) * list_lcm (h::[]) + = 1 * list_lcm [h] by ONE + = 1 * h by list_lcm_sing + = h by MULT_LEFT_1 + If l <> [], LENGTH l <> 0 by LENGTH_NIL ... [1] + SUM (h::l) + = h + SUM l by SUM + <= h + LENGTH l * list_lcm l by induction hypothesis + <= lcm h (list_lcm l) + LENGTH l * list_lcm l by LCM_LE, 0 < h + <= lcm h (list_lcm l) + LENGTH l * (lcm h (list_lcm l)) by LCM_LE, 0 < list_lcm l, [1] + = (1 + LENGTH l) * (lcm h (list_lcm l)) by RIGHT_ADD_DISTRIB + = SUC (LENGTH l) * (lcm h (list_lcm l)) by SUC_ONE_ADD + = LENGTH (h::l) * (lcm h (list_lcm l)) by LENGTH + = LENGTH (h::l) * list_lcm (h::l) by list_lcm_cons +*) +val list_lcm_lower_bound = store_thm( + "list_lcm_lower_bound", + ``!l. EVERY_POSITIVE l ==> SUM l <= (LENGTH l) * list_lcm l``, + Induct >> + rw[] >> + Cases_on `l = []` >- + rw[] >> + `lcm h (list_lcm l) + LENGTH l * (lcm h (list_lcm l)) = SUC (LENGTH l) * (lcm h (list_lcm l))` by rw[RIGHT_ADD_DISTRIB, SUC_ONE_ADD] >> + `LENGTH l <> 0` by metis_tac[LENGTH_NIL] >> + `0 < list_lcm l` by rw[list_lcm_pos] >> + `h <= lcm h (list_lcm l) /\ list_lcm l <= lcm h (list_lcm l)` by rw[LCM_LE] >> + `LENGTH l * list_lcm l <= LENGTH l * (lcm h (list_lcm l))` by rw[LE_MULT_LCANCEL] >> + `h + SUM l <= h + LENGTH l * list_lcm l` by rw[] >> + decide_tac); + +(* Another version to eliminate EVERY by MEM. *) +val list_lcm_lower_bound_alt = save_thm("list_lcm_lower_bound_alt", + list_lcm_lower_bound |> SIMP_RULE (srw_ss()) [EVERY_MEM]); +(* > list_lcm_lower_bound_alt; +val it = |- !l. POSITIVE l ==> SUM l <= LENGTH l * list_lcm l: thm +*) + +(* Theorem: list_lcm l is a common multiple of its members. + MEM x l ==> x divides (list_lcm l) *) +(* Proof: + By induction on l. + Base case: !x. MEM x [] ==> x divides (list_lcm []) + True since MEM x [] = F by MEM + Step case: !x. MEM x l ==> x divides (list_lcm l) ==> + !h x. MEM x (h::l) ==> x divides (list_lcm (h::l)) + Note MEM x (h::l) <=> x = h, or MEM x l by MEM + and list_lcm (h::l) = lcm h (list_lcm l) by list_lcm_cons + If x = h, + divides h (lcm h (list_lcm l)) is true by LCM_IS_LEAST_COMMON_MULTIPLE + If MEM x l, + x divides (list_lcm l) by induction hypothesis + (list_lcm l) divides (lcm h (list_lcm l)) by LCM_IS_LEAST_COMMON_MULTIPLE + Hence x divides (lcm h (list_lcm l)) by DIVIDES_TRANS +*) +val list_lcm_is_common_multiple = store_thm( + "list_lcm_is_common_multiple", + ``!x l. MEM x l ==> x divides (list_lcm l)``, + Induct_on `l` >> + rw[] >> + metis_tac[LCM_IS_LEAST_COMMON_MULTIPLE, DIVIDES_TRANS]); + +(* Theorem: If m is a common multiple of members of l, (list_lcm l) divides m. + (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m *) +(* Proof: + By induction on l. + Base case: !m. (!x. MEM x [] ==> x divides m) ==> divides (list_lcm []) m + Since list_lcm [] = 1 by list_lcm_nil + and divides 1 m is true by ONE_DIVIDES_ALL + Step case: !m. (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m ==> + !h m. (!x. MEM x (h::l) ==> x divides m) ==> divides (list_lcm (h::l)) m + Note MEM x (h::l) <=> x = h, or MEM x l by MEM + and list_lcm (h::l) = lcm h (list_lcm l) by list_lcm_cons + Put x = h, divides h m by MEM h (h::l) = T + Put MEM x l, x divides m by MEM x (h::l) = T + giving (list_lcm l) divides m by induction hypothesis + Hence divides (lcm h (list_lcm l)) m by LCM_IS_LEAST_COMMON_MULTIPLE +*) +val list_lcm_is_least_common_multiple = store_thm( + "list_lcm_is_least_common_multiple", + ``!l m. (!x. MEM x l ==> x divides m) ==> (list_lcm l) divides m``, + Induct >- + rw[] >> + rw[LCM_IS_LEAST_COMMON_MULTIPLE]); + +(* +> EVAL ``list_lcm []``; +val it = |- list_lcm [] = 1: thm +> EVAL ``list_lcm [1; 2; 3]``; +val it = |- list_lcm [1; 2; 3] = 6: thm +> EVAL ``list_lcm [1; 2; 3; 4; 5]``; +val it = |- list_lcm [1; 2; 3; 4; 5] = 60: thm +> EVAL ``list_lcm (GENLIST SUC 5)``; +val it = |- list_lcm (GENLIST SUC 5) = 60: thm +> EVAL ``list_lcm (GENLIST SUC 4)``; +val it = |- list_lcm (GENLIST SUC 4) = 12: thm +> EVAL ``lcm 5 (list_lcm (GENLIST SUC 4))``; +val it = |- lcm 5 (list_lcm (GENLIST SUC 4)) = 60: thm +> EVAL ``SNOC 5 (GENLIST SUC 4)``; +val it = |- SNOC 5 (GENLIST SUC 4) = [1; 2; 3; 4; 5]: thm +> EVAL ``list_lcm (SNOC 5 (GENLIST SUC 4))``; +val it = |- list_lcm (SNOC 5 (GENLIST SUC 4)) = 60: thm +> EVAL ``GENLIST (\k. leibniz 5 k) (SUC 5)``; +val it = |- GENLIST (\k. leibniz 5 k) (SUC 5) = [6; 30; 60; 60; 30; 6]: thm +> EVAL ``list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5))``; +val it = |- list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5)) = 60: thm +> EVAL ``list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5))``; +val it = |- (list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (\k. leibniz 5 k) (SUC 5))) <=> T: thm +> EVAL ``list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (leibniz 5) (SUC 5))``; +val it = |- (list_lcm (GENLIST SUC 5) = list_lcm (GENLIST (leibniz 5) (SUC 5))) <=> T: thm +*) + +(* Theorem: list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2) *) +(* Proof: + By induction on l1. + Base: !l2. list_lcm ([] ++ l2) = lcm (list_lcm []) (list_lcm l2) + LHS = list_lcm ([] ++ l2) + = list_lcm l2 by APPEND + = lcm 1 (list_lcm l2) by LCM_1 + = lcm (list_lcm []) (list_lcm l2) by list_lcm_nil + = RHS + Step: !l2. list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2) ==> + !h l2. list_lcm (h::l1 ++ l2) = lcm (list_lcm (h::l1)) (list_lcm l2) + list_lcm (h::l1 ++ l2) + = list_lcm (h::(l1 ++ l2)) by APPEND + = lcm h (list_lcm (l1 ++ l2)) by list_lcm_cons + = lcm h (lcm (list_lcm l1) (list_lcm l2)) by induction hypothesis + = lcm (lcm h (list_lcm l1)) (list_lcm l2) by LCM_ASSOC + = lcm (list_lcm (h::l1)) (list_lcm l2) by list_lcm_cons +*) +val list_lcm_append = store_thm( + "list_lcm_append", + ``!l1 l2. list_lcm (l1 ++ l2) = lcm (list_lcm l1) (list_lcm l2)``, + Induct >- + rw[] >> + rw[LCM_ASSOC]); + +(* Theorem: list_lcm (l1 ++ l2 ++ l3) = list_lcm [(list_lcm l1); (list_lcm l2); (list_lcm l3)] *) +(* Proof: + list_lcm (l1 ++ l2 ++ l3) + = lcm (list_lcm (l1 ++ l2)) (list_lcm l3) by list_lcm_append + = lcm (lcm (list_lcm l1) (list_lcm l2)) (list_lcm l3) by list_lcm_append + = lcm (list_lcm l1) (lcm (list_lcm l2) (list_lcm l3)) by LCM_ASSOC + = lcm (list_lcm l1) (list_lcm [(list_lcm l2); list_lcm l3]) by list_lcm_cons + = list_lcm [list_lcm l1; list_lcm l2; list_lcm l3] by list_lcm_cons +*) +val list_lcm_append_3 = store_thm( + "list_lcm_append_3", + ``!l1 l2 l3. list_lcm (l1 ++ l2 ++ l3) = list_lcm [(list_lcm l1); (list_lcm l2); (list_lcm l3)]``, + rw[list_lcm_append, LCM_ASSOC, list_lcm_cons]); + +(* Theorem: list_lcm (REVERSE l) = list_lcm l *) +(* Proof: + By induction on l. + Base: list_lcm (REVERSE []) = list_lcm [] + True since REVERSE [] = [] by REVERSE_DEF + Step: list_lcm (REVERSE l) = list_lcm l ==> + !h. list_lcm (REVERSE (h::l)) = list_lcm (h::l) + list_lcm (REVERSE (h::l)) + = list_lcm (REVERSE l ++ [h]) by REVERSE_DEF + = lcm (list_lcm (REVERSE l)) (list_lcm [h]) by list_lcm_append + = lcm (list_lcm l) (list_lcm [h]) by induction hypothesis + = lcm (list_lcm [h]) (list_lcm l) by LCM_COMM + = list_lcm ([h] ++ l) by list_lcm_append + = list_lcm (h::l) by CONS_APPEND +*) +val list_lcm_reverse = store_thm( + "list_lcm_reverse", + ``!l. list_lcm (REVERSE l) = list_lcm l``, + Induct >- + rw[] >> + rpt strip_tac >> + `list_lcm (REVERSE (h::l)) = list_lcm (REVERSE l ++ [h])` by rw[] >> + `_ = lcm (list_lcm (REVERSE l)) (list_lcm [h])` by rw[list_lcm_append] >> + `_ = lcm (list_lcm l) (list_lcm [h])` by rw[] >> + `_ = lcm (list_lcm [h]) (list_lcm l)` by rw[LCM_COMM] >> + `_ = list_lcm ([h] ++ l)` by rw[list_lcm_append] >> + `_ = list_lcm (h::l)` by rw[] >> + decide_tac); + +(* Theorem: list_lcm [1 .. (n + 1)] = lcm (n + 1) (list_lcm [1 .. n])) *) +(* Proof: + list_lcm [1 .. (n + 1)] + = list_lcm (SONC (n + 1) [1 .. n]) by listRangeINC_SNOC, 1 <= n + 1 + = lcm (n + 1) (list_lcm [1 .. n]) by list_lcm_snoc +*) +val list_lcm_suc = store_thm( + "list_lcm_suc", + ``!n. list_lcm [1 .. (n + 1)] = lcm (n + 1) (list_lcm [1 .. n])``, + rw[listRangeINC_SNOC, list_lcm_snoc]); + +(* Theorem: l <> [] /\ EVERY_POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l *) +(* Proof: + Note LENGTH l <> 0 by LENGTH_NIL + and SUM l <= LENGTH l * list_lcm l by list_lcm_lower_bound + so (SUM l) DIV (LENGTH l) <= list_lcm l by DIV_LE +*) +val list_lcm_nonempty_lower = store_thm( + "list_lcm_nonempty_lower", + ``!l. l <> [] /\ EVERY_POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l``, + metis_tac[list_lcm_lower_bound, DIV_LE, LENGTH_NIL, NOT_ZERO_LT_ZERO]); + +(* Theorem: l <> [] /\ POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l *) +(* Proof: + Note LENGTH l <> 0 by LENGTH_NIL + and SUM l <= LENGTH l * list_lcm l by list_lcm_lower_bound_alt + so (SUM l) DIV (LENGTH l) <= list_lcm l by DIV_LE +*) +val list_lcm_nonempty_lower_alt = store_thm( + "list_lcm_nonempty_lower_alt", + ``!l. l <> [] /\ POSITIVE l ==> (SUM l) DIV (LENGTH l) <= list_lcm l``, + metis_tac[list_lcm_lower_bound_alt, DIV_LE, LENGTH_NIL, NOT_ZERO_LT_ZERO]); + +(* Theorem: MEM x l /\ MEM y l ==> (lcm x y) <= list_lcm l *) +(* Proof: + Note x divides (list_lcm l) by list_lcm_is_common_multiple + and y divides (list_lcm l) by list_lcm_is_common_multiple + ==> (lcm x y) divides (list_lcm l) by LCM_IS_LEAST_COMMON_MULTIPLE +*) +val list_lcm_divisor_lcm_pair = store_thm( + "list_lcm_divisor_lcm_pair", + ``!l x y. MEM x l /\ MEM y l ==> (lcm x y) divides list_lcm l``, + rw[list_lcm_is_common_multiple, LCM_IS_LEAST_COMMON_MULTIPLE]); + +(* Theorem: POSITIVE l /\ MEM x l /\ MEM y l ==> (lcm x y) <= list_lcm l *) +(* Proof: + Note (lcm x y) divides (list_lcm l) by list_lcm_divisor_lcm_pair + Now 0 < list_lcm l by list_lcm_pos_alt + Thus (lcm x y) <= list_lcm l by DIVIDES_LE +*) +val list_lcm_lower_by_lcm_pair = store_thm( + "list_lcm_lower_by_lcm_pair", + ``!l x y. POSITIVE l /\ MEM x l /\ MEM y l ==> (lcm x y) <= list_lcm l``, + rw[list_lcm_divisor_lcm_pair, list_lcm_pos_alt, DIVIDES_LE]); + +(* Theorem: 0 < m /\ (!x. MEM x l ==> x divides m) ==> list_lcm l <= m *) +(* Proof: + Note list_lcm l divides m by list_lcm_is_least_common_multiple + Thus list_lcm l <= m by DIVIDES_LE, 0 < m +*) +val list_lcm_upper_by_common_multiple = store_thm( + "list_lcm_upper_by_common_multiple", + ``!l m. 0 < m /\ (!x. MEM x l ==> x divides m) ==> list_lcm l <= m``, + rw[list_lcm_is_least_common_multiple, DIVIDES_LE]); + +(* Theorem: list_lcm ls = FOLDR lcm 1 ls *) +(* Proof: + By induction on ls. + Base: list_lcm [] = FOLDR lcm 1 [] + list_lcm [] + = 1 by list_lcm_nil + = FOLDR lcm 1 [] by FOLDR + Step: list_lcm ls = FOLDR lcm 1 ls ==> + !h. list_lcm (h::ls) = FOLDR lcm 1 (h::ls) + list_lcm (h::ls) + = lcm h (list_lcm ls) by list_lcm_def + = lcm h (FOLDR lcm 1 ls) by induction hypothesis + = FOLDR lcm 1 (h::ls) by FOLDR +*) +val list_lcm_by_FOLDR = store_thm( + "list_lcm_by_FOLDR", + ``!ls. list_lcm ls = FOLDR lcm 1 ls``, + Induct >> rw[]); + +(* Theorem: list_lcm ls = FOLDL lcm 1 ls *) +(* Proof: + Note COMM lcm since !x y. lcm x y = lcm y x by LCM_COMM + and ASSOC lcm since !x y z. lcm x (lcm y z) = lcm (lcm x y) z by LCM_ASSOC + Now list_lcm ls + = FOLDR lcm 1 ls by list_lcm_by FOLDR + = FOLDL lcm 1 ls by FOLDL_EQ_FOLDR, COMM lcm, ASSOC lcm +*) +val list_lcm_by_FOLDL = store_thm( + "list_lcm_by_FOLDL", + ``!ls. list_lcm ls = FOLDL lcm 1 ls``, + simp[list_lcm_by_FOLDR] >> + irule (GSYM FOLDL_EQ_FOLDR) >> + rpt strip_tac >- + rw[LCM_ASSOC, combinTheory.ASSOC_DEF] >> + rw[LCM_COMM, combinTheory.COMM_DEF]); + +(* ------------------------------------------------------------------------- *) +(* Lists in Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* ------------------------------------------------------------------------- *) +(* Vertical Lists in Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* Define Vertical List in Leibniz Triangle *) +(* +val leibniz_vertical_def = Define ` + leibniz_vertical n = GENLIST SUC (SUC n) +`; + +(* Use overloading for leibniz_vertical n. *) +val _ = overload_on("leibniz_vertical", ``\n. GENLIST ((+) 1) (n + 1)``); +*) + +(* Define Vertical (downward list) in Leibniz Triangle *) + +(* Use overloading for leibniz_vertical n. *) +val _ = overload_on("leibniz_vertical", ``\n. [1 .. (n+1)]``); + +(* Theorem: leibniz_vertical n = GENLIST (\i. 1 + i) (n + 1) *) +(* Proof: + leibniz_vertical n + = [1 .. (n+1)] by notation + = GENLIST (\i. 1 + i) (n+1 + 1 - 1) by listRangeINC_def + = GENLIST (\i. 1 + i) (n + 1) by arithmetic +*) +val leibniz_vertical_alt = store_thm( + "leibniz_vertical_alt", + ``!n. leibniz_vertical n = GENLIST (\i. 1 + i) (n + 1)``, + rw[listRangeINC_def]); + +(* Theorem: leibniz_vertical 0 = [1] *) +(* Proof: + leibniz_vertical 0 + = [1 .. (0+1)] by notation + = [1 .. 1] by arithmetic + = [1] by listRangeINC_SING +*) +val leibniz_vertical_0 = store_thm( + "leibniz_vertical_0", + ``leibniz_vertical 0 = [1]``, + rw[]); + +(* Theorem: LENGTH (leibniz_vertical n) = n + 1 *) +(* Proof: + LENGTH (leibniz_vertical n) + = LENGTH [1 .. (n+1)] by notation + = n + 1 + 1 - 1 by listRangeINC_LEN + = n + 1 by arithmetic +*) +val leibniz_vertical_len = store_thm( + "leibniz_vertical_len", + ``!n. LENGTH (leibniz_vertical n) = n + 1``, + rw[listRangeINC_LEN]); + +(* Theorem: leibniz_vertical n <> [] *) +(* Proof: + LENGTH (leibniz_vertical n) + = n + 1 by leibniz_vertical_len + <> 0 by ADD1, SUC_NOT_ZERO + Thus leibniz_vertical n <> [] by LENGTH_EQ_0 +*) +val leibniz_vertical_not_nil = store_thm( + "leibniz_vertical_not_nil", + ``!n. leibniz_vertical n <> []``, + metis_tac[leibniz_vertical_len, LENGTH_EQ_0, DECIDE``!n. n + 1 <> 0``]); + +(* Theorem: EVERY_POSITIVE (leibniz_vertical n) *) +(* Proof: + EVERY_POSITIVE (leibniz_vertical n) + <=> EVERY_POSITIVE GENLIST (\i. 1 + i) (n+1) by leibniz_vertical_alt + <=> !i. i < n + 1 ==> 0 < 1 + i by EVERY_GENLIST + <=> !i. i < n + 1 ==> T by arithmetic + <=> T +*) +val leibniz_vertical_pos = store_thm( + "leibniz_vertical_pos", + ``!n. EVERY_POSITIVE (leibniz_vertical n)``, + rw[leibniz_vertical_alt, EVERY_GENLIST]); + +(* Theorem: POSITIVE (leibniz_vertical n) *) +(* Proof: by leibniz_vertical_pos, EVERY_MEM *) +val leibniz_vertical_pos_alt = store_thm( + "leibniz_vertical_pos_alt", + ``!n. POSITIVE (leibniz_vertical n)``, + rw[leibniz_vertical_pos, EVERY_MEM]); + +(* Theorem: 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_vertical n) *) +(* Proof: + Note: (leibniz_vertical n) has 1 to (n+1), inclusive: + MEM x (leibniz_vertical n) + <=> MEM x [1 .. (n+1)] by notation + <=> 1 <= x /\ x <= n + 1 by listRangeINC_MEM + <=> 0 < x /\ x <= n + 1 by num_CASES, LESS_EQ_MONO +*) +val leibniz_vertical_mem = store_thm( + "leibniz_vertical_mem", + ``!n x. 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_vertical n)``, + rw[]); + +(* Theorem: leibniz_vertical (n + 1) = SNOC (n + 2) (leibniz_vertical n) *) +(* Proof: + leibniz_vertical (n + 1) + = [1 .. (n+1 +1)] by notation + = SNOC (n+1 + 1) [1 .. (n+1)] by listRangeINC_SNOC + = SNOC (n + 2) (leibniz_vertical n) by notation +*) +val leibniz_vertical_snoc = store_thm( + "leibniz_vertical_snoc", + ``!n. leibniz_vertical (n + 1) = SNOC (n + 2) (leibniz_vertical n)``, + rw[listRangeINC_SNOC]);; + +(* Use overloading for leibniz_up n. *) +val _ = overload_on("leibniz_up", ``\n. REVERSE (leibniz_vertical n)``); + +(* Theorem: leibniz_up 0 = [1] *) +(* Proof: + leibniz_up 0 + = REVERSE (leibniz_vertical 0) by notation + = REVERSE [1] by leibniz_vertical_0 + = [1] by REVERSE_SING +*) +val leibniz_up_0 = store_thm( + "leibniz_up_0", + ``leibniz_up 0 = [1]``, + rw[]); + +(* Theorem: LENGTH (leibniz_up n) = n + 1 *) +(* Proof: + LENGTH (leibniz_up n) + = LENGTH (REVERSE (leibniz_vertical n)) by notation + = LENGTH (leibniz_vertical n) by LENGTH_REVERSE + = n + 1 by leibniz_vertical_len +*) +val leibniz_up_len = store_thm( + "leibniz_up_len", + ``!n. LENGTH (leibniz_up n) = n + 1``, + rw[leibniz_vertical_len]); + +(* Theorem: EVERY_POSITIVE (leibniz_up n) *) +(* Proof: + EVERY_POSITIVE (leibniz_up n) + <=> EVERY_POSITIVE (REVERSE (leibniz_vertical n)) by notation + <=> EVERY_POSITIVE (leibniz_vertical n) by EVERY_REVERSE + <=> T by leibniz_vertical_pos +*) +val leibniz_up_pos = store_thm( + "leibniz_up_pos", + ``!n. EVERY_POSITIVE (leibniz_up n)``, + rw[leibniz_vertical_pos, EVERY_REVERSE]); + +(* Theorem: 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_up n) *) +(* Proof: + Note: (leibniz_up n) has (n+1) downto 1, inclusive: + MEM x (leibniz_up n) + <=> MEM x (REVERSE (leibniz_vertical n)) by notation + <=> MEM x (leibniz_vertical n) by MEM_REVERSE + <=> T by leibniz_vertical_mem +*) +val leibniz_up_mem = store_thm( + "leibniz_up_mem", + ``!n x. 0 < x /\ x <= (n + 1) <=> MEM x (leibniz_up n)``, + rw[]); + +(* Theorem: leibniz_up (n + 1) = (n + 2) :: (leibniz_up n) *) +(* Proof: + leibniz_up (n + 1) + = REVERSE (leibniz_vertical (n + 1)) by notation + = REVERSE (SNOC (n + 2) (leibniz_vertical n)) by leibniz_vertical_snoc + = (n + 2) :: (leibniz_up n) by REVERSE_SNOC +*) +val leibniz_up_cons = store_thm( + "leibniz_up_cons", + ``!n. leibniz_up (n + 1) = (n + 2) :: (leibniz_up n)``, + rw[leibniz_vertical_snoc, REVERSE_SNOC]); + +(* ------------------------------------------------------------------------- *) +(* Horizontal List in Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* Define row (horizontal list) in Leibniz Triangle *) +(* +val leibniz_horizontal_def = Define ` + leibniz_horizontal n = GENLIST (leibniz n) (SUC n) +`; + +(* Use overloading for leibniz_horizontal n. *) +val _ = overload_on("leibniz_horizontal", ``\n. GENLIST (leibniz n) (n + 1)``); +*) + +(* Use overloading for leibniz_horizontal n. *) +val _ = overload_on("leibniz_horizontal", ``\n. GENLIST (leibniz n) (n + 1)``); + +(* +> EVAL ``leibniz_horizontal 0``; +val it = |- leibniz_horizontal 0 = [1]: thm +> EVAL ``leibniz_horizontal 1``; +val it = |- leibniz_horizontal 1 = [2; 2]: thm +> EVAL ``leibniz_horizontal 2``; +val it = |- leibniz_horizontal 2 = [3; 6; 3]: thm +> EVAL ``leibniz_horizontal 3``; +val it = |- leibniz_horizontal 3 = [4; 12; 12; 4]: thm +> EVAL ``leibniz_horizontal 4``; +val it = |- leibniz_horizontal 4 = [5; 20; 30; 20; 5]: thm +> EVAL ``leibniz_horizontal 5``; +val it = |- leibniz_horizontal 5 = [6; 30; 60; 60; 30; 6]: thm +> EVAL ``leibniz_horizontal 6``; +val it = |- leibniz_horizontal 6 = [7; 42; 105; 140; 105; 42; 7]: thm +> EVAL ``leibniz_horizontal 7``; +val it = |- leibniz_horizontal 7 = [8; 56; 168; 280; 280; 168; 56; 8]: thm +> EVAL ``leibniz_horizontal 8``; +val it = |- leibniz_horizontal 8 = [9; 72; 252; 504; 630; 504; 252; 72; 9]: thm +*) + +(* Theorem: leibniz_horizontal 0 = [1] *) +(* Proof: + leibniz_horizontal 0 + = GENLIST (leibniz 0) (0 + 1) by notation + = GENLIST (leibniz 0) 1 by arithmetic + = [leibniz 0 0] by GENLIST + = [1] by leibniz_n_0 +*) +val leibniz_horizontal_0 = store_thm( + "leibniz_horizontal_0", + ``leibniz_horizontal 0 = [1]``, + rw_tac std_ss[GENLIST_1, leibniz_n_0]); + +(* Theorem: LENGTH (leibniz_horizontal n) = n + 1 *) +(* Proof: + LENGTH (leibniz_horizontal n) + = LENGTH (GENLIST (leibniz n) (n + 1)) by notation + = n + 1 by LENGTH_GENLIST +*) +val leibniz_horizontal_len = store_thm( + "leibniz_horizontal_len", + ``!n. LENGTH (leibniz_horizontal n) = n + 1``, + rw[]); + +(* Theorem: k <= n ==> EL k (leibniz_horizontal n) = leibniz n k *) +(* Proof: + Note k <= n means k < SUC n. + EL k (leibniz_horizontal n) + = EL k (GENLIST (leibniz n) (n + 1)) by notation + = EL k (GENLIST (leibniz n) (SUC n)) by ADD1 + = leibniz n k by EL_GENLIST, k < SUC n. +*) +val leibniz_horizontal_el = store_thm( + "leibniz_horizontal_el", + ``!n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k)``, + rw[LESS_EQ_IMP_LESS_SUC]); + +(* Theorem: k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) *) +(* Proof: + Note k <= n ==> k < (n + 1) + Thus MEM (leibniz n k) (GENLIST (leibniz n) (n + 1)) by MEM_GENLIST + or MEM (leibniz n k) (leibniz_horizontal n) by notation +*) +val leibniz_horizontal_mem = store_thm( + "leibniz_horizontal_mem", + ``!n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n)``, + metis_tac[MEM_GENLIST, DECIDE``k <= n ==> k < n + 1``]); + +(* Theorem: MEM (leibniz n k) (leibniz_horizontal n) <=> k <= n *) +(* Proof: + If part: (leibniz n k) (leibniz_horizontal n) ==> k <= n + By contradiction, suppose n < k. + Then leibniz n k = 0 by binomial_less_0, ~(k <= n) + But ?m. m < n + 1 ==> 0 = leibniz n m by MEM_GENLIST + or m <= n ==> leibniz n m = 0 by m < n + 1 + Yet leibniz n m <> 0 by leibniz_eq_0 + This is a contradiction. + Only-if part: k <= n ==> (leibniz n k) (leibniz_horizontal n) + By MEM_GENLIST, this is to show: + ?m. m < n + 1 /\ (leibniz n k = leibniz n m) + Note k <= n ==> k < n + 1, + Take m = k, the result follows. +*) +val leibniz_horizontal_mem_iff = store_thm( + "leibniz_horizontal_mem_iff", + ``!n k. MEM (leibniz n k) (leibniz_horizontal n) <=> k <= n``, + rw_tac bool_ss[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `leibniz n k = 0` by rw[leibniz_less_0] >> + fs[MEM_GENLIST] >> + `m <= n` by decide_tac >> + fs[binomial_eq_0], + rw[MEM_GENLIST] >> + `k < n + 1` by decide_tac >> + metis_tac[] + ]); + +(* Theorem: MEM x (leibniz_horizontal n) <=> ?k. k <= n /\ (x = leibniz n k) *) +(* Proof: + By MEM_GENLIST, this is to show: + (?m. m < n + 1 /\ (x = (n + 1) * binomial n m)) <=> ?k. k <= n /\ (x = (n + 1) * binomial n k) + Since m < n + 1 <=> m <= n by LE_LT1 + This is trivially true. +*) +val leibniz_horizontal_member = store_thm( + "leibniz_horizontal_member", + ``!n x. MEM x (leibniz_horizontal n) <=> ?k. k <= n /\ (x = leibniz n k)``, + metis_tac[MEM_GENLIST, LE_LT1]); + +(* Theorem: k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k) *) +(* Proof: by EL_GENLIST *) +val leibniz_horizontal_element = store_thm( + "leibniz_horizontal_element", + ``!n k. k <= n ==> (EL k (leibniz_horizontal n) = leibniz n k)``, + rw[EL_GENLIST]); + +(* Theorem: TAKE 1 (leibniz_horizontal (n + 1)) = [n + 2] *) +(* Proof: + TAKE 1 (leibniz_horizontal (n + 1)) + = TAKE 1 (GENLIST (leibniz (n + 1)) (n + 1 + 1)) by notation + = TAKE 1 (GENLIST (leibniz (SUC n)) (SUC (SUC n))) by ADD1 + = TAKE 1 ((leibniz (SUC n) 0) :: GENLIST ((leibniz (SUC n)) o SUC) n) by GENLIST_CONS + = (leibniz (SUC n) 0):: TAKE 0 (GENLIST ((leibniz (SUC n)) o SUC) n) by TAKE_def + = [leibniz (SUC n) 0]:: [] by TAKE_0 + = [SUC n + 1] by leibniz_n_0 + = [n + 2] by ADD1 +*) +val leibniz_horizontal_head = store_thm( + "leibniz_horizontal_head", + ``!n. TAKE 1 (leibniz_horizontal (n + 1)) = [n + 2]``, + rpt strip_tac >> + `(!n. n + 1 = SUC n) /\ (!n. n + 2 = SUC (SUC n))` by decide_tac >> + rw[GENLIST_CONS, leibniz_n_0]); + +(* Theorem: k <= n ==> (leibniz n k) divides list_lcm (leibniz_horizontal n) *) +(* Proof: + Note MEM (leibniz n k) (leibniz_horizontal n) by leibniz_horizontal_mem + so (leibniz n k) divides list_lcm (leibniz_horizontal n) by list_lcm_is_common_multiple +*) +val leibniz_horizontal_divisor = store_thm( + "leibniz_horizontal_divisor", + ``!n k. k <= n ==> (leibniz n k) divides list_lcm (leibniz_horizontal n)``, + rw[leibniz_horizontal_mem, list_lcm_is_common_multiple]); + +(* Theorem: EVERY_POSITIVE (leibniz_horizontal n) *) +(* Proof: + Let l = leibniz_horizontal n + Then LENGTH l = n + 1 by leibniz_horizontal_len + EVERY_POSITIVE l + <=> !k. k < LENGTH l ==> 0 < (EL k l) by EVERY_EL + <=> !k. k < n + 1 ==> 0 < (EL k l) by above + <=> !k. k <= n ==> 0 < EL k l by arithmetic + <=> !k. k <= n ==> 0 < leibniz n k by leibniz_horizontal_el + <=> T by leibniz_pos +*) +Theorem leibniz_horizontal_pos: + !n. EVERY_POSITIVE (leibniz_horizontal n) +Proof + simp[EVERY_EL, binomial_pos] +QED + +(* Theorem: POSITIVE (leibniz_horizontal n) *) +(* Proof: by leibniz_horizontal_pos, EVERY_MEM *) +val leibniz_horizontal_pos_alt = store_thm( + "leibniz_horizontal_pos_alt", + ``!n. POSITIVE (leibniz_horizontal n)``, + metis_tac[leibniz_horizontal_pos, EVERY_MEM]); + +(* Theorem: leibniz_horizontal n = MAP (\j. (n+1) * j) (binomial_horizontal n) *) +(* Proof: + leibniz_horizontal n + = GENLIST (leibniz n) (n + 1) by notation + = GENLIST ((\j. (n + 1) * j) o (binomial n)) (n + 1) by leibniz_alt + = MAP (\j. (n + 1) * j) (GENLIST (binomial n) (n + 1)) by MAP_GENLIST + = MAP (\j. (n + 1) * j) (binomial_horizontal n) by notation +*) +val leibniz_horizontal_alt = store_thm( + "leibniz_horizontal_alt", + ``!n. leibniz_horizontal n = MAP (\j. (n+1) * j) (binomial_horizontal n)``, + rw_tac std_ss[leibniz_alt, MAP_GENLIST]); + +(* Theorem: list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n) *) +(* Proof: + Since LENGTH (binomial_horizontal n) = n + 1 by binomial_horizontal_len + binomial_horizontal n <> [] by LENGTH_NIL ... [1] + list_lcm (leibniz_horizontal n) + = list_lcm (MAP (\j (n+1) * j) (binomial_horizontal n)) by leibniz_horizontal_alt + = (n + 1) * list_lcm (binomial_horizontal n) by list_lcm_map_times, [1] +*) +val leibniz_horizontal_lcm_alt = store_thm( + "leibniz_horizontal_lcm_alt", + ``!n. list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n)``, + rpt strip_tac >> + `LENGTH (binomial_horizontal n) = n + 1` by rw[binomial_horizontal_len] >> + `n + 1 <> 0` by decide_tac >> + `binomial_horizontal n <> []` by metis_tac[LENGTH_NIL] >> + rw_tac std_ss[leibniz_horizontal_alt, list_lcm_map_times]); + +(* Theorem: SUM (leibniz_horizontal n) = (n + 1) * SUM (binomial_horizontal n) *) +(* Proof: + SUM (leibniz_horizontal n) + = SUM (MAP (\j. (n + 1) * j) (binomial_horizontal n)) by leibniz_horizontal_alt + = (n + 1) * SUM (binomial_horizontal n) by SUM_MULT +*) +val leibniz_horizontal_sum = store_thm( + "leibniz_horizontal_sum", + ``!n. SUM (leibniz_horizontal n) = (n + 1) * SUM (binomial_horizontal n)``, + rw[leibniz_horizontal_alt, SUM_MULT] >> + `(\j. j * (n + 1)) = $* (n + 1)` by rw[FUN_EQ_THM] >> + rw[]); + +(* Theorem: SUM (leibniz_horizontal n) = (n + 1) * 2 ** n *) +(* Proof: + SUM (leibniz_horizontal n) + = (n + 1) * SUM (binomial_horizontal n) by leibniz_horizontal_sum + = (n + 1) * 2 ** n by binomial_horizontal_sum +*) +val leibniz_horizontal_sum_eqn = store_thm( + "leibniz_horizontal_sum_eqn", + ``!n. SUM (leibniz_horizontal n) = (n + 1) * 2 ** n``, + rw[leibniz_horizontal_sum, binomial_horizontal_sum]); + +(* Theorem: SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = SUM (binomial_horizontal n) *) +(* Proof: + Note LENGTH (leibniz_horizontal n) = n + 1 by leibniz_horizontal_len + so 0 < LENGTH (leibniz_horizontal n) by 0 < n + 1 + + SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) + = ((n + 1) * SUM (binomial_horizontal n)) DIV (n + 1) by leibniz_horizontal_sum + = SUM (binomial_horizontal n) by MULT_TO_DIV, 0 < n + 1 +*) +val leibniz_horizontal_average = store_thm( + "leibniz_horizontal_average", + ``!n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = SUM (binomial_horizontal n)``, + metis_tac[leibniz_horizontal_sum, leibniz_horizontal_len, MULT_TO_DIV, DECIDE``0 < n + 1``]); + +(* Theorem: SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = 2 ** n *) +(* Proof: + SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) + = SUM (binomial_horizontal n) by leibniz_horizontal_average + = 2 ** n by binomial_horizontal_sum +*) +val leibniz_horizontal_average_eqn = store_thm( + "leibniz_horizontal_average_eqn", + ``!n. SUM (leibniz_horizontal n) DIV LENGTH (leibniz_horizontal n) = 2 ** n``, + rw[leibniz_horizontal_average, binomial_horizontal_sum]); + +(* ------------------------------------------------------------------------- *) +(* Transform from Vertical LCM to Horizontal LCM. *) +(* ------------------------------------------------------------------------- *) + +(* ------------------------------------------------------------------------- *) +(* Using Triplet and Paths *) +(* ------------------------------------------------------------------------- *) + +(* Define a triple type *) +val _ = Hol_datatype` + triple = <| a: num; + b: num; + c: num + |> +`; + +(* A triplet is a triple composed of Leibniz node and children. *) +val triplet_def = Define` + (triplet n k):triple = + <| a := leibniz n k; + b := leibniz (n + 1) k; + c := leibniz (n + 1) (k + 1) + |> +`; + +(* can even do this after definition of triple type: + +val triple_def = Define` + triple n k = + <| a := leibniz n k; + b := leibniz (n + 1) k; + c := leibniz (n + 1) (k + 1) + |> +`; +*) + +(* Overload elements of a triplet *) +(* +val _ = overload_on("tri_a", ``leibniz n k``); +val _ = overload_on("tri_b", ``leibniz (SUC n) k``); +val _ = overload_on("tri_c", ``leibniz (SUC n) (SUC k)``); + +val _ = overload_on("tri_a", ``(triple n k).a``); +val _ = overload_on("tri_b", ``(triple n k).b``); +val _ = overload_on("tri_c", ``(triple n k).c``); +*) +val _ = temp_overload_on("ta", ``(triplet n k).a``); +val _ = temp_overload_on("tb", ``(triplet n k).b``); +val _ = temp_overload_on("tc", ``(triplet n k).c``); + +(* Theorem: (ta = leibniz n k) /\ (tb = leibniz (n + 1) k) /\ (tc = leibniz (n + 1) (k + 1)) *) +(* Proof: by triplet_def *) +val leibniz_triplet_member = store_thm( + "leibniz_triplet_member", + ``!n k. (ta = leibniz n k) /\ (tb = leibniz (n + 1) k) /\ (tc = leibniz (n + 1) (k + 1))``, + rw[triplet_def]); + +(* Theorem: (k + 1) * tc = (n + 1 - k) * tb *) +(* Proof: + Apply: > leibniz_right_eqn |> SPEC ``n+1``; + val it = |- 0 < n + 1 ==> !k. (k + 1) * leibniz (n + 1) (k + 1) = (n + 1 - k) * leibniz (n + 1) k: thm +*) +val leibniz_right_entry = store_thm( + "leibniz_right_entry", + ``!(n k):num. (k + 1) * tc = (n + 1 - k) * tb``, + rw_tac arith_ss[triplet_def, leibniz_right_eqn]); + +(* Theorem: (n + 2) * ta = (n + 1 - k) * tb *) +(* Proof: + Apply: > leibniz_up_eqn |> SPEC ``n+1``; + val it = |- 0 < n + 1 ==> !k. (n + 1 + 1) * leibniz (n + 1 - 1) k = (n + 1 - k) * leibniz (n + 1) k: thm +*) +val leibniz_up_entry = store_thm( + "leibniz_up_entry", + ``!(n k):num. (n + 2) * ta = (n + 1 - k) * tb``, + rw_tac std_ss[triplet_def, leibniz_up_eqn |> SPEC ``n+1`` |> SIMP_RULE arith_ss[]]); + +(* Theorem: ta * tb = tc * (tb - ta) *) +(* Proof: + Apply > leibniz_property |> SPEC ``n+1``; + val it = |- 0 < n + 1 ==> !k. !k. leibniz (n + 1) k * leibniz (n + 1 - 1) k = + leibniz (n + 1) (k + 1) * (leibniz (n + 1) k - leibniz (n + 1 - 1) k): thm +*) +val leibniz_triplet_property = store_thm( + "leibniz_triplet_property", + ``!(n k):num. ta * tb = tc * (tb - ta)``, + rw_tac std_ss[triplet_def, MULT_COMM, leibniz_property |> SPEC ``n+1`` |> SIMP_RULE arith_ss[]]); + +(* Direct proof of same result, for the paper. *) + +(* Theorem: ta * tb = tc * (tb - ta) *) +(* Proof: + If n < k, + Note n < k ==> ta = 0 by triplet_def, leibniz_less_0 + also n + 1 < k + 1 ==> tc = 0 by triplet_def, leibniz_less_0 + Thus ta * tb = 0 = tc * (tb - ta) by MULT_EQ_0 + If ~(n < k), + Then (n + 2) - (n + 1 - k) = k + 1 by arithmetic, k <= n. + + (k + 1) * ta * tb + = (n + 2 - (n + 1 - k)) * ta * tb + = (n + 2) * ta * tb - (n + 1 - k) * ta * tb by RIGHT_SUB_DISTRIB + = (n + 1 - k) * tb * tb - (n + 1 - k) * ta * tb by leibniz_up_entry + = (n + 1 - k) * tb * tb - (n + 1 - k) * tb * ta by MULT_ASSOC, MULT_COMM + = (n + 1 - k) * tb * (tb - ta) by LEFT_SUB_DISTRIB + = (k + 1) * tc * (tb - ta) by leibniz_right_entry + + Since k + 1 <> 0, the result follows by MULT_LEFT_CANCEL +*) +Theorem leibniz_triplet_property[allow_rebind]: + !n k:num. ta * tb = tc * (tb - ta) +Proof + rpt strip_tac >> + Cases_on ‘n < k’ >- + rw[triplet_def, leibniz_less_0] >> + ‘(n + 2) - (n + 1 - k) = k + 1’ by decide_tac >> + ‘(k + 1) * ta * tb = (n + 2 - (n + 1 - k)) * ta * tb’ by rw[] >> + ‘_ = (n + 2) * ta * tb - (n + 1 - k) * ta * tb’ by rw_tac std_ss[RIGHT_SUB_DISTRIB] >> + ‘_ = (n + 1 - k) * tb * tb - (n + 1 - k) * ta * tb’ by rw_tac std_ss[leibniz_up_entry] >> + ‘_ = (n + 1 - k) * tb * tb - (n + 1 - k) * tb * ta’ by metis_tac[MULT_ASSOC, MULT_COMM] >> + ‘_ = (n + 1 - k) * tb * (tb - ta)’ by rw_tac std_ss[LEFT_SUB_DISTRIB] >> + ‘_ = (k + 1) * tc * (tb - ta)’ by rw_tac std_ss[leibniz_right_entry] >> + ‘k + 1 <> 0’ by decide_tac >> + metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] +QED + +(* Theorem: lcm tb ta = lcm tb tc *) +(* Proof: + Apply: > leibniz_lcm_exchange |> SPEC ``n+1``; + val it = |- 0 < n + 1 ==> + !k. lcm (leibniz (n + 1) k) (leibniz (n + 1 - 1) k) = + lcm (leibniz (n + 1) k) (leibniz (n + 1) (k + 1)): thm +*) +val leibniz_triplet_lcm = store_thm( + "leibniz_triplet_lcm", + ``!(n k):num. lcm tb ta = lcm tb tc``, + rw_tac std_ss[triplet_def, leibniz_lcm_exchange |> SPEC ``n+1`` |> SIMP_RULE arith_ss[]]); + +(* ------------------------------------------------------------------------- *) +(* Zigzag Path in Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* Define a path type *) +val _ = temp_type_abbrev("path", Type `:num list`); + +(* Define paths reachable by one zigzag *) +val leibniz_zigzag_def = Define` + leibniz_zigzag (p1: path) (p2: path) <=> + ?(n k):num (x y):path. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) +`; +val _ = overload_on("zigzag", ``leibniz_zigzag``); +val _ = set_fixity "zigzag" (Infix(NONASSOC, 450)); (* same as relation *) + +(* Theorem: p1 zigzag p2 ==> (list_lcm p1 = list_lcm p2) *) +(* Proof: + Given p1 zigzag p2, + ==> ?n k x y. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) by leibniz_zigzag_def + + list_lcm p1 + = list_lcm (x ++ [tb; ta] ++ y) by above + = lcm (list_lcm (x ++ [tb; ta])) (list_lcm y) by list_lcm_append + = lcm (list_lcm (x ++ ([tb; ta]))) (list_lcm y) by APPEND_ASSOC + = lcm (lcm (list_lcm x) (list_lcm ([tb; ta]))) (list_lcm y) by list_lcm_append + = lcm (lcm (list_lcm x) (lcm tb ta)) (list_lcm y) by list_lcm_append, list_lcm_sing + = lcm (lcm (list_lcm x) (lcm tb tc)) (list_lcm y) by leibniz_triplet_lcm + = lcm (lcm (list_lcm x) (list_lcm ([tb; tc]))) (list_lcm y) by list_lcm_append, list_lcm_sing + = lcm (list_lcm (x ++ ([tb; tc]))) (list_lcm y) by list_lcm_append + = lcm (list_lcm (x ++ [tb; tc])) (list_lcm y) by APPEND_ASSOC + = list_lcm (x ++ [tb; tc] ++ y) by list_lcm_append + = list_lcm p2 by above +*) +val list_lcm_zigzag = store_thm( + "list_lcm_zigzag", + ``!p1 p2. p1 zigzag p2 ==> (list_lcm p1 = list_lcm p2)``, + rw_tac std_ss[leibniz_zigzag_def] >> + `list_lcm (x ++ [tb; ta] ++ y) = lcm (list_lcm (x ++ [tb; ta])) (list_lcm y)` by rw[list_lcm_append] >> + `_ = lcm (list_lcm (x ++ ([tb; ta]))) (list_lcm y)` by rw[] >> + `_ = lcm (lcm (list_lcm x) (lcm tb ta)) (list_lcm y)` by rw[list_lcm_append] >> + `_ = lcm (lcm (list_lcm x) (lcm tb tc)) (list_lcm y)` by rw[leibniz_triplet_lcm] >> + `_ = lcm (list_lcm (x ++ ([tb; tc]))) (list_lcm y)` by rw[list_lcm_append] >> + `_ = lcm (list_lcm (x ++ [tb; tc])) (list_lcm y)` by rw[] >> + `_ = list_lcm (x ++ [tb; tc] ++ y)` by rw[list_lcm_append] >> + rw[]); + +(* Theorem: p1 zigzag p2 ==> !x. ([x] ++ p1) zigzag ([x] ++ p2) *) +(* Proof: + Since p1 zigzag p2 + ==> ?n k x y. (p1 = x ++ [tb; ta] ++ y) /\ (p2 = x ++ [tb; tc] ++ y) by leibniz_zigzag_def + + [x] ++ p1 + = [x] ++ (x ++ [tb; ta] ++ y) by above + = [x] ++ x ++ [tb; ta] ++ y by APPEND + [x] ++ p2 + = [x] ++ (x ++ [tb; tc] ++ y) by above + = [x] ++ x ++ [tb; tc] ++ y by APPEND + Take new x = [x] ++ x, new y = y. + Then ([x] ++ p1) zigzag ([x] ++ p2) by leibniz_zigzag_def +*) +val leibniz_zigzag_tail = store_thm( + "leibniz_zigzag_tail", + ``!p1 p2. p1 zigzag p2 ==> !x. ([x] ++ p1) zigzag ([x] ++ p2)``, + metis_tac[leibniz_zigzag_def, APPEND]); + +(* Theorem: k <= n ==> + TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) zigzag + TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) *) +(* Proof: + Since k <= n, k < n + 1, and k + 1 < n + 2. + Hence k < LENGTH (leibniz_horizontal (n + 1)), + + Let x = TAKE k (leibniz_horizontal (n + 1)) + and y = DROP (k + 1) (leibniz_horizontal n) + TAKE (k + 1) (leibniz_horizontal (n + 1)) + = TAKE (SUC k) (leibniz_horizontal (SUC n)) by ADD1 + = SNOC tb x by TAKE_SUC_BY_TAKE, k < LENGTH (leibniz_horizontal (n + 1)) + = x ++ [tb] by SNOC_APPEND + TAKE (k + 2) (leibniz_horizontal (n + 1)) + = TAKE (SUC (SUC k)) (leibniz_horizontal (SUC n)) by ADD1 + = SNOC tc (SNOC tb x) by TAKE_SUC_BY_TAKE, k + 1 < LENGTH (leibniz_horizontal (n + 1)) + = x ++ [tb; tc] by SNOC_APPEND + DROP k (leibniz_horizontal n) + = ta :: y by DROP_BY_DROP_SUC, k < LENGTH (leibniz_horizontal n) + = [ta] ++ y by CONS_APPEND + Hence + Let p1 = TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) + = x ++ [tb] ++ [ta] ++ y + = x ++ [tb; ta] ++ y by APPEND + Let p2 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) + = x ++ [tb; tc] ++ y + Therefore p1 zigzag p2 by leibniz_zigzag_def +*) +val leibniz_horizontal_zigzag = store_thm( + "leibniz_horizontal_zigzag", + ``!n k. k <= n ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) zigzag + TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n)``, + rpt strip_tac >> + qabbrev_tac `x = TAKE k (leibniz_horizontal (n + 1))` >> + qabbrev_tac `y = DROP (k + 1) (leibniz_horizontal n)` >> + `k <= n + 1` by decide_tac >> + `EL k (leibniz_horizontal n) = ta` by rw_tac std_ss[triplet_def, leibniz_horizontal_el] >> + `EL k (leibniz_horizontal (n + 1)) = tb` by rw_tac std_ss[triplet_def, leibniz_horizontal_el] >> + `EL (k + 1) (leibniz_horizontal (n + 1)) = tc` by rw_tac std_ss[triplet_def, leibniz_horizontal_el] >> + `k < n + 1` by decide_tac >> + `k < LENGTH (leibniz_horizontal (n + 1))` by rw[leibniz_horizontal_len] >> + `TAKE (k + 1) (leibniz_horizontal (n + 1)) = TAKE (SUC k) (leibniz_horizontal (n + 1))` by rw[ADD1] >> + `_ = SNOC tb x` by rw[TAKE_SUC_BY_TAKE, Abbr`x`] >> + `_ = x ++ [tb]` by rw[] >> + `SUC k < n + 2` by decide_tac >> + `SUC k < LENGTH (leibniz_horizontal (n + 1))` by rw[leibniz_horizontal_len] >> + `TAKE (k + 2) (leibniz_horizontal (n + 1)) = TAKE (SUC (SUC k)) (leibniz_horizontal (n + 1))` by rw[ADD1] >> + `_ = SNOC tc (SNOC tb x)` by rw_tac std_ss[TAKE_SUC_BY_TAKE, ADD1, Abbr`x`] >> + `_ = x ++ [tb; tc]` by rw[] >> + `DROP k (leibniz_horizontal n) = [ta] ++ y` by rw[DROP_BY_DROP_SUC, ADD1, Abbr`y`] >> + qabbrev_tac `p1 = TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n)` >> + qabbrev_tac `p2 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ y` >> + `p1 = x ++ [tb; ta] ++ y` by rw[Abbr`p1`, Abbr`x`, Abbr`y`] >> + `p2 = x ++ [tb; tc] ++ y` by rw[Abbr`p2`, Abbr`x`] >> + metis_tac[leibniz_zigzag_def]); + +(* Theorem: (leibniz_up 1) zigzag (leibniz_horizontal 1) *) +(* Proof: + Since leibniz_up 1 + = [2; 1] by EVAL_TAC + = [] ++ [2; 1] ++ [] by EVAL_TAC + and leibniz_horizontal 1 + = [2; 2] by EVAL_TAC + = [] ++ [2; 2] ++ [] by EVAL_TAC + Now the first Leibniz triplet is: + (triplet 0 0).a = 1 by EVAL_TAC + (triplet 0 0).b = 2 by EVAL_TAC + (triplet 0 0).c = 2 by EVAL_TAC + Hence (leibniz_up 1) zigzag (leibniz_horizontal 1) by leibniz_zigzag_def +*) +val leibniz_triplet_0 = store_thm( + "leibniz_triplet_0", + ``(leibniz_up 1) zigzag (leibniz_horizontal 1)``, + `leibniz_up 1 = [] ++ [2; 1] ++ []` by EVAL_TAC >> + `leibniz_horizontal 1 = [] ++ [2; 2] ++ []` by EVAL_TAC >> + `((triplet 0 0).a = 1) /\ ((triplet 0 0).b = 2) /\ ((triplet 0 0).c = 2)` by EVAL_TAC >> + metis_tac[leibniz_zigzag_def]); + +(* ------------------------------------------------------------------------- *) +(* Wriggle Paths in Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* Define paths reachable by many zigzags *) +(* +val leibniz_wriggle_def = Define` + leibniz_wriggle (p1: path) (p2: path) <=> + ?(m:num) (f:num -> path). + (p1 = f 0) /\ + (p2 = f m) /\ + (!k. k < m ==> (f k) zigzag (f (SUC k))) +`; +*) + +(* Define paths reachable by many zigzags by closure *) +val _ = overload_on("wriggle", ``RTC leibniz_zigzag``); (* RTC = reflexive transitive closure *) +val _ = set_fixity "wriggle" (Infix(NONASSOC, 450)); (* same as relation *) + +(* Theorem: p1 wriggle p2 ==> (list_lcm p1 = list_lcm p2) *) +(* Proof: + By RTC_STRONG_INDUCT. + Base: list_lcm p1 = list_lcm p1, trivially true. + Step: p1 zigzag p1' /\ p1' wriggle p2 /\ list_lcm p1' = list_lcm p2 ==> list_lcm p1 = list_lcm p2 + list_lcm p1 + = list_lcm p1' by list_lcm_zigzag + = list_lcm p2 by induction hypothesis +*) +val list_lcm_wriggle = store_thm( + "list_lcm_wriggle", + ``!p1 p2. p1 wriggle p2 ==> (list_lcm p1 = list_lcm p2)``, + ho_match_mp_tac RTC_STRONG_INDUCT >> + rpt strip_tac >- + rw[] >> + metis_tac[list_lcm_zigzag]); + +(* Theorem: p1 zigzag p2 ==> p1 wriggle p2 *) +(* Proof: + p1 wriggle p2 + = p1 (RTC zigzag) p2 by notation + = p1 zigzag p2 by RTC_SINGLE +*) +val leibniz_zigzag_wriggle = store_thm( + "leibniz_zigzag_wriggle", + ``!p1 p2. p1 zigzag p2 ==> p1 wriggle p2``, + rw[]); + +(* Theorem: p1 wriggle p2 ==> !x. ([x] ++ p1) wriggle ([x] ++ p2) *) +(* Proof: + By RTC_STRONG_INDUCT. + Base: [x] ++ p1 wriggle [x] ++ p1 + True by RTC_REFL. + Step: p1 zigzag p1' /\ p1' wriggle p2 /\ !x. [x] ++ p1' wriggle [x] ++ p2 ==> + [x] ++ p1 wriggle [x] ++ p2 + Since p1 zigzag p1', + so [x] ++ p1 zigzag [x] ++ p1' by leibniz_zigzag_tail + or [x] ++ p1 wriggle [x] ++ p1' by leibniz_zigzag_wriggle + With [x] ++ p1' wriggle [x] ++ p2 by induction hypothesis + Hence [x] ++ p1 wriggle [x] ++ p2 by RTC_TRANS +*) +val leibniz_wriggle_tail = store_thm( + "leibniz_wriggle_tail", + ``!p1 p2. p1 wriggle p2 ==> !x. ([x] ++ p1) wriggle ([x] ++ p2)``, + ho_match_mp_tac RTC_STRONG_INDUCT >> + rpt strip_tac >- + rw[] >> + metis_tac[leibniz_zigzag_tail, leibniz_zigzag_wriggle, RTC_TRANS]); + +(* Theorem: p1 wriggle p1 *) +(* Proof: by RTC_REFL *) +val leibniz_wriggle_refl = store_thm( + "leibniz_wriggle_refl", + ``!p1. p1 wriggle p1``, + metis_tac[RTC_REFL]); + +(* Theorem: p1 wriggle p2 /\ p2 wriggle p3 ==> p1 wriggle p3 *) +(* Proof: by RTC_TRANS *) +val leibniz_wriggle_trans = store_thm( + "leibniz_wriggle_trans", + ``!p1 p2 p3. p1 wriggle p2 /\ p2 wriggle p3 ==> p1 wriggle p3``, + metis_tac[RTC_TRANS]); + +(* Theorem: k <= n + 1 ==> + TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle + leibniz_horizontal (n + 1) *) +(* Proof: + By induction on the difference: n + 1 - k. + Base: k = n + 1 ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle + leibniz_horizontal (n + 1) + TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) + = TAKE (n + 2) (leibniz_horizontal (n + 1)) ++ DROP (n + 1) (leibniz_horizontal n) by k = n + 1 + = leibniz_horizontal (n + 1) ++ [] by TAKE_LENGTH_ID, DROP_LENGTH_NIL + = leibniz_horizontal (n + 1) by APPEND_NIL + Hence they wriggle to each other by RTC_REFL + Step: k <= n + 1 ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle + leibniz_horizontal (n + 1) + Let p1 = leibniz_horizontal (n + 1) + p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_horizontal n) + p3 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n) + Then p2 zigzag p3 by leibniz_horizontal_zigzag + and p3 wriggle p1 by induction hypothesis + Hence p2 wriggle p1 by RTC_RULES +*) +val leibniz_horizontal_wriggle_step = store_thm( + "leibniz_horizontal_wriggle_step", + ``!n k. k <= n + 1 ==> TAKE (k + 1) (leibniz_horizontal (n + 1)) ++ DROP k (leibniz_horizontal n) wriggle + leibniz_horizontal (n + 1)``, + Induct_on `n + 1 - k` >| [ + rpt strip_tac >> + rw_tac arith_ss[] >> + `n + 1 = k` by decide_tac >> + rw[TAKE_LENGTH_ID_rwt, DROP_LENGTH_NIL_rwt], + rpt strip_tac >> + `v = n - k` by decide_tac >> + `v = (n + 1) - (k + 1)` by decide_tac >> + `k <= n` by decide_tac >> + `k + 1 <= n + 1` by decide_tac >> + `k + 1 + 1 = k + 2` by decide_tac >> + qabbrev_tac `p1 = leibniz_horizontal (n + 1)` >> + qabbrev_tac `p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_horizontal n)` >> + qabbrev_tac `p3 = TAKE (k + 2) (leibniz_horizontal (n + 1)) ++ DROP (k + 1) (leibniz_horizontal n)` >> + `p2 zigzag p3` by rw[leibniz_horizontal_zigzag, Abbr`p1`, Abbr`p2`, Abbr`p3`] >> + metis_tac[RTC_RULES] + ]); + +(* Theorem: ([leibniz (n + 1) 0] ++ leibniz_horizontal n) wriggle leibniz_horizontal (n + 1) *) +(* Proof: + Apply > leibniz_horizontal_wriggle_step |> SPEC ``n:num`` |> SPEC ``0`` |> SIMP_RULE std_ss[DROP_0]; + val it = |- TAKE 1 (leibniz_horizontal (n + 1)) ++ leibniz_horizontal n wriggle leibniz_horizontal (n + 1): thm +*) +val leibniz_horizontal_wriggle = store_thm( + "leibniz_horizontal_wriggle", + ``!n. ([leibniz (n + 1) 0] ++ leibniz_horizontal n) wriggle leibniz_horizontal (n + 1)``, + rpt strip_tac >> + `TAKE 1 (leibniz_horizontal (n + 1)) = [leibniz (n + 1) 0]` by rw[leibniz_horizontal_head, binomial_n_0] >> + metis_tac[leibniz_horizontal_wriggle_step |> SPEC ``n:num`` |> SPEC ``0`` |> SIMP_RULE std_ss[DROP_0]]); + +(* ------------------------------------------------------------------------- *) +(* Path Transform keeping LCM *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: (leibniz_up n) wriggle (leibniz_horizontal n) *) +(* Proof: + By induction on n. + Base: leibniz_up 0 wriggle leibniz_horizontal 0 + Since leibniz_up 0 = [1] by leibniz_up_0 + and leibniz_horizontal 0 = [1] by leibniz_horizontal_0 + Hence leibniz_up 0 wriggle leibniz_horizontal 0 by leibniz_wriggle_refl + Step: leibniz_up n wriggle leibniz_horizontal n ==> + leibniz_up (SUC n) wriggle leibniz_horizontal (SUC n) + Let x = leibniz (n + 1) 0. + Then x = n + 2 by leibniz_n_0 + Now leibniz_up (n + 1) = [x] ++ (leibniz_up n) by leibniz_up_cons + Since leibniz_up n wriggle leibniz_horizontal n by induction hypothesis + so ([x] ++ (leibniz_up n)) wriggle + ([x] ++ (leibniz_horizontal n)) by leibniz_wriggle_tail + and ([x] ++ (leibniz_horizontal n)) wriggle + (leibniz_horizontal (n + 1)) by leibniz_horizontal_wriggle + Hence leibniz_up (SUC n) wriggle + leibniz_horizontal (SUC n) by leibniz_wriggle_trans, ADD1 +*) +val leibniz_up_wriggle_horizontal = store_thm( + "leibniz_up_wriggle_horizontal", + ``!n. (leibniz_up n) wriggle (leibniz_horizontal n)``, + Induct >- + rw[leibniz_up_0, leibniz_horizontal_0] >> + qabbrev_tac `x = leibniz (n + 1) 0` >> + `x = n + 2` by rw[leibniz_n_0, Abbr`x`] >> + `leibniz_up (n + 1) = [x] ++ (leibniz_up n)` by rw[leibniz_up_cons, Abbr`x`] >> + `([x] ++ (leibniz_up n)) wriggle ([x] ++ (leibniz_horizontal n))` by rw[leibniz_wriggle_tail] >> + `([x] ++ (leibniz_horizontal n)) wriggle (leibniz_horizontal (n + 1))` by rw[leibniz_horizontal_wriggle, Abbr`x`] >> + metis_tac[leibniz_wriggle_trans, ADD1]); + +(* Theorem: list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n) *) +(* Proof: + Since leibniz_up n = REVERSE (leibniz_vertical n) by notation + and leibniz_up n wriggle leibniz_horizontal n by leibniz_up_wriggle_horizontal + list_lcm (leibniz_vertical n) + = list_lcm (leibniz_up n) by list_lcm_reverse + = list_lcm (leibniz_horizontal n) by list_lcm_wriggle +*) +val leibniz_lcm_property = store_thm( + "leibniz_lcm_property", + ``!n. list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n)``, + metis_tac[leibniz_up_wriggle_horizontal, list_lcm_wriggle, list_lcm_reverse]); + +(* This is a milestone theorem. *) + +(* Theorem: k <= n ==> (leibniz n k) divides list_lcm (leibniz_vertical n) *) +(* Proof: + Note (leibniz n k) divides list_lcm (leibniz_horizontal n) by leibniz_horizontal_divisor + ==> (leibniz n k) divides list_lcm (leibniz_vertical n) by leibniz_lcm_property +*) +val leibniz_vertical_divisor = store_thm( + "leibniz_vertical_divisor", + ``!n k. k <= n ==> (leibniz n k) divides list_lcm (leibniz_vertical n)``, + metis_tac[leibniz_horizontal_divisor, leibniz_lcm_property]); + +(* ------------------------------------------------------------------------- *) +(* Lower Bound of Leibniz LCM *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 2 ** n <= list_lcm (leibniz_horizontal n) *) +(* Proof: + Note LENGTH (binomail_horizontal n) = n + 1 by binomial_horizontal_len + and EVERY_POSITIVE (binomial_horizontal n) by binomial_horizontal_pos .. [1] + list_lcm (leibniz_horizontal n) + = (n + 1) * list_lcm (binomial_horizontal n) by leibniz_horizontal_lcm_alt + >= SUM (binomial_horizontal n) by list_lcm_lower_bound, [1] + = 2 ** n by binomial_horizontal_sum +*) +val leibniz_horizontal_lcm_lower = store_thm( + "leibniz_horizontal_lcm_lower", + ``!n. 2 ** n <= list_lcm (leibniz_horizontal n)``, + rpt strip_tac >> + `LENGTH (binomial_horizontal n) = n + 1` by rw[binomial_horizontal_len] >> + `EVERY_POSITIVE (binomial_horizontal n)` by rw[binomial_horizontal_pos] >> + `list_lcm (leibniz_horizontal n) = (n + 1) * list_lcm (binomial_horizontal n)` by rw[leibniz_horizontal_lcm_alt] >> + `SUM (binomial_horizontal n) = 2 ** n` by rw[binomial_horizontal_sum] >> + metis_tac[list_lcm_lower_bound]); + +(* Theorem: 2 ** n <= list_lcm (leibniz_vertical n) *) +(* Proof: + list_lcm (leibniz_vertical n) + = list_lcm (leibniz_horizontal n) by leibniz_lcm_property + >= 2 ** n by leibniz_horizontal_lcm_lower +*) +val leibniz_vertical_lcm_lower = store_thm( + "leibniz_vertical_lcm_lower", + ``!n. 2 ** n <= list_lcm (leibniz_vertical n)``, + rw_tac std_ss[leibniz_horizontal_lcm_lower, leibniz_lcm_property]); + +(* Theorem: 2 ** n <= list_lcm [1 .. (n + 1)] *) +(* Proof: by leibniz_vertical_lcm_lower. *) +val lcm_lower_bound = store_thm( + "lcm_lower_bound", + ``!n. 2 ** n <= list_lcm [1 .. (n + 1)]``, + rw[leibniz_vertical_lcm_lower]); + +(* ------------------------------------------------------------------------- *) +(* Leibniz LCM Invariance *) +(* ------------------------------------------------------------------------- *) + +(* Use overloading for leibniz_col_arm rooted at leibniz a b, of length n. *) +val _ = overload_on("leibniz_col_arm", ``\a b n. MAP (\x. leibniz (a - x) b) [0 ..< n]``); + +(* Use overloading for leibniz_seg_arm rooted at leibniz a b, of length n. *) +val _ = overload_on("leibniz_seg_arm", ``\a b n. MAP (\x. leibniz a (b + x)) [0 ..< n]``); + +(* +> EVAL ``leibniz_col_arm 5 1 4``; +val it = |- leibniz_col_arm 5 1 4 = [30; 20; 12; 6]: thm +> EVAL ``leibniz_seg_arm 5 1 4``; +val it = |- leibniz_seg_arm 5 1 4 = [30; 60; 60; 30]: thm +> EVAL ``list_lcm (leibniz_col_arm 5 1 4)``; +val it = |- list_lcm (leibniz_col_arm 5 1 4) = 60: thm +> EVAL ``list_lcm (leibniz_seg_arm 5 1 4)``; +val it = |- list_lcm (leibniz_seg_arm 5 1 4) = 60: thm +*) + +(* Theorem: leibniz_col_arm a b 0 = [] *) +(* Proof: + leibniz_col_arm a b 0 + = MAP (\x. leibniz (a - x) b) [0 ..< 0] by notation + = MAP (\x. leibniz (a - x) b) [] by listRangeLHI_def + = [] by MAP +*) +val leibniz_col_arm_0 = store_thm( + "leibniz_col_arm_0", + ``!a b. leibniz_col_arm a b 0 = []``, + rw[]); + +(* Theorem: leibniz_seg_arm a b 0 = [] *) +(* Proof: + leibniz_seg_arm a b 0 + = MAP (\x. leibniz a (b + x)) [0 ..< 0] by notation + = MAP (\x. leibniz a (b + x)) [] by listRangeLHI_def + = [] by MAP +*) +val leibniz_seg_arm_0 = store_thm( + "leibniz_seg_arm_0", + ``!a b. leibniz_seg_arm a b 0 = []``, + rw[]); + +(* Theorem: leibniz_col_arm a b 1 = [leibniz a b] *) +(* Proof: + leibniz_col_arm a b 1 + = MAP (\x. leibniz (a - x) b) [0 ..< 1] by notation + = MAP (\x. leibniz (a - x) b) [0] by listRangeLHI_def + = (\x. leibniz (a - x) b) 0 ::[] by MAP + = [leibniz a b] by function application +*) +val leibniz_col_arm_1 = store_thm( + "leibniz_col_arm_1", + ``!a b. leibniz_col_arm a b 1 = [leibniz a b]``, + rw[listRangeLHI_def]); + +(* Theorem: leibniz_seg_arm a b 1 = [leibniz a b] *) +(* Proof: + leibniz_seg_arm a b 1 + = MAP (\x. leibniz a (b + x)) [0 ..< 1] by notation + = MAP (\x. leibniz a (b + x)) [0] by listRangeLHI_def + = (\x. leibniz a (b + x)) 0 :: [] by MAP + = [leibniz a b] by function application +*) +val leibniz_seg_arm_1 = store_thm( + "leibniz_seg_arm_1", + ``!a b. leibniz_seg_arm a b 1 = [leibniz a b]``, + rw[listRangeLHI_def]); + +(* Theorem: LENGTH (leibniz_col_arm a b n) = n *) +(* Proof: + LENGTH (leibniz_col_arm a b n) + = LENGTH (MAP (\x. leibniz (a - x) b) [0 ..< n]) by notation + = LENGTH [0 ..< n] by LENGTH_MAP + = LENGTH (GENLIST (\i. i) n) by listRangeLHI_def + = m by LENGTH_GENLIST +*) +val leibniz_col_arm_len = store_thm( + "leibniz_col_arm_len", + ``!a b n. LENGTH (leibniz_col_arm a b n) = n``, + rw[]); + +(* Theorem: LENGTH (leibniz_seg_arm a b n) = n *) +(* Proof: + LENGTH (leibniz_seg_arm a b n) + = LENGTH (MAP (\x. leibniz a (b + x)) [0 ..< n]) by notation + = LENGTH [0 ..< n] by LENGTH_MAP + = LENGTH (GENLIST (\i. i) n) by listRangeLHI_def + = m by LENGTH_GENLIST +*) +val leibniz_seg_arm_len = store_thm( + "leibniz_seg_arm_len", + ``!a b n. LENGTH (leibniz_seg_arm a b n) = n``, + rw[]); + +(* Theorem: k < n ==> !a b. EL k (leibniz_col_arm a b n) = leibniz (a - k) b *) +(* Proof: + Note LENGTH [0 ..< n] = n by LENGTH_listRangeLHI + EL k (leibniz_col_arm a b n) + = EL k (MAP (\x. leibniz (a - x) b) [0 ..< n]) by notation + = (\x. leibniz (a - x) b) (EL k [0 ..< n]) by EL_MAP + = (\x. leibniz (a - x) b) k by EL_listRangeLHI + = leibniz (a - k) b +*) +val leibniz_col_arm_el = store_thm( + "leibniz_col_arm_el", + ``!n k. k < n ==> !a b. EL k (leibniz_col_arm a b n) = leibniz (a - k) b``, + rw[EL_MAP, EL_listRangeLHI]); + +(* Theorem: k < n ==> !a b. EL k (leibniz_seg_arm a b n) = leibniz a (b + k) *) +(* Proof: + Note LENGTH [0 ..< n] = n by LENGTH_listRangeLHI + EL k (leibniz_seg_arm a b n) + = EL k (MAP (\x. leibniz a (b + x)) [0 ..< n]) by notation + = (\x. leibniz a (b + x)) (EL k [0 ..< n]) by EL_MAP + = (\x. leibniz a (b + x)) k by EL_listRangeLHI + = leibniz a (b + k) +*) +val leibniz_seg_arm_el = store_thm( + "leibniz_seg_arm_el", + ``!n k. k < n ==> !a b. EL k (leibniz_seg_arm a b n) = leibniz a (b + k)``, + rw[EL_MAP, EL_listRangeLHI]); + +(* Theorem: TAKE 1 (leibniz_seg_arm a b (n + 1)) = [leibniz a b] *) +(* Proof: + Note LENGTH (leibniz_seg_arm a b (n + 1)) = n + 1 by leibniz_seg_arm_len + and 0 < n + 1 by ADD1, SUC_POS + TAKE 1 (leibniz_seg_arm a b (n + 1)) + = TAKE (SUC 0) (leibniz_seg_arm a b (n + 1)) by ONE + = SNOC (EL 0 (leibniz_seg_arm a b (n + 1))) [] by TAKE_SUC_BY_TAKE, TAKE_0 + = [EL 0 (leibniz_seg_arm a b (n + 1))] by SNOC_NIL + = leibniz a b by leibniz_seg_arm_el +*) +val leibniz_seg_arm_head = store_thm( + "leibniz_seg_arm_head", + ``!a b n. TAKE 1 (leibniz_seg_arm a b (n + 1)) = [leibniz a b]``, + metis_tac[leibniz_seg_arm_len, leibniz_seg_arm_el, + ONE, TAKE_SUC_BY_TAKE, TAKE_0, SNOC_NIL, DECIDE``!n. 0 < n + 1 /\ (n + 0 = n)``]); + +(* Theorem: leibniz_col_arm (a + 1) b (n + 1) = leibniz (a + 1) b :: leibniz_col_arm a b n *) +(* Proof: + Note (\x. leibniz (a + 1 - x) b) o SUC + = (\x. leibniz (a + 1 - (x + 1)) b) by FUN_EQ_THM + = (\x. leibniz (a - x) b) by arithmetic + + leibniz_col_arm (a + 1) b (n + 1) + = MAP (\x. leibniz (a + 1 - x) b) [0 ..< (n + 1)] by notation + = MAP (\x. leibniz (a + 1 - x) b) (0::[1 ..< (n+1)]) by listRangeLHI_CONS, 0 < n + 1 + = (\x. leibniz (a + 1 - x) b) 0 :: MAP (\x. leibniz (a + 1 - x) b) [1 ..< (n+1)] by MAP + = leibniz (a + 1) b :: MAP (\x. leibniz (a + 1 - x) b) [1 ..< (n+1)] by function application + = leibniz (a + 1) b :: MAP ((\x. leibniz (a + 1 - x) b) o SUC) [0 ..< n] by listRangeLHI_MAP_SUC + = leibniz (a + 1) b :: MAP (\x. leibniz (a - x) b) [0 ..< n] by above + = leibniz (a + 1) b :: leibniz_col_arm a b n by notation +*) +val leibniz_col_arm_cons = store_thm( + "leibniz_col_arm_cons", + ``!a b n. leibniz_col_arm (a + 1) b (n + 1) = leibniz (a + 1) b :: leibniz_col_arm a b n``, + rpt strip_tac >> + `!a x. a + 1 - SUC x + 1 = a - x + 1` by decide_tac >> + `!a x. a + 1 - SUC x = a - x` by decide_tac >> + `(\x. leibniz (a + 1 - x) b) o SUC = (\x. leibniz (a + 1 - (x + 1)) b)` by rw[FUN_EQ_THM] >> + `0 < n + 1` by decide_tac >> + `leibniz_col_arm (a + 1) b (n + 1) = MAP (\x. leibniz (a + 1 - x) b) (0::[1 ..< (n+1)])` by rw[listRangeLHI_CONS] >> + `_ = leibniz (a + 1) b :: MAP (\x. leibniz (a + 1 - x) b) [0+1 ..< (n+1)]` by rw[] >> + `_ = leibniz (a + 1) b :: MAP ((\x. leibniz (a + 1 - x) b) o SUC) [0 ..< n]` by rw[listRangeLHI_MAP_SUC] >> + `_ = leibniz (a + 1) b :: leibniz_col_arm a b n` by rw[] >> + rw[]); + +(* Theorem: k < n ==> !a b. + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) zigzag + TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) *) +(* Proof: + Since k <= n, k < n + 1, and k + 1 < n + 2. + Hence k < LENGTH (leibniz_seg_arm a b (n + 1)), + + Let x = TAKE k (leibniz_seg_arm a b (n + 1)) + and y = DROP (k + 1) (leibniz_seg_arm a b n) + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) + = TAKE (SUC k) (leibniz_seg_arm (a + 1) b (n + 1)) by ADD1 + = SNOC t.b x by TAKE_SUC_BY_TAKE, k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1)) + = x ++ [t.b] by SNOC_APPEND + TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) + = TAKE (SUC (SUC k)) (leibniz_seg_arm (a + 1) b (SUC n)) by ADD1 + = SNOC t.c (SNOC t.b x) by TAKE_SUC_BY_TAKE, SUC k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1)) + = x ++ [t.b; t.c] by SNOC_APPEND + DROP k (leibniz_seg_arm a b n) + = t.a :: y by DROP_BY_DROP_SUC, k < LENGTH (leibniz_seg_arm a b n) + = [t.a] ++ y by CONS_APPEND + Hence + Let p1 = TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) + = x ++ [t.b] ++ [t.a] ++ y + = x ++ [t.b; t.a] ++ y by APPEND + Let p2 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) + = x ++ [t.b; t.c] ++ y + Therefore p1 zigzag p2 by leibniz_zigzag_def +*) +val leibniz_seg_arm_zigzag_step = store_thm( + "leibniz_seg_arm_zigzag_step", + ``!n k. k < n ==> !a b. + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) zigzag + TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n)``, + rpt strip_tac >> + qabbrev_tac `x = TAKE k (leibniz_seg_arm (a + 1) b (n + 1))` >> + qabbrev_tac `y = DROP (k + 1) (leibniz_seg_arm a b n)` >> + qabbrev_tac `t = triplet a (b + k)` >> + `k < n + 1 /\ k + 1 < n + 1` by decide_tac >> + `EL k (leibniz_seg_arm a b n) = t.a` by rw[triplet_def, leibniz_seg_arm_el, Abbr`t`] >> + `EL k (leibniz_seg_arm (a + 1) b (n + 1)) = t.b` by rw[triplet_def, leibniz_seg_arm_el, Abbr`t`] >> + `EL (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) = t.c` by rw[triplet_def, leibniz_seg_arm_el, Abbr`t`] >> + `k < LENGTH (leibniz_seg_arm a b (n + 1))` by rw[leibniz_seg_arm_len] >> + `TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) = TAKE (SUC k) (leibniz_seg_arm (a + 1) b (n + 1))` by rw[ADD1] >> + `_ = SNOC t.b x` by rw[TAKE_SUC_BY_TAKE, Abbr`x`] >> + `_ = x ++ [t.b]` by rw[] >> + `SUC k < n + 1` by decide_tac >> + `SUC k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1))` by rw[leibniz_seg_arm_len] >> + `k < LENGTH (leibniz_seg_arm (a + 1) b (n + 1))` by decide_tac >> + `TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) = TAKE (SUC (SUC k)) (leibniz_seg_arm (a + 1) b (n + 1))` by rw[ADD1] >> + `_ = SNOC t.c (SNOC t.b x)` by metis_tac[TAKE_SUC_BY_TAKE, ADD1] >> + `_ = x ++ [t.b; t.c]` by rw[] >> + `DROP k (leibniz_seg_arm a b n) = [t.a] ++ y` by rw[DROP_BY_DROP_SUC, ADD1, Abbr`y`] >> + qabbrev_tac `p1 = TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n)` >> + qabbrev_tac `p2 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ y` >> + `p1 = x ++ [t.b; t.a] ++ y` by rw[Abbr`p1`, Abbr`x`, Abbr`y`] >> + `p2 = x ++ [t.b; t.c] ++ y` by rw[Abbr`p2`, Abbr`x`] >> + metis_tac[leibniz_zigzag_def]); + +(* Theorem: k < n + 1 ==> !a b. + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle + leibniz_seg_arm (a + 1) b (n + 1) *) +(* Proof: + By induction on the difference: n - k. + Base: k = n ==> TAKE (k + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle + leibniz_seg_arm a b (n + 1) + Note LENGTH (leibniz_seg_arm (a + 1) b (n + 1)) = n + 1 by leibniz_seg_arm_len + and LENGTH (leibniz_seg_arm a b n) = n by leibniz_seg_arm_len + TAKE (k + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) + = TAKE (n + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP n (leibniz_seg_arm a b n) by k = n + = leibniz_seg_arm a b n ++ [] by TAKE_LENGTH_ID, DROP_LENGTH_NIL + = leibniz_seg_arm a b n by APPEND_NIL + Hence they wriggle to each other by RTC_REFL + Step: k < n + 1 ==> TAKE (k + 1) (leibniz_seg_arm a b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle + leibniz_seg_arm a b (n + 1) + Let p1 = leibniz_seg_arm (a + 1) b (n + 1) + p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_seg_arm a b n) + p3 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n) + Then p2 zigzag p3 by leibniz_seg_arm_zigzag_step + and p3 wriggle p1 by induction hypothesis + Hence p2 wriggle p1 by RTC_RULES +*) +val leibniz_seg_arm_wriggle_step = store_thm( + "leibniz_seg_arm_wriggle_step", + ``!n k. k < n + 1 ==> !a b. + TAKE (k + 1) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP k (leibniz_seg_arm a b n) wriggle + leibniz_seg_arm (a + 1) b (n + 1)``, + Induct_on `n - k` >| [ + rpt strip_tac >> + `k = n` by decide_tac >> + metis_tac[leibniz_seg_arm_len, TAKE_LENGTH_ID, DROP_LENGTH_NIL, APPEND_NIL, RTC_REFL], + rpt strip_tac >> + qabbrev_tac `p1 = leibniz_seg_arm (a + 1) b (n + 1)` >> + qabbrev_tac `p2 = TAKE (k + 1) p1 ++ DROP k (leibniz_seg_arm a b n)` >> + qabbrev_tac `p3 = TAKE (k + 2) (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP (k + 1) (leibniz_seg_arm a b n)` >> + `p2 zigzag p3` by rw[leibniz_seg_arm_zigzag_step, Abbr`p1`, Abbr`p2`, Abbr`p3`] >> + `v = n - (k + 1)` by decide_tac >> + `k + 1 < n + 1` by decide_tac >> + `k + 1 + 1 = k + 2` by decide_tac >> + metis_tac[RTC_RULES] + ]); + +(* Theorem: ([leibniz (a + 1) b] ++ leibniz_seg_arm a b n) wriggle leibniz_seg_arm (a + 1) b (n + 1) *) +(* Proof: + Apply > leibniz_seg_arm_wriggle_step |> SPEC ``n:num`` |> SPEC ``0`` |> SIMP_RULE std_ss[DROP_0]; + val it = + |- 0 < n + 1 ==> !a b. + TAKE 1 (leibniz_seg_arm (a + 1) b (n + 1)) ++ leibniz_seg_arm a b n wriggle + leibniz_seg_arm (a + 1) b (n + 1): + thm + + Note 0 < n + 1 by ADD1, SUC_POS + [leibniz (a + 1) b] ++ leibniz_seg_arm a b n + = TAKE 1 (leibniz_seg_arm (a + 1) b (n + 1)) ++ leibniz_seg_arm a b n by leibniz_seg_arm_head + = TAKE 1 (leibniz_seg_arm (a + 1) b (n + 1)) ++ DROP 0 (leibniz_seg_arm a b n) by DROP_0 + wriggle leibniz_seg_arm (a + 1) b (n + 1) by leibniz_seg_arm_wriggle_step, put k = 0 +*) +val leibniz_seg_arm_wriggle_row_arm = store_thm( + "leibniz_seg_arm_wriggle_row_arm", + ``!a b n. ([leibniz (a + 1) b] ++ leibniz_seg_arm a b n) wriggle leibniz_seg_arm (a + 1) b (n + 1)``, + rpt strip_tac >> + `0 < n + 1 /\ (0 + 1 = 1)` by decide_tac >> + metis_tac[leibniz_seg_arm_head, leibniz_seg_arm_wriggle_step, DROP_0]); + +(* Theorem: b <= a /\ n <= a + 1 - b ==> (leibniz_col_arm a b n) wriggle (leibniz_seg_arm a b n) *) +(* Proof: + By induction on n. + Base: leibniz_col_arm a b 0 wriggle leibniz_seg_arm a b 0 + Since leibniz_col_arm a b 0 = [] by leibniz_col_arm_0 + and leibniz_seg_arm a b 0 = [] by leibniz_seg_arm_0 + Hence leibniz_col_arm a b 0 wriggle leibniz_seg_arm a b 0 by leibniz_wriggle_refl + Step: !a b. leibniz_col_arm a b n wriggle leibniz_seg_arm a b n ==> + leibniz_col_arm a b (SUC n) wriggle leibniz_seg_arm a b (SUC n) + Induct_on a. + Base: b <= 0 /\ SUC n <= 0 + 1 - b ==> leibniz_col_arm 0 b (SUC n) wriggle leibniz_seg_arm 0 b (SUC n) + Note SUC n <= 1 - b ==> n = 0, since 0 <= b. + leibniz_col_arm 0 b (SUC 0) + = leibniz_col_arm 0 b 1 by ONE + = [leibniz 0 b] by leibniz_col_arm_1 + leibniz_seg_arm 0 b (SUC 0) + = leibniz_seg_arm 0 b 1 by ONE + = [leibniz 0 b] by leibniz_seg_arm_1 + Hence leibniz_col_arm 0 b 1 wriggle + leibniz_seg_arm 0 b 1 by leibniz_wriggle_refl + Step: b <= SUC a /\ SUC n <= SUC a + 1 - b ==> leibniz_col_arm (SUC a) b (SUC n) wriggle leibniz_seg_arm (SUC a) b (SUC n) + Note n <= a + 1 - b + If a + 1 = b, + Then n = 0, + leibniz_col_arm (SUC a) b (SUC 0) + = leibniz_col_arm (SUC a) b 1 by ONE + = [leibniz (SUC a) b] by leibniz_col_arm_1 + = leibniz_seg_arm (SUC a) b 1 by leibniz_seg_arm_1 + = leibniz_seg_arm (SUC a) b (SUC 0) by ONE + Hence leibniz_col_arm (SUC a) b 1 wriggle + leibniz_seg_arm (SUC a) b 1 by leibniz_wriggle_refl + If a + 1 <> b, + Then b <= a, and induction hypothesis applies. + Let x = leibniz (a + 1) b. + Then leibniz_col_arm (a + 1) b (n + 1) + = [x] ++ (leibniz_col_arm a b n) by leibniz_col_arm_cons + Since leibniz_col_arm a b n + wriggle leibniz_seg_arm a b n by induction hypothesis + so ([x] ++ (leibniz_col_arm a b n)) wriggle + ([x] ++ (leibniz_seg_arm a b n)) by leibniz_wriggle_tail + and ([x] ++ (leibniz_seg_arm a b n)) wriggle + (leibniz_seg_arm (a + 1) b (n + 1)) by leibniz_seg_arm_wriggle_row_arm + Hence leibniz_col_arm a b (SUC n) wriggle + leibniz_seg_arm a b (SUC n) by leibniz_wriggle_trans, ADD1 +*) +val leibniz_col_arm_wriggle_row_arm = store_thm( + "leibniz_col_arm_wriggle_row_arm", + ``!a b n. b <= a /\ n <= a + 1 - b ==> (leibniz_col_arm a b n) wriggle (leibniz_seg_arm a b n)``, + Induct_on `n` >- + rw[leibniz_col_arm_0, leibniz_seg_arm_0] >> + rpt strip_tac >> + Induct_on `a` >| [ + rpt strip_tac >> + `n = 0` by decide_tac >> + metis_tac[leibniz_col_arm_1, leibniz_seg_arm_1, ONE, leibniz_wriggle_refl], + rpt strip_tac >> + `n <= a + 1 - b` by decide_tac >> + Cases_on `a + 1 = b` >| [ + `n = 0` by decide_tac >> + metis_tac[leibniz_col_arm_1, leibniz_seg_arm_1, ONE, leibniz_wriggle_refl], + `b <= a` by decide_tac >> + qabbrev_tac `x = leibniz (a + 1) b` >> + `leibniz_col_arm (a + 1) b (n + 1) = [x] ++ (leibniz_col_arm a b n)` by rw[leibniz_col_arm_cons, Abbr`x`] >> + `([x] ++ (leibniz_col_arm a b n)) wriggle ([x] ++ (leibniz_seg_arm a b n))` by rw[leibniz_wriggle_tail] >> + `([x] ++ (leibniz_seg_arm a b n)) wriggle (leibniz_seg_arm (a + 1) b (n + 1))` by rw[leibniz_seg_arm_wriggle_row_arm, Abbr`x`] >> + metis_tac[leibniz_wriggle_trans, ADD1] + ] + ]); + +(* Theorem: b <= a /\ n <= a + 1 - b ==> (list_lcm (leibniz_col_arm a b n) = list_lcm (leibniz_seg_arm a b n)) *) +(* Proof: + Since (leibniz_col_arm a b n) wriggle (leibniz_seg_arm a b n) by leibniz_col_arm_wriggle_row_arm + the result follows by list_lcm_wriggle +*) +val leibniz_lcm_invariance = store_thm( + "leibniz_lcm_invariance", + ``!a b n. b <= a /\ n <= a + 1 - b ==> (list_lcm (leibniz_col_arm a b n) = list_lcm (leibniz_seg_arm a b n))``, + rw[leibniz_col_arm_wriggle_row_arm, list_lcm_wriggle]); + +(* This is a milestone theorem. *) + +(* This is used to give another proof of leibniz_up_wriggle_horizontal *) + +(* Theorem: leibniz_col_arm n 0 (n + 1) = leibniz_up n *) +(* Proof: + leibniz_col_arm n 0 (n + 1) + = MAP (\x. leibniz (n - x) 0) [0 ..< (n + 1)] by notation + = MAP (\x. leibniz (n - x) 0) [0 .. n] by listRangeLHI_to_INC + = MAP ((\x. leibniz x 0) o (\x. n - x)) [0 .. n] by function composition + = REVERSE (MAP (\x. leibniz x 0) [0 .. n]) by listRangeINC_REVERSE_MAP + = REVERSE (MAP (\x. x + 1) [0 .. n]) by leibniz_n_0 + = REVERSE (MAP SUC [0 .. n]) by ADD1 + = REVERSE (MAP (I o SUC) [0 .. n]) by I_THM + = REVERSE [1 .. (n+1)] by listRangeINC_MAP_SUC + = REVERSE (leibniz_vertical n) by notation + = leibniz_up n by notation +*) +val leibniz_col_arm_n_0 = store_thm( + "leibniz_col_arm_n_0", + ``!n. leibniz_col_arm n 0 (n + 1) = leibniz_up n``, + rpt strip_tac >> + `(\x. x + 1) = SUC` by rw[FUN_EQ_THM] >> + `(\x. leibniz x 0) o (\x. n - x + 0) = (\x. leibniz (n - x) 0)` by rw[FUN_EQ_THM] >> + `leibniz_col_arm n 0 (n + 1) = MAP (\x. leibniz (n - x) 0) [0 .. n]` by rw[listRangeLHI_to_INC] >> + `_ = MAP ((\x. leibniz x 0) o (\x. n - x + 0)) [0 .. n]` by rw[] >> + `_ = REVERSE (MAP (\x. leibniz x 0) [0 .. n])` by rw[listRangeINC_REVERSE_MAP] >> + `_ = REVERSE (MAP (\x. x + 1) [0 .. n])` by rw[leibniz_n_0] >> + `_ = REVERSE (MAP SUC [0 .. n])` by rw[ADD1] >> + `_ = REVERSE (MAP (I o SUC) [0 .. n])` by rw[] >> + `_ = REVERSE [1 .. (n+1)]` by rw[GSYM listRangeINC_MAP_SUC] >> + rw[]); + +(* Theorem: leibniz_seg_arm n 0 (n + 1) = leibniz_horizontal n *) +(* Proof: + leibniz_seg_arm n 0 (n + 1) + = MAP (\x. leibniz n x) [0 ..< (n + 1)] by notation + = MAP (\x. leibniz n x) [0 .. n] by listRangeLHI_to_INC + = MAP (leibniz n) [0 .. n] by FUN_EQ_THM + = MAP (leibniz n) (GENLIST (\i. i) (n + 1)) by listRangeINC_def + = GENLIST ((leibniz n) o I) (n + 1) by MAP_GENLIST + = GENLIST (leibniz n) (n + 1) by I_THM + = leibniz_horizontal n by notation +*) +val leibniz_seg_arm_n_0 = store_thm( + "leibniz_seg_arm_n_0", + ``!n. leibniz_seg_arm n 0 (n + 1) = leibniz_horizontal n``, + rpt strip_tac >> + `(\x. x) = I` by rw[FUN_EQ_THM] >> + `(\x. leibniz n x) = leibniz n` by rw[FUN_EQ_THM] >> + `leibniz_seg_arm n 0 (n + 1) = MAP (leibniz n) [0 .. n]` by rw_tac std_ss[listRangeLHI_to_INC] >> + `_ = MAP (leibniz n) (GENLIST (\i. i) (n + 1))` by rw[listRangeINC_def] >> + `_ = MAP (leibniz n) (GENLIST I (n + 1))` by metis_tac[] >> + `_ = GENLIST ((leibniz n) o I) (n + 1)` by rw[MAP_GENLIST] >> + `_ = GENLIST (leibniz n) (n + 1)` by rw[] >> + rw[]); + +(* Theorem: (leibniz_up n) wriggle (leibniz_horizontal n) *) +(* Proof: + Note 0 <= n /\ n + 1 <= n + 1 - 0, so leibniz_col_arm_wriggle_row_arm applies. + leibniz_up n + = leibniz_col_arm n 0 (n + 1) by leibniz_col_arm_n_0 + wriggle leibniz_seg_arm n 0 (n + 1) by leibniz_col_arm_wriggle_row_arm + = leibniz_horizontal n by leibniz_seg_arm_n_0 +*) +val leibniz_up_wriggle_horizontal_alt = store_thm( + "leibniz_up_wriggle_horizontal_alt", + ``!n. (leibniz_up n) wriggle (leibniz_horizontal n)``, + rpt strip_tac >> + `0 <= n /\ n + 1 <= n + 1 - 0` by decide_tac >> + metis_tac[leibniz_col_arm_wriggle_row_arm, leibniz_col_arm_n_0, leibniz_seg_arm_n_0]); + +(* Theorem: list_lcm (leibniz_up n) = list_lcm (leibniz_horizontal n) *) +(* Proof: by leibniz_up_wriggle_horizontal_alt, list_lcm_wriggle *) +val leibniz_up_lcm_eq_horizontal_lcm = store_thm( + "leibniz_up_lcm_eq_horizontal_lcm", + ``!n. list_lcm (leibniz_up n) = list_lcm (leibniz_horizontal n)``, + rw[leibniz_up_wriggle_horizontal_alt, list_lcm_wriggle]); + +(* This is another proof of the milestone theorem. *) + +(* ------------------------------------------------------------------------- *) +(* Set GCD as Big Operator *) +(* ------------------------------------------------------------------------- *) + +(* Big Operators: +SUM_IMAGE_DEF |- !f s. SIGMA f s = ITSET (\e acc. f e + acc) s 0: thm +PROD_IMAGE_DEF |- !f s. PI f s = ITSET (\e acc. f e * acc) s 1: thm +*) + +(* Define big_gcd for a set *) +val big_gcd_def = Define` + big_gcd s = ITSET gcd s 0 +`; + +(* Theorem: big_gcd {} = 0 *) +(* Proof: + big_gcd {} + = ITSET gcd {} 0 by big_gcd_def + = 0 by ITSET_EMPTY +*) +val big_gcd_empty = store_thm( + "big_gcd_empty", + ``big_gcd {} = 0``, + rw[big_gcd_def, ITSET_EMPTY]); + +(* Theorem: big_gcd {x} = x *) +(* Proof: + big_gcd {x} + = ITSET gcd {x} 0 by big_gcd_def + = gcd x 0 by ITSET_SING + = x by GCD_0R +*) +val big_gcd_sing = store_thm( + "big_gcd_sing", + ``!x. big_gcd {x} = x``, + rw[big_gcd_def, ITSET_SING]); + +(* Theorem: FINITE s /\ x NOTIN s ==> (big_gcd (x INSERT s) = gcd x (big_gcd s)) *) +(* Proof: + Note big_gcd s = ITSET gcd s 0 by big_lcm_def + Since !x y z. gcd x (gcd y z) = gcd y (gcd x z) by GCD_ASSOC_COMM + The result follows by ITSET_REDUCTION +*) +val big_gcd_reduction = store_thm( + "big_gcd_reduction", + ``!s x. FINITE s /\ x NOTIN s ==> (big_gcd (x INSERT s) = gcd x (big_gcd s))``, + rw[big_gcd_def, ITSET_REDUCTION, GCD_ASSOC_COMM]); + +(* Theorem: FINITE s ==> !x. x IN s ==> (big_gcd s) divides x *) +(* Proof: + By finite induction on s. + Base: x IN {} ==> big_gcd {} divides x + True since x IN {} = F by MEMBER_NOT_EMPTY + Step: !x. x IN s ==> big_gcd s divides x ==> + e NOTIN s /\ x IN (e INSERT s) ==> big_gcd (e INSERT s) divides x + Since e NOTIN s, + so big_gcd (e INSERT s) = gcd e (big_gcd s) by big_gcd_reduction + By IN_INSERT, + If x = e, + to show: gcd e (big_gcd s) divides e, true by GCD_IS_GREATEST_COMMON_DIVISOR + If x <> e, x IN s, + to show gcd e (big_gcd s) divides x, + Since (big_gcd s) divides x by induction hypothesis, x IN s + and (big_gcd s) divides gcd e (big_gcd s) by GCD_IS_GREATEST_COMMON_DIVISOR + so gcd e (big_gcd s) divides x by DIVIDES_TRANS +*) +val big_gcd_is_common_divisor = store_thm( + "big_gcd_is_common_divisor", + ``!s. FINITE s ==> !x. x IN s ==> (big_gcd s) divides x``, + Induct_on `FINITE` >> + rpt strip_tac >- + metis_tac[MEMBER_NOT_EMPTY] >> + metis_tac[big_gcd_reduction, IN_INSERT, GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_TRANS]); + +(* Theorem: FINITE s ==> !m. (!x. x IN s ==> m divides x) ==> m divides (big_gcd s) *) +(* Proof: + By finite induction on s. + Base: m divides big_gcd {} + Since big_gcd {} = 0 by big_gcd_empty + Hence true by ALL_DIVIDES_0 + Step: !m. (!x. x IN s ==> m divides x) ==> m divides big_gcd s ==> + e NOTIN s /\ !x. x IN e INSERT s ==> m divides x ==> m divides big_gcd (e INSERT s) + Note x IN e INSERT s ==> x = e \/ x IN s by IN_INSERT + Put x = e, then m divides e by x divides m, x = e + Put x IN s, then m divides big_gcd s by induction hypothesis + Therefore, m divides gcd e (big_gcd s) by GCD_IS_GREATEST_COMMON_DIVISOR + or m divides big_gcd (e INSERT s) by big_gcd_reduction, e NOTIN s +*) +val big_gcd_is_greatest_common_divisor = store_thm( + "big_gcd_is_greatest_common_divisor", + ``!s. FINITE s ==> !m. (!x. x IN s ==> m divides x) ==> m divides (big_gcd s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[big_gcd_empty] >> + metis_tac[big_gcd_reduction, GCD_IS_GREATEST_COMMON_DIVISOR, IN_INSERT]); + +(* Theorem: FINITE s ==> !x. big_gcd (x INSERT s) = gcd x (big_gcd s) *) +(* Proof: + If x IN s, + Then (big_gcd s) divides x by big_gcd_is_common_divisor + gcd x (big_gcd s) + = gcd (big_gcd s) x by GCD_SYM + = big_gcd s by divides_iff_gcd_fix + = big_gcd (x INSERT s) by ABSORPTION + If x NOTIN s, result is true by big_gcd_reduction +*) +val big_gcd_insert = store_thm( + "big_gcd_insert", + ``!s. FINITE s ==> !x. big_gcd (x INSERT s) = gcd x (big_gcd s)``, + rpt strip_tac >> + Cases_on `x IN s` >- + metis_tac[big_gcd_is_common_divisor, divides_iff_gcd_fix, ABSORPTION, GCD_SYM] >> + rw[big_gcd_reduction]); + +(* Theorem: big_gcd {x; y} = gcd x y *) +(* Proof: + big_gcd {x; y} + = big_gcd (x INSERT y) by notation + = gcd x (big_gcd {y}) by big_gcd_insert + = gcd x (big_gcd {y INSERT {}}) by notation + = gcd x (gcd y (big_gcd {})) by big_gcd_insert + = gcd x (gcd y 0) by big_gcd_empty + = gcd x y by gcd_0R +*) +val big_gcd_two = store_thm( + "big_gcd_two", + ``!x y. big_gcd {x; y} = gcd x y``, + rw[big_gcd_insert, big_gcd_empty]); + +(* Theorem: FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s *) +(* Proof: + By finite induction on s. + Base: {} <> {} /\ !x. x IN {} ==> 0 < x ==> 0 < big_gcd {} + True since {} <> {} = F + Step: s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s ==> + e NOTIN s /\ e INSERT s <> {} /\ !x. x IN e INSERT s ==> 0 < x ==> 0 < big_gcd (e INSERT s) + Note 0 < e /\ !x. x IN s ==> 0 < x by IN_INSERT + If s = {}, + big_gcd (e INSERT {}) + = big_gcd {e} by IN_INSERT + = e > 0 by big_gcd_sing + If s <> {}, + so 0 < big_gcd s by induction hypothesis + ==> 0 < gcd e (big_gcd s) by GCD_EQ_0 + or 0 < big_gcd (e INSERT s) by big_gcd_insert +*) +val big_gcd_positive = store_thm( + "big_gcd_positive", + ``!s. FINITE s /\ s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s``, + `!s. FINITE s ==> s <> {} /\ (!x. x IN s ==> 0 < x) ==> 0 < big_gcd s` suffices_by rw[] >> + Induct_on `FINITE` >> + rpt strip_tac >- + rw[] >> + `0 < e /\ (!x. x IN s ==> 0 < x)` by rw[] >> + Cases_on `s = {}` >- + rw[big_gcd_sing] >> + metis_tac[big_gcd_insert, GCD_EQ_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: FINITE s /\ s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s *) +(* Proof: + By finite induction on s. + Base: {} <> {} ==> ..., must be true. + Step: s <> {} ==> !!k. big_gcd (IMAGE ($* k) s) = k * big_gcd s ==> + e NOTIN s ==> big_gcd (IMAGE ($* k) (e INSERT s)) = k * big_gcd (e INSERT s) + If s = {}, + big_gcd (IMAGE ($* k) (e INSERT {})) + = big_gcd (IMAGE ($* k) {e}) by IN_INSERT, s = {} + = big_gcd {k * e} by IMAGE_SING + = k * e by big_gcd_sing + = k * big_gcd {e} by big_gcd_sing + = k * big_gcd (e INSERT {}) by IN_INSERT, s = {} + If s <> {}, + big_gcd (IMAGE ($* k) (e INSERT s)) + = big_gcd ((k * e) INSERT (IMAGE ($* k) s)) by IMAGE_INSERT + = gcd (k * e) (big_gcd (IMAGE ($* k) s)) by big_gcd_insert + = gcd (k * e) (k * big_gcd s) by induction hypothesis + = k * gcd e (big_gcd s) by GCD_COMMON_FACTOR + = k * big_gcd (e INSERT s) by big_gcd_insert +*) +val big_gcd_map_times = store_thm( + "big_gcd_map_times", + ``!s. FINITE s /\ s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s``, + `!s. FINITE s ==> s <> {} ==> !k. big_gcd (IMAGE ($* k) s) = k * big_gcd s` suffices_by rw[] >> + Induct_on `FINITE` >> + rpt strip_tac >- + rw[] >> + Cases_on `s = {}` >- + rw[big_gcd_sing] >> + `big_gcd (IMAGE ($* k) (e INSERT s)) = gcd (k * e) (k * big_gcd s)` by rw[big_gcd_insert] >> + `_ = k * gcd e (big_gcd s)` by rw[GCD_COMMON_FACTOR] >> + `_ = k * big_gcd (e INSERT s)` by rw[big_gcd_insert] >> + rw[]); + +(* ------------------------------------------------------------------------- *) +(* Set LCM as Big Operator *) +(* ------------------------------------------------------------------------- *) + +(* big_lcm s = ITSET (\e x. lcm e x) s 1 = ITSET lcm s 1, of course! *) +val big_lcm_def = Define` + big_lcm s = ITSET lcm s 1 +`; + +(* Theorem: big_lcm {} = 1 *) +(* Proof: + big_lcm {} + = ITSET lcm {} 1 by big_lcm_def + = 1 by ITSET_EMPTY +*) +val big_lcm_empty = store_thm( + "big_lcm_empty", + ``big_lcm {} = 1``, + rw[big_lcm_def, ITSET_EMPTY]); + +(* Theorem: big_lcm {x} = x *) +(* Proof: + big_lcm {x} + = ITSET lcm {x} 1 by big_lcm_def + = lcm x 1 by ITSET_SING + = x by LCM_1 +*) +val big_lcm_sing = store_thm( + "big_lcm_sing", + ``!x. big_lcm {x} = x``, + rw[big_lcm_def, ITSET_SING]); + +(* Theorem: FINITE s /\ x NOTIN s ==> (big_lcm (x INSERT s) = lcm x (big_lcm s)) *) +(* Proof: + Note big_lcm s = ITSET lcm s 1 by big_lcm_def + Since !x y z. lcm x (lcm y z) = lcm y (lcm x z) by LCM_ASSOC_COMM + The result follows by ITSET_REDUCTION +*) +val big_lcm_reduction = store_thm( + "big_lcm_reduction", + ``!s x. FINITE s /\ x NOTIN s ==> (big_lcm (x INSERT s) = lcm x (big_lcm s))``, + rw[big_lcm_def, ITSET_REDUCTION, LCM_ASSOC_COMM]); + +(* Theorem: FINITE s ==> !x. x IN s ==> x divides (big_lcm s) *) +(* Proof: + By finite induction on s. + Base: x IN {} ==> x divides big_lcm {} + True since x IN {} = F by MEMBER_NOT_EMPTY + Step: !x. x IN s ==> x divides big_lcm s ==> + e NOTIN s /\ x IN (e INSERT s) ==> x divides big_lcm (e INSERT s) + Since e NOTIN s, + so big_lcm (e INSERT s) = lcm e (big_lcm s) by big_lcm_reduction + By IN_INSERT, + If x = e, + to show: e divides lcm e (big_lcm s), true by LCM_DIVISORS + If x <> e, x IN s, + to show x divides lcm e (big_lcm s), + Since x divides (big_lcm s) by induction hypothesis, x IN s + and (big_lcm s) divides lcm e (big_lcm s) by LCM_DIVISORS + so x divides lcm e (big_lcm s) by DIVIDES_TRANS +*) +val big_lcm_is_common_multiple = store_thm( + "big_lcm_is_common_multiple", + ``!s. FINITE s ==> !x. x IN s ==> x divides (big_lcm s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + metis_tac[MEMBER_NOT_EMPTY] >> + metis_tac[big_lcm_reduction, IN_INSERT, LCM_DIVISORS, DIVIDES_TRANS]); + +(* Theorem: FINITE s ==> !m. (!x. x IN s ==> x divides m) ==> (big_lcm s) divides m *) +(* Proof: + By finite induction on s. + Base: big_lcm {} divides m + Since big_lcm {} = 1 by big_lcm_empty + Hence true by ONE_DIVIDES_ALL + Step: !m. (!x. x IN s ==> x divides m) ==> big_lcm s divides m ==> + e NOTIN s /\ !x. x IN e INSERT s ==> x divides m ==> big_lcm (e INSERT s) divides m + Note x IN e INSERT s ==> x = e \/ x IN s by IN_INSERT + Put x = e, then e divides m by x divides m, x = e + Put x IN s, then big_lcm s divides m by induction hypothesis + Therefore, lcm e (big_lcm s) divides m by LCM_IS_LEAST_COMMON_MULTIPLE + or big_lcm (e INSERT s) divides m by big_lcm_reduction, e NOTIN s +*) +val big_lcm_is_least_common_multiple = store_thm( + "big_lcm_is_least_common_multiple", + ``!s. FINITE s ==> !m. (!x. x IN s ==> x divides m) ==> (big_lcm s) divides m``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[big_lcm_empty] >> + metis_tac[big_lcm_reduction, LCM_IS_LEAST_COMMON_MULTIPLE, IN_INSERT]); + +(* Theorem: FINITE s ==> !x. big_lcm (x INSERT s) = lcm x (big_lcm s) *) +(* Proof: + If x IN s, + Then x divides (big_lcm s) by big_lcm_is_common_multiple + lcm x (big_lcm s) + = big_lcm s by divides_iff_lcm_fix + = big_lcm (x INSERT s) by ABSORPTION + If x NOTIN s, result is true by big_lcm_reduction +*) +val big_lcm_insert = store_thm( + "big_lcm_insert", + ``!s. FINITE s ==> !x. big_lcm (x INSERT s) = lcm x (big_lcm s)``, + rpt strip_tac >> + Cases_on `x IN s` >- + metis_tac[big_lcm_is_common_multiple, divides_iff_lcm_fix, ABSORPTION] >> + rw[big_lcm_reduction]); + +(* Theorem: big_lcm {x; y} = lcm x y *) +(* Proof: + big_lcm {x; y} + = big_lcm (x INSERT y) by notation + = lcm x (big_lcm {y}) by big_lcm_insert + = lcm x (big_lcm {y INSERT {}}) by notation + = lcm x (lcm y (big_lcm {})) by big_lcm_insert + = lcm x (lcm y 1) by big_lcm_empty + = lcm x y by LCM_1 +*) +val big_lcm_two = store_thm( + "big_lcm_two", + ``!x y. big_lcm {x; y} = lcm x y``, + rw[big_lcm_insert, big_lcm_empty]); + +(* Theorem: FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s *) +(* Proof: + By finite induction on s. + Base: !x. x IN {} ==> 0 < x ==> 0 < big_lcm {} + big_lcm {} = 1 > 0 by big_lcm_empty + Step: (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s ==> + e NOTIN s /\ !x. x IN e INSERT s ==> 0 < x ==> 0 < big_lcm (e INSERT s) + Note 0 < e /\ !x. x IN s ==> 0 < x by IN_INSERT + so 0 < big_lcm s by induction hypothesis + ==> 0 < lcm e (big_lcm s) by LCM_EQ_0 + or 0 < big_lcm (e INSERT s) by big_lcm_insert +*) +val big_lcm_positive = store_thm( + "big_lcm_positive", + ``!s. FINITE s ==> (!x. x IN s ==> 0 < x) ==> 0 < big_lcm s``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[big_lcm_empty] >> + `0 < e /\ (!x. x IN s ==> 0 < x)` by rw[] >> + metis_tac[big_lcm_insert, LCM_EQ_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: FINITE s /\ s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s *) +(* Proof: + By finite induction on s. + Base: {} <> {} ==> ..., must be true. + Step: s <> {} ==> !!k. big_lcm (IMAGE ($* k) s) = k * big_lcm s ==> + e NOTIN s ==> big_lcm (IMAGE ($* k) (e INSERT s)) = k * big_lcm (e INSERT s) + If s = {}, + big_lcm (IMAGE ($* k) (e INSERT {})) + = big_lcm (IMAGE ($* k) {e}) by IN_INSERT, s = {} + = big_lcm {k * e} by IMAGE_SING + = k * e by big_lcm_sing + = k * big_lcm {e} by big_lcm_sing + = k * big_lcm (e INSERT {}) by IN_INSERT, s = {} + If s <> {}, + big_lcm (IMAGE ($* k) (e INSERT s)) + = big_lcm ((k * e) INSERT (IMAGE ($* k) s)) by IMAGE_INSERT + = lcm (k * e) (big_lcm (IMAGE ($* k) s)) by big_lcm_insert + = lcm (k * e) (k * big_lcm s) by induction hypothesis + = k * lcm e (big_lcm s) by LCM_COMMON_FACTOR + = k * big_lcm (e INSERT s) by big_lcm_insert +*) +val big_lcm_map_times = store_thm( + "big_lcm_map_times", + ``!s. FINITE s /\ s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s``, + `!s. FINITE s ==> s <> {} ==> !k. big_lcm (IMAGE ($* k) s) = k * big_lcm s` suffices_by rw[] >> + Induct_on `FINITE` >> + rpt strip_tac >- + rw[] >> + Cases_on `s = {}` >- + rw[big_lcm_sing] >> + `big_lcm (IMAGE ($* k) (e INSERT s)) = lcm (k * e) (k * big_lcm s)` by rw[big_lcm_insert] >> + `_ = k * lcm e (big_lcm s)` by rw[LCM_COMMON_FACTOR] >> + `_ = k * big_lcm (e INSERT s)` by rw[big_lcm_insert] >> + rw[]); + +(* ------------------------------------------------------------------------- *) +(* LCM Lower bound using big LCM *) +(* ------------------------------------------------------------------------- *) + +(* Laurent's leib.v and leib.html + +Lemma leibn_lcm_swap m n : + lcmn 'L(m.+1, n) 'L(m, n) = lcmn 'L(m.+1, n) 'L(m.+1, n.+1). +Proof. +rewrite ![lcmn 'L(m.+1, n) _]lcmnC. +by apply/lcmn_swap/leibnS. +Qed. + +Notation "\lcm_ ( i < n ) F" := + (\big[lcmn/1%N]_(i < n ) F%N) + (at level 41, F at level 41, i, n at level 50, + format "'[' \lcm_ ( i < n ) '/ ' F ']'") : nat_scope. + +Canonical Structure lcmn_moid : Monoid.law 1 := + Monoid.Law lcmnA lcm1n lcmn1. +Canonical lcmn_comoid := Monoid.ComLaw lcmnC. + +Lemma lieb_line n i k : lcmn 'L(n.+1, i) (\lcm_(j < k) 'L(n, i + j)) = + \lcm_(j < k.+1) 'L(n.+1, i + j). +Proof. +elim: k i => [i|k1 IH i]. + by rewrite big_ord_recr !big_ord0 /= lcmn1 lcm1n addn0. +rewrite big_ord_recl /= addn0. +rewrite lcmnA leibn_lcm_swap. +rewrite (eq_bigr (fun j : 'I_k1 => 'L(n, i.+1 + j))). +rewrite -lcmnA. +rewrite IH. +rewrite [RHS]big_ord_recl. +rewrite addn0; congr (lcmn _ _). +by apply: eq_bigr => j _; rewrite addnS. +move=> j _. +by rewrite addnS. +Qed. + +Lemma leib_corner n : \lcm_(i < n.+1) 'L(i, 0) = \lcm_(i < n.+1) 'L(n, i). +Proof. +elim: n => [|n IH]; first by rewrite !big_ord_recr !big_ord0 /=. +rewrite big_ord_recr /= IH lcmnC. +rewrite (eq_bigr (fun i : 'I_n.+1 => 'L(n, 0 + i))) //. +by rewrite lieb_line. +Qed. + +Lemma main_result n : 2^n.-1 <= \lcm_(i < n) i.+1. +Proof. +case: n => [|n /=]; first by rewrite big_ord0. +have <-: \lcm_(i < n.+1) 'L(i, 0) = \lcm_(i < n.+1) i.+1. + by apply: eq_bigr => i _; rewrite leibn0. +rewrite leib_corner. +have -> : forall j, \lcm_(i < j.+1) 'L(n, i) = n.+1 * \lcm_(i < j.+1) 'C(n, i). + elim=> [|j IH]; first by rewrite !big_ord_recr !big_ord0 /= !lcm1n. + by rewrite big_ord_recr [in RHS]big_ord_recr /= IH muln_lcmr. +rewrite (expnDn 1 1) /= (eq_bigr (fun i : 'I_n.+1 => 'C(n, i))) => + [|i _]; last by rewrite !exp1n !muln1. +have <- : forall n m, \sum_(i < n) m = n * m. + by move=> m1 n1; rewrite sum_nat_const card_ord. +apply: leq_sum => i _. +apply: dvdn_leq; last by rewrite (bigD1 i) //= dvdn_lcml. +apply big_ind => // [x y Hx Hy|x H]; first by rewrite lcmn_gt0 Hx. +by rewrite bin_gt0 -ltnS. +Qed. + +*) + +(* +Lemma lieb_line n i k : lcmn 'L(n.+1, i) (\lcm_(j < k) 'L(n, i + j)) = \lcm_(j < k.+1) 'L(n.+1, i + j). + +translates to: + !n i k. lcm (leibniz (n + 1) i) (big_lcm {leibniz n (i + j) | j | j < k}) = + big_lcm {leibniz (n+1) (i + j) | j | j < k + 1}; + +The picture is: + + n-th row: L n i L n (i+1) .... L n (i + (k-1)) +(n+1)-th row: L (n+1) i + +(n+1)-th row: L (n+1) i L (n+1) (i+1) .... L (n+1) (i + (k-1)) L (n+1) (i + k) + +If k = 1, this is: L n i transform to: + L (n+1) i L (n+1) i L (n+1) (i+1) +which is Leibniz triplet. + +In general, if true for k, then for the next (k+1) + + n-th row: L n i L n (i+1) .... L n (i + (k-1)) L n (i + k) +(n+1)-th row: L (n+1) i += L n (i + k) +(n+1)-th row: L (n+1) i L (n+1) (i+1) .... L (n+1) (i + (k-1)) L (n+1) (i + k) +by induction hypothesis += +(n+1)-th row: L (n+1) i L (n+1) (i+1) .... L (n+1) (i + (k-1)) L (n+1) (i + k) L (n+1) (i + (k+1)) +by Leibniz triplet. + +*) + +(* Introduce a segment, a partial horizontal row, in Leibniz Denominator Triangle *) +val _ = overload_on("leibniz_seg", ``\n k h. IMAGE (\j. leibniz n (k + j)) (count h)``); +(* This is a segment starting at leibniz n k, of length h *) + +(* Introduce a horizontal row in Leibniz Denominator Triangle *) +val _ = overload_on("leibniz_row", ``\n h. IMAGE (leibniz n) (count h)``); +(* This is a row starting at leibniz n 0, of length h *) + +(* Introduce a vertical column in Leibniz Denominator Triangle *) +val _ = overload_on("leibniz_col", ``\h. IMAGE (\i. leibniz i 0) (count h)``); +(* This is a column starting at leibniz 0 0, descending for a length h *) + +(* Representations of paths based on indexed sets *) + +(* Theorem: leibniz_seg n k h = {leibniz n (k + j) | j | j IN (count h)} *) +(* Proof: by notation *) +val leibniz_seg_def = store_thm( + "leibniz_seg_def", + ``!n k h. leibniz_seg n k h = {leibniz n (k + j) | j | j IN (count h)}``, + rw[EXTENSION]); + +(* Theorem: leibniz_row n h = {leibniz n j | j | j IN (count h)} *) +(* Proof: by notation *) +val leibniz_row_def = store_thm( + "leibniz_row_def", + ``!n h. leibniz_row n h = {leibniz n j | j | j IN (count h)}``, + rw[EXTENSION]); + +(* Theorem: leibniz_col h = {leibniz j 0 | j | j IN (count h)} *) +(* Proof: by notation *) +val leibniz_col_def = store_thm( + "leibniz_col_def", + ``!h. leibniz_col h = {leibniz j 0 | j | j IN (count h)}``, + rw[EXTENSION]); + +(* Theorem: leibniz_col n = natural n *) +(* Proof: + leibniz_col n + = IMAGE (\i. leibniz i 0) (count n) by notation + = IMAGE (\i. i + 1) (count n) by leibniz_n_0 + = IMAGE (\i. SUC i) (count n) by ADD1 + = IMAGE SUC (count n) by FUN_EQ_THM + = natural n by notation +*) +val leibniz_col_eq_natural = store_thm( + "leibniz_col_eq_natural", + ``!n. leibniz_col n = natural n``, + rw[leibniz_n_0, ADD1, FUN_EQ_THM]); + +(* The following can be taken as a generalisation of the Leibniz Triplet LCM exchange. *) +(* When length h = 1, the top row is a singleton, and the next row is a duplet, altogether a triplet. *) + +(* Theorem: lcm (leibniz (n + 1) k) (big_lcm (leibniz_seg n k h)) = big_lcm (leibniz_seg (n + 1) k (h + 1)) *) +(* Proof: + Let p = (\j. leibniz n (k + j)), q = (\j. leibniz (n + 1) (k + j)). + Note q 0 = (leibniz (n + 1) k) by function application [1] + The goal is: lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count h))) = big_lcm (IMAGE q (count (h + 1))) + + By induction on h, length of the row. + Base case: lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count 0))) = big_lcm (IMAGE q (count (0 + 1))) + lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count 0))) + = lcm (q 0) (big_lcm (IMAGE p (count 0))) by [1] + = lcm (q 0) (big_lcm (IMAGE p {})) by COUNT_ZERO + = lcm (q 0) (big_lcm {}) by IMAGE_EMPTY + = lcm (q 0) 1 by big_lcm_empty + = q 0 by LCM_1 + = big_lcm {q 0} by big_lcm_sing + = big_lcm (IMAEG q {0}) by IMAGE_SING + = big_lcm (IMAGE q (count 1)) by count_def, EXTENSION + + Step case: lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (count h))) = big_lcm (IMAGE q (count (h + 1))) ==> + lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (upto h))) = big_lcm (IMAGE q (count (SUC h + 1))) + Note !n. FINITE (count n) by FINITE_COUNT + and !s. FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE + Also p h = (triplet n (k + h)).a by leibniz_triplet_member + q h = (triplet n (k + h)).b by leibniz_triplet_member + q (h + 1) = (triplet n (k + h)).c by leibniz_triplet_member + Thus lcm (q h) (p h) = lcm (q h) (q (h + 1)) by leibniz_triplet_lcm + + lcm (leibniz (n + 1) k) (big_lcm (IMAGE p (upto h))) + = lcm (q 0) (big_lcm (IMAGE p (count (SUC h)))) by [1], notation + = lcm (q 0) (big_lcm (IMAGE p (h INSERT count h))) by upto_by_count + = lcm (q 0) (big_lcm ((p h) INSERT (IMAGE p (count h)))) by IMAGE_INSERT + = lcm (q 0) (lcm (p h) (big_lcm (IMAGE p (count h)))) by big_lcm_insert + = lcm (p h) (lcm (q 0) (big_lcm (IMAGE p (count h)))) by LCM_ASSOC_COMM + = lcm (p h) (big_lcm (IMAGE q (count (h + 1)))) by induction hypothesis + = lcm (p h) (big_lcm (IMAGE q (count (SUC h)))) by ADD1 + = lcm (p h) (big_lcm (IMAGE q (h INSERT (count h))) by upto_by_count + = lcm (p h) (big_lcm ((q h) INSERT IMAGE q (count h))) by IMAGE_INSERT + = lcm (p h) (lcm (q h) (big_lcm (IMAGE q (count h)))) by big_lcm_insert + = lcm (lcm (p h) (q h)) (big_lcm (IMAGE q (count h))) by LCM_ASSOC + = lcm (lcm (q h) (p h)) (big_lcm (IMAGE q (count h))) by LCM_COM + = lcm (lcm (q h) (q (h + 1))) (big_lcm (IMAGE q (count h))) by leibniz_triplet_lcm + = lcm (q (h + 1)) (lcm (q h) (big_lcm (IMAGE q (count h)))) by LCM_ASSOC, LCM_COMM + = lcm (q (h + 1)) (big_lcm ((q h) INSERT IMAGE q (count h))) by big_lcm_insert + = lcm (q (h + 1)) (big_lcm (IMAGE q (h INSERT count h)) by IMAGE_INSERT + = lcm (q (h + 1)) (big_lcm (IMAGE q (count (h + 1)))) by upto_by_count, ADD1 + = big_lcm ((q (h + 1)) INSERT (IMAGE q (count (h + 1)))) by big_lcm_insert + = big_lcm IMAGE q ((h + 1) INSERT (count (h + 1))) by IMAGE_INSERT + = big_lcm (IMAGE q (count (SUC (h + 1)))) by upto_by_count + = big_lcm (IMAGE q (count (SUC h + 1))) by ADD +*) +val big_lcm_seg_transform = store_thm( + "big_lcm_seg_transform", + ``!n k h. lcm (leibniz (n + 1) k) (big_lcm (leibniz_seg n k h)) = + big_lcm (leibniz_seg (n + 1) k (h + 1))``, + rpt strip_tac >> + qabbrev_tac `p = (\j. leibniz n (k + j))` >> + qabbrev_tac `q = (\j. leibniz (n + 1) (k + j))` >> + Induct_on `h` >| [ + `count 0 = {}` by rw[] >> + `count 1 = {0}` by rw[COUNT_1] >> + rw_tac std_ss[IMAGE_EMPTY, big_lcm_empty, IMAGE_SING, LCM_1, big_lcm_sing, Abbr`p`, Abbr`q`], + `leibniz (n + 1) k = q 0` by rw[Abbr`q`] >> + simp[] >> + `lcm (q h) (p h) = lcm (q h) (q (h + 1))` by + (`p h = (triplet n (k + h)).a` by rw[leibniz_triplet_member, Abbr`p`] >> + `q h = (triplet n (k + h)).b` by rw[leibniz_triplet_member, Abbr`q`] >> + `q (h + 1) = (triplet n (k + h)).c` by rw[leibniz_triplet_member, Abbr`q`] >> + rw[leibniz_triplet_lcm]) >> + `lcm (q 0) (big_lcm (IMAGE p (count (SUC h)))) = lcm (q 0) (lcm (p h) (big_lcm (IMAGE p (count h))))` by rw[upto_by_count, big_lcm_insert] >> + `_ = lcm (p h) (lcm (q 0) (big_lcm (IMAGE p (count h))))` by rw[LCM_ASSOC_COMM] >> + `_ = lcm (p h) (big_lcm (IMAGE q (count (SUC h))))` by metis_tac[ADD1] >> + `_ = lcm (p h) (lcm (q h) (big_lcm (IMAGE q (count h))))` by rw[upto_by_count, big_lcm_insert] >> + `_ = lcm (q (h + 1)) (lcm (q h) (big_lcm (IMAGE q (count h))))` by metis_tac[LCM_ASSOC, LCM_COMM] >> + `_ = lcm (q (h + 1)) (big_lcm (IMAGE q (count (SUC h))))` by rw[upto_by_count, big_lcm_insert] >> + `_ = lcm (q (h + 1)) (big_lcm (IMAGE q (count (h + 1))))` by rw[ADD1] >> + `_ = big_lcm (IMAGE q (count (SUC (h + 1))))` by rw[upto_by_count, big_lcm_insert] >> + metis_tac[ADD] + ]); + +(* Theorem: lcm (leibniz (n + 1) 0) (big_lcm (leibniz_row n h)) = big_lcm (leibniz_row (n + 1) (h + 1)) *) +(* Proof: + Note !n h. leibniz_row n h = leibniz_seg n 0 h by FUN_EQ_THM + Take k = 0 in big_lcm_seg_transform, the result follows. +*) +val big_lcm_row_transform = store_thm( + "big_lcm_row_transform", + ``!n h. lcm (leibniz (n + 1) 0) (big_lcm (leibniz_row n h)) = big_lcm (leibniz_row (n + 1) (h + 1))``, + rpt strip_tac >> + `!n h. leibniz_row n h = leibniz_seg n 0 h` by rw[FUN_EQ_THM] >> + metis_tac[big_lcm_seg_transform]); + +(* Theorem: big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1)) *) +(* Proof: + Let f = \i. leibniz i 0, then f 0 = leibniz 0 0. + By induction on n. + Base: big_lcm (leibniz_col (0 + 1)) = big_lcm (leibniz_row 0 (0 + 1)) + big_lcm (leibniz_col (0 + 1)) + = big_lcm (IMAGE f (count 1)) by notation + = big_lcm (IMAGE f) {0}) by COUNT_1 + = big_lcm {f 0} by IMAGE_SING + = big_lcm {leibniz 0 0} by f 0 + = big_lcm (IMAGE (leibniz 0) {0}) by IMAGE_SING + = big_lcm (IMAGE (leibniz 0) (count 1)) by COUNT_1 + + Step: big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1)) ==> + big_lcm (leibniz_col (SUC n + 1)) = big_lcm (leibniz_row (SUC n) (SUC n + 1)) + big_lcm (leibniz_col (SUC n + 1)) + = big_lcm (IMAGE f (count (SUC n + 1))) by notation + = big_lcm (IMAGE f (count (SUC (n + 1)))) by ADD + = big_lcm (IMAGE f ((n + 1) INSERT (count (n + 1)))) by upto_by_count + = big_lcm ((f (n + 1)) INSERT (IMAGE f (count (n + 1)))) by IMAGE_INSERT + = lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by big_lcm_insert + = lcm (f (n + 1)) (big_lcm (IMAGE (leibniz n) (count (n + 1)))) by induction hypothesis + = lcm (leibniz (n + 1) 0) (big_lcm (IMAGE (leibniz n) (count (n + 1)))) by f (n + 1) + = big_lcm (IMAGE (leibniz (n + 1)) (count (n + 1 + 1))) by big_lcm_line_transform + = big_lcm (IMAGE (leibniz (SUC n)) (count (SUC n + 1))) by ADD1 +*) +val big_lcm_corner_transform = store_thm( + "big_lcm_corner_transform", + ``!n. big_lcm (leibniz_col (n + 1)) = big_lcm (leibniz_row n (n + 1))``, + Induct >- + rw[COUNT_1, IMAGE_SING] >> + qabbrev_tac `f = \i. leibniz i 0` >> + `big_lcm (IMAGE f (count (SUC n + 1))) = big_lcm (IMAGE f (count (SUC (n + 1))))` by rw[ADD] >> + `_ = lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[upto_by_count, big_lcm_insert] >> + `_ = lcm (leibniz (n + 1) 0) (big_lcm (IMAGE (leibniz n) (count (n + 1))))` by rw[Abbr`f`] >> + `_ = big_lcm (IMAGE (leibniz (n + 1)) (count (n + 1 + 1)))` by rw[big_lcm_row_transform] >> + `_ = big_lcm (IMAGE (leibniz (SUC n)) (count (SUC n + 1)))` by rw[ADD1] >> + rw[]); + +(* Theorem: (!x. x IN (count (n + 1)) ==> 0 < f x) ==> + SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1))) *) +(* Proof: + By induction on n. + Base: SUM (GENLIST f (0 + 1)) <= (0 + 1) * big_lcm (IMAGE f (count (0 + 1))) + LHS = SUM (GENLIST f 1) + = SUM [f 0] by GENLIST_1 + = f 0 by SUM + RHS = 1 * big_lcm (IMAGE f (count 1)) + = big_lcm (IMAGE f {0}) by COUNT_1 + = big_lcm (f 0) by IMAGE_SING + = f 0 by big_lcm_sing + Thus LHS <= RHS by arithmetic + Step: SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1))) ==> + SUM (GENLIST f (SUC n + 1)) <= (SUC n + 1) * big_lcm (IMAGE f (count (SUC n + 1))) + Note 0 < f (n + 1) by (n + 1) IN count (SUC n + 1) + and !y. y IN count (n + 1) ==> y IN count (SUC n + 1) by IN_COUNT + and !x. x IN IMAGE f (count (n + 1)) ==> 0 < x by IN_IMAGE, above + so 0 < big_lcm (IMAGE f (count (n + 1))) by big_lcm_positive + and 0 < SUC n by SUC_POS + Thus f (n + 1) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by LCM_LE + and big_lcm (IMAGE f (count (n + 1))) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by LCM_LE + + LHS = SUM (GENLIST f (SUC n + 1)) + = SUM (GENLIST f (SUC (n + 1))) by ADD + = SUM (SNOC (f (n + 1)) (GENLIST f (n + 1))) by GENLIST + = SUM (GENLIST f (n + 1)) + f (n + 1) by SUM_SNOC + RHS = (SUC n + 1) * big_lcm (IMAGE f (count (SUC n + 1))) + = (SUC n + 1) * big_lcm (IMAGE f (count (SUC (n + 1)))) by ADD + = (SUC n + 1) * big_lcm (IMAGE f ((n + 1) INSERT (count (n + 1)))) by upto_by_count + = (SUC n + 1) * big_lcm ((f (n + 1)) INSERT (IMAGE f (count (n + 1)))) by IMAGE_INSERT + = (SUC n + 1) * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by big_lcm_insert + = SUC n * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) + + 1 * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1)))) by RIGHT_ADD_DISTRIB + >= SUC n * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) by LCM_LE + = (n + 1) * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) by ADD1 + >= SUM (GENLIST f (n + 1)) + f (n + 1) by induction hypothesis + = LHS by above +*) +val big_lcm_count_lower_bound = store_thm( + "big_lcm_count_lower_bound", + ``!f n. (!x. x IN (count (n + 1)) ==> 0 < f x) ==> + SUM (GENLIST f (n + 1)) <= (n + 1) * big_lcm (IMAGE f (count (n + 1)))``, + rpt strip_tac >> + Induct_on `n` >| [ + rpt strip_tac >> + `SUM (GENLIST f 1) = f 0` by rw[] >> + `1 * big_lcm (IMAGE f (count 1)) = f 0` by rw[COUNT_1, big_lcm_sing] >> + rw[], + rpt strip_tac >> + `big_lcm (IMAGE f (count (SUC n + 1))) = big_lcm (IMAGE f (count (SUC (n + 1))))` by rw[ADD] >> + `_ = lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[upto_by_count, big_lcm_insert] >> + `!x. (SUC n + 1) * x = SUC n * x + x` by rw[] >> + `0 < f (n + 1)` by rw[] >> + `!y. y IN count (n + 1) ==> y IN count (SUC n + 1)` by rw[] >> + `!x. x IN IMAGE f (count (n + 1)) ==> 0 < x` by metis_tac[IN_IMAGE] >> + `0 < big_lcm (IMAGE f (count (n + 1)))` by rw[big_lcm_positive] >> + `0 < SUC n` by rw[] >> + `f (n + 1) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[LCM_LE] >> + `big_lcm (IMAGE f (count (n + 1))) <= lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by rw[LCM_LE] >> + `!a b c x. 0 < a /\ 0 < b /\ 0 < c /\ a <= x /\ b <= x ==> c * a + b <= c * x + x` by + (rpt strip_tac >> + `c * a <= c * x` by rw[] >> + decide_tac) >> + `SUC n * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) <= (SUC n + 1) * lcm (f (n + 1)) (big_lcm (IMAGE f (count (n + 1))))` by metis_tac[] >> + `SUC n * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1) = (n + 1) * (big_lcm (IMAGE f (count (n + 1)))) + f (n + 1)` by rw[ADD1] >> + `SUM (GENLIST f (SUC n + 1)) = SUM (GENLIST f (SUC (n + 1)))` by rw[ADD] >> + `_ = SUM (GENLIST f (n + 1)) + f (n + 1)` by rw[GENLIST, SUM_SNOC] >> + metis_tac[LESS_EQ_TRANS, DECIDE``!a x y. 0 < a /\ x <= y ==> x + a <= y + a``] + ]); + +(* Theorem: big_lcm (natural (n + 1)) = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) *) +(* Proof: + Note SUC = \i. i + 1 by ADD1, FUN_EQ_THM + = \i. leibniz i 0 by leibniz_n_0 + and leibniz n = \j. (n + 1) * binomial n j by leibniz_def, FUN_EQ_THM + so !s. IMAGE SUC s = IMAGE (\i. leibniz i 0) s by IMAGE_CONG + and !s. IMAGE (leibniz n) s = IMAGE (\j. (n + 1) * binomial n j) s by IMAGE_CONG + also !s. IMAGE (binomial n) s = IMAGE (\j. binomial n j) s by FUN_EQ_THM, IMAGE_CONG + and count (n + 1) <> {} by COUNT_EQ_EMPTY, n + 1 <> 0 [1] + + big_lcm (IMAGE SUC (count (n + 1))) + = big_lcm (IMAGE (\i. leibniz i 0) (count (n + 1))) by above + = big_lcm (IMAGE (leibniz n) (count (n + 1))) by big_lcm_corner_transform + = big_lcm (IMAGE (\j. (n + 1) * binomial n j) (count (n + 1))) by leibniz_def + = big_lcm (IMAGE ($* (n + 1)) (IMAGE (binomial n) (count (n + 1)))) by IMAGE_COMPOSE, o_DEF + = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) by big_lcm_map_times, FINITE_COUNT, [1] +*) +val big_lcm_natural_eqn = store_thm( + "big_lcm_natural_eqn", + ``!n. big_lcm (natural (n + 1)) = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1)))``, + rpt strip_tac >> + `SUC = \i. leibniz i 0` by rw[leibniz_n_0, FUN_EQ_THM] >> + `leibniz n = \j. (n + 1) * binomial n j` by rw[leibniz_def, FUN_EQ_THM] >> + `!s. IMAGE SUC s = IMAGE (\i. leibniz i 0) s` by rw[IMAGE_CONG] >> + `!s. IMAGE (leibniz n) s = IMAGE (\j. (n + 1) * binomial n j) s` by rw[IMAGE_CONG] >> + `!s. IMAGE (binomial n) s = IMAGE (\j. binomial n j) s` by rw[FUN_EQ_THM, IMAGE_CONG] >> + `count (n + 1) <> {}` by rw[COUNT_EQ_EMPTY] >> + `big_lcm (IMAGE SUC (count (n + 1))) = big_lcm (IMAGE (leibniz n) (count (n + 1)))` by rw[GSYM big_lcm_corner_transform] >> + `_ = big_lcm (IMAGE (\j. (n + 1) * binomial n j) (count (n + 1)))` by rw[] >> + `_ = big_lcm (IMAGE ($* (n + 1)) (IMAGE (binomial n) (count (n + 1))))` by rw[GSYM IMAGE_COMPOSE, combinTheory.o_DEF] >> + `_ = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1)))` by rw[big_lcm_map_times] >> + rw[]); + +(* Theorem: 2 ** n <= big_lcm (natural (n + 1)) *) +(* Proof: + Note !x. x IN (count (n + 1)) ==> 0 < (binomial n) x by binomial_pos, IN_COUNT [1] + big_lcm (natural (n + 1)) + = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) by big_lcm_natural_eqn + >= SUM (GENLIST (binomial n) (n + 1)) by big_lcm_count_lower_bound, [1] + = SUM (GENLIST (binomial n) (SUC n)) by ADD1 + = 2 ** n by binomial_sum +*) +val big_lcm_lower_bound = store_thm( + "big_lcm_lower_bound", + ``!n. 2 ** n <= big_lcm (natural (n + 1))``, + rpt strip_tac >> + `!x. x IN (count (n + 1)) ==> 0 < (binomial n) x` by rw[binomial_pos] >> + `big_lcm (IMAGE SUC (count (n + 1))) = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1)))` by rw[big_lcm_natural_eqn] >> + `SUM (GENLIST (binomial n) (n + 1)) = 2 ** n` by rw[GSYM binomial_sum, ADD1] >> + metis_tac[big_lcm_count_lower_bound]); + +(* Another proof of the milestone theorem. *) + +(* Theorem: big_lcm (set l) = list_lcm l *) +(* Proof: + By induction on l. + Base: big_lcm (set []) = list_lcm [] + big_lcm (set []) + = big_lcm {} by LIST_TO_SET + = 1 by big_lcm_empty + = list_lcm [] by list_lcm_nil + Step: big_lcm (set l) = list_lcm l ==> !h. big_lcm (set (h::l)) = list_lcm (h::l) + Note FINITE (set l) by FINITE_LIST_TO_SET + big_lcm (set (h::l)) + = big_lcm (h INSERT set l) by LIST_TO_SET + = lcm h (big_lcm (set l)) by big_lcm_insert, FINITE (set t) + = lcm h (list_lcm l) by induction hypothesis + = list_lcm (h::l) by list_lcm_cons +*) +val big_lcm_eq_list_lcm = store_thm( + "big_lcm_eq_list_lcm", + ``!l. big_lcm (set l) = list_lcm l``, + Induct >- + rw[big_lcm_empty] >> + rw[big_lcm_insert]); + +(* ------------------------------------------------------------------------- *) +(* List LCM depends only on its set of elements *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MEM x l ==> (list_lcm (x::l) = list_lcm l) *) +(* Proof: + By induction on l. + Base: MEM x [] ==> (list_lcm [x] = list_lcm []) + True by MEM x [] = F by MEM + Step: MEM x l ==> (list_lcm (x::l) = list_lcm l) ==> + !h. MEM x (h::l) ==> (list_lcm (x::h::l) = list_lcm (h::l)) + Note MEM x (h::l) ==> (x = h) \/ (MEM x l) by MEM + If x = h, + list_lcm (h::h::l) + = lcm h (lcm h (list_lcm l)) by list_lcm_cons + = lcm (lcm h h) (list_lcm l) by LCM_ASSOC + = lcm h (list_lcm l) by LCM_REF + = list_lcm (h::l) by list_lcm_cons + If x <> h, MEM x l + list_lcm (x::h::l) + = lcm x (lcm h (list_lcm l)) by list_lcm_cons + = lcm h (lcm x (list_lcm l)) by LCM_ASSOC_COMM + = lcm h (list_lcm (x::l)) by list_lcm_cons + = lcm h (list_lcm l) by induction hypothesis, MEM x l + = list_lcm (h::l) by list_lcm_cons +*) +val list_lcm_absorption = store_thm( + "list_lcm_absorption", + ``!x l. MEM x l ==> (list_lcm (x::l) = list_lcm l)``, + rpt strip_tac >> + Induct_on `l` >- + metis_tac[MEM] >> + rw[MEM] >| [ + `lcm h (lcm h (list_lcm l)) = lcm (lcm h h) (list_lcm l)` by rw[LCM_ASSOC] >> + rw[LCM_REF], + `lcm x (lcm h (list_lcm l)) = lcm h (lcm x (list_lcm l))` by rw[LCM_ASSOC_COMM] >> + `_ = lcm h (list_lcm (x::l))` by metis_tac[list_lcm_cons] >> + rw[] + ]); + +(* Theorem: list_lcm (nub l) = list_lcm l *) +(* Proof: + By induction on l. + Base: list_lcm (nub []) = list_lcm [] + True since nub [] = [] by nub_nil + Step: list_lcm (nub l) = list_lcm l ==> !h. list_lcm (nub (h::l)) = list_lcm (h::l) + If MEM h l, + list_lcm (nub (h::l)) + = list_lcm (nub l) by nub_cons, MEM h l + = list_lcm l by induction hypothesis + = list_lcm (h::l) by list_lcm_absorption, MEM h l + If ~(MEM h l), + list_lcm (nub (h::l)) + = list_lcm (h::nub l) by nub_cons, ~(MEM h l) + = lcm h (list_lcm (nub l)) by list_lcm_cons + = lcm h (list_lcm l) by induction hypothesis + = list_lcm (h::l) by list_lcm_cons +*) +val list_lcm_nub = store_thm( + "list_lcm_nub", + ``!l. list_lcm (nub l) = list_lcm l``, + Induct >- + rw[nub_nil] >> + metis_tac[nub_cons, list_lcm_cons, list_lcm_absorption]); + +(* Theorem: (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2)) *) +(* Proof: + By induction on l1. + Base: !l2. (set [] = set l2) ==> (list_lcm (nub []) = list_lcm (nub l2)) + Note set [] = set l2 ==> l2 = [] by LIST_TO_SET_EQ_EMPTY + Hence true. + Step: !l2. (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2)) ==> + !h l2. (set (h::l1) = set l2) ==> (list_lcm (nub (h::l1)) = list_lcm (nub l2)) + If MEM h l1, + Then h IN (set l1) by notation + set (h::l1) + = h INSERT set l1 by LIST_TO_SET + = set l1 by ABSORPTION_RWT + Thus set l1 = set l2, + so list_lcm (nub (h::l1)) + = list_lcm (nub l1) by nub_cons, MEM h l1 + = list_lcm (nub l2) by induction hypothesis, set l1 = set l2 + + If ~(MEM h l1), + Then set (h::l1) = set l2 + ==> ?p1 p2. nub l2 = p1 ++ [h] ++ p2 + and set l1 = set (p1 ++ p2) by LIST_TO_SET_REDUCTION + + list_lcm (nub (h::l1)) + = list_lcm (h::nub l1) by nub_cons, ~(MEM h l1) + = lcm h (list_lcm (nub l1)) by list_lcm_cons + = lcm h (list_lcm (nub (p1 ++ p2))) by induction hypothesis + = lcm h (list_lcm (p1 ++ p2)) by list_lcm_nub + = lcm h (lcm (list_lcm p1) (list_lcm p2)) by list_lcm_append + = lcm (list_lcm p1) (lcm h (list_lcm p2)) by LCM_ASSOC_COMM + = lcm (list_lcm p1) (list_lcm (h::p2)) by list_lcm_append + = lcm (list_lcm p1) (list_lcm ([h] ++ p2)) by CONS_APPEND + = list_lcm (p1 ++ ([h] ++ p2)) by list_lcm_append + = list_lcm (p1 ++ [h] ++ p2) by APPEND_ASSOC + = list_lcm (nub l2) by above +*) +val list_lcm_nub_eq_if_set_eq = store_thm( + "list_lcm_nub_eq_if_set_eq", + ``!l1 l2. (set l1 = set l2) ==> (list_lcm (nub l1) = list_lcm (nub l2))``, + Induct >- + rw[LIST_TO_SET_EQ_EMPTY] >> + rpt strip_tac >> + Cases_on `MEM h l1` >- + metis_tac[LIST_TO_SET, ABSORPTION_RWT, nub_cons] >> + `?p1 p2. (nub l2 = p1 ++ [h] ++ p2) /\ (set l1 = set (p1 ++ p2))` by metis_tac[LIST_TO_SET_REDUCTION] >> + `list_lcm (nub (h::l1)) = list_lcm (h::nub l1)` by rw[nub_cons] >> + `_ = lcm h (list_lcm (nub l1))` by rw[list_lcm_cons] >> + `_ = lcm h (list_lcm (nub (p1 ++ p2)))` by metis_tac[] >> + `_ = lcm h (list_lcm (p1 ++ p2))` by rw[list_lcm_nub] >> + `_ = lcm h (lcm (list_lcm p1) (list_lcm p2))` by rw[list_lcm_append] >> + `_ = lcm (list_lcm p1) (lcm h (list_lcm p2))` by rw[LCM_ASSOC_COMM] >> + `_ = lcm (list_lcm p1) (list_lcm ([h] ++ p2))` by rw[list_lcm_cons] >> + metis_tac[list_lcm_append, APPEND_ASSOC]); + +(* Theorem: (set l1 = set l2) ==> (list_lcm l1 = list_lcm l2) *) +(* Proof: + set l1 = set l2 + ==> list_lcm (nub l1) = list_lcm (nub l2) by list_lcm_nub_eq_if_set_eq + ==> list_lcm l1 = list_lcm l2 by list_lcm_nub +*) +val list_lcm_eq_if_set_eq = store_thm( + "list_lcm_eq_if_set_eq", + ``!l1 l2. (set l1 = set l2) ==> (list_lcm l1 = list_lcm l2)``, + metis_tac[list_lcm_nub_eq_if_set_eq, list_lcm_nub]); + +(* ------------------------------------------------------------------------- *) +(* Set LCM by List LCM *) +(* ------------------------------------------------------------------------- *) + +(* Define LCM of a set *) +(* none works! +val set_lcm_def = Define` + (set_lcm {} = 1) /\ + !s. FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm (s DELETE x)) +`; +val set_lcm_def = Define` + (set_lcm {} = 1) /\ + (!s. FINITE s ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s)))) +`; +val set_lcm_def = Define` + set_lcm s = if s = {} then 1 else lcm (CHOICE s) (set_lcm (REST s)) +`; +*) +val set_lcm_def = Define` + set_lcm s = list_lcm (SET_TO_LIST s) +`; + +(* Theorem: set_lcm {} = 1 *) +(* Proof: + set_lcm {} + = lcm_list (SET_TO_LIST {}) by set_lcm_def + = lcm_list [] by SET_TO_LIST_EMPTY + = 1 by list_lcm_nil +*) +val set_lcm_empty = store_thm( + "set_lcm_empty", + ``set_lcm {} = 1``, + rw[set_lcm_def]); + +(* Theorem: FINITE s /\ s <> {} ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s))) *) +(* Proof: + set_lcm s + = list_lcm (SET_TO_LIST s) by set_lcm_def + = list_lcm (CHOICE s::SET_TO_LIST (REST s)) by SET_TO_LIST_THM + = lcm (CHOICE s) (list_lcm (SET_TO_LIST (REST s))) by list_lcm_cons + = lcm (CHOICE s) (set_lcm (REST s)) by set_lcm_def +*) +val set_lcm_nonempty = store_thm( + "set_lcm_nonempty", + ``!s. FINITE s /\ s <> {} ==> (set_lcm s = lcm (CHOICE s) (set_lcm (REST s)))``, + rw[set_lcm_def, SET_TO_LIST_THM, list_lcm_cons]); + +(* Theorem: set_lcm {x} = x *) +(* Proof: + set_lcm {x} + = list_lcm (SET_TO_LIST {x}) by set_lcm_def + = list_lcm [x] by SET_TO_LIST_SING + = x by list_lcm_sing +*) +val set_lcm_sing = store_thm( + "set_lcm_sing", + ``!x. set_lcm {x} = x``, + rw_tac std_ss[set_lcm_def, SET_TO_LIST_SING, list_lcm_sing]); + +(* Theorem: set_lcm (set l) = list_lcm l *) +(* Proof: + Let t = SET_TO_LIST (set l) + Note FINITE (set l) by FINITE_LIST_TO_SET + Then set t + = set (SET_TO_LIST (set l)) by notation + = set l by SET_TO_LIST_INV, FINITE (set l) + + set_lcm (set l) + = list_lcm (SET_TO_LIST (set l)) by set_lcm_def + = list_lcm t by notation + = list_lcm l by list_lcm_eq_if_set_eq, set t = set l +*) +val set_lcm_eq_list_lcm = store_thm( + "set_lcm_eq_list_lcm", + ``!l. set_lcm (set l) = list_lcm l``, + rw[FINITE_LIST_TO_SET, SET_TO_LIST_INV, set_lcm_def, list_lcm_eq_if_set_eq]); + +(* Theorem: FINITE s ==> (set_lcm s = big_lcm s) *) +(* Proof: + set_lcm s + = list_lcm (SET_TO_LIST s) by set_lcm_def + = big_lcm (set (SET_TO_LIST s)) by big_lcm_eq_list_lcm + = big_lcm s by SET_TO_LIST_INV, FINITE s +*) +val set_lcm_eq_big_lcm = store_thm( + "set_lcm_eq_big_lcm", + ``!s. FINITE s ==> (big_lcm s = set_lcm s)``, + metis_tac[set_lcm_def, big_lcm_eq_list_lcm, SET_TO_LIST_INV]); + +(* Theorem: FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm s) *) +(* Proof: by big_lcm_insert, set_lcm_eq_big_lcm *) +val set_lcm_insert = store_thm( + "set_lcm_insert", + ``!s. FINITE s ==> !x. set_lcm (x INSERT s) = lcm x (set_lcm s)``, + rw[big_lcm_insert, GSYM set_lcm_eq_big_lcm]); + +(* Theorem: FINITE s /\ x IN s ==> x divides (set_lcm s) *) +(* Proof: + Note FINITE s /\ x IN s + ==> MEM x (SET_TO_LIST s) by MEM_SET_TO_LIST + ==> x divides list_lcm (SET_TO_LIST s) by list_lcm_is_common_multiple + or x divides (set_lcm s) by set_lcm_def +*) +val set_lcm_is_common_multiple = store_thm( + "set_lcm_is_common_multiple", + ``!x s. FINITE s /\ x IN s ==> x divides (set_lcm s)``, + rw[set_lcm_def] >> + `MEM x (SET_TO_LIST s)` by rw[MEM_SET_TO_LIST] >> + rw[list_lcm_is_common_multiple]); + +(* Theorem: FINITE s /\ (!x. x IN s ==> x divides m) ==> set_lcm s divides m *) +(* Proof: + Note FINITE s + ==> !x. x IN s <=> MEM x (SET_TO_LIST s) by MEM_SET_TO_LIST + Thus list_lcm (SET_TO_LIST s) divides m by list_lcm_is_least_common_multiple + or set_lcm s divides m by set_lcm_def +*) +val set_lcm_is_least_common_multiple = store_thm( + "set_lcm_is_least_common_multiple", + ``!s m. FINITE s /\ (!x. x IN s ==> x divides m) ==> set_lcm s divides m``, + metis_tac[set_lcm_def, MEM_SET_TO_LIST, list_lcm_is_least_common_multiple]); + +(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s) *) +(* Proof: + By finite induction on s. + Base: set_lcm {} = PROD_SET {} + set_lcm {} + = 1 by set_lcm_empty + = PROD_SET {} by PROD_SET_EMPTY + Step: PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s) ==> + e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> set_lcm (e INSERT s) = PROD_SET (e INSERT s) + Note !z. z IN s ==> coprime e z by IN_INSERT + Thus coprime e (PROD_SET s) by every_coprime_prod_set_coprime + set_lcm (e INSERT s) + = lcm e (set_lcm s) by set_lcm_insert + = lcm e (PROD_SET s) by induction hypothesis + = e * (PROD_SET s) by LCM_COPRIME + = PROD_SET (e INSERT s) by PROD_SET_INSERT, e NOTIN s +*) +val pairwise_coprime_prod_set_eq_set_lcm = store_thm( + "pairwise_coprime_prod_set_eq_set_lcm", + ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s)``, + `!s. FINITE s ==> PAIRWISE_COPRIME s ==> (set_lcm s = PROD_SET s)` suffices_by rw[] >> + Induct_on `FINITE` >> + rpt strip_tac >- + rw[set_lcm_empty, PROD_SET_EMPTY] >> + fs[] >> + `!z. z IN s ==> coprime e z` by metis_tac[] >> + `coprime e (PROD_SET s)` by rw[every_coprime_prod_set_coprime] >> + `set_lcm (e INSERT s) = lcm e (set_lcm s)` by rw[set_lcm_insert] >> + `_ = lcm e (PROD_SET s)` by rw[] >> + `_ = e * (PROD_SET s)` by rw[LCM_COPRIME] >> + `_ = PROD_SET (e INSERT s)` by rw[PROD_SET_INSERT] >> + rw[]); + +(* This is a generalisation of LCM_COPRIME |- !m n. coprime m n ==> (lcm m n = m * n) *) + +(* Theorem: FINITE s /\ PAIRWISE_COPRIME s /\ (!x. x IN s ==> x divides m) ==> (PROD_SET s) divides m *) +(* Proof: + Note PROD_SET s = set_lcm s by pairwise_coprime_prod_set_eq_set_lcm + and set_lcm s divides m by set_lcm_is_least_common_multiple + ==> (PROD_SET s) divides m +*) +val pairwise_coprime_prod_set_divides = store_thm( + "pairwise_coprime_prod_set_divides", + ``!s m. FINITE s /\ PAIRWISE_COPRIME s /\ (!x. x IN s ==> x divides m) ==> (PROD_SET s) divides m``, + rw[set_lcm_is_least_common_multiple, GSYM pairwise_coprime_prod_set_eq_set_lcm]); + +(* ------------------------------------------------------------------------- *) +(* Nair's Trick - using List LCM directly *) +(* ------------------------------------------------------------------------- *) + +(* Overload on consecutive LCM *) +val _ = overload_on("lcm_run", ``\n. list_lcm [1 .. n]``); + +(* Theorem: lcm_run n = FOLDL lcm 1 [1 .. n] *) +(* Proof: + lcm_run n + = list_lcm [1 .. n] by notation + = FOLDL lcm 1 [1 .. n] by list_lcm_by_FOLDL +*) +val lcm_run_by_FOLDL = store_thm( + "lcm_run_by_FOLDL", + ``!n. lcm_run n = FOLDL lcm 1 [1 .. n]``, + rw[list_lcm_by_FOLDL]); + +(* Theorem: lcm_run n = FOLDL lcm 1 [1 .. n] *) +(* Proof: + lcm_run n + = list_lcm [1 .. n] by notation + = FOLDR lcm 1 [1 .. n] by list_lcm_by_FOLDR +*) +val lcm_run_by_FOLDR = store_thm( + "lcm_run_by_FOLDR", + ``!n. lcm_run n = FOLDR lcm 1 [1 .. n]``, + rw[list_lcm_by_FOLDR]); + +(* Theorem: lcm_run 0 = 1 *) +(* Proof: + lcm_run 0 + = list_lcm [1 .. 0] by notation + = list_lcm [] by listRangeINC_EMPTY, 0 < 1 + = 1 by list_lcm_nil +*) +val lcm_run_0 = store_thm( + "lcm_run_0", + ``lcm_run 0 = 1``, + rw[listRangeINC_EMPTY]); + +(* Theorem: lcm_run 1 = 1 *) +(* Proof: + lcm_run 1 + = list_lcm [1 .. 1] by notation + = list_lcm [1] by leibniz_vertical_0 + = 1 by list_lcm_sing +*) +val lcm_run_1 = store_thm( + "lcm_run_1", + ``lcm_run 1 = 1``, + rw[leibniz_vertical_0, list_lcm_sing]); + +(* Theorem alias *) +val lcm_run_suc = save_thm("lcm_run_suc", list_lcm_suc); +(* val lcm_run_suc = |- !n. lcm_run (n + 1) = lcm (n + 1) (lcm_run n): thm *) + +(* Theorem: 0 < lcm_run n *) +(* Proof: + Note EVERY_POSITIVE [1 .. n] by listRangeINC_EVERY + so lcm_run n + = list_lcm [1 .. n] by notation + > 0 by list_lcm_pos +*) +val lcm_run_pos = store_thm( + "lcm_run_pos", + ``!n. 0 < lcm_run n``, + rw[list_lcm_pos, listRangeINC_EVERY]); + +(* Theorem: (lcm_run 2 = 2) /\ (lcm_run 3 = 6) /\ (lcm_run 4 = 12) /\ (lcm_run 5 = 60) /\ ... *) +(* Proof: by evaluation *) +val lcm_run_small = store_thm( + "lcm_run_small", + ``(lcm_run 2 = 2) /\ (lcm_run 3 = 6) /\ (lcm_run 4 = 12) /\ (lcm_run 5 = 60) /\ + (lcm_run 6 = 60) /\ (lcm_run 7 = 420) /\ (lcm_run 8 = 840) /\ (lcm_run 9 = 2520)``, + EVAL_TAC); + +(* Theorem: (n + 1) divides lcm_run (n + 1) /\ (lcm_run n) divides lcm_run (n + 1) *) +(* Proof: + If n = 0, + Then 0 + 1 = 1 by arithmetic + and lcm_run 0 = 1 by lcm_run_0 + Hence true by ONE_DIVIDES_ALL + If n <> 0, + Then n - 1 + 1 = n by arithmetic, 0 < n + lcm_run (n + 1) + = list_lcm [1 .. (n + 1)] by notation + = list_lcm (SNOC (n + 1) [1 .. n]) by leibniz_vertical_snoc + = lcm (n + 1) (list_lcm [1 .. n]) by list_lcm_snoc] + = lcm (n + 1) (lcm_run n) by notation + Hence true by LCM_DIVISORS +*) +val lcm_run_divisors = store_thm( + "lcm_run_divisors", + ``!n. (n + 1) divides lcm_run (n + 1) /\ (lcm_run n) divides lcm_run (n + 1)``, + strip_tac >> + Cases_on `n = 0` >- + rw[lcm_run_0] >> + `(n - 1 + 1 = n) /\ (n - 1 + 2 = n + 1)` by decide_tac >> + `lcm_run (n + 1) = list_lcm (SNOC (n + 1) [1 .. n])` by metis_tac[leibniz_vertical_snoc] >> + `_ = lcm (n + 1) (lcm_run n)` by rw[list_lcm_snoc] >> + rw[LCM_DIVISORS]); + +(* Theorem: lcm_run n <= lcm_run (n + 1) *) +(* Proof: + Note lcm_run n divides lcm_run (n + 1) by lcm_run_divisors + and 0 < lcm_run (n + 1) ] by lcm_run_pos + so lcm_run n <= lcm_run (n + 1) by DIVIDES_LE +*) +Theorem lcm_run_monotone[allow_rebind]: + !n. lcm_run n <= lcm_run (n + 1) +Proof rw[lcm_run_divisors, lcm_run_pos, DIVIDES_LE] +QED + +(* Theorem: 2 ** n <= lcm_run (n + 1) *) +(* Proof: + lcm_run (n + 1) + = list_lcm [1 .. (n + 1)] by notation + >= 2 ** n by lcm_lower_bound +*) +val lcm_run_lower = save_thm("lcm_run_lower", lcm_lower_bound); +(* +val lcm_run_lower = |- !n. 2 ** n <= lcm_run (n + 1): thm +*) + +(* Theorem: !n k. k <= n ==> leibniz n k divides lcm_run (n + 1) *) +(* Proof: by notation, leibniz_vertical_divisor *) +val lcm_run_leibniz_divisor = save_thm("lcm_run_leibniz_divisor", leibniz_vertical_divisor); +(* +val lcm_run_leibniz_divisor = |- !n k. k <= n ==> leibniz n k divides lcm_run (n + 1): thm +*) + +(* Theorem: n * 4 ** n <= lcm_run (2 * n + 1) *) +(* Proof: + If n = 0, LHS = 0, trivially true. + If n <> 0, 0 < n. + Let m = 2 * n. + + Claim: (m + 1) * binomial m n divides lcm_run (m + 1) [1] + Proof: Note n <= m by LESS_MONO_MULT, 1 <= 2 + ==> (leibniz m n) divides lcm_run (m + 1) by lcm_run_leibniz_divisor, n <= m + or (m + 1) * binomial m n divides lcm_run (m + 1) by leibniz_def + + Claim: n * binomial m n divides lcm_run (m + 1) [2] + Proof: Note 0 < m /\ n <= m - 1 by 0 < n + and m - 1 + 1 = m by 0 < m + Thus (leibniz (m - 1) n) divides lcm_run m by lcm_run_leibniz_divisor, n <= m - 1 + Note (lcm_run m) divides lcm_run (m + 1) by lcm_run_divisors + so (leibniz (m - 1) n) divides lcm_run (m + 1) by DIVIDES_TRANS + and leibniz (m - 1) n + = (m - n) * binomial m n by leibniz_up_alt + = n * binomial m n by m - n = n + + Note coprime n (m + 1) by GCD_EUCLID, GCD_1, 1 < n + Thus lcm (n * binomial m n) ((m + 1) * binomial m n) + = n * (m + 1) * binomial m n by LCM_COMMON_COPRIME + = n * ((m + 1) * binomial m n) by MULT_ASSOC + = n * leibniz m n by leibniz_def + ==> n * leibniz m n divides lcm_run (m + 1) by LCM_DIVIDES, [1], [2] + Note 0 < lcm_run (m + 1) by lcm_run_pos + or n * leibniz m n <= lcm_run (m + 1) by DIVIDES_LE, 0 < lcm_run (m + 1) + Now 4 ** n <= leibniz m n by leibniz_middle_lower + so n * 4 ** n <= n * leibniz m n by LESS_MONO_MULT, MULT_COMM + or n * 4 ** n <= lcm_run (m + 1) by LESS_EQ_TRANS +*) +val lcm_run_lower_odd = store_thm( + "lcm_run_lower_odd", + ``!n. n * 4 ** n <= lcm_run (2 * n + 1)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + `0 < n` by decide_tac >> + qabbrev_tac `m = 2 * n` >> + `(m + 1) * binomial m n divides lcm_run (m + 1)` by + (`n <= m` by rw[Abbr`m`] >> + metis_tac[lcm_run_leibniz_divisor, leibniz_def]) >> + `n * binomial m n divides lcm_run (m + 1)` by + (`0 < m /\ n <= m - 1` by rw[Abbr`m`] >> + `m - 1 + 1 = m` by decide_tac >> + `(leibniz (m - 1) n) divides lcm_run m` by metis_tac[lcm_run_leibniz_divisor] >> + `(lcm_run m) divides lcm_run (m + 1)` by rw[lcm_run_divisors] >> + `leibniz (m - 1) n = (m - n) * binomial m n` by rw[leibniz_up_alt] >> + `_ = n * binomial m n` by rw[Abbr`m`] >> + metis_tac[DIVIDES_TRANS]) >> + `coprime n (m + 1)` by rw[GCD_EUCLID, Abbr`m`] >> + `lcm (n * binomial m n) ((m + 1) * binomial m n) = n * (m + 1) * binomial m n` by rw[LCM_COMMON_COPRIME] >> + `_ = n * leibniz m n` by rw[leibniz_def, MULT_ASSOC] >> + `n * leibniz m n divides lcm_run (m + 1)` by metis_tac[LCM_DIVIDES] >> + `n * leibniz m n <= lcm_run (m + 1)` by rw[DIVIDES_LE, lcm_run_pos] >> + `4 ** n <= leibniz m n` by rw[leibniz_middle_lower, Abbr`m`] >> + metis_tac[LESS_MONO_MULT, MULT_COMM, LESS_EQ_TRANS]); + +(* Theorem: n * 4 ** n <= lcm_run (2 * (n + 1)) *) +(* Proof: + lcm_run (2 * (n + 1)) + = lcm_run (2 * n + 2) by arithmetic + >= lcm_run (2 * n + 1) by lcm_run_monotone + >= n * 4 ** n by lcm_run_lower_odd +*) +val lcm_run_lower_even = store_thm( + "lcm_run_lower_even", + ``!n. n * 4 ** n <= lcm_run (2 * (n + 1))``, + rpt strip_tac >> + `2 * (n + 1) = 2 * n + 1 + 1` by decide_tac >> + metis_tac[lcm_run_monotone, lcm_run_lower_odd, LESS_EQ_TRANS]); + +(* Theorem: ODD n ==> (HALF n) * HALF (2 ** n) <= lcm_run n *) +(* Proof: + Let k = HALF n. + Then n = 2 * k + 1 by ODD_HALF + and HALF (2 ** n) + = HALF (2 ** (2 * k + 1)) by above + = HALF (2 ** (SUC (2 * k))) by ADD1 + = HALF (2 * 2 ** (2 * k)) by EXP + = 2 ** (2 * k) by HALF_TWICE + = 4 ** k by EXP_EXP_MULT + Since k * 4 ** k <= lcm_run (2 * k + 1) by lcm_run_lower_odd + The result follows. +*) +val lcm_run_odd_lower = store_thm( + "lcm_run_odd_lower", + ``!n. ODD n ==> (HALF n) * HALF (2 ** n) <= lcm_run n``, + rpt strip_tac >> + qabbrev_tac `k = HALF n` >> + `n = 2 * k + 1` by rw[ODD_HALF, Abbr`k`] >> + `HALF (2 ** n) = HALF (2 ** (SUC (2 * k)))` by rw[ADD1] >> + `_ = HALF (2 * 2 ** (2 * k))` by rw[EXP] >> + `_ = 2 ** (2 * k)` by rw[HALF_TWICE] >> + `_ = 4 ** k` by rw[EXP_EXP_MULT] >> + metis_tac[lcm_run_lower_odd]); + +Theorem HALF_MULT_EVEN'[local] = ONCE_REWRITE_RULE [MULT_COMM] HALF_MULT_EVEN + +(* Theorem: EVEN n ==> HALF (n - 2) * HALF (HALF (2 ** n)) <= lcm_run n *) +(* Proof: + If n = 0, HALF (n - 2) = 0, so trivially true. + If n <> 0, + Let h = HALF n. + Then n = 2 * h by EVEN_HALF + Note h <> 0 by n <> 0 + so ?k. h = k + 1 by num_CASES, ADD1 + or n = 2 * k + 2 by n = 2 * (k + 1) + and HALF (HALF (2 ** n)) + = HALF (HALF (2 ** (2 * k + 2))) by above + = HALF (HALF (2 ** SUC (SUC (2 * k)))) by ADD1 + = HALF (HALF (2 * (2 * 2 ** (2 * k)))) by EXP + = 2 ** (2 * k) by HALF_TWICE + = 4 ** k by EXP_EXP_MULT + Also n - 2 = 2 * k by 0 < n, n = 2 * k + 2 + so HALF (n - 2) = k by HALF_TWICE + Since k * 4 ** k <= lcm_run (2 * (k + 1)) by lcm_run_lower_even + The result follows. +*) +Theorem lcm_run_even_lower: + !n. EVEN n ==> HALF (n - 2) * HALF (HALF (2 ** n)) <= lcm_run n +Proof + rpt strip_tac >> + Cases_on `n = 0` >- rw[] >> + qabbrev_tac `h = HALF n` >> + `n = 2 * h` by rw[EVEN_HALF, Abbr`h`] >> + `h <> 0` by rw[Abbr`h`] >> + `?k. h = k + 1` by metis_tac[num_CASES, ADD1] >> + `HALF (HALF (2 ** n)) = HALF (HALF (2 ** SUC (SUC (2 * k))))` by simp[ADD1] >> + `_ = HALF (HALF (2 * (2 * 2 ** (2 * k))))` by rw[EXP, HALF_MULT_EVEN'] >> + `_ = 2 ** (2 * k)` by rw[HALF_TWICE] >> + `_ = 4 ** k` by rw[EXP_EXP_MULT] >> + `n - 2 = 2 * k` by decide_tac >> + `HALF (n - 2) = k` by rw[HALF_TWICE] >> + metis_tac[lcm_run_lower_even] +QED + +(* Theorem: ODD n /\ 5 <= n ==> 2 ** n <= lcm_run n *) +(* Proof: + This follows by lcm_run_odd_lower + if we can show: 2 ** n <= HALF n * HALF (2 ** n) + + Note HALF 5 = 2 by arithmetic + and HALF 5 <= HALF n by DIV_LE_MONOTONE, 0 < 2 + Also n <> 0 by 5 <= n + so ?m. n = SUC m by num_CASES + HALF n * HALF (2 ** n) + = HALF n * HALF (2 * 2 ** m) by EXP + = HALF n * 2 ** m by HALF_TWICE + >= 2 * 2 ** m by LESS_MONO_MULT + = 2 ** (SUC m) by EXP + = 2 ** n by n = SUC m +*) +val lcm_run_odd_lower_alt = store_thm( + "lcm_run_odd_lower_alt", + ``!n. ODD n /\ 5 <= n ==> 2 ** n <= lcm_run n``, + rpt strip_tac >> + `2 ** n <= HALF n * HALF (2 ** n)` by + (`HALF 5 = 2` by EVAL_TAC >> + `HALF 5 <= HALF n` by rw[DIV_LE_MONOTONE] >> + `n <> 0` by decide_tac >> + `?m. n = SUC m` by metis_tac[num_CASES] >> + `HALF n * HALF (2 ** n) = HALF n * HALF (2 * 2 ** m)` by rw[EXP] >> + `_ = HALF n * 2 ** m` by rw[HALF_TWICE] >> + `2 * 2 ** m <= HALF n * 2 ** m` by rw[LESS_MONO_MULT] >> + rw[EXP]) >> + metis_tac[lcm_run_odd_lower, LESS_EQ_TRANS]); + +(* Theorem: EVEN n /\ 8 <= n ==> 2 ** n <= lcm_run n *) +(* Proof: + If n = 8, + Then 2 ** 8 = 256 by arithmetic + and lcm_run 8 = 840 by lcm_run_small + Thus true. + If n <> 8, + Note ODD 9 by arithmetic + so n <> 9 by ODD_EVEN + or 10 <= n by 8 <= n, n <> 9 + This follows by lcm_run_even_lower + if we can show: 2 ** n <= HALF (n - 2) * HALF (HALF (2 ** n)) + + Let m = n - 2. + Then 8 <= m by arithmetic + or HALF 8 <= HALF m by DIV_LE_MONOTONE, 0 < 2 + and HALF 8 = 4 = 2 * 2 by arithmetic + Now n = SUC (SUC m) by arithmetic + HALF m * HALF (HALF (2 ** n)) + = HALF m * HALF (HALF (2 ** (SUC (SUC m)))) by above + = HALF m * HALF (HALF (2 * (2 * 2 ** m))) by EXP + = HALF m * 2 ** m by HALF_TWICE + >= 4 * 2 ** m by LESS_MONO_MULT + = 2 * (2 * 2 ** m) by MULT_ASSOC + = 2 ** (SUC (SUC m)) by EXP + = 2 ** n by n = SUC (SUC m) +*) +Theorem lcm_run_even_lower_alt: + !n. EVEN n /\ 8 <= n ==> 2 ** n <= lcm_run n +Proof + rpt strip_tac >> + Cases_on `n = 8` >- rw[lcm_run_small] >> + `2 ** n <= HALF (n - 2) * HALF (HALF (2 ** n))` + by (`ODD 9` by rw[] >> + `n <> 9` by metis_tac[ODD_EVEN] >> + `8 <= n - 2` by decide_tac >> + qabbrev_tac `m = n - 2` >> + `n = SUC (SUC m)` by rw[Abbr`m`] >> + ‘HALF m * HALF (HALF (2 ** n)) = + HALF m * HALF (HALF (2 * (2 * 2 ** m)))’ by rw[EXP, HALF_MULT_EVEN'] >> + `_ = HALF m * 2 ** m` by rw[HALF_TWICE] >> + `HALF 8 <= HALF m` by rw[DIV_LE_MONOTONE] >> + `HALF 8 = 4` by EVAL_TAC >> + `2 * (2 * 2 ** m) <= HALF m * 2 ** m` by rw[LESS_MONO_MULT] >> + rw[EXP]) >> + metis_tac[lcm_run_even_lower, LESS_EQ_TRANS] +QED + +(* Theorem: 7 <= n ==> 2 ** n <= lcm_run n *) +(* Proof: + If EVEN n, + Node ODD 7 by arithmetic + so n <> 7 by EVEN_ODD + or 8 <= n by arithmetic + Hence true by lcm_run_even_lower_alt + If ~EVEN n, then ODD n by EVEN_ODD + Note 7 <= n ==> 5 <= n by arithmetic + Hence true by lcm_run_odd_lower_alt +*) +val lcm_run_lower_better = store_thm( + "lcm_run_lower_better", + ``!n. 7 <= n ==> 2 ** n <= lcm_run n``, + rpt strip_tac >> + `EVEN n \/ ODD n` by rw[EVEN_OR_ODD] >| [ + `ODD 7` by rw[] >> + `n <> 7` by metis_tac[ODD_EVEN] >> + rw[lcm_run_even_lower_alt], + rw[lcm_run_odd_lower_alt] + ]); + + +(* ------------------------------------------------------------------------- *) +(* Nair's Trick -- rework *) +(* ------------------------------------------------------------------------- *) + +(* +Picture: +leibniz_lcm_property |- !n. lcm_run (n + 1) = list_lcm (leibniz_horizontal n) +leibniz_horizontal_mem |- !n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) +so: +lcm_run (2*n + 1) = list_lcm (leibniz_horizontal (2*n)) +and leibniz_horizontal (2*n) has members: 0, 1, 2, ...., n, (n + 1), ....., (2*n) +note: n <= 2*n, always, (n+1) <= 2*n = (n+n) when 1 <= n. +thus: +Both B = (leibniz 2*n n) and C = (leibniz 2*n n+1) divides lcm_run (2*n + 1), + or (lcm B C) divides lcm_run (2*n + 1). +But (lcm B C) = (lcm B A) where A = (leibniz 2*n-1 n). +By leibniz_def |- !n k. leibniz n k = (n + 1) * binomial n k +By leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k + so B = (2*n + 1) * binomial 2*n n +and A = (2*n - n) * binomial 2*n n = n * binomial 2*n n +and lcm B A = lcm ((2*n + 1) * binomial 2*n n) (n * binomial 2*n n) + = (lcm (2*n + 1) n) * binomial 2*n n by LCM_COMMON_FACTOR + = n * (2*n + 1) * binomial 2*n n by coprime (2*n+1) n + = n * (leibniz 2*n n) by leibniz_def +*) + +(* Theorem: 0 < n ==> n * (leibniz (2 * n) n) divides lcm_run (2 * n + 1) *) +(* Proof: + Note 1 <= n by 0 < n + Let m = 2 * n, + Then n <= 2 * n = m, and + n + 1 <= n + n = m by arithmetic + Also coprime n (m + 1) by GCD_EUCLID + + Identify a triplet: + Let t = triplet (m - 1) n + Then t.a = leibniz (m - 1) n by triplet_def + t.b = leibniz m n by triplet_def + t.c = leibniz m (n + 1) by triplet_def + + Note MEM t.b (leibniz_horizontal m) by leibniz_horizontal_mem, n <= m + and MEM t.c (leibniz_horizontal m) by leibniz_horizontal_mem, n + 1 <= m + ==> lcm t.b t.c divides list_lcm (leibniz_horizontal m) by list_lcm_divisor_lcm_pair + = lcm_run (m + 1) by leibniz_lcm_property + + Let k = binomial m n. + lcm t.b t.c + = lcm t.b t.a by leibniz_triplet_lcm + = lcm ((m + 1) * k) t.a by leibniz_def + = lcm ((m + 1) * k) ((m - n) * k) by leibniz_up_alt + = lcm ((m + 1) * k) (n * k) by m = 2 * n + = n * (m + 1) * k by LCM_COMMON_COPRIME, LCM_SYM, coprime n (m + 1) + = n * leibniz m n by leibniz_def + Thus (n * leibniz m n) divides lcm_run (m + 1) +*) +val lcm_run_odd_factor = store_thm( + "lcm_run_odd_factor", + ``!n. 0 < n ==> n * (leibniz (2 * n) n) divides lcm_run (2 * n + 1)``, + rpt strip_tac >> + qabbrev_tac `m = 2 * n` >> + `n <= m /\ n + 1 <= m` by rw[Abbr`m`] >> + `coprime n (m + 1)` by rw[GCD_EUCLID, Abbr`m`] >> + qabbrev_tac `t = triplet (m - 1) n` >> + `t.a = leibniz (m - 1) n` by rw[triplet_def, Abbr`t`] >> + `t.b = leibniz m n` by rw[triplet_def, Abbr`t`] >> + `t.c = leibniz m (n + 1)` by rw[triplet_def, Abbr`t`] >> + `t.b divides lcm_run (m + 1)` by metis_tac[lcm_run_leibniz_divisor] >> + `t.c divides lcm_run (m + 1)` by metis_tac[lcm_run_leibniz_divisor] >> + `lcm t.b t.c divides lcm_run (m + 1)` by rw[LCM_IS_LEAST_COMMON_MULTIPLE] >> + qabbrev_tac `k = binomial m n` >> + `lcm t.b t.c = lcm t.b t.a` by rw[leibniz_triplet_lcm, Abbr`t`] >> + `_ = lcm ((m + 1) * k) ((m - n) * k)` by rw[leibniz_def, leibniz_up_alt, Abbr`k`] >> + `_ = lcm ((m + 1) * k) (n * k)` by rw[Abbr`m`] >> + `_ = n * (m + 1) * k` by rw[LCM_COMMON_COPRIME, LCM_SYM] >> + `_ = n * leibniz m n` by rw[leibniz_def, Abbr`k`] >> + metis_tac[]); + +(* Theorem: n * 4 ** n <= lcm_run (2 * n + 1) *) +(* Proof: + If n = 0, LHS = 0, trivially true. + If n <> 0, 0 < n. + Note 4 ** n <= leibniz (2 * n) n by leibniz_middle_lower + so n * 4 ** n <= n * leibniz (2 * n) n by LESS_MONO_MULT, MULT_COMM + Let k = n * leibniz (2 * n) n. + Then k divides lcm_run (2 * n + 1) by lcm_run_odd_factor + Now 0 < lcm_run (2 * n + 1) by lcm_run_pos + so k <= lcm_run (2 * n + 1) by DIVIDES_LE + Overall n * 4 ** n <= lcm_run (2 * n + 1) by LESS_EQ_TRANS +*) +Theorem lcm_run_lower_odd[allow_rebind]: + !n. n * 4 ** n <= lcm_run (2 * n + 1) +Proof + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + `0 < n` by decide_tac >> + `4 ** n <= leibniz (2 * n) n` by rw[leibniz_middle_lower] >> + `n * 4 ** n <= n * leibniz (2 * n) n` by rw[LESS_MONO_MULT, MULT_COMM] >> + `n * leibniz (2 * n) n <= lcm_run (2 * n + 1)` by rw[lcm_run_odd_factor, lcm_run_pos, DIVIDES_LE] >> + rw[LESS_EQ_TRANS] +QED + +(* Another direct proof of the same theorem *) + +(* Theorem: n * 4 ** n <= lcm_run (2 * n + 1) *) +(* Proof: + If n = 0, LHS = 0, trivially true. + If n <> 0, 0 < n, or 1 <= n by arithmetic + + Let m = 2 * n, + Then n <= 2 * n = m, and + n + 1 <= n + n = m by arithmetic, 1 <= n + Also coprime n (m + 1) by GCD_EUCLID + + Identify a triplet: + Let t = triplet (m - 1) n + Then t.a = leibniz (m - 1) n by triplet_def + t.b = leibniz m n by triplet_def + t.c = leibniz m (n + 1) by triplet_def + + Note MEM t.b (leibniz_horizontal m) by leibniz_horizontal_mem, n <= m + and MEM t.c (leibniz_horizontal m) by leibniz_horizontal_mem, n + 1 <= m + and POSITIVE (leibniz_horizontal m) by leibniz_horizontal_pos_alt + ==> lcm t.b t.c <= list_lcm (leibniz_horizontal m) by list_lcm_lower_by_lcm_pair + = lcm_run (m + 1) by leibniz_lcm_property + + Let k = binomial m n. + lcm t.b t.c + = lcm t.b t.a by leibniz_triplet_lcm + = lcm ((m + 1) * k) t.a by leibniz_def + = lcm ((m + 1) * k) ((m - n) * k) by leibniz_up_alt + = lcm ((m + 1) * k) (n * k) by m = 2 * n + = n * (m + 1) * k by LCM_COMMON_COPRIME, LCM_SYM, coprime n (m + 1) + = n * leibniz m n by leibniz_def + Thus (n * leibniz m n) divides lcm_run (m + 1) + + Note 4 ** n <= leibniz m n by leibniz_middle_lower + so n * 4 ** n <= n * leibniz m n by LESS_MONO_MULT, MULT_COMM + Overall n * 4 ** n <= lcm_run (2 * n + 1) by LESS_EQ_TRANS +*) +Theorem lcm_run_lower_odd[allow_rebind]: + !n. n * 4 ** n <= lcm_run (2 * n + 1) +Proof + rpt strip_tac >> + Cases_on ‘n = 0’ >- + rw[] >> + qabbrev_tac ‘m = 2 * n’ >> + ‘n <= m /\ n + 1 <= m’ by rw[Abbr‘m’] >> + ‘coprime n (m + 1)’ by rw[GCD_EUCLID, Abbr‘m’] >> + qabbrev_tac ‘t = triplet (m - 1) n’ >> + ‘t.a = leibniz (m - 1) n’ by rw[triplet_def, Abbr‘t’] >> + ‘t.b = leibniz m n’ by rw[triplet_def, Abbr‘t’] >> + ‘t.c = leibniz m (n + 1)’ by rw[triplet_def, Abbr‘t’] >> + ‘MEM t.b (leibniz_horizontal m)’ by metis_tac[leibniz_horizontal_mem] >> + ‘MEM t.c (leibniz_horizontal m)’ by metis_tac[leibniz_horizontal_mem] >> + ‘POSITIVE (leibniz_horizontal m)’ by metis_tac[leibniz_horizontal_pos_alt] >> + ‘lcm t.b t.c <= lcm_run (m + 1)’ by metis_tac[leibniz_lcm_property, list_lcm_lower_by_lcm_pair] >> + ‘lcm t.b t.c = n * leibniz m n’ by + (qabbrev_tac ‘k = binomial m n’ >> + ‘lcm t.b t.c = lcm t.b t.a’ by rw[leibniz_triplet_lcm, Abbr‘t’] >> + ‘_ = lcm ((m + 1) * k) ((m - n) * k)’ by rw[leibniz_def, leibniz_up_alt, Abbr‘k’] >> + ‘_ = lcm ((m + 1) * k) (n * k)’ by rw[Abbr‘m’] >> + ‘_ = n * (m + 1) * k’ by rw[LCM_COMMON_COPRIME, LCM_SYM] >> + ‘_ = n * leibniz m n’ by rw[leibniz_def, Abbr‘k’] >> + rw[]) >> + ‘4 ** n <= leibniz m n’ by rw[leibniz_middle_lower, Abbr‘m’] >> + ‘n * 4 ** n <= n * leibniz m n’ by rw[LESS_MONO_MULT] >> + metis_tac[LESS_EQ_TRANS] +QED + +(* Theorem: ODD n ==> (2 ** n <= lcm_run n <=> 5 <= n) *) +(* Proof: + If part: 2 ** n <= lcm_run n ==> 5 <= n + By contradiction, suppose n < 5. + By ODD n, n = 1 or n = 3. + If n = 1, LHS = 2 ** 1 = 2 by arithmetic + RHS = lcm_run 1 = 1 by lcm_run_1 + Hence false. + If n = 3, LHS = 2 ** 3 = 8 by arithmetic + RHS = lcm_run 3 = 6 by lcm_run_small + Hence false. + Only-if part: 5 <= n ==> 2 ** n <= lcm_run n + Let h = HALF n. + Then n = 2 * h + 1 by ODD_HALF + so 4 <= 2 * h by 5 - 1 = 4 + or 2 <= h by arithmetic + ==> 2 * 4 ** h <= h * 4 ** h by LESS_MONO_MULT + But 2 * 4 ** h + = 2 * (2 ** 2) ** h by arithmetic + = 2 * 2 ** (2 * h) by EXP_EXP_MULT + = 2 ** SUC (2 * h) by EXP + = 2 ** n by ADD1, n = 2 * h + 1 + With h * 4 ** h <= lcm_run n by lcm_run_lower_odd + or 2 ** n <= lcm_run n by LESS_EQ_TRANS +*) +val lcm_run_lower_odd_iff = store_thm( + "lcm_run_lower_odd_iff", + ``!n. ODD n ==> (2 ** n <= lcm_run n <=> 5 <= n)``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `n < 5` by decide_tac >> + `EVEN 0 /\ EVEN 2 /\ EVEN 4` by rw[] >> + `n <> 0 /\ n <> 2 /\ n <> 4` by metis_tac[EVEN_ODD] >> + `(n = 1) \/ (n = 3)` by decide_tac >- + fs[] >> + fs[lcm_run_small], + qabbrev_tac `h = HALF n` >> + `n = 2 * h + 1` by rw[ODD_HALF, Abbr`h`] >> + `2 * 4 ** h <= h * 4 ** h` by rw[] >> + `2 * 4 ** h = 2 * 2 ** (2 * h)` by rw[EXP_EXP_MULT] >> + `_ = 2 ** n` by rw[GSYM EXP] >> + `h * 4 ** h <= lcm_run n` by rw[lcm_run_lower_odd] >> + decide_tac + ]); + +(* Theorem: EVEN n ==> (2 ** n <= lcm_run n <=> (n = 0) \/ 8 <= n) *) +(* Proof: + If part: 2 ** n <= lcm_run n ==> (n = 0) \/ 8 <= n + By contradiction, suppose n <> 0 /\ n < 8. + By EVEN n, n = 2 or n = 4 or n = 6. + If n = 2, LHS = 2 ** 2 = 4 by arithmetic + RHS = lcm_run 2 = 2 by lcm_run_small + Hence false. + If n = 4, LHS = 2 ** 4 = 16 by arithmetic + RHS = lcm_run 4 = 12 by lcm_run_small + Hence false. + If n = 6, LHS = 2 ** 6 = 64 by arithmetic + RHS = lcm_run 6 = 60 by lcm_run_small + Hence false. + Only-if part: (n = 0) \/ 8 <= n ==> 2 ** n <= lcm_run n + If n = 0, LHS = 2 ** 0 = 1 by arithmetic + RHS = lcm_run 0 = 1 by lcm_run_0 + Hence true. + If n = 8, LHS = 2 ** 8 = 256 by arithmetic + RHS = lcm_run 8 = 840 by lcm_run_small + Hence true. + Otherwise, 10 <= n, since ODD 9. + Let h = HALF n, k = h - 1. + Then n = 2 * h by EVEN_HALF + = 2 * (k + 1) by k = h - 1 + = 2 * k + 2 by arithmetic + But lcm_run (2 * k + 1) <= lcm_run (2 * k + 2) by lcm_run_monotone + and k * 4 ** k <= lcm_run (2 * k + 1) by lcm_run_lower_odd + + Now 5 <= h by 10 <= h + so 4 <= k by k = h - 1 + ==> 4 * 4 ** k <= k * 4 ** k by LESS_MONO_MULT + + 4 * 4 ** k + = (2 ** 2) * (2 ** 2) ** k by arithmetic + = (2 ** 2) * (2 ** (2 * k)) by EXP_EXP_MULT + = 2 ** (2 * k + 2) by EXP_ADD + = 2 ** n by n = 2 * k + 2 + + Overall 2 ** n <= lcm_run n by LESS_EQ_TRANS +*) +val lcm_run_lower_even_iff = store_thm( + "lcm_run_lower_even_iff", + ``!n. EVEN n ==> (2 ** n <= lcm_run n <=> (n = 0) \/ 8 <= n)``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `n < 8` by decide_tac >> + `ODD 1 /\ ODD 3 /\ ODD 5 /\ ODD 7` by rw[] >> + `n <> 1 /\ n <> 3 /\ n <> 5 /\ n <> 7` by metis_tac[EVEN_ODD] >> + `(n = 2) \/ (n = 4) \/ (n = 6)` by decide_tac >- + fs[lcm_run_small] >- + fs[lcm_run_small] >> + fs[lcm_run_small], + fs[lcm_run_0], + Cases_on `n = 8` >- + rw[lcm_run_small] >> + `ODD 9` by rw[] >> + `n <> 9` by metis_tac[EVEN_ODD] >> + `10 <= n` by decide_tac >> + qabbrev_tac `h = HALF n` >> + `n = 2 * h` by rw[EVEN_HALF, Abbr`h`] >> + qabbrev_tac `k = h - 1` >> + `lcm_run (2 * k + 1) <= lcm_run (2 * k + 1 + 1)` by rw[lcm_run_monotone] >> + `2 * k + 1 + 1 = n` by rw[Abbr`k`] >> + `k * 4 ** k <= lcm_run (2 * k + 1)` by rw[lcm_run_lower_odd] >> + `4 * 4 ** k <= k * 4 ** k` by rw[Abbr`k`] >> + `4 * 4 ** k = 2 ** 2 * 2 ** (2 * k)` by rw[EXP_EXP_MULT] >> + `_ = 2 ** (2 * k + 2)` by rw[GSYM EXP_ADD] >> + `_ = 2 ** n` by rw[] >> + metis_tac[LESS_EQ_TRANS] + ]); + +(* Theorem: 2 ** n <= lcm_run n <=> (n = 0) \/ (n = 5) \/ 7 <= n *) +(* Proof: + If EVEN n, + Then n <> 5, n <> 7, so 8 <= n by arithmetic + Thus true by lcm_run_lower_even_iff + If ~EVEN n, then ODD n by EVEN_ODD + Then n <> 0, n <> 6, so 5 <= n by arithmetic + Thus true by lcm_run_lower_odd_iff +*) +val lcm_run_lower_better_iff = store_thm( + "lcm_run_lower_better_iff", + ``!n. 2 ** n <= lcm_run n <=> (n = 0) \/ (n = 5) \/ 7 <= n``, + rpt strip_tac >> + Cases_on `EVEN n` >| [ + `ODD 5 /\ ODD 7` by rw[] >> + `n <> 5 /\ n <> 7` by metis_tac[EVEN_ODD] >> + metis_tac[lcm_run_lower_even_iff, DECIDE``8 <= n <=> (7 <= n /\ n <> 7)``], + `EVEN 0 /\ EVEN 6` by rw[] >> + `ODD n /\ n <> 0 /\ n <> 6` by metis_tac[EVEN_ODD] >> + metis_tac[lcm_run_lower_odd_iff, DECIDE``5 <= n <=> (n = 5) \/ (n = 6) \/ (7 <= n)``] + ]); + +(* This is the ultimate goal! *) + +(* ------------------------------------------------------------------------- *) +(* Nair's Trick - using consecutive LCM *) +(* ------------------------------------------------------------------------- *) + +(* Define the consecutive LCM function *) +val lcm_upto_def = Define` + (lcm_upto 0 = 1) /\ + (lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n)) +`; + +(* Extract theorems from definition *) +val lcm_upto_0 = save_thm("lcm_upto_0", lcm_upto_def |> CONJUNCT1); +(* val lcm_upto_0 = |- lcm_upto 0 = 1: thm *) + +val lcm_upto_SUC = save_thm("lcm_upto_SUC", lcm_upto_def |> CONJUNCT2); +(* val lcm_upto_SUC = |- !n. lcm_upto (SUC n) = lcm (SUC n) (lcm_upto n): thm *) + +(* Theorem: (lcm_upto 0 = 1) /\ (!n. lcm_upto (n+1) = lcm (n+1) (lcm_upto n)) *) +(* Proof: by lcm_upto_def *) +val lcm_upto_alt = store_thm( + "lcm_upto_alt", + ``(lcm_upto 0 = 1) /\ (!n. lcm_upto (n+1) = lcm (n+1) (lcm_upto n))``, + metis_tac[lcm_upto_def, ADD1]); + +(* Theorem: lcm_upto 1 = 1 *) +(* Proof: + lcm_upto 1 + = lcm_upto (SUC 0) by ONE + = lcm (SUC 0) (lcm_upto 0) by lcm_upto_SUC + = lcm (SUC 0) 1 by lcm_upto_0 + = lcm 1 1 by ONE + = 1 by LCM_REF +*) +val lcm_upto_1 = store_thm( + "lcm_upto_1", + ``lcm_upto 1 = 1``, + metis_tac[lcm_upto_def, LCM_REF, ONE]); + +(* Theorem: lcm_upto n for small n *) +(* Proof: by evaluation. *) +val lcm_upto_small = store_thm( + "lcm_upto_small", + ``(lcm_upto 2 = 2) /\ (lcm_upto 3 = 6) /\ (lcm_upto 4 = 12) /\ + (lcm_upto 5 = 60) /\ (lcm_upto 6 = 60) /\ (lcm_upto 7 = 420) /\ + (lcm_upto 8 = 840) /\ (lcm_upto 9 = 2520) /\ (lcm_upto 10 = 2520)``, + EVAL_TAC); + +(* Theorem: lcm_upto n = list_lcm [1 .. n] *) +(* Proof: + By induction on n. + Base: lcm_upto 0 = list_lcm [1 .. 0] + lcm_upto 0 + = 1 by lcm_upto_0 + = list_lcm [] by list_lcm_nil + = list_lcm [1 .. 0] by listRangeINC_EMPTY + Step: lcm_upto n = list_lcm [1 .. n] ==> lcm_upto (SUC n) = list_lcm [1 .. SUC n] + lcm_upto (SUC n) + = lcm (SUC n) (lcm_upto n) by lcm_upto_SUC + = lcm (SUC n) (list_lcm [1 .. n]) by induction hypothesis + = list_lcm (SNOC (SUC n) [1 .. n]) by list_lcm_snoc + = list_lcm [1 .. (SUC n)] by listRangeINC_SNOC, ADD1, 1 <= n + 1 +*) +val lcm_upto_eq_list_lcm = store_thm( + "lcm_upto_eq_list_lcm", + ``!n. lcm_upto n = list_lcm [1 .. n]``, + Induct >- + rw[lcm_upto_0, list_lcm_nil, listRangeINC_EMPTY] >> + rw[lcm_upto_SUC, list_lcm_snoc, listRangeINC_SNOC, ADD1]); + +(* Theorem: 2 ** n <= lcm_upto (n + 1) *) +(* Proof: + lcm_upto (n + 1) + = list_lcm [1 .. (n + 1)] by lcm_upto_eq_list_lcm + >= 2 ** n by lcm_lower_bound +*) +val lcm_upto_lower = store_thm( + "lcm_upto_lower", + ``!n. 2 ** n <= lcm_upto (n + 1)``, + rw[lcm_upto_eq_list_lcm, lcm_lower_bound]); + +(* Theorem: 0 < lcm_upto (n + 1) *) +(* Proof: + lcm_upto (n + 1) + >= 2 ** n by lcm_upto_lower + > 0 by EXP_POS, 0 < 2 +*) +val lcm_upto_pos = store_thm( + "lcm_upto_pos", + ``!n. 0 < lcm_upto (n + 1)``, + metis_tac[lcm_upto_lower, EXP_POS, LESS_LESS_EQ_TRANS, DECIDE``0 < 2``]); + +(* Theorem: (n + 1) divides lcm_upto (n + 1) /\ (lcm_upto n) divides lcm_upto (n + 1) *) +(* Proof: + Note lcm_upto (n + 1) = lcm (n + 1) (lcm_upto n) by lcm_upto_alt + so (n + 1) divides lcm_upto (n + 1) + and (lcm_upto n) divides lcm_upto (n + 1) by LCM_DIVISORS +*) +val lcm_upto_divisors = store_thm( + "lcm_upto_divisors", + ``!n. (n + 1) divides lcm_upto (n + 1) /\ (lcm_upto n) divides lcm_upto (n + 1)``, + rw[lcm_upto_alt, LCM_DIVISORS]); + +(* Theorem: lcm_upto n <= lcm_upto (n + 1) *) +(* Proof: + Note (lcm_upto n) divides lcm_upto (n + 1) by lcm_upto_divisors + and 0 < lcm_upto (n + 1) by lcm_upto_pos + so lcm_upto n <= lcm_upto (n + 1) by DIVIDES_LE +*) +val lcm_upto_monotone = store_thm( + "lcm_upto_monotone", + ``!n. lcm_upto n <= lcm_upto (n + 1)``, + rw[lcm_upto_divisors, lcm_upto_pos, DIVIDES_LE]); + +(* Theorem: k <= n ==> (leibniz n k) divides lcm_upto (n + 1) *) +(* Proof: + Note (leibniz n k) divides list_lcm (leibniz_vertical n) by leibniz_vertical_divisor + ==> (leibniz n k) divides list_lcm [1 .. (n + 1)] by notation + or (leibniz n k) divides lcm_upto (n + 1) by lcm_upto_eq_list_lcm +*) +val lcm_upto_leibniz_divisor = store_thm( + "lcm_upto_leibniz_divisor", + ``!n k. k <= n ==> (leibniz n k) divides lcm_upto (n + 1)``, + metis_tac[leibniz_vertical_divisor, lcm_upto_eq_list_lcm]); + +(* Theorem: n * 4 ** n <= lcm_upto (2 * n + 1) *) +(* Proof: + If n = 0, LHS = 0, trivially true. + If n <> 0, 0 < n. + Let m = 2 * n. + + Claim: (m + 1) * binomial m n divides lcm_upto (m + 1) [1] + Proof: Note n <= m by LESS_MONO_MULT, 1 <= 2 + ==> (leibniz m n) divides lcm_upto (m + 1) by lcm_upto_leibniz_divisor, n <= m + or (m + 1) * binomial m n divides lcm_upto (m + 1) by leibniz_def + + Claim: n * binomial m n divides lcm_upto (m + 1) [2] + Proof: Note (lcm_upto m) divides lcm_upto (m + 1) by lcm_upto_divisors + Also 0 < m /\ n <= m - 1 by 0 < n + and m - 1 + 1 = m by 0 < m + Thus (leibniz (m - 1) n) divides lcm_upto m by lcm_upto_leibniz_divisor, n <= m - 1 + or (leibniz (m - 1) n) divides lcm_upto (m + 1) by DIVIDES_TRANS + and leibniz (m - 1) n + = (m - n) * binomial m n by leibniz_up_alt + = n * binomial m n by m - n = n + + Note coprime n (m + 1) by GCD_EUCLID, GCD_1, 1 < n + Thus lcm (n * binomial m n) ((m + 1) * binomial m n) + = n * (m + 1) * binomial m n by LCM_COMMON_COPRIME + = n * ((m + 1) * binomial m n) by MULT_ASSOC + = n * leibniz m n by leibniz_def + ==> n * leibniz m n divides lcm_upto (m + 1) by LCM_DIVIDES, [1], [2] + Note 0 < lcm_upto (m + 1) by lcm_upto_pos + or n * leibniz m n <= lcm_upto (m + 1) by DIVIDES_LE, 0 < lcm_upto (m + 1) + Now 4 ** n <= leibniz m n by leibniz_middle_lower + so n * 4 ** n <= n * leibniz m n by LESS_MONO_MULT, MULT_COMM + or n * 4 ** n <= lcm_upto (m + 1) by LESS_EQ_TRANS +*) +val lcm_upto_lower_odd = store_thm( + "lcm_upto_lower_odd", + ``!n. n * 4 ** n <= lcm_upto (2 * n + 1)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + `0 < n` by decide_tac >> + qabbrev_tac `m = 2 * n` >> + `(m + 1) * binomial m n divides lcm_upto (m + 1)` by + (`n <= m` by rw[Abbr`m`] >> + metis_tac[lcm_upto_leibniz_divisor, leibniz_def]) >> + `n * binomial m n divides lcm_upto (m + 1)` by + (`(lcm_upto m) divides lcm_upto (m + 1)` by rw[lcm_upto_divisors] >> + `0 < m /\ n <= m - 1` by rw[Abbr`m`] >> + `m - 1 + 1 = m` by decide_tac >> + `(leibniz (m - 1) n) divides lcm_upto m` by metis_tac[lcm_upto_leibniz_divisor] >> + `(leibniz (m - 1) n) divides lcm_upto (m + 1)` by metis_tac[DIVIDES_TRANS] >> + `leibniz (m - 1) n = (m - n) * binomial m n` by rw[leibniz_up_alt] >> + `_ = n * binomial m n` by rw[Abbr`m`] >> + metis_tac[]) >> + `coprime n (m + 1)` by rw[GCD_EUCLID, Abbr`m`] >> + `lcm (n * binomial m n) ((m + 1) * binomial m n) = n * (m + 1) * binomial m n` by rw[LCM_COMMON_COPRIME] >> + `_ = n * leibniz m n` by rw[leibniz_def, MULT_ASSOC] >> + `n * leibniz m n divides lcm_upto (m + 1)` by metis_tac[LCM_DIVIDES] >> + `n * leibniz m n <= lcm_upto (m + 1)` by rw[DIVIDES_LE, lcm_upto_pos] >> + `4 ** n <= leibniz m n` by rw[leibniz_middle_lower, Abbr`m`] >> + metis_tac[LESS_MONO_MULT, MULT_COMM, LESS_EQ_TRANS]); + +(* Theorem: n * 4 ** n <= lcm_upto (2 * (n + 1)) *) +(* Proof: + lcm_upto (2 * (n + 1)) + = lcm_upto (2 * n + 2) by arithmetic + >= lcm_upto (2 * n + 1) by lcm_upto_monotone + >= n * 4 ** n by lcm_upto_lower_odd +*) +val lcm_upto_lower_even = store_thm( + "lcm_upto_lower_even", + ``!n. n * 4 ** n <= lcm_upto (2 * (n + 1))``, + rpt strip_tac >> + `2 * (n + 1) = 2 * n + 1 + 1` by decide_tac >> + metis_tac[lcm_upto_monotone, lcm_upto_lower_odd, LESS_EQ_TRANS]); + +(* Theorem: 7 <= n ==> 2 ** n <= lcm_upto n *) +(* Proof: + If ODD n, ?k. n = SUC (2 * k) by ODD_EXISTS, + When 5 <= 7 <= n = 2 * k + 1 by ADD1 + 2 <= k by arithmetic + and lcm_upto n + = lcm_upto (2 * k + 1) by notation + >= k * 4 ** k by lcm_upto_lower_odd + >= 2 * 4 ** k by k >= 2, LESS_MONO_MULT + = 2 * 2 ** (2 * k) by EXP_EXP_MULT + = 2 ** SUC (2 * k) by EXP + = 2 ** n by n = SUC (2 * k) + If EVEN n, ?m. n = 2 * m by EVEN_EXISTS + Note ODD 7 /\ ODD 9 by arithmetic + If n = 8, + LHS = 2 ** 8 = 256, + RHS = lcm_upto 8 = 840 by lcm_upto_small + Hence true. + Otherwise, 10 <= n by 7 <= n, n <> 7, n <> 8, n <> 9 + Since 0 < n, 0 < m by MULT_EQ_0 + so ?k. m = SUC k by num_CASES + When 10 <= n = 2 * (k + 1) by ADD1 + 4 <= k by arithmetic + and lcm_upto n + = lcm_upto (2 * (k + 1)) by notation + >= k * 4 ** k by lcm_upto_lower_even + >= 4 * 4 ** k by k >= 4, LESS_MONO_MULT + = 4 ** SUC k by EXP + = 4 ** m by notation + = 2 ** (2 * m) by EXP_EXP_MULT + = 2 ** n by n = 2 * m +*) +val lcm_upto_lower_better = store_thm( + "lcm_upto_lower_better", + ``!n. 7 <= n ==> 2 ** n <= lcm_upto n``, + rpt strip_tac >> + Cases_on `ODD n` >| [ + `?k. n = SUC (2 * k)` by rw[GSYM ODD_EXISTS] >> + `2 <= k` by decide_tac >> + `2 * 4 ** k <= k * 4 ** k` by rw[LESS_MONO_MULT] >> + `lcm_upto n = lcm_upto (2 * k + 1)` by rw[ADD1] >> + `2 ** n = 2 * 2 ** (2 * k)` by rw[EXP] >> + `_ = 2 * 4 ** k` by rw[EXP_EXP_MULT] >> + metis_tac[lcm_upto_lower_odd, LESS_EQ_TRANS], + `ODD 7 /\ ODD 9` by rw[] >> + `EVEN n /\ n <> 7 /\ n <> 9` by metis_tac[ODD_EVEN] >> + `?m. n = 2 * m` by rw[GSYM EVEN_EXISTS] >> + `m <> 0` by decide_tac >> + `?k. m = SUC k` by metis_tac[num_CASES] >> + Cases_on `n = 8` >- + rw[lcm_upto_small] >> + `4 <= k` by decide_tac >> + `4 * 4 ** k <= k * 4 ** k` by rw[LESS_MONO_MULT] >> + `lcm_upto n = lcm_upto (2 * (k + 1))` by rw[ADD1] >> + `2 ** n = 4 ** m` by rw[EXP_EXP_MULT] >> + `_ = 4 * 4 ** k` by rw[EXP] >> + metis_tac[lcm_upto_lower_even, LESS_EQ_TRANS] + ]); + +(* This is a very significant result. *) + +(* ------------------------------------------------------------------------- *) +(* Simple LCM lower bounds -- rework *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: HALF (n + 1) <= lcm_run n *) +(* Proof: + If n = 0, + LHS = HALF 1 = 0 by arithmetic + RHS = lcm_run 0 = 1 by lcm_run_0 + Hence true. + If n <> 0, 0 < n. + Let l = [1 .. n]. + Then l <> [] by listRangeINC_NIL, n <> 0 + so EVERY_POSITIVE l by listRangeINC_EVERY + lcm_run n + = list_lcm l by notation + >= (SUM l) DIV (LENGTH l) by list_lcm_nonempty_lower, l <> [] + = (SUM l) DIV n by listRangeINC_LEN + = (HALF (n * (n + 1))) DIV n by sum_1_to_n_eqn + = HALF ((n * (n + 1)) DIV n) by DIV_DIV_DIV_MULT, 0 < 2, 0 < n + = HALF (n + 1) by MULT_TO_DIV +*) +val lcm_run_lower_simple = store_thm( + "lcm_run_lower_simple", + ``!n. HALF (n + 1) <= lcm_run n``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[lcm_run_0] >> + qabbrev_tac `l = [1 .. n]` >> + `l <> []` by rw[listRangeINC_NIL, Abbr`l`] >> + `EVERY_POSITIVE l` by rw[listRangeINC_EVERY, Abbr`l`] >> + `(SUM l) DIV (LENGTH l) = (SUM l) DIV n` by rw[listRangeINC_LEN, Abbr`l`] >> + `_ = (HALF (n * (n + 1))) DIV n` by rw[sum_1_to_n_eqn, Abbr`l`] >> + `_ = HALF ((n * (n + 1)) DIV n)` by rw[DIV_DIV_DIV_MULT] >> + `_ = HALF (n + 1)` by rw[MULT_TO_DIV] >> + metis_tac[list_lcm_nonempty_lower]); + +(* This is a simple result, good but not very useful. *) + +(* Theorem: lcm_run n = list_lcm (leibniz_vertical (n - 1)) *) +(* Proof: + If n = 0, + Then n - 1 + 1 = 0 - 1 + 1 = 1 + but lcm_run 0 = 1 = lcm_run 1, hence true. + If n <> 0, + Then n - 1 + 1 = n, hence true trivially. +*) +val lcm_run_alt = store_thm( + "lcm_run_alt", + ``!n. lcm_run n = list_lcm (leibniz_vertical (n - 1))``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[lcm_run_0, lcm_run_1] >> + rw[]); + +(* Theorem: 2 ** (n - 1) <= lcm_run n *) +(* Proof: + If n = 0, + LHS = HALF 1 = 0 by arithmetic + RHS = lcm_run 0 = 1 by lcm_run_0 + Hence true. + If n <> 0, 0 < n, or 1 <= n. + Let l = leibniz_horizontal (n - 1). + Then LENGTH l = n by leibniz_horizontal_len + so l <> [] by LENGTH_NIL, n <> 0 + and EVERY_POSITIVE l by leibniz_horizontal_pos + lcm_run n + = list_lcm (leibniz_vertical (n - 1)) by lcm_run_alt + = list_lcm l by leibniz_lcm_property + >= (SUM l) DIV (LENGTH l) by list_lcm_nonempty_lower, l <> [] + = 2 ** (n - 1) by leibniz_horizontal_average_eqn +*) +val lcm_run_lower_good = store_thm( + "lcm_run_lower_good", + ``!n. 2 ** (n - 1) <= lcm_run n``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[lcm_run_0] >> + `0 < n /\ 1 <= n /\ (n - 1 + 1 = n)` by decide_tac >> + qabbrev_tac `l = leibniz_horizontal (n - 1)` >> + `lcm_run n = list_lcm l` by metis_tac[leibniz_lcm_property] >> + `LENGTH l = n` by metis_tac[leibniz_horizontal_len] >> + `l <> []` by metis_tac[LENGTH_NIL] >> + `EVERY_POSITIVE l` by rw[leibniz_horizontal_pos, Abbr`l`] >> + metis_tac[list_lcm_nonempty_lower, leibniz_horizontal_average_eqn]); + +(* ------------------------------------------------------------------------- *) +(* Upper Bound by Leibniz Triangle *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: leibniz n k = (n + 1 - k) * binomial (n + 1) k *) +(* Proof: by leibniz_up_alt: +leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k +*) +val leibniz_eqn = store_thm( + "leibniz_eqn", + ``!n k. leibniz n k = (n + 1 - k) * binomial (n + 1) k``, + rw[GSYM leibniz_up_alt]); + +(* Theorem: leibniz n (k + 1) = (n - k) * binomial (n + 1) (k + 1) *) +(* Proof: by leibniz_up_alt: +leibniz_up_alt |- !n. 0 < n ==> !k. leibniz (n - 1) k = (n - k) * binomial n k +*) +val leibniz_right_alt = store_thm( + "leibniz_right_alt", + ``!n k. leibniz n (k + 1) = (n - k) * binomial (n + 1) (k + 1)``, + metis_tac[leibniz_up_alt, DECIDE``0 < n + 1 /\ (n + 1 - 1 = n) /\ (n + 1 - (k + 1) = n - k)``]); + +(* Leibniz Stack: + \ + \ + \ + \ + (L k k) <-- boundary of Leibniz Triangle + | \ |-- (m - k) = distance + | k <= m <= n <-- m + | \ (n - k) = height, or max distance + | binomial (n+1) (m+1) is at south-east of binomial n m + | \ + | \ + n-th row: ....... (L n k) ................. + +leibniz_binomial_identity +|- !m n k. k <= m /\ m <= n ==> (leibniz n k * binomial (n - k) (m - k) = leibniz m k * binomial (n + 1) (m + 1)) +This says: (leibniz n k) at bottom is related to a stack entry (leibniz m k). +leibniz_divides_leibniz_factor +|- !m n k. k <= m /\ m <= n ==> leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) +This is just a corollary of leibniz_binomial_identity, by divides_def. + +leibniz_horizontal_member_divides +|- !m n x. n <= TWICE m + 1 /\ m <= n /\ MEM x (leibniz_horizontal n) ==> + x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) +This says: for the n-th row, q = list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) + is a common multiple of all members of the n-th row when n <= TWICE m + 1 /\ m <= n +That means, for the n-th row, pick any m-th row for HALF (n - 1) <= m <= n +Compute its list_lcm (leibniz_horizontal m), then multiply by binomial (n + 1) (m + 1) as q. +This value q is a common multiple of all members in n-th row. +The proof goes through all members of n-th row, i.e. (L n k) for k <= n. +To apply leibniz_binomial_identity, the condition is k <= m, not k <= n. +Since m has been picked (between HALF n and n), divide k into two parts: k <= m, m < k <= n. +For the first part, apply leibniz_binomial_identity. +For the second part, use symmetry L n (n - k) = L n k, then apply leibniz_binomial_identity. +With k <= m, m <= n, we apply leibniz_binomial_identity: +(1) Each member x = leibniz n k divides p = leibniz m k * binomial (n + 1) (m + 1), stack member with a factor. +(2) But leibniz m k is a member of (leibniz_horizontal m) +(3) Thus leibniz m k divides list_lcm (leibniz_horizontal m), the stack member divides its row list_lcm + ==> p divides q by multiplying both by binomial (n + 1) (m + 1) +(4) Hence x divides q. +With the other half by symmetry, all members x divides q. +Corollary 1: +lcm_run_divides_property +|- !m n. n <= TWICE m /\ m <= n ==> lcm_run n divides binomial n m * lcm_run m +This follows by list_lcm_is_least_common_multiple and leibniz_lcm_property. +Corollary 2: +lcm_run_bound_recurrence +|- !m n. n <= TWICE m /\ m <= n ==> lcm_run n <= lcm_run m * binomial n m +Then lcm_run_upper_bound |- !n. lcm_run n <= 4 ** n follows by complete induction on n. +*) + +(* Theorem: k <= m /\ m <= n ==> + ((leibniz n k) * (binomial (n - k) (m - k)) = (leibniz m k) * (binomial (n + 1) (m + 1))) *) +(* Proof: + leibniz n k * (binomial (n - k) (m - k)) + = (n + 1) * (binomial n k) * (binomial (n - k) (m - k)) by leibniz_def + n! (n - k)! + = (n + 1) * ------------- * ------------------ binomial formula + k! (n - k)! (m - k)! (n - m)! + n! 1 + = (n + 1) * -------------- * ------------------ cancel (n - k)! + k! 1 (m - k)! (n - m)! + n! (m + 1)! + = (n + 1) * -------------- * ------------------ replace by (m + 1)! + k! (m + 1)! (m - k)! (n - m)! + (n + 1)! m! + = (m + 1) * -------------- * ------------------ merge and split factorials + k! (m + 1)! (m - k)! (n - m)! + m! (n + 1)! + = (m + 1) * -------------- * ------------------ binomial formula + k! (m - k)! (m + 1)! (n - m)! + = leibniz m k * binomial (n + 1) (m + 1) by leibniz_def +*) +val leibniz_binomial_identity = store_thm( + "leibniz_binomial_identity", + ``!m n k. k <= m /\ m <= n ==> + ((leibniz n k) * (binomial (n - k) (m - k)) = (leibniz m k) * (binomial (n + 1) (m + 1)))``, + rw[leibniz_def] >> + `m + 1 <= n + 1` by decide_tac >> + `m - k <= n - k` by decide_tac >> + `(n - k) - (m - k) = n - m` by decide_tac >> + `(n + 1) - (m + 1) = n - m` by decide_tac >> + `FACT m = binomial m k * (FACT (m - k) * FACT k)` by rw[binomial_formula2] >> + `FACT (n + 1) = binomial (n + 1) (m + 1) * (FACT (n - m) * FACT (m + 1))` by metis_tac[binomial_formula2] >> + `FACT n = binomial n k * (FACT (n - k) * FACT k)` by rw[binomial_formula2] >> + `FACT (n - k) = binomial (n - k) (m - k) * (FACT (n - m) * FACT (m - k))` by metis_tac[binomial_formula2] >> + `!n. FACT (n + 1) = (n + 1) * FACT n` by metis_tac[FACT, ADD1] >> + `FACT (n + 1) = FACT (n - m) * (FACT k * (FACT (m - k) * ((m + 1) * (binomial m k) * (binomial (n + 1) (m + 1)))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> + `FACT (n + 1) = FACT (n - m) * (FACT k * (FACT (m - k) * ((n + 1) * (binomial n k) * (binomial (n - k) (m - k)))))` by metis_tac[MULT_ASSOC, MULT_COMM] >> + metis_tac[MULT_LEFT_CANCEL, FACT_LESS, NOT_ZERO_LT_ZERO]); + +(* Theorem: k <= m /\ m <= n ==> leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) *) +(* Proof: + Note leibniz m k * binomial (n + 1) (m + 1) + = leibniz n k * binomial (n - k) (m - k) by leibniz_binomial_identity + Thus leibniz n k divides leibniz m k * binomial (n + 1) (m + 1) + by divides_def, MULT_COMM +*) +val leibniz_divides_leibniz_factor = store_thm( + "leibniz_divides_leibniz_factor", + ``!m n k. k <= m /\ m <= n ==> leibniz n k divides leibniz m k * binomial (n + 1) (m + 1)``, + metis_tac[leibniz_binomial_identity, divides_def, MULT_COMM]); + +(* Theorem: n <= 2 * m + 1 /\ m <= n /\ MEM x (leibniz_horizontal n) ==> + x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) *) +(* Proof: + Let q = list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1). + Note MEM x (leibniz_horizontal n) + ==> ?k. k <= n /\ (x = leibniz n k) by leibniz_horizontal_member + Here the picture is: + HALF n ... m .... n + 0 ........ k .......... n + We need k <= m to get x divides q, by applying leibniz_divides_leibniz_factor. + For m < k <= n, we shall use symmetry to get x divides q. + If k <= m, + Let p = (leibniz m k) * binomial (n + 1) (m + 1). + Then x divides p by leibniz_divides_leibniz_factor, k <= m, m <= n + and MEM (leibniz m k) (leibniz_horizontal m) by leibniz_horizontal_member, k <= m + ==> (leibniz m k) divides list_lcm (leibniz_horizontal m) by list_lcm_is_common_multiple + so (leibniz m k) * binomial (n + 1) (m + 1) + divides + list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) by DIVIDES_CANCEL, binomial_pos + or p divides q by notation + Thus x divides q by DIVIDES_TRANS + If ~(k <= m), then m < k. + Note x = leibniz n (n - k) by leibniz_sym, k <= n + Now n <= m + m + 1 by given n <= 2 * m + 1 + so n - k <= m + m + 1 - k by arithmetic + and m + m + 1 - k <= m by m < k, so m + 1 <= k + or n - k <= m by LESS_EQ_TRANS + Let j = n - k, p = (leibniz m j) * binomial (n + 1) (m + 1). + Then x divides p by leibniz_divides_leibniz_factor, j <= m, m <= n + and MEM (leibniz m j) (leibniz_horizontal m) by leibniz_horizontal_member, j <= m + ==> (leibniz m j) divides list_lcm (leibniz_horizontal m) by list_lcm_is_common_multiple + so (leibniz m j) * binomial (n + 1) (m + 1) + divides + list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1) by DIVIDES_CANCEL, binomial_pos + or p divides q by notation + Thus x divides q by DIVIDES_TRANS +*) +val leibniz_horizontal_member_divides = store_thm( + "leibniz_horizontal_member_divides", + ``!m n x. n <= 2 * m + 1 /\ m <= n /\ MEM x (leibniz_horizontal n) ==> + x divides list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1)``, + rpt strip_tac >> + qabbrev_tac `q = list_lcm (leibniz_horizontal m) * binomial (n + 1) (m + 1)` >> + `?k. k <= n /\ (x = leibniz n k)` by rw[GSYM leibniz_horizontal_member] >> + Cases_on `k <= m` >| [ + qabbrev_tac `p = (leibniz m k) * binomial (n + 1) (m + 1)` >> + `x divides p` by rw[leibniz_divides_leibniz_factor, Abbr`p`] >> + `MEM (leibniz m k) (leibniz_horizontal m)` by metis_tac[leibniz_horizontal_member] >> + `(leibniz m k) divides list_lcm (leibniz_horizontal m)` by rw[list_lcm_is_common_multiple] >> + `p divides q` by rw[GSYM DIVIDES_CANCEL, binomial_pos, Abbr`p`, Abbr`q`] >> + metis_tac[DIVIDES_TRANS], + `n - k <= m` by decide_tac >> + qabbrev_tac `j = n - k` >> + `x = leibniz n j` by rw[Once leibniz_sym, Abbr`j`] >> + qabbrev_tac `p = (leibniz m j) * binomial (n + 1) (m + 1)` >> + `x divides p` by rw[leibniz_divides_leibniz_factor, Abbr`p`] >> + `MEM (leibniz m j) (leibniz_horizontal m)` by metis_tac[leibniz_horizontal_member] >> + `(leibniz m j) divides list_lcm (leibniz_horizontal m)` by rw[list_lcm_is_common_multiple] >> + `p divides q` by rw[GSYM DIVIDES_CANCEL, binomial_pos, Abbr`p`, Abbr`q`] >> + metis_tac[DIVIDES_TRANS] + ]); + +(* Theorem: n <= 2 * m /\ m <= n ==> (lcm_run n) divides (lcm_run m) * binomial n m *) +(* Proof: + If n = 0, + Then lcm_run 0 = 1 by lcm_run_0 + Hence true by ONE_DIVIDES_ALL + If n <> 0, + Then 0 < n, and 0 < m by n <= 2 * m + Thus m - 1 <= n - 1 by m <= n + and n - 1 <= 2 * m - 1 by n <= 2 * m + = 2 * (m - 1) + 1 + Thus !x. MEM x (leibniz_horizontal (n - 1)) ==> + x divides list_lcm (leibniz_horizontal (m - 1)) * binomial n m + by leibniz_horizontal_member_divides + ==> list_lcm (leibniz_horizontal (n - 1)) divides + list_lcm (leibniz_horizontal (m - 1)) * binomial n m + by list_lcm_is_least_common_multiple + But lcm_run n = leibniz_horizontal (n - 1) by leibniz_lcm_property + and lcm_run m = leibniz_horizontal (m - 1) by leibniz_lcm_property + list_lcm (leibniz_horizontal h) divides q by list_lcm_is_least_common_multiple + Thus (lcm_run n) divides (lcm_run m) * binomial n m by above +*) +val lcm_run_divides_property = store_thm( + "lcm_run_divides_property", + ``!m n. n <= 2 * m /\ m <= n ==> (lcm_run n) divides (lcm_run m) * binomial n m``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[lcm_run_0] >> + `0 < n` by decide_tac >> + `0 < m` by decide_tac >> + `m - 1 <= n - 1` by decide_tac >> + `n - 1 <= 2 * (m - 1) + 1` by decide_tac >> + `(n - 1 + 1 = n) /\ (m - 1 + 1 = m)` by decide_tac >> + metis_tac[leibniz_horizontal_member_divides, list_lcm_is_least_common_multiple, leibniz_lcm_property]); + +(* Theorem: n <= 2 * m /\ m <= n ==> (lcm_run n) <= (lcm_run m) * binomial n m *) +(* Proof: + Note 0 < lcm_run m by lcm_run_pos + and 0 < binomial n m by binomial_pos + so 0 < lcm_run m * binomial n m by MULT_EQ_0 + Now (lcm_run n) divides (lcm_run m) * binomial n m by lcm_run_divides_property + Thus (lcm_run n) <= (lcm_run m) * binomial n m by DIVIDES_LE +*) +val lcm_run_bound_recurrence = store_thm( + "lcm_run_bound_recurrence", + ``!m n. n <= 2 * m /\ m <= n ==> (lcm_run n) <= (lcm_run m) * binomial n m``, + rpt strip_tac >> + `0 < lcm_run m * binomial n m` by metis_tac[lcm_run_pos, binomial_pos, MULT_EQ_0, NOT_ZERO_LT_ZERO] >> + rw[lcm_run_divides_property, DIVIDES_LE]); + +(* Theorem: lcm_run n <= 4 ** n *) +(* Proof: + By complete induction on n. + If EVEN n, + Base: n = 0. + LHS = lcm_run 0 = 1 by lcm_run_0 + RHS = 4 ** 0 = 1 by EXP + Hence true. + Step: n <> 0 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n + Let m = HALF n, c = lcm_run m * binomial n m. + Then n = 2 * m by EVEN_HALF + so m <= 2 * m = n by arithmetic + ==> lcm_run n <= c by lcm_run_bound_recurrence, m <= n + But m <> 0 by n <> 0 + so m < n by arithmetic + Now c = lcm_run m * binomial n m by notation + <= 4 ** m * binomial n m by induction hypothesis, m < n + <= 4 ** m * 4 ** m by binomial_middle_upper_bound + = 4 ** (m + m) by EXP_ADD + = 4 ** n by TIMES2, n = 2 * m + Hence lcm_run n <= 4 ** n. + If ~EVEN n, + Then ODD n by EVEN_ODD + Base: n = 1. + LHS = lcm_run 1 = 1 by lcm_run_1 + RHS = 4 ** 1 = 4 by EXP + Hence true. + Step: n <> 1 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n + Let m = HALF n, c = lcm_run (m + 1) * binomial n (m + 1). + Then n = 2 * m + 1 by ODD_HALF + and 0 < m by n <> 1 + and m + 1 <= 2 * m + 1 = n by arithmetic + ==> (lcm_run n) <= c by lcm_run_bound_recurrence, m + 1 <= n + But m + 1 <> n by m <> 0 + so m + 1 < n by m + 1 <> n + Now c = lcm_run (m + 1) * binomial n (m + 1) by notation + <= 4 ** (m + 1) * binomial n (m + 1) by induction hypothesis, m + 1 < n + = 4 ** (m + 1) * binomial n m by binomial_sym, n - (m + 1) = m + <= 4 ** (m + 1) * 4 ** m by binomial_middle_upper_bound + = 4 ** m * 4 ** (m + 1) by arithmetic + = 4 ** (m + (m + 1)) by EXP_ADD + = 4 ** (2 * m + 1) by arithmetic + = 4 ** n by n = 2 * m + 1 + Hence lcm_run n <= 4 ** n. +*) +val lcm_run_upper_bound = store_thm( + "lcm_run_upper_bound", + ``!n. lcm_run n <= 4 ** n``, + completeInduct_on `n` >> + Cases_on `EVEN n` >| [ + Cases_on `n = 0` >- + rw[lcm_run_0] >> + qabbrev_tac `m = HALF n` >> + `n = 2 * m` by rw[EVEN_HALF, Abbr`m`] >> + qabbrev_tac `c = lcm_run m * binomial n m` >> + `lcm_run n <= c` by rw[lcm_run_bound_recurrence, Abbr`c`] >> + `lcm_run m <= 4 ** m` by rw[] >> + `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> + `c <= 4 ** m * 4 ** m` by rw[LESS_MONO_MULT2, Abbr`c`] >> + `4 ** m * 4 ** m = 4 ** n` by metis_tac[EXP_ADD, TIMES2] >> + decide_tac, + `ODD n` by metis_tac[EVEN_ODD] >> + Cases_on `n = 1` >- + rw[lcm_run_1] >> + qabbrev_tac `m = HALF n` >> + `n = 2 * m + 1` by rw[ODD_HALF, Abbr`m`] >> + qabbrev_tac `c = lcm_run (m + 1) * binomial n (m + 1)` >> + `lcm_run n <= c` by rw[lcm_run_bound_recurrence, Abbr`c`] >> + `lcm_run (m + 1) <= 4 ** (m + 1)` by rw[] >> + `binomial n (m + 1) = binomial n m` by rw[Once binomial_sym] >> + `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> + `c <= 4 ** (m + 1) * 4 ** m` by rw[LESS_MONO_MULT2, Abbr`c`] >> + `4 ** (m + 1) * 4 ** m = 4 ** n` by metis_tac[MULT_COMM, EXP_ADD, ADD_ASSOC, TIMES2] >> + decide_tac + ]); + +(* This is a milestone theorem. *) + +(* ------------------------------------------------------------------------- *) +(* Beta Triangle *) +(* ------------------------------------------------------------------------- *) + +(* Define beta triangle *) +(* Use temp_overload so that beta is invisibe outside: +val beta_def = Define` + beta n k = k * (binomial n k) +`; +*) +val _ = temp_overload_on ("beta", ``\n k. k * (binomial n k)``); (* for temporary overloading *) +(* can use overload, but then hard to print and change the appearance of too many theorem? *) + +(* + +Pascal's Triangle (k <= n) +n = 0 1 = binomial 0 0 +n = 1 1 1 +n = 2 1 2 1 +n = 3 1 3 3 1 +n = 4 1 4 6 4 1 +n = 5 1 5 10 10 5 1 +n = 6 1 6 15 20 15 6 1 + +Beta Triangle (0 < k <= n) +n = 1 1 = 1 * (1) = leibniz_horizontal 0 +n = 2 2 2 = 2 * (1 1) = leibniz_horizontal 1 +n = 3 3 6 3 = 3 * (1 2 1) = leibniz_horizontal 2 +n = 4 4 12 12 4 = 4 * (1 3 3 1) = leibniz_horizontal 3 +n = 5 5 20 30 20 5 = 5 * (1 4 6 4 1) = leibniz_horizontal 4 +n = 6 6 30 60 60 30 6 = 6 * (1 5 10 10 5 1) = leibniz_horizontal 5 + +> EVAL ``let n = 10 in let k = 6 in (beta (n+1) (k+1) = leibniz n k)``; --> T +> EVAL ``let n = 10 in let k = 4 in (beta (n+1) (k+1) = leibniz n k)``; --> T +> EVAL ``let n = 10 in let k = 3 in (beta (n+1) (k+1) = leibniz n k)``; --> T + +*) + +(* Theorem: beta 0 n = 0 *) +(* Proof: + beta 0 n + = n * (binomial 0 n) by notation + = n * (if n = 0 then 1 else 0) by binomial_0_n + = 0 +*) +val beta_0_n = store_thm( + "beta_0_n", + ``!n. beta 0 n = 0``, + rw[binomial_0_n]); + +(* Theorem: beta n 0 = 0 *) +(* Proof: by notation *) +val beta_n_0 = store_thm( + "beta_n_0", + ``!n. beta n 0 = 0``, + rw[]); + +(* Theorem: n < k ==> (beta n k = 0) *) +(* Proof: by notation, binomial_less_0 *) +val beta_less_0 = store_thm( + "beta_less_0", + ``!n k. n < k ==> (beta n k = 0)``, + rw[binomial_less_0]); + +(* Theorem: beta (n + 1) (k + 1) = leibniz n k *) +(* Proof: + If k <= n, then k + 1 <= n + 1 by arithmetic + beta (n + 1) (k + 1) + = (k + 1) binomial (n + 1) (k + 1) by notation + = (k + 1) (n + 1)! / (k + 1)! (n - k)! by binomial_formula2 + = (n + 1) n! / k! (n - k)! by factorial composing and decomposing + = (n + 1) * binomial n k by binomial_formula2 + = leibniz_horizontal n k by leibniz_def + If ~(k <= n), then n < k /\ n + 1 < k + 1 by arithmetic + Then beta (n + 1) (k + 1) = 0 by beta_less_0 + and leibniz n k = 0 by leibniz_less_0 + Hence true. +*) +val beta_eqn = store_thm( + "beta_eqn", + ``!n k. beta (n + 1) (k + 1) = leibniz n k``, + rpt strip_tac >> + Cases_on `k <= n` >| [ + `(n + 1) - (k + 1) = n - k` by decide_tac >> + `k + 1 <= n + 1` by decide_tac >> + `FACT (n - k) * FACT k * beta (n + 1) (k + 1) = FACT (n - k) * FACT k * ((k + 1) * binomial (n + 1) (k + 1))` by rw[] >> + `_ = FACT (n - k) * FACT (k + 1) * binomial (n + 1) (k + 1)` by metis_tac[FACT, ADD1, MULT_ASSOC, MULT_COMM] >> + `_ = FACT (n + 1)` by metis_tac[binomial_formula2, MULT_ASSOC, MULT_COMM] >> + `_ = (n + 1) * FACT n` by metis_tac[FACT, ADD1] >> + `_ = FACT (n - k) * FACT k * ((n + 1) * binomial n k)` by metis_tac[binomial_formula2, MULT_ASSOC, MULT_COMM] >> + `_ = FACT (n - k) * FACT k * (leibniz n k)` by rw[leibniz_def] >> + `FACT k <> 0 /\ FACT (n - k) <> 0` by metis_tac[FACT_LESS, NOT_ZERO_LT_ZERO] >> + metis_tac[EQ_MULT_LCANCEL, MULT_ASSOC], + rw[beta_less_0, leibniz_less_0] + ]); + +(* Theorem: 0 < n /\ 0 < k ==> (beta n k = leibniz (n - 1) (k - 1)) *) +(* Proof: by beta_eqn *) +val beta_alt = store_thm( + "beta_alt", + ``!n k. 0 < n /\ 0 < k ==> (beta n k = leibniz (n - 1) (k - 1))``, + rw[GSYM beta_eqn]); + +(* Theorem: 0 < k /\ k <= n ==> 0 < beta n k *) +(* Proof: + 0 < beta n k + <=> beta n k <> 0 by NOT_ZERO_LT_ZERO + <=> k * (binomial n k) <> 0 by notation + <=> k <> 0 /\ binomial n k <> 0 by MULT_EQ_0 + <=> k <> 0 /\ k <= n by binomial_pos + <=> 0 < k /\ k <= n by NOT_ZERO_LT_ZERO +*) +val beta_pos = store_thm( + "beta_pos", + ``!n k. 0 < k /\ k <= n ==> 0 < beta n k``, + metis_tac[MULT_EQ_0, binomial_pos, NOT_ZERO_LT_ZERO]); + +(* Theorem: (beta n k = 0) <=> (k = 0) \/ n < k *) +(* Proof: + beta n k = 0 + <=> k * (binomial n k) = 0 by notation + <=> (k = 0) \/ (binomial n k = 0) by MULT_EQ_0 + <=> (k = 0) \/ (n < k) by binomial_eq_0 +*) +val beta_eq_0 = store_thm( + "beta_eq_0", + ``!n k. (beta n k = 0) <=> (k = 0) \/ n < k``, + rw[binomial_eq_0]); + +(* +binomial_sym |- !n k. k <= n ==> (binomial n k = binomial n (n - k)) +leibniz_sym |- !n k. k <= n ==> (leibniz n k = leibniz n (n - k)) +*) + +(* Theorem: k <= n ==> (beta n k = beta n (n - k + 1)) *) +(* Proof: + If k = 0, + Then beta n 0 = 0 by beta_n_0 + and beta n (n + 1) = 0 by beta_less_0 + Hence true. + If k <> 0, then 0 < k + Thus 0 < n by k <= n + beta n k + = leibniz (n - 1) (k - 1) by beta_alt + = leibniz (n - 1) (n - k) by leibniz_sym + = leibniz (n - 1) (n - k + 1 - 1) by arithmetic + = beta n (n - k + 1) by beta_alt +*) +val beta_sym = store_thm( + "beta_sym", + ``!n k. k <= n ==> (beta n k = beta n (n - k + 1))``, + rpt strip_tac >> + Cases_on `k = 0` >- + rw[beta_n_0, beta_less_0] >> + rw[beta_alt, Once leibniz_sym]); + +(* ------------------------------------------------------------------------- *) +(* Beta Horizontal List *) +(* ------------------------------------------------------------------------- *) + +(* +> EVAL ``leibniz_horizontal 3``; --> [4; 12; 12; 4] +> EVAL ``GENLIST (beta 4) 5``; --> [0; 4; 12; 12; 4] +> EVAL ``TL (GENLIST (beta 4) 5)``; --> [4; 12; 12; 4] +*) + +(* Use overloading for a row of beta n k, k = 1 to n. *) +(* val _ = overload_on("beta_horizontal", ``\n. TL (GENLIST (beta n) (n + 1))``); *) +(* use a direct GENLIST rather than tail of a GENLIST *) +val _ = temp_overload_on("beta_horizontal", ``\n. GENLIST (beta n o SUC) n``); (* for temporary overloading *) + +(* +> EVAL ``leibniz_horizontal 5``; --> [6; 30; 60; 60; 30; 6] +> EVAL ``beta_horizontal 6``; --> [6; 30; 60; 60; 30; 6] +*) + +(* Theorem: beta_horizontal 0 = [] *) +(* Proof: + beta_horizontal 0 + = GENLIST (beta 0 o SUC) 0 by notation + = [] by GENLIST +*) +val beta_horizontal_0 = store_thm( + "beta_horizontal_0", + ``beta_horizontal 0 = []``, + rw[]); + +(* Theorem: LENGTH (beta_horizontal n) = n *) +(* Proof: + LENGTH (beta_horizontal n) + = LENGTH (GENLIST (beta n o SUC) n) by notation + = n by LENGTH_GENLIST +*) +val beta_horizontal_len = store_thm( + "beta_horizontal_len", + ``!n. LENGTH (beta_horizontal n) = n``, + rw[]); + +(* Theorem: beta_horizontal (n + 1) = leibniz_horizontal n *) +(* Proof: + Note beta_horizontal (n + 1) = GENLIST ((beta (n + 1) o SUC)) (n + 1) by notation + and leibniz_horizontal n = GENLIST (leibniz n) (n + 1) by notation + Now (beta (n + 1)) o SUC) k + = beta (n + 1) (k + 1) by ADD1 + = leibniz n k by beta_eqn + Thus beta_horizontal (n + 1) = leibniz_horizontal n by GENLIST_FUN_EQ +*) +val beta_horizontal_eqn = store_thm( + "beta_horizontal_eqn", + ``!n. beta_horizontal (n + 1) = leibniz_horizontal n``, + rw[GENLIST_FUN_EQ, beta_eqn, ADD1]); + +(* Theorem: 0 < n ==> (beta_horizontal n = leibniz_horizontal (n - 1)) *) +(* Proof: by beta_horizontal_eqn *) +val beta_horizontal_alt = store_thm( + "beta_horizontal_alt", + ``!n. 0 < n ==> (beta_horizontal n = leibniz_horizontal (n - 1))``, + metis_tac[beta_horizontal_eqn, DECIDE``0 < n ==> (n - 1 + 1 = n)``]); + +(* Theorem: 0 < k /\ k <= n ==> MEM (beta n k) (beta_horizontal n) *) +(* Proof: + By MEM_GENLIST, this is to show: + ?m. m < n /\ (beta n k = beta n (SUC m)) + Since k <> 0, k = SUC m, + and SUC m = k <= n ==> m < n by arithmetic + So take this m, and the result follows. +*) +val beta_horizontal_mem = store_thm( + "beta_horizontal_mem", + ``!n k. 0 < k /\ k <= n ==> MEM (beta n k) (beta_horizontal n)``, + rpt strip_tac >> + rw[MEM_GENLIST] >> + `?m. k = SUC m` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> + `m < n` by decide_tac >> + metis_tac[]); + +(* too weak: +binomial_horizontal_mem |- !n k. k < n + 1 ==> MEM (binomial n k) (binomial_horizontal n) +leibniz_horizontal_mem |- !n k. k <= n ==> MEM (leibniz n k) (leibniz_horizontal n) +*) + +(* Theorem: MEM (beta n k) (beta_horizontal n) <=> 0 < k /\ k <= n *) +(* Proof: + By MEM_GENLIST, this is to show: + (?m. m < n /\ (beta n k = beta n (SUC m))) <=> 0 < k /\ k <= n + If part: (?m. m < n /\ (beta n k = beta n (SUC m))) ==> 0 < k /\ k <= n + By contradiction, suppose k = 0 \/ n < k + Note SUC m <> 0 /\ ~(n < SUC m) by m < n + Thus beta n (SUC m) <> 0 by beta_eq_0 + or beta n k <> 0 by beta n k = beta n (SUC m) + ==> (k <> 0) /\ ~(n < k) by beta_eq_0 + This contradicts k = 0 \/ n < k. + Only-if part: 0 < k /\ k <= n ==> ?m. m < n /\ (beta n k = beta n (SUC m)) + Note k <> 0, so ?m. k = SUC m by num_CASES + and SUC m <= n <=> m < n by LESS_EQ + so Take this m, and the result follows. +*) +val beta_horizontal_mem_iff = store_thm( + "beta_horizontal_mem_iff", + ``!n k. MEM (beta n k) (beta_horizontal n) <=> 0 < k /\ k <= n``, + rw[MEM_GENLIST] >> + rewrite_tac[EQ_IMP_THM] >> + strip_tac >| [ + spose_not_then strip_assume_tac >> + `SUC m <> 0 /\ ~(n < SUC m)` by decide_tac >> + `(k <> 0) /\ ~(n < k)` by metis_tac[beta_eq_0] >> + decide_tac, + strip_tac >> + `?m. k = SUC m` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> + metis_tac[LESS_EQ] + ]); + +(* Theorem: MEM x (beta_horizontal n) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k) *) +(* Proof: + By MEM_GENLIST, this is to show: + (?m. m < n /\ (x = beta n (SUC m))) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k) + Since 0 < k /\ k <= n <=> ?m. (k = SUC m) /\ m < n by num_CASES, LESS_EQ + This is trivially true. +*) +val beta_horizontal_member = store_thm( + "beta_horizontal_member", + ``!n x. MEM x (beta_horizontal n) <=> ?k. 0 < k /\ k <= n /\ (x = beta n k)``, + rw[MEM_GENLIST] >> + metis_tac[num_CASES, NOT_ZERO_LT_ZERO, SUC_NOT_ZERO, LESS_EQ]); + +(* Theorem: k < n ==> (EL k (beta_horizontal n) = beta n (k + 1)) *) +(* Proof: by EL_GENLIST, ADD1 *) +val beta_horizontal_element = store_thm( + "beta_horizontal_element", + ``!n k. k < n ==> (EL k (beta_horizontal n) = beta n (k + 1))``, + rw[EL_GENLIST, ADD1]); + +(* Theorem: 0 < n ==> (lcm_run n = list_lcm (beta_horizontal n)) *) +(* Proof: + Note n <> 0 + ==> n = SUC k for some k by num_CASES + or n = k + 1 by ADD1 + lcm_run n + = lcm_run (k + 1) + = list_lcm (leibniz_horizontal k) by leibniz_lcm_property + = list_lcm (beta_horizontal n) by beta_horizontal_eqn +*) +val lcm_run_by_beta_horizontal = store_thm( + "lcm_run_by_beta_horizontal", + ``!n. 0 < n ==> (lcm_run n = list_lcm (beta_horizontal n))``, + metis_tac[leibniz_lcm_property, beta_horizontal_eqn, num_CASES, ADD1, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < k /\ k <= n ==> (beta n k) divides lcm_run n *) +(* Proof: + Note 0 < n by 0 < k /\ k <= n + and MEM (beta n k) (beta_horizontal n) by beta_horizontal_mem + also lcm_run n = list_lcm (beta_horizontal n) by lcm_run_by_beta_horizontal, 0 < n + Thus (beta n k) divides lcm_run n by list_lcm_is_common_multiple +*) +val lcm_run_beta_divisor = store_thm( + "lcm_run_beta_divisor", + ``!n k. 0 < k /\ k <= n ==> (beta n k) divides lcm_run n``, + rw[beta_horizontal_mem, lcm_run_by_beta_horizontal, list_lcm_is_common_multiple]); + +(* Theorem: k <= m /\ m <= n ==> (beta n k) divides (beta m k) * (binomial n m) *) +(* Proof: + Note (binomial m k) * (binomial n m) + = (binomial n k) * (binomial (n - k) (m - k)) by binomial_product_identity + Thus binomial n k divides binomial m k * binomial n m by divides_def, MULT_COMM + ==> k * binomial n k divides k * (binomial m k * binomial n m) by DIVIDES_CANCEL_COMM + = (k * binomial m k) * binomial n m by MULT_ASSOC + or (beta n k) divides (beta m k) * (binomial n m) by notation +*) +val beta_divides_beta_factor = store_thm( + "beta_divides_beta_factor", + ``!m n k. k <= m /\ m <= n ==> (beta n k) divides (beta m k) * (binomial n m)``, + rw[] >> + `binomial n k divides binomial m k * binomial n m` by metis_tac[binomial_product_identity, divides_def, MULT_COMM] >> + metis_tac[DIVIDES_CANCEL_COMM, MULT_ASSOC]); + +(* Theorem: n <= 2 * m /\ m <= n ==> (lcm_run n) divides (binomial n m) * (lcm_run m) *) +(* Proof: + If n = 0, + Then lcm_run 0 = 1 by lcm_run_0 + Hence true by ONE_DIVIDES_ALL + If n <> 0, then 0 < n. + Let q = (binomial n m) * (lcm_run m) + + Claim: !x. MEM x (beta_horizontal n) ==> x divides q + Proof: Note MEM x (beta_horizontal n) + ==> ?k. 0 < k /\ k <= n /\ (x = beta n k) by beta_horizontal_member + Here the picture is: + HALF n ... m .... n + 0 ........ k ........... n + We need k <= m to get x divides q. + For m < k <= n, we shall use symmetry to get x divides q. + If k <= m, + Let p = (beta m k) * (binomial n m). + Then x divides p by beta_divides_beta_factor, k <= m, m <= n + and (beta m k) divides lcm_run m by lcm_run_beta_divisor, 0 < k /\ k <= m + so (beta m k) * (binomial n m) + divides + (lcm_run m) * (binomial n m) by DIVIDES_CANCEL, binomial_pos + or p divides q by MULT_COMM + Thus x divides q by DIVIDES_TRANS + If ~(k <= m), then m < k. + Note x = beta n (n - k + 1) by beta_sym, k <= n + Now n <= m + m by given + so n - k + 1 <= m + m + 1 - k by arithmetic + and m + m + 1 - k <= m by m < k + ==> n - k + 1 <= m by arithmetic + Let h = n - k + 1, p = (beta m h) * (binomial n m). + Then x divides p by beta_divides_beta_factor, h <= m, m <= n + and (beta m h) divides lcm_run m by lcm_run_beta_divisor, 0 < h /\ h <= m + so (beta m h) * (binomial n m) + divides + (lcm_run m) * (binomial n m) by DIVIDES_CANCEL, binomial_pos + or p divides q by MULT_COMM + Thus x divides q by DIVIDES_TRANS + + Therefore, + (list_lcm (beta_horizontal n)) divides q by list_lcm_is_least_common_multiple, Claim + or (lcm_run n) divides q by lcm_run_by_beta_horizontal, 0 < n +*) +val lcm_run_divides_property_alt = store_thm( + "lcm_run_divides_property_alt", + ``!m n. n <= 2 * m /\ m <= n ==> (lcm_run n) divides (binomial n m) * (lcm_run m)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[lcm_run_0] >> + `0 < n` by decide_tac >> + qabbrev_tac `q = (binomial n m) * (lcm_run m)` >> + `!x. MEM x (beta_horizontal n) ==> x divides q` by + (rpt strip_tac >> + `?k. 0 < k /\ k <= n /\ (x = beta n k)` by rw[GSYM beta_horizontal_member] >> + Cases_on `k <= m` >| [ + qabbrev_tac `p = (beta m k) * (binomial n m)` >> + `x divides p` by rw[beta_divides_beta_factor, Abbr`p`] >> + `(beta m k) divides lcm_run m` by rw[lcm_run_beta_divisor] >> + `p divides q` by metis_tac[DIVIDES_CANCEL, MULT_COMM, binomial_pos] >> + metis_tac[DIVIDES_TRANS], + `x = beta n (n - k + 1)` by rw[Once beta_sym] >> + `n - k + 1 <= m` by decide_tac >> + qabbrev_tac `h = n - k + 1` >> + qabbrev_tac `p = (beta m h) * (binomial n m)` >> + `x divides p` by rw[beta_divides_beta_factor, Abbr`p`] >> + `(beta m h) divides lcm_run m` by rw[lcm_run_beta_divisor, Abbr`h`] >> + `p divides q` by metis_tac[DIVIDES_CANCEL, MULT_COMM, binomial_pos] >> + metis_tac[DIVIDES_TRANS] + ]) >> + `(list_lcm (beta_horizontal n)) divides q` by metis_tac[list_lcm_is_least_common_multiple] >> + metis_tac[lcm_run_by_beta_horizontal]); + +(* This is the original lcm_run_divides_property to give lcm_run_upper_bound. *) + +(* Theorem: lcm_run n <= 4 ** n *) +(* Proof: + By complete induction on n. + If EVEN n, + Base: n = 0. + LHS = lcm_run 0 = 1 by lcm_run_0 + RHS = 4 ** 0 = 1 by EXP + Hence true. + Step: n <> 0 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n + Let m = HALF n, c = binomial n m * lcm_run m. + Then n = 2 * m by EVEN_HALF + so m <= 2 * m = n by arithmetic + Note 0 < binomial n m by binomial_pos, m <= n + and 0 < lcm_run m by lcm_run_pos + ==> 0 < c by MULT_EQ_0 + Thus (lcm_run n) divides c by lcm_run_divides_property, m <= n + or lcm_run n + <= c by DIVIDES_LE, 0 < c + = (binomial n m) * lcm_run m by notation + <= (binomial n m) * 4 ** m by induction hypothesis, m < n + <= 4 ** m * 4 ** m by binomial_middle_upper_bound + = 4 ** (m + m) by EXP_ADD + = 4 ** n by TIMES2, n = 2 * m + Hence lcm_run n <= 4 ** n. + If ~EVEN n, + Then ODD n by EVEN_ODD + Base: n = 1. + LHS = lcm_run 1 = 1 by lcm_run_1 + RHS = 4 ** 1 = 4 by EXP + Hence true. + Step: n <> 1 /\ !m. m < n ==> lcm_run m <= 4 ** m ==> lcm_run n <= 4 ** n + Let m = HALF n, c = binomial n (m + 1) * lcm_run (m + 1). + Then n = 2 * m + 1 by ODD_HALF + and 0 < m by n <> 1 + and m + 1 <= 2 * m + 1 = n by arithmetic + But m + 1 <> n by m <> 0 + so m + 1 < n by m + 1 <> n + Note 0 < binomial n (m + 1) by binomial_pos, m + 1 <= n + and 0 < lcm_run (m + 1) by lcm_run_pos + ==> 0 < c by MULT_EQ_0 + Thus (lcm_run n) divides c by lcm_run_divides_property, 0 < m + 1, m + 1 <= n + or lcm_run n + <= c by DIVIDES_LE, 0 < c + = (binomial n (m + 1)) * lcm_run (m + 1) by notation + <= (binomial n (m + 1)) * 4 ** (m + 1) by induction hypothesis, m + 1 < n + = (binomial n m) * 4 ** (m + 1) by binomial_sym, n - (m + 1) = m + <= 4 ** m * 4 ** (m + 1) by binomial_middle_upper_bound + = 4 ** (m + (m + 1)) by EXP_ADD + = 4 ** (2 * m + 1) by arithmetic + = 4 ** n by n = 2 * m + 1 + Hence lcm_run n <= 4 ** n. +*) +Theorem lcm_run_upper_bound[allow_rebind]: + !n. lcm_run n <= 4 ** n +Proof + completeInduct_on `n` >> + Cases_on `EVEN n` >| [ + Cases_on `n = 0` >- + rw[lcm_run_0] >> + qabbrev_tac `m = HALF n` >> + `n = 2 * m` by rw[EVEN_HALF, Abbr`m`] >> + qabbrev_tac `c = binomial n m * lcm_run m` >> + `m <= n` by decide_tac >> + `0 < c` by metis_tac[binomial_pos, lcm_run_pos, MULT_EQ_0, NOT_ZERO_LT_ZERO] >> + `lcm_run n <= c` by rw[lcm_run_divides_property, DIVIDES_LE, Abbr`c`] >> + `lcm_run m <= 4 ** m` by rw[] >> + `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> + `c <= 4 ** m * 4 ** m` by rw[LESS_MONO_MULT2, Abbr`c`] >> + `4 ** m * 4 ** m = 4 ** n` by metis_tac[EXP_ADD, TIMES2] >> + decide_tac, + `ODD n` by metis_tac[EVEN_ODD] >> + Cases_on `n = 1` >- + rw[lcm_run_1] >> + qabbrev_tac `m = HALF n` >> + `n = 2 * m + 1` by rw[ODD_HALF, Abbr`m`] >> + `0 < m` by rw[] >> + qabbrev_tac `c = binomial n (m + 1) * lcm_run (m + 1)` >> + `m + 1 <= n` by decide_tac >> + `0 < c` by metis_tac[binomial_pos, lcm_run_pos, MULT_EQ_0, NOT_ZERO_LT_ZERO] >> + `lcm_run n <= c` by rw[lcm_run_divides_property, DIVIDES_LE, Abbr`c`] >> + `lcm_run (m + 1) <= 4 ** (m + 1)` by rw[] >> + `binomial n (m + 1) = binomial n m` by rw[Once binomial_sym] >> + `binomial n m <= 4 ** m` by metis_tac[binomial_middle_upper_bound] >> + `c <= 4 ** m * 4 ** (m + 1)` by rw[LESS_MONO_MULT2, Abbr`c`] >> + `4 ** m * 4 ** (m + 1) = 4 ** n` by metis_tac[EXP_ADD, ADD_ASSOC, TIMES2] >> + decide_tac + ] +QED + +(* This is the original proof of the upper bound. *) + +(* ------------------------------------------------------------------------- *) +(* LCM Lower Bound using Maximum *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: POSITIVE l ==> MAX_LIST l <= list_lcm l *) +(* Proof: + If l = [], + Note MAX_LIST [] = 0 by MAX_LIST_NIL + and list_lcm [] = 1 by list_lcm_nil + Hence true. + If l <> [], + Let x = MAX_LIST l. + Then MEM x l by MAX_LIST_MEM + and x divides (list_lcm l) by list_lcm_is_common_multiple + Now 0 < list_lcm l by list_lcm_pos, EVERY_MEM + so x <= list_lcm l by DIVIDES_LE, 0 < list_lcm l +*) +val list_lcm_ge_max = store_thm( + "list_lcm_ge_max", + ``!l. POSITIVE l ==> MAX_LIST l <= list_lcm l``, + rpt strip_tac >> + Cases_on `l = []` >- + rw[MAX_LIST_NIL, list_lcm_nil] >> + `MEM (MAX_LIST l) l` by rw[MAX_LIST_MEM] >> + `0 < list_lcm l` by rw[list_lcm_pos, EVERY_MEM] >> + rw[list_lcm_is_common_multiple, DIVIDES_LE]); + +(* Theorem: (n + 1) * binomial n (HALF n) <= list_lcm [1 .. (n + 1)] *) +(* Proof: + Note !k. MEM k (binomial_horizontal n) ==> 0 < k by binomial_horizontal_pos_alt [1] + + list_lcm [1 .. (n + 1)] + = list_lcm (leibniz_vertical n) by notation + = list_lcm (leibniz_horizontal n) by leibniz_lcm_property + = (n + 1) * list_lcm (binomial_horizontal n) by leibniz_horizontal_lcm_alt + >= (n + 1) * MAX_LIST (binomial_horizontal n) by list_lcm_ge_max, [1], LE_MULT_LCANCEL + = (n + 1) * binomial n (HALF n) by binomial_horizontal_max +*) +val lcm_lower_bound_by_list_lcm = store_thm( + "lcm_lower_bound_by_list_lcm", + ``!n. (n + 1) * binomial n (HALF n) <= list_lcm [1 .. (n + 1)]``, + rpt strip_tac >> + `MAX_LIST (binomial_horizontal n) <= list_lcm (binomial_horizontal n)` by + (irule list_lcm_ge_max >> + metis_tac[binomial_horizontal_pos_alt]) >> + `list_lcm (leibniz_vertical n) = list_lcm (leibniz_horizontal n)` by rw[leibniz_lcm_property] >> + `_ = (n + 1) * list_lcm (binomial_horizontal n)` by rw[leibniz_horizontal_lcm_alt] >> + `n + 1 <> 0` by decide_tac >> + metis_tac[LE_MULT_LCANCEL, binomial_horizontal_max]); + +(* Theorem: FINITE s /\ (!x. x IN s ==> 0 < x) ==> MAX_SET s <= big_lcm s *) +(* Proof: + If s = {}, + Note MAX_SET {} = 0 by MAX_SET_EMPTY + and big_lcm {} = 1 by big_lcm_empty + Hence true. + If s <> {}, + Let x = MAX_SET s. + Then x IN s by MAX_SET_IN_SET + and x divides (big_lcm s) by big_lcm_is_common_multiple + Now 0 < big_lcm s by big_lcm_positive + so x <= big_lcm s by DIVIDES_LE, 0 < big_lcm s +*) +val big_lcm_ge_max = store_thm( + "big_lcm_ge_max", + ``!s. FINITE s /\ (!x. x IN s ==> 0 < x) ==> MAX_SET s <= big_lcm s``, + rpt strip_tac >> + Cases_on `s = {}` >- + rw[MAX_SET_EMPTY, big_lcm_empty] >> + `(MAX_SET s) IN s` by rw[MAX_SET_IN_SET] >> + `0 < big_lcm s` by rw[big_lcm_positive] >> + rw[big_lcm_is_common_multiple, DIVIDES_LE]); + +(* Theorem: (n + 1) * binomial n (HALF n) <= big_lcm (natural (n + 1)) *) +(* Proof: + Claim: MAX_SET (IMAGE (binomial n) (count (n + 1))) <= big_lcm (IMAGE (binomial n) count (n + 1)) + Proof: By big_lcm_ge_max, this is to show: + (1) FINITE (IMAGE (binomial n) (count (n + 1))) + This is true by FINITE_COUNT, IMAGE_FINITE + (2) !x. x IN IMAGE (binomial n) (count (n + 1)) ==> 0 < x + This is true by binomial_pos, IN_IMAGE, IN_COUNT + + big_lcm (natural (n + 1)) + = (n + 1) * big_lcm (IMAGE (binomial n) (count (n + 1))) by big_lcm_natural_eqn + >= (n + 1) * MAX_SET (IMAGE (binomial n) (count (n + 1))) by claim, LE_MULT_LCANCEL + = (n + 1) * binomial n (HALF n) by binomial_row_max +*) +val lcm_lower_bound_by_big_lcm = store_thm( + "lcm_lower_bound_by_big_lcm", + ``!n. (n + 1) * binomial n (HALF n) <= big_lcm (natural (n + 1))``, + rpt strip_tac >> + `MAX_SET (IMAGE (binomial n) (count (n + 1))) <= + big_lcm (IMAGE (binomial n) (count (n + 1)))` by + ((irule big_lcm_ge_max >> rpt conj_tac) >- + metis_tac[binomial_pos, IN_IMAGE, IN_COUNT, DECIDE``x < n + 1 ==> x <= n``] >> + rw[] + ) >> + metis_tac[big_lcm_natural_eqn, LE_MULT_LCANCEL, binomial_row_max, DECIDE``n + 1 <> 0``]); + +(* ------------------------------------------------------------------------- *) +(* Consecutive LCM function *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> + !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= list_lcm [1 .. n] *) +(* Proof: + Note ODD n ==> n <> 0 by EVEN_0, EVEN_ODD + If n = 1, + Note 1 <= pi by 0 < pi + so 2 <= 2 * pi by LE_MULT_LCANCEL, 2 <> 0 + or 1 < 2 * pi by arithmetic + Thus 1 DIV (2 * pi) = 0 by ONE_DIV, 1 < 2 * pi + and SQRT (1 DIV (2 * pi)) = 0 by ZERO_EXP, 0 ** h, h <> 0 + But list_lcm [1 .. 1] = 1 by list_lcm_sing + so SQRT (1 DIV (2 * pi)) * 2 ** 1 <= list_lcm [1 .. 1] by MULT + If n <> 1, + Then 0 < n - 1. + Let m = n - 1, then n = m + 1 by arithmetic + and n * binomial m (HALF m) <= list_lcm [1 .. n] by lcm_lower_bound_by_list_lcm + Now !a b c. (a DIV b) * c = (a * c) DIV b by DIV_1, MULT_RIGHT_1, c = c DIV 1, b * 1 = b + Note ODD n ==> EVEN m by EVEN_ODD_SUC, ADD1 + n * binomial m (HALF m) + = n * (2 ** n DIV SQRT (2 * pi * m)) by binomial_middle_by_stirling + = (2 ** n DIV SQRT (2 * pi * m)) * n by MULT_COMM + = (2 ** n * n) DIV (SQRT (2 * pi * m)) by above + = (n * 2 ** n) DIV (SQRT (2 * pi * m)) by MULT_COMM + = (n DIV SQRT (2 * pi * m)) * 2 ** n by above + = (SQRT (n DIV (2 * pi)) * 2 ** n by assumption, m = n - 1 + Hence SQRT (n DIV (2 * pi))) * (2 ** n) <= list_lcm [1 .. n] +*) +val lcm_lower_bound_by_list_lcm_stirling = store_thm( + "lcm_lower_bound_by_list_lcm_stirling", + ``Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> + !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= list_lcm [1 .. n]``, + rpt strip_tac >> + `!n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = 2 ** (n + 1) DIV SQRT (2 * pi * n))` by prove_tac[binomial_middle_by_stirling] >> + `n <> 0` by metis_tac[EVEN_0, EVEN_ODD] >> + Cases_on `n = 1` >| [ + `1 <= pi` by decide_tac >> + `1 < 2 * pi` by decide_tac >> + `1 DIV (2 * pi) = 0` by rw[ONE_DIV] >> + `SQRT (1 DIV (2 * pi)) * 2 ** 1 = 0` by rw[] >> + rw[list_lcm_sing], + `0 < n - 1 /\ (n = (n - 1) + 1)` by decide_tac >> + qabbrev_tac `m = n - 1` >> + `n * binomial m (HALF m) <= list_lcm [1 .. n]` by metis_tac[lcm_lower_bound_by_list_lcm] >> + `EVEN m` by metis_tac[EVEN_ODD_SUC, ADD1] >> + `!a b c. (a DIV b) * c = (a * c) DIV b` by metis_tac[DIV_1, MULT_RIGHT_1] >> + `n * binomial m (HALF m) = n * (2 ** n DIV SQRT (2 * pi * m))` by rw[] >> + `_ = (n DIV SQRT (2 * pi * m)) * 2 ** n` by metis_tac[MULT_COMM] >> + metis_tac[] + ]); + +(* Theorem: big_lcm (natural n) <= big_lcm (natural (n + 1)) *) +(* Proof: + Note FINITE (natural n) by natural_finite + and 0 < big_lcm (natural n) by big_lcm_positive, natural_element + big_lcm (natural n) + <= lcm (SUC n) (big_lcm (natural n)) by LCM_LE, 0 < SUC n, 0 < big_lcm (natural n) + = big_lcm ((SUC n) INSERT (natural n)) by big_lcm_insert + = big_lcm (natural (SUC n)) by natural_suc + = big_lcm (natural (n + 1)) by ADD1 +*) +val big_lcm_non_decreasing = store_thm( + "big_lcm_non_decreasing", + ``!n. big_lcm (natural n) <= big_lcm (natural (n + 1))``, + rpt strip_tac >> + `FINITE (natural n)` by rw[natural_finite] >> + `0 < big_lcm (natural n)` by rw[big_lcm_positive, natural_element] >> + `big_lcm (natural (n + 1)) = big_lcm (natural (SUC n))` by rw[ADD1] >> + `_ = big_lcm ((SUC n) INSERT (natural n))` by rw[natural_suc] >> + `_ = lcm (SUC n) (big_lcm (natural n))` by rw[big_lcm_insert] >> + rw[LCM_LE]); + +(* Theorem: Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> + !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= big_lcm (natural n) *) +(* Proof: + Note ODD n ==> n <> 0 by EVEN_0, EVEN_ODD + If n = 1, + Note 1 <= pi by 0 < pi + so 2 <= 2 * pi by LE_MULT_LCANCEL, 2 <> 0 + or 1 < 2 * pi by arithmetic + Thus 1 DIV (2 * pi) = 0 by ONE_DIV, 1 < 2 * pi + and SQRT (1 DIV (2 * pi)) = 0 by ZERO_EXP, 0 ** h, h <> 0 + But big_lcm (natural 1) = 1 by list_lcm_sing, natural_1 + so SQRT (1 DIV (2 * pi)) * 2 ** 1 <= big_lcm (natural 1) by MULT + If n <> 1, + Then 0 < n - 1. + Let m = n - 1, then n = m + 1 by arithmetic + and n * binomial m (HALF m) <= big_lcm (natural n) by lcm_lower_bound_by_big_lcm + Now !a b c. (a DIV b) * c = (a * c) DIV b by DIV_1, MULT_RIGHT_1, c = c DIV 1, b * 1 = b + Note ODD n ==> EVEN m by EVEN_ODD_SUC, ADD1 + n * binomial m (HALF m) + = n * (2 ** n DIV SQRT (2 * pi * m)) by binomial_middle_by_stirling + = (2 ** n DIV SQRT (2 * pi * m)) * n by MULT_COMM + = (2 ** n * n) DIV (SQRT (2 * pi * m)) by above + = (n * 2 ** n) DIV (SQRT (2 * pi * m)) by MULT_COMM + = (n DIV SQRT (2 * pi * m)) * 2 ** n by above + = (SQRT (n DIV (2 * pi)) * 2 ** n by assumption, m = n - 1 + Hence SQRT (n DIV (2 * pi))) * (2 ** n) <= big_lcm (natural n) +*) +val lcm_lower_bound_by_big_lcm_stirling = store_thm( + "lcm_lower_bound_by_big_lcm_stirling", + ``Stirling /\ (!n c. n DIV (SQRT (c * (n - 1))) = SQRT (n DIV c)) ==> + !n. ODD n ==> (SQRT (n DIV (2 * pi))) * (2 ** n) <= big_lcm (natural n)``, + rpt strip_tac >> + `!n. 0 < n /\ EVEN n ==> (binomial n (HALF n) = 2 ** (n + 1) DIV SQRT (2 * pi * n))` by prove_tac[binomial_middle_by_stirling] >> + `n <> 0` by metis_tac[EVEN_0, EVEN_ODD] >> + Cases_on `n = 1` >| [ + `1 <= pi` by decide_tac >> + `1 < 2 * pi` by decide_tac >> + `1 DIV (2 * pi) = 0` by rw[ONE_DIV] >> + `SQRT (1 DIV (2 * pi)) * 2 ** 1 = 0` by rw[] >> + rw[big_lcm_sing], + `0 < n - 1 /\ (n = (n - 1) + 1)` by decide_tac >> + qabbrev_tac `m = n - 1` >> + `n * binomial m (HALF m) <= big_lcm (natural n)` by metis_tac[lcm_lower_bound_by_big_lcm] >> + `EVEN m` by metis_tac[EVEN_ODD_SUC, ADD1] >> + `!a b c. (a DIV b) * c = (a * c) DIV b` by metis_tac[DIV_1, MULT_RIGHT_1] >> + `n * binomial m (HALF m) = n * (2 ** n DIV SQRT (2 * pi * m))` by rw[] >> + `_ = (n DIV SQRT (2 * pi * m)) * 2 ** n` by metis_tac[MULT_COMM] >> + metis_tac[] + ]); + +(* ------------------------------------------------------------------------- *) +(* Extra Theorems (not used) *) +(* ------------------------------------------------------------------------- *) + +(* +This is GCD_CANCEL_MULT by coprime p n, and coprime p n ==> coprime (p ** k) n by coprime_exp. +Note prime_not_divides_coprime. +*) + +(* Theorem: prime p /\ m divides n /\ ~((p * m) divides n) ==> (gcd (p * m) n = m) *) +(* Proof: + Note m divides n ==> ?q. n = q * m by divides_def + + Claim: coprime p q + Proof: By contradiction, suppose gcd p q <> 1. + Since (gcd p q) divides p by GCD_IS_GREATEST_COMMON_DIVISOR + so gcd p q = p by prime_def, gcd p q <> 1. + or p divides q by divides_iff_gcd_fix + Now, m <> 0 because + If m = 0, p * m = 0 by MULT_0 + Then m divides n and ~((p * m) divides n) are contradictory. + Thus p * m divides q * m by DIVIDES_MULTIPLE_IFF, MULT_COMM, p divides q, m <> 0 + But q * m = n, contradicting ~((p * m) divides n). + + gcd (p * m) n + = gcd (p * m) (q * m) by n = q * m + = m * gcd p q by GCD_COMMON_FACTOR, MULT_COMM + = m * 1 by coprime p q, from Claim + = m +*) +val gcd_prime_product_property = store_thm( + "gcd_prime_product_property", + ``!p m n. prime p /\ m divides n /\ ~((p * m) divides n) ==> (gcd (p * m) n = m)``, + rpt strip_tac >> + `?q. n = q * m` by rw[GSYM divides_def] >> + `m <> 0` by metis_tac[MULT_0] >> + `coprime p q` by + (spose_not_then strip_assume_tac >> + `(gcd p q) divides p` by rw[GCD_IS_GREATEST_COMMON_DIVISOR] >> + `gcd p q = p` by metis_tac[prime_def] >> + `p divides q` by rw[divides_iff_gcd_fix] >> + metis_tac[DIVIDES_MULTIPLE_IFF, MULT_COMM]) >> + metis_tac[GCD_COMMON_FACTOR, MULT_COMM, MULT_RIGHT_1]); + +(* Theorem: prime p /\ m divides n /\ ~((p * m) divides n) ==>(lcm (p * m) n = p * n) *) +(* Proof: + Note m <> 0 by MULT_0, m divides n /\ ~((p * m) divides n) + and m * lcm (p * m) n + = gcd (p * m) n * lcm (p * m) n by gcd_prime_product_property + = (p * m) * n by GCD_LCM + = (m * p) * n by MULT_COMM + = m * (p * n) by MULT_ASSOC + Thus lcm (p * m) n = p * n by MULT_LEFT_CANCEL +*) +val lcm_prime_product_property = store_thm( + "lcm_prime_product_property", + ``!p m n. prime p /\ m divides n /\ ~((p * m) divides n) ==>(lcm (p * m) n = p * n)``, + rpt strip_tac >> + `m <> 0` by metis_tac[MULT_0] >> + `m * lcm (p * m) n = gcd (p * m) n * lcm (p * m) n` by rw[gcd_prime_product_property] >> + `_ = (p * m) * n` by rw[GCD_LCM] >> + `_ = m * (p * n)` by metis_tac[MULT_COMM, MULT_ASSOC] >> + metis_tac[MULT_LEFT_CANCEL]); + +(* Theorem: prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) *) +(* Proof: + By induction on l. + Base: prime p /\ p divides list_lcm [] ==> p divides PROD_SET (set []) + Note list_lcm [] = 1 by list_lcm_nil + and PROD_SET (set []) + = PROD_SET {} by LIST_TO_SET + = 1 by PROD_SET_EMPTY + Hence conclusion is alredy in predicate, thus true. + Step: prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) ==> + prime p /\ p divides list_lcm (h::l) ==> p divides PROD_SET (set (h::l)) + Note PROD_SET (set (h::l)) + = PROD_SET (h INSERT set l) by LIST_TO_SET + This is to show: p divides PROD_SET (h INSERT set l) + + Let x = list_lcm l. + Since p divides (lcm h x) by given + so p divides (gcd h x) * (lcm h x) by DIVIDES_MULTIPLE + or p divides h * x by GCD_LCM + ==> p divides h or p divides x by P_EUCLIDES + Case: p divides h. + If h IN set l, or MEM h l, + Then h divides x by list_lcm_is_common_multiple + so p divides x by DIVIDES_TRANS + Thus p divides PROD_SET (set l) by induction hypothesis + or p divides PROD_SET (h INSERT set l) by ABSORPTION + If ~(h IN set l), + Then PROD_SET (h INSERT set l) = h * PROD_SET (set l) by PROD_SET_INSERT + or p divides PROD_SET (h INSERT set l) by DIVIDES_MULTIPLE, MULT_COMM + Case: p divides x. + If h IN set l, or MEM h l, + Then p divides PROD_SET (set l) by induction hypothesis + or p divides PROD_SET (h INSERT set l) by ABSORPTION + If ~(h IN set l), + Then PROD_SET (h INSERT set l) = h * PROD_SET (set l) by PROD_SET_INSERT + or p divides PROD_SET (h INSERT set l) by DIVIDES_MULTIPLE +*) +val list_lcm_prime_factor = store_thm( + "list_lcm_prime_factor", + ``!p l. prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l)``, + strip_tac >> + Induct >- + rw[] >> + rw[] >> + qabbrev_tac `x = list_lcm l` >> + `(gcd h x) * (lcm h x) = h * x` by rw[GCD_LCM] >> + `p divides (h * x)` by metis_tac[DIVIDES_MULTIPLE] >> + `p divides h \/ p divides x` by rw[P_EUCLIDES] >| [ + Cases_on `h IN set l` >| [ + `h divides x` by rw[list_lcm_is_common_multiple, Abbr`x`] >> + `p divides x` by metis_tac[DIVIDES_TRANS] >> + fs[ABSORPTION], + rw[PROD_SET_INSERT] >> + metis_tac[DIVIDES_MULTIPLE, MULT_COMM] + ], + Cases_on `h IN set l` >- + fs[ABSORPTION] >> + rw[PROD_SET_INSERT] >> + metis_tac[DIVIDES_MULTIPLE] + ]); + +(* Theorem: prime p /\ p divides PROD_SET (set l) ==> ?x. MEM x l /\ p divides x *) +(* Proof: + By induction on l. + Base: prime p /\ p divides PROD_SET (set []) ==> ?x. MEM x [] /\ p divides x + p divides PROD_SET (set []) + ==> p divides PROD_SET {} by LIST_TO_SET + ==> p divides 1 by PROD_SET_EMPTY + ==> p = 1 by DIVIDES_ONE + This contradicts with 1 < p by ONE_LT_PRIME + Step: prime p /\ p divides PROD_SET (set l) ==> ?x. MEM x l /\ p divides x ==> + !h. prime p /\ p divides PROD_SET (set (h::l)) ==> ?x. MEM x (h::l) /\ p divides x + Note PROD_SET (set (h::l)) + = PROD_SET (h INSERT set l) by LIST_TO_SET + This is to show: ?x. ((x = h) \/ MEM x l) /\ p divides x by MEM + If h IN set l, or MEM h l, + Then h INSERT set l = set l by ABSORPTION + Thus ?x. MEM x l /\ p divides x by induction hypothesis + If ~(h IN set l), + Then PROD_SET (h INSERT set l) = h * PROD_SET (set l) by PROD_SET_INSERT + Thus p divides h \/ p divides (PROD_SET (set l)) by P_EUCLIDES + Case p divides h. + Take x = h, the result is true. + Case p divides PROD_SET (set l). + Then ?x. MEM x l /\ p divides x by induction hypothesis +*) +val list_product_prime_factor = store_thm( + "list_product_prime_factor", + ``!p l. prime p /\ p divides PROD_SET (set l) ==> ?x. MEM x l /\ p divides x``, + strip_tac >> + Induct >| [ + rpt strip_tac >> + `PROD_SET (set []) = 1` by rw[PROD_SET_EMPTY] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `p <> 1` by decide_tac >> + metis_tac[DIVIDES_ONE], + rw[] >> + Cases_on `h IN set l` >- + metis_tac[ABSORPTION] >> + fs[PROD_SET_INSERT] >> + `p divides h \/ p divides (PROD_SET (set l))` by rw[P_EUCLIDES] >- + metis_tac[] >> + metis_tac[] + ]); + +(* Theorem: prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x *) +(* Proof: by list_lcm_prime_factor, list_product_prime_factor *) +val list_lcm_prime_factor_member = store_thm( + "list_lcm_prime_factor_member", + ``!p l. prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x``, + rw[list_lcm_prime_factor, list_product_prime_factor]); + +(* ------------------------------------------------------------------------- *) +(* Count Helper Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading (# is temporary): + over f s t = !x. x IN s ==> f x IN t + s bij_eq t = ?f. BIJ f s t + s =b= t = ?f. BIJ f s t +*) +(* Definitions and Theorems (# are exported, ! are in compute): + + Set Theorems: + over_inj |- !f s t. INJ f s t ==> over f s t + over_surj |- !f s t. SURJ f s t ==> over f s t + over_bij |- !f s t. BIJ f s t ==> over f s t + SURJ_CARD_IMAGE_EQ |- !f s t. FINITE t /\ over f s t ==> + (SURJ f s t <=> CARD (IMAGE f s) = CARD t) + FINITE_SURJ_IFF |- !f s t. FINITE t ==> + (SURJ f s t <=> CARD (IMAGE f s) = CARD t /\ over f s t) + INJ_IMAGE_BIJ_IFF |- !f s t. INJ f s t <=> BIJ f s (IMAGE f s) /\ over f s t + INJ_IFF_BIJ_IMAGE |- !f s t. over f s t ==> (INJ f s t <=> BIJ f s (IMAGE f s)) + INJ_IMAGE_IFF |- !f s t. INJ f s t <=> INJ f s (IMAGE f s) /\ over f s t + FUNSET_ALT |- !P Q. FUNSET P Q = {f | over f P Q} + + Bijective Equivalence: + bij_eq_empty |- !s t. s =b= t ==> (s = {} <=> t = {}) + bij_eq_refl |- !s. s =b= s + bij_eq_sym |- !s t. s =b= t <=> t =b= s + bij_eq_trans |- !s t u. s =b= t /\ t =b= u ==> s =b= u + bij_eq_equiv_on |- !P. $=b= equiv_on P + bij_eq_finite |- !s t. s =b= t ==> (FINITE s <=> FINITE t) + bij_eq_count |- !s. FINITE s ==> s =b= count (CARD s) + bij_eq_card |- !s t. s =b= t /\ (FINITE s \/ FINITE t) ==> CARD s = CARD t + bij_eq_card_eq |- !s t. FINITE s /\ FINITE t ==> (s =b= t <=> CARD s = CARD t) + + Alternate characterisation of maps: + surj_preimage_not_empty + |- !f s t. SURJ f s t <=> + over f s t /\ !y. y IN t ==> preimage f s y <> {} + inj_preimage_empty_or_sing + |- !f s t. INJ f s t <=> + over f s t /\ !y. y IN t ==> preimage f s y = {} \/ + SING (preimage f s y) + bij_preimage_sing |- !f s t. BIJ f s t <=> + over f s t /\ !y. y IN t ==> SING (preimage f s y) + surj_iff_preimage_card_not_0 + |- !f s t. FINITE s /\ over f s t ==> + (SURJ f s t <=> + !y. y IN t ==> CARD (preimage f s y) <> 0) + inj_iff_preimage_card_le_1 + |- !f s t. FINITE s /\ over f s t ==> + (INJ f s t <=> + !y. y IN t ==> CARD (preimage f s y) <= 1) + bij_iff_preimage_card_eq_1 + |- !f s t. FINITE s /\ over f s t ==> + (BIJ f s t <=> + !y. y IN t ==> CARD (preimage f s y) = 1) + finite_surj_inj_iff |- !f s t. FINITE s /\ SURJ f s t ==> + (INJ f s t <=> + !e. e IN IMAGE (preimage f s) t ==> CARD e = 1) +*) + +(* Overload a function from domain to range. + + NOTE: this is FUNSET --Chun Tian + *) +val _ = temp_overload_on("over", ``\f s t. !x. x IN s ==> f x IN t``); +(* not easy to make this a good infix operator! *) + +(* Theorem: INJ f s t ==> over f s t *) +(* Proof: by INJ_DEF. *) +Theorem over_inj: + !f s t. INJ f s t ==> over f s t +Proof + simp[INJ_DEF] +QED + +(* Theorem: SURJ f s t ==> over f s t *) +(* Proof: by SURJ_DEF. *) +Theorem over_surj: + !f s t. SURJ f s t ==> over f s t +Proof + simp[SURJ_DEF] +QED + +(* Theorem: BIJ f s t ==> over f s t *) +(* Proof: by BIJ_DEF, INJ_DEF. *) +Theorem over_bij: + !f s t. BIJ f s t ==> over f s t +Proof + simp[BIJ_DEF, INJ_DEF] +QED + +(* Theorem: FINITE t /\ over f s t ==> + (SURJ f s t <=> CARD (IMAGE f s) = CARD t) *) +(* Proof: + If part: SURJ f s t ==> CARD (IMAGE f s) = CARD t + Note IMAGE f s = t by IMAGE_SURJ + Hence true. + Only-if part: CARD (IMAGE f s) = CARD t ==> SURJ f s t + By contradiction, suppose ~SURJ f s t. + Then IMAGE f s <> t by IMAGE_SURJ + but IMAGE f s SUBSET t by IMAGE_SUBSET_TARGET + so IMAGE f s PSUBSET t by PSUBSET_DEF + ==> CARD (IMAGE f s) < CARD t + by CARD_PSUBSET + This contradicts CARD (IMAGE f s) = CARD t. +*) +Theorem SURJ_CARD_IMAGE_EQ: + !f s t. FINITE t /\ over f s t ==> + (SURJ f s t <=> CARD (IMAGE f s) = CARD t) +Proof + rw[EQ_IMP_THM] >- + fs[IMAGE_SURJ] >> + spose_not_then strip_assume_tac >> + `IMAGE f s <> t` by rw[GSYM IMAGE_SURJ] >> + `IMAGE f s PSUBSET t` by fs[IMAGE_SUBSET_TARGET, PSUBSET_DEF] >> + imp_res_tac CARD_PSUBSET >> + decide_tac +QED + +(* Theorem: FINITE t ==> + (SURJ f s t <=> CARD (IMAGE f s) = CARD t /\ over f s t) *) +(* Proof: + If part: true by SURJ_DEF, IMAGE_SURJ + Only-if part: true by SURJ_CARD_IMAGE_EQ +*) +Theorem FINITE_SURJ_IFF: + !f s t. FINITE t ==> + (SURJ f s t <=> CARD (IMAGE f s) = CARD t /\ over f s t) +Proof + metis_tac[SURJ_CARD_IMAGE_EQ, SURJ_DEF] +QED + +(* Note: this cannot be proved: +g `!f s t. FINITE t /\ over f s t ==> + (INJ f s t <=> CARD (IMAGE f s) = CARD t)`; +Take f = I, s = count m, t = count n, with m <= n. +Then INJ I (count m) (count n) +and IMAGE I (count m) = (count m) +so CARD (IMAGE f s) = m, CARD t = n, may not equal. +*) + +(* INJ_IMAGE_BIJ |- !s f. (?t. INJ f s t) ==> BIJ f s (IMAGE f s) *) + +(* Theorem: INJ f s t <=> (BIJ f s (IMAGE f s) /\ over f s t) *) +(* Proof: + If part: INJ f s t ==> BIJ f s (IMAGE f s) /\ over f s t + Note BIJ f s (IMAGE f s) by INJ_IMAGE_BIJ + and over f s t by INJ_DEF + Only-if: BIJ f s (IMAGE f s) /\ over f s t ==> INJ f s t + By INJ_DEF, this is to show: + (1) x IN s ==> f x IN t, true by given + (2) x IN s /\ y IN s /\ f x = f y ==> x = y + Note f x IN (IMAGE f s) by IN_IMAGE + and f y IN (IMAGE f s) by IN_IMAGE + so f x = f y ==> x = y by BIJ_IS_INJ +*) +Theorem INJ_IMAGE_BIJ_IFF: + !f s t. INJ f s t <=> (BIJ f s (IMAGE f s) /\ over f s t) +Proof + rw[EQ_IMP_THM] >- + metis_tac[INJ_IMAGE_BIJ] >- + fs[INJ_DEF] >> + rw[INJ_DEF] >> + metis_tac[BIJ_IS_INJ, IN_IMAGE] +QED + +(* Theorem: over f s t ==> (INJ f s t <=> BIJ f s (IMAGE f s)) *) +(* Proof: by INJ_IMAGE_BIJ_IFF. *) +Theorem INJ_IFF_BIJ_IMAGE: + !f s t. over f s t ==> (INJ f s t <=> BIJ f s (IMAGE f s)) +Proof + metis_tac[INJ_IMAGE_BIJ_IFF] +QED + +(* +INJ_IMAGE |- !f s t. INJ f s t ==> INJ f s (IMAGE f s) +*) + +(* Theorem: INJ f s t <=> INJ f s (IMAGE f s) /\ over f s t *) +(* Proof: + Let P = over f s t. + If part: INJ f s t ==> INJ f s (IMAGE f s) /\ P + Note INJ f s (IMAGE f s) by INJ_IMAGE + and P is true by INJ_DEF + Only-if part: INJ f s (IMAGE f s) /\ P ==> INJ f s t + Note s SUBSET s by SUBSET_REFL + and (IMAGE f s) SUBSET t by IMAGE_SUBSET_TARGET + Thus INJ f s t by INJ_SUBSET +*) +Theorem INJ_IMAGE_IFF: + !f s t. INJ f s t <=> INJ f s (IMAGE f s) /\ over f s t +Proof + rw[EQ_IMP_THM] >- + metis_tac[INJ_IMAGE] >- + fs[INJ_DEF] >> + `s SUBSET s` by rw[] >> + `(IMAGE f s) SUBSET t` by fs[IMAGE_SUBSET_TARGET] >> + metis_tac[INJ_SUBSET] +QED + +(* pred_setTheory: +FUNSET |- !P Q. FUNSET P Q = (\f. over f P Q) +*) + +(* Theorem: FUNSET P Q = {f | over f P Q} *) +(* Proof: by FUNSET, EXTENSION *) +Theorem FUNSET_ALT: + !P Q. FUNSET P Q = {f | over f P Q} +Proof + rw[FUNSET, EXTENSION] +QED + +(* ------------------------------------------------------------------------- *) +(* Bijective Equivalence *) +(* ------------------------------------------------------------------------- *) + +(* Overload bijectively equal. *) +val _ = overload_on("bij_eq", ``\s t. ?f. BIJ f s t``); +val _ = set_fixity "bij_eq" (Infix(NONASSOC, 450)); (* same as relation *) + +val _ = overload_on ("=b=", ``$bij_eq``); +val _ = set_fixity "=b=" (Infix(NONASSOC, 450)); + +(* +> BIJ_SYM; +val it = |- !s t. s bij_eq t <=> t bij_eq s: thm +> BIJ_SYM; +val it = |- !s t. s =b= t <=> t =b= s: thm +> FINITE_BIJ_COUNT_CARD +val it = |- !s. FINITE s ==> count (CARD s) =b= s: thm +*) + +(* Theorem: s =b= t ==> (s = {} <=> t = {}) *) +(* Proof: by BIJ_EMPTY. *) +Theorem bij_eq_empty: + !s t. s =b= t ==> (s = {} <=> t = {}) +Proof + metis_tac[BIJ_EMPTY] +QED + +(* Theorem: s =b= s *) +(* Proof: by BIJ_I_SAME *) +Theorem bij_eq_refl: + !s. s =b= s +Proof + metis_tac[BIJ_I_SAME] +QED + +(* Theorem alias *) +Theorem bij_eq_sym = BIJ_SYM; +(* val bij_eq_sym = |- !s t. s =b= t <=> t =b= s: thm *) + +Theorem bij_eq_trans = BIJ_TRANS; +(* val bij_eq_trans = |- !s t u. s =b= t /\ t =b= u ==> s =b= u: thm *) + +(* Idea: bij_eq is an equivalence relation on any set of sets. *) + +(* Theorem: $=b= equiv_on P *) +(* Proof: + By equiv_on_def, this is to show: + (1) s IN P ==> s =b= s, true by bij_eq_refl + (2) s IN P /\ t IN P ==> (t =b= s <=> s =b= t) + This is true by bij_eq_sym + (3) s IN P /\ s' IN P /\ t IN P /\ + BIJ f s s' /\ BIJ f' s' t ==> s =b= t + This is true by bij_eq_trans +*) +Theorem bij_eq_equiv_on: + !P. $=b= equiv_on P +Proof + rw[equiv_on_def] >- + simp[bij_eq_refl] >- + simp[Once bij_eq_sym] >> + metis_tac[bij_eq_trans] +QED + +(* Theorem: s =b= t ==> (FINITE s <=> FINITE t) *) +(* Proof: by BIJ_FINITE_IFF *) +Theorem bij_eq_finite: + !s t. s =b= t ==> (FINITE s <=> FINITE t) +Proof + metis_tac[BIJ_FINITE_IFF] +QED + +(* This is the iff version of: +pred_setTheory.FINITE_BIJ_CARD; +|- !f s t. FINITE s /\ BIJ f s t ==> CARD s = CARD t +*) + +(* Theorem: FINITE s ==> s =b= (count (CARD s)) *) +(* Proof: by FINITE_BIJ_COUNT_CARD, BIJ_SYM *) +Theorem bij_eq_count: + !s. FINITE s ==> s =b= (count (CARD s)) +Proof + metis_tac[FINITE_BIJ_COUNT_CARD, BIJ_SYM] +QED + +(* Theorem: s =b= t /\ (FINITE s \/ FINITE t) ==> CARD s = CARD t *) +(* Proof: by FINITE_BIJ_CARD, BIJ_SYM. *) +Theorem bij_eq_card: + !s t. s =b= t /\ (FINITE s \/ FINITE t) ==> CARD s = CARD t +Proof + metis_tac[FINITE_BIJ_CARD, BIJ_SYM] +QED + +(* Theorem: FINITE s /\ FINITE t ==> (s =b= t <=> CARD s = CARD t) *) +(* Proof: + If part: s =b= t ==> CARD s = CARD t + This is true by FINITE_BIJ_CARD + Only-if part: CARD s = CARD t ==> s =b= t + Let n = CARD s = CARD t. + Note ?f. BIJ f s (count n) by bij_eq_count + and ?g. BIJ g (count n) t by FINITE_BIJ_COUNT_CARD + Thus s =b= t by bij_eq_trans +*) +Theorem bij_eq_card_eq: + !s t. FINITE s /\ FINITE t ==> (s =b= t <=> CARD s = CARD t) +Proof + rw[EQ_IMP_THM] >- + metis_tac[FINITE_BIJ_CARD] >> + `?f. BIJ f s (count (CARD s))` by rw[bij_eq_count] >> + `?g. BIJ g (count (CARD t)) t` by rw[FINITE_BIJ_COUNT_CARD] >> + metis_tac[bij_eq_trans] +QED + +(* ------------------------------------------------------------------------- *) +(* Alternate characterisation of maps. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: SURJ f s t <=> + over f s t /\ (!y. y IN t ==> preimage f s y <> {}) *) +(* Proof: + Let P = over f s t, + Q = !y. y IN t ==> preimage f s y <> {}. + If part: SURJ f s t ==> P /\ Q + P is true by SURJ_DEF + Q is true by preimage_def, SURJ_DEF + Only-if part: P /\ Q ==> SURJ f s t + This is true by preimage_def, SURJ_DEF +*) +Theorem surj_preimage_not_empty: + !f s t. SURJ f s t <=> + over f s t /\ (!y. y IN t ==> preimage f s y <> {}) +Proof + rw[SURJ_DEF, preimage_def, EXTENSION] >> + metis_tac[] +QED + +(* Theorem: INJ f s t <=> + over f s t /\ + (!y. y IN t ==> (preimage f s y = {} \/ + SING (preimage f s y))) *) +(* Proof: + Let P = over f s t, + Q = !y. y IN t ==> preimage f s y = {} \/ SING (preimage f s y). + If part: INJ f s t ==> P /\ Q + P is true by INJ_DEF + For Q, if preimage f s y <> {}, + Then ?x. x IN preimage f s y by MEMBER_NOT_EMPTY + or ?x. x IN s /\ f x = y by in_preimage + Thus !z. z IN preimage f s y ==> z = x + by in_preimage, INJ_DEF + or SING (preimage f s y) by SING_DEF, EXTENSION + Only-if part: P /\ Q ==> INJ f s t + By INJ_DEF, this is to show: + !x y. x IN s /\ y IN s /\ f x = f y ==> x = y + Let z = f x, then z IN t by over f s t + so x IN preimage f s z by in_preimage + and y IN preimage f s z by in_preimage + Thus preimage f s z <> {} by MEMBER_NOT_EMPTY + so SING (preimage f s z) by implication + ==> x = y by SING_ELEMENT +*) +Theorem inj_preimage_empty_or_sing: + !f s t. INJ f s t <=> + over f s t /\ + (!y. y IN t ==> (preimage f s y = {} \/ + SING (preimage f s y))) +Proof + rw[EQ_IMP_THM] >- + fs[INJ_DEF] >- + ((Cases_on `preimage f s y = {}` >> simp[]) >> + `?x. x IN s /\ f x = y` by metis_tac[in_preimage, MEMBER_NOT_EMPTY] >> + simp[SING_DEF] >> + qexists_tac `x` >> + rw[preimage_def, EXTENSION] >> + metis_tac[INJ_DEF]) >> + rw[INJ_DEF] >> + qabbrev_tac `z = f x` >> + `z IN t` by fs[Abbr`z`] >> + `x IN preimage f s z` by fs[preimage_def] >> + `y IN preimage f s z` by fs[preimage_def] >> + metis_tac[MEMBER_NOT_EMPTY, SING_ELEMENT] +QED + +(* Theorem: BIJ f s t <=> + over f s t /\ + (!y. y IN t ==> SING (preimage f s y)) *) +(* Proof: + Let P = over f s t, + Q = !y. y IN t ==> SING (preimage f s y). + If part: BIJ f s t ==> P /\ Q + P is true by BIJ_DEF, INJ_DEF + For Q, + Note INJ f s t /\ SURJ f s t by BIJ_DEF + so preimage f s y <> {} by surj_preimage_not_empty + Thus SING (preimage f s y) by inj_preimage_empty_or_sing + Only-if part: P /\ Q ==> BIJ f s t + Note !y. y IN t ==> (preimage f s y) <> {} + by SING_DEF, NOT_EMPTY_SING + so SURJ f s t by surj_preimage_not_empty + and INJ f s t by inj_preimage_empty_or_sing + Thus BIJ f s t by BIJ_DEF +*) +Theorem bij_preimage_sing: + !f s t. BIJ f s t <=> + over f s t /\ + (!y. y IN t ==> SING (preimage f s y)) +Proof + rw[EQ_IMP_THM] >- + fs[BIJ_DEF, INJ_DEF] >- + metis_tac[BIJ_DEF, surj_preimage_not_empty, inj_preimage_empty_or_sing] >> + `INJ f s t` by metis_tac[inj_preimage_empty_or_sing] >> + `SURJ f s t` by metis_tac[SING_DEF, NOT_EMPTY_SING, surj_preimage_not_empty] >> + simp[BIJ_DEF] +QED + +(* Theorem: FINITE s /\ over f s t ==> + (SURJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <> 0) *) +(* Proof: + Note !y. FINITE (preimage f s y) by preimage_finite + and !y. CARD (preimage f s y) = 0 <=> preimage f s y = {} + by CARD_EQ_0 + The result follows by surj_preimage_not_empty +*) +Theorem surj_iff_preimage_card_not_0: + !f s t. FINITE s /\ over f s t ==> + (SURJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <> 0) +Proof + metis_tac[surj_preimage_not_empty, preimage_finite, CARD_EQ_0] +QED + +(* Theorem: FINITE s /\ over f s t ==> + (INJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <= 1) *) +(* Proof: + Note !y. FINITE (preimage f s y) by preimage_finite + and !y. CARD (preimage f s y) = 0 <=> preimage f s y = {} + by CARD_EQ_0 + and !y. CARD (preimage f s y) = 1 <=> SING (preimage f s y) + by CARD_EQ_1 + The result follows by inj_preimage_empty_or_sing, LE_ONE +*) +Theorem inj_iff_preimage_card_le_1: + !f s t. FINITE s /\ over f s t ==> + (INJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <= 1) +Proof + metis_tac[inj_preimage_empty_or_sing, preimage_finite, CARD_EQ_0, CARD_EQ_1, LE_ONE] +QED + +(* Theorem: FINITE s /\ over f s t ==> + (BIJ f s t <=> !y. y IN t ==> CARD (preimage f s y) = 1) *) +(* Proof: + Note !y. FINITE (preimage f s y) by preimage_finite + and !y. CARD (preimage f s y) = 1 <=> SING (preimage f s y) + by CARD_EQ_1 + The result follows by bij_preimage_sing +*) +Theorem bij_iff_preimage_card_eq_1: + !f s t. FINITE s /\ over f s t ==> + (BIJ f s t <=> !y. y IN t ==> CARD (preimage f s y) = 1) +Proof + metis_tac[bij_preimage_sing, preimage_finite, CARD_EQ_1] +QED + +(* Theorem: FINITE s /\ SURJ f s t ==> + (INJ f s t <=> !e. e IN IMAGE (preimage f s) t ==> CARD e = 1) *) +(* Proof: + If part: INJ f s t /\ x IN t ==> CARD (preimage f s x) = 1 + Note BIJ f s t by BIJ_DEF + and over f s t by BIJ_DEF, INJ_DEF + so CARD (preimage f s x) = 1 by bij_iff_preimage_card_eq_1 + Only-if part: !e. (?x. e = preimage f s x /\ x IN t) ==> CARD e = 1 ==> INJ f s t + Note over f s t by SURJ_DEF + and !x. x IN t ==> ?y. y IN s /\ f y = x by SURJ_DEF + Thus !y. y IN t ==> CARD (preimage f s y) = 1 by IN_IMAGE + so INJ f s t by inj_iff_preimage_card_le_1 +*) +Theorem finite_surj_inj_iff: + !f s t. FINITE s /\ SURJ f s t ==> + (INJ f s t <=> !e. e IN IMAGE (preimage f s) t ==> CARD e = 1) +Proof + rw[EQ_IMP_THM] >- + prove_tac[BIJ_DEF, INJ_DEF, bij_iff_preimage_card_eq_1] >> + fs[SURJ_DEF] >> + `!y. y IN t ==> CARD (preimage f s y) = 1` by metis_tac[] >> + rw[inj_iff_preimage_card_le_1] +QED + +(* ------------------------------------------------------------------------- *) +(* Necklace Theory Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: +*) +(* Definitions and Theorems (# are exported, ! are in compute): + + Necklace: + necklace_def |- !n a. necklace n a = + {ls | LENGTH ls = n /\ set ls SUBSET count a} + necklace_element |- !n a ls. ls IN necklace n a <=> + LENGTH ls = n /\ set ls SUBSET count a + necklace_length |- !n a ls. ls IN necklace n a ==> LENGTH ls = n + necklace_colors |- !n a ls. ls IN necklace n a ==> set ls SUBSET count a + necklace_property |- !n a ls. ls IN necklace n a ==> + LENGTH ls = n /\ set ls SUBSET count a + necklace_0 |- !a. necklace 0 a = {[]} + necklace_empty |- !n. 0 < n ==> (necklace n 0 = {}) + necklace_not_nil |- !n a ls. 0 < n /\ ls IN necklace n a ==> ls <> [] + necklace_suc |- !n a. necklace (SUC n) a = + IMAGE (\(c,ls). c::ls) (count a CROSS necklace n a) +! necklace_eqn |- !n a. necklace n a = + if n = 0 then {[]} + else IMAGE (\(c,ls). c::ls) (count a CROSS necklace (n - 1) a) + necklace_1 |- !a. necklace 1 a = {[e] | e IN count a} + necklace_finite |- !n a. FINITE (necklace n a) + necklace_card |- !n a. CARD (necklace n a) = a ** n + + Mono-colored necklace: + monocoloured_def |- !n a. monocoloured n a = + {ls | ls IN necklace n a /\ (ls <> [] ==> SING (set ls))} + monocoloured_element + |- !n a ls. ls IN monocoloured n a <=> + ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) + monocoloured_necklace |- !n a ls. ls IN monocoloured n a ==> ls IN necklace n a + monocoloured_subset |- !n a. monocoloured n a SUBSET necklace n a + monocoloured_finite |- !n a. FINITE (monocoloured n a) + monocoloured_0 |- !a. monocoloured 0 a = {[]} + monocoloured_1 |- !a. monocoloured 1 a = necklace 1 a + necklace_1_monocoloured + |- !a. necklace 1 a = monocoloured 1 a + monocoloured_empty|- !n. 0 < n ==> monocoloured n 0 = {} + monocoloured_mono |- !n. monocoloured n 1 = necklace n 1 + monocoloured_suc |- !n a. 0 < n ==> + monocoloured (SUC n) a = IMAGE (\ls. HD ls::ls) (monocoloured n a) + monocoloured_0_card |- !a. CARD (monocoloured 0 a) = 1 + monocoloured_card |- !n a. 0 < n ==> CARD (monocoloured n a) = a +! monocoloured_eqn |- !n a. monocoloured n a = + if n = 0 then {[]} + else IMAGE (\c. GENLIST (K c) n) (count a) + monocoloured_card_eqn |- !n a. CARD (monocoloured n a) = if n = 0 then 1 else a + + Multi-colored necklace: + multicoloured_def |- !n a. multicoloured n a = necklace n a DIFF monocoloured n a + multicoloured_element |- !n a ls. ls IN multicoloured n a <=> + ls IN necklace n a /\ ls <> [] /\ ~SING (set ls) + multicoloured_necklace|- !n a ls. ls IN multicoloured n a ==> ls IN necklace n a + multicoloured_subset |- !n a. multicoloured n a SUBSET necklace n a + multicoloured_finite |- !n a. FINITE (multicoloured n a) + multicoloured_0 |- !a. multicoloured 0 a = {} + multicoloured_1 |- !a. multicoloured 1 a = {} + multicoloured_n_0 |- !n. multicoloured n 0 = {} + multicoloured_n_1 |- !n. multicoloured n 1 = {} + multicoloured_empty |- !n. multicoloured n 0 = {} /\ multicoloured n 1 = {} + multi_mono_disjoint |- !n a. DISJOINT (multicoloured n a) (monocoloured n a) + multi_mono_exhaust |- !n a. necklace n a = multicoloured n a UNION monocoloured n a + multicoloured_card |- !n a. 0 < n ==> (CARD (multicoloured n a) = a ** n - a) + multicoloured_card_eqn|- !n a. CARD (multicoloured n a) = if n = 0 then 0 else a ** n - a + multicoloured_nonempty|- !n a. 1 < n /\ 1 < a ==> multicoloured n a <> {} + multicoloured_not_monocoloured + |- !n a ls. ls IN multicoloured n a ==> ls NOTIN monocoloured n a + multicoloured_not_monocoloured_iff + |- !n a ls. ls IN necklace n a ==> + (ls IN multicoloured n a <=> ls NOTIN monocoloured n a) + multicoloured_or_monocoloured + |- !n a ls. ls IN necklace n a ==> + ls IN multicoloured n a \/ ls IN monocoloured n a +*) + + +(* ------------------------------------------------------------------------- *) +(* Helper Theorems. *) +(* ------------------------------------------------------------------------- *) + +(* ------------------------------------------------------------------------- *) +(* Necklace *) +(* ------------------------------------------------------------------------- *) + +(* Define necklaces as lists of length n, i.e. with n beads, in a colors. *) +Definition necklace_def[nocompute]: + necklace n a = {ls | LENGTH ls = n /\ (set ls) SUBSET (count a) } +End +(* Note: use [nocompute] as this is not effective. *) + +(* Theorem: ls IN necklace n a <=> (LENGTH ls = n /\ (set ls) SUBSET (count a)) *) +(* Proof: by necklace_def *) +Theorem necklace_element: + !n a ls. ls IN necklace n a <=> (LENGTH ls = n /\ (set ls) SUBSET (count a)) +Proof + simp[necklace_def] +QED + +(* Theorem: ls IN (necklace n a) ==> LENGTH ls = n *) +(* Proof: by necklace_def *) +Theorem necklace_length: + !n a ls. ls IN (necklace n a) ==> LENGTH ls = n +Proof + simp[necklace_def] +QED + +(* Theorem: ls IN (necklace n a) ==> set ls SUBSET count a *) +(* Proof: by necklace_def *) +Theorem necklace_colors: + !n a ls. ls IN (necklace n a) ==> set ls SUBSET count a +Proof + simp[necklace_def] +QED + +(* Idea: If ls in (necklace n a), LENGTH ls = n and colors in count a. *) + +(* Theorem: ls IN (necklace n a) ==> LENGTH ls = n /\ set ls SUBSET count a *) +(* Proof: by necklace_def *) +Theorem necklace_property: + !n a ls. ls IN (necklace n a) ==> LENGTH ls = n /\ set ls SUBSET count a +Proof + simp[necklace_def] +QED + +(* ------------------------------------------------------------------------- *) +(* Know the necklaces. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: zero-length necklaces of whatever colors is the set of NIL. *) + +(* Theorem: necklace 0 a = {[]} *) +(* Proof: + necklace 0 a + = {ls | (LENGTH ls = 0) /\ (set ls) SUBSET (count a) } by necklace_def + = {ls | ls = [] /\ (set []) SUBSET (count a) } by LENGTH_NIL + = {ls | ls = [] /\ [] SUBSET (count a) } by LIST_TO_SET + = {[]} by EMPTY_SUBSET +*) +Theorem necklace_0: + !a. necklace 0 a = {[]} +Proof + rw[necklace_def, EXTENSION] >> + metis_tac[LIST_TO_SET, EMPTY_SUBSET] +QED + +(* Idea: necklaces with some length but 0 colors is EMPTY. *) + +(* Theorem: 0 < n ==> (necklace n 0 = {}) *) +(* Proof: + necklace n 0 + = {ls | LENGTH ls = n /\ (set ls) SUBSET (count 0) } by necklace_def + = {ls | LENGTH ls = n /\ (set ls) SUBSET {} by COUNT_0 + = {ls | LENGTH ls = n /\ (set ls = {}) } by SUBSET_EMPTY + = {ls | LENGTH ls = n /\ (ls = []) } by LIST_TO_SET_EQ_EMPTY + = {} by LENGTH_NIL, 0 < n +*) +Theorem necklace_empty: + !n. 0 < n ==> (necklace n 0 = {}) +Proof + rw[necklace_def, EXTENSION] +QED + +(* Idea: A necklace of length n <> 0 is non-NIL. *) + +(* Theorem: 0 < n /\ ls IN (necklace n a) ==> ls <> [] *) +(* Proof: + By contradiction, suppose ls = []. + Then n = LENGTH ls by necklace_element + = LENGTH [] = 0 by LENGTH_NIL + This contradicts 0 < n. +*) +Theorem necklace_not_nil: + !n a ls. 0 < n /\ ls IN (necklace n a) ==> ls <> [] +Proof + rw[necklace_def] >> + metis_tac[LENGTH_NON_NIL] +QED + +(* ------------------------------------------------------------------------- *) +(* To show: (necklace n a) is FINITE. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: Relate (necklace (n+1) a) to (necklace n a) for induction. *) + +(* Theorem: necklace (SUC n) a = + IMAGE (\(c, ls). c :: ls) (count a CROSS necklace n a) *) +(* Proof: + By necklace_def, EXTENSION, this is to show: + (1) LENGTH x = SUC n /\ set x SUBSET count a ==> + ?h t. (x = h::t) /\ h < a /\ (LENGTH t = n) /\ set t SUBSET count a + Note SUC n <> 0 by SUC_NOT_ZERO + so ?h t. x = h::t by list_CASES + Take these h, t, true by LENGTH, MEM + (2) h < a /\ set t SUBSET count a ==> x < a ==> LENGTH (h::t) = SUC (LENGTH t) + This is true by LENGTH + (3) h < a /\ set t SUBSET count a ==> set (h::t) SUBSET count a + Note set (h::t) c <=> + (c = h) \/ set t c by LIST_TO_SET_DEF + If c = h, h < a + ==> h IN count a by IN_COUNT + If set t c, set t SUBSET count a + ==> c IN count a by SUBSET_DEF + Thus set (h::t) SUBSET count a by SUBSET_DEF +*) +Theorem necklace_suc: + !n a. necklace (SUC n) a = + IMAGE (\(c, ls). c :: ls) (count a CROSS necklace n a) +Proof + rw[necklace_def, EXTENSION] >> + rw[pairTheory.EXISTS_PROD, EQ_IMP_THM] >| [ + `SUC n <> 0` by decide_tac >> + `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + qexists_tac `h` >> + qexists_tac `t` >> + fs[], + simp[], + fs[] + ] +QED + +(* Idea: display the necklaces. *) + +(* Theorem: necklace n a = + if n = 0 then {[]} + else IMAGE (\(c,ls). c::ls) (count a CROSS necklace (n - 1) a) *) +(* Proof: by necklace_0, necklace_suc. *) +Theorem necklace_eqn[compute]: + !n a. necklace n a = + if n = 0 then {[]} + else IMAGE (\(c,ls). c::ls) (count a CROSS necklace (n - 1) a) +Proof + rw[necklace_0] >> + metis_tac[necklace_suc, num_CASES, SUC_SUB1] +QED + +(* +> EVAL ``necklace 3 2``; += {[1; 1; 1]; [1; 1; 0]; [1; 0; 1]; [1; 0; 0]; [0; 1; 1]; [0; 1; 0]; [0; 0; 1]; [0; 0; 0]} +> EVAL ``necklace 2 3``; += {[2; 2]; [2; 1]; [2; 0]; [1; 2]; [1; 1]; [1; 0]; [0; 2]; [0; 1]; [0; 0]} +*) + +(* Idea: Unit-length necklaces are singletons from (count a). *) + +(* Theorem: necklace 1 a = {[e] | e IN count a} *) +(* Proof: + Let f = (\(c,ls). c::ls). + necklace 1 a + = necklace (SUC 0) a by ONE + = IMAGE f ((count a) CROSS (necklace 0 a)) by necklace_suc + = IMAGE f ((count a) CROSS {[]}) by necklace_0 + = {[e] | e IN count a} by EXTENSION +*) +Theorem necklace_1: + !a. necklace 1 a = {[e] | e IN count a} +Proof + rewrite_tac[ONE] >> + rw[necklace_suc, necklace_0, pairTheory.EXISTS_PROD, EXTENSION] +QED + +(* Idea: The set of (necklace n a) is finite. *) + +(* Theorem: FINITE (necklace n a) *) +(* Proof: + By induction on n. + Base: FINITE (necklace 0 a) + Note necklace 0 a = {[]} by necklace_0 + and FINITE {[]} by FINITE_SING + Step: FINITE (necklace n a) ==> FINITE (necklace (SUC n) a) + Let f = (\(c, ls). c :: ls), b = count a, c = necklace n a. + Note necklace (SUC n) a + = IMAGE f (b CROSS c) by necklace_suc + and FINITE b by FINITE_COUNT + and FINITE c by induction hypothesis + so FINITE (b CROSS c) by FINITE_CROSS + Thus FINITE (necklace (SUC n) a) by IMAGE_FINITE +*) +Theorem necklace_finite: + !n a. FINITE (necklace n a) +Proof + rpt strip_tac >> + Induct_on `n` >- + simp[necklace_0] >> + simp[necklace_suc] +QED + +(* ------------------------------------------------------------------------- *) +(* To show: CARD (necklace n a) = a^n. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: size of (necklace n a) = a^n. *) + +(* Theorem: CARD (necklace n a) = a ** n *) +(* Proof: + By induction on n. + Base: CARD (necklace 0 a) = a ** 0 + CARD (necklace 0 a) + = CARD {[]} by necklace_0 + = 1 by CARD_SING + = a ** 0 by EXP_0 + Step: CARD (necklace n a) = a ** n ==> + CARD (necklace (SUC n) a) = a ** SUC n + Let f = (\(c, ls). c :: ls), b = count a, c = necklace n a. + Note FINITE b by FINITE_COUNT + and FINITE c by necklace_finite + and FINITE (b CROSS c) by FINITE_CROSS + Also INJ f (b CROSS c) univ(:num list) by INJ_DEF, CONS_11 + CARD (necklace (SUC n) a) + = CARD (IMAGE f (b CROSS c)) by necklace_suc + = CARD (b CROSS c) by INJ_CARD_IMAGE_EQN + = CARD b * CARD c by CARD_CROSS + = a * CARD c by CARD_COUNT + = a * a ** n by induction hypothesis + = a ** SUC n by EXP +*) +Theorem necklace_card: + !n a. CARD (necklace n a) = a ** n +Proof + rpt strip_tac >> + Induct_on `n` >- + simp[necklace_0] >> + qabbrev_tac `f = (\(c:num, ls:num list). c :: ls)` >> + qabbrev_tac `b = count a` >> + qabbrev_tac `c = necklace n a` >> + `FINITE b` by rw[FINITE_COUNT, Abbr`b`] >> + `FINITE c` by rw[necklace_finite, Abbr`c`] >> + `necklace (SUC n) a = IMAGE f (b CROSS c)` by rw[necklace_suc, Abbr`f`, Abbr`b`, Abbr`c`] >> + `INJ f (b CROSS c) univ(:num list)` by rw[INJ_DEF, pairTheory.FORALL_PROD, Abbr`f`] >> + `FINITE (b CROSS c)` by rw[FINITE_CROSS] >> + `CARD (necklace (SUC n) a) = CARD (b CROSS c)` by rw[INJ_CARD_IMAGE_EQN] >> + `_ = CARD b * CARD c` by rw[CARD_CROSS] >> + `_ = a * a ** n` by fs[Abbr`b`, Abbr`c`] >> + simp[EXP] +QED + +(* ------------------------------------------------------------------------- *) +(* Mono-colored necklace - necklace with a single color. *) +(* ------------------------------------------------------------------------- *) + +(* Define mono-colored necklace *) +Definition monocoloured_def[nocompute]: + monocoloured n a = + {ls | ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) } +End +(* Note: use [nocompute] as this is not effective. *) + +(* Theorem: ls IN monocoloured n a <=> + ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) *) +(* Proof: by monocoloured_def *) +Theorem monocoloured_element: + !n a ls. ls IN monocoloured n a <=> + ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) +Proof + simp[monocoloured_def] +QED + +(* ------------------------------------------------------------------------- *) +(* Know the Mono-coloured necklaces. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: A monocoloured necklace is indeed a necklace. *) + +(* Theorem: ls IN monocoloured n a ==> ls IN necklace n a *) +(* Proof: by monocoloured_def *) +Theorem monocoloured_necklace: + !n a ls. ls IN monocoloured n a ==> ls IN necklace n a +Proof + simp[monocoloured_def] +QED + +(* Idea: The monocoloured set is subset of necklace set. *) + +(* Theorem: (monocoloured n a) SUBSET (necklace n a) *) +(* Proof: by monocoloured_necklace, SUBSET_DEF *) +Theorem monocoloured_subset: + !n a. (monocoloured n a) SUBSET (necklace n a) +Proof + simp[monocoloured_necklace, SUBSET_DEF] +QED + +(* Idea: The monocoloured set is FINITE. *) + +(* Theorem: FINITE (monocoloured n a) *) +(* Proof: + Note (monocoloured n a) SUBSET (necklace n a) by monocoloured_subset + and FINITE (necklace n a) by necklace_finite + so FINITE (monocoloured n a) by SUBSET_FINITE +*) +Theorem monocoloured_finite: + !n a. FINITE (monocoloured n a) +Proof + metis_tac[monocoloured_subset, necklace_finite, SUBSET_FINITE] +QED + +(* Idea: Zero-length monocoloured set is singleton NIL. *) + +(* Theorem: monocoloured 0 a = {[]} *) +(* Proof: + monocoloured 0 a + = {ls | ls IN necklace 0 a /\ (ls <> [] ==> SING (set ls)) } by monocoloured_def + = {ls | ls IN {[]} /\ (ls <> [] ==> SING (set ls)) } by necklace_0 + = {[]} by IN_SING +*) +Theorem monocoloured_0: + !a. monocoloured 0 a = {[]} +Proof + rw[monocoloured_def, necklace_0, EXTENSION, EQ_IMP_THM] +QED + +(* Idea: Unit-length monocoloured set are necklaces of length 1. *) + +(* Theorem: monocoloured 1 a = necklace 1 a *) +(* Proof: + By necklace_def, monocoloured_def, EXTENSION, + this is to show: + (LENGTH x = 1) /\ set x SUBSET count a /\ (x <> [] ==> SING (set x)) <=> + (LENGTH x = 1) /\ set x SUBSET count a + This is true by SING_LIST_TO_SET +*) +Theorem monocoloured_1: + !a. monocoloured 1 a = necklace 1 a +Proof + rw[necklace_def, monocoloured_def, EXTENSION] >> + metis_tac[SING_LIST_TO_SET] +QED + +(* Idea: Unit-length necklaces are monocoloured. *) + +(* Theorem: necklace 1 a = monocoloured 1 a *) +(* Proof: by monocoloured_1 *) +Theorem necklace_1_monocoloured: + !a. necklace 1 a = monocoloured 1 a +Proof + simp[monocoloured_1] +QED + +(* Idea: Some non-NIL necklaces are monocoloured. *) + +(* Theorem: 0 < n ==> monocoloured n 0 = {} *) +(* Proof: + Note (monocoloured n 0) SUBSET (necklace n 0) by monocoloured_subset + but necklace n 0 = {} by necklace_empty + so monocoloured n 0 = {} by SUBSET_EMPTY +*) +Theorem monocoloured_empty: + !n. 0 < n ==> monocoloured n 0 = {} +Proof + metis_tac[monocoloured_subset, necklace_empty, SUBSET_EMPTY] +QED + +(* Idea: One-colour necklaces are monocoloured. *) + +(* Theorem: monocoloured n 1 = necklace n 1 *) +(* Proof: + By monocoloured_def, necklace_def, EXTENSION, + this is to show: + set x SUBSET count 1 /\ x <> [] ==> SING (set x) + Note count 1 = {0} by COUNT_1 + and set x <> {} by LIST_TO_SET + so set x = {0} by SUBSET_SING_IFF + or SING (set x) by SING_DEF +*) +Theorem monocoloured_mono: + !n. monocoloured n 1 = necklace n 1 +Proof + rw[monocoloured_def, necklace_def, EXTENSION, EQ_IMP_THM] >> + fs[COUNT_1] >> + `set x = {0}` by fs[SUBSET_SING_IFF] >> + simp[] +QED + +(* ------------------------------------------------------------------------- *) +(* To show: CARD (monocoloured n a) = a. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: Relate (monocoloured (SUC n) a) to (monocoloured n a) for induction. *) + +(* Theorem: 0 < n ==> (monocoloured (SUC n) a = + IMAGE (\ls. HD ls :: ls) (monocoloured n a)) *) +(* Proof: + By monocoloured_def, necklace_def, EXTENSION, this is to show: + (1) 0 < n /\ LENGTH x = SUC n /\ set x SUBSET count a /\ x <> [] ==> SING (set x) ==> + ?ls. (x = HD ls::ls) /\ (LENGTH ls = n /\ set ls SUBSET count a) /\ + (ls <> [] ==> SING (set ls)) + Note SUC n <> 0 by SUC_NOT_ZERO + so x <> [] by LENGTH_NIL + ==> ?h t. x = h::t by list_CASES + and LENGTH t = n by LENGTH + but t <> [] by LENGTH_NON_NIL, 0 < n + so ?k p. t = k::p by list_CASES + Thus x = h::k::p by above + Now h IN set x by MEM + and k IN set x by MEM, SUBSET_DEF + so h = k by IN_SING, SING (set x) + Let ls = t, + then set ls SUBSET count a by MEM, SUBSET_DEF + and SING (set ls) by LIST_TO_SET_DEF + (2) 0 < LENGTH ls /\ set ls SUBSET count a /\ ls <> [] ==> SING (set ls) ==> + LENGTH (HD ls::ls) = SUC (LENGTH ls) + This is true by LENGTH + (3) 0 < LENGTH ls /\ set ls SUBSET count a /\ ls <> [] ==> SING (set ls) ==> + set (HD ls::ls) SUBSET count a + Note ls <> [] by LENGTH_NON_NIL + so ?h t. ls = h::t by list_CASES + Also set (h::ls) x <=> + (x = h) \/ set t x by LIST_TO_SET_DEF + Thus set (h::ls) SUBSET count a by SUBSET_DEF + (4) 0 < LENGTH ls /\ set ls SUBSET count a /\ ls <> [] ==> SING (set ls) ==> + SING (set (HD ls::ls)) + Note ls <> [] by LENGTH_NON_NIL + so ?h t. ls = h::t by list_CASES + Also set (h::ls) x <=> + (x = h) \/ set t x by LIST_TO_SET_DEF + Thus SING (set (h::ls)) by SUBSET_DEF +*) +Theorem monocoloured_suc: + !n a. 0 < n ==> (monocoloured (SUC n) a = + IMAGE (\ls. HD ls :: ls) (monocoloured n a)) +Proof + rw[monocoloured_def, necklace_def, EXTENSION] >> + rw[pairTheory.EXISTS_PROD, EQ_IMP_THM] >| [ + `SUC n <> 0` by decide_tac >> + `x <> [] /\ ?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + `LENGTH t = n` by fs[] >> + `t <> []` by metis_tac[LENGTH_NON_NIL] >> + `h IN set x` by fs[] >> + `?k p. t = k::p` by metis_tac[list_CASES] >> + `HD t IN set x` by rfs[SUBSET_DEF] >> + `HD t = h` by metis_tac[SING_DEF, IN_SING] >> + qexists_tac `t` >> + fs[], + simp[], + `ls <> [] /\ ?h t. ls = h::t` by metis_tac[LENGTH_NON_NIL, list_CASES] >> + fs[], + `ls <> [] /\ ?h t. ls = h::t` by metis_tac[LENGTH_NON_NIL, list_CASES] >> + fs[] + ] +QED + +(* Idea: size of (monocoloured 0 a) = 1. *) + +(* Theorem: CARD (monocoloured 0 a) = 1 *) +(* Proof: + Note monocoloured 0 a = {[]} by monocoloured_0 + so CARD (monocoloured 0 a) = 1 by CARD_SING +*) +Theorem monocoloured_0_card: + !a. CARD (monocoloured 0 a) = 1 +Proof + simp[monocoloured_0] +QED + +(* Idea: size of (monocoloured n a) = a. *) + +(* Theorem: 0 < n ==> CARD (monocoloured n a) = a *) +(* Proof: + By induction on n. + Base: 0 < 0 ==> (CARD (monocoloured 0 a) = a) + True by 0 < 0 = F. + Step: 0 < n ==> CARD (monocoloured n a) = a ==> + 0 < SUC n ==> (CARD (monocoloured (SUC n) a) = a) + If n = 0, + CARD (monocoloured (SUC 0) a) + = CARD (monocoloured 1 a) by ONE + = CARD (necklace 1 a) by monocoloured_1 + = a ** 1 by necklace_card + = a by EXP_1 + If n <> 0, then 0 < n. + Let f = (\ls. HD ls :: ls). + Then INJ f (monocoloured n a) + univ(:num list) by INJ_DEF, CONS_11 + and FINITE (monocoloured n a) by monocoloured_finite + CARD (monocoloured (SUC n) a) + = CARD (IMAGE f (monocoloured n a)) by monocoloured_suc + = CARD (monocoloured n a) by INJ_CARD_IMAGE_EQN + = a by induction hypothesis +*) +Theorem monocoloured_card: + !n a. 0 < n ==> CARD (monocoloured n a) = a +Proof + rpt strip_tac >> + Induct_on `n` >- + simp[] >> + (Cases_on `n = 0` >> simp[]) >- + simp[monocoloured_1, necklace_card] >> + qabbrev_tac `f = \ls:num list. HD ls :: ls` >> + `INJ f (monocoloured n a) univ(:num list)` by rw[INJ_DEF, Abbr`f`] >> + `FINITE (monocoloured n a)` by rw[monocoloured_finite] >> + `CARD (monocoloured (SUC n) a) = + CARD (IMAGE f (monocoloured n a))` by rw[monocoloured_suc, Abbr`f`] >> + `_ = CARD (monocoloured n a)` by rw[INJ_CARD_IMAGE_EQN] >> + fs[] +QED + +(* Theorem: monocoloured n a = + if n = 0 then {[]} else IMAGE (\c. GENLIST (K c) n) (count a) *) +(* Proof: + If n = 0, true by monocoloured_0 + If n <> 0, then 0 < n. + By monocoloured_def, necklace_def, EXTENSION, this is to show: + (1) 0 < LENGTH x /\ set x SUBSET count a /\ x <> [] ==> SING (set x) ==> + ?c. (x = GENLIST (K c) (LENGTH x)) /\ c < a + Note x <> [] by LENGTH_NON_NIL + so ?c. set x = {c} by SING_DEF + Then c < a by SUBSET_DEF, IN_COUNT + and x = GENLIST (K c) (LENGTH x) by LIST_TO_SET_SING_IFF + (2) c < a ==> LENGTH (GENLIST (K c) n) = n, + This is true by LENGTH_GENLIST + (3) c < a ==> set (GENLIST (K c) n) SUBSET count a + Note set (GENLIST (K c) n) = {c} by GENLIST_K_SET + so c < a ==> {c} SUBSET (count a) by SUBSET_DEF + (4) c < a /\ GENLIST (K c) n <> [] ==> SING (set (GENLIST (K c) n)) + Note set (GENLIST (K c) n) = {c} by GENLIST_K_SET + so SING (set (GENLIST (K c) n)) by SING_DEF +*) +Theorem monocoloured_eqn[compute]: + !n a. monocoloured n a = + if n = 0 then {[]} + else IMAGE (\c. GENLIST (K c) n) (count a) +Proof + rw_tac bool_ss[] >- + simp[monocoloured_0] >> + `0 < n` by decide_tac >> + rw[monocoloured_def, necklace_def, EXTENSION, EQ_IMP_THM] >| [ + `x <> []` by metis_tac[LENGTH_NON_NIL] >> + `SING (set x) /\ ?c. set x = {c}` by rw[GSYM SING_DEF] >> + `c < a` by fs[SUBSET_DEF] >> + `?b. x = GENLIST (K b) (LENGTH x)` by metis_tac[LIST_TO_SET_SING_IFF] >> + metis_tac[GENLIST_K_SET, IN_SING], + simp[], + rw[GENLIST_K_SET], + rw[GENLIST_K_SET] + ] +QED + +(* +> EVAL ``monocoloured 2 3``; = {[2; 2]; [1; 1]; [0; 0]}: thm +> EVAL ``monocoloured 3 2``; = {[1; 1; 1]; [0; 0; 0]}: thm +*) + +(* Slight improvement of a previous result. *) + +(* Theorem: CARD (monocoloured n a) = if n = 0 then 1 else a *) +(* Proof: + If n = 0, + CARD (monocoloured 0 a) + = CARD {[]} by monocoloured_eqn + = 1 by CARD_SING + If n <> 0, then 0 < n. + Let f = (\c:num. GENLIST (K c) n). + Then INJ f (count a) univ(:num list) + by INJ_DEF, GENLIST_K_SET, IN_SING + and FINITE (count a) by FINITE_COUNT + CARD (monocoloured n a) + = CARD (IMAGE f (count a)) by monocoloured_eqn + = CARD (count a) by INJ_CARD_IMAGE_EQN + = a by CARD_COUNT +*) +Theorem monocoloured_card_eqn: + !n a. CARD (monocoloured n a) = if n = 0 then 1 else a +Proof + rw[monocoloured_eqn] >> + qmatch_abbrev_tac `CARD (IMAGE f (count a)) = a` >> + `INJ f (count a) univ(:num list)` by + (rw[INJ_DEF, Abbr`f`] >> + `0 < n` by decide_tac >> + metis_tac[GENLIST_K_SET, IN_SING]) >> + rw[INJ_CARD_IMAGE_EQN] +QED + +(* ------------------------------------------------------------------------- *) +(* Multi-colored necklace *) +(* ------------------------------------------------------------------------- *) + +(* Define multi-colored necklace *) +Definition multicoloured_def: + multicoloured n a = (necklace n a) DIFF (monocoloured n a) +End +(* Note: EVAL can handle set DIFF. *) + +(* +> EVAL ``multicoloured 3 2``; += {[1; 1; 0]; [1; 0; 1]; [1; 0; 0]; [0; 1; 1]; [0; 1; 0]; [0; 0; 1]}: thm +> EVAL ``multicoloured 2 3``; += {[2; 1]; [2; 0]; [1; 2]; [1; 0]; [0; 2]; [0; 1]}: thm +*) + +(* Theorem: ls IN multicoloured n a <=> + ls IN necklace n a /\ ls <> [] /\ ~SING (set ls) *) +(* Proof: + ls IN multicoloured n a + <=> ls IN (necklace n a) DIFF (monocoloured n a) by multicoloured_def + <=> ls IN (necklace n a) /\ ls NOTIN (monocoloured n a) by IN_DIFF + <=> ls IN (necklace n a) /\ + ~ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) by monocoloured_def + <=> ls IN (necklace n a) /\ ls <> [] /\ ~SING (set ls) by logical equivalence + + t /\ ~(t /\ (p ==> q)) + = t /\ (~t \/ ~(p ==> q)) + = t /\ ~t \/ (t /\ ~(~p \/ q)) + = t /\ (p /\ ~q) +*) +Theorem multicoloured_element: + !n a ls. ls IN multicoloured n a <=> + ls IN necklace n a /\ ls <> [] /\ ~SING (set ls) +Proof + (rw[multicoloured_def, monocoloured_def, EQ_IMP_THM] >> simp[]) +QED + +(* ------------------------------------------------------------------------- *) +(* Know the Multi-coloured necklaces. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: multicoloured is a necklace. *) + +(* Theorem: ls IN multicoloured n a ==> ls IN necklace n a *) +(* Proof: by multicoloured_def *) +Theorem multicoloured_necklace: + !n a ls. ls IN multicoloured n a ==> ls IN necklace n a +Proof + simp[multicoloured_def] +QED + +(* Idea: The multicoloured set is subset of necklace set. *) + +(* Theorem: (multicoloured n a) SUBSET (necklace n a) *) +(* Proof: + Note multicoloured n a + = (necklace n a) DIFF (monocoloured n a) by multicoloured_def + so (multicoloured n a) SUBSET (necklace n a) by DIFF_SUBSET +*) +Theorem multicoloured_subset: + !n a. (multicoloured n a) SUBSET (necklace n a) +Proof + simp[multicoloured_def] +QED + +(* Idea: multicoloured set is FINITE. *) + +(* Theorem: FINITE (multicoloured n a) *) +(* Proof: + Note multicoloured n a + = (necklace n a) DIFF (monocoloured n a) by multicoloured_def + and FINITE (necklace n a) by necklace_finite + so FINITE (multicoloured n a) by FINITE_DIFF +*) +Theorem multicoloured_finite: + !n a. FINITE (multicoloured n a) +Proof + simp[multicoloured_def, necklace_finite, FINITE_DIFF] +QED + +(* Idea: (multicoloured 0 a) is EMPTY. *) + +(* Theorem: multicoloured 0 a = {} *) +(* Proof: + multicoloured 0 a + = (necklace 0 a) DIFF (monocoloured 0 a) by multicoloured_def + = {[]} - {[]} by necklace_0, monocoloured_0 + = {} by DIFF_EQ_EMPTY +*) +Theorem multicoloured_0: + !a. multicoloured 0 a = {} +Proof + simp[multicoloured_def, necklace_0, monocoloured_0] +QED + +(* Idea: (mutlicoloured 1 a) is also EMPTY. *) + +(* Theorem: multicoloured 1 a = {} *) +(* Proof: + multicoloured 1 a + = (necklace 1 a) DIFF (monocoloured 1 a) by multicoloured_def + = (necklace 1 a) DIFF (necklace 1 a) by monocoloured_1 + = {} by DIFF_EQ_EMPTY +*) +Theorem multicoloured_1: + !a. multicoloured 1 a = {} +Proof + simp[multicoloured_def, monocoloured_1] +QED + +(* Idea: (multicoloured n 0) without color is EMPTY. *) + +(* Theorem: multicoloured n 0 = {} *) +(* Proof: + If n = 0, + Then multicoloured 0 0 = {} by multicoloured_0 + If n <> 0, then 0 < n. + multicoloured n 0 + = (necklace n 0) DIFF (monocoloured n 0) by multicoloured_def + = {} DIFF (monocoloured n 0) by necklace_empty + = {} by EMPTY_DIFF +*) +Theorem multicoloured_n_0: + !n. multicoloured n 0 = {} +Proof + rpt strip_tac >> + Cases_on `n = 0` >- + simp[multicoloured_0] >> + simp[multicoloured_def, necklace_empty] +QED + +(* Idea: (multicoloured n 1) with one color is EMPTY. *) + +(* Theorem: multicoloured n 1 = {} *) +(* Proof: + multicoloured n 1 + = (necklace n 1) DIFF (monocoloured n 1) by multicoloured_def + = {necklace n 1} DIFF (necklace n 1) by monocoloured_mono + = {} by DIFF_EQ_EMPTY +*) +Theorem multicoloured_n_1: + !n. multicoloured n 1 = {} +Proof + simp[multicoloured_def, monocoloured_mono] +QED + +(* Theorem: multicoloured n 0 = {} /\ multicoloured n 1 = {} *) +(* Proof: by multicoloured_n_0, multicoloured_n_1. *) +Theorem multicoloured_empty: + !n. multicoloured n 0 = {} /\ multicoloured n 1 = {} +Proof + simp[multicoloured_n_0, multicoloured_n_1] +QED + +(* ------------------------------------------------------------------------- *) +(* To show: CARD (multicoloured n a) = a^n - a. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: a multicoloured necklace is not monocoloured. *) + +(* Theorem: DISJOINT (multicoloured n a) (monocoloured n a) *) +(* Proof: + Let s = necklace n a, t = monocoloured n a. + Then multicoloured n a = s DIFF t by multicoloured_def + so DISJOINT (multicoloured n a) t by DISJOINT_DIFF +*) +Theorem multi_mono_disjoint: + !n a. DISJOINT (multicoloured n a) (monocoloured n a) +Proof + simp[multicoloured_def, DISJOINT_DIFF] +QED + +(* Idea: a necklace is either monocoloured or multicolored. *) + +(* Theorem: necklace n a = (multicoloured n a) UNION (monocoloured n a) *) +(* Proof: + Let s = necklace n a, t = monocoloured n a. + Then multicoloured n a = s DIFF t by multicoloured_def + Now t SUBSET s by monocoloured_subset + so necklace n a = s + = (multicoloured n a) UNION t by UNION_DIFF +*) +Theorem multi_mono_exhaust: + !n a. necklace n a = (multicoloured n a) UNION (monocoloured n a) +Proof + simp[multicoloured_def, monocoloured_subset, UNION_DIFF] +QED + +(* Idea: size of (multicoloured n a) = a^n - a. *) + +(* Theorem: 0 < n ==> (CARD (multicoloured n a) = a ** n - a) *) +(* Proof: + Let s = necklace n a, + t = monocoloured n a. + Note t SUBSET s by monocoloured_subset + and FINITE s by necklace_finite + CARD (multicoloured n a) + = CARD (s DIFF t) by multicoloured_def + = CARD s - CARD t by SUBSET_DIFF_CARD, t SUBSET s + = a ** n - CARD t by necklace_card + = a ** n - a by monocoloured_card, 0 < n +*) +Theorem multicoloured_card: + !n a. 0 < n ==> (CARD (multicoloured n a) = a ** n - a) +Proof + rpt strip_tac >> + `(monocoloured n a) SUBSET (necklace n a)` by rw[monocoloured_subset] >> + `FINITE (necklace n a)` by rw[necklace_finite] >> + simp[multicoloured_def, SUBSET_DIFF_CARD, necklace_card, monocoloured_card] +QED + +(* Theorem: CARD (multicoloured n a) = if n = 0 then 0 else a ** n - a *) +(* Proof: + If n = 0, + CARD (multicoloured 0 a) + = CARD {} by multicoloured_0 + = 0 by CARD_EMPTY + If n <> 0, then 0 < n. + CARD (multicoloured 0 a) + = a ** n - a by multicoloured_card +*) +Theorem multicoloured_card_eqn: + !n a. CARD (multicoloured n a) = if n = 0 then 0 else a ** n - a +Proof + rpt strip_tac >> + Cases_on `n = 0` >- + simp[multicoloured_0] >> + simp[multicoloured_card] +QED + +(* Idea: (multicoloured n a) NOT empty when 1 < n /\ 1 < a. *) + +(* Theorem: 1 < n /\ 1 < a ==> (multicoloured n a) <> {} *) +(* Proof: + Let s = multicoloured n a. + Then FINITE s by multicoloured_finite + and CARD s = a ** n - a by multicoloured_card + Note a < a ** n by EXP_LT, 1 < a, 1 < n + Thus CARD s <> 0, + or s <> {} by CARD_EMPTY +*) +Theorem multicoloured_nonempty: + !n a. 1 < n /\ 1 < a ==> (multicoloured n a) <> {} +Proof + rpt strip_tac >> + qabbrev_tac `s = multicoloured n a` >> + `FINITE s` by rw[multicoloured_finite, Abbr`s`] >> + `CARD s = a ** n - a` by rw[multicoloured_card, Abbr`s`] >> + `a < a ** n` by rw[EXP_LT] >> + `CARD s <> 0` by decide_tac >> + rfs[] +QED + +(* ------------------------------------------------------------------------- *) + +(* For revised necklace proof using GCD. *) + +(* Idea: multicoloured lists are not monocoloured. *) + +(* Theorem: ls IN multicoloured n a ==> ~(ls IN monocoloured n a) *) +(* Proof: + Let s = necklace n a, + t = monocoloured n a. + Note multicoloured n a = s DIFF t by multicoloured_def + so ls IN multicoloured n a + ==> ls NOTIN t by IN_DIFF +*) +Theorem multicoloured_not_monocoloured: + !n a ls. ls IN multicoloured n a ==> ~(ls IN monocoloured n a) +Proof + simp[multicoloured_def] +QED + +(* Theorem: ls IN necklace n a ==> + (ls IN multicoloured n a <=> ~(ls IN monocoloured n a)) *) +(* Proof: + Let s = necklace n a, + t = monocoloured n a. + Note multicoloured n a = s DIFF t by multicoloured_def + so ls IN multicoloured n a + <=> ls IN s /\ ls NOTIN t by IN_DIFF +*) +Theorem multicoloured_not_monocoloured_iff: + !n a ls. ls IN necklace n a ==> + (ls IN multicoloured n a <=> ~(ls IN monocoloured n a)) +Proof + simp[multicoloured_def] +QED + +(* Theorem: ls IN necklace n a ==> + ls IN multicoloured n a \/ ls IN monocoloured n a *) +(* Proof: by multicoloured_def. *) +Theorem multicoloured_or_monocoloured: + !n a ls. ls IN necklace n a ==> + ls IN multicoloured n a \/ ls IN monocoloured n a +Proof + simp[multicoloured_def] +QED + +(* ------------------------------------------------------------------------- *) +(* Combinatorics Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading (# is temporary): +*) +(* Definitions and Theorems (# are exported, ! are in compute): + + Counting number of combinations: + sub_count_def |- !n k. sub_count n k = {s | s SUBSET count n /\ CARD s = k} + choose_def |- !n k. n choose k = CARD (sub_count n k) + sub_count_element |- !n k s. s IN sub_count n k <=> s SUBSET count n /\ CARD s = k + sub_count_subset |- !n k. sub_count n k SUBSET POW (count n) + sub_count_finite |- !n k. FINITE (sub_count n k) + sub_count_element_no_self + |- !n k s. s IN sub_count n k ==> n NOTIN s + sub_count_element_finite + |- !n k s. s IN sub_count n k ==> FINITE s + sub_count_n_0 |- !n. sub_count n 0 = {{}} + sub_count_0_n |- !n. sub_count 0 n = if n = 0 then {{}} else {} + sub_count_n_1 |- !n. sub_count n 1 = {{j} | j < n} + sub_count_n_n |- !n. sub_count n n = {count n} + sub_count_eq_empty |- !n k. sub_count n k = {} <=> n < k + sub_count_union |- !n k. sub_count (n + 1) (k + 1) = + IMAGE (\s. n INSERT s) (sub_count n k) UNION + sub_count n (k + 1) + sub_count_disjoint |- !n k. DISJOINT (IMAGE (\s. n INSERT s) (sub_count n k)) + (sub_count n (k + 1)) + sub_count_insert |- !n k s. s IN sub_count n k ==> + n INSERT s IN sub_count (n + 1) (k + 1) + sub_count_insert_card + |- !n k. CARD (IMAGE (\s. n INSERT s) (sub_count n k)) = + n choose k + sub_count_alt |- !n k. sub_count n 0 = {{}} /\ sub_count 0 (k + 1) = {} /\ + sub_count (n + 1) (k + 1) = + IMAGE (\s. n INSERT s) (sub_count n k) UNION + sub_count n (k + 1) +! sub_count_eqn |- !n k. sub_count n k = + if k = 0 then {{}} + else if n = 0 then {} + else IMAGE (\s. n - 1 INSERT s) (sub_count (n - 1) (k - 1)) UNION + sub_count (n - 1) k + choose_n_0 |- !n. n choose 0 = 1 + choose_n_1 |- !n. n choose 1 = n + choose_eq_0 |- !n k. n choose k = 0 <=> n < k + choose_0_n |- !n. 0 choose n = if n = 0 then 1 else 0 + choose_1_n |- !n. 1 choose n = if 1 < n then 0 else 1 + choose_n_n |- !n. n choose n = 1 + choose_recurrence |- !n k. (n + 1) choose (k + 1) = n choose k + n choose (k + 1) + choose_alt |- !n k. n choose 0 = 1 /\ 0 choose (k + 1) = 0 /\ + (n + 1) choose (k + 1) = n choose k + n choose (k + 1) +! choose_eqn |- !n k. n choose k = binomial n k + + Partition of the set of subsets by bijective equivalence: + sub_sets_def |- !P k. sub_sets P k = {s | s SUBSET P /\ CARD s = k} + sub_sets_sub_count |- !n k. sub_sets (count n) k = sub_count n k + sub_sets_equiv_class|- !s t. FINITE t /\ s SUBSET t ==> + sub_sets t (CARD s) = equiv_class $=b= (POW t) s + sub_count_equiv_class + |- !n k. k <= n ==> + sub_count n k = + equiv_class $=b= (POW (count n)) (count k) + count_power_partition |- !n. partition $=b= (POW (count n)) = + IMAGE (sub_count n) (upto n) + sub_count_count_inj |- !n m. INJ (sub_count n) (upto n) + univ(:(num -> bool) -> bool) + choose_sum_over_count |- !n. SIGMA ($choose n) (upto n) = 2 ** n + choose_sum_over_all |- !n. SUM (MAP ($choose n) [0 .. n]) = 2 ** n + + Counting number of permutations: + perm_count_def |- !n. perm_count n = {ls | ALL_DISTINCT ls /\ set ls = count n} + perm_def |- !n. perm n = CARD (perm_count n) + perm_count_element |- !ls n. ls IN perm_count n <=> ALL_DISTINCT ls /\ set ls = count n + perm_count_element_no_self + |- !ls n. ls IN perm_count n ==> ~MEM n ls + perm_count_element_length + |- !ls n. ls IN perm_count n ==> LENGTH ls = n + perm_count_subset |- !n. perm_count n SUBSET necklace n n + perm_count_finite |- !n. FINITE (perm_count n) + perm_count_0 |- perm_count 0 = {[]} + perm_count_1 |- perm_count 1 = {[0]} + interleave_def |- !x ls. x interleave ls = + IMAGE (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) + interleave_alt |- !ls x. x interleave ls = + {TAKE k ls ++ x::DROP k ls | k | k <= LENGTH ls} + interleave_element |- !ls x y. y IN x interleave ls <=> + ?k. k <= LENGTH ls /\ y = TAKE k ls ++ x::DROP k ls + interleave_nil |- !x. x interleave [] = {[x]} + interleave_length |- !ls x y. y IN x interleave ls ==> LENGTH y = 1 + LENGTH ls + interleave_distinct |- !ls x y. ALL_DISTINCT (x::ls) /\ y IN x interleave ls ==> + ALL_DISTINCT y + interleave_distinct_alt + |- !ls x y. ALL_DISTINCT ls /\ ~MEM x ls /\ + y IN x interleave ls ==> ALL_DISTINCT y + interleave_set |- !ls x y. y IN x interleave ls ==> set y = set (x::ls) + interleave_set_alt |- !ls x y. y IN x interleave ls ==> set y = x INSERT set ls + interleave_has_cons |- !ls x. x::ls IN x interleave ls + interleave_not_empty|- !ls x. x interleave ls <> {} + interleave_eq |- !n x y. ~MEM n x /\ ~MEM n y ==> + (n interleave x = n interleave y <=> x = y) + interleave_disjoint |- !l1 l2 x. ~MEM x l1 /\ l1 <> l2 ==> + DISJOINT (x interleave l1) (x interleave l2) + interleave_finite |- !ls x. FINITE (x interleave ls) + interleave_count_inj|- !ls x. ~MEM x ls ==> + INJ (\k. TAKE k ls ++ x::DROP k ls) + (upto (LENGTH ls)) univ(:'a list) + interleave_card |- !ls x. ~MEM x ls ==> CARD (x interleave ls) = 1 + LENGTH ls + interleave_revert |- !ls h. ALL_DISTINCT ls /\ MEM h ls ==> + ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ + set t = set ls DELETE h + interleave_revert_count + |- !ls n. ALL_DISTINCT ls /\ set ls = upto n ==> + ?t. ALL_DISTINCT t /\ ls IN n interleave t /\ + set t = count n + perm_count_suc |- !n. perm_count (SUC n) = + BIGUNION (IMAGE ($interleave n) (perm_count n)) + perm_count_suc_alt |- !n. perm_count (n + 1) = + BIGUNION (IMAGE ($interleave n) (perm_count n)) +! perm_count_eqn |- !n. perm_count n = + if n = 0 then {[]} + else BIGUNION (IMAGE ($interleave (n - 1)) (perm_count (n - 1))) + perm_0 |- perm 0 = 1 + perm_1 |- perm 1 = 1 + perm_count_interleave_finite + |- !n e. e IN IMAGE ($interleave n) (perm_count n) ==> FINITE e + perm_count_interleave_card + |- !n e. e IN IMAGE ($interleave n) (perm_count n) ==> CARD e = n + 1 + perm_count_interleave_disjoint + |- !n e s t. s IN IMAGE ($interleave n) (perm_count n) /\ + t IN IMAGE ($interleave n) (perm_count n) /\ s <> t ==> + DISJOINT s t + perm_count_interleave_inj + |- !n. INJ ($interleave n) (perm_count n) univ(:num list -> bool) + perm_suc |- !n. perm (SUC n) = SUC n * perm n + perm_suc_alt |- !n. perm (n + 1) = (n + 1) * perm n +! perm_eq_fact |- !n. perm n = FACT n + + Permutations of a set: + perm_set_def |- !s. perm_set s = {ls | ALL_DISTINCT ls /\ set ls = s} + perm_set_element |- !ls s. ls IN perm_set s <=> ALL_DISTINCT ls /\ set ls = s + perm_set_perm_count |- !n. perm_set (count n) = perm_count n + perm_set_empty |- perm_set {} = {[]} + perm_set_sing |- !x. perm_set {x} = {[x]} + perm_set_eq_empty_sing + |- !s. perm_set s = {[]} <=> s = {} + perm_set_has_self_list + |- !s. FINITE s ==> SET_TO_LIST s IN perm_set s + perm_set_not_empty |- !s. FINITE s ==> perm_set s <> {} + perm_set_list_not_empty + |- !ls. perm_set (set ls) <> {} + perm_set_map_element|- !ls f s n. ls IN perm_set s /\ BIJ f s (count n) ==> + MAP f ls IN perm_count n + perm_set_map_inj |- !f s n. BIJ f s (count n) ==> INJ (MAP f) (perm_set s) (perm_count n) + perm_set_map_surj |- !f s n. BIJ f s (count n) ==> SURJ (MAP f) (perm_set s) (perm_count n) + perm_set_map_bij |- !f s n. BIJ f s (count n) ==> BIJ (MAP f) (perm_set s) (perm_count n) + perm_set_bij_eq_perm_count + |- !s. FINITE s ==> perm_set s =b= perm_count (CARD s) + perm_set_finite |- !s. FINITE s ==> FINITE (perm_set s) + perm_set_card |- !s. FINITE s ==> CARD (perm_set s) = perm (CARD s) + perm_set_card_alt |- !s. FINITE s ==> CARD (perm_set s) = FACT (CARD s) + + Counting number of arrangements: + list_count_def |- !n k. list_count n k = + {ls | ALL_DISTINCT ls /\ + set ls SUBSET count n /\ LENGTH ls = k} + arrange_def |- !n k. n arrange k = CARD (list_count n k) + list_count_alt |- !n k. list_count n k = + {ls | ALL_DISTINCT ls /\ + set ls SUBSET count n /\ CARD (set ls) = k} + list_count_element |- !ls n k. ls IN list_count n k <=> + ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k + list_count_element_alt + |- !ls n k. ls IN list_count n k <=> + ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = k + list_count_element_set_card + |- !ls n k. ls IN list_count n k ==> CARD (set ls) = k + list_count_subset |- !n k. list_count n k SUBSET necklace k n + list_count_finite |- !n k. FINITE (list_count n k) + list_count_n_0 |- !n. list_count n 0 = {[]} + list_count_0_n |- !n. 0 < n ==> list_count 0 n = {} + list_count_n_n |- !n. list_count n n = perm_count n + list_count_eq_empty |- !n k. list_count n k = {} <=> n < k + list_count_by_image |- !n k. 0 < k ==> + list_count n k = + IMAGE (\ls. if ALL_DISTINCT ls then ls else []) + (necklace k n) DELETE [] +! list_count_eqn |- !n k. list_count n k = + if k = 0 then {[]} + else IMAGE (\ls. if ALL_DISTINCT ls then ls else []) + (necklace k n) DELETE [] + feq_set_equiv |- !s. feq set equiv_on s + list_count_set_eq_class + |- !ls n k. ls IN list_count n k ==> + equiv_class (feq set) (list_count n k) ls = perm_set (set ls) + list_count_set_eq_class_card + |- !ls n k. ls IN list_count n k ==> + CARD (equiv_class (feq set) (list_count n k) ls) = perm k + list_count_set_partititon_element_card + |- !n k e. e IN partition (feq set) (list_count n k) ==> CARD e = perm k + list_count_element_perm_set_not_empty + |- !ls n k. ls IN list_count n k ==> perm_set (set ls) <> {} + list_count_set_map_element + |- !s n k. s IN partition (feq set) (list_count n k) ==> + (set o CHOICE) s IN sub_count n k + list_count_set_map_inj + |- !n k. INJ (set o CHOICE) + (partition (feq set) (list_count n k)) + (sub_count n k) + list_count_set_map_surj + |- !n k. SURJ (set o CHOICE) + (partition (feq set) (list_count n k)) + (sub_count n k) + list_count_set_map_bij + |- !n k. BIJ (set o CHOICE) + (partition (feq set) (list_count n k)) + (sub_count n k) +! arrange_eqn |- !n k. n arrange k = (n choose k) * perm k + arrange_alt |- !n k. n arrange k = (n choose k) * FACT k + arrange_formula |- !n k. n arrange k = binomial n k * FACT k + arrange_formula2 |- !n k. k <= n ==> n arrange k = FACT n DIV FACT (n - k) + arrange_n_0 |- !n. n arrange 0 = 1 + arrange_0_n |- !n. 0 < n ==> 0 arrange n = 0 + arrange_n_n |- !n. n arrange n = perm n + arrange_n_n_alt |- !n. n arrange n = FACT n + arrange_eq_0 |- !n k. n arrange k = 0 <=> n < k +*) + +(* ------------------------------------------------------------------------- *) +(* Counting number of combinations. *) +(* ------------------------------------------------------------------------- *) + +(* The strategy: +This is to show, ultimately, C(n,k) = binomial n k. + +Conceptually, +C(n,k) = number of ways to choose k elements from a set of n elements. +Each choice gives a k-subset. + +Define C(n,k) = number of k-subsets of an n-set. +Prove that C(n,k) = binomial n k: +(1) C(0,0) = 1 +(2) C(0,1) = 0 +(3) C(SUC n, SUC k) = C(n,k) + C(n,SUC k) +show that any such C's is just the binomial function. + +binomial_alt +|- !n k. binomial n 0 = 1 /\ binomial 0 (k + 1) = 0 /\ + binomial (n + 1) (k + 1) = binomial n k + binomial n (k + 1) + +Moreover, bij_eq is an equivalence relation, and partitions the power set +of (count n) into equivalence classes of k-subsets for k = 0 to n. Thus + +SUM (GENLIST (choose n) (SUC n)) = CARD (POW (count n)) = 2 ** n + +the counterpart of binomial_sum |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n +*) + +(* Define the set of choices of k-subsets of (count n). *) +Definition sub_count_def[nocompute]: + sub_count n k = { (s:num -> bool) | s SUBSET (count n) /\ CARD s = k} +End +(* use [nocompute] as this is not effective for evalutaion. *) + +(* Define the number of choices of k-subsets of (count n). *) +Definition choose_def[nocompute]: + choose n k = CARD (sub_count n k) +End +(* use [nocompute] as this is not effective for evalutaion. *) +(* make this an infix operator *) +val _ = set_fixity "choose" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) +(* val choose_def = |- !n k. n choose k = CARD (sub_count n k): thm *) + +(* Theorem: s IN sub_count n k <=> s SUBSET count n /\ CARD s = k *) +(* Proof: by sub_count_def. *) +Theorem sub_count_element: + !n k s. s IN sub_count n k <=> s SUBSET count n /\ CARD s = k +Proof + simp[sub_count_def] +QED + +(* Theorem: (sub_count n k) SUBSET (POW (count n)) *) +(* Proof: + s IN sub_count n k + ==> s SUBSET (count n) by sub_count_def + ==> s IN POW (count n) by POW_DEF + Thus (sub_count n k) SUBSET (POW (count n)) by SUBSET_DEF +*) +Theorem sub_count_subset: + !n k. (sub_count n k) SUBSET (POW (count n)) +Proof + simp[sub_count_def, POW_DEF, SUBSET_DEF] +QED + +(* Theorem: FINITE (sub_count n k) *) +(* Proof: + Note (sub_count n k) SUBSET (POW (count n)) by sub_count_subset + and FINITE (count n) by FINITE_COUNT + so FINITE (POW (count n)) by FINITE_POW + Thus FINITE (sub_count n k) by SUBSET_FINITE +*) +Theorem sub_count_finite: + !n k. FINITE (sub_count n k) +Proof + metis_tac[sub_count_subset, FINITE_COUNT, FINITE_POW, SUBSET_FINITE] +QED + +(* Theorem: s IN sub_count n k ==> n NOTIN s *) +(* Proof: + Note s SUBSET (count n) by sub_count_element + and n NOTIN (count n) by COUNT_NOT_SELF + so n NOTIN s by SUBSET_DEF +*) +Theorem sub_count_element_no_self: + !n k s. s IN sub_count n k ==> n NOTIN s +Proof + metis_tac[sub_count_element, SUBSET_DEF, COUNT_NOT_SELF] +QED + +(* Theorem: s IN sub_count n k ==> FINITE s *) +(* Proof: + Note s SUBSET (count n) by sub_count_element + and FINITE (count n) by FINITE_COUNT + so FINITE s by SUBSET_FINITE +*) +Theorem sub_count_element_finite: + !n k s. s IN sub_count n k ==> FINITE s +Proof + metis_tac[sub_count_element, FINITE_COUNT, SUBSET_FINITE] +QED + +(* Theorem: sub_count n 0 = { EMPTY } *) +(* Proof: + By EXTENSION, IN_SING, this is to show: + (1) x IN sub_count n 0 ==> x = {} + x IN sub_count n 0 + <=> x SUBSET count n /\ CARD x = 0 by sub_count_def + ==> FINITE x /\ CARD x = 0 by SUBSET_FINITE, FINITE_COUNT + ==> x = {} by CARD_EQ_0 + (2) {} IN sub_count n 0 + {} IN sub_count n 0 + <=> {} SUBSET count n /\ CARD {} = 0 by sub_count_def + <=> T /\ CARD {} = 0 by EMPTY_SUBSET + <=> T /\ T by CARD_EMPTY + <=> T +*) +Theorem sub_count_n_0: + !n. sub_count n 0 = { EMPTY } +Proof + rewrite_tac[EXTENSION, EQ_IMP_THM] >> + rw[IN_SING] >| [ + fs[sub_count_def] >> + metis_tac[CARD_EQ_0, SUBSET_FINITE, FINITE_COUNT], + rw[sub_count_def] + ] +QED + +(* Theorem: sub_count 0 n = if n = 0 then { EMPTY } else EMPTY *) +(* Proof: + If n = 0, + then sub_count 0 n = { EMPTY } by sub_count_n_0 + If n <> 0, + s IN sub_count 0 n + <=> s SUBSET count 0 /\ CARD s = n by sub_count_def + <=> s SUBSET {} /\ CARD s = n by COUNT_0 + <=> CARD {} = n by SUBSET_EMPTY + <=> 0 = n by CARD_EMPTY + <=> F by n <> 0 + Thus sub_count 0 n = {} by MEMBER_NOT_EMPTY +*) +Theorem sub_count_0_n: + !n. sub_count 0 n = if n = 0 then { EMPTY } else EMPTY +Proof + rw[sub_count_n_0] >> + rw[sub_count_def, EXTENSION] >> + spose_not_then strip_assume_tac >> + `x = {}` by metis_tac[MEMBER_NOT_EMPTY] >> + fs[] +QED + +(* Theorem: sub_count n 1 = {{j} | j < n } *) +(* Proof: + By sub_count_def, EXTENSION, this is to show: + x SUBSET count n /\ CARD x = 1 <=> + ?j. (!x'. x' IN x <=> x' = j) /\ j < n + If part: + Note FINITE x by SUBSET_FINITE, FINITE_COUNT + so ?j. x = {j} by CARD_EQ_1, SING_DEF + Take this j. + Then !x'. x' IN x <=> x' = j + by IN_SING + and x SUBSET (count n) ==> j < n + by SUBSET_DEF, IN_COUNT + Only-if part: + Note j IN x, so x <> {} by MEMBER_NOT_EMPTY + The given shows x = {j} by ONE_ELEMENT_SING + and j < n ==> x SUBSET (count n) + by SUBSET_DEF, IN_COUNT + and CARD x = 1 by CARD_SING +*) +Theorem sub_count_n_1: + !n. sub_count n 1 = {{j} | j < n } +Proof + rw[sub_count_def, EXTENSION] >> + rw[EQ_IMP_THM] >| [ + `FINITE x` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + `?j. x = {j}` by metis_tac[CARD_EQ_1, SING_DEF] >> + metis_tac[SUBSET_DEF, IN_SING, IN_COUNT], + rw[SUBSET_DEF], + metis_tac[ONE_ELEMENT_SING, MEMBER_NOT_EMPTY, CARD_SING] + ] +QED + +(* Theorem: sub_count n n = {count n} *) +(* Proof: + s IN sub_count n n + <=> s SUBSET count n /\ CARD s = n by sub_count_def + <=> s SUBSET count n /\ CARD s = CARD (count n) + by CARD_COUNT + <=> s SUBSET count n /\ s = count n by SUBSET_CARD_EQ + <=> T /\ s = count n by SUBSET_REFL + Thus sub_count n n = {count n} by EXTENSION +*) +Theorem sub_count_n_n: + !n. sub_count n n = {count n} +Proof + rw_tac bool_ss[EXTENSION] >> + `FINITE (count n) /\ CARD (count n) = n` by rw[] >> + metis_tac[sub_count_element, SUBSET_CARD_EQ, SUBSET_REFL, IN_SING] +QED + +(* Theorem: sub_count n k = EMPTY <=> n < k *) +(* Proof: + If part: sub_count n k = {} ==> n < k + By contradiction, suppose k <= n. + Then (count k) SUBSET (count n) by COUNT_SUBSET, k <= n + and CARD (count k) = k by CARD_COUNT + so (count k) IN sub_count n k by sub_count_element + Thus sub_count n k <> {} by MEMBER_NOT_EMPTY + which is a contradiction. + Only-if part: n < k ==> sub_count n k = {} + By contradiction, suppose sub_count n k <> {}. + Then ?s. s IN sub_count n k by MEMBER_NOT_EMPTY + ==> s SUBSET count n /\ CARD s = k + by sub_count_element + Note FINITE (count n) by FINITE_COUNT + so CARD s <= CARD (count n) by CARD_SUBSET + ==> k <= n by CARD_COUNT + This contradicts n < k. +*) +Theorem sub_count_eq_empty: + !n k. sub_count n k = EMPTY <=> n < k +Proof + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `(count k) SUBSET (count n)` by rw[COUNT_SUBSET] >> + `CARD (count k) = k` by rw[] >> + metis_tac[sub_count_element, MEMBER_NOT_EMPTY], + spose_not_then strip_assume_tac >> + `?s. s IN sub_count n k` by rw[MEMBER_NOT_EMPTY] >> + fs[sub_count_element] >> + `FINITE (count n)` by rw[] >> + `CARD s <= n` by metis_tac[CARD_SUBSET, CARD_COUNT] >> + decide_tac + ] +QED + +(* Theorem: sub_count (n + 1) (k + 1) = + IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) *) +(* Proof: + By sub_count_def, EXTENSION, this is to show: + (1) x SUBSET count (n + 1) /\ CARD x = k + 1 ==> + ?s. (!y. y IN x <=> y = n \/ y IN s) /\ + s SUBSET count n /\ CARD s = k) \/ x SUBSET count n + Suppose ~(x SUBSET count n), + Then n IN x by SUBSET_DEF + Take s = x DELETE n. + Then y IN x <=> + y = n \/ y IN s by EXTENSION + and s SUBSET x by DELETE_SUBSET + so s SUBSET (count (n + 1) DELETE n) + by SUBSET_DELETE_BOTH + or s SUBSET (count n) by count_def + Note FINITE x by SUBSET_FINITE, FINITE_COUNT + so CARD s = k by CARD_DELETE, CARD_COUNT + (2) s SUBSET count n /\ x = n INSERT s ==> x SUBSET count (n + 1) + Note x SUBSET (n INSERT count n) by SUBSET_INSERT_BOTH + so x INSERT count (n + 1) by count_def, or count_add1 + (3) s SUBSET count n /\ x = n INSERT s ==> CARD x = CARD s + 1 + Note n NOTIN s by SUBSET_DEF, COUNT_NOT_SELF + and FINITE s by SUBSET_FINITE, FINITE_COUNT + so CARD x + = SUC (CARD s) by CARD_INSERT + = CARD s + 1 by ADD1 + (4) x SUBSET count n ==> x SUBSET count (n + 1) + Note (count n) SUBSET count (n + 1) by COUNT_SUBSET, n <= n + 1 + so x SUBSET count (n + 1) by SUBSET_TRANS +*) +Theorem sub_count_union: + !n k. sub_count (n + 1) (k + 1) = + IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) +Proof + rw[sub_count_def, EXTENSION, Once EQ_IMP_THM] >> simp[] >| [ + rename [‘x SUBSET count (n + 1)’, ‘CARD x = k + 1’] >> + Cases_on `x SUBSET count n` >> simp[] >> + `n IN x` by + (fs[SUBSET_DEF] >> rename [‘m IN x’, ‘~(m < n)’] >> + `m < n + 1` by simp[] >> + `m = n` by decide_tac >> + fs[]) >> + qexists_tac `x DELETE n` >> + `FINITE x` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + rw[] >- (rw[EQ_IMP_THM] >> simp[]) >> + `x DELETE n SUBSET (count (n + 1)) DELETE n` by rw[SUBSET_DELETE_BOTH] >> + `count (n + 1) DELETE n = count n` by rw[EXTENSION] >> + fs[], + + rename [‘s SUBSET count n’, ‘x SUBSET count (n + 1)’] >> + `x = n INSERT s` by fs[EXTENSION] >> + `x SUBSET (n INSERT count n)` by rw[SUBSET_INSERT_BOTH] >> + rfs[count_add1], + + rename [‘s SUBSET count n’, ‘CARD x = CARD s + 1’] >> + `x = n INSERT s` by fs[EXTENSION] >> + `n NOTIN s` by metis_tac[SUBSET_DEF, COUNT_NOT_SELF] >> + `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + rw[], + + metis_tac[COUNT_SUBSET, SUBSET_TRANS, DECIDE “n <= n + 1”] + ] +QED + +(* Theorem: DISJOINT (IMAGE (\s. n INSERT s) (sub_count n k)) (sub_count n (k + 1)) *) +(* Proof: + Let s = IMAGE (\s. n INSERT s) (sub_count n k), + t = sub_count n (k + 1). + By DISJOINT_DEF and contradiction, suppose s INTER t <> {}. + Then ?x. x IN s /\ x IN t by IN_INTER, MEMBER_NOT_EMPTY + Note n IN x by IN_IMAGE, IN_INSERT + but n NOTIN x by sub_count_element_no_self + This is a contradiction. +*) +Theorem sub_count_disjoint: + !n k. DISJOINT (IMAGE (\s. n INSERT s) (sub_count n k)) (sub_count n (k + 1)) +Proof + rw[DISJOINT_DEF, EXTENSION] >> + spose_not_then strip_assume_tac >> + rename [‘s IN sub_count n k’, ‘x IN sub_count n (k + 1)’] >> + `x = n INSERT s` by fs[EXTENSION] >> + `n IN x` by fs[] >> + metis_tac[sub_count_element_no_self] +QED + +(* Theorem: s IN sub_count n k ==> (n INSERT s) IN sub_count (n + 1) (k + 1) *) +(* Proof: + Note s SUBSET count n /\ CARD s = k by sub_count_element + and n NOTIN s by sub_count_element_no_self + and FINITE s by sub_count_element_finite + Now (n INSERT s) SUBSET (n INSERT count n) + by SUBSET_INSERT_BOTH + and n INSERT count n = count (n + 1) by count_add1 + and CARD (n INSERT s) = CARD s + 1 by CARD_INSERT + = k + 1 by given + Thus (n INSERT s) IN sub_count (n + 1) (k + 1) + by sub_count_element +*) +Theorem sub_count_insert: + !n k s. s IN sub_count n k ==> (n INSERT s) IN sub_count (n + 1) (k + 1) +Proof + rw[sub_count_def] >| [ + `!x. x < n ==> x < n + 1` by decide_tac >> + metis_tac[SUBSET_DEF, IN_COUNT], + `n NOTIN s` by metis_tac[SUBSET_DEF, COUNT_NOT_SELF] >> + `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + rw[] + ] +QED + +(* Theorem: CARD (IMAGE (\s. n INSERT s) (sub_count n k)) = n choose k *) +(* Proof: + Let f = \s. n INSERT s. + By choose_def, INJ_CARD_IMAGE, this is to show: + (1) FINITE (sub_count n k), true by sub_count_finite + (2) ?t. INJ f (sub_count n k) t + Let t = sub_count (n + 1) (k + 1). + By INJ_DEF, this is to show: + (1) s IN sub_count n k ==> n INSERT s IN sub_count (n + 1) (k + 1) + This is true by sub_count_insert + (2) s' IN sub_count n k /\ s IN sub_count n k /\ + n INSERT s' = n INSERT s ==> s' = s + Note n NOTIN s by sub_count_element_no_self + and n NOTIN s' by sub_count_element_no_self + s' + = s' DELETE n by DELETE_NON_ELEMENT + = (n INSERT s') DELETE n by DELETE_INSERT + = (n INSERT s) DELETE n by given + = s DELETE n by DELETE_INSERT + = s by DELETE_NON_ELEMENT +*) +Theorem sub_count_insert_card: + !n k. CARD (IMAGE (\s. n INSERT s) (sub_count n k)) = n choose k +Proof + rw[choose_def] >> + qabbrev_tac `f = \s. n INSERT s` >> + irule INJ_CARD_IMAGE >> + rpt strip_tac >- + rw[sub_count_finite] >> + qexists_tac `sub_count (n + 1) (k + 1)` >> + rw[INJ_DEF, Abbr`f`] >- + rw[sub_count_insert] >> + rename [‘n INSERT s1 = n INSERT s2’] >> + `n NOTIN s1 /\ n NOTIN s2` by metis_tac[sub_count_element_no_self] >> + metis_tac[DELETE_INSERT, DELETE_NON_ELEMENT] +QED + +(* Theorem: sub_count n 0 = { EMPTY } /\ + sub_count 0 (k + 1) = {} /\ + sub_count (n + 1) (k + 1) = + IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) *) +(* Proof: by sub_count_n_0, sub_count_0_n, sub_count_union. *) +Theorem sub_count_alt: + !n k. sub_count n 0 = { EMPTY } /\ + sub_count 0 (k + 1) = {} /\ + sub_count (n + 1) (k + 1) = + IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) +Proof + simp[sub_count_n_0, sub_count_0_n, sub_count_union] +QED + +(* Theorem: sub_count n k = + if k = 0 then { EMPTY } + else if n = 0 then {} + else IMAGE (\s. (n - 1) INSERT s) (sub_count (n - 1) (k - 1)) UNION + sub_count (n - 1) k *) +(* Proof: by sub_count_n_0, sub_count_0_n, sub_count_union. *) +Theorem sub_count_eqn[compute]: + !n k. sub_count n k = + if k = 0 then { EMPTY } + else if n = 0 then {} + else IMAGE (\s. (n - 1) INSERT s) (sub_count (n - 1) (k - 1)) UNION + sub_count (n - 1) k +Proof + rw[sub_count_n_0, sub_count_0_n] >> + metis_tac[sub_count_union, num_CASES, SUC_SUB1, ADD1] +QED + +(* +> EVAL ``sub_count 3 2``; +val it = |- sub_count 3 2 = {{2; 1}; {2; 0}; {1; 0}}: thm +> EVAL ``sub_count 4 2``; +val it = |- sub_count 4 2 = {{3; 2}; {3; 1}; {3; 0}; {2; 1}; {2; 0}; {1; 0}}: thm +> EVAL ``sub_count 3 3``; +val it = |- sub_count 3 3 = {{2; 1; 0}}: thm +*) + +(* Theorem: n choose 0 = 1 *) +(* Proof: + n choose 0 + = CARD (sub_count n 0) by choose_def + = CARD {{}} by sub_count_n_0 + = 1 by CARD_SING +*) +Theorem choose_n_0: + !n. n choose 0 = 1 +Proof + simp[choose_def, sub_count_n_0] +QED + +(* Theorem: n choose 1 = n *) +(* Proof: + Let s = {{j} | j < n}, + f = \j. {j}. + Then s = IMAGE f (count n) by EXTENSION + Note FINITE (count n) + and INJ f (count n) (POW (count n)) + Thus n choose 1 + = CARD (sub_count n 1) by choose_def + = CARD s by sub_count_n_1 + = CARD (count n) by INJ_CARD_IMAGE + = n by CARD_COUNT +*) +Theorem choose_n_1: + !n. n choose 1 = n +Proof + rw[choose_def, sub_count_n_1] >> + qabbrev_tac `s = {{j} | j < n}` >> + qabbrev_tac `f = \j:num. {j}` >> + `s = IMAGE f (count n)` by fs[EXTENSION, Abbr`f`, Abbr`s`] >> + `CARD (IMAGE f (count n)) = CARD (count n)` suffices_by fs[] >> + irule INJ_CARD_IMAGE >> + rw[] >> + qexists_tac `POW (count n)` >> + rw[INJ_DEF, Abbr`f`] >> + rw[POW_DEF] +QED + +(* Theorem: n choose k = 0 <=> n < k *) +(* Proof: + Note FINITE (sub_count n k) by sub_count_finite + n choose k = 0 + <=> CARD (sub_count n k) = 0 by choose_def + <=> sub_count n k = {} by CARD_EQ_0 + <=> n < k by sub_count_eq_empty +*) +Theorem choose_eq_0: + !n k. n choose k = 0 <=> n < k +Proof + metis_tac[choose_def, sub_count_eq_empty, sub_count_finite, CARD_EQ_0] +QED + +(* Theorem: 0 choose n = if n = 0 then 1 else 0 *) +(* Proof: + 0 choose n + = CARD (sub_count 0 n) by choose_def + = CARD (if n = 0 then {{}} else {}) + by sub_count_0_n + = if n = 0 then 1 else 0 by CARD_SING, CARD_EMPTY +*) +Theorem choose_0_n: + !n. 0 choose n = if n = 0 then 1 else 0 +Proof + rw[choose_def, sub_count_0_n] +QED + +(* Theorem: 1 choose n = if 1 < n then 0 else 1 *) +(* Proof: + If n = 0, 1 choose 0 = 1 by choose_n_0 + If n = 1, 1 choose 1 = 1 by choose_n_1 + Otherwise, 1 choose n = 0 by choose_eq_0, 1 < n +*) +Theorem choose_1_n: + !n. 1 choose n = if 1 < n then 0 else 1 +Proof + rw[choose_eq_0] >> + `n = 0 \/ n = 1` by decide_tac >- + simp[choose_n_0] >> + simp[choose_n_1] +QED + +(* Theorem: n choose n = 1 *) +(* Proof: + n choose n + = CARD (sub_count n n) by choose_def + = CARD {count n} by sub_count_n_n + = 1 by CARD_SING +*) +Theorem choose_n_n: + !n. n choose n = 1 +Proof + simp[choose_def, sub_count_n_n] +QED + +(* Theorem: (n + 1) choose (k + 1) = n choose k + n choose (k + 1) *) +(* Proof: + Let s = sub_count (n + 1) (k + 1), + u = sub_count n k, + v = sub_count n (k + 1), + t = IMAGE (\s. n INSERT s) u. + Then s = t UNION v by sub_count_union + and DISJOINT t v by sub_count_disjoint + and FINITE u /\ FINITE v by sub_count_finite + and FINITE t by IMAGE_FINITE + Thus CARD s = CARD t + CARD v by CARD_UNION_DISJOINT + = CARD u + CARD v by sub_count_insert_card, choose_def +*) +Theorem choose_recurrence: + !n k. (n + 1) choose (k + 1) = n choose k + n choose (k + 1) +Proof + rw[choose_def] >> + qabbrev_tac `s = sub_count (n + 1) (k + 1)` >> + qabbrev_tac `u = sub_count n k` >> + qabbrev_tac `v = sub_count n (k + 1)` >> + qabbrev_tac `t = IMAGE (\s. n INSERT s) u` >> + `s = t UNION v` by rw[sub_count_union, Abbr`s`, Abbr`t`, Abbr`v`] >> + `DISJOINT t v` by metis_tac[sub_count_disjoint] >> + `FINITE u /\ FINITE v` by rw[sub_count_finite, Abbr`u`, Abbr`v`] >> + `FINITE t` by rw[Abbr`t`] >> + `CARD s = CARD t + CARD v` by rw[CARD_UNION_DISJOINT] >> + metis_tac[sub_count_insert_card, choose_def] +QED + +(* This is Pascal's identity: C(n+1,k+1) = C(n,k) + C(n,k+1). *) +(* This corresponds to the 'sum of parents' rule of Pascal's triangle. *) + +(* Theorem: n choose 0 = 1 /\ 0 choose (k + 1) = 0 /\ + (n + 1) choose (k + 1) = n choose k + n choose (k + 1) *) +(* Proof: by choose_n_0, choose_0_n, choose_recurrence. *) +Theorem choose_alt: + !n k. n choose 0 = 1 /\ 0 choose (k + 1) = 0 /\ + (n + 1) choose (k + 1) = n choose k + n choose (k + 1) +Proof + simp[choose_n_0, choose_0_n, choose_recurrence] +QED + +(* Theorem: n choose k = binomial n k *) +(* Proof: by binomial_iff, choose_alt. *) +Theorem choose_eqn[compute]: + !n k. n choose k = binomial n k +Proof + prove_tac[binomial_iff, choose_alt] +QED + +(* +> EVAL ``5 choose 3``; +val it = |- 5 choose 3 = 10: thm +> EVAL ``MAP ($choose 5) [0 .. 5]``; +val it = |- MAP ($choose 5) [0 .. 5] = [1; 5; 10; 10; 5; 1]: thm +*) + +(* ------------------------------------------------------------------------- *) +(* Partition of the set of subsets by bijective equivalence. *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of k-subsets of a set. *) +Definition sub_sets_def[nocompute]: + sub_sets P k = { s | s SUBSET P /\ CARD s = k} +End +(* use [nocompute] as this is not effective for evalutaion. *) + +(* Theorem: s IN sub_sets P k <=> s SUBSET P /\ CARD s = k *) +(* Proof: by sub_sets_def. *) +Theorem sub_sets_element: + !P k s. s IN sub_sets P k <=> s SUBSET P /\ CARD s = k +Proof + simp[sub_sets_def] +QED + +(* Theorem: sub_sets (count n) k = sub_count n k *) +(* Proof: + sub_sets (count n) k + = {s | s SUBSET (count n) /\ CARD s = k} by sub_sets_def + = sub_count n k by sub_count_def +*) +Theorem sub_sets_sub_count: + !n k. sub_sets (count n) k = sub_count n k +Proof + simp[sub_sets_def, sub_count_def] +QED + +(* Theorem: FINITE t /\ s SUBSET t ==> + sub_sets t (CARD s) = equiv_class $=b= (POW t) s *) +(* Proof: + x IN sub_sets t (CARD s) + <=> x SUBSET t /\ CARD s = CARD x by sub_sets_element + <=> x SUBSET t /\ s =b= x by bij_eq_card_eq, SUBSET_FINITE + <=> x IN POW t /\ s =b= x by IN_POW + <=> x IN equiv_class $=b= (POW t) s by equiv_class_element +*) +Theorem sub_sets_equiv_class: + !s t. FINITE t /\ s SUBSET t ==> + sub_sets t (CARD s) = equiv_class $=b= (POW t) s +Proof + rw[sub_sets_def, IN_POW, EXTENSION] >> + metis_tac[bij_eq_card_eq, SUBSET_FINITE] +QED + +(* Theorem: s SUBSET (count n) ==> + sub_count n (CARD s) = equiv_class $=b= (POW (count n)) s *) +(* Proof: + Note FINITE (count n) by FINITE_COUNT + sub_count n (CARD s) + = sub_sets (count n) (CARD s) by sub_sets_sub_count + = equiv_class $=b= (POW t) s by sub_sets_equiv_class +*) +Theorem sub_count_equiv_class: + !n s. s SUBSET (count n) ==> + sub_count n (CARD s) = equiv_class $=b= (POW (count n)) s +Proof + simp[sub_sets_equiv_class, GSYM sub_sets_sub_count] +QED + +(* Theorem: partition $=b= (POW (count n)) = IMAGE (sub_count n) (upto n) *) +(* Proof: + Let R = $=b=, t = count n. + Note CARD t = n by CARD_COUNT + By EXTENSION and LESS_EQ_IFF_LESS_SUC, this is to show: + (1) x IN partition R (POW t) ==> ?k. x = sub_count n k /\ k <= n + Note ?s. s IN POW t /\ + x = equiv_class R (POW t) s by partition_element + Note FINITE t by SUBSET_FINITE + and s SUBSET t by IN_POW + so CARD s <= CARD t = n by CARD_SUBSET + Take k = CARD s. + Then k <= n /\ x = sub_count n k by sub_count_equiv_class + (2) k <= n ==> sub_count n k IN partition R (POW t) + Let s = count k + Then CARD s = k by CARD_COUNT + and s SUBSET t by COUNT_SUBSET, k <= n + so s IN POW t by IN_POW + Now sub_count n k + = equiv_class R (POW t) s by sub_count_equiv_class + ==> sub_count n k IN partition R (POW t) + by partition_element +*) +Theorem count_power_partition: + !n. partition $=b= (POW (count n)) = IMAGE (sub_count n) (upto n) +Proof + rpt strip_tac >> + qabbrev_tac `R = \(s:num -> bool) (t:num -> bool). s =b= t` >> + qabbrev_tac `t = count n` >> + rw[Once EXTENSION, partition_element, GSYM LESS_EQ_IFF_LESS_SUC, EQ_IMP_THM] >| [ + `FINITE t` by rw[Abbr`t`] >> + `x' SUBSET t` by fs[IN_POW] >> + `CARD x' <= n` by metis_tac[CARD_SUBSET, CARD_COUNT] >> + qexists_tac `CARD x'` >> + simp[sub_count_equiv_class, Abbr`R`, Abbr`t`], + qexists_tac `count x'` >> + `(count x') SUBSET t /\ (count x') IN POW t` by metis_tac[COUNT_SUBSET, IN_POW] >> + simp[] >> + qabbrev_tac `s = count x'` >> + `sub_count n (CARD s) = equiv_class R (POW t) s` suffices_by simp[Abbr`s`] >> + simp[sub_count_equiv_class, Abbr`R`, Abbr`t`] + ] +QED + +(* Theorem: INJ (sub_count n) (upto n) univ(:(num -> bool) -> bool) *) +(* Proof: + By INJ_DEF, this is to show: + !x y. x < SUC n /\ y < SUC n /\ sub_count n x = sub_count n y ==> x = y + Let s = count x. + Note x < SUC n <=> x <= n by arithmetic + ==> s SUBSET (count n) by COUNT_SUBSET, x <= n + and CARD s = x by CARD_COUNT + so s IN sub_count n x by sub_count_element + Thus s IN sub_count n y by given, sub_count n x = sub_count n y + ==> CARD s = x = y +*) +Theorem sub_count_count_inj: + !n m. INJ (sub_count n) (upto n) univ(:(num -> bool) -> bool) +Proof + rw[sub_count_def, EXTENSION, INJ_DEF] >> + `(count x) SUBSET (count n)` by rw[COUNT_SUBSET] >> + metis_tac[CARD_COUNT] +QED + +(* Idea: the sum of sizes of equivalence classes gives size of power set. *) + +(* Theorem: SIGMA ($choose n) (upto n) = 2 ** n *) +(* Proof: + Let R = $=b=, t = count n. + Then R equiv_on (POW t) by bij_eq_equiv_on + and FINITE t by FINITE_COUNT + so FINITE (POW t) by FINITE_POW + Thus CARD (POW t) = SIGMA CARD (partition R (POW t)) + by partition_CARD + LHS = CARD (POW t) + = 2 ** CARD t by CARD_POW + = 2 ** n by CARD_COUNT + Note INJ (sub_count n) (upto n) univ by sub_count_count_inj + RHS = SIGMA CARD (partition R (POW t)) + = SIGMA CARD (IMAGE (sub_count n) (upto n)) by count_power_partition + = SIGMA (CARD o sub_count n) (upto n) by SUM_IMAGE_INJ_o + = SIGMA ($choose n) (upto n) by FUN_EQ_THM, choose_def +*) +Theorem choose_sum_over_count: + !n. SIGMA ($choose n) (upto n) = 2 ** n +Proof + rpt strip_tac >> + qabbrev_tac `R = \(s:num -> bool) (t:num -> bool). s =b= t` >> + qabbrev_tac `t = count n` >> + `R equiv_on (POW t)` by rw[bij_eq_equiv_on, Abbr`R`] >> + `FINITE (POW t)` by rw[FINITE_POW, Abbr`t`] >> + imp_res_tac partition_CARD >> + `FINITE (upto n)` by rw[] >> + `SIGMA CARD (partition R (POW t)) = SIGMA CARD (IMAGE (sub_count n) (upto n))` by fs[count_power_partition, Abbr`R`, Abbr`t`] >> + `_ = SIGMA (CARD o (sub_count n)) (upto n)` by rw[SUM_IMAGE_INJ_o, sub_count_count_inj] >> + `_ = SIGMA ($choose n) (upto n)` by rw[choose_def, FUN_EQ_THM, SIGMA_CONG] >> + fs[CARD_POW, Abbr`t`] +QED + +(* This corresponds to: +> binomial_sum; +val it = |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n: thm +*) + +(* Theorem: SUM (MAP ($choose n) [0 .. n]) = 2 ** n *) +(* Proof: + SUM (MAP ($choose n) [0 .. n]) + = SIGMA ($choose n) (upto n) by SUM_IMAGE_upto + = 2 ** n by choose_sum_over_count +*) +Theorem choose_sum_over_all: + !n. SUM (MAP ($choose n) [0 .. n]) = 2 ** n +Proof + simp[GSYM SUM_IMAGE_upto, choose_sum_over_count] +QED + +(* A better representation of: +> binomial_sum; +val it = |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n: thm +*) + +(* ------------------------------------------------------------------------- *) +(* Counting number of permutations. *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of permutation tuples of (count n). *) +Definition perm_count_def[nocompute]: + perm_count n = { ls | ALL_DISTINCT ls /\ set ls = count n} +End +(* use [nocompute] as this is not effective for evalutaion. *) + +(* Define the number of choices of k-tuples of (count n). *) +Definition perm_def[nocompute]: + perm n = CARD (perm_count n) +End +(* use [nocompute] as this is not effective for evalutaion. *) + +(* Theorem: ls IN perm_count n <=> ALL_DISTINCT ls /\ set ls = count n *) +(* Proof: by perm_count_def. *) +Theorem perm_count_element: + !ls n. ls IN perm_count n <=> ALL_DISTINCT ls /\ set ls = count n +Proof + simp[perm_count_def] +QED + +(* Theorem: ls IN perm_count n ==> ~MEM n ls *) +(* Proof: + ls IN perm_count n + <=> ALL_DISTINCT ls /\ set ls = count n by perm_count_element + ==> ~MEM n ls by COUNT_NOT_SELF +*) +Theorem perm_count_element_no_self: + !ls n. ls IN perm_count n ==> ~MEM n ls +Proof + simp[perm_count_element] +QED + +(* Theorem: ls IN perm_count n ==> LENGTH ls = n *) +(* Proof: + ls IN perm_count n + <=> ALL_DISTINCT ls /\ set ls = count n by perm_count_element + LENGTH ls = CARD (set ls) by ALL_DISTINCT_CARD_LIST_TO_SET + = CARD (count n) by set ls = count n + = n by CARD_COUNT +*) +Theorem perm_count_element_length: + !ls n. ls IN perm_count n ==> LENGTH ls = n +Proof + metis_tac[perm_count_element, ALL_DISTINCT_CARD_LIST_TO_SET, CARD_COUNT] +QED + +(* Theorem: perm_count n SUBSET necklace n n *) +(* Proof: + ls IN perm_count n + <=> ALL_DISTINCT ls /\ set ls = count n by perm_count_element + Thus set ls SUBSET (count n) by SUBSET_REFL + and LENGTH ls = n by perm_count_element_length + Therefore ls IN necklace n n by necklace_element +*) +Theorem perm_count_subset: + !n. perm_count n SUBSET necklace n n +Proof + rw[perm_count_def, necklace_def, perm_count_element_length, SUBSET_DEF] +QED + +(* Theorem: FINITE (perm_count n) *) +(* Proof: + Note perm_count n SUBSET necklace n n by perm_count_subset + and FINITE (necklace n n) by necklace_finite + Thus FINITE (perm_count n) by SUBSET_FINITE +*) +Theorem perm_count_finite: + !n. FINITE (perm_count n) +Proof + metis_tac[perm_count_subset, necklace_finite, SUBSET_FINITE] +QED + +(* Theorem: perm_count 0 = {[]} *) +(* Proof: + ls IN perm_count 0 + <=> ALL_DISTINCT ls /\ set ls = count 0 by perm_count_element + <=> ALL_DISTINCT ls /\ set ls = {} by COUNT_0 + <=> ALL_DISTINCT ls /\ ls = [] by LIST_TO_SET_EQ_EMPTY + <=> ls = [] by ALL_DISTINCT + Thus perm_count 0 = {[]} by EXTENSION +*) +Theorem perm_count_0: + perm_count 0 = {[]} +Proof + rw[perm_count_def, EXTENSION, EQ_IMP_THM] >> + metis_tac[MEM, list_CASES] +QED + +(* Theorem: perm_count 1 = {[0]} *) +(* Proof: + ls IN perm_count 1 + <=> ALL_DISTINCT ls /\ set ls = count 1 by perm_count_element + <=> ALL_DISTINCT ls /\ set ls = {0} by COUNT_1 + <=> ls = [0] by DISTINCT_LIST_TO_SET_EQ_SING + Thus perm_count 1 = {[0]} by EXTENSION +*) +Theorem perm_count_1: + perm_count 1 = {[0]} +Proof + simp[perm_count_def, COUNT_1, DISTINCT_LIST_TO_SET_EQ_SING] +QED + +(* Define the interleave operation on a list. *) +Definition interleave_def: + interleave x ls = IMAGE (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) +End +(* make this an infix operator *) +val _ = set_fixity "interleave" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) +(* interleave_def; +val it = |- !x ls. x interleave ls = + IMAGE (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)): thm *) + +(* +> EVAL ``2 interleave [0; 1]``; +val it = |- 2 interleave [0; 1] = {[0; 1; 2]; [0; 2; 1]; [2; 0; 1]}: thm +*) + +(* Theorem: x interleave ls = {TAKE k ls ++ x::DROP k ls | k | k <= LENGTH ls} *) +(* Proof: by interleave_def, EXTENSION. *) +Theorem interleave_alt: + !ls x. x interleave ls = {TAKE k ls ++ x::DROP k ls | k | k <= LENGTH ls} +Proof + simp[interleave_def, EXTENSION] >> + metis_tac[LESS_EQ_IFF_LESS_SUC] +QED + +(* Theorem: y IN x interleave ls <=> + ?k. k <= LENGTH ls /\ y = TAKE k ls ++ x::DROP k ls *) +(* Proof: by interleave_alt, IN_IMAGE. *) +Theorem interleave_element: + !ls x y. y IN x interleave ls <=> + ?k. k <= LENGTH ls /\ y = TAKE k ls ++ x::DROP k ls +Proof + simp[interleave_alt] >> + metis_tac[] +QED + +(* Theorem: x interleave [] = {[x]} *) +(* Proof: + x interleave [] + = IMAGE (\k. TAKE k [] ++ x::DROP k []) (upto (LENGTH [])) + by interleave_def + = IMAGE (\k. [] ++ x::[]) (upto 0) by TAKE_nil, DROP_nil, LENGTH + = IMAGE (\k. [x]) (count 1) by APPEND, notation of upto + = IMAGE (\k. [x]) {0} by COUNT_1 + = [x] by IMAGE_DEF +*) +Theorem interleave_nil: + !x. x interleave [] = {[x]} +Proof + rw[interleave_def, EXTENSION] >> + metis_tac[DECIDE``0 < 1``] +QED + +(* Theorem: y IN (x interleave ls) ==> LENGTH y = 1 + LENGTH ls *) +(* Proof: + LENGTH y + = LENGTH (TAKE k ls ++ x::DROP k ls) by interleave_element, for some k + = LENGTH (TAKE k ls) + LENGTH (x::DROP k ls) by LENGTH_APPEND + = k + LENGTH (x :: DROP k ls) by LENGTH_TAKE, k <= LENGTH ls + = k + (1 + LENGTH (DROP k ls)) by LENGTH + = k + (1 + (LENGTH ls - k)) by LENGTH_DROP + = 1 + LENGTH ls by arithmetic, k <= LENGTH ls +*) +Theorem interleave_length: + !ls x y. y IN (x interleave ls) ==> LENGTH y = 1 + LENGTH ls +Proof + rw_tac bool_ss[interleave_element] >> + simp[] +QED + +(* Theorem: ALL_DISTINCT (x::ls) /\ y IN (x interleave ls) ==> ALL_DISTINCT y *) +(* Proof: + By interleave_def, IN_IMAGE, this is to show; + ALL_DISTINCT (TAKE k ls ++ x::DROP k ls) + To apply ALL_DISTINCT_APPEND, need to show: + (1) ~MEM x ls /\ MEM e (TAKE k ls) /\ MEM e (x::DROP k ls) ==> F + MEM e (x::DROP k ls) + <=> e = x \/ MEM e (DROP k ls) by MEM + MEM e (TAKE k ls) + ==> MEM e ls by MEM_TAKE + If e = x, + this contradicts ~MEM x ls. + If MEM e (DROP k ls), + with MEM e (TAKE k ls) + and ALL_DISTINCT ls gives F by ALL_DISTINCT_TAKE_DROP + (2) ALL_DISTINCT (TAKE k ls), true by ALL_DISTINCT_TAKE + (3) ~MEM x ls ==> ALL_DISTINCT (x::DROP k ls) + ALL_DISTINCT (x::DROP k ls) + <=> ~MEM x (DROP k ls) /\ + ALL_DISTINCT (DROP k ls) by ALL_DISTINCT + <=> ~MEM x (DROP k ls) /\ T by ALL_DISTINCT_DROP + ==> ~MEM x ls /\ T by MEM_DROP_IMP + ==> T /\ T = T +*) +Theorem interleave_distinct: + !ls x y. ALL_DISTINCT (x::ls) /\ y IN (x interleave ls) ==> ALL_DISTINCT y +Proof + rw_tac bool_ss[interleave_def, IN_IMAGE] >> + irule (ALL_DISTINCT_APPEND |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> + rpt strip_tac >| [ + fs[] >- + metis_tac[MEM_TAKE] >> + metis_tac[ALL_DISTINCT_TAKE_DROP], + fs[ALL_DISTINCT_TAKE], + fs[ALL_DISTINCT_DROP] >> + metis_tac[MEM_DROP_IMP] + ] +QED + +(* Theorem: ALL_DISTINCT ls /\ ~(MEM x ls) /\ + y IN (x interleave ls) ==> ALL_DISTINCT y *) +(* Proof: by interleave_distinct, ALL_DISTINCT. *) +Theorem interleave_distinct_alt: + !ls x y. ALL_DISTINCT ls /\ ~(MEM x ls) /\ + y IN (x interleave ls) ==> ALL_DISTINCT y +Proof + metis_tac[interleave_distinct, ALL_DISTINCT] +QED + +(* Theorem: y IN x interleave ls ==> set y = set (x::ls) *) +(* Proof: + Note y = TAKE k ls ++ x::DROP k ls by interleave_element, for some k + Let u = TAKE k ls, v = DROP k ls. + set y + = set (u ++ x::v) by above + = set u UNION set (x::v) by LIST_TO_SET_APPEND + = set u UNION (x INSERT set v) by LIST_TO_SET + = (x INSERT set v) UNION set u by UNION_COMM + = x INSERT (set v UNION set u) by INSERT_UNION_EQ + = x INSERT (set u UNION set v) by UNION_COMM + = x INSERT (set (u ++ v)) by LIST_TO_SET_APPEND + = x INSERT set ls by TAKE_DROP + = set (x::ls) by LIST_TO_SET +*) +Theorem interleave_set: + !ls x y. y IN x interleave ls ==> set y = set (x::ls) +Proof + rw_tac bool_ss[interleave_element] >> + qabbrev_tac `u = TAKE k ls` >> + qabbrev_tac `v = DROP k ls` >> + `set (u ++ x::v) = set u UNION set (x::v)` by rw[] >> + `_ = set u UNION (x INSERT set v)` by rw[] >> + `_ = (x INSERT set v) UNION set u` by rw[UNION_COMM] >> + `_ = x INSERT (set v UNION set u)` by rw[INSERT_UNION_EQ] >> + `_ = x INSERT (set u UNION set v)` by rw[UNION_COMM] >> + `_ = x INSERT (set (u ++ v))` by rw[] >> + `_ = x INSERT set ls` by metis_tac[TAKE_DROP] >> + simp[] +QED + +(* Theorem: y IN x interleave ls ==> set y = x INSERT set ls *) +(* Proof: + Note set y = set (x::ls) by interleave_set + = x INSERT set ls by LIST_TO_SET +*) +Theorem interleave_set_alt: + !ls x y. y IN x interleave ls ==> set y = x INSERT set ls +Proof + metis_tac[interleave_set, LIST_TO_SET] +QED + +(* Theorem: (x::ls) IN x interleave ls *) +(* Proof: + (x::ls) IN x interleave ls + <=> ?k. x::ls = TAKE k ls ++ [x] ++ DROP k ls /\ k < SUC (LENGTH ls) + by interleave_def + Take k = 0. + Then 0 < SUC (LENGTH ls) by SUC_POS + and TAKE 0 ls ++ [x] ++ DROP 0 ls + = [] ++ [x] ++ ls by TAKE_0, DROP_0 + = x::ls by APPEND +*) +Theorem interleave_has_cons: + !ls x. (x::ls) IN x interleave ls +Proof + rw[interleave_def] >> + qexists_tac `0` >> + simp[] +QED + +(* Theorem: x interleave ls <> EMPTY *) +(* Proof: + Note (x::ls) IN x interleave ls by interleave_has_cons + Thus x interleave ls <> {} by MEMBER_NOT_EMPTY +*) +Theorem interleave_not_empty: + !ls x. x interleave ls <> EMPTY +Proof + metis_tac[interleave_has_cons, MEMBER_NOT_EMPTY] +QED + +(* +MEM_APPEND_lemma +|- !a b c d x. a ++ [x] ++ b = c ++ [x] ++ d /\ ~MEM x b /\ ~MEM x a ==> + a = c /\ b = d +*) + +(* Theorem: ~MEM n x /\ ~MEM n y ==> (n interleave x = n interleave y <=> x = y) *) +(* Proof: + Let f = (\k. TAKE k x ++ n::DROP k x), + g = (\k. TAKE k y ++ n::DROP k y). + By interleave_def, this is to show: + IMAGE f (upto (LENGTH x)) = IMAGE g (upto (LENGTH y)) <=> x = y + Only-part part is trivial. + For the if part, + Note 0 IN (upto (LENGTH x) by SUC_POS, IN_COUNT + so f 0 IN IMAGE f (upto (LENGTH x)) + thus ?k. k < SUC (LENGTH y) /\ f 0 = g k by IN_IMAGE, IN_COUNT + so n::x = TAKE k y ++ [n] ++ DROP k y by notation of f 0 + but n::x = TAKE 0 x ++ [n] ++ DROP 0 x by TAKE_0, DROP_0 + and ~MEM n (TAKE 0 x) /\ ~MEM n (DROP 0 x) by TAKE_0, DROP_0 + so TAKE 0 x = TAKE k y /\ + DROP 0 x = DROP k y by MEM_APPEND_lemma + or x = y by TAKE_DROP +*) +Theorem interleave_eq: + !n x y. ~MEM n x /\ ~MEM n y ==> (n interleave x = n interleave y <=> x = y) +Proof + rw[interleave_def, EQ_IMP_THM] >> + qabbrev_tac `f = \k. TAKE k x ++ n::DROP k x` >> + qabbrev_tac `g = \k. TAKE k y ++ n::DROP k y` >> + `f 0 IN IMAGE f (upto (LENGTH x))` by fs[] >> + `?k. k < SUC (LENGTH y) /\ f 0 = g k` by metis_tac[IN_IMAGE, IN_COUNT] >> + fs[Abbr`f`, Abbr`g`] >> + `n::x = TAKE 0 x ++ [n] ++ DROP 0 x` by rw[] >> + `~MEM n (TAKE 0 x) /\ ~MEM n (DROP 0 x)` by rw[] >> + metis_tac[MEM_APPEND_lemma, TAKE_DROP] +QED + +(* Theorem: ~MEM x l1 /\ l1 <> l2 ==> DISJOINT (x interleave l1) (x interleave l2) *) +(* Proof: + Use DISJOINT_DEF, by contradiction, suppose y is in both. + Then ?k h. k <= LENGTH l1 and h <= LENGTH l2 + with y = TAKE k l1 ++ [x] ++ DROP k l1 by interleave_element + and y = TAKE h l2 ++ [x] ++ DROP h l2 by interleave_element + Now ~MEM x (TAKE k l1) by MEM_TAKE + and ~MEM x (DROP k l1) by MEM_DROP_IMP + Thus TAKE k l1 = TAKE h l2 /\ + DROP k l1 = DROP h l2 by MEM_APPEND_lemma + or l1 = l2 by TAKE_DROP + but this contradicts l1 <> l2. +*) +Theorem interleave_disjoint: + !l1 l2 x. ~MEM x l1 /\ l1 <> l2 ==> DISJOINT (x interleave l1) (x interleave l2) +Proof + rw[interleave_def, DISJOINT_DEF, EXTENSION] >> + spose_not_then strip_assume_tac >> + `~MEM x (TAKE k l1) /\ ~MEM x (DROP k l1)` by metis_tac[MEM_TAKE, MEM_DROP_IMP] >> + metis_tac[MEM_APPEND_lemma, TAKE_DROP] +QED + +(* Theorem: FINITE (x interleave ls) *) +(* Proof: + Let f = (\k. TAKE k ls ++ x::DROP k ls), + n = LENGTH ls. + FINITE (x interleave ls) + <=> FINITE (IMAGE f (upto n)) by interleave_def + <=> T by IMAGE_FINITE, FINITE_COUNT +*) +Theorem interleave_finite: + !ls x. FINITE (x interleave ls) +Proof + simp[interleave_def, IMAGE_FINITE] +QED + +(* Theorem: ~MEM x ls ==> + INJ (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) univ(:'a list) *) +(* Proof: + Let n = LENGTH ls, + s = upto n, + f = (\k. TAKE k ls ++ x::DROP k ls). + By INJ_DEF, this is to show: + (1) k IN s ==> f k IN univ(:'a list), true by types. + (2) k IN s /\ h IN s /\ f k = f h ==> k = h. + Note k <= LENGTH ls by IN_COUNT, k IN s + and h <= LENGTH ls by IN_COUNT, h IN s + and ls = TAKE k ls ++ DROP k ls by TAKE_DROP + so ~MEM x (TAKE k ls) /\ + ~MEM x (DROP k ls) by MEM_APPEND, ~MEM x ls + Thus TAKE k ls = TAKE h ls by MEM_APPEND_lemma + ==> k = h by LENGTH_TAKE + +MEM_APPEND_lemma +|- !a b c d x. a ++ [x] ++ b = c ++ [x] ++ d /\ + ~MEM x b /\ ~MEM x a ==> a = c /\ b = d +*) +Theorem interleave_count_inj: + !ls x. ~MEM x ls ==> + INJ (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) univ(:'a list) +Proof + rw[INJ_DEF] >> + `k <= LENGTH ls /\ k' <= LENGTH ls` by fs[] >> + `~MEM x (TAKE k ls) /\ ~MEM x (DROP k ls)` by metis_tac[TAKE_DROP, MEM_APPEND] >> + metis_tac[MEM_APPEND_lemma, LENGTH_TAKE] +QED + +(* Theorem: ~MEM x ls ==> CARD (x interleave ls) = 1 + LENGTH ls *) +(* Proof: + Let f = (\k. TAKE k ls ++ x::DROP k ls), + n = LENGTH ls. + Note FINITE (upto n) by FINITE_COUNT + and INJ f (upto n) univ(:'a list) + by interleave_count_inj, ~MEM x ls + CARD (x interleave ls) + = CARD (IMAGE f (upto n)) by interleave_def + = CARD (upto n) by INJ_CARD_IMAGE + = SUC n = 1 + n by CARD_COUNT, ADD1 +*) +Theorem interleave_card: + !ls x. ~MEM x ls ==> CARD (x interleave ls) = 1 + LENGTH ls +Proof + rw[interleave_def] >> + imp_res_tac interleave_count_inj >> + qabbrev_tac `n = LENGTH ls` >> + qabbrev_tac `s = upto n` >> + qabbrev_tac `f = (\k. TAKE k ls ++ x::DROP k ls)` >> + `FINITE s` by rw[Abbr`s`] >> + metis_tac[INJ_CARD_IMAGE, CARD_COUNT, ADD1] +QED + +(* Note: + interleave_distinct, interleave_length, and interleave_set + are effects after interleave. Now we need a kind of inverse: + deduce the effects before interleave. +*) + +(* Idea: a member h in a distinct list is the interleave of h with a smaller one. *) + +(* Theorem: ALL_DISTINCT ls /\ h IN set ls ==> + ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ set t = (set ls) DELETE h *) +(* Proof: + By induction on ls. + Base: ALL_DISTINCT [] /\ MEM h [] ==> ?t. ... + Since MEM h [] = F, this is true by MEM + Step: (ALL_DISTINCT ls /\ MEM h ls ==> + ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ set t = set ls DELETE h) ==> + !h'. ALL_DISTINCT (h'::ls) /\ MEM h (h'::ls) ==> + ?t. ALL_DISTINCT t /\ h'::ls IN h interleave t /\ set t = set (h'::ls) DELETE h + If h' = h, + Note ~MEM h ls /\ ALL_DISTINCT ls by ALL_DISTINCT + Take this ls, + Then set (h::ls) DELETE h + = (h INSERT set ls) DELETE h by LIST_TO_SET + = set ls by INSERT_DELETE_NON_ELEMENT + and h::ls IN h interleave ls by interleave_element, take k = 0. + If h' <> h, + Note ~MEM h' ls /\ ALL_DISTINCT ls by ALL_DISTINCT + and MEM h ls by MEM, h <> h' + Thus ?t. ALL_DISTINCT t /\ + ls IN h interleave t /\ + set t = set ls DELETE h by induction hypothesis + Note ~MEM h' t by set t = set ls DELETE h, ~MEM h' ls + Take this (h'::t), + Then ALL_DISTINCT (h'::t) by ALL_DISTINCT, ~MEM h' t + and set (h'::ls) DELETE h + = (h' INSERT set ls) DELETE h by LIST_TO_SET + = h' INSERT (set ls DELETE h) by DELETE_INSERT, h' <> h + = h' INSERT set t by above + = set (h'::t) + and h'::ls IN h interleave t by interleave_element, + take k = SUC k from ls IN h interleave t +*) +Theorem interleave_revert: + !ls h. ALL_DISTINCT ls /\ h IN set ls ==> + ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ set t = (set ls) DELETE h +Proof + rpt strip_tac >> + Induct_on `ls` >- + simp[] >> + rpt strip_tac >> + Cases_on `h' = h` >| [ + fs[] >> + qexists_tac `ls` >> + simp[INSERT_DELETE_NON_ELEMENT] >> + simp[interleave_element] >> + qexists_tac `0` >> + simp[], + fs[] >> + `~MEM h' t` by fs[] >> + qexists_tac `h'::t` >> + simp[DELETE_INSERT] >> + fs[interleave_element] >> + qexists_tac `SUC k` >> + simp[] + ] +QED + +(* A useful corollary for set s = count n. *) + +(* Theorem: ALL_DISTINCT ls /\ set ls = upto n ==> + ?t. ALL_DISTINCT t /\ ls IN n interleave t /\ set t = count n *) +(* Proof: + Note MEM n ls by set ls = upto n + so ?t. ALL_DISTINCT t /\ + ls IN n interleave t /\ + set t = set ls DELETE n by interleave_revert + = (upto n) DELETE n by given + = count n by upto_delete +*) +Theorem interleave_revert_count: + !ls n. ALL_DISTINCT ls /\ set ls = upto n ==> + ?t. ALL_DISTINCT t /\ ls IN n interleave t /\ set t = count n +Proof + rpt strip_tac >> + `MEM n ls` by fs[] >> + drule_then strip_assume_tac interleave_revert >> + first_x_assum (qspec_then `n` strip_assume_tac) >> + metis_tac[upto_delete] +QED + +(* Theorem: perm_count (SUC n) = + BIGUNION (IMAGE ($interleave n) (perm_count n)) *) +(* Proof: + By induction on n. + Base: perm_count (SUC 0) = + BIGUNION (IMAGE ($interleave 0) (perm_count 0)) + LHS = perm_count (SUC 0) + = perm_count 1 by ONE + = {[0]} by perm_count_1 + RHS = BIGUNION (IMAGE ($interleave 0) (perm_count 0)) + = BIGUNION (IMAGE ($interleave 0) {[]} by perm_count_0 + = BIGUNION {0 interleave []} by IMAGE_SING + = BIGUNION {{[0]}} by interleave_nil + = {[0]} = LHS by BIGUNION_SING + Step: perm_count (SUC n) = BIGUNION (IMAGE ($interleave n) (perm_count n)) ==> + perm_count (SUC (SUC n)) = + BIGUNION (IMAGE ($interleave (SUC n)) (perm_count (SUC n))) + Let f = $interleave (SUC n), + s = perm_count n, t = perm_count (SUC n). + y IN BIGUNION (IMAGE f t) + <=> ?x. x IN t /\ y IN f x by IN_BIGUNION_IMAGE + <=> ?x. (?z. z IN s /\ x IN n interleave z) /\ y IN (SUC n) interleave x + by IN_BIGUNION_IMAGE, induction hypothesis + <=> ?x z. ALL_DISTINCT z /\ set z = count n /\ + x IN n interleave z /\ + y IN (SUC n) interleave x by perm_count_element + If part: y IN perm_count (SUC (SUC n)) ==> ?x and z. + Note ALL_DISTINCT y /\ + set y = count (SUC (SUC n)) by perm_count_element + Then ?x. ALL_DISTINCT x /\ y IN (SUC n) interleave x /\ set x = upto n + by interleave_revert_count + so ?z. ALL_DISTINCT z /\ x IN n interleave z /\ set z = count n + by interleave_revert_count + Take these x and z. + Only-if part: ?x and z ==> y IN perm_count (SUC (SUC n)) + Note ~MEM n z by set z = count n, COUNT_NOT_SELF + ==> ALL_DISTINCT x /\ by interleave_distinct_alt + set x = upto n by interleave_set_alt, COUNT_SUC + Note ~MEM (SUC n) x by set x = upto n, COUNT_NOT_SELF + ==> ALL_DISTINCT y /\ by interleave_distinct_alt + set y = count (SUC (SUC n)) by interleave_set_alt, COUNT_SUC + ==> y IN perm_count (SUC (SUC n)) by perm_count_element +*) +Theorem perm_count_suc: + !n. perm_count (SUC n) = + BIGUNION (IMAGE ($interleave n) (perm_count n)) +Proof + Induct >| [ + rw[perm_count_0, perm_count_1] >> + simp[interleave_nil], + rw[IN_BIGUNION_IMAGE, EXTENSION, EQ_IMP_THM] >| [ + imp_res_tac perm_count_element >> + `?y. ALL_DISTINCT y /\ x IN (SUC n) interleave y /\ set y = upto n` by rw[interleave_revert_count] >> + `?t. ALL_DISTINCT t /\ y IN n interleave t /\ set t = count n` by rw[interleave_revert_count] >> + (qexists_tac `y` >> simp[]) >> + (qexists_tac `t` >> simp[]) >> + simp[perm_count_element], + fs[perm_count_element] >> + `~MEM n x''` by fs[] >> + `ALL_DISTINCT x' /\ set x' = upto n` by metis_tac[interleave_distinct_alt, interleave_set_alt, COUNT_SUC] >> + `~MEM (SUC n) x'` by fs[] >> + metis_tac[interleave_distinct_alt, interleave_set_alt, COUNT_SUC] + ] + ] +QED + +(* Theorem: perm_count (n + 1) = + BIGUNION (IMAGE ($interleave n) (perm_count n)) *) +(* Proof: by perm_count_suc, GSYM ADD1. *) +Theorem perm_count_suc_alt: + !n. perm_count (n + 1) = + BIGUNION (IMAGE ($interleave n) (perm_count n)) +Proof + simp[perm_count_suc, GSYM ADD1] +QED + +(* Theorem: perm_count n = + if n = 0 then {[]} + else BIGUNION (IMAGE ($interleave (n - 1)) (perm_count (n - 1))) *) +(* Proof: by perm_count_0, perm_count_suc. *) +Theorem perm_count_eqn[compute]: + !n. perm_count n = + if n = 0 then {[]} + else BIGUNION (IMAGE ($interleave (n - 1)) (perm_count (n - 1))) +Proof + rw[perm_count_0] >> + metis_tac[perm_count_suc, num_CASES, SUC_SUB1] +QED + +(* +> EVAL ``perm_count 3``; +val it = |- perm_count 3 = +{[0; 1; 2]; [0; 2; 1]; [2; 0; 1]; [1; 0; 2]; [1; 2; 0]; [2; 1; 0]}: thm +*) + +(* Historical note. +This use of interleave to list all permutations is called +the Steinhaus-Johnson-Trotter algorithm, due to re-discovery by various people. +Outside mathematics, this method was known already to 17th-century English change ringers. +Equivalently, this algorithm finds a Hamiltonian cycle in the permutohedron. + +Steinhaus-Johnson-Trotter algorithm +https://en.wikipedia.org/wiki/Steinhaus-Johnson-Trotter_algorithm + +1677 A book by Fabian Stedman lists the solutions for up to six bells. +1958 A book by Steinhaus describes a related puzzle of generating all permutations by a system of particles. +Selmer M. Johnson and Hale F. Trotter discovered the algorithm independently of each other in the early 1960s. +1962 Hale F. Trotter, "Algorithm 115: Perm", August 1962. +1963 Selmer M. Johnson, "Generation of permutations by adjacent transposition". + +*) + +(* Theorem: perm 0 = 1 *) +(* Proof: + perm 0 + = CARD (perm_count 0) by perm_def + = CARD {[]} by perm_count_0 + = 1 by CARD_SING +*) +Theorem perm_0: + perm 0 = 1 +Proof + simp[perm_def, perm_count_0] +QED + +(* Theorem: perm 1 = 1 *) +(* Proof: + perm 1 + = CARD (perm_count 1) by perm_def + = CARD {[0]} by perm_count_1 + = 1 by CARD_SING +*) +Theorem perm_1: + perm 1 = 1 +Proof + simp[perm_def, perm_count_1] +QED + +(* Theorem: e IN IMAGE ($interleave n) (perm_count n) ==> FINITE e *) +(* Proof: + e IN IMAGE ($interleave n) (perm_count n) + <=> ?ls. ls IN perm_count n /\ + e = n interleave ls by IN_IMAGE + Thus FINITE e by interleave_finite +*) +Theorem perm_count_interleave_finite: + !n e. e IN IMAGE ($interleave n) (perm_count n) ==> FINITE e +Proof + rw[] >> + simp[interleave_finite] +QED + +(* Theorem: e IN IMAGE ($interleave n) (perm_count n) ==> CARD e = n + 1 *) +(* Proof: + e IN IMAGE ($interleave n) (perm_count n) + <=> ?ls. ls IN perm_count n /\ + e = n interleave ls by IN_IMAGE + Note ~MEM n ls by perm_count_element_no_self + and LENGTH ls = n by perm_count_element_length + Thus CARD e = n + 1 by interleave_card, ~MEM n ls +*) +Theorem perm_count_interleave_card: + !n e. e IN IMAGE ($interleave n) (perm_count n) ==> CARD e = n + 1 +Proof + rw[] >> + `~MEM n x` by rw[perm_count_element_no_self] >> + `LENGTH x = n` by rw[perm_count_element_length] >> + simp[interleave_card] +QED + +(* Theorem: PAIR_DISJOINT (IMAGE ($interleave n) (perm_count n)) *) +(* Proof: + By IN_IMAGE, this is to show: + x IN perm_count n /\ y IN perm_count n /\ + n interleave x <> n interleave y ==> + DISJOINT (n interleave x) (n interleave y) + By contradiction, suppose there is a list ls in both. + Then x = y by interleave_disjoint + This contradicts n interleave x <> n interleave y. +*) +Theorem perm_count_interleave_disjoint: + !n e. PAIR_DISJOINT (IMAGE ($interleave n) (perm_count n)) +Proof + rw[perm_count_def] >> + `~MEM n x` by fs[] >> + metis_tac[interleave_disjoint] +QED + +(* Theorem: INJ ($interleave n) (perm_count n) univ(:(num list -> bool)) *) +(* Proof: + By INJ_DEF, this is to show: + (1) x IN perm_count n ==> n interleave x IN univ + This is true by type. + (2) x IN perm_count n /\ y IN perm_count n /\ + n interleave x = n interleave y ==> x = y + Note ~MEM n x by perm_count_element_no_self + and ~MEM n y by perm_count_element_no_self + Thus x = y by interleave_eq +*) +Theorem perm_count_interleave_inj: + !n. INJ ($interleave n) (perm_count n) univ(:(num list -> bool)) +Proof + rw[INJ_DEF, perm_count_def, interleave_eq] +QED + +(* Theorem: perm (SUC n) = (SUC n) * perm n *) +(* Proof: + Let f = $interleave n, + s = IMAGE f (perm_count n). + Note FINITE (perm_count n) by perm_count_finite + so FINITE s by IMAGE_FINITE + and !e. e IN s ==> + FINITE e /\ by perm_count_interleave_finite + CARD e = n + 1 by perm_count_interleave_card + and PAIR_DISJOINT s by perm_count_interleave_disjoint + and INJ f (perm_count n) univ(:(num list -> bool)) + by perm_count_interleave_inj + perm (SUC n) + = CARD (perm_count (SUC n)) by perm_def + = CARD (BIGUNION s) by perm_count_suc + = CARD s * (n + 1) by CARD_BIGUNION_SAME_SIZED_SETS + = CARD (perm_count n) * (n + 1) by INJ_CARD_IMAGE + = perm n * (n + 1) by perm_def + = (SUC n) * perm n by MULT_COMM, ADD1 +*) +Theorem perm_suc: + !n. perm (SUC n) = (SUC n) * perm n +Proof + rpt strip_tac >> + qabbrev_tac `f = $interleave n` >> + qabbrev_tac `s = IMAGE f (perm_count n)` >> + `FINITE (perm_count n)` by rw[perm_count_finite] >> + `FINITE s` by rw[Abbr`s`] >> + `!e. e IN s ==> FINITE e /\ CARD e = n + 1` + by metis_tac[perm_count_interleave_finite, perm_count_interleave_card] >> + `PAIR_DISJOINT s` by metis_tac[perm_count_interleave_disjoint] >> + `INJ f (perm_count n) univ(:(num list -> bool))` by rw[perm_count_interleave_inj, Abbr`f`] >> + simp[perm_def] >> + `CARD (perm_count (SUC n)) = CARD (BIGUNION s)` by rw[perm_count_suc, Abbr`s`, Abbr`f`] >> + `_ = CARD s * (n + 1)` by rw[CARD_BIGUNION_SAME_SIZED_SETS] >> + `_ = CARD (perm_count n) * (n + 1)` by metis_tac[INJ_CARD_IMAGE] >> + simp[ADD1] +QED + +(* Theorem: perm (n + 1) = (n + 1) * perm n *) +(* Proof: by perm_suc, ADD1 *) +Theorem perm_suc_alt: + !n. perm (n + 1) = (n + 1) * perm n +Proof + simp[perm_suc, GSYM ADD1] +QED + +(* Theorem: perm 0 = 1 /\ !n. perm (n + 1) = (n + 1) * perm n *) +(* Proof: by perm_0, perm_suc_alt *) +Theorem perm_alt: + perm 0 = 1 /\ !n. perm (n + 1) = (n + 1) * perm n +Proof + simp[perm_0, perm_suc_alt] +QED + +(* Theorem: perm n = FACT n *) +(* Proof: by FACT_iff, perm_alt. *) +Theorem perm_eq_fact[compute]: + !n. perm n = FACT n +Proof + metis_tac[FACT_iff, perm_alt, ADD1] +QED + +(* This is fantastic! *) + +(* +> EVAL ``perm 3``; = 6 +> EVAL ``MAP perm [0 .. 10]``; = +[1; 1; 2; 6; 24; 120; 720; 5040; 40320; 362880; 3628800] +*) + +(* ------------------------------------------------------------------------- *) +(* Permutations of a set. *) +(* ------------------------------------------------------------------------- *) + +(* Note: SET_TO_LIST, using CHOICE and REST, is not effective for computations. +SET_TO_LIST_THM +|- FINITE s ==> + SET_TO_LIST s = if s = {} then [] else CHOICE s::SET_TO_LIST (REST s) +*) + +(* Define the set of permutation lists of a set. *) +Definition perm_set_def[nocompute]: + perm_set s = {ls | ALL_DISTINCT ls /\ set ls = s} +End +(* use [nocompute] as this is not effective for evalutaion. *) +(* Note: this cannot be made effective, unless sort s to list by some ordering. *) + +(* Theorem: ls IN perm_set s <=> ALL_DISTINCT ls /\ set ls = s *) +(* Proof: perm_set_def *) +Theorem perm_set_element: + !ls s. ls IN perm_set s <=> ALL_DISTINCT ls /\ set ls = s +Proof + simp[perm_set_def] +QED + +(* Theorem: perm_set (count n) = perm_count n *) +(* Proof: by perm_count_def, perm_set_def. *) +Theorem perm_set_perm_count: + !n. perm_set (count n) = perm_count n +Proof + simp[perm_count_def, perm_set_def] +QED + +(* Theorem: perm_set {} = {[]} *) +(* Proof: + perm_set {} + = {ls | ALL_DISTINCT ls /\ set ls = {}} by perm_set_def + = {ls | ALL_DISTINCT ls /\ ls = []} by LIST_TO_SET_EQ_EMPTY + = {[]} by ALL_DISTINCT +*) +Theorem perm_set_empty: + perm_set {} = {[]} +Proof + rw[perm_set_def, EXTENSION] >> + metis_tac[ALL_DISTINCT] +QED + +(* Theorem: perm_set {x} = {[x]} *) +(* Proof: + perm_set {x} + = {ls | ALL_DISTINCT ls /\ set ls = {x}} by perm_set_def + = {ls | ls = [x]} by DISTINCT_LIST_TO_SET_EQ_SING + = {[x]} by notation +*) +Theorem perm_set_sing: + !x. perm_set {x} = {[x]} +Proof + simp[perm_set_def, DISTINCT_LIST_TO_SET_EQ_SING] +QED + +(* Theorem: perm_set s = {[]} <=> s = {} *) +(* Proof: + If part: perm_set s = {[]} ==> s = {} + By contradiction, suppose s <> {}. + ls IN perm_set s + <=> ALL_DISTINCT ls /\ set ls = s by perm_set_element + ==> ls <> [] by LIST_TO_SET_EQ_EMPTY + This contradicts perm_set s = {[]} by IN_SING + Only-if part: s = {} ==> perm_set s = {[]} + This is true by perm_set_empty +*) +Theorem perm_set_eq_empty_sing: + !s. perm_set s = {[]} <=> s = {} +Proof + rw[perm_set_empty, EQ_IMP_THM] >> + `[] IN perm_set s` by fs[] >> + fs[perm_set_element] +QED + +(* Theorem: FINITE s ==> (SET_TO_LIST s) IN perm_set s *) +(* Proof: + Let ls = SET_TO_LIST s. + Note ALL_DISTINCT ls by ALL_DISTINCT_SET_TO_LIST + and set ls = s by SET_TO_LIST_INV + Thus ls IN perm_set s by perm_set_element +*) +Theorem perm_set_has_self_list: + !s. FINITE s ==> (SET_TO_LIST s) IN perm_set s +Proof + simp[perm_set_element, ALL_DISTINCT_SET_TO_LIST, SET_TO_LIST_INV] +QED + +(* Theorem: FINITE s ==> perm_set s <> {} *) +(* Proof: + Let ls = SET_TO_LIST s. + Then ls IN perm_set s by perm_set_has_self_list + Thus perm_set s <> {} by MEMBER_NOT_EMPTY +*) +Theorem perm_set_not_empty: + !s. FINITE s ==> perm_set s <> {} +Proof + metis_tac[perm_set_has_self_list, MEMBER_NOT_EMPTY] +QED + +(* Theorem: perm_set (set ls) <> {} *) +(* Proof: + Note FINITE (set ls) by FINITE_LIST_TO_SET + so perm_set (set ls) <> {} by perm_set_not_empty +*) +Theorem perm_set_list_not_empty: + !ls. perm_set (set ls) <> {} +Proof + simp[FINITE_LIST_TO_SET, perm_set_not_empty] +QED + +(* Theorem: ls IN perm_set s /\ BIJ f s (count n) ==> MAP f ls IN perm_count n *) +(* Proof: + By perm_set_def, perm_count_def, this is to show: + (1) ALL_DISTINCT ls /\ BIJ f (set ls) (count n) ==> ALL_DISTINCT (MAP f ls) + Note INJ f (set ls) (count n) by BIJ_DEF + so ALL_DISTINCT (MAP f ls) by ALL_DISTINCT_MAP_INJ, INJ_DEF + (2) ALL_DISTINCT ls /\ BIJ f (set ls) (count n) ==> set (MAP f ls) = count n + Note SURJ f (set ls) (count n) by BIJ_DEF + so set (MAP f ls) + = IMAGE f (set ls) by LIST_TO_SET_MAP + = count n by IMAGE_SURJ +*) +Theorem perm_set_map_element: + !ls f s n. ls IN perm_set s /\ BIJ f s (count n) ==> MAP f ls IN perm_count n +Proof + rw[perm_set_def, perm_count_def] >- + metis_tac[ALL_DISTINCT_MAP_INJ, BIJ_IS_INJ] >> + simp[LIST_TO_SET_MAP] >> + fs[IMAGE_SURJ, BIJ_DEF] +QED + +(* Theorem: BIJ f s (count n) ==> + INJ (MAP f) (perm_set s) (perm_count n) *) +(* Proof: + By INJ_DEF, this is to show: + (1) x IN perm_set s ==> MAP f x IN perm_count n + This is true by perm_set_map_element + (2) x IN perm_set s /\ y IN perm_set s /\ MAP f x = MAP f y ==> x = y + Note LENGTH x = LENGTH y by LENGTH_MAP + By LIST_EQ, it remains to show: + !j. j < LENGTH x ==> EL j x = EL j y + Note EL j x IN s by perm_set_element, MEM_EL + and EL j y IN s by perm_set_element, MEM_EL + MAP f x = MAP f y + ==> EL j (MAP f x) = EL j (MAP f y) + ==> f (EL j x) = f (EL j y) by EL_MAP + ==> EL j x = EL j y by BIJ_IS_INJ +*) +Theorem perm_set_map_inj: + !f s n. BIJ f s (count n) ==> + INJ (MAP f) (perm_set s) (perm_count n) +Proof + rw[INJ_DEF] >- + metis_tac[perm_set_map_element] >> + irule LIST_EQ >> + `LENGTH x = LENGTH y` by metis_tac[LENGTH_MAP] >> + rw[] >> + `EL x' x IN s` by metis_tac[perm_set_element, MEM_EL] >> + `EL x' y IN s` by metis_tac[perm_set_element, MEM_EL] >> + metis_tac[EL_MAP, BIJ_IS_INJ] +QED + +(* Theorem: BIJ f s (count n) ==> + SURJ (MAP f) (perm_set s) (perm_count n) *) +(* Proof: + By SURJ_DEF, this is to show: + (1) x IN perm_set s ==> MAP f x IN perm_count n + This is true by perm_set_map_element + (2) x IN perm_count n ==> ?y. y IN perm_set s /\ MAP f y = x + Let y = MAP (LINV f s) x. Then to show: + (1) y IN perm_set s, + Note BIJ (LINV f s) (count n) s by BIJ_LINV_BIJ + By perm_set_element, perm_count_element, to show: + (1) ALL_DISTINCT (MAP (LINV f s) x) + Note INJ (LINV f s) (count n) s by BIJ_DEF + so ALL_DISTINCT (MAP (LINV f s) x) + by ALL_DISTINCT_MAP_INJ, INJ_DEF + (2) set (MAP (LINV f s) x) = s + Note SURJ (LINV f s) (count n) s by BIJ_DEF + so set (MAP (LINV f s) x) + = IMAGE (LINV f s) (set x) by LIST_TO_SET_MAP + = IMAGE (LINV f s) (count n) by set x = count n + = s by IMAGE_SURJ + (2) x IN perm_count n ==> MAP f (MAP (LINV f s) x) = x + Let g = f o LINV f s. + The goal is: MAP g x = x by MAP_COMPOSE + Note LENGTH (MAP g x) = LENGTH x by LENGTH_MAP + To apply LIST_EQ, just need to show: + !k. k < LENGTH x ==> + EL k (MAP g x) = EL k x + or to show: g (EL k x) = EL k x by EL_MAP + Now set x = count n by perm_count_element + so EL k x IN (count n) by MEM_EL + Thus g (EL k x) = EL k x by BIJ_LINV_INV +*) +Theorem perm_set_map_surj: + !f s n. BIJ f s (count n) ==> + SURJ (MAP f) (perm_set s) (perm_count n) +Proof + rw[SURJ_DEF] >- + metis_tac[perm_set_map_element] >> + qexists_tac `MAP (LINV f s) x` >> + rpt strip_tac >| [ + `BIJ (LINV f s) (count n) s` by rw[BIJ_LINV_BIJ] >> + fs[perm_set_element, perm_count_element] >> + rpt strip_tac >- + metis_tac[ALL_DISTINCT_MAP_INJ, BIJ_IS_INJ] >> + simp[LIST_TO_SET_MAP] >> + fs[IMAGE_SURJ, BIJ_DEF], + simp[MAP_COMPOSE] >> + qabbrev_tac `g = f o LINV f s` >> + irule LIST_EQ >> + `LENGTH (MAP g x) = LENGTH x` by rw[LENGTH_MAP] >> + rw[] >> + simp[EL_MAP] >> + fs[perm_count_element, Abbr`g`] >> + metis_tac[MEM_EL, BIJ_LINV_INV] + ] +QED + +(* Theorem: BIJ f s (count n) ==> + BIJ (MAP f) (perm_set s) (perm_count n) *) +(* Proof: + Note INJ (MAP f) (perm_set s) (perm_count n) by perm_set_map_inj + and SURJ (MAP f) (perm_set s) (perm_count n) by perm_set_map_surj + Thus BIJ (MAP f) (perm_set s) (perm_count n) by BIJ_DEF +*) +Theorem perm_set_map_bij: + !f s n. BIJ f s (count n) ==> + BIJ (MAP f) (perm_set s) (perm_count n) +Proof + simp[BIJ_DEF, perm_set_map_inj, perm_set_map_surj] +QED + +(* Theorem: FINITE s ==> perm_set s =b= perm_count (CARD s) *) +(* Proof: + Note ?f. BIJ f s (count (CARD s)) by bij_eq_count, FINITE s + Thus BIJ (MAP f) (perm_set s) (perm_count (CARD s)) + by perm_set_map_bij + showing perm_set s =b= perm_count (CARD s) by notation +*) +Theorem perm_set_bij_eq_perm_count: + !s. FINITE s ==> perm_set s =b= perm_count (CARD s) +Proof + rpt strip_tac >> + imp_res_tac bij_eq_count >> + metis_tac[perm_set_map_bij] +QED + +(* Theorem: FINITE s ==> FINITE (perm_set s) *) +(* Proof: + Note perm_set s =b= perm_count (CARD s) by perm_set_bij_eq_perm_count + and FINITE (perm_count (CARD s)) by perm_count_finite + so FINITE (perm_set s) by bij_eq_finite +*) +Theorem perm_set_finite: + !s. FINITE s ==> FINITE (perm_set s) +Proof + metis_tac[perm_set_bij_eq_perm_count, perm_count_finite, bij_eq_finite] +QED + +(* Theorem: FINITE s ==> CARD (perm_set s) = perm (CARD s) *) +(* Proof: + Note perm_set s =b= perm_count (CARD s) by perm_set_bij_eq_perm_count + and FINITE (perm_count (CARD s)) by perm_count_finite + so CARD (perm_set s) + = CARD (perm_count (CARD s)) by bij_eq_card + = perm (CARD s) by perm_def +*) +Theorem perm_set_card: + !s. FINITE s ==> CARD (perm_set s) = perm (CARD s) +Proof + metis_tac[perm_set_bij_eq_perm_count, perm_count_finite, bij_eq_card, perm_def] +QED + +(* This is a major result! *) + +(* Theorem: FINITE s ==> CARD (perm_set s) = FACT (CARD s) *) +(* Proof: by perm_set_card, perm_eq_fact. *) +Theorem perm_set_card_alt: + !s. FINITE s ==> CARD (perm_set s) = FACT (CARD s) +Proof + simp[perm_set_card, perm_eq_fact] +QED + +(* ------------------------------------------------------------------------- *) +(* Counting number of arrangements. *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of choices of k-tuples of (count n). *) +Definition list_count_def[nocompute]: + list_count n k = + { ls | ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k} +End +(* use [nocompute] as this is not effective for evalutaion. *) +(* Note: if defined as: + list_count n k = { ls | (set ls) SUBSET (count n) /\ CARD (set ls) = k} +then non-distinct lists will be in the set, which is not desirable. +*) + +(* Define the number of choices of k-tuples of (count n). *) +Definition arrange_def[nocompute]: + arrange n k = CARD (list_count n k) +End +(* use [nocompute] as this is not effective for evalutaion. *) +(* make this an infix operator *) +val _ = set_fixity "arrange" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) +(* arrange_def; +val it = |- !n k. n arrange k = CARD (list_count n k): thm *) + +(* Theorem: list_count n k = + { ls | ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k} *) +(* Proof: + ls IN list_count n k + <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k + by list_count_def + <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k + by ALL_DISTINCT_CARD_LIST_TO_SET + Hence the sets are equal by EXTENSION. +*) +Theorem list_count_alt: + !n k. list_count n k = + { ls | ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k} +Proof + simp[list_count_def, EXTENSION] >> + metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET] +QED + +(* Theorem: ls IN list_count n k <=> + ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k *) +(* Proof: by list_count_def. *) +Theorem list_count_element: + !ls n k. ls IN list_count n k <=> + ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k +Proof + simp[list_count_def] +QED + +(* Theorem: ls IN list_count n k <=> + ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k *) +(* Proof: by list_count_alt. *) +Theorem list_count_element_alt: + !ls n k. ls IN list_count n k <=> + ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k +Proof + simp[list_count_alt] +QED + +(* Theorem: ls IN list_count n k ==> CARD (set ls) = k *) +(* Proof: + ls IN list_count n k + <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k + by list_count_element + ==> CARD (set ls) = k by ALL_DISTINCT_CARD_LIST_TO_SET +*) +Theorem list_count_element_set_card: + !ls n k. ls IN list_count n k ==> CARD (set ls) = k +Proof + simp[list_count_def, ALL_DISTINCT_CARD_LIST_TO_SET] +QED + +(* Theorem: list_count n k SUBSET necklace k n *) +(* Proof: + ls IN list_count n k + <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k + by list_count_element + ==> (set ls) SUBSET (count n) /\ LENGTH ls = k + ==> ls IN necklace k n by necklace_def + Thus list_count n k SUBSET necklace k n by SUBSET_DEF +*) +Theorem list_count_subset: + !n k. list_count n k SUBSET necklace k n +Proof + simp[list_count_def, necklace_def, SUBSET_DEF] +QED + +(* Theorem: FINITE (list_count n k) *) +(* Proof: + Note list_count n k SUBSET necklace k n by list_count_subset + and FINITE (necklace k n) by necklace_finite + so FINITE (list_count n k) by SUBSET_FINITE +*) +Theorem list_count_finite: + !n k. FINITE (list_count n k) +Proof + metis_tac[list_count_subset, necklace_finite, SUBSET_FINITE] +QED + +(* Note: +list_count 4 2 has P(4,2) = 4 * 3 = 12 elements. +necklace 2 4 has 2 ** 4 = 16 elements. + +> EVAL ``necklace 2 4``; +val it = |- necklace 2 4 = + {[3; 3]; [3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 2]; [2; 1]; [2; 0]; + [1; 3]; [1; 2]; [1; 1]; [1; 0]; [0; 3]; [0; 2]; [0; 1]; [0; 0]}: thm +> EVAL ``IMAGE set (necklace 2 4)``; +val it = |- IMAGE set (necklace 2 4) = + {{3}; {2; 3}; {2}; {1; 3}; {1; 2}; {1}; {0; 3}; {0; 2}; {0; 1}; {0}}: +> EVAL ``IMAGE (\ls. if CARD (set ls) = 2 then ls else []) (necklace 2 4)``; +val it = |- IMAGE (\ls. if CARD (set ls) = 2 then ls else []) (necklace 2 4) = + {[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; + [1; 0]; [0; 3]; [0; 2]; [0; 1]; []}: thm +> EVAL ``let n = 4; k = 2 in (IMAGE (\ls. if CARD (set ls) = k then ls else []) (necklace k n)) DELETE []``; +val it = |- (let n = 4; k = 2 in +IMAGE (\ls. if CARD (set ls) = k then ls else []) (necklace k n) DELETE []) = + {[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; + [1; 0]; [0; 3]; [0; 2]; [0; 1]}: thm +> EVAL ``let n = 4; k = 2 in (IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n)) DELETE []``; +val it = |- (let n = 4; k = 2 in + IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE []) = + {[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; + [1; 0]; [0; 3]; [0; 2]; [0; 1]}: thm +*) + +(* Note: +P(n,k) = C(n,k) * k! +P(n,0) = C(n,0) * 0! = 1 +P(0,k+1) = C(0,k+1) * (k+1)! = 0 +*) + +(* Theorem: list_count n 0 = {[]} *) +(* Proof: + ls IN list_count n 0 + <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = 0 + by list_count_element + <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ ls = [] + by LENGTH_NIL + <=> T /\ T /\ ls = [] by ALL_DISTINCT, LIST_TO_SET, EMPTY_SUBSET + Thus list_count n 0 = {[]} by EXTENSION +*) +Theorem list_count_n_0: + !n. list_count n 0 = {[]} +Proof + rw[list_count_def, EXTENSION, EQ_IMP_THM] +QED + +(* Theorem: 0 < n ==> list_count 0 n = {} *) +(* Proof: + Note (list_count 0 n) SUBSET (necklace n 0) + by list_count_subset + but (necklace n 0) = {} by necklace_empty, 0 < n + Thus (list_count 0 n) = {} by SUBSET_EMPTY +*) +Theorem list_count_0_n: + !n. 0 < n ==> list_count 0 n = {} +Proof + metis_tac[list_count_subset, necklace_empty, SUBSET_EMPTY] +QED + +(* Theorem: list_count n n = perm_count n *) +(* Proof: + ls IN list_count n n + <=> ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = n + by list_count_element_alt + <=> ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = CARD (count n) + by CARD_COUNT + <=> ALL_DISTINCT ls /\ set ls SUBSET count n /\ set ls = count n + by SUBSET_CARD_EQ + <=> ALL_DISTINCT ls /\ set ls = count n by SUBSET_REFL + <=> ls IN perm_count n by perm_count_element +*) +Theorem list_count_n_n: + !n. list_count n n = perm_count n +Proof + rw_tac bool_ss[list_count_element_alt, EXTENSION] >> + `FINITE (count n) /\ CARD (count n) = n` by rw[] >> + metis_tac[SUBSET_REFL, SUBSET_CARD_EQ, perm_count_element] +QED + +(* Theorem: list_count n k = {} <=> n < k *) +(* Proof: + If part: list_count n k = {} ==> n < k + By contradiction, suppose k <= n. + Let ls = SET_TO_LIST (count k). + Note FINITE (count k) by FINITE_COUNT + Then ALL_DISTINCT ls by ALL_DISTINCT_SET_TO_LIST + and set ls = count k by SET_TO_LIST_INV + Now (count k) SUBSET (count n) by COUNT_SUBSET, k <= n + and CARD (count k) = k by CARD_COUNT + so ls IN list_count n k by list_count_element_alt + Thus list_count n k <> {} by MEMBER_NOT_EMPTY + which is a contradiction. + Only-if part: n < k ==> list_count n k = {} + By contradiction, suppose sub_count n k <> {}. + Then ?ls. ls IN list_count n k by MEMBER_NOT_EMPTY + ==> ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = k + by sub_count_element_alt + Note FINITE (count n) by FINITE_COUNT + so CARD (set ls) <= CARD (count n) + by CARD_SUBSET + ==> k <= n by CARD_COUNT + This contradicts n < k. +*) +Theorem list_count_eq_empty: + !n k. list_count n k = {} <=> n < k +Proof + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + qabbrev_tac `ls = SET_TO_LIST (count k)` >> + `FINITE (count k)` by rw[FINITE_COUNT] >> + `ALL_DISTINCT ls` by rw[ALL_DISTINCT_SET_TO_LIST, Abbr`ls`] >> + `set ls = count k` by rw[SET_TO_LIST_INV, Abbr`ls`] >> + `(count k) SUBSET (count n)` by rw[COUNT_SUBSET] >> + `CARD (count k) = k` by rw[] >> + metis_tac[list_count_element_alt, MEMBER_NOT_EMPTY], + spose_not_then strip_assume_tac >> + `?ls. ls IN list_count n k` by rw[MEMBER_NOT_EMPTY] >> + fs[list_count_element_alt] >> + `FINITE (count n)` by rw[] >> + `CARD (set ls) <= n` by metis_tac[CARD_SUBSET, CARD_COUNT] >> + decide_tac + ] +QED + +(* Theorem: 0 < k ==> + list_count n k = + IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] *) +(* Proof: + x IN IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] + <=> ?ls. x = (if ALL_DISTINCT ls then ls else []) /\ + LENGTH ls = k /\ set ls SUBSET count n) /\ x <> [] by IN_IMAGE, IN_DELETE + <=> ALL_DISTINCT x /\ LENGTH x = k /\ set x SUBSET count n by LENGTH_NIL, 0 < k, ls = x + <=> x IN list_count n k by list_count_element + Thus the two sets are equal by EXTENSION. +*) +Theorem list_count_by_image: + !n k. 0 < k ==> + list_count n k = + IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] +Proof + rw[list_count_def, necklace_def, EXTENSION] >> + (rw[EQ_IMP_THM] >> metis_tac[LENGTH_NIL, NOT_ZERO]) +QED + +(* Theorem: list_count n k = + if k = 0 then {[]} + else IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] *) +(* Proof: by list_count_n_0, list_count_by_image *) +Theorem list_count_eqn[compute]: + !n k. list_count n k = + if k = 0 then {[]} + else IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] +Proof + rw[list_count_n_0, list_count_by_image] +QED + +(* +> EVAL ``list_count 3 2``; +val it = |- list_count 3 2 = {[2; 1]; [2; 0]; [1; 2]; [1; 0]; [0; 2]; [0; 1]}: thm +> EVAL ``list_count 4 2``; +val it = |- list_count 4 2 = +{[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; [1; 0]; [0; 3]; [0; 2]; [0; 1]}: thm +*) + +(* Idea: define an equivalence relation feq set: set x = set y. + There are k! elements in each equivalence class. + Thus n arrange k = perm k * n choose k. *) + +(* Theorem: (feq set) equiv_on s *) +(* Proof: by feq_equiv. *) +Theorem feq_set_equiv: + !s. (feq set) equiv_on s +Proof + simp[feq_equiv] +QED + +(* +> EVAL ``list_count 3 2``; +val it = |- list_count 3 2 = {[2; 1]; [1; 2]; [2; 0]; [0; 2]; [1; 0]; [0; 1]}: thm +*) + +(* Theorem: ls IN list_count n k ==> + equiv_class (feq set) (list_count n k) ls = perm_set (set ls) *) +(* Proof: + Note ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k + by list_count_element + x IN equiv_class (feq set) (list_count n k) ls + <=> x IN (list_count n k) /\ (feq set) ls x by equiv_class_element + <=> x IN (list_count n k) /\ set ls = set x by feq_def + <=> ALL_DISTINCT x /\ set x SUBSET count n /\ LENGTH x = k /\ + set x = set ls by list_count_element + <=> ALL_DISTINCT x /\ LENGTH x = LENGTH ls /\ set x = set ls + by given + <=> ALL_DISTINCT x /\ set x = set ls by ALL_DISTINCT_CARD_LIST_TO_SET + <=> x IN perm_set (set ls) by perm_set_element +*) +Theorem list_count_set_eq_class: + !ls n k. ls IN list_count n k ==> + equiv_class (feq set) (list_count n k) ls = perm_set (set ls) +Proof + rw[list_count_def, perm_set_def, fequiv_def, Once EXTENSION] >> + rw[EQ_IMP_THM] >> + metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET] +QED + +(* Theorem: ls IN list_count n k ==> + CARD (equiv_class (feq set) (list_count n k) ls) = perm k *) +(* Proof: + Note ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k + by list_count_element + CARD (equiv_class (feq set) (list_count n k) ls) + = CARD (perm_set (set ls)) by list_count_set_eq_class + = perm (CARD (set ls)) by perm_set_card + = perm (LENGTH ls) by ALL_DISTINCT_CARD_LIST_TO_SET + = perm k by LENGTH ls = k +*) +Theorem list_count_set_eq_class_card: + !ls n k. ls IN list_count n k ==> + CARD (equiv_class (feq set) (list_count n k) ls) = perm k +Proof + rw[list_count_set_eq_class] >> + fs[list_count_element] >> + simp[perm_set_card, ALL_DISTINCT_CARD_LIST_TO_SET] +QED + +(* Theorem: e IN partition (feq set) (list_count n k) ==> CARD e = perm k *) +(* Proof: + By partition_element, this is to show: + ls IN list_count n k ==> + CARD (equiv_class (feq set) (list_count n k) ls) = perm k + This is true by list_count_set_eq_class_card. +*) +Theorem list_count_set_partititon_element_card: + !n k e. e IN partition (feq set) (list_count n k) ==> CARD e = perm k +Proof + rw_tac bool_ss [partition_element] >> + simp[list_count_set_eq_class_card] +QED + +(* Theorem: ls IN list_count n k ==> perm_set (set ls) <> {} *) +(* Proof: + Note (feq set) equiv_on (list_count n k) by feq_set_equiv + and perm_set (set ls) + = equiv_class (feq set) (list_count n k) ls by list_count_set_eq_class + <> {} by equiv_class_not_empty +*) +Theorem list_count_element_perm_set_not_empty: + !ls n k. ls IN list_count n k ==> perm_set (set ls) <> {} +Proof + metis_tac[list_count_set_eq_class, feq_set_equiv, equiv_class_not_empty] +QED + +(* This is more restrictive than perm_set_list_not_empty, hence not useful. *) + +(* Theorem: s IN (partition (feq set) (list_count n k)) ==> + (set o CHOICE) s IN (sub_count n k) *) +(* Proof: + s IN (partition (feq set) (list_count n k)) + <=> ?z. z IN list_count n k /\ + !x. x IN s <=> x IN list_count n k /\ set x = set z + by feq_partition_element + ==> z IN s, so s <> {} by MEMBER_NOT_EMPTY + Let ls = CHOICE s. + Then ls IN s by CHOICE_DEF + so ls IN list_count n k /\ set ls = set z + by implication + or ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k + by list_count_element + Note (set o CHOICE) s = set ls by o_THM + and CARD (set ls) = LENGTH ls by ALL_DISTINCT_CARD_LIST_TO_SET + so set ls IN (sub_count n k) by sub_count_element_alt +*) +Theorem list_count_set_map_element: + !s n k. s IN (partition (feq set) (list_count n k)) ==> + (set o CHOICE) s IN (sub_count n k) +Proof + rw[feq_partition_element] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + `(CHOICE s) IN s` by fs[CHOICE_DEF] >> + fs[list_count_element, sub_count_element] >> + metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET] +QED + +(* Theorem: INJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) *) +(* Proof: + Let R = feq set, + s = list_count n k, + t = sub_count n k. + By INJ_DEF, this is to show: + (1) x IN partition R s ==> (set o CHOICE) x IN t + This is true by list_count_set_map_element + (2) x IN partition R s /\ y IN partition R s /\ + (set o CHOICE) x = (set o CHOICE) y ==> x = y + Note ?u. u IN list_count n k + !ls. ls IN x <=> ls IN list_count n k /\ set ls = set u + by feq_partition_element + and ?v. v IN list_count n k + !ls. ls IN y <=> ls IN list_count n k /\ set ls = set v + by feq_partition_element + Thus u IN x, so x <> {} by MEMBER_NOT_EMPTY + and v IN y, so y <> {} by MEMBER_NOT_EMPTY + Let lx = CHOICE x IN x by CHOICE_DEF + and ly = CHOICE y IN y by CHOICE_DEF + With set lx = set ly by o_THM + Thus set lx = set u by implication + and set ly = set v by implication + so set u = set v by above + Thus x = y by EXTENSION +*) +Theorem list_count_set_map_inj: + !n k. INJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) +Proof + rw_tac bool_ss[INJ_DEF] >- + simp[list_count_set_map_element] >> + fs[feq_partition_element] >> + `x <> {} /\ y <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + `CHOICE x IN x /\ CHOICE y IN y` by fs[CHOICE_DEF] >> + `set z = set z'` by rfs[] >> + simp[EXTENSION] +QED + +(* Theorem: SURJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) *) +(* Proof: + Let R = feq set, + s = list_count n k, + t = sub_count n k. + By SURJ_DEF, this is to show: + (1) x IN partition R s ==> (set o CHOICE) x IN t + This is true by list_count_set_map_element + (2) x IN t ==> ?y. y IN partition R s /\ (set o CHOICE) y = x + Note x SUBSET count n /\ CARD x = k by sub_count_element + Thus FINITE x by SUBSET_FINITE, FINITE_COUNT + Let y = perm_set x. + To show; + (1) y IN partition R s + Note y IN partition R s + <=> ?ls. ls IN list_count n k /\ + !z. z IN perm_set x <=> z IN list_count n k /\ set z = set ls + by feq_partition_element + Let ls = SET_TO_LIST x. + Then ALL_DISTINCT ls by ALL_DISTINCT_SET_TO_LIST, FINITE x + and set ls = x by SET_TO_LIST_INV, FINITE x + so set ls SUBSET (count n) by above, x SUBSET count n + and LENGTH ls = k by SET_TO_LIST_CARD, FINITE x + so ls IN list_count n k by list_count_element + To show: !z. z IN perm_set x <=> z IN list_count n k /\ set z = set ls + z IN perm_set x + <=> ALL_DISTINCT z /\ set z = x by perm_set_element + <=> ALL_DISTINCT z /\ set z SUBSET count n /\ set z = x + by x SUBSET count n + <=> ALL_DISTINCT z /\ set z SUBSET count n /\ LENGTH z = CARD x + by ALL_DISTINCT_CARD_LIST_TO_SET + <=> z IN list_count n k /\ set z = set ls + by list_count_element, CARD x = k, set ls = x. + (2) (set o CHOICE) y = x + Note y <> {} by perm_set_not_empty, FINITE x + Then CHOICE y IN y by CHOICE_DEF + so (set o CHOICE) y + = set (CHOICE y) by o_THM + = x by perm_set_element, y = perm_set x +*) +Theorem list_count_set_map_surj: + !n k. SURJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) +Proof + rw_tac bool_ss[SURJ_DEF] >- + simp[list_count_set_map_element] >> + fs[sub_count_element] >> + `FINITE x` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + qexists_tac `perm_set x` >> + simp[feq_partition_element, list_count_element, perm_set_element] >> + rpt strip_tac >| [ + qabbrev_tac `ls = SET_TO_LIST x` >> + qexists_tac `ls` >> + `ALL_DISTINCT ls` by rw[ALL_DISTINCT_SET_TO_LIST, Abbr`ls`] >> + `set ls = x` by rw[SET_TO_LIST_INV, Abbr`ls`] >> + `LENGTH ls = k` by rw[SET_TO_LIST_CARD, Abbr`ls`] >> + rw[EQ_IMP_THM] >> + metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET], + `perm_set x <> {}` by fs[perm_set_not_empty] >> + qabbrev_tac `ls = CHOICE (perm_set x)` >> + `ls IN perm_set x` by fs[CHOICE_DEF, Abbr`ls`] >> + fs[perm_set_element] + ] +QED + +(* Theorem: BIJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) *) +(* Proof: + Let f = set o CHOICE, + s = partition (feq set) (list_count n k), + t = sub_count n k. + Note INJ f s t by list_count_set_map_inj + and SURJ f s t by list_count_set_map_surj + so BIJ f s t by BIJ_DEF +*) +Theorem list_count_set_map_bij: + !n k. BIJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) +Proof + simp[BIJ_DEF, list_count_set_map_inj, list_count_set_map_surj] +QED + +(* Theorem: n arrange k = (n choose k) * perm k *) +(* Proof: + Let R = feq set, + s = list_count n k, + t = sub_count n k. + Then FINITE s by list_count_finite + and R equiv_on s by feq_set_equiv + and !e. e IN partition R s ==> CARD e = perm k + by list_count_set_partititon_element_card + Thus CARD s = perm k * CARD (partition R s) + by equal_partition_card, [1] + Note CARD s = n arrange k by arrange_def + and BIJ (set o CHOICE) (partition R s) t + by list_count_set_map_bij + and FINITE t by sub_count_finite + so CARD (partition R s) + = CARD t by bij_eq_card + = n choose k by choose_def + Hence n arrange k = n choose k * perm k + by MULT_COMM, [1], above. +*) +Theorem arrange_eqn[compute]: + !n k. n arrange k = (n choose k) * perm k +Proof + rpt strip_tac >> + assume_tac list_count_set_map_bij >> + last_x_assum (qspecl_then [`n`, `k`] strip_assume_tac) >> + qabbrev_tac `R = feq (set :num list -> num -> bool)` >> + qabbrev_tac `s = list_count n k` >> + qabbrev_tac `t = sub_count n k` >> + `FINITE s` by rw[list_count_finite, Abbr`s`] >> + `R equiv_on s` by rw[feq_set_equiv, Abbr`R`] >> + `!e. e IN partition R s ==> CARD e = perm k` by metis_tac[list_count_set_partititon_element_card] >> + imp_res_tac equal_partition_card >> + `FINITE t` by rw[sub_count_finite, Abbr`t`] >> + `CARD (partition R s) = CARD t` by metis_tac[bij_eq_card] >> + simp[arrange_def, choose_def, Abbr`s`, Abbr`t`] +QED + +(* This is P(n,k) = C(n,k) * k! *) + +(* Theorem: n arrange k = (n choose k) * FACT k *) +(* Proof: + n arrange k + = (n choose k) * perm k by arrange_eqn + = (n choose k) * FACT k by perm_eq_fact +*) +Theorem arrange_alt: + !n k. n arrange k = (n choose k) * FACT k +Proof + simp[arrange_eqn, perm_eq_fact] +QED + +(* +> EVAL ``5 arrange 2``; = 20 +> EVAL ``MAP ($arrange 5) [0 .. 5]``; = [1; 5; 20; 60; 120; 120] +*) + +(* Theorem: n arrange k = (binomial n k) * FACT k *) +(* Proof: + n arrange k + = (n choose k) * FACT k by arrange_alt + = (binomial n k) * FACT k by choose_eqn +*) +Theorem arrange_formula: + !n k. n arrange k = (binomial n k) * FACT k +Proof + simp[arrange_alt, choose_eqn] +QED + +(* Theorem: k <= n ==> n arrange k = FACT n DIV FACT (n - k) *) +(* Proof: + Note 0 < FACT (n - k) by FACT_LESS + (n arrange k) * FACT (n - k) + = (binomial n k) * FACT k * FACT (n - k) by arrange_formula + = binomial n k * (FACT (n - k) * FACT k) by arithmetic + = FACT n by binomial_formula2, k <= n + Thus n arrange k = FACT n DIV FACT (n - k) by DIV_SOLVE +*) +Theorem arrange_formula2: + !n k. k <= n ==> n arrange k = FACT n DIV FACT (n - k) +Proof + rpt strip_tac >> + `0 < FACT (n - k)` by rw[FACT_LESS] >> + `(n arrange k) * FACT (n - k) = (binomial n k) * FACT k * FACT (n - k)` by rw[arrange_formula] >> + `_ = binomial n k * (FACT (n - k) * FACT k)` by rw[] >> + `_ = FACT n` by rw[binomial_formula2] >> + simp[DIV_SOLVE] +QED + +(* Theorem: n arrange 0 = 1 *) +(* Proof: + n arrange 0 + = CARD (list_count n 0) by arrange_def + = CARD {[]} by list_count_n_0 + = 1 by CARD_SING +*) +Theorem arrange_n_0: + !n. n arrange 0 = 1 +Proof + simp[arrange_def, perm_def, list_count_n_0] +QED + +(* Theorem: 0 < n ==> 0 arrange n = 0 *) +(* Proof: + 0 arrange n + = CARD (list_count 0 n) by arrange_def + = CARD {} by list_count_0_n, 0 < n + = 0 by CARD_EMPTY +*) +Theorem arrange_0_n: + !n. 0 < n ==> 0 arrange n = 0 +Proof + simp[arrange_def, perm_def, list_count_0_n] +QED + +(* Theorem: n arrange n = perm n *) +(* Proof: + n arrange n + = (binomial n n) * FACT n by arrange_formula + = 1 * FACT n by binomial_n_n + = perm n by perm_eq_fact +*) +Theorem arrange_n_n: + !n. n arrange n = perm n +Proof + simp[arrange_formula, binomial_n_n, perm_eq_fact] +QED + +(* Theorem: n arrange n = FACT n *) +(* Proof: + n arrange n + = (binomial n n) * FACT n by arrange_formula + = 1 * FACT n by binomial_n_n +*) +Theorem arrange_n_n_alt: + !n. n arrange n = FACT n +Proof + simp[arrange_formula, binomial_n_n] +QED + +(* Theorem: n arrange k = 0 <=> n < k *) +(* Proof: + Note FINITE (list_count n k) by list_count_finite + n arrange k = 0 + <=> CARD (list_count n k) = 0 by arrange_def + <=> list_count n k = {} by CARD_EQ_0 + <=> n < k by list_count_eq_empty +*) +Theorem arrange_eq_0: + !n k. n arrange k = 0 <=> n < k +Proof + metis_tac[arrange_def, list_count_eq_empty, list_count_finite, CARD_EQ_0] +QED + +(* Note: + +k-permutation recurrence? + +P(n,k) = C(n,k) * k! +P(n,0) = C(n,0) * 0! = 1 +P(0,k+1) = C(0,k+1) * (k+1)! = 0 + +C(n+1,k+1) = C(n,k) + C(n,k+1) +P(n+1,k+1)/(k+1)! = P(n,k)/k! + P(n,k+1)/(k+1)! +P(n+1,k+1) = (k+1) * P(n,k) + P(n,k+1) +P(n+1,k+1) = P(n,k) * (k + 1) + P(n,k+1) + +P(2,1) = 2: [0] [1] +P(2,2) = 2: [0,1] [1,0] +P(3,2) = 6: [0,1] [0,2] include 2: [0,2] [1,2] [2,0] [2,1] + [1,0] [1,2] exclude 2: [0,1] [1,0] + [2,0] [2,1] +P(3,2) = P(2,1) * 2 + P(2,2) = ([0][1],2 + 2,[0][1]) + to_lists {0,1} +P(4,3): include 3: P(3,2) * 3 + exclude 3: P(3,3) + +list_count (n+1) (k+1) = IMAGE (interleave k) (list_count n k) UNION list_count n (k + 1) + +closed? +https://math.stackexchange.com/questions/3060456/ +using Pascal argument + +*) + +val _ = export_theory (); diff --git a/examples/algebra/lib/jcLib.sig b/src/algebra/jcLib.sig similarity index 100% rename from examples/algebra/lib/jcLib.sig rename to src/algebra/jcLib.sig diff --git a/examples/algebra/lib/jcLib.sml b/src/algebra/jcLib.sml similarity index 100% rename from examples/algebra/lib/jcLib.sml rename to src/algebra/jcLib.sml diff --git a/src/algebra/monoidScript.sml b/src/algebra/monoidScript.sml new file mode 100644 index 0000000000..2cfa5988b7 --- /dev/null +++ b/src/algebra/monoidScript.sml @@ -0,0 +1,5180 @@ +(* ------------------------------------------------------------------------- *) +(* Monoid *) +(* Monoid Order and Invertibles *) +(* Monoid Maps *) +(* Submonoid *) +(* Applying Monoid Theory: Monoid Instances *) +(* Theory about folding a monoid (or group) operation over a bag of elements *) +(* ------------------------------------------------------------------------- *) +(* Author: (Joseph) Hing-Lun Chan (Australian National University, 2019) *) +(* ------------------------------------------------------------------------- *) + +(* + +Monoid Theory +============= +A monoid is a semi-group with an identity. +The units of a monoid form a group. +A finite, cancellative monoid is also a group. + +*) + +(*===========================================================================*) + +(* add all dependent libraries for script *) +open HolKernel boolLib bossLib Parse; + +(* declare new theory at start *) +val _ = new_theory "monoid"; + +(* ------------------------------------------------------------------------- *) + +(* val _ = load "jcLib"; *) +open jcLib; + +(* open dependent theories *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory logrootTheory + listTheory rich_listTheory bagTheory gcdsetTheory dep_rewrite; + +open numberTheory combinatoricsTheory primeTheory; + +(* ------------------------------------------------------------------------- *) +(* Monoid Documentation *) +(* ------------------------------------------------------------------------- *) +(* Data type: + The generic symbol for monoid data is g. + g.carrier = Carrier set of monoid, overloaded as G. + g.op = Binary operation of monoid, overloaded as *. + g.id = Identity element of monoid, overloaded as #e. + + Overloading: + * = g.op + #e = g.id + ** = g.exp + G = g.carrier + GITSET g s b = ITSET g.op s b +*) +(* Definitions and Theorems (# are exported): + + Definitions: + Monoid_def |- !g. Monoid g <=> + (!x y. x IN G /\ y IN G ==> x * y IN G) /\ + (!x y z. x IN G /\ y IN G /\ z IN G ==> ((x * y) * z = x * (y * z))) /\ + #e IN G /\ (!x. x IN G ==> (#e * x = x) /\ (x * #e = x)) + AbelianMonoid_def |- !g. AbelianMonoid g <=> Monoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x) +# FiniteMonoid_def |- !g. FiniteMonoid g <=> Monoid g /\ FINITE G +# FiniteAbelianMonoid_def |- !g. FiniteAbelianMonoid g <=> AbelianMonoid g /\ FINITE G + + Extract from definition: +# monoid_id_element |- !g. Monoid g ==> #e IN G +# monoid_op_element |- !g. Monoid g ==> !x y. x IN G /\ y IN G ==> x * y IN G + monoid_assoc |- !g. Monoid g ==> !x y z. x IN G /\ y IN G /\ z IN G ==> (x * y * z = x * (y * z)) + monoid_id |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) /\ (x * #e = x) +# monoid_lid |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) +# monoid_rid |- !g. Monoid g ==> !x. x IN G ==> (x * #e = x) +# monoid_id_id |- !g. Monoid g ==> (#e * #e = #e) + + Simple theorems: + FiniteAbelianMonoid_def_alt |- !g. FiniteAbelianMonoid g <=> FiniteMonoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x) + monoid_carrier_nonempty |- !g. Monoid g ==> G <> {} + monoid_lid_unique |- !g. Monoid g ==> !e. e IN G ==> (!x. x IN G ==> (e * x = x)) ==> (e = #e) + monoid_rid_unique |- !g. Monoid g ==> !e. e IN G ==> (!x. x IN G ==> (x * e = x)) ==> (e = #e) + monoid_id_unique |- !g. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x) /\ (e * x = x)) <=> (e = #e)) + + Iteration of the binary operation gives exponentiation: + monoid_exp_def |- !g x n. x ** n = FUNPOW ($* x) n #e +# monoid_exp_0 |- !g x. x ** 0 = #e +# monoid_exp_SUC |- !g x n. x ** SUC n = x * x ** n +# monoid_exp_element |- !g. Monoid g ==> !x. x IN G ==> !n. x ** n IN G +# monoid_exp_1 |- !g. Monoid g ==> !x. x IN G ==> (x ** 1 = x) +# monoid_id_exp |- !g. Monoid g ==> !n. #e ** n = #e + + Monoid commutative elements: + monoid_comm_exp |- !g. Monoid g ==> !x y. x IN G /\ y IN G ==> (x * y = y * x) ==> !n. x ** n * y = y * x ** n + monoid_exp_comm |- !g. Monoid g ==> !x. x IN G ==> !n. x ** n * x = x * x ** n +# monoid_exp_suc |- !g. Monoid g ==> !x. x IN G ==> !n. x ** SUC n = x ** n * x + monoid_comm_op_exp |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> + !n. (x * y) ** n = x ** n * y ** n + monoid_comm_exp_exp |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> + !n m. x ** n * y ** m = y ** m * x ** n +# monoid_exp_add |- !g. Monoid g ==> !x. x IN G ==> !n k. x ** (n + k) = x ** n * x ** k +# monoid_exp_mult |- !g. Monoid g ==> !x. x IN G ==> !n k. x ** (n * k) = (x ** n) ** k + monoid_exp_mult_comm |- !g. Monoid g ==> !x. x IN G ==> !m n. (x ** m) ** n = (x ** n) ** m + + + Finite Monoid: + finite_monoid_exp_not_distinct |- !g. FiniteMonoid g ==> !x. x IN G ==> + ?h k. (x ** h = x ** k) /\ h <> k + + Abelian Monoid ITSET Theorems: + GITSET_THM |- !s g b. FINITE s ==> (GITSET g s b = if s = {} then b + else GITSET g (REST s) (CHOICE s * b)) + GITSET_EMPTY |- !g b. GITSET g {} b = b + GITSET_INSERT |- !x. FINITE s ==> + !x g b. (GITSET g (x INSERT s) b = GITSET g (REST (x INSERT s)) (CHOICE (x INSERT s) * b)) + GITSET_PROPERTY |- !g s. FINITE s /\ s <> {} ==> !b. GITSET g s b = GITSET g (REST s) (CHOICE s * b) + + abelian_monoid_op_closure_comm_assoc_fun |- !g. AbelianMonoid g ==> closure_comm_assoc_fun $* G + COMMUTING_GITSET_INSERT |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::G. GITSET g (x INSERT s) b = GITSET g (s DELETE x) (x * b) + COMMUTING_GITSET_REDUCTION |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::G. GITSET g s (x * b) = x * GITSET g s b + COMMUTING_GITSET_RECURSES |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::G. GITSET g (x INSERT s) b = x * GITSET g (s DELETE x) b: + + Abelian Monoid PROD_SET: + GPROD_SET_def |- !g s. GPROD_SET g s = GITSET g s #e + GPROD_SET_THM |- !g s. (GPROD_SET g {} = #e) /\ + (AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !x::(G). GPROD_SET g (x INSERT s) = x * GPROD_SET g (s DELETE x)) + GPROD_SET_EMPTY |- !g s. GPROD_SET g {} = #e + GPROD_SET_SING |- !g. Monoid g ==> !x. x IN G ==> (GPROD_SET g {x} = x) + GPROD_SET_PROPERTY |- !g s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> GPROD_SET g s IN G + +*) + +(* ------------------------------------------------------------------------- *) +(* Monoid Definition. *) +(* ------------------------------------------------------------------------- *) + +(* Monoid and Group share the same type *) + +(* Set up monoid type as a record + A Monoid has: + . a carrier set (set = function 'a -> bool, since MEM is a boolean function) + . a binary operation (2-nary function) called multiplication + . an identity element for the binary operation +*) +val _ = Hol_datatype` + monoid = <| carrier: 'a -> bool; + op: 'a -> 'a -> 'a; + id: 'a + |>`; +(* If the field inv: 'a -> 'a; is included, + there will be an implicit monoid_inv accessor generated. + Later, when monoid_inv_def defines another monoid_inv, + the monoid_accessors will ALL be invalidated! + So, do not include the field inv here, + but use add_record_field ("inv", ``monoid_inv``) + to achieve the same effect. +*) + +(* Using symbols m for monoid and g for group + will give excessive overloading for Monoid and Group, + so the generic symbol for both is taken as g. *) +(* set overloading *) +val _ = overload_on ("*", ``g.op``); +val _ = overload_on ("#e", ``g.id``); +val _ = overload_on ("G", ``g.carrier``); + +(* Monoid Definition: + A Monoid is a set with elements of type 'a monoid, such that + . Closure: x * y is in the carrier set + . Associativity: (x * y) * z = x * (y * z)) + . Existence of identity: #e is in the carrier set + . Property of identity: #e * x = x * #e = x +*) +(* Define Monoid by predicate *) +val Monoid_def = Define` + Monoid (g:'a monoid) <=> + (!x y. x IN G /\ y IN G ==> x * y IN G) /\ + (!x y z. x IN G /\ y IN G /\ z IN G ==> ((x * y) * z = x * (y * z))) /\ + #e IN G /\ (!x. x IN G ==> (#e * x = x) /\ (x * #e = x)) +`; +(* export basic definition -- but too many and's. *) +(* val _ = export_rewrites ["Monoid_def"]; *) + +(* ------------------------------------------------------------------------- *) +(* More Monoid Defintions. *) +(* ------------------------------------------------------------------------- *) + +(* Abelian Monoid = a Monoid that is commutative: x * y = y * x. *) +val AbelianMonoid_def = Define` + AbelianMonoid (g:'a monoid) <=> + Monoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x) +`; +(* export simple definition -- but this one has commutativity, so don't. *) +(* val _ = export_rewrites ["AbelianMonoid_def"]; *) + +(* Finite Monoid = a Monoid with a finite carrier set. *) +val FiniteMonoid_def = Define` + FiniteMonoid (g:'a monoid) <=> + Monoid g /\ FINITE G +`; +(* export simple definition. *) +val _ = export_rewrites ["FiniteMonoid_def"]; + +(* Finite Abelian Monoid = a Monoid that is both Finite and Abelian. *) +val FiniteAbelianMonoid_def = Define` + FiniteAbelianMonoid (g:'a monoid) <=> + AbelianMonoid g /\ FINITE G +`; +(* export simple definition. *) +val _ = export_rewrites ["FiniteAbelianMonoid_def"]; + +(* ------------------------------------------------------------------------- *) +(* Basic theorems from definition. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: Finite Abelian Monoid = Finite Monoid /\ commutativity. *) +(* Proof: by definitions. *) +val FiniteAbelianMonoid_def_alt = store_thm( + "FiniteAbelianMonoid_def_alt", + ``!g:'a monoid. FiniteAbelianMonoid g <=> + FiniteMonoid g /\ !x y. x IN G /\ y IN G ==> (x * y = y * x)``, + rw[AbelianMonoid_def, EQ_IMP_THM]); + +(* Monoid clauses from definition, in implicative form, no for-all, internal use only. *) +val monoid_clauses = Monoid_def |> SPEC_ALL |> #1 o EQ_IMP_RULE; +(* > val monoid_clauses = + |- Monoid g ==> + (!x y. x IN G /\ y IN G ==> x * y IN G) /\ + (!x y z. x IN G /\ y IN G /\ z IN G ==> (x * y * z = x * (y * z))) /\ + #e IN G /\ !x. x IN G ==> (#e * x = x) /\ (x * #e = x) : thm *) + +(* Extract theorems from Monoid clauses. *) +(* No need to export as definition is already exported. *) + +(* Theorem: [Closure] x * y in carrier. *) +val monoid_op_element = save_thm("monoid_op_element", + monoid_clauses |> UNDISCH_ALL |> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); +(* > val monoid_op_element = |- !g. Monoid g ==> !x y. x IN G /\ y IN G ==> x * y IN G : thm*) + +(* Theorem: [Associativity] (x * y) * z = x * (y * z) *) +val monoid_assoc = save_thm("monoid_assoc", + monoid_clauses |> UNDISCH_ALL |> CONJUNCT2|> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); +(* > val monoid_assoc = |- !g. Monoid g ==> !x y z. x IN G /\ y IN G /\ z IN G ==> (x * y * z = x * (y * z)) : thm *) + +(* Theorem: [Identity exists] #e in carrier. *) +val monoid_id_element = save_thm("monoid_id_element", + monoid_clauses |> UNDISCH_ALL |> CONJUNCT2|> CONJUNCT2 |> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); +(* > val monoid_id_element = |- !g. Monoid g ==> #e IN G : thm *) + +(* Theorem: [Identity property] #e * x = x and x * #e = x *) +val monoid_id = save_thm("monoid_id", + monoid_clauses |> UNDISCH_ALL |> CONJUNCT2|> CONJUNCT2 |> CONJUNCT2 |> DISCH_ALL |> GEN_ALL); +(* > val monoid_id = |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) /\ (x * #e = x) : thm *) + +(* Theorem: [Left identity] #e * x = x *) +(* Proof: from monoid_id. *) +val monoid_lid = save_thm("monoid_lid", + monoid_id |> SPEC_ALL |> UNDISCH_ALL |> SPEC_ALL |> UNDISCH_ALL |> CONJUNCT1 + |> DISCH ``x IN G`` |> GEN_ALL |> DISCH_ALL |> GEN_ALL); +(* > val monoid_lid = |- !g. Monoid g ==> !x. x IN G ==> (#e * x = x) : thm *) + +(* Theorem: [Right identity] x * #e = x *) +(* Proof: from monoid_id. *) +val monoid_rid = save_thm("monoid_rid", + monoid_id |> SPEC_ALL |> UNDISCH_ALL |> SPEC_ALL |> UNDISCH_ALL |> CONJUNCT2 + |> DISCH ``x IN G`` |> GEN_ALL |> DISCH_ALL |> GEN_ALL); +(* > val monoid_rid = |- !g. Monoid g ==> !x. x IN G ==> (x * #e = x) : thm *) + +(* export simple statements (no complicated and's) *) +val _ = export_rewrites ["monoid_op_element"]; +(* val _ = export_rewrites ["monoid_assoc"]; -- no associativity *) +val _ = export_rewrites ["monoid_id_element"]; +val _ = export_rewrites ["monoid_lid"]; +val _ = export_rewrites ["monoid_rid"]; + +(* Theorem: #e * #e = #e *) +(* Proof: + by monoid_lid and monoid_id_element. +*) +val monoid_id_id = store_thm( + "monoid_id_id", + ``!g:'a monoid. Monoid g ==> (#e * #e = #e)``, + rw[]); + +val _ = export_rewrites ["monoid_id_id"]; + +(* ------------------------------------------------------------------------- *) +(* Theorems in basic Monoid Theory. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: [Monoid carrier nonempty] G <> {} *) +(* Proof: by monoid_id_element. *) +val monoid_carrier_nonempty = store_thm( + "monoid_carrier_nonempty", + ``!g:'a monoid. Monoid g ==> G <> {}``, + metis_tac[monoid_id_element, MEMBER_NOT_EMPTY]); + +(* Theorem: [Left Identity unique] !x. (e * x = x) ==> e = #e *) +(* Proof: + Put x = #e, + then e * #e = #e by given + but e * #e = e by monoid_rid + hence e = #e. +*) +val monoid_lid_unique = store_thm( + "monoid_lid_unique", + ``!g:'a monoid. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (e * x = x)) ==> (e = #e))``, + metis_tac[monoid_id_element, monoid_rid]); + +(* Theorem: [Right Identity unique] !x. (x * e = x) ==> e = #e *) +(* Proof: + Put x = #e, + then #e * e = #e by given + but #e * e = e by monoid_lid + hence e = #e. +*) +val monoid_rid_unique = store_thm( + "monoid_rid_unique", + ``!g:'a monoid. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x)) ==> (e = #e))``, + metis_tac[monoid_id_element, monoid_lid]); + +(* Theorem: [Identity unique] !x. (x * e = x) and (e * x = x) <=> e = #e *) +(* Proof: + If e, #e are two identities, + For e, put x = #e, #e*e = #e and e*#e = #e + For #e, put x = e, e*#e = e and #e*e = e + Therefore e = #e. +*) +val monoid_id_unique = store_thm( + "monoid_id_unique", + ``!g:'a monoid. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x) /\ (e * x = x)) <=> (e = #e))``, + metis_tac[monoid_id_element, monoid_id]); + + +(* ------------------------------------------------------------------------- *) +(* Application of basic Monoid Theory: *) +(* Exponentiation - the FUNPOW version of Monoid operation. *) +(* ------------------------------------------------------------------------- *) + +(* Define exponents of a monoid element: + For x in Monoid g, x ** 0 = #e + x ** (SUC n) = x * (x ** n) +*) +(* +val monoid_exp_def = Define` + (monoid_exp m x 0 = g.id) /\ + (monoid_exp m x (SUC n) = x * (monoid_exp m x n)) +`; +*) +val monoid_exp_def = Define `monoid_exp (g:'a monoid) (x:'a) n = FUNPOW (g.op x) n #e`; +(* val _ = export_rewrites ["monoid_exp_def"]; *) +(* +- monoid_exp_def; +> val it = |- !g x n. x ** n = FUNPOW ($* x) n #e : thm +*) + +(* export simple properties later *) +(* val _ = export_rewrites ["monoid_exp_def"]; *) + +(* Convert exp function to exp field, i.e. g.exp is defined to be monoid_exp. *) +val _ = add_record_field ("exp", ``monoid_exp``); +(* +- type_of ``g.exp``; +> val it = ``:'a -> num -> 'a`` : hol_type +*) +(* overloading *) +(* val _ = clear_overloads_on "**"; *) +(* val _ = overload_on ("**", ``monoid_exp g``); -- not this *) +val _ = overload_on ("**", ``g.exp``); + +(* Theorem: x ** 0 = #e *) +(* Proof: by definition and FUNPOW_0. *) +val monoid_exp_0 = store_thm( + "monoid_exp_0", + ``!g:'a monoid. !x:'a. x ** 0 = #e``, + rw[monoid_exp_def]); + +val _ = export_rewrites ["monoid_exp_0"]; + +(* Theorem: x ** (SUC n) = x * (x ** n) *) +(* Proof: by definition and FUNPOW_SUC. *) +val monoid_exp_SUC = store_thm( + "monoid_exp_SUC", + ``!g:'a monoid. !x:'a. !n. x ** (SUC n) = x * (x ** n)``, + rw[monoid_exp_def, FUNPOW_SUC]); + +(* should this be exported? Only FUNPOW_0 is exported. *) +val _ = export_rewrites ["monoid_exp_SUC"]; + +(* Theorem: (x ** n) in G *) +(* Proof: by induction on n. + Base case: x ** 0 IN G + x ** 0 = #e by monoid_exp_0 + in G by monoid_id_element. + Step case: x ** n IN G ==> x ** SUC n IN G + x ** SUC n + = x * (x ** n) by monoid_exp_SUC + in G by monoid_op_element and induction hypothesis +*) +val monoid_exp_element = store_thm( + "monoid_exp_element", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n. (x ** n) IN G``, + rpt strip_tac>> + Induct_on `n` >> + rw[]); + +val _ = export_rewrites ["monoid_exp_element"]; + +(* Theorem: x ** 1 = x *) +(* Proof: + x ** 1 + = x ** SUC 0 by ONE + = x * x ** 0 by monoid_exp_SUC + = x * #e by monoid_exp_0 + = x by monoid_rid +*) +val monoid_exp_1 = store_thm( + "monoid_exp_1", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> (x ** 1 = x)``, + rewrite_tac[ONE] >> + rw[]); + +val _ = export_rewrites ["monoid_exp_1"]; + +(* Theorem: (#e ** n) = #e *) +(* Proof: by induction on n. + Base case: #e ** 0 = #e + true by monoid_exp_0. + Step case: #e ** n = #e ==> #e ** SUC n = #e + #e ** SUC n + = #e * #e ** n by monoid_exp_SUC, monoid_id_element + = #e ** n by monoid_lid, monoid_exp_element + hence true by induction hypothesis. +*) +val monoid_id_exp = store_thm( + "monoid_id_exp", + ``!g:'a monoid. Monoid g ==> !n. #e ** n = #e``, + rpt strip_tac>> + Induct_on `n` >> + rw[]); + +val _ = export_rewrites ["monoid_id_exp"]; + +(* Theorem: For Abelian Monoid g, (x ** n) * y = y * (x ** n) *) +(* Proof: + Since x ** n IN G by monoid_exp_element + True by abelian property: !z y. z IN G /\ y IN G ==> z * y = y * z +*) +(* This is trivial for AbelianMonoid, since every element commutes. + However, what is needed is just for those elements that commute. *) + +(* Theorem: x * y = y * x ==> (x ** n) * y = y * (x ** n) *) +(* Proof: + By induction on n. + Base case: x ** 0 * y = y * x ** 0 + (x ** 0) * y + = #e * y by monoid_exp_0 + = y * #e by monoid_id + = y * (x ** 0) by monoid_exp_0 + Step case: x ** n * y = y * x ** n ==> x ** SUC n * y = y * x ** SUC n + x ** (SUC n) * y + = (x * x ** n) * y by monoid_exp_SUC + = x * ((x ** n) * y) by monoid_assoc + = x * (y * (x ** n)) by induction hypothesis + = (x * y) * (x ** n) by monoid_assoc + = (y * x) * (x ** n) by abelian property + = y * (x * (x ** n)) by monoid_assoc + = y * x ** (SUC n) by monoid_exp_SUC +*) +val monoid_comm_exp = store_thm( + "monoid_comm_exp", + ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G ==> (x * y = y * x) ==> !n. (x ** n) * y = y * (x ** n)``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + metis_tac[monoid_exp_SUC, monoid_assoc, monoid_exp_element]); + +(* do not export commutativity check *) +(* val _ = export_rewrites ["monoid_comm_exp"]; *) + +(* Theorem: (x ** n) * x = x * (x ** n) *) +(* Proof: + Since x * x = x * x, this is true by monoid_comm_exp. +*) +val monoid_exp_comm = store_thm( + "monoid_exp_comm", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n. (x ** n) * x = x * (x ** n)``, + rw[monoid_comm_exp]); + +(* no export of commutativity *) +(* val _ = export_rewrites ["monoid_exp_comm"]; *) + +(* Theorem: x ** (SUC n) = (x ** n) * x *) +(* Proof: by monoid_exp_SUC and monoid_exp_comm. *) +val monoid_exp_suc = store_thm( + "monoid_exp_suc", + ``!g:'a monoid. Monoid g ==> !x:'a. x IN G ==> !n. x ** (SUC n) = (x ** n) * x``, + rw[monoid_exp_comm]); + +(* no export of commutativity *) +(* val _ = export_rewrites ["monoid_exp_suc"]; *) + +(* Theorem: x * y = y * x ==> (x * y) ** n = (x ** n) * (y ** n) *) +(* Proof: + By induction on n. + Base case: (x * y) ** 0 = x ** 0 * y ** 0 + (x * y) ** 0 + = #e by monoid_exp_0 + = #e * #e by monoid_id_id + = (x ** 0) * (y ** 0) by monoid_exp_0 + Step case: (x * y) ** n = (x ** n) * (y ** n) ==> (x * y) ** SUC n = x ** SUC n * y ** SUC n + (x * y) ** (SUC n) + = (x * y) * ((x * y) ** n) by monoid_exp_SUC + = (x * y) * ((x ** n) * (y ** n)) by induction hypothesis + = x * (y * ((x ** n) * (y ** n))) by monoid_assoc + = x * ((y * (x ** n)) * (y ** n)) by monoid_assoc + = x * (((x ** n) * y) * (y ** n)) by monoid_comm_exp + = x * ((x ** n) * (y * (y ** n))) by monoid_assoc + = (x * (x ** n)) * (y * (y ** n)) by monoid_assoc +*) +val monoid_comm_op_exp = store_thm( + "monoid_comm_op_exp", + ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> !n. (x * y) ** n = (x ** n) * (y ** n)``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + `(x * y) ** SUC n = x * ((y * (x ** n)) * (y ** n))` by rw[monoid_assoc] >> + `_ = x * (((x ** n) * y) * (y ** n))` by metis_tac[monoid_comm_exp] >> + rw[monoid_assoc]); + +(* do not export commutativity check *) +(* val _ = export_rewrites ["monoid_comm_op_exp"]; *) + +(* Theorem: x IN G /\ y IN G /\ x * y = y * x ==> (x ** n) * (y ** m) = (y ** m) * (x ** n) *) +(* Proof: + By inducton on m. + Base case: x ** n * y ** 0 = y ** 0 * x ** n + LHS = x ** n * y ** 0 + = x ** n * #e by monoid_exp_0 + = x ** n by monoid_rid + = #e * x ** n by monoid_lid + = y ** 0 * x ** n by monoid_exp_0 + = RHS + Step case: x ** n * y ** m = y ** m * x ** n ==> x ** n * y ** SUC m = y ** SUC m * x ** n + LHS = x ** n * y ** SUC m + = x ** n * (y * y ** m) by monoid_exp_SUC + = (x ** n * y) * y ** m by monoid_assoc + = (y * x ** n) * y ** m by monoid_comm_exp (with single y) + = y * (x ** n * y ** m) by monoid_assoc + = y * (y ** m * x ** n) by induction hypothesis + = (y * y ** m) * x ** n by monoid_assoc + = y ** SUC m * x ** n by monoid_exp_SUC + = RHS +*) +val monoid_comm_exp_exp = store_thm( + "monoid_comm_exp_exp", + ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> + !n m. x ** n * y ** m = y ** m * x ** n``, + rpt strip_tac >> + Induct_on `m` >- + rw[] >> + `x ** n * y ** SUC m = x ** n * (y * y ** m)` by rw[] >> + `_ = (x ** n * y) * y ** m` by rw[monoid_assoc] >> + `_ = (y * x ** n) * y ** m` by metis_tac[monoid_comm_exp] >> + `_ = y * (x ** n * y ** m)` by rw[monoid_assoc] >> + `_ = y * (y ** m * x ** n)` by metis_tac[] >> + rw[monoid_assoc]); + +(* Theorem: x ** (n + k) = (x ** n) * (x ** k) *) +(* Proof: + By induction on n. + Base case: x ** (0 + k) = x ** 0 * x ** k + x ** (0 + k) + = x ** k by arithmetic + = #e * (x ** k) by monoid_lid + = (x ** 0) * (x ** k) by monoid_exp_0 + Step case: x ** (n + k) = x ** n * x ** k ==> x ** (SUC n + k) = x ** SUC n * x ** k + x ** (SUC n + k) + = x ** (SUC (n + k)) by arithmetic + = x * (x ** (n + k)) by monoid_exp_SUC + = x * ((x ** n) * (x ** k)) by induction hypothesis + = (x * (x ** n)) * (x ** k) by monoid_assoc + = (x ** SUC n) * (x ** k) by monoid_exp_def +*) +val monoid_exp_add = store_thm( + "monoid_exp_add", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n k. x ** (n + k) = (x ** n) * (x ** k)``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + rw_tac std_ss[monoid_exp_SUC, monoid_assoc, monoid_exp_element, DECIDE ``SUC n + k = SUC (n+k)``]); + +(* export simple result *) +val _ = export_rewrites ["monoid_exp_add"]; + +(* Theorem: x ** (n * k) = (x ** n) ** k *) +(* Proof: + By induction on n. + Base case: x ** (0 * k) = (x ** 0) ** k + x ** (0 * k) + = x ** 0 by arithmetic + = #e by monoid_exp_0 + = (#e) ** n by monoid_id_exp + = (x ** 0) ** n by monoid_exp_0 + Step case: x ** (n * k) = (x ** n) ** k ==> x ** (SUC n * k) = (x ** SUC n) ** k + x ** (SUC n * k) + = x ** (n * k + k) by arithmetic + = (x ** (n * k)) * (x ** k) by monoid_exp_add + = ((x ** n) ** k) * (x ** k) by induction hypothesis + = ((x ** n) * x) ** k by monoid_comm_op_exp and monoid_exp_comm + = (x * (x ** n)) ** k by monoid_exp_comm + = (x ** SUC n) ** k by monoid_exp_def +*) +val monoid_exp_mult = store_thm( + "monoid_exp_mult", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n k. x ** (n * k) = (x ** n) ** k``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + `SUC n * k = n * k + k` by metis_tac[MULT] >> + `x ** (SUC n * k) = ((x ** n) * x) ** k` by rw_tac std_ss[monoid_comm_op_exp, monoid_exp_comm, monoid_exp_element, monoid_exp_add] >> + rw[monoid_exp_comm]); + +(* export simple result *) +val _ = export_rewrites ["monoid_exp_mult"]; + +(* Theorem: x IN G ==> (x ** m) ** n = (x ** n) ** m *) +(* Proof: + (x ** m) ** n + = x ** (m * n) by monoid_exp_mult + = x ** (n * m) by MULT_COMM + = (x ** n) ** m by monoid_exp_mult +*) +val monoid_exp_mult_comm = store_thm( + "monoid_exp_mult_comm", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !m n. (x ** m) ** n = (x ** n) ** m``, + metis_tac[monoid_exp_mult, MULT_COMM]); + + +(* ------------------------------------------------------------------------- *) +(* Finite Monoid. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: For FINITE Monoid g and x IN G, x ** k cannot be all distinct. *) +(* Proof: + By contradiction. Assume !k h. x ** k = x ** h ==> k = h, then + Since G is FINITE, let c = CARD G. + The map (count (SUC c)) -> G such that n -> x ** n is: + (1) a map since each x ** n IN G + (2) injective since all x ** n are distinct + But c < SUC c = CARD (count (SUC c)), and this contradicts the Pigeon-hole Principle. +*) +val finite_monoid_exp_not_distinct = store_thm( + "finite_monoid_exp_not_distinct", + ``!g:'a monoid. FiniteMonoid g ==> !x. x IN G ==> ?h k. (x ** h = x ** k) /\ (h <> k)``, + rw[FiniteMonoid_def] >> + spose_not_then strip_assume_tac >> + qabbrev_tac `c = CARD G` >> + `INJ (\n. x ** n) (count (SUC c)) G` by rw[INJ_DEF] >> + `c < SUC c` by decide_tac >> + metis_tac[CARD_COUNT, PHP]); +(* +This theorem implies that, if x ** k are all distinct for a Monoid g, +then its carrier G must be INFINITE. +Otherwise, this is not immediately useful for a Monoid, as the op has no inverse. +However, it is useful for a Group, where the op has inverse, +hence reduce this to x ** (h-k) = #e, if h > k. +Also, it is useful for an Integral Domain, where the prod.op still has no inverse, +but being a Ring, it has subtraction and distribution, giving x ** k * (x ** (h-k) - #1) = #0 +from which the no-zero-divisor property of Integral Domain gives x ** (h-k) = #1. +*) + +(* ------------------------------------------------------------------------- *) +(* Abelian Monoid ITSET Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Define ITSET for Monoid -- fold of g.op, especially for Abelian Monoid (by lifting) *) +val _ = overload_on("GITSET", ``\(g:'a monoid) s b. ITSET g.op s b``); + +(* +> ITSET_def |> ISPEC ``s:'b -> bool`` |> ISPEC ``(g:'a monoid).op`` |> ISPEC ``b:'a``; +val it = |- GITSET g s b = if FINITE s then if s = {} then b else GITSET g (REST s) (CHOICE s * b) + else ARB: thm +*) + +fun gINST th = th |> SPEC_ALL |> INST_TYPE [beta |-> alpha] + |> Q.INST [`f` |-> `g.op`] |> GEN_ALL; +(* val gINST = fn: thm -> thm *) + +val GITSET_THM = save_thm("GITSET_THM", gINST ITSET_THM); +(* > val GITSET_THM = + |- !s g b. FINITE s ==> (GITSET g s b = if s = {} then b else GITSET g (REST s) (CHOICE s * b)) : thm +*) + +(* Theorem: GITSET {} b = b *) +val GITSET_EMPTY = save_thm("GITSET_EMPTY", gINST ITSET_EMPTY); +(* > val GITSET_EMPTY = |- !g b. GITSET g {} b = b : thm *) + +(* Theorem: GITSET g (x INSERT s) b = GITSET g (REST (x INSERT s)) ((CHOICE (x INSERT s)) * b) *) +(* Proof: + By GITSET_THM, since x INSERT s is non-empty. +*) +val GITSET_INSERT = save_thm( + "GITSET_INSERT", + gINST (ITSET_INSERT |> SPEC_ALL |> UNDISCH) |> DISCH_ALL |> GEN_ALL); +(* > val GITSET_INSERT = + |- !s. FINITE s ==> !x g b. (GITSET g (x INSERT s) b = GITSET g (REST (x INSERT s)) (CHOICE (x INSERT s) * b)) : thm +*) + +(* Theorem: [simplified GITSET_INSERT] + FINITE s /\ s <> {} ==> GITSET g s b = GITSET g (REST s) ((CHOICE s) * b) *) +(* Proof: + Replace (x INSERT s) in GITSET_INSERT by s, + GITSET g s b = GITSET g (REST s) ((CHOICE s) * b) + Since CHOICE s IN s by CHOICE_DEF + so (CHOICE s) INSERT s = s by ABSORPTION + and the result follows. +*) +val GITSET_PROPERTY = store_thm( + "GITSET_PROPERTY", + ``!g s. FINITE s /\ s <> {} ==> !b. GITSET g s b = GITSET g (REST s) ((CHOICE s) * b)``, + metis_tac[CHOICE_DEF, ABSORPTION, GITSET_INSERT]); + +(* Theorem: AbelianMonoid g ==> closure_comm_assoc_fun g.op G *) +(* Proof: + Note Monoid g /\ !x y::(G). x * y = y * x by AbelianMonoid_def + and !x y z::(G). x * y * z = y * x * z by monoid_assoc, above gives commutativity + Thus closure_comm_assoc_fun g.op G by closure_comm_assoc_fun_def +*) +val abelian_monoid_op_closure_comm_assoc_fun = store_thm( + "abelian_monoid_op_closure_comm_assoc_fun", + ``!g:'a monoid. AbelianMonoid g ==> closure_comm_assoc_fun g.op G``, + rw[AbelianMonoid_def, closure_comm_assoc_fun_def] >> + metis_tac[monoid_assoc]); + +(* Theorem: AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::(G). GITSET g (x INSERT s) b = GITSET g (s DELETE x) (x * b) *) +(* Proof: + Note closure_comm_assoc_fun g.op G by abelian_monoid_op_closure_comm_assoc_fun + The result follows by SUBSET_COMMUTING_ITSET_INSERT +*) +val COMMUTING_GITSET_INSERT = store_thm( + "COMMUTING_GITSET_INSERT", + ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::(G). GITSET g (x INSERT s) b = GITSET g (s DELETE x) (x * b)``, + metis_tac[abelian_monoid_op_closure_comm_assoc_fun, SUBSET_COMMUTING_ITSET_INSERT]); + +(* Theorem: AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::(G). GITSET g s (x * b) = x * (GITSET g s b) *) +(* Proof: + Note closure_comm_assoc_fun g.op G by abelian_monoid_op_closure_comm_assoc_fun + The result follows by SUBSET_COMMUTING_ITSET_REDUCTION +*) +val COMMUTING_GITSET_REDUCTION = store_thm( + "COMMUTING_GITSET_REDUCTION", + ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::(G). GITSET g s (x * b) = x * (GITSET g s b)``, + metis_tac[abelian_monoid_op_closure_comm_assoc_fun, SUBSET_COMMUTING_ITSET_REDUCTION]); + +(* Theorem: AbelianMonoid g ==> GITSET g (x INSERT s) b = x * (GITSET g (s DELETE x) b) *) +(* Proof: + Note closure_comm_assoc_fun g.op G by abelian_monoid_op_closure_comm_assoc_fun + The result follows by SUBSET_COMMUTING_ITSET_RECURSES +*) +val COMMUTING_GITSET_RECURSES = store_thm( + "COMMUTING_GITSET_RECURSES", + ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + !b x::(G). GITSET g (x INSERT s) b = x * (GITSET g (s DELETE x) b)``, + metis_tac[abelian_monoid_op_closure_comm_assoc_fun, SUBSET_COMMUTING_ITSET_RECURSES]); + +(* ------------------------------------------------------------------------- *) +(* Abelian Monoid PROD_SET *) +(* ------------------------------------------------------------------------- *) + +(* Define GPROD_SET via GITSET *) +val GPROD_SET_def = Define `GPROD_SET g s = GITSET g s #e`; + +(* Theorem: property of GPROD_SET *) +(* Proof: + This is to prove: + (1) GITSET g {} #e = #e + True by GITSET_EMPTY, and monoid_id_element. + (2) GITSET g (x INSERT s) #e = x * GITSET g (s DELETE x) #e + True by COMMUTING_GITSET_RECURSES, and monoid_id_element. +*) +val GPROD_SET_THM = store_thm( + "GPROD_SET_THM", + ``!g s. (GPROD_SET g {} = #e) /\ + (AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> + (!x::(G). GPROD_SET g (x INSERT s) = x * GPROD_SET g (s DELETE x)))``, + rw[GPROD_SET_def, RES_FORALL_THM, GITSET_EMPTY] >> + `Monoid g` by metis_tac[AbelianMonoid_def] >> + metis_tac[COMMUTING_GITSET_RECURSES, monoid_id_element]); + +(* Theorem: GPROD_SET g {} = #e *) +(* Proof: + GPROD_SET g {} + = GITSET g {} #e by GPROD_SET_def + = #e by GITSET_EMPTY + or directly by GPROD_SET_THM +*) +val GPROD_SET_EMPTY = store_thm( + "GPROD_SET_EMPTY", + ``!g s. GPROD_SET g {} = #e``, + rw[GPROD_SET_def, GITSET_EMPTY]); + +(* Theorem: Monoid g ==> !x. x IN G ==> (GPROD_SET g {x} = x) *) +(* Proof: + GPROD_SET g {x} + = GITSET g {x} #e by GPROD_SET_def + = x * #e by ITSET_SING + = x by monoid_rid +*) +val GPROD_SET_SING = store_thm( + "GPROD_SET_SING", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> (GPROD_SET g {x} = x)``, + rw[GPROD_SET_def, ITSET_SING]); + +(* +> ITSET_SING |> SPEC_ALL |> INST_TYPE[beta |-> alpha] |> Q.INST[`f` |-> `g.op`] |> GEN_ALL; +val it = |- !x g b. GITSET g {x} b = x * b: thm +> ITSET_SING |> SPEC_ALL |> INST_TYPE[beta |-> alpha] |> Q.INST[`f` |-> `g.op`] |> Q.INST[`b` |-> `#e`] |> REWRITE_RULE[GSYM GPROD_SET_def]; +val it = |- GPROD_SET g {x} = x * #e: thm +*) + +(* Theorem: GPROD_SET g s IN G *) +(* Proof: + By complete induction on CARD s. + Case s = {}, + Then GPROD_SET g {} = #e by GPROD_SET_EMPTY + and #e IN G by monoid_id_element + Case s <> {}, + Let x = CHOICE s, t = REST s, s = x INSERT t, x NOTIN t. + GPROD_SET g s + = GPROD_SET g (x INSERT t) by s = x INSERT t + = x * GPROD_SET g (t DELETE x) by GPROD_SET_THM + = x * GPROD_SET g t by DELETE_NON_ELEMENT, x NOTIN t + Hence GPROD_SET g s IN G by induction, and monoid_op_element. +*) +val GPROD_SET_PROPERTY = store_thm( + "GPROD_SET_PROPERTY", + ``!(g:'a monoid) s. AbelianMonoid g /\ FINITE s /\ s SUBSET G ==> GPROD_SET g s IN G``, + completeInduct_on `CARD s` >> + pop_assum (assume_tac o SIMP_RULE bool_ss[GSYM RIGHT_FORALL_IMP_THM, AND_IMP_INTRO]) >> + rpt strip_tac >> + `Monoid g` by metis_tac[AbelianMonoid_def] >> + Cases_on `s = {}` >- + rw[GPROD_SET_EMPTY] >> + `?x t. (x = CHOICE s) /\ (t = REST s) /\ (s = x INSERT t)` by rw[CHOICE_INSERT_REST] >> + `x IN G` by metis_tac[CHOICE_DEF, SUBSET_DEF] >> + `t SUBSET G /\ FINITE t` by metis_tac[REST_SUBSET, SUBSET_TRANS, SUBSET_FINITE] >> + `x NOTIN t` by metis_tac[CHOICE_NOT_IN_REST] >> + `CARD t < CARD s` by rw[] >> + metis_tac[GPROD_SET_THM, DELETE_NON_ELEMENT, monoid_op_element]); + +(* ---------------------------------------------------------------------- + monoid extension + + lifting a monoid so that its carrier is the whole of the type but the + op is the same on the old carrier set. + ---------------------------------------------------------------------- *) + +Definition extend_def: + extend m = <| carrier := UNIV; id := m.id; + op := λx y. if x IN m.carrier then + if y IN m.carrier then m.op x y else y + else x |> +End + +Theorem extend_is_monoid[simp]: + !m. Monoid m ==> Monoid (extend m) +Proof + simp[extend_def, EQ_IMP_THM, Monoid_def] >> rw[] >> rw[] >> + gvs[] +QED + +Theorem extend_carrier[simp]: + (extend m).carrier = UNIV +Proof + simp[extend_def] +QED + +Theorem extend_id[simp]: + (extend m).id = m.id +Proof + simp[extend_def] +QED + +Theorem extend_op: + x IN m.carrier /\ y IN m.carrier ==> (extend m).op x y = m.op x y +Proof + simp[extend_def] +QED + +(* + +Monoid Order +============ +This is an investigation of the equation x ** n = #e. +Given x IN G, x ** 0 = #e by monoid_exp_0 +But for those having non-trivial n with x ** n = #e, +the least value of n is called the order for the element x. +This is an important property for the element x, +especiallly later for Finite Group. + +Monoid Invertibles +================== +In a monoid M, not all elements are invertible. +But for those elements that are invertible, +they have interesting properties. +Indeed, being invertible, an operation .inv or |/ +can be defined through Skolemization, and later, +the Monoid Invertibles will be shown to be a Group. + +*) + +(* ------------------------------------------------------------------------- *) +(* Monoid Order and Invertibles Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: + ord x = order g x + maximal_order g = MAX_SET (IMAGE ord G) + G* = monoid_invertibles g + reciprocal x = monoid_inv g x + |/ = reciprocal +*) +(* Definitions and Theorems (# are exported): + + Definitions: + period_def |- !g x k. period g x k <=> 0 < k /\ (x ** k = #e) + order_def |- !g x. ord x = case OLEAST k. period g x k of NONE => 0 | SOME k => k + order_alt |- !g x. ord x = case OLEAST k. 0 < k /\ x ** k = #e of NONE => 0 | SOME k => k + order_property |- !g x. x ** ord x = #e + order_period |- !g x. 0 < ord x ==> period g x (ord x) + order_minimal |- !g x n. 0 < n /\ n < ord x ==> x ** n <> #e + order_eq_0 |- !g x. (ord x = 0) <=> !n. 0 < n ==> x ** n <> #e + order_thm |- !g x n. 0 < n ==> + ((ord x = n) <=> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e) + +# monoid_order_id |- !g. Monoid g ==> (ord #e = 1) + monoid_order_eq_1 |- !g. Monoid g ==> !x. x IN G ==> ((ord x = 1) <=> (x = #e)) + monoid_order_condition |- !g. Monoid g ==> !x. x IN G ==> !m. (x ** m = #e) <=> ord x divides m + monoid_order_divides_exp|- !g. Monoid g ==> !x n. x IN G ==> ((x ** n = #e) <=> ord x divides n) + monoid_order_power_eq_0 |- !g. Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) = 0) <=> 0 < k /\ (ord x = 0) + monoid_order_power |- !g. Monoid g ==> !x. x IN G ==> !k. ord (x ** k) * gcd (ord x) k = ord x + monoid_order_power_eqn |- !g. Monoid g ==> !x k. x IN G /\ 0 < k ==> (ord (x ** k) = ord x DIV gcd k (ord x)) + monoid_order_power_coprime |- !g. Monoid g ==> !x. x IN G ==> + !n. coprime n (ord x) ==> (ord (x ** n) = ord x) + monoid_order_cofactor |- !g. Monoid g ==> !x n. x IN G /\ 0 < ord x /\ n divides (ord x) ==> + (ord (x ** (ord x DIV n)) = n) + monoid_order_divisor |- !g. Monoid g ==> !x m. x IN G /\ 0 < ord x /\ m divides (ord x) ==> + ?y. y IN G /\ (ord y = m) + monoid_order_common |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> + ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) + monoid_order_common_coprime |- !g. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) /\ + coprime (ord x) (ord y) ==> ?z. z IN G /\ (ord z = ord x * ord y) + monoid_exp_mod_order |- !g. Monoid g ==> !x. x IN G /\ 0 < ord x ==> !n. x ** n = x ** (n MOD ord x) + abelian_monoid_order_common |- !g. AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> + ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) + abelian_monoid_order_common_coprime + |- !g. AbelianMonoid g ==> !x y. x IN G /\ y IN G /\ + coprime (ord x) (ord y) ==> ?z. z IN G /\ (ord z = ord x * ord y) + abelian_monoid_order_lcm |- !g. AbelianMonoid g ==> + !x y. x IN G /\ y IN G ==> ?z. z IN G /\ (ord z = lcm (ord x) (ord y)) + + Orders of elements: + orders_def |- !g n. orders g n = {x | x IN G /\ (ord x = n)} + orders_element |- !g x n. x IN orders g n <=> x IN G /\ (ord x = n) + orders_subset |- !g n. orders g n SUBSET G + orders_finite |- !g. FINITE G ==> !n. FINITE (orders g n) + orders_eq_1 |- !g. Monoid g ==> (orders g 1 = {#e}) + + Maximal Order: + maximal_order_alt |- !g. maximal_order g = MAX_SET {ord z | z | z IN G} + monoid_order_divides_maximal |- !g. FiniteAbelianMonoid g ==> + !x. x IN G /\ 0 < ord x ==> ord x divides maximal_order g + + Monoid Invertibles: + monoid_invertibles_def |- !g. G* = {x | x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e)} + monoid_invertibles_element |- !g x. x IN G* <=> x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e) + monoid_order_nonzero |- !g x. Monoid g /\ x IN G /\ 0 < ord x ==> x IN G* + + Invertibles_def |- !g. Invertibles g = <|carrier := G*; op := $*; id := #e|> + Invertibles_property |- !g. ((Invertibles g).carrier = G* ) /\ + ((Invertibles g).op = $* ) /\ + ((Invertibles g).id = #e) /\ + ((Invertibles g).exp = $** ) + Invertibles_carrier |- !g. (Invertibles g).carrier = G* + Invertibles_subset |- !g. (Invertibles g).carrier SUBSET G + Invertibles_order |- !g x. order (Invertibles g) x = ord x + + Monoid Inverse as an operation: + monoid_inv_def |- !g x. Monoid g /\ x IN G* ==> |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) + monoid_inv_def_alt |- !g. Monoid g ==> !x. x IN G* <=> + x IN G /\ |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) + monoid_inv_element |- !g. Monoid g ==> !x. x IN G* ==> x IN G +# monoid_id_invertible |- !g. Monoid g ==> #e IN G* +# monoid_inv_op_invertible |- !g. Monoid g ==> !x y. x IN G* /\ y IN G* ==> x * y IN G* +# monoid_inv_invertible |- !g. Monoid g ==> !x. x IN G* ==> |/ x IN G* + monoid_invertibles_is_monoid |- !g. Monoid g ==> Monoid (Invertibles g) + +*) + +(* ------------------------------------------------------------------------- *) +(* Monoid Order Definition. *) +(* ------------------------------------------------------------------------- *) + +(* Define order = optional LEAST period for an element x in Group g *) +val period_def = zDefine` + period (g:'a monoid) (x:'a) k <=> 0 < k /\ (x ** k = #e) +`; +val order_def = zDefine` + order (g:'a monoid) (x:'a) = case OLEAST k. period g x k of + NONE => 0 + | SOME k => k +`; +(* use zDefine here since these are not computationally effective. *) + +(* Expand order_def with period_def. *) +val order_alt = save_thm( + "order_alt", REWRITE_RULE [period_def] order_def); +(* val order_alt = + |- !g x. order g x = + case OLEAST k. 0 < k /\ x ** k = #e of NONE => 0 | SOME k => k: thm *) + +(* overloading on Monoid Order *) +val _ = overload_on ("ord", ``order g``); + +(* Theorem: (x ** (ord x) = #e *) +(* Proof: by definition, and x ** 0 = #e by monoid_exp_0. *) +val order_property = store_thm( + "order_property", + ``!g:'a monoid. !x:'a. (x ** (ord x) = #e)``, + ntac 2 strip_tac >> + simp_tac std_ss[order_def, period_def] >> + DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> + rw[]); + +(* Theorem: 0 < (ord x) ==> period g x (ord x) *) +(* Proof: by order_property, period_def. *) +val order_period = store_thm( + "order_period", + ``!g:'a monoid x:'a. 0 < (ord x) ==> period g x (ord x)``, + rw[order_property, period_def]); + +(* Theorem: !n. 0 < n /\ n < (ord x) ==> x ** n <> #e *) +(* Proof: by definition of OLEAST. *) +Theorem order_minimal: + !g:'a monoid x:'a. !n. 0 < n /\ n < ord x ==> x ** n <> #e +Proof + ntac 3 strip_tac >> + simp_tac std_ss[order_def, period_def] >> + DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> + rw_tac std_ss[] >> + metis_tac[DECIDE “~(0 < 0)”] +QED + +(* Theorem: (ord x = 0) <=> !n. 0 < n ==> x ** n <> #e *) +(* Proof: + Expand by order_def, period_def, this is to show: + (1) 0 < n /\ (!n. ~(0 < n) \/ x ** n <> #e) ==> x ** n <> #e + True by assertion. + (2) 0 < n /\ x ** n = #e /\ (!m. m < 0 ==> ~(0 < m) \/ x ** m <> #e) ==> (n = 0) <=> !n. 0 < n ==> x ** n <> #e + True by assertion. +*) +Theorem order_eq_0: + !g:'a monoid x. ord x = 0 <=> !n. 0 < n ==> x ** n <> #e +Proof + ntac 2 strip_tac >> + simp_tac std_ss[order_def, period_def] >> + DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> + rw_tac std_ss[] >> + metis_tac[DECIDE “~(0 < 0)”] +QED + +val std_ss = std_ss -* ["NOT_LT_ZERO_EQ_ZERO"] + +(* Theorem: 0 < n ==> ((ord x = n) <=> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e) *) +(* Proof: + If part: (ord x = n) ==> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e + This is to show: + (1) (ord x = n) ==> (x ** n = #e), true by order_property + (2) (ord x = n) ==> !m. 0 < m /\ m < n ==> x ** m <> #e, true by order_minimal + Only-if part: (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e ==> (ord x = n) + Expanding by order_def, period_def, this is to show: + (1) 0 < n /\ x ** n = #e /\ !n'. ~(0 < n') \/ x ** n' <> #e ==> 0 = n + Putting n' = n, the assumption is contradictory. + (2) 0 < n /\ 0 < n' /\ x ** n = #e /\ x ** n' = #e /\ ... ==> n' = n + The assumptions implies ~(n' < n), and ~(n < n'), hence n' = n. +*) +val order_thm = store_thm( + "order_thm", + ``!g:'a monoid x:'a. !n. 0 < n ==> + ((ord x = n) <=> (x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e)``, + rw[EQ_IMP_THM] >- + rw[order_property] >- + rw[order_minimal] >> + simp_tac std_ss[order_def, period_def] >> + DEEP_INTRO_TAC whileTheory.OLEAST_INTRO >> + rw_tac std_ss[] >- + metis_tac[] >> + `~(n' < n)` by metis_tac[] >> + `~(n < n')` by metis_tac[] >> + decide_tac); + +(* Theorem: Monoid g ==> (ord #e = 1) *) +(* Proof: + Since #e IN G by monoid_id_element + and #e ** 1 = #e by monoid_exp_1 + Obviously, 0 < 1 and there is no m such that 0 < m < 1 + hence true by order_thm +*) +val monoid_order_id = store_thm( + "monoid_order_id", + ``!g:'a monoid. Monoid g ==> (ord #e = 1)``, + rw[order_thm, DECIDE``!m . ~(0 < m /\ m < 1)``]); + +(* export simple result *) +val _ = export_rewrites ["monoid_order_id"]; + +(* Theorem: Monoid g ==> !x. x IN G ==> ((ord x = 1) <=> (x = #e)) *) +(* Proof: + If part: ord x = 1 ==> x = #e + Since x ** (ord x) = #e by order_property + ==> x ** 1 = #e by given + ==> x = #e by monoid_exp_1 + Only-if part: x = #e ==> ord x = 1 + i.e. to show ord #e = 1. + True by monoid_order_id. +*) +val monoid_order_eq_1 = store_thm( + "monoid_order_eq_1", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> ((ord x = 1) <=> (x = #e))``, + rw[EQ_IMP_THM] >> + `#e = x ** (ord x)` by rw[order_property] >> + rw[]); + +(* Theorem: Monoid g ==> !x. x IN G ==> !m. (x ** m = #e) <=> (ord x) divides m *) +(* Proof: + (ord x) is a period, and so divides all periods. + Let n = ord x. + If part: x^m = #e ==> n divides m + If n = 0, m = 0 by order_eq_0 + Hence true by ZERO_DIVIDES + If n <> 0, + By division algorithm, m = q * n + t for some q, t and t < n. + #e = x^m + = x^(q * n + t) + = (x^n)^q * x^t + = #e * x^t + Thus x^t = #e, but t < n. + If 0 < t, this contradicts order_minimal. + Hence t = 0, or n divides m. + Only-if part: n divides m ==> x^m = #e + By divides_def, ?k. m = k * n + x^m = x^(k * n) = (x^n)^k = #e^k = #e. +*) +val monoid_order_condition = store_thm( + "monoid_order_condition", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !m. (x ** m = #e) <=> (ord x) divides m``, + rpt strip_tac >> + qabbrev_tac `n = ord x` >> + rw[EQ_IMP_THM] >| [ + Cases_on `n = 0` >| [ + `~(0 < m)` by metis_tac[order_eq_0] >> + `m = 0` by decide_tac >> + rw[ZERO_DIVIDES], + `x ** n = #e` by rw[order_property, Abbr`n`] >> + `0 < n` by decide_tac >> + `?q t. (m = q * n + t) /\ t < n` by metis_tac[DIVISION] >> + `x ** m = x ** (n * q + t)` by metis_tac[MULT_COMM] >> + `_ = (x ** (n * q)) * (x ** t)` by rw[] >> + `_ = ((x ** n) ** q) * (x ** t)` by rw[] >> + `_ = x ** t` by rw[] >> + `~(0 < t)` by metis_tac[order_minimal] >> + `t = 0` by decide_tac >> + `m = q * n` by rw[] >> + metis_tac[divides_def] + ], + `x ** n = #e` by rw[order_property, Abbr`n`] >> + `?k. m = k * n` by rw[GSYM divides_def] >> + `x ** m = x ** (n * k)` by metis_tac[MULT_COMM] >> + `_ = (x ** n) ** k` by rw[] >> + rw[] + ]); + +(* Theorem: Monoid g ==> !x n. x IN G ==> (x ** n = #e <=> ord x divides n) *) +(* Proof: by monoid_order_condition *) +val monoid_order_divides_exp = store_thm( + "monoid_order_divides_exp", + ``!g:'a monoid. Monoid g ==> !x n. x IN G ==> ((x ** n = #e) <=> ord x divides n)``, + rw[monoid_order_condition]); + +(* Theorem: Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) = 0) <=> 0 < k /\ (ord x = 0) *) +(* Proof: + By order_eq_0, this is to show: + (1) !n. 0 < n ==> (x ** k) ** n <> #e ==> 0 < k + By contradiction. Assume k = 0. + Then x ** k = #e by monoid_exp_0 + and #e ** n = #e by monoid_id_exp + This contradicts the implication: (x ** k) ** n <> #e. + (2) 0 < n /\ !n. 0 < n ==> (x ** k) ** n <> #e ==> x ** n <> #e + By contradiction. Assume x ** n = #e. + Now, (x ** k) ** n + = x ** (k * n) by monoid_exp_mult + = x ** (n * k) by MULT_COMM + = (x ** n) * k by monoid_exp_mult + = #e ** k by x ** n = #e + = #e by monoid_id_exp + This contradicts the implication: (x ** k) ** n <> #e. + (3) 0 < n /\ !n. 0 < n ==> x ** n <> #e ==> (x ** k) ** n <> #e + By contradiction. Assume (x ** k) ** n = #e. + 0 < k /\ 0 < n ==> 0 < k * n by arithmetic + But (x ** n) ** k = x ** (n * k) by monoid_exp_mult + This contradicts the implication: (x ** k) ** n <> #e. +*) +val monoid_order_power_eq_0 = store_thm( + "monoid_order_power_eq_0", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) = 0) <=> 0 < k /\ (ord x = 0)``, + rw[order_eq_0, EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `k = 0` by decide_tac >> + `x ** k = #e` by rw[monoid_exp_0] >> + metis_tac[monoid_id_exp, DECIDE``0 < 1``], + metis_tac[monoid_exp_mult, MULT_COMM, monoid_id_exp], + `0 < k * n` by rw[LESS_MULT2] >> + metis_tac[monoid_exp_mult] + ]); + +(* Theorem: ord (x ** k) = ord x / gcd(ord x, k) + Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) * (gcd (ord x) k) = ord x) *) +(* Proof: + Let n = ord x, m = ord (x^k), d = gcd(n,k). + This is to show: m = n / d. + If k = 0, + m = ord (x^0) = ord #e = 1 by monoid_order_id + d = gcd(n,0) = n by GCD_0R + henc true. + If k <> 0, + First, show ord (x^k) = m divides n/d. + If n = 0, m = 0 by monoid_order_power_eq_0 + so ord (x^k) = m | (n/d) by ZERO_DIVIDES + If n <> 0, + (x^k)^(n/d) = x^(k * n/d) = x^(n * k/d) = (x^n)^(k/d) = #e, + so ord (x^k) = m | (n/d) by monoid_order_condition. + Second, show (n/d) divides m = ord (x^k), or equivalently: n divides d * m + x^(k * m) = (x^k)^m = #e = x^n, + so ord x = n | k * m by monoid_order_condition + Since d = gcd(k,n), there are integers a and b such that + ka + nb = d by LINEAR_GCD + Multiply by m: k * m * a + n * m * b = d * m. + But since n | k * m, it follows that n | d*m, + i.e. (n/d) | m by DIVIDES_CANCEL. + By DIVIDES_ANTISYM, ord (x^k) = m = n/d. +*) +val monoid_order_power = store_thm( + "monoid_order_power", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !k. (ord (x ** k) * (gcd (ord x) k) = ord x)``, + rpt strip_tac >> + qabbrev_tac `n = ord x` >> + qabbrev_tac `m = ord (x ** k)` >> + qabbrev_tac `d = gcd n k` >> + Cases_on `k = 0` >| [ + `d = n` by metis_tac[GCD_0R] >> + rw[Abbr`m`], + Cases_on `n = 0` >| [ + `0 < k` by decide_tac >> + `m = 0` by rw[monoid_order_power_eq_0, Abbr`n`, Abbr`m`] >> + rw[], + `x ** n = #e` by rw[order_property, Abbr`n`] >> + `0 < n /\ 0 < k` by decide_tac >> + `?p q. (n = p * d) /\ (k = q * d)` by metis_tac[FACTOR_OUT_GCD] >> + `k * p = n * q` by rw_tac arith_ss[] >> + `(x ** k) ** p = x ** (k * p)` by rw[] >> + `_ = x ** (n * q)` by metis_tac[] >> + `_ = (x ** n) ** q` by rw[] >> + `_ = #e` by rw[] >> + `m divides p` by rw[GSYM monoid_order_condition, Abbr`m`] >> + `x ** (m * k) = x ** (k * m)` by metis_tac[MULT_COMM] >> + `_ = (x ** k) ** m` by rw[] >> + `_ = #e` by rw[order_property, Abbr`m`] >> + `n divides (m * k)` by rw[GSYM monoid_order_condition, Abbr`n`, Abbr`m`] >> + `?u v. u * k = v * n + d` by rw[LINEAR_GCD, Abbr`d`] >> + `m * k * u = m * (u * k)` by rw_tac arith_ss[] >> + `_ = m * (v * n) + m * d` by metis_tac[LEFT_ADD_DISTRIB] >> + `_ = m * v * n + m * d` by rw_tac arith_ss[] >> + `n divides (m * k * u)` by metis_tac[DIVIDES_MULT] >> + `n divides (m * v * n)` by metis_tac[divides_def] >> + `n divides (m * d)` by metis_tac[DIVIDES_ADD_2] >> + `d <> 0` by metis_tac[MULT_EQ_0] >> + `0 < d` by decide_tac >> + `p divides m` by metis_tac[DIVIDES_CANCEL] >> + metis_tac[DIVIDES_ANTISYM] + ] + ]); + +(* Theorem: Monoid g ==> + !x k. x IN G /\ 0 < k ==> (ord (x ** k) = (ord x) DIV (gcd k (ord x))) *) +(* Proof: + Note ord (x ** k) * gcd k (ord x) = ord x by monoid_order_power, GCD_SYM + and 0 < gcd k (ord x) by GCD_EQ_0, 0 < k + ==> ord (x ** k) = (ord x) DIV (gcd k (ord x)) by MULT_EQ_DIV +*) +val monoid_order_power_eqn = store_thm( + "monoid_order_power_eqn", + ``!g:'a monoid. Monoid g ==> + !x k. x IN G /\ 0 < k ==> (ord (x ** k) = (ord x) DIV (gcd k (ord x)))``, + rpt strip_tac >> + `ord (x ** k) * gcd k (ord x) = ord x` by metis_tac[monoid_order_power, GCD_SYM] >> + `0 < gcd k (ord x)` by metis_tac[GCD_EQ_0, NOT_ZERO] >> + fs[MULT_EQ_DIV]); + +(* Theorem: Monoid g ==> !x. x IN G ==> !n. coprime n (ord x) ==> (ord (x ** n) = ord x) *) +(* Proof: + ord x + = ord (x ** n) * gcd (ord x) n by monoid_order_power + = ord (x ** n) * 1 by coprime_sym + = ord (x ** n) by MULT_RIGHT_1 +*) +val monoid_order_power_coprime = store_thm( + "monoid_order_power_coprime", + ``!g:'a monoid. Monoid g ==> !x. x IN G ==> !n. coprime n (ord x) ==> (ord (x ** n) = ord x)``, + metis_tac[monoid_order_power, coprime_sym, MULT_RIGHT_1]); + +(* Theorem: Monoid g ==> + !x n. x IN G /\ 0 < ord x /\ n divides ord x ==> (ord (x ** (ord x DIV n)) = n) *) +(* Proof: + Let m = ord x, k = m DIV n. + Since 0 < m, n <> 0, or 0 < n by ZERO_DIVIDES + Since n divides m, m = k * n by DIVIDES_EQN + Hence k divides m by divisors_def, MULT_COMM + and k <> 0 by MULT, m <> 0 + and gcd k m = k by divides_iff_gcd_fix + Now ord (x ** k) * k + = m by monoid_order_power + = k * n by above + = n * k by MULT_COMM + Hence ord (x ** k) = n by MULT_RIGHT_CANCEL, k <> 0 +*) +val monoid_order_cofactor = store_thm( + "monoid_order_cofactor", + ``!g: 'a monoid. Monoid g ==> + !x n. x IN G /\ 0 < ord x /\ n divides ord x ==> (ord (x ** (ord x DIV n)) = n)``, + rpt strip_tac >> + qabbrev_tac `m = ord x` >> + qabbrev_tac `k = m DIV n` >> + `0 < n` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + `m = k * n` by rw[GSYM DIVIDES_EQN, Abbr`k`] >> + `k divides m` by metis_tac[divides_def, MULT_COMM] >> + `k <> 0` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> + `gcd k m = k` by rw[GSYM divides_iff_gcd_fix] >> + metis_tac[monoid_order_power, GCD_SYM, MULT_COMM, MULT_RIGHT_CANCEL]); + +(* Theorem: If x IN G with ord x = n > 0, and m divides n, then G contains an element of order m. *) +(* Proof: + m divides n ==> n = k * m for some k, by divides_def. + Then x^k has order m: + (x^k)^m = x^(k * m) = x^n = #e + and for any h < m, + if (x^k)^h = x^(k * h) = #e means x has order k * h < k * m = n, + which is a contradiction with order_minimal. +*) +val monoid_order_divisor = store_thm( + "monoid_order_divisor", + ``!g:'a monoid. Monoid g ==> + !x m. x IN G /\ 0 < ord x /\ m divides (ord x) ==> ?y. y IN G /\ (ord y = m)``, + rpt strip_tac >> + `ord x <> 0` by decide_tac >> + `m <> 0` by metis_tac[ZERO_DIVIDES] >> + `0 < m` by decide_tac >> + `?k. ord x = k * m` by rw[GSYM divides_def] >> + qexists_tac `x ** k` >> + rw[] >> + `x ** (ord x) = #e` by rw[order_property] >> + `(x ** k) ** m = #e` by metis_tac[monoid_exp_mult] >> + `(!h. 0 < h /\ h < m ==> (x ** k) ** h <> #e)` suffices_by metis_tac[order_thm] >> + rpt strip_tac >> + `h <> 0` by decide_tac >> + `k <> 0 /\ k * h <> 0` by metis_tac[MULT, MULT_EQ_0] >> + `0 < k /\ 0 < k * h` by decide_tac >> + `k * h < k * m` by metis_tac[LT_MULT_LCANCEL] >> + `(x ** k) ** h = x ** (k * h)` by rw[] >> + metis_tac[order_minimal]); + +(* Theorem: If x * y = y * x, and n = ord x, m = ord y, + then there exists z IN G such that ord z = (lcm n m) / (gcd n m) *) +(* Proof: + Let n = ord x, m = ord y, d = gcd(n, m). + This is to show: ?z. z IN G /\ (ord z * d = n * m) + If n = 0, take z = x, by LCM_0. + If m = 0, take z = y, by LCM_0. + If n <> 0 and m <> 0, + First, get a pair with coprime orders. + ?p q. (n = p * d) /\ (m = q * d) /\ coprime p q by FACTOR_OUT_GCD + Let u = x^d, v = y^d + then ord u = ord (x^d) = ord x / gcd(n, d) = n/d = p by monoid_order_power + and ord v = ord (y^d) = ord y / gcd(m, d) = m/d = q by monoid_order_power + Now gcd(p,q) = 1, and there exists integers a and b such that + a * p + b * q = 1 by LINEAR_GCD + Let w = u^b * v^a + Then w^p = (u^b * v^a)^p + = (u^b)^p * (v^a)^p by monoid_comm_op_exp + = (u^p)^b * (v^a)^p by monoid_exp_mult_comm + = #e^b * v^(a * p) by p = ord u + = v^(a * p) by monoid_id_exp + = v^(1 - b * q) by LINEAR_GCD condition + = v^1 * |/ v^(b * q) by variant of monoid_exp_add + = v * 1/ (v^q)^b by monoid_exp_mult_comm + = v * 1/ #e^b by q = ord v + = v + Hence ord (w^p) = ord v = q, + Let c = ord w, c <> 0 since p * q <> 0 by GCD_0L + then q = ord (w^p) = c / gcd(c,p) by monoid_order_power + i.e. q * gcd(c,p) = c, or q divides c + Similarly, w^q = u, and p * gcd(c,q) = c, or p divides c. + Since coprime p q, p * q divides c, an order of element w IN G. + Hence there is some z in G such that ord z = p * q by monoid_order_divisor. + i.e. ord z = lcm p q = lcm (n/d) (m/d) = (lcm n m) / d. +*) +val monoid_order_common = store_thm( + "monoid_order_common", + ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) ==> + ?z. z IN G /\ ((ord z) * gcd (ord x) (ord y) = lcm (ord x) (ord y))``, + rpt strip_tac >> + qabbrev_tac `n = ord x` >> + qabbrev_tac `m = ord y` >> + qabbrev_tac `d = gcd n m` >> + Cases_on `n = 0` >- + metis_tac[LCM_0, MULT_EQ_0] >> + Cases_on `m = 0` >- + metis_tac[LCM_0, MULT_EQ_0] >> + `x ** n = #e` by rw[order_property, Abbr`n`] >> + `y ** m = #e` by rw[order_property, Abbr`m`] >> + `d <> 0` by rw[GCD_EQ_0, Abbr`d`] >> + `?p q. (n = p * d) /\ (m = q * d) /\ coprime p q` by rw[FACTOR_OUT_GCD, Abbr`d`] >> + qabbrev_tac `u = x ** d` >> + qabbrev_tac `v = y ** d` >> + `u IN G /\ v IN G` by rw[Abbr`u`, Abbr`v`] >> + `(gcd n d = d) /\ (gcd m d = d)` by rw[GCD_GCD, GCD_SYM, Abbr`d`] >> + `ord u = p` by metis_tac[monoid_order_power, MULT_RIGHT_CANCEL] >> + `ord v = q` by metis_tac[monoid_order_power, MULT_RIGHT_CANCEL] >> + `p <> 0 /\ q <> 0` by metis_tac[MULT_EQ_0] >> + `?a b. a * q = b * p + 1` by metis_tac[LINEAR_GCD] >> + `?h k. h * p = k * q + 1` by metis_tac[LINEAR_GCD, GCD_SYM] >> + qabbrev_tac `ua = u ** a` >> + qabbrev_tac `vh = v ** h` >> + qabbrev_tac `w = ua * vh` >> + `ua IN G /\ vh IN G /\ w IN G` by rw[Abbr`ua`, Abbr`vh`, Abbr`w`] >> + `ua * vh = (x ** d) ** a * (y ** d) ** h` by rw[] >> + `_ = x ** (d * a) * y ** (d * h)` by rw_tac std_ss[GSYM monoid_exp_mult] >> + `_ = y ** (d * h) * x ** (d * a)` by metis_tac[monoid_comm_exp_exp] >> + `_ = vh * ua` by rw[] >> + `w ** p = (ua * vh) ** p` by rw[] >> + `_ = ua ** p * vh ** p` by metis_tac[monoid_comm_op_exp] >> + `_ = (u ** p) ** a * (v ** h) ** p` by rw[monoid_exp_mult_comm] >> + `_ = #e ** a * v ** (h * p)` by rw[order_property] >> + `_ = v ** (h * p)` by rw[] >> + `_ = v ** (k * q + 1)` by rw_tac std_ss[] >> + `_ = v ** (k * q) * v` by rw[] >> + `_ = v ** (q * k) * v` by rw_tac std_ss[MULT_COMM] >> + `_ = (v ** q) ** k * v` by rw[] >> + `_ = #e ** k * v` by rw[order_property] >> + `_ = v` by rw[] >> + `w ** q = (ua * vh) ** q` by rw[] >> + `_ = ua ** q * vh ** q` by metis_tac[monoid_comm_op_exp] >> + `_ = (u ** a) ** q * (v ** q) ** h` by rw[monoid_exp_mult_comm] >> + `_ = u ** (a * q) * #e ** h` by rw[order_property] >> + `_ = u ** (a * q)` by rw[] >> + `_ = u ** (b * p + 1)` by rw_tac std_ss[] >> + `_ = u ** (b * p) * u` by rw[] >> + `_ = u ** (p * b) * u` by rw_tac std_ss[MULT_COMM] >> + `_ = (u ** p) ** b * u` by rw[] >> + `_ = #e ** b * u` by rw[order_property] >> + `_ = u` by rw[] >> + qabbrev_tac `c = ord w` >> + `q * gcd c p = c` by rw[monoid_order_power, Abbr`c`] >> + `p * gcd c q = c` by metis_tac[monoid_order_power] >> + `p divides c /\ q divides c` by metis_tac[divides_def, MULT_COMM] >> + `lcm p q = p * q` by rw[LCM_COPRIME] >> + `(p * q) divides c` by metis_tac[LCM_IS_LEAST_COMMON_MULTIPLE] >> + `p * q <> 0` by rw[MULT_EQ_0] >> + `c <> 0` by metis_tac[GCD_0L] >> + `0 < c` by decide_tac >> + `?z. z IN G /\ (ord z = p * q)` by metis_tac[monoid_order_divisor] >> + `ord z * d = d * (p * q)` by rw_tac arith_ss[] >> + `_ = lcm (d * p) (d * q)` by rw[LCM_COMMON_FACTOR] >> + `_ = lcm n m` by metis_tac[MULT_COMM] >> + metis_tac[]); + +(* This is a milestone. *) + +(* Theorem: If x * y = y * x, and n = ord x, m = ord y, and gcd n m = 1, + then there exists z IN G with ord z = (lcm n m) *) +(* Proof: + By monoid_order_common and gcd n m = 1. +*) +val monoid_order_common_coprime = store_thm( + "monoid_order_common_coprime", + ``!g:'a monoid. Monoid g ==> !x y. x IN G /\ y IN G /\ (x * y = y * x) /\ coprime (ord x) (ord y) ==> + ?z. z IN G /\ (ord z = (ord x) * (ord y))``, + metis_tac[monoid_order_common, GCD_LCM, MULT_RIGHT_1, MULT_LEFT_1]); +(* This version can be proved directly using previous technique, then derive the general case: + Let ord x = n, ord y = m. + Let d = gcd(n,m) p = n/d, q = m/d, gcd(p,q) = 1. + By p | n = ord x, there is u with ord u = p by monoid_order_divisor + By q | m = ord y, there is v with ord v = q by monoid_order_divisor + By gcd(ord u, ord v) = gcd(p,q) = 1, + there is z with ord z = lcm(p,q) = p * q = n/d * m/d = lcm(n,m)/gcd(n,m). +*) + +(* Theorem: Monoid g ==> !x. x IN G /\ 0 < ord x ==> !n. x ** n = x ** (n MOD (ord x)) *) +(* Proof: + Let z = ord x, 0 < z by given + Note n = (n DIV z) * z + (n MOD z) by DIVISION, 0 < z. + x ** n + = x ** ((n DIV z) * z + (n MOD z)) by above + = x ** ((n DIV z) * z) * x ** (n MOD z) by monoid_exp_add + = x ** (z * (n DIV z)) * x ** (n MOD z) by MULT_COMM + = (x ** z) ** (n DIV z) * x ** (n MOD z) by monoid_exp_mult + = #e ** (n DIV 2) * x ** (n MOD z) by order_property + = #e * x ** (n MOD z) by monoid_id_exp + = x ** (n MOD z) by monoid_lid +*) +val monoid_exp_mod_order = store_thm( + "monoid_exp_mod_order", + ``!g:'a monoid. Monoid g ==> !x. x IN G /\ 0 < ord x ==> !n. x ** n = x ** (n MOD (ord x))``, + rpt strip_tac >> + qabbrev_tac `z = ord x` >> + `x ** n = x ** ((n DIV z) * z + (n MOD z))` by metis_tac[DIVISION] >> + `_ = x ** ((n DIV z) * z) * x ** (n MOD z)` by rw[monoid_exp_add] >> + `_ = x ** (z * (n DIV z)) * x ** (n MOD z)` by metis_tac[MULT_COMM] >> + rw[monoid_exp_mult, order_property, Abbr`z`]); + +(* Theorem: AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> + ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) *) +(* Proof: by AbelianMonoid_def, monoid_order_common *) +val abelian_monoid_order_common = store_thm( + "abelian_monoid_order_common", + ``!g:'a monoid. AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> + ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y))``, + rw[AbelianMonoid_def, monoid_order_common]); + +(* Theorem: AbelianMonoid g ==> !x y. x IN G /\ y IN G /\ coprime (ord x) (ord y) ==> + ?z. z IN G /\ (ord z = ord x * ord y) *) +(* Proof: by AbelianMonoid_def, monoid_order_common_coprime *) +val abelian_monoid_order_common_coprime = store_thm( + "abelian_monoid_order_common_coprime", + ``!g:'a monoid. AbelianMonoid g ==> !x y. x IN G /\ y IN G /\ coprime (ord x) (ord y) ==> + ?z. z IN G /\ (ord z = ord x * ord y)``, + rw[AbelianMonoid_def, monoid_order_common_coprime]); + +(* Theorem: AbelianMonoid g ==> + !x y. x IN G /\ y IN G ==> ?z. z IN G /\ (ord z = lcm (ord x) (ord y)) *) +(* Proof: + If ord x = 0, + Then lcm 0 (ord y) = 0 = ord x by LCM_0 + Thus take z = x. + If ord y = 0 + lcm (ord x) 0 = 0 = ord y by LCM_0 + Thus take z = y. + Otherwise, 0 < ord x /\ 0 < ord y. + Let m = ord x, n = ord y. + Note ?a b p q. (lcm m n = p * q) /\ coprime p q /\ + (m = a * p) /\ (n = b * q) by lcm_gcd_park_decompose + Thus p divides m /\ q divides n by divides_def + ==> ?u. u IN G /\ (ord u = p) by monoid_order_divisor, p divides m + and ?v. v IN G /\ (ord v = q) by monoid_order_divisor, q divides n + ==> ?z. z IN G /\ (ord z = p * q) by monoid_order_common_coprime, coprime p q + or z IN G /\ (ord z = lcm m n) by above +*) +val abelian_monoid_order_lcm = store_thm( + "abelian_monoid_order_lcm", + ``!g:'a monoid. AbelianMonoid g ==> + !x y. x IN G /\ y IN G ==> ?z. z IN G /\ (ord z = lcm (ord x) (ord y))``, + rw[AbelianMonoid_def] >> + qabbrev_tac `m = ord x` >> + qabbrev_tac `n = ord y` >> + Cases_on `(m = 0) \/ (n = 0)` >- + metis_tac[LCM_0] >> + `0 < m /\ 0 < n` by decide_tac >> + `?a b p q. (lcm m n = p * q) /\ coprime p q /\ (m = a * p) /\ (n = b * q)` by metis_tac[lcm_gcd_park_decompose] >> + `p divides m /\ q divides n` by metis_tac[divides_def] >> + `?u. u IN G /\ (ord u = p)` by metis_tac[monoid_order_divisor] >> + `?v. v IN G /\ (ord v = q)` by metis_tac[monoid_order_divisor] >> + `?z. z IN G /\ (ord z = p * q)` by rw[monoid_order_common_coprime] >> + metis_tac[]); + +(* This is much better than: +abelian_monoid_order_common +|- !g. AbelianMonoid g ==> !x y. x IN G /\ y IN G ==> + ?z. z IN G /\ (ord z * gcd (ord x) (ord y) = lcm (ord x) (ord y)) +*) + +(* ------------------------------------------------------------------------- *) +(* Orders of elements *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of elements with a given order *) +val orders_def = Define ` + orders (g:'a monoid) n = {x | x IN G /\ (ord x = n)} +`; + +(* Theorem: !x n. x IN orders g n <=> x IN G /\ (ord x = n) *) +(* Proof: by orders_def *) +val orders_element = store_thm( + "orders_element", + ``!g:'a monoid. !x n. x IN orders g n <=> x IN G /\ (ord x = n)``, + rw[orders_def]); + +(* Theorem: !n. (orders g n) SUBSET G *) +(* Proof: by orders_def, SUBSET_DEF *) +val orders_subset = store_thm( + "orders_subset", + ``!g:'a monoid. !n. (orders g n) SUBSET G``, + rw[orders_def, SUBSET_DEF]); + +(* Theorem: FINITE G ==> !n. FINITE (orders g n) *) +(* Proof: by orders_subset, SUBSET_FINITE *) +val orders_finite = store_thm( + "orders_finite", + ``!g:'a monoid. FINITE G ==> !n. FINITE (orders g n)``, + metis_tac[orders_subset, SUBSET_FINITE]); + +(* Theorem: Monoid g ==> (orders g 1 = {#e}) *) +(* Proof: + orders g 1 + = {x | x IN G /\ (ord x = 1)} by orders_def + = {x | x IN G /\ (x = #e)} by monoid_order_eq_1 + = {#e} by monoid_id_elelment +*) +val orders_eq_1 = store_thm( + "orders_eq_1", + ``!g:'a monoid. Monoid g ==> (orders g 1 = {#e})``, + rw[orders_def, EXTENSION, EQ_IMP_THM, GSYM monoid_order_eq_1]); + +(* ------------------------------------------------------------------------- *) +(* Maximal Order *) +(* ------------------------------------------------------------------------- *) + +(* Overload maximal_order of a group *) +val _ = overload_on("maximal_order", ``\g:'a monoid. MAX_SET (IMAGE ord G)``); + +(* Theorem: maximal_order g = MAX_SET {ord z | z | z IN G} *) +(* Proof: by IN_IMAGE *) +val maximal_order_alt = store_thm( + "maximal_order_alt", + ``!g:'a monoid. maximal_order g = MAX_SET {ord z | z | z IN G}``, + rpt strip_tac >> + `IMAGE ord G = {ord z | z | z IN G}` by rw[EXTENSION] >> + rw[]); + +(* Theorem: In an Abelian Monoid, every nonzero order divides the maximal order. + FiniteAbelianMonoid g ==> !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g) *) +(* Proof: + Let m = maximal_order g = MAX_SET {ord x | x IN G} + Choose z IN G so that ord z = m. + Pick x IN G so that ord x = n. Question: will n divide m ? + + We have: ord x = n, ord z = m bigger. + Let d = gcd(n,m), a = n/d, b = m/d. + Since a | n = ord x, there is ord xa = a + Since b | m = ord y, there is ord xb = b + and gcd(a,b) = 1 by FACTOR_OUT_GCD + + If gcd(a,m) <> 1, let prime p divides gcd(a,m) by PRIME_FACTOR + + Since gcd(a,m) | a and gcd(a,m) divides m, + prime p | a, p | m = b * d, a product. + When prime p divides (b * d), p | b or p | d by P_EUCLIDES + But gcd(a,b)=1, they don't share any common factor, so p | a ==> p not divide b. + If p not divide b, so p | d. + But d | n, d | m, so p | n and p | m. + + Let p^i | n for some max i, mi = MAX_SET {i | p^i divides n}, p^mi | n ==> n = nq * p^mi + and p^j | m for some max j, mj = MAX_SET {j | p^j divides m), p^mj | m ==> m = mq * p^mj + If i <= j, + ppppp | n ppppppp | m + d should picks up all i of the p's, leaving a = n/d with no p, p cannot divide a. + But p | a, so i > j, but this will derive a contradiction: + pppppp | n pppp | m + d picks up j of the p's + Let u = p^i (all prime p in n), v = m/p^j (no prime p) + u | n, so there is ord x = u = p^i u = p^mi + v | m, so there is ord x = v = m/p^j v = m/p^mj + gcd(u,v)=1, since u is pure prime p, v has no prime p (possible gcd = 1, p, p^2, etc.) + So there is ord z = u * v = p^i * m /p^j = m * p^(i-j) .... > m, a contradiction! + + This case is impossible for the max order suitation. + + So gcd(a,m) = 1, there is ord z = a * m = n * m /d + But n * m /d <= m, since m is maximal + i.e. n <= d + But d | n, d <= n, + Hence n = d = gcd(m,n), apply divides_iff_gcd_fix: n divides m. +*) +val monoid_order_divides_maximal = store_thm( + "monoid_order_divides_maximal", + ``!g:'a monoid. FiniteAbelianMonoid g ==> + !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g)``, + rw[FiniteAbelianMonoid_def, AbelianMonoid_def] >> + qabbrev_tac `s = IMAGE ord G` >> + qabbrev_tac `m = MAX_SET s` >> + qabbrev_tac `n = ord x` >> + `#e IN G /\ (ord #e = 1)` by rw[] >> + `s <> {}` by metis_tac[IN_IMAGE, MEMBER_NOT_EMPTY] >> + `FINITE s` by metis_tac[IMAGE_FINITE] >> + `m IN s /\ !y. y IN s ==> y <= m` by rw[MAX_SET_DEF, Abbr`m`] >> + `?z. z IN G /\ (ord z = m)` by metis_tac[IN_IMAGE] >> + `!z. 0 < z <=> z <> 0` by decide_tac >> + `1 <= m` by metis_tac[in_max_set, IN_IMAGE] >> + `0 < m` by decide_tac >> + `?a b. (n = a * gcd n m) /\ (m = b * gcd n m) /\ coprime a b` by metis_tac[FACTOR_OUT_GCD] >> + qabbrev_tac `d = gcd n m` >> + `a divides n /\ b divides m` by metis_tac[divides_def, MULT_COMM] >> + `?xa. xa IN G /\ (ord xa = a)` by metis_tac[monoid_order_divisor] >> + `?xb. xb IN G /\ (ord xb = b)` by metis_tac[monoid_order_divisor] >> + Cases_on `coprime a m` >| [ + `?xc. xc IN G /\ (ord xc = a * m)` by metis_tac[monoid_order_common_coprime] >> + `a * m <= m` by metis_tac[IN_IMAGE] >> + `n * m = d * (a * m)` by rw_tac arith_ss[] >> + `n <= d` by metis_tac[LE_MULT_LCANCEL, LE_MULT_RCANCEL] >> + `d <= n` by metis_tac[GCD_DIVIDES, DIVIDES_MOD_0, DIVIDES_LE] >> + `n = d` by decide_tac >> + metis_tac [divides_iff_gcd_fix], + qabbrev_tac `q = gcd a m` >> + `?p. prime p /\ p divides q` by rw[PRIME_FACTOR] >> + `0 < a` by metis_tac[MULT] >> + `q divides a /\ q divides m` by metis_tac[GCD_DIVIDES, DIVIDES_MOD_0] >> + `p divides a /\ p divides m` by metis_tac[DIVIDES_TRANS] >> + `p divides b \/ p divides d` by metis_tac[P_EUCLIDES] >| [ + `p divides 1` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR, MULT] >> + metis_tac[DIVIDES_ONE, NOT_PRIME_1], + `d divides n` by metis_tac[divides_def] >> + `p divides n` by metis_tac[DIVIDES_TRANS] >> + `?i. 0 < i /\ (p ** i) divides n /\ !k. coprime (p ** k) (n DIV p ** i)` by rw[FACTOR_OUT_PRIME] >> + `?j. 0 < j /\ (p ** j) divides m /\ !k. coprime (p ** k) (m DIV p ** j)` by rw[FACTOR_OUT_PRIME] >> + Cases_on `i > j` >| [ + qabbrev_tac `u = p ** i` >> + qabbrev_tac `v = m DIV p ** j` >> + `0 < p` by metis_tac[PRIME_POS] >> + `v divides m` by metis_tac[DIVIDES_COFACTOR, EXP_EQ_0] >> + `?xu. xu IN G /\ (ord xu = u)` by metis_tac[monoid_order_divisor] >> + `?xv. xv IN G /\ (ord xv = v)` by metis_tac[monoid_order_divisor] >> + `coprime u v` by rw[Abbr`u`] >> + `?xz. xz IN G /\ (ord xz = u * v)` by rw[monoid_order_common_coprime] >> + `m = (p ** j) * v` by metis_tac[DIVIDES_FACTORS, EXP_EQ_0] >> + `p ** (i - j) * m = p ** (i - j) * (p ** j) * v` by rw_tac arith_ss[] >> + `j <= i` by decide_tac >> + `p ** (i - j) * (p ** j) = p ** (i - j + j)` by rw[EXP_ADD] >> + `_ = p ** i` by rw[SUB_ADD] >> + `p ** (i - j) * m = u * v` by rw_tac std_ss[Abbr`u`] >> + `0 < i - j` by decide_tac >> + `1 < p ** (i - j)` by rw[ONE_LT_EXP, ONE_LT_PRIME] >> + `m < p ** (i - j) * m` by rw[LT_MULT_RCANCEL] >> + `m < u * v` by metis_tac[] >> + `u * v > m` by decide_tac >> + `u * v <= m` by metis_tac[IN_IMAGE] >> + metis_tac[NOT_GREATER], + `i <= j` by decide_tac >> + `0 < p` by metis_tac[PRIME_POS] >> + `p ** i <> 0 /\ p ** j <> 0` by metis_tac[EXP_EQ_0] >> + `n = (p ** i) * (n DIV p ** i)` by metis_tac[DIVIDES_FACTORS] >> + `m = (p ** j) * (m DIV p ** j)` by metis_tac[DIVIDES_FACTORS] >> + `p ** (j - i) * (p ** i) = p ** (j - i + i)` by rw[EXP_ADD] >> + `_ = p ** j` by rw[SUB_ADD] >> + `m = p ** (j - i) * (p ** i) * (m DIV p ** j)` by rw_tac std_ss[] >> + `_ = (p ** i) * (p ** (j - i) * (m DIV p ** j))` by rw_tac arith_ss[] >> + qabbrev_tac `u = p ** i` >> + qabbrev_tac `v = n DIV u` >> + `u divides m` by metis_tac[divides_def, MULT_COMM] >> + `u divides d` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] >> + `?c. d = c * u` by metis_tac[divides_def] >> + `n = (a * c) * u` by rw_tac arith_ss[] >> + `v = c * a` by metis_tac[MULT_RIGHT_CANCEL, MULT_COMM] >> + `a divides v` by metis_tac[divides_def] >> + `p divides v` by metis_tac[DIVIDES_TRANS] >> + `p divides u` by metis_tac[DIVIDES_EXP_BASE, DIVIDES_REFL] >> + `d <> 0` by metis_tac[MULT_0] >> + `c <> 0` by metis_tac[MULT] >> + `v <> 0` by metis_tac[MULT_EQ_0] >> + `p divides (gcd v u)` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] >> + `coprime u v` by metis_tac[] >> + metis_tac[GCD_SYM, DIVIDES_ONE, NOT_PRIME_1] + ] + ] + ]); + +(* This is a milestone theorem. *) + +(* Another proof based on the following: + +The Multiplicative Group of a Finite Field (Ryan Vinroot) +http://www.math.wm.edu/~vinroot/430S13MultFiniteField.pdf + +*) + +(* Theorem: FiniteAbelianMonoid g ==> + !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g) *) +(* Proof: + Note AbelianMonoid g /\ FINITE G by FiniteAbelianMonoid_def + Let ord z = m = maximal_order g, attained by some z IN G. + Let ord x = n, and n <= m since m is maximal_order, so 0 < m. + Then x IN G /\ z IN G + ==> ?y. y IN G /\ ord y = lcm n m by abelian_monoid_order_lcm + Note lcm n m <= m by m is maximal_order + but m <= lcm n m by LCM_LE, lcm is a common multiple + ==> lcm n m = m by EQ_LESS_EQ + or n divides m by divides_iff_lcm_fix +*) +Theorem monoid_order_divides_maximal[allow_rebind]: + !g:'a monoid. + FiniteAbelianMonoid g ==> + !x. x IN G /\ 0 < ord x ==> (ord x) divides (maximal_order g) +Proof + rw[FiniteAbelianMonoid_def] >> + ‘Monoid g’ by metis_tac[AbelianMonoid_def] >> + qabbrev_tac ‘s = IMAGE ord G’ >> + qabbrev_tac ‘m = MAX_SET s’ >> + qabbrev_tac ‘n = ord x’ >> + ‘#e IN G /\ (ord #e = 1)’ by rw[] >> + ‘s <> {}’ by metis_tac[IN_IMAGE, MEMBER_NOT_EMPTY] >> + ‘FINITE s’ by rw[Abbr‘s’] >> + ‘m IN s /\ !y. y IN s ==> y <= m’ by rw[MAX_SET_DEF, Abbr‘m’] >> + ‘?z. z IN G /\ (ord z = m)’ by metis_tac[IN_IMAGE] >> + ‘?y. y IN G /\ (ord y = lcm n m)’ by metis_tac[abelian_monoid_order_lcm] >> + ‘n IN s /\ ord y IN s’ by rw[Abbr‘s’, Abbr‘n’] >> + ‘n <= m /\ lcm n m <= m’ by metis_tac[] >> + ‘0 < m’ by decide_tac >> + ‘m <= lcm n m’ by rw[LCM_LE] >> + rw[divides_iff_lcm_fix] +QED + +(* ------------------------------------------------------------------------- *) +(* Monoid Invertibles *) +(* ------------------------------------------------------------------------- *) + +(* The Invertibles are those with inverses. *) +val monoid_invertibles_def = Define` + monoid_invertibles (g:'a monoid) = + { x | x IN G /\ (?y. y IN G /\ (x * y = #e) /\ (y * x = #e)) } +`; +val _ = overload_on ("G*", ``monoid_invertibles g``); + +(* Theorem: x IN G* <=> x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e) *) +(* Proof: by monoid_invertibles_def. *) +val monoid_invertibles_element = store_thm( + "monoid_invertibles_element", + ``!g:'a monoid x. x IN G* <=> x IN G /\ ?y. y IN G /\ (x * y = #e) /\ (y * x = #e)``, + rw[monoid_invertibles_def]); + +(* Theorem: Monoid g /\ x IN G /\ 0 < ord x ==> x IN G* *) +(* Proof: + By monoid_invertibles_def, this is to show: + ?y. y IN G /\ (x * y = #e) /\ (y * x = #e) + Since x ** (ord x) = #e by order_property + and ord x = SUC n by ord x <> 0 + Now, x ** SUC n = x * x ** n by monoid_exp_SUC + x ** SUC n = x ** n * x by monoid_exp_suc + and x ** n IN G by monoid_exp_element + Hence taking y = x ** n will satisfy the requirements. +*) +val monoid_order_nonzero = store_thm( + "monoid_order_nonzero", + ``!g:'a monoid x. Monoid g /\ x IN G /\ 0 < ord x ==> x IN G*``, + rw[monoid_invertibles_def] >> + `x ** (ord x) = #e` by rw[order_property] >> + `ord x <> 0` by decide_tac >> + metis_tac[num_CASES, monoid_exp_SUC, monoid_exp_suc, monoid_exp_element]); + +(* The Invertibles of a monoid, will be a Group. *) +val Invertibles_def = Define` + Invertibles (g:'a monoid) : 'a monoid = + <| carrier := G*; + op := g.op; + id := g.id + |> +`; +(* +- type_of ``Invertibles g``; +> val it = ``:'a moniod`` : hol_type +*) + +(* Theorem: properties of Invertibles *) +(* Proof: by Invertibles_def. *) +val Invertibles_property = store_thm( + "Invertibles_property", + ``!g:'a monoid. ((Invertibles g).carrier = G*) /\ + ((Invertibles g).op = g.op) /\ + ((Invertibles g).id = #e) /\ + ((Invertibles g).exp = g.exp)``, + rw[Invertibles_def, monoid_exp_def, FUN_EQ_THM]); + +(* Theorem: (Invertibles g).carrier = monoid_invertibles g *) +(* Proof: by Invertibles_def. *) +val Invertibles_carrier = store_thm( + "Invertibles_carrier", + ``!g:'a monoid. (Invertibles g).carrier = monoid_invertibles g``, + rw[Invertibles_def]); + +(* Theorem: (Invertibles g).carrier SUBSET G *) +(* Proof: + (Invertibles g).carrier + = G* by Invertibles_def + = {x | x IN G /\ ... } by monoid_invertibles_def + SUBSET G by SUBSET_DEF +*) +val Invertibles_subset = store_thm( + "Invertibles_subset", + ``!g:'a monoid. (Invertibles g).carrier SUBSET G``, + rw[Invertibles_def, monoid_invertibles_def, SUBSET_DEF]); + +(* Theorem: order (Invertibles g) x = order g x *) +(* Proof: order_def, period_def, Invertibles_property *) +val Invertibles_order = store_thm( + "Invertibles_order", + ``!g:'a monoid. !x. order (Invertibles g) x = order g x``, + rw[order_def, period_def, Invertibles_property]); + +(* ------------------------------------------------------------------------- *) +(* Monoid Inverse as an operation *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: x IN G* means inverse of x exists. *) +(* Proof: by definition of G*. *) +val monoid_inv_from_invertibles = store_thm( + "monoid_inv_from_invertibles", + ``!g:'a monoid. Monoid g ==> !x. x IN G* ==> ?y. y IN G /\ (x * y = #e) /\ (y * x = #e)``, + rw[monoid_invertibles_def]); + +(* Convert this into the form: !g x. ?y. ..... for SKOLEM_THM *) +val lemma = prove( + ``!(g:'a monoid) x. ?y. Monoid g /\ x IN G* ==> y IN G /\ (x * y = #e) /\ (y * x = #e)``, + metis_tac[monoid_inv_from_invertibles]); + +(* Convert this into the form: !g x. ?y. ..... for SKOLEM_THM + + NOTE: added ‘(Monoid g /\ x NOTIN G* ==> y = ARB)’ to make it a total function. +val lemma = prove( + “!(g:'a monoid) x. + ?y. (Monoid g /\ x IN G* ==> y IN G /\ (x * y = #e) /\ (y * x = #e)) /\ + (Monoid g /\ x NOTIN G* ==> y = ARB)”, + rpt GEN_TAC + >> MP_TAC (Q.SPEC ‘g’ monoid_inv_from_invertibles) + >> Cases_on ‘Monoid g’ >> rw [] + >> Cases_on ‘x IN G*’ >> rw []); + *) + +(* Use Skolemization to generate the monoid_inv_from_invertibles function *) +val monoid_inv_def = new_specification( + "monoid_inv_def", ["monoid_inv"], (* name of function *) + SIMP_RULE (srw_ss()) [SKOLEM_THM] lemma); +(* |- !g x. Monoid g /\ x IN G* ==> + monoid_inv g x IN G /\ (x * monoid_inv g x = #e) /\ + (monoid_inv g x * x = #e) *) +(* +- type_of ``monoid_inv g``; +> val it = ``:'a -> 'a`` : hol_type +*) + +(* Convert inv function to inv field, i.e. m.inv is defined to be monoid_inv. *) +val _ = add_record_field ("inv", ``monoid_inv``); +(* +- type_of ``g.inv``; +> val it = ``:'a -> 'a`` : hol_type +*) +(* val _ = overload_on ("|/", ``g.inv``); *) (* for non-unicode input *) + +(* for unicode dispaly *) +val _ = add_rule{fixity = Suffix 2100, + term_name = "reciprocal", + block_style = (AroundEachPhrase, (PP.CONSISTENT, 0)), + paren_style = OnlyIfNecessary, + pp_elements = [TOK (UnicodeChars.sup_minus ^ UnicodeChars.sup_1)]}; +val _ = overload_on("reciprocal", ``monoid_inv g``); +val _ = overload_on ("|/", ``reciprocal``); (* for non-unicode input *) + +(* This means: reciprocal will have the display $^{-1}$, and here reciprocal is + short-name for monoid_inv g *) + +(* - monoid_inv_def; +> val it = |- !g x. Monoid g /\ x IN G* ==> |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) : thm +*) + +(* Theorem: x IN G* <=> x IN G /\ |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) *) +(* Proof: by definition. *) +val monoid_inv_def_alt = store_thm( + "monoid_inv_def_alt", + ``!g:'a monoid. Monoid g ==> (!x. x IN G* <=> x IN G /\ |/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e))``, + rw[monoid_invertibles_def, monoid_inv_def, EQ_IMP_THM] >> + metis_tac[]); + +(* In preparation for: The invertibles of a monoid form a group. *) + +(* Theorem: x IN G* ==> x IN G *) +(* Proof: by definition of G*. *) +val monoid_inv_element = store_thm( + "monoid_inv_element", + ``!g:'a monoid. Monoid g ==> !x. x IN G* ==> x IN G``, + rw[monoid_invertibles_def]); + +(* This export will cause rewrites of RHS = x IN G to become proving LHS = x IN G*, which is not useful. *) +(* val _ = export_rewrites ["monoid_inv_element"]; *) + +(* Theorem: #e IN G* *) +(* Proof: by monoid_id and definition. *) +val monoid_id_invertible = store_thm( + "monoid_id_invertible", + ``!g:'a monoid. Monoid g ==> #e IN G*``, + rw[monoid_invertibles_def] >> + qexists_tac `#e` >> + rw[]); + +val _ = export_rewrites ["monoid_id_invertible"]; + +(* This is a direct proof, next one is shorter. *) + +(* Theorem: [Closure for Invertibles] x, y IN G* ==> x * y IN G* *) +(* Proof: inverse of (x * y) = (inverse of y) * (inverse of x) + Note x IN G* ==> + |/x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e) by monoid_inv_def + y IN G* ==> + |/y IN G /\ (y * |/ y = #e) /\ ( |/ y * y = #e) by monoid_inv_def + Now x * y IN G and | /y * | / x IN G by monoid_op_element + and (x * y) * ( |/ y * |/ x) = #e by monoid_assoc, monoid_lid + also ( |/ y * |/ x) * (x * y) = #e by monoid_assoc, monoid_lid + Thus x * y IN G*, with ( |/ y * |/ x) as its inverse. +*) +val monoid_inv_op_invertible = store_thm( + "monoid_inv_op_invertible", + ``!g:'a monoid. Monoid g ==> !x y. x IN G* /\ y IN G* ==> x * y IN G*``, + rpt strip_tac>> + `x IN G /\ y IN G` by rw_tac std_ss[monoid_inv_element] >> + `|/ x IN G /\ (x * |/ x = #e) /\ ( |/ x * x = #e)` by rw_tac std_ss[monoid_inv_def] >> + `|/ y IN G /\ (y * |/ y = #e) /\ ( |/ y * y = #e)` by rw_tac std_ss[monoid_inv_def] >> + `x * y IN G /\ |/ y * |/ x IN G` by rw_tac std_ss[monoid_op_element] >> + `(x * y) * ( |/ y * |/ x) = x * ((y * |/ y) * |/ x)` by rw_tac std_ss[monoid_assoc, monoid_op_element] >> + `( |/ y * |/ x) * (x * y) = |/ y * (( |/ x * x) * y)` by rw_tac std_ss[monoid_assoc, monoid_op_element] >> + rw_tac std_ss[monoid_invertibles_def, GSPECIFICATION] >> + metis_tac[monoid_lid]); + +(* Better proof of the same theorem. *) + +(* Theorem: [Closure for Invertibles] x, y IN G* ==> x * y IN G* *) +(* Proof: inverse of (x * y) = (inverse of y) * (inverse of x) *) +Theorem monoid_inv_op_invertible[allow_rebind,simp]: + !g:'a monoid. Monoid g ==> !x y. x IN G* /\ y IN G* ==> x * y IN G* +Proof + rw[monoid_invertibles_def] >> + qexists_tac `y'' * y'` >> + rw_tac std_ss[monoid_op_element] >| [ + `x * y * (y'' * y') = x * ((y * y'') * y')` by rw[monoid_assoc], + `y'' * y' * (x * y) = y'' * ((y' * x) * y)` by rw[monoid_assoc] + ] >> rw_tac std_ss[monoid_lid] +QED + +(* Theorem: x IN G* ==> |/ x IN G* *) +(* Proof: by monoid_inv_def. *) +val monoid_inv_invertible = store_thm( + "monoid_inv_invertible", + ``!g:'a monoid. Monoid g ==> !x. x IN G* ==> |/ x IN G*``, + rpt strip_tac >> + rw[monoid_invertibles_def] >- + rw[monoid_inv_def] >> + metis_tac[monoid_inv_def, monoid_inv_element]); + +val _ = export_rewrites ["monoid_inv_invertible"]; + +(* Theorem: The Invertibles of a monoid form a monoid. *) +(* Proof: by checking definition. *) +val monoid_invertibles_is_monoid = store_thm( + "monoid_invertibles_is_monoid", + ``!g. Monoid g ==> Monoid (Invertibles g)``, + rpt strip_tac >> + `!x. x IN G* ==> x IN G` by rw[monoid_inv_element] >> + rw[Invertibles_def] >> + rewrite_tac[Monoid_def] >> + rw[monoid_assoc]); + +(* ------------------------------------------------------------------------- *) +(* Monoid Maps Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: + H = h.carrier + o = binary operation of homo_monoid: (homo_monoid g f).op + #i = identity of homo_monoid: (homo_monoid g f).id + fG = carrier of homo_monoid: (homo_monoid g f).carrier +*) +(* Definitions and Theorems (# are exported): + + Homomorphism, isomorphism, endomorphism, automorphism and submonoid: + MonoidHomo_def |- !f g h. MonoidHomo f g h <=> + (!x. x IN G ==> f x IN h.carrier) /\ (f #e = h.id) /\ + !x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y)) + MonoidIso_def |- !f g h. MonoidIso f g h <=> MonoidHomo f g h /\ BIJ f G h.carrier + MonoidEndo_def |- !f g. MonoidEndo f g <=> MonoidHomo f g g + MonoidAuto_def |- !f g. MonoidAuto f g <=> MonoidIso f g g + submonoid_def |- !h g. submonoid h g <=> MonoidHomo I h g + + Monoid Homomorphisms: + monoid_homo_id |- !f g h. MonoidHomo f g h ==> (f #e = h.id) + monoid_homo_element |- !f g h. MonoidHomo f g h ==> !x. x IN G ==> f x IN h.carrier + monoid_homo_cong |- !g h f1 f2. Monoid g /\ Monoid h /\ (!x. x IN G ==> (f1 x = f2 x)) ==> + (MonoidHomo f1 g h <=> MonoidHomo f2 g h) + monoid_homo_I_refl |- !g. MonoidHomo I g g + monoid_homo_trans |- !g h k f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k + monoid_homo_sym |- !g h f. Monoid g /\ MonoidHomo f g h /\ BIJ f G h.carrier ==> MonoidHomo (LINV f G) h g + monoid_homo_compose |- !g h k f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k + monoid_homo_exp |- !g h f. Monoid g /\ MonoidHomo f g h ==> + !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n + + Monoid Isomorphisms: + monoid_iso_property |- !f g h. MonoidIso f g h <=> + MonoidHomo f g h /\ !y. y IN h.carrier ==> ?!x. x IN G /\ (f x = y) + monoid_iso_id |- !f g h. MonoidIso f g h ==> (f #e = h.id) + monoid_iso_element |- !f g h. MonoidIso f g h ==> !x. x IN G ==> f x IN h.carrier + monoid_iso_monoid |- !g h f. Monoid g /\ MonoidIso f g h ==> Monoid h + monoid_iso_I_refl |- !g. MonoidIso I g g + monoid_iso_trans |- !g h k f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k + monoid_iso_sym |- !g h f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g + monoid_iso_compose |- !g h k f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k + monoid_iso_exp |- !g h f. Monoid g /\ MonoidIso f g h ==> + !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n + monoid_iso_linv_iso |- !g h f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g + monoid_iso_bij |- !g h f. MonoidIso f g h ==> BIJ f G h.carrier + monoid_iso_eq_id |- !g h f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> + !x. x IN G ==> ((f x = h.id) <=> (x = #e)) + monoid_iso_order |- !g h f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> + !x. x IN G ==> (order h (f x) = ord x) + monoid_iso_card_eq |- !g h f. MonoidIso f g h /\ FINITE G ==> (CARD G = CARD h.carrier) + + Monoid Automorphisms: + monoid_auto_id |- !f g. MonoidAuto f g ==> (f #e = #e) + monoid_auto_element |- !f g. MonoidAuto f g ==> !x. x IN G ==> f x IN G + monoid_auto_compose |- !g f1 f2. MonoidAuto f1 g /\ MonoidAuto f2 g ==> MonoidAuto (f1 o f2) g + monoid_auto_exp |- !g f. Monoid g /\ MonoidAuto f g ==> + !x. x IN G ==> !n. f (x ** n) = f x ** n + monoid_auto_I |- !g. MonoidAuto I g + monoid_auto_linv_auto|- !g f. Monoid g /\ MonoidAuto f g ==> MonoidAuto (LINV f G) g + monoid_auto_bij |- !g f. MonoidAuto f g ==> f PERMUTES G + monoid_auto_order |- !g f. Monoid g /\ MonoidAuto f g ==> + !x. x IN G ==> (ord (f x) = ord x) + + Submonoids: + submonoid_eqn |- !g h. submonoid h g <=> H SUBSET G /\ + (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ (h.id = #e) + submonoid_subset |- !g h. submonoid h g ==> H SUBSET G + submonoid_homo_homo |- !g h k f. submonoid h g /\ MonoidHomo f g k ==> MonoidHomo f h k + submonoid_refl |- !g. submonoid g g + submonoid_trans |- !g h k. submonoid g h /\ submonoid h k ==> submonoid g k + submonoid_I_antisym |- !g h. submonoid h g /\ submonoid g h ==> MonoidIso I h g + submonoid_carrier_antisym |- !g h. submonoid h g /\ G SUBSET H ==> MonoidIso I h g + submonoid_order_eqn |- !g h. Monoid g /\ Monoid h /\ submonoid h g ==> + !x. x IN H ==> (order h x = ord x) + + Homomorphic Image of Monoid: + image_op_def |- !g f x y. image_op g f x y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)) + image_op_inj |- !g f. INJ f G univ(:'b) ==> + !x y. x IN G /\ y IN G ==> (image_op g f (f x) (f y) = f (x * y)) + homo_monoid_def |- !g f. homo_monoid g f = <|carrier := IMAGE f G; op := image_op g f; id := f #e|> + homo_monoid_property |- !g f. (fG = IMAGE f G) /\ + (!x y. x IN fG /\ y IN fG ==> + (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))) /\ + (#i = f #e) + homo_monoid_element |- !g f x. x IN G ==> f x IN fG + homo_monoid_id |- !g f. f #e = #i + homo_monoid_op_inj |- !g f. INJ f G univ(:'b) ==> !x y. x IN G /\ y IN G ==> (f (x * y) = f x o f y) + homo_monoid_I |- !g. MonoidIso I (homo_group g I) g + + homo_monoid_closure |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> + !x y. x IN fG /\ y IN fG ==> x o y IN fG + homo_monoid_assoc |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> + !x y z. x IN fG /\ y IN fG /\ z IN fG ==> ((x o y) o z = x o y o z) + homo_monoid_id_property |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> #i IN fG /\ + !x. x IN fG ==> (#i o x = x) /\ (x o #i = x) + homo_monoid_comm |- !g f. AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> + !x y. x IN fG /\ y IN fG ==> (x o y = y o x) + homo_monoid_monoid |- !g f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> Monoid (homo_monoid g f) + homo_monoid_abelian_monoid |- !g f. AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> + AbelianMonoid (homo_monoid g f) + homo_monoid_by_inj |- !g f. Monoid g /\ INJ f G univ(:'b) ==> MonoidHomo f g (homo_monoid g f) + + Weaker form of Homomorphic of Monoid and image of identity: + WeakHomo_def |- !f g h. WeakHomo f g h <=> + (!x. x IN G ==> f x IN h.carrier) /\ + !x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y)) + WeakIso_def |- !f g h. WeakIso f g h <=> WeakHomo f g h /\ BIJ f G h.carrier + monoid_weak_iso_id |- !f g h. Monoid g /\ Monoid h /\ WeakIso f g h ==> (f #e = h.id) + + Injective Image of Monoid: + monoid_inj_image_def |- !g f. monoid_inj_image g f = + <|carrier := IMAGE f G; + op := (let t = LINV f G in \x y. f (t x * t y)); + id := f #e + |> + monoid_inj_image_monoid |- !g f. Monoid g /\ INJ f G univ(:'b) ==> Monoid (monoid_inj_image g f) + monoid_inj_image_abelian_monoid |- !g f. AbelianMonoid g /\ INJ f G univ(:'b) ==> AbelianMonoid (monoid_inj_image g f) + monoid_inj_image_monoid_homo |- !g f. INJ f G univ(:'b) ==> MonoidHomo f g (monoid_inj_image g f) +*) + +(* ------------------------------------------------------------------------- *) +(* Homomorphism, isomorphism, endomorphism, automorphism and submonoid. *) +(* ------------------------------------------------------------------------- *) + +(* val _ = overload_on ("H", ``h.carrier``); + +- type_of ``H``; +> val it = ``:'a -> bool`` : hol_type + +then MonoidIso f g h = MonoidHomo f g h /\ BIJ f G H +will make MonoidIso apply only for f: 'a -> 'a. + +will need val _ = overload_on ("H", ``(h:'b monoid).carrier``); +*) + +(* A function f from g to h is a homomorphism if monoid properties are preserved. *) +(* For monoids, need to ensure that identity is preserved, too. See: monoid_weak_iso_id. *) +val MonoidHomo_def = Define` + MonoidHomo (f:'a -> 'b) (g:'a monoid) (h:'b monoid) <=> + (!x. x IN G ==> f x IN h.carrier) /\ + (!x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y))) /\ + (f #e = h.id) +`; +(* +If MonoidHomo_def uses the condition: !x y. f (x * y) = h.op (f x) (f y) +this will mean a corresponding change in GroupHomo_def, but then +in quotientGroup <> will give a goal: +h <= g ==> x * y * H = (x * H) o (y * H) with no qualification on x, y! +*) + +(* A function f from g to h is an isomorphism if f is a bijective homomorphism. *) +val MonoidIso_def = Define` + MonoidIso f g h <=> MonoidHomo f g h /\ BIJ f G h.carrier +`; + +(* A monoid homomorphism from g to g is an endomorphism. *) +val MonoidEndo_def = Define `MonoidEndo f g <=> MonoidHomo f g g`; + +(* A monoid isomorphism from g to g is an automorphism. *) +val MonoidAuto_def = Define `MonoidAuto f g <=> MonoidIso f g g`; + +(* A submonoid h of g if identity is a homomorphism from h to g *) +val submonoid_def = Define `submonoid h g <=> MonoidHomo I h g`; + +(* ------------------------------------------------------------------------- *) +(* Monoid Homomorphisms *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MonoidHomo f g h ==> (f #e = h.id) *) +(* Proof: by MonoidHomo_def. *) +val monoid_homo_id = store_thm( + "monoid_homo_id", + ``!f g h. MonoidHomo f g h ==> (f #e = h.id)``, + rw[MonoidHomo_def]); + +(* Theorem: MonoidHomo f g h ==> !x. x IN G ==> f x IN h.carrier *) +(* Proof: by MonoidHomo_def *) +val monoid_homo_element = store_thm( + "monoid_homo_element", + ``!f g h. MonoidHomo f g h ==> !x. x IN G ==> f x IN h.carrier``, + rw_tac std_ss[MonoidHomo_def]); + +(* Theorem: Monoid g /\ Monoid h /\ (!x. x IN G ==> (f1 x = f2 x)) ==> (MonoidHomo f1 g h = MonoidHomo f2 g h) *) +(* Proof: by MonoidHomo_def, monoid_op_element, monoid_id_element *) +val monoid_homo_cong = store_thm( + "monoid_homo_cong", + ``!g h f1 f2. Monoid g /\ Monoid h /\ (!x. x IN G ==> (f1 x = f2 x)) ==> + (MonoidHomo f1 g h = MonoidHomo f2 g h)``, + rw_tac std_ss[MonoidHomo_def, EQ_IMP_THM] >- + metis_tac[monoid_op_element] >- + metis_tac[monoid_id_element] >- + metis_tac[monoid_op_element] >> + metis_tac[monoid_id_element]); + +(* Theorem: MonoidHomo I g g *) +(* Proof: by MonoidHomo_def. *) +val monoid_homo_I_refl = store_thm( + "monoid_homo_I_refl", + ``!g:'a monoid. MonoidHomo I g g``, + rw[MonoidHomo_def]); + +(* Theorem: MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo f2 o f1 g k *) +(* Proof: true by MonoidHomo_def. *) +val monoid_homo_trans = store_thm( + "monoid_homo_trans", + ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). + !f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k``, + rw[MonoidHomo_def]); + +(* Theorem: Monoid g /\ MonoidHomo f g h /\ BIJ f G h.carrier ==> MonoidHomo (LINV f G) h g *) +(* Proof: + Note BIJ f G h.carrier + ==> BIJ (LINV f G) h.carrier G by BIJ_LINV_BIJ + By MonoidHomo_def, this is to show: + (1) x IN h.carrier ==> LINV f G x IN G + With BIJ (LINV f G) h.carrier G + ==> INJ (LINV f G) h.carrier G by BIJ_DEF + ==> x IN h.carrier ==> LINV f G x IN G by INJ_DEF + (2) x IN h.carrier /\ y IN h.carrier ==> LINV f G (h.op x y) = LINV f G x * LINV f G y + With x IN h.carrier + ==> ?x1. (x = f x1) /\ x1 IN G by BIJ_DEF, SURJ_DEF + With y IN h.carrier + ==> ?y1. (y = f y1) /\ y1 IN G by BIJ_DEF, SURJ_DEF + and x1 * y1 IN G by monoid_op_element + LINV f G (h.op x y) + = LINV f G (f (x1 * y1)) by MonoidHomo_def + = x1 * y1 by BIJ_LINV_THM, x1 * y1 IN G + = (LINV f G (f x1)) * (LINV f G (f y1)) by BIJ_LINV_THM, x1 IN G, y1 IN G + = (LINV f G x) * (LINV f G y) by x = f x1, y = f y1. + (3) LINV f G h.id = #e + Since #e IN G by monoid_id_element + LINV f G h.id + = LINV f G (f #e) by MonoidHomo_def + = #e by BIJ_LINV_THM +*) +val monoid_homo_sym = store_thm( + "monoid_homo_sym", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidHomo f g h /\ BIJ f G h.carrier ==> + MonoidHomo (LINV f G) h g``, + rpt strip_tac >> + `BIJ (LINV f G) h.carrier G` by rw[BIJ_LINV_BIJ] >> + fs[MonoidHomo_def] >> + rpt strip_tac >- + metis_tac[BIJ_DEF, INJ_DEF] >- + (`?x1. (x = f x1) /\ x1 IN G` by metis_tac[BIJ_DEF, SURJ_DEF] >> + `?y1. (y = f y1) /\ y1 IN G` by metis_tac[BIJ_DEF, SURJ_DEF] >> + `g.op x1 y1 IN G` by rw[] >> + metis_tac[BIJ_LINV_THM]) >> + `#e IN G` by rw[] >> + metis_tac[BIJ_LINV_THM]); + +Theorem monoid_homo_sym_any: + Monoid g /\ MonoidHomo f g h /\ + (!x. x IN h.carrier ==> i x IN g.carrier /\ f (i x) = x) /\ + (!x. x IN g.carrier ==> i (f x) = x) + ==> + MonoidHomo i h g +Proof + rpt strip_tac >> fs[MonoidHomo_def] + \\ metis_tac[Monoid_def] +QED + +(* Theorem: MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k *) +(* Proof: by MonoidHomo_def *) +val monoid_homo_compose = store_thm( + "monoid_homo_compose", + ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). + !f1 f2. MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k``, + rw_tac std_ss[MonoidHomo_def]); +(* This is the same as monoid_homo_trans *) + +(* Theorem: Monoid g /\ MonoidHomo f g h ==> !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n *) +(* Proof: + By induction on n. + Base: f (x ** 0) = h.exp (f x) 0 + f (x ** 0) + = f #e by monoid_exp_0 + = h.id by monoid_homo_id + = h.exp (f x) 0 by monoid_exp_0 + Step: f (x ** SUC n) = h.exp (f x) (SUC n) + Note x ** n IN G by monoid_exp_element + f (x ** SUC n) + = f (x * x ** n) by monoid_exp_SUC + = h.op (f x) (f (x ** n)) by MonoidHomo_def + = h.op (f x) (h.exp (f x) n) by induction hypothesis + = h.exp (f x) (SUC n) by monoid_exp_SUC +*) +val monoid_homo_exp = store_thm( + "monoid_homo_exp", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidHomo f g h ==> + !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n``, + rpt strip_tac >> + Induct_on `n` >- + rw[monoid_exp_0, monoid_homo_id] >> + fs[monoid_exp_SUC, MonoidHomo_def]); + +(* ------------------------------------------------------------------------- *) +(* Monoid Isomorphisms *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MonoidIso f g h <=> MonoidHomo f g h /\ (!y. y IN h.carrier ==> ?!x. x IN G /\ (f x = y)) *) +(* Proof: + This is to prove: + (1) BIJ f G H /\ y IN H ==> ?!x. x IN G /\ (f x = y) + true by INJ_DEF and SURJ_DEF. + (2) !y. y IN H /\ MonoidHomo f g h ==> ?!x. x IN G /\ (f x = y) ==> BIJ f G H + true by INJ_DEF and SURJ_DEF, and + x IN G /\ GroupHomo f g h ==> f x IN H by MonoidHomo_def +*) +val monoid_iso_property = store_thm( + "monoid_iso_property", + ``!f g h. MonoidIso f g h <=> MonoidHomo f g h /\ (!y. y IN h.carrier ==> ?!x. x IN G /\ (f x = y))``, + rw_tac std_ss[MonoidIso_def, EQ_IMP_THM] >- + metis_tac[BIJ_THM] >> + rw[BIJ_THM] >> + metis_tac[MonoidHomo_def]); + +(* Note: all these proofs so far do not require the condition: f #e = h.id in MonoidHomo_def, + but evetually it should, as this is included in definitions of all resources. *) + +(* Theorem: MonoidIso f g h ==> (f #e = h.id) *) +(* Proof: by MonoidIso_def, monoid_homo_id. *) +val monoid_iso_id = store_thm( + "monoid_iso_id", + ``!f g h. MonoidIso f g h ==> (f #e = h.id)``, + rw_tac std_ss[MonoidIso_def, monoid_homo_id]); + +(* Theorem: MonoidIso f g h ==> !x. x IN G ==> f x IN h.carrier *) +(* Proof: by MonoidIso_def, monoid_homo_element *) +val monoid_iso_element = store_thm( + "monoid_iso_element", + ``!f g h. MonoidIso f g h ==> !x. x IN G ==> f x IN h.carrier``, + metis_tac[MonoidIso_def, monoid_homo_element]); + +(* Theorem: Monoid g /\ MonoidIso f g h ==> Monoid h *) +(* Proof: + This is to show: + (1) x IN h.carrier /\ y IN h.carrier ==> h.op x y IN h.carrier + Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property + ?y'. y' IN G /\ (f y' = y) by monoid_iso_property + h.op x y = f (x' * y') by MonoidHomo_def + As x' * y' IN G by monoid_op_element + hence f (x' * y') IN h.carrier by MonoidHomo_def + (2) x IN h.carrier /\ y IN h.carrier /\ z IN h.carrier ==> h.op (h.op x y) z = h.op x (h.op y z) + Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property + ?y'. y' IN G /\ (f y' = y) by monoid_iso_property + ?z'. z' IN G /\ (f z' = z) by monoid_iso_property + as x' * y' IN G by monoid_op_element + and f (x' * y') IN h.carrier by MonoidHomo_def + ?!t. t IN G /\ f t = f (x' * y') by monoid_iso_property + i.e. t = x' * y' by uniqueness + hence h.op (h.op x y) z = f (x' * y' * z') by MonoidHomo_def + Similary, + as y' * z' IN G by monoid_op_element + and f (y' * z') IN h.carrier by MonoidHomo_def + ?!s. s IN G /\ f s = f (y' * z') by monoid_iso_property + i.e. s = y' * z' by uniqueness + and h.op x (h.op y z) = f (x' * (y' * z')) by MonoidHomo_def + hence true by monoid_assoc. + (3) h.id IN h.carrier + Since #e IN G by monoid_id_element + (f #e) = h.id IN h.carrier by MonoidHomo_def + (4) x IN h.carrier ==> h.op h.id x = x + Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property + h.id IN h.carrier by monoid_id_element + ?!e. e IN G /\ f e = h.id = f #e by monoid_iso_property + i.e. e = #e by uniqueness + hence h.op h.id x = f (e * x') by MonoidHomo_def + = f (#e * x') + = f x' by monoid_lid + = x + (5) x IN h.carrier ==> h.op x h.id = x + Since ?x'. x' IN G /\ (f x' = x) by monoid_iso_property + h.id IN h.carrier by monoid_id_element + ?!e. e IN G /\ f e = h.id = f #e by monoid_iso_property + i.e. e = #e by uniqueness + hence h.op x h.id = f (x' * e) by MonoidHomo_def + = f (x' * #e) + = f x' by monoid_rid + = x +*) +val monoid_iso_monoid = store_thm( + "monoid_iso_monoid", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> Monoid h``, + rw[monoid_iso_property] >> + `(!x. x IN G ==> f x IN h.carrier) /\ + (!x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y))) /\ + (f #e = h.id)` by metis_tac[MonoidHomo_def] >> + rw_tac std_ss[Monoid_def] >- + metis_tac[monoid_op_element] >- + (`?x'. x' IN G /\ (f x' = x)` by metis_tac[] >> + `?y'. y' IN G /\ (f y' = y)` by metis_tac[] >> + `?z'. z' IN G /\ (f z' = z)` by metis_tac[] >> + `?t. t IN G /\ (t = x' * y')` by metis_tac[monoid_op_element] >> + `h.op (h.op x y) z = f (x' * y' * z')` by metis_tac[] >> + `?s. s IN G /\ (s = y' * z')` by metis_tac[monoid_op_element] >> + `h.op x (h.op y z) = f (x' * (y' * z'))` by metis_tac[] >> + `x' * y' * z' = x' * (y' * z')` by rw[monoid_assoc] >> + metis_tac[]) >- + metis_tac[monoid_id_element, MonoidHomo_def] >- + metis_tac[monoid_lid, monoid_id_element] >> + metis_tac[monoid_rid, monoid_id_element]); + +(* Theorem: MonoidIso I g g *) +(* Proof: + By MonoidIso_def, this is to show: + (1) MonoidHomo I g g, true by monoid_homo_I_refl + (2) BIJ I R R, true by BIJ_I_SAME +*) +val monoid_iso_I_refl = store_thm( + "monoid_iso_I_refl", + ``!g:'a monoid. MonoidIso I g g``, + rw[MonoidIso_def, monoid_homo_I_refl, BIJ_I_SAME]); + +(* Theorem: MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k *) +(* Proof: + By MonoidIso_def, this is to show: + (1) MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k + True by monoid_homo_trans. + (2) BIJ f1 G h.carrier /\ BIJ f2 h.carrier k.carrier ==> BIJ (f2 o f1) G k.carrier + True by BIJ_COMPOSE. +*) +val monoid_iso_trans = store_thm( + "monoid_iso_trans", + ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). + !f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k``, + rw[MonoidIso_def] >- + metis_tac[monoid_homo_trans] >> + metis_tac[BIJ_COMPOSE]); + +(* Theorem: Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g *) +(* Proof: + By MonoidIso_def, this is to show: + (1) MonoidHomo f g h /\ BIJ f G h.carrier ==> MonoidHomo (LINV f G) h g + True by monoid_homo_sym. + (2) BIJ f G h.carrier ==> BIJ (LINV f G) h.carrier G + True by BIJ_LINV_BIJ +*) +val monoid_iso_sym = store_thm( + "monoid_iso_sym", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g``, + rw[MonoidIso_def, monoid_homo_sym, BIJ_LINV_BIJ]); + +(* Theorem: MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k *) +(* Proof: + By MonoidIso_def, this is to show: + (1) MonoidHomo f1 g h /\ MonoidHomo f2 h k ==> MonoidHomo (f2 o f1) g k + True by monoid_homo_compose. + (2) BIJ f1 G h.carrier /\ BIJ f2 h.carrier k.carrier ==> BIJ (f2 o f1) G k.carrier + True by BIJ_COMPOSE +*) +val monoid_iso_compose = store_thm( + "monoid_iso_compose", + ``!(g:'a monoid) (h:'b monoid) (k:'c monoid). + !f1 f2. MonoidIso f1 g h /\ MonoidIso f2 h k ==> MonoidIso (f2 o f1) g k``, + rw_tac std_ss[MonoidIso_def] >- + metis_tac[monoid_homo_compose] >> + metis_tac[BIJ_COMPOSE]); +(* This is the same as monoid_iso_trans. *) + +(* Theorem: Monoid g /\ MonoidIso f g h ==> !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n *) +(* Proof: by MonoidIso_def, monoid_homo_exp *) +val monoid_iso_exp = store_thm( + "monoid_iso_exp", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> + !x. x IN G ==> !n. f (x ** n) = h.exp (f x) n``, + rw[MonoidIso_def, monoid_homo_exp]); + +(* Theorem: Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g *) +(* Proof: + By MonoidIso_def, MonoidHomo_def, this is to show: + (1) BIJ f G h.carrier /\ x IN h.carrier ==> LINV f G x IN G + True by BIJ_LINV_ELEMENT + (2) BIJ f G h.carrier /\ x IN h.carrier /\ y IN h.carrier ==> LINV f G (h.op x y) = LINV f G x * LINV f G y + Let x' = LINV f G x, y' = LINV f G y. + Then x' IN G /\ y' IN G by BIJ_LINV_ELEMENT + so x' * y' IN G by monoid_op_element + ==> f (x' * y') = h.op (f x') (f y') by MonoidHomo_def + = h.op x y by BIJ_LINV_THM + Thus LINV f G (h.op x y) + = LINV f G (f (x' * y')) by above + = x' * y' by BIJ_LINV_THM + (3) BIJ f G h.carrier ==> LINV f G h.id = #e + Note #e IN G by monoid_id_element + and f #e = h.id by MonoidHomo_def + ==> LINV f G h.id = #e by BIJ_LINV_THM + (4) BIJ f G h.carrier ==> BIJ (LINV f G) h.carrier G + True by BIJ_LINV_BIJ +*) +val monoid_iso_linv_iso = store_thm( + "monoid_iso_linv_iso", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ MonoidIso f g h ==> MonoidIso (LINV f G) h g``, + rw_tac std_ss[MonoidIso_def, MonoidHomo_def] >- + metis_tac[BIJ_LINV_ELEMENT] >- + (qabbrev_tac `x' = LINV f G x` >> + qabbrev_tac `y' = LINV f G y` >> + metis_tac[BIJ_LINV_THM, BIJ_LINV_ELEMENT, monoid_op_element]) >- + metis_tac[BIJ_LINV_THM, monoid_id_element] >> + rw_tac std_ss[BIJ_LINV_BIJ]); +(* This is the same as monoid_iso_sym, just a direct proof. *) + +(* Theorem: MonoidIso f g h ==> BIJ f G h.carrier *) +(* Proof: by MonoidIso_def *) +val monoid_iso_bij = store_thm( + "monoid_iso_bij", + ``!(g:'a monoid) (h:'b monoid) f. MonoidIso f g h ==> BIJ f G h.carrier``, + rw_tac std_ss[MonoidIso_def]); + +(* Theorem: Monoid g /\ Monoid h /\ MonoidIso f g h ==> + !x. x IN G ==> ((f x = h.id) <=> (x = #e)) *) +(* Proof: + Note MonoidHomo f g h /\ BIJ f G h.carrier by MonoidIso_def + If part: x IN G /\ f x = h.id ==> x = #e + By monoid_id_unique, this is to show: + (1) !y. y IN G ==> y * x = y + Let z = y * x. + Then z IN G by monoid_op_element + f z = h.op (f y) (f x) by MonoidHomo_def + = h.op (f y) h.id by f x = h.id + = f y by monoid_homo_element, monoid_id + Thus z = y by BIJ_DEF, INJ_DEF + (2) !y. y IN G ==> x * y = y + Let z = x * y. + Then z IN G by monoid_op_element + f z = h.op (f x) (f y) by MonoidHomo_def + = h.op h.id (f y) by f x = h.id + = f y by monoid_homo_element, monoid_id + Thus z = y by BIJ_DEF, INJ_DEF + + Only-if part: f #e = h.id, true by monoid_homo_id +*) +val monoid_iso_eq_id = store_thm( + "monoid_iso_eq_id", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> + !x. x IN G ==> ((f x = h.id) <=> (x = #e))``, + rw[MonoidIso_def] >> + rw[EQ_IMP_THM] >| [ + rw[GSYM monoid_id_unique] >| [ + qabbrev_tac `z = x' * x` >> + `z IN G` by rw[Abbr`z`] >> + `f z = h.op (f x') (f x)` by fs[MonoidHomo_def, Abbr`z`] >> + `_ = f x'` by metis_tac[monoid_homo_element, monoid_id] >> + metis_tac[BIJ_DEF, INJ_DEF], + qabbrev_tac `z = x * x'` >> + `z IN G` by rw[Abbr`z`] >> + `f z = h.op (f x) (f x')` by fs[MonoidHomo_def, Abbr`z`] >> + `_ = f x'` by metis_tac[monoid_homo_element, monoid_id] >> + metis_tac[BIJ_DEF, INJ_DEF] + ], + rw[monoid_homo_id] + ]); + +(* Theorem: Monoid g /\ Monoid h /\ MonoidIso f g h ==> !x. x IN G ==> (order h (f x) = ord x) *) +(* Proof: + Let n = ord x, y = f x. + If n = 0, to show: order h y = 0. + By contradiction, suppose order h y <> 0. + Let m = order h y. + Note h.id = h.exp y m by order_property + = f (x ** m) by monoid_iso_exp + Thus x ** m = #e by monoid_iso_eq_id, monoid_exp_element + But x ** m <> #e by order_eq_0, ord x = 0 + This is a contradiction. + + If n <> 0, to show: order h y = n. + Note ord x = n + ==> (x ** n = #e) /\ + !m. 0 < m /\ m < n ==> x ** m <> #e by order_thm, 0 < n + Note h.exp y n = f (x ** n) by monoid_iso_exp + = f #e by x ** n = #e + = h.id by monoid_iso_id, [1] + Claim: !m. 0 < m /\ m < n ==> h.exp y m <> h.id + Proof: By contradiction, suppose h.exp y m = h.id. + Then f (x ** m) = h.exp y m by monoid_iso_exp + = h.id by above + or x ** m = #e by monoid_iso_eq_id, monoid_exp_element + But !m. 0 < m /\ m < n ==> x ** m <> #e by above + This is a contradiction. + + Thus by [1] and claim, order h y = n by order_thm +*) +val monoid_iso_order = store_thm( + "monoid_iso_order", + ``!(g:'a monoid) (h:'b monoid) f. Monoid g /\ Monoid h /\ MonoidIso f g h ==> + !x. x IN G ==> (order h (f x) = ord x)``, + rpt strip_tac >> + qabbrev_tac `n = ord x` >> + qabbrev_tac `y = f x` >> + Cases_on `n = 0` >| [ + spose_not_then strip_assume_tac >> + qabbrev_tac `m = order h y` >> + `0 < m` by decide_tac >> + `h.exp y m = h.id` by metis_tac[order_property] >> + `f (x ** m) = h.id` by metis_tac[monoid_iso_exp] >> + `x ** m = #e` by metis_tac[monoid_iso_eq_id, monoid_exp_element] >> + metis_tac[order_eq_0], + `0 < n` by decide_tac >> + `(x ** n = #e) /\ !m. 0 < m /\ m < n ==> x ** m <> #e` by metis_tac[order_thm] >> + `h.exp y n = h.id` by metis_tac[monoid_iso_exp, monoid_iso_id] >> + `!m. 0 < m /\ m < n ==> h.exp y m <> h.id` by + (spose_not_then strip_assume_tac >> + `f (x ** m) = h.id` by metis_tac[monoid_iso_exp] >> + `x ** m = #e` by metis_tac[monoid_iso_eq_id, monoid_exp_element] >> + metis_tac[]) >> + metis_tac[order_thm] + ]); + +(* Theorem: MonoidIso f g h /\ FINITE G ==> (CARD G = CARD h.carrier) *) +(* Proof: by MonoidIso_def, FINITE_BIJ_CARD. *) +val monoid_iso_card_eq = store_thm( + "monoid_iso_card_eq", + ``!g:'a monoid h:'b monoid f. MonoidIso f g h /\ FINITE G ==> (CARD G = CARD h.carrier)``, + metis_tac[MonoidIso_def, FINITE_BIJ_CARD]); + +(* ------------------------------------------------------------------------- *) +(* Monoid Automorphisms *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MonoidAuto f g ==> (f #e = #e) *) +(* Proof: by MonoidAuto_def, monoid_iso_id. *) +val monoid_auto_id = store_thm( + "monoid_auto_id", + ``!f g. MonoidAuto f g ==> (f #e = #e)``, + rw_tac std_ss[MonoidAuto_def, monoid_iso_id]); + +(* Theorem: MonoidAuto f g ==> !x. x IN G ==> f x IN G *) +(* Proof: by MonoidAuto_def, monoid_iso_element *) +val monoid_auto_element = store_thm( + "monoid_auto_element", + ``!f g. MonoidAuto f g ==> !x. x IN G ==> f x IN G``, + metis_tac[MonoidAuto_def, monoid_iso_element]); + +(* Theorem: MonoidAuto f1 g /\ MonoidAuto f2 g ==> MonoidAuto (f1 o f2) g *) +(* Proof: by MonoidAuto_def, monoid_iso_compose *) +val monoid_auto_compose = store_thm( + "monoid_auto_compose", + ``!(g:'a monoid). !f1 f2. MonoidAuto f1 g /\ MonoidAuto f2 g ==> MonoidAuto (f1 o f2) g``, + metis_tac[MonoidAuto_def, monoid_iso_compose]); + +(* Theorem: Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> !n. f (x ** n) = (f x) ** n *) +(* Proof: by MonoidAuto_def, monoid_iso_exp *) +val monoid_auto_exp = store_thm( + "monoid_auto_exp", + ``!f g. Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> !n. f (x ** n) = (f x) ** n``, + rw[MonoidAuto_def, monoid_iso_exp]); + +(* Theorem: MonoidAuto I g *) +(* Proof: + MonoidAuto I g + <=> MonoidIso I g g by MonoidAuto_def + <=> MonoidHomo I g g /\ BIJ f G G by MonoidIso_def + <=> T /\ BIJ f G G by MonoidHomo_def, I_THM + <=> T /\ T by BIJ_I_SAME +*) +val monoid_auto_I = store_thm( + "monoid_auto_I", + ``!(g:'a monoid). MonoidAuto I g``, + rw_tac std_ss[MonoidAuto_def, MonoidIso_def, MonoidHomo_def, BIJ_I_SAME]); + +(* Theorem: Monoid g /\ MonoidAuto f g ==> MonoidAuto (LINV f G) g *) +(* Proof: + MonoidAuto I g + ==> MonoidIso I g g by MonoidAuto_def + ==> MonoidIso (LINV f G) g by monoid_iso_linv_iso + ==> MonoidAuto (LINV f G) g by MonoidAuto_def +*) +val monoid_auto_linv_auto = store_thm( + "monoid_auto_linv_auto", + ``!(g:'a monoid) f. Monoid g /\ MonoidAuto f g ==> MonoidAuto (LINV f G) g``, + rw_tac std_ss[MonoidAuto_def, monoid_iso_linv_iso]); + +(* Theorem: MonoidAuto f g ==> f PERMUTES G *) +(* Proof: by MonoidAuto_def, MonoidIso_def *) +val monoid_auto_bij = store_thm( + "monoid_auto_bij", + ``!g:'a monoid. !f. MonoidAuto f g ==> f PERMUTES G``, + rw_tac std_ss[MonoidAuto_def, MonoidIso_def]); + +(* Theorem: Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> (ord (f x) = ord x) *) +(* Proof: by MonoidAuto_def, monoid_iso_order. *) +val monoid_auto_order = store_thm( + "monoid_auto_order", + ``!(g:'a monoid) f. Monoid g /\ MonoidAuto f g ==> !x. x IN G ==> (ord (f x) = ord x)``, + rw[MonoidAuto_def, monoid_iso_order]); + +(* ------------------------------------------------------------------------- *) +(* Submonoids. *) +(* ------------------------------------------------------------------------- *) + +(* Use H to denote h.carrier *) +val _ = overload_on ("H", ``(h:'a monoid).carrier``); + +(* Theorem: submonoid h g ==> H SUBSET G /\ (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ (h.id = #e) *) +(* Proof: + submonoid h g + <=> MonoidHomo I h g by submonoid_def + <=> (!x. x IN H ==> I x IN G) /\ + (!x y. x IN H /\ y IN H ==> (I (h.op x y) = (I x) * (I y))) /\ + (h.id = I #e) by MonoidHomo_def + <=> (!x. x IN H ==> x IN G) /\ + (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ + (h.id = #e) by I_THM + <=> H SUBSET G + (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ + (h.id = #e) by SUBSET_DEF +*) +val submonoid_eqn = store_thm( + "submonoid_eqn", + ``!(g:'a monoid) (h:'a monoid). submonoid h g <=> + H SUBSET G /\ (!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) /\ (h.id = #e)``, + rw_tac std_ss[submonoid_def, MonoidHomo_def, SUBSET_DEF]); + +(* Theorem: submonoid h g ==> H SUBSET G *) +(* Proof: by submonoid_eqn *) +val submonoid_subset = store_thm( + "submonoid_subset", + ``!(g:'a monoid) (h:'a monoid). submonoid h g ==> H SUBSET G``, + rw_tac std_ss[submonoid_eqn]); + +(* Theorem: submonoid h g /\ MonoidHomo f g k ==> MonoidHomo f h k *) +(* Proof: + Note H SUBSET G by submonoid_subset + or !x. x IN H ==> x IN G by SUBSET_DEF + By MonoidHomo_def, this is to show: + (1) x IN H ==> f x IN k.carrier + True by MonoidHomo_def, MonoidHomo f g k + (2) x IN H /\ y IN H /\ f (h.op x y) = k.op (f x) (f y) + Note x IN H ==> x IN G by above + and y IN H ==> y IN G by above + f (h.op x y) + = f (x * y) by submonoid_eqn + = k.op (f x) (f y) by MonoidHomo_def + (3) f h.id = k.id + f (h.id) + = f #e by submonoid_eqn + = k.id by MonoidHomo_def +*) +val submonoid_homo_homo = store_thm( + "submonoid_homo_homo", + ``!(g:'a monoid) (h:'a monoid) (k:'b monoid) f. submonoid h g /\ MonoidHomo f g k ==> MonoidHomo f h k``, + rw_tac std_ss[submonoid_def, MonoidHomo_def]); + +(* Theorem: submonoid g g *) +(* Proof: + By submonoid_def, this is to show: + MonoidHomo I g g, true by monoid_homo_I_refl. +*) +val submonoid_refl = store_thm( + "submonoid_refl", + ``!g:'a monoid. submonoid g g``, + rw[submonoid_def, monoid_homo_I_refl]); + +(* Theorem: submonoid g h /\ submonoid h k ==> submonoid g k *) +(* Proof: + By submonoid_def, this is to show: + MonoidHomo I g h /\ MonoidHomo I h k ==> MonoidHomo I g k + Since I o I = I by combinTheory.I_o_ID + This is true by monoid_homo_trans +*) +val submonoid_trans = store_thm( + "submonoid_trans", + ``!(g h k):'a monoid. submonoid g h /\ submonoid h k ==> submonoid g k``, + prove_tac[submonoid_def, combinTheory.I_o_ID, monoid_homo_trans]); + +(* Theorem: submonoid h g /\ submonoid g h ==> MonoidIso I h g *) +(* Proof: + By submonoid_def, MonoidIso_def, this is to show: + MonoidHomo I h g /\ MonoidHomo I g h ==> BIJ I H G + By BIJ_DEF, INJ_DEF, SURJ_DEF, this is to show: + (1) x IN H ==> x IN G, true by submonoid_subset, submonoid h g + (2) x IN G ==> x IN H, true by submonoid_subset, submonoid g h +*) +val submonoid_I_antisym = store_thm( + "submonoid_I_antisym", + ``!(g:'a monoid) h. submonoid h g /\ submonoid g h ==> MonoidIso I h g``, + rw_tac std_ss[submonoid_def, MonoidIso_def] >> + fs[MonoidHomo_def] >> + rw_tac std_ss[BIJ_DEF, INJ_DEF, SURJ_DEF]); + +(* Theorem: submonoid h g /\ G SUBSET H ==> MonoidIso I h g *) +(* Proof: + By submonoid_def, MonoidIso_def, this is to show: + MonoidHomo I h g /\ G SUBSET H ==> BIJ I H G + By BIJ_DEF, INJ_DEF, SURJ_DEF, this is to show: + (1) x IN H ==> x IN G, true by submonoid_subset, submonoid h g + (2) x IN G ==> x IN H, true by G SUBSET H, given +*) +val submonoid_carrier_antisym = store_thm( + "submonoid_carrier_antisym", + ``!(g:'a monoid) h. submonoid h g /\ G SUBSET H ==> MonoidIso I h g``, + rpt (stripDup[submonoid_def]) >> + rw_tac std_ss[MonoidIso_def] >> + `H SUBSET G` by rw[submonoid_subset] >> + fs[MonoidHomo_def, SUBSET_DEF] >> + rw_tac std_ss[BIJ_DEF, INJ_DEF, SURJ_DEF]); + +(* Theorem: Monoid g /\ Monoid h /\ submonoid h g ==> !x. x IN H ==> (order h x = ord x) *) +(* Proof: + Note MonoidHomo I h g by submonoid_def + If ord x = 0, to show: order h x = 0. + By contradiction, suppose order h x <> 0. + Let n = order h x. + Then 0 < n by n <> 0 + and h.exp x n = h.id by order_property + or x ** n = #e by monoid_homo_exp, monoid_homo_id, I_THM + But x ** n <> #e by order_eq_0, ord x = 0 + This is a contradiction. + If ord x <> 0, to show: order h x = ord x. + Note 0 < ord x by ord x <> 0 + By order_thm, this is to show: + (1) h.exp x (ord x) = h.id + h.exp x (ord x) + = I (h.exp x (ord x)) by I_THM + = (I x) ** ord x by monoid_homo_exp + = x ** ord x by I_THM + = #e by order_property + = I (h.id) by monoid_homo_id + = h.id by I_THM + (2) 0 < m /\ m < ord x ==> h.exp x m <> h.id + By contradiction, suppose h.exp x m = h.id + h.exp x m + = I (h.exp x m) by I_THM + = (I x) ** m by monoid_homo_exp + = x ** m by I_THM + h.id = I (h.id) by I_THM + = #e by monoid_homo_id + Thus x ** m = #e by h.exp x m = h.id + But x ** m <> #e by order_thm + This is a contradiction. +*) +val submonoid_order_eqn = store_thm( + "submonoid_order_eqn", + ``!(g:'a monoid) h. Monoid g /\ Monoid h /\ submonoid h g ==> + !x. x IN H ==> (order h x = ord x)``, + rw[submonoid_def] >> + `!x. I x = x` by rw[] >> + Cases_on `ord x = 0` >| [ + spose_not_then strip_assume_tac >> + qabbrev_tac `n = order h x` >> + `0 < n` by decide_tac >> + `h.exp x n = h.id` by rw[order_property, Abbr`n`] >> + `x ** n = #e` by metis_tac[monoid_homo_exp, monoid_homo_id] >> + metis_tac[order_eq_0], + `0 < ord x` by decide_tac >> + rw[order_thm] >- + metis_tac[monoid_homo_exp, order_property, monoid_homo_id] >> + spose_not_then strip_assume_tac >> + `x ** m = #e` by metis_tac[monoid_homo_exp, monoid_homo_id] >> + metis_tac[order_thm] + ]); + +(* ------------------------------------------------------------------------- *) +(* Homomorphic Image of Monoid. *) +(* ------------------------------------------------------------------------- *) + +(* Define an operation on images *) +val image_op_def = Define` + image_op (g:'a monoid) (f:'a -> 'b) x y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)) +`; + +(* Theorem: INJ f G univ(:'b) ==> !x y. x IN G /\ y IN G ==> (image_op g f (f x) (f y) = f (x * y)) *) +(* Proof: + Note x = CHOICE (preimage f G (f x)) by preimage_inj_choice + and y = CHOICE (preimage f G (f y)) by preimage_inj_choice + image_op g f (f x) (f y) + = f (CHOICE (preimage f G (f x)) * CHOICE (preimage f G (f y)) by image_op_def + = f (x * y) by above +*) +val image_op_inj = store_thm( + "image_op_inj", + ``!g:'a monoid f. INJ f G univ(:'b) ==> + !x y. x IN G /\ y IN G ==> (image_op g f (f x) (f y) = f (g.op x y))``, + rw[image_op_def, preimage_inj_choice]); + +(* Define the homomorphic image of a monoid. *) +val homo_monoid_def = Define` + homo_monoid (g:'a monoid) (f:'a -> 'b) = + <| carrier := IMAGE f G; + op := image_op g f; + id := f #e + |> +`; + +(* set overloading *) +val _ = overload_on ("o", ``(homo_monoid (g:'a monoid) (f:'a -> 'b)).op``); +val _ = overload_on ("#i", ``(homo_monoid (g:'a monoid) (f:'a -> 'b)).id``); +val _ = overload_on ("fG", ``(homo_monoid (g:'a monoid) (f:'a -> 'b)).carrier``); + +(* Theorem: Properties of homo_monoid. *) +(* Proof: by homo_monoid_def and image_op_def. *) +val homo_monoid_property = store_thm( + "homo_monoid_property", + ``!(g:'a monoid) (f:'a -> 'b). (fG = IMAGE f G) /\ + (!x y. x IN fG /\ y IN fG ==> (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))) /\ + (#i = f #e)``, + rw[homo_monoid_def, image_op_def]); + +(* Theorem: !x. x IN G ==> f x IN fG *) +(* Proof: by homo_monoid_def, IN_IMAGE *) +val homo_monoid_element = store_thm( + "homo_monoid_element", + ``!(g:'a monoid) (f:'a -> 'b). !x. x IN G ==> f x IN fG``, + rw[homo_monoid_def]); + +(* Theorem: f #e = #i *) +(* Proof: by homo_monoid_def *) +val homo_monoid_id = store_thm( + "homo_monoid_id", + ``!(g:'a monoid) (f:'a -> 'b). f #e = #i``, + rw[homo_monoid_def]); + +(* Theorem: INJ f G univ(:'b) ==> + !x y. x IN G /\ y IN G ==> (f (x * y) = (f x) o (f y)) *) +(* Proof: + Note x = CHOICE (preimage f G (f x)) by preimage_inj_choice + and y = CHOICE (preimage f G (f y)) by preimage_inj_choice + f (x * y) + = f (CHOICE (preimage f G (f x)) * CHOICE (preimage f G (f y)) by above + = (f x) o (f y) by homo_monoid_property +*) +val homo_monoid_op_inj = store_thm( + "homo_monoid_op_inj", + ``!(g:'a monoid) (f:'a -> 'b). INJ f G univ(:'b) ==> + !x y. x IN G /\ y IN G ==> (f (x * y) = (f x) o (f y))``, + rw[homo_monoid_property, preimage_inj_choice]); + +(* Theorem: MonoidIso I (homo_monoid g I) g *) +(* Proof: + Note IMAGE I G = G by IMAGE_I + and INJ I G univ(:'a) by INJ_I + and !x. I x = x by I_THM + By MonoidIso_def, this is to show: + (1) MonoidHomo I (homo_monoid g I) g + By MonoidHomo_def, homo_monoid_def, this is to show: + x IN G /\ y IN G ==> image_op g I x y = x * y + This is true by image_op_inj + (2) BIJ I (homo_group g I).carrier G + (homo_group g I).carrier + = IMAGE I G by homo_monoid_property + = G by above, IMAGE_I + This is true by BIJ_I_SAME +*) +val homo_monoid_I = store_thm( + "homo_monoid_I", + ``!g:'a monoid. MonoidIso I (homo_monoid g I) g``, + rpt strip_tac >> + `IMAGE I G = G` by rw[] >> + `INJ I G univ(:'a)` by rw[INJ_I] >> + `!x. I x = x` by rw[] >> + rw_tac std_ss[MonoidIso_def] >| [ + rw_tac std_ss[MonoidHomo_def, homo_monoid_def] >> + metis_tac[image_op_inj], + rw[homo_monoid_property, BIJ_I_SAME] + ]); + +(* Theorem: [Closure] Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> x IN fG /\ y IN fG ==> x o y IN fG *) +(* Proof: + Let a = CHOICE (preimage f G x), + b = CHOICE (preimage f G y). + Then a IN G /\ x = f a by preimage_choice_property + b IN G /\ y = f b by preimage_choice_property + x o y + = (f a) o (f b) + = f (a * b) by homo_monoid_property + Note a * b IN G by monoid_op_element + so f (a * b) IN fG by homo_monoid_element +*) +val homo_monoid_closure = store_thm( + "homo_monoid_closure", + ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> + !x y. x IN fG /\ y IN fG ==> x o y IN fG``, + rw_tac std_ss[homo_monoid_property] >> + rw[preimage_choice_property]); + +(* Theorem: [Associative] Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> + x IN fG /\ y IN fG /\ z IN fG ==> (x o y) o z = x o (y o z) *) +(* Proof: + By MonoidHomo_def, + !x. x IN G ==> f x IN fG + !x y. x IN G /\ y IN G ==> (f (x * y) = f x o f y) + Let a = CHOICE (preimage f G x), + b = CHOICE (preimage f G y), + c = CHOICE (preimage f G z). + Then a IN G /\ x = f a by preimage_choice_property + b IN G /\ y = f b by preimage_choice_property + c IN G /\ z = f c by preimage_choice_property + (x o y) o z + = ((f a) o (f b)) o (f c) by expanding x, y, z + = f (a * b) o (f c) by homo_monoid_property + = f (a * b * c) by homo_monoid_property + = f (a * (b * c)) by monoid_assoc + = (f a) o f (b * c) by homo_monoid_property + = (f a) o ((f b) o (f c)) by homo_monoid_property + = x o (y o z) by contracting x, y, z +*) +val homo_monoid_assoc = store_thm( + "homo_monoid_assoc", + ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> + !x y z. x IN fG /\ y IN fG /\ z IN fG ==> ((x o y) o z = x o (y o z))``, + rw_tac std_ss[MonoidHomo_def] >> + `(fG = IMAGE f G) /\ !x y. x IN fG /\ y IN fG ==> (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))` by rw[homo_monoid_property] >> + qabbrev_tac `a = CHOICE (preimage f G x)` >> + qabbrev_tac `b = CHOICE (preimage f G y)` >> + qabbrev_tac `c = CHOICE (preimage f G z)` >> + `a IN G /\ (f a = x)` by metis_tac[preimage_choice_property] >> + `b IN G /\ (f b = y)` by metis_tac[preimage_choice_property] >> + `c IN G /\ (f c = z)` by metis_tac[preimage_choice_property] >> + `a * b IN G /\ b * c IN G ` by rw[] >> + `a * b * c = a * (b * c)` by rw[monoid_assoc] >> + metis_tac[]); + +(* Theorem: [Identity] Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> #i IN fG /\ #i o x = x /\ x o #i = x. *) +(* Proof: + By homo_monoid_property, #i = f #e, and #i IN fG. + Since CHOICE (preimage f G x) IN G /\ x = f (CHOICE (preimage f G x)) by preimage_choice_property + hence #i o x + = (f #e) o f (preimage f G x) + = f (#e * preimage f G x) by homo_monoid_property + = f (preimage f G x) by monoid_lid + = x + similarly for x o #i = x by monoid_rid +*) +val homo_monoid_id_property = store_thm( + "homo_monoid_id_property", + ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> + #i IN fG /\ (!x. x IN fG ==> (#i o x = x) /\ (x o #i = x))``, + rw[MonoidHomo_def, homo_monoid_property] >- + metis_tac[monoid_lid, monoid_id_element, preimage_choice_property] >> + metis_tac[monoid_rid, monoid_id_element, preimage_choice_property]); + +(* Theorem: [Commutative] AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> + x IN fG /\ y IN fG ==> (x o y = y o x) *) +(* Proof: + Note AbelianMonoid g ==> Monoid g and + !x y. x IN G /\ y IN G ==> (x * y = y * x) by AbelianMonoid_def + By MonoidHomo_def, + !x. x IN G ==> f x IN fG + !x y. x IN G /\ y IN G ==> (f (x * y) = f x o f y) + Let a = CHOICE (preimage f G x), + b = CHOICE (preimage f G y). + Then a IN G /\ x = f a by preimage_choice_property + b IN G /\ y = f b by preimage_choice_property + x o y + = (f a) o (f b) by expanding x, y + = f (a * b) by homo_monoid_property + = f (b * a) by AbelianMonoid_def, above + = (f b) o (f a) by homo_monoid_property + = y o x by contracting x, y +*) +val homo_monoid_comm = store_thm( + "homo_monoid_comm", + ``!(g:'a monoid) (f:'a -> 'b). AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> + !x y. x IN fG /\ y IN fG ==> (x o y = y o x)``, + rw_tac std_ss[AbelianMonoid_def, MonoidHomo_def] >> + `(fG = IMAGE f G) /\ !x y. x IN fG /\ y IN fG ==> (x o y = f (CHOICE (preimage f G x) * CHOICE (preimage f G y)))` by rw[homo_monoid_property] >> + qabbrev_tac `a = CHOICE (preimage f G x)` >> + qabbrev_tac `b = CHOICE (preimage f G y)` >> + `a IN G /\ (f a = x)` by metis_tac[preimage_choice_property] >> + `b IN G /\ (f b = y)` by metis_tac[preimage_choice_property] >> + `a * b = b * a` by rw[] >> + metis_tac[]); + +(* Theorem: Homomorphic image of a monoid is a monoid. + Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> Monoid (homo_monoid g f) *) +(* Proof: + This is to show each of these: + (1) x IN fG /\ y IN fG ==> x o y IN fG true by homo_monoid_closure + (2) x IN fG /\ y IN fG /\ z IN fG ==> (x o y) o z = (x o y) o z true by homo_monoid_assoc + (3) #i IN fG, true by homo_monoid_id_property + (4) x IN fG ==> #i o x = x, true by homo_monoid_id_property + (5) x IN fG ==> x o #i = x, true by homo_monoid_id_property +*) +val homo_monoid_monoid = store_thm( + "homo_monoid_monoid", + ``!(g:'a monoid) f. Monoid g /\ MonoidHomo f g (homo_monoid g f) ==> Monoid (homo_monoid g f)``, + rpt strip_tac >> + rw_tac std_ss[Monoid_def] >| [ + rw[homo_monoid_closure], + rw[homo_monoid_assoc], + rw[homo_monoid_id_property], + rw[homo_monoid_id_property], + rw[homo_monoid_id_property] + ]); + +(* Theorem: Homomorphic image of an Abelian monoid is an Abelian monoid. + AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> AbelianMonoid (homo_monoid g f) *) +(* Proof: + Note AbelianMonoid g ==> Monoid g by AbelianMonoid_def + By AbelianMonoid_def, this is to show: + (1) Monoid (homo_monoid g f), true by homo_monoid_monoid, Monoid g + (2) x IN fG /\ y IN fG ==> x o y = y o x, true by homo_monoid_comm, AbelianMonoid g +*) +val homo_monoid_abelian_monoid = store_thm( + "homo_monoid_abelian_monoid", + ``!(g:'a monoid) f. AbelianMonoid g /\ MonoidHomo f g (homo_monoid g f) ==> AbelianMonoid (homo_monoid g f)``, + metis_tac[homo_monoid_monoid, AbelianMonoid_def, homo_monoid_comm]); + +(* Theorem: Monoid g /\ INJ f G UNIV ==> MonoidHomo f g (homo_monoid g f) *) +(* Proof: + By MonoidHomo_def, homo_monoid_property, this is to show: + (1) x IN G ==> f x IN IMAGE f G, true by IN_IMAGE + (2) x IN G /\ y IN G ==> f (x * y) = f x o f y, true by homo_monoid_op_inj +*) +val homo_monoid_by_inj = store_thm( + "homo_monoid_by_inj", + ``!(g:'a monoid) (f:'a -> 'b). Monoid g /\ INJ f G UNIV ==> MonoidHomo f g (homo_monoid g f)``, + rw_tac std_ss[MonoidHomo_def, homo_monoid_property] >- + rw[] >> + rw[homo_monoid_op_inj]); + +(* ------------------------------------------------------------------------- *) +(* Weaker form of Homomorphic of Monoid and image of identity. *) +(* ------------------------------------------------------------------------- *) + +(* Let us take out the image of identity requirement *) +val WeakHomo_def = Define` + WeakHomo (f:'a -> 'b) (g:'a monoid) (h:'b monoid) <=> + (!x. x IN G ==> f x IN h.carrier) /\ + (!x y. x IN G /\ y IN G ==> (f (x * y) = h.op (f x) (f y))) + (* no requirement for: f #e = h.id *) +`; + +(* A function f from g to h is an isomorphism if f is a bijective homomorphism. *) +val WeakIso_def = Define` + WeakIso f g h <=> WeakHomo f g h /\ BIJ f G h.carrier +`; + +(* Then the best we can prove about monoid identities is the following: + Monoid g /\ Monoid h /\ WeakIso f g h ==> f #e = h.id + which is NOT: + Monoid g /\ Monoid h /\ WeakHomo f g h ==> f #e = h.id +*) + +(* Theorem: Monoid g /\ Monoid h /\ WeakIso f g h ==> f #e = h.id *) +(* Proof: + By monoid_id_unique: + |- !g. Monoid g ==> !e. e IN G ==> ((!x. x IN G ==> (x * e = x) /\ (e * x = x)) <=> (e = #e)) : thm + Hence this is to show: !x. x IN h.carrier ==> (h.op x (f #e) = x) /\ (h.op (f #e) x = x) + Note that WeakIso f g h = WeakHomo f g h /\ BIJ f G h.carrier. + (1) x IN h.carrier /\ f #e IN h.carrier ==> h.op x (f #e) = x + Since x = f y for some y IN G, by BIJ_THM + h.op x (f #e) = h.op (f y) (f #e) + = f (y * #e) by WeakHomo_def + = f y = x by monoid_rid + (2) x IN h.carrier /\ f #e IN h.carrier ==> h.op (f #e) x = x + Similar to above, + h.op (f #e) x = h.op (f #e) (f y) = f (#e * y) = f y = x by monoid_lid. +*) +val monoid_weak_iso_id = store_thm( + "monoid_weak_iso_id", + ``!f g h. Monoid g /\ Monoid h /\ WeakIso f g h ==> (f #e = h.id)``, + rw_tac std_ss[WeakIso_def] >> + `#e IN G /\ f #e IN h.carrier` by metis_tac[WeakHomo_def, monoid_id_element] >> + `!x. x IN h.carrier ==> (h.op x (f #e) = x) /\ (h.op (f #e) x = x)` suffices_by rw_tac std_ss[monoid_id_unique] >> + rpt strip_tac>| [ + `?y. y IN G /\ (f y = x)` by metis_tac[BIJ_THM] >> + metis_tac[WeakHomo_def, monoid_rid], + `?y. y IN G /\ (f y = x)` by metis_tac[BIJ_THM] >> + metis_tac[WeakHomo_def, monoid_lid] + ]); + +(* ------------------------------------------------------------------------- *) +(* Injective Image of Monoid. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: Given a Monoid g, and an injective function f, + then the image (f G) is a Monoid, with an induced binary operator: + op := (\x y. f (f^-1 x * f^-1 y)) *) + +(* Define a monoid injective image for an injective f, with LINV f G. *) +Definition monoid_inj_image_def: + monoid_inj_image (g:'a monoid) (f:'a -> 'b) = + <|carrier := IMAGE f G; + op := let t = LINV f G in (\x y. f (t x * t y)); + id := f #e + |> +End + +(* Theorem: Monoid g /\ INJ f G univ(:'b) ==> Monoid (monoid_inj_image g f) *) +(* Proof: + Note INJ f G univ(:'b) ==> BIJ f G (IMAGE f G) by INJ_IMAGE_BIJ_ALT + so !x. x IN G ==> t (f x) = x where t = LINV f G + and !x. x IN (IMAGE f G) ==> f (t x) = x by BIJ_LINV_THM + By Monoid_def, monoid_inj_image_def, this is to show: + (1) x IN IMAGE f G /\ y IN IMAGE f G ==> f (t x * t y) IN IMAGE f G + Note ?a. (x = f a) /\ a IN G by IN_IMAGE + ?b. (y = f b) /\ b IN G by IN_IMAGE + Hence f (t x * t y) + = f (t (f a) * t (f b)) by x = f a, y = f b + = f (a * b) by !y. t (f y) = y + Since a * b IN G by monoid_op_element + f (a * b) IN IMAGE f G by IN_IMAGE + (2) x IN IMAGE f G /\ y IN IMAGE f G /\ z IN IMAGE f G ==> f (t (f (t x * t y)) * t z) = f (t x * t (f (t y * t z))) + Note ?a. (x = f a) /\ a IN G by IN_IMAGE + ?b. (y = f b) /\ b IN G by IN_IMAGE + ?c. (z = f c) /\ c IN G by IN_IMAGE + LHS = f (t (f (t x * t y)) * t z)) + = f (t (f (t (f a) * t (f b))) * t (f c)) by x = f a, y = f b, z = f c + = f (t (f (a * b)) * c) by !y. t (f y) = y + = f ((a * b) * c) by !y. t (f y) = y, monoid_op_element + RHS = f (t x * t (f (t y * t z))) + = f (t (f a) * t (f (t (f b) * t (f c)))) by x = f a, y = f b, z = f c + = f (a * t (f (b * c))) by !y. t (f y) = y + = f (a * (b * c)) by !y. t (f y) = y + = LHS by monoid_assoc + (3) f #e IN IMAGE f G + Since #e IN G by monoid_id_element + so f #e IN IMAGE f G by IN_IMAGE + (4) x IN IMAGE f G ==> f (t (f #e) * t x) = x + Note ?a. (x = f a) /\ a IN G by IN_IMAGE + and #e IN G by monoid_id_element + Hence f (t (f #e) * t x) + = f (#e * t x) by !y. t (f y) = y + = f (#e * t (f a)) by x = f a + = f (#e * a) by !y. t (f y) = y + = f a by monoid_id + = x by x = f a + (5) x IN IMAGE f G ==> f (t x * t (f #e)) = x + Note ?a. (x = f a) /\ a IN G by IN_IMAGE + and #e IN G by monoid_id_element + Hence f (t x * t (f #e)) + = f (t x * #e) by !y. t (f y) = y + = f (t (f a) * #e) by x = f a + = f (a * #e) by !y. t (f y) = y + = f a by monoid_id + = x by x = f a +*) +Theorem monoid_inj_image_monoid: + !(g:'a monoid) (f:'a -> 'b). Monoid g /\ INJ f G univ(:'b) ==> Monoid (monoid_inj_image g f) +Proof + rpt strip_tac >> + `BIJ f G (IMAGE f G)` by rw[INJ_IMAGE_BIJ_ALT] >> + `(!x. x IN G ==> (LINV f G (f x) = x)) /\ (!x. x IN (IMAGE f G) ==> (f (LINV f G x) = x))` by metis_tac[BIJ_LINV_THM] >> + rw_tac std_ss[Monoid_def, monoid_inj_image_def] >| [ + `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> + `?b. (y = f b) /\ b IN G` by rw[GSYM IN_IMAGE] >> + rw[], + `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> + `?b. (y = f b) /\ b IN G` by rw[GSYM IN_IMAGE] >> + `?c. (z = f c) /\ c IN G` by rw[GSYM IN_IMAGE] >> + rw[monoid_assoc], + rw[], + `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> + rw[], + `?a. (x = f a) /\ a IN G` by rw[GSYM IN_IMAGE] >> + rw[] + ] +QED + +(* Theorem: AbelianMonoid g /\ INJ f G univ(:'b) ==> AbelianMonoid (monoid_inj_image g f) *) +(* Proof: + By AbelianMonoid_def, this is to show: + (1) Monoid g ==> Monoid (monoid_inj_image g f) + True by monoid_inj_image_monoid. + (2) x IN G /\ y IN G ==> (monoid_inj_image g f).op x y = (monoid_inj_image g f).op y x + By monoid_inj_image_def, this is to show: + f (t (f x) * t (f y)) = f (t (f y) * t (f x)) where t = LINV f G + Note INJ f G univ(:'b) ==> BIJ f G (IMAGE f G) by INJ_IMAGE_BIJ_ALT + so !x. x IN G ==> t (f x) = x + and !x. x IN (IMAGE f G) ==> f (t x) = x by BIJ_LINV_THM + f (t (f x) * t (f y)) + = f (x * y) by !y. t (f y) = y + = f (y * x) by commutativity condition + = f (t (f y) * t (f x)) by !y. t (f y) = y +*) +Theorem monoid_inj_image_abelian_monoid: + !(g:'a monoid) (f:'a -> 'b). AbelianMonoid g /\ INJ f G univ(:'b) ==> + AbelianMonoid (monoid_inj_image g f) +Proof + rw[AbelianMonoid_def] >- + rw[monoid_inj_image_monoid] >> + pop_assum mp_tac >> + pop_assum mp_tac >> + rw[monoid_inj_image_def] >> + metis_tac[INJ_IMAGE_BIJ_ALT, BIJ_LINV_THM] +QED + +(* Theorem: INJ f G univ(:'b) ==> MonoidHomo f g (monoid_inj_image g f) *) +(* Proof: + Let s = IMAGE f G. + Then BIJ f G s by INJ_IMAGE_BIJ_ALT + so INJ f G s by BIJ_DEF + + By MonoidHomo_def, monoid_inj_image_def, this is to show: + (1) x IN G ==> f x IN IMAGE f G, true by IN_IMAGE + (2) x IN R /\ y IN R ==> f (x * y) = f (LINV f R (f x) * LINV f R (f y)) + Since LINV f R (f x) = x by BIJ_LINV_THM + and LINV f R (f y) = y by BIJ_LINV_THM + The result is true. +*) +Theorem monoid_inj_image_monoid_homo: + !(g:'a monoid) f. INJ f G univ(:'b) ==> MonoidHomo f g (monoid_inj_image g f) +Proof + rw[MonoidHomo_def, monoid_inj_image_def] >> + qabbrev_tac `s = IMAGE f G` >> + `BIJ f G s` by rw[INJ_IMAGE_BIJ_ALT, Abbr`s`] >> + `INJ f G s` by metis_tac[BIJ_DEF] >> + metis_tac[BIJ_LINV_THM] +QED + +(* ------------------------------------------------------------------------- *) +(* Submonoid Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading (# are temporary): +# K = k.carrier +# x o y = h.op x y +# #i = h.id + h << g = Submonoid h g + h mINTER k = monoid_intersect h k + smbINTER g = submonoid_big_intersect g +*) +(* Definitions and Theorems (# are exported): + + Helper Theorems: + + Submonoid of a Monoid: + Submonoid_def |- !h g. h << g <=> + Monoid h /\ Monoid g /\ H SUBSET G /\ ($o = $* ) /\ (#i = #e) + submonoid_property |- !g h. h << g ==> + Monoid h /\ Monoid g /\ H SUBSET G /\ + (!x y. x IN H /\ y IN H ==> (x o y = x * y)) /\ (#i = #e) + submonoid_carrier_subset |- !g h. h << g ==> H SUBSET G +# submonoid_element |- !g h. h << g ==> !x. x IN H ==> x IN G +# submonoid_id |- !g h. h << g ==> (#i = #e) + submonoid_exp |- !g h. h << g ==> !x. x IN H ==> !n. h.exp x n = x ** n + submonoid_homomorphism |- !g h. h << g ==> Monoid h /\ Monoid g /\ submonoid h g + submonoid_order |- !g h. h << g ==> !x. x IN H ==> (order h x = ord x) + submonoid_alt |- !g. Monoid g ==> !h. h << g <=> H SUBSET G /\ + (!x y. x IN H /\ y IN H ==> h.op x y IN H) /\ + h.id IN H /\ (h.op = $* ) /\ (h.id = #e) + + Submonoid Theorems: + submonoid_reflexive |- !g. Monoid g ==> g << g + submonoid_antisymmetric |- !g h. h << g /\ g << h ==> (h = g) + submonoid_transitive |- !g h k. k << h /\ h << g ==> k << g + submonoid_monoid |- !g h. h << g ==> Monoid h + + Submonoid Intersection: + monoid_intersect_def |- !g h. g mINTER h = <|carrier := G INTER H; op := $*; id := #e|> + monoid_intersect_property |- !g h. ((g mINTER h).carrier = G INTER H) /\ + ((g mINTER h).op = $* ) /\ ((g mINTER h).id = #e) + monoid_intersect_element |- !g h x. x IN (g mINTER h).carrier ==> x IN G /\ x IN H + monoid_intersect_id |- !g h. (g mINTER h).id = #e + + submonoid_intersect_property |- !g h k. h << g /\ k << g ==> + ((h mINTER k).carrier = H INTER K) /\ + (!x y. x IN H INTER K /\ y IN H INTER K ==> + ((h mINTER k).op x y = x * y)) /\ ((h mINTER k).id = #e) + submonoid_intersect_monoid |- !g h k. h << g /\ k << g ==> Monoid (h mINTER k) + submonoid_intersect_submonoid |- !g h k. h << g /\ k << g ==> (h mINTER k) << g + + Submonoid Big Intersection: + submonoid_big_intersect_def |- !g. smbINTER g = + <|carrier := BIGINTER (IMAGE (\h. H) {h | h << g}); op := $*; id := #e|> + submonoid_big_intersect_property |- !g. + ((smbINTER g).carrier = BIGINTER (IMAGE (\h. H) {h | h << g})) /\ + (!x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> ((smbINTER g).op x y = x * y)) /\ + ((smbINTER g).id = #e) + submonoid_big_intersect_element |- !g x. x IN (smbINTER g).carrier <=> !h. h << g ==> x IN H + submonoid_big_intersect_op_element |- !g x y. x IN (smbINTER g).carrier /\ + y IN (smbINTER g).carrier ==> + (smbINTER g).op x y IN (smbINTER g).carrier + submonoid_big_intersect_has_id |- !g. (smbINTER g).id IN (smbINTER g).carrier + submonoid_big_intersect_subset |- !g. Monoid g ==> (smbINTER g).carrier SUBSET G + submonoid_big_intersect_monoid |- !g. Monoid g ==> Monoid (smbINTER g) + submonoid_big_intersect_submonoid |- !g. Monoid g ==> smbINTER g << g +*) + +(* ------------------------------------------------------------------------- *) +(* Submonoid of a Monoid *) +(* ------------------------------------------------------------------------- *) + +(* Use K to denote k.carrier *) +val _ = temp_overload_on ("K", ``(k:'a monoid).carrier``); + +(* Use o to denote h.op *) +val _ = temp_overload_on ("o", ``(h:'a monoid).op``); + +(* Use #i to denote h.id *) +val _ = temp_overload_on ("#i", ``(h:'a monoid).id``); + +(* A Submonoid is a subset of a monoid that's a monoid itself, keeping op, id. *) +val Submonoid_def = Define` + Submonoid (h:'a monoid) (g:'a monoid) <=> + Monoid h /\ Monoid g /\ + H SUBSET G /\ ($o = $* ) /\ (#i = #e) +`; + +(* Overload Submonoid *) +val _ = overload_on ("<<", ``Submonoid``); +val _ = set_fixity "<<" (Infix(NONASSOC, 450)); (* same as relation *) + +(* Note: The requirement $o = $* is stronger than the following: +val _ = overload_on ("<<", ``\(h g):'a monoid. Monoid g /\ Monoid h /\ submonoid h g``); +Since submonoid h g is based on MonoidHomo I g h, which only gives +!x y. x IN H /\ y IN H ==> (h.op x y = x * y)) + +This is not enough to satisfy monoid_component_equality, +hence cannot prove: h << g /\ g << h ==> h = g +*) + +(* +val submonoid_property = save_thm( + "submonoid_property", + Submonoid_def + |> SPEC_ALL + |> REWRITE_RULE [ASSUME ``h:'a monoid << g``] + |> CONJUNCTS + |> (fn thl => List.take(thl, 2) @ List.drop(thl, 3)) + |> LIST_CONJ + |> DISCH_ALL + |> Q.GEN `h` |> Q.GEN `g`); +val submonoid_property = |- !g h. h << g ==> Monoid h /\ Monoid g /\ ($o = $* ) /\ (#i = #e) +*) + +(* Theorem: properties of submonoid *) +(* Proof: Assume h << g, then derive all consequences of definition. *) +val submonoid_property = store_thm( + "submonoid_property", + ``!(g:'a monoid) h. h << g ==> Monoid h /\ Monoid g /\ H SUBSET G /\ + (!x y. x IN H /\ y IN H ==> (x o y = x * y)) /\ (#i = #e)``, + rw_tac std_ss[Submonoid_def]); + +(* Theorem: h << g ==> H SUBSET G *) +(* Proof: by Submonoid_def *) +val submonoid_carrier_subset = store_thm( + "submonoid_carrier_subset", + ``!(g:'a monoid) h. Submonoid h g ==> H SUBSET G``, + rw[Submonoid_def]); + +(* Theorem: elements in submonoid are also in monoid. *) +(* Proof: Since h << g ==> H SUBSET G by Submonoid_def. *) +val submonoid_element = store_thm( + "submonoid_element", + ``!(g:'a monoid) h. h << g ==> !x. x IN H ==> x IN G``, + rw_tac std_ss[Submonoid_def, SUBSET_DEF]); + +(* export simple result *) +val _ = export_rewrites ["submonoid_element"]; + +(* Theorem: h << g ==> (h.op = $* ) *) +(* Proof: by Subgroup_def *) +val submonoid_op = store_thm( + "submonoid_op", + ``!(g:'a monoid) h. h << g ==> (h.op = g.op)``, + rw[Submonoid_def]); + +(* Theorem: h << g ==> #i = #e *) +(* Proof: by Submonoid_def. *) +val submonoid_id = store_thm( + "submonoid_id", + ``!(g:'a monoid) h. h << g ==> (#i = #e)``, + rw_tac std_ss[Submonoid_def]); + +(* export simple results *) +val _ = export_rewrites["submonoid_id"]; + +(* Theorem: h << g ==> !x. x IN H ==> !n. h.exp x n = x ** n *) +(* Proof: by induction on n. + Base: h.exp x 0 = x ** 0 + LHS = h.exp x 0 + = h.id by monoid_exp_0 + = #e by submonoid_id + = x ** 0 by monoid_exp_0 + = RHS + Step: h.exp x n = x ** n ==> h.exp x (SUC n) = x ** SUC n + LHS = h.exp x (SUC n) + = h.op x (h.exp x n) by monoid_exp_SUC + = x * (h.exp x n) by submonoid_property + = x * x ** n by induction hypothesis + = x ** SUC n by monoid_exp_SUC + = RHS +*) +val submonoid_exp = store_thm( + "submonoid_exp", + ``!(g:'a monoid) h. h << g ==> !x. x IN H ==> !n. h.exp x n = x ** n``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + `h.exp x (SUC n) = h.op x (h.exp x n)` by rw_tac std_ss[monoid_exp_SUC] >> + `_ = x * (h.exp x n)` by metis_tac[submonoid_property, monoid_exp_element] >> + `_ = x * (x ** n)` by rw[] >> + `_ = x ** (SUC n)` by rw_tac std_ss[monoid_exp_SUC] >> + rw[]); + +(* Theorem: A submonoid h of g implies identity is a homomorphism from h to g. + or h << g ==> Monoid h /\ Monoid g /\ submonoid h g *) +(* Proof: + h << g ==> Monoid h /\ Monoid g by Submonoid_def + together with + H SUBSET G /\ ($o = $* ) /\ (#i = #e) by Submonoid_def + ==> !x. x IN H ==> x IN G /\ + !x y. x IN H /\ y IN H ==> (x o y = x * y) /\ + #i = #e by SUBSET_DEF + ==> MonoidHomo I h g by MonoidHomo_def, f = I. + ==> submonoid h g by submonoid_def +*) +val submonoid_homomorphism = store_thm( + "submonoid_homomorphism", + ``!(g:'a monoid) h. h << g ==> Monoid h /\ Monoid g /\ submonoid h g``, + rw_tac std_ss[Submonoid_def, submonoid_def, MonoidHomo_def, SUBSET_DEF]); + +(* original: +g `!(g:'a monoid) h. h << g = Monoid h /\ Monoid g /\ submonoid h g`; +e (rw_tac std_ss[Submonoid_def, submonoid_def, MonoidHomo_def, SUBSET_DEF, EQ_IMP_THM]); + +The only-if part (<==) cannot be proved: +Note Submonoid_def uses h.op = g.op, +but submonoid_def uses homomorphism I, and so cannot show this for any x y. +*) + +(* Theorem: h << g ==> !x. x IN H ==> (order h x = ord x) *) +(* Proof: + Note Monoid g /\ Monoid h /\ submonoid h g by submonoid_homomorphism, h << g + Thus !x. x IN H ==> (order h x = ord x) by submonoid_order_eqn +*) +val submonoid_order = store_thm( + "submonoid_order", + ``!(g:'a monoid) h. h << g ==> !x. x IN H ==> (order h x = ord x)``, + metis_tac[submonoid_homomorphism, submonoid_order_eqn]); + +(* Theorem: Monoid g ==> !h. Submonoid h g <=> + H SUBSET G /\ (!x y. x IN H /\ y IN H ==> h.op x y IN H) /\ (h.id IN H) /\ (h.op = $* ) /\ (h.id = #e) *) +(* Proof: + By Submonoid_def, EQ_IMP_THM, this is to show: + (1) x IN H /\ y IN H ==> x * y IN H, true by monoid_op_element + (2) #e IN H, true by monoid_id_element + (3) Monoid h + By Monoid_def, this is to show: + (1) x IN H /\ y IN H /\ z IN H + ==> x * y * z = x * (y * z), true by monoid_assoc, SUBSET_DEF + (2) x IN H ==> #e * x = x, true by monoid_lid, SUBSET_DEF + (3) x IN H ==> x * #e = x, true by monoid_rid, SUBSET_DEF +*) +val submonoid_alt = store_thm( + "submonoid_alt", + ``!g:'a monoid. Monoid g ==> !h. Submonoid h g <=> + H SUBSET G /\ (* subset *) + (!x y. x IN H /\ y IN H ==> h.op x y IN H) /\ (* closure *) + (h.id IN H) /\ (* has identity *) + (h.op = g.op ) /\ (h.id = #e)``, + rw_tac std_ss[Submonoid_def, EQ_IMP_THM] >- + metis_tac[monoid_op_element] >- + metis_tac[monoid_id_element] >> + rw_tac std_ss[Monoid_def] >- + fs[monoid_assoc, SUBSET_DEF] >- + fs[monoid_lid, SUBSET_DEF] >> + fs[monoid_rid, SUBSET_DEF]); + +(* ------------------------------------------------------------------------- *) +(* Submonoid Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: Monoid g ==> g << g *) +(* Proof: by Submonoid_def, SUBSET_REFL *) +val submonoid_reflexive = store_thm( + "submonoid_reflexive", + ``!g:'a monoid. Monoid g ==> g << g``, + rw_tac std_ss[Submonoid_def, SUBSET_REFL]); + +val monoid_component_equality = DB.fetch "-" "monoid_component_equality"; + +(* Theorem: h << g /\ g << h ==> (h = g) *) +(* Proof: + Since h << g ==> Monoid h /\ Monoid g /\ H SUBSET G /\ ($o = $* ) /\ (#i = #e) by Submonoid_def + and g << h ==> Monoid g /\ Monoid h /\ G SUBSET H /\ ($* = $o) /\ (#e = #i) by Submonoid_def + Now, H SUBSET G /\ G SUBSET H ==> H = G by SUBSET_ANTISYM + Hence h = g by monoid_component_equality +*) +val submonoid_antisymmetric = store_thm( + "submonoid_antisymmetric", + ``!g h:'a monoid. h << g /\ g << h ==> (h = g)``, + rw_tac std_ss[Submonoid_def] >> + full_simp_tac bool_ss[monoid_component_equality, SUBSET_ANTISYM]); + +(* Theorem: k << h /\ h << g ==> k << g *) +(* Proof: by Submonoid_def and SUBSET_TRANS *) +val submonoid_transitive = store_thm( + "submonoid_transitive", + ``!g h k:'a monoid. k << h /\ h << g ==> k << g``, + rw_tac std_ss[Submonoid_def] >> + metis_tac[SUBSET_TRANS]); + +(* Theorem: h << g ==> Monoid h *) +(* Proof: by Submonoid_def. *) +val submonoid_monoid = store_thm( + "submonoid_monoid", + ``!g h:'a monoid. h << g ==> Monoid h``, + rw[Submonoid_def]); + +(* ------------------------------------------------------------------------- *) +(* Submonoid Intersection *) +(* ------------------------------------------------------------------------- *) + +(* Define intersection of monoids *) +val monoid_intersect_def = Define` + monoid_intersect (g:'a monoid) (h:'a monoid) = + <| carrier := G INTER H; + op := $*; (* g.op *) + id := #e (* g.id *) + |> +`; + +val _ = overload_on ("mINTER", ``monoid_intersect``); +val _ = set_fixity "mINTER" (Infix(NONASSOC, 450)); (* same as relation *) +(* +> monoid_intersect_def; +val it = |- !g h. g mINTER h = <|carrier := G INTER H; op := $*; id := #e|>: thm +*) + +(* Theorem: ((g mINTER h).carrier = G INTER H) /\ + ((g mINTER h).op = $* ) /\ ((g mINTER h).id = #e) *) +(* Proof: by monoid_intersect_def *) +val monoid_intersect_property = store_thm( + "monoid_intersect_property", + ``!g h:'a monoid. ((g mINTER h).carrier = G INTER H) /\ + ((g mINTER h).op = $* ) /\ ((g mINTER h).id = #e)``, + rw[monoid_intersect_def]); + +(* Theorem: !x. x IN (g mINTER h).carrier ==> x IN G /\ x IN H *) +(* Proof: + x IN (g mINTER h).carrier + ==> x IN G INTER H by monoid_intersect_def + ==> x IN G and x IN H by IN_INTER +*) +val monoid_intersect_element = store_thm( + "monoid_intersect_element", + ``!g h:'a monoid. !x. x IN (g mINTER h).carrier ==> x IN G /\ x IN H``, + rw[monoid_intersect_def, IN_INTER]); + +(* Theorem: (g mINTER h).id = #e *) +(* Proof: by monoid_intersect_def. *) +val monoid_intersect_id = store_thm( + "monoid_intersect_id", + ``!g h:'a monoid. (g mINTER h).id = #e``, + rw[monoid_intersect_def]); + +(* Theorem: h << g /\ k << g ==> + ((h mINTER k).carrier = H INTER K) /\ + (!x y. x IN H INTER K /\ y IN H INTER K ==> ((h mINTER k).op x y = x * y)) /\ + ((h mINTER k).id = #e) *) +(* Proof: + (h mINTER k).carrier = H INTER K by monoid_intersect_def + Hence x IN (h mINTER k).carrier ==> x IN H /\ x IN K by IN_INTER + and y IN (h mINTER k).carrier ==> y IN H /\ y IN K by IN_INTER + so (h mINTER k).op x y = x o y by monoid_intersect_def + = x * y by submonoid_property + and (h mINTER k).id = #i by monoid_intersect_def + = #e by submonoid_property +*) +val submonoid_intersect_property = store_thm( + "submonoid_intersect_property", + ``!g h k:'a monoid. h << g /\ k << g ==> + ((h mINTER k).carrier = H INTER K) /\ + (!x y. x IN H INTER K /\ y IN H INTER K ==> ((h mINTER k).op x y = x * y)) /\ + ((h mINTER k).id = #e)``, + rw[monoid_intersect_def, submonoid_property]); + +(* Theorem: h << g /\ k << g ==> Monoid (h mINTER k) *) +(* Proof: + By definitions, this is to show: + (1) x IN H INTER K /\ y IN H INTER K ==> x o y IN H INTER K + x IN H INTER K ==> x IN H /\ x IN K by IN_INTER + y IN H INTER K ==> y IN H /\ y IN K by IN_INTER + x IN H /\ y IN H ==> x o y IN H by monoid_op_element + x IN K /\ y IN K ==> k.op x y IN K by monoid_op_element + x o y = x * y by submonoid_property + k.op x y = x * y by submonoid_property + Hence x o y IN H INTER K by IN_INTER + (2) x IN H INTER K /\ y IN H INTER K /\ z IN H INTER K ==> (x o y) o z = x o y o z + x IN H INTER K ==> x IN H by IN_INTER + y IN H INTER K ==> y IN H by IN_INTER + z IN H INTER K ==> z IN H by IN_INTER + x IN H /\ y IN H ==> x o y IN H by monoid_op_element + y IN H /\ z IN H ==> y o z IN H by monoid_op_element + x, y, z IN H ==> x, y, z IN G by submonoid_element + LHS = (x o y) o z + = (x o y) * z by submonoid_property + = (x * y) * z by submonoid_property + = x * (y * z) by monoid_assoc + = x * (y o z) by submonoid_property + = x o (y o z) = RHS by submonoid_property + (3) #i IN H INTER K + #i IN H and #i = #e by monoid_id_element, submonoid_id + k.id IN K and k.id = #e by monoid_id_element, submonoid_id + Hence #e = #i IN H INTER K by IN_INTER + (4) x IN H INTER K ==> #i o x = x + x IN H INTER K ==> x IN H by IN_INTER + ==> x IN G by submonoid_element + #i IN H and #i = #e by monoid_id_element, submonoid_id + #i o x + = #i * x by submonoid_property + = #e * x by submonoid_id + = x by monoid_id + (5) x IN H INTER K ==> x o #i = x + x IN H INTER K ==> x IN H by IN_INTER + ==> x IN G by submonoid_element + #i IN H and #i = #e by monoid_id_element, submonoid_id + x o #i + = x * #i by submonoid_property + = x * #e by submonoid_id + = x by monoid_id +*) +val submonoid_intersect_monoid = store_thm( + "submonoid_intersect_monoid", + ``!g h k:'a monoid. h << g /\ k << g ==> Monoid (h mINTER k)``, + rpt strip_tac >> + `Monoid h /\ Monoid k /\ Monoid g` by metis_tac[submonoid_property] >> + rw_tac std_ss[Monoid_def, monoid_intersect_def] >| [ + `x IN H /\ x IN K /\ y IN H /\ y IN K` by metis_tac[IN_INTER] >> + `x o y IN H /\ (x o y = x * y)` by metis_tac[submonoid_property, monoid_op_element] >> + `k.op x y IN K /\ (k.op x y = x * y)` by metis_tac[submonoid_property, monoid_op_element] >> + metis_tac[IN_INTER], + `x IN H /\ y IN H /\ z IN H` by metis_tac[IN_INTER] >> + `x IN G /\ y IN G /\ z IN G` by metis_tac[submonoid_element] >> + `x o y IN H /\ y o z IN H` by metis_tac[monoid_op_element] >> + `(x o y) o z = (x * y) * z` by metis_tac[submonoid_property] >> + `x o (y o z) = x * (y * z)` by metis_tac[submonoid_property] >> + rw[monoid_assoc], + metis_tac[IN_INTER, submonoid_id, monoid_id_element], + metis_tac[submonoid_property, monoid_id, submonoid_element, IN_INTER, monoid_id_element], + metis_tac[submonoid_property, monoid_id, submonoid_element, IN_INTER, monoid_id_element] + ]); + +(* Theorem: h << g /\ k << g ==> (h mINTER k) << g *) +(* Proof: + By Submonoid_def, this is to show: + (1) Monoid (h mINTER k), true by submonoid_intersect_monoid + (2) (h mINTER k).carrier SUBSET G + Since (h mINTER k).carrier = H INTER K by submonoid_intersect_property + and (H INTER K) SUBSET H by INTER_SUBSET + and h << g ==> H SUBSET G by submonoid_property + Hence (h mINTER k).carrier SUBSET G by SUBSET_TRANS + (3) (h mINTER k).op = $* + (h mINTER k).op = $o by monoid_intersect_def + = $* by Submonoid_def + (4) (h mINTER k).id = #e + (h mINTER k).id = #i by monoid_intersect_def + = #e by Submonoid_def +*) +val submonoid_intersect_submonoid = store_thm( + "submonoid_intersect_submonoid", + ``!g h k:'a monoid. h << g /\ k << g ==> (h mINTER k) << g``, + rpt strip_tac >> + `Monoid h /\ Monoid k /\ Monoid g` by metis_tac[submonoid_property] >> + rw[Submonoid_def] >| [ + metis_tac[submonoid_intersect_monoid], + `(h mINTER k).carrier = H INTER K` by metis_tac[submonoid_intersect_property] >> + `H SUBSET G` by rw[submonoid_property] >> + metis_tac[INTER_SUBSET, SUBSET_TRANS], + `(h mINTER k).op = $o` by rw[monoid_intersect_def] >> + metis_tac[Submonoid_def], + `(h mINTER k).id = #i` by rw[monoid_intersect_def] >> + metis_tac[Submonoid_def] + ]); + +(* ------------------------------------------------------------------------- *) +(* Submonoid Big Intersection *) +(* ------------------------------------------------------------------------- *) + +(* Define intersection of submonoids of a monoid *) +val submonoid_big_intersect_def = Define` + submonoid_big_intersect (g:'a monoid) = + <| carrier := BIGINTER (IMAGE (\h. H) {h | h << g}); + op := $*; (* g.op *) + id := #e (* g.id *) + |> +`; + +val _ = overload_on ("smbINTER", ``submonoid_big_intersect``); +(* +> submonoid_big_intersect_def; +val it = |- !g. smbINTER g = + <|carrier := BIGINTER (IMAGE (\h. H) {h | h << g}); op := $*; id := #e|>: thm +*) + +(* Theorem: ((smbINTER g).carrier = BIGINTER (IMAGE (\h. H) {h | h << g})) /\ + (!x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> ((smbINTER g).op x y = x * y)) /\ + ((smbINTER g).id = #e) *) +(* Proof: by submonoid_big_intersect_def. *) +val submonoid_big_intersect_property = store_thm( + "submonoid_big_intersect_property", + ``!g:'a monoid. ((smbINTER g).carrier = BIGINTER (IMAGE (\h. H) {h | h << g})) /\ + (!x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> ((smbINTER g).op x y = x * y)) /\ + ((smbINTER g).id = #e)``, + rw[submonoid_big_intersect_def]); + +(* Theorem: x IN (smbINTER g).carrier <=> (!h. h << g ==> x IN H) *) +(* Proof: + x IN (smbINTER g).carrier + <=> x IN BIGINTER (IMAGE (\h. H) {h | h << g}) by submonoid_big_intersect_def + <=> !P. P IN (IMAGE (\h. H) {h | h << g}) ==> x IN P by IN_BIGINTER + <=> !P. ?h. (P = H) /\ h IN {h | h << g}) ==> x IN P by IN_IMAGE + <=> !P. ?h. (P = H) /\ h << g) ==> x IN P by GSPECIFICATION + <=> !h. h << g ==> x IN H +*) +val submonoid_big_intersect_element = store_thm( + "submonoid_big_intersect_element", + ``!g:'a monoid. !x. x IN (smbINTER g).carrier <=> (!h. h << g ==> x IN H)``, + rw[submonoid_big_intersect_def] >> + metis_tac[]); + +(* Theorem: x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> (smbINTER g).op x y IN (smbINTER g).carrier *) +(* Proof: + Since x IN (smbINTER g).carrier, !h. h << g ==> x IN H by submonoid_big_intersect_element + also y IN (smbINTER g).carrier, !h. h << g ==> y IN H by submonoid_big_intersect_element + Now !h. h << g ==> x o y IN H by Submonoid_def, monoid_op_element + ==> x * y IN H by submonoid_property + Now, (smbINTER g).op x y = x * y by submonoid_big_intersect_property + Hence (smbINTER g).op x y IN (smbINTER g).carrier by submonoid_big_intersect_element +*) +val submonoid_big_intersect_op_element = store_thm( + "submonoid_big_intersect_op_element", + ``!g:'a monoid. !x y. x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> + (smbINTER g).op x y IN (smbINTER g).carrier``, + rpt strip_tac >> + `!h. h << g ==> x IN H /\ y IN H` by metis_tac[submonoid_big_intersect_element] >> + `!h. h << g ==> x * y IN H` by metis_tac[Submonoid_def, monoid_op_element, submonoid_property] >> + `(smbINTER g).op x y = x * y` by rw[submonoid_big_intersect_property] >> + metis_tac[submonoid_big_intersect_element]); + +(* Theorem: (smbINTER g).id IN (smbINTER g).carrier *) +(* Proof: + !h. h << g ==> #i = #e by submonoid_id + !h. h << g ==> #i IN H by Submonoid_def, monoid_id_element + Now (smbINTER g).id = #e by submonoid_big_intersect_property + Hence !h. h << g ==> (smbINTER g).id IN H by above + or (smbINTER g).id IN (smbINTER g).carrier by submonoid_big_intersect_element +*) +val submonoid_big_intersect_has_id = store_thm( + "submonoid_big_intersect_has_id", + ``!g:'a monoid. (smbINTER g).id IN (smbINTER g).carrier``, + rpt strip_tac >> + `!h. h << g ==> (#i = #e)` by rw[submonoid_id] >> + `!h. h << g ==> #i IN H` by rw[Submonoid_def] >> + `(smbINTER g).id = #e` by metis_tac[submonoid_big_intersect_property] >> + metis_tac[submonoid_big_intersect_element]); + +(* Theorem: Monoid g ==> (smbINTER g).carrier SUBSET G *) +(* Proof: + By submonoid_big_intersect_def, this is to show: + Monoid g ==> BIGINTER (IMAGE (\h. H) {h | h << g}) SUBSET G + Let P = IMAGE (\h. H) {h | h << g}. + Since g << g by submonoid_reflexive + so G IN P by IN_IMAGE, definition of P. + Thus P <> {} by MEMBER_NOT_EMPTY. + Now h << g ==> H SUBSET G by submonoid_property + Hence P SUBSET G by BIGINTER_SUBSET +*) +val submonoid_big_intersect_subset = store_thm( + "submonoid_big_intersect_subset", + ``!g:'a monoid. Monoid g ==> (smbINTER g).carrier SUBSET G``, + rw[submonoid_big_intersect_def] >> + qabbrev_tac `P = IMAGE (\h. H) {h | h << g}` >> + (`!x. x IN P <=> ?h. (H = x) /\ h << g` by (rw[Abbr`P`] >> metis_tac[])) >> + `g << g` by rw[submonoid_reflexive] >> + `P <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + `!h:'a monoid. h << g ==> H SUBSET G` by rw[submonoid_property] >> + metis_tac[BIGINTER_SUBSET]); + +(* Theorem: Monoid g ==> Monoid (smbINTER g) *) +(* Proof: + Monoid g ==> (smbINTER g).carrier SUBSET G by submonoid_big_intersect_subset + By Monoid_def, this is to show: + (1) x IN (smbINTER g).carrier /\ y IN (smbINTER g).carrier ==> (smbINTER g).op x y IN (smbINTER g).carrier + True by submonoid_big_intersect_op_element. + (2) (smbINTER g).op ((smbINTER g).op x y) z = (smbINTER g).op x ((smbINTER g).op y z) + Since (smbINTER g).op x y IN (smbINTER g).carrier by submonoid_big_intersect_op_element + and (smbINTER g).op y z IN (smbINTER g).carrier by submonoid_big_intersect_op_element + So this is to show: (x * y) * z = x * (y * z) by submonoid_big_intersect_property + Since x IN G, y IN G and z IN G by SUBSET_DEF + This follows by monoid_assoc. + (3) (smbINTER g).id IN (smbINTER g).carrier + This is true by submonoid_big_intersect_has_id. + (4) x IN (smbINTER g).carrier ==> (smbINTER g).op (smbINTER g).id x = x + Since (smbINTER g).id IN (smbINTER g).carrier by submonoid_big_intersect_op_element + and (smbINTER g).id = #e by submonoid_big_intersect_property + also x IN G by SUBSET_DEF + (smbINTER g).op (smbINTER g).id x + = #e * x by submonoid_big_intersect_property + = x by monoid_id + (5) x IN (smbINTER g).carrier ==> (smbINTER g).op x (smbINTER g).id = x + Since (smbINTER g).id IN (smbINTER g).carrier by submonoid_big_intersect_op_element + and (smbINTER g).id = #e by submonoid_big_intersect_property + also x IN G by SUBSET_DEF + (smbINTER g).op x (smbINTER g).id + = x * #e by submonoid_big_intersect_property + = x by monoid_id +*) +val submonoid_big_intersect_monoid = store_thm( + "submonoid_big_intersect_monoid", + ``!g:'a monoid. Monoid g ==> Monoid (smbINTER g)``, + rpt strip_tac >> + `(smbINTER g).carrier SUBSET G` by rw[submonoid_big_intersect_subset] >> + rw_tac std_ss[Monoid_def] >| [ + metis_tac[submonoid_big_intersect_op_element], + `(smbINTER g).op x y IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_op_element] >> + `(smbINTER g).op y z IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_op_element] >> + `(x * y) * z = x * (y * z)` suffices_by rw[submonoid_big_intersect_property] >> + `x IN G /\ y IN G /\ z IN G` by metis_tac[SUBSET_DEF] >> + rw[monoid_assoc], + metis_tac[submonoid_big_intersect_has_id], + `(smbINTER g).id = #e` by rw[submonoid_big_intersect_property] >> + `(smbINTER g).id IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_has_id] >> + `#e * x = x` suffices_by rw[submonoid_big_intersect_property] >> + `x IN G` by metis_tac[SUBSET_DEF] >> + rw[], + `(smbINTER g).id = #e` by rw[submonoid_big_intersect_property] >> + `(smbINTER g).id IN (smbINTER g).carrier` by metis_tac[submonoid_big_intersect_has_id] >> + `x * #e = x` suffices_by rw[submonoid_big_intersect_property] >> + `x IN G` by metis_tac[SUBSET_DEF] >> + rw[] + ]); + +(* Theorem: Monoid g ==> (smbINTER g) << g *) +(* Proof: + By Submonoid_def, this is to show: + (1) Monoid (smbINTER g) + True by submonoid_big_intersect_monoid. + (2) (smbINTER g).carrier SUBSET G + True by submonoid_big_intersect_subset. + (3) (smbINTER g).op = $* + True by submonoid_big_intersect_def + (4) (smbINTER g).id = #e + True by submonoid_big_intersect_def +*) +val submonoid_big_intersect_submonoid = store_thm( + "submonoid_big_intersect_submonoid", + ``!g:'a monoid. Monoid g ==> (smbINTER g) << g``, + rw_tac std_ss[Submonoid_def] >| [ + rw[submonoid_big_intersect_monoid], + rw[submonoid_big_intersect_subset], + rw[submonoid_big_intersect_def], + rw[submonoid_big_intersect_def] + ]); + +(* ------------------------------------------------------------------------- *) +(* Monoid Instances Documentation *) +(* ------------------------------------------------------------------------- *) +(* Monoid Data type: + The generic symbol for monoid data is g. + g.carrier = Carrier set of monoid + g.op = Binary operation of monoid + g.id = Identity element of monoid +*) +(* Definitions and Theorems (# are exported, ! in computeLib): + + The trivial monoid: + trivial_monoid_def |- !e. trivial_monoid e = <|carrier := {e}; id := e; op := (\x y. e)|> + trivial_monoid |- !e. FiniteAbelianMonoid (trivial_monoid e) + + The monoid of addition modulo n: + plus_mod_def |- !n. plus_mod n = + <|carrier := count n; + id := 0; + op := (\i j. (i + j) MOD n)|> + plus_mod_property |- !n. ((plus_mod n).carrier = count n) /\ + ((plus_mod n).op = (\i j. (i + j) MOD n)) /\ + ((plus_mod n).id = 0) /\ + (!x. x IN (plus_mod n).carrier ==> x < n) /\ + FINITE (plus_mod n).carrier /\ + (CARD (plus_mod n).carrier = n) + plus_mod_exp |- !n. 0 < n ==> !x k. (plus_mod n).exp x k = (k * x) MOD n + plus_mod_monoid |- !n. 0 < n ==> Monoid (plus_mod n) + plus_mod_abelian_monoid |- !n. 0 < n ==> AbelianMonoid (plus_mod n) + plus_mod_finite |- !n. FINITE (plus_mod n).carrier + plus_mod_finite_monoid |- !n. 0 < n ==> FiniteMonoid (plus_mod n) + plus_mod_finite_abelian_monoid |- !n. 0 < n ==> FiniteAbelianMonoid (plus_mod n) + + The monoid of multiplication modulo n: + times_mod_def |- !n. times_mod n = + <|carrier := count n; + id := if n = 1 then 0 else 1; + op := (\i j. (i * j) MOD n)|> +! times_mod_eval |- !n. ((times_mod n).carrier = count n) /\ + (!x y. (times_mod n).op x y = (x * y) MOD n) /\ + ((times_mod n).id = if n = 1 then 0 else 1) + times_mod_property |- !n. ((times_mod n).carrier = count n) /\ + ((times_mod n).op = (\i j. (i * j) MOD n)) /\ + ((times_mod n).id = if n = 1 then 0 else 1) /\ + (!x. x IN (times_mod n).carrier ==> x < n) /\ + FINITE (times_mod n).carrier /\ + (CARD (times_mod n).carrier = n) + times_mod_exp |- !n. 0 < n ==> !x k. (times_mod n).exp x k = (x MOD n) ** k MOD n + times_mod_monoid |- !n. 0 < n ==> Monoid (times_mod n) + times_mod_abelian_monoid |- !n. 0 < n ==> AbelianMonoid (times_mod n) + times_mod_finite |- !n. FINITE (times_mod n).carrier + times_mod_finite_monoid |- !n. 0 < n ==> FiniteMonoid (times_mod n) + times_mod_finite_abelian_monoid |- !n. 0 < n ==> FiniteAbelianMonoid (times_mod n) + + The Monoid of List concatenation: + lists_def |- lists = <|carrier := univ(:'a list); id := []; op := $++ |> + lists_monoid |- Monoid lists + + The Monoids from Set: + set_inter_def |- set_inter = <|carrier := univ(:'a -> bool); id := univ(:'a); op := $INTER|> + set_inter_monoid |- Monoid set_inter + set_inter_abelian_monoid |- AbelianMonoid set_inter + set_union_def |- set_union = <|carrier := univ(:'a -> bool); id := {}; op := $UNION|> + set_union_monoid |- Monoid set_union + set_union_abelian_monoid |- AbelianMonoid set_union + + Addition of numbers form a Monoid: + addition_monoid_def |- addition_monoid = <|carrier := univ(:num); op := $+; id := 0|> + addition_monoid_property |- (addition_monoid.carrier = univ(:num)) /\ + (addition_monoid.op = $+) /\ (addition_monoid.id = 0) + addition_monoid_abelian_monoid |- AbelianMonoid addition_monoid + addition_monoid_monoid |- Monoid addition_monoid + + Multiplication of numbers form a Monoid: + multiplication_monoid_def |- multiplication_monoid = <|carrier := univ(:num); op := $*; id := 1|> + multiplication_monoid_property |- (multiplication_monoid.carrier = univ(:num)) /\ + (multiplication_monoid.op = $* ) /\ (multiplication_monoid.id = 1) + multiplication_monoid_abelian_monoid |- AbelianMonoid multiplication_monoid + multiplication_monoid_monoid |- Monoid multiplication_monoid + + Powers of a fixed base form a Monoid: + power_monoid_def |- !b. power_monoid b = + <|carrier := {b ** j | j IN univ(:num)}; op := $*; id := 1|> + power_monoid_property |- !b. ((power_monoid b).carrier = {b ** j | j IN univ(:num)}) /\ + ((power_monoid b).op = $* ) /\ ((power_monoid b).id = 1) + power_monoid_abelian_monoid |- !b. AbelianMonoid (power_monoid b) + power_monoid_monoid |- !b. Monoid (power_monoid b) + + Logarithm is an isomorphism: + power_to_addition_homo |- !b. 1 < b ==> MonoidHomo (LOG b) (power_monoid b) addition_monoid + power_to_addition_iso |- !b. 1 < b ==> MonoidIso (LOG b) (power_monoid b) addition_monoid + + +*) +(* ------------------------------------------------------------------------- *) +(* The trivial monoid. *) +(* ------------------------------------------------------------------------- *) + +(* The trivial monoid: {#e} *) +val trivial_monoid_def = Define` + trivial_monoid e :'a monoid = + <| carrier := {e}; + id := e; + op := (\x y. e) + |> +`; + +(* +- type_of ``trivial_monoid e``; +> val it = ``:'a monoid`` : hol_type +> EVAL ``(trivial_monoid T).id``; +val it = |- (trivial_monoid T).id <=> T: thm +> EVAL ``(trivial_monoid 8).id``; +val it = |- (trivial_monoid 8).id = 8: thm +*) + +(* Theorem: {e} is indeed a monoid *) +(* Proof: check by definition. *) +val trivial_monoid = store_thm( + "trivial_monoid", + ``!e. FiniteAbelianMonoid (trivial_monoid e)``, + rw_tac std_ss[FiniteAbelianMonoid_def, AbelianMonoid_def, Monoid_def, trivial_monoid_def, IN_SING, FINITE_SING]); + +(* ------------------------------------------------------------------------- *) +(* The monoid of addition modulo n. *) +(* ------------------------------------------------------------------------- *) + +(* Additive Modulo Monoid *) +val plus_mod_def = Define` + plus_mod n :num monoid = + <| carrier := count n; + id := 0; + op := (\i j. (i + j) MOD n) + |> +`; +(* This monoid should be upgraded to add_mod, the additive group of ZN ring later. *) + +(* +- type_of ``plus_mod n``; +> val it = ``:num monoid`` : hol_type +> EVAL ``(plus_mod 7).op 5 6``; +val it = |- (plus_mod 7).op 5 6 = 4: thm +*) + +(* Theorem: properties of (plus_mod n) *) +(* Proof: by plus_mod_def. *) +val plus_mod_property = store_thm( + "plus_mod_property", + ``!n. ((plus_mod n).carrier = count n) /\ + ((plus_mod n).op = (\i j. (i + j) MOD n)) /\ + ((plus_mod n).id = 0) /\ + (!x. x IN (plus_mod n).carrier ==> x < n) /\ + (FINITE (plus_mod n).carrier) /\ + (CARD (plus_mod n).carrier = n)``, + rw[plus_mod_def]); + +(* Theorem: 0 < n ==> !x k. (plus_mod n).exp x k = (k * x) MOD n *) +(* Proof: + Expanding by definitions, this is to show: + FUNPOW (\j. (x + j) MOD n) k 0 = (k * x) MOD n + Applyy induction on k. + Base case: FUNPOW (\j. (x + j) MOD n) 0 0 = (0 * x) MOD n + LHS = FUNPOW (\j. (x + j) MOD n) 0 0 + = 0 by FUNPOW_0 + = 0 MOD n by ZERO_MOD, 0 < n + = (0 * x) MOD n by MULT + = RHS + Step case: FUNPOW (\j. (x + j) MOD n) (SUC k) 0 = (SUC k * x) MOD n + LHS = FUNPOW (\j. (x + j) MOD n) (SUC k) 0 + = (x + FUNPOW (\j. (x + j) MOD n) k 0) MOD n by FUNPOW_SUC + = (x + (k * x) MOD n) MOD n by induction hypothesis + = (x MOD n + (k * x) MOD n) MOD n by MOD_PLUS, MOD_MOD + = (x + k * x) MOD n by MOD_PLUS, MOD_MOD + = (k * x + x) MOD n by ADD_COMM + = ((SUC k) * x) MOD n by MULT + = RHS +*) +val plus_mod_exp = store_thm( + "plus_mod_exp", + ``!n. 0 < n ==> !x k. (plus_mod n).exp x k = (k * x) MOD n``, + rw_tac std_ss[plus_mod_def, monoid_exp_def] >> + Induct_on `k` >- + rw[] >> + rw_tac std_ss[FUNPOW_SUC] >> + metis_tac[MULT, ADD_COMM, MOD_PLUS, MOD_MOD]); + +(* Theorem: Additive Modulo n is a monoid. *) +(* Proof: check group definitions, use MOD_ADD_ASSOC. +*) +val plus_mod_monoid = store_thm( + "plus_mod_monoid", + ``!n. 0 < n ==> Monoid (plus_mod n)``, + rw_tac std_ss[plus_mod_def, Monoid_def, count_def, GSPECIFICATION, MOD_ADD_ASSOC]); + +(* Theorem: Additive Modulo n is an Abelian monoid. *) +(* Proof: by plus_mod_monoid and ADD_COMM. *) +val plus_mod_abelian_monoid = store_thm( + "plus_mod_abelian_monoid", + ``!n. 0 < n ==> AbelianMonoid (plus_mod n)``, + rw[plus_mod_monoid, plus_mod_def, AbelianMonoid_def, ADD_COMM]); + +(* Theorem: Additive Modulo n carrier is FINITE. *) +(* Proof: by FINITE_COUNT. *) +val plus_mod_finite = store_thm( + "plus_mod_finite", + ``!n. FINITE (plus_mod n).carrier``, + rw[plus_mod_def]); + +(* Theorem: Additive Modulo n is a FINITE monoid. *) +(* Proof: by plus_mod_monoid and plus_mod_finite. *) +val plus_mod_finite_monoid = store_thm( + "plus_mod_finite_monoid", + ``!n. 0 < n ==> FiniteMonoid (plus_mod n)``, + rw[FiniteMonoid_def, plus_mod_monoid, plus_mod_finite]); + +(* Theorem: Additive Modulo n is a FINITE Abelian monoid. *) +(* Proof: by plus_mod_abelian_monoid and plus_mod_finite. *) +val plus_mod_finite_abelian_monoid = store_thm( + "plus_mod_finite_abelian_monoid", + ``!n. 0 < n ==> FiniteAbelianMonoid (plus_mod n)``, + rw[FiniteAbelianMonoid_def, plus_mod_abelian_monoid, plus_mod_finite]); + +(* ------------------------------------------------------------------------- *) +(* The monoid of multiplication modulo n. *) +(* ------------------------------------------------------------------------- *) + +(* Multiplicative Modulo Monoid *) +val times_mod_def = zDefine` + times_mod n :num monoid = + <| carrier := count n; + id := if n = 1 then 0 else 1; + op := (\i j. (i * j) MOD n) + |> +`; +(* This monoid is taken as the multiplicative monoid of ZN ring later. *) +(* Use of zDefine to avoid incorporating into computeLib, by default. *) +(* Evaluation is given later in times_mod_eval. *) + +(* +- type_of ``times_mod n``; +> val it = ``:num monoid`` : hol_type +> EVAL ``(times_mod 7).op 5 6``; +val it = |- (times_mod 7).op 5 6 = 2: thm +*) + +(* Theorem: times_mod evaluation. *) +(* Proof: by times_mod_def. *) +val times_mod_eval = store_thm( + "times_mod_eval[compute]", + ``!n. ((times_mod n).carrier = count n) /\ + (!x y. (times_mod n).op x y = (x * y) MOD n) /\ + ((times_mod n).id = if n = 1 then 0 else 1)``, + rw_tac std_ss[times_mod_def]); + +(* Theorem: properties of (times_mod n) *) +(* Proof: by times_mod_def. *) +val times_mod_property = store_thm( + "times_mod_property", + ``!n. ((times_mod n).carrier = count n) /\ + ((times_mod n).op = (\i j. (i * j) MOD n)) /\ + ((times_mod n).id = if n = 1 then 0 else 1) /\ + (!x. x IN (times_mod n).carrier ==> x < n) /\ + (FINITE (times_mod n).carrier) /\ + (CARD (times_mod n).carrier = n)``, + rw[times_mod_def]); + +(* Theorem: 0 < n ==> !x k. (times_mod n).exp x k = ((x MOD n) ** k) MOD n *) +(* Proof: + Expanding by definitions, this is to show: + (1) n = 1 ==> FUNPOW (\j. (x * j) MOD n) k 0 = (x MOD n) ** k MOD n + or to show: FUNPOW (\j. 0) k 0 = 0 by MOD_1 + Note (\j. 0) = K 0 by FUN_EQ_THM + and FUNPOW (K 0) k 0 = 0 by FUNPOW_K + (2) n <> 1 ==> FUNPOW (\j. (x * j) MOD n) k 1 = (x MOD n) ** k MOD n + Note 1 < n by 0 < n /\ n <> 1 + By induction on k. + Base: FUNPOW (\j. (x * j) MOD n) 0 1 = (x MOD n) ** 0 MOD n + FUNPOW (\j. (x * j) MOD n) 0 1 + = 1 by FUNPOW_0 + = 1 MOD n by ONE_MOD, 1 < n + = ((x MOD n) ** 0) MOD n by EXP + Step: FUNPOW (\j. (x * j) MOD n) (SUC k) 1 = (x MOD n) ** SUC k MOD n + FUNPOW (\j. (x * j) MOD n) (SUC k) 1 + = (x * FUNPOW (\j. (x * j) MOD n) k 1) MOD n by FUNPOW_SUC + = (x * (x MOD n) ** k MOD n) MOD n by induction hypothesis + = ((x MOD n) * (x MOD n) ** k MOD n) MOD n by MOD_TIMES2, MOD_MOD, 0 < n + = ((x MOD n) * (x MOD n) ** k) MOD n by MOD_TIMES2, MOD_MOD, 0 < n + = ((x MOD n) ** SUC k) MOD n by EXP +*) +val times_mod_exp = store_thm( + "times_mod_exp", + ``!n. 0 < n ==> !x k. (times_mod n).exp x k = ((x MOD n) ** k) MOD n``, + rw_tac std_ss[times_mod_def, monoid_exp_def] >| [ + `(\j. 0) = K 0` by rw[FUN_EQ_THM] >> + metis_tac[FUNPOW_K], + `1 < n` by decide_tac >> + Induct_on `k` >- + rw[EXP, ONE_MOD] >> + `FUNPOW (\j. (x * j) MOD n) (SUC k) 1 = (x * FUNPOW (\j. (x * j) MOD n) k 1) MOD n` by rw_tac std_ss[FUNPOW_SUC] >> + metis_tac[EXP, MOD_TIMES2, MOD_MOD] + ]); + +(* Theorem: For n > 0, Multiplication Modulo n is a monoid. *) +(* Proof: check monoid definitions, use MOD_MULT_ASSOC. *) +Theorem times_mod_monoid: + !n. 0 < n ==> Monoid (times_mod n) +Proof + rw_tac std_ss[Monoid_def, times_mod_def, count_def, GSPECIFICATION] >| [ + rw[MOD_MULT_ASSOC], + decide_tac + ] +QED + +(* Theorem: For n > 0, Multiplication Modulo n is an Abelian monoid. *) +(* Proof: by times_mod_monoid and MULT_COMM. *) +val times_mod_abelian_monoid = store_thm( + "times_mod_abelian_monoid", + ``!n. 0 < n ==> AbelianMonoid (times_mod n)``, + rw[AbelianMonoid_def, times_mod_monoid, times_mod_def, MULT_COMM]); + +(* Theorem: Multiplication Modulo n carrier is FINITE. *) +(* Proof: by FINITE_COUNT. *) +val times_mod_finite = store_thm( + "times_mod_finite", + ``!n. FINITE (times_mod n).carrier``, + rw[times_mod_def]); + +(* Theorem: For n > 0, Multiplication Modulo n is a FINITE monoid. *) +(* Proof: by times_mod_monoid and times_mod_finite. *) +val times_mod_finite_monoid = store_thm( + "times_mod_finite_monoid", + ``!n. 0 < n ==> FiniteMonoid (times_mod n)``, + rw[times_mod_monoid, times_mod_finite, FiniteMonoid_def]); + +(* Theorem: For n > 0, Multiplication Modulo n is a FINITE Abelian monoid. *) +(* Proof: by times_mod_abelian_monoid and times_mod_finite. *) +val times_mod_finite_abelian_monoid = store_thm( + "times_mod_finite_abelian_monoid", + ``!n. 0 < n ==> FiniteAbelianMonoid (times_mod n)``, + rw[times_mod_abelian_monoid, times_mod_finite, FiniteAbelianMonoid_def, AbelianMonoid_def]); + +(* + +- EVAL ``(plus_mod 5).op 3 4``; +> val it = |- (plus_mod 5).op 3 4 = 2 : thm +- EVAL ``(plus_mod 5).id``; +> val it = |- (plus_mod 5).id = 0 : thm +- EVAL ``(times_mod 5).op 2 3``; +> val it = |- (times_mod 5).op 2 3 = 1 : thm +- EVAL ``(times_mod 5).op 5 3``; +> val it = |- (times_mod 5).id = 1 : thm +*) + +(* ------------------------------------------------------------------------- *) +(* The Monoid of List concatenation. *) +(* ------------------------------------------------------------------------- *) + +val lists_def = Define` + lists :'a list monoid = + <| carrier := UNIV; + id := []; + op := list$APPEND + |> +`; + +(* +> EVAL ``lists.op [1;2;3] [4;5]``; +val it = |- lists.op [1; 2; 3] [4; 5] = [1; 2; 3; 4; 5]: thm +*) + +(* Theorem: Lists form a Monoid *) +(* Proof: check definition. *) +val lists_monoid = store_thm( + "lists_monoid", + ``Monoid lists``, + rw_tac std_ss[Monoid_def, lists_def, IN_UNIV, GSPECIFICATION, APPEND, APPEND_NIL, APPEND_ASSOC]); + +(* after a long while ... + +val lists_monoid = store_thm( + "lists_monoid", + ``Monoid lists``, + rw[Monoid_def, lists_def]); +*) + +(* ------------------------------------------------------------------------- *) +(* The Monoids from Set. *) +(* ------------------------------------------------------------------------- *) + +(* The Monoid of set intersection *) +val set_inter_def = Define` + set_inter = <| carrier := UNIV; + id := UNIV; + op := (INTER) |> +`; + +(* +> EVAL ``set_inter.op {1;4;5;6} {5;6;8;9}``; +val it = |- set_inter.op {1; 4; 5; 6} {5; 6; 8; 9} = {5; 6}: thm +*) + +(* Theorem: set_inter is a Monoid. *) +(* Proof: check definitions. *) +val set_inter_monoid = store_thm( + "set_inter_monoid", + ``Monoid set_inter``, + rw[Monoid_def, set_inter_def, INTER_ASSOC]); + +val _ = export_rewrites ["set_inter_monoid"]; + +(* Theorem: set_inter is an abelian Monoid. *) +(* Proof: check definitions. *) +val set_inter_abelian_monoid = store_thm( + "set_inter_abelian_monoid", + ``AbelianMonoid set_inter``, + rw[AbelianMonoid_def, set_inter_def, INTER_COMM]); + +val _ = export_rewrites ["set_inter_abelian_monoid"]; + +(* The Monoid of set union *) +val set_union_def = Define` + set_union = <| carrier := UNIV; + id := EMPTY; + op := (UNION) |> +`; + +(* +> EVAL ``set_union.op {1;4;5;6} {5;6;8;9}``; +val it = |- set_union.op {1; 4; 5; 6} {5; 6; 8; 9} = {1; 4; 5; 6; 8; 9}: thm +*) + +(* Theorem: set_union is a Monoid. *) +(* Proof: check definitions. *) +val set_union_monoid = store_thm( + "set_union_monoid", + ``Monoid set_union``, + rw[Monoid_def, set_union_def, UNION_ASSOC]); + +val _ = export_rewrites ["set_union_monoid"]; + +(* Theorem: set_union is an abelian Monoid. *) +(* Proof: check definitions. *) +val set_union_abelian_monoid = store_thm( + "set_union_abelian_monoid", + ``AbelianMonoid set_union``, + rw[AbelianMonoid_def, set_union_def, UNION_COMM]); + +val _ = export_rewrites ["set_union_abelian_monoid"]; + +(* ------------------------------------------------------------------------- *) +(* Addition of numbers form a Monoid *) +(* ------------------------------------------------------------------------- *) + +(* Define the number addition monoid *) +val addition_monoid_def = Define` + addition_monoid = + <| carrier := univ(:num); + op := $+; + id := 0; + |> +`; + +(* +> EVAL ``addition_monoid.op 5 6``; +val it = |- addition_monoid.op 5 6 = 11: thm +*) + +(* Theorem: properties of addition_monoid *) +(* Proof: by addition_monoid_def *) +val addition_monoid_property = store_thm( + "addition_monoid_property", + ``(addition_monoid.carrier = univ(:num)) /\ + (addition_monoid.op = $+ ) /\ + (addition_monoid.id = 0)``, + rw[addition_monoid_def]); + +(* Theorem: AbelianMonoid (addition_monoid) *) +(* Proof: + By AbelianMonoid_def, Monoid_def, addition_monoid_def, this is to show: + (1) ?z. z = x + y. Take z = x + y. + (2) x + y + z = x + (y + z), true by ADD_ASSOC + (3) x + 0 = x /\ 0 + x = x, true by ADD, ADD_0 + (4) x + y = y + x, true by ADD_COMM +*) +val addition_monoid_abelian_monoid = store_thm( + "addition_monoid_abelian_monoid", + ``AbelianMonoid (addition_monoid)``, + rw_tac std_ss[AbelianMonoid_def, Monoid_def, addition_monoid_def, GSPECIFICATION, IN_UNIV] >> + simp[]); + +(* Theorem: Monoid (addition_monoid) *) +(* Proof: by addition_monoid_abelian_monoid, AbelianMonoid_def *) +val addition_monoid_monoid = store_thm( + "addition_monoid_monoid", + ``Monoid (addition_monoid)``, + metis_tac[addition_monoid_abelian_monoid, AbelianMonoid_def]); + +(* ------------------------------------------------------------------------- *) +(* Multiplication of numbers form a Monoid *) +(* ------------------------------------------------------------------------- *) + +(* Define the number multiplication monoid *) +val multiplication_monoid_def = Define` + multiplication_monoid = + <| carrier := univ(:num); + op := $*; + id := 1; + |> +`; + +(* +> EVAL ``multiplication_monoid.op 5 6``; +val it = |- multiplication_monoid.op 5 6 = 30: thm +*) + +(* Theorem: properties of multiplication_monoid *) +(* Proof: by multiplication_monoid_def *) +val multiplication_monoid_property = store_thm( + "multiplication_monoid_property", + ``(multiplication_monoid.carrier = univ(:num)) /\ + (multiplication_monoid.op = $* ) /\ + (multiplication_monoid.id = 1)``, + rw[multiplication_monoid_def]); + +(* Theorem: AbelianMonoid (multiplication_monoid) *) +(* Proof: + By AbelianMonoid_def, Monoid_def, multiplication_monoid_def, this is to show: + (1) ?z. z = x * y. Take z = x * y. + (2) x * y * z = x * (y * z), true by MULT_ASSOC + (3) x * 1 = x /\ 1 * x = x, true by MULT, MULT_1 + (4) x * y = y * x, true by MULT_COMM +*) +val multiplication_monoid_abelian_monoid = store_thm( + "multiplication_monoid_abelian_monoid", + ``AbelianMonoid (multiplication_monoid)``, + rw_tac std_ss[AbelianMonoid_def, Monoid_def, multiplication_monoid_def, GSPECIFICATION, IN_UNIV] >- + simp[] >> + simp[]); + +(* Theorem: Monoid (multiplication_monoid) *) +(* Proof: by multiplication_monoid_abelian_monoid, AbelianMonoid_def *) +val multiplication_monoid_monoid = store_thm( + "multiplication_monoid_monoid", + ``Monoid (multiplication_monoid)``, + metis_tac[multiplication_monoid_abelian_monoid, AbelianMonoid_def]); + +(* ------------------------------------------------------------------------- *) +(* Powers of a fixed base form a Monoid *) +(* ------------------------------------------------------------------------- *) + +(* Define the power monoid *) +val power_monoid_def = Define` + power_monoid (b:num) = + <| carrier := {b ** j | j IN univ(:num)}; + op := $*; + id := 1; + |> +`; + +(* +> EVAL ``(power_monoid 2).op (2 ** 3) (2 ** 4)``; +val it = |- (power_monoid 2).op (2 ** 3) (2 ** 4) = 128: thm +*) + +(* Theorem: properties of power monoid *) +(* Proof: by power_monoid_def *) +val power_monoid_property = store_thm( + "power_monoid_property", + ``!b. ((power_monoid b).carrier = {b ** j | j IN univ(:num)}) /\ + ((power_monoid b).op = $* ) /\ + ((power_monoid b).id = 1)``, + rw[power_monoid_def]); + + +(* Theorem: AbelianMonoid (power_monoid b) *) +(* Proof: + By AbelianMonoid_def, Monoid_def, power_monoid_def, this is to show: + (1) ?j''. b ** j * b ** j' = b ** j'' + Take j'' = j + j', true by EXP_ADD + (2) b ** j * b ** j' * b ** j'' = b ** j * (b ** j' * b ** j'') + True by EXP_ADD, ADD_ASSOC + (3) ?j. b ** j = 1 + or ?j. (b = 1) \/ (j = 0), true by j = 0. + (4) b ** j * b ** j' = b ** j' * b ** j + True by EXP_ADD, ADD_COMM +*) +val power_monoid_abelian_monoid = store_thm( + "power_monoid_abelian_monoid", + ``!b. AbelianMonoid (power_monoid b)``, + rw_tac std_ss[AbelianMonoid_def, Monoid_def, power_monoid_def, GSPECIFICATION, IN_UNIV] >- + metis_tac[EXP_ADD] >- + rw[EXP_ADD] >- + metis_tac[] >> + rw[EXP_ADD]); + +(* Theorem: Monoid (power_monoid b) *) +(* Proof: by power_monoid_abelian_monoid, AbelianMonoid_def *) +val power_monoid_monoid = store_thm( + "power_monoid_monoid", + ``!b. Monoid (power_monoid b)``, + metis_tac[power_monoid_abelian_monoid, AbelianMonoid_def]); + +(* ------------------------------------------------------------------------- *) +(* Logarithm is an isomorphism from Power Monoid to Addition Monoid *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 1 < b ==> MonoidHomo (LOG b) (power_monoid b) (addition_monoid) *) +(* Proof: + By MonoidHomo_def, power_monoid_def, addition_monoid_def, this is to show: + (1) LOG b (b ** j * b ** j') = LOG b (b ** j) + LOG b (b ** j') + LOG b (b ** j * b ** j') + = LOG b (b ** (j + j')) by EXP_ADD + = j + j' by LOG_EXACT_EXP + = LOG b (b ** j) + LOG b (b ** j') by LOG_EXACT_EXP + (2) LOG b 1 = 0, true by LOG_1 +*) +val power_to_addition_homo = store_thm( + "power_to_addition_homo", + ``!b. 1 < b ==> MonoidHomo (LOG b) (power_monoid b) (addition_monoid)``, + rw[MonoidHomo_def, power_monoid_def, addition_monoid_def] >- + rw[LOG_EXACT_EXP, GSYM EXP_ADD] >> + rw[LOG_1]); + +(* Theorem: 1 < b ==> MonoidIso (LOG b) (power_monoid b) (addition_monoid) *) +(* Proof: + By MonoidIso_def, this is to show: + (1) MonoidHomo (LOG b) (power_monoid b) addition_monoid + This is true by power_to_addition_homo + (2) BIJ (LOG b) (power_monoid b).carrier addition_monoid.carrier + By BIJ_DEF, this is to show: + (1) INJ (LOG b) {b ** j | j IN univ(:num)} univ(:num) + By INJ_DEF, this is to show: + LOG b (b ** j) = LOG b (b ** j') ==> b ** j = b ** j' + LOG b (b ** j) = LOG b (b ** j') + ==> j = j' by LOG_EXACT_EXP + ==> b ** j = b ** j' + (2) SURJ (LOG b) {b ** j | j IN univ(:num)} univ(:num) + By SURJ_DEF, this is to show: + ?y. (?j. y = b ** j) /\ (LOG b y = x) + Let j = x, y = b ** x, then true by LOG_EXACT_EXP +*) +val power_to_addition_iso = store_thm( + "power_to_addition_iso", + ``!b. 1 < b ==> MonoidIso (LOG b) (power_monoid b) (addition_monoid)``, + rw[MonoidIso_def] >- + rw[power_to_addition_homo] >> + rw_tac std_ss[BIJ_DEF, power_monoid_def, addition_monoid_def] >| [ + rw[INJ_DEF] >> + rfs[LOG_EXACT_EXP], + rw[SURJ_DEF] >> + metis_tac[LOG_EXACT_EXP] + ]); + +(* ------------------------------------------------------------------------- *) +(* Theory about folding a monoid (or group) operation over a bag of elements *) +(* ------------------------------------------------------------------------- *) + +Overload GITBAG = ``\(g:'a monoid) s b. ITBAG g.op s b``; + +Theorem GITBAG_THM = + ITBAG_THM |> CONV_RULE SWAP_FORALL_CONV + |> INST_TYPE [beta |-> alpha] |> Q.SPEC`(g:'a monoid).op` + |> GEN_ALL + +Theorem GITBAG_EMPTY[simp]: + !g a. GITBAG g {||} a = a +Proof + rw[ITBAG_EMPTY] +QED + +Theorem GITBAG_INSERT: + !b. FINITE_BAG b ==> + !g x a. GITBAG g (BAG_INSERT x b) a = + GITBAG g (BAG_REST (BAG_INSERT x b)) + (g.op (BAG_CHOICE (BAG_INSERT x b)) a) +Proof + rw[ITBAG_INSERT] +QED + +Theorem SUBSET_COMMUTING_ITBAG_INSERT: + !f b t. + SET_OF_BAG b SUBSET t /\ closure_comm_assoc_fun f t /\ FINITE_BAG b ==> + !x a::t. ITBAG f (BAG_INSERT x b) a = ITBAG f b (f x a) +Proof + simp[RES_FORALL_THM] + \\ rpt gen_tac \\ strip_tac + \\ completeInduct_on `BAG_CARD b` + \\ rw[] + \\ simp[ITBAG_INSERT, BAG_REST_DEF, EL_BAG] + \\ qmatch_goalsub_abbrev_tac`{|c|}` + \\ `BAG_IN c (BAG_INSERT x b)` by PROVE_TAC[BAG_CHOICE_DEF, BAG_INSERT_NOT_EMPTY] + \\ fs[BAG_IN_BAG_INSERT] + \\ `?b0. b = BAG_INSERT c b0` by PROVE_TAC [BAG_IN_BAG_DELETE, BAG_DELETE] + \\ `BAG_DIFF (BAG_INSERT x b) {| c |} = BAG_INSERT x b0` + by SRW_TAC [][BAG_INSERT_commutes] + \\ pop_assum SUBST_ALL_TAC + \\ first_x_assum(qspec_then`BAG_CARD b0`mp_tac) + \\ `FINITE_BAG b0` by FULL_SIMP_TAC (srw_ss()) [] + \\ impl_keep_tac >- SRW_TAC [numSimps.ARITH_ss][BAG_CARD_THM] + \\ disch_then(qspec_then`b0`mp_tac) + \\ impl_tac >- simp[] + \\ impl_tac >- fs[SUBSET_DEF] + \\ impl_tac >- simp[] + \\ strip_tac + \\ first_assum(qspec_then`x`mp_tac) + \\ first_x_assum(qspec_then`c`mp_tac) + \\ impl_keep_tac >- fs[SUBSET_DEF] + \\ disch_then(qspec_then`f x a`mp_tac) + \\ impl_keep_tac >- metis_tac[closure_comm_assoc_fun_def] + \\ strip_tac + \\ impl_tac >- simp[] + \\ disch_then(qspec_then`f c a`mp_tac) + \\ impl_keep_tac >- metis_tac[closure_comm_assoc_fun_def] + \\ disch_then SUBST1_TAC + \\ simp[] + \\ metis_tac[closure_comm_assoc_fun_def] +QED + +Theorem COMMUTING_GITBAG_INSERT: + !g b. AbelianMonoid g /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET G ==> + !x a::(G). GITBAG g (BAG_INSERT x b) a = GITBAG g b (g.op x a) +Proof + rpt strip_tac + \\ irule SUBSET_COMMUTING_ITBAG_INSERT + \\ metis_tac[abelian_monoid_op_closure_comm_assoc_fun] +QED + +Theorem GITBAG_INSERT_THM = + SIMP_RULE(srw_ss())[RES_FORALL_THM, PULL_FORALL, AND_IMP_INTRO] + COMMUTING_GITBAG_INSERT + +Theorem GITBAG_UNION: + !g. AbelianMonoid g ==> + !b1. FINITE_BAG b1 ==> !b2. FINITE_BAG b2 /\ SET_OF_BAG b1 SUBSET G + /\ SET_OF_BAG b2 SUBSET G ==> + !a. a IN G ==> GITBAG g (BAG_UNION b1 b2) a = GITBAG g b2 (GITBAG g b1 a) +Proof + gen_tac \\ strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ rw[] + \\ simp[BAG_UNION_INSERT] + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ gs[SUBSET_DEF] + \\ simp[GSYM CONJ_ASSOC] + \\ conj_tac >- metis_tac[] + \\ first_x_assum irule + \\ simp[] + \\ fs[AbelianMonoid_def] +QED + +Theorem GITBAG_in_carrier: + !g. AbelianMonoid g ==> + !b. FINITE_BAG b ==> !a. SET_OF_BAG b SUBSET G /\ a IN G ==> GITBAG g b a IN G +Proof + ntac 2 strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ simp[] + \\ rpt strip_tac + \\ drule COMMUTING_GITBAG_INSERT + \\ disch_then (qspec_then`b`mp_tac) + \\ fs[SUBSET_DEF] + \\ simp[RES_FORALL_THM, PULL_FORALL] + \\ strip_tac + \\ last_x_assum irule + \\ metis_tac[monoid_op_element, AbelianMonoid_def] +QED + +Overload GBAG = ``\(g:'a monoid) b. GITBAG g b g.id``; + +Theorem GBAG_in_carrier: + !g b. AbelianMonoid g /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET G ==> GBAG g b IN G +Proof + rw[] + \\ irule GITBAG_in_carrier + \\ metis_tac[AbelianMonoid_def, monoid_id_element] +QED + +Theorem GITBAG_GBAG: + !g. AbelianMonoid g ==> + !b. FINITE_BAG b ==> !a. a IN g.carrier /\ SET_OF_BAG b SUBSET g.carrier ==> + GITBAG g b a = g.op a (GITBAG g b g.id) +Proof + ntac 2 strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ rw[] >- fs[AbelianMonoid_def] + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[] + \\ conj_asm1_tac >- fs[SUBSET_DEF, AbelianMonoid_def] + \\ irule EQ_TRANS + \\ qexists_tac`g.op (g.op e a) (GBAG g b)` + \\ conj_tac >- ( + first_x_assum irule + \\ metis_tac[AbelianMonoid_def, monoid_op_element] ) + \\ first_x_assum(qspec_then`e`mp_tac) + \\ simp[] + \\ `g.op e (#e) = e` by metis_tac[AbelianMonoid_def, monoid_id] + \\ pop_assum SUBST1_TAC + \\ disch_then SUBST1_TAC + \\ fs[AbelianMonoid_def] + \\ irule monoid_assoc + \\ simp[] + \\ irule GBAG_in_carrier + \\ simp[AbelianMonoid_def] +QED + +Theorem GBAG_UNION: + AbelianMonoid g /\ FINITE_BAG b1 /\ FINITE_BAG b2 /\ + SET_OF_BAG b1 SUBSET g.carrier /\ SET_OF_BAG b2 SUBSET g.carrier ==> + GBAG g (BAG_UNION b1 b2) = g.op (GBAG g b1) (GBAG g b2) +Proof + rpt strip_tac + \\ DEP_REWRITE_TAC[GITBAG_UNION] + \\ simp[] + \\ conj_tac >- fs[AbelianMonoid_def] + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] + \\ simp[] + \\ irule GBAG_in_carrier + \\ simp[] +QED + +Theorem GITBAG_BAG_IMAGE_op: + !g. AbelianMonoid g ==> + !b. FINITE_BAG b ==> + !p q a. IMAGE p (SET_OF_BAG b) SUBSET g.carrier /\ + IMAGE q (SET_OF_BAG b) SUBSET g.carrier /\ a IN g.carrier ==> + GITBAG g (BAG_IMAGE (\x. g.op (p x) (q x)) b) a = + g.op (GITBAG g (BAG_IMAGE p b) a) (GBAG g (BAG_IMAGE q b)) +Proof + ntac 2 strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ rw[] >- fs[AbelianMonoid_def] + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ conj_asm1_tac + >- ( + gs[SUBSET_DEF, PULL_EXISTS] + \\ gs[AbelianMonoid_def] ) + \\ qmatch_goalsub_abbrev_tac`GITBAG g bb aa` + \\ first_assum(qspecl_then[`p`,`q`,`aa`]mp_tac) + \\ impl_tac >- ( + fs[SUBSET_DEF, PULL_EXISTS, Abbr`aa`] + \\ fs[AbelianMonoid_def] ) + \\ simp[] + \\ disch_then kall_tac + \\ simp[Abbr`aa`] + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] + \\ conj_asm1_tac >- ( + fs[SUBSET_DEF, PULL_EXISTS] + \\ fs[AbelianMonoid_def] ) + \\ irule EQ_SYM + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] + \\ conj_asm1_tac >- fs[AbelianMonoid_def] + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG |> SIMP_RULE(srw_ss())[PULL_FORALL,AND_IMP_INTRO] + |> Q.SPECL[`g`,`b`,`g.op x y`]] + \\ simp[] + \\ fs[AbelianMonoid_def] + \\ qmatch_goalsub_abbrev_tac`_ * _ * gp * ( _ * gq)` + \\ `gp IN g.carrier /\ gq IN g.carrier` + by ( + unabbrev_all_tac + \\ conj_tac \\ irule GBAG_in_carrier + \\ fs[AbelianMonoid_def] ) + \\ drule monoid_assoc + \\ strip_tac \\ gs[] +QED + +Theorem IMP_GBAG_EQ_ID: + AbelianMonoid g ==> + !b. BAG_EVERY ((=) g.id) b ==> GBAG g b = g.id +Proof + rw[] + \\ `FINITE_BAG b` + by ( + Cases_on`b = {||}` \\ simp[] + \\ once_rewrite_tac[GSYM unibag_FINITE] + \\ rewrite_tac[FINITE_BAG_OF_SET] + \\ `SET_OF_BAG b = {g.id}` + by ( + rw[SET_OF_BAG, FUN_EQ_THM] + \\ fs[BAG_EVERY] + \\ rw[EQ_IMP_THM] + \\ Cases_on`b` \\ rw[] ) + \\ pop_assum SUBST1_TAC + \\ simp[]) + \\ qpat_x_assum`BAG_EVERY _ _` mp_tac + \\ pop_assum mp_tac + \\ qid_spec_tac`b` + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ rw[] \\ gs[] + \\ drule COMMUTING_GITBAG_INSERT + \\ disch_then drule + \\ impl_keep_tac + >- ( + fs[SUBSET_DEF, BAG_EVERY] + \\ fs[AbelianMonoid_def] + \\ metis_tac[monoid_id_element] ) + \\ simp[RES_FORALL_THM, PULL_FORALL, AND_IMP_INTRO] + \\ disch_then(qspecl_then[`#e`,`#e`]mp_tac) + \\ simp[] + \\ metis_tac[monoid_id_element, monoid_id_id, AbelianMonoid_def] +QED + +Theorem GITBAG_CONG: + !g. AbelianMonoid g ==> + !b. FINITE_BAG b ==> !b' a a'. FINITE_BAG b' /\ + a IN g.carrier /\ SET_OF_BAG b SUBSET g.carrier /\ SET_OF_BAG b' SUBSET g.carrier + /\ (!x. BAG_IN x (BAG_UNION b b') /\ x <> g.id ==> b x = b' x) + ==> + GITBAG g b a = GITBAG g b' a +Proof + ntac 2 strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT \\ rw[] + >- ( + fs[BAG_IN, BAG_INN, EMPTY_BAG] + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] + \\ simp[] + \\ irule EQ_TRANS + \\ qexists_tac`g.op a g.id` + \\ conj_tac >- fs[AbelianMonoid_def] + \\ AP_TERM_TAC + \\ irule EQ_SYM + \\ irule IMP_GBAG_EQ_ID + \\ simp[BAG_EVERY, BAG_IN, BAG_INN] + \\ metis_tac[]) + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[] + \\ fs[SET_OF_BAG_INSERT] + \\ Cases_on`e = g.id` + >- ( + fs[AbelianMonoid_def] + \\ first_x_assum irule + \\ simp[] + \\ fs[BAG_INSERT] + \\ metis_tac[] ) + \\ `BAG_IN e b'` + by ( + simp[BAG_IN, BAG_INN] + \\ fs[BAG_INSERT] + \\ first_x_assum(qspec_then`e`mp_tac) + \\ simp[] ) + \\ drule BAG_DECOMPOSE + \\ disch_then(qx_choose_then`b2`strip_assume_tac) + \\ pop_assum SUBST_ALL_TAC + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[] \\ fs[SET_OF_BAG_INSERT] + \\ first_x_assum irule \\ simp[] + \\ fs[BAG_INSERT, AbelianMonoid_def] + \\ qx_gen_tac`x` + \\ disch_then assume_tac + \\ first_x_assum(qspec_then`x`mp_tac) + \\ impl_tac >- metis_tac[] + \\ IF_CASES_TAC \\ simp[] +QED + +Theorem GITBAG_same_op: + g1.op = g2.op ==> + !b. FINITE_BAG b ==> + !a. GITBAG g1 b a = GITBAG g2 b a +Proof + strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ rw[GITBAG_THM] +QED + +Theorem GBAG_IMAGE_PARTITION: + AbelianMonoid g /\ FINITE s ==> + !b. FINITE_BAG b ==> + IMAGE f (SET_OF_BAG b) SUBSET G /\ + (!x. BAG_IN x b ==> ?P. P IN s /\ P x) /\ + (!x P1 P2. BAG_IN x b /\ P1 IN s /\ P2 IN s /\ P1 x /\ P2 x ==> P1 = P2) + ==> + GBAG g (BAG_IMAGE (λP. GBAG g (BAG_IMAGE f (BAG_FILTER P b))) (BAG_OF_SET s)) = + GBAG g (BAG_IMAGE f b) +Proof + strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ simp[] + \\ conj_tac + >- ( + irule IMP_GBAG_EQ_ID + \\ simp[BAG_EVERY] + \\ rw[] + \\ imp_res_tac BAG_IN_BAG_IMAGE_IMP + \\ fs[] ) + \\ rpt strip_tac + \\ fs[SET_OF_BAG_INSERT] + \\ `?P. P IN s /\ P e` by metis_tac[] + \\ `?s0. s = P INSERT s0 /\ P NOTIN s0` by metis_tac[DECOMPOSITION] + \\ BasicProvers.VAR_EQ_TAC + \\ simp[BAG_OF_SET_INSERT_NON_ELEMENT] + \\ DEP_REWRITE_TAC[BAG_IMAGE_FINITE_INSERT] + \\ qpat_x_assum`_ ==> _`mp_tac + \\ impl_tac >- metis_tac[] + \\ strip_tac + \\ conj_tac >- metis_tac[FINITE_INSERT, FINITE_BAG_OF_SET] + \\ qmatch_goalsub_abbrev_tac`BAG_IMAGE ff (BAG_OF_SET s0)` + \\ `BAG_IMAGE ff (BAG_OF_SET s0) = + BAG_IMAGE (\P. GBAG g (BAG_IMAGE f (BAG_FILTER P b))) (BAG_OF_SET s0)` + by ( + irule BAG_IMAGE_CONG + \\ simp[Abbr`ff`] + \\ rw[] + \\ metis_tac[IN_INSERT] ) + \\ simp[Abbr`ff`] + \\ pop_assum kall_tac + \\ rpt(first_x_assum(qspec_then`ARB`kall_tac)) + \\ pop_assum mp_tac + \\ simp[BAG_OF_SET_INSERT_NON_ELEMENT] + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ fs[AbelianMonoid_def] + \\ conj_asm1_tac >- fs[SUBSET_DEF, PULL_EXISTS] + \\ conj_asm1_tac >- ( + fs[SUBSET_DEF, PULL_EXISTS] + \\ rw[] \\ irule GITBAG_in_carrier + \\ fs[SUBSET_DEF, PULL_EXISTS, AbelianMonoid_def] ) + \\ simp[] + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[] + \\ conj_asm1_tac + >- ( + simp[AbelianMonoid_def] + \\ irule GITBAG_in_carrier + \\ simp[AbelianMonoid_def] ) + \\ simp[] + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] \\ simp[] \\ strip_tac + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] \\ simp[] + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] \\ simp[] + \\ DEP_REWRITE_TAC[monoid_assoc] + \\ simp[] + \\ conj_tac >- ( irule GBAG_in_carrier \\ simp[] ) + \\ irule EQ_SYM + \\ irule GITBAG_GBAG + \\ simp[] +QED + +Theorem GBAG_PARTITION: + AbelianMonoid g /\ FINITE s /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET G /\ + (!x. BAG_IN x b ==> ?P. P IN s /\ P x) /\ + (!x P1 P2. BAG_IN x b /\ P1 IN s /\ P2 IN s /\ P1 x /\ P2 x ==> P1 = P2) + ==> + GBAG g (BAG_IMAGE (λP. GBAG g (BAG_FILTER P b)) (BAG_OF_SET s)) = GBAG g b +Proof + strip_tac + \\ `!P. FINITE_BAG (BAG_FILTER P b)` by metis_tac[FINITE_BAG_FILTER] + \\ `GBAG g b = GBAG g (BAG_IMAGE I b)` by metis_tac[BAG_IMAGE_FINITE_I] + \\ pop_assum SUBST1_TAC + \\ `(λP. GBAG g (BAG_FILTER P b)) = λP. GBAG g (BAG_IMAGE I (BAG_FILTER P b))` + by simp[FUN_EQ_THM] + \\ pop_assum SUBST1_TAC + \\ irule GBAG_IMAGE_PARTITION + \\ simp[] + \\ metis_tac[] +QED + +Theorem GBAG_IMAGE_FILTER: + AbelianMonoid g ==> + !b. FINITE_BAG b ==> IMAGE f (SET_OF_BAG b INTER P) SUBSET g.carrier ==> + GBAG g (BAG_IMAGE f (BAG_FILTER P b)) = + GBAG g (BAG_IMAGE (\x. if P x then f x else g.id) b) +Proof + strip_tac + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ rw[] + \\ fs[SUBSET_DEF, PULL_EXISTS] + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[SUBSET_DEF, PULL_EXISTS] + \\ conj_asm1_tac + >- ( + rw[] + \\ fs[AbelianMonoid_def] + \\ metis_tac[IN_DEF] ) + \\ irule EQ_SYM + \\ DEP_ONCE_REWRITE_TAC[GITBAG_GBAG] + \\ simp[SUBSET_DEF, PULL_EXISTS] + \\ fs[AbelianMonoid_def] + \\ qmatch_goalsub_abbrev_tac`_ * gg` + \\ `gg IN g.carrier` + by ( + simp[Abbr`gg`] + \\ irule GBAG_in_carrier + \\ simp[AbelianMonoid_def, SUBSET_DEF, PULL_EXISTS] ) + \\ IF_CASES_TAC \\ gs[] + \\ simp[Abbr`gg`] + \\ irule EQ_SYM + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[PULL_EXISTS, SUBSET_DEF, AbelianMonoid_def] + \\ conj_tac >- metis_tac[] + \\ qpat_x_assum`_ = _`(assume_tac o SYM) \\ simp[] + \\ irule GITBAG_GBAG + \\ simp[SUBSET_DEF, PULL_EXISTS] + \\ metis_tac[AbelianMonoid_def] +QED + +Theorem GBAG_INSERT: + AbelianMonoid g /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET g.carrier /\ x IN g.carrier ==> + GBAG g (BAG_INSERT x b) = g.op x (GBAG g b) +Proof + strip_tac + \\ DEP_REWRITE_TAC[GITBAG_INSERT_THM] + \\ simp[] + \\ `Monoid g` by fs[AbelianMonoid_def] \\ simp[] + \\ irule GITBAG_GBAG + \\ simp[] +QED + +Theorem MonoidHomo_GBAG: + AbelianMonoid g /\ AbelianMonoid h /\ + MonoidHomo f g h /\ FINITE_BAG b /\ SET_OF_BAG b SUBSET g.carrier ==> + f (GBAG g b) = GBAG h (BAG_IMAGE f b) +Proof + strip_tac + \\ ntac 2 (pop_assum mp_tac) + \\ qid_spec_tac`b` + \\ ho_match_mp_tac STRONG_FINITE_BAG_INDUCT + \\ simp[] + \\ fs[MonoidHomo_def] + \\ rpt strip_tac + \\ DEP_REWRITE_TAC[GBAG_INSERT] + \\ simp[] + \\ fs[SUBSET_DEF, PULL_EXISTS] + \\ `GBAG g b IN g.carrier` suffices_by metis_tac[] + \\ irule GBAG_in_carrier + \\ simp[SUBSET_DEF, PULL_EXISTS] +QED + +Theorem IMP_GBAG_EQ_EXP: + AbelianMonoid g /\ x IN g.carrier /\ SET_OF_BAG b SUBSET {x} ==> + GBAG g b = g.exp x (b x) +Proof + Induct_on`b x` \\ rw[] + >- ( + Cases_on`b = {||}` \\ simp[] + \\ fs[SUBSET_DEF] + \\ Cases_on`b` \\ fs[BAG_INSERT] ) + \\ `b = BAG_INSERT x (b - {|x|})` + by ( + simp[BAG_EXTENSION] + \\ simp[BAG_INN, BAG_INSERT, EMPTY_BAG, BAG_DIFF] + \\ rw[] ) + \\ qmatch_asmsub_abbrev_tac`BAG_INSERT x b0` + \\ fs[] + \\ `b0 x = v` by fs[BAG_INSERT] + \\ first_x_assum(qspecl_then[`b0`,`x`]mp_tac) + \\ simp[] + \\ impl_tac >- fs[SUBSET_DEF] + \\ DEP_REWRITE_TAC[GBAG_INSERT] + \\ simp[] + \\ simp[BAG_INSERT] + \\ rewrite_tac[GSYM arithmeticTheory.ADD1] + \\ simp[] + \\ DEP_REWRITE_TAC[GSYM FINITE_SET_OF_BAG] + \\ `SET_OF_BAG b0 SUBSET {x}` by fs[SUBSET_DEF] + \\ `FINITE {x}` by simp[] + \\ reverse conj_tac >- fs[SUBSET_DEF] + \\ metis_tac[SUBSET_FINITE] +QED + +(* ------------------------------------------------------------------------- *) + +(* export theory at end *) +val _ = export_theory(); + +(*===========================================================================*) diff --git a/src/algebra/numberScript.sml b/src/algebra/numberScript.sml new file mode 100644 index 0000000000..2ce602838d --- /dev/null +++ b/src/algebra/numberScript.sml @@ -0,0 +1,10245 @@ +(* ------------------------------------------------------------------------- *) +(* Elementary Number Theory - a collection of useful results for numbers *) +(* *) +(* Author: (Joseph) Hing-Lun Chan (Australian National University, 2019) *) +(* ------------------------------------------------------------------------- *) + +open HolKernel boolLib Parse bossLib; + +open prim_recTheory arithmeticTheory dividesTheory gcdTheory gcdsetTheory + logrootTheory pred_setTheory listTheory rich_listTheory listRangeTheory + indexedListsTheory; + +val _ = new_theory "number"; + +(* Overload non-decreasing functions with different arity. *) +val _ = overload_on("MONO", ``\f:num -> num. !x y. x <= y ==> f x <= f y``); +val _ = overload_on("MONO2", + ``\f:num -> num -> num. + !x1 y1 x2 y2. x1 <= x2 /\ y1 <= y2 ==> f x1 y1 <= f x2 y2``); +val _ = overload_on("MONO3", + ``\f:num -> num -> num -> num. + !x1 y1 z1 x2 y2 z2. x1 <= x2 /\ y1 <= y2 /\ z1 <= z2 ==> + f x1 y1 z1 <= f x2 y2 z2``); + +(* Overload non-increasing functions with different arity. *) +val _ = overload_on("RMONO", ``\f:num -> num. !x y. x <= y ==> f y <= f x``); +val _ = overload_on("RMONO2", + ``\f:num -> num -> num. + !x1 y1 x2 y2. x1 <= x2 /\ y1 <= y2 ==> f x2 y2 <= f x1 y1``); +val _ = overload_on("RMONO3", + ``\f:num -> num -> num -> num. + !x1 y1 z1 x2 y2 z2. x1 <= x2 /\ y1 <= y2 /\ z1 <= z2 ==> + f x2 y2 z2 <= f x1 y1 z1``); + +(* ------------------------------------------------------------------------- *) +(* More Set Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: DISJOINT (s DIFF t) t /\ DISJOINT t (s DIFF t) *) +(* Proof: + DISJOINT (s DIFF t) t + <=> (s DIFF t) INTER t = {} by DISJOINT_DEF + <=> !x. x IN (s DIFF t) INTER t <=> F by MEMBER_NOT_EMPTY + x IN (s DIFF t) INTER t + <=> x IN (s DIFF t) /\ x IN t by IN_INTER + <=> (x IN s /\ x NOTIN t) /\ x IN t by IN_DIFF + <=> x IN s /\ (x NOTIN t /\ x IN t) + <=> x IN s /\ F + <=> F + Similarly for DISJOINT t (s DIFF t) +*) +val DISJOINT_DIFF = store_thm( + "DISJOINT_DIFF", + ``!s t. (DISJOINT (s DIFF t) t) /\ (DISJOINT t (s DIFF t))``, + (rw[DISJOINT_DEF, EXTENSION] >> metis_tac[])); + +(* Theorem: DISJOINT s t <=> ((s DIFF t) = s) *) +(* Proof: by DISJOINT_DEF, DIFF_DEF, EXTENSION *) +val DISJOINT_DIFF_IFF = store_thm( + "DISJOINT_DIFF_IFF", + ``!s t. DISJOINT s t <=> ((s DIFF t) = s)``, + rw[DISJOINT_DEF, DIFF_DEF, EXTENSION] >> + metis_tac[]); + +(* Theorem: s UNION (t DIFF s) = s UNION t *) +(* Proof: + By EXTENSION, + x IN (s UNION (t DIFF s)) + = x IN s \/ x IN (t DIFF s) by IN_UNION + = x IN s \/ (x IN t /\ x NOTIN s) by IN_DIFF + = (x IN s \/ x IN t) /\ (x IN s \/ x NOTIN s) by LEFT_OR_OVER_AND + = (x IN s \/ x IN t) /\ T by EXCLUDED_MIDDLE + = x IN (s UNION t) by IN_UNION +*) +val UNION_DIFF_EQ_UNION = store_thm( + "UNION_DIFF_EQ_UNION", + ``!s t. s UNION (t DIFF s) = s UNION t``, + rw_tac std_ss[EXTENSION, IN_UNION, IN_DIFF] >> + metis_tac[]); + +(* Theorem: (s INTER (t DIFF s) = {}) /\ ((t DIFF s) INTER s = {}) *) +(* Proof: by DISJOINT_DIFF, GSYM DISJOINT_DEF *) +val INTER_DIFF = store_thm( + "INTER_DIFF", + ``!s t. (s INTER (t DIFF s) = {}) /\ ((t DIFF s) INTER s = {})``, + rw[DISJOINT_DIFF, GSYM DISJOINT_DEF]); + +(* Theorem: {x} SUBSET s /\ SING s <=> (s = {x}) *) +(* Proof: + Note {x} SUBSET s ==> x IN s by SUBSET_DEF + and SING s ==> ?y. s = {y} by SING_DEF + Thus x IN {y} ==> x = y by IN_SING +*) +Theorem SING_SUBSET : + !s x. {x} SUBSET s /\ SING s <=> (s = {x}) +Proof + metis_tac[SING_DEF, IN_SING, SUBSET_DEF] +QED + +(* Theorem: x IN (if b then {y} else {}) ==> (x = y) *) +(* Proof: by IN_SING, MEMBER_NOT_EMPTY *) +val IN_SING_OR_EMPTY = store_thm( + "IN_SING_OR_EMPTY", + ``!b x y. x IN (if b then {y} else {}) ==> (x = y)``, + rw[]); + +(* Theorem: FINITE s ==> ((CARD s = 1) <=> SING s) *) +(* Proof: + If part: CARD s = 1 ==> SING s + Since CARD s = 1 + ==> s <> {} by CARD_EMPTY + ==> ?x. x IN s by MEMBER_NOT_EMPTY + Claim: !y . y IN s ==> y = x + Proof: By contradiction, suppose y <> x. + Then y NOTIN {x} by EXTENSION + so CARD {y; x} = 2 by CARD_DEF + and {y; x} SUBSET s by SUBSET_DEF + thus CARD {y; x} <= CARD s by CARD_SUBSET + This contradicts CARD s = 1. + Hence SING s by SING_ONE_ELEMENT (or EXTENSION, SING_DEF) + Or, + With x IN s, {x} SUBSET s by SUBSET_DEF + If s <> {x}, then {x} PSUBSET s by PSUBSET_DEF + so CARD {x} < CARD s by CARD_PSUBSET + But CARD {x} = 1 by CARD_SING + and this contradicts CARD s = 1. + + Only-if part: SING s ==> CARD s = 1 + Since SING s + <=> ?x. s = {x} by SING_DEF + ==> CARD {x} = 1 by CARD_SING +*) +val CARD_EQ_1 = store_thm( + "CARD_EQ_1", + ``!s. FINITE s ==> ((CARD s = 1) <=> SING s)``, + rw[SING_DEF, EQ_IMP_THM] >| [ + `1 <> 0` by decide_tac >> + `s <> {} /\ ?x. x IN s` by metis_tac[CARD_EMPTY, MEMBER_NOT_EMPTY] >> + qexists_tac `x` >> + spose_not_then strip_assume_tac >> + `{x} PSUBSET s` by rw[PSUBSET_DEF] >> + `CARD {x} < CARD s` by rw[CARD_PSUBSET] >> + `CARD {x} = 1` by rw[CARD_SING] >> + decide_tac, + rw[CARD_SING] + ]); + +(* Theorem: x <> y ==> ((x INSERT s) DELETE y = x INSERT (s DELETE y)) *) +(* Proof: + z IN (x INSERT s) DELETE y + <=> z IN (x INSERT s) /\ z <> y by IN_DELETE + <=> (z = x \/ z IN s) /\ z <> y by IN_INSERT + <=> (z = x /\ z <> y) \/ (z IN s /\ z <> y) by RIGHT_AND_OVER_OR + <=> (z = x) \/ (z IN s /\ z <> y) by x <> y + <=> (z = x) \/ (z IN DELETE y) by IN_DELETE + <=> z IN x INSERT (s DELETE y) by IN_INSERT +*) +val INSERT_DELETE_COMM = store_thm( + "INSERT_DELETE_COMM", + ``!s x y. x <> y ==> ((x INSERT s) DELETE y = x INSERT (s DELETE y))``, + (rw[EXTENSION] >> metis_tac[])); + +(* Theorem: x NOTIN s ==> (x INSERT s) DELETE x = s *) +(* Proof: + (x INSERT s) DELETE x + = s DELETE x by DELETE_INSERT + = s by DELETE_NON_ELEMENT +*) +Theorem INSERT_DELETE_NON_ELEMENT: + !x s. x NOTIN s ==> (x INSERT s) DELETE x = s +Proof + simp[DELETE_INSERT, DELETE_NON_ELEMENT] +QED + +(* Theorem: s SUBSET u ==> (s INTER t) SUBSET u *) +(* Proof: + Note (s INTER t) SUBSET s by INTER_SUBSET + ==> (s INTER t) SUBSET u by SUBSET_TRANS +*) +val SUBSET_INTER_SUBSET = store_thm( + "SUBSET_INTER_SUBSET", + ``!s t u. s SUBSET u ==> (s INTER t) SUBSET u``, + metis_tac[INTER_SUBSET, SUBSET_TRANS]); + +(* Theorem: s DIFF (s DIFF t) = s INTER t *) +(* Proof: by IN_DIFF, IN_INTER *) +val DIFF_DIFF_EQ_INTER = store_thm( + "DIFF_DIFF_EQ_INTER", + ``!s t. s DIFF (s DIFF t) = s INTER t``, + rw[EXTENSION] >> + metis_tac[]); + +(* Theorem: (s = t) <=> (s SUBSET t /\ (t DIFF s = {})) *) +(* Proof: + s = t + <=> s SUBSET t /\ t SUBSET s by SET_EQ_SUBSET + <=> s SUBSET t /\ (t DIFF s = {}) by SUBSET_DIFF_EMPTY +*) +val SET_EQ_BY_DIFF = store_thm( + "SET_EQ_BY_DIFF", + ``!s t. (s = t) <=> (s SUBSET t /\ (t DIFF s = {}))``, + rw[SET_EQ_SUBSET, SUBSET_DIFF_EMPTY]); + +(* in pred_setTheory: +SUBSET_DELETE_BOTH |- !s1 s2 x. s1 SUBSET s2 ==> s1 DELETE x SUBSET s2 DELETE x +*) + +(* Theorem: s1 SUBSET s2 ==> x INSERT s1 SUBSET x INSERT s2 *) +(* Proof: by SUBSET_DEF *) +Theorem SUBSET_INSERT_BOTH: + !s1 s2 x. s1 SUBSET s2 ==> x INSERT s1 SUBSET x INSERT s2 +Proof + simp[SUBSET_DEF] +QED + +(* Theorem: x NOTIN s /\ (x INSERT s) SUBSET t ==> s SUBSET (t DELETE x) *) +(* Proof: by SUBSET_DEF *) +val INSERT_SUBSET_SUBSET = store_thm( + "INSERT_SUBSET_SUBSET", + ``!s t x. x NOTIN s /\ (x INSERT s) SUBSET t ==> s SUBSET (t DELETE x)``, + rw[SUBSET_DEF]); + +(* DIFF_INSERT |- !s t x. s DIFF (x INSERT t) = s DELETE x DIFF t *) + +(* Theorem: (s DIFF t) DELETE x = s DIFF (x INSERT t) *) +(* Proof: by EXTENSION *) +val DIFF_DELETE = store_thm( + "DIFF_DELETE", + ``!s t x. (s DIFF t) DELETE x = s DIFF (x INSERT t)``, + (rw[EXTENSION] >> metis_tac[])); + +(* Theorem: FINITE a /\ b SUBSET a ==> (CARD (a DIFF b) = CARD a - CARD b) *) +(* Proof: + Note FINITE b by SUBSET_FINITE + so a INTER b = b by SUBSET_INTER2 + CARD (a DIFF b) + = CARD a - CARD (a INTER b) by CARD_DIFF + = CARD a - CARD b by above +*) +Theorem SUBSET_DIFF_CARD: + !a b. FINITE a /\ b SUBSET a ==> (CARD (a DIFF b) = CARD a - CARD b) +Proof + metis_tac[CARD_DIFF, SUBSET_FINITE, SUBSET_INTER2] +QED + +(* Theorem: s SUBSET {x} <=> ((s = {}) \/ (s = {x})) *) +(* Proof: + Note !y. y IN s ==> y = x by SUBSET_DEF, IN_SING + If s = {}, then trivially true. + If s <> {}, + then ?y. y IN s by MEMBER_NOT_EMPTY, s <> {} + so y = x by above + ==> s = {x} by EXTENSION +*) +Theorem SUBSET_SING_IFF: + !s x. s SUBSET {x} <=> ((s = {}) \/ (s = {x})) +Proof + rw[SUBSET_DEF, EXTENSION] >> + metis_tac[] +QED + +(* Theorem: FINITE t /\ s SUBSET t ==> (CARD s = CARD t <=> s = t) *) +(* Proof: + If part: CARD s = CARD t ==> s = t + By contradiction, suppose s <> t. + Then s PSUBSET t by PSUBSET_DEF + so CARD s < CARD t by CARD_PSUBSET, FINITE t + This contradicts CARD s = CARD t. + Only-if part is trivial. +*) +Theorem SUBSET_CARD_EQ: + !s t. FINITE t /\ s SUBSET t ==> (CARD s = CARD t <=> s = t) +Proof + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `s PSUBSET t` by rw[PSUBSET_DEF] >> + `CARD s < CARD t` by rw[CARD_PSUBSET] >> + decide_tac +QED + +(* Theorem: (!x. x IN s ==> f x IN t) <=> (IMAGE f s) SUBSET t *) +(* Proof: + If part: (!x. x IN s ==> f x IN t) ==> (IMAGE f s) SUBSET t + y IN (IMAGE f s) + ==> ?x. (y = f x) /\ x IN s by IN_IMAGE + ==> f x = y IN t by given + hence (IMAGE f s) SUBSET t by SUBSET_DEF + Only-if part: (IMAGE f s) SUBSET t ==> (!x. x IN s ==> f x IN t) + x IN s + ==> f x IN (IMAGE f s) by IN_IMAGE + ==> f x IN t by SUBSET_DEF +*) +val IMAGE_SUBSET_TARGET = store_thm( + "IMAGE_SUBSET_TARGET", + ``!f s t. (!x. x IN s ==> f x IN t) <=> (IMAGE f s) SUBSET t``, + metis_tac[IN_IMAGE, SUBSET_DEF]); + +(* Theorem: SURJ f s t ==> CARD (IMAGE f s) = CARD t *) +(* Proof: + Note IMAGE f s = t by IMAGE_SURJ + Thus CARD (IMAGE f s) = CARD t by above +*) +Theorem SURJ_CARD_IMAGE: + !f s t. SURJ f s t ==> CARD (IMAGE f s) = CARD t +Proof + simp[IMAGE_SURJ] +QED + +(* ------------------------------------------------------------------------- *) +(* Image and Bijection (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: INJ f s t ==> INJ f s UNIV *) +(* Proof: + Note s SUBSET s by SUBSET_REFL + and t SUBSET univ(:'b) by SUBSET_UNIV + so INJ f s t ==> INJ f s univ(:'b) by INJ_SUBSET +*) +val INJ_UNIV = store_thm( + "INJ_UNIV", + ``!f s t. INJ f s t ==> INJ f s UNIV``, + metis_tac[INJ_SUBSET, SUBSET_REFL, SUBSET_UNIV]); + +(* Theorem: INJ f s UNIV ==> BIJ f s (IMAGE f s) *) +(* Proof: by definitions. *) +val INJ_IMAGE_BIJ_ALT = store_thm( + "INJ_IMAGE_BIJ_ALT", + ``!f s. INJ f s UNIV ==> BIJ f s (IMAGE f s)``, + rw[BIJ_DEF, INJ_DEF, SURJ_DEF]); + +(* Theorem: s <> {} ==> !e. IMAGE (K e) s = {e} *) +(* Proof: + IMAGE (K e) s + <=> {(K e) x | x IN s} by IMAGE_DEF + <=> {e | x IN s} by K_THM + <=> {e} by EXTENSION, if s <> {} +*) +val IMAGE_K = store_thm( + "IMAGE_K", + ``!s. s <> {} ==> !e. IMAGE (K e) s = {e}``, + rw[EXTENSION, EQ_IMP_THM]); + +(* Theorem: (!x y. (f x = f y) ==> (x = y)) ==> (!s e. e IN s <=> f e IN IMAGE f s) *) +(* Proof: + If part: e IN s ==> f e IN IMAGE f s + True by IMAGE_IN. + Only-if part: f e IN IMAGE f s ==> e IN s + ?x. (f e = f x) /\ x IN s by IN_IMAGE + f e = f x ==> e = x by given implication + Hence x IN s +*) +val IMAGE_ELEMENT_CONDITION = store_thm( + "IMAGE_ELEMENT_CONDITION", + ``!f:'a -> 'b. (!x y. (f x = f y) ==> (x = y)) ==> (!s e. e IN s <=> f e IN IMAGE f s)``, + rw[EQ_IMP_THM] >> + metis_tac[]); + +(* Theorem: BIGUNION (IMAGE (\x. {x}) s) = s *) +(* Proof: + z IN BIGUNION (IMAGE (\x. {x}) s) + <=> ?t. z IN t /\ t IN (IMAGE (\x. {x}) s) by IN_BIGUNION + <=> ?t. z IN t /\ (?y. y IN s /\ (t = {y})) by IN_IMAGE + <=> z IN {z} /\ (?y. y IN s /\ {z} = {y}) by picking t = {z} + <=> T /\ z IN s by picking y = z, IN_SING + Hence BIGUNION (IMAGE (\x. {x}) s) = s by EXTENSION +*) +val BIGUNION_ELEMENTS_SING = store_thm( + "BIGUNION_ELEMENTS_SING", + ``!s. BIGUNION (IMAGE (\x. {x}) s) = s``, + rw[EXTENSION, EQ_IMP_THM] >- + metis_tac[] >> + qexists_tac `{x}` >> + metis_tac[IN_SING]); + +(* Theorem: s SUBSET t /\ INJ f t UNIV ==> (IMAGE f (t DIFF s) = (IMAGE f t) DIFF (IMAGE f s)) *) +(* Proof: by SUBSET_DEF, INJ_DEF, EXTENSION, IN_IMAGE, IN_DIFF *) +Theorem IMAGE_DIFF: + !s t f. s SUBSET t /\ INJ f t UNIV ==> (IMAGE f (t DIFF s) = (IMAGE f t) DIFF (IMAGE f s)) +Proof + rw[SUBSET_DEF, INJ_DEF, EXTENSION] >> + metis_tac[] +QED + +(* ------------------------------------------------------------------------- *) +(* Set of Proper Subsets *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of all proper subsets of a set *) +val _ = overload_on ("PPOW", ``\s. (POW s) DIFF {s}``); + +(* Theorem: !s e. e IN PPOW s ==> e PSUBSET s *) +(* Proof: + e IN PPOW s + = e IN ((POW s) DIFF {s}) by notation + = (e IN POW s) /\ e NOTIN {s} by IN_DIFF + = (e SUBSET s) /\ e NOTIN {s} by IN_POW + = (e SUBSET s) /\ e <> s by IN_SING + = e PSUBSET s by PSUBSET_DEF +*) +val IN_PPOW = store_thm( + "IN_PPOW", + ``!s e. e IN PPOW s ==> e PSUBSET s``, + rw[PSUBSET_DEF, IN_POW]); + +(* Theorem: FINITE (PPOW s) *) +(* Proof: + Since PPOW s = (POW s) DIFF {s}, + FINITE s + ==> FINITE (POW s) by FINITE_POW + ==> FINITE ((POW s) DIFF {s}) by FINITE_DIFF + ==> FINITE (PPOW s) by above +*) +val FINITE_PPOW = store_thm( + "FINITE_PPOW", + ``!s. FINITE s ==> FINITE (PPOW s)``, + rw[FINITE_POW]); + +(* Theorem: FINITE s ==> CARD (PPOW s) = PRE (2 ** CARD s) *) +(* Proof: + CARD (PPOW s) + = CARD ((POW s) DIFF {s}) by notation + = CARD (POW s) - CARD ((POW s) INTER {s}) by CARD_DIFF + = CARD (POW s) - CARD {s} by INTER_SING, since s IN POW s + = 2 ** CARD s - CARD {s} by CARD_POW + = 2 ** CARD s - 1 by CARD_SING + = PRE (2 ** CARD s) by PRE_SUB1 +*) +val CARD_PPOW = store_thm( + "CARD_PPOW", + ``!s. FINITE s ==> (CARD (PPOW s) = PRE (2 ** CARD s))``, + rpt strip_tac >> + `FINITE {s}` by rw[FINITE_SING] >> + `FINITE (POW s)` by rw[FINITE_POW] >> + `s IN (POW s)` by rw[IN_POW, SUBSET_REFL] >> + `CARD (PPOW s) = CARD (POW s) - CARD ((POW s) INTER {s})` by rw[CARD_DIFF] >> + `_ = CARD (POW s) - CARD {s}` by rw[INTER_SING] >> + `_ = 2 ** CARD s - CARD {s}` by rw[CARD_POW] >> + `_ = 2 ** CARD s - 1` by rw[CARD_SING] >> + `_ = PRE (2 ** CARD s)` by rw[PRE_SUB1] >> + rw[]); + +(* Theorem: FINITE s ==> CARD (PPOW s) = PRE (2 ** CARD s) *) +(* Proof: by CARD_PPOW *) +val CARD_PPOW_EQN = store_thm( + "CARD_PPOW_EQN", + ``!s. FINITE s ==> (CARD (PPOW s) = (2 ** CARD s) - 1)``, + rw[CARD_PPOW]); + +(* ------------------------------------------------------------------------- *) +(* Partition Property *) +(* ------------------------------------------------------------------------- *) + +(* Overload partition by split *) +val _ = overload_on("split", ``\s u v. (s = u UNION v) /\ (DISJOINT u v)``); + +(* Pretty printing of partition by split *) +val _ = add_rule {block_style = (AroundEachPhrase, (PP.CONSISTENT, 2)), + fixity = Infix(NONASSOC, 450), + paren_style = OnlyIfNecessary, + term_name = "split", + pp_elements = [HardSpace 1, TOK "=|=", HardSpace 1, TM, + BreakSpace(1,1), TOK "#", BreakSpace(1,1)]}; + +(* Theorem: FINITE s ==> !u v. s =|= u # v ==> (PROD_SET s = PROD_SET u * PROD_SET v) *) +(* Proof: + By finite induction on s. + Base: {} = u UNION v ==> PROD_SET {} = PROD_SET u * PROD_SET v + Note u = {} and v = {} by EMPTY_UNION + and PROD_SET {} = 1 by PROD_SET_EMPTY + Hence true. + Step: !u v. (s = u UNION v) /\ DISJOINT u v ==> (PROD_SET s = PROD_SET u * PROD_SET v) ==> + e NOTIN s /\ e INSERT s = u UNION v ==> PROD_SET (e INSERT s) = PROD_SET u * PROD_SET v + Note e IN u \/ e IN v by IN_INSERT, IN_UNION + If e IN u, + Then e NOTIN v by IN_DISJOINT + Let w = u DELETE e. + Then e NOTIN w by IN_DELETE + and u = e INSERT w by INSERT_DELETE + Note s = w UNION v by EXTENSION, IN_INSERT, IN_UNION + ==> FINITE w by FINITE_UNION + and DISJOINT w v by DISJOINT_INSERT + PROD_SET (e INSERT s) + = e * PROD_SET s by PROD_SET_INSERT, FINITE s + = e * (PROD_SET w * PROD_SET v) by induction hypothesis + = (e * PROD_SET w) * PROD_SET v by MULT_ASSOC + = PROD_SET (e INSERT w) * PROD_SET v by PROD_SET_INSERT, FINITE w + = PROD_SET u * PROD_SET v + + Similarly for e IN v. +*) +val PROD_SET_PRODUCT_BY_PARTITION = store_thm( + "PROD_SET_PRODUCT_BY_PARTITION", + ``!s. FINITE s ==> !u v. s =|= u # v ==> (PROD_SET s = PROD_SET u * PROD_SET v)``, + Induct_on `FINITE` >> + rpt strip_tac >- + fs[PROD_SET_EMPTY] >> + `e IN u \/ e IN v` by metis_tac[IN_INSERT, IN_UNION] >| [ + qabbrev_tac `w = u DELETE e` >> + `u = e INSERT w` by rw[Abbr`w`] >> + `e NOTIN w` by rw[Abbr`w`] >> + `e NOTIN v` by metis_tac[IN_DISJOINT] >> + `s = w UNION v` by + (rw[EXTENSION] >> + metis_tac[IN_INSERT, IN_UNION]) >> + `FINITE w` by metis_tac[FINITE_UNION] >> + `DISJOINT w v` by metis_tac[DISJOINT_INSERT] >> + `PROD_SET (e INSERT s) = e * PROD_SET s` by rw[PROD_SET_INSERT] >> + `_ = e * (PROD_SET w * PROD_SET v)` by rw[] >> + `_ = (e * PROD_SET w) * PROD_SET v` by rw[] >> + `_ = PROD_SET u * PROD_SET v` by rw[PROD_SET_INSERT] >> + rw[], + qabbrev_tac `w = v DELETE e` >> + `v = e INSERT w` by rw[Abbr`w`] >> + `e NOTIN w` by rw[Abbr`w`] >> + `e NOTIN u` by metis_tac[IN_DISJOINT] >> + `s = u UNION w` by + (rw[EXTENSION] >> + metis_tac[IN_INSERT, IN_UNION]) >> + `FINITE w` by metis_tac[FINITE_UNION] >> + `DISJOINT u w` by metis_tac[DISJOINT_INSERT, DISJOINT_SYM] >> + `PROD_SET (e INSERT s) = e * PROD_SET s` by rw[PROD_SET_INSERT] >> + `_ = e * (PROD_SET u * PROD_SET w)` by rw[] >> + `_ = PROD_SET u * (e * PROD_SET w)` by rw[] >> + `_ = PROD_SET u * PROD_SET v` by rw[PROD_SET_INSERT] >> + rw[] + ]); + +(* ------------------------------------------------------------------------- *) +(* Arithmetic Theorems (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 3 = SUC 2 *) +(* Proof: by arithmetic *) +val THREE = store_thm( + "THREE", + ``3 = SUC 2``, + decide_tac); + +(* Theorem: 4 = SUC 3 *) +(* Proof: by arithmetic *) +val FOUR = store_thm( + "FOUR", + ``4 = SUC 3``, + decide_tac); + +(* Theorem: 5 = SUC 4 *) +(* Proof: by arithmetic *) +val FIVE = store_thm( + "FIVE", + ``5 = SUC 4``, + decide_tac); + +(* Overload squaring (temporalized by Chun Tian) *) +val _ = temp_overload_on("SQ", ``\n. n * n``); (* not n ** 2 *) + +(* Overload half of a number (temporalized by Chun Tian) *) +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); + +(* Overload twice of a number (temporalized by Chun Tian) *) +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); + +(* make divides infix *) +val _ = set_fixity "divides" (Infixl 480); (* relation is 450, +/- is 500, * is 600. *) + +(* Theorem alias *) +Theorem ZERO_LE_ALL = ZERO_LESS_EQ; +(* val ZERO_LE_ALL = |- !n. 0 <= n: thm *) + +(* Extract theorem *) +Theorem ONE_NOT_0 = DECIDE``1 <> 0``; +(* val ONE_NOT_0 = |- 1 <> 0: thm *) + +(* Theorem: !n. 1 < n ==> 0 < n *) +(* Proof: by arithmetic. *) +val ONE_LT_POS = store_thm( + "ONE_LT_POS", + ``!n. 1 < n ==> 0 < n``, + decide_tac); + +(* Theorem: !n. 1 < n ==> n <> 0 *) +(* Proof: by arithmetic. *) +val ONE_LT_NONZERO = store_thm( + "ONE_LT_NONZERO", + ``!n. 1 < n ==> n <> 0``, + decide_tac); + +(* Theorem: ~(1 < n) <=> (n = 0) \/ (n = 1) *) +(* Proof: by arithmetic. *) +val NOT_LT_ONE = store_thm( + "NOT_LT_ONE", + ``!n. ~(1 < n) <=> (n = 0) \/ (n = 1)``, + decide_tac); + +(* Theorem: n <> 0 <=> 1 <= n *) +(* Proof: by arithmetic. *) +val NOT_ZERO_GE_ONE = store_thm( + "NOT_ZERO_GE_ONE", + ``!n. n <> 0 <=> 1 <= n``, + decide_tac); + +(* Theorem: n <= 1 <=> (n = 0) \/ (n = 1) *) +(* Proof: by arithmetic *) +val LE_ONE = store_thm( + "LE_ONE", + ``!n. n <= 1 <=> (n = 0) \/ (n = 1)``, + decide_tac); + +(* arithmeticTheory.LESS_EQ_SUC_REFL |- !m. m <= SUC m *) + +(* Theorem: n < SUC n *) +(* Proof: by arithmetic. *) +val LESS_SUC = store_thm( + "LESS_SUC", + ``!n. n < SUC n``, + decide_tac); + +(* Theorem: 0 < n ==> PRE n < n *) +(* Proof: by arithmetic. *) +val PRE_LESS = store_thm( + "PRE_LESS", + ``!n. 0 < n ==> PRE n < n``, + decide_tac); + +(* Theorem: 0 < n ==> ?m. n = SUC m *) +(* Proof: by NOT_ZERO_LT_ZERO, num_CASES. *) +val SUC_EXISTS = store_thm( + "SUC_EXISTS", + ``!n. 0 < n ==> ?m. n = SUC m``, + metis_tac[NOT_ZERO_LT_ZERO, num_CASES]); + + +(* Theorem: 1 <> 0 *) +(* Proof: by ONE, SUC_ID *) +val ONE_NOT_ZERO = store_thm( + "ONE_NOT_ZERO", + ``1 <> 0``, + decide_tac); + +(* Theorem: (SUC m) + (SUC n) = m + n + 2 *) +(* Proof: + (SUC m) + (SUC n) + = (m + 1) + (n + 1) by ADD1 + = m + n + 2 by arithmetic +*) +val SUC_ADD_SUC = store_thm( + "SUC_ADD_SUC", + ``!m n. (SUC m) + (SUC n) = m + n + 2``, + decide_tac); + +(* Theorem: (SUC m) * (SUC n) = m * n + m + n + 1 *) +(* Proof: + (SUC m) * (SUC n) + = SUC m + (SUC m) * n by MULT_SUC + = SUC m + n * (SUC m) by MULT_COMM + = SUC m + (n + n * m) by MULT_SUC + = m * n + m + n + 1 by arithmetic +*) +val SUC_MULT_SUC = store_thm( + "SUC_MULT_SUC", + ``!m n. (SUC m) * (SUC n) = m * n + m + n + 1``, + rw[MULT_SUC]); + +(* Theorem: (SUC m = SUC n) <=> (m = n) *) +(* Proof: by prim_recTheory.INV_SUC_EQ *) +val SUC_EQ = store_thm( + "SUC_EQ", + ``!m n. (SUC m = SUC n) <=> (m = n)``, + rw[]); + +(* Theorem: (TWICE n = 0) <=> (n = 0) *) +(* Proof: MULT_EQ_0 *) +val TWICE_EQ_0 = store_thm( + "TWICE_EQ_0", + ``!n. (TWICE n = 0) <=> (n = 0)``, + rw[]); + +(* Theorem: (SQ n = 0) <=> (n = 0) *) +(* Proof: MULT_EQ_0 *) +val SQ_EQ_0 = store_thm( + "SQ_EQ_0", + ``!n. (SQ n = 0) <=> (n = 0)``, + rw[]); + +(* Theorem: (SQ n = 1) <=> (n = 1) *) +(* Proof: MULT_EQ_1 *) +val SQ_EQ_1 = store_thm( + "SQ_EQ_1", + ``!n. (SQ n = 1) <=> (n = 1)``, + rw[]); + +(* Theorem: (x * y * z = 0) <=> ((x = 0) \/ (y = 0) \/ (z = 0)) *) +(* Proof: by MULT_EQ_0 *) +val MULT3_EQ_0 = store_thm( + "MULT3_EQ_0", + ``!x y z. (x * y * z = 0) <=> ((x = 0) \/ (y = 0) \/ (z = 0))``, + metis_tac[MULT_EQ_0]); + +(* Theorem: (x * y * z = 1) <=> ((x = 1) /\ (y = 1) /\ (z = 1)) *) +(* Proof: by MULT_EQ_1 *) +val MULT3_EQ_1 = store_thm( + "MULT3_EQ_1", + ``!x y z. (x * y * z = 1) <=> ((x = 1) /\ (y = 1) /\ (z = 1))``, + metis_tac[MULT_EQ_1]); + +(* Theorem: 0 ** 2 = 0 *) +(* Proof: by ZERO_EXP *) +Theorem SQ_0: + 0 ** 2 = 0 +Proof + simp[] +QED + +(* Theorem: (n ** 2 = 0) <=> (n = 0) *) +(* Proof: by EXP_2, MULT_EQ_0 *) +Theorem EXP_2_EQ_0: + !n. (n ** 2 = 0) <=> (n = 0) +Proof + simp[] +QED + +(* LE_MULT_LCANCEL |- !m n p. m * n <= m * p <=> m = 0 \/ n <= p *) + +(* Theorem: n <= p ==> m * n <= m * p *) +(* Proof: + If m = 0, this is trivial. + If m <> 0, this is true by LE_MULT_LCANCEL. +*) +Theorem LE_MULT_LCANCEL_IMP: + !m n p. n <= p ==> m * n <= m * p +Proof + simp[] +QED + +(* ------------------------------------------------------------------------- *) +(* Maximum and minimum *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MAX m n = if m <= n then n else m *) +(* Proof: by MAX_DEF *) +val MAX_ALT = store_thm( + "MAX_ALT", + ``!m n. MAX m n = if m <= n then n else m``, + rw[MAX_DEF]); + +(* Theorem: MIN m n = if m <= n then m else n *) +(* Proof: by MIN_DEF *) +val MIN_ALT = store_thm( + "MIN_ALT", + ``!m n. MIN m n = if m <= n then m else n``, + rw[MIN_DEF]); + +(* Theorem: (!x y. x <= y ==> f x <= f y) ==> !x y. f (MAX x y) = MAX (f x) (f y) *) +(* Proof: by MAX_DEF *) +val MAX_SWAP = store_thm( + "MAX_SWAP", + ``!f. (!x y. x <= y ==> f x <= f y) ==> !x y. f (MAX x y) = MAX (f x) (f y)``, + rw[MAX_DEF] >> + Cases_on `x < y` >| [ + `f x <= f y` by rw[] >> + Cases_on `f x = f y` >- + rw[] >> + rw[], + `y <= x` by decide_tac >> + `f y <= f x` by rw[] >> + rw[] + ]); + +(* Theorem: (!x y. x <= y ==> f x <= f y) ==> !x y. f (MIN x y) = MIN (f x) (f y) *) +(* Proof: by MIN_DEF *) +val MIN_SWAP = store_thm( + "MIN_SWAP", + ``!f. (!x y. x <= y ==> f x <= f y) ==> !x y. f (MIN x y) = MIN (f x) (f y)``, + rw[MIN_DEF] >> + Cases_on `x < y` >| [ + `f x <= f y` by rw[] >> + Cases_on `f x = f y` >- + rw[] >> + rw[], + `y <= x` by decide_tac >> + `f y <= f x` by rw[] >> + rw[] + ]); + +(* Theorem: SUC (MAX m n) = MAX (SUC m) (SUC n) *) +(* Proof: + If m < n, then SUC m < SUC n by LESS_MONO_EQ + hence true by MAX_DEF. + If m = n, then true by MAX_IDEM. + If n < m, true by MAX_COMM of the case m < n. +*) +val SUC_MAX = store_thm( + "SUC_MAX", + ``!m n. SUC (MAX m n) = MAX (SUC m) (SUC n)``, + rw[MAX_DEF]); + +(* Theorem: SUC (MIN m n) = MIN (SUC m) (SUC n) *) +(* Proof: by MIN_DEF *) +val SUC_MIN = store_thm( + "SUC_MIN", + ``!m n. SUC (MIN m n) = MIN (SUC m) (SUC n)``, + rw[MIN_DEF]); + +(* Reverse theorems *) +val MAX_SUC = save_thm("MAX_SUC", GSYM SUC_MAX); +(* val MAX_SUC = |- !m n. MAX (SUC m) (SUC n) = SUC (MAX m n): thm *) +val MIN_SUC = save_thm("MIN_SUC", GSYM SUC_MIN); +(* val MIN_SUC = |- !m n. MIN (SUC m) (SUC n) = SUC (MIN m n): thm *) + +(* Theorem: x < n /\ y < n ==> MAX x y < n *) +(* Proof: + MAX x y + = if x < y then y else x by MAX_DEF + = either x or y + < n for either case +*) +val MAX_LESS = store_thm( + "MAX_LESS", + ``!x y n. x < n /\ y < n ==> MAX x y < n``, + rw[]); + +(* Theorem: m <= MAX m n /\ n <= MAX m n *) +(* Proof: by MAX_DEF *) +val MAX_IS_MAX = store_thm( + "MAX_IS_MAX", + ``!m n. m <= MAX m n /\ n <= MAX m n``, + rw_tac std_ss[MAX_DEF]); + +(* Theorem: MIN m n <= m /\ MIN m n <= n *) +(* Proof: by MIN_DEF *) +val MIN_IS_MIN = store_thm( + "MIN_IS_MIN", + ``!m n. MIN m n <= m /\ MIN m n <= n``, + rw_tac std_ss[MIN_DEF]); + +(* Theorem: (MAX (MAX m n) n = MAX m n) /\ (MAX m (MAX m n) = MAX m n) *) +(* Proof: by MAX_DEF *) +val MAX_ID = store_thm( + "MAX_ID", + ``!m n. (MAX (MAX m n) n = MAX m n) /\ (MAX m (MAX m n) = MAX m n)``, + rw[MAX_DEF]); + +(* Theorem: (MIN (MIN m n) n = MIN m n) /\ (MIN m (MIN m n) = MIN m n) *) +(* Proof: by MIN_DEF *) +val MIN_ID = store_thm( + "MIN_ID", + ``!m n. (MIN (MIN m n) n = MIN m n) /\ (MIN m (MIN m n) = MIN m n)``, + rw[MIN_DEF]); + +(* Theorem: a <= b /\ c <= d ==> MAX a c <= MAX b d *) +(* Proof: by MAX_DEF *) +val MAX_LE_PAIR = store_thm( + "MAX_LE_PAIR", + ``!a b c d. a <= b /\ c <= d ==> MAX a c <= MAX b d``, + rw[]); + +(* Theorem: a <= b /\ c <= d ==> MIN a c <= MIN b d *) +(* Proof: by MIN_DEF *) +val MIN_LE_PAIR = store_thm( + "MIN_LE_PAIR", + ``!a b c d. a <= b /\ c <= d ==> MIN a c <= MIN b d``, + rw[]); + +(* Theorem: MAX a (b + c) <= MAX a b + MAX a c *) +(* Proof: by MAX_DEF *) +val MAX_ADD = store_thm( + "MAX_ADD", + ``!a b c. MAX a (b + c) <= MAX a b + MAX a c``, + rw[MAX_DEF]); + +(* Theorem: MIN a (b + c) <= MIN a b + MIN a c *) +(* Proof: by MIN_DEF *) +val MIN_ADD = store_thm( + "MIN_ADD", + ``!a b c. MIN a (b + c) <= MIN a b + MIN a c``, + rw[MIN_DEF]); + +(* Theorem: 0 < n ==> (MAX 1 n = n) *) +(* Proof: by MAX_DEF *) +val MAX_1_POS = store_thm( + "MAX_1_POS", + ``!n. 0 < n ==> (MAX 1 n = n)``, + rw[MAX_DEF]); + +(* Theorem: 0 < n ==> (MIN 1 n = 1) *) +(* Proof: by MIN_DEF *) +val MIN_1_POS = store_thm( + "MIN_1_POS", + ``!n. 0 < n ==> (MIN 1 n = 1)``, + rw[MIN_DEF]); + +(* Theorem: MAX m n <= m + n *) +(* Proof: + If m < n, MAX m n = n <= m + n by arithmetic + Otherwise, MAX m n = m <= m + n by arithmetic +*) +val MAX_LE_SUM = store_thm( + "MAX_LE_SUM", + ``!m n. MAX m n <= m + n``, + rw[MAX_DEF]); + +(* Theorem: MIN m n <= m + n *) +(* Proof: + If m < n, MIN m n = m <= m + n by arithmetic + Otherwise, MIN m n = n <= m + n by arithmetic +*) +val MIN_LE_SUM = store_thm( + "MIN_LE_SUM", + ``!m n. MIN m n <= m + n``, + rw[MIN_DEF]); + +(* Theorem: MAX 1 (m ** n) = (MAX 1 m) ** n *) +(* Proof: + If m = 0, + Then 0 ** n = 0 or 1 by ZERO_EXP + Thus MAX 1 (0 ** n) = 1 by MAX_DEF + and (MAX 1 0) ** n = 1 by MAX_DEF, EXP_1 + If m <> 0, + Then 0 < m ** n by EXP_POS + so MAX 1 (m ** n) = m ** n by MAX_DEF + and (MAX 1 m) ** n = m ** n by MAX_DEF, 0 < m +*) +val MAX_1_EXP = store_thm( + "MAX_1_EXP", + ``!n m. MAX 1 (m ** n) = (MAX 1 m) ** n``, + rpt strip_tac >> + Cases_on `m = 0` >- + rw[ZERO_EXP, MAX_DEF] >> + `0 < m /\ 0 < m ** n` by rw[] >> + `MAX 1 (m ** n) = m ** n` by rw[MAX_DEF] >> + `MAX 1 m = m` by rw[MAX_DEF] >> + fs[]); + +(* Theorem: MIN 1 (m ** n) = (MIN 1 m) ** n *) +(* Proof: + If m = 0, + Then 0 ** n = 0 or 1 by ZERO_EXP + Thus MIN 1 (0 ** n) = 0 when n <> 0 or 1 when n = 0 by MIN_DEF + and (MIN 1 0) ** n = 0 ** n by MIN_DEF + If m <> 0, + Then 0 < m ** n by EXP_POS + so MIN 1 (m ** n) = 1 ** n by MIN_DEF + and (MIN 1 m) ** n = 1 ** n by MIN_DEF, 0 < m +*) +val MIN_1_EXP = store_thm( + "MIN_1_EXP", + ``!n m. MIN 1 (m ** n) = (MIN 1 m) ** n``, + rpt strip_tac >> + Cases_on `m = 0` >- + rw[ZERO_EXP, MIN_DEF] >> + `0 < m ** n` by rw[] >> + `MIN 1 (m ** n) = 1` by rw[MIN_DEF] >> + `MIN 1 m = 1` by rw[MIN_DEF] >> + fs[]); + +(* ------------------------------------------------------------------------- *) +(* Arithmetic Manipulations *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: (n * n = n) <=> ((n = 0) \/ (n = 1)) *) +(* Proof: + If part: n * n = n ==> (n = 0) \/ (n = 1) + By contradiction, suppose n <> 0 /\ n <> 1. + Since n * n = n = n * 1 by MULT_RIGHT_1 + then n = 1 by MULT_LEFT_CANCEL, n <> 0 + This contradicts n <> 1. + Only-if part: (n = 0) \/ (n = 1) ==> n * n = n + That is, 0 * 0 = 0 by MULT + and 1 * 1 = 1 by MULT_RIGHT_1 +*) +val SQ_EQ_SELF = store_thm( + "SQ_EQ_SELF", + ``!n. (n * n = n) <=> ((n = 0) \/ (n = 1))``, + rw_tac bool_ss[EQ_IMP_THM] >- + metis_tac[MULT_RIGHT_1, MULT_LEFT_CANCEL] >- + rw[] >> + rw[]); + +(* Theorem: m <= n /\ 0 < c ==> b ** c ** m <= b ** c ** n *) +(* Proof: + If b = 0, + Note 0 < c ** m /\ 0 < c ** n by EXP_POS, by 0 < c + Thus 0 ** c ** m = 0 by ZERO_EXP + and 0 ** c ** n = 0 by ZERO_EXP + Hence true. + If b <> 0, + Then c ** m <= c ** n by EXP_BASE_LEQ_MONO_IMP, 0 < c + so b ** c ** m <= b ** c ** n by EXP_BASE_LEQ_MONO_IMP, 0 < b +*) +val EXP_EXP_BASE_LE = store_thm( + "EXP_EXP_BASE_LE", + ``!b c m n. m <= n /\ 0 < c ==> b ** c ** m <= b ** c ** n``, + rpt strip_tac >> + Cases_on `b = 0` >- + rw[ZERO_EXP] >> + rw[EXP_BASE_LEQ_MONO_IMP]); + +(* Theorem: a <= b ==> a ** n <= b ** n *) +(* Proof: + Note a ** n <= b ** n by EXP_EXP_LE_MONO + Thus size (a ** n) <= size (b ** n) by size_monotone_le +*) +val EXP_EXP_LE_MONO_IMP = store_thm( + "EXP_EXP_LE_MONO_IMP", + ``!a b n. a <= b ==> a ** n <= b ** n``, + rw[]); + +(* Theorem: m <= n ==> !p. p ** n = p ** m * p ** (n - m) *) +(* Proof: + Note n = (n - m) + m by m <= n + p ** n + = p ** (n - m) * p ** m by EXP_ADD + = p ** m * p ** (n - m) by MULT_COMM +*) +val EXP_BY_ADD_SUB_LE = store_thm( + "EXP_BY_ADD_SUB_LE", + ``!m n. m <= n ==> !p. p ** n = p ** m * p ** (n - m)``, + rpt strip_tac >> + `n = (n - m) + m` by decide_tac >> + metis_tac[EXP_ADD, MULT_COMM]); + +(* Theorem: m < n ==> (p ** n = p ** m * p ** (n - m)) *) +(* Proof: by EXP_BY_ADD_SUB_LE, LESS_IMP_LESS_OR_EQ *) +val EXP_BY_ADD_SUB_LT = store_thm( + "EXP_BY_ADD_SUB_LT", + ``!m n. m < n ==> !p. p ** n = p ** m * p ** (n - m)``, + rw[EXP_BY_ADD_SUB_LE]); + +(* Theorem: 0 < m ==> m ** (SUC n) DIV m = m ** n *) +(* Proof: + m ** (SUC n) DIV m + = (m * m ** n) DIV m by EXP + = m ** n by MULT_TO_DIV, 0 < m +*) +val EXP_SUC_DIV = store_thm( + "EXP_SUC_DIV", + ``!m n. 0 < m ==> (m ** (SUC n) DIV m = m ** n)``, + simp[EXP, MULT_TO_DIV]); + +(* Theorem: n <= n ** 2 *) +(* Proof: + If n = 0, + Then n ** 2 = 0 >= 0 by ZERO_EXP + If n <> 0, then 0 < n by NOT_ZERO_LT_ZERO + Hence n = n ** 1 by EXP_1 + <= n ** 2 by EXP_BASE_LEQ_MONO_IMP +*) +val SELF_LE_SQ = store_thm( + "SELF_LE_SQ", + ``!n. n <= n ** 2``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + `0 < n /\ 1 <= 2` by decide_tac >> + metis_tac[EXP_BASE_LEQ_MONO_IMP, EXP_1]); + +(* Theorem: a <= b /\ c <= d ==> a + c <= b + d *) +(* Proof: by LESS_EQ_LESS_EQ_MONO, or + Note a <= b ==> a + c <= b + c by LE_ADD_RCANCEL + and c <= d ==> b + c <= b + d by LE_ADD_LCANCEL + Thus a + c <= b + d by LESS_EQ_TRANS +*) +val LE_MONO_ADD2 = store_thm( + "LE_MONO_ADD2", + ``!a b c d. a <= b /\ c <= d ==> a + c <= b + d``, + rw[LESS_EQ_LESS_EQ_MONO]); + +(* Theorem: a < b /\ c < d ==> a + c < b + d *) +(* Proof: + Note a < b ==> a + c < b + c by LT_ADD_RCANCEL + and c < d ==> b + c < b + d by LT_ADD_LCANCEL + Thus a + c < b + d by LESS_TRANS +*) +val LT_MONO_ADD2 = store_thm( + "LT_MONO_ADD2", + ``!a b c d. a < b /\ c < d ==> a + c < b + d``, + rw[LT_ADD_RCANCEL, LT_ADD_LCANCEL]); + +(* Theorem: a <= b /\ c <= d ==> a * c <= b * d *) +(* Proof: by LESS_MONO_MULT2, or + Note a <= b ==> a * c <= b * c by LE_MULT_RCANCEL + and c <= d ==> b * c <= b * d by LE_MULT_LCANCEL + Thus a * c <= b * d by LESS_EQ_TRANS +*) +val LE_MONO_MULT2 = store_thm( + "LE_MONO_MULT2", + ``!a b c d. a <= b /\ c <= d ==> a * c <= b * d``, + rw[LESS_MONO_MULT2]); + +(* Theorem: a < b /\ c < d ==> a * c < b * d *) +(* Proof: + Note 0 < b, by a < b. + and 0 < d, by c < d. + If c = 0, + Then a * c = 0 < b * d by MULT_EQ_0 + If c <> 0, then 0 < c by NOT_ZERO_LT_ZERO + a < b ==> a * c < b * c by LT_MULT_RCANCEL, 0 < c + c < d ==> b * c < b * d by LT_MULT_LCANCEL, 0 < b + Thus a * c < b * d by LESS_TRANS +*) +val LT_MONO_MULT2 = store_thm( + "LT_MONO_MULT2", + ``!a b c d. a < b /\ c < d ==> a * c < b * d``, + rpt strip_tac >> + `0 < b /\ 0 < d` by decide_tac >> + Cases_on `c = 0` >- + metis_tac[MULT_EQ_0, NOT_ZERO_LT_ZERO] >> + metis_tac[LT_MULT_RCANCEL, LT_MULT_LCANCEL, LESS_TRANS, NOT_ZERO_LT_ZERO]); + +(* Theorem: 1 < m /\ 1 < n ==> (m + n <= m * n) *) +(* Proof: + Let m = m' + 1, n = n' + 1. + Note m' <> 0 /\ n' <> 0. + Thus m' * n' <> 0 by MULT_EQ_0 + or 1 <= m' * n' + m * n + = (m' + 1) * (n' + 1) + = m' * n' + m' + n' + 1 by arithmetic + >= 1 + m' + n' + 1 by 1 <= m' * n' + = m + n +*) +val SUM_LE_PRODUCT = store_thm( + "SUM_LE_PRODUCT", + ``!m n. 1 < m /\ 1 < n ==> (m + n <= m * n)``, + rpt strip_tac >> + `m <> 0 /\ n <> 0` by decide_tac >> + `?m' n'. (m = m' + 1) /\ (n = n' + 1)` by metis_tac[num_CASES, ADD1] >> + `m * n = (m' + 1) * n' + (m' + 1)` by rw[LEFT_ADD_DISTRIB] >> + `_ = m' * n' + n' + (m' + 1)` by rw[RIGHT_ADD_DISTRIB] >> + `_ = m + (n' + m' * n')` by decide_tac >> + `m' * n' <> 0` by fs[] >> + decide_tac); + +(* Theorem: 0 < n ==> k * n + 1 <= (k + 1) * n *) +(* Proof: + k * n + 1 + <= k * n + n by 1 <= n + <= (k + 1) * n by RIGHT_ADD_DISTRIB +*) +val MULTIPLE_SUC_LE = store_thm( + "MULTIPLE_SUC_LE", + ``!n k. 0 < n ==> k * n + 1 <= (k + 1) * n``, + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Simple Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < m /\ 0 < n /\ (m + n = 2) ==> m = 1 /\ n = 1 *) +(* Proof: by arithmetic. *) +val ADD_EQ_2 = store_thm( + "ADD_EQ_2", + ``!m n. 0 < m /\ 0 < n /\ (m + n = 2) ==> (m = 1) /\ (n = 1)``, + rw_tac arith_ss[]); + +(* Theorem: EVEN 0 *) +(* Proof: by EVEN. *) +val EVEN_0 = store_thm( + "EVEN_0", + ``EVEN 0``, + simp[]); + +(* Theorem: ODD 1 *) +(* Proof: by ODD. *) +val ODD_1 = store_thm( + "ODD_1", + ``ODD 1``, + simp[]); + +(* Theorem: EVEN 2 *) +(* Proof: by EVEN_MOD2. *) +val EVEN_2 = store_thm( + "EVEN_2", + ``EVEN 2``, + EVAL_TAC); + +(* +EVEN_ADD |- !m n. EVEN (m + n) <=> (EVEN m <=> EVEN n) +ODD_ADD |- !m n. ODD (m + n) <=> (ODD m <=/=> ODD n) +EVEN_MULT |- !m n. EVEN (m * n) <=> EVEN m \/ EVEN n +ODD_MULT |- !m n. ODD (m * n) <=> ODD m /\ ODD n +*) + +(* Derive theorems. *) +val EVEN_SQ = save_thm("EVEN_SQ", + EVEN_MULT |> SPEC ``n:num`` |> SPEC ``n:num`` |> SIMP_RULE arith_ss[] |> GEN_ALL); +(* val EVEN_SQ = |- !n. EVEN (n ** 2) <=> EVEN n: thm *) +val ODD_SQ = save_thm("ODD_SQ", + ODD_MULT |> SPEC ``n:num`` |> SPEC ``n:num`` |> SIMP_RULE arith_ss[] |> GEN_ALL); +(* val ODD_SQ = |- !n. ODD (n ** 2) <=> ODD n: thm *) + +(* Theorem: EVEN (2 * a + b) <=> EVEN b *) +(* Proof: + EVEN (2 * a + b) + <=> EVEN (2 * a) /\ EVEN b by EVEN_ADD + <=> T /\ EVEN b by EVEN_DOUBLE + <=> EVEN b +*) +Theorem EQ_PARITY: + !a b. EVEN (2 * a + b) <=> EVEN b +Proof + rw[EVEN_ADD, EVEN_DOUBLE] +QED + +(* Theorem: ODD x <=> (x MOD 2 = 1) *) +(* Proof: + If part: ODD x ==> x MOD 2 = 1 + Since ODD x + <=> ~EVEN x by ODD_EVEN + <=> ~(x MOD 2 = 0) by EVEN_MOD2 + But x MOD 2 < 2 by MOD_LESS, 0 < 2 + so x MOD 2 = 1 by arithmetic + Only-if part: x MOD 2 = 1 ==> ODD x + By contradiction, suppose ~ODD x. + Then EVEN x by ODD_EVEN + and x MOD 2 = 0 by EVEN_MOD2 + This contradicts x MOD 2 = 1. +*) +val ODD_MOD2 = store_thm( + "ODD_MOD2", + ``!x. ODD x <=> (x MOD 2 = 1)``, + metis_tac[EVEN_MOD2, ODD_EVEN, MOD_LESS, + DECIDE``0 <> 1 /\ 0 < 2 /\ !n. n < 2 /\ n <> 1 ==> (n = 0)``]); + +(* Theorem: (EVEN n <=> ODD (SUC n)) /\ (ODD n <=> EVEN (SUC n)) *) +(* Proof: by EVEN, ODD, EVEN_OR_ODD *) +val EVEN_ODD_SUC = store_thm( + "EVEN_ODD_SUC", + ``!n. (EVEN n <=> ODD (SUC n)) /\ (ODD n <=> EVEN (SUC n))``, + metis_tac[EVEN, ODD, EVEN_OR_ODD]); + +(* Theorem: 0 < n ==> (EVEN n <=> ODD (PRE n)) /\ (ODD n <=> EVEN (PRE n)) *) +(* Proof: by EVEN, ODD, EVEN_OR_ODD, PRE_SUC_EQ *) +val EVEN_ODD_PRE = store_thm( + "EVEN_ODD_PRE", + ``!n. 0 < n ==> (EVEN n <=> ODD (PRE n)) /\ (ODD n <=> EVEN (PRE n))``, + metis_tac[EVEN, ODD, EVEN_OR_ODD, PRE_SUC_EQ]); + +(* Theorem: EVEN (n * (n + 1)) *) +(* Proof: + If EVEN n, true by EVEN_MULT + If ~(EVEN n), + Then EVEN (SUC n) by EVEN + or EVEN (n + 1) by ADD1 + Thus true by EVEN_MULT +*) +val EVEN_PARTNERS = store_thm( + "EVEN_PARTNERS", + ``!n. EVEN (n * (n + 1))``, + metis_tac[EVEN, EVEN_MULT, ADD1]); + +(* Theorem: EVEN n ==> (n = 2 * HALF n) *) +(* Proof: + Note EVEN n ==> ?m. n = 2 * m by EVEN_EXISTS + and HALF n = HALF (2 * m) by above + = m by MULT_TO_DIV, 0 < 2 + Thus n = 2 * m = 2 * HALF n by above +*) +val EVEN_HALF = store_thm( + "EVEN_HALF", + ``!n. EVEN n ==> (n = 2 * HALF n)``, + metis_tac[EVEN_EXISTS, MULT_TO_DIV, DECIDE``0 < 2``]); + +(* Theorem: ODD n ==> (n = 2 * HALF n + 1 *) +(* Proof: + Since n = HALF n * 2 + n MOD 2 by DIVISION, 0 < 2 + = 2 * HALF n + n MOD 2 by MULT_COMM + = 2 * HALF n + 1 by ODD_MOD2 +*) +val ODD_HALF = store_thm( + "ODD_HALF", + ``!n. ODD n ==> (n = 2 * HALF n + 1)``, + metis_tac[DIVISION, MULT_COMM, ODD_MOD2, DECIDE``0 < 2``]); + +(* Theorem: EVEN n ==> (HALF (SUC n) = HALF n) *) +(* Proof: + Note n = (HALF n) * 2 + (n MOD 2) by DIVISION, 0 < 2 + = (HALF n) * 2 by EVEN_MOD2 + Now SUC n + = n + 1 by ADD1 + = (HALF n) * 2 + 1 by above + Thus HALF (SUC n) + = ((HALF n) * 2 + 1) DIV 2 by above + = HALF n by DIV_MULT, 1 < 2 +*) +val EVEN_SUC_HALF = store_thm( + "EVEN_SUC_HALF", + ``!n. EVEN n ==> (HALF (SUC n) = HALF n)``, + rpt strip_tac >> + `n MOD 2 = 0` by rw[GSYM EVEN_MOD2] >> + `n = HALF n * 2 + n MOD 2` by rw[DIVISION] >> + `SUC n = HALF n * 2 + 1` by rw[] >> + metis_tac[DIV_MULT, DECIDE``1 < 2``]); + +(* Theorem: ODD n ==> (HALF (SUC n) = SUC (HALF n)) *) +(* Proof: + SUC n + = SUC (2 * HALF n + 1) by ODD_HALF + = 2 * HALF n + 1 + 1 by ADD1 + = 2 * HALF n + 2 by arithmetic + = 2 * (HALF n + 1) by LEFT_ADD_DISTRIB + = 2 * SUC (HALF n) by ADD1 + = SUC (HALF n) * 2 + 0 by MULT_COMM, ADD_0 + Hence HALF (SUC n) = SUC (HALF n) by DIV_UNIQUE, 0 < 2 +*) +val ODD_SUC_HALF = store_thm( + "ODD_SUC_HALF", + ``!n. ODD n ==> (HALF (SUC n) = SUC (HALF n))``, + rpt strip_tac >> + `SUC n = SUC (2 * HALF n + 1)` by rw[ODD_HALF] >> + `_ = SUC (HALF n) * 2 + 0` by rw[] >> + metis_tac[DIV_UNIQUE, DECIDE``0 < 2``]); + +(* Theorem: (HALF n = 0) <=> ((n = 0) \/ (n = 1)) *) +(* Proof: + If part: (HALF n = 0) ==> ((n = 0) \/ (n = 1)) + Note n = (HALF n) * 2 + (n MOD 2) by DIVISION, 0 < 2 + = n MOD 2 by HALF n = 0 + and n MOD 2 < 2 by MOD_LESS, 0 < 2 + so n < 2, or n = 0 or n = 1 by arithmetic + Only-if part: HALF 0 = 0, HALF 1 = 0. + True since both 0 or 1 < 2 by LESS_DIV_EQ_ZERO, 0 < 2 +*) +val HALF_EQ_0 = store_thm( + "HALF_EQ_0", + ``!n. (HALF n = 0) <=> ((n = 0) \/ (n = 1))``, + rw[LESS_DIV_EQ_ZERO, EQ_IMP_THM] >> + `n = (HALF n) * 2 + (n MOD 2)` by rw[DIVISION] >> + `n MOD 2 < 2` by rw[MOD_LESS] >> + decide_tac); + +(* Theorem: (HALF n = n) <=> (n = 0) *) +(* Proof: + If part: HALF n = n ==> n = 0 + Note n = 2 * HALF n + (n MOD 2) by DIVISION, MULT_COMM + so n = 2 * n + (n MOD 2) by HALF n = n + or 0 = n + (n MOD 2) by arithmetic + Thus n = 0 and (n MOD 2 = 0) by ADD_EQ_0 + Only-if part: HALF 0 = 0, true by ZERO_DIV, 0 < 2 +*) +val HALF_EQ_SELF = store_thm( + "HALF_EQ_SELF", + ``!n. (HALF n = n) <=> (n = 0)``, + rw[EQ_IMP_THM] >> + `n = 2 * HALF n + (n MOD 2)` by metis_tac[DIVISION, MULT_COMM, DECIDE``0 < 2``] >> + rw[ADD_EQ_0]); + +(* Theorem: 0 < n ==> HALF n < n *) +(* Proof: + Note HALF n <= n by DIV_LESS_EQ, 0 < 2 + and HALF n <> n by HALF_EQ_SELF, n <> 0 + so HALF n < n by arithmetic +*) +val HALF_LT = store_thm( + "HALF_LT", + ``!n. 0 < n ==> HALF n < n``, + rpt strip_tac >> + `HALF n <= n` by rw[DIV_LESS_EQ] >> + `HALF n <> n` by rw[HALF_EQ_SELF] >> + decide_tac); + +(* Theorem: 2 < n ==> (1 + HALF n < n) *) +(* Proof: + If EVEN n, + then 2 * HALF n = n by EVEN_HALF + so 2 + 2 * HALF n < n + n by 2 < n + or 1 + HALF n < n by arithmetic + If ~EVEN n, then ODD n by ODD_EVEN + then 1 + 2 * HALF n = 2 by ODD_HALF + so 1 + 2 * HALF n < n by 2 < n + also 2 + 2 * HALF n < n + n by 1 < n + or 1 + HALF n < n by arithmetic +*) +Theorem HALF_ADD1_LT: + !n. 2 < n ==> 1 + HALF n < n +Proof + rpt strip_tac >> + Cases_on `EVEN n` >| [ + `2 * HALF n = n` by rw[EVEN_HALF] >> + decide_tac, + `1 + 2 * HALF n = n` by rw[ODD_HALF, ODD_EVEN] >> + decide_tac + ] +QED + +(* Theorem alias *) +Theorem HALF_TWICE = MULT_DIV_2; +(* val HALF_TWICE = |- !n. HALF (2 * n) = n: thm *) + +(* Theorem: n * HALF m <= HALF (n * m) *) +(* Proof: + Let k = HALF m. + If EVEN m, + Then m = 2 * k by EVEN_HALF + HALF (n * m) + = HALF (n * (2 * k)) by above + = HALF (2 * (n * k)) by arithmetic + = n * k by HALF_TWICE + If ~EVEN m, then ODD m by ODD_EVEN + Then m = 2 * k + 1 by ODD_HALF + so HALF (n * m) + = HALF (n * (2 * k + 1)) by above + = HALF (2 * (n * k) + n) by LEFT_ADD_DISTRIB + = HALF (2 * (n * k)) + HALF n by ADD_DIV_ADD_DIV + = n * k + HALF n by HALF_TWICE + >= n * k by arithmetic +*) +Theorem HALF_MULT: !m n. n * (m DIV 2) <= (n * m) DIV 2 +Proof + rpt strip_tac >> + qabbrev_tac `k = m DIV 2` >> + Cases_on `EVEN m` + >- (`m = 2 * k` by rw[EVEN_HALF, Abbr`k`] >> + simp[]) >> + `ODD m` by rw[ODD_EVEN] >> + `m = 2 * k + 1` by rw[ODD_HALF, Abbr`k`] >> + simp[LEFT_ADD_DISTRIB] +QED + +(* Theorem: 2 * HALF n <= n /\ n <= SUC (2 * HALF n) *) +(* Proof: + If EVEN n, + Then n = 2 * HALF n by EVEN_HALF + and n = n < SUC n by LESS_SUC + or n <= n <= SUC n, + Giving 2 * HALF n <= n /\ n <= SUC (2 * HALF n) + If ~(EVEN n), then ODD n by EVEN_ODD + Then n = 2 * HALF n + 1 by ODD_HALF + = SUC (2 * HALF n) by ADD1 + or n - 1 < n = n + or n - 1 <= n <= n, + Giving 2 * HALF n <= n /\ n <= SUC (2 * HALF n) +*) +val TWO_HALF_LE_THM = store_thm( + "TWO_HALF_LE_THM", + ``!n. 2 * HALF n <= n /\ n <= SUC (2 * HALF n)``, + strip_tac >> + Cases_on `EVEN n` >- + rw[GSYM EVEN_HALF] >> + `ODD n` by rw[ODD_EVEN] >> + `n <> 0` by metis_tac[ODD] >> + `n = SUC (2 * HALF n)` by rw[ODD_HALF, ADD1] >> + `2 * HALF n = PRE n` by rw[] >> + rw[]); + +(* Theorem: 2 * ((HALF n) * m) <= n * m *) +(* Proof: + 2 * ((HALF n) * m) + = 2 * (m * HALF n) by MULT_COMM + <= 2 * (HALF (m * n)) by HALF_MULT + <= m * n by TWO_HALF_LE_THM + = n * m by MULT_COMM +*) +val TWO_HALF_TIMES_LE = store_thm( + "TWO_HALF_TIMES_LE", + ``!m n. 2 * ((HALF n) * m) <= n * m``, + rpt strip_tac >> + `2 * (m * HALF n) <= 2 * (HALF (m * n))` by rw[HALF_MULT] >> + `2 * (HALF (m * n)) <= m * n` by rw[TWO_HALF_LE_THM] >> + fs[]); + +(* Theorem: 0 < n ==> 1 + HALF n <= n *) +(* Proof: + If n = 1, + HALF 1 = 0, hence true. + If n <> 1, + Then HALF n <> 0 by HALF_EQ_0, n <> 0, n <> 1 + Thus 1 + HALF n + <= HALF n + HALF n by 1 <= HALF n + = 2 * HALF n + <= n by TWO_HALF_LE_THM +*) +val HALF_ADD1_LE = store_thm( + "HALF_ADD1_LE", + ``!n. 0 < n ==> 1 + HALF n <= n``, + rpt strip_tac >> + (Cases_on `n = 1` >> simp[]) >> + `HALF n <> 0` by metis_tac[HALF_EQ_0, NOT_ZERO] >> + `1 + HALF n <= 2 * HALF n` by decide_tac >> + `2 * HALF n <= n` by rw[TWO_HALF_LE_THM] >> + decide_tac); + +(* Theorem: (HALF n) ** 2 <= (n ** 2) DIV 4 *) +(* Proof: + Let k = HALF n. + Then 2 * k <= n by TWO_HALF_LE_THM + so (2 * k) ** 2 <= n ** 2 by EXP_EXP_LE_MONO + and (2 * k) ** 2 DIV 4 <= n ** 2 DIV 4 by DIV_LE_MONOTONE, 0 < 4 + But (2 * k) ** 2 DIV 4 + = 4 * k ** 2 DIV 4 by EXP_BASE_MULT + = k ** 2 by MULT_TO_DIV, 0 < 4 + Thus k ** 2 <= n ** 2 DIV 4. +*) +val HALF_SQ_LE = store_thm( + "HALF_SQ_LE", + ``!n. (HALF n) ** 2 <= (n ** 2) DIV 4``, + rpt strip_tac >> + qabbrev_tac `k = HALF n` >> + `2 * k <= n` by rw[TWO_HALF_LE_THM, Abbr`k`] >> + `(2 * k) ** 2 <= n ** 2` by rw[] >> + `(2 * k) ** 2 DIV 4 <= n ** 2 DIV 4` by rw[DIV_LE_MONOTONE] >> + `(2 * k) ** 2 DIV 4 = 4 * k ** 2 DIV 4` by rw[EXP_BASE_MULT] >> + `_ = k ** 2` by rw[MULT_TO_DIV] >> + decide_tac); + +(* Obtain theorems *) +val HALF_LE = save_thm("HALF_LE", + DIV_LESS_EQ |> SPEC ``2`` |> SIMP_RULE (arith_ss) [] |> SPEC ``n:num`` |> GEN_ALL); +(* val HALF_LE = |- !n. HALF n <= n: thm *) +val HALF_LE_MONO = save_thm("HALF_LE_MONO", + DIV_LE_MONOTONE |> SPEC ``2`` |> SIMP_RULE (arith_ss) []); +(* val HALF_LE_MONO = |- !x y. x <= y ==> HALF x <= HALF y: thm *) + +(* Theorem: HALF (SUC n) <= n *) +(* Proof: + If EVEN n, + Then ?k. n = 2 * k by EVEN_EXISTS + and SUC n = 2 * k + 1 + so HALF (SUC n) = k <= k + k = n by ineqaulities + Otherwise ODD n, by ODD_EVEN + Then ?k. n = 2 * k + 1 by ODD_EXISTS + and SUC n = 2 * k + 2 + so HALF (SUC n) = k + 1 <= k + k + 1 = n +*) +Theorem HALF_SUC: + !n. HALF (SUC n) <= n +Proof + rpt strip_tac >> + Cases_on `EVEN n` >| [ + `?k. n = 2 * k` by metis_tac[EVEN_EXISTS] >> + `HALF (SUC n) = k` by simp[ADD1] >> + decide_tac, + `?k. n = 2 * k + 1` by metis_tac[ODD_EXISTS, ODD_EVEN, ADD1] >> + `HALF (SUC n) = k + 1` by simp[ADD1] >> + decide_tac + ] +QED + +(* Theorem: 0 < n ==> HALF (SUC (SUC n)) <= n *) +(* Proof: + Note SUC (SUC n) = n + 2 by ADD1 + If EVEN n, + then ?k. n = 2 * k by EVEN_EXISTS + Since n = 2 * k <> 0 by NOT_ZERO, 0 < n + so k <> 0, or 1 <= k by MULT_EQ_0 + HALF (n + 2) + = k + 1 by arithmetic + <= k + k by above + = n + Otherwise ODD n, by ODD_EVEN + then ?k. n = 2 * k + 1 by ODD_EXISTS + HALF (n + 2) + = HALF (2 * k + 3) by arithmetic + = k + 1 by arithmetic + <= k + k + 1 by ineqaulities + = n +*) +Theorem HALF_SUC_SUC: + !n. 0 < n ==> HALF (SUC (SUC n)) <= n +Proof + rpt strip_tac >> + Cases_on `EVEN n` >| [ + `?k. n = 2 * k` by metis_tac[EVEN_EXISTS] >> + `0 < k` by metis_tac[MULT_EQ_0, NOT_ZERO] >> + `1 <= k` by decide_tac >> + `HALF (SUC (SUC n)) = k + 1` by simp[ADD1] >> + fs[], + `?k. n = 2 * k + 1` by metis_tac[ODD_EXISTS, ODD_EVEN, ADD1] >> + `HALF (SUC (SUC n)) = k + 1` by simp[ADD1] >> + fs[] + ] +QED + +(* Theorem: n < HALF (SUC m) ==> 2 * n + 1 <= m *) +(* Proof: + If EVEN m, + Then m = 2 * HALF m by EVEN_HALF + and SUC m = 2 * HALF m + 1 by ADD1 + so n < (2 * HALF m + 1) DIV 2 by given + or n < HALF m by arithmetic + 2 * n < 2 * HALF m by LT_MULT_LCANCEL + 2 * n < m by above + 2 * n + 1 <= m by arithmetic + Otherwise, ODD m by ODD_EVEN + Then m = 2 * HALF m + 1 by ODD_HALF + and SUC m = 2 * HALF m + 2 by ADD1 + so n < (2 * HALF m + 2) DIV 2 by given + or n < HALF m + 1 by arithmetic + 2 * n + 1 < 2 * HALF m + 1 by LT_MULT_LCANCEL, LT_ADD_RCANCEL + or 2 * n + 1 < m by above + Overall, 2 * n + 1 <= m. +*) +Theorem HALF_SUC_LE: + !n m. n < HALF (SUC m) ==> 2 * n + 1 <= m +Proof + rpt strip_tac >> + Cases_on `EVEN m` >| [ + `m = 2 * HALF m` by simp[EVEN_HALF] >> + `HALF (SUC m) = HALF (2 * HALF m + 1)` by metis_tac[ADD1] >> + `_ = HALF m` by simp[] >> + simp[], + `m = 2 * HALF m + 1` by simp[ODD_HALF, ODD_EVEN] >> + `HALF (SUC m) = HALF (2 * HALF m + 1 + 1)` by metis_tac[ADD1] >> + `_ = HALF m + 1` by simp[] >> + simp[] + ] +QED + +(* Theorem: 2 * n < m ==> n <= HALF m *) +(* Proof: + If EVEN m, + Then m = 2 * HALF m by EVEN_HALF + so 2 * n < 2 * HALF m by above + or n < HALF m by LT_MULT_LCANCEL + Otherwise, ODD m by ODD_EVEN + Then m = 2 * HALF m + 1 by ODD_HALF + so 2 * n < 2 * HALF m + 1 by above + so 2 * n <= 2 * HALF m by removing 1 + or n <= HALF m by LE_MULT_LCANCEL + Overall, n <= HALF m. +*) +Theorem HALF_EVEN_LE: + !n m. 2 * n < m ==> n <= HALF m +Proof + rpt strip_tac >> + Cases_on `EVEN m` >| [ + `2 * n < 2 * HALF m` by metis_tac[EVEN_HALF] >> + simp[], + `2 * n < 2 * HALF m + 1` by metis_tac[ODD_HALF, ODD_EVEN] >> + simp[] + ] +QED + +(* Theorem: 2 * n + 1 < m ==> n < HALF m *) +(* Proof: + If EVEN m, + Then m = 2 * HALF m by EVEN_HALF + so 2 * n + 1 < 2 * HALF m by above + or 2 * n < 2 * HALF m by removing 1 + or n < HALF m by LT_MULT_LCANCEL + Otherwise, ODD m by ODD_EVEN + Then m = 2 * HALF m + 1 by ODD_HALF + so 2 * n + 1 < 2 * HALF m + 1 by above + or 2 * n < 2 * HALF m by LT_ADD_RCANCEL + or n < HALF m by LT_MULT_LCANCEL + Overall, n < HALF m. +*) +Theorem HALF_ODD_LT: + !n m. 2 * n + 1 < m ==> n < HALF m +Proof + rpt strip_tac >> + Cases_on `EVEN m` >| [ + `2 * n + 1 < 2 * HALF m` by metis_tac[EVEN_HALF] >> + simp[], + `2 * n + 1 < 2 * HALF m + 1` by metis_tac[ODD_HALF, ODD_EVEN] >> + simp[] + ] +QED + +(* Theorem: EVEN n ==> !m. m * n = (TWICE m) * (HALF n) *) +(* Proof: + (TWICE m) * (HALF n) + = (2 * m) * (HALF n) by notation + = m * TWICE (HALF n) by MULT_COMM, MULT_ASSOC + = m * n by EVEN_HALF +*) +val MULT_EVEN = store_thm( + "MULT_EVEN", + ``!n. EVEN n ==> !m. m * n = (TWICE m) * (HALF n)``, + metis_tac[MULT_COMM, MULT_ASSOC, EVEN_HALF]); + +(* Theorem: ODD n ==> !m. m * n = m + (TWICE m) * (HALF n) *) +(* Proof: + m + (TWICE m) * (HALF n) + = m + (2 * m) * (HALF n) by notation + = m + m * (TWICE (HALF n)) by MULT_COMM, MULT_ASSOC + = m * (SUC (TWICE (HALF n))) by MULT_SUC + = m * (TWICE (HALF n) + 1) by ADD1 + = m * n by ODD_HALF +*) +val MULT_ODD = store_thm( + "MULT_ODD", + ``!n. ODD n ==> !m. m * n = m + (TWICE m) * (HALF n)``, + metis_tac[MULT_COMM, MULT_ASSOC, ODD_HALF, MULT_SUC, ADD1]); + +(* Theorem: EVEN m /\ m <> 0 ==> !n. EVEN n <=> EVEN (n MOD m) *) +(* Proof: + Note ?k. m = 2 * k by EVEN_EXISTS, EVEN m + and k <> 0 by MULT_EQ_0, m <> 0 + ==> (n MOD m) MOD 2 = n MOD 2 by MOD_MULT_MOD + The result follows by EVEN_MOD2 +*) +val EVEN_MOD_EVEN = store_thm( + "EVEN_MOD_EVEN", + ``!m. EVEN m /\ m <> 0 ==> !n. EVEN n <=> EVEN (n MOD m)``, + rpt strip_tac >> + `?k. m = 2 * k` by rw[GSYM EVEN_EXISTS] >> + `(n MOD m) MOD 2 = n MOD 2` by rw[MOD_MULT_MOD] >> + metis_tac[EVEN_MOD2]); + +(* Theorem: EVEN m /\ m <> 0 ==> !n. ODD n <=> ODD (n MOD m) *) +(* Proof: by EVEN_MOD_EVEN, ODD_EVEN *) +val EVEN_MOD_ODD = store_thm( + "EVEN_MOD_ODD", + ``!m. EVEN m /\ m <> 0 ==> !n. ODD n <=> ODD (n MOD m)``, + rw_tac std_ss[EVEN_MOD_EVEN, ODD_EVEN]); + +(* Theorem: c <= a ==> ((a - b) - (a - c) = c - b) *) +(* Proof: + a - b - (a - c) + = a - (b + (a - c)) by SUB_RIGHT_SUB, no condition + = a - ((a - c) + b) by ADD_COMM, no condition + = a - (a - c) - b by SUB_RIGHT_SUB, no condition + = a + c - a - b by SUB_SUB, c <= a + = c + a - a - b by ADD_COMM, no condition + = c + (a - a) - b by LESS_EQ_ADD_SUB, a <= a + = c + 0 - b by SUB_EQUAL_0 + = c - b +*) +val SUB_SUB_SUB = store_thm( + "SUB_SUB_SUB", + ``!a b c. c <= a ==> ((a - b) - (a - c) = c - b)``, + decide_tac); + +(* Theorem: c <= a ==> (a + b - (a - c) = c + b) *) +(* Proof: + a + b - (a - c) + = a + b + c - a by SUB_SUB, a <= c + = a + (b + c) - a by ADD_ASSOC + = (b + c) + a - a by ADD_COMM + = b + c - (a - a) by SUB_SUB, a <= a + = b + c - 0 by SUB_EQUAL_0 + = b + c by SUB_0 +*) +val ADD_SUB_SUB = store_thm( + "ADD_SUB_SUB", + ``!a b c. c <= a ==> (a + b - (a - c) = c + b)``, + decide_tac); + +(* Theorem: 0 < p ==> !m n. (m - n = p) <=> (m = n + p) *) +(* Proof: + If part: m - n = p ==> m = n + p + Note 0 < m - n by 0 < p + so n < m by LESS_MONO_ADD + or m = m - n + n by SUB_ADD, n <= m + = p + n by m - n = p + = n + p by ADD_COMM + Only-if part: m = n + p ==> m - n = p + m - n + = (n + p) - n by m = n + p + = p + n - n by ADD_COMM + = p by ADD_SUB +*) +val SUB_EQ_ADD = store_thm( + "SUB_EQ_ADD", + ``!p. 0 < p ==> !m n. (m - n = p) <=> (m = n + p)``, + decide_tac); + +(* Note: ADD_EQ_SUB |- !m n p. n <= p ==> ((m + n = p) <=> (m = p - n)) *) + +(* Theorem: 0 < a /\ 0 < b /\ a < c /\ (a * b = c * d) ==> (d < b) *) +(* Proof: + By contradiction, suppose b <= d. + Since a * b <> 0 by MULT_EQ_0, 0 < a, 0 < b + so d <> 0, or 0 < d by MULT_EQ_0, a * b <> 0 + Now a * b <= a * d by LE_MULT_LCANCEL, b <= d, a <> 0 + and a * d < c * d by LT_MULT_LCANCEL, a < c, d <> 0 + so a * b < c * d by LESS_EQ_LESS_TRANS + This contradicts a * b = c * d. +*) +val MULT_EQ_LESS_TO_MORE = store_thm( + "MULT_EQ_LESS_TO_MORE", + ``!a b c d. 0 < a /\ 0 < b /\ a < c /\ (a * b = c * d) ==> (d < b)``, + spose_not_then strip_assume_tac >> + `b <= d` by decide_tac >> + `0 < d` by decide_tac >> + `a * b <= a * d` by rw[LE_MULT_LCANCEL] >> + `a * d < c * d` by rw[LT_MULT_LCANCEL] >> + decide_tac); + +(* Theorem: 0 < c /\ 0 < d /\ a * b <= c * d /\ d < b ==> a < c *) +(* Proof: + By contradiction, suppose c <= a. + With d < b, which gives d <= b by LESS_IMP_LESS_OR_EQ + Thus c * d <= a * b by LE_MONO_MULT2 + or a * b = c * d by a * b <= c * d + Note 0 < c /\ 0 < d by given + ==> a < c by MULT_EQ_LESS_TO_MORE + This contradicts c <= a. + +MULT_EQ_LESS_TO_MORE +|- !a b c d. 0 < a /\ 0 < b /\ a < c /\ a * b = c * d ==> d < b + 0 < d /\ 0 < c /\ d < b /\ d * c = b * a ==> a < c +*) +val LE_IMP_REVERSE_LT = store_thm( + "LE_IMP_REVERSE_LT", + ``!a b c d. 0 < c /\ 0 < d /\ a * b <= c * d /\ d < b ==> a < c``, + spose_not_then strip_assume_tac >> + `c <= a` by decide_tac >> + `c * d <= a * b` by rw[LE_MONO_MULT2] >> + `a * b = c * d` by decide_tac >> + `a < c` by metis_tac[MULT_EQ_LESS_TO_MORE, MULT_COMM]); + +(* ------------------------------------------------------------------------- *) +(* Exponential Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: EVEN n ==> !m. m ** n = (SQ m) ** (HALF n) *) +(* Proof: + (SQ m) ** (HALF n) + = (m ** 2) ** (HALF n) by notation + = m ** (2 * HALF n) by EXP_EXP_MULT + = m ** n by EVEN_HALF +*) +val EXP_EVEN = store_thm( + "EXP_EVEN", + ``!n. EVEN n ==> !m. m ** n = (SQ m) ** (HALF n)``, + rpt strip_tac >> + `(SQ m) ** (HALF n) = m ** (2 * HALF n)` by rw[EXP_EXP_MULT] >> + `_ = m ** n` by rw[GSYM EVEN_HALF] >> + rw[]); + +(* Theorem: ODD n ==> !m. m ** n = m * (SQ m) ** (HALF n) *) +(* Proof: + m * (SQ m) ** (HALF n) + = m * (m ** 2) ** (HALF n) by notation + = m * m ** (2 * HALF n) by EXP_EXP_MULT + = m ** (SUC (2 * HALF n)) by EXP + = m ** (2 * HALF n + 1) by ADD1 + = m ** n by ODD_HALF +*) +val EXP_ODD = store_thm( + "EXP_ODD", + ``!n. ODD n ==> !m. m ** n = m * (SQ m) ** (HALF n)``, + rpt strip_tac >> + `m * (SQ m) ** (HALF n) = m * m ** (2 * HALF n)` by rw[EXP_EXP_MULT] >> + `_ = m ** (2 * HALF n + 1)` by rw[GSYM EXP, ADD1] >> + `_ = m ** n` by rw[GSYM ODD_HALF] >> + rw[]); + +(* An exponentiation identity *) +(* val EXP_THM = save_thm("EXP_THM", CONJ EXP_EVEN EXP_ODD); *) +(* +val EXP_THM = |- (!n. EVEN n ==> !m. m ** n = SQ m ** HALF n) /\ + !n. ODD n ==> !m. m ** n = m * SQ m ** HALF n: thm +*) +(* Next is better *) + +(* Theorem: m ** n = if n = 0 then 1 else if n = 1 then m else + if EVEN n then (m * m) ** HALF n else m * ((m * m) ** (HALF n)) *) +(* Proof: mainly by EXP_EVEN, EXP_ODD. *) +Theorem EXP_THM: + !m n. m ** n = if n = 0 then 1 else if n = 1 then m + else if EVEN n then (m * m) ** HALF n + else m * ((m * m) ** (HALF n)) +Proof + metis_tac[EXP_0, EXP_1, EXP_EVEN, EXP_ODD, EVEN_ODD] +QED + +(* Theorem: m ** n = + if n = 0 then 1 + else if EVEN n then (m * m) ** (HALF n) else m * (m * m) ** (HALF n) *) +(* Proof: + If n = 0, to show: + m ** 0 = 1, true by EXP_0 + If EVEN n, to show: + m ** n = (m * m) ** (HALF n), true by EXP_EVEN + If ~EVEN n, ODD n, to show: by EVEN_ODD + m ** n = m * (m * m) ** HALF n, true by EXP_ODD +*) +val EXP_EQN = store_thm( + "EXP_EQN", + ``!m n. m ** n = + if n = 0 then 1 + else if EVEN n then (m * m) ** (HALF n) else m * (m * m) ** (HALF n)``, + rw[] >- + rw[Once EXP_EVEN] >> + `ODD n` by metis_tac[EVEN_ODD] >> + rw[Once EXP_ODD]); + +(* Theorem: m ** n = if n = 0 then 1 else (if EVEN n then 1 else m) * (m * m) ** (HALF n) *) +(* Proof: by EXP_EQN *) +val EXP_EQN_ALT = store_thm( + "EXP_EQN_ALT", + ``!m n. m ** n = + if n = 0 then 1 + else (if EVEN n then 1 else m) * (m * m) ** (HALF n)``, + rw[Once EXP_EQN]); + +(* Theorem: m ** n = (if n = 0 then 1 else (if EVEN n then 1 else m) * (m ** 2) ** HALF n) *) +(* Proof: by EXP_EQN_ALT, EXP_2 *) +val EXP_ALT_EQN = store_thm( + "EXP_ALT_EQN", + ``!m n. m ** n = (if n = 0 then 1 else (if EVEN n then 1 else m) * (m ** 2) ** HALF n)``, + metis_tac[EXP_EQN_ALT, EXP_2]); + +(* Theorem: 1 < m ==> + (b ** n) MOD m = if (n = 0) then 1 + else let result = (b * b) ** (HALF n) MOD m + in if EVEN n then result else (b * result) MOD m *) +(* Proof: + This is to show: + (1) 1 < m ==> b ** 0 MOD m = 1 + b ** 0 MOD m + = 1 MOD m by EXP_0 + = 1 by ONE_MOD, 1 < m + (2) EVEN n ==> b ** n MOD m = (b ** 2) ** HALF n MOD m + b ** n MOD m + = (b * b) ** HALF n MOD m by EXP_EVEN + = (b ** 2) ** HALF n MOD m by EXP_2 + (3) ~EVEN n ==> b ** n MOD m = (b * (b ** 2) ** HALF n) MOD m + b ** n MOD m + = (b * (b * b) ** HALF n) MOD m by EXP_ODD, EVEN_ODD + = (b * (b ** 2) ** HALF n) MOD m by EXP_2 +*) +Theorem EXP_MOD_EQN: + !b n m. 1 < m ==> + ((b ** n) MOD m = + if (n = 0) then 1 + else let result = (b * b) ** (HALF n) MOD m + in if EVEN n then result else (b * result) MOD m) +Proof + rw[] + >- metis_tac[EXP_EVEN, EXP_2] >> + metis_tac[EXP_ODD, EXP_2, EVEN_ODD] +QED + +(* Pretty version of EXP_MOD_EQN, same pattern as EXP_EQN_ALT. *) + +(* Theorem: 1 < m ==> b ** n MOD m = + if n = 0 then 1 else + ((if EVEN n then 1 else b) * ((SQ b ** HALF n) MOD m)) MOD m *) +(* Proof: by EXP_MOD_EQN *) +val EXP_MOD_ALT = store_thm( + "EXP_MOD_ALT", + ``!b n m. 1 < m ==> b ** n MOD m = + if n = 0 then 1 else + ((if EVEN n then 1 else b) * ((SQ b ** HALF n) MOD m)) MOD m``, + rpt strip_tac >> + imp_res_tac EXP_MOD_EQN >> + last_x_assum (qspecl_then [`n`, `b`] strip_assume_tac) >> + rw[]); + +(* Theorem: x ** (y ** SUC n) = (x ** y) ** y ** n *) +(* Proof: + x ** (y ** SUC n) + = x ** (y * y ** n) by EXP + = (x ** y) ** (y ** n) by EXP_EXP_MULT +*) +val EXP_EXP_SUC = store_thm( + "EXP_EXP_SUC", + ``!x y n. x ** (y ** SUC n) = (x ** y) ** y ** n``, + rw[EXP, EXP_EXP_MULT]); + +(* Theorem: 1 + n * m <= (1 + m) ** n *) +(* Proof: + By induction on n. + Base: 1 + 0 * m <= (1 + m) ** 0 + LHS = 1 + 0 * m = 1 by arithmetic + RHS = (1 + m) ** 0 = 1 by EXP_0 + Hence true. + Step: 1 + n * m <= (1 + m) ** n ==> + 1 + SUC n * m <= (1 + m) ** SUC n + 1 + SUC n * m + = 1 + n * m + m by MULT + <= (1 + m) ** n + m by induction hypothesis + <= (1 + m) ** n + m * (1 + m) ** n by EXP_POS + <= (1 + m) * (1 + m) ** n by RIGHT_ADD_DISTRIB + = (1 + m) ** SUC n by EXP +*) +val EXP_LOWER_LE_LOW = store_thm( + "EXP_LOWER_LE_LOW", + ``!n m. 1 + n * m <= (1 + m) ** n``, + rpt strip_tac >> + Induct_on `n` >- + rw[EXP_0] >> + `0 < (1 + m) ** n` by rw[] >> + `1 + SUC n * m = 1 + (n * m + m)` by rw[MULT] >> + `_ = 1 + n * m + m` by decide_tac >> + `m <= m * (1 + m) ** n` by rw[] >> + `1 + SUC n * m <= (1 + m) ** n + m * (1 + m) ** n` by decide_tac >> + `(1 + m) ** n + m * (1 + m) ** n = (1 + m) * (1 + m) ** n` by decide_tac >> + `_ = (1 + m) ** SUC n` by rw[EXP] >> + decide_tac); + +(* Theorem: 0 < m /\ 1 < n ==> 1 + n * m < (1 + m) ** n *) +(* Proof: + By induction on n. + Base: 1 < 0 ==> 1 + 0 * m <= (1 + m) ** 0 + True since 1 < 0 = F. + Step: 1 < n ==> 1 + n * m < (1 + m) ** n ==> + 1 < SUC n ==> 1 + SUC n * m < (1 + m) ** SUC n + Note n <> 0, since SUC 0 = 1 by ONE + If n = 1, + Note m * m <> 0 by MULT_EQ_0, m <> 0 + (1 + m) ** SUC 1 + = (1 + m) ** 2 by TWO + = 1 + 2 * m + m * m by expansion + > 1 + 2 * m by 0 < m * m + = 1 + (SUC 1) * m + If n <> 1, then 1 < n. + 1 + SUC n * m + = 1 + n * m + m by MULT + < (1 + m) ** n + m by induction hypothesis, 1 < n + <= (1 + m) ** n + m * (1 + m) ** n by EXP_POS + <= (1 + m) * (1 + m) ** n by RIGHT_ADD_DISTRIB + = (1 + m) ** SUC n by EXP +*) +val EXP_LOWER_LT_LOW = store_thm( + "EXP_LOWER_LT_LOW", + ``!n m. 0 < m /\ 1 < n ==> 1 + n * m < (1 + m) ** n``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + rpt strip_tac >> + `n <> 0` by fs[] >> + Cases_on `n = 1` >| [ + simp[] >> + `(m + 1) ** 2 = (m + 1) * (m + 1)` by rw[GSYM EXP_2] >> + `_ = m * m + 2 * m + 1` by decide_tac >> + `0 < SQ m` by metis_tac[SQ_EQ_0, NOT_ZERO_LT_ZERO] >> + decide_tac, + `1 < n` by decide_tac >> + `0 < (1 + m) ** n` by rw[] >> + `1 + SUC n * m = 1 + (n * m + m)` by rw[MULT] >> + `_ = 1 + n * m + m` by decide_tac >> + `m <= m * (1 + m) ** n` by rw[] >> + `1 + SUC n * m < (1 + m) ** n + m * (1 + m) ** n` by decide_tac >> + `(1 + m) ** n + m * (1 + m) ** n = (1 + m) * (1 + m) ** n` by decide_tac >> + `_ = (1 + m) ** SUC n` by rw[EXP] >> + decide_tac + ]); + +(* +Note: EXP_LOWER_LE_LOW collects the first two terms of binomial expansion. + but EXP_LOWER_LE_HIGH collects the last two terms of binomial expansion. +*) + +(* Theorem: n * m ** (n - 1) + m ** n <= (1 + m) ** n *) +(* Proof: + By induction on n. + Base: 0 * m ** (0 - 1) + m ** 0 <= (1 + m) ** 0 + LHS = 0 * m ** (0 - 1) + m ** 0 + = 0 + 1 by EXP_0 + = 1 + <= 1 = (1 + m) ** 0 = RHS by EXP_0 + Step: n * m ** (n - 1) + m ** n <= (1 + m) ** n ==> + SUC n * m ** (SUC n - 1) + m ** SUC n <= (1 + m) ** SUC n + If n = 0, + LHS = 1 * m ** 0 + m ** 1 + = 1 + m by EXP_0, EXP_1 + = (1 + m) ** 1 = RHS by EXP_1 + If n <> 0, + Then SUC (n - 1) = n by n <> 0. + LHS = SUC n * m ** (SUC n - 1) + m ** SUC n + = (n + 1) * m ** n + m * m ** n by EXP, ADD1 + = (n + 1 + m) * m ** n by arithmetic + = n * m ** n + (1 + m) * m ** n by arithmetic + = n * m ** SUC (n - 1) + (1 + m) * m ** n by SUC (n - 1) = n + = n * m * m ** (n - 1) + (1 + m) * m ** n by EXP + = m * (n * m ** (n - 1)) + (1 + m) * m ** n by arithmetic + <= (1 + m) * (n * m ** (n - 1)) + (1 + m) * m ** n by m < 1 + m + = (1 + m) * (n * m ** (n - 1) + m ** n) by LEFT_ADD_DISTRIB + <= (1 + m) * (1 + m) ** n by induction hypothesis + = (1 + m) ** SUC n by EXP +*) +val EXP_LOWER_LE_HIGH = store_thm( + "EXP_LOWER_LE_HIGH", + ``!n m. n * m ** (n - 1) + m ** n <= (1 + m) ** n``, + rpt strip_tac >> + Induct_on `n` >- + simp[] >> + Cases_on `n = 0` >- + simp[EXP_0] >> + `SUC (n - 1) = n` by decide_tac >> + simp[EXP] >> + simp[ADD1] >> + `m * m ** n + (n + 1) * m ** n = (m + (n + 1)) * m ** n` by rw[LEFT_ADD_DISTRIB] >> + `_ = (n + (m + 1)) * m ** n` by decide_tac >> + `_ = n * m ** n + (m + 1) * m ** n` by rw[LEFT_ADD_DISTRIB] >> + `_ = n * m ** SUC (n - 1) + (m + 1) * m ** n` by rw[] >> + `_ = n * (m * m ** (n - 1)) + (m + 1) * m ** n` by rw[EXP] >> + `_ = m * (n * m ** (n - 1)) + (m + 1) * m ** n` by decide_tac >> + `m * (n * m ** (n - 1)) + (m + 1) * m ** n <= (m + 1) * (n * m ** (n - 1)) + (m + 1) * m ** n` by decide_tac >> + qabbrev_tac `t = n * m ** (n - 1) + m ** n` >> + `(m + 1) * (n * m ** (n - 1)) + (m + 1) * m ** n = (m + 1) * t` by rw[LEFT_ADD_DISTRIB, Abbr`t`] >> + `t <= (m + 1) ** n` by metis_tac[ADD_COMM] >> + `(m + 1) * t <= (m + 1) * (m + 1) ** n` by rw[] >> + decide_tac); + +(* Theorem: 1 < n ==> SUC n < 2 ** n *) +(* Proof: + Note 1 + n < (1 + 1) ** n by EXP_LOWER_LT_LOW, m = 1 + or SUC n < SUC 1 ** n by ADD1 + or SUC n < 2 ** n by TWO +*) +val SUC_X_LT_2_EXP_X = store_thm( + "SUC_X_LT_2_EXP_X", + ``!n. 1 < n ==> SUC n < 2 ** n``, + rpt strip_tac >> + `1 + n * 1 < (1 + 1) ** n` by rw[EXP_LOWER_LT_LOW] >> + fs[]); + +(* ------------------------------------------------------------------------- *) +(* DIVIDES Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < m ==> m * (n DIV m) <= n /\ n < m * SUC (n DIV m) *) +(* Proof: + Note n = n DIV m * m + n MOD m /\ + n MOD m < m by DIVISION + Thus m * (n DIV m) <= n by MULT_COMM + and n < m * (n DIV m) + m + = m * (n DIV m + 1) by LEFT_ADD_DISTRIB + = m * SUC (n DIV m) by ADD1 +*) +val DIV_MULT_LESS_EQ = store_thm( + "DIV_MULT_LESS_EQ", + ``!m n. 0 < m ==> m * (n DIV m) <= n /\ n < m * SUC (n DIV m)``, + ntac 3 strip_tac >> + `(n = n DIV m * m + n MOD m) /\ n MOD m < m` by rw[DIVISION] >> + `n < m * (n DIV m) + m` by decide_tac >> + `m * (n DIV m) + m = m * (SUC (n DIV m))` by rw[ADD1] >> + decide_tac); + +(* Theorem: 0 < n ==> (m - n) DIV n = if m < n then 0 else (m DIV n - 1) *) +(* Proof: + If m < n, then m - n = 0, so (m - n) DIV n = 0 by ZERO_DIV + Otherwise, n <= m, and (m - n) DIV n = m DIV n - 1 by SUB_DIV +*) +val SUB_DIV_EQN = store_thm( + "SUB_DIV_EQN", + ``!m n. 0 < n ==> ((m - n) DIV n = if m < n then 0 else (m DIV n - 1))``, + rw[SUB_DIV] >> + `m - n = 0` by decide_tac >> + rw[ZERO_DIV]); + +(* Theorem: 0 < n ==> (m - n) MOD n = if m < n then 0 else m MOD n *) +(* Proof: + If m < n, then m - n = 0, so (m - n) MOD n = 0 by ZERO_MOD + Otherwise, n <= m, and (m - n) MOD n = m MOD n by SUB_MOD +*) +val SUB_MOD_EQN = store_thm( + "SUB_MOD_EQN", + ``!m n. 0 < n ==> ((m - n) MOD n = if m < n then 0 else m MOD n)``, + rw[SUB_MOD]); + +(* Theorem: 0 < m /\ 0 < n /\ (n MOD m = 0) ==> n DIV (SUC m) < n DIV m *) +(* Proof: + Note n = n DIV (SUC m) * (SUC m) + n MOD (SUC m) by DIVISION + = n DIV m * m + n MOD m by DIVISION + = n DIV m * m by n MOD m = 0 + Thus n DIV SUC m * SUC m <= n DIV m * m by arithmetic + Note m < SUC m by LESS_SUC + and n DIV m <> 0, or 0 < n DIV m by DIV_MOD_EQ_0 + Thus n DIV (SUC m) < n DIV m by LE_IMP_REVERSE_LT +*) +val DIV_LT_SUC = store_thm( + "DIV_LT_SUC", + ``!m n. 0 < m /\ 0 < n /\ (n MOD m = 0) ==> n DIV (SUC m) < n DIV m``, + rpt strip_tac >> + `n DIV m * m = n` by metis_tac[DIVISION, ADD_0] >> + `_ = n DIV (SUC m) * (SUC m) + n MOD (SUC m)` by metis_tac[DIVISION, SUC_POS] >> + `n DIV SUC m * SUC m <= n DIV m * m` by decide_tac >> + `m < SUC m` by decide_tac >> + `0 < n DIV m` by metis_tac[DIV_MOD_EQ_0, NOT_ZERO_LT_ZERO] >> + metis_tac[LE_IMP_REVERSE_LT]); + +(* Theorem: 0 < x /\ 0 < y /\ x < y ==> !n. 0 < n /\ (n MOD x = 0) ==> n DIV y < n DIV x *) +(* Proof: + Note x < y ==> SUC x <= y by arithmetic + Thus n DIV y <= n DIV (SUC x) by DIV_LE_MONOTONE_REVERSE + But 0 < x /\ 0 < n /\ (n MOD x = 0) by given + ==> n DIV (SUC x) < n DIV x by DIV_LT_SUC + Hence n DIV y < n DIV x by inequalities +*) +val DIV_LT_MONOTONE_REVERSE = store_thm( + "DIV_LT_MONOTONE_REVERSE", + ``!x y. 0 < x /\ 0 < y /\ x < y ==> !n. 0 < n /\ (n MOD x = 0) ==> n DIV y < n DIV x``, + rpt strip_tac >> + `SUC x <= y` by decide_tac >> + `n DIV y <= n DIV (SUC x)` by rw[DIV_LE_MONOTONE_REVERSE] >> + `n DIV (SUC x) < n DIV x` by rw[DIV_LT_SUC] >> + decide_tac); + +(* Theorem: k <> 0 ==> (m divides n <=> (k * m) divides (k * n)) *) +(* Proof: + m divides n + <=> ?q. n = q * m by divides_def + <=> ?q. k * n = k * (q * m) by EQ_MULT_LCANCEL, k <> 0 + <=> ?q. k * n = q * (k * m) by MULT_ASSOC, MULT_COMM + <=> (k * m) divides (k * n) by divides_def +*) +val DIVIDES_MULTIPLE_IFF = store_thm( + "DIVIDES_MULTIPLE_IFF", + ``!m n k. k <> 0 ==> (m divides n <=> (k * m) divides (k * n))``, + rpt strip_tac >> + `m divides n <=> ?q. n = q * m` by rw[GSYM divides_def] >> + `_ = ?q. (k * n = k * (q * m))` by rw[EQ_MULT_LCANCEL] >> + metis_tac[divides_def, MULT_COMM, MULT_ASSOC]); + +(* Theorem: 0 < n /\ n divides m ==> m = n * (m DIV n) *) +(* Proof: + n divides m <=> m MOD n = 0 by DIVIDES_MOD_0 + m = (m DIV n) * n + (m MOD n) by DIVISION + = (m DIV n) * n by above + = n * (m DIV n) by MULT_COMM +*) +val DIVIDES_FACTORS = store_thm( + "DIVIDES_FACTORS", + ``!m n. 0 < n /\ n divides m ==> (m = n * (m DIV n))``, + metis_tac[DIVISION, DIVIDES_MOD_0, ADD_0, MULT_COMM]); + +(* Theorem: 0 < n /\ n divides m ==> (m DIV n) divides m *) +(* Proof: + By DIVIDES_FACTORS: m = (m DIV n) * n + Hence (m DIV n) | m by divides_def +*) +val DIVIDES_COFACTOR = store_thm( + "DIVIDES_COFACTOR", + ``!m n. 0 < n /\ n divides m ==> (m DIV n) divides m``, + metis_tac[DIVIDES_FACTORS, divides_def]); + +(* Theorem: n divides q ==> p * (q DIV n) = (p * q) DIV n *) +(* Proof: + n divides q ==> q MOD n = 0 by DIVIDES_MOD_0 + p * q = p * ((q DIV n) * n + q MOD n) by DIVISION + = p * ((q DIV n) * n) by ADD_0 + = p * (q DIV n) * n by MULT_ASSOC + = p * (q DIV n) * n + 0 by ADD_0 + Hence (p * q) DIV n = p * (q DIV n) by DIV_UNIQUE, 0 < n: + |- !n k q. (?r. (k = q * n + r) /\ r < n) ==> (k DIV n = q) +*) +val MULTIPLY_DIV = store_thm( + "MULTIPLY_DIV", + ``!n p q. 0 < n /\ n divides q ==> (p * (q DIV n) = (p * q) DIV n)``, + rpt strip_tac >> + `q MOD n = 0` by rw[GSYM DIVIDES_MOD_0] >> + `p * q = p * ((q DIV n) * n)` by metis_tac[DIVISION, ADD_0] >> + `_ = p * (q DIV n) * n + 0` by rw[MULT_ASSOC] >> + metis_tac[DIV_UNIQUE]); + +(* Note: The condition: n divides q is important: +> EVAL ``5 * (10 DIV 3)``; +val it = |- 5 * (10 DIV 3) = 15: thm +> EVAL ``(5 * 10) DIV 3``; +val it = |- 5 * 10 DIV 3 = 16: thm +*) + +(* Theorem: 0 < n /\ m divides n ==> !x. (x MOD n) MOD m = x MOD m *) +(* Proof: + Note 0 < m by ZERO_DIVIDES, 0 < n + Given divides m n ==> ?q. n = q * m by divides_def + Since x = (x DIV n) * n + (x MOD n) by DIVISION + = (x DIV n) * (q * m) + (x MOD n) by above + = ((x DIV n) * q) * m + (x MOD n) by MULT_ASSOC + Hence x MOD m + = ((x DIV n) * q) * m + (x MOD n)) MOD m by above + = (((x DIV n) * q * m) MOD m + (x MOD n) MOD m) MOD m by MOD_PLUS + = (0 + (x MOD n) MOD m) MOD m by MOD_EQ_0 + = (x MOD n) MOD m by ADD, MOD_MOD +*) +val DIVIDES_MOD_MOD = store_thm( + "DIVIDES_MOD_MOD", + ``!m n. 0 < n /\ m divides n ==> !x. (x MOD n) MOD m = x MOD m``, + rpt strip_tac >> + `0 < m` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> + `?q. n = q * m` by rw[GSYM divides_def] >> + `x MOD m = ((x DIV n) * n + (x MOD n)) MOD m` by rw[GSYM DIVISION] >> + `_ = (((x DIV n) * q) * m + (x MOD n)) MOD m` by rw[MULT_ASSOC] >> + `_ = (((x DIV n) * q * m) MOD m + (x MOD n) MOD m) MOD m` by rw[MOD_PLUS] >> + rw[MOD_EQ_0, MOD_MOD]); + +(* Theorem: m divides n <=> (m * k) divides (n * k) *) +(* Proof: by divides_def and EQ_MULT_LCANCEL. *) +val DIVIDES_CANCEL = store_thm( + "DIVIDES_CANCEL", + ``!k. 0 < k ==> !m n. m divides n <=> (m * k) divides (n * k)``, + rw[divides_def] >> + `k <> 0` by decide_tac >> + `!q. (q * m) * k = q * (m * k)` by rw_tac arith_ss[] >> + metis_tac[EQ_MULT_LCANCEL, MULT_COMM]); + +(* Theorem: m divides n ==> (k * m) divides (k * n) *) +(* Proof: + m divides n + ==> ?q. n = q * m by divides_def + So k * n = k * (q * m) + = (k * q) * m by MULT_ASSOC + = (q * k) * m by MULT_COMM + = q * (k * m) by MULT_ASSOC + Hence (k * m) divides (k * n) by divides_def +*) +val DIVIDES_CANCEL_COMM = store_thm( + "DIVIDES_CANCEL_COMM", + ``!m n k. m divides n ==> (k * m) divides (k * n)``, + metis_tac[MULT_ASSOC, MULT_COMM, divides_def]); + +(* Theorem: 0 < n /\ 0 < m ==> !x. n divides x ==> ((m * x) DIV (m * n) = x DIV n) *) +(* Proof: + n divides x ==> x = n * (x DIV n) by DIVIDES_FACTORS + or m * x = (m * n) * (x DIV n) by MULT_ASSOC + n divides x + ==> divides (m * n) (m * x) by DIVIDES_CANCEL_COMM + ==> m * x = (m * n) * ((m * x) DIV (m * n)) by DIVIDES_FACTORS + Equating expressions for m * x, + (m * n) * (x DIV n) = (m * n) * ((m * x) DIV (m * n)) + or x DIV n = (m * x) DIV (m * n) by MULT_LEFT_CANCEL +*) +val DIV_COMMON_FACTOR = store_thm( + "DIV_COMMON_FACTOR", + ``!m n. 0 < n /\ 0 < m ==> !x. n divides x ==> ((m * x) DIV (m * n) = x DIV n)``, + rpt strip_tac >> + `!n. n <> 0 <=> 0 < n` by decide_tac >> + `0 < m * n` by metis_tac[MULT_EQ_0] >> + metis_tac[DIVIDES_CANCEL_COMM, DIVIDES_FACTORS, MULT_ASSOC, MULT_LEFT_CANCEL]); + +(* Theorem: 0 < n /\ 0 < m /\ 0 < m DIV n /\ + n divides m /\ m divides x /\ (m DIV n) divides x ==> + (x DIV (m DIV n) = n * (x DIV m)) *) +(* Proof: + x DIV (m DIV n) + = (n * x) DIV (n * (m DIV n)) by DIV_COMMON_FACTOR, (m DIV n) divides x, 0 < m DIV n. + = (n * x) DIV m by DIVIDES_FACTORS, n divides m, 0 < n. + = n * (x DIV m) by MULTIPLY_DIV, m divides x, 0 < m. +*) +val DIV_DIV_MULT = store_thm( + "DIV_DIV_MULT", + ``!m n x. 0 < n /\ 0 < m /\ 0 < m DIV n /\ + n divides m /\ m divides x /\ (m DIV n) divides x ==> + (x DIV (m DIV n) = n * (x DIV m))``, + metis_tac[DIV_COMMON_FACTOR, DIVIDES_FACTORS, MULTIPLY_DIV]); + +(* ------------------------------------------------------------------------- *) +(* Basic Divisibility *) +(* ------------------------------------------------------------------------- *) + +(* Idea: a little trick to make divisibility to mean equality. *) + +(* Theorem: 0 < n /\ n < 2 * m ==> (m divides n <=> n = m) *) +(* Proof: + If part: 0 < n /\ n < 2 * m /\ m divides n ==> n = m + Note ?k. n = k * m by divides_def + Now k * m < 2 * m by n < 2 * m + so 0 < m /\ k < 2 by LT_MULT_LCANCEL + and 0 < k by MULT + so 1 <= k by LE_MULT_LCANCEL, 0 < m + Thus k = 1, or n = m. + Only-if part: true by DIVIDES_REFL +*) +Theorem divides_iff_equal: + !m n. 0 < n /\ n < 2 * m ==> (m divides n <=> n = m) +Proof + rw[EQ_IMP_THM] >> + `?k. n = k * m` by rw[GSYM divides_def] >> + `0 < m /\ k < 2` by fs[LT_MULT_LCANCEL] >> + `0 < k` by fs[] >> + `k = 1` by decide_tac >> + simp[] +QED + +(* Theorem: 0 < m /\ n divides m ==> !t. m divides (t * n) <=> (m DIV n) divides t *) +(* Proof: + Let k = m DIV n. + Since m <> 0, n divides m ==> n <> 0 by ZERO_DIVIDES + Thus m = k * n by DIVIDES_EQN, 0 < n + so 0 < k by MULT, NOT_ZERO_LT_ZERO + Hence k * n divides t * n <=> k divides t by DIVIDES_CANCEL, 0 < k +*) +val dividend_divides_divisor_multiple = store_thm( + "dividend_divides_divisor_multiple", + ``!m n. 0 < m /\ n divides m ==> !t. m divides (t * n) <=> (m DIV n) divides t``, + rpt strip_tac >> + qabbrev_tac `k = m DIV n` >> + `0 < n` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + `m = k * n` by rw[GSYM DIVIDES_EQN, Abbr`k`] >> + `0 < k` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> + metis_tac[DIVIDES_CANCEL]); + +(* Theorem: 0 < n /\ m divides n ==> 0 < m *) +(* Proof: + Since 0 < n means n <> 0, + then m divides n ==> m <> 0 by ZERO_DIVIDES + or 0 < m by NOT_ZERO_LT_ZERO +*) +(* Theorem: 1 < p ==> !m n. p ** m divides p ** n <=> m <= n *) +(* Proof: + Note p <> 0 /\ p <> 1 by 1 < p + + If-part: p ** m divides p ** n ==> m <= n + By contradiction, suppose n < m. + Let d = m - n, then d <> 0 by n < m + Note p ** m = p ** n * p ** d by EXP_BY_ADD_SUB_LT + and p ** n <> 0 by EXP_EQ_0, p <> 0 + Now ?q. p ** n = q * p ** m by divides_def + = q * p ** d * p ** n by MULT_ASSOC_COMM + Thus 1 * p ** n = q * p ** d * p ** n by MULT_LEFT_1 + or 1 = q * p ** d by MULT_RIGHT_CANCEL + ==> p ** d = 1 by MULT_EQ_1 + or d = 0 by EXP_EQ_1, p <> 1 + This contradicts d <> 0. + + Only-if part: m <= n ==> p ** m divides p ** n + Note p ** n = p ** m * p ** (n - m) by EXP_BY_ADD_SUB_LE + Thus p ** m divides p ** n by divides_def, MULT_COMM +*) +val power_divides_iff = store_thm( + "power_divides_iff", + ``!p. 1 < p ==> !m n. p ** m divides p ** n <=> m <= n``, + rpt strip_tac >> + `p <> 0 /\ p <> 1` by decide_tac >> + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `n < m /\ m - n <> 0` by decide_tac >> + qabbrev_tac `d = m - n` >> + `p ** m = p ** n * p ** d` by rw[EXP_BY_ADD_SUB_LT, Abbr`d`] >> + `p ** n <> 0` by rw[EXP_EQ_0] >> + `?q. p ** n = q * p ** m` by rw[GSYM divides_def] >> + `_ = q * p ** d * p ** n` by metis_tac[MULT_ASSOC_COMM] >> + `1 = q * p ** d` by metis_tac[MULT_RIGHT_CANCEL, MULT_LEFT_1] >> + `p ** d = 1` by metis_tac[MULT_EQ_1] >> + metis_tac[EXP_EQ_1], + `p ** n = p ** m * p ** (n - m)` by rw[EXP_BY_ADD_SUB_LE] >> + metis_tac[divides_def, MULT_COMM] + ]); + +(* Theorem: prime p ==> !m n. p ** m divides p ** n <=> m <= n *) +(* Proof: by power_divides_iff, ONE_LT_PRIME *) +val prime_power_divides_iff = store_thm( + "prime_power_divides_iff", + ``!p. prime p ==> !m n. p ** m divides p ** n <=> m <= n``, + rw[power_divides_iff, ONE_LT_PRIME]); + +(* Theorem: 0 < n /\ 1 < p ==> p divides p ** n *) +(* Proof: + Note 0 < n <=> 1 <= n by arithmetic + so p ** 1 divides p ** n by power_divides_iff + or p divides p ** n by EXP_1 +*) +val divides_self_power = store_thm( + "divides_self_power", + ``!n p. 0 < n /\ 1 < p ==> p divides p ** n``, + metis_tac[power_divides_iff, EXP_1, DECIDE``0 < n <=> 1 <= n``]); + +(* Theorem: a divides b /\ 0 < b /\ b < 2 * a ==> (b = a) *) +(* Proof: + Note ?k. b = k * a by divides_def + and 0 < k by MULT_EQ_0, 0 < b + and k < 2 by LT_MULT_RCANCEL, k * a < 2 * a + Thus k = 1 by 0 < k < 2 + or b = k * a = a by arithmetic +*) +Theorem divides_eq_thm: + !a b. a divides b /\ 0 < b /\ b < 2 * a ==> (b = a) +Proof + rpt strip_tac >> + `?k. b = k * a` by rw[GSYM divides_def] >> + `0 < k` by metis_tac[MULT_EQ_0, NOT_ZERO] >> + `k < 2` by metis_tac[LT_MULT_RCANCEL] >> + `k = 1` by decide_tac >> + simp[] +QED + +(* Idea: factor equals cofactor iff the number is a square of the factor. *) + +(* Theorem: 0 < m /\ m divides n ==> (m = n DIV m <=> n = m ** 2) *) +(* Proof: + n + = n DIV m * m + n MOD m by DIVISION, 0 < m + = n DIV m * m + 0 by DIVIDES_MOD_0, m divides n + = n DIV m * m by ADD_0 + If m = n DIV m, + then n = m * m = m ** 2 by EXP_2 + If n = m ** 2, + then n = m * m by EXP_2 + so m = n DIV m by EQ_MULT_RCANCEL +*) +Theorem factor_eq_cofactor: + !m n. 0 < m /\ m divides n ==> (m = n DIV m <=> n = m ** 2) +Proof + rw[EQ_IMP_THM] >> + `n = n DIV m * m + n MOD m` by rw[DIVISION] >> + `_ = m * m + 0` by metis_tac[DIVIDES_MOD_0] >> + simp[] +QED + +(* Theorem alias *) +Theorem euclid_prime = gcdTheory.P_EUCLIDES; +(* |- !p a b. prime p /\ p divides a * b ==> p divides a \/ p divides b *) + +(* Theorem alias *) +Theorem euclid_coprime = gcdTheory.L_EUCLIDES; +(* |- !a b c. coprime a b /\ b divides a * c ==> b divides c *) + +(* Both MOD_EQ_DIFF and MOD_EQ are required in MOD_MULT_LCANCEL *) + +(* Idea: equality exchange for MOD without negative. *) + +(* Theorem: b < n /\ c < n ==> + ((a + b) MOD n = (c + d) MOD n <=> + (a + (n - c)) MOD n = (d + (n - b)) MOD n) *) +(* Proof: + Note 0 < n by b < n or c < n + Let x = n - b, y = n - c. + The goal is: (a + b) MOD n = (c + d) MOD n <=> + (a + y) MOD n = (d + x) MOD n + Note n = b + x, n = c + y by arithmetic + (a + b) MOD n = (c + d) MOD n + <=> (a + b + x + y) MOD n = (c + d + x + y) MOD n by ADD_MOD + <=> (a + y + n) MOD n = (d + x + n) MOD n by above + <=> (a + y) MOD n = (d + x) MOD n by ADD_MOD + + For integers, this is simply: a + b = c + d <=> a - c = b - d. +*) +Theorem mod_add_eq_sub: + !n a b c d. b < n /\ c < n ==> + ((a + b) MOD n = (c + d) MOD n <=> + (a + (n - c)) MOD n = (d + (n - b)) MOD n) +Proof + rpt strip_tac >> + `0 < n` by decide_tac >> + `n = b + (n - b)` by decide_tac >> + `n = c + (n - c)` by decide_tac >> + qabbrev_tac `x = n - b` >> + qabbrev_tac `y = n - c` >> + `a + b + x + y = a + y + n` by decide_tac >> + `c + d + x + y = d + x + n` by decide_tac >> + `(a + b) MOD n = (c + d) MOD n <=> + (a + b + x + y) MOD n = (c + d + x + y) MOD n` by simp[ADD_MOD] >> + fs[ADD_MOD] +QED + +(* Idea: generalise above equality exchange for MOD. *) + +(* Theorem: 0 < n ==> + ((a + b) MOD n = (c + d) MOD n <=> + (a + (n - c MOD n)) MOD n = (d + (n - b MOD n)) MOD n) *) +(* Proof: + Let b' = b MOD n, c' = c MOD n. + Note b' < n by MOD_LESS, 0 < n + and c' < n by MOD_LESS, 0 < n + (a + b) MOD n = (c + d) MOD n + <=> (a + b') MOD n = (d + c') MOD n by MOD_PLUS2 + <=> (a + (n - c')) MOD n = (d + (n - b')) MOD n by mod_add_eq_sub +*) +Theorem mod_add_eq_sub_eq: + !n a b c d. 0 < n ==> + ((a + b) MOD n = (c + d) MOD n <=> + (a + (n - c MOD n)) MOD n = (d + (n - b MOD n)) MOD n) +Proof + rpt strip_tac >> + `b MOD n < n /\ c MOD n < n` by rw[] >> + `(a + b) MOD n = (a + b MOD n) MOD n` by simp[Once MOD_PLUS2] >> + `(c + d) MOD n = (d + c MOD n) MOD n` by simp[Once MOD_PLUS2] >> + prove_tac[mod_add_eq_sub] +QED + +(* +MOD_EQN is a trick to eliminate MOD: +|- !n. 0 < n ==> !a b. a MOD n = b <=> ?c. a = c * n + b /\ b < n +*) + +(* Idea: remove MOD for divides: need b divides (a MOD n) ==> b divides a. *) + +(* Theorem: 0 < n /\ b divides n /\ b divides (a MOD n) ==> b divides a *) +(* Proof: + Note ?k. n = k * b by divides_def, b divides n + and ?h. a MOD n = h * b by divides_def, b divides (a MOD n) + and ?c. a = c * n + h * b by MOD_EQN, 0 < n + = c * (k * b) + h * b by above + = (c * k + h) * b by RIGHT_ADD_DISTRIB + Thus b divides a by divides_def +*) +Theorem mod_divides: + !n a b. 0 < n /\ b divides n /\ b divides (a MOD n) ==> b divides a +Proof + rpt strip_tac >> + `?k. n = k * b` by rw[GSYM divides_def] >> + `?h. a MOD n = h * b` by rw[GSYM divides_def] >> + `?c. a = c * n + h * b` by metis_tac[MOD_EQN] >> + `_ = (c * k + h) * b` by simp[] >> + metis_tac[divides_def] +QED + +(* Idea: include converse of mod_divides. *) + +(* Theorem: 0 < n /\ b divides n ==> (b divides (a MOD n) <=> b divides a) *) +(* Proof: + If part: b divides n /\ b divides a MOD n ==> b divides a + This is true by mod_divides + Only-if part: b divides n /\ b divides a ==> b divides a MOD n + Note ?c. a = c * n + a MOD n by MOD_EQN, 0 < n + = c * n + 1 * a MOD n by MULT_LEFT_1 + Thus b divides (a MOD n) by divides_linear_sub +*) +Theorem mod_divides_iff: + !n a b. 0 < n /\ b divides n ==> (b divides (a MOD n) <=> b divides a) +Proof + rw[EQ_IMP_THM] >- + metis_tac[mod_divides] >> + `?c. a = c * n + a MOD n` by metis_tac[MOD_EQN] >> + metis_tac[divides_linear_sub, MULT_LEFT_1] +QED + +(* An application of +DIVIDES_MOD_MOD: +|- !m n. 0 < n /\ m divides n ==> !x. x MOD n MOD m = x MOD m +Let x = a linear combination. +(linear) MOD n MOD m = linear MOD m +change n to a product m * n, for z = linear MOD (m * n). +(linear) MOD (m * n) MOD g = linear MOD g +z MOD g = linear MOD g +requires: g divides (m * n) +*) + +(* Idea: generalise for MOD equation: a MOD n = b. Need c divides a ==> c divides b. *) + +(* Theorem: 0 < n /\ a MOD n = b /\ c divides n /\ c divides a ==> c divides b *) +(* Proof: + Note 0 < c by ZERO_DIVIDES, c divides n, 0 < n. + a MOD n = b + ==> (a MOD n) MOD c = b MOD c + ==> a MOD c = b MOD c by DIVIDES_MOD_MOD, 0 < n, c divides n + But a MOD c = 0 by DIVIDES_MOD_0, c divides a + so b MOD c = 0, or c divides b by DIVIDES_MOD_0, 0 < c +*) +Theorem mod_divides_divides: + !n a b c. 0 < n /\ a MOD n = b /\ c divides n /\ c divides a ==> c divides b +Proof + simp[mod_divides_iff] +QED + +(* Idea: include converse of mod_divides_divides. *) + +(* Theorem: 0 < n /\ a MOD n = b /\ c divides n ==> (c divides a <=> c divides b) *) +(* Proof: + If part: c divides a ==> c divides b, true by mod_divides_divides + Only-if part: c divides b ==> c divides a + Note b = a MOD n, so this is true by mod_divides +*) +Theorem mod_divides_divides_iff: + !n a b c. 0 < n /\ a MOD n = b /\ c divides n ==> (c divides a <=> c divides b) +Proof + simp[mod_divides_iff] +QED + +(* Idea: divides across MOD: from a MOD n = b MOD n to c divides a ==> c divides b. *) + +(* Theorem: 0 < n /\ a MOD n = b MOD n /\ c divides n /\ c divides a ==> c divides b *) +(* Proof: + Note c divides (b MOD n) by mod_divides_divides + so c divides b by mod_divides + Or, simply have both by mod_divides_iff +*) +Theorem mod_eq_divides: + !n a b c. 0 < n /\ a MOD n = b MOD n /\ c divides n /\ c divides a ==> c divides b +Proof + metis_tac[mod_divides_iff] +QED + +(* Idea: include converse of mod_eq_divides. *) + +(* Theorem: 0 < n /\ a MOD n = b MOD n /\ c divides n ==> (c divides a <=> c divides b) *) +(* Proof: + If part: c divides a ==> c divides b, true by mod_eq_divides, a MOD n = b MOD n + Only-if: c divides b ==> c divides a, true by mod_eq_divides, b MOD n = a MOD n +*) +Theorem mod_eq_divides_iff: + !n a b c. 0 < n /\ a MOD n = b MOD n /\ c divides n ==> (c divides a <=> c divides b) +Proof + metis_tac[mod_eq_divides] +QED + +(* Idea: special cross-multiply equality of MOD (m * n) implies pair equality: + from (m * a) MOD (m * n) = (n * b) MOD (m * n) to a = n /\ b = m. *) + +(* Theorem: coprime m n /\ 0 < a /\ a < 2 * n /\ 0 < b /\ b < 2 * m /\ + (m * a) MOD (m * n) = (n * b) MOD (m * n) ==> (a = n /\ b = m) *) +(* Proof: + Given (m * a) MOD (m * n) = (n * b) MOD (m * n) + Note n divides (n * b) by factor_divides + and n divides (m * n) by factor_divides + so n divides (m * a) by mod_eq_divides + ==> n divides a by euclid_coprime, MULT_COMM + Thus a = n by divides_iff_equal + Also m divides (m * a) by factor_divides + and m divides (m * n) by factor_divides + so m divides (n * b) by mod_eq_divides + ==> m divides b by euclid_coprime, GCD_SYM + Thus b = m by divides_iff_equal +*) +Theorem mod_mult_eq_mult: + !m n a b. coprime m n /\ 0 < a /\ a < 2 * n /\ 0 < b /\ b < 2 * m /\ + (m * a) MOD (m * n) = (n * b) MOD (m * n) ==> (a = n /\ b = m) +Proof + ntac 5 strip_tac >> + `0 < m /\ 0 < n` by decide_tac >> + `0 < m * n` by rw[] >> + strip_tac >| [ + `n divides (n * b)` by rw[DIVIDES_MULTIPLE] >> + `n divides (m * n)` by rw[DIVIDES_MULTIPLE] >> + `n divides (m * a)` by metis_tac[mod_eq_divides] >> + `n divides a` by metis_tac[euclid_coprime, MULT_COMM] >> + metis_tac[divides_iff_equal], + `m divides (m * a)` by rw[DIVIDES_MULTIPLE] >> + `m divides (m * n)` by metis_tac[DIVIDES_REFL, DIVIDES_MULTIPLE, MULT_COMM] >> + `m divides (n * b)` by metis_tac[mod_eq_divides] >> + `m divides b` by metis_tac[euclid_coprime, GCD_SYM] >> + metis_tac[divides_iff_equal] + ] +QED + +(* ------------------------------------------------------------------------- *) +(* Even and Odd Parity. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < n /\ EVEN m ==> EVEN (m ** n) *) +(* Proof: + Since EVEN m, m MOD 2 = 0 by EVEN_MOD2 + EVEN (m ** n) + <=> (m ** n) MOD 2 = 0 by EVEN_MOD2 + <=> (m MOD 2) ** n MOD 2 = 0 by EXP_MOD, 0 < 2 + ==> 0 ** n MOD 2 = 0 by above + <=> 0 MOD 2 = 0 by ZERO_EXP, n <> 0 + <=> 0 = 0 by ZERO_MOD + <=> T +*) +(* Note: arithmeticTheory.EVEN_EXP |- !m n. 0 < n /\ EVEN m ==> EVEN (m ** n) *) + +(* Theorem: !m n. 0 < n /\ ODD m ==> ODD (m ** n) *) +(* Proof: + Since ODD m, m MOD 2 = 1 by ODD_MOD2 + ODD (m ** n) + <=> (m ** n) MOD 2 = 1 by ODD_MOD2 + <=> (m MOD 2) ** n MOD 2 = 1 by EXP_MOD, 0 < 2 + ==> 1 ** n MOD 2 = 1 by above + <=> 1 MOD 2 = 1 by EXP_1, n <> 0 + <=> 1 = 1 by ONE_MOD, 1 < 2 + <=> T +*) +val ODD_EXP = store_thm( + "ODD_EXP", + ``!m n. 0 < n /\ ODD m ==> ODD (m ** n)``, + rw[ODD_MOD2] >> + `n <> 0 /\ 0 < 2` by decide_tac >> + metis_tac[EXP_MOD, EXP_1, ONE_MOD]); + +(* Theorem: 0 < n ==> !m. (EVEN m <=> EVEN (m ** n)) /\ (ODD m <=> ODD (m ** n)) *) +(* Proof: + First goal: EVEN m <=> EVEN (m ** n) + If part: EVEN m ==> EVEN (m ** n), true by EVEN_EXP + Only-if part: EVEN (m ** n) ==> EVEN m. + By contradiction, suppose ~EVEN m. + Then ODD m by EVEN_ODD + and ODD (m ** n) by ODD_EXP + or ~EVEN (m ** n) by EVEN_ODD + This contradicts EVEN (m ** n). + Second goal: ODD m <=> ODD (m ** n) + Just mirror the reasoning of first goal. +*) +val power_parity = store_thm( + "power_parity", + ``!n. 0 < n ==> !m. (EVEN m <=> EVEN (m ** n)) /\ (ODD m <=> ODD (m ** n))``, + metis_tac[EVEN_EXP, ODD_EXP, ODD_EVEN]); + +(* Theorem: 0 < n ==> EVEN (2 ** n) *) +(* Proof: + EVEN (2 ** n) + <=> (2 ** n) MOD 2 = 0 by EVEN_MOD2 + <=> (2 MOD 2) ** n MOD 2 = 0 by EXP_MOD + <=> 0 ** n MOD 2 = 0 by DIVMOD_ID, 0 < 2 + <=> 0 MOD 2 = 0 by ZERO_EXP, n <> 0 + <=> 0 = 0 by ZERO_MOD + <=> T +*) +Theorem EXP_2_EVEN: !n. 0 < n ==> EVEN (2 ** n) +Proof rw[EVEN_MOD2, ZERO_EXP] +QED +(* Michael's proof by induction +val EXP_2_EVEN = store_thm( + "EXP_2_EVEN", + ``!n. 0 < n ==> EVEN (2 ** n)``, + Induct >> rw[EXP, EVEN_DOUBLE]); + *) + +(* Theorem: 0 < n ==> ODD (2 ** n - 1) *) +(* Proof: + Since 0 < 2 ** n by EXP_POS, 0 < 2 + so 1 <= 2 ** n by LESS_EQ + thus SUC (2 ** n - 1) + = 2 ** n - 1 + 1 by ADD1 + = 2 ** n by SUB_ADD, 1 <= 2 ** n + and EVEN (2 ** n) by EXP_2_EVEN + Hence ODD (2 ** n - 1) by EVEN_ODD_SUC +*) +val EXP_2_PRE_ODD = store_thm( + "EXP_2_PRE_ODD", + ``!n. 0 < n ==> ODD (2 ** n - 1)``, + rpt strip_tac >> + `0 < 2 ** n` by rw[EXP_POS] >> + `SUC (2 ** n - 1) = 2 ** n` by decide_tac >> + metis_tac[EXP_2_EVEN, EVEN_ODD_SUC]); + +(* ------------------------------------------------------------------------- *) +(* Modulo Inverse *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: [Cancellation Law for MOD p] + For prime p, if x MOD p <> 0, + (x*y) MOD p = (x*z) MOD p ==> y MOD p = z MOD p *) +(* Proof: + (x*y) MOD p = (x*z) MOD p + ==> ((x*y) - (x*z)) MOD p = 0 by MOD_EQ_DIFF + ==> (x*(y-z)) MOD p = 0 by arithmetic LEFT_SUB_DISTRIB + ==> (y-z) MOD p = 0 by EUCLID_LEMMA, x MOD p <> 0 + ==> y MOD p = z MOD p if z <= y + + Since this theorem is symmetric in y, z, + First prove the theorem assuming z <= y, + then use the same proof for y <= z. +*) +Theorem MOD_MULT_LCANCEL: + !p x y z. prime p /\ (x * y) MOD p = (x * z) MOD p /\ x MOD p <> 0 ==> y MOD p = z MOD p +Proof + rpt strip_tac >> + `!a b c. c <= b /\ (a * b) MOD p = (a * c) MOD p /\ a MOD p <> 0 ==> b MOD p = c MOD p` by + (rpt strip_tac >> + `0 < p` by rw[PRIME_POS] >> + `(a * b - a * c) MOD p = 0` by rw[MOD_EQ_DIFF] >> + `(a * (b - c)) MOD p = 0` by rw[LEFT_SUB_DISTRIB] >> + metis_tac[EUCLID_LEMMA, MOD_EQ]) >> + Cases_on `z <= y` >- + metis_tac[] >> + `y <= z` by decide_tac >> + metis_tac[] +QED + +(* Theorem: prime p /\ (y * x) MOD p = (z * x) MOD p /\ x MOD p <> 0 ==> + y MOD p = z MOD p *) +(* Proof: by MOD_MULT_LCANCEL, MULT_COMM *) +Theorem MOD_MULT_RCANCEL: + !p x y z. prime p /\ (y * x) MOD p = (z * x) MOD p /\ x MOD p <> 0 ==> + y MOD p = z MOD p +Proof + metis_tac[MOD_MULT_LCANCEL, MULT_COMM] +QED + +(* Theorem: For prime p, 0 < x < p ==> ?y. 0 < y /\ y < p /\ (y*x) MOD p = 1 *) +(* Proof: + 0 < x < p + ==> ~ divides p x by NOT_LT_DIVIDES + ==> gcd p x = 1 by gcdTheory.PRIME_GCD + ==> ?k q. k * x = q * p + 1 by gcdTheory.LINEAR_GCD + ==> k*x MOD p = (q*p + 1) MOD p by arithmetic + ==> k*x MOD p = 1 by MOD_MULT, 1 < p. + ==> (k MOD p)*(x MOD p) MOD p = 1 by MOD_TIMES2 + ==> ((k MOD p) * x) MOD p = 1 by LESS_MOD, x < p. + Now k MOD p < p by MOD_LESS + and 0 < k MOD p since (k*x) MOD p <> 0 (by 1 <> 0) + and x MOD p <> 0 (by ~ divides p x) + by EUCLID_LEMMA + Hence take y = k MOD p, then 0 < y < p. +*) +val MOD_MULT_INV_EXISTS = store_thm( + "MOD_MULT_INV_EXISTS", + ``!p x. prime p /\ 0 < x /\ x < p ==> ?y. 0 < y /\ y < p /\ ((y * x) MOD p = 1)``, + rpt strip_tac >> + `0 < p /\ 1 < p` by metis_tac[PRIME_POS, ONE_LT_PRIME] >> + `gcd p x = 1` by metis_tac[PRIME_GCD, NOT_LT_DIVIDES] >> + `?k q. k * x = q * p + 1` by metis_tac[LINEAR_GCD, NOT_ZERO_LT_ZERO] >> + `1 = (k * x) MOD p` by metis_tac[MOD_MULT] >> + `_ = ((k MOD p) * (x MOD p)) MOD p` by metis_tac[MOD_TIMES2] >> + `0 < k MOD p` by + (`1 <> 0` by decide_tac >> + `x MOD p <> 0` by metis_tac[DIVIDES_MOD_0, NOT_LT_DIVIDES] >> + `k MOD p <> 0` by metis_tac[EUCLID_LEMMA, MOD_MOD] >> + decide_tac) >> + metis_tac[MOD_LESS, LESS_MOD]); + +(* Convert this theorem into MUL_INV_DEF *) + +(* Step 1: move ?y forward by collecting quantifiers *) +val lemma = prove( + ``!p x. ?y. prime p /\ 0 < x /\ x < p ==> 0 < y /\ y < p /\ ((y * x) MOD p = 1)``, + metis_tac[MOD_MULT_INV_EXISTS]); + +(* Step 2: apply SKOLEM_THM *) +(* +- SKOLEM_THM; +> val it = |- !P. (!x. ?y. P x y) <=> ?f. !x. P x (f x) : thm +*) +val MOD_MULT_INV_DEF = new_specification( + "MOD_MULT_INV_DEF", + ["MOD_MULT_INV"], (* avoid MOD_MULT_INV_EXISTS: thm *) + SIMP_RULE (srw_ss()) [SKOLEM_THM] lemma); +(* +> val MOD_MULT_INV_DEF = + |- !p x. + prime p /\ 0 < x /\ x < p ==> + 0 < MOD_MULT_INV p x /\ MOD_MULT_INV p x < p /\ + ((MOD_MULT_INV p x * x) MOD p = 1) : thm +*) + +(* ------------------------------------------------------------------------- *) +(* FACTOR Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: ~ prime n ==> n has a proper prime factor p *) +(* Proof: apply PRIME_FACTOR: + !n. n <> 1 ==> ?p. prime p /\ p divides n : thm +*) +val PRIME_FACTOR_PROPER = store_thm( + "PRIME_FACTOR_PROPER", + ``!n. 1 < n /\ ~prime n ==> ?p. prime p /\ p < n /\ (p divides n)``, + rpt strip_tac >> + `0 < n /\ n <> 1` by decide_tac >> + `?p. prime p /\ p divides n` by metis_tac[PRIME_FACTOR] >> + `~(n < p)` by metis_tac[NOT_LT_DIVIDES] >> + Cases_on `n = p` >- + full_simp_tac std_ss[] >> + `p < n` by decide_tac >> + metis_tac[]); + +(* Theorem: If p divides n, then there is a (p ** m) that maximally divides n. *) +(* Proof: + Consider the set s = {k | p ** k divides n} + Since p IN s, s <> {} by MEMBER_NOT_EMPTY + For k IN s, p ** k n divides ==> p ** k <= n by DIVIDES_LE + Since ?z. n <= p ** z by EXP_ALWAYS_BIG_ENOUGH + p ** k <= p ** z + k <= z by EXP_BASE_LE_MONO + or k < SUC z + Hence s SUBSET count (SUC z) by SUBSET_DEF + and FINITE s by FINITE_COUNT, SUBSET_FINITE + Let m = MAX_SET s + Then p ** m n divides by MAX_SET_DEF + Let q = n DIV (p ** m) + i.e. n = q * (p ** m) + If p divides q, then q = t * p + or n = t * p * (p ** m) + = t * (p * p ** m) by MULT_ASSOC + = t * p ** SUC m by EXP + i.e. p ** SUC m divides n, or SUC m IN s. + Since m < SUC m by LESS_SUC + This contradicts the maximal property of m. +*) +val FACTOR_OUT_POWER = store_thm( + "FACTOR_OUT_POWER", + ``!n p. 0 < n /\ 1 < p /\ p divides n ==> ?m. (p ** m) divides n /\ ~(p divides (n DIV (p ** m)))``, + rpt strip_tac >> + `p <= n` by rw[DIVIDES_LE] >> + `1 < n` by decide_tac >> + qabbrev_tac `s = {k | (p ** k) divides n }` >> + qexists_tac `MAX_SET s` >> + qabbrev_tac `m = MAX_SET s` >> + `!k. k IN s <=> (p ** k) divides n` by rw[Abbr`s`] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY, EXP_1] >> + `?z. n <= p ** z` by rw[EXP_ALWAYS_BIG_ENOUGH] >> + `!k. k IN s ==> k <= z` by metis_tac[DIVIDES_LE, EXP_BASE_LE_MONO, LESS_EQ_TRANS] >> + `!k. k <= z ==> k < SUC z` by decide_tac >> + `s SUBSET (count (SUC z))` by metis_tac[IN_COUNT, SUBSET_DEF, LESS_EQ_TRANS] >> + `FINITE s` by metis_tac[FINITE_COUNT, SUBSET_FINITE] >> + `m IN s /\ !y. y IN s ==> y <= m` by metis_tac[MAX_SET_DEF] >> + `(p ** m) divides n` by metis_tac[] >> + rw[] >> + spose_not_then strip_assume_tac >> + `0 < p` by decide_tac >> + `0 < p ** m` by rw[EXP_POS] >> + `n = (p ** m) * (n DIV (p ** m))` by rw[DIVIDES_FACTORS] >> + `?q. n DIV (p ** m) = q * p` by rw[GSYM divides_def] >> + `n = q * p ** SUC m` by metis_tac[MULT_COMM, MULT_ASSOC, EXP] >> + `SUC m <= m` by metis_tac[divides_def] >> + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Useful Theorems. *) +(* ------------------------------------------------------------------------- *) + +(* binomial_add: same as SUM_SQUARED *) + +(* Theorem: (a + b) ** 2 = a ** 2 + b ** 2 + 2 * a * b *) +(* Proof: + (a + b) ** 2 + = (a + b) * (a + b) by EXP_2 + = a * (a + b) + b * (a + b) by RIGHT_ADD_DISTRIB + = (a * a + a * b) + (b * a + b * b) by LEFT_ADD_DISTRIB + = a * a + b * b + 2 * a * b by arithmetic + = a ** 2 + b ** 2 + 2 * a * b by EXP_2 +*) +Theorem binomial_add: + !a b. (a + b) ** 2 = a ** 2 + b ** 2 + 2 * a * b +Proof + rpt strip_tac >> + `(a + b) ** 2 = (a + b) * (a + b)` by simp[] >> + `_ = a * a + b * b + 2 * a * b` by decide_tac >> + simp[] +QED + +(* Theorem: b <= a ==> ((a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b) *) +(* Proof: + If b = 0, + RHS = a ** 2 + 0 ** 2 - 2 * a * 0 + = a ** 2 + 0 - 0 + = a ** 2 + = (a - 0) ** 2 + = LHS + If b <> 0, + Then b * b <= a * b by LE_MULT_RCANCEL, b <> 0 + and b * b <= 2 * a * b + + LHS = (a - b) ** 2 + = (a - b) * (a - b) by EXP_2 + = a * (a - b) - b * (a - b) by RIGHT_SUB_DISTRIB + = (a * a - a * b) - (b * a - b * b) by LEFT_SUB_DISTRIB + = a * a - (a * b + (a * b - b * b)) by SUB_PLUS + = a * a - (a * b + a * b - b * b) by LESS_EQ_ADD_SUB, b * b <= a * b + = a * a - (2 * a * b - b * b) + = a * a + b * b - 2 * a * b by SUB_SUB, b * b <= 2 * a * b + = a ** 2 + b ** 2 - 2 * a * b by EXP_2 + = RHS +*) +Theorem binomial_sub: + !a b. b <= a ==> ((a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b) +Proof + rpt strip_tac >> + Cases_on `b = 0` >- + simp[] >> + `b * b <= a * b` by rw[] >> + `b * b <= 2 * a * b` by decide_tac >> + `(a - b) ** 2 = (a - b) * (a - b)` by simp[] >> + `_ = a * a + b * b - 2 * a * b` by decide_tac >> + rw[] +QED + +(* Theorem: 2 * a * b <= a ** 2 + b ** 2 *) +(* Proof: + If a = b, + LHS = 2 * a * a + = a * a + a * a + = a ** 2 + a ** 2 by EXP_2 + = RHS + If a < b, then 0 < b - a. + Thus 0 < (b - a) * (b - a) by MULT_EQ_0 + or 0 < (b - a) ** 2 by EXP_2 + so 0 < b ** 2 + a ** 2 - 2 * b * a by binomial_sub, a <= b + ==> 2 * a * b < a ** 2 + b ** 2 due to 0 < RHS. + If b < a, then 0 < a - b. + Thus 0 < (a - b) * (a - b) by MULT_EQ_0 + or 0 < (a - b) ** 2 by EXP_2 + so 0 < a ** 2 + b ** 2 - 2 * a * b by binomial_sub, b <= a + ==> 2 * a * b < a ** 2 + b ** 2 due to 0 < RHS. +*) +Theorem binomial_means: + !a b. 2 * a * b <= a ** 2 + b ** 2 +Proof + rpt strip_tac >> + Cases_on `a = b` >- + simp[] >> + Cases_on `a < b` >| [ + `b - a <> 0` by decide_tac >> + `(b - a) * (b - a) <> 0` by metis_tac[MULT_EQ_0] >> + `(b - a) * (b - a) = (b - a) ** 2` by simp[] >> + `_ = b ** 2 + a ** 2 - 2 * b * a` by rw[binomial_sub] >> + decide_tac, + `a - b <> 0` by decide_tac >> + `(a - b) * (a - b) <> 0` by metis_tac[MULT_EQ_0] >> + `(a - b) * (a - b) = (a - b) ** 2` by simp[] >> + `_ = a ** 2 + b ** 2 - 2 * a * b` by rw[binomial_sub] >> + decide_tac + ] +QED + +(* Theorem: b <= a ==> (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 *) +(* Proof: + Note (a - b) ** 2 = a ** 2 + b ** 2 - 2 * a * b by binomial_sub + and 2 * a * b <= a ** 2 + b ** 2 by binomial_means + Thus (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 +*) +Theorem binomial_sub_sum: + !a b. b <= a ==> (a - b) ** 2 + 2 * a * b = a ** 2 + b ** 2 +Proof + rpt strip_tac >> + imp_res_tac binomial_sub >> + assume_tac (binomial_means |> SPEC_ALL) >> + decide_tac +QED + +(* Theorem: b <= a ==> ((a - b) ** 2 + 4 * a * b = (a + b) ** 2) *) +(* Proof: + Note: 2 * a * b <= a ** 2 + b ** 2 by binomial_means, as [1] + (a - b) ** 2 + 4 * a * b + = a ** 2 + b ** 2 - 2 * a * b + 4 * a * b by binomial_sub, b <= a + = a ** 2 + b ** 2 + 4 * a * b - 2 * a * b by SUB_ADD, [1] + = a ** 2 + b ** 2 + 2 * a * b + = (a + b) ** 2 by binomial_add +*) +Theorem binomial_sub_add: + !a b. b <= a ==> ((a - b) ** 2 + 4 * a * b = (a + b) ** 2) +Proof + rpt strip_tac >> + `2 * a * b <= a ** 2 + b ** 2` by rw[binomial_means] >> + `(a - b) ** 2 + 4 * a * b = a ** 2 + b ** 2 - 2 * a * b + 4 * a * b` by rw[binomial_sub] >> + `_ = a ** 2 + b ** 2 + 4 * a * b - 2 * a * b` by decide_tac >> + `_ = a ** 2 + b ** 2 + 2 * a * b` by decide_tac >> + `_ = (a + b) ** 2` by rw[binomial_add] >> + decide_tac +QED + +(* Theorem: a ** 2 - b ** 2 = (a - b) * (a + b) *) +(* Proof: + a ** 2 - b ** 2 + = a * a - b * b by EXP_2 + = a * a + a * b - a * b - b * b by ADD_SUB + = a * a + a * b - (b * a + b * b) by SUB_PLUS + = a * (a + b) - b * (a + b) by LEFT_ADD_DISTRIB + = (a - b) * (a + b) by RIGHT_SUB_DISTRIB +*) +Theorem difference_of_squares: + !a b. a ** 2 - b ** 2 = (a - b) * (a + b) +Proof + rpt strip_tac >> + `a ** 2 - b ** 2 = a * a - b * b` by simp[] >> + `_ = a * a + a * b - a * b - b * b` by decide_tac >> + decide_tac +QED + +(* Theorem: a * a - b * b = (a - b) * (a + b) *) +(* Proof: + a * a - b * b + = a ** 2 - b ** 2 by EXP_2 + = (a + b) * (a - b) by difference_of_squares +*) +Theorem difference_of_squares_alt: + !a b. a * a - b * b = (a - b) * (a + b) +Proof + rw[difference_of_squares] +QED + +(* binomial_2: same as binomial_add, or SUM_SQUARED *) + +(* Theorem: (m + n) ** 2 = m ** 2 + n ** 2 + 2 * m * n *) +(* Proof: + (m + n) ** 2 + = (m + n) * (m + n) by EXP_2 + = m * m + n * m + m * n + n * n by LEFT_ADD_DISTRIB, RIGHT_ADD_DISTRIB + = m ** 2 + n ** 2 + 2 * m * n by EXP_2 +*) +val binomial_2 = store_thm( + "binomial_2", + ``!m n. (m + n) ** 2 = m ** 2 + n ** 2 + 2 * m * n``, + rpt strip_tac >> + `(m + n) ** 2 = (m + n) * (m + n)` by rw[] >> + `_ = m * m + n * m + m * n + n * n` by decide_tac >> + `_ = m ** 2 + n ** 2 + 2 * m * n` by rw[] >> + decide_tac); + +(* Obtain a corollary *) +val SUC_SQ = save_thm("SUC_SQ", + binomial_2 |> SPEC ``1`` |> SIMP_RULE (srw_ss()) [GSYM SUC_ONE_ADD]); +(* val SUC_SQ = |- !n. SUC n ** 2 = SUC (n ** 2) + TWICE n: thm *) + +(* Theorem: m <= n ==> SQ m <= SQ n *) +(* Proof: + Since m * m <= n * n by LE_MONO_MULT2 + so SQ m <= SQ n by notation +*) +val SQ_LE = store_thm( + "SQ_LE", + ``!m n. m <= n ==> SQ m <= SQ n``, + rw[]); + +(* Theorem: EVEN n /\ prime n <=> n = 2 *) +(* Proof: + If part: EVEN n /\ prime n ==> n = 2 + EVEN n ==> n MOD 2 = 0 by EVEN_MOD2 + ==> 2 divides n by DIVIDES_MOD_0, 0 < 2 + ==> n = 2 by prime_def, 2 <> 1 + Only-if part: n = 2 ==> EVEN n /\ prime n + Note EVEN 2 by EVEN_2 + and prime 2 by prime_2 +*) +(* Proof: + EVEN n ==> n MOD 2 = 0 by EVEN_MOD2 + ==> 2 divides n by DIVIDES_MOD_0, 0 < 2 + ==> n = 2 by prime_def, 2 <> 1 +*) +Theorem EVEN_PRIME: + !n. EVEN n /\ prime n <=> n = 2 +Proof + rw[EQ_IMP_THM] >> + `0 < 2 /\ 2 <> 1` by decide_tac >> + `2 divides n` by rw[DIVIDES_MOD_0, GSYM EVEN_MOD2] >> + metis_tac[prime_def] +QED + +(* Theorem: prime n /\ n <> 2 ==> ODD n *) +(* Proof: + By contradiction, suppose ~ODD n. + Then EVEN n by EVEN_ODD + but EVEN n /\ prime n ==> n = 2 by EVEN_PRIME + This contradicts n <> 2. +*) +val ODD_PRIME = store_thm( + "ODD_PRIME", + ``!n. prime n /\ n <> 2 ==> ODD n``, + metis_tac[EVEN_PRIME, EVEN_ODD]); + +(* Theorem: prime p ==> 2 <= p *) +(* Proof: by ONE_LT_PRIME *) +val TWO_LE_PRIME = store_thm( + "TWO_LE_PRIME", + ``!p. prime p ==> 2 <= p``, + metis_tac[ONE_LT_PRIME, DECIDE``1 < n <=> 2 <= n``]); + +(* Theorem: ~prime 4 *) +(* Proof: + Note 4 = 2 * 2 by arithmetic + so 2 divides 4 by divides_def + thus ~prime 4 by primes_def +*) +Theorem NOT_PRIME_4: + ~prime 4 +Proof + rpt strip_tac >> + `4 = 2 * 2` by decide_tac >> + `4 <> 2 /\ 4 <> 1 /\ 2 <> 1` by decide_tac >> + metis_tac[prime_def, divides_def] +QED + +(* Theorem: prime n /\ prime m ==> (n divides m <=> (n = m)) *) +(* Proof: + If part: prime n /\ prime m /\ n divides m ==> (n = m) + Note prime n + ==> n <> 1 by NOT_PRIME_1 + With n divides m by given + and prime m by given + Thus n = m by prime_def + Only-if part; prime n /\ prime m /\ (n = m) ==> n divides m + True as m divides m by DIVIDES_REFL +*) +val prime_divides_prime = store_thm( + "prime_divides_prime", + ``!n m. prime n /\ prime m ==> (n divides m <=> (n = m))``, + rw[EQ_IMP_THM] >> + `n <> 1` by metis_tac[NOT_PRIME_1] >> + metis_tac[prime_def]); +(* This is: dividesTheory.prime_divides_only_self; +|- !m n. prime m /\ prime n /\ m divides n ==> (m = n) +*) + +(* Theorem: 0 < m /\ 1 < n /\ (!p. prime p /\ p divides m ==> (p MOD n = 1)) ==> (m MOD n = 1) *) +(* Proof: + By complete induction on m. + If m = 1, trivially true by ONE_MOD + If m <> 1, + Then ?p. prime p /\ p divides m by PRIME_FACTOR, m <> 1 + and ?q. m = q * p by divides_def + and q divides m by divides_def, MULT_COMM + In order to apply induction hypothesis, + Show: q < m + Note q <= m by DIVIDES_LE, 0 < m + But p <> 1 by NOT_PRIME_1 + Thus q <> m by MULT_RIGHT_1, EQ_MULT_LCANCEL, m <> 0 + ==> q < m + Show: 0 < q + Since m = q * p and m <> 0 by above + Thus q <> 0, or 0 < q by MULT + Show: !p. prime p /\ p divides q ==> (p MOD n = 1) + If p divides q, and q divides m, + Then p divides m by DIVIDES_TRANS + ==> p MOD n = 1 by implication + + Hence q MOD n = 1 by induction hypothesis + and p MOD n = 1 by implication + Now 0 < n by 1 < n + m MDO n + = (q * p) MOD n by m = q * p + = (q MOD n * p MOD n) MOD n by MOD_TIMES2, 0 < n + = (1 * 1) MOD n by above + = 1 by MULT_RIGHT_1, ONE_MOD +*) +val ALL_PRIME_FACTORS_MOD_EQ_1 = store_thm( + "ALL_PRIME_FACTORS_MOD_EQ_1", + ``!m n. 0 < m /\ 1 < n /\ (!p. prime p /\ p divides m ==> (p MOD n = 1)) ==> (m MOD n = 1)``, + completeInduct_on `m` >> + rpt strip_tac >> + Cases_on `m = 1` >- + rw[] >> + `?p. prime p /\ p divides m` by rw[PRIME_FACTOR] >> + `?q. m = q * p` by rw[GSYM divides_def] >> + `q divides m` by metis_tac[divides_def, MULT_COMM] >> + `p <> 1` by metis_tac[NOT_PRIME_1] >> + `m <> 0` by decide_tac >> + `q <> m` by metis_tac[MULT_RIGHT_1, EQ_MULT_LCANCEL] >> + `q <= m` by metis_tac[DIVIDES_LE] >> + `q < m` by decide_tac >> + `q <> 0` by metis_tac[MULT] >> + `0 < q` by decide_tac >> + `!p. prime p /\ p divides q ==> (p MOD n = 1)` by metis_tac[DIVIDES_TRANS] >> + `q MOD n = 1` by rw[] >> + `p MOD n = 1` by rw[] >> + `0 < n` by decide_tac >> + metis_tac[MOD_TIMES2, MULT_RIGHT_1, ONE_MOD]); + +(* Theorem: prime p /\ 0 < n ==> !b. p divides (b ** n) <=> p divides b *) +(* Proof: + If part: p divides b ** n ==> p divides b + By induction on n. + Base: 0 < 0 ==> p divides b ** 0 ==> p divides b + True by 0 < 0 = F. + Step: 0 < n ==> p divides b ** n ==> p divides b ==> + 0 < SUC n ==> p divides b ** SUC n ==> p divides b + If n = 0, + b ** SUC 0 + = b ** 1 by ONE + = b by EXP_1 + so p divides b. + If n <> 0, 0 < n. + b ** SUC n + = b * b ** n by EXP + Thus p divides b, + or p divides (b ** n) by P_EUCLIDES + For the latter case, + p divides b by induction hypothesis, 0 < n + + Only-if part: p divides b ==> p divides b ** n + Since n <> 0, ?m. n = SUC m by num_CASES + and b ** n + = b ** SUC m + = b * b ** m by EXP + Thus p divides b ** n by DIVIDES_MULTIPLE, MULT_COMM +*) +val prime_divides_power = store_thm( + "prime_divides_power", + ``!p n. prime p /\ 0 < n ==> !b. p divides (b ** n) <=> p divides b``, + rw[EQ_IMP_THM] >| [ + Induct_on `n` >- + rw[] >> + rpt strip_tac >> + Cases_on `n = 0` >- + metis_tac[ONE, EXP_1] >> + `0 < n` by decide_tac >> + `b ** SUC n = b * b ** n` by rw[EXP] >> + metis_tac[P_EUCLIDES], + `n <> 0` by decide_tac >> + `?m. n = SUC m` by metis_tac[num_CASES] >> + `b ** SUC m = b * b ** m` by rw[EXP] >> + metis_tac[DIVIDES_MULTIPLE, MULT_COMM] + ]); + +(* Theorem: prime p ==> !n. 0 < n ==> p divides p ** n *) +(* Proof: + Since p divides p by DIVIDES_REFL + so p divides p ** n by prime_divides_power, 0 < n +*) +val prime_divides_self_power = store_thm( + "prime_divides_self_power", + ``!p. prime p ==> !n. 0 < n ==> p divides p ** n``, + rw[prime_divides_power, DIVIDES_REFL]); + +(* Theorem: prime p ==> !b n m. 0 < m /\ (b ** n = p ** m) ==> ?k. (b = p ** k) /\ (k * n = m) *) +(* Proof: + Note 1 < p by ONE_LT_PRIME + so p <> 0, 0 < p, p <> 1 by arithmetic + also m <> 0 by 0 < m + Thus p ** m <> 0 by EXP_EQ_0, p <> 0 + and p ** m <> 1 by EXP_EQ_1, p <> 1, m <> 0 + ==> n <> 0, 0 < n by EXP, b ** n = p ** m <> 1 + also b <> 0, 0 < b by EXP_EQ_0, b ** n = p ** m <> 0, 0 < n + + Step 1: show p divides b. + Note p divides (p ** m) by prime_divides_self_power, 0 < m + so p divides (b ** n) by given, b ** n = p ** m + or p divides b by prime_divides_power, 0 < b + + Step 2: express b = q * p ** u where ~(p divides q). + Note 1 < p /\ 0 < b /\ p divides b + ==> ?u. p ** u divides b /\ ~(p divides b DIV p ** u) by FACTOR_OUT_POWER + Let q = b DIV p ** u, v = u * n. + Since p ** u <> 0 by EXP_EQ_0, p <> 0 + so b = q * p ** u by DIVIDES_EQN, 0 < p ** u + p ** m + = (q * p ** u) ** n by b = q * p ** u + = q ** n * (p ** u) ** n by EXP_BASE_MULT + = q ** n * p ** (u * n) by EXP_EXP_MULT + = q ** n * p ** v by v = u * n + + Step 3: split cases + If v = m, + Then q ** n * p ** m = 1 * p ** m by above + or q ** n = 1 by EQ_MULT_RCANCEL, p ** m <> 0 + giving q = 1 by EXP_EQ_1, 0 < n + Thus b = p ** u by b = q * p ** u + Take k = u, the result follows. + + If v < m, + Let d = m - v. + Then 0 < d /\ (m = d + v) by arithmetic + and p ** m = p ** d * p ** v by EXP_ADD + Note p ** v <> 0 by EXP_EQ_0, p <> 0 + q ** n * p ** v = p ** d * p ** v + ==> q ** n = p ** d by EQ_MULT_RCANCEL, p ** v <> 0 + Now p divides p ** d by prime_divides_self_power, 0 < d + so p divides q ** n by above, q ** n = p ** d + ==> p divides q by prime_divides_power, 0 < n + This contradicts ~(p divides q) + + If m < v, + Let d = v - m. + Then 0 < d /\ (v = d + m) by arithmetic + and q ** n * p ** v + = q ** n * (p ** d * p ** m) by EXP_ADD + = q ** n * p ** d * p ** m by MULT_ASSOC + = 1 * p ** m by arithmetic, b ** n = p ** m + Hence q ** n * p ** d = 1 by EQ_MULT_RCANCEL, p ** m <> 0 + ==> (q ** n = 1) /\ (p ** d = 1) by MULT_EQ_1 + But p ** d <> 1 by EXP_EQ_1, 0 < d + This contradicts p ** d = 1. +*) +Theorem power_eq_prime_power: + !p. prime p ==> + !b n m. 0 < m /\ (b ** n = p ** m) ==> ?k. (b = p ** k) /\ (k * n = m) +Proof + rpt strip_tac >> + `1 < p` by rw[ONE_LT_PRIME] >> + `m <> 0 /\ 0 < p /\ p <> 0 /\ p <> 1` by decide_tac >> + `p ** m <> 0` by rw[EXP_EQ_0] >> + `p ** m <> 1` by rw[EXP_EQ_1] >> + `n <> 0` by metis_tac[EXP] >> + `0 < n /\ 0 < p ** m` by decide_tac >> + `b <> 0` by metis_tac[EXP_EQ_0] >> + `0 < b` by decide_tac >> + `p divides (p ** m)` by rw[prime_divides_self_power] >> + `p divides b` by metis_tac[prime_divides_power] >> + `?u. p ** u divides b /\ ~(p divides b DIV p ** u)` by metis_tac[FACTOR_OUT_POWER] >> + qabbrev_tac `q = b DIV p ** u` >> + `p ** u <> 0` by rw[EXP_EQ_0] >> + `0 < p ** u` by decide_tac >> + `b = q * p ** u` by rw[GSYM DIVIDES_EQN, Abbr`q`] >> + `q ** n * p ** (u * n) = p ** m` by metis_tac[EXP_BASE_MULT, EXP_EXP_MULT] >> + qabbrev_tac `v = u * n` >> + Cases_on `v = m` >| [ + `p ** m = 1 * p ** m` by simp[] >> + `q ** n = 1` by metis_tac[EQ_MULT_RCANCEL] >> + `q = 1` by metis_tac[EXP_EQ_1] >> + `b = p ** u` by simp[] >> + metis_tac[], + Cases_on `v < m` >| [ + `?d. d = m - v` by rw[] >> + `0 < d /\ (m = d + v)` by rw[] >> + `p ** m = p ** d * p ** v` by rw[EXP_ADD] >> + `p ** v <> 0` by metis_tac[EXP_EQ_0] >> + `q ** n = p ** d` by metis_tac[EQ_MULT_RCANCEL] >> + `p divides p ** d` by metis_tac[prime_divides_self_power] >> + metis_tac[prime_divides_power], + `m < v` by decide_tac >> + `?d. d = v - m` by rw[] >> + `0 < d /\ (v = d + m)` by rw[] >> + `d <> 0` by decide_tac >> + `q ** n * p ** d * p ** m = p ** m` by metis_tac[EXP_ADD, MULT_ASSOC] >> + `_ = 1 * p ** m` by rw[] >> + `q ** n * p ** d = 1` by metis_tac[EQ_MULT_RCANCEL] >> + `(q ** n = 1) /\ (p ** d = 1)` by metis_tac[MULT_EQ_1] >> + metis_tac[EXP_EQ_1] + ] + ] +QED + +(* Theorem: 1 < n ==> !m. (n ** m = n) <=> (m = 1) *) +(* Proof: + If part: n ** m = n ==> m = 1 + Note n = n ** 1 by EXP_1 + so n ** m = n ** 1 by given + or m = 1 by EXP_BASE_INJECTIVE, 1 < n + Only-if part: m = 1 ==> n ** m = n + This is true by EXP_1 +*) +val POWER_EQ_SELF = store_thm( + "POWER_EQ_SELF", + ``!n. 1 < n ==> !m. (n ** m = n) <=> (m = 1)``, + metis_tac[EXP_BASE_INJECTIVE, EXP_1]); + +(* Theorem: k < HALF n <=> k + 1 < n - k *) +(* Proof: + If part: k < HALF n ==> k + 1 < n - k + Claim: 1 < n - 2 * k. + Proof: If EVEN n, + Claim: n - 2 * k <> 0 + Proof: By contradiction, assume n - 2 * k = 0. + Then 2 * k = n = 2 * HALF n by EVEN_HALF + or k = HALF n by MULT_LEFT_CANCEL, 2 <> 0 + but this contradicts k < HALF n. + Claim: n - 2 * k <> 1 + Proof: By contradiction, assume n - 2 * k = 1. + Then n = 2 * k + 1 by SUB_EQ_ADD, 0 < 1 + or ODD n by ODD_EXISTS, ADD1 + but this contradicts EVEN n by EVEN_ODD + Thus n - 2 * k <> 1, or 1 < n - 2 * k by above claims. + Since 1 < n - 2 * k by above + so 2 * k + 1 < n by arithmetic + or k + k + 1 < n by TIMES2 + or k + 1 < n - k by arithmetic + + Only-if part: k + 1 < n - k ==> k < HALF n + Since k + 1 < n - k + so 2 * k + 1 < n by arithmetic + But n = 2 * HALF n + (n MOD 2) by DIVISION, MULT_COMM, 0 < 2 + and n MOD 2 < 2 by MOD_LESS, 0 < 2 + so n <= 2 * HALF n + 1 by arithmetic + Thus 2 * k + 1 < 2 * HALF n + 1 by LESS_LESS_EQ_TRANS + or k < HALF by LT_MULT_LCANCEL +*) +val LESS_HALF_IFF = store_thm( + "LESS_HALF_IFF", + ``!n k. k < HALF n <=> k + 1 < n - k``, + rw[EQ_IMP_THM] >| [ + `1 < n - 2 * k` by + (Cases_on `EVEN n` >| [ + `n - 2 * k <> 0` by + (spose_not_then strip_assume_tac >> + `2 * HALF n = n` by metis_tac[EVEN_HALF] >> + decide_tac) >> + `n - 2 * k <> 1` by + (spose_not_then strip_assume_tac >> + `n = 2 * k + 1` by decide_tac >> + `ODD n` by metis_tac[ODD_EXISTS, ADD1] >> + metis_tac[EVEN_ODD]) >> + decide_tac, + `n MOD 2 = 1` by metis_tac[EVEN_ODD, ODD_MOD2] >> + `n = 2 * HALF n + (n MOD 2)` by metis_tac[DIVISION, MULT_COMM, DECIDE``0 < 2``] >> + decide_tac + ]) >> + decide_tac, + `2 * k + 1 < n` by decide_tac >> + `n = 2 * HALF n + (n MOD 2)` by metis_tac[DIVISION, MULT_COMM, DECIDE``0 < 2``] >> + `n MOD 2 < 2` by rw[] >> + decide_tac + ]); + +(* Theorem: HALF n < k ==> n - k <= HALF n *) +(* Proof: + If k < n, + If EVEN n, + Note HALF n + HALF n < k + HALF n by HALF n < k + or 2 * HALF n < k + HALF n by TIMES2 + or n < k + HALF n by EVEN_HALF, EVEN n + or n - k < HALF n by LESS_EQ_SUB_LESS, k <= n + Hence true. + If ~EVEN n, then ODD n by EVEN_ODD + Note HALF n + HALF n + 1 < k + HALF n + 1 by HALF n < k + or 2 * HALF n + 1 < k + HALF n + 1 by TIMES2 + or n < k + HALF n + 1 by ODD_HALF + or n <= k + HALF n by arithmetic + so n - k <= HALF n by SUB_LESS_EQ_ADD, k <= n + If ~(k < n), then n <= k. + Thus n - k = 0, hence n - k <= HALF n by arithmetic +*) +val MORE_HALF_IMP = store_thm( + "MORE_HALF_IMP", + ``!n k. HALF n < k ==> n - k <= HALF n``, + rpt strip_tac >> + Cases_on `k < n` >| [ + Cases_on `EVEN n` >| [ + `n = 2 * HALF n` by rw[EVEN_HALF] >> + `n < k + HALF n` by decide_tac >> + `n - k < HALF n` by decide_tac >> + decide_tac, + `ODD n` by rw[ODD_EVEN] >> + `n = 2 * HALF n + 1` by rw[ODD_HALF] >> + decide_tac + ], + decide_tac + ]); + +(* Theorem: (!k. k < m ==> f k < f (k + 1)) ==> !k. k < m ==> f k < f m *) +(* Proof: + By induction on the difference (m - k): + Base: 0 = m - k /\ k < m ==> f k < f m + Note m = k and k < m contradicts, hence true. + Step: !m k. (v = m - k) ==> k < m ==> f k < f m ==> + SUC v = m - k /\ k < m ==> f k < f m + Note v + 1 = m - k by ADD1 + so v = m - (k + 1) by arithmetic + If v = 0, + Then m = k + 1 + so f k < f (k + 1) by implication + or f k < f m by m = k + 1 + If v <> 0, then 0 < v. + Then 0 < m - (k + 1) by v = m - (k + 1) + or k + 1 < m by arithmetic + Now f k < f (k + 1) by implication, k < m + and f (k + 1) < f m by induction hypothesis, put k = k + 1 + so f k < f m by LESS_TRANS +*) +val MONOTONE_MAX = store_thm( + "MONOTONE_MAX", + ``!f m. (!k. k < m ==> f k < f (k + 1)) ==> !k. k < m ==> f k < f m``, + rpt strip_tac >> + Induct_on `m - k` >| [ + rpt strip_tac >> + decide_tac, + rpt strip_tac >> + `v + 1 = m - k` by rw[] >> + `v = m - (k + 1)` by decide_tac >> + Cases_on `v = 0` >| [ + `m = k + 1` by decide_tac >> + rw[], + `k + 1 < m` by decide_tac >> + `f k < f (k + 1)` by rw[] >> + `f (k + 1) < f m` by rw[] >> + decide_tac + ] + ]); + +(* Theorem: (multiple gap) + If n divides m, n cannot divide any x: m - n < x < m, or m < x < m + n + n divides m ==> !x. m - n < x /\ x < m + n /\ n divides x ==> (x = m) *) +(* Proof: + All these x, when divided by n, have non-zero remainders. + Since n divides m and n divides x + ==> ?h. m = h * n, and ?k. x = k * n by divides_def + Hence m - n < x + ==> (h-1) * n < k * n by RIGHT_SUB_DISTRIB, MULT_LEFT_1 + and x < m + n + ==> k * n < (h+1) * n by RIGHT_ADD_DISTRIB, MULT_LEFT_1 + so 0 < n, and h-1 < k, and k < h+1 by LT_MULT_RCANCEL + that is, h <= k, and k <= h + Therefore h = k, or m = h * n = k * n = x. +*) +val MULTIPLE_INTERVAL = store_thm( + "MULTIPLE_INTERVAL", + ``!n m. n divides m ==> !x. m - n < x /\ x < m + n /\ n divides x ==> (x = m)``, + rpt strip_tac >> + `(?h. m = h*n) /\ (?k. x = k * n)` by metis_tac[divides_def] >> + `(h-1) * n < k * n` by metis_tac[RIGHT_SUB_DISTRIB, MULT_LEFT_1] >> + `k * n < (h+1) * n` by metis_tac[RIGHT_ADD_DISTRIB, MULT_LEFT_1] >> + `0 < n /\ h-1 < k /\ k < h+1` by metis_tac[LT_MULT_RCANCEL] >> + `h = k` by decide_tac >> + metis_tac[]); + +(* Theorem: 0 < m ==> (SUC (n MOD m) = SUC n MOD m + (SUC n DIV m - n DIV m) * m) *) +(* Proof: + Let x = n DIV m, y = (SUC n) DIV m. + Let a = SUC (n MOD m), b = (SUC n) MOD m. + Note SUC n = y * m + b by DIVISION, 0 < m, for (SUC n), [1] + and n = x * m + (n MOD m) by DIVISION, 0 < m, for n + so SUC n = SUC (x * m + (n MOD m)) by above + = x * m + a by ADD_SUC, [2] + Equating, x * m + a = y * m + b by [1], [2] + Now n < SUC n by SUC_POS + so n DIV m <= (SUC n) DIV m by DIV_LE_MONOTONE, n <= SUC n + or x <= y + so x * m <= y * m by LE_MULT_RCANCEL, m <> 0 + + Thus a = b + (y * m - x * m) by arithmetic + = b + (y - x) * m by RIGHT_SUB_DISTRIB +*) +val MOD_SUC_EQN = store_thm( + "MOD_SUC_EQN", + ``!m n. 0 < m ==> (SUC (n MOD m) = SUC n MOD m + (SUC n DIV m - n DIV m) * m)``, + rpt strip_tac >> + qabbrev_tac `x = n DIV m` >> + qabbrev_tac `y = (SUC n) DIV m` >> + qabbrev_tac `a = SUC (n MOD m)` >> + qabbrev_tac `b = (SUC n) MOD m` >> + `SUC n = y * m + b` by rw[DIVISION, Abbr`y`, Abbr`b`] >> + `n = x * m + (n MOD m)` by rw[DIVISION, Abbr`x`] >> + `SUC n = x * m + a` by rw[Abbr`a`] >> + `n < SUC n` by rw[] >> + `x <= y` by rw[DIV_LE_MONOTONE, Abbr`x`, Abbr`y`] >> + `x * m <= y * m` by rw[] >> + `a = b + (y * m - x * m)` by decide_tac >> + `_ = b + (y - x) * m` by rw[] >> + rw[]); + +(* Note: Compare this result with these in arithmeticTheory: +MOD_SUC |- 0 < y /\ SUC x <> SUC (x DIV y) * y ==> (SUC x MOD y = SUC (x MOD y)) +MOD_SUC_IFF |- 0 < y ==> ((SUC x MOD y = SUC (x MOD y)) <=> SUC x <> SUC (x DIV y) * y) +*) + +(* Theorem: 1 < n ==> 1 < HALF (n ** 2) *) +(* Proof: + 1 < n + ==> 2 <= n by arithmetic + ==> 2 ** 2 <= n ** 2 by EXP_EXP_LE_MONO + ==> (2 ** 2) DIV 2 <= (n ** 2) DIV 2 by DIV_LE_MONOTONE + ==> 2 <= (n ** 2) DIV 2 by arithmetic + ==> 1 < (n ** 2) DIV 2 by arithmetic +*) +val ONE_LT_HALF_SQ = store_thm( + "ONE_LT_HALF_SQ", + ``!n. 1 < n ==> 1 < HALF (n ** 2)``, + rpt strip_tac >> + `2 <= n` by decide_tac >> + `2 ** 2 <= n ** 2` by rw[] >> + `(2 ** 2) DIV 2 <= (n ** 2) DIV 2` by rw[DIV_LE_MONOTONE] >> + `(2 ** 2) DIV 2 = 2` by EVAL_TAC >> + decide_tac); + +(* Theorem: 0 < n ==> (HALF (2 ** n) = 2 ** (n - 1)) *) +(* Proof + By induction on n. + Base: 0 < 0 ==> 2 ** 0 DIV 2 = 2 ** (0 - 1) + This is trivially true as 0 < 0 = F. + Step: 0 < n ==> HALF (2 ** n) = 2 ** (n - 1) + ==> 0 < SUC n ==> HALF (2 ** SUC n) = 2 ** (SUC n - 1) + HALF (2 ** SUC n) + = HALF (2 * 2 ** n) by EXP + = 2 ** n by MULT_TO_DIV + = 2 ** (SUC n - 1) by SUC_SUB1 +*) +Theorem EXP_2_HALF: + !n. 0 < n ==> (HALF (2 ** n) = 2 ** (n - 1)) +Proof + Induct >> simp[EXP, MULT_TO_DIV] +QED + +(* +There is EVEN_MULT |- !m n. EVEN (m * n) <=> EVEN m \/ EVEN n +There is EVEN_DOUBLE |- !n. EVEN (TWICE n) +*) + +(* Theorem: EVEN n ==> (HALF (m * n) = m * HALF n) *) +(* Proof: + Note n = TWICE (HALF n) by EVEN_HALF + Let k = HALF n. + HALF (m * n) + = HALF (m * (2 * k)) by above + = HALF (2 * (m * k)) by MULT_COMM_ASSOC + = m * k by HALF_TWICE + = m * HALF n by notation +*) +val HALF_MULT_EVEN = store_thm( + "HALF_MULT_EVEN", + ``!m n. EVEN n ==> (HALF (m * n) = m * HALF n)``, + metis_tac[EVEN_HALF, MULT_COMM_ASSOC, HALF_TWICE]); + +(* Theorem: 0 < k /\ k * m < n ==> m < n *) +(* Proof: + Note ?h. k = SUC h by num_CASES, k <> 0 + k * m + = SUC h * m by above + = (h + 1) * m by ADD1 + = h * m + 1 * m by LEFT_ADD_DISTRIB + = h * m + m by MULT_LEFT_1 + Since 0 <= h * m, + so k * m < n ==> m < n. +*) +val MULT_LT_IMP_LT = store_thm( + "MULT_LT_IMP_LT", + ``!m n k. 0 < k /\ k * m < n ==> m < n``, + rpt strip_tac >> + `k <> 0` by decide_tac >> + `?h. k = SUC h` by metis_tac[num_CASES] >> + `k * m = h * m + m` by rw[ADD1] >> + decide_tac); + +(* Theorem: 0 < k /\ k * m <= n ==> m <= n *) +(* Proof: + Note 1 <= k by 0 < k + so 1 * m <= k * m by LE_MULT_RCANCEL + or m <= k * m <= n by inequalities +*) +Theorem MULT_LE_IMP_LE: + !m n k. 0 < k /\ k * m <= n ==> m <= n +Proof + rpt strip_tac >> + `1 <= k` by decide_tac >> + `1 * m <= k * m` by simp[] >> + decide_tac +QED + +(* Theorem: n * HALF ((SQ n) ** 2) <= HALF (n ** 5) *) +(* Proof: + n * HALF ((SQ n) ** 2) + <= HALF (n * (SQ n) ** 2) by HALF_MULT + = HALF (n * (n ** 2) ** 2) by EXP_2 + = HALF (n * n ** 4) by EXP_EXP_MULT + = HALF (n ** 5) by EXP +*) +val HALF_EXP_5 = store_thm( + "HALF_EXP_5", + ``!n. n * HALF ((SQ n) ** 2) <= HALF (n ** 5)``, + rpt strip_tac >> + `n * ((SQ n) ** 2) = n * n ** 4` by rw[EXP_2, EXP_EXP_MULT] >> + `_ = n ** 5` by rw[EXP] >> + metis_tac[HALF_MULT]); + +(* Theorem: n <= 2 * m <=> (n <> 0 ==> HALF (n - 1) < m) *) +(* Proof: + Let k = n - 1, then n = SUC k. + If part: n <= TWICE m /\ n <> 0 ==> HALF k < m + Note HALF (SUC k) <= m by DIV_LE_MONOTONE, HALF_TWICE + If EVEN n, + Then ODD k by EVEN_ODD_SUC + ==> HALF (SUC k) = SUC (HALF k) by ODD_SUC_HALF + Thus SUC (HALF k) <= m by above + or HALF k < m by LESS_EQ + If ~EVEN n, then ODD n by EVEN_ODD + Thus EVEN k by EVEN_ODD_SUC + ==> HALF (SUC k) = HALF k by EVEN_SUC_HALF + But k <> TWICE m by k = n - 1, n <= TWICE m + ==> HALF k <> m by EVEN_HALF + Thus HALF k < m by HALF k <= m, HALF k <> m + + Only-if part: n <> 0 ==> HALF k < m ==> n <= TWICE m + If n = 0, trivially true. + If n <> 0, has HALF k < m. + If EVEN n, + Then ODD k by EVEN_ODD_SUC + ==> HALF (SUC k) = SUC (HALF k) by ODD_SUC_HALF + But SUC (HALF k) <= m by HALF k < m + Thus HALF n <= m by n = SUC k + ==> TWICE (HALF n) <= TWICE m by LE_MULT_LCANCEL + or n <= TWICE m by EVEN_HALF + If ~EVEN n, then ODD n by EVEN_ODD + Then EVEN k by EVEN_ODD_SUC + ==> TWICE (HALF k) < TWICE m by LT_MULT_LCANCEL + or k < TWICE m by EVEN_HALF + or n <= TWICE m by n = k + 1 +*) +val LE_TWICE_ALT = store_thm( + "LE_TWICE_ALT", + ``!m n. n <= 2 * m <=> (n <> 0 ==> HALF (n - 1) < m)``, + rw[EQ_IMP_THM] >| [ + `n = SUC (n - 1)` by decide_tac >> + qabbrev_tac `k = n - 1` >> + `HALF (SUC k) <= m` by metis_tac[DIV_LE_MONOTONE, HALF_TWICE, DECIDE``0 < 2``] >> + Cases_on `EVEN n` >| [ + `ODD k` by rw[EVEN_ODD_SUC] >> + `HALF (SUC k) = SUC (HALF k)` by rw[ODD_SUC_HALF] >> + decide_tac, + `ODD n` by metis_tac[EVEN_ODD] >> + `EVEN k` by rw[EVEN_ODD_SUC] >> + `HALF (SUC k) = HALF k` by rw[EVEN_SUC_HALF] >> + `k <> TWICE m` by rw[Abbr`k`] >> + `HALF k <> m` by metis_tac[EVEN_HALF] >> + decide_tac + ], + Cases_on `n = 0` >- + rw[] >> + `n = SUC (n - 1)` by decide_tac >> + qabbrev_tac `k = n - 1` >> + Cases_on `EVEN n` >| [ + `ODD k` by rw[EVEN_ODD_SUC] >> + `HALF (SUC k) = SUC (HALF k)` by rw[ODD_SUC_HALF] >> + `HALF n <= m` by rw[] >> + metis_tac[LE_MULT_LCANCEL, EVEN_HALF, DECIDE``2 <> 0``], + `ODD n` by metis_tac[EVEN_ODD] >> + `EVEN k` by rw[EVEN_ODD_SUC] >> + `k < TWICE m` by metis_tac[LT_MULT_LCANCEL, EVEN_HALF, DECIDE``0 < 2``] >> + rw[Abbr`k`] + ] + ]); + +(* Theorem: (HALF n) DIV 2 ** m = n DIV (2 ** SUC m) *) +(* Proof: + (HALF n) DIV 2 ** m + = (n DIV 2) DIV (2 ** m) by notation + = n DIV (2 * 2 ** m) by DIV_DIV_DIV_MULT, 0 < 2, 0 < 2 ** m + = n DIV (2 ** (SUC m)) by EXP +*) +val HALF_DIV_TWO_POWER = store_thm( + "HALF_DIV_TWO_POWER", + ``!m n. (HALF n) DIV 2 ** m = n DIV (2 ** SUC m)``, + rw[DIV_DIV_DIV_MULT, EXP]); + +(* Theorem: 1 + 2 + 3 + 4 = 10 *) +(* Proof: by calculation. *) +Theorem fit_for_10: + 1 + 2 + 3 + 4 = 10 +Proof + decide_tac +QED + +(* Theorem: 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 = 100 *) +(* Proof: by calculation. *) +Theorem fit_for_100: + 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 = 100 +Proof + decide_tac +QED + +(* ------------------------------------------------------------------------- *) + +(* Theorem: If prime p divides n, ?m. 0 < m /\ (p ** m) divides n /\ n DIV (p ** m) has no p *) +(* Proof: + Let s = {j | (p ** j) divides n } + Since p ** 1 = p, 1 IN s, so s <> {}. + (p ** j) divides n + ==> p ** j <= n by DIVIDES_LE + ==> p ** j <= p ** z by EXP_ALWAYS_BIG_ENOUGH + ==> j <= z by EXP_BASE_LE_MONO + ==> s SUBSET count (SUC z), + so FINITE s by FINITE_COUNT, SUBSET_FINITE + Let m = MAX_SET s, + m IN s, so (p ** m) divides n by MAX_SET_DEF + 1 <= m, or 0 < m. + ?q. n = q * (p ** m) by divides_def + To prove: !k. gcd (p ** k) (n DIV (p ** m)) = 1 + By contradiction, suppose there is a k such that + gcd (p ** k) (n DIV (p ** m)) <> 1 + So there is a prime pp that divides this gcd, by PRIME_FACTOR + but pp | p ** k, a pure prime, so pp = p by DIVIDES_EXP_BASE, prime_divides_only_self + pp | n DIV (p ** m) + or pp * p ** m | n + p * SUC m | n, making m not MAX_SET s. +*) +val FACTOR_OUT_PRIME = store_thm( + "FACTOR_OUT_PRIME", + ``!n p. 0 < n /\ prime p /\ p divides n ==> ?m. 0 < m /\ (p ** m) divides n /\ !k. gcd (p ** k) (n DIV (p ** m)) = 1``, + rpt strip_tac >> + qabbrev_tac `s = {j | (p ** j) divides n }` >> + `!j. j IN s <=> (p ** j) divides n` by rw[Abbr`s`] >> + `p ** 1 = p` by rw[] >> + `1 IN s` by metis_tac[] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `?z. n <= p ** z` by rw[EXP_ALWAYS_BIG_ENOUGH] >> + `!j. j IN s ==> p ** j <= n` by metis_tac[DIVIDES_LE] >> + `!j. j IN s ==> p ** j <= p ** z` by metis_tac[LESS_EQ_TRANS] >> + `!j. j IN s ==> j <= z` by metis_tac[EXP_BASE_LE_MONO] >> + `!j. j <= z <=> j < SUC z` by decide_tac >> + `!j. j < SUC z <=> j IN count (SUC z)` by rw[] >> + `s SUBSET count (SUC z)` by metis_tac[SUBSET_DEF] >> + `FINITE s` by metis_tac[FINITE_COUNT, SUBSET_FINITE] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + qabbrev_tac `m = MAX_SET s` >> + `m IN s /\ !y. y IN s ==> y <= m`by rw[MAX_SET_DEF, Abbr`m`] >> + qexists_tac `m` >> + CONJ_ASM1_TAC >| [ + `1 <= m` by metis_tac[] >> + decide_tac, + CONJ_ASM1_TAC >- + metis_tac[] >> + qabbrev_tac `pm = p ** m` >> + `0 < p` by decide_tac >> + `0 < pm` by rw[ZERO_LT_EXP, Abbr`pm`] >> + `n MOD pm = 0` by metis_tac[DIVIDES_MOD_0] >> + `n = n DIV pm * pm` by metis_tac[DIVISION, ADD_0] >> + qabbrev_tac `qm = n DIV pm` >> + spose_not_then strip_assume_tac >> + `?q. prime q /\ q divides (gcd (p ** k) qm)` by rw[PRIME_FACTOR] >> + `0 <> pm /\ n <> 0` by decide_tac >> + `qm <> 0` by metis_tac[MULT] >> + `0 < qm` by decide_tac >> + qabbrev_tac `pk = p ** k` >> + `0 < pk` by rw[ZERO_LT_EXP, Abbr`pk`] >> + `(gcd pk qm) divides pk /\ (gcd pk qm) divides qm` by metis_tac[GCD_DIVIDES, DIVIDES_MOD_0] >> + `q divides pk /\ q divides qm` by metis_tac[DIVIDES_TRANS] >> + `k <> 0` by metis_tac[EXP, GCD_1] >> + `0 < k` by decide_tac >> + `q divides p` by metis_tac[DIVIDES_EXP_BASE] >> + `q = p` by rw[prime_divides_only_self] >> + `?x. qm = x * q` by rw[GSYM divides_def] >> + `n = x * p * pm` by metis_tac[] >> + `_ = x * (p * pm)` by rw_tac arith_ss[] >> + `_ = x * (p ** SUC m)` by rw[EXP, Abbr`pm`] >> + `(p ** SUC m) divides n` by metis_tac[divides_def] >> + `SUC m <= m` by metis_tac[] >> + decide_tac + ]); + +(* ------------------------------------------------------------------------- *) +(* Consequences of Coprime. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: If 1 < n, !x. coprime n x ==> 0 < x /\ 0 < x MOD n *) +(* Proof: + If x = 0, gcd n x = n. But n <> 1, hence x <> 0, or 0 < x. + x MOD n = 0 ==> x a multiple of n ==> gcd n x = n <> 1 if n <> 1. + Hence if 1 < n, coprime n x ==> x MOD n <> 0, or 0 < x MOD n. +*) +val MOD_NONZERO_WHEN_GCD_ONE = store_thm( + "MOD_NONZERO_WHEN_GCD_ONE", + ``!n. 1 < n ==> !x. coprime n x ==> 0 < x /\ 0 < x MOD n``, + ntac 4 strip_tac >> + conj_asm1_tac >| [ + `1 <> n` by decide_tac >> + `x <> 0` by metis_tac[GCD_0R] >> + decide_tac, + `1 <> n /\ x <> 0` by decide_tac >> + `?k q. k * x = q * n + 1` by metis_tac[LINEAR_GCD] >> + `(k*x) MOD n = 1` by rw_tac std_ss[MOD_MULT] >> + spose_not_then strip_assume_tac >> + `(x MOD n = 0) /\ 0 < n /\ 1 <> 0` by decide_tac >> + metis_tac[MOD_MULTIPLE_ZERO, MULT_COMM] + ]); + +(* Theorem: If 1 < n, coprime n x ==> ?k. ((k * x) MOD n = 1) /\ coprime n k *) +(* Proof: + gcd n x = 1 ==> x <> 0 by GCD_0R + Also, + gcd n x = 1 + ==> ?k q. k * x = q * n + 1 by LINEAR_GCD + ==> (k * x) MOD n = (q * n + 1) MOD n by arithmetic + ==> (k * x) MOD n = 1 by MOD_MULT, 1 < n. + + Let g = gcd n k. + Since 1 < n, 0 < n. + Since q * n+1 <> 0, x <> 0, k <> 0, hence 0 < k. + Hence 0 < g /\ (n MOD g = 0) /\ (k MOD g = 0) by GCD_DIVIDES. + Or n = a * g /\ k = b * g for some a, b. + Therefore: + (b * g) * x = q * (a * g) + 1 + (b * x) * g = (q * a) * g + 1 by arithmetic + Hence g divides 1, or g = 1 since 0 < g. +*) +val GCD_ONE_PROPERTY = store_thm( + "GCD_ONE_PROPERTY", + ``!n x. 1 < n /\ coprime n x ==> ?k. ((k * x) MOD n = 1) /\ coprime n k``, + rpt strip_tac >> + `n <> 1` by decide_tac >> + `x <> 0` by metis_tac[GCD_0R] >> + `?k q. k * x = q * n + 1` by metis_tac[LINEAR_GCD] >> + `(k * x) MOD n = 1` by rw_tac std_ss[MOD_MULT] >> + `?g. g = gcd n k` by rw[] >> + `n <> 0 /\ q*n + 1 <> 0` by decide_tac >> + `k <> 0` by metis_tac[MULT_EQ_0] >> + `0 < g /\ (n MOD g = 0) /\ (k MOD g = 0)` by metis_tac[GCD_DIVIDES, NOT_ZERO_LT_ZERO] >> + `g divides n /\ g divides k` by rw[DIVIDES_MOD_0] >> + `g divides (n * q) /\ g divides (k*x)` by rw[DIVIDES_MULT] >> + `g divides (n * q + 1)` by metis_tac [MULT_COMM] >> + `g divides 1` by metis_tac[DIVIDES_ADD_2] >> + metis_tac[DIVIDES_ONE]); + +(* Theorem: For 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> + ?y. 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1) *) +(* Proof: + gcd n x = 1 + ==> ?k. (k * x) MOD n = 1 /\ coprime n k by GCD_ONE_PROPERTY + (k * x) MOD n = 1 + ==> (k MOD n * x MOD n) MOD n = 1 by MOD_TIMES2 + ==> ((k MOD n) * x) MOD n = 1 by LESS_MOD, x < n. + + Now k MOD n < n by MOD_LESS + and 0 < k MOD n by MOD_MULTIPLE_ZERO and 1 <> 0. + + Hence take y = k MOD n, then 0 < y < n. + and gcd n k = 1 ==> gcd n (k MOD n) = 1 by MOD_WITH_GCD_ONE. +*) +val GCD_MOD_MULT_INV = store_thm( + "GCD_MOD_MULT_INV", + ``!n x. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> + ?y. 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1)``, + rpt strip_tac >> + `?k. ((k * x) MOD n = 1) /\ coprime n k` by rw_tac std_ss[GCD_ONE_PROPERTY] >> + `0 < n` by decide_tac >> + `(k MOD n * x MOD n) MOD n = 1` by rw_tac std_ss[MOD_TIMES2] >> + `((k MOD n) * x) MOD n = 1` by metis_tac[LESS_MOD] >> + `k MOD n < n` by rw_tac std_ss[MOD_LESS] >> + `1 <> 0` by decide_tac >> + `0 <> k MOD n` by metis_tac[MOD_MULTIPLE_ZERO] >> + `0 < k MOD n` by decide_tac >> + metis_tac[MOD_WITH_GCD_ONE]); + +(* Convert this into an existence definition *) +val lemma = prove( + ``!n x. ?y. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> + 0 < y /\ y < n /\ coprime n y /\ ((y * x) MOD n = 1)``, + metis_tac[GCD_MOD_MULT_INV]); + +val GEN_MULT_INV_DEF = new_specification( + "GEN_MULT_INV_DEF", + ["GCD_MOD_MUL_INV"], + SIMP_RULE (srw_ss()) [SKOLEM_THM] lemma); +(* > val GEN_MULT_INV_DEF = + |- !n x. 1 < n /\ 0 < x /\ x < n /\ coprime n x ==> + 0 < GCD_MOD_MUL_INV n x /\ GCD_MOD_MUL_INV n x < n /\ coprime n (GCD_MOD_MUL_INV n x) /\ + ((GCD_MOD_MUL_INV n x * x) MOD n = 1) : thm *) + +(* Theorem: If 1/c = 1/b - 1/a, then lcm a b = lcm a c. + a * b = c * (a - b) ==> lcm a b = lcm a c *) +(* Proof: + Idea: + lcm a c + = (a * c) DIV (gcd a c) by lcm_def + = (a * b * c) DIV (gcd a c) DIV b by MULT_DIV + = (a * b * c) DIV b * (gcd a c) by DIV_DIV_DIV_MULT + = (a * b * c) DIV gcd b*a b*c by GCD_COMMON_FACTOR + = (a * b * c) DIV gcd c*(a-b) c*b by given + = (a * b * c) DIV c * gcd (a-b) b by GCD_COMMON_FACTOR + = (a * b * c) DIV c * gcd a b by GCD_SUB_L + = (a * b * c) DIV c DIV gcd a b by DIV_DIV_DIV_MULT + = a * b DIV gcd a b by MULT_DIV + = lcm a b by lcm_def + + Details: + If a = 0, + lcm 0 b = 0 = lcm 0 c by LCM_0 + If a <> 0, + If b = 0, a * b = 0 = c * a by MULT_0, SUB_0 + Hence c = 0, hence true by MULT_EQ_0 + If b <> 0, c <> 0. by MULT_EQ_0 + So 0 < gcd a c, 0 < gcd a b by GCD_EQ_0 + and (gcd a c) divides a by GCD_IS_GREATEST_COMMON_DIVISOR + thus (gcd a c) divides (a * c) by DIVIDES_MULT + Note (a - b) <> 0 by MULT_EQ_0 + so ~(a <= b) by SUB_EQ_0 + or b < a, or b <= a for GCD_SUB_L later. + Now, + lcm a c + = (a * c) DIV (gcd a c) by lcm_def + = (b * ((a * c) DIV (gcd a c))) DIV b by MULT_COMM, MULT_DIV + = ((b * (a * c)) DIV (gcd a c)) DIV b by MULTIPLY_DIV + = (b * (a * c)) DIV ((gcd a c) * b) by DIV_DIV_DIV_MULT + = (b * a * c) DIV ((gcd a c) * b) by MULT_ASSOC + = c * (a * b) DIV (b * (gcd a c)) by MULT_COMM + = c * (a * b) DIV (gcd (b * a) (b * c)) by GCD_COMMON_FACTOR + = c * (a * b) DIV (gcd (a * b) (c * b)) by MULT_COMM + = c * (a * b) DIV (gcd (c * (a-b)) (c * b)) by a * b = c * (a - b) + = c * (a * b) DIV (c * gcd (a-b) b) by GCD_COMMON_FACTOR + = c * (a * b) DIV (c * gcd a b) by GCD_SUB_L + = c * (a * b) DIV c DIV (gcd a b) by DIV_DIV_DIV_MULT + = a * b DIV gcd a b by MULT_COMM, MULT_DIV + = lcm a b by lcm_def +*) +val LCM_EXCHANGE = store_thm( + "LCM_EXCHANGE", + ``!a b c. (a * b = c * (a - b)) ==> (lcm a b = lcm a c)``, + rpt strip_tac >> + Cases_on `a = 0` >- + rw[] >> + Cases_on `b = 0` >| [ + `c = 0` by metis_tac[MULT_EQ_0, SUB_0] >> + rw[], + `c <> 0` by metis_tac[MULT_EQ_0] >> + `0 < b /\ 0 < c` by decide_tac >> + `(gcd a c) divides a` by rw[GCD_IS_GREATEST_COMMON_DIVISOR] >> + `(gcd a c) divides (a * c)` by rw[DIVIDES_MULT] >> + `0 < gcd a c /\ 0 < gcd a b` by metis_tac[GCD_EQ_0, NOT_ZERO_LT_ZERO] >> + `~(a <= b)` by metis_tac[SUB_EQ_0, MULT_EQ_0] >> + `b <= a` by decide_tac >> + `lcm a c = (a * c) DIV (gcd a c)` by rw[lcm_def] >> + `_ = (b * ((a * c) DIV (gcd a c))) DIV b` by metis_tac[MULT_COMM, MULT_DIV] >> + `_ = ((b * (a * c)) DIV (gcd a c)) DIV b` by rw[MULTIPLY_DIV] >> + `_ = (b * (a * c)) DIV ((gcd a c) * b)` by rw[DIV_DIV_DIV_MULT] >> + `_ = (b * a * c) DIV ((gcd a c) * b)` by rw[MULT_ASSOC] >> + `_ = c * (a * b) DIV (b * (gcd a c))` by rw_tac std_ss[MULT_COMM] >> + `_ = c * (a * b) DIV (gcd (b * a) (b * c))` by rw[GCD_COMMON_FACTOR] >> + `_ = c * (a * b) DIV (gcd (a * b) (c * b))` by rw_tac std_ss[MULT_COMM] >> + `_ = c * (a * b) DIV (gcd (c * (a-b)) (c * b))` by rw[] >> + `_ = c * (a * b) DIV (c * gcd (a-b) b)` by rw[GCD_COMMON_FACTOR] >> + `_ = c * (a * b) DIV (c * gcd a b)` by rw[GCD_SUB_L] >> + `_ = c * (a * b) DIV c DIV (gcd a b)` by rw[DIV_DIV_DIV_MULT] >> + `_ = a * b DIV gcd a b` by metis_tac[MULT_COMM, MULT_DIV] >> + `_ = lcm a b` by rw[lcm_def] >> + decide_tac + ]); + +(* Theorem: LCM (k * m) (k * n) = k * LCM m n *) +(* Proof: + If m = 0 or n = 0, LHS = 0 = RHS. + If m <> 0 and n <> 0, + lcm (k * m) (k * n) + = (k * m) * (k * n) / gcd (k * m) (k * n) by GCD_LCM + = (k * m) * (k * n) / k * (gcd m n) by GCD_COMMON_FACTOR + = k * m * n / (gcd m n) + = k * LCM m n by GCD_LCM +*) +val LCM_COMMON_FACTOR = store_thm( + "LCM_COMMON_FACTOR", + ``!m n k. lcm (k * m) (k * n) = k * lcm m n``, + rpt strip_tac >> + `k * (k * (m * n)) = (k * m) * (k * n)` by rw_tac arith_ss[] >> + `_ = gcd (k * m) (k * n) * lcm (k * m) (k * n) ` by rw[GCD_LCM] >> + `_ = k * (gcd m n) * lcm (k * m) (k * n)` by rw[GCD_COMMON_FACTOR] >> + `_ = k * ((gcd m n) * lcm (k * m) (k * n))` by rw_tac arith_ss[] >> + Cases_on `k = 0` >- + rw[] >> + `(gcd m n) * lcm (k * m) (k * n) = k * (m * n)` by metis_tac[MULT_LEFT_CANCEL] >> + `_ = k * ((gcd m n) * (lcm m n))` by rw_tac std_ss[GCD_LCM] >> + `_ = (gcd m n) * (k * (lcm m n))` by rw_tac arith_ss[] >> + Cases_on `n = 0` >- + rw[] >> + metis_tac[MULT_LEFT_CANCEL, GCD_EQ_0]); + +(* Theorem: coprime a b ==> !c. lcm (a * c) (b * c) = a * b * c *) +(* Proof: + lcm (a * c) (b * c) + = lcm (c * a) (c * b) by MULT_COMM + = c * (lcm a b) by LCM_COMMON_FACTOR + = (lcm a b) * c by MULT_COMM + = a * b * c by LCM_COPRIME +*) +val LCM_COMMON_COPRIME = store_thm( + "LCM_COMMON_COPRIME", + ``!a b. coprime a b ==> !c. lcm (a * c) (b * c) = a * b * c``, + metis_tac[LCM_COMMON_FACTOR, LCM_COPRIME, MULT_COMM]); + +(* Theorem: 0 < n /\ m MOD n = 0 ==> gcd m n = n *) +(* Proof: + Since m MOD n = 0 + ==> n divides m by DIVIDES_MOD_0 + Hence gcd m n = gcd n m by GCD_SYM + = n by divides_iff_gcd_fix +*) +val GCD_MULTIPLE = store_thm( + "GCD_MULTIPLE", + ``!m n. 0 < n /\ (m MOD n = 0) ==> (gcd m n = n)``, + metis_tac[DIVIDES_MOD_0, divides_iff_gcd_fix, GCD_SYM]); + +(* Theorem: gcd (m * n) n = n *) +(* Proof: + gcd (m * n) n + = gcd (n * m) n by MULT_COMM + = gcd (n * m) (n * 1) by MULT_RIGHT_1 + = n * (gcd m 1) by GCD_COMMON_FACTOR + = n * 1 by GCD_1 + = n by MULT_RIGHT_1 +*) +val GCD_MULTIPLE_ALT = store_thm( + "GCD_MULTIPLE_ALT", + ``!m n. gcd (m * n) n = n``, + rpt strip_tac >> + `gcd (m * n) n = gcd (n * m) n` by rw[MULT_COMM] >> + `_ = gcd (n * m) (n * 1)` by rw[] >> + rw[GCD_COMMON_FACTOR]); + + +(* Theorem: k * a <= b ==> gcd a b = gcd a (b - k * a) *) +(* Proof: + By induction on k. + Base case: 0 * a <= b ==> gcd a b = gcd a (b - 0 * a) + True since b - 0 * a = b by MULT, SUB_0 + Step case: k * a <= b ==> (gcd a b = gcd a (b - k * a)) ==> + SUC k * a <= b ==> (gcd a b = gcd a (b - SUC k * a)) + SUC k * a <= b + ==> k * a + a <= b by MULT + so a <= b - k * a by arithmetic [1] + and k * a <= b by 0 <= b - k * a, [2] + gcd a (b - SUC k * a) + = gcd a (b - (k * a + a)) by MULT + = gcd a (b - k * a - a) by arithmetic + = gcd a (b - k * a - a + a) by GCD_ADD_L, ADD_COMM + = gcd a (b - k * a) by SUB_ADD, a <= b - k * a [1] + = gcd a b by induction hypothesis, k * a <= b [2] +*) +val GCD_SUB_MULTIPLE = store_thm( + "GCD_SUB_MULTIPLE", + ``!a b k. k * a <= b ==> (gcd a b = gcd a (b - k * a))``, + rpt strip_tac >> + Induct_on `k` >- + rw[] >> + rw_tac std_ss[] >> + `k * a + a <= b` by metis_tac[MULT] >> + `a <= b - k * a` by decide_tac >> + `k * a <= b` by decide_tac >> + `gcd a (b - SUC k * a) = gcd a (b - (k * a + a))` by rw[MULT] >> + `_ = gcd a (b - k * a - a)` by rw_tac arith_ss[] >> + `_ = gcd a (b - k * a - a + a)` by rw[GCD_ADD_L, ADD_COMM] >> + rw_tac std_ss[SUB_ADD]); + +(* Theorem: k * a <= b ==> (gcd b a = gcd a (b - k * a)) *) +(* Proof: by GCD_SUB_MULTIPLE, GCD_SYM *) +val GCD_SUB_MULTIPLE_COMM = store_thm( + "GCD_SUB_MULTIPLE_COMM", + ``!a b k. k * a <= b ==> (gcd b a = gcd a (b - k * a))``, + metis_tac[GCD_SUB_MULTIPLE, GCD_SYM]); + +(* Idea: a crude upper bound for greatest common divisor. + A better upper bound is: gcd m n <= MIN m n, by MIN_LE *) + +(* Theorem: 0 < m /\ 0 < n ==> gcd m n <= m /\ gcd m n <= n *) +(* Proof: + Let g = gcd m n. + Then g divides m /\ g divides n by GCD_PROPERTY + so g <= m /\ g <= n by DIVIDES_LE, 0 < m, 0 < n +*) +Theorem gcd_le: + !m n. 0 < m /\ 0 < n ==> gcd m n <= m /\ gcd m n <= n +Proof + ntac 3 strip_tac >> + qabbrev_tac `g = gcd m n` >> + `g divides m /\ g divides n` by metis_tac[GCD_PROPERTY] >> + simp[DIVIDES_LE] +QED + +(* Idea: a generalisation of GCD_LINEAR: +|- !j k. 0 < j ==> ?p q. p * j = q * k + gcd j k + This imposes a condition for (gcd a b) divides c. +*) + +(* Theorem: 0 < a ==> ((gcd a b) divides c <=> ?p q. p * a = q * b + c) *) +(* Proof: + Let d = gcd a b. + If part: d divides c ==> ?p q. p * a = q * b + c + Note ?k. c = k * d by divides_def + and ?u v. u * a = v * b + d by GCD_LINEAR, 0 < a + so (k * u) * a = (k * v) * b + (k * d) + Take p = k * u, q = k * v, + Then p * q = q * b + c + Only-if part: p * a = q * b + c ==> d divides c + Note d divides a /\ d divides b by GCD_PROPERTY + so d divides c by divides_linear_sub +*) +Theorem gcd_divides_iff: + !a b c. 0 < a ==> ((gcd a b) divides c <=> ?p q. p * a = q * b + c) +Proof + rpt strip_tac >> + qabbrev_tac `d = gcd a b` >> + rw_tac bool_ss[EQ_IMP_THM] >| [ + `?k. c = k * d` by rw[GSYM divides_def] >> + `?p q. p * a = q * b + d` by rw[GCD_LINEAR, Abbr`d`] >> + `k * (p * a) = k * (q * b + d)` by fs[] >> + `_ = k * (q * b) + k * d` by decide_tac >> + metis_tac[MULT_ASSOC], + `d divides a /\ d divides b` by metis_tac[GCD_PROPERTY] >> + metis_tac[divides_linear_sub] + ] +QED + +(* Theorem alias *) +Theorem gcd_linear_thm = gcd_divides_iff; +(* val gcd_linear_thm = +|- !a b c. 0 < a ==> (gcd a b divides c <=> ?p q. p * a = q * b + c): thm *) + +(* Idea: a version of GCD_LINEAR for MOD, without negatives. + That is: in MOD n. gcd (a b) can be expressed as a linear combination of a b. *) + +(* Theorem: 0 < n /\ 0 < a ==> ?p q. (p * a + q * b) MOD n = gcd a b MOD n *) +(* Proof: + Let d = gcd a b. + Then ?h k. h * a = k * b + d by GCD_LINEAR, 0 < a + Let p = h, q = k * n - k. + Then q + k = k * n. + (p * a) MOD n = (k * b + d) MOD n + <=> (p * a + q * b) MOD n = (q * b + k * b + d) MOD n by ADD_MOD + <=> (p * a + q * b) MOD n = (k * b * n + d) MOD n by above + <=> (p * a + q * b) MOD n = d MOD n by MOD_TIMES +*) +Theorem gcd_linear_mod_thm: + !n a b. 0 < n /\ 0 < a ==> ?p q. (p * a + q * b) MOD n = gcd a b MOD n +Proof + rpt strip_tac >> + qabbrev_tac `d = gcd a b` >> + `?p k. p * a = k * b + d` by rw[GCD_LINEAR, Abbr`d`] >> + `k <= k * n` by fs[] >> + `k * n - k + k = k * n` by decide_tac >> + qabbrev_tac `q = k * n - k` >> + qexists_tac `p` >> + qexists_tac `q` >> + `(p * a + q * b) MOD n = (q * b + k * b + d) MOD n` by rw[ADD_MOD] >> + `_ = ((q + k) * b + d) MOD n` by decide_tac >> + `_ = (k * b * n + d) MOD n` by rfs[] >> + simp[MOD_TIMES] +QED + +(* Idea: a simplification of gcd_linear_mod_thm when n = a. *) + +(* Theorem: 0 < a ==> ?q. (q * b) MOD a = (gcd a b) MOD a *) +(* Proof: + Let g = gcd a b. + Then ?p q. (p * a + q * b) MOD a = g MOD a by gcd_linear_mod_thm, n = a + so (q * b) MOD a = g MOD a by MOD_TIMES +*) +Theorem gcd_linear_mod_1: + !a b. 0 < a ==> ?q. (q * b) MOD a = (gcd a b) MOD a +Proof + metis_tac[gcd_linear_mod_thm, MOD_TIMES] +QED + +(* Idea: symmetric version of of gcd_linear_mod_1. *) + +(* Theorem: 0 < b ==> ?p. (p * a) MOD b = (gcd a b) MOD b *) +(* Proof: + Note ?p. (p * a) MOD b = (gcd b a) MOD b by gcd_linear_mod_1 + or = (gcd a b) MOD b by GCD_SYM +*) +Theorem gcd_linear_mod_2: + !a b. 0 < b ==> ?p. (p * a) MOD b = (gcd a b) MOD b +Proof + metis_tac[gcd_linear_mod_1, GCD_SYM] +QED + +(* Idea: replacing n = a * b in gcd_linear_mod_thm. *) + +(* Theorem: 0 < a /\ 0 < b ==> ?p q. (p * a + q * b) MOD (a * b) = (gcd a b) MOD (a * b) *) +(* Proof: by gcd_linear_mod_thm, n = a * b. *) +Theorem gcd_linear_mod_prod: + !a b. 0 < a /\ 0 < b ==> ?p q. (p * a + q * b) MOD (a * b) = (gcd a b) MOD (a * b) +Proof + simp[gcd_linear_mod_thm] +QED + +(* Idea: specialise gcd_linear_mod_prod for coprime a b. *) + +(* Theorem: 0 < a /\ 0 < b /\ coprime a b ==> + ?p q. (p * a + q * b) MOD (a * b) = 1 MOD (a * b) *) +(* Proof: by gcd_linear_mod_prod. *) +Theorem coprime_linear_mod_prod: + !a b. 0 < a /\ 0 < b /\ coprime a b ==> + ?p q. (p * a + q * b) MOD (a * b) = 1 MOD (a * b) +Proof + metis_tac[gcd_linear_mod_prod] +QED + +(* Idea: generalise gcd_linear_mod_thm for multiple of gcd a b. *) + +(* Theorem: 0 < n /\ 0 < a /\ gcd a b divides c ==> + ?p q. (p * a + q * b) MOD n = c MOD n *) +(* Proof: + Let d = gcd a b. + Note k. c = k * d by divides_def + and ?p q. (p * a + q * b) MOD n = d MOD n by gcd_linear_mod_thm + Thus (k * d) MOD n + = (k * (p * a + q * b)) MOD n by MOD_TIMES2, 0 < n + = (k * p * a + k * q * b) MOD n by LEFT_ADD_DISTRIB + Take (k * p) and (k * q) for the eventual p and q. +*) +Theorem gcd_multiple_linear_mod_thm: + !n a b c. 0 < n /\ 0 < a /\ gcd a b divides c ==> + ?p q. (p * a + q * b) MOD n = c MOD n +Proof + rpt strip_tac >> + qabbrev_tac `d = gcd a b` >> + `?k. c = k * d` by rw[GSYM divides_def] >> + `?p q. (p * a + q * b) MOD n = d MOD n` by metis_tac[gcd_linear_mod_thm] >> + `(k * (p * a + q * b)) MOD n = (k * d) MOD n` by metis_tac[MOD_TIMES2] >> + `k * (p * a + q * b) = k * p * a + k * q * b` by decide_tac >> + metis_tac[] +QED + +(* Idea: specialise gcd_multiple_linear_mod_thm for n = a * b. *) + +(* Theorem: 0 < a /\ 0 < b /\ gcd a b divides c ==> + ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b)) *) +(* Proof: by gcd_multiple_linear_mod_thm. *) +Theorem gcd_multiple_linear_mod_prod: + !a b c. 0 < a /\ 0 < b /\ gcd a b divides c ==> + ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) +Proof + simp[gcd_multiple_linear_mod_thm] +QED + +(* Idea: specialise gcd_multiple_linear_mod_prod for coprime a b. *) + +(* Theorem: 0 < a /\ 0 < b /\ coprime a b ==> + ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) *) +(* Proof: + Note coprime a b means gcd a b = 1 by notation + and 1 divides c by ONE_DIVIDES_ALL + so the result follows by gcd_multiple_linear_mod_prod +*) +Theorem coprime_multiple_linear_mod_prod: + !a b c. 0 < a /\ 0 < b /\ coprime a b ==> + ?p q. (p * a + q * b) MOD (a * b) = c MOD (a * b) +Proof + metis_tac[gcd_multiple_linear_mod_prod, ONE_DIVIDES_ALL] +QED + +(* ------------------------------------------------------------------------- *) +(* Coprime Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < n ==> !a b. coprime a b <=> coprime a (b ** n) *) +(* Proof: + If part: coprime a b ==> coprime a (b ** n) + True by coprime_exp_comm. + Only-if part: coprime a (b ** n) ==> coprime a b + If a = 0, + then b ** n = 1 by GCD_0L + and b = 1 by EXP_EQ_1, n <> 0 + Hence coprime 0 1 by GCD_0L + If a <> 0, + Since coprime a (b ** n) means + ?h k. h * a = k * b ** n + 1 by LINEAR_GCD, GCD_SYM + Let d = gcd a b. + Since d divides a and d divides b by GCD_IS_GREATEST_COMMON_DIVISOR + and d divides b ** n by divides_exp, 0 < n + so d divides 1 by divides_linear_sub + Thus d = 1 by DIVIDES_ONE + or coprime a b by notation +*) +val coprime_iff_coprime_exp = store_thm( + "coprime_iff_coprime_exp", + ``!n. 0 < n ==> !a b. coprime a b <=> coprime a (b ** n)``, + rw[EQ_IMP_THM] >- + rw[coprime_exp_comm] >> + `n <> 0` by decide_tac >> + Cases_on `a = 0` >- + metis_tac[GCD_0L, EXP_EQ_1] >> + `?h k. h * a = k * b ** n + 1` by metis_tac[LINEAR_GCD, GCD_SYM] >> + qabbrev_tac `d = gcd a b` >> + `d divides a /\ d divides b` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> + `d divides (b ** n)` by rw[divides_exp] >> + `d divides 1` by metis_tac[divides_linear_sub] >> + rw[GSYM DIVIDES_ONE]); + +(* Theorem: 1 < n /\ coprime n m ==> ~(n divides m) *) +(* Proof: + coprime n m + ==> gcd n m = 1 by notation + ==> n MOD m <> 0 by MOD_NONZERO_WHEN_GCD_ONE, with 1 < n + ==> ~(n divides m) by DIVIDES_MOD_0, with 0 < n +*) +val coprime_not_divides = store_thm( + "coprime_not_divides", + ``!m n. 1 < n /\ coprime n m ==> ~(n divides m)``, + metis_tac[MOD_NONZERO_WHEN_GCD_ONE, DIVIDES_MOD_0, ONE_LT_POS, NOT_ZERO_LT_ZERO]); + +(* Theorem: 1 < n ==> (!j. 0 < j /\ j <= m ==> coprime n j) ==> m < n *) +(* Proof: + By contradiction. Suppose n <= m. + Since 1 < n means 0 < n and n <> 1, + The implication shows + coprime n n, or n = 1 by notation + But gcd n n = n by GCD_REF + This contradicts n <> 1. +*) +val coprime_all_le_imp_lt = store_thm( + "coprime_all_le_imp_lt", + ``!n. 1 < n ==> !m. (!j. 0 < j /\ j <= m ==> coprime n j) ==> m < n``, + spose_not_then strip_assume_tac >> + `n <= m` by decide_tac >> + `0 < n /\ n <> 1` by decide_tac >> + metis_tac[GCD_REF]); + +(* Theorem: (!j. 1 < j /\ j <= m ==> ~(j divides n)) <=> (!j. 1 < j /\ j <= m ==> coprime j n) *) +(* Proof: + If part: (!j. 1 < j /\ j <= m ==> ~(j divides n)) /\ 1 < j /\ j <= m ==> coprime j n + Let d = gcd j n. + Then d divides j /\ d divides n by GCD_IS_GREATEST_COMMON_DIVISOR + Now 1 < j ==> 0 < j /\ j <> 0 + so d <= j by DIVIDES_LE, 0 < j + and d <> 0 by GCD_EQ_0, j <> 0 + By contradiction, suppose d <> 1. + Then 1 < d /\ d <= m by d <> 1, d <= j /\ j <= m + so ~(d divides n), a contradiction by implication + + Only-if part: (!j. 1 < j /\ j <= m ==> coprime j n) /\ 1 < j /\ j <= m ==> ~(j divides n) + Since coprime j n by implication + so ~(j divides n) by coprime_not_divides +*) +val coprime_condition = store_thm( + "coprime_condition", + ``!m n. (!j. 1 < j /\ j <= m ==> ~(j divides n)) <=> (!j. 1 < j /\ j <= m ==> coprime j n)``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + qabbrev_tac `d = gcd j n` >> + `d divides j /\ d divides n` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> + `0 < j /\ j <> 0` by decide_tac >> + `d <= j` by rw[DIVIDES_LE] >> + `d <> 0` by metis_tac[GCD_EQ_0] >> + `1 < d /\ d <= m` by decide_tac >> + metis_tac[], + metis_tac[coprime_not_divides] + ]); + +(* Note: +The above is the generalization of this observation: +- a prime n has all 1 < j < n coprime to n. Therefore, +- a number n has all 1 < j < m coprime to n, where m is the first non-trivial factor of n. + Of course, the first non-trivial factor of n must be a prime. +*) + +(* Theorem: 1 < m /\ (!j. 1 < j /\ j <= m ==> ~(j divides n)) ==> coprime m n *) +(* Proof: by coprime_condition, taking j = m. *) +val coprime_by_le_not_divides = store_thm( + "coprime_by_le_not_divides", + ``!m n. 1 < m /\ (!j. 1 < j /\ j <= m ==> ~(j divides n)) ==> coprime m n``, + rw[coprime_condition]); + +(* Idea: establish coprime (p * a + q * b) (a * b). *) +(* Note: the key is to apply coprime_by_prime_factor. *) + +(* Theorem: coprime a b /\ coprime p b /\ coprime q a ==> coprime (p * a + q * b) (a * b) *) +(* Proof: + Let z = p * a + q * b, c = a * b, d = gcd z c. + Then d divides z /\ d divides c by GCD_PROPERTY + By coprime_by_prime_factor, we need to show: + !t. prime t ==> ~(t divides z /\ t divides c) + By contradiction, suppose t divides z /\ t divides c. + Then t divides d by GCD_PROPERTY + or t divides c where c = a * b by DIVIDES_TRANS + so t divides a or p divides b by P_EUCLIDES + + If t divides a, + Then t divides (q * b) by divides_linear_sub + and ~(t divides b) by coprime_common_factor, NOT_PRIME_1 + so t divides q by P_EUCLIDES + ==> t = 1 by coprime_common_factor + This contradicts prime t by NOT_PRIME_1 + If t divides b, + Then t divides (p * a) by divides_linear_sub + and ~(t divides a) by coprime_common_factor, NOT_PRIME_1 + so t divides p by P_EUCLIDES + ==> t = 1 by coprime_common_factor + This contradicts prime t by NOT_PRIME_1 + Since all lead to contradiction, we have shown: + !t. prime t ==> ~(t divides z /\ t divides c) + Thus coprime z c by coprime_by_prime_factor +*) +Theorem coprime_linear_mult: + !a b p q. coprime a b /\ coprime p b /\ coprime q a ==> coprime (p * a + q * b) (a * b) +Proof + rpt strip_tac >> + qabbrev_tac `z = p * a + q * b` >> + qabbrev_tac `c = a * b` >> + irule (coprime_by_prime_factor |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> + rpt strip_tac >> + `p' divides a \/ p' divides b` by metis_tac[P_EUCLIDES] >| [ + `p' divides (q * b)` by metis_tac[divides_linear_sub, MULT_LEFT_1] >> + `~(p' divides b)` by metis_tac[coprime_common_factor, NOT_PRIME_1] >> + `p' divides q` by metis_tac[P_EUCLIDES] >> + metis_tac[coprime_common_factor, NOT_PRIME_1], + `p' divides (p * a)` by metis_tac[divides_linear_sub, MULT_LEFT_1, ADD_COMM] >> + `~(p' divides a)` by metis_tac[coprime_common_factor, NOT_PRIME_1, MULT_COMM] >> + `p' divides p` by metis_tac[P_EUCLIDES] >> + metis_tac[coprime_common_factor, NOT_PRIME_1] + ] +QED + +(* Idea: include converse of coprime_linear_mult. *) + +(* Theorem: coprime a b ==> + ((coprime p b /\ coprime q a) <=> coprime (p * a + q * b) (a * b)) *) +(* Proof: + If part: coprime p b /\ coprime q a ==> coprime (p * a + q * b) (a * b) + This is true by coprime_linear_mult. + Only-if: coprime (p * a + q * b) (a * b) ==> coprime p b /\ coprime q a + Let z = p * a + q * b. Consider a prime t. + For coprime p b. + If t divides p /\ t divides b, + Then t divides z by divides_linear + and t divides (a * b) by DIVIDES_MULTIPLE + so t = 1 by coprime_common_factor + This contradicts prime t by NOT_PRIME_1 + Thus coprime p b by coprime_by_prime_factor + For coprime q a. + If t divides q /\ t divides a, + Then t divides z by divides_linear + and t divides (a * b) by DIVIDES_MULTIPLE + so t = 1 by coprime_common_factor + This contradicts prime t by NOT_PRIME_1 + Thus coprime q a by coprime_by_prime_factor +*) +Theorem coprime_linear_mult_iff: + !a b p q. coprime a b ==> + ((coprime p b /\ coprime q a) <=> coprime (p * a + q * b) (a * b)) +Proof + rw_tac std_ss[EQ_IMP_THM] >- + simp[coprime_linear_mult] >- + (irule (coprime_by_prime_factor |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> + rpt strip_tac >> + `p' divides (p * a + q * b)` by metis_tac[divides_linear, MULT_COMM] >> + `p' divides (a * b)` by rw[DIVIDES_MULTIPLE] >> + metis_tac[coprime_common_factor, NOT_PRIME_1]) >> + irule (coprime_by_prime_factor |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> + rpt strip_tac >> + `p' divides (p * a + q * b)` by metis_tac[divides_linear, MULT_COMM] >> + `p' divides (a * b)` by metis_tac[DIVIDES_MULTIPLE, MULT_COMM] >> + metis_tac[coprime_common_factor, NOT_PRIME_1] +QED + +(* Idea: condition for a number to be coprime with prime power. *) + +(* Theorem: prime p /\ 0 < n ==> !q. coprime q (p ** n) <=> ~(p divides q) *) +(* Proof: + If part: prime p /\ 0 < n /\ coprime q (p ** n) ==> ~(p divides q) + By contradiction, suppose p divides q. + Note p divides (p ** n) by prime_divides_self_power, 0 < n + Thus p = 1 by coprime_common_factor + This contradicts p <> 1 by NOT_PRIME_1 + Only-if part: prime p /\ 0 < n /\ ~(p divides q) ==> coprime q (p ** n) + Note coprime q p by prime_not_divides_coprime, GCD_SYM + Thus coprime q (p ** n) by coprime_iff_coprime_exp, 0 < n +*) +Theorem coprime_prime_power: + !p n. prime p /\ 0 < n ==> !q. coprime q (p ** n) <=> ~(p divides q) +Proof + rw[EQ_IMP_THM] >- + metis_tac[prime_divides_self_power, coprime_common_factor, NOT_PRIME_1] >> + metis_tac[prime_not_divides_coprime, coprime_iff_coprime_exp, GCD_SYM] +QED + +(* Theorem: prime n ==> !m. 0 < m /\ m < n ==> coprime n m *) +(* Proof: + By contradiction. Let d = gcd n m, and d <> 1. + Since prime n, 0 < n by PRIME_POS + Thus d divides n, and d m divides by GCD_IS_GREATEST_COMMON_DIVISOR, n <> 0, m <> 0. + ==> d = n by prime_def, d <> 1. + ==> n divides m by d divides m + ==> n <= m by DIVIDES_LE + which contradicts m < n. +*) +val prime_coprime_all_lt = store_thm( + "prime_coprime_all_lt", + ``!n. prime n ==> !m. 0 < m /\ m < n ==> coprime n m``, + rpt strip_tac >> + spose_not_then strip_assume_tac >> + qabbrev_tac `d = gcd n m` >> + `0 < n` by rw[PRIME_POS] >> + `n <> 0 /\ m <> 0` by decide_tac >> + `d divides n /\ d divides m` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> + `d = n` by metis_tac[prime_def] >> + `n <= m` by rw[DIVIDES_LE] >> + decide_tac); + +(* Theorem: prime n /\ m < n ==> (!j. 0 < j /\ j <= m ==> coprime n j) *) +(* Proof: + Since m < n, all j < n. + Hence true by prime_coprime_all_lt +*) +val prime_coprime_all_less = store_thm( + "prime_coprime_all_less", + ``!m n. prime n /\ m < n ==> (!j. 0 < j /\ j <= m ==> coprime n j)``, + rpt strip_tac >> + `j < n` by decide_tac >> + rw[prime_coprime_all_lt]); + +(* Theorem: prime n <=> 1 < n /\ (!j. 0 < j /\ j < n ==> coprime n j)) *) +(* Proof: + If part: prime n ==> 1 < n /\ !j. 0 < j /\ j < n ==> coprime n j + (1) prime n ==> 1 < n by ONE_LT_PRIME + (2) prime n /\ 0 < j /\ j < n ==> coprime n j by prime_coprime_all_lt + Only-if part: !j. 0 < j /\ j < n ==> coprime n j ==> prime n + By contradiction, assume ~prime n. + Now, 1 < n /\ ~prime n + ==> ?p. prime p /\ p < n /\ p divides n by PRIME_FACTOR_PROPER + and prime p ==> 0 < p and 1 < p by PRIME_POS, ONE_LT_PRIME + Hence ~coprime p n by coprime_not_divides, 1 < p + But 0 < p < n ==> coprime n p by given implication + This is a contradiction by coprime_sym +*) +val prime_iff_coprime_all_lt = store_thm( + "prime_iff_coprime_all_lt", + ``!n. prime n <=> 1 < n /\ (!j. 0 < j /\ j < n ==> coprime n j)``, + rw[EQ_IMP_THM, ONE_LT_PRIME] >- + rw[prime_coprime_all_lt] >> + spose_not_then strip_assume_tac >> + `?p. prime p /\ p < n /\ p divides n` by rw[PRIME_FACTOR_PROPER] >> + `0 < p` by rw[PRIME_POS] >> + `1 < p` by rw[ONE_LT_PRIME] >> + metis_tac[coprime_not_divides, coprime_sym]); + +(* Theorem: prime n <=> (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n))) *) +(* Proof: + If part: prime n ==> (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n))) + Note 1 < n by ONE_LT_PRIME + By contradiction, suppose j divides n. + Then j = 1 or j = n by prime_def + This contradicts 1 < j /\ j < n. + Only-if part: (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n))) ==> prime n + This is to show: + !b. b divides n ==> b = 1 or b = n by prime_def + Since 1 < n, so n <> 0 by arithmetic + Thus b <= n by DIVIDES_LE + and b <> 0 by ZERO_DIVIDES + By contradiction, suppose b <> 1 and b <> n, but b divides n. + Then 1 < b /\ b < n by above + giving ~(b divides n) by implication + This contradicts with b divides n. +*) +val prime_iff_no_proper_factor = store_thm( + "prime_iff_no_proper_factor", + ``!n. prime n <=> (1 < n /\ (!j. 1 < j /\ j < n ==> ~(j divides n)))``, + rw_tac std_ss[EQ_IMP_THM] >- + rw[ONE_LT_PRIME] >- + metis_tac[prime_def, LESS_NOT_EQ] >> + rw[prime_def] >> + `b <= n` by rw[DIVIDES_LE] >> + `n <> 0` by decide_tac >> + `b <> 0` by metis_tac[ZERO_DIVIDES] >> + spose_not_then strip_assume_tac >> + `1 < b /\ b < n` by decide_tac >> + metis_tac[]); + +(* Theorem: FINITE s ==> !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s) *) +(* Proof: + By finite induction on s. + Base: coprime x (PROD_SET {}) + Note PROD_SET {} = 1 by PROD_SET_EMPTY + and coprime x 1 = T by GCD_1 + Step: !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s) ==> + e NOTIN s /\ x NOTIN e INSERT s /\ !z. z IN e INSERT s ==> coprime x z ==> + coprime x (PROD_SET (e INSERT s)) + Note coprime x e by IN_INSERT + and coprime x (PROD_SET s) by induction hypothesis + Thus coprime x (e * PROD_SET s) by coprime_product_coprime_sym + or coprime x PROD_SET (e INSERT s) by PROD_SET_INSERT +*) +val every_coprime_prod_set_coprime = store_thm( + "every_coprime_prod_set_coprime", + ``!s. FINITE s ==> !x. x NOTIN s /\ (!z. z IN s ==> coprime x z) ==> coprime x (PROD_SET s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_SET_EMPTY] >> + fs[] >> + rw[PROD_SET_INSERT, coprime_product_coprime_sym]); + +(* ------------------------------------------------------------------------- *) +(* GCD divisibility condition of Power Predecessors *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < t /\ m <= n ==> + (t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1)) *) +(* Proof: + Note !n. 1 <= t ** n by ONE_LE_EXP, 0 < t, [1] + + Claim: t ** (n - m) - 1 <= t ** n - 1, because: + Proof: Note n - m <= n always + so t ** (n - m) <= t ** n by EXP_BASE_LEQ_MONO_IMP, 0 < t + Now 1 <= t ** (n - m) and + 1 <= t ** n by [1] + Hence t ** (n - m) - 1 <= t ** n - 1. + + t ** (n - m) * (t ** m - 1) + t ** (n - m) - 1 + = (t ** (n - m) * t ** m - t ** (n - m)) + t ** (n - m) - 1 by LEFT_SUB_DISTRIB + = (t ** (n - m + m) - t ** (n - m)) + t ** (n - m) - 1 by EXP_ADD + = (t ** n - t ** (n - m)) + t ** (n - m) - 1 by SUB_ADD, m <= n + = (t ** n - (t ** (n - m) - 1 + 1)) + t ** (n - m) - 1 by SUB_ADD, 1 <= t ** (n - m) + = (t ** n - (1 + (t ** (n - m) - 1))) + t ** (n - m) - 1 by ADD_COMM + = (t ** n - 1 - (t ** (n - m) - 1)) + t ** (n - m) - 1 by SUB_PLUS, no condition + = t ** n - 1 by SUB_ADD, t ** (n - m) - 1 <= t ** n - 1 +*) +val power_predecessor_division_eqn = store_thm( + "power_predecessor_division_eqn", + ``!t m n. 0 < t /\ m <= n ==> + (t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1))``, + rpt strip_tac >> + `1 <= t ** n /\ 1 <= t ** (n - m)` by rw[ONE_LE_EXP] >> + `n - m <= n` by decide_tac >> + `t ** (n - m) <= t ** n` by rw[EXP_BASE_LEQ_MONO_IMP] >> + `t ** (n - m) - 1 <= t ** n - 1` by decide_tac >> + qabbrev_tac `z = t ** (n - m) - 1` >> + `t ** (n - m) * (t ** m - 1) + z = + t ** (n - m) * t ** m - t ** (n - m) + z` by decide_tac >> + `_ = t ** (n - m + m) - t ** (n - m) + z` by rw_tac std_ss[EXP_ADD] >> + `_ = t ** n - t ** (n - m) + z` by rw_tac std_ss[SUB_ADD] >> + `_ = t ** n - (z + 1) + z` by rw_tac std_ss[SUB_ADD, Abbr`z`] >> + `_ = t ** n + z - (z + 1)` by decide_tac >> + `_ = t ** n - 1` by decide_tac >> + decide_tac); + +(* This shows the pattern: + 1000000 so 9999999999 = 1000000 * 9999 + 999999 + ------------ or (b ** 10 - 1) = b ** 6 * (b ** 4 - 1) + (b ** 6 - 1) + 9999 | 9999999999 where b = 10. + 9999 + ---------- + 999999 +*) + +(* Theorem: 0 < t /\ m <= n ==> + (t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1) *) +(* Proof: by power_predecessor_division_eqn *) +val power_predecessor_division_alt = store_thm( + "power_predecessor_division_alt", + ``!t m n. 0 < t /\ m <= n ==> + (t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1)``, + rpt strip_tac >> + imp_res_tac power_predecessor_division_eqn >> + fs[]); + +(* Theorem: m < n ==> (gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** m - 1)) (t ** (n - m) - 1)) *) +(* Proof: + Case t = 0, + If n = 0, t ** 0 = 1 by ZERO_EXP + LHS = gcd 0 x = 0 by GCD_0L + = gcd 0 y = RHS by ZERO_EXP + If n <> 0, 0 ** n = 0 by ZERO_EXP + LHS = gcd (0 - 1) x + = gcd 0 x = 0 by GCD_0L + = gcd 0 y = RHS by ZERO_EXP + Case t <> 0, + Note t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1) + by power_predecessor_division_eqn + so t ** (n - m) * (t ** m - 1) <= t ** n - 1 by above, [1] + and t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1, [2] + gcd (t ** n - 1) (t ** m - 1) + = gcd (t ** m - 1) (t ** n - 1) by GCD_SYM + = gcd (t ** m - 1) ((t ** n - 1) - t ** (n - m) * (t ** m - 1)) + by GCD_SUB_MULTIPLE, [1] + = gcd (t ** m - 1)) (t ** (n - m) - 1) by [2] +*) +val power_predecessor_gcd_reduction = store_thm( + "power_predecessor_gcd_reduction", + ``!t n m. m <= n ==> (gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** m - 1)) (t ** (n - m) - 1))``, + rpt strip_tac >> + Cases_on `t = 0` >- + rw[ZERO_EXP] >> + `t ** n - 1 = t ** (n - m) * (t ** m - 1) + (t ** (n - m) - 1)` by rw[power_predecessor_division_eqn] >> + `t ** n - 1 - t ** (n - m) * (t ** m - 1) = t ** (n - m) - 1` by fs[] >> + `gcd (t ** n - 1) (t ** m - 1) = gcd (t ** m - 1) (t ** n - 1)` by rw_tac std_ss[GCD_SYM] >> + `_ = gcd (t ** m - 1) ((t ** n - 1) - t ** (n - m) * (t ** m - 1))` by rw_tac std_ss[GCD_SUB_MULTIPLE] >> + rw_tac std_ss[]); + +(* Theorem: gcd (t ** n - 1) (t ** m - 1) = t ** (gcd n m) - 1 *) +(* Proof: + By complete induction on (n + m): + Induction hypothesis: !m'. m' < n + m ==> + !n m. (m' = n + m) ==> (gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1) + Idea: if 0 < m, n < n + m. Put last n = m, m = n - m. That is m' = m + (n - m) = n. + Also if 0 < n, m < n + m. Put last n = n, m = m - n. That is m' = n + (m - n) = m. + + Thus to apply induction hypothesis, need 0 < n or 0 < m. + So take care of these special cases first. + + Case: n = 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 + LHS = gcd (t ** 0 - 1) (t ** m - 1) + = gcd 0 (t ** m - 1) by EXP + = t ** m - 1 by GCD_0L + = t ** (gcd 0 m) - 1 = RHS by GCD_0L + Case: m = 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 + LHS = gcd (t ** n - 1) (t ** 0 - 1) + = gcd (t ** n - 1) 0 by EXP + = t ** n - 1 by GCD_0R + = t ** (gcd n 0) - 1 = RHS by GCD_0R + + Case: m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 + That is, 0 < n, and 0 < m + also n < n + m, and m < n + m by arithmetic + + Use trichotomy of numbers: by LESS_LESS_CASES + Case: n = m /\ m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 + LHS = gcd (t ** m - 1) (t ** m - 1) + = t ** m - 1 by GCD_REF + = t ** (gcd m m) - 1 = RHS by GCD_REF + + Case: m < n /\ m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 + Since n < n + m by 0 < m + and m + (n - m) = (n - m) + m by ADD_COMM + = n by SUB_ADD, m <= n + gcd (t ** n - 1) (t ** m - 1) + = gcd ((t ** m - 1)) (t ** (n - m) - 1) by power_predecessor_gcd_reduction + = t ** gcd m (n - m) - 1 by induction hypothesis, m + (n - m) = n + = t ** gcd m n - 1 by GCD_SUB_R, m <= n + = t ** gcd n m - 1 by GCD_SYM + + Case: n < m /\ m <> 0 /\ n <> 0 ==> gcd (t ** n - 1) (t ** m - 1) = t ** gcd n m - 1 + Since m < n + m by 0 < n + and n + (m - n) = (m - n) + n by ADD_COMM + = m by SUB_ADD, n <= m + gcd (t ** n - 1) (t ** m - 1) + = gcd (t ** m - 1) (t ** n - 1) by GCD_SYM + = gcd ((t ** n - 1)) (t ** (m - n) - 1) by power_predecessor_gcd_reduction + = t ** gcd n (m - n) - 1 by induction hypothesis, n + (m - n) = m + = t ** gcd n m by GCD_SUB_R, n <= m +*) +val power_predecessor_gcd_identity = store_thm( + "power_predecessor_gcd_identity", + ``!t n m. gcd (t ** n - 1) (t ** m - 1) = t ** (gcd n m) - 1``, + rpt strip_tac >> + completeInduct_on `n + m` >> + rpt strip_tac >> + Cases_on `n = 0` >- + rw[EXP] >> + Cases_on `m = 0` >- + rw[EXP] >> + `(n = m) \/ (m < n) \/ (n < m)` by metis_tac[LESS_LESS_CASES] >- + rw[GCD_REF] >- + (`0 < m /\ n < n + m` by decide_tac >> + `m <= n` by decide_tac >> + `m + (n - m) = n` by metis_tac[SUB_ADD, ADD_COMM] >> + `gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** m - 1)) (t ** (n - m) - 1)` by rw[power_predecessor_gcd_reduction] >> + `_ = t ** gcd m (n - m) - 1` by metis_tac[] >> + metis_tac[GCD_SUB_R, GCD_SYM]) >> + `0 < n /\ m < n + m` by decide_tac >> + `n <= m` by decide_tac >> + `n + (m - n) = m` by metis_tac[SUB_ADD, ADD_COMM] >> + `gcd (t ** n - 1) (t ** m - 1) = gcd ((t ** n - 1)) (t ** (m - n) - 1)` by rw[power_predecessor_gcd_reduction, GCD_SYM] >> + `_ = t ** gcd n (m - n) - 1` by metis_tac[] >> + metis_tac[GCD_SUB_R]); + +(* Above is the formal proof of the following pattern: + For any base + gcd(999999,9999) = gcd(6 9s, 4 9s) = gcd(6,4) 9s = 2 9s = 99 + or 999999 MOD 9999 = (6 9s) MOD (4 9s) = 2 9s = 99 + Thus in general, + (m 9s) MOD (n 9s) = (m MOD n) 9s + Repeating the use of Euclidean algorithm then gives: + gcd (m 9s, n 9s) = (gcd m n) 9s + +Reference: A Mathematical Tapestry (by Jean Pedersen and Peter Hilton) +Chapter 4: A number-theory thread -- Folding numbers, a number trick, and some tidbits. +*) + +(* Theorem: 1 < t ==> ((t ** n - 1) divides (t ** m - 1) <=> n divides m) *) +(* Proof: + (t ** n - 1) divides (t ** m - 1) + <=> gcd (t ** n - 1) (t ** m - 1) = t ** n - 1 by divides_iff_gcd_fix + <=> t ** (gcd n m) - 1 = t ** n - 1 by power_predecessor_gcd_identity + <=> t ** (gcd n m) = t ** n by PRE_SUB1, INV_PRE_EQ, EXP_POS, 0 < t + <=> gcd n m = n by EXP_BASE_INJECTIVE, 1 < t + <=> n divides m by divides_iff_gcd_fix +*) +val power_predecessor_divisibility = store_thm( + "power_predecessor_divisibility", + ``!t n m. 1 < t ==> ((t ** n - 1) divides (t ** m - 1) <=> n divides m)``, + rpt strip_tac >> + `0 < t` by decide_tac >> + `!n. 0 < t ** n` by rw[EXP_POS] >> + `!x y. 0 < x /\ 0 < y ==> ((x - 1 = y - 1) <=> (x = y))` by decide_tac >> + `(t ** n - 1) divides (t ** m - 1) <=> ((gcd (t ** n - 1) (t ** m - 1) = t ** n - 1))` by rw[divides_iff_gcd_fix] >> + `_ = (t ** (gcd n m) - 1 = t ** n - 1)` by rw[power_predecessor_gcd_identity] >> + `_ = (t ** (gcd n m) = t ** n)` by rw[] >> + `_ = (gcd n m = n)` by rw[EXP_BASE_INJECTIVE] >> + rw[divides_iff_gcd_fix]); + +(* Theorem: t - 1 divides t ** n - 1 *) +(* Proof: + If t = 0, + Then t - 1 = 0 by integer subtraction + and t ** n - 1 = 0 by ZERO_EXP, either case of n. + Thus 0 divides 0 by ZERO_DIVIDES + If t = 1, + Then t - 1 = 0 by arithmetic + and t ** n - 1 = 0 by EXP_1 + Thus 0 divides 0 by ZERO_DIVIDES + Otherwise, 1 < t + and 1 divides n by ONE_DIVIDES_ALL + ==> t ** 1 - 1 divides t ** n - 1 by power_predecessor_divisibility + or t - 1 divides t ** n - 1 by EXP_1 +*) +Theorem power_predecessor_divisor: + !t n. t - 1 divides t ** n - 1 +Proof + rpt strip_tac >> + Cases_on `t = 0` >- + simp[ZERO_EXP] >> + Cases_on `t = 1` >- + simp[] >> + `1 < t` by decide_tac >> + metis_tac[power_predecessor_divisibility, EXP_1, ONE_DIVIDES_ALL] +QED + +(* Overload power predecessor *) +Overload tops = “\b:num n. b ** n - 1” + +(* + power_predecessor_division_eqn + |- !t m n. 0 < t /\ m <= n ==> tops t n = t ** (n - m) * tops t m + tops t (n - m) + power_predecessor_division_alt + |- !t m n. 0 < t /\ m <= n ==> tops t n - t ** (n - m) * tops t m = tops t (n - m) + power_predecessor_gcd_reduction + |- !t n m. m <= n ==> (gcd (tops t n) (tops t m) = gcd (tops t m) (tops t (n - m))) + power_predecessor_gcd_identity + |- !t n m. gcd (tops t n) (tops t m) = tops t (gcd n m) + power_predecessor_divisibility + |- !t n m. 1 < t ==> (tops t n divides tops t m <=> n divides m) + power_predecessor_divisor + |- !t n. t - 1 divides tops t n +*) + +(* Overload power predecessor base 10 *) +val _ = overload_on("nines", ``\n. tops 10 n``); + +(* Obtain corollaries *) + +val nines_division_eqn = save_thm("nines_division_eqn", + power_predecessor_division_eqn |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); +val nines_division_alt = save_thm("nines_division_alt", + power_predecessor_division_alt |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); +val nines_gcd_reduction = save_thm("nines_gcd_reduction", + power_predecessor_gcd_reduction |> ISPEC ``10``); +val nines_gcd_identity = save_thm("nines_gcd_identity", + power_predecessor_gcd_identity |> ISPEC ``10``); +val nines_divisibility = save_thm("nines_divisibility", + power_predecessor_divisibility |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); +val nines_divisor = save_thm("nines_divisor", + power_predecessor_divisor |> ISPEC ``10`` |> SIMP_RULE (srw_ss()) []); +(* +val nines_division_eqn = + |- !m n. m <= n ==> nines n = 10 ** (n - m) * nines m + nines (n - m): thm +val nines_division_alt = + |- !m n. m <= n ==> nines n - 10 ** (n - m) * nines m = nines (n - m): thm +val nines_gcd_reduction = + |- !n m. m <= n ==> gcd (nines n) (nines m) = gcd (nines m) (nines (n - m)): thm +val nines_gcd_identity = |- !n m. gcd (nines n) (nines m) = nines (gcd n m): thm +val nines_divisibility = |- !n m. nines n divides nines m <=> n divides m: thm +val nines_divisor = |- !n. 9 divides nines n: thm +*) + +(* ------------------------------------------------------------------------- *) +(* GCD involving Powers *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: prime m /\ prime n /\ m divides (n ** k) ==> (m = n) *) +(* Proof: + By induction on k. + Base: m divides n ** 0 ==> (m = n) + Since n ** 0 = 1 by EXP + and m divides 1 ==> m = 1 by DIVIDES_ONE + This contradicts 1 < m by ONE_LT_PRIME + Step: m divides n ** k ==> (m = n) ==> m divides n ** SUC k ==> (m = n) + Since n ** SUC k = n * n ** k by EXP + Also m divides n \/ m divides n ** k by P_EUCLIDES + If m divides n, then m = n by prime_divides_only_self + If m divides n ** k, then m = n by induction hypothesis +*) +val prime_divides_prime_power = store_thm( + "prime_divides_prime_power", + ``!m n k. prime m /\ prime n /\ m divides (n ** k) ==> (m = n)``, + rpt strip_tac >> + Induct_on `k` >| [ + rpt strip_tac >> + `1 < m` by rw[ONE_LT_PRIME] >> + `m = 1` by metis_tac[EXP, DIVIDES_ONE] >> + decide_tac, + metis_tac[EXP, P_EUCLIDES, prime_divides_only_self] + ]); + +(* This is better than FACTOR_OUT_PRIME *) + +(* Theorem: 0 < n /\ prime p ==> ?q m. (n = (p ** m) * q) /\ coprime p q *) +(* Proof: + If p divides n, + Then ?m. 0 < m /\ p ** m divides n /\ + !k. coprime (p ** k) (n DIV p ** m) by FACTOR_OUT_PRIME + Let q = n DIV (p ** m). + Note 0 < p by PRIME_POS + so 0 < p ** m by EXP_POS, 0 < p + Take this q and m, + Then n = (p ** m) * q by DIVIDES_EQN_COMM + and coprime p q by taking k = 1, EXP_1 + + If ~(p divides n), + Then coprime p n by prime_not_divides_coprime + Let q = n, m = 0. + Then n = 1 * q by EXP, MULT_LEFT_1 + and coprime p q. +*) +val prime_power_factor = store_thm( + "prime_power_factor", + ``!n p. 0 < n /\ prime p ==> ?q m. (n = (p ** m) * q) /\ coprime p q``, + rpt strip_tac >> + Cases_on `p divides n` >| [ + `?m. 0 < m /\ p ** m divides n /\ !k. coprime (p ** k) (n DIV p ** m)` by rw[FACTOR_OUT_PRIME] >> + qabbrev_tac `q = n DIV (p ** m)` >> + `0 < p` by rw[PRIME_POS] >> + `0 < p ** m` by rw[EXP_POS] >> + metis_tac[DIVIDES_EQN_COMM, EXP_1], + `coprime p n` by rw[prime_not_divides_coprime] >> + metis_tac[EXP, MULT_LEFT_1] + ]); + +(* Even this simple theorem is quite difficult to prove, why? *) +(* Because this needs a typical detective-style proof! *) + +(* Theorem: prime p /\ a divides (p ** n) ==> ?j. j <= n /\ (a = p ** j) *) +(* Proof: + Note 0 < p by PRIME_POS + so 0 < p ** n by EXP_POS + Thus 0 < a by ZERO_DIVIDES + ==> ?q m. (a = (p ** m) * q) /\ coprime p q by prime_power_factor + + Claim: q = 1 + Proof: By contradiction, suppose q <> 1. + Then ?t. prime t /\ t divides q by PRIME_FACTOR, q <> 1 + Now q divides a by divides_def + so t divides (p ** n) by DIVIDES_TRANS + ==> t = p by prime_divides_prime_power + But gcd t q = t by divides_iff_gcd_fix + or gcd p q = p by t = p + Yet p <> 1 by NOT_PRIME_1 + so this contradicts coprime p q. + + Thus a = p ** m by q = 1, Claim. + Note p ** m <= p ** n by DIVIDES_LE, 0 < p + and 1 < p by ONE_LT_PRIME + ==> m <= n by EXP_BASE_LE_MONO, 1 < p + Take j = m, and the result follows. +*) +val prime_power_divisor = store_thm( + "prime_power_divisor", + ``!p n a. prime p /\ a divides (p ** n) ==> ?j. j <= n /\ (a = p ** j)``, + rpt strip_tac >> + `0 < p` by rw[PRIME_POS] >> + `0 < p ** n` by rw[EXP_POS] >> + `0 < a` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + `?q m. (a = (p ** m) * q) /\ coprime p q` by rw[prime_power_factor] >> + `q = 1` by + (spose_not_then strip_assume_tac >> + `?t. prime t /\ t divides q` by rw[PRIME_FACTOR] >> + `q divides a` by metis_tac[divides_def] >> + `t divides (p ** n)` by metis_tac[DIVIDES_TRANS] >> + `t = p` by metis_tac[prime_divides_prime_power] >> + `gcd t q = t` by rw[GSYM divides_iff_gcd_fix] >> + metis_tac[NOT_PRIME_1]) >> + `a = p ** m` by rw[] >> + metis_tac[DIVIDES_LE, EXP_BASE_LE_MONO, ONE_LT_PRIME]); + +(* Theorem: prime p /\ prime q ==> + !m n. 0 < m /\ (p ** m = q ** n) ==> (p = q) /\ (m = n) *) +(* Proof: + First goal: p = q. + Since p divides p by DIVIDES_REFL + ==> p divides p ** m by divides_exp, 0 < m. + so p divides q ** n by given, p ** m = q ** n + Hence p = q by prime_divides_prime_power + Second goal: m = n. + Note p = q by first goal. + Since 1 < p by ONE_LT_PRIME + Hence m = n by EXP_BASE_INJECTIVE, 1 < p +*) +val prime_powers_eq = store_thm( + "prime_powers_eq", + ``!p q. prime p /\ prime q ==> + !m n. 0 < m /\ (p ** m = q ** n) ==> (p = q) /\ (m = n)``, + ntac 6 strip_tac >> + conj_asm1_tac >- + metis_tac[divides_exp, prime_divides_prime_power, DIVIDES_REFL] >> + metis_tac[EXP_BASE_INJECTIVE, ONE_LT_PRIME]); + +(* Theorem: prime p /\ prime q /\ p <> q ==> !m n. coprime (p ** m) (q ** n) *) +(* Proof: + Let d = gcd (p ** m) (q ** n). + By contradiction, d <> 1. + Then d divides (p ** m) /\ d divides (q ** n) by GCD_PROPERTY + ==> ?j. j <= m /\ (d = p ** j) by prime_power_divisor, prime p + and ?k. k <= n /\ (d = q ** k) by prime_power_divisor, prime q + Note j <> 0 /\ k <> 0 by EXP_0 + or 0 < j /\ 0 < k by arithmetic + ==> p = q, which contradicts p <> q by prime_powers_eq +*) +val prime_powers_coprime = store_thm( + "prime_powers_coprime", + ``!p q. prime p /\ prime q /\ p <> q ==> !m n. coprime (p ** m) (q ** n)``, + spose_not_then strip_assume_tac >> + qabbrev_tac `d = gcd (p ** m) (q ** n)` >> + `d divides (p ** m) /\ d divides (q ** n)` by metis_tac[GCD_PROPERTY] >> + metis_tac[prime_power_divisor, prime_powers_eq, EXP_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: prime p /\ prime q ==> !m n. 0 < m ==> ((p ** m divides q ** n) <=> (p = q) /\ (m <= n)) *) +(* Proof: + If part: p ** m divides q ** n ==> (p = q) /\ m <= n + Note p divides (p ** m) by prime_divides_self_power, 0 < m + so p divides (q ** n) by DIVIDES_TRANS + Thus p = q by prime_divides_prime_power + Note 1 < p by ONE_LT_PRIME + Thus m <= n by power_divides_iff + Only-if part: (p = q) /\ m <= n ==> p ** m divides q ** n + Note 1 < p by ONE_LT_PRIME + Thus p ** m divides q ** n by power_divides_iff +*) +val prime_powers_divide = store_thm( + "prime_powers_divide", + ``!p q. prime p /\ prime q ==> !m n. 0 < m ==> ((p ** m divides q ** n) <=> (p = q) /\ (m <= n))``, + metis_tac[ONE_LT_PRIME, divides_self_power, prime_divides_prime_power, power_divides_iff, DIVIDES_TRANS]); + +(* Theorem: prime p /\ q divides (p ** n) ==> (q = 1) \/ (p divides q) *) +(* Proof: + By contradiction, suppose q <> 1 /\ ~(p divides q). + Note ?j. j <= n /\ (q = p ** j) by prime_power_divisor + and 0 < j by EXP_0, q <> 1 + then p divides q by prime_divides_self_power, 0 < j + This contradicts ~(p divides q). +*) +Theorem PRIME_EXP_FACTOR: + !p q n. prime p /\ q divides (p ** n) ==> (q = 1) \/ (p divides q) +Proof + spose_not_then strip_assume_tac >> + `?j. j <= n /\ (q = p ** j)` by rw[prime_power_divisor] >> + `0 < j` by fs[] >> + metis_tac[prime_divides_self_power] +QED + +(* Theorem: gcd (b ** m) (b ** n) = b ** (MIN m n) *) +(* Proof: + If m = n, + LHS = gcd (b ** n) (b ** n) + = b ** n by GCD_REF + RHS = b ** (MIN n n) + = b ** n by MIN_IDEM + If m < n, + b ** n = b ** (n - m + m) by arithmetic + = b ** (n - m) * b ** m by EXP_ADD + so (b ** m) divides (b ** n) by divides_def + or gcd (b ** m) (b ** n) + = b ** m by divides_iff_gcd_fix + = b ** (MIN m n) by MIN_DEF + If ~(m < n), n < m. + Similar argument as m < n, with m n exchanged, use GCD_SYM. +*) +val gcd_powers = store_thm( + "gcd_powers", + ``!b m n. gcd (b ** m) (b ** n) = b ** (MIN m n)``, + rpt strip_tac >> + Cases_on `m = n` >- + rw[] >> + Cases_on `m < n` >| [ + `b ** n = b ** (n - m + m)` by rw[] >> + `_ = b ** (n - m) * b ** m` by rw[EXP_ADD] >> + `(b ** m) divides (b ** n)` by metis_tac[divides_def] >> + metis_tac[divides_iff_gcd_fix, MIN_DEF], + `n < m` by decide_tac >> + `b ** m = b ** (m - n + n)` by rw[] >> + `_ = b ** (m - n) * b ** n` by rw[EXP_ADD] >> + `(b ** n) divides (b ** m)` by metis_tac[divides_def] >> + metis_tac[divides_iff_gcd_fix, GCD_SYM, MIN_DEF] + ]); + +(* Theorem: lcm (b ** m) (b ** n) = b ** (MAX m n) *) +(* Proof: + If m = n, + LHS = lcm (b ** n) (b ** n) + = b ** n by LCM_REF + RHS = b ** (MAX n n) + = b ** n by MAX_IDEM + If m < n, + b ** n = b ** (n - m + m) by arithmetic + = b ** (n - m) * b ** m by EXP_ADD + so (b ** m) divides (b ** n) by divides_def + or lcm (b ** m) (b ** n) + = b ** n by divides_iff_lcm_fix + = b ** (MAX m n) by MAX_DEF + If ~(m < n), n < m. + Similar argument as m < n, with m n exchanged, use LCM_COMM. +*) +val lcm_powers = store_thm( + "lcm_powers", + ``!b m n. lcm (b ** m) (b ** n) = b ** (MAX m n)``, + rpt strip_tac >> + Cases_on `m = n` >- + rw[LCM_REF] >> + Cases_on `m < n` >| [ + `b ** n = b ** (n - m + m)` by rw[] >> + `_ = b ** (n - m) * b ** m` by rw[EXP_ADD] >> + `(b ** m) divides (b ** n)` by metis_tac[divides_def] >> + metis_tac[divides_iff_lcm_fix, MAX_DEF], + `n < m` by decide_tac >> + `b ** m = b ** (m - n + n)` by rw[] >> + `_ = b ** (m - n) * b ** n` by rw[EXP_ADD] >> + `(b ** n) divides (b ** m)` by metis_tac[divides_def] >> + metis_tac[divides_iff_lcm_fix, LCM_COMM, MAX_DEF] + ]); + +(* Theorem: 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m - 1) *) +(* Proof: + If m = n, + coprime (b ** n) (b ** n - 1) + <=> T by coprime_PRE + + Claim: !j. j < m ==> coprime (b ** j) (b ** m - 1) + Proof: b ** m + = b ** (m - j + j) by SUB_ADD + = b ** (m - j) * b ** j by EXP_ADD + Thus (b ** j) divides (b ** m) by divides_def + Now 0 < b ** m by EXP_POS + so coprime (b ** j) (PRE (b ** m)) by divides_imp_coprime_with_predecessor + or coprime (b ** j) (b ** m - 1) by PRE_SUB1 + + Given 0 < m, + b ** n + = b ** ((n DIV m) * m + n MOD m) by DIVISION + = b ** (m * (n DIV m) + n MOD m) by MULT_COMM + = b ** (m * (n DIV m)) * b ** (n MOD m) by EXP_ADD + = (b ** m) ** (n DIV m) * b ** (n MOD m) by EXP_EXP_MULT + Let z = b ** m, + Then b ** n = z ** (n DIV m) * b ** (n MOD m) + and 0 < z by EXP_POS + Since coprime z (z - 1) by coprime_PRE + ==> coprime (z ** (n DIV m)) (z - 1) by coprime_exp + gcd (b ** n) (b ** m - 1) + = gcd (z ** (n DIV m) * b ** (n MOD m)) (z - 1) + = gcd (b ** (n MOD m)) (z - 1) by GCD_SYM, GCD_CANCEL_MULT + Now (n MOD m) < m by MOD_LESS + so apply the claim to deduce the result. +*) +val coprime_power_and_power_predecessor = store_thm( + "coprime_power_and_power_predecessor", + ``!b m n. 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m - 1)``, + rpt strip_tac >> + `0 < b ** n /\ 0 < b ** m` by rw[EXP_POS] >> + Cases_on `m = n` >- + rw[coprime_PRE] >> + `!j. j < m ==> coprime (b ** j) (b ** m - 1)` by + (rpt strip_tac >> + `b ** m = b ** (m - j + j)` by rw[] >> + `_ = b ** (m - j) * b ** j` by rw[EXP_ADD] >> + `(b ** j) divides (b ** m)` by metis_tac[divides_def] >> + metis_tac[divides_imp_coprime_with_predecessor, PRE_SUB1]) >> + `b ** n = b ** ((n DIV m) * m + n MOD m)` by rw[GSYM DIVISION] >> + `_ = b ** (m * (n DIV m) + n MOD m)` by rw[MULT_COMM] >> + `_ = b ** (m * (n DIV m)) * b ** (n MOD m)` by rw[EXP_ADD] >> + `_ = (b ** m) ** (n DIV m) * b ** (n MOD m)` by rw[EXP_EXP_MULT] >> + qabbrev_tac `z = b ** m` >> + `coprime z (z - 1)` by rw[coprime_PRE] >> + `coprime (z ** (n DIV m)) (z - 1)` by rw[coprime_exp] >> + metis_tac[GCD_SYM, GCD_CANCEL_MULT, MOD_LESS]); + +(* Any counter-example? Theorem proved, no counter-example! *) +(* This is a most unexpected theorem. + At first I thought it only holds for prime base b, + but in HOL4 using the EVAL function shows it seems to hold for any base b. + As for the proof, I don't have a clue initially. + I try this idea: + For a prime base b, most likely ODD b, then ODD (b ** n) and ODD (b ** m). + But then EVEN (b ** m - 1), maybe ODD and EVEN will give coprime. + If base b is EVEN, then EVEN (b ** n) but ODD (b ** m - 1), so this can work. + However, in general ODD and EVEN do not give coprime: gcd 6 9 = 3. + Of course, if ODD and EVEN arise from powers of same base, like this theorem, then true! + Actually this follows from divides_imp_coprime_with_predecessor, sort of. + This success inspires the following theorem. +*) + +(* Theorem: 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m + 1) *) +(* Proof: + If m = n, + coprime (b ** n) (b ** n + 1) + <=> T by coprime_SUC + + Claim: !j. j < m ==> coprime (b ** j) (b ** m + 1) + Proof: b ** m + = b ** (m - j + j) by SUB_ADD + = b ** (m - j) * b ** j by EXP_ADD + Thus (b ** j) divides (b ** m) by divides_def + Now 0 < b ** m by EXP_POS + so coprime (b ** j) (SUC (b ** m)) by divides_imp_coprime_with_successor + or coprime (b ** j) (b ** m + 1) by ADD1 + + Given 0 < m, + b ** n + = b ** ((n DIV m) * m + n MOD m) by DIVISION + = b ** (m * (n DIV m) + n MOD m) by MULT_COMM + = b ** (m * (n DIV m)) * b ** (n MOD m) by EXP_ADD + = (b ** m) ** (n DIV m) * b ** (n MOD m) by EXP_EXP_MULT + Let z = b ** m, + Then b ** n = z ** (n DIV m) * b ** (n MOD m) + and 0 < z by EXP_POS + Since coprime z (z + 1) by coprime_SUC + ==> coprime (z ** (n DIV m)) (z + 1) by coprime_exp + gcd (b ** n) (b ** m + 1) + = gcd (z ** (n DIV m) * b ** (n MOD m)) (z + 1) + = gcd (b ** (n MOD m)) (z + 1) by GCD_SYM, GCD_CANCEL_MULT + Now (n MOD m) < m by MOD_LESS + so apply the claim to deduce the result. +*) +val coprime_power_and_power_successor = store_thm( + "coprime_power_and_power_successor", + ``!b m n. 0 < b /\ 0 < m ==> coprime (b ** n) (b ** m + 1)``, + rpt strip_tac >> + `0 < b ** n /\ 0 < b ** m` by rw[EXP_POS] >> + Cases_on `m = n` >- + rw[coprime_SUC] >> + `!j. j < m ==> coprime (b ** j) (b ** m + 1)` by + (rpt strip_tac >> + `b ** m = b ** (m - j + j)` by rw[] >> + `_ = b ** (m - j) * b ** j` by rw[EXP_ADD] >> + `(b ** j) divides (b ** m)` by metis_tac[divides_def] >> + metis_tac[divides_imp_coprime_with_successor, ADD1]) >> + `b ** n = b ** ((n DIV m) * m + n MOD m)` by rw[GSYM DIVISION] >> + `_ = b ** (m * (n DIV m) + n MOD m)` by rw[MULT_COMM] >> + `_ = b ** (m * (n DIV m)) * b ** (n MOD m)` by rw[EXP_ADD] >> + `_ = (b ** m) ** (n DIV m) * b ** (n MOD m)` by rw[EXP_EXP_MULT] >> + qabbrev_tac `z = b ** m` >> + `coprime z (z + 1)` by rw[coprime_SUC] >> + `coprime (z ** (n DIV m)) (z + 1)` by rw[coprime_exp] >> + metis_tac[GCD_SYM, GCD_CANCEL_MULT, MOD_LESS]); + +(* Note: +> type_of ``prime``; +val it = ":num -> bool": hol_type + +Thus prime is also a set, or prime = {p | prime p} +*) + +(* Theorem: p IN prime <=> prime p *) +(* Proof: by IN_DEF *) +val in_prime = store_thm( + "in_prime", + ``!p. p IN prime <=> prime p``, + rw[IN_DEF]); + +(* Theorem: PROD_SET {x} = x *) +(* Proof: + Since FINITE {x} by FINITE_SING + PROD_SET {x} + = PROD_SET (x INSERT {}) by SING_INSERT + = x * PROD_SET {} by PROD_SET_THM + = x by PROD_SET_EMPTY +*) +val PROD_SET_SING = store_thm( + "PROD_SET_SING", + ``!x. PROD_SET {x} = x``, + rw[PROD_SET_THM, FINITE_SING]); + +(* Theorem: FINITE s /\ 0 NOTIN s ==> 0 < PROD_SET s *) +(* Proof: + By FINITE_INDUCT on s. + Base case: 0 NOTIN {} ==> 0 < PROD_SET {} + Since PROD_SET {} = 1 by PROD_SET_THM + Hence true. + Step case: 0 NOTIN s ==> 0 < PROD_SET s ==> + e NOTIN s /\ 0 NOTIN e INSERT s ==> 0 < PROD_SET (e INSERT s) + PROD_SET (e INSERT s) + = e * PROD_SET (s DELETE e) by PROD_SET_THM + = e * PROD_SET s by DELETE_NON_ELEMENT + But e IN e INSERT s by COMPONENT + Hence e <> 0, or 0 < e by implication + and !x. x IN s ==> x IN (e INSERT s) by IN_INSERT + Thus 0 < PROD_SET s by induction hypothesis + Henec 0 < e * PROD_SET s by ZERO_LESS_MULT +*) +val PROD_SET_NONZERO = store_thm( + "PROD_SET_NONZERO", + ``!s. FINITE s /\ 0 NOTIN s ==> 0 < PROD_SET s``, + `!s. FINITE s ==> 0 NOTIN s ==> 0 < PROD_SET s` suffices_by rw[] >> + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + rw[PROD_SET_THM] >> + fs[] >> + `0 < e` by decide_tac >> + `PROD_SET (e INSERT s) = e * PROD_SET (s DELETE e)` by rw[PROD_SET_THM] >> + `_ = e * PROD_SET s` by metis_tac[DELETE_NON_ELEMENT] >> + rw[ZERO_LESS_MULT]); + +(* Theorem: FINITE s /\ s <> {} /\ 0 NOTIN s ==> + !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s) *) +(* Proof: + By FINITE_INDUCT on s. + Base case: {} <> {} ==> PROD_SET {} < PROD_SET (IMAGE f {}) + True since {} <> {} is false. + Step case: s <> {} /\ 0 NOTIN s ==> !f. INJ f s univ(:num) ==> PROD_SET s < PROD_SET (IMAGE f s) ==> + e NOTIN s /\ e INSERT s <> {} /\ 0 NOTIN e INSERT s /\ INJ f (e INSERT s) univ(:num) ==> + PROD_SET (e INSERT s) < PROD_SET (IMAGE f (e INSERT s)) + Note INJ f (e INSERT s) univ(:num) + ==> INJ f s univ(:num) /\ + !y. y IN s /\ (f e = f y) ==> (e = y) by INJ_INSERT + First, + PROD_SET (e INSERT s) + = e * PROD_SET (s DELETE e) by PROD_SET_THM + = e * PROD_SET s by DELETE_NON_ELEMENT + Next, + FINITE (IMAGE f s) by IMAGE_FINITE + f e NOTIN IMAGE f s by IN_IMAGE, e NOTIN s + PROD_SET (IMAGE f (e INSERT s)) + = f e * PROD_SET (IMAGE f s) by PROD_SET_IMAGE_REDUCTION + + If s = {}, + to show: e * PROD_SET {} < f e * PROD_SET {} by IMAGE_EMPTY + which is true since PROD_SET {} = 1 by PROD_SET_THM + and e < f e by given + If s <> {}, + Since e IN e INSERT s by COMPONENT + Hence 0 < e by e <> 0 + and !x. x IN s ==> x IN (e INSERT s) by IN_INSERT + Thus PROD_SET s < PROD_SET (IMAGE f s) by induction hypothesis + or e * PROD_SET s < e * PROD_SET (IMAGE f s) by LT_MULT_LCANCEL, 0 < e + Note 0 < PROD_SET (IMAGE f s) by IN_IMAGE, !x. x < f x /\ x <> 0 + so e * PROD_SET (IMAGE f s) < f e * PROD_SET (IMAGE f s) by LT_MULT_LCANCEL, e < f e + Hence PROD_SET (e INSERT s) < PROD_SET (IMAGE f (e INSERT s)) +*) +val PROD_SET_LESS = store_thm( + "PROD_SET_LESS", + ``!s. FINITE s /\ s <> {} /\ 0 NOTIN s ==> + !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s)``, + `!s. FINITE s ==> s <> {} /\ 0 NOTIN s ==> + !f. INJ f s univ(:num) /\ (!x. x < f x) ==> PROD_SET s < PROD_SET (IMAGE f s)` suffices_by rw[] >> + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + rw[] >> + `PROD_SET (e INSERT s) = e * PROD_SET (s DELETE e)` by rw[PROD_SET_THM] >> + `_ = e * PROD_SET s` by metis_tac[DELETE_NON_ELEMENT] >> + fs[INJ_INSERT] >> + `FINITE (IMAGE f s)` by rw[] >> + `f e NOTIN IMAGE f s` by metis_tac[IN_IMAGE] >> + `PROD_SET (IMAGE f (e INSERT s)) = f e * PROD_SET (IMAGE f s)` by rw[PROD_SET_IMAGE_REDUCTION] >> + Cases_on `s = {}` >- + rw[PROD_SET_SING, PROD_SET_THM] >> + `0 < e` by decide_tac >> + `PROD_SET s < PROD_SET (IMAGE f s)` by rw[] >> + `e * PROD_SET s < e * PROD_SET (IMAGE f s)` by rw[] >> + `e * PROD_SET (IMAGE f s) < (f e) * PROD_SET (IMAGE f s)` by rw[] >> + `(IMAGE f (e INSERT s)) = (f e INSERT IMAGE f s)` by rw[] >> + metis_tac[LESS_TRANS]); + +(* Theorem: FINITE s /\ s <> {} /\ 0 NOTIN s ==> PROD_SET s < PROD_SET (IMAGE SUC s) *) +(* Proof: + Since !m n. SUC m = SUC n <=> m = n by INV_SUC + thus INJ INJ SUC s univ(:num) by INJ_DEF + Hence the result follows by PROD_SET_LESS +*) +val PROD_SET_LESS_SUC = store_thm( + "PROD_SET_LESS_SUC", + ``!s. FINITE s /\ s <> {} /\ 0 NOTIN s ==> PROD_SET s < PROD_SET (IMAGE SUC s)``, + rpt strip_tac >> + (irule PROD_SET_LESS >> simp[]) >> + rw[INJ_DEF]); + +(* Theorem: FINITE s ==> !n x. x IN s /\ n divides x ==> n divides (PROD_SET s) *) +(* Proof: + By FINITE_INDUCT on s. + Base case: x IN {} /\ n divides x ==> n divides (PROD_SET {}) + True since x IN {} is false by NOT_IN_EMPTY + Step case: !n x. x IN s /\ n divides x ==> n divides (PROD_SET s) ==> + e NOTIN s /\ x IN e INSERT s /\ n divides x ==> n divides (PROD_SET (e INSERT s)) + PROD_SET (e INSERT s) + = e * PROD_SET (s DELETE e) by PROD_SET_THM + = e * PROD_SET s by DELETE_NON_ELEMENT + If x = e, + n divides x + means n divides e + hence n divides PROD_SET (e INSERT s) by DIVIDES_MULTIPLE, MULT_COMM + If x <> e, x IN s by IN_INSERT + n divides (PROD_SET s) by induction hypothesis + hence n divides PROD_SET (e INSERT s) by DIVIDES_MULTIPLE +*) +val PROD_SET_DIVISORS = store_thm( + "PROD_SET_DIVISORS", + ``!s. FINITE s ==> !n x. x IN s /\ n divides x ==> n divides (PROD_SET s)``, + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + metis_tac[NOT_IN_EMPTY] >> + `PROD_SET (e INSERT s) = e * PROD_SET (s DELETE e)` by rw[PROD_SET_THM] >> + `_ = e * PROD_SET s` by metis_tac[DELETE_NON_ELEMENT] >> + `(x = e) \/ (x IN s)` by rw[GSYM IN_INSERT] >- + metis_tac[DIVIDES_MULTIPLE, MULT_COMM] >> + metis_tac[DIVIDES_MULTIPLE]); + +(* Theorem: (Generalized Euclid's Lemma) + If prime p divides a PROD_SET, it divides a member of the PROD_SET. + FINITE s ==> !p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b *) +(* Proof: by induction of the PROD_SET, apply Euclid's Lemma. +- P_EUCLIDES; +> val it = + |- !p a b. prime p /\ p divides (a * b) ==> p divides a \/ p divides b : thm + By finite induction on s. + Base case: prime p /\ p divides (PROD_SET {}) ==> F + Since PROD_SET {} = 1 by PROD_SET_THM + and p divides 1 <=> p = 1 by DIVIDES_ONE + but prime p ==> p <> 1 by NOT_PRIME_1 + This gives the contradiction. + Step case: FINITE s /\ (!p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b) /\ + e NOTIN s /\ prime p /\ p divides (PROD_SET (e INSERT s)) ==> + ?b. ((b = e) \/ b IN s) /\ p divides b + Note PROD_SET (e INSERT s) = e * PROD_SET s by PROD_SET_THM, DELETE_NON_ELEMENT, e NOTIN s. + So prime p /\ p divides (PROD_SET (e INSERT s)) + ==> p divides e, or p divides (PROD_SET s) by P_EUCLIDES + If p divides e, just take b = e. + If p divides (PROD_SET s), there is such b by induction hypothesis +*) +val PROD_SET_EUCLID = store_thm( + "PROD_SET_EUCLID", + ``!s. FINITE s ==> !p. prime p /\ p divides (PROD_SET s) ==> ?b. b IN s /\ p divides b``, + ho_match_mp_tac FINITE_INDUCT >> + rw[] >- + metis_tac[PROD_SET_EMPTY, DIVIDES_ONE, NOT_PRIME_1] >> + `PROD_SET (e INSERT s) = e * PROD_SET s` + by metis_tac[PROD_SET_THM, DELETE_NON_ELEMENT] >> + Cases_on `p divides e` >- + metis_tac[] >> + metis_tac[P_EUCLIDES]); + +(* Theorem: FINITE s /\ x IN s ==> x divides PROD_SET s *) +(* Proof: + Note !n x. x IN s /\ n divides x + ==> n divides PROD_SET s by PROD_SET_DIVISORS + Put n = x, and x divides x = T by DIVIDES_REFL + and the result follows. +*) +val PROD_SET_ELEMENT_DIVIDES = store_thm( + "PROD_SET_ELEMENT_DIVIDES", + ``!s x. FINITE s /\ x IN s ==> x divides PROD_SET s``, + metis_tac[PROD_SET_DIVISORS, DIVIDES_REFL]); + +(* Theorem: FINITE s ==> !f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ + (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s) *) +(* Proof: + By finite induction on s. + Base: PROD_SET (IMAGE f {}) <= PROD_SET (IMAGE g {}) + Note PROD_SET (IMAGE f {}) + = PROD_SET {} by IMAGE_EMPTY + = 1 by PROD_SET_EMPTY + Thus true. + Step: !f g. (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s) ==> + e NOTIN s /\ !x. x IN e INSERT s ==> f x <= g x ==> + PROD_SET (IMAGE f (e INSERT s)) <= PROD_SET (IMAGE g (e INSERT s)) + Note INJ f s univ(:num) by INJ_INSERT + and INJ g s univ(:num) by INJ_INSERT + and f e NOTIN (IMAGE f s) by IN_IMAGE + and g e NOTIN (IMAGE g s) by IN_IMAGE + Applying LE_MONO_MULT2, + PROD_SET (IMAGE f (e INSERT s)) + = PROD_SET (f e INSERT IMAGE f s) by INSERT_IMAGE + = f e * PROD_SET (IMAGE f s) by PROD_SET_INSERT + <= g e * PROD_SET (IMAGE f s) by f e <= g e + <= g e * PROD_SET (IMAGE g s) by induction hypothesis + = PROD_SET (g e INSERT IMAGE g s) by PROD_SET_INSERT + = PROD_SET (IMAGE g (e INSERT s)) by INSERT_IMAGE +*) +val PROD_SET_LESS_EQ = store_thm( + "PROD_SET_LESS_EQ", + ``!s. FINITE s ==> !f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ + (!x. x IN s ==> f x <= g x) ==> PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_SET_EMPTY] >> + fs[INJ_INSERT] >> + `f e NOTIN (IMAGE f s)` by metis_tac[IN_IMAGE] >> + `g e NOTIN (IMAGE g s)` by metis_tac[IN_IMAGE] >> + `f e <= g e` by rw[] >> + `PROD_SET (IMAGE f s) <= PROD_SET (IMAGE g s)` by rw[] >> + rw[PROD_SET_INSERT, LE_MONO_MULT2]); + +(* Theorem: FINITE s ==> !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s *) +(* Proof: + By finite induction on s. + Base: PROD_SET {} <= n ** CARD {} + Note PROD_SET {} + = 1 by PROD_SET_EMPTY + = n ** 0 by EXP_0 + = n ** CARD {} by CARD_EMPTY + Step: !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s ==> + e NOTIN s /\ !x. x IN e INSERT s ==> x <= n ==> PROD_SET (e INSERT s) <= n ** CARD (e INSERT s) + Note !x. (x = e) \/ x IN s ==> x <= n by IN_INSERT + PROD_SET (e INSERT s) + = e * PROD_SET s by PROD_SET_INSERT + <= n * PROD_SET s by e <= n + <= n * (n ** CARD s) by induction hypothesis + = n ** (SUC (CARD s)) by EXP + = n ** CARD (e INSERT s) by CARD_INSERT, e NOTIN s +*) +val PROD_SET_LE_CONSTANT = store_thm( + "PROD_SET_LE_CONSTANT", + ``!s. FINITE s ==> !n. (!x. x IN s ==> x <= n) ==> PROD_SET s <= n ** CARD s``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_SET_EMPTY, EXP_0] >> + fs[] >> + `e <= n /\ PROD_SET s <= n ** CARD s` by rw[] >> + rw[PROD_SET_INSERT, EXP, CARD_INSERT, LE_MONO_MULT2]); + +(* Theorem: FINITE s ==> !n f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ (!x. x IN s ==> n <= f x * g x) ==> + n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s) *) +(* Proof: + By finite induction on s. + Base: n ** CARD {} <= PROD_SET (IMAGE f {}) * PROD_SET (IMAGE g {}) + Note n ** CARD {} + = n ** 0 by CARD_EMPTY + = 1 by EXP_0 + and PROD_SET (IMAGE f {}) + = PROD_SET {} by IMAGE_EMPTY + = 1 by PROD_SET_EMPTY + Step: !n f. INJ f s univ(:num) /\ INJ g s univ(:num) /\ + (!x. x IN s ==> n <= f x * g x) ==> + n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s) ==> + e NOTIN s /\ INJ f (e INSERT s) univ(:num) /\ INJ g (e INSERT s) univ(:num) /\ + !x. x IN e INSERT s ==> n <= f x * g x ==> + n ** CARD (e INSERT s) <= PROD_SET (IMAGE f (e INSERT s)) * PROD_SET (IMAGE g (e INSERT s)) + Note INJ f s univ(:num) /\ INJ g s univ(:num) by INJ_INSERT + and f e NOTIN (IMAGE f s) /\ g e NOTIN (IMAGE g s) by IN_IMAGE + PROD_SET (IMAGE f (e INSERT s)) * PROD_SET (IMAGE g (e INSERT s)) + = PROD_SET (f e INSERT (IMAGE f s)) * PROD_SET (g e INSERT (IMAGE g s)) by INSERT_IMAGE + = (f e * PROD_SET (IMAGE f s)) * (g e * PROD_SET (IMAGE g s)) by PROD_SET_INSERT + = (f e * g e) * (PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)) by MULT_ASSOC, MULT_COMM + >= n * (PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)) by n <= f e * g e + >= n * n ** CARD s by induction hypothesis + = n ** (SUC (CARD s)) by EXP + = n ** (CARD (e INSERT s)) by CARD_INSERT +*) +val PROD_SET_PRODUCT_GE_CONSTANT = store_thm( + "PROD_SET_PRODUCT_GE_CONSTANT", + ``!s. FINITE s ==> !n f g. INJ f s univ(:num) /\ INJ g s univ(:num) /\ + (!x. x IN s ==> n <= f x * g x) ==> + n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_SET_EMPTY, EXP_0] >> + fs[INJ_INSERT] >> + `f e NOTIN (IMAGE f s) /\ g e NOTIN (IMAGE g s)` by metis_tac[IN_IMAGE] >> + `n <= f e * g e /\ n ** CARD s <= PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s)` by rw[] >> + `PROD_SET (f e INSERT IMAGE f s) * PROD_SET (g e INSERT IMAGE g s) = + (f e * PROD_SET (IMAGE f s)) * (g e * PROD_SET (IMAGE g s))` by rw[PROD_SET_INSERT] >> + `_ = (f e * g e) * (PROD_SET (IMAGE f s) * PROD_SET (IMAGE g s))` by metis_tac[MULT_ASSOC, MULT_COMM] >> + metis_tac[EXP, CARD_INSERT, LE_MONO_MULT2]); + +(* ------------------------------------------------------------------------- *) +(* Pairwise Coprime Property *) +(* ------------------------------------------------------------------------- *) + +(* Overload pairwise coprime set *) +val _ = overload_on("PAIRWISE_COPRIME", ``\s. !x y. x IN s /\ y IN s /\ x <> y ==> coprime x y``); + +(* Theorem: e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> + (!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s *) +(* Proof: by IN_INSERT *) +val pairwise_coprime_insert = store_thm( + "pairwise_coprime_insert", + ``!s e. e NOTIN s /\ PAIRWISE_COPRIME (e INSERT s) ==> + (!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s``, + metis_tac[IN_INSERT]); + +(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> + !t. t SUBSET s ==> (PROD_SET t) divides (PROD_SET s) *) +(* Proof: + Note FINITE t by SUBSET_FINITE + By finite induction on t. + Base case: PROD_SET {} divides PROD_SET s + Note PROD_SET {} = 1 by PROD_SET_EMPTY + and 1 divides (PROD_SET s) by ONE_DIVIDES_ALL + Step case: t SUBSET s ==> PROD_SET t divides PROD_SET s ==> + e NOTIN t /\ e INSERT t SUBSET s ==> PROD_SET (e INSERT t) divides PROD_SET s + Let m = PROD_SET s. + Note e IN s /\ t SUBSET s by INSERT_SUBSET + Thus e divides m by PROD_SET_ELEMENT_DIVIDES + and (PROD_SET t) divides m by induction hypothesis + Also coprime e (PROD_SET t) by every_coprime_prod_set_coprime, SUBSET_DEF + Note PROD_SET (e INSERT t) = e * PROD_SET t by PROD_SET_INSERT + ==> e * PROD_SET t divides m by coprime_product_divides +*) +val pairwise_coprime_prod_set_subset_divides = store_thm( + "pairwise_coprime_prod_set_subset_divides", + ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> + !t. t SUBSET s ==> (PROD_SET t) divides (PROD_SET s)``, + rpt strip_tac >> + `FINITE t` by metis_tac[SUBSET_FINITE] >> + qpat_x_assum `t SUBSET s` mp_tac >> + qpat_x_assum `FINITE t` mp_tac >> + qid_spec_tac `t` >> + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_SET_EMPTY] >> + fs[] >> + `e divides PROD_SET s` by rw[PROD_SET_ELEMENT_DIVIDES] >> + `coprime e (PROD_SET t)` by prove_tac[every_coprime_prod_set_coprime, SUBSET_DEF] >> + rw[PROD_SET_INSERT, coprime_product_divides]); + +(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> + !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v) *) +(* Proof: + By finite induction on s. + Base: {} = u UNION v ==> coprime (PROD_SET u) (PROD_SET v) + Note u = {} and v = {} by EMPTY_UNION + and PROD_SET {} = 1 by PROD_SET_EMPTY + Hence true by GCD_1 + Step: PAIRWISE_COPRIME s ==> + !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v) ==> + e NOTIN s /\ e INSERT s = u UNION v ==> coprime (PROD_SET u) (PROD_SET v) + Note (!x. x IN s ==> coprime e x) /\ + PAIRWISE_COPRIME s by IN_INSERT + Note e IN u \/ e IN v by IN_INSERT, IN_UNION + If e IN u, + Then e NOTIN v by IN_DISJOINT + Let w = u DELETE e. + Then e NOTIN w by IN_DELETE + and u = e INSERT w by INSERT_DELETE + Note s = w UNION v by EXTENSION, IN_INSERT, IN_UNION + ==> FINITE w by FINITE_UNION + and DISJOINT w v by DISJOINT_INSERT + + Note coprime (PROD_SET w) (PROD_SET v) by induction hypothesis + and !x. x IN v ==> coprime e x by v SUBSET s + Also FINITE v by FINITE_UNION + so coprime e (PROD_SET v) by every_coprime_prod_set_coprime, FINITE v + ==> coprime (e * PROD_SET w) PROD_SET v by coprime_product_coprime + or coprime PROD_SET (e INSERT w) PROD_SET v by PROD_SET_INSERT + = coprime PROD_SET u PROD_SET v by above + + Similarly for e IN v. +*) +val pairwise_coprime_partition_coprime = store_thm( + "pairwise_coprime_partition_coprime", + ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> + !u v. (s = u UNION v) /\ DISJOINT u v ==> coprime (PROD_SET u) (PROD_SET v)``, + ntac 2 strip_tac >> + qpat_x_assum `PAIRWISE_COPRIME s` mp_tac >> + qpat_x_assum `FINITE s` mp_tac >> + qid_spec_tac `s` >> + Induct_on `FINITE` >> + rpt strip_tac >- + fs[PROD_SET_EMPTY] >> + `(!x. x IN s ==> coprime e x) /\ PAIRWISE_COPRIME s` by metis_tac[IN_INSERT] >> + `e IN u \/ e IN v` by metis_tac[IN_INSERT, IN_UNION] >| [ + qabbrev_tac `w = u DELETE e` >> + `u = e INSERT w` by rw[Abbr`w`] >> + `e NOTIN w` by rw[Abbr`w`] >> + `e NOTIN v` by metis_tac[IN_DISJOINT] >> + `s = w UNION v` by + (rw[EXTENSION] >> + metis_tac[IN_INSERT, IN_UNION]) >> + `FINITE w` by metis_tac[FINITE_UNION] >> + `DISJOINT w v` by metis_tac[DISJOINT_INSERT] >> + `coprime (PROD_SET w) (PROD_SET v)` by rw[] >> + `(!x. x IN v ==> coprime e x)` by rw[] >> + `FINITE v` by metis_tac[FINITE_UNION] >> + `coprime e (PROD_SET v)` by rw[every_coprime_prod_set_coprime] >> + metis_tac[coprime_product_coprime, PROD_SET_INSERT], + qabbrev_tac `w = v DELETE e` >> + `v = e INSERT w` by rw[Abbr`w`] >> + `e NOTIN w` by rw[Abbr`w`] >> + `e NOTIN u` by metis_tac[IN_DISJOINT] >> + `s = u UNION w` by + (rw[EXTENSION] >> + metis_tac[IN_INSERT, IN_UNION]) >> + `FINITE w` by metis_tac[FINITE_UNION] >> + `DISJOINT u w` by metis_tac[DISJOINT_INSERT, DISJOINT_SYM] >> + `coprime (PROD_SET u) (PROD_SET w)` by rw[] >> + `(!x. x IN u ==> coprime e x)` by rw[] >> + `FINITE u` by metis_tac[FINITE_UNION] >> + `coprime (PROD_SET u) e` by rw[every_coprime_prod_set_coprime, coprime_sym] >> + metis_tac[coprime_product_coprime_sym, PROD_SET_INSERT] + ]); + +(* Theorem: FINITE s /\ PAIRWISE_COPRIME s ==> !u v. (s = u UNION v) /\ DISJOINT u v ==> + (PROD_SET s = PROD_SET u * PROD_SET v) /\ (coprime (PROD_SET u) (PROD_SET v)) *) +(* Proof: by PROD_SET_PRODUCT_BY_PARTITION, pairwise_coprime_partition_coprime *) +val pairwise_coprime_prod_set_partition = store_thm( + "pairwise_coprime_prod_set_partition", + ``!s. FINITE s /\ PAIRWISE_COPRIME s ==> !u v. (s = u UNION v) /\ DISJOINT u v ==> + (PROD_SET s = PROD_SET u * PROD_SET v) /\ (coprime (PROD_SET u) (PROD_SET v))``, + metis_tac[PROD_SET_PRODUCT_BY_PARTITION, pairwise_coprime_partition_coprime]); + +(* Theorem: n! = PROD_SET (count (n+1)) *) +(* Proof: by induction on n. + Base case: FACT 0 = PROD_SET (IMAGE SUC (count 0)) + LHS = FACT 0 + = 1 by FACT + = PROD_SET {} by PROD_SET_THM + = PROD_SET (IMAGE SUC {}) by IMAGE_EMPTY + = PROD_SET (IMAGE SUC (count 0)) by COUNT_ZERO + = RHS + Step case: FACT n = PROD_SET (IMAGE SUC (count n)) ==> + FACT (SUC n) = PROD_SET (IMAGE SUC (count (SUC n))) + Note: (SUC n) NOTIN (IMAGE SUC (count n)) by IN_IMAGE, IN_COUNT [1] + LHS = FACT (SUC n) + = (SUC n) * (FACT n) by FACT + = (SUC n) * (PROD_SET (IMAGE SUC (count n))) by induction hypothesis + = (SUC n) * (PROD_SET (IMAGE SUC (count n)) DELETE (SUC n)) by DELETE_NON_ELEMENT, [1] + = PROD_SET ((SUC n) INSERT ((IMAGE SUC (count n)) DELETE (SUC n))) by PROD_SET_THM + = PROD_SET (IMAGE SUC (n INSERT (count n))) by IMAGE_INSERT + = PROD_SET (IMAGE SUC (count (SUC n))) by COUNT_SUC + = RHS +*) +val FACT_EQ_PROD = store_thm( + "FACT_EQ_PROD", + ``!n. FACT n = PROD_SET (IMAGE SUC (count n))``, + Induct_on `n` >- + rw[PROD_SET_THM, FACT] >> + rw[PROD_SET_THM, FACT, COUNT_SUC] >> + `(SUC n) NOTIN (IMAGE SUC (count n))` by rw[] >> + metis_tac[DELETE_NON_ELEMENT]); + +(* Theorem: n!/m! = product of (m+1) to n. + m < n ==> (FACT n = PROD_SET (IMAGE SUC ((count n) DIFF (count m))) * (FACT m)) *) +(* Proof: by factorial formula. + By induction on n. + Base case: m < 0 ==> ... + True since m < 0 = F. + Step case: !m. m < n ==> + (FACT n = PROD_SET (IMAGE SUC (count n DIFF count m)) * FACT m) ==> + !m. m < SUC n ==> + (FACT (SUC n) = PROD_SET (IMAGE SUC (count (SUC n) DIFF count m)) * FACT m) + Note that m < SUC n ==> m <= n. + and FACT (SUC n) = (SUC n) * FACT n by FACT + If m = n, + PROD_SET (IMAGE SUC (count (SUC n) DIFF count n)) * FACT n + = PROD_SET (IMAGE SUC {n}) * FACT n by IN_DIFF, IN_COUNT + = PROD_SET {SUC n} * FACT n by IN_IMAGE + = (SUC n) * FACT n by PROD_SET_THM + If m < n, + n NOTIN (count m) by IN_COUNT + so n INSERT ((count n) DIFF (count m)) + = (n INSERT (count n)) DIFF (count m) by INSERT_DIFF + = count (SUC n) DIFF (count m) by EXTENSION + Since (SUC n) NOTIN (IMAGE SUC ((count n) DIFF (count m))) by IN_IMAGE, IN_DIFF, IN_COUNT + and FINITE (IMAGE SUC ((count n) DIFF (count m))) by IMAGE_FINITE, FINITE_DIFF, FINITE_COUNT + Hence PROD_SET (IMAGE SUC (count (SUC n) DIFF count m)) * FACT m + = ((SUC n) * PROD_SET (IMAGE SUC (count n DIFF count m))) * FACT m by PROD_SET_IMAGE_REDUCTION + = (SUC n) * (PROD_SET (IMAGE SUC (count n DIFF count m))) * FACT m) by MULT_ASSOC + = (SUC n) * FACT n by induction hypothesis + = FACT (SUC n) by FACT +*) +val FACT_REDUCTION = store_thm( + "FACT_REDUCTION", + ``!n m. m < n ==> (FACT n = PROD_SET (IMAGE SUC ((count n) DIFF (count m))) * (FACT m))``, + Induct_on `n` >- + rw[] >> + rw_tac std_ss[FACT] >> + `m <= n` by decide_tac >> + Cases_on `m = n` >| [ + rw_tac std_ss[] >> + `count (SUC m) DIFF count m = {m}` by + (rw[DIFF_DEF] >> + rw[EXTENSION, EQ_IMP_THM]) >> + `PROD_SET (IMAGE SUC {m}) = SUC m` by rw[PROD_SET_THM] >> + metis_tac[], + `m < n` by decide_tac >> + `n NOTIN (count m)` by srw_tac[ARITH_ss][] >> + `n INSERT ((count n) DIFF (count m)) = (n INSERT (count n)) DIFF (count m)` by rw[] >> + `_ = count (SUC n) DIFF (count m)` by srw_tac[ARITH_ss][EXTENSION] >> + `(SUC n) NOTIN (IMAGE SUC ((count n) DIFF (count m)))` by rw[] >> + `FINITE (IMAGE SUC ((count n) DIFF (count m)))` by rw[] >> + metis_tac[PROD_SET_IMAGE_REDUCTION, MULT_ASSOC] + ]); + +(* ------------------------------------------------------------------------- *) +(* Logic Theorems. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: (A <=> B) <=> (A ==> B) /\ ((A ==> B) ==> (B ==> A)) *) +(* Proof: by logic. *) +val EQ_IMP2_THM = store_thm( + "EQ_IMP2_THM", + ``!A B. (A <=> B) <=> (A ==> B) /\ ((A ==> B) ==> (B ==> A))``, + metis_tac[]); + +(* Theorem: (b1 = b2) ==> (f b1 = f b2) *) +(* Proof: by substitution. *) +val BOOL_EQ = store_thm( + "BOOL_EQ", + ``!b1:bool b2:bool f. (b1 = b2) ==> (f b1 = f b2)``, + simp[]); + +(* Theorem: b /\ (c ==> d) ==> ((b ==> c) ==> d) *) +(* Proof: by logical implication. *) +val AND_IMP_IMP = store_thm((* was: IMP_IMP *) + "AND_IMP_IMP", + ``!b c d. b /\ (c ==> d) ==> ((b ==> c) ==> d)``, + metis_tac[]); + +(* Theorem: p /\ q ==> p \/ ~q *) +(* Proof: + Note p /\ q ==> p by AND1_THM + and p ==> p \/ ~q by OR_INTRO_THM1 + Thus p /\ q ==> p \/ ~q +*) +val AND_IMP_OR_NEG = store_thm( + "AND_IMP_OR_NEG", + ``!p q. p /\ q ==> p \/ ~q``, + metis_tac[]); + +(* Theorem: (p \/ q ==> r) ==> (p /\ ~q ==> r) *) +(* Proof: + (p \/ q) ==> r + = ~(p \/ q) \/ r by IMP_DISJ_THM + = (~p /\ ~q) \/ r by DE_MORGAN_THM + ==> (~p \/ q) \/ r by AND_IMP_OR_NEG + = ~(p /\ ~q) \/ r by DE_MORGAN_THM + = (p /\ ~q) ==> r by IMP_DISJ_THM +*) +val OR_IMP_IMP = store_thm( + "OR_IMP_IMP", + ``!p q r. ((p \/ q) ==> r) ==> ((p /\ ~q) ==> r)``, + metis_tac[]); + +(* Theorem: x IN (if b then s else t) <=> if b then x IN s else x IN t *) +(* Proof: by boolTheory.COND_RAND: + |- !f b x y. f (if b then x else y) = if b then f x else f y +*) +val PUSH_IN_INTO_IF = store_thm( + "PUSH_IN_INTO_IF", + ``!b x s t. x IN (if b then s else t) <=> if b then x IN s else x IN t``, + rw_tac std_ss[]); + +(* ------------------------------------------------------------------------- *) +(* More Theorems and Sets for Counting *) +(* ------------------------------------------------------------------------- *) + +(* Have simple (count n) *) + +(* Theorem: count 1 = {0} *) +(* Proof: rename COUNT_ZERO *) +val COUNT_0 = save_thm("COUNT_0", COUNT_ZERO); +(* val COUNT_0 = |- count 0 = {}: thm *) + +(* Theorem: count 1 = {0} *) +(* Proof: by count_def, EXTENSION *) +val COUNT_1 = store_thm( + "COUNT_1", + ``count 1 = {0}``, + rw[count_def, EXTENSION]); + +(* Theorem: n NOTIN (count n) *) +(* Proof: by IN_COUNT *) +val COUNT_NOT_SELF = store_thm( + "COUNT_NOT_SELF", + ``!n. n NOTIN (count n)``, + rw[]); + +(* Theorem: m <= n ==> count m SUBSET count n *) +(* Proof: by LENGTH_TAKE_EQ *) +val COUNT_SUBSET = store_thm( + "COUNT_SUBSET", + ``!m n. m <= n ==> count m SUBSET count n``, + rw[SUBSET_DEF]); + +(* Theorem: count (SUC n) SUBSET t <=> count n SUBSET t /\ n IN t *) +(* Proof: + count (SUC n) SUBSET t + <=> (n INSERT count n) SUBSET t by COUNT_SUC + <=> n IN t /\ (count n) SUBSET t by INSERT_SUBSET + <=> (count n) SUBSET t /\ n IN t by CONJ_COMM +*) +val COUNT_SUC_SUBSET = store_thm( + "COUNT_SUC_SUBSET", + ``!n t. count (SUC n) SUBSET t <=> count n SUBSET t /\ n IN t``, + metis_tac[COUNT_SUC, INSERT_SUBSET]); + +(* Theorem: t DIFF (count (SUC n)) = t DIFF (count n) DELETE n *) +(* Proof: + t DIFF (count (SUC n)) + = t DIFF (n INSERT count n) by COUNT_SUC + = t DIFF (count n) DELETE n by EXTENSION +*) +val DIFF_COUNT_SUC = store_thm( + "DIFF_COUNT_SUC", + ``!n t. t DIFF (count (SUC n)) = t DIFF (count n) DELETE n``, + rw[EXTENSION, EQ_IMP_THM]); + +(* COUNT_SUC |- !n. count (SUC n) = n INSERT count n *) + +(* Theorem: count (SUC n) = 0 INSERT (IMAGE SUC (count n)) *) +(* Proof: by EXTENSION *) +val COUNT_SUC_BY_SUC = store_thm( + "COUNT_SUC_BY_SUC", + ``!n. count (SUC n) = 0 INSERT (IMAGE SUC (count n))``, + rw[EXTENSION, EQ_IMP_THM] >> + (Cases_on `x` >> simp[])); + +(* Theorem: IMAGE f (count (SUC n)) = (f n) INSERT IMAGE f (count n) *) +(* Proof: + IMAGE f (count (SUC n)) + = IMAGE f (n INSERT (count n)) by COUNT_SUC + = f n INSERT IMAGE f (count n) by IMAGE_INSERT +*) +val IMAGE_COUNT_SUC = store_thm( + "IMAGE_COUNT_SUC", + ``!f n. IMAGE f (count (SUC n)) = (f n) INSERT IMAGE f (count n)``, + rw[COUNT_SUC]); + +(* Theorem: IMAGE f (count (SUC n)) = (f 0) INSERT IMAGE (f o SUC) (count n) *) +(* Proof: + IMAGE f (count (SUC n)) + = IMAGE f (0 INSERT (IMAGE SUC (count n))) by COUNT_SUC_BY_SUC + = f 0 INSERT IMAGE f (IMAGE SUC (count n)) by IMAGE_INSERT + = f 0 INSERT IMAGE (f o SUC) (count n) by IMAGE_COMPOSE +*) +val IMAGE_COUNT_SUC_BY_SUC = store_thm( + "IMAGE_COUNT_SUC_BY_SUC", + ``!f n. IMAGE f (count (SUC n)) = (f 0) INSERT IMAGE (f o SUC) (count n)``, + rw[COUNT_SUC_BY_SUC, IMAGE_COMPOSE]); + +(* Introduce countFrom m n, the set {m, m + 1, m + 2, ...., m + n - 1} *) +val _ = overload_on("countFrom", ``\m n. IMAGE ($+ m) (count n)``); + +(* Theorem: countFrom m 0 = {} *) +(* Proof: + countFrom m 0 + = IMAGE ($+ m) (count 0) by notation + = IMAGE ($+ m) {} by COUNT_0 + = {} by IMAGE_EMPTY +*) +val countFrom_0 = store_thm( + "countFrom_0", + ``!m. countFrom m 0 = {}``, + rw[]); + +(* Theorem: countFrom m (SUC n) = m INSERT countFrom (m + 1) n *) +(* Proof: by IMAGE_COUNT_SUC_BY_SUC *) +val countFrom_SUC = store_thm( + "countFrom_SUC", + ``!m n. !m n. countFrom m (SUC n) = m INSERT countFrom (m + 1) n``, + rpt strip_tac >> + `$+ m o SUC = $+ (m + 1)` by rw[FUN_EQ_THM] >> + rw[IMAGE_COUNT_SUC_BY_SUC]); + +(* Theorem: 0 < n ==> m IN countFrom m n *) +(* Proof: by IN_COUNT *) +val countFrom_first = store_thm( + "countFrom_first", + ``!m n. 0 < n ==> m IN countFrom m n``, + rw[] >> + metis_tac[ADD_0]); + +(* Theorem: x < m ==> x NOTIN countFrom m n *) +(* Proof: by IN_COUNT *) +val countFrom_less = store_thm( + "countFrom_less", + ``!m n x. x < m ==> x NOTIN countFrom m n``, + rw[]); + +(* Theorem: count n = countFrom 0 n *) +(* Proof: by EXTENSION *) +val count_by_countFrom = store_thm( + "count_by_countFrom", + ``!n. count n = countFrom 0 n``, + rw[EXTENSION]); + +(* Theorem: count (SUC n) = 0 INSERT countFrom 1 n *) +(* Proof: + count (SUC n) + = 0 INSERT IMAGE SUC (count n) by COUNT_SUC_BY_SUC + = 0 INSERT IMAGE ($+ 1) (count n) by FUN_EQ_THM + = 0 INSERT countFrom 1 n by notation +*) +val count_SUC_by_countFrom = store_thm( + "count_SUC_by_countFrom", + ``!n. count (SUC n) = 0 INSERT countFrom 1 n``, + rpt strip_tac >> + `SUC = $+ 1` by rw[FUN_EQ_THM] >> + rw[COUNT_SUC_BY_SUC]); + +(* Inclusion-Exclusion for two sets: + +CARD_UNION +|- !s. FINITE s ==> !t. FINITE t ==> + (CARD (s UNION t) + CARD (s INTER t) = CARD s + CARD t) +CARD_UNION_EQN +|- !s t. FINITE s /\ FINITE t ==> + (CARD (s UNION t) = CARD s + CARD t - CARD (s INTER t)) +CARD_UNION_DISJOINT +|- !s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> + (CARD (s UNION t) = CARD s + CARD t) +*) + +(* Inclusion-Exclusion for three sets. *) + +(* Theorem: FINITE a /\ FINITE b /\ FINITE c ==> + (CARD (a UNION b UNION c) = + CARD a + CARD b + CARD c + CARD (a INTER b INTER c) - + CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c)) *) +(* Proof: + Note FINITE (a UNION b) by FINITE_UNION + and FINITE (a INTER c) by FINITE_INTER + and FINITE (b INTER c) by FINITE_INTER + Also (a INTER c) INTER (b INTER c) + = a INTER b INTER c by EXTENSION + and CARD (a INTER b) <= CARD a by CARD_INTER_LESS_EQ + and CARD (a INTER b INTER c) <= CARD (b INTER c) by CARD_INTER_LESS_EQ, INTER_COMM + + CARD (a UNION b UNION c) + = CARD (a UNION b) + CARD c - CARD ((a UNION b) INTER c) + by CARD_UNION_EQN + = (CARD a + CARD b - CARD (a INTER b)) + + CARD c - CARD ((a UNION b) INTER c) by CARD_UNION_EQN + = (CARD a + CARD b - CARD (a INTER b)) + + CARD c - CARD ((a INTER c) UNION (b INTER c)) + by UNION_OVER_INTER + = (CARD a + CARD b - CARD (a INTER b)) + CARD c - + (CARD (a INTER c) + CARD (b INTER c) - CARD ((a INTER c) INTER (b INTER c))) + by CARD_UNION_EQN + = CARD a + CARD b + CARD c - CARD (a INTER b) - + (CARD (a INTER c) + CARD (b INTER c) - CARD (a INTER b INTER c)) + by CARD (a INTER b) <= CARD a + = CARD a + CARD b + CARD c - CARD (a INTER b) - + (CARD (b INTER c) + CARD (a INTER c) - CARD (a INTER b INTER c)) + by ADD_COMM + = CARD a + CARD b + CARD c - CARD (a INTER b) + + CARD (a INTER b INTER c) - CARD (b INTER c) - CARD (a INTER c) + by CARD (a INTER b INTER c) <= CARD (b INTER c) + = CARD a + CARD b + CARD c + CARD (a INTER b INTER c) + - CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c) + by arithmetic +*) +Theorem CARD_UNION_3_EQN: + !a b c. FINITE a /\ FINITE b /\ FINITE c ==> + (CARD (a UNION b UNION c) = + CARD a + CARD b + CARD c + CARD (a INTER b INTER c) - + CARD (a INTER b) - CARD (b INTER c) - CARD (a INTER c)) +Proof + rpt strip_tac >> + `FINITE (a UNION b) /\ FINITE (a INTER c) /\ FINITE (b INTER c)` by rw[] >> + (`(a INTER c) INTER (b INTER c) = a INTER b INTER c` by (rw[EXTENSION] >> metis_tac[])) >> + `CARD (a INTER b) <= CARD a` by rw[CARD_INTER_LESS_EQ] >> + `CARD (a INTER b INTER c) <= CARD (b INTER c)` by metis_tac[INTER_COMM, CARD_INTER_LESS_EQ] >> + `CARD (a UNION b UNION c) + = CARD (a UNION b) + CARD c - CARD ((a UNION b) INTER c)` by rw[CARD_UNION_EQN] >> + `_ = (CARD a + CARD b - CARD (a INTER b)) + + CARD c - CARD ((a UNION b) INTER c)` by rw[CARD_UNION_EQN] >> + `_ = (CARD a + CARD b - CARD (a INTER b)) + + CARD c - CARD ((a INTER c) UNION (b INTER c))` by fs[UNION_OVER_INTER, INTER_COMM] >> + `_ = (CARD a + CARD b - CARD (a INTER b)) + CARD c - + (CARD (a INTER c) + CARD (b INTER c) - CARD (a INTER b INTER c))` by metis_tac[CARD_UNION_EQN] >> + decide_tac +QED + +(* Simplification of the above result for 3 disjoint sets. *) + +(* Theorem: FINITE a /\ FINITE b /\ FINITE c /\ + DISJOINT a b /\ DISJOINT b c /\ DISJOINT a c ==> + (CARD (a UNION b UNION c) = CARD a + CARD b + CARD c) *) +(* Proof: by DISJOINT_DEF, CARD_UNION_3_EQN *) +Theorem CARD_UNION_3_DISJOINT: + !a b c. FINITE a /\ FINITE b /\ FINITE c /\ + DISJOINT a b /\ DISJOINT b c /\ DISJOINT a c ==> + (CARD (a UNION b UNION c) = CARD a + CARD b + CARD c) +Proof + rw[DISJOINT_DEF] >> + rw[CARD_UNION_3_EQN] +QED + +(* ------------------------------------------------------------------------- *) +(* Maximum and Minimum of a Set *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: FINITE s /\ s <> {} /\ s <> {MIN_SET s} ==> (MAX_SET (s DELETE (MIN_SET s)) = MAX_SET s) *) +(* Proof: + Let m = MIN_SET s, t = s DELETE m. + Then t SUBSET s by DELETE_SUBSET + so FINITE t by SUBSET_FINITE]); + Since m IN s by MIN_SET_IN_SET + so t <> {} by DELETE_EQ_SING, s <> {m} + ==> ?x. x IN t by MEMBER_NOT_EMPTY + and x IN s /\ x <> m by IN_DELETE + From x IN s ==> m <= x by MIN_SET_PROPERTY + With x <> m ==> m < x by LESS_OR_EQ + Also x <= MAX_SET s by MAX_SET_PROPERTY + Thus MAX_SET s <> m since m < x <= MAX_SET s + But MAX_SET s IN s by MAX_SET_IN_SET + Thus MAX_SET s IN t by IN_DELETE + Now !y. y IN t ==> + y IN s by SUBSET_DEF + or y <= MAX_SET s by MAX_SET_PROPERTY + Hence MAX_SET s = MAX_SET t by MAX_SET_TEST +*) +val MAX_SET_DELETE = store_thm( + "MAX_SET_DELETE", + ``!s. FINITE s /\ s <> {} /\ s <> {MIN_SET s} ==> (MAX_SET (s DELETE (MIN_SET s)) = MAX_SET s)``, + rpt strip_tac >> + qabbrev_tac `m = MIN_SET s` >> + qabbrev_tac `t = s DELETE m` >> + `t SUBSET s` by rw[Abbr`t`] >> + `FINITE t` by metis_tac[SUBSET_FINITE] >> + `t <> {}` by metis_tac[MIN_SET_IN_SET, DELETE_EQ_SING] >> + `?x. x IN t /\ x IN s /\ x <> m` by metis_tac[IN_DELETE, MEMBER_NOT_EMPTY] >> + `m <= x` by rw[MIN_SET_PROPERTY, Abbr`m`] >> + `m < x` by decide_tac >> + `x <= MAX_SET s` by rw[MAX_SET_PROPERTY] >> + `MAX_SET s <> m` by decide_tac >> + `MAX_SET s IN t` by rw[MAX_SET_IN_SET, Abbr`t`] >> + metis_tac[SUBSET_DEF, MAX_SET_PROPERTY, MAX_SET_TEST]); + +(* Theorem: MAX_SET (IMAGE SUC (count n)) = n *) +(* Proof: + By induction on n. + Base case: MAX_SET (IMAGE SUC (count 0)) = 0 + LHS = MAX_SET (IMAGE SUC (count 0)) + = MAX_SET (IMAGE SUC {}) by COUNT_ZERO + = MAX_SET {} by IMAGE_EMPTY + = 0 by MAX_SET_THM + = RHS + Step case: MAX_SET (IMAGE SUC (count n)) = n ==> + MAX_SET (IMAGE SUC (count (SUC n))) = SUC n + LHS = MAX_SET (IMAGE SUC (count (SUC n))) + = MAX_SET (IMAGE SUC (n INSERT count n)) by COUNT_SUC + = MAX_SET ((SUC n) INSERT (IMAGE SUC (count n))) by IMAGE_INSERT + = MAX (SUC n) (MAX_SET (IMAGE SUC (count n))) by MAX_SET_THM + = MAX (SUC n) n by induction hypothesis + = SUC n by LESS_SUC, MAX_DEF + = RHS +*) +Theorem MAX_SET_IMAGE_SUC_COUNT: + !n. MAX_SET (IMAGE SUC (count n)) = n +Proof + Induct_on ‘n’ >- + rw[] >> + ‘MAX_SET (IMAGE SUC (count (SUC n))) = + MAX_SET (IMAGE SUC (n INSERT count n))’ by rw[COUNT_SUC] >> + ‘_ = MAX_SET ((SUC n) INSERT (IMAGE SUC (count n)))’ by rw[] >> + ‘_ = MAX (SUC n) (MAX_SET (IMAGE SUC (count n)))’ by rw[MAX_SET_THM] >> + ‘_ = MAX (SUC n) n’ by rw[] >> + ‘_ = SUC n’ by metis_tac[LESS_SUC, MAX_DEF, MAX_COMM] >> + rw[] +QED + +(* Theorem: HALF x <= c ==> f x <= MAX_SET {f x | HALF x <= c} *) +(* Proof: + Let s = {f x | HALF x <= c} + Note !x. HALF x <= c + ==> SUC (2 * HALF x) <= SUC (2 * c) by arithmetic + and x <= SUC (2 * HALF x) by TWO_HALF_LE_THM + so x <= SUC (2 * c) < 2 * c + 2 by arithmetic + Thus s SUBSET (IMAGE f (count (2 * c + 2))) by SUBSET_DEF + Note FINITE (count (2 * c + 2)) by FINITE_COUNT + so FINITE s by IMAGE_FINITE, SUBSET_FINITE + and f x IN s by HALF x <= c + Thus f x <= MAX_SET s by MAX_SET_PROPERTY +*) +val MAX_SET_IMAGE_with_HALF = store_thm( + "MAX_SET_IMAGE_with_HALF", + ``!f c x. HALF x <= c ==> f x <= MAX_SET {f x | HALF x <= c}``, + rpt strip_tac >> + qabbrev_tac `s = {f x | HALF x <= c}` >> + `s SUBSET (IMAGE f (count (2 * c + 2)))` by + (rw[SUBSET_DEF, Abbr`s`] >> + `SUC (2 * HALF x'') <= SUC (2 * c)` by rw[] >> + `x'' <= SUC (2 * HALF x'')` by rw[TWO_HALF_LE_THM] >> + `x'' < 2 * c + 2` by decide_tac >> + metis_tac[]) >> + `FINITE s` by metis_tac[FINITE_COUNT, IMAGE_FINITE, SUBSET_FINITE] >> + (`f x IN s` by (rw[Abbr`s`] >> metis_tac[])) >> + rw[MAX_SET_PROPERTY]); + +(* +Note: A more general version, replacing HALF x by g x, would be desirable. +However, there is no way to show FINITE s for arbitrary g x. +*) + +(* Theorem: 0 < b /\ x DIV b <= c ==> f x <= MAX_SET {f x | x DIV b <= c} *) +(* Proof: + Let s = {f x | x DIV b <= c}. + Note !x. x DIV b <= c + ==> b * SUC (x DIV b) <= b * SUC c by arithmetic + and x < b * SUC (x DIV b) by DIV_MULT_LESS_EQ, 0 < b + so x < b * SUC c by arithmetic + Thus s SUBSET (IMAGE f (count (b * SUC c))) by SUBSET_DEF + Note FINITE (count (b * SUC c)) by FINITE_COUNT + so FINITE s by IMAGE_FINITE, SUBSET_FINITE + and f x IN s by HALF x <= c + Thus f x <= MAX_SET s by MAX_SET_PROPERTY +*) +val MAX_SET_IMAGE_with_DIV = store_thm( + "MAX_SET_IMAGE_with_DIV", + ``!f b c x. 0 < b /\ x DIV b <= c ==> f x <= MAX_SET {f x | x DIV b <= c}``, + rpt strip_tac >> + qabbrev_tac `s = {f x | x DIV b <= c}` >> + `s SUBSET (IMAGE f (count (b * SUC c)))` by + (rw[SUBSET_DEF, Abbr`s`] >> + `b * SUC (x'' DIV b) <= b * SUC c` by rw[] >> + `x'' < b * SUC (x'' DIV b)` by rw[DIV_MULT_LESS_EQ] >> + `x'' < b * SUC c` by decide_tac >> + metis_tac[]) >> + `FINITE s` by metis_tac[FINITE_COUNT, IMAGE_FINITE, SUBSET_FINITE] >> + (`f x IN s` by (rw[Abbr`s`] >> metis_tac[])) >> + rw[MAX_SET_PROPERTY]); + +(* Theorem: x - b <= c ==> f x <= MAX_SET {f x | x - b <= c} *) +(* Proof: + Let s = {f x | x - b <= c} + Note !x. x - b <= c ==> x <= b + c by arithmetic + so x <= 1 + b + c by arithmetic + Thus s SUBSET (IMAGE f (count (1 + b + c))) by SUBSET_DEF + Note FINITE (count (1 + b + c)) by FINITE_COUNT + so FINITE s by IMAGE_FINITE, SUBSET_FINITE + and f x IN s by x - b <= c + Thus f x <= MAX_SET s by MAX_SET_PROPERTY +*) +val MAX_SET_IMAGE_with_DEC = store_thm( + "MAX_SET_IMAGE_with_DEC", + ``!f b c x. x - b <= c ==> f x <= MAX_SET {f x | x - b <= c}``, + rpt strip_tac >> + qabbrev_tac `s = {f x | x - b <= c}` >> + `s SUBSET (IMAGE f (count (1 + b + c)))` by + (rw[SUBSET_DEF, Abbr`s`] >> + `x'' < b + (c + 1)` by decide_tac >> + metis_tac[]) >> + `FINITE s` by metis_tac[FINITE_COUNT, IMAGE_FINITE, SUBSET_FINITE] >> + `f x IN s` by + (rw[Abbr`s`] >> + `x <= b + c` by decide_tac >> + metis_tac[]) >> + rw[MAX_SET_PROPERTY]); + +(* ------------------------------------------------------------------------- *) +(* Finite and Cardinality Theorems *) +(* ------------------------------------------------------------------------- *) + + +(* Theorem: INJ f s UNIV /\ FINITE s ==> CARD (IMAGE f s) = CARD s *) +(* Proof: + !x y. x IN s /\ y IN s /\ f x = f y == x = y by INJ_DEF + FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE + Therefore BIJ f s (IMAGE f s) by BIJ_DEF, INJ_DEF, SURJ_DEF + Hence CARD (IMAGE f s) = CARD s by FINITE_BIJ_CARD_EQ +*) +val INJ_CARD_IMAGE_EQN = store_thm( + "INJ_CARD_IMAGE_EQN", + ``!f s. INJ f s UNIV /\ FINITE s ==> (CARD (IMAGE f s) = CARD s)``, + rw[INJ_DEF] >> + `FINITE (IMAGE f s)` by rw[IMAGE_FINITE] >> + `BIJ f s (IMAGE f s)` by rw[BIJ_DEF, INJ_DEF, SURJ_DEF] >> + metis_tac[FINITE_BIJ_CARD_EQ]); + + +(* Theorem: FINTIE s /\ FINITE t /\ CARD s = CARD t /\ INJ f s t ==> SURJ f s t *) +(* Proof: + For any map f from s to t, + (IMAGE f s) SUBSET t by IMAGE_SUBSET_TARGET + FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE + CARD (IMAGE f s) = CARD s by INJ_CARD_IMAGE + = CARD t by given + Hence (IMAGE f s) = t by SUBSET_EQ_CARD, FINITE t + or SURJ f s t by IMAGE_SURJ +*) +val FINITE_INJ_AS_SURJ = store_thm( + "FINITE_INJ_AS_SURJ", + ``!f s t. INJ f s t /\ FINITE s /\ FINITE t /\ (CARD s = CARD t) ==> SURJ f s t``, + rw[INJ_DEF] >> + `(IMAGE f s) SUBSET t` by rw[GSYM IMAGE_SUBSET_TARGET] >> + `FINITE (IMAGE f s)` by rw[IMAGE_FINITE] >> + `CARD (IMAGE f s) = CARD t` by metis_tac[INJ_DEF, INJ_CARD_IMAGE, INJ_SUBSET, SUBSET_REFL, SUBSET_UNIV] >> + rw[SUBSET_EQ_CARD, IMAGE_SURJ]); + +(* Reformulate theorem *) + +(* Theorem: FINITE s /\ FINITE t /\ CARD s = CARD t /\ + INJ f s t ==> SURJ f s t *) +(* Proof: by FINITE_INJ_AS_SURJ *) +Theorem FINITE_INJ_IS_SURJ: + !f s t. FINITE s /\ FINITE t /\ CARD s = CARD t /\ + INJ f s t ==> SURJ f s t +Proof + simp[FINITE_INJ_AS_SURJ] +QED + +(* Theorem: FINITE s /\ FINITE t /\ CARD s = CARD t /\ INJ f s t ==> BIJ f s t *) +(* Proof: + Note SURJ f s t by FINITE_INJ_IS_SURJ + so BIJ f s t by BIJ_DEF, INJ f s t +*) +Theorem FINITE_INJ_IS_BIJ: + !f s t. FINITE s /\ FINITE t /\ CARD s = CARD t /\ INJ f s t ==> BIJ f s t +Proof + simp[FINITE_INJ_IS_SURJ, BIJ_DEF] +QED + +(* Note: FINITE_SURJ_IS_BIJ is not easy, see helperFunction. *) + +(* Theorem: FINITE {P x | x < n} *) +(* Proof: + Since IMAGE (\i. P i) (count n) = {P x | x < n}, + this follows by + IMAGE_FINITE |- !s. FINITE s ==> !f. FINITE (IMAGE f s) : thm + FINITE_COUNT |- !n. FINITE (count n) : thm +*) +val FINITE_COUNT_IMAGE = store_thm( + "FINITE_COUNT_IMAGE", + ``!P n. FINITE {P x | x < n }``, + rpt strip_tac >> + `IMAGE (\i. P i) (count n) = {P x | x < n}` by rw[IMAGE_DEF] >> + metis_tac[IMAGE_FINITE, FINITE_COUNT]); + +(* Idea: improve FINITE_BIJ_COUNT to include CARD information. *) + +(* Theorem: FINITE s ==> ?f. BIJ f (count (CARD s)) s *) +(* Proof: + Note ?f b. BIJ f (count b) s by FINITE_BIJ_COUNT + and FINITE (count b) by FINITE_COUNT + so CARD s + = CARD (count b) by FINITE_BIJ + = b by CARD_COUNT +*) +Theorem FINITE_BIJ_COUNT_CARD: + !s. FINITE s ==> ?f. BIJ f (count (CARD s)) s +Proof + rpt strip_tac >> + imp_res_tac FINITE_BIJ_COUNT >> + metis_tac[FINITE_COUNT, CARD_COUNT, FINITE_BIJ] +QED + +(* Theorem: !n. 0 < n ==> IMAGE (\x. x MOD n) s SUBSET (count n) *) +(* Proof: by SUBSET_DEF, MOD_LESS. *) +val image_mod_subset_count = store_thm( + "image_mod_subset_count", + ``!s n. 0 < n ==> IMAGE (\x. x MOD n) s SUBSET (count n)``, + rw[SUBSET_DEF] >> + rw[MOD_LESS]); + +(* Theorem: !n. 0 < n ==> CARD (IMAGE (\x. x MOD n) s) <= n *) +(* Proof: + Let t = IMAGE (\x. x MOD n) s + Since t SUBSET count n by SUBSET_DEF, MOD_LESS + Now FINITE (count n) by FINITE_COUNT + and CARD (count n) = n by CARD_COUNT + So CARD t <= n by CARD_SUBSET +*) +val card_mod_image = store_thm( + "card_mod_image", + ``!s n. 0 < n ==> CARD (IMAGE (\x. x MOD n) s) <= n``, + rpt strip_tac >> + qabbrev_tac `t = IMAGE (\x. x MOD n) s` >> + (`t SUBSET count n` by (rw[SUBSET_DEF, Abbr`t`] >> metis_tac[MOD_LESS])) >> + metis_tac[CARD_SUBSET, FINITE_COUNT, CARD_COUNT]); +(* not used *) + +(* Theorem: !n. 0 < n /\ 0 NOTIN (IMAGE (\x. x MOD n) s) ==> CARD (IMAGE (\x. x MOD n) s) < n *) +(* Proof: + Let t = IMAGE (\x. x MOD n) s + Since t SUBSET count n by SUBSET_DEF, MOD_LESS + Now FINITE (count n) by FINITE_COUNT + and CARD (count n) = n by CARD_COUNT + So CARD t <= n by CARD_SUBSET + and FINITE t by SUBSET_FINITE + But 0 IN (count n) by IN_COUNT + yet 0 NOTIN t by given + Hence t <> (count n) by NOT_EQUAL_SETS + So CARD t <> n by SUBSET_EQ_CARD + Thus CARD t < n +*) +val card_mod_image_nonzero = store_thm( + "card_mod_image_nonzero", + ``!s n. 0 < n /\ 0 NOTIN (IMAGE (\x. x MOD n) s) ==> CARD (IMAGE (\x. x MOD n) s) < n``, + rpt strip_tac >> + qabbrev_tac `t = IMAGE (\x. x MOD n) s` >> + (`t SUBSET count n` by (rw[SUBSET_DEF, Abbr`t`] >> metis_tac[MOD_LESS])) >> + `FINITE (count n) /\ (CARD (count n) = n) ` by rw[] >> + `CARD t <= n` by metis_tac[CARD_SUBSET] >> + `0 IN (count n)` by rw[] >> + `t <> (count n)` by metis_tac[NOT_EQUAL_SETS] >> + `CARD t <> n` by metis_tac[SUBSET_EQ_CARD, SUBSET_FINITE] >> + decide_tac); +(* not used *) + +(* ------------------------------------------------------------------------- *) +(* Partition Property *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: FINITE s ==> !u v. s =|= u # v ==> ((u = {}) <=> (v = s)) *) +(* Proof: + If part: u = {} ==> v = s + Note s = {} UNION v by given, u = {} + = v by UNION_EMPTY + Only-if part: v = s ==> u = {} + Note FINITE u /\ FINITE v by FINITE_UNION + ==> CARD v = CARD u + CARD v by CARD_UNION_DISJOINT + ==> 0 = CARD u by arithmetic + Thus u = {} by CARD_EQ_0 +*) +val finite_partition_property = store_thm( + "finite_partition_property", + ``!s. FINITE s ==> !u v. s =|= u # v ==> ((u = {}) <=> (v = s))``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `FINITE u /\ FINITE v` by metis_tac[FINITE_UNION] >> + `CARD v = CARD u + CARD v` by metis_tac[CARD_UNION_DISJOINT] >> + `CARD u <> 0` by rw[CARD_EQ_0] >> + decide_tac); + +(* Theorem: FINITE s ==> !P. let u = {x | x IN s /\ P x} in let v = {x | x IN s /\ ~P x} in + FINITE u /\ FINITE v /\ s =|= u # v *) +(* Proof: + This is to show: + (1) FINITE u, true by SUBSET_DEF, SUBSET_FINITE + (2) FINITE v, true by SUBSET_DEF, SUBSET_FINITE + (3) u UNION v = s by IN_UNION + (4) DISJOINT u v, true by IN_DISJOINT, MEMBER_NOT_EMPTY +*) +Theorem finite_partition_by_predicate: + !s. FINITE s ==> + !P. let u = {x | x IN s /\ P x} ; + v = {x | x IN s /\ ~P x} + in + FINITE u /\ FINITE v /\ s =|= u # v +Proof + rw_tac std_ss[] >| [ + `u SUBSET s` by rw[SUBSET_DEF, Abbr`u`] >> + metis_tac[SUBSET_FINITE], + `v SUBSET s` by rw[SUBSET_DEF, Abbr`v`] >> + metis_tac[SUBSET_FINITE], + simp[EXTENSION, Abbr‘u’, Abbr‘v’] >> + metis_tac[], + simp[Abbr‘u’, Abbr‘v’, DISJOINT_DEF, EXTENSION] >> metis_tac[] + ] +QED + +(* Theorem: u SUBSET s ==> let v = s DIFF u in s =|= u # v *) +(* Proof: + This is to show: + (1) u SUBSET s ==> s = u UNION (s DIFF u), true by UNION_DIFF + (2) u SUBSET s ==> DISJOINT u (s DIFF u), true by DISJOINT_DIFF +*) +val partition_by_subset = store_thm( + "partition_by_subset", + ``!s u. u SUBSET s ==> let v = s DIFF u in s =|= u # v``, + rw[UNION_DIFF, DISJOINT_DIFF]); + +(* Theorem: x IN s ==> s =|= {x} # (s DELETE x) *) +(* Proof: + Note x IN s ==> {x} SUBSET s by SUBSET_DEF, IN_SING + and s DELETE x = s DIFF {x} by DELETE_DEF + Thus s =|= {x} # (s DELETE x) by partition_by_subset +*) +val partition_by_element = store_thm( + "partition_by_element", + ``!s x. x IN s ==> s =|= {x} # (s DELETE x)``, + metis_tac[partition_by_subset, DELETE_DEF, SUBSET_DEF, IN_SING]); + +(* ------------------------------------------------------------------------- *) +(* Splitting of a set *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: s =|= {} # t <=> (s = t) *) +(* Proof: + s =|= {} # t + <=> (s = {} UNION t) /\ (DISJOINT {} t) by notation + <=> (s = {} UNION t) /\ T by DISJOINT_EMPTY + <=> s = t by UNION_EMPTY +*) +val SPLIT_EMPTY = store_thm( + "SPLIT_EMPTY", + ``!s t. s =|= {} # t <=> (s = t)``, + rw[]); + +(* Theorem: s =|= u # v /\ v =|= a # b ==> s =|= u UNION a # b /\ u UNION a =|= u # a *) +(* Proof: + Note s =|= u # v <=> (s = u UNION v) /\ (DISJOINT u v) by notation + and v =|= a # b <=> (v = a UNION b) /\ (DISJOINT a b) by notation + Let c = u UNION a. + Then s = u UNION v by above + = u UNION (a UNION b) by above + = (u UNION a) UNION b by UNION_ASSOC + = c UNION b + Note DISJOINT u v + <=> DISJOINT u (a UNION b) + <=> DISJOINT (a UNION b) u by DISJOINT_SYM + <=> DISJOINT a u /\ DISJOINT b u by DISJOINT_UNION + <=> DISJOINT u a /\ DISJOINT u b by DISJOINT_SYM + + Thus DISJOINT c b + <=> DISJOINT (u UNION a) b by above + <=> DISJOINT u b /\ DISJOINT a b by DISJOINT_UNION + <=> T /\ T by above + <=> T + Therefore, + s =|= c # b by s = c UNION b /\ DISJOINT c b + and c =|= u # a by c = u UNION a /\ DISJOINT u a +*) +val SPLIT_UNION = store_thm( + "SPLIT_UNION", + ``!s u v a b. s =|= u # v /\ v =|= a # b ==> s =|= u UNION a # b /\ u UNION a =|= u # a``, + metis_tac[DISJOINT_UNION, DISJOINT_SYM, UNION_ASSOC]); + +(* Theorem: s =|= u # v <=> u SUBSET s /\ (v = s DIFF u) *) +(* Proof: + Note s =|= u # v <=> (s = u UNION v) /\ (DISJOINT u v) by notation + If part: s =|= u # v ==> u SUBSET s /\ (v = s DIFF u) + Note u SUBSET (u UNION v) by SUBSET_UNION + s DIFF u + = (u UNION v) DIFF u by s = u UNION v + = v DIFF u by DIFF_SAME_UNION + = v by DISJOINT_DIFF_IFF, DISJOINT_SYM + + Only-if part: u SUBSET s /\ (v = s DIFF u) ==> s =|= u # v + Note s = u UNION (s DIFF u) by UNION_DIFF, u SUBSET s + and DISJOINT u (s DIFF u) by DISJOINT_DIFF + Thus s =|= u # v by notation +*) +val SPLIT_EQ = store_thm( + "SPLIT_EQ", + ``!s u v. s =|= u # v <=> u SUBSET s /\ (v = s DIFF u)``, + rw[DISJOINT_DEF, SUBSET_DEF, EXTENSION] >> + metis_tac[]); + +(* Theorem: (s =|= u # v) = (s =|= v # u) *) +(* Proof: + s =|= u # v + = (s = u UNION v) /\ DISJOINT u v by notation + = (s = v UNION u) /\ DISJOINT u v by UNION_COMM + = (s = v UNION u) /\ DISJOINT v u by DISJOINT_SYM + = s =|= v # u by notation +*) +val SPLIT_SYM = store_thm( + "SPLIT_SYM", + ``!s u v. (s =|= u # v) = (s =|= v # u)``, + rw_tac std_ss[UNION_COMM, DISJOINT_SYM]); + +(* Theorem: (s =|= u # v) ==> (s =|= v # u) *) +(* Proof: by SPLIT_SYM *) +val SPLIT_SYM_IMP = store_thm( + "SPLIT_SYM_IMP", + ``!s u v. (s =|= u # v) ==> (s =|= v # u)``, + rw_tac std_ss[SPLIT_SYM]); + +(* Theorem: s =|= {x} # v <=> (x IN s /\ (v = s DELETE x)) *) +(* Proof: + s =|= {x} # v + <=> {x} SUBSET s /\ (v = s DIFF {x}) by SPLIT_EQ + <=> x IN s /\ (v = s DIFF {x}) by SUBSET_DEF + <=> x IN s /\ (v = s DELETE x) by DELETE_DEF +*) +val SPLIT_SING = store_thm( + "SPLIT_SING", + ``!s v x. s =|= {x} # v <=> (x IN s /\ (v = s DELETE x))``, + rw[SPLIT_EQ, SUBSET_DEF, DELETE_DEF]); + +(* Theorem: s =|= u # v ==> u SUBSET s /\ v SUBSET s *) +(* Proof: by SUBSET_UNION *) +Theorem SPLIT_SUBSETS: + !s u v. s =|= u # v ==> u SUBSET s /\ v SUBSET s +Proof + rw[] +QED + +(* Theorem: FINITE s /\ s =|= u # v ==> FINITE u /\ FINITE v *) +(* Proof: by SPLIT_SUBSETS, SUBSET_FINITE *) +Theorem SPLIT_FINITE: + !s u v. FINITE s /\ s =|= u # v ==> FINITE u /\ FINITE v +Proof + simp[SPLIT_SUBSETS, SUBSET_FINITE] +QED + +(* Theorem: FINITE s /\ s =|= u # v ==> (CARD s = CARD u + CARD v) *) +(* Proof: + Note FINITE u /\ FINITE v by SPLIT_FINITE + CARD s + = CARD (u UNION v) by notation + = CARD u + CARD v by CARD_UNION_DISJOINT +*) +Theorem SPLIT_CARD: + !s u v. FINITE s /\ s =|= u # v ==> (CARD s = CARD u + CARD v) +Proof + metis_tac[CARD_UNION_DISJOINT, SPLIT_FINITE] +QED + +(* Theorem: s =|= u # v <=> (u = s DIFF v) /\ (v = s DIFF u) *) +(* Proof: + If part: s =|= u # v ==> (u = s DIFF v) /\ (v = s DIFF u) + True by EXTENSION, IN_UNION, IN_DISJOINT, IN_DIFF. + Only-if part: (u = s DIFF v) /\ (v = s DIFF u) ==> s =|= u # v + True by EXTENSION, IN_UNION, IN_DISJOINT, IN_DIFF. +*) +val SPLIT_EQ_DIFF = store_thm( + "SPLIT_EQ_DIFF", + ``!s u v. s =|= u # v <=> (u = s DIFF v) /\ (v = s DIFF u)``, + rpt strip_tac >> + eq_tac >| [ + rpt strip_tac >| [ + rw[EXTENSION] >> + metis_tac[IN_UNION, IN_DISJOINT, IN_DIFF], + rw[EXTENSION] >> + metis_tac[IN_UNION, IN_DISJOINT, IN_DIFF] + ], + rpt strip_tac >| [ + rw[EXTENSION] >> + metis_tac[IN_UNION, IN_DIFF], + metis_tac[IN_DISJOINT, IN_DIFF] + ] + ]); + +(* Theorem alias *) +val SPLIT_BY_SUBSET = save_thm("SPLIT_BY_SUBSET", partition_by_subset); +(* val SPLIT_BY_SUBSET = |- !s u. u SUBSET s ==> (let v = s DIFF u in s =|= u # v): thm *) + +(* Theorem alias *) +val SUBSET_DIFF_DIFF = save_thm("SUBSET_DIFF_DIFF", DIFF_DIFF_SUBSET); +(* val SUBSET_DIFF_DIFF = |- !s t. t SUBSET s ==> (s DIFF (s DIFF t) = t) *) + +(* Theorem: s1 SUBSET t /\ s2 SUBSET t /\ (t DIFF s1 = t DIFF s2) ==> (s1 = s2) *) +(* Proof: + Let u = t DIFF s2. + Then u = t DIFF s1 by given + ==> t =|= u # s1 by SPLIT_BY_SUBSET, s1 SUBSET t + Thus s1 = t DIFF u by SPLIT_EQ + = t DIFF (t DIFF s2) by notation + = s2 by SUBSET_DIFF_DIFF, s2 SUBSET t +*) +val SUBSET_DIFF_EQ = store_thm( + "SUBSET_DIFF_EQ", + ``!s1 s2 t. s1 SUBSET t /\ s2 SUBSET t /\ (t DIFF s1 = t DIFF s2) ==> (s1 = s2)``, + metis_tac[SPLIT_BY_SUBSET, SPLIT_EQ, SUBSET_DIFF_DIFF]); + +(* ------------------------------------------------------------------------- *) +(* Bijective Inverses. *) +(* ------------------------------------------------------------------------- *) + +(* In pred_setTheory: +LINV_DEF |- !f s t. INJ f s t ==> !x. x IN s ==> (LINV f s (f x) = x) +BIJ_LINV_INV |- !f s t. BIJ f s t ==> !x. x IN t ==> (f (LINV f s x) = x) +BIJ_LINV_BIJ |- !f s t. BIJ f s t ==> BIJ (LINV f s) t s +RINV_DEF |- !f s t. SURJ f s t ==> !x. x IN t ==> (f (RINV f s x) = x) + +That's it, must be missing some! +Must assume: !y. y IN t ==> RINV f s y IN s +*) + +(* Theorem: BIJ f s t ==> !x. x IN t ==> (LINV f s x) IN s *) +(* Proof: + Since BIJ f s t ==> BIJ (LINV f s) t s by BIJ_LINV_BIJ + so x IN t ==> (LINV f s x) IN s by BIJ_DEF, INJ_DEF +*) +val BIJ_LINV_ELEMENT = store_thm( + "BIJ_LINV_ELEMENT", + ``!f s t. BIJ f s t ==> !x. x IN t ==> (LINV f s x) IN s``, + metis_tac[BIJ_LINV_BIJ, BIJ_DEF, INJ_DEF]); + +(* Theorem: (!x. x IN s ==> (LINV f s (f x) = x)) /\ (!x. x IN t ==> (f (LINV f s x) = x)) *) +(* Proof: + Since BIJ f s t ==> INJ f s t by BIJ_DEF + and INJ f s t ==> !x. x IN s ==> (LINV f s (f x) = x) by LINV_DEF + also BIJ f s t ==> !x. x IN t ==> (f (LINV f s x) = x) by BIJ_LINV_INV +*) +val BIJ_LINV_THM = store_thm( + "BIJ_LINV_THM", + ``!(f:'a -> 'b) s t. BIJ f s t ==> + (!x. x IN s ==> (LINV f s (f x) = x)) /\ (!x. x IN t ==> (f (LINV f s x) = x))``, + metis_tac[BIJ_DEF, LINV_DEF, BIJ_LINV_INV]); + +(* Theorem: BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> + !x. x IN s ==> (RINV f s (f x) = x) *) +(* Proof: + BIJ f s t means INJ f s t /\ SURJ f s t by BIJ_DEF + Hence x IN s ==> f x IN t by INJ_DEF or SURJ_DEF + ==> f (RINV f s (f x)) = f x by RINV_DEF, SURJ f s t + ==> RINV f s (f x) = x by INJ_DEF +*) +val BIJ_RINV_INV = store_thm( + "BIJ_RINV_INV", + ``!(f:'a -> 'b) s t. BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> + !x. x IN s ==> (RINV f s (f x) = x)``, + rw[BIJ_DEF] >> + `f x IN t` by metis_tac[INJ_DEF] >> + `f (RINV f s (f x)) = f x` by metis_tac[RINV_DEF] >> + metis_tac[INJ_DEF]); + +(* Theorem: BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> BIJ (RINV f s) t s *) +(* Proof: + By BIJ_DEF, this is to show: + (1) INJ (RINV f s) t s + By INJ_DEF, this is to show: + x IN t /\ y IN t /\ RINV f s x = RINV f s y ==> x = y + But SURJ f s t by BIJ_DEF + so f (RINV f s x) = x by RINV_DEF, SURJ f s t + and f (RINV f s y) = y by RINV_DEF, SURJ f s t + Thus x = y. + (2) SURJ (RINV f s) t s + By SURJ_DEF, this is to show: + x IN s ==> ?y. y IN t /\ (RINV f s y = x) + But INJ f s t by BIJ_DEF + so f x IN t by INJ_DEF + and RINV f s (f x) = x by BIJ_RINV_INV + Take y = f x to meet the criteria. +*) +val BIJ_RINV_BIJ = store_thm( + "BIJ_RINV_BIJ", + ``!(f:'a -> 'b) s t. BIJ f s t /\ (!y. y IN t ==> RINV f s y IN s) ==> BIJ (RINV f s) t s``, + rpt strip_tac >> + rw[BIJ_DEF] >- + metis_tac[INJ_DEF, BIJ_DEF, RINV_DEF] >> + rw[SURJ_DEF] >> + metis_tac[INJ_DEF, BIJ_DEF, BIJ_RINV_INV]); + +(* Theorem: INJ f t univ(:'b) /\ s SUBSET t ==> !x. x IN s ==> (LINV f t (f x) = x) *) +(* Proof: by LINV_DEF, SUBSET_DEF *) +Theorem LINV_SUBSET: + !(f:'a -> 'b) s t. INJ f t univ(:'b) /\ s SUBSET t ==> !x. x IN s ==> (LINV f t (f x) = x) +Proof + metis_tac[LINV_DEF, SUBSET_DEF] +QED + +(* ------------------------------------------------------------------------- *) +(* SUM_IMAGE and PROD_IMAGE Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: FINITE s ==> !f. (!x y. (f x = f y) ==> (x = y)) ==> (SIGMA f s = SIGMA I (IMAGE f s)) *) +(* Proof: + By finite induction on s. + Base case: SIGMA f {} = SIGMA I {} + SIGMA f {} + = 0 by SUM_IMAGE_THM + = SIGMA I {} by SUM_IMAGE_THM + = SUM_SET {} by SUM_SET_DEF + Step case: !f. (!x y. (f x = f y) ==> (x = y)) ==> (SIGMA f s = SUM_SET (IMAGE f s)) ==> + e NOTIN s ==> SIGMA f (e INSERT s) = SUM_SET (f e INSERT IMAGE f s) + Note FINITE s ==> FINITE (IMAGE f s) by IMAGE_FINITE + and e NOTIN s ==> f e NOTIN f s by the injective property + SIGMA f (e INSERT s) + = f e + SIGMA f (s DELETE e)) by SUM_IMAGE_THM + = f e + SIGMA f s by DELETE_NON_ELEMENT + = f e + SUM_SET (IMAGE f s)) by induction hypothesis + = f e + SUM_SET ((IMAGE f s) DELETE (f e)) by DELETE_NON_ELEMENT, f e NOTIN f s + = SUM_SET (f e INSERT IMAGE f s) by SUM_SET_THM +*) +val SUM_IMAGE_AS_SUM_SET = store_thm( + "SUM_IMAGE_AS_SUM_SET", + ``!s. FINITE s ==> !f. (!x y. (f x = f y) ==> (x = y)) ==> (SIGMA f s = SUM_SET (IMAGE f s))``, + HO_MATCH_MP_TAC FINITE_INDUCT >> + rw[SUM_SET_DEF] >- + rw[SUM_IMAGE_THM] >> + rw[SUM_IMAGE_THM, SUM_IMAGE_DELETE] >> + metis_tac[]); + +(* Theorem: x <> y ==> SIGMA f {x; y} = f x + f y *) +(* Proof: + Let s = {x; y}. + Then FINITE s by FINITE_UNION, FINITE_SING + and x INSERT s by INSERT_DEF + and s DELETE x = {y} by DELETE_DEF + SIGMA f s + = SIGMA f (x INSERT s) by above + = f x + SIGMA f (s DELETE x) by SUM_IMAGE_THM + = f x + SIGMA f {y} by above + = f x + f y by SUM_IMAGE_SING +*) +Theorem SUM_IMAGE_DOUBLET: + !f x y. x <> y ==> SIGMA f {x; y} = f x + f y +Proof + rpt strip_tac >> + qabbrev_tac `s = {x; y}` >> + `FINITE s` by fs[Abbr`s`] >> + `x INSERT s = s` by fs[Abbr`s`] >> + `s DELETE x = {x; y} DELETE x` by simp[Abbr`s`] >> + `_ = if y = x then {} else {y}` by EVAL_TAC >> + `_ = {y}` by simp[] >> + metis_tac[SUM_IMAGE_THM, SUM_IMAGE_SING] +QED + +(* Theorem: x <> y /\ y <> z /\ z <> x ==> SIGMA f {x; y; z} = f x + f y + f z *) +(* Proof: + Let s = {x; y; z}. + Then FINITE s by FINITE_UNION, FINITE_SING + and x INSERT s by INSERT_DEF + and s DELETE x = {y; z} by DELETE_DEF + SIGMA f s + = SIGMA f (x INSERT s) by above + = f x + SIGMA f (s DELETE x) by SUM_IMAGE_THM + = f x + SIGMA f {y; z} by above + = f x + f y + f z by SUM_IMAGE_DOUBLET +*) +Theorem SUM_IMAGE_TRIPLET: + !f x y z. x <> y /\ y <> z /\ z <> x ==> SIGMA f {x; y; z} = f x + f y + f z +Proof + rpt strip_tac >> + qabbrev_tac `s = {x; y; z}` >> + `FINITE s` by fs[Abbr`s`] >> + `x INSERT s = s` by fs[Abbr`s`] >> + `s DELETE x = {x; y; z} DELETE x` by simp[Abbr`s`] >> + `_ = if y = x then if z = x then {} else {z} + else y INSERT if z = x then {} else {z}` by EVAL_TAC >> + `_ = {y; z}` by simp[] >> + `SIGMA f s = f x + (f y + f z)` by metis_tac[SUM_IMAGE_THM, SUM_IMAGE_DOUBLET, SUM_IMAGE_SING] >> + decide_tac +QED + +(* +CARD_BIGUNION_SAME_SIZED_SETS +|- !n s. FINITE s /\ (!e. e IN s ==> FINITE e /\ CARD e = n) /\ + PAIR_DISJOINT s ==> CARD (BIGUNION s) = CARD s * n +*) + +(* Theorem: If n divides CARD e for all e in s, then n divides SIGMA CARD s. + FINITE s /\ (!e. e IN s ==> n divides (CARD e)) ==> n divides (SIGMA CARD s) *) +(* Proof: + Use finite induction and SUM_IMAGE_THM. + Base: n divides SIGMA CARD {} + Note SIGMA CARD {} = 0 by SUM_IMAGE_THM + and n divides 0 by ALL_DIVIDES_0 + Step: e NOTIN s /\ n divides (CARD e) ==> n divides SIGMA CARD (e INSERT s) + SIGMA CARD (e INSERT s) + = CARD e + SIGMA CARD (s DELETE e) by SUM_IMAGE_THM + = CARD e + SIGMA CARD s by DELETE_NON_ELEMENT + Note n divides (CARD e) by given + and n divides SIGMA CARD s by induction hypothesis + Thus n divides SIGMA CARD (e INSERT s) by DIVIDES_ADD_1 +*) +Theorem SIGMA_CARD_FACTOR: + !n s. FINITE s /\ (!e. e IN s ==> n divides (CARD e)) ==> n divides (SIGMA CARD s) +Proof + strip_tac >> + Induct_on `FINITE` >> + rw[] >- + rw[SUM_IMAGE_THM] >> + metis_tac[SUM_IMAGE_THM, DELETE_NON_ELEMENT, DIVIDES_ADD_1] +QED + +(* Theorem: FINITE s /\ t PSUBSET s /\ (!x. x IN s ==> f x <> 0) ==> SIGMA f t < SIGMA f s *) +(* Proof: + Note t SUBSET s /\ t <> s by PSUBSET_DEF + Thus SIGMA f t <= SIGMA f s by SUM_IMAGE_SUBSET_LE + By contradiction, suppose ~(SIGMA f t < SIGMA f s). + Then SIGMA f t = SIGMA f s by arithmetic [1] + + Let u = s DIFF t. + Then DISJOINT u t by DISJOINT_DIFF + and u UNION t = s by UNION_DIFF + Note FINITE u /\ FINITE t by FINITE_UNION + ==> SIGMA f s = SIGMA f u + SIGMA f t by SUM_IMAGE_DISJOINT + Thus SIGMA f u = 0 by arithmetic, [1] + + Now u SUBSET s by SUBSET_UNION + and u <> {} by finite_partition_property, t <> s + Thus ?x. x IN u by MEMBER_NOT_EMPTY + and f x <> 0 by SUBSET_DEF, implication + This contradicts f x = 0 by SUM_IMAGE_ZERO +*) +val SUM_IMAGE_PSUBSET_LT = store_thm( + "SUM_IMAGE_PSUBSET_LT", + ``!f s t. FINITE s /\ t PSUBSET s /\ (!x. x IN s ==> f x <> 0) ==> SIGMA f t < SIGMA f s``, + rw[PSUBSET_DEF] >> + `SIGMA f t <= SIGMA f s` by rw[SUM_IMAGE_SUBSET_LE] >> + spose_not_then strip_assume_tac >> + `SIGMA f t = SIGMA f s` by decide_tac >> + qabbrev_tac `u = s DIFF t` >> + `DISJOINT u t` by rw[DISJOINT_DIFF, Abbr`u`] >> + `u UNION t = s` by rw[UNION_DIFF, Abbr`u`] >> + `FINITE u /\ FINITE t` by metis_tac[FINITE_UNION] >> + `SIGMA f s = SIGMA f u + SIGMA f t` by rw[GSYM SUM_IMAGE_DISJOINT] >> + `SIGMA f u = 0` by decide_tac >> + `u SUBSET s` by rw[] >> + `u <> {}` by metis_tac[finite_partition_property] >> + metis_tac[SUM_IMAGE_ZERO, SUBSET_DEF, MEMBER_NOT_EMPTY]); + +(* Idea: Let s be a set of sets. If CARD s = SIGMA CARD s, + and all elements in s are non-empty, then all elements in s are SING. *) + +(* Theorem: FINITE s /\ (!e. e IN s ==> CARD e <> 0) ==> CARD s <= SIGMA CARD s *) +(* Proof: + By finite induction on set s. + Base: (!e. e IN {} ==> CARD e <> 0) ==> CARD {} <= SIGMA CARD {} + LHS = CARD {} = 0 by CARD_EMPTY + RHS = SIGMA CARD {} = 0 by SUM_IMAGE_EMPTY + Hence true. + Step: FINITE s /\ ((!e. e IN s ==> CARD e <> 0) ==> CARD s <= SIGMA CARD s) ==> + !e. e NOTIN s ==> + (!e'. e' IN e INSERT s ==> CARD e' <> 0) ==> + CARD (e INSERT s) <= SIGMA CARD (e INSERT s) + + Note !e'. e' IN s + ==> e' IN e INSERT s by IN_INSERT, e NOTIN s + ==> CARD e' <> 0 by implication, so induction hypothesis applies. + and CARD e <> 0 by e IN e INSERT s + CARD (e INSERT s) + = SUC (CARD s) by CARD_INSERT, e NOTIN s + = 1 + CARD s by SUC_ONE_ADD + + <= 1 + SIGMA CARD s by induction hypothesis + <= CARD e + SIGMA CARD s by 1 <= CARD e + = SIGMA (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s. +*) +Theorem card_le_sigma_card: + !s. FINITE s /\ (!e. e IN s ==> CARD e <> 0) ==> CARD s <= SIGMA CARD s +Proof + Induct_on `FINITE` >> + rw[] >> + `CARD e <> 0` by fs[] >> + `1 <= CARD e` by decide_tac >> + fs[] >> + simp[SUM_IMAGE_INSERT] +QED + +(* Theorem: FINITE s /\ (!e. e IN s ==> CARD e <> 0) /\ + CARD s = SIGMA CARD s ==> !e. e IN s ==> CARD e = 1 *) +(* Proof: + By finite induction on set s. + Base: (!e. e IN {} ==> CARD e <> 0) /\ CARD {} = SIGMA CARD {} ==> + !e. e IN {} ==> CARD e = 1 + Since e IN {} = F, this is trivially true. + Step: !s. FINITE s /\ + ((!e. e IN s ==> CARD e <> 0) /\ CARD s = SIGMA CARD s ==> + !e. e IN s ==> CARD e = 1) ==> + !e. e NOTIN s ==> + (!e'. e' IN e INSERT s ==> CARD e' <> 0) /\ + CARD (e INSERT s) = SIGMA CARD (e INSERT s) ==> + !e'. e' IN e INSERT s ==> CARD e' = 1 + Note !e'. e' IN s + ==> e' IN e INSERT s by IN_INSERT, e NOTIN s + ==> CARD e' <> 0 by implication, helps in induction hypothesis + Also e IN e INSERT s by IN_INSERT + so CARD e <> 0 by implication + + CARD e + CARD s + <= CARD e + SIGMA CARD s by card_le_sigma_card + = SIGMA CARD (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s + = CARD (e INSERT s) by given + = SUC (CARD s) by CARD_INSERT, e NOTIN s + = 1 + CARD s by SUC_ONE_ADD + Thus CARD e <= 1 by arithmetic + or CARD e = 1 by CARD e <> 0 + ==> CARD s = SIGMA CARD s by arithmetic, helps in induction hypothesis + Thus !e. e IN s ==> CARD e = 1 by induction hypothesis + and !e'. e' IN e INSERT s ==> CARD e' = 1 by CARD e = 1 +*) +Theorem card_eq_sigma_card: + !s. FINITE s /\ (!e. e IN s ==> CARD e <> 0) /\ + CARD s = SIGMA CARD s ==> !e. e IN s ==> CARD e = 1 +Proof + Induct_on `FINITE` >> + simp[] >> + ntac 6 strip_tac >> + `CARD e <> 0 /\ !e. e IN s ==> CARD e <> 0` by fs[] >> + imp_res_tac card_le_sigma_card >> + `CARD e + CARD s <= CARD e + SIGMA CARD s` by decide_tac >> + `CARD e + SIGMA CARD s = SIGMA CARD (e INSERT s)` by fs[SUM_IMAGE_INSERT] >> + `_ = 1 + CARD s` by rw[] >> + `CARD e <= 1` by fs[] >> + `CARD e = 1` by decide_tac >> + `CARD s = SIGMA CARD s` by fs[] >> + metis_tac[] +QED + +(* ------------------------------------------------------------------------- *) +(* SUM_SET and PROD_SET Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: FINITE s ==> !f. INJ f s UNIV ==> (SUM_SET (IMAGE f s) = SIGMA f s) *) +(* Proof: + By finite induction on s. + Base: SUM_SET (IMAGE f {}) = SIGMA f {} + SUM_SET (IMAGE f {}) + = SUM_SET {} by IMAGE_EMPTY + = 1 by SUM_SET_EMPTY + = SIGMA f {} by SUM_IMAGE_EMPTY + Step: !f. INJ f s univ(:num) ==> (SUM_SET (IMAGE f s) = SIGMA f s) ==> + e NOTIN s /\ INJ f (e INSERT s) univ(:num) ==> SUM_SET (IMAGE f (e INSERT s)) = SIGMA f (e INSERT s) + Note INJ f s univ(:num) by INJ_INSERT + and f e NOTIN (IMAGE f s) by IN_IMAGE + SUM_SET (IMAGE f (e INSERT s)) + = SUM_SET (f e INSERT (IMAGE f s)) by IMAGE_INSERT + = f e * SUM_SET (IMAGE f s) by SUM_SET_INSERT + = f e * SIGMA f s by induction hypothesis + = SIGMA f (e INSERT s) by SUM_IMAGE_INSERT +*) +val SUM_SET_IMAGE_EQN = store_thm( + "SUM_SET_IMAGE_EQN", + ``!s. FINITE s ==> !f. INJ f s UNIV ==> (SUM_SET (IMAGE f s) = SIGMA f s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[SUM_SET_EMPTY, SUM_IMAGE_EMPTY] >> + fs[INJ_INSERT] >> + `f e NOTIN (IMAGE f s)` by metis_tac[IN_IMAGE] >> + rw[SUM_SET_INSERT, SUM_IMAGE_INSERT]); + +(* Theorem: SUM_SET (count n) = (n * (n - 1)) DIV 2*) +(* Proof: + By induction on n. + Base case: SUM_SET (count 0) = 0 * (0 - 1) DIV 2 + LHS = SUM_SET (count 0) + = SUM_SET {} by COUNT_ZERO + = 0 by SUM_SET_THM + = 0 DIV 2 by ZERO_DIV + = 0 * (0 - 1) DIV 2 by MULT + = RHS + Step case: SUM_SET (count n) = n * (n - 1) DIV 2 ==> + SUM_SET (count (SUC n)) = SUC n * (SUC n - 1) DIV 2 + If n = 0, to show: SUM_SET (count 1) = 0 + SUM_SET (count 1) = SUM_SET {0} = 0 by SUM_SET_SING + If n <> 0, 0 < n. + First, FINITE (count n) by FINITE_COUNT + n NOTIN (count n) by IN_COUNT + LHS = SUM_SET (count (SUC n)) + = SUM_SET (n INSERT count n) by COUNT_SUC + = n + SUM_SET (count n DELETE n) by SUM_SET_THM + = n + SUM_SET (count n) by DELETE_NON_ELEMENT + = n + n * (n - 1) DIV 2 by induction hypothesis + = (n * 2 + n * (n - 1)) DIV 2 by ADD_DIV_ADD_DIV + = (n * (2 + (n - 1))) DIV 2 by LEFT_ADD_DISTRIB + = n * (n + 1) DIV 2 by arithmetic, 0 < n + = (SUC n - 1) * (SUC n) DIV 2 by ADD1, SUC_SUB1 + = SUC n * (SUC n - 1) DIV 2 by MULT_COMM + = RHS +*) +val SUM_SET_COUNT = store_thm( + "SUM_SET_COUNT", + ``!n. SUM_SET (count n) = (n * (n - 1)) DIV 2``, + Induct_on `n` >- + rw[] >> + Cases_on `n = 0` >| [ + rw[] >> + EVAL_TAC, + `0 < n` by decide_tac >> + `FINITE (count n)` by rw[] >> + `n NOTIN (count n)` by rw[] >> + `SUM_SET (count (SUC n)) = SUM_SET (n INSERT count n)` by rw[COUNT_SUC] >> + `_ = n + SUM_SET (count n DELETE n)` by rw[SUM_SET_THM] >> + `_ = n + SUM_SET (count n)` by metis_tac[DELETE_NON_ELEMENT] >> + `_ = n + n * (n - 1) DIV 2` by rw[] >> + `_ = (n * 2 + n * (n - 1)) DIV 2` by rw[ADD_DIV_ADD_DIV] >> + `_ = (n * (2 + (n - 1))) DIV 2` by rw[LEFT_ADD_DISTRIB] >> + `_ = n * (n + 1) DIV 2` by rw_tac arith_ss[] >> + `_ = (SUC n - 1) * SUC n DIV 2` by rw[ADD1, SUC_SUB1] >> + `_ = SUC n * (SUC n - 1) DIV 2 ` by rw[MULT_COMM] >> + decide_tac + ]); + +(* ------------------------------------------------------------------------- *) + + +(* Theorem: FINITE s ==> !f. INJ f s UNIV ==> (PROD_SET (IMAGE f s) = PI f s) *) +(* Proof: + By finite induction on s. + Base: PROD_SET (IMAGE f {}) = PI f {} + PROD_SET (IMAGE f {}) + = PROD_SET {} by IMAGE_EMPTY + = 1 by PROD_SET_EMPTY + = PI f {} by PROD_IMAGE_EMPTY + Step: !f. INJ f s univ(:num) ==> (PROD_SET (IMAGE f s) = PI f s) ==> + e NOTIN s /\ INJ f (e INSERT s) univ(:num) ==> PROD_SET (IMAGE f (e INSERT s)) = PI f (e INSERT s) + Note INJ f s univ(:num) by INJ_INSERT + and f e NOTIN (IMAGE f s) by IN_IMAGE + PROD_SET (IMAGE f (e INSERT s)) + = PROD_SET (f e INSERT (IMAGE f s)) by IMAGE_INSERT + = f e * PROD_SET (IMAGE f s) by PROD_SET_INSERT + = f e * PI f s by induction hypothesis + = PI f (e INSERT s) by PROD_IMAGE_INSERT +*) +val PROD_SET_IMAGE_EQN = store_thm( + "PROD_SET_IMAGE_EQN", + ``!s. FINITE s ==> !f. INJ f s UNIV ==> (PROD_SET (IMAGE f s) = PI f s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_SET_EMPTY, PROD_IMAGE_EMPTY] >> + fs[INJ_INSERT] >> + `f e NOTIN (IMAGE f s)` by metis_tac[IN_IMAGE] >> + rw[PROD_SET_INSERT, PROD_IMAGE_INSERT]); + +(* Theorem: PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** (SUM_SET (count m)) *) +(* Proof: + By induction on m. + Base case: PROD_SET (IMAGE (\j. n ** j) (count 0)) = n ** SUM_SET (count 0) + LHS = PROD_SET (IMAGE (\j. n ** j) (count 0)) + = PROD_SET (IMAGE (\j. n ** j) {}) by COUNT_ZERO + = PROD_SET {} by IMAGE_EMPTY + = 1 by PROD_SET_THM + RHS = n ** SUM_SET (count 0) + = n ** SUM_SET {} by COUNT_ZERO + = n ** 0 by SUM_SET_THM + = 1 by EXP + = LHS + Step case: PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** SUM_SET (count m) ==> + PROD_SET (IMAGE (\j. n ** j) (count (SUC m))) = n ** SUM_SET (count (SUC m)) + First, + FINITE (count m) by FINITE_COUNT + FINITE (IMAGE (\j. n ** j) (count m)) by IMAGE_FINITE + m NOTIN count m by IN_COUNT + and (\j. n ** j) m NOTIN IMAGE (\j. n ** j) (count m) by EXP_BASE_INJECTIVE, 1 < n + + LHS = PROD_SET (IMAGE (\j. n ** j) (count (SUC m))) + = PROD_SET (IMAGE (\j. n ** j) (m INSERT count m)) by COUNT_SUC + = n ** m * PROD_SET (IMAGE (\j. n ** j) (count m)) by PROD_SET_IMAGE_REDUCTION + = n ** m * n ** SUM_SET (count m) by induction hypothesis + = n ** (m + SUM_SET (count m)) by EXP_ADD + = n ** SUM_SET (m INSERT count m) by SUM_SET_INSERT + = n ** SUM_SET (count (SUC m)) by COUNT_SUC + = RHS +*) +val PROD_SET_IMAGE_EXP = store_thm( + "PROD_SET_IMAGE_EXP", + ``!n. 1 < n ==> !m. PROD_SET (IMAGE (\j. n ** j) (count m)) = n ** (SUM_SET (count m))``, + rpt strip_tac >> + Induct_on `m` >- + rw[PROD_SET_THM] >> + `FINITE (IMAGE (\j. n ** j) (count m))` by rw[] >> + `(\j. n ** j) m NOTIN IMAGE (\j. n ** j) (count m)` by rw[] >> + `m NOTIN count m` by rw[] >> + `PROD_SET (IMAGE (\j. n ** j) (count (SUC m))) = + PROD_SET (IMAGE (\j. n ** j) (m INSERT count m))` by rw[COUNT_SUC] >> + `_ = n ** m * PROD_SET (IMAGE (\j. n ** j) (count m))` by rw[PROD_SET_IMAGE_REDUCTION] >> + `_ = n ** m * n ** SUM_SET (count m)` by rw[] >> + `_ = n ** (m + SUM_SET (count m))` by rw[EXP_ADD] >> + `_ = n ** SUM_SET (m INSERT count m)` by rw[SUM_SET_INSERT] >> + `_ = n ** SUM_SET (count (SUC m))` by rw[COUNT_SUC] >> + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Partition and Equivalent Class *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: y IN equiv_class R s x <=> y IN s /\ R x y *) +(* Proof: by GSPECIFICATION *) +val equiv_class_element = store_thm( + "equiv_class_element", + ``!R s x y. y IN equiv_class R s x <=> y IN s /\ R x y``, + rw[]); + +(* Theorem: partition R {} = {} *) +(* Proof: by partition_def *) +val partition_on_empty = store_thm( + "partition_on_empty", + ``!R. partition R {} = {}``, + rw[partition_def]); + +(* +> partition_def; +val it = |- !R s. partition R s = {t | ?x. x IN s /\ (t = equiv_class R s x)}: thm +*) + +(* Theorem: t IN partition R s <=> ?x. x IN s /\ (t = equiv_class R s x) *) +(* Proof: by partition_def *) +Theorem partition_element: + !R s t. t IN partition R s <=> ?x. x IN s /\ (t = equiv_class R s x) +Proof + rw[partition_def] +QED + +(* Theorem: partition R s = IMAGE (equiv_class R s) s *) +(* Proof: + partition R s + = {t | ?x. x IN s /\ (t = {y | y IN s /\ R x y})} by partition_def + = {t | ?x. x IN s /\ (t = equiv_class R s x)} by notation + = IMAGE (equiv_class R s) s by IN_IMAGE +*) +val partition_elements = store_thm( + "partition_elements", + ``!R s. partition R s = IMAGE (equiv_class R s) s``, + rw[partition_def, EXTENSION] >> + metis_tac[]); + +(* Theorem alias *) +val partition_as_image = save_thm("partition_as_image", partition_elements); +(* val partition_as_image = + |- !R s. partition R s = IMAGE (\x. equiv_class R s x) s: thm *) + +(* Theorem: (R1 = R2) /\ (s1 = s2) ==> (partition R1 s1 = partition R2 s2) *) +(* Proof: by identity *) +val partition_cong = store_thm( + "partition_cong", + ``!R1 R2 s1 s2. (R1 = R2) /\ (s1 = s2) ==> (partition R1 s1 = partition R2 s2)``, + rw[]); +(* Just in case this is needed. *) + +(* +EMPTY_NOT_IN_partition +val it = |- R equiv_on s ==> {} NOTIN partition R s: thm +*) + +(* Theorem: R equiv_on s /\ e IN partition R s ==> e <> {} *) +(* Proof: by EMPTY_NOT_IN_partition. *) +Theorem partition_element_not_empty: + !R s e. R equiv_on s /\ e IN partition R s ==> e <> {} +Proof + metis_tac[EMPTY_NOT_IN_partition] +QED + +(* Theorem: R equiv_on s /\ x IN s ==> equiv_class R s x <> {} *) +(* Proof: + Note equiv_class R s x IN partition_element R s by partition_element + so equiv_class R s x <> {} by partition_element_not_empty +*) +Theorem equiv_class_not_empty: + !R s x. R equiv_on s /\ x IN s ==> equiv_class R s x <> {} +Proof + metis_tac[partition_element, partition_element_not_empty] +QED + +(* Theorem: R equiv_on s ==> (x IN s <=> ?e. e IN partition R s /\ x IN e) *) +(* Proof: + x IN s + <=> x IN (BIGUNION (partition R s)) by BIGUNION_partition + <=> ?e. e IN partition R s /\ x IN e by IN_BIGUNION +*) +Theorem partition_element_exists: + !R s x. R equiv_on s ==> (x IN s <=> ?e. e IN partition R s /\ x IN e) +Proof + rpt strip_tac >> + imp_res_tac BIGUNION_partition >> + metis_tac[IN_BIGUNION] +QED + +(* Theorem: When the partitions are equal size of n, CARD s = n * CARD (partition of s). + FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> + (CARD s = n * CARD (partition R s)) *) +(* Proof: + Note FINITE (partition R s) by FINITE_partition + so CARD s = SIGMA CARD (partition R s) by partition_CARD + = n * CARD (partition R s) by SIGMA_CARD_CONSTANT +*) +val equal_partition_card = store_thm( + "equal_partition_card", + ``!R s n. FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> + (CARD s = n * CARD (partition R s))``, + rw_tac std_ss[partition_CARD, FINITE_partition, GSYM SIGMA_CARD_CONSTANT]); + +(* Theorem: When the partitions are equal size of n, CARD s = n * CARD (partition of s). + FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> + n divides (CARD s) *) +(* Proof: by equal_partition_card, divides_def. *) +Theorem equal_partition_factor: + !R s n. FINITE s /\ R equiv_on s /\ (!e. e IN partition R s ==> (CARD e = n)) ==> + n divides (CARD s) +Proof + metis_tac[equal_partition_card, divides_def, MULT_COMM] +QED + +(* Theorem: When the partition size has a factor n, then n divides CARD s. + FINITE s /\ R equiv_on s /\ + (!e. e IN partition R s ==> n divides (CARD e)) ==> n divides (CARD s) *) +(* Proof: + Note FINITE (partition R s) by FINITE_partition + Thus CARD s = SIGMA CARD (partition R s) by partition_CARD + But !e. e IN partition R s ==> n divides (CARD e) + ==> n divides SIGMA CARD (partition R s) by SIGMA_CARD_FACTOR + Hence n divdes CARD s by above +*) +val factor_partition_card = store_thm( + "factor_partition_card", + ``!R s n. FINITE s /\ R equiv_on s /\ + (!e. e IN partition R s ==> n divides (CARD e)) ==> n divides (CARD s)``, + metis_tac[FINITE_partition, partition_CARD, SIGMA_CARD_FACTOR]); + +(* Note: +When a set s is partitioned by an equivalence relation R, +partition_CARD |- !R s. R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s)) +Can this be generalized to: f s = SIGMA f (partition R s) ? +If so, we can have (SIGMA f) s = SIGMA (SIGMA f) (partition R s) +Sort of yes, but have to use BIGUNION, and for a set_additive function f. +*) + +(* Overload every element finite of a superset *) +val _ = overload_on("EVERY_FINITE", ``\P. (!s. s IN P ==> FINITE s)``); + +(* +> FINITE_BIGUNION; +val it = |- !P. FINITE P /\ EVERY_FINITE P ==> FINITE (BIGUNION P): thm +*) + +(* Overload pairwise disjoint of a superset *) +val _ = overload_on("PAIR_DISJOINT", ``\P. (!s t. s IN P /\ t IN P /\ ~(s = t) ==> DISJOINT s t)``); + +(* +> partition_elements_disjoint; +val it = |- R equiv_on s ==> PAIR_DISJOINT (partition R s): thm +*) + +(* Theorem: t SUBSET s /\ PAIR_DISJOINT s ==> PAIR_DISJOINT t *) +(* Proof: by SUBSET_DEF *) +Theorem pair_disjoint_subset: + !s t. t SUBSET s /\ PAIR_DISJOINT s ==> PAIR_DISJOINT t +Proof + rw[SUBSET_DEF] +QED + +(* Overload an additive set function *) +val _ = overload_on("SET_ADDITIVE", + ``\f. (f {} = 0) /\ (!s t. FINITE s /\ FINITE t ==> (f (s UNION t) + f (s INTER t) = f s + f t))``); + +(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> + !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P) *) +(* Proof: + By finite induction on P. + Base: f (BIGUNION {}) = SIGMA f {} + f (BIGUNION {}) + = f {} by BIGUNION_EMPTY + = 0 by SET_ADDITIVE f + = SIGMA f {} = RHS by SUM_IMAGE_EMPTY + Step: e NOTIN P ==> f (BIGUNION (e INSERT P)) = SIGMA f (e INSERT P) + Given !s. s IN e INSERT P ==> FINITE s + thus !s. (s = e) \/ s IN P ==> FINITE s by IN_INSERT + hence FINITE e by implication + and EVERY_FINITE P by IN_INSERT + and FINITE (BIGUNION P) by FINITE_BIGUNION + Given PAIR_DISJOINT (e INSERT P) + so PAIR_DISJOINT P by IN_INSERT + and !s. s IN P ==> DISJOINT e s by IN_INSERT + hence DISJOINT e (BIGUNION P) by DISJOINT_BIGUNION + so e INTER (BIGUNION P) = {} by DISJOINT_DEF + and f (e INTER (BIGUNION P)) = 0 by SET_ADDITIVE f + f (BIGUNION (e INSERT P) + = f (BIGUNION (e INSERT P)) + f (e INTER (BIGUNION P)) by ADD_0 + = f e + f (BIGUNION P) by SET_ADDITIVE f + = f e + SIGMA f P by induction hypothesis + = f e + SIGMA f (P DELETE e) by DELETE_NON_ELEMENT + = SIGMA f (e INSERT P) by SUM_IMAGE_THM +*) +val disjoint_bigunion_add_fun = store_thm( + "disjoint_bigunion_add_fun", + ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> + !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P)``, + `!P. FINITE P ==> EVERY_FINITE P /\ PAIR_DISJOINT P ==> + !f. SET_ADDITIVE f ==> (f (BIGUNION P) = SIGMA f P)` suffices_by rw[] >> + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + rw_tac std_ss[BIGUNION_EMPTY, SUM_IMAGE_EMPTY] >> + rw_tac std_ss[BIGUNION_INSERT, SUM_IMAGE_THM] >> + `FINITE e /\ FINITE (BIGUNION P)` by rw[FINITE_BIGUNION] >> + `EVERY_FINITE P /\ PAIR_DISJOINT P` by rw[] >> + `!s. s IN P ==> DISJOINT e s` by metis_tac[IN_INSERT] >> + `f (e INTER (BIGUNION P)) = 0` by metis_tac[DISJOINT_DEF, DISJOINT_BIGUNION] >> + `f (e UNION BIGUNION P) = f (e UNION BIGUNION P) + f (e INTER (BIGUNION P))` by decide_tac >> + `_ = f e + f (BIGUNION P)` by metis_tac[] >> + `_ = f e + SIGMA f P` by prove_tac[] >> + metis_tac[DELETE_NON_ELEMENT]); + +(* Theorem: SET_ADDITIVE CARD *) +(* Proof: + Since CARD {} = 0 by CARD_EMPTY + and !s t. FINITE s /\ FINITE t + ==> CARD (s UNION t) + CARD (s INTER t) = CARD s + CARD t by CARD_UNION + Hence SET_ADDITIVE CARD by notation +*) +val set_additive_card = store_thm( + "set_additive_card", + ``SET_ADDITIVE CARD``, + rw[FUN_EQ_THM, CARD_UNION]); + +(* Note: DISJ_BIGUNION_CARD is only a prove_thm in pred_setTheoryScript.sml *) +(* +g `!P. FINITE P ==> EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P)` +e (PSet_ind.SET_INDUCT_TAC FINITE_INDUCT); +Q. use of this changes P to s, s to s', how? +*) + +(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P) *) +(* Proof: by disjoint_bigunion_add_fun, set_additive_card *) +val disjoint_bigunion_card = store_thm( + "disjoint_bigunion_card", + ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P)``, + rw[disjoint_bigunion_add_fun, set_additive_card]); + +(* Theorem alias *) +Theorem CARD_BIGUNION_PAIR_DISJOINT = disjoint_bigunion_card; +(* +val CARD_BIGUNION_PAIR_DISJOINT = + |- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> + CARD (BIGUNION P) = SIGMA CARD P: thm +*) + +(* Theorem: SET_ADDITIVE (SIGMA f) *) +(* Proof: + Since SIGMA f {} = 0 by SUM_IMAGE_EMPTY + and !s t. FINITE s /\ FINITE t + ==> SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t by SUM_IMAGE_UNION_EQN + Hence SET_ADDITIVE (SIGMA f) +*) +val set_additive_sigma = store_thm( + "set_additive_sigma", + ``!f. SET_ADDITIVE (SIGMA f)``, + rw[SUM_IMAGE_EMPTY, SUM_IMAGE_UNION_EQN]); + +(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> !f. SIGMA f (BIGUNION P) = SIGMA (SIGMA f) P *) +(* Proof: by disjoint_bigunion_add_fun, set_additive_sigma *) +val disjoint_bigunion_sigma = store_thm( + "disjoint_bigunion_sigma", + ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> !f. SIGMA f (BIGUNION P) = SIGMA (SIGMA f) P``, + rw[disjoint_bigunion_add_fun, set_additive_sigma]); + +(* Theorem: R equiv_on s /\ FINITE s ==> !f. SET_ADDITIVE f ==> (f s = SIGMA f (partition R s)) *) +(* Proof: + Let P = partition R s. + Then FINITE s + ==> FINITE P /\ !t. t IN P ==> FINITE t by FINITE_partition + and R equiv_on s + ==> BIGUNION P = s by BIGUNION_partition + ==> PAIR_DISJOINT P by partition_elements_disjoint + Hence f (BIGUNION P) = SIGMA f P by disjoint_bigunion_add_fun + or f s = SIGMA f P by above, BIGUNION_partition +*) +val set_add_fun_by_partition = store_thm( + "set_add_fun_by_partition", + ``!R s. R equiv_on s /\ FINITE s ==> !f. SET_ADDITIVE f ==> (f s = SIGMA f (partition R s))``, + rpt strip_tac >> + qabbrev_tac `P = partition R s` >> + `FINITE P /\ !t. t IN P ==> FINITE t` by metis_tac[FINITE_partition] >> + `BIGUNION P = s` by rw[BIGUNION_partition, Abbr`P`] >> + `PAIR_DISJOINT P` by metis_tac[partition_elements_disjoint] >> + rw[disjoint_bigunion_add_fun]); + +(* Theorem: R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s)) *) +(* Proof: by set_add_fun_by_partition, set_additive_card *) +val set_card_by_partition = store_thm( + "set_card_by_partition", + ``!R s. R equiv_on s /\ FINITE s ==> (CARD s = SIGMA CARD (partition R s))``, + rw[set_add_fun_by_partition, set_additive_card]); + +(* This is pred_setTheory.partition_CARD *) + +(* Theorem: R equiv_on s /\ FINITE s ==> !f. SIGMA f s = SIGMA (SIGMA f) (partition R s) *) +(* Proof: by set_add_fun_by_partition, set_additive_sigma *) +val set_sigma_by_partition = store_thm( + "set_sigma_by_partition", + ``!R s. R equiv_on s /\ FINITE s ==> !f. SIGMA f s = SIGMA (SIGMA f) (partition R s)``, + rw[set_add_fun_by_partition, set_additive_sigma]); + +(* Overload a multiplicative set function *) +val _ = overload_on("SET_MULTIPLICATIVE", + ``\f. (f {} = 1) /\ (!s t. FINITE s /\ FINITE t ==> (f (s UNION t) * f (s INTER t) = f s * f t))``); + +(* Theorem: FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> + !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P) *) +(* Proof: + By finite induction on P. + Base: f (BIGUNION {}) = PI f {} + f (BIGUNION {}) + = f {} by BIGUNION_EMPTY + = 1 by SET_MULTIPLICATIVE f + = PI f {} = RHS by PROD_IMAGE_EMPTY + Step: e NOTIN P ==> f (BIGUNION (e INSERT P)) = PI f (e INSERT P) + Given !s. s IN e INSERT P ==> FINITE s + thus !s. (s = e) \/ s IN P ==> FINITE s by IN_INSERT + hence FINITE e by implication + and EVERY_FINITE P by IN_INSERT + and FINITE (BIGUNION P) by FINITE_BIGUNION + Given PAIR_DISJOINT (e INSERT P) + so PAIR_DISJOINT P by IN_INSERT + and !s. s IN P ==> DISJOINT e s by IN_INSERT + hence DISJOINT e (BIGUNION P) by DISJOINT_BIGUNION + so e INTER (BIGUNION P) = {} by DISJOINT_DEF + and f (e INTER (BIGUNION P)) = 1 by SET_MULTIPLICATIVE f + f (BIGUNION (e INSERT P) + = f (BIGUNION (e INSERT P)) * f (e INTER (BIGUNION P)) by MULT_RIGHT_1 + = f e * f (BIGUNION P) by SET_MULTIPLICATIVE f + = f e * PI f P by induction hypothesis + = f e * PI f (P DELETE e) by DELETE_NON_ELEMENT + = PI f (e INSERT P) by PROD_IMAGE_THM +*) +val disjoint_bigunion_mult_fun = store_thm( + "disjoint_bigunion_mult_fun", + ``!P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> + !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P)``, + `!P. FINITE P ==> EVERY_FINITE P /\ PAIR_DISJOINT P ==> + !f. SET_MULTIPLICATIVE f ==> (f (BIGUNION P) = PI f P)` suffices_by rw[] >> + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + rw_tac std_ss[BIGUNION_EMPTY, PROD_IMAGE_EMPTY] >> + rw_tac std_ss[BIGUNION_INSERT, PROD_IMAGE_THM] >> + `FINITE e /\ FINITE (BIGUNION P)` by rw[FINITE_BIGUNION] >> + `EVERY_FINITE P /\ PAIR_DISJOINT P` by rw[] >> + `!s. s IN P ==> DISJOINT e s` by metis_tac[IN_INSERT] >> + `f (e INTER (BIGUNION P)) = 1` by metis_tac[DISJOINT_DEF, DISJOINT_BIGUNION] >> + `f (e UNION BIGUNION P) = f (e UNION BIGUNION P) * f (e INTER (BIGUNION P))` by metis_tac[MULT_RIGHT_1] >> + `_ = f e * f (BIGUNION P)` by metis_tac[] >> + `_ = f e * PI f P` by prove_tac[] >> + metis_tac[DELETE_NON_ELEMENT]); + +(* Theorem: R equiv_on s /\ FINITE s ==> !f. SET_MULTIPLICATIVE f ==> (f s = PI f (partition R s)) *) +(* Proof: + Let P = partition R s. + Then FINITE s + ==> FINITE P /\ EVERY_FINITE P by FINITE_partition + and R equiv_on s + ==> BIGUNION P = s by BIGUNION_partition + ==> PAIR_DISJOINT P by partition_elements_disjoint + Hence f (BIGUNION P) = PI f P by disjoint_bigunion_mult_fun + or f s = PI f P by above, BIGUNION_partition +*) +val set_mult_fun_by_partition = store_thm( + "set_mult_fun_by_partition", + ``!R s. R equiv_on s /\ FINITE s ==> !f. SET_MULTIPLICATIVE f ==> (f s = PI f (partition R s))``, + rpt strip_tac >> + qabbrev_tac `P = partition R s` >> + `FINITE P /\ !t. t IN P ==> FINITE t` by metis_tac[FINITE_partition] >> + `BIGUNION P = s` by rw[BIGUNION_partition, Abbr`P`] >> + `PAIR_DISJOINT P` by metis_tac[partition_elements_disjoint] >> + rw[disjoint_bigunion_mult_fun]); + +(* Theorem: FINITE s ==> !g. INJ g s univ(:'a) ==> !f. SIGMA f (IMAGE g s) = SIGMA (f o g) s *) +(* Proof: + By finite induction on s. + Base: SIGMA f (IMAGE g {}) = SIGMA (f o g) {} + LHS = SIGMA f (IMAGE g {}) + = SIGMA f {} by IMAGE_EMPTY + = 0 by SUM_IMAGE_EMPTY + = SIGMA (f o g) {} = RHS by SUM_IMAGE_EMPTY + Step: e NOTIN s ==> SIGMA f (IMAGE g (e INSERT s)) = SIGMA (f o g) (e INSERT s) + Note INJ g (e INSERT s) univ(:'a) + ==> INJ g s univ(:'a) /\ g e IN univ(:'a) /\ + !y. y IN s /\ (g e = g y) ==> (e = y) by INJ_INSERT + Thus g e NOTIN (IMAGE g s) by IN_IMAGE + SIGMA f (IMAGE g (e INSERT s)) + = SIGMA f (g e INSERT IMAGE g s) by IMAGE_INSERT + = f (g e) + SIGMA f (IMAGE g s) by SUM_IMAGE_THM, g e NOTIN (IMAGE g s) + = f (g e) + SIGMA (f o g) s by induction hypothesis + = (f o g) e + SIGMA (f o g) s by composition + = SIGMA (f o g) (e INSERT s) by SUM_IMAGE_THM, e NOTIN s +*) +val sum_image_by_composition = store_thm( + "sum_image_by_composition", + ``!s. FINITE s ==> !g. INJ g s univ(:'a) ==> !f. SIGMA f (IMAGE g s) = SIGMA (f o g) s``, + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + rw[SUM_IMAGE_EMPTY] >> + `INJ g s univ(:'a) /\ g e IN univ(:'a) /\ !y. y IN s /\ (g e = g y) ==> (e = y)` by metis_tac[INJ_INSERT] >> + `g e NOTIN (IMAGE g s)` by metis_tac[IN_IMAGE] >> + `(s DELETE e = s) /\ (IMAGE g s DELETE g e = IMAGE g s)` by metis_tac[DELETE_NON_ELEMENT] >> + rw[SUM_IMAGE_THM]); + +(* Overload on permutation *) +val _ = overload_on("PERMUTES", ``\f s. BIJ f s s``); +val _ = set_fixity "PERMUTES" (Infix(NONASSOC, 450)); (* same as relation *) + +(* Theorem: FINITE s ==> !g. g PERMUTES s ==> !f. SIGMA (f o g) s = SIGMA f s *) +(* Proof: + Given permutate g s = BIJ g s s by notation + ==> INJ g s s /\ SURJ g s s by BIJ_DEF + Now SURJ g s s ==> IMAGE g s = s by IMAGE_SURJ + Also s SUBSET univ(:'a) by SUBSET_UNIV + and s SUBSET s by SUBSET_REFL + Hence INJ g s univ(:'a) by INJ_SUBSET + With FINITE s, + SIGMA (f o g) s + = SIGMA f (IMAGE g s) by sum_image_by_composition + = SIGMA f s by above +*) +val sum_image_by_permutation = store_thm( + "sum_image_by_permutation", + ``!s. FINITE s ==> !g. g PERMUTES s ==> !f. SIGMA (f o g) s = SIGMA f s``, + rpt strip_tac >> + `INJ g s s /\ SURJ g s s` by metis_tac[BIJ_DEF] >> + `IMAGE g s = s` by rw[GSYM IMAGE_SURJ] >> + `s SUBSET univ(:'a)` by rw[SUBSET_UNIV] >> + `INJ g s univ(:'a)` by metis_tac[INJ_SUBSET, SUBSET_REFL] >> + `SIGMA (f o g) s = SIGMA f (IMAGE g s)` by rw[sum_image_by_composition] >> + rw[]); + +(* Theorem: FINITE s ==> !f:('b -> bool) -> num. (f {} = 0) ==> + !g. (!t. FINITE t /\ (!x. x IN t ==> g x <> {}) ==> INJ g t univ(:num -> bool)) ==> + (SIGMA f (IMAGE g s) = SIGMA (f o g) s) *) +(* Proof: + Let s1 = {x | x IN s /\ (g x = {})}, + s2 = {x | x IN s /\ (g x <> {})}. + Then s = s1 UNION s2 by EXTENSION + and DISJOINT s1 s2 by EXTENSION, DISJOINT_DEF + and DISJOINT (IMAGE g s1) (IMAGE g s2) by EXTENSION, DISJOINT_DEF + Now s1 SUBSET s /\ s1 SUBSET s by SUBSET_DEF + Since FINITE s by given + thus FINITE s1 /\ FINITE s2 by SUBSET_FINITE + and FINITE (IMAGE g s1) /\ FINITE (IMAGE g s2) by IMAGE_FINITE + + Step 1: decompose left summation + SIGMA f (IMAGE g s) + = SIGMA f (IMAGE g (s1 UNION s2)) by above, s = s1 UNION s2 + = SIGMA f ((IMAGE g s1) UNION (IMAGE g s2)) by IMAGE_UNION + = SIGMA f (IMAGE g s1) + SIGMA f (IMAGE g s2) by SUM_IMAGE_DISJOINT + + Claim: SIGMA f (IMAGE g s1) = 0 + Proof: If s1 = {}, + SIGMA f (IMAGE g {}) + = SIGMA f {} by IMAGE_EMPTY + = 0 by SUM_IMAGE_EMPTY + If s1 <> {}, + Note !x. x IN s1 ==> (g x = {}) by definition of s1 + Thus !y. y IN (IMAGE g s1) ==> (y = {}) by IN_IMAGE, IMAGE_EMPTY + Since s1 <> {}, IMAGE g s1 = {{}} by SING_DEF, IN_SING, SING_ONE_ELEMENT + SIGMA f (IMAGE g {}) + = SIGMA f {{}} by above + = f {} by SUM_IMAGE_SING + = 0 by given + + Step 2: decompose right summation + Also SIGMA (f o g) s + = SIGMA (f o g) (s1 UNION s2) by above, s = s1 UNION s2 + = SIGMA (f o g) s1 + SIGMA (f o g) s2 by SUM_IMAGE_DISJOINT + + Claim: SIGMA (f o g) s1 = 0 + Proof: Note !x. x IN s1 ==> (g x = {}) by definition of s1 + (f o g) x + = f (g x) by function application + = f {} by above + = 0 by given + Hence SIGMA (f o g) s1 + = 0 * CARD s1 by SIGMA_CONSTANT + = 0 by MULT + + Claim: SIGMA f (IMAGE g s2) = SIGMA (f o g) s2 + Proof: Note !x. x IN s2 ==> g x <> {} by definition of s2 + Thus INJ g s2 univ(:'b -> bool) by given + Hence SIGMA f (IMAGE g s2) + = SIGMA (f o g) (s2) by sum_image_by_composition + + Result follows by combining all the claims and arithmetic. +*) +val sum_image_by_composition_with_partial_inj = store_thm( + "sum_image_by_composition_with_partial_inj", + ``!s. FINITE s ==> !f:('b -> bool) -> num. (f {} = 0) ==> + !g. (!t. FINITE t /\ (!x. x IN t ==> g x <> {}) ==> INJ g t univ(:'b -> bool)) ==> + (SIGMA f (IMAGE g s) = SIGMA (f o g) s)``, + rpt strip_tac >> + qabbrev_tac `s1 = {x | x IN s /\ (g x = {})}` >> + qabbrev_tac `s2 = {x | x IN s /\ (g x <> {})}` >> + (`s = s1 UNION s2` by (rw[Abbr`s1`, Abbr`s2`, EXTENSION] >> metis_tac[])) >> + (`DISJOINT s1 s2` by (rw[Abbr`s1`, Abbr`s2`, EXTENSION, DISJOINT_DEF] >> metis_tac[])) >> + (`DISJOINT (IMAGE g s1) (IMAGE g s2)` by (rw[Abbr`s1`, Abbr`s2`, EXTENSION, DISJOINT_DEF] >> metis_tac[])) >> + `s1 SUBSET s /\ s2 SUBSET s` by rw[Abbr`s1`, Abbr`s2`, SUBSET_DEF] >> + `FINITE s1 /\ FINITE s2` by metis_tac[SUBSET_FINITE] >> + `FINITE (IMAGE g s1) /\ FINITE (IMAGE g s2)` by rw[] >> + `SIGMA f (IMAGE g s) = SIGMA f ((IMAGE g s1) UNION (IMAGE g s2))` by rw[] >> + `_ = SIGMA f (IMAGE g s1) + SIGMA f (IMAGE g s2)` by rw[SUM_IMAGE_DISJOINT] >> + `SIGMA f (IMAGE g s1) = 0` by + (Cases_on `s1 = {}` >- + rw[SUM_IMAGE_EMPTY] >> + `!x. x IN s1 ==> (g x = {})` by rw[Abbr`s1`] >> + `!y. y IN (IMAGE g s1) ==> (y = {})` by metis_tac[IN_IMAGE, IMAGE_EMPTY] >> + `{} IN {{}} /\ IMAGE g s1 <> {}` by rw[] >> + `IMAGE g s1 = {{}}` by metis_tac[SING_DEF, IN_SING, SING_ONE_ELEMENT] >> + `SIGMA f (IMAGE g s1) = f {}` by rw[SUM_IMAGE_SING] >> + rw[] + ) >> + `SIGMA (f o g) s = SIGMA (f o g) s1 + SIGMA (f o g) s2` by rw[SUM_IMAGE_DISJOINT] >> + `SIGMA (f o g) s1 = 0` by + (`!x. x IN s1 ==> (g x = {})` by rw[Abbr`s1`] >> + `!x. x IN s1 ==> ((f o g) x = 0)` by rw[] >> + metis_tac[SIGMA_CONSTANT, MULT]) >> + `SIGMA f (IMAGE g s2) = SIGMA (f o g) s2` by + (`!x. x IN s2 ==> g x <> {}` by rw[Abbr`s2`] >> + `INJ g s2 univ(:'b -> bool)` by rw[] >> + rw[sum_image_by_composition]) >> + decide_tac); + +(* Theorem: FINITE s ==> !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> + (SIGMA f (IMAGE g s) = SIGMA (f o g) s) *) +(* Proof: + By finite induction on s. + Base: SIGMA f (IMAGE g {}) = SIGMA (f o g) {} + SIGMA f (IMAGE g {}) + = SIGMA f {} by IMAGE_EMPTY + = 0 by SUM_IMAGE_EMPTY + = SIGMA (f o g) {} by SUM_IMAGE_EMPTY + Step: !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> + (SIGMA f (IMAGE g s) = SIGMA (f o g) s) ==> + e NOTIN s /\ !x y. x IN e INSERT s /\ y IN e INSERT s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0) + ==> SIGMA f (IMAGE g (e INSERT s)) = SIGMA (f o g) (e INSERT s) + Note !x y. ((x = e) \/ x IN s) /\ ((y = e) \/ y IN s) /\ (g x = g y) ==> + (x = y) \/ (f (g y) = 0) by IN_INSERT + If g e IN IMAGE g s, + Then ?x. x IN s /\ (g x = g e) by IN_IMAGE + and x <> e /\ (f (g e) = 0) by implication + SIGMA f (g e INSERT IMAGE g s) + = SIGMA f (IMAGE g s) by ABSORPTION, g e IN IMAGE g s + = SIGMA (f o g) s by induction hypothesis + = f (g x) + SIGMA (f o g) s by ADD + = (f o g) e + SIGMA (f o g) s by o_THM + = SIGMA (f o g) (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s + If g e NOTIN IMAGE g s, + SIGMA f (g e INSERT IMAGE g s) + = f (g e) + SIGMA f (IMAGE g s) by SUM_IMAGE_INSERT, g e NOTIN IMAGE g s + = f (g e) + SIGMA (f o g) s by induction hypothesis + = (f o g) e + SIGMA (f o g) s by o_THM + = SIGMA (f o g) (e INSERT s) by SUM_IMAGE_INSERT, e NOTIN s +*) +val sum_image_by_composition_without_inj = store_thm( + "sum_image_by_composition_without_inj", + ``!s. FINITE s ==> !f g. (!x y. x IN s /\ y IN s /\ (g x = g y) ==> (x = y) \/ (f (g x) = 0)) ==> + (SIGMA f (IMAGE g s) = SIGMA (f o g) s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[SUM_IMAGE_EMPTY] >> + fs[] >> + Cases_on `g e IN IMAGE g s` >| [ + `?x. x IN s /\ (g x = g e)` by metis_tac[IN_IMAGE] >> + `x <> e /\ (f (g e) = 0)` by metis_tac[] >> + `SIGMA f (g e INSERT IMAGE g s) = SIGMA f (IMAGE g s)` by metis_tac[ABSORPTION] >> + `_ = SIGMA (f o g) s` by rw[] >> + `_ = (f o g) e + SIGMA (f o g) s` by rw[] >> + `_ = SIGMA (f o g) (e INSERT s)` by rw[SUM_IMAGE_INSERT] >> + rw[], + `SIGMA f (g e INSERT IMAGE g s) = f (g e) + SIGMA f (IMAGE g s)` by rw[SUM_IMAGE_INSERT] >> + `_ = f (g e) + SIGMA (f o g) s` by rw[] >> + `_ = (f o g) e + SIGMA (f o g) s` by rw[] >> + `_ = SIGMA (f o g) (e INSERT s)` by rw[SUM_IMAGE_INSERT] >> + rw[] + ]); + +(* ------------------------------------------------------------------------- *) +(* Pre-image Theorems. *) +(* ------------------------------------------------------------------------- *) + +(* +- IN_IMAGE; +> val it = |- !y s f. y IN IMAGE f s <=> ?x. (y = f x) /\ x IN s : thm +*) + +(* Define preimage *) +val preimage_def = Define `preimage f s y = { x | x IN s /\ (f x = y) }`; + +(* Theorem: x IN (preimage f s y) <=> x IN s /\ (f x = y) *) +(* Proof: by preimage_def *) +val preimage_element = store_thm( + "preimage_element", + ``!f s x y. x IN (preimage f s y) <=> x IN s /\ (f x = y)``, + rw[preimage_def]); + +(* Theorem: x IN preimage f s y <=> (x IN s /\ (f x = y)) *) +(* Proof: by preimage_def *) +val in_preimage = store_thm( + "in_preimage", + ``!f s x y. x IN preimage f s y <=> (x IN s /\ (f x = y))``, + rw[preimage_def]); +(* same as theorem above. *) + +(* Theorem: (preimage f s y) SUBSET s *) +(* Proof: + x IN preimage f s y + <=> x IN s /\ f x = y by in_preimage + ==> x IN s + Thus (preimage f s y) SUBSET s by SUBSET_DEF +*) +Theorem preimage_subset: + !f s y. (preimage f s y) SUBSET s +Proof + simp[preimage_def, SUBSET_DEF] +QED + +(* Theorem: FINITE s ==> FINITE (preimage f s y) *) +(* Proof: + Note (preimage f s y) SUBSET s by preimage_subset + Thus FINITE (preimage f s y) by SUBSET_FINITE +*) +Theorem preimage_finite: + !f s y. FINITE s ==> FINITE (preimage f s y) +Proof + metis_tac[preimage_subset, SUBSET_FINITE] +QED + +(* Theorem: !x. x IN preimage f s y ==> f x = y *) +(* Proof: by definition. *) +val preimage_property = store_thm( + "preimage_property", + ``!f s y. !x. x IN preimage f s y ==> (f x = y)``, + rw[preimage_def]); + +(* This is bad: every pattern of f x = y (i.e. practically every equality!) will invoke the check: x IN preimage f s y! *) +(* val _ = export_rewrites ["preimage_property"]; *) + +(* Theorem: x IN s ==> x IN preimage f s (f x) *) +(* Proof: by IN_IMAGE. preimage_def. *) +val preimage_of_image = store_thm( + "preimage_of_image", + ``!f s x. x IN s ==> x IN preimage f s (f x)``, + rw[preimage_def]); + +(* Theorem: y IN (IMAGE f s) ==> CHOICE (preimage f s y) IN s /\ f (CHOICE (preimage f s y)) = y *) +(* Proof: + (1) prove: y IN IMAGE f s ==> CHOICE (preimage f s y) IN s + By IN_IMAGE, this is to show: + x IN s ==> CHOICE (preimage f s (f x)) IN s + Now, preimage f s (f x) <> {} since x is a pre-image. + hence CHOICE (preimage f s (f x)) IN preimage f s (f x) by CHOICE_DEF + hence CHOICE (preimage f s (f x)) IN s by preimage_def + (2) prove: y IN IMAGE f s /\ CHOICE (preimage f s y) IN s ==> f (CHOICE (preimage f s y)) = y + By IN_IMAGE, this is to show: x IN s ==> f (CHOICE (preimage f s (f x))) = f x + Now, x IN preimage f s (f x) by preimage_of_image + hence preimage f s (f x) <> {} by MEMBER_NOT_EMPTY + thus CHOICE (preimage f s (f x)) IN (preimage f s (f x)) by CHOICE_DEF + hence f (CHOICE (preimage f s (f x))) = f x by preimage_def +*) +val preimage_choice_property = store_thm( + "preimage_choice_property", + ``!f s y. y IN (IMAGE f s) ==> CHOICE (preimage f s y) IN s /\ (f (CHOICE (preimage f s y)) = y)``, + rpt gen_tac >> + strip_tac >> + conj_asm1_tac >| [ + full_simp_tac std_ss [IN_IMAGE] >> + `CHOICE (preimage f s (f x)) IN preimage f s (f x)` suffices_by rw[preimage_def] >> + metis_tac[CHOICE_DEF, preimage_of_image, MEMBER_NOT_EMPTY], + full_simp_tac std_ss [IN_IMAGE] >> + `x IN preimage f s (f x)` by rw_tac std_ss[preimage_of_image] >> + `CHOICE (preimage f s (f x)) IN (preimage f s (f x))` by metis_tac[CHOICE_DEF, MEMBER_NOT_EMPTY] >> + full_simp_tac std_ss [preimage_def, GSPECIFICATION] + ]); + +(* Theorem: INJ f s univ(:'b) ==> !x. x IN s ==> (preimage f s (f x) = {x}) *) +(* Proof: + preimage f s (f x) + = {x' | x' IN s /\ (f x' = f x)} by preimage_def + = {x' | x' IN s /\ (x' = x)} by INJ_DEF + = {x} by EXTENSION +*) +val preimage_inj = store_thm( + "preimage_inj", + ``!f s. INJ f s univ(:'b) ==> !x. x IN s ==> (preimage f s (f x) = {x})``, + rw[preimage_def, EXTENSION] >> + metis_tac[INJ_DEF]); + +(* Theorem: INJ f s univ(:'b) ==> !x. x IN s ==> (CHOICE (preimage f s (f x)) = x) *) +(* Proof: + CHOICE (preimage f s (f x)) + = CHOICE {x} by preimage_inj, INJ f s univ(:'b) + = x by CHOICE_SING +*) +val preimage_inj_choice = store_thm( + "preimage_inj_choice", + ``!f s. INJ f s univ(:'b) ==> !x. x IN s ==> (CHOICE (preimage f s (f x)) = x)``, + rw[preimage_inj]); + +(* Theorem: INJ (preimage f s) (IMAGE f s) (POW s) *) +(* Proof: + By INJ_DEF, this is to show: + (1) x IN s ==> preimage f s (f x) IN POW s + Let y = preimage f s (f x). + Then y SUBSET s by preimage_subset + so y IN (POW s) by IN_POW + (2) x IN s /\ y IN s /\ preimage f s (f x) = preimage f s (f y) ==> f x = f y + Note (f x) IN preimage f s (f x) by in_preimage + so (f y) IN preimage f s (f y) by given + Thus f x = f y by in_preimage +*) +Theorem preimage_image_inj: + !f s. INJ (preimage f s) (IMAGE f s) (POW s) +Proof + rw[INJ_DEF] >- + simp[preimage_subset, IN_POW] >> + metis_tac[in_preimage] +QED + +(* ------------------------------------------------------------------------- *) +(* Function Equivalence as Relation *) +(* ------------------------------------------------------------------------- *) + +(* For function f on a domain D, x, y in D are "equal" if f x = f y. *) +Definition fequiv_def: + fequiv x y f <=> (f x = f y) +End +Overload "==" = ``fequiv`` +val _ = set_fixity "==" (Infix(NONASSOC, 450)); + +(* Theorem: [Reflexive] (x == x) f *) +(* Proof: by definition, + and f x = f x. +*) +Theorem fequiv_refl[simp]: !f x. (x == x) f +Proof rw_tac std_ss[fequiv_def] +QED + +(* Theorem: [Symmetric] (x == y) f ==> (y == x) f *) +(* Proof: by defintion, + and f x = f y means the same as f y = f x. +*) +val fequiv_sym = store_thm( + "fequiv_sym", + ``!f x y. (x == y) f ==> (y == x) f``, + rw_tac std_ss[fequiv_def]); + +(* no export of commutativity *) + +(* Theorem: [Transitive] (x == y) f /\ (y == z) f ==> (x == z) f *) +(* Proof: by defintion, + and f x = f y + and f y = f z + implies f x = f z. +*) +val fequiv_trans = store_thm( + "fequiv_trans", + ``!f x y z. (x == y) f /\ (y == z) f ==> (x == z) f``, + rw_tac std_ss[fequiv_def]); + +(* Theorem: fequiv (==) is an equivalence relation on the domain. *) +(* Proof: by reflexive, symmetric and transitive. *) +val fequiv_equiv_class = store_thm( + "fequiv_equiv_class", + ``!f. (\x y. (x == y) f) equiv_on univ(:'a)``, + rw_tac std_ss[equiv_on_def, fequiv_def, EQ_IMP_THM]); + +(* ------------------------------------------------------------------------- *) +(* Function-based Equivalence *) +(* ------------------------------------------------------------------------- *) + +Overload feq = “flip (flip o fequiv)” +Overload feq_class[inferior] = “preimage” + +(* Theorem: x IN feq_class f s n <=> x IN s /\ (f x = n) *) +(* Proof: by feq_class_def *) +Theorem feq_class_element = in_preimage + +(* Note: + y IN equiv_class (feq f) s x +<=> y IN s /\ (feq f x y) by equiv_class_element +<=> y IN s /\ (f x = f y) by feq_def +*) + +(* Theorem: feq_class f s (f x) = equiv_class (feq f) s x *) +(* Proof: + feq_class f s (f x) + = {y | y IN s /\ (f y = f x)} by feq_class_def + = {y | y IN s /\ (f x = f y)} + = {y | y IN s /\ (feq f x y)} by feq_def + = equiv_class (feq f) s x by notation +*) +val feq_class_property = store_thm( + "feq_class_property", + ``!f s x. feq_class f s (f x) = equiv_class (feq f) s x``, + rw[in_preimage, EXTENSION, fequiv_def] >> metis_tac[]); + +(* Theorem: (feq_class f s) o f = equiv_class (feq f) s *) +(* Proof: by FUN_EQ_THM, feq_class_property *) +val feq_class_fun = store_thm( + "feq_class_fun", + ``!f s. (feq_class f s) o f = equiv_class (feq f) s``, + rw[FUN_EQ_THM, feq_class_property]); + +(* Theorem: feq f equiv_on s *) +(* Proof: by equiv_on_def, feq_def *) +val feq_equiv = store_thm( + "feq_equiv", + ``!s f. feq f equiv_on s``, + rw[equiv_on_def, fequiv_def] >> + metis_tac[]); + +(* Theorem: partition (feq f) s = IMAGE ((feq_class f s) o f) s *) +(* Proof: + Use partition_def |> ISPEC ``feq f`` |> ISPEC ``(s:'a -> bool)``; + + partition (feq f) s + = {t | ?x. x IN s /\ (t = {y | y IN s /\ feq f x y})} by partition_def + = {t | ?x. x IN s /\ (t = {y | y IN s /\ (f x = f y)})} by feq_def + = {t | ?x. x IN s /\ (t = feq_class f s (f x))} by feq_class_def + = {feq_class f s (f x) | x | x IN s } by rewriting + = IMAGE (feq_class f s) (IMAGE f s) by IN_IMAGE + = IMAGE ((feq_class f s) o f) s by IMAGE_COMPOSE +*) +val feq_partition = store_thm( + "feq_partition", + ``!s f. partition (feq f) s = IMAGE ((feq_class f s) o f) s``, + rw[partition_def, fequiv_def, in_preimage, EXTENSION, EQ_IMP_THM] >> + metis_tac[]); + +(* Theorem: t IN partition (feq f) s <=> ?z. z IN s /\ (!x. x IN t <=> x IN s /\ (f x = f z)) *) +(* Proof: by feq_partition, feq_class_def, EXTENSION *) +Theorem feq_partition_element: + !s f t. t IN partition (feq f) s <=> + ?z. z IN s /\ (!x. x IN t <=> x IN s /\ (f x = f z)) +Proof + rw[feq_partition, in_preimage, EXTENSION] >> metis_tac[] +QED + +(* Theorem: x IN s <=> ?e. e IN partition (feq f) s /\ x IN e *) +(* Proof: + Note (feq f) equiv_on s by feq_equiv + This result follows by partition_element_exists +*) +Theorem feq_partition_element_exists: + !f s x. x IN s <=> ?e. e IN partition (feq f) s /\ x IN e +Proof + simp[feq_equiv, partition_element_exists] +QED + +(* Theorem: e IN partition (feq f) s ==> e <> {} *) +(* Proof: + Note (feq f) equiv_on s by feq_equiv + so e <> {} by partition_element_not_empty +*) +Theorem feq_partition_element_not_empty: + !f s e. e IN partition (feq f) s ==> e <> {} +Proof + metis_tac[feq_equiv, partition_element_not_empty] +QED + +(* Theorem: partition (feq f) s = IMAGE (preimage f s o f) s *) +(* Proof: + x IN partition (feq f) s + <=> ?z. z IN s /\ !j. j IN x <=> j IN s /\ (f j = f z) by feq_partition_element + <=> ?z. z IN s /\ !j. j IN x <=> j IN (preimage f s (f z)) by preimage_element + <=> ?z. z IN s /\ (x = preimage f s (f z)) by EXTENSION + <=> ?z. z IN s /\ (x = (preimage f s o f) z) by composition (o_THM) + <=> x IN IMAGE (preimage f s o f) s by IN_IMAGE + Hence partition (feq f) s = IMAGE (preimage f s o f) s by EXTENSION + + or, + partition (feq f) s + = IMAGE (feq_class f s o f) s by feq_partition + = IMAGE (preiamge f s o f) s by feq_class_eq_preimage +*) +val feq_partition_by_preimage = feq_partition + +(* Theorem: FINITE s ==> !f g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s) *) +(* Proof: + Since (feq f) equiv_on s by feq_equiv + Hence !g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s) by set_sigma_by_partition +*) +val feq_sum_over_partition = store_thm( + "feq_sum_over_partition", + ``!s. FINITE s ==> !f g. SIGMA g s = SIGMA (SIGMA g) (partition (feq f) s)``, + rw[feq_equiv, set_sigma_by_partition]); + +(* Theorem: FINITE s ==> !f. CARD s = SIGMA CARD (partition (feq f) s) *) +(* Proof: + Note feq equiv_on s by feq_equiv + The result follows by partition_CARD +*) +val finite_card_by_feq_partition = store_thm( + "finite_card_by_feq_partition", + ``!s. FINITE s ==> !f. CARD s = SIGMA CARD (partition (feq f) s)``, + rw[feq_equiv, partition_CARD]); + +(* Theorem: FINITE s ==> !f. CARD s = SIGMA CARD (IMAGE ((preimage f s) o f) s) *) +(* Proof: + Note (feq f) equiv_on s by feq_equiv + CARD s + = SIGMA CARD (partition (feq f) s) by partition_CARD + = SIGMA CARD (IMAGE (preimage f s o f) s) by feq_partition_by_preimage +*) +val finite_card_by_image_preimage = store_thm( + "finite_card_by_image_preimage", + ``!s. FINITE s ==> !f. CARD s = SIGMA CARD (IMAGE ((preimage f s) o f) s)``, + rw[feq_equiv, partition_CARD, GSYM feq_partition]); + +(* Theorem: FINITE s /\ SURJ f s t ==> + CARD s = SIGMA CARD (IMAGE (preimage f s) t) *) +(* Proof: + CARD s + = SIGMA CARD (IMAGE (preimage f s o f) s) by finite_card_by_image_preimage + = SIGMA CARD (IMAGE (preimage f s) (IMAGE f s)) by IMAGE_COMPOSE + = SIGMA CARD (IMAGE (preimage f s) t) by IMAGE_SURJ +*) +Theorem finite_card_surj_by_image_preimage: + !f s t. FINITE s /\ SURJ f s t ==> + CARD s = SIGMA CARD (IMAGE (preimage f s) t) +Proof + rpt strip_tac >> + `CARD s = SIGMA CARD (IMAGE (preimage f s o f) s)` by rw[finite_card_by_image_preimage] >> + `_ = SIGMA CARD (IMAGE (preimage f s) (IMAGE f s))` by rw[IMAGE_COMPOSE] >> + `_ = SIGMA CARD (IMAGE (preimage f s) t)` by fs[IMAGE_SURJ] >> + simp[] +QED + +(* Theorem: BIJ (preimage f s) (IMAGE f s) (partition (feq f) s) *) +(* Proof: + Let g = preimage f s, t = IMAGE f s. + Note INJ g t (POW s) by preimage_image_inj + so BIJ g t (IMAGE g t) by INJ_IMAGE_BIJ + But IMAGE g t + = IMAGE (preimage f s) (IMAGE f s) by notation + = IMAGE (preimage f s o f) s by IMAGE_COMPOSE + = partition (feq f) s by feq_partition_by_preimage + Thus BIJ g t (partition (feq f) s) by above +*) +Theorem preimage_image_bij: + !f s. BIJ (preimage f s) (IMAGE f s) (partition (feq f) s) +Proof + rpt strip_tac >> + qabbrev_tac `g = preimage f s` >> + qabbrev_tac `t = IMAGE f s` >> + `BIJ g t (IMAGE g t)` by metis_tac[preimage_image_inj, INJ_IMAGE_BIJ] >> + simp[IMAGE_COMPOSE, feq_partition, Abbr`g`, Abbr`t`] +QED + +(* ------------------------------------------------------------------------- *) +(* Condition for surjection to be a bijection. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> SING e *) +(* Proof: + If part: e IN partition (feq f) s ==> SING e + e IN partition (feq f) s + <=> ?z. z IN s /\ !x. x IN e <=> x IN s /\ f x = f z + by feq_partition_element + Thus z IN e, so e <> {} by MEMBER_NOT_EMPTY + and !x. x IN e ==> x = z by INJ_DEF + so SING e by SING_ONE_ELEMENT + Only-if part: !e. e IN partition (feq f) s ==> SING e ==> INJ f s (IMAGE f s) + By INJ_DEF, IN_IMAGE, this is to show: + !x y. x IN s /\ y IN s /\ f x = f y ==> x = y + Note ?e. e IN (partition (feq f) s) /\ x IN e + by feq_partition_element_exists + and y IN e by feq_partition_element + then SING e by implication + so x = y by IN_SING +*) +Theorem inj_iff_partition_element_sing: + !f s. INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> SING e +Proof + rw[EQ_IMP_THM] >| [ + fs[feq_partition_element, INJ_DEF] >> + `e <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + simp[SING_ONE_ELEMENT], + rw[INJ_DEF] >> + `?e. e IN (partition (feq f) s) /\ x IN e` by fs[GSYM feq_partition_element_exists] >> + `y IN e` by metis_tac[feq_partition_element] >> + metis_tac[SING_DEF, IN_SING] + ] +QED + +(* Theorem: FINITE s ==> + (INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> CARD e = 1) *) +(* Proof: + INJ f s (IMAGE f s) + <=> !e. e IN (partition (feq f) s) ==> SING e by inj_iff_partition_element_sing + <=> !e. e IN (partition (feq f) s) ==> CARD e = 1 by FINITE_partition, CARD_EQ_1 +*) +Theorem inj_iff_partition_element_card_1: + !f s. FINITE s ==> + (INJ f s (IMAGE f s) <=> !e. e IN (partition (feq f) s) ==> CARD e = 1) +Proof + metis_tac[inj_iff_partition_element_sing, FINITE_partition, CARD_EQ_1] +QED + +(* Idea: for a finite domain, with target same size, surjection means injection. *) + +(* Theorem: FINITE s /\ CARD s = CARD t /\ SURJ f s t ==> INJ f s t *) +(* Proof: + Let p = partition (feq f) s. + Note IMAGE f s = t by IMAGE_SURJ + so FINITE t by IMAGE_FINITE + and CARD s = SIGMA CARD p by finite_card_by_feq_partition + and CARD t = CARD p by preimage_image_bij, bij_eq_card + Thus CARD p = SIGMA CARD p by given CARD s = CARD t + Now FINITE p by FINITE_partition + and !e. e IN p ==> FINITE e by FINITE_partition + and !e. e IN p ==> e <> {} by feq_partition_element_not_empty + so !e. e IN p ==> CARD e <> 0 by CARD_EQ_0 + Thus !e. e IN p ==> CARD e = 1 by card_eq_sigma_card + or INJ f s (IMAGE f s) by inj_iff_partition_element_card_1 + so INJ f s t by IMAGE f s = t +*) +Theorem FINITE_SURJ_IS_INJ: + !f s t. FINITE s /\ CARD s = CARD t /\ SURJ f s t ==> INJ f s t +Proof + rpt strip_tac >> + imp_res_tac finite_card_by_feq_partition >> + first_x_assum (qspec_then `f` strip_assume_tac) >> + qabbrev_tac `p = partition (feq f) s` >> + `IMAGE f s = t` by fs[IMAGE_SURJ] >> + `FINITE t` by rw[] >> + `CARD t = CARD p` by metis_tac[preimage_image_bij, FINITE_BIJ_CARD] >> + `FINITE p /\ !e. e IN p ==> FINITE e` by metis_tac[FINITE_partition] >> + `!e. e IN p ==> CARD e <> 0` by metis_tac[feq_partition_element_not_empty, CARD_EQ_0] >> + `!e. e IN p ==> CARD e = 1` by metis_tac[card_eq_sigma_card] >> + metis_tac[inj_iff_partition_element_card_1] +QED + +(* ------------------------------------------------------------------------- *) +(* Function Iteration *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < k /\ FUNPOW f k e = e ==> !n. FUNPOW f (n*k) e = e *) +(* Proof: + By induction on n: + Base case: FUNPOW f (0 * k) e = e + FUNPOW f (0 * k) e + = FUNPOW f 0 e by arithmetic + = e by FUNPOW_0 + Step case: FUNPOW f (n * k) e = e ==> FUNPOW f (SUC n * k) e = e + FUNPOW f (SUC n * k) e + = FUNPOW f (k + n * k) e by arithmetic + = FUNPOW f k (FUNPOW (n * k) e) by FUNPOW_ADD. + = FUNPOW f k e by induction hypothesis + = e by given +*) +val FUNPOW_MULTIPLE = store_thm( + "FUNPOW_MULTIPLE", + ``!f k e. 0 < k /\ (FUNPOW f k e = e) ==> !n. FUNPOW f (n*k) e = e``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + metis_tac[MULT_COMM, MULT_SUC, FUNPOW_ADD]); + +(* Theorem: 0 < k /\ FUNPOW f k e = e ==> !n. FUNPOW f n e = FUNPOW f (n MOD k) e *) +(* Proof: + FUNPOW f n e + = FUNPOW f ((n DIV k) * k + (n MOD k)) e by division algorithm + = FUNPOW f ((n MOD k) + (n DIV k) * k) e by arithmetic + = FUNPOW f (n MOD k) (FUNPOW (n DIV k) * k e) by FUNPOW_ADD + = FUNPOW f (n MOD k) e by FUNPOW_MULTIPLE +*) +val FUNPOW_MOD = store_thm( + "FUNPOW_MOD", + ``!f k e. 0 < k /\ (FUNPOW f k e = e) ==> !n. FUNPOW f n e = FUNPOW f (n MOD k) e``, + rpt strip_tac >> + `n = (n MOD k) + (n DIV k) * k` by metis_tac[DIVISION, ADD_COMM] >> + metis_tac[FUNPOW_ADD, FUNPOW_MULTIPLE]); + +(* Overload a RISING function (temporalizaed by Chun Tian) *) +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); + +(* Overload a FALLING function (temporalizaed by Chun Tian) *) +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); + +(* Theorem: RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x *) +(* Proof: + By induction on n. + Base: !m. m <= 0 ==> !x. FUNPOW f m x <= FUNPOW f 0 x + Note m = 0, and FUNPOW f 0 x <= FUNPOW f 0 x. + Step: !m. RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x ==> + !m. m <= SUC n ==> FUNPOW f m x <= FUNPOW f (SUC n) x + Note m <= n or m = SUC n. + If m = SUC n, this is trivial. + If m <= n, + FUNPOW f m x + <= FUNPOW f n x by induction hypothesis + <= f (FUNPOW f n x) by RISING f + = FUNPOW f (SUC n) x by FUNPOW_SUC +*) +val FUNPOW_LE_RISING = store_thm( + "FUNPOW_LE_RISING", + ``!f m n. RISING f /\ m <= n ==> !x. FUNPOW f m x <= FUNPOW f n x``, + strip_tac >> + Induct_on `n` >- + rw[] >> + rpt strip_tac >> + `(m <= n) \/ (m = SUC n)` by decide_tac >| [ + `FUNPOW f m x <= FUNPOW f n x` by rw[] >> + `FUNPOW f n x <= f (FUNPOW f n x)` by rw[] >> + `f (FUNPOW f n x) = FUNPOW f (SUC n) x` by rw[FUNPOW_SUC] >> + decide_tac, + rw[] + ]); + +(* Theorem: FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x *) +(* Proof: + By induction on n. + Base: !m. m <= 0 ==> !x. FUNPOW f 0 x <= FUNPOW f m x + Note m = 0, and FUNPOW f 0 x <= FUNPOW f 0 x. + Step: !m. FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x ==> + !m. m <= SUC n ==> FUNPOW f (SUC n) x <= FUNPOW f m x + Note m <= n or m = SUC n. + If m = SUC n, this is trivial. + If m <= n, + FUNPOW f (SUC n) x + = f (FUNPOW f n x) by FUNPOW_SUC + <= FUNPOW f n x by FALLING f + <= FUNPOW f m x by induction hypothesis +*) +val FUNPOW_LE_FALLING = store_thm( + "FUNPOW_LE_FALLING", + ``!f m n. FALLING f /\ m <= n ==> !x. FUNPOW f n x <= FUNPOW f m x``, + strip_tac >> + Induct_on `n` >- + rw[] >> + rpt strip_tac >> + `(m <= n) \/ (m = SUC n)` by decide_tac >| [ + `FUNPOW f (SUC n) x = f (FUNPOW f n x)` by rw[FUNPOW_SUC] >> + `f (FUNPOW f n x) <= FUNPOW f n x` by rw[] >> + `FUNPOW f n x <= FUNPOW f m x` by rw[] >> + decide_tac, + rw[] + ]); + +(* Theorem: (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x *) +(* Proof: + By induction on n. + Base: FUNPOW f 0 x <= FUNPOW g 0 x + FUNPOW f 0 x by FUNPOW_0 + = x + <= x = FUNPOW g 0 x by FUNPOW_0 + Step: FUNPOW f n x <= FUNPOW g n x ==> FUNPOW f (SUC n) x <= FUNPOW g (SUC n) x + FUNPOW f (SUC n) x + = f (FUNPOW f n x) by FUNPOW_SUC + <= g (FUNPOW f n x) by !x. f x <= g x + <= g (FUNPOW g n x) by induction hypothesis, MONO g + = FUNPOW g (SUC n) x by FUNPOW_SUC +*) +val FUNPOW_LE_MONO = store_thm( + "FUNPOW_LE_MONO", + ``!f g. (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + rw[FUNPOW_SUC] >> + `f (FUNPOW f n x) <= g (FUNPOW f n x)` by rw[] >> + `g (FUNPOW f n x) <= g (FUNPOW g n x)` by rw[] >> + decide_tac); + +(* Note: +There is no FUNPOW_LE_RMONO. FUNPOW_LE_MONO says: +|- !f g. (!x. f x <= g x) /\ MONO g ==> !n x. FUNPOW f n x <= FUNPOW g n x +To compare the terms in these two sequences: + x, f x, f (f x), f (f (f x)), ...... + x, g x, g (g x), g (g (g x)), ...... +For the first pair: x <= x. +For the second pair: f x <= g x, as g is cover. +For the third pair: f (f x) <= g (f x) by g is cover, + <= g (g x) by MONO g, and will not work if RMONO g. +*) + +(* Theorem: (!x. f x <= g x) /\ MONO f ==> !n x. FUNPOW f n x <= FUNPOW g n x *) +(* Proof: + By induction on n. + Base: FUNPOW f 0 x <= FUNPOW g 0 x + FUNPOW f 0 x by FUNPOW_0 + = x + <= x = FUNPOW g 0 x by FUNPOW_0 + Step: FUNPOW f n x <= FUNPOW g n x ==> FUNPOW f (SUC n) x <= FUNPOW g (SUC n) x + FUNPOW f (SUC n) x + = f (FUNPOW f n x) by FUNPOW_SUC + <= f (FUNPOW g n x) by induction hypothesis, MONO f + <= g (FUNPOW g n x) by !x. f x <= g x + = FUNPOW g (SUC n) x by FUNPOW_SUC +*) +val FUNPOW_GE_MONO = store_thm( + "FUNPOW_GE_MONO", + ``!f g. (!x. f x <= g x) /\ MONO f ==> !n x. FUNPOW f n x <= FUNPOW g n x``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + rw[FUNPOW_SUC] >> + `f (FUNPOW f n x) <= f (FUNPOW g n x)` by rw[] >> + `f (FUNPOW g n x) <= g (FUNPOW g n x)` by rw[] >> + decide_tac); + +(* Note: the name FUNPOW_SUC is taken: +FUNPOW_SUC |- !f n x. FUNPOW f (SUC n) x = f (FUNPOW f n x) +*) + +(* Theorem: FUNPOW SUC n m = m + n *) +(* Proof: + By induction on n. + Base: !m. FUNPOW SUC 0 m = m + 0 + LHS = FUNPOW SUC 0 m + = m by FUNPOW_0 + = m + 0 = RHS by ADD_0 + Step: !m. FUNPOW SUC n m = m + n ==> + !m. FUNPOW SUC (SUC n) m = m + SUC n + FUNPOW SUC (SUC n) m + = FUNPOW SUC n (SUC m) by FUNPOW + = (SUC m) + n by induction hypothesis + = m + SUC n by arithmetic +*) +val FUNPOW_ADD1 = store_thm( + "FUNPOW_ADD1", + ``!m n. FUNPOW SUC n m = m + n``, + Induct_on `n` >> + rw[FUNPOW]); + +(* Theorem: FUNPOW PRE n m = m - n *) +(* Proof: + By induction on n. + Base: !m. FUNPOW PRE 0 m = m - 0 + LHS = FUNPOW PRE 0 m + = m by FUNPOW_0 + = m + 0 = RHS by ADD_0 + Step: !m. FUNPOW PRE n m = m - n ==> + !m. FUNPOW PRE (SUC n) m = m - SUC n + FUNPOW PRE (SUC n) m + = FUNPOW PRE n (PRE m) by FUNPOW + = (PRE m) - n by induction hypothesis + = m - PRE n by arithmetic +*) +val FUNPOW_SUB1 = store_thm( + "FUNPOW_SUB1", + ``!m n. FUNPOW PRE n m = m - n``, + Induct_on `n` >- + rw[] >> + rw[FUNPOW]); + +(* Theorem: FUNPOW ($* b) n m = m * b ** n *) +(* Proof: + By induction on n. + Base: !m. !m. FUNPOW ($* b) 0 m = m * b ** 0 + LHS = FUNPOW ($* b) 0 m + = m by FUNPOW_0 + = m * 1 by MULT_RIGHT_1 + = m * b ** 0 = RHS by EXP_0 + Step: !m. FUNPOW ($* b) n m = m * b ** n ==> + !m. FUNPOW ($* b) (SUC n) m = m * b ** SUC n + FUNPOW ($* b) (SUC n) m + = FUNPOW ($* b) n (b * m) by FUNPOW + = b * m * b ** n by induction hypothesis + = m * (b * b ** n) by arithmetic + = m * b ** SUC n by EXP +*) +val FUNPOW_MUL = store_thm( + "FUNPOW_MUL", + ``!b m n. FUNPOW ($* b) n m = m * b ** n``, + strip_tac >> + Induct_on `n` >- + rw[] >> + rw[FUNPOW, EXP]); + +(* Theorem: 0 < b ==> (FUNPOW (combin$C $DIV b) n m = m DIV (b ** n)) *) +(* Proof: + By induction on n. + Let f = combin$C $DIV b. + Base: !m. FUNPOW f 0 m = m DIV b ** 0 + LHS = FUNPOW f 0 m + = m by FUNPOW_0 + = m DIV 1 by DIV_1 + = m DIV (b ** 0) = RHS by EXP_0 + Step: !m. FUNPOW f n m = m DIV b ** n ==> + !m. FUNPOW f (SUC n) m = m DIV b ** SUC n + FUNPOW f (SUC n) m + = FUNPOW f n (f m) by FUNPOW + = FUNPOW f n (m DIV b) by C_THM + = (m DIV b) DIV (b ** n) by induction hypothesis + = m DIV (b * b ** n) by DIV_DIV_DIV_MULT, 0 < b, 0 < b ** n + = m DIV b ** SUC n by EXP +*) +val FUNPOW_DIV = store_thm( + "FUNPOW_DIV", + ``!b m n. 0 < b ==> (FUNPOW (combin$C $DIV b) n m = m DIV (b ** n))``, + strip_tac >> + qabbrev_tac `f = combin$C $DIV b` >> + Induct_on `n` >- + rw[EXP_0] >> + rpt strip_tac >> + `FUNPOW f (SUC n) m = FUNPOW f n (m DIV b)` by rw[FUNPOW, Abbr`f`] >> + `_ = (m DIV b) DIV (b ** n)` by rw[] >> + `_ = m DIV (b * b ** n)` by rw[DIV_DIV_DIV_MULT] >> + `_ = m DIV b ** SUC n` by rw[EXP] >> + decide_tac); + +(* Theorem: FUNPOW SQ n m = m ** (2 ** n) *) +(* Proof: + By induction on n. + Base: !m. FUNPOW (\n. SQ n) 0 m = m ** 2 ** 0 + FUNPOW SQ 0 m + = m by FUNPOW_0 + = m ** 1 by EXP_1 + = m ** 2 ** 0 by EXP_0 + Step: !m. FUNPOW (\n. SQ n) n m = m ** 2 ** n ==> + !m. FUNPOW (\n. SQ n) (SUC n) m = m ** 2 ** SUC n + FUNPOW (\n. SQ n) (SUC n) m + = SQ (FUNPOW (\n. SQ n) n m) by FUNPOW_SUC + = SQ (m ** 2 ** n) by induction hypothesis + = (m ** 2 ** n) ** 2 by EXP_2 + = m ** (2 * 2 ** n) by EXP_EXP_MULT + = m ** 2 ** SUC n by EXP +*) +val FUNPOW_SQ = store_thm( + "FUNPOW_SQ", + ``!m n. FUNPOW SQ n m = m ** (2 ** n)``, + Induct_on `n` >- + rw[] >> + rw[FUNPOW_SUC, GSYM EXP_EXP_MULT, EXP]); + +(* Theorem: 0 < m /\ 0 < n ==> (FUNPOW (\n. (n * n) MOD m) n k = (k ** 2 ** n) MOD m) *) +(* Proof: + Lef f = (\n. SQ n MOD m). + By induction on n. + Base: !k. 0 < m /\ 0 < 0 ==> FUNPOW f 0 k = k ** 2 ** 0 MOD m + True since 0 < 0 = F. + Step: !k. 0 < m /\ 0 < n ==> FUNPOW f n k = k ** 2 ** n MOD m ==> + !k. 0 < m /\ 0 < SUC n ==> FUNPOW f (SUC n) k = k ** 2 ** SUC n MOD m + If n = 1, + FUNPOW f (SUC 0) k + = FUNPOW f 1 k by ONE + = f k by FUNPOW_1 + = SQ k MOD m by notation + = (k ** 2) MOD m by EXP_2 + = (k ** (2 ** 1)) MOD m by EXP_1 + If n <> 0, + FUNPOW f (SUC n) k + = f (FUNPOW f n k) by FUNPOW_SUC + = f (k ** 2 ** n MOD m) by induction hypothesis + = (k ** 2 ** n MOD m) * (k ** 2 ** n MOD m) MOD m by notation + = (k ** 2 ** n * k ** 2 ** n) MOD m by MOD_TIMES2 + = (k ** (2 ** n + 2 ** n)) MOD m by EXP_BASE_MULT + = (k ** (2 * 2 ** n)) MOD m by arithmetic + = (k ** 2 ** SUC n) MOD m by EXP +*) +val FUNPOW_SQ_MOD = store_thm( + "FUNPOW_SQ_MOD", + ``!m n k. 0 < m /\ 0 < n ==> (FUNPOW (\n. (n * n) MOD m) n k = (k ** 2 ** n) MOD m)``, + strip_tac >> + qabbrev_tac `f = \n. SQ n MOD m` >> + Induct >> + simp[] >> + rpt strip_tac >> + Cases_on `n = 0` >- + simp[Abbr`f`] >> + rw[FUNPOW_SUC, Abbr`f`] >> + `(k ** 2 ** n) ** 2 = k ** (2 * 2 ** n)` by rw[GSYM EXP_EXP_MULT] >> + `_ = k ** 2 ** SUC n` by rw[EXP] >> + rw[]); + +(* Theorem: 0 < n ==> (FUNPOW (\x. MAX x m) n k = MAX k m) *) +(* Proof: + By induction on n. + Base: !m k. 0 < 0 ==> FUNPOW (\x. MAX x m) 0 k = MAX k m + True by 0 < 0 = F. + Step: !m k. 0 < n ==> FUNPOW (\x. MAX x m) n k = MAX k m ==> + !m k. 0 < SUC n ==> FUNPOW (\x. MAX x m) (SUC n) k = MAX k m + If n = 0, + FUNPOW (\x. MAX x m) (SUC 0) k + = FUNPOW (\x. MAX x m) 1 k by ONE + = (\x. MAX x m) k by FUNPOW_1 + = MAX k m by function application + If n <> 0, + FUNPOW (\x. MAX x m) (SUC n) k + = f (FUNPOW (\x. MAX x m) n k) by FUNPOW_SUC + = (\x. MAX x m) (MAX k m) by induction hypothesis + = MAX (MAX k m) m by function application + = MAX k m by MAX_IS_MAX, m <= MAX k m +*) +val FUNPOW_MAX = store_thm( + "FUNPOW_MAX", + ``!m n k. 0 < n ==> (FUNPOW (\x. MAX x m) n k = MAX k m)``, + Induct_on `n` >- + simp[] >> + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + rw[FUNPOW_SUC] >> + `m <= MAX k m` by rw[] >> + rw[MAX_DEF]); + +(* Theorem: 0 < n ==> (FUNPOW (\x. MIN x m) n k = MIN k m) *) +(* Proof: + By induction on n. + Base: !m k. 0 < 0 ==> FUNPOW (\x. MIN x m) 0 k = MIN k m + True by 0 < 0 = F. + Step: !m k. 0 < n ==> FUNPOW (\x. MIN x m) n k = MIN k m ==> + !m k. 0 < SUC n ==> FUNPOW (\x. MIN x m) (SUC n) k = MIN k m + If n = 0, + FUNPOW (\x. MIN x m) (SUC 0) k + = FUNPOW (\x. MIN x m) 1 k by ONE + = (\x. MIN x m) k by FUNPOW_1 + = MIN k m by function application + If n <> 0, + FUNPOW (\x. MIN x m) (SUC n) k + = f (FUNPOW (\x. MIN x m) n k) by FUNPOW_SUC + = (\x. MIN x m) (MIN k m) by induction hypothesis + = MIN (MIN k m) m by function application + = MIN k m by MIN_IS_MIN, MIN k m <= m +*) +val FUNPOW_MIN = store_thm( + "FUNPOW_MIN", + ``!m n k. 0 < n ==> (FUNPOW (\x. MIN x m) n k = MIN k m)``, + Induct_on `n` >- + simp[] >> + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + rw[FUNPOW_SUC] >> + `MIN k m <= m` by rw[] >> + rw[MIN_DEF]); + +(* Theorem: FUNPOW (\(x,y). (f x, g y)) n (x,y) = (FUNPOW f n x, FUNPOW g n y) *) +(* Proof: + By induction on n. + Base: FUNPOW (\(x,y). (f x,g y)) 0 (x,y) = (FUNPOW f 0 x,FUNPOW g 0 y) + FUNPOW (\(x,y). (f x,g y)) 0 (x,y) + = (x,y) by FUNPOW_0 + = (FUNPOW f 0 x, FUNPOW g 0 y) by FUNPOW_0 + Step: FUNPOW (\(x,y). (f x,g y)) n (x,y) = (FUNPOW f n x,FUNPOW g n y) ==> + FUNPOW (\(x,y). (f x,g y)) (SUC n) (x,y) = (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y) + FUNPOW (\(x,y). (f x,g y)) (SUC n) (x,y) + = (\(x,y). (f x,g y)) (FUNPOW (\(x,y). (f x,g y)) n (x,y)) by FUNPOW_SUC + = (\(x,y). (f x,g y)) (FUNPOW f n x,FUNPOW g n y) by induction hypothesis + = (f (FUNPOW f n x),g (FUNPOW g n y)) by function application + = (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y) by FUNPOW_SUC +*) +val FUNPOW_PAIR = store_thm( + "FUNPOW_PAIR", + ``!f g n x y. FUNPOW (\(x,y). (f x, g y)) n (x,y) = (FUNPOW f n x, FUNPOW g n y)``, + rpt strip_tac >> + Induct_on `n` >> + rw[FUNPOW_SUC]); + +(* Theorem: FUNPOW (\(x,y,z). (f x, g y, h z)) n (x,y,z) = (FUNPOW f n x, FUNPOW g n y, FUNPOW h n z) *) +(* Proof: + By induction on n. + Base: FUNPOW (\(x,y,z). (f x,g y,h z)) 0 (x,y,z) = (FUNPOW f 0 x,FUNPOW g 0 y,FUNPOW h 0 z) + FUNPOW (\(x,y,z). (f x,g y,h z)) 0 (x,y,z) + = (x,y) by FUNPOW_0 + = (FUNPOW f 0 x, FUNPOW g 0 y, FUNPOW h 0 z) by FUNPOW_0 + Step: FUNPOW (\(x,y,z). (f x,g y,h z)) n (x,y,z) = + (FUNPOW f n x,FUNPOW g n y,FUNPOW h n z) ==> + FUNPOW (\(x,y,z). (f x,g y,h z)) (SUC n) (x,y,z) = + (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y,FUNPOW h (SUC n) z) + Let fun = (\(x,y,z). (f x,g y,h z)). + FUNPOW fun (SUC n) (x,y, z) + = fun (FUNPOW fun n (x,y,z)) by FUNPOW_SUC + = fun (FUNPOW f n x,FUNPOW g n y, FUNPOW h n z) by induction hypothesis + = (f (FUNPOW f n x),g (FUNPOW g n y), h (FUNPOW h n z)) by function application + = (FUNPOW f (SUC n) x,FUNPOW g (SUC n) y, FUNPOW h (SUC n) z) by FUNPOW_SUC +*) +val FUNPOW_TRIPLE = store_thm( + "FUNPOW_TRIPLE", + ``!f g h n x y z. FUNPOW (\(x,y,z). (f x, g y, h z)) n (x,y,z) = + (FUNPOW f n x, FUNPOW g n y, FUNPOW h n z)``, + rpt strip_tac >> + Induct_on `n` >> + rw[FUNPOW_SUC]); + + +(* Theorem: f PERMUTES s ==> (LINV f s) PERMUTES s *) +(* Proof: by BIJ_LINV_BIJ *) +Theorem LINV_permutes: + !f s. f PERMUTES s ==> (LINV f s) PERMUTES s +Proof + rw[BIJ_LINV_BIJ] +QED + +(* Theorem: f PERMUTES s ==> (FUNPOW f n) PERMUTES s *) +(* Proof: + By induction on n. + Base: FUNPOW f 0 PERMUTES s + Note FUNPOW f 0 = I by FUN_EQ_THM, FUNPOW_0 + and I PERMUTES s by BIJ_I_SAME + thus true. + Step: f PERMUTES s /\ FUNPOW f n PERMUTES s ==> + FUNPOW f (SUC n) PERMUTES s + Note FUNPOW f (SUC n) + = f o (FUNPOW f n) by FUN_EQ_THM, FUNPOW_SUC + Thus true by BIJ_COMPOSE +*) +Theorem FUNPOW_permutes: + !f s n. f PERMUTES s ==> (FUNPOW f n) PERMUTES s +Proof + rpt strip_tac >> + Induct_on `n` >| [ + `FUNPOW f 0 = I` by rw[FUN_EQ_THM] >> + simp[BIJ_I_SAME], + `FUNPOW f (SUC n) = f o (FUNPOW f n)` by rw[FUN_EQ_THM, FUNPOW_SUC] >> + metis_tac[BIJ_COMPOSE] + ] +QED + +(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s *) +(* Proof: + By induction on n. + Base: FUNPOW f 0 x IN s + Since FUNPOW f 0 x = x by FUNPOW_0 + This is trivially true. + Step: FUNPOW f n x IN s ==> FUNPOW f (SUC n) x IN s + FUNPOW f (SUC n) x + = f (FUNPOW f n x) by FUNPOW_SUC + But FUNPOW f n x IN s by induction hypothesis + so f (FUNPOW f n x) IN s by BIJ_ELEMENT, f PERMUTES s +*) +Theorem FUNPOW_closure: + !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s +Proof + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + metis_tac[FUNPOW_SUC, BIJ_ELEMENT] +QED + +(* Theorem: f PERMUTES s ==> FUNPOW (LINV f s) n PERMUTES s *) +(* Proof: by LINV_permutes, FUNPOW_permutes *) +Theorem FUNPOW_LINV_permutes: + !f s n. f PERMUTES s ==> FUNPOW (LINV f s) n PERMUTES s +Proof + simp[LINV_permutes, FUNPOW_permutes] +QED + +(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW f n x IN s *) +(* Proof: + By induction on n. + Base: FUNPOW (LINV f s) 0 x IN s + Since FUNPOW (LINV f s) 0 x = x by FUNPOW_0 + This is trivially true. + Step: FUNPOW (LINV f s) n x IN s ==> FUNPOW (LINV f s) (SUC n) x IN s + FUNPOW (LINV f s) (SUC n) x + = (LINV f s) (FUNPOW (LINV f s) n x) by FUNPOW_SUC + But FUNPOW (LINV f s) n x IN s by induction hypothesis + and (LINV f s) PERMUTES s by LINV_permutes + so (LINV f s) (FUNPOW (LINV f s) n x) IN s + by BIJ_ELEMENT +*) +Theorem FUNPOW_LINV_closure: + !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n x IN s +Proof + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + `(LINV f s) PERMUTES s` by rw[LINV_permutes] >> + prove_tac[FUNPOW_SUC, BIJ_ELEMENT] +QED + +(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW f n (FUNPOW (LINV f s) n x) = x *) +(* Proof: + By induction on n. + Base: FUNPOW f 0 (FUNPOW (LINV f s) 0 x) = x + FUNPOW f 0 (FUNPOW (LINV f s) 0 x) + = FUNPOW f 0 x by FUNPOW_0 + = x by FUNPOW_0 + Step: FUNPOW f n (FUNPOW (LINV f s) n x) = x ==> + FUNPOW f (SUC n) (FUNPOW (LINV f s) (SUC n) x) = x + Note (FUNPOW (LINV f s) n x) IN s by FUNPOW_LINV_closure + FUNPOW f (SUC n) (FUNPOW (LINV f s) (SUC n) x) + = FUNPOW f (SUC n) ((LINV f s) (FUNPOW (LINV f s) n x)) by FUNPOW_SUC + = FUNPOW f n (f ((LINV f s) (FUNPOW (LINV f s) n x))) by FUNPOW + = FUNPOW f n (FUNPOW (LINV f s) n x) by BIJ_LINV_THM + = x by induction hypothesis +*) +Theorem FUNPOW_LINV_EQ: + !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW f n (FUNPOW (LINV f s) n x) = x +Proof + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + `FUNPOW f (SUC n) (FUNPOW (LINV f s) (SUC n) x) + = FUNPOW f (SUC n) ((LINV f s) (FUNPOW (LINV f s) n x))` by rw[FUNPOW_SUC] >> + `_ = FUNPOW f n (f ((LINV f s) (FUNPOW (LINV f s) n x)))` by rw[FUNPOW] >> + `_ = FUNPOW f n (FUNPOW (LINV f s) n x)` by metis_tac[BIJ_LINV_THM, FUNPOW_LINV_closure] >> + simp[] +QED + +(* Theorem: f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n (FUNPOW f n x) = x *) +(* Proof: + By induction on n. + Base: FUNPOW (LINV f s) 0 (FUNPOW f 0 x) = x + FUNPOW (LINV f s) 0 (FUNPOW f 0 x) + = FUNPOW (LINV f s) 0 x by FUNPOW_0 + = x by FUNPOW_0 + Step: FUNPOW (LINV f s) n (FUNPOW f n x) = x ==> + FUNPOW (LINV f s) (SUC n) (FUNPOW f (SUC n) x) = x + Note (FUNPOW f n x) IN s by FUNPOW_closure + FUNPOW (LINV f s) (SUC n) (FUNPOW f (SUC n) x) + = FUNPOW (LINV f s) (SUC n) (f (FUNPOW f n x)) by FUNPOW_SUC + = FUNPOW (LINV f s) n ((LINV f s) (f (FUNPOW f n x))) by FUNPOW + = FUNPOW (LINV f s) n (FUNPOW f n x) by BIJ_LINV_THM + = x by induction hypothesis +*) +Theorem FUNPOW_EQ_LINV: + !f s x n. f PERMUTES s /\ x IN s ==> FUNPOW (LINV f s) n (FUNPOW f n x) = x +Proof + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + `FUNPOW (LINV f s) (SUC n) (FUNPOW f (SUC n) x) + = FUNPOW (LINV f s) (SUC n) (f (FUNPOW f n x))` by rw[FUNPOW_SUC] >> + `_ = FUNPOW (LINV f s) n ((LINV f s) (f (FUNPOW f n x)))` by rw[FUNPOW] >> + `_ = FUNPOW (LINV f s) n (FUNPOW f n x)` by metis_tac[BIJ_LINV_THM, FUNPOW_closure] >> + simp[] +QED + +(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW f (n - m) x = FUNPOW f n (FUNPOW (LINV f s) m x) *) +(* Proof: + FUNPOW f n (FUNPOW (LINV f s) m x) + = FUNPOW f (n - m + m) (FUNPOW (LINV f s) m x) by SUB_ADD, m <= n + = FUNPOW f (n - m) (FUNPOW f m (FUNPOW (LINV f s) m x)) by FUNPOW_ADD + = FUNPOW f (n - m) x by FUNPOW_LINV_EQ +*) +Theorem FUNPOW_SUB_LINV1: + !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW f (n - m) x = FUNPOW f n (FUNPOW (LINV f s) m x) +Proof + rpt strip_tac >> + `FUNPOW f n (FUNPOW (LINV f s) m x) + = FUNPOW f (n - m + m) (FUNPOW (LINV f s) m x)` by simp[] >> + `_ = FUNPOW f (n - m) (FUNPOW f m (FUNPOW (LINV f s) m x))` by rw[FUNPOW_ADD] >> + `_ = FUNPOW f (n - m) x` by rw[FUNPOW_LINV_EQ] >> + simp[] +QED + +(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW f (n - m) x = FUNPOW (LINV f s) m (FUNPOW f n x) *) +(* Proof: + Note FUNPOW f (n - m) x IN s by FUNPOW_closure + FUNPOW (LINV f s) m (FUNPOW f n x) + = FUNPOW (LINV f s) m (FUNPOW f (n - m + m) x) by SUB_ADD, m <= n + = FUNPOW (LINV f s) m (FUNPOW f (m + (n - m)) x) by ADD_COMM + = FUNPOW (LINV f s) m (FUNPOW f m (FUNPOW f (n - m) x)) by FUNPOW_ADD + = FUNPOW f (n - m) x by FUNPOW_EQ_LINV +*) +Theorem FUNPOW_SUB_LINV2: + !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW f (n - m) x = FUNPOW (LINV f s) m (FUNPOW f n x) +Proof + rpt strip_tac >> + `FUNPOW (LINV f s) m (FUNPOW f n x) + = FUNPOW (LINV f s) m (FUNPOW f (n - m + m) x)` by simp[] >> + `_ = FUNPOW (LINV f s) m (FUNPOW f (m + (n - m)) x)` by metis_tac[ADD_COMM] >> + `_ = FUNPOW (LINV f s) m (FUNPOW f m (FUNPOW f (n - m) x))` by rw[FUNPOW_ADD] >> + `_ = FUNPOW f (n - m) x` by rw[FUNPOW_EQ_LINV, FUNPOW_closure] >> + simp[] +QED + +(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW (LINV f s) (n - m) x = FUNPOW (LINV f s) n (FUNPOW f m x) *) +(* Proof: + FUNPOW (LINV f s) n (FUNPOW f m x) + = FUNPOW (LINV f s) (n - m + m) (FUNPOW f m x) by SUB_ADD, m <= n + = FUNPOW (LINV f s) (n - m) (FUNPOW (LINV f s) m (FUNPOW f m x)) by FUNPOW_ADD + = FUNPOW (LINV f s) (n - m) x by FUNPOW_EQ_LINV +*) +Theorem FUNPOW_LINV_SUB1: + !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW (LINV f s) (n - m) x = FUNPOW (LINV f s) n (FUNPOW f m x) +Proof + rpt strip_tac >> + `FUNPOW (LINV f s) n (FUNPOW f m x) + = FUNPOW (LINV f s) (n - m + m) (FUNPOW f m x)` by simp[] >> + `_ = FUNPOW (LINV f s) (n - m) (FUNPOW (LINV f s) m (FUNPOW f m x))` by rw[FUNPOW_ADD] >> + `_ = FUNPOW (LINV f s) (n - m) x` by rw[FUNPOW_EQ_LINV] >> + simp[] +QED + +(* Theorem: f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW (LINV f s) (n - m) x = FUNPOW f m (FUNPOW (LINV f s) n x) *) +(* Proof: + Note FUNPOW (LINV f s) (n - m) x IN s by FUNPOW_LINV_closure + FUNPOW f m (FUNPOW (LINV f s) n x) + = FUNPOW f m (FUNPOW (LINV f s) (n - m + m) x) by SUB_ADD, m <= n + = FUNPOW f m (FUNPOW (LINV f s) (m + (n - m)) x) by ADD_COMM + = FUNPOW f m (FUNPOW (LINV f s) m (FUNPOW (LINV f s) (n - m) x)) by FUNPOW_ADD + = FUNPOW (LINV f s) (n - m) x by FUNPOW_LINV_EQ +*) +Theorem FUNPOW_LINV_SUB2: + !f s x m n. f PERMUTES s /\ x IN s /\ m <= n ==> + FUNPOW (LINV f s) (n - m) x = FUNPOW f m (FUNPOW (LINV f s) n x) +Proof + rpt strip_tac >> + `FUNPOW f m (FUNPOW (LINV f s) n x) + = FUNPOW f m (FUNPOW (LINV f s) (n - m + m) x)` by simp[] >> + `_ = FUNPOW f m (FUNPOW (LINV f s) (m + (n - m)) x)` by metis_tac[ADD_COMM] >> + `_ = FUNPOW f m (FUNPOW (LINV f s) m (FUNPOW (LINV f s) (n - m) x))` by rw[FUNPOW_ADD] >> + `_ = FUNPOW (LINV f s) (n - m) x` by rw[FUNPOW_LINV_EQ, FUNPOW_LINV_closure] >> + simp[] +QED + +(* Theorem: f PERMUTES s /\ x IN s /\ y IN s ==> + (x = FUNPOW f n y <=> y = FUNPOW (LINV f s) n x) *) +(* Proof: + If part: x = FUNPOW f n y ==> y = FUNPOW (LINV f s) n x) + FUNPOW (LINV f s) n x) + = FUNPOW (LINV f s) n (FUNPOW f n y)) by x = FUNPOW f n y + = y by FUNPOW_EQ_LINV + Only-if part: y = FUNPOW (LINV f s) n x) ==> x = FUNPOW f n y + FUNPOW f n y + = FUNPOW f n (FUNPOW (LINV f s) n x)) by y = FUNPOW (LINV f s) n x) + = x by FUNPOW_LINV_EQ +*) +Theorem FUNPOW_LINV_INV: + !f s x y n. f PERMUTES s /\ x IN s /\ y IN s ==> + (x = FUNPOW f n y <=> y = FUNPOW (LINV f s) n x) +Proof + rw[EQ_IMP_THM] >- + rw[FUNPOW_EQ_LINV] >> + rw[FUNPOW_LINV_EQ] +QED + +(* ------------------------------------------------------------------------- *) +(* Euler Set and Totient Function Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: + natural n = IMAGE SUC (count n) + upto n = count (SUC n) +*) +(* Definitions and Theorems (# are exported, ! in computeLib): + + Residues: + residue_def |- !n. residue n = {i | 0 < i /\ i < n} + residue_element |- !n j. j IN residue n ==> 0 < j /\ j < n + residue_0 |- residue 0 = {} + residue_1 |- residue 1 = {} + residue_nonempty |- !n. 1 < n ==> residue n <> {} + residue_no_zero |- !n. 0 NOTIN residue n + residue_no_self |- !n. n NOTIN residue n +! residue_thm |- !n. residue n = count n DIFF {0} + residue_insert |- !n. 0 < n ==> (residue (SUC n) = n INSERT residue n) + residue_delete |- !n. 0 < n ==> (residue n DELETE n = residue n) + residue_suc |- !n. 0 < n ==> (residue (SUC n) = n INSERT residue n) + residue_count |- !n. 0 < n ==> (count n = 0 INSERT residue n) + residue_finite |- !n. FINITE (residue n) + residue_card |- !n. 0 < n ==> (CARD (residue n) = n - 1) + residue_prime_neq |- !p a n. prime p /\ a IN residue p /\ n <= p ==> + !x. x IN residue n ==> (a * n) MOD p <> (a * x) MOD p + prod_set_residue |- !n. PROD_SET (residue n) = FACT (n - 1) + + Naturals: + natural_element |- !n j. j IN natural n <=> 0 < j /\ j <= n + natural_property |- !n. natural n = {j | 0 < j /\ j <= n} + natural_finite |- !n. FINITE (natural n) + natural_card |- !n. CARD (natural n) = n + natural_not_0 |- !n. 0 NOTIN natural n + natural_0 |- natural 0 = {} + natural_1 |- natural 1 = {1} + natural_has_1 |- !n. 0 < n ==> 1 IN natural n + natural_has_last |- !n. 0 < n ==> n IN natural n + natural_suc |- !n. natural (SUC n) = SUC n INSERT natural n + natural_thm |- !n. natural n = set (GENLIST SUC n) + natural_divisor_natural |- !n a b. 0 < n /\ a IN natural n /\ b divides a ==> b IN natural n + natural_cofactor_natural |- !n a b. 0 < n /\ 0 < a /\ b IN natural n /\ a divides b ==> + b DIV a IN natural n + natural_cofactor_natural_reduced + |- !n a b. 0 < n /\ a divides n /\ b IN natural n /\ a divides b ==> + b DIV a IN natural (n DIV a) + + Uptos: + upto_finite |- !n. FINITE (upto n) + upto_card |- !n. CARD (upto n) = SUC n + upto_has_last |- !n. n IN upto n + upto_delete |- !n. upto n DELETE n = count n + upto_split_first |- !n. upto n = {0} UNION natural n + upto_split_last |- !n. upto n = count n UNION {n} + upto_by_count |- !n. upto n = n INSERT count n + upto_by_natural |- !n. upto n = 0 INSERT natural n + natural_by_upto |- !n. natural n = upto n DELETE 0 + + Euler Set and Totient Function: + Euler_def |- !n. Euler n = {i | 0 < i /\ i < n /\ coprime n i} + totient_def |- !n. totient n = CARD (Euler n) + Euler_element |- !n x. x IN Euler n <=> 0 < x /\ x < n /\ coprime n x +! Euler_thm |- !n. Euler n = residue n INTER {j | coprime j n} + Euler_finite |- !n. FINITE (Euler n) + Euler_0 |- Euler 0 = {} + Euler_1 |- Euler 1 = {} + Euler_has_1 |- !n. 1 < n ==> 1 IN Euler n + Euler_nonempty |- !n. 1 < n ==> Euler n <> {} + Euler_empty |- !n. (Euler n = {}) <=> (n = 0 \/ n = 1) + Euler_card_upper_le |- !n. totient n <= n + Euler_card_upper_lt |- !n. 1 < n ==> totient n < n + Euler_card_bounds |- !n. totient n <= n /\ (1 < n ==> 0 < totient n /\ totient n < n) + Euler_prime |- !p. prime p ==> (Euler p = residue p) + Euler_card_prime |- !p. prime p ==> (totient p = p - 1) + + Summation of Geometric Sequence: + sigma_geometric_natural_eqn |- !p. 0 < p ==> + !n. (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) + sigma_geometric_natural |- !p. 1 < p ==> + !n. SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) DIV (p - 1) + + Chinese Remainder Theorem: + mod_mod_prod_eq |- !m n a b. 0 < m /\ 0 < n /\ a MOD (m * n) = b MOD (m * n) ==> + a MOD m = b MOD m /\ a MOD n = b MOD n + coprime_mod_mod_prod_eq + |- !m n a b. 0 < m /\ 0 < n /\ coprime m n /\ + a MOD m = b MOD m /\ a MOD n = b MOD n ==> + a MOD (m * n) = b MOD (m * n) + coprime_mod_mod_prod_eq_iff + |- !m n. 0 < m /\ 0 < n /\ coprime m n ==> + !a b. a MOD (m * n) = b MOD (m * n) <=> + a MOD m = b MOD m /\ a MOD n = b MOD n + coprime_mod_mod_solve + |- !m n a b. 0 < m /\ 0 < n /\ coprime m n ==> + ?!x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n + + Useful Theorems: + PROD_SET_IMAGE_EXP_NONZERO |- !n m. PROD_SET (IMAGE (\j. n ** j) (count m)) = + PROD_SET (IMAGE (\j. n ** j) (residue m)) +*) + +(* ------------------------------------------------------------------------- *) +(* Residues -- close-relative of COUNT *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of residues = nonzero remainders *) +val residue_def = zDefine `residue n = { i | (0 < i) /\ (i < n) }`; +(* use zDefine as this is not computationally effective. *) + +(* Theorem: j IN residue n ==> 0 < j /\ j < n *) +(* Proof: by residue_def. *) +val residue_element = store_thm( + "residue_element", + ``!n j. j IN residue n ==> 0 < j /\ j < n``, + rw[residue_def]); + +(* Theorem: residue 0 = EMPTY *) +(* Proof: by residue_def *) +Theorem residue_0: + residue 0 = {} +Proof + simp[residue_def] +QED + +(* Theorem: residue 1 = EMPTY *) +(* Proof: by residue_def. *) +Theorem residue_1: + residue 1 = {} +Proof + simp[residue_def] +QED + +(* Theorem: 1 < n ==> residue n <> {} *) +(* Proof: + By residue_def, this is to show: 1 < n ==> ?x. x <> 0 /\ x < n + Take x = 1, this is true. +*) +val residue_nonempty = store_thm( + "residue_nonempty", + ``!n. 1 < n ==> residue n <> {}``, + rw[residue_def, EXTENSION] >> + metis_tac[DECIDE``1 <> 0``]); + +(* Theorem: 0 NOTIN residue n *) +(* Proof: by residue_def *) +Theorem residue_no_zero: + !n. 0 NOTIN residue n +Proof + simp[residue_def] +QED + +(* Theorem: n NOTIN residue n *) +(* Proof: by residue_def *) +Theorem residue_no_self: + !n. n NOTIN residue n +Proof + simp[residue_def] +QED + +(* Theorem: residue n = (count n) DIFF {0} *) +(* Proof: + residue n + = {i | 0 < i /\ i < n} by residue_def + = {i | i < n /\ i <> 0} by NOT_ZERO_LT_ZERO + = {i | i < n} DIFF {0} by IN_DIFF + = (count n) DIFF {0} by count_def +*) +val residue_thm = store_thm( + "residue_thm[compute]", + ``!n. residue n = (count n) DIFF {0}``, + rw[residue_def, EXTENSION]); +(* This is effective, put in computeLib. *) + +(* +> EVAL ``residue 10``; +val it = |- residue 10 = {9; 8; 7; 6; 5; 4; 3; 2; 1}: thm +*) + +(* Theorem: For n > 0, residue (SUC n) = n INSERT residue n *) +(* Proof: + residue (SUC n) + = {1, 2, ..., n} + = n INSERT {1, 2, ..., (n-1) } + = n INSERT residue n +*) +val residue_insert = store_thm( + "residue_insert", + ``!n. 0 < n ==> (residue (SUC n) = n INSERT residue n)``, + srw_tac[ARITH_ss][residue_def, EXTENSION]); + +(* Theorem: (residue n) DELETE n = residue n *) +(* Proof: Because n is not in (residue n). *) +val residue_delete = store_thm( + "residue_delete", + ``!n. 0 < n ==> ((residue n) DELETE n = residue n)``, + rpt strip_tac >> + `n NOTIN (residue n)` by rw[residue_def] >> + metis_tac[DELETE_NON_ELEMENT]); + +(* Theorem alias: rename *) +val residue_suc = save_thm("residue_suc", residue_insert); +(* val residue_suc = |- !n. 0 < n ==> (residue (SUC n) = n INSERT residue n): thm *) + +(* Theorem: count n = 0 INSERT (residue n) *) +(* Proof: by definition. *) +val residue_count = store_thm( + "residue_count", + ``!n. 0 < n ==> (count n = 0 INSERT (residue n))``, + srw_tac[ARITH_ss][residue_def, EXTENSION]); + +(* Theorem: FINITE (residue n) *) +(* Proof: by FINITE_COUNT. + If n = 0, residue 0 = {}, hence FINITE. + If n > 0, count n = 0 INSERT (residue n) by residue_count + hence true by FINITE_COUNT and FINITE_INSERT. +*) +val residue_finite = store_thm( + "residue_finite", + ``!n. FINITE (residue n)``, + Cases >- + rw[residue_def] >> + metis_tac[residue_count, FINITE_INSERT, count_def, FINITE_COUNT, DECIDE ``0 < SUC n``]); + +(* Theorem: For n > 0, CARD (residue n) = n-1 *) +(* Proof: + Since 0 INSERT (residue n) = count n by residue_count + the result follows by CARD_COUNT. +*) +val residue_card = store_thm( + "residue_card", + ``!n. 0 < n ==> (CARD (residue n) = n-1)``, + rpt strip_tac >> + `0 NOTIN (residue n)` by rw[residue_def] >> + `0 INSERT (residue n) = count n` by rw[residue_count] >> + `SUC (CARD (residue n)) = n` by metis_tac[residue_finite, CARD_INSERT, CARD_COUNT] >> + decide_tac); + +(* Theorem: For prime m, a in residue m, n <= m, a*n MOD m <> a*x MOD m for all x in residue n *) +(* Proof: + Assume the contrary, that a*n MOD m = a*x MOD m + Since a in residue m and m is prime, MOD_MULT_LCANCEL gives: n MOD m = x MOD m + If n = m, n MOD m = 0, but x MOD m <> 0, hence contradiction. + If n < m, then since x < n <= m, n = x, contradicting x < n. +*) +val residue_prime_neq = store_thm( + "residue_prime_neq", + ``!p a n. prime p /\ a IN (residue p) /\ n <= p ==> !x. x IN (residue n) ==> (a*n) MOD p <> (a*x) MOD p``, + rw[residue_def] >> + spose_not_then strip_assume_tac >> + `0 < p` by rw[PRIME_POS] >> + `(a MOD p <> 0) /\ (x MOD p <> 0)` by rw_tac arith_ss[] >> + `n MOD p = x MOD p` by metis_tac[MOD_MULT_LCANCEL] >> + Cases_on `n = p` >- + metis_tac [DIVMOD_ID] >> + `n < p` by decide_tac >> + `(n MOD p = n) /\ (x MOD p = x)` by rw_tac arith_ss[] >> + decide_tac); + +(* Idea: the product of residues is a factorial. *) + +(* Theorem: PROD_SET (residue n) = FACT (n - 1) *) +(* Proof: + By induction on n. + Base: PROD_SET (residue 0) = FACT (0 - 1) + PROD_SET (residue 0) + = PROD_SET {} by residue_0 + = 1 by PROD_SET_EMPTY + = FACT 0 by FACT_0 + = FACT (0 - 1) by arithmetic + Step: PROD_SET (residue n) = FACT (n - 1) ==> + PROD_SET (residue (SUC n)) = FACT (SUC n - 1) + If n = 0, + PROD_SET (residue (SUC 0)) + = PROD_SET (residue 1) by ONE + = PROD_SET {} by residue_1 + = 1 by PROD_SET_EMPTY + = FACT 0 by FACT_0 + + If n <> 0, then 0 < n. + Note FINITE (residue n) by residue_finite + PROD_SET (residue (SUC n)) + = PROD_SET (n INSERT residue n) by residue_insert + = n * PROD_SET ((residue n) DELETE n) by PROD_SET_THM + = n * PROD_SET (residue n) by residue_delete + = n * FACT (n - 1) by induction hypothesis + = FACT (SUC (n - 1)) by FACT + = FACT (SUC n - 1) by arithmetic +*) +Theorem prod_set_residue: + !n. PROD_SET (residue n) = FACT (n - 1) +Proof + Induct >- + simp[residue_0, PROD_SET_EMPTY, FACT_0] >> + Cases_on `n = 0` >- + simp[residue_1, PROD_SET_EMPTY, FACT_0] >> + `FINITE (residue n)` by rw[residue_finite] >> + `n = SUC (n - 1)` by decide_tac >> + `SUC (n - 1) = SUC n - 1` by decide_tac >> + `PROD_SET (residue (SUC n)) = PROD_SET (n INSERT residue n)` by rw[residue_insert] >> + `_ = n * PROD_SET ((residue n) DELETE n)` by rw[PROD_SET_THM] >> + `_ = n * PROD_SET (residue n)` by rw[residue_delete] >> + `_ = n * FACT (n - 1)` by rw[] >> + metis_tac[FACT] +QED + +(* Theorem: natural n = set (GENLIST SUC n) *) +(* Proof: + By induction on n. + Base: natural 0 = set (GENLIST SUC 0) + LHS = natural 0 = {} by natural_0 + RHS = set (GENLIST SUC 0) + = set [] by GENLIST_0 + = {} by LIST_TO_SET + Step: natural n = set (GENLIST SUC n) ==> + natural (SUC n) = set (GENLIST SUC (SUC n)) + natural (SUC n) + = SUC n INSERT natural n by natural_suc + = SUC n INSERT (set (GENLIST SUC n)) by induction hypothesis + = set (SNOC (SUC n) (GENLIST SUC n)) by LIST_TO_SET_SNOC + = set (GENLIST SUC (SUC n)) by GENLIST +*) +val natural_thm = store_thm( + "natural_thm", + ``!n. natural n = set (GENLIST SUC n)``, + Induct >- + rw[] >> + rw[natural_suc, LIST_TO_SET_SNOC, GENLIST]); + +(* ------------------------------------------------------------------------- *) +(* Uptos -- counting from 0 and inclusive. *) +(* ------------------------------------------------------------------------- *) + +(* Overload on another count-related set *) +val _ = overload_on("upto", ``\n. count (SUC n)``); + +(* Theorem: FINITE (upto n) *) +(* Proof: by FINITE_COUNT *) +val upto_finite = store_thm( + "upto_finite", + ``!n. FINITE (upto n)``, + rw[]); + +(* Theorem: CARD (upto n) = SUC n *) +(* Proof: by CARD_COUNT *) +val upto_card = store_thm( + "upto_card", + ``!n. CARD (upto n) = SUC n``, + rw[]); + +(* Theorem: n IN (upto n) *) +(* Proof: byLESS_SUC_REFL *) +val upto_has_last = store_thm( + "upto_has_last", + ``!n. n IN (upto n)``, + rw[]); + +(* Theorem: (upto n) DELETE n = count n *) +(* Proof: + (upto n) DELETE n + = (count (SUC n)) DELETE n by notation + = (n INSERT count n) DELETE n by COUNT_SUC + = count n DELETE n by DELETE_INSERT + = count n by DELETE_NON_ELEMENT, COUNT_NOT_SELF +*) +Theorem upto_delete: + !n. (upto n) DELETE n = count n +Proof + metis_tac[COUNT_SUC, COUNT_NOT_SELF, DELETE_INSERT, DELETE_NON_ELEMENT] +QED + +(* Theorem: upto n = {0} UNION (natural n) *) +(* Proof: + By UNION_DEF, EXTENSION, this is to show: + (1) x < SUC n ==> (x = 0) \/ ?x'. (x = SUC x') /\ x' < n + If x = 0, trivially true. + If x <> 0, x = SUC m. + Take x' = m, + then SUC m = x < SUC n ==> m < n by LESS_MONO_EQ + (2) (x = 0) \/ ?x'. (x = SUC x') /\ x' < n ==> x < SUC n + If x = 0, 0 < SUC n by SUC_POS + If ?x'. (x = SUC x') /\ x' < n, + x' < n ==> SUC x' = x < SUC n by LESS_MONO_EQ +*) +val upto_split_first = store_thm( + "upto_split_first", + ``!n. upto n = {0} UNION (natural n)``, + rw[EXTENSION, EQ_IMP_THM] >> + Cases_on `x` >- + rw[] >> + metis_tac[LESS_MONO_EQ]); + +(* Theorem: upto n = (count n) UNION {n} *) +(* Proof: + By UNION_DEF, EXTENSION, this is to show: + (1) x < SUC n ==> x < n \/ (x = n) + True by LESS_THM. + (2) x < n \/ (x = n) ==> x < SUC n + True by LESS_THM. +*) +val upto_split_last = store_thm( + "upto_split_last", + ``!n. upto n = (count n) UNION {n}``, + rw[EXTENSION, EQ_IMP_THM]); + +(* Theorem: upto n = n INSERT (count n) *) +(* Proof: + upto n + = count (SUC n) by notation + = {x | x < SUC n} by count_def + = {x | (x = n) \/ (x < n)} by prim_recTheory.LESS_THM + = x INSERT {x| x < n} by INSERT_DEF + = x INSERT (count n) by count_def +*) +val upto_by_count = store_thm( + "upto_by_count", + ``!n. upto n = n INSERT (count n)``, + rw[EXTENSION]); + +(* Theorem: upto n = 0 INSERT (natural n) *) +(* Proof: + upto n + = count (SUC n) by notation + = {x | x < SUC n} by count_def + = {x | ((x = 0) \/ (?m. x = SUC m)) /\ x < SUC n)} by num_CASES + = {x | (x = 0 /\ x < SUC n) \/ (?m. x = SUC m /\ x < SUC n)} by SUC_POS + = 0 INSERT {SUC m | SUC m < SUC n} by INSERT_DEF + = 0 INSERT {SUC m | m < n} by LESS_MONO_EQ + = 0 INSERT (IMAGE SUC (count n)) by IMAGE_DEF + = 0 INSERT (natural n) by notation +*) +val upto_by_natural = store_thm( + "upto_by_natural", + ``!n. upto n = 0 INSERT (natural n)``, + rw[EXTENSION] >> + metis_tac[num_CASES, LESS_MONO_EQ, SUC_POS]); + +(* Theorem: natural n = count (SUC n) DELETE 0 *) +(* Proof: + count (SUC n) DELETE 0 + = {x | x < SUC n} DELETE 0 by count_def + = {x | x < SUC n} DIFF {0} by DELETE_DEF + = {x | x < SUC n /\ x <> 0} by DIFF_DEF + = {SUC m | SUC m < SUC n} by num_CASES + = {SUC m | m < n} by LESS_MONO_EQ + = IMAGE SUC (count n) by IMAGE_DEF + = natural n by notation +*) +val natural_by_upto = store_thm( + "natural_by_upto", + ``!n. natural n = count (SUC n) DELETE 0``, + (rw[EXTENSION, EQ_IMP_THM] >> metis_tac[num_CASES, LESS_MONO_EQ])); + +(* ------------------------------------------------------------------------- *) +(* Euler Set and Totient Function *) +(* ------------------------------------------------------------------------- *) + +(* Euler's totient function *) +val Euler_def = zDefine` + Euler n = { i | 0 < i /\ i < n /\ (gcd n i = 1) } +`; +(* that is, Euler n = { i | i in (residue n) /\ (gcd n i = 1) }; *) +(* use zDefine as this is not computationally effective. *) + +val totient_def = Define` + totient n = CARD (Euler n) +`; + +(* Theorem: x IN (Euler n) <=> 0 < x /\ x < n /\ coprime n x *) +(* Proof: by Euler_def. *) +val Euler_element = store_thm( + "Euler_element", + ``!n x. x IN (Euler n) <=> 0 < x /\ x < n /\ coprime n x``, + rw[Euler_def]); + +(* Theorem: Euler n = (residue n) INTER {j | coprime j n} *) +(* Proof: by Euler_def, residue_def, EXTENSION, IN_INTER *) +val Euler_thm = store_thm( + "Euler_thm[compute]", + ``!n. Euler n = (residue n) INTER {j | coprime j n}``, + rw[Euler_def, residue_def, GCD_SYM, EXTENSION]); +(* This is effective, put in computeLib. *) + +(* +> EVAL ``Euler 10``; +val it = |- Euler 10 = {9; 7; 3; 1}: thm +> EVAL ``totient 10``; +val it = |- totient 10 = 4: thm +*) + +(* Theorem: FINITE (Euler n) *) +(* Proof: + Since (Euler n) SUBSET count n by Euler_def, SUBSET_DEF + and FINITE (count n) by FINITE_COUNT + ==> FINITE (Euler n) by SUBSET_FINITE +*) +val Euler_finite = store_thm( + "Euler_finite", + ``!n. FINITE (Euler n)``, + rpt strip_tac >> + `(Euler n) SUBSET count n` by rw[Euler_def, SUBSET_DEF] >> + metis_tac[FINITE_COUNT, SUBSET_FINITE]); + +(* Theorem: Euler 0 = {} *) +(* Proof: by Euler_def *) +val Euler_0 = store_thm( + "Euler_0", + ``Euler 0 = {}``, + rw[Euler_def]); + +(* Theorem: Euler 1 = {} *) +(* Proof: by Euler_def *) +val Euler_1 = store_thm( + "Euler_1", + ``Euler 1 = {}``, + rw[Euler_def]); + +(* Theorem: 1 < n ==> 1 IN (Euler n) *) +(* Proof: by Euler_def *) +val Euler_has_1 = store_thm( + "Euler_has_1", + ``!n. 1 < n ==> 1 IN (Euler n)``, + rw[Euler_def]); + +(* Theorem: 1 < n ==> (Euler n) <> {} *) +(* Proof: by Euler_has_1, MEMBER_NOT_EMPTY *) +val Euler_nonempty = store_thm( + "Euler_nonempty", + ``!n. 1 < n ==> (Euler n) <> {}``, + metis_tac[Euler_has_1, MEMBER_NOT_EMPTY]); + +(* Theorem: (Euler n = {}) <=> ((n = 0) \/ (n = 1)) *) +(* Proof: + If part: Euler n = {} ==> n = 0 \/ n = 1 + By contradiction, suppose ~(n = 0 \/ n = 1). + Then 1 < n, but Euler n <> {} by Euler_nonempty + This contradicts Euler n = {}. + Only-if part: n = 0 \/ n = 1 ==> Euler n = {} + Note Euler 0 = {} by Euler_0 + and Euler 1 = {} by Euler_1 +*) +val Euler_empty = store_thm( + "Euler_empty", + ``!n. (Euler n = {}) <=> ((n = 0) \/ (n = 1))``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `1 < n` by decide_tac >> + metis_tac[Euler_nonempty], + rw[Euler_0], + rw[Euler_1] + ]); + +(* Theorem: totient n <= n *) +(* Proof: + Since (Euler n) SUBSET count n by Euler_def, SUBSET_DEF + and FINITE (count n) by FINITE_COUNT + and (CARD (count n) = n by CARD_COUNT + Hence CARD (Euler n) <= n by CARD_SUBSET + or totient n <= n by totient_def +*) +val Euler_card_upper_le = store_thm( + "Euler_card_upper_le", + ``!n. totient n <= n``, + rpt strip_tac >> + `(Euler n) SUBSET count n` by rw[Euler_def, SUBSET_DEF] >> + metis_tac[totient_def, CARD_SUBSET, FINITE_COUNT, CARD_COUNT]); + +(* Theorem: 1 < n ==> totient n < n *) +(* Proof: + First, (Euler n) SUBSET count n by Euler_def, SUBSET_DEF + Now, ~(coprime 0 n) by coprime_0L, n <> 1 + so 0 NOTIN (Euler n) by Euler_def + but 0 IN (count n) by IN_COUNT, 0 < n + Thus (Euler n) <> (count n) by EXTENSION + and (Euler n) PSUBSET (count n) by PSUBSET_DEF + Since FINITE (count n) by FINITE_COUNT + and (CARD (count n) = n by CARD_COUNT + Hence CARD (Euler n) < n by CARD_PSUBSET + or totient n < n by totient_def +*) +val Euler_card_upper_lt = store_thm( + "Euler_card_upper_lt", + ``!n. 1 < n ==> totient n < n``, + rpt strip_tac >> + `(Euler n) SUBSET count n` by rw[Euler_def, SUBSET_DEF] >> + `0 < n /\ n <> 1` by decide_tac >> + `~(coprime 0 n)` by metis_tac[coprime_0L] >> + `0 NOTIN (Euler n)` by rw[Euler_def] >> + `0 IN (count n)` by rw[] >> + `(Euler n) <> (count n)` by metis_tac[EXTENSION] >> + `(Euler n) PSUBSET (count n)` by rw[PSUBSET_DEF] >> + metis_tac[totient_def, CARD_PSUBSET, FINITE_COUNT, CARD_COUNT]); + +(* Theorem: (totient n <= n) /\ (1 < n ==> 0 < totient n /\ totient n < n) *) +(* Proof: + This is to show: + (1) totient n <= n, + True by Euler_card_upper_le. + (2) 1 < n ==> 0 < totient n + Since (Euler n) <> {} by Euler_nonempty + Also FINITE (Euler n) by Euler_finite + Hence CARD (Euler n) <> 0 by CARD_EQ_0 + or 0 < totient n by totient_def + (3) 1 < n ==> totient n < n + True by Euler_card_upper_lt. +*) +val Euler_card_bounds = store_thm( + "Euler_card_bounds", + ``!n. (totient n <= n) /\ (1 < n ==> 0 < totient n /\ totient n < n)``, + rw[] >- + rw[Euler_card_upper_le] >- + (`(Euler n) <> {}` by rw[Euler_nonempty] >> + `FINITE (Euler n)` by rw[Euler_finite] >> + `totient n <> 0` by metis_tac[totient_def, CARD_EQ_0] >> + decide_tac) >> + rw[Euler_card_upper_lt]); + +(* Theorem: For prime p, (Euler p = residue p) *) +(* Proof: + By Euler_def, residue_def, this is to show: + For prime p, gcd p x = 1 for 0 < x < p. + Since x < p, x does not divide p, result follows by PRIME_GCD. + or, this is true by prime_coprime_all_lt +*) +val Euler_prime = store_thm( + "Euler_prime", + ``!p. prime p ==> (Euler p = residue p)``, + rw[Euler_def, residue_def, EXTENSION, EQ_IMP_THM] >> + rw[prime_coprime_all_lt]); + +(* Theorem: For prime p, totient p = p - 1 *) +(* Proof: + totient p + = CARD (Euler p) by totient_def + = CARD (residue p) by Euler_prime + = p - 1 by residue_card, and prime p > 0. +*) +val Euler_card_prime = store_thm( + "Euler_card_prime", + ``!p. prime p ==> (totient p = p - 1)``, + rw[totient_def, Euler_prime, residue_card, PRIME_POS]); + +(* ------------------------------------------------------------------------- *) +(* Summation of Geometric Sequence *) +(* ------------------------------------------------------------------------- *) + +(* Geometric Series: + Let s = p + p ** 2 + p ** 3 + p * s = p ** 2 + p ** 3 + p ** 4 + p * s - s = p ** 4 - p + (p - 1) * s = p * (p ** 3 - 1) +*) + +(* Theorem: 0 < p ==> !n. (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) *) +(* Proof: + By induction on n. + Base: (p - 1) * SIGMA (\j. p ** j) (natural 0) = p * (p ** 0 - 1) + LHS = (p - 1) * SIGMA (\j. p ** j) (natural 0) + = (p - 1) * SIGMA (\j. p ** j) {} by natural_0 + = (p - 1) * 0 by SUM_IMAGE_EMPTY + = 0 by MULT_0 + RHS = p * (p ** 0 - 1) + = p * (1 - 1) by EXP + = p * 0 by SUB_EQUAL_0 + = 0 = LHS by MULT_0 + Step: (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1) ==> + (p - 1) * SIGMA (\j. p ** j) (natural (SUC n)) = p * (p ** SUC n - 1) + Note FINITE (natural n) by natural_finite + and (SUC n) NOTIN (natural n) by natural_element + Also p <= p ** (SUC n) by X_LE_X_EXP, SUC_POS + and 1 <= p by 0 < p + thus p ** (SUC n) <> 0 by EXP_POS, 0 < p + so p ** (SUC n) <= p * p ** (SUC n) by LE_MULT_LCANCEL, p ** (SUC n) <> 0 + (p - 1) * SIGMA (\j. p ** j) (natural (SUC n)) + = (p - 1) * SIGMA (\j. p ** j) ((SUC n) INSERT (natural n)) by natural_suc + = (p - 1) * ((p ** SUC n) + SIGMA (\j. p ** j) ((natural n) DELETE (SUC n))) by SUM_IMAGE_THM + = (p - 1) * ((p ** SUC n) + SIGMA (\j. p ** j) (natural n)) by DELETE_NON_ELEMENT + = (p - 1) * (p ** SUC n) + (p - 1) * SIGMA (\j. p ** j) (natural n) by LEFT_ADD_DISTRIB + = (p - 1) * (p ** SUC n) + p * (p ** n - 1) by induction hypothesis + = (p - 1) * (p ** SUC n) + (p * p ** n - p) by LEFT_SUB_DISTRIB + = (p - 1) * (p ** SUC n) + (p ** (SUC n) - p) by EXP + = (p * p ** SUC n - p ** SUC n) + (p ** SUC n - p) by RIGHT_SUB_DISTRIB + = (p * p ** SUC n - p ** SUC n + p ** SUC n - p by LESS_EQ_ADD_SUB, p <= p ** (SUC n) + = p ** p ** SUC n - p by SUB_ADD, p ** (SUC n) <= p * p ** (SUC n) + = p * (p ** SUC n - 1) by LEFT_SUB_DISTRIB + *) +val sigma_geometric_natural_eqn = store_thm( + "sigma_geometric_natural_eqn", + ``!p. 0 < p ==> !n. (p - 1) * SIGMA (\j. p ** j) (natural n) = p * (p ** n - 1)``, + rpt strip_tac >> + Induct_on `n` >- + rw_tac std_ss[natural_0, SUM_IMAGE_EMPTY, EXP, MULT_0] >> + `FINITE (natural n)` by rw[natural_finite] >> + `(SUC n) NOTIN (natural n)` by rw[natural_element] >> + qabbrev_tac `q = p ** SUC n` >> + `p <= q` by rw[X_LE_X_EXP, Abbr`q`] >> + `1 <= p` by decide_tac >> + `q <> 0` by rw[EXP_POS, Abbr`q`] >> + `q <= p * q` by rw[LE_MULT_LCANCEL] >> + `(p - 1) * SIGMA (\j. p ** j) (natural (SUC n)) + = (p - 1) * SIGMA (\j. p ** j) ((SUC n) INSERT (natural n))` by rw[natural_suc] >> + `_ = (p - 1) * (q + SIGMA (\j. p ** j) ((natural n) DELETE (SUC n)))` by rw[SUM_IMAGE_THM, Abbr`q`] >> + `_ = (p - 1) * (q + SIGMA (\j. p ** j) (natural n))` by metis_tac[DELETE_NON_ELEMENT] >> + `_ = (p - 1) * q + (p - 1) * SIGMA (\j. p ** j) (natural n)` by rw[LEFT_ADD_DISTRIB] >> + `_ = (p - 1) * q + p * (p ** n - 1)` by rw[] >> + `_ = (p - 1) * q + (p * p ** n - p)` by rw[LEFT_SUB_DISTRIB] >> + `_ = (p - 1) * q + (q - p)` by rw[EXP, Abbr`q`] >> + `_ = (p * q - q) + (q - p)` by rw[RIGHT_SUB_DISTRIB] >> + `_ = (p * q - q + q) - p` by rw[LESS_EQ_ADD_SUB] >> + `_ = p * q - p` by rw[SUB_ADD] >> + `_ = p * (q - 1)` by rw[LEFT_SUB_DISTRIB] >> + rw[]); + +(* Theorem: 1 < p ==> !n. SIGMA (\j. p ** j) (natural n) = (p * (p ** n - 1)) DIV (p - 1) *) +(* Proof: + Since 1 < p, + ==> 0 < p - 1, and 0 < p by arithmetic + Let t = SIGMA (\j. p ** j) (natural n) + With 0 < p, + (p - 1) * t = p * (p ** n - 1) by sigma_geometric_natural_eqn, 0 < p + Hence t = (p * (p ** n - 1)) DIV (p - 1) by DIV_SOLVE, 0 < (p - 1) +*) +val sigma_geometric_natural = store_thm( + "sigma_geometric_natural", + ``!p. 1 < p ==> !n. SIGMA (\j. p ** j) (natural n) = (p * (p ** n - 1)) DIV (p - 1)``, + rpt strip_tac >> + `0 < p - 1 /\ 0 < p` by decide_tac >> + rw[sigma_geometric_natural_eqn, DIV_SOLVE]); + +(* ------------------------------------------------------------------------- *) +(* Chinese Remainder Theorem. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: when a MOD (m * n) = b MOD (m * n), break up modulus m * n. *) + +(* Theorem: 0 < m /\ 0 < n /\ a MOD (m * n) = b MOD (m * n) ==> + a MOD m = b MOD m /\ a MOD n = b MOD n *) +(* Proof: + Either b <= a, or a < b, which implies a <= b. + The statement is symmetrical in a and b, + so proceed by lemma with b <= a, without loss of generality. + Note 0 < m * n by MULT_POS + so ?c. a = b + c * (m * n) by MOD_MOD_EQN, 0 < m * n + Thus a = b + (c * m) * n by arithmetic + and a = b + (c * n) * m by arithmetic + ==> a MOD m = b MOD m by MOD_MOD_EQN, 0 < m + and a MOD n = b MOD n by MOD_MOD_EQN, 0 < n +*) +Theorem mod_mod_prod_eq: + !m n a b. 0 < m /\ 0 < n /\ a MOD (m * n) = b MOD (m * n) ==> + a MOD m = b MOD m /\ a MOD n = b MOD n +Proof + ntac 5 strip_tac >> + `!a b. b <= a /\ a MOD (m * n) = b MOD (m * n) ==> + a MOD m = b MOD m /\ a MOD n = b MOD n` by + (ntac 3 strip_tac >> + `0 < m * n` by fs[] >> + `?c. a' = b' + c * (m * n)` by metis_tac[MOD_MOD_EQN] >> + `a' = b' + (c * m) * n` by decide_tac >> + `a' = b' + (c * n) * m` by decide_tac >> + metis_tac[MOD_MOD_EQN]) >> + (Cases_on `b <= a` >> simp[]) +QED + +(* Idea: converse of mod_mod_prod_eq when coprime. *) + +(* Theorem: 0 < m /\ 0 < n /\ coprime m n /\ + a MOD m = b MOD m /\ a MOD n = b MOD n ==> + a MOD (m * n) = b MOD (m * n) *) +(* Proof: + Either b <= a, or a < b, which implies a <= b. + The statement is symmetrical in a and b, + so proceed by lemma with b <= a, without loss of generality. + Note 0 < m * n by MULT_POS + and ?h. a = b + h * m by MOD_MOD_EQN, 0 < m + and ?k. a = b + k * n by MOD_MOD_EQN, 0 < n + ==> h * m = k * n by EQ_ADD_LCANCEL + Thus n divides (h * m) by divides_def + or n divides h by euclid_coprime, coprime m n + ==> ?c. h = c * n by divides_def + so a = b + c * (m * n) by above + Thus a MOD (m * n) = b MOD (m * n) + by MOD_MOD_EQN, 0 < m * n +*) +Theorem coprime_mod_mod_prod_eq: + !m n a b. 0 < m /\ 0 < n /\ coprime m n /\ + a MOD m = b MOD m /\ a MOD n = b MOD n ==> + a MOD (m * n) = b MOD (m * n) +Proof + rpt strip_tac >> + `!a b. b <= a /\ a MOD m = b MOD m /\ a MOD n = b MOD n ==> + a MOD (m * n) = b MOD (m * n)` by + (rpt strip_tac >> + `0 < m * n` by fs[] >> + `?h. a' = b' + h * m` by metis_tac[MOD_MOD_EQN] >> + `?k. a' = b' + k * n` by metis_tac[MOD_MOD_EQN] >> + `h * m = k * n` by decide_tac >> + `n divides (h * m)` by metis_tac[divides_def] >> + `n divides h` by metis_tac[euclid_coprime, MULT_COMM] >> + `?c. h = c * n` by rw[GSYM divides_def] >> + `a' = b' + c * (m * n)` by fs[] >> + metis_tac[MOD_MOD_EQN]) >> + (Cases_on `b <= a` >> simp[]) +QED + +(* Idea: combine both parts for a MOD (m * n) = b MOD (m * n). *) + +(* Theorem: 0 < m /\ 0 < n /\ coprime m n ==> + !a b. a MOD (m * n) = b MOD (m * n) <=> a MOD m = b MOD m /\ a MOD n = b MOD n *) +(* Proof: + If part is true by mod_mod_prod_eq + Only-if part is true by coprime_mod_mod_prod_eq +*) +Theorem coprime_mod_mod_prod_eq_iff: + !m n. 0 < m /\ 0 < n /\ coprime m n ==> + !a b. a MOD (m * n) = b MOD (m * n) <=> a MOD m = b MOD m /\ a MOD n = b MOD n +Proof + metis_tac[mod_mod_prod_eq, coprime_mod_mod_prod_eq] +QED + +(* Idea: application, the Chinese Remainder Theorem for two coprime moduli. *) + +(* Theorem: 0 < m /\ 0 < n /\ coprime m n ==> + ?!x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n *) +(* Proof: + By EXISTS_UNIQUE_THM, this is to show: + (1) Existence: ?x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n + Note ?p q. (p * m + q * n) MOD (m * n) = 1 MOD (m * n) + by coprime_linear_mod_prod + so (p * m + q * n) MOD m = 1 MOD m + and (p * m + q * n) MOD n = 1 MOD n by mod_mod_prod_eq + or (q * n) MOD m = 1 MOD m by MOD_TIMES + and (p * m) MOD n = 1 MOD n by MOD_TIMES + Let z = b * p * m + a * q * n. + z MOD m + = (b * p * m + a * q * n) MOD m + = (a * q * n) MOD m by MOD_TIMES + = ((a MOD m) * (q * n) MOD m) MOD m by MOD_TIMES2 + = a MOD m by MOD_TIMES, above + and z MOD n + = (b * p * m + a * q * n) MDO n + = (b * p * m) MOD n by MOD_TIMES + = ((b MOD n) * (p * m) MOD n) MOD n by MOD_TIMES2 + = b MOD n by MOD_TIMES, above + Take x = z MOD (m * n). + Then x < m * n by MOD_LESS + and x MOD m = z MOD m = a MOD m by MOD_MULT_MOD + and x MOD n = z MOD n = b MOD n by MOD_MULT_MOD + (2) Uniqueness: + x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n /\ + y < m * n /\ y MOD m = a MOD m /\ y MOD n = b MOD n ==> x = y + Note x MOD m = y MOD m by both equal to a MOD m + and x MOD n = y MOD n by both equal to b MOD n + Thus x MOD (m * n) = y MOD (m * n) by coprime_mod_mod_prod_eq + so x = y by LESS_MOD, both < m * n +*) +Theorem coprime_mod_mod_solve: + !m n a b. 0 < m /\ 0 < n /\ coprime m n ==> + ?!x. x < m * n /\ x MOD m = a MOD m /\ x MOD n = b MOD n +Proof + rw[EXISTS_UNIQUE_THM] >| [ + `?p q. (p * m + q * n) MOD (m * n) = 1 MOD (m * n)` by rw[coprime_linear_mod_prod] >> + qabbrev_tac `u = p * m + q * n` >> + `u MOD m = 1 MOD m /\ u MOD n = 1 MOD n` by metis_tac[mod_mod_prod_eq] >> + `(q * n) MOD m = 1 MOD m /\ (p * m) MOD n = 1 MOD n` by rfs[MOD_TIMES, Abbr`u`] >> + qabbrev_tac `z = b * p * m + a * q * n` >> + qexists_tac `z MOD (m * n)` >> + rw[] >| [ + `z MOD (m * n) MOD m = z MOD m` by rw[MOD_MULT_MOD] >> + `_ = (a * q * n) MOD m` by rw[Abbr`z`] >> + `_ = ((a MOD m) * (q * n) MOD m) MOD m` by rw[MOD_TIMES2] >> + `_ = a MOD m` by fs[] >> + decide_tac, + `z MOD (m * n) MOD n = z MOD n` by metis_tac[MOD_MULT_MOD, MULT_COMM] >> + `_ = (b * p * m) MOD n` by rw[Abbr`z`] >> + `_ = ((b MOD n) * (p * m) MOD n) MOD n` by rw[MOD_TIMES2] >> + `_ = b MOD n` by fs[] >> + decide_tac + ], + metis_tac[coprime_mod_mod_prod_eq, LESS_MOD] + ] +QED + +(* Yes! The Chinese Remainder Theorem with two modular equations. *) + +(* +For an algorithm: +* define bezout, input pair (m, n), output pair (p, q) +* define a dot-product: + (p, q) dot (m, n) = p * m + q * n + with (p, q) dot (m, n) MOD (m * n) = (gcd m n) MOD (m * n) +* define a scale-product: + (a, b) scale (p, q) = (a * p, b * q) + with z = ((a, b) scale (p, q)) dot (m, n) + and x = z MOD (m * n) + = (((a, b) scale (p, q)) dot (m, n)) MOD (m * n) + = (((a, b) scale (bezout (m, n))) dot (m, n)) MOD (m * n) + +Note that bezout (m, n) is the extended Euclidean algorithm. + +*) + +(* ------------------------------------------------------------------------- *) +(* Useful Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Note: + count m = {i | i < m} defined in pred_set + residue m = {i | 0 < i /\ i < m} defined in Euler + The difference i = 0 gives n ** 0 = 1, which does not make a difference for PROD_SET. +*) + +(* Theorem: PROD_SET (IMAGE (\j. n ** j) (count m)) = + PROD_SET (IMAGE (\j. n ** j) (residue m)) *) +(* Proof: + Let f = \j. n ** j. + When m = 0, + Note count 0 = {} by COUNT_0 + and residue 0 = {} by residue_0 + Thus LHS = RHS. + When m = 1, + Note count 1 = {0} by COUNT_1 + and residue 1 = {} by residue_1 + Thus LHS = PROD_SET (IMAGE f {0}) + = PROD_SET {f 0} by IMAGE_SING + = f 0 by PROD_SET_SING + = n ** 0 = 1 by EXP_0 + RHS = PROD_SET (IMAGE f {}) + = PROD_SET {} by IMAGE_EMPTY + = 1 by PROD_SET_EMPTY + = LHS + For m <> 0, m <> 1, + When n = 0, + Note !j. f j = f j = 0 then 1 else 0 by ZERO_EXP + Thus IMAGE f (count m) = {0; 1} by count_def, EXTENSION, 1 < m + and IMAGE f (residue m) = {0} by residue_def, EXTENSION, 1 < m + Thus LHS = PROD_SET {0; 1} + = 0 * 1 = 0 by PROD_SET_THM + RHS = PROD_SET {0} + = 0 = LHS by PROD_SET_SING + When n = 1, + Note f = K 1 by EXP_1, FUN_EQ_THM + and count m <> {} by COUNT_NOT_EMPTY, 0 < m + and residue m <> {} by residue_nonempty, 1 < m + Thus LHS = PROD_SET (IMAGE (K 1) (count m)) + = PROD_SET {1} by IMAGE_K + = PROD_SET (IMAGE (K 1) (residue m)) by IMAGE_K + = RHS + For 1 < m, and 1 < n, + Note 0 IN count m by IN_COUNT, 0 < m + also (IMAGE f (count m)) DELETE 1 + = IMAGE f (residue m) by IMAGE_DEF, EXP_EQ_1, EXP, 1 < n + PROD_SET (IMAGE f (count m)) + = PROD_SET (IMAGE f (0 INSERT count m)) by ABSORPTION + = PROD_SET (f 0 INSERT IMAGE f (count m)) by IMAGE_INSERT + = n ** 0 * PROD_SET ((IMAGE f (count m)) DELETE n ** 0) by PROD_SET_THM + = PROD_SET ((IMAGE f (count m)) DELETE 1) by EXP_0 + = PROD_SET ((IMAGE f (residue m))) by above +*) +Theorem PROD_SET_IMAGE_EXP_NONZERO: + !n m. PROD_SET (IMAGE (\j. n ** j) (count m)) = + PROD_SET (IMAGE (\j. n ** j) (residue m)) +Proof + rpt strip_tac >> + qabbrev_tac `f = \j. n ** j` >> + Cases_on `m = 0` >- + simp[residue_0] >> + Cases_on `m = 1` >- + simp[residue_1, COUNT_1, Abbr`f`, PROD_SET_THM] >> + `0 < m /\ 1 < m` by decide_tac >> + Cases_on `n = 0` >| [ + `!j. f j = if j = 0 then 1 else 0` by rw[Abbr`f`] >> + `IMAGE f (count m) = {0; 1}` by + (rw[EXTENSION, EQ_IMP_THM] >- + metis_tac[ONE_NOT_ZERO] >> + metis_tac[] + ) >> + `IMAGE f (residue m) = {0}` by + (rw[residue_def, EXTENSION, EQ_IMP_THM] >> + `0 < 1` by decide_tac >> + metis_tac[]) >> + simp[PROD_SET_THM], + Cases_on `n = 1` >| [ + `f = K 1` by rw[FUN_EQ_THM, Abbr`f`] >> + `count m <> {}` by fs[COUNT_NOT_EMPTY] >> + `residue m <> {}` by fs[residue_nonempty] >> + simp[IMAGE_K], + `0 < n /\ 1 < n` by decide_tac >> + `0 IN count m` by rw[] >> + `FINITE (IMAGE f (count m))` by rw[] >> + `(IMAGE f (count m)) DELETE 1 = IMAGE f (residue m)` by + (rw[residue_def, IMAGE_DEF, Abbr`f`, EXTENSION, EQ_IMP_THM] >- + metis_tac[EXP, NOT_ZERO] >- + metis_tac[] >> + `j <> 0` by decide_tac >> + metis_tac[EXP_EQ_1] + ) >> + `PROD_SET (IMAGE f (count m)) = PROD_SET (IMAGE f (0 INSERT count m))` by metis_tac[ABSORPTION] >> + `_ = PROD_SET (f 0 INSERT IMAGE f (count m))` by rw[] >> + `_ = n ** 0 * PROD_SET ((IMAGE f (count m)) DELETE n ** 0)` by rw[PROD_SET_THM, Abbr`f`] >> + `_ = 1 * PROD_SET ((IMAGE f (count m)) DELETE 1)` by metis_tac[EXP_0] >> + `_ = PROD_SET ((IMAGE f (residue m)))` by rw[] >> + decide_tac + ] + ] +QED + +(* Overload sublist by infix operator *) +val _ = temp_overload_on ("<=", ``sublist``); + +(* Theorem: m < n ==> [m; n] <= [m .. n] *) +(* Proof: + By induction on n. + Base: !m. m < 0 ==> [m; 0] <= [m .. 0], true by m < 0 = F. + Step: !m. m < n ==> [m; n] <= [m .. n] ==> + !m. m < SUC n ==> [m; SUC n] <= [m .. SUC n] + Note m < SUC n means m <= n. + If m = n, LHS = [n; SUC n] + RHS = [n .. (n + 1)] = [n; SUC n] by ADD1 + = LHS, thus true by sublist_refl + If m < n, [m; n] <= [m .. n] by induction hypothesis + SNOC (SUC n) [m; n] <= SNOC (SUC n) [m .. n] by sublist_snoc + [m; n; SUC n] <= [m .. SUC n] by SNOC, listRangeINC_SNOC + But [m; SUC n] <= [m; n; SUC n] by sublist_def + Thus [m; SUC n] <= [m .. SUC n] by sublist_trans +*) +val listRangeINC_sublist = store_thm( + "listRangeINC_sublist", + ``!m n. m < n ==> [m; n] <= [m .. n]``, + Induct_on `n` >- + rw[] >> + rpt strip_tac >> + `(m = n) \/ m < n` by decide_tac >| [ + rw[listRangeINC_def, ADD1] >> + rw[sublist_refl], + `[m; n] <= [m .. n]` by rw[] >> + `SNOC (SUC n) [m; n] <= SNOC (SUC n) [m .. n]` by rw[sublist_snoc] >> + `SNOC (SUC n) [m; n] = [m; n; SUC n]` by rw[] >> + `SNOC (SUC n) [m .. n] = [m .. SUC n]` by rw[listRangeINC_SNOC, ADD1] >> + `[m; SUC n] <= [m; n; SUC n]` by rw[sublist_def] >> + metis_tac[sublist_trans] + ]); + +(* Theorem: m + 1 < n ==> [m; (n - 1)] <= [m ..< n] *) +(* Proof: + By induction on n. + Base: !m. m + 1 < 0 ==> [m; 0 - 1] <= [m ..< 0], true by m + 1 < 0 = F. + Step: !m. m + 1 < n ==> [m; n - 1] <= [m ..< n] ==> + !m. m + 1 < SUC n ==> [m; SUC n - 1] <= [m ..< SUC n] + Note m + 1 < SUC n means m + 1 <= n. + If m + 1 = n, LHS = [m; SUC n - 1] = [m; n] + RHS = [m ..< (n + 1)] = [m; n] by ADD1 + = LHS, thus true by sublist_refl + If m + 1 < n, [m; n - 1] <= [m ..< n] by induction hypothesis + SNOC n [m; n - 1] <= SNOC n [m ..< n] by sublist_snoc + [m; n - 1; n] <= [m ..< SUC n] by SNOC, listRangeLHI_SNOC, ADD1 + But [m; SUC n - 1] <= [m; n] <= [m; n - 1; n] by sublist_def + Thus [m; SUC n - 1] <= [m ..< SUC n] by sublist_trans +*) +val listRangeLHI_sublist = store_thm( + "listRangeLHI_sublist", + ``!m n. m + 1 < n ==> [m; (n - 1)] <= [m ..< n]``, + Induct_on `n` >- + rw[] >> + rpt strip_tac >> + `SUC n - 1 = n` by decide_tac >> + `(m + 1 = n) \/ m + 1 < n` by decide_tac >| [ + rw[listRangeLHI_def, ADD1] >> + rw[sublist_refl], + `[m; n - 1] <= [m ..< n]` by rw[] >> + `SNOC n [m; n - 1] <= SNOC n [m ..< n]` by rw[sublist_snoc] >> + `SNOC n [m; n - 1] = [m; n - 1; n]` by rw[] >> + `SNOC n [m ..< n] = [m ..< SUC n]` by rw[listRangeLHI_SNOC, ADD1] >> + `[m; SUC n - 1] <= [m; n - 1; n]` by rw[sublist_def] >> + metis_tac[sublist_trans] + ]); + +(* Theorem: sl <= ls /\ ALL_DISTINCT ls /\ j < h /\ h < LENGTH sl ==> + findi (EL j sl) ls < findi (EL h sl) ls *) +(* Proof: + Let x = EL j sl, + y = EL h sl, + p = findi x ls, + q = findi y ls. + Then MEM x sl /\ MEM y sl by EL_MEM + and ALL_DISTINCT sl by sublist_ALL_DISTINCT + + With MEM x sl, + Note ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ + sl = l3 ++ [x] ++ l4 /\ + l3 <= l1 /\ l4 <= l2 by sublist_order, sl <= ls + Thus j = LENGTH l3 by ALL_DISTINCT_EL_APPEND, j < LENGTH sl + + Claim: MEM y l4 + Proof: By contradiction, suppose ~MEM y l4. + Note y <> x by ALL_DISTINCT_EL_IMP, j <> h + ==> MEM y l3 by MEM_APPEND + ==> ?k. k < LENGTH l3 /\ y = EL k l3 by MEM_EL + But LENGTH l3 < LENGTH sl by LENGTH_APPEND + and y = EL k sl by EL_APPEND1 + Thus k = h by ALL_DISTINCT_EL_IMP, k < LENGTH sl + or h < j, contradicting j < h by j = LENGTH l3 + + Thus ?l5 l6 l7 l8. l2 = l5 ++ [x] ++ l6 /\ + l4 = l7 ++ [x] ++ l8 /\ + l7 <= l5 /\ l8 <= l6 by sublist_order, l4 <= l2 + + Hence, ls = l1 ++ [x] ++ l5 ++ [y] ++ l6. + Now p < LENGTH ls /\ q < LENGTH ls by MEM_findi + so x = EL p ls /\ y = EL q ls by findi_EL_iff + and p = LENGTH l1 by ALL_DISTINCT_EL_APPEND + and q = LENGTH (l1 ++ [x] ++ l5) by ALL_DISTINCT_EL_APPEND + Thus p < q by LENGTH_APPEND +*) +Theorem sublist_element_order: + !ls sl j h. sl <= ls /\ ALL_DISTINCT ls /\ j < h /\ h < LENGTH sl ==> + findi (EL j sl) ls < findi (EL h sl) ls +Proof + rpt strip_tac >> + qabbrev_tac `x = EL j sl` >> + qabbrev_tac `y = EL h sl` >> + qabbrev_tac `p = findi x ls` >> + qabbrev_tac `q = findi y ls` >> + `MEM x sl /\ MEM y sl` by fs[EL_MEM, Abbr`x`, Abbr`y`] >> + assume_tac sublist_order >> + last_x_assum (qspecl_then [`ls`, `sl`, `x`] strip_assume_tac) >> + rfs[] >> + `ALL_DISTINCT sl` by metis_tac[sublist_ALL_DISTINCT] >> + `j = LENGTH l3` by metis_tac[ALL_DISTINCT_EL_APPEND, LESS_TRANS] >> + `MEM y l4` by + (spose_not_then strip_assume_tac >> + `y <> x` by fs[ALL_DISTINCT_EL_IMP, Abbr`x`, Abbr`y`] >> + `MEM y l3` by fs[] >> + `?k. k < LENGTH l3 /\ y = EL k l3` by simp[GSYM MEM_EL] >> + `LENGTH l3 < LENGTH sl` by fs[] >> + `y = EL k sl` by fs[EL_APPEND1] >> + `k = h` by metis_tac[ALL_DISTINCT_EL_IMP, LESS_TRANS] >> + decide_tac) >> + assume_tac sublist_order >> + last_x_assum (qspecl_then [`l2`, `l4`, `y`] strip_assume_tac) >> + rfs[] >> + rename1 `l2 = l5 ++ [y] ++ l6` >> + `p < LENGTH ls /\ q < LENGTH ls` by fs[MEM_findi, Abbr`p`, Abbr`q`] >> + `x = EL p ls /\ y = EL q ls` by fs[findi_EL_iff, Abbr`p`, Abbr`q`] >> + `p = LENGTH l1` by metis_tac[ALL_DISTINCT_EL_APPEND] >> + `ls = l1 ++ [x] ++ l5 ++ [y] ++ l6` by fs[] >> + `q = LENGTH (l1 ++ [x] ++ l5)` by metis_tac[ALL_DISTINCT_EL_APPEND] >> + fs[] +QED + +(* Theorem: let fs = FILTER P ls in ALL_DISTINCT ls /\ j < h /\ h < LENGTH fs ==> + findi (EL j fs) ls < findi (EL h fs) l *) +(* Proof: + Let fs = FILTER P ls. + Then fs <= ls by FILTER_sublist + Thus findi (EL j fs) ls + < findi (EL h fs) ls by sublist_element_order +*) +Theorem FILTER_element_order: + !P ls j h. let fs = FILTER P ls in ALL_DISTINCT ls /\ j < h /\ h < LENGTH fs ==> + findi (EL j fs) ls < findi (EL h fs) ls +Proof + rw_tac std_ss[] >> + `fs <= ls` by simp[FILTER_sublist, Abbr`fs`] >> + fs[sublist_element_order] +QED + +(* ------------------------------------------------------------------------- *) + +(* export theory at end *) +val _ = export_theory(); + +(*===========================================================================*) diff --git a/src/algebra/primeScript.sml b/src/algebra/primeScript.sml new file mode 100644 index 0000000000..fb6b4d08a2 --- /dev/null +++ b/src/algebra/primeScript.sml @@ -0,0 +1,11019 @@ +(* ------------------------------------------------------------------------- *) +(* Integer Functions Computation (logPower) *) +(* Prime Power (primePower) *) +(* Primality Tests (primes) *) +(* Gauss' Little Theorem *) +(* Mobius Function and Inversion. *) +(* ------------------------------------------------------------------------- *) +(* Author: (Joseph) Hing-Lun Chan (Australian National University, 2019) *) +(* ------------------------------------------------------------------------- *) + +open HolKernel boolLib bossLib Parse; + +open arithmeticTheory pred_setTheory dividesTheory gcdTheory logrootTheory + listTheory rich_listTheory listRangeTheory gcdsetTheory optionTheory + numberTheory combinatoricsTheory prim_recTheory; + +val _ = new_theory "prime"; + +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); + +(* ------------------------------------------------------------------------- *) +(* Integer Functions Computation Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: + SQRT n = ROOT 2 n + LOG2 n = LOG 2 n + n power_of b = perfect_power n b +*) + +(* Definitions and Theorems (# are exported): + + ROOT computation: + ROOT_POWER |- !a n. 1 < a /\ 0 < n ==> (ROOT n (a ** n) = a) + ROOT_FROM_POWER |- !m n b. 0 < m /\ (b ** m = n) ==> (b = ROOT m n) +# ROOT_OF_0 |- !m. 0 < m ==> (ROOT m 0 = 0) +# ROOT_OF_1 |- !m. 0 < m ==> (ROOT m 1 = 1) + ROOT_EQ_0 |- !m. 0 < m ==> !n. (ROOT m n = 0) <=> (n = 0) +# ROOT_1 |- !n. ROOT 1 n = n + ROOT_THM |- !r. 0 < r ==> !n p. (ROOT r n = p) <=> p ** r <= n /\ n < SUC p ** r + ROOT_EQN |- !r n. 0 < r ==> (ROOT r n = + (let m = TWICE (ROOT r (n DIV 2 ** r)) + in m + if (m + 1) ** r <= n then 1 else 0)) + ROOT_SUC |- !r n. 0 < r ==> + ROOT r (SUC n) = ROOT r n + + if SUC n = SUC (ROOT r n) ** r then 1 else 0 + ROOT_EQ_1 |- !m. 0 < m ==> !n. (ROOT m n = 1) <=> 0 < n /\ n < 2 ** m + ROOT_LE_SELF |- !m n. 0 < m ==> ROOT m n <= n + ROOT_EQ_SELF |- !m n. 0 < m ==> (ROOT m n = n) <=> (m = 1) \/ (n = 0) \/ (n = 1)) + ROOT_GE_SELF |- !m n. 0 < m ==> (n <= ROOT m n) <=> (m = 1) \/ (n = 0) \/ (n = 1)) + ROOT_LE_REVERSE |- !a b n. 0 < a /\ a <= b ==> ROOT b n <= ROOT a n + + Square Root: + SQRT_PROPERTY |- !n. 0 < n ==> SQRT n ** 2 <= n /\ n < SUC (SQRT n) ** 2 + SQRT_UNIQUE |- !n p. p ** 2 <= n /\ n < SUC p ** 2 ==> SQRT n = p + SQRT_THM |- !n p. (SQRT n = p) <=> p ** 2 <= n /\ n < SUC p ** 2 + SQ_SQRT_LE |- !n. SQ (SQRT n) <= n + SQ_SQRT_LE_alt |- !n. SQRT n ** 2 <= n + SQRT_LE |- !n m. n <= m ==> SQRT n <= SQRT m + SQRT_LT |- !n m. n < m ==> SQRT n <= SQRT m +# SQRT_0 |- SQRT 0 = 0 +# SQRT_1 |- SQRT 1 = 1 + SQRT_EQ_0 |- !n. (SQRT n = 0) <=> (n = 0) + SQRT_EQ_1 |- !n. (SQRT n = 1) <=> (n = 1) \/ (n = 2) \/ (n = 3) + SQRT_EXP_2 |- !n. SQRT (n ** 2) = n + SQRT_OF_SQ |- !n. SQRT (n ** 2) = n + SQRT_SQ |- !n. SQRT (SQ n) = n + SQRT_LE_SELF |- !n. SQRT n <= n + SQRT_GE_SELF |- !n. n <= SQRT n <=> (n = 0) \/ (n = 1) + SQRT_EQ_SELF |- !n. (SQRT n = n) <=> (n = 0) \/ (n = 1) + SQRT_LE_IMP |- !n m. SQRT n <= m ==> n <= 3 * m ** 2 + SQRT_MULT_LE |- !n m. SQRT n * SQRT m <= SQRT (n * m) + SQRT_LT_IMP |- !n m. SQRT n < m ==> n < m ** 2 + LT_SQRT_IMP |- !n m. n < SQRT m ==> n ** 2 < m + SQRT_LT_SQRT |- !n m. SQRT n < SQRT m ==> n < m + + Square predicate: + square_def |- !n. square n <=> ?k. n = k * k + square_alt |- !n. square n <=> ?k. n = k ** 2 +! square_eqn |- !n. square n <=> SQRT n ** 2 = n + square_0 |- square 0 + square_1 |- square 1 + prime_non_square |- !p. prime p ==> ~square p + SQ_SQRT_LT |- !n. ~square n ==> SQRT n * SQRT n < n + SQ_SQRT_LT_alt |- !n. ~square n ==> SQRT n ** 2 < n + odd_square_lt |- !n m. ~square n ==> ((2 * m + 1) ** 2 < n <=> m < HALF (1 + SQRT n)) + + Logarithm: + LOG_EXACT_EXP |- !a. 1 < a ==> !n. LOG a (a ** n) = n + EXP_TO_LOG |- !a b n. 1 < a /\ 0 < b /\ b <= a ** n ==> LOG a b <= n + LOG_THM |- !a n. 1 < a /\ 0 < n ==> + !p. (LOG a n = p) <=> a ** p <= n /\ n < a ** SUC p + LOG_EVAL |- !m n. LOG m n = if m <= 1 \/ n = 0 then LOG m n + else if n < m then 0 else SUC (LOG m (n DIV m)) + LOG_TEST |- !a n. 1 < a /\ 0 < n ==> + !p. (LOG a n = p) <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n + LOG_POWER |- !b x n. 1 < b /\ 0 < x /\ 0 < n ==> + n * LOG b x <= LOG b (x ** n) /\ + LOG b (x ** n) < n * SUC (LOG b x) + LOG_LE_REVERSE |- !a b n. 1 < a /\ 0 < n /\ a <= b ==> LOG b n <= LOG a n + +# LOG2_1 |- LOG2 1 = 0 +# LOG2_2 |- LOG2 2 = 1 + LOG2_THM |- !n. 0 < n ==> !p. (LOG2 n = p) <=> 2 ** p <= n /\ n < 2 ** SUC p + LOG2_PROPERTY |- !n. 0 < n ==> 2 ** LOG2 n <= n /\ n < 2 ** SUC (LOG2 n) + TWO_EXP_LOG2_LE |- !n. 0 < n ==> 2 ** LOG2 n <= n + LOG2_UNIQUE |- !n m. 2 ** m <= n /\ n < 2 ** SUC m ==> (LOG2 n = m) + LOG2_EQ_0 |- !n. 0 < n ==> (LOG2 n = 0 <=> n = 1) + LOG2_EQ_1 |- !n. 0 < n ==> ((LOG2 n = 1) <=> (n = 2) \/ (n = 3)) + LOG2_LE_MONO |- !n m. 0 < n ==> n <= m ==> LOG2 n <= LOG2 m + LOG2_LE |- !n m. 0 < n /\ n <= m ==> LOG2 n <= LOG2 m + LOG2_LT |- !n m. 0 < n /\ n < m ==> LOG2 n <= LOG2 m + LOG2_LT_SELF |- !n. 0 < n ==> LOG2 n < n + LOG2_NEQ_SELF |- !n. 0 < n ==> LOG2 n <> n + LOG2_EQ_SELF |- !n. (LOG2 n = n) ==> (n = 0) +# LOG2_POS |- !n. 1 < n ==> 0 < LOG2 n + LOG2_TWICE_LT |- !n. 1 < n ==> 1 < 2 * LOG2 n + LOG2_TWICE_SQ |- !n. 1 < n ==> 4 <= (2 * LOG2 n) ** 2 + LOG2_SUC_TWICE_SQ |- !n. 0 < n ==> 4 <= (2 * SUC (LOG2 n)) ** 2 + LOG2_SUC_SQ |- !n. 1 < n ==> 1 < SUC (LOG2 n) ** 2 + LOG2_SUC_TIMES_SQ_DIV_2_POS |- !n m. 1 < m ==> 0 < SUC (LOG2 n) * (m ** 2 DIV 2) + LOG2_2_EXP |- !n. LOG2 (2 ** n) = n + LOG2_EXACT_EXP |- !n. (2 ** LOG2 n = n) <=> ?k. n = 2 ** k + LOG2_MULT_EXP |- !n m. 0 < n ==> (LOG2 (n * 2 ** m) = LOG2 n + m) + LOG2_TWICE |- !n. 0 < n ==> (LOG2 (TWICE n) = 1 + LOG2 n) + LOG2_HALF |- !n. 1 < n ==> (LOG2 (HALF n) = LOG2 n - 1) + LOG2_BY_HALF |- !n. 1 < n ==> (LOG2 n = 1 + LOG2 (HALF n)) + LOG2_DIV_EXP |- !n m. 2 ** m < n ==> (LOG2 (n DIV 2 ** m) = LOG2 n - m) + + LOG2 Computation: + halves_def |- !n. halves n = if n = 0 then 0 else SUC (halves (HALF n)) + halves_alt |- !n. halves n = if n = 0 then 0 else 1 + halves (HALF n) +# halves_0 |- halves 0 = 0 +# halves_1 |- halves 1 = 1 +# halves_2 |- halves 2 = 2 +# halves_pos |- !n. 0 < n ==> 0 < halves n + halves_by_LOG2 |- !n. 0 < n ==> (halves n = 1 + LOG2 n) + LOG2_compute |- !n. LOG2 n = if n = 0 then LOG2 0 else halves n - 1 + halves_le |- !m n. m <= n ==> halves m <= halves n + halves_eq_0 |- !n. (halves n = 0) <=> (n = 0) + halves_eq_1 |- !n. (halves n = 1) <=> (n = 1) + + Perfect Power and Power Free: + perfect_power_def |- !n m. perfect_power n m <=> ?e. n = m ** e + perfect_power_self |- !n. perfect_power n n + perfect_power_0_m |- !m. perfect_power 0 m <=> (m = 0) + perfect_power_1_m |- !m. perfect_power 1 m + perfect_power_n_0 |- !n. perfect_power n 0 <=> (n = 0) \/ (n = 1) + perfect_power_n_1 |- !n. perfect_power n 1 <=> (n = 1) + perfect_power_mod_eq_0 |- !n m. 0 < m /\ 1 < n /\ n MOD m = 0 ==> + (perfect_power n m <=> perfect_power (n DIV m) m) + perfect_power_mod_ne_0 |- !n m. 0 < m /\ 1 < n /\ n MOD m <> 0 ==> ~perfect_power n m + perfect_power_test |- !n m. perfect_power n m <=> + if n = 0 then m = 0 + else if n = 1 then T + else if m = 0 then n <= 1 + else if m = 1 then n = 1 + else if n MOD m = 0 then perfect_power (n DIV m) m + else F + perfect_power_suc |- !m n. 1 < m /\ perfect_power n m /\ perfect_power (SUC n) m ==> + (m = 2) /\ (n = 1) + perfect_power_not_suc |- !m n. 1 < m /\ 1 < n /\ perfect_power n m ==> ~perfect_power (SUC n) m + LOG_SUC |- !b n. 1 < b /\ 0 < n ==> + LOG b (SUC n) = LOG b n + + if perfect_power (SUC n) b then 1 else 0 + perfect_power_bound_LOG2 |- !n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= LOG2 n /\ (n = m ** k) + perfect_power_condition |- !p q. prime p /\ (?x y. 0 < x /\ (p ** x = q ** y)) ==> perfect_power q p + perfect_power_cofactor |- !n p. 0 < p /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) + perfect_power_cofactor_alt + |- !n p. 0 < n /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) + perfect_power_2_odd |- !n. perfect_power n 2 ==> (ODD n <=> (n = 1)) + + Power Free: + power_free_def |- !n. power_free n <=> !m e. (n = m ** e) ==> (m = n) /\ (e = 1) + power_free_0 |- power_free 0 <=> F + power_free_1 |- power_free 1 <=> F + power_free_gt_1 |- !n. power_free n ==> 1 < n + power_free_alt |- !n. power_free n <=> 1 < n /\ !m. perfect_power n m ==> (n = m) + prime_is_power_free |- !n. prime n ==> power_free n + power_free_perfect_power |- !m n. power_free n /\ perfect_power n m ==> (n = m) + power_free_property |- !n. power_free n ==> !j. 1 < j ==> ROOT j n ** j <> n + power_free_check_all |- !n. power_free n <=> 1 < n /\ !j. 1 < j ==> ROOT j n ** j <> n + + Upper Logarithm: + count_up_def |- !n m k. count_up n m k = if m = 0 then 0 + else if n <= m then k + else count_up n (2 * m) (SUC k) + ulog_def |- !n. ulog n = count_up n 1 0 +# ulog_0 |- ulog 0 = 0 +# ulog_1 |- ulog 1 = 0 +# ulog_2 |- ulog 2 = 1 + + count_up_exit |- !m n. m <> 0 /\ n <= m ==> !k. count_up n m k = k + count_up_suc |- !m n. m <> 0 /\ m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k) + count_up_suc_eqn |- !m. m <> 0 ==> !n t. 2 ** t * m < n ==> + !k. count_up n m k = count_up n (2 ** SUC t * m) (SUC k + t) + count_up_exit_eqn |- !m. m <> 0 ==> !n t. 2 ** t * m < 2 * n /\ n <= 2 ** t * m ==> + !k. count_up n m k = k + t + ulog_unique |- !m n. 2 ** m < 2 * n /\ n <= 2 ** m ==> (ulog n = m) + ulog_eqn |- !n. ulog n = if 1 < n then SUC (LOG2 (n - 1)) else 0 + ulog_suc |- !n. 0 < n ==> (ulog (SUC n) = SUC (LOG2 n)) + ulog_property |- !n. 0 < n ==> 2 ** ulog n < 2 * n /\ n <= 2 ** ulog n + ulog_thm |- !n. 0 < n ==> !m. (ulog n = m) <=> 2 ** m < 2 * n /\ n <= 2 ** m + ulog_def_alt |- (ulog 0 = 0) /\ + !n. 0 < n ==> !m. (ulog n = m) <=> n <= 2 ** m /\ 2 ** m < TWICE n + ulog_eq_0 |- !n. (ulog n = 0) <=> (n = 0) \/ (n = 1) + ulog_eq_1 |- !n. (ulog n = 1) <=> (n = 2) + ulog_le_1 |- !n. ulog n <= 1 <=> n <= 2 + ulog_le |- !m n. n <= m ==> ulog n <= ulog m + ulog_lt |- !m n. n < m ==> ulog n <= ulog m + ulog_2_exp |- !n. ulog (2 ** n) = n + ulog_le_self |- !n. ulog n <= n + ulog_eq_self |- !n. (ulog n = n) <=> (n = 0) + ulog_lt_self |- !n. 0 < n ==> ulog n < n + ulog_exp_exact |- !n. (2 ** ulog n = n) <=> perfect_power n 2 + ulog_exp_not_exact |- !n. ~perfect_power n 2 ==> 2 ** ulog n <> n + ulog_property_not_exact |- !n. 0 < n /\ ~perfect_power n 2 ==> n < 2 ** ulog n + ulog_property_odd |- !n. 1 < n /\ ODD n ==> n < 2 ** ulog n + exp_to_ulog |- !m n. n <= 2 ** m ==> ulog n <= m +# ulog_pos |- !n. 1 < n ==> 0 < ulog n + ulog_ge_1 |- !n. 1 < n ==> 1 <= ulog n + ulog_sq_gt_1 |- !n. 2 < n ==> 1 < ulog n ** 2 + ulog_twice_sq |- !n. 1 < n ==> 4 <= TWICE (ulog n) ** 2 + ulog_alt |- !n. ulog n = if n = 0 then 0 + else if perfect_power n 2 then LOG2 n else SUC (LOG2 n) + ulog_LOG2 |- !n. 0 < n ==> LOG2 n <= ulog n /\ ulog n <= 1 + LOG2 n + perfect_power_bound_ulog + |- !n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= ulog n /\ (n = m ** k) + + Upper Log Theorems: + ulog_mult |- !m n. ulog (m * n) <= ulog m + ulog n + ulog_exp |- !m n. ulog (m ** n) <= n * ulog m + ulog_even |- !n. 0 < n /\ EVEN n ==> (ulog n = 1 + ulog (HALF n)) + ulog_odd |- !n. 1 < n /\ ODD n ==> ulog (HALF n) + 1 <= ulog n + ulog_half |- !n. 1 < n ==> ulog (HALF n) + 1 <= ulog n + sqrt_upper |- !n. SQRT n <= 2 ** ulog n + + Power Free up to a limit: + power_free_upto_def |- !n k. n power_free_upto k <=> !j. 1 < j /\ j <= k ==> ROOT j n ** j <> n + power_free_upto_0 |- !n. n power_free_upto 0 <=> T + power_free_upto_1 |- !n. n power_free_upto 1 <=> T + power_free_upto_suc |- !n k. 0 < k /\ n power_free_upto k ==> + (n power_free_upto k + 1 <=> ROOT (k + 1) n ** (k + 1) <> n) + power_free_check_upto |- !n b. LOG2 n <= b ==> (power_free n <=> 1 < n /\ n power_free_upto b) + power_free_check_upto_LOG2 |- !n. power_free n <=> 1 < n /\ n power_free_upto LOG2 n + power_free_check_upto_ulog |- !n. power_free n <=> 1 < n /\ n power_free_upto ulog n + power_free_2 |- power_free 2 + power_free_3 |- power_free 3 + power_free_test_def |- !n. power_free_test n <=> 1 < n /\ n power_free_upto ulog n + power_free_test_eqn |- !n. power_free_test n <=> power_free n + power_free_test_upto_LOG2 |- !n. power_free n <=> + 1 < n /\ !j. 1 < j /\ j <= LOG2 n ==> ROOT j n ** j <> n + power_free_test_upto_ulog |- !n. power_free n <=> + 1 < n /\ !j. 1 < j /\ j <= ulog n ==> ROOT j n ** j <> n + + Another Characterisation of Power Free: + power_index_def |- !n k. power_index n k = + if k <= 1 then 1 + else if ROOT k n ** k = n then k + else power_index n (k - 1) + power_index_0 |- !n. power_index n 0 = 1 + power_index_1 |- !n. power_index n 1 = 1 + power_index_eqn |- !n k. ROOT (power_index n k) n ** power_index n k = n + power_index_root |- !n k. perfect_power n (ROOT (power_index n k) n) + power_index_of_1 |- !k. power_index 1 k = if k = 0 then 1 else k + power_index_exact_root |- !n k. 0 < k /\ (ROOT k n ** k = n) ==> (power_index n k = k) + power_index_not_exact_root |- !n k. ROOT k n ** k <> n ==> (power_index n k = power_index n (k - 1)) + power_index_no_exact_roots |- !m n k. k <= m /\ (!j. k < j /\ j <= m ==> ROOT j n ** j <> n) ==> + (power_index n m = power_index n k) + power_index_lower |- !m n k. k <= m /\ (ROOT k n ** k = n) ==> k <= power_index n m + power_index_pos |- !n k. 0 < power_index n k + power_index_upper |- !n k. 0 < k ==> power_index n k <= k + power_index_equal |- !m n k. 0 < k /\ k <= m ==> + ((power_index n m = power_index n k) <=> !j. k < j /\ j <= m ==> ROOT j n ** j <> n) + power_index_property |- !m n k. (power_index n m = k) ==> !j. k < j /\ j <= m ==> ROOT j n ** j <> n + + power_free_by_power_index_LOG2 + |- !n. power_free n <=> 1 < n /\ (power_index n (LOG2 n) = 1) + power_free_by_power_index_ulog + |- !n. power_free n <=> 1 < n /\ (power_index n (ulog n) = 1) + +*) + +(* Rework proof of ROOT_COMPUTE in logroot theory. *) +(* ./num/extra_theories/logrootScript.sml *) + +(* ROOT r n = r-th root of n. + +Make use of indentity: +n ^ (1/r) = 2 (n/ 2^r) ^(1/r) + +if n = 0 then 0 +else (* precompute *) let x = 2 * r-th root of (n DIV (2 ** r)) + (* apply *) in if n < (SUC x) ** r then x else (SUC x) +*) + +(* Theorem: 0 < r ==> (ROOT r n = + let m = 2 * ROOT r (n DIV 2 ** r) in m + if (m + 1) ** r <= n then 1 else 0) *) +(* Proof: + ROOT k n + = if n < SUC m ** k then m else SUC m by ROOT_COMPUTE + = if SUC m ** k <= n then SUC m else m by logic + = if (m + 1) ** k <= n then (m + 1) else m by ADD1 + = m + if (m + 1) ** k <= n then 1 else 0 by arithmetic +*) +val ROOT_EQN = store_thm( + "ROOT_EQN", + ``!r n. 0 < r ==> (ROOT r n = + let m = 2 * ROOT r (n DIV 2 ** r) in m + if (m + 1) ** r <= n then 1 else 0)``, + rw_tac std_ss[] >> + Cases_on `(m + 1) ** r <= n` >- + rw[ROOT_COMPUTE, ADD1] >> + rw[ROOT_COMPUTE, ADD1]); + +(* ------------------------------------------------------------------------- *) +(* Square Root *) +(* ------------------------------------------------------------------------- *) + +(* +> EVAL ``SQRT 4``; +val it = |- SQRT 4 = 2: thm +> EVAL ``(SQRT 4) ** 2``; +val it = |- SQRT 4 ** 2 = 4: thm +> EVAL ``(SQRT 5) ** 2``; +val it = |- SQRT 5 ** 2 = 4: thm +> EVAL ``(SQRT 8) ** 2``; +val it = |- SQRT 8 ** 2 = 4: thm +> EVAL ``(SQRT 9) ** 2``; +val it = |- SQRT 9 ** 2 = 9: thm + +> EVAL ``LOG2 4``; +val it = |- LOG2 4 = 2: thm +> EVAL ``2 ** (LOG2 4)``; +val it = |- 2 ** LOG2 4 = 4: thm +> EVAL ``2 ** (LOG2 5)``; +val it = |- 2 ** LOG2 5 = 4: thm +> EVAL ``2 ** (LOG2 6)``; +val it = |- 2 ** LOG2 6 = 4: thm +> EVAL ``2 ** (LOG2 7)``; +val it = |- 2 ** LOG2 7 = 4: thm +> EVAL ``2 ** (LOG2 8)``; +val it = |- 2 ** LOG2 8 = 8: thm + +> EVAL ``SQRT 9``; +val it = |- SQRT 9 = 3: thm +> EVAL ``SQRT 8``; +val it = |- SQRT 8 = 2: thm +> EVAL ``SQRT 7``; +val it = |- SQRT 7 = 2: thm +> EVAL ``SQRT 6``; +val it = |- SQRT 6 = 2: thm +> EVAL ``SQRT 5``; +val it = |- SQRT 5 = 2: thm +> EVAL ``SQRT 4``; +val it = |- SQRT 4 = 2: thm +> EVAL ``SQRT 3``; +val it = |- SQRT 3 = 1: thm +*) + +(* +EXP_BASE_LT_MONO |- !b. 1 < b ==> !n m. b ** m < b ** n <=> m < n +LT_EXP_ISO |- !e a b. 1 < e ==> (a < b <=> e ** a < e ** b) + +ROOT_exists |- !r n. 0 < r ==> ?rt. rt ** r <= n /\ n < SUC rt ** r +ROOT_UNIQUE |- !r n p. p ** r <= n /\ n < SUC p ** r ==> (ROOT r n = p) +ROOT_LE_MONO |- !r x y. 0 < r ==> x <= y ==> ROOT r x <= ROOT r y + +LOG_exists |- ?f. !a n. 1 < a /\ 0 < n ==> a ** f a n <= n /\ n < a ** SUC (f a n) +LOG_UNIQUE |- !a n p. a ** p <= n /\ n < a ** SUC p ==> (LOG a n = p) +LOG_LE_MONO |- !a x y. 1 < a /\ 0 < x ==> x <= y ==> LOG a x <= LOG a y + +LOG_EXP |- !n a b. 1 < a /\ 0 < b ==> (LOG a (a ** n * b) = n + LOG a b) +LOG |- !a n. 1 < a /\ 0 < n ==> a ** LOG a n <= n /\ n < a ** SUC (LOG a n) +*) + +(* Theorem: SQ (SQRT n) <= n *) +(* Proof: by SQRT_PROPERTY, EXP_2 *) +val SQ_SQRT_LE = store_thm( + "SQ_SQRT_LE", + ``!n. SQ (SQRT n) <= n``, + metis_tac[SQRT_PROPERTY, EXP_2]); + +(* Extract theorem *) +Theorem SQ_SQRT_LE_alt = SQRT_PROPERTY |> SPEC_ALL |> CONJUNCT1 |> GEN_ALL; +(* val SQ_SQRT_LE_alt = |- !n. SQRT n ** 2 <= n: thm *) + +(* Theorem: SQRT (SQ n) = n *) +(* Proof: + SQRT (SQ n) + = SQRT (n ** 2) by EXP_2 + = n by SQRT_EXP_2 +*) +val SQRT_SQ = store_thm( + "SQRT_SQ", + ``!n. SQRT (SQ n) = n``, + metis_tac[SQRT_EXP_2, EXP_2]); + +(* Theorem: SQRT n <= n *) +(* Proof: + Note n <= n ** 2 by SELF_LE_SQ + Thus SQRT n <= SQRT (n ** 2) by SQRT_LE + or SQRT n <= n by SQRT_EXP_2 +*) +val SQRT_LE_SELF = store_thm( + "SQRT_LE_SELF", + ``!n. SQRT n <= n``, + metis_tac[SELF_LE_SQ, SQRT_LE, SQRT_EXP_2]); + +(* Theorem: SQRT n <= m ==> n <= 3 * (m ** 2) *) +(* Proof: + Note n < (SUC (SQRT n)) ** 2 by SQRT_PROPERTY + = SUC ((SQRT n) ** 2) + 2 * SQRT n by SUC_SQ + Thus n <= m ** 2 + 2 * m by SQRT n <= m + <= m ** 2 + 2 * m ** 2 by arithmetic + = 3 * m ** 2 +*) +val SQRT_LE_IMP = store_thm( + "SQRT_LE_IMP", + ``!n m. SQRT n <= m ==> n <= 3 * (m ** 2)``, + rpt strip_tac >> + `n < (SUC (SQRT n)) ** 2` by rw[SQRT_PROPERTY] >> + `SUC (SQRT n) ** 2 = SUC ((SQRT n) ** 2) + 2 * SQRT n` by rw[SUC_SQ] >> + `SQRT n ** 2 <= m ** 2` by rw[] >> + `2 * SQRT n <= 2 * m` by rw[] >> + `2 * m <= 2 * m * m` by rw[] >> + `2 * m * m = 2 * m ** 2` by rw[] >> + decide_tac); + +(* Theorem: (SQRT n) * (SQRT m) <= SQRT (n * m) *) +(* Proof: + Note (SQRT n) ** 2 <= n by SQRT_PROPERTY + and (SQRT m) ** 2 <= m by SQRT_PROPERTY + so (SQRT n) ** 2 * (SQRT m) ** 2 <= n * m by LE_MONO_MULT2 + or ((SQRT n) * (SQRT m)) ** 2 <= n * m by EXP_BASE_MULT + ==> (SQRT n) * (SQRT m) <= SQRT (n * m) by SQRT_LE, SQRT_OF_SQ +*) +Theorem SQRT_MULT_LE: + !n m. (SQRT n) * (SQRT m) <= SQRT (n * m) +Proof + rpt strip_tac >> + qabbrev_tac `h = SQRT n` >> + qabbrev_tac `k = SQRT m` >> + `h ** 2 <= n` by simp[SQRT_PROPERTY, Abbr`h`] >> + `k ** 2 <= m` by simp[SQRT_PROPERTY, Abbr`k`] >> + `(h * k) ** 2 <= n * m` by metis_tac[LE_MONO_MULT2, EXP_BASE_MULT] >> + metis_tac[SQRT_LE, SQRT_OF_SQ] +QED + +(* ------------------------------------------------------------------------- *) +(* Square predicate *) +(* ------------------------------------------------------------------------- *) + +(* Define square predicate. *) + +Definition square_def[nocompute]: + square (n:num) = ?k. n = k * k +End +(* use [nocompute] as this is not effective. *) + +(* Theorem: square n = ?k. n = k ** 2 *) +(* Proof: by square_def. *) +Theorem square_alt: + !n. square n = ?k. n = k ** 2 +Proof + simp[square_def] +QED + +(* Theorem: square n <=> (SQRT n) ** 2 = n *) +(* Proof: + If part: square n ==> (SQRT n) ** 2 = n + This is true by SQRT_SQ, EXP_2 + Only-if part: (SQRT n) ** 2 = n ==> square n + Take k = SQRT n for n = k ** 2. +*) +Theorem square_eqn[compute]: + !n. square n <=> (SQRT n) ** 2 = n +Proof + metis_tac[square_def, SQRT_SQ, EXP_2] +QED + +(* +EVAL ``square 10``; F +EVAL ``square 16``; T +*) + +(* Theorem: square 0 *) +(* Proof: by 0 = 0 * 0. *) +Theorem square_0: + square 0 +Proof + simp[square_def] +QED + +(* Theorem: square 1 *) +(* Proof: by 1 = 1 * 1. *) +Theorem square_1: + square 1 +Proof + simp[square_def] +QED + +(* Theorem: prime p ==> ~square p *) +(* Proof: + By contradiction, suppose (square p). + Then p = k * k by square_def + thus k divides p by divides_def + so k = 1 or k = p by prime_def + If k = 1, + then p = 1 * 1 = 1 by arithmetic + but p <> 1 by NOT_PRIME_1 + If k = p, + then p * 1 = p * p by arithmetic + or 1 = p by EQ_MULT_LCANCEL, NOT_PRIME_0 + but p <> 1 by NOT_PRIME_1 +*) +Theorem prime_non_square: + !p. prime p ==> ~square p +Proof + rpt strip_tac >> + `?k. p = k * k` by rw[GSYM square_def] >> + `k divides p` by metis_tac[divides_def] >> + `(k = 1) \/ (k = p)` by metis_tac[prime_def] >- + fs[NOT_PRIME_1] >> + `p * 1 = p * p` by metis_tac[MULT_RIGHT_1] >> + `1 = p` by metis_tac[EQ_MULT_LCANCEL, NOT_PRIME_0] >> + metis_tac[NOT_PRIME_1] +QED + +(* Theorem: ~square n ==> (SQRT n) * (SQRT n) < n *) +(* Proof: + Note (SQRT n) * (SQRT n) <= n by SQ_SQRT_LE + but (SQRT n) * (SQRT n) <> n by square_def + so (SQRT n) * (SQRT n) < n by inequality +*) +Theorem SQ_SQRT_LT: + !n. ~square n ==> (SQRT n) * (SQRT n) < n +Proof + rpt strip_tac >> + `(SQRT n) * (SQRT n) <= n` by simp[SQ_SQRT_LE] >> + `(SQRT n) * (SQRT n) <> n` by metis_tac[square_def] >> + decide_tac +QED + +(* Theorem: ~square n ==> SQRT n ** 2 < n *) +(* Proof: by SQ_SQRT_LT, EXP_2. *) +Theorem SQ_SQRT_LT_alt: + !n. ~square n ==> SQRT n ** 2 < n +Proof + metis_tac[SQ_SQRT_LT, EXP_2] +QED + +(* Theorem: ~square n ==> ((2 * m + 1) ** 2 < n <=> m < HALF (1 + SQRT n)) *) +(* Proof: + If part: (2 * m + 1) ** 2 < n ==> m < HALF (1 + SQRT n) + (2 * m + 1) ** 2 < n + ==> 2 * m + 1 <= SQRT n by SQRT_LT, SQRT_OF_SQ + ==> 2 * (m + 1) <= 1 + SQRT n by arithmetic + ==> m < HALF (1 + SQRT n) by X_LT_DIV + Only-if part: m < HALF (1 + SQRT n) ==> (2 * m + 1) ** 2 < n + m < HALF (1 + SQRT n) + <=> 2 * (m + 1) <= 1 + SQRT n by X_LT_DIV + <=> 2 * m + 1 <= SQRT n by arithmetic + <=> (2 * m + 1) ** 2 <= (SQRT n) ** 2 by EXP_EXP_LE_MONO + ==> (2 * m + 1) ** 2 <= n by SQ_SQRT_LE_alt + But n <> (2 * m + 1) ** 2 by ~square n + so (2 * m + 1) ** 2 < n +*) +Theorem odd_square_lt: + !n m. ~square n ==> ((2 * m + 1) ** 2 < n <=> m < HALF (1 + SQRT n)) +Proof + rw[EQ_IMP_THM] >| [ + `2 * m + 1 <= SQRT n` by metis_tac[SQRT_LT, SQRT_OF_SQ] >> + `2 * (m + 1) <= 1 + SQRT n` by decide_tac >> + fs[X_LT_DIV], + `2 * (m + 1) <= 1 + SQRT n` by fs[X_LT_DIV] >> + `2 * m + 1 <= SQRT n` by decide_tac >> + `(2 * m + 1) ** 2 <= (SQRT n) ** 2` by simp[] >> + `(SQRT n) ** 2 <= n` by fs[SQ_SQRT_LE_alt] >> + `n <> (2 * m + 1) ** 2` by metis_tac[square_alt] >> + decide_tac + ] +QED + +(* Theorem: 1 < m ==> 0 < SUC (LOG2 n) * (m ** 2 DIV 2) *) +(* Proof: + Since 1 < m ==> 1 < m ** 2 DIV 2 by ONE_LT_HALF_SQ + Hence 0 < m ** 2 DIV 2 + and 0 < 0 < SUC (LOG2 n) by prim_recTheory.LESS_0 + Therefore 0 < SUC (LOG2 n) * (m ** 2 DIV 2) by ZERO_LESS_MULT +*) +val LOG2_SUC_TIMES_SQ_DIV_2_POS = store_thm( + "LOG2_SUC_TIMES_SQ_DIV_2_POS", + ``!n m. 1 < m ==> 0 < SUC (LOG2 n) * (m ** 2 DIV 2)``, + rpt strip_tac >> + `1 < m ** 2 DIV 2` by rw[ONE_LT_HALF_SQ] >> + `0 < m ** 2 DIV 2 /\ 0 < SUC (LOG2 n)` by decide_tac >> + rw[ZERO_LESS_MULT]); + +(* Theorem: 1 < n ==> LOG2 (HALF n) = (LOG2 n) - 1 *) +(* Proof: + Note: > LOG_DIV |> SPEC ``2`` |> SPEC ``n:num``; + val it = |- 1 < 2 /\ 2 <= n ==> LOG2 n = 1 + LOG2 (HALF n): thm + Hence the result. +*) +val LOG2_HALF = store_thm( + "LOG2_HALF", + ``!n. 1 < n ==> (LOG2 (HALF n) = (LOG2 n) - 1)``, + rpt strip_tac >> + `LOG2 n = 1 + LOG2 (HALF n)` by rw[LOG_DIV] >> + decide_tac); + +(* Theorem: 1 < n ==> (LOG2 n = 1 + LOG2 (HALF n)) *) +(* Proof: by LOG_DIV: +> LOG_DIV |> SPEC ``2``; +val it = |- !x. 1 < 2 /\ 2 <= x ==> (LOG2 x = 1 + LOG2 (HALF x)): thm +*) +val LOG2_BY_HALF = store_thm( + "LOG2_BY_HALF", + ``!n. 1 < n ==> (LOG2 n = 1 + LOG2 (HALF n))``, + rw[LOG_DIV]); + +(* Theorem: 2 ** m < n ==> LOG2 (n DIV 2 ** m) = (LOG2 n) - m *) +(* Proof: + By induction on m. + Base: !n. 2 ** 0 < n ==> LOG2 (n DIV 2 ** 0) = LOG2 n - 0 + LOG2 (n DIV 2 ** 0) + = LOG2 (n DIV 1) by EXP_0 + = LOG2 n by DIV_1 + = LOG2 n - 0 by SUB_0 + Step: !n. 2 ** m < n ==> LOG2 (n DIV 2 ** m) = LOG2 n - m ==> + !n. 2 ** SUC m < n ==> LOG2 (n DIV 2 ** SUC m) = LOG2 n - SUC m + Note 2 ** SUC m = 2 * 2 ** m by EXP, [1] + Thus HALF (2 * 2 ** m) <= HALF n by DIV_LE_MONOTONE + or 2 ** m <= HALF n by HALF_TWICE + If 2 ** m < HALF n, + LOG2 (n DIV 2 ** SUC m) + = LOG2 (n DIV (2 * 2 ** m)) by [1] + = LOG2 ((HALF n) DIV 2 ** m) by DIV_DIV_DIV_MULT + = LOG2 (HALF n) - m by induction hypothesis, 2 ** m < HALF n + = (LOG2 n - 1) - m by LOG2_HALF, 1 < n + = LOG2 n - (1 + m) by arithmetic + = LOG2 n - SUC m by ADD1 + Otherwise 2 ** m = HALF n, + LOG2 (n DIV 2 ** SUC m) + = LOG2 (n DIV (2 * 2 ** m)) by [1] + = LOG2 ((HALF n) DIV 2 ** m) by DIV_DIV_DIV_MULT + = LOG2 ((HALF n) DIV (HALF n)) by 2 ** m = HALF n + = LOG2 1 by DIVMOD_ID, 0 < HALF n + = 0 by LOG2_1 + LOG2 n + = 1 + LOG2 (HALF n) by LOG_DIV + = 1 + LOG2 (2 ** m) by 2 ** m = HALF n + = 1 + m by LOG2_2_EXP + = SUC m by SUC_ONE_ADD + Thus RHS = LOG2 n - SUC m = 0 = LHS. +*) + +Theorem LOG2_DIV_EXP: + !n m. 2 ** m < n ==> LOG2 (n DIV 2 ** m) = LOG2 n - m +Proof + Induct_on ‘m’ >- rw[] >> + rpt strip_tac >> + ‘1 < 2 ** SUC m’ by rw[ONE_LT_EXP] >> + ‘1 < n’ by decide_tac >> + fs[EXP] >> + ‘2 ** m <= HALF n’ + by metis_tac[DIV_LE_MONOTONE, HALF_TWICE, LESS_IMP_LESS_OR_EQ, + DECIDE “0 < 2”] >> + ‘LOG2 (n DIV (TWICE (2 ** m))) = LOG2 ((HALF n) DIV 2 ** m)’ + by rw[DIV_DIV_DIV_MULT] >> + fs[LESS_OR_EQ] >- rw[LOG2_HALF] >> + ‘LOG2 n = 1 + LOG2 (HALF n)’ by rw[LOG_DIV] >> + ‘_ = 1 + m’ by metis_tac[LOG2_2_EXP] >> + ‘_ = SUC m’ by rw[] >> + ‘0 < HALF n’ suffices_by rw[] >> + metis_tac[DECIDE “0 < 2”, ZERO_LT_EXP] +QED + +(* ------------------------------------------------------------------------- *) +(* LOG2 Computation *) +(* ------------------------------------------------------------------------- *) + +(* Define halves n = count of HALFs of n to 0, recursively. *) +val halves_def = Define` + halves n = if n = 0 then 0 else SUC (halves (HALF n)) +`; + +(* Theorem: halves n = if n = 0 then 0 else 1 + (halves (HALF n)) *) +(* Proof: by halves_def, ADD1 *) +val halves_alt = store_thm( + "halves_alt", + ``!n. halves n = if n = 0 then 0 else 1 + (halves (HALF n))``, + rw[Once halves_def, ADD1]); + +(* Extract theorems from definition *) +val halves_0 = save_thm("halves_0[simp]", halves_def |> SPEC ``0`` |> SIMP_RULE arith_ss[]); +(* val halves_0 = |- halves 0 = 0: thm *) +val halves_1 = save_thm("halves_1[simp]", halves_def |> SPEC ``1`` |> SIMP_RULE arith_ss[]); +(* val halves_1 = |- halves 1 = 1: thm *) +val halves_2 = save_thm("halves_2[simp]", halves_def |> SPEC ``2`` |> SIMP_RULE arith_ss[halves_1]); +(* val halves_2 = |- halves 2 = 2: thm *) + +(* Theorem: 0 < n ==> 0 < halves n *) +(* Proof: by halves_def *) +val halves_pos = store_thm( + "halves_pos[simp]", + ``!n. 0 < n ==> 0 < halves n``, + rw[Once halves_def]); + +(* Theorem: 0 < n ==> (halves n = 1 + LOG2 n) *) +(* Proof: + By complete induction on n. + Assume: !m. m < n ==> 0 < m ==> (halves m = 1 + LOG2 m) + To show: 0 < n ==> (halves n = 1 + LOG2 n) + Note HALF n < n by HALF_LT, 0 < n + Need 0 < HALF n to apply induction hypothesis. + If HALF n = 0, + Then n = 1 by HALF_EQ_0 + halves 1 + = SUC (halves 0) by halves_def + = 1 by halves_def + = 1 + LOG2 1 by LOG2_1 + If HALF n <> 0, + Then n <> 1 by HALF_EQ_0 + so 1 < n by n <> 0, n <> 1. + halves n + = SUC (halves (HALF n)) by halves_def + = SUC (1 + LOG2 (HALF n)) by induction hypothesis + = SUC (LOG2 n) by LOG2_BY_HALF + = 1 + LOG2 n by ADD1 +*) +val halves_by_LOG2 = store_thm( + "halves_by_LOG2", + ``!n. 0 < n ==> (halves n = 1 + LOG2 n)``, + completeInduct_on `n` >> + strip_tac >> + rw[Once halves_def] >> + Cases_on `n = 1` >- + simp[Once halves_def] >> + `HALF n < n` by rw[HALF_LT] >> + `HALF n <> 0` by fs[HALF_EQ_0] >> + simp[LOG2_BY_HALF]); + +(* Theorem: LOG2 n = if n = 0 then LOG2 0 else (halves n - 1) *) +(* Proof: + If 0 < n, + Note 0 < halves n by halves_pos + and halves n = 1 + LOG2 n by halves_by_LOG2 + or LOG2 n = halves - 1. + If n = 0, make it an infinite loop. +*) +val LOG2_compute = store_thm( + "LOG2_compute[compute]", + ``!n. LOG2 n = if n = 0 then LOG2 0 else (halves n - 1)``, + rpt strip_tac >> + (Cases_on `n = 0` >> simp[]) >> + `0 < halves n` by rw[] >> + `halves n = 1 + LOG2 n` by rw[halves_by_LOG2] >> + decide_tac); + +(* Put this to computeLib *) +(* val _ = computeLib.add_persistent_funs ["LOG2_compute"]; *) + +(* +EVAL ``LOG2 16``; --> 4 +EVAL ``LOG2 17``; --> 4 +EVAL ``LOG2 32``; --> 5 +EVAL ``LOG2 1024``; --> 10 +EVAL ``LOG2 1023``; --> 9 +*) + +(* Michael's method *) +(* +Define `count_divs n = if 2 <= n then 1 + count_divs (n DIV 2) else 0`; + +g `0 < n ==> (LOG2 n = count_divs n)`; +e (completeInduct_on `n`); +e strip_tac; +e (ONCE_REWRITE_TAC [theorm "count_divs_def"]); +e (Cases_on `2 <= n`); +e (mp_tac (Q.SPECL [`2`, `n`] LOG_DIV)); +e (simp[]); +(* prove on-the-fly *) +e (`0 < n DIV 2` suffices_by simp[]); +(* DB.match [] ``x < k DIV n``; *) +e (simp[arithmeticTheory.X_LT_DIV]); +e (`n = 1` by simp[]); +LOG_1; +e (simp[it]); +val foo = top_thm(); + +g `!n. LOG2 n = if 0 < n then count_divs n else LOG2 n`; + +e (rw[]); +e (simp[foo]); +e (lfs[]); ??? + +val bar = top_thm(); +var bar = save_thm("bar", bar); +computeLib.add_persistent_funs ["bar"]; +EVAL ``LOG2 16``; +EVAL ``LOG2 17``; +EVAL ``LOG2 32``; +EVAL ``LOG2 1024``; +EVAL ``LOG2 1023``; +EVAL ``LOG2 0``; -- loops! + +So for n = 97, +EVAL ``LOG2 97``; --> 6 +EVAL ``4 * LOG2 97 * LOG2 97``; --> 4 * 6 * 6 = 4 * 36 = 144 + +Need ord_r (97) > 144, r < 97, not possible ??? + +val count_divs_def = Define `count_divs n = if 1 < n then 1 + count_divs (n DIV 2) else 0`; + +val LOG2_by_count_divs = store_thm( + "LOG2_by_count_divs", + ``!n. 0 < n ==> (LOG2 n = count_divs n)``, + completeInduct_on `n` >> + strip_tac >> + ONCE_REWRITE_TAC[count_divs_def] >> + rw[] >| [ + mp_tac (Q.SPECL [`2`, `n`] LOG_DIV) >> + `2 <= n` by decide_tac >> + `0 < n DIV 2` by rw[X_LT_DIV] >> + simp[], + `n = 1` by decide_tac >> + simp[LOG_1] + ]); + +val LOG2_compute = store_thm( + "LOG2_compute[compute]", + ``!n. LOG2 n = if 0 < n then count_divs n else LOG2 n``, + rw_tac std_ss[LOG2_by_count_divs]); + +*) + +(* Theorem: m <= n ==> halves m <= halves n *) +(* Proof: + If m = 0, + Then halves m = 0 by halves_0 + Thus halves m <= halves n by 0 <= halves n + If m <> 0, + Then 0 < m and 0 < n by m <= n + so halves m = 1 + LOG2 m by halves_by_LOG2 + and halves n = 1 + LOG2 n by halves_by_LOG2 + and LOG2 m <= LOG2 n by LOG2_LE + ==> halves m <= halves n by arithmetic +*) +val halves_le = store_thm( + "halves_le", + ``!m n. m <= n ==> halves m <= halves n``, + rpt strip_tac >> + Cases_on `m = 0` >- + rw[] >> + `0 < m /\ 0 < n` by decide_tac >> + `LOG2 m <= LOG2 n` by rw[LOG2_LE] >> + rw[halves_by_LOG2]); + +(* Theorem: (halves n = 0) <=> (n = 0) *) +(* Proof: by halves_pos, halves_0 *) +val halves_eq_0 = store_thm( + "halves_eq_0", + ``!n. (halves n = 0) <=> (n = 0)``, + metis_tac[halves_pos, halves_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: (halves n = 1) <=> (n = 1) *) +(* Proof: + If part: halves n = 1 ==> n = 1 + By contradiction, assume n <> 1. + Note n <> 0 by halves_eq_0 + so 2 <= n by n <> 0, n <> 1 + or halves 2 <= halves n by halves_le + But halves 2 = 2 by halves_2 + This gives 2 <= 1, a contradiction. + Only-if part: halves 1 = 1, true by halves_1 +*) +val halves_eq_1 = store_thm( + "halves_eq_1", + ``!n. (halves n = 1) <=> (n = 1)``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `n <> 0` by metis_tac[halves_eq_0, DECIDE``1 <> 0``] >> + `2 <= n` by decide_tac >> + `halves 2 <= halves n` by rw[halves_le] >> + fs[]); + +(* ------------------------------------------------------------------------- *) +(* Perfect Power *) +(* ------------------------------------------------------------------------- *) + +(* Define a PerfectPower number *) +val perfect_power_def = Define` + perfect_power (n:num) (m:num) <=> ?e. (n = m ** e) +`; + +(* Overload perfect_power *) +val _ = overload_on("power_of", ``perfect_power``); +val _ = set_fixity "power_of" (Infix(NONASSOC, 450)); (* same as relation *) +(* from pretty-printing, a good idea. *) + +(* Theorem: perfect_power n n *) +(* Proof: + True since n = n ** 1 by EXP_1 +*) +val perfect_power_self = store_thm( + "perfect_power_self", + ``!n. perfect_power n n``, + metis_tac[perfect_power_def, EXP_1]); + +(* Theorem: perfect_power 0 m <=> (m = 0) *) +(* Proof: by perfect_power_def, EXP_EQ_0 *) +val perfect_power_0_m = store_thm( + "perfect_power_0_m", + ``!m. perfect_power 0 m <=> (m = 0)``, + rw[perfect_power_def, EQ_IMP_THM]); + +(* Theorem: perfect_power 1 m *) +(* Proof: by perfect_power_def, take e = 0 *) +val perfect_power_1_m = store_thm( + "perfect_power_1_m", + ``!m. perfect_power 1 m``, + rw[perfect_power_def] >> + metis_tac[]); + +(* Theorem: perfect_power n 0 <=> ((n = 0) \/ (n = 1)) *) +(* Proof: by perfect_power_def, ZERO_EXP. *) +val perfect_power_n_0 = store_thm( + "perfect_power_n_0", + ``!n. perfect_power n 0 <=> ((n = 0) \/ (n = 1))``, + rw[perfect_power_def] >> + metis_tac[ZERO_EXP]); + +(* Theorem: perfect_power n 1 <=> (n = 1) *) +(* Proof: by perfect_power_def, EXP_1 *) +val perfect_power_n_1 = store_thm( + "perfect_power_n_1", + ``!n. perfect_power n 1 <=> (n = 1)``, + rw[perfect_power_def]); + +(* Theorem: 0 < m /\ 1 < n /\ (n MOD m = 0) ==> + (perfect_power n m) <=> (perfect_power (n DIV m) m) *) +(* Proof: + If part: perfect_power n m ==> perfect_power (n DIV m) m + Note ?e. n = m ** e by perfect_power_def + and e <> 0 by EXP_0, n <> 1 + so ?k. e = SUC k by num_CASES + or n = m ** SUC k + ==> n DIV m = m ** k by EXP_SUC_DIV + Thus perfect_power (n DIV m) m by perfect_power_def + Only-if part: perfect_power (n DIV m) m ==> perfect_power n m + Note ?e. n DIV m = m ** e by perfect_power_def + Now m divides n by DIVIDES_MOD_0, n MOD m = 0, 0 < m + ==> n = m * (n DIV m) by DIVIDES_EQN_COMM, 0 < m + = m * m ** e by above + = m ** (SUC e) by EXP + Thus perfect_power n m by perfect_power_def +*) +val perfect_power_mod_eq_0 = store_thm( + "perfect_power_mod_eq_0", + ``!n m. 0 < m /\ 1 < n /\ (n MOD m = 0) ==> + ((perfect_power n m) <=> (perfect_power (n DIV m) m))``, + rw[perfect_power_def] >> + rw[EQ_IMP_THM] >| [ + `m ** e <> 1` by decide_tac >> + `e <> 0` by metis_tac[EXP_0] >> + `?k. e = SUC k` by metis_tac[num_CASES] >> + qexists_tac `k` >> + rw[EXP_SUC_DIV], + `m divides n` by rw[DIVIDES_MOD_0] >> + `n = m * (n DIV m)` by rw[GSYM DIVIDES_EQN_COMM] >> + metis_tac[EXP] + ]); + +(* Theorem: 0 < m /\ 1 < n /\ (n MOD m <> 0) ==> ~(perfect_power n m) *) +(* Proof: + By contradiction, assume perfect_power n m. + Then ?e. n = m ** e by perfect_power_def + Now e <> 0 by EXP_0, n <> 1 + so ?k. e = SUC k by num_CASES + n = m ** SUC k + = m * (m ** k) by EXP + = (m ** k) * m by MULT_COMM + Thus m divides n by divides_def + ==> n MOD m = 0 by DIVIDES_MOD_0 + This contradicts n MOD m <> 0. +*) +val perfect_power_mod_ne_0 = store_thm( + "perfect_power_mod_ne_0", + ``!n m. 0 < m /\ 1 < n /\ (n MOD m <> 0) ==> ~(perfect_power n m)``, + rpt strip_tac >> + fs[perfect_power_def] >> + `n <> 1` by decide_tac >> + `e <> 0` by metis_tac[EXP_0] >> + `?k. e = SUC k` by metis_tac[num_CASES] >> + `n = m * m ** k` by fs[EXP] >> + `m divides n` by metis_tac[divides_def, MULT_COMM] >> + metis_tac[DIVIDES_MOD_0]); + +(* Theorem: perfect_power n m = + if n = 0 then (m = 0) + else if n = 1 then T + else if m = 0 then (n <= 1) + else if m = 1 then (n = 1) + else if n MOD m = 0 then perfect_power (n DIV m) m else F *) +(* Proof: + If n = 0, to show: + perfect_power 0 m <=> (m = 0), true by perfect_power_0_m + If n = 1, to show: + perfect_power 1 m = T, true by perfect_power_1_m + If m = 0, to show: + perfect_power n 0 <=> (n <= 1), true by perfect_power_n_0 + If m = 1, to show: + perfect_power n 1 <=> (n = 1), true by perfect_power_n_1 + Otherwise, + If n MOD m = 0, to show: + perfect_power (n DIV m) m <=> perfect_power n m, true + by perfect_power_mod_eq_0 + If n MOD m <> 0, to show: + ~perfect_power n m, true by perfect_power_mod_ne_0 +*) +val perfect_power_test = store_thm( + "perfect_power_test", + ``!n m. perfect_power n m = + if n = 0 then (m = 0) + else if n = 1 then T + else if m = 0 then (n <= 1) + else if m = 1 then (n = 1) + else if n MOD m = 0 then perfect_power (n DIV m) m else F``, + rpt strip_tac >> + (Cases_on `n = 0` >> simp[perfect_power_0_m]) >> + (Cases_on `n = 1` >> simp[perfect_power_1_m]) >> + `1 < n` by decide_tac >> + (Cases_on `m = 0` >> simp[perfect_power_n_0]) >> + `0 < m` by decide_tac >> + (Cases_on `m = 1` >> simp[perfect_power_n_1]) >> + (Cases_on `n MOD m = 0` >> simp[]) >- + rw[perfect_power_mod_eq_0] >> + rw[perfect_power_mod_ne_0]); + +(* Theorem: 1 < m /\ perfect_power n m /\ perfect_power (SUC n) m ==> (m = 2) /\ (n = 1) *) +(* Proof: + Note ?x. n = m ** x by perfect_power_def + and ?y. SUC n = m ** y by perfect_power_def + Since n < SUC n by LESS_SUC + ==> x < y by EXP_BASE_LT_MONO + Let d = y - x. + Then 0 < d /\ (y = x + d). + Let v = m ** d + Note 1 < v by ONE_LT_EXP, 1 < m + and m ** y = n * v by EXP_ADD + Let z = v - 1. + Then 0 < z /\ (v = z + 1). + and SUC n = n * v + = n * (z + 1) + = n * z + n * 1 by LEFT_ADD_DISTRIB + = n * z + n + ==> n * z = 1 by ADD1 + ==> n = 1 /\ z = 1 by MULT_EQ_1 + so v = 2 by v = z + 1 + + To show: m = 2. + By contradiction, suppose m <> 2. + Then 2 < m by 1 < m, m <> 2 + ==> 2 ** y < m ** y by EXP_EXP_LT_MONO + = n * v = 2 = 2 ** 1 by EXP_1 + ==> y < 1 by EXP_BASE_LT_MONO + Thus y = 0, but y <> 0 by x < y, + leading to a contradiction. +*) + +Theorem perfect_power_suc: + !m n. 1 < m /\ perfect_power n m /\ perfect_power (SUC n) m ==> + m = 2 /\ n = 1 +Proof + ntac 3 strip_tac >> + `?x. n = m ** x` by fs[perfect_power_def] >> + `?y. SUC n = m ** y` by fs[GSYM perfect_power_def] >> + `n < SUC n` by decide_tac >> + `x < y` by metis_tac[EXP_BASE_LT_MONO] >> + qabbrev_tac `d = y - x` >> + `0 < d /\ (y = x + d)` by fs[Abbr`d`] >> + qabbrev_tac `v = m ** d` >> + `m ** y = n * v` by fs[EXP_ADD, Abbr`v`] >> + `1 < v` by rw[ONE_LT_EXP, Abbr`v`] >> + qabbrev_tac `z = v - 1` >> + `0 < z /\ (v = z + 1)` by fs[Abbr`z`] >> + `n * v = n * z + n * 1` by rw[] >> + `n * z = 1` by decide_tac >> + `n = 1 /\ z = 1` by metis_tac[MULT_EQ_1] >> + `v = 2` by decide_tac >> + simp[] >> + spose_not_then strip_assume_tac >> + `2 < m` by decide_tac >> + `2 ** y < m ** y` by simp[EXP_EXP_LT_MONO] >> + `m ** y = 2` by decide_tac >> + `2 ** y < 2 ** 1` by metis_tac[EXP_1] >> + `y < 1` by fs[EXP_BASE_LT_MONO] >> + decide_tac +QED + +(* Theorem: 1 < m /\ 1 < n /\ perfect_power n m ==> ~perfect_power (SUC n) m *) +(* Proof: + By contradiction, suppose perfect_power (SUC n) m. + Then n = 1 by perfect_power_suc + This contradicts 1 < n. +*) +val perfect_power_not_suc = store_thm( + "perfect_power_not_suc", + ``!m n. 1 < m /\ 1 < n /\ perfect_power n m ==> ~perfect_power (SUC n) m``, + spose_not_then strip_assume_tac >> + `n = 1` by metis_tac[perfect_power_suc] >> + decide_tac); + +(* Theorem: 1 < b /\ 0 < n ==> + (LOG b (SUC n) = LOG b n + if perfect_power (SUC n) b then 1 else 0) *) +(* Proof: + Let x = LOG b n, y = LOG b (SUC n). x <= y + Note SUC n <= b ** SUC x /\ b ** SUC x <= b * n by LOG_TEST + and SUC (SUC n) <= b ** SUC y /\ b ** SUC y <= b * SUC n by LOG_TEST, 0 < SUC n + + If SUC n = b ** SUC x, + Then perfect_power (SUC n) b by perfect_power_def + and y = LOG b (SUC n) + = LOG b (b ** SUC x) + = SUC x by LOG_EXACT_EXP + = x + 1 by ADD1 + hence true. + Otherwise, SUC n < b ** SUC x, + Then SUC (SUC n) <= b ** SUC x by SUC n < b ** SUC x + and b * n < b * SUC n by LT_MULT_LCANCEL, n < SUC n + Thus b ** SUC x <= b * n < b * SUC n + or y = x by LOG_TEST + Claim: ~perfect_power (SUC n) b + Proof: By contradiction, suppose perfect_power (SUC n) b. + Then ?e. SUC n = b ** e. + Thus y = LOG b (SUC n) + = LOG b (b ** e) by LOG_EXACT_EXP + = e + ==> b * n < b * SUC n + = b * b ** e by SUC n = b ** e + = b ** SUC e by EXP + = b ** SUC x by e = y = x + This contradicts b ** SUC x <= b * n + With ~perfect_power (SUC n) b, hence true. +*) + +Theorem LOG_SUC: + !b n. 1 < b /\ 0 < n ==> + (LOG b (SUC n) = LOG b n + if perfect_power (SUC n) b then 1 else 0) +Proof + rpt strip_tac >> + qabbrev_tac ‘x = LOG b n’ >> + qabbrev_tac ‘y = LOG b (SUC n)’ >> + ‘0 < SUC n’ by decide_tac >> + ‘SUC n <= b ** SUC x /\ b ** SUC x <= b * n’ by metis_tac[LOG_TEST] >> + ‘SUC (SUC n) <= b ** SUC y /\ b ** SUC y <= b * SUC n’ + by metis_tac[LOG_TEST] >> + ‘(SUC n = b ** SUC x) \/ (SUC n < b ** SUC x)’ by decide_tac >| [ + ‘perfect_power (SUC n) b’ by metis_tac[perfect_power_def] >> + ‘y = SUC x’ by rw[LOG_EXACT_EXP, Abbr‘y’] >> + simp[], + ‘SUC (SUC n) <= b ** SUC x’ by decide_tac >> + ‘b * n < b * SUC n’ by rw[] >> + ‘b ** SUC x <= b * SUC n’ by decide_tac >> + ‘y = x’ by metis_tac[LOG_TEST] >> + ‘~perfect_power (SUC n) b’ + by (spose_not_then strip_assume_tac >> + `?e. SUC n = b ** e` by fs[perfect_power_def] >> + `y = e` by (simp[Abbr`y`] >> fs[] >> rfs[LOG_EXACT_EXP]) >> + `b * n < b ** SUC x` by metis_tac[EXP] >> + decide_tac) >> + simp[] + ] +QED + +(* +LOG_SUC; +|- !b n. 1 < b /\ 0 < n ==> LOG b (SUC n) = LOG b n + if perfect_power (SUC n) b then 1 else 0 +Let v = LOG b n. + + v v+1. v+2. v+3. + ----------------------------------------------- + b b ** 2 b ** 3 b ** 4 + +> EVAL ``MAP (LOG 2) [1 .. 20]``; +val it = |- MAP (LOG 2) [1 .. 20] = + [0; 1; 1; 2; 2; 2; 2; 3; 3; 3; 3; 3; 3; 3; 3; 4; 4; 4; 4; 4]: thm + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 +*) + +(* Theorem: 0 < n ==> !m. perfect_power n m <=> ?k. k <= LOG2 n /\ (n = m ** k) *) +(* Proof: + If part: perfect_power n m ==> ?k. k <= LOG2 n /\ (n = m ** k) + Given perfect_power n m, ?e. (n = m ** e) by perfect_power_def + If n = 1, + Then LOG2 1 = 0 by LOG2_1 + Take k = 0, then 1 = m ** 0 by EXP_0 + If n <> 1, so e <> 0 by EXP + and m <> 1 by EXP_1 + also n <> 0, so m <> 0 by ZERO_EXP + Therefore 2 <= m + ==> 2 ** e <= m ** e by EXP_BASE_LE_MONO, 1 < 2 + But n < 2 ** (SUC (LOG2 n)) by LOG2_PROPERTY, 0 < n + or 2 ** e < 2 ** (SUC (LOG2 n)) + hence e < SUC (LOG2 n) by EXP_BASE_LT_MONO, 1 < 2 + i.e. e <= LOG2 n + Only-if part: ?k. k <= LOG2 n /\ (n = m ** k) ==> perfect_power n m + True by perfect_power_def. +*) +val perfect_power_bound_LOG2 = store_thm( + "perfect_power_bound_LOG2", + ``!n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= LOG2 n /\ (n = m ** k)``, + rw[EQ_IMP_THM] >| [ + Cases_on `n = 1` >- + simp[] >> + `?e. (n = m ** e)` by rw[GSYM perfect_power_def] >> + `n <> 0 /\ 1 < n /\ 1 < 2` by decide_tac >> + `e <> 0` by metis_tac[EXP] >> + `m <> 1` by metis_tac[EXP_1] >> + `m <> 0` by metis_tac[ZERO_EXP] >> + `2 <= m` by decide_tac >> + `2 ** e <= n` by rw[EXP_BASE_LE_MONO] >> + `n < 2 ** (SUC (LOG2 n))` by rw[LOG2_PROPERTY] >> + `e < SUC (LOG2 n)` by metis_tac[EXP_BASE_LT_MONO, LESS_EQ_LESS_TRANS] >> + `e <= LOG2 n` by decide_tac >> + metis_tac[], + metis_tac[perfect_power_def] + ]); + +(* Theorem: prime p /\ (?x y. 0 < x /\ (p ** x = q ** y)) ==> perfect_power q p *) +(* Proof: + Note ?k. (q = p ** k) by power_eq_prime_power, prime p, 0 < x + Thus perfect_power q p by perfect_power_def +*) +val perfect_power_condition = store_thm( + "perfect_power_condition", + ``!p q. prime p /\ (?x y. 0 < x /\ (p ** x = q ** y)) ==> perfect_power q p``, + metis_tac[power_eq_prime_power, perfect_power_def]); + +(* Theorem: 0 < p /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) *) +(* Proof: + Let q = n DIV p. + Then n = p * q by DIVIDES_EQN_COMM, 0 < p + If part: perfect_power n p ==> perfect_power q p + Note ?k. n = p ** k by perfect_power_def + If k = 0, + Then p * q = p ** 0 = 1 by EXP + ==> p = 1 and q = 1 by MULT_EQ_1 + so perfect_power q p by perfect_power_self + If k <> 0, k = SUC h for some h. + Then p * q = p ** SUC h + = p * p ** h by EXP + or q = p ** h by MULT_LEFT_CANCEL, p <> 0 + so perfect_power q p by perfect_power_self + + Only-if part: perfect_power q p ==> perfect_power n p + Note ?k. q = p ** k by perfect_power_def + so n = p * q = p ** SUC k by EXP + thus perfect_power n p by perfect_power_def +*) +val perfect_power_cofactor = store_thm( + "perfect_power_cofactor", + ``!n p. 0 < p /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p)``, + rpt strip_tac >> + qabbrev_tac `q = n DIV p` >> + `n = p * q` by rw[GSYM DIVIDES_EQN_COMM, Abbr`q`] >> + simp[EQ_IMP_THM] >> + rpt strip_tac >| [ + `?k. p * q = p ** k` by rw[GSYM perfect_power_def] >> + Cases_on `k` >| [ + `(p = 1) /\ (q = 1)` by metis_tac[MULT_EQ_1, EXP] >> + metis_tac[perfect_power_self], + `q = p ** n'` by metis_tac[EXP, MULT_LEFT_CANCEL, NOT_ZERO_LT_ZERO] >> + metis_tac[perfect_power_def] + ], + `?k. q = p ** k` by rw[GSYM perfect_power_def] >> + `p * q = p ** SUC k` by rw[EXP] >> + metis_tac[perfect_power_def] + ]); + +(* Theorem: 0 < n /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p) *) +(* Proof: + Note 0 < p by ZERO_DIVIDES, 0 < n + The result follows by perfect_power_cofactor +*) +val perfect_power_cofactor_alt = store_thm( + "perfect_power_cofactor_alt", + ``!n p. 0 < n /\ p divides n ==> (perfect_power n p <=> perfect_power (n DIV p) p)``, + rpt strip_tac >> + `0 < p` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> + qabbrev_tac `q = n DIV p` >> + rw[perfect_power_cofactor]); + +(* Theorem: perfect_power n 2 ==> (ODD n <=> (n = 1)) *) +(* Proof: + If part: perfect_power n 2 /\ ODD n ==> n = 1 + By contradiction, suppose n <> 1. + Note ?k. n = 2 ** k by perfect_power_def + Thus k <> 0 by EXP + so ?h. k = SUC h by num_CASES + n = 2 ** (SUC h) by above + = 2 * 2 ** h by EXP + ==> EVEN n by EVEN_DOUBLE + This contradicts ODD n by EVEN_ODD + Only-if part: perfect_power n 2 /\ n = 1 ==> ODD n + This is true by ODD_1 +*) +val perfect_power_2_odd = store_thm( + "perfect_power_2_odd", + ``!n. perfect_power n 2 ==> (ODD n <=> (n = 1))``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `?k. n = 2 ** k` by rw[GSYM perfect_power_def] >> + `k <> 0` by metis_tac[EXP] >> + `?h. k = SUC h` by metis_tac[num_CASES] >> + `n = 2 * 2 ** h` by rw[EXP] >> + metis_tac[EVEN_DOUBLE, EVEN_ODD]); + +(* ------------------------------------------------------------------------- *) +(* Power Free *) +(* ------------------------------------------------------------------------- *) + +(* Define a PowerFree number: a trivial perfect power *) +val power_free_def = zDefine` + power_free (n:num) <=> !m e. (n = m ** e) ==> (m = n) /\ (e = 1) +`; +(* Use zDefine as this is not computationally effective. *) + +(* Theorem: power_free 0 = F *) +(* Proof: + Note 0 ** 2 = 0 by ZERO_EXP + Thus power_free 0 = F by power_free_def +*) +val power_free_0 = store_thm( + "power_free_0", + ``power_free 0 = F``, + rw[power_free_def]); + +(* Theorem: power_free 1 = F *) +(* Proof: + Note 0 ** 0 = 1 by ZERO_EXP + Thus power_free 1 = F by power_free_def +*) +val power_free_1 = store_thm( + "power_free_1", + ``power_free 1 = F``, + rw[power_free_def]); + +(* Theorem: power_free n ==> 1 < n *) +(* Proof: + By contradiction, suppose n = 0 or n = 1. + Then power_free 0 = F by power_free_0 + and power_free 1 = F by power_free_1 +*) +val power_free_gt_1 = store_thm( + "power_free_gt_1", + ``!n. power_free n ==> 1 < n``, + metis_tac[power_free_0, power_free_1, DECIDE``1 < n <=> (n <> 0 /\ n <> 1)``]); + +(* Theorem: power_free n <=> 1 < n /\ (!m. perfect_power n m ==> (n = m)) *) +(* Proof: + If part: power_free n ==> 1 < n /\ (!m. perfect_power n m ==> (n = m)) + Note power_free n + ==> 1 < n by power_free_gt_1 + Now ?e. n = m ** e by perfect_power_def + ==> n = m by power_free_def + + Only-if part: 1 < n /\ (!m. perfect_power n m ==> (n = m)) ==> power_free n + By power_free_def, this is to show: + (n = m ** e) ==> (m = n) /\ (e = 1) + Note perfect_power n m by perfect_power_def, ?e. + ==> m = n by implication + so n = n ** e by given, m = n + ==> e = 1 by POWER_EQ_SELF +*) +Theorem power_free_alt: + power_free n <=> 1 < n /\ !m. perfect_power n m ==> n = m +Proof + rw[EQ_IMP_THM] + >- rw[power_free_gt_1] + >- fs[power_free_def, perfect_power_def] >> + fs[power_free_def, perfect_power_def, PULL_EXISTS] >> + rpt strip_tac >> + first_x_assum $ drule_then strip_assume_tac >> gs[] +QED + +(* Theorem: prime n ==> power_free n *) +(* Proof: + Let n = m ** e. To show that n is power_free, + (1) show m = n, by squeezing m as a factor of prime n. + (2) show e = 1, by applying prime_powers_eq + This is a typical detective-style proof. + + Note prime n ==> n <> 1 by NOT_PRIME_1 + + Claim: !m e. n = m ** e ==> m = n + Proof: Note m <> 1 by EXP_1, n <> 1 + and e <> 0 by EXP, n <> 1 + Thus e = SUC k for some k by num_CASES + n = m ** SUC k + = m * (m ** k) by EXP + = (m ** k) * m by MULT_COMM + Thus m divides n, by divides_def + But m <> 1, so m = n by prime_def + + The claim satisfies half of the power_free_def. + With m = n, prime m, + and e <> 0 by EXP, n <> 1 + Thus n = n ** 1 = m ** e by EXP_1 + ==> e = 1 by prime_powers_eq, 0 < e. +*) +val prime_is_power_free = store_thm( + "prime_is_power_free", + ``!n. prime n ==> power_free n``, + rpt strip_tac >> + `n <> 1` by metis_tac[NOT_PRIME_1] >> + `!m e. (n = m ** e) ==> (m = n)` by + (rpt strip_tac >> + `m <> 1` by metis_tac[EXP_1] >> + metis_tac[EXP, num_CASES, MULT_COMM, divides_def, prime_def]) >> + `!m e. (n = m ** e) ==> (e = 1)` by metis_tac[EXP, EXP_1, prime_powers_eq, NOT_ZERO_LT_ZERO] >> + metis_tac[power_free_def]); + +(* Theorem: power_free n /\ perfect_power n m ==> (n = m) *) +(* Proof: + Note ?e. n = m ** e by perfect_power_def + ==> n = m by power_free_def +*) +val power_free_perfect_power = store_thm( + "power_free_perfect_power", + ``!m n. power_free n /\ perfect_power n m ==> (n = m)``, + metis_tac[perfect_power_def, power_free_def]); + +(* Theorem: power_free n ==> (!j. 1 < j ==> (ROOT j n) ** j <> n) *) +(* Proof: + By contradiction, suppose (ROOT j n) ** j = n. + Then j = 1 by power_free_def + This contradicts 1 < j. +*) +val power_free_property = store_thm( + "power_free_property", + ``!n. power_free n ==> (!j. 1 < j ==> (ROOT j n) ** j <> n)``, + spose_not_then strip_assume_tac >> + `j = 1` by metis_tac[power_free_def] >> + decide_tac); + +(* We have: +power_free_0 |- power_free 0 <=> F +power_free_1 |- power_free 1 <=> F +So, given 1 < n, how to check power_free n ? +*) + +(* Theorem: power_free n <=> 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n) *) +(* Proof: + If part: power_free n ==> 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n) + Note 1 < n by power_free_gt_1 + The rest is true by power_free_property. + Only-if part: 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n) ==> power_free n + By contradiction, assume ~(power_free n). + That is, ?m e. n = m ** e /\ (m = m ** e ==> e <> 1) by power_free_def + Note 1 < m /\ 0 < e by ONE_LT_EXP, 1 < n + Thus ROOT e n = m by ROOT_POWER, 1 < m, 0 < e + By the implication, ~(1 < e), or e <= 1. + Since 0 < e, this shows e = 1. + Then m = m ** e by EXP_1 + This gives e <> 1, a contradiction. +*) +val power_free_check_all = store_thm( + "power_free_check_all", + ``!n. power_free n <=> 1 < n /\ (!j. 1 < j ==> (ROOT j n) ** j <> n)``, + rw[EQ_IMP_THM] >- + rw[power_free_gt_1] >- + rw[power_free_property] >> + simp[power_free_def] >> + spose_not_then strip_assume_tac >> + `1 < m /\ 0 < e` by metis_tac[ONE_LT_EXP] >> + `ROOT e n = m` by rw[ROOT_POWER] >> + `~(1 < e)` by metis_tac[] >> + `e = 1` by decide_tac >> + rw[]); + +(* However, there is no need to check all the exponents: + just up to (LOG2 n) or (ulog n) is sufficient. + See earlier part with power_free_upto_def. *) + +(* ------------------------------------------------------------------------- *) +(* Upper Logarithm *) +(* ------------------------------------------------------------------------- *) + +(* Find the power of 2 more or equal to n *) +Definition count_up_def: + count_up n m k = + if m = 0 then 0 (* just to provide m <> 0 for the next one *) + else if n <= m then k else count_up n (2 * m) (SUC k) +Termination WF_REL_TAC `measure (λ(n, m, k). n - m)` +End + +(* Define upper LOG2 n by count_up *) +val ulog_def = Define` + ulog n = count_up n 1 0 +`; + +(* +> EVAL ``ulog 1``; --> 0 +> EVAL ``ulog 2``; --> 1 +> EVAL ``ulog 3``; --> 2 +> EVAL ``ulog 4``; --> 2 +> EVAL ``ulog 5``; --> 3 +> EVAL ``ulog 6``; --> 3 +> EVAL ``ulog 7``; --> 3 +> EVAL ``ulog 8``; --> 3 +> EVAL ``ulog 9``; --> 4 +*) + +(* Theorem: ulog 0 = 0 *) +(* Proof: + ulog 0 + = count_up 0 1 0 by ulog_def + = 0 by count_up_def, 0 <= 1 +*) +val ulog_0 = store_thm( + "ulog_0[simp]", + ``ulog 0 = 0``, + rw[ulog_def, Once count_up_def]); + +(* Theorem: ulog 1 = 0 *) +(* Proof: + ulog 1 + = count_up 1 1 0 by ulog_def + = 0 by count_up_def, 1 <= 1 +*) +val ulog_1 = store_thm( + "ulog_1[simp]", + ``ulog 1 = 0``, + rw[ulog_def, Once count_up_def]); + +(* Theorem: ulog 2 = 1 *) +(* Proof: + ulog 2 + = count_up 2 1 0 by ulog_def + = count_up 2 2 1 by count_up_def, ~(1 < 2) + = 1 by count_up_def, 2 <= 2 +*) +val ulog_2 = store_thm( + "ulog_2[simp]", + ``ulog 2 = 1``, + rw[ulog_def, Once count_up_def] >> + rw[Once count_up_def]); + +(* Theorem: m <> 0 /\ n <= m ==> !k. count_up n m k = k *) +(* Proof: by count_up_def *) +val count_up_exit = store_thm( + "count_up_exit", + ``!m n. m <> 0 /\ n <= m ==> !k. count_up n m k = k``, + rw[Once count_up_def]); + +(* Theorem: m <> 0 /\ m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k) *) +(* Proof: by count_up_def *) +val count_up_suc = store_thm( + "count_up_suc", + ``!m n. m <> 0 /\ m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k)``, + rw[Once count_up_def]); + +(* Theorem: m <> 0 ==> + !t. 2 ** t * m < n ==> !k. count_up n m k = count_up n (2 ** (SUC t) * m) ((SUC k) + t) *) +(* Proof: + By induction on t. + Base: 2 ** 0 * m < n ==> !k. count_up n m k = count_up n (2 ** SUC 0 * m) (SUC k + 0) + Simplifying, this is to show: + m < n ==> !k. count_up n m k = count_up n (2 * m) (SUC k) + which is true by count_up_suc. + Step: 2 ** t * m < n ==> !k. count_up n m k = count_up n (2 ** SUC t * m) (SUC k + t) ==> + 2 ** SUC t * m < n ==> !k. count_up n m k = count_up n (2 ** SUC (SUC t) * m) (SUC k + SUC t) + Note 2 ** SUC t <> 0 by EXP_EQ_0, 2 <> 0 + so 2 ** SUC t * m <> 0 by MULT_EQ_0, m <> 0 + and 2 ** SUC t * m + = 2 * 2 ** t * m by EXP + = 2 * (2 ** t * m) by MULT_ASSOC + Thus (2 ** t * m) < n by MULT_LT_IMP_LT, 0 < 2 + count_up n m k + = count_up n (2 ** SUC t * m) (SUC k + t) by induction hypothesis + = count_up n (2 * (2 ** SUC t * m)) (SUC (SUC k + t)) by count_up_suc + = count_up n (2 ** SUC (SUC t) * m) (SUC k + SUC t) by EXP, ADD1 +*) +val count_up_suc_eqn = store_thm( + "count_up_suc_eqn", + ``!m. m <> 0 ==> + !n t. 2 ** t * m < n ==> !k. count_up n m k = count_up n (2 ** (SUC t) * m) ((SUC k) + t)``, + ntac 3 strip_tac >> + Induct >- + rw[count_up_suc] >> + rpt strip_tac >> + qabbrev_tac `q = 2 ** t * m` >> + `2 ** SUC t <> 0` by metis_tac[EXP_EQ_0, DECIDE``2 <> 0``] >> + `2 ** SUC t * m <> 0` by metis_tac[MULT_EQ_0] >> + `2 ** SUC t * m = 2 * q` by rw_tac std_ss[EXP, MULT_ASSOC, Abbr`q`] >> + `q < n` by rw[MULT_LT_IMP_LT] >> + rw[count_up_suc, EXP, ADD1]); + +(* Theorem: m <> 0 ==> !n t. 2 ** t * m < 2 * n /\ n <= 2 ** t * m ==> !k. count_up n m k = k + t *) +(* Proof: + If t = 0, + Then n <= m by EXP + so count_up n m k + = k by count_up_exit + = k + 0 by ADD_0 + If t <> 0, + Then ?s. t = SUC s by num_CASES + Note 2 ** t * m + = 2 ** SUC s * m by above + = 2 * 2 ** s * m by EXP + = 2 * (2 ** s * m) by MULT_ASSOC + Note 2 ** SUC s * m < 2 * n by given + so (2 ** s * m) < n by LT_MULT_RCANCEL, 2 <> 0 + + count_up n m k + = count_up n (2 ** t * m) ((SUC k) + t) by count_up_suc_eqn + = (SUC k) + t by count_up_exit +*) +val count_up_exit_eqn = store_thm( + "count_up_exit_eqn", + ``!m. m <> 0 ==> !n t. 2 ** t * m < 2 * n /\ n <= 2 ** t * m ==> !k. count_up n m k = k + t``, + rpt strip_tac >> + Cases_on `t` >- + fs[count_up_exit] >> + qabbrev_tac `q = 2 ** n' * m` >> + `2 ** SUC n' * m = 2 * q` by rw_tac std_ss[EXP, MULT_ASSOC, Abbr`q`] >> + `q < n` by decide_tac >> + `count_up n m k = count_up n (2 ** (SUC n') * m) ((SUC k) + n')` by rw[count_up_suc_eqn, Abbr`q`] >> + `_ = (SUC k) + n'` by rw[count_up_exit] >> + rw[]); + +(* Theorem: 2 ** m < 2 * n /\ n <= 2 ** m ==> (ulog n = m) *) +(* Proof: + Put m = 1 in count_up_exit_eqn: + 2 ** t * 1 < 2 * n /\ n <= 2 ** t * 1 ==> !k. count_up n 1 k = k + t + Put k = 0, and apply MULT_RIGHT_1, ADD: + 2 ** t * 1 < 2 * n /\ n <= 2 ** t * 1 ==> count_up n 1 0 = t + Then apply ulog_def to get the result, and rename t by m. +*) +val ulog_unique = store_thm( + "ulog_unique", + ``!m n. 2 ** m < 2 * n /\ n <= 2 ** m ==> (ulog n = m)``, + metis_tac[ulog_def, count_up_exit_eqn, MULT_RIGHT_1, ADD, DECIDE``1 <> 0``]); + +(* Theorem: ulog n = if 1 < n then SUC (LOG2 (n - 1)) else 0 *) +(* Proof: + If 1 < n, + Then 0 < n - 1 by 1 < n + ==> 2 ** LOG2 (n - 1) <= (n - 1) /\ + (n - 1) < 2 ** SUC (LOG2 (n - 1)) by LOG2_PROPERTY + or 2 ** LOG2 (n - 1) < n /\ + n <= 2 ** SUC (LOG2 (n - 1)) by shifting inequalities + Let t = SUC (LOG2 (n - 1)). + Then 2 ** t = 2 * 2 ** (LOG2 (n - 1)) by EXP + < 2 * n by LT_MULT_LCANCEL, 2 ** LOG2 (n - 1) < n + Thus ulog n = t by ulog_unique. + If ~(1 < n), + Then n <= 1, or n = 0 or n = 1. + If n = 0, ulog n = 0 by ulog_0 + If n = 1, ulog n = 0 by ulog_1 +*) +val ulog_eqn = store_thm( + "ulog_eqn", + ``!n. ulog n = if 1 < n then SUC (LOG2 (n - 1)) else 0``, + rw[] >| [ + `0 < n - 1` by decide_tac >> + `2 ** LOG2 (n - 1) <= (n - 1) /\ (n - 1) < 2 ** SUC (LOG2 (n - 1))` by metis_tac[LOG2_PROPERTY] >> + `2 * 2 ** LOG2 (n - 1) < 2 * n /\ n <= 2 ** SUC (LOG2 (n - 1))` by decide_tac >> + rw[EXP, ulog_unique], + metis_tac[ulog_0, ulog_1, DECIDE``~(1 < n) <=> (n = 0) \/ (n = 1)``] + ]); + +(* Theorem: 0 < n ==> (ulog (SUC n) = SUC (LOG2 n)) *) +(* Proof: + Note 0 < n ==> 1 < SUC n by LT_ADD_RCANCEL, ADD1 + Thus ulog (SUC n) + = SUC (LOG2 (SUC n - 1)) by ulog_eqn + = SUC (LOG2 n) by SUC_SUB1 +*) +val ulog_suc = store_thm( + "ulog_suc", + ``!n. 0 < n ==> (ulog (SUC n) = SUC (LOG2 n))``, + rpt strip_tac >> + `1 < SUC n` by decide_tac >> + rw[ulog_eqn]); + +(* Theorem: 0 < n ==> 2 ** (ulog n) < 2 * n /\ n <= 2 ** (ulog n) *) +(* Proof: + Apply ulog_eqn, this is to show: + (1) 1 < n ==> 2 ** SUC (LOG2 (n - 1)) < 2 * n + Let m = n - 1. + Note 0 < m by 1 < n + ==> 2 ** LOG2 m <= m by TWO_EXP_LOG2_LE, 0 < m + or <= n - 1 by notation + Thus 2 ** LOG2 m < n by inequality [1] + and 2 ** SUC (LOG2 m) + = 2 * 2 ** (LOG2 m) by EXP + < 2 * n by LT_MULT_LCANCEL, [1] + (2) 1 < n ==> n <= 2 ** SUC (LOG2 (n - 1)) + Let m = n - 1. + Note 0 < m by 1 < n + ==> m < 2 ** SUC (LOG2 m) by LOG2_PROPERTY, 0 < m + n - 1 < 2 ** SUC (LOG2 m) by notation + n <= 2 ** SUC (LOG2 m) by inequality [2] + or n <= 2 ** SUC (LOG2 (n - 1)) by notation +*) +val ulog_property = store_thm( + "ulog_property", + ``!n. 0 < n ==> 2 ** (ulog n) < 2 * n /\ n <= 2 ** (ulog n)``, + rw[ulog_eqn] >| [ + `0 < n - 1` by decide_tac >> + qabbrev_tac `m = n - 1` >> + `2 ** SUC (LOG2 m) = 2 * 2 ** (LOG2 m)` by rw[EXP] >> + `2 ** LOG2 m <= n - 1` by rw[TWO_EXP_LOG2_LE, Abbr`m`] >> + decide_tac, + `0 < n - 1` by decide_tac >> + qabbrev_tac `m = n - 1` >> + `2 ** SUC (LOG2 m) = 2 * 2 ** (LOG2 m)` by rw[EXP] >> + `n - 1 < 2 ** SUC (LOG2 m)` by metis_tac[LOG2_PROPERTY] >> + decide_tac + ]); + +(* Theorem: 0 < n ==> !m. (ulog n = m) <=> 2 ** m < 2 * n /\ n <= 2 ** m *) +(* Proof: + If part: 0 < n ==> 2 ** (ulog n) < 2 * n /\ n <= 2 ** (ulog n) + True by ulog_property, 0 < n + Only-if part: 2 ** m < 2 * n /\ n <= 2 ** m ==> ulog n = m + True by ulog_unique +*) +val ulog_thm = store_thm( + "ulog_thm", + ``!n. 0 < n ==> !m. (ulog n = m) <=> (2 ** m < 2 * n /\ n <= 2 ** m)``, + metis_tac[ulog_property, ulog_unique]); + +(* Theorem: (ulog 0 = 0) /\ !n. 0 < n ==> !m. (ulog n = m) <=> (n <= 2 ** m /\ 2 ** m < 2 * n) *) +(* Proof: by ulog_0 ulog_thm *) +Theorem ulog_def_alt: + (ulog 0 = 0) /\ + !n. 0 < n ==> !m. (ulog n = m) <=> (n <= 2 ** m /\ 2 ** m < 2 * n) +Proof rw[ulog_0, ulog_thm] +QED + +(* Theorem: (ulog n = 0) <=> ((n = 0) \/ (n = 1)) *) +(* Proof: + Note !n. SUC n <> 0 by NOT_SUC + so if 1 < n, ulog n <> 0 by ulog_eqn + Thus ulog n = 0 <=> ~(1 < n) by above + or <=> n <= 1 by negation + or <=> n = 0 or n = 1 by range +*) +val ulog_eq_0 = store_thm( + "ulog_eq_0", + ``!n. (ulog n = 0) <=> ((n = 0) \/ (n = 1))``, + rw[ulog_eqn]); + +(* Theorem: (ulog n = 1) <=> (n = 2) *) +(* Proof: + If part: ulog n = 1 ==> n = 2 + Note n <> 0 and n <> 1 by ulog_eq_0 + Thus 1 < n, or 0 < n - 1 by arithmetic + ==> SUC (LOG2 (n - 1)) = 1 by ulog_eqn, 1 < n + or LOG2 (n - 1) = 0 by SUC_EQ, ONE + ==> n - 1 < 2 by LOG_EQ_0, 0 < n - 1 + or n <= 2 by inequality + Combine with 1 < n, n = 2. + Only-if part: ulog 2 = 1 + ulog 2 + = ulog (SUC 1) by TWO + = SUC (LOG2 1) by ulog_suc + = SUC 0 by LOG_1, 0 < 2 + = 1 by ONE +*) +val ulog_eq_1 = store_thm( + "ulog_eq_1", + ``!n. (ulog n = 1) <=> (n = 2)``, + rw[EQ_IMP_THM] >> + `n <> 0 /\ n <> 1` by metis_tac[ulog_eq_0, DECIDE``1 <> 0``] >> + `1 < n /\ 0 < n - 1` by decide_tac >> + `SUC (LOG2 (n - 1)) = 1` by metis_tac[ulog_eqn] >> + `LOG2 (n - 1) = 0` by decide_tac >> + `n - 1 < 2` by metis_tac[LOG_EQ_0, DECIDE``1 < 2``] >> + decide_tac); + +(* Theorem: ulog n <= 1 <=> n <= 2 *) +(* Proof: + ulog n <= 1 + <=> ulog n = 0 \/ ulog n = 1 by arithmetic + <=> n = 0 \/ n = 1 \/ n = 2 by ulog_eq_0, ulog_eq_1 + <=> n <= 2 by arithmetic + +*) +val ulog_le_1 = store_thm( + "ulog_le_1", + ``!n. ulog n <= 1 <=> n <= 2``, + rpt strip_tac >> + `ulog n <= 1 <=> ((ulog n = 0) \/ (ulog n = 1))` by decide_tac >> + rw[ulog_eq_0, ulog_eq_1]); + +(* Theorem: n <= m ==> ulog n <= ulog m *) +(* Proof: + If n = 0, + Note ulog 0 = 0 by ulog_0 + and 0 <= ulog m for anything + If n = 1, + Note ulog 1 = 0 by ulog_1 + Thus 0 <= ulog m by arithmetic + If n <> 1, then 1 < n + Note n <= m, so 1 < m + Thus 0 < n - 1 by arithmetic + and n - 1 <= m - 1 by arithmetic + ==> LOG2 (n - 1) <= LOG2 (m - 1) by LOG2_LE + ==> SUC (LOG2 (n - 1)) <= SUC (LOG2 (m - 1)) by LESS_EQ_MONO + or ulog n <= ulog m by ulog_eqn, 1 < n, 1 < m +*) +val ulog_le = store_thm( + "ulog_le", + ``!m n. n <= m ==> ulog n <= ulog m``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + Cases_on `n = 1` >- + rw[] >> + rw[ulog_eqn, LOG2_LE]); + +(* Theorem: n < m ==> ulog n <= ulog m *) +(* Proof: by ulog_le *) +val ulog_lt = store_thm( + "ulog_lt", + ``!m n. n < m ==> ulog n <= ulog m``, + rw[ulog_le]); + +(* Theorem: ulog (2 ** n) = n *) +(* Proof: + Note 0 < 2 ** n by EXP_POS, 0 < 2 + From 1 < 2 by arithmetic + ==> 2 ** n < 2 * 2 ** n by LT_MULT_RCANCEL, 0 < 2 ** n + Now 2 ** n <= 2 ** n by LESS_EQ_REFL + Thus ulog (2 ** n) = n by ulog_unique +*) +val ulog_2_exp = store_thm( + "ulog_2_exp", + ``!n. ulog (2 ** n) = n``, + rpt strip_tac >> + `0 < 2 ** n` by rw[EXP_POS] >> + `2 ** n < 2 * 2 ** n` by decide_tac >> + `2 ** n <= 2 ** n` by decide_tac >> + rw[ulog_unique]); + +(* Theorem: ulog n <= n *) +(* Proof: + Note n < 2 ** n by X_LT_EXP_X + Thus ulog n <= ulog (2 ** n) by ulog_lt + or ulog n <= n by ulog_2_exp +*) +val ulog_le_self = store_thm( + "ulog_le_self", + ``!n. ulog n <= n``, + metis_tac[X_LT_EXP_X, ulog_lt, ulog_2_exp, DECIDE``1 < 2n``]); + +(* Theorem: ulog n = n <=> n = 0 *) +(* Proof: + If part: ulog n = n ==> n = 0 + By contradiction, assume n <> 0 + Then ?k. n = SUC k by num_CASES, n < 0 + so 2 ** SUC k < 2 * SUC k by ulog_property + or 2 * 2 ** k < 2 * SUC k by EXP + ==> 2 ** k < SUC k by arithmetic + or 2 ** k <= k by arithmetic + This contradicts k < 2 ** k by X_LT_EXP_X, 0 < 2 + Only-if part: ulog 0 = 0 + This is true by ulog_0 +*) +val ulog_eq_self = store_thm( + "ulog_eq_self", + ``!n. (ulog n = n) <=> (n = 0)``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `?k. n = SUC k` by metis_tac[num_CASES] >> + `2 * (2 ** k) = 2 ** SUC k` by rw[EXP] >> + `0 < n` by decide_tac >> + `2 ** SUC k < 2 * SUC k` by metis_tac[ulog_property] >> + `2 ** k <= k` by decide_tac >> + `k < 2 ** k` by rw[X_LT_EXP_X] >> + decide_tac); + +(* Theorem: 0 < n ==> ulog n < n *) +(* Proof: + By contradiction, assume ~(ulog n < n). + Then n <= ulog n by ~(ulog n < n) + But ulog n <= n by ulog_le_self + ==> ulog n = n by arithmetic + so n = 0 by ulog_eq_self + This contradicts 0 < n. +*) +val ulog_lt_self = store_thm( + "ulog_lt_self", + ``!n. 0 < n ==> ulog n < n``, + rpt strip_tac >> + spose_not_then strip_assume_tac >> + `ulog n <= n` by rw[ulog_le_self] >> + `ulog n = n` by decide_tac >> + `n = 0` by rw[GSYM ulog_eq_self] >> + decide_tac); + +(* Theorem: (2 ** (ulog n) = n) <=> perfect_power n 2 *) +(* Proof: + Using perfect_power_def, + If part: 2 ** (ulog n) = n ==> ?e. n = 2 ** e + True by taking e = ulog n. + Only-if part: 2 ** ulog (2 ** e) = 2 ** e + This is true by ulog_2_exp +*) +val ulog_exp_exact = store_thm( + "ulog_exp_exact", + ``!n. (2 ** (ulog n) = n) <=> perfect_power n 2``, + rw[perfect_power_def, EQ_IMP_THM] >- + metis_tac[] >> + rw[ulog_2_exp]); + +(* Theorem: ~(perfect_power n 2) ==> 2 ** ulog n <> n *) +(* Proof: by ulog_exp_exact. *) +val ulog_exp_not_exact = store_thm( + "ulog_exp_not_exact", + ``!n. ~(perfect_power n 2) ==> 2 ** ulog n <> n``, + rw[ulog_exp_exact]); + +(* Theorem: 0 < n /\ ~(perfect_power n 2) ==> n < 2 ** ulog n *) +(* Proof: + Note n <= 2 ** ulog n by ulog_property, 0 < n + But n <> 2 ** ulog n by ulog_exp_not_exact, ~(perfect_power n 2) + Thus n < 2 ** ulog n by LESS_OR_EQ +*) +val ulog_property_not_exact = store_thm( + "ulog_property_not_exact", + ``!n. 0 < n /\ ~(perfect_power n 2) ==> n < 2 ** ulog n``, + metis_tac[ulog_property, ulog_exp_not_exact, LESS_OR_EQ]); + +(* Theorem: 1 < n /\ ODD n ==> n < 2 ** ulog n *) +(* Proof: + Note 0 < n /\ n <> 1 by 1 < n + Thus n <= 2 ** ulog n by ulog_property, 0 < n + But ~(perfect_power n 2) by perfect_power_2_odd, n <> 1 + ==> n <> 2 ** ulog n by ulog_exp_not_exact, ~(perfect_power n 2) + Thus n < 2 ** ulog n by LESS_OR_EQ +*) +val ulog_property_odd = store_thm( + "ulog_property_odd", + ``!n. 1 < n /\ ODD n ==> n < 2 ** ulog n``, + rpt strip_tac >> + `0 < n /\ n <> 1` by decide_tac >> + `n <= 2 ** ulog n` by metis_tac[ulog_property] >> + `~(perfect_power n 2)` by metis_tac[perfect_power_2_odd] >> + `2 ** ulog n <> n` by rw[ulog_exp_not_exact] >> + decide_tac); + +(* Theorem: n <= 2 ** m ==> ulog n <= m *) +(* Proof: + n <= 2 ** m + ==> ulog n <= ulog (2 ** m) by ulog_le + ==> ulog n <= m by ulog_2_exp +*) +val exp_to_ulog = store_thm( + "exp_to_ulog", + ``!m n. n <= 2 ** m ==> ulog n <= m``, + metis_tac[ulog_le, ulog_2_exp]); + +(* Theorem: 1 < n ==> 0 < ulog n *) +(* Proof: + Note 1 < n ==> n <> 0 /\ n <> 1 by arithmetic + so ulog n <> 0 by ulog_eq_0 + or 0 < ulog n by NOT_ZERO_LT_ZERO +*) +val ulog_pos = store_thm( + "ulog_pos[simp]", + ``!n. 1 < n ==> 0 < ulog n``, + metis_tac[ulog_eq_0, NOT_ZERO, DECIDE``1 < n <=> n <> 0 /\ n <> 1``]); + +(* Theorem: 1 < n ==> 1 <= ulog n *) +(* Proof: + Note 0 < ulog n by ulog_pos + Thus 1 <= ulog n by arithmetic +*) +val ulog_ge_1 = store_thm( + "ulog_ge_1", + ``!n. 1 < n ==> 1 <= ulog n``, + metis_tac[ulog_pos, DECIDE``0 < n ==> 1 <= n``]); + +(* Theorem: 2 < n ==> 1 < (ulog n) ** 2 *) +(* Proof: + Note 1 < n /\ n <> 2 by 2 < n + so 0 < ulog n by ulog_pos, 1 < n + and ulog n <> 1 by ulog_eq_1, n <> 2 + Thus 1 < ulog n by ulog n <> 0, ulog n <> 1 + so 1 < (ulog n) ** 2 by ONE_LT_EXP, 0 < 2 +*) +val ulog_sq_gt_1 = store_thm( + "ulog_sq_gt_1", + ``!n. 2 < n ==> 1 < (ulog n) ** 2``, + rpt strip_tac >> + `1 < n /\ n <> 2` by decide_tac >> + `0 < ulog n` by rw[] >> + `ulog n <> 1` by rw[ulog_eq_1] >> + `1 < ulog n` by decide_tac >> + rw[ONE_LT_EXP]); + +(* Theorem: 1 < n ==> 4 <= (2 * ulog n) ** 2 *) +(* Proof: + Note 0 < ulog n by ulog_pos, 1 < n + Thus 2 <= 2 * ulog n by arithmetic + or 4 <= (2 * ulog n) ** 2 by EXP_BASE_LE_MONO +*) +val ulog_twice_sq = store_thm( + "ulog_twice_sq", + ``!n. 1 < n ==> 4 <= (2 * ulog n) ** 2``, + rpt strip_tac >> + `0 < ulog n` by rw[ulog_pos] >> + `2 <= 2 * ulog n` by decide_tac >> + `2 ** 2 <= (2 * ulog n) ** 2` by rw[EXP_BASE_LE_MONO] >> + `2 ** 2 = 4` by rw[] >> + decide_tac); + +(* Theorem: ulog n = if n = 0 then 0 + else if (perfect_power n 2) then (LOG2 n) else SUC (LOG2 n) *) +(* Proof: + This is to show: + (1) ulog 0 = 0, true by ulog_0 + (2) perfect_power n 2 ==> ulog n = LOG2 n + Note ?k. n = 2 ** k by perfect_power_def + Thus ulog n = k by ulog_exp_exact + and LOG2 n = k by LOG_EXACT_EXP, 1 < 2 + (3) ~(perfect_power n 2) ==> ulog n = SUC (LOG2 n) + Let m = SUC (LOG2 n). + Then 2 ** m + = 2 * 2 ** (LOG2 n) by EXP + <= 2 * n by TWO_EXP_LOG2_LE + But n <> LOG2 n by LOG2_EXACT_EXP, ~(perfect_power n 2) + Thus 2 ** m < 2 * n [1] + + Also n < 2 ** m by LOG2_PROPERTY + Thus n <= 2 ** m, [2] + giving ulog n = m by ulog_unique, [1] [2] +*) +val ulog_alt = store_thm( + "ulog_alt", + ``!n. ulog n = if n = 0 then 0 + else if (perfect_power n 2) then (LOG2 n) else SUC (LOG2 n)``, + rw[] >- + metis_tac[perfect_power_def, ulog_exp_exact, LOG_EXACT_EXP, DECIDE``1 < 2``] >> + qabbrev_tac `m = SUC (LOG2 n)` >> + (irule ulog_unique >> rpt conj_tac) >| [ + `2 ** m = 2 * 2 ** (LOG2 n)` by rw[EXP, Abbr`m`] >> + `2 ** (LOG2 n) <= n` by rw[TWO_EXP_LOG2_LE] >> + `2 ** (LOG2 n) <> n` by rw[LOG2_EXACT_EXP, GSYM perfect_power_def] >> + decide_tac, + `n < 2 ** m` by rw[LOG2_PROPERTY, Abbr`m`] >> + decide_tac + ]); + +(* +Thus, for 0 < n, (ulog n) and SUC (LOG2 n) differ only for (perfect_power n 2). +This means that replacing AKS bounds of SUC (LOG2 n) by (ulog n) +only affect calculations involving (perfect_power n 2), +which are irrelevant for primality testing ! +However, in display, (ulog n) is better, while SUC (LOG2 n) is a bit ugly. +*) + +(* Theorem: 0 < n ==> (LOG2 n <= ulog n /\ ulog n <= 1 + LOG2 n) *) +(* Proof: by ulog_alt *) +val ulog_LOG2 = store_thm( + "ulog_LOG2", + ``!n. 0 < n ==> (LOG2 n <= ulog n /\ ulog n <= 1 + LOG2 n)``, + rw[ulog_alt]); + +(* Theorem: 0 < n ==> !m. perfect_power n m <=> ?k. k <= ulog n /\ (n = m ** k) *) +(* Proof: by perfect_power_bound_LOG2, ulog_LOG2 *) +val perfect_power_bound_ulog = store_thm( + "perfect_power_bound_ulog", + ``!n. 0 < n ==> !m. perfect_power n m <=> ?k. k <= ulog n /\ (n = m ** k)``, + rw[EQ_IMP_THM] >| [ + `LOG2 n <= ulog n` by rw[ulog_LOG2] >> + metis_tac[perfect_power_bound_LOG2, LESS_EQ_TRANS], + metis_tac[perfect_power_def] + ]); + +(* ------------------------------------------------------------------------- *) +(* Upper Log Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: ulog (m * n) <= ulog m + ulog n *) +(* Proof: + Let x = ulog (m * n), y = ulog m + ulog n. + Note m * n <= 2 ** x < 2 * m * n by ulog_thm + and m <= 2 ** ulog m < 2 * m by ulog_thm + and n <= 2 ** ulog n < 2 * n by ulog_thm + Note that 2 ** ulog m * 2 ** ulog n = 2 ** y by EXP_ADD + Multiplying inequalities, + m * n <= 2 ** y by LE_MONO_MULT2 + 2 ** y < 4 * m * n by LT_MONO_MULT2 + The picture is: + m * n ....... 2 * m * n ....... 4 * m * n + 2 ** x somewhere + 2 ** y somewhere + If 2 ** y < 2 * m * n, + Then x = y by ulog_unique + Otherwise, + 2 ** y is in the second range. + Then 2 ** x < 2 ** y since 2 ** x in the first + or x < y by EXP_BASE_LT_MONO + Combining these two cases: x <= y. +*) +val ulog_mult = store_thm( + "ulog_mult", + ``!m n. ulog (m * n) <= ulog m + ulog n``, + rpt strip_tac >> + Cases_on `(m = 0) \/ (n = 0)` >- + fs[] >> + `m * n <> 0` by rw[] >> + `0 < m /\ 0 < n /\ 0 < m * n` by decide_tac >> + qabbrev_tac `x = ulog (m * n)` >> + qabbrev_tac `y = ulog m + ulog n` >> + `m * n <= 2 ** x /\ 2 ** x < TWICE (m * n)` by metis_tac[ulog_thm] >> + `m * n <= 2 ** y /\ 2 ** y < (TWICE m) * (TWICE n)` by metis_tac[ulog_thm, LE_MONO_MULT2, LT_MONO_MULT2, EXP_ADD] >> + Cases_on `2 ** y < TWICE (m * n)` >| [ + `y = x` by metis_tac[ulog_unique] >> + decide_tac, + `2 ** x < 2 ** y /\ 1 < 2` by decide_tac >> + `x < y` by metis_tac[EXP_BASE_LT_MONO] >> + decide_tac + ]); + +(* Theorem: ulog (m ** n) <= n * ulog m *) +(* Proof: + By induction on n. + Base: ulog (m ** 0) <= 0 * ulog m + LHS = ulog (m ** 0) + = ulog 1 by EXP_0 + = 0 by ulog_1 + <= 0 * ulog m by MULT + = RHS + Step: ulog (m ** n) <= n * ulog m ==> ulog (m ** SUC n) <= SUC n * ulog m + LHS = ulog (m ** SUC n) + = ulog (m * m ** n) by EXP + <= ulog m + ulog (m ** n) by ulog_mult + <= ulog m + n * ulog m by induction hypothesis + = (1 + n) * ulog m by RIGHT_ADD_DISTRIB + = SUC n * ulog m by ADD1, ADD_COMM + = RHS +*) +val ulog_exp = store_thm( + "ulog_exp", + ``!m n. ulog (m ** n) <= n * ulog m``, + rpt strip_tac >> + Induct_on `n` >> + rw[EXP_0] >> + `ulog (m ** SUC n) <= ulog m + ulog (m ** n)` by rw[EXP, ulog_mult] >> + `ulog m + ulog (m ** n) <= ulog m + n * ulog m` by rw[] >> + `ulog m + n * ulog m = SUC n * ulog m` by rw[ADD1] >> + decide_tac); + +(* Theorem: 0 < n /\ EVEN n ==> (ulog n = 1 + ulog (HALF n)) *) +(* Proof: + Let k = HALF n. + Then 0 < k by HALF_EQ_0, EVEN n + and EVEN n ==> n = TWICE k by EVEN_HALF + Note n <= 2 ** ulog n < 2 * n by ulog_thm, by 0 < n + and k <= 2 ** ulog k < 2 * k by ulog_thm, by 0 < k + so 2 * k <= 2 * 2 ** ulog k < 2 * 2 * k by multiplying 2 + or n <= 2 ** (1 + ulog k) < 2 * n by EXP + Thus ulog n = 1 + ulog k by ulog_unique +*) +val ulog_even = store_thm( + "ulog_even", + ``!n. 0 < n /\ EVEN n ==> (ulog n = 1 + ulog (HALF n))``, + rpt strip_tac >> + qabbrev_tac `k = HALF n` >> + `n = TWICE k` by rw[EVEN_HALF, Abbr`k`] >> + `0 < k` by rw[Abbr`k`] >> + `n <= 2 ** ulog n /\ 2 ** ulog n < 2 * n` by metis_tac[ulog_thm] >> + `k <= 2 ** ulog k /\ 2 ** ulog k < 2 * k` by metis_tac[ulog_thm] >> + `2 <> 0` by decide_tac >> + `n <= 2 * 2 ** ulog k` by rw[LE_MULT_LCANCEL] >> + `2 * 2 ** ulog k < 2 * n` by rw[LT_MULT_LCANCEL] >> + `2 * 2 ** ulog k = 2 ** (1 + ulog k)` by metis_tac[EXP, ADD1, ADD_COMM] >> + metis_tac[ulog_unique]); + +(* Theorem: 1 < n /\ ODD n ==> ulog (HALF n) + 1 <= ulog n *) +(* Proof: + Let k = HALF n. + Then 0 < k by HALF_EQ_0, 1 < n + and ODD n ==> n = TWICE k + 1 by ODD_HALF + Note n <= 2 ** ulog n < 2 * n by ulog_thm, by 0 < n + and k <= 2 ** ulog k < 2 * k by ulog_thm, by 0 < k + so 2 * k <= 2 * 2 ** ulog k < 2 * 2 * k by multiplying 2 + or (2 * k) <= 2 ** (1 + ulog k) < 2 * (2 * k) by EXP + Since 2 * k < n, so 2 * (2 * k) < 2 * n, + the picture is: + 2 * k ... n ...... 2 * (2 * k) ... 2 * n + <--- 2 ** ulog n ----> + <--- 2 ** (1 + ulog k) --> + If n <= 2 ** (1 + ulog k), then ulog n = 1 + ulog k by ulog_unique + Otherwise, 2 ** (1 + ulog k) < 2 ** ulog n + so 1 + ulog k < ulog n by EXP_BASE_LT_MONO, 1 < 2 + Combining, 1 + ulog k <= ulog n. +*) +val ulog_odd = store_thm( + "ulog_odd", + ``!n. 1 < n /\ ODD n ==> ulog (HALF n) + 1 <= ulog n``, + rpt strip_tac >> + qabbrev_tac `k = HALF n` >> + `(n <> 0) /\ (n <> 1)` by decide_tac >> + `0 < n /\ 0 < k` by metis_tac[HALF_EQ_0, NOT_ZERO_LT_ZERO] >> + `n = TWICE k + 1` by rw[ODD_HALF, Abbr`k`] >> + `n <= 2 ** ulog n /\ 2 ** ulog n < 2 * n` by metis_tac[ulog_thm] >> + `k <= 2 ** ulog k /\ 2 ** ulog k < 2 * k` by metis_tac[ulog_thm] >> + `2 <> 0 /\ 1 < 2` by decide_tac >> + `2 * k <= 2 * 2 ** ulog k` by rw[LE_MULT_LCANCEL] >> + `2 * 2 ** ulog k < 2 * (2 * k)` by rw[LT_MULT_LCANCEL] >> + `2 * 2 ** ulog k = 2 ** (1 + ulog k)` by metis_tac[EXP, ADD1, ADD_COMM] >> + Cases_on `n <= 2 ** (1 + ulog k)` >| [ + `2 * k < n` by decide_tac >> + `2 * (2 * k) < 2 * n` by rw[LT_MULT_LCANCEL] >> + `2 ** (1 + ulog k) < TWICE n` by decide_tac >> + `1 + ulog k = ulog n` by metis_tac[ulog_unique] >> + decide_tac, + `2 ** (1 + ulog k) < 2 ** ulog n` by decide_tac >> + `1 + ulog k < ulog n` by metis_tac[EXP_BASE_LT_MONO] >> + decide_tac + ]); + +(* +EVAL ``let n = 13 in [ulog (HALF n) + 1; ulog n]``; +|- (let n = 13 in [ulog (HALF n) + 1; ulog n]) = [4; 4]: +|- (let n = 17 in [ulog (HALF n) + 1; ulog n]) = [4; 5]: +*) + +(* Theorem: 1 < n ==> ulog (HALF n) + 1 <= ulog n *) +(* Proof: + Note 1 < n ==> 0 < n by arithmetic + If EVEN n, true by ulog_even, 0 < n + If ODD n, true by ulog_odd, 1 < n, ODD_EVEN. +*) +val ulog_half = store_thm( + "ulog_half", + ``!n. 1 < n ==> ulog (HALF n) + 1 <= ulog n``, + rpt strip_tac >> + Cases_on `EVEN n` >- + rw[ulog_even] >> + rw[ODD_EVEN, ulog_odd]); + +(* Theorem: SQRT n <= 2 ** (ulog n) *) +(* Proof: + Note n <= 2 ** ulog n by ulog_property + and SQRT n <= n by SQRT_LE_SELF + Thus SQRT n <= 2 ** ulog n by LESS_EQ_TRANS + or SQRT n <= +*) +val sqrt_upper = store_thm( + "sqrt_upper", + ``!n. SQRT n <= 2 ** (ulog n)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + `n <= 2 ** ulog n` by rw[ulog_property] >> + `SQRT n <= n` by rw[SQRT_LE_SELF] >> + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Power Free up to a limit *) +(* ------------------------------------------------------------------------- *) + +(* Define a power free property of a number *) +val power_free_upto_def = Define` + power_free_upto n k <=> !j. 1 < j /\ j <= k ==> (ROOT j n) ** j <> n +`; +(* make this an infix relation. *) +val _ = set_fixity "power_free_upto" (Infix(NONASSOC, 450)); (* same as relation *) + +(* Theorem: (n power_free_upto 0) = T *) +(* Proof: by power_free_upto_def, no counter-example. *) +val power_free_upto_0 = store_thm( + "power_free_upto_0", + ``!n. (n power_free_upto 0) = T``, + rw[power_free_upto_def]); + +(* Theorem: (n power_free_upto 1) = T *) +(* Proof: by power_free_upto_def, no counter-example. *) +val power_free_upto_1 = store_thm( + "power_free_upto_1", + ``!n. (n power_free_upto 1) = T``, + rw[power_free_upto_def]); + +(* Theorem: 0 < k /\ (n power_free_upto k) ==> + ((n power_free_upto (k + 1)) <=> ROOT (k + 1) n ** (k + 1) <> n) *) +(* Proof: by power_free_upto_def *) +val power_free_upto_suc = store_thm( + "power_free_upto_suc", + ``!n k. 0 < k /\ (n power_free_upto k) ==> + ((n power_free_upto (k + 1)) <=> ROOT (k + 1) n ** (k + 1) <> n)``, + rw[power_free_upto_def] >> + rw[EQ_IMP_THM] >> + metis_tac[LESS_OR_EQ, DECIDE``k < n + 1 ==> k <= n``]); + +(* Theorem: LOG2 n <= b ==> (power_free n <=> (1 < n /\ n power_free_upto b)) *) +(* Proof: + If part: LOG2 n <= b /\ power_free n ==> 1 < n /\ n power_free_upto b + (1) 1 < n, + By contradiction, suppose n <= 1. + Then n = 0, but power_free 0 = F by power_free_0 + or n = 1, but power_free 1 = F by power_free_1 + (2) n power_free_upto b, + By power_free_upto_def, this is to show: + 1 < j /\ j <= b ==> ROOT j n ** j <> n + By contradiction, suppose ROOT j n ** j = n. + Then n = m ** j where m = ROOT j n, with j <> 1. + This contradicts power_free n by power_free_def + + Only-if part: 1 < n /\ LOG2 n <= b /\ n power_free_upto b ==> power_free n + By contradiction, suppose ~(power_free n). + Then ?e. n = m ** e with n = m ==> e <> 1 by power_free_def + ==> perfect_power n m by perfect_power_def + Thus ?k. k <= LOG2 n /\ (n = m ** k) by perfect_power_bound_LOG2, 0 < n + Now k <> 0 by EXP_0, n <> 1 + so m = ROOT k n by ROOT_FROM_POWER, k <> 0 + + Claim: k <> 1 + Proof: Note m <> 0 by ROOT_EQ_0, n <> 0 + and m <> 1 by EXP_1, k <> 0, n <> 1 + ==> 1 < m by m <> 0, m <> 1 + Thus n = m ** e = m ** k ==> k = e by EXP_BASE_INJECTIVE + But e <> 1 + since e = 1 ==> n <> m, by n = m ==> e <> 1 + yet n = m ** 1 ==> n = m by EXP_1 + Since k = e, k <> 1. + + Therefore 1 < k by k <> 0, k <> 1 + and k <= LOG2 n /\ LOG2 n <= b ==> k <= b by arithmetic + With 1 < k /\ k <= b /\ m = ROOT k n /\ m ** k = n, + These will give a contradiction by power_free_upto_def +*) +val power_free_check_upto = store_thm( + "power_free_check_upto", + ``!n b. LOG2 n <= b ==> (power_free n <=> (1 < n /\ n power_free_upto b))``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `(n = 0) \/ (n = 1)` by decide_tac >- + fs[power_free_0] >> + fs[power_free_1], + rw[power_free_upto_def] >> + spose_not_then strip_assume_tac >> + `j <> 1` by decide_tac >> + metis_tac[power_free_def], + simp[power_free_def] >> + spose_not_then strip_assume_tac >> + `perfect_power n m` by metis_tac[perfect_power_def] >> + `0 < n /\ n <> 1` by decide_tac >> + `?k. k <= LOG2 n /\ (n = m ** k)` by rw[GSYM perfect_power_bound_LOG2] >> + `k <> 0` by metis_tac[EXP_0] >> + `m = ROOT k n` by rw[ROOT_FROM_POWER] >> + `k <> 1` by + (`m <> 0` by rw[ROOT_EQ_0] >> + `m <> 1 /\ e <> 1` by metis_tac[EXP_1] >> + `1 < m` by decide_tac >> + metis_tac[EXP_BASE_INJECTIVE]) >> + `1 < k` by decide_tac >> + `k <= b` by decide_tac >> + metis_tac[power_free_upto_def] + ]); + +(* Theorem: power_free n <=> (1 < n /\ n power_free_upto LOG2 n) *) +(* Proof: by power_free_check_upto, LOG2 n <= LOG2 n *) +val power_free_check_upto_LOG2 = store_thm( + "power_free_check_upto_LOG2", + ``!n. power_free n <=> (1 < n /\ n power_free_upto LOG2 n)``, + rw[power_free_check_upto]); + +(* Theorem: power_free n <=> (1 < n /\ n power_free_upto ulog n) *) +(* Proof: + If n = 0, + LHS = power_free 0 = F by power_free_0 + = RHS, as 1 < 0 = F + If n <> 0, + Then LOG2 n <= ulog n by ulog_LOG2, 0 < n + The result follows by power_free_check_upto +*) +val power_free_check_upto_ulog = store_thm( + "power_free_check_upto_ulog", + ``!n. power_free n <=> (1 < n /\ n power_free_upto ulog n)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[power_free_0] >> + rw[power_free_check_upto, ulog_LOG2]); + +(* Theorem: power_free 2 *) +(* Proof: + power_free 2 + <=> 2 power_free_upto (LOG2 2) by power_free_check_upto_LOG2 + <=> 2 power_free_upto 1 by LOG2_2 + <=> T by power_free_upto_1 +*) +val power_free_2 = store_thm( + "power_free_2", + ``power_free 2``, + rw[power_free_check_upto_LOG2, power_free_upto_1]); + +(* Theorem: power_free 3 *) +(* Proof: + Note 3 power_free_upto 1 by power_free_upto_1 + power_free 3 + <=> 3 power_free_upto (ulog 3) by power_free_check_upto_ulog + <=> 3 power_free_upto 2 by evaluation + <=> ROOT 2 3 ** 2 <> 3 by power_free_upto_suc, 0 < 1 + <=> T by evaluation +*) +val power_free_3 = store_thm( + "power_free_3", + ``power_free 3``, + `3 power_free_upto 1` by rw[power_free_upto_1] >> + `ulog 3 = 2` by EVAL_TAC >> + `ROOT 2 3 ** 2 <> 3` by EVAL_TAC >> + `power_free 3 <=> 3 power_free_upto 2` by rw[power_free_check_upto_ulog] >> + metis_tac[power_free_upto_suc, DECIDE``0 < 1 /\ (1 + 1 = 2)``]); + +(* Define a power free test, based on (ulog n), for computation. *) +val power_free_test_def = Define` + power_free_test n <=>(1 < n /\ n power_free_upto (ulog n)) +`; + +(* Theorem: power_free_test n = power_free n *) +(* Proof: by power_free_test_def, power_free_check_upto_ulog *) +val power_free_test_eqn = store_thm( + "power_free_test_eqn", + ``!n. power_free_test n = power_free n``, + rw[power_free_test_def, power_free_check_upto_ulog]); + +(* Theorem: power_free n <=> + (1 < n /\ !j. 1 < j /\ j <= (LOG2 n) ==> ROOT j n ** j <> n) *) +(* Proof: by power_free_check_upto_ulog, power_free_upto_def *) +val power_free_test_upto_LOG2 = store_thm( + "power_free_test_upto_LOG2", + ``!n. power_free n <=> + (1 < n /\ !j. 1 < j /\ j <= (LOG2 n) ==> ROOT j n ** j <> n)``, + rw[power_free_check_upto_LOG2, power_free_upto_def]); + +(* Theorem: power_free n <=> + (1 < n /\ !j. 1 < j /\ j <= (ulog n) ==> ROOT j n ** j <> n) *) +(* Proof: by power_free_check_upto_ulog, power_free_upto_def *) +val power_free_test_upto_ulog = store_thm( + "power_free_test_upto_ulog", + ``!n. power_free n <=> + (1 < n /\ !j. 1 < j /\ j <= (ulog n) ==> ROOT j n ** j <> n)``, + rw[power_free_check_upto_ulog, power_free_upto_def]); + +(* ------------------------------------------------------------------------- *) +(* Another Characterisation of Power Free *) +(* ------------------------------------------------------------------------- *) + +(* Define power index of n, the highest index of n in power form by descending from k *) +val power_index_def = Define ` + power_index n k <=> + if k <= 1 then 1 + else if (ROOT k n) ** k = n then k + else power_index n (k - 1) +`; + +(* Theorem: power_index n 0 = 1 *) +(* Proof: by power_index_def *) +val power_index_0 = store_thm( + "power_index_0", + ``!n. power_index n 0 = 1``, + rw[Once power_index_def]); + +(* Theorem: power_index n 1 = 1 *) +(* Proof: by power_index_def *) +val power_index_1 = store_thm( + "power_index_1", + ``!n. power_index n 1 = 1``, + rw[Once power_index_def]); + +(* Theorem: (ROOT (power_index n k) n) ** (power_index n k) = n *) +(* Proof: + By induction on k. + Base: ROOT (power_index n 0) n ** power_index n 0 = n + ROOT (power_index n 0) n ** power_index n 0 + = (ROOT 1 n) ** 1 by power_index_0 + = n ** 1 by ROOT_1 + = n by EXP_1 + Step: ROOT (power_index n k) n ** power_index n k = n ==> + ROOT (power_index n (SUC k)) n ** power_index n (SUC k) = n + If k = 0, + ROOT (power_index n (SUC 0)) n ** power_index n (SUC 0) + = ROOT (power_index n 1) n ** power_index n 1 by ONE + = (ROOT 1 n) ** 1 by power_index_1 + = n ** 1 by ROOT_1 + = n by EXP_1 + If k <> 0, + Then ~(SUC k <= 1) by 0 < k + If ROOT (SUC k) n ** SUC k = n, + Then power_index n (SUC k) = SUC k by power_index_def + so ROOT (power_index n (SUC k)) n ** power_index n (SUC k) + = ROOT (SUC k) n ** SUC k by above + = n by condition + If ROOT (SUC k) n ** SUC k <> n, + Then power_index n (SUC k) = power_index n k by power_index_def + so ROOT (power_index n (SUC k)) n ** power_index n (SUC k) + = ROOT (power_index n k) n ** power_index n k by above + = n by induction hypothesis +*) +val power_index_eqn = store_thm( + "power_index_eqn", + ``!n k. (ROOT (power_index n k) n) ** (power_index n k) = n``, + rpt strip_tac >> + Induct_on `k` >- + rw[power_index_0] >> + Cases_on `k = 0` >- + rw[power_index_1] >> + `~(SUC k <= 1)` by decide_tac >> + rw_tac std_ss[Once power_index_def] >- + rw[Once power_index_def] >> + `power_index n (SUC k) = power_index n k` by rw[Once power_index_def] >> + rw[]); + +(* Theorem: perfect_power n (ROOT (power_index n k) n) *) +(* Proof: + Let m = ROOT (power_index n k) n. + By perfect_power_def, this is to show: + ?e. n = m ** e + Take e = power_index n k. + m ** e + = (ROOT (power_index n k) n) ** (power_index n k) by root_compute_eqn + = n by power_index_eqn +*) +val power_index_root = store_thm( + "power_index_root", + ``!n k. perfect_power n (ROOT (power_index n k) n)``, + metis_tac[perfect_power_def, power_index_eqn]); + +(* Theorem: power_index 1 k = if k = 0 then 1 else k *) +(* Proof: + If k = 0, + power_index 1 0 = 1 by power_index_0 + If k <> 0, then 0 < k. + If k = 1, + Then power_index 1 1 = 1 = k by power_index_1 + If k <> 1, 1 < k. + Note ROOT k 1 = 1 by ROOT_OF_1, 0 < k. + so power_index 1 k = k by power_index_def +*) +val power_index_of_1 = store_thm( + "power_index_of_1", + ``!k. power_index 1 k = if k = 0 then 1 else k``, + rw[Once power_index_def]); + +(* Theorem: 0 < k /\ ((ROOT k n) ** k = n) ==> (power_index n k = k) *) +(* Proof: + If k = 1, + True since power_index n 1 = 1 by power_index_1 + If k <> 1, then 1 < k by 0 < k + True by power_index_def +*) +val power_index_exact_root = store_thm( + "power_index_exact_root", + ``!n k. 0 < k /\ ((ROOT k n) ** k = n) ==> (power_index n k = k)``, + rpt strip_tac >> + Cases_on `k = 1` >- + rw[power_index_1] >> + `1 < k` by decide_tac >> + rw[Once power_index_def]); + +(* Theorem: (ROOT k n) ** k <> n ==> (power_index n k = power_index n (k - 1)) *) +(* Proof: + If k = 0, + Then k = k - 1 by k = 0 + Thus true trivially. + If k = 1, + Note power_index n 1 = 1 by power_index_1 + and power_index n 0 = 1 by power_index_0 + Thus true. + If k <> 0 /\ k <> 1, then 1 < k by arithmetic + True by power_index_def +*) +val power_index_not_exact_root = store_thm( + "power_index_not_exact_root", + ``!n k. (ROOT k n) ** k <> n ==> (power_index n k = power_index n (k - 1))``, + rpt strip_tac >> + Cases_on `k = 0` >| [ + `k = k - 1` by decide_tac >> + rw[], + Cases_on `k = 1` >- + rw[power_index_0, power_index_1] >> + `1 < k` by decide_tac >> + rw[Once power_index_def] + ]); + +(* Theorem: k <= m /\ (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n) ==> (power_index n m = power_index n k) *) +(* Proof: + By induction on (m - k). + Base: k <= m /\ 0 = m - k ==> power_index n m = power_index n k + Note m <= k by 0 = m - k + so m = k by k <= m + Thus true trivially. + Step: !m'. v = m' - k /\ k <= m' /\ ... ==> power_index n m' = power_index n k ==> + SUC v = m - k ==> power_index n m = power_index n k + If m = k, true trivially. + If m <> k, then k < m. + Thus k <= (m - 1), and v = (m - 1) - k. + Note ROOT m n ** m <> n by j = m in implication + Thus power_index n m + = power_index n (m - 1) by power_index_not_exact_root + = power_index n k by induction hypothesis, m' = m - 1. +*) +val power_index_no_exact_roots = store_thm( + "power_index_no_exact_roots", + ``!m n k. k <= m /\ (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n) ==> (power_index n m = power_index n k)``, + rpt strip_tac >> + Induct_on `m - k` >| [ + rpt strip_tac >> + `m = k` by decide_tac >> + rw[], + rpt strip_tac >> + Cases_on `m = k` >- + rw[] >> + `ROOT m n ** m <> n` by rw[] >> + `k <= m - 1` by decide_tac >> + `power_index n (m - 1) = power_index n k` by rw[] >> + rw[power_index_not_exact_root] + ]); + +(* The theorem power_index_equal requires a detective-style proof, based on these lemma. *) + +(* Theorem: k <= m /\ ((ROOT k n) ** k = n) ==> k <= power_index n m *) +(* Proof: + If k = 0, + Then n = 1 by EXP + If m = 0, + Then power_index 1 0 = 1 by power_index_of_1 + But k <= 0, so k = 0 by arithmetic + Hence k <= power_index n m + If m <> 0, + Then power_index 1 m = m by power_index_of_1 + Hence k <= power_index 1 m = m by given + + If k <> 0, then 0 < k. + Let s = {j | j <= m /\ ((ROOT j n) ** j = n)} + Then s SUBSET (count (SUC m)) by SUBSET_DEF + ==> FINITE s by SUBSET_FINITE, FINITE_COUNT + Note k IN s by given + ==> s <> {} by MEMBER_NOT_EMPTY + Let t = MAX_SET s. + + Claim: !x. t < x /\ x <= m ==> (ROOT x n) ** x <> n + Proof: By contradiction, suppose (ROOT x n) ** x = n + Then x IN s, so x <= t by MAX_SET_PROPERTY + This contradicts t < x. + + Note t IN s by MAX_SET_IN_SET + so t <= m /\ (ROOT t n) ** t = n by above + Thus power_index n m = power_index n t by power_index_no_exact_roots, t <= m + and power_index n t = t by power_index_exact_root, (ROOT t n) ** t = n + But k <= t by MAX_SET_PROPERTY + Thus k <= t = power_index n m +*) +val power_index_lower = store_thm( + "power_index_lower", + ``!m n k. k <= m /\ ((ROOT k n) ** k = n) ==> k <= power_index n m``, + rpt strip_tac >> + Cases_on `k = 0` >| [ + `n = 1` by fs[EXP] >> + rw[power_index_of_1], + `0 < k` by decide_tac >> + qabbrev_tac `s = {j | j <= m /\ ((ROOT j n) ** j = n)}` >> + `!j. j IN s <=> j <= m /\ ((ROOT j n) ** j = n)` by rw[Abbr`s`] >> + `s SUBSET (count (SUC m))` by rw[SUBSET_DEF] >> + `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + `k IN s` by rw[] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + qabbrev_tac `t = MAX_SET s` >> + `!x. t < x /\ x <= m ==> (ROOT x n) ** x <> n` by + (spose_not_then strip_assume_tac >> + `x IN s` by rw[] >> + `x <= t` by rw[MAX_SET_PROPERTY, Abbr`t`] >> + decide_tac) >> + `t IN s` by rw[MAX_SET_IN_SET, Abbr`t`] >> + `power_index n m = power_index n t` by metis_tac[power_index_no_exact_roots] >> + `k <= t` by rw[MAX_SET_PROPERTY, Abbr`t`] >> + `(ROOT t n) ** t = n` by metis_tac[] >> + `power_index n t = t` by rw[power_index_exact_root] >> + decide_tac + ]); + +(* Theorem: 0 < power_index n k *) +(* Proof: + If k = 0, + True since power_index n 0 = 1 by power_index_0 + If k <> 0, + Then 1 <= k. + Note (ROOT 1 n) ** 1 = n ** 1 = n by ROOT_1, EXP_1 + Thus 1 <= power_index n k by power_index_lower + or 0 < power_index n k +*) +val power_index_pos = store_thm( + "power_index_pos", + ``!n k. 0 < power_index n k``, + rpt strip_tac >> + Cases_on `k = 0` >- + rw[power_index_0] >> + `1 <= power_index n k` by rw[power_index_lower, EXP_1] >> + decide_tac); + +(* Theorem: 0 < k ==> power_index n k <= k *) +(* Proof: + By induction on k. + Base: 0 < 0 ==> power_index n 0 <= 0 + True by 0 < 0 = F. + Step: 0 < k ==> power_index n k <= k ==> + 0 < SUC k ==> power_index n (SUC k) <= SUC k + If k = 0, + Then SUC k = 1 by ONE + True since power_index n 1 = 1 by power_index_1 + If k <> 0, + Let m = SUC k, or k = m - 1. + Then 1 < m by arithmetic + If (ROOT m n) ** m = n, + Then power_index n m + = m <= m by power_index_exact_root + If (ROOT m n) ** m <> n, + Then power_index n m + = power_index n (m - 1) by power_index_not_exact_root + = power_index n k by m - 1 = k + <= k by induction hypothesis + But k < SUC k = m by LESS_SUC + Thus power_index n m < m by LESS_EQ_LESS_TRANS + or power_index n m <= m by LESS_IMP_LESS_OR_EQ +*) +val power_index_upper = store_thm( + "power_index_upper", + ``!n k. 0 < k ==> power_index n k <= k``, + strip_tac >> + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `k = 0` >- + rw[power_index_1] >> + `1 < SUC k` by decide_tac >> + qabbrev_tac `m = SUC k` >> + Cases_on `(ROOT m n) ** m = n` >- + rw[power_index_exact_root] >> + rw[power_index_not_exact_root, Abbr`m`]); + +(* Theorem: 0 < k /\ k <= m ==> + ((power_index n m = power_index n k) <=> (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n)) *) +(* Proof: + If part: 0 < k /\ k <= m /\ power_index n m = power_index n k /\ k < j /\ j <= m ==> ROOT j n ** j <> n + By contradiction, suppose ROOT j n ** j = n. + Then j <= power_index n m by power_index_lower + But power_index n k <= k by power_index_upper, 0 < k + Thus j <= k by LESS_EQ_TRANS + This contradicts k < j. + Only-if part: 0 < k /\ k <= m /\ !j. k < j /\ j <= m ==> ROOT j n ** j <> n ==> + power_index n m = power_index n k + True by power_index_no_exact_roots +*) +val power_index_equal = store_thm( + "power_index_equal", + ``!m n k. 0 < k /\ k <= m ==> + ((power_index n m = power_index n k) <=> (!j. k < j /\ j <= m ==> (ROOT j n) ** j <> n))``, + rpt strip_tac >> + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `j <= power_index n m` by rw[power_index_lower] >> + `power_index n k <= k` by rw[power_index_upper] >> + decide_tac, + rw[power_index_no_exact_roots] + ]); + +(* Theorem: (power_index n m = k) ==> !j. k < j /\ j <= m ==> (ROOT j n) ** j <> n *) +(* Proof: + By contradiction, suppose k < j /\ j <= m /\ (ROOT j n) ** j = n. + Then j <= power_index n m by power_index_lower + This contradicts power_index n m = k < j by given +*) +val power_index_property = store_thm( + "power_index_property", + ``!m n k. (power_index n m = k) ==> !j. k < j /\ j <= m ==> (ROOT j n) ** j <> n``, + spose_not_then strip_assume_tac >> + `j <= power_index n m` by rw[power_index_lower] >> + decide_tac); + +(* Theorem: power_free n <=> (1 < n) /\ (power_index n (LOG2 n) = 1) *) +(* Proof: + By power_free_check_upto_LOG2, power_free_upto_def, this is to show: + 1 < n /\ (!j. 1 < j /\ j <= LOG2 n ==> ROOT j n ** j <> n) <=> + 1 < n /\ (power_index n (LOG2 n) = 1) + If part: + Note 0 < LOG2 n by LOG2_POS, 1 < n + power_index n (LOG2 n) + = power_index n 1 by power_index_no_exact_roots, 1 <= LOG2 n + = 1 by power_index_1 + Only-if part, true by power_index_property +*) +val power_free_by_power_index_LOG2 = store_thm( + "power_free_by_power_index_LOG2", + ``!n. power_free n <=> (1 < n) /\ (power_index n (LOG2 n) = 1)``, + rw[power_free_check_upto_LOG2, power_free_upto_def] >> + rw[EQ_IMP_THM] >| [ + `0 < LOG2 n` by rw[] >> + `1 <= LOG2 n` by decide_tac >> + `power_index n (LOG2 n) = power_index n 1` by rw[power_index_no_exact_roots] >> + rw[power_index_1], + metis_tac[power_index_property] + ]); + +(* Theorem: power_free n <=> (1 < n) /\ (power_index n (ulog n) = 1) *) +(* Proof: + By power_free_check_upto_ulog, power_free_upto_def, this is to show: + 1 < n /\ (!j. 1 < j /\ j <= ulog n ==> ROOT j n ** j <> n) <=> + 1 < n /\ (power_index n (ulog n) = 1) + If part: + Note 0 < ulog n by ulog_POS, 1 < n + power_index n (ulog n) + = power_index n 1 by power_index_no_exact_roots, 1 <= ulog n + = 1 by power_index_1 + Only-if part, true by power_index_property +*) +val power_free_by_power_index_ulog = store_thm( + "power_free_by_power_index_ulog", + ``!n. power_free n <=> (1 < n) /\ (power_index n (ulog n) = 1)``, + rw[power_free_check_upto_ulog, power_free_upto_def] >> + rw[EQ_IMP_THM] >| [ + `0 < ulog n` by rw[] >> + `1 <= ulog n` by decide_tac >> + `power_index n (ulog n) = power_index n 1` by rw[power_index_no_exact_roots] >> + rw[power_index_1], + metis_tac[power_index_property] + ]); + +(* ------------------------------------------------------------------------- *) +(* Prime Power Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: + ppidx n = prime_power_index p n + common_prime_divisors m n = (prime_divisors m) INTER (prime_divisors n) + total_prime_divisors m n = (prime_divisors m) UNION (prime_divisors n) + park_on m n = {p | p IN common_prime_divisors m n /\ ppidx m <= ppidx n} + park_off m n = {p | p IN common_prime_divisors m n /\ ppidx n < ppidx m} + park m n = PROD_SET (IMAGE (\p. p ** ppidx m) (park_on m n)) +*) +(* Definitions and Theorems (# are exported): + + Helper Theorem: + self_to_log_index_member |- !n x. MEM x [1 .. n] ==> MEM (x ** LOG x n) [1 .. n] + + Prime Power or Coprime Factors: + prime_power_or_coprime_factors |- !n. 1 < n ==> (?p k. 0 < k /\ prime p /\ (n = p ** k)) \/ + ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n /\ b < n + non_prime_power_coprime_factors |- !n. 1 < n /\ ~(?p k. 0 < k /\ prime p /\ (n = p ** k)) ==> + ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ a < n /\ 1 < b /\ b < n + pairwise_coprime_for_prime_powers |- !s f. s SUBSET prime ==> PAIRWISE_COPRIME (IMAGE (\p. p ** f p) s) + + Prime Power Index: + prime_power_index_exists |- !n p. 0 < n /\ prime p ==> ?m. p ** m divides n /\ coprime p (n DIV p ** m) + prime_power_index_def |- !p n. 0 < n /\ prime p ==> + p ** ppidx n divides n /\ coprime p (n DIV p ** ppidx n) + prime_power_factor_divides |- !n p. prime p ==> p ** ppidx n divides n + prime_power_cofactor_coprime |- !n p. 0 < n /\ prime p ==> coprime p (n DIV p ** ppidx n) + prime_power_eqn |- !n p. 0 < n /\ prime p ==> (n = p ** ppidx n * (n DIV p ** ppidx n)) + prime_power_divisibility |- !n p. 0 < n /\ prime p ==> !k. p ** k divides n <=> k <= ppidx n + prime_power_index_maximal |- !n p. 0 < n /\ prime p ==> !k. k > ppidx n ==> ~(p ** k divides n) + prime_power_index_of_divisor |- !m n. 0 < n /\ m divides n ==> !p. prime p ==> ppidx m <= ppidx n + prime_power_index_test |- !n p. 0 < n /\ prime p ==> + !k. (k = ppidx n) <=> ?q. (n = p ** k * q) /\ coprime p q: + prime_power_index_1 |- !p. prime p ==> (ppidx 1 = 0) + prime_power_index_eq_0 |- !n p. 0 < n /\ prime p /\ ~(p divides n) ==> (ppidx n = 0) + prime_power_index_prime_power |- !p. prime p ==> !k. ppidx (p ** k) = k + prime_power_index_prime |- !p. prime p ==> (ppidx p = 1) + prime_power_index_eqn |- !n p. 0 < n /\ prime p ==> (let q = n DIV p ** ppidx n in + (n = p ** ppidx n * q) /\ coprime p q) + prime_power_index_pos |- !n p. 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n + + Prime Power and GCD, LCM: + gcd_prime_power_factor |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + (gcd a b = p ** MIN (ppidx a) (ppidx b) * gcd (a DIV p ** ppidx a) (b DIV p ** ppidx b)) + gcd_prime_power_factor_divides_gcd + |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + p ** MIN (ppidx a) (ppidx b) divides gcd a b + gcd_prime_power_cofactor_coprime + |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + coprime p (gcd (a DIV p ** ppidx a) (b DIV p ** ppidx b)) + gcd_prime_power_index |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + (ppidx (gcd a b) = MIN (ppidx a) (ppidx b)) + gcd_prime_power_divisibility |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + !k. p ** k divides gcd a b ==> k <= MIN (ppidx a) (ppidx b) + + lcm_prime_power_factor |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + (lcm a b = p ** MAX (ppidx a) (ppidx b) * lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b)) + lcm_prime_power_factor_divides_lcm + |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + p ** MAX (ppidx a) (ppidx b) divides lcm a b + lcm_prime_power_cofactor_coprime + |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + coprime p (lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b)) + lcm_prime_power_index |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + (ppidx (lcm a b) = MAX (ppidx a) (ppidx b)) + lcm_prime_power_divisibility |- !a b p. 0 < a /\ 0 < b /\ prime p ==> + !k. p ** k divides lcm a b ==> k <= MAX (ppidx a) (ppidx b) + + Prime Powers and List LCM: + list_lcm_prime_power_factor_divides |- !l p. prime p ==> p ** MAX_LIST (MAP ppidx l) divides list_lcm l + list_lcm_prime_power_index |- !l p. prime p /\ POSITIVE l ==> + (ppidx (list_lcm l) = MAX_LIST (MAP ppidx l)) + list_lcm_prime_power_divisibility |- !l p. prime p /\ POSITIVE l ==> + !k. p ** k divides list_lcm l ==> k <= MAX_LIST (MAP ppidx l) + list_lcm_prime_power_factor_member |- !l p. prime p /\ l <> [] /\ POSITIVE l ==> + !k. p ** k divides list_lcm l ==> ?x. MEM x l /\ p ** k divides x + lcm_special_for_prime_power |- !p. prime p ==> !m n. (n = p ** SUC (ppidx m)) ==> (lcm n m = p * m) + lcm_special_for_coprime_factors |- !n a b. (n = a * b) /\ coprime a b ==> + !m. a divides m /\ b divides m ==> (lcm n m = m) + + Prime Divisors: + prime_divisors_def |- !n. prime_divisors n = {p | prime p /\ p divides n} + prime_divisors_element |- !n p. p IN prime_divisors n <=> prime p /\ p divides n + prime_divisors_subset_natural |- !n. 0 < n ==> prime_divisors n SUBSET natural n + prime_divisors_finite |- !n. 0 < n ==> FINITE (prime_divisors n) + prime_divisors_0 |- prime_divisors 0 = prime + prime_divisors_1 |- prime_divisors 1 = {} + prime_divisors_subset_prime |- !n. prime_divisors n SUBSET prime + prime_divisors_nonempty |- !n. 1 < n ==> prime_divisors n <> {} + prime_divisors_empty_iff |- !n. (prime_divisors n = {}) <=> (n = 1) + prime_divisors_0_not_sing |- ~SING (prime_divisors 0) + prime_divisors_prime |- !n. prime n ==> (prime_divisors n = {n}) + prime_divisors_prime_power |- !n. prime n ==> !k. 0 < k ==> (prime_divisors (n ** k) = {n}) + prime_divisors_sing |- !n. SING (prime_divisors n) <=> ?p k. prime p /\ 0 < k /\ (n = p ** k) + prime_divisors_sing_alt |- !n p. (prime_divisors n = {p}) <=> ?k. prime p /\ 0 < k /\ (n = p ** k) + prime_divisors_sing_property |- !n. SING (prime_divisors n) ==> (let p = CHOICE (prime_divisors n) in + prime p /\ (n = p ** ppidx n)) + prime_divisors_divisor_subset |- !m n. m divides n ==> prime_divisors m SUBSET prime_divisors n + prime_divisors_common_divisor |- !n m x. x divides m /\ x divides n ==> + prime_divisors x SUBSET prime_divisors m INTER prime_divisors n + prime_divisors_common_multiple |- !n m x. m divides x /\ n divides x ==> + prime_divisors m UNION prime_divisors n SUBSET prime_divisors x + prime_power_index_common_divisor |- !n m x. 0 < m /\ 0 < n /\ x divides m /\ x divides n ==> + !p. prime p ==> ppidx x <= MIN (ppidx m) (ppidx n) + prime_power_index_common_multiple |- !n m x. 0 < x /\ m divides x /\ n divides x ==> + !p. prime p ==> MAX (ppidx m) (ppidx n) <= ppidx x + prime_power_index_le_log_index |- !n p. 0 < n /\ prime p ==> ppidx n <= LOG p n + + Prime-related Sets: + primes_upto_def |- !n. primes_upto n = {p | prime p /\ p <= n} + prime_powers_upto_def |- !n. prime_powers_upto n = IMAGE (\p. p ** LOG p n) (primes_upto n) + prime_power_divisors_def |- !n. prime_power_divisors n = IMAGE (\p. p ** ppidx n) (prime_divisors n) + + primes_upto_element |- !n p. p IN primes_upto n <=> prime p /\ p <= n + primes_upto_subset_natural |- !n. primes_upto n SUBSET natural n + primes_upto_finite |- !n. FINITE (primes_upto n) + primes_upto_pairwise_coprime |- !n. PAIRWISE_COPRIME (primes_upto n) + primes_upto_0 |- primes_upto 0 = {} + primes_count_0 |- primes_count 0 = 0 + primes_upto_1 |- primes_upto 1 = {} + primes_count_1 |- primes_count 1 = 0 + + prime_powers_upto_element |- !n x. x IN prime_powers_upto n <=> + ?p. (x = p ** LOG p n) /\ prime p /\ p <= n + prime_powers_upto_element_alt |- !p n. prime p /\ p <= n ==> p ** LOG p n IN prime_powers_upto n + prime_powers_upto_finite |- !n. FINITE (prime_powers_upto n) + prime_powers_upto_pairwise_coprime |- !n. PAIRWISE_COPRIME (prime_powers_upto n) + prime_powers_upto_0 |- prime_powers_upto 0 = {} + prime_powers_upto_1 |- prime_powers_upto 1 = {} + + prime_power_divisors_element |- !n x. x IN prime_power_divisors n <=> + ?p. (x = p ** ppidx n) /\ prime p /\ p divides n + prime_power_divisors_element_alt |- !p n. prime p /\ p divides n ==> + p ** ppidx n IN prime_power_divisors n + prime_power_divisors_finite |- !n. 0 < n ==> FINITE (prime_power_divisors n) + prime_power_divisors_pairwise_coprime |- !n. PAIRWISE_COPRIME (prime_power_divisors n) + prime_power_divisors_1 |- prime_power_divisors 1 = {} + + Factorisations: + prime_factorisation |- !n. 0 < n ==> (n = PROD_SET (prime_power_divisors n)) + basic_prime_factorisation |- !n. 0 < n ==> + (n = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n))) + divisor_prime_factorisation |- !m n. 0 < n /\ m divides n ==> + (m = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors n))) + gcd_prime_factorisation |- !m n. 0 < m /\ 0 < n ==> + (gcd m n = PROD_SET (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) + (prime_divisors m INTER prime_divisors n))) + lcm_prime_factorisation |- !m n. 0 < m /\ 0 < n ==> + (lcm m n = PROD_SET (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) + (prime_divisors m UNION prime_divisors n))) + + GCD and LCM special coprime decompositions: + common_prime_divisors_element |- !m n p. p IN common_prime_divisors m n <=> + p IN prime_divisors m /\ p IN prime_divisors n + common_prime_divisors_finite |- !m n. 0 < m /\ 0 < n ==> FINITE (common_prime_divisors m n) + common_prime_divisors_pairwise_coprime |- !m n. PAIRWISE_COPRIME (common_prime_divisors m n) + common_prime_divisors_min_image_pairwise_coprime + |- !m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n)) + total_prime_divisors_element |- !m n p. p IN total_prime_divisors m n <=> + p IN prime_divisors m \/ p IN prime_divisors n + total_prime_divisors_finite |- !m n. 0 < m /\ 0 < n ==> FINITE (total_prime_divisors m n) + total_prime_divisors_pairwise_coprime |- !m n. PAIRWISE_COPRIME (total_prime_divisors m n) + total_prime_divisors_max_image_pairwise_coprime + |- !m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n)) + + park_on_element |- !m n p. p IN park_on m n <=> + p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n + park_off_element |- !m n p. p IN park_off m n <=> + p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m + park_off_alt |- !m n. park_off m n = common_prime_divisors m n DIFF park_on m n + park_on_subset_common |- !m n. park_on m n SUBSET common_prime_divisors m n + park_off_subset_common |- !m n. park_off m n SUBSET common_prime_divisors m n + park_on_subset_total |- !m n. park_on m n SUBSET total_prime_divisors m n + park_off_subset_total |- !m n. park_off m n SUBSET total_prime_divisors m n + park_on_off_partition |- !m n. common_prime_divisors m n =|= park_on m n # park_off m n + park_off_image_has_not_1 |- !m n. 1 NOTIN IMAGE (\p. p ** ppidx m) (park_off m n) + + park_on_off_common_image_partition + |- !m n. (let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n) in + let u = IMAGE (\p. p ** ppidx m) (park_on m n) in + let v = IMAGE (\p. p ** ppidx n) (park_off m n) in + 0 < m ==> s =|= u # v) + gcd_park_decomposition |- !m n. 0 < m /\ 0 < n ==> + (let a = mypark m n in let b = gcd m n DIV a in + (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ + (gcd m n = a * b) /\ coprime a b) + gcd_park_decompose |- !m n. 0 < m /\ 0 < n ==> + (let a = mypark m n in let b = gcd m n DIV a in + (gcd m n = a * b) /\ coprime a b) + + park_on_off_total_image_partition + |- !m n. (let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n) in + let u = IMAGE (\p. p ** ppidx m) (prime_divisors m DIFF park_on m n) in + let v = IMAGE (\p. p ** ppidx n) (prime_divisors n DIFF park_off m n) in + 0 < m /\ 0 < n ==> s =|= u # v) + lcm_park_decomposition |- !m n. 0 < m /\ 0 < n ==> + (let a = park m n in let b = gcd m n DIV a in + let p = m DIV a in let q = a * n DIV gcd m n in + (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ + (p = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors m DIFF park_on m n))) /\ + (q = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n DIFF park_off m n))) /\ + (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q)) + lcm_park_decompose |- !m n. 0 < m /\ 0 < n ==> + (let a = park m n in let p = m DIV a in let q = a * n DIV gcd m n in + (lcm m n = p * q) /\ coprime p q) + lcm_gcd_park_decompose |- !m n. 0 < m /\ 0 < n ==> + (let a = park m n in let b = gcd m n DIV a in + let p = m DIV a in let q = a * n DIV gcd m n in + (lcm m n = p * q) /\ coprime p q /\ + (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q)) + + Consecutive LCM Recurrence: + lcm_fun_def |- (lcm_fun 0 = 1) /\ + !n. lcm_fun (SUC n) = if n = 0 then 1 else + case some p. ?k. 0 < k /\ prime p /\ (SUC n = p ** k) of + NONE => lcm_fun n + | SOME p => p * lcm_fun n + lcm_fun_0 |- lcm_fun 0 = 1 + lcm_fun_SUC |- !n. lcm_fun (SUC n) = if n = 0 then 1 else + case some p. ?k. 0 < k /\ prime p /\ (SUC n = p ** k) of + NONE => lcm_fun n + | SOME p => p * lcm_fun n + lcm_fun_1 |- lcm_fun 1 = 1 + lcm_fun_2 |- lcm_fun 2 = 2 + lcm_fun_suc_some |- !n p. prime p /\ (?k. 0 < k /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = p * lcm_fun n) + lcm_fun_suc_none |- !n. ~(?p k. 0 < k /\ prime p /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = lcm_fun n) + list_lcm_prime_power_index_lower |- !l p. prime p /\ l <> [] /\ POSITIVE l ==> + !x. MEM x l ==> ppidx x <= ppidx (list_lcm l) + list_lcm_with_last_prime_power |- !n p k. prime p /\ (n + 2 = p ** k) ==> + (list_lcm [1 .. n + 2] = p * list_lcm (leibniz_vertical n)) + list_lcm_with_last_non_prime_power |- !n. (!p k. (k = 0) \/ ~prime p \/ n + 2 <> p ** k) ==> + (list_lcm [1 .. n + 2] = list_lcm (leibniz_vertical n)) + list_lcm_eq_lcm_fun |- !n. list_lcm (leibniz_vertical n) = lcm_fun (n + 1) + lcm_fun_lower_bound |- !n. 2 ** n <= lcm_fun (n + 1) + lcm_fun_lower_bound_alt |- !n. 0 < n ==> 2 ** (n - 1) <= lcm_fun n + prime_power_index_suc_special |- !n p. 0 < n /\ prime p /\ (SUC n = p ** ppidx (SUC n)) ==> + (ppidx (SUC n) = SUC (ppidx (list_lcm [1 .. n]))) + prime_power_index_suc_property |- !n p. 0 < n /\ prime p /\ (n + 1 = p ** ppidx (n + 1)) ==> + (ppidx (n + 1) = 1 + ppidx (list_lcm [1 .. n])) + + Consecutive LCM Recurrence - Rework: + list_lcm_by_last_prime_power |- !n. SING (prime_divisors (n + 1)) ==> + (list_lcm [1 .. (n + 1)] = CHOICE (prime_divisors (n + 1)) * list_lcm [1 .. n]) + list_lcm_by_last_non_prime_power |- !n. ~SING (prime_divisors (n + 1)) ==> + (list_lcm (leibniz_vertical n) = list_lcm [1 .. n]) + list_lcm_recurrence |- !n. list_lcm (leibniz_vertical n) = (let s = prime_divisors (n + 1) in + if SING s then CHOICE s * list_lcm [1 .. n] else list_lcm [1 .. n]) + list_lcm_option_last_prime_power |- !n p. (prime_divisors (n + 1) = {p}) ==> + (list_lcm (leibniz_vertical n) = p * list_lcm [1 .. n]) + list_lcm_option_last_non_prime_power |- !n. (!p. prime_divisors (n + 1) <> {p}) ==> + (list_lcm (leibniz_vertical n) = list_lcm [1 .. n]) + list_lcm_option_recurrence |- !n. list_lcm (leibniz_vertical n) = + case some p. prime_divisors (n + 1) = {p} of + NONE => list_lcm [1 .. n] + | SOME p => p * list_lcm [1 .. n] + + Relating Consecutive LCM to Prime Functions: + Theorems on Prime-related Sets: + prime_powers_upto_list_mem |- !n x. MEM x (SET_TO_LIST (prime_powers_upto n)) <=> + ?p. (x = p ** LOG p n) /\ prime p /\ p <= n + prime_powers_upto_lcm_prime_to_log_divisor + |- !n p. prime p /\ p <= n ==> + p ** LOG p n divides set_lcm (prime_powers_upto n) + prime_powers_upto_lcm_prime_divisor + |- !n p. prime p /\ p <= n ==> p divides set_lcm (prime_powers_upto n) + prime_powers_upto_lcm_prime_to_power_divisor + |- !n p. prime p /\ p <= n ==> + p ** ppidx n divides set_lcm (prime_powers_upto n) + prime_powers_upto_lcm_divisor |- !n x. 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n) + + Consecutive LCM and Prime-related Sets: + lcm_run_eq_set_lcm_prime_powers |- !n. lcm_run n = set_lcm (prime_powers_upto n) + set_lcm_prime_powers_upto_eqn |- !n. set_lcm (prime_powers_upto n) = PROD_SET (prime_powers_upto n) + lcm_run_eq_prod_set_prime_powers |- !n. lcm_run n = PROD_SET (prime_powers_upto n) + prime_powers_upto_prod_set_le |- !n. PROD_SET (prime_powers_upto n) <= n ** primes_count n + lcm_run_upper_by_primes_count |- !n. lcm_run n <= n ** primes_count n + prime_powers_upto_prod_set_ge |- !n. PROD_SET (primes_upto n) <= PROD_SET (prime_powers_upto n) + lcm_run_lower_by_primes_product |- !n. PROD_SET (primes_upto n) <= lcm_run n + prime_powers_upto_prod_set_mix_ge |- !n. n ** primes_count n <= + PROD_SET (primes_upto n) * PROD_SET (prime_powers_upto n) + primes_count_upper_by_product |- !n. n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n + primes_count_upper_by_lcm_run |- !n. n ** primes_count n <= lcm_run n ** 2 + lcm_run_lower_by_primes_count |- !n. SQRT (n ** primes_count n) <= lcm_run n +*) + +(* ------------------------------------------------------------------------- *) +(* Helper Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MEM x [1 .. n] ==> MEM (x ** LOG x n) [1 .. n] *) +(* Proof: + By listRangeINC_MEM, this is to show: + (1) 1 <= x /\ x <= n ==> 1 <= x ** LOG x n + Note 0 < x by 1 <= x + so 0 < x ** LOG x n by EXP_POS, 0 < x + or 1 <= x ** LOG x n by arithmetic + (2) 1 <= x /\ x <= n ==> x ** LOG x n <= n + Note 1 <= n /\ 0 < n by arithmetic + If x = 1, + Then true by EXP_1 + If x <> 1, + Then 1 < x, so true by LOG +*) +Theorem self_to_log_index_member: + !n x. MEM x [1 .. n] ==> MEM (x ** LOG x n) [1 .. n] +Proof + rw[listRangeINC_MEM] >> + ‘0 < n /\ 1 <= n’ by decide_tac >> + Cases_on ‘x = 1’ >- + rw[EXP_1] >> rw[LOG] +QED + +(* ------------------------------------------------------------------------- *) +(* Prime Power or Coprime Factors *) +(* ------------------------------------------------------------------------- *) + +(* +The concept of a prime number goes like this: +* Given a number n > 1, it has factors n = a * b. + Either there are several possibilities, or there is just the trivial: 1 * n and n * 1. + A number with only trivial factors is a prime, otherwise it is a composite. +The concept of a prime power number goes like this: +* Given a number n > 1, it has factors n = a * b. + Either a and b can be chosen to be coprime, or this choice is impossible. + A number that cannot have coprime factors is a prime power, otherwise a coprime product. +*) + +(* Theorem: 1 < n ==> (?p k. 0 < k /\ prime p /\ (n = p ** k)) \/ + (?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n /\ b < n) *) +(* Proof: + Note 1 < n ==> 0 < n /\ n <> 0 /\ n <> 1 by arithmetic + Now ?p. prime p /\ p divides n by PRIME_FACTOR, n <> 1 + so ?k. 0 < k /\ p ** k divides n /\ + coprime p (n DIV p ** k) by FACTOR_OUT_PRIME, EXP_1, 0 < n + Note 0 < p by PRIME_POS + so 0 < p ** k by EXP_POS + Let b = n DIV p ** k. + Then n = (p ** k) * b by DIVIDES_EQN_COMM, 0 < p ** m + + If b = 1, + Then n = p ** k by MULT_RIGHT_1 + Take k for the first assertion. + If b <> 1, + Let a = p ** k. + Then coprime a b by coprime_exp, coprime p b + Since p <> 1 by NOT_PRIME_1 + so a <> 1 by EXP_EQ_1 + and b <> 0 by MULT_0 + Now a divides n /\ b divides n by divides_def, MULT_COMM + so a <= n /\ b <= n by DIVIDES_LE, 0 < n + but a <> n /\ b <> n by MULT_LEFT_ID, MULT_RIGHT_ID + Thus 1 < a /\ 1 < b /\ a < n /\ b < n by arithmetic + Take a, b for the second assertion. +*) +val prime_power_or_coprime_factors = store_thm( + "prime_power_or_coprime_factors", + ``!n. 1 < n ==> (?p k. 0 < k /\ prime p /\ (n = p ** k)) \/ + (?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n /\ b < n)``, + rpt strip_tac >> + `0 < n /\ n <> 0 /\ n <> 1` by decide_tac >> + `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> + `?k. 0 < k /\ p ** k divides n /\ coprime p (n DIV p ** k)` by metis_tac[FACTOR_OUT_PRIME, EXP_1] >> + `0 < p ** k` by rw[PRIME_POS, EXP_POS] >> + qabbrev_tac `b = n DIV p ** k` >> + `n = (p ** k) * b` by rw[GSYM DIVIDES_EQN_COMM, Abbr`b`] >> + Cases_on `b = 1` >- + metis_tac[MULT_RIGHT_1] >> + qabbrev_tac `a = p ** k` >> + `coprime a b` by rw[coprime_exp, Abbr`a`] >> + `a <> 1` by metis_tac[EXP_EQ_1, NOT_PRIME_1, NOT_ZERO_LT_ZERO] >> + `b <> 0` by metis_tac[MULT_0] >> + `a divides n /\ b divides n` by metis_tac[divides_def, MULT_COMM] >> + `a <= n /\ b <= n` by rw[DIVIDES_LE] >> + `a <> n /\ b <> n` by metis_tac[MULT_LEFT_ID, MULT_RIGHT_ID] >> + `1 < a /\ 1 < b /\ a < n /\ b < n` by decide_tac >> + metis_tac[]); + +(* The following is the more powerful version of this: + Simple theorem: If n is not a prime, then ?a b. (n = a * b) /\ 1 < a /\ a < n /\ 1 < b /\ b < n + Powerful theorem: If n is not a prime power, then ?a b. (n = a * b) /\ 1 < a /\ a < n /\ 1 < b /\ b < n +*) + +(* Theorem: 1 < n /\ ~(?p k. 0 < k /\ prime p /\ (n = p ** k)) ==> + ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ a < n /\ 1 < b /\ b < n *) +(* Proof: + Since 1 < n, n <> 1 and 0 < n by arithmetic + Note ?p. prime p /\ p divides n by PRIME_FACTOR, n <> 1 + and ?m. 0 < m /\ p ** m divides n /\ + !k. coprime (p ** k) (n DIV p ** m) by FACTOR_OUT_PRIME, 0 < n + + Let a = p ** m, b = n DIV a. + Since 0 < p by PRIME_POS + so 0 < a by EXP_POS, 0 < p + Thus n = a * b by DIVIDES_EQN_COMM, 0 < a + + Also 1 < p by ONE_LT_PRIME + so 1 < a by ONE_LT_EXP, 1 < p, 0 < m + and b < n by DIV_LESS, Abbr, 0 < n + Now b <> 1 by MULT_RIGHT_1, n not perfect power of any prime + and b <> 0 by MULT_0, n = a * b, 0 < n + ==> 1 < b by b <> 0 /\ b <> 1 + Also a <= n by DIVIDES_LE + and a <> n by MULT_RIGHT_1 + ==> a < n by arithmetic + Take these a and b, the result follows. +*) +val non_prime_power_coprime_factors = store_thm( + "non_prime_power_coprime_factors", + ``!n. 1 < n /\ ~(?p k. 0 < k /\ prime p /\ (n = p ** k)) ==> + ?a b. (n = a * b) /\ coprime a b /\ 1 < a /\ a < n /\ 1 < b /\ b < n``, + rpt strip_tac >> + `0 < n` by decide_tac >> + `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> + `?m. 0 < m /\ p ** m divides n /\ !k. coprime (p ** k) (n DIV p ** m)` by rw[FACTOR_OUT_PRIME] >> + qabbrev_tac `a = p ** m` >> + qabbrev_tac `b = n DIV a` >> + `0 < a` by rw[PRIME_POS, EXP_POS, Abbr`a`] >> + `n = a * b` by metis_tac[DIVIDES_EQN_COMM] >> + `1 < a` by rw[ONE_LT_EXP, ONE_LT_PRIME, Abbr`a`] >> + `b < n` by rw[DIV_LESS, Abbr`b`] >> + `b <> 1` by metis_tac[MULT_RIGHT_1] >> + `b <> 0` by metis_tac[MULT_0, NOT_ZERO_LT_ZERO] >> + `1 < b` by decide_tac >> + `a <= n` by rw[DIVIDES_LE] >> + `a <> n` by metis_tac[MULT_RIGHT_1] >> + `a < n` by decide_tac >> + metis_tac[]); + +(* Theorem: s SUBSET prime ==> PAIRWISE_COPRIME (IMAGE (\p. p ** f p) s) *) +(* Proof: + By SUBSET_DEF, this is to show: + (!x. x IN s ==> prime x) /\ p IN s /\ p' IN s /\ p ** f <> p' ** f ==> coprime (p ** f) (p' ** f) + Note prime p /\ prime p' by in_prime + and p <> p' by p ** f <> p' ** f + Thus coprime (p ** f) (p' ** f) by prime_powers_coprime +*) +val pairwise_coprime_for_prime_powers = store_thm( + "pairwise_coprime_for_prime_powers", + ``!s f. s SUBSET prime ==> PAIRWISE_COPRIME (IMAGE (\p. p ** f p) s)``, + rw[SUBSET_DEF] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[in_prime] >> + rw[prime_powers_coprime]); + +(* ------------------------------------------------------------------------- *) +(* Prime Power Index *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < n /\ prime p ==> ?m. (p ** m) divides n /\ coprime p (n DIV (p ** m)) *) +(* Proof: + Note ?q m. (n = (p ** m) * q) /\ coprime p q by prime_power_factor + Let t = p ** m. + Then t divides n by divides_def, MULT_COMM + Now 0 < p by PRIME_POS + so 0 < t by EXP_POS + ==> n = t * (n DIV t) by DIVIDES_EQN_COMM + Thus q = n DIV t by MULT_LEFT_CANCEL + Take this m, and the result follows. +*) +val prime_power_index_exists = store_thm( + "prime_power_index_exists", + ``!n p. 0 < n /\ prime p ==> ?m. (p ** m) divides n /\ coprime p (n DIV (p ** m))``, + rpt strip_tac >> + `?q m. (n = (p ** m) * q) /\ coprime p q` by rw[prime_power_factor] >> + qabbrev_tac `t = p ** m` >> + `t divides n` by metis_tac[divides_def, MULT_COMM] >> + `0 < t` by rw[PRIME_POS, EXP_POS, Abbr`t`] >> + metis_tac[DIVIDES_EQN_COMM, MULT_LEFT_CANCEL, NOT_ZERO_LT_ZERO]); + +(* Apply Skolemization *) +val lemma = prove( + ``!p n. ?m. 0 < n /\ prime p ==> (p ** m) divides n /\ coprime p (n DIV (p ** m))``, + metis_tac[prime_power_index_exists]); +(* Note !p n, for first parameter p, second parameter n. *) +(* +- SKOLEM_THM; +> val it = |- !P. (!x. ?y. P x y) <=> ?f. !x. P x (f x) : thm +*) +(* Define prime power index *) +(* +- SIMP_RULE bool_ss [SKOLEM_THM] lemma; +> val it = |- ?f. !p n. 0 < n /\ prime p ==> p ** f p n divides n /\ coprime p (n DIV p ** f p n): thm +*) +val prime_power_index_def = new_specification( + "prime_power_index_def", + ["prime_power_index"], + SIMP_RULE bool_ss [SKOLEM_THM] lemma); +(* +> val prime_power_index_def = |- !p n. 0 < n /\ prime p ==> + p ** prime_power_index p n divides n /\ coprime p (n DIV p ** prime_power_index p n): thm +*) + +(* Overload on prime_power_index of prime p *) +val _ = overload_on("ppidx", ``prime_power_index p``); + +(* +> prime_power_index_def; +val it = |- !p n. 0 < n /\ prime p ==> p ** ppidx n divides n /\ coprime p (n DIV p ** ppidx n): thm +*) + +(* Theorem: prime p ==> (p ** (ppidx n)) divides n *) +(* Proof: by prime_power_index_def, and ALL_DIVIDES_0 *) +val prime_power_factor_divides = store_thm( + "prime_power_factor_divides", + ``!n p. prime p ==> (p ** (ppidx n)) divides n``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[] >> + rw[prime_power_index_def]); + +(* Theorem: 0 < n /\ prime p ==> coprime p (n DIV p ** ppidx n) *) +(* Proof: by prime_power_index_def *) +val prime_power_cofactor_coprime = store_thm( + "prime_power_cofactor_coprime", + ``!n p. 0 < n /\ prime p ==> coprime p (n DIV p ** ppidx n)``, + rw[prime_power_index_def]); + +(* Theorem: 0 < n /\ prime p ==> (n = p ** (ppidx n) * (n DIV p ** (ppidx n))) *) +(* Proof: + Let q = p ** (ppidx n). + Then q divides n by prime_power_factor_divides + But 0 < n ==> n <> 0, + so q <> 0, or 0 < q by ZERO_DIVIDES + Thus n = q * (n DIV q) by DIVIDES_EQN_COMM, 0 < q +*) +val prime_power_eqn = store_thm( + "prime_power_eqn", + ``!n p. 0 < n /\ prime p ==> (n = p ** (ppidx n) * (n DIV p ** (ppidx n)))``, + rpt strip_tac >> + qabbrev_tac `q = p ** (ppidx n)` >> + `q divides n` by rw[prime_power_factor_divides, Abbr`q`] >> + `0 < q` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + rw[GSYM DIVIDES_EQN_COMM]); + +(* Theorem: 0 < n /\ prime p ==> !k. (p ** k) divides n <=> k <= (ppidx n) *) +(* Proof: + Let m = ppidx n. + Then p ** m divides n by prime_power_factor_divides + If part: p ** k divides n ==> k <= m + By contradiction, suppose m < k. + Let q = n DIV p ** m. + Then n = p ** m * q by prime_power_eqn + ==> ?t. n = p ** k * t by divides_def, MULT_COMM + Let d = k - m. + Then 0 < d by m < k + ==> p ** k = p ** m * p ** d by EXP_BY_ADD_SUB_LT + But 0 < p ** m by PRIME_POS, EXP_POS + so p ** m <> 0 by arithmetic + Thus q = p ** d * t by MULT_LEFT_CANCEL, MULT_ASSOC + Since p divides p ** d by prime_divides_self_power, 0 < d + so p divides q by DIVIDES_MULT + or gcd p q = p by divides_iff_gcd_fix + But coprime p q by prime_power_cofactor_coprime + This is a contradiction since p <> 1 by NOT_PRIME_1 + + Only-if part: k <= m ==> p ** k divides n + Note p ** m = p ** d * p ** k by EXP_BY_ADD_SUB_LE, MULT_COMM + Thus p ** k divides p ** m by divides_def + ==> p ** k divides n by DIVIDES_TRANS +*) + +Theorem prime_power_divisibility: + !n p. 0 < n /\ prime p ==> !k. (p ** k) divides n <=> k <= (ppidx n) +Proof + rpt strip_tac >> + qabbrev_tac `m = ppidx n` >> + `p ** m divides n` by rw[prime_power_factor_divides, Abbr`m`] >> + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `m < k` by decide_tac >> + qabbrev_tac `q = n DIV p ** m` >> + `n = p ** m * q` by rw[prime_power_eqn, Abbr`m`, Abbr`q`] >> + `?t. n = p ** k * t` by metis_tac[divides_def, MULT_COMM] >> + `p ** k = p ** m * p ** (k - m)` by rw[EXP_BY_ADD_SUB_LT] >> + `0 < k - m` by decide_tac >> + qabbrev_tac `d = k - m` >> + `0 < p ** m` by rw[PRIME_POS, EXP_POS] >> + `p ** m <> 0` by decide_tac >> + `q = p ** d * t` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> + `p divides p ** d` by rw[prime_divides_self_power] >> + `p divides q` by simp[DIVIDES_MULTIPLE] >> + `gcd p q = p` by rw[GSYM divides_iff_gcd_fix] >> + `coprime p q` by rw[GSYM prime_power_cofactor_coprime, Abbr`m`, Abbr`q`] >> + metis_tac[NOT_PRIME_1], + `p ** m = p ** (m - k) * p ** k` by rw[EXP_BY_ADD_SUB_LE, MULT_COMM] >> + `p ** k divides p ** m` by metis_tac[divides_def] >> + metis_tac[DIVIDES_TRANS] + ] +QED + +(* Theorem: 0 < n /\ prime p ==> !k. k > ppidx n ==> ~(p ** k divides n) *) +(* Proof: by prime_power_divisibility *) +val prime_power_index_maximal = store_thm( + "prime_power_index_maximal", + ``!n p. 0 < n /\ prime p ==> !k. k > ppidx n ==> ~(p ** k divides n)``, + rw[prime_power_divisibility]); + +(* Theorem: 0 < n /\ m divides n ==> !p. prime p ==> ppidx m <= ppidx n *) +(* Proof: + Note 0 < m by ZERO_DIVIDES, 0 < n + Thus p ** ppidx m divides m by prime_power_factor_divides, 0 < m + ==> p ** ppidx m divides n by DIVIDES_TRANS + or ppidx m <= ppidx n by prime_power_divisibility, 0 < n +*) +val prime_power_index_of_divisor = store_thm( + "prime_power_index_of_divisor", + ``!m n. 0 < n /\ m divides n ==> !p. prime p ==> ppidx m <= ppidx n``, + rpt strip_tac >> + `0 < m` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + `p ** ppidx m divides m` by rw[prime_power_factor_divides] >> + `p ** ppidx m divides n` by metis_tac[DIVIDES_TRANS] >> + rw[GSYM prime_power_divisibility]); + +(* Theorem: 0 < n /\ prime p ==> !k. (k = ppidx n) <=> (?q. (n = p ** k * q) /\ coprime p q) *) +(* Proof: + If part: k = ppidx n ==> ?q. (n = p ** k * q) /\ coprime p q + Let q = n DIV p ** k, where k = ppidx n. + Then n = p ** k * q by prime_power_eqn + and coprime p q by prime_power_cofactor_coprime + Only-if part: n = p ** k * q /\ coprime p q ==> k = ppidx n + Note n = p ** (ppidx n) * q by prime_power_eqn + + Thus p ** k divides n by divides_def, MULT_COMM + ==> k <= ppidx n by prime_power_divisibility + + Claim: ppidx n <= k + Proof: By contradiction, suppose k < ppidx n. + Let d = ppidx n - k, then 0 < d. + Let nq = n DIV p ** (ppidx n). + Then n = p ** (ppidx n) * nq by prime_power_eqn + Note p ** ppidx n = p ** k * p ** d by EXP_BY_ADD_SUB_LT + Now 0 < p ** k by PRIME_POS, EXP_POS + so q = p ** d * nq by MULT_LEFT_CANCEL, MULT_ASSOC, p ** k <> 0 + But p divides p ** d by prime_divides_self_power, 0 < d + and p ** d divides q by divides_def, MULT_COMM + ==> p divides q by DIVIDES_TRANS + or gcd p q = p by divides_iff_gcd_fix + This contradicts coprime p q as p <> 1 by NOT_PRIME_1 + + With k <= ppidx n and ppidx n <= k, k = ppidx n by LESS_EQUAL_ANTISYM +*) +val prime_power_index_test = store_thm( + "prime_power_index_test", + ``!n p. 0 < n /\ prime p ==> !k. (k = ppidx n) <=> (?q. (n = p ** k * q) /\ coprime p q)``, + rw_tac std_ss[EQ_IMP_THM] >- + metis_tac[prime_power_eqn, prime_power_cofactor_coprime] >> + qabbrev_tac `n = p ** k * q` >> + `p ** k divides n` by metis_tac[divides_def, MULT_COMM] >> + `k <= ppidx n` by rw[GSYM prime_power_divisibility] >> + `ppidx n <= k` by + (spose_not_then strip_assume_tac >> + `k < ppidx n /\ 0 < ppidx n - k` by decide_tac >> + `p ** ppidx n = p ** k * p ** (ppidx n - k)` by rw[EXP_BY_ADD_SUB_LT] >> + qabbrev_tac `d = ppidx n - k` >> + qabbrev_tac `nq = n DIV p ** (ppidx n)` >> + `n = p ** (ppidx n) * nq` by rw[prime_power_eqn, Abbr`nq`] >> + `0 < p ** k` by rw[PRIME_POS, EXP_POS] >> + `q = p ** d * nq` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC, NOT_ZERO_LT_ZERO] >> + `p divides p ** d` by rw[prime_divides_self_power] >> + `p ** d divides q` by metis_tac[divides_def, MULT_COMM] >> + `p divides q` by metis_tac[DIVIDES_TRANS] >> + `gcd p q = p` by rw[GSYM divides_iff_gcd_fix] >> + metis_tac[NOT_PRIME_1]) >> + decide_tac); + +(* Theorem: prime p ==> (ppidx 1 = 0) *) +(* Proof: + Note 1 = p ** 0 * 1 by EXP, MULT_RIGHT_1 + and coprime p 1 by GCD_1 + so ppidx 1 = 0 by prime_power_index_test, 0 < 1 +*) +val prime_power_index_1 = store_thm( + "prime_power_index_1", + ``!p. prime p ==> (ppidx 1 = 0)``, + rpt strip_tac >> + `1 = p ** 0 * 1` by rw[] >> + `coprime p 1` by rw[GCD_1] >> + metis_tac[prime_power_index_test, DECIDE``0 < 1``]); + +(* Theorem: 0 < n /\ prime p /\ ~(p divides n) ==> (ppidx n = 0) *) +(* Proof: + By contradiction, suppose ppidx n <> 0. + Then 0 < ppidx n by NOT_ZERO_LT_ZERO + Note p ** ppidx n divides n by prime_power_index_def, 0 < n + and 1 < p by ONE_LT_PRIME + so p divides p ** ppidx n by divides_self_power, 0 < n, 1 < p + ==> p divides n by DIVIDES_TRANS + This contradicts ~(p divides n). +*) +val prime_power_index_eq_0 = store_thm( + "prime_power_index_eq_0", + ``!n p. 0 < n /\ prime p /\ ~(p divides n) ==> (ppidx n = 0)``, + spose_not_then strip_assume_tac >> + `p ** ppidx n divides n` by rw[prime_power_index_def] >> + `p divides p ** ppidx n` by rw[divides_self_power, ONE_LT_PRIME] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: prime p ==> (ppidx (p ** k) = k) *) +(* Proof: + Note p ** k = p ** k * 1 by EXP, MULT_RIGHT_1 + and coprime p 1 by GCD_1 + Now 0 < p ** k by PRIME_POS, EXP_POS + so ppidx (p ** k) = k by prime_power_index_test, 0 < p ** k +*) +val prime_power_index_prime_power = store_thm( + "prime_power_index_prime_power", + ``!p. prime p ==> !k. ppidx (p ** k) = k``, + rpt strip_tac >> + `p ** k = p ** k * 1` by rw[] >> + `coprime p 1` by rw[GCD_1] >> + `0 < p ** k` by rw[PRIME_POS, EXP_POS] >> + metis_tac[prime_power_index_test]); + +(* Theorem: prime p ==> (ppidx p = 1) *) +(* Proof: + Note 0 < p by PRIME_POS + and p = p ** 1 * 1 by EXP_1, MULT_RIGHT_1 + and coprime p 1 by GCD_1 + so ppidx p = 1 by prime_power_index_test +*) +val prime_power_index_prime = store_thm( + "prime_power_index_prime", + ``!p. prime p ==> (ppidx p = 1)``, + rpt strip_tac >> + `0 < p` by rw[PRIME_POS] >> + `p = p ** 1 * 1` by rw[] >> + metis_tac[prime_power_index_test, GCD_1]); + +(* Theorem: 0 < n /\ prime p ==> let q = n DIV (p ** ppidx n) in (n = p ** ppidx n * q) /\ (coprime p q) *) +(* Proof: + This is to show: + (1) n = p ** ppidx n * q + Note p ** ppidx n divides n by prime_power_index_def + Now 0 < p by PRIME_POS + so 0 < p ** ppidx n by EXP_POS + ==> n = p ** ppidx n * q by DIVIDES_EQN_COMM, 0 < p ** ppidx n + (2) coprime p q, true by prime_power_index_def +*) +val prime_power_index_eqn = store_thm( + "prime_power_index_eqn", + ``!n p. 0 < n /\ prime p ==> let q = n DIV (p ** ppidx n) in (n = p ** ppidx n * q) /\ (coprime p q)``, + metis_tac[prime_power_index_def, PRIME_POS, EXP_POS, DIVIDES_EQN_COMM]); + +(* Theorem: 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n *) +(* Proof: + By contradiction, suppose ~(0 < ppidx n). + Then ppidx n = 0 by NOT_ZERO_LT_ZERO + Note ?q. coprime p q /\ + n = p ** ppidx n * q by prime_power_index_eqn + = p ** 0 * q by ppidx n = 0 + = 1 * q by EXP_0 + = q by MULT_LEFT_1 + But 1 < p by ONE_LT_PRIME + and coprime p q ==> ~(p divides q) by coprime_not_divides + This contradicts p divides q by p divides n, n = q +*) +val prime_power_index_pos = store_thm( + "prime_power_index_pos", + ``!n p. 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n``, + spose_not_then strip_assume_tac >> + `ppidx n = 0` by decide_tac >> + `?q. coprime p q /\ (n = p ** ppidx n * q)` by metis_tac[prime_power_index_eqn] >> + `_ = q` by rw[] >> + metis_tac[coprime_not_divides, ONE_LT_PRIME]); + +(* ------------------------------------------------------------------------- *) +(* Prime Power and GCD, LCM *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> + (gcd a b = p ** MIN (ppidx a) (ppidx b) * gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b))) *) +(* Proof: + Let ma = ppidx a, qa = a DIV p ** ma. + Let mb = ppidx b, qb = b DIV p ** mb. + Then coprime p qa by prime_power_cofactor_coprime + and coprime p qb by prime_power_cofactor_coprime + Also a = p ** ma * qa by prime_power_eqn + and b = p ** mb * qb by prime_power_eqn + + If ma < mb, let d = mb - ma. + Then p ** mb = p ** ma * p ** d by EXP_BY_ADD_SUB_LT + and coprime (p ** d) qa by coprime_exp + gcd a b + = p ** ma * gcd qa (p ** d * qb) by GCD_COMMON_FACTOR, MULT_ASSOC + = p ** ma * gcd qa qb by gcd_coprime_cancel, GCD_SYM, coprime (p ** d) qa + = p ** (MIN ma mb) * gcd qa qb by MIN_DEF + + If ~(ma < mb), let d = ma - mb. + Then p ** ma = p ** mb * p ** d by EXP_BY_ADD_SUB_LE + and coprime (p ** d) qb by coprime_exp + gcd a b + = p ** mb * gcd (p ** d * qa) qb by GCD_COMMON_FACTOR, MULT_ASSOC + = p ** mb * gcd qa qb by gcd_coprime_cancel, coprime (p ** d) qb + = p ** (MIN ma mb) * gcd qa qb by MIN_DEF +*) +val gcd_prime_power_factor = store_thm( + "gcd_prime_power_factor", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> + (gcd a b = p ** MIN (ppidx a) (ppidx b) * gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b)))``, + rpt strip_tac >> + qabbrev_tac `ma = ppidx a` >> + qabbrev_tac `qa = a DIV p ** ma` >> + qabbrev_tac `mb = ppidx b` >> + qabbrev_tac `qb = b DIV p ** mb` >> + `coprime p qa` by rw[prime_power_cofactor_coprime, Abbr`ma`, Abbr`qa`] >> + `coprime p qb` by rw[prime_power_cofactor_coprime, Abbr`mb`, Abbr`qb`] >> + `a = p ** ma * qa` by rw[prime_power_eqn, Abbr`ma`, Abbr`qa`] >> + `b = p ** mb * qb` by rw[prime_power_eqn, Abbr`mb`, Abbr`qb`] >> + Cases_on `ma < mb` >| [ + `p ** mb = p ** ma * p ** (mb - ma)` by rw[EXP_BY_ADD_SUB_LT] >> + qabbrev_tac `d = mb - ma` >> + `coprime (p ** d) qa` by rw[coprime_exp] >> + `gcd a b = p ** ma * gcd qa (p ** d * qb)` by metis_tac[GCD_COMMON_FACTOR, MULT_ASSOC] >> + `_ = p ** ma * gcd qa qb` by metis_tac[gcd_coprime_cancel, GCD_SYM] >> + rw[MIN_DEF], + `p ** ma = p ** mb * p ** (ma - mb)` by rw[EXP_BY_ADD_SUB_LE] >> + qabbrev_tac `d = ma - mb` >> + `coprime (p ** d) qb` by rw[coprime_exp] >> + `gcd a b = p ** mb * gcd (p ** d * qa) qb` by metis_tac[GCD_COMMON_FACTOR, MULT_ASSOC] >> + `_ = p ** mb * gcd qa qb` by rw[gcd_coprime_cancel] >> + rw[MIN_DEF] + ]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (p ** MIN (ppidx a) (ppidx b)) divides (gcd a b) *) +(* Proof: by gcd_prime_power_factor, divides_def *) +val gcd_prime_power_factor_divides_gcd = store_thm( + "gcd_prime_power_factor_divides_gcd", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (p ** MIN (ppidx a) (ppidx b)) divides (gcd a b)``, + prove_tac[gcd_prime_power_factor, divides_def, MULT_COMM]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> coprime p (gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b))) *) +(* Proof: + Let ma = ppidx a, qa = a DIV p ** ma. + Let mb = ppidx b, qb = b DIV p ** mb. + Then coprime p qa by prime_power_cofactor_coprime + gcd p (gcd qa qb) + = gcd (gcd p qa) qb by GCD_ASSOC + = gcd 1 qb by coprime p qa + = 1 by GCD_1 +*) +val gcd_prime_power_cofactor_coprime = store_thm( + "gcd_prime_power_cofactor_coprime", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> coprime p (gcd (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b)))``, + rw[prime_power_cofactor_coprime, GCD_ASSOC, GCD_1]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (ppidx (gcd a b) = MIN (ppidx a) (ppidx b)) *) +(* Proof: + Let ma = ppidx a, qa = a DIV p ** ma. + Let mb = ppidx b, qb = b DIV p ** mb. + Let m = MIN ma mb. + Then gcd a b = p ** m * (gcd qa qb) by gcd_prime_power_factor + Note 0 < gcd a b by GCD_POS + and coprime p (gcd qa qb) by gcd_prime_power_cofactor_coprime + Thus ppidx (gcd a b) = m by prime_power_index_test +*) +val gcd_prime_power_index = store_thm( + "gcd_prime_power_index", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (ppidx (gcd a b) = MIN (ppidx a) (ppidx b))``, + metis_tac[gcd_prime_power_factor, GCD_POS, prime_power_index_test, gcd_prime_power_cofactor_coprime]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides gcd a b ==> k <= MIN (ppidx a) (ppidx b) *) +(* Proof: + Note 0 < gcd a b by GCD_POS + Thus k <= ppidx (gcd a b) by prime_power_divisibility + or k <= MIN (ppidx a) (ppidx b) by gcd_prime_power_index +*) +val gcd_prime_power_divisibility = store_thm( + "gcd_prime_power_divisibility", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides gcd a b ==> k <= MIN (ppidx a) (ppidx b)``, + metis_tac[GCD_POS, prime_power_divisibility, gcd_prime_power_index]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> + (lcm a b = p ** MAX (ppidx a) (ppidx b) * lcm (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b))) *) +(* Proof: + Let ma = ppidx a, qa = a DIV p ** ma. + Let mb = ppidx b, qb = b DIV p ** mb. + Then coprime p qa by prime_power_cofactor_coprime + and coprime p qb by prime_power_cofactor_coprime + Also a = p ** ma * qa by prime_power_eqn + and b = p ** mb * qb by prime_power_eqn + Note (gcd a b) * (lcm a b) = a * b by GCD_LCM + and gcd qa qb <> 0 by GCD_EQ_0, MULT_0, 0 < a, 0 < b. + + If ma < mb, + Then gcd a b = p ** ma * gcd qa qb by gcd_prime_power_factor, MIN_DEF + and a * b = (p ** ma * qa) * (p ** mb * qb) by above + Note p ** ma <> 0 by MULT, 0 < a = p ** ma * qa + gcd qa qb * lcm a b + = qa * (p ** mb * qb) by MULT_LEFT_CANCEL, MULT_ASSOC + = qa * qb * (p ** mb) by MULT_ASSOC_COMM + = gcd qa qb * lcm qa qb * (p ** mb) by GCD_LCM + Thus lcm a b = lcm qa qb * p ** mb by MULT_LEFT_CANCEL, MULT_ASSOC + = p ** mb * lcm qa qb by MULT_COMM + = p ** (MAX ma mb) * lcm qa qb by MAX_DEF + + If ~(ma < mb), + Then gcd a b = p ** mb * gcd qa qb by gcd_prime_power_factor, MIN_DEF + and a * b = (p ** mb * qb) * (p ** ma * qa) by MULT_COMM + Note p ** mb <> 0 by MULT, 0 < b = p ** mb * qb + gcd qa qb * lcm a b + = qb * (p ** ma * qa) by MULT_LEFT_CANCEL, MULT_ASSOC + = qa * qb * (p ** ma) by MULT_ASSOC_COMM, MULT_COMM + = gcd qa qb * lcm qa qb * (p ** ma) by GCD_LCM + Thus lcm a b = lcm qa qb * p ** ma by MULT_LEFT_CANCEL, MULT_ASSOC + = p ** ma * lcm qa qb by MULT_COMM + = p ** (MAX ma mb) * lcm qa qb by MAX_DEF +*) +val lcm_prime_power_factor = store_thm( + "lcm_prime_power_factor", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> + (lcm a b = p ** MAX (ppidx a) (ppidx b) * lcm (a DIV p ** (ppidx a)) (b DIV p ** (ppidx b)))``, + rpt strip_tac >> + qabbrev_tac `ma = ppidx a` >> + qabbrev_tac `qa = a DIV p ** ma` >> + qabbrev_tac `mb = ppidx b` >> + qabbrev_tac `qb = b DIV p ** mb` >> + `coprime p qa` by rw[prime_power_cofactor_coprime, Abbr`ma`, Abbr`qa`] >> + `coprime p qb` by rw[prime_power_cofactor_coprime, Abbr`mb`, Abbr`qb`] >> + `a = p ** ma * qa` by rw[prime_power_eqn, Abbr`ma`, Abbr`qa`] >> + `b = p ** mb * qb` by rw[prime_power_eqn, Abbr`mb`, Abbr`qb`] >> + `(gcd a b) * (lcm a b) = a * b` by rw[GCD_LCM] >> + `gcd qa qb <> 0` by metis_tac[GCD_EQ_0, MULT_0, NOT_ZERO_LT_ZERO] >> + Cases_on `ma < mb` >| [ + `gcd a b = p ** ma * gcd qa qb` by metis_tac[gcd_prime_power_factor, MIN_DEF] >> + `a * b = (p ** ma * qa) * (p ** mb * qb)` by rw[] >> + `p ** ma <> 0` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> + `gcd qa qb * lcm a b = qa * (p ** mb * qb)` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> + `_ = qa * qb * (p ** mb)` by rw[MULT_ASSOC_COMM] >> + `_ = gcd qa qb * lcm qa qb * (p ** mb)` by metis_tac[GCD_LCM] >> + `lcm a b = lcm qa qb * p ** mb` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> + rw[MAX_DEF, Once MULT_COMM], + `gcd a b = p ** mb * gcd qa qb` by metis_tac[gcd_prime_power_factor, MIN_DEF] >> + `a * b = (p ** mb * qb) * (p ** ma * qa)` by rw[Once MULT_COMM] >> + `p ** mb <> 0` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> + `gcd qa qb * lcm a b = qb * (p ** ma * qa)` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> + `_ = qa * qb * (p ** ma)` by rw[MULT_ASSOC_COMM, Once MULT_COMM] >> + `_ = gcd qa qb * lcm qa qb * (p ** ma)` by metis_tac[GCD_LCM] >> + `lcm a b = lcm qa qb * p ** ma` by metis_tac[MULT_LEFT_CANCEL, MULT_ASSOC] >> + rw[MAX_DEF, Once MULT_COMM] + ]); + +(* The following is the two-number version of prime_power_factor_divides *) + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (p ** MAX (ppidx a) (ppidx b)) divides (lcm a b) *) +(* Proof: by lcm_prime_power_factor, divides_def *) +val lcm_prime_power_factor_divides_lcm = store_thm( + "lcm_prime_power_factor_divides_lcm", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (p ** MAX (ppidx a) (ppidx b)) divides (lcm a b)``, + prove_tac[lcm_prime_power_factor, divides_def, MULT_COMM]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> coprime p (lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b)) *) +(* Proof: + Let ma = ppidx a, qa = a DIV p ** ma. + Let mb = ppidx b, qb = b DIV p ** mb. + Then coprime p qa by prime_power_cofactor_coprime + and coprime p qb by prime_power_cofactor_coprime + + Simple if we have: gcd_over_lcm: gcd x (lcm y z) = lcm (gcd x y) (gcd x z) + But we don't, so use another approach. + + Note 1 < p by ONE_LT_PRIME + Let d = gcd p (lcm qa qb). + By contradiction, assume d <> 1. + Note d divides p by GCD_IS_GREATEST_COMMON_DIVISOR + so d = p by prime_def, d <> 1 + or p divides lcm qa qb by divides_iff_gcd_fix, gcd p (lcm qa qb) = d = p + But (lcm qa qb) divides (qa * qb) by GCD_LCM, divides_def + so p divides qa * qb by DIVIDES_TRANS + ==> p divides qa or p divides qb by P_EUCLIDES + This contradicts coprime p qa + and coprime p qb by coprime_not_divides, 1 < p +*) +val lcm_prime_power_cofactor_coprime = store_thm( + "lcm_prime_power_cofactor_coprime", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> coprime p (lcm (a DIV p ** ppidx a) (b DIV p ** ppidx b))``, + rpt strip_tac >> + qabbrev_tac `ma = ppidx a` >> + qabbrev_tac `mb = ppidx b` >> + qabbrev_tac `qa = a DIV p ** ma` >> + qabbrev_tac `qb = b DIV p ** mb` >> + `coprime p qa` by rw[prime_power_cofactor_coprime, Abbr`ma`, Abbr`qa`] >> + `coprime p qb` by rw[prime_power_cofactor_coprime, Abbr`mb`, Abbr`qb`] >> + spose_not_then strip_assume_tac >> + qabbrev_tac `d = gcd p (lcm qa qb)` >> + `d divides p` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> + `d = p` by metis_tac[prime_def] >> + `p divides lcm qa qb` by rw[divides_iff_gcd_fix, Abbr`d`] >> + `(lcm qa qb) divides (qa * qb)` by metis_tac[GCD_LCM, divides_def] >> + `p divides qa * qb` by metis_tac[DIVIDES_TRANS] >> + `1 < p` by rw[ONE_LT_PRIME] >> + metis_tac[P_EUCLIDES, coprime_not_divides]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> (ppidx (lcm a b) = MAX (ppidx a) (ppidx b)) *) +(* Proof: + Let ma = ppidx a, qa = a DIV p ** ma. + Let mb = ppidx b, qb = b DIV p ** mb. + Let m = MAX ma mb. + Then lcm a b = p ** m * (lcm qa qb) by lcm_prime_power_factor + Note 0 < lcm a b by LCM_POS + and coprime p (lcm qa qb) by lcm_prime_power_cofactor_coprime + so ppidx (lcm a b) = m by prime_power_index_test +*) +val lcm_prime_power_index = store_thm( + "lcm_prime_power_index", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> (ppidx (lcm a b) = MAX (ppidx a) (ppidx b))``, + metis_tac[lcm_prime_power_factor, LCM_POS, lcm_prime_power_cofactor_coprime, prime_power_index_test]); + +(* Theorem: 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides lcm a b ==> k <= MAX (ppidx a) (ppidx b) *) +(* Proof: + Note 0 < lcm a b by LCM_POS + so k <= ppidx (lcm a b) by prime_power_divisibility + or k <= MAX (ppidx a) (ppidx b) by lcm_prime_power_index +*) +val lcm_prime_power_divisibility = store_thm( + "lcm_prime_power_divisibility", + ``!a b p. 0 < a /\ 0 < b /\ prime p ==> !k. p ** k divides lcm a b ==> k <= MAX (ppidx a) (ppidx b)``, + metis_tac[LCM_POS, prime_power_divisibility, lcm_prime_power_index]); + +(* ------------------------------------------------------------------------- *) +(* Prime Powers and List LCM *) +(* ------------------------------------------------------------------------- *) + +(* +If a prime-power divides a list_lcm, the prime-power must divides some element in the list for list_lcm. +Note: this is not true for non-prime-power. +*) + +(* Theorem: prime p ==> p ** (MAX_LIST (MAP (ppidx) l)) divides list_lcm l *) +(* Proof: + If l = [], + p ** MAX_LIST (MAP ppidx []) + = p ** MAX_LIST [] by MAP + = p ** 0 by MAX_LIST_NIL + = 1 + Hence true by ONE_DIVIDES_ALL + In fact, list_lcm [] = 1 by list_lcm_nil + If l <> [], + Let ml = MAP ppidx l. + Then ml <> [] by MAP_EQ_NIL + ==> MEM (MAX_LIST ml) ml by MAX_LIST_MEM, ml <> [] + so ?x. (MAX_LIST ml = ppidx x) /\ MEM x l by MEM_MAP + Thus p ** ppidx x divides x by prime_power_factor_divides + Now x divides list_lcm l by list_lcm_is_common_multiple + so p ** (ppidx x) + = p ** (MAX_LIST ml) divides list_lcm l by DIVIDES_TRANS +*) +val list_lcm_prime_power_factor_divides = store_thm( + "list_lcm_prime_power_factor_divides", + ``!l p. prime p ==> p ** (MAX_LIST (MAP (ppidx) l)) divides list_lcm l``, + rpt strip_tac >> + Cases_on `l = []` >- + rw[MAX_LIST_NIL] >> + qabbrev_tac `ml = MAP ppidx l` >> + `ml <> []` by rw[Abbr`ml`] >> + `MEM (MAX_LIST ml) ml` by rw[MAX_LIST_MEM] >> + `?x. (MAX_LIST ml = ppidx x) /\ MEM x l` by metis_tac[MEM_MAP] >> + `p ** ppidx x divides x` by rw[prime_power_factor_divides] >> + `x divides list_lcm l` by rw[list_lcm_is_common_multiple] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: prime p /\ POSITIVE l ==> (ppidx (list_lcm l) = MAX_LIST (MAP ppidx l)) *) +(* Proof: + By induction on l. + Base: ppidx (list_lcm []) = MAX_LIST (MAP ppidx []) + ppidx (list_lcm []) + = ppidx 1 by list_lcm_nil + = 0 by prime_power_index_1 + = MAX_LIST [] by MAX_LIST_NIL + = MAX_LIST (MAP ppidx []) by MAP + + Step: ppidx (list_lcm l) = MAX_LIST (MAP ppidx l) ==> + ppidx (list_lcm (h::l)) = MAX_LIST (MAP ppidx (h::l)) + Note 0 < list_lcm l by list_lcm_pos, EVERY_MEM + ppidx (list_lcm (h::l)) + = ppidx (lcm h (list_lcm l)) by list_lcm_cons + = MAX (ppidx h) (ppidx (list_lcm l)) by lcm_prime_power_index, 0 < list_lcm l + = MAX (ppidx h) (MAX_LIST (MAP ppidx l)) by induction hypothesis + = MAX_LIST (ppidx h :: MAP ppidx l) by MAX_LIST_CONS + = MAX_LIST (MAP ppidx (h::l)) by MAP +*) +val list_lcm_prime_power_index = store_thm( + "list_lcm_prime_power_index", + ``!l p. prime p /\ POSITIVE l ==> (ppidx (list_lcm l) = MAX_LIST (MAP ppidx l))``, + Induct >- + rw[prime_power_index_1] >> + rpt strip_tac >> + `0 < list_lcm l` by rw[list_lcm_pos, EVERY_MEM] >> + `ppidx (list_lcm (h::l)) = ppidx (lcm h (list_lcm l))` by rw[list_lcm_cons] >> + `_ = MAX (ppidx h) (ppidx (list_lcm l))` by rw[lcm_prime_power_index] >> + `_ = MAX (ppidx h) (MAX_LIST (MAP ppidx l))` by rw[] >> + `_ = MAX_LIST (ppidx h :: MAP ppidx l)` by rw[MAX_LIST_CONS] >> + `_ = MAX_LIST (MAP ppidx (h::l))` by rw[] >> + rw[]); + +(* Theorem: prime p /\ POSITIVE l ==> + !k. p ** k divides list_lcm l ==> k <= MAX_LIST (MAP ppidx l) *) +(* Proof: + Note 0 < list_lcm l by list_lcm_pos, EVERY_MEM + so k <= ppidx (list_lcm l) by prime_power_divisibility + or k <= MAX_LIST (MAP ppidx l) by list_lcm_prime_power_index +*) +val list_lcm_prime_power_divisibility = store_thm( + "list_lcm_prime_power_divisibility", + ``!l p. prime p /\ POSITIVE l ==> + !k. p ** k divides list_lcm l ==> k <= MAX_LIST (MAP ppidx l)``, + rpt strip_tac >> + `0 < list_lcm l` by rw[list_lcm_pos, EVERY_MEM] >> + metis_tac[prime_power_divisibility, list_lcm_prime_power_index]); + +(* Theorem: prime p /\ l <> [] /\ POSITIVE l ==> + !k. p ** k divides list_lcm l ==> ?x. MEM x l /\ p ** k divides x *) +(* Proof: + Let ml = MAP ppidx l. + + Step 1: Get member x that attains ppidx x = MAX_LIST ml + Note ml <> [] by MAP_EQ_NIL + Then MEM (MAX_LIST ml) ml by MAX_LIST_MEM, ml <> [] + ==> ?x. (MAX_LIST ml = ppidx x) /\ MEM x l by MEM_MAP + + Step 2: Show that this is a suitable x + Note p ** k divides list_lcm l by given + ==> k <= MAX_LIST ml by list_lcm_prime_power_divisibility + Now 1 < p by ONE_LT_PRIME + so p ** k divides p ** (MAX_LIST ml) by power_divides_iff, 1 < p + and p ** (ppidx x) divides x by prime_power_factor_divides + Thus p ** k divides x by DIVIDES_TRANS + + Take this x, and the result follows. +*) +val list_lcm_prime_power_factor_member = store_thm( + "list_lcm_prime_power_factor_member", + ``!l p. prime p /\ l <> [] /\ POSITIVE l ==> + !k. p ** k divides list_lcm l ==> ?x. MEM x l /\ p ** k divides x``, + rpt strip_tac >> + qabbrev_tac `ml = MAP ppidx l` >> + `ml <> []` by rw[Abbr`ml`] >> + `MEM (MAX_LIST ml) ml` by rw[MAX_LIST_MEM] >> + `?x. (MAX_LIST ml = ppidx x) /\ MEM x l` by metis_tac[MEM_MAP] >> + `k <= MAX_LIST ml` by rw[list_lcm_prime_power_divisibility, Abbr`ml`] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `p ** k divides p ** (MAX_LIST ml)` by rw[power_divides_iff] >> + `p ** (ppidx x) divides x` by rw[prime_power_factor_divides] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: prime p ==> !m n. (n = p ** SUC (ppidx m)) ==> (lcm n m = p * m) *) +(* Proof: + If m = 0, + lcm n 0 = 0 by LCM_0 + = p * 0 by MULT_0 + If m <> 0, then 0 < m. + Note 0 < n by PRIME_POS, EXP_POS + Let nq = n DIV p ** (ppidx n), mq = m DIV p ** (ppidx m). + Let k = ppidx m. + Note ppidx n = SUC k by prime_power_index_prime_power + and nq = 1 by DIVMOD_ID + Now MAX (ppidx n) (ppidx m) + = MAX (SUC k) k by above + = SUC k by MAX_DEF + + lcm n m + = p ** MAX (ppidx n) (ppidx m) * (lcm nq mq) by lcm_prime_power_factor + = p ** (SUC k) * (lcm 1 mq) by above + = p ** (SUC k) * mq by LCM_1 + = p * p ** k * mq by EXP + = p * (p ** k * mq) by MULT_ASSOC + = p * m by prime_power_eqn +*) +val lcm_special_for_prime_power = store_thm( + "lcm_special_for_prime_power", + ``!p. prime p ==> !m n. (n = p ** SUC (ppidx m)) ==> (lcm n m = p * m)``, + rpt strip_tac >> + Cases_on `m = 0` >- + rw[] >> + `0 < m` by decide_tac >> + `0 < n` by rw[PRIME_POS, EXP_POS] >> + qabbrev_tac `k = ppidx m` >> + `ppidx n = SUC k` by rw[prime_power_index_prime_power] >> + `MAX (SUC k) k = SUC k` by rw[MAX_DEF] >> + qabbrev_tac `mq = m DIV p ** (ppidx m)` >> + qabbrev_tac `nq = n DIV p ** (ppidx n)` >> + `nq = 1` by rw[DIVMOD_ID, Abbr`nq`] >> + `lcm n m = p ** (SUC k) * (lcm nq mq)` by metis_tac[lcm_prime_power_factor] >> + metis_tac[LCM_1, EXP, MULT_ASSOC, prime_power_eqn]); + +(* Theorem: (n = a * b) /\ coprime a b ==> !m. a divides m /\ b divides m ==> (lcm n m = m) *) +(* Proof: + If n = 0, + Then a * b = 0 ==> a = 0 or b = 0 by MULT_EQ_0 + so a divides m /\ b divides m ==> m = 0 by ZERO_DIVIDES + Since lcm 0 m = 0 by LCM_0 + so lcm n m = m by above + If n <> 0, + Note (a * b) divides m by coprime_product_divides + or n divides m by n = a * b + so lcm n m = m by divides_iff_lcm_fix +*) +Theorem lcm_special_for_coprime_factors: + !n a b. n = a * b /\ coprime a b ==> + !m. a divides m /\ b divides m ==> lcm n m = m +Proof + rpt strip_tac >> Cases_on `n = 0` >| [ + `m = 0` by metis_tac[MULT_EQ_0, ZERO_DIVIDES] >> + simp[LCM_0], + `n divides m` by rw[coprime_product_divides] >> + rw[GSYM divides_iff_lcm_fix] + ] +QED + +(* ------------------------------------------------------------------------- *) +(* Prime Divisors *) +(* ------------------------------------------------------------------------- *) + +(* Define the prime divisors of a number *) +val prime_divisors_def = zDefine` + prime_divisors n = {p | prime p /\ p divides n} +`; +(* use zDefine as this is not effective. *) + +(* Theorem: p IN prime_divisors n <=> prime p /\ p divides n *) +(* Proof: by prime_divisors_def *) +val prime_divisors_element = store_thm( + "prime_divisors_element", + ``!n p. p IN prime_divisors n <=> prime p /\ p divides n``, + rw[prime_divisors_def]); + +(* Theorem: 0 < n ==> (prime_divisors n) SUBSET (natural n) *) +(* Proof: + By prime_divisors_element, SUBSET_DEF, + this is to show: ?x'. (x = SUC x') /\ x' < n + Note prime x /\ x divides n + ==> 0 < x /\ x <= n by PRIME_POS, DIVIDES_LE, 0 < n + ==> 0 < x /\ PRE x < n by arithmetic + Take x' = PRE x, + Then SUC x' = SUC (PRE x) = x by SUC_PRE, 0 < x +*) +val prime_divisors_subset_natural = store_thm( + "prime_divisors_subset_natural", + ``!n. 0 < n ==> (prime_divisors n) SUBSET (natural n)``, + rw[prime_divisors_element, SUBSET_DEF] >> + `x <= n` by rw[DIVIDES_LE] >> + `PRE x < n` by decide_tac >> + `0 < x` by rw[PRIME_POS] >> + metis_tac[SUC_PRE]); + +(* Theorem: 0 < n ==> FINITE (prime_divisors n) *) +(* Proof: + Note (prime_divisors n) SUBSET (natural n) by prime_divisors_subset_natural, 0 < n + and FINITE (natural n) by natural_finite + so FINITE (prime_divisors n) by SUBSET_FINITE +*) +val prime_divisors_finite = store_thm( + "prime_divisors_finite", + ``!n. 0 < n ==> FINITE (prime_divisors n)``, + metis_tac[prime_divisors_subset_natural, natural_finite, SUBSET_FINITE]); + +(* Theorem: prime_divisors 0 = {p | prime p} *) +(* Proof: by prime_divisors_def, ALL_DIVIDES_0 *) +Theorem prime_divisors_0: prime_divisors 0 = {p | prime p} +Proof rw[prime_divisors_def] +QED + +(* Note: prime: num -> bool is also a set, so prime = {p | prime p} *) + +(* Theorem: prime_divisors n = {} *) +(* Proof: by prime_divisors_def, DIVIDES_ONE, NOT_PRIME_1 *) +val prime_divisors_1 = store_thm( + "prime_divisors_1", + ``prime_divisors 1 = {}``, + rw[prime_divisors_def, EXTENSION]); + +(* Theorem: (prime_divisors n) SUBSET prime *) +(* Proof: by prime_divisors_element, SUBSET_DEF, IN_DEF *) +val prime_divisors_subset_prime = store_thm( + "prime_divisors_subset_prime", + ``!n. (prime_divisors n) SUBSET prime``, + rw[prime_divisors_element, SUBSET_DEF, IN_DEF]); + +(* Theorem: 1 < n ==> prime_divisors n <> {} *) +(* Proof: + Note n <> 1 by 1 < n + so ?p. prime p /\ p divides n by PRIME_FACTOR + or p IN prime_divisors n by prime_divisors_element + ==> prime_divisors n <> {} by MEMBER_NOT_EMPTY +*) +val prime_divisors_nonempty = store_thm( + "prime_divisors_nonempty", + ``!n. 1 < n ==> prime_divisors n <> {}``, + metis_tac[PRIME_FACTOR, prime_divisors_element, MEMBER_NOT_EMPTY, DECIDE``1 < n ==> n <> 1``]); + +(* Theorem: (prime_divisors n = {}) <=> (n = 1) *) +(* Proof: by prime_divisors_def, DIVIDES_ONE, NOT_PRIME_1, PRIME_FACTOR *) +val prime_divisors_empty_iff = store_thm( + "prime_divisors_empty_iff", + ``!n. (prime_divisors n = {}) <=> (n = 1)``, + rw[prime_divisors_def, EXTENSION] >> + metis_tac[DIVIDES_ONE, NOT_PRIME_1, PRIME_FACTOR]); + +(* Theorem: ~ SING (prime_divisors 0) *) +(* Proof: + Let s = prime_divisors 0. + By contradiction, suppose SING s. + Note prime 2 by PRIME_2 + and prime 3 by PRIME_3 + so 2 IN s /\ 3 IN s by prime_divisors_0 + This contradicts SING s by SING_ELEMENT +*) +val prime_divisors_0_not_sing = store_thm( + "prime_divisors_0_not_sing", + ``~ SING (prime_divisors 0)``, + rpt strip_tac >> + qabbrev_tac `s = prime_divisors 0` >> + `2 IN s /\ 3 IN s` by rw[PRIME_2, PRIME_3, prime_divisors_0, Abbr`s`] >> + metis_tac[SING_ELEMENT, DECIDE``2 <> 3``]); + +(* Theorem: prime n ==> (prime_divisors n = {n}) *) +(* Proof: + By prime_divisors_def, EXTENSION, this is to show: + prime x /\ x divides n <=> (x = n) + This is true by prime_divides_prime +*) +val prime_divisors_prime = store_thm( + "prime_divisors_prime", + ``!n. prime n ==> (prime_divisors n = {n})``, + rw[prime_divisors_def, EXTENSION] >> + metis_tac[prime_divides_prime]); + +(* Theorem: prime n ==> (prime_divisors n = {n}) *) +(* Proof: + By prime_divisors_def, EXTENSION, this is to show: + prime x /\ x divides n ** k <=> (x = n) + If part: prime x /\ x divides n ** k ==> (x = n) + This is true by prime_divides_prime_power + Only-if part: prime n /\ 0 < k ==> n divides n ** k + This is true by prime_divides_power, DIVIDES_REFL +*) +val prime_divisors_prime_power = store_thm( + "prime_divisors_prime_power", + ``!n. prime n ==> !k. 0 < k ==> (prime_divisors (n ** k) = {n})``, + rw[prime_divisors_def, EXTENSION] >> + rw[EQ_IMP_THM] >- + metis_tac[prime_divides_prime_power] >> + metis_tac[prime_divides_power, DIVIDES_REFL]); + +(* Theorem: SING (prime_divisors n) <=> ?p k. prime p /\ 0 < k /\ (n = p ** k) *) +(* Proof: + If part: SING (prime_divisors n) ==> ?p k. prime p /\ 0 < k /\ (n = p ** k) + Note n <> 0 by prime_divisors_0_not_sing + Claim: n <> 1 + Proof: By contradiction, suppose n = 1. + Then prime_divisors 1 = {} by prime_divisors_1 + but SING {} = F by SING_EMPTY + + Thus 1 < n by n <> 0, n <> 1 + ==> ?p. prime p /\ p divides n by PRIME_FACTOR + also ?q m. (n = p ** m * q) /\ (coprime p q) by prime_power_factor, 0 < n + Note q <> 0 by MULT_EQ_0 + Claim: q = 1 + Proof: By contradiction, suppose q <> 1. + Then 1 < q by q <> 0, q <> 1 + ==> ?z. prime z /\ z divides q by PRIME_FACTOR + Now 1 < p by ONE_LT_PRIME + so ~(p divides q) by coprime_not_divides, 1 < p, coprime p q + or p <> z by z divides q, but ~(p divides q) + But q divides n by divides_def, n = p ** m * q + Thus z divides n by DIVIDES_TRANS + so p IN (prime_divisors n) by prime_divisors_element + and z IN (prime_divisors n) by prime_divisors_element + This contradicts SING (prime_divisors n) by SING_ELEMENT + + Thus q = 1, + ==> n = p ** m by MULT_RIGHT_1 + and m <> 0 by EXP_0, n <> 1 + Thus take this prime p, and exponent m, and 0 < m by NOT_ZERO_LT_ZERO + + Only-if part: ?p k. prime p /\ 0 < k /\ (n = p ** k) ==> SING (prime_divisors n) + Note (prime_divisors p ** k) = {p} by prime_divisors_prime_power + so SING (prime_divisors n) by SING_DEF +*) +val prime_divisors_sing = store_thm( + "prime_divisors_sing", + ``!n. SING (prime_divisors n) <=> ?p k. prime p /\ 0 < k /\ (n = p ** k)``, + rw[EQ_IMP_THM] >| [ + `n <> 0` by metis_tac[prime_divisors_0_not_sing] >> + `n <> 1` by metis_tac[prime_divisors_1, SING_EMPTY] >> + `0 < n /\ 1 < n` by decide_tac >> + `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> + `?q m. (n = p ** m * q) /\ (coprime p q)` by rw[prime_power_factor] >> + `q <> 0` by metis_tac[MULT_EQ_0] >> + Cases_on `q = 1` >- + metis_tac[MULT_RIGHT_1, EXP_0, NOT_ZERO_LT_ZERO] >> + `?z. prime z /\ z divides q` by rw[PRIME_FACTOR] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `p <> z` by metis_tac[coprime_not_divides] >> + `z divides n` by metis_tac[divides_def, DIVIDES_TRANS] >> + metis_tac[prime_divisors_element, SING_ELEMENT], + metis_tac[prime_divisors_prime_power, SING_DEF] + ]); + +(* Theorem: (prime_divisors n = {p}) <=> ?k. prime p /\ 0 < k /\ (n = p ** k) *) +(* Proof: + If part: prime_divisors n = {p} ==> ?k. prime p /\ 0 < k /\ (n = p ** k) + Note prime p by prime_divisors_element, IN_SING + and SING (prime_divisors n) by SING_DEF + ==> ?q k. prime q /\ 0 < k /\ (n = q ** k) by prime_divisors_sing + Take this k, then q = p by prime_divisors_prime_power, IN_SING + Only-if part: prime p ==> prime_divisors (p ** k) = {p} + This is true by prime_divisors_prime_power +*) +val prime_divisors_sing_alt = store_thm( + "prime_divisors_sing_alt", + ``!n p. (prime_divisors n = {p}) <=> ?k. prime p /\ 0 < k /\ (n = p ** k)``, + metis_tac[prime_divisors_sing, SING_DEF, IN_SING, prime_divisors_element, prime_divisors_prime_power]); + +(* Theorem: SING (prime_divisors n) ==> + let p = CHOICE (prime_divisors n) in prime p /\ (n = p ** ppidx n) *) +(* Proof: + Let s = prime_divisors n. + Note n <> 0 by prime_divisors_0_not_sing + and n <> 1 by prime_divisors_1, SING_EMPTY + ==> s <> {} by prime_divisors_empty_iff, n <> 1 + Note p = CHOICE s IN s by CHOICE_DEF + so prime p /\ p divides n by prime_divisors_element + Thus need only to show: n = p ** ppidx n + Note ?q. (n = p ** ppidx n * q) /\ + coprime p q by prime_power_factor, prime_power_index_test, 0 < n + Claim: q = 1 + Proof: By contradiction, suppose q <> 1. + Note 1 < p by ONE_LT_PRIME, prime p + and q <> 0 by MULT_EQ_0 + ==> ?z. prime z /\ z divides q by PRIME_FACTOR, 1 < q + Note ~(p divides q) by coprime_not_divides, 1 < p + ==> z <> p by z divides q + Also q divides n by divides_def, n = p ** ppidx n * q + ==> z divides n by DIVIDES_TRANS + Thus p IN s /\ z IN s by prime_divisors_element + or p = z, contradicts z <> p by SING_ELEMENT + + Thus q = 1, and n = p ** ppidx n by MULT_RIGHT_1 +*) +val prime_divisors_sing_property = store_thm( + "prime_divisors_sing_property", + ``!n. SING (prime_divisors n) ==> + let p = CHOICE (prime_divisors n) in prime p /\ (n = p ** ppidx n)``, + ntac 2 strip_tac >> + qabbrev_tac `s = prime_divisors n` >> + `n <> 0` by metis_tac[prime_divisors_0_not_sing] >> + `n <> 1` by metis_tac[prime_divisors_1, SING_EMPTY] >> + `s <> {}` by rw[prime_divisors_empty_iff, Abbr`s`] >> + `prime (CHOICE s) /\ (CHOICE s) divides n` by metis_tac[CHOICE_DEF, prime_divisors_element] >> + rw_tac std_ss[] >> + rw[] >> + `0 < n` by decide_tac >> + `?q. (n = p ** ppidx n * q) /\ coprime p q` by metis_tac[prime_power_factor, prime_power_index_test] >> + reverse (Cases_on `q = 1`) >| [ + `q <> 0` by metis_tac[MULT_EQ_0] >> + `?z. prime z /\ z divides q` by rw[PRIME_FACTOR] >> + `z <> p` by metis_tac[coprime_not_divides, ONE_LT_PRIME] >> + `z divides n` by metis_tac[divides_def, DIVIDES_TRANS] >> + metis_tac[prime_divisors_element, SING_ELEMENT], + rw[] + ]); + +(* Theorem: m divides n ==> (prime_divisors m) SUBSET (prime_divisors n) *) +(* Proof: + Note !x. x IN prime_divisors m + ==> prime x /\ x divides m by prime_divisors_element + ==> primx x /\ x divides n by DIVIDES_TRANS + ==> x IN prime_divisors n by prime_divisors_element + or (prime_divisors m) SUBSET (prime_divisors n) by SUBSET_DEF +*) +val prime_divisors_divisor_subset = store_thm( + "prime_divisors_divisor_subset", + ``!m n. m divides n ==> (prime_divisors m) SUBSET (prime_divisors n)``, + rw[prime_divisors_element, SUBSET_DEF] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: x divides m /\ x divides n ==> + (prime_divisors x SUBSET (prime_divisors m) INTER (prime_divisors n)) *) +(* Proof: + By prime_divisors_element, SUBSET_DEF, this is to show: + (1) x' divides x /\ x divides m ==> x' divides m, true by DIVIDES_TRANS + (2) x' divides x /\ x divides n ==> x' divides n, true by DIVIDES_TRANS +*) +val prime_divisors_common_divisor = store_thm( + "prime_divisors_common_divisor", + ``!n m x. x divides m /\ x divides n ==> + (prime_divisors x SUBSET (prime_divisors m) INTER (prime_divisors n))``, + rw[prime_divisors_element, SUBSET_DEF] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: m divides x /\ n divides x ==> + (prime_divisors m UNION prime_divisors n) SUBSET prime_divisors x *) +(* Proof: + By prime_divisors_element, SUBSET_DEF, this is to show: + (1) x' divides m /\ m divides x ==> x' divides x, true by DIVIDES_TRANS + (2) x' divides n /\ n divides x ==> x' divides x, true by DIVIDES_TRANS +*) +val prime_divisors_common_multiple = store_thm( + "prime_divisors_common_multiple", + ``!n m x. m divides x /\ n divides x ==> + (prime_divisors m UNION prime_divisors n) SUBSET prime_divisors x``, + rw[prime_divisors_element, SUBSET_DEF] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: 0 < m /\ 0 < n /\ x divides m /\ x divides n ==> + !p. prime p ==> ppidx x <= MIN (ppidx m) (ppidx n) *) +(* Proof: + Note ppidx x <= ppidx m by prime_power_index_of_divisor, 0 < m + and ppidx x <= ppidx n by prime_power_index_of_divisor, 0 < n + ==> ppidx x <= MIN (ppidx m) (ppidx n) by MIN_LE +*) +val prime_power_index_common_divisor = store_thm( + "prime_power_index_common_divisor", + ``!n m x. 0 < m /\ 0 < n /\ x divides m /\ x divides n ==> + !p. prime p ==> ppidx x <= MIN (ppidx m) (ppidx n)``, + rw[MIN_LE, prime_power_index_of_divisor]); + +(* Theorem: 0 < x /\ m divides x /\ n divides x ==> + !p. prime p ==> MAX (ppidx m) (ppidx n) <= ppidx x *) +(* Proof: + Note ppidx m <= ppidx x by prime_power_index_of_divisor, 0 < x + and ppidx n <= ppidx x by prime_power_index_of_divisor, 0 < x + ==> MAX (ppidx m) (ppidx n) <= ppidx x by MAX_LE +*) +val prime_power_index_common_multiple = store_thm( + "prime_power_index_common_multiple", + ``!n m x. 0 < x /\ m divides x /\ n divides x ==> + !p. prime p ==> MAX (ppidx m) (ppidx n) <= ppidx x``, + rw[MAX_LE, prime_power_index_of_divisor]); + +(* +prime p = 2, n = 10, LOG 2 10 = 3, but ppidx 10 = 1, since 4 cannot divide 10. +10 = 2^1 * 5^1 +*) + +(* Theorem: 0 < n /\ prime p ==> ppidx n <= LOG p n *) +(* Proof: + By contradiction, suppose LOG p n < ppidx n. + Then SUC (LOG p n) <= ppidx n by arithmetic + Note 1 < p by ONE_LT_PRIME + so p ** (SUC (LOG p n)) divides p ** ppidx n by power_divides_iff, 1 < p + But p ** ppidx n divides n by prime_power_index_def + ==> p ** SUC (LOG p n) divides n by DIVIDES_TRANS + or p ** SUC (LOG p n) <= n by DIVIDES_LE, 0 < n + This contradicts n < p ** SUC (LOG p n) by LOG, 0 < n, 1 < p +*) +val prime_power_index_le_log_index = store_thm( + "prime_power_index_le_log_index", + ``!n p. 0 < n /\ prime p ==> ppidx n <= LOG p n``, + spose_not_then strip_assume_tac >> + `SUC (LOG p n) <= ppidx n` by decide_tac >> + `1 < p` by rw[ONE_LT_PRIME] >> + `p ** (SUC (LOG p n)) divides p ** ppidx n` by rw[power_divides_iff] >> + `p ** ppidx n divides n` by rw[prime_power_index_def] >> + `p ** SUC (LOG p n) divides n` by metis_tac[DIVIDES_TRANS] >> + `p ** SUC (LOG p n) <= n` by rw[DIVIDES_LE] >> + `n < p ** SUC (LOG p n)` by rw[LOG] >> + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Prime-related Sets *) +(* ------------------------------------------------------------------------- *) + +(* +Example: Take n = 10. +primes_upto 10 = {2; 3; 5; 7} +prime_powers_upto 10 = {8; 9; 5; 7} +SET_TO_LIST (prime_powers_upto 10) = [8; 9; 5; 7] +set_lcm (prime_powers_upto 10) = 2520 +lcm_run 10 = 2520 + +Given n, +First get (primes_upto n) = {p | prime p /\ p <= n} +For each prime p, map to p ** LOG p n. + +logroot.LOG |- !a n. 1 < a /\ 0 < n ==> a ** LOG a n <= n /\ n < a ** SUC (LOG a n) +*) + +(* val _ = clear_overloads_on "pd"; in Mobius theory *) +(* open primePowerTheory; *) + +(* +> prime_power_index_def; +val it = |- !p n. 0 < n /\ prime p ==> p ** ppidx n divides n /\ coprime p (n DIV p ** ppidx n): thm +*) + +(* Define the set of primes up to n *) +val primes_upto_def = Define` + primes_upto n = {p | prime p /\ p <= n} +`; + +(* Overload the counts of primes up to n *) +val _ = overload_on("primes_count", ``\n. CARD (primes_upto n)``); + +(* Define the prime powers up to n *) +val prime_powers_upto_def = Define` + prime_powers_upto n = IMAGE (\p. p ** LOG p n) (primes_upto n) +`; + +(* Define the prime power divisors of n *) +val prime_power_divisors_def = Define` + prime_power_divisors n = IMAGE (\p. p ** ppidx n) (prime_divisors n) +`; + +(* Theorem: p IN primes_upto n <=> prime p /\ p <= n *) +(* Proof: by primes_upto_def *) +val primes_upto_element = store_thm( + "primes_upto_element", + ``!n p. p IN primes_upto n <=> prime p /\ p <= n``, + rw[primes_upto_def]); + +(* Theorem: (primes_upto n) SUBSET (natural n) *) +(* Proof: + By primes_upto_def, SUBSET_DEF, + this is to show: prime x /\ x <= n ==> ?x'. (x = SUC x') /\ x' < n + Note 0 < x by PRIME_POS, prime x + so PRE x < n by x <= n + and SUC (PRE x) = x by SUC_PRE, 0 < x + Take x' = PRE x, and the result follows. +*) +val primes_upto_subset_natural = store_thm( + "primes_upto_subset_natural", + ``!n. (primes_upto n) SUBSET (natural n)``, + rw[primes_upto_def, SUBSET_DEF] >> + `0 < x` by rw[PRIME_POS] >> + `PRE x < n` by decide_tac >> + metis_tac[SUC_PRE]); + +(* Theorem: FINITE (primes_upto n) *) +(* Proof: + Note (primes_upto n) SUBSET (natural n) by primes_upto_subset_natural + and FINITE (natural n) by natural_finite + ==> FINITE (primes_upto n) by SUBSET_FINITE +*) +val primes_upto_finite = store_thm( + "primes_upto_finite", + ``!n. FINITE (primes_upto n)``, + metis_tac[primes_upto_subset_natural, natural_finite, SUBSET_FINITE]); + +(* Theorem: PAIRWISE_COPRIME (primes_upto n) *) +(* Proof: + Let s = prime_power_divisors n + This is to show: prime x /\ prime y /\ x <> y ==> coprime x y + This is true by primes_coprime +*) +val primes_upto_pairwise_coprime = store_thm( + "primes_upto_pairwise_coprime", + ``!n. PAIRWISE_COPRIME (primes_upto n)``, + metis_tac[primes_upto_element, primes_coprime]); + +(* Theorem: primes_upto 0 = {} *) +(* Proof: + p IN primes_upto 0 + <=> prime p /\ p <= 0 by primes_upto_element + <=> prime 0 by p <= 0 + <=> F by NOT_PRIME_0 +*) +val primes_upto_0 = store_thm( + "primes_upto_0", + ``primes_upto 0 = {}``, + rw[primes_upto_element, EXTENSION]); + +(* Theorem: primes_count 0 = 0 *) +(* Proof: + primes_count 0 + = CARD (primes_upto 0) by notation + = CARD {} by primes_upto_0 + = 0 by CARD_EMPTY +*) +val primes_count_0 = store_thm( + "primes_count_0", + ``primes_count 0 = 0``, + rw[primes_upto_0]); + +(* Theorem: primes_upto 1 = {} *) +(* Proof: + p IN primes_upto 1 + <=> prime p /\ p <= 1 by primes_upto_element + <=> prime 0 or prime 1 by p <= 1 + <=> F by NOT_PRIME_0, NOT_PRIME_1 +*) +val primes_upto_1 = store_thm( + "primes_upto_1", + ``primes_upto 1 = {}``, + rw[primes_upto_element, EXTENSION, DECIDE``x <= 1 <=> (x = 0) \/ (x = 1)``] >> + metis_tac[NOT_PRIME_0, NOT_PRIME_1]); + +(* Theorem: primes_count 1 = 0 *) +(* Proof: + primes_count 1 + = CARD (primes_upto 1) by notation + = CARD {} by primes_upto_1 + = 0 by CARD_EMPTY +*) +val primes_count_1 = store_thm( + "primes_count_1", + ``primes_count 1 = 0``, + rw[primes_upto_1]); + +(* Theorem: x IN prime_powers_upto n <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n *) +(* Proof: by prime_powers_upto_def, primes_upto_element *) +val prime_powers_upto_element = store_thm( + "prime_powers_upto_element", + ``!n x. x IN prime_powers_upto n <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n``, + rw[prime_powers_upto_def, primes_upto_element]); + +(* Theorem: prime p /\ p <= n ==> (p ** LOG p n) IN (prime_powers_upto n) *) +(* Proof: by prime_powers_upto_element *) +val prime_powers_upto_element_alt = store_thm( + "prime_powers_upto_element_alt", + ``!p n. prime p /\ p <= n ==> (p ** LOG p n) IN (prime_powers_upto n)``, + metis_tac[prime_powers_upto_element]); + +(* Theorem: FINITE (prime_powers_upto n) *) +(* Proof: + Note prime_powers_upto n = + IMAGE (\p. p ** LOG p n) (primes_upto n) by prime_powers_upto_def + and FINITE (primes_upto n) by primes_upto_finite + ==> FINITE (prime_powers_upto n) by IMAGE_FINITE +*) +val prime_powers_upto_finite = store_thm( + "prime_powers_upto_finite", + ``!n. FINITE (prime_powers_upto n)``, + rw[prime_powers_upto_def, primes_upto_finite]); + +(* Theorem: PAIRWISE_COPRIME (prime_powers_upto n) *) +(* Proof: + Let s = prime_power_divisors n + This is to show: x IN s /\ y IN s /\ x <> y ==> coprime x y + Note ?p1. prime p1 /\ (x = p1 ** LOG p1 n) /\ p1 <= n by prime_powers_upto_element + and ?p2. prime p2 /\ (y = p2 ** LOG p2 n) /\ p2 <= n by prime_powers_upto_element + and p1 <> p2 by prime_powers_eq + Thus coprime x y by prime_powers_coprime +*) +val prime_powers_upto_pairwise_coprime = store_thm( + "prime_powers_upto_pairwise_coprime", + ``!n. PAIRWISE_COPRIME (prime_powers_upto n)``, + metis_tac[prime_powers_upto_element, prime_powers_eq, prime_powers_coprime]); + +(* Theorem: prime_powers_upto 0 = {} *) +(* Proof: + x IN prime_powers_upto 0 + <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= 0 by prime_powers_upto_element + <=> ?p. (x = p ** LOG p n) /\ prime 0 by p <= 0 + <=> F by NOT_PRIME_0 +*) +val prime_powers_upto_0 = store_thm( + "prime_powers_upto_0", + ``prime_powers_upto 0 = {}``, + rw[prime_powers_upto_element, EXTENSION]); + +(* Theorem: prime_powers_upto 1 = {} *) +(* Proof: + x IN prime_powers_upto 1 + <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= 1 by prime_powers_upto_element + <=> ?p. (x = p ** LOG p n) /\ prime 0 or prime 1 by p <= 0 + <=> F by NOT_PRIME_0, NOT_PRIME_1 +*) +val prime_powers_upto_1 = store_thm( + "prime_powers_upto_1", + ``prime_powers_upto 1 = {}``, + rw[prime_powers_upto_element, EXTENSION, DECIDE``x <= 1 <=> (x = 0) \/ (x = 1)``] >> + metis_tac[NOT_PRIME_0, NOT_PRIME_1]); + +(* Theorem: x IN prime_power_divisors n <=> ?p. (x = p ** ppidx n) /\ prime p /\ p divides n *) +(* Proof: by prime_power_divisors_def, prime_divisors_element *) +val prime_power_divisors_element = store_thm( + "prime_power_divisors_element", + ``!n x. x IN prime_power_divisors n <=> ?p. (x = p ** ppidx n) /\ prime p /\ p divides n``, + rw[prime_power_divisors_def, prime_divisors_element]); + +(* Theorem: prime p /\ p divides n ==> (p ** ppidx n) IN (prime_power_divisors n) *) +(* Proof: by prime_power_divisors_element *) +val prime_power_divisors_element_alt = store_thm( + "prime_power_divisors_element_alt", + ``!p n. prime p /\ p divides n ==> (p ** ppidx n) IN (prime_power_divisors n)``, + metis_tac[prime_power_divisors_element]); + +(* Theorem: 0 < n ==> FINITE (prime_power_divisors n) *) +(* Proof: + Note prime_power_divisors n = + IMAGE (\p. p ** ppidx n) (prime_divisors n) by prime_power_divisors_def + and FINITE (prime_divisors n) by prime_divisors_finite, 0 < n + ==> FINITE (prime_power_divisors n) by IMAGE_FINITE +*) +val prime_power_divisors_finite = store_thm( + "prime_power_divisors_finite", + ``!n. 0 < n ==> FINITE (prime_power_divisors n)``, + rw[prime_power_divisors_def, prime_divisors_finite]); + +(* Theorem: PAIRWISE_COPRIME (prime_power_divisors n) *) +(* Proof: + Let s = prime_power_divisors n + This is to show: x IN s /\ y IN s /\ x <> y ==> coprime x y + Note ?p1. prime p1 /\ + (x = p1 ** prime_power_index p1 n) /\ p1 divides n by prime_power_divisors_element + and ?p2. prime p2 /\ + (y = p2 ** prime_power_index p2 n) /\ p2 divides n by prime_power_divisors_element + and p1 <> p2 by prime_powers_eq + Thus coprime x y by prime_powers_coprime +*) +val prime_power_divisors_pairwise_coprime = store_thm( + "prime_power_divisors_pairwise_coprime", + ``!n. PAIRWISE_COPRIME (prime_power_divisors n)``, + metis_tac[prime_power_divisors_element, prime_powers_eq, prime_powers_coprime]); + +(* Theorem: prime_power_divisors 1 = {} *) +(* Proof: + x IN prime_power_divisors 1 + <=> ?p. (x = p ** ppidx n) /\ prime p /\ p divides 1 by prime_power_divisors_element + <=> ?p. (x = p ** ppidx n) /\ prime 1 by DIVIDES_ONE + <=> F by NOT_PRIME_1 +*) +val prime_power_divisors_1 = store_thm( + "prime_power_divisors_1", + ``prime_power_divisors 1 = {}``, + rw[prime_power_divisors_element, EXTENSION]); + +(* ------------------------------------------------------------------------- *) +(* Factorisations *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < n ==> (n = PROD_SET (prime_power_divisors n)) *) +(* Proof: + Let s = prime_power_divisors n. + The goal becomes: n = PROD_SET s + Note FINITE s by prime_power_divisors_finite + + Claim: (PROD_SET s) divides n + Proof: Note !z. z IN s <=> + ?p. (z = p ** ppidx n) /\ prime p /\ p divides n by prime_power_divisors_element + ==> !z. z IN s ==> z divides n by prime_power_index_def + + Note PAIRWISE_COPRIME s by prime_power_divisors_pairwise_coprime + Thus set_lcm s = PROD_SET s by pairwise_coprime_prod_set_eq_set_lcm + But (set_lcm s) divides n by set_lcm_is_least_common_multiple + ==> PROD_SET s divides n by above + + Therefore, ?q. n = q * PROD_SET s by divides_def, Claim. + Claim: q = 1 + Proof: By contradiction, suppose q <> 1. + Then ?p. prime p /\ p divides q by PRIME_FACTOR + Let u = p ** ppidx n, v = n DIV u. + Then u divides n /\ coprime p v by prime_power_index_def, 0 < n, prime p + Note 0 < p by PRIME_POS + ==> 0 < u by EXP_POS, 0 < p + Thus n = v * u by DIVIDES_EQN, 0 < u + + Claim: u divides (PROD_SET s) + Proof: Note q divides n by divides_def, MULT_COMM + ==> p divides n by DIVIDES_TRANS + ==> p IN (prime_divisors n) by prime_divisors_element + ==> u IN s by prime_power_divisors_element_alt + Thus u divides (PROD_SET s) by PROD_SET_ELEMENT_DIVIDES, FINITE s + + Hence ?t. PROD_SET s = t * u by divides_def, u divides (PROD_SET s) + or v * u = n = q * (t * u) by above + = (q * t) * u by MULT_ASSOC + ==> v = q * t by MULT_RIGHT_CANCEL, NOT_ZERO_LT_ZERO + But p divideq q by above + ==> p divides v by DIVIDES_MULT + Note 1 < p by ONE_LT_PRIME + ==> ~(coprime p v) by coprime_not_divides + This contradicts coprime p v. + + Thus n = q * PROD_SET s, and q = 1 by Claim + or n = PROD_SET s by MULT_LEFT_1 +*) +val prime_factorisation = store_thm( + "prime_factorisation", + ``!n. 0 < n ==> (n = PROD_SET (prime_power_divisors n))``, + rpt strip_tac >> + qabbrev_tac `s = prime_power_divisors n` >> + `FINITE s` by rw[prime_power_divisors_finite, Abbr`s`] >> + `(PROD_SET s) divides n` by + (`!z. z IN s ==> z divides n` by metis_tac[prime_power_divisors_element, prime_power_index_def] >> + `PAIRWISE_COPRIME s` by metis_tac[prime_power_divisors_pairwise_coprime, Abbr`s`] >> + metis_tac[pairwise_coprime_prod_set_eq_set_lcm, set_lcm_is_least_common_multiple]) >> + `?q. n = q * PROD_SET s` by rw[GSYM divides_def] >> + `q = 1` by + (spose_not_then strip_assume_tac >> + `?p. prime p /\ p divides q` by rw[PRIME_FACTOR] >> + qabbrev_tac `u = p ** ppidx n` >> + qabbrev_tac `v = n DIV u` >> + `u divides n /\ coprime p v` by rw[prime_power_index_def, Abbr`u`, Abbr`v`] >> + `0 < u` by rw[EXP_POS, PRIME_POS, Abbr`u`] >> + `n = v * u` by rw[GSYM DIVIDES_EQN, Abbr`v`] >> + `u divides (PROD_SET s)` by + (`p divides n` by metis_tac[divides_def, MULT_COMM, DIVIDES_TRANS] >> + `p IN (prime_divisors n)` by rw[prime_divisors_element] >> + `u IN s` by metis_tac[prime_power_divisors_element_alt] >> + rw[PROD_SET_ELEMENT_DIVIDES]) >> + `?t. PROD_SET s = t * u` by rw[GSYM divides_def] >> + `v = q * t` by metis_tac[MULT_RIGHT_CANCEL, MULT_ASSOC, NOT_ZERO_LT_ZERO] >> + `p divides v` by rw[DIVIDES_MULT] >> + `1 < p` by rw[ONE_LT_PRIME] >> + metis_tac[coprime_not_divides]) >> + rw[]); + +(* This is a little milestone theorem. *) + +(* Theorem: 0 < n ==> (n = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n))) *) +(* Proof: by prime_factorisation, prime_power_divisors_def *) +val basic_prime_factorisation = store_thm( + "basic_prime_factorisation", + ``!n. 0 < n ==> (n = PROD_SET (IMAGE (\p. p ** ppidx n) (prime_divisors n)))``, + rw[prime_factorisation, GSYM prime_power_divisors_def]); + +(* Theorem: 0 < n /\ m divides n ==> (m = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors n))) *) +(* Proof: + Note 0 < m by ZERO_DIVIDES, 0 < n + Let s = prime_divisors n, t = IMAGE (\p. p ** ppidx m) s. + The goal is: m = PROD_SET t + + Note FINITE s by prime_divisors_finite + ==> FINITE t by IMAGE_FINITE + and PAIRWISE_COPRIME t by prime_divisors_element, prime_powers_coprime + + By DIVIDES_ANTISYM, this is to show: + (1) m divides PROD_SET t + Let r = prime_divisors m + Then m = PROD_SET (IMAGE (\p. p ** ppidx m) r) by basic_prime_factorisation + and r SUBSET s by prime_divisors_divisor_subset + ==> (IMAGE (\p. p ** ppidx m) r) SUBSET t by IMAGE_SUBSET + ==> m divides PROD_SET t by pairwise_coprime_prod_set_subset_divides + (2) PROD_SET t divides m + Claim: !x. x IN t ==> x divides m + Proof: Note ?p. p IN s /\ (x = p ** (ppidx m)) by IN_IMAGE + and prime p by prime_divisors_element + so 1 < p by ONE_LT_PRIME + Now p ** ppidx m divides m by prime_power_factor_divides + or x divides m by above + Hence PROD_SET t divides m by pairwise_coprime_prod_set_divides +*) +val divisor_prime_factorisation = store_thm( + "divisor_prime_factorisation", + ``!m n. 0 < n /\ m divides n ==> (m = PROD_SET (IMAGE (\p. p ** ppidx m) (prime_divisors n)))``, + rpt strip_tac >> + `0 < m` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + qabbrev_tac `s = prime_divisors n` >> + qabbrev_tac `t = IMAGE (\p. p ** ppidx m) s` >> + `FINITE s` by rw[prime_divisors_finite, Abbr`s`] >> + `FINITE t` by rw[Abbr`t`] >> + `PAIRWISE_COPRIME t` by + (rw[Abbr`t`] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> + rw[prime_powers_coprime]) >> + (irule DIVIDES_ANTISYM >> rpt conj_tac) >| [ + qabbrev_tac `r = prime_divisors m` >> + `m = PROD_SET (IMAGE (\p. p ** ppidx m) r)` by rw[basic_prime_factorisation, Abbr`r`] >> + `r SUBSET s` by rw[prime_divisors_divisor_subset, Abbr`r`, Abbr`s`] >> + metis_tac[pairwise_coprime_prod_set_subset_divides, IMAGE_SUBSET], + `!x. x IN t ==> x divides m` by + (rpt strip_tac >> + qabbrev_tac `f = \p:num. p ** (ppidx m)` >> + `?p. p IN s /\ (x = p ** (ppidx m))` by metis_tac[IN_IMAGE] >> + `prime p` by metis_tac[prime_divisors_element] >> + rw[prime_power_factor_divides]) >> + rw[pairwise_coprime_prod_set_divides] + ]); + +(* Theorem: 0 < m /\ 0 < n ==> + (gcd m n = PROD_SET (IMAGE (\p. p ** (MIN (ppidx m) (ppidx n))) + ((prime_divisors m) INTER (prime_divisors n)))) *) +(* Proof: + Let sm = prime_divisors m, sn = prime_divisors n, s = sm INTER sn. + Let tm = IMAGE (\p. p ** ppidx m) sm, tn = IMAGE (\p. p ** ppidx m) sn, + t = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) s. + The goal is: gcd m n = PROD_SET t + + By GCD_PROPERTY, this is to show: + (1) PROD_SET t divides m /\ PROD_SET t divides n + Note FINITE sm /\ FINITE sn by prime_divisors_finite + ==> FINITE s by FINITE_INTER + and FINITE tm /\ FINITE tn /\ FINITE t by IMAGE_FINITE + Also PAIRWISE_COPRIME t by IN_INTER, prime_divisors_element, prime_powers_coprime + + Claim: !x. x IN t ==> x divides m /\ x divides n + Prood: Note x IN t + ==> ?p. p IN s /\ x = p ** MIN (ppidx m) (ppidx n) by IN_IMAGE + ==> p IN sm /\ p IN sn by IN_INTER + Note prime p by prime_divisors_element + ==> p ** ppidx m divides m by prime_power_factor_divides + and p ** ppidx n divides n by prime_power_factor_divides + Note MIN (ppidx m) (ppidx n) <= ppidx m by MIN_DEF + and MIN (ppidx m) (ppidx n) <= ppidx n by MIN_DEF + ==> x divides p ** ppidx m by prime_power_divides_iff + and x divides p ** ppidx n by prime_power_divides_iff + or x divides m /\ x divides n by DIVIDES_TRANS + + Therefore, PROD_SET t divides m by pairwise_coprime_prod_set_divides, Claim + and PROD_SET t divides n by pairwise_coprime_prod_set_divides, Claim + + (2) !x. x divides m /\ x divides n ==> x divides PROD_SET t + Let k = PROD_SET t, sx = prime_divisors x, tx = IMAGE (\p. p ** ppidx x) sx. + Note 0 < x by ZERO_DIVIDES, 0 < m + and x = PROD_SET tx by basic_prime_factorisation, 0 < x + The aim is to show: PROD_SET tx divides k + + Note FINITE sx by prime_divisors_finite + ==> FINITE tx by IMAGE_FINITE + and PAIRWISE_COPRIME tx by prime_divisors_element, prime_powers_coprime + + Claim: !z. z IN tx ==> z divides k + Proof: Note z IN tx + ==> ?p. p IN sx /\ (z = p ** ppidx x) by IN_IMAGE + Note prime p by prime_divisors_element + and sx SUBSET sm /\ sx SUBSET sn by prime_divisors_divisor_subset, x | m, x | n + ==> p IN sm /\ p IN sn by SUBSET_DEF + or p IN s by IN_INTER + Also ppidx x <= MIN (ppidx m) (ppidx n) by prime_power_index_common_divisor + ==> z divides p ** MIN (ppidx m) (ppidx n) by prime_power_divides_iff + But p ** MIN (ppidx m) (ppidx n) IN t by IN_IMAGE + ==> p ** MIN (ppidx m) (ppidx n) divides k by PROD_SET_ELEMENT_DIVIDES + or z divides k by DIVIDES_TRANS + + Therefore, PROD_SET tx divides k by pairwise_coprime_prod_set_divides +*) +val gcd_prime_factorisation = store_thm( + "gcd_prime_factorisation", + ``!m n. 0 < m /\ 0 < n ==> + (gcd m n = PROD_SET (IMAGE (\p. p ** (MIN (ppidx m) (ppidx n))) + ((prime_divisors m) INTER (prime_divisors n))))``, + rpt strip_tac >> + qabbrev_tac `sm = prime_divisors m` >> + qabbrev_tac `sn = prime_divisors n` >> + qabbrev_tac `s = sm INTER sn` >> + qabbrev_tac `tm = IMAGE (\p. p ** ppidx m) sm` >> + qabbrev_tac `tn = IMAGE (\p. p ** ppidx m) sn` >> + qabbrev_tac `t = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) s` >> + `FINITE sm /\ FINITE sn /\ FINITE s` by rw[prime_divisors_finite, Abbr`sm`, Abbr`sn`, Abbr`s`] >> + `FINITE tm /\ FINITE tn /\ FINITE t` by rw[Abbr`tm`, Abbr`tn`, Abbr`t`] >> + `PAIRWISE_COPRIME t` by + (rw[Abbr`t`] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element, IN_INTER] >> + rw[prime_powers_coprime]) >> + `!x. x IN t ==> x divides m /\ x divides n` by + (ntac 2 strip_tac >> + qabbrev_tac `f = \p:num. p ** MIN (ppidx m) (ppidx n)` >> + `?p. p IN s /\ p IN sm /\ p IN sn /\ (x = p ** MIN (ppidx m) (ppidx n))` by metis_tac[IN_IMAGE, IN_INTER] >> + `prime p` by metis_tac[prime_divisors_element] >> + `p ** ppidx m divides m /\ p ** ppidx n divides n` by rw[prime_power_factor_divides] >> + `MIN (ppidx m) (ppidx n) <= ppidx m /\ MIN (ppidx m) (ppidx n) <= ppidx n` by rw[] >> + metis_tac[prime_power_divides_iff, DIVIDES_TRANS]) >> + rw[GCD_PROPERTY] >- + rw[pairwise_coprime_prod_set_divides] >- + rw[pairwise_coprime_prod_set_divides] >> + qabbrev_tac `k = PROD_SET t` >> + qabbrev_tac `sx = prime_divisors x` >> + qabbrev_tac `tx = IMAGE (\p. p ** ppidx x) sx` >> + `0 < x` by metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO] >> + `x = PROD_SET tx` by rw[basic_prime_factorisation, Abbr`tx`, Abbr`sx`] >> + `FINITE sx` by rw[prime_divisors_finite, Abbr`sx`] >> + `FINITE tx` by rw[Abbr`tx`] >> + `PAIRWISE_COPRIME tx` by + (rw[Abbr`tx`] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> + rw[prime_powers_coprime]) >> + `!z. z IN tx ==> z divides k` by + (rw[Abbr`tx`] >> + `prime p` by metis_tac[prime_divisors_element] >> + `p IN sm /\ p IN sn` by metis_tac[prime_divisors_divisor_subset, SUBSET_DEF] >> + `p IN s` by metis_tac[IN_INTER] >> + `ppidx x <= MIN (ppidx m) (ppidx n)` by rw[prime_power_index_common_divisor] >> + `(p ** ppidx x) divides p ** MIN (ppidx m) (ppidx n)` by rw[prime_power_divides_iff] >> + qabbrev_tac `f = \p:num. p ** MIN (ppidx m) (ppidx n)` >> + `p ** MIN (ppidx m) (ppidx n) IN t` by metis_tac[IN_IMAGE] >> + metis_tac[PROD_SET_ELEMENT_DIVIDES, DIVIDES_TRANS]) >> + rw[pairwise_coprime_prod_set_divides]); + +(* This is a major milestone theorem. *) + +(* Theorem: 0 < m /\ 0 < n ==> + (lcm m n = PROD_SET (IMAGE (\p. p ** (MAX (ppidx m) (ppidx n))) + ((prime_divisors m) UNION (prime_divisors n)))) *) +(* Proof: + Let sm = prime_divisors m, sn = prime_divisors n, s = sm UNION sn. + Let tm = IMAGE (\p. p ** ppidx m) sm, tn = IMAGE (\p. p ** ppidx m) sn, + t = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) s. + The goal is: lcm m n = PROD_SET t + + By LCM_PROPERTY, this is to show: + (1) m divides PROD_SET t /\ n divides PROD_SET t + Let k = PROD_SET t. + Note m = PROD_SET tm by basic_prime_factorisation, 0 < m + and n = PROD_SET tn by basic_prime_factorisation, 0 < n + Also PAIRWISE_COPRIME tm by prime_divisors_element, prime_powers_coprime + and PAIRWISE_COPRIME tn by prime_divisors_element, prime_powers_coprime + + Claim: !z. z IN tm ==> z divides k + Proof: Note z IN tm + ==> ?p. p IN sm /\ (z = p ** ppidx m) by IN_IMAGE + ==> p IN s by IN_UNION + and prime p by prime_divisors_element + Note ppidx m <= MAX (ppidx m) (ppidx n) by MAX_DEF + ==> z divides p ** MAX (ppidx m) (ppidx n) by prime_power_divides_iff + But p ** MAX (ppidx m) (ppidx n) IN t by IN_IMAGE + and p ** MAX (ppidx m) (ppidx n) divides k by PROD_SET_ELEMENT_DIVIDES + Thus z divides k by DIVIDES_TRANS + + Similarly, !z. z IN tn ==> z divides k + Hence (PROD_SET tm) divides k /\ (PROD_SET tn) divides k by pairwise_coprime_prod_set_divides + or m divides k /\ n divides k by above + + (2) m divides x /\ n divides x ==> PROD_SET t divides x + If x = 0, trivially true by ALL_DIVIDES_ZERO + If x <> 0, then 0 < x. + Note PAIRWISE_COPRIME t by prime_divisors_element, prime_powers_coprimem IN_UNION + + Claim: !z. z IN t ==> z divides x + Proof: Note z IN t + ==> ?p. p IN s /\ (z = p ** MAX (ppidx m) (ppidx n)) by IN_IMAGE + or prime p by prime_divisors_element, IN_UNION + Note MAX (ppidx m) (ppidx n) <= ppidx x by prime_power_index_common_multiple, 0 < x + so z divides p ** ppidx x by prime_power_divides_iff + But p ** ppidx x divides x by prime_power_factor_divides + Thus z divides x by DIVIDES_TRANS + Hence PROD_SET t divides x by pairwise_coprime_prod_set_divides +*) +val lcm_prime_factorisation = store_thm( + "lcm_prime_factorisation", + ``!m n. 0 < m /\ 0 < n ==> + (lcm m n = PROD_SET (IMAGE (\p. p ** (MAX (ppidx m) (ppidx n))) + ((prime_divisors m) UNION (prime_divisors n))))``, + rpt strip_tac >> + qabbrev_tac `sm = prime_divisors m` >> + qabbrev_tac `sn = prime_divisors n` >> + qabbrev_tac `s = sm UNION sn` >> + qabbrev_tac `tm = IMAGE (\p. p ** ppidx m) sm` >> + qabbrev_tac `tn = IMAGE (\p. p ** ppidx n) sn` >> + qabbrev_tac `t = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) s` >> + `FINITE sm /\ FINITE sn /\ FINITE s` by rw[prime_divisors_finite, Abbr`sm`, Abbr`sn`, Abbr`s`] >> + `FINITE tm /\ FINITE tn /\ FINITE t` by rw[Abbr`tm`, Abbr`tn`, Abbr`t`] >> + rw[LCM_PROPERTY] >| [ + qabbrev_tac `k = PROD_SET t` >> + `m = PROD_SET tm` by rw[basic_prime_factorisation, Abbr`tm`, Abbr`sm`] >> + `PAIRWISE_COPRIME tm` by + (rw[Abbr`tm`] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> + rw[prime_powers_coprime]) >> + `!z. z IN tm ==> z divides k` by + (rw[Abbr`tm`] >> + `prime p` by metis_tac[prime_divisors_element] >> + `p IN s` by metis_tac[IN_UNION] >> + `ppidx m <= MAX (ppidx m) (ppidx n)` by rw[] >> + `(p ** ppidx m) divides p ** MAX (ppidx m) (ppidx n)` by rw[prime_power_divides_iff] >> + qabbrev_tac `f = \p:num. p ** MAX (ppidx m) (ppidx n)` >> + `p ** MAX (ppidx m) (ppidx n) IN t` by metis_tac[IN_IMAGE] >> + metis_tac[PROD_SET_ELEMENT_DIVIDES, DIVIDES_TRANS]) >> + rw[pairwise_coprime_prod_set_divides], + qabbrev_tac `k = PROD_SET t` >> + `n = PROD_SET tn` by rw[basic_prime_factorisation, Abbr`tn`, Abbr`sn`] >> + `PAIRWISE_COPRIME tn` by + (rw[Abbr`tn`] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element] >> + rw[prime_powers_coprime]) >> + `!z. z IN tn ==> z divides k` by + (rw[Abbr`tn`] >> + `prime p` by metis_tac[prime_divisors_element] >> + `p IN s` by metis_tac[IN_UNION] >> + `ppidx n <= MAX (ppidx m) (ppidx n)` by rw[] >> + `(p ** ppidx n) divides p ** MAX (ppidx m) (ppidx n)` by rw[prime_power_divides_iff] >> + qabbrev_tac `f = \p:num. p ** MAX (ppidx m) (ppidx n)` >> + `p ** MAX (ppidx m) (ppidx n) IN t` by metis_tac[IN_IMAGE] >> + metis_tac[PROD_SET_ELEMENT_DIVIDES, DIVIDES_TRANS]) >> + rw[pairwise_coprime_prod_set_divides], + Cases_on `x = 0` >- + rw[] >> + `0 < x` by decide_tac >> + `PAIRWISE_COPRIME t` by + (rw[Abbr`t`] >> + `prime p /\ prime p' /\ p <> p'` by metis_tac[prime_divisors_element, IN_UNION] >> + rw[prime_powers_coprime]) >> + `!z. z IN t ==> z divides x` by + (rw[Abbr`t`] >> + `prime p` by metis_tac[prime_divisors_element, IN_UNION] >> + `MAX (ppidx m) (ppidx n) <= ppidx x` by rw[prime_power_index_common_multiple] >> + `p ** MAX (ppidx m) (ppidx n) divides p ** ppidx x` by rw[prime_power_divides_iff] >> + `p ** ppidx x divides x` by rw[prime_power_factor_divides] >> + metis_tac[DIVIDES_TRANS]) >> + rw[pairwise_coprime_prod_set_divides] + ]); + +(* Another major milestone theorem. *) + +(* ------------------------------------------------------------------------- *) +(* GCD and LCM special coprime decompositions *) +(* ------------------------------------------------------------------------- *) + +(* +Notes +=|=== +Given two numbers m and n, with d = gcd m n, and cofactors a = m/d, b = n/d. +Is it true that gcd a n = 1 ? + +Take m = 2^3 * 3^2 = 8 * 9 = 72, n = 2^2 * 3^3 = 4 * 27 = 108 +Then gcd m n = d = 2^2 * 3^2 = 4 * 9 = 36, with cofactors a = 2, b = 3. +In this case, gcd a n = gcd 2 108 <> 1. +But lcm m n = 2^3 * 3^3 = 8 * 27 = 216 + +Ryan Vinroot's method: +Take m = 2^7 * 3^5 * 5^4 * 7^4 n = 2^6 * 3*7 * 5^4 * 11^4 +Then m = a b c d = (7^4) (5^4) (2^7) (3^5) + and n = x y z t = (11^4) (5^4) (3^7) (2^6) +Note b = y always, and lcm m n = a b c x z, gcd m n = d y z +Define P = a b c, Q = x z, then coprime P Q, and lcm P Q = a b c x z = lcm m n = P * Q + +a = PROD (all prime factors of m which are not prime factors of n) = 7^4 +b = PROD (all prime factors of m common with m and equal powers in both) = 5^4 +c = PROD (all prime factors of m common with m but more powers in m) = 2^7 +d = PROD (all prime factors of m common with m but more powers in n) = 3^5 + +x = PROD (all prime factors of n which are not prime factors of m) = 11^4 +y = PROD (all prime factors of n common with n and equal powers in both) = 5^4 +z = PROD (all prime factors of n common with n but more powers in n) = 3^7 +t = PROD (all prime factors of n common with n but more powers in m) = 2^6 + +Let d = gcd m n. If d <> 1, then it consists of prime powers, e.g. 2^3 * 3^2 * 5 +Since d is to take the minimal of prime powers common to both m n, +each prime power in d must occur fully in either m or n. +e.g. it may be the case that: m = 2^3 * 3 * 5 * a, n = 2 * 3^2 * 5 * b +where a, b don't have prime factors 2, 3, 5, and coprime a b. +and lcm m n = a * b * 2^3 * 3^2 * 5, taking the maximal of prime powers common to both. + = (a * 2^3) * (b * 3^2 * 5) = P * Q with coprime P Q. + +Ryan Vinroot's method (again): +Take m = 2^7 * 3^5 * 5^4 * 7^4 n = 2^6 * 3*7 * 5^4 * 11^4 +Then gcd m n = 2^6 * 3^5 * 5^4, lcm m n = 2^7 * 3^7 * 5^4 * 7^4 * 11^4 +Take d = 3^5 * 5^4 (compare m to gcd n m, take the full factors of gcd in m ) + e = gcd n m / d = 2^6 (take what is left over) +Then P = m / d = 2^7 * 7^4 + Q = n / e = 3^7 * 5^4 * 11^4 + so P | m, there is ord p = P. +and Q | n, there is ord q = Q. +and coprime P Q, so ord (p * q) = P * Q = lcm m n. + +d = PROD {p ** ppidx m | p | prime p /\ p | (gcd m n) /\ (ppidx (gcd n m) = ppidx m)} +e = gcd n m / d + +prime_factorisation |- !n. 0 < n ==> (n = PROD_SET (prime_power_divisors n) + +This is because: m = 2^7 * 3^5 * 5^4 * 7^4 * 11^0 + n = 2^6 * 3^7 * 5^4 * 7^0 * 11^4 +we know that gcd m n = 2^6 * 3^5 * 5^4 * 7^0 * 11^0 taking minimum + lcm m n = 2^7 * 3^7 * 5^4 * 7^4 * 11^4 taking maximum +Thus, using gcd m n as a guide, +pick d = 2^0 * 3^5 * 5^4 , taking common minimum, +Then P = m / d will remove these common minimum from m, +but Q = n / e = n / (gcd m n / d) = n * d / gcd m n will include their common maximum +This separation of prime factors keep coprime P Q, but P * Q = lcm m n. + +*) + +(* Overload the park sets *) +val _ = overload_on ("common_prime_divisors", + ``\m n. (prime_divisors m) INTER (prime_divisors n)``); +val _ = overload_on ("total_prime_divisors", + ``\m n. (prime_divisors m) UNION (prime_divisors n)``); +val _ = overload_on ("park_on", + ``\m n. {p | p IN common_prime_divisors m n /\ ppidx m <= ppidx n}``); +val _ = overload_on ("park_off", + ``\m n. {p | p IN common_prime_divisors m n /\ ppidx n < ppidx m}``); +(* Overload the parking divisor of GCD *) +val _ = overload_on("park", ``\m n. PROD_SET (IMAGE (\p. p ** ppidx m) (park_on m n))``); + +(* Note: +The basic one is park_on m n, defined only for 0 < m and 0 < n. +*) + +(* Theorem: p IN common_prime_divisors m n <=> p IN prime_divisors m /\ p IN prime_divisors n *) +(* Proof: by IN_INTER *) +val common_prime_divisors_element = store_thm( + "common_prime_divisors_element", + ``!m n p. p IN common_prime_divisors m n <=> p IN prime_divisors m /\ p IN prime_divisors n``, + rw[]); + +(* Theorem: 0 < m /\ 0 < n ==> FINITE (common_prime_divisors m n) *) +(* Proof: by prime_divisors_finite, FINITE_INTER *) +val common_prime_divisors_finite = store_thm( + "common_prime_divisors_finite", + ``!m n. 0 < m /\ 0 < n ==> FINITE (common_prime_divisors m n)``, + rw[prime_divisors_finite]); + +(* Theorem: PAIRWISE_COPRIME (common_prime_divisors m n) *) +(* Proof: + Note x IN prime_divisors m ==> prime x by prime_divisors_element + and y IN prime_divisors n ==> prime y by prime_divisors_element + and x <> y ==> coprime x y by primes_coprime +*) +val common_prime_divisors_pairwise_coprime = store_thm( + "common_prime_divisors_pairwise_coprime", + ``!m n. PAIRWISE_COPRIME (common_prime_divisors m n)``, + metis_tac[prime_divisors_element, primes_coprime, IN_INTER]); + +(* Theorem: PAIRWISE_COPRIME (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n)) *) +(* Proof: + Note (prime_divisors m) SUBSET prime by prime_divisors_subset_prime + so (common_prime_divisors m n) SUBSET prime by SUBSET_INTER_SUBSET + Thus true by pairwise_coprime_for_prime_powers +*) +val common_prime_divisors_min_image_pairwise_coprime = store_thm( + "common_prime_divisors_min_image_pairwise_coprime", + ``!m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n))``, + metis_tac[prime_divisors_subset_prime, SUBSET_INTER_SUBSET, pairwise_coprime_for_prime_powers]); + +(* Theorem: p IN total_prime_divisors m n <=> p IN prime_divisors m \/ p IN prime_divisors n *) +(* Proof: by IN_UNION *) +val total_prime_divisors_element = store_thm( + "total_prime_divisors_element", + ``!m n p. p IN total_prime_divisors m n <=> p IN prime_divisors m \/ p IN prime_divisors n``, + rw[]); + +(* Theorem: 0 < m /\ 0 < n ==> FINITE (total_prime_divisors m n) *) +(* Proof: by prime_divisors_finite, FINITE_UNION *) +val total_prime_divisors_finite = store_thm( + "total_prime_divisors_finite", + ``!m n. 0 < m /\ 0 < n ==> FINITE (total_prime_divisors m n)``, + rw[prime_divisors_finite]); + +(* Theorem: PAIRWISE_COPRIME (total_prime_divisors m n) *) +(* Proof: + Note x IN prime_divisors m ==> prime x by prime_divisors_element + and y IN prime_divisors n ==> prime y by prime_divisors_element + and x <> y ==> coprime x y by primes_coprime +*) +val total_prime_divisors_pairwise_coprime = store_thm( + "total_prime_divisors_pairwise_coprime", + ``!m n. PAIRWISE_COPRIME (total_prime_divisors m n)``, + metis_tac[prime_divisors_element, primes_coprime, IN_UNION]); + +(* Theorem: PAIRWISE_COPRIME (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n)) *) +(* Proof: + Note prime_divisors m SUBSET prime by prime_divisors_subset_prime + and prime_divisors n SUBSET prime by prime_divisors_subset_prime + so (total_prime_divisors m n) SUBSET prime by UNION_SUBSET + Thus true by pairwise_coprime_for_prime_powers +*) +val total_prime_divisors_max_image_pairwise_coprime = store_thm( + "total_prime_divisors_max_image_pairwise_coprime", + ``!m n. PAIRWISE_COPRIME (IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n))``, + metis_tac[prime_divisors_subset_prime, UNION_SUBSET, pairwise_coprime_for_prime_powers]); + +(* Theorem: p IN park_on m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n *) +(* Proof: by IN_INTER *) +val park_on_element = store_thm( + "park_on_element", + ``!m n p. p IN park_on m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n``, + rw[] >> + metis_tac[]); + +(* Theorem: p IN park_off m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m *) +(* Proof: by IN_INTER *) +val park_off_element = store_thm( + "park_off_element", + ``!m n p. p IN park_off m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m``, + rw[] >> + metis_tac[]); + +(* Theorem: park_off m n = (common_prime_divisors m n) DIFF (park_on m n) *) +(* Proof: by EXTENSION, NOT_LESS_EQUAL *) +val park_off_alt = store_thm( + "park_off_alt", + ``!m n. park_off m n = (common_prime_divisors m n) DIFF (park_on m n)``, + rw[EXTENSION] >> + metis_tac[NOT_LESS_EQUAL]); + +(* Theorem: park_on m n SUBSET common_prime_divisors m n *) +(* Proof: by SUBSET_DEF *) +val park_on_subset_common = store_thm( + "park_on_subset_common", + ``!m n. park_on m n SUBSET common_prime_divisors m n``, + rw[SUBSET_DEF]); + +(* Theorem: park_off m n SUBSET common_prime_divisors m n *) +(* Proof: by SUBSET_DEF *) +val park_off_subset_common = store_thm( + "park_off_subset_common", + ``!m n. park_off m n SUBSET common_prime_divisors m n``, + rw[SUBSET_DEF]); + +(* Theorem: park_on m n SUBSET total_prime_divisors m n *) +(* Proof: by SUBSET_DEF *) +val park_on_subset_total = store_thm( + "park_on_subset_total", + ``!m n. park_on m n SUBSET total_prime_divisors m n``, + rw[SUBSET_DEF]); + +(* Theorem: park_off m n SUBSET total_prime_divisors m n *) +(* Proof: by SUBSET_DEF *) +val park_off_subset_total = store_thm( + "park_off_subset_total", + ``!m n. park_off m n SUBSET total_prime_divisors m n``, + rw[SUBSET_DEF]); + +(* Theorem: common_prime_divisors m n =|= park_on m n # park_off m n *) +(* Proof: + Let s = common_prime_divisors m n. + Note park_on m n SUBSET s by park_on_subset_common + and park_off m n = s DIFF (park_on m n) by park_off_alt + so s = park_on m n UNION park_off m n /\ + DISJOINT (park_on m n) (park_off m n) by partition_by_subset +*) +val park_on_off_partition = store_thm( + "park_on_off_partition", + ``!m n. common_prime_divisors m n =|= park_on m n # park_off m n``, + metis_tac[partition_by_subset, park_on_subset_common, park_off_alt]); + +(* Theorem: 1 NOTIN (IMAGE (\p. p ** ppidx m) (park_off m n)) *) +(* Proof: + By contradiction, suppse 1 IN (IMAGE (\p. p ** ppidx m) (park_off m n)). + Then ?p. p IN park_off m n /\ (1 = p ** ppidx m) by IN_IMAGE + or p IN prime_divisors m /\ + p IN prime_divisors n /\ ppidx n < ppidx m by park_off_element + But prime p by prime_divisors_element + and p <> 1 by NOT_PRIME_1 + Thus ppidx m = 0 by EXP_EQ_1 + or ppidx n < 0, which is F by NOT_LESS_0 +*) +val park_off_image_has_not_1 = store_thm( + "park_off_image_has_not_1", + ``!m n. 1 NOTIN (IMAGE (\p. p ** ppidx m) (park_off m n))``, + rw[] >> + spose_not_then strip_assume_tac >> + `prime p` by metis_tac[prime_divisors_element] >> + `p <> 1` by metis_tac[NOT_PRIME_1] >> + decide_tac); + +(* +For the example, +This is because: m = 2^7 * 3^5 * 5^4 * 7^4 * 11^0 + n = 2^6 * 3^7 * 5^4 * 7^0 * 11^4 +we know that gcd m n = 2^6 * 3^5 * 5^4 * 7^0 * 11^0 taking minimum + lcm m n = 2^7 * 3^7 * 5^4 * 7^4 * 11^4 taking maximum +Thus, using gcd m n as a guide, +pick d = 2^0 * 3^5 * 5^4 , taking common minimum, +Then P = m / d will remove these common minimum from m, +but Q = n / e = n / (gcd m n / d) = n * d / gcd m n will include their common maximum +This separation of prime factors keep coprime P Q, but P * Q = lcm m n. +common_prime_divisors m n = {2; 3; 5} s = {2^6; 3^5; 5^4} with MIN +park_on m n = {3; 5} u = IMAGE (\p. p ** ppidx m) (park_on m n) = {3^5; 5^4} +park_off m n = {2} v = IMAGE (\p. p ** ppidx n) (park_off m n) = {2^6} +Note IMAGE (\p. p ** ppidx m) (park_off m n) = {2^7} +and IMAGE (\p. p ** ppidx n) (park_on m n) = {3^7; 5^4} + +total_prime_divisors m n = {2; 3; 5; 7; 11} s = {2^7; 3^7; 5^4; 7^4; 11^4} with MAX +sm = (prime_divisors m) DIFF (park_on m n) = {2; 7}, u = IMAGE (\p. p ** ppidx m) sm = {2^7; 7^4} +sn = (prime_divisors n) DIFF (park_off m n) = {3; 5; 11}, v = IMAGE (\p. p ** ppidx n) sn = {3^7; 5^4; 11^4} + +park_on_element +|- !m n p. p IN park_on m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx m <= ppidx n +park_off_element +|- !m n p. p IN park_off m n <=> p IN prime_divisors m /\ p IN prime_divisors n /\ ppidx n < ppidx m +*) + +(* Theorem: let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n) in + let u = IMAGE (\p. p ** ppidx m) (park_on m n) in + let v = IMAGE (\p. p ** ppidx n) (park_off m n) in + 0 < m ==> s =|= u # v *) +(* Proof: + This is to show: + (1) s = u UNION v + By EXTENSION, this is to show: + (a) !x. x IN s ==> x IN u \/ x IN v by IN_UNION + Note x IN s + ==> ?p. (x = p ** MIN (ppidx m) (ppidx n)) /\ + p IN common_prime_divisors m n by IN_IMAGE + If ppidx m <= ppidx n + Then MIN (ppidx m) (ppidx n) = ppidx m by MIN_DEF + and p IN park_on m n by common_prime_divisors_element, park_on_element + ==> x IN u by IN_IMAGE + If ~(ppidx m <= ppidx n), + so ppidx n < ppidx m by NOT_LESS_EQUAL + Then MIN (ppidx m) (ppidx n) = ppidx n by MIN_DEF + and p IN park_off m n by common_prime_divisors_element, park_off_element + ==> x IN v by IN_IMAGE + (b) x IN u ==> x IN s + Note x IN u + ==> ?p. (x = p ** ppidx m) /\ + p IN park_on m n by IN_IMAGE + ==> ppidx m <= ppidx n by park_on_element + Thus MIN (ppidx m) (ppidx n) = ppidx m by MIN_DEF + so p IN common_prime_divisors m n by park_on_subset_common, SUBSET_DEF + ==> x IN s by IN_IMAGE + (c) x IN v ==> x IN s + Note x IN v + ==> ?p. (x = p ** ppidx n) /\ + p IN park_off m n by IN_IMAGE + ==> ppidx n < ppidx m by park_off_element + Thus MIN (ppidx m) (ppidx n) = ppidx n by MIN_DEF + so p IN common_prime_divisors m n by park_off_subset_common, SUBSET_DEF + ==> x IN s by IN_IMAGE + (2) DISJOINT u v + This is to show: u INTER v = {} by DISJOINT_DEF + By contradiction, suppse u INTER v <> {}. + Then ?x. x IN u /\ x IN v by MEMBER_NOT_EMPTY, IN_INTER + Thus ?p. p IN park_on m n /\ (x = p ** ppidx m) by IN_IMAGE + and ?q. q IN park_off m n /\ (x = q ** prime_power_index q n) by IN_IMAGE + ==> prime p /\ prime q /\ p divides m by park_on_element, park_off_element + prime_divisors_element + Also 0 < ppidx m by prime_power_index_pos, p divides m, 0 < m + ==> p = q by prime_powers_eq + Thus p IN (park_on m n) INTER (park_off m n) by IN_INTER + But DISJOINT (park_on m n) (park_off m n) by park_on_off_partition + so there is a contradiction by IN_DISJOINT +*) +val park_on_off_common_image_partition = store_thm( + "park_on_off_common_image_partition", + ``!m n. let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n) in + let u = IMAGE (\p. p ** ppidx m) (park_on m n) in + let v = IMAGE (\p. p ** ppidx n) (park_off m n) in + 0 < m ==> s =|= u # v``, + rpt strip_tac >> + qabbrev_tac `f = \p:num. p ** MIN (ppidx m) (ppidx n)` >> + qabbrev_tac `f1 = \p:num. p ** ppidx m` >> + qabbrev_tac `f2 = \p:num. p ** ppidx n` >> + rw_tac std_ss[] >| [ + rw[EXTENSION, EQ_IMP_THM] >| [ + `?p. (x = p ** MIN (ppidx m) (ppidx n)) /\ p IN common_prime_divisors m n` by metis_tac[IN_IMAGE] >> + Cases_on `ppidx m <= ppidx n` >| [ + `MIN (ppidx m) (ppidx n) = ppidx m` by rw[MIN_DEF] >> + `p IN park_on m n` by metis_tac[common_prime_divisors_element, park_on_element] >> + metis_tac[IN_IMAGE], + `MIN (ppidx m) (ppidx n) = ppidx n` by rw[MIN_DEF] >> + `p IN park_off m n` by metis_tac[common_prime_divisors_element, park_off_element, NOT_LESS_EQUAL] >> + metis_tac[IN_IMAGE] + ], + `?p. (x = p ** ppidx m) /\ p IN park_on m n` by metis_tac[IN_IMAGE] >> + `ppidx m <= ppidx n` by metis_tac[park_on_element] >> + `MIN (ppidx m) (ppidx n) = ppidx m` by rw[MIN_DEF] >> + `p IN common_prime_divisors m n` by metis_tac[park_on_subset_common, SUBSET_DEF] >> + metis_tac[IN_IMAGE], + `?p. (x = p ** ppidx n) /\ p IN park_off m n` by metis_tac[IN_IMAGE] >> + `ppidx n < ppidx m` by metis_tac[park_off_element] >> + `MIN (ppidx m) (ppidx n) = ppidx n` by rw[MIN_DEF] >> + `p IN common_prime_divisors m n` by metis_tac[park_off_subset_common, SUBSET_DEF] >> + metis_tac[IN_IMAGE] + ], + rw[DISJOINT_DEF] >> + spose_not_then strip_assume_tac >> + `?x. x IN u /\ x IN v` by metis_tac[MEMBER_NOT_EMPTY, IN_INTER] >> + `?p. p IN park_on m n /\ (x = p ** ppidx m)` by prove_tac[IN_IMAGE] >> + `?q. q IN park_off m n /\ (x = q ** prime_power_index q n)` by prove_tac[IN_IMAGE] >> + `prime p /\ prime q /\ p divides m` by metis_tac[park_on_element, park_off_element, prime_divisors_element] >> + `0 < ppidx m` by rw[prime_power_index_pos] >> + `p = q` by metis_tac[prime_powers_eq] >> + metis_tac[park_on_off_partition, IN_DISJOINT] + ]); + +(* Theorem: 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in + (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ (gcd m n = a * b) /\ coprime a b *) +(* Proof: + Let s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n), + u = IMAGE (\p. p ** ppidx m) (park_on m n), + v = IMAGE (\p. p ** ppidx n) (park_off m n). + Then s =|= u # v by park_on_off_common_image_partition + Let a = PROD_SET u, b = PROD_SET v, c = PROD_SET s. + Then FINITE s by common_prime_divisors_finite, IMAGE_FINITE, 0 < m, 0 < n + and PAIRWISE_COPRIME s by common_prime_divisors_min_image_pairwise_coprime + ==> (c = a * b) /\ coprime a b by pairwise_coprime_prod_set_partition + Note c = gcd m n by gcd_prime_factorisation + and a = park m n by notation + Note c <> 0 by GCD_EQ_0, 0 < m, 0 < n + Thus a <> 0, or 0 < a by MULT_EQ_0 + so b = c DIV a by DIV_SOLVE_COMM, 0 < a + Therefore, + b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n)) /\ + gcd m n = a * b /\ coprime a b by above +*) + +Theorem gcd_park_decomposition: + !m n. 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in + b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n)) /\ + gcd m n = a * b /\ coprime a b +Proof + rpt strip_tac >> + qabbrev_tac `s = IMAGE (\p. p ** MIN (ppidx m) (ppidx n)) (common_prime_divisors m n)` >> + qabbrev_tac `u = IMAGE (\p. p ** ppidx m) (park_on m n)` >> + qabbrev_tac `v = IMAGE (\p. p ** ppidx n) (park_off m n)` >> + `s =|= u # v` by metis_tac[park_on_off_common_image_partition] >> + qabbrev_tac `a = PROD_SET u` >> + qabbrev_tac `b = PROD_SET v` >> + qabbrev_tac `c = PROD_SET s` >> + `FINITE s` by rw[common_prime_divisors_finite, Abbr`s`] >> + `PAIRWISE_COPRIME s` by metis_tac[common_prime_divisors_min_image_pairwise_coprime] >> + `(c = a * b) /\ coprime a b` + by (simp[Abbr`a`, Abbr`b`, Abbr`c`] >> + metis_tac[pairwise_coprime_prod_set_partition]) >> + metis_tac[gcd_prime_factorisation, GCD_EQ_0, MULT_EQ_0, DIV_SOLVE_COMM, + NOT_ZERO_LT_ZERO] +QED + +(* Theorem: 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in + (gcd m n = a * b) /\ coprime a b *) +(* Proof: by gcd_park_decomposition *) +val gcd_park_decompose = store_thm( + "gcd_park_decompose", + ``!m n. 0 < m /\ 0 < n ==> let a = park m n in let b = gcd m n DIV a in + (gcd m n = a * b) /\ coprime a b``, + metis_tac[gcd_park_decomposition]); + +(* +For the example: +total_prime_divisors m n = {2; 3; 5; 7; 11} s = {2^7; 3^7; 5^4; 7^4; 11^4} with MAX +sm = (prime_divisors m) DIFF (park_on m n) = {2; 7}, u = IMAGE (\p. p ** ppidx m) sm = {2^7; 7^4} +sn = (prime_divisors n) DIFF (park_off m n) = {3; 5; 11}, v = IMAGE (\p. p ** ppidx n) sn = {3^7; 5^4; 11^4} +*) + +(* Theorem: let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n) in + let u = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)) in + let v = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)) in + 0 < m /\ 0 < n ==> s =|= u # v *) +(* Proof: + This is to show: + (1) s = u UNION v + By EXTENSION, this is to show: + (a) x IN s ==> x IN u \/ x IN v + Note x IN s + ==> ?p. p IN total_prime_divisors m n /\ + (x = p ** MAX (ppidx m) (ppidx n)) by IN_IMAGE + By total_prime_divisors_element, + + If p IN prime_divisors m, + Then prime p /\ p divides m by prime_divisors_element + If p IN park_on m n, + Then p IN prime_divisors n /\ + ppidx m <= ppidx n by park_on_element + ==> MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF + Note DISJOINT (park_on m n) (park_off m n) by park_on_off_partition + Thus p NOTIN park_off m n by IN_DISJOINT + ==> p IN prime_divisors n DIFF park_off m n by IN_DIFF + Therefore x IN v by IN_IMAGE + If p NOTIN park_on m n, + Then p IN prime_divisors m DIFF park_on m n by IN_DIFF + By park_on_element, either [1] or [2]: + [1] p NOTIN prime_divisors n + Then ppidx n = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < n + ==> MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF + Therefore x IN u by IN_IMAGE + [2] ~(ppidx m <= ppidx n) + Then MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF + Therefore x IN u by IN_IMAGE + + If p IN prime_divisors n, + Then prime p /\ p divides n by prime_divisors_element + If p IN park_off m n, + Then p IN prime_divisors m /\ + ppidx n < ppidx m by park_off_element + ==> MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF + Note DISJOINT (park_on m n) (park_off m n) by park_on_off_partition + Thus p NOTIN park_on m n by IN_DISJOINT + ==> p IN prime_divisors m DIFF park_on m n by IN_DIFF + Therefore x IN u by IN_IMAGE + If p NOTIN park_off m n, + Then p IN prime_divisors n DIFF park_off m n by IN_DIFF + By park_off_element, either [1] or [2]: + [1] p NOTIN prime_divisors m + Then ppidx m = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < m + ==> MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF + Therefore x IN v by IN_IMAGE + [2] ~(ppidx n < ppidx m) + Then MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF + Therefore x IN v by IN_IMAGE + + (b) x IN u ==> x IN s + Note x IN u + ==> ?p. p IN prime_divisors m DIFF park_on m n /\ + (x = p ** ppidx m) by IN_IMAGE + Thus p IN prime_divisors m /\ p NOTIN park_on m n by IN_DIFF + Note p IN total_prime_divisors m n by total_prime_divisors_element + By park_on_element, either [1] or [2]: + [1] p NOTIN prime_divisors n + Then ppidx n = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < n + ==> MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF + Therefore x IN u by IN_IMAGE + [2] ~(ppidx m <= ppidx n) + Then MAX (ppidx m) (ppidx n) = ppidx m by MAX_DEF + Therefore x IN u by IN_IMAGE + + (c) x IN v ==> x IN s + Note x IN v + ==> ?p. p IN prime_divisors n DIFF park_off m n /\ + (x = p ** ppidx n) by IN_IMAGE + Thus p IN prime_divisors n /\ p NOTIN park_off m n by IN_DIFF + Note p IN total_prime_divisors m n by total_prime_divisors_element + By park_off_element, either [1] or [2]: + [1] p NOTIN prime_divisors m + Then ppidx m = 0 by prime_divisors_element, prime_power_index_eq_0, 0 < m + ==> MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF + Therefore x IN v by IN_IMAGE + [2] ~(ppidx n < ppidx m) + Then MAX (ppidx m) (ppidx n) = ppidx n by MAX_DEF + Therefore x IN v by IN_IMAGE + + (2) DISJOINT u v + This is to show: u INTER v = {} by DISJOINT_DEF + By contradiction, suppse u INTER v <> {}. + Then ?x. x IN u /\ x IN v by MEMBER_NOT_EMPTY, IN_INTER + Note x IN u + ==> ?p. p IN prime_divisors m DIFF park_on m n /\ + (x = p ** ppidx m) by IN_IMAGE + and x IN v + ==> ?q. q IN prime_divisors n DIFF park_off m n /\ + (x = q ** prime_power_index q n) by IN_IMAGE + Thus p IN prime_divisors m /\ p NOTIN park_on m n by IN_DIFF + and q IN prime_divisors n /\ q NOTIN park_off m n by IN_DIFF [1] + Now prime p /\ prime q /\ p divides m by prime_divisors_element + and 0 < ppidx m by prime_power_index_pos, p divides m, 0 < m + ==> p = q by prime_powers_eq + Thus p IN common_prime_divisors m n by common_prime_divisors_element, [1] + ==> p IN park_on m n \/ p IN park_off m n by park_on_off_partition, IN_UNION + This is a contradiction with [1]. +*) +val park_on_off_total_image_partition = store_thm( + "park_on_off_total_image_partition", + ``!m n. let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n) in + let u = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)) in + let v = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)) in + 0 < m /\ 0 < n ==> s =|= u # v``, + rpt strip_tac >> + qabbrev_tac `f = \p:num. p ** MAX (ppidx m) (ppidx n)` >> + qabbrev_tac `f1 = \p:num. p ** ppidx m` >> + qabbrev_tac `f2 = \p:num. p ** ppidx n` >> + rw_tac std_ss[] >| [ + rw[EXTENSION, EQ_IMP_THM] >| [ + `?p. p IN total_prime_divisors m n /\ (x = p ** MAX (ppidx m) (ppidx n))` by metis_tac[IN_IMAGE] >> + `p IN prime_divisors m \/ p IN prime_divisors n` by rw[GSYM total_prime_divisors_element] >| [ + `prime p /\ p divides m` by metis_tac[prime_divisors_element] >> + Cases_on `p IN park_on m n` >| [ + `p IN prime_divisors n /\ ppidx m <= ppidx n` by metis_tac[park_on_element] >> + `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> + `p NOTIN park_off m n` by metis_tac[park_on_off_partition, IN_DISJOINT] >> + `p IN prime_divisors n DIFF park_off m n` by rw[] >> + metis_tac[IN_IMAGE], + `p IN prime_divisors m DIFF park_on m n` by rw[] >> + `p NOTIN prime_divisors n \/ ~(ppidx m <= ppidx n)` by metis_tac[park_on_element] >| [ + `ppidx n = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> + `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE], + `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE] + ] + ], + `prime p /\ p divides n` by metis_tac[prime_divisors_element] >> + Cases_on `p IN park_off m n` >| [ + `p IN prime_divisors m /\ ppidx n < ppidx m` by metis_tac[park_off_element] >> + `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> + `p NOTIN park_on m n` by metis_tac[park_on_off_partition, IN_DISJOINT] >> + `p IN prime_divisors m DIFF park_on m n` by rw[] >> + metis_tac[IN_IMAGE], + `p IN prime_divisors n DIFF park_off m n` by rw[] >> + `p NOTIN prime_divisors m \/ ~(ppidx n < ppidx m)` by metis_tac[park_off_element] >| [ + `ppidx m = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> + `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE], + `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE] + ] + ] + ], + `?p. p IN prime_divisors m DIFF park_on m n /\ (x = p ** ppidx m)` by prove_tac[IN_IMAGE] >> + `p IN prime_divisors m /\ p NOTIN park_on m n` by metis_tac[IN_DIFF] >> + `p IN total_prime_divisors m n` by rw[total_prime_divisors_element] >> + `p NOTIN prime_divisors n \/ ~(ppidx m <= ppidx n)` by metis_tac[park_on_element] >| [ + `ppidx n = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> + `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE], + `MAX (ppidx m) (ppidx n) = ppidx m` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE] + ], + `?p. p IN prime_divisors n DIFF park_off m n /\ (x = p ** ppidx n)` by prove_tac[IN_IMAGE] >> + `p IN prime_divisors n /\ p NOTIN park_off m n` by metis_tac[IN_DIFF] >> + `p IN total_prime_divisors m n` by rw[total_prime_divisors_element] >> + `p NOTIN prime_divisors m \/ ~(ppidx n < ppidx m)` by metis_tac[park_off_element] >| [ + `ppidx m = 0` by metis_tac[prime_divisors_element, prime_power_index_eq_0] >> + `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE], + `MAX (ppidx m) (ppidx n) = ppidx n` by rw[MAX_DEF] >> + metis_tac[IN_IMAGE] + ] + ], + rw[DISJOINT_DEF] >> + spose_not_then strip_assume_tac >> + `?x. x IN u /\ x IN v` by metis_tac[MEMBER_NOT_EMPTY, IN_INTER] >> + `?p. p IN prime_divisors m DIFF park_on m n /\ (x = p ** ppidx m)` by prove_tac[IN_IMAGE] >> + `?q. q IN prime_divisors n DIFF park_off m n /\ (x = q ** prime_power_index q n)` by prove_tac[IN_IMAGE] >> + `p IN prime_divisors m /\ p NOTIN park_on m n` by metis_tac[IN_DIFF] >> + `q IN prime_divisors n /\ q NOTIN park_off m n` by metis_tac[IN_DIFF] >> + `prime p /\ prime q /\ p divides m` by metis_tac[prime_divisors_element] >> + `0 < ppidx m` by rw[prime_power_index_pos] >> + `p = q` by metis_tac[prime_powers_eq] >> + `p IN common_prime_divisors m n` by rw[common_prime_divisors_element] >> + metis_tac[park_on_off_partition, IN_UNION] + ]); + +(* Theorem: 0 < m /\ 0 < n ==> + let a = park m n in let b = gcd m n DIV a in + let p = m DIV a in let q = (a * n) DIV (gcd m n) in + (b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n))) /\ + (p = PROD_SET (IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)))) /\ + (q = PROD_SET (IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)))) /\ + (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q) *) +(* Proof: + Let s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n), + u = IMAGE (\p. p ** ppidx m) (park_on m n), + v = IMAGE (\p. p ** ppidx n) (park_off m n), + h = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n)), + k = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n)), + a = PROD_SET u, b = PROD_SET v, c = PROD_SET s, p = PROD_SET h, q = PROD_SET k + x = IMAGE (\p. p ** ppidx m) (prime_divisors m), + y = IMAGE (\p. p ** ppidx n) (prime_divisors n), + Let g = gcd m n. + + Step 1: GCD + Note a = park m n by notation + and g = a * b by gcd_park_decomposition + + Step 2: LCM + Note c = lcm m n by lcm_prime_factorisation + Note s =|= h # k by park_on_off_total_image_partition + and FINITE (total_prime_divisors m n) by total_prime_divisors_finite, 0 < m, 0 < n + ==> FINITE s by IMAGE_FINITE + also PAIRWISE_COPRIME s by total_prime_divisors_max_image_pairwise_coprime + Thus (c = p * q) /\ coprime p q by pairwise_coprime_prod_set_partition + + Step 3: Identities + Note m = PROD_SET x by basic_prime_factorisation + n = PROD_SET y by basic_prime_factorisation + + For the identity: m = a * p + We need: PROD_SET x = PROD_SET u * PROD_SET h + This requires: x = u UNION h /\ DISJOINT u h, i.e. x =|= u # h + or partition: (prime_divisors m) --> (park_on m n) and (prime_divisors m) DIFF (park_on m n) + + Claim: m = a * p + Proof: Claim: h = x DIFF u + Proof: Let pk = park_on m n, pm = prime_divisors m, f = \p. p ** ppidx m. + Note pk SUBSET pm by park_on_element, prime_divisors_element, SUBSET_DEF + ==> INJ f pm UNIV by INJ_DEF, prime_divisors_element, + prime_power_index_pos, prime_powers_eq + x DIFF u + = (IMAGE f pm) DIFF (IMAGE f pk) by notation + = IMAGE f (pm DIFF pk) by IMAGE_DIFF + = h by notation + Note FINITE x by prime_divisors_finite, IMAGE_FINITE + and u SUBSET x by SUBSET_DEF, IMAGE_SUBSET + Thus x =|= u # h by partition_by_subset + ==> m = a * p by PROD_SET_PRODUCT_BY_PARTITION + + For the identity: n = b * q + We need: PROD_SET y = PROD_SET v * PROD_SET k + This requires: y = v UNION k /\ DISJOINT v k, i.e y =|= v # k + or partition: (prime_divisors n) --> (park_off m n) and (prime_divisors n) DIFF (park_off m n) + + Claim: n = b * q + Proof: Claim: k = y DIFF v + Proof: Let pk = park_off m n, pn = prime_divisors n, f = \p. p ** ppidx n. + Note pk SUBSET pn by park_off_element, prime_divisors_element, SUBSET_DEF + ==> INJ f pn UNIV by INJ_DEF, prime_divisors_element, + prime_power_index_pos, prime_powers_eq + y DIFF v + = (IMAGE f pn) DIFF (IMAGE f pk) by notation + = IMAGE f (pn DIFF pk) by IMAGE_DIFF + = k by notation + Note FINITE y by prime_divisors_finite, IMAGE_FINITE + and v SUBSET y by SUBSET_DEF, IMAGE_SUBSET + Thus y =|= v # k by partition_by_subset + ==> n = b * q by PROD_SET_PRODUCT_BY_PARTITION + + Proof better: + Note m * n = g * c by GCD_LCM + = (a * b) * (p * q) by above + = (a * p) * (b * q) by MULT_COMM, MULT_ASSOC + = m * (b * q) by m = a * p + Thus n = b * q by MULT_LEFT_CANCEL, 0 < m + + Thus g <> 0 /\ c <> 0 by GCD_EQ_0, LCM_EQ_0, m <> 0, n <> 0 + ==> p <> 0 /\ a <> 0 by MULT_EQ_0 + ==> b = g DIV a by DIV_SOLVE_COMM, 0 < a + ==> p = m DIV a by DIV_SOLVE_COMM, 0 < a + and q = c DIV p by DIV_SOLVE_COMM, 0 < p + Note g divides n by GCD_IS_GREATEST_COMMON_DIVISOR + so g divides a * n by DIVIDES_MULTIPLE + or a * n = a * (b * q) by n = b * q from Claim 2 + = (a * b) * q by MULT_ASSOC + = g * q by g = a * b + = q * g by MULT_COMM + so g divides a * n by divides_def + Thus q = c DIV p by above + = ((m * n) DIV g) DIV p by lcm_def, m <> 0, n <> 0 + = (m * n) DIV (g * p) by DIV_DIV_DIV_MULT, 0 < g, 0 < p + = ((a * p) * n) DIV (g * p) by m = a * p, Claim 1 + = (p * (a * n)) DIV (p * g) by MULT_COMM, MULT_ASSOC + = (a * n) DIV g by DIV_COMMON_FACTOR, 0 < p, g divides a * n + + This gives all the assertions: + (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ + (m = a * p) /\ (n = b * q) by MULT_COMM +*) + +Theorem lcm_park_decomposition: + !m n. + 0 < m /\ 0 < n ==> + let a = park m n ; b = gcd m n DIV a ; + p = m DIV a ; q = (a * n) DIV (gcd m n) + in + b = PROD_SET (IMAGE (\p. p ** ppidx n) (park_off m n)) /\ + p = PROD_SET (IMAGE (\p. p ** ppidx m) + ((prime_divisors m) DIFF (park_on m n))) /\ + q = PROD_SET (IMAGE (\p. p ** ppidx n) + ((prime_divisors n) DIFF (park_off m n))) /\ + lcm m n = p * q /\ coprime p q /\ gcd m n = a * b /\ m = a * p /\ + n = b * q +Proof + rpt strip_tac >> + qabbrev_tac ‘s = IMAGE (\p. p ** MAX (ppidx m) (ppidx n)) (total_prime_divisors m n)’ >> + qabbrev_tac ‘u = IMAGE (\p. p ** ppidx m) (park_on m n)’ >> + qabbrev_tac ‘v = IMAGE (\p. p ** ppidx n) (park_off m n)’ >> + qabbrev_tac ‘h = IMAGE (\p. p ** ppidx m) ((prime_divisors m) DIFF (park_on m n))’ >> + qabbrev_tac ‘k = IMAGE (\p. p ** ppidx n) ((prime_divisors n) DIFF (park_off m n))’ >> + qabbrev_tac ‘a = PROD_SET u’ >> + qabbrev_tac ‘b = PROD_SET v’ >> + qabbrev_tac ‘c = PROD_SET s’ >> + qabbrev_tac ‘p = PROD_SET h’ >> + qabbrev_tac ‘q = PROD_SET k’ >> + qabbrev_tac ‘x = IMAGE (\p. p ** ppidx m) (prime_divisors m)’ >> + qabbrev_tac ‘y = IMAGE (\p. p ** ppidx n) (prime_divisors n)’ >> + qabbrev_tac ‘g = gcd m n’ >> + ‘a = park m n’ by rw[Abbr‘a’] >> + ‘g = a * b’ by metis_tac[gcd_park_decomposition] >> + ‘c = lcm m n’ by rw[lcm_prime_factorisation, Abbr‘c’, Abbr‘s’] >> + ‘s =|= h # k’ by metis_tac[park_on_off_total_image_partition] >> + ‘FINITE s’ by rw[total_prime_divisors_finite, Abbr‘s’] >> + ‘PAIRWISE_COPRIME s’ + by metis_tac[total_prime_divisors_max_image_pairwise_coprime] >> + ‘(c = p * q) /\ coprime p q’ + by (simp[Abbr‘p’, Abbr‘q’, Abbr‘c’] >> + metis_tac[pairwise_coprime_prod_set_partition]) >> + ‘m = PROD_SET x’ by rw[basic_prime_factorisation, Abbr‘x’] >> + ‘n = PROD_SET y’ by rw[basic_prime_factorisation, Abbr‘y’] >> + ‘m = a * p’ + by (‘h = x DIFF u’ + by (‘park_on m n SUBSET prime_divisors m’ + by metis_tac[park_on_element,prime_divisors_element,SUBSET_DEF] >> + ‘INJ (\p. p ** ppidx m) (prime_divisors m) UNIV’ + by (rw[INJ_DEF] >> + metis_tac[prime_divisors_element, prime_power_index_pos, + prime_powers_eq]) >> + metis_tac[IMAGE_DIFF]) >> + ‘FINITE x’ by rw[prime_divisors_finite, Abbr‘x’] >> + ‘u SUBSET x’ by rw[SUBSET_DEF, Abbr‘u’, Abbr‘x’] >> + ‘x =|= u # h’ by metis_tac[partition_by_subset] >> + metis_tac[PROD_SET_PRODUCT_BY_PARTITION]) >> + ‘n = b * q’ + by (‘m * n = g * c’ by metis_tac[GCD_LCM] >> + ‘_ = (a * p) * (b * q)’ by rw[] >> + ‘_ = m * (b * q)’ by rw[] >> + metis_tac[MULT_LEFT_CANCEL, NOT_ZERO_LT_ZERO]) >> + ‘m <> 0 /\ n <> 0’ by decide_tac >> + ‘g <> 0 /\ c <> 0’ by metis_tac[GCD_EQ_0, LCM_EQ_0] >> + ‘p <> 0 /\ a <> 0’ by metis_tac[MULT_EQ_0] >> + ‘b = g DIV a’ by metis_tac[DIV_SOLVE_COMM, NOT_ZERO_LT_ZERO] >> + ‘p = m DIV a’ by metis_tac[DIV_SOLVE_COMM, NOT_ZERO_LT_ZERO] >> + ‘q = c DIV p’ by metis_tac[DIV_SOLVE_COMM, NOT_ZERO_LT_ZERO] >> + ‘g divides a * n’ by metis_tac[divides_def, MULT_ASSOC, MULT_COMM] >> + ‘c = (m * n) DIV g’ by metis_tac[lcm_def] >> + ‘q = (m * n) DIV (g * p)’ by metis_tac[DIV_DIV_DIV_MULT, NOT_ZERO_LT_ZERO] >> + ‘_ = (p * (a * n)) DIV (p * g)’ by metis_tac[MULT_COMM, MULT_ASSOC] >> + ‘_ = (a * n) DIV g’ by metis_tac[DIV_COMMON_FACTOR, NOT_ZERO_LT_ZERO] >> + metis_tac[] +QED + +(* Theorem: 0 < m /\ 0 < n ==> let a = park m n in let p = m DIV a in let q = (a * n) DIV (gcd m n) in + (lcm m n = p * q) /\ coprime p q *) +(* Proof: by lcm_park_decomposition *) +val lcm_park_decompose = store_thm( + "lcm_park_decompose", + ``!m n. 0 < m /\ 0 < n ==> let a = park m n in let p = m DIV a in let q = (a * n) DIV (gcd m n) in + (lcm m n = p * q) /\ coprime p q``, + metis_tac[lcm_park_decomposition]); + +(* Theorem: 0 < m /\ 0 < n ==> + let a = park m n in let b = gcd m n DIV a in + let p = m DIV a in let q = (a * n) DIV (gcd m n) in + (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q) *) +(* Proof: by lcm_park_decomposition *) +val lcm_gcd_park_decompose = store_thm( + "lcm_gcd_park_decompose", + ``!m n. 0 < m /\ 0 < n ==> + let a = park m n in let b = gcd m n DIV a in + let p = m DIV a in let q = (a * n) DIV (gcd m n) in + (lcm m n = p * q) /\ coprime p q /\ (gcd m n = a * b) /\ (m = a * p) /\ (n = b * q)``, + metis_tac[lcm_park_decomposition]); + +(* ------------------------------------------------------------------------- *) +(* Consecutive LCM Recurrence *) +(* ------------------------------------------------------------------------- *) + +(* +> optionTheory.some_def; +val it = |- !P. $some P = if ?x. P x then SOME (@x. P x) else NONE: thm +*) + +(* +Cannot do this: Definition is schematic in the following variables: p + +val lcm_fun_def = Define` + lcm_fun n = if n = 0 then 1 + else if n = 1 then 1 + else if ?p k. 0 < k /\ prime p /\ (n = p ** k) then p * lcm_fun (n - 1) + else lcm_fun (n - 1) +`; +*) + +(* NOT this: +val lcm_fun_def = Define` + (lcm_fun 1 = 1) /\ + (lcm_fun (SUC n) = case some p. ?k. (SUC n = p ** k) of + SOME p => p * (lcm_fun n) + | NONE => lcm_fun n) +`; +*) + +(* +Question: don't know how to prove termination +(* Define the B(n) function *) +val lcm_fun_def = Define` + (lcm_fun 1 = 1) /\ + (lcm_fun n = case some p. ?k. 0 < k /\ prime p /\ (n = p ** k) of + SOME p => p * (lcm_fun (n - 1)) + | NONE => lcm_fun (n - 1)) +`; + +(* use a measure that is decreasing *) +e (WF_REL_TAC `measure (\n k. k * n)`); +e (rpt strip_tac); +*) + +(* Define the Consecutive LCM Function *) +val lcm_fun_def = Define` + (lcm_fun 0 = 1) /\ + (lcm_fun (SUC n) = if n = 0 then 1 else + case some p. ?k. 0 < k /\ prime p /\ (SUC n = p ** k) of + SOME p => p * (lcm_fun n) + | NONE => lcm_fun n) +`; + +(* Another possible definition -- but need to work with pairs: + +val lcm_fun_def = Define` + (lcm_fun 0 = 1) /\ + (lcm_fun (SUC n) = if n = 0 then 1 else + case some (p, k). 0 < k /\ prime p /\ (SUC n = p ** k) of + SOME (p, k) => p * (lcm_fun n) + | NONE => lcm_fun n) +`; + +By prime_powers_eq, when SOME, such (p, k) exists uniquely, or NONE. +*) + +(* Get components of definition *) +val lcm_fun_0 = save_thm("lcm_fun_0", lcm_fun_def |> CONJUNCT1); +(* val lcm_fun_0 = |- lcm_fun 0 = 1: thm *) +val lcm_fun_SUC = save_thm("lcm_fun_SUC", lcm_fun_def |> CONJUNCT2); +(* val lcm_fun_SUC = |- !n. lcm_fun (SUC n) = if n = 0 then 1 else + case some p. ?k. SUC n = p ** k of + NONE => lcm_fun n | SOME p => p * lcm_fun n: thm *) + +(* Theorem: lcm_fun 1 = 1 *) +(* Proof: + lcm_fun 1 + = lcm_fun (SUC 0) by ONE + = 1 by lcm_fun_def +*) +val lcm_fun_1 = store_thm( + "lcm_fun_1", + ``lcm_fun 1 = 1``, + rw_tac bool_ss[lcm_fun_def, ONE]); + +(* Theorem: lcm_fun 2 = 2 *) +(* Proof: + Note 2 = 2 ** 1 by EXP_1 + and prime 2 by PRIME_2 + and 0 < k /\ prime p /\ (2 ** 1 = p ** k) + ==> (p = 2) /\ (k = 1) by prime_powers_eq + + lcm_fun 2 + = lcm_fun (SUC 1) by TWO + = case some p. ?k. 0 < k /\ prime p /\ (SUC 1 = p ** k) of + SOME p => p * (lcm_fun 1) + | NONE => lcm_fun 1) by lcm_fun_def + = SOME 2 by some_intro, above + = 2 * (lcm_fun 1) by definition + = 2 * 1 by lcm_fun_1 + = 2 by arithmetic +*) +Theorem lcm_fun_2: + lcm_fun 2 = 2 +Proof + simp_tac bool_ss[lcm_fun_def, lcm_fun_1, TWO] >> + `prime 2 /\ (2 = 2 ** 1)` by rw[PRIME_2] >> + DEEP_INTRO_TAC some_intro >> + rw_tac std_ss[] + >- metis_tac[prime_powers_eq] >> + metis_tac[DECIDE``0 <> 1``] +QED + +(* Theorem: prime p /\ (?k. 0 < k /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = p * lcm_fun n) *) +(* Proof: by lcm_fun_def, prime_powers_eq *) +Theorem lcm_fun_suc_some: + !n p. prime p /\ (?k. 0 < k /\ (SUC n = p ** k)) ==> + lcm_fun (SUC n) = p * lcm_fun n +Proof + rw[lcm_fun_def] >> + DEEP_INTRO_TAC some_intro >> + rw_tac std_ss[] >> + metis_tac[prime_powers_eq, DECIDE “~(0 < 0)”] +QED + +(* Theorem: ~(?p k. 0 < k /\ prime p /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = lcm_fun n) *) +(* Proof: by lcm_fun_def *) +val lcm_fun_suc_none = store_thm( + "lcm_fun_suc_none", + ``!n. ~(?p k. 0 < k /\ prime p /\ (SUC n = p ** k)) ==> (lcm_fun (SUC n) = lcm_fun n)``, + rw[lcm_fun_def] >> + DEEP_INTRO_TAC some_intro >> + rw_tac std_ss[] >> + `k <> 0` by decide_tac >> + metis_tac[]); + +(* Theorem: prime p /\ l <> [] /\ POSITIVE l ==> !x. MEM x l ==> ppidx x <= ppidx (list_lcm l) *) +(* Proof: + Note ppidx (list_lcm l) = MAX_LIST (MAP ppidx l) by list_lcm_prime_power_index + and MEM (ppidx x) (MAP ppidx l) by MEM_MAP, MEM x l + Thus ppidx x <= ppidx (list_lcm l) by MAX_LIST_PROPERTY +*) +val list_lcm_prime_power_index_lower = store_thm( + "list_lcm_prime_power_index_lower", + ``!l p. prime p /\ l <> [] /\ POSITIVE l ==> !x. MEM x l ==> ppidx x <= ppidx (list_lcm l)``, + rpt strip_tac >> + `ppidx (list_lcm l) = MAX_LIST (MAP ppidx l)` by rw[list_lcm_prime_power_index] >> + `MEM (ppidx x) (MAP ppidx l)` by metis_tac[MEM_MAP] >> + rw[MAX_LIST_PROPERTY]); + +(* +The keys to show list_lcm_eq_lcm_fun are: +(1) Given a number n and a prime p that divides n, you can extract all the p's in n, + giving n = (p ** k) * q for some k, and coprime p q. This is FACTOR_OUT_PRIME, or FACTOR_OUT_POWER. +(2) To figure out the LCM, use the GCD_LCM identity, i.e. figure out first the GCD. + +Therefore, let m = consecutive LCM. +Consider given two number m, n; and a prime p with p divides n. +By (1), n = (p ** k) * q, with coprime p q. +If q > 1, then n = a * b where a, b are both less than n, and coprime a b: take a = p ** k, b = q. + Now, if a divides m, and b divides m --- which is the case when m = consecutive LCM, + By coprime a b, (a * b) divides m, or n divides m, + or gcd m n = n by divides_iff_gcd_fix + or lcm m n = (m * n) DIV (gcd m n) = (m * n) DIV n = m (or directly by divides_iff_lcm_fix) +If q = 1, then n is a pure prime p power: n = p ** k, with k > 0. + Now, m = (p ** j) * t with coprime p t, although it may be that j = 0. + For list LCM, j <= k, since the numbers are consecutive. In fact, j = k - 1 + Thus n = (p ** j) * p, and gcd m n = (p ** j) gcd p t = (p ** j) by GCD_COMMON_FACTOR + or lcm m n = (m * n) DIV (gcd m n) + = m * (n DIV (p ** j)) + = m * ((p ** j) * p) DIV (p ** j) + = m * p = p * m +*) + +(* Theorem: prime p /\ (n + 2 = p ** k) ==> (list_lcm [1 .. (n + 2)] = p * list_lcm [1 .. (n + 1)]) *) +(* Proof: + Note n + 2 = SUC (SUC n) <> 1 by ADD1, TWO + Thus p ** k <> 1, or k <> 0 by EXP_EQ_1 + ==> ?h. k = SUC h by num_CASES + and n + 2 = x ** SUC h by above + + Let l = [1 .. (n + 1)], m = list_lcm l. + Note POSITIVE l by leibniz_vertical_pos, EVERY_MEM + Now h < SUC h = k by LESS_SUC + so p ** h < p ** k = n + 2 by EXP_BASE_LT_MONO, 1 < p + ==> MEM (p ** h) l by leibniz_vertical_mem + Note l <> [] by leibniz_vertical_not_nil + so ppidx (p ** h) <= ppidx m by list_lcm_prime_power_index_lower + or h <= ppidx m by prime_power_index_prime_power + + Claim: ppidx m <= h + Proof: By contradiction, suppose h < ppidx m. + Then k <= ppidx m by k = SUC h + and p ** k divides p ** (ppidx m) by power_divides_iff + But p ** (ppidx m) divides m by prime_power_factor_divides + so p ** k divides m by DIVIDES_TRANS + ==> ?z. MEM z l /\ (n + 2) divides z by list_lcm_prime_power_factor_member + or (n + 2) <= z by DIVIDES_LE, 0 < z, all members are positive + Now z <= n + 1 by leibniz_vertical_mem + This leads to a contradiction: n + 2 <= n + 1. + + Therefore ppidx m = h by h <= ppidx m /\ ppidx m <= h, by Claim. + + list_lcm [1 .. (n + 2)] + = list_lcm (SNOC (n + 2) l) by leibniz_vertical_snoc, n + 2 = SUC (n + 1) + = lcm (n + 2) m by list_lcm_snoc + = p * m by lcm_special_for_prime_power +*) +val list_lcm_with_last_prime_power = store_thm( + "list_lcm_with_last_prime_power", + ``!n p k. prime p /\ (n + 2 = p ** k) ==> (list_lcm [1 .. (n + 2)] = p * list_lcm [1 .. (n + 1)])``, + rpt strip_tac >> + `n + 2 <> 1` by decide_tac >> + `0 <> k` by metis_tac[EXP_EQ_1] >> + `?h. k = SUC h` by metis_tac[num_CASES] >> + qabbrev_tac `l = leibniz_vertical n` >> + qabbrev_tac `m = list_lcm l` >> + `POSITIVE l` by rw[leibniz_vertical_pos, EVERY_MEM, Abbr`l`] >> + `h < k` by rw[] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `p ** h < p ** k` by rw[EXP_BASE_LT_MONO] >> + `0 < p ** h` by rw[PRIME_POS, EXP_POS] >> + `p ** h <= n + 1` by decide_tac >> + `MEM (p ** h) l` by rw[leibniz_vertical_mem, Abbr`l`] >> + `ppidx (p ** h) = h` by rw[prime_power_index_prime_power] >> + `l <> []` by rw[leibniz_vertical_not_nil, Abbr`l`] >> + `h <= ppidx m` by metis_tac[list_lcm_prime_power_index_lower] >> + `ppidx m <= h` by + (spose_not_then strip_assume_tac >> + `k <= ppidx m` by decide_tac >> + `p ** k divides p ** (ppidx m)` by rw[power_divides_iff] >> + `p ** (ppidx m) divides m` by rw[prime_power_factor_divides] >> + `p ** k divides m` by metis_tac[DIVIDES_TRANS] >> + `?z. MEM z l /\ (n + 2) divides z` by metis_tac[list_lcm_prime_power_factor_member] >> + `(n + 2) <= z` by rw[DIVIDES_LE] >> + `z <= n + 1` by metis_tac[leibniz_vertical_mem, Abbr`l`] >> + decide_tac) >> + `h = ppidx m` by decide_tac >> + `list_lcm [1 .. (n + 2)] = list_lcm (SNOC (n + 2) l)` by rw[GSYM leibniz_vertical_snoc, Abbr`l`] >> + `_ = lcm (n + 2) m` by rw[list_lcm_snoc, Abbr`m`] >> + `_ = p * m` by rw[lcm_special_for_prime_power] >> + rw[]); + +(* Theorem: (!p k. (k = 0) \/ ~prime p \/ n + 2 <> p ** k) ==> + (list_lcm [1 .. (n + 2)] = list_lcm [1 .. (n + 1)]) *) +(* Proof: + Note 1 < n + 2, + ==> ?a b. (n + 2 = a * b) /\ coprime a b /\ + 1 < a /\ 1 < b /\ a < n + 2 /\ b < n + 2 by prime_power_or_coprime_factors + or 0 < a /\ 0 < b /\ a <= n + 1 /\ b <= n + 1 by arithmetic + Let l = leibniz_vertical n, m = list_lcm l. + Then MEM a l and MEM b l by leibniz_vertical_mem + and a divides m /\ b divides m by list_lcm_is_common_multiple + ==> (n + 2) divides m by coprime_product_divides, coprime a b + + list_lcm (leibniz_vertical (n + 1)) + = list_lcm (SNOC (n + 2) l) by leibniz_vertical_snoc + = lcm (n + 2) m by list_lcm_snoc + = m by divides_iff_lcm_fix +*) +val list_lcm_with_last_non_prime_power = store_thm( + "list_lcm_with_last_non_prime_power", + ``!n. (!p k. (k = 0) \/ ~prime p \/ n + 2 <> p ** k) ==> + (list_lcm [1 .. (n + 2)] = list_lcm [1 .. (n + 1)])``, + rpt strip_tac >> + `1 < n + 2` by decide_tac >> + `!k. ~(0 < k) = (k = 0)` by decide_tac >> + `?a b. (n + 2 = a * b) /\ coprime a b /\ 1 < a /\ 1 < b /\ a < n + 2 /\ b < n + 2` by metis_tac[prime_power_or_coprime_factors] >> + `0 < a /\ 0 < b /\ a <= n + 1 /\ b <= n + 1` by decide_tac >> + qabbrev_tac `l = leibniz_vertical n` >> + qabbrev_tac `m = list_lcm l` >> + `MEM a l /\ MEM b l` by rw[leibniz_vertical_mem, Abbr`l`] >> + `a divides m /\ b divides m` by rw[list_lcm_is_common_multiple, Abbr`m`] >> + `(n + 2) divides m` by rw[coprime_product_divides] >> + `list_lcm [1 .. (n + 2)] = list_lcm (SNOC (n + 2) l)` by rw[GSYM leibniz_vertical_snoc, Abbr`l`] >> + `_ = lcm (n + 2) m` by rw[list_lcm_snoc, Abbr`m`] >> + `_ = m` by rw[GSYM divides_iff_lcm_fix] >> + rw[]); + +(* Theorem: list_lcm [1 .. (n + 1)] = lcm_fun (n + 1) *) +(* Proof: + By induction on n. + Base: list_lcm [1 .. 0 + 1] = lcm_fun (0 + 1) + LHS = list_lcm [1 .. 0 + 1] + = list_lcm [1] by leibniz_vertical_0 + = 1 by list_lcm_sing + RHS = lcm_fun (0 + 1) + = lcm_fun 1 by ADD + = 1 = LHS by lcm_fun_1 + Step: list_lcm [1 .. n + 1] = lcm_fun (n + 1) ==> + list_lcm [1 .. SUC n + 1] = lcm_fun (SUC n + 1) + Note (SUC n) <> 0 by SUC_NOT_ZERO + and n + 2 = SUC (SUC n) by ADD1, TWO + By lcm_fun_def, this is to show: + list_lcm [1 .. SUC n + 1] = case some p. ?k. 0 < k /\ prime p /\ (SUC (SUC n) = p ** k) of + NONE => lcm_fun (SUC n) + | SOME p => p * lcm_fun (SUC n) + + If SOME, + Then 0 < k /\ prime p /\ SUC (SUC n) = p ** k + This is the case of perfect prime power. + list_lcm (leibniz_vertical (SUC n)) + = list_lcm (leibniz_vertical (n + 1)) by ADD1 + = p * list_lcm (leibniz_vertical n) by list_lcm_with_last_prime_power + = p * lcm_fun (SUC n) by induction hypothesis + If NONE, + Then !x k. ~(0 < k) \/ ~prime x \/ SUC (SUC n) <> x ** k + This is the case of non-perfect prime power. + list_lcm (leibniz_vertical (SUC n)) + = list_lcm (leibniz_vertical (n + 1)) by ADD1 + = list_lcm (leibniz_vertical n) by list_lcm_with_last_non_prime_power + = lcm_fun (SUC n) by induction hypothesis +*) +val list_lcm_eq_lcm_fun = store_thm( + "list_lcm_eq_lcm_fun", + ``!n. list_lcm [1 .. (n + 1)] = lcm_fun (n + 1)``, + Induct >- + rw[leibniz_vertical_0, list_lcm_sing, lcm_fun_1] >> + `(SUC n) + 1 = SUC (SUC n)` by rw[] >> + `list_lcm [1 .. SUC n + 1] = case some p. ?k. 0 < k /\ prime p /\ ((SUC n) + 1 = p ** k) of + NONE => lcm_fun (SUC n) + | SOME p => p * lcm_fun (SUC n)` suffices_by rw[lcm_fun_def] >> + `n + 2 = (SUC n) + 1` by rw[] >> + DEEP_INTRO_TAC some_intro >> + rw[] >- + metis_tac[list_lcm_with_last_prime_power, ADD1] >> + metis_tac[list_lcm_with_last_non_prime_power, ADD1]); + +(* This is a major milestone theorem! *) + +(* Theorem: 2 ** n <= lcm_fun (SUC n) *) +(* Proof: + Note 2 ** n <= list_lcm (leibniz_vertical n) by lcm_lower_bound + and list_lcm (leibniz_vertical n) = lcm_fun (SUC n) by list_lcm_eq_lcm_fun\ + so 2 ** n <= lcm_fun (SUC n) +*) +val lcm_fun_lower_bound = store_thm( + "lcm_fun_lower_bound", + ``!n. 2 ** n <= lcm_fun (n + 1)``, + rw[GSYM list_lcm_eq_lcm_fun, lcm_lower_bound]); + +(* Theorem: 0 < n ==> 2 ** (n - 1) <= lcm_fun n *) +(* Proof: + Note 0 < n ==> ?m. n = SUC m by num_CASES + or m = n - 1 by SUC_SUB1 + Apply lcm_fun_lower_bound, + put n = SUC m, and the result follows. +*) +val lcm_fun_lower_bound_alt = store_thm( + "lcm_fun_lower_bound_alt", + ``!n. 0 < n ==> 2 ** (n - 1) <= lcm_fun n``, + rpt strip_tac >> + `n <> 0` by decide_tac >> + `?m. n = SUC m` by metis_tac[num_CASES] >> + `(n - 1 = m) /\ (n = m + 1)` by decide_tac >> + metis_tac[lcm_fun_lower_bound]); + +(* Theorem: 0 < n /\ prime p /\ (SUC n = p ** ppidx (SUC n)) ==> + (ppidx (SUC n) = SUC (ppidx (list_lcm [1 .. n]))) *) +(* Proof: + Let z = SUC n, + Then z = p ** ppidx z by given + Note n <> 0 /\ z <> 1 by 0 < n + ==> ppidx z <> 0 by EXP_EQ_1, z <> 1 + ==> ?h. ppidx z = SUC h by num_CASES + + Let l = [1 .. n], m = list_lcm l, j = ppidx m. + Current goal is to show: SUC h = SUC j + which only need to show: h = j by INV_SUC_EQ + Note l <> [] by listRangeINC_NIL + and POSITIVE l by listRangeINC_MEM, [1] + Also 1 < p by ONE_LT_PRIME + + Claim: h <= j + Proof: Note h < SUC h by LESS_SUC + Thus p ** h < z = p ** SUC h by EXP_BASE_LT_MONO, 1 < p + ==> p ** h <= n by z = SUC n + Also 0 < p ** h by EXP_POS, 0 < p + ==> MEM (p ** h) l by listRangeINC_MEM, 0 < p ** h /\ p ** h <= n + Note ppidx (p ** h) = h by prime_power_index_prime_power + Thus h <= j = ppidx m by list_lcm_prime_power_index_lower, l <> [] + + Claim: j <= h + Proof: By contradiction, suppose h < j. + Then SUC h <= j by arithmetic + ==> z divides p ** j by power_divides_iff, 1 < p, z = p ** SUC h, SUC h <= j + But p ** j divides m by prime_power_factor_divides + ==> z divides m by DIVIDES_TRANS + Thus ?y. MEM y l /\ z divides y by list_lcm_prime_power_factor_member, l <> [] + Note 0 < y by all members of l, [1] + so z <= y by DIVIDES_LE, 0 < y + or SUC n <= y by z = SUC n + But ?u. n = u + 1 by num_CASES, ADD1, n <> 0 + so y <= n by listRangeINC_MEM, MEM y l + This leads to SUC n <= n, a contradiction. + + By these two claims, h = j. +*) +val prime_power_index_suc_special = store_thm( + "prime_power_index_suc_special", + ``!n p. 0 < n /\ prime p /\ (SUC n = p ** ppidx (SUC n)) ==> + (ppidx (SUC n) = SUC (ppidx (list_lcm [1 .. n])))``, + rpt strip_tac >> + qabbrev_tac `z = SUC n` >> + `n <> 0 /\ z <> 1` by rw[Abbr`z`] >> + `?h. ppidx z = SUC h` by metis_tac[EXP_EQ_1, num_CASES] >> + qabbrev_tac `l = [1 .. n]` >> + qabbrev_tac `m = list_lcm l` >> + qabbrev_tac `j = ppidx m` >> + `h <= j /\ j <= h` suffices_by rw[] >> + `l <> []` by rw[listRangeINC_NIL, Abbr`l`] >> + `POSITIVE l` by rw[Abbr`l`] >> + `1 < p` by rw[ONE_LT_PRIME] >> + rpt strip_tac >| [ + `h < SUC h` by rw[] >> + `p ** h < z` by metis_tac[EXP_BASE_LT_MONO] >> + `p ** h <= n` by rw[Abbr`z`] >> + `0 < p ** h` by rw[EXP_POS] >> + `MEM (p ** h) l` by rw[Abbr`l`] >> + metis_tac[prime_power_index_prime_power, list_lcm_prime_power_index_lower], + spose_not_then strip_assume_tac >> + `SUC h <= j` by decide_tac >> + `z divides p ** j` by metis_tac[power_divides_iff] >> + `p ** j divides m` by rw[prime_power_factor_divides, Abbr`j`] >> + `z divides m` by metis_tac[DIVIDES_TRANS] >> + `?y. MEM y l /\ z divides y` by metis_tac[list_lcm_prime_power_factor_member] >> + `SUC n <= y` by rw[DIVIDES_LE, Abbr`z`] >> + `y <= n` by metis_tac[listRangeINC_MEM] >> + decide_tac + ]); + +(* Theorem: 0 < n /\ prime p /\ (n + 1 = p ** ppidx (n + 1)) ==> + (ppidx (n + 1) = 1 + (ppidx (list_lcm [1 .. n]))) *) +(* Proof: by prime_power_index_suc_special, ADD1, ADD_COMM *) +val prime_power_index_suc_property = store_thm( + "prime_power_index_suc_property", + ``!n p. 0 < n /\ prime p /\ (n + 1 = p ** ppidx (n + 1)) ==> + (ppidx (n + 1) = 1 + (ppidx (list_lcm [1 .. n])))``, + metis_tac[prime_power_index_suc_special, ADD1, ADD_COMM]); + +(* ------------------------------------------------------------------------- *) +(* Consecutive LCM Recurrence - Rework *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: SING (prime_divisors (n + 1)) ==> + (list_lcm [1 .. (n + 1)] = CHOICE (prime_divisors (n + 1)) * list_lcm [1 .. n]) *) +(* Proof: + Let z = n + 1. + Then ?p. prime_divisors z = {p} by SING_DEF + By CHOICE_SING, this is to show: list_lcm [1 .. z] = p * list_lcm [1 .. n] + + Note prime p /\ (z = p ** ppidx z) by prime_divisors_sing_property, CHOICE_SING + and z <> 1 /\ n <> 0 by prime_divisors_1, NOT_SING_EMPTY, ADD + Note ppidx z <> 0 by EXP_EQ_1, z <> 1 + ==> ?h. ppidx z = SUC h by num_CASES, EXP + Thus z = p ** SUC h = p ** h * p by EXP, MULT_COMM + + Let m = list_lcm [1 .. n], j = ppidx m. + Note EVERY_POSITIVE l by listRangeINC_MEM, EVERY_MEM + so 0 < m by list_lcm_pos + ==> ?q. (m = p ** j * q) /\ + coprime p q by prime_power_index_eqn + Note 0 < n by n <> 0 + Thus SUC h = SUC j by prime_power_index_suc_special, ADD1, 0 < n + or h = j by INV_SUC_EQ + + list_lcm [1 .. z] + = lcm z m by list_lcm_suc + = p * m by lcm_special_for_prime_power +*) +Theorem list_lcm_by_last_prime_power: + !n. + SING (prime_divisors (n + 1)) ==> + list_lcm [1 .. (n + 1)] = + CHOICE (prime_divisors (n + 1)) * list_lcm [1 .. n] +Proof + rpt strip_tac >> + qabbrev_tac ‘z = n + 1’ >> + ‘?p. prime_divisors z = {p}’ by rw[GSYM SING_DEF] >> + rw[] >> + ‘prime p /\ (z = p ** ppidx z)’ by metis_tac[prime_divisors_sing_property, CHOICE_SING] >> + ‘z <> 1 /\ n <> 0’ by metis_tac[prime_divisors_1, NOT_SING_EMPTY, ADD] >> + ‘?h. ppidx z = SUC h’ by metis_tac[EXP_EQ_1, num_CASES] >> + qabbrev_tac ‘m = list_lcm [1 .. n]’ >> + qabbrev_tac ‘j = ppidx m’ >> + ‘0 < m’ by rw[list_lcm_pos, EVERY_MEM, Abbr‘m’] >> + ‘?q. (m = p ** j * q) /\ coprime p q’ by metis_tac[prime_power_index_eqn] >> + ‘0 < n’ by decide_tac >> + ‘SUC h = SUC j’ by metis_tac[prime_power_index_suc_special, ADD1] >> + ‘h = j’ by decide_tac >> + ‘list_lcm [1 .. z] = lcm z m’ by rw[list_lcm_suc, Abbr‘z’, Abbr‘m’] >> + ‘_ = p * m’ by metis_tac[lcm_special_for_prime_power] >> + rw[] +QED + +(* Theorem: ~ SING (prime_divisors (n + 1)) ==> (list_lcm [1 .. (n + 1)] = list_lcm [1 .. n]) *) +(* Proof: + Let z = n + 1, l = [1 .. n], m = list_lcm l. + The goal is to show: list_lcm [1 .. z] = m. + + If z = 1, + Then n = 0 by 1 = n + 1 + LHS = list_lcm [1 .. z] + = list_lcm [1 .. 1] by z = 1 + = list_lcm [1] by listRangeINC_SING + = 1 by list_lcm_sing + RHS = list_lcm [1 .. n] + = list_lcm [1 .. 0] by n = 0 + = list_lcm [] by listRangeINC_EMPTY + = 1 = LHS by list_lcm_nil + If z <> 1, + Note z <> 0, or 0 < z by z = n + 1 + ==> ?p. prime p /\ p divides z by PRIME_FACTOR, z <> 1 + and 0 < ppidx z by prime_power_index_pos, 0 < z + Let t = p ** ppidx z. + Then ?q. (z = t * q) /\ coprime p q by prime_power_index_eqn, 0 < z + ==> coprime t q by coprime_exp + Thus t <> 0 /\ q <> 0 by MULT_EQ_0, z <> 0 + and q <> 1 by prime_divisors_sing, MULT_RIGHT_1, ~SING (prime_divisors z) + Note p <> 1 by NOT_PRIME_1 + and t <> 1 by EXP_EQ_1, ppidx z <> 0 + Thus 0 < q /\ q < n + 1 by z = t * q = n + 1 + and 0 < t /\ t < n + 1 by z = t * q = n + 1 + + Then MEM q l by listRangeINC_MEM, 1 <= q <= n + and MEM t l by listRangeINC_MEM, 1 <= t <= n + ==> q divides m /\ t divides m by list_lcm_is_common_multiple + ==> q * t = z divides m by coprime_product_divides, coprime t q + + list_lcm [1 .. z] + = lcm z m by list_lcm_suc + = m by divides_iff_lcm_fix +*) + +Theorem list_lcm_by_last_non_prime_power: + !n. ~ SING (prime_divisors (n + 1)) ==> + list_lcm [1 .. (n + 1)] = list_lcm [1 .. n] +Proof + rpt strip_tac >> + qabbrev_tac `z = n + 1` >> + Cases_on `z = 1` >| [ + `n = 0` by rw[Abbr`z`] >> + `([1 .. z] = [1]) /\ ([1 .. n] = [])` by rw[listRangeINC_EMPTY] >> + rw[list_lcm_sing, list_lcm_nil], + `z <> 0 /\ 0 < z` by rw[Abbr`z`] >> + `?p. prime p /\ p divides z` by rw[PRIME_FACTOR] >> + `0 < ppidx z` by rw[prime_power_index_pos] >> + qabbrev_tac `t = p ** ppidx z` >> + `?q. (z = t * q) /\ coprime p q /\ coprime t q` + by metis_tac[prime_power_index_eqn, coprime_exp] >> + `t <> 0 /\ q <> 0` by metis_tac[MULT_EQ_0] >> + `q <> 1` by metis_tac[prime_divisors_sing, MULT_RIGHT_1] >> + `t <> 1` by metis_tac[EXP_EQ_1, NOT_PRIME_1, NOT_ZERO_LT_ZERO] >> + `0 < q /\ q < n + 1` by rw[Abbr`z`] >> + `0 < t /\ t < n + 1` by rw[Abbr`z`] >> + qabbrev_tac `l = [1 .. n]` >> + qabbrev_tac `m = list_lcm l` >> + `MEM q l /\ MEM t l` by rw[Abbr`l`] >> + `q divides m /\ t divides m` + by simp[list_lcm_is_common_multiple, Abbr`m`] >> + `z divides m` + by (simp[] >> metis_tac[coprime_sym, coprime_product_divides]) >> + `list_lcm [1 .. z] = lcm z m` by rw[list_lcm_suc, Abbr`z`, Abbr`m`] >> + `_ = m` by rw[GSYM divides_iff_lcm_fix] >> + rw[] + ] +QED + +(* Theorem: list_lcm [1 .. (n + 1)] = let s = prime_divisors (n + 1) in + if SING s then CHOICE s * list_lcm [1 .. n] else list_lcm [1 .. n] *) +(* Proof: by list_lcm_with_last_prime_power, list_lcm_with_last_non_prime_power *) +val list_lcm_recurrence = store_thm( + "list_lcm_recurrence", + ``!n. list_lcm [1 .. (n + 1)] = let s = prime_divisors (n + 1) in + if SING s then CHOICE s * list_lcm [1 .. n] else list_lcm [1 .. n]``, + rw[list_lcm_by_last_prime_power, list_lcm_by_last_non_prime_power]); + +(* Theorem: (prime_divisors (n + 1) = {p}) ==> (list_lcm [1 .. (n + 1)] = p * list_lcm [1 .. n]) *) +(* Proof: by list_lcm_by_last_prime_power, SING_DEF *) +val list_lcm_option_last_prime_power = store_thm( + "list_lcm_option_last_prime_power", + ``!n p. (prime_divisors (n + 1) = {p}) ==> (list_lcm [1 .. (n + 1)] = p * list_lcm [1 .. n])``, + rw[list_lcm_by_last_prime_power, SING_DEF]); + +(* Theorem: (!p. prime_divisors (n + 1) <> {p}) ==> (list_lcm [1 .. (n + 1)] = list_lcm [1 .. n]) *) +(* Proof: by ist_lcm_by_last_non_prime_power, SING_DEF *) +val list_lcm_option_last_non_prime_power = store_thm( + "list_lcm_option_last_non_prime_power", + ``!n. (!p. prime_divisors (n + 1) <> {p}) ==> (list_lcm [1 .. (n + 1)] = list_lcm [1 .. n])``, + rw[list_lcm_by_last_non_prime_power, SING_DEF]); + +(* Theorem: list_lcm [1 .. (n + 1)] = case some p. (prime_divisors (n + 1)) = {p} of + NONE => list_lcm [1 .. n] + | SOME p => p * list_lcm [1 .. n] *) +(* Proof: + For SOME p, true by list_lcm_option_last_prime_power + For NONE, true by list_lcm_option_last_non_prime_power +*) +val list_lcm_option_recurrence = store_thm( + "list_lcm_option_recurrence", + ``!n. list_lcm [1 .. (n + 1)] = case some p. (prime_divisors (n + 1)) = {p} of + NONE => list_lcm [1 .. n] + | SOME p => p * list_lcm [1 .. n]``, + rpt strip_tac >> + DEEP_INTRO_TAC optionTheory.some_intro >> + rw[list_lcm_option_last_prime_power, list_lcm_option_last_non_prime_power]); + +(* ------------------------------------------------------------------------- *) +(* Relating Consecutive LCM to Prime Functions *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: MEM x (SET_TO_LIST (prime_powers_upto n)) <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n *) +(* Proof: + Let s = prime_powers_upto n. + Then FINITE s by prime_powers_upto_finite + and !x. x IN s <=> MEM x (SET_TO_LIST s) by MEM_SET_TO_LIST + The result follows by prime_powers_upto_element +*) +val prime_powers_upto_list_mem = store_thm( + "prime_powers_upto_list_mem", + ``!n x. MEM x (SET_TO_LIST (prime_powers_upto n)) <=> ?p. (x = p ** LOG p n) /\ prime p /\ p <= n``, + rw[MEM_SET_TO_LIST, prime_powers_upto_element, prime_powers_upto_finite]); + +(* +LOG_EQ_0 |- !a b. 1 < a /\ 0 < b ==> ((LOG a b = 0) <=> b < a) +*) + +(* Theorem: prime p /\ p <= n ==> p ** LOG p n divides set_lcm (prime_powers_upto n) *) +(* Proof: + Let s = prime_powers_upto n. + Note FINITE s by prime_powers_upto_finite + and p ** LOG p n IN s by prime_powers_upto_element_alt + ==> p ** LOG p n divides set_lcm s by set_lcm_is_common_multiple +*) +val prime_powers_upto_lcm_prime_to_log_divisor = store_thm( + "prime_powers_upto_lcm_prime_to_log_divisor", + ``!n p. prime p /\ p <= n ==> p ** LOG p n divides set_lcm (prime_powers_upto n)``, + rpt strip_tac >> + `FINITE (prime_powers_upto n)` by rw[prime_powers_upto_finite] >> + `p ** LOG p n IN prime_powers_upto n` by rw[prime_powers_upto_element_alt] >> + rw[set_lcm_is_common_multiple]); + +(* Theorem: prime p /\ p <= n ==> p divides set_lcm (prime_powers_upto n) *) +(* Proof: + Note 1 < p by ONE_LT_PRIME + so LOG p n <> 0 by LOG_EQ_0, 1 < p + ==> p divides p ** LOG p n by divides_self_power, 1 < p + + Note p ** LOG p n divides set_lcm s by prime_powers_upto_lcm_prime_to_log_divisor + Thus p divides set_lcm s by DIVIDES_TRANS +*) +val prime_powers_upto_lcm_prime_divisor = store_thm( + "prime_powers_upto_lcm_prime_divisor", + ``!n p. prime p /\ p <= n ==> p divides set_lcm (prime_powers_upto n)``, + rpt strip_tac >> + `1 < p` by rw[ONE_LT_PRIME] >> + `LOG p n <> 0` by rw[LOG_EQ_0] >> + `p divides p ** LOG p n` by rw[divides_self_power] >> + `p ** LOG p n divides set_lcm (prime_powers_upto n)` by rw[prime_powers_upto_lcm_prime_to_log_divisor] >> + metis_tac[DIVIDES_TRANS]); + +(* Theorem: prime p /\ p <= n ==> p ** ppidx n divides set_lcm (prime_powers_upto n) *) +(* Proof: + Note 1 < p by ONE_LT_PRIME + and 0 < n by p <= n + ==> ppidx n <= LOG p n by prime_power_index_le_log_index, 0 < n + Thus p ** ppidx n divides p ** LOG p n by power_divides_iff, 1 < p + and p ** LOG p n divides set_lcm (prime_powers_upto n) by prime_powers_upto_lcm_prime_to_log_divisor + or p ** ppidx n divides set_lcm (prime_powers_upto n) by DIVIDES_TRANS +*) +val prime_powers_upto_lcm_prime_to_power_divisor = store_thm( + "prime_powers_upto_lcm_prime_to_power_divisor", + ``!n p. prime p /\ p <= n ==> p ** ppidx n divides set_lcm (prime_powers_upto n)``, + rpt strip_tac >> + `1 < p` by rw[ONE_LT_PRIME] >> + `0 < n` by decide_tac >> + `ppidx n <= LOG p n` by rw[prime_power_index_le_log_index] >> + `p ** ppidx n divides p ** LOG p n` by rw[power_divides_iff] >> + `p ** LOG p n divides set_lcm (prime_powers_upto n)` by rw[prime_powers_upto_lcm_prime_to_log_divisor] >> + metis_tac[DIVIDES_TRANS]); + +(* The next theorem is based on this example: +Take n = 10, +prime_powers_upto 10 = {2^3; 3^2; 5^1; 7^1} = {8; 9; 5; 7} +set_lcm (prime_powers_upto 10) = 2520 +For any 1 <= x <= 10, e.g. x = 6. +6 <= 10, 6 divides set_lcm (prime_powers_upto 10). + +The reason is that: +6 = PROD_SET (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) by prime_factorisation +prime_divisors 6 = {2; 3} +Because 2, 3 <= 6, 6 <= 10, the divisors 2,3 <= 10 by DIVIDES_LE +Thus 2^(LOG 2 10) = 2^3, 3^(LOG 3 10) = 3^2 IN prime_powers_upto 10) by prime_powers_upto_element_alt +But 2^(ppidx 6) = 2^1 = 2 divides 6, 3^(ppidx 6) = 3^1 = 3 divides 6 by prime_power_index_def + so 2^(ppidx 6) <= 10 and 3^(ppidx 6) <= 10. + +In this example, 2^1 < 2^3 3^1 < 3^2 how to compare (ppidx x) with (LOG p n) in general? ## +Due to this, 2^(ppidx 6) divides 2^(LOG 2 10), by prime_powers_divide + and 3^(ppidx 6) divides 3^(LOG 3 10), +And 2^(LOG 2 10) divides set_lcm (prime_powers_upto 10) by prime_powers_upto_lcm_prime_to_log_divisor +and 3^(LOG 3 10) divides set_lcm (prime_powers_upto 10) by prime_powers_upto_lcm_prime_to_log_divisor +or !z. z IN (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) + ==> z divides set_lcm (prime_powers_upto 10) by verification +Hence set_lcm (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) divides set_lcm (prime_powers_upto 10) + by set_lcm_is_least_common_multiple +But PAIRWISE_COPRIME (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)), +Thus set_lcm (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) + = PROD_SET (IMAGE (\p. p ** ppidx 6) (prime_divisors 6)) by pairwise_coprime_prod_set_eq_set_lcm + = 6 by above +Hence x divides set_lcm (prime_powers_upto 10) + +## maybe: + ppidx x <= LOG p x by prime_power_index_le_log_index + LOG p x <= LOG p n by LOG_LE_MONO +*) + +(* Theorem: 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n) *) +(* Proof: + Note 0 < n by 0 < x /\ x <= n + Let m = set_lcm (prime_powers_upto n). + The goal becomes: x divides m. + + Let s = prime_power_divisors x. + Then x = PROD_SET s by prime_factorisation, 0 < x + + Claim: !z. z IN s ==> z divides m + Proof: By prime_power_divisors_element, this is to show: + prime p /\ p divides x ==> p ** ppidx x divides m + Note p <= x by DIVIDES_LE, 0 < x + Thus p <= n by p <= x, x <= n + ==> p ** LOG p n IN prime_powers_upto n by prime_powers_upto_element_alt, b <= n + ==> p ** LOG p n divides m by prime_powers_upto_lcm_prime_to_log_divisor + Note 1 < p by ONE_LT_PRIME + and ppidx x <= LOG p x by prime_power_index_le_log_index, 0 < n + also LOG p x <= LOG p n by LOG_LE_MONO, 1 < p + ==> ppidx x <= LOG p n by arithmetic + ==> p ** ppidx x divides p ** LOG p n by power_divides_iff, 1 < p + Thus p ** ppidx x divides m by DIVIDES_TRANS + + Note FINITE s by prime_power_divisors_finite + and set_lcm s divides m by set_lcm_is_least_common_multiple, FINITE s + Also PAIRWISE_COPRIME s by prime_power_divisors_pairwise_coprime + ==> PROD_SET s = set_lcm s by pairwise_coprime_prod_set_eq_set_lcm + Thus x divides m by set_lcm s divides m +*) +val prime_powers_upto_lcm_divisor = store_thm( + "prime_powers_upto_lcm_divisor", + ``!n x. 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n)``, + rpt strip_tac >> + `0 < n` by decide_tac >> + qabbrev_tac `m = set_lcm (prime_powers_upto n)` >> + qabbrev_tac `s = prime_power_divisors x` >> + `x = PROD_SET s` by rw[prime_factorisation, Abbr`s`] >> + `!z. z IN s ==> z divides m` by + (rw[prime_power_divisors_element, Abbr`s`] >> + `p <= x` by rw[DIVIDES_LE] >> + `p <= n` by decide_tac >> + `p ** LOG p n IN prime_powers_upto n` by rw[prime_powers_upto_element_alt] >> + `p ** LOG p n divides m` by rw[prime_powers_upto_lcm_prime_to_log_divisor, Abbr`m`] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `ppidx x <= LOG p x` by rw[prime_power_index_le_log_index] >> + `LOG p x <= LOG p n` by rw[LOG_LE_MONO] >> + `ppidx x <= LOG p n` by decide_tac >> + `p ** ppidx x divides p ** LOG p n` by rw[power_divides_iff] >> + metis_tac[DIVIDES_TRANS]) >> + `FINITE s` by rw[prime_power_divisors_finite, Abbr`s`] >> + `set_lcm s divides m` by rw[set_lcm_is_least_common_multiple] >> + metis_tac[prime_power_divisors_pairwise_coprime, pairwise_coprime_prod_set_eq_set_lcm]); + +(* This is a key result. *) + +(* ------------------------------------------------------------------------- *) +(* Consecutive LCM and Prime-related Sets *) +(* ------------------------------------------------------------------------- *) + +(* +Useful: +list_lcm_is_common_multiple |- !x l. MEM x l ==> x divides list_lcm l +list_lcm_prime_factor |- !p l. prime p /\ p divides list_lcm l ==> p divides PROD_SET (set l) +list_lcm_prime_factor_member |- !p l. prime p /\ p divides list_lcm l ==> ?x. MEM x l /\ p divides x +prime_power_index_pos |- !n p. 0 < n /\ prime p /\ p divides n ==> 0 < ppidx n +*) + +(* Theorem: lcm_run n = set_lcm (prime_powers_upto n) *) +(* Proof: + By DIVIDES_ANTISYM, this is to show: + (1) lcm_run n divides set_lcm (prime_powers_upto n) + Let m = set_lcm (prime_powers_upto n) + Note !x. MEM x [1 .. n] <=> 0 < x /\ x <= n by listRangeINC_MEM + and !x. 0 < x /\ x <= n ==> x divides m by prime_powers_upto_lcm_divisor + Thus lcm_run n divides m by list_lcm_is_least_common_multiple + (2) set_lcm (prime_powers_upto n) divides lcm_run n + Let s = prime_powers_upto n, m = lcm_run n + Claim: !z. z IN s ==> z divides m + Proof: Note ?p. (z = p ** LOG p n) /\ + prime p /\ p <= n by prime_powers_upto_element + Now 0 < p by PRIME_POS + so MEM p [1 .. n] by listRangeINC_MEM + ==> MEM z [1 .. n] by self_to_log_index_member + Thus z divides m by list_lcm_is_common_multiple + + Note FINITE s by prime_powers_upto_finite + Thus set_lcm s divides m by set_lcm_is_least_common_multiple, Claim +*) +val lcm_run_eq_set_lcm_prime_powers = store_thm( + "lcm_run_eq_set_lcm_prime_powers", + ``!n. lcm_run n = set_lcm (prime_powers_upto n)``, + rpt strip_tac >> + (irule DIVIDES_ANTISYM >> rpt conj_tac) >| [ + `!x. MEM x [1 .. n] <=> 0 < x /\ x <= n` by rw[listRangeINC_MEM] >> + `!x. 0 < x /\ x <= n ==> x divides set_lcm (prime_powers_upto n)` by rw[prime_powers_upto_lcm_divisor] >> + rw[list_lcm_is_least_common_multiple], + qabbrev_tac `s = prime_powers_upto n` >> + qabbrev_tac `m = lcm_run n` >> + `!z. z IN s ==> z divides m` by + (rw[prime_powers_upto_element, Abbr`s`] >> + `0 < p` by rw[PRIME_POS] >> + `MEM p [1 .. n]` by rw[listRangeINC_MEM] >> + `MEM (p ** LOG p n) [1 .. n]` by rw[self_to_log_index_member] >> + rw[list_lcm_is_common_multiple, Abbr`m`]) >> + `FINITE s` by rw[prime_powers_upto_finite, Abbr`s`] >> + rw[set_lcm_is_least_common_multiple] + ]); + +(* Theorem: set_lcm (prime_powers_upto n) = PROD_SET (prime_powers_upto n) *) +(* Proof: + Let s = prime_powers_upto n. + Note FINITE s by prime_powers_upto_finite + and PAIRWISE_COPRIME s by prime_powers_upto_pairwise_coprime + Thus set_lcm s = PROD_SET s by pairwise_coprime_prod_set_eq_set_lcm +*) +val set_lcm_prime_powers_upto_eqn = store_thm( + "set_lcm_prime_powers_upto_eqn", + ``!n. set_lcm (prime_powers_upto n) = PROD_SET (prime_powers_upto n)``, + metis_tac[prime_powers_upto_finite, prime_powers_upto_pairwise_coprime, pairwise_coprime_prod_set_eq_set_lcm]); + +(* Theorem: lcm_run n = PROD_SET (prime_powers_upto n) *) +(* Proof: + lcm_run n + = set_lcm (prime_powers_upto n) + = PROD_SET (prime_powers_upto n) +*) +val lcm_run_eq_prod_set_prime_powers = store_thm( + "lcm_run_eq_prod_set_prime_powers", + ``!n. lcm_run n = PROD_SET (prime_powers_upto n)``, + rw[lcm_run_eq_set_lcm_prime_powers, set_lcm_prime_powers_upto_eqn]); + +(* Theorem: PROD_SET (prime_powers_upto n) <= n ** (primes_count n) *) +(* Proof: + Let s = (primes_upto n), f = \p. p ** LOG p n, t = prime_powers_upto n. + Then IMAGE f s = t by prime_powers_upto_def + and FINITE s by primes_upto_finite + and FINITE t by IMAGE_FINITE + + Claim: !x. x IN t ==> x <= n + Proof: Note x IN t ==> + ?p. (x = p ** LOG p n) /\ prime p /\ p <= n by prime_powers_upto_element + Now 1 < p by ONE_LT_PRIME + so 0 < n by 1 < p, p <= n + and p ** LOG p n <= n by LOG + or x <= n + + Thus PROD_SET t <= n ** CARD t by PROD_SET_LE_CONSTANT, Claim + + Claim: INJ f s t + Proof: By prime_powers_upto_element_alt, primes_upto_element, INJ_DEF, + This is to show: prime p /\ prime p' /\ p ** LOG p n = p' ** LOG p' n ==> p = p' + Note 1 < p by ONE_LT_PRIME + so 0 < n by 1 < p, p <= n + and LOG p n <> 0 by LOG_EQ_0, p <= n + or 0 < LOG p n by NOT_ZERO_LT_ZERO + ==> p = p' by prime_powers_eq + + Thus CARD (IMAGE f s) = CARD s by INJ_CARD_IMAGE, Claim + or PROD_SET t <= n ** CARD s by above +*) + +Theorem prime_powers_upto_prod_set_le: + !n. PROD_SET (prime_powers_upto n) <= n ** (primes_count n) +Proof + rpt strip_tac >> + qabbrev_tac ‘s = (primes_upto n)’ >> + qabbrev_tac ‘f = \p. p ** LOG p n’ >> + qabbrev_tac ‘t = prime_powers_upto n’ >> + ‘IMAGE f s = t’ by simp[prime_powers_upto_def, Abbr‘f’, Abbr‘s’, Abbr‘t’] >> + ‘FINITE s’ by rw[primes_upto_finite, Abbr‘s’] >> + ‘FINITE t’ by metis_tac[IMAGE_FINITE] >> + ‘!x. x IN t ==> x <= n’ + by (rw[prime_powers_upto_element, Abbr‘t’, Abbr‘f’] >> + ‘1 < p’ by rw[ONE_LT_PRIME] >> + rw[LOG]) >> + ‘PROD_SET t <= n ** CARD t’ by rw[PROD_SET_LE_CONSTANT] >> + ‘INJ f s t’ + by (rw[prime_powers_upto_element_alt, primes_upto_element, INJ_DEF, Abbr‘f’, + Abbr‘s’, Abbr‘t’] >> + ‘1 < p’ by rw[ONE_LT_PRIME] >> + ‘0 < n’ by decide_tac >> + ‘LOG p n <> 0’ by rw[LOG_EQ_0] >> + metis_tac[prime_powers_eq, NOT_ZERO_LT_ZERO]) >> + metis_tac[INJ_CARD_IMAGE] +QED + +(* Theorem: lcm_run n <= n ** (primes_count n) *) +(* Proof: + lcm_run n + = PROD_SET (prime_powers_upto n) by lcm_run_eq_prod_set_prime_powers + <= n ** (primes_count n) by prime_powers_upto_prod_set_le +*) +val lcm_run_upper_by_primes_count = store_thm( + "lcm_run_upper_by_primes_count", + ``!n. lcm_run n <= n ** (primes_count n)``, + rw[lcm_run_eq_prod_set_prime_powers, prime_powers_upto_prod_set_le]); + +(* This is a significant result. *) + +(* Theorem: PROD_SET (primes_upto n) <= PROD_SET (prime_powers_upto n) *) +(* Proof: + Let s = primes_upto n, f = \p. p ** LOG p n, t = prime_powers_upto n. + The goal becomes: PROD_SET s <= PROD_SET t + Note IMAGE f s = t by prime_powers_upto_def + and FINITE s by primes_upto_finite + + Claim: INJ f s univ(:num) + Proof: By primes_upto_element, INJ_DEF, + This is to show: prime p /\ prime p' /\ p ** LOG p n = p' ** LOG p' n ==> p = p' + Note 1 < p by ONE_LT_PRIME + so 0 < n by 1 < p, p <= n + Thus LOG p n <> 0 by LOG_EQ_0, p <= n + or 0 < LOG p n by NOT_ZERO_LT_ZERO + ==> p = p' by prime_powers_eq + + Also INJ I s univ(:num) by primes_upto_element, INJ_DEF + and IMAGE I s = s by IMAGE_I + + Claim: !x. x IN s ==> I x <= f x + Proof: By primes_upto_element, + This is to show: prime x /\ x <= n ==> x <= x ** LOG x n + Note 1 < x by ONE_LT_PRIME + so 0 < n by 1 < x, x <= n + Thus LOG x n <> 0 by LOG_EQ_0 + or 1 <= LOG x n by LOG x n <> 0 + ==> x ** 1 <= x ** LOG x n by EXP_BASE_LE_MONO + or x <= x ** LOG x n by EXP_1 + + Hence PROD_SET s <= PROD_SET t by PROD_SET_LESS_EQ +*) +val prime_powers_upto_prod_set_ge = store_thm( + "prime_powers_upto_prod_set_ge", + ``!n. PROD_SET (primes_upto n) <= PROD_SET (prime_powers_upto n)``, + rpt strip_tac >> + qabbrev_tac `s = primes_upto n` >> + qabbrev_tac `f = \p. p ** LOG p n` >> + qabbrev_tac `t = prime_powers_upto n` >> + `IMAGE f s = t` by rw[prime_powers_upto_def, Abbr`f`, Abbr`s`, Abbr`t`] >> + `FINITE s` by rw[primes_upto_finite, Abbr`s`] >> + `INJ f s univ(:num)` by + (rw[primes_upto_element, INJ_DEF, Abbr`f`, Abbr`s`] >> + `1 < p` by rw[ONE_LT_PRIME] >> + `LOG p n <> 0` by rw[LOG_EQ_0] >> + metis_tac[prime_powers_eq, NOT_ZERO_LT_ZERO]) >> + `INJ I s univ(:num)` by rw[primes_upto_element, INJ_DEF, Abbr`s`] >> + `IMAGE I s = s` by rw[] >> + `!x. x IN s ==> I x <= f x` by + (rw[primes_upto_element, Abbr`f`, Abbr`s`] >> + `1 < x` by rw[ONE_LT_PRIME] >> + `LOG x n <> 0` by rw[LOG_EQ_0] >> + `1 <= LOG x n` by decide_tac >> + metis_tac[EXP_BASE_LE_MONO, EXP_1]) >> + metis_tac[PROD_SET_LESS_EQ]); + +(* Theorem: PROD_SET (primes_upto n) <= lcm_run n *) +(* Proof: + lcm_run n + = set_lcm (prime_powers_upto n) by lcm_run_eq_set_lcm_prime_powers + = PROD_SET (prime_powers_upto n) by set_lcm_prime_powers_upto_eqn + >= PROD_SET (primes_upto n) by prime_powers_upto_prod_set_ge +*) +val lcm_run_lower_by_primes_product = store_thm( + "lcm_run_lower_by_primes_product", + ``!n. PROD_SET (primes_upto n) <= lcm_run n``, + rpt strip_tac >> + `lcm_run n = set_lcm (prime_powers_upto n)` by rw[lcm_run_eq_set_lcm_prime_powers] >> + `_ = PROD_SET (prime_powers_upto n)` by rw[set_lcm_prime_powers_upto_eqn] >> + rw[prime_powers_upto_prod_set_ge]); + +(* This is another significant result. *) + +(* These are essentially Chebyshev functions. *) + +(* Theorem: n ** primes_count n <= PROD_SET (primes_upto n) * (PROD_SET (prime_powers_upto n)) *) +(* Proof: + Let s = (primes_upto n), f = \p. p ** LOG p n, t = prime_powers_upto n. + The goal becomes: n ** CARD s <= PROD_SET s * PROD_SET t + + Note IMAGE f s = t by prime_powers_upto_def + and FINITE s by primes_upto_finite + and FINITE t by IMAGE_FINITE + + Claim: !p. p IN s ==> n <= I p * f p + Proof: By primes_upto_element, + This is to show: prime p /\ p <= n ==> n < p * p ** LOG p n + Note 1 < p by ONE_LT_PRIME + so 0 < n by 1 < p, p <= n + ==> n < p ** (SUC (LOG p n)) by LOG + = p * p ** (LOG p n) by EXP + or n <= p * p ** (LOG p n) by LESS_IMP_LESS_OR_EQ + + Note INJ I s univ(:num) by primes_upto_element, INJ_DEF, + and IMAGE I s = s by IMAGE_I + + Claim: INJ f s univ(:num) + Proof: By primes_upto_element, INJ_DEF, + This is to show: prime p /\ prime p' /\ p ** LOG p n = p' ** LOG p' n ==> p = p' + Note 1 < p by ONE_LT_PRIME + so 0 < n by 1 < p, p <= n + ==> LOG p n <> 0 by LOG_EQ_0 + or 0 < LOG p n by NOT_ZERO_LT_ZERO + Thus p = p' by prime_powers_eq + + Therefore, + n ** CARD s <= PROD_SET (IMAGE I s) * PROD_SET (IMAGE f s) + by PROD_SET_PRODUCT_GE_CONSTANT + or n ** CARD s <= PROD_SET s * PROD_SET t by above +*) + +Theorem prime_powers_upto_prod_set_mix_ge: + !n. n ** primes_count n <= + PROD_SET (primes_upto n) * (PROD_SET (prime_powers_upto n)) +Proof + rpt strip_tac >> + qabbrev_tac ‘s = (primes_upto n)’ >> + qabbrev_tac ‘f = \p. p ** LOG p n’ >> + qabbrev_tac ‘t = prime_powers_upto n’ >> + ‘IMAGE f s = t’ by rw[prime_powers_upto_def, Abbr‘f’, Abbr‘s’, Abbr‘t’] >> + ‘FINITE s’ by rw[primes_upto_finite, Abbr‘s’] >> + ‘FINITE t’ by rw[] >> + ‘!p. p IN s ==> n <= I p * f p’ by + (rw[primes_upto_element, Abbr‘s’, Abbr‘f’] >> + ‘1 < p’ by rw[ONE_LT_PRIME] >> + rw[LOG, GSYM EXP, LESS_IMP_LESS_OR_EQ]) >> + ‘INJ I s univ(:num)’ by rw[primes_upto_element, INJ_DEF, Abbr‘s’] >> + ‘IMAGE I s = s’ by rw[] >> + ‘INJ f s univ(:num)’ by + (rw[primes_upto_element, INJ_DEF, Abbr‘f’, Abbr‘s’] >> + ‘1 < p’ by rw[ONE_LT_PRIME] >> + ‘LOG p n <> 0’ by rw[LOG_EQ_0] >> + metis_tac[prime_powers_eq, NOT_ZERO_LT_ZERO]) >> + metis_tac[PROD_SET_PRODUCT_GE_CONSTANT] +QED + +(* Another significant result. *) + +(* Theorem: n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n *) +(* Proof: + n ** primes_count n + <= PROD_SET (primes_upto n) * (PROD_SET (prime_powers_upto n)) by prime_powers_upto_prod_set_mix_ge + = PROD_SET (primes_upto n) * lcm_run n by lcm_run_eq_prod_set_prime_powers +*) +val primes_count_upper_by_product = store_thm( + "primes_count_upper_by_product", + ``!n. n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n``, + metis_tac[prime_powers_upto_prod_set_mix_ge, lcm_run_eq_prod_set_prime_powers]); + +(* Theorem: n ** primes_count n <= (lcm_run n) ** 2 *) +(* Proof: + n ** primes_count n + <= PROD_SET (primes_upto n) * lcm_run n by primes_count_upper_by_product + <= lcm_run n * lcm_run n by lcm_run_lower_by_primes_product + = (lcm_run n) ** 2 by EXP_2 +*) +val primes_count_upper_by_lcm_run = store_thm( + "primes_count_upper_by_lcm_run", + ``!n. n ** primes_count n <= (lcm_run n) ** 2``, + rpt strip_tac >> + `n ** primes_count n <= PROD_SET (primes_upto n) * lcm_run n` by rw[primes_count_upper_by_product] >> + `PROD_SET (primes_upto n) <= lcm_run n` by rw[lcm_run_lower_by_primes_product] >> + metis_tac[LESS_MONO_MULT, LESS_EQ_TRANS, EXP_2]); + +(* Theorem: SQRT (n ** (primes_count n)) <= lcm_run n *) +(* Proof: + Note n ** primes_count n <= (lcm_run n) ** 2 by primes_count_upper_by_lcm_run + ==> SQRT (n ** primes_count n) <= SQRT ((lcm_run n) ** 2) by ROOT_LE_MONO, 0 < 2 + But SQRT ((lcm_run n) ** 2) = lcm_run n by ROOT_UNIQUE + Thus SQRT (n ** (primes_count n)) <= lcm_run n +*) +val lcm_run_lower_by_primes_count = store_thm( + "lcm_run_lower_by_primes_count", + ``!n. SQRT (n ** (primes_count n)) <= lcm_run n``, + rpt strip_tac >> + `n ** primes_count n <= (lcm_run n) ** 2` by rw[primes_count_upper_by_lcm_run] >> + `SQRT (n ** primes_count n) <= SQRT ((lcm_run n) ** 2)` by rw[ROOT_LE_MONO] >> + `SQRT ((lcm_run n) ** 2) = lcm_run n` by rw[ROOT_UNIQUE] >> + decide_tac); + +(* Therefore: + L(n) <= n ** pi(n) by lcm_run_upper_by_primes_count + PI(n) <= L(n) by lcm_run_lower_by_primes_product + n ** pi(n) <= PI(n) * L(n) by primes_count_upper_by_product + + giving: L(n) <= n ** pi(n) <= L(n) ** 2 by primes_count_upper_by_lcm_run + and: SQRT (n ** pi(n)) <= L(n) <= (n ** pi(n)) by lcm_run_lower_by_primes_count +*) + +(* ------------------------------------------------------------------------- *) +(* Primality Tests Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: +*) +(* + + Two Factors Properties: + two_factors_property_1 |- !n a b. (n = a * b) /\ a < SQRT n ==> SQRT n <= b + two_factors_property_2 |- !n a b. (n = a * b) /\ SQRT n < a ==> b <= SQRT n + two_factors_property |- !n a b. (n = a * b) ==> a <= SQRT n \/ b <= SQRT n + + Primality or Compositeness based on SQRT: + prime_by_sqrt_factors |- !p. prime p <=> + 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) + prime_factor_estimate |- !n. 1 < n ==> + (~prime n <=> ?p. prime p /\ p divides n /\ p <= SQRT n) + + Primality Testing Algorithm: + factor_seek_def |- !q n c. factor_seek n c q = + if c <= q then n + else if 1 < q /\ (n MOD q = 0) then q + else factor_seek n c (q + 1) + prime_test_def |- !n. prime_test n <=> + if n <= 1 then F else factor_seek n (1 + SQRT n) 2 = n + factor_seek_bound |- !n c q. 0 < n ==> factor_seek n c q <= n + factor_seek_thm |- !n c q. 1 < q /\ q <= c /\ c <= n ==> + (factor_seek n c q = n <=> !p. q <= p /\ p < c ==> ~(p divides n)) + prime_test_thm |- !n. prime n <=> prime_test n + +*) + +(* ------------------------------------------------------------------------- *) +(* Helper Theorems *) +(* ------------------------------------------------------------------------- *) + +(* ------------------------------------------------------------------------- *) +(* Two Factors Properties *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: (n = a * b) /\ a < SQRT n ==> SQRT n <= b *) +(* Proof: + If n = 0, then a = 0 or b = 0 by MULT_EQ_0 + But SQRT 0 = 0 by SQRT_0 + so a <> 0, making b = 0, and SQRT n <= b true. + If n <> 0, a <> 0 and b <> 0 by MULT_EQ_0 + By contradiction, suppose b < SQRT n. + Then n = a * b < a * SQRT n by LT_MULT_LCANCEL, 0 < a. + and a * SQRT n < SQRT n * SQRT n by LT_MULT_RCANCEL, 0 < SQRT n. + making n < (SQRT n) ** 2 by LESS_TRANS, EXP_2 + This contradicts (SQRT n) ** 2 <= n by SQRT_PROPERTY +*) +val two_factors_property_1 = store_thm( + "two_factors_property_1", + ``!n a b. (n = a * b) /\ a < SQRT n ==> SQRT n <= b``, + rpt strip_tac >> + Cases_on `n = 0` >| [ + `a <> 0 /\ (b = 0) /\ (SQRT n = 0)` by metis_tac[MULT_EQ_0, SQRT_0, DECIDE``~(0 < 0)``] >> + decide_tac, + `a <> 0 /\ b <> 0` by metis_tac[MULT_EQ_0] >> + spose_not_then strip_assume_tac >> + `b < SQRT n` by decide_tac >> + `0 < a /\ 0 < b /\ 0 < SQRT n` by decide_tac >> + `n < a * SQRT n` by rw[] >> + `a * SQRT n < SQRT n * SQRT n` by rw[] >> + `n < (SQRT n) ** 2` by metis_tac[LESS_TRANS, EXP_2] >> + `(SQRT n) ** 2 <= n` by rw[SQRT_PROPERTY] >> + decide_tac + ]); + +(* Theorem: (n = a * b) /\ SQRT n < a ==> b <= SQRT n *) +(* Proof: + If n = 0, then a = 0 or b = 0 by MULT_EQ_0 + But SQRT 0 = 0 by SQRT_0 + so a <> 0, making b = 0, and b <= SQRT n true. + If n <> 0, a <> 0 and b <> 0 by MULT_EQ_0 + By contradiction, suppose SQRT n < b. + Then SUC (SQRT n) ** 2 + = SUC (SQRT n) * SUC (SQRT n) by EXP_2 + <= a * SUC (SQRT n) by LT_MULT_RCANCEL, 0 < SUC (SQRT n). + <= a * b = n by LT_MULT_LCANCEL, 0 < a. + Giving (SUC (SQRT n)) ** 2 <= n by LESS_EQ_TRANS + or SQRT ((SUC (SQRT n)) ** 2) <= SQRT n by SQRT_LE + or SUC (SQRT n) <= SQRT n by SQRT_OF_SQ + which is a contradiction to !m. SUC m > m by LESS_SUC_REFL + *) +val two_factors_property_2 = store_thm( + "two_factors_property_2", + ``!n a b. (n = a * b) /\ SQRT n < a ==> b <= SQRT n``, + rpt strip_tac >> + Cases_on `n = 0` >| [ + `a <> 0 /\ (b = 0) /\ (SQRT 0 = 0)` by metis_tac[MULT_EQ_0, SQRT_0, DECIDE``~(0 < 0)``] >> + decide_tac, + `a <> 0 /\ b <> 0` by metis_tac[MULT_EQ_0] >> + spose_not_then strip_assume_tac >> + `SQRT n < b` by decide_tac >> + `SUC (SQRT n) <= a /\ SUC (SQRT n) <= b` by decide_tac >> + `SUC (SQRT n) * SUC (SQRT n) <= a * SUC (SQRT n)` by rw[] >> + `a * SUC (SQRT n) <= n` by rw[] >> + `SUC (SQRT n) ** 2 <= n` by metis_tac[LESS_EQ_TRANS, EXP_2] >> + `SUC (SQRT n) <= SQRT n` by metis_tac[SQRT_LE, SQRT_OF_SQ] >> + decide_tac + ]); + +(* Theorem: (n = a * b) ==> a <= SQRT n \/ b <= SQRT n *) +(* Proof: + By contradiction, suppose SQRT n < a /\ SQRT n < b. + Then (n = a * b) /\ SQRT n < a ==> b <= SQRT n by two_factors_property_2 + which contradicts SQRT n < b. + *) +val two_factors_property = store_thm( + "two_factors_property", + ``!n a b. (n = a * b) ==> a <= SQRT n \/ b <= SQRT n``, + rpt strip_tac >> + spose_not_then strip_assume_tac >> + `SQRT n < a` by decide_tac >> + metis_tac[two_factors_property_2]); + +(* ------------------------------------------------------------------------- *) +(* Primality or Compositeness based on SQRT *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: prime p <=> 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) *) +(* Proof: + If part: prime p ==> 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) + First one: prime p ==> 1 < p is true by ONE_LT_PRIME + Second one: by contradiction, suppose q divides p. + But prime p /\ q divides p ==> (q = p) or (q = 1) by prime_def + Given 1 < q, q <> 1, hence q = p. + This means p <= SQRT p, giving p = 0 or p = 1 by SQRT_GE_SELF + which contradicts p <> 0 and p <> 1 by PRIME_POS, prime_def + Only-if part: 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p) ==> prime p + By prime_def, this is to show: + (1) p <> 1, true since 1 < p. + (2) b divides p ==> (b = p) \/ (b = 1) + By contradiction, suppose b <> p /\ b <> 1. + b divides p ==> ?a. p = a * b by divides_def + which means a <= SQRT p \/ b <= SQRT p by two_factors_property + If a <= SQRT p, + 1 < p ==> p <> 0, so a <> 0 by MULT + also b <> p ==> a <> 1 by MULT_LEFT_1 + so 1 < a, and a divides p by prime_def, MULT_COMM + The implication gives ~(a divides p), a contradiction. + If b <= SQRT p, + 1 < p ==> p <> 0, so b <> 0 by MULT_0 + also b <> 1, so 1 < b, and b divides p. + The implication gives ~(b divides p), a contradiction. + *) +val prime_by_sqrt_factors = store_thm( + "prime_by_sqrt_factors", + ``!p. prime p <=> 1 < p /\ !q. 1 < q /\ q <= SQRT p ==> ~(q divides p)``, + rw[EQ_IMP_THM] >- + rw[ONE_LT_PRIME] >- + (spose_not_then strip_assume_tac >> + `0 < p` by rw[PRIME_POS] >> + `p <> 0 /\ q <> 1` by decide_tac >> + `(q = p) /\ p <> 1` by metis_tac[prime_def] >> + metis_tac[SQRT_GE_SELF]) >> + rw[prime_def] >> + spose_not_then strip_assume_tac >> + `?a. p = a * b` by rw[GSYM divides_def] >> + `a <= SQRT p \/ b <= SQRT p` by rw[two_factors_property] >| [ + `a <> 1` by metis_tac[MULT_LEFT_1] >> + `p <> 0` by decide_tac >> + `a <> 0` by metis_tac[MULT] >> + `1 < a` by decide_tac >> + metis_tac[divides_def, MULT_COMM], + `p <> 0` by decide_tac >> + `b <> 0` by metis_tac[MULT_0] >> + `1 < b` by decide_tac >> + metis_tac[] + ]); + +(* Theorem: 1 < n ==> (~prime n <=> ?p. prime p /\ p divides n /\ p <= SQRT n) *) +(* Proof: + If part ~prime n ==> ?p. prime p /\ p divides n /\ p <= SQRT n + Given n <> 1, ?p. prime p /\ p divides n by PRIME_FACTOR + If p <= SQRT n, take this p. + If ~(p <= SQRT n), SQRT n < p. + Since p divides n, ?q. n = p * q by divides_def, MULT_COMM + Hence q <= SQRT n by two_factors_property_2 + Since prime p but ~prime n, q <> 1 by MULT_RIGHT_1 + so ?t. prime t /\ t divides q by PRIME_FACTOR + Since 1 < n, n <> 0, so q <> 0 by MULT_0 + so t divides q ==> t <= q by DIVIDES_LE, 0 < q. + Take t, then t divides n by DIVIDES_TRANS + and t <= SQRT n by LESS_EQ_TRANS + Only-if part: ?p. prime p /\ p divides n /\ p <= SQRT n ==> ~prime n + By contradiction, assume prime n. + Then p divides n ==> p = 1 or p = n by prime_def + But prime p ==> p <> 1, so p = n by ONE_LT_PRIME + Giving p <= SQRT p, + thus forcing p = 0 or p = 1 by SQRT_GE_SELF + Both are impossible for prime p. +*) +val prime_factor_estimate = store_thm( + "prime_factor_estimate", + ``!n. 1 < n ==> (~prime n <=> ?p. prime p /\ p divides n /\ p <= SQRT n)``, + rpt strip_tac >> + `n <> 1` by decide_tac >> + rw[EQ_IMP_THM] >| [ + `?p. prime p /\ p divides n` by rw[PRIME_FACTOR] >> + Cases_on `p <= SQRT n` >- + metis_tac[] >> + `SQRT n < p` by decide_tac >> + `?q. n = q * p` by rw[GSYM divides_def] >> + `_ = p * q` by rw[MULT_COMM] >> + `q <= SQRT n` by metis_tac[two_factors_property_2] >> + `q <> 1` by metis_tac[MULT_RIGHT_1] >> + `?t. prime t /\ t divides q` by rw[PRIME_FACTOR] >> + `n <> 0` by decide_tac >> + `q <> 0` by metis_tac[MULT_0] >> + `0 < q ` by decide_tac >> + `t <= q` by rw[DIVIDES_LE] >> + `q divides n` by metis_tac[divides_def] >> + metis_tac[DIVIDES_TRANS, LESS_EQ_TRANS], + spose_not_then strip_assume_tac >> + `1 < p` by rw[ONE_LT_PRIME] >> + `p <> 1 /\ p <> 0` by decide_tac >> + `p = n` by metis_tac[prime_def] >> + metis_tac[SQRT_GE_SELF] + ]); + +(* ------------------------------------------------------------------------- *) +(* Primality Testing Algorithm *) +(* ------------------------------------------------------------------------- *) + +(* Seek the prime factor of number n, starting with q, up to cutoff c. *) +Definition factor_seek_def: + factor_seek n c q = + if c <= q then n + else if 1 < q /\ (n MOD q = 0) then q + else factor_seek n c (q + 1) +Termination + WF_REL_TAC ‘measure (λ(n,c,q). c - q)’ >> simp[] +End +(* Use 1 < q so that, for prime n, it gives a result n for any initial q, including q = 1. *) + +(* Primality test by seeking a factor exceeding (SQRT n). *) +val prime_test_def = Define` + prime_test n = + if n <= 1 then F + else factor_seek n (1 + SQRT n) 2 = n +`; + +(* +EVAL ``MAP prime_test [1 .. 15]``; = [F; T; T; F; T; F; T; F; F; F; T; F; T; F; F]: thm +*) + +(* Theorem: 0 < n ==> factor_seek n c q <= n *) +(* Proof: + By induction from factor_seek_def. + If c <= q, + Then factor_seek n c q = n, hence true by factor_seek_def + If q = 0, + Then factor_seek n c 0 = 0, hence true by factor_seek_def + If n MOD q = 0, + Then factor_seek n c q = q by factor_seek_def + Thus q divides n by DIVIDES_MOD_0, q <> 0 + hence q <= n by DIVIDES_LE, 0 < n + Otherwise, + factor_seek n c q + = factor_seek n c (q + 1) by factor_seek_def + <= n by induction hypothesis +*) +val factor_seek_bound = store_thm( + "factor_seek_bound", + ``!n c q. 0 < n ==> factor_seek n c q <= n``, + ho_match_mp_tac (theorem "factor_seek_ind") >> + rw[] >> + rw[Once factor_seek_def] >> + `q divides n` by rw[DIVIDES_MOD_0] >> + rw[DIVIDES_LE]); + +(* Theorem: 1 < q /\ q <= c /\ c <= n ==> + ((factor_seek n c q = n) <=> (!p. q <= p /\ p < c ==> ~(p divides n))) *) +(* Proof: + By induction from factor_seek_def, this is to show: + (1) n MOD q = 0 ==> ?p. (q <= p /\ p < c) /\ p divides n + Take p = q, then n MOD q = 0 ==> q divides n by DIVIDES_MOD_0, 0 < q + (2) n MOD q <> 0 ==> factor_seek n c (q + 1) = n <=> + !p. q <= p /\ p < c ==> ~(p divides n) + factor_seek n c (q + 1) = n + <=> !p. q + 1 <= p /\ p < c ==> ~(p divides n)) by induction hypothesis + or !p. q < p /\ p < c ==> ~(p divides n)) + But n MOD q <> 0 gives ~(q divides n) by DIVIDES_MOD_0, 0 < q + Thus !p. q <= p /\ p < c ==> ~(p divides n)) +*) +val factor_seek_thm = store_thm( + "factor_seek_thm", + ``!n c q. 1 < q /\ q <= c /\ c <= n ==> + ((factor_seek n c q = n) <=> (!p. q <= p /\ p < c ==> ~(p divides n)))``, + ho_match_mp_tac (theorem "factor_seek_ind") >> + rw[] >> + rw[Once factor_seek_def] >| [ + qexists_tac `q` >> + rw[DIVIDES_MOD_0], + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `0 < q` by decide_tac >> + `p <> q` by metis_tac[DIVIDES_MOD_0] >> + `q + 1 <= p` by decide_tac >> + metis_tac[] + ]); + +(* Theorem: prime n = prime_test n *) +(* Proof: + prime n + <=> 1 < n /\ !q. 1 < q /\ n <= SQRT n ==> ~(n divides p) by prime_by_sqrt_factors + <=> 1 < n /\ !q. 2 <= q /\ n < c ==> ~(n divides p) where c = 1 + SQRT n + Under 1 < n, + Note SQRT n <> 0 by SQRT_EQ_0, n <> 0 + so 1 < 1 + SQRT n = c, or 2 <= c. + Also SQRT n <= n by SQRT_LE_SELF + but SQRT n <> n by SQRT_EQ_SELF, 1 < n + so SQRT n < n, or c <= n. + Thus 1 < n /\ !q. 2 <= q /\ n < c ==> ~(n divides p) + <=> factor_seek n c q = n by factor_seek_thm + <=> prime_test n by prime_test_def +*) +val prime_test_thm = store_thm( + "prime_test_thm", + ``!n. prime n = prime_test n``, + rw[prime_test_def, prime_by_sqrt_factors] >> + rw[EQ_IMP_THM] >| [ + qabbrev_tac `c = SQRT n + 1` >> + `0 < 2` by decide_tac >> + `SQRT n <> 0` by rw[SQRT_EQ_0] >> + `2 <= c` by rw[Abbr`c`] >> + `SQRT n <= n` by rw[SQRT_LE_SELF] >> + `SQRT n <> n` by rw[SQRT_EQ_SELF] >> + `c <= n` by rw[Abbr`c`] >> + `!q. 2 <= q /\ q < c ==> ~(q divides n)` by fs[Abbr`c`] >> + rw[factor_seek_thm], + qabbrev_tac `c = SQRT n + 1` >> + `0 < 2` by decide_tac >> + `SQRT n <> 0` by rw[SQRT_EQ_0] >> + `2 <= c` by rw[Abbr`c`] >> + `SQRT n <= n` by rw[SQRT_LE_SELF] >> + `SQRT n <> n` by rw[SQRT_EQ_SELF] >> + `c <= n` by rw[Abbr`c`] >> + fs[factor_seek_thm] >> + `!p. 1 < p /\ p <= SQRT n ==> ~(p divides n)` by fs[Abbr`c`] >> + rw[] + ]); + +(* ------------------------------------------------------------------------- *) +(* Gauss' Little Theorem *) +(* ------------------------------------------------------------------------- *) +(* Overloading: +*) +(* Definitions and Theorems (# are exported, ! in computeLib): + + GCD Equivalence Class: + gcd_matches_def |- !n d. gcd_matches n d = {j | j IN natural n /\ (gcd j n = d)} +! gcd_matches_alt |- !n d. gcd_matches n d = natural n INTER {j | gcd j n = d} + gcd_matches_element |- !n d j. j IN gcd_matches n d <=> 0 < j /\ j <= n /\ (gcd j n = d) + gcd_matches_subset |- !n d. gcd_matches n d SUBSET natural n + gcd_matches_finite |- !n d. FINITE (gcd_matches n d) + gcd_matches_0 |- !d. gcd_matches 0 d = {} + gcd_matches_with_0 |- !n. gcd_matches n 0 = {} + gcd_matches_1 |- !d. gcd_matches 1 d = if d = 1 then {1} else {} + gcd_matches_has_divisor |- !n d. 0 < n /\ d divides n ==> d IN gcd_matches n d + gcd_matches_element_divides |- !n d j. j IN gcd_matches n d ==> d divides j /\ d divides n + gcd_matches_eq_empty |- !n d. 0 < n ==> ((gcd_matches n d = {}) <=> ~(d divides n)) + + Phi Function: + phi_def |- !n. phi n = CARD (coprimes n) + phi_thm |- !n. phi n = LENGTH (FILTER (\j. coprime j n) (GENLIST SUC n)) + phi_fun |- phi = CARD o coprimes + phi_pos |- !n. 0 < n ==> 0 < phi n + phi_0 |- phi 0 = 0 + phi_eq_0 |- !n. (phi n = 0) <=> (n = 0) + phi_1 |- phi 1 = 1 + phi_eq_totient |- !n. 1 < n ==> (phi n = totient n) + phi_prime |- !n. prime n ==> (phi n = n - 1) + phi_2 |- phi 2 = 1 + phi_gt_1 |- !n. 2 < n ==> 1 < phi n + phi_le |- !n. phi n <= n + phi_lt |- !n. 1 < n ==> phi n < n + + Divisors: + divisors_def |- !n. divisors n = {d | 0 < d /\ d <= n /\ d divides n} + divisors_element |- !n d. d IN divisors n <=> 0 < d /\ d <= n /\ d divides n + divisors_element_alt |- !n. 0 < n ==> !d. d IN divisors n <=> d divides n + divisors_has_element |- !n d. d IN divisors n ==> 0 < n + divisors_has_1 |- !n. 0 < n ==> 1 IN divisors n + divisors_has_last |- !n. 0 < n ==> n IN divisors n + divisors_not_empty |- !n. 0 < n ==> divisors n <> {} + divisors_0 |- divisors 0 = {} + divisors_1 |- divisors 1 = {1} + divisors_eq_empty |- !n. divisors n = {} <=> n = 0 +! divisors_eqn |- !n. divisors n = + IMAGE (\j. if j + 1 divides n then j + 1 else 1) (count n) + divisors_has_factor |- !n p q. 0 < n /\ n = p * q ==> p IN divisors n /\ q IN divisors n + divisors_has_cofactor |- !n d. d IN divisors n ==> n DIV d IN divisors n + divisors_delete_last |- !n. divisors n DELETE n = {m | 0 < m /\ m < n /\ m divides n} + divisors_nonzero |- !n d. d IN divisors n ==> 0 < d + divisors_subset_natural |- !n. divisors n SUBSET natural n + divisors_finite |- !n. FINITE (divisors n) + divisors_divisors_bij |- !n. (\d. n DIV d) PERMUTES divisors n + + An upper bound for divisors: + divisor_le_cofactor_ge |- !n p. 0 < p /\ p divides n /\ p <= SQRT n ==> SQRT n <= n DIV p + divisor_gt_cofactor_le |- !n p. 0 < p /\ p divides n /\ SQRT n < p ==> n DIV p <= SQRT n + divisors_cofactor_inj |- !n. INJ (\j. n DIV j) (divisors n) univ(:num) + divisors_card_upper |- !n. CARD (divisors n) <= TWICE (SQRT n) + + Gauss' Little Theorem: + gcd_matches_divisor_element |- !n d. d divides n ==> + !j. j IN gcd_matches n d ==> j DIV d IN coprimes_by n d + gcd_matches_bij_coprimes_by |- !n d. d divides n ==> + BIJ (\j. j DIV d) (gcd_matches n d) (coprimes_by n d) + gcd_matches_bij_coprimes |- !n d. 0 < n /\ d divides n ==> + BIJ (\j. j DIV d) (gcd_matches n d) (coprimes (n DIV d)) + divisors_eq_gcd_image |- !n. divisors n = IMAGE (gcd n) (natural n) + gcd_eq_equiv_class |- !n d. feq_class (gcd n) (natural n) d = gcd_matches n d + gcd_eq_equiv_class_fun |- !n. feq_class (gcd n) (natural n) = gcd_matches n + gcd_eq_partition_by_divisors |- !n. partition (feq (gcd n)) (natural n) = + IMAGE (gcd_matches n) (divisors n) + gcd_eq_equiv_on_natural |- !n. feq (gcd n) equiv_on natural n + sum_over_natural_by_gcd_partition + |- !f n. SIGMA f (natural n) = + SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) + sum_over_natural_by_divisors |- !f n. SIGMA f (natural n) = + SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) + gcd_matches_from_divisors_inj |- !n. INJ (gcd_matches n) (divisors n) univ(:num -> bool) + gcd_matches_and_coprimes_by_same_size |- !n. CARD o gcd_matches n = CARD o coprimes_by n + coprimes_by_with_card |- !n. 0 < n ==> CARD o coprimes_by n = + (\d. phi (if d IN divisors n then n DIV d else 0)) + coprimes_by_divisors_card |- !n x. x IN divisors n ==> + (CARD o coprimes_by n) x = (\d. phi (n DIV d)) x + Gauss_little_thm |- !n. SIGMA phi (divisors n) = n + + Euler phi function is multiplicative for coprimes: + coprimes_mult_by_image + |- !m n. coprime m n ==> + coprimes (m * n) = + IMAGE (\(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)) + (coprimes m CROSS coprimes n) + coprimes_map_cross_inj + |- !m n. coprime m n ==> + INJ (\(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)) + (coprimes m CROSS coprimes n) univ(:num) + phi_mult |- !m n. coprime m n ==> phi (m * n) = phi m * phi n + phi_primes_distinct |- !p q. prime p /\ prime q /\ p <> q ==> phi (p * q) = (p - 1) * (q - 1) + + Euler phi function for prime powers: + multiples_upto_def |- !m n. m multiples_upto n = {x | m divides x /\ 0 < x /\ x <= n} + multiples_upto_element + |- !m n x. x IN m multiples_upto n <=> m divides x /\ 0 < x /\ x <= n + multiples_upto_alt |- !m n. m multiples_upto n = {x | ?k. x = k * m /\ 0 < x /\ x <= n} + multiples_upto_element_alt + |- !m n x. x IN m multiples_upto n <=> ?k. x = k * m /\ 0 < x /\ x <= n + multiples_upto_eqn |- !m n. m multiples_upto n = {x | m divides x /\ x IN natural n} + multiples_upto_0_n |- !n. 0 multiples_upto n = {} + multiples_upto_1_n |- !n. 1 multiples_upto n = natural n + multiples_upto_m_0 |- !m. m multiples_upto 0 = {} + multiples_upto_m_1 |- !m. m multiples_upto 1 = if m = 1 then {1} else {} + multiples_upto_thm |- !m n. m multiples_upto n = + if m = 0 then {} else IMAGE ($* m) (natural (n DIV m)) + multiples_upto_subset + |- !m n. m multiples_upto n SUBSET natural n + multiples_upto_finite + |- !m n. FINITE (m multiples_upto n) + multiples_upto_card |- !m n. CARD (m multiples_upto n) = if m = 0 then 0 else n DIV m + coprimes_prime_power|- !p n. prime p ==> + coprimes (p ** n) = natural (p ** n) DIFF p multiples_upto p ** n + phi_prime_power |- !p n. prime p ==> phi (p ** SUC n) = (p - 1) * p ** n + phi_prime_sq |- !p. prime p ==> phi (p * p) = p * (p - 1) + phi_primes |- !p q. prime p /\ prime q ==> + phi (p * q) = if p = q then p * (p - 1) else (p - 1) * (q - 1) + + Recursive definition of phi: + rec_phi_def |- !n. rec_phi n = if n = 0 then 0 + else if n = 1 then 1 + else n - SIGMA (\a. rec_phi a) {m | m < n /\ m divides n} + rec_phi_0 |- rec_phi 0 = 0 + rec_phi_1 |- rec_phi 1 = 1 + rec_phi_eq_phi |- !n. rec_phi n = phi n + + Useful Theorems: + coprimes_from_not_1_inj |- INJ coprimes (univ(:num) DIFF {1}) univ(:num -> bool) + divisors_eq_image_gcd_upto |- !n. 0 < n ==> divisors n = IMAGE (gcd n) (upto n) + gcd_eq_equiv_on_upto |- !n. feq (gcd n) equiv_on upto n + gcd_eq_upto_partition_by_divisors + |- !n. 0 < n ==> + partition (feq (gcd n)) (upto n) = + IMAGE (preimage (gcd n) (upto n)) (divisors n) + sum_over_upto_by_gcd_partition + |- !f n. SIGMA f (upto n) = + SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n)) + sum_over_upto_by_divisors |- !f n. 0 < n ==> + SIGMA f (upto n) = + SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n)) + + divisors_eq_image_gcd_count |- !n. divisors n = IMAGE (gcd n) (count n) + gcd_eq_equiv_on_count |- !n. feq (gcd n) equiv_on count n + gcd_eq_count_partition_by_divisors + |- !n. partition (feq (gcd n)) (count n) = + IMAGE (preimage (gcd n) (count n)) (divisors n) + sum_over_count_by_gcd_partition + |- !f n. SIGMA f (count n) = + SIGMA (SIGMA f) (partition (feq (gcd n)) (count n)) + sum_over_count_by_divisors |- !f n. SIGMA f (count n) = + SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) + + divisors_eq_image_gcd_natural + |- !n. divisors n = IMAGE (gcd n) (natural n) + gcd_eq_natural_partition_by_divisors + |- !n. partition (feq (gcd n)) (natural n) = + IMAGE (preimage (gcd n) (natural n)) (divisors n) + sum_over_natural_by_preimage_divisors + |- !f n. SIGMA f (natural n) = + SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) + sum_image_divisors_cong |- !f g. f 0 = g 0 /\ (!n. SIGMA f (divisors n) = SIGMA g (divisors n)) ==> f = g +*) + +(* Theory: + +Given the set natural 6 = {1, 2, 3, 4, 5, 6} +Every element has a gcd with 6: IMAGE (gcd 6) (natural 6) = {1, 2, 3, 2, 1, 6} = {1, 2, 3, 6}. +Thus the original set is partitioned by gcd: {{1, 5}, {2, 4}, {3}, {6}} +Since (gcd 6) j is a divisor of 6, and they run through all possible divisors of 6, + SIGMA f (natural 6) += f 1 + f 2 + f 3 + f 4 + f 5 + f 6 += (f 1 + f 5) + (f 2 + f 4) + f 3 + f 6 += (SIGMA f {1, 5}) + (SIGMA f {2, 4}) + (SIGMA f {3}) + (SIGMA f {6}) += SIGMA (SIGMA f) {{1, 5}, {2, 4}, {3}, {6}} += SIGMA (SIGMA f) (partition (feq (natural 6) (gcd 6)) (natural 6)) + +SIGMA:('a -> num) -> ('a -> bool) -> num +SIGMA (f:num -> num):(num -> bool) -> num +SIGMA (SIGMA (f:num -> num)) (s:(num -> bool) -> bool):num + +How to relate this to (divisors n) ? +First, observe IMAGE (gcd 6) (natural 6) = divisors 6 +and partition {{1, 5}, {2, 4}, {3}, {6}} = IMAGE (preimage (gcd 6) (natural 6)) (divisors 6) + + SIGMA f (natural 6) += SIGMA (SIGMA f) (partition (feq (natural 6) (gcd 6)) (natural 6)) += SIGMA (SIGMA f) (IMAGE (preimage (gcd 6) (natural 6)) (divisors 6)) + +divisors n:num -> bool +preimage (gcd n):(num -> bool) -> num -> num -> bool +preimage (gcd n) (natural n):num -> num -> bool +IMAGE (preimage (gcd n) (natural n)) (divisors n):(num -> bool) -> bool + +How to relate this to (coprimes d), where d divides n ? +Note {1, 5} with (gcd 6) j = 1, equals to (coprimes (6 DIV 1)) = coprimes 6 + {2, 4} with (gcd 6) j = 2, BIJ to {2/2, 4/2} with gcd (6/2) (j/2) = 1, i.e {1, 2} = coprimes 3 + {3} with (gcd 6) j = 3, BIJ to {3/3} with gcd (6/3) (j/3) = 1, i.e. {1} = coprimes 2 + {6} with (gcd 6) j = 6, BIJ to {6/6} with gcd (6/6) (j/6) = 1, i.e. {1} = coprimes 1 +Hence CARD {{1, 5}, {2, 4}, {3}, {6}} = CARD (partition) + = CARD {{1, 5}/1, {2,4}/2, {3}/3, {6}/6} = CARD (reduced-partition) + = CARD {(coprimes 6/1) (coprimes 6/2) (coprimes 6/3) (coprimes 6/6)} + = CARD {(coprimes 6) (coprimes 3) (coprimes 2) (coprimes 1)} + = SIGMA (CARD (coprimes d)), over d divides 6) + = SIGMA (phi d), over d divides 6. +*) + +(* Theorem: coprimes n = set (FILTER (\j. coprime j n) (GENLIST SUC n)) *) +(* Proof: + coprimes n + = (natural n) INTER {j | coprime j n} by coprimes_alt + = (set (GENLIST SUC n)) INTER {j | coprime j n} by natural_thm + = {j | coprime j n} INTER (set (GENLIST SUC n)) by INTER_COMM + = set (FILTER (\j. coprime j n) (GENLIST SUC n)) by LIST_TO_SET_FILTER +*) +val coprimes_thm = store_thm( + "coprimes_thm", + ``!n. coprimes n = set (FILTER (\j. coprime j n) (GENLIST SUC n))``, + rw[coprimes_alt, natural_thm, INTER_COMM, LIST_TO_SET_FILTER]); + +(* Relate coprimes to Euler totient *) + +(* Theorem: 1 < n ==> (coprimes n = Euler n) *) +(* Proof: + By Euler_def, this is to show: + (1) x IN coprimes n ==> 0 < x, true by coprimes_element + (2) x IN coprimes n ==> x < n, true by coprimes_element_less + (3) x IN coprimes n ==> coprime n x, true by coprimes_element, GCD_SYM + (4) 0 < x /\ x < n /\ coprime n x ==> x IN coprimes n + That is, to show: 0 < x /\ x <= n /\ coprime x n. + Since x < n ==> x <= n by LESS_IMP_LESS_OR_EQ + Hence true by GCD_SYM +*) +val coprimes_eq_Euler = store_thm( + "coprimes_eq_Euler", + ``!n. 1 < n ==> (coprimes n = Euler n)``, + rw[Euler_def, EXTENSION, EQ_IMP_THM] >- + metis_tac[coprimes_element] >- + rw[coprimes_element_less] >- + metis_tac[coprimes_element, GCD_SYM] >> + metis_tac[coprimes_element, GCD_SYM, LESS_IMP_LESS_OR_EQ]); + +(* Theorem: prime n ==> (coprimes n = residue n) *) +(* Proof: + Since prime n ==> 1 < n by ONE_LT_PRIME + Hence coprimes n + = Euler n by coprimes_eq_Euler + = residue n by Euler_prime +*) +val coprimes_prime = store_thm( + "coprimes_prime", + ``!n. prime n ==> (coprimes n = residue n)``, + rw[ONE_LT_PRIME, coprimes_eq_Euler, Euler_prime]); + +(* ------------------------------------------------------------------------- *) +(* Coprimes by a divisor *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of coprimes by a divisor of n *) +val coprimes_by_def = Define ` + coprimes_by n d = if (0 < n /\ d divides n) then coprimes (n DIV d) else {} +`; + +(* +EVAL ``coprimes_by 10 2``; = {4; 3; 2; 1} +EVAL ``coprimes_by 10 5``; = {1} +*) + +(* Theorem: j IN (coprimes_by n d) <=> (0 < n /\ d divides n /\ j IN coprimes (n DIV d)) *) +(* Proof: by coprimes_by_def, MEMBER_NOT_EMPTY *) +val coprimes_by_element = store_thm( + "coprimes_by_element", + ``!n d j. j IN (coprimes_by n d) <=> (0 < n /\ d divides n /\ j IN coprimes (n DIV d))``, + metis_tac[coprimes_by_def, MEMBER_NOT_EMPTY]); + +(* Theorem: FINITE (coprimes_by n d) *) +(* Proof: + From coprimes_by_def, this follows by: + (1) !k. FINITE (coprimes k) by coprimes_finite + (2) FINITE {} by FINITE_EMPTY +*) +val coprimes_by_finite = store_thm( + "coprimes_by_finite", + ``!n d. FINITE (coprimes_by n d)``, + rw[coprimes_by_def, coprimes_finite]); + +(* Theorem: coprimes_by 0 d = {} *) +(* Proof: by coprimes_by_def *) +val coprimes_by_0 = store_thm( + "coprimes_by_0", + ``!d. coprimes_by 0 d = {}``, + rw[coprimes_by_def]); + +(* Theorem: coprimes_by n 0 = {} *) +(* Proof: + coprimes_by n 0 + = if 0 < n /\ 0 divides n then coprimes (n DIV 0) else {} + = 0 < 0 then coprimes (n DIV 0) else {} by ZERO_DIVIDES + = {} by prim_recTheory.LESS_REFL +*) +val coprimes_by_by_0 = store_thm( + "coprimes_by_by_0", + ``!n. coprimes_by n 0 = {}``, + rw[coprimes_by_def]); + +(* Theorem: 0 < n ==> (coprimes_by n 1 = coprimes n) *) +(* Proof: + Since 1 divides n by ONE_DIVIDES_ALL + coprimes_by n 1 + = coprimes (n DIV 1) by coprimes_by_def + = coprimes n by DIV_ONE, ONE +*) +val coprimes_by_by_1 = store_thm( + "coprimes_by_by_1", + ``!n. 0 < n ==> (coprimes_by n 1 = coprimes n)``, + rw[coprimes_by_def]); + +(* Theorem: 0 < n ==> (coprimes_by n n = {1}) *) +(* Proof: + Since n divides n by DIVIDES_REFL + coprimes_by n n + = coprimes (n DIV n) by coprimes_by_def + = coprimes 1 by DIVMOD_ID, 0 < n + = {1} by coprimes_1 +*) +val coprimes_by_by_last = store_thm( + "coprimes_by_by_last", + ``!n. 0 < n ==> (coprimes_by n n = {1})``, + rw[coprimes_by_def, coprimes_1]); + +(* Theorem: 0 < n /\ d divides n ==> (coprimes_by n d = coprimes (n DIV d)) *) +(* Proof: by coprimes_by_def *) +val coprimes_by_by_divisor = store_thm( + "coprimes_by_by_divisor", + ``!n d. 0 < n /\ d divides n ==> (coprimes_by n d = coprimes (n DIV d))``, + rw[coprimes_by_def]); + +(* Theorem: 0 < n ==> ((coprimes_by n d = {}) <=> ~(d divides n)) *) +(* Proof: + If part: 0 < n /\ coprimes_by n d = {} ==> ~(d divides n) + By contradiction. Suppose d divides n. + Then d divides n and 0 < n means + 0 < d /\ d <= n by divides_pos, 0 < n + Also coprimes_by n d = coprimes (n DIV d) by coprimes_by_def + so coprimes (n DIV d) = {} <=> n DIV d = 0 by coprimes_eq_empty + Thus n < d by DIV_EQUAL_0 + which contradicts d <= n. + Only-if part: 0 < n /\ ~(d divides n) ==> coprimes n d = {} + This follows by coprimes_by_def +*) +val coprimes_by_eq_empty = store_thm( + "coprimes_by_eq_empty", + ``!n d. 0 < n ==> ((coprimes_by n d = {}) <=> ~(d divides n))``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `0 < d /\ d <= n` by metis_tac[divides_pos] >> + `n DIV d = 0` by metis_tac[coprimes_by_def, coprimes_eq_empty] >> + `n < d` by rw[GSYM DIV_EQUAL_0] >> + decide_tac, + rw[coprimes_by_def] + ]); + +(* ------------------------------------------------------------------------- *) +(* GCD Equivalence Class *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of values with the same gcd *) +val gcd_matches_def = zDefine ` + gcd_matches n d = {j| j IN (natural n) /\ (gcd j n = d)} +`; +(* use zDefine as this is not computationally effective. *) + +(* Theorem: gcd_matches n d = (natural n) INTER {j | gcd j n = d} *) +(* Proof: by gcd_matches_def *) +Theorem gcd_matches_alt[compute]: + !n d. gcd_matches n d = (natural n) INTER {j | gcd j n = d} +Proof + simp[gcd_matches_def, EXTENSION] +QED + +(* +EVAL ``gcd_matches 10 2``; = {8; 6; 4; 2} +EVAL ``gcd_matches 10 5``; = {5} +*) + +(* Theorem: j IN gcd_matches n d <=> 0 < j /\ j <= n /\ (gcd j n = d) *) +(* Proof: by gcd_matches_def *) +val gcd_matches_element = store_thm( + "gcd_matches_element", + ``!n d j. j IN gcd_matches n d <=> 0 < j /\ j <= n /\ (gcd j n = d)``, + rw[gcd_matches_def, natural_element]); + +(* Theorem: (gcd_matches n d) SUBSET (natural n) *) +(* Proof: by gcd_matches_def, SUBSET_DEF *) +val gcd_matches_subset = store_thm( + "gcd_matches_subset", + ``!n d. (gcd_matches n d) SUBSET (natural n)``, + rw[gcd_matches_def, SUBSET_DEF]); + +(* Theorem: FINITE (gcd_matches n d) *) +(* Proof: + Since (gcd_matches n d) SUBSET (natural n) by coprimes_subset + and !n. FINITE (natural n) by natural_finite + so FINITE (gcd_matches n d) by SUBSET_FINITE +*) +val gcd_matches_finite = store_thm( + "gcd_matches_finite", + ``!n d. FINITE (gcd_matches n d)``, + metis_tac[gcd_matches_subset, natural_finite, SUBSET_FINITE]); + +(* Theorem: gcd_matches 0 d = {} *) +(* Proof: + j IN gcd_matches 0 d + <=> 0 < j /\ j <= 0 /\ (gcd j 0 = d) by gcd_matches_element + Since no j can satisfy this, the set is empty. +*) +val gcd_matches_0 = store_thm( + "gcd_matches_0", + ``!d. gcd_matches 0 d = {}``, + rw[gcd_matches_element, EXTENSION]); + +(* Theorem: gcd_matches n 0 = {} *) +(* Proof: + x IN gcd_matches n 0 + <=> 0 < x /\ x <= n /\ (gcd x n = 0) by gcd_matches_element + <=> 0 < x /\ x <= n /\ (x = 0) /\ (n = 0) by GCD_EQ_0 + <=> F by 0 < x, x = 0 + Hence gcd_matches n 0 = {} by EXTENSION +*) +val gcd_matches_with_0 = store_thm( + "gcd_matches_with_0", + ``!n. gcd_matches n 0 = {}``, + rw[EXTENSION, gcd_matches_element]); + +(* Theorem: gcd_matches 1 d = if d = 1 then {1} else {} *) +(* Proof: + j IN gcd_matches 1 d + <=> 0 < j /\ j <= 1 /\ (gcd j 1 = d) by gcd_matches_element + Only j to satisfy this is j = 1. + and d = gcd 1 1 = 1 by GCD_REF + If d = 1, j = 1 is the only element. + If d <> 1, the only element is taken out, set is empty. +*) +val gcd_matches_1 = store_thm( + "gcd_matches_1", + ``!d. gcd_matches 1 d = if d = 1 then {1} else {}``, + rw[gcd_matches_element, EXTENSION]); + +(* Theorem: 0 < n /\ d divides n ==> d IN (gcd_matches n d) *) +(* Proof: + Note 0 < n /\ d divides n + ==> 0 < d, and d <= n by divides_pos + and gcd d n = d by divides_iff_gcd_fix + Hence d IN (gcd_matches n d) by gcd_matches_element +*) +val gcd_matches_has_divisor = store_thm( + "gcd_matches_has_divisor", + ``!n d. 0 < n /\ d divides n ==> d IN (gcd_matches n d)``, + rw[gcd_matches_element] >- + metis_tac[divisor_pos] >- + rw[DIVIDES_LE] >> + rw[GSYM divides_iff_gcd_fix]); + +(* Theorem: j IN (gcd_matches n d) ==> d divides j /\ d divides n *) +(* Proof: + If j IN (gcd_matches n d), gcd j n = d by gcd_matches_element + This means d divides j /\ d divides n by GCD_IS_GREATEST_COMMON_DIVISOR +*) +val gcd_matches_element_divides = store_thm( + "gcd_matches_element_divides", + ``!n d j. j IN (gcd_matches n d) ==> d divides j /\ d divides n``, + metis_tac[gcd_matches_element, GCD_IS_GREATEST_COMMON_DIVISOR]); + +(* Theorem: 0 < n ==> ((gcd_matches n d = {}) <=> ~(d divides n)) *) +(* Proof: + If part: 0 < n /\ (gcd_matches n d = {}) ==> ~(d divides n) + By contradiction, suppose d divides n. + Then d IN gcd_matches n d by gcd_matches_has_divisor + This contradicts gcd_matches n d = {} by MEMBER_NOT_EMPTY + Only-if part: 0 < n /\ ~(d divides n) ==> (gcd_matches n d = {}) + By contradiction, suppose gcd_matches n d <> {}. + Then ?j. j IN (gcd_matches n d) by MEMBER_NOT_EMPTY + Giving d divides j /\ d divides n by gcd_matches_element_divides + This contradicts ~(d divides n). +*) +val gcd_matches_eq_empty = store_thm( + "gcd_matches_eq_empty", + ``!n d. 0 < n ==> ((gcd_matches n d = {}) <=> ~(d divides n))``, + rw[EQ_IMP_THM] >- + metis_tac[gcd_matches_has_divisor, MEMBER_NOT_EMPTY] >> + metis_tac[gcd_matches_element_divides, MEMBER_NOT_EMPTY]); + +(* ------------------------------------------------------------------------- *) +(* Phi Function *) +(* ------------------------------------------------------------------------- *) + +(* Define the Euler phi function from coprime set *) +val phi_def = Define ` + phi n = CARD (coprimes n) +`; +(* Since (coprimes n) is computable, phi n is now computable *) + +(* +> EVAL ``phi 10``; +val it = |- phi 10 = 4: thm +*) + +(* Theorem: phi n = LENGTH (FILTER (\j. coprime j n) (GENLIST SUC n)) *) +(* Proof: + Let ls = FILTER (\j. coprime j n) (GENLIST SUC n). + Note ALL_DISTINCT (GENLIST SUC n) by ALL_DISTINCT_GENLIST, SUC_EQ + Thus ALL_DISTINCT ls by FILTER_ALL_DISTINCT + phi n = CARD (coprimes n) by phi_def + = CARD (set ls) by coprimes_thm + = LENGTH ls by ALL_DISTINCT_CARD_LIST_TO_SET +*) +val phi_thm = store_thm( + "phi_thm", + ``!n. phi n = LENGTH (FILTER (\j. coprime j n) (GENLIST SUC n))``, + rpt strip_tac >> + qabbrev_tac `ls = FILTER (\j. coprime j n) (GENLIST SUC n)` >> + `ALL_DISTINCT ls` by rw[ALL_DISTINCT_GENLIST, FILTER_ALL_DISTINCT, Abbr`ls`] >> + `phi n = CARD (coprimes n)` by rw[phi_def] >> + `_ = CARD (set ls)` by rw[coprimes_thm, Abbr`ls`] >> + `_ = LENGTH ls` by rw[ALL_DISTINCT_CARD_LIST_TO_SET] >> + decide_tac); + +(* Theorem: phi = CARD o coprimes *) +(* Proof: by phi_def, FUN_EQ_THM *) +val phi_fun = store_thm( + "phi_fun", + ``phi = CARD o coprimes``, + rw[phi_def, FUN_EQ_THM]); + +(* Theorem: 0 < n ==> 0 < phi n *) +(* Proof: + Since 1 IN coprimes n by coprimes_has_1 + so coprimes n <> {} by MEMBER_NOT_EMPTY + and FINITE (coprimes n) by coprimes_finite + hence phi n <> 0 by CARD_EQ_0 + or 0 < phi n +*) +val phi_pos = store_thm( + "phi_pos", + ``!n. 0 < n ==> 0 < phi n``, + rpt strip_tac >> + `coprimes n <> {}` by metis_tac[coprimes_has_1, MEMBER_NOT_EMPTY] >> + `FINITE (coprimes n)` by rw[coprimes_finite] >> + `phi n <> 0` by rw[phi_def, CARD_EQ_0] >> + decide_tac); + +(* Theorem: phi 0 = 0 *) +(* Proof: + phi 0 + = CARD (coprimes 0) by phi_def + = CARD {} by coprimes_0 + = 0 by CARD_EMPTY +*) +val phi_0 = store_thm( + "phi_0", + ``phi 0 = 0``, + rw[phi_def, coprimes_0]); + +(* Theorem: (phi n = 0) <=> (n = 0) *) +(* Proof: + If part: (phi n = 0) ==> (n = 0) by phi_pos, NOT_ZERO_LT_ZERO + Only-if part: phi 0 = 0 by phi_0 +*) +val phi_eq_0 = store_thm( + "phi_eq_0", + ``!n. (phi n = 0) <=> (n = 0)``, + metis_tac[phi_0, phi_pos, NOT_ZERO_LT_ZERO]); + +(* Theorem: phi 1 = 1 *) +(* Proof: + phi 1 + = CARD (coprimes 1) by phi_def + = CARD {1} by coprimes_1 + = 1 by CARD_SING +*) +val phi_1 = store_thm( + "phi_1", + ``phi 1 = 1``, + rw[phi_def, coprimes_1]); + +(* Theorem: 1 < n ==> (phi n = totient n) *) +(* Proof: + phi n + = CARD (coprimes n) by phi_def + = CARD (Euler n ) by coprimes_eq_Euler + = totient n by totient_def +*) +val phi_eq_totient = store_thm( + "phi_eq_totient", + ``!n. 1 < n ==> (phi n = totient n)``, + rw[phi_def, totient_def, coprimes_eq_Euler]); + +(* Theorem: prime n ==> (phi n = n - 1) *) +(* Proof: + Since prime n ==> 1 < n by ONE_LT_PRIME + Hence phi n + = totient n by phi_eq_totient + = n - 1 by Euler_card_prime +*) +val phi_prime = store_thm( + "phi_prime", + ``!n. prime n ==> (phi n = n - 1)``, + rw[ONE_LT_PRIME, phi_eq_totient, Euler_card_prime]); + +(* Theorem: phi 2 = 1 *) +(* Proof: + Since prime 2 by PRIME_2 + so phi 2 = 2 - 1 = 1 by phi_prime +*) +val phi_2 = store_thm( + "phi_2", + ``phi 2 = 1``, + rw[phi_prime, PRIME_2]); + +(* Theorem: 2 < n ==> 1 < phi n *) +(* Proof: + Note 1 IN (coprimes n) by coprimes_has_1, 0 < n + and (n - 1) IN (coprimes n) by coprimes_has_last_but_1, 1 < n + and n - 1 <> 1 by 2 < n + Now FINITE (coprimes n) by coprimes_finite] + and {1; (n-1)} SUBSET (coprimes n) by SUBSET_DEF, above + Note CARD {1; (n-1)} = 2 by CARD_INSERT, CARD_EMPTY, TWO + thus 2 <= CARD (coprimes n) by CARD_SUBSET + or 1 < phi n by phi_def +*) +val phi_gt_1 = store_thm( + "phi_gt_1", + ``!n. 2 < n ==> 1 < phi n``, + rw[phi_def] >> + `0 < n /\ 1 < n /\ n - 1 <> 1` by decide_tac >> + `1 IN (coprimes n)` by rw[coprimes_has_1] >> + `(n - 1) IN (coprimes n)` by rw[coprimes_has_last_but_1] >> + `FINITE (coprimes n)` by rw[coprimes_finite] >> + `{1; (n-1)} SUBSET (coprimes n)` by rw[SUBSET_DEF] >> + `CARD {1; (n-1)} = 2` by rw[] >> + `2 <= CARD (coprimes n)` by metis_tac[CARD_SUBSET] >> + decide_tac); + +(* Theorem: phi n <= n *) +(* Proof: + Note phi n = CARD (coprimes n) by phi_def + and coprimes n SUBSET natural n by coprimes_subset + Now FINITE (natural n) by natural_finite + and CARD (natural n) = n by natural_card + so CARD (coprimes n) <= n by CARD_SUBSET +*) +val phi_le = store_thm( + "phi_le", + ``!n. phi n <= n``, + metis_tac[phi_def, coprimes_subset, natural_finite, natural_card, CARD_SUBSET]); + +(* Theorem: 1 < n ==> phi n < n *) +(* Proof: + Note phi n = CARD (coprimes n) by phi_def + and 1 < n ==> !j. j IN coprimes n ==> j < n by coprimes_element_less + but 0 NOTIN coprimes n by coprimes_no_0 + or coprimes n SUBSET (count n) DIFF {0} by SUBSET_DEF, IN_DIFF + Let s = (count n) DIFF {0}. + Note {0} SUBSET count n by SUBSET_DEF]); + so count n INTER {0} = {0} by SUBSET_INTER_ABSORPTION + Now FINITE s by FINITE_COUNT, FINITE_DIFF + and CARD s = n - 1 by CARD_COUNT, CARD_DIFF, CARD_SING + so CARD (coprimes n) <= n - 1 by CARD_SUBSET + or phi n < n by arithmetic +*) +val phi_lt = store_thm( + "phi_lt", + ``!n. 1 < n ==> phi n < n``, + rw[phi_def] >> + `!j. j IN coprimes n ==> j < n` by rw[coprimes_element_less] >> + `!j. j IN coprimes n ==> j <> 0` by metis_tac[coprimes_no_0] >> + qabbrev_tac `s = (count n) DIFF {0}` >> + `coprimes n SUBSET s` by rw[SUBSET_DEF, Abbr`s`] >> + `{0} SUBSET count n` by rw[SUBSET_DEF] >> + `count n INTER {0} = {0}` by metis_tac[SUBSET_INTER_ABSORPTION, INTER_COMM] >> + `FINITE s` by rw[Abbr`s`] >> + `CARD s = n - 1` by rw[Abbr`s`] >> + `CARD (coprimes n) <= n - 1` by metis_tac[CARD_SUBSET] >> + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Divisors *) +(* ------------------------------------------------------------------------- *) + +(* Define the set of divisors of a number. *) +Definition divisors_def[nocompute]: + divisors n = {d | 0 < d /\ d <= n /\ d divides n} +End +(* use [nocompute] as this is not computationally effective. *) +(* Note: use of 0 < d to have positive divisors, as only 0 divides 0. *) +(* Note: use of d <= n to give divisors_0 = {}, since ALL_DIVIDES_0. *) +(* Note: for 0 < n, d <= n is redundant, as DIVIDES_LE implies it. *) + +(* Theorem: d IN divisors n <=> 0 < d /\ d <= n /\ d divides n *) +(* Proof: by divisors_def *) +Theorem divisors_element: + !n d. d IN divisors n <=> 0 < d /\ d <= n /\ d divides n +Proof + rw[divisors_def] +QED + +(* Theorem: 0 < n ==> !d. d IN divisors n <=> d divides n *) +(* Proof: + If part: d IN divisors n ==> d divides n + This is true by divisors_element + Only-if part: 0 < n /\ d divides n ==> d IN divisors n + Since 0 < n /\ d divides n + ==> 0 < d /\ d <= n by divides_pos + Hence d IN divisors n by divisors_element +*) +Theorem divisors_element_alt: + !n. 0 < n ==> !d. d IN divisors n <=> d divides n +Proof + metis_tac[divisors_element, divides_pos] +QED + +(* Theorem: d IN divisors n ==> 0 < n *) +(* Proof: + Note 0 < d /\ d <= n /\ d divides n by divisors_def + so 0 < n by inequality +*) +Theorem divisors_has_element: + !n d. d IN divisors n ==> 0 < n +Proof + simp[divisors_def] +QED + +(* Theorem: 0 < n ==> 1 IN (divisors n) *) +(* Proof: + Note 1 divides n by ONE_DIVIDES_ALL + Hence 1 IN (divisors n) by divisors_element_alt +*) +Theorem divisors_has_1: + !n. 0 < n ==> 1 IN (divisors n) +Proof + simp[divisors_element_alt] +QED + +(* Theorem: 0 < n ==> n IN (divisors n) *) +(* Proof: + Note n divides n by DIVIDES_REFL + Hence n IN (divisors n) by divisors_element_alt +*) +Theorem divisors_has_last: + !n. 0 < n ==> n IN (divisors n) +Proof + simp[divisors_element_alt] +QED + +(* Theorem: 0 < n ==> divisors n <> {} *) +(* Proof: by divisors_has_last, MEMBER_NOT_EMPTY *) +Theorem divisors_not_empty: + !n. 0 < n ==> divisors n <> {} +Proof + metis_tac[divisors_has_last, MEMBER_NOT_EMPTY] +QED + +(* Theorem: divisors 0 = {} *) +(* Proof: by divisors_def, 0 < d /\ d <= 0 is impossible. *) +Theorem divisors_0: + divisors 0 = {} +Proof + simp[divisors_def] +QED + +(* Theorem: divisors 1 = {1} *) +(* Proof: by divisors_def, 0 < d /\ d <= 1 ==> d = 1. *) +Theorem divisors_1: + divisors 1 = {1} +Proof + rw[divisors_def, EXTENSION] +QED + +(* Theorem: divisors n = {} <=> n = 0 *) +(* Proof: + By EXTENSION, this is to show: + (1) divisors n = {} ==> n = 0 + By contradiction, suppose n <> 0. + Then 1 IN (divisors n) by divisors_has_1 + This contradicts divisors n = {} by MEMBER_NOT_EMPTY + (2) n = 0 ==> divisors n = {} + This is true by divisors_0 +*) +Theorem divisors_eq_empty: + !n. divisors n = {} <=> n = 0 +Proof + rw[EQ_IMP_THM] >- + metis_tac[divisors_has_1, MEMBER_NOT_EMPTY, NOT_ZERO] >> + simp[divisors_0] +QED + +(* Idea: a method to evaluate divisors. *) + +(* Theorem: divisors n = IMAGE (\j. if (j + 1) divides n then j + 1 else 1) (count n) *) +(* Proof: + Let f = \j. if (j + 1) divides n then j + 1 else 1. + If n = 0, + divisors 0 + = {d | 0 < d /\ d <= 0 /\ d divides 0} by divisors_def + = {} by 0 < d /\ d <= 0 + = IMAGE f {} by IMAGE_EMPTY + = IMAGE f (count 0) by COUNT_0 + If n <> 0, + divisors n + = {d | 0 < d /\ d <= n /\ d divides n} by divisors_def + = {d | d <> 0 /\ d <= n /\ d divides n} by 0 < d + = {k + 1 | (k + 1) <= n /\ (k + 1) divides n} + by num_CASES, d <> 0 + = {k + 1 | k < n /\ (k + 1) divides n} by arithmetic + = IMAGE f {k | k < n} by IMAGE_DEF + = IMAGE f (count n) by count_def +*) +Theorem divisors_eqn[compute]: + !n. divisors n = IMAGE (\j. if (j + 1) divides n then j + 1 else 1) (count n) +Proof + (rw[divisors_def, EXTENSION, EQ_IMP_THM] >> rw[]) >> + `?k. x = SUC k` by metis_tac[num_CASES, NOT_ZERO] >> + qexists_tac `k` >> + fs[ADD1] +QED + +(* +> EVAL ``divisors 3``; = {3; 1}: thm +> EVAL ``divisors 4``; = {4; 2; 1}: thm +> EVAL ``divisors 5``; = {5; 1}: thm +> EVAL ``divisors 6``; = {6; 3; 2; 1}: thm +> EVAL ``divisors 7``; = {7; 1}: thm +> EVAL ``divisors 8``; = {8; 4; 2; 1}: thm +> EVAL ``divisors 9``; = {9; 3; 1}: thm +*) + +(* Idea: each factor of a product divides the product. *) + +(* Theorem: 0 < n /\ n = p * q ==> p IN divisors n /\ q IN divisors n *) +(* Proof: + Note 0 < p /\ 0 < q by MULT_EQ_0 + so p <= n /\ q <= n by arithmetic + and p divides n by divides_def + and q divides n by divides_def, MULT_COMM + ==> p IN divisors n /\ + q IN divisors n by divisors_element_alt, 0 < n +*) +Theorem divisors_has_factor: + !n p q. 0 < n /\ n = p * q ==> p IN divisors n /\ q IN divisors n +Proof + (rw[divisors_element_alt] >> metis_tac[MULT_EQ_0, NOT_ZERO]) +QED + +(* Idea: when factor divides, its cofactor also divides. *) + +(* Theorem: d IN divisors n ==> (n DIV d) IN divisors n *) +(* Proof: + Note 0 < d /\ d <= n /\ d divides n by divisors_def + and 0 < n by 0 < d /\ d <= n + so 0 < n DIV d by DIV_POS, 0 < n + and n DIV d <= n by DIV_LESS_EQ, 0 < d + and n DIV d divides n by DIVIDES_COFACTOR, 0 < d + so (n DIV d) IN divisors n by divisors_def +*) +Theorem divisors_has_cofactor: + !n d. d IN divisors n ==> (n DIV d) IN divisors n +Proof + simp [divisors_def] >> + ntac 3 strip_tac >> + `0 < n` by decide_tac >> + rw[DIV_POS, DIV_LESS_EQ, DIVIDES_COFACTOR] +QED + +(* Theorem: (divisors n) DELETE n = {m | 0 < m /\ m < n /\ m divides n} *) +(* Proof: + (divisors n) DELETE n + = {m | 0 < m /\ m <= n /\ m divides n} DELETE n by divisors_def + = {m | 0 < m /\ m <= n /\ m divides n} DIFF {n} by DELETE_DEF + = {m | 0 < m /\ m <> n /\ m <= n /\ m divides n} by IN_DIFF + = {m | 0 < m /\ m < n /\ m divides n} by LESS_OR_EQ +*) +Theorem divisors_delete_last: + !n. (divisors n) DELETE n = {m | 0 < m /\ m < n /\ m divides n} +Proof + rw[divisors_def, EXTENSION, EQ_IMP_THM] +QED + +(* Theorem: d IN (divisors n) ==> 0 < d *) +(* Proof: by divisors_def. *) +Theorem divisors_nonzero: + !n d. d IN (divisors n) ==> 0 < d +Proof + simp[divisors_def] +QED + +(* Theorem: (divisors n) SUBSET (natural n) *) +(* Proof: + By SUBSET_DEF, this is to show: + x IN (divisors n) ==> x IN (natural n) + x IN (divisors n) + ==> 0 < x /\ x <= n /\ x divides n by divisors_element + ==> 0 < x /\ x <= n + ==> x IN (natural n) by natural_element +*) +Theorem divisors_subset_natural: + !n. (divisors n) SUBSET (natural n) +Proof + rw[divisors_element, natural_element, SUBSET_DEF] +QED + +(* Theorem: FINITE (divisors n) *) +(* Proof: + Since (divisors n) SUBSET (natural n) by divisors_subset_natural + and FINITE (naturnal n) by natural_finite + so FINITE (divisors n) by SUBSET_FINITE +*) +Theorem divisors_finite: + !n. FINITE (divisors n) +Proof + metis_tac[divisors_subset_natural, natural_finite, SUBSET_FINITE] +QED + +(* Theorem: BIJ (\d. n DIV d) (divisors n) (divisors n) *) +(* Proof: + By BIJ_DEF, INJ_DEF, SURJ_DEF, this is to show: + (1) d IN divisors n ==> n DIV d IN divisors n + This is true by divisors_has_cofactor + (2) d IN divisors n /\ d' IN divisors n /\ n DIV d = n DIV d' ==> d = d' + d IN divisors n ==> d divides n /\ 0 < d by divisors_element + d' IN divisors n ==> d' divides n /\ 0 < d' by divisors_element + Also d IN divisors n ==> 0 < n by divisors_has_element + Hence n = (n DIV d) * d and n = (n DIV d') * d' by DIVIDES_EQN + giving (n DIV d) * d = (n DIV d') * d' + Now (n DIV d) <> 0, otherwise contradicts n <> 0 by MULT + Hence d = d' by EQ_MULT_LCANCEL + (3) same as (1), true by divisors_has_cofactor + (4) x IN divisors n ==> ?d. d IN divisors n /\ (n DIV d = x) + Note x IN divisors n ==> x divides n by divisors_element + and 0 < n by divisors_has_element + Let d = n DIV x. + Then d IN divisors n by divisors_has_cofactor + and n DIV d = n DIV (n DIV x) = x by divide_by_cofactor, 0 < n +*) +Theorem divisors_divisors_bij: + !n. (\d. n DIV d) PERMUTES divisors n +Proof + rw[BIJ_DEF, INJ_DEF, SURJ_DEF] >- + rw[divisors_has_cofactor] >- + (`n = (n DIV d) * d` by metis_tac[DIVIDES_EQN, divisors_element] >> + `n = (n DIV d') * d'` by metis_tac[DIVIDES_EQN, divisors_element] >> + `0 < n` by metis_tac[divisors_has_element] >> + `n DIV d <> 0` by metis_tac[MULT, NOT_ZERO] >> + metis_tac[EQ_MULT_LCANCEL]) >- + rw[divisors_has_cofactor] >> + `0 < n` by metis_tac[divisors_has_element] >> + metis_tac[divisors_element, divisors_has_cofactor, divide_by_cofactor] +QED + +(* ------------------------------------------------------------------------- *) +(* An upper bound for divisors. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: if a divisor of n is less or equal to (SQRT n), its cofactor is more or equal to (SQRT n) *) + +(* Theorem: 0 < p /\ p divides n /\ p <= SQRT n ==> SQRT n <= (n DIV p) *) +(* Proof: + Let m = SQRT n, then p <= m. + By contradiction, suppose (n DIV p) < m. + Then n = (n DIV p) * p by DIVIDES_EQN, 0 < p + <= (n DIV p) * m by p <= m + < m * m by (n DIV p) < m + <= n by SQ_SQRT_LE + giving n < n, which is a contradiction. +*) +Theorem divisor_le_cofactor_ge: + !n p. 0 < p /\ p divides n /\ p <= SQRT n ==> SQRT n <= (n DIV p) +Proof + rpt strip_tac >> + qabbrev_tac `m = SQRT n` >> + spose_not_then strip_assume_tac >> + `n = (n DIV p) * p` by rfs[DIVIDES_EQN] >> + `(n DIV p) * p <= (n DIV p) * m` by fs[] >> + `(n DIV p) * m < m * m` by fs[] >> + `m * m <= n` by simp[SQ_SQRT_LE, Abbr`m`] >> + decide_tac +QED + +(* Idea: if a divisor of n is greater than (SQRT n), its cofactor is less or equal to (SQRT n) *) + +(* Theorem: 0 < p /\ p divides n /\ SQRT n < p ==> (n DIV p) <= SQRT n *) +(* Proof: + Let m = SQRT n, then m < p. + By contradiction, suppose m < (n DIV p). + Let q = (n DIV p). + Then SUC m <= p, SUC m <= q by m < p, m < q + and n = q * p by DIVIDES_EQN, 0 < p + >= (SUC m) * (SUC m) by LESS_MONO_MULT2 + = (SUC m) ** 2 by EXP_2 + > n by SQRT_PROPERTY + which is a contradiction. +*) +Theorem divisor_gt_cofactor_le: + !n p. 0 < p /\ p divides n /\ SQRT n < p ==> (n DIV p) <= SQRT n +Proof + rpt strip_tac >> + qabbrev_tac `m = SQRT n` >> + spose_not_then strip_assume_tac >> + `n = (n DIV p) * p` by rfs[DIVIDES_EQN] >> + qabbrev_tac `q = n DIV p` >> + `SUC m <= p /\ SUC m <= q` by decide_tac >> + `(SUC m) * (SUC m) <= q * p` by simp[LESS_MONO_MULT2] >> + `n < (SUC m) * (SUC m)` by metis_tac[SQRT_PROPERTY, EXP_2] >> + decide_tac +QED + +(* Idea: for (divisors n), the map (\j. n DIV j) is injective. *) + +(* Theorem: INJ (\j. n DIV j) (divisors n) univ(:num) *) +(* Proof: + By INJ_DEF, this is to show: + (1) !x. x IN (divisors n) ==> (\j. n DIV j) x IN univ(:num) + True by types, n DIV j is a number, with type :num. + (2) !x y. x IN (divisors n) /\ y IN (divisors n) /\ n DIV x = n DIV y ==> x = y + Note x divides n /\ 0 < x /\ x <= n by divisors_def + and y divides n /\ 0 < y /\ x <= n by divisors_def + Let p = n DIV x, q = n DIV y. + Note 0 < n by divisors_has_element + then 0 < p, 0 < q by DIV_POS, 0 < n + Then n = p * x = q * y by DIVIDES_EQN, 0 < x, 0 < y + But p = q by given + so x = y by EQ_MULT_LCANCEL +*) +Theorem divisors_cofactor_inj: + !n. INJ (\j. n DIV j) (divisors n) univ(:num) +Proof + rw[INJ_DEF, divisors_def] >> + `n = n DIV j * j` by fs[GSYM DIVIDES_EQN] >> + `n = n DIV j' * j'` by fs[GSYM DIVIDES_EQN] >> + `0 < n` by fs[GSYM divisors_has_element] >> + metis_tac[EQ_MULT_LCANCEL, DIV_POS, NOT_ZERO] +QED + +(* Idea: an upper bound for CARD (divisors n). + +To prove: 0 < n ==> CARD (divisors n) <= 2 * SQRT n +Idea of proof: + Consider the two sets, + s = {x | x IN divisors n /\ x <= SQRT n} + t = {x | x IN divisors n /\ SQRT n <= x} + Note s SUBSET (natural (SQRT n)), so CARD s <= SQRT n. + Also t SUBSET (natural (SQRT n)), so CARD t <= SQRT n. + There is a bijection between the two parts: + BIJ (\j. n DIV j) s t + Now divisors n = s UNION t + CARD (divisors n) + = CARD s + CARD t - CARD (s INTER t) + <= CARD s + CARD t + <= SQRT n + SQRT n + = 2 * SQRT n + + The BIJ part will be quite difficult. + So the actual proof is a bit different. +*) + +(* Theorem: CARD (divisors n) <= 2 * SQRT n *) +(* Proof: + Let m = SQRT n, + d = divisors n, + s = {x | x IN d /\ x <= m}, + f = \j. n DIV j, + t = IMAGE f s. + + Claim: s SUBSET natural m + Proof: By SUBSET_DEF, this is to show: + x IN d /\ x <= m ==> ?y. x = SUC y /\ y < m + Note 0 < x by divisors_nonzero + Let y = PRE x. + Then x = SUC (PRE x) by SUC_PRE + and PRE x < x by PRE_LESS + so PRE x < m by inequality, x <= m + + Claim: BIJ f s t + Proof: Note s SUBSET d by SUBSET_DEF + and INJ f d univ(:num) by divisors_cofactor_inj + so INJ f s univ(:num) by INJ_SUBSET, SUBSET_REFL + ==> BIJ f s t by INJ_IMAGE_BIJ_ALT + + Claim: d = s UNION t + Proof: By EXTENSION, EQ_IMP_THM, this is to show: + (1) x IN divisors n ==> x <= m \/ ?j. x = n DIV j /\ j IN divisors n /\ j <= m + If x <= m, this is trivial. + Otherwise, m < x. + Let j = n DIV x. + Then x = n DIV (n DIV x) by divide_by_cofactor + and (n DIV j) IN divisors n by divisors_has_cofactor + and (n DIV j) <= m by divisor_gt_cofactor_le + (2) j IN divisors n ==> n DIV j IN divisors n + This is true by divisors_has_cofactor + + Now FINITE (natural m) by natural_finite + so FINITE s by SUBSET_FINITE + and FINITE t by IMAGE_FINITE + so CARD s <= m by CARD_SUBSET, natural_card + Also CARD t = CARD s by FINITE_BIJ_CARD + + CARD d <= CARD s + CARD t by CARD_UNION_LE, d = s UNION t + <= m + m by above + = 2 * m by arithmetic +*) +Theorem divisors_card_upper: + !n. CARD (divisors n) <= 2 * SQRT n +Proof + rpt strip_tac >> + qabbrev_tac `m = SQRT n` >> + qabbrev_tac `d = divisors n` >> + qabbrev_tac `s = {x | x IN d /\ x <= m}` >> + qabbrev_tac `f = \j. n DIV j` >> + qabbrev_tac `t = (IMAGE f s)` >> + `s SUBSET (natural m)` by + (rw[SUBSET_DEF, Abbr`s`] >> + `0 < x` by metis_tac[divisors_nonzero] >> + qexists_tac `PRE x` >> + simp[]) >> + `BIJ f s t` by + (simp[Abbr`t`] >> + irule INJ_IMAGE_BIJ_ALT >> + `s SUBSET d` by rw[SUBSET_DEF, Abbr`s`] >> + `INJ f d univ(:num)` by metis_tac[divisors_cofactor_inj] >> + metis_tac[INJ_SUBSET, SUBSET_REFL]) >> + `d = s UNION t` by + (rw[EXTENSION, Abbr`d`, Abbr`s`, Abbr`t`, Abbr`f`, EQ_IMP_THM] >| [ + (Cases_on `x <= m` >> simp[]) >> + qexists_tac `n DIV x` >> + `0 < x /\ x <= n /\ x divides n` by fs[divisors_element] >> + simp[divide_by_cofactor, divisors_has_cofactor] >> + `m < x` by decide_tac >> + simp[divisor_gt_cofactor_le, Abbr`m`], + simp[divisors_has_cofactor] + ]) >> + `FINITE (natural m)` by simp[natural_finite] >> + `FINITE s /\ FINITE t` by metis_tac[SUBSET_FINITE, IMAGE_FINITE] >> + `CARD s <= m` by metis_tac[CARD_SUBSET, natural_card] >> + `CARD t = CARD s` by metis_tac[FINITE_BIJ_CARD] >> + `CARD d <= CARD s + CARD t` by metis_tac[CARD_UNION_LE] >> + decide_tac +QED + +(* This is a remarkable result! *) + + +(* ------------------------------------------------------------------------- *) +(* Gauss' Little Theorem *) +(* ------------------------------------------------------------------------- *) +(* ------------------------------------------------------------------------- *) +(* Gauss' Little Theorem: sum of phi over divisors *) +(* ------------------------------------------------------------------------- *) +(* ------------------------------------------------------------------------- *) +(* Gauss' Little Theorem: A general theory on sum over divisors *) +(* ------------------------------------------------------------------------- *) + +(* +Let n = 6. (divisors 6) = {1, 2, 3, 6} + IMAGE coprimes (divisors 6) += {coprimes 1, coprimes 2, coprimes 3, coprimes 6} += {{1}, {1}, {1, 2}, {1, 5}} <-- will collapse + IMAGE (preimage (gcd 6) (count 6)) (divisors 6) += {{preimage in count 6 of those gcd 6 j = 1}, + {preimage in count 6 of those gcd 6 j = 2}, + {preimage in count 6 of those gcd 6 j = 3}, + {preimage in count 6 of those gcd 6 j = 6}} += {{1, 5}, {2, 4}, {3}, {6}} += {1x{1, 5}, 2x{1, 2}, 3x{1}, 6x{1}} +!s. s IN (IMAGE (preimage (gcd n) (count n)) (divisors n)) +==> ?d. d divides n /\ d < n /\ s = preimage (gcd n) (count n) d +==> ?d. d divides n /\ d < n /\ s = IMAGE (TIMES d) (coprimes ((gcd n d) DIV d)) + + IMAGE (feq_class (count 6) (gcd 6)) (divisors 6) += {{feq_class in count 6 of those gcd 6 j = 1}, + {feq_class in count 6 of those gcd 6 j = 2}, + {feq_class in count 6 of those gcd 6 j = 3}, + {feq_class in count 6 of those gcd 6 j = 6}} += {{1, 5}, {2, 4}, {3}, {6}} += {1x{1, 5}, 2x{1, 2}, 3x{1}, 6x{1}} +That is: CARD {1, 5} = CARD (coprime 6) = CARD (coprime (6 DIV 1)) + CARD {2, 4} = CARD (coprime 3) = CARD (coprime (6 DIV 2)) + CARD {3} = CARD (coprime 2) = CARD (coprime (6 DIV 3))) + CARD {6} = CARD (coprime 1) = CARD (coprime (6 DIV 6))) + +*) +(* Note: + In general, what is the condition for: SIGMA f s = SIGMA g t ? + Conceptually, + SIGMA f s = f s1 + f s2 + f s3 + ... + f sn + and SIGMA g t = g t1 + g t2 + g t3 + ... + g tm + +SUM_IMAGE_eq_SUM_MAP_SET_TO_LIST + +Use disjoint_bigunion_card +|- !P. FINITE P /\ EVERY_FINITE P /\ PAIR_DISJOINT P ==> (CARD (BIGUNION P) = SIGMA CARD P) +If a partition P = {s | condition on s} the element s = IMAGE g t +e.g. P = {{1, 5} {2, 4} {3} {6}} + = {IMAGE (TIMES 1) (coprimes 6/1), + IMAGE (TIMES 2) (coprimes 6/2), + IMAGE (TIMES 3) (coprimes 6/3), + IMAGE (TIMES 6) (coprimes 6/6)} + = IMAGE (\d. TIMES d o coprimes (6/d)) {1, 2, 3, 6} + +*) + +(* Theorem: d divides n ==> !j. j IN gcd_matches n d ==> j DIV d IN coprimes_by n d *) +(* Proof: + When n = 0, gcd_matches 0 d = {} by gcd_matches_0, hence trivially true. + Otherwise, + By coprimes_by_def, this is to show: + 0 < n /\ d divides n ==> !j. j IN gcd_matches n d ==> j DIV d IN coprimes (n DIV d) + Note j IN gcd_matches n d + ==> d divides j by gcd_matches_element_divides + Also d IN gcd_matches n d by gcd_matches_has_divisor + so 0 < d /\ (d = gcd j n) by gcd_matches_element + or d <> 0 /\ (d = gcd n j) by GCD_SYM + With the given d divides n, + j = d * (j DIV d) by DIVIDES_EQN, MULT_COMM, 0 < d + n = d * (n DIV d) by DIVIDES_EQN, MULT_COMM, 0 < d + Hence d = d * gcd (n DIV d) (j DIV d) by GCD_COMMON_FACTOR + or d * 1 = d * gcd (n DIV d) (j DIV d) by MULT_RIGHT_1 + giving 1 = gcd (n DIV d) (j DIV d) by EQ_MULT_LCANCEL, d <> 0 + or coprime (j DIV d) (n DIV d) by GCD_SYM + Also j IN natural n by gcd_matches_subset, SUBSET_DEF + Hence 0 < j DIV d /\ j DIV d <= n DIV d by natural_cofactor_natural_reduced + or j DIV d IN coprimes (n DIV d) by coprimes_element +*) +val gcd_matches_divisor_element = store_thm( + "gcd_matches_divisor_element", + ``!n d. d divides n ==> !j. j IN gcd_matches n d ==> j DIV d IN coprimes_by n d``, + rpt strip_tac >> + Cases_on `n = 0` >- + metis_tac[gcd_matches_0, NOT_IN_EMPTY] >> + `0 < n` by decide_tac >> + rw[coprimes_by_def] >> + `d divides j` by metis_tac[gcd_matches_element_divides] >> + `0 < d /\ 0 < j /\ j <= n /\ (d = gcd n j)` by metis_tac[gcd_matches_has_divisor, gcd_matches_element, GCD_SYM] >> + `d <> 0` by decide_tac >> + `(j = d * (j DIV d)) /\ (n = d * (n DIV d))` by metis_tac[DIVIDES_EQN, MULT_COMM] >> + `coprime (n DIV d) (j DIV d)` by metis_tac[GCD_COMMON_FACTOR, MULT_RIGHT_1, EQ_MULT_LCANCEL] >> + `0 < j DIV d /\ j DIV d <= n DIV d` by metis_tac[natural_cofactor_natural_reduced, natural_element] >> + metis_tac[coprimes_element, GCD_SYM]); + +(* Theorem: d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes_by n d) *) +(* Proof: + When n = 0, gcd_matches 0 d = {} by gcd_matches_0 + and coprimes_by 0 d = {} by coprimes_by_0, hence trivially true. + Otherwise, + By definitions, this is to show: + (1) j IN gcd_matches n d ==> j DIV d IN coprimes_by n d + True by gcd_matches_divisor_element. + (2) j IN gcd_matches n d /\ j' IN gcd_matches n d /\ j DIV d = j' DIV d ==> j = j' + Note j IN gcd_matches n d /\ j' IN gcd_matches n d + ==> d divides j /\ d divides j' by gcd_matches_element_divides + Also d IN (gcd_matches n d) by gcd_matches_has_divisor + so 0 < d by gcd_matches_element + Thus j = (j DIV d) * d by DIVIDES_EQN, 0 < d + and j' = (j' DIV d) * d by DIVIDES_EQN, 0 < d + Since j DIV d = j' DIV d, j = j'. + (3) same as (1), true by gcd_matches_divisor_element, + (4) d divides n /\ x IN coprimes_by n d ==> ?j. j IN gcd_matches n d /\ (j DIV d = x) + Note x IN coprimes (n DIV d) by coprimes_by_def + ==> 0 < x /\ x <= n DIV d /\ (coprime x (n DIV d)) by coprimes_element + And d divides n /\ 0 < n + ==> 0 < d /\ d <> 0 by ZERO_DIVIDES, 0 < n + Giving (x * d) DIV d = x by MULT_DIV, 0 < d + Let j = x * d. so j DIV d = x by above + Then d divides j by divides_def + ==> j = (j DIV d) * d by DIVIDES_EQN, 0 < d + Note d divides n + ==> n = (n DIV d) * d by DIVIDES_EQN, 0 < d + Hence gcd j n + = gcd (d * (j DIV d)) (d * (n DIV d)) by MULT_COMM + = d * gcd (j DIV d) (n DIV d) by GCD_COMMON_FACTOR + = d * gcd x (n DIV d) by x = j DIV d + = d * 1 by coprime x (n DIV d) + = d by MULT_RIGHT_1 + Since j = x * d, 0 < j by MULT_EQ_0, 0 < x, 0 < d + Also x <= n DIV d + means j DIV d <= n DIV d by x = j DIV d + so (j DIV d) * d <= (n DIV d) * d by LE_MULT_RCANCEL, d <> 0 + or j <= n by above + Hence j IN gcd_matches n d by gcd_matches_element +*) +val gcd_matches_bij_coprimes_by = store_thm( + "gcd_matches_bij_coprimes_by", + ``!n d. d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes_by n d)``, + rpt strip_tac >> + Cases_on `n = 0` >| [ + `gcd_matches n d = {}` by rw[gcd_matches_0] >> + `coprimes_by n d = {}` by rw[coprimes_by_0] >> + rw[], + `0 < n` by decide_tac >> + rw[BIJ_DEF, INJ_DEF, SURJ_DEF, EQ_IMP_THM] >- + rw[GSYM gcd_matches_divisor_element] >- + metis_tac[gcd_matches_element_divides, gcd_matches_has_divisor, gcd_matches_element, DIVIDES_EQN] >- + rw[GSYM gcd_matches_divisor_element] >> + `0 < x /\ x <= n DIV d /\ (coprime x (n DIV d))` by metis_tac[coprimes_by_def, coprimes_element] >> + `0 < d /\ d <> 0` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> + `(x * d) DIV d = x` by rw[MULT_DIV] >> + qabbrev_tac `j = x * d` >> + `d divides j` by metis_tac[divides_def] >> + `(n = (n DIV d) * d) /\ (j = (j DIV d) * d)` by rw[GSYM DIVIDES_EQN] >> + `gcd j n = d` by metis_tac[GCD_COMMON_FACTOR, MULT_COMM, MULT_RIGHT_1] >> + `0 < j` by metis_tac[MULT_EQ_0, NOT_ZERO] >> + `j <= n` by metis_tac[LE_MULT_RCANCEL] >> + metis_tac[gcd_matches_element] + ]); + +(* Theorem: 0 < n /\ d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes (n DIV d)) *) +(* Proof: by gcd_matches_bij_coprimes_by, coprimes_by_by_divisor *) +val gcd_matches_bij_coprimes = store_thm( + "gcd_matches_bij_coprimes", + ``!n d. 0 < n /\ d divides n ==> BIJ (\j. j DIV d) (gcd_matches n d) (coprimes (n DIV d))``, + metis_tac[gcd_matches_bij_coprimes_by, coprimes_by_by_divisor]); + +(* Note: it is not useful to show: + CARD o (gcd_matches n) = CARD o coprimes, + as FUN_EQ_THM will demand: CARD (gcd_matches n x) = CARD (coprimes x), + which is not possible. +*) + +(* Theorem: divisors n = IMAGE (gcd n) (natural n) *) +(* Proof: + divisors n + = {d | 0 < d /\ d <= n /\ d divides n} by divisors_def + = {d | d IN (natural n) /\ d divides n} by natural_element + = {d | d IN (natural n) /\ (gcd d n = d)} by divides_iff_gcd_fix + = {d | d IN (natural n) /\ (gcd n d = d)} by GCD_SYM + = {gcd n d | d | d IN (natural n)} by replacemnt + = IMAGE (gcd n) (natural n) by IMAGE_DEF + The replacemnt requires: + d IN (natural n) ==> gcd n d IN (natural n) + d IN (natural n) ==> gcd n (gcd n d) = gcd n d + which are given below. + + Or, by divisors_def, natuarl_elemnt, IN_IMAGE, this is to show: + (1) 0 < x /\ x <= n /\ x divides n ==> ?y. (x = gcd n y) /\ 0 < y /\ y <= n + Note x divides n ==> gcd x n = x by divides_iff_gcd_fix + or gcd n x = x by GCD_SYM + Take this x, and the result follows. + (2) 0 < y /\ y <= n ==> 0 < gcd n y /\ gcd n y <= n /\ gcd n y divides n + Note 0 < n by arithmetic + and gcd n y divides n by GCD_IS_GREATEST_COMMON_DIVISOR, 0 < n + and 0 < gcd n y by GCD_EQ_0, n <> 0 + and gcd n y <= n by DIVIDES_LE, 0 < n +*) +Theorem divisors_eq_gcd_image: + !n. divisors n = IMAGE (gcd n) (natural n) +Proof + rw_tac std_ss[divisors_def, GSPECIFICATION, EXTENSION, IN_IMAGE, natural_element, EQ_IMP_THM] >| [ + `0 < n` by decide_tac >> + metis_tac[divides_iff_gcd_fix, GCD_SYM], + metis_tac[GCD_EQ_0, NOT_ZERO], + `0 < n` by decide_tac >> + metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_LE], + metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] + ] +QED + +(* Theorem: feq_class (gcd n) (natural n) d = gcd_matches n d *) +(* Proof: + feq_class (gcd n) (natural n) d + = {x | x IN natural n /\ (gcd n x = d)} by feq_class_def + = {j | j IN natural n /\ (gcd j n = d)} by GCD_SYM + = gcd_matches n d by gcd_matches_def +*) +val gcd_eq_equiv_class = store_thm( + "gcd_eq_equiv_class", + ``!n d. feq_class (gcd n) (natural n) d = gcd_matches n d``, + rewrite_tac[gcd_matches_def] >> + rw[EXTENSION, GCD_SYM, in_preimage]); + +(* Theorem: feq_class (gcd n) (natural n) = gcd_matches n *) +(* Proof: by FUN_EQ_THM, gcd_eq_equiv_class *) +val gcd_eq_equiv_class_fun = store_thm( + "gcd_eq_equiv_class_fun", + ``!n. feq_class (gcd n) (natural n) = gcd_matches n``, + rw[FUN_EQ_THM, gcd_eq_equiv_class]); + +(* Theorem: partition (feq (gcd n)) (natural n) = IMAGE (gcd_matches n) (divisors n) *) +(* Proof: + partition (feq (gcd n)) (natural n) + = IMAGE (equiv_class (feq (gcd n)) (natural n)) (natural n) by partition_elements + = IMAGE ((feq_class (gcd n) (natural n)) o (gcd n)) (natural n) by feq_class_fun + = IMAGE ((gcd_matches n) o (gcd n)) (natural n) by gcd_eq_equiv_class_fun + = IMAGE (gcd_matches n) (IMAGE (gcd n) (natural n)) by IMAGE_COMPOSE + = IMAGE (gcd_matches n) (divisors n) by divisors_eq_gcd_image, 0 < n +*) +Theorem gcd_eq_partition_by_divisors: + !n. partition (feq (gcd n)) (natural n) = IMAGE (gcd_matches n) (divisors n) +Proof + rpt strip_tac >> + qabbrev_tac `f = gcd n` >> + qabbrev_tac `s = natural n` >> + `partition (feq f) s = IMAGE (equiv_class (feq f) s) s` by rw[partition_elements] >> + `_ = IMAGE ((feq_class f s) o f) s` by rw[feq_class_fun] >> + `_ = IMAGE ((gcd_matches n) o f) s` by rw[gcd_eq_equiv_class_fun, Abbr`f`, Abbr`s`] >> + `_ = IMAGE (gcd_matches n) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> + `_ = IMAGE (gcd_matches n) (divisors n)` by rw[divisors_eq_gcd_image, Abbr`f`, Abbr`s`] >> + simp[] +QED + +(* Theorem: (feq (gcd n)) equiv_on (natural n) *) +(* Proof: + By feq_equiv |- !s f. feq f equiv_on s + Taking s = upto n, f = natural n. +*) +val gcd_eq_equiv_on_natural = store_thm( + "gcd_eq_equiv_on_natural", + ``!n. (feq (gcd n)) equiv_on (natural n)``, + rw[feq_equiv]); + +(* Theorem: SIGMA f (natural n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) *) +(* Proof: + Let g = gcd n, s = natural n. + Since FINITE s by natural_finite + and (feq g) equiv_on s by feq_equiv + The result follows by set_sigma_by_partition +*) +val sum_over_natural_by_gcd_partition = store_thm( + "sum_over_natural_by_gcd_partition", + ``!f n. SIGMA f (natural n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n))``, + rw[feq_equiv, natural_finite, set_sigma_by_partition]); + +(* Theorem: SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) *) +(* Proof: + SIGMA f (natural n) + = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) by sum_over_natural_by_gcd_partition + = SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) by gcd_eq_partition_by_divisors +*) +Theorem sum_over_natural_by_divisors: + !f n. SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (gcd_matches n) (divisors n)) +Proof + simp[sum_over_natural_by_gcd_partition, gcd_eq_partition_by_divisors] +QED + +(* Theorem: INJ (gcd_matches n) (divisors n) univ(num) *) +(* Proof: + By INJ_DEF, this is to show: + x IN divisors n /\ y IN divisors n /\ gcd_matches n x = gcd_matches n y ==> x = y + Note 0 < x /\ x <= n /\ x divides n by divisors_def + also 0 < y /\ y <= n /\ y divides n by divisors_def + Hence (gcd x n = x) /\ (gcd y n = y) by divides_iff_gcd_fix + ==> x IN gcd_matches n x by gcd_matches_element + so x IN gcd_matches n y by gcd_matches n x = gcd_matches n y + with gcd x n = y by gcd_matches_element + Therefore y = gcd x n = x. +*) +Theorem gcd_matches_from_divisors_inj: + !n. INJ (gcd_matches n) (divisors n) univ(:num -> bool) +Proof + rw[INJ_DEF] >> + fs[divisors_def] >> + `(gcd x n = x) /\ (gcd y n = y)` by rw[GSYM divides_iff_gcd_fix] >> + metis_tac[gcd_matches_element] +QED + +(* Theorem: CARD o (gcd_matches n) = CARD o (coprimes_by n) *) +(* Proof: + By composition and FUN_EQ_THM, this is to show: + !x. CARD (gcd_matches n x) = CARD (coprimes_by n x) + If x divides n, + Then BIJ (\j. j DIV x) (gcd_matches n x) (coprimes_by n x) by gcd_matches_bij_coprimes_by + Also FINITE (gcd_matches n x) by gcd_matches_finite + and FINITE (coprimes_by n x) by coprimes_by_finite + Hence CARD (gcd_matches n x) = CARD (coprimes_by n x) by FINITE_BIJ_CARD_EQ + If ~(x divides n), + If n = 0, + then gcd_matches 0 x = {} by gcd_matches_0 + and coprimes_by 0 x = {} by coprimes_by_0 + Hence true. + If n <> 0, + then gcd_matches n x = {} by gcd_matches_eq_empty, 0 < n + and coprimes_by n x = {} by coprimes_by_eq_empty, 0 < n + Hence CARD {} = CARD {}. +*) +val gcd_matches_and_coprimes_by_same_size = store_thm( + "gcd_matches_and_coprimes_by_same_size", + ``!n. CARD o (gcd_matches n) = CARD o (coprimes_by n)``, + rw[FUN_EQ_THM] >> + Cases_on `x divides n` >| [ + `BIJ (\j. j DIV x) (gcd_matches n x) (coprimes_by n x)` by rw[gcd_matches_bij_coprimes_by] >> + `FINITE (gcd_matches n x)` by rw[gcd_matches_finite] >> + `FINITE (coprimes_by n x)` by rw[coprimes_by_finite] >> + metis_tac[FINITE_BIJ_CARD_EQ], + Cases_on `n = 0` >- + rw[gcd_matches_0, coprimes_by_0] >> + `gcd_matches n x = {}` by rw[gcd_matches_eq_empty] >> + `coprimes_by n x = {}` by rw[coprimes_by_eq_empty] >> + rw[] + ]); + +(* Theorem: 0 < n ==> (CARD o (coprimes_by n) = \d. phi (if d IN (divisors n) then n DIV d else 0)) *) +(* Proof: + By FUN_EQ_THM, + CARD o (coprimes_by n) x + = CARD (coprimes_by n x) by composition, combinTheory.o_THM + = CARD (if x divides n then coprimes (n DIV x) else {}) by coprimes_by_def, 0 < n + If x divides n, + then x <= n by DIVIDES_LE + and 0 < x by divisor_pos, 0 < n + so x IN (divisors n) by divisors_element + CARD o (coprimes_by n) x + = CARD (coprimes (n DIV x)) + = phi (n DIV x) by phi_def + If ~(x divides n), + x NOTIN (divisors n) by divisors_element + CARD o (coprimes_by n) x + = CARD {} + = 0 by CARD_EMPTY + = phi 0 by phi_0 + Hence the same function as: + \d. phi (if d IN (divisors n) then n DIV d else 0) +*) +Theorem coprimes_by_with_card: + !n. 0 < n ==> (CARD o (coprimes_by n) = \d. phi (if d IN (divisors n) then n DIV d else 0)) +Proof + rw[coprimes_by_def, phi_def, divisors_def, FUN_EQ_THM] >> + metis_tac[DIVIDES_LE, divisor_pos, coprimes_0] +QED + +(* Theorem: x IN (divisors n) ==> (CARD o (coprimes_by n)) x = (\d. phi (n DIV d)) x *) +(* Proof: + Since x IN (divisors n) ==> x divides n by divisors_element + CARD o (coprimes_by n) x + = CARD (coprimes (n DIV x)) by coprimes_by_def + = phi (n DIV x) by phi_def +*) +Theorem coprimes_by_divisors_card: + !n x. x IN (divisors n) ==> (CARD o (coprimes_by n)) x = (\d. phi (n DIV d)) x +Proof + rw[coprimes_by_def, phi_def, divisors_def] +QED + +(* +SUM_IMAGE_CONG |- (s1 = s2) /\ (!x. x IN s2 ==> (f1 x = f2 x)) ==> (SIGMA f1 s1 = SIGMA f2 s2) +*) + +(* Theorem: SIGMA phi (divisors n) = n *) +(* Proof: + Note INJ (gcd_matches n) (divisors n) univ(:num -> bool) by gcd_matches_from_divisors_inj + and (\d. n DIV d) PERMUTES (divisors n) by divisors_divisors_bij + n = CARD (natural n) by natural_card + = SIGMA CARD (partition (feq (gcd n)) (natural n)) by partition_CARD + = SIGMA CARD (IMAGE (gcd_matches n) (divisors n)) by gcd_eq_partition_by_divisors + = SIGMA (CARD o (gcd_matches n)) (divisors n) by sum_image_by_composition + = SIGMA (CARD o (coprimes_by n)) (divisors n) by gcd_matches_and_coprimes_by_same_size + = SIGMA (\d. phi (n DIV d)) (divisors n) by SUM_IMAGE_CONG, coprimes_by_divisors_card + = SIGMA phi (divisors n) by sum_image_by_permutation +*) +Theorem Gauss_little_thm: + !n. SIGMA phi (divisors n) = n +Proof + rpt strip_tac >> + `FINITE (natural n)` by rw[natural_finite] >> + `(feq (gcd n)) equiv_on (natural n)` by rw[gcd_eq_equiv_on_natural] >> + `INJ (gcd_matches n) (divisors n) univ(:num -> bool)` by rw[gcd_matches_from_divisors_inj] >> + `(\d. n DIV d) PERMUTES (divisors n)` by rw[divisors_divisors_bij] >> + `FINITE (divisors n)` by rw[divisors_finite] >> + `n = CARD (natural n)` by rw[natural_card] >> + `_ = SIGMA CARD (partition (feq (gcd n)) (natural n))` by rw[partition_CARD] >> + `_ = SIGMA CARD (IMAGE (gcd_matches n) (divisors n))` by rw[gcd_eq_partition_by_divisors] >> + `_ = SIGMA (CARD o (gcd_matches n)) (divisors n)` by prove_tac[sum_image_by_composition] >> + `_ = SIGMA (CARD o (coprimes_by n)) (divisors n)` by rw[gcd_matches_and_coprimes_by_same_size] >> + `_ = SIGMA (\d. phi (n DIV d)) (divisors n)` by rw[SUM_IMAGE_CONG, coprimes_by_divisors_card] >> + `_ = SIGMA phi (divisors n)` by metis_tac[sum_image_by_permutation] >> + decide_tac +QED + +(* This is a milestone theorem. *) + +(* ------------------------------------------------------------------------- *) +(* Euler phi function is multiplicative for coprimes. *) +(* ------------------------------------------------------------------------- *) + +(* +EVAL ``coprimes 2``; = {1} +EVAL ``coprimes 3``; = {2; 1} +EVAL ``coprimes 6``; = {5; 1} + +Let phi(n) = the set of remainders coprime to n and not exceeding n. +Then phi(2) = {1}, phi(3) = {1,2} +We shall show phi(6) = {z = (3 * x + 2 * y) mod 6 | x IN phi(2), y IN phi(3)}. +(1,1) corresponds to z = (3 * 1 + 2 * 1) mod 6 = 5, right! +(1,2) corresponds to z = (3 * 1 + 2 * 2) mod 6 = 1, right! +*) + +(* Idea: give an expression for coprimes (m * n). *) + +(* Theorem: coprime m n ==> + coprimes (m * n) = + IMAGE (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) + ((coprimes m) CROSS (coprimes n)) *) +(* Proof: + Let f = \(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n). + If m = 0 or n = 0, + When m = 0, to show: + coprimes 0 = IMAGE f ((coprimes 0) CROSS (coprimes n)) + RHS + = IMAGE f ({} CROSS (coprimes n)) by coprimes_0 + = IMAGE f {} by CROSS_EMPTY + = {} by IMAGE_EMPTY + = LHS by coprimes_0 + When n = 0, to show: + coprimes 0 = IMAGE f ((coprimes m) CROSS (coprimes 0)) + RHS + = IMAGE f ((coprimes n) CROSS {}) by coprimes_0 + = IMAGE f {} by CROSS_EMPTY + = {} by IMAGE_EMPTY + = LHS by coprimes_0 + + If m = 1, or n = 1, + When m = 1, to show: + coprimes n = IMAGE f ((coprimes 1) CROSS (coprimes n)) + RHS + = IMAGE f ({1} CROSS (coprimes n)) by coprimes_1 + = IMAGE f {(1,y) | y IN coprimes n} by IN_CROSS + = {if n = 1 then 1 else (n + y) MOD n | y IN coprimes n} + by IN_IMAGE + = {1} if n = 1, or {y MOD n | y IN coprimes n} if 1 < n + = {1} if n = 1, or {y | y IN coprimes n} if 1 < n + by coprimes_element_alt, LESS_MOD, y < n + = LHS by coprimes_1 + When n = 1, to show: + coprimes m = IMAGE f ((coprimes m) CROSS (coprimes 1)) + RHS + = IMAGE f ((coprimes m) CROSS {1}) by coprimes_1 + = IMAGE f {(x,1) | x IN coprimes m} by IN_CROSS + = {if m = 1 then 1 else (x + m) MOD m | x IN coprimes m} + by IN_IMAGE + = {1} if m = 1, or {x MOD m | x IN coprimes m} if 1 < m + = {1} if m = 1, or {x | x IN coprimes m} if 1 < m + by coprimes_element_alt, LESS_MOD, x < m + = LHS by coprimes_1 + + Now, 1 < m, 1 < n, and 0 < m, 0 < n. + Therefore 1 < m * n, and 0 < m * n. by MULT_EQ_1, MULT_EQ_0 + and function f = \(x,y). (x * n + y * m) MOD (m * n). + If part: z IN coprimes (m * n) ==> + ?x y. z = (x * n + y * m) MOD (m * n) /\ x IN coprimes m /\ y IN coprimes n + Note z < m * n /\ coprime z (m * n) by coprimes_element_alt, 1 < m * n + for x < m /\ coprime x m, and y < n /\ coprime y n + by coprimes_element_alt, 1 < m, 1 < n + Now ?p q. (p * m + q * n) MOD (m * n) + = z MOD (m * n) by coprime_multiple_linear_mod_prod + = z by LESS_MOD, z < m * n + Note ?h x. p = h * n + x /\ x < n by DA, 0 < n + and ?k y. q = k * m + y /\ y < m by DA, 0 < m + z + = (p * m + q * n) MOD (m * n) by above + = (h * n * m + x * m + k * m * n + y * n) MOD (m * n) + = ((x * m + y * n) + (h + k) * (m * n)) MOD (m * n) + = (x * m + y * n) MOD (m * n) by MOD_PLUS2, MOD_EQ_0 + Take these x and y, but need to show: + (1) coprime x n + Let g = gcd x n, + Then g divides x /\ g divides n by GCD_PROPERTY + so g divides (m * n) by DIVIDES_MULTIPLE + so g divides z by divides_linear, mod_divides_divides + ==> g = 1, or coprime x n by coprime_common_factor + (2) coprime y m + Let g = gcd y m, + Then g divides y /\ g divides m by GCD_PROPERTY + so g divides (m * n) by DIVIDES_MULTIPLE + so g divides z by divides_linear, mod_divides_divides + ==> g = 1, or coprime y m by coprime_common_factor + + Only-if part: coprime m n /\ x IN coprimes m /\ y IN coprimes n ==> + (x * n + y * m) MOD (m * n) IN coprimes (m * n) + Note x < m /\ coprime x m by coprimes_element_alt, 1 < m + and y < n /\ coprime y n by coprimes_element_alt, 1 < n + Let z = x * m + y * n. + Then coprime z (m * n) by coprime_linear_mult + so coprime (z MOD (m * n)) (m * n) by GCD_MOD_COMM + and z MOD (m * n) < m * n by MOD_LESS, 0 < m * n +*) +Theorem coprimes_mult_by_image: + !m n. coprime m n ==> + coprimes (m * n) = + IMAGE (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) + ((coprimes m) CROSS (coprimes n)) +Proof + rpt strip_tac >> + Cases_on `m = 0 \/ n = 0` >- + fs[coprimes_0] >> + Cases_on `m = 1 \/ n = 1` >| [ + fs[coprimes_1] >| [ + rw[EXTENSION, pairTheory.EXISTS_PROD] >> + Cases_on `n = 1` >- + simp[coprimes_1] >> + fs[coprimes_element_alt] >> + metis_tac[LESS_MOD], + rw[EXTENSION, pairTheory.EXISTS_PROD] >> + Cases_on `m = 1` >- + simp[coprimes_1] >> + fs[coprimes_element_alt] >> + metis_tac[LESS_MOD] + ], + `m * n <> 0 /\ m * n <> 1` by rw[] >> + `1 < m /\ 1 < n /\ 1 < m * n` by decide_tac >> + rw[EXTENSION, pairTheory.EXISTS_PROD] >> + rw[EQ_IMP_THM] >| [ + rfs[coprimes_element_alt] >> + `1 < m /\ 1 < n /\ 0 < m /\ 0 < n /\ 0 < m * n` by decide_tac >> + `?p q. (p * m + q * n) MOD (m * n) = x MOD (m * n)` by rw[coprime_multiple_linear_mod_prod] >> + `?h u. p = h * n + u /\ u < n` by metis_tac[DA] >> + `?k v. q = k * m + v /\ v < m` by metis_tac[DA] >> + `p * m + q * n = h * n * m + u * m + k * m * n + v * n` by simp[] >> + `_ = (u * m + v * n) + (h + k) * (m * n)` by simp[] >> + `(u * m + v * n) MOD (m * n) = x MOD (m * n)` by metis_tac[MOD_PLUS2, MOD_EQ_0, ADD_0] >> + `_ = x` by rw[] >> + `coprime u n` by + (qabbrev_tac `g = gcd u n` >> + `0 < g` by rw[GCD_POS, Abbr`g`] >> + `g divides u /\ g divides n` by metis_tac[GCD_PROPERTY] >> + `g divides (m * n)` by rw[DIVIDES_MULTIPLE] >> + `g divides x` by metis_tac[divides_linear, MULT_COMM, mod_divides_divides] >> + metis_tac[coprime_common_factor]) >> + `coprime v m` by + (qabbrev_tac `g = gcd v m` >> + `0 < g` by rw[GCD_POS, Abbr`g`] >> + `g divides v /\ g divides m` by metis_tac[GCD_PROPERTY] >> + `g divides (m * n)` by metis_tac[DIVIDES_MULTIPLE, MULT_COMM] >> + `g divides x` by metis_tac[divides_linear, MULT_COMM, mod_divides_divides] >> + metis_tac[coprime_common_factor]) >> + metis_tac[MULT_COMM], + rfs[coprimes_element_alt] >> + `0 < m * n` by decide_tac >> + `coprime (m * p_2 + n * p_1) (m * n)` by metis_tac[coprime_linear_mult, MULT_COMM] >> + metis_tac[GCD_MOD_COMM] + ] + ] +QED + +(* Yes! a milestone theorem. *) + +(* Idea: in coprimes (m * n), the image map is injective. *) + +(* Theorem: coprime m n ==> + INJ (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) + ((coprimes m) CROSS (coprimes n)) univ(:num) *) +(* Proof: + Let f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n). + To show: coprime m n ==> INJ f ((coprimes m) CROSS (coprimes n)) univ(:num) + If m = 0, or n = 0, + When m = 0, + INJ f ((coprimes 0) CROSS (coprimes n)) univ(:num) + <=> INJ f ({} CROSS (coprimes n)) univ(:num) by coprimes_0 + <=> INJ f {} univ(:num) by CROSS_EMPTY + <=> T by INJ_EMPTY + When n = 0, + INJ f ((coprimes m) CROSS (coprimes 0)) univ(:num) + <=> INJ f ((coprimes m) CROSS {}) univ(:num) by coprimes_0 + <=> INJ f {} univ(:num) by CROSS_EMPTY + <=> T by INJ_EMPTY + + If m = 1, or n = 1, + When m = 1, + INJ f ((coprimes 1) CROSS (coprimes n)) univ(:num) + <=> INJ f ({1} CROSS (coprimes n)) univ(:num) by coprimes_1 + If n = 1, this is + INJ f ({1} CROSS {1}) univ(:num) by coprimes_1 + <=> INJ f {(1,1)} univ(:num) by CROSS_SINGS + <=> T by INJ_DEF + If n <> 1, this is by INJ_DEF: + to show: !p q. p IN coprimes n /\ q IN coprimes n ==> p MOD n = q MOD n ==> p = q + Now p < n /\ q < n by coprimes_element_alt, 1 < n + With p MOD n = q MOD n, so p = q by LESS_MOD + When n = 1, + INJ f ((coprimes m) CROSS (coprimes 1)) univ(:num) + <=> INJ f ((coprimes m) CROSS {1}) univ(:num) by coprimes_1 + If m = 1, this is + INJ f ({1} CROSS {1}) univ(:num) by coprimes_1 + <=> INJ f {(1,1)} univ(:num) by CROSS_SINGS + <=> T by INJ_DEF + If m <> 1, this is by INJ_DEF: + to show: !p q. p IN coprimes m /\ q IN coprimes m ==> p MOD m = q MOD m ==> p = q + Now p < m /\ q < m by coprimes_element_alt, 1 < m + With p MOD m = q MOD m, so p = q by LESS_MOD + + Now 1 < m and 1 < n, so 1 < m * n by MULT_EQ_1, MULT_EQ_0 + By INJ_DEF, coprimes_element_alt, this is to show: + !x y u v. x < m /\ coprime x m /\ y < n /\ coprime y n /\ + u < m /\ coprime u m /\ v < n /\ coprime v n /\ + (x * n + y * m) MOD (m * n) = (u * n + v * m) MOD (m * n) + ==> x = u /\ y = v + Note x * n < n * m by LT_MULT_RCANCEL, 0 < n, x < m + and v * m < n * m by LT_MULT_RCANCEL, 0 < m, v < n + Thus (y * m + (n * m - v * m)) MOD (n * m) + = (u * n + (n * m - x * n)) MOD (n * m) by mod_add_eq_sub + Now y * m + (n * m - v * m) = m * (n + y - v) by arithmetic + and u * n + (n * m - x * n) = n * (m + u - x) by arithmetic + and 0 < n + y - v /\ n + y - v < 2 * n by y < n, v < n + and 0 < m + u - x /\ m + u - x < 2 * m by x < m, u < m + ==> n + y - v = n /\ m + u - x = m by mod_mult_eq_mult + ==> n + y = n + v /\ m + u = m + x by arithmetic + ==> y = v /\ x = u by EQ_ADD_LCANCEL +*) +Theorem coprimes_map_cross_inj: + !m n. coprime m n ==> + INJ (\(x,y). if (m * n = 1) then 1 else (x * n + y * m) MOD (m * n)) + ((coprimes m) CROSS (coprimes n)) univ(:num) +Proof + rpt strip_tac >> + qabbrev_tac `f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)` >> + Cases_on `m = 0 \/ n = 0` >- + fs[coprimes_0] >> + Cases_on `m = 1 \/ n = 1` >| [ + fs[coprimes_1, INJ_DEF, pairTheory.FORALL_PROD, Abbr`f`] >| [ + (Cases_on `n = 1` >> simp[coprimes_1]) >> + fs[coprimes_element_alt], + (Cases_on `m = 1` >> simp[coprimes_1]) >> + fs[coprimes_element_alt] + ], + `m * n <> 0 /\ m * n <> 1` by rw[] >> + `1 < m /\ 1 < n /\ 1 < m * n` by decide_tac >> + simp[INJ_DEF, pairTheory.FORALL_PROD] >> + ntac 6 strip_tac >> + rfs[coprimes_element_alt, Abbr`f`] >> + `0 < m /\ 0 < n /\ 0 < m * n` by decide_tac >> + `n * p_1 < n * m /\ m * p_2' < n * m` by simp[] >> + `(m * p_2 + (n * m - m * p_2')) MOD (n * m) = + (n * p_1' + (n * m - n * p_1)) MOD (n * m)` by simp[GSYM mod_add_eq_sub] >> + `m * p_2 + (n * m - m * p_2') = m * (n + p_2 - p_2')` by decide_tac >> + `n * p_1' + (n * m - n * p_1) = n * (m + p_1' - p_1)` by decide_tac >> + `0 < n + p_2 - p_2' /\ n + p_2 - p_2' < 2 * n` by decide_tac >> + `0 < m + p_1' - p_1 /\ m + p_1' - p_1 < 2 * m` by decide_tac >> + `n + p_2 - p_2' = n /\ m + p_1' - p_1 = m` by metis_tac[mod_mult_eq_mult, MULT_COMM] >> + simp[] + ] +QED + +(* Another milestone theorem! *) + +(* Idea: Euler phi function is multiplicative for coprimes. *) + +(* Theorem: coprime m n ==> phi (m * n) = phi m * phi n *) +(* Proof: + Let f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n), + u = coprimes m, + v = coprimes n. + Then coprimes (m * n) = IMAGE f (u CROSS v) by coprimes_mult_by_image + and INJ f (u CROSS v) univ(:num) by coprimes_map_cross_inj + Note FINITE u /\ FINITE v by coprimes_finite + so FINITE (u CROSS v) by FINITE_CROSS + phi (m * n) + = CARD (coprimes (m * n)) by phi_def + = CARD (IMAGE f (u CROSS v)) by above + = CARD (u CROSS v) by INJ_CARD_IMAGE + = (CARD u) * (CARD v) by CARD_CROSS + = phi m * phi n by phi_def +*) +Theorem phi_mult: + !m n. coprime m n ==> phi (m * n) = phi m * phi n +Proof + rw[phi_def] >> + imp_res_tac coprimes_mult_by_image >> + imp_res_tac coprimes_map_cross_inj >> + qabbrev_tac `f = \(x,y). if m * n = 1 then 1 else (x * n + y * m) MOD (m * n)` >> + qabbrev_tac `u = coprimes m` >> + qabbrev_tac `v = coprimes n` >> + `FINITE u /\ FINITE v` by rw[coprimes_finite, Abbr`u`, Abbr`v`] >> + `FINITE (u CROSS v)` by rw[] >> + metis_tac[INJ_CARD_IMAGE, CARD_CROSS] +QED + +(* This is the ultimate goal! *) + +(* Idea: an expression for phi (p * q) with distinct primes p and q. *) + +(* Theorem: prime p /\ prime q /\ p <> q ==> phi (p * q) = (p - 1) * (q - 1) *) +(* Proof: + Note coprime p q by primes_coprime + phi (p * q) + = phi p * phi q by phi_mult + = (p - 1) * (q - 1) by phi_prime +*) +Theorem phi_primes_distinct: + !p q. prime p /\ prime q /\ p <> q ==> phi (p * q) = (p - 1) * (q - 1) +Proof + simp[primes_coprime, phi_mult, phi_prime] +QED + +(* ------------------------------------------------------------------------- *) +(* Euler phi function for prime powers. *) +(* ------------------------------------------------------------------------- *) + +(* +EVAL ``coprimes 9``; = {8; 7; 5; 4; 2; 1} +EVAL ``divisors 9``; = {9; 3; 1} +EVAL ``IMAGE (\x. 3 * x) (natural 3)``; = {9; 6; 3} +EVAL ``IMAGE (\x. 3 * x) (natural 9)``; = {27; 24; 21; 18; 15; 12; 9; 6; 3} + +> EVAL ``IMAGE ($* 3) (natural (8 DIV 3))``; = {6; 3} +> EVAL ``IMAGE ($* 3) (natural (9 DIV 3))``; = {9; 6; 3} +> EVAL ``IMAGE ($* 3) (natural (10 DIV 3))``; = {9; 6; 3} +> EVAL ``IMAGE ($* 3) (natural (12 DIV 3))``; = {12; 9; 6; 3} +*) + +(* Idea: develop a special set in anticipation for counting. *) + +(* Define the set of positive multiples of m, up to n *) +val multiples_upto_def = zDefine` + multiples_upto m n = {x | m divides x /\ 0 < x /\ x <= n} +`; +(* use zDefine as this is not effective for evalutaion. *) +(* make this an infix operator *) +val _ = set_fixity "multiples_upto" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) + +(* +> multiples_upto_def; +val it = |- !m n. m multiples_upto n = {x | m divides x /\ 0 < x /\ x <= n}: thm +*) + +(* Theorem: x IN m multiples_upto n <=> m divides x /\ 0 < x /\ x <= n *) +(* Proof: by multiples_upto_def. *) +Theorem multiples_upto_element: + !m n x. x IN m multiples_upto n <=> m divides x /\ 0 < x /\ x <= n +Proof + simp[multiples_upto_def] +QED + +(* Theorem: m multiples_upto n = {x | ?k. x = k * m /\ 0 < x /\ x <= n} *) +(* Proof: + m multiples_upto n + = {x | m divides x /\ 0 < x /\ x <= n} by multiples_upto_def + = {x | ?k. x = k * m /\ 0 < x /\ x <= n} by divides_def +*) +Theorem multiples_upto_alt: + !m n. m multiples_upto n = {x | ?k. x = k * m /\ 0 < x /\ x <= n} +Proof + rw[multiples_upto_def, EXTENSION] >> + metis_tac[divides_def] +QED + +(* Theorem: x IN m multiples_upto n <=> ?k. x = k * m /\ 0 < x /\ x <= n *) +(* Proof: by multiples_upto_alt. *) +Theorem multiples_upto_element_alt: + !m n x. x IN m multiples_upto n <=> ?k. x = k * m /\ 0 < x /\ x <= n +Proof + simp[multiples_upto_alt] +QED + +(* Theorem: m multiples_upto n = {x | m divides x /\ x IN natural n} *) +(* Proof: + m multiples_upto n + = {x | m divides x /\ 0 < x /\ x <= n} by multiples_upto_def + = {x | m divides x /\ x IN natural n} by natural_element +*) +Theorem multiples_upto_eqn: + !m n. m multiples_upto n = {x | m divides x /\ x IN natural n} +Proof + simp[multiples_upto_def, natural_element, EXTENSION] +QED + +(* Theorem: 0 multiples_upto n = {} *) +(* Proof: + 0 multiples_upto n + = {x | 0 divides x /\ 0 < x /\ x <= n} by multiples_upto_def + = {x | x = 0 /\ 0 < x /\ x <= n} by ZERO_DIVIDES + = {} by contradiction +*) +Theorem multiples_upto_0_n: + !n. 0 multiples_upto n = {} +Proof + simp[multiples_upto_def, EXTENSION] +QED + +(* Theorem: 1 multiples_upto n = natural n *) +(* Proof: + 1 multiples_upto n + = {x | 1 divides x /\ x IN natural n} by multiples_upto_eqn + = {x | T /\ x IN natural n} by ONE_DIVIDES_ALL + = natural n by EXTENSION +*) +Theorem multiples_upto_1_n: + !n. 1 multiples_upto n = natural n +Proof + simp[multiples_upto_eqn, EXTENSION] +QED + +(* Theorem: m multiples_upto 0 = {} *) +(* Proof: + m multiples_upto 0 + = {x | m divides x /\ 0 < x /\ x <= 0} by multiples_upto_def + = {x | m divides x /\ F} by arithmetic + = {} by contradiction +*) +Theorem multiples_upto_m_0: + !m. m multiples_upto 0 = {} +Proof + simp[multiples_upto_def, EXTENSION] +QED + +(* Theorem: m multiples_upto 1 = if m = 1 then {1} else {} *) +(* Proof: + m multiples_upto 1 + = {x | m divides x /\ 0 < x /\ x <= 1} by multiples_upto_def + = {x | m divides x /\ x = 1} by arithmetic + = {1} if m = 1, {} otherwise by DIVIDES_ONE +*) +Theorem multiples_upto_m_1: + !m. m multiples_upto 1 = if m = 1 then {1} else {} +Proof + rw[multiples_upto_def, EXTENSION] >> + spose_not_then strip_assume_tac >> + `x = 1` by decide_tac >> + fs[] +QED + +(* Idea: an expression for (m multiples_upto n), for direct evaluation. *) + +(* Theorem: m multiples_upto n = + if m = 0 then {} + else IMAGE ($* m) (natural (n DIV m)) *) +(* Proof: + If m = 0, + Then 0 multiples_upto n = {} by multiples_upto_0_n + If m <> 0. + By multiples_upto_alt, EXTENSION, this is to show: + (1) 0 < k * m /\ k * m <= n ==> + ?y. k * m = m * y /\ ?x. y = SUC x /\ x < n DIV m + Note k <> 0 by MULT_EQ_0 + and k <= n DIV m by X_LE_DIV, 0 < m + so k - 1 < n DIV m by arithmetic + Let y = k, x = k - 1. + Note SUC x = SUC (k - 1) = k = y. + (2) x < n DIV m ==> ?k. m * SUC x = k * m /\ 0 < m * SUC x /\ m * SUC x <= n + Note SUC x <= n DIV m by arithmetic + so m * SUC x <= n by X_LE_DIV, 0 < m + and 0 < m * SUC x by MULT_EQ_0 + Take k = SUC x, true by MULT_COMM +*) +Theorem multiples_upto_thm[compute]: + !m n. m multiples_upto n = + if m = 0 then {} + else IMAGE ($* m) (natural (n DIV m)) +Proof + rpt strip_tac >> + Cases_on `m = 0` >- + fs[multiples_upto_0_n] >> + fs[multiples_upto_alt, EXTENSION] >> + rw[EQ_IMP_THM] >| [ + qexists_tac `k` >> + simp[] >> + `0 < k /\ 0 < m` by metis_tac[MULT_EQ_0, NOT_ZERO] >> + `k <= n DIV m` by rw[X_LE_DIV] >> + `k - 1 < n DIV m` by decide_tac >> + qexists_tac `k - 1` >> + simp[], + `SUC x'' <= n DIV m` by decide_tac >> + `m * SUC x'' <= n` by rfs[X_LE_DIV] >> + simp[] >> + metis_tac[MULT_COMM] + ] +QED + +(* +EVAL ``3 multiples_upto 9``; = {9; 6; 3} +EVAL ``3 multiples_upto 11``; = {9; 6; 3} +EVAL ``3 multiples_upto 12``; = {12; 9; 6; 3} +EVAL ``3 multiples_upto 13``; = {12; 9; 6; 3} +*) + +(* Theorem: m multiples_upto n SUBSET natural n *) +(* Proof: by multiples_upto_eqn, SUBSET_DEF. *) +Theorem multiples_upto_subset: + !m n. m multiples_upto n SUBSET natural n +Proof + simp[multiples_upto_eqn, SUBSET_DEF] +QED + +(* Theorem: FINITE (m multiples_upto n) *) +(* Proof: + Let s = m multiples_upto n + Note s SUBSET natural n by multiples_upto_subset + and FINITE natural n by natural_finite + so FINITE s by SUBSET_FINITE +*) +Theorem multiples_upto_finite: + !m n. FINITE (m multiples_upto n) +Proof + metis_tac[multiples_upto_subset, natural_finite, SUBSET_FINITE] +QED + +(* Theorem: CARD (m multiples_upto n) = if m = 0 then 0 else n DIV m *) +(* Proof: + If m = 0, + CARD (0 multiples_upto n) + = CARD {} by multiples_upto_0_n + = 0 by CARD_EMPTY + If m <> 0, + Claim: INJ ($* m) (natural (n DIV m)) univ(:num) + Proof: By INJ_DEF, this is to show: + !x. x IN (natural (n DIV m)) /\ + m * x = m * y ==> x = y, true by EQ_MULT_LCANCEL, m <> 0 + Note FINITE (natural (n DIV m)) by natural_finite + CARD (m multiples_upto n) + = CARD (IMAGE ($* m) (natural (n DIV m))) by multiples_upto_thm, m <> 0 + = CARD (natural (n DIV m)) by INJ_CARD_IMAGE + = n DIV m by natural_card +*) +Theorem multiples_upto_card: + !m n. CARD (m multiples_upto n) = if m = 0 then 0 else n DIV m +Proof + rpt strip_tac >> + Cases_on `m = 0` >- + simp[multiples_upto_0_n] >> + simp[multiples_upto_thm] >> + `INJ ($* m) (natural (n DIV m)) univ(:num)` by rw[INJ_DEF] >> + metis_tac[INJ_CARD_IMAGE, natural_finite, natural_card] +QED + +(* Idea: an expression for the set of coprimes of a prime power. *) + +(* Theorem: prime p ==> + coprimes (p ** n) = natural (p ** n) DIFF p multiples_upto (p ** n) *) +(* Proof: + If n = 0, + LHS = coprimes (p ** 0) + = coprimes 1 by EXP_0 + = {1} by coprimes_1 + RHS = natural (p ** 0) DIFF p multiples_upto (p ** 0) + = natural 1 DIFF p multiples_upto 1 + = natural 1 DIFF {} by multiples_upto_m_1, NOT_PRIME_1 + = {1} DIFF {} by natural_1 + = {1} = LHS by DIFF_EMPTY + If n <> 0, + By coprimes_def, multiples_upto_def, EXTENSION, this is to show: + coprime (SUC x) (p ** n) <=> ~(p divides SUC x) + This is true by coprime_prime_power +*) +Theorem coprimes_prime_power: + !p n. prime p ==> + coprimes (p ** n) = natural (p ** n) DIFF p multiples_upto (p ** n) +Proof + rpt strip_tac >> + Cases_on `n = 0` >| [ + `p <> 1` by metis_tac[NOT_PRIME_1] >> + simp[coprimes_1, multiples_upto_m_1, natural_1, EXP_0], + rw[coprimes_def, multiples_upto_def, EXTENSION] >> + (rw[EQ_IMP_THM] >> rfs[coprime_prime_power]) + ] +QED + +(* Idea: an expression for phi of a prime power. *) + +(* Theorem: prime p ==> phi (p ** SUC n) = (p - 1) * p ** n *) +(* Proof: + Let m = SUC n, + u = natural (p ** m), + v = p multiples_upto (p ** m). + Note 0 < p by PRIME_POS + and FINITE u by natural_finite + and v SUBSET u by multiples_upto_subset + + phi (p ** m) + = CARD (coprimes (p ** m)) by phi_def + = CARD (u DIFF v) by coprimes_prime_power + = CARD u - CARD v by SUBSET_DIFF_CARD + = p ** m - CARD v by natural_card + = p ** m - (p ** m DIV p) by multiples_upto_card, p <> 0 + = p ** m - p ** n by EXP_SUC_DIV, 0 < p + = p * p ** n - p ** n by EXP + = (p - 1) * p ** n by RIGHT_SUB_DISTRIB +*) +Theorem phi_prime_power: + !p n. prime p ==> phi (p ** SUC n) = (p - 1) * p ** n +Proof + rpt strip_tac >> + qabbrev_tac `m = SUC n` >> + qabbrev_tac `u = natural (p ** m)` >> + qabbrev_tac `v = p multiples_upto (p ** m)` >> + `0 < p` by rw[PRIME_POS] >> + `FINITE u` by rw[natural_finite, Abbr`u`] >> + `v SUBSET u` by rw[multiples_upto_subset, Abbr`v`, Abbr`u`] >> + `phi (p ** m) = CARD (coprimes (p ** m))` by rw[phi_def] >> + `_ = CARD (u DIFF v)` by rw[coprimes_prime_power, Abbr`u`, Abbr`v`] >> + `_ = CARD u - CARD v` by rw[SUBSET_DIFF_CARD] >> + `_ = p ** m - (p ** m DIV p)` by rw[natural_card, multiples_upto_card, Abbr`u`, Abbr`v`] >> + `_ = p ** m - p ** n` by rw[EXP_SUC_DIV, Abbr`m`] >> + `_ = p * p ** n - p ** n` by rw[GSYM EXP] >> + `_ = (p - 1) * p ** n` by decide_tac >> + simp[] +QED + +(* Yes, a spectacular theorem! *) + +(* Idea: specialise phi_prime_power for prime squared. *) + +(* Theorem: prime p ==> phi (p * p) = p * (p - 1) *) +(* Proof: + phi (p * p) + = phi (p ** 2) by EXP_2 + = phi (p ** SUC 1) by TWO + = (p - 1) * p ** 1 by phi_prime_power + = p * (p - 1) by EXP_1 +*) +Theorem phi_prime_sq: + !p. prime p ==> phi (p * p) = p * (p - 1) +Proof + rpt strip_tac >> + `phi (p * p) = phi (p ** SUC 1)` by rw[] >> + simp[phi_prime_power] +QED + +(* Idea: Euler phi function for a product of primes. *) + +(* Theorem: prime p /\ prime q ==> + phi (p * q) = if p = q then p * (p - 1) else (p - 1) * (q - 1) *) +(* Proof: + If p = q, phi (p * p) = p * (p - 1) by phi_prime_sq + If p <> q, phi (p * q) = (p - 1) * (q - 1) by phi_primes_distinct +*) +Theorem phi_primes: + !p q. prime p /\ prime q ==> + phi (p * q) = if p = q then p * (p - 1) else (p - 1) * (q - 1) +Proof + metis_tac[phi_prime_sq, phi_primes_distinct] +QED + +(* Finally, another nice result. *) + +(* ------------------------------------------------------------------------- *) +(* Recursive definition of phi *) +(* ------------------------------------------------------------------------- *) + +(* Define phi by recursion *) +Definition rec_phi_def: + rec_phi n = if n = 0 then 0 + else if n = 1 then 1 + else n - SIGMA rec_phi { m | m < n /\ m divides n} +Termination + WF_REL_TAC `$< : num -> num -> bool` >> srw_tac[][] +End +(* This is the recursive form of Gauss' Little Theorem: n = SUM phi m, m divides n *) + +(* Just using Define without any condition will trigger: + +Initial goal: + +?R. WF R /\ !n a. n <> 0 /\ n <> 1 /\ a IN {m | m < n /\ m divides n} ==> R a n + +Unable to prove termination! + +Try using "TotalDefn.tDefine ". + +The termination goal has been set up using Defn.tgoal . + +So one can set up: +g `?R. WF R /\ !n a. n <> 0 /\ n <> 1 /\ a IN {m | m < n /\ m divides n} ==> R a n`; +e (WF_REL_TAC `$< : num -> num -> bool`); +e (srw[][]); + +which gives the tDefine solution. +*) + +(* Theorem: rec_phi 0 = 0 *) +(* Proof: by rec_phi_def *) +val rec_phi_0 = store_thm( + "rec_phi_0", + ``rec_phi 0 = 0``, + rw[rec_phi_def]); + +(* Theorem: rec_phi 1 = 1 *) +(* Proof: by rec_phi_def *) +val rec_phi_1 = store_thm( + "rec_phi_1", + ``rec_phi 1 = 1``, + rw[Once rec_phi_def]); + +(* Theorem: rec_phi n = phi n *) +(* Proof: + By complete induction on n. + If n = 0, + rec_phi 0 = 0 by rec_phi_0 + = phi 0 by phi_0 + If n = 1, + rec_phi 1 = 1 by rec_phi_1 + = phi 1 by phi_1 + Othewise, 0 < n, 1 < n. + Let s = {m | m < n /\ m divides n}. + Note s SUBSET (count n) by SUBSET_DEF + thus FINITE s by SUBSET_FINITE, FINITE_COUNT + Hence !m. m IN s + ==> (rec_phi m = phi m) by induction hypothesis + Also n NOTIN s by EXTENSION + and n INSERT s + = {m | m <= n /\ m divides n} + = {m | 0 < m /\ m <= n /\ m divides n} by divisor_pos, 0 < n + = divisors n by divisors_def, EXTENSION, LESS_OR_EQ + + rec_phi n + = n - (SIGMA rec_phi s) by rec_phi_def + = n - (SIGMA phi s) by SUM_IMAGE_CONG, rec_phi m = phi m + = (SIGMA phi (divisors n)) - (SIGMA phi s) by Gauss' Little Theorem + = (SIGMA phi (n INSERT s)) - (SIGMA phi s) by divisors n = n INSERT s + = (phi n + SIGMA phi (s DELETE n)) - (SIGMA phi s) by SUM_IMAGE_THM + = (phi n + SIGMA phi s) - (SIGMA phi s) by DELETE_NON_ELEMENT + = phi n by ADD_SUB +*) +Theorem rec_phi_eq_phi: + !n. rec_phi n = phi n +Proof + completeInduct_on `n` >> + Cases_on `n = 0` >- + rw[rec_phi_0, phi_0] >> + Cases_on `n = 1` >- + rw[rec_phi_1, phi_1] >> + `0 < n /\ 1 < n` by decide_tac >> + qabbrev_tac `s = {m | m < n /\ m divides n}` >> + qabbrev_tac `t = SIGMA rec_phi s` >> + `!m. m IN s <=> m < n /\ m divides n` by rw[Abbr`s`] >> + `!m. m IN s ==> (rec_phi m = phi m)` by rw[] >> + `t = SIGMA phi s` by rw[SUM_IMAGE_CONG, Abbr`t`] >> + `s SUBSET (count n)` by rw[SUBSET_DEF] >> + `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> + `n NOTIN s` by rw[] >> + (`n INSERT s = divisors n` by (rw[divisors_def, EXTENSION] >> metis_tac[divisor_pos, LESS_OR_EQ, DIVIDES_REFL])) >> + `n = SIGMA phi (divisors n)` by rw[Gauss_little_thm] >> + `_ = phi n + SIGMA phi (s DELETE n)` by rw[GSYM SUM_IMAGE_THM] >> + `_ = phi n + t` by metis_tac[DELETE_NON_ELEMENT] >> + `rec_phi n = n - t` by metis_tac[rec_phi_def] >> + decide_tac +QED + + +(* ------------------------------------------------------------------------- *) +(* Useful Theorems (not used). *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: INJ (coprimes) (univ(:num) DIFF {1}) univ(:num -> bool) *) +(* Proof: + By INJ_DEF, this is to show: + x <> 1 /\ y <> 1 /\ coprimes x = coprimes y ==> x = y + If x = 0, then y = 0 by coprimes_eq_empty + If y = 0, then x = 0 by coprimes_eq_empty + If x <> 0 and y <> 0, + with x <> 1 and y <> 1 by given + then 1 < x and 1 < y. + Since MAX_SET (coprimes x) = x - 1 by coprimes_max, 1 < x + and MAX_SET (coprimes y) = y - 1 by coprimes_max, 1 < y + If coprimes x = coprimes y, + x - 1 = y - 1 by above + Hence x = y by CANCEL_SUB +*) +val coprimes_from_not_1_inj = store_thm( + "coprimes_from_not_1_inj", + ``INJ (coprimes) (univ(:num) DIFF {1}) univ(:num -> bool)``, + rw[INJ_DEF] >> + Cases_on `x = 0` >- + metis_tac[coprimes_eq_empty] >> + Cases_on `y = 0` >- + metis_tac[coprimes_eq_empty] >> + `1 < x /\ 1 < y` by decide_tac >> + `x - 1 = y - 1` by metis_tac[coprimes_max] >> + decide_tac); +(* Not very useful. *) + +(* Here is group of related theorems for (divisors n): + divisors_eq_image_gcd_upto + divisors_eq_image_gcd_count + divisors_eq_image_gcd_natural + + This first one is proved independently, then the second and third are derived. + Of course, the best is the third one, which is now divisors_eq_gcd_image (above) + Here, I rework all proofs of these three from divisors_eq_gcd_image, + so divisors_eq_image_gcd_natural = divisors_eq_gcd_image. +*) + +(* Theorem: 0 < n ==> divisors n = IMAGE (gcd n) (upto n) *) +(* Proof: + Note gcd n 0 = n by GCD_0 + and n IN divisors n by divisors_has_last, 0 < n + divisors n + = (gcd n 0) INSERT (divisors n) by ABSORPTION + = (gcd n 0) INSERT (IMAGE (gcd n) (natural n)) by divisors_eq_gcd_image + = IMAGE (gcd n) (0 INSERT (natural n)) by IMAGE_INSERT + = IMAGE (gcd n) (upto n) by upto_by_natural +*) +Theorem divisors_eq_image_gcd_upto: + !n. 0 < n ==> divisors n = IMAGE (gcd n) (upto n) +Proof + rpt strip_tac >> + `IMAGE (gcd n) (upto n) = IMAGE (gcd n) (0 INSERT natural n)` by simp[upto_by_natural] >> + `_ = (gcd n 0) INSERT (IMAGE (gcd n) (natural n))` by fs[] >> + `_ = n INSERT (divisors n)` by fs[divisors_eq_gcd_image] >> + metis_tac[divisors_has_last, ABSORPTION] +QED + +(* Theorem: (feq (gcd n)) equiv_on (upto n) *) +(* Proof: + By feq_equiv |- !s f. feq f equiv_on s + Taking s = upto n, f = gcd n. +*) +val gcd_eq_equiv_on_upto = store_thm( + "gcd_eq_equiv_on_upto", + ``!n. (feq (gcd n)) equiv_on (upto n)``, + rw[feq_equiv]); + +(* Theorem: 0 < n ==> partition (feq (gcd n)) (upto n) = IMAGE (preimage (gcd n) (upto n)) (divisors n) *) +(* Proof: + Let f = gcd n, s = upto n. + partition (feq f) s + = IMAGE (preimage f s o f) s by feq_partition + = IMAGE (preimage f s) (IMAGE f s) by IMAGE_COMPOSE + = IMAGE (preimage f s) (IMAGE (gcd n) (upto n)) by expansion + = IMAGE (preimage f s) (divisors n) by divisors_eq_image_gcd_upto, 0 < n +*) +val gcd_eq_upto_partition_by_divisors = store_thm( + "gcd_eq_upto_partition_by_divisors", + ``!n. 0 < n ==> partition (feq (gcd n)) (upto n) = IMAGE (preimage (gcd n) (upto n)) (divisors n)``, + rpt strip_tac >> + qabbrev_tac `f = gcd n` >> + qabbrev_tac `s = upto n` >> + `partition (feq f) s = IMAGE (preimage f s o f) s` by rw[feq_partition] >> + `_ = IMAGE (preimage f s) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> + rw[divisors_eq_image_gcd_upto, Abbr`f`, Abbr`s`]); + +(* Theorem: SIGMA f (upto n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n)) *) +(* Proof: + Let g = gcd n, s = upto n. + Since FINITE s by upto_finite + and (feq g) equiv_on s by feq_equiv + The result follows by set_sigma_by_partition +*) +val sum_over_upto_by_gcd_partition = store_thm( + "sum_over_upto_by_gcd_partition", + ``!f n. SIGMA f (upto n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n))``, + rw[feq_equiv, set_sigma_by_partition]); + +(* Theorem: 0 < n ==> SIGMA f (upto n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n)) *) +(* Proof: + SIGMA f (upto n) + = SIGMA (SIGMA f) (partition (feq (gcd n)) (upto n)) by sum_over_upto_by_gcd_partition + = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n)) by gcd_eq_upto_partition_by_divisors, 0 < n +*) +val sum_over_upto_by_divisors = store_thm( + "sum_over_upto_by_divisors", + ``!f n. 0 < n ==> SIGMA f (upto n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (upto n)) (divisors n))``, + rw[sum_over_upto_by_gcd_partition, gcd_eq_upto_partition_by_divisors]); + +(* Similar results based on count *) + +(* Theorem: divisors n = IMAGE (gcd n) (count n) *) +(* Proof: + If n = 0, + LHS = divisors 0 = {} by divisors_0 + RHS = IMAGE (gcd 0) (count 0) + = IMAGE (gcd 0) {} by COUNT_0 + = {} = LHS by IMAGE_EMPTY + If n <> 0, 0 < n. + divisors n + = IMAGE (gcd n) (upto n) by divisors_eq_image_gcd_upto, 0 < n + = IMAGE (gcd n) (n INSERT (count n)) by upto_by_count + = (gcd n n) INSERT (IMAGE (gcd n) (count n)) by IMAGE_INSERT + = n INSERT (IMAGE (gcd n) (count n)) by GCD_REF + = (gcd n 0) INSERT (IMAGE (gcd n) (count n)) by GCD_0R + = IMAGE (gcd n) (0 INSERT (count n)) by IMAGE_INSERT + = IMAGE (gcd n) (count n) by IN_COUNT, ABSORPTION, 0 < n. +*) +Theorem divisors_eq_image_gcd_count: + !n. divisors n = IMAGE (gcd n) (count n) +Proof + rpt strip_tac >> + Cases_on `n = 0` >- + simp[divisors_0] >> + `0 < n` by decide_tac >> + `divisors n = IMAGE (gcd n) (upto n)` by rw[divisors_eq_image_gcd_upto] >> + `_ = IMAGE (gcd n) (n INSERT (count n))` by rw[upto_by_count] >> + `_ = n INSERT (IMAGE (gcd n) (count n))` by rw[GCD_REF] >> + `_ = (gcd n 0) INSERT (IMAGE (gcd n) (count n))` by rw[GCD_0R] >> + `_ = IMAGE (gcd n) (0 INSERT (count n))` by rw[] >> + metis_tac[IN_COUNT, ABSORPTION] +QED + +(* Theorem: (feq (gcd n)) equiv_on (count n) *) +(* Proof: + By feq_equiv |- !s f. feq f equiv_on s + Taking s = upto n, f = count n. +*) +val gcd_eq_equiv_on_count = store_thm( + "gcd_eq_equiv_on_count", + ``!n. (feq (gcd n)) equiv_on (count n)``, + rw[feq_equiv]); + +(* Theorem: partition (feq (gcd n)) (count n) = IMAGE (preimage (gcd n) (count n)) (divisors n) *) +(* Proof: + Let f = gcd n, s = count n. + partition (feq f) s + = IMAGE (preimage f s o f) s by feq_partition + = IMAGE (preimage f s) (IMAGE f s) by IMAGE_COMPOSE + = IMAGE (preimage f s) (IMAGE (gcd n) (count n)) by expansion + = IMAGE (preimage f s) (divisors n) by divisors_eq_image_gcd_count +*) +Theorem gcd_eq_count_partition_by_divisors: + !n. partition (feq (gcd n)) (count n) = IMAGE (preimage (gcd n) (count n)) (divisors n) +Proof + rpt strip_tac >> + qabbrev_tac `f = gcd n` >> + qabbrev_tac `s = count n` >> + `partition (feq f) s = IMAGE (preimage f s o f) s` by rw[feq_partition] >> + `_ = IMAGE (preimage f s) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> + rw[divisors_eq_image_gcd_count, Abbr`f`, Abbr`s`] +QED + +(* Theorem: SIGMA f (count n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (count n)) *) +(* Proof: + Let g = gcd n, s = count n. + Since FINITE s by FINITE_COUNT + and (feq g) equiv_on s by feq_equiv + The result follows by set_sigma_by_partition +*) +val sum_over_count_by_gcd_partition = store_thm( + "sum_over_count_by_gcd_partition", + ``!f n. SIGMA f (count n) = SIGMA (SIGMA f) (partition (feq (gcd n)) (count n))``, + rw[feq_equiv, set_sigma_by_partition]); + +(* Theorem: SIGMA f (count n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) *) +(* Proof: + SIGMA f (count n) + = SIGMA (SIGMA f) (partition (feq (gcd n)) (count n)) by sum_over_count_by_gcd_partition + = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) by gcd_eq_count_partition_by_divisors +*) +Theorem sum_over_count_by_divisors: + !f n. SIGMA f (count n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (count n)) (divisors n)) +Proof + rw[sum_over_count_by_gcd_partition, gcd_eq_count_partition_by_divisors] +QED + +(* Similar results based on natural *) + +(* Theorem: divisors n = IMAGE (gcd n) (natural n) *) +(* Proof: + If n = 0, + LHS = divisors 0 = {} by divisors_0 + RHS = IMAGE (gcd 0) (natural 0) + = IMAGE (gcd 0) {} by natural_0 + = {} = LHS by IMAGE_EMPTY + If n <> 0, 0 < n. + divisors n + = IMAGE (gcd n) (upto n) by divisors_eq_image_gcd_upto, 0 < n + = IMAGE (gcd n) (0 INSERT natural n) by upto_by_natural + = (gcd 0 n) INSERT (IMAGE (gcd n) (natural n)) by IMAGE_INSERT + = n INSERT (IMAGE (gcd n) (natural n)) by GCD_0L + = (gcd n n) INSERT (IMAGE (gcd n) (natural n)) by GCD_REF + = IMAGE (gcd n) (n INSERT (natural n)) by IMAGE_INSERT + = IMAGE (gcd n) (natural n) by natural_has_last, ABSORPTION, 0 < n. +*) +Theorem divisors_eq_image_gcd_natural: + !n. divisors n = IMAGE (gcd n) (natural n) +Proof + rpt strip_tac >> + Cases_on `n = 0` >- + simp[divisors_0, natural_0] >> + `0 < n` by decide_tac >> + `divisors n = IMAGE (gcd n) (upto n)` by rw[divisors_eq_image_gcd_upto] >> + `_ = IMAGE (gcd n) (0 INSERT (natural n))` by rw[upto_by_natural] >> + `_ = n INSERT (IMAGE (gcd n) (natural n))` by rw[GCD_0L] >> + `_ = (gcd n n) INSERT (IMAGE (gcd n) (natural n))` by rw[GCD_REF] >> + `_ = IMAGE (gcd n) (n INSERT (natural n))` by rw[] >> + metis_tac[natural_has_last, ABSORPTION] +QED +(* This is the same as divisors_eq_gcd_image *) + +(* Theorem: partition (feq (gcd n)) (natural n) = IMAGE (preimage (gcd n) (natural n)) (divisors n) *) +(* Proof: + Let f = gcd n, s = natural n. + partition (feq f) s + = IMAGE (preimage f s o f) s by feq_partition + = IMAGE (preimage f s) (IMAGE f s) by IMAGE_COMPOSE + = IMAGE (preimage f s) (IMAGE (gcd n) (natural n)) by expansion + = IMAGE (preimage f s) (divisors n) by divisors_eq_image_gcd_natural +*) +Theorem gcd_eq_natural_partition_by_divisors: + !n. partition (feq (gcd n)) (natural n) = IMAGE (preimage (gcd n) (natural n)) (divisors n) +Proof + rpt strip_tac >> + qabbrev_tac `f = gcd n` >> + qabbrev_tac `s = natural n` >> + `partition (feq f) s = IMAGE (preimage f s o f) s` by rw[feq_partition] >> + `_ = IMAGE (preimage f s) (IMAGE f s)` by rw[IMAGE_COMPOSE] >> + rw[divisors_eq_image_gcd_natural, Abbr`f`, Abbr`s`] +QED + +(* Theorem: SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) *) +(* Proof: + SIGMA f (natural n) + = SIGMA (SIGMA f) (partition (feq (gcd n)) (natural n)) by sum_over_natural_by_gcd_partition + = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) by gcd_eq_natural_partition_by_divisors +*) +Theorem sum_over_natural_by_preimage_divisors: + !f n. SIGMA f (natural n) = SIGMA (SIGMA f) (IMAGE (preimage (gcd n) (natural n)) (divisors n)) +Proof + rw[sum_over_natural_by_gcd_partition, gcd_eq_natural_partition_by_divisors] +QED + +(* Theorem: (f 0 = g 0) /\ (!n. SIGMA f (divisors n) = SIGMA g (divisors n)) ==> (f = g) *) +(* Proof: + By FUN_EQ_THM, this is to show: !x. f x = g x. + By complete induction on x. + Let s = divisors x, t = s DELETE x. + If x = 0, f 0 = g 0 is true by given + Otherwise x <> 0. + Then x IN s by divisors_has_last, 0 < x + and s = x INSERT t /\ x NOTIN t by INSERT_DELETE, IN_DELETE + Note FINITE s by divisors_finite + so FINITE t by FINITE_DELETE + + Claim: SIGMA f t = SIGMA g t + Proof: By SUM_IMAGE_CONG, this is to show: + !z. z IN t ==> (f z = g z) + But z IN s <=> 0 < z /\ z <= x /\ z divides x by divisors_element + so z IN t <=> 0 < z /\ z < x /\ z divides x by IN_DELETE + ==> f z = g z by induction hypothesis, [1] + + Now SIGMA f s = SIGMA g s by implication + or f x + SIGMA f t = g x + SIGMA g t by SUM_IMAGE_INSERT + or f x = g x by [1], SIGMA f t = SIGMA g t +*) +Theorem sum_image_divisors_cong: + !f g. (f 0 = g 0) /\ (!n. SIGMA f (divisors n) = SIGMA g (divisors n)) ==> (f = g) +Proof + rw[FUN_EQ_THM] >> + completeInduct_on `x` >> + qabbrev_tac `s = divisors x` >> + qabbrev_tac `t = s DELETE x` >> + (Cases_on `x = 0` >> simp[]) >> + `x IN s` by rw[divisors_has_last, Abbr`s`] >> + `s = x INSERT t /\ x NOTIN t` by rw[Abbr`t`] >> + `SIGMA f t = SIGMA g t` by + ((irule SUM_IMAGE_CONG >> simp[]) >> + rw[divisors_element, Abbr`t`, Abbr`s`]) >> + `FINITE t` by rw[divisors_finite, Abbr`t`, Abbr`s`] >> + `SIGMA f s = f x + SIGMA f t` by rw[SUM_IMAGE_INSERT] >> + `SIGMA g s = g x + SIGMA g t` by rw[SUM_IMAGE_INSERT] >> + `SIGMA f s = SIGMA g s` by metis_tac[] >> + decide_tac +QED +(* But this is not very useful! *) + +(* ------------------------------------------------------------------------- *) +(* Mobius Function and Inversion Documentation *) +(* ------------------------------------------------------------------------- *) +(* Overloading: + sq_free s = {n | n IN s /\ square_free n} + non_sq_free s = {n | n IN s /\ ~(square_free n)} + even_sq_free s = {n | n IN (sq_free s) /\ EVEN (CARD (prime_factors n))} + odd_sq_free s = {n | n IN (sq_free s) /\ ODD (CARD (prime_factors n))} + less_divisors n = {x | x IN (divisors n) /\ x <> n} + proper_divisors n = {x | x IN (divisors n) /\ x <> 1 /\ x <> n} +*) +(* Definitions and Theorems (# are exported): + + Helper Theorems: + + Square-free Number and Square-free Sets: + square_free_def |- !n. square_free n <=> !p. prime p /\ p divides n ==> ~(p * p divides n) + square_free_1 |- square_free 1 + square_free_prime |- !n. prime n ==> square_free n + + sq_free_element |- !s n. n IN sq_free s <=> n IN s /\ square_free n + sq_free_subset |- !s. sq_free s SUBSET s + sq_free_finite |- !s. FINITE s ==> FINITE (sq_free s) + non_sq_free_element |- !s n. n IN non_sq_free s <=> n IN s /\ ~square_free n + non_sq_free_subset |- !s. non_sq_free s SUBSET s + non_sq_free_finite |- !s. FINITE s ==> FINITE (non_sq_free s) + sq_free_split |- !s. (s = sq_free s UNION non_sq_free s) /\ + (sq_free s INTER non_sq_free s = {}) + sq_free_union |- !s. s = sq_free s UNION non_sq_free s + sq_free_inter |- !s. sq_free s INTER non_sq_free s = {} + sq_free_disjoint |- !s. DISJOINT (sq_free s) (non_sq_free s) + + Prime Divisors of a Number and Partitions of Square-free Set: + prime_factors_def |- !n. prime_factors n = {p | prime p /\ p IN divisors n} + prime_factors_element |- !n p. p IN prime_factors n <=> prime p /\ p <= n /\ p divides n + prime_factors_subset |- !n. prime_factors n SUBSET divisors n + prime_factors_finite |- !n. FINITE (prime_factors n) + + even_sq_free_element |- !s n. n IN even_sq_free s <=> n IN s /\ square_free n /\ EVEN (CARD (prime_factors n)) + even_sq_free_subset |- !s. even_sq_free s SUBSET s + even_sq_free_finite |- !s. FINITE s ==> FINITE (even_sq_free s) + odd_sq_free_element |- !s n. n IN odd_sq_free s <=> n IN s /\ square_free n /\ ODD (CARD (prime_factors n)) + odd_sq_free_subset |- !s. odd_sq_free s SUBSET s + odd_sq_free_finite |- !s. FINITE s ==> FINITE (odd_sq_free s) + sq_free_split_even_odd |- !s. (sq_free s = even_sq_free s UNION odd_sq_free s) /\ + (even_sq_free s INTER odd_sq_free s = {}) + sq_free_union_even_odd |- !s. sq_free s = even_sq_free s UNION odd_sq_free s + sq_free_inter_even_odd |- !s. even_sq_free s INTER odd_sq_free s = {} + sq_free_disjoint_even_odd |- !s. DISJOINT (even_sq_free s) (odd_sq_free s) + + Less Divisors of a number: + less_divisors_element |- !n x. x IN less_divisors n <=> 0 < x /\ x < n /\ x divides n + less_divisors_0 |- less_divisors 0 = {} + less_divisors_1 |- less_divisors 1 = {} + less_divisors_subset_divisors + |- !n. less_divisors n SUBSET divisors n + less_divisors_finite |- !n. FINITE (less_divisors n) + less_divisors_prime |- !n. prime n ==> (less_divisors n = {1}) + less_divisors_has_1 |- !n. 1 < n ==> 1 IN less_divisors n + less_divisors_nonzero |- !n x. x IN less_divisors n ==> 0 < x + less_divisors_has_cofactor |- !n d. 1 < d /\ d IN less_divisors n ==> n DIV d IN less_divisors n + + Proper Divisors of a number: + proper_divisors_element |- !n x. x IN proper_divisors n <=> 1 < x /\ x < n /\ x divides n + proper_divisors_0 |- proper_divisors 0 = {} + proper_divisors_1 |- proper_divisors 1 = {} + proper_divisors_subset |- !n. proper_divisors n SUBSET less_divisors n + proper_divisors_finite |- !n. FINITE (proper_divisors n) + proper_divisors_not_1 |- !n. 1 NOTIN proper_divisors n + proper_divisors_by_less_divisors + |- !n. proper_divisors n = less_divisors n DELETE 1 + proper_divisors_prime |- !n. prime n ==> (proper_divisors n = {}) + proper_divisors_has_cofactor|- !n d. d IN proper_divisors n ==> n DIV d IN proper_divisors n + proper_divisors_min_gt_1 |- !n. proper_divisors n <> {} ==> 1 < MIN_SET (proper_divisors n) + proper_divisors_max_min |- !n. proper_divisors n <> {} ==> + (MAX_SET (proper_divisors n) = n DIV MIN_SET (proper_divisors n)) /\ + (MIN_SET (proper_divisors n) = n DIV MAX_SET (proper_divisors n)) + + Useful Properties of Less Divisors: + less_divisors_min |- !n. 1 < n ==> (MIN_SET (less_divisors n) = 1) + less_divisors_max |- !n. MAX_SET (less_divisors n) <= n DIV 2 + less_divisors_subset_natural |- !n. less_divisors n SUBSET natural (n DIV 2) + + Properties of Summation equals Perfect Power: + perfect_power_special_inequality |- !p. 1 < p ==> !n. p * (p ** n - 1) < (p - 1) * (2 * p ** n) + perfect_power_half_inequality_1 |- !p n. 1 < p /\ 0 < n ==> 2 * p ** (n DIV 2) <= p ** n + perfect_power_half_inequality_2 |- !p n. 1 < p /\ 0 < n ==> + (p ** (n DIV 2) - 2) * p ** (n DIV 2) <= p ** n - 2 * p ** (n DIV 2) + sigma_eq_perfect_power_bounds_1 |- !p. 1 < p ==> + !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> + (!n. 0 < n ==> n * f n <= p ** n) /\ + !n. 0 < n ==> p ** n - 2 * p ** (n DIV 2) < n * f n + sigma_eq_perfect_power_bounds_2 |- !p. 1 < p ==> + !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> + (!n. 0 < n ==> n * f n <= p ** n) /\ + !n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * f n + +*) + +(* ------------------------------------------------------------------------- *) +(* Helper Theorems *) +(* ------------------------------------------------------------------------- *) + +(* ------------------------------------------------------------------------- *) +(* Mobius Function and Inversion *) +(* ------------------------------------------------------------------------- *) + + +(* ------------------------------------------------------------------------- *) +(* Square-free Number and Square-free Sets *) +(* ------------------------------------------------------------------------- *) + +(* Define square-free number *) +val square_free_def = Define` + square_free n = !p. prime p /\ p divides n ==> ~(p * p divides n) +`; + +(* Theorem: square_free 1 *) +(* Proof: + square_free 1 + <=> !p. prime p /\ p divides 1 ==> ~(p * p divides 1) by square_free_def + <=> prime 1 ==> ~(1 * 1 divides 1) by DIVIDES_ONE + <=> F ==> ~(1 * 1 divides 1) by NOT_PRIME_1 + <=> T by false assumption +*) +val square_free_1 = store_thm( + "square_free_1", + ``square_free 1``, + rw[square_free_def]); + +(* Theorem: prime n ==> square_free n *) +(* Proof: + square_free n + <=> !p. prime p /\ p divides n ==> ~(p * p divides n) by square_free_def + By contradiction, suppose (p * p divides n). + Since p divides n ==> (p = n) \/ (p = 1) by prime_def + and p * p divides ==> (p * p = n) \/ (p * p = 1) by prime_def + but p <> 1 by prime_def + so p * p <> 1 by MULT_EQ_1 + Thus p * p = n = p, + or p = 0 \/ p = 1 by SQ_EQ_SELF + But p <> 0 by NOT_PRIME_0 + and p <> 1 by NOT_PRIME_1 + Thus there is a contradiction. +*) +val square_free_prime = store_thm( + "square_free_prime", + ``!n. prime n ==> square_free n``, + rw_tac std_ss[square_free_def] >> + spose_not_then strip_assume_tac >> + `p * p = p` by metis_tac[prime_def, MULT_EQ_1] >> + metis_tac[SQ_EQ_SELF, NOT_PRIME_0, NOT_PRIME_1]); + +(* Overload square-free filter of a set *) +val _ = overload_on("sq_free", ``\s. {n | n IN s /\ square_free n}``); + +(* Overload non-square-free filter of a set *) +val _ = overload_on("non_sq_free", ``\s. {n | n IN s /\ ~(square_free n)}``); + +(* Theorem: n IN sq_free s <=> n IN s /\ square_free n *) +(* Proof: by notation. *) +val sq_free_element = store_thm( + "sq_free_element", + ``!s n. n IN sq_free s <=> n IN s /\ square_free n``, + rw[]); + +(* Theorem: sq_free s SUBSET s *) +(* Proof: by SUBSET_DEF *) +val sq_free_subset = store_thm( + "sq_free_subset", + ``!s. sq_free s SUBSET s``, + rw[SUBSET_DEF]); + +(* Theorem: FINITE s ==> FINITE (sq_free s) *) +(* Proof: by sq_free_subset, SUBSET_FINITE *) +val sq_free_finite = store_thm( + "sq_free_finite", + ``!s. FINITE s ==> FINITE (sq_free s)``, + metis_tac[sq_free_subset, SUBSET_FINITE]); + +(* Theorem: n IN non_sq_free s <=> n IN s /\ ~(square_free n) *) +(* Proof: by notation. *) +val non_sq_free_element = store_thm( + "non_sq_free_element", + ``!s n. n IN non_sq_free s <=> n IN s /\ ~(square_free n)``, + rw[]); + +(* Theorem: non_sq_free s SUBSET s *) +(* Proof: by SUBSET_DEF *) +val non_sq_free_subset = store_thm( + "non_sq_free_subset", + ``!s. non_sq_free s SUBSET s``, + rw[SUBSET_DEF]); + +(* Theorem: FINITE s ==> FINITE (non_sq_free s) *) +(* Proof: by non_sq_free_subset, SUBSET_FINITE *) +val non_sq_free_finite = store_thm( + "non_sq_free_finite", + ``!s. FINITE s ==> FINITE (non_sq_free s)``, + metis_tac[non_sq_free_subset, SUBSET_FINITE]); + +(* Theorem: (s = (sq_free s) UNION (non_sq_free s)) /\ ((sq_free s) INTER (non_sq_free s) = {}) *) +(* Proof: + This is to show: + (1) s = (sq_free s) UNION (non_sq_free s) + True by EXTENSION, IN_UNION. + (2) (sq_free s) INTER (non_sq_free s) = {} + True by EXTENSION, IN_INTER +*) +val sq_free_split = store_thm( + "sq_free_split", + ``!s. (s = (sq_free s) UNION (non_sq_free s)) /\ ((sq_free s) INTER (non_sq_free s) = {})``, + (rw[EXTENSION] >> metis_tac[])); + +(* Theorem: s = (sq_free s) UNION (non_sq_free s) *) +(* Proof: extract from sq_free_split. *) +val sq_free_union = save_thm("sq_free_union", sq_free_split |> SPEC_ALL |> CONJUNCT1 |> GEN_ALL); +(* val sq_free_union = |- !s. s = sq_free s UNION non_sq_free s: thm *) + +(* Theorem: (sq_free s) INTER (non_sq_free s) = {} *) +(* Proof: extract from sq_free_split. *) +val sq_free_inter = save_thm("sq_free_inter", sq_free_split |> SPEC_ALL |> CONJUNCT2 |> GEN_ALL); +(* val sq_free_inter = |- !s. sq_free s INTER non_sq_free s = {}: thm *) + +(* Theorem: DISJOINT (sq_free s) (non_sq_free s) *) +(* Proof: by DISJOINT_DEF, sq_free_inter. *) +val sq_free_disjoint = store_thm( + "sq_free_disjoint", + ``!s. DISJOINT (sq_free s) (non_sq_free s)``, + rw_tac std_ss[DISJOINT_DEF, sq_free_inter]); + +(* ------------------------------------------------------------------------- *) +(* Prime Divisors of a Number and Partitions of Square-free Set *) +(* ------------------------------------------------------------------------- *) + +(* Define the prime divisors of a number *) +val prime_factors_def = zDefine` + prime_factors n = {p | prime p /\ p IN (divisors n)} +`; +(* use zDefine as this cannot be computed. *) +(* prime_divisors is used in triangle.hol *) + +(* Theorem: p IN prime_factors n <=> prime p /\ p <= n /\ p divides n *) +(* Proof: + p IN prime_factors n + <=> prime p /\ p IN (divisors n) by prime_factors_def + <=> prime p /\ 0 < p /\ p <= n /\ p divides n by divisors_def + <=> prime p /\ p <= n /\ p divides n by PRIME_POS +*) +Theorem prime_factors_element: + !n p. p IN prime_factors n <=> prime p /\ p <= n /\ p divides n +Proof + rw[prime_factors_def, divisors_def] >> + metis_tac[PRIME_POS] +QED + +(* Theorem: (prime_factors n) SUBSET (divisors n) *) +(* Proof: + p IN (prime_factors n) + ==> p IN (divisors n) by prime_factors_def + Hence (prime_factors n) SUBSET (divisors n) by SUBSET_DEF +*) +val prime_factors_subset = store_thm( + "prime_factors_subset", + ``!n. (prime_factors n) SUBSET (divisors n)``, + rw[prime_factors_def, SUBSET_DEF]); + +(* Theorem: FINITE (prime_factors n) *) +(* Proof: + Since (prime_factors n) SUBSET (divisors n) by prime_factors_subset + and FINITE (divisors n) by divisors_finite + Thus FINITE (prime_factors n) by SUBSET_FINITE +*) +val prime_factors_finite = store_thm( + "prime_factors_finite", + ``!n. FINITE (prime_factors n)``, + metis_tac[prime_factors_subset, divisors_finite, SUBSET_FINITE]); + +(* Overload even square-free filter of a set *) +val _ = overload_on("even_sq_free", ``\s. {n | n IN (sq_free s) /\ EVEN (CARD (prime_factors n))}``); + +(* Overload odd square-free filter of a set *) +val _ = overload_on("odd_sq_free", ``\s. {n | n IN (sq_free s) /\ ODD (CARD (prime_factors n))}``); + +(* Theorem: n IN even_sq_free s <=> n IN s /\ square_free n /\ EVEN (CARD (prime_factors n)) *) +(* Proof: by notation. *) +val even_sq_free_element = store_thm( + "even_sq_free_element", + ``!s n. n IN even_sq_free s <=> n IN s /\ square_free n /\ EVEN (CARD (prime_factors n))``, + (rw[] >> metis_tac[])); + +(* Theorem: even_sq_free s SUBSET s *) +(* Proof: by SUBSET_DEF *) +val even_sq_free_subset = store_thm( + "even_sq_free_subset", + ``!s. even_sq_free s SUBSET s``, + rw[SUBSET_DEF]); + +(* Theorem: FINITE s ==> FINITE (even_sq_free s) *) +(* Proof: by even_sq_free_subset, SUBSET_FINITE *) +val even_sq_free_finite = store_thm( + "even_sq_free_finite", + ``!s. FINITE s ==> FINITE (even_sq_free s)``, + metis_tac[even_sq_free_subset, SUBSET_FINITE]); + +(* Theorem: n IN odd_sq_free s <=> n IN s /\ square_free n /\ ODD (CARD (prime_factors n)) *) +(* Proof: by notation. *) +val odd_sq_free_element = store_thm( + "odd_sq_free_element", + ``!s n. n IN odd_sq_free s <=> n IN s /\ square_free n /\ ODD (CARD (prime_factors n))``, + (rw[] >> metis_tac[])); + +(* Theorem: odd_sq_free s SUBSET s *) +(* Proof: by SUBSET_DEF *) +val odd_sq_free_subset = store_thm( + "odd_sq_free_subset", + ``!s. odd_sq_free s SUBSET s``, + rw[SUBSET_DEF]); + +(* Theorem: FINITE s ==> FINITE (odd_sq_free s) *) +(* Proof: by odd_sq_free_subset, SUBSET_FINITE *) +val odd_sq_free_finite = store_thm( + "odd_sq_free_finite", + ``!s. FINITE s ==> FINITE (odd_sq_free s)``, + metis_tac[odd_sq_free_subset, SUBSET_FINITE]); + +(* Theorem: (sq_free s = (even_sq_free s) UNION (odd_sq_free s)) /\ + ((even_sq_free s) INTER (odd_sq_free s) = {}) *) +(* Proof: + This is to show: + (1) sq_free s = even_sq_free s UNION odd_sq_free s + True by EXTENSION, IN_UNION, EVEN_ODD. + (2) even_sq_free s INTER odd_sq_free s = {} + True by EXTENSION, IN_INTER, EVEN_ODD. +*) +val sq_free_split_even_odd = store_thm( + "sq_free_split_even_odd", + ``!s. (sq_free s = (even_sq_free s) UNION (odd_sq_free s)) /\ + ((even_sq_free s) INTER (odd_sq_free s) = {})``, + (rw[EXTENSION] >> metis_tac[EVEN_ODD])); + +(* Theorem: sq_free s = (even_sq_free s) UNION (odd_sq_free s) *) +(* Proof: extract from sq_free_split_even_odd. *) +val sq_free_union_even_odd = + save_thm("sq_free_union_even_odd", sq_free_split_even_odd |> SPEC_ALL |> CONJUNCT1 |> GEN_ALL); +(* val sq_free_union_even_odd = + |- !s. sq_free s = even_sq_free s UNION odd_sq_free s: thm *) + +(* Theorem: (even_sq_free s) INTER (odd_sq_free s) = {} *) +(* Proof: extract from sq_free_split_even_odd. *) +val sq_free_inter_even_odd = + save_thm("sq_free_inter_even_odd", sq_free_split_even_odd |> SPEC_ALL |> CONJUNCT2 |> GEN_ALL); +(* val sq_free_inter_even_odd = + |- !s. even_sq_free s INTER odd_sq_free s = {}: thm *) + +(* Theorem: DISJOINT (even_sq_free s) (odd_sq_free s) *) +(* Proof: by DISJOINT_DEF, sq_free_inter_even_odd. *) +val sq_free_disjoint_even_odd = store_thm( + "sq_free_disjoint_even_odd", + ``!s. DISJOINT (even_sq_free s) (odd_sq_free s)``, + rw_tac std_ss[DISJOINT_DEF, sq_free_inter_even_odd]); + +(* ------------------------------------------------------------------------- *) +(* Less Divisors of a number. *) +(* ------------------------------------------------------------------------- *) + +(* Overload the set of divisors less than n *) +val _ = overload_on("less_divisors", ``\n. {x | x IN (divisors n) /\ x <> n}``); + +(* Theorem: x IN (less_divisors n) <=> (0 < x /\ x < n /\ x divides n) *) +(* Proof: by divisors_element. *) +val less_divisors_element = store_thm( + "less_divisors_element", + ``!n x. x IN (less_divisors n) <=> (0 < x /\ x < n /\ x divides n)``, + rw[divisors_element, EQ_IMP_THM]); + +(* Theorem: less_divisors 0 = {} *) +(* Proof: by divisors_element. *) +val less_divisors_0 = store_thm( + "less_divisors_0", + ``less_divisors 0 = {}``, + rw[divisors_element]); + +(* Theorem: less_divisors 1 = {} *) +(* Proof: by divisors_element. *) +val less_divisors_1 = store_thm( + "less_divisors_1", + ``less_divisors 1 = {}``, + rw[divisors_element]); + +(* Theorem: (less_divisors n) SUBSET (divisors n) *) +(* Proof: by SUBSET_DEF *) +val less_divisors_subset_divisors = store_thm( + "less_divisors_subset_divisors", + ``!n. (less_divisors n) SUBSET (divisors n)``, + rw[SUBSET_DEF]); + +(* Theorem: FINITE (less_divisors n) *) +(* Proof: + Since (less_divisors n) SUBSET (divisors n) by less_divisors_subset_divisors + and FINITE (divisors n) by divisors_finite + so FINITE (proper_divisors n) by SUBSET_FINITE +*) +val less_divisors_finite = store_thm( + "less_divisors_finite", + ``!n. FINITE (less_divisors n)``, + metis_tac[divisors_finite, less_divisors_subset_divisors, SUBSET_FINITE]); + +(* Theorem: prime n ==> (less_divisors n = {1}) *) +(* Proof: + Since prime n + ==> !b. b divides n ==> (b = n) \/ (b = 1) by prime_def + But (less_divisors n) excludes n by less_divisors_element + and 1 < n by ONE_LT_PRIME + Hence less_divisors n = {1} +*) +val less_divisors_prime = store_thm( + "less_divisors_prime", + ``!n. prime n ==> (less_divisors n = {1})``, + rpt strip_tac >> + `!b. b divides n ==> (b = n) \/ (b = 1)` by metis_tac[prime_def] >> + rw[less_divisors_element, EXTENSION, EQ_IMP_THM] >| [ + `x <> n` by decide_tac >> + metis_tac[], + rw[ONE_LT_PRIME] + ]); + +(* Theorem: 1 < n ==> 1 IN (less_divisors n) *) +(* Proof: + 1 IN (less_divisors n) + <=> 1 < n /\ 1 divides n by less_divisors_element + <=> T by ONE_DIVIDES_ALL +*) +val less_divisors_has_1 = store_thm( + "less_divisors_has_1", + ``!n. 1 < n ==> 1 IN (less_divisors n)``, + rw[less_divisors_element]); + +(* Theorem: x IN (less_divisors n) ==> 0 < x *) +(* Proof: by less_divisors_element. *) +val less_divisors_nonzero = store_thm( + "less_divisors_nonzero", + ``!n x. x IN (less_divisors n) ==> 0 < x``, + rw[less_divisors_element]); + +(* Theorem: 1 < d /\ d IN (less_divisors n) ==> (n DIV d) IN (less_divisors n) *) +(* Proof: + d IN (less_divisors n) + ==> d IN (divisors n) by less_divisors_subset_divisors + ==> (n DIV d) IN (divisors n) by divisors_has_cofactor + Note 0 < d /\ d <= n ==> 0 < n by divisors_element + Also n DIV d < n by DIV_LESS, 0 < n /\ 1 < d + thus n DIV d <> n by LESS_NOT_EQ + Hence (n DIV d) IN (less_divisors n) by notation +*) +val less_divisors_has_cofactor = store_thm( + "less_divisors_has_cofactor", + ``!n d. 1 < d /\ d IN (less_divisors n) ==> (n DIV d) IN (less_divisors n)``, + rw[divisors_has_cofactor, divisors_element, DIV_LESS, LESS_NOT_EQ]); + +(* ------------------------------------------------------------------------- *) +(* Proper Divisors of a number. *) +(* ------------------------------------------------------------------------- *) + +(* Overload the set of proper divisors of n *) +val _ = overload_on("proper_divisors", ``\n. {x | x IN (divisors n) /\ x <> 1 /\ x <> n}``); + +(* Theorem: x IN (proper_divisors n) <=> (1 < x /\ x < n /\ x divides n) *) +(* Proof: + Since x IN (divisors n) + ==> 0 < x /\ x <= n /\ x divides n by divisors_element + Since x <= n but x <> n, x < n. + With x <> 0 /\ x <> 1 ==> 1 < x. +*) +val proper_divisors_element = store_thm( + "proper_divisors_element", + ``!n x. x IN (proper_divisors n) <=> (1 < x /\ x < n /\ x divides n)``, + rw[divisors_element, EQ_IMP_THM]); + +(* Theorem: proper_divisors 0 = {} *) +(* Proof: by proper_divisors_element. *) +val proper_divisors_0 = store_thm( + "proper_divisors_0", + ``proper_divisors 0 = {}``, + rw[proper_divisors_element, EXTENSION]); + +(* Theorem: proper_divisors 1 = {} *) +(* Proof: by proper_divisors_element. *) +val proper_divisors_1 = store_thm( + "proper_divisors_1", + ``proper_divisors 1 = {}``, + rw[proper_divisors_element, EXTENSION]); + +(* Theorem: (proper_divisors n) SUBSET (less_divisors n) *) +(* Proof: by SUBSET_DEF *) +val proper_divisors_subset = store_thm( + "proper_divisors_subset", + ``!n. (proper_divisors n) SUBSET (less_divisors n)``, + rw[SUBSET_DEF]); + +(* Theorem: FINITE (proper_divisors n) *) +(* Proof: + Since (proper_divisors n) SUBSET (less_divisors n) by proper_divisors_subset + and FINITE (less_divisors n) by less_divisors_finite + so FINITE (proper_divisors n) by SUBSET_FINITE +*) +val proper_divisors_finite = store_thm( + "proper_divisors_finite", + ``!n. FINITE (proper_divisors n)``, + metis_tac[less_divisors_finite, proper_divisors_subset, SUBSET_FINITE]); + +(* Theorem: 1 NOTIN (proper_divisors n) *) +(* Proof: proper_divisors_element *) +val proper_divisors_not_1 = store_thm( + "proper_divisors_not_1", + ``!n. 1 NOTIN (proper_divisors n)``, + rw[proper_divisors_element]); + +(* Theorem: proper_divisors n = (less_divisors n) DELETE 1 *) +(* Proof: + proper_divisors n + = {x | x IN (divisors n) /\ x <> 1 /\ x <> n} by notation + = {x | x IN (divisors n) /\ x <> n} DELETE 1 by IN_DELETE + = (less_divisors n) DELETE 1 +*) +val proper_divisors_by_less_divisors = store_thm( + "proper_divisors_by_less_divisors", + ``!n. proper_divisors n = (less_divisors n) DELETE 1``, + rw[divisors_element, EXTENSION, EQ_IMP_THM]); + +(* Theorem: prime n ==> (proper_divisors n = {}) *) +(* Proof: + proper_divisors n + = (less_divisors n) DELETE 1 by proper_divisors_by_less_divisors + = {1} DELETE 1 by less_divisors_prime, prime n + = {} by SING_DELETE +*) +val proper_divisors_prime = store_thm( + "proper_divisors_prime", + ``!n. prime n ==> (proper_divisors n = {})``, + rw[proper_divisors_by_less_divisors, less_divisors_prime]); + +(* Theorem: d IN (proper_divisors n) ==> (n DIV d) IN (proper_divisors n) *) +(* Proof: + Let e = n DIV d. + Since d IN (proper_divisors n) + ==> 1 < d /\ d < n by proper_divisors_element + and d IN (less_divisors n) by proper_divisors_subset + so e IN (less_divisors n) by less_divisors_has_cofactor + and 0 < e by less_divisors_nonzero + Since d divides n by less_divisors_element + so n = e * d by DIV_MULT_EQ, 0 < d + thus e <> 1 since n <> d by MULT_LEFT_1 + With 0 < e /\ e <> 1 + ==> e IN (proper_divisors n) by proper_divisors_by_less_divisors, IN_DELETE +*) +val proper_divisors_has_cofactor = store_thm( + "proper_divisors_has_cofactor", + ``!n d. d IN (proper_divisors n) ==> (n DIV d) IN (proper_divisors n)``, + rpt strip_tac >> + qabbrev_tac `e = n DIV d` >> + `1 < d /\ d < n` by metis_tac[proper_divisors_element] >> + `d IN (less_divisors n)` by metis_tac[proper_divisors_subset, SUBSET_DEF] >> + `e IN (less_divisors n)` by rw[less_divisors_has_cofactor, Abbr`e`] >> + `0 < e` by metis_tac[less_divisors_nonzero] >> + `0 < d /\ n <> d` by decide_tac >> + `e <> 1` by metis_tac[less_divisors_element, DIV_MULT_EQ, MULT_LEFT_1] >> + metis_tac[proper_divisors_by_less_divisors, IN_DELETE]); + +(* Theorem: (proper_divisors n) <> {} ==> 1 < MIN_SET (proper_divisors n) *) +(* Proof: + Let s = proper_divisors n. + Since !x. x IN s ==> 1 < x by proper_divisors_element + But MIN_SET s IN s by MIN_SET_IN_SET + Hence 1 < MIN_SET s by above +*) +val proper_divisors_min_gt_1 = store_thm( + "proper_divisors_min_gt_1", + ``!n. (proper_divisors n) <> {} ==> 1 < MIN_SET (proper_divisors n)``, + metis_tac[MIN_SET_IN_SET, proper_divisors_element]); + +(* Theorem: (proper_divisors n) <> {} ==> + (MAX_SET (proper_divisors n) = n DIV (MIN_SET (proper_divisors n))) /\ + (MIN_SET (proper_divisors n) = n DIV (MAX_SET (proper_divisors n))) *) +(* Proof: + Let s = proper_divisors n, b = MIN_SET s. + By MAX_SET_ELIM, this is to show: + (1) FINITE s, true by proper_divisors_finite + (2) s <> {} /\ x IN s /\ !y. y IN s ==> y <= x ==> x = n DIV b /\ b = n DIV x + Note s <> {} ==> n <> 0 by proper_divisors_0 + Let m = n DIV b. + Note n DIV x IN s by proper_divisors_has_cofactor, 0 < n, 1 < b. + Also b IN s /\ b <= x by MIN_SET_IN_SET, s <> {} + thus 1 < b by proper_divisors_min_gt_1 + so m IN s by proper_divisors_has_cofactor, 0 < n, 1 < x. + or 1 < m by proper_divisors_nonzero + and m <= x by implication, x = MAX_SET s. + Thus n DIV x <= n DIV m by DIV_LE_MONOTONE_REVERSE [1], 0 < x, 0 < m. + But n DIV m + = n DIV (n DIV b) = b by divide_by_cofactor, b divides n. + so n DIV x <= b by [1] + Since b <= n DIV x by MIN_SET_PROPERTY, b = MIN_SET s, n DIV x IN s. + so n DIV x = b by LESS_EQUAL_ANTISYM (gives second subgoal) + Hence m = n DIV b + = n DIV (n DIV x) = x by divide_by_cofactor, x divides n (gives first subgoal) +*) +val proper_divisors_max_min = store_thm( + "proper_divisors_max_min", + ``!n. (proper_divisors n) <> {} ==> + (MAX_SET (proper_divisors n) = n DIV (MIN_SET (proper_divisors n))) /\ + (MIN_SET (proper_divisors n) = n DIV (MAX_SET (proper_divisors n)))``, + ntac 2 strip_tac >> + qabbrev_tac `s = proper_divisors n` >> + qabbrev_tac `b = MIN_SET s` >> + DEEP_INTRO_TAC MAX_SET_ELIM >> + strip_tac >- + rw[proper_divisors_finite, Abbr`s`] >> + ntac 3 strip_tac >> + `n <> 0` by metis_tac[proper_divisors_0] >> + `b IN s /\ b <= x` by rw[MIN_SET_IN_SET, Abbr`b`] >> + `1 < b` by rw[proper_divisors_min_gt_1, Abbr`s`, Abbr`b`] >> + `0 < n /\ 1 < x` by decide_tac >> + qabbrev_tac `m = n DIV b` >> + `m IN s /\ (n DIV x) IN s` by rw[proper_divisors_has_cofactor, Abbr`m`, Abbr`s`] >> + `1 < m` by metis_tac[proper_divisors_element] >> + `0 < x /\ 0 < m` by decide_tac >> + `n DIV x <= n DIV m` by rw[DIV_LE_MONOTONE_REVERSE] >> + `b divides n /\ x divides n` by metis_tac[proper_divisors_element] >> + `n DIV m = b` by rw[divide_by_cofactor, Abbr`m`] >> + `b <= n DIV x` by rw[MIN_SET_PROPERTY, Abbr`b`] >> + `b = n DIV x` by rw[LESS_EQUAL_ANTISYM] >> + `m = x` by rw[divide_by_cofactor, Abbr`m`] >> + decide_tac); + +(* This is a milestone theorem. *) + +(* ------------------------------------------------------------------------- *) +(* Useful Properties of Less Divisors *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 1 < n ==> (MIN_SET (less_divisors n) = 1) *) +(* Proof: + Let s = less_divisors n. + Since 1 < n ==> 1 IN s by less_divisors_has_1 + so s <> {} by MEMBER_NOT_EMPTY + and !y. y IN s ==> 0 < y by less_divisors_nonzero + or !y. y IN s ==> 1 <= y by LESS_EQ + Hence 1 = MIN_SET s by MIN_SET_TEST +*) +val less_divisors_min = store_thm( + "less_divisors_min", + ``!n. 1 < n ==> (MIN_SET (less_divisors n) = 1)``, + metis_tac[less_divisors_has_1, MEMBER_NOT_EMPTY, + MIN_SET_TEST, less_divisors_nonzero, LESS_EQ, ONE]); + +(* Theorem: MAX_SET (less_divisors n) <= n DIV 2 *) +(* Proof: + Let s = less_divisors n, m = MAX_SET s. + If s = {}, + Then m = MAX_SET {} = 0 by MAX_SET_EMPTY + and 0 <= n DIV 2 is trivial. + If s <> {}, + Then n <> 0 /\ n <> 1 by less_divisors_0, less_divisors_1 + Note 1 IN s by less_divisors_has_1 + Consider t = s DELETE 1. + Then t = proper_divisors n by proper_divisors_by_less_divisors + If t = {}, + Then s = {1} by DELETE_EQ_SING + and m = 1 by SING_DEF, IN_SING (same as MAX_SET_SING) + Since 2 <= n by 1 < n + thus n DIV n <= n DIV 2 by DIV_LE_MONOTONE_REVERSE + or n DIV n = 1 = m <= n DIV 2 by DIVMOD_ID, 0 < n + If t <> {}, + Let b = MIN_SET t + Then MAX_SET t = n DIV b by proper_divisors_max_min, t <> {} + Since MIN_SET s = 1 by less_divisors_min, 1 < n + and FINITE s by less_divisors_finite + and s <> {1} by DELETE_EQ_SING + thus m = MAX_SET t by MAX_SET_DELETE, s <> {1} + + Now 1 < b by proper_divisors_min_gt_1 + so 2 <= b by LESS_EQ, 1 < b + Hence n DIV b <= n DIV 2 by DIV_LE_MONOTONE_REVERSE + or m <= n DIV 2 by m = MAX_SET t = n DIV b +*) + +Theorem less_divisors_max: + !n. MAX_SET (less_divisors n) <= n DIV 2 +Proof + rpt strip_tac >> + qabbrev_tac `s = less_divisors n` >> + qabbrev_tac `m = MAX_SET s` >> + Cases_on `s = {}` >- rw[MAX_SET_EMPTY, Abbr`m`] >> + `n <> 0 /\ n <> 1` by metis_tac[less_divisors_0, less_divisors_1] >> + `1 < n` by decide_tac >> + `1 IN s` by rw[less_divisors_has_1, Abbr`s`] >> + qabbrev_tac `t = proper_divisors n` >> + `t = s DELETE 1` by rw[proper_divisors_by_less_divisors, Abbr`t`, Abbr`s`] >> + Cases_on `t = {}` >| [ + `s = {1}` by rfs[] >> + `m = 1` by rw[MAX_SET_SING, Abbr`m`] >> + `(2 <= n) /\ (0 < 2) /\ (0 < n) /\ (n DIV n = 1)` by rw[] >> + metis_tac[DIV_LE_MONOTONE_REVERSE], + qabbrev_tac `b = MIN_SET t` >> + `MAX_SET t = n DIV b` by metis_tac[proper_divisors_max_min] >> + `MIN_SET s = 1` by rw[less_divisors_min, Abbr`s`] >> + `FINITE s` by rw[less_divisors_finite, Abbr`s`] >> + `s <> {1}` by metis_tac[DELETE_EQ_SING] >> + `m = MAX_SET t` by metis_tac[MAX_SET_DELETE] >> + `1 < b` by rw[proper_divisors_min_gt_1, Abbr`b`, Abbr`t`] >> + `2 <= b /\ (0 < b) /\ (0 < 2)` by decide_tac >> + `n DIV b <= n DIV 2` by rw[DIV_LE_MONOTONE_REVERSE] >> + decide_tac + ] +QED + +(* Theorem: (less_divisors n) SUBSET (natural (n DIV 2)) *) +(* Proof: + Let s = less_divisors n + If n = 0 or n - 1, + Then s = {} by less_divisors_0, less_divisors_1 + and {} SUBSET t, for any t. by EMPTY_SUBSET + If n <> 0 and n <> 1, 1 < n. + Note FINITE s by less_divisors_finite + and x IN s ==> x <= MAX_SET s by MAX_SET_PROPERTY, FINITE s + But MAX_SET s <= n DIV 2 by less_divisors_max + Thus x IN s ==> x <= n DIV 2 by LESS_EQ_TRANS + Note s <> {} by MEMBER_NOT_EMPTY, x IN s + and x IN s ==> MIN_SET s <= x by MIN_SET_PROPERTY, s <> {} + Since 1 = MIN_SET s, 1 <= x by less_divisors_min, 1 < n + Thus 0 < x <= n DIV 2 by LESS_EQ + or x IN (natural (n DIV 2)) by natural_element +*) +val less_divisors_subset_natural = store_thm( + "less_divisors_subset_natural", + ``!n. (less_divisors n) SUBSET (natural (n DIV 2))``, + rpt strip_tac >> + qabbrev_tac `s = less_divisors n` >> + qabbrev_tac `m = n DIV 2` >> + Cases_on `(n = 0) \/ (n = 1)` >- + metis_tac[less_divisors_0, less_divisors_1, EMPTY_SUBSET] >> + `1 < n` by decide_tac >> + rw_tac std_ss[SUBSET_DEF] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + `FINITE s` by rw[less_divisors_finite, Abbr`s`] >> + `x <= MAX_SET s` by rw[MAX_SET_PROPERTY] >> + `MIN_SET s <= x` by rw[MIN_SET_PROPERTY] >> + `MAX_SET s <= m` by rw[less_divisors_max, Abbr`s`, Abbr`m`] >> + `MIN_SET s = 1` by rw[less_divisors_min, Abbr`s`] >> + `0 < x /\ x <= m` by decide_tac >> + rw[natural_element]); + +(* ------------------------------------------------------------------------- *) +(* Properties of Summation equals Perfect Power *) +(* ------------------------------------------------------------------------- *) + +(* Idea for the theorem below (for m = n DIV 2 when applied in bounds): + p * (p ** m - 1) / (p - 1) + < p * p ** m / (p - 1) discard subtraction + <= p * p ** m / (p / 2) replace by smaller denominator + = 2 * p ** m double division and cancel p + or p * (p ** m - 1) < (p - 1) * 2 * p ** m +*) + +(* Theorem: 1 < p ==> !n. p * (p ** n - 1) < (p - 1) * (2 * p ** n) *) +(* Proof: + Let q = p ** n + Then 1 <= q by ONE_LE_EXP, 0 < p + so p <= p * q by LE_MULT_LCANCEL, p <> 0 + Also 1 < p ==> 2 <= p by LESS_EQ + so 2 * q <= p * q by LE_MULT_RCANCEL, q <> 0 + Thus LHS + = p * (q - 1) + = p * q - p by LEFT_SUB_DISTRIB + And RHS + = (p - 1) * (2 * q) + = p * (2 * q) - 2 * q by RIGHT_SUB_DISTRIB + = 2 * (p * q) - 2 * q by MULT_ASSOC, MULT_COMM + = (p * q + p * q) - 2 * q by TIMES2 + = (p * q - p + p + p * q) - 2 * q by SUB_ADD, p <= p * q + = LHS + p + p * q - 2 * q by above + = LHS + p + (p * q - 2 * q) by LESS_EQ_ADD_SUB, 2 * q <= p * q + = LHS + p + (p - 2) * q by RIGHT_SUB_DISTRIB + + Since 0 < p by 1 < p + and 0 <= (p - 2) * q by 2 <= p + Hence LHS < RHS by discarding positive terms +*) +val perfect_power_special_inequality = store_thm( + "perfect_power_special_inequality", + ``!p. 1 < p ==> !n. p * (p ** n - 1) < (p - 1) * (2 * p ** n)``, + rpt strip_tac >> + qabbrev_tac `q = p ** n` >> + `p <> 0 /\ 2 <= p` by decide_tac >> + `1 <= q` by rw[ONE_LE_EXP, Abbr`q`] >> + `p <= p * q` by rw[] >> + `2 * q <= p * q` by rw[] >> + qabbrev_tac `l = p * (q - 1)` >> + qabbrev_tac `r = (p - 1) * (2 * q)` >> + `l = p * q - p` by rw[Abbr`l`] >> + `r = p * (2 * q) - 2 * q` by rw[Abbr`r`] >> + `_ = 2 * (p * q) - 2 * q` by rw[] >> + `_ = (p * q + p * q) - 2 * q` by rw[] >> + `_ = (p * q - p + p + p * q) - 2 * q` by rw[] >> + `_ = l + p + p * q - 2 * q` by rw[] >> + `_ = l + p + (p * q - 2 * q)` by rw[] >> + `_ = l + p + (p - 2) * q` by rw[] >> + decide_tac); + +(* Theorem: 1 < p /\ 1 < n ==> + p ** (n DIV 2) * p ** (n DIV 2) <= p ** n /\ + 2 * p ** (n DIV 2) <= p ** (n DIV 2) * p ** (n DIV 2) *) +(* Proof: + Let m = n DIV 2, q = p ** m. + The goal becomes: q * q <= p ** n /\ 2 * q <= q * q. + Note 1 < p ==> 0 < p. + First goal: q * q <= p ** n + Then 0 < q by EXP_POS, 0 < p + and 2 * m <= n by DIV_MULT_LE, 0 < 2. + thus p ** (2 * m) <= p ** n by EXP_BASE_LE_MONO, 1 < p. + Since p ** (2 * m) + = p ** (m + m) by TIMES2 + = q * q by EXP_ADD + Thus q * q <= p ** n by above + + Second goal: 2 * q <= q * q + Since 1 < n, so 2 <= n by LESS_EQ + so 2 DIV 2 <= n DIV 2 by DIV_LE_MONOTONE, 0 < 2. + or 1 <= m, i.e. 0 < m by DIVMOD_ID, 0 < 2. + Thus 1 < q by ONE_LT_EXP, 1 < p, 0 < m. + so 2 <= q by LESS_EQ + and 2 * q <= q * q by MULT_RIGHT_CANCEL, q <> 0. + Hence 2 * q <= p ** n by LESS_EQ_TRANS +*) +val perfect_power_half_inequality_lemma = prove( + ``!p n. 1 < p /\ 1 < n ==> + p ** (n DIV 2) * p ** (n DIV 2) <= p ** n /\ + 2 * p ** (n DIV 2) <= p ** (n DIV 2) * p ** (n DIV 2)``, + ntac 3 strip_tac >> + qabbrev_tac `m = n DIV 2` >> + qabbrev_tac `q = p ** m` >> + strip_tac >| [ + `0 < p /\ 0 < 2` by decide_tac >> + `0 < q /\ q <> 0` by rw[EXP_POS, Abbr`q`] >> + `2 * m <= n` by metis_tac[DIV_MULT_LE, MULT_COMM] >> + `p ** (2 * m) <= p ** n` by rw[EXP_BASE_LE_MONO] >> + `p ** (2 * m) = p ** (m + m)` by rw[] >> + `_ = q * q` by rw[EXP_ADD, Abbr`q`] >> + decide_tac, + `2 <= n /\ 0 < 2` by decide_tac >> + `1 <= m` by metis_tac[DIV_LE_MONOTONE, DIVMOD_ID] >> + `0 < m` by decide_tac >> + `1 < q` by rw[ONE_LT_EXP, Abbr`q`] >> + rw[] + ]); + +(* Theorem: 1 < p /\ 0 < n ==> 2 * p ** (n DIV 2) <= p ** n *) +(* Proof: + Let m = n DIV 2, q = p ** m. + The goal becomes: 2 * q <= p ** n + If n = 1, + Then m = 0 by ONE_DIV, 0 < 2. + and q = 1 by EXP + and p ** n = p by EXP_1 + Since 1 < p ==> 2 <= p by LESS_EQ + Hence 2 * q <= p = p ** n by MULT_RIGHT_1 + If n <> 1, 1 < n. + Then q * q <= p ** n /\ + 2 * q <= q * q by perfect_power_half_inequality_lemma + Hence 2 * q <= p ** n by LESS_EQ_TRANS +*) +val perfect_power_half_inequality_1 = store_thm( + "perfect_power_half_inequality_1", + ``!p n. 1 < p /\ 0 < n ==> 2 * p ** (n DIV 2) <= p ** n``, + rpt strip_tac >> + qabbrev_tac `m = n DIV 2` >> + qabbrev_tac `q = p ** m` >> + Cases_on `n = 1` >| [ + `m = 0` by rw[Abbr`m`] >> + `(q = 1) /\ (p ** n = p)` by rw[Abbr`q`] >> + `2 <= p` by decide_tac >> + rw[], + `1 < n` by decide_tac >> + `q * q <= p ** n /\ 2 * q <= q * q` by rw[perfect_power_half_inequality_lemma, Abbr`q`, Abbr`m`] >> + decide_tac + ]); + +(* Theorem: 1 < p /\ 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) <= p ** n - 2 * p ** (n DIV 2) *) +(* Proof: + Let m = n DIV 2, q = p ** m. + The goal becomes: (q - 2) * q <= p ** n - 2 * q + If n = 1, + Then m = 0 by ONE_DIV, 0 < 2. + and q = 1 by EXP + and p ** n = p by EXP_1 + Since 1 < p ==> 2 <= p by LESS_EQ + or 0 <= p - 2 by SUB_LEFT_LESS_EQ + Hence (q - 2) * q = 0 <= p - 2 + If n <> 1, 1 < n. + Then q * q <= p ** n /\ 2 * q <= q * q by perfect_power_half_inequality_lemma + Thus q * q - 2 * q <= p ** n - 2 * q by LE_SUB_RCANCEL, 2 * q <= q * q + or (q - 2) * q <= p ** n - 2 * q by RIGHT_SUB_DISTRIB +*) +val perfect_power_half_inequality_2 = store_thm( + "perfect_power_half_inequality_2", + ``!p n. 1 < p /\ 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) <= p ** n - 2 * p ** (n DIV 2)``, + rpt strip_tac >> + qabbrev_tac `m = n DIV 2` >> + qabbrev_tac `q = p ** m` >> + Cases_on `n = 1` >| [ + `m = 0` by rw[Abbr`m`] >> + `(q = 1) /\ (p ** n = p)` by rw[Abbr`q`] >> + `0 <= p - 2 /\ (1 - 2 = 0)` by decide_tac >> + rw[], + `1 < n` by decide_tac >> + `q * q <= p ** n /\ 2 * q <= q * q` by rw[perfect_power_half_inequality_lemma, Abbr`q`, Abbr`m`] >> + decide_tac + ]); + +(* Already in pred_setTheory: +SUM_IMAGE_SUBSET_LE; +!f s t. FINITE s /\ t SUBSET s ==> SIGMA f t <= SIGMA f s: thm +SUM_IMAGE_MONO_LESS_EQ; +|- !s. FINITE s ==> (!x. x IN s ==> f x <= g x) ==> SIGMA f s <= SIGMA g s: thm +*) + +(* Theorem: 1 < p ==> !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> + (!n. 0 < n ==> n * (f n) <= p ** n) /\ + (!n. 0 < n ==> p ** n - 2 * p ** (n DIV 2) < n * (f n)) *) +(* Proof: + Step 1: prove a specific lemma for sum decomposition + Claim: !n. 0 < n ==> (divisors n DIFF {n}) SUBSET (natural (n DIV 2)) /\ + (p ** n = SIGMA (\d. d * f d) (divisors n)) ==> + (p ** n = n * f n + SIGMA (\d. d * f d) (divisors n DIFF {n})) + Proof: Let s = divisors n, a = {n}, b = s DIFF a, m = n DIV 2. + Then b = less_divisors n by EXTENSION,IN_DIFF + and b SUBSET (natural m) by less_divisors_subset_natural + This gives the first part. + For the second part: + Note a SUBSET s by divisors_has_last, SUBSET_DEF + and b SUBSET s by DIFF_SUBSET + Thus s = b UNION a by UNION_DIFF, a SUBSET s + and DISJOINT b a by DISJOINT_DEF, EXTENSION + Now FINITE s by divisors_finite + so FINITE a /\ FINITE b by SUBSET_FINITE, by a SUBSEt s /\ b SUBSET s + + p ** n + = SIGMA (\d. d * f d) s by implication + = SIGMA (\d. d * f d) (b UNION a) by above, s = b UNION a + = SIGMA (\d. d * f d) b + SIGMA (\d. d * f d) a by SUM_IMAGE_DISJOINT, FINITE a /\ FINITE b + = SIGMA (\d. d * f d) b + n * f n by SUM_IMAGE_SING + = n * f n + SIGMA (\d. d * f d) b by ADD_COMM + This gives the second part. + + Step 2: Upper bound, to show: !n. 0 < n ==> n * f n <= p ** n + Let b = divisors n DIFF {n} + Since n * f n + SIGMA (\d. d * f d) b = p ** n by lemma + Hence n * f n <= p ** n by 0 <= SIGMA (\d. d * f d) b + + Step 3: Lower bound, to show: !n. 0 < n ==> p ** n - p ** (n DIV 2) <= n * f n + Let s = divisors n, a = {n}, b = s DIFF a, m = n DIV 2. + Note b SUBSET (natural m) /\ + (p ** n = n * f n + SIGMA (\d. d * f d) b) by lemma + Since FINITE (natural m) by natural_finite + thus SIGMA (\d. d * f d) b + <= SIGMA (\d. d * f d) (natural m) by SUM_IMAGE_SUBSET_LE [1] + Also !d. d IN (natural m) ==> 0 < d by natural_element + and !d. 0 < d ==> d * f d <= p ** d by upper bound (Step 2) + thus !d. d IN (natural m) ==> d * f d <= p ** d by implication + Hence SIGMA (\d. d * f d) (natural m) + <= SIGMA (\d. p ** d) (natural m) by SUM_IMAGE_MONO_LESS_EQ [2] + Now 1 < p ==> 0 < p /\ (p - 1) <> 0 by arithmetic + + (p - 1) * SIGMA (\d. d * f d) b + <= (p - 1) * SIGMA (\d. d * f d) (natural m) by LE_MULT_LCANCEL, [1] + <= (p - 1) * SIGMA (\d. p ** d) (natural m) by LE_MULT_LCANCEL, [2] + = p * (p ** m - 1) by sigma_geometric_natural_eqn + < (p - 1) * (2 * p ** m) by perfect_power_special_inequality + + (p - 1) * SIGMA (\d. d * f d) b < (p - 1) * (2 * p ** m) by LESS_EQ_LESS_TRANS + or SIGMA (\d. d * f d) b < 2 * p ** m by LT_MULT_LCANCEL, (p - 1) <> 0 + + But 2 * p ** m <= p ** n by perfect_power_half_inequality_1, 1 < p, 0 < n + Thus p ** n = p ** n - 2 * p ** m + 2 * p ** m by SUB_ADD, 2 * p ** m <= p ** n + Combinig with lemma, + p ** n - 2 * p ** m + 2 * p ** m < n * f n + 2 * p ** m + or p ** n - 2 * p ** m < n * f n by LESS_MONO_ADD_EQ, no condition +*) +Theorem sigma_eq_perfect_power_bounds_1: + !p. + 1 < p ==> + !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> + (!n. 0 < n ==> n * (f n) <= p ** n) /\ + (!n. 0 < n ==> p ** n - 2 * p ** (n DIV 2) < n * (f n)) +Proof + ntac 4 strip_tac >> + ‘!n. 0 < n ==> + (divisors n DIFF {n}) SUBSET (natural (n DIV 2)) /\ + (p ** n = SIGMA (\d. d * f d) (divisors n) ==> + p ** n = n * f n + SIGMA (\d. d * f d) (divisors n DIFF {n}))’ + by (ntac 2 strip_tac >> + qabbrev_tac `s = divisors n` >> + qabbrev_tac `a = {n}` >> + qabbrev_tac `b = s DIFF a` >> + qabbrev_tac `m = n DIV 2` >> + `b = less_divisors n` by rw[EXTENSION, Abbr`b`, Abbr`a`, Abbr`s`] >> + `b SUBSET (natural m)` by metis_tac[less_divisors_subset_natural] >> + strip_tac >- rw[] >> + `a SUBSET s` by rw[divisors_has_last, SUBSET_DEF, Abbr`s`, Abbr`a`] >> + `b SUBSET s` by rw[Abbr`b`] >> + `s = b UNION a` by rw[UNION_DIFF, Abbr`b`] >> + `DISJOINT b a` + by (rw[DISJOINT_DEF, Abbr`b`, EXTENSION] >> metis_tac[]) >> + `FINITE s` by rw[divisors_finite, Abbr`s`] >> + `FINITE a /\ FINITE b` by metis_tac[SUBSET_FINITE] >> + strip_tac >> + `_ = SIGMA (\d. d * f d) (b UNION a)` by metis_tac[Abbr`s`] >> + `_ = SIGMA (\d. d * f d) b + SIGMA (\d. d * f d) a` + by rw[SUM_IMAGE_DISJOINT] >> + `_ = SIGMA (\d. d * f d) b + n * f n` by rw[SUM_IMAGE_SING, Abbr`a`] >> + rw[]) >> + conj_asm1_tac >| [ + rpt strip_tac >> + `p ** n = n * f n + SIGMA (\d. d * f d) (divisors n DIFF {n})` by rw[] >> + decide_tac, + rpt strip_tac >> + qabbrev_tac `s = divisors n` >> + qabbrev_tac `a = {n}` >> + qabbrev_tac `b = s DIFF a` >> + qabbrev_tac `m = n DIV 2` >> + `b SUBSET (natural m) /\ (p ** n = n * f n + SIGMA (\d. d * f d) b)` + by rw[Abbr`s`, Abbr`a`, Abbr`b`, Abbr`m`] >> + `FINITE (natural m)` by rw[natural_finite] >> + `SIGMA (\d. d * f d) b <= SIGMA (\d. d * f d) (natural m)` + by rw[SUM_IMAGE_SUBSET_LE] >> + `!d. d IN (natural m) ==> 0 < d` by rw[natural_element] >> + `SIGMA (\d. d * f d) (natural m) <= SIGMA (\d. p ** d) (natural m)` + by rw[SUM_IMAGE_MONO_LESS_EQ] >> + `0 < p /\ (p - 1) <> 0` by decide_tac >> + `(p - 1) * SIGMA (\d. p ** d) (natural m) = p * (p ** m - 1)` + by rw[sigma_geometric_natural_eqn] >> + `p * (p ** m - 1) < (p - 1) * (2 * p ** m)` + by rw[perfect_power_special_inequality] >> + `SIGMA (\d. d * f d) b < 2 * p ** m` + by metis_tac[LE_MULT_LCANCEL, LESS_EQ_TRANS, LESS_EQ_LESS_TRANS, + LT_MULT_LCANCEL] >> + `p ** n < n * f n + 2 * p ** m` by decide_tac >> + `2 * p ** m <= p ** n` by rw[perfect_power_half_inequality_1, Abbr`m`] >> + decide_tac + ] +QED + +(* Theorem: 1 < p ==> !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> + (!n. 0 < n ==> n * (f n) <= p ** n) /\ + (!n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n)) *) +(* Proof: + For the first goal: (!n. 0 < n ==> n * (f n) <= p ** n) + True by sigma_eq_perfect_power_bounds_1. + For the second goal: (!n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n)) + Let m = n DIV 2. + Then p ** n - 2 * p ** m < n * (f n) by sigma_eq_perfect_power_bounds_1 + and (p ** m - 2) * p ** m <= p ** n - 2 * p ** m by perfect_power_half_inequality_2 + Hence (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n) by LESS_EQ_LESS_TRANS +*) +val sigma_eq_perfect_power_bounds_2 = store_thm( + "sigma_eq_perfect_power_bounds_2", + ``!p. 1 < p ==> !f. (!n. 0 < n ==> (p ** n = SIGMA (\d. d * f d) (divisors n))) ==> + (!n. 0 < n ==> n * (f n) <= p ** n) /\ + (!n. 0 < n ==> (p ** (n DIV 2) - 2) * p ** (n DIV 2) < n * (f n))``, + rpt strip_tac >- + rw[sigma_eq_perfect_power_bounds_1] >> + qabbrev_tac `m = n DIV 2` >> + `p ** n - 2 * p ** m < n * (f n)` by rw[sigma_eq_perfect_power_bounds_1, Abbr`m`] >> + `(p ** m - 2) * p ** m <= p ** n - 2 * p ** m` by rw[perfect_power_half_inequality_2, Abbr`m`] >> + decide_tac); + +(* This is a milestone theorem. *) + +(* ------------------------------------------------------------------------- *) + +(* export theory at end *) +val _ = export_theory(); + +(*===========================================================================*) diff --git a/src/bag/bagScript.sml b/src/bag/bagScript.sml index 936592ebc3..584e4d989a 100644 --- a/src/bag/bagScript.sml +++ b/src/bag/bagScript.sml @@ -3324,7 +3324,24 @@ Theorem unibag_SUB_BAG: Proof rw[unibag_thm,SUB_BAG,BAG_IN,BAG_INN] QED - +Theorem BAG_OF_SET_IMAGE_INJ: + !f s. + (!x y. x IN s /\ y IN s /\ f x = f y ==> x = y) ==> + BAG_OF_SET (IMAGE f s) = BAG_IMAGE f (BAG_OF_SET s) +Proof + rw[FUN_EQ_THM, BAG_OF_SET, BAG_IMAGE_DEF] + \\ rw[] \\ gs[GSYM BAG_OF_SET] + \\ gs[BAG_FILTER_BAG_OF_SET] + \\ simp[BAG_CARD_BAG_OF_SET] + >- ( + irule SING_CARD_1 + \\ simp[SING_TEST, GSYM pred_setTheory.MEMBER_NOT_EMPTY] + \\ metis_tac[] ) + >- simp[EXTENSION] + \\ qmatch_asmsub_abbrev_tac`INFINITE z` + \\ `z = {}` suffices_by metis_tac[FINITE_EMPTY] + \\ simp[EXTENSION, Abbr`z`] +QED (*---------------------------------------------------------------------------*) (* Add multiset type to the TypeBase. *) diff --git a/src/bag/primeFactorScript.sml b/src/bag/primeFactorScript.sml index a92536c993..2e34ccbd0a 100644 --- a/src/bag/primeFactorScript.sml +++ b/src/bag/primeFactorScript.sml @@ -2,15 +2,9 @@ (* Fundamental theorem of arithmetic for num. *) (*---------------------------------------------------------------------------*) -open HolKernel Parse boolLib bossLib simpLib BasicProvers metisLib - bagTheory dividesTheory arithmeticTheory; +open HolKernel Parse boolLib bossLib; -(* Interactive -quietdec := true; -app load ["dividesTheory", "gcdTheory", "bagTheory"]; -open dividesTheory bagTheory arithmeticTheory; -quietdec := false; -*) +open simpLib BasicProvers metisLib bagTheory dividesTheory arithmeticTheory; val _ = new_theory "primeFactor"; diff --git a/src/list/src/indexedListsScript.sml b/src/list/src/indexedListsScript.sml index df76b227f6..a152c0160b 100644 --- a/src/list/src/indexedListsScript.sml +++ b/src/list/src/indexedListsScript.sml @@ -1,7 +1,7 @@ -open HolKernel Parse boolLib +open HolKernel Parse boolLib BasicProvers; (* bossLib approximation *) -open BasicProvers TotalDefn simpLib numLib IndDefLib +open TotalDefn simpLib numLib IndDefLib listTheory rich_listTheory; fun simp thl = ASM_SIMP_TAC (srw_ss() ++ numSimps.ARITH_ss) thl fun dsimp thl = @@ -12,13 +12,11 @@ fun kall_tac th = K ALL_TAC th val metis_tac = metisLib.METIS_TAC val qid_spec_tac = Q.ID_SPEC_TAC fun rw thl = SRW_TAC[] thl -fun fs thl = full_simp_tac (srw_ss()) thl +fun fs thl = full_simp_tac (srw_ss() ++ numSimps.ARITH_ss) thl; val rename1 = Q.RENAME1_TAC val qspec_then = Q.SPEC_THEN val zDefine = Lib.with_flag (computeLib.auto_import_definitions,false) Define -open listTheory rich_listTheory - val _ = new_theory "indexedLists"; Definition MAPi_def[simp,nocompute]: @@ -149,6 +147,38 @@ val findi_def = Define` findi x (h::t) = if x = h then 0 else 1 + findi x t `; +Theorem findi_nil = findi_def |> CONJUNCT1; +(* val findi_nil = |- !x. findi x [] = 0: thm *) + +Theorem findi_cons = findi_def |> CONJUNCT2; +(* val findi_cons = |- !x h t. findi x (h::t) = if x = h then 0 else 1 + findi x t: thm *) + +(* Theorem: ~MEM x ls ==> findi x ls = LENGTH ls *) +(* Proof: + By induction on ls. + Base: ~MEM x [] ==> findi x [] = LENGTH [] + findi x [] + = 0 by findi_nil + = LENGTH [] by LENGTH + Step: ~MEM x ls ==> findi x ls = LENGTH ls ==> + !h. ~MEM x (h::ls) ==> findi x (h::ls) = LENGTH (h::ls) + Note ~MEM x (h::ls) + ==> x <> h /\ ~MEM x ls by MEM + Thus findi x (h::ls) + = 1 + findi x ls by findi_cons + = 1 + LENGTH ls by induction hypothesis + = SUC (LENGTH ls) by ADD1 + = LENGTH (h::ls) by LENGTH +*) +Theorem findi_none: + !ls x. ~MEM x ls ==> findi x ls = LENGTH ls +Proof + rpt strip_tac >> + Induct_on `ls` >- + simp[findi_nil] >> + simp[findi_cons] +QED + val MEM_findi = store_thm( "MEM_findi", ``MEM x l ==> findi x l < LENGTH l``, @@ -167,6 +197,69 @@ val EL_findi = store_thm( ``!l x. MEM x l ==> EL (findi x l) l = x``, Induct_on`l` >> rw[findi_def] >> simp[DECIDE ``1 + x = SUC x``]); +(* Theorem: ALL_DISTINCT ls /\ MEM x ls /\ n < LENGTH ls ==> (x = EL n ls <=> findi x ls = n) *) +(* Proof: + If part: x = EL n ls ==> findi x ls = n + Given ALL_DISTINCT ls /\ n < LENGTH ls + This is true by findi_EL + Only-if part: findi x ls = n ==> x = EL n ls + Given MEM x ls + This is true by EL_findi +*) +Theorem findi_EL_iff: + !ls x n. ALL_DISTINCT ls /\ MEM x ls /\ n < LENGTH ls ==> (x = EL n ls <=> findi x ls = n) +Proof + metis_tac[findi_EL, EL_findi] +QED + +(* Theorem: findi x (l1 ++ l2) = if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 *) +(* Proof: + By induction on l1. + Base: findi x ([] ++ l2) = if MEM x [] then findi x [] else LENGTH [] + findi x l2 + Note MEM x [] = F by MEM + so findi x ([] ++ l2) + = findi x l2 by APPEND + = 0 + findi x l2 by ADD + = LENGTH [] + findi x l2 by LENGTH + Step: findi x (l1 ++ l2) = if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 ==> + !h. findi x (h::l1 ++ l2) = if MEM x (h::l1) then findi x (h::l1) + else LENGTH (h::l1) + findi x l2 + + Note findi x (h::l1 ++ l2) + = if x = h then 0 else 1 + findi x (l1 ++ l2) by findi_cons + + Case: MEM x (h::l1). + To show: findi x (h::l1 ++ l2) = findi x (h::l1). + Note MEM x (h::l1) + <=> x = h \/ MEM x l1 by MEM + If x = h, + findi x (h::l1 ++ l2) + = 0 = findi x (h::l1) by findi_cons + If x <> h, then MEM x l1. + findi x (h::l1 ++ l2) + = 1 + findi x (l1 ++ l2) by x <> h + = 1 + findi x l1 by induction hypothesis + = findi x (h::l1) by findi_cons + + Case: ~MEM x (h::l1). + To show: findi x (h::l1 ++ l2) = LENGTH (h::l1) + findi x l2. + Note ~MEM x (h::l1) + <=> x <> h /\ ~MEM x l1 by MEM + findi x (h::l1 ++ l2) + = 1 + findi x (l1 ++ l2) by x <> h + = 1 + (LENGTH l1 + findi x l2) by induction hypothesis + = (1 + LENGTH l1) + findi x l2 by arithmetic + = LENGTH (h::l1) + findi x l2 by LENGTH +*) +Theorem findi_APPEND: + !l1 l2 x. findi x (l1 ++ l2) = if MEM x l1 then findi x l1 else LENGTH l1 + findi x l2 +Proof + rpt strip_tac >> + Induct_on `l1` >- + simp[] >> + (rw[findi_cons] >> fs[]) +QED + val delN_def = Define` delN i [] = [] /\ delN i (h::t) = if i = 0 then t diff --git a/src/list/src/listRangeScript.sml b/src/list/src/listRangeScript.sml index 1e4c2309c8..7a4e39f5b5 100644 --- a/src/list/src/listRangeScript.sml +++ b/src/list/src/listRangeScript.sml @@ -1,10 +1,223 @@ -open HolKernel Parse boolLib -open BasicProvers TotalDefn simpLib numSimps numLib +(* ------------------------------------------------------------------------- *) +(* List of Range of Numbers *) +(* ------------------------------------------------------------------------- *) -open listTheory +open HolKernel Parse boolLib BasicProvers; + +open arithmeticTheory TotalDefn simpLib numSimps numLib listTheory metisLib + pred_setTheory listSimps dividesTheory; + +val decide_tac = DECIDE_TAC; +val metis_tac = METIS_TAC; +val qabbrev_tac = Q.ABBREV_TAC; +val qexists_tac = Q.EXISTS_TAC; +fun simp l = ASM_SIMP_TAC (srw_ss() ++ ARITH_ss) l; +fun fs l = FULL_SIMP_TAC (srw_ss() ++ ARITH_ss) l; +fun rfs l = REV_FULL_SIMP_TAC (srw_ss() ++ ARITH_ss) l; +val rw = SRW_TAC [ARITH_ss]; val _ = new_theory "listRange"; +(* ------------------------------------------------------------------------- *) +(* Range Conjunction and Disjunction *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: a <= j /\ j <= a <=> (j = a) *) +(* Proof: trivial by arithmetic. *) +val every_range_sing = store_thm( + "every_range_sing", + ``!a j. a <= j /\ j <= a <=> (j = a)``, + decide_tac); + +(* Theorem: a <= b ==> + ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ !j. a + 1 <= j /\ j <= b ==> f j)) *) +(* Proof: + If part: !j. a <= j /\ j <= b ==> f j ==> + f a /\ !j. a + 1 <= j /\ j <= b ==> f j + This is trivial since a + 1 = SUC a. + Only-if part: f a /\ !j. a + 1 <= j /\ j <= b ==> f j ==> + !j. a <= j /\ j <= b ==> f j + Note a <= j <=> a = j or a < j by arithmetic + If a = j, this is trivial. + If a < j, then a + 1 <= j, also trivial. +*) +val every_range_cons = store_thm( + "every_range_cons", + ``!f a b. a <= b ==> + ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ !j. a + 1 <= j /\ j <= b ==> f j))``, + rw[EQ_IMP_THM] >> + `(a = j) \/ (a < j)` by decide_tac >- + fs[] >> + fs[]); + +(* Theorem: a <= b ==> ((!j. PRE a <= j /\ j <= b ==> f j) <=> (f (PRE a) /\ !j. a <= j /\ j <= b ==> f j)) *) +(* Proof: + !j. PRE a <= j /\ j <= b ==> f j + <=> !j. (PRE a = j \/ a <= j) /\ j <= b ==> f j by arithmetic + <=> !j. (j = PRE a ==> f j) /\ a <= j /\ j <= b ==> f j by RIGHT_AND_OVER_OR, DISJ_IMP_THM + <=> !j. a <= j /\ j <= b ==> f j /\ f (PRE a) +*) +Theorem every_range_split_head: + !f a b. a <= b ==> + ((!j. PRE a <= j /\ j <= b ==> f j) <=> (f (PRE a) /\ !j. a <= j /\ j <= b ==> f j)) +Proof + rpt strip_tac >> + `!j. PRE a <= j <=> PRE a = j \/ a <= j` by decide_tac >> + metis_tac[] +QED + +(* Theorem: a <= b ==> ((!j. a <= j /\ j <= SUC b ==> f j) <=> (f (SUC b) /\ !j. a <= j /\ j <= b ==> f j)) *) +(* Proof: + !j. a <= j /\ j <= SUC b ==> f j + <=> !j. a <= j /\ (j <= b \/ j = SUC b) ==> f j by arithmetic + <=> !j. a <= j /\ j <= b ==> f j /\ (j = SUC b ==> f j) by LEFT_AND_OVER_OR, DISJ_IMP_THM + <=> !j. a <= j /\ j <= b ==> f j /\ f (SUC b) +*) +Theorem every_range_split_last: + !f a b. a <= b ==> + ((!j. a <= j /\ j <= SUC b ==> f j) <=> (f (SUC b) /\ !j. a <= j /\ j <= b ==> f j)) +Proof + rpt strip_tac >> + `!j. j <= SUC b <=> j <= b \/ j = SUC b` by decide_tac >> + metis_tac[] +QED + +(* Theorem: a <= b ==> ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ f b /\ !j. a < j /\ j < b ==> f j)) *) +(* Proof: + !j. a <= j /\ j <= b ==> f j + <=> !j. (a < j \/ a = j) /\ (j < b \/ j = b) ==> f j by arithmetic + <=> !j. a = j ==> f j /\ j = b ==> f j /\ !j. a < j /\ j < b ==> f j by LEFT_AND_OVER_OR, DISJ_IMP_THM + <=> f a /\ f b /\ !j. a < j /\ j < b ==> f j +*) +Theorem every_range_less_ends: + !f a b. a <= b ==> + ((!j. a <= j /\ j <= b ==> f j) <=> (f a /\ f b /\ !j. a < j /\ j < b ==> f j)) +Proof + rpt strip_tac >> + `!m n. m <= n <=> m < n \/ m = n` by decide_tac >> + metis_tac[] +QED + +(* Theorem: a < b /\ f a /\ ~f b ==> + ?m. a <= m /\ m < b /\ (!j. a <= j /\ j <= m ==> f j) /\ ~f (SUC m) *) +(* Proof: + Let s = {p | a <= p /\ p < b /\ (!j. a <= j /\ j <= p ==> f j)} + Pick m = MAX_SET s. + Note f a ==> a IN s by every_range_sing + so s <> {} by MEMBER_NOT_EMPTY + Also s SUBSET (count b) by SUBSET_DEF + so FINITE s by FINITE_COUNT, SUBSET_FINITE + ==> m IN s by MAX_SET_IN_SET + Thus a <= m /\ m < b /\ (!j. a <= j /\ j <= m ==> f j) + It remains to show: ~f (SUC m). + By contradiction, suppose f (SUC m). + Since m < b, SUC m <= b. + But ~f b, so SUC m <> b by given + Thus a <= m < SUC m, and SUC m < b, + and !j. a <= j /\ j <= SUC m ==> f j) + ==> SUC m IN s by every_range_split_last + Then SUC m <= m by X_LE_MAX_SET + which is impossible by LESS_SUC +*) +Theorem every_range_span_max: + !f a b. a < b /\ f a /\ ~f b ==> + ?m. a <= m /\ m < b /\ (!j. a <= j /\ j <= m ==> f j) /\ ~f (SUC m) +Proof + rpt strip_tac >> + qabbrev_tac `s = {p | a <= p /\ p < b /\ (!j. a <= j /\ j <= p ==> f j)}` >> + qabbrev_tac `m = MAX_SET s` >> + qexists_tac `m` >> + `a IN s` by fs[every_range_sing, Abbr`s`] >> + `s SUBSET (count b)` by fs[SUBSET_DEF, Abbr`s`] >> + `FINITE s /\ s <> {}` by metis_tac[FINITE_COUNT, SUBSET_FINITE, MEMBER_NOT_EMPTY] >> + `m IN s` by fs[MAX_SET_IN_SET, Abbr`m`] >> + rfs[Abbr`s`] >> + spose_not_then strip_assume_tac >> + qabbrev_tac `s = {p | a <= p /\ p < b /\ (!j. a <= j /\ j <= p ==> f j)}` >> + `SUC m <> b` by metis_tac[] >> + `a <= SUC m /\ SUC m < b` by decide_tac >> + `SUC m IN s` by fs[every_range_split_last, Abbr`s`] >> + `SUC m <= m` by simp[X_LE_MAX_SET, Abbr`m`] >> + decide_tac +QED + +(* Theorem: a < b /\ ~f a /\ f b ==> + ?m. a < m /\ m <= b /\ (!j. m <= j /\ j <= b ==> f j) /\ ~f (PRE m) *) +(* Proof: + Let s = {p | a < p /\ p <= b /\ (!j. p <= j /\ j <= b ==> f j)} + Pick m = MIN_SET s. + Note f b ==> b IN s by every_range_sing + so s <> {} by MEMBER_NOT_EMPTY + ==> m IN s by MIN_SET_IN_SET + Thus a < m /\ m <= b /\ (!j. m <= j /\ j <= b ==> f j) + It remains to show: ~f (PRE m). + By contradiction, suppose f (PRE m). + Since a < m, a <= PRE m. + But ~f a, so PRE m <> a by given + Thus a < PRE m, and PRE m <= b, + and !j. PRE m <= j /\ j <= b ==> f j) + ==> PRE m IN s by every_range_split_head + Then m <= PRE m by MIN_SET_PROPERTY + which is impossible by PRE_LESS, a < m ==> 0 < m +*) +Theorem every_range_span_min: + !f a b. a < b /\ ~f a /\ f b ==> + ?m. a < m /\ m <= b /\ (!j. m <= j /\ j <= b ==> f j) /\ ~f (PRE m) +Proof + rpt strip_tac >> + qabbrev_tac `s = {p | a < p /\ p <= b /\ (!j. p <= j /\ j <= b ==> f j)}` >> + qabbrev_tac `m = MIN_SET s` >> + qexists_tac `m` >> + `b IN s` by fs[every_range_sing, Abbr`s`] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + `m IN s` by fs[MIN_SET_IN_SET, Abbr`m`] >> + rfs[Abbr`s`] >> + spose_not_then strip_assume_tac >> + qabbrev_tac `s = {p | a < p /\ p <= b /\ (!j. p <= j /\ j <= b ==> f j)}` >> + `PRE m <> a` by metis_tac[] >> + `a < PRE m /\ PRE m <= b` by decide_tac >> + `PRE m IN s` by fs[every_range_split_head, Abbr`s`] >> + `m <= PRE m` by simp[MIN_SET_PROPERTY, Abbr`m`] >> + decide_tac +QED + +(* Theorem: ?j. a <= j /\ j <= a <=> (j = a) *) +(* Proof: trivial by arithmetic. *) +val exists_range_sing = store_thm( + "exists_range_sing", + ``!a. ?j. a <= j /\ j <= a <=> (j = a)``, + metis_tac[LESS_EQ_REFL]); + +(* Theorem: a <= b ==> + ((?j. a <= j /\ j <= b /\ f j) <=> (f a \/ ?j. a + 1 <= j /\ j <= b /\ f j)) *) +(* Proof: + If part: ?j. a <= j /\ j <= b /\ f j ==> + f a \/ ?j. a + 1 <= j /\ j <= b /\ f j + This is trivial since a + 1 = SUC a. + Only-if part: f a /\ ?j. a + 1 <= j /\ j <= b /\ f j ==> + ?j. a <= j /\ j <= b /\ f j + Note a <= j <=> a = j or a < j by arithmetic + If a = j, this is trivial. + If a < j, then a + 1 <= j, also trivial. +*) +val exists_range_cons = store_thm( + "exists_range_cons", + ``!f a b. a <= b ==> + ((?j. a <= j /\ j <= b /\ f j) <=> (f a \/ ?j. a + 1 <= j /\ j <= b /\ f j))``, + rw[EQ_IMP_THM] >| [ + `(a = j) \/ (a < j)` by decide_tac >- + fs[] >> + `a + 1 <= j` by decide_tac >> + metis_tac[], + metis_tac[LESS_EQ_REFL], + `a <= j` by decide_tac >> + metis_tac[] + ]); + +(* ------------------------------------------------------------------------- *) +(* List Range *) +(* ------------------------------------------------------------------------- *) + val listRangeINC_def = Define` listRangeINC m n = GENLIST (\i. m + i) (n + 1 - m) `; @@ -107,6 +320,777 @@ val EL_listRangeLHI = store_thm( Cases_on `i` THEN SRW_TAC[ARITH_ss][]); +(* Theorem: [m .. n] = [m ..< SUC n] *) +(* Proof: + = [m .. n] + = GENLIST (\i. m + i) (n + 1 - m) by listRangeINC_def + = [m ..< (n + 1)] by listRangeLHI_def + = [m ..< SUC n] by ADD1 +*) +Theorem listRangeINC_to_LHI: + !m n. [m .. n] = [m ..< SUC n] +Proof + rw[listRangeLHI_def, listRangeINC_def, ADD1] +QED + +(* Theorem: set [1 .. n] = IMAGE SUC (count n) *) +(* Proof: + x IN set [1 .. n] + <=> 1 <= x /\ x <= n by listRangeINC_MEM + <=> 0 < x /\ PRE x < n by arithmetic + <=> 0 < SUC (PRE x) /\ PRE x < n by SUC_PRE, 0 < x + <=> x IN IMAGE SUC (count n) by IN_COUNT, IN_IMAGE +*) +Theorem listRangeINC_SET: + !n. set [1 .. n] = IMAGE SUC (count n) +Proof + rw[EXTENSION, EQ_IMP_THM] >> + `0 < x /\ PRE x < n` by decide_tac >> + metis_tac[SUC_PRE] +QED + +(* Theorem: LENGTH [m .. n] = n + 1 - m *) +(* Proof: + LENGTH [m .. n] + = LENGTH (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def + = n + 1 - m by LENGTH_GENLIST +*) +val listRangeINC_LEN = store_thm( + "listRangeINC_LEN", + ``!m n. LENGTH [m .. n] = n + 1 - m``, + rw[listRangeINC_def]); + +(* Theorem: ([m .. n] = []) <=> (n + 1 <= m) *) +(* Proof: + [m .. n] = [] + <=> LENGTH [m .. n] = 0 by LENGTH_NIL + <=> n + 1 - m = 0 by listRangeINC_LEN + <=> n + 1 <= m by arithmetic +*) +val listRangeINC_NIL = store_thm( + "listRangeINC_NIL", + ``!m n. ([m .. n] = []) <=> (n + 1 <= m)``, + metis_tac[listRangeINC_LEN, LENGTH_NIL, DECIDE``(n + 1 - m = 0) <=> (n + 1 <= m)``]); + +(* Rename a theorem *) +val listRangeINC_MEM = save_thm("listRangeINC_MEM", + MEM_listRangeINC |> GEN ``x:num`` |> GEN ``n:num`` |> GEN ``m:num``); +(* +val listRangeINC_MEM = |- !m n x. MEM x [m .. n] <=> m <= x /\ x <= n: thm +*) + +(* +EL_listRangeLHI +|- lo + i < hi ==> EL i [lo ..< hi] = lo + i +*) + +(* Theorem: m + i <= n ==> (EL i [m .. n] = m + i) *) +(* Proof: by listRangeINC_def *) +val listRangeINC_EL = store_thm( + "listRangeINC_EL", + ``!m n i. m + i <= n ==> (EL i [m .. n] = m + i)``, + rw[listRangeINC_def]); + +(* Theorem: EVERY P [m .. n] <=> !x. m <= x /\ x <= n ==> P x *) +(* Proof: + EVERY P [m .. n] + <=> !x. MEM x [m .. n] ==> P x by EVERY_MEM + <=> !x. m <= x /\ x <= n ==> P x by MEM_listRangeINC +*) +val listRangeINC_EVERY = store_thm( + "listRangeINC_EVERY", + ``!P m n. EVERY P [m .. n] <=> !x. m <= x /\ x <= n ==> P x``, + rw[EVERY_MEM, MEM_listRangeINC]); + + +(* Theorem: EXISTS P [m .. n] <=> ?x. m <= x /\ x <= n /\ P x *) +(* Proof: + EXISTS P [m .. n] + <=> ?x. MEM x [m .. n] /\ P x by EXISTS_MEM + <=> ?x. m <= x /\ x <= n /\ P e by MEM_listRangeINC +*) +val listRangeINC_EXISTS = store_thm( + "listRangeINC_EXISTS", + ``!P m n. EXISTS P [m .. n] <=> ?x. m <= x /\ x <= n /\ P x``, + metis_tac[EXISTS_MEM, MEM_listRangeINC]); + +(* Theorem: EVERY P [m .. n] <=> ~(EXISTS ($~ o P) [m .. n]) *) +(* Proof: + EVERY P [m .. n] + <=> !x. m <= x /\ x <= n ==> P x by listRangeINC_EVERY + <=> ~(?x. m <= x /\ x <= n /\ ~(P x)) by negation + <=> ~(EXISTS ($~ o P) [m .. m]) by listRangeINC_EXISTS +*) +val listRangeINC_EVERY_EXISTS = store_thm( + "listRangeINC_EVERY_EXISTS", + ``!P m n. EVERY P [m .. n] <=> ~(EXISTS ($~ o P) [m .. n])``, + rw[listRangeINC_EVERY, listRangeINC_EXISTS]); + +(* Theorem: EXISTS P [m .. n] <=> ~(EVERY ($~ o P) [m .. n]) *) +(* Proof: + EXISTS P [m .. n] + <=> ?x. m <= x /\ x <= m /\ P x by listRangeINC_EXISTS + <=> ~(!x. m <= x /\ x <= n ==> ~(P x)) by negation + <=> ~(EVERY ($~ o P) [m .. n]) by listRangeINC_EVERY +*) +val listRangeINC_EXISTS_EVERY = store_thm( + "listRangeINC_EXISTS_EVERY", + ``!P m n. EXISTS P [m .. n] <=> ~(EVERY ($~ o P) [m .. n])``, + rw[listRangeINC_EXISTS, listRangeINC_EVERY]); + +(* Theorem: m <= n + 1 ==> ([m .. (n + 1)] = SNOC (n + 1) [m .. n]) *) +(* Proof: + [m .. (n + 1)] + = GENLIST (\i. m + i) ((n + 1) + 1 - m) by listRangeINC_def + = GENLIST (\i. m + i) (1 + (n + 1 - m)) by arithmetic + = GENLIST (\i. m + i) (n + 1 - m) ++ GENLIST (\t. (\i. m + i) (t + n + 1 - m)) 1 by GENLIST_APPEND + = [m .. n] ++ GENLIST (\t. (\i. m + i) (t + n + 1 - m)) 1 by listRangeINC_def + = [m .. n] ++ [(\t. (\i. m + i) (t + n + 1 - m)) 0] by GENLIST_1 + = [m .. n] ++ [m + n + 1 - m] by function evaluation + = [m .. n] ++ [n + 1] by arithmetic + = SNOC (n + 1) [m .. n] by SNOC_APPEND +*) +val listRangeINC_SNOC = store_thm( + "listRangeINC_SNOC", + ``!m n. m <= n + 1 ==> ([m .. (n + 1)] = SNOC (n + 1) [m .. n])``, + rw[listRangeINC_def] >> + `(n + 2 - m = 1 + (n + 1 - m)) /\ (n + 1 - m + m = n + 1)` by decide_tac >> + rw_tac std_ss[GENLIST_APPEND, GENLIST_1]); + +(* Theorem: m <= n + 1 ==> (FRONT [m .. (n + 1)] = [m .. n]) *) +(* Proof: + FRONT [m .. (n + 1)] + = FRONT (SNOC (n + 1) [m .. n])) by listRangeINC_SNOC + = [m .. n] by FRONT_SNOC +*) +Theorem listRangeINC_FRONT: + !m n. m <= n + 1 ==> (FRONT [m .. (n + 1)] = [m .. n]) +Proof + simp[listRangeINC_SNOC, FRONT_SNOC] +QED + +(* Theorem: m <= n ==> (LAST [m .. n] = n) *) +(* Proof: + Let ls = [m .. n] + Note ls <> [] by listRangeINC_NIL + so LAST ls + = EL (PRE (LENGTH ls)) ls by LAST_EL + = EL (PRE (n + 1 - m)) ls by listRangeINC_LEN + = EL (n - m) ls by arithmetic + = n by listRangeINC_EL + Or + LAST [m .. n] + = LAST (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def + = LAST (GENLIST (\i. m + i) (SUC (n - m)) by arithmetic, m <= n + = (\i. m + i) (n - m) by GENLIST_LAST + = m + (n - m) by function application + = n by m <= n + Or + If n = 0, then m <= 0 means m = 0. + LAST [0 .. 0] = LAST [0] = 0 = n by LAST_DEF + Otherwise n = SUC k. + LAST [m .. n] + = LAST (SNOC n [m .. k]) by listRangeINC_SNOC, ADD1 + = n by LAST_SNOC +*) +Theorem listRangeINC_LAST: + !m n. m <= n ==> (LAST [m .. n] = n) +Proof + rpt strip_tac >> + Cases_on `n` >- + fs[] >> + metis_tac[listRangeINC_SNOC, LAST_SNOC, ADD1] +QED + +(* Theorem: REVERSE [m .. n] = MAP (\x. n - x + m) [m .. n] *) +(* Proof: + REVERSE [m .. n] + = REVERSE (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def + = GENLIST (\x. (\i. m + i) (PRE (n + 1 - m) - x)) (n + 1 - m) by REVERSE_GENLIST + = GENLIST (\x. (\i. m + i) (n - m - x)) (n + 1 - m) by PRE + = GENLIST (\x. (m + n - m - x) (n + 1 - m) by function application + = GENLIST (\x. n - x) (n + 1 - m) by arithmetic + + MAP (\x. n - x + m) [m .. n] + = MAP (\x. n - x + m) (GENLIST (\i. m + i) (n + 1 - m)) by listRangeINC_def + = GENLIST ((\x. n - x + m) o (\i. m + i)) (n + 1 - m) by MAP_GENLIST + = GENLIST (\i. n - (m + i) + m) (n + 1 - m) by function composition + = GENLIST (\i. n - i) (n + 1 - m) by arithmetic +*) +val listRangeINC_REVERSE = store_thm( + "listRangeINC_REVERSE", + ``!m n. REVERSE [m .. n] = MAP (\x. n - x + m) [m .. n]``, + rpt strip_tac >> + `(\m'. PRE (n + 1 - m) - m' + m) = ((\x. n - x + m) o (\i. i + m))` + by rw[FUN_EQ_THM, combinTheory.o_THM] >> + rw[listRangeINC_def, REVERSE_GENLIST, MAP_GENLIST]); + +(* Theorem: REVERSE (MAP f [m .. n]) = MAP (f o (\x. n - x + m)) [m .. n] *) +(* Proof: + REVERSE (MAP f [m .. n]) + = MAP f (REVERSE [m .. n]) by MAP_REVERSE + = MAP f (MAP (\x. n - x + m) [m .. n]) by listRangeINC_REVERSE + = MAP (f o (\x. n - x + m)) [m .. n] by MAP_MAP_o +*) +val listRangeINC_REVERSE_MAP = store_thm( + "listRangeINC_REVERSE_MAP", + ``!f m n. REVERSE (MAP f [m .. n]) = MAP (f o (\x. n - x + m)) [m .. n]``, + metis_tac[MAP_REVERSE, listRangeINC_REVERSE, MAP_MAP_o]); + +(* Theorem: MAP f [(m + 1) .. (n + 1)] = MAP (f o SUC) [m .. n] *) +(* Proof: + Note (\i. (m + 1) + i) = SUC o (\i. (m + i)) by FUN_EQ_THM + MAP f [(m + 1) .. (n + 1)] + = MAP f (GENLIST (\i. (m + 1) + i) ((n + 1) + 1 - (m + 1))) by listRangeINC_def + = MAP f (GENLIST (\i. (m + 1) + i) (n + 1 - m)) by arithmetic + = MAP f (GENLIST (SUC o (\i. (m + i))) (n + 1 - m)) by above + = MAP (f o SUC) (GENLIST (\i. (m + i)) (n + 1 - m)) by MAP_GENLIST + = MAP (f o SUC) [m .. n] by listRangeINC_def +*) +val listRangeINC_MAP_SUC = store_thm( + "listRangeINC_MAP_SUC", + ``!f m n. MAP f [(m + 1) .. (n + 1)] = MAP (f o SUC) [m .. n]``, + rpt strip_tac >> + `(\i. (m + 1) + i) = SUC o (\i. (m + i))` by rw[FUN_EQ_THM] >> + rw[listRangeINC_def, MAP_GENLIST]); + +(* Theorem: a <= b /\ b <= c ==> ([a .. b] ++ [(b + 1) .. c] = [a .. c]) *) +(* Proof: + By listRangeINC_def, this is to show: + GENLIST (\i. a + i) (b + 1 - a) ++ GENLIST (\i. b + (i + 1)) (c - b) = GENLIST (\i. a + i) (c + 1 - a) + Let f = \i. a + i. + Note (\t. f (t + (b + 1 - a))) = (\i. b + (i + 1)) by FUN_EQ_THM + Thus GENLIST (\i. b + (i + 1)) (c - b) = GENLIST (\t. f (t + (b + 1 - a))) (c - b) by GENLIST_FUN_EQ + Now (c - b) + (b + 1 - a) = c + 1 - a by a <= b /\ b <= c + The result follows by GENLIST_APPEND +*) +val listRangeINC_APPEND = store_thm( + "listRangeINC_APPEND", + ``!a b c. a <= b /\ b <= c ==> ([a .. b] ++ [(b + 1) .. c] = [a .. c])``, + rw[listRangeINC_def] >> + qabbrev_tac `f = \i. a + i` >> + `(\t. f (t + (b + 1 - a))) = (\i. b + (i + 1))` by rw[FUN_EQ_THM, Abbr`f`] >> + `GENLIST (\i. b + (i + 1)) (c - b) = GENLIST (\t. f (t + (b + 1 - a))) (c - b)` by rw[GSYM GENLIST_FUN_EQ] >> + `(c - b) + (b + 1 - a) = c + 1 - a` by decide_tac >> + metis_tac[GENLIST_APPEND]); + +(* Theorem: SUM [m .. n] = SUM [1 .. n] - SUM [1 .. (m - 1)] *) +(* Proof: + If m = 0, + Then [1 .. (m-1)] = [1 .. 0] = [] by listRangeINC_EMPTY + SUM [0 .. n] + = SUM (0::[1 .. n]) by listRangeINC_CONS + = 0 + SUM [1 .. n] by SUM_CONS + = SUM [1 .. n] - 0 by arithmetic + = SUM [1 .. n] - SUM [] by SUM_NIL + If m = 1, + Then [1 .. (m-1)] = [1 .. 0] = [] by listRangeINC_EMPTY + SUM [1 .. n] + = SUM [1 .. n] - 0 by arithmetic + = SUM [1 .. n] - SUM [] by SUM_NIL + Otherwise 1 < m, or 1 <= m - 1. + If n < m, + Then SUM [m .. n] = 0 by listRangeINC_EMPTY + If n = 0, + Then SUM [1 .. 0] = 0 by listRangeINC_EMPTY + and 0 - SUM [1 .. (m - 1)] = 0 by integer subtraction + If n <> 0, + Then 1 <= n /\ n <= m - 1 by arithmetic + ==> [1 .. m - 1] = + [1 .. n] ++ [(n + 1) .. (m - 1)] by listRangeINC_APPEND + or SUM [1 .. m - 1] + = SUM [1 .. n] + SUM [(n + 1) .. (m - 1)] by SUM_APPEND + Thus SUM [1 .. n] - SUM [1 .. m - 1] = 0 by subtraction + If ~(n < m), then m <= n. + Note m - 1 < n /\ (m - 1 + 1 = m) by arithmetic + Thus [1 .. n] = [1 .. (m - 1)] ++ [m .. n] by listRangeINC_APPEND + or SUM [1 .. n] + = SUM [1 .. (m - 1)] + SUM [m .. n] by SUM_APPEND + The result follows by subtraction +*) +val listRangeINC_SUM = store_thm( + "listRangeINC_SUM", + ``!m n. SUM [m .. n] = SUM [1 .. n] - SUM [1 .. (m - 1)]``, + rpt strip_tac >> + Cases_on `m = 0` >- + rw[listRangeINC_EMPTY, listRangeINC_CONS] >> + Cases_on `m = 1` >- + rw[listRangeINC_EMPTY] >> + Cases_on `n < m` >| [ + Cases_on `n = 0` >- + rw[listRangeINC_EMPTY] >> + `1 <= n /\ n <= m - 1` by decide_tac >> + `[1 .. m - 1] = [1 .. n] ++ [(n + 1) .. (m - 1)]` by rw[listRangeINC_APPEND] >> + `SUM [1 .. m - 1] = SUM [1 .. n] + SUM [(n + 1) .. (m - 1)]` by rw[GSYM SUM_APPEND] >> + `SUM [m .. n] = 0` by rw[listRangeINC_EMPTY] >> + decide_tac, + `1 <= m - 1 /\ m - 1 <= n /\ (m - 1 + 1 = m)` by decide_tac >> + `[1 .. n] = [1 .. (m - 1)] ++ [m .. n]` by metis_tac[listRangeINC_APPEND] >> + `SUM [1 .. n] = SUM [1 .. (m - 1)] + SUM [m .. n]` by rw[GSYM SUM_APPEND] >> + decide_tac + ]); + +(* Theorem: [1 .. n] = GENLIST SUC n *) +(* Proof: by listRangeINC_def *) +val listRangeINC_1_n = store_thm( + "listRangeINC_1_n", + ``!n. [1 .. n] = GENLIST SUC n``, + rpt strip_tac >> + `(\i. i + 1) = SUC` by rw[FUN_EQ_THM] >> + rw[listRangeINC_def]); + +(* Theorem: MAP f [1 .. n] = GENLIST (f o SUC) n *) +(* Proof: + MAP f [1 .. n] + = MAP f (GENLIST SUC n) by listRangeINC_1_n + = GENLIST (f o SUC) n by MAP_GENLIST +*) +val listRangeINC_MAP = store_thm( + "listRangeINC_MAP", + ``!f n. MAP f [1 .. n] = GENLIST (f o SUC) n``, + rw[listRangeINC_1_n, MAP_GENLIST]); + +(* Theorem: SUM (MAP f [1 .. (SUC n)]) = f (SUC n) + SUM (MAP f [1 .. n]) *) +(* Proof: + SUM (MAP f [1 .. (SUC n)]) + = SUM (MAP f (SNOC (SUC n) [1 .. n])) by listRangeINC_SNOC + = SUM (SNOC (f (SUC n)) (MAP f [1 .. n])) by MAP_SNOC + = f (SUC n) + SUM (MAP f [1 .. n]) by SUM_SNOC +*) +val listRangeINC_SUM_MAP = store_thm( + "listRangeINC_SUM_MAP", + ``!f n. SUM (MAP f [1 .. (SUC n)]) = f (SUC n) + SUM (MAP f [1 .. n])``, + rw[listRangeINC_SNOC, MAP_SNOC, SUM_SNOC, ADD1]); + +(* Theorem: m < j /\ j <= n ==> [m .. n] = [m .. j-1] ++ j::[j+1 .. n] *) +(* Proof: + Note m < j implies m <= j-1. + [m .. n] + = [m .. j-1] ++ [j .. n] by listRangeINC_APPEND, m <= j-1 + = [m .. j-1] ++ j::[j+1 .. n] by listRangeINC_CONS, j <= n +*) +Theorem listRangeINC_SPLIT: + !m n j. m < j /\ j <= n ==> [m .. n] = [m .. j-1] ++ j::[j+1 .. n] +Proof + rpt strip_tac >> + `m <= j - 1 /\ j - 1 <= n /\ (j - 1) + 1 = j` by decide_tac >> + `[m .. n] = [m .. j-1] ++ [j .. n]` by metis_tac[listRangeINC_APPEND] >> + simp[listRangeINC_CONS] +QED + +(* Theorem: [m ..< (n + 1)] = [m .. n] *) +(* Proof: + [m ..< (n + 1)] + = GENLIST (\i. m + i) (n + 1 - m) by listRangeLHI_def + = [m .. n] by listRangeINC_def +*) +Theorem listRangeLHI_to_INC: + !m n. [m ..< (n + 1)] = [m .. n] +Proof + rw[listRangeLHI_def, listRangeINC_def] +QED + +(* Theorem: set [0 ..< n] = count n *) +(* Proof: + x IN set [0 ..< n] + <=> 0 <= x /\ x < n by listRangeLHI_MEM + <=> x < n by arithmetic + <=> x IN count n by IN_COUNT +*) +Theorem listRangeLHI_SET: + !n. set [0 ..< n] = count n +Proof + simp[EXTENSION] +QED + +(* Theorem alias *) +Theorem listRangeLHI_LEN = LENGTH_listRangeLHI |> GEN_ALL |> SPEC ``m:num`` |> SPEC ``n:num`` |> GEN_ALL; +(* val listRangeLHI_LEN = |- !n m. LENGTH [m ..< n] = n - m: thm *) + +(* Theorem: ([m ..< n] = []) <=> n <= m *) +(* Proof: + If n = 0, LHS = T, RHS = T hence true. + If n <> 0, then n = SUC k by num_CASES + [m ..< n] = [] + <=> [m ..< SUC k] = [] by n = SUC k + <=> [m .. k] = [] by listRangeLHI_to_INC + <=> k + 1 <= m by listRangeINC_NIL + <=> n <= m by ADD1 +*) +val listRangeLHI_NIL = store_thm( + "listRangeLHI_NIL", + ``!m n. ([m ..< n] = []) <=> n <= m``, + rpt strip_tac >> + Cases_on `n` >- + rw[listRangeLHI_def] >> + rw[listRangeLHI_to_INC, listRangeINC_NIL, ADD1]); + +(* Theorem: MEM x [m ..< n] <=> m <= x /\ x < n *) +(* Proof: by MEM_listRangeLHI *) +val listRangeLHI_MEM = store_thm( + "listRangeLHI_MEM", + ``!m n x. MEM x [m ..< n] <=> m <= x /\ x < n``, + rw[MEM_listRangeLHI]); + +(* Theorem: m + i < n ==> EL i [m ..< n] = m + i *) +(* Proof: EL_listRangeLHI *) +val listRangeLHI_EL = store_thm( + "listRangeLHI_EL", + ``!m n i. m + i < n ==> (EL i [m ..< n] = m + i)``, + rw[EL_listRangeLHI]); + +(* Theorem: EVERY P [m ..< n] <=> !x. m <= x /\ x < n ==> P x *) +(* Proof: + EVERY P [m ..< n] + <=> !x. MEM x [m ..< n] ==> P e by EVERY_MEM + <=> !x. m <= x /\ x < n ==> P e by MEM_listRangeLHI +*) +val listRangeLHI_EVERY = store_thm( + "listRangeLHI_EVERY", + ``!P m n. EVERY P [m ..< n] <=> !x. m <= x /\ x < n ==> P x``, + rw[EVERY_MEM, MEM_listRangeLHI]); + +(* Theorem: m <= n ==> ([m ..< n + 1] = SNOC n [m ..< n]) *) +(* Proof: + If n = 0, + Then m = 0 by m <= n + LHS = [0 ..< 1] = [0] + RHS = SNOC 0 [0 ..< 0] + = SNOC 0 [] by listRangeLHI_def + = [0] = LHS by SNOC + If n <> 0, + Then n = (n - 1) + 1 by arithmetic + [m ..< n + 1] + = [m .. n] by listRangeLHI_to_INC + = SNOC n [m .. n - 1] by listRangeINC_SNOC, m <= (n - 1) + 1 + = SNOC n [m ..< n] by listRangeLHI_to_INC +*) +val listRangeLHI_SNOC = store_thm( + "listRangeLHI_SNOC", + ``!m n. m <= n ==> ([m ..< n + 1] = SNOC n [m ..< n])``, + rpt strip_tac >> + Cases_on `n = 0` >| [ + `m = 0` by decide_tac >> + rw[listRangeLHI_def], + `n = (n - 1) + 1` by decide_tac >> + `[m ..< n + 1] = [m .. n]` by rw[listRangeLHI_to_INC] >> + `_ = SNOC n [m .. n - 1]` by metis_tac[listRangeINC_SNOC] >> + `_ = SNOC n [m ..< n]` by rw[GSYM listRangeLHI_to_INC] >> + rw[] + ]); + +(* Theorem: m <= n ==> (FRONT [m .. < n + 1] = [m .. (FRONT [m ..< n + 1] = [m ..< n]) +Proof + simp[listRangeLHI_SNOC, FRONT_SNOC] +QED + +(* Theorem: m <= n ==> (LAST [m ..< n + 1] = n) *) +(* Proof: + LAST [m ..< n + 1] + = LAST (SNOC n [m ..< n]) by listRangeLHI_SNOC + = n by LAST_SNOC +*) +Theorem listRangeLHI_LAST: + !m n. m <= n ==> (LAST [m ..< n + 1] = n) +Proof + simp[listRangeLHI_SNOC, LAST_SNOC] +QED + +(* Theorem: REVERSE [m ..< n] = MAP (\x. n - 1 - x + m) [m ..< n] *) +(* Proof: + If n = 0, + LHS = REVERSE [] by listRangeLHI_def + = [] by REVERSE_DEF + = MAP f [] = RHS by MAP + If n <> 0, + Then n = k + 1 for some k by num_CASES, ADD1 + REVERSE [m ..< n] + = REVERSE [m .. k] by listRangeLHI_to_INC + = MAP (\x. k - x + m) [m .. k] by listRangeINC_REVERSE + = MAP (\x. n - 1 - x + m) [m ..< n] by listRangeLHI_to_INC +*) +val listRangeLHI_REVERSE = store_thm( + "listRangeLHI_REVERSE", + ``!m n. REVERSE [m ..< n] = MAP (\x. n - 1 - x + m) [m ..< n]``, + rpt strip_tac >> + Cases_on `n` >- + rw[listRangeLHI_def] >> + `REVERSE [m ..< SUC n'] = REVERSE [m .. n']` by rw[listRangeLHI_to_INC, ADD1] >> + `_ = MAP (\x. n' - x + m) [m .. n']` by rw[listRangeINC_REVERSE] >> + `_ = MAP (\x. n' - x + m) [m ..< (SUC n')]` by rw[GSYM listRangeLHI_to_INC, ADD1] >> + rw[]); + +(* Theorem: REVERSE (MAP f [m ..< n]) = MAP (f o (\x. n - 1 - x + m)) [m ..< n] *) +(* Proof: + REVERSE (MAP f [m ..< n]) + = MAP f (REVERSE [m ..< n]) by MAP_REVERSE + = MAP f (MAP (\x. n - 1 - x + m) [m ..< n]) by listRangeLHI_REVERSE + = MAP (f o (\x. n - 1 - x + m)) [m ..< n] by MAP_MAP_o +*) +val listRangeLHI_REVERSE_MAP = store_thm( + "listRangeLHI_REVERSE_MAP", + ``!f m n. REVERSE (MAP f [m ..< n]) = MAP (f o (\x. n - 1 - x + m)) [m ..< n]``, + metis_tac[MAP_REVERSE, listRangeLHI_REVERSE, MAP_MAP_o]); + +(* Theorem: MAP f [(m + 1) ..< (n + 1)] = MAP (f o SUC) [m ..< n] *) +(* Proof: + Note (\i. (m + 1) + i) = SUC o (\i. (m + i)) by FUN_EQ_THM + MAP f [(m + 1) ..< (n + 1)] + = MAP f (GENLIST (\i. (m + 1) + i) ((n + 1) - (m + 1))) by listRangeLHI_def + = MAP f (GENLIST (\i. (m + 1) + i) (n - m)) by arithmetic + = MAP f (GENLIST (SUC o (\i. (m + i))) (n - m)) by above + = MAP (f o SUC) (GENLIST (\i. (m + i)) (n - m)) by MAP_GENLIST + = MAP (f o SUC) [m ..< n] by listRangeLHI_def +*) +val listRangeLHI_MAP_SUC = store_thm( + "listRangeLHI_MAP_SUC", + ``!f m n. MAP f [(m + 1) ..< (n + 1)] = MAP (f o SUC) [m ..< n]``, + rpt strip_tac >> + `(\i. (m + 1) + i) = SUC o (\i. (m + i))` by rw[FUN_EQ_THM] >> + rw[listRangeLHI_def, MAP_GENLIST]); + +(* Theorem: a <= b /\ b <= c ==> [a ..< b] ++ [b ..< c] = [a ..< c] *) +(* Proof: + If a = b, + LHS = [a ..< a] ++ [a ..< c] + = [] ++ [a ..< c] by listRangeLHI_def + = [a ..< c] = RHS by APPEND + If a <> b, + Then a < b, by a <= b + so b <> 0, and c <> 0 by b <= c + Let b = b' + 1, c = c' + 1 by num_CASES, ADD1 + Then a <= b' /\ b' <= c. + [a ..< b] ++ [b ..< c] + = [a .. b'] ++ [b' + 1 .. c'] by listRangeLHI_to_INC + = [a .. c'] by listRangeINC_APPEND + = [a ..< c] by listRangeLHI_to_INC +*) +val listRangeLHI_APPEND = store_thm( + "listRangeLHI_APPEND", + ``!a b c. a <= b /\ b <= c ==> ([a ..< b] ++ [b ..< c] = [a ..< c])``, + rpt strip_tac >> + `(a = b) \/ (a < b)` by decide_tac >- + rw[listRangeLHI_def] >> + `b <> 0 /\ c <> 0` by decide_tac >> + `?b' c'. (b = b' + 1) /\ (c = c' + 1)` by metis_tac[num_CASES, ADD1] >> + `a <= b' /\ b' <= c` by decide_tac >> + `[a ..< b] ++ [b ..< c] = [a .. b'] ++ [b' + 1 .. c']` by rw[listRangeLHI_to_INC] >> + `_ = [a .. c']` by rw[listRangeINC_APPEND] >> + `_ = [a ..< c]` by rw[GSYM listRangeLHI_to_INC] >> + rw[]); + +(* Theorem: SUM [m ..< n] = SUM [1 ..< n] - SUM [1 ..< m] *) +(* Proof: + If n = 0, + LHS = SUM [m ..< 0] = SUM [] = 0 by listRangeLHI_EMPTY + RHS = SUM [1 ..< 0] - SUM [1 ..< m] + = SUM [] - SUM [1 ..< m] by listRangeLHI_EMPTY + = 0 - SUM [1 ..< m] = 0 = LHS by integer subtraction + If m = 0, + LHS = SUM [0 ..< n] + = SUM (0 :: [1 ..< n]) by listRangeLHI_CONS + = 0 + SUM [1 ..< n] by SUM + = SUM [1 ..< n] by arithmetic + RHS = SUM [1 ..< n] - SUM [1 ..< 0] by integer subtraction + = SUM [1 ..< n] - SUM [] by listRangeLHI_EMPTY + = SUM [1 ..< n] - 0 by SUM + = LHS + Otherwise, + n <> 0, and m <> 0. + Let n = n' + 1, m = m' + 1 by num_CASES, ADD1 + SUM [m ..< n] + = SUM [m .. n'] by listRangeLHI_to_INC + = SUM [1 .. n'] - SUM [1 .. m - 1] by listRangeINC_SUM + = SUM [1 .. n'] - SUM [1 .. m'] by m' = m - 1 + = SUM [1 ..< n] - SUM [1 ..< m] by listRangeLHI_to_INC +*) +val listRangeLHI_SUM = store_thm( + "listRangeLHI_SUM", + ``!m n. SUM [m ..< n] = SUM [1 ..< n] - SUM [1 ..< m]``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[listRangeLHI_EMPTY] >> + Cases_on `m = 0` >- + rw[listRangeLHI_EMPTY, listRangeLHI_CONS] >> + `?n' m'. (n = n' + 1) /\ (m = m' + 1)` by metis_tac[num_CASES, ADD1] >> + `SUM [m ..< n] = SUM [m .. n']` by rw[listRangeLHI_to_INC] >> + `_ = SUM [1 .. n'] - SUM [1 .. m - 1]` by rw[GSYM listRangeINC_SUM] >> + `_ = SUM [1 .. n'] - SUM [1 .. m']` by rw[] >> + `_ = SUM [1 ..< n] - SUM [1 ..< m]` by rw[GSYM listRangeLHI_to_INC] >> + rw[]); + +(* Theorem: [0 ..< n] = GENLIST I n *) +(* Proof: by listRangeINC_def *) +val listRangeLHI_0_n = store_thm( + "listRangeLHI_0_n", + ``!n. [0 ..< n] = GENLIST I n``, + rpt strip_tac >> + `(\i:num. i) = I` by rw[FUN_EQ_THM] >> + rw[listRangeLHI_def]); + +(* Theorem: MAP f [0 ..< n] = GENLIST f n *) +(* Proof: + MAP f [0 ..< n] + = MAP f (GENLIST I n) by listRangeLHI_0_n + = GENLIST (f o I) n by MAP_GENLIST + = GENLIST f n by I_THM +*) +val listRangeLHI_MAP = store_thm( + "listRangeLHI_MAP", + ``!f n. MAP f [0 ..< n] = GENLIST f n``, + rw[listRangeLHI_0_n, MAP_GENLIST]); + +(* Theorem: SUM (MAP f [0 ..< (SUC n)]) = f n + SUM (MAP f [0 ..< n]) *) +(* Proof: + SUM (MAP f [0 ..< (SUC n)]) + = SUM (MAP f (SNOC n [0 ..< n])) by listRangeLHI_SNOC + = SUM (SNOC (f n) (MAP f [0 ..< n])) by MAP_SNOC + = f n + SUM (MAP f [0 ..< n]) by SUM_SNOC +*) +val listRangeLHI_SUM_MAP = store_thm( + "listRangeLHI_SUM_MAP", + ``!f n. SUM (MAP f [0 ..< (SUC n)]) = f n + SUM (MAP f [0 ..< n])``, + rw[listRangeLHI_SNOC, MAP_SNOC, SUM_SNOC, ADD1]); + +(* Theorem: m <= j /\ j < n ==> [m ..< n] = [m ..< j] ++ j::[j+1 ..< n] *) +(* Proof: + Note j < n implies j <= n. + [m ..< n] + = [m ..< j] ++ [j ..< n] by listRangeLHI_APPEND, j <= n + = [m ..< j] ++ j::[j+1 ..< n] by listRangeLHI_CONS, j < n +*) +Theorem listRangeLHI_SPLIT: + !m n j. m <= j /\ j < n ==> [m ..< n] = [m ..< j] ++ j::[j+1 ..< n] +Proof + rpt strip_tac >> + `[m ..< n] = [m ..< j] ++ [j ..< n]` by simp[listRangeLHI_APPEND] >> + simp[listRangeLHI_CONS] +QED + +(* listRangeTheory.listRangeLHI_ALL_DISTINCT |- ALL_DISTINCT [lo ..< hi] *) + +(* Theorem: ALL_DISTINCT [m .. n] *) +(* Proof: + ALL_DISTINCT [m .. n] + <=> ALL_DISTINCT [m ..< n + 1] by listRangeLHI_to_INC + <=> T by listRangeLHI_ALL_DISTINCT +*) +Theorem listRangeINC_ALL_DISTINCT: + !m n. ALL_DISTINCT [m .. n] +Proof + metis_tac[listRangeLHI_to_INC, listRangeLHI_ALL_DISTINCT] +QED + +(* Theorem: m <= n ==> EVERY P [m - 1 .. n] <=> (P (m - 1) /\ EVERY P [m ..n]) *) +(* Proof: + EVERY P [m - 1 .. n] + <=> !x. m - 1 <= x /\ x <= n ==> P x by listRangeINC_EVERY + <=> !x. (m - 1 = x \/ m <= x) /\ x <= n ==> P x by arithmetic + <=> !x. (x = m - 1 ==> P x) /\ m <= x /\ x <= n ==> P x + by RIGHT_AND_OVER_OR, DISJ_IMP_THM + <=> P (m - 1) /\ EVERY P [m .. n] by listRangeINC_EVERY +*) +Theorem listRangeINC_EVERY_split_head: + !P m n. m <= n ==> (EVERY P [m - 1 .. n] <=> P (m - 1) /\ EVERY P [m ..n]) +Proof + rw[listRangeINC_EVERY] >> + `!x. m <= x + 1 <=> m - 1 = x \/ m <= x` by decide_tac >> + (rw[EQ_IMP_THM] >> metis_tac[]) +QED + +(* Theorem: m <= n ==> (EVERY P [m .. (n + 1)] <=> P (n + 1) /\ EVERY P [m .. n]) *) +(* Proof: + EVERY P [m .. (n + 1)] + <=> !x. m <= x /\ x <= n + 1 ==> P x by listRangeINC_EVERY + <=> !x. m <= x /\ (x <= n \/ x = n + 1) ==> P x by arithmetic + <=> !x. m <= x /\ x <= n ==> P x /\ P (n + 1) by LEFT_AND_OVER_OR, DISJ_IMP_THM + <=> P (n + 1) /\ EVERY P [m .. n] by listRangeINC_EVERY +*) +Theorem listRangeINC_EVERY_split_last: + !P m n. m <= n ==> (EVERY P [m .. (n + 1)] <=> P (n + 1) /\ EVERY P [m .. n]) +Proof + rw[listRangeINC_EVERY] >> + `!x. x <= n + 1 <=> x <= n \/ x = n + 1` by decide_tac >> + metis_tac[] +QED + +(* Theorem: m <= n ==> (EVERY P [m .. n] <=> P n /\ EVERY P [m ..< n]) *) +(* Proof: + EVERY P [m .. n] + <=> !x. m <= x /\ x <= n ==> P x by listRangeINC_EVERY + <=> !x. m <= x /\ (x < n \/ x = n) ==> P x by arithmetic + <=> !x. m <= x /\ x < n ==> P x /\ P n by LEFT_AND_OVER_OR, DISJ_IMP_THM + <=> P n /\ EVERY P [m ..< n] by listRangeLHI_EVERY +*) +Theorem listRangeINC_EVERY_less_last: + !P m n. m <= n ==> (EVERY P [m .. n] <=> P n /\ EVERY P [m ..< n]) +Proof + rw[listRangeINC_EVERY, listRangeLHI_EVERY] >> + `!x. x <= n <=> x < n \/ x = n` by decide_tac >> + metis_tac[] +QED + +(* Theorem: m < n /\ P m /\ ~P n ==> + ?k. m <= k /\ k < n /\ EVERY P [m .. k] /\ ~P (SUC k) *) +(* Proof: + m < n /\ P m /\ ~P n + ==> ?k. m <= k /\ k < m /\ + (!j. m <= j /\ j <= k ==> P j) /\ ~P (SUC k) by every_range_span_max + ==> ?k. m <= k /\ k < m /\ + EVERY P [m .. k] /\ ~P (SUC k) by listRangeINC_EVERY +*) +Theorem listRangeINC_EVERY_span_max: + !P m n. m < n /\ P m /\ ~P n ==> + ?k. m <= k /\ k < n /\ EVERY P [m .. k] /\ ~P (SUC k) +Proof + simp[listRangeINC_EVERY, every_range_span_max] +QED + +(* Theorem: m < n /\ ~P m /\ P n ==> + ?k. m < k /\ k <= n /\ EVERY P [k .. n] /\ ~P (PRE k) *) +(* Proof: + m < n /\ P m /\ ~P n + ==> ?k. m < k /\ k <= n /\ + (!j. k <= j /\ j <= n ==> P j) /\ ~P (PRE k) by every_range_span_min + ==> ?k. m < k /\ k <= n /\ + EVERY P [k .. n] /\ ~P (PRE k) by listRangeINC_EVERY +*) +Theorem listRangeINC_EVERY_span_min: + !P m n. m < n /\ ~P m /\ P n ==> + ?k. m < k /\ k <= n /\ EVERY P [k .. n] /\ ~P (PRE k) +Proof + simp[listRangeINC_EVERY, every_range_span_min] +QED + +(* temporarily make divides an infix *) +val _ = temp_set_fixity "divides" (Infixl 480); + +(* Theorem: 0 < n /\ m <= x /\ x divides n ==> MEM x [m .. n] *) +(* Proof: + Note x divdes n ==> x <= n by DIVIDES_LE, 0 < n + so MEM x [m .. n] by listRangeINC_MEM +*) +val listRangeINC_has_divisors = store_thm( + "listRangeINC_has_divisors", + ``!m n x. 0 < n /\ m <= x /\ x divides n ==> MEM x [m .. n]``, + rw[listRangeINC_MEM, DIVIDES_LE]); +(* Theorem: 0 < n /\ m <= x /\ x divides n ==> MEM x [m ..< n + 1] *) +(* Proof: + Note the condition implies: + MEM x [m .. n] by listRangeINC_has_divisors + = MEM x [m ..< n + 1] by listRangeLHI_to_INC +*) +val listRangeLHI_has_divisors = store_thm( + "listRangeLHI_has_divisors", + ``!m n x. 0 < n /\ m <= x /\ x divides n ==> MEM x [m ..< n + 1]``, + metis_tac[listRangeINC_has_divisors, listRangeLHI_to_INC]); val _ = export_theory(); diff --git a/src/list/src/listScript.sml b/src/list/src/listScript.sml index 14602cf8cc..71569a4c45 100644 --- a/src/list/src/listScript.sml +++ b/src/list/src/listScript.sml @@ -16,36 +16,29 @@ (* DATE : September 15, 1991 *) (* ===================================================================== *) +open HolKernel Parse boolLib BasicProvers; -(* ---------------------------------------------------------------------- - Require ancestor theory structures to be present. The parents of list - are "arithmetic", "pair", "pred_set" and those theories behind the - datatype definition library - ---------------------------------------------------------------------- *) - -local - open arithmeticTheory pairTheory pred_setTheory Datatype - OpenTheoryMap -in end; - - -(*--------------------------------------------------------------------------- - * Open structures used in the body. - *---------------------------------------------------------------------------*) - -open HolKernel Parse boolLib Num_conv BasicProvers mesonLib +open Num_conv mesonLib arithmeticTheory simpLib boolSimps pairTheory pred_setTheory TotalDefn metisLib relationTheory combinTheory quotientLib +local open pairTheory pred_setTheory Datatype OpenTheoryMap +in end; + val ERR = mk_HOL_ERR "listScript" val arith_ss = bool_ss ++ numSimps.ARITH_ss ++ numSimps.REDUCE_ss fun simp l = ASM_SIMP_TAC (srw_ss()++boolSimps.LET_ss++numSimps.ARITH_ss) l - val rw = SRW_TAC [] val metis_tac = METIS_TAC fun fs l = FULL_SIMP_TAC (srw_ss()) l +val std_ss = arith_ss ++ boolSimps.LET_ss; +fun DECIDE_TAC (g as (asl,_)) = + ((MAP_EVERY UNDISCH_TAC (filter numSimps.is_arith asl) THEN + CONV_TAC Arith.ARITH_CONV) + ORELSE tautLib.TAUT_TAC) g; +val decide_tac = DECIDE_TAC; val _ = new_theory "list"; @@ -64,17 +57,6 @@ val PRE = prim_recTheory.PRE; val LESS_MONO = prim_recTheory.LESS_MONO; val INV_SUC_EQ = prim_recTheory.INV_SUC_EQ; val num_Axiom = prim_recTheory.num_Axiom; - -val ADD_CLAUSES = arithmeticTheory.ADD_CLAUSES; -val LESS_ADD_1 = arithmeticTheory.LESS_ADD_1; -val LESS_EQ = arithmeticTheory.LESS_EQ; -val NOT_LESS = arithmeticTheory.NOT_LESS; -val LESS_EQ_ADD = arithmeticTheory.LESS_EQ_ADD; -val num_CASES = arithmeticTheory.num_CASES; -val LESS_MONO_EQ = arithmeticTheory.LESS_MONO_EQ; -val LESS_MONO_EQ = arithmeticTheory.LESS_MONO_EQ; -val ADD_EQ_0 = arithmeticTheory.ADD_EQ_0; -val ONE = arithmeticTheory.ONE; val PAIR_EQ = pairTheory.PAIR_EQ; (*---------------------------------------------------------------------------*) @@ -455,6 +437,19 @@ val EQ_LIST = store_thm("EQ_LIST", REPEAT STRIP_TAC THEN ASM_REWRITE_TAC [CONS_11]); +(* Theorem: ls <> [] <=> (ls = HD ls::TL ls) *) +(* Proof: + If part: ls <> [] ==> (ls = HD ls::TL ls) + ls <> [] + ==> ?h t. ls = h::t by list_CASES + ==> ls = (HD ls)::(TL ls) by HD, TL + Only-if part: (ls = HD ls::TL ls) ==> ls <> [] + This is true by NOT_NIL_CONS +*) +val LIST_NOT_NIL = store_thm( + "LIST_NOT_NIL", + ``!ls. ls <> [] <=> (ls = HD ls::TL ls)``, + metis_tac[list_CASES, HD, TL, NOT_NIL_CONS]); Theorem CONS: !l : 'a list. ~NULL l ==> HD l :: TL l = l @@ -548,6 +543,10 @@ val MAP_MAP_o = store_thm("MAP_MAP_o", REPEAT GEN_TAC THEN REWRITE_TAC [MAP_o, o_DEF] THEN BETA_TAC THEN REFL_TAC); +(* Theorem alias *) +val MAP_COMPOSE = save_thm("MAP_COMPOSE", MAP_MAP_o); +(* val MAP_COMPOSE = |- !f g l. MAP f (MAP g l) = MAP (f o g) l: thm *) + val EL_MAP = store_thm("EL_MAP", (“!n l. n < (LENGTH l) ==> !f:'a->'b. EL n (MAP f l) = f (EL n l)”), INDUCT_TAC THEN LIST_INDUCT_TAC @@ -742,12 +741,53 @@ val LENGTH_NIL = store_thm("LENGTH_NIL[simp]", LIST_INDUCT_TAC THEN REWRITE_TAC [LENGTH, NOT_SUC, NOT_CONS_NIL]); +(* Note: There is LENGTH_NIL, but no LENGTH_NON_NIL *) + +(* Theorem: 0 < LENGTH l <=> l <> [] *) +(* Proof: + Since (LENGTH l = 0) <=> (l = []) by LENGTH_NIL + l <> [] <=> LENGTH l <> 0, + or 0 < LENGTH l by NOT_ZERO_LT_ZERO +*) +val LENGTH_NON_NIL = store_thm( + "LENGTH_NON_NIL", + ``!l. 0 < LENGTH l <=> l <> []``, + metis_tac[LENGTH_NIL, NOT_ZERO_LT_ZERO]); + +(* val LENGTH_EQ_0 = save_thm("LENGTH_EQ_0", LENGTH_EQ_NUM |> CONJUNCT1); *) +val LENGTH_EQ_0 = save_thm("LENGTH_EQ_0", LENGTH_NIL); +(* > val LENGTH_EQ_0 = |- !l. (LENGTH l = 0) <=> (l = []): thm *) + Theorem LENGTH1 : (1 = LENGTH l) <=> ?e. l = [e] Proof Cases_on `l` >> srw_tac [][LENGTH_NIL] QED +(* Theorem: (LENGTH l = 1) <=> ?x. l = [x] *) +(* Proof: + If part: (LENGTH l = 1) ==> ?x. l = [x] + Since LENGTH l <> 0, l <> [] by LENGTH_NIL + or ?h t. l = h::t by list_CASES + and LENGTH t = 0 by LENGTH + so t = [] by LENGTH_NIL + Hence l = [x] + Only-if part: (l = [x]) ==> (LENGTH l = 1) + True by LENGTH. +*) +val LENGTH_EQ_1 = store_thm( + "LENGTH_EQ_1", + ``!l. (LENGTH l = 1) <=> ?x. l = [x]``, + rw [GSYM LENGTH1] +(*rw[EQ_IMP_THM] >| [ + `LENGTH l <> 0` by decide_tac >> + `?h t. l = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + `SUC (LENGTH t) = 1` by metis_tac[LENGTH] >> + `LENGTH t = 0` by decide_tac >> + metis_tac[LENGTH_NIL], + rw[] + ]*)); + Theorem LENGTH2 : (2 = LENGTH l) <=> ?a b. l = [a;b] Proof @@ -1796,7 +1836,6 @@ val FILTER_REVERSE = store_thm( ASM_SIMP_TAC bool_ss [FILTER, REVERSE_DEF, FILTER_APPEND_DISTRIB, COND_RAND, COND_RATOR, APPEND_NIL]); - (* ---------------------------------------------------------------------- FRONT and LAST ---------------------------------------------------------------------- *) @@ -2664,6 +2703,11 @@ val SNOC = new_recursive_definition { }; val _ = BasicProvers.export_rewrites ["SNOC"] +val SNOC_NIL = save_thm("SNOC_NIL", SNOC |> CONJUNCT1); +(* > val SNOC_NIL = |- !x. SNOC x [] = [x]: thm *) +val SNOC_CONS = save_thm("SNOC_CONS", SNOC |> CONJUNCT2); +(* > val SNOC_CONS = |- !x x' l. SNOC x (x'::l) = x'::SNOC x l: thm *) + val LENGTH_SNOC = store_thm( "LENGTH_SNOC", “!(x:'a) l. LENGTH (SNOC x l) = SUC (LENGTH l)”, @@ -2694,8 +2738,12 @@ val SNOC_APPEND = store_thm("SNOC_APPEND", “!x (l:('a) list). SNOC x l = APPEND l [x]”, GEN_TAC THEN LIST_INDUCT_TAC THEN ASM_REWRITE_TAC [SNOC, APPEND]); -(* |- !l. l <> [] ==> SNOC (LAST l) (FRONT l) = l *) -Theorem SNOC_LAST_FRONT = +(* |- !l. l <> [] ==> SNOC (LAST l) (FRONT l) = l + + NOTE: was called "SNOC_LAST_FRONT", renamed due to name conflicts with + "examples/algebra" (cf. rich_listTheory.SNOC_LAST_FRONT) + *) +Theorem SNOC_OF_LAST_FRONT = REWRITE_RULE [GSYM SNOC_APPEND] APPEND_FRONT_LAST val LIST_TO_SET_SNOC = Q.store_thm("LIST_TO_SET_SNOC", @@ -2837,6 +2885,13 @@ Proof Induct_on ‘l’ >> rw [SNOC, isPREFIX] QED +local val REVERSE = REVERSE_SNOC_DEF +in +val MAP_REVERSE = Q.store_thm ("MAP_REVERSE", + `!f l. MAP f (REVERSE l) = REVERSE (MAP f l)`, + GEN_TAC THEN LIST_INDUCT_TAC THEN ASM_REWRITE_TAC [REVERSE, MAP, MAP_SNOC]); +end; + (*--------------------------------------------------------------*) (* List generator *) (* GENLIST f n = [f 0;...; f(n-1)] *) @@ -2975,6 +3030,26 @@ val GENLIST_NUMERALS = store_thm( REWRITE_TAC [GENLIST_GENLIST_AUX, GENLIST_AUX]); val _ = export_rewrites ["GENLIST_NUMERALS"] +(* Theorem: GENLIST f 0 = [] *) +(* Proof: by GENLIST *) +val GENLIST_0 = store_thm( + "GENLIST_0", + ``!f. GENLIST f 0 = []``, + rw[]); + +(* Theorem: GENLIST f 1 = [f 0] *) +(* Proof: + GENLIST f 1 + = GENLIST f (SUC 0) by ONE + = SNOC (f 0) (GENLIST f 0) by GENLIST + = SNOC (f 0) [] by GENLIST + = [f 0] by SNOC +*) +val GENLIST_1 = store_thm( + "GENLIST_1", + ``!f. GENLIST f 1 = [f 0]``, + rw[]); + val MEM_GENLIST = Q.store_thm( "MEM_GENLIST", ‘MEM x (GENLIST f n) <=> ?m. m < n /\ (x = f m)’, @@ -3030,6 +3105,14 @@ Proof simp[GENLIST_CONS] QED +(* Theorem alias *) +Theorem GENLIST_EQ = + GENLIST_CONG |> GEN ``n:num`` |> GEN ``f2:num -> 'a`` + |> GEN ``f1:num -> 'a``; +(* +val GENLIST_EQ = |- !f1 f2 n. (!m. m < n ==> f1 m = f2 m) ==> GENLIST f1 n = GENLIST f2 n: thm +*) + Theorem LIST_REL_O: !R1 R2. LIST_REL (R1 O R2) = LIST_REL R1 O LIST_REL R2 Proof @@ -3042,6 +3125,67 @@ Proof >- metis_tac[] QED +(* Theorem: (GENLIST f n = []) <=> (n = 0) *) +(* Proof: + If part: GENLIST f n = [] ==> n = 0 + By contradiction, suppose n <> 0. + Then LENGTH (GENLIST f n) = n <> 0 by LENGTH_GENLIST + This contradicts LENGTH [] = 0. + Only-if part: GENLIST f 0 = [], true by GENLIST_0 +*) +val GENLIST_EQ_NIL = store_thm( + "GENLIST_EQ_NIL", + ``!f n. (GENLIST f n = []) <=> (n = 0)``, + rw[EQ_IMP_THM] >> + metis_tac[LENGTH_GENLIST, LENGTH_NIL]); + +(* Theorem: LAST (GENLIST f (SUC n)) = f n *) +(* Proof: + LAST (GENLIST f (SUC n)) + = LAST (SNOC (f n) (GENLIST f n)) by GENLIST + = f n by LAST_SNOC +*) +val GENLIST_LAST = store_thm( + "GENLIST_LAST", + ``!f n. LAST (GENLIST f (SUC n)) = f n``, + rw[GENLIST]); + +(* Note: + +- EVERY_MAP; +> val it = |- !P f l. EVERY P (MAP f l) <=> EVERY (\x. P (f x)) l : thm +- EVERY_GENLIST; +> val it = |- !n. EVERY P (GENLIST f n) <=> !i. i < n ==> P (f i) : thm +- MAP_GENLIST; +> val it = |- !f g n. MAP f (GENLIST g n) = GENLIST (f o g) n : thm +*) + +(* Note: the following can use EVERY_GENLIST. *) + +(* Theorem: !k. (k < n ==> f k = c) <=> EVERY (\x. x = c) (GENLIST f n) *) +(* Proof: by induction on n. + Base case: !c. (!k. k < 0 ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f 0) + Since GENLIST f 0 = [], this is true as no k < 0. + Step case: (!k. k < n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f n) ==> + (!k. k < SUC n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f (SUC n)) + EVERY (\x. x = c) (GENLIST f (SUC n)) + <=> EVERY (\x. x = c) (SNOC (f n) (GENLIST f n)) by GENLIST + <=> EVERY (\x. x = c) (GENLIST f n) /\ (f n = c) by EVERY_SNOC + <=> (!k. k < n ==> (f k = c)) /\ (f n = c) by induction hypothesis + <=> !k. k < SUC n ==> (f k = c) +*) +val GENLIST_CONSTANT = store_thm( + "GENLIST_CONSTANT", + ``!f n c. (!k. k < n ==> (f k = c)) <=> EVERY (\x. x = c) (GENLIST f n)``, + strip_tac >> + Induct_on ‘n’ >- + rw[] >> + rw_tac std_ss[EVERY_DEF, GENLIST, EVERY_SNOC, EQ_IMP_THM] >- + metis_tac[prim_recTheory.LESS_SUC] >> + Cases_on `k = n` >- + rw_tac std_ss[] >> + metis_tac[prim_recTheory.LESS_THM]); + (* ---------------------------------------------------------------------- *) val FOLDL_SNOC = store_thm("FOLDL_SNOC", diff --git a/src/list/src/rich_listScript.sml b/src/list/src/rich_listScript.sml index 983441f5a5..e3e04b93b7 100644 --- a/src/list/src/rich_listScript.sml +++ b/src/list/src/rich_listScript.sml @@ -1,12 +1,18 @@ -open HolKernel Parse BasicProvers boolLib numLib metisLib simpLib -open combinTheory arithmeticTheory prim_recTheory pred_setTheory -local open listTheory listSimps pred_setSimps TotalDefn in end +(* ===================================================================== *) +(* FILE : rich_listScript.sml *) +(* DESCRIPTION : Enriched Theory of Lists *) +(* ===================================================================== *) + +open HolKernel Parse boolLib BasicProvers; + +open numLib metisLib simpLib combinTheory arithmeticTheory prim_recTheory + pred_setTheory listTheory pairTheory markerLib TotalDefn; + +local open listSimps pred_setSimps in end; -open listTheory val FILTER_APPEND = FILTER_APPEND_DISTRIB val REVERSE = REVERSE_SNOC_DEF - -open markerLib +val decide_tac = numLib.DECIDE_TAC; val () = new_theory "rich_list" @@ -17,6 +23,11 @@ val metis_tac = METIS_TAC val rw = SRW_TAC[numSimps.ARITH_ss] fun simp thl = ASM_SIMP_TAC (srw_ss() ++ numSimps.ARITH_ss) thl fun fs thl = FULL_SIMP_TAC (srw_ss() ++ numSimps.ARITH_ss) thl +fun rfs thl = REV_FULL_SIMP_TAC (srw_ss() ++ numSimps.ARITH_ss) thl; +val qabbrev_tac = Q.ABBREV_TAC; +val qexists_tac = Q.EXISTS_TAC; +val qspecl_then = Q.SPECL_THEN; +val qid_spec_tac = Q.ID_SPEC_TAC; val DEF0 = Lib.with_flag (boolLib.def_suffix, "") TotalDefn.Define val DEF = Lib.with_flag (boolLib.def_suffix, "_DEF") TotalDefn.Define @@ -1084,9 +1095,7 @@ val NULL_FOLDL = Q.store_thm ("NULL_FOLDL", THEN REWRITE_TAC [NULL_DEF, FOLDL_SNOC, NULL_EQ, FOLDL, GSYM NOT_NIL_SNOC]); -val MAP_REVERSE = Q.store_thm ("MAP_REVERSE", - `!f l. MAP f (REVERSE l) = REVERSE (MAP f l)`, - GEN_TAC THEN LIST_INDUCT_TAC THEN ASM_REWRITE_TAC [REVERSE, MAP, MAP_SNOC]); +val MAP_REVERSE = save_thm ("MAP_REVERSE", MAP_REVERSE); val SEG_LENGTH_ID = Q.store_thm ("SEG_LENGTH_ID", `!l. SEG (LENGTH l) 0 l = l`, @@ -3186,7 +3195,6 @@ local fun simp ths = asm_simp_tac (simpss()) ths fun dsimp ths = asm_simp_tac (simpss() ++ boolSimps.DNF_ss) ths val decide_tac = numLib.DECIDE_TAC - open pred_setTheory open listTheory pairTheory; in val LIST_TO_SET_EQ_SING = Q.store_thm("LIST_TO_SET_EQ_SING", @@ -3225,7 +3233,7 @@ Theorem ALL_DISTINCT_FRONT : Proof rpt STRIP_TAC >> ‘ALL_DISTINCT l = ALL_DISTINCT (SNOC (LAST l) (FRONT l))’ - by rw [SNOC_LAST_FRONT] + by rw [SNOC_OF_LAST_FRONT] >> FULL_SIMP_TAC std_ss [ALL_DISTINCT_SNOC] QED @@ -3505,8 +3513,6 @@ QED end (* end CakeML lemmas *) -local open listTheory in - Theorem nub_GENLIST: nub (GENLIST f n) = MAP f (FILTER (\i. !j. (i < j) /\ (j < n) ==> f i <> f j) (COUNT_LIST n)) @@ -3550,8 +3556,6 @@ Proof \\ rw[] QED -end - (* alternative definition of UNIQUE *) val UNIQUE_LIST_ELEM_COUNT = store_thm ( "UNIQUE_LIST_ELEM_COUNT", ``!e L. UNIQUE e L = (LIST_ELEM_COUNT e L = 1)``, @@ -3719,6 +3723,3601 @@ Proof >> REWRITE_TAC [GSYM FILTER_APPEND_DISTRIB] QED +(* ------------------------------------------------------------------------- *) +(* More List Theorems from examples/algebra *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: l <> [] ==> (l = SNOC (LAST l) (FRONT l)) *) +(* Proof: + l + = FRONT l ++ [LAST l] by APPEND_FRONT_LAST, l <> [] + = SNOC (LAST l) (FRONT l) by SNOC_APPEND + *) +val SNOC_LAST_FRONT = store_thm( + "SNOC_LAST_FRONT", + ``!l. l <> [] ==> (l = SNOC (LAST l) (FRONT l))``, + rw[APPEND_FRONT_LAST]); + +(* Theorem: REVERSE [x] = [x] *) +(* Proof: + REVERSE [x] + = [] ++ [x] by REVERSE_DEF + = [x] by APPEND +*) +val REVERSE_SING = store_thm( + "REVERSE_SING", + ``!x. REVERSE [x] = [x]``, + rw[]); + +(* Theorem: ls <> [] ==> (HD (REVERSE ls) = LAST ls) *) +(* Proof: + HD (REVERSE ls) + = HD (REVERSE (SNOC (LAST ls) (FRONT ls))) by SNOC_LAST_FRONT + = HD (LAST ls :: (REVERSE (FRONT ls)) by REVERSE_SNOC + = LAST ls by HD +*) +Theorem REVERSE_HD: + !ls. ls <> [] ==> (HD (REVERSE ls) = LAST ls) +Proof + metis_tac[SNOC_LAST_FRONT, REVERSE_SNOC, HD] +QED + +(* Theorem: ls <> [] ==> (TL (REVERSE ls) = REVERSE (FRONT ls)) *) +(* Proof: + TL (REVERSE ls) + = TL (REVERSE (SNOC (LAST ls) (FRONT ls))) by SNOC_LAST_FRONT + = TL (LAST ls :: (REVERSE (FRONT ls)) by REVERSE_SNOC + = REVERSE (FRONT ls) by TL +*) +Theorem REVERSE_TL: + !ls. ls <> [] ==> (TL (REVERSE ls) = REVERSE (FRONT ls)) +Proof + metis_tac[SNOC_LAST_FRONT, REVERSE_SNOC, TL] +QED + +(* Theorem: EL (LENGTH ls) (ls ++ h::t) = h *) +(* Proof: + Let l2 = h::t. + Note ~NULL l2 by NULL + so EL (LENGTH ls) (ls ++ h::t) + = EL (LENGTH ls) (ls ++ l2) by notation + = HD l2 by EL_LENGTH_APPEND + = HD (h::t) = h by notation +*) +val EL_LENGTH_APPEND_0 = store_thm( + "EL_LENGTH_APPEND_0", + ``!ls h t. EL (LENGTH ls) (ls ++ h::t) = h``, + rw[EL_LENGTH_APPEND]); + +(* Theorem: EL (LENGTH ls + 1) (ls ++ h::k::t) = k *) +(* Proof: + Let l1 = ls ++ [h]. + Then LENGTH l1 = LENGTH ls + 1 by LENGTH + Note ls ++ h::k::t = l1 ++ k::t by APPEND + EL (LENGTH ls + 1) (ls ++ h::k::t) + = EL (LENGTH l1) (l1 ++ k::t) by above + = k by EL_LENGTH_APPEND_0 +*) +val EL_LENGTH_APPEND_1 = store_thm( + "EL_LENGTH_APPEND_1", + ``!ls h k t. EL (LENGTH ls + 1) (ls ++ h::k::t) = k``, + rpt strip_tac >> + qabbrev_tac `l1 = ls ++ [h]` >> + `LENGTH l1 = LENGTH ls + 1` by rw[Abbr`l1`] >> + `ls ++ h::k::t = l1 ++ k::t` by rw[Abbr`l1`] >> + metis_tac[EL_LENGTH_APPEND_0]); + +(* Theorem: 0 < LENGTH ls <=> (ls = HD ls::TL ls) *) +(* Proof: + If part: 0 < LENGTH ls ==> (ls = HD ls::TL ls) + Note LENGTH ls <> 0 by arithmetic + so ~(NULL l) by NULL_LENGTH + or ls = HD ls :: TL ls by CONS + Only-if part: (ls = HD ls::TL ls) ==> 0 < LENGTH ls + Note LENGTH ls = SUC (LENGTH (TL ls)) by LENGTH + but 0 < SUC (LENGTH (TL ls)) by SUC_POS +*) +val LIST_HEAD_TAIL = store_thm( + "LIST_HEAD_TAIL", + ``!ls. 0 < LENGTH ls <=> (ls = HD ls::TL ls)``, + metis_tac[LIST_NOT_NIL, NOT_NIL_EQ_LENGTH_NOT_0]); + +(* Theorem: p <> [] /\ q <> [] ==> ((p = q) <=> ((HD p = HD q) /\ (TL p = TL q))) *) +(* Proof: by cases on p and cases on q, CONS_11 *) +val LIST_EQ_HEAD_TAIL = store_thm( + "LIST_EQ_HEAD_TAIL", + ``!p q. p <> [] /\ q <> [] ==> + ((p = q) <=> ((HD p = HD q) /\ (TL p = TL q)))``, + (Cases_on `p` >> Cases_on `q` >> fs[])); + +(* Theorem: [x] = [y] <=> x = y *) +(* Proof: by EQ_LIST and notation. *) +val LIST_SING_EQ = store_thm( + "LIST_SING_EQ", + ``!x y. ([x] = [y]) <=> (x = y)``, + rw_tac bool_ss[]); + +(* Theorem: LENGTH [x] = 1 *) +(* Proof: by LENGTH, ONE. *) +val LENGTH_SING = store_thm( + "LENGTH_SING", + ``!x. LENGTH [x] = 1``, + rw_tac bool_ss[LENGTH, ONE]); + +(* Theorem: ls <> [] ==> LENGTH (TL ls) < LENGTH ls *) +(* Proof: by LENGTH_TL, LENGTH_EQ_0 *) +val LENGTH_TL_LT = store_thm( + "LENGTH_TL_LT", + ``!ls. ls <> [] ==> LENGTH (TL ls) < LENGTH ls``, + metis_tac[LENGTH_TL, LENGTH_EQ_0, NOT_ZERO_LT_ZERO, DECIDE``n <> 0 ==> n - 1 < n``]); + +(* Theorem: MAP f [x] = [f x] *) +(* Proof: by MAP *) +val MAP_SING = store_thm( + "MAP_SING", + ``!f x. MAP f [x] = [f x]``, + rw[]); + +(* listTheory.MAP_TL |- !l f. MAP f (TL l) = TL (MAP f l) *) + +(* Theorem: ls <> [] ==> HD (MAP f ls) = f (HD ls) *) +(* Proof: + Note 0 < LENGTH ls by LENGTH_NON_NIL + HD (MAP f ls) + = EL 0 (MAP f ls) by EL + = f (EL 0 ls) by EL_MAP, 0 < LENGTH ls + = f (HD ls) by EL +*) +Theorem MAP_HD: + !ls f. ls <> [] ==> HD (MAP f ls) = f (HD ls) +Proof + metis_tac[EL_MAP, EL, LENGTH_NON_NIL] +QED + +(* +LAST_EL |- !ls. ls <> [] ==> LAST ls = EL (PRE (LENGTH ls)) ls +*) + +(* Theorem: t <> [] ==> (LAST t = EL (LENGTH t) (h::t)) *) +(* Proof: + Note LENGTH t <> 0 by LENGTH_EQ_0 + or 0 < LENGTH t + LAST t + = EL (PRE (LENGTH t)) t by LAST_EL + = EL (SUC (PRE (LENGTH t))) (h::t) by EL + = EL (LENGTH t) (h::t) bu SUC_PRE, 0 < LENGTH t +*) +val LAST_EL_CONS = store_thm( + "LAST_EL_CONS", + ``!h t. t <> [] ==> (LAST t = EL (LENGTH t) (h::t))``, + rpt strip_tac >> + `0 < LENGTH t` by metis_tac[LENGTH_EQ_0, NOT_ZERO_LT_ZERO] >> + `LAST t = EL (PRE (LENGTH t)) t` by rw[LAST_EL] >> + `_ = EL (SUC (PRE (LENGTH t))) (h::t)` by rw[] >> + metis_tac[SUC_PRE]); + +(* Theorem alias *) +val FRONT_LENGTH = save_thm ("FRONT_LENGTH", LENGTH_FRONT); +(* val FRONT_LENGTH = |- !l. l <> [] ==> (LENGTH (FRONT l) = PRE (LENGTH l)): thm *) + +(* Theorem: l <> [] /\ n < LENGTH (FRONT l) ==> (EL n (FRONT l) = EL n l) *) +(* Proof: by EL_FRONT, NULL *) +val FRONT_EL = store_thm( + "FRONT_EL", + ``!l n. l <> [] /\ n < LENGTH (FRONT l) ==> (EL n (FRONT l) = EL n l)``, + metis_tac[EL_FRONT, NULL, list_CASES]); + +(* Theorem: (LENGTH l = 1) ==> (FRONT l = []) *) +(* Proof: + Note ?x. l = [x] by LENGTH_EQ_1 + FRONT l + = FRONT [x] by above + = [] by FRONT_DEF +*) +val FRONT_EQ_NIL = store_thm( + "FRONT_EQ_NIL", + ``!l. (LENGTH l = 1) ==> (FRONT l = [])``, + rw[LENGTH_EQ_1] >> + rw[FRONT_DEF]); + +(* Theorem: 1 < LENGTH l ==> FRONT l <> [] *) +(* Proof: + Note LENGTH l <> 0 by 1 < LENGTH l + Thus ?h s. l = h::s by list_CASES + or 1 < 1 + LENGTH s + so 0 < LENGTH s by arithmetic + Thus ?k t. s = k::t by list_CASES + FRONT l + = FRONT (h::k::t) + = h::FRONT (k::t) by FRONT_CONS + <> [] by list_CASES +*) +val FRONT_NON_NIL = store_thm( + "FRONT_NON_NIL", + ``!l. 1 < LENGTH l ==> FRONT l <> []``, + rpt strip_tac >> + `LENGTH l <> 0` by decide_tac >> + `?h s. l = h::s` by metis_tac[list_CASES, LENGTH_EQ_0] >> + `LENGTH l = 1 + LENGTH s` by rw[] >> + `LENGTH s <> 0` by decide_tac >> + `?k t. s = k::t` by metis_tac[list_CASES, LENGTH_EQ_0] >> + `FRONT l = h::FRONT (k::t)` by fs[FRONT_CONS] >> + fs[]); + +(* Theorem: ls <> [] ==> MEM (HD ls) ls *) +(* Proof: + Note ls = h::t by list_CASES + MEM (HD (h::t)) (h::t) + <=> MEM h (h::t) by HD + <=> T by MEM +*) +val HEAD_MEM = store_thm( + "HEAD_MEM", + ``!ls. ls <> [] ==> MEM (HD ls) ls``, + (Cases_on `ls` >> simp[])); + +(* Theorem: ls <> [] ==> MEM (LAST ls) ls *) +(* Proof: + By induction on ls. + Base: [] <> [] ==> MEM (LAST []) [] + True by [] <> [] = F. + Step: ls <> [] ==> MEM (LAST ls) ls ==> + !h. h::ls <> [] ==> MEM (LAST (h::ls)) (h::ls) + If ls = [], + MEM (LAST [h]) [h] + <=> MEM h [h] by LAST_DEF + <=> T by MEM + If ls <> [], + MEM (LAST [h::ls]) (h::ls) + <=> MEM (LAST ls) (h::ls) by LAST_DEF + <=> LAST ls = h \/ MEM (LAST ls) ls by MEM + <=> LAST ls = h \/ T by induction hypothesis + <=> T by logical or +*) +val LAST_MEM = store_thm( + "LAST_MEM", + ``!ls. ls <> [] ==> MEM (LAST ls) ls``, + Induct >- + decide_tac >> + (Cases_on `ls = []` >> rw[LAST_DEF])); + +(* Idea: the last equals the head when there is no tail. *) + +(* Theorem: ~MEM h t /\ LAST (h::t) = h <=> t = [] *) +(* Proof: + If part: ~MEM h t /\ LAST (h::t) = h ==> t = [] + By contradiction, suppose t <> []. + Then h = LAST (h::t) = LAST t by LAST_CONS_cond, t <> [] + so MEM h t by LAST_MEM + This contradicts ~MEM h t. + Only-if part: t = [] ==> ~MEM h t /\ LAST (h::t) = h + Note MEM h [] = F, so ~MEM h [] = T by MEM + and LAST [h] = h by LAST_CONS +*) +Theorem LAST_EQ_HD: + !h t. ~MEM h t /\ LAST (h::t) = h <=> t = [] +Proof + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + metis_tac[LAST_CONS_cond, LAST_MEM] +QED + +(* Theorem: ls <> [] /\ ALL_DISTINCT ls ==> ~MEM (LAST ls) (FRONT ls) *) +(* Proof: + Let k = LENGTH ls. + Then 0 < k by LENGTH_EQ_0, NOT_ZERO + and LENGTH (FRONT ls) = PRE k by LENGTH_FRONT, ls <> [] + so ?n. n < PRE k /\ + LAST ls = EL n (FRONT ls) by MEM_EL + = EL n ls by FRONT_EL, ls <> [] + but LAST ls = EL (PRE k) ls by LAST_EL, ls <> [] + Thus n = PRE k by ALL_DISTINCT_EL_IMP + This contradicts n < PRE k by arithmetic +*) +Theorem MEM_FRONT_NOT_LAST: + !ls. ls <> [] /\ ALL_DISTINCT ls ==> ~MEM (LAST ls) (FRONT ls) +Proof + rpt strip_tac >> + qabbrev_tac `k = LENGTH ls` >> + `0 < k` by metis_tac[LENGTH_EQ_0, NOT_ZERO] >> + `LENGTH (FRONT ls) = PRE k` by fs[LENGTH_FRONT, Abbr`k`] >> + fs[MEM_EL] >> + `LAST ls = EL n ls` by fs[FRONT_EL] >> + `LAST ls = EL (PRE k) ls` by rfs[LAST_EL, Abbr`k`] >> + `n < k /\ PRE k < k` by decide_tac >> + `n = PRE k` by metis_tac[ALL_DISTINCT_EL_IMP] >> + decide_tac +QED + +(* Theorem: ls = [] <=> !x. ~MEM x ls *) +(* Proof: + If part: !x. ~MEM x [], true by MEM + Only-if part: !x. ~MEM x ls ==> ls = [] + By contradiction, suppose ls <> []. + Then ?h t. ls = h::t by list_CASES + and MEM h ls by MEM + which contradicts !x. ~MEM x ls. +*) +Theorem NIL_NO_MEM: + !ls. ls = [] <=> !x. ~MEM x ls +Proof + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + metis_tac[list_CASES, MEM] +QED + +(* +el_append3 +|- !l1 x l2. EL (LENGTH l1) (l1 ++ [x] ++ l2) = x +*) + +(* Theorem: MEM h (l1 ++ [x] ++ l2) <=> MEM h (x::(l1 ++ l2)) *) +(* Proof: + MEM h (l1 ++ [x] ++ l2) + <=> MEM h l1 \/ h = x \/ MEM h l2 by MEM, MEM_APPEND + <=> h = x \/ MEM h l1 \/ MEM h l2 + <=> h = x \/ MEM h (l1 ++ l2) by MEM_APPEND + <=> MEM h (x::(l1 + l2)) by MEM +*) +Theorem MEM_APPEND_3: + !l1 x l2 h. MEM h (l1 ++ [x] ++ l2) <=> MEM h (x::(l1 ++ l2)) +Proof + rw[] >> + metis_tac[] +QED + +(* Theorem: DROP 1 (h::t) = t *) +(* Proof: DROP_def *) +val DROP_1 = store_thm( + "DROP_1", + ``!h t. DROP 1 (h::t) = t``, + rw[]); + +(* Theorem: FRONT [x] = [] *) +(* Proof: FRONT_def *) +val FRONT_SING = store_thm( + "FRONT_SING", + ``!x. FRONT [x] = []``, + rw[]); + +(* Theorem: ls <> [] ==> (TL ls = DROP 1 ls) *) +(* Proof: + Note ls = h::t by list_CASES + so TL (h::t) + = t by TL + = DROP 1 (h::t) by DROP_def +*) +val TAIL_BY_DROP = store_thm( + "TAIL_BY_DROP", + ``!ls. ls <> [] ==> (TL ls = DROP 1 ls)``, + Cases_on `ls` >- + decide_tac >> + rw[]); + +(* Theorem: ls <> [] ==> (FRONT ls = TAKE (LENGTH ls - 1) ls) *) +(* Proof: + By induction on ls. + Base: [] <> [] ==> FRONT [] = TAKE (LENGTH [] - 1) [] + True by [] <> [] = F. + Step: ls <> [] ==> FRONT ls = TAKE (LENGTH ls - 1) ls ==> + !h. h::ls <> [] ==> FRONT (h::ls) = TAKE (LENGTH (h::ls) - 1) (h::ls) + If ls = [], + FRONT [h] + = [] by FRONT_SING + = TAKE 0 [h] by TAKE_0 + = TAKE (LENGTH [h] - 1) [h] by LENGTH_SING + If ls <> [], + FRONT (h::ls) + = h::FRONT ls by FRONT_DEF + = h::TAKE (LENGTH ls - 1) ls by induction hypothesis + = TAKE (LENGTH (h::ls) - 1) (h::ls) by TAKE_def +*) +val FRONT_BY_TAKE = store_thm( + "FRONT_BY_TAKE", + ``!ls. ls <> [] ==> (FRONT ls = TAKE (LENGTH ls - 1) ls)``, + Induct >- + decide_tac >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + `LENGTH ls <> 0` by rw[] >> + rw[FRONT_DEF]); + +(* Theorem: HD (h::t ++ ls) = h *) +(* Proof: + HD (h::t ++ ls) + = HD (h::(t ++ ls)) by APPEND + = h by HD +*) +Theorem HD_APPEND: + !h t ls. HD (h::t ++ ls) = h +Proof + simp[] +QED + +(* Theorem: 0 <> n ==> (EL (n-1) t = EL n (h::t)) *) +(* Proof: + Note n = SUC k for some k by num_CASES + so EL k t = EL (SUC k) (h::t) by EL_restricted +*) +Theorem EL_TAIL: + !h t n. 0 <> n ==> (EL (n-1) t = EL n (h::t)) +Proof + rpt strip_tac >> + `n = SUC (n - 1)` by decide_tac >> + metis_tac[EL_restricted] +QED + +(* Idea: If all elements are the same, the set is SING. *) + +(* Theorem: ls <> [] /\ EVERY ($= c) ls ==> SING (set ls) *) +(* Proof: + Note set ls = {c} by LIST_TO_SET_EQ_SING + thus SING (set ls) by SING_DEF +*) +Theorem MONOLIST_SET_SING: + !c ls. ls <> [] /\ EVERY ($= c) ls ==> SING (set ls) +Proof + metis_tac[LIST_TO_SET_EQ_SING, SING_DEF] +QED + +(* +> EVAL ``set [3;3;3]``; +val it = |- set [3; 3; 3] = set [3; 3; 3]: thm +*) + +(* Put LIST_TO_SET into compute +(* Near: put to helperList *) +Theorem LIST_TO_SET_EVAL[compute] = LIST_TO_SET |> GEN_ALL; +(* val LIST_TO_SET_EVAL = |- !t h. set [] = {} /\ set (h::t) = h INSERT set t: thm *) +(* cannot add to computeLib directly LIST_TO_SET, which is not in current theory. *) + *) + +(* +> EVAL ``set [3;3;3]``; +val it = |- set [3; 3; 3] = {3}: thm +*) + +(* Theorem: set ls = count n ==> !j. j < LENGTH ls ==> EL j ls < n *) +(* Proof: + Note MEM (EL j ls) ls by EL_MEM + so EL j ls IN (count n) by set ls = count n + or EL j ls < n by IN_COUNT +*) +Theorem set_list_eq_count: + !ls n. set ls = count n ==> !j. j < LENGTH ls ==> EL j ls < n +Proof + metis_tac[EL_MEM, IN_COUNT] +QED + +(* Theorem: set ls = IMAGE (\j. EL j ls) (count (LENGTH ls)) *) +(* Proof: + Let f = \j. EL j ls, n = LENGTH ls. + x IN IMAGE f (count n) + <=> ?j. x = f j /\ j IN (count n) by IN_IMAGE + <=> ?j. x = EL j ls /\ j < n by notation, IN_COUNT + <=> MEM x ls by MEM_EL + <=> x IN set ls by notation + Thus set ls = IMAGE f (count n) by EXTENSION +*) +Theorem list_to_set_eq_el_image: + !ls. set ls = IMAGE (\j. EL j ls) (count (LENGTH ls)) +Proof + rw[EXTENSION] >> + metis_tac[MEM_EL] +QED + +(* Theorem: ALL_DISTINCT ls ==> INJ (\j. EL j ls) (count (LENGTH ls)) univ(:num) *) +(* Proof: + By INJ_DEF this is to show: + (1) EL j ls IN univ(:'a), true by IN_UNIV, function type + (2) !x y. x < LENGTH ls /\ y < LENGTH ls /\ EL x ls = EL y ls ==> x = y + This is true by ALL_DISTINCT_EL_IMP, ALL_DISTINCT ls +*) +Theorem all_distinct_list_el_inj: + !ls. ALL_DISTINCT ls ==> INJ (\j. EL j ls) (count (LENGTH ls)) univ(:'a) +Proof + rw[INJ_DEF, ALL_DISTINCT_EL_IMP] +QED + +(* MAP_ZIP_SAME |- !ls f. MAP f (ZIP (ls,ls)) = MAP (\x. f (x,x)) ls *) + +(* Theorem: ZIP ((MAP f ls), (MAP g ls)) = MAP (\x. (f x, g x)) ls *) +(* Proof: + ZIP ((MAP f ls), (MAP g ls)) + = MAP (\(x, y). (f x, y)) (ZIP (ls, (MAP g ls))) by ZIP_MAP + = MAP (\(x, y). (f x, y)) (MAP (\(x, y). (x, g y)) (ZIP (ls, ls))) by ZIP_MAP + = MAP (\(x, y). (f x, y)) (MAP (\j. (\(x, y). (x, g y)) (j,j)) ls) by MAP_ZIP_SAME + = MAP (\(x, y). (f x, y)) o (\j. (\(x, y). (x, g y)) (j,j)) ls by MAP_COMPOSE + = MAP (\x. (f x, g x)) ls by FUN_EQ_THM +*) +val ZIP_MAP_MAP = store_thm( + "ZIP_MAP_MAP", + ``!ls f g. ZIP ((MAP f ls), (MAP g ls)) = MAP (\x. (f x, g x)) ls``, + rw[ZIP_MAP, MAP_COMPOSE] >> + qabbrev_tac `f1 = \p. (f (FST p),SND p)` >> + qabbrev_tac `f2 = \x. (x,g x)` >> + qabbrev_tac `f3 = \x. (f x,g x)` >> + `f1 o f2 = f3` by rw[FUN_EQ_THM, Abbr`f1`, Abbr`f2`, Abbr`f3`] >> + rw[]); + +(* Theorem: MAP2 f (MAP g1 ls) (MAP g2 ls) = MAP (\x. f (g1 x) (g2 x)) ls *) +(* Proof: + Let k = LENGTH ls. + Note LENGTH (MAP g1 ls) = k by LENGTH_MAP + and LENGTH (MAP g2 ls) = k by LENGTH_MAP + MAP2 f (MAP g1 ls) (MAP g2 ls) + = MAP (UNCURRY f) (ZIP ((MAP g1 ls), (MAP g2 ls))) by MAP2_MAP + = MAP (UNCURRY f) (MAP (\x. (g1 x, g2 x)) ls) by ZIP_MAP_MAP + = MAP ((UNCURRY f) o (\x. (g1 x, g2 x))) ls by MAP_COMPOSE + = MAP (\x. f (g1 x) (g2 y)) ls by FUN_EQ_THM +*) +val MAP2_MAP_MAP = store_thm( + "MAP2_MAP_MAP", + ``!ls f g1 g2. MAP2 f (MAP g1 ls) (MAP g2 ls) = MAP (\x. f (g1 x) (g2 x)) ls``, + rw[MAP2_MAP, ZIP_MAP_MAP, MAP_COMPOSE] >> + qabbrev_tac `f1 = UNCURRY f o (\x. (g1 x,g2 x))` >> + qabbrev_tac `f2 = \x. f (g1 x) (g2 x)` >> + `f1 = f2` by rw[FUN_EQ_THM, Abbr`f1`, Abbr`f2`] >> + rw[]); + +(* Theorem: EL n (l1 ++ l2) = if n < LENGTH l1 then EL n l1 else EL (n - LENGTH l1) l2 *) +(* Proof: by EL_APPEND1, EL_APPEND2 *) +val EL_APPEND = store_thm( + "EL_APPEND", + ``!n l1 l2. EL n (l1 ++ l2) = if n < LENGTH l1 then EL n l1 else EL (n - LENGTH l1) l2``, + rw[EL_APPEND1, EL_APPEND2]); + +(* Theorem: j < LENGTH ls ==> ?l1 l2. ls = l1 ++ (EL j ls)::l2 *) +(* Proof: + Let x = EL j ls. + Then MEM x ls by EL_MEM, j < LENGTH ls + so ?l1 l2. l = l1 ++ x::l2 by MEM_SPLIT + Pick these l1 and l2. +*) +Theorem EL_SPLIT: + !ls j. j < LENGTH ls ==> ?l1 l2. ls = l1 ++ (EL j ls)::l2 +Proof + metis_tac[EL_MEM, MEM_SPLIT] +QED + +(* Theorem: j < k /\ k < LENGTH ls ==> + ?l1 l2 l3. ls = l1 ++ (EL j ls)::l2 ++ (EL k ls)::l3 *) +(* Proof: + Let a = EL j ls, + b = EL k ls. + Note j < LENGTH ls by j < k, k < LENGTH ls + so MEM a ls /\ MEM b ls by MEM_EL + + Now ls + = TAKE k ls ++ DROP k ls by TAKE_DROP + = TAKE k ls ++ b::(DROP (k+1) ls) by DROP_EL_CONS + Let lt = TAKE k ls. + Then LENGTH lt = k by LENGTH_TAKE + and a = EL j lt by EL_TAKE + and lt + = TAKE j lt ++ DROP j lt by TAKE_DROP + = TAKE j lt ++ a::(DROP (j+1) lt) by DROP_EL_CONS + Pick l1 = TAKE j lt, l2 = DROP (j+1) lt, l3 = DROP (k+1) ls. +*) +Theorem EL_SPLIT_2: + !ls j k. j < k /\ k < LENGTH ls ==> + ?l1 l2 l3. ls = l1 ++ (EL j ls)::l2 ++ (EL k ls)::l3 +Proof + rpt strip_tac >> + qabbrev_tac `a = EL j ls` >> + qabbrev_tac `b = EL k ls` >> + `j < LENGTH ls` by decide_tac >> + `MEM a ls /\ MEM b ls` by metis_tac[MEM_EL] >> + `ls = TAKE k ls ++ b::(DROP (k+1) ls)` by metis_tac[TAKE_DROP, DROP_EL_CONS] >> + qabbrev_tac `lt = TAKE k ls` >> + `LENGTH lt = k` by simp[Abbr`lt`] >> + `a = EL j lt` by simp[EL_TAKE, Abbr`a`, Abbr`lt`] >> + `lt = TAKE j lt ++ a::(DROP (j+1) lt)` by metis_tac[TAKE_DROP, DROP_EL_CONS] >> + metis_tac[] +QED + +(* Theorem: (l1 ++ l2 = m1 ++ m2) /\ (LENGTH l1 = LENGTH m1) <=> (l1 = m1) /\ (l2 = m2) *) +(* Proof: + By APPEND_EQ_APPEND, + ?l. (l1 = m1 ++ l) /\ (m2 = l ++ l2) \/ ?l. (m1 = l1 ++ l) /\ (l2 = l ++ m2). + Thus this is to show: + (1) LENGTH (m1 ++ l) = LENGTH m1 ==> m1 ++ l = m1, true since l = [] by LENGTH_APPEND, LENGTH_NIL + (2) LENGTH (m1 ++ l) = LENGTH m1 ==> l2 = l ++ l2, true since l = [] by LENGTH_APPEND, LENGTH_NIL + (3) LENGTH l1 = LENGTH (l1 ++ l) ==> l1 = l1 ++ l, true since l = [] by LENGTH_APPEND, LENGTH_NIL + (4) LENGTH l1 = LENGTH (l1 ++ l) ==> l ++ m2 = m2, true since l = [] by LENGTH_APPEND, LENGTH_NIL +*) +val APPEND_EQ_APPEND_EQ = store_thm( + "APPEND_EQ_APPEND_EQ", + ``!l1 l2 m1 m2. (l1 ++ l2 = m1 ++ m2) /\ (LENGTH l1 = LENGTH m1) <=> (l1 = m1) /\ (l2 = m2)``, + rw[APPEND_EQ_APPEND] >> + rw[EQ_IMP_THM] >- + fs[] >- + fs[] >- + (fs[] >> + `LENGTH l = 0` by decide_tac >> + fs[]) >> + fs[] >> + `LENGTH l = 0` by decide_tac >> + fs[]); + +(* ------------------------------------------------------------------------- *) +(* More about DROP and TAKE *) +(* ------------------------------------------------------------------------- *) + +(* listTheory.HD_DROP |- !n l. n < LENGTH l ==> HD (DROP n l) = EL n l *) + +(* Theorem: n < LENGTH ls ==> TL (DROP n ls) = DROP n (TL ls) *) +(* Proof: + Note 0 < LENGTH ls, so ls <> [] by LENGTH_NON_NIL + so ?h t. ls = h::t by NOT_NIL_CONS + TL (DROP n ls) + = TL (EL n ls::DROP (SUC n) ls) by DROP_CONS_EL + = DROP (SUC n) ls by TL + = DROP (SUC n) (h::t) by above + = DROP n t by DROP + = DROP n (TL ls) by TL +*) +Theorem TL_DROP: + !ls n. n < LENGTH ls ==> TL (DROP n ls) = DROP n (TL ls) +Proof + rpt strip_tac >> + `0 < LENGTH ls` by decide_tac >> + `TL (DROP n ls) = TL (EL n ls::DROP (SUC n) ls)` by simp[DROP_CONS_EL] >> + `_ = DROP (SUC n) ls` by simp[] >> + `_ = DROP (SUC n) (HD ls::TL ls)` by metis_tac[LIST_HEAD_TAIL] >> + simp[] +QED + +(* Theorem: x <> [] ==> (TAKE 1 (x ++ y) = TAKE 1 x) *) +(* Proof: + x <> [] means ?h t. x = h::t by list_CASES + TAKE 1 (x ++ y) + = TAKE 1 ((h::t) ++ y) + = TAKE 1 (h:: t ++ y) by APPEND + = h::TAKE 0 (t ++ y) by TAKE_def + = h::TAKE 0 t by TAKE_0 + = TAKE 1 (h::t) by TAKE_def +*) +val TAKE_1_APPEND = store_thm( + "TAKE_1_APPEND", + ``!x y. x <> [] ==> (TAKE 1 (x ++ y) = TAKE 1 x)``, + Cases_on `x`>> rw[]); + +(* Theorem: x <> [] ==> (DROP 1 (x ++ y) = (DROP 1 x) ++ y) *) +(* Proof: + x <> [] means ?h t. x = h::t by list_CASES + DROP 1 (x ++ y) + = DROP 1 ((h::t) ++ y) + = DROP 1 (h:: t ++ y) by APPEND + = DROP 0 (t ++ y) by DROP_def + = t ++ y by DROP_0 + = (DROP 1 (h::t)) ++ y by DROP_def +*) +val DROP_1_APPEND = store_thm( + "DROP_1_APPEND", + ``!x y. x <> [] ==> (DROP 1 (x ++ y) = (DROP 1 x) ++ y)``, + Cases_on `x` >> rw[]); + +(* Theorem: DROP (SUC n) x = DROP 1 (DROP n x) *) +(* Proof: + By induction on x. + Base case: !n. DROP (SUC n) [] = DROP 1 (DROP n []) + LHS = DROP (SUC n) [] = [] by DROP_def + RHS = DROP 1 (DROP n []) + = DROP 1 [] by DROP_def + = [] = LHS by DROP_def + Step case: !n. DROP (SUC n) x = DROP 1 (DROP n x) ==> + !h n. DROP (SUC n) (h::x) = DROP 1 (DROP n (h::x)) + If n = 0, + LHS = DROP (SUC 0) (h::x) + = DROP 1 (h::x) by ONE + RHS = DROP 1 (DROP 0 (h::x)) + = DROP 1 (h::x) = LHS by DROP_0 + If n <> 0, + LHS = DROP (SUC n) (h::x) + = DROP n x by DROP_def + RHS = DROP 1 (DROP n (h::x) + = DROP 1 (DROP (n-1) x) by DROP_def + = DROP (SUC (n-1)) x by induction hypothesis + = DROP n x = LHS by SUC (n-1) = n, n <> 0. +*) +val DROP_SUC = store_thm( + "DROP_SUC", + ``!n x. DROP (SUC n) x = DROP 1 (DROP n x)``, + Induct_on `x` >> + rw[DROP_def] >> + `n = SUC (n-1)` by decide_tac >> + metis_tac[]); + +(* Theorem: TAKE (SUC n) x = (TAKE n x) ++ (TAKE 1 (DROP n x)) *) +(* Proof: + By induction on x. + Base case: !n. TAKE (SUC n) [] = TAKE n [] ++ TAKE 1 (DROP n []) + LHS = TAKE (SUC n) [] = [] by TAKE_def + RHS = TAKE n [] ++ TAKE 1 (DROP n []) + = [] ++ TAKE 1 [] by TAKE_def, DROP_def + = TAKE 1 [] by APPEND + = [] = LHS by TAKE_def + Step case: !n. TAKE (SUC n) x = TAKE n x ++ TAKE 1 (DROP n x) ==> + !h n. TAKE (SUC n) (h::x) = TAKE n (h::x) ++ TAKE 1 (DROP n (h::x)) + If n = 0, + LHS = TAKE (SUC 0) (h::x) + = TAKE 1 (h::x) by ONE + RHS = TAKE 0 (h::x) ++ TAKE 1 (DROP 0 (h::x)) + = [] ++ TAKE 1 (h::x) by TAKE_def, DROP_def + = TAKE 1 (h::x) = LHS by APPEND + If n <> 0, + LHS = TAKE (SUC n) (h::x) + = h :: TAKE n x by TAKE_def + RHS = TAKE n (h::x) ++ TAKE 1 (DROP n (h::x)) + = (h:: TAKE (n-1) x) ++ TAKE 1 (DROP (n-1) x) by TAKE_def, DROP_def, n <> 0. + = h :: (TAKE (n-1) x ++ TAKE 1 (DROP (n-1) x)) by APPEND + = h :: TAKE (SUC (n-1)) x by induction hypothesis + = h :: TAKE n x by SUC (n-1) = n, n <> 0. +*) +val TAKE_SUC = store_thm( + "TAKE_SUC", + ``!n x. TAKE (SUC n) x = (TAKE n x) ++ (TAKE 1 (DROP n x))``, + Induct_on `x` >> + rw[TAKE_def, DROP_def] >> + `n = SUC (n-1)` by decide_tac >> + metis_tac[]); + +(* Theorem: k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x)) *) +(* Proof: + By induction on k. + Base case: !x. 0 < LENGTH x ==> (TAKE (SUC 0) x = SNOC (EL 0 x) (TAKE 0 x)) + 0 < LENGTH x + ==> ?h t. x = h::t by LENGTH_NIL, list_CASES + LHS = TAKE (SUC 0) x + = TAKE 1 (h::t) by ONE + = h::TAKE 0 t by TAKE_def + = h::[] by TAKE_0 + = [h] + = SNOC h [] by SNOC + = SNOC h (TAKE 0 (h::t)) by TAKE_0 + = SNOC (EL 0 (h::t)) (TAKE 0 (h::t)) by EL + = RHS + Step case: !x. k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x)) ==> + !x. SUC k < LENGTH x ==> (TAKE (SUC (SUC k)) x = SNOC (EL (SUC k) x) (TAKE (SUC k) x)) + Since 0 < SUC k by prim_recTheory.LESS_0 + 0 < LENGTH x by LESS_TRANS + ==> ?h t. x = h::t by LENGTH_NIL, list_CASES + and LENGTH (h::t) = SUC (LENGTH t) by LENGTH + hence k < LENGTH t by LESS_MONO_EQ + LHS = TAKE (SUC (SUC k)) (h::t) + = h :: TAKE (SUC k) t by TAKE_def + = h :: SNOC (EL k t) (TAKE k t) by induction hypothesis, k < LENGTH t. + = SNOC (EL k t) (h :: TAKE k t) by SNOC + = SNOC (EL (SUC k) (h::t)) (h :: TAKE k t) by EL_restricted + = SNOC (EL (SUC k) (h::t)) (TAKE (SUC k) (h::t)) by TAKE_def + = RHS +*) +val TAKE_SUC_BY_TAKE = store_thm( + "TAKE_SUC_BY_TAKE", + ``!k x. k < LENGTH x ==> (TAKE (SUC k) x = SNOC (EL k x) (TAKE k x))``, + Induct_on `k` >| [ + rpt strip_tac >> + `LENGTH x <> 0` by decide_tac >> + `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + rw[], + rpt strip_tac >> + `LENGTH x <> 0` by decide_tac >> + `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + `k < LENGTH t` by metis_tac[LENGTH, LESS_MONO_EQ] >> + rw_tac std_ss[TAKE_def, SNOC, EL_restricted] + ]); + +(* Theorem: k < LENGTH x ==> (DROP k x = (EL k x) :: (DROP (SUC k) x)) *) +(* Proof: + By induction on k. + Base case: !x. 0 < LENGTH x ==> (DROP 0 x = EL 0 x::DROP (SUC 0) x) + 0 < LENGTH x + ==> ?h t. x = h::t by LENGTH_NIL, list_CASES + LHS = DROP 0 (h::t) + = h::t by DROP_0 + = (EL 0 (h::t))::t by EL + = (EL 0 (h::t))::(DROP 1 (h::t)) by DROP_def + = EL 0 x::DROP (SUC 0) x by ONE + = RHS + Step case: !x. k < LENGTH x ==> (DROP k x = EL k x::DROP (SUC k) x) ==> + !x. SUC k < LENGTH x ==> (DROP (SUC k) x = EL (SUC k) x::DROP (SUC (SUC k)) x) + Since 0 < SUC k by prim_recTheory.LESS_0 + 0 < LENGTH x by LESS_TRANS + ==> ?h t. x = h::t by LENGTH_NIL, list_CASES + and LENGTH (h::t) = SUC (LENGTH t) by LENGTH + hence k < LENGTH t by LESS_MONO_EQ + LHS = DROP (SUC k) (h::t) + = DROP k t by DROP_def + = EL k x::DROP (SUC k) x by induction hypothesis + = EL k t :: DROP (SUC (SUC k)) (h::t) by DROP_def + = EL (SUC k) (h::t)::DROP (SUC (SUC k)) (h::t) by EL + = RHS +*) +val DROP_BY_DROP_SUC = store_thm( + "DROP_BY_DROP_SUC", + ``!k x. k < LENGTH x ==> (DROP k x = (EL k x) :: (DROP (SUC k) x))``, + Induct_on `k` >| [ + rpt strip_tac >> + `LENGTH x <> 0` by decide_tac >> + `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + rw[], + rpt strip_tac >> + `LENGTH x <> 0` by decide_tac >> + `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> + `k < LENGTH t` by metis_tac[LENGTH, LESS_MONO_EQ] >> + rw[] + ]); + +(* Theorem: n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u *) +(* Proof: + By induction on n. + Base: !ls. 0 < LENGTH ls ==> ?u. DROP 0 ls = [EL 0 ls] ++ u + Note LENGTH ls <> 0 by 0 < LENGTH ls + ==> ls <> [] by LENGTH_NIL + ==> ?h t. ls = h::t by list_CASES + DROP 0 ls + = ls by DROP_0 + = [h] ++ t by ls = h::t, CONS_APPEND + = [EL 0 ls] ++ t by EL + Take u = t. + Step: !ls. n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u ==> + !ls. SUC n < LENGTH ls ==> ?u. DROP (SUC n) ls = [EL (SUC n) ls] ++ u + Note LENGTH ls <> 0 by SUC n < LENGTH ls + ==> ?h t. ls = h::t by list_CASES, LENGTH_NIL + Now LENGTH ls = SUC (LENGTH t) by LENGTH + ==> n < LENGTH t by SUC n < SUC (LENGTH t) + Thus ?u. DROP n t = [EL n t] ++ u by induction hypothesis + + DROP (SUC n) ls + = DROP (SUC n) (h::t) by ls = h::t + = DROP n t by DROP_def + = [EL n t] ++ u by above + = [EL (SUC n) (h::t)] ++ u by EL_restricted + Take this u. +*) +val DROP_HEAD_ELEMENT = store_thm( + "DROP_HEAD_ELEMENT", + ``!ls n. n < LENGTH ls ==> ?u. DROP n ls = [EL n ls] ++ u``, + Induct_on `n` >| [ + rpt strip_tac >> + `LENGTH ls <> 0` by decide_tac >> + `?h t. ls = h::t` by metis_tac[list_CASES, LENGTH_NIL] >> + rw[], + rw[] >> + `LENGTH ls <> 0` by decide_tac >> + `?h t. ls = h::t` by metis_tac[list_CASES, LENGTH_NIL] >> + `LENGTH ls = SUC (LENGTH t)` by rw[] >> + `n < LENGTH t` by decide_tac >> + `?u. DROP n t = [EL n t] ++ u` by rw[] >> + rw[] + ]); + +(* Theorem: DROP n (TAKE n ls) = [] *) +(* Proof: + If n <= LENGTH ls, + Then LENGTH (TAKE n ls) = n by LENGTH_TAKE_EQ + Thus DROP n (TAKE n ls) = [] by DROP_LENGTH_TOO_LONG + If LENGTH ls < n + Then LENGTH (TAKE n ls) = LENGTH ls by LENGTH_TAKE_EQ + Thus DROP n (TAKE n ls) = [] by DROP_LENGTH_TOO_LONG +*) +val DROP_TAKE_EQ_NIL = store_thm( + "DROP_TAKE_EQ_NIL", + ``!ls n. DROP n (TAKE n ls) = []``, + rw[LENGTH_TAKE_EQ, DROP_LENGTH_TOO_LONG]); + +(* Theorem: TAKE m (DROP n ls) = DROP n (TAKE (n + m) ls) *) +(* Proof: + If n <= LENGTH ls, + Then LENGTH (TAKE n ls) = n by LENGTH_TAKE_EQ, n <= LENGTH ls + DROP n (TAKE (n + m) ls) + = DROP n (TAKE n ls ++ TAKE m (DROP n ls)) by TAKE_SUM + = DROP n (TAKE n ls) ++ DROP (n - LENGTH (TAKE n ls)) (TAKE m (DROP n ls)) by DROP_APPEND + = [] ++ DROP (n - LENGTH (TAKE n ls)) (TAKE m (DROP n ls)) by DROP_TAKE_EQ_NIL + = DROP (n - LENGTH (TAKE n ls)) (TAKE m (DROP n ls)) by APPEND + = DROP 0 (TAKE m (DROP n ls)) by above + = TAKE m (DROP n ls) by DROP_0 + If LENGTH ls < n, + Then DROP n ls = [] by DROP_LENGTH_TOO_LONG + and TAKE (n + m) ls = ls by TAKE_LENGTH_TOO_LONG + DROP n (TAKE (n + m) ls) + = DROP n ls by TAKE_LENGTH_TOO_LONG + = [] by DROP_LENGTH_TOO_LONG + = TAKE m [] by TAKE_nil + = TAKE m (DROP n ls) by DROP_LENGTH_TOO_LONG +*) +val TAKE_DROP_SWAP = store_thm( + "TAKE_DROP_SWAP", + ``!ls m n. TAKE m (DROP n ls) = DROP n (TAKE (n + m) ls)``, + rpt strip_tac >> + Cases_on `n <= LENGTH ls` >| [ + qabbrev_tac `x = TAKE m (DROP n ls)` >> + `DROP n (TAKE (n + m) ls) = DROP n (TAKE n ls ++ x)` by rw[TAKE_SUM, Abbr`x`] >> + `_ = DROP n (TAKE n ls) ++ DROP (n - LENGTH (TAKE n ls)) x` by rw[DROP_APPEND] >> + `_ = DROP (n - LENGTH (TAKE n ls)) x` by rw[DROP_TAKE_EQ_NIL] >> + `_ = DROP 0 x` by rw[LENGTH_TAKE_EQ] >> + rw[], + `DROP n ls = []` by rw[DROP_LENGTH_TOO_LONG] >> + `TAKE (n + m) ls = ls` by rw[TAKE_LENGTH_TOO_LONG] >> + rw[] + ]); + +(* Theorem: TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) = l1 *) +(* Proof: + TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) + = TAKE (LENGTH l1) (l1 ++ LUPDATE x k l2) by LUPDATE_APPEND2 + = l1 by TAKE_LENGTH_APPEND +*) +val TAKE_LENGTH_APPEND2 = store_thm( + "TAKE_LENGTH_APPEND2", + ``!l1 l2 x k. TAKE (LENGTH l1) (LUPDATE x (LENGTH l1 + k) (l1 ++ l2)) = l1``, + rw_tac std_ss[LUPDATE_APPEND2, TAKE_LENGTH_APPEND]); + +(* Theorem: LENGTH (TAKE n l) <= LENGTH l *) +(* Proof: by LENGTH_TAKE_EQ *) +val LENGTH_TAKE_LE = store_thm( + "LENGTH_TAKE_LE", + ``!n l. LENGTH (TAKE n l) <= LENGTH l``, + rw[LENGTH_TAKE_EQ]); + +(* Theorem: ALL_DISTINCT ls ==> + !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F *) +(* Proof: + By induction on ls. + Base: ALL_DISTINCT [] ==> !k e. MEM e (TAKE k []) /\ MEM e (DROP k []) ==> F + MEM e (TAKE k []) = MEM e [] = F by TAKE_nil, MEM + MEM e (DROP k []) = MEM e [] = F by DROP_nil, MEM + Step: ALL_DISTINCT ls ==> + !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F ==> + !h. ALL_DISTINCT (h::ls) ==> + !k e. MEM e (TAKE k (h::ls)) /\ MEM e (DROP k (h::ls)) ==> F + Note ~MEM h ls /\ ALL_DISTINCT ls by ALL_DISTINCT + If k = 0, + MEM e (TAKE 0 (h::ls)) + <=> MEM e [] = F by TAKE_0, MEM + hence true. + If k <> 0, + MEM e (TAKE k (h::ls)) + <=> MEM e (h::TAKE (k - 1) ls) by TAKE_def, k <> 0 + <=> e = h \/ MEM e (TAKE (k - 1) ls) by MEM + MEM e (DROP k (h::ls)) + <=> MEM e (DROP (k - 1) ls) by DROP_def, k <> 0 + ==> MEM e ls by MEM_DROP_IMP + If e = h, + this contradicts ~MEM h ls. + If MEM e (TAKE (k - 1) ls) + this contradicts the induction hypothesis. +*) +Theorem ALL_DISTINCT_TAKE_DROP: + !ls. ALL_DISTINCT ls ==> + !k e. MEM e (TAKE k ls) /\ MEM e (DROP k ls) ==> F +Proof + Induct >- + simp[] >> + rw[] >> + Cases_on `k = 0` >- + fs[] >> + spose_not_then strip_assume_tac >> + rfs[] >- + metis_tac[MEM_DROP_IMP] >> + metis_tac[] +QED + +(* Theorem: ALL_DISTINCT (x::y::ls) <=> ALL_DISTINCT (y::x::ls) *) +(* Proof: + If x = y, this is trivial. + If x <> y, + ALL_DISTINCT (x::y::ls) + <=> (x <> y /\ ~MEM x ls) /\ ~MEM y ls /\ ALL_DISTINCT ls by ALL_DISTINCT + <=> (y <> x /\ ~MEM y ls) /\ ~MEM x ls /\ ALL_DISTINCT ls + <=> ALL_DISTINCT (y::x::ls) by ALL_DISTINCT +*) +Theorem ALL_DISTINCT_SWAP: + !ls x y. ALL_DISTINCT (x::y::ls) <=> ALL_DISTINCT (y::x::ls) +Proof + rw[] >> + metis_tac[] +QED + +(* Theorem: ALL_DISTINCT ls /\ ls <> [] /\ j < LENGTH ls ==> (EL j ls = LAST ls <=> j + 1 = LENGTH ls) *) +(* Proof: + Note 0 < LENGTH ls by LENGTH_EQ_0 + EL j ls = LAST ls + <=> EL j ls = EL (PRE (LENGTH ls)) ls by LAST_EL + <=> j = PRE (LENGTH ls) by ALL_DISTINCT_EL_IMP, j < LENGTH ls + <=> j + 1 = LENGTH ls by SUC_PRE, ADD1, 0 < LENGTH ls +*) +Theorem ALL_DISTINCT_LAST_EL_IFF: + !ls j. ALL_DISTINCT ls /\ ls <> [] /\ j < LENGTH ls ==> (EL j ls = LAST ls <=> j + 1 = LENGTH ls) +Proof + rw[LAST_EL] >> + `0 < LENGTH ls` by metis_tac[LENGTH_EQ_0, NOT_ZERO] >> + `PRE (LENGTH ls) + 1 = LENGTH ls` by decide_tac >> + `EL j ls = EL (PRE (LENGTH ls)) ls <=> j = PRE (LENGTH ls)` by fs[ALL_DISTINCT_EL_IMP] >> + simp[] +QED + +(* Theorem: ALL_DISTINCT ls /\ j < LENGTH ls /\ ls = l1 ++ [EL j ls] ++ l2 ==> j = LENGTH l1 *) +(* Proof: + Note EL j ls = EL (LENGTH l1) ls by el_append3 + and LENGTH l1 < LENGTH ls by LENGTH_APPEND + so j = LENGTH l1 by ALL_DISTINCT_EL_IMP +*) +Theorem ALL_DISTINCT_EL_APPEND: + !ls l1 l2 j. ALL_DISTINCT ls /\ j < LENGTH ls /\ ls = l1 ++ [EL j ls] ++ l2 ==> j = LENGTH l1 +Proof + rpt strip_tac >> + `EL j ls = EL (LENGTH l1) ls` by metis_tac[el_append3] >> + `LENGTH ls = LENGTH l1 + 1 + LENGTH l2` by metis_tac[LENGTH_APPEND, LENGTH_SING] >> + `LENGTH l1 < LENGTH ls` by decide_tac >> + metis_tac[ALL_DISTINCT_EL_IMP] +QED + +(* Theorem: ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) *) +(* Proof: + By induction on l1. + Base: ALL_DISTINCT ([] ++ [x] ++ l2) <=> ALL_DISTINCT (x::([] ++ l2)) + ALL_DISTINCT ([] ++ [x] ++ l2) + <=> ALL_DISTINCT (x::l2) by APPEND_NIL + <=> ALL_DISTINCT (x::([] ++ l2)) by APPEND_NIL + Step: ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) ==> + !h. ALL_DISTINCT (h::l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(h::l1 ++ l2)) + + ALL_DISTINCT (h::l1 ++ [x] ++ l2) + <=> ALL_DISTINCT (h::(l1 ++ [x] ++ l2)) by APPEND + <=> ~MEM h (l1 ++ [x] ++ l2) /\ + ALL_DISTINCT (l1 ++ [x] ++ l2) by ALL_DISTINCT + <=> ~MEM h (l1 ++ [x] ++ l2) /\ + ALL_DISTINCT (x::(l1 ++ l2)) by induction hypothesis + <=> ~MEM h (x::(l1 ++ l2)) /\ + ALL_DISTINCT (x::(l1 ++ l2)) by MEM_APPEND_3 + <=> ALL_DISTINCT (h::x::(l1 ++ l2)) by ALL_DISTINCT + <=> ALL_DISTINCT (x::h::(l1 ++ l2)) by ALL_DISTINCT_SWAP + <=> ALL_DISTINCT (x::(h::l1 ++ l2)) by APPEND +*) +Theorem ALL_DISTINCT_APPEND_3: + !l1 x l2. ALL_DISTINCT (l1 ++ [x] ++ l2) <=> ALL_DISTINCT (x::(l1 ++ l2)) +Proof + rpt strip_tac >> + Induct_on `l1` >- + simp[] >> + rpt strip_tac >> + `ALL_DISTINCT (h::l1 ++ [x] ++ l2) <=> ALL_DISTINCT (h::(l1 ++ [x] ++ l2))` by rw[] >> + `_ = (~MEM h (l1 ++ [x] ++ l2) /\ ALL_DISTINCT (l1 ++ [x] ++ l2))` by rw[] >> + `_ = (~MEM h (l1 ++ [x] ++ l2) /\ ALL_DISTINCT (x::(l1 ++ l2)))` by rw[] >> + `_ = (~MEM h (x::(l1 ++ l2)) /\ ALL_DISTINCT (x::(l1 ++ l2)))` by rw[MEM_APPEND_3] >> + `_ = ALL_DISTINCT (h::x::(l1 ++ l2))` by rw[] >> + `_ = ALL_DISTINCT (x::h::(l1 ++ l2))` by rw[ALL_DISTINCT_SWAP] >> + `_ = ALL_DISTINCT (x::(h::l1 ++ l2))` by metis_tac[APPEND] >> + simp[] +QED + +(* Theorem: ALL_DISTINCT l ==> !x. MEM x l <=> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 *) +(* Proof: + If part: MEM x l ==> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 + Note ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p2 by MEM_SPLIT_APPEND_last + Now ALL_DISTINCT (p1 ++ [x]) by ALL_DISTINCT_APPEND, ALL_DISTINCT l + But MEM x [x] by MEM + so ~MEM x p1 by ALL_DISTINCT_APPEND + + Only-if part: MEM x (p1 ++ [x] ++ p2), true by MEM_APPEND +*) +Theorem MEM_SPLIT_APPEND_distinct: + !l. ALL_DISTINCT l ==> !x. MEM x l <=> ?p1 p2. (l = p1 ++ [x] ++ p2) /\ ~MEM x p1 /\ ~MEM x p2 +Proof + rw[EQ_IMP_THM] >- + metis_tac[MEM_SPLIT_APPEND_last, ALL_DISTINCT_APPEND, MEM] >> + rw[] +QED + +(* Theorem: MEM x ls <=> + ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (TAKE k ls) *) +(* Proof: + If part: MEM x ls ==> ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (TAKE k ls) + Note ?pfx sfx. ls = pfx ++ [x] ++ sfx /\ ~MEM x pfx + by MEM_SPLIT_APPEND_first + Take k = LENGTH pfx. + Then k < LENGTH ls by LENGTH_APPEND + and EL k ls + = EL k (pfx ++ [x] ++ sfx) + = x by el_append3 + and TAKE k ls ++ x::DROP (k+1) ls + = TAKE k (pfx ++ [x] ++ sfx) ++ + [x] ++ + DROP (k+1) ((pfx ++ [x] ++ sfx)) + = pfx ++ [x] ++ by TAKE_APPEND1 + (DROP (k+1)(pfx + [x]) + ++ sfx by DROP_APPEND1 + = pfx ++ [x] ++ sfx by DROP_LENGTH_NIL + = ls + and TAKE k ls = pfx by TAKE_APPEND1 + Only-if part: k < LENGTH ls /\ ls = TAKE k ls ++ [EL k ls] ++ DROP (k + 1) ls /\ + ~MEM (EL k ls) (TAKE k ls) ==> MEM (EL k ls) ls + This is true by EL_MEM, just need k < LENGTH ls +*) +Theorem MEM_SPLIT_TAKE_DROP_first: + !ls x. MEM x ls <=> + ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (TAKE k ls) +Proof + rw[EQ_IMP_THM] >| [ + imp_res_tac MEM_SPLIT_APPEND_first >> + qexists_tac `LENGTH pfx` >> + rpt strip_tac >- + fs[] >- + fs[el_append3] >- + fs[TAKE_APPEND1, DROP_APPEND1] >> + `TAKE (LENGTH pfx) ls = pfx` by rw[TAKE_APPEND1] >> + fs[], + fs[EL_MEM] + ] +QED + +(* Theorem: MEM x ls <=> + ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (DROP (k+1) ls) *) +(* Proof: + If part: MEM x ls ==> ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (DROP (k+1) ls) + Note ?pfx sfx. ls = pfx ++ [x] ++ sfx /\ ~MEM x sfx + by MEM_SPLIT_APPEND_last + Take k = LENGTH pfx. + Then k < LENGTH ls by LENGTH_APPEND + and EL k ls + = EL k (pfx ++ [x] ++ sfx) + = x by el_append3 + and TAKE k ls ++ x::DROP (k+1) ls + = TAKE k (pfx ++ [x] ++ sfx) ++ + [x] ++ + DROP (k+1) ((pfx ++ [x] ++ sfx)) + = pfx ++ [x] ++ by TAKE_APPEND1 + (DROP (k+1)(pfx + [x]) + ++ sfx by DROP_APPEND1 + = pfx ++ [x] ++ sfx by DROP_LENGTH_NIL + = ls + and DROP (k + 1) ls) = sfx by DROP_APPEND1, DROP_LENGTH_NIL + Only-if part: k < LENGTH ls /\ ls = TAKE k ls ++ [EL k ls] ++ DROP (k + 1) ls /\ + ~MEM (EL k ls) (DROP (k+1) ls)) ==> MEM (EL k ls) ls + This is true by EL_MEM, just need k < LENGTH ls +*) +Theorem MEM_SPLIT_TAKE_DROP_last: + !ls x. MEM x ls <=> + ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ ~MEM x (DROP (k+1) ls) +Proof + rw[EQ_IMP_THM] >| [ + imp_res_tac MEM_SPLIT_APPEND_last >> + qexists_tac `LENGTH pfx` >> + rpt strip_tac >- + fs[] >- + fs[el_append3] >- + fs[TAKE_APPEND1, DROP_APPEND1] >> + `DROP (LENGTH pfx + 1) ls = sfx` by rw[DROP_APPEND1] >> + fs[], + fs[EL_MEM] + ] +QED + +(* Theorem: ALL_DISTINCT ls ==> + !x. MEM x ls <=> + ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ + ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k+1) ls) *) +(* Proof: + If part: MEM x ls ==> ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ + ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k+1) ls) + Note ?p1 p2. ls = p1 ++ [x] ++ p2 /\ ~MEM x p1 /\ ~MEM x p2 + by MEM_SPLIT_APPEND_distinct + Take k = LENGTH p1. + Then k < LENGTH ls by LENGTH_APPEND + and EL k ls + = EL k (p1 ++ [x] ++ p2) + = x by el_append3 + and TAKE k ls ++ x::DROP (k+1) ls + = TAKE k (p1 ++ [x] ++ p2) ++ + [x] ++ + DROP (k+1) ((p1 ++ [x] ++ p2)) + = p1 ++ [x] ++ by TAKE_APPEND1 + (DROP (k+1)(p1 + [x]) + ++ p2 by DROP_APPEND1 + = p1 ++ [x] ++ p2 by DROP_LENGTH_NIL + = ls + and TAKE k ls = p1 by TAKE_APPEND1 + and DROP (k + 1) ls) = p2 by DROP_APPEND1, DROP_LENGTH_NIL + Only-if part: k < LENGTH ls /\ ls = TAKE k ls ++ [EL k ls] ++ DROP (k + 1) ls /\ + ~MEM (EL k ls) (TAKE k ls) /\ ~MEM (EL k ls) (DROP (k+1) ls)) ==> MEM (EL k ls) ls + This is true by EL_MEM, just need k < LENGTH ls +*) +Theorem MEM_SPLIT_TAKE_DROP_distinct: + !ls. ALL_DISTINCT ls ==> + !x. MEM x ls <=> + ?k. k < LENGTH ls /\ x = EL k ls /\ + ls = TAKE k ls ++ x::DROP (k+1) ls /\ + ~MEM x (TAKE k ls) /\ ~MEM x (DROP (k+1) ls) +Proof + rw[EQ_IMP_THM] >| [ + `?p1 p2. ls = p1 ++ [x] ++ p2 /\ ~MEM x p1 /\ ~MEM x p2` by rw[GSYM MEM_SPLIT_APPEND_distinct] >> + qexists_tac `LENGTH p1` >> + rpt strip_tac >- + fs[] >- + fs[el_append3] >- + fs[TAKE_APPEND1, DROP_APPEND1] >- + rfs[TAKE_APPEND1] >> + `DROP (LENGTH p1 + 1) ls = p2` by rw[DROP_APPEND1] >> + fs[], + fs[EL_MEM] + ] +QED + +(* ------------------------------------------------------------------------- *) +(* More about List Filter. *) +(* ------------------------------------------------------------------------- *) + +(* Idea: the j-th element of FILTER must have j elements filtered beforehand. *) + +(* Theorem: let fs = FILTER P ls in ls = l1 ++ x::l2 /\ P x ==> + x = EL (LENGTH (FILTER P l1)) fs *) +(* Proof: + Let l3 = x::l2, then ls = l1 ++ l3. + Let j = LENGTH (FILTER P l1). + EL j fs + = EL j (FILTER P ls) by given + = EL j (FILTER P l1 ++ FILTER P l3) by FILTER_APPEND_DISTRIB + = EL 0 (FILTER P l3) by EL_APPEND, j = LENGTH (FILTER P l1) + = EL 0 (FILTER P (x::l2)) by notation + = EL 0 (x::FILTER P l2) by FILTER, P x + = x by HD +*) +Theorem FILTER_EL_IMP: + !P ls l1 l2 x. let fs = FILTER P ls in ls = l1 ++ x::l2 /\ P x ==> + x = EL (LENGTH (FILTER P l1)) fs +Proof + rw_tac std_ss[] >> + qabbrev_tac `l3 = x::l2` >> + qabbrev_tac `j = LENGTH (FILTER P l1)` >> + `EL j fs = EL j (FILTER P l1 ++ FILTER P l3)` by simp[FILTER_APPEND_DISTRIB, Abbr`fs`] >> + `_ = EL 0 (FILTER P (x::l2))` by simp[EL_APPEND, Abbr`j`, Abbr`l3`] >> + fs[] +QED + +(* Theorem: let fs = FILTER P ls in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ j < LENGTH fs ==> + (x = EL j fs <=> P x /\ j = LENGTH (FILTER P l1)) *) +(* Proof: + Let k = LENGTH (FILTER P l1). + If part: j < LENGTH fs /\ x = EL j fs ==> P x /\ j = k + Note j < LENGTH fs /\ x = EL j fs by given + ==> MEM x fs by MEM_EL + ==> P x by MEM_FILTER + Thus x = EL k fs by FILTER_EL_IMP + Let l3 = x::l2, then ls = l1 ++ l3. + Then FILTER P l3 = x :: FILTER P l2 by FILTER + or FILTER P l3 <> [] by NOT_NIL_CONS + or LENGTH (FILTER P l3) <> 0 by LENGTH_EQ_0, [1] + + LENGTH fs + = LENGTH (FILTER P ls) by notation + = LENGTH (FILTER P l1 ++ FILTER P l3) by FILTER_APPEND_DISTRIB + = k + LENGTH (FILTER P l3) by LENGTH_APPEND + Thus k < LENGTH fs by [1] + + Note ALL_DISTINCT ls + ==> ALL_DISTINCT fs by FILTER_ALL_DISTINCT + With x = EL j fs = EL k fs by above + and j < LENGTH fs /\ k < LENGTH fs by above + ==> j = k by ALL_DISTINCT_EL_IMP + + Only-if part: j < LENGTH fs /\ P x /\ j = k ==> x = EL j fs + This is true by FILTER_EL_IMP +*) +Theorem FILTER_EL_IFF: + !P ls l1 l2 x j. let fs = FILTER P ls in ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ j < LENGTH fs ==> + (x = EL j fs <=> P x /\ j = LENGTH (FILTER P l1)) +Proof + rw_tac std_ss[] >> + qabbrev_tac `k = LENGTH (FILTER P l1)` >> + simp[EQ_IMP_THM] >> + ntac 2 strip_tac >| [ + `MEM x fs` by metis_tac[MEM_EL] >> + `P x` by fs[MEM_FILTER, Abbr`fs`] >> + qabbrev_tac `ls = l1 ++ x::l2` >> + `EL j fs = EL k fs` by metis_tac[FILTER_EL_IMP] >> + qabbrev_tac `l3 = x::l2` >> + `FILTER P l3 = x :: FILTER P l2` by simp[Abbr`l3`] >> + `LENGTH (FILTER P l3) <> 0` by fs[] >> + `fs = FILTER P l1 ++ FILTER P l3` by fs[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`ls`] >> + `LENGTH fs = k + LENGTH (FILTER P l3)` by fs[Abbr`k`] >> + `k < LENGTH fs` by decide_tac >> + `ALL_DISTINCT fs` by simp[FILTER_ALL_DISTINCT, Abbr`fs`] >> + metis_tac[ALL_DISTINCT_EL_IMP], + metis_tac[FILTER_EL_IMP] + ] +QED + +(* Derive theorems for head = (EL 0 fs) *) + +(* Theorem: ls = l1 ++ x::l2 /\ P x /\ FILTER P l1 = [] ==> x = HD (FILTER P ls) *) +(* Proof: + Note FILTER P l1 = [] by given + ==> LENGTH (FILTER P l1) = 0 by LENGTH + Thus x = EL 0 (FILTER P ls) by FILTER_EL_IMP + = HD (FILTER P ls) by EL +*) +Theorem FILTER_HD: + !P ls l1 l2 x. ls = l1 ++ x::l2 /\ P x /\ FILTER P l1 = [] ==> x = HD (FILTER P ls) +Proof + metis_tac[LENGTH, FILTER_EL_IMP, EL] +QED + +(* Theorem: ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> + (x = HD (FILTER P ls) <=> FILTER P l1 = []) *) +(* Proof: + Let fs = FILTER P ls. + Note MEM x ls by MEM_APPEND, MEM + and P x ==> fs <> [] by MEM_FILTER, NIL_NO_MEM + so 0 < LENGTH fs by LENGTH_EQ_0 + Thus x = HD fs + = EL 0 fs by EL + <=> LENGTH (FILTER P l1) = 0 by FILTER_EL_IFF + <=> FILTER P l1 = [] by LENGTH_EQ_0 +*) +Theorem FILTER_HD_IFF: + !P ls l1 l2 x. ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> + (x = HD (FILTER P ls) <=> FILTER P l1 = []) +Proof + rpt strip_tac >> + qabbrev_tac `fs = FILTER P ls` >> + `MEM x ls` by metis_tac[MEM_APPEND, MEM] >> + `MEM x fs` by fs[MEM_FILTER, Abbr`fs`] >> + `0 < LENGTH fs` by metis_tac[NIL_NO_MEM, LENGTH_EQ_0, NOT_ZERO] >> + metis_tac[FILTER_EL_IFF, EL, LENGTH_EQ_0] +QED + +(* Derive theorems for last = (EL (LENGTH fs - 1) fs) *) + +(* Theorem: ls = l1 ++ x::l2 /\ P x /\ FILTER P l2 = [] ==> + x = LAST (FILTER P ls) *) +(* Proof: + Let fs = FILTER P ls, + k = LENGTH fs. + Note MEM x ls by MEM_APPEND, MEM + and P x ==> fs <> [] by MEM_FILTER, NIL_NO_MEM + so 0 < LENGTH fs = k by LENGTH_EQ_0 + + Note FILTER P l2 = [] by given + ==> LENGTH (FILTER P l2) = 0 by LENGTH + k = LENGTH fs + = LENGTH (FILTER P ls) by notation + = LENGTH (FILTER P l1) + 1 by FILTER_APPEND_DISTRIB, ONE + or LENGTH (FILTER P l1) = PRE k + Thus x = EL (PRE k) fs by FILTER_EL_IMP + = LAST fs by LAST_EL, fs <> [] +*) +Theorem FILTER_LAST: + !P ls l1 l2 x. ls = l1 ++ x::l2 /\ P x /\ FILTER P l2 = [] ==> + x = LAST (FILTER P ls) +Proof + rpt strip_tac >> + qabbrev_tac `fs = FILTER P ls` >> + qabbrev_tac `k = LENGTH fs` >> + `MEM x ls` by metis_tac[MEM_APPEND, MEM] >> + `MEM x fs` by fs[MEM_FILTER, Abbr`fs`] >> + `fs <> [] /\ 0 < k` by metis_tac[NIL_NO_MEM, LENGTH_EQ_0, NOT_ZERO] >> + `k = LENGTH (FILTER P l1) + 1` by fs[FILTER_APPEND_DISTRIB, Abbr`k`, Abbr`fs`] >> + `LENGTH (FILTER P l1) = PRE k` by decide_tac >> + metis_tac[FILTER_EL_IMP, LAST_EL] +QED + +(* Theorem: ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> + (x = LAST (FILTER P ls) <=> FILTER P l2 = []) *) +(* Proof: + Let fs = FILTER P ls, + k = LENGTH fs, + j = LENGTH (FILTER P l1). + Note MEM x ls by MEM_APPEND, MEM + and P x ==> fs <> [] by MEM_FILTER, NIL_NO_MEM + so 0 < LENGTH fs = k by LENGTH_EQ_0 + and PRE k < k by arithmetic + + k = LENGTH fs + = LENGTH (FILTER P ls) by notation + = j + 1 + LENGTH (FILTER P l2) by FILTER_APPEND_DISTRIB, ONE + so j = PRE k <=> LENGTH (FILTER P l2) = 0 by arithmetic + + Thus x = LAST fs + = EL (PRE k) fs by LAST_EL + <=> PRE k = j by FILTER_EL_IFF + <=> LENGTH (FILTER P l2) = 0 by above + <=> FILTER P l2 = [] by LENGTH_EQ_0 +*) +Theorem FILTER_LAST_IFF: + !P ls l1 l2 x. ALL_DISTINCT ls /\ ls = l1 ++ x::l2 /\ P x ==> + (x = LAST (FILTER P ls) <=> FILTER P l2 = []) +Proof + rpt strip_tac >> + qabbrev_tac `fs = FILTER P ls` >> + qabbrev_tac `k = LENGTH fs` >> + qabbrev_tac `j = LENGTH (FILTER P l1)` >> + `MEM x ls` by metis_tac[MEM_APPEND, MEM] >> + `MEM x fs` by fs[MEM_FILTER, Abbr`fs`] >> + `fs <> [] /\ 0 < k` by metis_tac[NIL_NO_MEM, LENGTH_EQ_0, NOT_ZERO] >> + `k = j + 1 + LENGTH (FILTER P l2)` by fs[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`k`, Abbr`j`] >> + `PRE k < k /\ (j = PRE k <=> LENGTH (FILTER P l2) = 0)` by decide_tac >> + metis_tac[FILTER_EL_IFF, LAST_EL, LENGTH_EQ_0] +QED + +(* Idea: for FILTER over a range, the range between successive filter elements is filtered. *) + +(* Theorem: let fs = FILTER P ls; j = LENGTH (FILTER P l1) in + ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y /\ FILTER P l2 = [] ==> + x = EL j fs /\ y = EL (j + 1) fs *) +(* Proof: + Let l4 = y::l3, then + ls = l1 ++ x::l2 ++ l4 + = l1 ++ x::(l2 ++ l4) by APPEND_ASSOC_CONS + Thus x = EL j fs by FILTER_EL_IMP + + Now let l5 = l1 ++ x::l2, + k = LENGTH (FILTER P l5). + Then ls = l5 ++ y::l3 by APPEND_ASSOC + and y = EL k fs by FILTER_EL_IMP + + Note FILTER P l5 + = FILTER P l1 ++ FILTER P (x::l2) by FILTER_APPEND_DISTRIB + = FILTER P l1 ++ x :: FILTER P l2 by FILTER + = FILTER P l1 ++ [x] by FILTER P l2 = [] + and k = LENGTH (FILTER P l5) + = LENGTH (FILTER P l1 ++ [x]) by above + = j + 1 by LENGTH_APPEND +*) +Theorem FILTER_EL_NEXT: + !P ls l1 l2 l3 x y. let fs = FILTER P ls; j = LENGTH (FILTER P l1) in + ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y /\ FILTER P l2 = [] ==> + x = EL j fs /\ y = EL (j + 1) fs +Proof + rw_tac std_ss[] >| [ + qabbrev_tac `l4 = y::l3` >> + qabbrev_tac `ls = l1 ++ x::l2 ++ l4` >> + `ls = l1 ++ x::(l2 ++ l4)` by simp[Abbr`ls`] >> + metis_tac[FILTER_EL_IMP], + qabbrev_tac `l5 = l1 ++ x::l2` >> + qabbrev_tac `ls = l5 ++ y::l3` >> + `FILTER P l5 = FILTER P l1 ++ [x]` by fs[FILTER_APPEND_DISTRIB, Abbr`l5`] >> + `LENGTH (FILTER P l5) = j + 1` by fs[Abbr`j`] >> + metis_tac[FILTER_EL_IMP] + ] +QED + +(* Theorem: let fs = FILTER P ls; j = LENGTH (FILTER P l1) in + ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> + (x = EL j fs /\ y = EL (j + 1) fs <=> FILTER P l2 = []) *) +(* Proof: + Note fs = FILTER P ls + = FILTER P (l1 ++ x::l2 ++ y::l3) by given + = FILTER P l1 ++ + x :: FILTER P l2 ++ + y :: FILTER P l3 by FILTER_APPEND_DISTRIB, FILTER + Thus LENGTH fs + = j + SUC (LENGTH (FILTER P l2)) + + SUC (LENGTH (FILTER P l3)) by LENGTH_APPEND + or j + 2 <= LENGTH fs by arithmetic + or j < LENGTH fs, j + 1 < LENGTH fs by inequality + + Let l4 = y::l3, then + ls = l1 ++ x::l2 ++ l4 + = l1 ++ x::(l2 ++ l4) by APPEND_ASSOC_CONS + Thus x = EL j fs by FILTER_EL_IFF, j < LENGTH fs + + Now let l5 = l1 ++ x::l2, + k = LENGTH (FILTER P l5). + Then ls = l5 ++ y::l3 by APPEND_ASSOC + and fs = FILTER P l5 ++ + y :: FILTER P l3 by FILTER_APPEND_DISTRIB, FILTER + so LENGTH fs = k + SUC (LENGTH P l3) by LENGTH_APPEND + Thus k < LENGTH fs + and y = EL k fs by FILTER_EL_IFF + + Also FILTER P l5 = FILTER P l1 ++ + x :: FILTER P l2 by FILTER_APPEND_DISTRIB, FILTER + so k = j + SUC (LENGTH (FILTER P l2)) by LENGTH_APPEND + Thus k = j + 1 + <=> LENGTH (FILTER P l2) = 0 by arithmetic + + Note ALL_DISTINCT fs by FILTER_ALL_DISTINCT + so EL k fs = EL (j + 1) fs + <=> k = j + 1 + <=> LENGTH (FILTER P l2) = 0 by above + <=> FILTER P l2 = [] by LENGTH_EQ_0 +*) +Theorem FILTER_EL_NEXT_IFF: + !P ls l1 l2 l3 x y. let fs = FILTER P ls; j = LENGTH (FILTER P l1) in + ALL_DISTINCT ls /\ ls = l1 ++ x::l2 ++ y::l3 /\ P x /\ P y ==> + (x = EL j fs /\ y = EL (j + 1) fs <=> FILTER P l2 = []) +Proof + rw_tac std_ss[] >> + qabbrev_tac `ls = l1 ++ x::l2 ++ y::l3` >> + `j + 2 <= LENGTH fs` by + (`fs = FILTER P l1 ++ x::FILTER P l2 ++ y::FILTER P l3` by simp[FILTER_APPEND_DISTRIB, Abbr`fs`, Abbr`ls`] >> + `LENGTH fs = j + SUC (LENGTH (FILTER P l2)) + SUC (LENGTH (FILTER P l3))` by fs[Abbr`j`] >> + decide_tac) >> + `j < LENGTH fs` by decide_tac >> + qabbrev_tac `l4 = y::l3` >> + `ls = l1 ++ x::(l2 ++ l4)` by simp[Abbr`ls`] >> + `x = EL j fs` by metis_tac[FILTER_EL_IFF] >> + qabbrev_tac `l5 = l1 ++ x::l2` >> + qabbrev_tac `k = LENGTH (FILTER P l5)` >> + `ls = l5 ++ y::l3` by simp[Abbr`l5`, Abbr`ls`] >> + `k < LENGTH fs /\ (k = j + 1 <=> FILTER P l2 = [])` by + (`fs = FILTER P l5 ++ y::FILTER P l3` by rfs[FILTER_APPEND_DISTRIB, Abbr`fs`] >> + `LENGTH fs = k + SUC (LENGTH (FILTER P l3))` by fs[Abbr`k`] >> + `FILTER P l5 = FILTER P l1 ++ x :: FILTER P l2` by rfs[FILTER_APPEND_DISTRIB, Abbr`l5`] >> + `k = j + SUC (LENGTH (FILTER P l2))` by fs[Abbr`k`, Abbr`j`] >> + simp[]) >> + `y = EL k fs` by metis_tac[FILTER_EL_IFF] >> + `j + 1 < LENGTH fs` by decide_tac >> + `ALL_DISTINCT fs` by simp[FILTER_ALL_DISTINCT, Abbr`fs`] >> + metis_tac[ALL_DISTINCT_EL_IMP] +QED + +(* ------------------------------------------------------------------------- *) +(* Unit-List and Mono-List *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: (LENGTH l = 1) ==> SING (set l) *) +(* Proof: + Since ?x. l = [x] by LENGTH_EQ_1 + set l = {x} by LIST_TO_SET_DEF + or SING (set l) by SING_DEF +*) +val SING_LIST_TO_SET = store_thm((* was: LIST_TO_SET_SING *) + "SING_LIST_TO_SET", + ``!l. (LENGTH l = 1) ==> SING (set l)``, + rw[LENGTH_EQ_1, SING_DEF] >> + `set [x] = {x}` by rw[] >> + metis_tac[]); + +(* Mono-list Theory: a mono-list is a list l with SING (set l) *) + +(* Theorem: Two mono-lists are equal if their lengths and sets are equal. + SING (set l1) /\ SING (set l2) ==> + ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) *) +(* Proof: + By induction on l1. + Base case: !l2. SING (set []) /\ SING (set l2) ==> + (([] = l2) <=> (LENGTH [] = LENGTH l2) /\ (set [] = set l2)) + True by SING (set []) is False, by SING_EMPTY. + Step case: !l2. SING (set l1) /\ SING (set l2) ==> + ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) ==> + !h l2. SING (set (h::l1)) /\ SING (set l2) ==> + ((h::l1 = l2) <=> (LENGTH (h::l1) = LENGTH l2) /\ (set (h::l1) = set l2)) + This is to show: + (1) 1 = LENGTH l2 /\ {h} = set l2 ==> + ([h] = l2) <=> (SUC (LENGTH []) = LENGTH l2) /\ (h INSERT set [] = set l2) + If-part, l2 = [h], + LENGTH l2 = 1 = SUC 0 = SUC (LENGTH []) by LENGTH, ONE + and set l2 = set [h] = {h} = h INSERT set [] by LIST_TO_SET + Only-if part, LENGTH l2 = SUC 0 = 1 by ONE + Then ?x. l2 = [x] by LENGTH_EQ_1 + so set l2 = {x} = {h} by LIST_TO_SET + or x = h, hence l2 = [h] by EQUAL_SING + (2) set l1 = {h} /\ SING (set l2) ==> + (h::l1 = l2) <=> (SUC (LENGTH l1) = LENGTH l2) /\ (h INSERT set l1 = set l2) + If part, h::l1 = l2. + Then LENGTH l2 = LENGTH (h::l1) = SUC (LENGTH l1) by LENGTH + and set l2 = set (h::l1) = h INSERT set l1 by LIST_TO_SET + Only-if part, SUC (LENGTH l1) = LENGTH l2. + Since 0 < SUC (LENGTH l1) by prim_recTheory.LESS_0 + 0 < LENGTH l2 by LESS_TRANS + so ?k t. l2 = k::t by LENGTH_NON_NIL, list_CASES + Since LENGTH l2 = SUC (LENGTH t) by LENGTH + LENGTH l1 = LENGTH t by prim_recTheory.INV_SUC_EQ + and set l2 = k INSERT set t by LIST_TO_SET + Given SING (set l2), + either (set t = {}), or (set t = {k}) by SING_INSERT + If set t = {}, + then t = [] by LIST_TO_SET_EQ_EMPTY + and l1 = [] by LENGTH_NIL, LENGTH l1 = LENGTH t. + so set l1 = {} by LIST_TO_SET_EQ_EMPTY + contradicting set l1 = {h} by NOT_SING_EMPTY + If set t = {k}, + then set l2 = set t by ABSORPTION, set l2 = k INSERT set {k}. + or k = h by IN_SING + so l1 = t by induction hypothesis + giving l2 = h::l1 +*) +Theorem MONOLIST_EQ: + !l1 l2. SING (set l1) /\ SING (set l2) ==> + ((l1 = l2) <=> (LENGTH l1 = LENGTH l2) /\ (set l1 = set l2)) +Proof + Induct >> rw[NOT_SING_EMPTY, SING_INSERT] >| [ + Cases_on `l2` >> rw[] >> + full_simp_tac (srw_ss()) [SING_INSERT, EQUAL_SING] >> + rw[LENGTH_NIL, NOT_SING_EMPTY, EQUAL_SING] >> metis_tac[], + Cases_on `l2` >> rw[] >> + full_simp_tac (srw_ss()) [SING_INSERT, LENGTH_NIL, NOT_SING_EMPTY, + EQUAL_SING] >> + metis_tac[] + ] +QED + +(* Theorem: A non-mono-list has at least one element in tail that is distinct from its head. + ~SING (set (h::t)) ==> ?h'. h' IN set t /\ h' <> h *) +(* Proof: + By SING_INSERT, this is to show: + t <> [] /\ set t <> {h} ==> ?h'. MEM h' t /\ h' <> h + Now, t <> [] ==> set t <> {} by LIST_TO_SET_EQ_EMPTY + so ?e. e IN set t by MEMBER_NOT_EMPTY + hence MEM e t, + and MEM x t <=/=> (x = h) by EXTENSION + Therefore, e <> h, so take h' = e. +*) +val NON_MONO_TAIL_PROPERTY = store_thm( + "NON_MONO_TAIL_PROPERTY", + ``!l. ~SING (set (h::t)) ==> ?h'. h' IN set t /\ h' <> h``, + rw[SING_INSERT] >> + `set t <> {}` by metis_tac[LIST_TO_SET_EQ_EMPTY] >> + `?e. e IN set t` by metis_tac[MEMBER_NOT_EMPTY] >> + full_simp_tac (srw_ss())[EXTENSION] >> + metis_tac[]); + +(* ------------------------------------------------------------------------- *) +(* GENLIST Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: GENLIST (K e) (SUC n) = e :: GENLIST (K e) n *) +(* Proof: + GENLIST (K e) (SUC n) + = (K e) 0::GENLIST ((K e) o SUC) n by GENLIST_CONS + = e :: GENLIST ((K e) o SUC) n by K_THM + = e :: GENLIST (K e) n by K_o_THM +*) +val GENLIST_K_CONS = save_thm("GENLIST_K_CONS", + SIMP_CONV (srw_ss()) [GENLIST_CONS] + ``GENLIST (K e) (SUC n)`` |> GEN ``n:num`` |> GEN ``e``); +(* val GENLIST_K_CONS = |- !e n. GENLIST (K e) (SUC n) = e::GENLIST (K e) n: thm *) + +(* Theorem: GENLIST (K e) (n + m) = GENLIST (K e) m ++ GENLIST (K e) n *) +(* Proof: + Note (\t. e) = K e by FUN_EQ_THM + GENLIST (K e) (n + m) + = GENLIST (K e) m ++ GENLIST (\t. (K e) (t + m)) n by GENLIST_APPEND + = GENLIST (K e) m ++ GENLIST (\t. e) n by K_THM + = GENLIST (K e) m ++ GENLIST (K e) n by above +*) +val GENLIST_K_ADD = store_thm( + "GENLIST_K_ADD", + ``!e n m. GENLIST (K e) (n + m) = GENLIST (K e) m ++ GENLIST (K e) n``, + rpt strip_tac >> + `(\t. e) = K e` by rw[FUN_EQ_THM] >> + rw[GENLIST_APPEND] >> + metis_tac[]); + +(* Theorem: (!k. k < n ==> (f k = e)) ==> (GENLIST f n = GENLIST (K e) n) *) +(* Proof: + By induction on n. + Base: GENLIST f 0 = GENLIST (K e) 0 + GENLIST f 0 + = [] by GENLIST_0 + = GENLIST (K e) 0 by GENLIST_0 + Step: GENLIST f n = GENLIST (K e) n ==> + GENLIST f (SUC n) = GENLIST (K e) (SUC n) + GENLIST f (SUC n) + = SNOC (f n) (GENLIST f n) by GENLIST + = SNOC e (GENLIST f n) by applying f to n + = SNOC e (GENLIST (K e) n) by induction hypothesis + = GENLIST (K e) (SUC n) by GENLIST +*) +val GENLIST_K_LESS = store_thm( + "GENLIST_K_LESS", + ``!f e n. (!k. k < n ==> (f k = e)) ==> (GENLIST f n = GENLIST (K e) n)``, + rpt strip_tac >> + Induct_on `n` >> + rw[GENLIST]); + +(* Theorem: (!k. 0 < k /\ k <= n ==> (f k = e)) ==> (GENLIST (f o SUC) n = GENLIST (K e) n) *) +(* Proof: + Base: GENLIST (f o SUC) 0 = GENLIST (K e) 0 + GENLIST (f o SUC) 0 + = [] by GENLIST_0 + = GENLIST (K e) 0 by GENLIST_0 + Step: GENLIST (f o SUC) n = GENLIST (K e) n ==> + GENLIST (f o SUC) (SUC n) = GENLIST (K e) (SUC n) + GENLIST (f o SUC) (SUC n) + = SNOC (f n) (GENLIST (f o SUC) n) by GENLIST + = SNOC e (GENLIST (f o SUC) n) by applying f to n + = SNOC e GENLIST (K e) n by induction hypothesis + = GENLIST (K e) (SUC n) by GENLIST +*) +val GENLIST_K_RANGE = store_thm( + "GENLIST_K_RANGE", + ``!f e n. (!k. 0 < k /\ k <= n ==> (f k = e)) ==> (GENLIST (f o SUC) n = GENLIST (K e) n)``, + rpt strip_tac >> + Induct_on `n` >> + rw[GENLIST]); + +(* Theorem: GENLIST (K c) a ++ GENLIST (K c) b = GENLIST (K c) (a + b) *) +(* Proof: + Note (\t. c) = K c by FUN_EQ_THM + GENLIST (K c) (a + b) + = GENLIST (K c) (b + a) by ADD_COMM + = GENLIST (K c) a ++ GENLIST (\t. (K c) (t + a)) b by GENLIST_APPEND + = GENLIST (K c) a ++ GENLIST (\t. c) b by applying constant function + = GENLIST (K c) a ++ GENLIST (K c) b by GENLIST_FUN_EQ +*) +val GENLIST_K_APPEND = store_thm( + "GENLIST_K_APPEND", + ``!a b c. GENLIST (K c) a ++ GENLIST (K c) b = GENLIST (K c) (a + b)``, + rpt strip_tac >> + `(\t. c) = K c` by rw[FUN_EQ_THM] >> + `GENLIST (K c) (a + b) = GENLIST (K c) (b + a)` by rw[] >> + `_ = GENLIST (K c) a ++ GENLIST (\t. (K c) (t + a)) b` by rw[GENLIST_APPEND] >> + rw[GENLIST_FUN_EQ]); + +(* Theorem: GENLIST (K c) n ++ [c] = [c] ++ GENLIST (K c) n *) +(* Proof: + GENLIST (K c) n ++ [c] + = GENLIST (K c) n ++ GENLIST (K c) 1 by GENLIST_1 + = GENLIST (K c) (n + 1) by GENLIST_K_APPEND + = GENLIST (K c) (1 + n) by ADD_COMM + = GENLIST (K c) 1 ++ GENLIST (K c) n by GENLIST_K_APPEND + = [c] ++ GENLIST (K c) n by GENLIST_1 +*) +val GENLIST_K_APPEND_K = store_thm( + "GENLIST_K_APPEND_K", + ``!c n. GENLIST (K c) n ++ [c] = [c] ++ GENLIST (K c) n``, + metis_tac[GENLIST_K_APPEND, GENLIST_1, ADD_COMM, combinTheory.K_THM]); + +(* Theorem: 0 < n ==> (MEM x (GENLIST (K c) n) <=> (x = c)) *) +(* Proof: + MEM x (GENLIST (K c) n + <=> ?m. m < n /\ (x = (K c) m) by MEM_GENLIST + <=> ?m. m < n /\ (x = c) by K_THM + <=> (x = c) by taking m = 0, 0 < n +*) +Theorem GENLIST_K_MEM: + !x c n. 0 < n ==> (MEM x (GENLIST (K c) n) <=> (x = c)) +Proof + metis_tac[MEM_GENLIST, combinTheory.K_THM] +QED + +(* Theorem: 0 < n ==> (set (GENLIST (K c) n) = {c}) *) +(* Proof: + By induction on n. + Base: 0 < 0 ==> (set (GENLIST (K c) 0) = {c}) + Since 0 < 0 = F, hence true. + Step: 0 < n ==> (set (GENLIST (K c) n) = {c}) ==> + 0 < SUC n ==> (set (GENLIST (K c) (SUC n)) = {c}) + If n = 0, + set (GENLIST (K c) (SUC 0) + = set (GENLIST (K c) 1 by ONE + = set [(K c) 0] by GENLIST_1 + = set [c] by K_THM + = {c} by LIST_TO_SET + If n <> 0, 0 < n. + set (GENLIST (K c) (SUC n) + = set (SNOC ((K c) n) (GENLIST (K c) n)) by GENLIST + = set (SNOC c (GENLIST (K c) n) by K_THM + = c INSERT set (GENLIST (K c) n) by LIST_TO_SET_SNOC + = c INSERT {c} by induction hypothesis + = {c} by IN_INSERT + *) +Theorem GENLIST_K_SET: + !c n. 0 < n ==> (set (GENLIST (K c) n) = {c}) +Proof + rpt strip_tac >> + Induct_on `n` >- + simp[] >> + (Cases_on `n = 0` >> simp[]) >> + `0 < n` by decide_tac >> + simp[GENLIST, LIST_TO_SET_SNOC] +QED + +(* Theorem: ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) *) +(* Proof: + By induction on ls. + Base: [] <> [] ==> (SING (set []) <=> ?c. [] = GENLIST (K c) (LENGTH [])) + Since [] <> [] = F, hence true. + Step: ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) ==> + !h. h::ls <> [] ==> + (SING (set (h::ls)) <=> ?c. h::ls = GENLIST (K c) (LENGTH (h::ls))) + Note h::ls <> [] = T. + If part: SING (set (h::ls)) ==> ?c. h::ls = GENLIST (K c) (LENGTH (h::ls)) + Note SING (set (h::ls)) means + set ls = {h} by LIST_TO_SET_DEF, IN_SING + Let n = LENGTH ls, 0 < n by LENGTH_NON_NIL + Note ls <> [] by LIST_TO_SET, IN_SING, MEMBER_NOT_EMPTY + and SING (set ls) by SING_DEF + ==> ?c. ls = GENLIST (K c) n by induction hypothesis + so set ls = {c} by GENLIST_K_SET, 0 < n + ==> h = c by IN_SING + GENLIST (K c) (LENGTH (h::ls) + = (K c) h :: ls by GENLIST_K_CONS + = c :: ls by K_THM + = h::ls by h = c + Only-if part: ?c. h::ls = GENLIST (K c) (LENGTH (h::ls)) ==> SING (set (h::ls)) + set (h::ls) + = set (GENLIST (K c) (LENGTH (h::ls))) by given + = set ((K c) h :: GENLIST (K c) (LENGTH ls)) by GENLIST_K_CONS + = set (c :: GENLIST (K c) (LENGTH ls)) by K_THM + = c INSERT set (GENLIST (K c) (LENGTH ls)) by LIST_TO_SET + = c INSERT {c} by GENLIST_K_SET + = {c} by IN_INSERT + Hence SING (set (h::ls)) by SING_DEF +*) +Theorem LIST_TO_SET_SING_IFF: + !ls. ls <> [] ==> (SING (set ls) <=> ?c. ls = GENLIST (K c) (LENGTH ls)) +Proof + Induct >- + simp[] >> + (rw[EQ_IMP_THM] >> simp[]) >| [ + qexists_tac `h` >> + qabbrev_tac `n = LENGTH ls` >> + `ls <> []` by metis_tac[LIST_TO_SET, IN_SING, MEMBER_NOT_EMPTY] >> + `SING (set ls)` by fs[SING_DEF] >> + fs[] >> + `0 < n` by metis_tac[LENGTH_NON_NIL] >> + `h = c` by metis_tac[GENLIST_K_SET, IN_SING] >> + simp[GENLIST_K_CONS], + spose_not_then strip_assume_tac >> + fs[GENLIST_K_CONS] >> + `0 < LENGTH ls` by metis_tac[LENGTH_NON_NIL] >> + metis_tac[GENLIST_K_SET] + ] +QED + +(* Theorem: ALL_DISTINCT l /\ (set l = {x}) <=> (l = [x]) *) +(* Proof: + If part: ALL_DISTINCT l /\ set l = {x} ==> l = [x] + Note set l = {x} + ==> l <> [] /\ EVERY ($= x) l by LIST_TO_SET_EQ_SING + Let P = (S= x). + Note l <> [] ==> ?h t. l = h::t by list_CASES + so h = x /\ EVERY P t by EVERY_DEF + and ~(MEM h t) /\ ALL_DISTINCT t by ALL_DISTINCT + By contradiction, suppose l <> [x]. + Then t <> [] ==> ?u v. t = u::v by list_CASES + and MEM u t by MEM + but u = h by EVERY_DEF + ==> MEM h t, which contradicts ~(MEM h t). + + Only-if part: l = [x] ==> ALL_DISTINCT l /\ set l = {x} + Note ALL_DISTINCT [x] = T by ALL_DISTINCT_SING + and set [x] = {x} by MONO_LIST_TO_SET +*) +val DISTINCT_LIST_TO_SET_EQ_SING = store_thm( + "DISTINCT_LIST_TO_SET_EQ_SING", + ``!l x. ALL_DISTINCT l /\ (set l = {x}) <=> (l = [x])``, + rw[EQ_IMP_THM] >> + qabbrev_tac `P = ($= x)` >> + `!y. P y ==> (y = x)` by rw[Abbr`P`] >> + `l <> [] /\ EVERY P l` by metis_tac[LIST_TO_SET_EQ_SING, Abbr`P`] >> + `?h t. l = h::t` by metis_tac[list_CASES] >> + `(h = x) /\ (EVERY P t)` by metis_tac[EVERY_DEF] >> + `~(MEM h t) /\ ALL_DISTINCT t` by metis_tac[ALL_DISTINCT] >> + spose_not_then strip_assume_tac >> + `t <> []` by rw[] >> + `?u v. t = u::v` by metis_tac[list_CASES] >> + `MEM u t` by rw[] >> + metis_tac[EVERY_DEF]); + +(* ------------------------------------------------------------------------- *) +(* Maximum of a List *) +(* ------------------------------------------------------------------------- *) + +(* Define MAX of a list *) +val MAX_LIST_def = Define` + (MAX_LIST [] = 0) /\ + (MAX_LIST (h::t) = MAX h (MAX_LIST t)) +`; + +(* export simple recursive definition *) +(* val _ = export_rewrites["MAX_LIST_def"]; *) + +(* Theorem: MAX_LIST [] = 0 *) +(* Proof: by MAX_LIST_def *) +val MAX_LIST_NIL = save_thm("MAX_LIST_NIL", MAX_LIST_def |> CONJUNCT1); +(* val MAX_LIST_NIL = |- MAX_LIST [] = 0: thm *) + +(* Theorem: MAX_LIST (h::t) = MAX h (MAX_LIST t) *) +(* Proof: by MAX_LIST_def *) +val MAX_LIST_CONS = save_thm("MAX_LIST_CONS", MAX_LIST_def |> CONJUNCT2); +(* val MAX_LIST_CONS = |- !h t. MAX_LIST (h::t) = MAX h (MAX_LIST t): thm *) + +(* export simple results *) +val _ = export_rewrites["MAX_LIST_NIL", "MAX_LIST_CONS"]; + +(* Theorem: MAX_LIST [x] = x *) +(* Proof: + MAX_LIST [x] + = MAX x (MAX_LIST []) by MAX_LIST_CONS + = MAX x 0 by MAX_LIST_NIL + = x by MAX_0 +*) +val MAX_LIST_SING = store_thm( + "MAX_LIST_SING", + ``!x. MAX_LIST [x] = x``, + rw[]); + +(* Theorem: (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l *) +(* Proof: + By induction on l. + Base: (MAX_LIST [] = 0) <=> EVERY (\x. x = 0) [] + LHS: MAX_LIST [] = 0, true by MAX_LIST_NIL + RHS: EVERY (\x. x = 0) [], true by EVERY_DEF + Step: (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l ==> + !h. (MAX_LIST (h::l) = 0) <=> EVERY (\x. x = 0) (h::l) + MAX_LIST (h::l) = 0 + <=> MAX h (MAX_LIST l) = 0 by MAX_LIST_CONS + <=> (h = 0) /\ (MAX_LIST l = 0) by MAX_EQ_0 + <=> (h = 0) /\ EVERY (\x. x = 0) l by induction hypothesis + <=> EVERY (\x. x = 0) (h::l) by EVERY_DEF +*) +val MAX_LIST_EQ_0 = store_thm( + "MAX_LIST_EQ_0", + ``!l. (MAX_LIST l = 0) <=> EVERY (\x. x = 0) l``, + Induct >> + rw[MAX_EQ_0]); + +(* Theorem: l <> [] ==> MEM (MAX_LIST l) l *) +(* Proof: + By induction on l. + Base: [] <> [] ==> MEM (MAX_LIST []) [] + Trivially true by [] <> [] = F. + Step: l <> [] ==> MEM (MAX_LIST l) l ==> + !h. h::l <> [] ==> MEM (MAX_LIST (h::l)) (h::l) + If l = [], + Note MAX_LIST [h] = h by MAX_LIST_SING + and MEM h [h] by MEM + Hence true. + If l <> [], + Let x = MAX_LIST (h::l) + = MAX h (MAX_LIST l) by MAX_LIST_CONS + ==> x = h \/ x = MAX_LIST l by MAX_CASES + If x = h, MEM x (h::l) by MEM + If x = MAX_LIST l, MEM x l by induction hypothesis +*) +val MAX_LIST_MEM = store_thm( + "MAX_LIST_MEM", + ``!l. l <> [] ==> MEM (MAX_LIST l) l``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `l = []` >- + rw[] >> + rw[] >> + metis_tac[MAX_CASES]); + +(* Theorem: MEM x l ==> x <= MAX_LIST l *) +(* Proof: + By induction on l. + Base: !x. MEM x [] ==> x <= MAX_LIST [] + Trivially true since MEM x [] = F by MEM + Step: !x. MEM x l ==> x <= MAX_LIST l ==> !h x. MEM x (h::l) ==> x <= MAX_LIST (h::l) + Note MEM x (h::l) means (x = h) \/ MEM x l by MEM + and MAX_LIST (h::l) = MAX h (MAX_LIST l) by MAX_LIST_CONS + If x = h, x <= MAX h (MAX_LIST l) by MAX_LE + If MEM x l, x <= MAX_LIST l by induction hypothesis + or x <= MAX h (MAX_LIST l) by MAX_LE, LESS_EQ_TRANS +*) +val MAX_LIST_PROPERTY = store_thm( + "MAX_LIST_PROPERTY", + ``!l x. MEM x l ==> x <= MAX_LIST l``, + Induct >- + rw[] >> + rw[MAX_LIST_CONS] >- + decide_tac >> + rw[]); + +(* Theorem: l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> y <= x) ==> (x = MAX_LIST l) *) +(* Proof: + Let m = MAX_LIST l. + Since MEM x l /\ x <= m by MAX_LIST_PROPERTY + and MEM m l ==> m <= x by MAX_LIST_MEM, implication, l <> [] + Hence x = m by EQ_LESS_EQ +*) +val MAX_LIST_TEST = store_thm( + "MAX_LIST_TEST", + ``!l. l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> y <= x) ==> (x = MAX_LIST l)``, + metis_tac[MAX_LIST_MEM, MAX_LIST_PROPERTY, EQ_LESS_EQ]); + +(* Theorem: MAX_LIST t <= MAX_LIST (h::t) *) +(* Proof: + Note MAX_LIST (h::t) = MAX h (MAX_LIST t) by MAX_LIST_def + and MAX_LIST t <= MAX h (MAX_LIST t) by MAX_IS_MAX + Thus MAX_LIST t <= MAX_LIST (h::t) +*) +val MAX_LIST_LE = store_thm( + "MAX_LIST_LE", + ``!h t. MAX_LIST t <= MAX_LIST (h::t)``, + rw_tac std_ss[MAX_LIST_def]); + +(* Theorem: (!x. f x <= g x) ==> !ls. MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls) *) +(* Proof: + By induction on ls. + Base: MAX_LIST (MAP f []) <= MAX_LIST (MAP g []) + LHS = MAX_LIST (MAP f []) = MAX_LIST [] by MAP + RHS = MAX_LIST (MAP g []) = MAX_LIST [] by MAP + Hence true. + Step: MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls) ==> + !h. MAX_LIST (MAP f (h::ls)) <= MAX_LIST (MAP g (h::ls)) + MAX_LIST (MAP f (h::ls)) + = MAX_LIST (f h::MAP f ls) by MAP + = MAX (f h) (MAX_LIST (MAP f ls)) by MAX_LIST_def + <= MAX (f h) (MAX_LIST (MAP g ls)) by induction hypothesis + <= MAX (g h) (MAX_LIST (MAP g ls)) by properties of f, g + = MAX_LIST (g h::MAP g ls) by MAX_LIST_def + = MAX_LIST (MAP g (h::ls)) by MAP +*) +val MAX_LIST_MAP_LE = store_thm( + "MAX_LIST_MAP_LE", + ``!f g. (!x. f x <= g x) ==> !ls. MAX_LIST (MAP f ls) <= MAX_LIST (MAP g ls)``, + rpt strip_tac >> + Induct_on `ls` >- + rw[] >> + rw[]); + +(* ------------------------------------------------------------------------- *) +(* Minimum of a List *) +(* ------------------------------------------------------------------------- *) + +(* Define MIN of a list *) +val MIN_LIST_def = Define` + MIN_LIST (h::t) = if t = [] then h else MIN h (MIN_LIST t) +`; + +(* Theorem: MIN_LIST [x] = x *) +(* Proof: by MIN_LIST_def *) +val MIN_LIST_SING = store_thm( + "MIN_LIST_SING", + ``!x. MIN_LIST [x] = x``, + rw[MIN_LIST_def]); + +(* Theorem: t <> [] ==> (MIN_LIST (h::t) = MIN h (MIN_LIST t)) *) +(* Proof: by MIN_LIST_def *) +val MIN_LIST_CONS = store_thm( + "MIN_LIST_CONS", + ``!h t. t <> [] ==> (MIN_LIST (h::t) = MIN h (MIN_LIST t))``, + rw[MIN_LIST_def]); + +(* export simple results *) +val _ = export_rewrites["MIN_LIST_SING", "MIN_LIST_CONS"]; + +(* Theorem: l <> [] ==> MEM (MIN_LIST l) l *) +(* Proof: + By induction on l. + Base: [] <> [] ==> MEM (MIN_LIST []) [] + Trivially true by [] <> [] = F. + Step: l <> [] ==> MEM (MIN_LIST l) l ==> + !h. h::l <> [] ==> MEM (MIN_LIST (h::l)) (h::l) + If l = [], + Note MIN_LIST [h] = h by MIN_LIST_SING + and MEM h [h] by MEM + Hence true. + If l <> [], + Let x = MIN_LIST (h::l) + = MIN h (MIN_LIST l) by MIN_LIST_CONS + ==> x = h \/ x = MIN_LIST l by MIN_CASES + If x = h, MEM x (h::l) by MEM + If x = MIN_LIST l, MEM x l by induction hypothesis +*) +val MIN_LIST_MEM = store_thm( + "MIN_LIST_MEM", + ``!l. l <> [] ==> MEM (MIN_LIST l) l``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `l = []` >- + rw[] >> + rw[] >> + metis_tac[MIN_CASES]); + +(* Theorem: l <> [] ==> !x. MEM x l ==> (MIN_LIST l) <= x *) +(* Proof: + By induction on l. + Base: [] <> [] ==> ... + Trivially true since [] <> [] = F + Step: l <> [] ==> !x. MEM x l ==> MIN_LIST l <= x ==> + !h. h::l <> [] ==> !x. MEM x (h::l) ==> MIN_LIST (h::l) <= x + Note MEM x (h::l) means (x = h) \/ MEM x l by MEM + If l = [], + MEM x [h] means x = h by MEM + and MIN_LIST [h] = h, hence true by MIN_LIST_SING + If l <> [], + MIN_LIST (h::l) = MIN h (MIN_LIST l) by MIN_LIST_CONS + If x = h, MIN h (MIN_LIST l) <= x by MIN_LE + If MEM x l, MIN_LIST l <= x by induction hypothesis + or MIN h (MIN_LIST l) <= x by MIN_LE, LESS_EQ_TRANS +*) +val MIN_LIST_PROPERTY = store_thm( + "MIN_LIST_PROPERTY", + ``!l. l <> [] ==> !x. MEM x l ==> (MIN_LIST l) <= x``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `l = []` >- + fs[MIN_LIST_SING, MEM] >> + fs[MIN_LIST_CONS, MEM]); + +(* Theorem: l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> x <= y) ==> (x = MIN_LIST l) *) +(* Proof: + Let m = MIN_LIST l. + Since MEM x l /\ m <= x by MIN_LIST_PROPERTY + and MEM m l ==> x <= m by MIN_LIST_MEM, implication, l <> [] + Hence x = m by EQ_LESS_EQ +*) +val MIN_LIST_TEST = store_thm( + "MIN_LIST_TEST", + ``!l. l <> [] ==> !x. MEM x l /\ (!y. MEM y l ==> x <= y) ==> (x = MIN_LIST l)``, + metis_tac[MIN_LIST_MEM, MIN_LIST_PROPERTY, EQ_LESS_EQ]); + +(* Theorem: l <> [] ==> MIN_LIST l <= MAX_LIST l *) +(* Proof: + Since MEM (MIN_LIST l) l by MIN_LIST_MEM + so MIN_LIST l <= MAX_LIST l by MAX_LIST_PROPERTY +*) +val MIN_LIST_LE_MAX_LIST = store_thm( + "MIN_LIST_LE_MAX_LIST", + ``!l. l <> [] ==> MIN_LIST l <= MAX_LIST l``, + rw[MIN_LIST_MEM, MAX_LIST_PROPERTY]); + +(* Theorem: t <> [] ==> MIN_LIST (h::t) <= MIN_LIST t *) +(* Proof: + Note MIN_LIST (h::t) = MIN h (MIN_LIST t) by MIN_LIST_def, t <> [] + and MIN h (MIN_LIST t) <= MIN_LIST t by MIN_IS_MIN + Thus MIN_LIST (h::t) <= MIN_LIST t +*) +val MIN_LIST_LE = store_thm( + "MIN_LIST_LE", + ``!h t. t <> [] ==> MIN_LIST (h::t) <= MIN_LIST t``, + rw_tac std_ss[MIN_LIST_def]); + +(* Theorem: a <= b /\ c <= d ==> MIN a c <= MIN b d *) +(* Proof: by MIN_DEF *) +val MIN_LE_PAIR = prove( + ``!a b c d. a <= b /\ c <= d ==> MIN a c <= MIN b d``, + rw[]); + +(* Theorem: (!x. f x <= g x) ==> !ls. MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls) *) +(* Proof: + By induction on ls. + Base: MIN_LIST (MAP f []) <= MIN_LIST (MAP g []) + LHS = MIN_LIST (MAP f []) = MIN_LIST [] by MAP + RHS = MIN_LIST (MAP g []) = MIN_LIST [] by MAP + Hence true. + Step: MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls) ==> + !h. MIN_LIST (MAP f (h::ls)) <= MIN_LIST (MAP g (h::ls)) + If ls = [], + MIN_LIST (MAP f [h]) + = MIN_LIST [f h] by MAP + = f h by MIN_LIST_def + <= g h by properties of f, g + = MIN_LIST [g h] by MIN_LIST_def + = MIN_LIST (MAP g [h]) by MAP + Otherwise ls <> [], + MIN_LIST (MAP f (h::ls)) + = MIN_LIST (f h::MAP f ls) by MAP + = MIN (f h) (MIN_LIST (MAP f ls)) by MIN_LIST_def + <= MIN (g h) (MIN_LIST (MAP g ls)) by MIN_LE_PAIR, induction hypothesis + = MIN_LIST (g h::MAP g ls) by MIN_LIST_def + = MIN_LIST (MAP g (h::ls)) by MAP +*) +val MIN_LIST_MAP_LE = store_thm( + "MIN_LIST_MAP_LE", + ``!f g. (!x. f x <= g x) ==> !ls. MIN_LIST (MAP f ls) <= MIN_LIST (MAP g ls)``, + rpt strip_tac >> + Induct_on `ls` >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[MIN_LIST_def] >> + rw[MIN_LIST_def, MIN_LE_PAIR]); + +(* ------------------------------------------------------------------------- *) +(* Increasing and decreasing list bounds *) +(* ------------------------------------------------------------------------- *) + +(* Overload increasing list and decreasing list *) +val _ = overload_on("MONO_INC", + ``\ls:num list. !m n. m <= n /\ n < LENGTH ls ==> EL m ls <= EL n ls``); +val _ = overload_on("MONO_DEC", + ``\ls:num list. !m n. m <= n /\ n < LENGTH ls ==> EL n ls <= EL m ls``); + +(* Theorem: MONO_INC []*) +(* Proof: no member to falsify. *) +Theorem MONO_INC_NIL: + MONO_INC [] +Proof + simp[] +QED + +(* Theorem: MONO_INC (h::t) ==> MONO_INC t *) +(* Proof: + This is to show: m <= n /\ n < LENGTH t ==> EL m t <= EL n t + Note m <= n <=> SUC m <= SUC n by arithmetic + and n < LENGTH t <=> SUC n < LENGTH (h::t) by LENGTH + Thus EL (SUC m) (h::t) <= EL (SUC n) (h::t) by MONO_INC (h::t) + or EL m t <= EL n t by EL +*) +Theorem MONO_INC_CONS: + !h t. MONO_INC (h::t) ==> MONO_INC t +Proof + rw[] >> + first_x_assum (qspecl_then [`SUC m`, `SUC n`] strip_assume_tac) >> + rfs[] +QED + +(* Theorem: MONO_INC (h::t) /\ MEM x t ==> h <= x *) +(* Proof: + Note MEM x t + ==> ?n. n < LENGTH t /\ x = EL n t by MEM_EL + or SUC n < SUC (LENGTH t) by inequality + Now 0 < SUC n, or 0 <= SUC n, + and SUC n < SUC (LENGTH t) = LENGTH (h::t) by LENGTH + so EL 0 (h::t) <= EL (SUC n) (h::t) by MONO_INC (h::t) + or h <= EL n t = x by EL +*) +Theorem MONO_INC_HD: + !h t x. MONO_INC (h::t) /\ MEM x t ==> h <= x +Proof + rpt strip_tac >> + fs[MEM_EL] >> + last_x_assum (qspecl_then [`0`,`SUC n`] strip_assume_tac) >> + rfs[] +QED + +(* Theorem: MONO_DEC []*) +(* Proof: no member to falsify. *) +Theorem MONO_DEC_NIL: + MONO_DEC [] +Proof + simp[] +QED + +(* Theorem: MONO_DEC (h::t) ==> MONO_DEC t *) +(* Proof: + This is to show: m <= n /\ n < LENGTH t ==> EL n t <= EL m t + Note m <= n <=> SUC m <= SUC n by arithmetic + and n < LENGTH t <=> SUC n < LENGTH (h::t) by LENGTH + Thus EL (SUC n) (h::t) <= EL (SUC m) (h::t) by MONO_DEC (h::t) + or EL n t <= EL m t by EL +*) +Theorem MONO_DEC_CONS: + !h t. MONO_DEC (h::t) ==> MONO_DEC t +Proof + rw[] >> + first_x_assum (qspecl_then [`SUC m`, `SUC n`] strip_assume_tac) >> + rfs[] +QED + +(* Theorem: MONO_DEC (h::t) /\ MEM x t ==> x <= h *) +(* Proof: + Note MEM x t + ==> ?n. n < LENGTH t /\ x = EL n t by MEM_EL + or SUC n < SUC (LENGTH t) by inequality + Now 0 < SUC n, or 0 <= SUC n, + and SUC n < SUC (LENGTH t) = LENGTH (h::t) by LENGTH + so EL (SUC n) (h::t) <= EL 0 (h::t) by MONO_DEC (h::t) + or x = EL n t <= h by EL +*) +Theorem MONO_DEC_HD: + !h t x. MONO_DEC (h::t) /\ MEM x t ==> x <= h +Proof + rpt strip_tac >> + fs[MEM_EL] >> + last_x_assum (qspecl_then [`0`,`SUC n`] strip_assume_tac) >> + rfs[] +QED + +(* Theorem: ls <> [] /\ (!m n. m <= n ==> EL m ls <= EL n ls) ==> (MAX_LIST ls = LAST ls) *) +(* Proof: + By induction on ls. + Base: [] <> [] /\ MONO_INC [] ==> MAX_LIST [] = LAST [] + Note [] <> [] = F, hence true. + Step: ls <> [] /\ MONO_INC ls ==> MAX_LIST ls = LAST ls ==> + !h. h::ls <> [] /\ MONO_INC (h::ls) ==> MAX_LIST (h::ls) = LAST (h::ls) + If ls = [], + LHS = MAX_LIST [h] = h by MAX_LIST_def + RHS = LAST [h] = h = LHS by LAST_DEF + If ls <> [], + Note h <= LAST ls by LAST_EL_CONS, increasing property + and MONO_INC ls by EL, m <= n ==> SUC m <= SUC n + MAX_LIST (h::ls) + = MAX h (MAX_LIST ls) by MAX_LIST_def + = MAX h (LAST ls) by induction hypothesis + = LAST ls by MAX_DEF, h <= LAST ls + = LAST (h::ls) by LAST_DEF +*) +val MAX_LIST_MONO_INC = store_thm( + "MAX_LIST_MONO_INC", + ``!ls. ls <> [] /\ MONO_INC ls ==> (MAX_LIST ls = LAST ls)``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + `h <= LAST ls` by + (`LAST ls = EL (LENGTH ls) (h::ls)` by rw[LAST_EL_CONS] >> + `h = EL 0 (h::ls)` by rw[] >> + `LENGTH ls < LENGTH (h::ls)` by rw[] >> + metis_tac[DECIDE``0 <= n``]) >> + `MONO_INC ls` by + (rpt strip_tac >> + `SUC m <= SUC n` by decide_tac >> + `EL (SUC m) (h::ls) <= EL (SUC n) (h::ls)` by rw[] >> + fs[]) >> + rw[MAX_DEF, LAST_DEF]); + +(* Theorem: ls <> [] /\ MONO_DEC ls ==> (MAX_LIST ls = HD ls) *) +(* Proof: + By induction on ls. + Base: [] <> [] /\ MONO_DEC [] ==> MAX_LIST [] = HD [] + Note [] <> [] = F, hence true. + Step: ls <> [] /\ MONO_DEC ls ==> MAX_LIST ls = HD ls ==> + !h. h::ls <> [] /\ MONO_DEC (h::ls) ==> MAX_LIST (h::ls) = HD (h::ls) + If ls = [], + LHS = MAX_LIST [h] = h by MAX_LIST_def + RHS = HD [h] = h = LHS by HD + If ls <> [], + Note HD ls <= h by HD, decreasing property + and MONO_DEC ls by EL, m <= n ==> SUC m <= SUC n + MAX_LIST (h::ls) + = MAX h (MAX_LIST ls) by MAX_LIST_def + = MAX h (HD ls) by induction hypothesis + = h by MAX_DEF, HD ls <= h + = HD (h::ls) by HD +*) +val MAX_LIST_MONO_DEC = store_thm( + "MAX_LIST_MONO_DEC", + ``!ls. ls <> [] /\ MONO_DEC ls ==> (MAX_LIST ls = HD ls)``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + `HD ls <= h` by + (`HD ls = EL 1 (h::ls)` by rw[] >> + `h = EL 0 (h::ls)` by rw[] >> + `0 < LENGTH ls` by metis_tac[LENGTH_EQ_0, NOT_ZERO_LT_ZERO] >> + `1 < LENGTH (h::ls)` by rw[] >> + metis_tac[DECIDE``0 <= 1``]) >> + `MONO_DEC ls` by + (rpt strip_tac >> + `SUC m <= SUC n` by decide_tac >> + `EL (SUC n) (h::ls) <= EL (SUC m) (h::ls)` by rw[] >> + fs[]) >> + rw[MAX_DEF]); + +(* Theorem: ls <> [] /\ MONO_INC ls ==> (MIN_LIST ls = HD ls) *) +(* Proof: + By induction on ls. + Base: [] <> [] /\ MONO_INC [] ==> MIN_LIST [] = HD [] + Note [] <> [] = F, hence true. + Step: ls <> [] /\ MONO_INC ls ==> MIN_LIST ls = HD ls ==> + !h. h::ls <> [] /\ MONO_INC (h::ls) ==> MIN_LIST (h::ls) = HD (h::ls) + If ls = [], + LHS = MIN_LIST [h] = h by MIN_LIST_def + RHS = HD [h] = h = LHS by HD + If ls <> [], + Note h <= HD ls by HD, increasing property + and MONO_INC ls by EL, m <= n ==> SUC m <= SUC n + MIN_LIST (h::ls) + = MIN h (MIN_LIST ls) by MIN_LIST_def + = MIN h (HD ls) by induction hypothesis + = h by MIN_DEF, h <= HD ls + = HD (h::ls) by HD +*) +val MIN_LIST_MONO_INC = store_thm( + "MIN_LIST_MONO_INC", + ``!ls. ls <> [] /\ MONO_INC ls ==> (MIN_LIST ls = HD ls)``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + `h <= HD ls` by + (`HD ls = EL 1 (h::ls)` by rw[] >> + `h = EL 0 (h::ls)` by rw[] >> + `0 < LENGTH ls` by metis_tac[LENGTH_EQ_0, NOT_ZERO_LT_ZERO] >> + `1 < LENGTH (h::ls)` by rw[] >> + metis_tac[DECIDE``0 <= 1``]) >> + `MONO_INC ls` by + (rpt strip_tac >> + `SUC m <= SUC n` by decide_tac >> + `EL (SUC m) (h::ls) <= EL (SUC n) (h::ls)` by rw[] >> + fs[]) >> + rw[MIN_DEF]); + +(* Theorem: ls <> [] /\ MONO_DEC ls ==> (MIN_LIST ls = LAST ls) *) +(* Proof: + By induction on ls. + Base: [] <> [] /\ MONO_DEC [] ==> MIN_LIST [] = LAST [] + Note [] <> [] = F, hence true. + Step: ls <> [] /\ MONO_DEC ls ==> MIN_LIST ls = LAST ls ==> + !h. h::ls <> [] /\ MONO_DEC (h::ls) ==> MAX_LIST (h::ls) = LAST (h::ls) + If ls = [], + LHS = MIN_LIST [h] = h by MIN_LIST_def + RHS = LAST [h] = h = LHS by LAST_DEF + If ls <> [], + Note LAST ls <= h by LAST_EL_CONS, decreasing property + and MONO_DEC ls by EL, m <= n ==> SUC m <= SUC n + MIN_LIST (h::ls) + = MIN h (MIN_LIST ls) by MIN_LIST_def + = MIN h (LAST ls) by induction hypothesis + = LAST ls by MIN_DEF, LAST ls <= h + = LAST (h::ls) by LAST_DEF +*) +val MIN_LIST_MONO_DEC = store_thm( + "MIN_LIST_MONO_DEC", + ``!ls. ls <> [] /\ MONO_DEC ls ==> (MIN_LIST ls = LAST ls)``, + Induct >- + rw[] >> + rpt strip_tac >> + Cases_on `ls = []` >- + rw[] >> + `LAST ls <= h` by + (`LAST ls = EL (LENGTH ls) (h::ls)` by rw[LAST_EL_CONS] >> + `h = EL 0 (h::ls)` by rw[] >> + `LENGTH ls < LENGTH (h::ls)` by rw[] >> + metis_tac[DECIDE``0 <= n``]) >> + `MONO_DEC ls` by + (rpt strip_tac >> + `SUC m <= SUC n` by decide_tac >> + `EL (SUC n) (h::ls) <= EL (SUC m) (h::ls)` by rw[] >> + fs[]) >> + rw[MIN_DEF, LAST_DEF]); + +(* ------------------------------------------------------------------------- *) +(* Sublist: an order-preserving portion of a list *) +(* ------------------------------------------------------------------------- *) + +(* Definition of sublist *) +val sublist_def = Define` + (sublist [] x = T) /\ + (sublist (h1::t1) [] = F) /\ + (sublist (h1::t1) (h2::t2) <=> + ((h1 = h2) /\ sublist t1 t2 \/ ~(h1 = h2) /\ sublist (h1::t1) t2)) +`; + +(* Overload sublist by infix operator *) +val _ = temp_overload_on ("<=", ``sublist``); +(* +> sublist_def; +val it = |- (!x. [] <= x <=> T) /\ (!t1 h1. h1::t1 <= [] <=> F) /\ + !t2 t1 h2 h1. h1::t1 <= h2::t2 <=> + (h1 = h2) /\ t1 <= t2 \/ h1 <> h2 /\ h1::t1 <= t2: thm +*) + +(* Theorem: [] <= p *) +(* Proof: by sublist_def *) +val sublist_nil = store_thm( + "sublist_nil", + ``!p. [] <= p``, + rw[sublist_def]); + +(* Theorem: p <= q <=> h::p <= h::q *) +(* Proof: by sublist_def *) +val sublist_cons = store_thm( + "sublist_cons", + ``!h p q. p <= q <=> h::p <= h::q``, + rw[sublist_def]); + +(* Theorem: p <= [] <=> (p = []) *) +(* Proof: + If p = [], then [] <= [] by sublist_nil + If p = h::t, then h::t <= [] = F by sublist_def +*) +val sublist_of_nil = store_thm( + "sublist_of_nil", + ``!p. p <= [] <=> (p = [])``, + (Cases_on `p` >> rw[sublist_def])); + +(* Theorem: (!p q. (h::p) <= q ==> p <= q) = (!p q. p <= q ==> p <= (h::q)) *) +(* Proof: + If part: (!p q. (h::p) <= q ==> p <= q) ==> (!p q. p <= q ==> p <= (h::q)) + p <= q + ==> h::p <= h::q by sublist_cons + ==> p <= h::q by implication + Only-if part: (!p q. p <= q ==> p <= (h::q)) ==> (!p q. (h::p) <= q ==> p <= q) + (h::p) <= q + ==> (h::p) <= (h::q) by implication + ==> p <= q by sublist_cons +*) +val sublist_cons_eq = store_thm( + "sublist_cons_eq", + ``!h. (!p q. (h::p) <= q ==> p <= q) = (!p q. p <= q ==> p <= (h::q))``, + rw[EQ_IMP_THM] >> + metis_tac[sublist_cons]); + +(* Theorem: h::p <= q ==> p <= q *) +(* Proof: + By induction on q. + Base: !h p. h::p <= [] ==> p <= [] + True since h::p <= [] = F by sublist_def + + Step: !h p. h::p <= q ==> p <= q ==> + !h h' p. h'::p <= h::q ==> p <= h::q + If p = [], true by sublist_nil + If p = h''::t, + p <= h::q + <=> (h'' = h) /\ t <= q \/ h'' <> h /\ h''::t <= q by sublist_def + If h'' = h, then t <= q by sublist_def, induction hypothesis + If h'' <> h, then h''::t <= q by sublist_def, induction hypothesis +*) +val sublist_cons_remove = store_thm( + "sublist_cons_remove", + ``!h p q. h::p <= q ==> p <= q``, + Induct_on `q` >- + rw[sublist_def] >> + rpt strip_tac >> + (Cases_on `p` >> simp[sublist_def]) >> + (Cases_on `h'' = h` >> metis_tac[sublist_def])); + +(* Theorem: p <= q ==> p <= h::q *) +(* Proof: by sublist_cons_eq, sublist_cons_remove *) +val sublist_cons_include = store_thm( + "sublist_cons_include", + ``!h p q. p <= q ==> p <= h::q``, + metis_tac[sublist_cons_eq, sublist_cons_remove]); + +(* Theorem: p <= q ==> LENGTH p <= LENGTH q *) +(* Proof: + By induction on q. + Base: !p. p <= [] ==> LENGTH p <= LENGTH [] + Note p = [] by sublist_of_nil + Thus true by arithemtic + Step: !p. p <= q ==> LENGTH p <= LENGTH q ==> + !h p. p <= h::q ==> LENGTH p <= LENGTH (h::q) + If p = [], LENGTH p = 0 by LENGTH + and 0 <= LENGTH (h::q). + If p = h'::t, + If h = h', + (h::t) <= (h::q) + <=> t <= q by sublist_def, same heads + ==> LENGTH t <= LENGTH q by inductive hypothesis + ==> SUC(LENGTH t) <= SUC(LENGTH q) + = LENGTH (h::t) <= LENGTH (h::q) + If ~(h = h'), + (h'::t) <= (h::q) + <=> (h'::t) <= q by sublist_def, different heads + ==> LENGTH (h'::t) <= LENGTH q by inductive hypothesis + ==> LENGTH (h'::t) <= SUC(LENGTH q) by arithemtic + = LENGTH (h'::t) <= LENGTH (h::q) +*) +val sublist_length = store_thm( + "sublist_length", + ``!p q. p <= q ==> LENGTH p <= LENGTH q``, + Induct_on `q` >- + rw[sublist_of_nil] >> + rpt strip_tac >> + (Cases_on `p` >> simp[]) >> + (Cases_on `h = h'` >> fs[sublist_def]) >> + `LENGTH (h'::t) <= LENGTH q` by rw[] >> + `LENGTH t < LENGTH (h'::t)` by rw[LENGTH_TL_LT] >> + decide_tac); + +(* Theorem: [Reflexive] p <= p *) +(* Proof: + By induction on p. + Base: [] <= [], true by sublist_nil + Step: p <= p ==> !h. h::p <= h::p, true by sublist_cons +*) +val sublist_refl = store_thm( + "sublist_refl", + ``!p:'a list. p <= p``, + Induct >> rw[sublist_def]); + +(* Theorem: [Anti-symmetric] !p q. (p <= q) /\ (q <= p) ==> (p = q) *) +(* Proof: + By induction on q. + Base: !p. p <= [] /\ [] <= p ==> (p = []) + Note p <= [] already gives p = [] by sublist_of_nil + Step: !p. p <= q /\ q <= p ==> (p = q) ==> + !h p. p <= h::q /\ h::q <= p ==> (p = h::q) + If p = [], h::q <= [] = F by sublist_def + If p = (h'::t), + If h = h', + ((h::t) <= (h::q)) /\ ((h::q) <= (h::t)) + <=> (t <= q) and (q <= t) by sublist_def, same heads + ==> t = q by inductive hypothesis + <=> (h::t) = (h::q) by list equality + If ~(h = h'), + ((h'::t) <= (h::q)) /\ ((h::q) <= (h'::t)) + <=> ((h'::t) <= q) /\ ((h::q) <= t) by sublist_def, different heads + ==> (LENGTH (h'::t) <= LENGTH q) /\ + (LENGTH (h::q) <= LENGTH t) by sublist_length + ==> (LENGTh t < LENGTH q) /\ + (LENGTH q < LENGTH t) by LENGTH_TL_LT + = F by arithmetic + Hence the implication is T. +*) +val sublist_antisym = store_thm( + "sublist_antisym", + ``!p q:'a list. p <= q /\ q <= p ==> (p = q)``, + Induct_on `q` >- + rw[sublist_of_nil] >> + rpt strip_tac >> + Cases_on `p` >- + fs[sublist_def] >> + (Cases_on `h' = h` >> fs[sublist_def]) >> + `LENGTH (h'::t) <= LENGTH q /\ LENGTH (h::q) <= LENGTH t` by rw[sublist_length] >> + fs[LENGTH_TL_LT]); + +(* Theorem [Transitive]: for all lists p, q, r; (p <= q) /\ (q <= r) ==> (p <= r) *) +(* Proof: + By induction on list r and taking note of cases. + By induction on r. + Base: !p q. p <= q /\ q <= [] ==> p <= [] + Note q = [] by sublist_of_nil + so p = [] by sublist_of_nil + Step: !p q. p <= q /\ q <= r ==> p <= r ==> + !h p q. p <= q /\ q <= h::r ==> p <= h::r + If p = [], true by sublist_nil + If p = h'::t, to show: + h'::t <= q /\ q <= h::r ==> + (h' = h) /\ t <= r \/ + h' <> h /\ h'::t <= r by sublist_def + If q = [], [] <= h::r = F by sublist_def + If q = h''::t', this reduces to: + (1) h' = h'' /\ t <= t' /\ h'' = h /\ t' <= r ==> t <= r + Note t <= t' /\ t' <= r ==> t <= r by induction hypothesis + (2) h' = h'' /\ t <= t' /\ h'' <> h /\ h''::t' <= r ==> h''::t <= r + Note t <= t' ==> h''::t <= h''::t' by sublist_cons + With h''::t' <= r ==> h''::t <= r by induction hypothesis + (3) h' <> h'' /\ h'::t <= t' /\ h'' = h /\ t' <= r ==> + (h' = h) /\ t <= r \/ h' <> h /\ h'::t <= r + Note h'::t <= t' ==> t <= t' by sublist_cons_remove + With t' <= r ==> t <= r by induction hypothesis + Or simply h'::t <= t' /\ t' <= r + ==> h'::t <= r by induction hypothesis + Hence this is true. + (4) h' <> h'' /\ h'::t <= t' /\ h'' <> h /\ h''::t' <= r ==> + (h' = h) /\ t <= r \/ h' <> h /\ h'::t <= r + Same reasoning as (3). +*) +val sublist_trans = store_thm( + "sublist_trans", + ``!p q r:'a list. p <= q /\ q <= r ==> p <= r``, + Induct_on `r` >- + fs[sublist_of_nil] >> + rpt strip_tac >> + (Cases_on `p` >> fs[sublist_def]) >> + (Cases_on `q` >> fs[sublist_def]) >- + metis_tac[] >- + metis_tac[sublist_cons] >- + metis_tac[sublist_cons_remove] >> + metis_tac[sublist_cons_remove]); + +(* The above theorems show that sublist is a partial ordering. *) + +(* Theorem: p <= q ==> SNOC h p <= SNOC h q *) +(* Proof: + By induction on q. + Base: !h p. p <= [] ==> SNOC h p <= SNOC h [] + Note p = [] by sublist_of_nil + Thus SNOC h [] <= SNOC h [] by sublist_refl + Step: !h p. p <= q ==> SNOC h p <= SNOC h q ==> + !h h' p. p <= h::q ==> SNOC h' p <= SNOC h' (h::q) + If p = [], + Note [] <= q by sublist_nil + Thus SNOC h' [] + <= SNOC h' q by induction hypothesis + <= h::SNOC h' q by sublist_cons_include + = SNOC h' (h::q) by SNOC + If p = h''::t, + If h = h'', + Then t <= q by sublist_def, same heads + ==> SNOC h' t <= SNOC h' q by induction hypothesis + ==> h::SNOC h' t <= h::SNOC h' q by rw[sublist_cons + or SNOC h' (h::t) <= SNOC h' (h::q) by SNOC + or SNOC h' p <= SNOC h' (h::q) by p = h::t + If h <> h'', + Then p <= q by sublist_def, different heads + ==> SNOC h' p <= SNOC h' q by induction hypothesis + ==> SNOC h' p <= h::SNOC h' q by sublist_cons_include +*) +val sublist_snoc = store_thm( + "sublist_snoc", + ``!h p q. p <= q ==> SNOC h p <= SNOC h q``, + Induct_on `q` >- + rw[sublist_of_nil, sublist_refl] >> + rw[sublist_def] >> + Cases_on `p` >- + rw[sublist_nil, sublist_cons_include] >> + metis_tac[sublist_def, sublist_cons, SNOC]); + +(* Theorem: MEM x ls ==> [x] <= ls *) +(* Proof: + By induction on ls. + Base: !x. MEM x [] ==> [x] <= [] + True since MEM x [] = F. + Step: !x. MEM x ls ==> [x] <= ls ==> + !h x. MEM x (h::ls) ==> [x] <= h::ls + If x = h, + Then [h] <= h::ls by sublist_nil, sublist_cons + If x <> h, + Then MEM h ls by MEM x (h::ls) + ==> [x] <= ls by induction hypothesis + ==> [x] <= h::ls by sublist_cons_include +*) +val sublist_member_sing = store_thm( + "sublist_member_sing", + ``!ls x. MEM x ls ==> [x] <= ls``, + Induct >- + rw[] >> + rw[] >- + rw[sublist_nil, GSYM sublist_cons] >> + rw[sublist_cons_include]); + +(* Theorem: TAKE n ls <= ls *) +(* Proof: + By induction on ls. + Base: !n. TAKE n [] <= [] + LHS = TAKE n [] = [] by TAKE_def + <= [] = RHS by sublist_nil + Step: !n. TAKE n ls <= ls ==> !h n. TAKE n (h::ls) <= h::ls + If n = 0, + TAKE 0 (h::ls) + = [] by TAKE_def + <= h::ls by sublist_nil + If n <> 0, + TAKE n (h::ls) + = h::TAKE (n - 1) ls by TAKE_def + Now TAKE (n - 1) ls <= ls by induction hypothesis + Thus h::TAKE (n - 1) ls <= h::ls by sublist_cons +*) +val sublist_take = store_thm( + "sublist_take", + ``!ls n. TAKE n ls <= ls``, + Induct >- + rw[sublist_nil] >> + rpt strip_tac >> + Cases_on `n = 0` >- + rw[sublist_nil] >> + rw[GSYM sublist_cons]); + +(* Theorem: DROP n ls <= ls *) +(* Proof: + By induction on ls. + Base: !n. DROP n [] <= [] + LHS = DROP n [] = [] by DROP_def + <= [] = RHS by sublist_nil + Step: !n. DROP n ls <= ls ==> !h n. DROP n (h::ls) <= h::ls + If n = 0, + DROP 0 (h::ls) + = h::ls by DROP_def + <= h::ls by sublist_refl + If n <> 0, + DROP n (h::ls) + = DROP n ls by DROP_def + <= ls by induction hypothesis + <= h::ls by sublist_cons_include +*) +val sublist_drop = store_thm( + "sublist_drop", + ``!ls n. DROP n ls <= ls``, + Induct >- + rw[sublist_nil] >> + rpt strip_tac >> + Cases_on `n = 0` >- + rw[sublist_refl] >> + rw[sublist_cons_include]); + +(* Theorem: ls <> [] ==> TL ls <= ls *) +(* Proof: + Note TL ls = DROP 1 ls by TAIL_BY_DROP, ls <> [] + Thus TL ls <= ls by sublist_drop +*) +val sublist_tail = store_thm( + "sublist_tail", + ``!ls. ls <> [] ==> TL ls <= ls``, + rw[TAIL_BY_DROP, sublist_drop]); + +(* Theorem: ls <> [] ==> FRONT ls <= ls *) +(* Proof: + Note FRONT ls = TAKE (LENGTH ls - 1) ls by FRONT_BY_TAKE + so FRONT ls <= ls by sublist_take +*) +val sublist_front = store_thm( + "sublist_front", + ``!ls. ls <> [] ==> FRONT ls <= ls``, + rw[FRONT_BY_TAKE, sublist_take]); + +(* Theorem: ls <> [] ==> [HD ls] <= ls *) +(* Proof: HEAD_MEM, sublist_member_sing *) +val sublist_head_sing = store_thm( + "sublist_head_sing", + ``!ls. ls <> [] ==> [HD ls] <= ls``, + rw[HEAD_MEM, sublist_member_sing]); + +(* Theorem: ls <> [] ==> [LAST ls] <= ls *) +(* Proof: LAST_MEM, sublist_member_sing *) +val sublist_last_sing = store_thm( + "sublist_last_sing", + ``!ls. ls <> [] ==> [LAST ls] <= ls``, + rw[LAST_MEM, sublist_member_sing]); + +(* Theorem: l <= ls ==> !P. EVERY P ls ==> EVERY P l *) +(* Proof: + By induction on ls. + Base: !l. l <= [] ==> !P. EVERY P [] ==> EVERY P l + Note l <= [] ==> l = [] by sublist_of_nil + and EVERY P [] = T by implication, or EVERY_DEF + Step: !l. l <= ls ==> !P. EVERY P ls ==> EVERY P l ==> + !h l. l <= h::ls ==> !P. EVERY P (h::ls) ==> EVERY P l + l <= h::ls + If l = [], then EVERY P [] = T by EVERY_DEF + Otherwise, let l = k::t by list_CASES + Note EVERY P (h::ls) + ==> P h /\ EVERY P ls by EVERY_DEF + Then k::t <= h::ls + ==> k = h /\ t <= ls + or k <> h /\ k::t <= ls by sublist_def + For the first case, h = k, + P k /\ EVERY P t by induction hypothesis + ==> EVERY P (k::t) = EVERY P l by EVERY_DEF + For the second case, h <> k, + EVERY P (k::t) = EVERY P l by induction hypothesis +*) +val sublist_every = store_thm( + "sublist_every", + ``!l ls. l <= ls ==> !P. EVERY P ls ==> EVERY P l``, + Induct_on `ls` >- + rw[sublist_of_nil] >> + (Cases_on `l` >> simp[]) >> + metis_tac[sublist_def, EVERY_DEF]); + +(* ------------------------------------------------------------------------- *) +(* More sublists, applying partial order properties *) +(* ------------------------------------------------------------------------- *) + +(* Observation: +When doing induction proofs on sublists about p <= q, +Always induct on q, then take cases on p. +*) + +(* The following induction theorem is already present during definition: +> theorem "sublist_ind"; +val it = |- !P. (!x. P [] x) /\ (!h1 t1. P (h1::t1) []) /\ + (!h1 t1 h2 t2. P t1 t2 /\ P (h1::t1) t2 ==> P (h1::t1) (h2::t2)) ==> + !v v1. P v v1: thm + +Just prove it as an exercise. +*) + +(* Theorem: [Induction] For any property P satisfying, + (a) !y. P [] y = T + (b) !h x y. P x y /\ sublist x y ==> P (h::x) (h::y) + (c) !h x y. P x y /\ sublist x y ==> P x (h::y) + then !x y. sublist x y ==> P x y. +*) +(* Proof: + By induction on y. + Base: !x. x <= [] ==> P x [] + Note x = [] by sublist_of_nil + and P [] [] = T by given + Step: !x. x <= y ==> P x y ==> + !h x. x <= h::y ==> P x (h::y) + If x = [], then [] <= h::y = F by sublist_def + If x = h'::t, + If h' = h, t <= y by sublist_def, same heads + Thus P t y by induction hypothesis + and P t y /\ t <= y ==> P (h::t) (h::y) = P x (h::y) + If h' <> h, x <= y by sublist_def, different heads + Thus P x y by induction hypothesis + and P x y /\ x <= y ==> P x (h::y). +*) +val sublist_induct = store_thm( + "sublist_induct", + ``!P. (!y. P [] y) /\ + (!h x y. P x y /\ x <= y ==> P (h::x) (h::y)) /\ + (!h x y. P x y /\ x <= y ==> P x (h::y)) ==> + !x y. x <= y ==> P x y``, + ntac 2 strip_tac >> + Induct_on `y` >- + rw[sublist_of_nil] >> + rpt strip_tac >> + (Cases_on `x` >> fs[sublist_def])); + +(* +Note that from definition: +sublist_ind +|- !P. (!x. P [] x) /\ (!h1 t1. P (h1::t1) []) /\ + (!h1 t1 h2 t2. P t1 t2 /\ P (h1::t1) t2 ==> P (h1::t1) (h2::t2)) ==> + !v v1. P v v1 + +sublist_induct +|- !P. (!y. P [] y) /\ (!h x y. P x y /\ x <= y ==> P (h::x) (h::y)) /\ + (!h x y. P x y /\ x <= y ==> P x (h::y)) ==> + !x y. x <= y ==> P x y + +The second is better. +*) + +(* Theorem: p <= q /\ MEM x p ==> MEM x q *) +(* Proof: + By sublist_induct, this is to show: + (1) MEM x [] ==> MEM x q + Note MEM x [] = F by MEM + Hence true. + (2) MEM x p ==> MEM x q /\ p <= q /\ MEM x (h::p) ==> MEM x (h::q) + If x = h, then MEM h (h::q) = T by MEM + If x <> h, MEM x (h::p) + ==> MEM x p by MEM, x <> h + ==> MEM x q by induction hypothesis + ==> MEM x (h::q) by MEM, x <> h + (3) MEM x p ==> MEM x q /\ p <= q /\ MEM x p ==> MEM x (h::q) + If x = h, then MEM h (h::q) = T by MEM + If x <> h, MEM x p + ==> MEM x q by induction hypothesis + ==> MEM x (h::q) by MEM, x <> h +*) +Theorem sublist_mem: + !p q x. p <= q /\ MEM x p ==> MEM x q +Proof + rpt strip_tac >> + pop_assum mp_tac >> + pop_assum mp_tac >> + qid_spec_tac `q` >> + qid_spec_tac `p` >> + ho_match_mp_tac sublist_induct >> + rpt strip_tac >- + fs[] >- + (Cases_on `x = h` >> fs[]) >> + (Cases_on `x = h` >> fs[]) +QED + +(* Theorem: sl <= ls ==> set sl SUBSET set ls *) +(* Proof: + set sl SUBSET set ls + <=> !x. x IN set (sl) ==> x IN set ls by SUBSET_DEF + <=> !x. MEM x sl ==> MEM x ls by notation + ==> T by sublist_mem +*) +Theorem sublist_subset: + !ls sl. sl <= ls ==> set sl SUBSET set ls +Proof + metis_tac[SUBSET_DEF, sublist_mem] +QED + +(* Theorem: p <= q /\ ALL_DISTINCT q ==> ALL_DISTINCT p *) +(* Proof: + By sublist_induct, this is to show: + (1) ALL_DISTINCT q ==> ALL_DISTINCT [] + Note ALL_DISTINCT [] = T by ALL_DISTINCT + (2) ALL_DISTINCT q ==> ALL_DISTINCT p /\ p <= q /\ ALL_DISTINCT (h::q) ==> ALL_DISTINCT (h::p) + ALL_DISTINCT (h::q) + <=> ~MEM h q /\ ALL_DISTINCT q by ALL_DISTINCT + ==> ~MEM h q /\ ALL_DISTINCT p by induction hypothesis + ==> ~MEM h p /\ ALL_DISTINCT p by sublist_mem + <=> ALL_DISTINCT (h::p) by ALL_DISTINCT + (3) ALL_DISTINCT q ==> ALL_DISTINCT p /\ p <= q /\ ALL_DISTINCT (h::q) ==> ALL_DISTINCT p + ALL_DISTINCT (h::q) + ==> ALL_DISTINCT q by ALL_DISTINCT + ==> ALL_DISTINCT p by induction hypothesis +*) +Theorem sublist_ALL_DISTINCT: + !p q. p <= q /\ ALL_DISTINCT q ==> ALL_DISTINCT p +Proof + rpt strip_tac >> + pop_assum mp_tac >> + pop_assum mp_tac >> + qid_spec_tac `q` >> + qid_spec_tac `p` >> + ho_match_mp_tac sublist_induct >> + rpt strip_tac >- + simp[] >- + (fs[] >> metis_tac[sublist_mem]) >> + fs[] +QED + +(* Theorem: [Eliminate append from left]: (x ++ p) <= q ==> sublist p <= q *) +(* Proof: + By induction on the extra list x. + The induction step follows from sublist_cons_remove. + + By induction on x. + Base: !p q. [] ++ p <= q ==> p <= q + True since [] ++ p = p by APPEND + Step: !p q. x ++ p <= q ==> p <= q ==> + !h p q. h::x ++ p <= q ==> p <= q + h::x ++ p <= q + = h::(x ++ p) <= q by APPEND + ==> (x ++ p) <= q by sublist_cons_remove + ==> p <= q by induction hypothesis +*) +val sublist_append_remove = store_thm( + "sublist_append_remove", + ``!p q x. x ++ p <= q ==> p <= q``, + Induct_on `x` >> metis_tac[sublist_cons_remove, APPEND]); + +(* Theorem: [Include append to right] p <= q ==> p <= (x ++ q) *) +(* Proof: + By induction on list x. + The induction step follows by sublist_cons_include. + + By induction on x. + Base: !p q. p <= q ==> p <= [] ++ q + True since [] ++ q = q by APPEND + Step: !p q. p <= q ==> p <= x ++ q ==> + !h p q. p <= q ==> p <= h::x ++ q + p <= q + ==> p <= x ++ q by induction hypothesis + ==> p <= h::(x ++ q) by sublist_cons_include + = p <= h::x ++ q by APPEND +*) +val sublist_append_include = store_thm( + "sublist_append_include", + ``!p q x. p <= q ==> p <= x ++ q``, + Induct_on `x` >> metis_tac[sublist_cons_include, APPEND]); + +(* Theorem: [append after] p <= (p ++ q) *) +(* Proof: + By induction on list p, and note that p and (p ++ q) have the same head. + Base: !q. [] <= [] ++ q, true by sublist_nil + Step: !q. p <= p ++ q ==> !h q. h::p <= h::p ++ q + p <= p ++ q by induction hypothesis + ==> h::p <= h::(p ++ q) by sublist_cons + ==> h::p <= h::p ++ q by APPEND +*) +val sublist_append_suffix = store_thm( + "sublist_append_suffix", + ``!p q. p <= p ++ q``, + Induct_on `p` >> rw[sublist_def]); + +(* Theorem: [append before] p <= (q ++ p) *) +(* Proof: + By induction on q. + Base: !p. p <= [] ++ p + Note [] ++ p = p by APPEND + and p <= p by sublist_refl + Step: !p. p <= q ++ p ==> !h p. p <= h::q ++ p + p <= q ++ p by induction hypothesis + ==> p <= h::(q ++ p) by sublist_cons_include + = p <= h::q ++ p by APPEND +*) +val sublist_append_prefix = store_thm( + "sublist_append_prefix", + ``!p q. p <= q ++ p``, + Induct_on `q` >- + rw[sublist_refl] >> + rw[sublist_cons_include]); + +(* Theorem: [prefix append] p <= q <=> (x ++ p) <= (x ++ q) *) +(* Proof: + By induction on x. + Base: !p q. p <= q <=> [] ++ p <= [] ++ q + Since [] ++ p = p /\ [] ++ q = q by APPEND + This is trivially true. + Step: !p q. p <= q <=> x ++ p <= x ++ q ==> + !h p q. p <= q <=> h::x ++ p <= h::x ++ q + p <= q <=> x ++ p <= x ++ q by induction hypothesis + <=> h::(x ++ p) <= h::(x ++ q) by sublist_cons + <=> h::x ++ p <= h::x ++ q by APPEND +*) +val sublist_prefix = store_thm( + "sublist_prefix", + ``!x p q. p <= q <=> (x ++ p) <= (x ++ q)``, + Induct_on `x` >> metis_tac[sublist_cons, APPEND]); + +(* Theorem: [no append to left] !p q. (p ++ q) <= q ==> p = [] *) +(* Proof: + By induction on q. + Base: !p. p ++ [] <= [] ==> (p = []) + Note p ++ [] = p by APPEND + and p <= [] ==> p = [] by sublist_of_nil + Step: !p. p ++ q <= q ==> (p = []) ==> + !h p. p ++ h::q <= h::q ==> (p = []) + If p = [], true trivially. + If p = h'::t, + (h'::t) ++ (h::q) <= h::q + = h'::(t ++ h::q) <= h::q by APPEND + If h' = h, + Then t ++ h::q <= q by sublist_def, same heads + or (t ++ [h]) ++ q <= q by APPEND + ==> t ++ [h] = [] by induction hypothesis + which is F, hence h' <> h. + If h' <> h, + Then p ++ h::q <= q by sublist_def, different heads + or (p ++ [h]) ++ q <= q by APPEND + ==> (p ++ [h]) = [] by induction hypothesis + which is F, hence neither h' <> h. + All these shows that p = h'::t is impossible. +*) +val sublist_prefix_nil = store_thm( + "sublist_prefix_nil", + ``!p q. (p ++ q) <= q ==> (p = [])``, + Induct_on `q` >- + rw[sublist_of_nil] >> + rpt strip_tac >> + (Cases_on `p` >> fs[sublist_def]) >| [ + `t ++ h::q = (t ++ [h])++ q` by rw[] >> + `t ++ [h] <> []` by rw[] >> + metis_tac[], + `(t ++ h::q) <= q` by metis_tac[sublist_cons_remove] >> + `t ++ h::q = (t ++ [h])++ q` by rw[] >> + `t ++ [h] <> []` by rw[] >> + metis_tac[] + ]); + +(* Theorem: [tail append - if] p <= q ==> (p ++ [h]) <= (q ++ [h]) *) +(* Proof: + p <= q + ==> SNOC h p <= SNOC h q by sublist_snoc + ==> (p ++ [h]) <= (q ++ [h]) by SNOC_APPEND +*) +Theorem sublist_append_if: + !p q h. p <= q ==> (p ++ [h]) <= (q ++ [h]) +Proof + rw[sublist_snoc, GSYM SNOC_APPEND] +QED + +(* Theorem: [tail append - only if] p ++ [h] <= q ++ [h] ==> p <= q *) +(* Proof: + By induction on q. + Base: !p h. p ++ [h] <= [] ++ [h] ==> p <= [] + Note [] ++ [h] = [h] by APPEND + and p ++ [h] <= [h] ==> p = [] by sublist_prefix_nil + and [] <= [] by sublist_nil + Step: !p h. p ++ [h] <= q ++ [h] ==> p <= q ==> + !h p h'. p ++ [h'] <= h::q ++ [h'] ==> p <= h::q + If p = [], [h'] <= h::q ++ [h'] = F by sublist_def + If p = h''::t, + h''::t ++ [h'] = h''::(t ++ [h']) by APPEND + If h'' = h', + Then t ++ [h'] <= q ++ [h'] by sublist_def, same heads + ==> t <= q by induction hypothesis + If h'' <> h', + Then h''::t ++ [h'] <= q ++ [h'] by sublist_def, different heads + ==> h''::t <= q by induction hypothesis +*) +val sublist_append_only_if = store_thm( + "sublist_append_only_if", + ``!p q h. (p ++ [h]) <= (q ++ [h]) ==> p <= q``, + Induct_on `q` >- + metis_tac[sublist_prefix_nil, sublist_nil, APPEND] >> + rpt strip_tac >> + (Cases_on `p` >> fs[sublist_def]) >- + metis_tac[] >> + `h''::(t ++ [h']) = (h''::t) ++ [h']` by rw[] >> + metis_tac[]); + +(* Theorem: [tail append] p <= q <=> p ++ [h] <= q ++ [h] *) +(* Proof: by sublist_append_if, sublist_append_only_if *) +val sublist_append_iff = store_thm( + "sublist_append_iff", + ``!p q h. p <= q <=> (p ++ [h]) <= (q ++ [h])``, + metis_tac[sublist_append_if, sublist_append_only_if]); + +(* Theorem: [suffix append] sublist p q ==> sublist (p ++ x) (q ++ x) *) +(* Proof: + By induction on x. + Base: !p q. p <= q <=> p ++ [] <= q ++ [] + True by p ++ [] = p, q ++ [] = q by APPEND + Step: !p q. p <= q <=> p ++ x <= q ++ x ==> + !h p q. p <= q <=> p ++ h::x <= q ++ h::x + p <= q + <=> (p ++ [h]) <= (q ++ [h]) by sublist_append_iff + <=> (p ++ [h]) ++ x <= (q ++ [h]) ++ x by induction hypothesis + <=> p ++ (h::x) <= q ++ (h::x) by APPEND +*) +val sublist_suffix = store_thm( + "sublist_suffix", + ``!x p q. p <= q <=> (p ++ x) <= (q ++ x)``, + Induct >- + rw[] >> + rpt strip_tac >> + `!z. z ++ h::x = (z ++ [h]) ++ x` by rw[] >> + metis_tac[sublist_append_iff]); + +(* Theorem : (a <= b) /\ (c <= d) ==> (a ++ c) <= (b ++ d) *) +(* Proof: + Note a ++ c <= a ++ d by sublist_prefix + and a ++ d <= b ++ d by sublist_suffix + ==> a ++ c <= b ++ d by sublist_trans +*) +val sublist_append_pair = store_thm( + "sublist_append_pair", + ``!a b c d. (a <= b) /\ (c <= d) ==> (a ++ c) <= (b ++ d)``, + metis_tac[sublist_prefix, sublist_suffix, sublist_trans]); + +(* Theorem: [Extended Append, or Decomposition] (h::t) <= q <=> ?x y. (q = x ++ (h::y)) /\ (t <= y) *) +(* Proof: + By induction on list q. + Base case is to prove: + sublist (h::t) [] <=> ?x y. ([] = x ++ (h::y)) /\ (sublist t y) + Hypothesis sublist (h::t) [] is F by SUBLIST_NIL. + In the conclusion, [] cannot be decomposed, hence implication is valid. + Step case is to prove: + (sublist (h::t) q <=> ?x y. (q = x ++ (h::y)) /\ (sublist t y)) ==> + (sublist (h::t) (h'::q) <=> ?x y. (h'::q = x ++ (h::y)) /\ (sublist t y)) + Applying SUBLIST definition and split the if-and-only-if parts, there are 4 cases: + When h = h', if part: + sublist (h::t) (h::q) ==> ?x y. (h::q = x ++ (h::y)) /\ (sublist t y) + For this case, choose x=[], y=q, and sublist (h::t) (h::q) = sublist t q, by SUBLIST same head. + When h = h', only-if part: + ?x y. (h::q = x ++ (h::y)) /\ (sublist t y) ==> sublist (h::t) (h::q) + Take cases on x. + When x = [], + h::q = h::y ==> q = y, + hence sublist t y = sublist t q ==> sublist (h::t) (h::q) by SUBLIST same head. + When x = h''::t', + h::q = (h''::t') ++ (h::y) = h''::(t' ++ (h::y)) ==> + q = t' ++ (h::y), + hence sublist t y ==> sublist t q (by SUBLIST_APPENDR_I) ==> sublist (h::t) (h::q). + When ~(h=h'), if part: + sublist (h::t) (h'::q) ==> ?x y. (h'::q = x ++ (h::y)) /\ (sublist t y) + From hypothesis, + sublist (h::t) (h'::q) + = sublist (h::t) q when ~(h=h'), by SUBLIST definition + ==> ?x1 y1. (q = x1 ++ (h::y1)) /\ (sublist t y1)) by inductive hypothesis + Now (h'::q) = (h'::(x1 ++ (h::y1)) = (h'::x1) ++ (h::y1) by APPEND associativity + So taking x = h'::x1, y = y1, this gives the conclusion. + When ~(h=h'), only-if part: + ?x y. (h'::q = x ++ (h::y)) /\ (sublist t y) ==> sublist (h::t) (h'::q) + The case x = [] is impossible by list equality, since ~(h=h'). + Hence x = h'::t', giving: + q = t'++(h::y) /\ (sublist t y) + = sublist (h::t) q by inductive hypothesis (only-if) + ==> sublist (h::t) (h'::q) by SUBLIST, different head. +*) +val sublist_append_extend = store_thm( + "sublist_append_extend", + ``!h t q. h::t <= q <=> ?x y. (q = x ++ (h::y)) /\ (t <= y)``, + ntac 2 strip_tac >> + Induct >- + rw[sublist_of_nil] >> + rpt strip_tac >> + (Cases_on `h = h'` >> rw[EQ_IMP_THM]) >| [ + `h::q = [] ++ [h] ++ q` by rw[] >> + metis_tac[sublist_cons], + `h::t <= h::y` by rw[GSYM sublist_cons] >> + `x ++ [h] ++ y = x ++ (h::y)` by rw[] >> + metis_tac[sublist_append_include], + `h::t <= q` by metis_tac[sublist_def] >> + metis_tac[APPEND, APPEND_ASSOC], + `h::t <= h::y` by rw[GSYM sublist_cons] >> + `x ++ [h] ++ y = x ++ (h::y)` by rw[] >> + metis_tac[sublist_append_include] + ]); + +(* ------------------------------------------------------------------------- *) +(* Applications of sublist. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: p <= q ==> (MAP f p) <= (MAP f q) *) +(* Proof: + By induction on q. + Base: !p. p <= [] ==> MAP f p <= MAP f [] + Note p = [] by sublist_of_nil + and MAP f [] by MAP + so [] <= [] by sublist_nil + Step: !p. p <= q ==> MAP f p <= MAP f q ==> + !h p. p <= h::q ==> MAP f p <= MAP f (h::q) + If p = [], [] <= h::q = F by sublist_def + If p = h'::t, + If h' = h, + Then t <= q by sublist_def, same heads + ==> MAP f t <= MAP f q by induction hypothesis + ==> h::MAP f t <= h::MAP f q by sublist_cons + ==> MAP f (h::t) <= MAP f (h::q) by MAP + or MAP f p <= MAP f (h::q) by p = h::t + If h' <> h, + Then p <= q by sublist_def, different heads + ==> MAP f p <= MAP f q by induction hypothesis + ==> MAP f p <= h::MAP f q by sublist_cons_include + or MAP f p <= MAP f (h::q) by MAP +*) +val MAP_SUBLIST = store_thm( + "MAP_SUBLIST", + ``!f p q. p <= q ==> (MAP f p) <= (MAP f q)``, + strip_tac >> + Induct_on `q` >- + rw[sublist_of_nil, sublist_nil] >> + rpt strip_tac >> + (Cases_on `p` >> simp[sublist_def]) >> + metis_tac[sublist_def, sublist_cons_include, MAP]); + +(* Theorem: l1 <= l2 ==> SUM l1 <= SUM l2 *) +(* Proof: + By induction on q. + Base: !p. p <= [] ==> SUM p <= SUM [] + Note p = [] by sublist_of_nil + and SUM [] = 0 by SUM + Hence true. + Step: !p. p <= q ==> SUM p <= SUM q ==> + !h p. p <= h::q ==> SUM p <= SUM (h::q) + If p = [], [] <= h::q = F by sublist_def + If p = h'::t, + If h' = h, + Then t <= q by sublist_def, same heads + ==> SUM t <= SUM q by induction hypothesis + ==> h + SUM t <= h + SUM q by arithmetic + ==> SUM (h::t) <= SUM (h::q) by SUM + or SUM p <= SUM (h::q) by p = h::t + If h' <> h, + Then p <= q by sublist_def, different heads + ==> SUM p <= SUM q by induction hypothesis + ==> SUM p <= h + SUM q by arithmetic + ==> SUM p <= SUM (h::q) by SUM +*) +val SUM_SUBLIST = store_thm( + "SUM_SUBLIST", + ``!p q. p <= q ==> SUM p <= SUM q``, + Induct_on `q` >- + rw[sublist_of_nil] >> + rpt strip_tac >> + (Cases_on `p` >> fs[sublist_def]) >> + `h' + SUM t <= SUM q` by metis_tac[SUM] >> + decide_tac); + +(* Idea: express order-preserving in sublist *) + +(* Note: +A simple statement of order-preserving: + +g `p <= q /\ MEM x p /\ MEM y p /\ findi x p <= findi y p ==> findi x q <= findi y q`; + +This simple statement has a counter-example: +q = [1;2;3;4;3;5;1] +p = [2;4;1] +MEM 4 p /\ MEM 1 p /\ findi 4 p = 1 <= findi 1 p = 2, but findi 4 q = 3, yet findi 1 q = 0. +This is because findi gives the first appearance of the member. +This can be fixed by ALL_DISTINCT, but the idea of order-preserving should not depend on ALL_DISTINCT. +*) + +(* Theorem: sl <= ls /\ MEM x sl ==> + ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 *) +(* Proof: + By sublist_induct, this is to show: + (1) MEM x [] ==> ?l1 l2 l3 l4... + Note MEM x [] = F by MEM + hence true. + (2) MEM x sl ==> ?l1 l2 l3 l4... /\ sl <= ls /\ MEM x (h::sl) ==> + ?l1 l2 l3 l4. h::ls = l1 ++ [x] ++ l2 /\ h::sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 + Note MEM x (h::sl) + ==> x = h \/ MEM x sl by MEM + If x = h, + Then h::ls = [h] ++ ls by CONS_APPEND + and h::sl = [h] ++ sl by CONS_APPEND + Pick l1 = [], l2 = ls, l3 = [], l4 = sl. + Then l3 <= l1 since by sublist_nil + and l4 <= l2 since sl <= ls by induction hypothesis + Otherwise, MEM x sl, + Note ?l1 l2 l3 l4. + ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 + by induction hypothesis + Then h::ls = h::(l1 ++ [x] ++ l2) + = h::l1 ++ [x] ++ l2 by APPEND + and h::sl = h::(l3 ++ [x] ++ l4) + = h::l3 ++ [x] ++ l4 by APPEND + Pick new l1 = h::l1, l2 = l2, l3 = h::l3, l4 = l4. + Then l3 <= l1 ==> h::l3 <= h::l1 by sublist_cons + (3) MEM x sl ==> ?l1 l2 l3 l4... /\ sl <= ls /\ MEM x sl ==> + ?l1 l2 l3 l4. h::ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 + Note ?l1 l2 l3 l4. + ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 + by induction hypothesis + Then h::ls = h::(l1 ++ [x] ++ l2) + = h::l1 ++ [x] ++ l2 by APPEND + Pick new l1 = h::l1, l2 = l2, l3 = l3, l4 = l4. + Then l3 <= l1 ==> l3 <= h::l1 by sublist_cons_include +*) +Theorem sublist_order: + !ls sl x. sl <= ls /\ MEM x sl ==> + ?l1 l2 l3 l4. ls = l1 ++ [x] ++ l2 /\ sl = l3 ++ [x] ++ l4 /\ l3 <= l1 /\ l4 <= l2 +Proof + rpt strip_tac >> + pop_assum mp_tac >> + pop_assum mp_tac >> + qid_spec_tac `ls` >> + qid_spec_tac `sl` >> + ho_match_mp_tac sublist_induct >> + rpt strip_tac >- + fs[] >- + (fs[] >| [ + map_every qexists_tac [`[]`, `ls`, `[]`, `sl`] >> + simp[sublist_nil], + fs[] >> + map_every qexists_tac [`h::l1`, `l2`, `h::l3`, `l4`] >> + simp[GSYM sublist_cons] + ]) >> + fs[] >> + map_every qexists_tac [`h::l1`, `l2`, `l3`, `l4`] >> + simp[sublist_cons_include] +QED + +(* Theorem: sl <= ls /\ MONO_INC ls ==> MONO_INC sl *) +(* Proof: + By sublist induction, this is to show: + (1) n < LENGTH [] /\ m <= n ==> EL m [] <= EL n [] + Note LENGTH [] = 0 by LENGTH + so assumption is F, hence T. + (2) MONO_INC ls ==> MONO_INC sl /\ sl <= ls /\ + MONO_INC (h::ls) /\ m <= n /\ n < LENGTH (h::sl) ==> EL m (h::sl) <= EL n (h::sl) + Note MONO_INC (h::ls) ==> MONO_INC ls by MONO_INC_CONS + If m = 0, + If n = 0, + Then EL 0 (h::sl) = h, hence T. + If 0 < n, + Then 0 <= PRE n, + so EL n (h::sl) = EL (PRE n) sl + Let x = EL 0 sl. + Then x <= EL (PRE n) sl by MONO_INC sl + But MEM x sl by EL_MEM + ==> MEM x ls by sublist_mem + so h <= x by MONO_INC (h::ls) + Thus h <= EL n (h::sl) by inequality + If 0 < m, + Then m <= n means 0 < n. + Thus PRE m <= PRE n + EL m (h::sl) + = EL (PRE m) sl by EL_CONS, 0 < m + <= EL (PRE n) sl by induction hypothesis + = EL n (h::sl) by EL_CONS, 0 < n + + (3) MONO_INC ls ==> MONO_INC sl /\ sl <= ls /\ + MONO_INC (h::ls) /\ m <= n /\ n < LENGTH sl ==> EL m sl <= EL n sl + Note MONO_INC (h::ls) ==> MONO_INC ls by MONO_INC_CONS + Thus MONO_INC sl by induction hypothesis + so m <= n ==> EL m sl <= EL n sl by MONO_INC sl +*) +Theorem sublist_MONO_INC: + !ls sl. sl <= ls /\ MONO_INC ls ==> MONO_INC sl +Proof + ntac 3 strip_tac >> + pop_assum mp_tac >> + pop_assum mp_tac >> + qid_spec_tac `ls` >> + qid_spec_tac `sl` >> + ho_match_mp_tac sublist_induct >> + rpt strip_tac >- + fs[] >- + (`MONO_INC ls` by metis_tac[MONO_INC_CONS] >> + `m = 0 \/ 0 < m` by decide_tac >| [ + `n = 0 \/ 0 < n` by decide_tac >- + simp[] >> + `0 <= PRE n` by decide_tac >> + qabbrev_tac `x = EL 0 sl` >> + `x <= EL (PRE n) sl` by fs[Abbr`x`] >> + `MEM x sl` by fs[EL_MEM, Abbr`x`] >> + `h <= x` by metis_tac[MONO_INC_HD, sublist_mem] >> + simp[EL_CONS], + `0 < n /\ PRE m <= PRE n` by decide_tac >> + `EL (PRE m) sl <= EL (PRE n) sl` by fs[] >> + simp[EL_CONS] + ]) >> + `MONO_INC ls` by metis_tac[MONO_INC_CONS] >> + fs[] +QED + +(* Theorem: sl <= ls /\ MONO_DEC ls ==> MONO_DEC sl *) +(* Proof: + By sublist induction, this is to show: + (1) n < LENGTH [] /\ m <= n ==> EL n [] <= EL m [] + Note LENGTH [] = 0 by LENGTH + so assumption is F, hence T. + (2) MONO_DEC ls ==> MONO_DEC sl /\ sl <= ls /\ + MONO_DEC (h::ls) /\ m <= n /\ n < LENGTH (h::sl) ==> EL n (h::sl) <= EL m (h::sl) + Note MONO_DEC (h::ls) ==> MONO_DEC ls by MONO_DEC_CONS + If m = 0, + If n = 0, + Then EL 0 (h::sl) = h, hence T. + If 0 < n, + Then 0 <= PRE n, + so EL n (h::sl) = EL (PRE n) sl + Let x = EL 0 sl. + Then EL (PRE n) sl <= x by MONO_DEC sl + But MEM x sl by EL_MEM + ==> MEM x ls by sublist_mem + so x <= h by MONO_DEC (h::ls) + Thus EL n (h::sl) <= h by inequality + If 0 < m, + Then m <= n means 0 < n. + Thus PRE m <= PRE n + EL n (h::sl) + = EL (PRE n) sl by EL_CONS, 0 < n + <= EL (PRE m) sl by induction hypothesis + = EL m (h::sl) by EL_CONS, 0 < m + + (3) MONO_DEC ls ==> MONO_DEC sl /\ sl <= ls /\ + MONO_DEC (h::ls) /\ m <= n /\ n < LENGTH sl ==> EL n sl <= EL m sl + Note MONO_DEC (h::ls) ==> MONO_DEC ls by MONO_DEC_CONS + Thus MONO_DEC sl by induction hypothesis + so m <= n ==> EL n sl <= EL m sl by MONO_DEC sl +*) +Theorem sublist_MONO_DEC: + !ls sl. sl <= ls /\ MONO_DEC ls ==> MONO_DEC sl +Proof + ntac 3 strip_tac >> + pop_assum mp_tac >> + pop_assum mp_tac >> + qid_spec_tac `ls` >> + qid_spec_tac `sl` >> + ho_match_mp_tac sublist_induct >> + rpt strip_tac >- + fs[] >- + (`MONO_DEC ls` by metis_tac[MONO_DEC_CONS] >> + `m = 0 \/ 0 < m` by decide_tac >| [ + `n = 0 \/ 0 < n` by decide_tac >- + simp[] >> + `0 <= PRE n` by decide_tac >> + qabbrev_tac `x = EL 0 sl` >> + `EL (PRE n) sl <= x` by fs[Abbr`x`] >> + `MEM x sl` by fs[EL_MEM, Abbr`x`] >> + `x <= h` by metis_tac[MONO_DEC_HD, sublist_mem] >> + simp[EL_CONS], + `0 < n /\ PRE m <= PRE n` by decide_tac >> + `EL (PRE n) sl <= EL (PRE m) sl` by fs[] >> + simp[EL_CONS] + ]) >> + `MONO_DEC ls` by metis_tac[MONO_DEC_CONS] >> + fs[] +QED + +(* Yes, finally! *) + +(* ------------------------------------------------------------------------- *) +(* FILTER as sublist. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: FILTER P ls <= ls *) +(* Proof: + By induction on ls. + Base: FILTER P [] <= [], + Note FILTER P [] = [] by FILTER + and [] <= [] by sublist_refl + Step: FILTER P ls <= ls ==> + !h. FILTER P (h::ls) <= h::ls + If P h, + FILTER P ls <= ls by induction hypothesis + ==> h::FILTER P ls <= h::ls by sublist_cons + ==> FILTER P (h::ls) <= h::ls by FILTER, P h. + + If ~P h, + FILTER P ls <= ls by induction hypothesis + ==> FILTER P ls <= h::ls by sublist_cons_include + ==> FILTER P (h::ls) <= h::ls by FILTER, ~P h. +*) +Theorem FILTER_sublist: + !P ls. FILTER P ls <= ls +Proof + strip_tac >> + Induct >- + simp[sublist_refl] >> + rpt strip_tac >> + Cases_on `P h` >- + metis_tac[FILTER, sublist_cons] >> + metis_tac[FILTER, sublist_cons_include] +QED + +(* Theorem: MONO_INC ls ==> MONO_INC (FILTER P ls) *) +(* Proof: + Note (FILTER P ls) <= ls by FILTER_sublist + With MONO_INC ls + ==> MONO_INC (FILTER P ls) by sublist_MONO_INC +*) +Theorem FILTER_MONO_INC: + !P ls. MONO_INC ls ==> MONO_INC (FILTER P ls) +Proof + metis_tac[FILTER_sublist, sublist_MONO_INC] +QED + +(* Theorem: MONO_DEC ls ==> MONO_DEC (FILTER P ls) *) +(* Proof: + Note (FILTER P ls) <= ls by FILTER_sublist + With MONO_DEC ls + ==> MONO_DEC (FILTER P ls) by sublist_MONO_DEC +*) +Theorem FILTER_MONO_DEC: + !P ls. MONO_DEC ls ==> MONO_DEC (FILTER P ls) +Proof + metis_tac[FILTER_sublist, sublist_MONO_DEC] +QED + (* ------------------------------------------------------------------------ *) local diff --git a/src/num/extra_theories/dividesScript.sml b/src/num/extra_theories/dividesScript.sml index 85c435652e..29a6190a44 100644 --- a/src/num/extra_theories/dividesScript.sml +++ b/src/num/extra_theories/dividesScript.sml @@ -1,17 +1,31 @@ -open HolKernel Parse boolLib simpLib BasicProvers - prim_recTheory arithmeticTheory boolSimps - metisLib numLib; +open HolKernel Parse boolLib BasicProvers; + +open simpLib computeLib prim_recTheory arithmeticTheory boolSimps + metisLib numLib TotalDefn; val CALC = EQT_ELIM o reduceLib.REDUCE_CONV; val ARITH_TAC = CONV_TAC Arith.ARITH_CONV; val DECIDE = EQT_ELIM o Arith.ARITH_CONV; +fun DECIDE_TAC (g as (asl,_)) = + ((MAP_EVERY UNDISCH_TAC (filter numSimps.is_arith asl) THEN + CONV_TAC Arith.ARITH_CONV) + ORELSE tautLib.TAUT_TAC) g; + +val decide_tac = DECIDE_TAC; +val metis_tac = METIS_TAC; val arith_ss = numLib.arith_ss; +val rw = srw_tac[]; +val qabbrev_tac = Q.ABBREV_TAC; +val qspec_then = Q.SPEC_THEN; fun simp ths = asm_simp_tac (srw_ss() ++ numSimps.ARITH_ss) ths fun gvs ths = global_simp_tac {droptrues = true, elimvars = true, oldestfirst = true, strip = true} (srw_ss() ++ numSimps.ARITH_ss) ths + +fun fs l = FULL_SIMP_TAC (srw_ss() ++ numSimps.ARITH_ss) l; + val op >~ = Q.>~ val ARW = RW_TAC arith_ss; @@ -439,6 +453,17 @@ val ZERO_LT_PRIMES = Q.store_thm `!n. 0 < PRIMES n`, METIS_TAC [LESS_TRANS, ONE_LT_PRIMES, DECIDE ``0 < 1``]); +(* Theorem: !n. ?p. prime p /\ n < p *) +(* Proof: + Since ?i. n < PRIMES i by NEXT_LARGER_PRIME + and prime (PRIMES i) by primePRIMES + Take p = PRIMES i. +*) +val prime_always_bigger = store_thm( + "prime_always_bigger", + ``!n. ?p. prime p /\ n < p``, + metis_tac[NEXT_LARGER_PRIME, primePRIMES]); + (*---------------------------------------------------------------------------*) (* Directly computable version of divides *) (*---------------------------------------------------------------------------*) @@ -466,4 +491,601 @@ Proof ] QED +(* ------------------------------------------------------------------------- *) +(* DIVIDES Theorems (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* temporarily make divides an infix *) +val _ = temp_set_fixity "divides" (Infixl 480); + +(* Theorem: 0 < n ==> ((m DIV n = 0) <=> m < n) *) +(* Proof: + If part: 0 < n /\ m DIV n = 0 ==> m < n + Since m = m DIV n * n + m MOD n) /\ (m MOD n < n) by DIVISION, 0 < n + so m = 0 * n + m MOD n by m DIV n = 0 + = 0 + m MOD n by MULT + = m MOD n by ADD + Since m MOD n < n, m < n. + Only-if part: 0 < n /\ m < n ==> m DIV n = 0 + True by LESS_DIV_EQ_ZERO. +*) +val DIV_EQUAL_0 = store_thm( + "DIV_EQUAL_0", + ``!m n. 0 < n ==> ((m DIV n = 0) <=> m < n)``, + rw[EQ_IMP_THM] >- + metis_tac[DIVISION, MULT, ADD] >> + rw[LESS_DIV_EQ_ZERO]); +(* This is an improvement of + arithmeticTheory.DIV_EQ_0 = |- 1 < b ==> (n DIV b = 0 <=> n < b) *) + +(* Theorem: 0 < m /\ m <= n ==> 0 < n DIV m *) +(* Proof: + Note n = (n DIV m) * m + n MOD m /\ + n MDO m < m by DIVISION, 0 < m + ==> n MOD m < n by m <= n + Thus 0 < (n DIV m) * m by inequality + so 0 < n DIV m by ZERO_LESS_MULT +*) +Theorem DIV_POS: + !m n. 0 < m /\ m <= n ==> 0 < n DIV m +Proof + rpt strip_tac >> + imp_res_tac (DIVISION |> SPEC_ALL) >> + first_x_assum (qspec_then `n` strip_assume_tac) >> + first_x_assum (qspec_then `n` strip_assume_tac) >> + `0 < (n DIV m) * m` by decide_tac >> + metis_tac[ZERO_LESS_MULT] +QED + +(* Theorem: 0 < z ==> (x DIV z = y DIV z <=> x - x MOD z = y - y MOD z) *) +(* Proof: + Note x = (x DIV z) * z + x MOD z by DIVISION + and y = (y DIV z) * z + y MDO z by DIVISION + x DIV z = y DIV z + <=> (x DIV z) * z = (y DIV z) * z by EQ_MULT_RCANCEL + <=> x - x MOD z = y - y MOD z by arithmetic +*) +Theorem DIV_EQ: + !x y z. 0 < z ==> (x DIV z = y DIV z <=> x - x MOD z = y - y MOD z) +Proof + rpt strip_tac >> + `x = (x DIV z) * z + x MOD z` by simp[DIVISION] >> + `y = (y DIV z) * z + y MOD z` by simp[DIVISION] >> + `x DIV z = y DIV z <=> (x DIV z) * z = (y DIV z) * z` by simp[] >> + decide_tac +QED + +(* Theorem: a MOD n + b < n ==> (a + b) DIV n = a DIV n *) +(* Proof: + Note 0 < n by a MOD n + b < n + a + b + = ((a DIV n) * n + a MOD n) + b by DIVISION, 0 < n + = (a DIV n) * n + (a MOD n + b) by ADD_ASSOC + + If a MOD n + b < n, + Then (a + b) DIV n = a DIV n /\ + (a + b) MOD n = a MOD n + b by DIVMOD_UNIQ +*) +Theorem ADD_DIV_EQ: + !n a b. a MOD n + b < n ==> (a + b) DIV n = a DIV n +Proof + rpt strip_tac >> + `0 < n` by decide_tac >> + `a = (a DIV n) * n + a MOD n` by simp[DIVISION] >> + `a + b = (a DIV n) * n + (a MOD n + b)` by decide_tac >> + metis_tac[DIVMOD_UNIQ] +QED + +(* Theorem: 0 < y /\ x <= y * z ==> x DIV y <= z *) +(* Proof: + x <= y * z + ==> x DIV y <= (y * z) DIV y by DIV_LE_MONOTONE, 0 < y + = z by MULT_TO_DIV +*) +val DIV_LE = store_thm( + "DIV_LE", + ``!x y z. 0 < y /\ x <= y * z ==> x DIV y <= z``, + metis_tac[DIV_LE_MONOTONE, MULT_TO_DIV]); + +(* Theorem: 0 < n ==> !x y. (x * n = y) ==> (x = y DIV n) *) +(* Proof: + x = (x * n + 0) DIV n by DIV_MULT, 0 < n + = (x * n) DIV n by ADD_0 +*) +val DIV_SOLVE = store_thm( + "DIV_SOLVE", + ``!n. 0 < n ==> !x y. (x * n = y) ==> (x = y DIV n)``, + metis_tac[DIV_MULT, ADD_0]); + +(* Theorem: 0 < n ==> !x y. (n * x = y) ==> (x = y DIV n) *) +(* Proof: by DIV_SOLVE, MULT_COMM *) +val DIV_SOLVE_COMM = store_thm( + "DIV_SOLVE_COMM", + ``!n. 0 < n ==> !x y. (n * x = y) ==> (x = y DIV n)``, + rw[DIV_SOLVE, MULT_TO_DIV]); + +(* Theorem: 1 < n ==> (1 DIV n = 0) *) +(* Proof: + Since 1 = (1 DIV n) * n + (1 MOD n) by DIVISION, 0 < n. + and 1 MOD n = 1 by ONE_MOD, 1 < n. + thus (1 DIV n) * n = 0 by arithmetic + or 1 DIV n = 0 since n <> 0 by MULT_EQ_0 +*) +val ONE_DIV = store_thm( + "ONE_DIV", + ``!n. 1 < n ==> (1 DIV n = 0)``, + rpt strip_tac >> + `0 < n /\ n <> 0` by decide_tac >> + `1 = (1 DIV n) * n + (1 MOD n)` by rw[DIVISION] >> + `_ = (1 DIV n) * n + 1` by rw[ONE_MOD] >> + `(1 DIV n) * n = 0` by decide_tac >> + metis_tac[MULT_EQ_0]); + +(* Theorem: ODD n ==> !m. m divides n ==> ODD m *) +(* Proof: + Since m divides n + ==> ?q. n = q * m by divides_def + By contradiction, suppose ~ODD m. + Then EVEN m by ODD_EVEN + and EVEN (q * m) = EVEN n by EVEN_MULT + or ~ODD n by ODD_EVEN + This contradicts with ODD n. +*) +val DIVIDES_ODD = store_thm( + "DIVIDES_ODD", + ``!n. ODD n ==> !m. m divides n ==> ODD m``, + metis_tac[divides_def, EVEN_MULT, EVEN_ODD]); + +(* Note: For EVEN n, m divides n cannot conclude EVEN m. +Example: EVEN 2 or ODD 3 both divides EVEN 6. +*) + +(* Theorem: EVEN m ==> !n. m divides n ==> EVEN n*) +(* Proof: + Since m divides n + ==> ?q. n = q * m by divides_def + Given EVEN m + Then EVEN (q * m) = n by EVEN_MULT +*) +val DIVIDES_EVEN = store_thm( + "DIVIDES_EVEN", + ``!m. EVEN m ==> !n. m divides n ==> EVEN n``, + metis_tac[divides_def, EVEN_MULT]); + +(* Theorem: EVEN n = 2 divides n *) +(* Proof: + EVEN n + <=> n MOD 2 = 0 by EVEN_MOD2 + <=> 2 divides n by DIVIDES_MOD_0, 0 < 2 +*) +val EVEN_ALT = store_thm( + "EVEN_ALT", + ``!n. EVEN n = 2 divides n``, + rw[EVEN_MOD2, DIVIDES_MOD_0]); + +(* Theorem: ODD n = ~(2 divides n) *) +(* Proof: + Note n MOD 2 < 2 by MOD_LESS + and !x. x < 2 <=> (x = 0) \/ (x = 1) by arithmetic + ODD n + <=> n MOD 2 = 1 by ODD_MOD2 + <=> ~(2 divides n) by DIVIDES_MOD_0, 0 < 2 + Or, + ODD n = ~(EVEN n) by ODD_EVEN + = ~(2 divides n) by EVEN_ALT +*) +val ODD_ALT = store_thm( + "ODD_ALT", + ``!n. ODD n = ~(2 divides n)``, + metis_tac[EVEN_ODD, EVEN_ALT]); + +(* Theorem: 0 < n ==> !q. (q DIV n) * n <= q *) +(* Proof: + Since q = (q DIV n) * n + q MOD n by DIVISION + Thus (q DIV n) * n <= q by discarding remainder +*) +val DIV_MULT_LE = store_thm( + "DIV_MULT_LE", + ``!n. 0 < n ==> !q. (q DIV n) * n <= q``, + rpt strip_tac >> + `q = (q DIV n) * n + q MOD n` by rw[DIVISION] >> + decide_tac); + +(* Theorem: 0 < n ==> !q. n divides q <=> ((q DIV n) * n = q) *) +(* Proof: + If part: n divides q ==> q DIV n * n = q + q = (q DIV n) * n + q MOD n by DIVISION + = (q DIV n) * n + 0 by MOD_EQ_0_DIVISOR, divides_def + = (q DIV n) * n by ADD_0 + Only-if part: q DIV n * n = q ==> n divides q + True by divides_def +*) +val DIV_MULT_EQ = store_thm( + "DIV_MULT_EQ", + ``!n. 0 < n ==> !q. n divides q <=> ((q DIV n) * n = q)``, + metis_tac[divides_def, DIVISION, MOD_EQ_0_DIVISOR, ADD_0]); +(* same as DIVIDES_EQN below *) + +(* Theorem: 0 < x /\ 0 < y /\ x <= y ==> !n. n DIV y <= n DIV x *) +(* Proof: + If n DIV y = 0, + Then 0 <= n DIV x is trivially true. + If n DIV y <> 0, + (n DIV y) * x <= (n DIV y) * y by LE_MULT_LCANCEL, x <= y, n DIV y <> 0 + <= n by DIV_MULT_LE + Hence (n DIV y) * x <= n by LESS_EQ_TRANS + Then ((n DIV y) * x) DIV x <= n DIV x by DIV_LE_MONOTONE + or n DIV y <= n DIV x by MULT_DIV +*) +val DIV_LE_MONOTONE_REVERSE = store_thm( + "DIV_LE_MONOTONE_REVERSE", + ``!x y. 0 < x /\ 0 < y /\ x <= y ==> !n. n DIV y <= n DIV x``, + rpt strip_tac >> + Cases_on `n DIV y = 0` >- + decide_tac >> + `(n DIV y) * x <= (n DIV y) * y` by rw[LE_MULT_LCANCEL] >> + `(n DIV y) * y <= n` by rw[DIV_MULT_LE] >> + `(n DIV y) * x <= n` by decide_tac >> + `((n DIV y) * x) DIV x <= n DIV x` by rw[DIV_LE_MONOTONE] >> + metis_tac[MULT_DIV]); + +(* Theorem: n divides m <=> (m = (m DIV n) * n) *) +(* Proof: + Since n divides m <=> m MOD n = 0 by DIVIDES_MOD_0 + and m = (m DIV n) * n + (m MOD n) by DIVISION + If part: n divides m ==> m = m DIV n * n + This is true by ADD_0 + Only-if part: m = m DIV n * n ==> n divides m + Since !x y. x + y = x <=> y = 0 by ADD_INV_0 + The result follows. +*) +val DIVIDES_EQN = store_thm( + "DIVIDES_EQN", + ``!n. 0 < n ==> !m. n divides m <=> (m = (m DIV n) * n)``, + metis_tac[DIVISION, DIVIDES_MOD_0, ADD_0, ADD_INV_0]); + +(* Theorem: 0 < n ==> !m. n divides m <=> (m = n * (m DIV n)) *) +(* Proof: vy DIVIDES_EQN, MULT_COMM *) +val DIVIDES_EQN_COMM = store_thm( + "DIVIDES_EQN_COMM", + ``!n. 0 < n ==> !m. n divides m <=> (m = n * (m DIV n))``, + rw_tac std_ss[DIVIDES_EQN, MULT_COMM]); + +(* Theorem: 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1) *) +(* Proof: + Apply DIV_SUB |> GEN_ALL |> SPEC ``1`` |> REWRITE_RULE[MULT_RIGHT_1]; + val it = |- !n m. 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1): thm +*) +val SUB_DIV = save_thm("SUB_DIV", + DIV_SUB |> GEN ``n:num`` |> GEN ``m:num`` |> GEN ``q:num`` |> SPEC ``1`` + |> REWRITE_RULE[MULT_RIGHT_1]); +(* val SUB_DIV = |- !m n. 0 < n /\ n <= m ==> ((m - n) DIV n = m DIV n - 1): thm *) + +(* Theorem: 0 < n ==> !k m. (m MOD n = 0) ==> ((k * n = m) <=> (k = m DIV n)) *) +(* Proof: + Note m MOD n = 0 + ==> n divides m by DIVIDES_MOD_0, 0 < n + ==> m = (m DIV n) * n by DIVIDES_EQN, 0 < n + k * n = m + <=> k * n = (m DIV n) * n by above + <=> k = (m DIV n) by EQ_MULT_RCANCEL, n <> 0. +*) +val DIV_EQ_MULT = store_thm( + "DIV_EQ_MULT", + ``!n. 0 < n ==> !k m. (m MOD n = 0) ==> ((k * n = m) <=> (k = m DIV n))``, + rpt strip_tac >> + `n <> 0` by decide_tac >> + `m = (m DIV n) * n` by rw[GSYM DIVIDES_EQN, DIVIDES_MOD_0] >> + metis_tac[EQ_MULT_RCANCEL]); + +(* Theorem: 0 < n ==> !k m. (m MOD n = 0) ==> (k * n < m <=> k < m DIV n) *) +(* Proof: + k * n < m + <=> k * n < (m DIV n) * n by DIVIDES_EQN, DIVIDES_MOD_0, 0 < n + <=> k < m DIV n by LT_MULT_RCANCEL, n <> 0 +*) +val MULT_LT_DIV = store_thm( + "MULT_LT_DIV", + ``!n. 0 < n ==> !k m. (m MOD n = 0) ==> (k * n < m <=> k < m DIV n)``, + metis_tac[DIVIDES_EQN, DIVIDES_MOD_0, LT_MULT_RCANCEL, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < n ==> !k m. (m MOD n = 0) ==> (m <= n * k <=> m DIV n <= k) *) +(* Proof: + m <= n * k + <=> (m DIV n) * n <= n * k by DIVIDES_EQN, DIVIDES_MOD_0, 0 < n + <=> (m DIV n) * n <= k * n by MULT_COMM + <=> m DIV n <= k by LE_MULT_RCANCEL, n <> 0 +*) +val LE_MULT_LE_DIV = store_thm( + "LE_MULT_LE_DIV", + ``!n. 0 < n ==> !k m. (m MOD n = 0) ==> (m <= n * k <=> m DIV n <= k)``, + metis_tac[DIVIDES_EQN, DIVIDES_MOD_0, MULT_COMM, LE_MULT_RCANCEL, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < m ==> ((n DIV m = 0) /\ (n MOD m = 0) <=> (n = 0)) *) +(* Proof: + If part: (n DIV m = 0) /\ (n MOD m = 0) ==> (n = 0) + Note n DIV m = 0 ==> n < m by DIV_EQUAL_0 + Thus n MOD m = n by LESS_MOD + or n = 0 + Only-if part: 0 DIV m = 0 by ZERO_DIV + 0 MOD m = 0 by ZERO_MOD +*) +Theorem DIV_MOD_EQ_0: + !m n. 0 < m ==> ((n DIV m = 0) /\ (n MOD m = 0) <=> (n = 0)) +Proof + rpt strip_tac >> + rw[EQ_IMP_THM] >> + metis_tac[DIV_EQUAL_0, LESS_MOD] +QED + +(* Theorem: 0 < n /\ a ** n divides b ==> a divides b *) +(* Proof: + Note ?k. n = SUC k by num_CASES, n <> 0 + and ?q. b = q * (a ** n) by divides_def + = q * (a * a ** k) by EXP + = (q * a ** k) * a by arithmetic + Thus a divides b by divides_def +*) +Theorem EXP_divides : (* was: EXP_DIVIDES *) + !a b n. 0 < n /\ a ** n divides b ==> a divides b +Proof + rpt strip_tac >> + `?k. n = SUC k` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> + `?q. b = q * a ** n` by rw[GSYM divides_def] >> + `_ = q * (a * a ** k)` by rw[EXP] >> + `_ = (q * a ** k) * a` by decide_tac >> + metis_tac[divides_def] +QED + +(* Theorem: n divides m ==> !k. n divides (k * m) *) +(* Proof: + n divides m ==> ?q. m = q * n by divides_def + Hence k * m = k * (q * n) + = (k * q) * n by MULT_ASSOC + or n divides (k * m) by divides_def +*) +val DIVIDES_MULTIPLE = store_thm( + "DIVIDES_MULTIPLE", + ``!m n. n divides m ==> !k. n divides (k * m)``, + metis_tac[divides_def, MULT_ASSOC]); + +val divisor_pos = store_thm( + "divisor_pos", + ``!m n. 0 < n /\ m divides n ==> 0 < m``, + metis_tac[ZERO_DIVIDES, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < n /\ m divides n ==> 0 < m /\ m <= n *) +(* Proof: + Since 0 < n /\ m divides n, + then 0 < m by divisor_pos + and m <= n by DIVIDES_LE +*) +val divides_pos = store_thm( + "divides_pos", + ``!m n. 0 < n /\ m divides n ==> 0 < m /\ m <= n``, + metis_tac[divisor_pos, DIVIDES_LE]); + +(* Theorem: 0 < n /\ m divides n ==> (n DIV (n DIV m) = m) *) +(* Proof: + Since 0 < n /\ m divides n, 0 < m by divisor_pos + Hence n = (n DIV m) * m by DIVIDES_EQN, 0 < m + Note 0 < n DIV m, otherwise contradicts 0 < n by MULT + Now n = m * (n DIV m) by MULT_COMM + = m * (n DIV m) + 0 by ADD_0 + Therefore n DIV (n DIV m) = m by DIV_UNIQUE +*) +val divide_by_cofactor = store_thm( + "divide_by_cofactor", + ``!m n. 0 < n /\ m divides n ==> (n DIV (n DIV m) = m)``, + rpt strip_tac >> + `0 < m` by metis_tac[divisor_pos] >> + `n = (n DIV m) * m` by rw[GSYM DIVIDES_EQN] >> + `0 < n DIV m` by metis_tac[MULT, NOT_ZERO_LT_ZERO] >> + `n = m * (n DIV m) + 0` by metis_tac[MULT_COMM, ADD_0] >> + metis_tac[DIV_UNIQUE]); + +(* Theorem: 0 < n ==> !a b. a divides b ==> a divides b ** n *) +(* Proof: + Since 0 < n, n = SUC m for some m. + thus b ** n = b ** (SUC m) + = b * b ** m by EXP + Now a divides b means + ?k. b = k * a by divides_def + so b ** n + = k * a * b ** m + = (k * b ** m) * a by MULT_COMM, MULT_ASSOC + Hence a divides (b ** n) by divides_def +*) +val divides_exp = store_thm( + "divides_exp", + ``!n. 0 < n ==> !a b. a divides b ==> a divides b ** n``, + rw_tac std_ss[divides_def] >> + `n <> 0` by decide_tac >> + `?m. n = SUC m` by metis_tac[num_CASES] >> + `(q * a) ** n = q * a * (q * a) ** m` by rw[EXP] >> + `_ = q * (q * a) ** m * a` by rw[MULT_COMM, MULT_ASSOC] >> + metis_tac[]); + +(* Note; converse need prime divisor: +DIVIDES_EXP_BASE |- !a b n. prime a /\ 0 < n ==> (a divides b <=> a divides b ** n) +Counter-example for a general base: 12 divides 36 = 6^2, but ~(12 divides 6) +*) + +(* Better than: DIVIDES_ADD_1 |- !a b c. a divides b /\ a divides c ==> a divides b + c *) + +(* Theorem: c divides a /\ c divides b ==> !h k. c divides (h * a + k * b) *) +(* Proof: + Since c divides a, ?u. a = u * c by divides_def + and c divides b, ?v. b = v * c by divides_def + h * a + k * b + = h * (u * c) + k * (v * c) by above + = h * u * c + k * v * c by MULT_ASSOC + = (h * u + k * v) * c by RIGHT_ADD_DISTRIB + Hence c divides (h * a + k * b) by divides_def +*) +val divides_linear = store_thm( + "divides_linear", + ``!a b c. c divides a /\ c divides b ==> !h k. c divides (h * a + k * b)``, + rw_tac std_ss[divides_def] >> + metis_tac[RIGHT_ADD_DISTRIB, MULT_ASSOC]); + +(* Theorem: c divides a /\ c divides b ==> !h k d. (h * a = k * b + d) ==> c divides d *) +(* Proof: + If c = 0, + 0 divides a ==> a = 0 by ZERO_DIVIDES + 0 divides b ==> b = 0 by ZERO_DIVIDES + Thus d = 0 by arithmetic + and 0 divides 0 by ZERO_DIVIDES + If c <> 0, 0 < c. + c divides a ==> (a MOD c = 0) by DIVIDES_MOD_0 + c divides b ==> (b MOD c = 0) by DIVIDES_MOD_0 + Hence 0 = (h * a) MOD c by MOD_TIMES2, ZERO_MOD + = (0 + d MOD c) MOD c by MOD_PLUS, MOD_TIMES2, ZERO_MOD + = d MOD c by MOD_MOD + or c divides d by DIVIDES_MOD_0 +*) +val divides_linear_sub = store_thm( + "divides_linear_sub", + ``!a b c. c divides a /\ c divides b ==> !h k d. (h * a = k * b + d) ==> c divides d``, + rpt strip_tac >> + Cases_on `c = 0` >| [ + `(a = 0) /\ (b = 0)` by metis_tac[ZERO_DIVIDES] >> + `d = 0` by rw_tac arith_ss[] >> + rw[], + `0 < c` by decide_tac >> + `(a MOD c = 0) /\ (b MOD c = 0)` by rw[GSYM DIVIDES_MOD_0] >> + `0 = (h * a) MOD c` by metis_tac[MOD_TIMES2, ZERO_MOD, MULT_0] >> + `_ = (0 + d MOD c) MOD c` by metis_tac[MOD_PLUS, MOD_TIMES2, ZERO_MOD, MULT_0] >> + `_ = d MOD c` by rw[MOD_MOD] >> + rw[DIVIDES_MOD_0] + ]); + +(* ------------------------------------------------------------------------- *) +(* Factorial *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: FACT 0 = 1 *) +(* Proof: by FACT *) +val FACT_0 = store_thm( + "FACT_0", + ``FACT 0 = 1``, + EVAL_TAC); + +(* Theorem: FACT 1 = 1 *) +(* Proof: + FACT 1 + = FACT (SUC 0) by ONE + = (SUC 0) * FACT 0 by FACT + = (SUC 0) * 1 by FACT + = 1 by ONE +*) +val FACT_1 = store_thm( + "FACT_1", + ``FACT 1 = 1``, + EVAL_TAC); + +(* Theorem: FACT 2 = 2 *) +(* Proof: + FACT 2 + = FACT (SUC 1) by TWO + = (SUC 1) * FACT 1 by FACT + = (SUC 1) * 1 by FACT_1 + = 2 by TWO +*) +val FACT_2 = store_thm( + "FACT_2", + ``FACT 2 = 2``, + EVAL_TAC); + +(* Theorem: (FACT n = 1) <=> n <= 1 *) +(* Proof: + If n = 0, + LHS = (FACT 0 = 1) = T by FACT_0 + RHS = 0 <= 1 = T by arithmetic + If n <> 0, n = SUC m by num_CASES + LHS = FACT (SUC m) = 1 + <=> (SUC m) * FACT m = 1 by FACT + <=> SUC m = 1 /\ FACT m = 1 by MULT_EQ_1 + <=> m = 0 /\ FACT m = 1 by m = PRE 1 = 0 + <=> m = 0 by FACT_0 + RHS = SUC m <= 1 + <=> ~(1 <= m) by NOT_LEQ + <=> m < 1 by NOT_LESS_EQUAL + <=> m = 0 by arithmetic +*) +val FACT_EQ_1 = store_thm( + "FACT_EQ_1", + ``!n. (FACT n = 1) <=> n <= 1``, + rpt strip_tac >> + Cases_on `n` >> + rw[FACT_0] >> + rw[FACT] >> + `!m. SUC m <= 1 <=> (m = 0)` by decide_tac >> + metis_tac[FACT_0]); + +(* Theorem: (FACT n = n) <=> (n = 1) \/ (n = 2) *) +(* Proof: + If part: (FACT n = n) ==> (n = 1) \/ (n = 2) + Note n <> 0 by FACT_0: FACT 0 = 1 + ==> ?m. n = SUC m by num_CASES + Thus SUC m * FACT m = SUC m by FACT + = SUC m * 1 by MULT_RIGHT_1 + ==> FACT m = 1 by EQ_MULT_LCANCEL, SUC_NOT + or m <= 1 by FACT_EQ_1 + Thus m = 0 or 1 by arithmetic + or n = 1 or 2 by ONE, TWO + + Only-if part: (FACT 1 = 1) /\ (FACT 2 = 2) + Note FACT 1 = 1 by FACT_1 + and FACT 2 = 2 by FACT_2 +*) +val FACT_EQ_SELF = store_thm( + "FACT_EQ_SELF", + ``!n. (FACT n = n) <=> (n = 1) \/ (n = 2)``, + rw[EQ_IMP_THM] >| [ + `n <> 0` by metis_tac[FACT_0, DECIDE``1 <> 0``] >> + `?m. n = SUC m` by metis_tac[num_CASES] >> + fs[FACT] >> + `FACT m = 1` by metis_tac[MULT_LEFT_1, EQ_MULT_RCANCEL, SUC_NOT] >> + `m <= 1` by rw[GSYM FACT_EQ_1] >> + decide_tac, + rw[FACT_1], + rw[FACT_2] + ]); + +(* Theorem: 0 < n ==> n <= FACT n *) +(* Proof: + Note n <> 0 by 0 < n + ==> ?m. n = SUC m by num_CASES + Thus FACT n + = FACT (SUC m) by n = SUC m + = (SUC m) * FACT m by FACT_LESS: 0 < FACT m + >= (SUC m) by LE_MULT_CANCEL_LBARE + >= n by n = SUC m +*) +val FACT_GE_SELF = store_thm( + "FACT_GE_SELF", + ``!n. 0 < n ==> n <= FACT n``, + rpt strip_tac >> + `?m. n = SUC m` by metis_tac[num_CASES, NOT_ZERO_LT_ZERO] >> + rw[FACT] >> + rw[FACT_LESS]); + +(* Theorem: 0 < n ==> (FACT (n-1) = FACT n DIV n) *) +(* Proof: + Since n = SUC(n-1) by SUC_PRE, 0 < n. + and FACT n = n * FACT (n-1) by FACT + = FACT (n-1) * n by MULT_COMM + = FACT (n-1) * n + 0 by ADD_0 + Hence FACT (n-1) = FACT n DIV n by DIV_UNIQUE, 0 < n. +*) +val FACT_DIV = store_thm( + "FACT_DIV", + ``!n. 0 < n ==> (FACT (n-1) = FACT n DIV n)``, + rpt strip_tac >> + `n = SUC(n-1)` by decide_tac >> + `FACT n = n * FACT (n-1)` by metis_tac[FACT] >> + `_ = FACT (n-1) * n + 0` by rw[MULT_COMM] >> + metis_tac[DIV_UNIQUE]); + val _ = export_theory(); diff --git a/src/num/extra_theories/gcdScript.sml b/src/num/extra_theories/gcdScript.sml index e917987834..87434477e6 100644 --- a/src/num/extra_theories/gcdScript.sml +++ b/src/num/extra_theories/gcdScript.sml @@ -1,29 +1,43 @@ -(* gcd = greatest common divisor *) +(* ------------------------------------------------------------------------- *) +(* Elementary Number Theory - a collection of useful results for numbers *) +(* (gcd = greatest common divisor) *) +(* *) +(* Author: (Joseph) Hing-Lun Chan (Australian National University, 2019) *) +(* ------------------------------------------------------------------------- *) -open HolKernel Parse boolLib TotalDefn BasicProvers - arithmeticTheory dividesTheory simpLib boolSimps - Induction; +open HolKernel Parse boolLib BasicProvers -open numSimps metisLib; +open prim_recTheory arithmeticTheory dividesTheory simpLib boolSimps + Induction TotalDefn numSimps; -val arith_ss = bool_ss ++ numSimps.ARITH_ss +open numSimps metisLib; +val arith_ss = bool_ss ++ ARITH_ss; +val std_ss = arith_ss; val ARW = RW_TAC arith_ss val DECIDE = Drule.EQT_ELIM o Arith.ARITH_CONV; fun DECIDE_TAC (g as (asl,_)) = - ((MAP_EVERY UNDISCH_TAC (filter numSimps.is_arith asl) THEN + ((MAP_EVERY UNDISCH_TAC (filter is_arith asl) THEN CONV_TAC Arith.ARITH_CONV) ORELSE tautLib.TAUT_TAC) g; +val decide_tac = DECIDE_TAC; +val metis_tac = METIS_TAC; +val rw = SRW_TAC [ARITH_ss]; +val qabbrev_tac = Q.ABBREV_TAC; +fun simp l = ASM_SIMP_TAC (srw_ss() ++ ARITH_ss) l; +fun fs l = FULL_SIMP_TAC (srw_ss() ++ ARITH_ss) l; + val _ = new_theory "gcd"; -val IS_GCD = Q.new_definition +val is_gcd_def = Q.new_definition ("is_gcd_def", `is_gcd a b c <=> divides c a /\ divides c b /\ !d. divides d a /\ divides d b ==> divides d c`); +val IS_GCD = is_gcd_def; val IS_GCD_UNIQUE = store_thm("IS_GCD_UNIQUE", Term `!a b c d. is_gcd a b c /\ is_gcd a b d ==> (c = d)`, @@ -75,6 +89,7 @@ val GCD = val gcd_ind = GEN_ALL (DB.fetch "-" "gcd_ind"); +Overload coprime = “\x y. gcd x y = 1” (* from examples/algebra *) val GCD_IS_GCD = store_thm("GCD_IS_GCD", @@ -100,6 +115,9 @@ val GCD_SYM = store_thm("GCD_SYM", Term `!a b. gcd a b = gcd b a`, PROVE_TAC[GCD_IS_GCD,IS_GCD_UNIQUE,IS_GCD_SYM]); +(* |- gcd a b = gcd b a *) +val GCD_COMM = save_thm("GCD_COMM", GCD_SYM |> SPEC_ALL); + val GCD_0R = store_thm("GCD_0R", Term `!a. gcd a 0 = a`, PROVE_TAC[GCD_IS_GCD,IS_GCD_UNIQUE,IS_GCD_0R]); @@ -110,6 +128,13 @@ val GCD_0L = store_thm("GCD_0L", PROVE_TAC[GCD_IS_GCD,IS_GCD_UNIQUE,IS_GCD_0L]); val _ = export_rewrites ["GCD_0L"] +(* Theorem: (gcd 0 x = x) /\ (gcd x 0 = x) *) +(* Proof: by GCD_0L, GCD_0R *) +val GCD_0 = store_thm( + "GCD_0", + ``!x. (gcd 0 x = x) /\ (gcd x 0 = x)``, + rw_tac std_ss[GCD_0L, GCD_0R]); + val GCD_ADD_R = store_thm("GCD_ADD_R", Term `!a b. gcd a (a+b) = gcd a b`, ARW[] THEN MATCH_MP_TAC (SPECL[Term `a:num`, Term `a+b`] IS_GCD_UNIQUE) @@ -264,6 +289,13 @@ val LINEAR_GCD = store_thm( PROVE_TAC[LINEAR_GCD_AUX] ]); +(* Theorem: 0 < j ==> ?p q. p * j = q * k + gcd j k *) +(* Proof: by LINEAR_GCD, GCD_SYM *) +val GCD_LINEAR = store_thm( + "GCD_LINEAR", + ``!j k. 0 < j ==> ?p q. p * j = q * k + gcd j k``, + metis_tac[LINEAR_GCD, GCD_SYM, NOT_ZERO]); + val gcd_lemma0 = prove( ``!a b. gcd a b = if b <= a then gcd (a - b) b else gcd a (b - a)``, @@ -363,6 +395,9 @@ val LCM_COMM = store_thm( ``lcm a b = lcm b a``, SRW_TAC [][lcm_def, GCD_SYM, MULT_COMM]); +(* |- !a b. lcm a b = lcm b a *) +val LCM_SYM = save_thm("LCM_SYM", LCM_COMM |> GEN ``b:num`` |> GEN ``a:num``); + val LCM_LE = store_thm( "LCM_LE", ``0 < m /\ 0 < n ==> (m <= lcm m n) /\ (m <= lcm n m)``, @@ -458,4 +493,1160 @@ Proof rewrite_tac[LESS_EQ_ADD] QED +(* ------------------------------------------------------------------------- *) +(* Basic GCD, LCM Theorems (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: If 0 < n, 0 < m, let g = gcd n m, then 0 < g and n MOD g = 0 and m MOD g = 0 *) +(* Proof: + 0 < n ==> n <> 0, 0 < m ==> m <> 0, by NOT_ZERO_LT_ZERO + hence g = gcd n m <> 0, or 0 < g. by GCD_EQ_0 + g = gcd n m ==> (g divides n) /\ (g divides m) by GCD_IS_GCD, is_gcd_def + ==> (n MOD g = 0) /\ (m MOD g = 0) by DIVIDES_MOD_0 +*) +val GCD_DIVIDES = store_thm( + "GCD_DIVIDES", + ``!m n. 0 < n /\ 0 < m ==> 0 < gcd n m /\ (n MOD (gcd n m) = 0) /\ (m MOD (gcd n m) = 0)``, + ntac 3 strip_tac >> + conj_asm1_tac >- + metis_tac[GCD_EQ_0, NOT_ZERO_LT_ZERO] >> + metis_tac[GCD_IS_GCD, is_gcd_def, DIVIDES_MOD_0]); + +(* Theorem: gcd n (gcd n m) = gcd n m *) +(* Proof: + If n = 0, + gcd 0 (gcd n m) = gcd n m by GCD_0L + If m = 0, + gcd n (gcd n 0) + = gcd n n by GCD_0R + = n = gcd n 0 by GCD_REF + If n <> 0, m <> 0, d <> 0 by GCD_EQ_0 + Since d divides n, n MOD d = 0 + gcd n d + = gcd d n by GCD_SYM + = gcd (n MOD d) d by GCD_EFFICIENTLY, d <> 0 + = gcd 0 d by GCD_DIVIDES + = d by GCD_0L +*) +val GCD_GCD = store_thm( + "GCD_GCD", + ``!m n. gcd n (gcd n m) = gcd n m``, + rpt strip_tac >> + Cases_on `n = 0` >- rw[] >> + Cases_on `m = 0` >- rw[] >> + `0 < n /\ 0 < m` by decide_tac >> + metis_tac[GCD_SYM, GCD_EFFICIENTLY, GCD_DIVIDES, GCD_EQ_0, GCD_0L]); + +(* Theorem: GCD m n * LCM m n = m * n *) +(* Proof: + By lcm_def: + lcm m n = if (m = 0) \/ (n = 0) then 0 else m * n DIV gcd m n + If m = 0, + gcd 0 n * lcm 0 n = n * 0 = 0 = 0 * n, hence true. + If n = 0, + gcd m 0 * lcm m 0 = m * 0 = 0 = m * 0, hence true. + If m <> 0, n <> 0, + gcd m n * lcm m n = gcd m n * (m * n DIV gcd m n) = m * n. +*) +val GCD_LCM = store_thm( + "GCD_LCM", + ``!m n. gcd m n * lcm m n = m * n``, + rw[lcm_def] >> + `0 < m /\ 0 < n` by decide_tac >> + `0 < gcd m n /\ (n MOD gcd m n = 0)` by rw[GCD_DIVIDES] >> + qabbrev_tac `d = gcd m n` >> + `m * n = (m * n) DIV d * d + (m * n) MOD d` by rw[DIVISION] >> + `(m * n) MOD d = 0` by metis_tac[MOD_TIMES2, ZERO_MOD, MULT_0] >> + metis_tac[ADD_0, MULT_COMM]); + +(* temporarily make divides an infix *) +val _ = temp_set_fixity "divides" (Infixl 480); (* relation is 450, +/- is 500, * is 600. *) + +(* Theorem: m divides (lcm m n) /\ n divides (lcm m n) *) +(* Proof: by LCM_IS_LEAST_COMMON_MULTIPLE *) +val LCM_DIVISORS = store_thm( + "LCM_DIVISORS", + ``!m n. m divides (lcm m n) /\ n divides (lcm m n)``, + rw[LCM_IS_LEAST_COMMON_MULTIPLE]); + +(* Theorem: m divides p /\ n divides p ==> (lcm m n) divides p *) +(* Proof: by LCM_IS_LEAST_COMMON_MULTIPLE *) +val LCM_IS_LCM = store_thm( + "LCM_IS_LCM", + ``!m n p. m divides p /\ n divides p ==> (lcm m n) divides p``, + rw[LCM_IS_LEAST_COMMON_MULTIPLE]); + +(* Theorem: (lcm m n = 0) <=> ((m = 0) \/ (n = 0)) *) +(* Proof: + If part: lcm m n = 0 ==> m = 0 \/ n = 0 + By contradiction, suppse m = 0 /\ n = 0. + Then gcd m n = 0 by GCD_EQ_0 + and m * n = 0 by MULT_EQ_0 + but (gcd m n) * (lcm m n) = m * n by GCD_LCM + so RHS <> 0, but LHS = 0 by MULT_0 + This is a contradiction. + Only-if part: m = 0 \/ n = 0 ==> lcm m n = 0 + True by LCM_0 +*) +val LCM_EQ_0 = store_thm( + "LCM_EQ_0", + ``!m n. (lcm m n = 0) <=> ((m = 0) \/ (n = 0))``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `(gcd m n) * (lcm m n) = m * n` by rw[GCD_LCM] >> + `gcd m n <> 0` by rw[GCD_EQ_0] >> + `m * n <> 0` by rw[MULT_EQ_0] >> + metis_tac[MULT_0], + rw[LCM_0], + rw[LCM_0] + ]); + +(* Theorem: lcm a a = a *) +(* Proof: + If a = 0, + lcm 0 0 = 0 by LCM_0 + If a <> 0, + (gcd a a) * (lcm a a) = a * a by GCD_LCM + a * (lcm a a) = a * a by GCD_REF + lcm a a = a by MULT_LEFT_CANCEL, a <> 0 +*) +val LCM_REF = store_thm( + "LCM_REF", + ``!a. lcm a a = a``, + metis_tac[num_CASES, LCM_0, GCD_LCM, GCD_REF, MULT_LEFT_CANCEL]); + +(* Theorem: a divides n /\ b divides n ==> (lcm a b) divides n *) +(* Proof: same as LCM_IS_LCM *) +val LCM_DIVIDES = store_thm( + "LCM_DIVIDES", + ``!n a b. a divides n /\ b divides n ==> (lcm a b) divides n``, + rw[LCM_IS_LCM]); +(* +> LCM_IS_LCM |> ISPEC ``a:num`` |> ISPEC ``b:num`` |> ISPEC ``n:num`` |> GEN_ALL; +val it = |- !n b a. a divides n /\ b divides n ==> lcm a b divides n: thm +*) + +(* Theorem: 0 < m \/ 0 < n ==> 0 < gcd m n *) +(* Proof: by GCD_EQ_0, NOT_ZERO_LT_ZERO *) +val GCD_POS = store_thm( + "GCD_POS", + ``!m n. 0 < m \/ 0 < n ==> 0 < gcd m n``, + metis_tac[GCD_EQ_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < m /\ 0 < n ==> 0 < lcm m n *) +(* Proof: by LCM_EQ_0, NOT_ZERO_LT_ZERO *) +val LCM_POS = store_thm( + "LCM_POS", + ``!m n. 0 < m /\ 0 < n ==> 0 < lcm m n``, + metis_tac[LCM_EQ_0, NOT_ZERO_LT_ZERO]); + +(* Theorem: n divides m <=> gcd n m = n *) +(* Proof: + If part: + n divides m ==> ?k. m = k * n by divides_def + gcd n m + = gcd n (k * n) + = gcd (n * 1) (n * k) by MULT_RIGHT_1, MULT_COMM + = n * gcd 1 k by GCD_COMMON_FACTOR + = n * 1 by GCD_1 + = n by MULT_RIGHT_1 + Only-if part: gcd n m = n ==> n divides m + If n = 0, gcd 0 m = m by GCD_0L + But gcd n m = n = 0 by givien + hence m = 0, + and 0 divides 0 is true by DIVIDES_REFL + If n <> 0, + If m = 0, LHS true by GCD_0R + RHS true by ALL_DIVIDES_0 + If m <> 0, + then 0 < n and 0 < m, + gcd n m = gcd (m MOD n) n by GCD_EFFICIENTLY + if (m MOD n) = 0 + then n divides m by DIVIDES_MOD_0 + If (m MOD n) <> 0, + so (m MOD n) MOD (gcd n m) = 0 by GCD_DIVIDES + or (m MOD n) MOD n = 0 by gcd n m = n, given + or m MOD n = 0 by MOD_MOD + contradicting (m MOD n) <> 0, hence true. +*) +val divides_iff_gcd_fix = store_thm( + "divides_iff_gcd_fix", + ``!m n. n divides m <=> (gcd n m = n)``, + rw[EQ_IMP_THM] >| [ + `?k. m = k * n` by rw[GSYM divides_def] >> + `gcd n m = gcd (n * 1) (n * k)` by rw[MULT_COMM] >> + rw[GCD_COMMON_FACTOR, GCD_1], + Cases_on `n = 0` >- + metis_tac[GCD_0L, DIVIDES_REFL] >> + Cases_on `m = 0` >- + metis_tac[GCD_0R, ALL_DIVIDES_0] >> + `0 < n /\ 0 < m` by decide_tac >> + Cases_on `m MOD n = 0` >- + metis_tac[DIVIDES_MOD_0] >> + `0 < m MOD n` by decide_tac >> + metis_tac[GCD_EFFICIENTLY, GCD_DIVIDES, MOD_MOD] + ]); + +(* Theorem: !m n. n divides m <=> (lcm n m = m) *) +(* Proof: + If n = 0, + n divides m <=> m = 0 by ZERO_DIVIDES + and lcm 0 0 = 0 = m by LCM_0 + If n <> 0, + gcd n m * lcm n m = n * m by GCD_LCM + If part: n divides m ==> (lcm n m = m) + Then gcd n m = n by divides_iff_gcd_fix + so n * lcm n m = n * m by above + lcm n m = m by MULT_LEFT_CANCEL, n <> 0 + Only-if part: lcm n m = m ==> n divides m + If m = 0, n divdes 0 = true by ALL_DIVIDES_0 + If m <> 0, + Then gcd n m * m = n * m by above + or gcd n m = n by MULT_RIGHT_CANCEL, m <> 0 + so n divides m by divides_iff_gcd_fix +*) +val divides_iff_lcm_fix = store_thm( + "divides_iff_lcm_fix", + ``!m n. n divides m <=> (lcm n m = m)``, + rpt strip_tac >> + Cases_on `n = 0` >- + metis_tac[ZERO_DIVIDES, LCM_0] >> + metis_tac[GCD_LCM, MULT_LEFT_CANCEL, MULT_RIGHT_CANCEL, divides_iff_gcd_fix, ALL_DIVIDES_0]); + +(* ------------------------------------------------------------------------- *) +(* Consequences of Coprime. *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: coprime n x /\ coprime n y ==> coprime n (x * y) *) +(* Proof: + gcd n x = 1 ==> no common factor between x and n + gcd n y = 1 ==> no common factor between y and n + Hence there is no common factor between (x * y) and n, or gcd n (x * y) = 1 + + gcd n (x * y) = gcd n y by GCD_CANCEL_MULT, since coprime n x. + = 1 by given +*) +val PRODUCT_WITH_GCD_ONE = store_thm( + "PRODUCT_WITH_GCD_ONE", + ``!n x y. coprime n x /\ coprime n y ==> coprime n (x * y)``, + metis_tac[GCD_CANCEL_MULT]); + +(* Theorem: For 0 < n, coprime n x ==> coprime n (x MOD n) *) +(* Proof: + Since n <> 0, + 1 = gcd n x by given + = gcd (x MOD n) n by GCD_EFFICIENTLY + = gcd n (x MOD n) by GCD_SYM +*) +val MOD_WITH_GCD_ONE = store_thm( + "MOD_WITH_GCD_ONE", + ``!n x. 0 < n /\ coprime n x ==> coprime n (x MOD n)``, + rpt strip_tac >> + `0 <> n` by decide_tac >> + metis_tac[GCD_EFFICIENTLY, GCD_SYM]); + +(* Theorem: (c = gcd a b) <=> + c divides a /\ c divides b /\ !x. x divides a /\ x divides b ==> x divides c *) +(* Proof: + By GCD_IS_GREATEST_COMMON_DIVISOR + (gcd a b) divides a [1] + and (gcd a b) divides b [2] + and !p. p divides a /\ p divides b ==> p divides (gcd a b) [3] + Hence if part is true, and for the only-if part, + We have c divides (gcd a b) by [3] above, + and (gcd a b) divides c by [1], [2] and the given implication + Therefore c = gcd a b by DIVIDES_ANTISYM +*) +val GCD_PROPERTY = store_thm( + "GCD_PROPERTY", + ``!a b c. (c = gcd a b) <=> + c divides a /\ c divides b /\ !x. x divides a /\ x divides b ==> x divides c``, + rw[GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_ANTISYM, EQ_IMP_THM]); + +(* Theorem: gcd a (gcd b c) = gcd (gcd a b) c *) +(* Proof: + Since (gcd a (gcd b c)) divides a by GCD_PROPERTY + (gcd a (gcd b c)) divides b by GCD_PROPERTY, DIVIDES_TRANS + (gcd a (gcd b c)) divides c by GCD_PROPERTY, DIVIDES_TRANS + (gcd (gcd a b) c) divides a by GCD_PROPERTY, DIVIDES_TRANS + (gcd (gcd a b) c) divides b by GCD_PROPERTY, DIVIDES_TRANS + (gcd (gcd a b) c) divides c by GCD_PROPERTY + We have + (gcd (gcd a b) c) divides (gcd b c) by GCD_PROPERTY + and (gcd (gcd a b) c) divides (gcd a (gcd b c)) by GCD_PROPERTY + Also (gcd a (gcd b c)) divides (gcd a b) by GCD_PROPERTY + and (gcd a (gcd b c)) divides (gcd (gcd a b) c) by GCD_PROPERTY + Therefore gcd a (gcd b c) = gcd (gcd a b) c by DIVIDES_ANTISYM +*) +val GCD_ASSOC = store_thm( + "GCD_ASSOC", + ``!a b c. gcd a (gcd b c) = gcd (gcd a b) c``, + rpt strip_tac >> + `(gcd a (gcd b c)) divides a` by metis_tac[GCD_PROPERTY] >> + `(gcd a (gcd b c)) divides b` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> + `(gcd a (gcd b c)) divides c` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> + `(gcd (gcd a b) c) divides a` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> + `(gcd (gcd a b) c) divides b` by metis_tac[GCD_PROPERTY, DIVIDES_TRANS] >> + `(gcd (gcd a b) c) divides c` by metis_tac[GCD_PROPERTY] >> + `(gcd (gcd a b) c) divides (gcd a (gcd b c))` by metis_tac[GCD_PROPERTY] >> + `(gcd a (gcd b c)) divides (gcd (gcd a b) c)` by metis_tac[GCD_PROPERTY] >> + rw[DIVIDES_ANTISYM]); + +(* Note: + With identity by GCD_1: (gcd 1 x = 1) /\ (gcd x 1 = 1) + GCD forms a monoid in numbers. +*) + +(* Theorem: gcd a (gcd b c) = gcd b (gcd a c) *) +(* Proof: + gcd a (gcd b c) + = gcd (gcd a b) c by GCD_ASSOC + = gcd (gcd b a) c by GCD_SYM + = gcd b (gcd a c) by GCD_ASSOC +*) +val GCD_ASSOC_COMM = store_thm( + "GCD_ASSOC_COMM", + ``!a b c. gcd a (gcd b c) = gcd b (gcd a c)``, + metis_tac[GCD_ASSOC, GCD_SYM]); + +(* Theorem: (c = lcm a b) <=> + a divides c /\ b divides c /\ !x. a divides x /\ b divides x ==> c divides x *) +(* Proof: + By LCM_IS_LEAST_COMMON_MULTIPLE + a divides (lcm a b) [1] + and b divides (lcm a b) [2] + and !p. a divides p /\ divides b p ==> divides (lcm a b) p [3] + Hence if part is true, and for the only-if part, + We have c divides (lcm a b) by implication and [1], [2] + and (lcm a b) divides c by [3] + Therefore c = lcm a b by DIVIDES_ANTISYM +*) +val LCM_PROPERTY = store_thm( + "LCM_PROPERTY", + ``!a b c. (c = lcm a b) <=> + a divides c /\ b divides c /\ !x. a divides x /\ b divides x ==> c divides x``, + rw[LCM_IS_LEAST_COMMON_MULTIPLE, DIVIDES_ANTISYM, EQ_IMP_THM]); + +(* Theorem: lcm a (lcm b c) = lcm (lcm a b) c *) +(* Proof: + Since a divides (lcm a (lcm b c)) by LCM_PROPERTY + b divides (lcm a (lcm b c)) by LCM_PROPERTY, DIVIDES_TRANS + c divides (lcm a (lcm b c)) by LCM_PROPERTY, DIVIDES_TRANS + a divides (lcm (lcm a b) c) by LCM_PROPERTY, DIVIDES_TRANS + b divides (lcm (lcm a b) c) by LCM_PROPERTY, DIVIDES_TRANS + c divides (lcm (lcm a b) c) by LCM_PROPERTY + We have + (lcm b c) divides (lcm (lcm a b) c) by LCM_PROPERTY + and (lcm a (lcm b c)) divides (lcm (lcm a b) c) by LCM_PROPERTY + Also (lcm a b) divides (lcm a (lcm b c)) by LCM_PROPERTY + and (lcm (lcm a b) c) divides (lcm a (lcm b c)) by LCM_PROPERTY + Therefore lcm a (lcm b c) = lcm (lcm a b) c by DIVIDES_ANTISYM +*) +val LCM_ASSOC = store_thm( + "LCM_ASSOC", + ``!a b c. lcm a (lcm b c) = lcm (lcm a b) c``, + rpt strip_tac >> + `a divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY] >> + `b divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> + `c divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> + `a divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> + `b divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY, DIVIDES_TRANS] >> + `c divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY] >> + `(lcm a (lcm b c)) divides (lcm (lcm a b) c)` by metis_tac[LCM_PROPERTY] >> + `(lcm (lcm a b) c) divides (lcm a (lcm b c))` by metis_tac[LCM_PROPERTY] >> + rw[DIVIDES_ANTISYM]); + +(* Note: + With the identity by LCM_0: (lcm 0 x = 0) /\ (lcm x 0 = 0) + LCM forms a monoid in numbers. +*) + +(* Theorem: lcm a (lcm b c) = lcm b (lcm a c) *) +(* Proof: + lcm a (lcm b c) + = lcm (lcm a b) c by LCM_ASSOC + = lcm (lcm b a) c by LCM_COMM + = lcm b (lcm a c) by LCM_ASSOC +*) +val LCM_ASSOC_COMM = store_thm( + "LCM_ASSOC_COMM", + ``!a b c. lcm a (lcm b c) = lcm b (lcm a c)``, + metis_tac[LCM_ASSOC, LCM_COMM]); + +(* Theorem: b <= a ==> gcd (a - b) b = gcd a b *) +(* Proof: + gcd (a - b) b + = gcd b (a - b) by GCD_SYM + = gcd (b + (a - b)) b by GCD_ADD_L + = gcd (a - b + b) b by ADD_COMM + = gcd a b by SUB_ADD, b <= a. + +Note: If a < b, a - b = 0 for num, hence gcd (a - b) b = gcd 0 b = b. +*) +val GCD_SUB_L = store_thm( + "GCD_SUB_L", + ``!a b. b <= a ==> (gcd (a - b) b = gcd a b)``, + metis_tac[GCD_SYM, GCD_ADD_L, ADD_COMM, SUB_ADD]); + +(* Theorem: a <= b ==> gcd a (b - a) = gcd a b *) +(* Proof: + gcd a (b - a) + = gcd (b - a) a by GCD_SYM + = gcd b a by GCD_SUB_L + = gcd a b by GCD_SYM +*) +val GCD_SUB_R = store_thm( + "GCD_SUB_R", + ``!a b. a <= b ==> (gcd a (b - a) = gcd a b)``, + metis_tac[GCD_SYM, GCD_SUB_L]); + +(* Theorem: prime a ==> a divides b iff a divides b ** n for any n *) +(* Proof: + by induction on n. + Base case: 0 < 0 ==> (a divides b <=> a divides (b ** 0)) + True since 0 < 0 is False. + Step case: 0 < n ==> (a divides b <=> a divides (b ** n)) ==> + 0 < SUC n ==> (a divides b <=> a divides (b ** SUC n)) + i.e. 0 < n ==> (a divides b <=> a divides (b ** n))==> + a divides b <=> a divides (b * b ** n) by EXP + If n = 0, b ** 0 = 1 by EXP + Hence true. + If n <> 0, 0 < n, + If part: a divides b /\ 0 < n ==> (a divides b <=> a divides (b ** n)) ==> a divides (b ** n) + True by DIVIDES_MULT. + Only-if part: a divides (b * b ** n) /\ 0 < n ==> (a divides b <=> a divides (b ** n)) ==> a divides (b ** n) + Since prime a, a divides b, or a divides (b ** n) by P_EUCLIDES + Either case is true. +*) +val DIVIDES_EXP_BASE = store_thm( + "DIVIDES_EXP_BASE", + ``!a b n. prime a /\ 0 < n ==> (a divides b <=> a divides (b ** n))``, + rpt strip_tac >> + Induct_on `n` >- + rw[] >> + rw[EXP] >> + Cases_on `n = 0` >- + rw[EXP] >> + `0 < n` by decide_tac >> + rw[EQ_IMP_THM] >- + metis_tac[DIVIDES_MULT] >> + `a divides b \/ a divides (b ** n)` by rw[P_EUCLIDES] >> + metis_tac[]); + +(* Theorem: coprime m n ==> LCM m n = m * n *) +(* Proof: + By GCD_LCM, with gcd m n = 1. +*) +val LCM_COPRIME = store_thm( + "LCM_COPRIME", + ``!m n. coprime m n ==> (lcm m n = m * n)``, + metis_tac[GCD_LCM, MULT_LEFT_1]); + +(* Theorem: 0 < m ==> (gcd m n = gcd m (n MOD m)) *) +(* Proof: + gcd m n + = gcd (n MOD m) m by GCD_EFFICIENTLY, m <> 0 + = gcd m (n MOD m) by GCD_SYM +*) +val GCD_MOD = store_thm( + "GCD_MOD", + ``!m n. 0 < m ==> (gcd m n = gcd m (n MOD m))``, + rw[Once GCD_EFFICIENTLY, GCD_SYM]); + +(* Theorem: 0 < m ==> (gcd n m = gcd (n MOD m) m) *) +(* Proof: by GCD_MOD, GCD_COMM *) +val GCD_MOD_COMM = store_thm( + "GCD_MOD_COMM", + ``!m n. 0 < m ==> (gcd n m = gcd (n MOD m) m)``, + metis_tac[GCD_MOD, GCD_COMM]); + +(* Theorem: gcd a (b * a + c) = gcd a c *) +(* Proof: + If a = 0, + Then b * 0 + c = c by arithmetic + Hence trivially true. + If a <> 0, + gcd a (b * a + c) + = gcd ((b * a + c) MOD a) a by GCD_EFFICIENTLY, 0 < a + = gcd (c MOD a) a by MOD_TIMES, 0 < a + = gcd a c by GCD_EFFICIENTLY, 0 < a +*) +val GCD_EUCLID = store_thm( + "GCD_EUCLID", + ``!a b c. gcd a (b * a + c) = gcd a c``, + rpt strip_tac >> + Cases_on `a = 0` >- + rw[] >> + metis_tac[GCD_EFFICIENTLY, MOD_TIMES, NOT_ZERO_LT_ZERO]); + +(* Theorem: gcd (b * a + c) a = gcd a c *) +(* Proof: by GCD_EUCLID, GCD_SYM *) +val GCD_REDUCE = store_thm( + "GCD_REDUCE", + ``!a b c. gcd (b * a + c) a = gcd a c``, + rw[GCD_EUCLID, GCD_SYM]); + +(* Theorem alias *) +Theorem GCD_REDUCE_BY_COPRIME = GCD_CANCEL_MULT; +(* val GCD_REDUCE_BY_COPRIME = + |- !m n k. coprime m k ==> gcd m (k * n) = gcd m n: thm *) + +(* ------------------------------------------------------------------------- *) +(* Coprime Theorems (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: coprime n (n + 1) *) +(* Proof: + Since n < n + 1 ==> n <= n + 1, + gcd n (n + 1) + = gcd n (n + 1 - n) by GCD_SUB_R + = gcd n 1 by arithmetic + = 1 by GCD_1 +*) +val coprime_SUC = store_thm( + "coprime_SUC", + ``!n. coprime n (n + 1)``, + rw[GCD_SUB_R]); + +(* Theorem: 0 < n ==> coprime n (n - 1) *) +(* Proof: + gcd n (n - 1) + = gcd (n - 1) n by GCD_SYM + = gcd (n - 1) (n - 1 + 1) by SUB_ADD, 0 <= n + = 1 by coprime_SUC +*) +val coprime_PRE = store_thm( + "coprime_PRE", + ``!n. 0 < n ==> coprime n (n - 1)``, + metis_tac[GCD_SYM, coprime_SUC, DECIDE``!n. 0 < n ==> (n - 1 + 1 = n)``]); + +(* Theorem: coprime 0 n ==> n = 1 *) +(* Proof: + gcd 0 n = n by GCD_0L + = 1 by coprime 0 n +*) +val coprime_0L = store_thm( + "coprime_0L", + ``!n. coprime 0 n <=> (n = 1)``, + rw[GCD_0L]); + +(* Theorem: coprime n 0 ==> n = 1 *) +(* Proof: + gcd n 0 = n by GCD_0L + = 1 by coprime n 0 +*) +val coprime_0R = store_thm( + "coprime_0R", + ``!n. coprime n 0 <=> (n = 1)``, + rw[GCD_0R]); + +(* Theorem: (coprime 0 n <=> n = 1) /\ (coprime n 0 <=> n = 1) *) +(* Proof: by coprime_0L, coprime_0R *) +Theorem coprime_0: + !n. (coprime 0 n <=> n = 1) /\ (coprime n 0 <=> n = 1) +Proof + simp[coprime_0L, coprime_0R] +QED + +(* Theorem: coprime x y = coprime y x *) +(* Proof: + coprime x y + means gcd x y = 1 + so gcd y x = 1 by GCD_SYM + thus coprime y x +*) +val coprime_sym = store_thm( + "coprime_sym", + ``!x y. coprime x y = coprime y x``, + rw[GCD_SYM]); + +(* Theorem: coprime k n /\ n <> 1 ==> k <> 0 *) +(* Proof: by coprime_0L *) +val coprime_neq_1 = store_thm( + "coprime_neq_1", + ``!n k. coprime k n /\ n <> 1 ==> k <> 0``, + fs[coprime_0L]); + +(* Theorem: coprime k n /\ 1 < n ==> 0 < k *) +(* Proof: by coprime_neq_1 *) +val coprime_gt_1 = store_thm( + "coprime_gt_1", + ``!n k. coprime k n /\ 1 < n ==> 0 < k``, + metis_tac[coprime_neq_1, NOT_ZERO_LT_ZERO, DECIDE``~(1 < 1)``]); + +(* Note: gcd (c ** n) m = gcd c m is false when n = 0, where c ** 0 = 1. *) + +(* Theorem: coprime c m ==> !n. coprime (c ** n) m *) +(* Proof: by induction on n. + Base case: coprime (c ** 0) m + Since c ** 0 = 1 by EXP + and coprime 1 m is true by GCD_1 + Step case: coprime c m /\ coprime (c ** n) m ==> coprime (c ** SUC n) m + coprime c m means + coprime m c by GCD_SYM + + gcd m (c ** SUC n) + = gcd m (c * c ** n) by EXP + = gcd m (c ** n) by GCD_CANCEL_MULT, coprime m c + = 1 by induction hypothesis + Hence coprime m (c ** SUC n) + or coprime (c ** SUC n) m by GCD_SYM +*) +val coprime_exp = store_thm( + "coprime_exp", + ``!c m. coprime c m ==> !n. coprime (c ** n) m``, + rpt strip_tac >> + Induct_on `n` >- + rw[EXP, GCD_1] >> + metis_tac[EXP, GCD_CANCEL_MULT, GCD_SYM]); + +(* Theorem: coprime a b ==> !n. coprime a (b ** n) *) +(* Proof: by coprime_exp, GCD_SYM *) +val coprime_exp_comm = store_thm( + "coprime_exp_comm", + ``!a b. coprime a b ==> !n. coprime a (b ** n)``, + metis_tac[coprime_exp, GCD_SYM]); + +(* Theorem: coprime x z /\ coprime y z ==> coprime (x * y) z *) +(* Proof: + By GCD_CANCEL_MULT: + |- !m n k. coprime m k ==> (gcd m (k * n) = gcd m n) + Hence follows by coprime_sym. +*) +val coprime_product_coprime = store_thm( + "coprime_product_coprime", + ``!x y z. coprime x z /\ coprime y z ==> coprime (x * y) z``, + metis_tac[GCD_CANCEL_MULT, GCD_SYM]); + +(* Theorem: coprime z x /\ coprime z y ==> coprime z (x * y) *) +(* Proof: + Note gcd z x = 1 by given + ==> gcd z (x * y) + = gcd z y by GCD_CANCEL_MULT + = 1 by given +*) +val coprime_product_coprime_sym = store_thm( + "coprime_product_coprime_sym", + ``!x y z. coprime z x /\ coprime z y ==> coprime z (x * y)``, + rw[GCD_CANCEL_MULT]); +(* This is the same as PRODUCT_WITH_GCD_ONE *) + +(* Theorem: coprime x z ==> (coprime y z <=> coprime (x * y) z) *) +(* Proof: + If part: coprime x z /\ coprime y z ==> coprime (x * y) z + True by coprime_product_coprime + Only-if part: coprime x z /\ coprime (x * y) z ==> coprime y z + Let d = gcd y z. + Then d divides z /\ d divides y by GCD_PROPERTY + so d divides (x * y) by DIVIDES_MULT, MULT_COMM + or d divides (gcd (x * y) z) by GCD_PROPERTY + d divides 1 by coprime (x * y) z + ==> d = 1 by DIVIDES_ONE + or coprime y z by notation +*) +val coprime_product_coprime_iff = store_thm( + "coprime_product_coprime_iff", + ``!x y z. coprime x z ==> (coprime y z <=> coprime (x * y) z)``, + rw[EQ_IMP_THM] >- + rw[coprime_product_coprime] >> + qabbrev_tac `d = gcd y z` >> + metis_tac[GCD_PROPERTY, DIVIDES_MULT, MULT_COMM, DIVIDES_ONE]); + +(* Theorem: a divides n /\ b divides n /\ coprime a b ==> (a * b) divides n *) +(* Proof: by LCM_COPRIME, LCM_DIVIDES *) +val coprime_product_divides = store_thm( + "coprime_product_divides", + ``!n a b. a divides n /\ b divides n /\ coprime a b ==> (a * b) divides n``, + metis_tac[LCM_COPRIME, LCM_DIVIDES]); + +(* Theorem: 0 < m /\ coprime m n ==> coprime m (n MOD m) *) +(* Proof: + gcd m n + = if m = 0 then n else gcd (n MOD m) m by GCD_EFFICIENTLY + = gcd (n MOD m) m by decide_tac, m <> 0 + = gcd m (n MOD m) by GCD_SYM + Hence true since coprime m n <=> gcd m n = 1. +*) +val coprime_mod = store_thm( + "coprime_mod", + ``!m n. 0 < m /\ coprime m n ==> coprime m (n MOD m)``, + metis_tac[GCD_EFFICIENTLY, GCD_SYM, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < m ==> (coprime m n = coprime m (n MOD m)) *) +(* Proof: by GCD_MOD *) +val coprime_mod_iff = store_thm( + "coprime_mod_iff", + ``!m n. 0 < m ==> (coprime m n = coprime m (n MOD m))``, + rw[Once GCD_MOD]); + +(* Theorem: 1 < n /\ coprime n k /\ 1 < p /\ p divides n ==> ~(p divides k) *) +(* Proof: + First, 1 < n ==> n <> 0 and n <> 1 + If k = 0, gcd n k = n by GCD_0R + But coprime n k means gcd n k = 1, so k <> 0. + By contradiction. + If p divides k, and given p divides n, + then p divides gcd n k = 1 by GCD_IS_GREATEST_COMMON_DIVISOR, n <> 0 and k <> 0 + or p = 1 by DIVIDES_ONE + which contradicts 1 < p. +*) +val coprime_factor_not_divides = store_thm( + "coprime_factor_not_divides", + ``!n k. 1 < n /\ coprime n k ==> !p. 1 < p /\ p divides n ==> ~(p divides k)``, + rpt strip_tac >> + `n <> 0 /\ n <> 1 /\ p <> 1` by decide_tac >> + metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR, DIVIDES_ONE, GCD_0R]); + +(* Theorem: m divides n ==> !k. coprime n k ==> coprime m k *) +(* Proof: + Let d = gcd m k. + Then d divides m /\ d divides k by GCD_IS_GREATEST_COMMON_DIVISOR + ==> d divides n by DIVIDES_TRANS + so d divides 1 by GCD_IS_GREATEST_COMMON_DIVISOR, coprime n k + ==> d = 1 by DIVIDES_ONE +*) +val coprime_factor_coprime = store_thm( + "coprime_factor_coprime", + ``!m n. m divides n ==> !k. coprime n k ==> coprime m k``, + rpt strip_tac >> + qabbrev_tac `d = gcd m k` >> + `d divides m /\ d divides k` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> + `d divides n` by metis_tac[DIVIDES_TRANS] >> + `d divides 1` by metis_tac[GCD_IS_GREATEST_COMMON_DIVISOR] >> + rw[GSYM DIVIDES_ONE]); + +(* Idea: common factor of two coprime numbers. *) + +(* Theorem: coprime a b /\ c divides a /\ c divides b ==> c = 1 *) +(* Proof: + Note c divides gcd a b by GCD_PROPERTY + or c divides 1 by coprime a b + so c = 1 by DIVIDES_ONE +*) +Theorem coprime_common_factor: + !a b c. coprime a b /\ c divides a /\ c divides b ==> c = 1 +Proof + metis_tac[GCD_PROPERTY, DIVIDES_ONE] +QED + +(* Theorem: prime p /\ ~(p divides n) ==> coprime p n *) +(* Proof: + Since divides p 0, so n <> 0. by ALL_DIVIDES_0 + If n = 1, certainly coprime p n by GCD_1 + If n <> 1, + Let gcd p n = d. + Since d divides p by GCD_IS_GREATEST_COMMON_DIVISOR + and prime p by given + so d = 1 or d = p by prime_def + but d <> p by divides_iff_gcd_fix + Hence d = 1, or coprime p n. +*) +val prime_not_divides_coprime = store_thm( + "prime_not_divides_coprime", + ``!n p. prime p /\ ~(p divides n) ==> coprime p n``, + rpt strip_tac >> + `n <> 0` by metis_tac[ALL_DIVIDES_0] >> + Cases_on `n = 1` >- + rw[] >> + `0 < p` by rw[PRIME_POS] >> + `p <> 0` by decide_tac >> + metis_tac[prime_def, divides_iff_gcd_fix, GCD_IS_GREATEST_COMMON_DIVISOR]); + +(* Theorem: prime p /\ ~(coprime p n) ==> p divides n *) +(* Proof: + Let d = gcd p n. + Then d divides p by GCD_IS_GREATEST_COMMON_DIVISOR + ==> d = p by prime_def + Thus p divides n by divides_iff_gcd_fix + + Or: this is just the inverse of prime_not_divides_coprime. +*) +val prime_not_coprime_divides = store_thm( + "prime_not_coprime_divides", + ``!n p. prime p /\ ~(coprime p n) ==> p divides n``, + metis_tac[prime_not_divides_coprime]); + +(* Theorem: 1 < n /\ prime p /\ p divides n ==> !k. coprime n k ==> coprime p k *) +(* Proof: + Since coprime n k /\ p divides n + ==> ~(p divides k) by coprime_factor_not_divides + Then prime p /\ ~(p divides k) + ==> coprime p k by prime_not_divides_coprime +*) +val coprime_prime_factor_coprime = store_thm( + "coprime_prime_factor_coprime", + ``!n p. 1 < n /\ prime p /\ p divides n ==> !k. coprime n k ==> coprime p k``, + metis_tac[coprime_factor_not_divides, prime_not_divides_coprime, ONE_LT_PRIME]); + +(* This is better: +coprime_factor_coprime +|- !m n. m divides n ==> !k. coprime n k ==> coprime m k +*) + +(* Idea: a characterisation of the coprime property of two numbers. *) + +(* Theorem: coprime m n <=> !p. prime p ==> ~(p divides m /\ p divides n) *) +(* Proof: + If part: coprime m n /\ prime p ==> ~(p divides m) \/ ~(p divides n) + By contradiction, suppose p divides m /\ p divides n. + Then p = 1 by coprime_common_factor + This contradicts prime p by NOT_PRIME_1 + Only-if part: !p. prime p ==> ~(p divides m) \/ ~(p divides m) ==> coprime m n + Let d = gcd m n. + By contradiction, suppose d <> 1. + Then ?p. prime p /\ p divides d by PRIME_FACTOR, d <> 1. + Now d divides m /\ d divides n by GCD_PROPERTY + so p divides m /\ p divides n by DIVIDES_TRANS + This contradicts the assumption. +*) +Theorem coprime_by_prime_factor: + !m n. coprime m n <=> !p. prime p ==> ~(p divides m /\ p divides n) +Proof + rw[EQ_IMP_THM] >- + metis_tac[coprime_common_factor, NOT_PRIME_1] >> + qabbrev_tac `d = gcd m n` >> + spose_not_then strip_assume_tac >> + `?p. prime p /\ p divides d` by rw[PRIME_FACTOR] >> + `d divides m /\ d divides n` by metis_tac[GCD_PROPERTY] >> + metis_tac[DIVIDES_TRANS] +QED + +(* Idea: coprime_by_prime_factor with reduced testing of primes, useful in practice. *) + +(* Theorem: 0 < m /\ 0 < n ==> + (coprime m n <=> + !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m /\ p divides n)) *) +(* Proof: + If part: coprime m n /\ prime p /\ ... ==> ~(p divides m) \/ ~(p divides n) + By contradiction, suppose p divides m /\ p divides n. + Then p = 1 by coprime_common_factor + This contradicts prime p by NOT_PRIME_1 + Only-if part: !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m) \/ ~(p divides m) ==> coprime m n + Let d = gcd m n. + By contradiction, suppose d <> 1. + Then ?p. prime p /\ p divides d by PRIME_FACTOR, d <> 1. + Now d divides m /\ d divides n by GCD_PROPERTY + so p divides m /\ p divides n by DIVIDES_TRANS + Thus p <= m /\ p <= n by DIVIDES_LE, 0 < m, 0 < n + This contradicts the assumption. +*) +Theorem coprime_by_prime_factor_le: + !m n. 0 < m /\ 0 < n ==> + (coprime m n <=> + !p. prime p /\ p <= m /\ p <= n ==> ~(p divides m /\ p divides n)) +Proof + rw[EQ_IMP_THM] >- + metis_tac[coprime_common_factor, NOT_PRIME_1] >> + qabbrev_tac `d = gcd m n` >> + spose_not_then strip_assume_tac >> + `?p. prime p /\ p divides d` by rw[PRIME_FACTOR] >> + `d divides m /\ d divides n` by metis_tac[GCD_PROPERTY] >> + `0 < p` by rw[PRIME_POS] >> + metis_tac[DIVIDES_TRANS, DIVIDES_LE] +QED + +(* Note: counter-example for converse: gcd 3 11 = 1, but ~(3 divides 10). *) + +(* Theorem: 0 < m /\ n divides m ==> coprime n (PRE m) *) +(* Proof: + Since n divides m + ==> ?q. m = q * n by divides_def + Also 0 < m means m <> 0, + ==> ?k. m = SUC k by num_CASES + = k + 1 by ADD1 + so m - k = 1, k = PRE m. + Let d = gcd n k. + Then d divides n /\ d divides k by GCD_IS_GREATEST_COMMON_DIVISOR + and d divides n ==> d divides m by DIVIDES_MULTIPLE, m = q * n + so d divides (m - k) by DIVIDES_SUB + or d divides 1 by m - k = 1 + ==> d = 1 by DIVIDES_ONE +*) +val divides_imp_coprime_with_predecessor = store_thm( + "divides_imp_coprime_with_predecessor", + ``!m n. 0 < m /\ n divides m ==> coprime n (PRE m)``, + rpt strip_tac >> + `?q. m = q * n` by rw[GSYM divides_def] >> + `m <> 0` by decide_tac >> + `?k. m = k + 1` by metis_tac[num_CASES, ADD1] >> + `(k = PRE m) /\ (m - k = 1)` by decide_tac >> + qabbrev_tac `d = gcd n k` >> + `d divides n /\ d divides k` by rw[GCD_IS_GREATEST_COMMON_DIVISOR, Abbr`d`] >> + `d divides m` by rw[DIVIDES_MULTIPLE] >> + `d divides (m - k)` by rw[DIVIDES_SUB] >> + metis_tac[DIVIDES_ONE]); + +(* Theorem: coprime p n ==> (gcd (p * m) n = gcd m n) *) +(* Proof: + Note coprime p n means coprime n p by GCD_SYM + gcd (p * m) n + = gcd n (p * m) by GCD_SYM + = gcd n p by GCD_CANCEL_MULT +*) +val gcd_coprime_cancel = store_thm( + "gcd_coprime_cancel", + ``!m n p. coprime p n ==> (gcd (p * m) n = gcd m n)``, + rw[GCD_CANCEL_MULT, GCD_SYM]); + +(* The following is a direct, but tricky, proof of the above result *) + +(* Theorem: coprime p n ==> (gcd (p * m) n = gcd m n) *) +(* Proof: + gcd (p * m) n + = gcd (p * m) (n * 1) by MULT_RIGHT_1 + = gcd (p * m) (n * (gcd m 1)) by GCD_1 + = gcd (p * m) (gcd (n * m) n) by GCD_COMMON_FACTOR + = gcd (gcd (p * m) (n * m)) n by GCD_ASSOC + = gcd (m * (gcd p n)) n by GCD_COMMON_FACTOR, MULT_COMM + = gcd (m * 1) n by coprime p n + = gcd m n by MULT_RIGHT_1 + + Simple proof of GCD_CANCEL_MULT: + (a*c, b) = (a*c , b*1) = (a * c, b * (c, 1)) = (a * c, b * c, b) = ((a, b) * c, b) = (c, b) since (a,b) = 1. +*) +Theorem gcd_coprime_cancel[allow_rebind]: + !m n p. coprime p n ==> (gcd (p * m) n = gcd m n) +Proof + rpt strip_tac >> + ‘gcd (p * m) n = gcd (p * m) (n * (gcd m 1))’ by rw[GCD_1] >> + ‘_ = gcd (p * m) (gcd (n * m) n)’ by rw[GSYM GCD_COMMON_FACTOR] >> + ‘_ = gcd (gcd (p * m) (n * m)) n’ by rw[GCD_ASSOC] >> + ‘_ = gcd m n’ by rw[GCD_COMMON_FACTOR, MULT_COMM] >> + rw[] +QED + +(* Theorem: prime p /\ prime q /\ p <> q ==> coprime p q *) +(* Proof: + Let d = gcd p q. + By contradiction, suppose d <> 1. + Then d divides p /\ d divides q by GCD_PROPERTY + so d = 1 or d = p by prime_def + and d = 1 or d = q by prime_def + But p <> q by given + so d = 1, contradicts d <> 1. +*) +val primes_coprime = store_thm( + "primes_coprime", + ``!p q. prime p /\ prime q /\ p <> q ==> coprime p q``, + spose_not_then strip_assume_tac >> + qabbrev_tac `d = gcd p q` >> + `d divides p /\ d divides q` by metis_tac[GCD_PROPERTY] >> + metis_tac[prime_def]); + +(* Theorem: prime p ==> p cannot divide k! for p > k. + prime p /\ k < p ==> ~(p divides (FACT k)) *) +(* Proof: + Since all terms of k! are less than p, and p has only 1 and p as factor. + By contradiction, and induction on k. + Base case: prime p ==> 0 < p ==> p divides (FACT 0) ==> F + Since FACT 0 = 1 by FACT + and p divides 1 <=> p = 1 by DIVIDES_ONE + but prime p ==> 1 < p by ONE_LT_PRIME + so this is a contradiction. + Step case: prime p /\ k < p ==> p divides (FACT k) ==> F ==> + SUC k < p ==> p divides (FACT (SUC k)) ==> F + Since FACT (SUC k) = SUC k * FACT k by FACT + and prime p /\ p divides (FACT (SUC k)) + ==> p divides (SUC k), + or p divides (FACT k) by P_EUCLIDES + But SUC k < p, so ~(p divides (SUC k)) by NOT_LT_DIVIDES + Hence p divides (FACT k) ==> F by induction hypothesis +*) +val PRIME_BIG_NOT_DIVIDES_FACT = store_thm( + "PRIME_BIG_NOT_DIVIDES_FACT", + ``!p k. prime p /\ k < p ==> ~(p divides (FACT k))``, + (spose_not_then strip_assume_tac) >> + Induct_on `k` >| [ + rw[FACT] >> + metis_tac[ONE_LT_PRIME, LESS_NOT_EQ], + rw[FACT] >> + (spose_not_then strip_assume_tac) >> + `k < p /\ 0 < SUC k` by decide_tac >> + metis_tac[P_EUCLIDES, NOT_LT_DIVIDES] + ]); + +(* Theorem: n divides m ==> coprime n (SUC m) *) +(* Proof: + If n = 0, + then m = 0 by ZERO_DIVIDES + gcd 0 (SUC 0) + = SUC 0 by GCD_0L + = 1 by ONE + If n = 1, + gcd 1 (SUC m) = 1 by GCD_1 + If n <> 0, + gcd n (SUC m) + = gcd ((SUC m) MOD n) n by GCD_EFFICIENTLY + = gcd 1 n by n divides m + = 1 by GCD_1 +*) +val divides_imp_coprime_with_successor = store_thm( + "divides_imp_coprime_with_successor", + ``!m n. n divides m ==> coprime n (SUC m)``, + rpt strip_tac >> + Cases_on `n = 0` >- + rw[GSYM ZERO_DIVIDES] >> + Cases_on `n = 1` >- + rw[] >> + `0 < n /\ 1 < n` by decide_tac >> + `m MOD n = 0` by rw[GSYM DIVIDES_MOD_0] >> + `(SUC m) MOD n = (m + 1) MOD n` by rw[ADD1] >> + `_ = (m MOD n + 1 MOD n) MOD n` by rw[MOD_PLUS] >> + `_ = (0 + 1) MOD n` by rw[ONE_MOD] >> + `_ = 1` by rw[ONE_MOD] >> + metis_tac[GCD_EFFICIENTLY, GCD_1]); + +(* ------------------------------------------------------------------------- *) +(* Modulo Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Idea: eliminate modulus n when a MOD n = b MOD n. *) + +(* Theorem: 0 < n /\ b <= a ==> (a MOD n = b MOD n <=> ?c. a = b + c * n) *) +(* Proof: + If part: a MOD n = b MOD n ==> ?c. a = b + c * n + Note ?c. a = c * n + b MOD n by MOD_EQN + and b = (b DIV n) * n + b MOD n by DIVISION + Let q = b DIV n, + Then q * n <= c * n by LE_ADD_RCANCEL, b <= a + a + = c * n + (b - q * n) by above + = b + (c * n - q * n) by arithmetic, q * n <= c * n + = b + (c - q) * n by RIGHT_SUB_DISTRIB + Take (c - q) as c. + Only-if part: (b + c * n) MOD n = b MOD n + This is true by MOD_TIMES +*) +Theorem MOD_MOD_EQN: + !n a b. 0 < n /\ b <= a ==> (a MOD n = b MOD n <=> ?c. a = b + c * n) +Proof + rw[EQ_IMP_THM] >| [ + `?c. a = c * n + b MOD n` by metis_tac[MOD_EQN] >> + `b = (b DIV n) * n + b MOD n` by rw[DIVISION] >> + qabbrev_tac `q = b DIV n` >> + `q * n <= c * n` by metis_tac[LE_ADD_RCANCEL] >> + `a = b + (c * n - q * n)` by decide_tac >> + `_ = b + (c - q) * n` by decide_tac >> + metis_tac[], + simp[] + ] +QED + +(* Idea: a convenient form of MOD_PLUS. *) + +(* Theorem: 0 < n ==> (x + y) MOD n = (x + y MOD n) MOD n *) +(* Proof: + Let q = y DIV n, r = y MOD n. + Then y = q * n + r by DIVISION, 0 < n + (x + y) MOD n + = (x + (q * n + r)) MOD n by above + = (q * n + (x + r)) MOD n by arithmetic + = (x + r) MOD n by MOD_PLUS, MOD_EQ_0 +*) +Theorem MOD_PLUS2: + !n x y. 0 < n ==> (x + y) MOD n = (x + y MOD n) MOD n +Proof + rpt strip_tac >> + `y = (y DIV n) * n + y MOD n` by metis_tac[DIVISION] >> + simp[] +QED + +(* Theorem: If n > 0, a MOD n = b MOD n ==> (a - b) MOD n = 0 *) +(* Proof: + a = (a DIV n)*n + (a MOD n) by DIVISION + b = (b DIV n)*n + (b MOD n) by DIVISION + Hence a - b = ((a DIV n) - (b DIV n))* n + = a multiple of n + Therefore (a - b) MOD n = 0. +*) +val MOD_EQ_DIFF = store_thm( + "MOD_EQ_DIFF", + ``!n a b. 0 < n /\ (a MOD n = b MOD n) ==> ((a - b) MOD n = 0)``, + rpt strip_tac >> + `a = a DIV n * n + a MOD n` by metis_tac[DIVISION] >> + `b = b DIV n * n + b MOD n` by metis_tac[DIVISION] >> + `a - b = (a DIV n - b DIV n) * n` by rw_tac arith_ss[] >> + metis_tac[MOD_EQ_0]); +(* Note: The reverse is true only when a >= b: + (a-b) MOD n = 0 cannot imply a MOD n = b MOD n *) + +(* Theorem: if n > 0, a >= b, then (a - b) MOD n = 0 <=> a MOD n = b MOD n *) +(* Proof: + (a-b) MOD n = 0 + ==> n divides (a-b) by MOD_0_DIVIDES + ==> (a-b) = k*n for some k by divides_def + ==> a = b + k*n need b <= a to apply arithmeticTheory.SUB_ADD + ==> a MOD n = b MOD n by arithmeticTheory.MOD_TIMES + + The converse is given by MOD_EQ_DIFF. +*) +val MOD_EQ = store_thm( + "MOD_EQ", + ``!n a b. 0 < n /\ b <= a ==> (((a - b) MOD n = 0) <=> (a MOD n = b MOD n))``, + rw[EQ_IMP_THM] >| [ + `?k. a - b = k * n` by metis_tac[DIVIDES_MOD_0, divides_def] >> + `a = k*n + b` by rw_tac arith_ss[] >> + metis_tac[MOD_TIMES], + metis_tac[MOD_EQ_DIFF] + ]); + +(* Theorem: [Euclid's Lemma] A prime a divides product iff the prime a divides factor. + [in MOD notation] For prime p, x*y MOD p = 0 <=> x MOD p = 0 or y MOD p = 0 *) +(* Proof: + The if part is already in P_EUCLIDES: + !p a b. prime p /\ divides p (a * b) ==> p divides a \/ p divides b + Convert the divides to MOD by DIVIDES_MOD_0. + The only-if part is: + (1) divides p x ==> divides p (x * y) + (2) divides p y ==> divides p (x * y) + Both are true by DIVIDES_MULT: !a b c. a divides b ==> a divides (b * c). + The symmetry of x and y can be taken care of by MULT_COMM. +*) +val EUCLID_LEMMA = store_thm( + "EUCLID_LEMMA", + ``!p x y. prime p ==> (((x * y) MOD p = 0) <=> (x MOD p = 0) \/ (y MOD p = 0))``, + rpt strip_tac >> + `0 < p` by rw[PRIME_POS] >> + rw[GSYM DIVIDES_MOD_0, EQ_IMP_THM] >> + metis_tac[P_EUCLIDES, DIVIDES_MULT, MULT_COMM]); + +(* Idea: For prime p, FACT (p-1) MOD p <> 0 *) + +(* Theorem: prime p /\ n < p ==> FACT n MOD p <> 0 *) +(* Proof: + Note 1 < p by ONE_LT_PRIME + By induction on n. + Base: 0 < p ==> (FACT 0 MOD p = 0) ==> F + Note FACT 0 = 1 by FACT_0 + and 1 MOD p = 1 by LESS_MOD, 1 < p + and 1 = 0 is F. + Step: n < p ==> (FACT n MOD p = 0) ==> F ==> + SUC n < p ==> (FACT (SUC n) MOD p = 0) ==> F + If n = 0, SUC 0 = 1 by ONE + Note FACT 1 = 1 by FACT_1 + and 1 MOD p = 1 by LESS_MOD, 1 < p + and 1 = 0 is F. + If n <> 0, 0 < n. + (FACT (SUC n)) MOD p = 0 + <=> (SUC n * FACT n) MOD p = 0 by FACT + Note (SUC n) MOD p <> 0 by MOD_LESS, SUC n < p + and (FACT n) MOD p <> 0 by induction hypothesis + so (SUC n * FACT n) MOD p <> 0 by EUCLID_LEMMA + This is a contradiction. +*) +Theorem FACT_MOD_PRIME: + !p n. prime p /\ n < p ==> FACT n MOD p <> 0 +Proof + rpt strip_tac >> + `1 < p` by rw[ONE_LT_PRIME] >> + Induct_on `n` >- + simp[FACT_0] >> + Cases_on `n = 0` >- + simp[FACT_1] >> + rw[FACT] >> + `n < p` by decide_tac >> + `(SUC n) MOD p <> 0` by fs[] >> + metis_tac[EUCLID_LEMMA] +QED + val _ = export_theory(); diff --git a/src/num/extra_theories/logrootScript.sml b/src/num/extra_theories/logrootScript.sml index f2f5063f32..d5f5bf0a14 100644 --- a/src/num/extra_theories/logrootScript.sml +++ b/src/num/extra_theories/logrootScript.sml @@ -1,6 +1,6 @@ -open HolKernel boolLib Parse -open Parse BasicProvers metisLib simpLib -open arithmeticTheory pairTheory combinTheory +open HolKernel boolLib Parse BasicProvers; + +open metisLib simpLib arithmeticTheory pairTheory combinTheory computeLib; val _ = new_theory "logroot"; @@ -14,8 +14,17 @@ fun DECIDE_TAC (g as (asl, _)) = (MAP_EVERY UNDISCH_TAC (filter numSimps.is_arith_asm asl) THEN CONV_TAC Arith.ARITH_CONV) g -val DECIDE = EQT_ELIM o Arith.ARITH_CONV -fun simp ths = ASM_SIMP_TAC (srw_ss() ++ ARITH_ss) ths +val decide_tac = DECIDE_TAC; +val metis_tac = METIS_TAC; + +val DECIDE = EQT_ELIM o Arith.ARITH_CONV; +val rw = SRW_TAC [ARITH_ss]; +val std_ss = arith_ss; +val qabbrev_tac = Q.ABBREV_TAC; +val qexists_tac = Q.EXISTS_TAC; +fun simp l = ASM_SIMP_TAC (srw_ss() ++ ARITH_ss) l; +fun fs l = FULL_SIMP_TAC (srw_ss() ++ ARITH_ss) l; +fun rfs l = REV_FULL_SIMP_TAC (srw_ss() ++ ARITH_ss) l; val Define = TotalDefn.Define val zDefine = Lib.with_flag (computeLib.auto_import_definitions, false) Define @@ -29,6 +38,10 @@ val lt_mult2 = Q.prove( THEN METIS_TAC [LE_MULT_LCANCEL, LT_MULT_RCANCEL, LESS_EQ_LESS_TRANS, LESS_OR_EQ]); +(* ------------------------------------------------------------------------- *) +(* Exponential Theorems *) +(* ------------------------------------------------------------------------- *) + val exp_lemma2 = Q.prove( `!a b r. 0 < r ==> a < b ==> a ** r < b ** r`, REPEAT STRIP_TAC @@ -85,6 +98,103 @@ val EXP_LE_ISO = Q.store_thm("EXP_LE_ISO", `!a b r. 0 < r ==> (a <= b <=> a ** r <= b ** r)`, PROVE_TAC [NOT_LESS, exp_lemma3, exp_lemma2, LESS_OR_EQ, NOT_LESS]); +(* Theorem: 0 < m ==> ((n ** m = n) <=> ((m = 1) \/ (n = 0) \/ (n = 1))) *) +(* Proof: + If part: n ** m = n ==> n = 0 \/ n = 1 + By contradiction, assume n <> 0 /\ n <> 1. + Then ?k. m = SUC k by num_CASES, 0 < m + so n ** SUC k = n by n ** m = n + or n * n ** k = n by EXP + ==> n ** k = 1 by MULT_EQ_SELF, 0 < n + ==> n = 1 or k = 0 by EXP_EQ_1 + ==> n = 1 or m = 1, + These contradict n <> 1 and m <> 1. + Only-if part: n ** 1 = n /\ 0 ** m = 0 /\ 1 ** m = 1 + These are true by EXP_1, ZERO_EXP. +*) +val EXP_EQ_SELF = store_thm( + "EXP_EQ_SELF", + ``!n m. 0 < m ==> ((n ** m = n) <=> ((m = 1) \/ (n = 0) \/ (n = 1)))``, + rw_tac std_ss[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `m <> 0` by decide_tac >> + `?k. m = SUC k` by metis_tac[num_CASES] >> + `n * n ** k = n` by fs[EXP] >> + `n ** k = 1` by metis_tac[MULT_EQ_SELF, NOT_ZERO_LT_ZERO] >> + fs[EXP_EQ_1], + rw[], + rw[], + rw[] + ]); + +(* Obtain a theorem *) +val EXP_LE = save_thm("EXP_LE", X_LE_X_EXP |> GEN ``x:num`` |> SPEC ``b:num`` |> GEN_ALL); +(* val EXP_LE = |- !n b. 0 < n ==> b <= b ** n: thm *) + +(* Theorem: 1 < b /\ 1 < n ==> b < b ** n *) +(* Proof: + By contradiction, assume ~(b < b ** n). + Then b ** n <= b by arithmetic + But b <= b ** n by EXP_LE, 0 < n + ==> b ** n = b by EQ_LESS_EQ + ==> b = 1 or n = 0 or n = 1. + All these contradict 1 < b and 1 < n. +*) +val EXP_LT = store_thm( + "EXP_LT", + ``!n b. 1 < b /\ 1 < n ==> b < b ** n``, + spose_not_then strip_assume_tac >> + `b <= b ** n` by rw[EXP_LE] >> + `b ** n = b` by decide_tac >> + rfs[EXP_EQ_SELF]); + +(* Theorem: 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c) *) +(* Proof: + Let d = m - n. + Then 0 < d, and m = n + d by arithmetic + and 0 < a ==> a ** n <> 0 by EXP_EQ_0 + a ** n * b + = a ** (n + d) * c by m = n + d + = (a ** n * a ** d) * c by EXP_ADD + = a ** n * (a ** d * c) by MULT_ASSOC + The result follows by MULT_LEFT_CANCEL +*) +val EXP_LCANCEL = store_thm( + "EXP_LCANCEL", + ``!a b c n m. 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c)``, + rpt strip_tac >> + `0 < m - n /\ (m = n + (m - n))` by decide_tac >> + qabbrev_tac `d = m - n` >> + `a ** n <> 0` by metis_tac[EXP_EQ_0, NOT_ZERO_LT_ZERO] >> + metis_tac[EXP_ADD, MULT_ASSOC, MULT_LEFT_CANCEL]); + +(* Theorem: 0 < a /\ n < m /\ (a ** n * b = a ** m * c) ==> ?d. 0 < d /\ (b = a ** d * c) *) +(* Proof: by EXP_LCANCEL, MULT_COMM. *) +val EXP_RCANCEL = store_thm( + "EXP_RCANCEL", + ``!a b c n m. 0 < a /\ n < m /\ (b * a ** n = c * a ** m) ==> ?d. 0 < d /\ (b = c * a ** d)``, + metis_tac[EXP_LCANCEL, MULT_COMM]); + +(* +EXP_POS |- !m n. 0 < m ==> 0 < m ** n +ONE_LT_EXP |- !x y. 1 < x ** y <=> 1 < x /\ 0 < y +ZERO_LT_EXP |- 0 < x ** y <=> 0 < x \/ (y = 0) +*) + +(* Theorem: 0 < m ==> 1 <= m ** n *) +(* Proof: + 0 < m ==> 0 < m ** n by EXP_POS + or 1 <= m ** n by arithmetic +*) +val ONE_LE_EXP = store_thm( + "ONE_LE_EXP", + ``!m n. 0 < m ==> 1 <= m ** n``, + metis_tac[EXP_POS, DECIDE``!x. 0 < x <=> 1 <= x``]); + +(* ------------------------------------------------------------------------- *) +(* ROOT and LOG *) +(* ------------------------------------------------------------------------- *) + val ROOT_exists = Q.store_thm("ROOT_exists", `!r n. 0 < r ==> ?rt. rt ** r <= n /\ n < SUC rt ** r`, Induct_on `n` @@ -199,7 +309,6 @@ val LOG_BASE = Q.store_thm("LOG_BASE", THEN RW_TAC arith_ss [LEFT_ADD_DISTRIB, RIGHT_ADD_DISTRIB, EXP_ADD, ADD1, EXP_1, square]); - val LOG_EXP = Q.store_thm("LOG_EXP", `!n a b. 1n < a /\ 0 < b ==> (LOG a (a ** n * b) = n + LOG a b)`, REPEAT STRIP_TAC @@ -687,6 +796,620 @@ QED val () = Theory.delete_const "iSQRTd" +(* ------------------------------------------------------------------------- *) +(* ROOT Computation *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: ROOT n (a ** n) = a *) +(* Proof: + Since a < SUC a by LESS_SUC + a ** n < (SUC a) ** n by EXP_BASE_LT_MONO + Let b = a ** n, + Then a ** n <= b by LESS_EQ_REFL + and b < (SUC a) ** n by above + Hence a = ROOT n b by ROOT_UNIQUE +*) +val ROOT_POWER = store_thm( + "ROOT_POWER", + ``!a n. 1 < a /\ 0 < n ==> (ROOT n (a ** n) = a)``, + rw[EXP_BASE_LT_MONO, ROOT_UNIQUE]); + +(* Theorem: 0 < m /\ (b ** m = n) ==> (b = ROOT m n) *) +(* Proof: + Note n <= n by LESS_EQ_REFL + so b ** m <= n by b ** m = n + Also b < SUC b by LESS_SUC + so b ** m < (SUC b) ** m by EXP_EXP_LT_MONO, 0 < m + so n < (SUC b) ** m by b ** m = n + Thus b = ROOT m n by ROOT_UNIQUE +*) +val ROOT_FROM_POWER = store_thm( + "ROOT_FROM_POWER", + ``!m n b. 0 < m /\ (b ** m = n) ==> (b = ROOT m n)``, + rpt strip_tac >> + rw[ROOT_UNIQUE]); + +(* Theorem: 0 < m ==> (ROOT m 0 = 0) *) +(* Proof: + Note 0 ** m = 0 by EXP_0 + Thus 0 = ROOT m 0 by ROOT_FROM_POWER +*) +val ROOT_OF_0 = store_thm( + "ROOT_OF_0[simp]", + ``!m. 0 < m ==> (ROOT m 0 = 0)``, + rw[ROOT_FROM_POWER]); + +(* Theorem: 0 < m ==> (ROOT m 1 = 1) *) +(* Proof: + Note 1 ** m = 1 by EXP_1 + Thus 1 = ROOT m 1 by ROOT_FROM_POWER +*) +val ROOT_OF_1 = store_thm( + "ROOT_OF_1[simp]", + ``!m. 0 < m ==> (ROOT m 1 = 1)``, + rw[ROOT_FROM_POWER]); + +(* Theorem: 0 < r ==> !n p. (ROOT r n = p) <=> (p ** r <= n /\ n < SUC p ** r) *) +(* Proof: + If part: 0 < r ==> ROOT r n ** r <= n /\ n < SUC (ROOT r n) ** r + This is true by ROOT, 0 < r + Only-if part: p ** r <= n /\ n < SUC p ** r ==> ROOT r n = p + This is true by ROOT_UNIQUE +*) +val ROOT_THM = store_thm( + "ROOT_THM", + ``!r. 0 < r ==> !n p. (ROOT r n = p) <=> (p ** r <= n /\ n < SUC p ** r)``, + metis_tac[ROOT, ROOT_UNIQUE]); + +(* Theorem: 0 < m ==> !n. (ROOT m n = 0) <=> (n = 0) *) +(* Proof: + If part: ROOT m n = 0 ==> n = 0 + Note n < SUC (ROOT m n) ** r by ROOT + or n < SUC 0 ** m by ROOT m n = 0 + so n < 1 by ONE, EXP_1 + or n = 0 by arithmetic + Only-if part: ROOT m 0 = 0, true by ROOT_OF_0 +*) +val ROOT_EQ_0 = store_thm( + "ROOT_EQ_0", + ``!m. 0 < m ==> !n. (ROOT m n = 0) <=> (n = 0)``, + rw[EQ_IMP_THM] >> + `n < 1` by metis_tac[ROOT, EXP_1, ONE] >> + decide_tac); + +(* Theorem: ROOT 1 n = n *) +(* Proof: + Note n ** 1 = n by EXP_1 + so n ** 1 <= n + Also n < SUC n by LESS_SUC + so n < SUC n ** 1 by EXP_1 + Thus ROOT 1 n = n by ROOT_UNIQUE +*) +val ROOT_1 = store_thm( + "ROOT_1[simp]", + ``!n. ROOT 1 n = n``, + rw[ROOT_UNIQUE]); + +(* Theorem: 0 < r ==> + (ROOT r (SUC n) = ROOT r n + if SUC n = (SUC (ROOT r n)) ** r then 1 else 0) *) +(* Proof: + Let x = ROOT r n, y = ROOT r (SUC n). x <= y. + Note n < (SUC x) ** r /\ x ** r <= n by ROOT_THM + and SUC n < (SUC y) ** r /\ y ** r <= SUC n by ROOT_THM + Since n < (SUC x) ** r, + SUC n <= (SUC x) ** r. + If SUC n = (SUC x) ** r, + Then y = ROOT r (SUC n) + = ROOT r ((SUC x) ** r) + = SUC x by ROOT_POWER + If SUC n < (SUC x) ** r, + Then x ** r <= n < SUC n by LESS_SUC + Thus x = y by ROOT_THM +*) +val ROOT_SUC = store_thm( + "ROOT_SUC", + ``!r n. 0 < r ==> + (ROOT r (SUC n) = ROOT r n + if SUC n = (SUC (ROOT r n)) ** r then 1 else 0)``, + rpt strip_tac >> + qabbrev_tac `x = ROOT r n` >> + qabbrev_tac `y = ROOT r (SUC n)` >> + Cases_on `n = 0` >| [ + `x = 0` by rw[ROOT_OF_0, Abbr`x`] >> + `y = 1` by rw[ROOT_OF_1, Abbr`y`] >> + simp[], + `x <> 0` by rw[ROOT_EQ_0, Abbr`x`] >> + `n < (SUC x) ** r /\ x ** r <= n` by metis_tac[ROOT_THM] >> + `SUC n < (SUC y) ** r /\ y ** r <= SUC n` by metis_tac[ROOT_THM] >> + `(SUC n = (SUC x) ** r) \/ SUC n < (SUC x) ** r` by decide_tac >| [ + `1 < SUC x` by decide_tac >> + `y = SUC x` by metis_tac[ROOT_POWER] >> + simp[], + `x ** r <= SUC n` by decide_tac >> + `x = y` by metis_tac[ROOT_THM] >> + simp[] + ] + ]); + +(* +ROOT_SUC; +|- !r n. 0 < r ==> ROOT r (SUC n) = ROOT r n + if SUC n = SUC (ROOT r n) ** r then 1 else 0 +Let z = ROOT r n. + + z(n) + ------------------------------------------------- + n (n+1=(z+1)**r) + +> EVAL ``MAP (ROOT 2) [1 .. 20]``; +val it = |- MAP (ROOT 2) [1 .. 20] = + [1; 1; 1; 2; 2; 2; 2; 2; 3; 3; 3; 3; 3; 3; 3; 4; 4; 4; 4; 4]: thm + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 +*) + +(* Theorem: 0 < m ==> !n. (ROOT m n = 1) <=> (0 < n /\ n < 2 ** m) *) +(* Proof: + ROOT m n = 1 + <=> 1 ** m <= n /\ n < (SUC 1) ** m by ROOT_THM, 0 < m + <=> 1 <= n /\ n < 2 ** m by TWO, EXP_1 + <=> 0 < n /\ n < 2 ** m by arithmetic +*) +val ROOT_EQ_1 = store_thm( + "ROOT_EQ_1", + ``!m. 0 < m ==> !n. (ROOT m n = 1) <=> (0 < n /\ n < 2 ** m)``, + rpt strip_tac >> + `!n. 0 < n <=> 1 <= n` by decide_tac >> + metis_tac[ROOT_THM, TWO, EXP_1]); + +(* Theorem: 0 < m ==> ROOT m n <= n *) +(* Proof: + Let r = ROOT m n. + Note r <= r ** m by X_LE_X_EXP, 0 < m + <= n by ROOT +*) +val ROOT_LE_SELF = store_thm( + "ROOT_LE_SELF", + ``!m n. 0 < m ==> ROOT m n <= n``, + metis_tac[X_LE_X_EXP, ROOT, LESS_EQ_TRANS]); + +(* Theorem: 0 < m ==> ((ROOT m n = n) <=> ((m = 1) \/ (n = 0) \/ (n = 1))) *) +(* Proof: + If part: ROOT m n = n ==> m = 1 \/ n = 0 \/ n = 1 + Note n ** m <= n by ROOT, 0 < r + But n <= n ** m by X_LE_X_EXP, 0 < m + so n ** m = n by EQ_LESS_EQ + ==> m = 1 or n = 0 or n = 1 by EXP_EQ_SELF + Only-if part: ROOT 1 n = n /\ ROOT m 0 = 0 /\ ROOT m 1 = 1 + True by ROOT_1, ROOT_OF_0, ROOT_OF_1. +*) +Theorem ROOT_EQ_SELF: + !m n. 0 < m ==> (ROOT m n = n <=> m = 1 \/ n = 0 \/ n = 1) +Proof + rw_tac std_ss[EQ_IMP_THM] >> rw[] >> + `n ** m <= n` by metis_tac[ROOT] >> + `n <= n ** m` by rw[X_LE_X_EXP] >> + `n ** m = n` by decide_tac >> + fs[] +QED + +(* Theorem: 0 < m ==> (n <= ROOT m n <=> ((m = 1) \/ (n = 0) \/ (n = 1))) *) +(* Proof: + Note ROOT m n <= n by ROOT_LE_SELF + Thus n <= ROOT m n <=> ROOT m n = n by EQ_LESS_EQ + The result follows by ROOT_EQ_SELF +*) +val ROOT_GE_SELF = store_thm( + "ROOT_GE_SELF", + ``!m n. 0 < m ==> (n <= ROOT m n <=> ((m = 1) \/ (n = 0) \/ (n = 1)))``, + metis_tac[ROOT_LE_SELF, ROOT_EQ_SELF, EQ_LESS_EQ]); + +(* +EVAL ``MAP (\k. ROOT k 100) [1 .. 10]``; = [100; 10; 4; 3; 2; 2; 1; 1; 1; 1]: thm + +This shows (ROOT k) is a decreasing function of k, +but this is very hard to prove without some real number theory. +Even this is hard to prove: ROOT 3 n <= ROOT 2 n + +No! -- this can be proved, see below. +*) + +(* Theorem: 0 < a /\ a <= b ==> ROOT b n <= ROOT a n *) +(* Proof: + Let x = ROOT a n, y = ROOT b n. To show: y <= x. + By contradiction, suppose x < y. + Then SUC x <= y. + Note x ** a <= n /\ n < (SUC x) ** a by ROOT + and y ** b <= n /\ n < (SUC y) ** b by ROOT + But a <= b + (SUC x) ** a + <= (SUC x) ** b by EXP_BASE_LEQ_MONO_IMP, 0 < SUC x, a <= b + <= y ** b by EXP_EXP_LE_MONO, 0 < b + This leads to n < (SUC x) ** a <= y ** b <= n, a contradiction. +*) +val ROOT_LE_REVERSE = store_thm( + "ROOT_LE_REVERSE", + ``!a b n. 0 < a /\ a <= b ==> ROOT b n <= ROOT a n``, + rpt strip_tac >> + qabbrev_tac `x = ROOT a n` >> + qabbrev_tac `y = ROOT b n` >> + spose_not_then strip_assume_tac >> + `0 < b /\ SUC x <= y` by decide_tac >> + `x ** a <= n /\ n < (SUC x) ** a` by rw[ROOT, Abbr`x`] >> + `y ** b <= n /\ n < (SUC y) ** b` by rw[ROOT, Abbr`y`] >> + `(SUC x) ** a <= (SUC x) ** b` by rw[EXP_BASE_LEQ_MONO_IMP] >> + `(SUC x) ** b <= y ** b` by rw[EXP_EXP_LE_MONO] >> + decide_tac); + +(* ------------------------------------------------------------------------- *) +(* Square Root *) +(* ------------------------------------------------------------------------- *) + +(* Use overload for SQRT *) +val _ = overload_on ("SQRT", ``\n. ROOT 2 n``); + +(* Theorem: 0 < n ==> (SQRT n) ** 2 <= n /\ n < SUC (SQRT n) ** 2 *) +(* Proof: by ROOT: + |- !r n. 0 < r ==> ROOT r n ** r <= n /\ n < SUC (ROOT r n) ** r +*) +val SQRT_PROPERTY = store_thm( + "SQRT_PROPERTY", + ``!n. (SQRT n) ** 2 <= n /\ n < SUC (SQRT n) ** 2``, + rw[ROOT]); + +(* Get a useful theorem *) +Theorem SQRT_UNIQUE = ROOT_UNIQUE |> SPEC ``2``; +(* val SQRT_UNIQUE = |- !n p. p ** 2 <= n /\ n < SUC p ** 2 ==> SQRT n = p: thm *) + +(* Obtain a theorem *) +val SQRT_THM = save_thm("SQRT_THM", + ROOT_THM |> SPEC ``2`` |> SIMP_RULE (srw_ss())[]); +(* val SQRT_THM = |- !n p. (SQRT n = p) <=> p ** 2 <= n /\ n < SUC p ** 2: thm *) + +(* Theorem: n <= m ==> SQRT n <= SQRT m *) +(* Proof: by ROOT_LE_MONO *) +val SQRT_LE = store_thm( + "SQRT_LE", + ``!n m. n <= m ==> SQRT n <= SQRT m``, + rw[ROOT_LE_MONO]); + +(* Theorem: n < m ==> SQRT n <= SQRT m *) +(* Proof: + Since n < m ==> n <= m by LESS_IMP_LESS_OR_EQ + This is true by ROOT_LE_MONO +*) +val SQRT_LT = store_thm( + "SQRT_LT", + ``!n m. n < m ==> SQRT n <= SQRT m``, + rw[ROOT_LE_MONO, LESS_IMP_LESS_OR_EQ]); + +(* Theorem: SQRT 0 = 0 *) +(* Proof: by ROOT_OF_0 *) +val SQRT_0 = store_thm( + "SQRT_0[simp]", + ``SQRT 0 = 0``, + rw[]); + +(* Theorem: SQRT 1 = 1 *) +(* Proof: by ROOT_OF_1 *) +val SQRT_1 = store_thm( + "SQRT_1[simp]", + ``SQRT 1 = 1``, + rw[]); + +(* Theorem: SQRT n = 0 <=> n = 0 *) +(* Proof: + If part: SQRT n = 0 ==> n = 0. + By contradiction, suppose n <> 0. + This means 1 <= n + Hence SQRT 1 <= SQRT n by SQRT_LE + so 1 <= SQRT n by SQRT_1 + This contradicts SQRT n = 0. + Only-if part: n = 0 ==> SQRT n = 0 + True since SQRT 0 = 0 by SQRT_0 +*) +val SQRT_EQ_0 = store_thm( + "SQRT_EQ_0", + ``!n. (SQRT n = 0) <=> (n = 0)``, + rw[EQ_IMP_THM] >> + spose_not_then strip_assume_tac >> + `1 <= n` by decide_tac >> + `SQRT 1 <= SQRT n` by rw[SQRT_LE] >> + `SQRT 1 = 1` by rw[] >> + decide_tac); + +(* Theorem: SQRT n = 1 <=> n = 1 \/ n = 2 \/ n = 3 *) +(* Proof: + If part: SQRT n = 1 ==> (n = 1) \/ (n = 2) \/ (n = 3). + By contradiction, suppose n <> 1 /\ n <> 2 /\ n <> 3. + Note n <> 0 by SQRT_EQ_0 + This means 4 <= n + Hence SQRT 4 <= SQRT n by SQRT_LE + so 2 <= SQRT n by EVAL_TAC, SQRT 4 = 2 + This contradicts SQRT n = 1. + Only-if part: n = 1 \/ n = 2 \/ n = 3 ==> SQRT n = 1 + All these are true by EVAL_TAC +*) +val SQRT_EQ_1 = store_thm( + "SQRT_EQ_1", + ``!n. (SQRT n = 1) <=> ((n = 1) \/ (n = 2) \/ (n = 3))``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `n <> 0` by metis_tac[SQRT_EQ_0] >> + `4 <= n` by decide_tac >> + `SQRT 4 <= SQRT n` by rw[SQRT_LE] >> + `SQRT 4 = 2` by EVAL_TAC >> + decide_tac, + EVAL_TAC, + EVAL_TAC, + EVAL_TAC + ]); + +(* Theorem: SQRT (n ** 2) = n *) +(* Proof: + If 1 < n, true by ROOT_POWER, 0 < 2 + Otherwise, n = 0 or n = 1. + When n = 0, + SQRT (0 ** 2) = SQRT 0 = 0 by SQRT_0 + When n = 1, + SQRT (1 ** 2) = SQRT 1 = 1 by SQRT_1 +*) +val SQRT_EXP_2 = store_thm( + "SQRT_EXP_2", + ``!n. SQRT (n ** 2) = n``, + rpt strip_tac >> + Cases_on `1 < n` >- + fs[ROOT_POWER] >> + `(n = 0) \/ (n = 1)` by decide_tac >> + rw[]); + +(* Theorem alias *) +val SQRT_OF_SQ = save_thm("SQRT_OF_SQ", SQRT_EXP_2); +(* val SQRT_OF_SQ = |- !n. SQRT (n ** 2) = n: thm *) + +(* Theorem: (n <= SQRT n) <=> ((n = 0) \/ (n = 1)) *) +(* Proof: + If part: (n <= SQRT n) ==> ((n = 0) \/ (n = 1)) + By contradiction, suppose n <> 0 /\ n <> 1. + Then 1 < n, implying n ** 2 <= SQRT n ** 2 by EXP_BASE_LE_MONO + but SQRT n ** 2 <= n by SQRT_PROPERTY + so n ** 2 <= n by LESS_EQ_TRANS + or n * n <= n * 1 by EXP_2 + or n <= 1 by LE_MULT_LCANCEL, n <> 0. + This contradicts 1 < n. + Only-if part: ((n = 0) \/ (n = 1)) ==> (n <= SQRT n) + This is to show: + (1) 0 <= SQRT 0, true by SQRT 0 = 0 by SQRT_0 + (2) 1 <= SQRT 1, true by SQRT 1 = 1 by SQRT_1 +*) +val SQRT_GE_SELF = store_thm( + "SQRT_GE_SELF", + ``!n. (n <= SQRT n) <=> ((n = 0) \/ (n = 1))``, + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `1 < n` by decide_tac >> + `n ** 2 <= SQRT n ** 2` by rw[EXP_BASE_LE_MONO] >> + `SQRT n ** 2 <= n` by rw[SQRT_PROPERTY] >> + `n ** 2 <= n` by metis_tac[LESS_EQ_TRANS] >> + `n * n <= n * 1` by metis_tac[EXP_2, MULT_RIGHT_1] >> + `n <= 1` by metis_tac[LE_MULT_LCANCEL] >> + decide_tac, + rw[], + rw[] + ]); + +(* Theorem: (SQRT n = n) <=> ((n = 0) \/ (n = 1)) *) +(* Proof: by ROOT_EQ_SELF, 0 < 2 *) +val SQRT_EQ_SELF = store_thm( + "SQRT_EQ_SELF", + ``!n. (SQRT n = n) <=> ((n = 0) \/ (n = 1))``, + rw[ROOT_EQ_SELF]); + +(* Theorem: SQRT n < m ==> n < m ** 2 *) +(* Proof: + SQRT n < m + ==> SUC (SQRT n) <= m by arithmetic + ==> (SUC (SQRT m)) ** 2 <= m ** 2 by EXP_EXP_LE_MONO + But n < (SUC (SQRT n)) ** 2 by SQRT_PROPERTY + Thus n < m ** 2 by inequality +*) +Theorem SQRT_LT_IMP: + !n m. SQRT n < m ==> n < m ** 2 +Proof + rpt strip_tac >> + `SUC (SQRT n) <= m` by decide_tac >> + `SUC (SQRT n) ** 2 <= m ** 2` by simp[EXP_EXP_LE_MONO] >> + `n < SUC (SQRT n) ** 2` by simp[SQRT_PROPERTY] >> + decide_tac +QED + +(* Theorem: n < SQRT m ==> n ** 2 < m *) +(* Proof: + n < SQRT m + ==> n ** 2 < (SQRT m) ** 2 by EXP_EXP_LT_MONO + But (SQRT m) ** 2 <= m by SQRT_PROPERTY + Thus n ** 2 < m by inequality +*) +Theorem LT_SQRT_IMP: + !n m. n < SQRT m ==> n ** 2 < m +Proof + rpt strip_tac >> + `n ** 2 < (SQRT m) ** 2` by simp[EXP_EXP_LT_MONO] >> + `(SQRT m) ** 2 <= m` by simp[SQRT_PROPERTY] >> + decide_tac +QED + +(* Theorem: SQRT n < SQRT m ==> n < m *) +(* Proof: + SQRT n < SQRT m + ==> n < (SQRT m) ** 2 by SQRT_LT_IMP + and (SQRT m) ** 2 <= m by SQRT_PROPERTY + so n < m by inequality +*) +Theorem SQRT_LT_SQRT: + !n m. SQRT n < SQRT m ==> n < m +Proof + rpt strip_tac >> + imp_res_tac SQRT_LT_IMP >> + `(SQRT m) ** 2 <= m` by simp[SQRT_PROPERTY] >> + decide_tac +QED + +(* Non-theorems: + SQRT n <= SQRT m ==> n <= m + counter-example: SQRT 5 = 2 = SQRT 4, but 5 > 4. + + n < m ==> SQRT n < SQRT m + counter-example: 4 < 5, but SQRT 4 = 2 = SQRT 5. +*) + +(* ------------------------------------------------------------------------- *) +(* Logarithm *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 1 < a ==> LOG a (a ** n) = n *) +(* Proof: + LOG a (a ** n) + = LOG a ((a ** n) * 1) by MULT_RIGHT_1 + = n + LOG a 1 by LOG_EXP + = n + 0 by LOG_1 + = n by ADD_0 +*) +val LOG_EXACT_EXP = store_thm( + "LOG_EXACT_EXP", + ``!a. 1 < a ==> !n. LOG a (a ** n) = n``, + metis_tac[MULT_RIGHT_1, LOG_EXP, LOG_1, ADD_0, DECIDE``0 < 1``]); + +(* Theorem: 1 < a /\ 0 < b /\ b <= a ** n ==> LOG a b <= n *) +(* Proof: + Given b <= a ** n + LOG a b <= LOG a (a ** n) by LOG_LE_MONO + = n by LOG_EXACT_EXP +*) +val EXP_TO_LOG = store_thm( + "EXP_TO_LOG", + ``!a b n. 1 < a /\ 0 < b /\ b <= a ** n ==> LOG a b <= n``, + metis_tac[LOG_LE_MONO, LOG_EXACT_EXP]); + +(* Theorem: 1 < a /\ 0 < n ==> !p. (LOG a n = p) <=> (a ** p <= n /\ n < a ** SUC p) *) +(* Proof: + If part: 1 < a /\ 0 < n ==> a ** LOG a n <= n /\ n < a ** SUC (LOG a n) + This is true by LOG. + Only-if part: a ** p <= n /\ n < a ** SUC p ==> LOG a n = p + This is true by LOG_UNIQUE +*) +val LOG_THM = store_thm( + "LOG_THM", + ``!a n. 1 < a /\ 0 < n ==> !p. (LOG a n = p) <=> (a ** p <= n /\ n < a ** SUC p)``, + metis_tac[LOG, LOG_UNIQUE]); + +(* Theorem: LOG m n = if m <= 1 \/ (n = 0) then LOG m n + else if n < m then 0 else SUC (LOG m (n DIV m)) *) +(* Proof: by LOG_RWT *) +val LOG_EVAL = store_thm( + "LOG_EVAL", (* was: "LOG_EVAL[compute]" *) + ``!m n. LOG m n = if m <= 1 \/ (n = 0) then LOG m n + else if n < m then 0 else SUC (LOG m (n DIV m))``, + rw[LOG_RWT]); +(* Put to computeLib for LOG evaluation of any base *) + +(* +> EVAL ``MAP (LOG 3) [1 .. 20]``; = + [0; 0; 1; 1; 1; 1; 1; 1; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2]: thm +> EVAL ``MAP (LOG 3) [1 .. 30]``; = + [0; 0; 1; 1; 1; 1; 1; 1; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 3; 3; 3; 3]: thm +*) + +(* Theorem: 1 < a /\ 0 < n ==> + !p. (LOG a n = p) <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n *) +(* Proof: + Note !p. LOG a n = p + <=> n < a ** SUC p /\ a ** p <= n by LOG_THM + <=> n < a ** SUC p /\ a * a ** p <= a * n by LE_MULT_LCANCEL + <=> n < a ** SUC p /\ a ** SUC p <= a * n by EXP + <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n by arithmetic +*) +val LOG_TEST = store_thm( + "LOG_TEST", + ``!a n. 1 < a /\ 0 < n ==> + !p. (LOG a n = p) <=> SUC n <= a ** SUC p /\ a ** SUC p <= a * n``, + rw[EQ_IMP_THM] >| [ + `n < a ** SUC (LOG a n)` by metis_tac[LOG_THM] >> + decide_tac, + `a ** (LOG a n) <= n` by metis_tac[LOG_THM] >> + rw[EXP], + `a * a ** p <= a * n` by fs[EXP] >> + `a ** p <= n` by fs[] >> + `n < a ** SUC p` by decide_tac >> + metis_tac[LOG_THM] + ]); + +(* For continuous functions, log_b (x ** y) = y * log_b x. *) + +(* Theorem: 1 < b /\ 0 < x /\ 0 < n ==> + n * LOG b x <= LOG b (x ** n) /\ LOG b (x ** n) < n * SUC (LOG b x) *) +(* Proof: + Note: +> LOG_THM |> SPEC ``b:num`` |> SPEC ``x:num``; +val it = |- 1 < b /\ 0 < x ==> !p. LOG b x = p <=> b ** p <= x /\ x < b ** SUC p: thm +> LOG_THM |> SPEC ``b:num`` |> SPEC ``(x:num) ** n``; +val it = |- 1 < b /\ 0 < x ** n ==> + !p. LOG b (x ** n) = p <=> b ** p <= x ** n /\ x ** n < b ** SUC p: thm + + Let y = LOG b x, z = LOG b (x ** n). + Then b ** y <= x /\ x < b ** SUC y by LOG_THM, (1) + and b ** z <= x ** n /\ x ** n < b ** SUC z by LOG_THM, (2) + From (1), + b ** (n * y) <= x ** n /\ by EXP_EXP_LE_MONO, EXP_EXP_MULT + x ** n < b ** (n * SUC y) by EXP_EXP_LT_MONO, EXP_EXP_MULT, 0 < n + Cross combine with (2), + b ** (n * y) <= x ** n < b ** SUC z + and b ** z <= x ** n < b ** (n * y) + ==> n * y < SUC z /\ z < n * SUC y by EXP_BASE_LT_MONO + or n * y <= z /\ z < n * SUC y +*) +val LOG_POWER = store_thm( + "LOG_POWER", + ``!b x n. 1 < b /\ 0 < x /\ 0 < n ==> + n * LOG b x <= LOG b (x ** n) /\ LOG b (x ** n) < n * SUC (LOG b x)``, + ntac 4 strip_tac >> + `0 < x ** n` by rw[] >> + qabbrev_tac `y = LOG b x` >> + qabbrev_tac `z = LOG b (x ** n)` >> + `b ** y <= x /\ x < b ** SUC y` by metis_tac[LOG_THM] >> + `b ** z <= x ** n /\ x ** n < b ** SUC z` by metis_tac[LOG_THM] >> + `b ** (y * n) <= x ** n` by rw[EXP_EXP_MULT] >> + `x ** n < b ** ((SUC y) * n)` by rw[EXP_EXP_MULT] >> + `b ** (y * n) < b ** SUC z` by decide_tac >> + `b ** z < b ** (SUC y * n)` by decide_tac >> + `y * n < SUC z` by metis_tac[EXP_BASE_LT_MONO] >> + `z < SUC y * n` by metis_tac[EXP_BASE_LT_MONO] >> + decide_tac); + +(* Theorem: 1 < a /\ 0 < n /\ a <= b ==> LOG b n <= LOG a n *) +(* Proof: + Let x = LOG a n, y = LOG b n. To show: y <= x. + By contradiction, suppose x < y. + Then SUC x <= y. + Note a ** x <= n /\ n < a ** SUC x by LOG_THM + and b ** y <= n /\ n < b ** SUC y by LOG_THM + But a <= b + a ** SUC x + <= b ** SUC x by EXP_EXP_LE_MONO, 0 < SUC x + <= b ** y by EXP_BASE_LEQ_MONO_IMP, SUC x <= y + This leads to n < a ** SUC x <= b ** y <= n, a contradiction. +*) +val LOG_LE_REVERSE = store_thm( + "LOG_LE_REVERSE", + ``!a b n. 1 < a /\ 0 < n /\ a <= b ==> LOG b n <= LOG a n``, + rpt strip_tac >> + qabbrev_tac `x = LOG a n` >> + qabbrev_tac `y = LOG b n` >> + spose_not_then strip_assume_tac >> + `1 < b /\ SUC x <= y` by decide_tac >> + `a ** x <= n /\ n < a ** SUC x` by metis_tac[LOG_THM] >> + `b ** y <= n /\ n < b ** SUC y` by metis_tac[LOG_THM] >> + `a ** SUC x <= b ** SUC x` by rw[EXP_EXP_LE_MONO] >> + `b ** SUC x <= b ** y` by rw[EXP_BASE_LEQ_MONO_IMP] >> + decide_tac); + (* ----------------------------------------------------------------------- *) (* @@ -744,9 +1467,280 @@ val _ = add_conv (``$SQRT``, 1, cbv_SQRT_CONV) compset2; time (CBV_CONV compset2) ``SQRT 123456789123456789123456789``; time (CBV_CONV compset1) ``ROOT 2 123456789123456789123456789``; +*) +(* Overload LOG base 2 *) +val _ = overload_on ("LOG2", ``\n. LOG 2 n``); + +(* Theorem: LOG2 1 = 0 *) +(* Proof: + LOG_1 |> SPEC ``2``; + val it = |- 1 < 2 ==> LOG2 1 = 0: thm +*) +val LOG2_1 = store_thm( + "LOG2_1[simp]", + ``LOG2 1 = 0``, + rw[LOG_1]); + +(* Theorem: LOG2 2 = 1 *) +(* Proof: + LOG_BASE |> SPEC ``2``; + val it = |- 1 < 2 ==> LOG2 2 = 1: thm +*) +val LOG2_2 = store_thm( + "LOG2_2[simp]", + ``LOG2 2 = 1``, + rw[LOG_BASE]); + +(* Obtain a theorem *) +val LOG2_THM = save_thm("LOG2_THM", + LOG_THM |> SPEC ``2`` |> SIMP_RULE (srw_ss())[]); +(* val LOG2_THM = |- !n. 0 < n ==> !p. (LOG2 n = p) <=> 2 ** p <= n /\ n < 2 ** SUC p: thm *) + +(* Obtain a theorem *) +Theorem LOG2_PROPERTY = LOG |> SPEC ``2`` |> SIMP_RULE (srw_ss())[]; +(* val LOG2_PROPERTY = |- !n. 0 < n ==> 2 ** LOG2 n <= n /\ n < 2 ** SUC (LOG2 n): thm *) + +(* Theorem: 0 < n ==> 2 ** LOG2 n <= n) *) +(* Proof: by LOG2_PROPERTY *) +val TWO_EXP_LOG2_LE = store_thm( + "TWO_EXP_LOG2_LE", + ``!n. 0 < n ==> 2 ** LOG2 n <= n``, + rw[LOG2_PROPERTY]); + +(* Obtain a theorem *) +val LOG2_UNIQUE = save_thm("LOG2_UNIQUE", + LOG_UNIQUE |> SPEC ``2`` |> SPEC ``n:num`` |> SPEC ``m:num`` |> GEN_ALL); +(* val LOG2_UNIQUE = |- !n m. 2 ** m <= n /\ n < 2 ** SUC m ==> LOG2 n = m: thm *) + +(* Theorem: 0 < n ==> ((LOG2 n = 0) <=> (n = 1)) *) +(* Proof: + LOG_EQ_0 |> SPEC ``2``; + |- !b. 1 < 2 /\ 0 < b ==> (LOG2 b = 0 <=> b < 2) +*) +val LOG2_EQ_0 = store_thm( + "LOG2_EQ_0", + ``!n. 0 < n ==> ((LOG2 n = 0) <=> (n = 1))``, + rw[LOG_EQ_0]); + +(* Theorem: 0 < n ==> LOG2 n = 1 <=> (n = 2) \/ (n = 3) *) +(* Proof: + If part: LOG2 n = 1 ==> n = 2 \/ n = 3 + Note 2 ** 1 <= n /\ n < 2 ** SUC 1 by LOG2_PROPERTY + or 2 <= n /\ n < 4 by arithmetic + Thus n = 2 or n = 3. + Only-if part: LOG2 2 = 1 /\ LOG2 3 = 1 + Note LOG2 2 = 1 by LOG2_2 + and LOG2 3 = 1 by LOG2_UNIQUE + since 2 ** 1 <= 3 /\ 3 < 2 ** SUC 1 ==> (LOG2 3 = 1) +*) +val LOG2_EQ_1 = store_thm( + "LOG2_EQ_1", + ``!n. 0 < n ==> ((LOG2 n = 1) <=> ((n = 2) \/ (n = 3)))``, + rw_tac std_ss[EQ_IMP_THM] >| [ + imp_res_tac LOG2_PROPERTY >> + rfs[], + rw[], + irule LOG2_UNIQUE >> + simp[] + ]); + +(* Obtain theorem *) +val LOG2_LE_MONO = save_thm("LOG2_LE_MONO", + LOG_LE_MONO |> SPEC ``2`` |> SPEC ``n:num`` |> SPEC ``m:num`` + |> SIMP_RULE (srw_ss())[] |> GEN_ALL); +(* val LOG2_LE_MONO = |- !n m. 0 < n ==> n <= m ==> LOG2 n <= LOG2 m: thm *) + +(* Theorem: 0 < n /\ n <= m ==> LOG2 n <= LOG2 m *) +(* Proof: by LOG_LE_MONO *) +val LOG2_LE = store_thm( + "LOG2_LE", + ``!n m. 0 < n /\ n <= m ==> LOG2 n <= LOG2 m``, + rw[LOG_LE_MONO, DECIDE``1 < 2``]); + +(* Note: next is not LOG2_LT_MONO! *) + +(* Theorem: 0 < n /\ n < m ==> LOG2 n <= LOG2 m *) +(* Proof: + Since n < m ==> n <= m by LESS_IMP_LESS_OR_EQ + This is true by LOG_LE_MONO +*) +val LOG2_LT = store_thm( + "LOG2_LT", + ``!n m. 0 < n /\ n < m ==> LOG2 n <= LOG2 m``, + rw[LOG_LE_MONO, LESS_IMP_LESS_OR_EQ, DECIDE``1 < 2``]); + +(* Theorem: 0 < n ==> LOG2 n < n *) +(* Proof: + LOG2 n + < 2 ** (LOG2 n) by X_LT_EXP_X, 1 < 2 + <= n by LOG2_PROPERTY, 0 < n +*) +val LOG2_LT_SELF = store_thm( + "LOG2_LT_SELF", + ``!n. 0 < n ==> LOG2 n < n``, + rpt strip_tac >> + `LOG2 n < 2 ** (LOG2 n)` by rw[X_LT_EXP_X] >> + `2 ** LOG2 n <= n` by rw[LOG2_PROPERTY] >> + decide_tac); + +(* Theorem: 0 < n ==> LOG2 n <> n *) +(* Proof: + Note n < LOG2 n by LOG2_LT_SELF + Thus n <> LOG2 n by arithmetic +*) +val LOG2_NEQ_SELF = store_thm( + "LOG2_NEQ_SELF", + ``!n. 0 < n ==> LOG2 n <> n``, + rpt strip_tac >> + `LOG2 n < n` by rw[LOG2_LT_SELF] >> + decide_tac); + +(* Theorem: LOG2 n = n ==> n = 0 *) +(* Proof: by LOG2_NEQ_SELF *) +val LOG2_EQ_SELF = store_thm( + "LOG2_EQ_SELF", + ``!n. (LOG2 n = n) ==> (n = 0)``, + metis_tac[LOG2_NEQ_SELF, DECIDE``~(0 < n) <=> (n = 0)``]); + +(* Theorem: 1 < n ==> 0 < LOG2 n *) +(* Proof: + 1 < n + ==> 2 <= n + ==> LOG2 2 <= LOG2 n by LOG2_LE + ==> 1 <= LOG2 n by LOG_BASE, LOG2 2 = 1 + or 0 < LOG2 n +*) +val LOG2_POS = store_thm( + "LOG2_POS[simp]", + ``!n. 1 < n ==> 0 < LOG2 n``, + rpt strip_tac >> + `LOG2 2 = 1` by rw[LOG_BASE, DECIDE``1 < 2``] >> + `2 <= n` by decide_tac >> + `LOG2 2 <= LOG2 n` by rw[LOG2_LE] >> + decide_tac); + +(* Theorem: 1 < n ==> 1 < 2 * LOG2 n *) +(* Proof: + 1 < n + ==> 2 <= n + ==> LOG2 2 <= LOG2 n by LOG2_LE + ==> 1 <= LOG2 n by LOG_BASE, LOG2 2 = 1 + ==> 2 * 1 <= 2 * LOG2 n by LE_MULT_LCANCEL + or 1 < 2 * LOG2 n +*) +val LOG2_TWICE_LT = store_thm( + "LOG2_TWICE_LT", + ``!n. 1 < n ==> 1 < 2 * (LOG2 n)``, + rpt strip_tac >> + `LOG2 2 = 1` by rw[LOG_BASE, DECIDE``1 < 2``] >> + `2 <= n` by decide_tac >> + `LOG2 2 <= LOG2 n` by rw[LOG2_LE] >> + `1 <= LOG2 n` by decide_tac >> + `2 <= 2 * LOG2 n` by rw_tac arith_ss[LE_MULT_LCANCEL, DECIDE``0 < 2``] >> + decide_tac); + +(* Theorem: 1 < n ==> 4 <= (2 * (LOG2 n)) ** 2 *) +(* Proof: + 1 < n + ==> 2 <= n + ==> LOG2 2 <= LOG2 n by LOG2_LE + ==> 1 <= LOG2 n by LOG2_2, or LOG_BASE, LOG2 2 = 1 + ==> 2 * 1 <= 2 * LOG2 n by LE_MULT_LCANCEL + ==> 2 ** 2 <= (2 * LOG2 n) ** 2 by EXP_EXP_LE_MONO + ==> 4 <= (2 * LOG2 n) ** 2 +*) +val LOG2_TWICE_SQ = store_thm( + "LOG2_TWICE_SQ", + ``!n. 1 < n ==> 4 <= (2 * (LOG2 n)) ** 2``, + rpt strip_tac >> + `LOG2 2 = 1` by rw[] >> + `2 <= n` by decide_tac >> + `LOG2 2 <= LOG2 n` by rw[LOG2_LE] >> + `1 <= LOG2 n` by decide_tac >> + `2 <= 2 * LOG2 n` by rw_tac arith_ss[LE_MULT_LCANCEL, DECIDE``0 < 2``] >> + `2 ** 2 <= (2 * LOG2 n) ** 2` by rw[EXP_EXP_LE_MONO, DECIDE``0 < 2``] >> + `2 ** 2 = 4` by rw_tac arith_ss[] >> + decide_tac); + +(* Theorem: 0 < n ==> 4 <= (2 * SUC (LOG2 n)) ** 2 *) +(* Proof: + 0 < n + ==> 1 <= n + ==> LOG2 1 <= LOG2 n by LOG2_LE + ==> 0 <= LOG2 n by LOG2_1, or LOG_BASE, LOG2 1 = 0 + ==> 1 <= SUC (LOG2 n) by LESS_EQ_MONO + ==> 2 * 1 <= 2 * SUC (LOG2 n) by LE_MULT_LCANCEL + ==> 2 ** 2 <= (2 * SUC (LOG2 n)) ** 2 by EXP_EXP_LE_MONO + ==> 4 <= (2 * SUC (LOG2 n)) ** 2 +*) +val LOG2_SUC_TWICE_SQ = store_thm( + "LOG2_SUC_TWICE_SQ", + ``!n. 0 < n ==> 4 <= (2 * SUC (LOG2 n)) ** 2``, + rpt strip_tac >> + `LOG2 1 = 0` by rw[] >> + `1 <= n` by decide_tac >> + `LOG2 1 <= LOG2 n` by rw[LOG2_LE] >> + `1 <= SUC (LOG2 n)` by decide_tac >> + `2 <= 2 * SUC (LOG2 n)` by rw_tac arith_ss[LE_MULT_LCANCEL, DECIDE``0 < 2``] >> + `2 ** 2 <= (2 * SUC (LOG2 n)) ** 2` by rw[EXP_EXP_LE_MONO, DECIDE``0 < 2``] >> + `2 ** 2 = 4` by rw_tac arith_ss[] >> + decide_tac); + +(* Theorem: 1 < n ==> 1 < (SUC (LOG2 n)) ** 2 *) +(* Proof: + Note 0 < LOG2 n by LOG2_POS, 1 < n + so 1 < SUC (LOG2 n) by arithmetic + ==> 1 < (SUC (LOG2 n)) ** 2 by ONE_LT_EXP, 0 < 2 +*) +val LOG2_SUC_SQ = store_thm( + "LOG2_SUC_SQ", + ``!n. 1 < n ==> 1 < (SUC (LOG2 n)) ** 2``, + rpt strip_tac >> + `0 < LOG2 n` by rw[] >> + `1 < SUC (LOG2 n)` by decide_tac >> + rw[ONE_LT_EXP]); + +(* Theorem: LOG2 (2 ** n) = n *) +(* Proof: by LOG_EXACT_EXP *) +val LOG2_2_EXP = store_thm( + "LOG2_2_EXP", + ``!n. LOG2 (2 ** n) = n``, + rw[LOG_EXACT_EXP]); + +(* Theorem: (2 ** (LOG2 n) = n) <=> ?k. n = 2 ** k *) +(* Proof: + If part: 2 ** LOG2 n = n ==> ?k. n = 2 ** k + True by taking k = LOG2 n. + Only-if part: 2 ** LOG2 (2 ** k) = 2 ** k + Note LOG2 n = k by LOG_EXACT_EXP, 1 < 2 + or n = 2 ** k = 2 ** LOG2 n. +*) +val LOG2_EXACT_EXP = store_thm( + "LOG2_EXACT_EXP", + ``!n. (2 ** (LOG2 n) = n) <=> ?k. n = 2 ** k``, + metis_tac[LOG2_2_EXP]); + +(* Theorem: 0 < n ==> LOG2 (n * 2 ** m) = (LOG2 n) + m *) +(* Proof: + LOG_EXP |> SPEC ``m:num`` |> SPEC ``2`` |> SPEC ``n:num``; + val it = |- 1 < 2 /\ 0 < n ==> LOG2 (2 ** m * n) = m + LOG2 n: thm +*) +val LOG2_MULT_EXP = store_thm( + "LOG2_MULT_EXP", + ``!n m. 0 < n ==> (LOG2 (n * 2 ** m) = (LOG2 n) + m)``, + rw[GSYM LOG_EXP]); + +(* Theorem: 0 < n ==> (LOG2 (2 * n) = 1 + LOG2 n) *) +(* Proof: + LOG_MULT |> SPEC ``2`` |> SPEC ``n:num``; + val it = |- 1 < 2 /\ 0 < n ==> LOG2 (TWICE n) = SUC (LOG2 n): thm *) +val LOG2_TWICE = store_thm( + "LOG2_TWICE", + ``!n. 0 < n ==> (LOG2 (2 * n) = 1 + LOG2 n)``, + rw[LOG_MULT]); (* ----------------------------------------------------------------------- *) diff --git a/src/num/theories/arithmeticScript.sml b/src/num/theories/arithmeticScript.sml index b7d42a88f3..3cbb9dab69 100644 --- a/src/num/theories/arithmeticScript.sml +++ b/src/num/theories/arithmeticScript.sml @@ -13,9 +13,9 @@ open HolKernel boolLib Parse BasicProvers; -open simpLib boolSimps mesonLib metisLib; +open simpLib boolSimps mesonLib metisLib numTheory prim_recTheory; -local open numTheory prim_recTheory SatisfySimps DefnBase in end +local open SatisfySimps DefnBase in end local open OpenTheoryMap @@ -33,39 +33,17 @@ val _ = new_theory "arithmetic"; val _ = if !Globals.interactive then () else Feedback.emit_WARNING := false; -val NOT_SUC = numTheory.NOT_SUC -and INV_SUC = numTheory.INV_SUC -and INDUCTION = numTheory.INDUCTION; - -val num_Axiom = prim_recTheory.num_Axiom -Theorem num_case_def = prim_recTheory.num_case_def - -val INV_SUC_EQ = prim_recTheory.INV_SUC_EQ -and LESS_REFL = prim_recTheory.LESS_REFL -and SUC_LESS = prim_recTheory.SUC_LESS -and NOT_LESS_0 = prim_recTheory.NOT_LESS_0 -and LESS_MONO = prim_recTheory.LESS_MONO -and LESS_SUC_REFL = prim_recTheory.LESS_SUC_REFL -and LESS_SUC = prim_recTheory.LESS_SUC -and LESS_THM = prim_recTheory.LESS_THM -and LESS_SUC_IMP = prim_recTheory.LESS_SUC_IMP -and LESS_0 = prim_recTheory.LESS_0 -and EQ_LESS = prim_recTheory.EQ_LESS -and SUC_ID = prim_recTheory.SUC_ID -and NOT_LESS_EQ = prim_recTheory.NOT_LESS_EQ -and LESS_NOT_EQ = prim_recTheory.LESS_NOT_EQ -and LESS_SUC_SUC = prim_recTheory.LESS_SUC_SUC -and PRE = prim_recTheory.PRE -and RTC_IM_TC = prim_recTheory.RTC_IM_TC -and TC_IM_RTC_SUC = prim_recTheory.TC_IM_RTC_SUC -and LESS_ALT = prim_recTheory.LESS_ALT; - +Theorem num_case_def = num_case_def +val metis_tac = METIS_TAC; fun bossify stac ths = stac (srw_ss()) ths val simp = bossify asm_simp_tac val fs = bossify full_simp_tac val gvs = bossify (global_simp_tac {droptrues = true, elimvars = true, oldestfirst = true, strip = true}) +val rw = srw_tac[]; +val std_ss = bool_ss; +val qabbrev_tac = Q.ABBREV_TAC; (*---------------------------------------------------------------------------* * The basic arithmetic operations. * @@ -282,6 +260,14 @@ val num_case_compute = store_thm ("num_case_compute", val SUC_NOT = save_thm ("SUC_NOT", GEN (“n:num”) (NOT_EQ_SYM (SPEC (“n:num”) NOT_SUC))); +(* Theorem: 0 < SUC n *) +(* Proof: by arithmetic. *) +val SUC_POS = save_thm("SUC_POS", LESS_0); + +(* Theorem: 0 < SUC n *) +(* Proof: by arithmetic. *) +val SUC_NOT_ZERO = save_thm("SUC_NOT_ZERO", NOT_SUC); + val ADD_0 = store_thm ("ADD_0", “!m. m + 0 = m”, INDUCT_TAC THEN ASM_REWRITE_TAC[ADD]); @@ -321,6 +307,8 @@ Proof REWRITE_TAC [NOT_LESS_0, LESS_0, NOT_SUC] QED +Theorem NOT_ZERO = NOT_ZERO_LT_ZERO + Theorem NOT_LT_ZERO_EQ_ZERO[simp]: !n. ~(0 < n) <=> (n = 0) Proof REWRITE_TAC [GSYM NOT_ZERO_LT_ZERO] @@ -1331,6 +1319,8 @@ val LESS_LESS_CASES = store_thm ("LESS_LESS_CASES", REPEAT_TCL DISJ_CASES_THEN (fn t => REWRITE_TAC[t]) th end); +Theorem num_nchotomy = LESS_LESS_CASES (* from examples/algebra *) + Theorem GREATER_EQ: !n m. n >= m <=> m <= n Proof @@ -1390,6 +1380,8 @@ val LESS_MULT2 = store_thm ("LESS_MULT2", REPEAT GEN_TAC THEN CONV_TAC CONTRAPOS_CONV THEN REWRITE_TAC[NOT_LESS, LESS_EQ_0, DE_MORGAN_THM, MULT_EQ_0]); +val MULT_POS = save_thm("MULT_POS", LESS_MULT2); + Theorem ZERO_LESS_MULT[simp]: !m n. 0 < m * n <=> 0 < m /\ 0 < n Proof @@ -1423,6 +1415,43 @@ val FACT_LESS = store_thm ("FACT_LESS", INDUCT_TAC THEN REWRITE_TAC[FACT, ONE, LESS_SUC_REFL] THEN MATCH_MP_TAC LESS_MULT2 THEN ASM_REWRITE_TAC[LESS_0]); +(* Theorem: 1 <= FACT n *) +(* Proof: + Note 0 < FACT n by FACT_LESS + so 1 <= FACT n by arithmetic +*) +val FACT_GE_1 = store_thm( + "FACT_GE_1", + ``!n. 1 <= FACT n``, + metis_tac[FACT_LESS, LESS_OR, ONE]); + +(* Idea: test if a function f is factorial. *) + +(* Theorem: f = FACT <=> f 0 = 1 /\ !n. f (SUC n) = SUC n * f n *) +(* Proof: + If part is true by FACT + Only-if part, apply FUN_EQ_THM, this is to show: + !n. f n = FACT n. + By induction on n. + Base: f 0 = FACT 0 + f 0 + = 1 by given + = FACT 0 by FACT_0 + Step: f n = FACT n ==> f (SUC n) = FACT (SUC n) + f (SUC n) + = SUC n * f n by given + = SUC n * FACT n by induction hypothesis + = FACT (SUC n) by FACT +*) +Theorem FACT_iff: + !f. f = FACT <=> f 0 = 1 /\ !n. f (SUC n) = SUC n * f n +Proof + rw[FACT, EQ_IMP_THM] >> + rw[FUN_EQ_THM] >> + Induct_on `x` >> + simp[FACT] +QED + (*---------------------------------------------------------------------------*) (* Theorems about evenness and oddity [JRH 92.07.14] *) (*---------------------------------------------------------------------------*) @@ -3082,12 +3111,33 @@ Theorem ZERO_LT_EXP[simp]: Proof METIS_TAC [NOT_ZERO_LT_ZERO, EXP_EQ_0] QED +(* Theorem: m <> 0 ==> m ** n <> 0 *) +(* Proof: by EXP_EQ_0 *) +val EXP_NONZERO = store_thm( + "EXP_NONZERO", + ``!m n. m <> 0 ==> m ** n <> 0``, + metis_tac[EXP_EQ_0]); + +(* Theorem: 0 < m ==> 0 < m ** n *) +(* Proof: by EXP_NONZERO *) +val EXP_POS = store_thm( + "EXP_POS", + ``!m n. 0 < m ==> 0 < m ** n``, + rw[EXP_NONZERO]); + Theorem ONE_LE_EXP[simp]: 1 <= x EXP y <=> 0 < x \/ y = 0 Proof REWRITE_TAC[LESS_EQ_IFF_LESS_SUC, ONE, LESS_MONO_EQ, ZERO_LT_EXP] QED +(* Theorem: n ** 0 = 1 *) +(* Proof: by EXP *) +val EXP_0 = store_thm( + "EXP_0", + ``!n. n ** 0 = 1``, + rw_tac std_ss[EXP]); + Theorem EXP_1[simp]: !n. (1 EXP n = 1) /\ (n EXP 1 = n) Proof @@ -3096,6 +3146,16 @@ Proof INDUCT_TAC THEN ASM_REWRITE_TAC [MULT_EQ_1, EXP] QED +(* Theorem: n ** 2 = n * n *) +(* Proof: + n ** 2 = n * (n ** 1) = n * (n * (n ** 0)) = n * (n * 1) = n * n + or n ** 2 = n * (n ** 1) = n * n by EXP_1: !n. (1 ** n = 1) /\ (n ** 1 = n) +*) +val EXP_2 = store_thm( + "EXP_2", + ``!n. n ** 2 = n * n``, + metis_tac[EXP, TWO, EXP_1]); + Theorem EXP_EQ_1[simp]: !n m. (n EXP m = 1) <=> (n = 1) \/ (m = 0) Proof @@ -3377,8 +3437,11 @@ QED val _ = print "Minimums and maximums\n" -val MAX = new_definition("MAX_DEF", “MAX m n = if m < n then n else m”); -val MIN = new_definition("MIN_DEF", “MIN m n = if m < n then m else n”); +val MAX_DEF = new_definition("MAX_DEF", “MAX m n = if m < n then n else m”); +val MIN_DEF = new_definition("MIN_DEF", “MIN m n = if m < n then m else n”); + +val MAX = MAX_DEF; +val MIN = MIN_DEF; val ARW = RW_TAC bool_ss @@ -3481,6 +3544,20 @@ val MAX_IDEM = store_thm ("MAX_IDEM", “!n. MAX n n = n”, PROVE_TAC [MAX]); +(* Theorem: (MAX n m = n) \/ (MAX n m = m) *) +(* Proof: by MAX_DEF *) +val MAX_CASES = store_thm( + "MAX_CASES", + ``!m n. (MAX n m = n) \/ (MAX n m = m)``, + rw[MAX_DEF]); + +(* Theorem: (MIN n m = n) \/ (MIN n m = m) *) +(* Proof: by MIN_DEF *) +val MIN_CASES = store_thm( + "MIN_CASES", + ``!m n. (MIN n m = n) \/ (MIN n m = m)``, + rw[MIN_DEF]); + val EXISTS_GREATEST = store_thm ("EXISTS_GREATEST", “!P. (?x. P x) /\ (?x:num. !y. y > x ==> ~P y) <=> ?x. P x /\ !y. y > x ==> ~P y”, @@ -3792,6 +3869,36 @@ val FUNPOW_1 = store_thm ("FUNPOW_1", REWRITE_TAC [FUNPOW, ONE]); val _ = export_rewrites ["FUNPOW_1"] +(* Theorem: FUNPOW f 2 x = f (f x) *) +(* Proof: by definition. *) +val FUNPOW_2 = store_thm( + "FUNPOW_2", + ``!f x. FUNPOW f 2 x = f (f x)``, + simp_tac bool_ss [FUNPOW, TWO, ONE]); + +(* Theorem: FUNPOW (K c) n x = if n = 0 then x else c *) +(* Proof: + By induction on n. + Base: !x c. FUNPOW (K c) 0 x = if 0 = 0 then x else c + FUNPOW (K c) 0 x + = x by FUNPOW + = if 0 = 0 then x else c by 0 = 0 is true + Step: !x c. FUNPOW (K c) n x = if n = 0 then x else c ==> + !x c. FUNPOW (K c) (SUC n) x = if SUC n = 0 then x else c + FUNPOW (K c) (SUC n) x + = FUNPOW (K c) n ((K c) x) by FUNPOW + = if n = 0 then ((K c) c) else c by induction hypothesis + = if n = 0 then c else c by K_THM + = c by either case + = if SUC n = 0 then x else c by SUC n = 0 is false +*) +val FUNPOW_K = store_thm( + "FUNPOW_K", + ``!n x c. FUNPOW (K c) n x = if n = 0 then x else c``, + Induct >- + rw[] >> + metis_tac[FUNPOW, combinTheory.K_THM, SUC_NOT_ZERO]); + Theorem FUNPOW_CONG: !n x f g. (!m. m < n ==> f (FUNPOW f m x) = g (FUNPOW f m x)) @@ -3810,6 +3917,14 @@ Proof Induct \\ SRW_TAC[][FUNPOW_SUC] QED +(* Theorem: FUNPOW f m (FUNPOW f n x) = FUNPOW f n (FUNPOW f m x) *) +(* Proof: by FUNPOW_ADD, ADD_COMM *) +Theorem FUNPOW_COMM: + !f m n x. FUNPOW f m (FUNPOW f n x) = FUNPOW f n (FUNPOW f m x) +Proof + metis_tac[FUNPOW_ADD, ADD_COMM] +QED + val NRC_0 = save_thm ("NRC_0", CONJUNCT1 NRC); val _ = export_rewrites ["NRC_0"] @@ -3986,13 +4101,6 @@ val SUB_MOD = Q.store_thm ("SUB_MOD", `!m n. 0 ((m-n) MOD n = m MOD n)`, METIS_TAC [ADD_MODULUS,ADD_SUB,LESS_EQ_EXISTS,ADD_SYM]); -fun Cases (asl,g) = - let val (v,_) = dest_forall g - in GEN_TAC THEN STRUCT_CASES_TAC (SPEC v num_CASES) - end (asl,g); - -fun Cases_on v (asl,g) = STRUCT_CASES_TAC (SPEC v num_CASES) (asl,g); - val ONE_LT_MULT_IMP = Q.store_thm ("ONE_LT_MULT_IMP", `!p q. 1 < p /\ 0 < q ==> 1 < p * q`, REPEAT Cases THEN @@ -4007,7 +4115,7 @@ val ONE_LT_MULT = Q.store_thm ("ONE_LT_MULT", REWRITE_TAC [ONE] THEN INDUCT_TAC THEN RW_TAC bool_ss [ADD_CLAUSES, MULT_CLAUSES,LESS_REFL,LESS_0] THENL [METIS_TAC [NOT_SUC_LESS_EQ_0,LESS_OR_EQ], - Cases_on “y:num” THEN + Cases_on ‘y’ THEN RW_TAC bool_ss [MULT_CLAUSES,ADD_CLAUSES,LESS_REFL, LESS_MONO_EQ,ZERO_LESS_ADD,LESS_0] THEN METIS_TAC [ZERO_LESS_MULT]]); @@ -4586,4 +4694,230 @@ Proof ASM_REWRITE_TAC[] ] ] QED +(* ------------------------------------------------------------------------- *) +(* Arithmetic Manipulations (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: n * p = m * p <=> p = 0 \/ n = m *) +(* Proof: + n * p = m * p + <=> n * p - m * p = 0 by SUB_EQUAL_0 + <=> (n - m) * p = 0 by RIGHT_SUB_DISTRIB + <=> n - m = 0 or p = 0 by MULT_EQ_0 + <=> n = m or p = 0 by SUB_EQUAL_0 +*) +val MULT_RIGHT_CANCEL = store_thm( + "MULT_RIGHT_CANCEL", + ``!m n p. (n * p = m * p) <=> (p = 0) \/ (n = m)``, + rw[]); + +(* Theorem: p * n = p * m <=> p = 0 \/ n = m *) +(* Proof: by MULT_RIGHT_CANCEL and MULT_COMM. *) +val MULT_LEFT_CANCEL = store_thm( + "MULT_LEFT_CANCEL", + ``!m n p. (p * n = p * m) <=> (p = 0) \/ (n = m)``, + rw[MULT_RIGHT_CANCEL, MULT_COMM]); + +(* Theorem: m * (n * p) = n * (m * p) *) +(* Proof: + m * (n * p) + = (m * n) * p by MULT_ASSOC + = (n * m) * p by MULT_COMM + = n * (m * p) by MULT_ASSOC +*) +val MULT_COMM_ASSOC = store_thm( + "MULT_COMM_ASSOC", + ``!m n p. m * (n * p) = n * (m * p)``, + metis_tac[MULT_COMM, MULT_ASSOC]); + +(* Theorem: 0 < n ==> ((n * m) DIV n = m) *) +(* Proof: + Since n * m = m * n by MULT_COMM + = m * n + 0 by ADD_0 + and 0 < n by given + Hence (n * m) DIV n = m by DIV_UNIQUE: + |- !n k q. (?r. (k = q * n + r) /\ r < n) ==> (k DIV n = q) +*) +val MULT_TO_DIV = store_thm( + "MULT_TO_DIV", + ``!m n. 0 < n ==> ((n * m) DIV n = m)``, + metis_tac[MULT_COMM, ADD_0, DIV_UNIQUE]); +(* This is commutative version of: +arithmeticTheory.MULT_DIV |- !n q. 0 < n ==> (q * n DIV n = q) +*) + +(* Theorem: m * (n * p) = m * p * n *) +(* Proof: by MULT_ASSOC, MULT_COMM *) +val MULT_ASSOC_COMM = store_thm( + "MULT_ASSOC_COMM", + ``!m n p. m * (n * p) = m * p * n``, + metis_tac[MULT_ASSOC, MULT_COMM]); + +(* Theorem: 0 < n ==> !m. (m * n = n) <=> (m = 1) *) +(* Proof: by MULT_EQ_ID *) +val MULT_LEFT_ID = store_thm( + "MULT_LEFT_ID", + ``!n. 0 < n ==> !m. (m * n = n) <=> (m = 1)``, + metis_tac[MULT_EQ_ID, NOT_ZERO_LT_ZERO]); + +(* Theorem: 0 < n ==> !m. (n * m = n) <=> (m = 1) *) +(* Proof: by MULT_EQ_ID *) +val MULT_RIGHT_ID = store_thm( + "MULT_RIGHT_ID", + ``!n. 0 < n ==> !m. (n * m = n) <=> (m = 1)``, + metis_tac[MULT_EQ_ID, MULT_COMM, NOT_ZERO_LT_ZERO]); + +(* Theorem alias *) +Theorem MULT_EQ_SELF = MULT_RIGHT_ID; +(* val MULT_EQ_SELF = |- !n. 0 < n ==> !m. (n * m = n) <=> (m = 1): thm *) + +(* ------------------------------------------------------------------------- *) +(* Modulo Theorems (from examples/algebra) *) +(* ------------------------------------------------------------------------- *) + +(* Theorem: 0 < n ==> !a b. (a MOD n = b) <=> ?c. (a = c * n + b) /\ (b < n) *) +(* Proof: + If part: (a MOD n = b) ==> ?c. (a = c * n + b) /\ (b < n) + Or to show: ?c. (a = c * n + a MOD n) /\ a MOD n < n + Taking c = a DIV n, this is true by DIVISION + Only-if part: (a = c * n + b) /\ (b < n) ==> (a MOD n = b) + Or to show: b < n ==> (c * n + b) MOD n = b + (c * n + b) MOD n + = ((c * n) MOD n + b MOD n) MOD n by MOD_PLUS + = (0 + b MOD n) MOD n by MOD_EQ_0 + = b MOD n by MOD_MOD + = b by LESS_MOD, b < n +*) +val MOD_EQN = store_thm( + "MOD_EQN", + ``!n. 0 < n ==> !a b. (a MOD n = b) <=> ?c. (a = c * n + b) /\ (b < n)``, + rw_tac std_ss[EQ_IMP_THM] >- + metis_tac[DIVISION] >> + metis_tac[MOD_PLUS, MOD_EQ_0, ADD, MOD_MOD, LESS_MOD]); + +(* Theorem: If n > 0, k MOD n = 0 ==> !x. (k*x) MOD n = 0 *) +(* Proof: + (k*x) MOD n = (k MOD n * x MOD n) MOD n by MOD_TIMES2 + = (0 * x MOD n) MOD n by given + = 0 MOD n by MULT_0 and MULT_COMM + = 0 by ZERO_MOD +*) +Theorem MOD_MULTIPLE_ZERO : + !n k. 0 < n /\ (k MOD n = 0) ==> !x. ((k*x) MOD n = 0) +Proof + metis_tac[MOD_TIMES2, MULT_0, MULT_COMM, ZERO_MOD] +QED + +(* Theorem: (x + y + z) MOD n = (x MOD n + y MOD n + z MOD n) MOD n *) +(* Proof: + (x + y + z) MOD n + = ((x + y) MOD n + z MOD n) MOD n by MOD_PLUS + = ((x MOD n + y MOD n) MOD n + z MOD n) MOD n by MOD_PLUS + = (x MOD n + y MOD n + z MOD n) MOD n by MOD_MOD +*) +val MOD_PLUS3 = store_thm( + "MOD_PLUS3", + ``!n. 0 < n ==> !x y z. (x + y + z) MOD n = (x MOD n + y MOD n + z MOD n) MOD n``, + metis_tac[MOD_PLUS, MOD_MOD]); + +(* Theorem: Addition is associative in MOD: if x, y, z all < n, + ((x + y) MOD n + z) MOD n = (x + (y + z) MOD n) MOD n. *) +(* Proof: + ((x * y) MOD n * z) MOD n + = (((x * y) MOD n) MOD n * z MOD n) MOD n by MOD_TIMES2 + = ((x * y) MOD n * z MOD n) MOD n by MOD_MOD + = (x * y * z) MOD n by MOD_TIMES2 + = (x * (y * z)) MOD n by MULT_ASSOC + = (x MOD n * (y * z) MOD n) MOD n by MOD_TIMES2 + = (x MOD n * ((y * z) MOD n) MOD n) MOD n by MOD_MOD + = (x * (y * z) MOD n) MOD n by MOD_TIMES2 + or + ((x + y) MOD n + z) MOD n + = ((x + y) MOD n + z MOD n) MOD n by LESS_MOD, z < n + = (x + y + z) MOD n by MOD_PLUS + = (x + (y + z)) MOD n by ADD_ASSOC + = (x MOD n + (y + z) MOD n) MOD n by MOD_PLUS + = (x + (y + z) MOD n) MOD n by LESS_MOD, x < n +*) +val MOD_ADD_ASSOC = store_thm( + "MOD_ADD_ASSOC", + ``!n x y z. 0 < n /\ x < n /\ y < n /\ z < n ==> + ((x + y) MOD n + z) MOD n = (x + (y + z) MOD n) MOD n``, + metis_tac[LESS_MOD, MOD_PLUS, ADD_ASSOC]); + +(* Theorem: mutliplication is associative in MOD: + (x*y MOD n * z) MOD n = (x * y*Z MOD n) MOD n *) +(* Proof: + ((x * y) MOD n * z) MOD n + = (((x * y) MOD n) MOD n * z MOD n) MOD n by MOD_TIMES2 + = ((x * y) MOD n * z MOD n) MOD n by MOD_MOD + = (x * y * z) MOD n by MOD_TIMES2 + = (x * (y * z)) MOD n by MULT_ASSOC + = (x MOD n * (y * z) MOD n) MOD n by MOD_TIMES2 + = (x MOD n * ((y * z) MOD n) MOD n) MOD n by MOD_MOD + = (x * (y * z) MOD n) MOD n by MOD_TIMES2 + or + ((x * y) MOD n * z) MOD n + = ((x * y) MOD n * z MOD n) MOD n by LESS_MOD, z < n + = (((x * y) * z) MOD n) MOD n by MOD_TIMES2 + = ((x * (y * z)) MOD n) MOD n by MULT_ASSOC + = (x MOD n * (y * z) MOD n) MOD n by MOD_TIMES2 + = (x * (y * z) MOD n) MOD n by LESS_MOD, x < n +*) +val MOD_MULT_ASSOC = store_thm( + "MOD_MULT_ASSOC", + ``!n x y z. 0 < n /\ x < n /\ y < n /\ z < n ==> + ((x * y) MOD n * z) MOD n = (x * (y * z) MOD n) MOD n``, + metis_tac[LESS_MOD, MOD_TIMES2, MULT_ASSOC]); + +(* Theorem: If n > 0, ((n - x) MOD n + x) MOD n = 0 for x < n. *) +(* Proof: + ((n - x) MOD n + x) MOD n + = ((n - x) MOD n + x MOD n) MOD n by LESS_MOD + = (n - x + x) MOD n by MOD_PLUS + = n MOD n by SUB_ADD and 0 <= n + = (1*n) MOD n by MULT_LEFT_1 + = 0 by MOD_EQ_0 +*) +val MOD_ADD_INV = store_thm( + "MOD_ADD_INV", + ``!n x. 0 < n /\ x < n ==> (((n - x) MOD n + x) MOD n = 0)``, + metis_tac[LESS_MOD, MOD_PLUS, SUB_ADD, LESS_IMP_LESS_OR_EQ, MOD_EQ_0, MULT_LEFT_1]); + +(* Theorem: n < m ==> ((n MOD m = 0) <=> (n = 0)) *) +(* Proof: + Note n < m ==> (n MOD m = n) by LESS_MOD + Thus (n MOD m = 0) <=> (n = 0) by above +*) +val MOD_EQ_0_IFF = store_thm( + "MOD_EQ_0_IFF", + ``!m n. n < m ==> ((n MOD m = 0) <=> (n = 0))``, + rw_tac bool_ss[LESS_MOD]); + +(* Theorem: ((a MOD n) ** m) MOD n = (a ** m) MOD n *) +(* Proof: by induction on m. + Base case: (a MOD n) ** 0 MOD n = a ** 0 MOD n + (a MOD n) ** 0 MOD n + = 1 MOD n by EXP + = a ** 0 MOD n by EXP + Step case: (a MOD n) ** m MOD n = a ** m MOD n ==> (a MOD n) ** SUC m MOD n = a ** SUC m MOD n + (a MOD n) ** SUC m MOD n + = ((a MOD n) * (a MOD n) ** m) MOD n by EXP + = ((a MOD n) * (((a MOD n) ** m) MOD n)) MOD n by MOD_TIMES2, MOD_MOD + = ((a MOD n) * (a ** m MOD n)) MOD n by induction hypothesis + = (a * a ** m) MOD n by MOD_TIMES2 + = a ** SUC m MOD n by EXP +*) +val MOD_EXP = store_thm( + "MOD_EXP", + ``!n. 0 < n ==> !a m. ((a MOD n) ** m) MOD n = (a ** m) MOD n``, + rpt strip_tac >> + Induct_on `m` >- + rw[EXP] >> + `(a MOD n) ** SUC m MOD n = ((a MOD n) * (a MOD n) ** m) MOD n` by rw[EXP] >> + `_ = ((a MOD n) * (((a MOD n) ** m) MOD n)) MOD n` by metis_tac[MOD_TIMES2, MOD_MOD] >> + `_ = ((a MOD n) * (a ** m MOD n)) MOD n` by rw[] >> + `_ = (a * a ** m) MOD n` by rw[MOD_TIMES2] >> + rw[EXP]); + val _ = export_theory() diff --git a/src/parallel_builds/core/Holmakefile b/src/parallel_builds/core/Holmakefile index afb42529e3..42e423db7e 100644 --- a/src/parallel_builds/core/Holmakefile +++ b/src/parallel_builds/core/Holmakefile @@ -2,7 +2,7 @@ CLINE_OPTIONS = -r # directories to build under src/ SRCRELNAMES = \ - bag Boolify/src \ + algebra bag Boolify/src \ coalgebras \ datatype/inftree \ emit \ @@ -104,7 +104,6 @@ EX3DIRS = arm/ARM_security_properties \ diningcryptos \ l3-machine-code/cheri/step - ifdef POLY EX3DIRS += machine-code theorem-prover endif diff --git a/src/pred_set/src/gcdsetScript.sml b/src/pred_set/src/gcdsetScript.sml index f7bfcc546c..d056161f4a 100644 --- a/src/pred_set/src/gcdsetScript.sml +++ b/src/pred_set/src/gcdsetScript.sml @@ -1,11 +1,25 @@ -open HolKernel Parse boolLib BasicProvers +open HolKernel Parse boolLib BasicProvers; -open dividesTheory pred_setTheory -open gcdTheory simpLib metisLib +open arithmeticTheory dividesTheory pred_setTheory gcdTheory simpLib metisLib; -val ARITH_ss = numSimps.ARITH_ss +local open TotalDefn numSimps; in end; -val _ = new_theory "gcdset" +val ARITH_ss = numSimps.ARITH_ss; +val zDefine = + Lib.with_flag (computeLib.auto_import_definitions, false) TotalDefn.Define; + +fun DECIDE_TAC (g as (asl,_)) = + ((MAP_EVERY UNDISCH_TAC (filter numSimps.is_arith asl) THEN + CONV_TAC Arith.ARITH_CONV) + ORELSE tautLib.TAUT_TAC) g; + +val decide_tac = DECIDE_TAC; +val metis_tac = METIS_TAC; +val qabbrev_tac = Q.ABBREV_TAC; +val qexists_tac = Q.EXISTS_TAC; +val rw = SRW_TAC [ARITH_ss]; + +val _ = new_theory "gcdset"; val gcdset_def = new_definition( "gcdset_def", @@ -84,5 +98,385 @@ val gcdset_INSERT = store_thm( ]); val _ = export_rewrites ["gcdset_INSERT"] +(* ------------------------------------------------------------------------- *) +(* Naturals -- counting from 1 rather than 0, and inclusive. *) +(* ------------------------------------------------------------------------- *) + +(* Overload the set of natural numbers (like count) *) +val _ = overload_on("natural", ``\n. IMAGE SUC (count n)``); + +(* Theorem: j IN (natural n) <=> 0 < j /\ j <= n *) +(* Proof: + Note j <> 0 by natural_not_0 + j IN (natural n) + ==> j IN IMAGE SUC (count n) by notation + ==> ?x. x < n /\ (j = SUC x) by IN_IMAGE + Since SUC x <> 0 by numTheory.NOT_SUC + Hence j <> 0, + and x < n ==> SUC x < SUC n by LESS_MONO_EQ + or j < SUC n by above, j = SUC x + thus j <= n by prim_recTheory.LESS_THM +*) +val natural_element = store_thm( + "natural_element", + ``!n j. j IN (natural n) <=> 0 < j /\ j <= n``, + rw[EQ_IMP_THM] >> + `j <> 0` by decide_tac >> + `?m. j = SUC m` by metis_tac[num_CASES] >> + `m < n` by decide_tac >> + metis_tac[]); + +(* Theorem: natural n = {j| 0 < j /\ j <= n} *) +(* Proof: by natural_element, IN_IMAGE *) +val natural_property = store_thm( + "natural_property", + ``!n. natural n = {j| 0 < j /\ j <= n}``, + rw[EXTENSION, natural_element]); + +(* Theorem: FINITE (natural n) *) +(* Proof: FINITE_COUNT, IMAGE_FINITE *) +val natural_finite = store_thm( + "natural_finite", + ``!n. FINITE (natural n)``, + rw[]); + +(* Theorem: CARD (natural n) = n *) +(* Proof: + CARD (natural n) + = CARD (IMAGE SUC (count n)) by notation + = CARD (count n) by CARD_IMAGE_SUC + = n by CARD_COUNT +*) +val natural_card = store_thm( + "natural_card", + ``!n. CARD (natural n) = n``, + rw[CARD_IMAGE_SUC]); + +(* Theorem: 0 NOTIN (natural n) *) +(* Proof: by NOT_SUC *) +val natural_not_0 = store_thm( + "natural_not_0", + ``!n. 0 NOTIN (natural n)``, + rw[]); + +(* Theorem: natural 0 = {} *) +(* Proof: + natural 0 + = IMAGE SUC (count 0) by notation + = IMAGE SUC {} by COUNT_ZERO + = {} by IMAGE_EMPTY +*) +val natural_0 = store_thm( + "natural_0", + ``natural 0 = {}``, + rw[]); + +(* Theorem: natural 1 = {1} *) +(* Proof: + natural 1 + = IMAGE SUC (count 1) by notation + = IMAGE SUC {0} by count_add1 + = {SUC 0} by IMAGE_DEF + = {1} by ONE +*) +val natural_1 = store_thm( + "natural_1", + ``natural 1 = {1}``, + rw[EXTENSION, EQ_IMP_THM]); + +(* Theorem: 0 < n ==> 1 IN (natural n) *) +(* Proof: by natural_element, LESS_OR, ONE *) +val natural_has_1 = store_thm( + "natural_has_1", + ``!n. 0 < n ==> 1 IN (natural n)``, + rw[natural_element]); + +(* Theorem: 0 < n ==> n IN (natural n) *) +(* Proof: by natural_element *) +val natural_has_last = store_thm( + "natural_has_last", + ``!n. 0 < n ==> n IN (natural n)``, + rw[natural_element]); + +(* Theorem: natural (SUC n) = (SUC n) INSERT (natural n) *) +(* Proof: + natural (SUC n) + <=> {j | 0 < j /\ j <= (SUC n)} by natural_property + <=> {j | 0 < j /\ (j <= n \/ (j = SUC n))} by LE + <=> {j | j IN (natural n) \/ (j = SUC n)} by natural_property + <=> (SUC n) INSERT (natural n) by INSERT_DEF +*) +val natural_suc = store_thm( + "natural_suc", + ``!n. natural (SUC n) = (SUC n) INSERT (natural n)``, + rw[EXTENSION, EQ_IMP_THM]); + +(* temporarily make divides an infix *) +val _ = temp_set_fixity "divides" (Infixl 480); + +(* Theorem: 0 < n /\ a IN (natural n) /\ b divides a ==> b IN (natural n) *) +(* Proof: + By natural_element, this is to show: + (1) 0 < a /\ b divides a ==> 0 < b + True by divisor_pos + (2) 0 < a /\ b divides a ==> b <= n + Since b divides a + ==> b <= a by DIVIDES_LE, 0 < a + ==> b <= n by LESS_EQ_TRANS +*) +val natural_divisor_natural = store_thm( + "natural_divisor_natural", + ``!n a b. 0 < n /\ a IN (natural n) /\ b divides a ==> b IN (natural n)``, + rw[natural_element] >- + metis_tac[divisor_pos] >> + metis_tac[DIVIDES_LE, LESS_EQ_TRANS]); + +(* Theorem: 0 < n /\ 0 < a /\ b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural n) *) +(* Proof: + Let c = b DIV a. + By natural_element, this is to show: + 0 < a /\ 0 < b /\ b <= n /\ a divides b ==> 0 < c /\ c <= n + Since a divides b ==> b = c * a by DIVIDES_EQN, 0 < a + so b = a * c by MULT_COMM + or c divides b by divides_def + Thus 0 < c /\ c <= b by divides_pos + or c <= n by LESS_EQ_TRANS +*) +val natural_cofactor_natural = store_thm( + "natural_cofactor_natural", + ``!n a b. 0 < n /\ 0 < a /\ b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural n)``, + rewrite_tac[natural_element] >> + ntac 4 strip_tac >> + qabbrev_tac `c = b DIV a` >> + `b = c * a` by rw[GSYM DIVIDES_EQN, Abbr`c`] >> + `c divides b` by metis_tac[divides_def, MULT_COMM] >> + `0 < c /\ c <= b` by metis_tac[divides_pos] >> + decide_tac); + +(* Theorem: 0 < n /\ a divides n /\ b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural (n DIV a)) *) +(* Proof: + Let c = b DIV a. + By natural_element, this is to show: + 0 < n /\ a divides b /\ 0 < b /\ b <= n /\ a divides b ==> 0 < c /\ c <= n DIV a + Note 0 < a by ZERO_DIVIES, 0 < n + Since a divides b ==> b = c * a by DIVIDES_EQN, 0 < a [1] + or c divides b by divides_def, MULT_COMM + Thus 0 < c, since 0 divides b means b = 0. by ZERO_DIVIDES, b <> 0 + Now n = (n DIV a) * a by DIVIDES_EQN, 0 < a [2] + With b <= n, c * a <= (n DIV a) * a by [1], [2] + Hence c <= n DIV a by LE_MULT_RCANCEL, a <> 0 +*) +val natural_cofactor_natural_reduced = store_thm( + "natural_cofactor_natural_reduced", + ``!n a b. 0 < n /\ a divides n /\ + b IN (natural n) /\ a divides b ==> (b DIV a) IN (natural (n DIV a))``, + rewrite_tac[natural_element] >> + ntac 4 strip_tac >> + qabbrev_tac `c = b DIV a` >> + `a <> 0` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> + `(b = c * a) /\ (n = (n DIV a) * a)` by rw[GSYM DIVIDES_EQN, Abbr`c`] >> + `c divides b` by metis_tac[divides_def, MULT_COMM] >> + `0 < c` by metis_tac[ZERO_DIVIDES, NOT_ZERO] >> + metis_tac[LE_MULT_RCANCEL]); + +(* ------------------------------------------------------------------------- *) +(* Coprimes *) +(* ------------------------------------------------------------------------- *) + +(* Define the coprimes set: integers from 1 to n that are coprime to n *) +(* +val coprimes_def = zDefine ` + coprimes n = {j | 0 < j /\ j <= n /\ coprime j n} +`; +*) +(* Note: j <= n ensures that coprimes n is finite. *) +(* Note: 0 < j is only to ensure coprimes 1 = {1} *) +val coprimes_def = zDefine ` + coprimes n = {j | j IN (natural n) /\ coprime j n} +`; +(* use zDefine as this is not computationally effective. *) + +(* Theorem: j IN coprimes n <=> 0 < j /\ j <= n /\ coprime j n *) +(* Proof: by coprimes_def, natural_element *) +val coprimes_element = store_thm( + "coprimes_element", + ``!n j. j IN coprimes n <=> 0 < j /\ j <= n /\ coprime j n``, + (rw[coprimes_def, natural_element] >> metis_tac[])); + +(* Theorem: coprimes n = (natural n) INTER {j | coprime j n} *) +(* Proof: by coprimes_def, EXTENSION, IN_INTER *) +val coprimes_alt = store_thm( + "coprimes_alt[compute]", + ``!n. coprimes n = (natural n) INTER {j | coprime j n}``, + rw[coprimes_def, EXTENSION]); +(* This is effective, put in computeLib. *) + +(* +> EVAL ``coprimes 10``; +val it = |- coprimes 10 = {9; 7; 3; 1}: thm +*) + +(* Theorem: coprimes n SUBSET natural n *) +(* Proof: by coprimes_def, SUBSET_DEF *) +val coprimes_subset = store_thm( + "coprimes_subset", + ``!n. coprimes n SUBSET natural n``, + rw[coprimes_def, SUBSET_DEF]); + +(* Theorem: FINITE (coprimes n) *) +(* Proof: + Since (coprimes n) SUBSET (natural n) by coprimes_subset + and !n. FINITE (natural n) by natural_finite + so FINITE (coprimes n) by SUBSET_FINITE +*) +val coprimes_finite = store_thm( + "coprimes_finite", + ``!n. FINITE (coprimes n)``, + metis_tac[coprimes_subset, natural_finite, SUBSET_FINITE]); + +(* Theorem: coprimes 0 = {} *) +(* Proof: + By coprimes_element, 0 < j /\ j <= 0, + which is impossible, hence empty. +*) +val coprimes_0 = store_thm( + "coprimes_0", + ``coprimes 0 = {}``, + rw[coprimes_element, EXTENSION]); + +(* Theorem: coprimes 1 = {1} *) +(* Proof: + By coprimes_element, 0 < j /\ j <= 1, + Only possible j is 1, and gcd 1 1 = 1. + *) +val coprimes_1 = store_thm( + "coprimes_1", + ``coprimes 1 = {1}``, + rw[coprimes_element, EXTENSION]); + +(* Theorem: 0 < n ==> 1 IN (coprimes n) *) +(* Proof: by coprimes_element, GCD_1 *) +val coprimes_has_1 = store_thm( + "coprimes_has_1", + ``!n. 0 < n ==> 1 IN (coprimes n)``, + rw[coprimes_element]); + +(* Theorem: (coprimes n = {}) <=> (n = 0) *) +(* Proof: + If part: coprimes n = {} ==> n = 0 + By contradiction. + Suppose n <> 0, then 0 < n. + Then 1 IN (coprimes n) by coprimes_has_1 + hence (coprimes n) <> {} by MEMBER_NOT_EMPTY + This contradicts (coprimes n) = {}. + Only-if part: n = 0 ==> coprimes n = {} + True by coprimes_0 +*) +val coprimes_eq_empty = store_thm( + "coprimes_eq_empty", + ``!n. (coprimes n = {}) <=> (n = 0)``, + rw[EQ_IMP_THM] >- + metis_tac[coprimes_has_1, MEMBER_NOT_EMPTY, NOT_ZERO_LT_ZERO] >> + rw[coprimes_0]); + +(* Theorem: 0 NOTIN (coprimes n) *) +(* Proof: + By coprimes_element, 0 < j /\ j <= n, + Hence j <> 0, or 0 NOTIN (coprimes n) +*) +val coprimes_no_0 = store_thm( + "coprimes_no_0", + ``!n. 0 NOTIN (coprimes n)``, + rw[coprimes_element]); + +(* Theorem: 1 < n ==> n NOTIN coprimes n *) +(* Proof: + By coprimes_element, 0 < j /\ j <= n /\ gcd j n = 1 + If j = n, 1 = gcd j n = gcd n n = n by GCD_REF + which is excluded by 1 < n, so j <> n. +*) +val coprimes_without_last = store_thm( + "coprimes_without_last", + ``!n. 1 < n ==> n NOTIN coprimes n``, + rw[coprimes_element]); + +(* Theorem: n IN coprimes n <=> (n = 1) *) +(* Proof: + By coprimes_element, 0 < j /\ j <= n /\ gcd j n = 1 + If n IN coprimes n, 1 = gcd j n = gcd n n = n by GCD_REF + If n = 1, 0 < n, n <= n, and gcd n n = n = 1 by GCD_REF +*) +val coprimes_with_last = store_thm( + "coprimes_with_last", + ``!n. n IN coprimes n <=> (n = 1)``, + rw[coprimes_element]); + +(* Theorem: 1 < n ==> (n - 1) IN (coprimes n) *) +(* Proof: by coprimes_element, coprime_PRE, GCD_SYM *) +val coprimes_has_last_but_1 = store_thm( + "coprimes_has_last_but_1", + ``!n. 1 < n ==> (n - 1) IN (coprimes n)``, + rpt strip_tac >> + `0 < n /\ 0 < n - 1` by decide_tac >> + rw[coprimes_element, coprime_PRE, GCD_SYM]); + +(* Theorem: 1 < n ==> !j. j IN coprimes n ==> j < n *) +(* Proof: + Since j IN coprimes n ==> j <= n by coprimes_element + If j = n, then gcd n n = n <> 1 by GCD_REF + Thus j <> n, or j < n. or by coprimes_without_last +*) +val coprimes_element_less = store_thm( + "coprimes_element_less", + ``!n. 1 < n ==> !j. j IN coprimes n ==> j < n``, + metis_tac[coprimes_element, coprimes_without_last, LESS_OR_EQ]); + +(* Theorem: 1 < n ==> !j. j IN coprimes n <=> j < n /\ coprime j n *) +(* Proof: + If part: j IN coprimes n ==> j < n /\ coprime j n + Note 0 < j /\ j <= n /\ coprime j n by coprimes_element + By contradiction, suppose n <= j. + Then j = n, but gcd n n = n <> 1 by GCD_REF + Only-if part: j < n /\ coprime j n ==> j IN coprimes n + This is to show: + 0 < j /\ j <= n /\ coprime j n by coprimes_element + By contradiction, suppose ~(0 < j). + Then j = 0, but gcd 0 n = n <> 1 by GCD_0L +*) +val coprimes_element_alt = store_thm( + "coprimes_element_alt", + ``!n. 1 < n ==> !j. j IN coprimes n <=> j < n /\ coprime j n``, + rw[coprimes_element] >> + `n <> 1` by decide_tac >> + rw[EQ_IMP_THM] >| [ + spose_not_then strip_assume_tac >> + `j = n` by decide_tac >> + metis_tac[GCD_REF], + spose_not_then strip_assume_tac >> + `j = 0` by decide_tac >> + metis_tac[GCD_0L] + ]); + +(* Theorem: 1 < n ==> (MAX_SET (coprimes n) = n - 1) *) +(* Proof: + Let s = coprimes n, m = MAX_SET s. + Note (n - 1) IN s by coprimes_has_last_but_1, 1 < n + Hence s <> {} by MEMBER_NOT_EMPTY + and FINITE s by coprimes_finite + Since !x. x IN s ==> x < n by coprimes_element_less, 1 < n + also !x. x < n ==> x <= (n - 1) by SUB_LESS_OR + Therefore MAX_SET s = n - 1 by MAX_SET_TEST +*) +val coprimes_max = store_thm( + "coprimes_max", + ``!n. 1 < n ==> (MAX_SET (coprimes n) = n - 1)``, + rpt strip_tac >> + qabbrev_tac `s = coprimes n` >> + `(n - 1) IN s` by rw[coprimes_has_last_but_1, Abbr`s`] >> + `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> + `FINITE s` by rw[coprimes_finite, Abbr`s`] >> + `!x. x IN s ==> x < n` by rw[coprimes_element_less, Abbr`s`] >> + `!x. x < n ==> x <= (n - 1)` by decide_tac >> + metis_tac[MAX_SET_TEST]); val _ = export_theory() diff --git a/src/pred_set/src/more_theories/cardinalScript.sml b/src/pred_set/src/more_theories/cardinalScript.sml index 2362e5fdf6..5a0972f8df 100644 --- a/src/pred_set/src/more_theories/cardinalScript.sml +++ b/src/pred_set/src/more_theories/cardinalScript.sml @@ -1461,13 +1461,6 @@ Proof simp[set_exp_def, FUN_EQ_THM] >> metis_tac[] QED - -val COUNT_EQ_EMPTY = Q.store_thm( - "COUNT_EQ_EMPTY[simp]", - ‘(count n = {}) <=> (n = 0)’, - simp[EXTENSION, EQ_IMP_THM] >> CONV_TAC CONTRAPOS_CONV >> simp[] >> - strip_tac >> qexists_tac ‘n - 1’ >> simp[]); - val POW_EQ_X_EXP_X = Q.store_thm( "POW_EQ_X_EXP_X", ‘INFINITE A ==> POW A =~ A ** A’, @@ -1537,6 +1530,7 @@ Proof Induct_on ‘FINITE’ >> simp[] >> rw[] >> gvs[CARD1_SING] QED +(* cf. permutesTheory.permutes_alt_bijns *) Definition bijns_def: bijns A = { f | BIJ f A A /\ !a. a NOTIN A ==> f a = a } End diff --git a/src/pred_set/src/pred_setScript.sml b/src/pred_set/src/pred_setScript.sml index 6bf834aca7..739a66fc09 100644 --- a/src/pred_set/src/pred_setScript.sml +++ b/src/pred_set/src/pred_setScript.sml @@ -10,16 +10,24 @@ (* DATE: January 1992 *) (* =====================================================================*) -open HolKernel Parse boolLib Prim_rec pairLib numLib numpairTheory +open HolKernel Parse boolLib BasicProvers; + +open Prim_rec pairLib numLib numpairTheory pairTheory numTheory prim_recTheory arithmeticTheory whileTheory - BasicProvers metisLib mesonLib simpLib boolSimps dividesTheory - combinTheory relationTheory optionTheory; + metisLib mesonLib simpLib boolSimps dividesTheory + combinTheory relationTheory optionTheory TotalDefn; val AP = numLib.ARITH_PROVE val ARITH_ss = numSimps.ARITH_ss val arith_ss = bool_ss ++ ARITH_ss val DECIDE = numLib.ARITH_PROVE +val decide_tac = DECIDE_TAC; +val metis_tac = METIS_TAC; +val qabbrev_tac = Q.ABBREV_TAC; +val qid_spec_tac = Q.ID_SPEC_TAC; +val qexists_tac = Q.EXISTS_TAC; + (* don't eta-contract these; that will force tactics to use one fixed version of srw_ss() *) fun fs thl = FULL_SIMP_TAC (srw_ss() ++ ARITH_ss) thl @@ -1685,7 +1693,73 @@ Proof ASM_REWRITE_TAC [EXTENSION,IN_SING,CHOICE_SING]] QED +(* Theorem: A non-empty set with all elements equal to a is the singleton {a} *) +(* Proof: by singleton definition. *) +val ONE_ELEMENT_SING = store_thm( + "ONE_ELEMENT_SING", + ``!s a. s <> {} /\ (!k. k IN s ==> (k = a)) ==> (s = {a})``, + rw[EXTENSION, EQ_IMP_THM] >> + metis_tac[]); + +(* Theorem: !x. x IN s ==> s INTER {x} = {x} *) +(* Proof: + s INTER {x} + = {x | x IN s /\ x IN {x}} by INTER_DEF + = {x' | x' IN s /\ x' = x} by IN_SING + = {x} by EXTENSION +*) +val INTER_SING = store_thm( + "INTER_SING", + ``!s x. x IN s ==> (s INTER {x} = {x})``, + rw[INTER_DEF, EXTENSION, EQ_IMP_THM]); + +(* Theorem: {x} INTER s = if x IN s then {x} else {} *) +(* Proof: by EXTENSION *) +val SING_INTER = store_thm( + "SING_INTER", + ``!s x. {x} INTER s = if x IN s then {x} else {}``, + rw[EXTENSION] >> + metis_tac[]); + +(* Theorem: s <> {} ==> (SING s <=> !x y. x IN s /\ y IN s ==> (x = y)) *) +(* Proof: + If part: SING s ==> !x y. x IN s /\ y IN s ==> (x = y)) + SING s ==> ?t. s = {t} by SING_DEF + x IN s ==> x = t by IN_SING + y IN s ==> y = t by IN_SING + Hence x = y + Only-if part: !x y. x IN s /\ y IN s ==> (x = y)) ==> SING s + True by ONE_ELEMENT_SING +*) +val SING_ONE_ELEMENT = store_thm( + "SING_ONE_ELEMENT", + ``!s. s <> {} ==> (SING s <=> !x y. x IN s /\ y IN s ==> (x = y))``, + metis_tac[SING_DEF, IN_SING, ONE_ELEMENT_SING]); +(* Theorem: SING s ==> (!x y. x IN s /\ y IN s ==> (x = y)) *) +(* Proof: + Note SING s <=> ?z. s = {z} by SING_DEF + and x IN {z} <=> x = z by IN_SING + and y IN {z} <=> y = z by IN_SING + Thus x = y +*) +val SING_ELEMENT = store_thm( + "SING_ELEMENT", + ``!s. SING s ==> (!x y. x IN s /\ y IN s ==> (x = y))``, + metis_tac[SING_DEF, IN_SING]); +(* Note: the converse really needs s <> {} *) + +(* Theorem: SING s <=> s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y)) *) +(* Proof: + If part: SING s ==> s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y)) + True by SING_EMPTY, SING_ELEMENT. + Only-if part: s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y)) ==> SING s + True by SING_ONE_ELEMENT. +*) +val SING_TEST = store_thm( + "SING_TEST", + ``!s. SING s <=> s <> {} /\ (!x y. x IN s /\ y IN s ==> (x = y))``, + metis_tac[SING_EMPTY, SING_ELEMENT, SING_ONE_ELEMENT]); (* ===================================================================== *) (* The image of a function on a set. *) @@ -2129,8 +2203,190 @@ val BIJ_INV = store_thm >> RW_TAC std_ss [] >> PROVE_TAC []); +(* Theorem: (!x. x IN s ==> (f x = g x)) ==> (INJ f s t <=> INJ g s t) *) +(* Proof: by INJ_DEF *) +val INJ_CONG = store_thm( + "INJ_CONG", + ``!f g s t. (!x. x IN s ==> (f x = g x)) ==> (INJ f s t <=> INJ g s t)``, + rw[INJ_DEF]); + +(* Theorem: (!x. x IN s ==> (f x = g x)) ==> (SURJ f s t <=> SURJ g s t) *) +(* Proof: by SURJ_DEF *) +val SURJ_CONG = store_thm( + "SURJ_CONG", + ``!f g s t. (!x. x IN s ==> (f x = g x)) ==> (SURJ f s t <=> SURJ g s t)``, + rw[SURJ_DEF] >> + metis_tac[]); + +(* Theorem: (!x. x IN s ==> (f x = g x)) ==> (BIJ f s t <=> BIJ g s t) *) +(* Proof: by BIJ_DEF, INJ_CONG, SURJ_CONG *) +val BIJ_CONG = store_thm( + "BIJ_CONG", + ``!f g s t. (!x. x IN s ==> (f x = g x)) ==> (BIJ f s t <=> BIJ g s t)``, + rw[BIJ_DEF] >> + metis_tac[INJ_CONG, SURJ_CONG]); + +(* +BIJ_LINV_BIJ |- !f s t. BIJ f s t ==> BIJ (LINV f s) t s +Cannot prove |- !f s t. BIJ f s t <=> BIJ (LINV f s) t s +because LINV f s depends on f! +*) + +(* Theorem: INJ f s t /\ x IN s ==> f x IN t *) +(* Proof: by INJ_DEF *) +val INJ_ELEMENT = store_thm( + "INJ_ELEMENT", + ``!f s t x. INJ f s t /\ x IN s ==> f x IN t``, + rw_tac std_ss[INJ_DEF]); + +(* Theorem: SURJ f s t /\ x IN s ==> f x IN t *) +(* Proof: by SURJ_DEF *) +val SURJ_ELEMENT = store_thm( + "SURJ_ELEMENT", + ``!f s t x. SURJ f s t /\ x IN s ==> f x IN t``, + rw_tac std_ss[SURJ_DEF]); + +(* Theorem: BIJ f s t /\ x IN s ==> f x IN t *) +(* Proof: by BIJ_DEF *) +val BIJ_ELEMENT = store_thm( + "BIJ_ELEMENT", + ``!f s t x. BIJ f s t /\ x IN s ==> f x IN t``, + rw_tac std_ss[BIJ_DEF, INJ_DEF]); + +(* Theorem: INJ f UNIV UNIV ==> INJ f s UNIV *) +(* Proof: + Note s SUBSET univ(:'a) by SUBSET_UNIV + and univ(:'b) SUBSET univ('b) by SUBSET_REFL + so INJ f univ(:'a) univ(:'b) ==> INJ f s univ(:'b) by INJ_SUBSET +*) +val INJ_SUBSET_UNIV = store_thm( + "INJ_SUBSET_UNIV", + ``!(f:'a -> 'b) (s:'a -> bool). INJ f UNIV UNIV ==> INJ f s UNIV``, + metis_tac[INJ_SUBSET, SUBSET_UNIV, SUBSET_REFL]); + +(* Theorem: INJ f P univ(:'b) ==> + !s t. s SUBSET P /\ t SUBSET P ==> ((IMAGE f s = IMAGE f t) <=> (s = t)) *) +(* Proof: + If part: IMAGE f s = IMAGE f t ==> s = t + Claim: s SUBSET t + Proof: by SUBSET_DEF, this is to show: x IN s ==> x IN t + x IN s + ==> f x IN (IMAGE f s) by INJ_DEF, IN_IMAGE + or f x IN (IMAGE f t) by given + ==> ?x'. x' IN t /\ (f x' = f x) by IN_IMAGE + But x IN P /\ x' IN P by SUBSET_DEF + Thus f x' = f x ==> x' = x by INJ_DEF + + Claim: t SUBSET s + Proof: similar to above by INJ_DEF, IN_IMAGE, SUBSET_DEF + + Hence s = t by SUBSET_ANTISYM + + Only-if part: s = t ==> IMAGE f s = IMAGE f t + This is trivially true. +*) +val INJ_IMAGE_EQ = store_thm( + "INJ_IMAGE_EQ", + ``!P f. INJ f P univ(:'b) ==> + !s t. s SUBSET P /\ t SUBSET P ==> ((IMAGE f s = IMAGE f t) <=> (s = t))``, + rw[EQ_IMP_THM] >> + (irule SUBSET_ANTISYM >> rpt conj_tac) >| [ + rw[SUBSET_DEF] >> + `?x'. x' IN t /\ (f x' = f x)` by metis_tac[IMAGE_IN, IN_IMAGE] >> + metis_tac[INJ_DEF, SUBSET_DEF], + rw[SUBSET_DEF] >> + `?x'. x' IN s /\ (f x' = f x)` by metis_tac[IMAGE_IN, IN_IMAGE] >> + metis_tac[INJ_DEF, SUBSET_DEF] + ]); + +(* Theorem: INJ f P univ(:'b) ==> + !s t. s SUBSET P /\ t SUBSET P ==> (IMAGE f (s INTER t) = (IMAGE f s) INTER (IMAGE f t)) *) +(* Proof: by EXTENSION, INJ_DEF, SUBSET_DEF *) +val INJ_IMAGE_INTER = store_thm( + "INJ_IMAGE_INTER", + ``!P f. INJ f P univ(:'b) ==> + !s t. s SUBSET P /\ t SUBSET P ==> (IMAGE f (s INTER t) = (IMAGE f s) INTER (IMAGE f t))``, + rw[EXTENSION] >> + metis_tac[INJ_DEF, SUBSET_DEF]); + +(* Theorem: INJ f P univ(:'b) ==> + !s t. s SUBSET P /\ t SUBSET P ==> (DISJOINT s t <=> DISJOINT (IMAGE f s) (IMAGE f t)) *) +(* Proof: + DISJOINT (IMAGE f s) (IMAGE f t) + <=> (IMAGE f s) INTER (IMAGE f t) = {} by DISJOINT_DEF + <=> IMAGE f (s INTER t) = {} by INJ_IMAGE_INTER, INJ f P univ(:'b) + <=> s INTER t = {} by IMAGE_EQ_EMPTY + <=> DISJOINT s t by DISJOINT_DEF +*) +val INJ_IMAGE_DISJOINT = store_thm( + "INJ_IMAGE_DISJOINT", + ``!P f. INJ f P univ(:'b) ==> + !s t. s SUBSET P /\ t SUBSET P ==> (DISJOINT s t <=> DISJOINT (IMAGE f s) (IMAGE f t))``, + metis_tac[DISJOINT_DEF, INJ_IMAGE_INTER, IMAGE_EQ_EMPTY]); + +(* Theorem: INJ I s univ(:'a) *) +(* Proof: + Note !x. I x = x by I_THM + so !x. x IN s ==> I x IN univ(:'a) by IN_UNIV + and !x y. x IN s /\ y IN s ==> (I x = I y) ==> (x = y) by above + Hence INJ I s univ(:'b) by INJ_DEF +*) +val INJ_I = store_thm( + "INJ_I", + ``!s:'a -> bool. INJ I s univ(:'a)``, + rw[INJ_DEF]); + +(* Theorem: INJ I (IMAGE f s) univ(:'b) *) +(* Proof: + Since !x. x IN (IMAGE f s) ==> x IN univ(:'b) by IN_UNIV + and !x y. x IN (IMAGE f s) /\ y IN (IMAGE f s) ==> + (I x = I y) ==> (x = y) by I_THM + Hence INJ I (IMAGE f s) univ(:'b) by INJ_DEF +*) +val INJ_I_IMAGE = store_thm( + "INJ_I_IMAGE", + ``!s f. INJ I (IMAGE f s) univ(:'b)``, + rw[INJ_DEF]); + +(* Theorem: BIJ f s t ==> !x y. x IN s /\ y IN s /\ (f x = f y) ==> (x = y) *) +(* Proof: by BIJ_DEF, INJ_DEF *) +Theorem BIJ_IS_INJ: + !f s t. BIJ f s t ==> !x y. x IN s /\ y IN s /\ (f x = f y) ==> (x = y) +Proof + rw[BIJ_DEF, INJ_DEF] +QED + +(* Theorem: BIJ f s t ==> !x. x IN t ==> ?y. y IN s /\ f y = x *) +(* Proof: by BIJ_DEF, SURJ_DEF. *) +Theorem BIJ_IS_SURJ: + !f s t. BIJ f s t ==> !x. x IN t ==> ?y. y IN s /\ f y = x +Proof + simp[BIJ_DEF, SURJ_DEF] +QED + +(* Theorem: INJ f s s /\ x IN s /\ y IN s ==> ((f x = f y) <=> (x = y)) *) +(* Proof: by INJ_DEF *) +Theorem INJ_EQ_11: + !f s x y. INJ f s s /\ x IN s /\ y IN s ==> ((f x = f y) <=> (x = y)) +Proof + metis_tac[INJ_DEF] +QED +(* Theorem: INJ f univ(:'a) univ(:'b) ==> !x y. f x = f y <=> x = y *) +(* Proof: by INJ_DEF, IN_UNIV. *) +Theorem INJ_IMP_11: + !f. INJ f univ(:'a) univ(:'b) ==> !x y. f x = f y <=> x = y +Proof + metis_tac[INJ_DEF, IN_UNIV] +QED +(* This is better than INJ_EQ_11 above. *) +(* Theorem: BIJ I s s *) +(* Proof: by definitions. *) +val BIJ_I_SAME = store_thm( + "BIJ_I_SAME", + ``!s. BIJ I s s``, + rw[BIJ_DEF, INJ_DEF, SURJ_DEF]); (* ===================================================================== *) (* Fun set and Schroeder Bernstein Theorems (from util_probTheory) *) @@ -2305,6 +2561,21 @@ Proof PROVE_TAC [] ] QED +(* Theorem: BIJ f s t <=> (!x. x IN s ==> f x IN t) /\ (!y. y IN t ==> ?!x. x IN s /\ (f x = y)) *) +(* Proof: + This is to prove: + (1) y IN t ==> ?!x. x IN s /\ (f x = y) + x exists by SURJ_DEF, and x is unique by INJ_DEF. + (2) x IN s /\ y IN s /\ f x = f y ==> x = y + true by INJ_DEF. + (3) x IN t ==> ?y. y IN s /\ (f y = x) + true by SURJ_DEF. +*) +val BIJ_THM = store_thm( + "BIJ_THM", + ``!f s t. BIJ f s t <=> (!x. x IN s ==> f x IN t) /\ (!y. y IN t ==> ?!x. x IN s /\ (f x = y))``, + RW_TAC std_ss [BIJ_DEF, INJ_DEF, SURJ_DEF, EQ_IMP_THM] >> metis_tac[]); + val BIJ_INSERT_IMP = store_thm (* from util_prob *) ("BIJ_INSERT_IMP", ``!f e s t. @@ -3130,6 +3401,21 @@ val CARD_SING = IMP_RES_THEN (ASSUME_TAC o SPEC (“x:'a”)) FINITE_INSERT THEN IMP_RES_TAC CARD_DEF THEN ASM_REWRITE_TAC [NOT_IN_EMPTY,CARD_DEF]); +(* Theorem: SING s ==> (CARD s = 1) *) +(* Proof: + Note s = {x} for some x by SING_DEF + so CARD s = 1 by CARD_SING +*) +Theorem SING_CARD_1: + !s. SING s ==> (CARD s = 1) +Proof + metis_tac[SING_DEF, CARD_SING] +QED +(* Note: SING s <=> (CARD s = 1) cannot be proved. +Only SING_IFF_CARD1 |- !s. SING s <=> (CARD s = 1) /\ FINITE s +That is: FINITE s /\ (CARD s = 1) ==> SING s +*) + Theorem SING_IFF_CARD1: !s:'a set. SING s <=> CARD s = 1 /\ FINITE s Proof @@ -3279,6 +3565,22 @@ val FINITE_BIJ_CARD = store_thm ``!f s t. FINITE s /\ BIJ f s t ==> (CARD s = CARD t)``, PROVE_TAC [FINITE_BIJ]); +(* Idea: improve FINITE_BIJ with iff of finiteness of s and t. *) + +(* Theorem: BIJ f s t ==> (FINITE s <=> FINITE t) *) +(* Proof: + If part: FINITE s ==> FINITE t + This is true by FINITE_BIJ + Only-if part: FINITE t ==> FINITE s + Note BIJ (LINV f s) t s by BIJ_LINV_BIJ + Thus FINITE s by FINITE_BIJ +*) +Theorem BIJ_FINITE_IFF: + !f s t. BIJ f s t ==> (FINITE s <=> FINITE t) +Proof + metis_tac[FINITE_BIJ, BIJ_LINV_BIJ] +QED + val FINITE_BIJ_CARD_EQ = Q.store_thm ("FINITE_BIJ_CARD_EQ", `!S. FINITE S ==> !t f. BIJ f S t /\ FINITE t ==> (CARD S = CARD t)`, @@ -3409,6 +3711,42 @@ val INJ_CARD_IMAGE_EQ = Q.store_thm ("INJ_CARD_IMAGE_EQ", `INJ f s t ==> FINITE s ==> (CARD (IMAGE f s) = CARD s)`, REPEAT STRIP_TAC THEN IMP_RES_TAC INJ_CARD_IMAGE) ; +(* Theorem: For a 1-1 map f: s -> s, s and (IMAGE f s) are of the same size. *) +(* Proof: + By finite induction on the set s: + Base case: CARD (IMAGE f {}) = CARD {} + True by IMAGE f {} = {} by IMAGE_EMPTY + Step case: !s. FINITE s /\ (CARD (IMAGE f s) = CARD s) ==> !e. e NOTIN s ==> (CARD (IMAGE f (e INSERT s)) = CARD (e INSERT s)) + CARD (IMAGE f (e INSERT s)) + = CARD (f e INSERT IMAGE f s) by IMAGE_INSERT + = SUC (CARD (IMAGE f s)) by CARD_INSERT: e NOTIN s, f e NOTIN s, for 1-1 map + = SUC (CARD s) by induction hypothesis + = CARD (e INSERT s) by CARD_INSERT: e NOTIN s. +*) +Theorem FINITE_CARD_IMAGE: + !s f. (!x y. (f x = f y) <=> (x = y)) /\ FINITE s ==> + (CARD (IMAGE f s) = CARD s) +Proof + Induct_on ‘FINITE’ >> rw[] +QED + +(* Theorem: !s. FINITE s ==> CARD (IMAGE SUC s)) = CARD s *) +(* Proof: + Since !n m. SUC n = SUC m <=> n = m by numTheory.INV_SUC + This is true by FINITE_CARD_IMAGE. +*) +val CARD_IMAGE_SUC = store_thm( + "CARD_IMAGE_SUC", + ``!s. FINITE s ==> (CARD (IMAGE SUC s) = CARD s)``, + rw[FINITE_CARD_IMAGE]); + +(* Theorem: FINITE s /\ FINITE t /\ DISJOINT s t ==> (CARD (s UNION t) = CARD s + CARD t) *) +(* Proof: by CARD_UNION_EQN, DISJOINT_DEF, CARD_EMPTY *) +val CARD_UNION_DISJOINT = store_thm( + "CARD_UNION_DISJOINT", + ``!s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> (CARD (s UNION t) = CARD s + CARD t)``, + rw_tac std_ss[CARD_UNION_EQN, DISJOINT_DEF, CARD_EMPTY]); + (* ------------------------------------------------------------------------- *) (* Relational form of CARD (from cardinalTheory) *) (* ------------------------------------------------------------------------- *) @@ -3619,6 +3957,17 @@ val COUNT_NOT_EMPTY = store_thm (* from probabilityTheory *) >> MATCH_MP_TAC LESS_EQ_LESS_TRANS >> Q.EXISTS_TAC `x` >> ASM_REWRITE_TAC []); +(* Theorem: (count n = {}) <=> (n = 0) *) +(* Proof: + Since FINITE (count n) by FINITE_COUNT + and CARD (count n) = n by CARD_COUNT + so count n = {} <=> n = 0 by CARD_EQ_0 +*) +val COUNT_EQ_EMPTY = store_thm( + "COUNT_EQ_EMPTY[simp]", + ``(count n = {}) <=> (n = 0)``, + metis_tac[FINITE_COUNT, CARD_COUNT, CARD_EQ_0]); + (* =====================================================================*) (* Infiniteness *) (* =====================================================================*) @@ -4815,6 +5164,281 @@ Proof rw[] >> fs[ITSET_THM] QED +(* Theorem: FINITE s /\ s <> {} ==> (ITSET f s b = ITSET f (REST s) (f (CHOICE s) b)) *) +(* Proof: by ITSET_THM. *) +val ITSET_PROPERTY = store_thm( + "ITSET_PROPERTY", + ``!s f b. FINITE s /\ s <> {} ==> (ITSET f s b = ITSET f (REST s) (f (CHOICE s) b))``, + rw[ITSET_THM]); + +(* Theorem: (f = g) ==> (ITSET f = ITSET g) *) +(* Proof: by congruence rule *) +val ITSET_CONG = store_thm( + "ITSET_CONG", + ``!f g. (f = g) ==> (ITSET f = ITSET g)``, + rw[]); + +(* Reduction of ITSET *) + +(* Theorem: (!x y z. f x (f y z) = f y (f x z)) ==> + !s x b. FINITE s /\ x NOTIN s ==> (ITSET f (x INSERT s) b = f x (ITSET f s b)) *) +(* Proof: + Since x NOTIN s ==> s DELETE x = s by DELETE_NON_ELEMENT + The result is true by COMMUTING_ITSET_RECURSES +*) +val ITSET_REDUCTION = store_thm( + "ITSET_REDUCTION", + ``!f. (!x y z. f x (f y z) = f y (f x z)) ==> + !s x b. FINITE s /\ x NOTIN s ==> (ITSET f (x INSERT s) b = f x (ITSET f s b))``, + rw[COMMUTING_ITSET_RECURSES, DELETE_NON_ELEMENT]); + +(* ------------------------------------------------------------------------- *) +(* Rework of ITSET Theorems *) +(* ------------------------------------------------------------------------- *) + +(* Define a function that gives closure and is commute_associative *) +val closure_comm_assoc_fun_def = Define` + closure_comm_assoc_fun f s <=> + (!x y. x IN s /\ y IN s ==> f x y IN s) /\ (* closure *) + (!x y z. x IN s /\ y IN s /\ z IN s ==> (f x (f y z) = f y (f x z))) (* comm_assoc *) +`; + +(* Theorem: FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> + !(x b):: t. ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) *) +(* Proof: + By complete induction on CARD s. + The goal is to show: + ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) [1] + Applying ITSET_INSERT to LHS, this is to prove: + ITSET f z (f y b) = ITSET f (s DELETE x) (f x b) + | | + | y = CHOICE (x INSERT s) + +--- z = REST (x INSERT s) + Note y NOTIN z by CHOICE_NOT_IN_REST + If x IN s, + then x INSERT s = s by ABSORPTION + thus y = CHOICE s, z = REST s by x INSERT s = s + + If x = y, + Since s = CHOICE s INSERT REST s by CHOICE_INSERT_REST + = y INSERT z by above y, z + Hence z = s DELETE y by DELETE_INSERT + Since CARD z < CARD s, apply induction: + ITSET f (y INSERT z) b = ITSET f (z DELETE y) (f y b) [2a] + For the original goal [1], + LHS = ITSET f (x INSERT s) b + = ITSET f s b by x INSERT s = s + = ITSET f (y INSERT z) b by s = y INSERT z + = ITSET f (z DELETE y) (f y b) by induction hypothesis [2a] + = ITSET f z (f y b) by DELETE_NON_ELEMENT, y NOTIN z + = ITSET f (s DELETE y) (f y b) by z = s DELETE y + = ITSET f (s DELETE x) (f x b) by x = y + = RHS + + If x <> y, let u = z DELETE x. + Note REST s = z = x INSERT u by INSERT_DELETE + Now s = x INSERT (y INSERT u) + = x INSERT v, where v = y INSERT u, and x NOTIN z. + Therefore (s DELETE x) = v by DELETE_INSERT + Since CARD v < CARD s, apply induction: + ITSET f (x INSERT v) b = ITSET f (v DELETE x) (f x b) [2b] + For the original goal [1], + LHS = ITSET f (x INSERT s) b + = ITSET f s b by x INSERT s = s + = ITSET f (x INSERT v) b by s = x INSERT v + = ITSET f (v DELETE x) (f x b) by induction hypothesis [2b] + = ITSET f v (f x b) by x NOTIN v + = ITSET f (s DELETE x) (f x b) by v = s DELETE x + = RHS + + If x NOTIN s, + then s DELETE x = x by DELETE_NON_ELEMENT + To prove: ITSET f (x INSERT s) b = ITSET f s (f x b) by [1] + The CHOICE/REST of (x INSERT s) cannot be simplified, but can be replaced by: + Note (x INSERT s) <> {} by NOT_EMPTY_INSERT + y INSERT z + = CHOICE (x INSERT s) INSERT (REST (x INSERT s)) by y = CHOICE (x INSERT s), z = REST (x INSERT s) + = x INSERT s by CHOICE_INSERT_REST + + If y = x, + Then z = s by DELETE_INSERT, y INSERT z = x INSERT s, y = x. + because s = s DELETE x by DELETE_NON_ELEMENT, x NOTIN s. + = (x INSERT s) DELETE x by DELETE_INSERT + = (y INSERT z) DELETE x by above + = (y INSERT z) DELETE y by y = x + = z DELETE y by DELETE_INSERT + = z by DELETE_NON_ELEMENT, y NOTIN z. + For the modified goal [1], + LHS = ITSET f (x INSERT s) b + = ITSET f (REST (x INSERT s)) (f (CHOICE (x INSERT s)) b) by ITSET_PROPERTY + = ITSET f z (f y b) by y = CHOICE (x INSERT s), z = REST (x INSERT s) + = ITSET f s (f x b) by z = s, y = x + = RHS + + If y <> x, + Then x IN z and y IN s by IN_INSERT, x INSERT s = y INSERT z and x <> y. + and s = y INSERT (s DELETE y) by INSERT_DELETE, y IN s + = y INSERT u where u = s DELETE y + Then y NOTIN u by IN_DELETE + and z = x INSERT u, + because x INSERT u + = x INSERT (s DELETE y) by u = s DELETE y + = (x INSERT s) DELETE y by DELETE_INSERT, x <> y + = (y INSERT z) DELETE y by x INSERT s = y INSERT z + = z by INSERT_DELETE + and x NOTIN u by IN_DELETE, u = s DELETE y, but x NOTIN s. + Thus CARD u < CARD s by CARD_INSERT, s = y INSERT u. + Apply induction: + !x b. ITSET f (x INSERT u) b = ITSET f (u DELETE x) (f x b) [2c] + + For the modified goal [1], + LHS = ITSET f (x INSERT s) b + = ITSET f (REST (x INSERT s)) (f (CHOICE (x INSERT s)) b) by ITSET_PROPERTY + = ITSET f z (f y b) by z = REST (x INSERT s), y = CHOICE (x INSERT s) + = ITSET f (x INSERT u) (f y b) by z = x INSERT u + = ITSET f (u DELETE x) (f x (f y b)) by induction hypothesis, [2c] + = ITSET f u (f x (f y b)) by x NOTIN u + RHS = ITSET f s (f x b) + = ITSET f (y INSERT u) (f x b) by s = y INSERT u + = ITSET f (u DELETE y) (f y (f x b)) by induction hypothesis, [2c] + = ITSET f u (f y (f x b)) by y NOTIN u + Applying the commute_associativity of f, LHS = RHS. +*) +Theorem SUBSET_COMMUTING_ITSET_INSERT: + !f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> + !(x b)::t. ITSET f (x INSERT s) b = ITSET f (s DELETE x) (f x b) +Proof + completeInduct_on `CARD s` >> + rule_assum_tac(SIMP_RULE bool_ss[GSYM RIGHT_FORALL_IMP_THM, AND_IMP_INTRO]) >> + rw[RES_FORALL_THM] >> + rw[ITSET_INSERT] >> + qabbrev_tac `y = CHOICE (x INSERT s)` >> + qabbrev_tac `z = REST (x INSERT s)` >> + `y NOTIN z` by metis_tac[CHOICE_NOT_IN_REST] >> + `!x s. x IN s ==> (x INSERT s = s)` by rw[ABSORPTION] >> + `!x s. x NOTIN s ==> (s DELETE x = s)` by rw[DELETE_NON_ELEMENT] >> + Cases_on `x IN s` >| [ + `s = y INSERT z` by metis_tac[NOT_IN_EMPTY, CHOICE_INSERT_REST] >> + `FINITE z` by metis_tac[REST_SUBSET, SUBSET_FINITE] >> + `CARD s = SUC (CARD z)` by rw[] >> + `CARD z < CARD s` by decide_tac >> + `z = s DELETE y` by metis_tac[DELETE_INSERT] >> + `z SUBSET t` by metis_tac[DELETE_SUBSET, SUBSET_TRANS] >> + Cases_on `x = y` >- metis_tac[] >> + `x IN z` by metis_tac[IN_INSERT] >> + qabbrev_tac `u = z DELETE x` >> + `z = x INSERT u` by rw[INSERT_DELETE, Abbr`u`] >> + `x NOTIN u` by metis_tac[IN_DELETE] >> + qabbrev_tac `v = y INSERT u` >> + `s = x INSERT v` by simp[INSERT_COMM, Abbr `v`] >> + `x NOTIN v` by rw[Abbr `v`] >> + `FINITE v` by metis_tac[FINITE_INSERT] >> + `CARD s = SUC (CARD v)` by metis_tac[CARD_INSERT] >> + `CARD v < CARD s` by decide_tac >> + `v SUBSET t` by metis_tac[INSERT_SUBSET, SUBSET_TRANS] >> + `s DELETE x = v` by rw[DELETE_INSERT, Abbr `v`] >> + `v = s DELETE x` by rw[] >> + `y IN t` by metis_tac[NOT_INSERT_EMPTY, CHOICE_DEF, SUBSET_DEF] >> + metis_tac[], + `x INSERT s <> {}` by rw[] >> + `y INSERT z = x INSERT s` by rw[CHOICE_INSERT_REST, Abbr`y`, Abbr`z`] >> + Cases_on `x = y` >- metis_tac[DELETE_INSERT, ITSET_PROPERTY] >> + `x IN z /\ y IN s` by metis_tac[IN_INSERT] >> + qabbrev_tac `u = s DELETE y` >> + `s = y INSERT u` by rw[INSERT_DELETE, Abbr`u`] >> + `y NOTIN u` by metis_tac[IN_DELETE] >> + `z = x INSERT u` by metis_tac[DELETE_INSERT, INSERT_DELETE] >> + `x NOTIN u` by metis_tac[IN_DELETE] >> + `FINITE u` by metis_tac[FINITE_DELETE, SUBSET_FINITE] >> + `CARD u < CARD s` by rw[] >> + `u SUBSET t` by metis_tac[DELETE_SUBSET, SUBSET_TRANS] >> + `y IN t` by metis_tac[CHOICE_DEF, SUBSET_DEF] >> + `f y b IN t /\ f x b IN t` by prove_tac[closure_comm_assoc_fun_def] >> + `ITSET f z (f y b) = ITSET f (x INSERT u) (f y b)` by rw[] >> + `_ = ITSET f (u DELETE x) (f x (f y b))` by metis_tac[] >> + `_ = ITSET f u (f x (f y b))` by rw[] >> + `ITSET f s (f x b) = ITSET f (y INSERT u) (f x b)` by rw[] >> + `_ = ITSET f (u DELETE y) (f y (f x b))` by metis_tac[] >> + `_ = ITSET f u (f y (f x b))` by rw[] >> + `f x (f y b) = f y (f x b)` by prove_tac[closure_comm_assoc_fun_def] >> + metis_tac[] + ] +QED + +(* This is a generalisation of COMMUTING_ITSET_INSERT, removing the requirement + of commuting everywhere. *) + +(* Theorem: FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> + !(x b)::t. ITSET f s (f x b) = f x (ITSET f s b) *) +(* Proof: + By complete induction on CARD s. + The goal is to show: ITSET f s (f x b) = f x (ITSET f s b) + Base: s = {}, + LHS = ITSET f {} (f x b) + = f x b by ITSET_EMPTY + = f x (ITSET f {} b) by ITSET_EMPTY + = RHS + Step: s <> {}, + Let s = y INSERT z, where y = CHOICE s, z = REST s. + Then y NOTIN z by CHOICE_NOT_IN_REST + But y IN t by CHOICE_DEF, SUBSET_DEF + and z SUBSET t by REST_SUBSET, SUBSET_TRANS + Also FINITE z by REST_SUBSET, SUBSET_FINITE + Thus CARD s = SUC (CARD z) by CARD_INSERT + or CARD z < CARD s + Note f x b IN t /\ f y b IN t by closure_comm_assoc_fun_def + + LHS = ITSET f s (f x b) + = ITSET f (y INSERT z) (f x b) by s = y INSERT z + = ITSET f (z DELETE y) (f y (f x b)) by SUBSET_COMMUTING_ITSET_INSERT, y, f x b IN t + = ITSET f z (f y (f x b)) by DELETE_NON_ELEMENT, y NOTIN z + = ITSET f z (f x (f y b)) by closure_comm_assoc_fun_def, x, y, b IN t + = f x (ITSET f z (f y b)) by inductive hypothesis, CARD z < CARD s, x, f y b IN t + = f x (ITSET f (z DELETE y) (f y b)) by DELETE_NON_ELEMENT, y NOTIN z + = f x (ITSET f (y INSERT z) b) by SUBSET_COMMUTING_ITSET_INSERT, y, f y b IN t + = f x (ITSET f s b) by s = y INSERT z + = RHS +*) +val SUBSET_COMMUTING_ITSET_REDUCTION = store_thm( + "SUBSET_COMMUTING_ITSET_REDUCTION", + ``!f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> + !(x b)::t. ITSET f s (f x b) = f x (ITSET f s b)``, + completeInduct_on `CARD s` >> + rule_assum_tac(SIMP_RULE bool_ss [GSYM RIGHT_FORALL_IMP_THM, AND_IMP_INTRO]) >> + rw[RES_FORALL_THM] >> + Cases_on `s = {}` >- + rw[ITSET_EMPTY] >> + `?y z. (y = CHOICE s) /\ (z = REST s) /\ (s = y INSERT z)` by rw[CHOICE_INSERT_REST] >> + `y NOTIN z` by metis_tac[CHOICE_NOT_IN_REST] >> + `y IN t` by metis_tac[CHOICE_DEF, SUBSET_DEF] >> + `z SUBSET t` by metis_tac[REST_SUBSET, SUBSET_TRANS] >> + `FINITE z` by metis_tac[REST_SUBSET, SUBSET_FINITE] >> + `CARD s = SUC (CARD z)` by rw[] >> + `CARD z < CARD s` by decide_tac >> + `f x b IN t /\ f y b IN t /\ (f y (f x b) = f x (f y b))` + by prove_tac[closure_comm_assoc_fun_def] >> + metis_tac[SUBSET_COMMUTING_ITSET_INSERT, DELETE_NON_ELEMENT]); + +(* This helps to prove the next generalisation. *) + +(* Theorem: FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> + !(x b):: t. ITSET f (x INSERT s) b = f x (ITSET f (s DELETE x) b) *) +(* Proof: + Note (s DELETE x) SUBSET t by DELETE_SUBSET, SUBSET_TRANS + and FINITE (s DELETE x) by FINITE_DELETE, FINITE s + ITSET f (x INSERT s) b + = ITSET f (s DELETE x) (f x b) by SUBSET_COMMUTING_ITSET_INSERT + = f x (ITSET f (s DELETE x) b) by SUBSET_COMMUTING_ITSET_REDUCTION, (s DELETE x) SUBSET t +*) +val SUBSET_COMMUTING_ITSET_RECURSES = store_thm( + "SUBSET_COMMUTING_ITSET_RECURSES", + ``!f s t. FINITE s /\ s SUBSET t /\ closure_comm_assoc_fun f t ==> + !(x b):: t. ITSET f (x INSERT s) b = f x (ITSET f (s DELETE x) b)``, + rw[RES_FORALL_THM] >> + `(s DELETE x) SUBSET t` by metis_tac[DELETE_SUBSET, SUBSET_TRANS] >> + `FINITE (s DELETE x)` by rw[] >> + metis_tac[SUBSET_COMMUTING_ITSET_INSERT, SUBSET_COMMUTING_ITSET_REDUCTION]); + (* ---------------------------------------------------------------------- SUM_IMAGE @@ -4851,6 +5475,24 @@ val SUM_IMAGE_THM = store_thm( SRW_TAC [ARITH_ss][Abbr`g`] ]); +(* Theorem: SIGMA f {} = 0 *) +(* Proof: by SUM_IMAGE_THM *) +val SUM_IMAGE_EMPTY = store_thm( + "SUM_IMAGE_EMPTY", + ``!f. SIGMA f {} = 0``, + rw[SUM_IMAGE_THM]); + +(* Theorem: FINITE s ==> !e. e NOTIN s ==> (SIGMA f (e INSERT s) = f e + (SIGMA f s)) *) +(* Proof: + SIGMA f (e INSERT s) + = f e + SIGMA f (s DELETE e) by SUM_IMAGE_THM + = f e + SIGMA f s by DELETE_NON_ELEMENT +*) +val SUM_IMAGE_INSERT = store_thm( + "SUM_IMAGE_INSERT", + ``!f s. FINITE s ==> !e. e NOTIN s ==> (SIGMA f (e INSERT s) = f e + (SIGMA f s))``, + rw[SUM_IMAGE_THM, DELETE_NON_ELEMENT]); + val SUM_IMAGE_SING = store_thm( "SUM_IMAGE_SING", ``!f e. SUM_IMAGE f {e} = f e``, @@ -5024,6 +5666,12 @@ HO_MATCH_MP_TAC FINITE_INDUCT THEN SRW_TAC [][SUM_IMAGE_THM,SUM_IMAGE_DELETE] QED +(* Theorem: (!x. x IN s ==> (f1 x = f2 x)) ==> (SIGMA f1 s = SIGMA f2 s) *) +val SIGMA_CONG = store_thm( + "SIGMA_CONG", + ``!s f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (SIGMA f1 s = SIGMA f2 s)``, + rw[SUM_IMAGE_CONG]); + Theorem SUM_IMAGE_ZERO: !s. FINITE s ==> ((SIGMA f s = 0) <=> (!x. x IN s ==> (f x = 0))) Proof @@ -5033,6 +5681,45 @@ Proof METIS_TAC [] QED +(* Theorem: FINITE s ==> (CARD s = SIGMA (\x. 1) s) *) +(* Proof: + By finite induction: + Base case: CARD {} = SIGMA (\x. 1) {} + LHS = CARD {} + = 0 by CARD_EMPTY + RHS = SIGMA (\x. 1) {} + = 0 = LHS by SUM_IMAGE_THM + Step case: (CARD s = SIGMA (\x. 1) s) ==> + !e. e NOTIN s ==> (CARD (e INSERT s) = SIGMA (\x. 1) (e INSERT s)) + CARD (e INSERT s) + = SUC (CARD s) by CARD_DEF + = SUC (SIGMA (\x. 1) s) by induction hypothesis + = 1 + SIGMA (\x. 1) s by ADD1, ADD_COMM + = (\x. 1) e + SIGMA (\x. 1) s by function application + = (\x. 1) e + SIGMA (\x. 1) (s DELETE e) by DELETE_NON_ELEMENT + = SIGMA (\x. 1) (e INSERT s) by SUM_IMAGE_THM +*) +val CARD_AS_SIGMA = store_thm( + "CARD_AS_SIGMA", + ``!s. FINITE s ==> (CARD s = SIGMA (\x. 1) s)``, + ho_match_mp_tac FINITE_INDUCT >> + conj_tac >- + rw[SUM_IMAGE_THM] >> + rpt strip_tac >> + `CARD (e INSERT s) = SUC (SIGMA (\x. 1) s)` by rw[] >> + `_ = 1 + SIGMA (\x. 1) s` by rw_tac std_ss[ADD1, ADD_COMM] >> + `_ = (\x. 1) e + SIGMA (\x. 1) s` by rw[] >> + `_ = (\x. 1) e + SIGMA (\x. 1) (s DELETE e)` by metis_tac[DELETE_NON_ELEMENT] >> + `_ = SIGMA (\x. 1) (e INSERT s)` by rw[SUM_IMAGE_THM] >> + decide_tac); + +(* Theorem: FINITE s ==> (CARD s = SIGMA (K 1) s) *) +(* Proof: by CARD_AS_SIGMA, SIGMA_CONG *) +val CARD_EQ_SIGMA = store_thm( + "CARD_EQ_SIGMA", + ``!s. FINITE s ==> (CARD s = SIGMA (K 1) s)``, + rw[CARD_AS_SIGMA, SIGMA_CONG]); + Theorem ABS_DIFF_SUM_IMAGE: !s. FINITE s ==> (ABS_DIFF (SIGMA f s) (SIGMA g s) <= SIGMA (\x. ABS_DIFF (f x) (g x)) s) @@ -5113,6 +5800,133 @@ Proof \\ fs[DELETE_NON_ELEMENT] QED +(* Theorem: FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (SIGMA f s = k * CARD s) *) +(* Proof: + By finite induction on s. + Base: SIGMA f {} = k * CARD {} + SIGMA f {} + = 0 by SUM_IMAGE_EMPTY + = k * 0 by MULT_0 + = k * CARD {} by CARD_EMPTY + Step: SIGMA f s = k * CARD s /\ e NOTIN s /\ !x. x IN e INSERT s /\ f x = k ==> + SIGMA f (e INSERT s) = k * CARD (e INSERT s) + Note f e = k /\ !x. x IN s ==> f x = k by IN_INSERT + SIGMA f (e INSERT s) + = f e + SIGMA f (s DELETE e) by SUM_IMAGE_THM + = k + SIGMA f s by DELETE_NON_ELEMENT, f e = k + = k + k * CARD s by induction hypothesis + = k * (1 + CARD s) by LEFT_ADD_DISTRIB + = k * SUC (CARD s) by SUC_ONE_ADD + = k * CARD (e INSERT s) by CARD_INSERT +*) +val SIGMA_CONSTANT = store_thm( + "SIGMA_CONSTANT", + ``!s. FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (SIGMA f s = k * CARD s)``, + ho_match_mp_tac FINITE_INDUCT >> + rpt strip_tac >- + rw[SUM_IMAGE_EMPTY] >> + `(f e = k) /\ !x. x IN s ==> (f x = k)` by rw[] >> + `SIGMA f (e INSERT s) = f e + SIGMA f (s DELETE e)` by rw[SUM_IMAGE_THM] >> + `_ = k + SIGMA f s` by metis_tac[DELETE_NON_ELEMENT] >> + `_ = k + k * CARD s` by rw[] >> + `_ = k * (1 + CARD s)` by rw[] >> + `_ = k * SUC (CARD s)` by rw[ADD1] >> + `_ = k * CARD (e INSERT s)` by rw[CARD_INSERT] >> + rw[]); + +(* Theorem: FINITE s ==> !c. SIGMA (K c) s = c * CARD s *) +(* Proof: by SIGMA_CONSTANT. *) +val SUM_IMAGE_CONSTANT = store_thm( + "SUM_IMAGE_CONSTANT", + ``!s. FINITE s ==> !c. SIGMA (K c) s = c * CARD s``, + rw[SIGMA_CONSTANT]); + +(* Idea: If !e. e IN s, CARD e = n, SIGMA CARD s = n * CARD s. *) + +(* Theorem: FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s *) +(* Proof: by SIGMA_CONSTANT, take f = CARD. *) +Theorem SIGMA_CARD_CONSTANT: + !n s. FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s +Proof + simp[SIGMA_CONSTANT] +QED + +(* Theorem alias, or rename SIGMA_CARD_CONSTANT *) +Theorem SIGMA_CARD_SAME_SIZE_SETS = SIGMA_CARD_CONSTANT; +(* val SIGMA_CARD_SAME_SIZE_SETS = + |- !n s. FINITE s /\ (!e. e IN s ==> CARD e = n) ==> SIGMA CARD s = n * CARD s: thm *) + +(* Theorem: FINITE s ==> !f g. (!x. x IN s ==> f x <= g x) ==> (SIGMA f s <= SIGMA g s) *) +(* Proof: + By finite induction: + Base case: !f g. (!x. x IN {} ==> f x <= g x) ==> SIGMA f {} <= SIGMA g {} + True since SIGMA f {} = 0 by SUM_IMAGE_THM + Step case: !f g. (!x. x IN s ==> f x <= g x) ==> SIGMA f s <= SIGMA g s ==> + e NOTIN s ==> + !x. x IN e INSERT s ==> f x <= g x ==> !f g. SIGMA f (e INSERT s) <= SIGMA g (e INSERT s) + SIGMA f (e INSERT s) <= SIGMA g (e INSERT s) + means f e + SIGMA f (s DELETE e) <= g e + SIGMA g (s DELETE e) by SUM_IMAGE_THM + or f e + SIGMA f s <= g e + SIGMA g s by DELETE_NON_ELEMENT + Now, x IN e INSERT s ==> (x = e) or (x IN s) by IN_INSERT + Therefore f e <= g e, and !x IN s, f x <= g x by x IN (e INSERT s) implication + or f e <= g e, and SIGMA f s <= SIGMA g s by induction hypothesis + Hence f e + SIGMA f s <= g e + SIGMA g s by arithmetic +*) +val SIGMA_LE_SIGMA = store_thm( + "SIGMA_LE_SIGMA", + ``!s. FINITE s ==> !f g. (!x. x IN s ==> f x <= g x) ==> (SIGMA f s <= SIGMA g s)``, + ho_match_mp_tac FINITE_INDUCT >> + conj_tac >- + rw[SUM_IMAGE_THM] >> + rw[SUM_IMAGE_THM, DELETE_NON_ELEMENT] >> + `f e <= g e /\ SIGMA f s <= SIGMA g s` by rw[] >> + decide_tac); + +(* Theorem: FINITE s /\ FINITE t ==> !f. SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t *) +(* Proof: + Note SIGMA f (s UNION t) + = SIGMA f s + SIGMA f t - SIGMA f (s INTER t) by SUM_IMAGE_UNION + now s INTER t SUBSET s /\ s INTER t SUBSET t by INTER_SUBSET + so SIGMA f (s INTER t) <= SIGMA f s by SUM_IMAGE_SUBSET_LE + and SIGMA f (s INTER t) <= SIGMA f t by SUM_IMAGE_SUBSET_LE + thus SIGMA f (s INTER t) <= SIGMA f s + SIGMA f t by arithmetic + The result follows by ADD_EQ_SUB +*) +val SUM_IMAGE_UNION_EQN = store_thm( + "SUM_IMAGE_UNION_EQN", + ``!s t. FINITE s /\ FINITE t ==> !f. SIGMA f (s UNION t) + SIGMA f (s INTER t) = SIGMA f s + SIGMA f t``, + rpt strip_tac >> + `SIGMA f (s UNION t) = SIGMA f s + SIGMA f t - SIGMA f (s INTER t)` by rw[SUM_IMAGE_UNION] >> + `SIGMA f (s INTER t) <= SIGMA f s` by rw[SUM_IMAGE_SUBSET_LE, INTER_SUBSET] >> + `SIGMA f (s INTER t) <= SIGMA f t` by rw[SUM_IMAGE_SUBSET_LE, INTER_SUBSET] >> + `SIGMA f (s INTER t) <= SIGMA f s + SIGMA f t` by decide_tac >> + rw[ADD_EQ_SUB]); + +(* Theorem: FINITE s /\ FINITE t /\ DISJOINT s t ==> !f. SIGMA f (s UNION t) = SIGMA f s + SIGMA f t *) +(* Proof: + SIGMA f (s UNION t) + = SIGMA f s + SIGMA f t - SIGMA f (s INTER t) by SUM_IMAGE_UNION + = SIGMA f s + SIGMA f t - SIGMA f {} by DISJOINT_DEF + = SIGMA f s + SIGMA f t - 0 by SUM_IMAGE_EMPTY + = SIGMA f s + SIGMA f t by arithmetic +*) +val SUM_IMAGE_DISJOINT = store_thm( + "SUM_IMAGE_DISJOINT", + ``!s t. FINITE s /\ FINITE t /\ DISJOINT s t ==> !f. SIGMA f (s UNION t) = SIGMA f s + SIGMA f t``, + rw_tac std_ss[SUM_IMAGE_UNION, DISJOINT_DEF, SUM_IMAGE_EMPTY]); + +(* Theorem: FINITE s /\ s <> {} ==> !f g. (!x. x IN s ==> f x < g x) ==> SIGMA f s < SIGMA g s *) +(* Proof: + Note s <> {} ==> ?x. x IN s by MEMBER_NOT_EMPTY + Thus ?x. x IN s /\ f x < g x by implication + and !x. x IN s ==> f x <= g x by LESS_IMP_LESS_OR_EQ + ==> SIGMA f s < SIGMA g s by SUM_IMAGE_MONO_LESS +*) +val SUM_IMAGE_MONO_LT = store_thm( + "SUM_IMAGE_MONO_LT", + ``!s. FINITE s /\ s <> {} ==> !f g. (!x. x IN s ==> f x < g x) ==> SIGMA f s < SIGMA g s``, + metis_tac[SUM_IMAGE_MONO_LESS, LESS_IMP_LESS_OR_EQ, MEMBER_NOT_EMPTY]); + (*---------------------------------------------------------------------------*) (* SUM_SET sums the elements of a set of natural numbers *) (*---------------------------------------------------------------------------*) @@ -5133,6 +5947,17 @@ Proof SRW_TAC [][SUM_SET_DEF, SUM_IMAGE_SING] QED +(* Theorem: FINITE s /\ x NOTIN s ==> (SUM_SET (x INSERT s) = x + SUM_SET s) *) +(* Proof: + SUM_SET (x INSERT s) + = x + SUM_SET (s DELETE x) by SUM_SET_THM + = x + SUM_SET s by DELETE_NON_ELEMENT +*) +val SUM_SET_INSERT = store_thm( + "SUM_SET_INSERT", + ``!s x. FINITE s /\ x NOTIN s ==> (SUM_SET (x INSERT s) = x + SUM_SET s)``, + rw[SUM_SET_THM, DELETE_NON_ELEMENT]); + val SUM_SET_SUBSET_LE = store_thm( "SUM_SET_SUBSET_LE", ``!s t. FINITE t /\ s SUBSET t ==> SUM_SET s <= SUM_SET t``, @@ -5264,6 +6089,112 @@ Proof \\ METIS_TAC[DELETE_NON_ELEMENT] QED +(* Theorem: PI f {} = 1 *) +(* Proof: by PROD_IMAGE_THM *) +val PROD_IMAGE_EMPTY = store_thm( + "PROD_IMAGE_EMPTY", + ``!f. PI f {} = 1``, + rw[PROD_IMAGE_THM]); + +(* Theorem: FINITE s ==> !f e. e NOTIN s ==> (PI f (e INSERT s) = (f e) * PI f s) *) +(* Proof: by PROD_IMAGE_THM, DELETE_NON_ELEMENT *) +val PROD_IMAGE_INSERT = store_thm( + "PROD_IMAGE_INSERT", + ``!s. FINITE s ==> !f e. e NOTIN s ==> (PI f (e INSERT s) = (f e) * PI f s)``, + rw[PROD_IMAGE_THM, DELETE_NON_ELEMENT]); + +(* Theorem: FINITE s ==> !f e. 0 < f e ==> + (PI f (s DELETE e) = if e IN s then ((PI f s) DIV (f e)) else PI f s) *) +(* Proof: + If e IN s, + Note PI f (e INSERT s) = (f e) * PI f (s DELETE e) by PROD_IMAGE_THM + Thus PI f (s DELETE e) = PI f (e INSERT s) DIV (f e) by DIV_SOLVE_COMM, 0 < f e + = (PI f s) DIV (f e) by ABSORPTION, e IN s. + If e NOTIN s, + PI f (s DELETE e) = PI f e by DELETE_NON_ELEMENT +*) +val PROD_IMAGE_DELETE = store_thm( + "PROD_IMAGE_DELETE", + ``!s. FINITE s ==> !f e. 0 < f e ==> + (PI f (s DELETE e) = if e IN s then ((PI f s) DIV (f e)) else PI f s)``, + rpt strip_tac >> + rw_tac std_ss[] >- + metis_tac[PROD_IMAGE_THM, DIV_SOLVE_COMM, ABSORPTION] >> + metis_tac[DELETE_NON_ELEMENT]); +(* The original proof of SUM_IMAGE_DELETE is clumsy. *) + +(* Theorem: (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s) *) +(* Proof: + If INFINITE s, + PI f1 s + = ITSET (\e acc. f e * acc) s 1 by PROD_IMAGE_DEF + = ARB by ITSET_def + Similarly, PI f2 s = ARB = PI f1 s. + If FINITE s, + Apply finite induction on s. + Base: PI f1 {} = PI f2 {}, true by PROD_IMAGE_EMPTY + Step: !f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s) ==> + e NOTIN s /\ !x. x IN e INSERT s ==> (f1 x = f2 x) ==> PI f1 (e INSERT s) = PI f2 (e INSERT s) + Note !x. x IN e INSERT s ==> (f1 x = f2 x) + ==> (f1 e = f2 e) \/ !x. s IN s ==> (f1 x = f2 x) by IN_INSERT + PI f1 (e INSERT s) + = (f1 e) * (PI f1 s) by PROD_IMAGE_INSERT, e NOTIN s + = (f1 e) * (PI f2 s) by induction hypothesis + = (f2 e) * (PI f2 s) by f1 e = f2 e + = PI f2 (e INSERT s) by PROD_IMAGE_INSERT, e NOTIN s +*) +val PROD_IMAGE_CONG = store_thm( + "PROD_IMAGE_CONG", + ``!s f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s)``, + rpt strip_tac >> + reverse (Cases_on `FINITE s`) >| [ + rw[PROD_IMAGE_DEF, Once ITSET_def] >> + rw[Once ITSET_def], + pop_assum mp_tac >> + pop_assum mp_tac >> + qid_spec_tac `s` >> + `!s. FINITE s ==> !f1 f2. (!x. x IN s ==> (f1 x = f2 x)) ==> (PI f1 s = PI f2 s)` suffices_by rw[] >> + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_IMAGE_EMPTY] >> + metis_tac[PROD_IMAGE_INSERT, IN_INSERT] + ]); + +(* Theorem: FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s) *) +(* Proof: + By finite induction on s. + Base: PI f {} = k ** CARD {} + PI f {} + = 1 by PROD_IMAGE_THM + = c ** 0 by EXP + = c ** CARD {} by CARD_DEF + Step: !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s) ==> + e NOTIN s ==> PI f (e INSERT s) = k ** CARD (e INSERT s) + PI f (e INSERT s) + = ((f e) * PI (K c) (s DELETE e) by PROD_IMAGE_THM + = c * PI (K c) (s DELETE e) by function application + = c * PI (K c) s by DELETE_NON_ELEMENT + = c * c ** CARD s by induction hypothesis + = c ** (SUC (CARD s)) by EXP + = c ** CARD (e INSERT s) by CARD_INSERT, e NOTIN s +*) +val PI_CONSTANT = store_thm( + "PI_CONSTANT", + ``!s. FINITE s ==> !f k. (!x. x IN s ==> (f x = k)) ==> (PI f s = k ** CARD s)``, + Induct_on `FINITE` >> + rpt strip_tac >- + rw[PROD_IMAGE_THM] >> + rw[PROD_IMAGE_THM, CARD_INSERT] >> + fs[] >> + metis_tac[DELETE_NON_ELEMENT, EXP]); + +(* Theorem: FINITE s ==> !c. PI (K c) s = c ** (CARD s) *) +(* Proof: by PI_CONSTANT. *) +val PROD_IMAGE_CONSTANT = store_thm( + "PROD_IMAGE_CONSTANT", + ``!s. FINITE s ==> !c. PI (K c) s = c ** (CARD s)``, + rw[PI_CONSTANT]); + (*---------------------------------------------------------------------------*) (* PROD_SET multiplies the elements of a set of natural numbers *) (*---------------------------------------------------------------------------*) @@ -5293,9 +6224,24 @@ val PROD_SET_IMAGE_REDUCTION = store_thm( (PROD_SET (IMAGE f (x INSERT s)) = (f x) * PROD_SET (IMAGE f s))``, METIS_TAC [DELETE_NON_ELEMENT, IMAGE_INSERT, PROD_SET_THM]); +(* PROD_SET_IMAGE_REDUCTION |> ISPEC ``I:num -> num``; *) -(* every finite, non-empty set of natural numbers has a maximum element *) +(* Theorem: FINITE s /\ x NOTIN s ==> (PROD_SET (x INSERT s) = x * PROD_SET s) *) +(* Proof: + Since !x. I x = x by I_THM + and !s. IMAGE I s = s by IMAGE_I + thus the result follows by PROD_SET_IMAGE_REDUCTION +*) +val PROD_SET_INSERT = store_thm( + "PROD_SET_INSERT", + ``!x s. FINITE s /\ x NOTIN s ==> (PROD_SET (x INSERT s) = x * PROD_SET s)``, + metis_tac[PROD_SET_IMAGE_REDUCTION, combinTheory.I_THM, IMAGE_I]); +(* ------------------------------------------------------------------------- *) +(* Maximum and Minimum of a Set *) +(* ------------------------------------------------------------------------- *) + +(* every finite, non-empty set of natural numbers has a maximum element *) val max_lemma = prove( ``!s. FINITE s ==> ?x. (s <> {} ==> x IN s /\ !y. y IN s ==> y <= x) /\ ((s = {}) ==> (x = 0))``, @@ -5511,6 +6457,168 @@ Proof full_simp_tac (srw_ss()) [] QED +(* Theorem: FINITE s /\ MAX_SET s < n ==> !x. x IN s ==> x < n *) +(* Proof: + Since x IN s, s <> {} by MEMBER_NOT_EMPTY + Hence x <= MAX_SET s by MAX_SET_DEF + Thus x < n by LESS_EQ_LESS_TRANS +*) +val MAX_SET_LESS = store_thm( + "MAX_SET_LESS", + ``!s n. FINITE s /\ MAX_SET s < n ==> !x. x IN s ==> x < n``, + metis_tac[MEMBER_NOT_EMPTY, MAX_SET_DEF, LESS_EQ_LESS_TRANS]); + +(* Theorem: FINITE s /\ s <> {} ==> !x. x IN s /\ (!y. y IN s ==> y <= x) ==> (x = MAX_SET s) *) +(* Proof: + Let m = MAX_SET s. + Since m IN s /\ x <= m by MAX_SET_DEF + and m IN s ==> m <= x by implication + Hence x = m. +*) +val MAX_SET_TEST = store_thm( + "MAX_SET_TEST", + ``!s. FINITE s /\ s <> {} ==> !x. x IN s /\ (!y. y IN s ==> y <= x) ==> (x = MAX_SET s)``, + rpt strip_tac >> + qabbrev_tac `m = MAX_SET s` >> + `m IN s /\ x <= m` by rw[MAX_SET_DEF, Abbr`m`] >> + `m <= x` by rw[] >> + decide_tac); + +(* Theorem: s <> {} ==> !x. x IN s /\ (!y. y IN s ==> x <= y) ==> (x = MIN_SET s) *) +(* Proof: + Let m = MIN_SET s. + Since m IN s /\ m <= x by MIN_SET_LEM + and m IN s ==> x <= m by implication + Hence x = m. +*) +val MIN_SET_TEST = store_thm( + "MIN_SET_TEST", + ``!s. s <> {} ==> !x. x IN s /\ (!y. y IN s ==> x <= y) ==> (x = MIN_SET s)``, + rpt strip_tac >> + qabbrev_tac `m = MIN_SET s` >> + `m IN s /\ m <= x` by rw[MIN_SET_LEM, Abbr`m`] >> + `x <= m` by rw[] >> + decide_tac); + +(* Theorem: FINITE s /\ s <> {} ==> !x. x IN s ==> ((MAX_SET s = x) <=> (!y. y IN s ==> y <= x)) *) +(* Proof: + Let m = MAX_SET s. + If part: y IN s ==> y <= m, true by MAX_SET_DEF + Only-if part: !y. y IN s ==> y <= x ==> m = x + Note m IN s /\ x <= m by MAX_SET_DEF + and m IN s ==> m <= x by implication + Hence x = m. +*) +Theorem MAX_SET_TEST_IFF: + !s. FINITE s /\ s <> {} ==> + !x. x IN s ==> ((MAX_SET s = x) <=> (!y. y IN s ==> y <= x)) +Proof + rpt strip_tac >> + qabbrev_tac `m = MAX_SET s` >> + rw[EQ_IMP_THM] >- rw[MAX_SET_DEF, Abbr‘m’] >> + `m IN s /\ x <= m` by rw[MAX_SET_DEF, Abbr`m`] >> + `m <= x` by rw[] >> + decide_tac +QED + +(* Theorem: s <> {} ==> !x. x IN s ==> ((MIN_SET s = x) <=> (!y. y IN s ==> x <= y)) *) +(* Proof: + Let m = MIN_SET s. + If part: y IN s ==> m <= y, true by MIN_SET_LEM + Only-if part: !y. y IN s ==> x <= y ==> m = x + Note m IN s /\ m <= x by MIN_SET_LEM + and m IN s ==> x <= m by implication + Hence x = m. +*) +Theorem MIN_SET_TEST_IFF: + !s. s <> {} ==> !x. x IN s ==> ((MIN_SET s = x) <=> (!y. y IN s ==> x <= y)) +Proof + rpt strip_tac >> + qabbrev_tac `m = MIN_SET s` >> + rw[EQ_IMP_THM] >- rw[MIN_SET_LEM, Abbr‘m’] >> + `m IN s /\ m <= x` by rw[MIN_SET_LEM, Abbr`m`] >> + `x <= m` by rw[] >> decide_tac +QED + +(* Theorem: MAX_SET {} = 0 *) +(* Proof: by MAX_SET_REWRITES *) +val MAX_SET_EMPTY = save_thm("MAX_SET_EMPTY", MAX_SET_REWRITES |> CONJUNCT1); +(* val MAX_SET_EMPTY = |- MAX_SET {} = 0: thm *) + +(* Theorem: MAX_SET {e} = e *) +(* Proof: by MAX_SET_REWRITES *) +val MAX_SET_SING = save_thm("MAX_SET_SING", MAX_SET_REWRITES |> CONJUNCT2 |> GEN_ALL); +(* val MAX_SET_SING = |- !e. MAX_SET {e} = e: thm *) + +(* Theorem: FINITE s /\ s <> {} ==> MAX_SET s IN s *) +(* Proof: by MAX_SET_DEF *) +val MAX_SET_IN_SET = store_thm( + "MAX_SET_IN_SET", + ``!s. FINITE s /\ s <> {} ==> MAX_SET s IN s``, + rw[MAX_SET_DEF]); + +(* Theorem: FINITE s ==> !x. x IN s ==> x <= MAX_SET s *) +(* Proof: by in_max_set *) +val MAX_SET_PROPERTY = save_thm("MAX_SET_PROPERTY", in_max_set); +(* val MAX_SET_PROPERTY = |- !s. FINITE s ==> !x. x IN s ==> x <= MAX_SET s: thm *) + +(* Note: MIN_SET {} is undefined. *) + +(* Theorem: MIN_SET {e} = e *) +(* Proof: by MIN_SET_THM *) +val MIN_SET_SING = save_thm("MIN_SET_SING", MIN_SET_THM |> CONJUNCT1); +(* val MIN_SET_SING = |- !e. MIN_SET {e} = e: thm *) + +(* Theorem: s <> {} ==> MIN_SET s IN s *) +(* Proof: by MIN_SET_LEM *) +val MIN_SET_IN_SET = save_thm("MIN_SET_IN_SET", + MIN_SET_LEM |> SPEC_ALL |> UNDISCH |> CONJUNCT1 |> DISCH_ALL |> GEN_ALL); +(* val MIN_SET_IN_SET = |- !s. s <> {} ==> MIN_SET s IN s: thm *) + +(* Theorem: s <> {} ==> !x. x IN s ==> MIN_SET s <= x *) +(* Proof: by MIN_SET_LEM *) +val MIN_SET_PROPERTY = save_thm("MIN_SET_PROPERTY", + MIN_SET_LEM |> SPEC_ALL |> UNDISCH |> CONJUNCT2 |> DISCH_ALL |> GEN_ALL); +(* val MIN_SET_PROPERTY =|- !s. s <> {} ==> !x. x IN s ==> MIN_SET s <= x: thm *) + +(* Theorem: FINITE s ==> ((MAX_SET s = 0) <=> (s = {}) \/ (s = {0})) *) +(* Proof: + If part: MAX_SET s = 0 ==> (s = {}) \/ (s = {0}) + By contradiction, suppose s <> {} /\ s <> {0}. + Then ?x. x IN s /\ x <> 0 by ONE_ELEMENT_SING + Thus x <= MAX_SET s by in_max_set + so MAX_SET s <> 0 by x <> 0 + This contradicts MAX_SET s = 0. + Only-if part: (s = {}) \/ (s = {0}) ==> MAX_SET s = 0 + If s = {}, MAX_SET s = 0 by MAX_SET_EMPTY + If s = {0}, MAX_SET s = 0 by MAX_SET_SING +*) +val MAX_SET_EQ_0 = store_thm( + "MAX_SET_EQ_0", + ``!s. FINITE s ==> ((MAX_SET s = 0) <=> (s = {}) \/ (s = {0}))``, + (rw[EQ_IMP_THM] >> simp[]) >> + CCONTR_TAC >> + `s <> {} /\ s <> {0}` by metis_tac[] >> + `?x. x IN s /\ x <> 0` by metis_tac[ONE_ELEMENT_SING] >> + `x <= MAX_SET s` by rw[in_max_set] >> + decide_tac); + +(* Theorem: s <> {} ==> ((MIN_SET s = 0) <=> 0 IN s) *) +(* Proof: + If part: MIN_SET s = 0 ==> 0 IN s + This is true by MIN_SET_IN_SET. + Only-if part: 0 IN s ==> MIN_SET s = 0 + Note MIN_SET s <= 0 by MIN_SET_LEM, 0 IN s + Thus MIN_SET s = 0 by arithmetic +*) +val MIN_SET_EQ_0 = store_thm( + "MIN_SET_EQ_0", + ``!s. s <> {} ==> ((MIN_SET s = 0) <=> 0 IN s)``, + rw[EQ_IMP_THM] >- + metis_tac[MIN_SET_IN_SET] >> + `MIN_SET s <= 0` by rw[MIN_SET_LEM] >> + decide_tac); + (*---------------------------------------------------------------------------*) (* POW s is the powerset of s *) (*---------------------------------------------------------------------------*) diff --git a/src/real/Holmakefile b/src/real/Holmakefile index 6a8082b7aa..e066f92038 100644 --- a/src/real/Holmakefile +++ b/src/real/Holmakefile @@ -1,5 +1,5 @@ INCLUDES = ../integer ../hol88 ../pred_set/src/more_theories \ - ../res_quan/src ../rational + ../res_quan/src ../rational ../algebra TARGETS = $(subst prove_real_assumsTheory.uo,,$(DEFAULT_TARGETS)) diff --git a/examples/algebra/monoid/monoidRealScript.sml b/src/real/real_algebraScript.sml similarity index 81% rename from examples/algebra/monoid/monoidRealScript.sml rename to src/real/real_algebraScript.sml index 65a7e5c6dc..7f97611f28 100644 --- a/examples/algebra/monoid/monoidRealScript.sml +++ b/src/real/real_algebraScript.sml @@ -1,9 +1,12 @@ (* ------------------------------------------------------------------------- *) (* The monoids of addition and multiplication of real numbers. *) (* ------------------------------------------------------------------------- *) -open HolKernel boolLib bossLib Parse monoidTheory realTheory -val _ = new_theory"monoidReal"; +open HolKernel boolLib bossLib Parse; + +open realTheory monoidTheory; + +val _ = new_theory "real_algebra"; Definition real_add_monoid_def: real_add_monoid : real monoid = @@ -11,8 +14,8 @@ Definition real_add_monoid_def: End Theorem real_add_monoid_simps[simp]: - real_add_monoid.carrier = UNIV ∧ - real_add_monoid.op = (real_add) ∧ + real_add_monoid.carrier = UNIV /\ + real_add_monoid.op = (real_add) /\ real_add_monoid.id = 0 Proof rw[real_add_monoid_def] @@ -34,8 +37,8 @@ Definition real_mul_monoid_def: End Theorem real_mul_monoid_simps[simp]: - real_mul_monoid.carrier = UNIV ∧ - real_mul_monoid.op = (real_mul) ∧ + real_mul_monoid.carrier = UNIV /\ + real_mul_monoid.op = (real_mul) /\ real_mul_monoid.id = 1 Proof rw[real_mul_monoid_def] From d9c43039ffbc18492acec03e554caabc07f737c4 Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Mon, 22 Apr 2024 03:45:18 +1000 Subject: [PATCH 2/8] Changes of examples/{AKS|fermat|simple_complexity} after src/algebra/monoidThoery --- examples/AKS/compute/Holmakefile | 4 +- examples/AKS/compute/computeAKSScript.sml | 24 +- examples/AKS/compute/computeBasicScript.sml | 30 +- examples/AKS/compute/computeOrderScript.sml | 32 +- examples/AKS/compute/computeParamScript.sml | 26 +- examples/AKS/compute/computePolyScript.sml | 29 +- examples/AKS/compute/computeRingScript.sml | 33 +- examples/AKS/machine/Holmakefile | 4 +- examples/AKS/machine/countAKSScript.sml | 27 +- examples/AKS/machine/countBasicScript.sml | 26 +- examples/AKS/machine/countMacroScript.sml | 31 +- examples/AKS/machine/countModuloScript.sml | 34 +- examples/AKS/machine/countMonadScript.sml | 11 +- examples/AKS/machine/countOrderScript.sml | 28 +- examples/AKS/machine/countParamScript.sml | 27 +- examples/AKS/machine/countPolyScript.sml | 30 +- examples/AKS/machine/countPowerScript.sml | 33 +- examples/AKS/machine/countPrimeScript.sml | 29 +- examples/AKS/theories/AKScleanScript.sml | 20 +- examples/AKS/theories/AKSimprovedScript.sml | 25 +- examples/AKS/theories/AKSintroScript.sml | 31 +- examples/AKS/theories/AKSmapsScript.sml | 20 +- examples/AKS/theories/AKSrevisedScript.sml | 23 +- examples/AKS/theories/AKSsetsScript.sml | 26 +- examples/AKS/theories/AKSshiftScript.sml | 41 +- examples/AKS/theories/AKStheoremScript.sml | 23 +- examples/AKS/theories/Holmakefile | 4 +- examples/fermat/count/Holmakefile | 4 +- examples/fermat/count/combinatoricsScript.sml | 2809 ----------------- examples/fermat/count/helperCountScript.sml | 531 ---- examples/fermat/count/mapCountScript.sml | 20 +- examples/fermat/count/permutationScript.sml | 17 +- examples/fermat/count/symmetryScript.sml | 16 +- examples/fermat/little/FLTactionScript.sml | 15 +- examples/fermat/little/FLTbinomialScript.sml | 69 +- examples/fermat/little/FLTeulerScript.sml | 64 +- .../fermat/little/FLTfixedpointScript.sml | 9 +- examples/fermat/little/FLTgroupScript.sml | 43 +- examples/fermat/little/FLTnecklaceScript.sml | 14 +- examples/fermat/little/FLTnumberScript.sml | 14 +- examples/fermat/little/FLTpetersenScript.sml | 11 +- examples/fermat/little/Holmakefile | 4 +- examples/fermat/little/cycleScript.sml | 9 +- examples/fermat/little/necklaceScript.sml | 1033 ------ examples/fermat/little/patternScript.sml | 10 +- examples/fermat/twosq/Holmakefile | 4 +- examples/fermat/twosq/cornerScript.sml | 16 +- examples/fermat/twosq/helperTwosqScript.sml | 17 +- examples/fermat/twosq/hoppingScript.sml | 23 +- .../fermat/twosq/involuteActionScript.sml | 10 +- examples/fermat/twosq/involuteFixScript.sml | 9 +- examples/fermat/twosq/involuteScript.sml | 13 +- .../fermat/twosq/iterateComposeScript.sml | 15 +- .../fermat/twosq/iterateComputeScript.sml | 28 +- examples/fermat/twosq/iterationScript.sml | 13 +- examples/fermat/twosq/quarityScript.sml | 31 +- examples/fermat/twosq/twoSquaresScript.sml | 26 +- examples/fermat/twosq/windmillScript.sml | 19 +- examples/simple_complexity/lib/Holmakefile | 2 +- .../simple_complexity/lib/bitsizeScript.sml | 18 +- .../lib/complexityScript.sml | 19 +- examples/simple_complexity/loop/Holmakefile | 2 +- .../loop/loopDecreaseScript.sml | 17 +- .../loop/loopDivideScript.sml | 22 +- .../loop/loopIncreaseScript.sml | 16 +- .../simple_complexity/loop/loopListScript.sml | 13 +- .../loop/loopMultiplyScript.sml | 17 +- .../simple_complexity/loop/loopScript.sml | 15 +- 68 files changed, 273 insertions(+), 5455 deletions(-) delete mode 100644 examples/fermat/count/combinatoricsScript.sml delete mode 100644 examples/fermat/count/helperCountScript.sml delete mode 100644 examples/fermat/little/necklaceScript.sml diff --git a/examples/AKS/compute/Holmakefile b/examples/AKS/compute/Holmakefile index 58e20303cf..5fd92ae750 100644 --- a/examples/AKS/compute/Holmakefile +++ b/examples/AKS/compute/Holmakefile @@ -1,4 +1,2 @@ -PRE_INCLUDES = ../../algebra/ring - -ALGEBRA_INCLUDES = lib monoid group field polynomial finitefield +ALGEBRA_INCLUDES = group ring field polynomial finitefield INCLUDES = $(patsubst %,../../algebra/%,$(ALGEBRA_INCLUDES)) diff --git a/examples/AKS/compute/computeAKSScript.sml b/examples/AKS/compute/computeAKSScript.sml index 529d5de288..3d51a99bd8 100644 --- a/examples/AKS/compute/computeAKSScript.sml +++ b/examples/AKS/compute/computeAKSScript.sml @@ -12,11 +12,12 @@ val _ = new_theory "computeAKS"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory primeTheory; (* Get dependent theories local *) (* val _ = load "computeParamTheory"; *) @@ -29,27 +30,8 @@ open computeRingTheory computePolyTheory; open polyWeakTheory polyRingTheory; open polyMonicTheory polyDivisionTheory; -(* -open helperFunctionTheory; (* for SQRT_LE *) -open logPowerTheory; (* for ulog *) -open ringTheory ringInstancesTheory; (* for ZN_coprime_order_alt *) -open monoidOrderTheory; -*) open ringInstancesTheory; (* for ZN_ring *) -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; -open logPowerTheory; - - (* ------------------------------------------------------------------------- *) (* AKS Computations Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/compute/computeBasicScript.sml b/examples/AKS/compute/computeBasicScript.sml index fbc1e860b3..193d942810 100644 --- a/examples/AKS/compute/computeBasicScript.sml +++ b/examples/AKS/compute/computeBasicScript.sml @@ -12,38 +12,18 @@ val _ = new_theory "computeBasic"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -(* (* val _ = load "helperListTheory"; -- in polyRingTheory *) *) -(* val _ = load "logPowerTheory"; *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; -open pred_setTheory listTheory arithmeticTheory; - -open logPowerTheory; (* for LOG2, SQRT, and Perfect Power, Power Free *) -open logrootTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* val _ = load "GaussTheory"; *) -open EulerTheory; -open GaussTheory; +open pred_setTheory listTheory arithmeticTheory logrootTheory dividesTheory + gcdTheory gcdsetTheory numberTheory combinatoricsTheory primeTheory; (* val _ = load "whileTheory"; *) open whileTheory; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Basic Computations Documentation *) diff --git a/examples/AKS/compute/computeOrderScript.sml b/examples/AKS/compute/computeOrderScript.sml index 74b17b5396..0f464af2f2 100644 --- a/examples/AKS/compute/computeOrderScript.sml +++ b/examples/AKS/compute/computeOrderScript.sml @@ -12,46 +12,18 @@ val _ = new_theory "computeOrder"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* open dependent theories *) -(* val _ = load "fieldInstancesTheory"; *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory logrootTheory primeTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperFunctionTheory"; -- in ringTheory *) *) -(* (* val _ = load "helperListTheory"; -- in polyRingTheory *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; -open pred_setTheory listTheory arithmeticTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* val _ = load "fieldInstancesTheory"; *) -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; open groupOrderTheory; -open monoidOrderTheory; -(* val _ = load "GaussTheory"; *) -open EulerTheory; -open GaussTheory; - -(* val _ = load "computeBasicTheory"; *) open computeBasicTheory; -open logrootTheory logPowerTheory; - (* ------------------------------------------------------------------------- *) (* Order Computations Documentation *) diff --git a/examples/AKS/compute/computeParamScript.sml b/examples/AKS/compute/computeParamScript.sml index 0db2386213..6ed5e66079 100644 --- a/examples/AKS/compute/computeParamScript.sml +++ b/examples/AKS/compute/computeParamScript.sml @@ -14,26 +14,17 @@ val _ = new_theory "computeParam"; open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory logrootTheory + dividesTheory gcdTheory numberTheory combinatoricsTheory primeTheory; (* Get dependent theories local *) open computeOrderTheory; -open helperFunctionTheory; (* for SQRT_LE *) -open logPowerTheory; (* for ulog *) open ringTheory ringInstancesTheory; (* for ZN_coprime_order_alt *) -open monoidOrderTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -open GaussTheory; (* for phi_pos *) -open EulerTheory; (* for residue_def *) -open triangleTheory; (* for list_lcm_pos *) +open monoidTheory; +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* AKS Parameter Documentation *) @@ -67,7 +58,6 @@ open triangleTheory; (* for list_lcm_pos *) coprime_candidates_ne_1 |- !n m. 1 < m ==> 1 NOTIN coprime_candidates n m ZN_order_good_enough |- !n m. 1 < n /\ 1 < m ==> ?k. 1 < k /\ coprime k n /\ m <= ordz k n - Smallest Candidate least_prime_candidates_property |- !n m. 1 < n /\ 0 < m ==> !h. prime h /\ coprime h n /\ h < MIN_SET (prime_candidates n m) ==> @@ -278,10 +268,6 @@ open triangleTheory; (* for list_lcm_pos *) param_good_range |- !n k. (param n = good k) ==> 1 < n /\ 1 < k /\ k < n *) -(* ------------------------------------------------------------------------- *) -(* Helper Theorems *) -(* ------------------------------------------------------------------------- *) - (* ------------------------------------------------------------------------- *) (* AKS Parameter *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/compute/computePolyScript.sml b/examples/AKS/compute/computePolyScript.sml index 85e912fb15..29d2fa8f8b 100644 --- a/examples/AKS/compute/computePolyScript.sml +++ b/examples/AKS/compute/computePolyScript.sml @@ -12,27 +12,11 @@ val _ = new_theory "computePoly"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* open dependent theories *) -(* val _ = load "fieldInstancesTheory"; *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open pred_setTheory listTheory arithmeticTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; +open pred_setTheory listTheory rich_listTheory arithmeticTheory numberTheory + combinatoricsTheory dividesTheory gcdTheory logrootTheory whileTheory; (* val _ = load "polyFieldModuloTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; @@ -46,13 +30,10 @@ open ringBinomialTheory; (* val _ = load "computeOrderTheory"; *) open computeBasicTheory computeOrderTheory; -open logrootTheory logPowerTheory; - -(* val _ = load "whileTheory"; *) -open whileTheory; - -open rich_listTheory; (* for FRONT and LAST *) +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Polynomial Computations Documentation *) diff --git a/examples/AKS/compute/computeRingScript.sml b/examples/AKS/compute/computeRingScript.sml index 38c6e73ad6..55dca4fd1f 100644 --- a/examples/AKS/compute/computeRingScript.sml +++ b/examples/AKS/compute/computeRingScript.sml @@ -12,34 +12,11 @@ val _ = new_theory "computeRing"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* open dependent theories *) -(* val _ = load "fieldInstancesTheory"; *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open pred_setTheory listTheory arithmeticTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* -polyBinomial.hol polyEval.hol polyGCD.hol polyRing.hol -polyDerivative.hol polyField.hol polyIrreducible.hol polyRoot.hol -polyDivides.hol polyFieldDivision.hol polyMonic.hol polyWeak.hol -polyDivision.hol polyFieldModulo.hol polyProduct.hol polynomial.hol -*) +open pred_setTheory listTheory rich_listTheory arithmeticTheory numberTheory + combinatoricsTheory dividesTheory gcdTheory logrootTheory; (* val _ = load "polyFieldModuloTheory"; *) open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; @@ -53,11 +30,11 @@ open ringBinomialTheory; (* val _ = load "computePolyTheory"; *) open computeBasicTheory computeOrderTheory computePolyTheory; -open logrootTheory logPowerTheory; - open ringInstancesTheory; -open rich_listTheory; (* for FRONT and LAST *) +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Modulo Polynomial Computations in (ZN n) Ring Documentation *) diff --git a/examples/AKS/machine/Holmakefile b/examples/AKS/machine/Holmakefile index 869daf0102..8ad1f406f8 100644 --- a/examples/AKS/machine/Holmakefile +++ b/examples/AKS/machine/Holmakefile @@ -1,6 +1,4 @@ -PRE_INCLUDES = ../../algebra/ring - -ALGEBRA_INCLUDES = lib monoid group field polynomial finitefield +ALGEBRA_INCLUDES = group ring field polynomial finitefield INCLUDES = $(patsubst %,../../algebra/%,$(ALGEBRA_INCLUDES)) \ ../../simple_complexity/lib ../../simple_complexity/loop ../compute \ $(HOLDIR)/src/monad/more_monads diff --git a/examples/AKS/machine/countAKSScript.sml b/examples/AKS/machine/countAKSScript.sml index cca6b11a1e..93ae6be990 100644 --- a/examples/AKS/machine/countAKSScript.sml +++ b/examples/AKS/machine/countAKSScript.sml @@ -12,12 +12,12 @@ val _ = new_theory "countAKS"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + rich_listTheory listRangeTheory numberTheory combinatoricsTheory + logrootTheory pairTheory optionTheory primeTheory; (* Get dependent theories local *) (* val _ = load "countParamTheory"; *) @@ -35,29 +35,11 @@ open bitsizeTheory complexityTheory; open loopIncreaseTheory loopDecreaseTheory; open loopDivideTheory loopListTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; -open rich_listTheory listRangeTheory; - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - -(* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; (* val _ = load "ringInstancesTheory"; *) open ringInstancesTheory; (* for ZN order *) -(* val _ = load "computeAKSTheory"; *) open computeParamTheory computeAKSTheory; open computeBasicTheory; (* for power_free_check_eqn *) @@ -70,6 +52,9 @@ open polynomialTheory polyWeakTheory; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* AKS computations in monadic style Documentation *) diff --git a/examples/AKS/machine/countBasicScript.sml b/examples/AKS/machine/countBasicScript.sml index e508654b7b..70c41b43f6 100644 --- a/examples/AKS/machine/countBasicScript.sml +++ b/examples/AKS/machine/countBasicScript.sml @@ -12,15 +12,13 @@ val _ = new_theory "countBasic"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory logrootTheory pairTheory optionTheory + listRangeTheory primeTheory; -(* Get dependent theories local *) -(* val _ = load "countMacroTheory"; *) open countMonadTheory countMacroTheory; open bitsizeTheory complexityTheory; @@ -28,27 +26,15 @@ open loopIncreaseTheory loopDecreaseTheory; open loopDivideTheory; open loopMultiplyTheory; (* for loop2_mul_rise_steps_le *) -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open pred_setTheory listTheory arithmeticTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* val _ = load "logPowerTheory"; *) -open logrootTheory logPowerTheory; - (* val _ = load "monadsyntax"; *) open monadsyntax; -open pairTheory optionTheory; -open listRangeTheory; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Basic Computations with Count Monad Documentation *) diff --git a/examples/AKS/machine/countMacroScript.sml b/examples/AKS/machine/countMacroScript.sml index a1979d3218..caba76d540 100644 --- a/examples/AKS/machine/countMacroScript.sml +++ b/examples/AKS/machine/countMacroScript.sml @@ -12,48 +12,29 @@ val _ = new_theory "countMacro"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory rich_listTheory arithmeticTheory dividesTheory + gcdTheory numberTheory combinatoricsTheory pairTheory optionTheory + listRangeTheory primeTheory; -(* Get dependent theories local *) -(* val _ = load "complexityTheory"; *) open bitsizeTheory complexityTheory; -(* val _ = load "loopIncreaseTheory"; *) -(* val _ = load "loopDecreaseTheory"; *) -(* val _ = load "loopDivideTheory"; *) -(* val _ = load "loopMultiplyTheory"; *) -(* val _ = load "loopListTheory"; *) -(* pre-load and open here for other count scripts. *) open loopIncreaseTheory loopDecreaseTheory; open loopDivideTheory loopMultiplyTheory loopListTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open pred_setTheory listTheory arithmeticTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* val _ = load "countMonadTheory"; *) open countMonadTheory; (* val _ = load "monadsyntax"; *) open monadsyntax; -open pairTheory optionTheory; -open listRangeTheory; -open logPowerTheory; (* for halves *) val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Macros of Count Monad Documentation *) diff --git a/examples/AKS/machine/countModuloScript.sml b/examples/AKS/machine/countModuloScript.sml index 6703b7ddb0..63b2b1517d 100644 --- a/examples/AKS/machine/countModuloScript.sml +++ b/examples/AKS/machine/countModuloScript.sml @@ -12,48 +12,30 @@ val _ = new_theory "countModulo"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + logrootTheory numberTheory combinatoricsTheory pairTheory optionTheory + listRangeTheory; -(* Get dependent theories local *) -(* val _ = load "countMacroTheory"; *) open countMonadTheory countMacroTheory; open bitsizeTheory complexityTheory; open loopIncreaseTheory loopDecreaseTheory; open loopDivideTheory loopMultiplyTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - -(* -(* val _ = load "computeBasicTheory"; *) -open computeBasicTheory; (* for exp_mod_eqn *) -*) - (* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; -open listRangeTheory; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Modulo Computations with Count Monad Documentation *) diff --git a/examples/AKS/machine/countMonadScript.sml b/examples/AKS/machine/countMonadScript.sml index 10f5e9dcde..56c63cc56c 100644 --- a/examples/AKS/machine/countMonadScript.sml +++ b/examples/AKS/machine/countMonadScript.sml @@ -12,31 +12,22 @@ val _ = new_theory "countMonad"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) - -(* Get dependent theories local *) - -(* Get dependent theories in lib *) -open pred_setTheory listTheory arithmeticTheory; +open pred_setTheory listTheory arithmeticTheory pairTheory optionTheory; (* val _ = load "errorStateMonadTheory"; *) open errorStateMonadTheory; (* val _ = load "monadsyntax"; *) open monadsyntax; -open pairTheory optionTheory; val _ = set_grammar_ancestry ["pair", "option", "arithmetic"]; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "errorState"; - (* ------------------------------------------------------------------------- *) (* Count Monad Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/machine/countOrderScript.sml b/examples/AKS/machine/countOrderScript.sml index bc22f89360..01a0472001 100644 --- a/examples/AKS/machine/countOrderScript.sml +++ b/examples/AKS/machine/countOrderScript.sml @@ -12,47 +12,31 @@ val _ = new_theory "countOrder"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory logrootTheory pairTheory optionTheory + listRangeTheory; -(* Get dependent theories local *) -(* val _ = load "countModuloTheory"; *) open countMonadTheory countMacroTheory; open countModuloTheory; open bitsizeTheory complexityTheory; open loopIncreaseTheory loopDecreaseTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - -(* val _ = load "computeOrderTheory"; *) open computeOrderTheory; (* for ordz_seek and ordz_simple *) open ringInstancesTheory; (* for ZN_order_mod_1, ZN_order_mod *) (* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; -open listRangeTheory; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Order Computations with Count Monad Documentation *) diff --git a/examples/AKS/machine/countParamScript.sml b/examples/AKS/machine/countParamScript.sml index ecb2adef39..b7cd0e0af8 100644 --- a/examples/AKS/machine/countParamScript.sml +++ b/examples/AKS/machine/countParamScript.sml @@ -12,15 +12,13 @@ val _ = new_theory "countParam"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + logrootTheory pairTheory optionTheory listRangeTheory numberTheory + combinatoricsTheory primeTheory; -(* Get dependent theories local *) -(* val _ = load "countPowerTheory"; *) open countMonadTheory countMacroTheory; open countBasicTheory countPowerTheory; @@ -30,27 +28,11 @@ open countOrderTheory; open bitsizeTheory complexityTheory; open loopIncreaseTheory loopDecreaseTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - (* val _ = load "computeParamTheory"; *) open computeParamTheory; (* for param_search_result *) (* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; -open listRangeTheory; (* val _ = load "ringInstancesTheory"; *) open ringInstancesTheory; (* for ZN order *) @@ -58,6 +40,9 @@ open ringInstancesTheory; (* for ZN order *) val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* AKS parameter with Count Monad Documentation *) diff --git a/examples/AKS/machine/countPolyScript.sml b/examples/AKS/machine/countPolyScript.sml index 6073439434..ec1c1dca8f 100644 --- a/examples/AKS/machine/countPolyScript.sml +++ b/examples/AKS/machine/countPolyScript.sml @@ -12,15 +12,13 @@ val _ = new_theory "countPoly"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + rich_listTheory listRangeTheory logrootTheory numberTheory + combinatoricsTheory pairTheory optionTheory primeTheory; -(* Get dependent theories local *) -(* val _ = load "countModuloTheory"; *) open countMonadTheory countMacroTheory; open countModuloTheory; @@ -28,24 +26,8 @@ open bitsizeTheory complexityTheory; open loopIncreaseTheory loopDecreaseTheory; open loopDivideTheory loopListTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; -open rich_listTheory listRangeTheory; - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - (* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; (* val _ = load "ringInstancesTheory"; *) open ringInstancesTheory; (* for ZN order *) @@ -59,6 +41,12 @@ open polynomialTheory polyWeakTheory; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +(* Overload sublist by infix operator *) +val _ = temp_overload_on ("<=", ``sublist``); + +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Polynomial computations in monadic style Documentation *) diff --git a/examples/AKS/machine/countPowerScript.sml b/examples/AKS/machine/countPowerScript.sml index 7264ca42e1..31cc594edb 100644 --- a/examples/AKS/machine/countPowerScript.sml +++ b/examples/AKS/machine/countPowerScript.sml @@ -12,15 +12,13 @@ val _ = new_theory "countPower"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory logrootTheory pairTheory optionTheory + listRangeTheory primeTheory; -(* Get dependent theories local *) -(* val _ = load "countBasicTheory"; *) open countMonadTheory countMacroTheory; open countBasicTheory; @@ -28,31 +26,16 @@ open bitsizeTheory complexityTheory; open loopIncreaseTheory loopDecreaseTheory; open loopDivideTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - -(* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; -open listRangeTheory; - -(* (* val _ = load "sublistTheory"; -- from recurrence theory *) *) -open sublistTheory; (* for MAP_SUBLIST, SUM_SUBLIST, listRangeINC_sublist *) val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Power Computations with Count Monad Documentation *) diff --git a/examples/AKS/machine/countPrimeScript.sml b/examples/AKS/machine/countPrimeScript.sml index 57d6fb9745..9f27f0a09c 100644 --- a/examples/AKS/machine/countPrimeScript.sml +++ b/examples/AKS/machine/countPrimeScript.sml @@ -12,46 +12,27 @@ val _ = new_theory "countPrime"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +open pred_setTheory listTheory arithmeticTheory dividesTheory numberTheory + combinatoricsTheory logrootTheory pairTheory optionTheory primeTheory; -(* Get dependent theories local *) -(* val _ = load "countPowerTheory"; *) open countMonadTheory countMacroTheory; open countBasicTheory countPowerTheory; open bitsizeTheory complexityTheory; open loopIncreaseTheory; -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open pred_setTheory listTheory arithmeticTheory; -open dividesTheory; -(* open rich_listTheory listRangeTheory; *) - -(* (* val _ = load "logPowerTheory"; *) *) -open logrootTheory logPowerTheory; - -(* val _ = load "primesTheory"; *) -open primesTheory; - (* (* val _ = load "monadsyntax"; *) *) open monadsyntax; -open pairTheory optionTheory; val _ = monadsyntax.enable_monadsyntax(); val _ = monadsyntax.enable_monad "Count"; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Primality Test in monadic style Documentation *) diff --git a/examples/AKS/theories/AKScleanScript.sml b/examples/AKS/theories/AKScleanScript.sml index 144e649d65..0053ad642a 100644 --- a/examples/AKS/theories/AKScleanScript.sml +++ b/examples/AKS/theories/AKScleanScript.sml @@ -14,6 +14,10 @@ val _ = new_theory "AKSclean"; open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory logrootTheory + numberTheory combinatoricsTheory dividesTheory gcdTheory primeTheory; + (* Get dependent theories local *) open AKSimprovedTheory; open AKSrevisedTheory; @@ -25,16 +29,6 @@ open AKSshiftTheory; open countAKSTheory; (* for aks0_eq_aks *) -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -open dividesTheory gcdTheory; -open logPowerTheory; - open fieldInstancesTheory; open ringInstancesTheory; open groupInstancesTheory; (* for Estar_group *) @@ -53,9 +47,9 @@ open computeRingTheory; open computeParamTheory; open computeAKSTheory; -open GaussTheory; (* for phi_le *) - - +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* AKS Clean Presentation Documentation *) diff --git a/examples/AKS/theories/AKSimprovedScript.sml b/examples/AKS/theories/AKSimprovedScript.sml index 714b0dca08..3f06990199 100644 --- a/examples/AKS/theories/AKSimprovedScript.sml +++ b/examples/AKS/theories/AKSimprovedScript.sml @@ -14,6 +14,10 @@ val _ = new_theory "AKSimproved"; open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory logrootTheory + numberTheory combinatoricsTheory dividesTheory gcdTheory primeTheory; + (* Get dependent theories local *) open AKSrevisedTheory; open AKStheoremTheory; @@ -43,31 +47,16 @@ open monoidTheory groupTheory ringTheory fieldTheory; open subgroupTheory; open groupOrderTheory; -open monoidOrderTheory; open fieldMapTheory; open ringUnitTheory; -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -open dividesTheory gcdTheory; - -open triangleTheory; -open binomialTheory; - open ringBinomialTheory; open ringDividesTheory; -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; open groupOrderTheory; -open monoidOrderTheory; open groupCyclicTheory; @@ -79,7 +68,6 @@ open fieldOrderTheory; open fieldProductTheory; -open logPowerTheory; open computeBasicTheory; open computeOrderTheory; open computePolyTheory; @@ -99,11 +87,6 @@ open ffConjugateTheory; open ffMasterTheory; open ffMinimalTheory; -(* (* val _ = load "GaussTheory"; *) *) -open EulerTheory; -open GaussTheory; - - (* ------------------------------------------------------------------------- *) (* AKS Bounds Improvement Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/theories/AKSintroScript.sml b/examples/AKS/theories/AKSintroScript.sml index 2348e58740..13a63cf9c6 100644 --- a/examples/AKS/theories/AKSintroScript.sml +++ b/examples/AKS/theories/AKSintroScript.sml @@ -7,12 +7,15 @@ (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory listRangeTheory dividesTheory + gcdTheory numberTheory combinatoricsTheory; + (* declare new theory at start *) val _ = new_theory "AKSintro"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; @@ -25,11 +28,6 @@ open ffUnityTheory; open ffConjugateTheory; open ffExistTheory; -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyDivisionTheory"; *) *) -(* (* val _ = load "polyMapTheory"; *) *) open polynomialTheory polyWeakTheory polyRingTheory polyDivisionTheory; open polyBinomialTheory polyEvalTheory; @@ -44,19 +42,11 @@ open polyRingModuloTheory; open polyMapTheory; open polyIrreducibleTheory; -(* Get dependent theories local *) -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "ringUnitTheory"; *) *) -(* (* val _ = load "integralDomainTheory"; *) *) -(* (* val _ = load "fieldTheory"; *) *) open monoidTheory groupTheory ringTheory ringUnitTheory; open fieldTheory fieldMapTheory; (* (* val _ = load "ringBinomialTheory"; *) *) open ringBinomialTheory; -open binomialTheory; (* (* val _ = load "ringInstancesTheory"; *) *) open ringInstancesTheory; @@ -64,19 +54,6 @@ open ringInstancesTheory; (* val _ = load "computeRingTheory"; *) open computeRingTheory; (* for overloads on x^, x+^, x^+, x^- *) -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperFunctionTheory; -open helperListTheory; (* for listRangeINC_EVERY *) - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - - (* ------------------------------------------------------------------------- *) (* Introspective Relation for AKS Theorem Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/theories/AKSmapsScript.sml b/examples/AKS/theories/AKSmapsScript.sml index 551a5f1bca..3c0a3ddffd 100644 --- a/examples/AKS/theories/AKSmapsScript.sml +++ b/examples/AKS/theories/AKSmapsScript.sml @@ -14,13 +14,14 @@ val _ = new_theory "AKSmaps"; open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory numberTheory + logrootTheory combinatoricsTheory dividesTheory gcdTheory primeTheory; + (* Get dependent theories local *) open AKSsetsTheory; open AKSintroTheory; -(* For SQRT n and LOG2 n *) -open logPowerTheory; - open monoidTheory groupTheory ringTheory ringUnitTheory; open fieldTheory; @@ -46,20 +47,8 @@ open polyIrreducibleTheory; open subgroupTheory; open groupOrderTheory; -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -open dividesTheory gcdTheory; - -open GaussTheory; (* for phi_eq_0 *) - -open binomialTheory; open ringBinomialTheory; -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; @@ -69,7 +58,6 @@ open ffAdvancedTheory; open ffPolyTheory; open ffUnityTheory; - (* ------------------------------------------------------------------------- *) (* Mappings for Introspective Sets Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/theories/AKSrevisedScript.sml b/examples/AKS/theories/AKSrevisedScript.sml index f6cf0b1423..9eee32a6d0 100644 --- a/examples/AKS/theories/AKSrevisedScript.sml +++ b/examples/AKS/theories/AKSrevisedScript.sml @@ -14,13 +14,16 @@ val _ = new_theory "AKSrevised"; open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory numberTheory + combinatoricsTheory dividesTheory gcdTheory primeTheory; + (* Get dependent theories local *) open AKStheoremTheory; open AKSmapsTheory; open AKSsetsTheory; open AKSintroTheory; open AKSshiftTheory; -open logPowerTheory; open computeParamTheory; (* Get polynomial theory of Ring *) @@ -37,31 +40,16 @@ open polyDividesTheory; open monoidTheory groupTheory ringTheory fieldTheory; open subgroupTheory; open groupOrderTheory; -open monoidOrderTheory; open fieldMapTheory; open ringUnitTheory; -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory helperListTheory; -open helperFunctionTheory; - -open dividesTheory gcdTheory; - -open triangleTheory; -open binomialTheory; - open ringBinomialTheory; open ringDividesTheory; -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; open groupOrderTheory; -open monoidOrderTheory; open groupCyclicTheory; @@ -84,9 +72,6 @@ open ffConjugateTheory; open ffMasterTheory; open ffMinimalTheory; -open GaussTheory; - - (* ------------------------------------------------------------------------- *) (* AKS parameter k revised (not required to be prime) Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/theories/AKSsetsScript.sml b/examples/AKS/theories/AKSsetsScript.sml index d78a4f0d23..be9f71e3f4 100644 --- a/examples/AKS/theories/AKSsetsScript.sml +++ b/examples/AKS/theories/AKSsetsScript.sml @@ -12,12 +12,15 @@ val _ = new_theory "AKSsets"; (* ------------------------------------------------------------------------- *) - open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory dividesTheory + gcdTheory gcdsetTheory logrootTheory numberTheory combinatoricsTheory + primeTheory; + (* Get dependent theories local *) open AKSintroTheory; -open logPowerTheory; (* for perfect_power_condition *) open monoidTheory groupTheory ringTheory ringUnitTheory; @@ -38,27 +41,14 @@ open polyRootTheory; open polyProductTheory; open subgroupTheory; -open monoidOrderTheory groupOrderTheory; - -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory helperFunctionTheory; - -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -open binomialTheory; +open groupOrderTheory; open ringBinomialTheory; -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; -open GaussTheory; - - (* ------------------------------------------------------------------------- *) (* Introspective Sets Documentation *) (* ------------------------------------------------------------------------- *) @@ -1095,7 +1085,7 @@ val reduceP_subset_setP = store_thm( Then PM SUBSET {p | poly p /\ ((p = []) \/ deg p < n)} by SUBSET_DEF which is BIJ with {p | weak p /\ (LENGTH p = n)} by weak_poly_poly_bij Since FINITE {p | weak p /\ (LENGTH p = n)} by weak_poly_finite - so FINITE {p | poly p /\ ((p = []) \/ deg p < n)} by FINITE_BIJ_PROPERTY + so FINITE {p | poly p /\ ((p = []) \/ deg p < n)} by FINITE_BIJ Hence FINITE PM by SUBSET_FINITE *) val reduceP_finite = store_thm( @@ -1106,7 +1096,7 @@ val reduceP_finite = store_thm( `PM SUBSET {p | poly p /\ ((p = []) \/ deg p < n)}` by rw[SUBSET_DEF, reduceP_element, setP_element] >> `BIJ chop {p | weak p /\ (LENGTH p = n)} {p | poly p /\ ((p = []) \/ deg p < n)}` by rw[weak_poly_poly_bij] >> `FINITE {p | weak p /\ (LENGTH p = n)}` by rw[weak_poly_finite] >> - `FINITE {p | poly p /\ ((p = []) \/ deg p < n)}` by metis_tac[FINITE_BIJ_PROPERTY] >> + `FINITE {p | poly p /\ ((p = []) \/ deg p < n)}` by metis_tac[FINITE_BIJ] >> metis_tac[SUBSET_FINITE]); (* Theorem: 1 < k ==> |0| IN PM *) diff --git a/examples/AKS/theories/AKSshiftScript.sml b/examples/AKS/theories/AKSshiftScript.sml index 72488bd071..77d3fea338 100644 --- a/examples/AKS/theories/AKSshiftScript.sml +++ b/examples/AKS/theories/AKSshiftScript.sml @@ -12,23 +12,16 @@ val _ = new_theory "AKSshift"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "SatisfySimps"; (* for SatisfySimps.SATISFY_ss *) *) +(* open dependent theories *) +open pred_setTheory listTheory arithmeticTheory dividesTheory gcdTheory + numberTheory combinatoricsTheory; -(* Get dependent theories local *) -(* val _ = load "AKSintroTheory"; *) open AKSintroTheory; open computeRingTheory; (* for overloads on x^, x+^, x^+, x^- *) -(* Get polynomial theory of Ring *) -(* (* val _ = load "polyWeakTheory"; *) *) -(* (* val _ = load "polyRingTheory"; *) *) -(* (* val _ = load "polyDivisionTheory"; *) *) -(* (* val _ = load "polyBinomialTheory"; *) *) -(* (* val _ = load "polyMapTheory"; *) *) open polynomialTheory polyWeakTheory polyRingTheory polyFieldTheory; open polyBinomialTheory polyDivisionTheory polyEvalTheory; @@ -36,46 +29,21 @@ open polyBinomialTheory polyDivisionTheory polyEvalTheory; open polyDividesTheory; open polyMonicTheory; -(* (* val _ = load "polyFieldTheory"; *) *) -(* (* val _ = load "polyFieldDivisionTheory"; *) *) -(* (* val _ = load "polyFieldModuloTheory"; *) *) open polyFieldDivisionTheory; open polyFieldModuloTheory; open polyRingModuloTheory; open polyMapTheory; -(* (* val _ = load "monoidTheory"; *) *) -(* (* val _ = load "groupTheory"; *) *) -(* (* val _ = load "ringTheory"; *) *) -(* (* val _ = load "ringUnitTheory"; *) *) -(* (* val _ = load "integralDomainTheory"; *) *) open monoidTheory groupTheory ringTheory fieldTheory; open subgroupTheory; open groupOrderTheory; -open monoidMapTheory groupMapTheory ringMapTheory; -(* open ringUnitTheory; *) - -(* Get dependent theories in lib *) -(* (* val _ = load "helperNumTheory"; -- in monoidTheory *) *) -(* (* val _ = load "helperSetTheory"; -- in monoidTheory *) *) -open helperNumTheory helperSetTheory helperListTheory; - -(* open dependent theories *) -open pred_setTheory listTheory arithmeticTheory; -(* (* val _ = load "dividesTheory"; -- in helperNumTheory *) *) -(* (* val _ = load "gcdTheory"; -- in helperNumTheory *) *) -open dividesTheory gcdTheory; - -(* (* val _ = load "binomialTheory"; *) *) -open binomialTheory; +open groupMapTheory ringMapTheory; (* (* val _ = load "ringBinomialTheory"; *) *) open ringBinomialTheory; open ringDividesTheory; -(* (* val _ = load "fieldInstancesTheory"; *) *) -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; @@ -86,7 +54,6 @@ open ffAdvancedTheory; open ffPolyTheory; open ffUnityTheory; - (* ------------------------------------------------------------------------- *) (* Introspective Shifting Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/AKS/theories/AKStheoremScript.sml b/examples/AKS/theories/AKStheoremScript.sml index 39c23fdc97..cd444d1053 100644 --- a/examples/AKS/theories/AKStheoremScript.sml +++ b/examples/AKS/theories/AKStheoremScript.sml @@ -14,18 +14,19 @@ val _ = new_theory "AKStheorem"; open jcLib; +(* open dependent theories *) +open prim_recTheory pred_setTheory listTheory arithmeticTheory logrootTheory + dividesTheory gcdTheory numberTheory listRangeTheory combinatoricsTheory + primeTheory; + (* Get dependent theories local *) open AKSmapsTheory; open AKSsetsTheory; open AKSintroTheory; - open AKSshiftTheory; -open logPowerTheory; open computeRingTheory; open computeParamTheory; -open EulerTheory; -open helperFunctionTheory; open monoidTheory groupTheory ringTheory ringUnitTheory; @@ -46,20 +47,9 @@ open polyCyclicTheory; open subgroupTheory; open groupOrderTheory; -(* Get dependent theories in lib *) -open helperNumTheory helperSetTheory; - -(* open dependent theories *) -open prim_recTheory pred_setTheory listTheory arithmeticTheory; -open dividesTheory gcdTheory; - -open binomialTheory; -open GaussTheory; (* for phi *) - open ringBinomialTheory; open ringDividesTheory; -open monoidInstancesTheory; open groupInstancesTheory; open ringInstancesTheory; open fieldInstancesTheory; @@ -70,6 +60,9 @@ open ffPolyTheory; open ffUnityTheory; open ffExistTheory; +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* AKS Main Theorem Documentation *) diff --git a/examples/AKS/theories/Holmakefile b/examples/AKS/theories/Holmakefile index ded442cc1f..328269682f 100644 --- a/examples/AKS/theories/Holmakefile +++ b/examples/AKS/theories/Holmakefile @@ -1,5 +1,3 @@ -PRE_INCLUDES = ../../algebra/ring - -ALGEBRA_INCLUDES = lib monoid group field polynomial finitefield +ALGEBRA_INCLUDES = group field ring polynomial finitefield INCLUDES = $(patsubst %,../../algebra/%,$(ALGEBRA_INCLUDES)) \ ../../simple_complexity/lib ../compute ../machine diff --git a/examples/fermat/count/Holmakefile b/examples/fermat/count/Holmakefile index 2d3e2e7bb3..ca5b666e1b 100644 --- a/examples/fermat/count/Holmakefile +++ b/examples/fermat/count/Holmakefile @@ -1,8 +1,6 @@ # prefix to HOL examples LOC_PREFIX = $(HOLDIR)/examples -PRE_INCLUDES = $(LOC_PREFIX)/algebra/ring - -ALGEBRA_INCLUDES = lib monoid group field polynomial finitefield +ALGEBRA_INCLUDES = ring group field polynomial finitefield INCLUDES = $(patsubst %,$(LOC_PREFIX)/algebra/%,$(ALGEBRA_INCLUDES)) \ ../little ../twosq diff --git a/examples/fermat/count/combinatoricsScript.sml b/examples/fermat/count/combinatoricsScript.sml deleted file mode 100644 index c99c97514a..0000000000 --- a/examples/fermat/count/combinatoricsScript.sml +++ /dev/null @@ -1,2809 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Combinatorics: combinations, permutations and arrangements. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "combinatorics"; - -(* ------------------------------------------------------------------------- *) - - -(* open dependent theories *) -(* arithmeticTheory -- load by default *) - -(* val _ = load "helperCountTheory"; *) -open helperCountTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def, prime_def *) -open EulerTheory; (* for upto_delete *) - -(* for later computation *) -open listTheory; -open rich_listTheory; -open helperListTheory; -open listRangeTheory; - -(* (* val _ = load "binomialTheory"; *) *) -open binomialTheory; (* for binomial_iff, binomial_n_n, binomial_formula2 *) - -(* (* val _ = load "necklaceTheory"; *) *) -open necklaceTheory; (* for necklace_def, necklace_finite *) - - -(* ------------------------------------------------------------------------- *) -(* Combinatorics Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading (# is temporary): -*) -(* Definitions and Theorems (# are exported, ! are in compute): - - Counting number of combinations: - sub_count_def |- !n k. sub_count n k = {s | s SUBSET count n /\ CARD s = k} - choose_def |- !n k. n choose k = CARD (sub_count n k) - sub_count_element |- !n k s. s IN sub_count n k <=> s SUBSET count n /\ CARD s = k - sub_count_subset |- !n k. sub_count n k SUBSET POW (count n) - sub_count_finite |- !n k. FINITE (sub_count n k) - sub_count_element_no_self - |- !n k s. s IN sub_count n k ==> n NOTIN s - sub_count_element_finite - |- !n k s. s IN sub_count n k ==> FINITE s - sub_count_n_0 |- !n. sub_count n 0 = {{}} - sub_count_0_n |- !n. sub_count 0 n = if n = 0 then {{}} else {} - sub_count_n_1 |- !n. sub_count n 1 = {{j} | j < n} - sub_count_n_n |- !n. sub_count n n = {count n} - sub_count_eq_empty |- !n k. sub_count n k = {} <=> n < k - sub_count_union |- !n k. sub_count (n + 1) (k + 1) = - IMAGE (\s. n INSERT s) (sub_count n k) UNION - sub_count n (k + 1) - sub_count_disjoint |- !n k. DISJOINT (IMAGE (\s. n INSERT s) (sub_count n k)) - (sub_count n (k + 1)) - sub_count_insert |- !n k s. s IN sub_count n k ==> - n INSERT s IN sub_count (n + 1) (k + 1) - sub_count_insert_card - |- !n k. CARD (IMAGE (\s. n INSERT s) (sub_count n k)) = - n choose k - sub_count_alt |- !n k. sub_count n 0 = {{}} /\ sub_count 0 (k + 1) = {} /\ - sub_count (n + 1) (k + 1) = - IMAGE (\s. n INSERT s) (sub_count n k) UNION - sub_count n (k + 1) -! sub_count_eqn |- !n k. sub_count n k = - if k = 0 then {{}} - else if n = 0 then {} - else IMAGE (\s. n - 1 INSERT s) (sub_count (n - 1) (k - 1)) UNION - sub_count (n - 1) k - choose_n_0 |- !n. n choose 0 = 1 - choose_n_1 |- !n. n choose 1 = n - choose_eq_0 |- !n k. n choose k = 0 <=> n < k - choose_0_n |- !n. 0 choose n = if n = 0 then 1 else 0 - choose_1_n |- !n. 1 choose n = if 1 < n then 0 else 1 - choose_n_n |- !n. n choose n = 1 - choose_recurrence |- !n k. (n + 1) choose (k + 1) = n choose k + n choose (k + 1) - choose_alt |- !n k. n choose 0 = 1 /\ 0 choose (k + 1) = 0 /\ - (n + 1) choose (k + 1) = n choose k + n choose (k + 1) -! choose_eqn |- !n k. n choose k = binomial n k - - Partition of the set of subsets by bijective equivalence: - sub_sets_def |- !P k. sub_sets P k = {s | s SUBSET P /\ CARD s = k} - sub_sets_sub_count |- !n k. sub_sets (count n) k = sub_count n k - sub_sets_equiv_class|- !s t. FINITE t /\ s SUBSET t ==> - sub_sets t (CARD s) = equiv_class $=b= (POW t) s - sub_count_equiv_class - |- !n k. k <= n ==> - sub_count n k = - equiv_class $=b= (POW (count n)) (count k) - count_power_partition |- !n. partition $=b= (POW (count n)) = - IMAGE (sub_count n) (upto n) - sub_count_count_inj |- !n m. INJ (sub_count n) (upto n) - univ(:(num -> bool) -> bool) - choose_sum_over_count |- !n. SIGMA ($choose n) (upto n) = 2 ** n - choose_sum_over_all |- !n. SUM (MAP ($choose n) [0 .. n]) = 2 ** n - - Counting number of permutations: - perm_count_def |- !n. perm_count n = {ls | ALL_DISTINCT ls /\ set ls = count n} - perm_def |- !n. perm n = CARD (perm_count n) - perm_count_element |- !ls n. ls IN perm_count n <=> ALL_DISTINCT ls /\ set ls = count n - perm_count_element_no_self - |- !ls n. ls IN perm_count n ==> ~MEM n ls - perm_count_element_length - |- !ls n. ls IN perm_count n ==> LENGTH ls = n - perm_count_subset |- !n. perm_count n SUBSET necklace n n - perm_count_finite |- !n. FINITE (perm_count n) - perm_count_0 |- perm_count 0 = {[]} - perm_count_1 |- perm_count 1 = {[0]} - interleave_def |- !x ls. x interleave ls = - IMAGE (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) - interleave_alt |- !ls x. x interleave ls = - {TAKE k ls ++ x::DROP k ls | k | k <= LENGTH ls} - interleave_element |- !ls x y. y IN x interleave ls <=> - ?k. k <= LENGTH ls /\ y = TAKE k ls ++ x::DROP k ls - interleave_nil |- !x. x interleave [] = {[x]} - interleave_length |- !ls x y. y IN x interleave ls ==> LENGTH y = 1 + LENGTH ls - interleave_distinct |- !ls x y. ALL_DISTINCT (x::ls) /\ y IN x interleave ls ==> - ALL_DISTINCT y - interleave_distinct_alt - |- !ls x y. ALL_DISTINCT ls /\ ~MEM x ls /\ - y IN x interleave ls ==> ALL_DISTINCT y - interleave_set |- !ls x y. y IN x interleave ls ==> set y = set (x::ls) - interleave_set_alt |- !ls x y. y IN x interleave ls ==> set y = x INSERT set ls - interleave_has_cons |- !ls x. x::ls IN x interleave ls - interleave_not_empty|- !ls x. x interleave ls <> {} - interleave_eq |- !n x y. ~MEM n x /\ ~MEM n y ==> - (n interleave x = n interleave y <=> x = y) - interleave_disjoint |- !l1 l2 x. ~MEM x l1 /\ l1 <> l2 ==> - DISJOINT (x interleave l1) (x interleave l2) - interleave_finite |- !ls x. FINITE (x interleave ls) - interleave_count_inj|- !ls x. ~MEM x ls ==> - INJ (\k. TAKE k ls ++ x::DROP k ls) - (upto (LENGTH ls)) univ(:'a list) - interleave_card |- !ls x. ~MEM x ls ==> CARD (x interleave ls) = 1 + LENGTH ls - interleave_revert |- !ls h. ALL_DISTINCT ls /\ MEM h ls ==> - ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ - set t = set ls DELETE h - interleave_revert_count - |- !ls n. ALL_DISTINCT ls /\ set ls = upto n ==> - ?t. ALL_DISTINCT t /\ ls IN n interleave t /\ - set t = count n - perm_count_suc |- !n. perm_count (SUC n) = - BIGUNION (IMAGE ($interleave n) (perm_count n)) - perm_count_suc_alt |- !n. perm_count (n + 1) = - BIGUNION (IMAGE ($interleave n) (perm_count n)) -! perm_count_eqn |- !n. perm_count n = - if n = 0 then {[]} - else BIGUNION (IMAGE ($interleave (n - 1)) (perm_count (n - 1))) - perm_0 |- perm 0 = 1 - perm_1 |- perm 1 = 1 - perm_count_interleave_finite - |- !n e. e IN IMAGE ($interleave n) (perm_count n) ==> FINITE e - perm_count_interleave_card - |- !n e. e IN IMAGE ($interleave n) (perm_count n) ==> CARD e = n + 1 - perm_count_interleave_disjoint - |- !n e s t. s IN IMAGE ($interleave n) (perm_count n) /\ - t IN IMAGE ($interleave n) (perm_count n) /\ s <> t ==> - DISJOINT s t - perm_count_interleave_inj - |- !n. INJ ($interleave n) (perm_count n) univ(:num list -> bool) - perm_suc |- !n. perm (SUC n) = SUC n * perm n - perm_suc_alt |- !n. perm (n + 1) = (n + 1) * perm n -! perm_eq_fact |- !n. perm n = FACT n - - Permutations of a set: - perm_set_def |- !s. perm_set s = {ls | ALL_DISTINCT ls /\ set ls = s} - perm_set_element |- !ls s. ls IN perm_set s <=> ALL_DISTINCT ls /\ set ls = s - perm_set_perm_count |- !n. perm_set (count n) = perm_count n - perm_set_empty |- perm_set {} = {[]} - perm_set_sing |- !x. perm_set {x} = {[x]} - perm_set_eq_empty_sing - |- !s. perm_set s = {[]} <=> s = {} - perm_set_has_self_list - |- !s. FINITE s ==> SET_TO_LIST s IN perm_set s - perm_set_not_empty |- !s. FINITE s ==> perm_set s <> {} - perm_set_list_not_empty - |- !ls. perm_set (set ls) <> {} - perm_set_map_element|- !ls f s n. ls IN perm_set s /\ BIJ f s (count n) ==> - MAP f ls IN perm_count n - perm_set_map_inj |- !f s n. BIJ f s (count n) ==> INJ (MAP f) (perm_set s) (perm_count n) - perm_set_map_surj |- !f s n. BIJ f s (count n) ==> SURJ (MAP f) (perm_set s) (perm_count n) - perm_set_map_bij |- !f s n. BIJ f s (count n) ==> BIJ (MAP f) (perm_set s) (perm_count n) - perm_set_bij_eq_perm_count - |- !s. FINITE s ==> perm_set s =b= perm_count (CARD s) - perm_set_finite |- !s. FINITE s ==> FINITE (perm_set s) - perm_set_card |- !s. FINITE s ==> CARD (perm_set s) = perm (CARD s) - perm_set_card_alt |- !s. FINITE s ==> CARD (perm_set s) = FACT (CARD s) - - Counting number of arrangements: - list_count_def |- !n k. list_count n k = - {ls | ALL_DISTINCT ls /\ - set ls SUBSET count n /\ LENGTH ls = k} - arrange_def |- !n k. n arrange k = CARD (list_count n k) - list_count_alt |- !n k. list_count n k = - {ls | ALL_DISTINCT ls /\ - set ls SUBSET count n /\ CARD (set ls) = k} - list_count_element |- !ls n k. ls IN list_count n k <=> - ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k - list_count_element_alt - |- !ls n k. ls IN list_count n k <=> - ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = k - list_count_element_set_card - |- !ls n k. ls IN list_count n k ==> CARD (set ls) = k - list_count_subset |- !n k. list_count n k SUBSET necklace k n - list_count_finite |- !n k. FINITE (list_count n k) - list_count_n_0 |- !n. list_count n 0 = {[]} - list_count_0_n |- !n. 0 < n ==> list_count 0 n = {} - list_count_n_n |- !n. list_count n n = perm_count n - list_count_eq_empty |- !n k. list_count n k = {} <=> n < k - list_count_by_image |- !n k. 0 < k ==> - list_count n k = - IMAGE (\ls. if ALL_DISTINCT ls then ls else []) - (necklace k n) DELETE [] -! list_count_eqn |- !n k. list_count n k = - if k = 0 then {[]} - else IMAGE (\ls. if ALL_DISTINCT ls then ls else []) - (necklace k n) DELETE [] - feq_set_equiv |- !s. feq set equiv_on s - list_count_set_eq_class - |- !ls n k. ls IN list_count n k ==> - equiv_class (feq set) (list_count n k) ls = perm_set (set ls) - list_count_set_eq_class_card - |- !ls n k. ls IN list_count n k ==> - CARD (equiv_class (feq set) (list_count n k) ls) = perm k - list_count_set_partititon_element_card - |- !n k e. e IN partition (feq set) (list_count n k) ==> CARD e = perm k - list_count_element_perm_set_not_empty - |- !ls n k. ls IN list_count n k ==> perm_set (set ls) <> {} - list_count_set_map_element - |- !s n k. s IN partition (feq set) (list_count n k) ==> - (set o CHOICE) s IN sub_count n k - list_count_set_map_inj - |- !n k. INJ (set o CHOICE) - (partition (feq set) (list_count n k)) - (sub_count n k) - list_count_set_map_surj - |- !n k. SURJ (set o CHOICE) - (partition (feq set) (list_count n k)) - (sub_count n k) - list_count_set_map_bij - |- !n k. BIJ (set o CHOICE) - (partition (feq set) (list_count n k)) - (sub_count n k) -! arrange_eqn |- !n k. n arrange k = (n choose k) * perm k - arrange_alt |- !n k. n arrange k = (n choose k) * FACT k - arrange_formula |- !n k. n arrange k = binomial n k * FACT k - arrange_formula2 |- !n k. k <= n ==> n arrange k = FACT n DIV FACT (n - k) - arrange_n_0 |- !n. n arrange 0 = 1 - arrange_0_n |- !n. 0 < n ==> 0 arrange n = 0 - arrange_n_n |- !n. n arrange n = perm n - arrange_n_n_alt |- !n. n arrange n = FACT n - arrange_eq_0 |- !n k. n arrange k = 0 <=> n < k -*) - -(* ------------------------------------------------------------------------- *) -(* Counting number of combinations. *) -(* ------------------------------------------------------------------------- *) - -(* The strategy: -This is to show, ultimately, C(n,k) = binomial n k. - -Conceptually, -C(n,k) = number of ways to choose k elements from a set of n elements. -Each choice gives a k-subset. - -Define C(n,k) = number of k-subsets of an n-set. -Prove that C(n,k) = binomial n k: -(1) C(0,0) = 1 -(2) C(0,1) = 0 -(3) C(SUC n, SUC k) = C(n,k) + C(n,SUC k) -show that any such C's is just the binomial function. - -binomial_alt -|- !n k. binomial n 0 = 1 /\ binomial 0 (k + 1) = 0 /\ - binomial (n + 1) (k + 1) = binomial n k + binomial n (k + 1) - -Moreover, bij_eq is an equivalence relation, and partitions the power set -of (count n) into equivalence classes of k-subsets for k = 0 to n. Thus - -SUM (GENLIST (choose n) (SUC n)) = CARD (POW (count n)) = 2 ** n - -the counterpart of binomial_sum |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n -*) - -(* Define the set of choices of k-subsets of (count n). *) -Definition sub_count_def[nocompute]: - sub_count n k = { (s:num -> bool) | s SUBSET (count n) /\ CARD s = k} -End -(* use [nocompute] as this is not effective for evalutaion. *) - -(* Define the number of choices of k-subsets of (count n). *) -Definition choose_def[nocompute]: - choose n k = CARD (sub_count n k) -End -(* use [nocompute] as this is not effective for evalutaion. *) -(* make this an infix operator *) -val _ = set_fixity "choose" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) -(* val choose_def = |- !n k. n choose k = CARD (sub_count n k): thm *) - -(* Theorem: s IN sub_count n k <=> s SUBSET count n /\ CARD s = k *) -(* Proof: by sub_count_def. *) -Theorem sub_count_element: - !n k s. s IN sub_count n k <=> s SUBSET count n /\ CARD s = k -Proof - simp[sub_count_def] -QED - -(* Theorem: (sub_count n k) SUBSET (POW (count n)) *) -(* Proof: - s IN sub_count n k - ==> s SUBSET (count n) by sub_count_def - ==> s IN POW (count n) by POW_DEF - Thus (sub_count n k) SUBSET (POW (count n)) by SUBSET_DEF -*) -Theorem sub_count_subset: - !n k. (sub_count n k) SUBSET (POW (count n)) -Proof - simp[sub_count_def, POW_DEF, SUBSET_DEF] -QED - -(* Theorem: FINITE (sub_count n k) *) -(* Proof: - Note (sub_count n k) SUBSET (POW (count n)) by sub_count_subset - and FINITE (count n) by FINITE_COUNT - so FINITE (POW (count n)) by FINITE_POW - Thus FINITE (sub_count n k) by SUBSET_FINITE -*) -Theorem sub_count_finite: - !n k. FINITE (sub_count n k) -Proof - metis_tac[sub_count_subset, FINITE_COUNT, FINITE_POW, SUBSET_FINITE] -QED - -(* Theorem: s IN sub_count n k ==> n NOTIN s *) -(* Proof: - Note s SUBSET (count n) by sub_count_element - and n NOTIN (count n) by COUNT_NOT_SELF - so n NOTIN s by SUBSET_DEF -*) -Theorem sub_count_element_no_self: - !n k s. s IN sub_count n k ==> n NOTIN s -Proof - metis_tac[sub_count_element, SUBSET_DEF, COUNT_NOT_SELF] -QED - -(* Theorem: s IN sub_count n k ==> FINITE s *) -(* Proof: - Note s SUBSET (count n) by sub_count_element - and FINITE (count n) by FINITE_COUNT - so FINITE s by SUBSET_FINITE -*) -Theorem sub_count_element_finite: - !n k s. s IN sub_count n k ==> FINITE s -Proof - metis_tac[sub_count_element, FINITE_COUNT, SUBSET_FINITE] -QED - -(* Theorem: sub_count n 0 = { EMPTY } *) -(* Proof: - By EXTENSION, IN_SING, this is to show: - (1) x IN sub_count n 0 ==> x = {} - x IN sub_count n 0 - <=> x SUBSET count n /\ CARD x = 0 by sub_count_def - ==> FINITE x /\ CARD x = 0 by SUBSET_FINITE, FINITE_COUNT - ==> x = {} by CARD_EQ_0 - (2) {} IN sub_count n 0 - {} IN sub_count n 0 - <=> {} SUBSET count n /\ CARD {} = 0 by sub_count_def - <=> T /\ CARD {} = 0 by EMPTY_SUBSET - <=> T /\ T by CARD_EMPTY - <=> T -*) -Theorem sub_count_n_0: - !n. sub_count n 0 = { EMPTY } -Proof - rewrite_tac[EXTENSION, EQ_IMP_THM] >> - rw[IN_SING] >| [ - fs[sub_count_def] >> - metis_tac[CARD_EQ_0, SUBSET_FINITE, FINITE_COUNT], - rw[sub_count_def] - ] -QED - -(* Theorem: sub_count 0 n = if n = 0 then { EMPTY } else EMPTY *) -(* Proof: - If n = 0, - then sub_count 0 n = { EMPTY } by sub_count_n_0 - If n <> 0, - s IN sub_count 0 n - <=> s SUBSET count 0 /\ CARD s = n by sub_count_def - <=> s SUBSET {} /\ CARD s = n by COUNT_0 - <=> CARD {} = n by SUBSET_EMPTY - <=> 0 = n by CARD_EMPTY - <=> F by n <> 0 - Thus sub_count 0 n = {} by MEMBER_NOT_EMPTY -*) -Theorem sub_count_0_n: - !n. sub_count 0 n = if n = 0 then { EMPTY } else EMPTY -Proof - rw[sub_count_n_0] >> - rw[sub_count_def, EXTENSION] >> - spose_not_then strip_assume_tac >> - `x = {}` by metis_tac[MEMBER_NOT_EMPTY] >> - fs[] -QED - -(* Theorem: sub_count n 1 = {{j} | j < n } *) -(* Proof: - By sub_count_def, EXTENSION, this is to show: - x SUBSET count n /\ CARD x = 1 <=> - ?j. (!x'. x' IN x <=> x' = j) /\ j < n - If part: - Note FINITE x by SUBSET_FINITE, FINITE_COUNT - so ?j. x = {j} by CARD_EQ_1, SING_DEF - Take this j. - Then !x'. x' IN x <=> x' = j - by IN_SING - and x SUBSET (count n) ==> j < n - by SUBSET_DEF, IN_COUNT - Only-if part: - Note j IN x, so x <> {} by MEMBER_NOT_EMPTY - The given shows x = {j} by ONE_ELEMENT_SING - and j < n ==> x SUBSET (count n) - by SUBSET_DEF, IN_COUNT - and CARD x = 1 by CARD_SING -*) -Theorem sub_count_n_1: - !n. sub_count n 1 = {{j} | j < n } -Proof - rw[sub_count_def, EXTENSION] >> - rw[EQ_IMP_THM] >| [ - `FINITE x` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - `?j. x = {j}` by metis_tac[CARD_EQ_1, SING_DEF] >> - metis_tac[SUBSET_DEF, IN_SING, IN_COUNT], - rw[SUBSET_DEF], - metis_tac[ONE_ELEMENT_SING, MEMBER_NOT_EMPTY, CARD_SING] - ] -QED - -(* Theorem: sub_count n n = {count n} *) -(* Proof: - s IN sub_count n n - <=> s SUBSET count n /\ CARD s = n by sub_count_def - <=> s SUBSET count n /\ CARD s = CARD (count n) - by CARD_COUNT - <=> s SUBSET count n /\ s = count n by SUBSET_CARD_EQ - <=> T /\ s = count n by SUBSET_REFL - Thus sub_count n n = {count n} by EXTENSION -*) -Theorem sub_count_n_n: - !n. sub_count n n = {count n} -Proof - rw_tac bool_ss[EXTENSION] >> - `FINITE (count n) /\ CARD (count n) = n` by rw[] >> - metis_tac[sub_count_element, SUBSET_CARD_EQ, SUBSET_REFL, IN_SING] -QED - -(* Theorem: sub_count n k = EMPTY <=> n < k *) -(* Proof: - If part: sub_count n k = {} ==> n < k - By contradiction, suppose k <= n. - Then (count k) SUBSET (count n) by COUNT_SUBSET, k <= n - and CARD (count k) = k by CARD_COUNT - so (count k) IN sub_count n k by sub_count_element - Thus sub_count n k <> {} by MEMBER_NOT_EMPTY - which is a contradiction. - Only-if part: n < k ==> sub_count n k = {} - By contradiction, suppose sub_count n k <> {}. - Then ?s. s IN sub_count n k by MEMBER_NOT_EMPTY - ==> s SUBSET count n /\ CARD s = k - by sub_count_element - Note FINITE (count n) by FINITE_COUNT - so CARD s <= CARD (count n) by CARD_SUBSET - ==> k <= n by CARD_COUNT - This contradicts n < k. -*) -Theorem sub_count_eq_empty: - !n k. sub_count n k = EMPTY <=> n < k -Proof - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - `(count k) SUBSET (count n)` by rw[COUNT_SUBSET] >> - `CARD (count k) = k` by rw[] >> - metis_tac[sub_count_element, MEMBER_NOT_EMPTY], - spose_not_then strip_assume_tac >> - `?s. s IN sub_count n k` by rw[MEMBER_NOT_EMPTY] >> - fs[sub_count_element] >> - `FINITE (count n)` by rw[] >> - `CARD s <= n` by metis_tac[CARD_SUBSET, CARD_COUNT] >> - decide_tac - ] -QED - -(* Theorem: sub_count (n + 1) (k + 1) = - IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) *) -(* Proof: - By sub_count_def, EXTENSION, this is to show: - (1) x SUBSET count (n + 1) /\ CARD x = k + 1 ==> - ?s. (!y. y IN x <=> y = n \/ y IN s) /\ - s SUBSET count n /\ CARD s = k) \/ x SUBSET count n - Suppose ~(x SUBSET count n), - Then n IN x by SUBSET_DEF - Take s = x DELETE n. - Then y IN x <=> - y = n \/ y IN s by EXTENSION - and s SUBSET x by DELETE_SUBSET - so s SUBSET (count (n + 1) DELETE n) - by SUBSET_DELETE_BOTH - or s SUBSET (count n) by count_def - Note FINITE x by SUBSET_FINITE, FINITE_COUNT - so CARD s = k by CARD_DELETE, CARD_COUNT - (2) s SUBSET count n /\ x = n INSERT s ==> x SUBSET count (n + 1) - Note x SUBSET (n INSERT count n) by SUBSET_INSERT_BOTH - so x INSERT count (n + 1) by count_def, or count_add1 - (3) s SUBSET count n /\ x = n INSERT s ==> CARD x = CARD s + 1 - Note n NOTIN s by SUBSET_DEF, COUNT_NOT_SELF - and FINITE s by SUBSET_FINITE, FINITE_COUNT - so CARD x - = SUC (CARD s) by CARD_INSERT - = CARD s + 1 by ADD1 - (4) x SUBSET count n ==> x SUBSET count (n + 1) - Note (count n) SUBSET count (n + 1) by COUNT_SUBSET, n <= n + 1 - so x SUBSET count (n + 1) by SUBSET_TRANS -*) -Theorem sub_count_union: - !n k. sub_count (n + 1) (k + 1) = - IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) -Proof - rw[sub_count_def, EXTENSION, Once EQ_IMP_THM] >> simp[] >| [ - rename [‘x ⊆ count (n + 1)’, ‘CARD x = k + 1’] >> - Cases_on `x SUBSET count n` >> simp[] >> - `n IN x` by - (fs[SUBSET_DEF] >> rename [‘m ∈ x’, ‘¬(m < n)’] >> - `m < n + 1` by simp[] >> - `m = n` by decide_tac >> - fs[]) >> - qexists_tac `x DELETE n` >> - `FINITE x` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - rw[] >- (rw[EQ_IMP_THM] >> simp[]) >> - `x DELETE n SUBSET (count (n + 1)) DELETE n` by rw[SUBSET_DELETE_BOTH] >> - `count (n + 1) DELETE n = count n` by rw[EXTENSION] >> - fs[], - - rename [‘s ⊆ count n’, ‘x ⊆ count (n + 1)’] >> - `x = n INSERT s` by fs[EXTENSION] >> - `x SUBSET (n INSERT count n)` by rw[SUBSET_INSERT_BOTH] >> - rfs[count_add1], - - rename [‘s ⊆ count n’, ‘CARD x = CARD s + 1’] >> - `x = n INSERT s` by fs[EXTENSION] >> - `n NOTIN s` by metis_tac[SUBSET_DEF, COUNT_NOT_SELF] >> - `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - rw[], - - metis_tac[COUNT_SUBSET, SUBSET_TRANS, DECIDE “n ≤ n + 1”] - ] -QED - -(* Theorem: DISJOINT (IMAGE (\s. n INSERT s) (sub_count n k)) (sub_count n (k + 1)) *) -(* Proof: - Let s = IMAGE (\s. n INSERT s) (sub_count n k), - t = sub_count n (k + 1). - By DISJOINT_DEF and contradiction, suppose s INTER t <> {}. - Then ?x. x IN s /\ x IN t by IN_INTER, MEMBER_NOT_EMPTY - Note n IN x by IN_IMAGE, IN_INSERT - but n NOTIN x by sub_count_element_no_self - This is a contradiction. -*) -Theorem sub_count_disjoint: - !n k. DISJOINT (IMAGE (\s. n INSERT s) (sub_count n k)) (sub_count n (k + 1)) -Proof - rw[DISJOINT_DEF, EXTENSION] >> - spose_not_then strip_assume_tac >> - rename [‘s ∈ sub_count n k’, ‘x ∈ sub_count n (k + 1)’] >> - `x = n INSERT s` by fs[EXTENSION] >> - `n IN x` by fs[] >> - metis_tac[sub_count_element_no_self] -QED - -(* Theorem: s IN sub_count n k ==> (n INSERT s) IN sub_count (n + 1) (k + 1) *) -(* Proof: - Note s SUBSET count n /\ CARD s = k by sub_count_element - and n NOTIN s by sub_count_element_no_self - and FINITE s by sub_count_element_finite - Now (n INSERT s) SUBSET (n INSERT count n) - by SUBSET_INSERT_BOTH - and n INSERT count n = count (n + 1) by count_add1 - and CARD (n INSERT s) = CARD s + 1 by CARD_INSERT - = k + 1 by given - Thus (n INSERT s) IN sub_count (n + 1) (k + 1) - by sub_count_element -*) -Theorem sub_count_insert: - !n k s. s IN sub_count n k ==> (n INSERT s) IN sub_count (n + 1) (k + 1) -Proof - rw[sub_count_def] >| [ - `!x. x < n ==> x < n + 1` by decide_tac >> - metis_tac[SUBSET_DEF, IN_COUNT], - `n NOTIN s` by metis_tac[SUBSET_DEF, COUNT_NOT_SELF] >> - `FINITE s` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - rw[] - ] -QED - -(* Theorem: CARD (IMAGE (\s. n INSERT s) (sub_count n k)) = n choose k *) -(* Proof: - Let f = \s. n INSERT s. - By choose_def, INJ_CARD_IMAGE, this is to show: - (1) FINITE (sub_count n k), true by sub_count_finite - (2) ?t. INJ f (sub_count n k) t - Let t = sub_count (n + 1) (k + 1). - By INJ_DEF, this is to show: - (1) s IN sub_count n k ==> n INSERT s IN sub_count (n + 1) (k + 1) - This is true by sub_count_insert - (2) s' IN sub_count n k /\ s IN sub_count n k /\ - n INSERT s' = n INSERT s ==> s' = s - Note n NOTIN s by sub_count_element_no_self - and n NOTIN s' by sub_count_element_no_self - s' - = s' DELETE n by DELETE_NON_ELEMENT - = (n INSERT s') DELETE n by DELETE_INSERT - = (n INSERT s) DELETE n by given - = s DELETE n by DELETE_INSERT - = s by DELETE_NON_ELEMENT -*) -Theorem sub_count_insert_card: - !n k. CARD (IMAGE (\s. n INSERT s) (sub_count n k)) = n choose k -Proof - rw[choose_def] >> - qabbrev_tac `f = \s. n INSERT s` >> - irule INJ_CARD_IMAGE >> - rpt strip_tac >- - rw[sub_count_finite] >> - qexists_tac `sub_count (n + 1) (k + 1)` >> - rw[INJ_DEF, Abbr`f`] >- - rw[sub_count_insert] >> - rename [‘n INSERT s1 = n INSERT s2’] >> - `n NOTIN s1 /\ n NOTIN s2` by metis_tac[sub_count_element_no_self] >> - metis_tac[DELETE_INSERT, DELETE_NON_ELEMENT] -QED - -(* Theorem: sub_count n 0 = { EMPTY } /\ - sub_count 0 (k + 1) = {} /\ - sub_count (n + 1) (k + 1) = - IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) *) -(* Proof: by sub_count_n_0, sub_count_0_n, sub_count_union. *) -Theorem sub_count_alt: - !n k. sub_count n 0 = { EMPTY } /\ - sub_count 0 (k + 1) = {} /\ - sub_count (n + 1) (k + 1) = - IMAGE (\s. n INSERT s) (sub_count n k) UNION sub_count n (k + 1) -Proof - simp[sub_count_n_0, sub_count_0_n, sub_count_union] -QED - -(* Theorem: sub_count n k = - if k = 0 then { EMPTY } - else if n = 0 then {} - else IMAGE (\s. (n - 1) INSERT s) (sub_count (n - 1) (k - 1)) UNION - sub_count (n - 1) k *) -(* Proof: by sub_count_n_0, sub_count_0_n, sub_count_union. *) -Theorem sub_count_eqn[compute]: - !n k. sub_count n k = - if k = 0 then { EMPTY } - else if n = 0 then {} - else IMAGE (\s. (n - 1) INSERT s) (sub_count (n - 1) (k - 1)) UNION - sub_count (n - 1) k -Proof - rw[sub_count_n_0, sub_count_0_n] >> - metis_tac[sub_count_union, num_CASES, SUC_SUB1, ADD1] -QED - -(* -> EVAL ``sub_count 3 2``; -val it = |- sub_count 3 2 = {{2; 1}; {2; 0}; {1; 0}}: thm -> EVAL ``sub_count 4 2``; -val it = |- sub_count 4 2 = {{3; 2}; {3; 1}; {3; 0}; {2; 1}; {2; 0}; {1; 0}}: thm -> EVAL ``sub_count 3 3``; -val it = |- sub_count 3 3 = {{2; 1; 0}}: thm -*) - -(* Theorem: n choose 0 = 1 *) -(* Proof: - n choose 0 - = CARD (sub_count n 0) by choose_def - = CARD {{}} by sub_count_n_0 - = 1 by CARD_SING -*) -Theorem choose_n_0: - !n. n choose 0 = 1 -Proof - simp[choose_def, sub_count_n_0] -QED - -(* Theorem: n choose 1 = n *) -(* Proof: - Let s = {{j} | j < n}, - f = \j. {j}. - Then s = IMAGE f (count n) by EXTENSION - Note FINITE (count n) - and INJ f (count n) (POW (count n)) - Thus n choose 1 - = CARD (sub_count n 1) by choose_def - = CARD s by sub_count_n_1 - = CARD (count n) by INJ_CARD_IMAGE - = n by CARD_COUNT -*) -Theorem choose_n_1: - !n. n choose 1 = n -Proof - rw[choose_def, sub_count_n_1] >> - qabbrev_tac `s = {{j} | j < n}` >> - qabbrev_tac `f = \j:num. {j}` >> - `s = IMAGE f (count n)` by fs[EXTENSION, Abbr`f`, Abbr`s`] >> - `CARD (IMAGE f (count n)) = CARD (count n)` suffices_by fs[] >> - irule INJ_CARD_IMAGE >> - rw[] >> - qexists_tac `POW (count n)` >> - rw[INJ_DEF, Abbr`f`] >> - rw[POW_DEF] -QED - -(* Theorem: n choose k = 0 <=> n < k *) -(* Proof: - Note FINITE (sub_count n k) by sub_count_finite - n choose k = 0 - <=> CARD (sub_count n k) = 0 by choose_def - <=> sub_count n k = {} by CARD_EQ_0 - <=> n < k by sub_count_eq_empty -*) -Theorem choose_eq_0: - !n k. n choose k = 0 <=> n < k -Proof - metis_tac[choose_def, sub_count_eq_empty, sub_count_finite, CARD_EQ_0] -QED - -(* Theorem: 0 choose n = if n = 0 then 1 else 0 *) -(* Proof: - 0 choose n - = CARD (sub_count 0 n) by choose_def - = CARD (if n = 0 then {{}} else {}) - by sub_count_0_n - = if n = 0 then 1 else 0 by CARD_SING, CARD_EMPTY -*) -Theorem choose_0_n: - !n. 0 choose n = if n = 0 then 1 else 0 -Proof - rw[choose_def, sub_count_0_n] -QED - -(* Theorem: 1 choose n = if 1 < n then 0 else 1 *) -(* Proof: - If n = 0, 1 choose 0 = 1 by choose_n_0 - If n = 1, 1 choose 1 = 1 by choose_n_1 - Otherwise, 1 choose n = 0 by choose_eq_0, 1 < n -*) -Theorem choose_1_n: - !n. 1 choose n = if 1 < n then 0 else 1 -Proof - rw[choose_eq_0] >> - `n = 0 \/ n = 1` by decide_tac >- - simp[choose_n_0] >> - simp[choose_n_1] -QED - -(* Theorem: n choose n = 1 *) -(* Proof: - n choose n - = CARD (sub_count n n) by choose_def - = CARD {count n} by sub_count_n_n - = 1 by CARD_SING -*) -Theorem choose_n_n: - !n. n choose n = 1 -Proof - simp[choose_def, sub_count_n_n] -QED - -(* Theorem: (n + 1) choose (k + 1) = n choose k + n choose (k + 1) *) -(* Proof: - Let s = sub_count (n + 1) (k + 1), - u = sub_count n k, - v = sub_count n (k + 1), - t = IMAGE (\s. n INSERT s) u. - Then s = t UNION v by sub_count_union - and DISJOINT t v by sub_count_disjoint - and FINITE u /\ FINITE v by sub_count_finite - and FINITE t by IMAGE_FINITE - Thus CARD s = CARD t + CARD v by CARD_UNION_DISJOINT - = CARD u + CARD v by sub_count_insert_card, choose_def -*) -Theorem choose_recurrence: - !n k. (n + 1) choose (k + 1) = n choose k + n choose (k + 1) -Proof - rw[choose_def] >> - qabbrev_tac `s = sub_count (n + 1) (k + 1)` >> - qabbrev_tac `u = sub_count n k` >> - qabbrev_tac `v = sub_count n (k + 1)` >> - qabbrev_tac `t = IMAGE (\s. n INSERT s) u` >> - `s = t UNION v` by rw[sub_count_union, Abbr`s`, Abbr`t`, Abbr`v`] >> - `DISJOINT t v` by metis_tac[sub_count_disjoint] >> - `FINITE u /\ FINITE v` by rw[sub_count_finite, Abbr`u`, Abbr`v`] >> - `FINITE t` by rw[Abbr`t`] >> - `CARD s = CARD t + CARD v` by rw[CARD_UNION_DISJOINT] >> - metis_tac[sub_count_insert_card, choose_def] -QED - -(* This is Pascal's identity: C(n+1,k+1) = C(n,k) + C(n,k+1). *) -(* This corresponds to the 'sum of parents' rule of Pascal's triangle. *) - -(* Theorem: n choose 0 = 1 /\ 0 choose (k + 1) = 0 /\ - (n + 1) choose (k + 1) = n choose k + n choose (k + 1) *) -(* Proof: by choose_n_0, choose_0_n, choose_recurrence. *) -Theorem choose_alt: - !n k. n choose 0 = 1 /\ 0 choose (k + 1) = 0 /\ - (n + 1) choose (k + 1) = n choose k + n choose (k + 1) -Proof - simp[choose_n_0, choose_0_n, choose_recurrence] -QED - -(* Theorem: n choose k = binomial n k *) -(* Proof: by binomial_iff, choose_alt. *) -Theorem choose_eqn[compute]: - !n k. n choose k = binomial n k -Proof - prove_tac[binomial_iff, choose_alt] -QED - -(* -> EVAL ``5 choose 3``; -val it = |- 5 choose 3 = 10: thm -> EVAL ``MAP ($choose 5) [0 .. 5]``; -val it = |- MAP ($choose 5) [0 .. 5] = [1; 5; 10; 10; 5; 1]: thm -*) - -(* ------------------------------------------------------------------------- *) -(* Partition of the set of subsets by bijective equivalence. *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of k-subsets of a set. *) -Definition sub_sets_def[nocompute]: - sub_sets P k = { s | s SUBSET P /\ CARD s = k} -End -(* use [nocompute] as this is not effective for evalutaion. *) - -(* Theorem: s IN sub_sets P k <=> s SUBSET P /\ CARD s = k *) -(* Proof: by sub_sets_def. *) -Theorem sub_sets_element: - !P k s. s IN sub_sets P k <=> s SUBSET P /\ CARD s = k -Proof - simp[sub_sets_def] -QED - -(* Theorem: sub_sets (count n) k = sub_count n k *) -(* Proof: - sub_sets (count n) k - = {s | s SUBSET (count n) /\ CARD s = k} by sub_sets_def - = sub_count n k by sub_count_def -*) -Theorem sub_sets_sub_count: - !n k. sub_sets (count n) k = sub_count n k -Proof - simp[sub_sets_def, sub_count_def] -QED - -(* Theorem: FINITE t /\ s SUBSET t ==> - sub_sets t (CARD s) = equiv_class $=b= (POW t) s *) -(* Proof: - x IN sub_sets t (CARD s) - <=> x SUBSET t /\ CARD s = CARD x by sub_sets_element - <=> x SUBSET t /\ s =b= x by bij_eq_card_eq, SUBSET_FINITE - <=> x IN POW t /\ s =b= x by IN_POW - <=> x IN equiv_class $=b= (POW t) s by equiv_class_element -*) -Theorem sub_sets_equiv_class: - !s t. FINITE t /\ s SUBSET t ==> - sub_sets t (CARD s) = equiv_class $=b= (POW t) s -Proof - rw[sub_sets_def, IN_POW, EXTENSION] >> - metis_tac[bij_eq_card_eq, SUBSET_FINITE] -QED - -(* Theorem: s SUBSET (count n) ==> - sub_count n (CARD s) = equiv_class $=b= (POW (count n)) s *) -(* Proof: - Note FINITE (count n) by FINITE_COUNT - sub_count n (CARD s) - = sub_sets (count n) (CARD s) by sub_sets_sub_count - = equiv_class $=b= (POW t) s by sub_sets_equiv_class -*) -Theorem sub_count_equiv_class: - !n s. s SUBSET (count n) ==> - sub_count n (CARD s) = equiv_class $=b= (POW (count n)) s -Proof - simp[sub_sets_equiv_class, GSYM sub_sets_sub_count] -QED - -(* Theorem: partition $=b= (POW (count n)) = IMAGE (sub_count n) (upto n) *) -(* Proof: - Let R = $=b=, t = count n. - Note CARD t = n by CARD_COUNT - By EXTENSION and LESS_EQ_IFF_LESS_SUC, this is to show: - (1) x IN partition R (POW t) ==> ?k. x = sub_count n k /\ k <= n - Note ?s. s IN POW t /\ - x = equiv_class R (POW t) s by partition_element - Note FINITE t by SUBSET_FINITE - and s SUBSET t by IN_POW - so CARD s <= CARD t = n by CARD_SUBSET - Take k = CARD s. - Then k <= n /\ x = sub_count n k by sub_count_equiv_class - (2) k <= n ==> sub_count n k IN partition R (POW t) - Let s = count k - Then CARD s = k by CARD_COUNT - and s SUBSET t by COUNT_SUBSET, k <= n - so s IN POW t by IN_POW - Now sub_count n k - = equiv_class R (POW t) s by sub_count_equiv_class - ==> sub_count n k IN partition R (POW t) - by partition_element -*) -Theorem count_power_partition: - !n. partition $=b= (POW (count n)) = IMAGE (sub_count n) (upto n) -Proof - rpt strip_tac >> - qabbrev_tac `R = \(s:num -> bool) (t:num -> bool). s =b= t` >> - qabbrev_tac `t = count n` >> - rw[Once EXTENSION, partition_element, GSYM LESS_EQ_IFF_LESS_SUC, EQ_IMP_THM] >| [ - `FINITE t` by rw[Abbr`t`] >> - `x' SUBSET t` by fs[IN_POW] >> - `CARD x' <= n` by metis_tac[CARD_SUBSET, CARD_COUNT] >> - qexists_tac `CARD x'` >> - simp[sub_count_equiv_class, Abbr`R`, Abbr`t`], - qexists_tac `count x'` >> - `(count x') SUBSET t /\ (count x') IN POW t` by metis_tac[COUNT_SUBSET, IN_POW] >> - simp[] >> - qabbrev_tac `s = count x'` >> - `sub_count n (CARD s) = equiv_class R (POW t) s` suffices_by simp[Abbr`s`] >> - simp[sub_count_equiv_class, Abbr`R`, Abbr`t`] - ] -QED - -(* Theorem: INJ (sub_count n) (upto n) univ(:(num -> bool) -> bool) *) -(* Proof: - By INJ_DEF, this is to show: - !x y. x < SUC n /\ y < SUC n /\ sub_count n x = sub_count n y ==> x = y - Let s = count x. - Note x < SUC n <=> x <= n by arithmetic - ==> s SUBSET (count n) by COUNT_SUBSET, x <= n - and CARD s = x by CARD_COUNT - so s IN sub_count n x by sub_count_element - Thus s IN sub_count n y by given, sub_count n x = sub_count n y - ==> CARD s = x = y -*) -Theorem sub_count_count_inj: - !n m. INJ (sub_count n) (upto n) univ(:(num -> bool) -> bool) -Proof - rw[sub_count_def, EXTENSION, INJ_DEF] >> - `(count x) SUBSET (count n)` by rw[COUNT_SUBSET] >> - metis_tac[CARD_COUNT] -QED - -(* Idea: the sum of sizes of equivalence classes gives size of power set. *) - -(* Theorem: SIGMA ($choose n) (upto n) = 2 ** n *) -(* Proof: - Let R = $=b=, t = count n. - Then R equiv_on (POW t) by bij_eq_equiv_on - and FINITE t by FINITE_COUNT - so FINITE (POW t) by FINITE_POW - Thus CARD (POW t) = SIGMA CARD (partition R (POW t)) - by partition_CARD - LHS = CARD (POW t) - = 2 ** CARD t by CARD_POW - = 2 ** n by CARD_COUNT - Note INJ (sub_count n) (upto n) univ by sub_count_count_inj - RHS = SIGMA CARD (partition R (POW t)) - = SIGMA CARD (IMAGE (sub_count n) (upto n)) by count_power_partition - = SIGMA (CARD o sub_count n) (upto n) by SUM_IMAGE_INJ_o - = SIGMA ($choose n) (upto n) by FUN_EQ_THM, choose_def -*) -Theorem choose_sum_over_count: - !n. SIGMA ($choose n) (upto n) = 2 ** n -Proof - rpt strip_tac >> - qabbrev_tac `R = \(s:num -> bool) (t:num -> bool). s =b= t` >> - qabbrev_tac `t = count n` >> - `R equiv_on (POW t)` by rw[bij_eq_equiv_on, Abbr`R`] >> - `FINITE (POW t)` by rw[FINITE_POW, Abbr`t`] >> - imp_res_tac partition_CARD >> - `FINITE (upto n)` by rw[] >> - `SIGMA CARD (partition R (POW t)) = SIGMA CARD (IMAGE (sub_count n) (upto n))` by fs[count_power_partition, Abbr`R`, Abbr`t`] >> - `_ = SIGMA (CARD o (sub_count n)) (upto n)` by rw[SUM_IMAGE_INJ_o, sub_count_count_inj] >> - `_ = SIGMA ($choose n) (upto n)` by rw[choose_def, FUN_EQ_THM, SIGMA_CONG] >> - fs[CARD_POW, Abbr`t`] -QED - -(* This corresponds to: -> binomial_sum; -val it = |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n: thm -*) - -(* Theorem: SUM (MAP ($choose n) [0 .. n]) = 2 ** n *) -(* Proof: - SUM (MAP ($choose n) [0 .. n]) - = SIGMA ($choose n) (upto n) by SUM_IMAGE_upto - = 2 ** n by choose_sum_over_count -*) -Theorem choose_sum_over_all: - !n. SUM (MAP ($choose n) [0 .. n]) = 2 ** n -Proof - simp[GSYM SUM_IMAGE_upto, choose_sum_over_count] -QED - -(* A better representation of: -> binomial_sum; -val it = |- !n. SUM (GENLIST (binomial n) (SUC n)) = 2 ** n: thm -*) - -(* ------------------------------------------------------------------------- *) -(* Counting number of permutations. *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of permutation tuples of (count n). *) -Definition perm_count_def[nocompute]: - perm_count n = { ls | ALL_DISTINCT ls /\ set ls = count n} -End -(* use [nocompute] as this is not effective for evalutaion. *) - -(* Define the number of choices of k-tuples of (count n). *) -Definition perm_def[nocompute]: - perm n = CARD (perm_count n) -End -(* use [nocompute] as this is not effective for evalutaion. *) - -(* Theorem: ls IN perm_count n <=> ALL_DISTINCT ls /\ set ls = count n *) -(* Proof: by perm_count_def. *) -Theorem perm_count_element: - !ls n. ls IN perm_count n <=> ALL_DISTINCT ls /\ set ls = count n -Proof - simp[perm_count_def] -QED - -(* Theorem: ls IN perm_count n ==> ~MEM n ls *) -(* Proof: - ls IN perm_count n - <=> ALL_DISTINCT ls /\ set ls = count n by perm_count_element - ==> ~MEM n ls by COUNT_NOT_SELF -*) -Theorem perm_count_element_no_self: - !ls n. ls IN perm_count n ==> ~MEM n ls -Proof - simp[perm_count_element] -QED - -(* Theorem: ls IN perm_count n ==> LENGTH ls = n *) -(* Proof: - ls IN perm_count n - <=> ALL_DISTINCT ls /\ set ls = count n by perm_count_element - LENGTH ls = CARD (set ls) by ALL_DISTINCT_CARD_LIST_TO_SET - = CARD (count n) by set ls = count n - = n by CARD_COUNT -*) -Theorem perm_count_element_length: - !ls n. ls IN perm_count n ==> LENGTH ls = n -Proof - metis_tac[perm_count_element, ALL_DISTINCT_CARD_LIST_TO_SET, CARD_COUNT] -QED - -(* Theorem: perm_count n SUBSET necklace n n *) -(* Proof: - ls IN perm_count n - <=> ALL_DISTINCT ls /\ set ls = count n by perm_count_element - Thus set ls SUBSET (count n) by SUBSET_REFL - and LENGTH ls = n by perm_count_element_length - Therefore ls IN necklace n n by necklace_element -*) -Theorem perm_count_subset: - !n. perm_count n SUBSET necklace n n -Proof - rw[perm_count_def, necklace_def, perm_count_element_length, SUBSET_DEF] -QED - -(* Theorem: FINITE (perm_count n) *) -(* Proof: - Note perm_count n SUBSET necklace n n by perm_count_subset - and FINITE (necklace n n) by necklace_finite - Thus FINITE (perm_count n) by SUBSET_FINITE -*) -Theorem perm_count_finite: - !n. FINITE (perm_count n) -Proof - metis_tac[perm_count_subset, necklace_finite, SUBSET_FINITE] -QED - -(* Theorem: perm_count 0 = {[]} *) -(* Proof: - ls IN perm_count 0 - <=> ALL_DISTINCT ls /\ set ls = count 0 by perm_count_element - <=> ALL_DISTINCT ls /\ set ls = {} by COUNT_0 - <=> ALL_DISTINCT ls /\ ls = [] by LIST_TO_SET_EQ_EMPTY - <=> ls = [] by ALL_DISTINCT - Thus perm_count 0 = {[]} by EXTENSION -*) -Theorem perm_count_0: - perm_count 0 = {[]} -Proof - rw[perm_count_def, EXTENSION, EQ_IMP_THM] >> - metis_tac[MEM, list_CASES] -QED - -(* Theorem: perm_count 1 = {[0]} *) -(* Proof: - ls IN perm_count 1 - <=> ALL_DISTINCT ls /\ set ls = count 1 by perm_count_element - <=> ALL_DISTINCT ls /\ set ls = {0} by COUNT_1 - <=> ls = [0] by DISTINCT_LIST_TO_SET_EQ_SING - Thus perm_count 1 = {[0]} by EXTENSION -*) -Theorem perm_count_1: - perm_count 1 = {[0]} -Proof - simp[perm_count_def, COUNT_1, DISTINCT_LIST_TO_SET_EQ_SING] -QED - -(* Define the interleave operation on a list. *) -Definition interleave_def: - interleave x ls = IMAGE (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) -End -(* make this an infix operator *) -val _ = set_fixity "interleave" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) -(* interleave_def; -val it = |- !x ls. x interleave ls = - IMAGE (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)): thm *) - -(* -> EVAL ``2 interleave [0; 1]``; -val it = |- 2 interleave [0; 1] = {[0; 1; 2]; [0; 2; 1]; [2; 0; 1]}: thm -*) - -(* Theorem: x interleave ls = {TAKE k ls ++ x::DROP k ls | k | k <= LENGTH ls} *) -(* Proof: by interleave_def, EXTENSION. *) -Theorem interleave_alt: - !ls x. x interleave ls = {TAKE k ls ++ x::DROP k ls | k | k <= LENGTH ls} -Proof - simp[interleave_def, EXTENSION] >> - metis_tac[LESS_EQ_IFF_LESS_SUC] -QED - -(* Theorem: y IN x interleave ls <=> - ?k. k <= LENGTH ls /\ y = TAKE k ls ++ x::DROP k ls *) -(* Proof: by interleave_alt, IN_IMAGE. *) -Theorem interleave_element: - !ls x y. y IN x interleave ls <=> - ?k. k <= LENGTH ls /\ y = TAKE k ls ++ x::DROP k ls -Proof - simp[interleave_alt] >> - metis_tac[] -QED - -(* Theorem: x interleave [] = {[x]} *) -(* Proof: - x interleave [] - = IMAGE (\k. TAKE k [] ++ x::DROP k []) (upto (LENGTH [])) - by interleave_def - = IMAGE (\k. [] ++ x::[]) (upto 0) by TAKE_nil, DROP_nil, LENGTH - = IMAGE (\k. [x]) (count 1) by APPEND, notation of upto - = IMAGE (\k. [x]) {0} by COUNT_1 - = [x] by IMAGE_DEF -*) -Theorem interleave_nil: - !x. x interleave [] = {[x]} -Proof - rw[interleave_def, EXTENSION] >> - metis_tac[DECIDE``0 < 1``] -QED - -(* Theorem: y IN (x interleave ls) ==> LENGTH y = 1 + LENGTH ls *) -(* Proof: - LENGTH y - = LENGTH (TAKE k ls ++ x::DROP k ls) by interleave_element, for some k - = LENGTH (TAKE k ls) + LENGTH (x::DROP k ls) by LENGTH_APPEND - = k + LENGTH (x :: DROP k ls) by LENGTH_TAKE, k <= LENGTH ls - = k + (1 + LENGTH (DROP k ls)) by LENGTH - = k + (1 + (LENGTH ls - k)) by LENGTH_DROP - = 1 + LENGTH ls by arithmetic, k <= LENGTH ls -*) -Theorem interleave_length: - !ls x y. y IN (x interleave ls) ==> LENGTH y = 1 + LENGTH ls -Proof - rw_tac bool_ss[interleave_element] >> - simp[] -QED - -(* Theorem: ALL_DISTINCT (x::ls) /\ y IN (x interleave ls) ==> ALL_DISTINCT y *) -(* Proof: - By interleave_def, IN_IMAGE, this is to show; - ALL_DISTINCT (TAKE k ls ++ x::DROP k ls) - To apply ALL_DISTINCT_APPEND, need to show: - (1) ~MEM x ls /\ MEM e (TAKE k ls) /\ MEM e (x::DROP k ls) ==> F - MEM e (x::DROP k ls) - <=> e = x \/ MEM e (DROP k ls) by MEM - MEM e (TAKE k ls) - ==> MEM e ls by MEM_TAKE - If e = x, - this contradicts ~MEM x ls. - If MEM e (DROP k ls), - with MEM e (TAKE k ls) - and ALL_DISTINCT ls gives F by ALL_DISTINCT_TAKE_DROP - (2) ALL_DISTINCT (TAKE k ls), true by ALL_DISTINCT_TAKE - (3) ~MEM x ls ==> ALL_DISTINCT (x::DROP k ls) - ALL_DISTINCT (x::DROP k ls) - <=> ~MEM x (DROP k ls) /\ - ALL_DISTINCT (DROP k ls) by ALL_DISTINCT - <=> ~MEM x (DROP k ls) /\ T by ALL_DISTINCT_DROP - ==> ~MEM x ls /\ T by MEM_DROP_IMP - ==> T /\ T = T -*) -Theorem interleave_distinct: - !ls x y. ALL_DISTINCT (x::ls) /\ y IN (x interleave ls) ==> ALL_DISTINCT y -Proof - rw_tac bool_ss[interleave_def, IN_IMAGE] >> - irule (ALL_DISTINCT_APPEND |> SPEC_ALL |> #2 o EQ_IMP_RULE) >> - rpt strip_tac >| [ - fs[] >- - metis_tac[MEM_TAKE] >> - metis_tac[ALL_DISTINCT_TAKE_DROP], - fs[ALL_DISTINCT_TAKE], - fs[ALL_DISTINCT_DROP] >> - metis_tac[MEM_DROP_IMP] - ] -QED - -(* Theorem: ALL_DISTINCT ls /\ ~(MEM x ls) /\ - y IN (x interleave ls) ==> ALL_DISTINCT y *) -(* Proof: by interleave_distinct, ALL_DISTINCT. *) -Theorem interleave_distinct_alt: - !ls x y. ALL_DISTINCT ls /\ ~(MEM x ls) /\ - y IN (x interleave ls) ==> ALL_DISTINCT y -Proof - metis_tac[interleave_distinct, ALL_DISTINCT] -QED - -(* Theorem: y IN x interleave ls ==> set y = set (x::ls) *) -(* Proof: - Note y = TAKE k ls ++ x::DROP k ls by interleave_element, for some k - Let u = TAKE k ls, v = DROP k ls. - set y - = set (u ++ x::v) by above - = set u UNION set (x::v) by LIST_TO_SET_APPEND - = set u UNION (x INSERT set v) by LIST_TO_SET - = (x INSERT set v) UNION set u by UNION_COMM - = x INSERT (set v UNION set u) by INSERT_UNION_EQ - = x INSERT (set u UNION set v) by UNION_COMM - = x INSERT (set (u ++ v)) by LIST_TO_SET_APPEND - = x INSERT set ls by TAKE_DROP - = set (x::ls) by LIST_TO_SET -*) -Theorem interleave_set: - !ls x y. y IN x interleave ls ==> set y = set (x::ls) -Proof - rw_tac bool_ss[interleave_element] >> - qabbrev_tac `u = TAKE k ls` >> - qabbrev_tac `v = DROP k ls` >> - `set (u ++ x::v) = set u UNION set (x::v)` by rw[] >> - `_ = set u UNION (x INSERT set v)` by rw[] >> - `_ = (x INSERT set v) UNION set u` by rw[UNION_COMM] >> - `_ = x INSERT (set v UNION set u)` by rw[INSERT_UNION_EQ] >> - `_ = x INSERT (set u UNION set v)` by rw[UNION_COMM] >> - `_ = x INSERT (set (u ++ v))` by rw[] >> - `_ = x INSERT set ls` by metis_tac[TAKE_DROP] >> - simp[] -QED - -(* Theorem: y IN x interleave ls ==> set y = x INSERT set ls *) -(* Proof: - Note set y = set (x::ls) by interleave_set - = x INSERT set ls by LIST_TO_SET -*) -Theorem interleave_set_alt: - !ls x y. y IN x interleave ls ==> set y = x INSERT set ls -Proof - metis_tac[interleave_set, LIST_TO_SET] -QED - -(* Theorem: (x::ls) IN x interleave ls *) -(* Proof: - (x::ls) IN x interleave ls - <=> ?k. x::ls = TAKE k ls ++ [x] ++ DROP k ls /\ k < SUC (LENGTH ls) - by interleave_def - Take k = 0. - Then 0 < SUC (LENGTH ls) by SUC_POS - and TAKE 0 ls ++ [x] ++ DROP 0 ls - = [] ++ [x] ++ ls by TAKE_0, DROP_0 - = x::ls by APPEND -*) -Theorem interleave_has_cons: - !ls x. (x::ls) IN x interleave ls -Proof - rw[interleave_def] >> - qexists_tac `0` >> - simp[] -QED - -(* Theorem: x interleave ls <> EMPTY *) -(* Proof: - Note (x::ls) IN x interleave ls by interleave_has_cons - Thus x interleave ls <> {} by MEMBER_NOT_EMPTY -*) -Theorem interleave_not_empty: - !ls x. x interleave ls <> EMPTY -Proof - metis_tac[interleave_has_cons, MEMBER_NOT_EMPTY] -QED - -(* -MEM_APPEND_lemma -|- !a b c d x. a ++ [x] ++ b = c ++ [x] ++ d /\ ~MEM x b /\ ~MEM x a ==> - a = c /\ b = d -*) - -(* Theorem: ~MEM n x /\ ~MEM n y ==> (n interleave x = n interleave y <=> x = y) *) -(* Proof: - Let f = (\k. TAKE k x ++ n::DROP k x), - g = (\k. TAKE k y ++ n::DROP k y). - By interleave_def, this is to show: - IMAGE f (upto (LENGTH x)) = IMAGE g (upto (LENGTH y)) <=> x = y - Only-part part is trivial. - For the if part, - Note 0 IN (upto (LENGTH x) by SUC_POS, IN_COUNT - so f 0 IN IMAGE f (upto (LENGTH x)) - thus ?k. k < SUC (LENGTH y) /\ f 0 = g k by IN_IMAGE, IN_COUNT - so n::x = TAKE k y ++ [n] ++ DROP k y by notation of f 0 - but n::x = TAKE 0 x ++ [n] ++ DROP 0 x by TAKE_0, DROP_0 - and ~MEM n (TAKE 0 x) /\ ~MEM n (DROP 0 x) by TAKE_0, DROP_0 - so TAKE 0 x = TAKE k y /\ - DROP 0 x = DROP k y by MEM_APPEND_lemma - or x = y by TAKE_DROP -*) -Theorem interleave_eq: - !n x y. ~MEM n x /\ ~MEM n y ==> (n interleave x = n interleave y <=> x = y) -Proof - rw[interleave_def, EQ_IMP_THM] >> - qabbrev_tac `f = \k. TAKE k x ++ n::DROP k x` >> - qabbrev_tac `g = \k. TAKE k y ++ n::DROP k y` >> - `f 0 IN IMAGE f (upto (LENGTH x))` by fs[] >> - `?k. k < SUC (LENGTH y) /\ f 0 = g k` by metis_tac[IN_IMAGE, IN_COUNT] >> - fs[Abbr`f`, Abbr`g`] >> - `n::x = TAKE 0 x ++ [n] ++ DROP 0 x` by rw[] >> - `~MEM n (TAKE 0 x) /\ ~MEM n (DROP 0 x)` by rw[] >> - metis_tac[MEM_APPEND_lemma, TAKE_DROP] -QED - -(* Theorem: ~MEM x l1 /\ l1 <> l2 ==> DISJOINT (x interleave l1) (x interleave l2) *) -(* Proof: - Use DISJOINT_DEF, by contradiction, suppose y is in both. - Then ?k h. k <= LENGTH l1 and h <= LENGTH l2 - with y = TAKE k l1 ++ [x] ++ DROP k l1 by interleave_element - and y = TAKE h l2 ++ [x] ++ DROP h l2 by interleave_element - Now ~MEM x (TAKE k l1) by MEM_TAKE - and ~MEM x (DROP k l1) by MEM_DROP_IMP - Thus TAKE k l1 = TAKE h l2 /\ - DROP k l1 = DROP h l2 by MEM_APPEND_lemma - or l1 = l2 by TAKE_DROP - but this contradicts l1 <> l2. -*) -Theorem interleave_disjoint: - !l1 l2 x. ~MEM x l1 /\ l1 <> l2 ==> DISJOINT (x interleave l1) (x interleave l2) -Proof - rw[interleave_def, DISJOINT_DEF, EXTENSION] >> - spose_not_then strip_assume_tac >> - `~MEM x (TAKE k l1) /\ ~MEM x (DROP k l1)` by metis_tac[MEM_TAKE, MEM_DROP_IMP] >> - metis_tac[MEM_APPEND_lemma, TAKE_DROP] -QED - -(* Theorem: FINITE (x interleave ls) *) -(* Proof: - Let f = (\k. TAKE k ls ++ x::DROP k ls), - n = LENGTH ls. - FINITE (x interleave ls) - <=> FINITE (IMAGE f (upto n)) by interleave_def - <=> T by IMAGE_FINITE, FINITE_COUNT -*) -Theorem interleave_finite: - !ls x. FINITE (x interleave ls) -Proof - simp[interleave_def, IMAGE_FINITE] -QED - -(* Theorem: ~MEM x ls ==> - INJ (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) univ(:'a list) *) -(* Proof: - Let n = LENGTH ls, - s = upto n, - f = (\k. TAKE k ls ++ x::DROP k ls). - By INJ_DEF, this is to show: - (1) k IN s ==> f k IN univ(:'a list), true by types. - (2) k IN s /\ h IN s /\ f k = f h ==> k = h. - Note k <= LENGTH ls by IN_COUNT, k IN s - and h <= LENGTH ls by IN_COUNT, h IN s - and ls = TAKE k ls ++ DROP k ls by TAKE_DROP - so ~MEM x (TAKE k ls) /\ - ~MEM x (DROP k ls) by MEM_APPEND, ~MEM x ls - Thus TAKE k ls = TAKE h ls by MEM_APPEND_lemma - ==> k = h by LENGTH_TAKE - -MEM_APPEND_lemma -|- !a b c d x. a ++ [x] ++ b = c ++ [x] ++ d /\ - ~MEM x b /\ ~MEM x a ==> a = c /\ b = d -*) -Theorem interleave_count_inj: - !ls x. ~MEM x ls ==> - INJ (\k. TAKE k ls ++ x::DROP k ls) (upto (LENGTH ls)) univ(:'a list) -Proof - rw[INJ_DEF] >> - `k <= LENGTH ls /\ k' <= LENGTH ls` by fs[] >> - `~MEM x (TAKE k ls) /\ ~MEM x (DROP k ls)` by metis_tac[TAKE_DROP, MEM_APPEND] >> - metis_tac[MEM_APPEND_lemma, LENGTH_TAKE] -QED - -(* Theorem: ~MEM x ls ==> CARD (x interleave ls) = 1 + LENGTH ls *) -(* Proof: - Let f = (\k. TAKE k ls ++ x::DROP k ls), - n = LENGTH ls. - Note FINITE (upto n) by FINITE_COUNT - and INJ f (upto n) univ(:'a list) - by interleave_count_inj, ~MEM x ls - CARD (x interleave ls) - = CARD (IMAGE f (upto n)) by interleave_def - = CARD (upto n) by INJ_CARD_IMAGE - = SUC n = 1 + n by CARD_COUNT, ADD1 -*) -Theorem interleave_card: - !ls x. ~MEM x ls ==> CARD (x interleave ls) = 1 + LENGTH ls -Proof - rw[interleave_def] >> - imp_res_tac interleave_count_inj >> - qabbrev_tac `n = LENGTH ls` >> - qabbrev_tac `s = upto n` >> - qabbrev_tac `f = (\k. TAKE k ls ++ x::DROP k ls)` >> - `FINITE s` by rw[Abbr`s`] >> - metis_tac[INJ_CARD_IMAGE, CARD_COUNT, ADD1] -QED - -(* Note: - interleave_distinct, interleave_length, and interleave_set - are effects after interleave. Now we need a kind of inverse: - deduce the effects before interleave. -*) - -(* Idea: a member h in a distinct list is the interleave of h with a smaller one. *) - -(* Theorem: ALL_DISTINCT ls /\ h IN set ls ==> - ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ set t = (set ls) DELETE h *) -(* Proof: - By induction on ls. - Base: ALL_DISTINCT [] /\ MEM h [] ==> ?t. ... - Since MEM h [] = F, this is true by MEM - Step: (ALL_DISTINCT ls /\ MEM h ls ==> - ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ set t = set ls DELETE h) ==> - !h'. ALL_DISTINCT (h'::ls) /\ MEM h (h'::ls) ==> - ?t. ALL_DISTINCT t /\ h'::ls IN h interleave t /\ set t = set (h'::ls) DELETE h - If h' = h, - Note ~MEM h ls /\ ALL_DISTINCT ls by ALL_DISTINCT - Take this ls, - Then set (h::ls) DELETE h - = (h INSERT set ls) DELETE h by LIST_TO_SET - = set ls by INSERT_DELETE_NON_ELEMENT - and h::ls IN h interleave ls by interleave_element, take k = 0. - If h' <> h, - Note ~MEM h' ls /\ ALL_DISTINCT ls by ALL_DISTINCT - and MEM h ls by MEM, h <> h' - Thus ?t. ALL_DISTINCT t /\ - ls IN h interleave t /\ - set t = set ls DELETE h by induction hypothesis - Note ~MEM h' t by set t = set ls DELETE h, ~MEM h' ls - Take this (h'::t), - Then ALL_DISTINCT (h'::t) by ALL_DISTINCT, ~MEM h' t - and set (h'::ls) DELETE h - = (h' INSERT set ls) DELETE h by LIST_TO_SET - = h' INSERT (set ls DELETE h) by DELETE_INSERT, h' <> h - = h' INSERT set t by above - = set (h'::t) - and h'::ls IN h interleave t by interleave_element, - take k = SUC k from ls IN h interleave t -*) -Theorem interleave_revert: - !ls h. ALL_DISTINCT ls /\ h IN set ls ==> - ?t. ALL_DISTINCT t /\ ls IN h interleave t /\ set t = (set ls) DELETE h -Proof - rpt strip_tac >> - Induct_on `ls` >- - simp[] >> - rpt strip_tac >> - Cases_on `h' = h` >| [ - fs[] >> - qexists_tac `ls` >> - simp[INSERT_DELETE_NON_ELEMENT] >> - simp[interleave_element] >> - qexists_tac `0` >> - simp[], - fs[] >> - `~MEM h' t` by fs[] >> - qexists_tac `h'::t` >> - simp[DELETE_INSERT] >> - fs[interleave_element] >> - qexists_tac `SUC k` >> - simp[] - ] -QED - -(* A useful corollary for set s = count n. *) - -(* Theorem: ALL_DISTINCT ls /\ set ls = upto n ==> - ?t. ALL_DISTINCT t /\ ls IN n interleave t /\ set t = count n *) -(* Proof: - Note MEM n ls by set ls = upto n - so ?t. ALL_DISTINCT t /\ - ls IN n interleave t /\ - set t = set ls DELETE n by interleave_revert - = (upto n) DELETE n by given - = count n by upto_delete -*) -Theorem interleave_revert_count: - !ls n. ALL_DISTINCT ls /\ set ls = upto n ==> - ?t. ALL_DISTINCT t /\ ls IN n interleave t /\ set t = count n -Proof - rpt strip_tac >> - `MEM n ls` by fs[] >> - drule_then strip_assume_tac interleave_revert >> - first_x_assum (qspec_then `n` strip_assume_tac) >> - metis_tac[upto_delete] -QED - -(* Theorem: perm_count (SUC n) = - BIGUNION (IMAGE ($interleave n) (perm_count n)) *) -(* Proof: - By induction on n. - Base: perm_count (SUC 0) = - BIGUNION (IMAGE ($interleave 0) (perm_count 0)) - LHS = perm_count (SUC 0) - = perm_count 1 by ONE - = {[0]} by perm_count_1 - RHS = BIGUNION (IMAGE ($interleave 0) (perm_count 0)) - = BIGUNION (IMAGE ($interleave 0) {[]} by perm_count_0 - = BIGUNION {0 interleave []} by IMAGE_SING - = BIGUNION {{[0]}} by interleave_nil - = {[0]} = LHS by BIGUNION_SING - Step: perm_count (SUC n) = BIGUNION (IMAGE ($interleave n) (perm_count n)) ==> - perm_count (SUC (SUC n)) = - BIGUNION (IMAGE ($interleave (SUC n)) (perm_count (SUC n))) - Let f = $interleave (SUC n), - s = perm_count n, t = perm_count (SUC n). - y IN BIGUNION (IMAGE f t) - <=> ?x. x IN t /\ y IN f x by IN_BIGUNION_IMAGE - <=> ?x. (?z. z IN s /\ x IN n interleave z) /\ y IN (SUC n) interleave x - by IN_BIGUNION_IMAGE, induction hypothesis - <=> ?x z. ALL_DISTINCT z /\ set z = count n /\ - x IN n interleave z /\ - y IN (SUC n) interleave x by perm_count_element - If part: y IN perm_count (SUC (SUC n)) ==> ?x and z. - Note ALL_DISTINCT y /\ - set y = count (SUC (SUC n)) by perm_count_element - Then ?x. ALL_DISTINCT x /\ y IN (SUC n) interleave x /\ set x = upto n - by interleave_revert_count - so ?z. ALL_DISTINCT z /\ x IN n interleave z /\ set z = count n - by interleave_revert_count - Take these x and z. - Only-if part: ?x and z ==> y IN perm_count (SUC (SUC n)) - Note ~MEM n z by set z = count n, COUNT_NOT_SELF - ==> ALL_DISTINCT x /\ by interleave_distinct_alt - set x = upto n by interleave_set_alt, COUNT_SUC - Note ~MEM (SUC n) x by set x = upto n, COUNT_NOT_SELF - ==> ALL_DISTINCT y /\ by interleave_distinct_alt - set y = count (SUC (SUC n)) by interleave_set_alt, COUNT_SUC - ==> y IN perm_count (SUC (SUC n)) by perm_count_element -*) -Theorem perm_count_suc: - !n. perm_count (SUC n) = - BIGUNION (IMAGE ($interleave n) (perm_count n)) -Proof - Induct >| [ - rw[perm_count_0, perm_count_1] >> - simp[interleave_nil], - rw[IN_BIGUNION_IMAGE, EXTENSION, EQ_IMP_THM] >| [ - imp_res_tac perm_count_element >> - `?y. ALL_DISTINCT y /\ x IN (SUC n) interleave y /\ set y = upto n` by rw[interleave_revert_count] >> - `?t. ALL_DISTINCT t /\ y IN n interleave t /\ set t = count n` by rw[interleave_revert_count] >> - (qexists_tac `y` >> simp[]) >> - (qexists_tac `t` >> simp[]) >> - simp[perm_count_element], - fs[perm_count_element] >> - `~MEM n x''` by fs[] >> - `ALL_DISTINCT x' /\ set x' = upto n` by metis_tac[interleave_distinct_alt, interleave_set_alt, COUNT_SUC] >> - `~MEM (SUC n) x'` by fs[] >> - metis_tac[interleave_distinct_alt, interleave_set_alt, COUNT_SUC] - ] - ] -QED - -(* Theorem: perm_count (n + 1) = - BIGUNION (IMAGE ($interleave n) (perm_count n)) *) -(* Proof: by perm_count_suc, GSYM ADD1. *) -Theorem perm_count_suc_alt: - !n. perm_count (n + 1) = - BIGUNION (IMAGE ($interleave n) (perm_count n)) -Proof - simp[perm_count_suc, GSYM ADD1] -QED - -(* Theorem: perm_count n = - if n = 0 then {[]} - else BIGUNION (IMAGE ($interleave (n - 1)) (perm_count (n - 1))) *) -(* Proof: by perm_count_0, perm_count_suc. *) -Theorem perm_count_eqn[compute]: - !n. perm_count n = - if n = 0 then {[]} - else BIGUNION (IMAGE ($interleave (n - 1)) (perm_count (n - 1))) -Proof - rw[perm_count_0] >> - metis_tac[perm_count_suc, num_CASES, SUC_SUB1] -QED - -(* -> EVAL ``perm_count 3``; -val it = |- perm_count 3 = -{[0; 1; 2]; [0; 2; 1]; [2; 0; 1]; [1; 0; 2]; [1; 2; 0]; [2; 1; 0]}: thm -*) - -(* Historical note. -This use of interleave to list all permutations is called -the Steinhaus–Johnson–Trotter algorithm, due to re-discovery by various people. -Outside mathematics, this method was known already to 17th-century English change ringers. -Equivalently, this algorithm finds a Hamiltonian cycle in the permutohedron. - -Steinhaus–Johnson–Trotter algorithm -https://en.wikipedia.org/wiki/Steinhaus–Johnson–Trotter_algorithm - -1677 A book by Fabian Stedman lists the solutions for up to six bells. -1958 A book by Steinhaus describes a related puzzle of generating all permutations by a system of particles. -Selmer M. Johnson and Hale F. Trotter discovered the algorithm independently of each other in the early 1960s. -1962 Hale F. Trotter, "Algorithm 115: Perm", August 1962. -1963 Selmer M. Johnson, "Generation of permutations by adjacent transposition". - -*) - -(* Theorem: perm 0 = 1 *) -(* Proof: - perm 0 - = CARD (perm_count 0) by perm_def - = CARD {[]} by perm_count_0 - = 1 by CARD_SING -*) -Theorem perm_0: - perm 0 = 1 -Proof - simp[perm_def, perm_count_0] -QED - -(* Theorem: perm 1 = 1 *) -(* Proof: - perm 1 - = CARD (perm_count 1) by perm_def - = CARD {[0]} by perm_count_1 - = 1 by CARD_SING -*) -Theorem perm_1: - perm 1 = 1 -Proof - simp[perm_def, perm_count_1] -QED - -(* Theorem: e IN IMAGE ($interleave n) (perm_count n) ==> FINITE e *) -(* Proof: - e IN IMAGE ($interleave n) (perm_count n) - <=> ?ls. ls IN perm_count n /\ - e = n interleave ls by IN_IMAGE - Thus FINITE e by interleave_finite -*) -Theorem perm_count_interleave_finite: - !n e. e IN IMAGE ($interleave n) (perm_count n) ==> FINITE e -Proof - rw[] >> - simp[interleave_finite] -QED - -(* Theorem: e IN IMAGE ($interleave n) (perm_count n) ==> CARD e = n + 1 *) -(* Proof: - e IN IMAGE ($interleave n) (perm_count n) - <=> ?ls. ls IN perm_count n /\ - e = n interleave ls by IN_IMAGE - Note ~MEM n ls by perm_count_element_no_self - and LENGTH ls = n by perm_count_element_length - Thus CARD e = n + 1 by interleave_card, ~MEM n ls -*) -Theorem perm_count_interleave_card: - !n e. e IN IMAGE ($interleave n) (perm_count n) ==> CARD e = n + 1 -Proof - rw[] >> - `~MEM n x` by rw[perm_count_element_no_self] >> - `LENGTH x = n` by rw[perm_count_element_length] >> - simp[interleave_card] -QED - -(* Theorem: PAIR_DISJOINT (IMAGE ($interleave n) (perm_count n)) *) -(* Proof: - By IN_IMAGE, this is to show: - x IN perm_count n /\ y IN perm_count n /\ - n interleave x <> n interleave y ==> - DISJOINT (n interleave x) (n interleave y) - By contradiction, suppose there is a list ls in both. - Then x = y by interleave_disjoint - This contradicts n interleave x <> n interleave y. -*) -Theorem perm_count_interleave_disjoint: - !n e. PAIR_DISJOINT (IMAGE ($interleave n) (perm_count n)) -Proof - rw[perm_count_def] >> - `~MEM n x` by fs[] >> - metis_tac[interleave_disjoint] -QED - -(* Theorem: INJ ($interleave n) (perm_count n) univ(:(num list -> bool)) *) -(* Proof: - By INJ_DEF, this is to show: - (1) x IN perm_count n ==> n interleave x IN univ - This is true by type. - (2) x IN perm_count n /\ y IN perm_count n /\ - n interleave x = n interleave y ==> x = y - Note ~MEM n x by perm_count_element_no_self - and ~MEM n y by perm_count_element_no_self - Thus x = y by interleave_eq -*) -Theorem perm_count_interleave_inj: - !n. INJ ($interleave n) (perm_count n) univ(:(num list -> bool)) -Proof - rw[INJ_DEF, perm_count_def, interleave_eq] -QED - -(* Theorem: perm (SUC n) = (SUC n) * perm n *) -(* Proof: - Let f = $interleave n, - s = IMAGE f (perm_count n). - Note FINITE (perm_count n) by perm_count_finite - so FINITE s by IMAGE_FINITE - and !e. e IN s ==> - FINITE e /\ by perm_count_interleave_finite - CARD e = n + 1 by perm_count_interleave_card - and PAIR_DISJOINT s by perm_count_interleave_disjoint - and INJ f (perm_count n) univ(:(num list -> bool)) - by perm_count_interleave_inj - perm (SUC n) - = CARD (perm_count (SUC n)) by perm_def - = CARD (BIGUNION s) by perm_count_suc - = CARD s * (n + 1) by CARD_BIGUNION_SAME_SIZED_SETS - = CARD (perm_count n) * (n + 1) by INJ_CARD_IMAGE - = perm n * (n + 1) by perm_def - = (SUC n) * perm n by MULT_COMM, ADD1 -*) -Theorem perm_suc: - !n. perm (SUC n) = (SUC n) * perm n -Proof - rpt strip_tac >> - qabbrev_tac `f = $interleave n` >> - qabbrev_tac `s = IMAGE f (perm_count n)` >> - `FINITE (perm_count n)` by rw[perm_count_finite] >> - `FINITE s` by rw[Abbr`s`] >> - `!e. e IN s ==> FINITE e /\ CARD e = n + 1` - by metis_tac[perm_count_interleave_finite, perm_count_interleave_card] >> - `PAIR_DISJOINT s` by metis_tac[perm_count_interleave_disjoint] >> - `INJ f (perm_count n) univ(:(num list -> bool))` by rw[perm_count_interleave_inj, Abbr`f`] >> - simp[perm_def] >> - `CARD (perm_count (SUC n)) = CARD (BIGUNION s)` by rw[perm_count_suc, Abbr`s`, Abbr`f`] >> - `_ = CARD s * (n + 1)` by rw[CARD_BIGUNION_SAME_SIZED_SETS] >> - `_ = CARD (perm_count n) * (n + 1)` by metis_tac[INJ_CARD_IMAGE] >> - simp[ADD1] -QED - -(* Theorem: perm (n + 1) = (n + 1) * perm n *) -(* Proof: by perm_suc, ADD1 *) -Theorem perm_suc_alt: - !n. perm (n + 1) = (n + 1) * perm n -Proof - simp[perm_suc, GSYM ADD1] -QED - -(* Theorem: perm 0 = 1 /\ !n. perm (n + 1) = (n + 1) * perm n *) -(* Proof: by perm_0, perm_suc_alt *) -Theorem perm_alt: - perm 0 = 1 /\ !n. perm (n + 1) = (n + 1) * perm n -Proof - simp[perm_0, perm_suc_alt] -QED - -(* Theorem: perm n = FACT n *) -(* Proof: by FACT_iff, perm_alt. *) -Theorem perm_eq_fact[compute]: - !n. perm n = FACT n -Proof - metis_tac[FACT_iff, perm_alt, ADD1] -QED - -(* This is fantastic! *) - -(* -> EVAL ``perm 3``; = 6 -> EVAL ``MAP perm [0 .. 10]``; = -[1; 1; 2; 6; 24; 120; 720; 5040; 40320; 362880; 3628800] -*) - -(* ------------------------------------------------------------------------- *) -(* Permutations of a set. *) -(* ------------------------------------------------------------------------- *) - -(* Note: SET_TO_LIST, using CHOICE and REST, is not effective for computations. -SET_TO_LIST_THM -|- FINITE s ==> - SET_TO_LIST s = if s = {} then [] else CHOICE s::SET_TO_LIST (REST s) -*) - -(* Define the set of permutation lists of a set. *) -Definition perm_set_def[nocompute]: - perm_set s = {ls | ALL_DISTINCT ls /\ set ls = s} -End -(* use [nocompute] as this is not effective for evalutaion. *) -(* Note: this cannot be made effective, unless sort s to list by some ordering. *) - -(* Theorem: ls IN perm_set s <=> ALL_DISTINCT ls /\ set ls = s *) -(* Proof: perm_set_def *) -Theorem perm_set_element: - !ls s. ls IN perm_set s <=> ALL_DISTINCT ls /\ set ls = s -Proof - simp[perm_set_def] -QED - -(* Theorem: perm_set (count n) = perm_count n *) -(* Proof: by perm_count_def, perm_set_def. *) -Theorem perm_set_perm_count: - !n. perm_set (count n) = perm_count n -Proof - simp[perm_count_def, perm_set_def] -QED - -(* Theorem: perm_set {} = {[]} *) -(* Proof: - perm_set {} - = {ls | ALL_DISTINCT ls /\ set ls = {}} by perm_set_def - = {ls | ALL_DISTINCT ls /\ ls = []} by LIST_TO_SET_EQ_EMPTY - = {[]} by ALL_DISTINCT -*) -Theorem perm_set_empty: - perm_set {} = {[]} -Proof - rw[perm_set_def, EXTENSION] >> - metis_tac[ALL_DISTINCT] -QED - -(* Theorem: perm_set {x} = {[x]} *) -(* Proof: - perm_set {x} - = {ls | ALL_DISTINCT ls /\ set ls = {x}} by perm_set_def - = {ls | ls = [x]} by DISTINCT_LIST_TO_SET_EQ_SING - = {[x]} by notation -*) -Theorem perm_set_sing: - !x. perm_set {x} = {[x]} -Proof - simp[perm_set_def, DISTINCT_LIST_TO_SET_EQ_SING] -QED - -(* Theorem: perm_set s = {[]} <=> s = {} *) -(* Proof: - If part: perm_set s = {[]} ==> s = {} - By contradiction, suppose s <> {}. - ls IN perm_set s - <=> ALL_DISTINCT ls /\ set ls = s by perm_set_element - ==> ls <> [] by LIST_TO_SET_EQ_EMPTY - This contradicts perm_set s = {[]} by IN_SING - Only-if part: s = {} ==> perm_set s = {[]} - This is true by perm_set_empty -*) -Theorem perm_set_eq_empty_sing: - !s. perm_set s = {[]} <=> s = {} -Proof - rw[perm_set_empty, EQ_IMP_THM] >> - `[] IN perm_set s` by fs[] >> - fs[perm_set_element] -QED - -(* Theorem: FINITE s ==> (SET_TO_LIST s) IN perm_set s *) -(* Proof: - Let ls = SET_TO_LIST s. - Note ALL_DISTINCT ls by ALL_DISTINCT_SET_TO_LIST - and set ls = s by SET_TO_LIST_INV - Thus ls IN perm_set s by perm_set_element -*) -Theorem perm_set_has_self_list: - !s. FINITE s ==> (SET_TO_LIST s) IN perm_set s -Proof - simp[perm_set_element, ALL_DISTINCT_SET_TO_LIST, SET_TO_LIST_INV] -QED - -(* Theorem: FINITE s ==> perm_set s <> {} *) -(* Proof: - Let ls = SET_TO_LIST s. - Then ls IN perm_set s by perm_set_has_self_list - Thus perm_set s <> {} by MEMBER_NOT_EMPTY -*) -Theorem perm_set_not_empty: - !s. FINITE s ==> perm_set s <> {} -Proof - metis_tac[perm_set_has_self_list, MEMBER_NOT_EMPTY] -QED - -(* Theorem: perm_set (set ls) <> {} *) -(* Proof: - Note FINITE (set ls) by FINITE_LIST_TO_SET - so perm_set (set ls) <> {} by perm_set_not_empty -*) -Theorem perm_set_list_not_empty: - !ls. perm_set (set ls) <> {} -Proof - simp[FINITE_LIST_TO_SET, perm_set_not_empty] -QED - -(* Theorem: ls IN perm_set s /\ BIJ f s (count n) ==> MAP f ls IN perm_count n *) -(* Proof: - By perm_set_def, perm_count_def, this is to show: - (1) ALL_DISTINCT ls /\ BIJ f (set ls) (count n) ==> ALL_DISTINCT (MAP f ls) - Note INJ f (set ls) (count n) by BIJ_DEF - so ALL_DISTINCT (MAP f ls) by ALL_DISTINCT_MAP_INJ, INJ_DEF - (2) ALL_DISTINCT ls /\ BIJ f (set ls) (count n) ==> set (MAP f ls) = count n - Note SURJ f (set ls) (count n) by BIJ_DEF - so set (MAP f ls) - = IMAGE f (set ls) by LIST_TO_SET_MAP - = count n by IMAGE_SURJ -*) -Theorem perm_set_map_element: - !ls f s n. ls IN perm_set s /\ BIJ f s (count n) ==> MAP f ls IN perm_count n -Proof - rw[perm_set_def, perm_count_def] >- - metis_tac[ALL_DISTINCT_MAP_INJ, BIJ_IS_INJ] >> - simp[LIST_TO_SET_MAP] >> - fs[IMAGE_SURJ, BIJ_DEF] -QED - -(* Theorem: BIJ f s (count n) ==> - INJ (MAP f) (perm_set s) (perm_count n) *) -(* Proof: - By INJ_DEF, this is to show: - (1) x IN perm_set s ==> MAP f x IN perm_count n - This is true by perm_set_map_element - (2) x IN perm_set s /\ y IN perm_set s /\ MAP f x = MAP f y ==> x = y - Note LENGTH x = LENGTH y by LENGTH_MAP - By LIST_EQ, it remains to show: - !j. j < LENGTH x ==> EL j x = EL j y - Note EL j x IN s by perm_set_element, MEM_EL - and EL j y IN s by perm_set_element, MEM_EL - MAP f x = MAP f y - ==> EL j (MAP f x) = EL j (MAP f y) - ==> f (EL j x) = f (EL j y) by EL_MAP - ==> EL j x = EL j y by BIJ_IS_INJ -*) -Theorem perm_set_map_inj: - !f s n. BIJ f s (count n) ==> - INJ (MAP f) (perm_set s) (perm_count n) -Proof - rw[INJ_DEF] >- - metis_tac[perm_set_map_element] >> - irule LIST_EQ >> - `LENGTH x = LENGTH y` by metis_tac[LENGTH_MAP] >> - rw[] >> - `EL x' x IN s` by metis_tac[perm_set_element, MEM_EL] >> - `EL x' y IN s` by metis_tac[perm_set_element, MEM_EL] >> - metis_tac[EL_MAP, BIJ_IS_INJ] -QED - -(* Theorem: BIJ f s (count n) ==> - SURJ (MAP f) (perm_set s) (perm_count n) *) -(* Proof: - By SURJ_DEF, this is to show: - (1) x IN perm_set s ==> MAP f x IN perm_count n - This is true by perm_set_map_element - (2) x IN perm_count n ==> ?y. y IN perm_set s /\ MAP f y = x - Let y = MAP (LINV f s) x. Then to show: - (1) y IN perm_set s, - Note BIJ (LINV f s) (count n) s by BIJ_LINV_BIJ - By perm_set_element, perm_count_element, to show: - (1) ALL_DISTINCT (MAP (LINV f s) x) - Note INJ (LINV f s) (count n) s by BIJ_DEF - so ALL_DISTINCT (MAP (LINV f s) x) - by ALL_DISTINCT_MAP_INJ, INJ_DEF - (2) set (MAP (LINV f s) x) = s - Note SURJ (LINV f s) (count n) s by BIJ_DEF - so set (MAP (LINV f s) x) - = IMAGE (LINV f s) (set x) by LIST_TO_SET_MAP - = IMAGE (LINV f s) (count n) by set x = count n - = s by IMAGE_SURJ - (2) x IN perm_count n ==> MAP f (MAP (LINV f s) x) = x - Let g = f o LINV f s. - The goal is: MAP g x = x by MAP_COMPOSE - Note LENGTH (MAP g x) = LENGTH x by LENGTH_MAP - To apply LIST_EQ, just need to show: - !k. k < LENGTH x ==> - EL k (MAP g x) = EL k x - or to show: g (EL k x) = EL k x by EL_MAP - Now set x = count n by perm_count_element - so EL k x IN (count n) by MEM_EL - Thus g (EL k x) = EL k x by BIJ_LINV_INV -*) -Theorem perm_set_map_surj: - !f s n. BIJ f s (count n) ==> - SURJ (MAP f) (perm_set s) (perm_count n) -Proof - rw[SURJ_DEF] >- - metis_tac[perm_set_map_element] >> - qexists_tac `MAP (LINV f s) x` >> - rpt strip_tac >| [ - `BIJ (LINV f s) (count n) s` by rw[BIJ_LINV_BIJ] >> - fs[perm_set_element, perm_count_element] >> - rpt strip_tac >- - metis_tac[ALL_DISTINCT_MAP_INJ, BIJ_IS_INJ] >> - simp[LIST_TO_SET_MAP] >> - fs[IMAGE_SURJ, BIJ_DEF], - simp[MAP_COMPOSE] >> - qabbrev_tac `g = f o LINV f s` >> - irule LIST_EQ >> - `LENGTH (MAP g x) = LENGTH x` by rw[LENGTH_MAP] >> - rw[] >> - simp[EL_MAP] >> - fs[perm_count_element, Abbr`g`] >> - metis_tac[MEM_EL, BIJ_LINV_INV] - ] -QED - -(* Theorem: BIJ f s (count n) ==> - BIJ (MAP f) (perm_set s) (perm_count n) *) -(* Proof: - Note INJ (MAP f) (perm_set s) (perm_count n) by perm_set_map_inj - and SURJ (MAP f) (perm_set s) (perm_count n) by perm_set_map_surj - Thus BIJ (MAP f) (perm_set s) (perm_count n) by BIJ_DEF -*) -Theorem perm_set_map_bij: - !f s n. BIJ f s (count n) ==> - BIJ (MAP f) (perm_set s) (perm_count n) -Proof - simp[BIJ_DEF, perm_set_map_inj, perm_set_map_surj] -QED - -(* Theorem: FINITE s ==> perm_set s =b= perm_count (CARD s) *) -(* Proof: - Note ?f. BIJ f s (count (CARD s)) by bij_eq_count, FINITE s - Thus BIJ (MAP f) (perm_set s) (perm_count (CARD s)) - by perm_set_map_bij - showing perm_set s =b= perm_count (CARD s) by notation -*) -Theorem perm_set_bij_eq_perm_count: - !s. FINITE s ==> perm_set s =b= perm_count (CARD s) -Proof - rpt strip_tac >> - imp_res_tac bij_eq_count >> - metis_tac[perm_set_map_bij] -QED - -(* Theorem: FINITE s ==> FINITE (perm_set s) *) -(* Proof: - Note perm_set s =b= perm_count (CARD s) by perm_set_bij_eq_perm_count - and FINITE (perm_count (CARD s)) by perm_count_finite - so FINITE (perm_set s) by bij_eq_finite -*) -Theorem perm_set_finite: - !s. FINITE s ==> FINITE (perm_set s) -Proof - metis_tac[perm_set_bij_eq_perm_count, perm_count_finite, bij_eq_finite] -QED - -(* Theorem: FINITE s ==> CARD (perm_set s) = perm (CARD s) *) -(* Proof: - Note perm_set s =b= perm_count (CARD s) by perm_set_bij_eq_perm_count - and FINITE (perm_count (CARD s)) by perm_count_finite - so CARD (perm_set s) - = CARD (perm_count (CARD s)) by bij_eq_card - = perm (CARD s) by perm_def -*) -Theorem perm_set_card: - !s. FINITE s ==> CARD (perm_set s) = perm (CARD s) -Proof - metis_tac[perm_set_bij_eq_perm_count, perm_count_finite, bij_eq_card, perm_def] -QED - -(* This is a major result! *) - -(* Theorem: FINITE s ==> CARD (perm_set s) = FACT (CARD s) *) -(* Proof: by perm_set_card, perm_eq_fact. *) -Theorem perm_set_card_alt: - !s. FINITE s ==> CARD (perm_set s) = FACT (CARD s) -Proof - simp[perm_set_card, perm_eq_fact] -QED - -(* ------------------------------------------------------------------------- *) -(* Counting number of arrangements. *) -(* ------------------------------------------------------------------------- *) - -(* Define the set of choices of k-tuples of (count n). *) -Definition list_count_def[nocompute]: - list_count n k = - { ls | ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k} -End -(* use [nocompute] as this is not effective for evalutaion. *) -(* Note: if defined as: - list_count n k = { ls | (set ls) SUBSET (count n) /\ CARD (set ls) = k} -then non-distinct lists will be in the set, which is not desirable. -*) - -(* Define the number of choices of k-tuples of (count n). *) -Definition arrange_def[nocompute]: - arrange n k = CARD (list_count n k) -End -(* use [nocompute] as this is not effective for evalutaion. *) -(* make this an infix operator *) -val _ = set_fixity "arrange" (Infix(NONASSOC, 550)); (* higher than arithmetic op 500. *) -(* arrange_def; -val it = |- !n k. n arrange k = CARD (list_count n k): thm *) - -(* Theorem: list_count n k = - { ls | ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k} *) -(* Proof: - ls IN list_count n k - <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k - by list_count_def - <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k - by ALL_DISTINCT_CARD_LIST_TO_SET - Hence the sets are equal by EXTENSION. -*) -Theorem list_count_alt: - !n k. list_count n k = - { ls | ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k} -Proof - simp[list_count_def, EXTENSION] >> - metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET] -QED - -(* Theorem: ls IN list_count n k <=> - ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k *) -(* Proof: by list_count_def. *) -Theorem list_count_element: - !ls n k. ls IN list_count n k <=> - ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k -Proof - simp[list_count_def] -QED - -(* Theorem: ls IN list_count n k <=> - ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k *) -(* Proof: by list_count_alt. *) -Theorem list_count_element_alt: - !ls n k. ls IN list_count n k <=> - ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ CARD (set ls) = k -Proof - simp[list_count_alt] -QED - -(* Theorem: ls IN list_count n k ==> CARD (set ls) = k *) -(* Proof: - ls IN list_count n k - <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k - by list_count_element - ==> CARD (set ls) = k by ALL_DISTINCT_CARD_LIST_TO_SET -*) -Theorem list_count_element_set_card: - !ls n k. ls IN list_count n k ==> CARD (set ls) = k -Proof - simp[list_count_def, ALL_DISTINCT_CARD_LIST_TO_SET] -QED - -(* Theorem: list_count n k SUBSET necklace k n *) -(* Proof: - ls IN list_count n k - <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = k - by list_count_element - ==> (set ls) SUBSET (count n) /\ LENGTH ls = k - ==> ls IN necklace k n by necklace_def - Thus list_count n k SUBSET necklace k n by SUBSET_DEF -*) -Theorem list_count_subset: - !n k. list_count n k SUBSET necklace k n -Proof - simp[list_count_def, necklace_def, SUBSET_DEF] -QED - -(* Theorem: FINITE (list_count n k) *) -(* Proof: - Note list_count n k SUBSET necklace k n by list_count_subset - and FINITE (necklace k n) by necklace_finite - so FINITE (list_count n k) by SUBSET_FINITE -*) -Theorem list_count_finite: - !n k. FINITE (list_count n k) -Proof - metis_tac[list_count_subset, necklace_finite, SUBSET_FINITE] -QED - -(* Note: -list_count 4 2 has P(4,2) = 4 * 3 = 12 elements. -necklace 2 4 has 2 ** 4 = 16 elements. - -> EVAL ``necklace 2 4``; -val it = |- necklace 2 4 = - {[3; 3]; [3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 2]; [2; 1]; [2; 0]; - [1; 3]; [1; 2]; [1; 1]; [1; 0]; [0; 3]; [0; 2]; [0; 1]; [0; 0]}: thm -> EVAL ``IMAGE set (necklace 2 4)``; -val it = |- IMAGE set (necklace 2 4) = - {{3}; {2; 3}; {2}; {1; 3}; {1; 2}; {1}; {0; 3}; {0; 2}; {0; 1}; {0}}: -> EVAL ``IMAGE (\ls. if CARD (set ls) = 2 then ls else []) (necklace 2 4)``; -val it = |- IMAGE (\ls. if CARD (set ls) = 2 then ls else []) (necklace 2 4) = - {[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; - [1; 0]; [0; 3]; [0; 2]; [0; 1]; []}: thm -> EVAL ``let n = 4; k = 2 in (IMAGE (\ls. if CARD (set ls) = k then ls else []) (necklace k n)) DELETE []``; -val it = |- (let n = 4; k = 2 in -IMAGE (\ls. if CARD (set ls) = k then ls else []) (necklace k n) DELETE []) = - {[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; - [1; 0]; [0; 3]; [0; 2]; [0; 1]}: thm -> EVAL ``let n = 4; k = 2 in (IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n)) DELETE []``; -val it = |- (let n = 4; k = 2 in - IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE []) = - {[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; - [1; 0]; [0; 3]; [0; 2]; [0; 1]}: thm -*) - -(* Note: -P(n,k) = C(n,k) * k! -P(n,0) = C(n,0) * 0! = 1 -P(0,k+1) = C(0,k+1) * (k+1)! = 0 -*) - -(* Theorem: list_count n 0 = {[]} *) -(* Proof: - ls IN list_count n 0 - <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ LENGTH ls = 0 - by list_count_element - <=> ALL_DISTINCT ls /\ (set ls) SUBSET (count n) /\ ls = [] - by LENGTH_NIL - <=> T /\ T /\ ls = [] by ALL_DISTINCT, LIST_TO_SET, EMPTY_SUBSET - Thus list_count n 0 = {[]} by EXTENSION -*) -Theorem list_count_n_0: - !n. list_count n 0 = {[]} -Proof - rw[list_count_def, EXTENSION, EQ_IMP_THM] -QED - -(* Theorem: 0 < n ==> list_count 0 n = {} *) -(* Proof: - Note (list_count 0 n) SUBSET (necklace n 0) - by list_count_subset - but (necklace n 0) = {} by necklace_empty, 0 < n - Thus (list_count 0 n) = {} by SUBSET_EMPTY -*) -Theorem list_count_0_n: - !n. 0 < n ==> list_count 0 n = {} -Proof - metis_tac[list_count_subset, necklace_empty, SUBSET_EMPTY] -QED - -(* Theorem: list_count n n = perm_count n *) -(* Proof: - ls IN list_count n n - <=> ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = n - by list_count_element_alt - <=> ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = CARD (count n) - by CARD_COUNT - <=> ALL_DISTINCT ls /\ set ls SUBSET count n /\ set ls = count n - by SUBSET_CARD_EQ - <=> ALL_DISTINCT ls /\ set ls = count n by SUBSET_REFL - <=> ls IN perm_count n by perm_count_element -*) -Theorem list_count_n_n: - !n. list_count n n = perm_count n -Proof - rw_tac bool_ss[list_count_element_alt, EXTENSION] >> - `FINITE (count n) /\ CARD (count n) = n` by rw[] >> - metis_tac[SUBSET_REFL, SUBSET_CARD_EQ, perm_count_element] -QED - -(* Theorem: list_count n k = {} <=> n < k *) -(* Proof: - If part: list_count n k = {} ==> n < k - By contradiction, suppose k <= n. - Let ls = SET_TO_LIST (count k). - Note FINITE (count k) by FINITE_COUNT - Then ALL_DISTINCT ls by ALL_DISTINCT_SET_TO_LIST - and set ls = count k by SET_TO_LIST_INV - Now (count k) SUBSET (count n) by COUNT_SUBSET, k <= n - and CARD (count k) = k by CARD_COUNT - so ls IN list_count n k by list_count_element_alt - Thus list_count n k <> {} by MEMBER_NOT_EMPTY - which is a contradiction. - Only-if part: n < k ==> list_count n k = {} - By contradiction, suppose sub_count n k <> {}. - Then ?ls. ls IN list_count n k by MEMBER_NOT_EMPTY - ==> ALL_DISTINCT ls /\ set ls SUBSET count n /\ CARD (set ls) = k - by sub_count_element_alt - Note FINITE (count n) by FINITE_COUNT - so CARD (set ls) <= CARD (count n) - by CARD_SUBSET - ==> k <= n by CARD_COUNT - This contradicts n < k. -*) -Theorem list_count_eq_empty: - !n k. list_count n k = {} <=> n < k -Proof - rw[EQ_IMP_THM] >| [ - spose_not_then strip_assume_tac >> - qabbrev_tac `ls = SET_TO_LIST (count k)` >> - `FINITE (count k)` by rw[FINITE_COUNT] >> - `ALL_DISTINCT ls` by rw[ALL_DISTINCT_SET_TO_LIST, Abbr`ls`] >> - `set ls = count k` by rw[SET_TO_LIST_INV, Abbr`ls`] >> - `(count k) SUBSET (count n)` by rw[COUNT_SUBSET] >> - `CARD (count k) = k` by rw[] >> - metis_tac[list_count_element_alt, MEMBER_NOT_EMPTY], - spose_not_then strip_assume_tac >> - `?ls. ls IN list_count n k` by rw[MEMBER_NOT_EMPTY] >> - fs[list_count_element_alt] >> - `FINITE (count n)` by rw[] >> - `CARD (set ls) <= n` by metis_tac[CARD_SUBSET, CARD_COUNT] >> - decide_tac - ] -QED - -(* Theorem: 0 < k ==> - list_count n k = - IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] *) -(* Proof: - x IN IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] - <=> ?ls. x = (if ALL_DISTINCT ls then ls else []) /\ - LENGTH ls = k /\ set ls SUBSET count n) /\ x <> [] by IN_IMAGE, IN_DELETE - <=> ALL_DISTINCT x /\ LENGTH x = k /\ set x SUBSET count n by LENGTH_NIL, 0 < k, ls = x - <=> x IN list_count n k by list_count_element - Thus the two sets are equal by EXTENSION. -*) -Theorem list_count_by_image: - !n k. 0 < k ==> - list_count n k = - IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] -Proof - rw[list_count_def, necklace_def, EXTENSION] >> - (rw[EQ_IMP_THM] >> metis_tac[LENGTH_NIL, NOT_ZERO]) -QED - -(* Theorem: list_count n k = - if k = 0 then {[]} - else IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] *) -(* Proof: by list_count_n_0, list_count_by_image *) -Theorem list_count_eqn[compute]: - !n k. list_count n k = - if k = 0 then {[]} - else IMAGE (\ls. if ALL_DISTINCT ls then ls else []) (necklace k n) DELETE [] -Proof - rw[list_count_n_0, list_count_by_image] -QED - -(* -> EVAL ``list_count 3 2``; -val it = |- list_count 3 2 = {[2; 1]; [2; 0]; [1; 2]; [1; 0]; [0; 2]; [0; 1]}: thm -> EVAL ``list_count 4 2``; -val it = |- list_count 4 2 = -{[3; 2]; [3; 1]; [3; 0]; [2; 3]; [2; 1]; [2; 0]; [1; 3]; [1; 2]; [1; 0]; [0; 3]; [0; 2]; [0; 1]}: thm -*) - -(* Idea: define an equivalence relation feq set: set x = set y. - There are k! elements in each equivalence class. - Thus n arrange k = perm k * n choose k. *) - -(* Theorem: (feq set) equiv_on s *) -(* Proof: by feq_equiv. *) -Theorem feq_set_equiv: - !s. (feq set) equiv_on s -Proof - simp[feq_equiv] -QED - -(* -> EVAL ``list_count 3 2``; -val it = |- list_count 3 2 = {[2; 1]; [1; 2]; [2; 0]; [0; 2]; [1; 0]; [0; 1]}: thm -*) - -(* Theorem: ls IN list_count n k ==> - equiv_class (feq set) (list_count n k) ls = perm_set (set ls) *) -(* Proof: - Note ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k - by list_count_element - x IN equiv_class (feq set) (list_count n k) ls - <=> x IN (list_count n k) /\ (feq set) ls x by equiv_class_element - <=> x IN (list_count n k) /\ set ls = set x by feq_def - <=> ALL_DISTINCT x /\ set x SUBSET count n /\ LENGTH x = k /\ - set x = set ls by list_count_element - <=> ALL_DISTINCT x /\ LENGTH x = LENGTH ls /\ set x = set ls - by given - <=> ALL_DISTINCT x /\ set x = set ls by ALL_DISTINCT_CARD_LIST_TO_SET - <=> x IN perm_set (set ls) by perm_set_element -*) -Theorem list_count_set_eq_class: - !ls n k. ls IN list_count n k ==> - equiv_class (feq set) (list_count n k) ls = perm_set (set ls) -Proof - rw[list_count_def, perm_set_def, fequiv_def, Once EXTENSION] >> - rw[EQ_IMP_THM] >> - metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET] -QED - -(* Theorem: ls IN list_count n k ==> - CARD (equiv_class (feq set) (list_count n k) ls) = perm k *) -(* Proof: - Note ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k - by list_count_element - CARD (equiv_class (feq set) (list_count n k) ls) - = CARD (perm_set (set ls)) by list_count_set_eq_class - = perm (CARD (set ls)) by perm_set_card - = perm (LENGTH ls) by ALL_DISTINCT_CARD_LIST_TO_SET - = perm k by LENGTH ls = k -*) -Theorem list_count_set_eq_class_card: - !ls n k. ls IN list_count n k ==> - CARD (equiv_class (feq set) (list_count n k) ls) = perm k -Proof - rw[list_count_set_eq_class] >> - fs[list_count_element] >> - simp[perm_set_card, ALL_DISTINCT_CARD_LIST_TO_SET] -QED - -(* Theorem: e IN partition (feq set) (list_count n k) ==> CARD e = perm k *) -(* Proof: - By partition_element, this is to show: - ls IN list_count n k ==> - CARD (equiv_class (feq set) (list_count n k) ls) = perm k - This is true by list_count_set_eq_class_card. -*) -Theorem list_count_set_partititon_element_card: - !n k e. e IN partition (feq set) (list_count n k) ==> CARD e = perm k -Proof - rw_tac bool_ss [partition_element] >> - simp[list_count_set_eq_class_card] -QED - -(* Theorem: ls IN list_count n k ==> perm_set (set ls) <> {} *) -(* Proof: - Note (feq set) equiv_on (list_count n k) by feq_set_equiv - and perm_set (set ls) - = equiv_class (feq set) (list_count n k) ls by list_count_set_eq_class - <> {} by equiv_class_not_empty -*) -Theorem list_count_element_perm_set_not_empty: - !ls n k. ls IN list_count n k ==> perm_set (set ls) <> {} -Proof - metis_tac[list_count_set_eq_class, feq_set_equiv, equiv_class_not_empty] -QED - -(* This is more restrictive than perm_set_list_not_empty, hence not useful. *) - -(* Theorem: s IN (partition (feq set) (list_count n k)) ==> - (set o CHOICE) s IN (sub_count n k) *) -(* Proof: - s IN (partition (feq set) (list_count n k)) - <=> ?z. z IN list_count n k /\ - !x. x IN s <=> x IN list_count n k /\ set x = set z - by feq_partition_element - ==> z IN s, so s <> {} by MEMBER_NOT_EMPTY - Let ls = CHOICE s. - Then ls IN s by CHOICE_DEF - so ls IN list_count n k /\ set ls = set z - by implication - or ALL_DISTINCT ls /\ set ls SUBSET count n /\ LENGTH ls = k - by list_count_element - Note (set o CHOICE) s = set ls by o_THM - and CARD (set ls) = LENGTH ls by ALL_DISTINCT_CARD_LIST_TO_SET - so set ls IN (sub_count n k) by sub_count_element_alt -*) -Theorem list_count_set_map_element: - !s n k. s IN (partition (feq set) (list_count n k)) ==> - (set o CHOICE) s IN (sub_count n k) -Proof - rw[feq_partition_element] >> - `s <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - `(CHOICE s) IN s` by fs[CHOICE_DEF] >> - fs[list_count_element, sub_count_element] >> - metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET] -QED - -(* Theorem: INJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) *) -(* Proof: - Let R = feq set, - s = list_count n k, - t = sub_count n k. - By INJ_DEF, this is to show: - (1) x IN partition R s ==> (set o CHOICE) x IN t - This is true by list_count_set_map_element - (2) x IN partition R s /\ y IN partition R s /\ - (set o CHOICE) x = (set o CHOICE) y ==> x = y - Note ?u. u IN list_count n k - !ls. ls IN x <=> ls IN list_count n k /\ set ls = set u - by feq_partition_element - and ?v. v IN list_count n k - !ls. ls IN y <=> ls IN list_count n k /\ set ls = set v - by feq_partition_element - Thus u IN x, so x <> {} by MEMBER_NOT_EMPTY - and v IN y, so y <> {} by MEMBER_NOT_EMPTY - Let lx = CHOICE x IN x by CHOICE_DEF - and ly = CHOICE y IN y by CHOICE_DEF - With set lx = set ly by o_THM - Thus set lx = set u by implication - and set ly = set v by implication - so set u = set v by above - Thus x = y by EXTENSION -*) -Theorem list_count_set_map_inj: - !n k. INJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) -Proof - rw_tac bool_ss[INJ_DEF] >- - simp[list_count_set_map_element] >> - fs[feq_partition_element] >> - `x <> {} /\ y <> {}` by metis_tac[MEMBER_NOT_EMPTY] >> - `CHOICE x IN x /\ CHOICE y IN y` by fs[CHOICE_DEF] >> - `set z = set z'` by rfs[] >> - simp[EXTENSION] -QED - -(* Theorem: SURJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) *) -(* Proof: - Let R = feq set, - s = list_count n k, - t = sub_count n k. - By SURJ_DEF, this is to show: - (1) x IN partition R s ==> (set o CHOICE) x IN t - This is true by list_count_set_map_element - (2) x IN t ==> ?y. y IN partition R s /\ (set o CHOICE) y = x - Note x SUBSET count n /\ CARD x = k by sub_count_element - Thus FINITE x by SUBSET_FINITE, FINITE_COUNT - Let y = perm_set x. - To show; - (1) y IN partition R s - Note y IN partition R s - <=> ?ls. ls IN list_count n k /\ - !z. z IN perm_set x <=> z IN list_count n k /\ set z = set ls - by feq_partition_element - Let ls = SET_TO_LIST x. - Then ALL_DISTINCT ls by ALL_DISTINCT_SET_TO_LIST, FINITE x - and set ls = x by SET_TO_LIST_INV, FINITE x - so set ls SUBSET (count n) by above, x SUBSET count n - and LENGTH ls = k by SET_TO_LIST_CARD, FINITE x - so ls IN list_count n k by list_count_element - To show: !z. z IN perm_set x <=> z IN list_count n k /\ set z = set ls - z IN perm_set x - <=> ALL_DISTINCT z /\ set z = x by perm_set_element - <=> ALL_DISTINCT z /\ set z SUBSET count n /\ set z = x - by x SUBSET count n - <=> ALL_DISTINCT z /\ set z SUBSET count n /\ LENGTH z = CARD x - by ALL_DISTINCT_CARD_LIST_TO_SET - <=> z IN list_count n k /\ set z = set ls - by list_count_element, CARD x = k, set ls = x. - (2) (set o CHOICE) y = x - Note y <> {} by perm_set_not_empty, FINITE x - Then CHOICE y IN y by CHOICE_DEF - so (set o CHOICE) y - = set (CHOICE y) by o_THM - = x by perm_set_element, y = perm_set x -*) -Theorem list_count_set_map_surj: - !n k. SURJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) -Proof - rw_tac bool_ss[SURJ_DEF] >- - simp[list_count_set_map_element] >> - fs[sub_count_element] >> - `FINITE x` by metis_tac[SUBSET_FINITE, FINITE_COUNT] >> - qexists_tac `perm_set x` >> - simp[feq_partition_element, list_count_element, perm_set_element] >> - rpt strip_tac >| [ - qabbrev_tac `ls = SET_TO_LIST x` >> - qexists_tac `ls` >> - `ALL_DISTINCT ls` by rw[ALL_DISTINCT_SET_TO_LIST, Abbr`ls`] >> - `set ls = x` by rw[SET_TO_LIST_INV, Abbr`ls`] >> - `LENGTH ls = k` by rw[SET_TO_LIST_CARD, Abbr`ls`] >> - rw[EQ_IMP_THM] >> - metis_tac[ALL_DISTINCT_CARD_LIST_TO_SET], - `perm_set x <> {}` by fs[perm_set_not_empty] >> - qabbrev_tac `ls = CHOICE (perm_set x)` >> - `ls IN perm_set x` by fs[CHOICE_DEF, Abbr`ls`] >> - fs[perm_set_element] - ] -QED - -(* Theorem: BIJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) *) -(* Proof: - Let f = set o CHOICE, - s = partition (feq set) (list_count n k), - t = sub_count n k. - Note INJ f s t by list_count_set_map_inj - and SURJ f s t by list_count_set_map_surj - so BIJ f s t by BIJ_DEF -*) -Theorem list_count_set_map_bij: - !n k. BIJ (set o CHOICE) (partition (feq set) (list_count n k)) (sub_count n k) -Proof - simp[BIJ_DEF, list_count_set_map_inj, list_count_set_map_surj] -QED - -(* Theorem: n arrange k = (n choose k) * perm k *) -(* Proof: - Let R = feq set, - s = list_count n k, - t = sub_count n k. - Then FINITE s by list_count_finite - and R equiv_on s by feq_set_equiv - and !e. e IN partition R s ==> CARD e = perm k - by list_count_set_partititon_element_card - Thus CARD s = perm k * CARD (partition R s) - by equal_partition_card, [1] - Note CARD s = n arrange k by arrange_def - and BIJ (set o CHOICE) (partition R s) t - by list_count_set_map_bij - and FINITE t by sub_count_finite - so CARD (partition R s) - = CARD t by bij_eq_card - = n choose k by choose_def - Hence n arrange k = n choose k * perm k - by MULT_COMM, [1], above. -*) -Theorem arrange_eqn[compute]: - !n k. n arrange k = (n choose k) * perm k -Proof - rpt strip_tac >> - assume_tac list_count_set_map_bij >> - last_x_assum (qspecl_then [`n`, `k`] strip_assume_tac) >> - qabbrev_tac `R = feq (set :num list -> num -> bool)` >> - qabbrev_tac `s = list_count n k` >> - qabbrev_tac `t = sub_count n k` >> - `FINITE s` by rw[list_count_finite, Abbr`s`] >> - `R equiv_on s` by rw[feq_set_equiv, Abbr`R`] >> - `!e. e IN partition R s ==> CARD e = perm k` by metis_tac[list_count_set_partititon_element_card] >> - imp_res_tac equal_partition_card >> - `FINITE t` by rw[sub_count_finite, Abbr`t`] >> - `CARD (partition R s) = CARD t` by metis_tac[bij_eq_card] >> - simp[arrange_def, choose_def, Abbr`s`, Abbr`t`] -QED - -(* This is P(n,k) = C(n,k) * k! *) - -(* Theorem: n arrange k = (n choose k) * FACT k *) -(* Proof: - n arrange k - = (n choose k) * perm k by arrange_eqn - = (n choose k) * FACT k by perm_eq_fact -*) -Theorem arrange_alt: - !n k. n arrange k = (n choose k) * FACT k -Proof - simp[arrange_eqn, perm_eq_fact] -QED - -(* -> EVAL ``5 arrange 2``; = 20 -> EVAL ``MAP ($arrange 5) [0 .. 5]``; = [1; 5; 20; 60; 120; 120] -*) - -(* Theorem: n arrange k = (binomial n k) * FACT k *) -(* Proof: - n arrange k - = (n choose k) * FACT k by arrange_alt - = (binomial n k) * FACT k by choose_eqn -*) -Theorem arrange_formula: - !n k. n arrange k = (binomial n k) * FACT k -Proof - simp[arrange_alt, choose_eqn] -QED - -(* Theorem: k <= n ==> n arrange k = FACT n DIV FACT (n - k) *) -(* Proof: - Note 0 < FACT (n - k) by FACT_LESS - (n arrange k) * FACT (n - k) - = (binomial n k) * FACT k * FACT (n - k) by arrange_formula - = binomial n k * (FACT (n - k) * FACT k) by arithmetic - = FACT n by binomial_formula2, k <= n - Thus n arrange k = FACT n DIV FACT (n - k) by DIV_SOLVE -*) -Theorem arrange_formula2: - !n k. k <= n ==> n arrange k = FACT n DIV FACT (n - k) -Proof - rpt strip_tac >> - `0 < FACT (n - k)` by rw[FACT_LESS] >> - `(n arrange k) * FACT (n - k) = (binomial n k) * FACT k * FACT (n - k)` by rw[arrange_formula] >> - `_ = binomial n k * (FACT (n - k) * FACT k)` by rw[] >> - `_ = FACT n` by rw[binomial_formula2] >> - simp[DIV_SOLVE] -QED - -(* Theorem: n arrange 0 = 1 *) -(* Proof: - n arrange 0 - = CARD (list_count n 0) by arrange_def - = CARD {[]} by list_count_n_0 - = 1 by CARD_SING -*) -Theorem arrange_n_0: - !n. n arrange 0 = 1 -Proof - simp[arrange_def, perm_def, list_count_n_0] -QED - -(* Theorem: 0 < n ==> 0 arrange n = 0 *) -(* Proof: - 0 arrange n - = CARD (list_count 0 n) by arrange_def - = CARD {} by list_count_0_n, 0 < n - = 0 by CARD_EMPTY -*) -Theorem arrange_0_n: - !n. 0 < n ==> 0 arrange n = 0 -Proof - simp[arrange_def, perm_def, list_count_0_n] -QED - -(* Theorem: n arrange n = perm n *) -(* Proof: - n arrange n - = (binomial n n) * FACT n by arrange_formula - = 1 * FACT n by binomial_n_n - = perm n by perm_eq_fact -*) -Theorem arrange_n_n: - !n. n arrange n = perm n -Proof - simp[arrange_formula, binomial_n_n, perm_eq_fact] -QED - -(* Theorem: n arrange n = FACT n *) -(* Proof: - n arrange n - = (binomial n n) * FACT n by arrange_formula - = 1 * FACT n by binomial_n_n -*) -Theorem arrange_n_n_alt: - !n. n arrange n = FACT n -Proof - simp[arrange_formula, binomial_n_n] -QED - -(* Theorem: n arrange k = 0 <=> n < k *) -(* Proof: - Note FINITE (list_count n k) by list_count_finite - n arrange k = 0 - <=> CARD (list_count n k) = 0 by arrange_def - <=> list_count n k = {} by CARD_EQ_0 - <=> n < k by list_count_eq_empty -*) -Theorem arrange_eq_0: - !n k. n arrange k = 0 <=> n < k -Proof - metis_tac[arrange_def, list_count_eq_empty, list_count_finite, CARD_EQ_0] -QED - -(* Note: - -k-permutation recurrence? - -P(n,k) = C(n,k) * k! -P(n,0) = C(n,0) * 0! = 1 -P(0,k+1) = C(0,k+1) * (k+1)! = 0 - -C(n+1,k+1) = C(n,k) + C(n,k+1) -P(n+1,k+1)/(k+1)! = P(n,k)/k! + P(n,k+1)/(k+1)! -P(n+1,k+1) = (k+1) * P(n,k) + P(n,k+1) -P(n+1,k+1) = P(n,k) * (k + 1) + P(n,k+1) - -P(2,1) = 2: [0] [1] -P(2,2) = 2: [0,1] [1,0] -P(3,2) = 6: [0,1] [0,2] include 2: [0,2] [1,2] [2,0] [2,1] - [1,0] [1,2] exclude 2: [0,1] [1,0] - [2,0] [2,1] -P(3,2) = P(2,1) * 2 + P(2,2) = ([0][1],2 + 2,[0][1]) + to_lists {0,1} -P(4,3): include 3: P(3,2) * 3 - exclude 3: P(3,3) - -list_count (n+1) (k+1) = IMAGE (interleave k) (list_count n k) UNION list_count n (k + 1) - -closed? -https://math.stackexchange.com/questions/3060456/ -using Pascal argument - -*) - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/fermat/count/helperCountScript.sml b/examples/fermat/count/helperCountScript.sml deleted file mode 100644 index b76a28dd8b..0000000000 --- a/examples/fermat/count/helperCountScript.sml +++ /dev/null @@ -1,531 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Count Helper. *) -(* ------------------------------------------------------------------------- *) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "helperCount"; - -(* ------------------------------------------------------------------------- *) - - -(* open dependent theories *) -(* val _ = load "EulerTheory"; *) -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; - - -(* ------------------------------------------------------------------------- *) -(* Count Helper Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading (# is temporary): - over f s t = !x. x IN s ==> f x IN t - s bij_eq t = ?f. BIJ f s t - s =b= t = ?f. BIJ f s t -*) -(* Definitions and Theorems (# are exported, ! are in compute): - - Set Theorems: - over_inj |- !f s t. INJ f s t ==> over f s t - over_surj |- !f s t. SURJ f s t ==> over f s t - over_bij |- !f s t. BIJ f s t ==> over f s t - SURJ_CARD_IMAGE_EQ |- !f s t. FINITE t /\ over f s t ==> - (SURJ f s t <=> CARD (IMAGE f s) = CARD t) - FINITE_SURJ_IFF |- !f s t. FINITE t ==> - (SURJ f s t <=> CARD (IMAGE f s) = CARD t /\ over f s t) - INJ_IMAGE_BIJ_IFF |- !f s t. INJ f s t <=> BIJ f s (IMAGE f s) /\ over f s t - INJ_IFF_BIJ_IMAGE |- !f s t. over f s t ==> (INJ f s t <=> BIJ f s (IMAGE f s)) - INJ_IMAGE_IFF |- !f s t. INJ f s t <=> INJ f s (IMAGE f s) /\ over f s t - FUNSET_ALT |- !P Q. FUNSET P Q = {f | over f P Q} - - Bijective Equivalence: - bij_eq_empty |- !s t. s =b= t ==> (s = {} <=> t = {}) - bij_eq_refl |- !s. s =b= s - bij_eq_sym |- !s t. s =b= t <=> t =b= s - bij_eq_trans |- !s t u. s =b= t /\ t =b= u ==> s =b= u - bij_eq_equiv_on |- !P. $=b= equiv_on P - bij_eq_finite |- !s t. s =b= t ==> (FINITE s <=> FINITE t) - bij_eq_count |- !s. FINITE s ==> s =b= count (CARD s) - bij_eq_card |- !s t. s =b= t /\ (FINITE s \/ FINITE t) ==> CARD s = CARD t - bij_eq_card_eq |- !s t. FINITE s /\ FINITE t ==> (s =b= t <=> CARD s = CARD t) - - Alternate characterisation of maps: - surj_preimage_not_empty - |- !f s t. SURJ f s t <=> - over f s t /\ !y. y IN t ==> preimage f s y <> {} - inj_preimage_empty_or_sing - |- !f s t. INJ f s t <=> - over f s t /\ !y. y IN t ==> preimage f s y = {} \/ - SING (preimage f s y) - bij_preimage_sing |- !f s t. BIJ f s t <=> - over f s t /\ !y. y IN t ==> SING (preimage f s y) - surj_iff_preimage_card_not_0 - |- !f s t. FINITE s /\ over f s t ==> - (SURJ f s t <=> - !y. y IN t ==> CARD (preimage f s y) <> 0) - inj_iff_preimage_card_le_1 - |- !f s t. FINITE s /\ over f s t ==> - (INJ f s t <=> - !y. y IN t ==> CARD (preimage f s y) <= 1) - bij_iff_preimage_card_eq_1 - |- !f s t. FINITE s /\ over f s t ==> - (BIJ f s t <=> - !y. y IN t ==> CARD (preimage f s y) = 1) - finite_surj_inj_iff |- !f s t. FINITE s /\ SURJ f s t ==> - (INJ f s t <=> - !e. e IN IMAGE (preimage f s) t ==> CARD e = 1) -*) - -(* ------------------------------------------------------------------------- *) -(* Set Theorems *) -(* ------------------------------------------------------------------------- *) - -(* Overload a function from domain to range. *) -val _ = overload_on("over", ``\f s t. !x. x IN s ==> f x IN t``); -(* not easy to make this a good infix operator! *) - -(* Theorem: INJ f s t ==> over f s t *) -(* Proof: by INJ_DEF. *) -Theorem over_inj: - !f s t. INJ f s t ==> over f s t -Proof - simp[INJ_DEF] -QED - -(* Theorem: SURJ f s t ==> over f s t *) -(* Proof: by SURJ_DEF. *) -Theorem over_surj: - !f s t. SURJ f s t ==> over f s t -Proof - simp[SURJ_DEF] -QED - -(* Theorem: BIJ f s t ==> over f s t *) -(* Proof: by BIJ_DEF, INJ_DEF. *) -Theorem over_bij: - !f s t. BIJ f s t ==> over f s t -Proof - simp[BIJ_DEF, INJ_DEF] -QED - -(* Theorem: FINITE t /\ over f s t ==> - (SURJ f s t <=> CARD (IMAGE f s) = CARD t) *) -(* Proof: - If part: SURJ f s t ==> CARD (IMAGE f s) = CARD t - Note IMAGE f s = t by IMAGE_SURJ - Hence true. - Only-if part: CARD (IMAGE f s) = CARD t ==> SURJ f s t - By contradiction, suppose ~SURJ f s t. - Then IMAGE f s <> t by IMAGE_SURJ - but IMAGE f s SUBSET t by IMAGE_SUBSET_TARGET - so IMAGE f s PSUBSET t by PSUBSET_DEF - ==> CARD (IMAGE f s) < CARD t - by CARD_PSUBSET - This contradicts CARD (IMAGE f s) = CARD t. -*) -Theorem SURJ_CARD_IMAGE_EQ: - !f s t. FINITE t /\ over f s t ==> - (SURJ f s t <=> CARD (IMAGE f s) = CARD t) -Proof - rw[EQ_IMP_THM] >- - fs[IMAGE_SURJ] >> - spose_not_then strip_assume_tac >> - `IMAGE f s <> t` by rw[GSYM IMAGE_SURJ] >> - `IMAGE f s PSUBSET t` by fs[IMAGE_SUBSET_TARGET, PSUBSET_DEF] >> - imp_res_tac CARD_PSUBSET >> - decide_tac -QED - -(* Theorem: FINITE t ==> - (SURJ f s t <=> CARD (IMAGE f s) = CARD t /\ over f s t) *) -(* Proof: - If part: true by SURJ_DEF, IMAGE_SURJ - Only-if part: true by SURJ_CARD_IMAGE_EQ -*) -Theorem FINITE_SURJ_IFF: - !f s t. FINITE t ==> - (SURJ f s t <=> CARD (IMAGE f s) = CARD t /\ over f s t) -Proof - metis_tac[SURJ_CARD_IMAGE_EQ, SURJ_DEF] -QED - -(* Note: this cannot be proved: -g `!f s t. FINITE t /\ over f s t ==> - (INJ f s t <=> CARD (IMAGE f s) = CARD t)`; -Take f = I, s = count m, t = count n, with m <= n. -Then INJ I (count m) (count n) -and IMAGE I (count m) = (count m) -so CARD (IMAGE f s) = m, CARD t = n, may not equal. -*) - -(* INJ_IMAGE_BIJ |- !s f. (?t. INJ f s t) ==> BIJ f s (IMAGE f s) *) - -(* Theorem: INJ f s t <=> (BIJ f s (IMAGE f s) /\ over f s t) *) -(* Proof: - If part: INJ f s t ==> BIJ f s (IMAGE f s) /\ over f s t - Note BIJ f s (IMAGE f s) by INJ_IMAGE_BIJ - and over f s t by INJ_DEF - Only-if: BIJ f s (IMAGE f s) /\ over f s t ==> INJ f s t - By INJ_DEF, this is to show: - (1) x IN s ==> f x IN t, true by given - (2) x IN s /\ y IN s /\ f x = f y ==> x = y - Note f x IN (IMAGE f s) by IN_IMAGE - and f y IN (IMAGE f s) by IN_IMAGE - so f x = f y ==> x = y by BIJ_IS_INJ -*) -Theorem INJ_IMAGE_BIJ_IFF: - !f s t. INJ f s t <=> (BIJ f s (IMAGE f s) /\ over f s t) -Proof - rw[EQ_IMP_THM] >- - metis_tac[INJ_IMAGE_BIJ] >- - fs[INJ_DEF] >> - rw[INJ_DEF] >> - metis_tac[BIJ_IS_INJ, IN_IMAGE] -QED - -(* Theorem: over f s t ==> (INJ f s t <=> BIJ f s (IMAGE f s)) *) -(* Proof: by INJ_IMAGE_BIJ_IFF. *) -Theorem INJ_IFF_BIJ_IMAGE: - !f s t. over f s t ==> (INJ f s t <=> BIJ f s (IMAGE f s)) -Proof - metis_tac[INJ_IMAGE_BIJ_IFF] -QED - -(* -INJ_IMAGE |- !f s t. INJ f s t ==> INJ f s (IMAGE f s) -*) - -(* Theorem: INJ f s t <=> INJ f s (IMAGE f s) /\ over f s t *) -(* Proof: - Let P = over f s t. - If part: INJ f s t ==> INJ f s (IMAGE f s) /\ P - Note INJ f s (IMAGE f s) by INJ_IMAGE - and P is true by INJ_DEF - Only-if part: INJ f s (IMAGE f s) /\ P ==> INJ f s t - Note s SUBSET s by SUBSET_REFL - and (IMAGE f s) SUBSET t by IMAGE_SUBSET_TARGET - Thus INJ f s t by INJ_SUBSET -*) -Theorem INJ_IMAGE_IFF: - !f s t. INJ f s t <=> INJ f s (IMAGE f s) /\ over f s t -Proof - rw[EQ_IMP_THM] >- - metis_tac[INJ_IMAGE] >- - fs[INJ_DEF] >> - `s SUBSET s` by rw[] >> - `(IMAGE f s) SUBSET t` by fs[IMAGE_SUBSET_TARGET] >> - metis_tac[INJ_SUBSET] -QED - -(* pred_setTheory: -FUNSET |- !P Q. FUNSET P Q = (\f. over f P Q) -*) - -(* Theorem: FUNSET P Q = {f | over f P Q} *) -(* Proof: by FUNSET, EXTENSION *) -Theorem FUNSET_ALT: - !P Q. FUNSET P Q = {f | over f P Q} -Proof - rw[FUNSET, EXTENSION] -QED - -(* ------------------------------------------------------------------------- *) -(* Bijective Equivalence *) -(* ------------------------------------------------------------------------- *) - -(* Overload bijectively equal. *) -val _ = overload_on("bij_eq", ``\s t. ?f. BIJ f s t``); -val _ = set_fixity "bij_eq" (Infix(NONASSOC, 450)); (* same as relation *) - -val _ = overload_on ("=b=", ``$bij_eq``); -val _ = set_fixity "=b=" (Infix(NONASSOC, 450)); - -(* -> BIJ_SYM; -val it = |- !s t. s bij_eq t <=> t bij_eq s: thm -> BIJ_SYM; -val it = |- !s t. s =b= t <=> t =b= s: thm -> FINITE_BIJ_COUNT_CARD -val it = |- !s. FINITE s ==> count (CARD s) =b= s: thm -*) - -(* Theorem: s =b= t ==> (s = {} <=> t = {}) *) -(* Proof: by BIJ_EMPTY. *) -Theorem bij_eq_empty: - !s t. s =b= t ==> (s = {} <=> t = {}) -Proof - metis_tac[BIJ_EMPTY] -QED - -(* Theorem: s =b= s *) -(* Proof: by BIJ_I_SAME *) -Theorem bij_eq_refl: - !s. s =b= s -Proof - metis_tac[BIJ_I_SAME] -QED - -(* Theorem alias *) -Theorem bij_eq_sym = BIJ_SYM; -(* val bij_eq_sym = |- !s t. s =b= t <=> t =b= s: thm *) - -Theorem bij_eq_trans = BIJ_TRANS; -(* val bij_eq_trans = |- !s t u. s =b= t /\ t =b= u ==> s =b= u: thm *) - -(* Idea: bij_eq is an equivalence relation on any set of sets. *) - -(* Theorem: $=b= equiv_on P *) -(* Proof: - By equiv_on_def, this is to show: - (1) s IN P ==> s =b= s, true by bij_eq_refl - (2) s IN P /\ t IN P ==> (t =b= s <=> s =b= t) - This is true by bij_eq_sym - (3) s IN P /\ s' IN P /\ t IN P /\ - BIJ f s s' /\ BIJ f' s' t ==> s =b= t - This is true by bij_eq_trans -*) -Theorem bij_eq_equiv_on: - !P. $=b= equiv_on P -Proof - rw[equiv_on_def] >- - simp[bij_eq_refl] >- - simp[Once bij_eq_sym] >> - metis_tac[bij_eq_trans] -QED - -(* Theorem: s =b= t ==> (FINITE s <=> FINITE t) *) -(* Proof: by BIJ_FINITE_IFF *) -Theorem bij_eq_finite: - !s t. s =b= t ==> (FINITE s <=> FINITE t) -Proof - metis_tac[BIJ_FINITE_IFF] -QED - -(* This is the iff version of: -pred_setTheory.FINITE_BIJ_CARD; -|- !f s t. FINITE s /\ BIJ f s t ==> CARD s = CARD t -*) - -(* Theorem: FINITE s ==> s =b= (count (CARD s)) *) -(* Proof: by FINITE_BIJ_COUNT_CARD, BIJ_SYM *) -Theorem bij_eq_count: - !s. FINITE s ==> s =b= (count (CARD s)) -Proof - metis_tac[FINITE_BIJ_COUNT_CARD, BIJ_SYM] -QED - -(* Theorem: s =b= t /\ (FINITE s \/ FINITE t) ==> CARD s = CARD t *) -(* Proof: by FINITE_BIJ_CARD, BIJ_SYM. *) -Theorem bij_eq_card: - !s t. s =b= t /\ (FINITE s \/ FINITE t) ==> CARD s = CARD t -Proof - metis_tac[FINITE_BIJ_CARD, BIJ_SYM] -QED - -(* Theorem: FINITE s /\ FINITE t ==> (s =b= t <=> CARD s = CARD t) *) -(* Proof: - If part: s =b= t ==> CARD s = CARD t - This is true by FINITE_BIJ_CARD - Only-if part: CARD s = CARD t ==> s =b= t - Let n = CARD s = CARD t. - Note ?f. BIJ f s (count n) by bij_eq_count - and ?g. BIJ g (count n) t by FINITE_BIJ_COUNT_CARD - Thus s =b= t by bij_eq_trans -*) -Theorem bij_eq_card_eq: - !s t. FINITE s /\ FINITE t ==> (s =b= t <=> CARD s = CARD t) -Proof - rw[EQ_IMP_THM] >- - metis_tac[FINITE_BIJ_CARD] >> - `?f. BIJ f s (count (CARD s))` by rw[bij_eq_count] >> - `?g. BIJ g (count (CARD t)) t` by rw[FINITE_BIJ_COUNT_CARD] >> - metis_tac[bij_eq_trans] -QED - -(* ------------------------------------------------------------------------- *) -(* Alternate characterisation of maps. *) -(* ------------------------------------------------------------------------- *) - -(* Theorem: SURJ f s t <=> - over f s t /\ (!y. y IN t ==> preimage f s y <> {}) *) -(* Proof: - Let P = over f s t, - Q = !y. y IN t ==> preimage f s y <> {}. - If part: SURJ f s t ==> P /\ Q - P is true by SURJ_DEF - Q is true by preimage_def, SURJ_DEF - Only-if part: P /\ Q ==> SURJ f s t - This is true by preimage_def, SURJ_DEF -*) -Theorem surj_preimage_not_empty: - !f s t. SURJ f s t <=> - over f s t /\ (!y. y IN t ==> preimage f s y <> {}) -Proof - rw[SURJ_DEF, preimage_def, EXTENSION] >> - metis_tac[] -QED - -(* Theorem: INJ f s t <=> - over f s t /\ - (!y. y IN t ==> (preimage f s y = {} \/ - SING (preimage f s y))) *) -(* Proof: - Let P = over f s t, - Q = !y. y IN t ==> preimage f s y = {} \/ SING (preimage f s y). - If part: INJ f s t ==> P /\ Q - P is true by INJ_DEF - For Q, if preimage f s y <> {}, - Then ?x. x IN preimage f s y by MEMBER_NOT_EMPTY - or ?x. x IN s /\ f x = y by in_preimage - Thus !z. z IN preimage f s y ==> z = x - by in_preimage, INJ_DEF - or SING (preimage f s y) by SING_DEF, EXTENSION - Only-if part: P /\ Q ==> INJ f s t - By INJ_DEF, this is to show: - !x y. x IN s /\ y IN s /\ f x = f y ==> x = y - Let z = f x, then z IN t by over f s t - so x IN preimage f s z by in_preimage - and y IN preimage f s z by in_preimage - Thus preimage f s z <> {} by MEMBER_NOT_EMPTY - so SING (preimage f s z) by implication - ==> x = y by SING_ELEMENT -*) -Theorem inj_preimage_empty_or_sing: - !f s t. INJ f s t <=> - over f s t /\ - (!y. y IN t ==> (preimage f s y = {} \/ - SING (preimage f s y))) -Proof - rw[EQ_IMP_THM] >- - fs[INJ_DEF] >- - ((Cases_on `preimage f s y = {}` >> simp[]) >> - `?x. x IN s /\ f x = y` by metis_tac[in_preimage, MEMBER_NOT_EMPTY] >> - simp[SING_DEF] >> - qexists_tac `x` >> - rw[preimage_def, EXTENSION] >> - metis_tac[INJ_DEF]) >> - rw[INJ_DEF] >> - qabbrev_tac `z = f x` >> - `z IN t` by fs[Abbr`z`] >> - `x IN preimage f s z` by fs[preimage_def] >> - `y IN preimage f s z` by fs[preimage_def] >> - metis_tac[MEMBER_NOT_EMPTY, SING_ELEMENT] -QED - -(* Theorem: BIJ f s t <=> - over f s t /\ - (!y. y IN t ==> SING (preimage f s y)) *) -(* Proof: - Let P = over f s t, - Q = !y. y IN t ==> SING (preimage f s y). - If part: BIJ f s t ==> P /\ Q - P is true by BIJ_DEF, INJ_DEF - For Q, - Note INJ f s t /\ SURJ f s t by BIJ_DEF - so preimage f s y <> {} by surj_preimage_not_empty - Thus SING (preimage f s y) by inj_preimage_empty_or_sing - Only-if part: P /\ Q ==> BIJ f s t - Note !y. y IN t ==> (preimage f s y) <> {} - by SING_DEF, NOT_EMPTY_SING - so SURJ f s t by surj_preimage_not_empty - and INJ f s t by inj_preimage_empty_or_sing - Thus BIJ f s t by BIJ_DEF -*) -Theorem bij_preimage_sing: - !f s t. BIJ f s t <=> - over f s t /\ - (!y. y IN t ==> SING (preimage f s y)) -Proof - rw[EQ_IMP_THM] >- - fs[BIJ_DEF, INJ_DEF] >- - metis_tac[BIJ_DEF, surj_preimage_not_empty, inj_preimage_empty_or_sing] >> - `INJ f s t` by metis_tac[inj_preimage_empty_or_sing] >> - `SURJ f s t` by metis_tac[SING_DEF, NOT_EMPTY_SING, surj_preimage_not_empty] >> - simp[BIJ_DEF] -QED - -(* Theorem: FINITE s /\ over f s t ==> - (SURJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <> 0) *) -(* Proof: - Note !y. FINITE (preimage f s y) by preimage_finite - and !y. CARD (preimage f s y) = 0 <=> preimage f s y = {} - by CARD_EQ_0 - The result follows by surj_preimage_not_empty -*) -Theorem surj_iff_preimage_card_not_0: - !f s t. FINITE s /\ over f s t ==> - (SURJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <> 0) -Proof - metis_tac[surj_preimage_not_empty, preimage_finite, CARD_EQ_0] -QED - -(* Theorem: FINITE s /\ over f s t ==> - (INJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <= 1) *) -(* Proof: - Note !y. FINITE (preimage f s y) by preimage_finite - and !y. CARD (preimage f s y) = 0 <=> preimage f s y = {} - by CARD_EQ_0 - and !y. CARD (preimage f s y) = 1 <=> SING (preimage f s y) - by CARD_EQ_1 - The result follows by inj_preimage_empty_or_sing, LE_ONE -*) -Theorem inj_iff_preimage_card_le_1: - !f s t. FINITE s /\ over f s t ==> - (INJ f s t <=> !y. y IN t ==> CARD (preimage f s y) <= 1) -Proof - metis_tac[inj_preimage_empty_or_sing, preimage_finite, CARD_EQ_0, CARD_EQ_1, LE_ONE] -QED - -(* Theorem: FINITE s /\ over f s t ==> - (BIJ f s t <=> !y. y IN t ==> CARD (preimage f s y) = 1) *) -(* Proof: - Note !y. FINITE (preimage f s y) by preimage_finite - and !y. CARD (preimage f s y) = 1 <=> SING (preimage f s y) - by CARD_EQ_1 - The result follows by bij_preimage_sing -*) -Theorem bij_iff_preimage_card_eq_1: - !f s t. FINITE s /\ over f s t ==> - (BIJ f s t <=> !y. y IN t ==> CARD (preimage f s y) = 1) -Proof - metis_tac[bij_preimage_sing, preimage_finite, CARD_EQ_1] -QED - -(* Theorem: FINITE s /\ SURJ f s t ==> - (INJ f s t <=> !e. e IN IMAGE (preimage f s) t ==> CARD e = 1) *) -(* Proof: - If part: INJ f s t /\ x IN t ==> CARD (preimage f s x) = 1 - Note BIJ f s t by BIJ_DEF - and over f s t by BIJ_DEF, INJ_DEF - so CARD (preimage f s x) = 1 by bij_iff_preimage_card_eq_1 - Only-if part: !e. (?x. e = preimage f s x /\ x IN t) ==> CARD e = 1 ==> INJ f s t - Note over f s t by SURJ_DEF - and !x. x IN t ==> ?y. y IN s /\ f y = x by SURJ_DEF - Thus !y. y IN t ==> CARD (preimage f s y) = 1 by IN_IMAGE - so INJ f s t by inj_iff_preimage_card_le_1 -*) -Theorem finite_surj_inj_iff: - !f s t. FINITE s /\ SURJ f s t ==> - (INJ f s t <=> !e. e IN IMAGE (preimage f s) t ==> CARD e = 1) -Proof - rw[EQ_IMP_THM] >- - prove_tac[BIJ_DEF, INJ_DEF, bij_iff_preimage_card_eq_1] >> - fs[SURJ_DEF] >> - `!y. y IN t ==> CARD (preimage f s y) = 1` by metis_tac[] >> - rw[inj_iff_preimage_card_le_1] -QED - - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/fermat/count/mapCountScript.sml b/examples/fermat/count/mapCountScript.sml index f57b864313..6c0c5335a6 100644 --- a/examples/fermat/count/mapCountScript.sml +++ b/examples/fermat/count/mapCountScript.sml @@ -12,24 +12,10 @@ val _ = new_theory "mapCount"; (* ------------------------------------------------------------------------- *) +open arithmeticTheory pred_setTheory gcdsetTheory numberTheory listTheory + rich_listTheory listRangeTheory combinatoricsTheory; -(* open dependent theories *) -(* arithmeticTheory -- load by default *) - -(* val _ = load "combinatoricsTheory"; *) -open helperCountTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; - -open listTheory rich_listTheory; -open listRangeTheory; -open helperListTheory; - -open necklaceTheory; (* for necklace_def *) -open combinatoricsTheory; - +val _ = temp_overload_on("over", ``\f s t. !x. x IN s ==> f x IN t``); (* ------------------------------------------------------------------------- *) (* Counting of maps between finite sets Documentation *) diff --git a/examples/fermat/count/permutationScript.sml b/examples/fermat/count/permutationScript.sml index aff5247f7e..e537d5d796 100644 --- a/examples/fermat/count/permutationScript.sml +++ b/examples/fermat/count/permutationScript.sml @@ -12,30 +12,23 @@ val _ = new_theory "permutation"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* for stripDup *) -(* open dependent theories *) -(* val _ = load "symmetryTheory"; *) -open pred_setTheory arithmeticTheory; -open helperCountTheory; -open helperSetTheory; - -open listTheory rich_listTheory; -open helperListTheory; +open pred_setTheory arithmeticTheory gcdsetTheory numberTheory listTheory + listRangeTheory rich_listTheory combinatoricsTheory; open mapCountTheory; -open combinatoricsTheory; (* Get dependent theories local *) open monoidTheory groupTheory; -open submonoidTheory subgroupTheory; -open monoidMapTheory groupMapTheory; +open subgroupTheory; +open groupMapTheory; open quotientGroupTheory; (* for homo_image_def *) open symmetryTheory; +val _ = temp_overload_on("over", ``\f s t. !x. x IN s ==> f x IN t``); (* ------------------------------------------------------------------------- *) (* Permutation Group Documentation *) diff --git a/examples/fermat/count/symmetryScript.sml b/examples/fermat/count/symmetryScript.sml index 4f989be82b..6e298244a2 100644 --- a/examples/fermat/count/symmetryScript.sml +++ b/examples/fermat/count/symmetryScript.sml @@ -12,27 +12,21 @@ val _ = new_theory "symmetry"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; (* for stripDup *) -(* open dependent theories *) -(* val _ = load "mapCountTheory"; *) -open pred_setTheory arithmeticTheory; -open helperCountTheory; -open helperSetTheory; +open pred_setTheory arithmeticTheory gcdsetTheory numberTheory + combinatoricsTheory; open mapCountTheory; (* for on_def *) -open combinatoricsTheory; -(* Get dependent theories local *) -(* val _ = load "fieldMapTheory"; *) open monoidTheory groupTheory; open ringTheory fieldTheory; -open submonoidTheory subgroupTheory; -open monoidMapTheory groupMapTheory; +open subgroupTheory; +open groupMapTheory; open ringMapTheory fieldMapTheory; +val _ = temp_overload_on("over", ``\f s t. !x. x IN s ==> f x IN t``); (* ------------------------------------------------------------------------- *) (* Symmetry Group Documentation *) diff --git a/examples/fermat/little/FLTactionScript.sml b/examples/fermat/little/FLTactionScript.sml index 46dbbed0e3..7ee0af5c17 100644 --- a/examples/fermat/little/FLTactionScript.sml +++ b/examples/fermat/little/FLTactionScript.sml @@ -35,25 +35,14 @@ val _ = new_theory "FLTaction"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* val _ = load "FLTnecklaceTheory"; *) -open helperNumTheory helperSetTheory; -open arithmeticTheory pred_setTheory; -(* val _ = load "helperFunctionTheory"; *) -open helperFunctionTheory; (* for prime_power_divisor, PRIME_EXP_FACTOR *) +open arithmeticTheory pred_setTheory dividesTheory gcdTheory gcdsetTheory + logrootTheory numberTheory combinatoricsTheory; open cycleTheory; -open necklaceTheory; -(* val _ = load "groupInstancesTheory"; *) -(* val _ = load "groupActionTheory"; *) open groupTheory; open groupActionTheory; -open dividesTheory; (* for divides_def, prime_def *) - - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Action Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/little/FLTbinomialScript.sml b/examples/fermat/little/FLTbinomialScript.sml index 63cfff82ce..8161d12cc9 100644 --- a/examples/fermat/little/FLTbinomialScript.sml +++ b/examples/fermat/little/FLTbinomialScript.sml @@ -32,18 +32,11 @@ Proof: (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory; + (* declare new theory at start *) val _ = new_theory "FLTbinomial"; -(* ------------------------------------------------------------------------- *) - - -(* open dependent theories *) -(* val _ = load "binomialTheory"; *) -open arithmeticTheory; (* for MOD and EXP *) -open dividesTheory; (* for PRIME_POS *) - - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Binomial Documentation *) (* ------------------------------------------------------------------------- *) @@ -97,66 +90,8 @@ open dividesTheory; (* for PRIME_POS *) (* Part 2: General Theory -------------------------------------------------- *) -val PRIME_FACTOR_PROPER = helperNumTheory.PRIME_FACTOR_PROPER; -(* |- !n. 1 < n /\ ~prime n ==> ?p. prime p /\ p < n /\ p divides n *) - -val MULTIPLE_INTERVAL = helperNumTheory.MULTIPLE_INTERVAL; -(* |- !n m. n divides m ==> !x. m - n < x /\ x < m + n /\ n divides x ==> x = m *) - -val PROD_SET_EUCLID = helperSetTheory.PROD_SET_EUCLID; -(* |- !s. FINITE s ==> !p. prime p /\ p divides PROD_SET s ==> ?b. b IN s /\ p divides b *) -(* This is: Generalized Euclid's Lemma. *) - -val PRIME_BIG_NOT_DIVIDES_FACT = helperFunctionTheory.PRIME_BIG_NOT_DIVIDES_FACT; -(* |- !p k. prime p /\ k < p ==> ~(p divides FACT k) *) - -val FACT_EQ_PROD = helperFunctionTheory.FACT_EQ_PROD; -(* |- !n. FACT n = PROD_SET (IMAGE SUC (count n)) *) - -val FACT_REDUCTION = helperFunctionTheory.FACT_REDUCTION; -(* |- !n m. m < n ==> FACT n = PROD_SET (IMAGE SUC (count n DIFF count m)) * FACT m *) -(* That is: n!/m! = product of (m+1) to n *) - (* Part 3: Actual Proof ---------------------------------------------------- *) -(* ------------------------------------------------------------------------- *) -(* Binomial Theorem for prime exponent and modulo. *) -(* ------------------------------------------------------------------------- *) - -val prime_divides_binomials = binomialTheory.prime_divides_binomials; -(* |- !n. prime n ==> 1 < n /\ !k. 0 < k /\ k < n ==> n divides binomial n k *) - -val prime_divisor_property = binomialTheory.prime_divisor_property; -(* |- !n p. 1 < n /\ p < n /\ prime p /\ p divides n ==> - ~(p divides FACT (n - 1) DIV FACT (n - p)) *) - -val divides_binomials_imp_prime = binomialTheory.divides_binomials_imp_prime; -(* !n. 1 < n /\ (!k. 0 < k /\ k < n ==> n divides binomial n k) ==> prime n *) - -val prime_iff_divides_binomials = binomialTheory.prime_iff_divides_binomials; -(* |- !n. prime n <=> 1 < n /\ !k. 0 < k /\ k < n ==> n divides binomial n k *) - -val binomial_range_shift = binomialTheory.binomial_range_shift; -(* |- !n. 0 < n ==> ((!k. 0 < k /\ k < n ==> binomial n k MOD n = 0) <=> - !h. h < PRE n ==> binomial n (SUC h) MOD n = 0) *) - -val binomial_mod_zero = binomialTheory.binomial_mod_zero; -(* |- !n. 0 < n ==> !k. binomial n k MOD n = 0 <=> - !x y. (binomial n k * x ** (n - k) * y ** k) MOD n = 0 *) - -val binomial_range_shift_alt = binomialTheory.binomial_range_shift_alt; -(* |- !n. 0 < n ==> - ((!k. 0 < k /\ k < n ==> !x y. (binomial n k * x ** (n - k) * y ** k) MOD n = 0) <=> - !h. h < PRE n ==> !x y. (binomial n (SUC h) * x ** (n - SUC h) * y ** SUC h) MOD n = 0) *) - -val binomial_mod_zero_alt = binomialTheory.binomial_mod_zero_alt; -(* |- !n. 0 < n ==> - ((!k. 0 < k /\ k < n ==> binomial n k MOD n = 0) <=> - !x y. SUM (GENLIST ((\k. (binomial n k * x ** (n - k) * y ** k) MOD n) o SUC) (PRE n)) = 0) *) - -val binomial_thm_prime = binomialTheory.binomial_thm_prime; -(* |- !p. prime p ==> !x y. (x + y) ** p MOD p = (x ** p + y ** p) MOD p *) - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/little/FLTeulerScript.sml b/examples/fermat/little/FLTeulerScript.sml index ee49b5a44c..9d7ba349b1 100644 --- a/examples/fermat/little/FLTeulerScript.sml +++ b/examples/fermat/little/FLTeulerScript.sml @@ -42,20 +42,12 @@ val _ = new_theory "FLTeuler"; (* ------------------------------------------------------------------------- *) +open arithmeticTheory pred_setTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory; -(* open dependent theories *) -(* val _ = load "EulerTheory"; *) -open arithmeticTheory pred_setTheory; -open dividesTheory gcdTheory; (* for GCD_0R *) - -open helperNumTheory; (* for MOD_EXP *) - -(* val _ = load "finiteGroupTheory"; *) -(* val _ = load "groupInstancesTheory"; *) open groupTheory; (* for FiniteGroup_def *) open groupOrderTheory; (* for finite_group_Fermat *) - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Number Group Documentation *) (* ------------------------------------------------------------------------- *) @@ -112,58 +104,6 @@ open groupOrderTheory; (* for finite_group_Fermat *) (* Group-theoretic Proof appplied to E^{*}[n]. *) (* ------------------------------------------------------------------------- *) -(* ------------------------------------------------------------------------- *) -(* Relatively prime, or Coprime. *) -(* ------------------------------------------------------------------------- *) - -val coprime_mod = helperFunctionTheory.coprime_mod; -(* |- !m n. 0 < m /\ coprime m n ==> coprime m (n MOD m) *) - -val coprime_sym = helperFunctionTheory.coprime_sym; -(* |- !x y. coprime x y <=> coprime y x *) - -val MOD_NONZERO_WHEN_GCD_ONE = helperFunctionTheory.MOD_NONZERO_WHEN_GCD_ONE; -(* |- !n. 1 < n ==> !x. coprime n x ==> 0 < x /\ 0 < x MOD n *) - -val PRODUCT_WITH_GCD_ONE = helperFunctionTheory.PRODUCT_WITH_GCD_ONE; -(* |- !n x y. coprime n x /\ coprime n y ==> coprime n (x * y) *) - -val MOD_WITH_GCD_ONE = helperFunctionTheory.MOD_WITH_GCD_ONE; -(* |- !n x. 0 < n /\ coprime n x ==> coprime n (x MOD n) *) - -val GCD_DIVIDES = helperFunctionTheory.GCD_DIVIDES; -(* |- !m n. 0 < n /\ 0 < m ==> 0 < gcd n m /\ n MOD gcd n m = 0 /\ m MOD gcd n m = 0 *) - -val GCD_ONE_PROPERTY = helperFunctionTheory.GCD_ONE_PROPERTY; -(* |- !n x. 1 < n /\ coprime n x ==> ?k. (k * x) MOD n = 1 /\ coprime n k *) - -(* ------------------------------------------------------------------------- *) -(* Establish the existence of multiplicative inverse when p is prime. *) -(* ------------------------------------------------------------------------- *) - -val GCD_MOD_MULT_INV = helperFunctionTheory.GCD_MOD_MULT_INV; -(* |- !n x. 1 < n /\ coprime n x /\ 0 < x /\ x < n ==> - ?y. 0 < y /\ y < n /\ coprime n y /\ (y * x) MOD n = 1 *) - -(* Convert this into an existence definition *) -val GEN_MULT_INV_DEF = helperFunctionTheory.GEN_MULT_INV_DEF; -(* |- !n x. 1 < n /\ coprime n x /\ 0 < x /\ x < n ==> - 0 < GCD_MOD_MUL_INV n x /\ GCD_MOD_MUL_INV n x < n /\ - coprime n (GCD_MOD_MUL_INV n x) /\ (GCD_MOD_MUL_INV n x * x) MOD n = 1 *) - -(* ------------------------------------------------------------------------- *) -(* Euler's set and totient function *) -(* ------------------------------------------------------------------------- *) - -val Euler_def = EulerTheory.Euler_def; -(* |- !n. Euler n = {i | 0 < i /\ i < n /\ coprime n i} *) - -val totient_def = EulerTheory.totient_def; -(* |- !n. totient n = CARD (Euler n) *) - -val Euler_card_prime = EulerTheory.Euler_card_prime; -(* |- !p. prime p ==> totient p = p - 1 *) - (* ------------------------------------------------------------------------- *) (* Euler's group of coprimes. *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/little/FLTfixedpointScript.sml b/examples/fermat/little/FLTfixedpointScript.sml index 302b2cb0d4..7f3edba1d2 100644 --- a/examples/fermat/little/FLTfixedpointScript.sml +++ b/examples/fermat/little/FLTfixedpointScript.sml @@ -31,14 +31,10 @@ val _ = new_theory "FLTfixedpoint"; (* ------------------------------------------------------------------------- *) - (* open dependent theories *) -(* val _ = load "FLTactionTheory"; *) -open helperNumTheory helperSetTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for PRIME_POS *) +open arithmeticTheory pred_setTheory dividesTheory numberTheory + combinatoricsTheory; -open necklaceTheory; open cycleTheory; (* val _ = load "groupInstancesTheory"; *) @@ -46,7 +42,6 @@ open cycleTheory; open groupTheory; open groupActionTheory; - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Action Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/little/FLTgroupScript.sml b/examples/fermat/little/FLTgroupScript.sml index 7af727a597..2c83e45a15 100644 --- a/examples/fermat/little/FLTgroupScript.sml +++ b/examples/fermat/little/FLTgroupScript.sml @@ -37,22 +37,13 @@ which is Euler's generalization of Fermat's Little Theorem. (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; -(* declare new theory at start *) -val _ = new_theory "FLTgroup"; - -(* ------------------------------------------------------------------------- *) - +open arithmeticTheory pred_setTheory dividesTheory numberTheory + combinatoricsTheory; -(* open dependent theories *) -(* val _ = load "dividesTheory"; *) -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for PRIME_POS *) - -(* val _ = load "finiteGroupTheory"; *) -(* val _ = load "groupInstancesTheory"; *) open groupTheory; (* for FiniteGroup_def *) open groupOrderTheory; (* for finite_group_Fermat *) +val _ = new_theory "FLTgroup"; (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Number Group Documentation *) @@ -99,22 +90,6 @@ open groupOrderTheory; (* for finite_group_Fermat *) (* Establish the existence of multiplicative inverse when p is prime. *) (* ------------------------------------------------------------------------- *) -val EUCLID_LEMMA = helperNumTheory.EUCLID_LEMMA; -(* |- !p x y. prime p ==> ((x * y) MOD p = 0 <=> x MOD p = 0 \/ y MOD p = 0) -[Euclid's Lemma in MOD notation] -A prime divides a product iff the prime divides a factor. *) - -val MOD_MULT_INV_EXISTS = helperNumTheory.MOD_MULT_INV_EXISTS; -(* |- !p x. prime p /\ 0 < x /\ x < p ==> - ?y. 0 < y /\ y < p /\ ((y * x) MOD p = 1) -[Existence of Inverse] For prime p, 0 < x < p ==> ?y. (y * x) MOD p = 1 *) - -(* Convert this into an existence definition *) -val MOD_MULT_INV_DEF = helperNumTheory.MOD_MULT_INV_DEF; -(* |- !p x. prime p /\ 0 < x /\ x < p ==> - 0 < MOD_MULT_INV p x /\ MOD_MULT_INV p x < p /\ - (MOD_MULT_INV p x * x) MOD p = 1 *) - (* Part 2: General Theory -------------------------------------------------- *) (* ------------------------------------------------------------------------- *) @@ -125,18 +100,6 @@ val MOD_MULT_INV_DEF = helperNumTheory.MOD_MULT_INV_DEF; (* Residue -- a close-relative of COUNT *) (* ------------------------------------------------------------------------- *) -val residue_def = EulerTheory.residue_def; -(* |- !n. residue n = {i | 0 < i /\ i < n} *) - -val residue_count = EulerTheory.residue_count; -(* |- !n. 0 < n ==> count n = 0 INSERT residue n *) - -val residue_finite = EulerTheory.residue_finite; -(* |- !n. FINITE (residue n) *) - -val residue_card = EulerTheory.residue_card; -(* |- !n. 0 < n ==> CARD (residue n) = n - 1 *) - (* ------------------------------------------------------------------------- *) (* The Group Z^{*}[p] = Multiplication Modulo p, for prime p. *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/little/FLTnecklaceScript.sml b/examples/fermat/little/FLTnecklaceScript.sml index aa095879ce..f49c6158be 100644 --- a/examples/fermat/little/FLTnecklaceScript.sml +++ b/examples/fermat/little/FLTnecklaceScript.sml @@ -31,20 +31,11 @@ val _ = new_theory "FLTnecklace"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* val _ = load "patternTheory"; *) -open helperNumTheory helperSetTheory; -open arithmeticTheory pred_setTheory; +open arithmeticTheory dividesTheory logrootTheory gcdTheory pred_setTheory + numberTheory combinatoricsTheory; open cycleTheory patternTheory; -(* val _ = load "necklaceTheory"; *) -open necklaceTheory; -open dividesTheory; (* for PRIME_POS *) -open gcdTheory; (* for PRIME_GCD *) - - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by necklace Documentation *) (* ------------------------------------------------------------------------- *) @@ -1193,7 +1184,6 @@ Proof metis_tac[DIVIDES_MOD_0, MOD_EQ] QED - (* ------------------------------------------------------------------------- *) (* export theory at end *) diff --git a/examples/fermat/little/FLTnumberScript.sml b/examples/fermat/little/FLTnumberScript.sml index 715618a033..139f2b9465 100644 --- a/examples/fermat/little/FLTnumberScript.sml +++ b/examples/fermat/little/FLTnumberScript.sml @@ -76,18 +76,8 @@ val _ = new_theory "FLTnumber"; (* ------------------------------------------------------------------------- *) - -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; *) -open helperNumTheory helperSetTheory; -open arithmeticTheory pred_setTheory; -open helperFunctionTheory; (* for FACT_0, FACT_MOD_PRIME *) - -open dividesTheory; (* for PRIME_POS, ONE_LT_PRIME *) - -(* val _ = load "EulerTheory"; *) -open EulerTheory; (* for residue_def *) - +open arithmeticTheory pred_setTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory; (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Number Theory Documentation *) diff --git a/examples/fermat/little/FLTpetersenScript.sml b/examples/fermat/little/FLTpetersenScript.sml index 7782dd91b8..7f54412bd1 100644 --- a/examples/fermat/little/FLTpetersenScript.sml +++ b/examples/fermat/little/FLTpetersenScript.sml @@ -31,19 +31,12 @@ val _ = new_theory "FLTpetersen"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* val _ = load "FLTactionTheory"; *) -open helperNumTheory helperSetTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for PRIME_POS *) - -open necklaceTheory; (* for multicoloured_finite *) +open arithmeticTheory dividesTheory gcdTheory logrootTheory numberTheory; +open pred_setTheory combinatoricsTheory; open groupTheory; open groupActionTheory; - (* ------------------------------------------------------------------------- *) (* Fermat's Little Theorem by Action Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/little/Holmakefile b/examples/fermat/little/Holmakefile index 77830c8a87..f6375cb56f 100644 --- a/examples/fermat/little/Holmakefile +++ b/examples/fermat/little/Holmakefile @@ -1,7 +1,5 @@ # prefix to HOL examples LOC_PREFIX = $(HOLDIR)/examples -PRE_INCLUDES = $(LOC_PREFIX)/algebra/ring - -ALGEBRA_INCLUDES = lib monoid group +ALGEBRA_INCLUDES = group ring INCLUDES = $(patsubst %,$(LOC_PREFIX)/algebra/%,$(ALGEBRA_INCLUDES)) diff --git a/examples/fermat/little/cycleScript.sml b/examples/fermat/little/cycleScript.sml index 9f951048b7..0f0e72d562 100644 --- a/examples/fermat/little/cycleScript.sml +++ b/examples/fermat/little/cycleScript.sml @@ -43,14 +43,9 @@ val _ = new_theory "cycle"; (* ------------------------------------------------------------------------- *) - (* open dependent theories *) -(* val _ = load "helperFunctionTheory"; *) -open helperListTheory; (* for DROP_SUC, TAKE_SUC *) -open arithmeticTheory pred_setTheory listTheory; - -open gcdTheory; (* for GCD_0R, LINEAR_GCD *) - +open arithmeticTheory gcdTheory pred_setTheory listTheory rich_listTheory + combinatoricsTheory; (* ------------------------------------------------------------------------- *) (* Cycle Theory Documentation *) diff --git a/examples/fermat/little/necklaceScript.sml b/examples/fermat/little/necklaceScript.sml deleted file mode 100644 index 78a0318fd9..0000000000 --- a/examples/fermat/little/necklaceScript.sml +++ /dev/null @@ -1,1033 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Necklace Theory - monocoloured and multicoloured. *) -(* ------------------------------------------------------------------------- *) - -(* - -Necklace Theory -=============== - -Consider the set N of necklaces of length n (i.e. with number of beads = n) -with a colors (i.e. the number of bead colors = a). A linear picture of such -a necklace is: - -+--+--+--+--+--+--+--+ -|2 |4 |0 |3 |1 |2 |3 | p = 7, with (lots of) beads of a = 5 colors: 01234. -+--+--+--+--+--+--+--+ - -Since a bead can have any of the a colors, and there are n beads in total, - -Number of such necklaces = CARD N = a*a*...*a = a^n. - -There is only 1 necklace of pure color A, 1 necklace with pure color B, etc. - -Number of monocoloured necklaces = a = CARD S, where S = monocoloured necklaces. - -So, N = S UNION M, where M = multicoloured necklaces (i.e. more than one color). - -Since S and M are disjoint, CARD M = CARD N - CARD S = a^n - a. - -*) - -(*===========================================================================*) - -(*===========================================================================*) - -(* add all dependent libraries for script *) -open HolKernel boolLib bossLib Parse; - -(* declare new theory at start *) -val _ = new_theory "necklace"; - -(* ------------------------------------------------------------------------- *) - - -(* open dependent theories *) -(* val _ = load "helperFunctionTheory"; *) -open arithmeticTheory pred_setTheory listTheory; -open helperNumTheory helperSetTheory; -open helperListTheory; (* for LENGTH_NON_NIL, LIST_TO_SET_SING_IFF *) - - -(* ------------------------------------------------------------------------- *) -(* Necklace Theory Documentation *) -(* ------------------------------------------------------------------------- *) -(* Overloading: -*) -(* Definitions and Theorems (# are exported, ! are in compute): - - Necklace: - necklace_def |- !n a. necklace n a = - {ls | LENGTH ls = n /\ set ls SUBSET count a} - necklace_element |- !n a ls. ls IN necklace n a <=> - LENGTH ls = n /\ set ls SUBSET count a - necklace_length |- !n a ls. ls IN necklace n a ==> LENGTH ls = n - necklace_colors |- !n a ls. ls IN necklace n a ==> set ls SUBSET count a - necklace_property |- !n a ls. ls IN necklace n a ==> - LENGTH ls = n /\ set ls SUBSET count a - necklace_0 |- !a. necklace 0 a = {[]} - necklace_empty |- !n. 0 < n ==> (necklace n 0 = {}) - necklace_not_nil |- !n a ls. 0 < n /\ ls IN necklace n a ==> ls <> [] - necklace_suc |- !n a. necklace (SUC n) a = - IMAGE (\(c,ls). c::ls) (count a CROSS necklace n a) -! necklace_eqn |- !n a. necklace n a = - if n = 0 then {[]} - else IMAGE (\(c,ls). c::ls) (count a CROSS necklace (n - 1) a) - necklace_1 |- !a. necklace 1 a = {[e] | e IN count a} - necklace_finite |- !n a. FINITE (necklace n a) - necklace_card |- !n a. CARD (necklace n a) = a ** n - - Mono-colored necklace: - monocoloured_def |- !n a. monocoloured n a = - {ls | ls IN necklace n a /\ (ls <> [] ==> SING (set ls))} - monocoloured_element - |- !n a ls. ls IN monocoloured n a <=> - ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) - monocoloured_necklace |- !n a ls. ls IN monocoloured n a ==> ls IN necklace n a - monocoloured_subset |- !n a. monocoloured n a SUBSET necklace n a - monocoloured_finite |- !n a. FINITE (monocoloured n a) - monocoloured_0 |- !a. monocoloured 0 a = {[]} - monocoloured_1 |- !a. monocoloured 1 a = necklace 1 a - necklace_1_monocoloured - |- !a. necklace 1 a = monocoloured 1 a - monocoloured_empty|- !n. 0 < n ==> monocoloured n 0 = {} - monocoloured_mono |- !n. monocoloured n 1 = necklace n 1 - monocoloured_suc |- !n a. 0 < n ==> - monocoloured (SUC n) a = IMAGE (\ls. HD ls::ls) (monocoloured n a) - monocoloured_0_card |- !a. CARD (monocoloured 0 a) = 1 - monocoloured_card |- !n a. 0 < n ==> CARD (monocoloured n a) = a -! monocoloured_eqn |- !n a. monocoloured n a = - if n = 0 then {[]} - else IMAGE (\c. GENLIST (K c) n) (count a) - monocoloured_card_eqn |- !n a. CARD (monocoloured n a) = if n = 0 then 1 else a - - Multi-colored necklace: - multicoloured_def |- !n a. multicoloured n a = necklace n a DIFF monocoloured n a - multicoloured_element |- !n a ls. ls IN multicoloured n a <=> - ls IN necklace n a /\ ls <> [] /\ ~SING (set ls) - multicoloured_necklace|- !n a ls. ls IN multicoloured n a ==> ls IN necklace n a - multicoloured_subset |- !n a. multicoloured n a SUBSET necklace n a - multicoloured_finite |- !n a. FINITE (multicoloured n a) - multicoloured_0 |- !a. multicoloured 0 a = {} - multicoloured_1 |- !a. multicoloured 1 a = {} - multicoloured_n_0 |- !n. multicoloured n 0 = {} - multicoloured_n_1 |- !n. multicoloured n 1 = {} - multicoloured_empty |- !n. multicoloured n 0 = {} /\ multicoloured n 1 = {} - multi_mono_disjoint |- !n a. DISJOINT (multicoloured n a) (monocoloured n a) - multi_mono_exhaust |- !n a. necklace n a = multicoloured n a UNION monocoloured n a - multicoloured_card |- !n a. 0 < n ==> (CARD (multicoloured n a) = a ** n - a) - multicoloured_card_eqn|- !n a. CARD (multicoloured n a) = if n = 0 then 0 else a ** n - a - multicoloured_nonempty|- !n a. 1 < n /\ 1 < a ==> multicoloured n a <> {} - multicoloured_not_monocoloured - |- !n a ls. ls IN multicoloured n a ==> ls NOTIN monocoloured n a - multicoloured_not_monocoloured_iff - |- !n a ls. ls IN necklace n a ==> - (ls IN multicoloured n a <=> ls NOTIN monocoloured n a) - multicoloured_or_monocoloured - |- !n a ls. ls IN necklace n a ==> - ls IN multicoloured n a \/ ls IN monocoloured n a -*) - - -(* ------------------------------------------------------------------------- *) -(* Helper Theorems. *) -(* ------------------------------------------------------------------------- *) - -(* ------------------------------------------------------------------------- *) -(* Necklace *) -(* ------------------------------------------------------------------------- *) - -(* Define necklaces as lists of length n, i.e. with n beads, in a colors. *) -Definition necklace_def[nocompute]: - necklace n a = {ls | LENGTH ls = n /\ (set ls) SUBSET (count a) } -End -(* Note: use [nocompute] as this is not effective. *) - -(* Theorem: ls IN necklace n a <=> (LENGTH ls = n /\ (set ls) SUBSET (count a)) *) -(* Proof: by necklace_def *) -Theorem necklace_element: - !n a ls. ls IN necklace n a <=> (LENGTH ls = n /\ (set ls) SUBSET (count a)) -Proof - simp[necklace_def] -QED - -(* Theorem: ls IN (necklace n a) ==> LENGTH ls = n *) -(* Proof: by necklace_def *) -Theorem necklace_length: - !n a ls. ls IN (necklace n a) ==> LENGTH ls = n -Proof - simp[necklace_def] -QED - -(* Theorem: ls IN (necklace n a) ==> set ls SUBSET count a *) -(* Proof: by necklace_def *) -Theorem necklace_colors: - !n a ls. ls IN (necklace n a) ==> set ls SUBSET count a -Proof - simp[necklace_def] -QED - -(* Idea: If ls in (necklace n a), LENGTH ls = n and colors in count a. *) - -(* Theorem: ls IN (necklace n a) ==> LENGTH ls = n /\ set ls SUBSET count a *) -(* Proof: by necklace_def *) -Theorem necklace_property: - !n a ls. ls IN (necklace n a) ==> LENGTH ls = n /\ set ls SUBSET count a -Proof - simp[necklace_def] -QED - -(* ------------------------------------------------------------------------- *) -(* Know the necklaces. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: zero-length necklaces of whatever colors is the set of NIL. *) - -(* Theorem: necklace 0 a = {[]} *) -(* Proof: - necklace 0 a - = {ls | (LENGTH ls = 0) /\ (set ls) SUBSET (count a) } by necklace_def - = {ls | ls = [] /\ (set []) SUBSET (count a) } by LENGTH_NIL - = {ls | ls = [] /\ [] SUBSET (count a) } by LIST_TO_SET - = {[]} by EMPTY_SUBSET -*) -Theorem necklace_0: - !a. necklace 0 a = {[]} -Proof - rw[necklace_def, EXTENSION] >> - metis_tac[LIST_TO_SET, EMPTY_SUBSET] -QED - -(* Idea: necklaces with some length but 0 colors is EMPTY. *) - -(* Theorem: 0 < n ==> (necklace n 0 = {}) *) -(* Proof: - necklace n 0 - = {ls | LENGTH ls = n /\ (set ls) SUBSET (count 0) } by necklace_def - = {ls | LENGTH ls = n /\ (set ls) SUBSET {} by COUNT_0 - = {ls | LENGTH ls = n /\ (set ls = {}) } by SUBSET_EMPTY - = {ls | LENGTH ls = n /\ (ls = []) } by LIST_TO_SET_EQ_EMPTY - = {} by LENGTH_NIL, 0 < n -*) -Theorem necklace_empty: - !n. 0 < n ==> (necklace n 0 = {}) -Proof - rw[necklace_def, EXTENSION] -QED - -(* Idea: A necklace of length n <> 0 is non-NIL. *) - -(* Theorem: 0 < n /\ ls IN (necklace n a) ==> ls <> [] *) -(* Proof: - By contradiction, suppose ls = []. - Then n = LENGTH ls by necklace_element - = LENGTH [] = 0 by LENGTH_NIL - This contradicts 0 < n. -*) -Theorem necklace_not_nil: - !n a ls. 0 < n /\ ls IN (necklace n a) ==> ls <> [] -Proof - rw[necklace_def] >> - metis_tac[LENGTH_NON_NIL] -QED - -(* ------------------------------------------------------------------------- *) -(* To show: (necklace n a) is FINITE. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: Relate (necklace (n+1) a) to (necklace n a) for induction. *) - -(* Theorem: necklace (SUC n) a = - IMAGE (\(c, ls). c :: ls) (count a CROSS necklace n a) *) -(* Proof: - By necklace_def, EXTENSION, this is to show: - (1) LENGTH x = SUC n /\ set x SUBSET count a ==> - ?h t. (x = h::t) /\ h < a /\ (LENGTH t = n) /\ set t SUBSET count a - Note SUC n <> 0 by SUC_NOT_ZERO - so ?h t. x = h::t by list_CASES - Take these h, t, true by LENGTH, MEM - (2) h < a /\ set t SUBSET count a ==> x < a ==> LENGTH (h::t) = SUC (LENGTH t) - This is true by LENGTH - (3) h < a /\ set t SUBSET count a ==> set (h::t) SUBSET count a - Note set (h::t) c <=> - (c = h) \/ set t c by LIST_TO_SET_DEF - If c = h, h < a - ==> h IN count a by IN_COUNT - If set t c, set t SUBSET count a - ==> c IN count a by SUBSET_DEF - Thus set (h::t) SUBSET count a by SUBSET_DEF -*) -Theorem necklace_suc: - !n a. necklace (SUC n) a = - IMAGE (\(c, ls). c :: ls) (count a CROSS necklace n a) -Proof - rw[necklace_def, EXTENSION] >> - rw[pairTheory.EXISTS_PROD, EQ_IMP_THM] >| [ - `SUC n <> 0` by decide_tac >> - `?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - qexists_tac `h` >> - qexists_tac `t` >> - fs[], - simp[], - fs[] - ] -QED - -(* Idea: display the necklaces. *) - -(* Theorem: necklace n a = - if n = 0 then {[]} - else IMAGE (\(c,ls). c::ls) (count a CROSS necklace (n - 1) a) *) -(* Proof: by necklace_0, necklace_suc. *) -Theorem necklace_eqn[compute]: - !n a. necklace n a = - if n = 0 then {[]} - else IMAGE (\(c,ls). c::ls) (count a CROSS necklace (n - 1) a) -Proof - rw[necklace_0] >> - metis_tac[necklace_suc, num_CASES, SUC_SUB1] -QED - -(* -> EVAL ``necklace 3 2``; -= {[1; 1; 1]; [1; 1; 0]; [1; 0; 1]; [1; 0; 0]; [0; 1; 1]; [0; 1; 0]; [0; 0; 1]; [0; 0; 0]} -> EVAL ``necklace 2 3``; -= {[2; 2]; [2; 1]; [2; 0]; [1; 2]; [1; 1]; [1; 0]; [0; 2]; [0; 1]; [0; 0]} -*) - -(* Idea: Unit-length necklaces are singletons from (count a). *) - -(* Theorem: necklace 1 a = {[e] | e IN count a} *) -(* Proof: - Let f = (\(c,ls). c::ls). - necklace 1 a - = necklace (SUC 0) a by ONE - = IMAGE f ((count a) CROSS (necklace 0 a)) by necklace_suc - = IMAGE f ((count a) CROSS {[]}) by necklace_0 - = {[e] | e IN count a} by EXTENSION -*) -Theorem necklace_1: - !a. necklace 1 a = {[e] | e IN count a} -Proof - rewrite_tac[ONE] >> - rw[necklace_suc, necklace_0, pairTheory.EXISTS_PROD, EXTENSION] -QED - -(* Idea: The set of (necklace n a) is finite. *) - -(* Theorem: FINITE (necklace n a) *) -(* Proof: - By induction on n. - Base: FINITE (necklace 0 a) - Note necklace 0 a = {[]} by necklace_0 - and FINITE {[]} by FINITE_SING - Step: FINITE (necklace n a) ==> FINITE (necklace (SUC n) a) - Let f = (\(c, ls). c :: ls), b = count a, c = necklace n a. - Note necklace (SUC n) a - = IMAGE f (b CROSS c) by necklace_suc - and FINITE b by FINITE_COUNT - and FINITE c by induction hypothesis - so FINITE (b CROSS c) by FINITE_CROSS - Thus FINITE (necklace (SUC n) a) by IMAGE_FINITE -*) -Theorem necklace_finite: - !n a. FINITE (necklace n a) -Proof - rpt strip_tac >> - Induct_on `n` >- - simp[necklace_0] >> - simp[necklace_suc] -QED - -(* ------------------------------------------------------------------------- *) -(* To show: CARD (necklace n a) = a^n. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: size of (necklace n a) = a^n. *) - -(* Theorem: CARD (necklace n a) = a ** n *) -(* Proof: - By induction on n. - Base: CARD (necklace 0 a) = a ** 0 - CARD (necklace 0 a) - = CARD {[]} by necklace_0 - = 1 by CARD_SING - = a ** 0 by EXP_0 - Step: CARD (necklace n a) = a ** n ==> - CARD (necklace (SUC n) a) = a ** SUC n - Let f = (\(c, ls). c :: ls), b = count a, c = necklace n a. - Note FINITE b by FINITE_COUNT - and FINITE c by necklace_finite - and FINITE (b CROSS c) by FINITE_CROSS - Also INJ f (b CROSS c) univ(:num list) by INJ_DEF, CONS_11 - CARD (necklace (SUC n) a) - = CARD (IMAGE f (b CROSS c)) by necklace_suc - = CARD (b CROSS c) by INJ_CARD_IMAGE_EQN - = CARD b * CARD c by CARD_CROSS - = a * CARD c by CARD_COUNT - = a * a ** n by induction hypothesis - = a ** SUC n by EXP -*) -Theorem necklace_card: - !n a. CARD (necklace n a) = a ** n -Proof - rpt strip_tac >> - Induct_on `n` >- - simp[necklace_0] >> - qabbrev_tac `f = (\(c:num, ls:num list). c :: ls)` >> - qabbrev_tac `b = count a` >> - qabbrev_tac `c = necklace n a` >> - `FINITE b` by rw[FINITE_COUNT, Abbr`b`] >> - `FINITE c` by rw[necklace_finite, Abbr`c`] >> - `necklace (SUC n) a = IMAGE f (b CROSS c)` by rw[necklace_suc, Abbr`f`, Abbr`b`, Abbr`c`] >> - `INJ f (b CROSS c) univ(:num list)` by rw[INJ_DEF, pairTheory.FORALL_PROD, Abbr`f`] >> - `FINITE (b CROSS c)` by rw[FINITE_CROSS] >> - `CARD (necklace (SUC n) a) = CARD (b CROSS c)` by rw[INJ_CARD_IMAGE_EQN] >> - `_ = CARD b * CARD c` by rw[CARD_CROSS] >> - `_ = a * a ** n` by fs[Abbr`b`, Abbr`c`] >> - simp[EXP] -QED - -(* ------------------------------------------------------------------------- *) -(* Mono-colored necklace - necklace with a single color. *) -(* ------------------------------------------------------------------------- *) - -(* Define mono-colored necklace *) -Definition monocoloured_def[nocompute]: - monocoloured n a = - {ls | ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) } -End -(* Note: use [nocompute] as this is not effective. *) - -(* Theorem: ls IN monocoloured n a <=> - ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) *) -(* Proof: by monocoloured_def *) -Theorem monocoloured_element: - !n a ls. ls IN monocoloured n a <=> - ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) -Proof - simp[monocoloured_def] -QED - -(* ------------------------------------------------------------------------- *) -(* Know the Mono-coloured necklaces. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: A monocoloured necklace is indeed a necklace. *) - -(* Theorem: ls IN monocoloured n a ==> ls IN necklace n a *) -(* Proof: by monocoloured_def *) -Theorem monocoloured_necklace: - !n a ls. ls IN monocoloured n a ==> ls IN necklace n a -Proof - simp[monocoloured_def] -QED - -(* Idea: The monocoloured set is subset of necklace set. *) - -(* Theorem: (monocoloured n a) SUBSET (necklace n a) *) -(* Proof: by monocoloured_necklace, SUBSET_DEF *) -Theorem monocoloured_subset: - !n a. (monocoloured n a) SUBSET (necklace n a) -Proof - simp[monocoloured_necklace, SUBSET_DEF] -QED - -(* Idea: The monocoloured set is FINITE. *) - -(* Theorem: FINITE (monocoloured n a) *) -(* Proof: - Note (monocoloured n a) SUBSET (necklace n a) by monocoloured_subset - and FINITE (necklace n a) by necklace_finite - so FINITE (monocoloured n a) by SUBSET_FINITE -*) -Theorem monocoloured_finite: - !n a. FINITE (monocoloured n a) -Proof - metis_tac[monocoloured_subset, necklace_finite, SUBSET_FINITE] -QED - -(* Idea: Zero-length monocoloured set is singleton NIL. *) - -(* Theorem: monocoloured 0 a = {[]} *) -(* Proof: - monocoloured 0 a - = {ls | ls IN necklace 0 a /\ (ls <> [] ==> SING (set ls)) } by monocoloured_def - = {ls | ls IN {[]} /\ (ls <> [] ==> SING (set ls)) } by necklace_0 - = {[]} by IN_SING -*) -Theorem monocoloured_0: - !a. monocoloured 0 a = {[]} -Proof - rw[monocoloured_def, necklace_0, EXTENSION, EQ_IMP_THM] -QED - -(* Idea: Unit-length monocoloured set are necklaces of length 1. *) - -(* Theorem: monocoloured 1 a = necklace 1 a *) -(* Proof: - By necklace_def, monocoloured_def, EXTENSION, - this is to show: - (LENGTH x = 1) /\ set x SUBSET count a /\ (x <> [] ==> SING (set x)) <=> - (LENGTH x = 1) /\ set x SUBSET count a - This is true by LIST_TO_SET_SING -*) -Theorem monocoloured_1: - !a. monocoloured 1 a = necklace 1 a -Proof - rw[necklace_def, monocoloured_def, EXTENSION] >> - metis_tac[LIST_TO_SET_SING] -QED - -(* Idea: Unit-length necklaces are monocoloured. *) - -(* Theorem: necklace 1 a = monocoloured 1 a *) -(* Proof: by monocoloured_1 *) -Theorem necklace_1_monocoloured: - !a. necklace 1 a = monocoloured 1 a -Proof - simp[monocoloured_1] -QED - -(* Idea: Some non-NIL necklaces are monocoloured. *) - -(* Theorem: 0 < n ==> monocoloured n 0 = {} *) -(* Proof: - Note (monocoloured n 0) SUBSET (necklace n 0) by monocoloured_subset - but necklace n 0 = {} by necklace_empty - so monocoloured n 0 = {} by SUBSET_EMPTY -*) -Theorem monocoloured_empty: - !n. 0 < n ==> monocoloured n 0 = {} -Proof - metis_tac[monocoloured_subset, necklace_empty, SUBSET_EMPTY] -QED - -(* Idea: One-colour necklaces are monocoloured. *) - -(* Theorem: monocoloured n 1 = necklace n 1 *) -(* Proof: - By monocoloured_def, necklace_def, EXTENSION, - this is to show: - set x SUBSET count 1 /\ x <> [] ==> SING (set x) - Note count 1 = {0} by COUNT_1 - and set x <> {} by LIST_TO_SET - so set x = {0} by SUBSET_SING_IFF - or SING (set x) by SING_DEF -*) -Theorem monocoloured_mono: - !n. monocoloured n 1 = necklace n 1 -Proof - rw[monocoloured_def, necklace_def, EXTENSION, EQ_IMP_THM] >> - fs[COUNT_1] >> - `set x = {0}` by fs[SUBSET_SING_IFF] >> - simp[] -QED - -(* ------------------------------------------------------------------------- *) -(* To show: CARD (monocoloured n a) = a. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: Relate (monocoloured (SUC n) a) to (monocoloured n a) for induction. *) - -(* Theorem: 0 < n ==> (monocoloured (SUC n) a = - IMAGE (\ls. HD ls :: ls) (monocoloured n a)) *) -(* Proof: - By monocoloured_def, necklace_def, EXTENSION, this is to show: - (1) 0 < n /\ LENGTH x = SUC n /\ set x SUBSET count a /\ x <> [] ==> SING (set x) ==> - ?ls. (x = HD ls::ls) /\ (LENGTH ls = n /\ set ls SUBSET count a) /\ - (ls <> [] ==> SING (set ls)) - Note SUC n <> 0 by SUC_NOT_ZERO - so x <> [] by LENGTH_NIL - ==> ?h t. x = h::t by list_CASES - and LENGTH t = n by LENGTH - but t <> [] by LENGTH_NON_NIL, 0 < n - so ?k p. t = k::p by list_CASES - Thus x = h::k::p by above - Now h IN set x by MEM - and k IN set x by MEM, SUBSET_DEF - so h = k by IN_SING, SING (set x) - Let ls = t, - then set ls SUBSET count a by MEM, SUBSET_DEF - and SING (set ls) by LIST_TO_SET_DEF - (2) 0 < LENGTH ls /\ set ls SUBSET count a /\ ls <> [] ==> SING (set ls) ==> - LENGTH (HD ls::ls) = SUC (LENGTH ls) - This is true by LENGTH - (3) 0 < LENGTH ls /\ set ls SUBSET count a /\ ls <> [] ==> SING (set ls) ==> - set (HD ls::ls) SUBSET count a - Note ls <> [] by LENGTH_NON_NIL - so ?h t. ls = h::t by list_CASES - Also set (h::ls) x <=> - (x = h) \/ set t x by LIST_TO_SET_DEF - Thus set (h::ls) SUBSET count a by SUBSET_DEF - (4) 0 < LENGTH ls /\ set ls SUBSET count a /\ ls <> [] ==> SING (set ls) ==> - SING (set (HD ls::ls)) - Note ls <> [] by LENGTH_NON_NIL - so ?h t. ls = h::t by list_CASES - Also set (h::ls) x <=> - (x = h) \/ set t x by LIST_TO_SET_DEF - Thus SING (set (h::ls)) by SUBSET_DEF -*) -Theorem monocoloured_suc: - !n a. 0 < n ==> (monocoloured (SUC n) a = - IMAGE (\ls. HD ls :: ls) (monocoloured n a)) -Proof - rw[monocoloured_def, necklace_def, EXTENSION] >> - rw[pairTheory.EXISTS_PROD, EQ_IMP_THM] >| [ - `SUC n <> 0` by decide_tac >> - `x <> [] /\ ?h t. x = h::t` by metis_tac[LENGTH_NIL, list_CASES] >> - `LENGTH t = n` by fs[] >> - `t <> []` by metis_tac[LENGTH_NON_NIL] >> - `h IN set x` by fs[] >> - `?k p. t = k::p` by metis_tac[list_CASES] >> - `HD t IN set x` by rfs[SUBSET_DEF] >> - `HD t = h` by metis_tac[SING_DEF, IN_SING] >> - qexists_tac `t` >> - fs[], - simp[], - `ls <> [] /\ ?h t. ls = h::t` by metis_tac[LENGTH_NON_NIL, list_CASES] >> - fs[], - `ls <> [] /\ ?h t. ls = h::t` by metis_tac[LENGTH_NON_NIL, list_CASES] >> - fs[] - ] -QED - -(* Idea: size of (monocoloured 0 a) = 1. *) - -(* Theorem: CARD (monocoloured 0 a) = 1 *) -(* Proof: - Note monocoloured 0 a = {[]} by monocoloured_0 - so CARD (monocoloured 0 a) = 1 by CARD_SING -*) -Theorem monocoloured_0_card: - !a. CARD (monocoloured 0 a) = 1 -Proof - simp[monocoloured_0] -QED - -(* Idea: size of (monocoloured n a) = a. *) - -(* Theorem: 0 < n ==> CARD (monocoloured n a) = a *) -(* Proof: - By induction on n. - Base: 0 < 0 ==> (CARD (monocoloured 0 a) = a) - True by 0 < 0 = F. - Step: 0 < n ==> CARD (monocoloured n a) = a ==> - 0 < SUC n ==> (CARD (monocoloured (SUC n) a) = a) - If n = 0, - CARD (monocoloured (SUC 0) a) - = CARD (monocoloured 1 a) by ONE - = CARD (necklace 1 a) by monocoloured_1 - = a ** 1 by necklace_card - = a by EXP_1 - If n <> 0, then 0 < n. - Let f = (\ls. HD ls :: ls). - Then INJ f (monocoloured n a) - univ(:num list) by INJ_DEF, CONS_11 - and FINITE (monocoloured n a) by monocoloured_finite - CARD (monocoloured (SUC n) a) - = CARD (IMAGE f (monocoloured n a)) by monocoloured_suc - = CARD (monocoloured n a) by INJ_CARD_IMAGE_EQN - = a by induction hypothesis -*) -Theorem monocoloured_card: - !n a. 0 < n ==> CARD (monocoloured n a) = a -Proof - rpt strip_tac >> - Induct_on `n` >- - simp[] >> - (Cases_on `n = 0` >> simp[]) >- - simp[monocoloured_1, necklace_card] >> - qabbrev_tac `f = \ls:num list. HD ls :: ls` >> - `INJ f (monocoloured n a) univ(:num list)` by rw[INJ_DEF, Abbr`f`] >> - `FINITE (monocoloured n a)` by rw[monocoloured_finite] >> - `CARD (monocoloured (SUC n) a) = - CARD (IMAGE f (monocoloured n a))` by rw[monocoloured_suc, Abbr`f`] >> - `_ = CARD (monocoloured n a)` by rw[INJ_CARD_IMAGE_EQN] >> - fs[] -QED - -(* Theorem: monocoloured n a = - if n = 0 then {[]} else IMAGE (\c. GENLIST (K c) n) (count a) *) -(* Proof: - If n = 0, true by monocoloured_0 - If n <> 0, then 0 < n. - By monocoloured_def, necklace_def, EXTENSION, this is to show: - (1) 0 < LENGTH x /\ set x SUBSET count a /\ x <> [] ==> SING (set x) ==> - ?c. (x = GENLIST (K c) (LENGTH x)) /\ c < a - Note x <> [] by LENGTH_NON_NIL - so ?c. set x = {c} by SING_DEF - Then c < a by SUBSET_DEF, IN_COUNT - and x = GENLIST (K c) (LENGTH x) by LIST_TO_SET_SING_IFF - (2) c < a ==> LENGTH (GENLIST (K c) n) = n, - This is true by LENGTH_GENLIST - (3) c < a ==> set (GENLIST (K c) n) SUBSET count a - Note set (GENLIST (K c) n) = {c} by GENLIST_K_SET - so c < a ==> {c} SUBSET (count a) by SUBSET_DEF - (4) c < a /\ GENLIST (K c) n <> [] ==> SING (set (GENLIST (K c) n)) - Note set (GENLIST (K c) n) = {c} by GENLIST_K_SET - so SING (set (GENLIST (K c) n)) by SING_DEF -*) -Theorem monocoloured_eqn[compute]: - !n a. monocoloured n a = - if n = 0 then {[]} - else IMAGE (\c. GENLIST (K c) n) (count a) -Proof - rw_tac bool_ss[] >- - simp[monocoloured_0] >> - `0 < n` by decide_tac >> - rw[monocoloured_def, necklace_def, EXTENSION, EQ_IMP_THM] >| [ - `x <> []` by metis_tac[LENGTH_NON_NIL] >> - `SING (set x) /\ ?c. set x = {c}` by rw[GSYM SING_DEF] >> - `c < a` by fs[SUBSET_DEF] >> - `?b. x = GENLIST (K b) (LENGTH x)` by metis_tac[LIST_TO_SET_SING_IFF] >> - metis_tac[GENLIST_K_SET, IN_SING], - simp[], - rw[GENLIST_K_SET], - rw[GENLIST_K_SET] - ] -QED - -(* -> EVAL ``monocoloured 2 3``; = {[2; 2]; [1; 1]; [0; 0]}: thm -> EVAL ``monocoloured 3 2``; = {[1; 1; 1]; [0; 0; 0]}: thm -*) - -(* Slight improvement of a previous result. *) - -(* Theorem: CARD (monocoloured n a) = if n = 0 then 1 else a *) -(* Proof: - If n = 0, - CARD (monocoloured 0 a) - = CARD {[]} by monocoloured_eqn - = 1 by CARD_SING - If n <> 0, then 0 < n. - Let f = (\c:num. GENLIST (K c) n). - Then INJ f (count a) univ(:num list) - by INJ_DEF, GENLIST_K_SET, IN_SING - and FINITE (count a) by FINITE_COUNT - CARD (monocoloured n a) - = CARD (IMAGE f (count a)) by monocoloured_eqn - = CARD (count a) by INJ_CARD_IMAGE_EQN - = a by CARD_COUNT -*) -Theorem monocoloured_card_eqn: - !n a. CARD (monocoloured n a) = if n = 0 then 1 else a -Proof - rw[monocoloured_eqn] >> - qmatch_abbrev_tac `CARD (IMAGE f (count a)) = a` >> - `INJ f (count a) univ(:num list)` by - (rw[INJ_DEF, Abbr`f`] >> - `0 < n` by decide_tac >> - metis_tac[GENLIST_K_SET, IN_SING]) >> - rw[INJ_CARD_IMAGE_EQN] -QED - -(* ------------------------------------------------------------------------- *) -(* Multi-colored necklace *) -(* ------------------------------------------------------------------------- *) - -(* Define multi-colored necklace *) -Definition multicoloured_def: - multicoloured n a = (necklace n a) DIFF (monocoloured n a) -End -(* Note: EVAL can handle set DIFF. *) - -(* -> EVAL ``multicoloured 3 2``; -= {[1; 1; 0]; [1; 0; 1]; [1; 0; 0]; [0; 1; 1]; [0; 1; 0]; [0; 0; 1]}: thm -> EVAL ``multicoloured 2 3``; -= {[2; 1]; [2; 0]; [1; 2]; [1; 0]; [0; 2]; [0; 1]}: thm -*) - -(* Theorem: ls IN multicoloured n a <=> - ls IN necklace n a /\ ls <> [] /\ ~SING (set ls) *) -(* Proof: - ls IN multicoloured n a - <=> ls IN (necklace n a) DIFF (monocoloured n a) by multicoloured_def - <=> ls IN (necklace n a) /\ ls NOTIN (monocoloured n a) by IN_DIFF - <=> ls IN (necklace n a) /\ - ~ls IN necklace n a /\ (ls <> [] ==> SING (set ls)) by monocoloured_def - <=> ls IN (necklace n a) /\ ls <> [] /\ ~SING (set ls) by logical equivalence - - t /\ ~(t /\ (p ==> q)) - = t /\ (~t \/ ~(p ==> q)) - = t /\ ~t \/ (t /\ ~(~p \/ q)) - = t /\ (p /\ ~q) -*) -Theorem multicoloured_element: - !n a ls. ls IN multicoloured n a <=> - ls IN necklace n a /\ ls <> [] /\ ~SING (set ls) -Proof - (rw[multicoloured_def, monocoloured_def, EQ_IMP_THM] >> simp[]) -QED - -(* ------------------------------------------------------------------------- *) -(* Know the Multi-coloured necklaces. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: multicoloured is a necklace. *) - -(* Theorem: ls IN multicoloured n a ==> ls IN necklace n a *) -(* Proof: by multicoloured_def *) -Theorem multicoloured_necklace: - !n a ls. ls IN multicoloured n a ==> ls IN necklace n a -Proof - simp[multicoloured_def] -QED - -(* Idea: The multicoloured set is subset of necklace set. *) - -(* Theorem: (multicoloured n a) SUBSET (necklace n a) *) -(* Proof: - Note multicoloured n a - = (necklace n a) DIFF (monocoloured n a) by multicoloured_def - so (multicoloured n a) SUBSET (necklace n a) by DIFF_SUBSET -*) -Theorem multicoloured_subset: - !n a. (multicoloured n a) SUBSET (necklace n a) -Proof - simp[multicoloured_def] -QED - -(* Idea: multicoloured set is FINITE. *) - -(* Theorem: FINITE (multicoloured n a) *) -(* Proof: - Note multicoloured n a - = (necklace n a) DIFF (monocoloured n a) by multicoloured_def - and FINITE (necklace n a) by necklace_finite - so FINITE (multicoloured n a) by FINITE_DIFF -*) -Theorem multicoloured_finite: - !n a. FINITE (multicoloured n a) -Proof - simp[multicoloured_def, necklace_finite, FINITE_DIFF] -QED - -(* Idea: (multicoloured 0 a) is EMPTY. *) - -(* Theorem: multicoloured 0 a = {} *) -(* Proof: - multicoloured 0 a - = (necklace 0 a) DIFF (monocoloured 0 a) by multicoloured_def - = {[]} - {[]} by necklace_0, monocoloured_0 - = {} by DIFF_EQ_EMPTY -*) -Theorem multicoloured_0: - !a. multicoloured 0 a = {} -Proof - simp[multicoloured_def, necklace_0, monocoloured_0] -QED - -(* Idea: (mutlicoloured 1 a) is also EMPTY. *) - -(* Theorem: multicoloured 1 a = {} *) -(* Proof: - multicoloured 1 a - = (necklace 1 a) DIFF (monocoloured 1 a) by multicoloured_def - = (necklace 1 a) DIFF (necklace 1 a) by monocoloured_1 - = {} by DIFF_EQ_EMPTY -*) -Theorem multicoloured_1: - !a. multicoloured 1 a = {} -Proof - simp[multicoloured_def, monocoloured_1] -QED - -(* Idea: (multicoloured n 0) without color is EMPTY. *) - -(* Theorem: multicoloured n 0 = {} *) -(* Proof: - If n = 0, - Then multicoloured 0 0 = {} by multicoloured_0 - If n <> 0, then 0 < n. - multicoloured n 0 - = (necklace n 0) DIFF (monocoloured n 0) by multicoloured_def - = {} DIFF (monocoloured n 0) by necklace_empty - = {} by EMPTY_DIFF -*) -Theorem multicoloured_n_0: - !n. multicoloured n 0 = {} -Proof - rpt strip_tac >> - Cases_on `n = 0` >- - simp[multicoloured_0] >> - simp[multicoloured_def, necklace_empty] -QED - -(* Idea: (multicoloured n 1) with one color is EMPTY. *) - -(* Theorem: multicoloured n 1 = {} *) -(* Proof: - multicoloured n 1 - = (necklace n 1) DIFF (monocoloured n 1) by multicoloured_def - = {necklace n 1} DIFF (necklace n 1) by monocoloured_mono - = {} by DIFF_EQ_EMPTY -*) -Theorem multicoloured_n_1: - !n. multicoloured n 1 = {} -Proof - simp[multicoloured_def, monocoloured_mono] -QED - -(* Theorem: multicoloured n 0 = {} /\ multicoloured n 1 = {} *) -(* Proof: by multicoloured_n_0, multicoloured_n_1. *) -Theorem multicoloured_empty: - !n. multicoloured n 0 = {} /\ multicoloured n 1 = {} -Proof - simp[multicoloured_n_0, multicoloured_n_1] -QED - -(* ------------------------------------------------------------------------- *) -(* To show: CARD (multicoloured n a) = a^n - a. *) -(* ------------------------------------------------------------------------- *) - -(* Idea: a multicoloured necklace is not monocoloured. *) - -(* Theorem: DISJOINT (multicoloured n a) (monocoloured n a) *) -(* Proof: - Let s = necklace n a, t = monocoloured n a. - Then multicoloured n a = s DIFF t by multicoloured_def - so DISJOINT (multicoloured n a) t by DISJOINT_DIFF -*) -Theorem multi_mono_disjoint: - !n a. DISJOINT (multicoloured n a) (monocoloured n a) -Proof - simp[multicoloured_def, DISJOINT_DIFF] -QED - -(* Idea: a necklace is either monocoloured or multicolored. *) - -(* Theorem: necklace n a = (multicoloured n a) UNION (monocoloured n a) *) -(* Proof: - Let s = necklace n a, t = monocoloured n a. - Then multicoloured n a = s DIFF t by multicoloured_def - Now t SUBSET s by monocoloured_subset - so necklace n a = s - = (multicoloured n a) UNION t by UNION_DIFF -*) -Theorem multi_mono_exhaust: - !n a. necklace n a = (multicoloured n a) UNION (monocoloured n a) -Proof - simp[multicoloured_def, monocoloured_subset, UNION_DIFF] -QED - -(* Idea: size of (multicoloured n a) = a^n - a. *) - -(* Theorem: 0 < n ==> (CARD (multicoloured n a) = a ** n - a) *) -(* Proof: - Let s = necklace n a, - t = monocoloured n a. - Note t SUBSET s by monocoloured_subset - and FINITE s by necklace_finite - CARD (multicoloured n a) - = CARD (s DIFF t) by multicoloured_def - = CARD s - CARD t by SUBSET_DIFF_CARD, t SUBSET s - = a ** n - CARD t by necklace_card - = a ** n - a by monocoloured_card, 0 < n -*) -Theorem multicoloured_card: - !n a. 0 < n ==> (CARD (multicoloured n a) = a ** n - a) -Proof - rpt strip_tac >> - `(monocoloured n a) SUBSET (necklace n a)` by rw[monocoloured_subset] >> - `FINITE (necklace n a)` by rw[necklace_finite] >> - simp[multicoloured_def, SUBSET_DIFF_CARD, necklace_card, monocoloured_card] -QED - -(* Theorem: CARD (multicoloured n a) = if n = 0 then 0 else a ** n - a *) -(* Proof: - If n = 0, - CARD (multicoloured 0 a) - = CARD {} by multicoloured_0 - = 0 by CARD_EMPTY - If n <> 0, then 0 < n. - CARD (multicoloured 0 a) - = a ** n - a by multicoloured_card -*) -Theorem multicoloured_card_eqn: - !n a. CARD (multicoloured n a) = if n = 0 then 0 else a ** n - a -Proof - rpt strip_tac >> - Cases_on `n = 0` >- - simp[multicoloured_0] >> - simp[multicoloured_card] -QED - -(* Idea: (multicoloured n a) NOT empty when 1 < n /\ 1 < a. *) - -(* Theorem: 1 < n /\ 1 < a ==> (multicoloured n a) <> {} *) -(* Proof: - Let s = multicoloured n a. - Then FINITE s by multicoloured_finite - and CARD s = a ** n - a by multicoloured_card - Note a < a ** n by EXP_LT, 1 < a, 1 < n - Thus CARD s <> 0, - or s <> {} by CARD_EMPTY -*) -Theorem multicoloured_nonempty: - !n a. 1 < n /\ 1 < a ==> (multicoloured n a) <> {} -Proof - rpt strip_tac >> - qabbrev_tac `s = multicoloured n a` >> - `FINITE s` by rw[multicoloured_finite, Abbr`s`] >> - `CARD s = a ** n - a` by rw[multicoloured_card, Abbr`s`] >> - `a < a ** n` by rw[EXP_LT] >> - `CARD s <> 0` by decide_tac >> - rfs[] -QED - -(* ------------------------------------------------------------------------- *) - -(* For revised necklace proof using GCD. *) - -(* Idea: multicoloured lists are not monocoloured. *) - -(* Theorem: ls IN multicoloured n a ==> ~(ls IN monocoloured n a) *) -(* Proof: - Let s = necklace n a, - t = monocoloured n a. - Note multicoloured n a = s DIFF t by multicoloured_def - so ls IN multicoloured n a - ==> ls NOTIN t by IN_DIFF -*) -Theorem multicoloured_not_monocoloured: - !n a ls. ls IN multicoloured n a ==> ~(ls IN monocoloured n a) -Proof - simp[multicoloured_def] -QED - -(* Theorem: ls IN necklace n a ==> - (ls IN multicoloured n a <=> ~(ls IN monocoloured n a)) *) -(* Proof: - Let s = necklace n a, - t = monocoloured n a. - Note multicoloured n a = s DIFF t by multicoloured_def - so ls IN multicoloured n a - <=> ls IN s /\ ls NOTIN t by IN_DIFF -*) -Theorem multicoloured_not_monocoloured_iff: - !n a ls. ls IN necklace n a ==> - (ls IN multicoloured n a <=> ~(ls IN monocoloured n a)) -Proof - simp[multicoloured_def] -QED - -(* Theorem: ls IN necklace n a ==> - ls IN multicoloured n a \/ ls IN monocoloured n a *) -(* Proof: by multicoloured_def. *) -Theorem multicoloured_or_monocoloured: - !n a ls. ls IN necklace n a ==> - ls IN multicoloured n a \/ ls IN monocoloured n a -Proof - simp[multicoloured_def] -QED - - -(* ------------------------------------------------------------------------- *) - -(* export theory at end *) -val _ = export_theory(); - -(*===========================================================================*) diff --git a/examples/fermat/little/patternScript.sml b/examples/fermat/little/patternScript.sml index 467ad459c6..da15a2d8a7 100644 --- a/examples/fermat/little/patternScript.sml +++ b/examples/fermat/little/patternScript.sml @@ -116,18 +116,12 @@ val _ = new_theory "pattern"; (* ------------------------------------------------------------------------- *) - (* open dependent theories *) -(* val _ = load "cycleTheory"; *) -open arithmeticTheory pred_setTheory listTheory; -open helperNumTheory helperSetTheory; -open helperListTheory; (* for LENGTH_NON_NIL *) +open arithmeticTheory pred_setTheory listTheory numberTheory dividesTheory + combinatoricsTheory; open cycleTheory; -open dividesTheory; (* for prime_def, NOT_PRIME_0 *) - - (* ------------------------------------------------------------------------- *) (* Pattern Theory Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/twosq/Holmakefile b/examples/fermat/twosq/Holmakefile index 4368914df1..98aae4c86f 100644 --- a/examples/fermat/twosq/Holmakefile +++ b/examples/fermat/twosq/Holmakefile @@ -1,7 +1,5 @@ # prefix to HOL examples LOC_PREFIX = $(HOLDIR)/examples -PRE_INCLUDES = $(LOC_PREFIX)/algebra/ring - -ALGEBRA_INCLUDES = lib monoid group field polynomial finitefield +ALGEBRA_INCLUDES = group ring field polynomial finitefield INCLUDES = $(patsubst %,$(LOC_PREFIX)/algebra/%,$(ALGEBRA_INCLUDES)) diff --git a/examples/fermat/twosq/cornerScript.sml b/examples/fermat/twosq/cornerScript.sml index e9eba52a3c..0381d08f71 100644 --- a/examples/fermat/twosq/cornerScript.sml +++ b/examples/fermat/twosq/cornerScript.sml @@ -12,26 +12,20 @@ val _ = new_theory "corner"; (* ------------------------------------------------------------------------- *) +open arithmeticTheory pred_setTheory dividesTheory gcdsetTheory numberTheory + pairTheory combinatoricsTheory; -(* open dependent theories *) -(* arithmeticTheory -- load by default *) - -(* val _ = load "quarityTheory"; *) open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def, prime_def *) -open EulerTheory; (* for natural_finite, natural_card *) open quarityTheory; -open pairTheory; (* val _ = load "involuteFixTheory"; *) open involuteTheory; open involuteFixTheory; +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Fermat Two Squares by Corners Documentation *) diff --git a/examples/fermat/twosq/helperTwosqScript.sml b/examples/fermat/twosq/helperTwosqScript.sml index 3329996c5e..6dd612af3c 100644 --- a/examples/fermat/twosq/helperTwosqScript.sml +++ b/examples/fermat/twosq/helperTwosqScript.sml @@ -12,21 +12,8 @@ val _ = new_theory "helperTwosq"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -open helperFunctionTheory; -open helperSetTheory; -open helperNumTheory; - -(* arithmeticTheory -- load by default *) -open arithmeticTheory pred_setTheory; -open dividesTheory; -open gcdTheory; (* for GCD_IS_GREATEST_COMMON_DIVISOR *) - -open logPowerTheory; (* for SQRT *) - -open whileTheory; (* for HOARE_SPEC_DEF *) - +open arithmeticTheory pred_setTheory dividesTheory gcdTheory numberTheory + whileTheory combinatoricsTheory primeTheory; (* ------------------------------------------------------------------------- *) (* Helper Theorems Documentation *) diff --git a/examples/fermat/twosq/hoppingScript.sml b/examples/fermat/twosq/hoppingScript.sml index a5a1b2a177..5a9ce3393f 100644 --- a/examples/fermat/twosq/hoppingScript.sml +++ b/examples/fermat/twosq/hoppingScript.sml @@ -12,28 +12,14 @@ val _ = new_theory "hopping"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* arithmeticTheory -- load by default *) +open arithmeticTheory pred_setTheory logrootTheory dividesTheory pairTheory + listTheory rich_listTheory listRangeTheory indexedListsTheory + numberTheory combinatoricsTheory primeTheory; (* val _ = load "quarityTheory"; *) open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def, prime_def *) -open logPowerTheory; (* for square_alt *) - -open listTheory rich_listTheory; -open helperListTheory; -open listRangeTheory; (* for listRangeLHI_ALL_DISTINCT *) -open indexedListsTheory; (* for findi_EL and EL_findi *) - -open sublistTheory; open quarityTheory; -open pairTheory; (* val _ = load "twoSquaresTheory"; *) open windmillTheory; @@ -44,6 +30,9 @@ open iterateComposeTheory; (* for involute_involute_fix_orbit_fix_odd *) open iterateComputeTheory; (* for iterate_while_thm *) open twoSquaresTheory; (* for loop test found *) +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Node Hopping Algorithm Documentation *) diff --git a/examples/fermat/twosq/involuteActionScript.sml b/examples/fermat/twosq/involuteActionScript.sml index e3ba4fece8..b8aa2625c2 100644 --- a/examples/fermat/twosq/involuteActionScript.sml +++ b/examples/fermat/twosq/involuteActionScript.sml @@ -12,17 +12,10 @@ val _ = new_theory "involuteAction"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* arithmeticTheory -- load by default *) +open arithmeticTheory pred_setTheory numberTheory dividesTheory; (* val _ = load "helperTwosqTheory"; *) open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def, prime_def *) (* val _ = load "involuteTheory"; *) open involuteTheory; @@ -33,7 +26,6 @@ open groupInstancesTheory; (* for Zadd_def *) (* val _ = load "groupActionTheory"; *) open groupActionTheory; (* for fixed_points_def *) - (* ------------------------------------------------------------------------- *) (* Involution and Action Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/twosq/involuteFixScript.sml b/examples/fermat/twosq/involuteFixScript.sml index 50736ccfdb..4cce431ac3 100644 --- a/examples/fermat/twosq/involuteFixScript.sml +++ b/examples/fermat/twosq/involuteFixScript.sml @@ -12,18 +12,13 @@ val _ = new_theory "involuteFix"; (* ------------------------------------------------------------------------- *) +open arithmeticTheory pred_setTheory numberTheory gcdsetTheory + combinatoricsTheory; (* open dependent theories *) -open helperFunctionTheory; (* for FUNPOW_2 *) -open helperSetTheory; (* for BIJ_ELEMENT *) -open helperNumTheory; (* for MOD_EQ *) open helperTwosqTheory; (* for doublet_finite, doublet_card *) open involuteTheory; -(* arithmeticTheory -- load by default *) -open arithmeticTheory pred_setTheory; - - (* ------------------------------------------------------------------------- *) (* Involution Fix Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/twosq/involuteScript.sml b/examples/fermat/twosq/involuteScript.sml index 46807b00bb..e417e9719c 100644 --- a/examples/fermat/twosq/involuteScript.sml +++ b/examples/fermat/twosq/involuteScript.sml @@ -12,20 +12,11 @@ val _ = new_theory "involute"; (* ------------------------------------------------------------------------- *) +open arithmeticTheory pred_setTheory gcdsetTheory numberTheory + combinatoricsTheory; -(* open dependent theories *) -(* arithmeticTheory -- load by default *) - -(* val _ = load "helperTwosqTheory"; *) -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; open helperTwosqTheory; (* for FUNPOW_closure *) -(* arithmeticTheory -- load by default *) -open arithmeticTheory pred_setTheory; - - (* ------------------------------------------------------------------------- *) (* Involution: Basic Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/fermat/twosq/iterateComposeScript.sml b/examples/fermat/twosq/iterateComposeScript.sml index 9a9ec0a44c..b08167d94f 100644 --- a/examples/fermat/twosq/iterateComposeScript.sml +++ b/examples/fermat/twosq/iterateComposeScript.sml @@ -7,22 +7,16 @@ (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; +open arithmeticTheory pred_setTheory dividesTheory gcdsetTheory numberTheory + combinatoricsTheory; + (* declare new theory at start *) val _ = new_theory "iterateCompose"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* arithmeticTheory -- load by default *) - (* val _ = load "helperTwosqTheory"; *) open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def, prime_def *) (* val _ = load "involuteFixTheory"; *) open involuteTheory; (* for involute_bij *) @@ -32,6 +26,9 @@ open involuteFixTheory; open iterationTheory; open iterateComputeTheory; (* for iterate_while_thm *) +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Iteration of Involution Composition Documentation *) diff --git a/examples/fermat/twosq/iterateComputeScript.sml b/examples/fermat/twosq/iterateComputeScript.sml index 2a44839e18..66ef3dca35 100644 --- a/examples/fermat/twosq/iterateComputeScript.sml +++ b/examples/fermat/twosq/iterateComputeScript.sml @@ -12,24 +12,13 @@ val _ = new_theory "iterateCompute"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -open helperFunctionTheory; -open helperSetTheory; -open helperNumTheory; - -(* arithmeticTheory -- load by default *) -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def *) +open arithmeticTheory pred_setTheory dividesTheory numberTheory listTheory + rich_listTheory listRangeTheory combinatoricsTheory whileTheory; open iterationTheory; -open listTheory rich_listTheory listRangeTheory; -open helperListTheory; (* for listRangeINC_SNOC *) (* val _ = load "helperTwosqTheory"; *) open helperTwosqTheory; (* for WHILE_RULE_PRE_POST *) -open whileTheory; (* for WHILE definition *) - (* ------------------------------------------------------------------------- *) (* Iteration Period Computation Documentation *) @@ -651,27 +640,32 @@ Proof irule WHILE_RULE_PRE_POST >> qexists_tac `\x. MEM x ls /\ findi x ls <= n` >> qexists_tac `\x. 1 + c - findi x ls` >> - rw[] >| [ + rw[] >| (* 5 subgoals *) + [ (* goal 1 (of 5) *) `a = HD ls` by rw[iterate_trace_head, Abbr`ls`] >> metis_tac[HEAD_MEM, iterate_trace_non_nil], + (* goal 2 (of 5) *) `a = iterate a b 0` by simp[] >> `findi a ls = 0` by metis_tac[iterate_trace_element_idx, DECIDE``0 <= c``] >> decide_tac, + (* goal 3 (of 5) *) qabbrev_tac `p = iterate_period b a` >> qabbrev_tac `y = iterate a b c` >> - `findi y ls = c` by metis_tac[iterate_trace_element_idx, DECIDE``c <= c``] >> + `findi y ls = c` by metis_tac[iterate_trace_element_idx, DECIDE``c <= (c :num)``] >> `x <> y` by metis_tac[NOT_LESS] >> `findi (b x) ls = 1 + findi x ls` by metis_tac[iterate_trace_index] >> decide_tac, + (* goal 4 (of 5) *) qabbrev_tac `p = iterate_period b a` >> `?j. j <= c /\ (x = iterate a b j)` by metis_tac[iterate_trace_member_iff] >> `findi x ls = j` by metis_tac[iterate_trace_element_idx] >> `~(j < n)` by metis_tac[] >> `j = n` by decide_tac >> fs[], + (* goal 5 (of 5) *) qabbrev_tac `p = iterate_period b a` >> qabbrev_tac `y = iterate a b c` >> - `findi y ls = c` by metis_tac[iterate_trace_element_idx, DECIDE``c <= c``] >> + `findi y ls = c` by metis_tac[iterate_trace_element_idx, DECIDE``c <= (c :num)``] >> simp[whileTheory.HOARE_SPEC_DEF] >> rpt strip_tac >| [ `x <> y` by metis_tac[NOT_LESS] >> @@ -892,8 +886,6 @@ Proof fs[iterate_while_thm] QED - - (* ------------------------------------------------------------------------- *) (* export theory at end *) diff --git a/examples/fermat/twosq/iterationScript.sml b/examples/fermat/twosq/iterationScript.sml index 4059fbff5f..e4534ee979 100644 --- a/examples/fermat/twosq/iterationScript.sml +++ b/examples/fermat/twosq/iterationScript.sml @@ -12,17 +12,8 @@ val _ = new_theory "iteration"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* open helperTwosqTheory; *) -open helperFunctionTheory; -open helperSetTheory; -open helperNumTheory; - -(* arithmeticTheory -- load by default *) -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def *) - +open arithmeticTheory pred_setTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory; (* ------------------------------------------------------------------------- *) (* Iteration Period Documentation *) diff --git a/examples/fermat/twosq/quarityScript.sml b/examples/fermat/twosq/quarityScript.sml index e00a9a4317..cffd89ee45 100644 --- a/examples/fermat/twosq/quarityScript.sml +++ b/examples/fermat/twosq/quarityScript.sml @@ -7,33 +7,20 @@ (* add all dependent libraries for script *) open HolKernel boolLib bossLib Parse; +open arithmeticTheory pred_setTheory pairTheory listTheory rich_listTheory + listRangeTheory dividesTheory gcdTheory logrootTheory numberTheory + combinatoricsTheory primeTheory; + +open helperTwosqTheory windmillTheory; + (* declare new theory at start *) val _ = new_theory "quarity"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* arithmeticTheory -- load by default *) -(* val _ = load "helperTwosqTheory"; *) -open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperListTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open listTheory rich_listTheory listRangeTheory; -(* val _ = load "dividesTheory"; *) -open dividesTheory; - -(* val _ = load "windmillTheory"; *) -open windmillTheory; - -open pairTheory; (* for FORALL_PROD, PAIR_EQ *) - -(* val _ = load "GaussTheory"; *) -open logrootTheory logPowerTheory GaussTheory EulerTheory; (* for SQRT, divisors_upper_bound *) - +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Quarity Documentation *) diff --git a/examples/fermat/twosq/twoSquaresScript.sml b/examples/fermat/twosq/twoSquaresScript.sml index 2e21df9d3f..8ea9bdb464 100644 --- a/examples/fermat/twosq/twoSquaresScript.sml +++ b/examples/fermat/twosq/twoSquaresScript.sml @@ -12,18 +12,12 @@ val _ = new_theory "twoSquares"; (* ------------------------------------------------------------------------- *) - -(* open dependent theories *) -(* arithmeticTheory -- load by default *) - (* val _ = load "windmillTheory"; *) open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -open dividesTheory; (* for divides_def, prime_def *) -open logPowerTheory; (* for prime_non_square *) + +open arithmeticTheory pred_setTheory dividesTheory numberTheory gcdsetTheory + pairTheory listTheory rich_listTheory listRangeTheory combinatoricsTheory + primeTheory; open windmillTheory; @@ -38,21 +32,15 @@ open iterateComposeTheory; (* val _ = load "iterateComputeTheory"; *) open iterateComputeTheory; -(* for later computation *) -open listTheory; -open rich_listTheory; (* for MAP_REVERSE *) -open helperListTheory; (* for listRangeINC_LEN *) -open listRangeTheory; (* for listRangeINC_CONS *) - (* for group action *) (* val _ = load "involuteActionTheory"; *) open involuteActionTheory; open groupActionTheory; open groupInstancesTheory; -(* for pairs *) -open pairTheory; (* for ELIM_UNCURRY, PAIR_FST_SND_EQ, PAIR_EQ, FORALL_PROD *) - +val _ = temp_overload_on("SQ", ``\n. n * (n :num)``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Windmills of the minds Documentation *) diff --git a/examples/fermat/twosq/windmillScript.sml b/examples/fermat/twosq/windmillScript.sml index ec2a807c1b..0d22050ce7 100644 --- a/examples/fermat/twosq/windmillScript.sml +++ b/examples/fermat/twosq/windmillScript.sml @@ -12,30 +12,15 @@ val _ = new_theory "windmill"; (* ------------------------------------------------------------------------- *) - (* open dependent theories *) -(* arithmeticTheory -- load by default *) -(* val _ = load "helperTwosqTheory"; *) open helperTwosqTheory; -open helperNumTheory; -open helperSetTheory; -open helperFunctionTheory; -open arithmeticTheory pred_setTheory; -(* val _ = load "dividesTheory"; *) -open dividesTheory; -(* val _ = load "gcdTheory"; *) -open gcdTheory; (* for P_EUCLIDES *) -open pairTheory; (* for FORALL_PROD, PAIR_EQ *) +open arithmeticTheory pred_setTheory numberTheory combinatoricsTheory + dividesTheory gcdTheory pairTheory logrootTheory primeTheory; (* val _ = load "involuteFixTheory"; *) open involuteTheory involuteFixTheory; -(* val _ = load "GaussTheory"; *) -open logrootTheory logPowerTheory; (* for SQRT, SQRT_LE *) -open GaussTheory; (* for divisors_has_factor *) - - (* ------------------------------------------------------------------------- *) (* Windmills and Involutions Documentation *) (* ------------------------------------------------------------------------- *) diff --git a/examples/simple_complexity/lib/Holmakefile b/examples/simple_complexity/lib/Holmakefile index c37318a6e9..30e0860ab8 100644 --- a/examples/simple_complexity/lib/Holmakefile +++ b/examples/simple_complexity/lib/Holmakefile @@ -1 +1 @@ -INCLUDES = ../../algebra/lib +INCLUDES = $(HOLDIR)/src/algebra diff --git a/examples/simple_complexity/lib/bitsizeScript.sml b/examples/simple_complexity/lib/bitsizeScript.sml index 572987d0d1..c25a15a490 100644 --- a/examples/simple_complexity/lib/bitsizeScript.sml +++ b/examples/simple_complexity/lib/bitsizeScript.sml @@ -12,22 +12,16 @@ val _ = new_theory "bitsize"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "helperFunctionTheory"; (* has helperNum, helperSet *) *) -(* val _ = load "helperListTheory"; *) -open pred_setTheory arithmeticTheory dividesTheory gcdTheory; -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; (* for DIV_EQ_0 *) - -(* open dependent theories *) -open listTheory rich_listTheory; - -(* val _ = load "logPowerTheory"; *) -open logrootTheory logPowerTheory; (* for LOG_1, ulog *) +open pred_setTheory arithmeticTheory dividesTheory gcdTheory numberTheory + combinatoricsTheory listTheory rich_listTheory logrootTheory + primeTheory; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Bit Size for Numbers Documentation *) diff --git a/examples/simple_complexity/lib/complexityScript.sml b/examples/simple_complexity/lib/complexityScript.sml index 5a3a79ca9c..b457dc474d 100644 --- a/examples/simple_complexity/lib/complexityScript.sml +++ b/examples/simple_complexity/lib/complexityScript.sml @@ -12,22 +12,19 @@ val _ = new_theory "complexity"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* val _ = load "bitsizeTheory"; *) -open bitsizeTheory; -open logrootTheory; (* for LOG_1 *) - -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; -open logPowerTheory; (* for ulog_pos *) - (* open dependent theories *) -open prim_recTheory pred_setTheory arithmeticTheory dividesTheory gcdTheory; -open listTheory rich_listTheory;; +open prim_recTheory pred_setTheory arithmeticTheory dividesTheory gcdTheory + listTheory rich_listTheory logrootTheory numberTheory combinatoricsTheory + primeTheory; + +open bitsizeTheory; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); (* ------------------------------------------------------------------------- *) (* Computational Complexity Documentation *) diff --git a/examples/simple_complexity/loop/Holmakefile b/examples/simple_complexity/loop/Holmakefile index 862db19adc..807c307bb6 100644 --- a/examples/simple_complexity/loop/Holmakefile +++ b/examples/simple_complexity/loop/Holmakefile @@ -1 +1 @@ -INCLUDES = ../../algebra/lib ../lib +INCLUDES = ../lib diff --git a/examples/simple_complexity/loop/loopDecreaseScript.sml b/examples/simple_complexity/loop/loopDecreaseScript.sml index 0b8b13af5b..4794120266 100644 --- a/examples/simple_complexity/loop/loopDecreaseScript.sml +++ b/examples/simple_complexity/loop/loopDecreaseScript.sml @@ -12,22 +12,17 @@ val _ = new_theory "loopDecrease"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "loopTheory"; *) -open loopTheory; - (* open dependent theories *) -open arithmeticTheory; -open dividesTheory; -open helperNumTheory helperListTheory helperFunctionTheory; (* for DIV_EQUAL_0 *) -open listTheory rich_listTheory; -open listRangeTheory; +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory listTheory + rich_listTheory listRangeTheory; + +open loopTheory; +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Loop Recurrence with Decreasing argument Documentation *) diff --git a/examples/simple_complexity/loop/loopDivideScript.sml b/examples/simple_complexity/loop/loopDivideScript.sml index 711cf355f9..348c53cbd1 100644 --- a/examples/simple_complexity/loop/loopDivideScript.sml +++ b/examples/simple_complexity/loop/loopDivideScript.sml @@ -12,26 +12,20 @@ val _ = new_theory "loopDivide"; (* ------------------------------------------------------------------------- *) - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "loopTheory"; *) -open loopTheory; - -(* val _ = load "bitsizeTheory"; *) -open bitsizeTheory; - (* open dependent theories *) -open arithmeticTheory dividesTheory; -open helperNumTheory helperListTheory helperFunctionTheory; (* for DIV_EQUAL_0 *) -open listTheory rich_listTheory; -open listRangeTheory; +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory listTheory + rich_listTheory listRangeTheory logrootTheory; -open logrootTheory; (* for LOG_EQ_0 *) -open logPowerTheory; (* for LOG_LE_REVERSE *) +open loopTheory bitsizeTheory; +val _ = temp_overload_on("SQ", ``\n. n * n``); +val _ = temp_overload_on("HALF", ``\n. n DIV 2``); +val _ = temp_overload_on("TWICE", ``\n. 2 * n``); +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Loop Recurrence with Dividing argument Documentation *) diff --git a/examples/simple_complexity/loop/loopIncreaseScript.sml b/examples/simple_complexity/loop/loopIncreaseScript.sml index f69fe7bfbf..16b961b5ce 100644 --- a/examples/simple_complexity/loop/loopIncreaseScript.sml +++ b/examples/simple_complexity/loop/loopIncreaseScript.sml @@ -12,21 +12,17 @@ val _ = new_theory "loopIncrease"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "loopTheory"; *) -open loopTheory; - (* open dependent theories *) -open arithmeticTheory dividesTheory; -open helperNumTheory helperListTheory helperFunctionTheory; (* for DIV_EQUAL_0 *) -open listTheory rich_listTheory; -open listRangeTheory; +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory listTheory + rich_listTheory listRangeTheory; + +open loopTheory; +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Loop Recurrence with Increasing argument Documentation *) diff --git a/examples/simple_complexity/loop/loopListScript.sml b/examples/simple_complexity/loop/loopListScript.sml index aef9ce8f32..808e5b8221 100644 --- a/examples/simple_complexity/loop/loopListScript.sml +++ b/examples/simple_complexity/loop/loopListScript.sml @@ -12,8 +12,6 @@ val _ = new_theory "loopList"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; @@ -25,14 +23,11 @@ open loopTheory; open bitsizeTheory; (* open dependent theories *) -open arithmeticTheory dividesTheory; -open helperNumTheory helperListTheory helperFunctionTheory; -open listTheory rich_listTheory; -open listRangeTheory; - -(* val _ = load "sublistTheory"; *) -open sublistTheory; (* for sublist_drop *) +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory listTheory + rich_listTheory listRangeTheory; +(* Overload sublist by infix operator *) +val _ = temp_overload_on ("<=", ``sublist``); (* ------------------------------------------------------------------------- *) (* Loop Recurrence with List argument Documentation *) diff --git a/examples/simple_complexity/loop/loopMultiplyScript.sml b/examples/simple_complexity/loop/loopMultiplyScript.sml index 289307cdb6..9c6198dc3f 100644 --- a/examples/simple_complexity/loop/loopMultiplyScript.sml +++ b/examples/simple_complexity/loop/loopMultiplyScript.sml @@ -12,24 +12,17 @@ val _ = new_theory "loopMultiply"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "loopTheory"; *) -open loopTheory; - (* open dependent theories *) -open arithmeticTheory dividesTheory; -open helperNumTheory helperListTheory helperFunctionTheory; (* for DIV_EQUAL_0 *) -open listTheory rich_listTheory; -open listRangeTheory; +open arithmeticTheory dividesTheory numberTheory combinatoricsTheory listTheory + rich_listTheory listRangeTheory logrootTheory primeTheory; -(* val _ = load "logPowerTheory"; *) -open logrootTheory logPowerTheory; (* for mop_eqn *) +open loopTheory; +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Loop Recurrence with Multiplying argument Documentation *) diff --git a/examples/simple_complexity/loop/loopScript.sml b/examples/simple_complexity/loop/loopScript.sml index 79a0000f54..c65c1b4b68 100644 --- a/examples/simple_complexity/loop/loopScript.sml +++ b/examples/simple_complexity/loop/loopScript.sml @@ -12,21 +12,14 @@ val _ = new_theory "loop"; (* ------------------------------------------------------------------------- *) - - (* val _ = load "jcLib"; *) open jcLib; -(* Get dependent theories in lib *) -(* val _ = load "logPowerTheory"; (* has helperNum, helperSet, helperFunction *) *) -open helperNumTheory helperSetTheory helperListTheory helperFunctionTheory; - -(* open dependent theories *) -open listTheory rich_listTheory; -open listRangeTheory; - -open arithmeticTheory; +open arithmeticTheory listTheory rich_listTheory listRangeTheory numberTheory + combinatoricsTheory; +val _ = temp_overload_on ("RISING", ``\f. !x:num. x <= f x``); +val _ = temp_overload_on ("FALLING", ``\f. !x:num. f x <= x``); (* ------------------------------------------------------------------------- *) (* Loop Recurrence Documentation *) From 4630435c969379dc681d26c9243642fe1754641f Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Mon, 22 Apr 2024 04:19:06 +1000 Subject: [PATCH 3/8] Fixes of examples/lambda work due to one renamed theorem in rich_listTheory --- examples/lambda/barendregt/boehmScript.sml | 8 ++++---- examples/lambda/barendregt/chap2Script.sml | 4 ++-- examples/lambda/barendregt/chap3Script.sml | 7 +++++++ examples/lambda/barendregt/head_reductionScript.sml | 12 ++++++------ examples/lambda/barendregt/solvableScript.sml | 8 ++++---- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/examples/lambda/barendregt/boehmScript.sml b/examples/lambda/barendregt/boehmScript.sml index c621bb499e..4e4ea5f2b6 100644 --- a/examples/lambda/barendregt/boehmScript.sml +++ b/examples/lambda/barendregt/boehmScript.sml @@ -2762,7 +2762,7 @@ Proof >> ‘LENGTH as = d - m’ by rw [Abbr ‘as’, LENGTH_FRONT] >> qabbrev_tac ‘b = LAST l’ >> Know ‘l = SNOC b as’ - >- (ASM_SIMP_TAC std_ss [Abbr ‘as’, Abbr ‘b’, SNOC_LAST_FRONT]) + >- (ASM_SIMP_TAC std_ss [Abbr ‘as’, Abbr ‘b’, SNOC_OF_LAST_FRONT]) >> DISCH_TAC >> qabbrev_tac ‘p3 = MAP rightctxt (REVERSE (MAP VAR l))’ >> ‘Boehm_transform p3’ by rw [Abbr ‘p3’, MAP_MAP_o, GSYM MAP_REVERSE] @@ -3342,7 +3342,7 @@ Proof Know ‘LAMl Z (VAR z) = LAMl (FRONT Z) (LAM z (VAR z))’ >- (REWRITE_TAC [GSYM LAMl_SNOC] \\ Suff ‘SNOC z (FRONT Z) = Z’ >- Rewr \\ - qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_LAST_FRONT >> art []) >> Rewr' \\ + qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) >> Rewr' \\ REWRITE_TAC [appstar_APPEND] \\ qabbrev_tac ‘t :term = LAM z (VAR z)’ \\ MATCH_MP_TAC lameq_TRANS >> Q.EXISTS_TAC ‘t @* MAP VAR vs’ \\ @@ -3369,7 +3369,7 @@ Proof Know ‘LAMl Z (VAR z) = LAMl (FRONT Z) (LAM z (VAR z))’ >- (REWRITE_TAC [GSYM LAMl_SNOC] \\ Suff ‘SNOC z (FRONT Z) = Z’ >- Rewr \\ - qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_LAST_FRONT >> art []) >> Rewr' \\ + qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) >> Rewr' \\ Know ‘args2' ++ MAP VAR vs = SNOC (VAR b0) (args2' ++ MAP VAR (FRONT vs))’ >- (qunabbrev_tac ‘b0’ \\ Know ‘VAR (LAST bs) :term = LAST (MAP VAR vs)’ @@ -3379,7 +3379,7 @@ Proof MATCH_MP_TAC (GSYM FRONT_APPEND_NOT_NIL) >> rw []) >> Rewr' \\ Suff ‘LAST (MAP VAR vs) = LAST (args2' ++ MAP VAR vs)’ >- (Rewr' >> qabbrev_tac ‘l = args2' ++ MAP VAR vs’ \\ - MATCH_MP_TAC (GSYM SNOC_LAST_FRONT) >> rw [Abbr ‘l’]) \\ + MATCH_MP_TAC SNOC_LAST_FRONT >> rw [Abbr ‘l’]) \\ MATCH_MP_TAC (GSYM LAST_APPEND_NOT_NIL) >> rw []) >> Rewr' \\ REWRITE_TAC [appstar_SNOC] \\ qabbrev_tac ‘t :term = LAM z (VAR z)’ \\ diff --git a/examples/lambda/barendregt/chap2Script.sml b/examples/lambda/barendregt/chap2Script.sml index 9b383a7bd2..adee2e9ba3 100644 --- a/examples/lambda/barendregt/chap2Script.sml +++ b/examples/lambda/barendregt/chap2Script.sml @@ -1225,7 +1225,7 @@ Proof >- (MATCH_MP_TAC LAMl_ALPHA_ssub >> rw [DISJOINT_SYM] \\ Suff ‘FV M = set Z’ >- METIS_TAC [DISJOINT_SYM] \\ rw [Abbr ‘M’, FV_appstar] \\ - ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_LAST_FRONT] \\ + ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] \\ POP_ORW \\ simp [LIST_TO_SET_SNOC] \\ rw [Once EXTENSION, MEM_MAP] \\ @@ -1241,7 +1241,7 @@ Proof >> qabbrev_tac ‘y = LAST Y’ (* postfix for LAMl_ALPHA_ssub *) >> ‘!t. LAMl Y t = LAMl (SNOC y (FRONT Y)) t’ - by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_LAST_FRONT]) >> POP_ORW + by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_OF_LAST_FRONT]) >> POP_ORW >> REWRITE_TAC [LAMl_SNOC] >> Know ‘fm ' M = VAR y @* MAP VAR (FRONT Y)’ >- (simp [Abbr ‘M’, ssub_appstar] \\ diff --git a/examples/lambda/barendregt/chap3Script.sml b/examples/lambda/barendregt/chap3Script.sml index 5d295c27cc..1ab3be1d79 100644 --- a/examples/lambda/barendregt/chap3Script.sml +++ b/examples/lambda/barendregt/chap3Script.sml @@ -1164,6 +1164,13 @@ val beta_eta_lameta = store_thm( ] ]); +(* |- !M N. + lameta M N ==> ?Z. reduction (beta RUNION eta) M Z /\ + reduction (beta RUNION eta) N Z + *) +Theorem lameta_CR = REWRITE_RULE [beta_eta_lameta, beta_eta_CR] + (Q.SPEC ‘beta RUNION eta’ theorem3_13) + val beta_eta_normal_form_benf = store_thm( "beta_eta_normal_form_benf", ``normal_form (beta RUNION eta) = benf``, diff --git a/examples/lambda/barendregt/head_reductionScript.sml b/examples/lambda/barendregt/head_reductionScript.sml index cae85908dd..2687d1e2ed 100644 --- a/examples/lambda/barendregt/head_reductionScript.sml +++ b/examples/lambda/barendregt/head_reductionScript.sml @@ -1654,7 +1654,7 @@ Theorem lameq_hnf_fresh_subst : Proof Induct_on ‘as’ using SNOC_INDUCT >> rw [] >> Cases_on ‘args = []’ >- fs [] - >> ‘args = SNOC (LAST args) (FRONT args)’ by PROVE_TAC [SNOC_LAST_FRONT] + >> ‘args = SNOC (LAST args) (FRONT args)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] >> POP_ORW >> REWRITE_TAC [appstar_SNOC, SUB_THM] >> MATCH_MP_TAC lameq_TRANS @@ -1951,7 +1951,7 @@ Proof >- (MATCH_MP_TAC LAMl_ALPHA_ssub >> rw [DISJOINT_SYM] \\ Suff ‘FV M = set Z’ >- METIS_TAC [DISJOINT_SYM] \\ rw [Abbr ‘M’, FV_appstar] \\ - ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_LAST_FRONT] \\ + ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] \\ POP_ORW \\ simp [LIST_TO_SET_SNOC] \\ rw [Once EXTENSION, MEM_MAP] \\ @@ -1966,7 +1966,7 @@ Proof >> ‘FDOM fm = set Z’ by rw [FDOM_fromPairs, Abbr ‘fm’] >> qabbrev_tac ‘y = LAST Y’ >> ‘!t. LAMl Y t = LAMl (SNOC y (FRONT Y)) t’ - by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_LAST_FRONT]) >> POP_ORW + by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_OF_LAST_FRONT]) >> POP_ORW >> REWRITE_TAC [LAMl_SNOC] >> Know ‘fm ' M = VAR y @* MAP VAR (FRONT Y)’ >- (simp [Abbr ‘M’, ssub_appstar] \\ @@ -2066,7 +2066,7 @@ Proof Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ Know ‘SNOC y vs = Y’ >- (qunabbrevl_tac [‘y’, ‘vs’] \\ - MATCH_MP_TAC SNOC_LAST_FRONT >> art []) \\ + MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) \\ DISCH_THEN (ONCE_REWRITE_TAC o wrap o SYM) \\ rw [ALL_DISTINCT_SNOC] >- (qunabbrev_tac ‘vs1’ >> PROVE_TAC [MEM_TAKE]) \\ @@ -2094,7 +2094,7 @@ Proof >- (Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ Know ‘SNOC y vs = Y’ >- (qunabbrevl_tac [‘y’, ‘vs’] \\ - MATCH_MP_TAC SNOC_LAST_FRONT >> art []) \\ + MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) \\ DISCH_THEN (ONCE_REWRITE_TAC o wrap o SYM) \\ rw [ALL_DISTINCT_SNOC] \\ qunabbrev_tac ‘vs1’ >> PROVE_TAC [MEM_TAKE]) @@ -2139,7 +2139,7 @@ Proof Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ Know ‘SNOC y vs = Y’ >- (qunabbrevl_tac [‘y’, ‘vs’] \\ - MATCH_MP_TAC SNOC_LAST_FRONT >> art []) \\ + MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) \\ DISCH_THEN (ONCE_REWRITE_TAC o wrap o SYM) \\ rw [ALL_DISTINCT_SNOC] \\ CCONTR_TAC >> fs [Abbr ‘vs2’] \\ diff --git a/examples/lambda/barendregt/solvableScript.sml b/examples/lambda/barendregt/solvableScript.sml index 0c6d1800ad..d5c5233be1 100644 --- a/examples/lambda/barendregt/solvableScript.sml +++ b/examples/lambda/barendregt/solvableScript.sml @@ -1610,7 +1610,7 @@ Proof >- (MATCH_MP_TAC LAMl_ALPHA_ssub >> rw [DISJOINT_SYM] \\ Suff ‘FV M = set Z’ >- METIS_TAC [DISJOINT_SYM] \\ rw [Abbr ‘M’, FV_appstar] \\ - ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_LAST_FRONT] \\ + ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] \\ POP_ORW \\ simp [LIST_TO_SET_SNOC] \\ rw [Once EXTENSION, MEM_MAP] \\ @@ -1626,7 +1626,7 @@ Proof >> qabbrev_tac ‘y = LAST Y’ (* postfix for LAMl_ALPHA_ssub *) >> ‘!t. LAMl Y t = LAMl (SNOC y (FRONT Y)) t’ - by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_LAST_FRONT]) >> POP_ORW + by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_OF_LAST_FRONT]) >> POP_ORW >> REWRITE_TAC [LAMl_SNOC] >> Know ‘fm ' M = VAR y @* MAP VAR (FRONT Y)’ >- (simp [Abbr ‘M’, ssub_appstar] \\ @@ -1657,11 +1657,11 @@ Proof >> qabbrev_tac ‘vs = FRONT Y’ >> Know ‘ALL_DISTINCT vs /\ ~MEM y vs’ >- (Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ - ‘Y = SNOC y vs’ by METIS_TAC [SNOC_LAST_FRONT] >> POP_ORW \\ + ‘Y = SNOC y vs’ by METIS_TAC [SNOC_OF_LAST_FRONT] >> POP_ORW \\ rw [ALL_DISTINCT_SNOC]) >> STRIP_TAC >> MATCH_MP_TAC principle_hnf_permutator_lemma - >> ‘SNOC y vs = Y’ by METIS_TAC [SNOC_LAST_FRONT] >> POP_ORW + >> ‘SNOC y vs = Y’ by METIS_TAC [SNOC_OF_LAST_FRONT] >> POP_ORW >> rw [EVERY_MEM, Abbr ‘vs’, LENGTH_FRONT] >- art [] >> FIRST_X_ASSUM MATCH_MP_TAC >> Q.EXISTS_TAC ‘e’ >> art [] From 12c5f2ef701ef39c457840d4980b9af64392dcab Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Mon, 22 Apr 2024 05:07:05 +1000 Subject: [PATCH 4/8] Mention new algebra theories in next-release.md (using some words from examples/algebra/monoid/README.md) --- doc/next-release.md | 10 ++++++++++ examples/algebra/monoid/README.md | 14 -------------- examples/algebra/monoid/files.txt | 29 ----------------------------- 3 files changed, 10 insertions(+), 43 deletions(-) delete mode 100644 examples/algebra/monoid/README.md delete mode 100644 examples/algebra/monoid/files.txt diff --git a/doc/next-release.md b/doc/next-release.md index 0086c27c92..00e6040d12 100644 --- a/doc/next-release.md +++ b/doc/next-release.md @@ -70,6 +70,14 @@ New theories: in Ethereum, is included and was the basis for this work. A rudimentary computable version based on sptrees is included; faster evaluation using cvcompute is left for future work. + +- `monoid` and `real_algebra`: These are combined theory using materials ever in + `examples/algebra/monoid`. A monoid as an algebraic structure: with a carrier set, + a binary operation and an identity element. + +- `number`, `combinatorics` and `prime`: These are combined theory using materials + from `examples/algebra/lib`, etc. They contain some more advanced results from + number theory (in particular properties of prime numbers) and combinatorics. New tools: ---------- @@ -173,6 +181,8 @@ number of components. - __armv8-memory-model__ (in `examples/arm`): a port by Anthony Fox of Viktor Vafeiadis’s [Coq formalization of the Armv8 Memory Model](https://github.com/vafeiadis/arm-model), which is based on the official [mixed-size Armv8 memory model](https://github.com/herd/herdtools7/blob/95785c747750be4a3b64adfab9d5f5ee0ead8240/herd/libdir/aarch64.cat) and associated [paper](https://doi.org/10.1145/3458926). +- __Data Encryption Standard (DES)__ (in `examples/Crypto/DES`) is added to HOL's collection of block ciphers. + - __*p*-adic numbers__ (in `examples/padics`): a construction of the *p*-adic numbers by Noah Gorrell. The approach taken defines the prime valuation function *ν* on first the natural numbers and then the rationals. It then defines the absolute value on ℚ so as to establish a $p$-metric. diff --git a/examples/algebra/monoid/README.md b/examples/algebra/monoid/README.md deleted file mode 100644 index d3715abce4..0000000000 --- a/examples/algebra/monoid/README.md +++ /dev/null @@ -1,14 +0,0 @@ - -# Monoid Library - -A monoid as an algebraic structure: with a carrier set, a binary operation and an identity element. - -## Theory -* __monoid__, axioms and basic properties. -* __monoidOrder__, exponentiation and element order, -* __monoidMap__, homomorphism and isomorphism between monoids. -* __submonoid__, properties of submonoid, as homomorphic image of identity map. - -## Application -* __monoidInstances__, Zn as an additive monoid, also a multiplicative monoid. - diff --git a/examples/algebra/monoid/files.txt b/examples/algebra/monoid/files.txt deleted file mode 100644 index 5a90ec8c93..0000000000 --- a/examples/algebra/monoid/files.txt +++ /dev/null @@ -1,29 +0,0 @@ -(* ------------------------------------------------------------------------- *) -(* Hierarchy of Monoid Library *) -(* *) -(* Author: Joseph Chan *) -(* Date: December, 2014 *) -(* ------------------------------------------------------------------------- *) - -0 monoid -- monoid axioms and basic properties. -* pred_set - -1 monoidOrder -- monoid exponentiation and element order, -* primePower -* 0 monoid - -2 monoidMap -- maps between monoids: homomorphism and isomorphism. -* 0 monoid -* 1 monoidOrder - -3 submonoid -- properties of submonoid, as homomorphic image of identity map. -* 0 monoid -* 2 monoidMap - -3 monoidInstances -- instances of monoid: (ZN n) as an addition monoid, also a multiplication monoid. -* divides -* gcd -* logPower -* 0 monoid -* 2 monoidMap - From 2da4265c965ef3ad80e39255b48657eb18e71210 Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Tue, 23 Apr 2024 10:31:06 +1000 Subject: [PATCH 5/8] Revert name changes of SNOC_LAST_FRONT for better backward compatibility --- examples/algebra/polynomial/polyWeakScript.sml | 8 ++++---- examples/lambda/barendregt/boehmScript.sml | 6 +++--- examples/lambda/barendregt/chap2Script.sml | 4 ++-- .../lambda/barendregt/head_reductionScript.sml | 12 ++++++------ examples/lambda/barendregt/solvableScript.sml | 8 ++++---- src/algebra/monoidScript.sml | 18 +++++++----------- src/list/src/listScript.sml | 8 ++------ src/list/src/rich_listScript.sml | 6 +++--- 8 files changed, 31 insertions(+), 39 deletions(-) diff --git a/examples/algebra/polynomial/polyWeakScript.sml b/examples/algebra/polynomial/polyWeakScript.sml index d83988eb9d..a0419af2f0 100644 --- a/examples/algebra/polynomial/polyWeakScript.sml +++ b/examples/algebra/polynomial/polyWeakScript.sml @@ -1361,7 +1361,7 @@ val weak_cmult_snoc = store_thm( (* Proof: c o p = MAP (\x. c * x) p by weak_cmult_map - = MAP (\x. c * x) (SNOC (LAST p) (FRONT p)) by SNOC_LAST_FRONT + = MAP (\x. c * x) (SNOC (LAST p) (FRONT p)) by SNOC_LAST_FRONT' = SNOC (\x. c * x) (LAST p) (MAP (\x. c * x) (FRONT p)) by MAP_SNOC = SNOC (c * LAST p) (c o FRONT p) by weak_cmult_map @@ -1375,7 +1375,7 @@ val weak_cmult_front_last = store_thm( ntac 4 strip_tac >> qabbrev_tac `f = \(x:'a). c * x` >> `c o p = MAP f p` by rw[weak_cmult_map, Abbr`f`] >> - `_ = MAP f (SNOC (LAST p) (FRONT p))` by metis_tac[SNOC_LAST_FRONT, poly_zero] >> + `_ = MAP f (SNOC (LAST p) (FRONT p))` by metis_tac[SNOC_LAST_FRONT', poly_zero] >> `_ = SNOC (f (LAST p)) (MAP f (FRONT p))` by rw[MAP_SNOC] >> `_ = SNOC (c * LAST p) (c o FRONT p)` by rw[weak_cmult_map, weak_front_last, Abbr`f`] >> rw[]); @@ -4215,7 +4215,7 @@ QED Note p <> [] by poly_zero If part: lead p = #0 ==> chop (FRONT p) = chop p chop p - = chop (SNOC (LAST p) (FRONT p)) by SNOC_LAST_FRONT, p <> [] + = chop (SNOC (LAST p) (FRONT p)) by SNOC_LAST_FRONT', p <> [] = chop (SNOC (lead p) (FRONT p)) by poly_lead_alt], p <> |0| = chop (SNOC #0 (FRONT p)) by given = chop (FRONT p) by poly_chop_alt @@ -4237,7 +4237,7 @@ val poly_chop_front = store_thm( rpt strip_tac >> `p <> []` by metis_tac[poly_zero] >> rw_tac std_ss[EQ_IMP_THM] >| [ - `p = SNOC (LAST p) (FRONT p)` by rw[SNOC_LAST_FRONT] >> + `p = SNOC (LAST p) (FRONT p)` by rw[SNOC_LAST_FRONT'] >> `LAST p = lead p` by rw[GSYM poly_lead_alt] >> metis_tac[poly_chop_alt], spose_not_then strip_assume_tac >> diff --git a/examples/lambda/barendregt/boehmScript.sml b/examples/lambda/barendregt/boehmScript.sml index 4e4ea5f2b6..98b53277f2 100644 --- a/examples/lambda/barendregt/boehmScript.sml +++ b/examples/lambda/barendregt/boehmScript.sml @@ -2762,7 +2762,7 @@ Proof >> ‘LENGTH as = d - m’ by rw [Abbr ‘as’, LENGTH_FRONT] >> qabbrev_tac ‘b = LAST l’ >> Know ‘l = SNOC b as’ - >- (ASM_SIMP_TAC std_ss [Abbr ‘as’, Abbr ‘b’, SNOC_OF_LAST_FRONT]) + >- (ASM_SIMP_TAC std_ss [Abbr ‘as’, Abbr ‘b’, SNOC_LAST_FRONT]) >> DISCH_TAC >> qabbrev_tac ‘p3 = MAP rightctxt (REVERSE (MAP VAR l))’ >> ‘Boehm_transform p3’ by rw [Abbr ‘p3’, MAP_MAP_o, GSYM MAP_REVERSE] @@ -3342,7 +3342,7 @@ Proof Know ‘LAMl Z (VAR z) = LAMl (FRONT Z) (LAM z (VAR z))’ >- (REWRITE_TAC [GSYM LAMl_SNOC] \\ Suff ‘SNOC z (FRONT Z) = Z’ >- Rewr \\ - qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) >> Rewr' \\ + qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_LAST_FRONT >> art []) >> Rewr' \\ REWRITE_TAC [appstar_APPEND] \\ qabbrev_tac ‘t :term = LAM z (VAR z)’ \\ MATCH_MP_TAC lameq_TRANS >> Q.EXISTS_TAC ‘t @* MAP VAR vs’ \\ @@ -3369,7 +3369,7 @@ Proof Know ‘LAMl Z (VAR z) = LAMl (FRONT Z) (LAM z (VAR z))’ >- (REWRITE_TAC [GSYM LAMl_SNOC] \\ Suff ‘SNOC z (FRONT Z) = Z’ >- Rewr \\ - qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) >> Rewr' \\ + qunabbrev_tac ‘z’ >> MATCH_MP_TAC SNOC_LAST_FRONT >> art []) >> Rewr' \\ Know ‘args2' ++ MAP VAR vs = SNOC (VAR b0) (args2' ++ MAP VAR (FRONT vs))’ >- (qunabbrev_tac ‘b0’ \\ Know ‘VAR (LAST bs) :term = LAST (MAP VAR vs)’ diff --git a/examples/lambda/barendregt/chap2Script.sml b/examples/lambda/barendregt/chap2Script.sml index adee2e9ba3..9b383a7bd2 100644 --- a/examples/lambda/barendregt/chap2Script.sml +++ b/examples/lambda/barendregt/chap2Script.sml @@ -1225,7 +1225,7 @@ Proof >- (MATCH_MP_TAC LAMl_ALPHA_ssub >> rw [DISJOINT_SYM] \\ Suff ‘FV M = set Z’ >- METIS_TAC [DISJOINT_SYM] \\ rw [Abbr ‘M’, FV_appstar] \\ - ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] \\ + ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_LAST_FRONT] \\ POP_ORW \\ simp [LIST_TO_SET_SNOC] \\ rw [Once EXTENSION, MEM_MAP] \\ @@ -1241,7 +1241,7 @@ Proof >> qabbrev_tac ‘y = LAST Y’ (* postfix for LAMl_ALPHA_ssub *) >> ‘!t. LAMl Y t = LAMl (SNOC y (FRONT Y)) t’ - by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_OF_LAST_FRONT]) >> POP_ORW + by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_LAST_FRONT]) >> POP_ORW >> REWRITE_TAC [LAMl_SNOC] >> Know ‘fm ' M = VAR y @* MAP VAR (FRONT Y)’ >- (simp [Abbr ‘M’, ssub_appstar] \\ diff --git a/examples/lambda/barendregt/head_reductionScript.sml b/examples/lambda/barendregt/head_reductionScript.sml index 2687d1e2ed..cae85908dd 100644 --- a/examples/lambda/barendregt/head_reductionScript.sml +++ b/examples/lambda/barendregt/head_reductionScript.sml @@ -1654,7 +1654,7 @@ Theorem lameq_hnf_fresh_subst : Proof Induct_on ‘as’ using SNOC_INDUCT >> rw [] >> Cases_on ‘args = []’ >- fs [] - >> ‘args = SNOC (LAST args) (FRONT args)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] + >> ‘args = SNOC (LAST args) (FRONT args)’ by PROVE_TAC [SNOC_LAST_FRONT] >> POP_ORW >> REWRITE_TAC [appstar_SNOC, SUB_THM] >> MATCH_MP_TAC lameq_TRANS @@ -1951,7 +1951,7 @@ Proof >- (MATCH_MP_TAC LAMl_ALPHA_ssub >> rw [DISJOINT_SYM] \\ Suff ‘FV M = set Z’ >- METIS_TAC [DISJOINT_SYM] \\ rw [Abbr ‘M’, FV_appstar] \\ - ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] \\ + ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_LAST_FRONT] \\ POP_ORW \\ simp [LIST_TO_SET_SNOC] \\ rw [Once EXTENSION, MEM_MAP] \\ @@ -1966,7 +1966,7 @@ Proof >> ‘FDOM fm = set Z’ by rw [FDOM_fromPairs, Abbr ‘fm’] >> qabbrev_tac ‘y = LAST Y’ >> ‘!t. LAMl Y t = LAMl (SNOC y (FRONT Y)) t’ - by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_OF_LAST_FRONT]) >> POP_ORW + by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_LAST_FRONT]) >> POP_ORW >> REWRITE_TAC [LAMl_SNOC] >> Know ‘fm ' M = VAR y @* MAP VAR (FRONT Y)’ >- (simp [Abbr ‘M’, ssub_appstar] \\ @@ -2066,7 +2066,7 @@ Proof Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ Know ‘SNOC y vs = Y’ >- (qunabbrevl_tac [‘y’, ‘vs’] \\ - MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) \\ + MATCH_MP_TAC SNOC_LAST_FRONT >> art []) \\ DISCH_THEN (ONCE_REWRITE_TAC o wrap o SYM) \\ rw [ALL_DISTINCT_SNOC] >- (qunabbrev_tac ‘vs1’ >> PROVE_TAC [MEM_TAKE]) \\ @@ -2094,7 +2094,7 @@ Proof >- (Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ Know ‘SNOC y vs = Y’ >- (qunabbrevl_tac [‘y’, ‘vs’] \\ - MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) \\ + MATCH_MP_TAC SNOC_LAST_FRONT >> art []) \\ DISCH_THEN (ONCE_REWRITE_TAC o wrap o SYM) \\ rw [ALL_DISTINCT_SNOC] \\ qunabbrev_tac ‘vs1’ >> PROVE_TAC [MEM_TAKE]) @@ -2139,7 +2139,7 @@ Proof Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ Know ‘SNOC y vs = Y’ >- (qunabbrevl_tac [‘y’, ‘vs’] \\ - MATCH_MP_TAC SNOC_OF_LAST_FRONT >> art []) \\ + MATCH_MP_TAC SNOC_LAST_FRONT >> art []) \\ DISCH_THEN (ONCE_REWRITE_TAC o wrap o SYM) \\ rw [ALL_DISTINCT_SNOC] \\ CCONTR_TAC >> fs [Abbr ‘vs2’] \\ diff --git a/examples/lambda/barendregt/solvableScript.sml b/examples/lambda/barendregt/solvableScript.sml index d5c5233be1..0c6d1800ad 100644 --- a/examples/lambda/barendregt/solvableScript.sml +++ b/examples/lambda/barendregt/solvableScript.sml @@ -1610,7 +1610,7 @@ Proof >- (MATCH_MP_TAC LAMl_ALPHA_ssub >> rw [DISJOINT_SYM] \\ Suff ‘FV M = set Z’ >- METIS_TAC [DISJOINT_SYM] \\ rw [Abbr ‘M’, FV_appstar] \\ - ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_OF_LAST_FRONT] \\ + ‘Z = SNOC (LAST Z) (FRONT Z)’ by PROVE_TAC [SNOC_LAST_FRONT] \\ POP_ORW \\ simp [LIST_TO_SET_SNOC] \\ rw [Once EXTENSION, MEM_MAP] \\ @@ -1626,7 +1626,7 @@ Proof >> qabbrev_tac ‘y = LAST Y’ (* postfix for LAMl_ALPHA_ssub *) >> ‘!t. LAMl Y t = LAMl (SNOC y (FRONT Y)) t’ - by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_OF_LAST_FRONT]) >> POP_ORW + by (ASM_SIMP_TAC std_ss [Abbr ‘y’, SNOC_LAST_FRONT]) >> POP_ORW >> REWRITE_TAC [LAMl_SNOC] >> Know ‘fm ' M = VAR y @* MAP VAR (FRONT Y)’ >- (simp [Abbr ‘M’, ssub_appstar] \\ @@ -1657,11 +1657,11 @@ Proof >> qabbrev_tac ‘vs = FRONT Y’ >> Know ‘ALL_DISTINCT vs /\ ~MEM y vs’ >- (Q.PAT_X_ASSUM ‘ALL_DISTINCT Y’ MP_TAC \\ - ‘Y = SNOC y vs’ by METIS_TAC [SNOC_OF_LAST_FRONT] >> POP_ORW \\ + ‘Y = SNOC y vs’ by METIS_TAC [SNOC_LAST_FRONT] >> POP_ORW \\ rw [ALL_DISTINCT_SNOC]) >> STRIP_TAC >> MATCH_MP_TAC principle_hnf_permutator_lemma - >> ‘SNOC y vs = Y’ by METIS_TAC [SNOC_OF_LAST_FRONT] >> POP_ORW + >> ‘SNOC y vs = Y’ by METIS_TAC [SNOC_LAST_FRONT] >> POP_ORW >> rw [EVERY_MEM, Abbr ‘vs’, LENGTH_FRONT] >- art [] >> FIRST_X_ASSUM MATCH_MP_TAC >> Q.EXISTS_TAC ‘e’ >> art [] diff --git a/src/algebra/monoidScript.sml b/src/algebra/monoidScript.sml index 2cfa5988b7..f1c0b31b44 100644 --- a/src/algebra/monoidScript.sml +++ b/src/algebra/monoidScript.sml @@ -1,4 +1,10 @@ (* ------------------------------------------------------------------------- *) +(* Monoid Theory *) +(* ========================================================================= *) +(* A monoid is a semi-group with an identity. *) +(* The units of a monoid form a group. *) +(* A finite, cancellative monoid is also a group. *) +(* ------------------------------------------------------------------------- *) (* Monoid *) (* Monoid Order and Invertibles *) (* Monoid Maps *) @@ -6,19 +12,9 @@ (* Applying Monoid Theory: Monoid Instances *) (* Theory about folding a monoid (or group) operation over a bag of elements *) (* ------------------------------------------------------------------------- *) -(* Author: (Joseph) Hing-Lun Chan (Australian National University, 2019) *) +(* (Joseph) Hing-Lun Chan, The Australian National University, 2014-2019 *) (* ------------------------------------------------------------------------- *) -(* - -Monoid Theory -============= -A monoid is a semi-group with an identity. -The units of a monoid form a group. -A finite, cancellative monoid is also a group. - -*) - (*===========================================================================*) (* add all dependent libraries for script *) diff --git a/src/list/src/listScript.sml b/src/list/src/listScript.sml index 71569a4c45..67dfb6299c 100644 --- a/src/list/src/listScript.sml +++ b/src/list/src/listScript.sml @@ -2738,12 +2738,8 @@ val SNOC_APPEND = store_thm("SNOC_APPEND", “!x (l:('a) list). SNOC x l = APPEND l [x]”, GEN_TAC THEN LIST_INDUCT_TAC THEN ASM_REWRITE_TAC [SNOC, APPEND]); -(* |- !l. l <> [] ==> SNOC (LAST l) (FRONT l) = l - - NOTE: was called "SNOC_LAST_FRONT", renamed due to name conflicts with - "examples/algebra" (cf. rich_listTheory.SNOC_LAST_FRONT) - *) -Theorem SNOC_OF_LAST_FRONT = +(* |- !l. l <> [] ==> SNOC (LAST l) (FRONT l) = l *) +Theorem SNOC_LAST_FRONT = REWRITE_RULE [GSYM SNOC_APPEND] APPEND_FRONT_LAST val LIST_TO_SET_SNOC = Q.store_thm("LIST_TO_SET_SNOC", diff --git a/src/list/src/rich_listScript.sml b/src/list/src/rich_listScript.sml index e3e04b93b7..2089344f0c 100644 --- a/src/list/src/rich_listScript.sml +++ b/src/list/src/rich_listScript.sml @@ -3233,7 +3233,7 @@ Theorem ALL_DISTINCT_FRONT : Proof rpt STRIP_TAC >> ‘ALL_DISTINCT l = ALL_DISTINCT (SNOC (LAST l) (FRONT l))’ - by rw [SNOC_OF_LAST_FRONT] + by rw [SNOC_LAST_FRONT] >> FULL_SIMP_TAC std_ss [ALL_DISTINCT_SNOC] QED @@ -3733,8 +3733,8 @@ QED = FRONT l ++ [LAST l] by APPEND_FRONT_LAST, l <> [] = SNOC (LAST l) (FRONT l) by SNOC_APPEND *) -val SNOC_LAST_FRONT = store_thm( - "SNOC_LAST_FRONT", +val SNOC_LAST_FRONT' = store_thm( + "SNOC_LAST_FRONT'", ``!l. l <> [] ==> (l = SNOC (LAST l) (FRONT l))``, rw[APPEND_FRONT_LAST]); From a4084dad40e8cc7e339ef97a149dcccd877b6ea1 Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Tue, 23 Apr 2024 11:21:43 +1000 Subject: [PATCH 6/8] FTBFS examples/lambda --- examples/lambda/barendregt/boehmScript.sml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lambda/barendregt/boehmScript.sml b/examples/lambda/barendregt/boehmScript.sml index 98b53277f2..160d44dbf4 100644 --- a/examples/lambda/barendregt/boehmScript.sml +++ b/examples/lambda/barendregt/boehmScript.sml @@ -3379,7 +3379,7 @@ Proof MATCH_MP_TAC (GSYM FRONT_APPEND_NOT_NIL) >> rw []) >> Rewr' \\ Suff ‘LAST (MAP VAR vs) = LAST (args2' ++ MAP VAR vs)’ >- (Rewr' >> qabbrev_tac ‘l = args2' ++ MAP VAR vs’ \\ - MATCH_MP_TAC SNOC_LAST_FRONT >> rw [Abbr ‘l’]) \\ + MATCH_MP_TAC SNOC_LAST_FRONT' >> rw [Abbr ‘l’]) \\ MATCH_MP_TAC (GSYM LAST_APPEND_NOT_NIL) >> rw []) >> Rewr' \\ REWRITE_TAC [appstar_SNOC] \\ qabbrev_tac ‘t :term = LAM z (VAR z)’ \\ From e6ce07231667c75940e69090d0a4c5aea1700a8c Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Fri, 3 May 2024 11:47:11 +1000 Subject: [PATCH 7/8] Move new files into subdirectories under src/algebra --- src/algebra/Holmakefile | 2 +- src/algebra/base/Holmakefile | 9 +++++++++ src/algebra/{ => base}/combinatoricsScript.sml | 0 src/algebra/{ => base}/numberScript.sml | 0 src/algebra/{ => base}/primeScript.sml | 0 src/algebra/construction/Holmakefile | 9 +++++++++ src/algebra/{ => construction}/jcLib.sig | 0 src/algebra/{ => construction}/jcLib.sml | 0 src/algebra/{ => construction}/monoidScript.sml | 0 9 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/algebra/base/Holmakefile rename src/algebra/{ => base}/combinatoricsScript.sml (100%) rename src/algebra/{ => base}/numberScript.sml (100%) rename src/algebra/{ => base}/primeScript.sml (100%) create mode 100644 src/algebra/construction/Holmakefile rename src/algebra/{ => construction}/jcLib.sig (100%) rename src/algebra/{ => construction}/jcLib.sml (100%) rename src/algebra/{ => construction}/monoidScript.sml (100%) diff --git a/src/algebra/Holmakefile b/src/algebra/Holmakefile index 21292da5dc..e7e4fcffda 100644 --- a/src/algebra/Holmakefile +++ b/src/algebra/Holmakefile @@ -1,4 +1,4 @@ -INCLUDES = ../bag +INCLUDES = ../bag ../integer ../pred_set/src/more_theories base construction ifdef HOLBUILD .PHONY: link-to-sigobj diff --git a/src/algebra/base/Holmakefile b/src/algebra/base/Holmakefile new file mode 100644 index 0000000000..aec38cf720 --- /dev/null +++ b/src/algebra/base/Holmakefile @@ -0,0 +1,9 @@ +INCLUDES = ../../bag ../../integer ../../pred_set/src/more_theories + +ifdef HOLBUILD +.PHONY: link-to-sigobj +all: link-to-sigobj + +link-to-sigobj: $(DEFAULT_TARGETS) + $(HOL_LNSIGOBJ) +endif diff --git a/src/algebra/combinatoricsScript.sml b/src/algebra/base/combinatoricsScript.sml similarity index 100% rename from src/algebra/combinatoricsScript.sml rename to src/algebra/base/combinatoricsScript.sml diff --git a/src/algebra/numberScript.sml b/src/algebra/base/numberScript.sml similarity index 100% rename from src/algebra/numberScript.sml rename to src/algebra/base/numberScript.sml diff --git a/src/algebra/primeScript.sml b/src/algebra/base/primeScript.sml similarity index 100% rename from src/algebra/primeScript.sml rename to src/algebra/base/primeScript.sml diff --git a/src/algebra/construction/Holmakefile b/src/algebra/construction/Holmakefile new file mode 100644 index 0000000000..53614d8b99 --- /dev/null +++ b/src/algebra/construction/Holmakefile @@ -0,0 +1,9 @@ +INCLUDES = ../../bag ../../integer ../../pred_set/src/more_theories ../base + +ifdef HOLBUILD +.PHONY: link-to-sigobj +all: link-to-sigobj + +link-to-sigobj: $(DEFAULT_TARGETS) + $(HOL_LNSIGOBJ) +endif diff --git a/src/algebra/jcLib.sig b/src/algebra/construction/jcLib.sig similarity index 100% rename from src/algebra/jcLib.sig rename to src/algebra/construction/jcLib.sig diff --git a/src/algebra/jcLib.sml b/src/algebra/construction/jcLib.sml similarity index 100% rename from src/algebra/jcLib.sml rename to src/algebra/construction/jcLib.sml diff --git a/src/algebra/monoidScript.sml b/src/algebra/construction/monoidScript.sml similarity index 100% rename from src/algebra/monoidScript.sml rename to src/algebra/construction/monoidScript.sml From 4b7d021796670f412b0423ef4465ef3453d92096 Mon Sep 17 00:00:00 2001 From: "Chun Tian (binghe)" Date: Fri, 3 May 2024 13:33:10 +1000 Subject: [PATCH 8/8] FTBFS (help) --- src/algebra/Holmakefile | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/algebra/Holmakefile b/src/algebra/Holmakefile index e7e4fcffda..d15c9fd006 100644 --- a/src/algebra/Holmakefile +++ b/src/algebra/Holmakefile @@ -1,9 +1 @@ -INCLUDES = ../bag ../integer ../pred_set/src/more_theories base construction - -ifdef HOLBUILD -.PHONY: link-to-sigobj -all: link-to-sigobj - -link-to-sigobj: $(DEFAULT_TARGETS) - $(HOL_LNSIGOBJ) -endif +INCLUDES = base construction