1-- The intermediate value theorem for continuous functions on a closed interval2-- 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.Real.Analysis.IntermediateValue8import Intervals910-- The points of the interval where f stays below the target value.11def belowPoints (f : Set) (A B V : Real) : Set:=12 { X ∈ closedInterval(A, B) | (f(X):R) < V }1314-- A continuous function on [A, B] that starts below V and ends above it takes15-- the value V. The point is the supremum C of the set where f < V: if f(C)16-- were below V, continuity would put a point to the right of C into that set;17-- if f(C) were above V, every point of the set would sit below C minus some18-- positive margin, so C would not be the least upper bound.19theorem closedInterval.intermediate_value (f : Set) (A B V : Real)20requiresordered : A ≤ B21requires continuous : ContinuousOn(f, closedInterval(A, B))22requires below : (f(A):R) < V23requires above : V < (f(B):R)24shows∃ C ∈ closedInterval(A, B), f(C) = V25proof26rcases continuous with⟨function, subset, pointwise⟩27have leftMember := closedInterval.left_mem with*28have rightMember := closedInterval.right_mem with*29have aData : A ∈ closedInterval(A, B) ∧ (f(A):R) < V30constructor31 . exact leftMember32 . exact below33have aIn : A ∈ belowPoints(f, A, B, V)34exact belowPoints.membership.backward with aData35have inhabited : Nonempty(belowPoints(f, A, B, V)) :=⟨A, aIn⟩36have pointsReal : belowPoints(f, A, B, V) ⊆ ℝ37intros X member38obtain⟨inInterval, negative⟩:= belowPoints.membership.forward with member39obtain⟨realX, lowerX, upperX⟩:= closedInterval.membership.forward with inInterval40exact realX41have upper : RealUpperBound(belowPoints(f, A, B, V), B)42unfold RealUpperBound43have typedB : Real(B) byassumption44refine⟨typedB, pointsReal, _⟩45intros X member46obtain⟨inInterval, negative⟩:= belowPoints.membership.forward with member47obtain⟨realX, lowerX, upperX⟩:= closedInterval.membership.forward with inInterval48exact realLe.subset with*49have bounded : ∃ D, RealUpperBound(belowPoints(f, A, B, V), D) :=⟨B, upper⟩50obtain⟨C, lubC⟩:=Real.least_upper_bound_property at belowPoints(f, A, B, V) with*51rcases lubC with⟨⟨typedC, pointsSubset, boundsC⟩, leastC⟩52have realC : C ∈ ℝ byexact typedC53have aSubset := boundsC with A, aIn54have aLe := realLe.of_subset at A, C with*55have cSubset := leastC with B, upper56have cLe := realLe.of_subset at C, B with*57have cData : C ∈ ℝ ∧ A ≤ C ∧ C ≤ B byrefine⟨realC, aLe, cLe⟩58have cIn : C ∈ closedInterval(A, B)59exact closedInterval.membership.backward with cData60have within := pointwise with C, cIn61witness C62constructor63 . exact cIn64 . havesplit : (f(C):R) < V ∨¬((f(C):R) < V)65tauto66casessplitwith lowC | notLow67 . exfalso68have gap : 0 < V - (f(C):R) bylinarith69obtain⟨D, realD, positiveD, maps⟩:=70 ContinuousWithinAt.maps_ball with*71have halfPositive := half.positive at D with*72have halfLess := half.lt_self at D with*73have reach : C + half(D) ≤ B ∨¬(C + half(D) ≤ B) bytauto74cases reach with inside | outside75 . have xReal := realAddValue.real at C, half(D) with*76have xMember : C + half(D) ∈ ℝ byexact xReal77have xData : C + half(D) ∈ ℝ ∧ A ≤ C + half(D) ∧ C + half(D) ≤ B78refine⟨xMember, _, inside⟩79linarith80have xIn : C + half(D) ∈ closedInterval(A, B)81exact closedInterval.membership.backward with xData82have nearData : C - D < C + half(D) ∧ C + half(D) < C + D83constructor84 . linarith85 . linarith86have near : C + half(D) ∈ openBall(C, D)87exact openBall.order_characterization.backward with*88have mapped := maps with C + half(D), xIn, near89obtain⟨lowerBound, upperBound⟩:= openBall.order_characterization.forward with mapped90have xLow : (f(C + half(D)):R) < V bylinarith91have xPointData : C + half(D) ∈ closedInterval(A, B) ∧ (f(C + half(D)):R) < V92constructor93 . exact xIn94 . exact xLow95have xInPoints : C + half(D) ∈ belowPoints(f, A, B, V)96exact belowPoints.membership.backward with xPointData97have xSubset := boundsC with C + half(D), xInPoints98have xLe := realLe.of_subset at C + half(D), C with*99linarith100 . have bLess := realLess.of_not_le with*101have nearData : C - D < B ∧ B < C + D102constructor103 . linarith104 . linarith105have near : B ∈ openBall(C, D)106exact openBall.order_characterization.backward with*107have mapped := maps with B, rightMember, near108obtain⟨lowerBound, upperBound⟩:= openBall.order_characterization.forward with mapped109linarith110 . have notBelow := realLe.of_not_less with*111have split2 : V < (f(C):R) ∨¬(V < (f(C):R)) bytauto112cases split2 with highC | notHigh113 . exfalso114have gap : 0 < (f(C):R) - V bylinarith115obtain⟨D, realD, positiveD, maps⟩:=116 ContinuousWithinAt.maps_ball with*117have halfPositive := half.positive at D with*118have halfLess := half.lt_self at D with*119have marginReal := realSubValue.real at C, half(D) with*120have smallerUpper : RealUpperBound(belowPoints(f, A, B, V), C - half(D))121unfold RealUpperBound122refine⟨marginReal, pointsReal, _⟩123intros X member124obtain⟨xIn, xLow⟩:= belowPoints.membership.forward with member125obtain⟨xMember, lowerX, upperX⟩:= closedInterval.membership.forward with xIn126have xSubsetC := boundsC with X, member127have xLeC := realLe.of_subset at X, C with*128have xLe : X ≤ C - half(D)129by_contra notLe130have far := realLess.of_not_le with*131have nearData : C - D < X ∧ X < C + D132constructor133 . linarith134 . linarith135have near : X ∈ openBall(C, D)136exact openBall.order_characterization.backward with*137have mapped := maps with X, xIn, near138obtain⟨lowerBound, upperBound⟩:= openBall.order_characterization.forward with mapped139linarith140exact realLe.subset with*141have cSubsetSmaller := leastC with C - half(D), smallerUpper142have cLeSmaller := realLe.of_subset at C, C - half(D) with*143linarith144 . have notAbove := realLe.of_not_less at f(C), V with*145exact realLe.antisymmetric with*146147-- The usual statement with weak inequalities at the endpoints.148theorem closedInterval.intermediate_value_le (f : Set) (A B V : Real)149requiresordered : A ≤ B150requires continuous : ContinuousOn(f, closedInterval(A, B))151requires below : (f(A):R) ≤ V152requires above : V ≤ (f(B):R)153shows∃ C ∈ closedInterval(A, B), f(C) = V154proof155have leftMember := closedInterval.left_mem at A, B with*156have rightMember := closedInterval.right_mem at A, B with*157have function : f : ℝ → ℝ158rcases continuous with⟨function, subset, pointwise⟩159exact function160have leftCase : (f(A):R) = V ∨¬((f(A):R) = V) bytauto161cases leftCase with leftEqual | leftDifferent162 . refine⟨A, leftMember, leftEqual⟩163 . have rightCase : (f(B):R) = V ∨¬((f(B):R) = V)164tauto165cases rightCase with rightEqual | rightDifferent166 . refine⟨B, rightMember, rightEqual⟩167 . have strictBelow : (f(A):R) < V168have notLe : ¬(V ≤ (f(A):R))169intro le170have equal := realLe.antisymmetric at f(A), V with*171exact leftDifferent with equal172exact realLess.of_not_le with*173have strictAbove : V < (f(B):R)174have notLe : ¬((f(B):R) ≤ V)175intro le176have equal := realLe.antisymmetric at f(B), V with*177exact rightDifferent with equal178exact realLess.of_not_le with*179exact closedInterval.intermediate_value with*180181-- A continuous function that changes sign on [A, B] has a zero there.182theorem closedInterval.zero_of_sign_change (f : Set) (A B : Real)183requiresordered : A ≤ B184requires continuous : ContinuousOn(f, closedInterval(A, B))185requires negative : (f(A):R) < 0186requires positive : 0 < (f(B):R)187shows∃ C ∈ closedInterval(A, B), f(C) = (0:R)188proof189exact closedInterval.intermediate_value with*190