1-- Minimum and maximum of two natural numbers2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.Nat.MinMax8import OrderLemmas910defunique natMin (a b : Nat) (m ∈ ℕ) :=11 (a ≤ b ∧ m = a) ∨ (b < a ∧ m = b)12proof13havesplit:= Less.or_LessEqual at b, a14casessplitwith above | below15 . refine⟨b, nat.omega.forward at b with*, ?_⟩16right17exact⟨above, rfl⟩18 . refine⟨a, nat.omega.forward at a with*, ?_⟩19left20exact⟨below, rfl⟩2122uniqueness23intros m mMember first n nMember second24cases first with firstLow | firstHigh25 . cases second with secondLow | secondHigh26 . cc27 . exfalso28exact LessEqual.not_Less with firstLow.left, secondHigh.left29 . cases second with secondLow | secondHigh30 . exfalso31exact LessEqual.not_Less with secondLow.left, firstHigh.left32 . cc3334defunique natMax (a b : Nat) (m ∈ ℕ) :=35 (a ≤ b ∧ m = b) ∨ (b < a ∧ m = a)36proof37havesplit:= Less.or_LessEqual at b, a38casessplitwith above | below39 . refine⟨a, nat.omega.forward at a with*, ?_⟩40right41exact⟨above, rfl⟩42 . refine⟨b, nat.omega.forward at b with*, ?_⟩43left44exact⟨below, rfl⟩4546uniqueness47intros m mMember first n nMember second48cases first with firstLow | firstHigh49 . cases second with secondLow | secondHigh50 . cc51 . exfalso52exact LessEqual.not_Less with firstLow.left, secondHigh.left53 . cases second with secondLow | secondHigh54 . exfalso55exact LessEqual.not_Less with secondLow.left, firstHigh.left56 . cc5758theorem natMin.natural (a b : Nat)59showsNat(natMin(a, b))60proof61have selected := natMin.selected at a, b with*62exact nat.omega.backward with selected.left6364theorem natMax.natural (a b : Nat)65showsNat(natMax(a, b))66proof67have selected := natMax.selected at a, b with*68exact nat.omega.backward with selected.left6970theorem natMin.of_le (a b : Nat)71requires below : a ≤ b72shows natMin(a, b) = a73proof74have selected := natMin.selected at a, b with*75rcases selected with⟨member, property⟩76cases property with low | high77 . exact low.right78 . exfalso79exact LessEqual.not_Less with below, high.left8081theorem natMin.of_ge (a b : Nat)82requires above : b ≤ a83shows natMin(a, b) = b84proof85have selected := natMin.selected at a, b with*86rcases selected with⟨member, property⟩87cases property with low | high88 . have same := LessEqual.antisymmetric with low.left, above89rw low.right90exact same91 . exact high.right9293theorem natMax.of_le (a b : Nat)94requires below : a ≤ b95shows natMax(a, b) = b96proof97have selected := natMax.selected at a, b with*98rcases selected with⟨member, property⟩99cases property with low | high100 . exact low.right101 . exfalso102exact LessEqual.not_Less with below, high.left103104theorem natMax.of_ge (a b : Nat)105requires above : b ≤ a106shows natMax(a, b) = a107proof108have selected := natMax.selected at a, b with*109rcases selected with⟨member, property⟩110cases property with low | high111 . have same := LessEqual.antisymmetric with low.left, above112rw low.right113 symm114exact same115 . exact high.right116117theorem natMin.self (a : Nat)118shows natMin(a, a) = a119proof120exact natMin.of_le with (LessEqual.reflexive at a)121122theorem natMax.self (a : Nat)123shows natMax(a, a) = a124proof125exact natMax.of_le with (LessEqual.reflexive at a)126127theorem natMin.commutative (a b : Nat)128shows natMin(a, b) = natMin(b, a)129proof130have total := LessEqual.total at a, b131cases total with below | above132 . haveleft:= natMin.of_le with below133haveright:= natMin.of_ge at b, a with below134rw [left, right]135rfl136 . haveleft:= natMin.of_ge with above137haveright:= natMin.of_le at b, a with above138rw [left, right]139rfl140141theorem natMax.commutative (a b : Nat)142shows natMax(a, b) = natMax(b, a)143proof144have total := LessEqual.total at a, b145cases total with below | above146 . haveleft:= natMax.of_le with below147haveright:= natMax.of_ge at b, a with below148rw [left, right]149rfl150 . haveleft:= natMax.of_ge with above151haveright:= natMax.of_le at b, a with above152rw [left, right]153rfl154155theorem natMin.cases (a b : Nat)156shows natMin(a, b) = a ∨ natMin(a, b) = b157proof158have total := LessEqual.total at a, b159cases total with below | above160 . left161exact natMin.of_le with below162 . right163exact natMin.of_ge with above164165theorem natMax.cases (a b : Nat)166shows natMax(a, b) = a ∨ natMax(a, b) = b167proof168have total := LessEqual.total at a, b169cases total with below | above170 . right171exact natMax.of_le with below172 . left173exact natMax.of_ge with above174175theorem natMin.le_left (a b : Nat)176shows natMin(a, b) ≤ a177proof178have total := LessEqual.total at a, b179cases total with below | above180 . have value101 := natMin.of_le with below181rw value101182exact LessEqual.reflexive with*183 . have value102 := natMin.of_ge with above184rw value102185exact above186187theorem natMin.le_right (a b : Nat)188shows natMin(a, b) ≤ b189proof190have total := LessEqual.total at a, b191cases total with below | above192 . have value103 := natMin.of_le with below193rw value103194exact below195 . have value104 := natMin.of_ge with above196rw value104197exact LessEqual.reflexive with*198199theorem natMax.ge_left (a b : Nat)200shows a ≤ natMax(a, b)201proof202have total := LessEqual.total at a, b203cases total with below | above204 . have value105 := natMax.of_le with below205rw value105206exact below207 . have value106 := natMax.of_ge with above208rw value106209exact LessEqual.reflexive with*210211theorem natMax.ge_right (a b : Nat)212shows b ≤ natMax(a, b)213proof214have total := LessEqual.total at a, b215cases total with below | above216 . have value107 := natMax.of_le with below217rw value107218exact LessEqual.reflexive with*219 . have value108 := natMax.of_ge with above220rw value108221exact above222223theorem natMin.le_natMax (a b : Nat)224shows natMin(a, b) ≤ natMax(a, b)225proof226have low := natMin.le_left at a, b227have high := natMax.ge_left at a, b228exact LessEqual.transitive with low, high229230theorem natMin.le_iff (a b c : Nat)231shows c ≤ natMin(a, b) ↔ c ≤ a ∧ c ≤ b232proof233have minNat := natMin.natural at a, b234have low := natMin.le_left at a, b235have high := natMin.le_right at a, b236constructor237 . intro below238constructor239 . exact LessEqual.transitive with below, low240 . exact LessEqual.transitive with below, high241 . intro both242have total := LessEqual.total at a, b243cases total with first | second244 . have value109 := natMin.of_le with first245rw value109246exact both.left247 . have value110 := natMin.of_ge with second248rw value110249exact both.right250251theorem natMin.le_iff_or (a b c : Nat)252shows natMin(a, b) ≤ c ↔ a ≤ c ∨ b ≤ c253proof254have minNat := natMin.natural at a, b255constructor256 . intro below257havesplit:= natMin.cases at a, b258casessplitwith first | second259 . left260rw← first261exact below262 . right263rw← second264exact below265 . introsplit266casessplitwith first | second267 . have low := natMin.le_left at a, b268exact LessEqual.transitive with low, first269 . have high := natMin.le_right at a, b270exact LessEqual.transitive with high, second271272theorem natMax.le_iff (a b c : Nat)273shows natMax(a, b) ≤ c ↔ a ≤ c ∧ b ≤ c274proof275have maxNat := natMax.natural at a, b276have low := natMax.ge_left at a, b277have high := natMax.ge_right at a, b278constructor279 . intro below280constructor281 . exact LessEqual.transitive with low, below282 . exact LessEqual.transitive with high, below283 . intro both284have total := LessEqual.total at a, b285cases total with first | second286 . have value111 := natMax.of_le with first287rw value111288exact both.right289 . have value112 := natMax.of_ge with second290rw value112291exact both.left292293theorem natMax.ge_iff_or (a b c : Nat)294shows c ≤ natMax(a, b) ↔ c ≤ a ∨ c ≤ b295proof296have maxNat := natMax.natural at a, b297constructor298 . intro below299havesplit:= natMax.cases at a, b300casessplitwith first | second301 . left302rw← first303exact below304 . right305rw← second306exact below307 . introsplit308casessplitwith first | second309 . have low := natMax.ge_left at a, b310exact LessEqual.transitive with first, low311 . have high := natMax.ge_right at a, b312exact LessEqual.transitive with second, high313314theorem natMin.lt_iff (a b c : Nat)315shows c < natMin(a, b) ↔ c < a ∧ c < b316proof317have minNat := natMin.natural at a, b318have low := natMin.le_left at a, b319have high := natMin.le_right at a, b320constructor321 . intro below322constructor323 . exact Less.transitive_LessEqual with below, low324 . exact Less.transitive_LessEqual with below, high325 . intro both326have total := LessEqual.total at a, b327cases total with first | second328 . have value113 := natMin.of_le with first329rw value113330exact both.left331 . have value114 := natMin.of_ge with second332rw value114333exact both.right334335theorem natMax.lt_iff (a b c : Nat)336shows natMax(a, b) < c ↔ a < c ∧ b < c337proof338have maxNat := natMax.natural at a, b339have low := natMax.ge_left at a, b340have high := natMax.ge_right at a, b341constructor342 . intro below343constructor344 . exact LessEqual.transitive_Less with low, below345 . exact LessEqual.transitive_Less with high, below346 . intro both347have total := LessEqual.total at a, b348cases total with first | second349 . have value115 := natMax.of_le with first350rw value115351exact both.right352 . have value116 := natMax.of_ge with second353rw value116354exact both.left355356theorem natMin.eq_left_iff (a b : Nat)357shows natMin(a, b) = a ↔ a ≤ b358proof359constructor360 . intro same361have high := natMin.le_right at a, b362rw same at high363exact high364 . intro below365exact natMin.of_le with below366367theorem natMin.eq_right_iff (a b : Nat)368shows natMin(a, b) = b ↔ b ≤ a369proof370constructor371 . intro same372have low := natMin.le_left at a, b373rw same at low374exact low375 . intro above376exact natMin.of_ge with above377378theorem natMax.eq_left_iff (a b : Nat)379shows natMax(a, b) = a ↔ b ≤ a380proof381constructor382 . intro same383have high := natMax.ge_right at a, b384rw same at high385exact high386 . intro above387exact natMax.of_ge with above388389theorem natMax.eq_right_iff (a b : Nat)390shows natMax(a, b) = b ↔ a ≤ b391proof392constructor393 . intro same394have low := natMax.ge_left at a, b395rw same at low396exact low397 . intro below398exact natMax.of_le with below399400theorem natMin.zero_left (a : Nat)401shows natMin(0, a) =0402proof403have zero : 0≤ a bylinarith404exact natMin.of_le with zero405406theorem natMin.zero_right (a : Nat)407shows natMin(a, 0) =0408proof409have zero : 0≤ a bylinarith410exact natMin.of_ge with zero411412theorem natMax.zero_left (a : Nat)413shows natMax(0, a) = a414proof415have zero : 0≤ a bylinarith416exact natMax.of_le with zero417418theorem natMax.zero_right (a : Nat)419shows natMax(a, 0) = a420proof421have zero : 0≤ a bylinarith422exact natMax.of_ge with zero423424theorem natMin.eq_zero_iff (a b : Nat)425shows natMin(a, b) =0↔ a =0∨ b =0426proof427constructor428 . intro zero429havesplit:= natMin.cases at a, b430casessplitwith first | second431 . left432rw← first433exact zero434 . right435rw← second436exact zero437 . introsplit438casessplitwith first | second439 . rw first440exact natMin.zero_left with*441 . rw second442exact natMin.zero_right with*443444theorem natMax.eq_zero_iff (a b : Nat)445shows natMax(a, b) =0↔ a =0∧ b =0446proof447constructor448 . intro zero449have low := natMax.ge_left at a, b450have high := natMax.ge_right at a, b451rw zero at low452rw zero at high453constructor454 . exact LessEqual.eq_zero with low455 . exact LessEqual.eq_zero with high456 . intro both457rw [both.left, both.right]458exact natMax.self with*459460theorem natMin.monotone (a b c d : Nat)461requires first : a ≤ c462requires second : b ≤ d463shows natMin(a, b) ≤ natMin(c, d)464proof465have low := natMin.le_left at a, b466have high := natMin.le_right at a, b467apply natMin.le_iff.backward468constructor469 . exact LessEqual.transitive with low, first470 . exact LessEqual.transitive with high, second471472theorem natMax.monotone (a b c d : Nat)473requires first : a ≤ c474requires second : b ≤ d475shows natMax(a, b) ≤ natMax(c, d)476proof477have low := natMax.ge_left at c, d478have high := natMax.ge_right at c, d479apply natMax.le_iff.backward480constructor481 . exact LessEqual.transitive with first, low482 . exact LessEqual.transitive with second, high483484theorem natMin.associative (a b c : Nat)485shows natMin(natMin(a, b), c) = natMin(a, natMin(b, c))486proof487have innerLeft := natMin.natural at a, b488have innerRight := natMin.natural at b, c489have total := LessEqual.total at a, b490cases total with below | above491 . have value117 := natMin.of_le with below492rw value117493have totalAC := LessEqual.total at a, c494cases totalAC with belowAC | aboveAC495 . have bound : a ≤ natMin(b, c)496exact natMin.le_iff.backward with⟨below, belowAC⟩497have value1 := natMin.of_le with belowAC498have value2 := natMin.of_le with bound499rw [value1, value2]500rfl501 . have bound : natMin(b, c) ≤ a502have inner := natMin.le_right at b, c503exact LessEqual.transitive with inner, aboveAC504have value3 := natMin.of_ge with aboveAC505have value4 := natMin.of_ge with bound506rw [value3, value4]507have totalBC := LessEqual.total at b, c508cases totalBC with belowBC | aboveBC509 . have value118 := natMin.of_le with belowBC510rw value118511have bound2 : c ≤ b := LessEqual.transitive with aboveAC, below512exact LessEqual.antisymmetric with bound2, belowBC513 . have value119 := natMin.of_ge with aboveBC514rw value119515rfl516 . have value120 := natMin.of_ge with above517rw value120518have totalBC := LessEqual.total at b, c519cases totalBC with belowBC | aboveBC520 . have value121 := natMin.of_le with belowBC521rw value121522have value5 := natMin.of_ge with above523rw value5524rfl525 . have value122 := natMin.of_ge with aboveBC526rw value122527have bound : c ≤ a := LessEqual.transitive with aboveBC, above528have value6 := natMin.of_ge with bound529rw value6530rfl531532theorem natMax.associative (a b c : Nat)533shows natMax(natMax(a, b), c) = natMax(a, natMax(b, c))534proof535have innerLeft := natMax.natural at a, b536have innerRight := natMax.natural at b, c537have total := LessEqual.total at a, b538cases total with below | above539 . have value123 := natMax.of_le with below540rw value123541have totalBC := LessEqual.total at b, c542cases totalBC with belowBC | aboveBC543 . have value124 := natMax.of_le with belowBC544rw value124545have bound : a ≤ c := LessEqual.transitive with below, belowBC546have value7 := natMax.of_le with bound547rw value7548rfl549 . have value125 := natMax.of_ge with aboveBC550rw value125551have value8 := natMax.of_le with below552rw value8553rfl554 . have value126 := natMax.of_ge with above555rw value126556have totalAC := LessEqual.total at a, c557cases totalAC with belowAC | aboveAC558 . have bound : a ≤ natMax(b, c)559have inner := natMax.ge_right at b, c560exact LessEqual.transitive with belowAC, inner561have value9 := natMax.of_le with belowAC562have value10 := natMax.of_le with bound563rw [value9, value10]564have totalBC := LessEqual.total at b, c565cases totalBC with belowBC | aboveBC566 . have value127 := natMax.of_le with belowBC567rw value127568rfl569 . have value128 := natMax.of_ge with aboveBC570rw value128571have bound2 : b ≤ c := LessEqual.transitive with above, belowAC572exact LessEqual.antisymmetric with aboveBC, bound2573 . have bound : natMax(b, c) ≤ a574exact natMax.le_iff.backward with⟨above, aboveAC⟩575have value11 := natMax.of_ge with aboveAC576have value12 := natMax.of_ge with bound577rw [value11, value12]578rfl579580theorem natMin.add_left (a b k : Nat)581shows natMin(k + a, k + b) = k + natMin(a, b)582proof583have total := LessEqual.total at a, b584cases total with below | above585 . have shifted : k + a ≤ k + b bylinarith586have value13 := natMin.of_le with below587have value14 := natMin.of_le with shifted588rw [value13, value14]589rfl590 . have shifted : k + b ≤ k + a bylinarith591have value15 := natMin.of_ge with above592have value16 := natMin.of_ge with shifted593rw [value15, value16]594rfl595596theorem natMin.add_right (a b k : Nat)597shows natMin(a + k, b + k) = natMin(a, b) + k598proof599have total := LessEqual.total at a, b600cases total with below | above601 . have shifted : a + k ≤ b + k bylinarith602have value17 := natMin.of_le with below603have value18 := natMin.of_le with shifted604rw [value17, value18]605rfl606 . have shifted : b + k ≤ a + k bylinarith607have value19 := natMin.of_ge with above608have value20 := natMin.of_ge with shifted609rw [value19, value20]610rfl611612theorem natMax.add_left (a b k : Nat)613shows natMax(k + a, k + b) = k + natMax(a, b)614proof615have total := LessEqual.total at a, b616cases total with below | above617 . have shifted : k + a ≤ k + b bylinarith618have value21 := natMax.of_le with below619have value22 := natMax.of_le with shifted620rw [value21, value22]621rfl622 . have shifted : k + b ≤ k + a bylinarith623have value23 := natMax.of_ge with above624have value24 := natMax.of_ge with shifted625rw [value23, value24]626rfl627628theorem natMax.add_right (a b k : Nat)629shows natMax(a + k, b + k) = natMax(a, b) + k630proof631have total := LessEqual.total at a, b632cases total with below | above633 . have shifted : a + k ≤ b + k bylinarith634have value25 := natMax.of_le with below635have value26 := natMax.of_le with shifted636rw [value25, value26]637rfl638 . have shifted : b + k ≤ a + k bylinarith639have value27 := natMax.of_ge with above640have value28 := natMax.of_ge with shifted641rw [value27, value28]642rfl643644theorem natMin.add_one (a b : Nat)645shows natMin(a +1, b +1) = natMin(a, b) +1646proof647exact natMin.add_right at a, b, 1648649theorem natMax.add_one (a b : Nat)650shows natMax(a +1, b +1) = natMax(a, b) +1651proof652exact natMax.add_right at a, b, 1653654theorem natMin.mul_left (a b k : Nat)655shows natMin(k * a, k * b) = k * natMin(a, b)656proof657have total := LessEqual.total at a, b658cases total with below | above659 . have scaled := LessEqual.mul_left at a, b, k with below660have value29 := natMin.of_le with below661have value30 := natMin.of_le with scaled662rw [value29, value30]663rfl664 . have scaled := LessEqual.mul_left at b, a, k with above665have value31 := natMin.of_ge with above666have value32 := natMin.of_ge with scaled667rw [value31, value32]668rfl669670theorem natMin.mul_right (a b k : Nat)671shows natMin(a * k, b * k) = natMin(a, b) * k672proof673have total := LessEqual.total at a, b674cases total with below | above675 . have scaled := LessEqual.mul_right at a, b, k with below676have value33 := natMin.of_le with below677have value34 := natMin.of_le with scaled678rw [value33, value34]679rfl680 . have scaled := LessEqual.mul_right at b, a, k with above681have value35 := natMin.of_ge with above682have value36 := natMin.of_ge with scaled683rw [value35, value36]684rfl685686theorem natMax.mul_left (a b k : Nat)687shows natMax(k * a, k * b) = k * natMax(a, b)688proof689have total := LessEqual.total at a, b690cases total with below | above691 . have scaled := LessEqual.mul_left at a, b, k with below692have value37 := natMax.of_le with below693have value38 := natMax.of_le with scaled694rw [value37, value38]695rfl696 . have scaled := LessEqual.mul_left at b, a, k with above697have value39 := natMax.of_ge with above698have value40 := natMax.of_ge with scaled699rw [value39, value40]700rfl701702theorem natMax.mul_right (a b k : Nat)703shows natMax(a * k, b * k) = natMax(a, b) * k704proof705have total := LessEqual.total at a, b706cases total with below | above707 . have scaled := LessEqual.mul_right at a, b, k with below708have value41 := natMax.of_le with below709have value42 := natMax.of_le with scaled710rw [value41, value42]711rfl712 . have scaled := LessEqual.mul_right at b, a, k with above713have value43 := natMax.of_ge with above714have value44 := natMax.of_ge with scaled715rw [value43, value44]716rfl717718theorem natMin.add_natMax (a b : Nat)719shows natMin(a, b) + natMax(a, b) = a + b720proof721have total := LessEqual.total at a, b722cases total with below | above723 . have value129 := natMin.of_le with below724have value130 := natMax.of_le with below725rw [value129, value130]726rfl727 . have value131 := natMin.of_ge with above728have value132 := natMax.of_ge with above729rw [value131, value132]730ring731732theorem natMax.distribute_natMin (a b c : Nat)733shows natMax(a, natMin(b, c)) = natMin(natMax(a, b), natMax(a, c))734proof735have leftNat := natMax.natural at a, b736have rightNat := natMax.natural at a, c737have reflexive := LessEqual.reflexive at a738have total := LessEqual.total at b, c739cases total with below | above740 . have outer := natMax.monotone at a, b, a, c with reflexive, below741have value45 := natMin.of_le with below742have value46 := natMin.of_le with outer743rw [value45, value46]744rfl745 . have outer := natMax.monotone at a, c, a, b with reflexive, above746have value47 := natMin.of_ge with above747have value48 := natMin.of_ge with outer748rw [value47, value48]749rfl750751theorem natMin.distribute_natMax (a b c : Nat)752shows natMin(a, natMax(b, c)) = natMax(natMin(a, b), natMin(a, c))753proof754have leftNat := natMin.natural at a, b755have rightNat := natMin.natural at a, c756have reflexive := LessEqual.reflexive at a757have total := LessEqual.total at b, c758cases total with below | above759 . have outer := natMin.monotone at a, b, a, c with reflexive, below760have value49 := natMax.of_le with below761have value50 := natMax.of_le with outer762rw [value49, value50]763rfl764 . have outer := natMin.monotone at a, c, a, b with reflexive, above765have value51 := natMax.of_ge with above766have value52 := natMax.of_ge with outer767rw [value51, value52]768rfl769770theorem natMin.absorb_natMax (a b : Nat)771shows natMin(a, natMax(a, b)) = a772proof773have high := natMax.ge_left at a, b774exact natMin.of_le with high775776theorem natMax.absorb_natMin (a b : Nat)777shows natMax(a, natMin(a, b)) = a778proof779have low := natMin.le_left at a, b780exact natMax.of_ge with low781