1-- Real analysis: L'Hôpital's rule2-- 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.LHopital8import CauchyMeanValue910-- Section: Preparations11theorem openBall.right_between (A C X E : Real)12requires member : X ∈ openBall(A, E)13requiresleft : A < C14requiresright : C < X15shows C ∈ openBall(A, E)16proof17obtain⟨lower, upper⟩:= openBall.order_characterization.forward with member18have data : A - E < C ∧ C < A + E19constructor20 . linarith21 . linarith22exact openBall.order_characterization.backward with data2324theorem openBall.left_between (A C X E : Real)25requires member : X ∈ openBall(A, E)26requiresleft : X < C27requiresright : C < A28shows C ∈ openBall(A, E)29proof30obtain⟨lower, upper⟩:= openBall.order_characterization.forward with member31have data : A - E < C ∧ C < A + E32constructor33 . linarith34 . linarith35exact openBall.order_characterization.backward with data3637theorem closedInterval.subset_ball_right (A X W : Real)38requires member : X ∈ openBall(A, W)39requires positive : 0 < W40shows closedInterval(A, X) ⊆ openBall(A, W)41proof42intros Y memberY43obtain⟨realY, lower, upper⟩:= closedInterval.membership.forward with memberY44have typedY : Real(Y) byexact realY45obtain⟨lowerX, upperX⟩:= openBall.order_characterization.forward with member46have data : A - W < Y ∧ Y < A + W47constructor48 . linarith49 . linarith50exact openBall.order_characterization.backward with data5152theorem closedInterval.subset_ball_left (X A W : Real)53requires member : X ∈ openBall(A, W)54requires positive : 0 < W55shows closedInterval(X, A) ⊆ openBall(A, W)56proof57intros Y memberY58obtain⟨realY, lower, upper⟩:= closedInterval.membership.forward with memberY59have typedY : Real(Y) byexact realY60obtain⟨lowerX, upperX⟩:= openBall.order_characterization.forward with member61have data : A - W < Y ∧ Y < A + W62constructor63 . linarith64 . linarith65exact openBall.order_characterization.backward with data6667-- Section: OneSidedQuotients68-- To the right of A, the quotient f/g at X is the quotient of the69-- derivatives at some point strictly between A and X.70theorem LHopital.right (f g : ℝ → ℝ) (A W X : Real)71requires radius : 0 < W72requires differentiableF : ∀ Y ∈ openBall(A, W), DifferentiableAt(f, Y)73requires differentiableG : ∀ Y ∈ openBall(A, W), DifferentiableAt(g, Y)74requires nonzero : ∀ Y ∈ openBall(A, W), Y ≠ A → g′(Y) ≠ (0:R)75requires zeroF : f(A) =076requires zeroG : g(A) =077requires member : X ∈ openBall(A, W)78requiresright : A < X79shows∃ C ∈ ℝ, A < C ∧ C < X ∧80 pointwiseDiv(f, g)(X) = pointwiseDiv(f′, g′)(C)81proof82have realA : A ∈ ℝ bysimp [Real]83have realX : X ∈ ℝ bysimp [Real]84have fFunction : f : ℝ → ℝ byassumption85have gFunction : g : ℝ → ℝ byassumption86have subset := closedInterval.subset_ball_right at A, X, W with member, radius87have intervalReal := closedInterval.subset_reals at A, X with*88have onIntervalF : ∀ Y ∈ closedInterval(A, X), DifferentiableAt(f, Y)89intros Y memberY90have inBall := subset with Y, memberY91exact differentiableF with Y, inBall92have onIntervalG : ∀ Y ∈ closedInterval(A, X), DifferentiableAt(g, Y)93intros Y memberY94have inBall := subset with Y, memberY95exact differentiableG with Y, inBall96have continuousF := ContinuousOn.of_differentiable with97 fFunction, intervalReal, onIntervalF98have continuousG := ContinuousOn.of_differentiable with99 gFunction, intervalReal, onIntervalG100have interiorF : ∀ C ∈ ℝ, A < C → C < X → DifferentiableAt(f, C)101intros C realC left rightC102have typedC : Real(C) byexact realC103have inBall := openBall.right_between at A, C, X, W with104 member, left, rightC105exact differentiableF with C, inBall106have interiorG : ∀ C ∈ ℝ, A < C → C < X → DifferentiableAt(g, C)107intros C realC left rightC108have typedC : Real(C) byexact realC109have inBall := openBall.right_between at A, C, X, W with110 member, left, rightC111exact differentiableG with C, inBall112have typedFX : Real(f(X))113exact FunctionBetween.apply_closed with fFunction, realX114have typedGX : Real(g(X))115exact FunctionBetween.apply_closed with gFunction, realX116have nonzeroGX : g(X) ≠ (0:R)117intro vanishes118have endpoints : g(A) = g(X)119rw [zeroG, vanishes]120rfl121have unfoldedG : ∀ C ∈ ℝ, A < C → C < X →∃ D ∈ ℝ, HasDerivativeAt(g, D, C)122intros C realC left rightC123have differentiableAt := interiorG with C, realC, left, rightC124unfold DifferentiableAt at differentiableAt125exact differentiableAt126obtain⟨C, realC, left, rightC, zeroDerivative⟩:= Rolle.theorem with127right, continuousG, endpoints, unfoldedG128have typedC : Real(C) byexact realC129have value := derivative.of_has_derivative with zeroDerivative130have inBall := openBall.right_between at A, C, X, W with131 member, left, rightC132have distinct : C ≠ A133intro equal134rw equal atleft135linarith136have nonzeroC := nonzero with C, inBall, distinct137exact nonzeroC with value138obtain⟨C, realC, left, rightC, equation⟩:= CauchyMeanValue.theorem with139 fFunction, gFunction, right, continuousF, continuousG, interiorF, interiorG140have typedC : Real(C) byexact realC141have inBall := openBall.right_between at A, C, X, W with member, left, rightC142have distinct : C ≠ A143intro equal144rw equal atleft145linarith146have nonzeroC := nonzero with C, inBall, distinct147have typedDF := derivative.real at f, C148have typedDG := derivative.real at g, C149have cross : derivative(f)(C) * g(X) = derivative(g)(C) * f(X)150rw [zeroF, zeroG] at equation151linear_combination equation152have quotientValue := pointwiseDiv.value at f, g, X with153 fFunction, gFunction, typedFX, typedGX154have derivativeFFunction := derivative.function at f155have derivativeGFunction := derivative.function at g156have derivativeQuotient := pointwiseDiv.value at157 derivative(f), derivative(g), C with158 derivativeFFunction, derivativeGFunction, typedDF, typedDG159have equal := realDiv.eq_of_cross at160 f(X), g(X), derivative(f)(C), derivative(g)(C) with161 typedFX, typedGX, typedDF, typedDG, nonzeroGX, nonzeroC, cross162witness C163refine⟨realC, left, rightC, _⟩164rw [quotientValue, derivativeQuotient]165exact equal166167theorem LHopital.left (f g : ℝ → ℝ) (A W X : Real)168requires radius : 0 < W169requires differentiableF : ∀ Y ∈ openBall(A, W), DifferentiableAt(f, Y)170requires differentiableG : ∀ Y ∈ openBall(A, W), DifferentiableAt(g, Y)171requires nonzero : ∀ Y ∈ openBall(A, W), Y ≠ A → g′(Y) ≠ (0:R)172requires zeroF : f(A) =0173requires zeroG : g(A) =0174requires member : X ∈ openBall(A, W)175requiresleft : X < A176shows∃ C ∈ ℝ, X < C ∧ C < A ∧177 pointwiseDiv(f, g)(X) = pointwiseDiv(f′, g′)(C)178proof179have realA : A ∈ ℝ bysimp [Real]180have realX : X ∈ ℝ bysimp [Real]181have fFunction : f : ℝ → ℝ byassumption182have gFunction : g : ℝ → ℝ byassumption183have subset := closedInterval.subset_ball_left at X, A, W with member, radius184have intervalReal := closedInterval.subset_reals at X, A with*185have onIntervalF : ∀ Y ∈ closedInterval(X, A), DifferentiableAt(f, Y)186intros Y memberY187have inBall := subset with Y, memberY188exact differentiableF with Y, inBall189have onIntervalG : ∀ Y ∈ closedInterval(X, A), DifferentiableAt(g, Y)190intros Y memberY191have inBall := subset with Y, memberY192exact differentiableG with Y, inBall193have continuousF := ContinuousOn.of_differentiable with194 fFunction, intervalReal, onIntervalF195have continuousG := ContinuousOn.of_differentiable with196 gFunction, intervalReal, onIntervalG197have interiorF : ∀ C ∈ ℝ, X < C → C < A → DifferentiableAt(f, C)198intros C realC leftC rightC199have typedC : Real(C) byexact realC200have inBall := openBall.left_between at A, C, X, W with201 member, leftC, rightC202exact differentiableF with C, inBall203have interiorG : ∀ C ∈ ℝ, X < C → C < A → DifferentiableAt(g, C)204intros C realC leftC rightC205have typedC : Real(C) byexact realC206have inBall := openBall.left_between at A, C, X, W with207 member, leftC, rightC208exact differentiableG with C, inBall209have typedFX : Real(f(X))210exact FunctionBetween.apply_closed with fFunction, realX211have typedGX : Real(g(X))212exact FunctionBetween.apply_closed with gFunction, realX213have nonzeroGX : g(X) ≠ (0:R)214intro vanishes215have endpoints : g(X) = g(A)216rw [zeroG, vanishes]217rfl218have unfoldedG : ∀ C ∈ ℝ, X < C → C < A →∃ D ∈ ℝ, HasDerivativeAt(g, D, C)219intros C realC leftC rightC220have differentiableAt := interiorG with C, realC, leftC, rightC221unfold DifferentiableAt at differentiableAt222exact differentiableAt223obtain⟨C, realC, leftC, rightC, zeroDerivative⟩:= Rolle.theorem with224left, continuousG, endpoints, unfoldedG225have typedC : Real(C) byexact realC226have value := derivative.of_has_derivative with zeroDerivative227have inBall := openBall.left_between at A, C, X, W with228 member, leftC, rightC229have distinct : C ≠ A230intro equal231rw equal at rightC232linarith233have nonzeroC := nonzero with C, inBall, distinct234exact nonzeroC with value235obtain⟨C, realC, leftC, rightC, equation⟩:= CauchyMeanValue.theorem with236 fFunction, gFunction, left, continuousF, continuousG, interiorF, interiorG237have typedC : Real(C) byexact realC238have inBall := openBall.left_between at A, C, X, W with member, leftC, rightC239have distinct : C ≠ A240intro equal241rw equal at rightC242linarith243have nonzeroC := nonzero with C, inBall, distinct244have typedDF := derivative.real at f, C245have typedDG := derivative.real at g, C246have cross : derivative(f)(C) * g(X) = derivative(g)(C) * f(X)247rw [zeroF, zeroG] at equation248 symm249linear_combination equation250have quotientValue := pointwiseDiv.value at f, g, X with251 fFunction, gFunction, typedFX, typedGX252have derivativeFFunction := derivative.function at f253have derivativeGFunction := derivative.function at g254have derivativeQuotient := pointwiseDiv.value at255 derivative(f), derivative(g), C with256 derivativeFFunction, derivativeGFunction, typedDF, typedDG257have equal := realDiv.eq_of_cross at258 f(X), g(X), derivative(f)(C), derivative(g)(C) with259 typedFX, typedGX, typedDF, typedDG, nonzeroGX, nonzeroC, cross260witness C261refine⟨realC, leftC, rightC, _⟩262rw [quotientValue, derivativeQuotient]263exact equal264265-- Section: LHopital266-- L'Hôpital's rule in the 0/0 form at a point: when f and g are267-- differentiable near A with f(A) = g(A) = 0 and g′ nonzero away from A,268-- f′/g′ → L at A forces f/g → L at A.269theorem LHopital.theorem (f g : ℝ → ℝ) (A L W : Real)270requires radius : 0 < W271requires differentiableF : ∀ X ∈ openBall(A, W), DifferentiableAt(f, X)272requires differentiableG : ∀ X ∈ openBall(A, W), DifferentiableAt(g, X)273requires nonzero : ∀ X ∈ openBall(A, W), X ≠ A → g′(X) ≠ (0:R)274requires zeroF : f(A) =0275requires zeroG : g(A) =0276requires limit : TendsToAt(pointwiseDiv(f′, g′), L, A)277shows TendsToAt(pointwiseDiv(f, g), L, A)278proof279have fFunction : f : ℝ → ℝ byassumption280have gFunction : g : ℝ → ℝ byassumption281have quotientFunction := pointwiseDiv.function with fFunction, gFunction282constructor283 . exact quotientFunction284 . intros E realE positive285obtain⟨D, realD, positiveD, maps⟩:= TendsToAt.eventually with286 limit, positive287have typedD : Real(D) byexact realD288obtain⟨S, realS, positiveS, belowD, belowW⟩:=289 positiveRadius.common with positiveD, radius290have typedS : Real(S) byexact realS291witness S292refine⟨realS, positiveS, _⟩293intros X realX distinct near294have typedX : Real(X) byexact realX295have nearD := openBall.mono with near, belowD296have nearW := openBall.mono with near, belowW297have order := realLess.trichotomy at X, A298rcases order with below | equal | above299 . obtain⟨C, realC, leftC, rightC, value⟩:= LHopital.left with300 fFunction, gFunction, radius, differentiableF, differentiableG, nonzero,301 zeroF, zeroG, nearW, below302have typedC : Real(C) byexact realC303have inBallD := openBall.left_between at A, C, X, D with304 nearD, leftC, rightC305have distinctC : C ≠ A306intro equalC307rw equalC at rightC308linarith309have mapped := maps with C, realC, distinctC, inBallD310rw value311exact mapped312 . exfalso313exact distinct with equal314 . obtain⟨C, realC, leftC, rightC, value⟩:= LHopital.right with315 fFunction, gFunction, radius, differentiableF, differentiableG, nonzero,316 zeroF, zeroG, nearW, above317have typedC : Real(C) byexact realC318have inBallD := openBall.right_between at A, C, X, D with319 nearD, leftC, rightC320have distinctC : C ≠ A321intro equalC322rw equalC at leftC323linarith324have mapped := maps with C, realC, distinctC, inBallD325rw value326exact mapped327