diff --git a/.gitignore b/.gitignore index 8b0e4b53..b7055155 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ hol_lib_inlined.ml unit_tests_inlined.ml unit_tests.byte unit_tests.native +hol-*.ckpt +__pycache__/ cake-x64-64.tar.gz cake-x64-64 @@ -35,4 +37,4 @@ checkpoint candle_boot.ml types.txt -candle_insulate.ml \ No newline at end of file +candle_insulate.ml diff --git a/100/green.ml b/100/green.ml new file mode 100644 index 00000000..f5845574 --- /dev/null +++ b/100/green.ml @@ -0,0 +1,5228 @@ +(* ========================================================================= *) +(* Green's theorem for rectifiable Jordan curves via the Cauchy transform. *) +(* *) +(* MAIN RESULT (COMPLEX_GREEN): *) +(* For f locally C^1 near the curve, g a rectifiable closed path: *) +(* (1/2i) path_integral g f *) +(* = integral_C winding_number(g,z) * dbar(f)(z) dA(z) *) +(* *) +(* COROLLARIES: *) +(* COMPLEX_GREEN_ALT -- with Cx(Re(winding_number)) (technical form) *) +(* COMPLEX_GREEN_INSIDE -- integral restricted to inside(path_image g) *) +(* GREEN_THEOREM_CURL -- real curl form with partial derivatives *) +(* GREEN_AREA_ABS -- orientation-free area via path integral *) +(* *) +(* HIGH-LEVEL PROOF STRATEGY *) +(* *) +(* The approach avoids the usual regularity theory for the Riemann mapping *) +(* or smoothing/approximation of the boundary curve. Instead, it uses the *) +(* Cauchy transform (the convolution operator Tf(w) = integral f(z)/(z-w)) *) +(* as a left inverse of the d-bar operator, inspired by Kostya_I's answer: *) +(* https://mathoverflow.net/questions/307713 *) +(* The technique is rooted in the Cauchy transform / dbar framework of *) +(* Ahlfors, "Lectures on Quasiconformal Mappings" (1966, 2nd ed. 2006), *) +(* and appears in Bonk's UCLA complex analysis lecture notes, Ch. 20: *) +(* https://www.math.ucla.edu/~mbonk/complana.pdf *) +(* The key steps are: *) +(* *) +(* 1. POMPEIU FORMULA (CAUCHY_TRANSFORM_INVERTS_DBAR): *) +(* For f in C^1_c(C), integral dbar(f)(z)/(z-w) dA(z) = -pi*f(w). *) +(* Proved via polar coordinates and integration by parts. *) +(* See Ahlfors, "Complex Analysis", Ch.5; Bonk, lecture notes, Lemma 20. *) +(* *) +(* 2. FUBINI EXCHANGE (FUBINI_PATH_AREA): *) +(* Swap the path integral and area integral: *) +(* integral_C (integral_gamma f(z)/(z-w) |dw|) dA(z) *) +(* = integral_gamma (integral_C f(z)/(z-w) dA(z)) |dw| *) +(* Uses product-space absolute integrability (Lp estimates + Holder). *) +(* *) +(* 3. SMOOTH EXTENSION (SMOOTH_EXTENSION_FROM_OPEN): *) +(* Given f locally C^1 near the curve, extend to a C^1 function with *) +(* compact support on all of C. Uses Hermite cutoff functions composed *) +(* with a finite ball cover (Lebesgue number lemma). *) +(* *) +(* 4. ASSEMBLY (COMPLEX_GREEN_ALT): *) +(* Combine Pompeiu + Fubini: the path integral of f equals an area *) +(* integral of winding_number * dbar(f). The winding number appears *) +(* because the Cauchy kernel 1/(z-w) integrated along the path gives *) +(* 2*pi*i*winding_number(g,w) by definition. The smooth extension step *) +(* reduces the local C^1 case to the global C^1 case. *) +(* ========================================================================= *) + +needs "Multivariate/cauchy.ml";; +needs "Multivariate/lpspaces.ml";; + +prioritize_real();; + +(* ========================================================================= *) +(* The Wirtinger (d-bar) derivative. *) +(* *) +(* For a real-differentiable function f: C -> C with Jacobian J at z: *) +(* df/dz-bar = (1/2)(J11 - J22 + i*(J21 + J12)) *) +(* = (1/2)(df/dx + i*df/dy) *) +(* *) +(* f is holomorphic iff df/dz-bar = 0 (Cauchy-Riemann equations). *) +(* ========================================================================= *) + +(* The d-bar derivative in terms of the Jacobian matrix *) +let wirtinger_dbar = new_definition + `wirtinger_dbar (f:complex->complex) (z:complex) = + complex((jacobian f (at z))$1$1 - (jacobian f (at z))$2$2, + (jacobian f (at z))$2$1 + (jacobian f (at z))$1$2) / Cx(&2)`;; + +(* The holomorphic Wirtinger derivative df/dz *) +let wirtinger_dz = new_definition + `wirtinger_dz (f:complex->complex) (z:complex) = + complex((jacobian f (at z))$1$1 + (jacobian f (at z))$2$2, + (jacobian f (at z))$2$1 - (jacobian f (at z))$1$2) / Cx(&2)`;; + +(* ------------------------------------------------------------------------- *) +(* Key property: wirtinger_dbar = 0 iff f is holomorphic. *) +(* This is exactly the Cauchy-Riemann equations. *) +(* ------------------------------------------------------------------------- *) + +let WIRTINGER_DBAR_EQ_ZERO = prove + (`!f z. f differentiable (at z) + ==> (wirtinger_dbar f z = Cx(&0) <=> + f complex_differentiable (at z))`, + REPEAT STRIP_TAC THEN + REWRITE_TAC[wirtinger_dbar; COMPLEX_EQ; RE_DIV_CX; IM_DIV_CX; RE; IM; + RE_CX; IM_CX] THEN + REWRITE_TAC[CAUCHY_RIEMANN] THEN + ASM_REWRITE_TAC[] THEN REAL_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* When f is holomorphic, wirtinger_dz agrees with complex_derivative. *) +(* ------------------------------------------------------------------------- *) + +let WIRTINGER_DZ_COMPLEX_DERIVATIVE = prove + (`!f z. f complex_differentiable (at z) + ==> wirtinger_dz f z = complex_derivative f z`, + REPEAT STRIP_TAC THEN + FIRST_ASSUM(MP_TAC o MATCH_MP COMPLEX_DERIVATIVE_JACOBIAN) THEN + FIRST_ASSUM(STRIP_ASSUME_TAC o + GEN_REWRITE_RULE I [CAUCHY_RIEMANN]) THEN + REWRITE_TAC[wirtinger_dz] THEN + DISCH_THEN SUBST1_TAC THEN + ASM_REWRITE_TAC[COMPLEX_EQ; RE_DIV_CX; IM_DIV_CX; RE; IM] THEN + REAL_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* The Jacobian can be recovered from wirtinger_dz and wirtinger_dbar. *) +(* This is the real content: the Jacobian acts as *) +(* J(h) = dz(f) * h + dbar(f) * cnj(h) *) +(* which connects the real derivative to the Wirtinger operators. *) +(* ------------------------------------------------------------------------- *) + +let FRECHET_WIRTINGER = prove + (`!f f' z. (f has_derivative f') (at z) + ==> !h:complex. f'(h) = + wirtinger_dz f z * h + wirtinger_dbar f z * cnj h`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `(f:complex->complex) differentiable (at z)` ASSUME_TAC THENL + [ASM_MESON_TAC[differentiable]; ALL_TAC] THEN + SUBGOAL_THEN `f' = (\h:complex. jacobian (f:complex->complex) (at z) ** h)` SUBST1_TAC THENL + [MATCH_MP_TAC FRECHET_DERIVATIVE_UNIQUE_AT THEN + EXISTS_TAC `f:complex->complex` THEN + EXISTS_TAC `z:complex` THEN + ASM_REWRITE_TAC[GSYM JACOBIAN_WORKS]; + ALL_TAC] THEN + REWRITE_TAC[wirtinger_dz; wirtinger_dbar] THEN + REWRITE_TAC[COMPLEX_EQ] THEN + REWRITE_TAC[cnj; complex_mul; complex_add; RE; IM; + RE_DIV_CX; IM_DIV_CX; + matrix_vector_mul; DIMINDEX_2; SUM_2] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + SIMP_TAC[LAMBDA_BETA; DIMINDEX_2; ARITH] THEN + REWRITE_TAC[GSYM RE_DEF; GSYM IM_DEF] THEN + REAL_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Wirtinger d-bar of z-bar is 1 (conjugation). *) +(* This is a key test: d/dz-bar(z-bar) = 1. *) +(* We need the Jacobian of the conjugation function. *) +(* ------------------------------------------------------------------------- *) + +let HAS_DERIVATIVE_CNJ = prove + (`!z. (cnj has_derivative cnj) (at z)`, + GEN_TAC THEN MATCH_MP_TAC HAS_DERIVATIVE_LINEAR THEN + REWRITE_TAC[LINEAR_CNJ]);; + +(* ------------------------------------------------------------------------- *) +(* Wirtinger d-bar of specific simple functions. *) +(* We compute d-bar(z-bar) = 1, which is needed for the area formula. *) +(* This requires computing the Jacobian of conjugation. *) +(* ------------------------------------------------------------------------- *) + +(* Jacobian of conjugation: cnj(x + iy) = x - iy, so J = [[1,0],[0,-1]] *) +let JACOBIAN_CNJ = prove + (`!z. jacobian cnj (at z) = (vector[vector[&1;&0]; vector[&0;-- &1]]:real^2^2)`, + GEN_TAC THEN REWRITE_TAC[jacobian] THEN + SUBGOAL_THEN `frechet_derivative cnj (at z) = cnj` SUBST1_TAC THENL + [MATCH_MP_TAC HAS_FRECHET_DERIVATIVE_UNIQUE_AT THEN + REWRITE_TAC[HAS_DERIVATIVE_CNJ]; ALL_TAC] THEN + SUBGOAL_THEN + `cnj = (\x:complex. + (vector[vector[&1;&0]; vector[&0;-- &1]]:real^2^2) ** x)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `w:complex` THEN + REWRITE_TAC[COMPLEX_EQ] THEN + REWRITE_TAC[cnj; matrix_vector_mul; DIMINDEX_2; SUM_2] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + SIMP_TAC[LAMBDA_BETA; DIMINDEX_2; ARITH; VECTOR_2] THEN + REWRITE_TAC[GSYM RE_DEF; GSYM IM_DEF; RE; IM] THEN + REAL_ARITH_TAC; + REWRITE_TAC[MATRIX_OF_MATRIX_VECTOR_MUL]]);; + +let WIRTINGER_DBAR_CNJ = prove + (`wirtinger_dbar cnj = \z. Cx(&1)`, + REWRITE_TAC[FUN_EQ_THM; wirtinger_dbar; JACOBIAN_CNJ; VECTOR_2] THEN + REWRITE_TAC[CX_DEF] THEN + CONV_TAC(ONCE_DEPTH_CONV REAL_RAT_REDUCE_CONV) THEN + REWRITE_TAC[complex_div; COMPLEX_EQ; RE; IM; complex_mul; + complex_inv; RE_CX; IM_CX] THEN + REAL_ARITH_TAC);; + +let WIRTINGER_DBAR_I = prove + (`wirtinger_dbar I = \z. Cx(&0)`, + SIMP_TAC[I_DEF; FUN_EQ_THM; DIFFERENTIABLE_ID; WIRTINGER_DBAR_EQ_ZERO; + COMPLEX_DIFFERENTIABLE_ID]);; + +(* ------------------------------------------------------------------------- *) +(* Wirtinger d-bar is continuous when the Jacobian is continuous. *) +(* This is needed for the Fubini step in the Gauss-Green proof. *) +(* ------------------------------------------------------------------------- *) + +let WIRTINGER_DBAR_FRECHET = prove + (`!f:complex->complex z. + wirtinger_dbar f z = + (frechet_derivative f (at z) (Cx(&1)) + + ii * frechet_derivative f (at z) ii) / Cx(&2)`, + REPEAT GEN_TAC THEN + REWRITE_TAC[wirtinger_dbar; jacobian; COMPLEX_EQ] THEN + REWRITE_TAC[complex_add; complex_mul; RE; IM; + RE_DIV_CX; IM_DIV_CX; ii; CX_DEF; complex] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + SIMP_TAC[MATRIX_COMPONENT; DIMINDEX_2; ARITH] THEN + SIMP_TAC[LAMBDA_BETA; DIMINDEX_2; ARITH; VECTOR_2] THEN + REWRITE_TAC[COMPLEX_BASIS] THEN + REWRITE_TAC[GSYM RE_DEF; GSYM IM_DEF; ii; CX_DEF; complex] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + SIMP_TAC[VECTOR_2; LAMBDA_BETA; DIMINDEX_2; ARITH] THEN + REWRITE_TAC[GSYM RE_DEF; GSYM IM_DEF] THEN + REAL_ARITH_TAC);; + +let WIRTINGER_DBAR_CONTINUOUS = prove + (`!f:complex->complex s. + (!h. (\z. frechet_derivative f (at z) h) continuous_on s) + ==> (\z. wirtinger_dbar f z) continuous_on s`, + REPEAT STRIP_TAC THEN REWRITE_TAC[WIRTINGER_DBAR_FRECHET] THEN + REWRITE_TAC[complex_div] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_RMUL THEN + MATCH_MP_TAC CONTINUOUS_ON_ADD THEN CONJ_TAC THENL + [FIRST_X_ASSUM(MP_TAC o SPEC `Cx(&1)`) THEN REWRITE_TAC[]; + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_LMUL THEN + FIRST_X_ASSUM(MP_TAC o SPEC `ii`) THEN REWRITE_TAC[]]);; + +(* Wirtinger d-bar has bounded support when f does *) +let WIRTINGER_DBAR_BOUNDED_SUPPORT = prove + (`!f:complex->complex. + f differentiable_on (:complex) /\ + bounded (support (+) f (:complex)) + ==> bounded (support (+) (wirtinger_dbar f) (:complex))`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC BOUNDED_SUBSET THEN + EXISTS_TAC `closure(support (+) (f:complex->complex) (:complex))` THEN + ASM_SIMP_TAC[BOUNDED_CLOSURE] THEN + REWRITE_TAC[SUBSET] THEN X_GEN_TAC `z:complex` THEN + REWRITE_TAC[IN_SUPPORT; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + DISCH_TAC THEN + REWRITE_TAC[CLOSURE_APPROACHABLE] THEN + X_GEN_TAC `e:real` THEN DISCH_TAC THEN + ASM_CASES_TAC + `?y:complex. y IN support (+) (f:complex->complex) (:complex) /\ + dist(z,y) < e` THENL + [ASM_MESON_TAC[DIST_SYM]; ALL_TAC] THEN + (* f is zero in ball(z,e), so derivative is zero, contradicting dbar != 0 *) + SUBGOAL_THEN `!y:complex. dist(y,z) < e ==> (f:complex->complex) y = vec 0` + ASSUME_TAC THENL + [X_GEN_TAC `y:complex` THEN DISCH_TAC THEN + UNDISCH_TAC + `~(?y:complex. y IN support (+) (f:complex->complex) (:complex) /\ + dist(z:complex,y) < e)` THEN + REWRITE_TAC[NOT_EXISTS_THM; DE_MORGAN_THM; + IN_SUPPORT; IN_UNIV; NEUTRAL_VECTOR_ADD; REAL_NOT_LT] THEN + DISCH_THEN(MP_TAC o SPEC `y:complex`) THEN + ASM_MESON_TAC[DIST_SYM; REAL_NOT_LE]; ALL_TAC] THEN + SUBGOAL_THEN `frechet_derivative (f:complex->complex) (at z) = (\h. vec 0)` + ASSUME_TAC THENL + [MATCH_MP_TAC HAS_FRECHET_DERIVATIVE_UNIQUE_AT THEN + MATCH_MP_TAC HAS_DERIVATIVE_TRANSFORM_AT THEN + MAP_EVERY EXISTS_TAC + [`(\x:complex. vec 0):complex->complex`; `e:real`] THEN + ASM_REWRITE_TAC[HAS_DERIVATIVE_CONST] THEN + GEN_TAC THEN DISCH_TAC THEN CONV_TAC SYM_CONV THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + UNDISCH_TAC `~(wirtinger_dbar (f:complex->complex) z = vec 0)` THEN + REWRITE_TAC[WIRTINGER_DBAR_FRECHET] THEN + ASM_REWRITE_TAC[COMPLEX_VEC_0; COMPLEX_MUL_RZERO] THEN + REWRITE_TAC[COMPLEX_ADD_LID; complex_div; COMPLEX_MUL_LZERO]);; + +(* ========================================================================= *) +(* The Cauchy transform inverts the d-bar operator (Pompeiu formula). *) +(* *) +(* The Cauchy transform of g is Tg(w) = (1/pi) int g(z)/(z-w) dA(z). *) +(* For f in C^1_c(C), T(dbar f) = -f, or equivalently: *) +(* integral (dbar f)(z) / (z - w) dA(z) = -pi * f(w) *) +(* *) +(* This is the key identity CAUCHY_TRANSFORM_INVERTS_DBAR below. *) +(* See Ahlfors, "Complex Analysis" Ch.4 Sec.6; Bonk, lecture notes, *) +(* Lemma 20.2; Ahlfors QC Ch.5. *) +(* *) +(* Proof approach: polar coordinates centered at w, integration by parts. *) +(* We state the theorem with explicit hypotheses rather than defining *) +(* the Cauchy transform or C^1_c as HOL Light constants. *) +(* ========================================================================= *) + +(* ----- Proof sketch for CAUCHY_TRANSFORM_INVERTS_DBAR ----- *) +(* *) +(* Let g(r,t) = f(w + r * exp(i*t)). Then: *) +(* (dbar f)(w + r*exp(it)) = exp(it)/2 * [dg/dr + (i/r)*dg/dt] *) +(* *) +(* The integrand (dbar f)(z) / (z - w) in polar becomes: *) +(* (dbar f)(w + r*exp(it)) * exp(-it)/r * r [Jacobian = r] *) +(* = (dbar f)(w + r*exp(it)) * exp(-it) *) +(* = (1/2) * [dg/dr + (i/r)*dg/dt] *) +(* *) +(* After Fubini (r from 0 to R, t from 0 to 2*pi): *) +(* integral = (1/2) int_0^{2pi} int_0^R dg/dr dr dt *) +(* + (i/2) int_0^R (1/r) int_0^{2pi} dg/dt dt dr *) +(* *) +(* Radial part: by FTC, int_0^R dg/dr dr = g(R,t) - g(0,t) *) +(* = 0 - f(w) = -f(w) for large R (compact support). *) +(* Hence = (1/2) * 2*pi * (-f(w)) = -pi * f(w). *) +(* *) +(* Angular part: int_0^{2pi} dg/dt dt = g(r,2pi) - g(r,0) = 0 *) +(* (periodicity of exp). So this part = 0. *) +(* *) +(* Total: -pi * f(w). QED. *) + +(* Sub-lemma: translation reduces to w = 0 *) +let CAUCHY_TRANSFORM_DBAR_TRANSLATION = prove + (`!f:complex->complex w. + integral (:complex) (\z. wirtinger_dbar f z / (z - w)) = + integral (:complex) (\z. wirtinger_dbar f (w + z) / z)`, + REPEAT GEN_TAC THEN + MP_TAC(ISPEC `w:complex` TRANSLATION_UNIV) THEN + DISCH_THEN(fun th -> + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) [SYM th]) THEN + REWRITE_TAC[GSYM INTEGRAL_TRANSLATION] THEN + REWRITE_TAC[VECTOR_ARITH `(w + x) - w:complex = x`]);; + +(* Helper lemmas for the polar coordinates proof *) + +let CONTINUOUS_ON_TRANSLATE_UNIV = prove + (`!f:complex->complex a. + f continuous_on (:complex) + ==> (\z. f(a + z)) continuous_on (:complex)`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\z:complex. (f:complex->complex)(a + z)) = + (f:complex->complex) o (\z:complex. a + z)` SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_ADD THEN + REWRITE_TAC[CONTINUOUS_ON_CONST; CONTINUOUS_ON_ID]; + FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (REWRITE_RULE[IMP_CONJ] + CONTINUOUS_ON_SUBSET)) THEN REWRITE_TAC[SUBSET_UNIV]]);; + +let BOUNDED_SUPPORT_TRANSLATE = prove + (`!f:complex->complex a. + bounded (support (+) f (:complex)) + ==> bounded (support (+) (\z. f(a + z)) (:complex))`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `support (+) (\z:complex. (f:complex->complex)(a + z)) (:complex) SUBSET + IMAGE (\z. --a + z) (support (+) f (:complex))` + ASSUME_TAC THENL + [REWRITE_TAC[SUBSET; IN_SUPPORT; IN_UNIV; IN_IMAGE] THEN + X_GEN_TAC `z:complex` THEN STRIP_TAC THEN + EXISTS_TAC `a + z:complex` THEN + ASM_REWRITE_TAC[VECTOR_ARITH `--a + a + z:complex = z`; + NEUTRAL_VECTOR_ADD] THEN + REWRITE_TAC[IN_SUPPORT; IN_UNIV] THEN + ASM_REWRITE_TAC[NEUTRAL_VECTOR_ADD]; + ALL_TAC] THEN + MATCH_MP_TAC BOUNDED_SUBSET THEN + EXISTS_TAC `IMAGE (\z:complex. --a + z) + (support (+) (f:complex->complex) (:complex))` THEN + ASM_SIMP_TAC[BOUNDED_TRANSLATION]);; + +let POLAR_SIMPLIFY = prove + (`!g:complex (r:real^1) (e:complex). + &0 < drop r /\ ~(e = Cx(&0)) + ==> drop r % (g / (Cx(drop r) * e)) = g * inv e`, + REPEAT STRIP_TAC THEN REWRITE_TAC[COMPLEX_CMUL] THEN + SUBGOAL_THEN `~(Cx(drop r) = Cx(&0))` ASSUME_TAC THENL + [REWRITE_TAC[CX_INJ] THEN ASM_REAL_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[complex_div; COMPLEX_INV_MUL] THEN + SUBGOAL_THEN `Cx(drop r) * (g * (inv(Cx(drop r)) * inv e)) = + (Cx(drop r) * inv(Cx(drop r))) * (g * inv e)` + SUBST1_TAC THENL + [SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + ASM_SIMP_TAC[COMPLEX_MUL_RINV; COMPLEX_MUL_LID]);; + +let INV_CEXP_II = prove + (`!t. inv(cexp(ii * Cx t)) = cnj(cexp(ii * Cx t))`, + GEN_TAC THEN REWRITE_TAC[CNJ_CEXP] THEN + CONV_TAC SYM_CONV THEN REWRITE_TAC[GSYM CEXP_NEG] THEN + AP_TERM_TAC THEN + REWRITE_TAC[cnj; ii; CX_DEF; complex_mul; complex_neg; + RE; IM; COMPLEX_EQ] THEN + REAL_ARITH_TAC);; + +let POLAR_CEXP_SIMPLIFY = prove + (`!g:complex (r:real^1) t. + &0 < drop r + ==> drop r % (g / (Cx(drop r) * cexp(ii * Cx(drop t)))) = + g * cnj(cexp(ii * Cx(drop t)))`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `~(cexp(ii * Cx(drop t)) = Cx(&0))` ASSUME_TAC THENL + [REWRITE_TAC[CEXP_NZ]; ALL_TAC] THEN + ASM_SIMP_TAC[POLAR_SIMPLIFY] THEN + REWRITE_TAC[INV_CEXP_II]);; + +let WIRTINGER_DBAR_FRECHET_POLAR = prove + (`!f:complex->complex z e. + f differentiable at z + ==> frechet_derivative f (at z) e + + ii * frechet_derivative f (at z) (ii * e) = + Cx(&2) * wirtinger_dbar f z * cnj e`, + REPEAT STRIP_TAC THEN + FIRST_X_ASSUM(MP_TAC o REWRITE_RULE[differentiable]) THEN + DISCH_THEN(X_CHOOSE_TAC `f':complex->complex`) THEN + MP_TAC(ISPECL [`f:complex->complex`; `f':complex->complex`; `z:complex`] + FRECHET_WIRTINGER) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN `f' = frechet_derivative (f:complex->complex) (at z)` + SUBST_ALL_TAC THENL + [MATCH_MP_TAC FRECHET_DERIVATIVE_AT THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + ASM_REWRITE_TAC[CNJ_MUL; CNJ_II] THEN + CONV_TAC COMPLEX_RING);; + +let CEXP_2PII = prove + (`cexp(ii * Cx(&2 * pi)) = Cx(&1)`, + MP_TAC(SPEC `&1` CEXP_INTEGER_2PI) THEN + REWRITE_TAC[INTEGER_CLOSED; REAL_MUL_LID; COMPLEX_MUL_SYM]);; + +let CEXP_0II = prove + (`cexp(ii * Cx(&0)) = Cx(&1)`, + REWRITE_TAC[COMPLEX_MUL_RZERO; CEXP_0]);; + +(* ========================================================================= *) +(* Absolutely integrable cpow. *) +(* Uses polar Fubini to show (z - w) cpow p is integrable on bounded sets *) +(* for Re p > -2. *) +(* ========================================================================= *) + +let NORM_CPOW_COMPLEX = prove + (`!w z. real z + ==> norm(w cpow z) = + if w = Cx(&0) /\ z = Cx(&0) then &0 else norm(w) rpow (Re z)`, + X_GEN_TAC `w:complex` THEN + REWRITE_TAC[FORALL_REAL; RE_CX; CX_INJ] THEN X_GEN_TAC `r:real` THEN + REWRITE_TAC[cpow] THEN ASM_CASES_TAC `w = Cx(&0)` THEN + ASM_SIMP_TAC[COMPLEX_NORM_0; RPOW_ZERO; COND_ID] THEN + ASM_SIMP_TAC[NORM_CEXP; RE_MUL_CX; RE_CLOG] THEN + ASM_REWRITE_TAC[rpow; NORM_POS_LT; NORM_EQ_0; COMPLEX_VEC_0]);; + +let ABSOLUTELY_INTEGRABLE_CPOW = prove + (`!s p w. + bounded s /\ measurable s /\ real p /\ --(&2) < Re p + ==> (\z. (z - w) cpow p) absolutely_integrable_on s`, + REWRITE_TAC[IMP_CONJ; RIGHT_FORALL_IMP_THM; FORALL_REAL] THEN + GEN_TAC THEN REPEAT DISCH_TAC THEN X_GEN_TAC `p:real` THEN + REWRITE_TAC[RE_CX] THEN DISCH_TAC THEN X_GEN_TAC `w:complex` THEN + FIRST_X_ASSUM(MP_TAC o SPEC `w:complex` o MATCH_MP BOUNDED_SUBSET_CBALL) THEN + DISCH_THEN(X_CHOOSE_THEN `r:real` STRIP_ASSUME_TAC) THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `cball(w:complex,r)` THEN + ASM_SIMP_TAC[MEASURABLE_IMP_LEBESGUE_MEASURABLE] THEN + REPEAT(FIRST_X_ASSUM(K ALL_TAC o + check (free_in `s:complex->bool`) o concl)) THEN + SUBST1_TAC(COMPLEX_RING `w = w + Cx(&0)`) THEN + REWRITE_TAC[GSYM ABSOLUTELY_INTEGRABLE_TRANSLATION; CBALL_TRANSLATION] THEN + REWRITE_TAC[COMPLEX_RING `(w + y) - (w + Cx(&0)) = y`] THEN + ONCE_REWRITE_TAC[ABSOLUTELY_INTEGRABLE_MEASURABLE] THEN + ASM_SIMP_TAC[MEASURABLE_ON_CPOW; LEBESGUE_MEASURABLE_CBALL] THEN + SIMP_TAC[NORM_CPOW_COMPLEX; REAL_CX; CX_INJ; RE_CX] THEN + ASM_CASES_TAC `p:real = &0` THEN ASM_REWRITE_TAC[] THENL + [REWRITE_TAC[RPOW_0; COND_RAND; LIFT_NUM] THEN + MATCH_MP_TAC INTEGRABLE_CASES THEN REWRITE_TAC[INTEGRABLE_ON_CONST] THEN + REWRITE_TAC[SET_RULE `{x | x IN s /\ ~(x = a)} = s DELETE a`] THEN + REWRITE_TAC[MEASURABLE_DELETE; MEASURABLE_CBALL]; + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE] THEN + GEN_REWRITE_TAC I [GSYM ABSOLUTELY_INTEGRABLE_RESTRICT_UNIV] THEN + W(MP_TAC o PART_MATCH (lhand o rand) FUBINI_TONELLI_POLAR o snd) THEN + ANTS_TAC THENL + [MATCH_MP_TAC MEASURABLE_ON_CASES THEN + REWRITE_TAC[IN_GSPEC; MEASURABLE_ON_CONST] THEN + REWRITE_TAC[LEBESGUE_MEASURABLE_CBALL] THEN MATCH_MP_TAC + CONTINUOUS_AE_IMP_MEASURABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `{Cx(&0)}` THEN + REWRITE_TAC[LEBESGUE_MEASURABLE_UNIV; NEGLIGIBLE_SING] THEN + SUBGOAL_THEN + `(\x:complex. lift(norm x rpow p)) = + (lift o (\x. x rpow p) o drop) o lift o norm` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN + REWRITE_TAC[CONTINUOUS_ON_LIFT_NORM] THEN + MP_TAC(SPECL[`(:real) DELETE (&0)`; `p:real`] REAL_CONTINUOUS_ON_RPOW) THEN + REWRITE_TAC[IN_DELETE; REAL_CONTINUOUS_ON] THEN + MATCH_MP_TAC(REWRITE_RULE[IMP_CONJ_ALT] CONTINUOUS_ON_SUBSET) THEN + REWRITE_TAC[IMAGE_o] THEN MATCH_MP_TAC IMAGE_SUBSET THEN + REWRITE_TAC[SUBSET; FORALL_IN_IMAGE; IN_DIFF; IN_SING; IN_UNIV; + IN_DELETE; COMPLEX_NORM_ZERO]; + DISCH_THEN SUBST1_TAC] THEN + REWRITE_TAC[COND_RAND; COND_RATOR] THEN + REWRITE_TAC[IN_CBALL_0; GSYM COMPLEX_VEC_0] THEN + REWRITE_TAC[COMPLEX_NORM_MUL; NORM_CEXP_II; REAL_MUL_RID] THEN + REWRITE_TAC[NORM_0; LIFT_NUM; VECTOR_MUL_RZERO] THEN + REWRITE_TAC[NORM_LIFT; REAL_ABS_NORM; GSYM LIFT_CMUL] THEN + REWRITE_TAC[COMPLEX_NORM_CX; REAL_ABS_RPOW; REAL_ABS_ABS] THEN + REWRITE_TAC[ABSOLUTELY_INTEGRABLE_CONST; INTEGRAL_CONST] THEN + REWRITE_TAC[EMPTY_GSPEC; NEGLIGIBLE_EMPTY; COND_RAND] THEN + REWRITE_TAC[VECTOR_MUL_RZERO] THEN + REWRITE_TAC[REWRITE_RULE[IN] INTEGRABLE_RESTRICT_INTER] THEN + MATCH_MP_TAC INTEGRABLE_CMUL THEN + REWRITE_TAC[SET_RULE `(\x. P x) INTER {x | Q x} = {x | Q x /\ P x}`] THEN + REWRITE_TAC[REAL_ARITH `&0 <= x /\ abs x <= a <=> &0 <= x /\ x <= a`] THEN + SUBGOAL_THEN + `{x | &0 <= drop x /\ drop x <= r} = interval[vec 0,lift r]` + SUBST1_TAC THENL + [REWRITE_TAC[EXTENSION; IN_ELIM_THM; IN_INTERVAL_1] THEN + REWRITE_TAC[LIFT_DROP; DROP_VEC]; + ALL_TAC] THEN + ASM_CASES_TAC `interval[vec 0,lift r] = {}` THEN + ASM_REWRITE_TAC[INTEGRABLE_ON_EMPTY] THEN + RULE_ASSUM_TAC(REWRITE_RULE[INTERVAL_NE_EMPTY_1; DROP_VEC; LIFT_DROP]) THEN + MP_TAC(SPECL + [`\x. inv(p + &2) * x rpow (p + &2)`; `\x. x rpow (p + &1)`; + `&0:real`; `r:real`] REAL_FUNDAMENTAL_THEOREM_OF_CALCULUS_INTERIOR) THEN + ASM_REWRITE_TAC[] THEN ANTS_TAC THENL + [CONJ_TAC THENL + [MATCH_MP_TAC REAL_CONTINUOUS_ON_LMUL THEN + MATCH_MP_TAC REAL_CONTINUOUS_ON_RPOW THEN ASM_REAL_ARITH_TAC; + REWRITE_TAC[IN_REAL_INTERVAL] THEN REPEAT STRIP_TAC THEN + REAL_DIFF_TAC THEN + ASM_REWRITE_TAC[REAL_ARITH `(p + &2) - &1:real = p + &1`] THEN + UNDISCH_TAC `--(&2):real < p` THEN CONV_TAC REAL_FIELD]; + DISCH_THEN(MP_TAC o MATCH_MP HAS_REAL_INTEGRAL_INTEGRABLE)] THEN + REWRITE_TAC[REAL_INTEGRABLE_ON; IMAGE_LIFT_REAL_INTERVAL; LIFT_NUM] THEN + MATCH_MP_TAC INTEGRABLE_SPIKE THEN EXISTS_TAC `{vec 0:real^1}` THEN + REWRITE_TAC[NEGLIGIBLE_SING; IN_SING; IN_DIFF; GSYM DROP_EQ] THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; LIFT_DROP; o_THM] THEN + ASM_SIMP_TAC[real_abs; RPOW_ADD_ALT; RPOW_POW; REAL_POW_1] THEN + REAL_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* inv(z - w) is absolutely integrable on bounded measurable sets. *) +(* ------------------------------------------------------------------------- *) + +let INV_ABSOLUTELY_INTEGRABLE_BOUNDED = prove + (`!s w:complex. + bounded s /\ measurable s + ==> (\z. inv(z - w)) absolutely_integrable_on s`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC(REWRITE_RULE[IMP_IMP] ABSOLUTELY_INTEGRABLE_SPIKE) THEN + EXISTS_TAC `(\z:complex. (z - w) cpow (--Cx(&1)))` THEN + EXISTS_TAC `{w:complex}` THEN + REWRITE_TAC[NEGLIGIBLE_SING] THEN CONJ_TAC THENL + [X_GEN_TAC `z:complex` THEN REWRITE_TAC[IN_DIFF; IN_SING] THEN + STRIP_TAC THEN REWRITE_TAC[CPOW_NEG; CPOW_N; COMPLEX_POW_1] THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[]; + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_CPOW THEN + ASM_SIMP_TAC[REAL_NEG; REAL_CX; RE_NEG; RE_CX] THEN + CONV_TAC REAL_RAT_REDUCE_CONV]);; + +(* ========================================================================= *) +(* Lp-based infrastructure. *) +(* Uses Holder's inequality and Lp-space theory to prove integrability of *) +(* Cauchy-type kernels f(z)/(z-w). *) +(* ========================================================================= *) + +let LSPACE_COMPLEX_INV = prove + (`!s p w:complex. + bounded s /\ measurable s /\ p < &2 ==> (\z. inv(z - w)) IN lspace s p`, + REPEAT STRIP_TAC THEN REWRITE_TAC[LSPACE_ALT; IN_ELIM_THM] THEN + CONJ_TAC THENL + [MP_TAC(SPECL [`s:complex->bool`; `--Cx(&1)`; `w:complex`] + ABSOLUTELY_INTEGRABLE_CPOW) THEN + ASM_SIMP_TAC[CPOW_NEG; CPOW_N; REAL_NEG; REAL_CX; RE_NEG; RE_CX] THEN + CONV_TAC REAL_RAT_REDUCE_CONV THEN + REWRITE_TAC[COMPLEX_POW_1; COMPLEX_SUB_0] THEN + REWRITE_TAC[ABSOLUTELY_INTEGRABLE_MEASURABLE] THEN + DISCH_THEN(MP_TAC o CONJUNCT1) THEN MATCH_MP_TAC EQ_IMP THEN + AP_THM_TAC THEN AP_TERM_TAC THEN ABS_TAC THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[COMPLEX_SUB_REFL; COMPLEX_INV_0]; + MP_TAC(SPECL [`s:complex->bool`; `--Cx p`; `w:complex`] + ABSOLUTELY_INTEGRABLE_CPOW) THEN + ASM_SIMP_TAC[CPOW_NEG; CPOW_N; REAL_NEG; REAL_CX; RE_NEG; RE_CX] THEN + ASM_REWRITE_TAC[REAL_LT_NEG2] THEN DISCH_THEN(MP_TAC o + MATCH_MP ABSOLUTELY_INTEGRABLE_NORM) THEN + REWRITE_TAC[] THEN MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_SPIKE THEN + EXISTS_TAC `{w:complex}` THEN REWRITE_TAC[NEGLIGIBLE_SING] THEN + X_GEN_TAC `z:complex` THEN REWRITE_TAC[IN_DIFF; IN_SING] THEN + STRIP_TAC THEN AP_TERM_TAC THEN + REWRITE_TAC[COMPLEX_NORM_INV; RPOW_INV] THEN AP_TERM_TAC THEN + ASM_SIMP_TAC[NORM_CPOW_COMPLEX; REAL_CX; COMPLEX_SUB_0; RE_CX]]);; + +(* ------------------------------------------------------------------------- *) +(* u(z) / (z - w) is absolutely integrable for compactly supported u. *) +(* ------------------------------------------------------------------------- *) + +let CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE = prove + (`!u:complex->complex w. + u continuous_on (:complex) /\ + bounded (support (+) u (:complex)) + ==> (\z. u(z) / (z - w)) absolutely_integrable_on (:complex)`, + REPEAT STRIP_TAC THEN + REWRITE_TAC[complex_div] THEN + FIRST_X_ASSUM(MP_TAC o SPEC `Cx(&0)` o MATCH_MP BOUNDED_SUBSET_CBALL) THEN + DISCH_THEN(X_CHOOSE_THEN `R:real` STRIP_ASSUME_TAC) THEN + MATCH_MP_TAC(REWRITE_RULE[IMP_IMP] ABSOLUTELY_INTEGRABLE_SPIKE) THEN + EXISTS_TAC `(\z:complex. if z IN cball(Cx(&0),R) then + u z * inv(z - w) else vec 0)` THEN + EXISTS_TAC `{}:complex->bool` THEN + REWRITE_TAC[NEGLIGIBLE_EMPTY; IN_DIFF; NOT_IN_EMPTY; IN_UNIV] THEN + CONJ_TAC THENL + [X_GEN_TAC `z:complex` THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN `~((z:real^2) IN support (+) (u:real^2->real^2) (:real^2))` + MP_TAC THENL [ASM_MESON_TAC[SUBSET]; ALL_TAC] THEN + REWRITE_TAC[support; IN_ELIM_THM; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + DISCH_TAC THEN ASM_REWRITE_TAC[COMPLEX_MUL_LZERO; COMPLEX_VEC_0]; + REWRITE_TAC[ABSOLUTELY_INTEGRABLE_RESTRICT_UNIV] THEN + SUBGOAL_THEN + `(\z:real^2. (u:real^2->real^2) z * inv(z - w)) = + (\z. u z * (\z. inv(z - w)) z)` SUBST1_TAC THENL + [REWRITE_TAC[]; ALL_TAC] THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_BOUNDED_MEASURABLE_PRODUCT THEN + REWRITE_TAC[BILINEAR_COMPLEX_MUL] THEN + REPEAT CONJ_TAC THENL + [MATCH_MP_TAC MEASURABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + REWRITE_TAC[SUBSET_UNIV] THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON THEN ASM_REWRITE_TAC[]; + MATCH_MP_TAC MEASURABLE_IMP_LEBESGUE_MEASURABLE THEN + REWRITE_TAC[MEASURABLE_CBALL]]; + MATCH_MP_TAC COMPACT_IMP_BOUNDED THEN + MATCH_MP_TAC COMPACT_CONTINUOUS_IMAGE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN EXISTS_TAC `(:complex)` THEN + ASM_REWRITE_TAC[SUBSET_UNIV]; + REWRITE_TAC[COMPACT_CBALL]]; + MATCH_MP_TAC INV_ABSOLUTELY_INTEGRABLE_BOUNDED THEN + REWRITE_TAC[BOUNDED_CBALL; MEASURABLE_CBALL]]]);; + +(* ------------------------------------------------------------------------- *) +(* Helper: if-then-else with constant predicate under integral. *) +(* ------------------------------------------------------------------------- *) + +let INTEGRAL_IF_CONST = prove + (`!s P (f:real^M->real^N). + integral s (\y. if P then f y else vec 0) = + if P then integral s f else vec 0`, + REPEAT GEN_TAC THEN COND_CASES_TAC THEN REWRITE_TAC[ETA_AX; INTEGRAL_0]);; + +(* ------------------------------------------------------------------------- *) +(* Negligibility of a graph in the product space real^1 x complex. *) +(* The graph {(x,y) | y = h(x)} is a 1-dim curve in 3-dim space. *) +(* Uses FUBINI_TONELLI_NEGLIGIBLE: each x-slice is a singleton (negligible). *) +(* ------------------------------------------------------------------------- *) + +let NEGLIGIBLE_GRAPH_PRODUCT = prove + (`!h:real^1->complex. h measurable_on (:real^1) ==> + negligible {p:real^(1,2)finite_sum | sndcart p = h(fstcart p)}`, + GEN_TAC THEN DISCH_TAC THEN + SUBGOAL_THEN `(\p:real^(1,2)finite_sum. sndcart p - (h:real^1->complex)(fstcart p)) measurable_on + (:real^(1,2)finite_sum)` ASSUME_TAC THENL + [MATCH_MP_TAC MEASURABLE_ON_SUB THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON THEN + MATCH_MP_TAC LINEAR_CONTINUOUS_ON THEN REWRITE_TAC[LINEAR_SNDCART]; + MATCH_MP_TAC(INST_TYPE [`:2`,`:N`] MEASURABLE_ON_COMPOSE_FSTCART) THEN + ASM_REWRITE_TAC[]]; ALL_TAC] THEN + SUBGOAL_THEN `lebesgue_measurable + {p:real^(1,2)finite_sum | sndcart p = (h:real^1->complex)(fstcart p)}` + ASSUME_TAC THENL + [SUBGOAL_THEN `{p:real^(1,2)finite_sum | sndcart p = h(fstcart p)} = + {p | (\p:real^(1,2)finite_sum. sndcart p - h(fstcart p)) p IN + {vec 0:complex}}` + SUBST1_TAC THENL + [REWRITE_TAC[EXTENSION; IN_ELIM_THM; IN_SING] THEN + GEN_TAC THEN CONV_TAC VECTOR_ARITH; ALL_TAC] THEN + MATCH_MP_TAC LEBESGUE_MEASURABLE_PREIMAGE_CLOSED THEN + ASM_REWRITE_TAC[CLOSED_SING]; ALL_TAC] THEN + FIRST_X_ASSUM(MP_TAC o MATCH_MP + (INST_TYPE [`:1`,`:M`; `:2`,`:N`] FUBINI_TONELLI_NEGLIGIBLE)) THEN + DISCH_THEN SUBST1_TAC THEN + REWRITE_TAC[IN_ELIM_THM; FSTCART_PASTECART; SNDCART_PASTECART] THEN + SUBGOAL_THEN + `{x:real^1 | ~negligible {y:complex | y = (h:real^1->complex) x}} = {}` + SUBST1_TAC THENL + [REWRITE_TAC[EXTENSION; IN_ELIM_THM; NOT_IN_EMPTY] THEN + GEN_TAC THEN REWRITE_TAC[SET_RULE `{y:complex | y = a} = {a}`] THEN + REWRITE_TAC[NEGLIGIBLE_SING]; ALL_TAC] THEN + REWRITE_TAC[NEGLIGIBLE_EMPTY]);; + +(* ------------------------------------------------------------------------- *) +(* Measurability of the Fubini integrand on the product space. *) +(* Key idea: extend g from [0,1] to all of R via Tietze, then factor the *) +(* function as u(y) * inv(y - G(x)) * (if x IN [0,1] then g'(x) else 0). *) +(* Each factor is measurable using standard library lemmas. *) +(* ------------------------------------------------------------------------- *) + +let MEASURABLE_ON_FUBINI_INTEGRAND = prove + (`!u:complex->complex g:real^1->complex. + u continuous_on (:complex) /\ + g absolutely_continuous_on interval[vec 0,vec 1] + ==> (\p:real^(1,2)finite_sum. + if fstcart p IN interval[vec 0,vec 1] + then u(sndcart p) / (sndcart p - g(fstcart p)) * + vector_derivative g (at (fstcart p)) + else vec 0) + measurable_on (:real^(1,2)finite_sum)`, + REPEAT STRIP_TAC THEN + (* Step 1: Extend g continuously from [0,1] to all of R via Tietze *) + SUBGOAL_THEN + `?G:real^1->complex. G continuous_on (:real^1) /\ + (!x. x IN interval[vec 0,vec 1] ==> G x = (g:real^1->complex) x)` + STRIP_ASSUME_TAC THENL + [MP_TAC(ISPECL [`g:real^1->complex`; `(:real^1)`; + `interval[vec 0:real^1,vec 1]`] TIETZE_UNBOUNDED) THEN + ANTS_TAC THENL + [CONJ_TAC THENL + [MATCH_MP_TAC CLOSED_SUBSET THEN + REWRITE_TAC[CLOSED_INTERVAL; SUBSET_UNIV]; + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH; path]]; + MESON_TAC[]]; ALL_TAC] THEN + (* Step 2: Show original = u(y)*inv(y-G(x))*(if-then-else vd_g) via EQ *) + MATCH_MP_TAC MEASURABLE_ON_EQ THEN + EXISTS_TAC `(\p:real^(1,2)finite_sum. + u(sndcart p:complex) * inv(sndcart p - (G:real^1->complex)(fstcart p)) * + (if fstcart p IN interval[vec 0,vec 1] + then vector_derivative (g:real^1->complex) (at (fstcart p)) + else vec 0))` THEN + CONJ_TAC THENL + [(* Equality: factored form = original, pointwise *) + X_GEN_TAC `p:real^(1,2)finite_sum` THEN REWRITE_TAC[IN_UNIV] THEN + COND_CASES_TAC THENL + [ASM_SIMP_TAC[] THEN REWRITE_TAC[complex_div; COMPLEX_MUL_ASSOC]; + REWRITE_TAC[COMPLEX_VEC_0; COMPLEX_MUL_RZERO]]; + ALL_TAC] THEN + (* Step 3: Show factored form measurable as A * (B * C). + Note: complex * is right-associative, so a * b * c = a * (b * c). *) + MATCH_MP_TAC MEASURABLE_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [(* A = u(sndcart p): u continuous => measurable, compose with sndcart *) + MATCH_MP_TAC(INST_TYPE [`:1`,`:M`] MEASURABLE_ON_COMPOSE_SNDCART) THEN + MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON THEN ASM_REWRITE_TAC[]; + (* B * C = inv(sndcart p - G(fstcart p)) * (if ... vd ... else vec 0) *) + MATCH_MP_TAC MEASURABLE_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [(* B = inv(sndcart p - G(fstcart p)) *) + MATCH_MP_TAC MEASURABLE_ON_COMPLEX_INV THEN CONJ_TAC THENL + [(* sndcart p - G(fstcart p) measurable on full space *) + MATCH_MP_TAC MEASURABLE_ON_SUB THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON THEN + MATCH_MP_TAC LINEAR_CONTINUOUS_ON THEN REWRITE_TAC[LINEAR_SNDCART]; + MATCH_MP_TAC(INST_TYPE [`:2`,`:N`] MEASURABLE_ON_COMPOSE_FSTCART) THEN + MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON THEN ASM_REWRITE_TAC[]]; + (* negligible {p | sndcart p = G(fstcart p)} *) + SUBGOAL_THEN + `{p:real^(1,2)finite_sum | + sndcart p - (G:real^1->complex)(fstcart p) = Cx(&0)} = + {p | sndcart p = G(fstcart p)}` + SUBST1_TAC THENL + [REWRITE_TAC[EXTENSION; IN_ELIM_THM; GSYM COMPLEX_VEC_0] THEN + GEN_TAC THEN CONV_TAC VECTOR_ARITH; + MATCH_MP_TAC NEGLIGIBLE_GRAPH_PRODUCT THEN + MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON THEN ASM_REWRITE_TAC[]]]; + (* C = if fstcart p IN [0,1] then vd_g(fstcart p) else vec 0 *) + SUBGOAL_THEN + `(\t:real^1. if t IN interval[vec 0,vec 1] + then vector_derivative (g:real^1->complex) (at t) + else vec 0) measurable_on (:real^1)` + MP_TAC THENL + [REWRITE_TAC[MEASURABLE_ON_UNIV] THEN + MATCH_MP_TAC INTEGRABLE_IMP_MEASURABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + DISCH_THEN(MP_TAC o MATCH_MP + (INST_TYPE [`:2`,`:N`] MEASURABLE_ON_COMPOSE_FSTCART)) THEN + MATCH_MP_TAC(REWRITE_RULE[IMP_CONJ] MEASURABLE_ON_EQ) THEN + X_GEN_TAC `p:real^(1,2)finite_sum` THEN + REWRITE_TAC[IN_UNIV]]]);; + +(* ------------------------------------------------------------------------- *) +(* Uniform bound on Cauchy kernel L^1 norm over compact sets of poles. *) +(* For w in compact K, integral |u(y)/(y-w)| dy <= C uniformly. *) +(* Key tool: cball(0,R) subset cball(w,R+S) + translation of integral. *) +(* ------------------------------------------------------------------------- *) + +let CAUCHY_KERNEL_NORM_UNIFORM_BOUND = prove + (`!u:complex->complex K. + u continuous_on (:complex) /\ + bounded (support (+) u (:complex)) /\ + compact K + ==> ?C. &0 <= C /\ + !w. w IN K ==> + drop(integral (:complex) + (\y. lift(norm(u y / (y - w))))) <= C`, + REPEAT STRIP_TAC THEN + (* Extract R: support SUBSET cball(0,R) *) + FIRST_X_ASSUM(MP_TAC o SPEC `Cx(&0)` o MATCH_MP BOUNDED_SUBSET_CBALL) THEN + DISCH_THEN(X_CHOOSE_THEN `R:real` STRIP_ASSUME_TAC) THEN + (* Extract S: K SUBSET cball(0,S) *) + FIRST_X_ASSUM(MP_TAC o MATCH_MP COMPACT_IMP_BOUNDED) THEN + DISCH_THEN(MP_TAC o SPEC `Cx(&0)` o MATCH_MP BOUNDED_SUBSET_CBALL) THEN + DISCH_THEN(X_CHOOSE_THEN `S:real` STRIP_ASSUME_TAC) THEN + (* u bounded by M on cball(0,R) *) + SUBGOAL_THEN + `?M. &0 <= M /\ !y:complex. norm((u:complex->complex) y) <= M` + STRIP_ASSUME_TAC THENL + [MP_TAC(ISPECL [`u:complex->complex`; `(:complex)`] + CONTINUOUS_ON_CLOSURE) THEN + ASM_REWRITE_TAC[CLOSURE_UNIV; IN_UNIV] THEN + DISCH_TAC THEN + MP_TAC(ISPECL [`u:complex->complex`; + `cball(Cx(&0),R)`] COMPACT_CONTINUOUS_IMAGE) THEN + ANTS_TAC THENL + [CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN ASM_REWRITE_TAC[SUBSET_UNIV]; + REWRITE_TAC[COMPACT_CBALL]]; ALL_TAC] THEN + DISCH_THEN(MP_TAC o MATCH_MP COMPACT_IMP_BOUNDED) THEN + REWRITE_TAC[BOUNDED_POS; FORALL_IN_IMAGE] THEN + DISCH_THEN(X_CHOOSE_THEN `M:real` STRIP_ASSUME_TAC) THEN + EXISTS_TAC `M:real` THEN CONJ_TAC THENL + [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + X_GEN_TAC `y:complex` THEN + ASM_CASES_TAC `(y:complex) IN cball(Cx(&0),R)` THENL + [ASM_SIMP_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN `~(y IN support (+) (u:complex->complex) (:complex))` + MP_TAC THENL [ASM_MESON_TAC[SUBSET]; ALL_TAC] THEN + REWRITE_TAC[support; IN_ELIM_THM; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + DISCH_TAC THEN ASM_REWRITE_TAC[NORM_0] THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + (* inv absolutely integrable on cball(0,R+S) *) + SUBGOAL_THEN + `!w:complex. (\z. inv(z - w)) + absolutely_integrable_on cball(Cx(&0),R + S)` + ASSUME_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC INV_ABSOLUTELY_INTEGRABLE_BOUNDED THEN + REWRITE_TAC[BOUNDED_CBALL; MEASURABLE_CBALL]; ALL_TAC] THEN + (* The constant bound *) + ABBREV_TAC + `C = M * drop(integral (cball(Cx(&0),R + S)) + (\z:complex. lift(norm(inv z))))` THEN + EXISTS_TAC `C:real` THEN CONJ_TAC THENL + [(* C >= 0 *) + EXPAND_TAC "C" THEN MATCH_MP_TAC REAL_LE_MUL THEN + ASM_REWRITE_TAC[] THEN MATCH_MP_TAC INTEGRAL_DROP_POS THEN CONJ_TAC THENL + [FIRST_X_ASSUM(MP_TAC o SPEC `Cx(&0)`) THEN + REWRITE_TAC[COMPLEX_SUB_RZERO] THEN + DISCH_THEN(fun th -> + ACCEPT_TAC(MATCH_MP ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE + (CONV_RULE(ONCE_DEPTH_CONV BETA_CONV) + (MATCH_MP ABSOLUTELY_INTEGRABLE_NORM th)))); + REWRITE_TAC[LIFT_DROP; NORM_POS_LE]]; ALL_TAC] THEN + (* For each w in K, the integral bound holds *) + X_GEN_TAC `w:complex` THEN DISCH_TAC THEN + (* Restrict integral to cball(0,R) since u = 0 outside *) + SUBGOAL_THEN + `integral (:complex) (\y. lift(norm((u:complex->complex) y / (y - w)))) = + integral (cball(Cx(&0),R)) (\y:complex. lift(norm(u y / (y - w))))` + SUBST1_TAC THENL + [CONV_TAC SYM_CONV THEN GEN_REWRITE_TAC LAND_CONV [GSYM INTEGRAL_RESTRICT_UNIV] THEN + MATCH_MP_TAC INTEGRAL_EQ THEN X_GEN_TAC `y:complex` THEN + REWRITE_TAC[IN_UNIV] THEN COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN `(u:complex->complex) y = vec 0` SUBST1_TAC THENL + [SUBGOAL_THEN + `~(y IN support (+) (u:complex->complex) (:complex))` MP_TAC THENL + [ASM_MESON_TAC[SUBSET]; ALL_TAC] THEN + REWRITE_TAC[support; IN_ELIM_THM; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + SIMP_TAC[]; + REWRITE_TAC[COMPLEX_VEC_0; complex_div; COMPLEX_MUL_LZERO; + COMPLEX_NORM_CX; REAL_ABS_NUM; NORM_0; LIFT_NUM]]; ALL_TAC] THEN + (* Bound: norm(u y / (y-w)) <= M * norm(inv(y-w)) *) + SUBGOAL_THEN + `drop(integral (cball(Cx(&0),R)) + (\y:complex. lift(norm((u:complex->complex) y / (y - w))))) <= + M * drop(integral (cball(Cx(&0),R)) + (\y:complex. lift(norm(inv(y - w)))))` + (fun th -> MATCH_MP_TAC REAL_LE_TRANS THEN EXISTS_TAC + `M * drop(integral (cball(Cx(&0),R)) + (\y:complex. lift(norm(inv(y - w:complex)))))` THEN + CONJ_TAC THENL [ACCEPT_TAC th; ALL_TAC]) THENL + [MATCH_MP_TAC REAL_LE_TRANS THEN + EXISTS_TAC `drop(integral (cball(Cx(&0),R)) + (\y:complex. lift(M * norm(inv(y - w:complex)))))` THEN + CONJ_TAC THENL + [MATCH_MP_TAC INTEGRAL_DROP_LE THEN REPEAT CONJ_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + ASM_REWRITE_TAC[SUBSET_UNIV] THEN CONJ_TAC THENL + [MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC BOUNDED_SUBSET THEN + EXISTS_TAC `cball(Cx(&0),R)` THEN + ASM_REWRITE_TAC[BOUNDED_CBALL]; + MATCH_MP_TAC MEASURABLE_IMP_LEBESGUE_MEASURABLE THEN + REWRITE_TAC[MEASURABLE_CBALL]]; + REWRITE_TAC[LIFT_CMUL] THEN + MATCH_MP_TAC INTEGRABLE_CMUL THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC INV_ABSOLUTELY_INTEGRABLE_BOUNDED THEN + REWRITE_TAC[BOUNDED_CBALL; MEASURABLE_CBALL]; + X_GEN_TAC `y:complex` THEN DISCH_TAC THEN + REWRITE_TAC[LIFT_DROP] THEN + REWRITE_TAC[complex_div; COMPLEX_NORM_MUL] THEN + MATCH_MP_TAC REAL_LE_RMUL THEN + ASM_REWRITE_TAC[NORM_POS_LE]]; + REWRITE_TAC[LIFT_CMUL] THEN + SUBGOAL_THEN + `(\y:complex. lift(norm(inv(y - w)))) integrable_on cball(Cx(&0),R)` + (fun th -> SIMP_TAC[CONV_RULE(ONCE_DEPTH_CONV BETA_CONV) + (MATCH_MP INTEGRAL_CMUL th); DROP_CMUL; + REAL_LE_REFL]) THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC INV_ABSOLUTELY_INTEGRABLE_BOUNDED THEN + REWRITE_TAC[BOUNDED_CBALL; MEASURABLE_CBALL]]; ALL_TAC] THEN + (* Subset: cball(0,R) SUBSET cball(w,R+S) since w IN cball(0,S) *) + SUBGOAL_THEN `cball(Cx(&0),R) SUBSET cball(w:complex,R + S)` + ASSUME_TAC THENL + [REWRITE_TAC[SUBSET] THEN X_GEN_TAC `y:complex` THEN + DISCH_TAC THEN + SUBGOAL_THEN `w:complex IN cball(Cx(&0),S)` MP_TAC THENL + [ASM_MESON_TAC[SUBSET]; ALL_TAC] THEN + REWRITE_TAC[IN_CBALL; dist; COMPLEX_SUB_LZERO; NORM_NEG] THEN + UNDISCH_TAC `y:complex IN cball(Cx(&0),R)` THEN + REWRITE_TAC[IN_CBALL; dist; COMPLEX_SUB_LZERO; NORM_NEG] THEN + REPEAT STRIP_TAC THEN + MP_TAC(ISPECL [`w:complex`; `Cx(&0)`; `y:complex`] DIST_TRIANGLE) THEN + REWRITE_TAC[dist; COMPLEX_SUB_RZERO; COMPLEX_SUB_LZERO; NORM_NEG] THEN + ASM_REAL_ARITH_TAC; ALL_TAC] THEN + (* Monotonicity of integral over subset for nonneg functions *) + SUBGOAL_THEN + `M * drop(integral (cball(Cx(&0),R)) + (\y:complex. lift(norm(inv(y - w))))) <= + M * drop(integral (cball(w:complex,R + S)) + (\y. lift(norm(inv(y - w)))))` + (fun th -> MATCH_MP_TAC REAL_LE_TRANS THEN EXISTS_TAC + `M * drop(integral (cball(w:complex,R + S)) + (\y. lift(norm(inv(y - w)))))` THEN + CONJ_TAC THENL [ACCEPT_TAC th; ALL_TAC]) THENL + [MATCH_MP_TAC REAL_LE_LMUL THEN ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC INTEGRAL_SUBSET_DROP_LE THEN + ASM_REWRITE_TAC[LIFT_DROP; NORM_POS_LE] THEN + CONJ_TAC THEN MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC INV_ABSOLUTELY_INTEGRABLE_BOUNDED THENL + [REWRITE_TAC[BOUNDED_CBALL; MEASURABLE_CBALL]; + REWRITE_TAC[BOUNDED_CBALL; MEASURABLE_CBALL]]; ALL_TAC] THEN + (* Translation: integral over cball(w,R+S) = integral over cball(0,R+S) *) + SUBGOAL_THEN + `M * drop(integral (cball(w:complex,R + S)) + (\y. lift(norm(inv(y - w))))) = C` + (fun th -> ASM_REWRITE_TAC[th; REAL_LE_REFL]) THEN + EXPAND_TAC "C" THEN AP_TERM_TAC THEN AP_TERM_TAC THEN + SUBGOAL_THEN + `cball(w:complex,R + S) = IMAGE (\z. w + z) (cball(Cx(&0),R + S))` + SUBST1_TAC THENL + [REWRITE_TAC[GSYM CBALL_TRANSLATION; COMPLEX_ADD_RID]; ALL_TAC] THEN + REWRITE_TAC[GSYM INTEGRAL_TRANSLATION] THEN + MATCH_MP_TAC INTEGRAL_EQ THEN X_GEN_TAC `z:complex` THEN + DISCH_TAC THEN BETA_TAC THEN AP_TERM_TAC THEN AP_TERM_TAC THEN + AP_TERM_TAC THEN SIMPLE_COMPLEX_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Measurability of the weighted Cauchy kernel norm integral on [0,1]. *) +(* Uses dominated convergence to show continuity and convergence of *) +(* truncated integrals, then MEASURABLE_ON_LIMIT + MEASURABLE_ON_DROP_MUL. *) +(* ------------------------------------------------------------------------- *) + +let SUPPORT_BOUNDED_OUTSIDE = prove + (`!(u:real^M->real^N) R y. + (!z. z IN support (+) u (:real^M) ==> norm z <= R) /\ + norm(y) > R ==> u y = vec 0`, + REPEAT GEN_TAC THEN STRIP_TAC THEN + ASM_CASES_TAC `(u:real^M->real^N) y = vec 0` THEN ASM_REWRITE_TAC[] THEN + FIRST_X_ASSUM(MP_TAC o SPEC `y:real^M`) THEN + REWRITE_TAC[IN_SUPPORT; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + ASM_REWRITE_TAC[] THEN ASM_REAL_ARITH_TAC);; + +let CAUCHY_KERNEL_NORM_TRUNC_INTEGRABLE = prove + (`!u:complex->complex w:complex n:num. + u continuous_on (:complex) /\ bounded(support (+) u (:complex)) + ==> (\y. lift(real_min (norm(u y / (y - w))) (&n))) + integrable_on (:complex)`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC MEASURABLE_BOUNDED_BY_INTEGRABLE_IMP_INTEGRABLE THEN + EXISTS_TAC `(\y:complex. lift(norm(u y / (y - w))))` THEN REPEAT CONJ_TAC THENL + [SUBGOAL_THEN `(\y:complex. lift(norm(u y / (y - w)))) measurable_on (:complex)` ASSUME_TAC THENL + [MATCH_MP_TAC INTEGRABLE_IMP_MEASURABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_LIFT_NORM_INTEGRABLE THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN ASM_REWRITE_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN `(\y:complex. lift(real_min (norm(u y / (y - w))) (&n))) = + (\y. (lambda i. min (lift(norm(u y / (y - w)))$i) (lift(&n)$i)):real^1)` SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `y:complex` THEN + SIMP_TAC[CART_EQ; LAMBDA_BETA; DIMINDEX_1; FORALL_1] THEN + REWRITE_TAC[GSYM drop; LIFT_DROP; real_min]; ALL_TAC] THEN + MATCH_MP_TAC MEASURABLE_ON_MIN THEN ASM_REWRITE_TAC[MEASURABLE_ON_CONST]; + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_LIFT_NORM_INTEGRABLE THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN ASM_REWRITE_TAC[]; + X_GEN_TAC `y:complex` THEN REWRITE_TAC[IN_UNIV; NORM_LIFT; LIFT_DROP; real_min] THEN + COND_CASES_TAC THEN REWRITE_TAC[REAL_ABS_NORM; REAL_LE_REFL] THEN ASM_REAL_ARITH_TAC]);; + +let CAUCHY_KERNEL_NORM_TRUNC_CONTINUOUS = prove + (`!u:complex->complex n:num. + u continuous_on (:complex) /\ bounded(support (+) u (:complex)) + ==> (\w. integral (:complex) + (\y. lift(real_min (norm(u y / (y - w))) (&n)))) + continuous_on (:complex)`, + REPEAT STRIP_TAC THEN + REWRITE_TAC[CONTINUOUS_ON_SEQUENTIALLY; IN_UNIV; o_DEF] THEN + MAP_EVERY X_GEN_TAC [`w:num->complex`; `w0:complex`] THEN + DISCH_TAC THEN + FIRST_ASSUM(MP_TAC o GEN_REWRITE_RULE I [BOUNDED_POS]) THEN + DISCH_THEN(X_CHOOSE_THEN `R:real` STRIP_ASSUME_TAC) THEN + SUBGOAL_THEN + `!k:num. (\y:complex. lift(real_min (norm(u y / (y - (w:num->complex) k))) (&n))) + integrable_on (:complex)` ASSUME_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC CAUCHY_KERNEL_NORM_TRUNC_INTEGRABLE THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN + `(\y:complex. if y IN cball(vec 0, R) then lift(&n) else vec 0) + integrable_on (:complex)` ASSUME_TAC THENL + [MATCH_MP_TAC INTEGRABLE_ON_SUPERSET THEN + EXISTS_TAC `cball(vec 0:complex, R)` THEN REPEAT CONJ_TAC THENL + [X_GEN_TAC `y:complex` THEN + REWRITE_TAC[] THEN DISCH_TAC THEN ASM_REWRITE_TAC[]; + REWRITE_TAC[SUBSET_UNIV]; + MATCH_MP_TAC INTEGRABLE_EQ THEN + EXISTS_TAC `(\y:complex. lift(&n))` THEN CONJ_TAC THENL + [X_GEN_TAC `y:complex` THEN DISCH_TAC THEN + BETA_TAC THEN ASM_REWRITE_TAC[]; + REWRITE_TAC[INTEGRABLE_ON_CONST; MEASURABLE_CBALL]]]; + ALL_TAC] THEN + SUBGOAL_THEN + `!k:num y:complex. ~(y = w0) + ==> norm(lift(real_min (norm(u y / (y - (w:num->complex) k))) (&n))) <= + drop(if y IN cball(vec 0:complex, R) then lift(&n) else vec 0)` + ASSUME_TAC THENL + [REPEAT GEN_TAC THEN DISCH_TAC THEN REWRITE_TAC[NORM_LIFT] THEN + ASM_CASES_TAC `(y:complex) IN cball(vec 0:complex, R)` THENL + [ASM_REWRITE_TAC[LIFT_DROP] THEN + REWRITE_TAC[real_min] THEN COND_CASES_TAC THEN + REWRITE_TAC[REAL_ABS_NORM] THEN ASM_REAL_ARITH_TAC; + ASM_REWRITE_TAC[DROP_VEC] THEN + SUBGOAL_THEN `(u:complex->complex) y = vec 0` SUBST1_TAC THENL + [MP_TAC(ISPECL [`u:complex->complex`; `R:real`; `y:complex`] + SUPPORT_BOUNDED_OUTSIDE) THEN + ASM_REWRITE_TAC[] THEN DISCH_THEN MATCH_MP_TAC THEN + POP_ASSUM MP_TAC THEN REWRITE_TAC[IN_CBALL_0] THEN ASM_REAL_ARITH_TAC; + REWRITE_TAC[complex_div; COMPLEX_VEC_0; COMPLEX_MUL_LZERO; + COMPLEX_NORM_CX; REAL_ABS_NUM; real_min] THEN + REAL_ARITH_TAC]]; + ALL_TAC] THEN + SUBGOAL_THEN + `!y:complex. ~(y = w0:complex) + ==> ((\k:num. lift(real_min (norm((u:complex->complex) y / (y - (w:num->complex) k))) (&(n:num)))) + --> lift(real_min (norm(u y / (y - w0))) (&n))) sequentially` + ASSUME_TAC THENL + [X_GEN_TAC `y:complex` THEN DISCH_TAC THEN + SUBGOAL_THEN + `!z:complex. lift(real_min (norm((u:complex->complex) (y:complex) / (y - z))) (&(n:num))) = + (lambda i. min (lift(norm(u y / (y - z)))$i) (lift(&n)$i)):real^1` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN + SIMP_TAC[CART_EQ; LAMBDA_BETA; DIMINDEX_1; FORALL_1] THEN + REWRITE_TAC[GSYM drop; LIFT_DROP; real_min]; ALL_TAC] THEN + MATCH_MP_TAC LIM_MIN THEN CONJ_TAC THENL + [MATCH_MP_TAC LIM_NORM THEN + REWRITE_TAC[complex_div] THEN + MATCH_MP_TAC LIM_COMPLEX_MUL THEN + CONJ_TAC THENL [REWRITE_TAC[LIM_CONST]; ALL_TAC] THEN + MATCH_MP_TAC LIM_COMPLEX_INV THEN CONJ_TAC THENL + [MATCH_MP_TAC LIM_SUB THEN + REWRITE_TAC[LIM_CONST] THEN ASM_REWRITE_TAC[]; + REWRITE_TAC[COMPLEX_SUB_0] THEN ASM_MESON_TAC[]]; + REWRITE_TAC[LIM_CONST]]; + ALL_TAC] THEN + MP_TAC(ISPECL + [`\k:num. \y:complex. + lift(real_min (norm((u:complex->complex) y / (y - (w:num->complex) k))) (&(n:num)))`; + `\y:complex. + lift(real_min (norm((u:complex->complex) y / (y - w0:complex))) (&(n:num)))`; + `\y:complex. + if y IN cball(vec 0:complex, R:real) then lift(&(n:num)) else vec 0`; + `(:complex)`; + `{w0:complex}`] + DOMINATED_CONVERGENCE_AE) THEN + CONV_TAC(LAND_CONV(DEPTH_CONV BETA_CONV)) THEN + REWRITE_TAC[IN_UNIV; IN_DIFF; IN_SING] THEN + DISCH_THEN(fun th -> ASM_REWRITE_TAC[] THEN + MP_TAC th THEN ASM_REWRITE_TAC[NEGLIGIBLE_SING]) THEN + DISCH_THEN(fun th -> ACCEPT_TAC(CONJUNCT2 th)));; + +let CAUCHY_KERNEL_NORM_TRUNC_CONVERGES = prove + (`!u:complex->complex w:complex. + u continuous_on (:complex) /\ bounded(support (+) u (:complex)) + ==> ((\n. integral (:complex) + (\y. lift(real_min (norm(u y / (y - w))) (&n)))) + --> integral (:complex) + (\y. lift(norm(u y / (y - w))))) sequentially`, + REPEAT STRIP_TAC THEN + MP_TAC(ISPECL + [`\k:num. \y:complex. + lift(real_min (norm((u:complex->complex) y / (y - w:complex))) (&k))`; + `\y:complex. + lift(norm((u:complex->complex) y / (y - w:complex)))`; + `\y:complex. + lift(norm((u:complex->complex) y / (y - w:complex)))`; + `(:complex)`; + `{}:complex->bool`] + DOMINATED_CONVERGENCE_AE) THEN + CONV_TAC(LAND_CONV(DEPTH_CONV BETA_CONV)) THEN + REWRITE_TAC[IN_UNIV; IN_DIFF; NOT_IN_EMPTY; NEGLIGIBLE_EMPTY] THEN + ANTS_TAC THENL + [REPEAT CONJ_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC CAUCHY_KERNEL_NORM_TRUNC_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_LIFT_NORM_INTEGRABLE THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + REPEAT GEN_TAC THEN + REWRITE_TAC[NORM_LIFT; LIFT_DROP; real_min] THEN + COND_CASES_TAC THEN REWRITE_TAC[REAL_ABS_NORM; REAL_LE_REFL] THEN + ASM_REAL_ARITH_TAC; + X_GEN_TAC `y:complex` THEN + MATCH_MP_TAC LIM_EVENTUALLY THEN + REWRITE_TAC[EVENTUALLY_SEQUENTIALLY] THEN + MP_TAC(SPEC `norm((u:complex->complex) y / (y - w:complex))` REAL_ARCH_SIMPLE) THEN + DISCH_THEN(X_CHOOSE_TAC `N:num`) THEN + EXISTS_TAC `N:num` THEN + X_GEN_TAC `k:num` THEN DISCH_TAC THEN + AP_TERM_TAC THEN + REWRITE_TAC[real_min] THEN + COND_CASES_TAC THEN REWRITE_TAC[] THEN + UNDISCH_TAC `~(norm((u:complex->complex) y / (y - w)) <= &k)` THEN + REWRITE_TAC[REAL_NOT_LE] THEN DISCH_TAC THEN + UNDISCH_TAC `N:num <= k` THEN + REWRITE_TAC[GSYM REAL_OF_NUM_LE] THEN + ASM_REAL_ARITH_TAC]; + STRIP_TAC THEN ASM_REWRITE_TAC[]]);; + +let CAUCHY_KERNEL_WEIGHTED_MEASURABLE = prove + (`!u:complex->complex g:real^1->complex. + u continuous_on (:complex) /\ bounded(support (+) u (:complex)) /\ + g absolutely_continuous_on interval[vec 0,vec 1] + ==> (\x. norm(vector_derivative g (at x)) % + integral (:complex) (\y. lift(norm(u y / (y - g x))))) + measurable_on interval[vec 0, vec 1]`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\x. norm(vector_derivative (g:real^1->complex) (at x)) % + integral (:complex) (\y. lift(norm((u:complex->complex) y / (y - g x))))) = + (\x. drop((\x. lift(norm(vector_derivative g (at x)))) x) % + (\x. integral (:complex) (\y. lift(norm(u y / (y - g x))))) x)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC MEASURABLE_ON_DROP_MUL THEN CONJ_TAC THENL + [MATCH_MP_TAC MEASURABLE_ON_NORM THEN + MATCH_MP_TAC INTEGRABLE_IMP_MEASURABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS THEN + ASM_REWRITE_TAC[]; + MATCH_MP_TAC(ISPECL + [`\n:num. \x:real^1. integral (:complex) + (\y:complex. lift(real_min (norm((u:complex->complex) y / (y - (g:real^1->complex) x))) (&n)))`; + `\x:real^1. integral (:complex) + (\y:complex. lift(norm((u:complex->complex) y / (y - (g:real^1->complex) x))))`; + `interval[vec 0:real^1, vec 1]`; + `{}:real^1->bool`] + MEASURABLE_ON_LIMIT) THEN + CONV_TAC(LAND_CONV(DEPTH_CONV BETA_CONV)) THEN + REWRITE_TAC[NEGLIGIBLE_EMPTY; IN_DIFF; NOT_IN_EMPTY; IN_UNIV] THEN + CONJ_TAC THENL + [X_GEN_TAC `nn:num` THEN + MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON_CLOSED_SUBSET THEN + REWRITE_TAC[CLOSED_INTERVAL] THEN + SUBGOAL_THEN + `(\x:real^1. integral (:complex) + (\y:complex. lift(real_min (norm((u:complex->complex) y / (y - (g:real^1->complex) x))) (&nn)))) = + (\w. integral (:complex) + (\y. lift(real_min (norm(u y / (y - w))) (&nn)))) o g` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH; path]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + MATCH_MP_TAC CAUCHY_KERNEL_NORM_TRUNC_CONTINUOUS THEN + ASM_REWRITE_TAC[]]; + X_GEN_TAC `x:real^1` THEN DISCH_TAC THEN + MP_TAC(ISPECL [`u:complex->complex`; `(g:real^1->complex) x`] + CAUCHY_KERNEL_NORM_TRUNC_CONVERGES) THEN + ASM_REWRITE_TAC[]]]);; + +(* ------------------------------------------------------------------------- *) +(* Fubini exchange: swap path integral and area integral. *) +(* Uses FUBINI_INTEGRAL_SWAP on the product space real^1 x complex. *) +(* LMUL integrability handled via INTEGRAL_SPIKE + NEGLIGIBLE_AC_PATH. *) +(* ------------------------------------------------------------------------- *) + +(* AC path image is negligible (measure zero in R^2) *) +let NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE = prove + (`!g:real^1->complex. + g absolutely_continuous_on interval[vec 0,vec 1] + ==> negligible(path_image g)`, + GEN_TAC THEN DISCH_TAC THEN + REWRITE_TAC[path_image] THEN + MATCH_MP_TAC NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_IMAGE_LOWDIM THEN + ASM_REWRITE_TAC[IS_INTERVAL_INTERVAL] THEN + REWRITE_TAC[DIMINDEX_2] THEN ARITH_TAC);; + +let PATH_INTEGRABLE_CONTINUOUS_ABSOLUTELY_CONTINUOUS = prove + (`!f:complex->complex g:real^1->complex. + f continuous_on path_image g /\ + g absolutely_continuous_on interval[vec 0,vec 1] + ==> f path_integrable_on g`, + REPEAT STRIP_TAC THEN + REWRITE_TAC[path_integrable_on; HAS_PATH_INTEGRAL; GSYM integrable_on] THEN + SUBGOAL_THEN + `g:real^1->complex has_bounded_variation_on interval[vec 0,vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]; ALL_TAC] THEN + FIRST_ASSUM(MP_TAC o MATCH_MP + ABSOLUTELY_INTEGRABLE_BOUNDED_VARIATION_DERIVATIVE) THEN + REWRITE_TAC[LEFT_IMP_EXISTS_THM] THEN + MAP_EVERY X_GEN_TAC [`gd:real^1->complex`; `s:real^1->bool`] THEN + STRIP_TAC THEN + SUBGOAL_THEN `path(g:real^1->complex)` ASSUME_TAC THENL + [REWRITE_TAC[path] THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_ON_IMP_CONTINUOUS; + IS_INTERVAL_INTERVAL]; ALL_TAC] THEN + SUBGOAL_THEN + `(\t. (f:complex->complex)((g:real^1->complex) t)) + continuous_on interval[vec 0,vec 1]` + ASSUME_TAC THENL + [REWRITE_TAC[GSYM o_DEF] THEN MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN + CONJ_TAC THENL [ASM_MESON_TAC[path]; ALL_TAC] THEN + ASM_MESON_TAC[CONTINUOUS_ON_SUBSET; path_image; IMAGE_o; SUBSET_REFL]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\t. (f:complex->complex)((g:real^1->complex) t) * (gd:real^1->complex) t) + integrable_on interval[vec 0,vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC(ISPEC `( * ):complex->complex->complex` + ABSOLUTELY_INTEGRABLE_BOUNDED_MEASURABLE_PRODUCT) THEN + ASM_REWRITE_TAC[BILINEAR_COMPLEX_MUL] THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON_CLOSED_SUBSET THEN + ASM_REWRITE_TAC[CLOSED_INTERVAL]; + MATCH_MP_TAC COMPACT_IMP_BOUNDED THEN + MATCH_MP_TAC COMPACT_CONTINUOUS_IMAGE THEN + ASM_REWRITE_TAC[COMPACT_INTERVAL]]; ALL_TAC] THEN + MP_TAC(ISPECL + [`\t. (f:complex->complex)((g:real^1->complex) t) * + (gd:real^1->complex) t`; + `\t. (f:complex->complex)((g:real^1->complex) t) * + vector_derivative g (at t)`; + `s:real^1->bool`; + `interval[vec 0:real^1,vec 1]`] + INTEGRABLE_SPIKE) THEN + CONV_TAC(LAND_CONV(DEPTH_CONV BETA_CONV)) THEN + REWRITE_TAC[IMP_IMP] THEN DISCH_THEN MATCH_MP_TAC THEN + ASM_REWRITE_TAC[] THEN + X_GEN_TAC `t:real^1` THEN REWRITE_TAC[IN_DIFF] THEN STRIP_TAC THEN + AP_TERM_TAC THEN MATCH_MP_TAC VECTOR_DERIVATIVE_AT THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_DIFF]);; + +let FUBINI_PATH_AREA = prove + (`!u:complex->complex g. + u continuous_on (:complex) /\ + bounded (support (+) u (:complex)) /\ + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g + ==> path_integral g (\w. integral (:complex) + (\z. u(z) / (z - w))) = + integral (:complex) + (\z. u(z) * path_integral g (\w. Cx(&1) / (z - w)))`, + REPEAT STRIP_TAC THEN REWRITE_TAC[PATH_INTEGRAL_INTEGRAL] THEN + (* Cauchy kernel integrability *) + SUBGOAL_THEN + `!w:complex. (\z. u(z:complex) / (z - w)) + absolutely_integrable_on (:complex)` + ASSUME_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN + `!w:complex. (\z. u(z:complex) / (z - w)) integrable_on (:complex)` + ASSUME_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + (* Abs integrability on product: use FUBINI_TONELLI decomposition *) + SUBGOAL_THEN + `(\p:real^(1,2)finite_sum. + if fstcart p IN interval[vec 0,vec 1] + then u(sndcart p:complex) / (sndcart p - g(fstcart p)) * + vector_derivative g (at (fstcart p)) + else vec 0) + absolutely_integrable_on (:real^(1,2)finite_sum)` + ASSUME_TAC THENL + [(* Measurability on product space *) + SUBGOAL_THEN + `(\p:real^(1,2)finite_sum. + if fstcart p IN interval[vec 0,vec 1] + then u(sndcart p:complex) / (sndcart p - g(fstcart p)) * + vector_derivative g (at (fstcart p)) + else vec 0) + measurable_on (:real^(1,2)finite_sum)` + MP_TAC THENL + [MATCH_MP_TAC MEASURABLE_ON_FUBINI_INTEGRAND THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + DISCH_THEN(MP_TAC o MATCH_MP FUBINI_TONELLI) THEN + DISCH_THEN(fun th -> REWRITE_TAC[th]) THEN + REWRITE_TAC[FSTCART_PASTECART; SNDCART_PASTECART] THEN + CONJ_TAC THENL + [(* Negligible bad set: every slice is abs integrable *) + SUBGOAL_THEN + `{x:real^1 | ~((\y:complex. + if x IN interval[vec 0,vec 1] + then u y / (y - g x) * vector_derivative g (at x) + else vec 0) + absolutely_integrable_on (:complex))} = {}` + SUBST1_TAC THENL + [REWRITE_TAC[EXTENSION; IN_ELIM_THM; NOT_IN_EMPTY] THEN + X_GEN_TAC `t:real^1` THEN + REWRITE_TAC[TAUT `~(~p) <=> p`] THEN + ASM_CASES_TAC `t:real^1 IN interval[vec 0,vec 1]` THENL + [ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_COMPLEX_RMUL THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[absolutely_integrable_on; + NORM_0; LIFT_NUM; INTEGRABLE_0]]; + REWRITE_TAC[NEGLIGIBLE_EMPTY]]; + (* Norm integral integrability: Tonelli condition *) + (* Simplify: for x not in [0,1], integrand is 0. For x in [0,1], *) + (* factor out norm(vd_g(x)) and bound the Cauchy kernel norm. *) + SUBGOAL_THEN + `(\x:real^1. integral (:complex) + (\y. lift(norm(if x IN interval[vec 0,vec 1] + then u y / (y - g x) * vector_derivative g (at x) + else vec 0)))) = + (\x. if x IN interval[vec 0,vec 1] + then norm(vector_derivative g (at x)) % + integral (:complex) + (\y. lift(norm(u y / (y - (g:real^1->complex) x)))) + else vec 0)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `x:real^1` THEN + COND_CASES_TAC THENL + [ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN + `(\y:complex. lift(norm(u y / (y - g(x:real^1)) * + vector_derivative g (at x)))) = + (\y. norm(vector_derivative g (at x)) % + lift(norm(u y / (y - g x))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + REWRITE_TAC[COMPLEX_NORM_MUL] THEN + ONCE_REWRITE_TAC[REAL_MUL_SYM] THEN + REWRITE_TAC[LIFT_CMUL]; ALL_TAC] THEN + MATCH_MP_TAC INTEGRAL_CMUL THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[NORM_0; LIFT_NUM; INTEGRAL_0]]; ALL_TAC] THEN + (* Now use INTEGRABLE_RESTRICT_UNIV to work on [0,1] *) + REWRITE_TAC[INTEGRABLE_RESTRICT_UNIV] THEN + (* Get the uniform bound on the Cauchy kernel norm *) + MP_TAC(ISPECL [`u:complex->complex`; + `path_image(g:real^1->complex)`] CAUCHY_KERNEL_NORM_UNIFORM_BOUND) THEN + ANTS_TAC THENL + [ASM_REWRITE_TAC[] THEN MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `C:real` STRIP_ASSUME_TAC) THEN + (* Bound: for x in [0,1], the norm integral <= C * norm(vd_g(x)) *) + MATCH_MP_TAC MEASURABLE_BOUNDED_BY_INTEGRABLE_IMP_INTEGRABLE THEN + EXISTS_TAC `(\x:real^1. C % lift(norm( + vector_derivative (g:real^1->complex) (at x))))` THEN + REPEAT CONJ_TAC THENL + [MATCH_MP_TAC CAUCHY_KERNEL_WEIGHTED_MEASURABLE THEN + ASM_REWRITE_TAC[]; + (* Dominating function integrable on [0,1]: + AC implies abs integrability of vector_derivative *) + MATCH_MP_TAC INTEGRABLE_CMUL THEN + MP_TAC(ISPEC `g:real^1->complex` + ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS) THEN + ASM_REWRITE_TAC[] THEN + REWRITE_TAC[absolutely_integrable_on] THEN + STRIP_TAC THEN ASM_REWRITE_TAC[]; + (* Pointwise bound: BETA_TAC for beta-redexes from EXISTS_TAC, + swap RHS factors so REAL_LE_LMUL gives the right subgoal *) + BETA_TAC THEN + X_GEN_TAC `x:real^1` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN DISCH_TAC THEN + REWRITE_TAC[NORM_MUL; real_abs; NORM_POS_LE] THEN + REWRITE_TAC[DROP_CMUL; LIFT_DROP] THEN + GEN_REWRITE_TAC RAND_CONV [REAL_MUL_SYM] THEN + MATCH_MP_TAC REAL_LE_LMUL THEN + CONJ_TAC THENL + [REWRITE_TAC[NORM_POS_LE]; + REWRITE_TAC[NORM_REAL; GSYM drop] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x /\ x <= C ==> abs(x) <= C`) THEN + CONJ_TAC THENL + [MATCH_MP_TAC INTEGRAL_DROP_POS THEN CONJ_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + REWRITE_TAC[LIFT_DROP; NORM_POS_LE]]; + FIRST_X_ASSUM MATCH_MP_TAC THEN + REWRITE_TAC[path_image; IN_IMAGE] THEN + EXISTS_TAC `x:real^1` THEN + ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC]]]]]; + ALL_TAC] THEN + (* Apply Fubini *) + FIRST_X_ASSUM(MP_TAC o MATCH_MP FUBINI_INTEGRAL_SWAP) THEN + REWRITE_TAC[FSTCART_PASTECART; SNDCART_PASTECART] THEN + REWRITE_TAC[INTEGRAL_IF_CONST; INTEGRAL_RESTRICT_UNIV] THEN + DISCH_TAC THEN + (* Pull g'(x) inside on LHS using INTEGRAL_COMPLEX_RMUL *) + SUBGOAL_THEN + `!x:real^1. + integral (:complex) (\z:complex. u z / (z - g x)) * + vector_derivative g (at x) = + integral (:complex) + (\z. u z / (z - g x) * vector_derivative g (at x))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN CONV_TAC SYM_CONV THEN + MATCH_MP_TAC INTEGRAL_COMPLEX_RMUL THEN ASM_REWRITE_TAC[]; ALL_TAC] THEN + (* LHS now matches Fubini LHS. Substitute to reduce to RHS equality. *) + FIRST_X_ASSUM(fun th -> GEN_REWRITE_TAC LAND_CONV [th]) THEN + (* Use INTEGRAL_SPIKE: path_image g is negligible, so integrands need *) + (* only agree off the path. Off the path, LMUL applies. *) + MATCH_MP_TAC INTEGRAL_SPIKE THEN + EXISTS_TAC `path_image (g:real^1->complex)` THEN + CONJ_TAC THENL + [MATCH_MP_TAC NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + X_GEN_TAC `z:complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN + (* Algebra: u z / (z-w) * g'(x) = u z * (Cx(&1)/(z-w) * g'(x)) *) + SUBGOAL_THEN + `!x:real^1. u(z:complex) / (z - (g:real^1->complex) x) * + vector_derivative g (at x) = + u z * (Cx(&1) / (z - g x) * vector_derivative g (at x))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN REWRITE_TAC[complex_div; COMPLEX_MUL_LID; COMPLEX_MUL_ASSOC]; + ALL_TAC] THEN + CONV_TAC SYM_CONV THEN MATCH_MP_TAC INTEGRAL_COMPLEX_LMUL THEN + (* Integrability of (\t. Cx(&1)/(z-g t) * g'(t)) on [0,1] for z off path *) + SUBGOAL_THEN + `(\t:real^1. Cx(&1) / ((g:real^1->complex) t - z) * + vector_derivative g (at t)) + integrable_on interval[vec 0,vec 1]` + ASSUME_TAC THENL + [REWRITE_TAC[GSYM PATH_INTEGRABLE_ON] THEN + MATCH_MP_TAC PATH_INTEGRABLE_CONTINUOUS_ABSOLUTELY_CONTINUOUS THEN + ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_DIV THEN + REWRITE_TAC[CONTINUOUS_ON_CONST] THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUB THEN + REWRITE_TAC[CONTINUOUS_ON_CONST; CONTINUOUS_ON_ID]; + REWRITE_TAC[COMPLEX_SUB_0] THEN + X_GEN_TAC `w:complex` THEN DISCH_TAC THEN DISCH_TAC THEN + UNDISCH_TAC `~(z IN path_image(g:real^1->complex))` THEN + REWRITE_TAC[] THEN ASM_MESON_TAC[]]; + ALL_TAC] THEN + (* Rewrite Cx(&1)/(z-w) as --(Cx(&1)/(w-z)), then use INTEGRABLE_NEG *) + SUBGOAL_THEN + `(\t:real^1. Cx(&1) / (z - (g:real^1->complex) t) * + vector_derivative g (at t)) = + (\t. --(Cx(&1) / (g t - z) * vector_derivative g (at t)))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; complex_div] THEN X_GEN_TAC `t:real^1` THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) [GSYM COMPLEX_NEG_SUB] THEN + REWRITE_TAC[COMPLEX_INV_NEG; COMPLEX_MUL_RNEG; COMPLEX_MUL_LNEG]; + ALL_TAC] THEN + MATCH_MP_TAC INTEGRABLE_NEG THEN ASM_REWRITE_TAC[]);; + +(* ------------------------------------------------------------------------- *) +(* Absolute integrability of the translated dbar kernel. *) +(* ------------------------------------------------------------------------- *) + +let ABSOLUTELY_INTEGRABLE_DBAR_KERNEL = prove + (`!f:complex->complex w. + (\z. wirtinger_dbar f z) continuous_on (:complex) /\ + bounded (support (+) (wirtinger_dbar f) (:complex)) + ==> (\z. wirtinger_dbar f (w + z) / z) absolutely_integrable_on + (:complex)`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\z:complex. wirtinger_dbar (f:complex->complex) (w + z) / z) = + (\z. wirtinger_dbar f (w + z) / (z - Cx(&0)))` SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; COMPLEX_SUB_RZERO]; ALL_TAC] THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_TRANSLATE_UNIV THEN + RULE_ASSUM_TAC(REWRITE_RULE[ETA_AX]) THEN ASM_REWRITE_TAC[]; + MATCH_MP_TAC BOUNDED_SUPPORT_TRANSLATE THEN ASM_REWRITE_TAC[]]);; + +(* ------------------------------------------------------------------------- *) +(* Angular / polar path lemmas for polar coordinates proof. *) +(* ------------------------------------------------------------------------- *) + +(* Derivative chain: t -> Cx(drop t) *) +let HAS_DERIVATIVE_CX_DROP = prove + (`!t:real^1. ((\t. Cx(drop t)) has_derivative (\h. Cx(drop h))) (at t)`, + GEN_TAC THEN MATCH_MP_TAC HAS_DERIVATIVE_LINEAR THEN + REWRITE_TAC[linear; DROP_ADD; DROP_CMUL; CX_ADD; CX_MUL; COMPLEX_CMUL]);; + +(* Derivative chain: t -> ii * Cx(drop t) *) +let HAS_DERIVATIVE_II_CX_DROP = prove + (`!t:real^1. ((\t. ii * Cx(drop t)) has_derivative (\h. ii * Cx(drop h))) + (at t)`, + GEN_TAC THEN + SUBGOAL_THEN `(\t:real^1. ii * Cx(drop t)) = + ((\z:complex. ii * z) o (\t:real^1. Cx(drop t)))` SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN `(\h:real^1. ii * Cx(drop h)) = + ((\z:complex. ii * z) o (\h:real^1. Cx(drop h)))` SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [MESON_TAC[HAS_DERIVATIVE_CX_DROP]; + MATCH_MP_TAC HAS_DERIVATIVE_LINEAR THEN + MATCH_ACCEPT_TAC LINEAR_COMPLEX_MUL]);; + +(* Derivative chain: t -> cexp(ii * Cx(drop t)) *) +let HAS_DERIVATIVE_CEXP_POLAR = prove + (`!t:real^1. ((\t. cexp(ii * Cx(drop t))) has_derivative + (\h. cexp(ii * Cx(drop t)) * (ii * Cx(drop h)))) (at t)`, + GEN_TAC THEN + SUBGOAL_THEN `(\t:real^1. cexp(ii * Cx(drop t))) = + (cexp o (\t:real^1. ii * Cx(drop t)))` SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN `(\h:real^1. cexp(ii * Cx(drop t)) * (ii * Cx(drop h))) = + ((\x:complex. cexp(ii * Cx(drop t)) * x) o (\h:real^1. ii * Cx(drop h)))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [MESON_TAC[HAS_DERIVATIVE_II_CX_DROP]; + MP_TAC(SPEC `ii * Cx(drop(t:real^1))` HAS_COMPLEX_DERIVATIVE_CEXP) THEN + REWRITE_TAC[has_complex_derivative]]);; + +(* Affine map z -> w + c*z has derivative h -> c*h *) +let HAS_DERIVATIVE_AFFINE_MUL = prove + (`!c:complex w:complex a:complex. + ((\z. w + c * z) has_derivative (\h. c * h)) (at a)`, + REPEAT GEN_TAC THEN + SUBGOAL_THEN + `((\z:complex. w) has_derivative (\h:complex. vec 0:complex)) (at a) /\ + ((\z:complex. c * z) has_derivative (\h:complex. c * h)) (at a)` MP_TAC THENL + [CONJ_TAC THENL + [REWRITE_TAC[HAS_DERIVATIVE_CONST]; + MATCH_MP_TAC HAS_DERIVATIVE_LINEAR THEN + MATCH_ACCEPT_TAC LINEAR_COMPLEX_MUL]; + DISCH_THEN(ACCEPT_TAC o REWRITE_RULE[VECTOR_ADD_LID] o + MATCH_MP HAS_DERIVATIVE_ADD)]);; + +(* Derivative of full polar path t -> w + Cx(r) * cexp(ii * Cx(drop t)) *) +let HAS_DERIVATIVE_POLAR_PATH = prove + (`!w:complex r t:real^1. + ((\t. w + Cx r * cexp(ii * Cx(drop t))) has_derivative + (\h. Cx r * (cexp(ii * Cx(drop t)) * (ii * Cx(drop h))))) (at t)`, + REPEAT GEN_TAC THEN + SUBGOAL_THEN `(\t:real^1. w + Cx r * cexp(ii * Cx(drop t))) = + ((\z:complex. w + Cx r * z) o (\t:real^1. cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h:real^1. Cx r * (cexp(ii * Cx(drop(t:real^1))) * (ii * Cx(drop h)))) = + ((\h:complex. Cx r * h) o + (\h:real^1. cexp(ii * Cx(drop t)) * (ii * Cx(drop h))))` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [MESON_TAC[HAS_DERIVATIVE_CEXP_POLAR]; + REWRITE_TAC[HAS_DERIVATIVE_AFFINE_MUL]]);; + + +(* Continuity helper: (\z. c * z) continuous on (:complex) *) +let CONTINUOUS_ON_COMPLEX_LMUL_FN = prove + (`!c:complex. (\z:complex. c * z) continuous_on (:complex)`, + GEN_TAC THEN MATCH_MP_TAC LINEAR_CONTINUOUS_ON THEN + MATCH_ACCEPT_TAC LINEAR_COMPLEX_MUL);; + +(* Continuity: t -> ii * Cx(drop t) on (:real^1) *) +let CONT_II_CX_DROP = prove + (`(\t:real^1. ii * Cx(drop t)) continuous_on (:real^1)`, + SUBGOAL_THEN + `((\z:complex. ii * z) o (\t:real^1. Cx(drop t))) continuous_on (:real^1)` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_CX_DROP THEN REWRITE_TAC[CONTINUOUS_ON_ID]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + REWRITE_TAC[SUBSET_UNIV; CONTINUOUS_ON_COMPLEX_LMUL_FN]]);; + +(* Continuity: t -> cexp(ii * Cx(drop t)) on (:real^1) *) +let CONT_CEXP_POLAR = prove + (`(\t:real^1. cexp(ii * Cx(drop t))) continuous_on (:real^1)`, + SUBGOAL_THEN + `(cexp o (\t:real^1. ii * Cx(drop t))) continuous_on (:real^1)` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [REWRITE_TAC[CONT_II_CX_DROP]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + REWRITE_TAC[SUBSET_UNIV; CONTINUOUS_ON_CEXP]]);; + +(* Continuity: full polar path on (:real^1) *) +let CONTINUOUS_ON_POLAR_PATH = prove + (`!w:complex r. + (\t. w + Cx r * cexp(ii * Cx(drop t))) continuous_on (:real^1)`, + REPEAT GEN_TAC THEN + SUBGOAL_THEN + `((\z:complex. w + Cx r * z) o (\t:real^1. cexp(ii * Cx(drop t)))) + continuous_on (:real^1)` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [REWRITE_TAC[CONT_CEXP_POLAR]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + SUBGOAL_THEN + `((\z:complex. w + z) o (\z:complex. Cx r * z)) continuous_on (:complex)` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_COMPLEX_LMUL_FN]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + MATCH_MP_TAC CONTINUOUS_ON_ADD THEN + REWRITE_TAC[CONTINUOUS_ON_CONST; CONTINUOUS_ON_ID]]]);; + +(* Continuity on the interval [0, 2*pi] *) +let CONTINUOUS_ON_POLAR_PATH_INTERVAL = prove + (`!w:complex r. + (\t. w + Cx r * cexp(ii * Cx(drop t))) continuous_on + interval[vec 0:real^1, lift(&2 * pi)]`, + REPEAT GEN_TAC THEN MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; CONTINUOUS_ON_POLAR_PATH]);; + +(* f composed with polar path continuous *) +let CONTINUOUS_ON_F_POLAR = prove + (`!f:complex->complex w r. + f continuous_on (:complex) + ==> (\t. f(w + Cx r * cexp(ii * Cx(drop t)))) continuous_on + interval[vec 0:real^1, lift(&2 * pi)]`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `((f:complex->complex) o + (\t:real^1. w + Cx r * cexp(ii * Cx(drop t)))) continuous_on + interval[vec 0:real^1, lift(&2 * pi)]` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_POLAR_PATH_INTERVAL]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN ASM_REWRITE_TAC[SUBSET_UNIV]]);; + +(* Vector derivative of f composed with polar path *) +let HAS_VECTOR_DERIVATIVE_F_POLAR = prove + (`!f:complex->complex w r t. + f differentiable at (w + Cx r * cexp(ii * Cx(drop t))) + ==> ((\t. f(w + Cx r * cexp(ii * Cx(drop t)))) + has_vector_derivative + (frechet_derivative f (at (w + Cx r * cexp(ii * Cx(drop t)))) + (Cx r * cexp(ii * Cx(drop t)) * ii))) + (at t)`, + REPEAT STRIP_TAC THEN REWRITE_TAC[has_vector_derivative] THEN + SUBGOAL_THEN + `((\t:real^1. (f:complex->complex)(w + Cx r * cexp(ii * Cx(drop t)))) + has_derivative + (\h. frechet_derivative f + (at (w + Cx r * cexp(ii * Cx(drop(t:real^1))))) + (Cx r * (cexp(ii * Cx(drop t)) * (ii * Cx(drop h)))))) + (at t)` + MP_TAC THENL + [SUBGOAL_THEN + `(\t:real^1. (f:complex->complex)(w + Cx r * cexp(ii * Cx(drop t)))) = + (f o (\t:real^1. w + Cx r * cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop(t:real^1))))) + (Cx r * (cexp(ii * Cx(drop t)) * (ii * Cx(drop h))))) = + (frechet_derivative f (at (w + Cx r * cexp(ii * Cx(drop t)))) o + (\h:real^1. Cx r * (cexp(ii * Cx(drop t)) * (ii * Cx(drop h)))))` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [REWRITE_TAC[HAS_DERIVATIVE_POLAR_PATH]; + FIRST_X_ASSUM(MP_TAC o + GEN_REWRITE_RULE I [FRECHET_DERIVATIVE_WORKS]) THEN + REWRITE_TAC[]]; + DISCH_TAC THEN + SUBGOAL_THEN + `(\h:real^1. drop h % frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop(t:real^1))))) + (Cx r * cexp(ii * Cx(drop t)) * ii)) = + (\h:real^1. frechet_derivative f + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (Cx r * (cexp(ii * Cx(drop t)) * (ii * Cx(drop h)))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `h:real^1` THEN + SUBGOAL_THEN + `Cx r * (cexp(ii * Cx(drop t)) * (ii * Cx(drop(h:real^1)))) = + drop h % (Cx r * cexp(ii * Cx(drop t)) * ii)` + SUBST1_TAC THENL + [REWRITE_TAC[COMPLEX_CMUL; CX_DEF; complex_mul; RE; IM; + REAL_MUL_RZERO; REAL_SUB_RZERO; REAL_ADD_LID] THEN + REWRITE_TAC[COMPLEX_EQ; RE; IM] THEN REAL_ARITH_TAC; + SUBGOAL_THEN `linear (frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop(t:real^1))))))` + (fun th -> REWRITE_TAC[MATCH_MP LINEAR_CMUL th]) THEN + MATCH_MP_TAC LINEAR_FRECHET_DERIVATIVE THEN ASM_REWRITE_TAC[]]; + ASM_REWRITE_TAC[]]]);; + +(* Angular FTC: integral of derivative along polar path *) +let ANGULAR_FTC = prove + (`!f:complex->complex w r. + f continuous_on (:complex) /\ + f differentiable_on (:complex) + ==> ((\t. frechet_derivative f (at (w + Cx r * cexp(ii * Cx(drop t)))) + (Cx r * cexp(ii * Cx(drop t)) * ii)) + has_integral + (f(w + Cx r * cexp(ii * Cx(drop(lift(&2 * pi))))) - + f(w + Cx r * cexp(ii * Cx(drop(vec 0)))))) + (interval[vec 0, lift(&2 * pi)])`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC FUNDAMENTAL_THEOREM_OF_CALCULUS_INTERIOR THEN + REWRITE_TAC[DROP_VEC; LIFT_DROP] THEN + CONJ_TAC THENL + [MP_TAC PI_POS THEN REAL_ARITH_TAC; ALL_TAC] THEN + CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_F_POLAR THEN ASM_REWRITE_TAC[]; + X_GEN_TAC `t:real^1` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; LIFT_DROP] THEN + STRIP_TAC THEN + MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_F_POLAR THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV]]);; + +(* The angular integral is zero by 2*pi-periodicity of exp *) +let ANGULAR_INTEGRAL_ZERO = prove + (`!f:complex->complex w r. + f continuous_on (:complex) /\ + f differentiable_on (:complex) + ==> integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative f (at (w + Cx r * cexp(ii * Cx(drop t)))) + (Cx r * cexp(ii * Cx(drop t)) * ii)) = vec 0`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC INTEGRAL_UNIQUE THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `r:real`] + ANGULAR_FTC) THEN + ASM_REWRITE_TAC[] THEN + REWRITE_TAC[LIFT_DROP; DROP_VEC] THEN + REWRITE_TAC[CEXP_2PII; CEXP_0II] THEN + REWRITE_TAC[COMPLEX_MUL_RID; VECTOR_SUB_REFL]);; + +(* ========================================================================= *) +(* Frechet derivative commutes with real scalar multiplication *) +(* ========================================================================= *) + +let FRECHET_DERIVATIVE_CX_LMUL = prove + (`!f:complex->complex z c x. + f differentiable at z + ==> frechet_derivative f (at z) (Cx c * x) = + Cx c * frechet_derivative f (at z) x`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `linear(frechet_derivative (f:complex->complex) (at z))` MP_TAC THENL + [MATCH_MP_TAC LINEAR_FRECHET_DERIVATIVE THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + DISCH_TAC THEN + SUBGOAL_THEN `Cx c * (x:complex) = c % x` SUBST1_TAC THENL + [REWRITE_TAC[COMPLEX_CMUL]; ALL_TAC] THEN + SUBGOAL_THEN `Cx c * frechet_derivative (f:complex->complex) (at z) x = + c % frechet_derivative f (at z) x` SUBST1_TAC THENL + [REWRITE_TAC[COMPLEX_CMUL]; ALL_TAC] THEN + ASM_MESON_TAC[LINEAR_CMUL]);; + +(* For r != 0, the angular integral of f'(z)(ii * exp(it)) is zero *) +let ANGULAR_INTEGRAL_II_ZERO = prove + (`!f:complex->complex w r. + f continuous_on (:complex) /\ + f differentiable_on (:complex) /\ + ~(r = &0) + ==> integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative f + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) = vec 0`, + REPEAT STRIP_TAC THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `r:real`] + ANGULAR_INTEGRAL_ZERO) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN + `!t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (Cx r * cexp(ii * Cx(drop t)) * ii) = + Cx r * frechet_derivative f + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))` + (fun th -> RULE_ASSUM_TAC(REWRITE_RULE[th])) THENL + [X_GEN_TAC `t:real^1` THEN + SUBGOAL_THEN + `Cx r * cexp(ii * Cx(drop t)) * ii = + Cx r * (ii * cexp(ii * Cx(drop(t:real^1))))` + SUBST1_TAC THENL + [SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + MATCH_MP_TAC FRECHET_DERIVATIVE_CX_LMUL THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; OPEN_UNIV; IN_UNIV]; + SUBGOAL_THEN + `((\t. frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)]))` + ASSUME_TAC THENL + [MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `r:real`] + ANGULAR_FTC) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN `!t:real^1. + frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (Cx r * cexp(ii * Cx(drop t)) * ii) = + Cx r * frechet_derivative f + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))` + (fun th -> RULE_ASSUM_TAC(REWRITE_RULE[th])) THENL + [X_GEN_TAC `t:real^1` THEN + SUBGOAL_THEN `Cx r * cexp(ii * Cx(drop t)) * ii = + Cx r * (ii * cexp(ii * Cx(drop(t:real^1))))` + SUBST1_TAC THENL + [SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + MATCH_MP_TAC FRECHET_DERIVATIVE_CX_LMUL THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV]; + ALL_TAC] THEN + FIRST_X_ASSUM(MP_TAC o MATCH_MP HAS_INTEGRAL_INTEGRABLE) THEN + REWRITE_TAC[GSYM COMPLEX_CMUL; INTEGRABLE_CMUL_EQ] THEN + ASM_MESON_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `Cx r * integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx r * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) = vec 0` + MP_TAC THENL + [ASM_SIMP_TAC[GSYM INTEGRAL_COMPLEX_LMUL] THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + REWRITE_TAC[COMPLEX_VEC_0; COMPLEX_ENTIRE; CX_INJ] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [UNDISCH_TAC `~(r = &0)` THEN ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[GSYM COMPLEX_VEC_0]]]);; + +(* ========================================================================= *) +(* Radial path lemmas for polar coordinates proof *) +(* ========================================================================= *) + +(* Radial path: r -> w + Cx(drop r) * cexp(ii * Cx t0) for fixed t0 *) + +let HAS_DERIVATIVE_RADIAL_PATH = prove + (`!w:complex t0:real r:real^1. + ((\r. w + Cx(drop r) * cexp(ii * Cx t0)) has_derivative + (\h. Cx(drop h) * cexp(ii * Cx t0))) (at r)`, + REPEAT GEN_TAC THEN + SUBGOAL_THEN + `((\r:real^1. w) has_derivative (\h:real^1. vec 0:complex)) (at r) /\ + ((\r:real^1. Cx(drop r) * cexp(ii * Cx t0)) has_derivative + (\h:real^1. Cx(drop h) * cexp(ii * Cx t0))) (at r)` + MP_TAC THENL + [CONJ_TAC THENL + [REWRITE_TAC[HAS_DERIVATIVE_CONST]; + SUBGOAL_THEN + `(\r:real^1. Cx(drop r) * cexp(ii * Cx t0)) = + ((\z:complex. cexp(ii * Cx t0) * z) o (\r:real^1. Cx(drop r)))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF; COMPLEX_MUL_SYM]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h:real^1. Cx(drop h) * cexp(ii * Cx t0)) = + ((\z:complex. cexp(ii * Cx t0) * z) o (\h:real^1. Cx(drop h)))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF; COMPLEX_MUL_SYM]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [MESON_TAC[HAS_DERIVATIVE_CX_DROP]; + MATCH_MP_TAC HAS_DERIVATIVE_LINEAR THEN + MATCH_ACCEPT_TAC LINEAR_COMPLEX_MUL]]; + DISCH_THEN(ACCEPT_TAC o REWRITE_RULE[VECTOR_ADD_LID] o + MATCH_MP HAS_DERIVATIVE_ADD)]);; + + +let HAS_VECTOR_DERIVATIVE_F_RADIAL = prove + (`!f:complex->complex w t0 r. + f differentiable at (w + Cx(drop r) * cexp(ii * Cx t0)) + ==> ((\r. f(w + Cx(drop r) * cexp(ii * Cx t0))) + has_vector_derivative + (frechet_derivative f (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0)))) + (at r)`, + REPEAT STRIP_TAC THEN REWRITE_TAC[has_vector_derivative] THEN + SUBGOAL_THEN + `((\r:real^1. (f:complex->complex)(w + Cx(drop r) * cexp(ii * Cx t0))) + has_derivative + (\h. frechet_derivative f + (at (w + Cx(drop(r:real^1)) * cexp(ii * Cx t0))) + (Cx(drop h) * cexp(ii * Cx t0)))) + (at r)` + MP_TAC THENL + [SUBGOAL_THEN + `(\r:real^1. (f:complex->complex)(w + Cx(drop r) * cexp(ii * Cx t0))) = + (f o (\r:real^1. w + Cx(drop r) * cexp(ii * Cx t0)))` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop(r:real^1)) * cexp(ii * Cx t0))) + (Cx(drop h) * cexp(ii * Cx t0))) = + (frechet_derivative f (at (w + Cx(drop r) * cexp(ii * Cx t0))) o + (\h:real^1. Cx(drop h) * cexp(ii * Cx t0)))` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [REWRITE_TAC[HAS_DERIVATIVE_RADIAL_PATH]; + FIRST_X_ASSUM(MP_TAC o + GEN_REWRITE_RULE I [FRECHET_DERIVATIVE_WORKS]) THEN + REWRITE_TAC[]]; + DISCH_TAC THEN + SUBGOAL_THEN + `(\h:real^1. drop h % frechet_derivative (f:complex->complex) + (at (w + Cx(drop(r:real^1)) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0))) = + (\h:real^1. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (Cx(drop h) * cexp(ii * Cx t0)))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `h:real^1` THEN + SUBGOAL_THEN `linear (frechet_derivative (f:complex->complex) + (at (w + Cx(drop(r:real^1)) * cexp(ii * Cx t0))))` + (fun th -> + REWRITE_TAC[GSYM(MATCH_MP LINEAR_CMUL th)] THEN + REWRITE_TAC[COMPLEX_CMUL]) THEN + MATCH_MP_TAC LINEAR_FRECHET_DERIVATIVE THEN ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[]]]);; + +(* The radial path is continuous *) +let CONTINUOUS_ON_RADIAL_PATH = prove + (`!w:complex t0. (\r:real^1. w + Cx(drop r) * cexp(ii * Cx t0)) + continuous_on (:real^1)`, + REPEAT GEN_TAC THEN + MATCH_MP_TAC CONTINUOUS_ON_ADD THEN + REWRITE_TAC[CONTINUOUS_ON_CONST] THEN + SUBGOAL_THEN + `((\z:complex. z * cexp(ii * Cx t0)) o (\r:real^1. Cx(drop r))) + continuous_on (:real^1)` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_CX_DROP THEN REWRITE_TAC[CONTINUOUS_ON_ID]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN EXISTS_TAC `(:complex)` THEN + REWRITE_TAC[SUBSET_UNIV] THEN + SUBGOAL_THEN `(\z:complex. z * cexp(ii * Cx t0)) = + (\z. cexp(ii * Cx t0) * z)` + (fun th -> REWRITE_TAC[th; CONTINUOUS_ON_COMPLEX_LMUL_FN]) THEN + REWRITE_TAC[FUN_EQ_THM; COMPLEX_MUL_SYM]]);; + +(* Continuity of f composed with radial path *) +let CONTINUOUS_ON_F_RADIAL = prove + (`!f:complex->complex w t0. + f continuous_on (:complex) + ==> (\r. f(w + Cx(drop r) * cexp(ii * Cx t0))) continuous_on + {r:real^1 | &0 <= drop r}`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `((f:complex->complex) o + (\r:real^1. w + Cx(drop r) * cexp(ii * Cx t0))) continuous_on + {r:real^1 | &0 <= drop r}` + (fun th -> ACCEPT_TAC(REWRITE_RULE[o_DEF] th)) THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; CONTINUOUS_ON_RADIAL_PATH]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN EXISTS_TAC `(:complex)` THEN + ASM_REWRITE_TAC[SUBSET_UNIV]]);; + +(* Radial FTC for f on [0, R] *) +let RADIAL_FTC = prove + (`!f:complex->complex w t0 R. + f continuous_on (:complex) /\ + f differentiable_on (:complex) /\ + &0 < R + ==> ((\r. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0))) + has_integral + (f(w + Cx R * cexp(ii * Cx t0)) - f(w))) + (interval[vec 0, lift R])`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `((\r:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0))) + has_integral + (f(w + Cx(drop(lift R)) * cexp(ii * Cx t0)) - + f(w + Cx(drop(vec 0:real^1)) * cexp(ii * Cx t0)))) + (interval[vec 0, lift R])` + MP_TAC THENL + [MATCH_MP_TAC FUNDAMENTAL_THEOREM_OF_CALCULUS_INTERIOR THEN + REWRITE_TAC[DROP_VEC; LIFT_DROP] THEN + CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `{r:real^1 | &0 <= drop r}` THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_F_RADIAL THEN ASM_REWRITE_TAC[]; + REWRITE_TAC[SUBSET; IN_INTERVAL_1; IN_ELIM_THM; DROP_VEC; LIFT_DROP] THEN + REAL_ARITH_TAC]; + X_GEN_TAC `r:real^1` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; LIFT_DROP] THEN + STRIP_TAC THEN + MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_F_RADIAL THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV]]; + REWRITE_TAC[DROP_VEC; LIFT_DROP; COMPLEX_MUL_LZERO; COMPLEX_ADD_RID]]);; + + +(* For r > 0, the inner polar integral simplifies: *) +(* drop r % (dbar f z / (Cx(drop r) * cexp)) = *) +(* inv(Cx 2) * integral [0,2pi] f'(z)(cexp) *) +(* Uses: POLAR_CEXP_SIMPLIFY + WIRTINGER_DBAR_FRECHET_POLAR + *) +(* ANGULAR_INTEGRAL_II_ZERO *) + +let INNER_INTEGRAL_SIMPLIFY = prove + (`!f:complex->complex w r. + f continuous_on (:complex) /\ + f differentiable_on (:complex) /\ + (!h. (\z. frechet_derivative f (at z) h) continuous_on (:complex)) /\ + &0 < drop r + ==> integral (interval[vec 0, lift(&2 * pi)]) + (\t. drop r % + (wirtinger_dbar f (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t))))) = + inv(Cx(&2)) * + integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `!t:real^1. + drop r % + (wirtinger_dbar (f:complex->complex) + (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t)))) = + inv(Cx(&2)) * + (frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) + + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t))))` + ASSUME_TAC THENL + [X_GEN_TAC `t:real^1` THEN + ASM_SIMP_TAC[POLAR_CEXP_SIMPLIFY] THEN + MATCH_MP_TAC(COMPLEX_FIELD + `~(Cx(&2) = Cx(&0)) /\ f1 + ii * f2 = Cx(&2) * d * c + ==> d * c = inv(Cx(&2)) * (f1 + ii * f2)`) THEN + CONJ_TAC THENL + [REWRITE_TAC[CX_INJ] THEN REAL_ARITH_TAC; + MATCH_MP_TAC WIRTINGER_DBAR_FRECHET_POLAR THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; OPEN_UNIV; IN_UNIV]]; + ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN + `((\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) + + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)]))` + ASSUME_TAC THENL + [SUBGOAL_THEN + `(\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) + + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) = + (\t. Cx(&2) * wirtinger_dbar f + (w + Cx(drop r) * cexp(ii * Cx(drop t))) * + cnj(cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `t:real^1` THEN + MATCH_MP_TAC WIRTINGER_DBAR_FRECHET_POLAR THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV]; + ALL_TAC] THEN + MATCH_MP_TAC INTEGRABLE_CONTINUOUS THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [SUBGOAL_THEN + `(\t:real^1. wirtinger_dbar (f:complex->complex) + (w + Cx(drop r) * cexp(ii * Cx(drop t)))) = + ((\z. wirtinger_dbar f z) o + (\t. w + Cx(drop r) * cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_POLAR_PATH_INTERVAL]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + REWRITE_TAC[SUBSET_UNIV] THEN + MATCH_MP_TAC WIRTINGER_DBAR_CONTINUOUS THEN + ASM_REWRITE_TAC[]; + SUBGOAL_THEN + `(\t:real^1. cnj(cexp(ii * Cx(drop t)))) = + (cnj o (\t. cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; CONT_CEXP_POLAR]; + REWRITE_TAC[CONTINUOUS_ON_CNJ]]]]; + ALL_TAC] THEN + SUBGOAL_THEN + `integral (interval[vec 0, lift(&2 * pi)]) + (\t:real^1. inv(Cx(&2)) * + (frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) + + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t))))) = + inv(Cx(&2)) * + integral (interval[vec 0, lift(&2 * pi)]) + (\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) + + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_COMPLEX_LMUL THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + AP_TERM_TAC THEN + SUBGOAL_THEN + `((\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)])) /\ + ((\t:real^1. ii * frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)]))` + STRIP_ASSUME_TAC THENL + [(* e2 integrable *) + SUBGOAL_THEN + `((\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)]))` + ASSUME_TAC THENL + [SUBGOAL_THEN `~(drop(r:real^1) = &0)` ASSUME_TAC THENL + [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `drop(r:real^1)`] + ANGULAR_FTC) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN `!t:real^1. + frechet_derivative (f:complex->complex) + (at (w + Cx(drop(r:real^1)) * cexp(ii * Cx(drop t)))) + (Cx(drop r) * cexp(ii * Cx(drop t)) * ii) = + Cx(drop r) * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))` + (fun th -> RULE_ASSUM_TAC(REWRITE_RULE[th])) THENL + [X_GEN_TAC `t':real^1` THEN + SUBGOAL_THEN + `Cx(drop(r:real^1)) * cexp(ii * Cx(drop t')) * ii = + Cx(drop r) * (ii * cexp(ii * Cx(drop(t':real^1))))` + SUBST1_TAC THENL + [SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + MATCH_MP_TAC FRECHET_DERIVATIVE_CX_LMUL THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV]; + ALL_TAC] THEN + FIRST_X_ASSUM(MP_TAC o MATCH_MP HAS_INTEGRAL_INTEGRABLE) THEN + REWRITE_TAC[GSYM COMPLEX_CMUL; INTEGRABLE_CMUL_EQ] THEN + ASM_MESON_TAC[]; + ALL_TAC] THEN + (* ii*e2 integrable *) + SUBGOAL_THEN + `((\t:real^1. ii * frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)]))` + ASSUME_TAC THENL + [SUBGOAL_THEN + `(\t:real^1. ii * frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) = + ((\x:complex. ii * x) o + (\t. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC INTEGRABLE_LINEAR THEN + ASM_REWRITE_TAC[LINEAR_COMPLEX_MUL]; + ALL_TAC] THEN + (* e1 = (e1+ii*e2) - ii*e2 *) + CONJ_TAC THENL + [SUBGOAL_THEN + `(\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))) = + (\t. (frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) + + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) - + ii * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `t':real^1` THEN + CONV_TAC COMPLEX_RING; + ALL_TAC] THEN + MATCH_MP_TAC INTEGRABLE_SUB THEN ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_ADD] THEN + SUBGOAL_THEN `~(drop(r:real^1) = &0)` ASSUME_TAC THENL + [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `drop(r:real^1)`] + ANGULAR_INTEGRAL_II_ZERO) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN + `((\t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) + integrable_on (interval[vec 0, lift(&2 * pi)]))` + ASSUME_TAC THENL + [MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `drop(r:real^1)`] + ANGULAR_FTC) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN `!t:real^1. + frechet_derivative (f:complex->complex) + (at (w + Cx(drop(r:real^1)) * cexp(ii * Cx(drop t)))) + (Cx(drop r) * cexp(ii * Cx(drop t)) * ii) = + Cx(drop r) * frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))` + (fun th -> RULE_ASSUM_TAC(REWRITE_RULE[th])) THENL + [X_GEN_TAC `t':real^1` THEN + SUBGOAL_THEN `Cx(drop(r:real^1)) * cexp(ii * Cx(drop t')) * ii = + Cx(drop r) * (ii * cexp(ii * Cx(drop(t':real^1))))` + SUBST1_TAC THENL + [SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + MATCH_MP_TAC FRECHET_DERIVATIVE_CX_LMUL THEN + ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV]; + ALL_TAC] THEN + FIRST_X_ASSUM(MP_TAC o MATCH_MP HAS_INTEGRAL_INTEGRABLE) THEN + REWRITE_TAC[GSYM COMPLEX_CMUL; INTEGRABLE_CMUL_EQ] THEN + ASM_MESON_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `integral (interval[vec 0, lift(&2 * pi)]) + (\t:real^1. ii * frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) = vec 0` + (fun th -> REWRITE_TAC[th; VECTOR_ADD_RID]) THEN + SUBGOAL_THEN + `integral (interval[vec 0, lift(&2 * pi)]) + (\t:real^1. ii * frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t)))) = + ii * integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (ii * cexp(ii * Cx(drop t))))` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_COMPLEX_LMUL THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + ASM_REWRITE_TAC[COMPLEX_VEC_0; COMPLEX_MUL_RZERO]]);; + +(* For each fixed angle t, the radial integral from 0 to infinity *) +(* of the radial derivative equals -f(w) (by FTC + compact support). *) + +let RADIAL_INTEGRAL_NEG_FW = prove + (`!f:complex->complex w t0. + f continuous_on (:complex) /\ + f differentiable_on (:complex) /\ + bounded (support (+) f (:complex)) + ==> integral {r:real^1 | &0 <= drop r} + (\r. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0))) = --(f w)`, + REPEAT STRIP_TAC THEN + FIRST_X_ASSUM(MP_TAC o GEN_REWRITE_RULE I [BOUNDED_POS]) THEN + DISCH_THEN(X_CHOOSE_THEN `R:real` STRIP_ASSUME_TAC) THEN + ABBREV_TAC `B = R + norm(w:complex) + &1` THEN + SUBGOAL_THEN + `((\r:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0))) + has_integral --(f w)) {r:real^1 | &0 <= drop r}` + (fun th -> REWRITE_TAC[MATCH_MP INTEGRAL_UNIQUE th]) THEN + REWRITE_TAC[HAS_INTEGRAL_LIM_AT_POSINFINITY] THEN + CONJ_TAC THENL + [(* Integrability on each [vec 0, a] *) + X_GEN_TAC `a:real^1` THEN + ASM_CASES_TAC `drop(a:real^1) <= &0` THENL + [MATCH_MP_TAC INTEGRABLE_ON_NULL THEN + REWRITE_TAC[CONTENT_EQ_0_1; DROP_VEC] THEN ASM_REAL_ARITH_TAC; + MATCH_MP_TAC HAS_INTEGRAL_INTEGRABLE THEN + EXISTS_TAC `(f:complex->complex)(w + Cx(drop(a:real^1)) * cexp(ii * Cx t0)) - f w` THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `t0:real`; + `drop(a:real^1)`] RADIAL_FTC) THEN + ASM_REWRITE_TAC[] THEN + ANTS_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[LIFT_DROP]]; + ALL_TAC] THEN + REWRITE_TAC[LIM_AT_POSINFINITY] THEN + X_GEN_TAC `e:real` THEN DISCH_TAC THEN + EXISTS_TAC `B:real` THEN + X_GEN_TAC `a:real` THEN DISCH_TAC THEN + SUBGOAL_THEN `&0 < a` ASSUME_TAC THENL + [MP_TAC(ISPEC `w:complex` NORM_POS_LE) THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `integral (interval[vec 0:real^1, lift a]) + (\r. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx t0))) + (cexp(ii * Cx t0))) = + f(w + Cx a * cexp(ii * Cx t0)) - f w` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_UNIQUE THEN + MATCH_MP_TAC RADIAL_FTC THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN `(f:complex->complex)(w + Cx a * cexp(ii * Cx t0)) = vec 0` + SUBST1_TAC THENL + [MATCH_MP_TAC(TAUT `(~p ==> F) ==> p`) THEN DISCH_TAC THEN + SUBGOAL_THEN `norm(w + Cx a * cexp(ii * Cx t0):complex) <= R` MP_TAC THENL + [SUBGOAL_THEN + `w + Cx a * cexp(ii * Cx t0) IN + support (+) (f:complex->complex) (:complex)` MP_TAC THENL + [REWRITE_TAC[IN_SUPPORT; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + ASM_REWRITE_TAC[]; + ASM_MESON_TAC[]]; + ALL_TAC] THEN + MP_TAC(ISPECL [`Cx a * cexp(ii * Cx t0)`; + `w + Cx a * cexp(ii * Cx t0):complex`] + NORM_TRIANGLE_SUB) THEN + REWRITE_TAC[COMPLEX_RING `x - (w + x):complex = --w`; NORM_NEG] THEN + REWRITE_TAC[COMPLEX_NORM_MUL; NORM_CEXP_II; COMPLEX_NORM_CX; + REAL_MUL_RID] THEN + MP_TAC(ISPEC `w:complex` NORM_POS_LE) THEN + ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + REWRITE_TAC[VECTOR_SUB_LZERO; DIST_REFL] THEN + ASM_REAL_ARITH_TAC);; + +(* Helper: in real^1, nonneg and nonzero means positive *) +let DROP_POS_OF_NONNEG_NZ = prove + (`!r:real^1. &0 <= drop r /\ ~(r = vec 0) ==> &0 < drop r`, + GEN_TAC THEN REWRITE_TAC[CART_EQ; DIMINDEX_1; FORALL_1; + VEC_COMPONENT; drop] THEN REAL_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Helpers for Fubini swap of polar integrals. *) +(* ------------------------------------------------------------------------- *) + +let WIRTINGER_DZ_FRECHET = prove + (`!f:complex->complex z. + wirtinger_dz f z = + (frechet_derivative f (at z) (Cx(&1)) - + ii * frechet_derivative f (at z) ii) / Cx(&2)`, + REPEAT GEN_TAC THEN + REWRITE_TAC[wirtinger_dz; jacobian; COMPLEX_EQ] THEN + REWRITE_TAC[complex_add; complex_sub; complex_mul; complex_neg; + RE; IM; RE_DIV_CX; IM_DIV_CX; ii; CX_DEF; complex] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + SIMP_TAC[MATRIX_COMPONENT; DIMINDEX_2; ARITH] THEN + SIMP_TAC[LAMBDA_BETA; DIMINDEX_2; ARITH; VECTOR_2] THEN + REWRITE_TAC[COMPLEX_BASIS] THEN + REWRITE_TAC[GSYM RE_DEF; GSYM IM_DEF; ii; CX_DEF; complex] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + SIMP_TAC[VECTOR_2; LAMBDA_BETA; DIMINDEX_2; ARITH] THEN + REWRITE_TAC[GSYM RE_DEF; GSYM IM_DEF] THEN + REAL_ARITH_TAC);; + +let WIRTINGER_DZ_CONTINUOUS = prove + (`!f:complex->complex s. + (!h. (\z. frechet_derivative f (at z) h) continuous_on s) + ==> (\z. wirtinger_dz f z) continuous_on s`, + REPEAT STRIP_TAC THEN REWRITE_TAC[WIRTINGER_DZ_FRECHET] THEN + REWRITE_TAC[complex_div] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_RMUL THEN + MATCH_MP_TAC CONTINUOUS_ON_SUB THEN CONJ_TAC THENL + [FIRST_X_ASSUM(MP_TAC o SPEC `Cx(&1)`) THEN REWRITE_TAC[]; + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_LMUL THEN + FIRST_X_ASSUM(MP_TAC o SPEC `ii`) THEN REWRITE_TAC[]]);; + +let FRECHET_CONTINUOUS_ON_JOINT = prove + (`!f:complex->complex (g:real^P->complex) (h:real^P->complex) s. + f differentiable_on (:complex) /\ + (!e. (\z. frechet_derivative f (at z) e) continuous_on (:complex)) /\ + g continuous_on s /\ + h continuous_on s + ==> (\x. frechet_derivative f (at (g x)) (h x)) continuous_on s`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `!z:complex. (f:complex->complex) differentiable (at z)` + ASSUME_TAC THENL + [ASM_MESON_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; OPEN_UNIV; IN_UNIV]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\x:real^P. frechet_derivative (f:complex->complex) + (at ((g:real^P->complex) x)) ((h:real^P->complex) x)) = + (\x. wirtinger_dz f (g x) * h x + wirtinger_dbar f (g x) * cnj(h x))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `x:real^P` THEN + MP_TAC(ISPECL [`f:complex->complex`; + `frechet_derivative (f:complex->complex) + (at ((g:real^P->complex) x))`; + `(g:real^P->complex) x`] FRECHET_WIRTINGER) THEN + ANTS_TAC THENL + [ASM_REWRITE_TAC[GSYM FRECHET_DERIVATIVE_WORKS]; + DISCH_THEN(fun th -> REWRITE_TAC[th])]; + ALL_TAC] THEN + SUBGOAL_THEN `wirtinger_dz (f:complex->complex) continuous_on (:complex)` + ASSUME_TAC THENL + [MATCH_MP_TAC(REWRITE_RULE[ETA_AX] WIRTINGER_DZ_CONTINUOUS) THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN `wirtinger_dbar (f:complex->complex) continuous_on (:complex)` + ASSUME_TAC THENL + [MATCH_MP_TAC(REWRITE_RULE[ETA_AX] WIRTINGER_DBAR_CONTINUOUS) THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_ADD THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [GEN_REWRITE_TAC LAND_CONV [GSYM o_DEF] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN ASM_REWRITE_TAC[SUBSET_UNIV]; + ASM_REWRITE_TAC[ETA_AX]]; + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [GEN_REWRITE_TAC LAND_CONV [GSYM o_DEF] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN ASM_REWRITE_TAC[SUBSET_UNIV]; + GEN_REWRITE_TAC LAND_CONV [GSYM o_DEF] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN + ASM_REWRITE_TAC[ETA_AX; CONTINUOUS_ON_CNJ]]]);; + +let CONTINUOUS_ON_CEXP_SNDCART = prove + (`!s. (\z:real^(1,1)finite_sum. + cexp(ii * Cx(drop(sndcart z)))) continuous_on s`, + GEN_TAC THEN GEN_REWRITE_TAC LAND_CONV [GSYM o_DEF] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN + REWRITE_TAC[CONTINUOUS_ON_CEXP] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_LMUL THEN + MATCH_MP_TAC CONTINUOUS_ON_CX_DROP THEN + MATCH_MP_TAC LINEAR_CONTINUOUS_ON THEN REWRITE_TAC[LINEAR_SNDCART]);; + +let CONTINUOUS_ON_POLAR_PASTECART = prove + (`!w:complex s. + (\z:real^(1,1)finite_sum. + w + Cx(drop(fstcart z)) * cexp(ii * Cx(drop(sndcart z)))) + continuous_on s`, + REPEAT GEN_TAC THEN MATCH_MP_TAC CONTINUOUS_ON_ADD THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_MUL THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_CX_DROP THEN + MATCH_MP_TAC LINEAR_CONTINUOUS_ON THEN REWRITE_TAC[LINEAR_FSTCART]; + REWRITE_TAC[CONTINUOUS_ON_CEXP_SNDCART]]]);; + +let FRECHET_DERIVATIVE_ZERO_OUTSIDE = prove + (`!f:real^M->real^N z h R. + f differentiable_on (:real^M) /\ + (!y. R < norm y ==> f y = vec 0) /\ + R < norm z + ==> frechet_derivative f (at z) h = vec 0`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(f has_derivative (\h:real^M. vec 0:real^N)) (at (z:real^M))` + ASSUME_TAC THENL + [MP_TAC(ISPECL [`\y:real^M. vec 0:real^N`; `\h:real^M. vec 0:real^N`; + `f:real^M->real^N`; `z:real^M`; + `(norm(z:real^M) - R) / &2`] + HAS_DERIVATIVE_TRANSFORM_AT) THEN + REWRITE_TAC[HAS_DERIVATIVE_CONST] THEN ANTS_TAC THENL + [CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + X_GEN_TAC `y:real^M` THEN REWRITE_TAC[dist] THEN DISCH_TAC THEN + CONV_TAC SYM_CONV THEN FIRST_X_ASSUM MATCH_MP_TAC THEN + MP_TAC(ISPECL [`z:real^M`; `y:real^M`] NORM_TRIANGLE_SUB) THEN + REWRITE_TAC[NORM_SUB] THEN ASM_REAL_ARITH_TAC; + REWRITE_TAC[]]; + ALL_TAC] THEN + FIRST_X_ASSUM(MP_TAC o MATCH_MP FRECHET_DERIVATIVE_AT) THEN + DISCH_THEN(fun th -> REWRITE_TAC[GSYM th]));; + +let ZERO_OUTSIDE_SUPPORT_BALL = prove + (`!f:real^M->real^N R. + (!x. x IN support (+) f (:real^M) ==> norm x <= R) + ==> (!y. R < norm y ==> f y = vec 0)`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC(TAUT `(~p ==> F) ==> p`) THEN DISCH_TAC THEN + SUBGOAL_THEN `y IN support (+) (f:real^M->real^N) (:real^M)` MP_TAC THENL + [REWRITE_TAC[IN_SUPPORT; IN_UNIV; NEUTRAL_VECTOR_ADD] THEN + ASM_REWRITE_TAC[]; + DISCH_THEN(fun th -> FIRST_ASSUM(MP_TAC o C MATCH_MP th)) THEN + ASM_REAL_ARITH_TAC]);; + +let POLAR_POINT_NORM_BOUND = prove + (`!w:complex r:real^1 t:real^1 R. + R + norm w + &1 <= drop r + ==> R < norm(w + Cx(drop r) * cexp(ii * Cx(drop t)))`, + REPEAT STRIP_TAC THEN + MP_TAC(ISPECL [`Cx(drop(r:real^1)) * cexp(ii * Cx(drop(t:real^1)))`; + `w + Cx(drop(r:real^1)) * cexp(ii * Cx(drop(t:real^1)))`] + NORM_TRIANGLE_SUB) THEN + REWRITE_TAC[COMPLEX_RING `x - (w + x):complex = --w`; NORM_NEG] THEN + REWRITE_TAC[COMPLEX_NORM_MUL; NORM_CEXP_II; COMPLEX_NORM_CX; + REAL_MUL_RID] THEN + MP_TAC(ISPEC `w:complex` NORM_POS_LE) THEN ASM_REAL_ARITH_TAC);; + +let POLAR_FUBINI_SWAP = prove + (`!f:complex->complex w. + f continuous_on (:complex) /\ + f differentiable_on (:complex) /\ + (!h. (\z. frechet_derivative f (at z) h) continuous_on (:complex)) /\ + bounded (support (+) f (:complex)) + ==> + integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))) = + integral (interval[vec 0, lift(&2 * pi)]) + (\t. integral {r:real^1 | &0 <= drop r} + (\r. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))))`, + REPEAT STRIP_TAC THEN + FIRST_X_ASSUM(MP_TAC o GEN_REWRITE_RULE I [BOUNDED_POS]) THEN + DISCH_THEN(X_CHOOSE_THEN `R:real` STRIP_ASSUME_TAC) THEN + ABBREV_TAC `B = R + norm(w:complex) + &1` THEN + SUBGOAL_THEN `&0 < B` ASSUME_TAC THENL + [EXPAND_TAC "B" THEN MP_TAC(ISPEC `w:complex` NORM_POS_LE) THEN + ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + MP_TAC(ISPECL [`f:complex->complex`; `R:real`] + ZERO_OUTSIDE_SUPPORT_BALL) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + SUBGOAL_THEN + `!r:real^1 t:real^1. B < drop r ==> + frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) = vec 0` + ASSUME_TAC THENL + [REPEAT STRIP_TAC THEN MATCH_MP_TAC FRECHET_DERIVATIVE_ZERO_OUTSIDE THEN + EXISTS_TAC `R:real` THEN ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC POLAR_POINT_NORM_BOUND THEN + EXPAND_TAC "B" THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))) = + integral (interval[vec 0:real^1, lift B]) + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))))` + SUBST1_TAC THENL + [ONCE_REWRITE_TAC[GSYM INTEGRAL_RESTRICT_UNIV] THEN + AP_TERM_TAC THEN REWRITE_TAC[FUN_EQ_THM] THEN + X_GEN_TAC `r:real^1` THEN + REWRITE_TAC[IN_ELIM_THM; IN_INTERVAL_1; DROP_VEC; LIFT_DROP] THEN + ASM_CASES_TAC `&0 <= drop(r:real^1)` THEN ASM_REWRITE_TAC[] THEN + ASM_CASES_TAC `drop(r:real^1) <= B` THEN ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN `!t:real^1. + frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))) = vec 0` + (fun th -> REWRITE_TAC[th; INTEGRAL_0]) THEN + GEN_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `integral (interval[vec 0, lift(&2 * pi)]) + (\t. integral {r:real^1 | &0 <= drop r} + (\r. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))) = + integral (interval[vec 0, lift(&2 * pi)]) + (\t. integral (interval[vec 0:real^1, lift B]) + (\r. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))))` + SUBST1_TAC THENL + [AP_TERM_TAC THEN REWRITE_TAC[FUN_EQ_THM] THEN + X_GEN_TAC `t:real^1` THEN + ONCE_REWRITE_TAC[GSYM INTEGRAL_RESTRICT_UNIV] THEN + AP_TERM_TAC THEN REWRITE_TAC[FUN_EQ_THM] THEN + X_GEN_TAC `r:real^1` THEN + REWRITE_TAC[IN_ELIM_THM; IN_INTERVAL_1; DROP_VEC; LIFT_DROP] THEN + ASM_CASES_TAC `&0 <= drop(r:real^1)` THEN ASM_REWRITE_TAC[] THEN + ASM_CASES_TAC `drop(r:real^1) <= B` THEN ASM_REWRITE_TAC[] THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + MP_TAC(ISPECL [ + `\r:real^1 t:real^1. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))`; + `vec 0:real^1`; `lift B:real^1`; + `vec 0:real^1`; `lift(&2 * pi):real^1` + ] INTEGRAL_SWAP_CONTINUOUS) THEN + REWRITE_TAC[] THEN + DISCH_THEN MATCH_MP_TAC THEN + MATCH_MP_TAC FRECHET_CONTINUOUS_ON_JOINT THEN + ASM_REWRITE_TAC[CONTINUOUS_ON_POLAR_PASTECART; + CONTINUOUS_ON_CEXP_SNDCART]);; + +(* Full polar decomposition: converts 2D dbar integral to constant *) +(* angular integral. Combines FUBINI_POLAR + INNER_INTEGRAL_SIMPLIFY + *) +(* Fubini swap + RADIAL_INTEGRAL_NEG_FW. *) + +let POLAR_INTEGRAL_DECOMPOSITION = prove + (`!f:complex->complex w. + f differentiable_on (:complex) /\ + (!h. (\z. frechet_derivative f (at z) h) continuous_on (:complex)) /\ + bounded (support (+) f (:complex)) + ==> integral (:complex) (\z. wirtinger_dbar f (w + z) / z) = + inv(Cx(&2)) * + integral (interval[vec 0:real^1, lift(&2 * pi)]) (\t:real^1. --(f w))`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `(f:complex->complex) continuous_on (:complex)` ASSUME_TAC THENL + [MATCH_MP_TAC DIFFERENTIABLE_IMP_CONTINUOUS_ON THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\z. wirtinger_dbar (f:complex->complex) (w + z) / z) + absolutely_integrable_on (:complex)` ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_DBAR_KERNEL THEN CONJ_TAC THENL + [MATCH_MP_TAC WIRTINGER_DBAR_CONTINUOUS THEN ASM_REWRITE_TAC[]; + MATCH_MP_TAC WIRTINGER_DBAR_BOUNDED_SUPPORT THEN ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + (* Step 1: Apply FUBINI_POLAR *) + SUBGOAL_THEN + `integral (:complex) (\z. wirtinger_dbar (f:complex->complex) (w + z) / z) = + integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. drop r % + (wirtinger_dbar f (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t))))))` + SUBST1_TAC THENL + [MP_TAC(BETA_RULE(ISPEC + `\z. wirtinger_dbar (f:complex->complex) (w + z) / z` + FUBINI_POLAR)) THEN + ANTS_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + DISCH_THEN(ACCEPT_TAC o SYM o CONJUNCT2 o CONJUNCT2); + ALL_TAC] THEN + (* Step 2: Simplify inner integral + factor out inv(Cx 2) *) + SUBGOAL_THEN + `integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. drop r % + (wirtinger_dbar (f:complex->complex) + (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t)))))) = + inv(Cx(&2)) * + integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))))` + SUBST1_TAC THENL + [(* Step 2a: LHS outer integrand abs integrable from FUBINI_POLAR *) + SUBGOAL_THEN + `(\r:real^1. integral (interval[vec 0, lift(&2 * pi)]) + (\t. drop r % + (wirtinger_dbar (f:complex->complex) + (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t)))))) + absolutely_integrable_on {r | &0 <= drop r}` + ASSUME_TAC THENL + [MP_TAC(BETA_RULE(ISPEC + `\z. wirtinger_dbar (f:complex->complex) (w + z) / z` + FUBINI_POLAR)) THEN + ANTS_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + DISCH_THEN(fun th -> ACCEPT_TAC(CONJUNCT1(CONJUNCT2 th))); + ALL_TAC] THEN + (* Step 2b: INTEGRAL_SPIKE to equate LHS with inv(Cx 2) * G *) + SUBGOAL_THEN + `integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. drop r % + (wirtinger_dbar (f:complex->complex) + (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t)))))) = + integral {r:real^1 | &0 <= drop r} + (\r. inv(Cx(&2)) * + integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))))` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_SPIKE THEN + EXISTS_TAC `{vec 0:real^1}` THEN + REWRITE_TAC[NEGLIGIBLE_SING; IN_DIFF; IN_ELIM_THM; IN_SING] THEN + GEN_TAC THEN STRIP_TAC THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `x:real^1`] + INNER_INTEGRAL_SIMPLIFY) THEN + ASM_REWRITE_TAC[] THEN + ANTS_TAC THENL + [ASM_MESON_TAC[DROP_POS_OF_NONNEG_NZ]; SIMP_TAC[]]; + ALL_TAC] THEN + (* Step 2c: G integrable *) + SUBGOAL_THEN + `(\r:real^1. integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))) + integrable_on {r | &0 <= drop r}` + ASSUME_TAC THENL + [SUBGOAL_THEN + `(\r:real^1. inv(Cx(&2)) * + integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))) + integrable_on {r | &0 <= drop r}` + MP_TAC THENL + [MATCH_MP_TAC(REWRITE_RULE[IMP_IMP] INTEGRABLE_SPIKE) THEN + EXISTS_TAC + `(\r:real^1. integral (interval[vec 0, lift(&2 * pi)]) + (\t. drop r % + (wirtinger_dbar (f:complex->complex) + (w + Cx(drop r) * cexp(ii * Cx(drop t))) / + (Cx(drop r) * cexp(ii * Cx(drop t))))))` THEN + EXISTS_TAC `{vec 0:real^1}` THEN + REWRITE_TAC[NEGLIGIBLE_SING; IN_DIFF; IN_ELIM_THM; IN_SING] THEN + CONJ_TAC THENL + [GEN_TAC THEN STRIP_TAC THEN + CONV_TAC SYM_CONV THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `x:real^1`] + INNER_INTEGRAL_SIMPLIFY) THEN + ASM_REWRITE_TAC[] THEN + ANTS_TAC THENL + [ASM_MESON_TAC[DROP_POS_OF_NONNEG_NZ]; SIMP_TAC[]]; + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + REWRITE_TAC[INTEGRABLE_COMPLEX_LMUL_EQ] THEN + STRIP_TAC THEN ASM_REWRITE_TAC[] THEN + UNDISCH_TAC `inv(Cx(&2)) = Cx(&0)` THEN + REWRITE_TAC[COMPLEX_INV_EQ_0; CX_INJ] THEN REAL_ARITH_TAC; + ALL_TAC] THEN + (* Step 2d: Factor out inv(Cx 2) *) + ASM_SIMP_TAC[GSYM INTEGRAL_COMPLEX_LMUL]; + ALL_TAC] THEN + (* Step 3: Swap order of integration (Fubini) *) + SUBGOAL_THEN + `integral {r:real^1 | &0 <= drop r} + (\r. integral (interval[vec 0, lift(&2 * pi)]) + (\t. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t))))) = + integral (interval[vec 0, lift(&2 * pi)]) + (\t. integral {r:real^1 | &0 <= drop r} + (\r. frechet_derivative f + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))))` + SUBST1_TAC THENL + [MATCH_MP_TAC POLAR_FUBINI_SWAP THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* Step 4: Apply RADIAL_INTEGRAL_NEG_FW *) + SUBGOAL_THEN + `!t:real^1. integral {r:real^1 | &0 <= drop r} + (\r. frechet_derivative (f:complex->complex) + (at (w + Cx(drop r) * cexp(ii * Cx(drop t)))) + (cexp(ii * Cx(drop t)))) = --(f w)` + (fun th -> REWRITE_TAC[th]) THEN + X_GEN_TAC `t0:real^1` THEN + MP_TAC(SPECL [`f:complex->complex`; `w:complex`; `drop(t0:real^1)`] + RADIAL_INTEGRAL_NEG_FW) THEN + ASM_REWRITE_TAC[]);; + +(* ------------------------------------------------------------------------- *) +(* Helper lemmas for the Pompeiu formula. *) +(* ------------------------------------------------------------------------- *) + +let INTEGRAL_CONST_2PI = prove + (`!c:complex. + integral (interval[vec 0:real^1, lift(&2 * pi)]) (\t. c) = + Cx(&2 * pi) * c`, + GEN_TAC THEN REWRITE_TAC[INTEGRAL_CONST] THEN + SUBGOAL_THEN `content(interval[vec 0:real^1, lift(&2 * pi)]) = &2 * pi` + SUBST1_TAC THENL + [MP_TAC(SPECL [`vec 0:real^1`; `lift(&2 * pi)`] CONTENT_1) THEN + REWRITE_TAC[DROP_VEC; LIFT_DROP] THEN + MP_TAC PI_POS THEN REAL_ARITH_TAC; + REWRITE_TAC[COMPLEX_CMUL]]);; + +let INV_TWO_TIMES_TWO_PI = prove + (`inv(Cx(&2)) * Cx(&2 * pi) = Cx(pi)`, + REWRITE_TAC[CX_MUL] THEN + SUBGOAL_THEN `inv(Cx(&2)) * Cx(&2) = Cx(&1)` + (fun th -> REWRITE_TAC[COMPLEX_MUL_ASSOC; th; COMPLEX_MUL_LID]) THEN + MATCH_MP_TAC COMPLEX_MUL_LINV THEN + REWRITE_TAC[CX_INJ] THEN REAL_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* The Pompeiu formula: Cauchy transform inverts dbar. *) +(* ------------------------------------------------------------------------- *) + +let CAUCHY_TRANSFORM_INVERTS_DBAR = prove + (`!f:complex->complex w. + f differentiable_on (:complex) /\ + (!h. (\z. frechet_derivative f (at z) h) continuous_on (:complex)) /\ + bounded (support (+) f (:complex)) + ==> integral (:complex) + (\z. wirtinger_dbar f z / (z - w)) = --(Cx pi * f w)`, + REPEAT STRIP_TAC THEN + ONCE_REWRITE_TAC[CAUCHY_TRANSFORM_DBAR_TRANSLATION] THEN + SUBGOAL_THEN + `integral (:complex) (\z. wirtinger_dbar (f:complex->complex) (w + z) / z) = + inv(Cx(&2)) * + integral (interval[vec 0:real^1, lift(&2 * pi)]) (\t:real^1. --(f w))` + SUBST1_TAC THENL + [MATCH_MP_TAC POLAR_INTEGRAL_DECOMPOSITION THEN ASM_REWRITE_TAC[]; + (* Step 5: Compute the constant integral and simplify *) + REWRITE_TAC[INTEGRAL_CONST_2PI] THEN + REWRITE_TAC[COMPLEX_MUL_ASSOC; INV_TWO_TIMES_TWO_PI] THEN + REWRITE_TAC[COMPLEX_MUL_RNEG]]);; + +(* ========================================================================= *) +(* Hermite cutoff infrastructure for smooth extension. *) +(* ========================================================================= *) + +let PIECEWISE_HAS_DERIVATIVE_LOCAL = prove + (`!(f:real^M->real^N) g ef f' z d0. + &0 < d0 /\ + (f has_derivative f') (at z) /\ + (g has_derivative f') (at z) /\ + f z = g z /\ + (!y:real^M. norm(y - z) < d0 ==> ef y = f y \/ ef y = g y) /\ + ef z = f z + ==> (ef has_derivative f') (at z)`, + REPEAT GEN_TAC THEN + REWRITE_TAC[HAS_DERIVATIVE_AT_ALT] THEN + STRIP_TAC THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `e:real` THEN DISCH_TAC THEN + UNDISCH_TAC + `!e. &0 < e + ==> (?d. &0 < d /\ + (!y. norm (y - z) < d + ==> norm ((f:real^M->real^N) y - f z - f' (y - z)) + <= e * norm (y - z)))` THEN + DISCH_THEN(MP_TAC o SPEC `e:real`) THEN + ANTS_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `d1:real` STRIP_ASSUME_TAC) THEN + UNDISCH_TAC + `!e. &0 < e + ==> (?d. &0 < d /\ + (!y. norm (y - z) < d + ==> norm ((g:real^M->real^N) y - g z - f' (y - z)) + <= e * norm (y - z)))` THEN + DISCH_THEN(MP_TAC o SPEC `e:real`) THEN + ANTS_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `d2:real` STRIP_ASSUME_TAC) THEN + EXISTS_TAC `min d0 (min d1 d2)` THEN + CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + X_GEN_TAC `y:real^M` THEN DISCH_TAC THEN + UNDISCH_TAC + `!y:real^M. norm(y - z) < d0 + ==> (ef:real^M->real^N) y = f y \/ ef y = g y` THEN + DISCH_THEN(MP_TAC o SPEC `y:real^M`) THEN + ANTS_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [SUBGOAL_THEN + `(ef:real^M->real^N) y - g z - f'(y - z:real^M) = + f y - f z - f'(y - z)` SUBST1_TAC THENL + [SUBGOAL_THEN `(g:real^M->real^N) z = f z` + (fun th -> REWRITE_TAC[th]) THENL + [ASM_MESON_TAC[]; ASM_REWRITE_TAC[]]; + FIRST_X_ASSUM(MATCH_MP_TAC o + check (fun th -> free_in `d1:real` (concl th))) THEN + ASM_REAL_ARITH_TAC]; + SUBGOAL_THEN + `(ef:real^M->real^N) y - g z - f'(y - z:real^M) = + g y - g z - f'(y - z)` SUBST1_TAC THENL + [ASM_REWRITE_TAC[]; + FIRST_X_ASSUM(MATCH_MP_TAC o + check (fun th -> free_in `d2:real` (concl th))) THEN + ASM_REAL_ARITH_TAC]]);; + +let PIECEWISE_HAS_REAL_DERIVATIVE_LOCAL = prove + (`!f g ef f' x d0. + &0 < d0 /\ + (f has_real_derivative f') (atreal x) /\ + (g has_real_derivative f') (atreal x) /\ + f x = g x /\ + (!y. abs(y - x) < d0 ==> ef y = f y \/ ef y = g y) /\ + ef x = f x + ==> (ef has_real_derivative f') (atreal x)`, + REPEAT GEN_TAC THEN + REWRITE_TAC[HAS_REAL_FRECHET_DERIVATIVE_AT; o_DEF; LIFT_DROP] THEN + STRIP_TAC THEN + MATCH_MP_TAC PIECEWISE_HAS_DERIVATIVE_LOCAL THEN + MAP_EVERY EXISTS_TAC + [`(\y:real^1. lift(f(drop y)))`; + `(\y:real^1. lift(g(drop y)))`; + `d0:real`] THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ASM_REWRITE_TAC[LIFT_EQ; LIFT_DROP] THEN + X_GEN_TAC `y:real^1` THEN + DISCH_TAC THEN + FIRST_X_ASSUM(MP_TAC o SPEC `drop y`) THEN + ANTS_TAC THENL + [POP_ASSUM MP_TAC THEN + REWRITE_TAC[NORM_REAL; GSYM drop; DROP_SUB; LIFT_DROP]; + SIMP_TAC[]]);; + +let hermite_cutoff = new_definition + `hermite_cutoff (t:real) = + if t <= &0 then &1 + else if &1 <= t then &0 + else &2 * t pow 3 - &3 * t pow 2 + &1`;; + +let hermite_cutoff_deriv = new_definition + `hermite_cutoff_deriv (t:real) = + if t <= &0 then &0 + else if &1 <= t then &0 + else &6 * t pow 2 - &6 * t`;; + + +let HAS_REAL_DERIVATIVE_HERMITE_INTERIOR = prove + (`!t. &0 < t /\ t < &1 + ==> (hermite_cutoff has_real_derivative (&6 * t pow 2 - &6 * t)) + (atreal t)`, + REPEAT STRIP_TAC THEN + MP_TAC(SPECL [`\t:real. &2 * t pow 3 - &3 * t pow 2 + &1`; + `&6 * t pow 2 - &6 * t`; `hermite_cutoff`; + `t:real`; `min t (&1 - t)`] + HAS_REAL_DERIVATIVE_TRANSFORM_ATREAL) THEN + ANTS_TAC THENL + [CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + CONJ_TAC THENL + [X_GEN_TAC `s:real` THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + COND_CASES_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + COND_CASES_TAC THENL [ASM_REAL_ARITH_TAC; REFL_TAC]; + REAL_DIFF_TAC THEN CONV_TAC NUM_REDUCE_CONV THEN REAL_ARITH_TAC]; + SIMP_TAC[]]);; + +let HAS_REAL_DERIVATIVE_HERMITE_LEFT = prove + (`!t. t < &0 + ==> (hermite_cutoff has_real_derivative (&0)) (atreal t)`, + REPEAT STRIP_TAC THEN + MP_TAC(SPECL [`\t:real. &1`; `&0:real`; `hermite_cutoff`; + `t:real`; `-- t:real`] + HAS_REAL_DERIVATIVE_TRANSFORM_ATREAL) THEN + ANTS_TAC THENL + [CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + CONJ_TAC THENL + [X_GEN_TAC `s:real` THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + COND_CASES_TAC THENL [REFL_TAC; ASM_REAL_ARITH_TAC]; + REAL_DIFF_TAC THEN REFL_TAC]; + SIMP_TAC[]]);; + +let HAS_REAL_DERIVATIVE_HERMITE_RIGHT = prove + (`!t. &1 < t + ==> (hermite_cutoff has_real_derivative (&0)) (atreal t)`, + REPEAT STRIP_TAC THEN + MP_TAC(SPECL [`\t:real. &0:real`; `&0:real`; `hermite_cutoff`; + `t:real`; `t - &1:real`] + HAS_REAL_DERIVATIVE_TRANSFORM_ATREAL) THEN + ANTS_TAC THENL + [CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + CONJ_TAC THENL + [X_GEN_TAC `s:real` THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + COND_CASES_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + COND_CASES_TAC THENL [REFL_TAC; ASM_REAL_ARITH_TAC]; + REAL_DIFF_TAC THEN REFL_TAC]; + SIMP_TAC[]]);; + +let HAS_REAL_DERIVATIVE_HERMITE_AT_0 = prove + (`(hermite_cutoff has_real_derivative (&0)) (atreal (&0))`, + MATCH_MP_TAC PIECEWISE_HAS_REAL_DERIVATIVE_LOCAL THEN + MAP_EVERY EXISTS_TAC + [`\t:real. &1`; `\t:real. &2 * t pow 3 - &3 * t pow 2 + &1`; `&1`] THEN + REPEAT CONJ_TAC THENL + [REAL_ARITH_TAC; REAL_DIFF_TAC THEN REFL_TAC; + REAL_DIFF_TAC THEN CONV_TAC NUM_REDUCE_CONV THEN REAL_ARITH_TAC; + REAL_ARITH_TAC; + X_GEN_TAC `t:real` THEN REWRITE_TAC[REAL_SUB_RZERO] THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + COND_CASES_TAC THENL [DISJ1_TAC THEN REFL_TAC; ALL_TAC] THEN + COND_CASES_TAC THENL [ASM_REAL_ARITH_TAC; DISJ2_TAC THEN REFL_TAC]; + REWRITE_TAC[hermite_cutoff] THEN REAL_ARITH_TAC]);; + +let HAS_REAL_DERIVATIVE_HERMITE_AT_1 = prove + (`(hermite_cutoff has_real_derivative (&0)) (atreal (&1))`, + MATCH_MP_TAC PIECEWISE_HAS_REAL_DERIVATIVE_LOCAL THEN + MAP_EVERY EXISTS_TAC + [`\t:real. &2 * t pow 3 - &3 * t pow 2 + &1`; `\t:real. &0`; `&1`] THEN + REPEAT CONJ_TAC THENL + [REAL_ARITH_TAC; + REAL_DIFF_TAC THEN CONV_TAC NUM_REDUCE_CONV THEN REAL_ARITH_TAC; + REAL_DIFF_TAC THEN REFL_TAC; REAL_ARITH_TAC; + X_GEN_TAC `t:real` THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + COND_CASES_TAC THENL [DISJ1_TAC THEN ASM_REAL_ARITH_TAC; ALL_TAC] THEN + COND_CASES_TAC THENL [DISJ2_TAC THEN REAL_ARITH_TAC; ALL_TAC] THEN + DISJ1_TAC THEN REFL_TAC; + REWRITE_TAC[hermite_cutoff] THEN REAL_ARITH_TAC]);; + +let HAS_REAL_DERIVATIVE_HERMITE_EXPLICIT = prove + (`!t. (hermite_cutoff has_real_derivative hermite_cutoff_deriv t) (atreal t)`, + GEN_TAC THEN REWRITE_TAC[hermite_cutoff_deriv] THEN + COND_CASES_TAC THENL + [ASM_CASES_TAC `t = &0` THENL + [ASM_REWRITE_TAC[HAS_REAL_DERIVATIVE_HERMITE_AT_0]; + MATCH_MP_TAC HAS_REAL_DERIVATIVE_HERMITE_LEFT THEN + ASM_REAL_ARITH_TAC]; + COND_CASES_TAC THENL + [ASM_CASES_TAC `t = &1` THENL + [ASM_REWRITE_TAC[HAS_REAL_DERIVATIVE_HERMITE_AT_1]; + MATCH_MP_TAC HAS_REAL_DERIVATIVE_HERMITE_RIGHT THEN + ASM_REAL_ARITH_TAC]; + MATCH_MP_TAC HAS_REAL_DERIVATIVE_HERMITE_INTERIOR THEN + ASM_REAL_ARITH_TAC]]);; + +let POLY_CONTINUOUS_REAL1 = prove + (`(\x:real^1. lift(&6 * drop x pow 2 - &6 * drop x)) continuous_on s`, + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + SUBGOAL_THEN + `(\x:real^1. lift(&6 * drop x pow 2 - &6 * drop x)) = + (\x. &6 % lift(x dot x) - &6 % x)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; + VECTOR_SUB_COMPONENT; VECTOR_MUL_COMPONENT; + lift; LAMBDA_BETA; LE_REFL; DOT_1; drop; REAL_POW_2] THEN + REAL_ARITH_TAC; + MATCH_MP_TAC CONTINUOUS_ON_SUB THEN CONJ_TAC THEN + MATCH_MP_TAC CONTINUOUS_ON_CMUL THENL + [MATCH_MP_TAC CONTINUOUS_ON_LIFT_DOT2 THEN + REWRITE_TAC[CONTINUOUS_ON_ID]; + REWRITE_TAC[CONTINUOUS_ON_ID]]]);; + +let HERMITE_CUTOFF_DERIV_CONTINUOUS = prove + (`(lift o hermite_cutoff_deriv o drop) continuous_on (:real^1)`, + SUBGOAL_THEN + `(lift o hermite_cutoff_deriv o drop) = + (\x:real^1. if drop x <= &0 then vec 0 + else if drop x <= &1 + then lift(&6 * drop x pow 2 - &6 * drop x) + else vec 0)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; hermite_cutoff_deriv] THEN + X_GEN_TAC `x:real^1` THEN + COND_CASES_TAC THEN REWRITE_TAC[LIFT_NUM] THEN + ASM_CASES_TAC `&1 <= drop x` THENL + [ASM_REWRITE_TAC[LIFT_NUM] THEN + COND_CASES_TAC THENL + [SUBGOAL_THEN `drop(x:real^1) = &1` SUBST1_TAC THENL + [ASM_REAL_ARITH_TAC; ALL_TAC] THEN + CONV_TAC REAL_RAT_REDUCE_CONV THEN REWRITE_TAC[LIFT_NUM]; + REFL_TAC]; + ASM_REWRITE_TAC[] THEN + COND_CASES_TAC THENL [REFL_TAC; ASM_REAL_ARITH_TAC]]; + ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_CASES_LE THEN + REPEAT CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_CASES_LE THEN + REPEAT CONJ_TAC THENL + [REWRITE_TAC[POLY_CONTINUOUS_REAL1]; + REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; o_DEF; LIFT_DROP; CONTINUOUS_ON_ID]; + SIMP_TAC[] THEN CONV_TAC REAL_RAT_REDUCE_CONV THEN + REWRITE_TAC[LIFT_NUM]]; + REWRITE_TAC[o_DEF; LIFT_DROP; CONTINUOUS_ON_ID]; + SIMP_TAC[] THEN CONV_TAC REAL_RAT_REDUCE_CONV THEN + REWRITE_TAC[LIFT_NUM]]);; + +let HERMITE_CUTOFF_DIFFERENTIABLE = prove + (`!x:real^1. (lift o hermite_cutoff o drop) differentiable (at x)`, + GEN_TAC THEN REWRITE_TAC[differentiable] THEN + EXISTS_TAC `\v:real^1. hermite_cutoff_deriv(drop x) % v` THEN + MP_TAC(SPEC `drop x` HAS_REAL_DERIVATIVE_HERMITE_EXPLICIT) THEN + REWRITE_TAC[HAS_REAL_FRECHET_DERIVATIVE_AT; o_DEF; LIFT_DROP]);; + +let HERMITE_CUTOFF_DIFFERENTIABLE_ON = prove + (`!s. (lift o hermite_cutoff o drop) differentiable_on s`, + REWRITE_TAC[differentiable_on] THEN + REPEAT STRIP_TAC THEN + MATCH_MP_TAC DIFFERENTIABLE_AT_WITHIN THEN + REWRITE_TAC[HERMITE_CUTOFF_DIFFERENTIABLE]);; + +let HERMITE_CUTOFF_CONTINUOUS = prove + (`(lift o hermite_cutoff o drop) continuous_on (:real^1)`, + MATCH_MP_TAC DIFFERENTIABLE_IMP_CONTINUOUS_ON THEN + REWRITE_TAC[HERMITE_CUTOFF_DIFFERENTIABLE_ON]);; + +(* Chain rule for hermite_cutoff composed with phi(z) = (norm z^2 - R^2)/c *) +let HERMITE_CUTOFF_CHI_HAS_DERIVATIVE = prove + (`!R c (z:complex). + &0 < c + ==> ((\z'. lift(hermite_cutoff((norm z' pow 2 - R pow 2) / c))) + has_derivative + (\h:complex. lift(hermite_cutoff_deriv((norm z pow 2 - R pow 2) / c) * + (&2 * (z dot h)) / c))) (at z)`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\z':complex. lift(hermite_cutoff((norm z' pow 2 - R pow 2) / c))) = + (lift o hermite_cutoff o drop) o + (\z':complex. lift((norm z' pow 2 - R pow 2) / c))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_DEF; LIFT_DROP]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h:complex. lift(hermite_cutoff_deriv((norm z pow 2 - R pow 2) / c) * + (&2 * (z dot h)) / c)) = + (\h. hermite_cutoff_deriv((norm(z:complex) pow 2 - R pow 2) / c) % + lift((&2 * (z dot h)) / c))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; VECTOR_MUL_COMPONENT; + lift; LAMBDA_BETA; LE_REFL] THEN REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `(\h:complex. hermite_cutoff_deriv((norm(z:complex) pow 2 - R pow 2) / c) % + lift((&2 * (z dot h)) / c)) = + (\v:real^1. hermite_cutoff_deriv((norm z pow 2 - R pow 2) / c) % v) o + (\h:complex. lift((&2 * (z dot h)) / c))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_DEF]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [SUBGOAL_THEN + `(\z':complex. lift((norm z' pow 2 - R pow 2) / c)) = + (\z'. inv(c) % lift(norm z' pow 2) + lift(--(R pow 2 / c)))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; VECTOR_MUL_COMPONENT; + VECTOR_ADD_COMPONENT; lift; LAMBDA_BETA; LE_REFL] THEN + UNDISCH_TAC `&0 < c` THEN CONV_TAC REAL_FIELD; + ALL_TAC] THEN + SUBGOAL_THEN + `(\h:complex. lift((&2 * (z dot h)) / c)) = + (\h. inv(c) % (\h. &2 % lift(z dot h)) h + (\h:complex. vec 0) h)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; VECTOR_MUL_COMPONENT; + VECTOR_ADD_COMPONENT; VEC_COMPONENT; lift; LAMBDA_BETA; LE_REFL; + real_div; REAL_MUL_ASSOC] THEN + REAL_ARITH_TAC; + MATCH_MP_TAC HAS_DERIVATIVE_ADD THEN + REWRITE_TAC[HAS_DERIVATIVE_CONST] THEN + MATCH_MP_TAC HAS_DERIVATIVE_CMUL THEN + REWRITE_TAC[HAS_DERIVATIVE_SQNORM_AT]]; + MP_TAC(SPEC `(norm(z:complex) pow 2 - R pow 2) / c` + HAS_REAL_DERIVATIVE_HERMITE_EXPLICIT) THEN + REWRITE_TAC[HAS_REAL_FRECHET_DERIVATIVE_AT; o_DEF; LIFT_DROP]]);; + +(* Helper lemmas for continuity of composed functions *) +let HERMITE_CUTOFF_LE_0 = prove + (`!t. t <= &0 ==> hermite_cutoff t = &1`, + GEN_TAC THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + ASM_REWRITE_TAC[]);; + +let HERMITE_CUTOFF_GE_1 = prove + (`!t. &1 <= t ==> hermite_cutoff t = &0`, + GEN_TAC THEN DISCH_TAC THEN + REWRITE_TAC[hermite_cutoff] THEN + COND_CASES_TAC THENL [ASM_REAL_ARITH_TAC; ASM_REWRITE_TAC[]]);; + +let HERMITE_CUTOFF_NONNEG = prove + (`!t. &0 <= hermite_cutoff t`, + GEN_TAC THEN + ASM_CASES_TAC `t <= &0` THENL + [ASM_SIMP_TAC[HERMITE_CUTOFF_LE_0] THEN REAL_ARITH_TAC; ALL_TAC] THEN + ASM_CASES_TAC `&1 <= t` THENL + [ASM_SIMP_TAC[HERMITE_CUTOFF_GE_1] THEN REAL_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[hermite_cutoff] THEN ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN + `&2 * t pow 3 - &3 * t pow 2 + &1 = (&1 - t) pow 2 * (&1 + &2 * t)` + SUBST1_TAC THENL + [CONV_TAC REAL_RING; ALL_TAC] THEN + MATCH_MP_TAC REAL_LE_MUL THEN CONJ_TAC THENL + [MATCH_MP_TAC REAL_POW_LE THEN ASM_REAL_ARITH_TAC; + ASM_REAL_ARITH_TAC]);; + +(* ========================================================================= *) +(* C^1 extension from open sets: weaken f differentiable_on (:complex) to *) +(* f differentiable_on u for an open set u containing the compact set K. *) +(* Approach: cover K by finitely many balls inside u, build C^1 ball bumps, *) +(* sum them, compose with hermite_cutoff to get a C^1 cutoff chi = 1 on K *) +(* with support inside u, then ef = chi * f extends to all of C. *) +(* ========================================================================= *) + +(* Shifted version: derivative of hermite_cutoff((|w-a|^2 - R^2)/c) *) +let HERMITE_CUTOFF_SHIFTED_HAS_DERIVATIVE = prove + (`!a:complex R c (z:complex). + &0 < c + ==> ((\w. lift(hermite_cutoff((norm(w - a) pow 2 - R pow 2) / c))) + has_derivative + (\h:complex. lift(hermite_cutoff_deriv + ((norm(z - a) pow 2 - R pow 2) / c) * + (&2 * ((z - a) dot h)) / c))) (at z)`, + REPEAT STRIP_TAC THEN + MP_TAC(ISPECL + [`\w:complex. w - a`; + `\w:complex. lift(hermite_cutoff((norm w pow 2 - R pow 2) / c))`; + `\h:complex. h`; + `\h:complex. lift(hermite_cutoff_deriv + ((norm(z - a:complex) pow 2 - R pow 2) / c) * + (&2 * ((z - a) dot h)) / c)`; + `z:complex`] + DIFF_CHAIN_AT) THEN + REWRITE_TAC[o_DEF] THEN + DISCH_THEN MATCH_MP_TAC THEN CONJ_TAC THENL + [SUBGOAL_THEN `(\h:complex. h) = (\h. h - (vec 0):complex)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; VECTOR_SUB_RZERO]; ALL_TAC] THEN + MATCH_MP_TAC HAS_DERIVATIVE_SUB THEN + REWRITE_TAC[HAS_DERIVATIVE_ID; HAS_DERIVATIVE_CONST]; + MP_TAC(SPECL [`R:real`; `c:real`; `z - a:complex`] + HERMITE_CUTOFF_CHI_HAS_DERIVATIVE) THEN + ASM_REWRITE_TAC[]]);; + +(* Continuity of shifted phi *) +let PHI_SHIFTED_CONTINUOUS_ON = prove + (`!a:complex c R s. &0 < c ==> + (\z. lift((norm(z - a) pow 2 - R pow 2) / c)) continuous_on s`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\z:complex. lift((norm(z - a) pow 2 - R pow 2) / c)) = + (\z. inv(c) % lift((z - a) dot (z - a)) + lift(--(R pow 2 / c)))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; NORM_POW_2] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; VECTOR_ADD_COMPONENT; + VECTOR_MUL_COMPONENT; lift; LAMBDA_BETA; LE_REFL] THEN + UNDISCH_TAC `&0 < c` THEN CONV_TAC REAL_FIELD; + MATCH_MP_TAC CONTINUOUS_ON_ADD THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_CMUL THEN + MATCH_MP_TAC CONTINUOUS_ON_LIFT_DOT2 THEN + CONJ_TAC THEN MATCH_MP_TAC CONTINUOUS_ON_SUB THEN + REWRITE_TAC[CONTINUOUS_ON_ID; CONTINUOUS_ON_CONST]; + REWRITE_TAC[CONTINUOUS_ON_CONST]]]);; + +(* Continuity of hcd(shifted phi) and hc(shifted phi) *) +let HCD_SHIFTED_CONTINUOUS_ON = prove + (`!a:complex c R s. &0 < c ==> + (\z. lift(hermite_cutoff_deriv((norm(z - a) pow 2 - R pow 2) / c))) + continuous_on s`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\z:complex. lift(hermite_cutoff_deriv + ((norm(z - a) pow 2 - R pow 2) / c))) = + (lift o hermite_cutoff_deriv o drop) o + (\z. lift((norm(z - a) pow 2 - R pow 2) / c))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [ASM_SIMP_TAC[PHI_SHIFTED_CONTINUOUS_ON]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; HERMITE_CUTOFF_DERIV_CONTINUOUS]]);; + +let HC_SHIFTED_CONTINUOUS_ON = prove + (`!a:complex c R s. &0 < c ==> + (\z. lift(hermite_cutoff((norm(z - a) pow 2 - R pow 2) / c))) + continuous_on s`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `(\z:complex. lift(hermite_cutoff + ((norm(z - a) pow 2 - R pow 2) / c))) = + (lift o hermite_cutoff o drop) o + (\z. lift((norm(z - a) pow 2 - R pow 2) / c))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [ASM_SIMP_TAC[PHI_SHIFTED_CONTINUOUS_ON]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; HERMITE_CUTOFF_CONTINUOUS]]);; + +(* ========================================================================= *) +(* C^1 extension from open sets. *) +(* Given compact K in open u, extend C^1 function on u to C^1 on all of C *) +(* with compact support inside u. Uses finite ball cover + hermite cutoff. *) +(* ========================================================================= *) + +let SMOOTH_EXTENSION_FROM_OPEN = prove + (`!f:complex->complex u (K:complex->bool). + compact K /\ open u /\ K SUBSET u /\ + f differentiable_on u /\ + (!h:complex. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> ?ef:complex->complex. + ef differentiable_on (:complex) /\ + (!h:complex. (\z. frechet_derivative ef (at z) h) + continuous_on (:complex)) /\ + bounded(support (+) ef (:complex)) /\ + (!z. z IN K ==> ef z = f z)`, + REPEAT STRIP_TAC THEN + (* Step 1: Lebesgue number *) + MP_TAC(ISPEC `K:complex->bool` HEINE_BOREL_LEMMA) THEN + ASM_REWRITE_TAC[] THEN + DISCH_THEN(MP_TAC o SPEC `{u:complex->bool}`) THEN + REWRITE_TAC[IN_SING; UNIONS_1; UNWIND_THM2] THEN + ANTS_TAC THENL [ASM_MESON_TAC[]; ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `e:real` STRIP_ASSUME_TAC) THEN + (* Step 2: Finite e/3-net A covering K *) + MP_TAC(ISPEC `K:complex->bool` COMPACT_IMP_TOTALLY_BOUNDED) THEN + ASM_REWRITE_TAC[] THEN + DISCH_THEN(MP_TAC o SPEC `e / &3`) THEN + ASM_SIMP_TAC[REAL_LT_DIV; REAL_OF_NUM_LT; ARITH] THEN + DISCH_THEN(X_CHOOSE_THEN `A:complex->bool` STRIP_ASSUME_TAC) THEN + (* Step 3: Parameters r = e/3, cc = 3r^2 *) + ABBREV_TAC `r = e / &3` THEN + ABBREV_TAC `cc = &3 * r pow 2` THEN + SUBGOAL_THEN `&0 < r` ASSUME_TAC THENL + [EXPAND_TAC "r" THEN ASM_REAL_ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN `&0 < cc` ASSUME_TAC THENL + [EXPAND_TAC "cc" THEN MATCH_MP_TAC REAL_LT_MUL THEN + ASM_SIMP_TAC[REAL_POW_LT] THEN REAL_ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN `r pow 2 + cc = (&2 * r) pow 2` ASSUME_TAC THENL + [EXPAND_TAC "cc" THEN REWRITE_TAC[REAL_POW_MUL; REAL_POW_2] THEN + REAL_ARITH_TAC; ALL_TAC] THEN + (* Step 4: cball(a, 2r) SUBSET u for each a IN A *) + SUBGOAL_THEN `!a:complex. a IN A ==> cball(a, &2 * r) SUBSET u` + ASSUME_TAC THENL + [X_GEN_TAC `a:complex` THEN DISCH_TAC THEN + MATCH_MP_TAC SUBSET_TRANS THEN EXISTS_TAC `ball(a:complex, e)` THEN + CONJ_TAC THENL + [REWRITE_TAC[SUBSET; IN_CBALL; IN_BALL] THEN GEN_TAC THEN + MATCH_MP_TAC(REAL_ARITH `t < e ==> x <= t ==> x < e`) THEN + EXPAND_TAC "r" THEN ASM_REAL_ARITH_TAC; + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM SET_TAC[]]; + ALL_TAC] THEN + (* Step 5: W = union of cballs, compact, W SUBSET u *) + ABBREV_TAC `W = UNIONS(IMAGE (\a:complex. cball(a, &2 * r)) A)` THEN + SUBGOAL_THEN `(W:complex->bool) SUBSET u` ASSUME_TAC THENL + [EXPAND_TAC "W" THEN REWRITE_TAC[UNIONS_SUBSET; FORALL_IN_IMAGE] THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN `compact(W:complex->bool)` ASSUME_TAC THENL + [EXPAND_TAC "W" THEN MATCH_MP_TAC COMPACT_UNIONS THEN + ASM_SIMP_TAC[FINITE_IMAGE; FORALL_IN_IMAGE; COMPACT_CBALL]; + ALL_TAC] THEN + SUBGOAL_THEN `open((:complex) DIFF W)` ASSUME_TAC THENL + [MATCH_MP_TAC OPEN_DIFF THEN REWRITE_TAC[OPEN_UNIV] THEN + ASM_MESON_TAC[COMPACT_IMP_CLOSED]; ALL_TAC] THEN + (* Step 6: Bump = 0 when dist >= 2r *) + SUBGOAL_THEN + `!z:complex a:complex. + &2 * r <= norm(z - a) + ==> hermite_cutoff((norm(z - a) pow 2 - r pow 2) / cc) = &0` + ASSUME_TAC THENL + [REPEAT STRIP_TAC THEN MATCH_MP_TAC HERMITE_CUTOFF_GE_1 THEN + ASM_SIMP_TAC[REAL_LE_RDIV_EQ; REAL_MUL_LID] THEN + SUBGOAL_THEN `(&2 * r) pow 2 <= norm(z - a:complex) pow 2` + MP_TAC THENL + [MATCH_MP_TAC REAL_POW_LE2 THEN ASM_REAL_ARITH_TAC; ALL_TAC] THEN + ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + (* Step 7: Bump = 1 when dist <= r *) + SUBGOAL_THEN + `!z:complex a:complex. + norm(z - a) <= r + ==> hermite_cutoff((norm(z - a) pow 2 - r pow 2) / cc) = &1` + ASSUME_TAC THENL + [REPEAT STRIP_TAC THEN MATCH_MP_TAC HERMITE_CUTOFF_LE_0 THEN + SUBGOAL_THEN `norm(z - a:complex) pow 2 <= r pow 2` MP_TAC THENL + [MATCH_MP_TAC REAL_POW_LE2 THEN ASM_SIMP_TAC[NORM_POS_LE]; + ALL_TAC] THEN + REWRITE_TAC[real_div; REAL_ARITH `x * y <= &0 <=> &0 <= --(x * y)`; + GSYM REAL_MUL_LNEG] THEN + DISCH_TAC THEN MATCH_MP_TAC REAL_LE_MUL THEN + ASM_REWRITE_TAC[REAL_LE_INV_EQ] THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + (* Step 8: All bumps are nonneg *) + SUBGOAL_THEN + `!z:complex a:complex. + &0 <= hermite_cutoff((norm(z - a) pow 2 - r pow 2) / cc)` + ASSUME_TAC THENL + [REWRITE_TAC[HERMITE_CUTOFF_NONNEG]; ALL_TAC] THEN + (* Step 9: ef = vec 0 outside W *) + SUBGOAL_THEN + `!z:complex. ~(z IN W) + ==> hermite_cutoff(&1 - sum A (\a:complex. + hermite_cutoff((norm(z - a) pow 2 - r pow 2) / cc))) % + (f:complex->complex) z = vec 0` + ASSUME_TAC THENL + [X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + (* Each cball(a, 2r) is a subset of W *) + SUBGOAL_THEN `!b:complex. b IN A ==> cball(b, &2 * r) SUBSET W` + ASSUME_TAC THENL + [X_GEN_TAC `b:complex` THEN DISCH_TAC THEN EXPAND_TAC "W" THEN + REWRITE_TAC[UNIONS_IMAGE; SUBSET; IN_ELIM_THM] THEN + X_GEN_TAC `y:complex` THEN DISCH_TAC THEN + EXISTS_TAC `b:complex` THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* All bumps are 0 *) + SUBGOAL_THEN + `sum A (\a:complex. hermite_cutoff( + (norm(z - a) pow 2 - r pow 2) / cc)) = &0` + (fun th -> REWRITE_TAC[th; REAL_SUB_RZERO]) THENL + [MATCH_MP_TAC SUM_EQ_0 THEN + X_GEN_TAC `a:complex` THEN DISCH_TAC THEN BETA_TAC THEN + FIRST_X_ASSUM(fun bz -> MATCH_MP_TAC bz) THEN + SUBGOAL_THEN `~(z:complex IN cball(a, &2 * r))` MP_TAC THENL + [SUBGOAL_THEN `cball(a:complex, &2 * r) SUBSET W` MP_TAC THENL + [FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]; + ASM_MESON_TAC[SUBSET]]; + REWRITE_TAC[IN_CBALL; dist; NORM_SUB; REAL_NOT_LE] THEN + REAL_ARITH_TAC]; + ALL_TAC] THEN + SUBGOAL_THEN `hermite_cutoff(&1) = &0` SUBST1_TAC THENL + [MATCH_MP_TAC HERMITE_CUTOFF_GE_1 THEN REAL_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[VECTOR_MUL_LZERO]; + ALL_TAC] THEN + (* Step 10: sigma >= 1 on K *) + SUBGOAL_THEN + `!z:complex. z IN K + ==> &1 <= sum A (\a. hermite_cutoff( + (norm(z - a) pow 2 - r pow 2) / cc))` + ASSUME_TAC THENL + [X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + SUBGOAL_THEN `?a:complex. a IN A /\ norm(z - a) < r` MP_TAC THENL + [EXPAND_TAC "r" THEN + SUBGOAL_THEN + `(z:complex) IN UNIONS(IMAGE (\x:complex. ball(x, e / &3)) A)` + MP_TAC THENL + [ASM_MESON_TAC[SUBSET]; ALL_TAC] THEN + REWRITE_TAC[UNIONS_IMAGE; IN_ELIM_THM] THEN + REWRITE_TAC[IN_BALL; dist] THEN + MESON_TAC[NORM_SUB]; + ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `a0:complex` STRIP_ASSUME_TAC) THEN + MATCH_MP_TAC REAL_LE_TRANS THEN + EXISTS_TAC `sum {a0:complex} (\a. hermite_cutoff( + (norm(z - a) pow 2 - r pow 2) / cc))` THEN + CONJ_TAC THENL + [REWRITE_TAC[SUM_SING] THEN BETA_TAC THEN + SUBGOAL_THEN + `hermite_cutoff((norm(z - a0:complex) pow 2 - r pow 2) / cc) = &1` + (fun th -> REWRITE_TAC[th; REAL_LE_REFL]) THEN + FIRST_X_ASSUM(fun bz -> MATCH_MP_TAC bz) THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + MATCH_MP_TAC SUM_SUBSET_SIMPLE THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ASM_REWRITE_TAC[IN_DIFF] THEN ASM SET_TAC[]; + ALL_TAC] THEN + (* EXISTS_TAC with the extension function *) + EXISTS_TAC + `\w:complex. + hermite_cutoff(&1 - sum A (\a:complex. + hermite_cutoff((norm(w - a) pow 2 - r pow 2) / cc))) % + (f:complex->complex) w` THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + REPEAT CONJ_TAC THENL + [(* Property 1: ef differentiable_on (:complex) *) + SIMP_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT; + OPEN_UNIV; IN_UNIV] THEN + X_GEN_TAC `z:complex` THEN + ASM_CASES_TAC `(z:complex) IN u` THENL + [(* z IN u: product rule *) + MATCH_MP_TAC DIFFERENTIABLE_MUL_AT THEN CONJ_TAC THENL + [(* Scalar part: (lift o chi) differentiable at z *) + SUBGOAL_THEN + `(lift o (\w:complex. hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))) = + (lift o hermite_cutoff o drop) o + (\w:complex. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC DIFFERENTIABLE_CHAIN_AT THEN CONJ_TAC THENL + [(* Inner: lift(1 - sigma) differentiable at z *) + SUBGOAL_THEN + `(\w:complex. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc)))) = + (\w. lift(&1) - vsum A (\a:complex. lift(hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; LIFT_SUB; LIFT_SUM; o_DEF]; + ALL_TAC] THEN + MATCH_MP_TAC DIFFERENTIABLE_SUB THEN CONJ_TAC THENL + [REWRITE_TAC[DIFFERENTIABLE_CONST]; + MATCH_MP_TAC DIFFERENTIABLE_VSUM THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `a:complex` THEN DISCH_TAC THEN + REWRITE_TAC[differentiable] THEN + EXISTS_TAC `\h:complex. lift( + hermite_cutoff_deriv( + (norm(z - a) pow 2 - r pow 2) / cc) * + (&2 * ((z - a) dot h)) / cc)` THEN + MATCH_MP_TAC HERMITE_CUTOFF_SHIFTED_HAS_DERIVATIVE THEN + ASM_REWRITE_TAC[]]; + (* Outer: (lift o hc o drop) differentiable *) + REWRITE_TAC[HERMITE_CUTOFF_DIFFERENTIABLE]]; + (* f differentiable at z *) + UNDISCH_TAC `(f:complex->complex) differentiable_on u` THEN + ASM_SIMP_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT] THEN + DISCH_THEN(MP_TAC o SPEC `z:complex`) THEN + ASM_REWRITE_TAC[]]; + (* z NOT IN u: ef locally zero *) + SUBGOAL_THEN `~((z:complex) IN W)` ASSUME_TAC THENL + [ASM SET_TAC[]; ALL_TAC] THEN + REWRITE_TAC[differentiable] THEN + EXISTS_TAC `(\h:complex. (vec 0):complex)` THEN + MATCH_MP_TAC HAS_DERIVATIVE_TRANSFORM_WITHIN_OPEN THEN + EXISTS_TAC `(\w:complex. (vec 0):complex)` THEN + EXISTS_TAC `(:complex) DIFF W` THEN + ASM_REWRITE_TAC[IN_DIFF; IN_UNIV] THEN CONJ_TAC THENL + [X_GEN_TAC `y:complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN CONV_TAC SYM_CONV THEN ASM_MESON_TAC[]; + REWRITE_TAC[HAS_DERIVATIVE_CONST]]]; + (* Property 2: continuous frechet_derivative *) + GEN_TAC THEN + (* SUBGOAL A: chi has_derivative [chain rule] at every z *) + SUBGOAL_THEN + `!z':complex. + ((\w:complex. lift(hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))) + has_derivative + (\h':complex. hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc))))) + (at z')` + ASSUME_TAC THENL + [X_GEN_TAC `z':complex` THEN + SUBGOAL_THEN + `(\w:complex. lift(hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))) = + (lift o hermite_cutoff o drop) o + (\w. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h':complex. hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc)))) = + (\v:real^1. hermite_cutoff_deriv( + drop(lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))) % v) o + (\h'. (vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc)))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_AT THEN CONJ_TAC THENL + [(* Inner: (\w. lift(1 - sigma(w))) has_derivative *) + SUBGOAL_THEN + `(\w:complex. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc)))) = + (\w. lift(&1) - vsum A (\a:complex. lift(hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; LIFT_SUB; LIFT_SUM; o_DEF]; + ALL_TAC] THEN + MATCH_MP_TAC HAS_DERIVATIVE_SUB THEN CONJ_TAC THENL + [REWRITE_TAC[HAS_DERIVATIVE_CONST]; + MATCH_MP_TAC HAS_DERIVATIVE_VSUM THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `a:complex` THEN DISCH_TAC THEN + MATCH_MP_TAC HERMITE_CUTOFF_SHIFTED_HAS_DERIVATIVE THEN + ASM_REWRITE_TAC[]]; + (* Outer: (lift o hc o drop) has_derivative at inner point *) + MP_TAC(SPEC `&1 - sum A (\a:complex. hermite_cutoff( + (norm(z' - a:complex) pow 2 - r pow 2) / cc))` + HAS_REAL_DERIVATIVE_HERMITE_EXPLICIT) THEN + REWRITE_TAC[HAS_REAL_FRECHET_DERIVATIVE_AT; o_DEF; LIFT_DROP]]; + ALL_TAC] THEN + (* SUBGOAL B: chi derivative continuous on (:complex) *) + SUBGOAL_THEN + `(\z':complex. + hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h)) / cc)))) + continuous_on (:complex)` + ASSUME_TAC THENL + [(* hcd(1-sigma(z)) % (vec 0 - vsum A (...)) continuous *) + MATCH_MP_TAC CONTINUOUS_ON_MUL THEN CONJ_TAC THENL + [(* (lift o hcd(1-sigma)) continuous *) + REWRITE_TAC[o_DEF] THEN + SUBGOAL_THEN + `(\z':complex. lift(hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))) = + (lift o hermite_cutoff_deriv o drop) o + (\z'. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [(* (\z'. lift(1 - sigma(z'))) continuous *) + SUBGOAL_THEN + `(\z':complex. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc)))) = + (\z'. lift(&1) - vsum A (\a:complex. lift(hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; LIFT_SUB; LIFT_SUM; o_DEF]; + ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUB THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_VSUM THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `a:complex` THEN DISCH_TAC THEN + ASM_SIMP_TAC[HC_SHIFTED_CONTINUOUS_ON]]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; HERMITE_CUTOFF_DERIV_CONTINUOUS]]; + (* vec 0 - vsum A (\a. lift(hcd * stuff)) continuous *) + MATCH_MP_TAC CONTINUOUS_ON_SUB THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_VSUM THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `a:complex` THEN DISCH_TAC THEN + (* lift(hcd(phi_a(z)) * (2*(z-a) dot h)/cc) *) + SUBGOAL_THEN + `(\z':complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h)) / cc)) = + (\z'. hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) % + lift((&2 * ((z' - a) dot h)) / cc))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; + VECTOR_MUL_COMPONENT; lift; LAMBDA_BETA; LE_REFL] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_MUL THEN CONJ_TAC THENL + [REWRITE_TAC[o_DEF] THEN + ASM_SIMP_TAC[HCD_SHIFTED_CONTINUOUS_ON]; + SUBGOAL_THEN + `(\z':complex. lift((&2 * ((z' - a) dot h)) / cc)) = + (\z'. (inv cc * &2) % lift((z' - a) dot h))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + SIMP_TAC[CART_EQ; DIMINDEX_1; FORALL_1; + VECTOR_MUL_COMPONENT; lift; LAMBDA_BETA; LE_REFL] THEN + UNDISCH_TAC `&0 < cc` THEN CONV_TAC REAL_FIELD; + MATCH_MP_TAC CONTINUOUS_ON_CMUL THEN + MATCH_MP_TAC CONTINUOUS_ON_LIFT_DOT2 THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUB THEN + REWRITE_TAC[CONTINUOUS_ON_ID; CONTINUOUS_ON_CONST]; + REWRITE_TAC[CONTINUOUS_ON_CONST]]]]]]; + ALL_TAC] THEN + (* SUBGOAL C: continuous_on u *) + SUBGOAL_THEN + `(\z':complex. + frechet_derivative + (\w. hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))) % + (f:complex->complex) w) (at z') h) + continuous_on u` + ASSUME_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_EQ THEN + EXISTS_TAC + `\z':complex. + hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + frechet_derivative (f:complex->complex) (at z') h + + drop(hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h)) / cc)))) % + (f:complex->complex) z'` THEN + CONJ_TAC THENL + [(* Formula equals frechet_derivative on u *) + X_GEN_TAC `z':complex` THEN DISCH_TAC THEN + CONV_TAC SYM_CONV THEN + MP_TAC(ISPECL + [`\z'':complex. hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z'' - a) pow 2 - r pow 2) / cc))) % + (f:complex->complex) z''`; + `\h':complex. + hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + frechet_derivative (f:complex->complex) (at z') h' + + drop(hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc)))) % + (f:complex->complex) z'`; + `z':complex`] + FRECHET_DERIVATIVE_AT) THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ANTS_TAC THENL + [(* Prove ef has_derivative [formula] at z' *) + MP_TAC(ISPECL + [`\z'':complex. hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z'' - a) pow 2 - r pow 2) / cc)))`; + `\h':complex. + drop(hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc))))`; + `f:complex->complex`; + `frechet_derivative (f:complex->complex) (at z')`; + `z':complex`] + HAS_DERIVATIVE_MUL_AT) THEN + REWRITE_TAC[o_DEF] THEN + DISCH_THEN MATCH_MP_TAC THEN CONJ_TAC THENL + [(* (lift o chi) has_derivative (lift o chi') at z' *) + SUBGOAL_THEN + `(\h':complex. + lift(drop(hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc)))))) = + (\h':complex. + hermite_cutoff_deriv(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))) % + ((vec 0):real^1 - vsum A (\a:complex. lift( + hermite_cutoff_deriv( + (norm(z' - a) pow 2 - r pow 2) / cc) * + (&2 * ((z' - a) dot h')) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; LIFT_DROP]; ALL_TAC] THEN + FIRST_X_ASSUM(MP_TAC o SPEC `z':complex`) THEN + REWRITE_TAC[]; + (* f has_derivative at z' *) + REWRITE_TAC[GSYM FRECHET_DERIVATIVE_WORKS] THEN + UNDISCH_TAC `(f:complex->complex) differentiable_on u` THEN + ASM_SIMP_TAC[DIFFERENTIABLE_ON_EQ_DIFFERENTIABLE_AT] THEN + DISCH_THEN(MP_TAC o SPEC `z':complex`) THEN + ASM_REWRITE_TAC[]]; + (* Use equation to close equality goal *) + DISCH_THEN(fun th -> REWRITE_TAC[GSYM th]) THEN REFL_TAC]; + (* Formula continuous on u *) + MATCH_MP_TAC CONTINUOUS_ON_ADD THEN CONJ_TAC THENL + [(* Term 1: hc(1-sigma) % Df h *) + MATCH_MP_TAC CONTINUOUS_ON_MUL THEN CONJ_TAC THENL + [(* (lift o hc(1-sigma)) continuous on u *) + REWRITE_TAC[o_DEF] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + SUBGOAL_THEN + `(\z':complex. lift(hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))) = + (lift o hermite_cutoff o drop) o + (\z'. lift(&1 - sum A (\a:complex. hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM; LIFT_DROP]; ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN CONJ_TAC THENL + [SUBGOAL_THEN + `(\z':complex. lift(&1 - sum A (\a:complex. + hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc)))) = + (\z'. lift(&1) - vsum A (\a:complex. lift( + hermite_cutoff( + (norm(z' - a) pow 2 - r pow 2) / cc))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; LIFT_SUB; LIFT_SUM; o_DEF]; + ALL_TAC] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUB THEN CONJ_TAC THENL + [REWRITE_TAC[CONTINUOUS_ON_CONST]; + MATCH_MP_TAC CONTINUOUS_ON_VSUM THEN + ASM_REWRITE_TAC[] THEN + X_GEN_TAC `a:complex` THEN DISCH_TAC THEN + ASM_SIMP_TAC[HC_SHIFTED_CONTINUOUS_ON]]; + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:real^1)` THEN + REWRITE_TAC[SUBSET_UNIV; HERMITE_CUTOFF_CONTINUOUS]]; + (* frechet_derivative f h continuous on u *) + ASM_REWRITE_TAC[]]; + (* Term 2: drop(D(h)) % f *) + MATCH_MP_TAC CONTINUOUS_ON_MUL THEN CONJ_TAC THENL + [(* (lift o drop(D(h))) = D(h) continuous on u *) + REWRITE_TAC[o_DEF] THEN + REWRITE_TAC[LIFT_DROP] THEN + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + ASM_REWRITE_TAC[SUBSET_UNIV]; + (* f continuous on u *) + MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `u:complex->bool` THEN + REWRITE_TAC[SUBSET_REFL] THEN + MATCH_MP_TAC DIFFERENTIABLE_IMP_CONTINUOUS_ON THEN + ASM_REWRITE_TAC[]]]]; + ALL_TAC] THEN + (* SUBGOAL D: continuous_on ((:complex) DIFF W) *) + SUBGOAL_THEN + `(\z':complex. + frechet_derivative + (\w. hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))) % + (f:complex->complex) w) (at z') h) + continuous_on ((:complex) DIFF W)` + ASSUME_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_EQ THEN + EXISTS_TAC `(\z':complex. (vec 0):complex)` THEN + CONJ_TAC THENL + [X_GEN_TAC `z':complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN CONV_TAC SYM_CONV THEN + MP_TAC(ISPECL + [`\w:complex. hermite_cutoff(&1 - + sum A (\a:complex. hermite_cutoff( + (norm(w - a) pow 2 - r pow 2) / cc))) % + (f:complex->complex) w`; + `\h':complex. (vec 0):complex`; + `z':complex`] + FRECHET_DERIVATIVE_AT) THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ANTS_TAC THENL + [(* Prove ef has_derivative vec 0 at z' *) + MATCH_MP_TAC HAS_DERIVATIVE_TRANSFORM_WITHIN_OPEN THEN + EXISTS_TAC `(\w:complex. (vec 0):complex)` THEN + EXISTS_TAC `(:complex) DIFF W` THEN + ASM_REWRITE_TAC[IN_DIFF; IN_UNIV] THEN CONJ_TAC THENL + [X_GEN_TAC `y:complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN CONV_TAC SYM_CONV THEN ASM_MESON_TAC[]; + REWRITE_TAC[HAS_DERIVATIVE_CONST]]; + (* Use equation to close equality goal *) + DISCH_THEN(fun th -> REWRITE_TAC[GSYM th]) THEN REWRITE_TAC[]]; + REWRITE_TAC[CONTINUOUS_ON_CONST]]; + ALL_TAC] THEN + (* Combine: continuity on (:complex) *) + SIMP_TAC[CONTINUOUS_ON_EQ_CONTINUOUS_AT; OPEN_UNIV; IN_UNIV] THEN + X_GEN_TAC `w:complex` THEN + ASM_CASES_TAC `(w:complex) IN u` THENL + [ASM_MESON_TAC[CONTINUOUS_ON_EQ_CONTINUOUS_AT]; + SUBGOAL_THEN `(w:complex) IN (:complex) DIFF W` ASSUME_TAC THENL + [REWRITE_TAC[IN_DIFF; IN_UNIV] THEN ASM SET_TAC[]; + ALL_TAC] THEN + ASM_MESON_TAC[CONTINUOUS_ON_EQ_CONTINUOUS_AT]]; + (* Property 3: bounded support *) + MATCH_MP_TAC BOUNDED_SUBSET THEN + EXISTS_TAC `W:complex->bool` THEN CONJ_TAC THENL + [ASM_MESON_TAC[COMPACT_IMP_BOUNDED]; ALL_TAC] THEN + REWRITE_TAC[SUBSET; support; IN_ELIM_THM; NEUTRAL_VECTOR_ADD; + IN_UNIV] THEN + ASM_MESON_TAC[]; + (* Property 4: ef = f on K *) + X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + SUBGOAL_THEN + `&1 <= sum A (\a:complex. hermite_cutoff( + (norm(z - a) pow 2 - r pow 2) / cc))` + ASSUME_TAC THENL + [FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN + `hermite_cutoff(&1 - sum A (\a:complex. + hermite_cutoff((norm(z - a) pow 2 - r pow 2) / cc))) = &1` + SUBST1_TAC THENL + [MATCH_MP_TAC HERMITE_CUTOFF_LE_0 THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + REWRITE_TAC[VECTOR_MUL_LID]]);; + +(* ========================================================================= *) +(* Product-space absolute integrability for the Cauchy kernel integrand. *) +(* Factored out from FUBINI_PATH_AREA to expose integrability separately. *) +(* Does NOT need simple_path or pathfinish = pathstart. *) +(* ========================================================================= *) + +let ABSOLUTELY_INTEGRABLE_CAUCHY_PATH_PRODUCT = prove + (`!u:complex->complex g:real^1->complex. + u continuous_on (:complex) /\ + bounded (support (+) u (:complex)) /\ + g absolutely_continuous_on interval[vec 0,vec 1] + ==> (\p:real^(1,2)finite_sum. + if fstcart p IN interval[vec 0,vec 1] + then u(sndcart p) / (sndcart p - g(fstcart p)) * + vector_derivative g (at (fstcart p)) + else vec 0) + absolutely_integrable_on (:real^(1,2)finite_sum)`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN + `!w:complex. (\z. u(z:complex) / (z - w)) + absolutely_integrable_on (:complex)` + ASSUME_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + (* Measurability on product space *) + SUBGOAL_THEN + `(\p:real^(1,2)finite_sum. + if fstcart p IN interval[vec 0,vec 1] + then u(sndcart p:complex) / (sndcart p - g(fstcart p)) * + vector_derivative g (at (fstcart p)) + else vec 0) + measurable_on (:real^(1,2)finite_sum)` + MP_TAC THENL + [MATCH_MP_TAC MEASURABLE_ON_FUBINI_INTEGRAND THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + DISCH_THEN(MP_TAC o MATCH_MP FUBINI_TONELLI) THEN + DISCH_THEN(fun th -> REWRITE_TAC[th]) THEN + REWRITE_TAC[FSTCART_PASTECART; SNDCART_PASTECART] THEN + CONJ_TAC THENL + [(* Negligible bad set: every slice is abs integrable *) + SUBGOAL_THEN + `{x:real^1 | ~((\y:complex. + if x IN interval[vec 0,vec 1] + then u y / (y - g x) * vector_derivative g (at x) + else vec 0) + absolutely_integrable_on (:complex))} = {}` + SUBST1_TAC THENL + [REWRITE_TAC[EXTENSION; IN_ELIM_THM; NOT_IN_EMPTY] THEN + X_GEN_TAC `t:real^1` THEN + REWRITE_TAC[TAUT `~(~p) <=> p`] THEN + ASM_CASES_TAC `t:real^1 IN interval[vec 0,vec 1]` THENL + [ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_COMPLEX_RMUL THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[absolutely_integrable_on; + NORM_0; LIFT_NUM; INTEGRABLE_0]]; + REWRITE_TAC[NEGLIGIBLE_EMPTY]]; + (* Norm integral integrability: Tonelli condition *) + SUBGOAL_THEN + `(\x:real^1. integral (:complex) + (\y. lift(norm(if x IN interval[vec 0,vec 1] + then u y / (y - g x) * vector_derivative g (at x) + else vec 0)))) = + (\x. if x IN interval[vec 0,vec 1] + then norm(vector_derivative g (at x)) % + integral (:complex) + (\y. lift(norm(u y / (y - (g:real^1->complex) x)))) + else vec 0)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN X_GEN_TAC `x:real^1` THEN + COND_CASES_TAC THENL + [ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN + `(\y:complex. lift(norm(u y / (y - g(x:real^1)) * + vector_derivative g (at x)))) = + (\y. norm(vector_derivative g (at x)) % + lift(norm(u y / (y - g x))))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + REWRITE_TAC[COMPLEX_NORM_MUL] THEN + ONCE_REWRITE_TAC[REAL_MUL_SYM] THEN + REWRITE_TAC[LIFT_CMUL]; ALL_TAC] THEN + MATCH_MP_TAC INTEGRAL_CMUL THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[NORM_0; LIFT_NUM; INTEGRAL_0]]; ALL_TAC] THEN + REWRITE_TAC[INTEGRABLE_RESTRICT_UNIV] THEN + MP_TAC(ISPECL [`u:complex->complex`; + `path_image(g:real^1->complex)`] CAUCHY_KERNEL_NORM_UNIFORM_BOUND) THEN + ANTS_TAC THENL + [ASM_REWRITE_TAC[] THEN MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `C:real` STRIP_ASSUME_TAC) THEN + MATCH_MP_TAC MEASURABLE_BOUNDED_BY_INTEGRABLE_IMP_INTEGRABLE THEN + EXISTS_TAC `(\x:real^1. C % lift(norm( + vector_derivative (g:real^1->complex) (at x))))` THEN + REPEAT CONJ_TAC THENL + [MATCH_MP_TAC CAUCHY_KERNEL_WEIGHTED_MEASURABLE THEN + ASM_REWRITE_TAC[]; + MATCH_MP_TAC INTEGRABLE_CMUL THEN + MP_TAC(ISPEC `g:real^1->complex` + ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS) THEN + ASM_REWRITE_TAC[] THEN + REWRITE_TAC[absolutely_integrable_on] THEN + STRIP_TAC THEN ASM_REWRITE_TAC[]; + BETA_TAC THEN + X_GEN_TAC `x:real^1` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN DISCH_TAC THEN + REWRITE_TAC[NORM_MUL; real_abs; NORM_POS_LE] THEN + REWRITE_TAC[DROP_CMUL; LIFT_DROP] THEN + GEN_REWRITE_TAC RAND_CONV [REAL_MUL_SYM] THEN + MATCH_MP_TAC REAL_LE_LMUL THEN + CONJ_TAC THENL + [REWRITE_TAC[NORM_POS_LE]; + REWRITE_TAC[NORM_REAL; GSYM drop] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x /\ x <= C ==> abs(x) <= C`) THEN + CONJ_TAC THENL + [MATCH_MP_TAC INTEGRAL_DROP_POS THEN CONJ_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_NORM THEN + MATCH_MP_TAC CAUCHY_KERNEL_ABSOLUTELY_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + REWRITE_TAC[LIFT_DROP; NORM_POS_LE]]; + FIRST_X_ASSUM MATCH_MP_TAC THEN + REWRITE_TAC[path_image; IN_IMAGE] THEN + EXISTS_TAC `x:real^1` THEN + ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC]]]]]);; + +(* ========================================================================= *) +(* Integrability of winding_number * dbar f without simple_path. *) +(* Derived from the Fubini product-space integrability via: *) +(* Fubini abs integ -> has_integral -> integrable -> *) +(* INTEGRABLE_SPIKE -> INTEGRABLE_COMPLEX_LMUL_EQ *) +(* ========================================================================= *) + +let INTEGRABLE_WINDING_DBAR_PRODUCT = prove + (`!f:complex->complex g:real^1->complex. + (\z. wirtinger_dbar f z) continuous_on (:complex) /\ + bounded (support (+) (wirtinger_dbar f) (:complex)) /\ + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g + ==> (\z. Cx(Re(winding_number(g,z))) * + wirtinger_dbar f z) integrable_on (:complex)`, + REPEAT STRIP_TAC THEN + (* Step 1: Product-space absolute integrability via Fubini *) + MP_TAC(ISPECL + [`\z:complex. wirtinger_dbar (f:complex->complex) z`; + `g:real^1->complex`] + ABSOLUTELY_INTEGRABLE_CAUCHY_PATH_PRODUCT) THEN + RULE_ASSUM_TAC(REWRITE_RULE[ETA_AX]) THEN + REWRITE_TAC[ETA_AX] THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + FIRST_ASSUM(MP_TAC o MATCH_MP FUBINI_ABSOLUTELY_INTEGRABLE_ALT) THEN + REWRITE_TAC[FSTCART_PASTECART; SNDCART_PASTECART] THEN + DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC) THEN + REWRITE_TAC[INTEGRAL_IF_CONST; INTEGRAL_RESTRICT_UNIV] THEN + DISCH_THEN(fun th -> MP_TAC(MATCH_MP HAS_INTEGRAL_INTEGRABLE th)) THEN + DISCH_TAC THEN + (* Step 2: INTEGRABLE_SPIKE: Fubini integrand = -(2*pi*i)*(wn*dbar) off path *) + SUBGOAL_THEN + `(\z:complex. --(Cx(&2) * Cx pi * ii) * + (Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (f:complex->complex) z)) + integrable_on (:complex)` ASSUME_TAC THENL + [MP_TAC(REWRITE_RULE[IMP_IMP] (ISPECL [ + `(\z:complex. integral (interval[vec 0,vec 1]) + (\t:real^1. wirtinger_dbar (f:complex->complex) z / + (z - (g:real^1->complex) t) * + vector_derivative g (at t)))`; + `(\z:complex. --(Cx(&2) * Cx pi * ii) * + (Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (f:complex->complex) z))`; + `path_image(g:real^1->complex)`; + `(:complex)`] INTEGRABLE_SPIKE)) THEN + DISCH_THEN MATCH_MP_TAC THEN + REPEAT CONJ_TAC THENL + [ASM_MESON_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE]; + (* Pointwise equality off path *) + X_GEN_TAC `z:complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN BETA_TAC THEN CONV_TAC SYM_CONV THEN + (* Winding number path_integral facts *) + MP_TAC(ISPECL [`g:real^1->complex`; `z:complex`] + HAS_PATH_INTEGRAL_WINDING_NUMBER_AC) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + FIRST_ASSUM(ASSUME_TAC o MATCH_MP HAS_PATH_INTEGRAL_INTEGRABLE) THEN + FIRST_ASSUM(ASSUME_TAC o MATCH_MP PATH_INTEGRAL_UNIQUE) THEN + (* Integrability of 1/(gt-z)*vd from path_integrable *) + SUBGOAL_THEN + `(\t:real^1. Cx(&1) / ((g:real^1->complex) t - z) * + vector_derivative g (at t)) + integrable_on interval[vec 0,vec 1]` + ASSUME_TAC THENL + [UNDISCH_TAC `(\w:complex. Cx(&1) / (w - z)) + path_integrable_on (g:real^1->complex)` THEN + REWRITE_TAC[PATH_INTEGRABLE_ON]; + ALL_TAC] THEN + (* Sign flip: 1/(z-gt) = -(1/(gt-z)), proved once *) + SUBGOAL_THEN + `(\t:real^1. Cx(&1) / (z - (g:real^1->complex) t) * + vector_derivative g (at t)) = + (\t. --(Cx(&1) / (g t - z) * vector_derivative g (at t)))` + ASSUME_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; complex_div; COMPLEX_MUL_LID] THEN + X_GEN_TAC `t:real^1` THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [GSYM COMPLEX_NEG_SUB] THEN + REWRITE_TAC[COMPLEX_INV_NEG; COMPLEX_MUL_LNEG]; + ALL_TAC] THEN + (* Factor integrand: dbar/(z-gt)*vd = dbar * (1/(z-gt)*vd) *) + SUBGOAL_THEN + `!t:real^1. wirtinger_dbar (f:complex->complex) z / + (z - (g:real^1->complex) t) * vector_derivative g (at t) = + wirtinger_dbar f z * + (Cx(&1) / (z - g t) * vector_derivative g (at t))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN + REWRITE_TAC[complex_div; COMPLEX_MUL_LID; COMPLEX_MUL_ASSOC]; + ALL_TAC] THEN + (* Factor out dbar z via INTEGRAL_COMPLEX_LMUL *) + SUBGOAL_THEN + `integral (interval[vec 0,vec 1]) + (\t:real^1. wirtinger_dbar (f:complex->complex) z * + (Cx(&1) / (z - (g:real^1->complex) t) * + vector_derivative g (at t))) = + wirtinger_dbar f z * + integral (interval[vec 0,vec 1]) + (\t. Cx(&1) / (z - g t) * vector_derivative g (at t))` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_COMPLEX_LMUL THEN + ASM_REWRITE_TAC[] THEN MATCH_MP_TAC INTEGRABLE_NEG THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* Evaluate inner integral: sign flip + neg + path_integral *) + SUBGOAL_THEN + `integral (interval[vec 0,vec 1]) + (\t:real^1. Cx(&1) / (z - (g:real^1->complex) t) * + vector_derivative g (at t)) = + --(Cx(&2) * Cx pi * ii * + winding_number(g:real^1->complex,z))` + SUBST1_TAC THENL + [ASM_REWRITE_TAC[] THEN ASM_SIMP_TAC[INTEGRAL_NEG] THEN + AP_TERM_TAC THEN + REWRITE_TAC[GSYM PATH_INTEGRAL_INTEGRAL] THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* Replace wn with Cx(Re(wn)) since wn is integer *) + SUBGOAL_THEN + `winding_number(g:real^1->complex,z) = + Cx(Re(winding_number(g,z)))` SUBST1_TAC THENL + [REWRITE_TAC[GSYM REAL; real] THEN + SUBGOAL_THEN + `complex_integer(winding_number(g:real^1->complex,z))` MP_TAC THENL + [MATCH_MP_TAC INTEGER_WINDING_NUMBER THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + REWRITE_TAC[complex_integer] THEN SIMP_TAC[]; + ALL_TAC] THEN + SIMPLE_COMPLEX_ARITH_TAC; + BETA_TAC THEN ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + (* Step 3: Extract constant -(2*pi*i) via INTEGRABLE_COMPLEX_LMUL_EQ *) + UNDISCH_TAC + `(\z:complex. --(Cx(&2) * Cx pi * ii) * + (Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (f:complex->complex) z)) + integrable_on (:complex)` THEN + REWRITE_TAC[INTEGRABLE_COMPLEX_LMUL_EQ] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [UNDISCH_TAC `--(Cx(&2) * Cx pi * ii) = Cx(&0)` THEN + REWRITE_TAC[COMPLEX_NEG_EQ_0] THEN + MP_TAC CX_2PII_NZ THEN MESON_TAC[]; + FIRST_X_ASSUM ACCEPT_TAC]);; + +(* ========================================================================= *) +(* Most general Gauss-Green formula: no simple_path, local C^1. *) +(* Uses smooth extension + Cauchy transform + Fubini. *) +(* ========================================================================= *) + +let COMPLEX_GREEN_ALT = prove + (`!f:complex->complex g u. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + open u /\ + inside(path_image g) UNION path_image g SUBSET u /\ + f differentiable_on u /\ + (!h:complex. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> (\z. Cx(Re(winding_number(g,z))) * + wirtinger_dbar f z) integrable_on (:complex) /\ + f path_integrable_on g /\ + Cx(inv(&2)) / ii * path_integral g f = + integral (:complex) + (\z. Cx(Re(winding_number(g,z))) * + wirtinger_dbar f z)`, + REPEAT GEN_TAC THEN STRIP_TAC THEN + (* Local lemma: Gauss-Green for globally C^1 functions *) + let complex_green_univ = prove + (`!f:complex->complex g. + f differentiable_on (:complex) /\ + (!h. (\z. frechet_derivative f (at z) h) continuous_on (:complex)) /\ + bounded (support (+) f (:complex)) /\ + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g + ==> (\z. Cx(Re(winding_number(g,z))) * + wirtinger_dbar f z) integrable_on (:complex) /\ + f path_integrable_on g /\ + Cx(inv(&2)) / ii * path_integral g f = + integral (:complex) + (\z. Cx(Re(winding_number(g,z))) * + wirtinger_dbar f z)`, + REPEAT GEN_TAC THEN STRIP_TAC THEN + (* Step 1: wirtinger_dbar f is continuous with bounded support *) + SUBGOAL_THEN + `(\z. wirtinger_dbar (f:complex->complex) z) continuous_on (:complex)` + ASSUME_TAC THENL + [MATCH_MP_TAC WIRTINGER_DBAR_CONTINUOUS THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `bounded (support (+) (wirtinger_dbar (f:complex->complex)) (:complex))` + ASSUME_TAC THENL + [MATCH_MP_TAC WIRTINGER_DBAR_BOUNDED_SUPPORT THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* Step 2: The Cauchy transform inverts dbar *) + SUBGOAL_THEN + `!w. integral (:complex) + (\z. wirtinger_dbar (f:complex->complex) z / (z - w)) = + --(Cx pi * f w)` ASSUME_TAC THENL + [GEN_TAC THEN MATCH_MP_TAC CAUCHY_TRANSFORM_INVERTS_DBAR THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* Step 3: Cx(Re(wn))*dbar f integrable + f path integrable *) + SUBGOAL_THEN + `(\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (f:complex->complex) z) integrable_on (:complex)` + ASSUME_TAC THENL + [MATCH_MP_TAC INTEGRABLE_WINDING_DBAR_PRODUCT THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN `(f:complex->complex) path_integrable_on g` ASSUME_TAC THENL + [MATCH_MP_TAC PATH_INTEGRABLE_CONTINUOUS_ABSOLUTELY_CONTINUOUS THEN + CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `(:complex)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + MATCH_MP_TAC DIFFERENTIABLE_IMP_CONTINUOUS_ON THEN ASM_REWRITE_TAC[]; + ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + (* Step 4: The key equation from Fubini + Cauchy transform *) + SUBGOAL_THEN + `--(Cx pi) * path_integral g (f:complex->complex) = + integral (:complex) + (\z. wirtinger_dbar f z * + path_integral g (\w. Cx(&1) / (z - w)))` ASSUME_TAC THENL + [SUBGOAL_THEN + `path_integral g (\w. --(Cx pi) * (f:complex->complex) w) = + --(Cx pi) * path_integral g f` ASSUME_TAC THENL + [MATCH_MP_TAC PATH_INTEGRAL_COMPLEX_LMUL THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `path_integral g + (\w. integral (:complex) + (\z. wirtinger_dbar (f:complex->complex) z / (z - w))) = + integral (:complex) + (\z. wirtinger_dbar f z * + path_integral g (\w. Cx(&1) / (z - w)))` ASSUME_TAC THENL + [MATCH_MP_TAC FUBINI_PATH_AREA THEN + RULE_ASSUM_TAC(REWRITE_RULE[ETA_AX]) THEN + REWRITE_TAC[ETA_AX] THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\w. integral (:complex) + (\z. wirtinger_dbar (f:complex->complex) z / (z - w))) = + (\w. --(Cx pi) * f w)` ASSUME_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN + ASM_REWRITE_TAC[COMPLEX_MUL_LNEG]; + ALL_TAC] THEN + ASM_MESON_TAC[]; + ALL_TAC] THEN + (* Step 5: Combine Fubini equation with winding number *) + SUBGOAL_THEN + `--(Cx pi) * path_integral g (f:complex->complex) = + --(Cx(&2) * Cx pi * ii) * + integral (:complex) + (\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar f z)` ASSUME_TAC THENL + [ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN + `integral (:complex) + (\z. wirtinger_dbar (f:complex->complex) z * + path_integral g (\w. Cx(&1) / (z - w))) = + integral (:complex) + (\z. wirtinger_dbar f z * + (--(Cx(&2) * Cx pi * ii * + Cx(Re(winding_number(g:real^1->complex,z))))))` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_SPIKE THEN + EXISTS_TAC `path_image(g:real^1->complex)` THEN + CONJ_TAC THENL + [ASM_MESON_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE]; ALL_TAC] THEN + X_GEN_TAC `z:complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN AP_TERM_TAC THEN + SUBGOAL_THEN + `path_integral g (\w. Cx(&1) / (z - w)) = + --(Cx(&2) * Cx pi * ii * winding_number(g:real^1->complex,z))` + SUBST1_TAC THENL + [MP_TAC(ISPECL [`g:real^1->complex`; `z:complex`] + HAS_PATH_INTEGRAL_WINDING_NUMBER_AC) THEN + ASM_REWRITE_TAC[] THEN DISCH_TAC THEN + FIRST_ASSUM(ASSUME_TAC o MATCH_MP HAS_PATH_INTEGRAL_INTEGRABLE) THEN + FIRST_ASSUM(ASSUME_TAC o MATCH_MP PATH_INTEGRAL_UNIQUE) THEN + SUBGOAL_THEN + `(\w:complex. Cx(&1) / (z - w)) = + (\w. --(Cx(&1) / (w - z)))` SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; complex_div; COMPLEX_MUL_LID] THEN + X_GEN_TAC `w:complex` THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [GSYM COMPLEX_NEG_SUB] THEN + REWRITE_TAC[COMPLEX_INV_NEG]; + ALL_TAC] THEN + ASM_SIMP_TAC[PATH_INTEGRAL_NEG] THEN + ASM_REWRITE_TAC[] THEN SIMPLE_COMPLEX_ARITH_TAC; + ALL_TAC] THEN + REPEAT AP_TERM_TAC THEN + REWRITE_TAC[GSYM REAL; real] THEN + SUBGOAL_THEN + `complex_integer(winding_number(g:real^1->complex,z))` MP_TAC THENL + [MATCH_MP_TAC INTEGER_WINDING_NUMBER THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + REWRITE_TAC[complex_integer] THEN SIMP_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\z. wirtinger_dbar (f:complex->complex) z * + (--(Cx(&2) * Cx pi * ii * + Cx(Re(winding_number(g:real^1->complex,z)))))) = + (\z. --(Cx(&2) * Cx pi * ii) * + (Cx(Re(winding_number(g,z))) * wirtinger_dbar f z))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM] THEN GEN_TAC THEN SIMPLE_COMPLEX_ARITH_TAC; + ALL_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_COMPLEX_LMUL]; + ALL_TAC] THEN + (* Step 6: Derive the goal from the key equation using complex field *) + MP_TAC CX_2PII_NZ THEN MP_TAC CX_PI_NZ THEN + UNDISCH_TAC + `--(Cx pi) * path_integral g (f:complex->complex) = + --(Cx(&2) * Cx pi * ii) * + integral (:complex) + (\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar f z)` THEN + REWRITE_TAC[CX_INV] THEN CONV_TAC COMPLEX_FIELD) in + (* Use smooth extension + local lemma to prove the result *) + MP_TAC(ISPECL [`f:complex->complex`; `u:complex->bool`; + `path_image g UNION inside(path_image g):complex->bool`] + SMOOTH_EXTENSION_FROM_OPEN) THEN + ANTS_TAC THENL + [CONJ_TAC THENL + [MATCH_MP_TAC COMPACT_WITH_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + CONJ_TAC THENL [ASM SET_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_THEN `ef:complex->complex` STRIP_ASSUME_TAC) THEN + SUBGOAL_THEN `open(inside(path_image(g:real^1->complex)))` ASSUME_TAC THENL + [MATCH_MP_TAC OPEN_INSIDE THEN MATCH_MP_TAC COMPACT_IMP_CLOSED THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + SUBGOAL_THEN + `!z. z IN inside(path_image g) ==> + frechet_derivative (ef:complex->complex) (at z) = + frechet_derivative f (at z)` ASSUME_TAC THENL + [X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + CONV_TAC SYM_CONV THEN + MATCH_MP_TAC HAS_FRECHET_DERIVATIVE_UNIQUE_AT THEN + MATCH_MP_TAC HAS_DERIVATIVE_TRANSFORM_WITHIN_OPEN THEN + EXISTS_TAC `ef:complex->complex` THEN + EXISTS_TAC `inside(path_image(g:real^1->complex))` THEN + ASM_REWRITE_TAC[] THEN CONJ_TAC THENL + [X_GEN_TAC `w:complex` THEN DISCH_TAC THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_UNION]; + ASM_MESON_TAC[FRECHET_DERIVATIVE_WORKS; + differentiable_on; IN_UNIV; WITHIN_UNIV]]; + ALL_TAC] THEN + (* Get integrability, path_integrable and equality from complex_green_univ *) + SUBGOAL_THEN + `(\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (ef:complex->complex) z) + integrable_on (:complex) /\ + ef path_integrable_on g /\ + Cx(inv(&2)) / ii * path_integral g ef = + integral (:complex) + (\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar ef z)` STRIP_ASSUME_TAC THENL + [MATCH_MP_TAC complex_green_univ THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + (* Pointwise equality off path: Cx(Re(wn))*dbar ef = Cx(Re(wn))*dbar f *) + SUBGOAL_THEN + `!z. ~(z IN path_image(g:real^1->complex)) ==> + Cx(Re(winding_number(g,z))) * + wirtinger_dbar (ef:complex->complex) z = + Cx(Re(winding_number(g,z))) * wirtinger_dbar f z` + ASSUME_TAC THENL + [X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + SUBGOAL_THEN + `z IN inside(path_image(g:real^1->complex)) \/ + z IN outside(path_image g)` MP_TAC THENL + [MP_TAC(ISPEC `path_image(g:real^1->complex)` INSIDE_UNION_OUTSIDE) THEN + REWRITE_TAC[EXTENSION; IN_UNION; IN_DIFF; IN_UNIV] THEN + DISCH_THEN(MP_TAC o SPEC `z:complex`) THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [AP_TERM_TAC THEN REWRITE_TAC[wirtinger_dbar; jacobian] THEN + ASM_SIMP_TAC[]; + SUBGOAL_THEN `winding_number(g:real^1->complex,z) = Cx(&0)` + SUBST1_TAC THENL + [MATCH_MP_TAC WINDING_NUMBER_ZERO_IN_OUTSIDE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; + REWRITE_TAC[RE_CX; COMPLEX_MUL_LZERO]]]; + ALL_TAC] THEN + (* Transfer integrability: Cx(Re(wn))*dbar ef -> Cx(Re(wn))*dbar f *) + CONJ_TAC THENL + [MP_TAC(CONV_RULE (DEPTH_CONV BETA_CONV) (ISPECL + [`\z:complex. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (ef:complex->complex) z`; + `\z:complex. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (f:complex->complex) z`; + `path_image(g:real^1->complex)`; + `(:complex)`] + INTEGRABLE_SPIKE)) THEN + DISCH_THEN(fun th -> MATCH_MP_TAC(REWRITE_RULE[IMP_IMP] th)) THEN + REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + ASM_MESON_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE]; + ALL_TAC] THEN + (* Transfer path_integrable from ef to f *) + CONJ_TAC THENL + [MATCH_MP_TAC PATH_INTEGRABLE_EQ THEN + EXISTS_TAC `ef:complex->complex` THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_UNION]; + ALL_TAC] THEN + (* Bring equation with ef back as antecedent for SUBST1_TAC flow *) + UNDISCH_TAC + `Cx(inv(&2)) / ii * path_integral g (ef:complex->complex) = + integral (:complex) + (\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar ef z)` THEN + SUBGOAL_THEN + `path_integral g (ef:complex->complex) = path_integral g f` + SUBST1_TAC THENL + [MATCH_MP_TAC PATH_INTEGRAL_EQ THEN + X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_UNION]; + ALL_TAC] THEN + SUBGOAL_THEN + `integral (:complex) + (\z. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (ef:complex->complex) z) = + integral (:complex) + (\z. Cx(Re(winding_number(g,z))) * + wirtinger_dbar f z)` + SUBST1_TAC THENL + [MATCH_MP_TAC INTEGRAL_SPIKE THEN + EXISTS_TAC `path_image(g:real^1->complex)` THEN + REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + ASM_MESON_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE]; + ALL_TAC] THEN + DISCH_THEN ACCEPT_TAC);; + +(* Relational form: replaces Cx(Re(winding_number)) with winding_number *) +(* (they agree a.e. since winding_number is integer off path_image g). *) +let COMPLEX_GREEN = prove + (`!f:complex->complex g u. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + open u /\ + inside(path_image g) UNION path_image g SUBSET u /\ + f differentiable_on u /\ + (!h:complex. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> (\z. winding_number(g,z) * wirtinger_dbar f z) + integrable_on (:complex) /\ + f path_integrable_on g /\ + path_integral g f = + Cx(&2) * ii * + integral (:complex) + (\z. winding_number(g,z) * wirtinger_dbar f z)`, + REPEAT GEN_TAC THEN DISCH_TAC THEN + FIRST_ASSUM(MP_TAC o MATCH_MP COMPLEX_GREEN_ALT) THEN + FIRST_X_ASSUM STRIP_ASSUME_TAC THEN + REWRITE_TAC[CX_INV; COMPLEX_FIELD + `inv(Cx(&2)) / ii * x = y <=> x = Cx(&2) * ii * y`] THEN + STRIP_TAC THEN + SUBGOAL_THEN `path(g:real^1->complex)` ASSUME_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + (* wn*dbar f integrable via INTEGRABLE_SPIKE from Cx(Re(wn))*dbar f *) + CONJ_TAC THENL + [MP_TAC(CONV_RULE (DEPTH_CONV BETA_CONV) (ISPECL + [`\z:complex. Cx(Re(winding_number(g:real^1->complex,z))) * + wirtinger_dbar (f:complex->complex) z`; + `\z:complex. winding_number(g:real^1->complex,z) * + wirtinger_dbar (f:complex->complex) z`; + `path_image(g:real^1->complex)`; + `(:complex)`] + INTEGRABLE_SPIKE)) THEN + DISCH_THEN(fun th -> MATCH_MP_TAC(REWRITE_RULE[IMP_IMP] th)) THEN + REWRITE_TAC[IN_DIFF; IN_UNIV] THEN CONJ_TAC THENL + [CONJ_TAC THENL + [ASM_MESON_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE]; + REPEAT STRIP_TAC THEN AP_THM_TAC THEN AP_TERM_TAC THEN + CONV_TAC SYM_CONV THEN REWRITE_TAC[GSYM REAL; real] THEN + ASM_MESON_TAC[INTEGER_WINDING_NUMBER; complex_integer]]; + ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN + REPLICATE_TAC 2 AP_TERM_TAC THEN + MATCH_MP_TAC INTEGRAL_SPIKE THEN + EXISTS_TAC `path_image g:complex->bool` THEN + ASM_SIMP_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE; IN_DIFF; IN_UNIV] THEN + REPEAT STRIP_TAC THEN AP_THM_TAC THEN AP_TERM_TAC THEN + CONV_TAC SYM_CONV THEN REWRITE_TAC[GSYM REAL; real] THEN + ASM_MESON_TAC[INTEGER_WINDING_NUMBER; complex_integer]) + +(* Restrict integral to inside(path_image g): winding_number = 0 outside. *) +let COMPLEX_GREEN_INSIDE = prove + (`!f:complex->complex g u. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + open u /\ + inside(path_image g) UNION path_image g SUBSET u /\ + f differentiable_on u /\ + (!h:complex. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> (\z. winding_number(g,z) * wirtinger_dbar f z) + integrable_on inside(path_image g) /\ + f path_integrable_on g /\ + path_integral g f = + Cx(&2) * ii * + integral (inside(path_image g)) + (\z. winding_number(g,z) * wirtinger_dbar f z)`, + REPEAT GEN_TAC THEN DISCH_TAC THEN CONJ_TAC THENL + [FIRST_ASSUM(MP_TAC o CONJUNCT1 o MATCH_MP COMPLEX_GREEN) THEN + DISCH_TAC THEN + SUBGOAL_THEN + `(\z:complex. if z IN inside(path_image(g:real^1->complex)) + then winding_number(g,z) * + wirtinger_dbar (f:complex->complex) z + else vec 0) + integrable_on (:complex)` MP_TAC THENL + [MP_TAC(CONV_RULE (DEPTH_CONV BETA_CONV) (ISPECL + [`\z:complex. winding_number(g:real^1->complex,z) * + wirtinger_dbar (f:complex->complex) z`; + `\z:complex. if z IN inside(path_image(g:real^1->complex)) + then winding_number(g,z) * + wirtinger_dbar (f:complex->complex) z + else vec 0`; + `path_image(g:real^1->complex)`; + `(:complex)`] + INTEGRABLE_SPIKE)) THEN + DISCH_THEN(fun th -> MATCH_MP_TAC(REWRITE_RULE[IMP_IMP] th)) THEN + CONJ_TAC THENL + [CONJ_TAC THENL + [MATCH_MP_TAC NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE THEN + ASM_REWRITE_TAC[]; + X_GEN_TAC `z:complex` THEN REWRITE_TAC[IN_DIFF; IN_UNIV] THEN + DISCH_TAC THEN COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN + REWRITE_TAC[COMPLEX_VEC_0] THEN + SUBGOAL_THEN `winding_number(g:real^1->complex,z) = Cx(&0)` + SUBST1_TAC THENL + [MATCH_MP_TAC WINDING_NUMBER_ZERO_IN_OUTSIDE THEN + ASM_SIMP_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH] THEN + ASM_REWRITE_TAC[OUTSIDE_INSIDE; IN_DIFF; IN_UNIV; IN_UNION]; + REWRITE_TAC[COMPLEX_MUL_LZERO]]]; + ASM_REWRITE_TAC[]]; + REWRITE_TAC[INTEGRABLE_RESTRICT_UNIV]]; + ALL_TAC] THEN + FIRST_ASSUM(STRIP_ASSUME_TAC o MATCH_MP COMPLEX_GREEN) THEN + CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN + REPLICATE_TAC 2 AP_TERM_TAC THEN + GEN_REWRITE_TAC RAND_CONV [GSYM INTEGRAL_RESTRICT_UNIV] THEN + MATCH_MP_TAC INTEGRAL_SPIKE THEN + EXISTS_TAC `path_image g:complex->bool` THEN + ASM_SIMP_TAC[NEGLIGIBLE_ABSOLUTELY_CONTINUOUS_PATH_IMAGE; IN_DIFF; IN_UNIV] THEN + X_GEN_TAC `z:complex` THEN STRIP_TAC THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN + REWRITE_TAC[COMPLEX_VEC_0] THEN CONV_TAC SYM_CONV THEN + REWRITE_TAC[COMPLEX_ENTIRE] THEN DISJ1_TAC THEN + MATCH_MP_TAC WINDING_NUMBER_ZERO_IN_OUTSIDE THEN + ASM_SIMP_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH] THEN + ASM_REWRITE_TAC[OUTSIDE_INSIDE; IN_DIFF; IN_UNIV; IN_UNION]);; + +(* Path integral of cnj for absolutely continuous closed curves. *) +(* General form: path_integral = Cx(&2)*ii * integral_inside(wn). *) +let HAS_PATH_INTEGRAL_CNJ = prove + (`!g:real^1->complex. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g + ==> cnj path_integrable_on g /\ + path_integral g cnj = + Cx(&2) * ii * + integral (inside(path_image g)) + (\z. winding_number(g,z))`, + GEN_TAC THEN STRIP_TAC THEN + MP_TAC(ISPECL [`cnj`; `g:real^1->complex`; `(:complex)`] + COMPLEX_GREEN_INSIDE) THEN + ANTS_TAC THENL + [ASM_REWRITE_TAC[OPEN_UNIV; SUBSET_UNIV] THEN CONJ_TAC THENL + [MATCH_MP_TAC DIFFERENTIABLE_AT_IMP_DIFFERENTIABLE_ON THEN + GEN_TAC THEN DISCH_TAC THEN + MATCH_MP_TAC HAS_DERIVATIVE_IMP_DIFFERENTIABLE THEN + EXISTS_TAC `cnj` THEN REWRITE_TAC[HAS_DERIVATIVE_CNJ]; + GEN_TAC THEN + REWRITE_TAC[MATCH_MP HAS_FRECHET_DERIVATIVE_UNIQUE_AT + (SPEC_ALL HAS_DERIVATIVE_CNJ)] THEN + REWRITE_TAC[CONTINUOUS_ON_CONST]]; + ALL_TAC] THEN + STRIP_TAC THEN CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN REPLICATE_TAC 2 AP_TERM_TAC THEN + MATCH_MP_TAC INTEGRAL_EQ THEN + REWRITE_TAC[WIRTINGER_DBAR_CNJ; COMPLEX_MUL_RID]);; + +(* Gauss-Green for cnj: integral of wirtinger_dbar cnj = 1 gives area. *) +let COMPLEX_GREEN_CNJ = prove + (`!g. g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) + ==> (cnj has_path_integral + Cx(&2) * ii * Cx(measure(inside(path_image g)))) g`, + GEN_TAC THEN STRIP_TAC THEN + REWRITE_TAC[HAS_PATH_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + MP_TAC(SPEC `g:real^1->complex` HAS_PATH_INTEGRAL_CNJ) THEN + ASM_REWRITE_TAC[] THEN + STRIP_TAC THEN CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN REPLICATE_TAC 2 AP_TERM_TAC THEN + SUBGOAL_THEN `measurable(inside(path_image(g:real^1->complex)))` + ASSUME_TAC THENL + [MATCH_MP_TAC MEASURABLE_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + MP_TAC(ISPECL [`\z:complex. winding_number(g:real^1->complex,z)`; + `\z:complex. Cx(&1)`; + `inside(path_image(g:real^1->complex))`] + INTEGRAL_EQ) THEN + ANTS_TAC THENL [ASM_SIMP_TAC[]; DISCH_THEN SUBST1_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_CONST_GEN] THEN + REWRITE_TAC[COMPLEX_CMUL; COMPLEX_MUL_RID]);; + +(* Cauchy's theorem for identity (no wn hypothesis, closed path only). *) +let HAS_PATH_INTEGRAL_I_CLOSED = prove + (`!g:real^1->complex. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g + ==> (I has_path_integral Cx(&0)) g`, + GEN_TAC THEN STRIP_TAC THEN + MP_TAC(ISPECL [`I:complex->complex`; `g:real^1->complex`; `(:complex)`] + COMPLEX_GREEN_INSIDE) THEN + ANTS_TAC THENL + [ASM_REWRITE_TAC[OPEN_UNIV; SUBSET_UNIV] THEN CONJ_TAC THENL + [REWRITE_TAC[I_DEF; DIFFERENTIABLE_ON_ID]; + GEN_TAC THEN REWRITE_TAC[I_DEF] THEN + SUBGOAL_THEN + `!z:complex. frechet_derivative (\x:complex. x) (at z) = (\h. h)` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN CONV_TAC SYM_CONV THEN + MATCH_MP_TAC FRECHET_DERIVATIVE_AT THEN + REWRITE_TAC[HAS_DERIVATIVE_ID]; + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + REWRITE_TAC[CONTINUOUS_ON_CONST]]]; + DISCH_THEN(MP_TAC o CONJUNCT2) THEN + REWRITE_TAC[GSYM HAS_PATH_INTEGRAL_INTEGRABLE_INTEGRAL]] THEN + MATCH_MP_TAC EQ_IMP THEN AP_THM_TAC THEN AP_TERM_TAC THEN + REWRITE_TAC[WIRTINGER_DBAR_I] THEN + REWRITE_TAC[COMPLEX_MUL_RZERO] THEN + SUBGOAL_THEN `(\z:complex. Cx(&0)) = (\z. vec 0)` SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; COMPLEX_VEC_0]; ALL_TAC] THEN + REWRITE_TAC[INTEGRAL_0; COMPLEX_MUL_RZERO; COMPLEX_VEC_0]);; + +(* Cauchy's theorem for the identity: wirtinger_dbar I = 0. *) +(* Trivial corollary of HAS_PATH_INTEGRAL_I_CLOSED (extra wn hyp unused). *) +let COMPLEX_GREEN_I = prove + (`!g. g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) + ==> (I has_path_integral Cx(&0)) g`, + MESON_TAC[HAS_PATH_INTEGRAL_I_CLOSED]);; + +(* Complex area formula: measure of inside from path integral of cnj. *) +let COMPLEX_GREEN_AREA = prove + (`!g. g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) + ==> cnj path_integrable_on g /\ + Cx(measure(inside(path_image g))) = + --ii / Cx(&2) * path_integral g cnj`, + GEN_TAC THEN DISCH_THEN(MP_TAC o MATCH_MP COMPLEX_GREEN_CNJ) THEN + REWRITE_TAC[HAS_PATH_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + STRIP_TAC THEN ASM_REWRITE_TAC[] THEN + UNDISCH_TAC `path_integral g cnj = + Cx(&2) * ii * Cx(measure(inside(path_image g)))` THEN + CONV_TAC COMPLEX_FIELD);; + +(* Orientation-free complex area formula for simple closed curves. *) +(* Uses COMPLEX_GREEN_INSIDE and case split on wn=+1/-1. *) +let COMPLEX_GREEN_AREA_ABS = prove + (`!g:real^1->complex. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + simple_path g /\ pathfinish g = pathstart g + ==> cnj path_integrable_on g /\ + measure(inside(path_image g)) = + norm(path_integral g cnj) / &2`, + GEN_TAC THEN STRIP_TAC THEN + MP_TAC(SPEC `g:real^1->complex` HAS_PATH_INTEGRAL_CNJ) THEN + ASM_REWRITE_TAC[] THEN + STRIP_TAC THEN CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN `measurable(inside(path_image g):complex->bool)` + ASSUME_TAC THENL + [MATCH_MP_TAC MEASURABLE_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + MP_TAC(ISPEC `g:real^1->complex` + SIMPLE_CLOSED_PATH_WINDING_NUMBER_INSIDE) THEN + ANTS_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [SUBGOAL_THEN + `integral (inside(path_image g)) + (\z. winding_number(g:real^1->complex,z)) = + Cx(measure(inside(path_image g)))` + SUBST1_TAC THENL + [MP_TAC(ISPECL [`\z:complex. winding_number(g:real^1->complex,z)`; + `\z:complex. Cx(&1)`; + `inside(path_image(g:real^1->complex))`] + INTEGRAL_EQ) THEN + ANTS_TAC THENL [ASM_SIMP_TAC[]; DISCH_THEN SUBST1_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_CONST_GEN] THEN + REWRITE_TAC[COMPLEX_CMUL; COMPLEX_MUL_RID]; + ALL_TAC]; + SUBGOAL_THEN + `integral (inside(path_image g)) + (\z. winding_number(g:real^1->complex,z)) = + --Cx(measure(inside(path_image g)))` + SUBST1_TAC THENL + [MP_TAC(ISPECL [`\z:complex. winding_number(g:real^1->complex,z)`; + `\z:complex. --Cx(&1)`; + `inside(path_image(g:real^1->complex))`] + INTEGRAL_EQ) THEN + ANTS_TAC THENL [ASM_SIMP_TAC[]; DISCH_THEN SUBST1_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_CONST_GEN] THEN + REWRITE_TAC[COMPLEX_CMUL] THEN + ONCE_REWRITE_TAC[COMPLEX_MUL_SYM] THEN + REWRITE_TAC[COMPLEX_MUL_LNEG; COMPLEX_MUL_LID]; + ALL_TAC]] THEN + REWRITE_TAC[COMPLEX_MUL_RNEG; NORM_NEG; + COMPLEX_NORM_MUL; COMPLEX_NORM_CX; COMPLEX_NORM_II; + REAL_ABS_NUM; REAL_MUL_LID] THEN + REWRITE_TAC[COMPLEX_NORM_CX] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x ==> x = (&2 * abs x) / &2`) THEN + MATCH_MP_TAC MEASURE_POS_LE THEN ASM_REWRITE_TAC[]);; + +(* ------------------------------------------------------------------------- *) +(* Real Green's theorem (curl form) and real area formulas. *) +(* ------------------------------------------------------------------------- *) + +(* Green's theorem in classical curl form: *) +(* integral(f2 dx + f1 dy) = integral(df1/dx - df2/dy) dA *) +(* Expands wirtinger_dbar integral into explicit partial derivatives. *) +(* Statement uses real^2, basis 1, basis 2 to avoid complex paraphernalia. *) +(* Helper lemmas (green_theorem_real, wirtinger_dbar_2i_im, linearity) *) +(* are localized inside the proof. *) +let GREEN_THEOREM_CURL = prove + (`!f:real^2->real^2 g u. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) /\ + open u /\ + inside(path_image g) UNION path_image g SUBSET u /\ + f differentiable_on u /\ + (!h:real^2. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> (\z. lift(frechet_derivative f (at z) (basis 1) $1 - + frechet_derivative f (at z) (basis 2) $2)) + integrable_on inside(path_image g) /\ + (\t. lift(f(g t)$2 * vector_derivative g (at t) $1 + + f(g t)$1 * vector_derivative g (at t) $2)) + integrable_on interval[vec 0,vec 1] /\ + integral (interval[vec 0,vec 1]) + (\t. lift(f(g t)$2 * vector_derivative g (at t) $1 + + f(g t)$1 * vector_derivative g (at t) $2)) = + integral (inside(path_image g)) + (\z. lift(frechet_derivative f (at z) (basis 1) $1 - + frechet_derivative f (at z) (basis 2) $2))`, + let green_theorem_real = prove + (`!f:complex->complex g u. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) /\ + open u /\ + inside(path_image g) UNION path_image g SUBSET u /\ + f differentiable_on u /\ + (!h:complex. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> ((\t. lift(f(g t)$2 * vector_derivative g (at t) $1 + + f(g t)$1 * vector_derivative g (at t) $2)) + has_integral + lift(Im(Cx(&2) * ii * + integral (inside(path_image g)) + (wirtinger_dbar f)))) + (interval[vec 0,vec 1])`, + REPEAT GEN_TAC THEN STRIP_TAC THEN + MP_TAC(ISPECL [`f:complex->complex`; `g:real^1->complex`; `u:complex->bool`] + COMPLEX_GREEN_INSIDE) THEN + ASM_REWRITE_TAC[] THEN + DISCH_THEN(MP_TAC o CONJUNCT2) THEN + REWRITE_TAC[GSYM HAS_PATH_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + SUBGOAL_THEN + `integral (inside(path_image g)) + (\z. winding_number(g:real^1->complex,z) * wirtinger_dbar f z) = + integral (inside(path_image g)) (wirtinger_dbar f)` + (fun th -> REWRITE_TAC[th]) THENL + [MATCH_MP_TAC INTEGRAL_EQ THEN + X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + ASM_SIMP_TAC[COMPLEX_MUL_LID]; + ALL_TAC] THEN + REWRITE_TAC[REWRITE_RULE[complex_mul] HAS_PATH_INTEGRAL] THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [HAS_INTEGRAL_COMPONENTWISE] THEN + REWRITE_TAC[DIMINDEX_2; FORALL_2; GSYM IM_DEF; GSYM RE_DEF] THEN + DISCH_THEN(MP_TAC o CONJUNCT2) THEN + REWRITE_TAC[IM; GSYM IM_DEF; GSYM RE_DEF] THEN + SIMP_TAC[REAL_ADD_AC] THEN + DISCH_THEN ACCEPT_TAC) + and wirtinger_dbar_2i_im = prove + (`!f:complex->complex z. + Im(Cx(&2) * ii * wirtinger_dbar f z) = + frechet_derivative f (at z) (Cx(&1)) $1 - + frechet_derivative f (at z) ii $2`, + REPEAT GEN_TAC THEN + REWRITE_TAC[WIRTINGER_DBAR_FRECHET; complex_div; GSYM CX_INV] THEN + REWRITE_TAC[IM_MUL_CX] THEN + ONCE_REWRITE_TAC[COMPLEX_MUL_ASSOC] THEN + REWRITE_TAC[IM_MUL_CX; IM_MUL_II; RE_ADD; RE_MUL_II] THEN + REWRITE_TAC[RE_DEF; IM_DEF] THEN + CONV_TAC REAL_FIELD) in + let linear_lift_im_2i = prove + (`linear(\z:complex. lift(Im(Cx(&2) * ii * z)))`, + SUBGOAL_THEN + `(\z:complex. lift(Im(Cx(&2) * ii * z))) = + (\x:complex. lift(x$2)) o (\z:complex. Cx(&2) * ii * z)` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; o_THM] THEN GEN_TAC THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN REWRITE_TAC[IM_DEF]; + ALL_TAC] THEN + MATCH_MP_TAC LINEAR_COMPOSE THEN + REWRITE_TAC[LINEAR_LIFT_COMPONENT] THEN + MATCH_MP_TAC LINEAR_COMPLEX_LMUL THEN + MATCH_MP_TAC LINEAR_COMPLEX_LMUL THEN + REWRITE_TAC[LINEAR_ID]) in + REWRITE_TAC[COMPLEX_BASIS] THEN + REPEAT GEN_TAC THEN STRIP_TAC THEN + SUBGOAL_THEN + `wirtinger_dbar (f:complex->complex) integrable_on + inside(path_image g)` + ASSUME_TAC THENL + [MATCH_MP_TAC INTEGRABLE_EQ THEN + EXISTS_TAC + `\z:complex. winding_number(g:real^1->complex,z) * + wirtinger_dbar (f:complex->complex) z` THEN + CONJ_TAC THENL + [X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ASM_SIMP_TAC[COMPLEX_MUL_LID]; + MP_TAC(ISPECL [`f:complex->complex`; `g:real^1->complex`; + `u:complex->bool`] + COMPLEX_GREEN_INSIDE) THEN + ANTS_TAC THENL [ASM_REWRITE_TAC[]; SIMP_TAC[]]]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\z. lift(frechet_derivative (f:complex->complex) (at z) (Cx(&1)) $1 - + frechet_derivative f (at z) ii $2)) + integrable_on inside(path_image g)` + ASSUME_TAC THENL + [MATCH_MP_TAC INTEGRABLE_EQ THEN + EXISTS_TAC + `(\z:complex. lift(Im(Cx(&2) * ii * z))) o + wirtinger_dbar (f:complex->complex)` THEN + CONJ_TAC THENL + [X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + REWRITE_TAC[o_THM] THEN AP_TERM_TAC THEN + REWRITE_TAC[wirtinger_dbar_2i_im]; + MATCH_MP_TAC INTEGRABLE_LINEAR THEN + ASM_REWRITE_TAC[linear_lift_im_2i]]; + ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN + MP_TAC(ISPECL [`f:complex->complex`; `g:real^1->complex`; `u:complex->bool`] + green_theorem_real) THEN + ASM_REWRITE_TAC[] THEN + SUBGOAL_THEN + `lift(Im(Cx(&2) * ii * + integral (inside(path_image g)) + (wirtinger_dbar (f:complex->complex)))) = + integral (inside(path_image g)) + (\z. lift(frechet_derivative f (at z) (Cx(&1)) $1 - + frechet_derivative f (at z) ii $2))` + (fun th -> REWRITE_TAC[th]) THENL + [MP_TAC(ISPECL [`wirtinger_dbar (f:complex->complex)`; + `inside(path_image(g:real^1->complex))`; + `\z:complex. lift(Im(Cx(&2) * ii * z))`] + INTEGRAL_LINEAR) THEN + ANTS_TAC THENL + [ASM_REWRITE_TAC[linear_lift_im_2i]; + REWRITE_TAC[o_DEF] THEN + DISCH_THEN(SUBST1_TAC o SYM) THEN + MATCH_MP_TAC INTEGRAL_EQ THEN + X_GEN_TAC `z:complex` THEN DISCH_TAC THEN + BETA_TAC THEN AP_TERM_TAC THEN REWRITE_TAC[wirtinger_dbar_2i_im]]; + ALL_TAC] THEN + REWRITE_TAC[HAS_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + STRIP_TAC THEN ASM_REWRITE_TAC[]);; + +(* De-complexified area formula: integral of x*dy = area (x*y' form). *) +let GREEN_AREA = prove + (`!g. g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) + ==> (\t. lift(g t $1 * vector_derivative g (at t) $2)) + integrable_on interval[vec 0,vec 1] /\ + integral (interval[vec 0,vec 1]) + (\t. lift(g t $1 * vector_derivative g (at t) $2)) = + lift(measure(inside(path_image g)))`, + REWRITE_TAC[GSYM HAS_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + REPEAT STRIP_TAC THEN + MP_TAC(SPEC `g:real^1->complex` COMPLEX_GREEN_I) THEN + MP_TAC(SPEC `g:real^1->complex` COMPLEX_GREEN_CNJ) THEN + ASM_REWRITE_TAC[IMP_IMP] THEN + REWRITE_TAC[REWRITE_RULE[complex_mul] HAS_PATH_INTEGRAL] THEN + REWRITE_TAC[RE_CNJ; IM_CNJ] THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [HAS_INTEGRAL_COMPONENTWISE] THEN + REWRITE_TAC[DIMINDEX_2; FORALL_2; GSYM IM_DEF; GSYM RE_DEF; I_DEF] THEN + REWRITE_TAC[RE; IM; RE_II; IM_II; RE_CX; IM_CX; IM_MUL_CX; RE_MUL_CX] THEN + REWRITE_TAC[REAL_MUL_LZERO; REAL_MUL_RZERO; LIFT_NUM] THEN + REWRITE_TAC[REAL_MUL_LID; COMPLEX_VEC_0] THEN + DISCH_THEN(fun th -> + let b = CONJUNCT2(CONJUNCT1 th) and d = CONJUNCT2(CONJUNCT2 th) in + ASSUME_TAC b THEN ASSUME_TAC d) THEN + SUBGOAL_THEN + `!t:real^1. lift(Re (g t) * Im (vector_derivative g (at t))) = + inv(&2) % + (lift (Re (g t) * Im (vector_derivative g (at t)) + + --Im (g t) * Re (vector_derivative g (at t))) + + lift (Re (g t) * Im (vector_derivative g (at t)) + + Im (g t) * Re (vector_derivative g (at t))))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN REWRITE_TAC[GSYM LIFT_ADD; GSYM LIFT_CMUL] THEN + AP_TERM_TAC THEN REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `lift(measure(inside(path_image(g:real^1->complex)))) = + inv(&2) % (lift (&2 * measure(inside(path_image g))) + + (vec 0:real^1))` + SUBST1_TAC THENL + [REWRITE_TAC[VECTOR_ADD_RID; GSYM LIFT_CMUL; LIFT_EQ] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + MATCH_MP_TAC HAS_INTEGRAL_CMUL THEN + MATCH_MP_TAC HAS_INTEGRAL_ADD THEN + CONJ_TAC THEN FIRST_ASSUM ACCEPT_TAC);; + +(* De-complexified area formula: integral of y*dx = -area (x'*y form). *) +let GREEN_AREA_ALT = prove + (`!g. g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) + ==> (\t. lift(vector_derivative g (at t) $1 * g t $2)) + integrable_on interval[vec 0,vec 1] /\ + integral (interval[vec 0,vec 1]) + (\t. lift(vector_derivative g (at t) $1 * g t $2)) = + --lift(measure(inside(path_image g)))`, + REWRITE_TAC[GSYM HAS_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + REPEAT STRIP_TAC THEN + MP_TAC(SPEC `g:real^1->complex` COMPLEX_GREEN_I) THEN + MP_TAC(SPEC `g:real^1->complex` COMPLEX_GREEN_CNJ) THEN + ASM_REWRITE_TAC[IMP_IMP] THEN + REWRITE_TAC[REWRITE_RULE[complex_mul] HAS_PATH_INTEGRAL] THEN + REWRITE_TAC[RE_CNJ; IM_CNJ] THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [HAS_INTEGRAL_COMPONENTWISE] THEN + REWRITE_TAC[DIMINDEX_2; FORALL_2; GSYM IM_DEF; GSYM RE_DEF; I_DEF] THEN + REWRITE_TAC[RE; IM; RE_II; IM_II; RE_CX; IM_CX; IM_MUL_CX; RE_MUL_CX] THEN + REWRITE_TAC[REAL_MUL_LZERO; REAL_MUL_RZERO; LIFT_NUM] THEN + REWRITE_TAC[REAL_MUL_LID; COMPLEX_VEC_0] THEN + DISCH_THEN(fun th -> + let b = CONJUNCT2(CONJUNCT1 th) and d = CONJUNCT2(CONJUNCT2 th) in + ASSUME_TAC b THEN ASSUME_TAC d) THEN + SUBGOAL_THEN + `!t:real^1. lift(Re (vector_derivative g (at t)) * Im (g t)) = + --inv(&2) % + (lift (Re (g t) * Im (vector_derivative g (at t)) + + --Im (g t) * Re (vector_derivative g (at t))) - + lift (Re (g t) * Im (vector_derivative g (at t)) + + Im (g t) * Re (vector_derivative g (at t))))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN REWRITE_TAC[GSYM LIFT_SUB; GSYM LIFT_CMUL] THEN + AP_TERM_TAC THEN REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `--lift(measure(inside(path_image(g:real^1->complex)))) = + --inv(&2) % (lift (&2 * measure(inside(path_image g))) - + (vec 0:real^1))` + SUBST1_TAC THENL + [REWRITE_TAC[VECTOR_SUB_RZERO; GSYM LIFT_CMUL; GSYM LIFT_NEG; LIFT_EQ] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + MATCH_MP_TAC HAS_INTEGRAL_CMUL THEN + MATCH_MP_TAC HAS_INTEGRAL_SUB THEN + CONJ_TAC THEN FIRST_ASSUM ACCEPT_TAC);; + +(* Orientation-free real area formula: x*dy form. *) +(* For wn=+1 uses GREEN_AREA; for wn=-1 derives from componentwise *) +(* extraction of cnj and I path integrals. *) +let GREEN_AREA_ABS = prove + (`!g:real^1->complex. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + simple_path g /\ pathfinish g = pathstart g + ==> (\t. lift(g t $1 * vector_derivative g (at t) $2)) + integrable_on interval[vec 0,vec 1] /\ + norm(integral (interval[vec 0,vec 1]) + (\t. lift(g t $1 * vector_derivative g (at t) $2))) = + measure(inside(path_image g))`, + GEN_TAC THEN STRIP_TAC THEN + MP_TAC(ISPEC `g:real^1->complex` + SIMPLE_CLOSED_PATH_WINDING_NUMBER_INSIDE) THEN + ANTS_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [(* wn = +1: apply GREEN_AREA directly *) + MP_TAC(SPEC `g:real^1->complex` GREEN_AREA) THEN + ASM_REWRITE_TAC[] THEN + REWRITE_TAC[GSYM HAS_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + DISCH_TAC THEN CONJ_TAC THENL + [ASM_MESON_TAC[HAS_INTEGRAL_INTEGRABLE]; ALL_TAC] THEN + FIRST_ASSUM(SUBST1_TAC o MATCH_MP INTEGRAL_UNIQUE) THEN + REWRITE_TAC[NORM_LIFT] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x ==> abs x = x`) THEN + MATCH_MP_TAC MEASURE_POS_LE THEN + MATCH_MP_TAC MEASURABLE_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; + ALL_TAC] THEN + (* wn = -1: componentwise extraction *) + SUBGOAL_THEN `measurable(inside(path_image g):complex->bool)` + ASSUME_TAC THENL + [MATCH_MP_TAC MEASURABLE_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + SUBGOAL_THEN + `(cnj has_path_integral + (Cx(&2) * ii * --Cx(measure(inside(path_image g))))) (g:real^1->complex)` + ASSUME_TAC THENL + [REWRITE_TAC[HAS_PATH_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + MP_TAC(SPEC `g:real^1->complex` HAS_PATH_INTEGRAL_CNJ) THEN + ASM_REWRITE_TAC[] THEN + STRIP_TAC THEN CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN REPLICATE_TAC 2 AP_TERM_TAC THEN + MP_TAC(ISPECL [`\z:complex. winding_number(g:real^1->complex,z)`; + `\z:complex. --Cx(&1)`; + `inside(path_image(g:real^1->complex))`] + INTEGRAL_EQ) THEN + ANTS_TAC THENL [ASM_SIMP_TAC[]; DISCH_THEN SUBST1_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_CONST_GEN] THEN + REWRITE_TAC[COMPLEX_CMUL] THEN + ONCE_REWRITE_TAC[COMPLEX_MUL_SYM] THEN + REWRITE_TAC[COMPLEX_MUL_LNEG; COMPLEX_MUL_LID]; + ALL_TAC] THEN + SUBGOAL_THEN `(I has_path_integral Cx(&0)) (g:real^1->complex)` + ASSUME_TAC THENL + [MATCH_MP_TAC HAS_PATH_INTEGRAL_I_CLOSED THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + UNDISCH_TAC + `(cnj has_path_integral + Cx(&2) * ii * --Cx(measure(inside(path_image g)))) + (g:real^1->complex)` THEN + UNDISCH_TAC `(I has_path_integral Cx(&0)) (g:real^1->complex)` THEN + REWRITE_TAC[IMP_IMP] THEN + REWRITE_TAC[REWRITE_RULE[complex_mul] HAS_PATH_INTEGRAL] THEN + REWRITE_TAC[RE_CNJ; IM_CNJ] THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [HAS_INTEGRAL_COMPONENTWISE] THEN + REWRITE_TAC[DIMINDEX_2; FORALL_2; GSYM IM_DEF; GSYM RE_DEF; I_DEF] THEN + REWRITE_TAC[RE; IM; RE_II; IM_II; RE_CX; IM_CX; IM_MUL_CX; RE_MUL_CX; + RE_NEG; IM_NEG; COMPLEX_MUL_RNEG] THEN + REWRITE_TAC[REAL_MUL_LZERO; REAL_MUL_RZERO; LIFT_NUM; + REAL_NEG_0; REAL_MUL_LNEG] THEN + REWRITE_TAC[REAL_MUL_LID; COMPLEX_VEC_0; LIFT_NEG] THEN + DISCH_THEN(fun th -> + let c1 = CONJUNCT1 th and c2 = CONJUNCT2 th in + let b = CONJUNCT2 c1 and d = CONJUNCT2 c2 in + ASSUME_TAC b THEN ASSUME_TAC d) THEN + SUBGOAL_THEN + `((\t:real^1. lift(Re(g t) * Im(vector_derivative g (at t)))) + has_integral --lift(measure(inside(path_image(g:real^1->complex))))) + (interval[vec 0,vec 1])` + ASSUME_TAC THENL + [SUBGOAL_THEN + `!t:real^1. lift(Re (g t) * Im (vector_derivative g (at t))) = + inv(&2) % + (lift (Re (g t) * Im (vector_derivative g (at t)) + + --(Im (g t) * Re (vector_derivative g (at t)))) + + lift (Re (g t) * Im (vector_derivative g (at t)) + + Im (g t) * Re (vector_derivative g (at t))))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN REWRITE_TAC[GSYM LIFT_ADD; GSYM LIFT_CMUL] THEN + AP_TERM_TAC THEN REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `--lift(measure(inside(path_image(g:real^1->complex)))) = + inv(&2) % (lift (&2 * --measure(inside(path_image g))) + + (vec 0:real^1))` + SUBST1_TAC THENL + [REWRITE_TAC[VECTOR_ADD_RID; GSYM LIFT_CMUL; GSYM LIFT_NEG; LIFT_EQ] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + MATCH_MP_TAC HAS_INTEGRAL_CMUL THEN + MATCH_MP_TAC HAS_INTEGRAL_ADD THEN + CONJ_TAC THEN FIRST_ASSUM ACCEPT_TAC; + ALL_TAC] THEN + CONJ_TAC THENL + [ASM_MESON_TAC[HAS_INTEGRAL_INTEGRABLE]; ALL_TAC] THEN + FIRST_ASSUM(SUBST1_TAC o MATCH_MP INTEGRAL_UNIQUE) THEN + REWRITE_TAC[NORM_NEG; NORM_LIFT] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x ==> abs x = x`) THEN + MATCH_MP_TAC MEASURE_POS_LE THEN ASM_REWRITE_TAC[]);; + +(* Orientation-free real area formula: y*dx form. *) +let GREEN_AREA_ABS_ALT = prove + (`!g:real^1->complex. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + simple_path g /\ pathfinish g = pathstart g + ==> (\t. lift(vector_derivative g (at t) $1 * g t $2)) + integrable_on interval[vec 0,vec 1] /\ + norm(integral (interval[vec 0,vec 1]) + (\t. lift(vector_derivative g (at t) $1 * g t $2))) = + measure(inside(path_image g))`, + GEN_TAC THEN STRIP_TAC THEN + MP_TAC(ISPEC `g:real^1->complex` + SIMPLE_CLOSED_PATH_WINDING_NUMBER_INSIDE) THEN + ANTS_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + DISCH_THEN DISJ_CASES_TAC THENL + [(* wn = +1: apply GREEN_AREA_ALT directly *) + MP_TAC(SPEC `g:real^1->complex` GREEN_AREA_ALT) THEN + ASM_REWRITE_TAC[] THEN + REWRITE_TAC[GSYM HAS_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + DISCH_TAC THEN CONJ_TAC THENL + [ASM_MESON_TAC[HAS_INTEGRAL_INTEGRABLE]; ALL_TAC] THEN + FIRST_ASSUM(SUBST1_TAC o MATCH_MP INTEGRAL_UNIQUE) THEN + REWRITE_TAC[NORM_NEG; NORM_LIFT] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x ==> abs x = x`) THEN + MATCH_MP_TAC MEASURE_POS_LE THEN + MATCH_MP_TAC MEASURABLE_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; + ALL_TAC] THEN + (* wn = -1: componentwise extraction *) + SUBGOAL_THEN `measurable(inside(path_image g):complex->bool)` + ASSUME_TAC THENL + [MATCH_MP_TAC MEASURABLE_INSIDE THEN + MATCH_MP_TAC COMPACT_PATH_IMAGE THEN + ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + SUBGOAL_THEN + `(cnj has_path_integral + (Cx(&2) * ii * --Cx(measure(inside(path_image g))))) (g:real^1->complex)` + ASSUME_TAC THENL + [REWRITE_TAC[HAS_PATH_INTEGRAL_INTEGRABLE_INTEGRAL] THEN + MP_TAC(SPEC `g:real^1->complex` HAS_PATH_INTEGRAL_CNJ) THEN + ASM_REWRITE_TAC[] THEN + STRIP_TAC THEN CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN REPLICATE_TAC 2 AP_TERM_TAC THEN + MP_TAC(ISPECL [`\z:complex. winding_number(g:real^1->complex,z)`; + `\z:complex. --Cx(&1)`; + `inside(path_image(g:real^1->complex))`] + INTEGRAL_EQ) THEN + ANTS_TAC THENL [ASM_SIMP_TAC[]; DISCH_THEN SUBST1_TAC] THEN + ASM_SIMP_TAC[INTEGRAL_CONST_GEN] THEN + REWRITE_TAC[COMPLEX_CMUL] THEN + ONCE_REWRITE_TAC[COMPLEX_MUL_SYM] THEN + REWRITE_TAC[COMPLEX_MUL_LNEG; COMPLEX_MUL_LID]; + ALL_TAC] THEN + SUBGOAL_THEN `(I has_path_integral Cx(&0)) (g:real^1->complex)` + ASSUME_TAC THENL + [MATCH_MP_TAC HAS_PATH_INTEGRAL_I_CLOSED THEN ASM_REWRITE_TAC[]; + ALL_TAC] THEN + UNDISCH_TAC + `(cnj has_path_integral + Cx(&2) * ii * --Cx(measure(inside(path_image g)))) + (g:real^1->complex)` THEN + UNDISCH_TAC `(I has_path_integral Cx(&0)) (g:real^1->complex)` THEN + REWRITE_TAC[IMP_IMP] THEN + REWRITE_TAC[REWRITE_RULE[complex_mul] HAS_PATH_INTEGRAL] THEN + REWRITE_TAC[RE_CNJ; IM_CNJ] THEN + GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) + [HAS_INTEGRAL_COMPONENTWISE] THEN + REWRITE_TAC[DIMINDEX_2; FORALL_2; GSYM IM_DEF; GSYM RE_DEF; I_DEF] THEN + REWRITE_TAC[RE; IM; RE_II; IM_II; RE_CX; IM_CX; IM_MUL_CX; RE_MUL_CX; + RE_NEG; IM_NEG; COMPLEX_MUL_RNEG] THEN + REWRITE_TAC[REAL_MUL_LZERO; REAL_MUL_RZERO; LIFT_NUM; + REAL_NEG_0; REAL_MUL_LNEG] THEN + REWRITE_TAC[REAL_MUL_LID; COMPLEX_VEC_0; LIFT_NEG] THEN + DISCH_THEN(fun th -> + let c1 = CONJUNCT1 th and c2 = CONJUNCT2 th in + let b = CONJUNCT2 c1 and d = CONJUNCT2 c2 in + ASSUME_TAC b THEN ASSUME_TAC d) THEN + SUBGOAL_THEN + `((\t:real^1. lift(Re(vector_derivative g (at t)) * Im(g t))) + has_integral lift(measure(inside(path_image(g:real^1->complex))))) + (interval[vec 0,vec 1])` + ASSUME_TAC THENL + [SUBGOAL_THEN + `!t:real^1. lift(Re(vector_derivative g (at t)) * Im(g t)) = + --inv(&2) % + (lift (Re (g t) * Im (vector_derivative g (at t)) + + --(Im (g t) * Re (vector_derivative g (at t)))) - + lift (Re (g t) * Im (vector_derivative g (at t)) + + Im (g t) * Re (vector_derivative g (at t))))` + (fun th -> REWRITE_TAC[th]) THENL + [GEN_TAC THEN REWRITE_TAC[GSYM LIFT_SUB; GSYM LIFT_CMUL] THEN + AP_TERM_TAC THEN REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `lift(measure(inside(path_image(g:real^1->complex)))) = + --inv(&2) % (lift (&2 * --measure(inside(path_image g))) - + (vec 0:real^1))` + SUBST1_TAC THENL + [REWRITE_TAC[VECTOR_SUB_RZERO; GSYM LIFT_CMUL; GSYM LIFT_NEG; LIFT_EQ] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + MATCH_MP_TAC HAS_INTEGRAL_CMUL THEN + MATCH_MP_TAC HAS_INTEGRAL_SUB THEN + CONJ_TAC THEN FIRST_ASSUM ACCEPT_TAC; + ALL_TAC] THEN + CONJ_TAC THENL + [ASM_MESON_TAC[HAS_INTEGRAL_INTEGRABLE]; ALL_TAC] THEN + FIRST_ASSUM(SUBST1_TAC o MATCH_MP INTEGRAL_UNIQUE) THEN + REWRITE_TAC[NORM_LIFT] THEN + MATCH_MP_TAC(REAL_ARITH `&0 <= x ==> abs x = x`) THEN + MATCH_MP_TAC MEASURE_POS_LE THEN ASM_REWRITE_TAC[]);; diff --git a/100/isoperimetric.ml b/100/isoperimetric.ml index 72d33bfc..4b38b31a 100644 --- a/100/isoperimetric.ml +++ b/100/isoperimetric.ml @@ -4,6 +4,7 @@ needs "Multivariate/cauchy.ml";; needs "Multivariate/lpspaces.ml";; +needs "100/green.ml";; (* ------------------------------------------------------------------------- *) (* A few lemmas to switch between views of a convex curve. *) @@ -1109,1007 +1110,34 @@ let SCALED_WIRTINGER_INEQUALITY = prove (* Part 2: a very special case of Green's theorem for a convex area. *) (* ------------------------------------------------------------------------- *) -let AREA_BELOW_ARCLET = prove - (`!(g:real^1->real^2) g' u v s. - drop u <= drop v /\ - g u$1 <= g v$1 /\ - g absolutely_continuous_on interval[u,v] /\ - IMAGE g (interval[u,v]) SUBSET {z | &0 <= z$2} /\ - (!x y. x IN interval[u,v] /\ y IN interval[u,v] /\ - g x = g y ==> x = y) /\ - (!x y. x IN IMAGE g (interval[u,v]) /\ y IN IMAGE g (interval[u,v]) /\ - x$1 = y$1 ==> x = y) /\ - negligible s /\ - (!t. t IN interval[u,v] DIFF s - ==> (g has_vector_derivative g'(t)) (at t)) - ==> measurable {z:real^2 | ?w. w IN IMAGE g (interval[u,v]) /\ - w$1 = z$1 /\ &0 <= z$2 /\ z$2 <= w$2} /\ - (\t. lift(g'(t)$1 * g(t)$2)) absolutely_integrable_on interval[u,v] /\ - integral (interval[u,v]) (\t. lift(g'(t)$1 * g(t)$2)) = - lift(measure {z:real^2 | ?w. w IN IMAGE g (interval[u,v]) /\ - w$1 = z$1 /\ &0 <= z$2 /\ z$2 <= w$2})`, - REPEAT GEN_TAC THEN REWRITE_TAC[INJECTIVE_ON_ALT] THEN STRIP_TAC THEN - MP_TAC(fst(EQ_IMP_RULE(ISPECL - [`\t. lift((g:real^1->real^2)(t)$1)`; `interval[u:real^1,v]`] - INJECTIVE_ON_LEFT_INVERSE))) THEN - REWRITE_TAC[LIFT_EQ] THEN ANTS_TAC THENL [ASM SET_TAC[]; ALL_TAC] THEN - DISCH_THEN(X_CHOOSE_TAC `h:real^1->real^1`) THEN - ABBREV_TAC - `ax = IMAGE (\t. (g:real^1->real^2)(t)$1) (interval[u,v])` THEN - FIRST_ASSUM(MP_TAC o MATCH_MP (REWRITE_RULE[IMP_CONJ] - ABSOLUTELY_CONTINUOUS_ON_IMP_CONTINUOUS)) THEN - REWRITE_TAC[IS_INTERVAL_INTERVAL] THEN DISCH_TAC THEN - MP_TAC(ISPECL - [`\t. lift((g:real^1->real^2)(t)$1)`; - `h:real^1->real^1`; - `interval[u:real^1,v]`; - `IMAGE lift ax`] CONTINUOUS_ON_INVERSE_INTO_1D) THEN - ASM_REWRITE_TAC[COMPACT_INTERVAL] THEN ANTS_TAC THENL - [EXPAND_TAC "ax" THEN REWRITE_TAC[GSYM IMAGE_o; o_DEF] THEN - MATCH_MP_TAC CONTINUOUS_ON_LIFT_COMPONENT_COMPOSE THEN ASM_MESON_TAC[]; - DISCH_TAC] THEN - ABBREV_TAC `f = \x. (g:real^1->real^2)(h(lift x))$2` THEN - SUBGOAL_THEN `f real_continuous_on ax` ASSUME_TAC THENL - [EXPAND_TAC "f" THEN REWRITE_TAC[REAL_CONTINUOUS_ON] THEN - REWRITE_TAC[o_DEF; LIFT_DROP] THEN - MATCH_MP_TAC CONTINUOUS_ON_LIFT_COMPONENT_COMPOSE THEN - GEN_REWRITE_TAC LAND_CONV [GSYM o_DEF] THEN - MATCH_MP_TAC CONTINUOUS_ON_COMPOSE THEN ASM_REWRITE_TAC[] THEN - MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN - EXISTS_TAC `interval[u:real^1,v]` THEN CONJ_TAC THENL - [ASM_MESON_TAC[]; ALL_TAC] THEN - EXPAND_TAC "ax" THEN REWRITE_TAC[SUBSET; FORALL_IN_IMAGE] THEN - ASM_SIMP_TAC[]; - ALL_TAC] THEN - SUBGOAL_THEN - `{z:real^2 | ?w:real^2. w IN IMAGE g (interval[u:real^1,v]) /\ - w$1 = z$1 /\ &0 <= z$2 /\ z$2 <= w$2} = - {z:real^2 | z$1 IN ax /\ &0 <= z$2 /\ z$2 <= f(z$1)}` - SUBST1_TAC THENL - [MAP_EVERY EXPAND_TAC ["ax"; "f"] THEN - REWRITE_TAC[EXISTS_IN_IMAGE] THEN ASM SET_TAC[]; - ALL_TAC] THEN - MP_TAC(SPECL [`\t. lift((g:real^1->real^2)(t)$1)`; - `interval[u:real^1,v]`] - CONTINUOUS_INJECTIVE_IMP_MONOTONIC) THEN - REWRITE_TAC[LIFT_DROP; IS_INTERVAL_INTERVAL] THEN ANTS_TAC THENL - [CONJ_TAC THENL [ALL_TAC; ASM_MESON_TAC[]] THEN - MATCH_MP_TAC CONTINUOUS_ON_LIFT_COMPONENT_COMPOSE THEN - ASM_MESON_TAC[]; - ALL_TAC] THEN - MATCH_MP_TAC(TAUT `(q ==> p) /\ (p ==> r) ==> p \/ q ==> r`) THEN - CONJ_TAC THENL - [DISCH_THEN(MP_TAC o SPECL [`v:real^1`; `u:real^1`]) THEN - ASM_REWRITE_TAC[REAL_LE_REFL; IN_INTERVAL_1] THEN - ASM_REWRITE_TAC[GSYM REAL_NOT_LE] THEN - GEN_REWRITE_TAC LAND_CONV [REAL_ARITH `v <= u <=> u <= v ==> u = v`] THEN - ASM_SIMP_TAC[DROP_EQ; REAL_NOT_LE; REAL_LE_ANTISYM] THEN - MESON_TAC[REAL_LT_REFL]; - DISCH_TAC] THEN - SUBGOAL_THEN - `ax = real_interval[(g:real^1->real^2) u$1,g v$1]` - SUBST_ALL_TAC THENL - [MP_TAC(ISPECL [`lift o (\t. (g:real^1->real^2)(t)$1)`; - `u:real^1`; `v:real^1`] - CONTINUOUS_INCREASING_IMAGE_INTERVAL_1) THEN - ASM_REWRITE_TAC[IMAGE_o; o_THM] THEN ANTS_TAC THENL - [ASM_REWRITE_TAC[INTERVAL_NE_EMPTY_1; LIFT_DROP; o_DEF] THEN - CONJ_TAC THENL - [MATCH_MP_TAC CONTINUOUS_ON_LIFT_COMPONENT_COMPOSE THEN - ASM_MESON_TAC[]; - REWRITE_TAC[REAL_LE_LT; DROP_EQ] THEN ASM_MESON_TAC[]]; - DISCH_THEN(MP_TAC o AP_TERM `IMAGE drop`) THEN - REWRITE_TAC[GSYM IMAGE_o; IMAGE_LIFT_DROP] THEN - DISCH_THEN SUBST1_TAC THEN - REWRITE_TAC[IMAGE_DROP_INTERVAL; LIFT_DROP]]; - ALL_TAC] THEN - FIRST_ASSUM(ASSUME_TAC o MATCH_MP ABSOLUTELY_REAL_INTEGRABLE_CONTINUOUS) THEN - FIRST_ASSUM(MP_TAC o MATCH_MP ABSOLUTELY_REAL_INTEGRABLE_IMP_INTEGRABLE) THEN - REWRITE_TAC[real_integrable_on] THEN DISCH_THEN(X_CHOOSE_TAC `m:real`) THEN - MP_TAC(SPECL [`f:real->real`; - `real_interval[(g:real^1->real^2) u$1,(g v)$1]`; - `m:real`] - HAS_REAL_INTEGRAL_AREA_UNDER_CURVE) THEN - ASM_REWRITE_TAC[] THEN ANTS_TAC THENL - [ASM SET_TAC[]; - REWRITE_TAC[HAS_MEASURE_MEASURABLE_MEASURE]] THEN - STRIP_TAC THEN ASM_REWRITE_TAC[] THEN - MP_TAC(ISPECL - [`lift o f o drop`; - `\t. lift((g:real^1->real^2)(t)$1)`; - `\t. lift((g':real^1->real^2)(t)$1)`; - `u:real^1`; `v:real^1`; `s:real^1->bool`] - ABSOLUTE_INTEGRAL_SUBSTITUTION) THEN - ASM_REWRITE_TAC[o_THM; LIFT_DROP; REAL_POS] THEN - ANTS_TAC THENL - [REPEAT CONJ_TAC THENL - [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_CONTINUOUS THEN - ASM_REWRITE_TAC[GSYM IMAGE_LIFT_REAL_INTERVAL; GSYM REAL_CONTINUOUS_ON]; - FIRST_X_ASSUM(MATCH_MP_TAC o GEN_REWRITE_RULE I - [ABSOLUTELY_CONTINUOUS_ON_COMPONENTWISE]) THEN - REWRITE_TAC[DIMINDEX_2; ARITH]; - X_GEN_TAC `x:real^1` THEN DISCH_TAC THEN - MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_AT_WITHIN THEN - SUBGOAL_THEN - `((g:real^1->real^2) has_vector_derivative g' x) (at x)` - MP_TAC THENL [ASM_SIMP_TAC[]; ALL_TAC] THEN - GEN_REWRITE_TAC LAND_CONV [HAS_VECTOR_DERIVATIVE_COMPONENTWISE_AT] THEN - SIMP_TAC[DIMINDEX_2; ARITH]; - REWRITE_TAC[REAL_LE_LT; DROP_EQ] THEN ASM_MESON_TAC[]]; - ALL_TAC] THEN - REWRITE_TAC[GSYM LIFT_CMUL] THEN MATCH_MP_TAC MONO_AND THEN CONJ_TAC THENL - [MATCH_MP_TAC(REWRITE_RULE[IMP_CONJ] - ABSOLUTELY_INTEGRABLE_EQ) THEN - EXPAND_TAC "f" THEN REWRITE_TAC[] THEN ASM_SIMP_TAC[]; - MATCH_MP_TAC EQ_IMP] THEN - BINOP_TAC THENL - [MATCH_MP_TAC INTEGRAL_EQ THEN - EXPAND_TAC "f" THEN REWRITE_TAC[] THEN ASM_SIMP_TAC[]; - FIRST_ASSUM(MP_TAC o GEN_REWRITE_RULE I - [HAS_REAL_INTEGRAL_INTEGRABLE_INTEGRAL]) THEN - SIMP_TAC[IMP_CONJ; REAL_INTEGRAL] THEN DISCH_TAC THEN - REWRITE_TAC[IMAGE_LIFT_REAL_INTERVAL; GSYM LIFT_EQ; LIFT_DROP]]);; - -let AREA_ABOVE_ARCLET = prove - (`!(g:real^1->real^2) g' u v s. - drop u <= drop v /\ - g v$1 <= g u$1 /\ - g absolutely_continuous_on interval[u,v] /\ - IMAGE g (interval[u,v]) SUBSET {z | z$2 <= &0} /\ - (!x y. x IN interval[u,v] /\ y IN interval[u,v] /\ - g x = g y ==> x = y) /\ - (!x y. x IN IMAGE g (interval[u,v]) /\ y IN IMAGE g (interval[u,v]) /\ - x$1 = y$1 ==> x = y) /\ - negligible s /\ - (!t. t IN interval[u,v] DIFF s - ==> (g has_vector_derivative g'(t)) (at t)) - ==> measurable {z:real^2 | ?w. w IN IMAGE g (interval[u,v]) /\ - w$1 = z$1 /\ w$2 <= z$2 /\ z$2 <= &0} /\ - (\t. lift(g'(t)$1 * g(t)$2)) absolutely_integrable_on interval[u,v] /\ - integral (interval[u,v]) (\t. lift(g'(t)$1 * g(t)$2)) = - lift(measure {z:real^2 | ?w. w IN IMAGE g (interval[u,v]) /\ - w$1 = z$1 /\ w$2 <= z$2 /\ z$2 <= &0})`, - REPEAT GEN_TAC THEN STRIP_TAC THEN - MP_TAC(ISPECL - [`(--) o (g:real^1->real^2)`; - `(--) o (g':real^1->real^2)`; - `u:real^1`; `v:real^1`; `s:real^1->bool`] - AREA_BELOW_ARCLET) THEN - ASM_REWRITE_TAC[o_THM; IMAGE_o] THEN ANTS_TAC THENL - [ASM_REWRITE_TAC[VECTOR_EQ_NEG2] THEN - ASM_SIMP_TAC[o_DEF; HAS_VECTOR_DERIVATIVE_NEG; REAL_LE_NEG2; - ABSOLUTELY_CONTINUOUS_ON_NEG; VECTOR_NEG_COMPONENT] THEN - ONCE_REWRITE_TAC[TAUT `p /\ q /\ r ==> s <=> p /\ q ==> r ==> s`] THEN - ONCE_REWRITE_TAC[FORALL_IN_IMAGE_2] THEN - ASM_REWRITE_TAC[REAL_EQ_NEG2; VECTOR_EQ_NEG2; IMP_IMP; GSYM CONJ_ASSOC; - VECTOR_NEG_COMPONENT] THEN - ONCE_REWRITE_TAC[SET_RULE - `IMAGE f s SUBSET {x | P x} <=> s SUBSET {x | P(f x)}`] THEN - ASM_REWRITE_TAC[REAL_NEG_GE0; VECTOR_NEG_COMPONENT]; - ALL_TAC] THEN - REWRITE_TAC[SET_RULE - `(?w:real^N. w IN IMAGE (--) s /\ P w) <=> (?w. w IN s /\ P(--w))`] THEN - SUBGOAL_THEN `!P. {z:real^2 | P z} = IMAGE (--) {z | P(--z)}` - (fun th -> GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) [th]) - THENL - [MP_TAC(VECTOR_ARITH `!w:real^2. --(--w) = w`) THEN SET_TAC[]; ALL_TAC] THEN - REWRITE_TAC[MEASURABLE_NEGATIONS; MEASURE_NEGATIONS] THEN - REWRITE_TAC[REAL_LE_NEG2; REAL_EQ_NEG2; VECTOR_NEG_COMPONENT] THEN - REWRITE_TAC[REAL_NEG_GE0; REAL_MUL_LNEG; REAL_MUL_RNEG; REAL_NEG_NEG] THEN - REWRITE_TAC[CONJ_ACI]);; - let GREEN_AREA_THEOREM = prove - (`!(g:real^1->real^2) g' u a b. + (`!(g:real^1->real^2) g' u a. simple_path g /\ pathstart g = a /\ pathfinish g = a /\ - b IN path_image g /\ a$1 < b$1 /\ a$2 = b$2 /\ - dist(a,b) = diameter(path_image g) /\ - convex(inside(path_image g)) /\ g absolutely_continuous_on interval[vec 0,vec 1] /\ negligible u /\ (!t. t IN interval[vec 0,vec 1] DIFF u ==> (g has_vector_derivative g'(t)) (at t)) - ==> (\t. lift(g'(t)$1 * g(t)$2)) absolutely_integrable_on + ==> (\t. lift(g'(t)$1 * g(t)$2)) integrable_on interval[vec 0,vec 1] /\ norm(integral (interval[vec 0,vec 1]) (\t. lift(g'(t)$1 * g(t)$2))) = measure(inside(path_image g))`, - - (*** Make a the origin (automation doesn't handle integral so more work) ***) - - MATCH_MP_TAC(MESON[] - `((!g g' u b. P g g' u (vec 0) b) ==> (!g g' u a b. P g g' u a b)) /\ - (!g g' u b. P g g' u (vec 0) b) - ==> !g g' u a b. P g g' u a b`) THEN - CONJ_TAC THENL - [DISCH_TAC THEN REPEAT GEN_TAC THEN STRIP_TAC THEN - FIRST_X_ASSUM(MP_TAC o SPECL - [`(\x. --a + x) o (g:real^1->real^2)`; - `g':real^1->real^2`; - `u:real^1->bool`; `--a + b:real^2`]) THEN - ASM_REWRITE_TAC(!invariant_under_translation) THEN ANTS_TAC THENL - [REWRITE_TAC[VECTOR_ADD_LINV; VEC_COMPONENT] THEN - ASM_REWRITE_TAC[NORM_ARITH `dist(vec 0,--a + b) = dist(a,b)`] THEN - REWRITE_TAC[VECTOR_NEG_COMPONENT; VECTOR_ADD_COMPONENT] THEN - REPLICATE_TAC 2 (CONJ_TAC THENL [ASM_REAL_ARITH_TAC; ALL_TAC]) THEN - ASM_SIMP_TAC[ABSOLUTELY_CONTINUOUS_ISOMETRIC_COMPOSE; - NORM_ARITH `dist(--a + x,--a + y) = dist(x,y)`] THEN - REPEAT STRIP_TAC THEN REWRITE_TAC[o_DEF] THEN - GEN_REWRITE_TAC LAND_CONV [GSYM VECTOR_ADD_LID] THEN - MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_ADD THEN - ASM_SIMP_TAC[HAS_VECTOR_DERIVATIVE_CONST]; - ALL_TAC] THEN - REWRITE_TAC[VECTOR_ADD_COMPONENT; VECTOR_NEG_COMPONENT] THEN - REWRITE_TAC[REAL_ARITH `x * (--a + y):real = x * y - a * x`] THEN - REWRITE_TAC[LIFT_SUB; LIFT_CMUL] THEN MATCH_MP_TAC MONO_AND THEN - CONJ_TAC THENL - [SUBGOAL_THEN - `(\t. (a:real^2)$2 % lift((g':real^1->real^2) t$1)) - absolutely_integrable_on interval [vec 0,vec 1]` - MP_TAC THENL - [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_CMUL THEN - MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_LIFT_COMPONENT THEN - REWRITE_TAC[DIMINDEX_2; ARITH] THEN MATCH_MP_TAC - ABSOLUTELY_INTEGRABLE_ABSOLUTELY_CONTINUOUS_DERIVATIVE THEN - ASM_MESON_TAC[HAS_VECTOR_DERIVATIVE_AT_WITHIN]; - REWRITE_TAC[GSYM IMP_CONJ_ALT]] THEN - DISCH_THEN(MP_TAC o MATCH_MP ABSOLUTELY_INTEGRABLE_ADD) THEN - REWRITE_TAC[VECTOR_SUB_ADD]; - MATCH_MP_TAC EQ_IMP THEN AP_THM_TAC THEN - AP_TERM_TAC THEN AP_TERM_TAC THEN REWRITE_TAC[integral] THEN - AP_TERM_TAC THEN ABS_TAC THEN - SUBGOAL_THEN - `((\t. (a:real^2)$2 % lift((g':real^1->real^2) t$1)) - has_integral a$2 % lift((pathfinish g:real^2)$1 - pathstart g$1)) - (interval[vec 0,vec 1])` - MP_TAC THENL - [MATCH_MP_TAC HAS_INTEGRAL_CMUL THEN - REWRITE_TAC[pathstart; pathfinish; LIFT_SUB] THEN MATCH_MP_TAC - FUNDAMENTAL_THEOREM_OF_CALCULUS_ABSOLUTELY_CONTINUOUS THEN - EXISTS_TAC `u:real^1->bool` THEN - ASM_SIMP_TAC[DROP_VEC; REAL_POS; DIMINDEX_2; ARITH; - ABSOLUTELY_CONTINUOUS_ON_LIFT_COMPONENT; - HAS_VECTOR_DERIVATIVE_LIFT_COMPONENT_AT; - HAS_VECTOR_DERIVATIVE_AT_WITHIN]; - ASM_REWRITE_TAC[REAL_SUB_REFL; LIFT_NUM; VECTOR_MUL_RZERO]] THEN - DISCH_THEN(fun th -> EQ_TAC THEN MP_TAC th) THEN - REWRITE_TAC[GSYM IMP_CONJ_ALT] THENL - [DISCH_THEN(MP_TAC o MATCH_MP HAS_INTEGRAL_ADD); - DISCH_THEN(MP_TAC o MATCH_MP HAS_INTEGRAL_SUB)] THEN - REWRITE_TAC[VECTOR_SUB_ADD; VECTOR_ADD_RID; VECTOR_SUB_RZERO]]; - REWRITE_TAC[VEC_COMPONENT; REAL_ARITH `&0:real = x <=> x = &0`] THEN - REWRITE_TAC[DIST_0]] THEN - - (*** WLOG assume we start with upper left-to-right part ***) - - MATCH_MP_TAC(MESON[] - `!Q. ((!g g' u b t:real^1. Q g b t ==> P g g' u b) - ==> (!g g' u b. P g g' u b)) /\ - (!g g' u b t. Q g b t ==> P g g' u b) - ==> !g g' u b. P g g' u b`) THEN - EXISTS_TAC - `\g b t. &0 < drop t /\ drop t < &1 /\ g t = b /\ - IMAGE g (interval[vec 0:real^1,t]) SUBSET {z:real^2 | &0 <= z$2} /\ - IMAGE g (interval[t,vec 1]) SUBSET {z | z$2 <= &0}` THEN - REWRITE_TAC[IMP_IMP] THEN CONJ_TAC THENL - [DISCH_TAC THEN REPEAT GEN_TAC THEN STRIP_TAC THEN - MP_TAC(ASSUME `(b:real^2) IN path_image g`) THEN - GEN_REWRITE_TAC (LAND_CONV o TOP_DEPTH_CONV) [path_image; IN_IMAGE] THEN - REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN - DISCH_THEN(X_CHOOSE_THEN `t:real^1` (STRIP_ASSUME_TAC o GSYM)) THEN - - SUBGOAL_THEN `&0 < drop t /\ drop t < &1` STRIP_ASSUME_TAC THENL - [ASM_REWRITE_TAC[REAL_LT_LE] THEN - ASM_REWRITE_TAC[DROP_EQ; GSYM DROP_VEC] THEN - RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THEN - ASM_MESON_TAC[VEC_COMPONENT; REAL_LT_REFL]; - ALL_TAC] THEN - - SUBGOAL_THEN - `IMAGE (g:real^1->real^2) (interval[vec 0,t]) SUBSET {z | &0 <= z$2} /\ - IMAGE (g:real^1->real^2) (interval[t,vec 1]) SUBSET {z | z$2 <= &0} \/ - IMAGE (g:real^1->real^2) (interval[vec 0,t]) SUBSET {z | z$2 <= &0} /\ - IMAGE (g:real^1->real^2) (interval[t,vec 1]) SUBSET {z | &0 <= z$2}` - MP_TAC THENL - [MP_TAC(ISPECL - [`inside(path_image g):real^2->bool`; `vec 0:real^2`; `b:real^2`] - CONVEX_OPEN_SEGMENT_CASES_ALT) THEN ASM_SIMP_TAC[GSYM lemma1] THEN - ANTS_TAC THENL - [ASM_MESON_TAC[HULL_INC; PATHSTART_IN_PATH_IMAGE]; - ASM_SIMP_TAC[JORDAN_INSIDE_OUTSIDE; INTERIOR_OPEN; OPEN_INSIDE; - CLOSED_PATH_IMAGE; SIMPLE_PATH_IMP_PATH]] THEN - STRIP_TAC THENL - [SUBGOAL_THEN - `IMAGE (g:real^1->real^2) (interval[vec 0,t]) = segment[vec 0,b] \/ - IMAGE g (interval[t,vec 1]) = segment[vec 0,b]` - MP_TAC THENL - [SUBGOAL_THEN - `connected_in (subtopology euclidean (path_image g)) - (segment(vec 0:real^2,b))` - MP_TAC THENL - [REWRITE_TAC[CONNECTED_IN_SUBTOPOLOGY] THEN - ASM_REWRITE_TAC[CONNECTED_IN_EUCLIDEAN; CONNECTED_SEGMENT]; - ALL_TAC] THEN - REWRITE_TAC[CONNECTED_IN_EQ_SUBSET_SEPARATED_UNION] THEN - DISCH_THEN(MP_TAC o SPECL - [`IMAGE (g:real^1->real^2) (interval(vec 0,t))`; - `IMAGE (g:real^1->real^2) (interval(t,vec 1))`] o CONJUNCT2) THEN - REWRITE_TAC[separated_in; TOPSPACE_EUCLIDEAN_SUBTOPOLOGY] THEN - REWRITE_TAC[CLOSURE_OF_SUBTOPOLOGY; EUCLIDEAN_CLOSURE_OF] THEN - ANTS_TAC THENL - [REWRITE_TAC[GSYM CONJ_ASSOC] THEN ONCE_REWRITE_TAC[TAUT - `p /\ q /\ r <=> (p /\ q) /\ (p /\ q ==> r)`] THEN - CONJ_TAC THENL - [REWRITE_TAC[path_image] THEN CONJ_TAC THEN - MATCH_MP_TAC IMAGE_SUBSET THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - SIMP_TAC[SET_RULE - `s SUBSET t ==> s INTER t = s /\ t INTER s = s /\ - s INTER t INTER u = s INTER u`] THEN - STRIP_TAC] THEN - REWRITE_TAC[CONJ_ASSOC] THEN CONJ_TAC THENL - [CONJ_TAC THEN MATCH_MP_TAC(SET_RULE - `!t'. t SUBSET t' /\ s INTER t' = {} ==> s INTER t = {}`) - THENL - [EXISTS_TAC `IMAGE (g:real^1->real^2) (interval[t,vec 1])`; - EXISTS_TAC `IMAGE (g:real^1->real^2) (interval[vec 0,t])`] THEN - (CONJ_TAC THENL - [MATCH_MP_TAC CLOSURE_MINIMAL THEN CONJ_TAC THENL - [MATCH_MP_TAC IMAGE_SUBSET THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - MATCH_MP_TAC COMPACT_IMP_CLOSED] THEN - MATCH_MP_TAC COMPACT_CONTINUOUS_IMAGE THEN - REWRITE_TAC[COMPACT_INTERVAL] THEN - MATCH_MP_TAC CONTINUOUS_ON_SUBSET THEN - EXISTS_TAC `interval[vec 0:real^1,vec 1]` THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL] THEN - ASM_MESON_TAC[simple_path; path]; - REWRITE_TAC[SET_RULE - `IMAGE g s INTER IMAGE g t = {} <=> - !x y. x IN s /\ y IN t /\ g x = g y ==> F`] THEN - MAP_EVERY X_GEN_TAC [`x:real^1`; `y:real^1`] THEN - REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN STRIP_TAC THEN - FIRST_X_ASSUM(MP_TAC o SPECL [`x:real^1`; `y:real^1`] o - CONJUNCT2 o GEN_REWRITE_RULE I [simple_path]) THEN - ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN - ASM_REWRITE_TAC[GSYM DROP_EQ; DROP_VEC; NOT_IMP] THEN - ASM_REAL_ARITH_TAC]); - UNDISCH_TAC `segment(vec 0:real^2,b) SUBSET path_image g` THEN - REWRITE_TAC[open_segment; path_image; GSYM IMAGE_UNION] THEN - MATCH_MP_TAC(SET_RULE - `IMAGE g (i DIFF j) SUBSET t - ==> s DIFF t SUBSET IMAGE g i - ==> s DIFF t SUBSET IMAGE g j`) THEN - MATCH_MP_TAC SUBSET_TRANS THEN - EXISTS_TAC `IMAGE (g:real^1->real^2) {vec 0,t,vec 1}` THEN - CONJ_TAC THENL - [MATCH_MP_TAC IMAGE_SUBSET THEN - REWRITE_TAC[SUBSET; IN_UNION; IN_DIFF; IN_INTERVAL_1; - IN_INSERT; DROP_VEC; GSYM DROP_EQ; NOT_IN_EMPTY] THEN - REAL_ARITH_TAC; - REWRITE_TAC[IMAGE_CLAUSES; INSERT_SUBSET; EMPTY_SUBSET] THEN - RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THEN - ASM_REWRITE_TAC[IN_INSERT]]]; - REWRITE_TAC[open_segment; OPEN_CLOSED_INTERVAL_1]] THEN - DISCH_THEN(MP_TAC o MATCH_MP (SET_RULE - `s DIFF t SUBSET IMAGE g (i DIFF i') \/ - s DIFF t SUBSET IMAGE g (j DIFF j') - ==> i' SUBSET i /\ j' SUBSET j /\ - t SUBSET IMAGE g i' /\ t SUBSET IMAGE g j' - ==> s SUBSET IMAGE g i \/ s SUBSET IMAGE g j`)) THEN - ANTS_TAC THENL - [REWRITE_TAC[INSERT_SUBSET; EMPTY_SUBSET] THEN - RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THEN - ASM_REWRITE_TAC[IMAGE_CLAUSES; IN_INSERT] THEN - ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; REAL_LE_REFL]; - ALL_TAC] THEN - MATCH_MP_TAC MONO_OR THEN - ASM_SIMP_TAC[GSYM PATH_IMAGE_SUBPATH; DROP_VEC] THEN - CONJ_TAC THEN DISCH_TAC THEN CONV_TAC SYM_CONV THEN - MATCH_MP_TAC CONNECTED_SUBSET_PATH_IMAGE_ARC THEN - ASM_REWRITE_TAC[CONNECTED_SEGMENT; PATHSTART_SUBPATH; - PATHFINISH_SUBPATH] THEN - RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THEN - ASM_REWRITE_TAC[ENDS_IN_SEGMENT] THEN - MATCH_MP_TAC ARC_SIMPLE_PATH_SUBPATH THEN - ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; REAL_LE_REFL; REAL_POS] THEN - ASM_MESON_TAC[VEC_COMPONENT; REAL_LT_REFL]; - ALL_TAC] THEN - DISCH_TAC THEN MP_TAC(ISPECL - [`inside(path_image g):real^2->bool`; - `vec 0:real^2`; `b:real^2`; `midpoint(vec 0,b):real^2`; - `basis 2:real^2`; `&0:real`] - CONVEX_TRIPLE_RELATIVE_FRONTIER) THEN - ASM_SIMP_TAC[DOT_BASIS; DIMINDEX_2; ARITH] THEN ANTS_TAC THENL - [ASM_REWRITE_TAC[midpoint; VECTOR_ADD_COMPONENT; VECTOR_MUL_COMPONENT; - VEC_COMPONENT; CART_EQ; FORALL_2; DIMINDEX_2] THEN - CONJ_TAC THENL [ALL_TAC; ASM_REAL_ARITH_TAC] THEN - MATCH_MP_TAC SUBSET_TRANS THEN - EXISTS_TAC `segment[vec 0:real^2,b]` THEN - REWRITE_TAC[GSYM midpoint; INSERT_SUBSET; EMPTY_SUBSET] THEN - REWRITE_TAC[ENDS_IN_SEGMENT; MIDPOINT_IN_SEGMENT] THEN - ASM_SIMP_TAC[RELATIVE_FRONTIER_OPEN; JORDAN_INSIDE_OUTSIDE] THEN - REWRITE_TAC[path_image] THEN FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP - (SET_RULE `IMAGE g s = v \/ IMAGE g t = v - ==> s SUBSET u /\ t SUBSET u ==> v SUBSET IMAGE g u`)) THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL]; - ALL_TAC] THEN - DISCH_THEN(DISJ_CASES_THEN (MP_TAC o MATCH_MP SUBSET_CLOSURE)) THEN - SIMP_TAC[CLOSURE_CLOSED; CLOSED_HALFSPACE_COMPONENT_GE; - CLOSED_HALFSPACE_COMPONENT_LE] THEN - ASM_SIMP_TAC[GSYM lemma1] THEN DISCH_THEN(MP_TAC o MATCH_MP - (MESON[SUBSET_TRANS; HULL_SUBSET] - `convex hull s SUBSET t ==> s SUBSET t`)) THEN - (SUBGOAL_THEN - `path_image g = - IMAGE (g:real^1->real^2) (interval[vec 0,t]) UNION - IMAGE (g:real^1->real^2) (interval[t,vec 1])` - SUBST1_TAC THENL - [REWRITE_TAC[GSYM IMAGE_UNION; path_image] THEN AP_TERM_TAC THEN - REWRITE_TAC[EXTENSION; IN_UNION; IN_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - SIMP_TAC[UNION_SUBSET; real_ge] THEN DISCH_THEN(K ALL_TAC)]) - THENL [ALL_TAC; ONCE_REWRITE_TAC[DISJ_SYM]] THEN - POP_ASSUM MP_TAC THEN MATCH_MP_TAC MONO_OR THEN - CONJ_TAC THEN DISCH_THEN SUBST1_TAC THEN - MATCH_MP_TAC SEGMENT_SUBSET_CONVEX THEN - ASM_REWRITE_TAC[VEC_COMPONENT; IN_ELIM_THM; REAL_LE_REFL] THEN - REWRITE_TAC[CONVEX_HALFSPACE_COMPONENT_LE] THEN - ONCE_REWRITE_TAC[GSYM real_ge] THEN - REWRITE_TAC[CONVEX_HALFSPACE_COMPONENT_GE]; - - SUBGOAL_THEN - `IMAGE (g:real^1->real^2) - (interval(vec 0,t) UNION interval(t,vec 1)) INTER - {z | z$2 = &0} = {}` - MP_TAC THENL - [FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `s SUBSET inside p - ==> inside p INTER p = {} /\ i SUBSET p /\ i INTER h SUBSET s - ==> i INTER h = {}`)) THEN - REWRITE_TAC[INSIDE_NO_OVERLAP] THEN CONJ_TAC THENL - [REWRITE_TAC[path_image; IMAGE_UNION; UNION_SUBSET] THEN - CONJ_TAC THEN MATCH_MP_TAC IMAGE_SUBSET THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - REWRITE_TAC[open_segment; OPEN_CLOSED_INTERVAL_1] THEN - MATCH_MP_TAC(SET_RULE - `((!w. w IN (i UNION j) /\ g w = b ==> w = y) /\ - (!w. w IN i /\ g w = a ==> w = x) /\ - (!w. w IN j /\ g w = a ==> w = z)) /\ - IMAGE g (i UNION j) INTER h SUBSET s - ==> IMAGE g ((i DIFF {x,y}) UNION (j DIFF {y,z})) INTER h - SUBSET s DIFF {a,b}`) THEN - CONJ_TAC THENL - [REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; IN_UNION; GSYM DROP_EQ] THEN - REPEAT CONJ_TAC THEN X_GEN_TAC `z:real^1` THEN STRIP_TAC THEN - FIRST_X_ASSUM(MP_TAC o CONJUNCT2 o - GEN_REWRITE_RULE I [simple_path]) THEN - RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THENL - [DISCH_THEN(MP_TAC o SPECL [`t:real^1`; `z:real^1`]); - DISCH_THEN(MP_TAC o SPECL [`t:real^1`; `z:real^1`]); - DISCH_THEN(MP_TAC o SPECL [`vec 0:real^1`; `z:real^1`]); - DISCH_THEN(MP_TAC o SPECL [`vec 0:real^1`; `z:real^1`])] THEN - ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; GSYM DROP_EQ] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - SUBGOAL_THEN - `IMAGE (g:real^1->real^2) - (interval[vec 0,t] UNION interval[t,vec 1]) = - path_image g` - SUBST1_TAC THENL - [REWRITE_TAC[path_image] THEN AP_TERM_TAC THEN - REWRITE_TAC[EXTENSION; IN_UNION; IN_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - REWRITE_TAC[SUBSET; IN_INTER; IN_ELIM_THM] THEN - X_GEN_TAC `z:real^2` THEN STRIP_TAC THEN - SUBGOAL_THEN - `dist(vec 0:real^2,z) <= norm(b:real^2) /\ - dist(b,z) <= norm(b:real^2)` - MP_TAC THENL - [ASM_REWRITE_TAC[] THEN CONJ_TAC THEN - MATCH_MP_TAC DIST_LE_DIAMETER THEN - ASM_SIMP_TAC[BOUNDED_PATH_IMAGE; SIMPLE_PATH_IMP_PATH] THEN - ASM_MESON_TAC[PATHSTART_IN_PATH_IMAGE; PATHFINISH_IN_PATH_IMAGE]; - REWRITE_TAC[dist; NORM_LE_SQUARE]] THEN - REWRITE_TAC[NORM_POS_LE; DOT_2; NORM_POW_2] THEN - REWRITE_TAC[IN_SEGMENT; CART_EQ; DIMINDEX_2; FORALL_2] THEN - ASM_REWRITE_TAC[VECTOR_SUB_COMPONENT; VECTOR_ADD_COMPONENT; - VECTOR_MUL_COMPONENT; VEC_COMPONENT] THEN - CONV_TAC REAL_RAT_REDUCE_CONV THEN - REWRITE_TAC[REAL_MUL_RZERO; REAL_SUB_LZERO; REAL_ADD_RID] THEN - REWRITE_TAC[GSYM REAL_POW_2; GSYM REAL_LE_SQUARE_ABS] THEN - REWRITE_TAC[REAL_ABS_NEG] THEN DISCH_THEN(MP_TAC o MATCH_MP - (REAL_ARITH `abs z <= abs b /\ abs(b - z) <= abs b - ==> &0 < b ==> &0 <= z /\ z <= b`)) THEN - ASM_REWRITE_TAC[] THEN STRIP_TAC THEN - EXISTS_TAC `(z:real^2)$1 / (b:real^2)$1` THEN - ASM_SIMP_TAC[REAL_LE_LDIV_EQ; REAL_LE_RDIV_EQ; REAL_DIV_RMUL; - REAL_LT_IMP_NZ] THEN - ASM_REAL_ARITH_TAC; - REWRITE_TAC[IMAGE_UNION; EMPTY_UNION; SET_RULE - `(s UNION t) INTER u = s INTER u UNION t INTER u`] THEN - STRIP_TAC] THEN - MATCH_MP_TAC(TAUT - `(~(p1 /\ q2) /\ ~(p2 /\ q1)) /\ (p1 \/ p2) /\ (q1 \/ q2) - ==> p1 /\ q1 \/ p2 /\ q2`) THEN - CONJ_TAC THENL - [REWRITE_TAC[GSYM UNION_SUBSET; GSYM IMAGE_UNION] THEN - ASM_SIMP_TAC[UNION_INTERVAL_1; IN_INTERVAL_1; DROP_VEC] THEN - REWRITE_TAC[GSYM path_image] THEN CONJ_TAC THEN - DISCH_THEN(MP_TAC o SPEC `convex:(real^2->bool)->bool` o - MATCH_MP HULL_MONO) THEN - ASM_SIMP_TAC[REWRITE_RULE[pathstart; pathfinish] lemma1] THEN - DISCH_THEN(MP_TAC o MATCH_MP SUBSET_INTERIOR) THEN - W(MP_TAC o PART_MATCH rand INSIDE_SUBSET_INTERIOR_CONVEX_HULL o - lhand o lhand o snd) THEN - REWRITE_TAC[IMP_IMP] THEN - DISCH_THEN(MP_TAC o MATCH_MP SUBSET_TRANS) THEN - SIMP_TAC[HULL_P; CONVEX_HALFSPACE_COMPONENT_LE; - REWRITE_RULE[real_ge] CONVEX_HALFSPACE_COMPONENT_GE; - REWRITE_RULE[real_ge] INTERIOR_HALFSPACE_COMPONENT_GE; - INTERIOR_HALFSPACE_COMPONENT_LE] THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `s SUBSET i ==> ~(s SUBSET j) ==> ~(i SUBSET j)`)) THEN - REWRITE_TAC[SUBSET; IN_ELIM_THM] THEN - DISCH_THEN(MP_TAC o SPEC `midpoint(vec 0,b):real^2`) THEN - REWRITE_TAC[MIDPOINT_IN_SEGMENT; NOT_IMP] THEN - (CONJ_TAC THENL - [ASM_MESON_TAC[VEC_COMPONENT; REAL_LT_REFL]; - REWRITE_TAC[midpoint; VECTOR_MUL_COMPONENT]]) THEN - ASM_REWRITE_TAC[VECTOR_ADD_COMPONENT; VEC_COMPONENT] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - ASM_SIMP_TAC[CLOSED_OPEN_INTERVAL_1; DROP_VEC] THEN - REWRITE_TAC[IMAGE_UNION; UNION_SUBSET; IMAGE_CLAUSES; INSERT_SUBSET; - IN_ELIM_THM; EMPTY_SUBSET] THEN - RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THEN - ASM_REWRITE_TAC[VEC_COMPONENT; REAL_LE_REFL] THEN - REWRITE_TAC[REAL_LE_LT] THEN CONJ_TAC THEN - MATCH_MP_TAC(SET_RULE - `s SUBSET {x | P x} \/ s SUBSET {x | P' x} - ==> s SUBSET {x | P x \/ Q x} \/ s SUBSET {x | P' x \/ Q' x}`) THEN - MATCH_MP_TAC CONNECTED_IN_SUBSET_SEPARATED_UNION THEN - EXISTS_TAC `euclidean:(real^2)topology` THEN - ASM_SIMP_TAC[CONNECTED_IN_EUCLIDEAN; separated_in; SUBSET_UNIV; - EUCLIDEAN_CLOSURE_OF; TOPSPACE_EUCLIDEAN] THEN - REWRITE_TAC[SET_RULE `{x | P x} UNION {x | Q x} = {x | P x \/ Q x}`; - REAL_ARITH `&0 < x \/ x < &0 <=> ~(x = &0)`; - REAL_ARITH `x < &0 \/ &0 < x <=> ~(x = &0)`] THEN - ASM_REWRITE_TAC[SET_RULE - `s SUBSET {x | ~P x} <=> s INTER {x | P x} = {}`] THEN - ASM_SIMP_TAC[CLOSURE_HALFSPACE_COMPONENT_LT; - REWRITE_RULE[real_ge; real_gt] CLOSURE_HALFSPACE_COMPONENT_GT] THEN - REWRITE_TAC[EXTENSION; IN_INTER; IN_ELIM_THM; NOT_IN_EMPTY] THEN - REWRITE_TAC[REAL_LET_ANTISYM; REAL_LTE_ANTISYM] THEN - MATCH_MP_TAC CONNECTED_CONTINUOUS_IMAGE THEN - REWRITE_TAC[CONNECTED_INTERVAL] THEN - FIRST_X_ASSUM(MP_TAC o MATCH_MP SIMPLE_PATH_IMP_PATH) THEN - REWRITE_TAC[path] THEN - MATCH_MP_TAC(REWRITE_RULE[IMP_CONJ_ALT] CONTINUOUS_ON_SUBSET) THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC]; - ALL_TAC] THEN - STRIP_TAC THENL - [FIRST_X_ASSUM MATCH_MP_TAC THEN - MAP_EVERY EXISTS_TAC [`u:real^1->bool`; `b:real^2`; `t:real^1`] THEN - ASM_REWRITE_TAC[]; - ALL_TAC] THEN - - FIRST_X_ASSUM(MP_TAC o SPECL - [`reflect_along (basis 2) o (g:real^1->real^2)`; - `reflect_along (basis 2) o (g':real^1->real^2)`; - `u:real^1->bool`; `b:real^2`; `t:real^1`]) THEN - ASM_REWRITE_TAC[o_THM; IMAGE_o] THEN - MP_TAC(ISPEC `basis 2:real^2` - ORTHOGONAL_TRANSFORMATION_REFLECT_ALONG) THEN - DISCH_TAC THEN - FIRST_ASSUM(STRIP_ASSUME_TAC o - GEN_REWRITE_RULE I [ORTHOGONAL_TRANSFORMATION]) THEN - FIRST_ASSUM(MP_TAC o MATCH_MP - ORTHOGONAL_TRANSFORMATION_INJECTIVE) THEN - REWRITE_TAC[INJECTIVE_ALT] THEN DISCH_TAC THEN - FIRST_ASSUM(ASSUME_TAC o MATCH_MP - ORTHOGONAL_TRANSFORMATION_SURJECTIVE) THEN - ASM_SIMP_TAC(!invariant_under_linear) THEN ANTS_TAC THENL - [ONCE_REWRITE_TAC[SET_RULE - `IMAGE f s SUBSET {x | P x} <=> s SUBSET {x | P(f x)}`] THEN - REWRITE_TAC[IN_IMAGE; CART_EQ; FORALL_2; DIMINDEX_2] THEN - ASM_SIMP_TAC[REFLECT_ALONG_BASIS_COMPONENT; VEC_COMPONENT; - DIMINDEX_2; ARITH; REAL_NEG_0] THEN - ASM_REWRITE_TAC[REAL_NEG_LE0; REAL_NEG_GE0] THEN - REWRITE_TAC[REAL_ARITH `&0 = --x <=> x = &0`] THEN - ASM_SIMP_TAC[ABSOLUTELY_CONTINUOUS_ON_COMPOSE_LINEAR] THEN - CONJ_TAC THENL [ASM_MESON_TAC[]; ALL_TAC] THEN - X_GEN_TAC `x:real^1` THEN STRIP_TAC THEN - REWRITE_TAC[has_vector_derivative] THEN MP_TAC(ISPECL - [`g:real^1->real^2`; `reflect_along (basis 2):real^2->real^2`; - `\y. drop y % (g':real^1->real^2) x`; - `reflect_along (basis 2):real^2->real^2`; `x:real^1`] - DIFF_CHAIN_AT) THEN - ASM_SIMP_TAC[HAS_DERIVATIVE_LINEAR; GSYM has_vector_derivative] THEN - REWRITE_TAC[has_vector_derivative; o_DEF] THEN - ASM_SIMP_TAC[LINEAR_CMUL]; - - ASM_SIMP_TAC[REFLECT_ALONG_BASIS_COMPONENT; DIMINDEX_2; ARITH] THEN - REWRITE_TAC[REAL_MUL_RNEG; LIFT_NEG; IMP_CONJ] THEN - SIMP_TAC[ABSOLUTELY_INTEGRABLE_NEG_EQ; INTEGRAL_NEG; - ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE; NORM_NEG]]; - - ALL_TAC] THEN - REPEAT GEN_TAC THEN STRIP_TAC THEN - - SUBGOAL_THEN - `!z. z IN path_image g ==> &0 <= (z:real^2)$1 /\ z$1 <= (b:real^2)$1` - ASSUME_TAC THENL - [REPEAT GEN_TAC THEN STRIP_TAC THEN - SUBGOAL_THEN - `dist(vec 0:real^2,z) <= norm(b:real^2) /\ - dist(b,z) <= norm(b:real^2)` - MP_TAC THENL - [ASM_REWRITE_TAC[] THEN CONJ_TAC THEN - MATCH_MP_TAC DIST_LE_DIAMETER THEN - ASM_SIMP_TAC[BOUNDED_PATH_IMAGE; SIMPLE_PATH_IMP_PATH] THEN - ASM_MESON_TAC[PATHSTART_IN_PATH_IMAGE; PATHFINISH_IN_PATH_IMAGE]; - REWRITE_TAC[dist; VECTOR_SUB_LZERO; NORM_NEG] THEN - DISCH_THEN(MP_TAC o MATCH_MP (REAL_ARITH - `norm(z:real^2) <= b /\ norm(w:real^2) <= b - ==> abs(z$1) <= norm z /\ abs(w$1) <= norm w - ==> abs(z$1) <= b /\ abs(w$1) <= b`)) THEN - REWRITE_TAC[COMPONENT_LE_NORM] THEN - REWRITE_TAC[VECTOR_SUB_COMPONENT] THEN - REWRITE_TAC[vector_norm; DOT_2] THEN - ASM_REWRITE_TAC[REAL_MUL_RZERO; REAL_ADD_RID] THEN - REWRITE_TAC[GSYM REAL_POW_2; POW_2_SQRT_ABS] THEN - ASM_REAL_ARITH_TAC]; - ALL_TAC] THEN - - MP_TAC(SPECL [`g:real^1->real^2`; `g':real^1->real^2`; - `t:real^1`; `vec 1:real^1`; `u:real^1->bool`] - AREA_ABOVE_ARCLET) THEN - MP_TAC(SPECL [`g:real^1->real^2`; `g':real^1->real^2`; - `vec 0:real^1`; `t:real^1`; `u:real^1->bool`] - AREA_BELOW_ARCLET) THEN - ASM_REWRITE_TAC[] THEN MATCH_MP_TAC(TAUT - `(p1 /\ p2) /\ (q1 /\ q2 ==> r) ==> (p1 ==> q1) ==> (p2 ==> q2) ==> r`) THEN - CONJ_TAC THENL - [RULE_ASSUM_TAC(REWRITE_RULE[pathstart; pathfinish]) THEN CONJ_TAC THEN - (CONJ_TAC THENL - [ASM_SIMP_TAC[REAL_LT_IMP_LE; VEC_COMPONENT; DROP_VEC]; ALL_TAC] THEN - CONJ_TAC THENL - [ASM_SIMP_TAC[REAL_LT_IMP_LE; VEC_COMPONENT]; ALL_TAC] THEN - CONJ_TAC THENL - [FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (REWRITE_RULE[IMP_CONJ] - ABSOLUTELY_CONTINUOUS_ON_SUBSET)) THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - CONJ_TAC THENL - [MAP_EVERY X_GEN_TAC [`x:real^1`; `y:real^1`] THEN - REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN STRIP_TAC THEN - FIRST_X_ASSUM(MP_TAC o CONJUNCT2 o REWRITE_RULE[simple_path]) THEN - DISCH_THEN(MP_TAC o SPECL [`x:real^1`; `y:real^1`]) THEN - ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; GSYM DROP_EQ] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - CONJ_TAC THENL - [ALL_TAC; - X_GEN_TAC `x:real^1` THEN - REWRITE_TAC[IN_DIFF; IN_INTERVAL_1; DROP_VEC] THEN - STRIP_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN - ASM_REWRITE_TAC[IN_DIFF; IN_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC] THEN - MAP_EVERY X_GEN_TAC [`x:real^2`; `y:real^2`] THEN - ABBREV_TAC `c = (y:real^2)$1` THEN STRIP_TAC THEN - GEN_REWRITE_TAC I [TAUT `p <=> ~p ==> F`] THEN DISCH_TAC THEN - - ASM_CASES_TAC `c:real = &0` THENL - [SUBGOAL_THEN - `dist(b:real^2,x) <= norm(b:real^2) /\ - dist(b:real^2,y) <= norm(b:real^2)` - MP_TAC THENL - [ASM_REWRITE_TAC[] THEN CONJ_TAC THEN - MATCH_MP_TAC DIST_LE_DIAMETER THEN - ASM_SIMP_TAC[BOUNDED_PATH_IMAGE; SIMPLE_PATH_IMP_PATH] THEN - REWRITE_TAC[path_image] THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `w IN IMAGE g s ==> s SUBSET t ==> w IN IMAGE g t`)) THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL] THEN - ASM_REAL_ARITH_TAC; - REWRITE_TAC[dist; NORM_LE_SQUARE]] THEN - REWRITE_TAC[NORM_POS_LE; DOT_2; NORM_POW_2] THEN - ASM_REWRITE_TAC[VECTOR_SUB_COMPONENT; VECTOR_ADD_COMPONENT; - VECTOR_MUL_COMPONENT; VEC_COMPONENT] THEN - REWRITE_TAC[REAL_SUB_RZERO; REAL_SUB_LZERO; REAL_MUL_LZERO; - REAL_NEG_NEG; REAL_ADD_LID; REAL_MUL_LNEG; - REAL_MUL_RNEG; REAL_ADD_RID] THEN - DISCH_THEN(CONJUNCTS_THEN (MP_TAC o MATCH_MP - (REAL_ARITH `b + x:real <= b ==> &0 <= x ==> x = &0`))) THEN - REWRITE_TAC[REAL_LE_SQUARE; REAL_ENTIRE] THEN - REPEAT STRIP_TAC THEN UNDISCH_TAC `~(x:real^2 = y)` THEN - ASM_REWRITE_TAC[CART_EQ; FORALL_2; DIMINDEX_2]; - ALL_TAC] THEN - - ASM_CASES_TAC `c:real = (b:real^2)$1` THENL - [SUBGOAL_THEN - `dist(vec 0:real^2,x) <= norm(b:real^2) /\ - dist(vec 0:real^2,y) <= norm(b:real^2)` - MP_TAC THENL - [ASM_REWRITE_TAC[] THEN CONJ_TAC THEN - MATCH_MP_TAC DIST_LE_DIAMETER THEN - ASM_SIMP_TAC[BOUNDED_PATH_IMAGE; SIMPLE_PATH_IMP_PATH] THEN - (CONJ_TAC THENL - [ASM_MESON_TAC[PATHSTART_IN_PATH_IMAGE; pathstart]; - ALL_TAC]) THEN - REWRITE_TAC[path_image] THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `w IN IMAGE g s ==> s SUBSET t ==> w IN IMAGE g t`)) THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL] THEN - ASM_REAL_ARITH_TAC; - REWRITE_TAC[dist; NORM_LE_SQUARE]] THEN - REWRITE_TAC[NORM_POS_LE; DOT_2; NORM_POW_2] THEN - ASM_REWRITE_TAC[VECTOR_SUB_COMPONENT; VECTOR_ADD_COMPONENT; - VECTOR_MUL_COMPONENT; VEC_COMPONENT] THEN - REWRITE_TAC[REAL_SUB_RZERO; REAL_SUB_LZERO; REAL_MUL_LZERO; - REAL_NEG_NEG; REAL_ADD_LID; REAL_MUL_LNEG; - REAL_MUL_RNEG; REAL_ADD_RID] THEN - DISCH_THEN(CONJUNCTS_THEN (MP_TAC o MATCH_MP - (REAL_ARITH `b + x:real <= b ==> &0 <= x ==> x = &0`))) THEN - REWRITE_TAC[REAL_LE_SQUARE; REAL_ENTIRE] THEN - REPEAT STRIP_TAC THEN UNDISCH_TAC `~(x:real^2 = y)` THEN - ASM_REWRITE_TAC[CART_EQ; FORALL_2; DIMINDEX_2]; - ALL_TAC] THEN - - SUBGOAL_THEN `&0 < c /\ c < (b:real^2)$1` STRIP_ASSUME_TAC THENL - [ASM_REWRITE_TAC[REAL_LT_LE] THEN EXPAND_TAC "c" THEN - FIRST_X_ASSUM MATCH_MP_TAC THEN REWRITE_TAC[path_image] THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `w IN IMAGE g s ==> s SUBSET t ==> w IN IMAGE g t`)) THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC]) - THENL - [SUBGOAL_THEN - `?z. z IN IMAGE (g:real^1->real^2) (interval(t,vec 1)) /\ - z$1 = c` - STRIP_ASSUME_TAC THENL - [REWRITE_TAC[OPEN_CLOSED_INTERVAL_1] THEN MATCH_MP_TAC(SET_RULE - `(?z. z IN IMAGE g s /\ P z) /\ ~P(g a) /\ ~P(g b) - ==> ?z. z IN IMAGE g (s DIFF {a,b}) /\ P z`) THEN - ASM_SIMP_TAC[VEC_COMPONENT; REAL_LT_IMP_NE] THEN - - MATCH_MP_TAC CONNECTED_IVT_COMPONENT THEN - REWRITE_TAC[DIMINDEX_2; ARITH] THEN - REWRITE_TAC[RIGHT_EXISTS_AND_THM; EXISTS_IN_IMAGE] THEN - REWRITE_TAC[RIGHT_AND_EXISTS_THM] THEN - MAP_EVERY EXISTS_TAC [`vec 1:real^1`; `t:real^1`] THEN - ASM_SIMP_TAC[IN_INTERVAL_1; DROP_VEC; REAL_LT_IMP_LE; - VEC_COMPONENT; REAL_LE_REFL] THEN - MATCH_MP_TAC CONNECTED_CONTINUOUS_IMAGE THEN - REWRITE_TAC[CONNECTED_INTERVAL] THEN - FIRST_X_ASSUM(MP_TAC o MATCH_MP SIMPLE_PATH_IMP_PATH) THEN - REWRITE_TAC[path] THEN - MATCH_MP_TAC(REWRITE_RULE[IMP_CONJ_ALT] CONTINUOUS_ON_SUBSET) THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC]; - - SUBGOAL_THEN - `?z. z IN IMAGE (g:real^1->real^2) (interval(vec 0,t)) /\ - z$1 = c` - STRIP_ASSUME_TAC THENL - [REWRITE_TAC[OPEN_CLOSED_INTERVAL_1] THEN MATCH_MP_TAC(SET_RULE - `(?z. z IN IMAGE g s /\ P z) /\ ~P(g a) /\ ~P(g b) - ==> ?z. z IN IMAGE g (s DIFF {a,b}) /\ P z`) THEN - ASM_SIMP_TAC[VEC_COMPONENT; REAL_LT_IMP_NE] THEN - - MATCH_MP_TAC CONNECTED_IVT_COMPONENT THEN - REWRITE_TAC[DIMINDEX_2; ARITH] THEN - REWRITE_TAC[RIGHT_EXISTS_AND_THM; EXISTS_IN_IMAGE] THEN - REWRITE_TAC[RIGHT_AND_EXISTS_THM] THEN - MAP_EVERY EXISTS_TAC [`vec 0:real^1`; `t:real^1`] THEN - ASM_SIMP_TAC[IN_INTERVAL_1; DROP_VEC; REAL_LT_IMP_LE; - VEC_COMPONENT; REAL_LE_REFL] THEN - MATCH_MP_TAC CONNECTED_CONTINUOUS_IMAGE THEN - REWRITE_TAC[CONNECTED_INTERVAL] THEN - FIRST_X_ASSUM(MP_TAC o MATCH_MP SIMPLE_PATH_IMP_PATH) THEN - REWRITE_TAC[path] THEN - MATCH_MP_TAC(REWRITE_RULE[IMP_CONJ_ALT] CONTINUOUS_ON_SUBSET) THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC]] THEN - (MP_TAC(ISPECL - [`convex hull (path_image g:real^2->bool)`; - `x:real^2`; `y:real^2`; `z:real^2`; `basis 1:real^2`; `c:real`] - CONVEX_TRIPLE_RELATIVE_FRONTIER) THEN - SIMP_TAC[DOT_BASIS; DIMINDEX_2; ARITH] THEN - ASM_REWRITE_TAC[CONVEX_CONVEX_HULL; NOT_IMP; GSYM CONJ_ASSOC] THEN - SUBGOAL_THEN `relative_frontier (convex hull path_image g):real^2->bool = - path_image g` - SUBST1_TAC THENL - [TRANS_TAC EQ_TRANS - `frontier(convex hull path_image g):real^2->bool` THEN - CONJ_TAC THENL - [ALL_TAC; ASM_SIMP_TAC[lemma2; pathstart; pathfinish]] THEN - MATCH_MP_TAC RELATIVE_FRONTIER_NONEMPTY_INTERIOR THEN - ASM_SIMP_TAC[lemma1; CONVEX_INTERIOR_CLOSURE; pathstart; pathfinish; - INTERIOR_OPEN; JORDAN_INSIDE_OUTSIDE]; - ALL_TAC] THEN - CONJ_TAC THENL - [REWRITE_TAC[INSERT_SUBSET; EMPTY_SUBSET; path_image] THEN - REPEAT CONJ_TAC THEN FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `w IN IMAGE g s ==> s SUBSET t ==> w IN IMAGE g t`)) THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL] THEN - ASM_REAL_ARITH_TAC; - GEN_REWRITE_TAC I [CONJ_ASSOC]] THEN - CONJ_TAC THENL - [CONJ_TAC THEN FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `z IN s ==> ~(x IN s) ==> ~(x = z)`)) THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `x IN s ==> DISJOINT s t ==> ~(x IN t)`)) THEN - REWRITE_TAC[SET_RULE - `DISJOINT (IMAGE f s) (IMAGE f t) <=> - !x y. x IN s /\ y IN t ==> ~(f x = f y)`] THEN - MAP_EVERY X_GEN_TAC [`x:real^1`; `y:real^1`] THEN - REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN REPEAT STRIP_TAC THEN - FIRST_X_ASSUM(MP_TAC o SPECL [`x:real^1`; `y:real^1`] o - CONJUNCT2 o GEN_REWRITE_RULE I [simple_path]) THEN - ASM_REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN - ASM_REWRITE_TAC[GSYM DROP_EQ; DROP_VEC; NOT_IMP] THEN - ASM_REAL_ARITH_TAC; - ALL_TAC] THEN - REWRITE_TAC[DE_MORGAN_THM] THEN CONJ_TAC THEN DISCH_THEN - (MP_TAC o MATCH_MP (MESON[HULL_SUBSET; SUBSET_TRANS] - `convex hull s SUBSET t ==> s SUBSET t`)) THEN - REWRITE_TAC[SUBSET; NOT_FORALL_THM; IN_ELIM_THM] THENL - [EXISTS_TAC `b:real^2` THEN ASM_REWRITE_TAC[REAL_NOT_LE]; - EXISTS_TAC `vec 0:real^2` THEN - ASM_REWRITE_TAC[real_ge; NOT_IMP; REAL_NOT_LE; VEC_COMPONENT] THEN - ASM_MESON_TAC[pathstart; PATHSTART_IN_PATH_IMAGE]]); - - ALL_TAC] THEN - - DISCH_THEN(CONJUNCTS_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC)) THEN - REWRITE_TAC[HAS_ABSOLUTE_INTEGRAL] THEN MATCH_MP_TAC(TAUT - `(p2 /\ p1 ==> p) /\ (q2 /\ q1 ==> q) - ==> p1 /\ q1 ==> p2 /\ q2 ==> p /\ q`) THEN - CONJ_TAC THENL - [STRIP_TAC THEN MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_COMBINE THEN - EXISTS_TAC `t:real^1` THEN ASM_SIMP_TAC[DROP_VEC; REAL_LT_IMP_LE]; - DISCH_THEN(MP_TAC o MATCH_MP - (REWRITE_RULE[TAUT `p /\ q /\ r /\ s ==> t <=> - r /\ s ==> p /\ q ==> t`] HAS_INTEGRAL_COMBINE)) THEN - ASM_SIMP_TAC[DROP_VEC; REAL_LT_IMP_LE]] THEN - DISCH_THEN(MP_TAC o MATCH_MP INTEGRAL_UNIQUE) THEN - DISCH_THEN SUBST1_TAC THEN REWRITE_TAC[GSYM LIFT_ADD; NORM_LIFT] THEN - MATCH_MP_TAC(REAL_ARITH - `&0 <= x /\ &0 <= y /\ x + y = z ==> abs(x + y) = z`) THEN - ASM_SIMP_TAC[MEASURE_POS_LE] THEN - - W(MP_TAC o PART_MATCH (rand o rand) MEASURE_NEGLIGIBLE_UNION o - lhand o snd) THEN - ASM_REWRITE_TAC[] THEN ANTS_TAC THENL - [MATCH_MP_TAC NEGLIGIBLE_SUBSET THEN - EXISTS_TAC `{z:real^2 | z$2 = &0}` THEN - REWRITE_TAC[NEGLIGIBLE_STANDARD_HYPERPLANE] THEN - REWRITE_TAC[GSYM REAL_LE_ANTISYM] THEN SET_TAC[]; - DISCH_THEN(SUBST1_TAC o SYM)] THEN - - MATCH_MP_TAC EQ_TRANS THEN - EXISTS_TAC `measure(convex hull (path_image g):real^2->bool)` THEN - CONJ_TAC THENL - [ALL_TAC; - ASM_SIMP_TAC[lemma1] THEN MATCH_MP_TAC MEASURE_CLOSURE THEN - ASM_SIMP_TAC[BOUNDED_INSIDE; BOUNDED_PATH_IMAGE; SIMPLE_PATH_IMP_PATH; - NEGLIGIBLE_CONVEX_FRONTIER]] THEN - - SUBGOAL_THEN - `frontier(convex hull (path_image g)):real^2->bool = path_image g` - ASSUME_TAC THENL [ASM_SIMP_TAC[lemma2]; ALL_TAC] THEN - - MATCH_MP_TAC MEASURE_NEGLIGIBLE_SYMDIFF THEN - MATCH_MP_TAC NEGLIGIBLE_SUBSET THEN - EXISTS_TAC `{z:real^2 | z$2 = &0}` THEN - REWRITE_TAC[NEGLIGIBLE_STANDARD_HYPERPLANE] THEN - MATCH_MP_TAC(SET_RULE - `(!x. ~(x IN h) ==> (x IN s <=> x IN t)) - ==> s DIFF t UNION t DIFF s SUBSET h`) THEN - - X_GEN_TAC `z:real^2` THEN REWRITE_TAC[IN_ELIM_THM; IN_UNION] THEN - DISCH_TAC THEN EQ_TAC THENL - [DISCH_THEN(DISJ_CASES_THEN - (X_CHOOSE_THEN `w:real^2` STRIP_ASSUME_TAC)) THEN - MATCH_MP_TAC CONVEX_HULL_CONTAINS THEN - MAP_EVERY EXISTS_TAC [`w:real^2`; `vector[(z:real^2)$1;&0]:real^2`] THEN - (REPEAT CONJ_TAC THENL - [MATCH_MP_TAC HULL_INC THEN REWRITE_TAC[path_image] THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `y IN IMAGE f s ==> s SUBSET t ==> y IN IMAGE f t`)) THEN - REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC] THEN ASM_REAL_ARITH_TAC; - - MATCH_MP_TAC CONVEX_HULL_CONTAINS THEN - MAP_EVERY EXISTS_TAC [`vec 0:real^2`; `b:real^2`] THEN - REWRITE_TAC[CONJ_ASSOC] THEN CONJ_TAC THENL - [ASM_MESON_TAC[HULL_INC; PATHSTART_IN_PATH_IMAGE; - PATHFINISH_IN_PATH_IMAGE]; - ALL_TAC] THEN - ASM_REWRITE_TAC[IN_SEGMENT; CART_EQ; DIMINDEX_2; FORALL_2; VECTOR_2; - VECTOR_ADD_COMPONENT; VECTOR_MUL_COMPONENT; VEC_COMPONENT] THEN - EXISTS_TAC `(z:real^2)$1 / (b:real^2)$1` THEN - REWRITE_TAC[CONJ_ASSOC; REAL_MUL_RZERO; REAL_ADD_LID] THEN - ASM_SIMP_TAC[REAL_DIV_RMUL; REAL_LT_IMP_NZ] THEN - ASM_SIMP_TAC[REAL_LE_RDIV_EQ; REAL_LE_LDIV_EQ] THEN - REWRITE_TAC[REAL_MUL_LZERO; REAL_MUL_LID] THEN - UNDISCH_THEN `(w:real^2)$1 = (z:real^2)$1` (SUBST1_TAC o SYM) THEN - FIRST_X_ASSUM MATCH_MP_TAC THEN REWRITE_TAC[path_image] THEN - FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP (SET_RULE - `w IN IMAGE g s ==> s SUBSET t ==> w IN IMAGE g t`)) THEN - ASM_REWRITE_TAC[SUBSET_INTERVAL_1; DROP_VEC; REAL_LE_REFL] THEN - ASM_REAL_ARITH_TAC; - - ALL_TAC]) THEN - ONCE_REWRITE_TAC[SEGMENT_SYM] THEN - ASM_REWRITE_TAC[IN_SEGMENT; CART_EQ; DIMINDEX_2; FORALL_2; VECTOR_2; - VECTOR_ADD_COMPONENT; VECTOR_MUL_COMPONENT] - THENL - [EXISTS_TAC `(z:real^2)$2 / (w:real^2)$2` THEN - SUBGOAL_THEN `&0 < (w:real^2)$2` MP_TAC THENL - [ASM_REAL_ARITH_TAC; ALL_TAC]; - EXISTS_TAC `--((z:real^2)$2) / --((w:real^2)$2)` THEN - SUBGOAL_THEN `&0 < --((w:real^2)$2)` MP_TAC THENL - [ASM_REAL_ARITH_TAC; ALL_TAC]] THEN - SIMP_TAC[REAL_LE_LDIV_EQ; REAL_LE_RDIV_EQ] THEN - ASM_REWRITE_TAC[REAL_MUL_LZERO; REAL_MUL_LID] THEN - ASM_REWRITE_TAC[REAL_LE_NEG2; REAL_NEG_GE0] THEN - CONV_TAC REAL_FIELD; - ALL_TAC] THEN - - DISCH_TAC THEN - FIRST_ASSUM(DISJ_CASES_TAC o MATCH_MP (REAL_ARITH - `~(x:real = &0) ==> &0 < x \/ x < &0`)) - THENL [DISJ1_TAC; DISJ2_TAC] THEN - MP_TAC(ISPECL - [`convex hull (path_image g):real^2->bool`; - `vector[(z:real^2)$1;&0]:real^2`; `z:real^2`] - SEGMENT_OUT_TO_FRONTIER) THEN - ASM_SIMP_TAC[CLOSURE_INC] THEN - GEN_REWRITE_TAC (LAND_CONV o LAND_CONV o ONCE_DEPTH_CONV) [CART_EQ] THEN - ASM_REWRITE_TAC[FORALL_2; VECTOR_2; DIMINDEX_2] THEN - ASM_SIMP_TAC[BOUNDED_CONVEX_HULL; BOUNDED_PATH_IMAGE; - SIMPLE_PATH_IMP_PATH] THEN - MATCH_MP_TAC MONO_EXISTS THEN X_GEN_TAC `w:real^2` THEN - REWRITE_TAC[path_image] THEN - REPEAT(DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC)) THEN - REWRITE_TAC[IN_SEGMENT] THEN - REWRITE_TAC[CART_EQ; DIMINDEX_2; VECTOR_ADD_COMPONENT; VECTOR_MUL_COMPONENT; - VECTOR_2; FORALL_2; REAL_MUL_RZERO; REAL_ADD_LID] THEN - ASM_SIMP_TAC[REAL_FIELD - `~(z' = &0) - ==> (z = (&1 - u) * z + u * w /\ z' = u * w' <=> - ~(u = &0) /\ w = z /\ w' = z' / u)`] THEN - DISCH_THEN(X_CHOOSE_THEN `u:real` STRIP_ASSUME_TAC) THEN - (MATCH_MP_TAC(TAUT `q /\ r /\ (r ==> p) ==> p /\ q /\ r`) THEN - CONJ_TAC THENL [FIRST_X_ASSUM ACCEPT_TAC; ALL_TAC] THEN CONJ_TAC THENL - [ASM_SIMP_TAC[REAL_LE_LDIV_EQ; REAL_LE_RDIV_EQ; REAL_LT_LE] THEN - ASM_SIMP_TAC[REAL_LT_IMP_LE] THEN - REWRITE_TAC[REAL_ARITH `z <= z * u <=> --z * u <= --z * &1`; - REAL_ARITH `z * u <= z <=> z * u <= z * &1`] THEN - MATCH_MP_TAC REAL_LE_LMUL THEN ASM_REAL_ARITH_TAC; - POP_ASSUM(K ALL_TAC) THEN POP_ASSUM(K ALL_TAC) THEN STRIP_TAC]) THEN - UNDISCH_TAC `w IN IMAGE (g:real^1->real^2) (interval [vec 0,vec 1])` THEN - (SUBGOAL_THEN - `interval[vec 0:real^1,vec 1] = interval[vec 0,t] UNION interval[t,vec 1]` - SUBST1_TAC THENL - [REWRITE_TAC[EXTENSION; IN_UNION; IN_INTERVAL_1; DROP_VEC] THEN - ASM_REAL_ARITH_TAC; - REWRITE_TAC[IMAGE_UNION; IN_UNION]]) THEN - DISCH_THEN(DISJ_CASES_THEN MP_TAC) THEN REWRITE_TAC[] THEN - RULE_ASSUM_TAC(REWRITE_RULE[SUBSET]) THEN - DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN - REWRITE_TAC[IN_ELIM_THM] THEN ASM_REAL_ARITH_TAC);; + MP_TAC(SPEC `g:real^1->real^2` GREEN_AREA_ABS_ALT) THEN + ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC MONO_AND THEN CONJ_TAC THENL + [MATCH_MP_TAC INTEGRABLE_SPIKE; + DISCH_THEN(SUBST1_TAC o SYM) THEN AP_TERM_TAC THEN + MATCH_MP_TAC INTEGRAL_SPIKE] THEN + EXISTS_TAC `u:real^1->bool` THEN ASM_REWRITE_TAC[] THEN + X_GEN_TAC `x:real^1` THEN DISCH_TAC THEN AP_TERM_TAC THEN + AP_THM_TAC THEN AP_TERM_TAC THEN AP_THM_TAC THEN AP_TERM_TAC THEN + ONCE_REWRITE_TAC[GSYM LIFT_EQ] THEN + MATCH_MP_TAC VECTOR_DERIVATIVE_UNIQUE_AT THEN + MAP_EVERY EXISTS_TAC [`g:real^1->real^2`; `x:real^1`] THEN + ASM_SIMP_TAC[] THEN + REWRITE_TAC[GSYM VECTOR_DERIVATIVE_WORKS; VECTOR_DIFFERENTIABLE] THEN + ASM_MESON_TAC[]);; (* ------------------------------------------------------------------------- *) (* Part 3: Isoperimetric theorem for a convex curve. *) @@ -2422,14 +1450,14 @@ let ISOPERIMETRIC_THEOREM_CONVEX = prove (*** Use the Green formula for the area inside the curve ***) SUBGOAL_THEN - `(\t. lift(g'(t)$1 * (g:real^1->real^2)(t)$2)) absolutely_integrable_on + `(\t. lift(g'(t)$1 * (g:real^1->real^2)(t)$2)) integrable_on interval[vec 0,vec 1] /\ norm(integral (interval[vec 0,vec 1]) (\t. lift((g':real^1->real^2)(t)$1 * g(t)$2))) = measure(inside(path_image g))` STRIP_ASSUME_TAC THENL [MATCH_MP_TAC GREEN_AREA_THEOREM THEN - MAP_EVERY EXISTS_TAC [`s:real^1->bool`; `a:real^2`; `b:real^2`] THEN + MAP_EVERY EXISTS_TAC [`s:real^1->bool`; `a:real^2`] THEN ASM_SIMP_TAC[IN_DIFF; dist] THEN FIRST_X_ASSUM(SUBST1_TAC o MATCH_MP (VECTOR_ARITH `b - a:real^N = c ==> b = a + c`)) THEN ASM_REWRITE_TAC[VECTOR_ADD_COMPONENT; VECTOR_MUL_COMPONENT] THEN @@ -2444,8 +1472,7 @@ let ISOPERIMETRIC_THEOREM_CONVEX = prove sgn % lift(measure(inside(path_image(g:real^1->real^2))))) (interval[vec 0,vec 1])` STRIP_ASSUME_TAC THENL - [ASM_SIMP_TAC[HAS_INTEGRAL_INTEGRABLE_INTEGRAL; - ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE] THEN + [ASM_SIMP_TAC[HAS_INTEGRAL_INTEGRABLE_INTEGRAL] THEN FIRST_X_ASSUM(MP_TAC o GEN_REWRITE_RULE LAND_CONV [NORM_1]) THEN REWRITE_TAC[real_abs] THEN COND_CASES_TAC THEN DISCH_THEN(SUBST1_TAC o SYM) THENL diff --git a/CHANGES b/CHANGES index 56fdc920..c50a651e 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,160 @@ * page: https://github.com/jrh13/hol-light/commits/master * * ***************************************************************** +Wed 8th Apr 2026 100/green.ml [new file], 100/isoperimetric.ml, Library/words.ml, Multivariate/measure.ml, Multivariate/transcendentals.ml, Multivariate/realanalysis.ml, Multivariate/cauchy.ml + +Added a proof of Green's theorem in a fairly general form, autoformalized by +Claude Opus 4.6. The proof is based on the Cauchy transform as a left inverse +of the Wirtinger d-bar derivative operator. This approach, avoiding any +approximation or subdivision arguments while allowing quite general path +parametrization, was inspired by Kostya_I's answer here: + + https://mathoverflow.net/questions/307713 + +The technique is rooted in the Cauchy transform / dbar framework of Ahlfors, +"Lectures on Quasiconformal Mappings" (1966, 2nd ed. 2006), and appears in +Bonk's UCLA complex analysis lecture notes, Ch. 20: + + https://www.math.ucla.edu/~mbonk/complana.pdf + +The primary versions use complex infrastructure in the statements. More +traditional (but verbose) real-analytic variants of the Green theorem and +area formulas are derived as easy consequences, e.g. + + GREEN_THEOREM_CURL = + |- f g u. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + (!z. z IN inside(path_image g) ==> winding_number(g,z) = Cx(&1)) /\ + open u /\ + inside(path_image g) UNION path_image g SUBSET u /\ + f differentiable_on u /\ + (!h:real^2. (\z. frechet_derivative f (at z) h) continuous_on u) + ==> (\z. lift(frechet_derivative f (at z) (basis 1) $1 - + frechet_derivative f (at z) (basis 2) $2)) + integrable_on inside(path_image g) /\ + (\t. lift(f(g t)$2 * vector_derivative g (at t) $1 + + f(g t)$1 * vector_derivative g (at t) $2)) + integrable_on interval[vec 0,vec 1] /\ + integral (interval[vec 0,vec 1]) + (\t. lift(f(g t)$2 * vector_derivative g (at t) $1 + + f(g t)$1 * vector_derivative g (at t) $2)) = + integral (inside(path_image g)) + (\z. lift(frechet_derivative f (at z) (basis 1) $1 - + frechet_derivative f (at z) (basis 2) $2)) + +As a side-effect, added a number of general lemmas to the Multivariate +libraries, notably some results on absolutely continuous paths including +the winding number integral HAS_PATH_INTEGRAL_WINDING_NUMBER_AC, a +complement to HAS_PATH_INTEGRAL_WINDING_NUMBER with non-comparable +hypotheses: + + ABSOLUTELY_CONTINUOUS_IMP_PATH + ABSOLUTELY_CONTINUOUS_IMP_RECTIFIABLE_PATH + ABSOLUTELY_CONTINUOUS_JOINPATHS + ABSOLUTELY_CONTINUOUS_REVERSEPATH + ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS + CEXP_LIPSCHITZ_BOUNDED + HAS_PATH_INTEGRAL_WINDING_NUMBER_AC + LEBESGUE_MEASURABLE_SING + LSPACE_ALT + LSPACE_SUBSET + MEASURABLE_ON_CLOG + MEASURABLE_ON_CPOW + NEGLIGIBLE_REAL + +Wed 8th Apr 2026 Library/words.ml + +Added a word-level carryless multiplication operation word_pmul, in +effect performing multiplication in GF2[X], using the bits of a +machine word to represent polynomial coefficients in a quite standard +way (little-endian: LSB = constant term). It has generic word sizes +`word_pmul:M word->N word->P word`, so it can be used as a target for +modeling different variants supported by CPUs or just used in other +settings. There is an associated evaluation conversion WORD_PMUL_CONV +that is included in the usual WORD_RED_CONV / WORD_REDUCE_CONV suite, +as well as a bitwise conversion BIT_WORD_PMUL_CONV. New definition: + + word_pmul + +new theorems: + + BITVAL_BIT_WORD_PMUL + BIT_WORD_PMUL + BIT_WORD_PMUL_ALT + WORD_PMUL_0 + WORD_PMUL_POW2 + WORD_PMUL_STEP + WORD_PMUL_SYM + WORD_PMUL_XOR + WORD_PMUL_ZX + +and conversions: + + WORD_PMUL_CONV + BIT_WORD_PMUL_CONV + +Wed 8th Apr 2026 Examples/doomsday.ml + +Added a new example autoformalized by Claude Opus 4.6, Conway's "Doomsday +Algorithm" for finding the weekday for a given (valid Gregorian) date. +The overall correctness theorem is: + + DOOMSDAY_ALGORITHM_CORRECT = + |- !y m d. valid_date(y,m,d) + ==> doomsday_algorithm y m d = day_of_week y m d + +Tue 7th Apr 2026 mcp/* + +Incorporated a number of updates from Sanketh Menda to the MCP server, +including queue-based sentinel signaling (in place of polling) and +a more efficient apply_tactic, consolidated into a single MCP eval +roundtrip. + +Thu 2nd Apr 2026 mcp/make_checkpoint.py + +Added a small but useful refinement from Nevin Ebeid to the MCP checkpointing +script, with a new block to send Gc.compact ();; to the OCaml REPL right +after any extra loads finish and before the DMTCP checkpoint is taken. This +performs heap compaction before creating the checkpoint, which can dramatically +decrease the checkpoint size. + +Mon 31st Mar 2026 Library/components.ml, Library/records.ml [new files], unit_tests.ml + +Added a port by June Lee of code originating in s2n-bignum for creating record +types using a natural syntax, together with the supporting library of state +components ("lenses"). + +Mon 31st Mar 2026 calc_num.ml + +Added a fix from Daniel Nezamabadi to a case where polymorphic comparison <= +was mistakenly being used on the bignum type instead of the appropriate special +numeric comparison <=/. + +Mon 31st Mar 2026 mcp/* [new directory] + +Added a HOL Light MCP server contributed by Sanketh Menda, to support +LLM-assisted theorem proving more efficiently. This is a FastMCPstdio server, +embedding HOL Light as a subprocess with OCaml-side JSON serialization for +structured goal states. It also provides a custom checkpoint creation script +mcp/make_checkpoint.py for creating efficiently restartable checkpoints (on +Linux). The tools provided are: + + - apply_tactic: apply tactic, return new state or proved theorem + - backtrack: undo N tactic steps + - eval: raw OCaml/HOL Light evaluation + - goal_state: inspect current goals as JSON + - hol_help: serve SKILL.md directly from the MCP server + - hol_interrupt: send SIGINT to cancel hung tactics + - hol_load: load a HOL Light file via needs + - hol_restart: Kill existing HOL process and start a fresh one + - hol_status: report alive/dead, pid, checkpoint name, uptime etc. + - hol_type: get the type of a term + - search_theorems: search by name, return JSON results + - set_goal: set proof goal, return JSON goal state + +See mcp/README.md and other mcp/*.md for more information. + Sun 22nd Mar 2026 100/buffon.ml [new file], holtest.mk Added a proof, entirely autoformalized by Claude Opus 4.6, of the solution @@ -46,6 +200,34 @@ Numbers (weak and strong), Fair Games Theorem (Doob optional stopping), Borel-Cantelli lemmas, martingale convergence and the Azuma-Hoeffding inequality. +Thu 5th Mar 2026 Help/mapi.hlp + +Added a documentation file for the new "mapi" function introduced as +part of the METIS restructuring. + +Wed 4th Mar 2026 Library/words.ml + +Added a few miscellaneous word lemmas connecting logical left and right +shifts with (1) leading/trailing bit counts, and (2) bit reversal. + + WORD_CLZ_USHR = + |- !(x:N word) n. + word_clz (word_ushr x n) = MIN (dimindex(:N)) (word_clz x + n) + + WORD_CTZ_SHL = + |- !(x:N word) n. + word_ctz (word_shl x n) = MIN (dimindex(:N)) (word_ctz x + n) + + WORD_SHL_AS_USHR = + |- !(x:N word) n. + word_shl x n = + word_reversefields 1 (word_ushr (word_reversefields 1 x) n) + + WORD_USHR_AS_SHL = + |- !(x:N word) n. + word_ushr x n = + word_reversefields 1 (word_shl (word_reversefields 1 x) n) + Thu 26th Feb 2026 Multivariate/metric.ml, Multivariate/paths.ml Added major new results about path-connectedness in general metric diff --git a/Examples/doomsday.ml b/Examples/doomsday.ml new file mode 100644 index 00000000..8610550d --- /dev/null +++ b/Examples/doomsday.ml @@ -0,0 +1,713 @@ +(* ========================================================================= *) +(* Formalization of Conway's Doomsday Algorithm by Claude Opus 4.6 *) +(* ========================================================================= *) + +(* ========================================================================= *) +(* Part 1: Calendar primitives *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Leap year: a year is a leap year iff it is divisible by 4 but not 100, *) +(* or divisible by 400. *) +(* ------------------------------------------------------------------------- *) + +let leap_year = new_definition + `leap_year y <=> (4 divides y /\ ~(100 divides y)) \/ 400 divides y`;; + +(* Equivalent characterization using MOD *) +let LEAP_YEAR_MOD = prove + (`!y. leap_year y <=> + y MOD 4 = 0 /\ ~(y MOD 100 = 0) \/ y MOD 400 = 0`, + GEN_TAC THEN REWRITE_TAC[leap_year; DIVIDES_MOD]);; + +(* ------------------------------------------------------------------------- *) +(* Days in each month *) +(* ------------------------------------------------------------------------- *) + +let days_in_month = new_definition + `days_in_month y m = + if m = 1 then 31 + else if m = 2 then (if leap_year y then 29 else 28) + else if m = 3 then 31 + else if m = 4 then 30 + else if m = 5 then 31 + else if m = 6 then 30 + else if m = 7 then 31 + else if m = 8 then 31 + else if m = 9 then 30 + else if m = 10 then 31 + else if m = 11 then 30 + else if m = 12 then 31 + else 0`;; + +(* ------------------------------------------------------------------------- *) +(* Days in a year *) +(* ------------------------------------------------------------------------- *) + +let days_in_year = new_definition + `days_in_year y = if leap_year y then 366 else 365`;; + +(* ------------------------------------------------------------------------- *) +(* Valid date predicate *) +(* ------------------------------------------------------------------------- *) + +let valid_date = new_definition + `valid_date (y,m,d) <=> + 1 <= m /\ m <= 12 /\ 1 <= d /\ d <= days_in_month y m`;; + +(* ------------------------------------------------------------------------- *) +(* Concrete leap year evaluations *) +(* ------------------------------------------------------------------------- *) + +let LEAP_YEAR_2000 = prove + (`leap_year 2000`, + REWRITE_TAC[LEAP_YEAR_MOD] THEN CONV_TAC NUM_REDUCE_CONV);; + +let NOT_LEAP_YEAR_1900 = prove + (`~(leap_year 1900)`, + REWRITE_TAC[LEAP_YEAR_MOD] THEN CONV_TAC NUM_REDUCE_CONV);; + +let LEAP_YEAR_2024 = prove + (`leap_year 2024`, + REWRITE_TAC[LEAP_YEAR_MOD] THEN CONV_TAC NUM_REDUCE_CONV);; + +let NOT_LEAP_YEAR_2023 = prove + (`~(leap_year 2023)`, + REWRITE_TAC[LEAP_YEAR_MOD] THEN CONV_TAC NUM_REDUCE_CONV);; + +let NOT_LEAP_YEAR_2100 = prove + (`~(leap_year 2100)`, + REWRITE_TAC[LEAP_YEAR_MOD] THEN CONV_TAC NUM_REDUCE_CONV);; + +let LEAP_YEAR_2400 = prove + (`leap_year 2400`, + REWRITE_TAC[LEAP_YEAR_MOD] THEN CONV_TAC NUM_REDUCE_CONV);; + +(* Useful tactic for 12-month case splits *) +let MONTH_CASES_TAC = + GEN_TAC THEN REWRITE_TAC[IMP_CONJ_ALT; ARITH_RULE `n <= 12 <=> n < 13`] THEN + CONV_TAC EXPAND_CASES_CONV;; + +(* Days in month bounds *) +let DAYS_IN_MONTH_POS = prove + (`!y m. 1 <= m /\ m <= 12 ==> 1 <= days_in_month y m`, + MONTH_CASES_TAC THEN + REWRITE_TAC[days_in_month] THEN ARITH_TAC);; + +let DAYS_IN_MONTH_UPPER = prove + (`!y m. days_in_month y m <= 31`, + REPEAT GEN_TAC THEN REWRITE_TAC[days_in_month] THEN + REPEAT(COND_CASES_TAC THEN ASM_REWRITE_TAC[]) THEN ARITH_TAC);; + +(* ========================================================================= *) +(* Part 2: Day counting *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Cumulative days before a given month within a year *) +(* ------------------------------------------------------------------------- *) + +let cumdays_before = new_definition + `cumdays_before y m = + if m <= 1 then 0 + else if m = 2 then 31 + else if m = 3 then (if leap_year y then 60 else 59) + else if m = 4 then (if leap_year y then 91 else 90) + else if m = 5 then (if leap_year y then 121 else 120) + else if m = 6 then (if leap_year y then 152 else 151) + else if m = 7 then (if leap_year y then 182 else 181) + else if m = 8 then (if leap_year y then 213 else 212) + else if m = 9 then (if leap_year y then 244 else 243) + else if m = 10 then (if leap_year y then 274 else 273) + else if m = 11 then (if leap_year y then 305 else 304) + else if m = 12 then (if leap_year y then 335 else 334) + else (if leap_year y then 366 else 365)`;; + +(* ------------------------------------------------------------------------- *) +(* Day of year: ordinal day number within a year (January 1 = 1) *) +(* ------------------------------------------------------------------------- *) + +let day_of_year = new_definition + `day_of_year y m d = cumdays_before y m + d`;; + +(* Cumdays_before is consistent with days_in_month: each step adds one *) +let CUMDAYS_BEFORE_STEP = prove + (`!y m. 1 <= m /\ m <= 12 ==> + cumdays_before y (m + 1) = cumdays_before y m + days_in_month y m`, + MONTH_CASES_TAC THEN REWRITE_TAC[cumdays_before; days_in_month] THEN + ARITH_TAC);; + +(* Days in year equals sum of all monthly days *) +let DAYS_IN_YEAR_SUM = prove + (`!y. days_in_year y = cumdays_before y 13`, + GEN_TAC THEN REWRITE_TAC[days_in_year; cumdays_before] THEN + CONV_TAC NUM_REDUCE_CONV THEN + COND_CASES_TAC THEN ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Cumulative days: total days from year 0 to start of year y *) +(* ------------------------------------------------------------------------- *) + +let cumulative_days = define + `cumulative_days 0 = 0 /\ + cumulative_days (SUC y) = cumulative_days y + days_in_year y`;; + +(* ------------------------------------------------------------------------- *) +(* Day number: absolute day count from epoch *) +(* ------------------------------------------------------------------------- *) + +let day_number = new_definition + `day_number y m d = cumulative_days y + day_of_year y m d`;; + +(* ------------------------------------------------------------------------- *) +(* Day of week: 0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat *) +(* Epoch calibration: +5 makes Jan 1, year 0 = Saturday (6) *) +(* ------------------------------------------------------------------------- *) + +let day_of_week = new_definition + `day_of_week y m d = (day_number y m d + 5) MOD 7`;; + +(* ========================================================================= *) +(* Part 3: Doomsday algorithm definitions *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Doomsday dates: the day of the month that is a "doomsday" for each *) +(* month. These all fall on the same day of the week within any given year. *) +(* ------------------------------------------------------------------------- *) + +let doomsday_date = new_definition + `doomsday_date y m = + if m = 1 then (if leap_year y then 4 else 3) + else if m = 2 then (if leap_year y then 29 else 28) + else if m = 3 then 7 + else if m = 4 then 4 + else if m = 5 then 9 + else if m = 6 then 6 + else if m = 7 then 11 + else if m = 8 then 8 + else if m = 9 then 5 + else if m = 10 then 10 + else if m = 11 then 7 + else 12`;; + +(* ------------------------------------------------------------------------- *) +(* Century anchor: the doomsday for the first year of each century *) +(* ------------------------------------------------------------------------- *) + +let century_anchor = new_definition + `century_anchor c = (5 * (c MOD 4) + 2) MOD 7`;; + +(* ------------------------------------------------------------------------- *) +(* The Doomsday algorithm: computes the day of the week for the doomsday *) +(* of any year. *) +(* ------------------------------------------------------------------------- *) + +let doomsday = new_definition + `doomsday y = + (century_anchor (y DIV 100) + y MOD 100 + (y MOD 100) DIV 4) MOD 7`;; + +(* ========================================================================= *) +(* Part 4: Key lemmas and main correctness proof *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* All doomsday dates are valid *) +(* ------------------------------------------------------------------------- *) + +let VALID_DOOMSDAY = prove + (`!y m. 1 <= m /\ m <= 12 ==> valid_date(y, m, doomsday_date y m)`, + MONTH_CASES_TAC THEN + REWRITE_TAC[valid_date; doomsday_date; days_in_month] THEN + ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Helper: equal MOD propagates through addition *) +(* ------------------------------------------------------------------------- *) + +let MOD_ADD_EQ_COMPAT = prove + (`!a b c n. a MOD n = b MOD n ==> (c + a) MOD n = (c + b) MOD n`, + REPEAT STRIP_TAC THEN + ONCE_REWRITE_TAC[GSYM MOD_ADD_MOD] THEN + ASM_REWRITE_TAC[]);; + +(* ------------------------------------------------------------------------- *) +(* Reference doomsday: the day-of-week value computed from cumulative_days *) +(* ------------------------------------------------------------------------- *) + +let year_doomsday = new_definition + `year_doomsday y = (cumulative_days y + (if leap_year y then 2 else 1)) MOD 7`;; + +(* year_doomsday equals day_of_week of any doomsday date *) +let YEAR_DOOMSDAY_EQ_DOW = prove + (`!y m. 1 <= m /\ m <= 12 ==> + day_of_week y m (doomsday_date y m) = year_doomsday y`, + MONTH_CASES_TAC THEN + REWRITE_TAC[day_of_week; day_number; year_doomsday; day_of_year] THEN + REWRITE_TAC[cumdays_before; doomsday_date] THEN + CONV_TAC NUM_REDUCE_CONV THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN CONV_TAC NUM_REDUCE_CONV THEN + REWRITE_TAC[GSYM ADD_ASSOC] THEN CONV_TAC NUM_REDUCE_CONV THEN + ONCE_REWRITE_TAC[GSYM MOD_ADD_MOD] THEN ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Corollary: all doomsday dates in a year fall on the same weekday *) +(* ------------------------------------------------------------------------- *) + +let DOOMSDAY_SAME_WEEKDAY = prove + (`!y m1 m2. 1 <= m1 /\ m1 <= 12 /\ 1 <= m2 /\ m2 <= 12 ==> + day_of_week y m1 (doomsday_date y m1) = + day_of_week y m2 (doomsday_date y m2)`, + MESON_TAC[YEAR_DOOMSDAY_EQ_DOW]);; + +(* ========================================================================= *) +(* Part 5: Main correctness theorem -- year_doomsday y = doomsday y *) +(* ========================================================================= *) + +(* The proof strategy: decompose y = 100*c + t where t < 100. *) +(* Show doomsday and year_doomsday satisfy the same recurrence within a *) +(* century, then verify agreement at century boundaries using 400-year *) +(* periodicity. *) + +(* ------------------------------------------------------------------------- *) +(* Year-to-year step for year_doomsday (mod 7 shift) *) +(* ------------------------------------------------------------------------- *) + +let CUMULATIVE_DAYS_SUC = prove + (`!y. cumulative_days (SUC y) = cumulative_days y + days_in_year y`, + REWRITE_TAC[cumulative_days]);; + +(* ------------------------------------------------------------------------- *) +(* The main theorem: year_doomsday y = doomsday y *) +(* *) +(* We prove this by strong induction on y, verifying base cases and *) +(* showing the formula is preserved across year boundaries. *) +(* ------------------------------------------------------------------------- *) + +(* First, prove it for years 0-399 computationally. *) +(* We do this by proving the formula for each year in a 400-year block *) +(* matches the year_doomsday value. *) + +(* Key lemma: leap year structure within a century *) +(* For years 100*c + t where 0 < t < 100: leap_year iff 4 | t *) +let LEAP_YEAR_IN_CENTURY = prove + (`!c t. 0 < t /\ t < 100 ==> (leap_year (100 * c + t) <=> t MOD 4 = 0)`, + REPEAT STRIP_TAC THEN REWRITE_TAC[LEAP_YEAR_MOD] THEN + ASM_SIMP_TAC[MOD_MULT_ADD; LE_1; MOD_LT] THEN + REWRITE_TAC[ARITH_RULE `100 * c = 4 * (25 * c)`; MOD_MULT_ADD] THEN + REWRITE_TAC[MULT_ASSOC; ARITH; TAUT `(p \/ q <=> p) <=> q ==> p`] THEN + REWRITE_TAC[GSYM DIVIDES_MOD] THEN MATCH_MP_TAC(NUMBER_RULE + `f divides fh /\ f divides h ==> fh divides h * c + t ==> f divides t`) THEN + REWRITE_TAC[DIVIDES_MOD] THEN ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Recurrence for year_doomsday *) +(* ------------------------------------------------------------------------- *) + +let YEAR_DOOMSDAY_STEP = prove + (`!y. year_doomsday(SUC y) = + (year_doomsday y + (if leap_year(SUC y) then 2 else 1)) MOD 7`, + GEN_TAC THEN + REWRITE_TAC[year_doomsday; CUMULATIVE_DAYS_SUC; days_in_year] THEN + CONV_TAC MOD_DOWN_CONV THEN + COND_CASES_TAC THEN REWRITE_TAC[] THEN + ONCE_REWRITE_TAC[ADD_SYM] THEN + REPEAT(MATCH_MP_TAC MOD_ADD_EQ_COMPAT) THEN + CONV_TAC NUM_REDUCE_CONV);; + +(* ------------------------------------------------------------------------- *) +(* Within-century step for the doomsday formula *) +(* For 0 < t < 100: doomsday increments by the same leap offset *) +(* ------------------------------------------------------------------------- *) + +let DOOMSDAY_STEP = prove + (`!c t. t < 99 ==> + doomsday(100 * c + SUC t) = + (doomsday(100 * c + t) + (if SUC t MOD 4 = 0 then 2 else 1)) MOD 7`, + REPEAT STRIP_TAC THEN + REWRITE_TAC[doomsday; century_anchor] THEN + SUBGOAL_THEN `SUC t < 100 /\ t < 100` STRIP_ASSUME_TAC THENL + [ASM_ARITH_TAC; ALL_TAC] THEN + ASM_SIMP_TAC[DIV_MULT_ADD; MOD_MULT_ADD; ARITH_RULE `~(100 = 0)`; + DIV_LT; MOD_LT; ADD_CLAUSES] THEN + CONV_TAC MOD_DOWN_CONV THEN + AP_THM_TAC THEN AP_TERM_TAC THEN ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Within a century: if the base case matches, the entire century matches *) +(* ------------------------------------------------------------------------- *) + +let WITHIN_CENTURY = prove + (`!c. year_doomsday(100 * c) = doomsday(100 * c) ==> + !t. t < 100 ==> year_doomsday(100 * c + t) = doomsday(100 * c + t)`, + GEN_TAC THEN DISCH_TAC THEN INDUCT_TAC THENL + [REWRITE_TAC[ADD_CLAUSES] THEN ASM_REWRITE_TAC[]; + DISCH_TAC THEN + SUBGOAL_THEN `year_doomsday(100 * c + t) = doomsday(100 * c + t)` + ASSUME_TAC THENL + [FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN `100 * c + SUC t = SUC(100 * c + t)` SUBST1_TAC THENL + [ARITH_TAC; ALL_TAC] THEN + ASM_REWRITE_TAC[YEAR_DOOMSDAY_STEP] THEN + SUBGOAL_THEN `SUC(100 * c + t) = 100 * c + SUC t` SUBST1_TAC THENL + [ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN `t < 99` ASSUME_TAC THENL [ASM_ARITH_TAC; ALL_TAC] THEN + ASM_SIMP_TAC[DOOMSDAY_STEP] THEN + AP_THM_TAC THEN AP_TERM_TAC THEN AP_TERM_TAC THEN + MP_TAC(SPECL [`c:num`; `SUC t`] LEAP_YEAR_IN_CENTURY) THEN + ANTS_TAC THENL [ASM_ARITH_TAC; DISCH_TAC THEN ASM_REWRITE_TAC[]]]);; + +(* ------------------------------------------------------------------------- *) +(* Century base cases: computed iteratively using the recurrence *) +(* ------------------------------------------------------------------------- *) + +let YEAR_DOOMSDAY_0 = prove + (`year_doomsday 0 = 2`, + REWRITE_TAC[year_doomsday; cumulative_days; LEAP_YEAR_MOD] THEN + CONV_TAC NUM_REDUCE_CONV);; + +let DOOMSDAY_0 = prove + (`doomsday 0 = 2`, + REWRITE_TAC[doomsday; century_anchor] THEN CONV_TAC NUM_REDUCE_CONV);; + +(* Iterative computation of year_doomsday at century boundaries *) +let compute_year_doomsday n = + let rec go k th = + if k >= n then th + else + let k_tm = mk_small_numeral k in + let step_th = SPEC k_tm YEAR_DOOMSDAY_STEP in + let step_th' = CONV_RULE( + LAND_CONV(RAND_CONV NUM_SUC_CONV) THENC + RAND_CONV( + ONCE_DEPTH_CONV NUM_SUC_CONV THENC + REWRITE_CONV[th; LEAP_YEAR_MOD] THENC + NUM_REDUCE_CONV)) step_th in + go (k+1) step_th' + in go 0 YEAR_DOOMSDAY_0;; + +let YEAR_DOOMSDAY_100 = compute_year_doomsday 100;; +let YEAR_DOOMSDAY_200 = compute_year_doomsday 200;; +let YEAR_DOOMSDAY_300 = compute_year_doomsday 300;; +let YEAR_DOOMSDAY_400 = compute_year_doomsday 400;; + +(* Doomsday algorithm values at century boundaries *) +let DOOMSDAY_100 = prove(`doomsday 100 = 0`, + REWRITE_TAC[doomsday; century_anchor] THEN CONV_TAC NUM_REDUCE_CONV);; +let DOOMSDAY_200 = prove(`doomsday 200 = 5`, + REWRITE_TAC[doomsday; century_anchor] THEN CONV_TAC NUM_REDUCE_CONV);; +let DOOMSDAY_300 = prove(`doomsday 300 = 3`, + REWRITE_TAC[doomsday; century_anchor] THEN CONV_TAC NUM_REDUCE_CONV);; + +(* ------------------------------------------------------------------------- *) +(* 400-year periodicity for both doomsday and year_doomsday *) +(* ------------------------------------------------------------------------- *) + +let LEAP_YEAR_400_PERIOD = prove + (`!y. leap_year(y + 400) <=> leap_year y`, + GEN_TAC THEN REWRITE_TAC[LEAP_YEAR_MOD] THEN + REWRITE_TAC[ARITH_RULE `y + 400 = y + 4 * 100`; MOD_MULT_ADD] THEN + REWRITE_TAC[ARITH_RULE `4 * 100 = 400 * 1`; MOD_MULT_ADD]);; + +let DOOMSDAY_400_PERIOD = prove + (`!y. doomsday(y + 400) = doomsday y`, + GEN_TAC THEN REWRITE_TAC[doomsday; century_anchor] THEN + REWRITE_TAC[ARITH_RULE `y + 400 = 100 * 4 + y`; MOD_MULT_ADD] THEN + SIMP_TAC[DIV_MULT_ADD; ARITH_RULE `~(100 = 0)`] THEN + REWRITE_TAC[ARITH_RULE `4 + y = 4 * 1 + y`; MOD_MULT_ADD]);; + +let YEAR_DOOMSDAY_400_PERIOD = prove + (`!y. year_doomsday(y + 400) = year_doomsday y`, + INDUCT_TAC THENL + [REWRITE_TAC[ADD_CLAUSES; YEAR_DOOMSDAY_400; YEAR_DOOMSDAY_0]; + REWRITE_TAC[ARITH_RULE `SUC y + 400 = SUC(y + 400)`] THEN + REWRITE_TAC[YEAR_DOOMSDAY_STEP] THEN + SUBGOAL_THEN `leap_year(SUC(y + 400)) <=> leap_year(SUC y)` SUBST1_TAC THENL + [REWRITE_TAC[ARITH_RULE `SUC(y + 400) = SUC y + 400`] THEN + REWRITE_TAC[LEAP_YEAR_400_PERIOD]; + ASM_REWRITE_TAC[]]]);; + +(* ------------------------------------------------------------------------- *) +(* Century base case for all c: year_doomsday(100*c) = doomsday(100*c) *) +(* ------------------------------------------------------------------------- *) + +let CENTURY_BASE_ALL = prove + (`!c. year_doomsday(100 * c) = doomsday(100 * c)`, + GEN_TAC THEN + MP_TAC(SPECL [`c:num`; `4`] DIVISION) THEN ANTS_TAC THENL + [ARITH_TAC; ALL_TAC] THEN + ABBREV_TAC `q = c DIV 4` THEN ABBREV_TAC `r = c MOD 4` THEN + STRIP_TAC THEN + SUBGOAL_THEN `100 * c = 400 * q + 100 * r` SUBST1_TAC THENL + [ASM_REWRITE_TAC[] THEN ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN `r < 4` MP_TAC THENL [ASM_ARITH_TAC; ALL_TAC] THEN + SPEC_TAC(`r:num`,`r:num`) THEN SPEC_TAC(`q:num`, `q:num`) THEN + INDUCT_TAC THENL + [REWRITE_TAC[MULT_CLAUSES; ADD_CLAUSES] THEN CONV_TAC EXPAND_CASES_CONV THEN + CONV_TAC NUM_REDUCE_CONV THEN + REWRITE_TAC[YEAR_DOOMSDAY_0; YEAR_DOOMSDAY_100; YEAR_DOOMSDAY_200; + YEAR_DOOMSDAY_300; DOOMSDAY_0; DOOMSDAY_100; DOOMSDAY_200; + DOOMSDAY_300]; + REPEAT STRIP_TAC THEN + REWRITE_TAC[ARITH_RULE `400 * SUC q + x = (400 * q + x) + 400`] THEN + REWRITE_TAC[YEAR_DOOMSDAY_400_PERIOD; DOOMSDAY_400_PERIOD] THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]]);; + +(* ========================================================================= *) +(* THE MAIN THEOREM: year_doomsday y = doomsday y for all y *) +(* ========================================================================= *) + +let YEAR_DOOMSDAY_EQ_DOOMSDAY = prove + (`!y. year_doomsday y = doomsday y`, + GEN_TAC THEN + MP_TAC(SPECL [`y:num`; `100`] DIVISION) THEN ANTS_TAC THENL + [ARITH_TAC; ALL_TAC] THEN + ABBREV_TAC `c = y DIV 100` THEN ABBREV_TAC `t = y MOD 100` THEN + STRIP_TAC THEN + SUBGOAL_THEN `y = 100 * c + t` SUBST1_TAC THENL + [ASM_ARITH_TAC; ALL_TAC] THEN + MP_TAC(SPEC `c:num` WITHIN_CENTURY) THEN + DISCH_THEN(fun th -> MP_TAC(MP th (SPEC `c:num` CENTURY_BASE_ALL))) THEN + DISCH_THEN(MP_TAC o SPEC `t:num`) THEN ASM_REWRITE_TAC[]);; + +(* ========================================================================= *) +(* DOOMSDAY_CORRECT: The Doomsday algorithm gives the correct day of week *) +(* for the doomsday date in every month of every year. *) +(* ========================================================================= *) + +let DOOMSDAY_CORRECT = prove + (`!y m. 1 <= m /\ m <= 12 ==> + day_of_week y m (doomsday_date y m) = doomsday y`, + REPEAT STRIP_TAC THEN + MP_TAC(SPECL [`y:num`; `m:num`] YEAR_DOOMSDAY_EQ_DOW) THEN + ASM_REWRITE_TAC[YEAR_DOOMSDAY_EQ_DOOMSDAY]);; + +(* ========================================================================= *) +(* Part 6: Corollaries and applications *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Day-of-week shift: adding days shifts the weekday *) +(* ------------------------------------------------------------------------- *) + +let DOW_SHIFT = prove + (`!y m d1 d2. day_of_week y m (d1 + d2) = (day_of_week y m d1 + d2) MOD 7`, + REPEAT GEN_TAC THEN + REWRITE_TAC[day_of_week; day_number; day_of_year] THEN + CONV_TAC MOD_DOWN_CONV THEN + AP_THM_TAC THEN AP_TERM_TAC THEN ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Day-of-week from doomsday: for any date on or after the doomsday date *) +(* ------------------------------------------------------------------------- *) + +let DOW_FROM_DOOMSDAY = prove + (`!y m d. 1 <= m /\ m <= 12 /\ doomsday_date y m <= d ==> + day_of_week y m d = (doomsday y + d - doomsday_date y m) MOD 7`, + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `d = doomsday_date y m + (d - doomsday_date y m)` + SUBST1_TAC THENL [ASM_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[DOW_SHIFT] THEN + ASM_SIMP_TAC[DOOMSDAY_CORRECT] THEN + CONV_TAC MOD_DOWN_CONV THEN + AP_THM_TAC THEN AP_TERM_TAC THEN ASM_ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* Cumulative days 400-year periodicity (mod 7) *) +(* ------------------------------------------------------------------------- *) + +let compute_cumdays n = + let rec go k th = + if k >= n then th + else + let k_tm = mk_small_numeral k in + let step_th = SPEC k_tm CUMULATIVE_DAYS_SUC in + let step_th' = CONV_RULE( + LAND_CONV(RAND_CONV NUM_SUC_CONV) THENC + RAND_CONV( + ONCE_DEPTH_CONV NUM_SUC_CONV THENC + REWRITE_CONV[th; days_in_year; LEAP_YEAR_MOD] THENC + NUM_REDUCE_CONV)) step_th in + go (k+1) step_th' + in go 0 (CONJUNCT1 cumulative_days);; + +let CUMDAYS_400 = compute_cumdays 400;; + +let CUMULATIVE_DAYS_400_MOD7 = prove + (`!y. cumulative_days(y + 400) MOD 7 = cumulative_days y MOD 7`, + INDUCT_TAC THENL + [REWRITE_TAC[ADD_CLAUSES; CUMDAYS_400; cumulative_days] THEN + CONV_TAC NUM_REDUCE_CONV; + REWRITE_TAC[ARITH_RULE `SUC y + 400 = SUC(y + 400)`] THEN + REWRITE_TAC[CUMULATIVE_DAYS_SUC; days_in_year] THEN + SUBGOAL_THEN `leap_year(y + 400) <=> leap_year y` + (fun th -> REWRITE_TAC[th]) THENL + [REWRITE_TAC[LEAP_YEAR_400_PERIOD]; ALL_TAC] THEN + COND_CASES_TAC THEN REWRITE_TAC[] THEN + CONV_TAC MOD_DOWN_CONV THEN + ONCE_REWRITE_TAC[ADD_SYM] THEN + MATCH_MP_TAC MOD_ADD_EQ_COMPAT THEN + ASM_REWRITE_TAC[]]);; + +(* ------------------------------------------------------------------------- *) +(* 400-year periodicity for the full day_of_week function *) +(* ------------------------------------------------------------------------- *) + +let DAY_OF_WEEK_400_PERIOD = prove + (`!y m d. day_of_week (y + 400) m d = day_of_week y m d`, + REPEAT GEN_TAC THEN + REWRITE_TAC[day_of_week; day_number; day_of_year] THEN + SUBGOAL_THEN `cumdays_before (y + 400) m = cumdays_before y m` + SUBST1_TAC THENL + [REWRITE_TAC[cumdays_before; LEAP_YEAR_400_PERIOD]; ALL_TAC] THEN + CONV_TAC MOD_DOWN_CONV THEN + ONCE_REWRITE_TAC[ADD_SYM] THEN + MATCH_MP_TAC MOD_ADD_EQ_COMPAT THEN + ONCE_REWRITE_TAC[ADD_SYM] THEN + MATCH_MP_TAC MOD_ADD_EQ_COMPAT THEN + REWRITE_TAC[CUMULATIVE_DAYS_400_MOD7]);; + +(* ========================================================================= *) +(* Part 7: Full doomsday algorithm for any date *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Day-of-week is periodic with period 7 in the day argument *) +(* ------------------------------------------------------------------------- *) + +let DOW_PERIOD = prove + (`!y m d k. day_of_week y m (d + 7 * k) = day_of_week y m d`, + REPEAT GEN_TAC THEN + REWRITE_TAC[day_of_week; day_number; day_of_year] THEN + CONV_TAC MOD_DOWN_CONV THEN + ONCE_REWRITE_TAC[ADD_SYM] THEN + MATCH_MP_TAC MOD_ADD_EQ_COMPAT THEN + ONCE_REWRITE_TAC[ARITH_RULE + `c + b + d + 7 * k = 7 * k + (c + b + d)`] THEN + REWRITE_TAC[MOD_MULT_ADD]);; + +(* ------------------------------------------------------------------------- *) +(* Doomsday date bounds: all doomsday dates are between 1 and 29 *) +(* ------------------------------------------------------------------------- *) + +let DOOMSDAY_DATE_BOUND = prove + (`!y m. 1 <= m /\ m <= 12 ==> + 1 <= doomsday_date y m /\ doomsday_date y m <= 29`, + MONTH_CASES_TAC THEN REWRITE_TAC[doomsday_date] THEN ARITH_TAC);; + +(* ------------------------------------------------------------------------- *) +(* The full doomsday algorithm: computes day of week for any valid date. *) +(* *) +(* Conway's mental process: *) +(* 1. Compute the year's doomsday from century anchor + year offset *) +(* 2. Look up the doomsday date for the month (4/4, 6/6, 8/8, etc.) *) +(* 3. Count the offset from the doomsday date to the target day *) +(* 4. Add that offset (mod 7) to the year's doomsday *) +(* *) +(* The +35 (= 5*7) is a safety offset to avoid natural number underflow *) +(* when the target day is before the doomsday date in the month. *) +(* ------------------------------------------------------------------------- *) + +let doomsday_algorithm = new_definition + `doomsday_algorithm y m d = + (doomsday y + d + 35 - doomsday_date y m) MOD 7`;; + +(* ------------------------------------------------------------------------- *) +(* THE FULL CORRECTNESS THEOREM: the doomsday algorithm computes the *) +(* correct day of the week for every valid Gregorian date. *) +(* ------------------------------------------------------------------------- *) + +let DOOMSDAY_ALGORITHM_CORRECT = prove + (`!y m d. valid_date(y,m,d) ==> + doomsday_algorithm y m d = day_of_week y m d`, + REPEAT STRIP_TAC THEN + REWRITE_TAC[doomsday_algorithm] THEN + SUBGOAL_THEN `day_of_week y m d = day_of_week y m (d + 35)` SUBST1_TAC THENL + [ONCE_REWRITE_TAC[ARITH_RULE `d + 35 = d + 7 * 5`] THEN + REWRITE_TAC[DOW_PERIOD]; ALL_TAC] THEN + MP_TAC(SPECL [`y:num`; `m:num`; `d + 35`] DOW_FROM_DOOMSDAY) THEN + ANTS_TAC THENL + [RULE_ASSUM_TAC(REWRITE_RULE[valid_date]) THEN + MP_TAC(SPECL [`y:num`; `m:num`] DOOMSDAY_DATE_BOUND) THEN + ASM_ARITH_TAC; + DISCH_THEN SUBST1_TAC THEN + AP_THM_TAC THEN AP_TERM_TAC THEN + MP_TAC(SPECL [`y:num`; `m:num`] DOOMSDAY_DATE_BOUND) THEN + RULE_ASSUM_TAC(REWRITE_RULE[valid_date]) THEN + ASM_ARITH_TAC]);; + +(* ========================================================================= *) +(* Part 8: Enumerated day type with named output and evaluation *) +(* ========================================================================= *) + +let day_INDUCT,day_RECURSION = define_type + "day = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday";; + +(* Conway's names: + * Noneday Oneday Twosday Treblesday Foursday Fiveday Sixturday + *) + +let dayname = new_definition + `dayname n = if n = 0 then Sunday + else if n = 1 then Monday + else if n = 2 then Tuesday + else if n = 3 then Wednesday + else if n = 4 then Thursday + else if n = 5 then Friday + else Saturday`;; + +let doomsday_day = new_definition + `doomsday_day y m d = dayname(doomsday_algorithm y m d)`;; + +let DOOMSDAY_DAY_CORRECT = prove + (`!y m d. valid_date(y,m,d) + ==> doomsday_day y m d = dayname(day_of_week y m d)`, + REWRITE_TAC[doomsday_day] THEN + MESON_TAC[DOOMSDAY_ALGORITHM_CORRECT]);; + +(* ------------------------------------------------------------------------- *) +(* Evaluation conversion for doomsday_day. *) +(* Usage: DOOMSDAY_DAY_CONV `doomsday_day 1966 12 14` *) +(* |- doomsday_day 1966 12 14 = Wednesday *) +(* ------------------------------------------------------------------------- *) + +let DOOMSDAY_DAY_CONV = + let cs = Compute.bool_compset () in + Compute.set_skip cs `COND:bool->num->num->num` (Some 1); + Compute.set_skip cs `COND:bool->day->day->day` (Some 1); + num_compute_add_convs cs; + Compute.add_thms [doomsday_day; doomsday_algorithm; doomsday; century_anchor; + doomsday_date; LEAP_YEAR_MOD; dayname] cs; + Compute.WEAK_CBV_CONV cs;; + +(* ------------------------------------------------------------------------- *) +(* Famous dates with named days *) +(* ------------------------------------------------------------------------- *) + +let DAY_1776_7_4 = prove + (`doomsday_day 1776 7 4 = Thursday`, + CONV_TAC DOOMSDAY_DAY_CONV);; + +let DAY_1969_7_20 = prove + (`doomsday_day 1969 7 20 = Sunday`, + CONV_TAC DOOMSDAY_DAY_CONV);; + +let DAY_1989_11_9 = prove + (`doomsday_day 1989 11 9 = Thursday`, + CONV_TAC DOOMSDAY_DAY_CONV);; + +let DAY_2000_1_1 = prove + (`doomsday_day 2000 1 1 = Saturday`, + CONV_TAC DOOMSDAY_DAY_CONV);; + +let DAY_2026_3_26 = prove + (`doomsday_day 2026 3 26 = Thursday`, + CONV_TAC DOOMSDAY_DAY_CONV);; + +let DAY_1966_12_14 = prove + (`doomsday_day 1966 12 14 = Wednesday`, + CONV_TAC DOOMSDAY_DAY_CONV);; + +let DAY_2005_03_25 = prove + (`doomsday_day 2005 03 25 = Friday`, + CONV_TAC DOOMSDAY_DAY_CONV);; diff --git a/Help/mapi.hlp b/Help/mapi.hlp new file mode 100644 index 00000000..906da91b --- /dev/null +++ b/Help/mapi.hlp @@ -0,0 +1,28 @@ +\DOC mapi + +\TYPE {mapi : (int -> 'a -> 'b) -> 'a list -> 'b list} + +\SYNOPSIS +Applies a function to every element of a list, supplying the element's index. + +\DESCRIBE +{mapi f [x0;...;xn]} returns {[f 0 x0; f 1 x1;...;f n xn]}. The function {f} +receives the zero-based index of each element as its first argument. + +\FAILURE +Never fails. + +\EXAMPLE +{ + # mapi (fun i x -> (i, x)) ["a"; "b"; "c"];; + val it : (int * string) list = [(0, "a"); (1, "b"); (2, "c")] + # mapi (fun i x -> x + i) [10; 20; 30];; + val it : int list = [10; 21; 32] + # mapi (fun i _ -> i) [5; 5; 5; 5];; + val it : int list = [0; 1; 2; 3] +} + +\SEEALSO +map, map2. + +\ENDDOC diff --git a/Library/components.ml b/Library/components.ml new file mode 100644 index 00000000..0b8529a6 --- /dev/null +++ b/Library/components.ml @@ -0,0 +1,276 @@ +(* ========================================================================= *) +(* General theory of state components (lenses). *) +(* *) +(* This gives a hierarchical way of describing state using ":>" to compose, *) +(* analogous to record components. The components are essentially just pairs *) +(* of reader and writer function, wrapped in a special type only for brevity *) +(* when stating component types explicitly. This idea is often called a *) +(* "lens", (e.g. https://medium.com/javascript-scene/lenses-b85976cb0534). *) +(* *) +(* Ported from s2n-bignum (common/components.ml). *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Storing useful per-case theorems not true of a general component. *) +(* ------------------------------------------------------------------------- *) + +let component_read_write_thms = ref ([]:thm list);; + +let add_component_read_write_thms l = + component_read_write_thms := union l (!component_read_write_thms);; + +let component_alias_thms = ref ([]:thm list);; + +let valid_component_thms = ref ([]:thm list);; + +let add_valid_component_thms l = + valid_component_thms := union l (!valid_component_thms);; + +let strongly_valid_component_thms = ref ([]:thm list);; + +let add_strongly_valid_component_thms l = + strongly_valid_component_thms := + union l (!strongly_valid_component_thms);; + +let weakly_valid_component_thms = ref ([]:thm list);; + +let add_weakly_valid_component_thms l = + weakly_valid_component_thms := + union l (!weakly_valid_component_thms);; + +let extensionally_valid_component_thms = ref ([]:thm list);; + +let add_extensionally_valid_component_thms l = + extensionally_valid_component_thms := + union l (!extensionally_valid_component_thms);; + +let component_orthogonality_thms = ref ([]:thm list);; + +let add_component_orthogonality_thms l = + component_orthogonality_thms := union l (!component_orthogonality_thms);; + +(* ------------------------------------------------------------------------- *) +(* Set up a type ":(S,V)component" for a component of type ":V" in a *) +(* larger state space ":S", which is really just a reader function S->V *) +(* and a writer function V->S->S updating the corresponding field. *) +(* ------------------------------------------------------------------------- *) + +let component_tybij = + let th = prove(`?rw:(S->V)#(V->S->S). T`,REWRITE_TAC[]) in + REWRITE_RULE[] + (new_type_definition "component" ("component","dest_component") th);; + +let COMPONENT_INJ = prove + (`!rw rw'. component rw = component rw' <=> rw = rw'`, + MESON_TAC[component_tybij]);; + +let FORALL_COMPONENT = prove + (`(!c:(S,V)component. P c) <=> !r w. P(component(r,w))`, + MESON_TAC[component_tybij; PAIR]);; + +let EXISTS_COMPONENT = prove + (`(?c:(S,V)component. P c) <=> ?r w. P(component(r,w))`, + MESON_TAC[component_tybij; PAIR]);; + +let read_def = new_definition + `read (c:(S,V)component) = FST(dest_component c)`;; + +let write_def = new_definition + `write (c:(S,V)component) = SND(dest_component c)`;; + +let read = prove + (`!(r:S->V) w. read(component(r,w)) = r`, + REWRITE_TAC[read_def; component_tybij]);; + +let write = prove + (`!(r:S->V) w. write(component(r,w)) = w`, + REWRITE_TAC[write_def; component_tybij]);; + +let COMPONENT_EQ = prove + (`!c1 c2. c1 = c2 <=> read c1 = read c2 /\ write c1 = write c2`, + REWRITE_TAC[COMPONENT_INJ; FORALL_COMPONENT; read; write; PAIR_EQ]);; + +(* ------------------------------------------------------------------------- *) +(* A sort of identity for state components, corresponding to the full state. *) +(* ------------------------------------------------------------------------- *) + +let entirety = new_definition + `entirety = component((\s:S. s),(\x:S s:S. x))`;; + +let READ_ENTIRETY = prove + (`read entirety = I /\ (!s. read entirety s = s)`, + REWRITE_TAC[read; entirety; I_THM; FUN_EQ_THM]);; + +let WRITE_ENTIRETY = prove + (`(!y. write entirety y = \s. y) /\ (!s y. write entirety y s = y)`, + REWRITE_TAC[write; entirety; I_THM; FUN_EQ_THM]);; + +let READ_WRITE_ENTIRETY = prove + (`!y s. read entirety (write entirety y s) = y`, + REWRITE_TAC[WRITE_ENTIRETY; READ_ENTIRETY; I_DEF]);; + +add_component_read_write_thms [READ_WRITE_ENTIRETY];; + +(* ------------------------------------------------------------------------- *) +(* Composition operation on state components. *) +(* ------------------------------------------------------------------------- *) + +parse_as_infix(":>",(28,"right"));; + +let component_compose = new_definition + `(c1:(S,T)component) :> (c2:(T,U)component) = + component((read c2 o read c1), + (\v s. write c1 (write c2 v (read c1 s)) s))`;; + +let COMPONENT_COMPOSE_ASSOC = prove + (`!sc1 sc2 sc3. sc1 :> (sc2 :> sc3) = (sc1 :> sc2) :> sc3`, + REWRITE_TAC[FORALL_COMPONENT; component_compose; read; write; o_DEF]);; + +let READ_COMPONENT_COMPOSE = prove + (`!sc1 sc2 s. read (sc1 :> sc2) s = read sc2 (read sc1 s)`, + REWRITE_TAC[FORALL_COMPONENT; read; component_compose; read; write; o_DEF]);; + +let WRITE_COMPONENT_COMPOSE = prove + (`!sc1 sc2 s x. write (sc1 :> sc2) x s = + write sc1 (write sc2 x (read sc1 s)) s`, + REWRITE_TAC[FORALL_COMPONENT; read; write; component_compose]);; + +let COMPOSE_ENTIRETY = prove + (`(!c. c :> entirety = c) /\ (!c. entirety :> c = c)`, + REWRITE_TAC[FORALL_COMPONENT; component_compose; entirety; FUN_EQ_THM; + read; write; COMPONENT_EQ; o_DEF]);; + +let READ_WRITE_COMPONENT_COMPOSE = prove + (`!sc1 sc2. + (!y s. read sc1 (write sc1 y s) = y) /\ + (!y s. read sc2 (write sc2 y s) = y) + ==> !y s. read (sc1 :> sc2) (write (sc1 :> sc2) y s) = y`, + SIMP_TAC[READ_COMPONENT_COMPOSE; WRITE_COMPONENT_COMPOSE]);; + +(* ------------------------------------------------------------------------- *) +(* Individual read-versus-write properties *) +(* ------------------------------------------------------------------------- *) + +let read_over_write = new_definition + `read_over_write (c:(S,A)component) <=> + !y s. read c (write c y s) = y`;; + +let write_over_write = new_definition + `write_over_write (c:(S,A)component) <=> + !y z s. write c z (write c y s) = write c z s`;; + +let write_over_read = new_definition + `write_over_read (c:(S,A)component) <=> + !s. write c (read c s) s = s`;; + +let weak_read_over_write = new_definition + `weak_read_over_write (c:(S,A)component) <=> + ?f. !y s. read c (write c y s) = f y`;; + +(* ------------------------------------------------------------------------- *) +(* Valid state components. *) +(* ------------------------------------------------------------------------- *) + +let valid_component = define + `valid_component c <=> + (!y s. read c (write c y s) = y) /\ + (!y z s. write c z (write c y s) = write c z s)`;; + +let VALID_COMPONENT_COMPOSE = prove + (`!c1 c2. valid_component c1 /\ valid_component c2 + ==> valid_component(c1 :> c2)`, + REPEAT GEN_TAC THEN REWRITE_TAC[valid_component] THEN STRIP_TAC THEN + ASM_REWRITE_TAC[WRITE_COMPONENT_COMPOSE; READ_COMPONENT_COMPOSE]);; + +let VALID_COMPONENT_ENTIRETY = prove + (`valid_component entirety`, + REWRITE_TAC[valid_component; WRITE_ENTIRETY; READ_ENTIRETY; I_DEF]);; + +add_valid_component_thms [VALID_COMPONENT_ENTIRETY];; + +(* ------------------------------------------------------------------------- *) +(* A slightly weaker version where writes may be modified (e.g. truncated). *) +(* ------------------------------------------------------------------------- *) + +let weakly_valid_component = define + `weakly_valid_component c <=> + (?f. !y s. read c (write c y s) = f y) /\ + (!y z s. write c z (write c y s) = write c z s)`;; + +let VALID_IMP_WEAKLY_VALID_COMPONENT = prove + (`!c:(S,V)component. valid_component c ==> weakly_valid_component c`, + REWRITE_TAC[valid_component; weakly_valid_component] THEN + REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN + EXISTS_TAC `\x:V. x` THEN ASM_REWRITE_TAC[]);; + +let WEAKLY_VALID_COMPONENT_ENTIRETY = prove + (`weakly_valid_component entirety`, + SIMP_TAC[VALID_IMP_WEAKLY_VALID_COMPONENT; VALID_COMPONENT_ENTIRETY]);; + +add_weakly_valid_component_thms [WEAKLY_VALID_COMPONENT_ENTIRETY];; + +(* ------------------------------------------------------------------------- *) +(* A stronger notion more in line with our expectations. *) +(* ------------------------------------------------------------------------- *) + +let strongly_valid_component = define + `strongly_valid_component c <=> + (!y s. read c (write c y s) = y) /\ + (!s. write c (read c s) s = s) /\ + (!y z s. write c z (write c y s) = write c z s)`;; + +let STRONGLY_VALID_IMP_VALID_COMPONENT = prove + (`!c:(S,V)component. strongly_valid_component c ==> valid_component c`, + SIMP_TAC[valid_component; strongly_valid_component]);; + +let STRONGLY_VALID_COMPONENT_COMPOSE = prove + (`!c1 c2. strongly_valid_component c1 /\ strongly_valid_component c2 + ==> strongly_valid_component(c1 :> c2)`, + REPEAT GEN_TAC THEN REWRITE_TAC[strongly_valid_component] THEN STRIP_TAC THEN + ASM_REWRITE_TAC[WRITE_COMPONENT_COMPOSE; READ_COMPONENT_COMPOSE]);; + +let STRONGLY_VALID_COMPONENT_ENTIRETY = prove + (`strongly_valid_component entirety`, + REWRITE_TAC[strongly_valid_component; READ_ENTIRETY; WRITE_ENTIRETY]);; + +add_strongly_valid_component_thms [STRONGLY_VALID_COMPONENT_ENTIRETY];; + +(* ------------------------------------------------------------------------- *) +(* And likewise a version of that allowing write truncation. *) +(* ------------------------------------------------------------------------- *) + +let extensionally_valid_component = define + `extensionally_valid_component (c:(S,A)component) <=> + (?f. !y s. read c (write c y s) = f y) /\ + (!s. write c (read c s) s = s) /\ + (!y z s. write c z (write c y s) = write c z s)`;; + +let STRONGLY_VALID_IMP_EXTENSIONALLY_VALID_COMPONENT = prove + (`!c:(S,V)component. + strongly_valid_component c ==> extensionally_valid_component c`, + REWRITE_TAC[extensionally_valid_component; strongly_valid_component] THEN + REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN + EXISTS_TAC `\x:V. x` THEN ASM_REWRITE_TAC[]);; + +let EXTENSIONALLY_VALID_COMPONENT_ENTIRETY = prove + (`extensionally_valid_component entirety`, + SIMP_TAC[STRONGLY_VALID_COMPONENT_ENTIRETY; + STRONGLY_VALID_IMP_EXTENSIONALLY_VALID_COMPONENT]);; + +add_extensionally_valid_component_thms + [EXTENSIONALLY_VALID_COMPONENT_ENTIRETY];; + +(* ------------------------------------------------------------------------- *) +(* General independence/orthogonality of state components. *) +(* ------------------------------------------------------------------------- *) + +let orthogonal_components = define + `orthogonal_components sc1 sc2 <=> + (!y z s. write sc1 y (write sc2 z s) = + write sc2 z (write sc1 y s)) /\ + (!y s. read sc2 (write sc1 y s) = read sc2 s) /\ + (!z s. read sc1 (write sc2 z s) = read sc1 s)`;; + +let ORTHOGONAL_COMPONENTS_SYM = prove + (`!sc1 sc2. orthogonal_components sc1 sc2 <=> orthogonal_components sc2 sc1`, + REWRITE_TAC[orthogonal_components] THEN MESON_TAC[]);; diff --git a/Library/records.ml b/Library/records.ml new file mode 100644 index 00000000..a2539488 --- /dev/null +++ b/Library/records.ml @@ -0,0 +1,142 @@ +(* ========================================================================= *) +(* Definition of record types, defining standard components for the fields. *) +(* *) +(* Ported from s2n-bignum (common/records.ml). *) +(* ========================================================================= *) + +needs "Library/components.ml";; + +let parse_record_specification = + let ptype src = + let pty,rst = parse_pretype src in + type_of_pretype pty,rst in + let recordelement src = + match src with + (Ident t)::(Resword ":")::rst -> + let ty,ors = ptype rst in + (t,ty),ors + | _ -> raise Noparse in + let recordelements = + listof recordelement (a(Resword ";")) "';' or '}'" in + let recspec = + a (Resword "{") ++ recordelements ++ a (Resword "}") + >> (fun ((_,s),_) -> s) in + fun s -> match lex(explode s) with + ((Ident tyname)::(Ident "=")::rst) -> + let rs,er = recspec rst in + if er <> [] then failwith "Stuff after record spec" + else tyname,rs + | _ -> raise Noparse;; + +let define_record_type = + let tweak = (MATCH_MP o prove) + (`(?r. P r) /\ (?w. Q w) ==> ?c. P(read c) /\ Q(write c)`, + REWRITE_TAC[EXISTS_COMPONENT; read; write] THEN MESON_TAC[]) in + fun s -> + let tyname,fields = parse_record_specification s in + let tyname_rec = tyname^"_RECORD" in + let ith,rth = + define_type_raw [mk_vartype tyname,[tyname_rec,map snd fields]] in + let f,ebod = dest_forall(concl rth) in + let fn,abod = dest_exists ebod in + let avs,eqn = strip_forall abod in + let left = rand(lhand eqn) in + let leftys = mk_fun_ty (type_of left) (type_of left) in + let fnms = map fst fields in + let defs = map2 + (fun fnm v -> let vn,vty = dest_var v in + let v' = mk_var(vn^"'",vty) in + let right = subst [v',v] left in + let wfn = mk_var("writer",mk_fun_ty (type_of v) leftys) in + let wdef = mk_eq(mk_comb(mk_comb(wfn,v'),left),right) in + let wcdef = list_mk_forall(v'::avs,wdef) in + let wethm = prove_recursive_functions_exist rth wcdef in + let rfn = mk_var("reader", + mk_fun_ty (type_of left) (type_of v)) in + let rdef = mk_eq(mk_comb(rfn,left),v) in + let rcdef = list_mk_forall(avs,rdef) in + let rethm = prove_recursive_functions_exist rth rcdef in + new_specification [fnm] (tweak (CONJ rethm wethm))) + fnms avs in + ith,rth,end_itlist CONJ defs;; + +(* ------------------------------------------------------------------------- *) +(* Get the components out of a record definition *) +(* ------------------------------------------------------------------------- *) + +let rec get_record_components cth = + let cjs = conjuncts(concl cth) in + let n = length cjs / 2 in + let rcjs = map (fun i -> el (2 * i) cjs) (0--(n-1)) in + map (lhand o lhand o snd o strip_forall) rcjs;; + +(* ------------------------------------------------------------------------- *) +(* Construct "read and write same component" theorems for all fields. *) +(* ------------------------------------------------------------------------- *) + +let record_read_write_thms (ith,cth) = + let pat = `!(y:B) (s:A). read c (write c y s) = y` + and ctm = `c:(A,B)component` in + let rule tm = + prove(instantiate(term_match [] ctm tm) pat, + REPEAT(MATCH_MP_TAC ith ORELSE GEN_TAC) THEN + REWRITE_TAC[cth] THEN NO_TAC) in + map rule (get_record_components cth);; + +(* ------------------------------------------------------------------------- *) +(* Construct "valid component" for all fields. *) +(* ------------------------------------------------------------------------- *) + +let record_strongly_valid_component_thms (ith,cth) = + let rule tm = + prove(list_mk_icomb "strongly_valid_component" [tm], + GEN_REWRITE_TAC I [strongly_valid_component] THEN + REPEAT CONJ_TAC THEN + REPEAT(MATCH_MP_TAC ith ORELSE GEN_TAC) THEN + REWRITE_TAC[cth] THEN NO_TAC) in + map rule (get_record_components cth);; + +let record_valid_component_thms (ith,cth) = + map (MATCH_MP STRONGLY_VALID_IMP_VALID_COMPONENT) + (record_strongly_valid_component_thms (ith,cth));; + +let record_extensionally_valid_component_thms (ith,cth) = + map (MATCH_MP STRONGLY_VALID_IMP_EXTENSIONALLY_VALID_COMPONENT) + (record_strongly_valid_component_thms (ith,cth));; + +let record_weakly_valid_component_thms (ith,cth) = + map (MATCH_MP VALID_IMP_WEAKLY_VALID_COMPONENT) + (record_valid_component_thms (ith,cth));; + +(* ------------------------------------------------------------------------- *) +(* Create orthogonality theorems for all distinct pairs. *) +(* ------------------------------------------------------------------------- *) + +let record_orthogonality_thms (ith,cth) = + let cmps = get_record_components cth in + let dps = filter (fun (a,b) -> a <> b) + (allpairs (fun a b -> a,b) cmps cmps) in + let cjs = map + (fun (a,b) -> list_mk_icomb "orthogonal_components" [a;b]) dps in + let tac = + REWRITE_TAC[orthogonal_components] THEN + REPEAT(CONJ_TAC ORELSE MATCH_MP_TAC ith ORELSE GEN_TAC) THEN + REWRITE_TAC[cth] in + map (fun tm -> prove(tm,tac)) cjs;; + +(* ------------------------------------------------------------------------- *) +(* Combined function that does the definition and adds all the theorems. *) +(* ------------------------------------------------------------------------- *) + +let define_auto_record_type def = + let ith,rth,cth = define_record_type def in + (add_component_read_write_thms (record_read_write_thms (ith,cth)); + add_strongly_valid_component_thms + (record_strongly_valid_component_thms (ith,cth)); + add_valid_component_thms (record_valid_component_thms (ith,cth)); + add_extensionally_valid_component_thms + (record_extensionally_valid_component_thms (ith,cth)); + add_weakly_valid_component_thms + (record_weakly_valid_component_thms (ith,cth)); + add_component_orthogonality_thms (record_orthogonality_thms (ith,cth)); + ith,rth,cth);; diff --git a/Library/tactician_light.ml b/Library/tactician_light.ml new file mode 100644 index 00000000..9914bcf4 --- /dev/null +++ b/Library/tactician_light.ml @@ -0,0 +1,995 @@ +(* ========================================================================= *) +(* Tactician Light *) +(* *) +(* A proof format translator for HOL Light, converting between *) +(* interactive (g/e) and structured (prove) proof styles. *) +(* *) +(* Inspired by Mark Adams' Tactician tool for HOL Light: *) +(* http://www.proof-technologies.com/tactician/ *) +(* which provided similar functionality via a "hiproof" *) +(* representation and refactoring pipeline. This is a from-scratch *) +(* reimplementation using string-based tactic recording rather than *) +(* the original promotion/demotion mechanism. *) +(* *) +(* ========================================================================= *) +(* *) +(* USAGE *) +(* *) +(* Load into a HOL Light session: *) +(* loadt "tactician_light.ml";; *) +(* *) +(* --- Interactive proof development (interactive -> structured) --- *) +(* *) +(* Record a proof interactively, then extract the structured form: *) +(* *) +(* xg `!x:num. x = x`;; (* set goal, start recording *) *) +(* xe "GEN_TAC";; (* execute + record tactic *) *) +(* xe "REFL_TAC";; (* execute + record tactic *) *) +(* xt();; (* print structured proof *) *) +(* xs();; (* return as string *) *) +(* *) +(* Other recording commands: *) +(* re "name" tac;; execute tactic value + record with given name *) +(* xb();; undo last recorded step *) +(* xr n;; record rotation (same as r n but recorded) *) +(* xp();; print current recording *) +(* xreset();; reset recording *) +(* xshow();; show goal, recording, and structured proof *) +(* *) +(* --- Structured proof flattening (structured -> interactive) --- *) +(* *) +(* Parse and execute a structured proof step by step: *) +(* *) +(* flatten_proof `!x:num. x = x` "GEN_TAC THEN REFL_TAC";; *) +(* *) +(* --- File-level batch conversion --- *) +(* *) +(* Convert entire files between formats: *) +(* *) +(* s2i "structured.ml" "interactive.ml";; *) +(* Reads prove(...) blocks, flattens each to g/e commands. *) +(* Input format: let NAME = prove(`goal`, tactic);; *) +(* Output format: g `goal`;; e(tac1);; ... let NAME = top_thm();; *) +(* *) +(* i2s "interactive.ml" "structured.ml";; *) +(* Reads g/e commands, replays through HOL Light, outputs prove. *) +(* Input format: g `goal`;; e(tac1);; e(tac2);; *) +(* Output format: let NAME = prove(`goal`, tac1 THEN tac2);; *) +(* *) +(* The i2s direction extracts tactic strings from e(...) expressions *) +(* automatically -- no manual stringification needed. It also handles *) +(* b();; for undo, r n;; for rotation, p();; (skipped), and *) +(* let NAME = top_thm();; for naming. *) +(* *) +(* THEN optimization: when all THENL branches are identical, the *) +(* structured output uses THEN instead. Common head tactics are also *) +(* factored out across branches. *) +(* *) +(* Output is formatted to stay within 78 columns where possible, *) +(* packing short tactics on one line in HOL Light house style. *) +(* *) +(* KNOWN LIMITATIONS *) +(* *) +(* - Cross-level rotations (i2s direction): if r n rotates goals *) +(* from different tree depths past each other (e.g. doing r 2 *) +(* when the stack has [A_sub1, A_sub2, B] to work on B first), *) +(* the tree builder will misassign tactics to branches. Same-level *) +(* rotations (reordering siblings) work correctly. *) +(* *) +(* - Type annotations in xt()/xs(): the interactive commands use *) +(* string_of_term on the goal, which may drop type annotations *) +(* needed for the output to reparse. The file-level i2s preserves *) +(* the original goal string and does not have this problem. *) +(* *) +(* - Multi-proof files with dependencies: s2i and i2s process proofs *) +(* sequentially but do not bind theorem names between proofs. If *) +(* proof N references a name defined by proof N-1, conversion will *) +(* fail. Workaround: convert one proof at a time, or ensure proofs *) +(* are self-contained. *) +(* *) +(* ========================================================================= *) + +(* ------------------------------------------------------------------------- *) +(* Tactic string scanner *) +(* *) +(* Bracket, backtick and string-aware character-by-character scanner *) +(* used throughout for parsing tactic expressions. *) +(* ------------------------------------------------------------------------- *) + +let is_ident_char c = match c with + | 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' | '\'' -> true + | _ -> false;; + +type scan_st = { + s_pd: int; + s_bd: int; + s_bt: bool; + s_st: bool; +};; + +let scan0 = { s_pd=0; s_bd=0; s_bt=false; s_st=false };; + +let scan_char ss c = + if ss.s_st then + (if c = '"' then { ss with s_st = false } else ss) + else if ss.s_bt then + (if c = '`' then { ss with s_bt = false } else ss) + else match c with + | '"' -> { ss with s_st = true } + | '`' -> { ss with s_bt = true } + | '(' -> { ss with s_pd = ss.s_pd + 1 } + | ')' -> { ss with s_pd = ss.s_pd - 1 } + | '[' -> { ss with s_bd = ss.s_bd + 1 } + | ']' -> { ss with s_bd = ss.s_bd - 1 } + | _ -> ss;; + +let at_top ss = + ss.s_pd = 0 && ss.s_bd = 0 && not ss.s_bt && not ss.s_st;; + +(* ------------------------------------------------------------------------- *) +(* Proof tree data type and tactic evaluator *) +(* ------------------------------------------------------------------------- *) + +type proof_tree = + | Pstep of string * proof_tree list + | Popen;; + +let the__tac__ref = ref ALL_TAC;; + +let eval_tactic (s:string) : tactic = + let tmp = Filename.temp_file "hol_tac_" ".ml" in + let oc = open_out tmp in + Printf.fprintf oc "the__tac__ref := (%s);;\n" s; + close_out oc; + let old_tie = !type_invention_error in + type_invention_error := false; + (try loadt tmp + with e -> (type_invention_error := old_tie; + (try Sys.remove tmp with _ -> ()); raise e)); + type_invention_error := old_tie; + (try Sys.remove tmp with _ -> ()); + !the__tac__ref;; + +(* eval_and_apply: evaluate a tactic string AND apply it to the current *) +(* goal in the same temp file context. This avoids a closure capture bug *) +(* where tactics with FIRST_ASSUM/MATCH_MP/SET_RULE and invented type *) +(* variables fail when the closure is built in one loadt and applied later. *) +(* By doing e(tac) in the same temp file, values like SET_RULE results *) +(* remain live when the tactic is applied to the goal. *) +let eval_and_apply (s:string) : unit = + let tmp = Filename.temp_file "hol_tac_" ".ml" in + let oc = open_out tmp in + Printf.fprintf oc "e(%s);;\n" s; + close_out oc; + let old_tie = !type_invention_error in + type_invention_error := false; + (try loadt tmp + with e -> (type_invention_error := old_tie; + (try Sys.remove tmp with _ -> ()); raise e)); + type_invention_error := old_tie; + (try Sys.remove tmp with _ -> ());; + +(* ------------------------------------------------------------------------- *) +(* Recording layer *) +(* ------------------------------------------------------------------------- *) + +let tac_record : (string * int) list ref = ref [];; +let tac_goal : term ref = ref `T`;; + +let num_goals () : int = + try let (_,gls,_) = hd(!current_goalstack) in length gls + with Failure _ -> 0;; + +let xg tm = + tac_record := []; + tac_goal := tm; + g tm;; + +let xe (s:string) = + let n0 = num_goals () in + eval_and_apply s; + let n1 = num_goals () in + tac_record := !tac_record @ [(s, n1 - n0 + 1)]; + !current_goalstack;; + +let re (s:string) (tac:tactic) = + let n0 = num_goals () in + let gs = e tac in + let n1 = num_goals () in + tac_record := !tac_record @ [(s, n1 - n0 + 1)]; + gs;; + +let xb () = + (match !tac_record with + | [] -> () + | _ -> tac_record := rev (tl (rev !tac_record))); + b();; + +let xr n = + let gs = r n in + tac_record := !tac_record @ [("__ROTATE__", n)]; + gs;; + +let xp () = + let steps = !tac_record in + if steps = [] then print_string "(no steps recorded)\n" + else + do_list (fun (s,n) -> + if s = "__ROTATE__" then + Printf.printf " r %-48d\n" n + else + Printf.printf " %-50s [%d]\n" s n) steps; + Printf.printf " (%d steps total)\n%!" (length steps);; + +(* ------------------------------------------------------------------------- *) +(* Build proof tree from recording *) +(* ------------------------------------------------------------------------- *) + +let rec build_ptree steps = + match steps with + | [] -> (Popen, []) + | (tac, n) :: rest -> + if tac = "__ROTATE__" then build_ptree rest + else if n <= 0 then (Pstep(tac, []), rest) + else + let children, remaining = build_ptree_children n rest in + (Pstep(tac, children), remaining) + +and build_ptree_children n steps = + if n <= 0 then ([], steps) + else + let ids = ref [] in + (for ii = n - 1 downto 0 do ids := ii :: !ids done); + let queue = ref !ids in + let s = ref steps in + let pairs = ref [] in + (for dummy = 1 to n do + let keep_looping = ref true in + (while !keep_looping do + if !s = [] then keep_looping := false + else if fst (hd !s) = "__ROTATE__" then + (let k = snd (hd !s) in + s := tl !s; + let q = !queue in + let ql = length q in + let kk = ((k mod ql) + ql) mod ql in + if kk > 0 then + (let front = ref [] in + let back = ref q in + (for dummy2 = 1 to kk do + front := hd !back :: !front; + back := tl !back + done); + queue := !back @ rev !front) + else ()) + else keep_looping := false + done); + let orig_idx = hd !queue in + let (child, rest) = build_ptree !s in + pairs := (orig_idx, child) :: !pairs; + s := rest; + queue := tl !queue + done); + let sorted = sort (fun (a,_) (b,_) -> a < b) (rev !pairs) in + (map snd sorted, !s);; + +let get_ptree () = + let tree, left = build_ptree !tac_record in + if left <> [] then + Printf.printf "(* Warning: %d unused recording steps *)\n" (length left); + tree;; + +(* ------------------------------------------------------------------------- *) +(* Proof tree equality and THEN optimization *) +(* ------------------------------------------------------------------------- *) + +let rec ptree_eq t1 t2 = match t1, t2 with + | Popen, Popen -> true + | Pstep(s1,c1), Pstep(s2,c2) -> + s1 = s2 && length c1 = length c2 && forall2 ptree_eq c1 c2 + | _ -> false;; + +let all_ptree_eq = function + | [] | [_] -> true + | h :: t -> forall (ptree_eq h) t;; + +let common_head trees = + match trees with + | [] -> None + | Popen :: _ -> None + | Pstep(h,_) :: rest -> + if forall (fun t -> match t with + | Pstep(s,_) -> s = h | _ -> false) rest + then Some h + else None;; + +let factor_head trees = + match common_head trees with + | None -> None + | Some h -> + let children = map (fun t -> match t with + | Pstep(_, cs) -> cs | _ -> []) trees in + if forall (fun cs -> length cs = 1) children then + Some(h, map hd children) + else None;; + +(* ------------------------------------------------------------------------- *) +(* Pretty printer - proof tree to structured tactic string *) +(* ------------------------------------------------------------------------- *) + +let pad n = String.make (max 0 n) ' ';; + +(* Split a tactic string at top-level THEN (not THENL) keywords *) +let split_at_then s = + let len = String.length s in + if len = 0 then [s] + else + let ss = ref scan0 in + let parts = ref [] in + let start = ref 0 in + let i = ref 0 in + while !i < len do + ss := scan_char !ss s.[!i]; + if at_top !ss && + !i + 4 <= len && + String.sub s !i 4 = "THEN" && + (!i = 0 || not (is_ident_char s.[!i - 1])) && + (!i + 4 >= len || not (is_ident_char s.[!i + 4])) then ( + parts := String.trim (String.sub s !start (!i - !start)) :: !parts; + i := !i + 4; + while !i < len && (s.[!i] = ' ' || s.[!i] = '\n' || + s.[!i] = '\r' || s.[!i] = '\t') do incr i done; + start := !i + ) else incr i + done; + let last = String.trim (String.sub s !start (len - !start)) in + let parts = if last = "" then !parts else last :: !parts in + rev parts;; + +(* Format a tactic string to stay within 78 columns, starting at col. *) +(* Greedily packs THEN-separated parts on lines, breaking when needed. *) +let format_tac col tac = + let parts = split_at_then tac in + match parts with + | [] -> tac + | [_] -> tac + | _ -> + let buf = Buffer.create 256 in + let cur_col = ref col in + let is_first = ref true in + do_list (fun part -> + let plen = String.length part in + if !is_first then ( + Buffer.add_string buf part; + cur_col := col + plen; + is_first := false + ) else if !cur_col + 6 + plen <= 78 then ( + Buffer.add_string buf " THEN "; + Buffer.add_string buf part; + cur_col := !cur_col + 6 + plen + ) else ( + Buffer.add_string buf " THEN\n"; + Buffer.add_string buf (pad col); + Buffer.add_string buf part; + cur_col := col + plen + ) + ) parts; + Buffer.contents buf;; + +let rec render col tree = + match tree with + | Popen -> "CHEAT_TAC" + | Pstep(tac, []) -> format_tac col tac + | Pstep(tac, [child]) -> + format_tac col tac ^ " THEN\n" ^ pad col ^ render col child + | Pstep(tac, children) when all_ptree_eq children -> + format_tac col tac ^ " THEN\n" ^ pad col ^ render col (hd children) + | Pstep(tac, children) -> + (match factor_head children with + | Some(h, new_children) -> + format_tac col tac ^ " THEN\n" ^ pad col ^ + render col (Pstep(h, new_children)) + | None -> + let ic = col + 2 in + format_tac col tac ^ " THENL\n" ^ pad (col + 1) ^ + "[" ^ String.concat (";\n" ^ pad ic) + (map (render ic) children) ^ "]");; + +let xt () = + let tree = get_ptree () in + let gs = string_of_term !tac_goal in + let ts = render 2 tree in + Printf.printf "let RESULT = prove\n (`%s`,\n %s);;\n%!" gs ts;; + +let xs () = + let tree = get_ptree () in + let gs = string_of_term !tac_goal in + let ts = render 2 tree in + Printf.sprintf "let RESULT = prove\n (`%s`,\n %s);;" gs ts;; + +let xreset () = + tac_record := []; + Printf.printf "(recording cleared)\n%!";; + +let xshow () = + Printf.printf "Goal: `%s`\n\n" (string_of_term !tac_goal); + Printf.printf "Recording (%d steps):\n" (length !tac_record); + xp(); + Printf.printf "\nStructured proof:\n"; + xt();; + +(* ------------------------------------------------------------------------- *) +(* Tactic string parser (structured -> interactive) *) +(* *) +(* Parses a tactic expression, finding THENL branching structure *) +(* and let...in bindings. THEN chains are kept as atomic strings. *) +(* ------------------------------------------------------------------------- *) + +type ftree = + | Fleaf of string + | Fbranch of string * ftree list + | Fafter of ftree * ftree + | Flet of string * ftree;; + +(* Find first top-level THENL in string s *) +(* Returns Some(prefix, bracket_contents, suffix) or None *) +let find_thenl s = + let len = String.length s in + let ss = ref scan0 in + let result = ref None in + let i = ref 0 in + while !i < len && !result = None do + ss := scan_char !ss s.[!i]; + if at_top !ss && !i + 5 <= len then begin + let before_ok = (!i = 0 || not (is_ident_char s.[!i - 1])) in + if before_ok && + !i + 5 <= len && String.sub s !i 5 = "THENL" && + (!i + 5 >= len || not (is_ident_char s.[!i + 5])) then + begin + let prefix = String.trim (String.sub s 0 !i) in + let j = ref (!i + 5) in + while !j < len && (s.[!j] = ' ' || s.[!j] = '\n' || + s.[!j] = '\r' || s.[!j] = '\t') do + incr j + done; + if !j < len && s.[!j] = '[' then begin + let bstart = !j + 1 in + let depth = ref 1 in + let bs = ref scan0 in + let k = ref bstart in + while !k < len && !depth > 0 do + bs := scan_char !bs s.[!k]; + if not (!bs).s_bt && not (!bs).s_st && (!bs).s_pd = 0 then begin + if s.[!k] = '[' then incr depth + else if s.[!k] = ']' then decr depth + end; + if !depth > 0 then incr k + done; + let bcontent = String.sub s bstart (!k - bstart) in + let rest = if !k + 1 < len + then String.trim (String.sub s (!k + 1) (len - !k - 1)) + else "" in + let suffix = + let rlen = String.length rest in + if rlen >= 4 && String.sub rest 0 4 = "THEN" && + (rlen = 4 || not (is_ident_char rest.[4])) && + not (rlen >= 5 && String.sub rest 0 5 = "THENL") then + String.trim (String.sub rest 4 (rlen - 4)) + else rest in + result := Some(prefix, bcontent, suffix) + end + end + end; + incr i + done; + !result;; + +(* Split string at top-level semicolons *) +let split_semis s = + let len = String.length s in + let ss = ref scan0 in + let parts = ref [] in + let start = ref 0 in + for i = 0 to len - 1 do + ss := scan_char !ss s.[i]; + if at_top !ss && s.[i] = ';' then begin + parts := String.trim (String.sub s !start (i - !start)) :: !parts; + start := i + 1 + end + done; + let last = String.trim (String.sub s !start (len - !start)) in + let parts = if last = "" then !parts else last :: !parts in + rev parts;; + +(* Find top-level 'let ... in' binding *) +let find_let_in s = + let len = String.length s in + if len < 6 || String.sub s 0 4 <> "let " then None + else + let ss = ref scan0 in + let depth = ref 0 in + let result = ref None in + let i = ref 0 in + while !i < len && !result = None do + ss := scan_char !ss s.[!i]; + if at_top !ss then ( + if !i + 3 <= len && String.sub s !i 3 = "let" && + (!i = 0 || not (is_ident_char s.[!i - 1])) && + (!i + 3 >= len || not (is_ident_char s.[!i + 3])) then + depth := !depth + 1; + if !i + 2 <= len && String.sub s !i 2 = "in" && + (!i = 0 || not (is_ident_char s.[!i - 1])) && + (!i + 2 >= len || not (is_ident_char s.[!i + 2])) then ( + depth := !depth - 1; + if !depth = 0 then + result := Some !i + ) + ); + incr i + done; + match !result with + | None -> None + | Some pos -> + let binding = String.trim (String.sub s 0 pos) in + let body = String.trim (String.sub s (pos + 2) (len - pos - 2)) in + Some(binding, body);; + +let rec parse_tac s = + let s = String.trim s in + match find_let_in s with + | Some(binding, body) -> Flet(binding, parse_tac body) + | None -> + match find_thenl s with + | None -> Fleaf s + | Some(prefix, content, suffix) -> + let branches = split_semis content in + let core = Fbranch(String.trim prefix, + map parse_tac branches) in + if suffix = "" then core + else Fafter(core, parse_tac suffix);; + +(* ------------------------------------------------------------------------- *) +(* Flatten structured proof to interactive form *) +(* ------------------------------------------------------------------------- *) + +let rec flatten_step prefix tree = + match tree with + | Fleaf s -> + Printf.printf "%se(%s);;\n%!" prefix s; + let n0 = num_goals () in + eval_and_apply s; + let n1 = num_goals () in + n1 - n0 + 1 + | Fbranch(pre, branches) -> + Printf.printf "%se(%s);;\n%!" prefix pre; + let n0 = num_goals () in + eval_and_apply pre; + let n1 = num_goals () in + let nbranch = n1 - n0 + 1 in + if nbranch <> length branches then + (Printf.printf + "(* ERROR: %s produced %d subgoals but THENL has %d branches *)\n" + pre nbranch (length branches); + nbranch) + else + let total = ref 0 in + let idx = ref 1 in + do_list (fun branch -> + Printf.printf "%s(* branch %d of %d *)\n%!" prefix !idx nbranch; + let m = flatten_step prefix branch in + total := !total + m; + incr idx + ) branches; + !total + | Fafter(main, suffix) -> + (match main with + | Fbranch(pre, branches) -> + flatten_step prefix + (Fbranch(pre, map (fun b -> Fafter(b, suffix)) branches)) + | _ -> + let remaining = flatten_step prefix main in + if remaining = 0 then 0 + else + let total = ref 0 in + for dummy = 1 to remaining do + let m = flatten_step prefix suffix in + total := !total + m + done; + !total) + | Flet(binding, body) -> + Printf.printf "%s%s;;\n%!" prefix binding; + let tmp = Filename.temp_file "hol_let_" ".ml" in + let oc = open_out tmp in + Printf.fprintf oc "%s;;\n" binding; + close_out oc; + (try loadt tmp + with e -> (try Sys.remove tmp with _ -> ()); raise e); + (try Sys.remove tmp with _ -> ()); + flatten_step prefix body;; + +let flatten_proof tm tac_str = + let _ = g tm in + let tree = parse_tac tac_str in + Printf.printf "g `%s`;;\n\n%!" (string_of_term tm); + let remaining = flatten_step "" tree in + if remaining = 0 then + Printf.printf "\n(* Proof complete. *)\n%!" + else + Printf.printf "\n(* %d subgoals remaining *)\n%!" remaining;; + +(* ------------------------------------------------------------------------- *) +(* File-level proof format conversion *) +(* ------------------------------------------------------------------------- *) + +let read_whole_file fn = + let ic = Stdlib.open_in fn in + let n = in_channel_length ic in + let buf = Buffer.create n in + (try while true do Buffer.add_char buf (input_char ic) done + with End_of_file -> ()); + Stdlib.close_in ic; + Buffer.contents buf;; + +let write_whole_file fn s = + let oc = open_out fn in + output_string oc s; + close_out oc;; + +(* --- Bracket-aware scanning for file parsing --- *) + +let find_backtick s i = + let len = String.length s in + let j = ref i in + while !j < len && s.[!j] <> '`' do incr j done; + if !j < len then !j else -1;; + +let find_close_paren s start = + let len = String.length s in + let dep = ref 1 in + let in_str = ref false in + let in_bt = ref false in + let cmt = ref 0 in + let i = ref start in + while !i < len && !dep > 0 do + let c = s.[!i] in + if !in_str then + (if c = '\\' && !i + 1 < len then incr i + else if c = '"' then in_str := false) + else if !in_bt then + (if c = '`' then in_bt := false) + else if !cmt > 0 then + (if c = '(' && !i + 1 < len && s.[!i+1] = '*' then + (cmt := !cmt + 1; incr i) + else if c = '*' && !i + 1 < len && s.[!i+1] = ')' then + (cmt := !cmt - 1; incr i)) + else + (if c = '"' then in_str := true + else if c = '`' then in_bt := true + else if c = '(' && !i + 1 < len && s.[!i+1] = '*' then + (cmt := 1; incr i) + else if c = '(' then dep := !dep + 1 + else if c = ')' then dep := !dep - 1); + if !dep > 0 then incr i + done; + if !dep = 0 then !i else -1;; + +let find_double_semi s i = + let len = String.length s in + let j = ref i in + while !j < len - 1 && not (s.[!j] = ';' && s.[!j+1] = ';') do + incr j + done; + if !j < len - 1 then !j else -1;; + +let string_has_sub s sub = + let slen = String.length s in + let sublen = String.length sub in + let found = ref false in + let i = ref 0 in + while !i <= slen - sublen && not !found do + if String.sub s !i sublen = sub then found := true + else incr i + done; + !found;; + +(* --- Flatten to buffer (used by s2i) --- *) + +let rec flatten_to_buf buf prefix tree = + match tree with + | Fleaf s -> + Buffer.add_string buf (Printf.sprintf "%se(%s);;\n" prefix s); + let n0 = num_goals () in + eval_and_apply s; + let n1 = num_goals () in + n1 - n0 + 1 + | Fbranch(pre, branches) -> + Buffer.add_string buf (Printf.sprintf "%se(%s);;\n" prefix pre); + let n0 = num_goals () in + eval_and_apply pre; + let n1 = num_goals () in + let nbranch = n1 - n0 + 1 in + if nbranch <> length branches then + (Buffer.add_string buf (Printf.sprintf + "%s(* ERROR: %s produced %d subgoals but THENL has %d branches *)\n" + prefix pre nbranch (length branches)); + nbranch) + else + let total = ref 0 in + let idx = ref 1 in + do_list (fun branch -> + Buffer.add_string buf (Printf.sprintf + "%s(* branch %d of %d *)\n" prefix !idx nbranch); + let m = flatten_to_buf buf prefix branch in + total := !total + m; + incr idx + ) branches; + !total + | Fafter(main, suffix) -> + (match main with + | Fbranch(pre, branches) -> + flatten_to_buf buf prefix + (Fbranch(pre, map (fun b -> Fafter(b, suffix)) branches)) + | _ -> + let remaining = flatten_to_buf buf prefix main in + if remaining = 0 then 0 + else + let total = ref 0 in + for dummy = 1 to remaining do + let m = flatten_to_buf buf prefix suffix in + total := !total + m + done; + !total) + | Flet(binding, body) -> + Buffer.add_string buf (Printf.sprintf "%s%s;;\n" prefix binding); + let tmp = Filename.temp_file "hol_let_" ".ml" in + let oc = open_out tmp in + Printf.fprintf oc "%s;;\n" binding; + close_out oc; + (try loadt tmp + with e -> (try Sys.remove tmp with _ -> ()); raise e); + (try Sys.remove tmp with _ -> ()); + flatten_to_buf buf prefix body;; + +(* --- Parse interactive proof file --- *) + +let parse_interactive content = + let len = String.length content in + let proofs = ref [] in + let cur_name = ref (None : string option) in + let cur_goal = ref "" in + let cur_steps = ref ([] : string list) in + let flush () = + if !cur_goal <> "" then ( + proofs := (!cur_name, !cur_goal, rev !cur_steps) :: !proofs; + cur_name := None; cur_goal := ""; cur_steps := []) in + let i = ref 0 in + while !i < len do + while !i < len && + (let c = content.[!i] in + c = ' ' || c = '\n' || c = '\r' || c = '\t') do incr i done; + if !i >= len then () + else if !i + 1 < len && content.[!i] = '(' && content.[!i+1] = '*' + then ( + let d = ref 1 in + i := !i + 2; + while !i < len - 1 && !d > 0 do + if content.[!i] = '(' && content.[!i+1] = '*' then + (d := !d + 1; i := !i + 2) + else if content.[!i] = '*' && content.[!i+1] = ')' then + (d := !d - 1; i := !i + 2) + else incr i + done) + else if content.[!i] = 'g' && !i + 1 < len && + not (is_ident_char content.[!i+1]) then ( + flush (); + let j = find_backtick content (!i + 1) in + if j >= 0 then ( + let j2 = find_backtick content (j + 1) in + if j2 >= 0 then ( + cur_goal := String.sub content (j + 1) (j2 - j - 1); + let semi = find_double_semi content (j2 + 1) in + i := (if semi >= 0 then semi + 2 else j2 + 1)) + else i := j + 1) + else incr i) + else if content.[!i] = 'b' && !i + 1 < len && + not (is_ident_char content.[!i+1]) then ( + let semi = find_double_semi content (!i + 1) in + let arg = String.trim (String.sub content (!i + 1) + ((if semi >= 0 then semi else len) - !i - 1)) in + if arg = "()" then ( + (match !cur_steps with + | [] -> () + | _ :: rest -> cur_steps := rest); + i := (if semi >= 0 then semi + 2 else len)) + else + i := (if semi >= 0 then semi + 2 else len)) + else if content.[!i] = 'r' && !i + 1 < len && + not (is_ident_char content.[!i+1]) then ( + let semi = find_double_semi content (!i + 1) in + if semi >= 0 then ( + let arg = String.trim (String.sub content (!i + 1) (semi - !i - 1)) in + let arg = let al = String.length arg in + if al >= 2 && arg.[0] = '(' && arg.[al - 1] = ')' then + String.trim (String.sub arg 1 (al - 2)) + else arg in + (try let n = int_of_string arg in + cur_steps := ("__ROTATE__ " ^ string_of_int n) :: !cur_steps + with _ -> ()); + i := semi + 2) + else incr i) + else if content.[!i] = 'p' && !i + 1 < len && + not (is_ident_char content.[!i+1]) then ( + let semi = find_double_semi content (!i + 1) in + i := (if semi >= 0 then semi + 2 else len)) + else if content.[!i] = 'e' && !i + 1 < len && + content.[!i+1] = '(' then ( + let pend = find_close_paren content (!i + 2) in + if pend >= 0 then ( + let tac = String.trim + (String.sub content (!i + 2) (pend - !i - 2)) in + cur_steps := tac :: !cur_steps; + let semi = find_double_semi content (pend + 1) in + i := (if semi >= 0 then semi + 2 else pend + 1)) + else incr i) + else if !i + 4 < len && String.sub content !i 4 = "let " then ( + let semi = find_double_semi content !i in + if semi >= 0 then ( + let chunk = String.sub content !i (semi - !i) in + if string_has_sub chunk "top_thm" then ( + let j = ref 4 in + while !j < String.length chunk && + (chunk.[!j] = ' ' || chunk.[!j] = '\n') do incr j done; + let nstart = !j in + while !j < String.length chunk && + is_ident_char chunk.[!j] do incr j done; + if !j > nstart then + cur_name := Some (String.sub chunk nstart (!j - nstart))); + i := semi + 2) + else incr i) + else ( + let semi = find_double_semi content !i in + if semi >= 0 then i := semi + 2 + else (while !i < len && content.[!i] <> '\n' do incr i done; + if !i < len then incr i)) + done; + flush (); + rev !proofs;; + +(* --- Parse structured proof file --- *) + +let parse_structured content = + let len = String.length content in + let proofs = ref [] in + let i = ref 0 in + let in_str = ref false in + let in_bt = ref false in + let cmt = ref 0 in + let dep = ref 0 in + while !i < len do + let c = content.[!i] in + if !in_str then + (if c = '\\' && !i + 1 < len then incr i + else if c = '"' then in_str := false) + else if !in_bt then + (if c = '`' then in_bt := false) + else if !cmt > 0 then + (if c = '(' && !i + 1 < len && content.[!i+1] = '*' then + (cmt := !cmt + 1; incr i) + else if c = '*' && !i + 1 < len && content.[!i+1] = ')' then + (cmt := !cmt - 1; incr i)) + else ( + (match c with + | '"' -> in_str := true + | '`' -> in_bt := true + | '(' when !i + 1 < len && content.[!i+1] = '*' -> + cmt := 1; incr i + | '(' -> dep := !dep + 1 + | ')' -> dep := !dep - 1 + | _ -> ()); + if !dep = 0 && !cmt = 0 && not !in_str && not !in_bt && + !i + 5 <= len && String.sub content !i 5 = "prove" && + (!i = 0 || not (is_ident_char content.[!i-1])) && + (!i + 5 >= len || not (is_ident_char content.[!i+5])) + then ( + let name = ref "RESULT" in + let j = ref (!i - 1) in + while !j >= 0 && (let ch = content.[!j] in + ch = ' ' || ch = '\n' || ch = '\r' || ch = '\t' || + ch = '=') do decr j done; + let ne = !j + 1 in + while !j >= 0 && is_ident_char content.[!j] do decr j done; + let ns = !j + 1 in + if ns < ne then name := String.sub content ns (ne - ns); + let k = ref (!i + 5) in + while !k < len && content.[!k] <> '(' do incr k done; + if !k < len then ( + let pstart = !k + 1 in + let bt1 = find_backtick content pstart in + if bt1 >= 0 then ( + let bt2 = find_backtick content (bt1 + 1) in + if bt2 >= 0 then ( + let goal = String.sub content (bt1 + 1) (bt2 - bt1 - 1) in + let m = ref (bt2 + 1) in + while !m < len && (let ch = content.[!m] in + ch = ' ' || ch = '\n' || ch = '\r' || ch = '\t' || + ch = ',') do incr m done; + let pend = find_close_paren content pstart in + if pend >= 0 then ( + let tac = String.trim + (String.sub content !m (pend - !m)) in + proofs := (!name, goal, tac) :: !proofs; + i := pend + 1) + else i := !m) + else i := bt1 + 1) + else i := pstart) + else i := !k) + ); + incr i + done; + rev !proofs;; + +(* --- i2s: interactive to structured --- *) + +let i2s infile outfile = + let content = read_whole_file infile in + let proofs = parse_interactive content in + if proofs = [] then + (Printf.printf "No proof blocks found in %s\n%!" infile; ()) + else + let buf = Buffer.create 4096 in + do_list (fun (name, goal, steps) -> + let nm = (match name with Some n -> n | None -> "RESULT") in + Printf.printf "Converting %s ...\n%!" nm; + let tm = parse_term goal in + let _ = xg tm in + do_list (fun s -> + let rp = "__ROTATE__ " in + let rplen = String.length rp in + if String.length s > rplen && + String.sub s 0 rplen = rp then + (let n = int_of_string (String.sub s rplen + (String.length s - rplen)) in + Printf.printf " r %d\n%!" n; + let _ = xr n in ()) + else + (Printf.printf " xe %s\n%!" + (String.sub s 0 (min 60 (String.length s))); + let _ = xe s in ())) steps; + if num_goals () <> 0 then + Printf.printf " WARNING: %d subgoals remaining\n%!" + (num_goals ()); + let tree = get_ptree () in + let ts = render 2 tree in + Buffer.add_string buf + (Printf.sprintf "let %s = prove\n (`%s`,\n %s);;\n\n" + nm goal ts) + ) proofs; + write_whole_file outfile (Buffer.contents buf); + Printf.printf "Wrote %d proof(s) to %s\n%!" (length proofs) outfile;; + +(* --- s2i: structured to interactive --- *) + +let s2i infile outfile = + let content = read_whole_file infile in + let proofs = parse_structured content in + if proofs = [] then + (Printf.printf "No prove(...) blocks found in %s\n%!" infile; ()) + else + let buf = Buffer.create 4096 in + do_list (fun (name, goal, tac_str) -> + Printf.printf "Converting %s ...\n%!" name; + let tm = parse_term goal in + let _ = g tm in + Buffer.add_string buf + (Printf.sprintf "(* %s *)\ng `%s`;;\n" name goal); + let tree = parse_tac tac_str in + let remaining = flatten_to_buf buf "" tree in + if remaining = 0 then + Buffer.add_string buf + (Printf.sprintf "let %s = top_thm();;\n\n" name) + else + Buffer.add_string buf + (Printf.sprintf "(* %d subgoals remaining *)\n\n" remaining) + ) proofs; + write_whole_file outfile (Buffer.contents buf); + Printf.printf "Wrote %d proof(s) to %s\n%!" (length proofs) outfile;; diff --git a/Library/words.ml b/Library/words.ml index baf9435a..354a50ff 100644 --- a/Library/words.ml +++ b/Library/words.ml @@ -6104,6 +6104,47 @@ let WORD_CLZ_REVERSEFIELDS = prove (`!x:N word. word_clz(word_reversefields 1 x) = word_ctz x`, MESON_TAC[WORD_REVERSEFIELDS_REVERSEFIELDS; WORD_CTZ_REVERSEFIELDS]);; +let WORD_CLZ_USHR = prove + (`!(x:N word) n. + word_clz (word_ushr x n) = MIN (dimindex(:N)) (word_clz x + n)`, + REPEAT GEN_TAC THEN + REWRITE_TAC[WORD_CLZ_UNIQUE_GEN; BIT_WORD_USHR] THEN + CONJ_TAC THENL [ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[ARITH_RULE `MIN a b < a <=> b < a`] THEN + SIMP_TAC[ARITH_RULE + `c + n < N ==> N - MIN N (c + n) - 1 + n = N - c - 1`] THEN + REWRITE_TAC[ARITH_RULE + `N - MIN N c <= i <=> N <= i + c`] THEN + MP_TAC(ISPECL [`x:N word`; `word_clz(x:N word)`] WORD_CLZ_UNIQUE_GEN) THEN + REWRITE_TAC[LE_REFL] THEN STRIP_TAC THEN CONJ_TAC THENL + [DISCH_TAC; GEN_TAC THEN DISCH_TAC] THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_ARITH_TAC);; + +let WORD_SHL_AS_USHR = prove + (`!(x:N word) n. + word_shl x n = + word_reversefields 1 (word_ushr (word_reversefields 1 x) n)`, + REPEAT GEN_TAC THEN REWRITE_TAC[WORD_EQ_BITS_ALT] THEN + REWRITE_TAC[BIT_REVERSEFIELDS_1; BIT_WORD_SHL; BIT_WORD_USHR] THEN + X_GEN_TAC `i:num` THEN DISCH_TAC THEN ASM_REWRITE_TAC[] THEN + ASM_SIMP_TAC[ARITH_RULE `i < N ==> (N - 1 - i + n < N <=> n <= i)`] THEN + ASM_CASES_TAC `n:num <= i` THEN ASM_REWRITE_TAC[] THEN + AP_THM_TAC THEN AP_TERM_TAC THEN ASM_ARITH_TAC);; + +let WORD_USHR_AS_SHL = prove + (`!(x:N word) n. + word_ushr x n = + word_reversefields 1 (word_shl (word_reversefields 1 x) n)`, + REWRITE_TAC[WORD_SHL_AS_USHR; WORD_REVERSEFIELDS_REVERSEFIELDS]);; + +let WORD_CTZ_SHL = prove + (`!(x:N word) n. + word_ctz (word_shl x n) = MIN (dimindex(:N)) (word_ctz x + n)`, + REPEAT GEN_TAC THEN + REWRITE_TAC[GSYM WORD_CLZ_REVERSEFIELDS; WORD_SHL_AS_USHR; + WORD_REVERSEFIELDS_REVERSEFIELDS] THEN + REWRITE_TAC[WORD_CLZ_USHR]);; + (* ------------------------------------------------------------------------- *) (* Byte reversal, with type constrained to multiple of 8. *) (* ------------------------------------------------------------------------- *) @@ -6143,6 +6184,123 @@ let WORD_BYTEREVERSE_REVERSEFIELDS = prove REWRITE_TAC[DIMINDEX_TYBIT0; MULT_ASSOC] THEN CONV_TAC NUM_REDUCE_CONV THEN SIMP_TAC[DIV_MULT; ARITH_EQ]);; +(* ------------------------------------------------------------------------- *) +(* Multiplication of binary polynomials as words, "carryless multiplication" *) +(* ------------------------------------------------------------------------- *) + +let word_pmul = define + `(word_pmul:M word->N word->P word) x y = + word_of_bits {k | ODD(CARD {i | i <= k /\ bit i x /\ bit (k - i) y})}`;; + +let BIT_WORD_PMUL_ALT = prove + (`!x y k. bit k ((word_pmul:M word->N word->P word) x y) <=> + k < dimindex(:P) /\ + ODD(CARD {i | i <= k /\ bit i x /\ bit (k - i) y})`, + REWRITE_TAC[word_pmul; BIT_WORD_OF_BITS; IN_ELIM_THM]);; + +let BIT_WORD_PMUL = prove + (`!x y k. bit k ((word_pmul:M word->N word->P word) x y) <=> + k < dimindex(:P) /\ + ODD(nsum (0..k) (\i. bitval(bit i x) * bitval(bit (k - i) y)))`, + REPEAT GEN_TAC THEN REWRITE_TAC[BIT_WORD_PMUL_ALT] THEN + AP_TERM_TAC THEN REWRITE_TAC[GSYM BITVAL_AND] THEN + REWRITE_TAC[bitval; GSYM NSUM_RESTRICT_SET] THEN + SIMP_TAC[NSUM_CONST; FINITE_RESTRICT; FINITE_NUMSEG] THEN + REWRITE_TAC[MULT_CLAUSES; LE_0; IN_NUMSEG]);; + +let BITVAL_BIT_WORD_PMUL = prove + (`!x y k. bitval(bit k ((word_pmul:M word->N word->P word) x y)) = + if k < dimindex(:P) + then nsum(0..k) (\i. bitval(bit i x) * bitval(bit (k - i) y)) MOD 2 + else 0`, + REPEAT GEN_TAC THEN REWRITE_TAC[BIT_WORD_PMUL] THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[BITVAL_CLAUSES; BITVAL_ODD]);; + +let WORD_PMUL_0 = prove + (`(!y. (word_pmul:M word->N word->P word) (word 0) y = word 0) /\ + (!x. (word_pmul:M word->N word->P word) x (word 0) = word 0)`, + REWRITE_TAC[word_pmul; BIT_WORD_0; MULT_CLAUSES; EMPTY_GSPEC; + CARD_CLAUSES; ODD; WORD_OF_BITS_EMPTY]);; + +let WORD_PMUL_XOR = prove + (`(!x y z. (word_pmul:M word->N word->P word) (word_xor x y) z = + word_xor (word_pmul x z) (word_pmul y z)) /\ + (!x y z. (word_pmul:M word->N word->P word) x (word_xor y z) = + word_xor (word_pmul x y) (word_pmul x z))`, + REPEAT STRIP_TAC THEN + GEN_REWRITE_TAC I [WORD_EQ_BITS_ALT] THEN + REWRITE_TAC[BIT_WORD_PMUL; BIT_WORD_XOR_ALT] THEN + X_GEN_TAC `i:num` THEN DISCH_TAC THEN + ASM_REWRITE_TAC[GSYM ODD_ADD] THEN + REWRITE_TAC[MESON[ODD_ADD; NOT_ODD] `(ODD x <=> ODD y) <=> EVEN(x + y)`] THEN + REWRITE_TAC[GSYM NSUM_ADD_NUMSEG] THEN + MATCH_MP_TAC(ISPEC `EVEN` NSUM_CLOSED) THEN + SIMP_TAC[EVEN; EVEN_ADD] THEN X_GEN_TAC `j:num` THEN DISCH_TAC THEN + REWRITE_TAC[IN_NUMSEG; LE_0; BITVAL_XOR] THEN + REWRITE_TAC[bitval] THEN + REPEAT(COND_CASES_TAC THEN ASM_REWRITE_TAC[]) THEN + CONV_TAC NUM_REDUCE_CONV);; + +let WORD_PMUL_ZX = prove + (`(!x y. word_pmul (word_zx x:P word) y = + (word_pmul:M word->N word->P word) x y) /\ + (!x y. word_pmul x (word_zx y:P word) = + (word_pmul:M word->N word->P word) x y)`, + REPEAT STRIP_TAC THEN REWRITE_TAC[word_pmul; BIT_WORD_ZX] THEN + GEN_REWRITE_TAC I [WORD_OF_BITS_EQ] THEN SIMP_TAC[IN_ELIM_THM] THEN + REPEAT STRIP_TAC THEN AP_TERM_TAC THEN AP_TERM_TAC THEN + GEN_REWRITE_TAC I [EXTENSION] THEN GEN_TAC THEN + REWRITE_TAC[IN_ELIM_THM] THEN + EQ_TAC THEN STRIP_TAC THEN ASM_REWRITE_TAC[] THEN ASM_ARITH_TAC);; + +let WORD_PMUL_STEP = prove + (`!x y. (word_pmul:M word->N word->N word) x y = + word_xor (if bit 0 x then y else word 0) + (word_pmul (word_ushr x 1) (word_shl y 1))`, + REPEAT GEN_TAC THEN GEN_REWRITE_TAC I [WORD_EQ_BITS_ALT] THEN + X_GEN_TAC `i:num` THEN DISCH_TAC THEN + ASM_REWRITE_TAC[BIT_WORD_XOR; BIT_WORD_PMUL] THEN + REWRITE_TAC[TAUT `(p <=> ~(q <=> r)) <=> ~(p <=> r) <=> q`] THEN + REWRITE_TAC[GSYM ODD_ADD] THEN + REWRITE_TAC[BIT_WORD_USHR; BIT_WORD_SHL] THEN + ASM_SIMP_TAC[ARITH_RULE `i:num < N ==> i - j < N`] THEN + REWRITE_TAC[ARITH_RULE `i - j - 1 = i - (j + 1)`] THEN + REWRITE_TAC[ARITH_RULE `1 <= i - j <=> j + 1 <= i`] THEN + REWRITE_TAC[GSYM(SPEC `1` NSUM_OFFSET)] THEN + SIMP_TAC[NSUM_CLAUSES_LEFT; LE_0] THEN REWRITE_TAC[ADD_CLAUSES] THEN + REWRITE_TAC[GSYM ADD1; NSUM_CLAUSES_NUMSEG; ARITH_RULE `1 <= SUC n`] THEN + SIMP_TAC[ARITH_RULE `~(SUC i <= i)`; BITVAL_CLAUSES; MULT_CLAUSES] THEN + REWRITE_TAC[ADD_CLAUSES; ARITH_RULE `(i + j) + j = 2 * j + i`] THEN + REWRITE_TAC[ODD_ADD; ODD_MULT; ARITH_ODD; SUB_0] THEN + REWRITE_TAC[bitval] THEN REPEAT(COND_CASES_TAC THEN ASM_REWRITE_TAC[]) THEN + REWRITE_TAC[BIT_WORD_0] THEN CONV_TAC NUM_REDUCE_CONV);; + +let WORD_PMUL_SYM = prove + (`!x y. (word_pmul:M word->M word->N word) x y = word_pmul y x`, + REPEAT GEN_TAC THEN REWRITE_TAC[WORD_EQ_BITS_ALT] THEN + REWRITE_TAC[BIT_WORD_PMUL] THEN X_GEN_TAC `i:num` THEN DISCH_TAC THEN + GEN_REWRITE_TAC (RAND_CONV o ONCE_DEPTH_CONV) [NSUM_REFLECT] THEN + ASM_SIMP_TAC[LT; SUB_0; ARITH_RULE `j:num <= i ==> i - (i - j) = j`] THEN + REWRITE_TAC[MULT_SYM]);; + +let WORD_PMUL_POW2 = prove + (`(!(x:N word) n. word_pmul (word(2 EXP n):N word) x = word_shl x n) /\ + (!(x:N word) n. word_pmul x (word(2 EXP n):N word) = word_shl x n)`, + GEN_REWRITE_TAC (RAND_CONV o ONCE_DEPTH_CONV) [WORD_PMUL_SYM] THEN + REWRITE_TAC[] THEN REPEAT GEN_TAC THEN + ONCE_REWRITE_TAC[WORD_EQ_BITS_ALT] THEN + X_GEN_TAC `i:num` THEN DISCH_TAC THEN + ASM_REWRITE_TAC[BIT_WORD_PMUL; BIT_WORD_SHL; BIT_WORD_POW2] THEN + REWRITE_TAC[GSYM BITVAL_AND] THEN + REWRITE_TAC[GSYM CONJ_ASSOC; bitval] THEN + ONCE_REWRITE_TAC[MESON[] + `(if p /\ q then 1 else 0) = if p then (if q then 1 else 0) else 0`] THEN + SIMP_TAC[NSUM_DELTA; IN_NUMSEG; LE_0] THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[ODD] THEN + ONCE_REWRITE_TAC[COND_RAND] THEN + REWRITE_TAC[ARITH_ODD; TAUT `(if x then T else F) <=> x`] THEN + MATCH_MP_TAC(TAUT `p ==> (p /\ q <=> q)`) THEN ASM_ARITH_TAC);; + (* ------------------------------------------------------------------------- *) (* Alignment. The definition rolls in the assumption that the value is a *) (* power of 2 no more than the wordsize, which seems intuitively natural. *) @@ -7085,6 +7243,33 @@ let WORD_BIT_CONV = conva) tm | _ -> failwith "WORD_BIT_CONV";; +let BIT_WORD_PMUL_CONV = + let main_conv = + RAND_CONV + (EXPAND_NSUM_CONV THENC + DEPTH_BINOP_CONV `(+):num->num->num` + (BINOP2_CONV + (RAND_CONV WORD_BIT_CONV THENC GEN_REWRITE_CONV I [BITVAL_CLAUSES]) + (RAND_CONV(LAND_CONV NUM_SUB_CONV THENC WORD_BIT_CONV) THENC + GEN_REWRITE_CONV I [BITVAL_CLAUSES]) THENC + NUM_MULT_CONV) THENC + DEPTH_CONV NUM_ADD_CONV) THENC + NUM_ODD_CONV in + let conv = + GEN_REWRITE_CONV I [BIT_WORD_PMUL] THENC + LAND_CONV(RAND_CONV DIMINDEX_CONV THENC NUM_LT_CONV) THENC + ((GEN_REWRITE_CONV I [TAUT `T /\ p <=> p`] THENC main_conv) ORELSEC + GEN_REWRITE_CONV I [TAUT `F /\ p <=> F`]) in + fun tm -> + match tm with + Comb(Comb(Const("bit",_),i), + Comb(Comb(Const("word_pmul",_), + Comb(Const("word",_),m)), + Comb(Const("word",_),n))) + when is_numeral i && is_numeral m && is_numeral n -> conv tm + | _ -> failwith "BIT_WORD_PMUL_CONV";; + + let WORD_EQ_CONV = let pth = prove (`word(NUMERAL m):N word = (word(NUMERAL n):N word) <=> @@ -8063,6 +8248,30 @@ let WORD_REVERSEFIELDS_CONV = RAND_CONV EXPAND_NSUM_CONV THENC DEPTH_CONV(GEN_REWRITE_CONV I [BITVAL_CLAUSES] ORELSEC NUM_RED_CONV);; +let WORD_PMUL_CONV = + let in_conv = + GEN_REWRITE_CONV I [GSYM(CONJUNCT2 WORD_PMUL_ZX)] THENC + RAND_CONV WORD_ZX_CONV + and base_conv = + GEN_REWRITE_CONV I [CONJUNCT2 WORD_PMUL_0] + and step_conv = + GEN_REWRITE_CONV I [WORD_PMUL_STEP] THENC + BINOP2_CONV + (RATOR_CONV(LAND_CONV BIT_WORD_CONV) THENC + GEN_REWRITE_CONV I [COND_CLAUSES]) + (BINOP2_CONV (WORD_USHR_CONV) WORD_SHL_CONV) in + let rec conv tm = + try base_conv tm with Failure _ -> + (step_conv THENC RAND_CONV conv THENC WORD_XOR_CONV) tm in + let fullconv = in_conv THENC conv in + fun tm -> + match tm with + Comb(Comb(Const("word_pmul",_), + Comb(Const("word",_),m)), + Comb(Const("word",_),n)) + when is_numeral m && is_numeral n -> fullconv tm + | _ -> failwith "WORD_PMUL_CONV";; + let WORD_JSHL_CONV = let pth = prove (`word_jshl (word(NUMERAL m):N word) (word(NUMERAL n):N word) = @@ -8197,6 +8406,8 @@ let word_red_conv_list = WORD_BYTEREVERSE_CONV; `word_reversefields (NUMERAL b) (word(NUMERAL n):N word)`, WORD_REVERSEFIELDS_CONV; + `(word_pmul:M word->N word->P word) (word(NUMERAL m)) (word(NUMERAL n))`, + WORD_PMUL_CONV; `word_jshl (word(NUMERAL m):N word) (word(NUMERAL n))`,WORD_JSHL_CONV; `word_jshr (word(NUMERAL m):N word) (word(NUMERAL n))`,WORD_JSHR_CONV; `word_jushr (word(NUMERAL m):N word) (word(NUMERAL n))`,WORD_JUSHR_CONV; diff --git a/Multivariate/cauchy.ml b/Multivariate/cauchy.ml index 71b63e50..e29cdeeb 100644 --- a/Multivariate/cauchy.ml +++ b/Multivariate/cauchy.ml @@ -20396,6 +20396,455 @@ let WINDING_NUMBER_AS_CONTINUOUS_LOGARITHM = prove REWRITE_TAC[COMPLEX_SUB_0] THEN ASM_MESON_TAC[pathstart; PATHSTART_IN_PATH_IMAGE]]);; +(* ------------------------------------------------------------------------- *) +(* Winding number integral for absolutely continuous closed curves. *) +(* Incomparable with HAS_PATH_INTEGRAL_WINDING_NUMBER (which uses *) +(* valid_path = piecewise differentiable): valid_path allows unbounded *) +(* derivatives so need not be AC/rectifiable; AC paths are differentiable *) +(* a.e. but the exceptional null set need not be finite. *) +(* Proof: construct indefinite integral ef of 1/(g-z)*gd, show *) +(* G(t) = cexp(-ef(t))*(g(t)-z) has zero a.e. derivative via product rule, *) +(* hence G is constant by AC FTC. Then cexp(q-ef) is constant, and *) +(* q-ef = q(0) by discrete range argument (CEXP_EQ). Finally ef(1) = 2pi*wn. *) +(* ------------------------------------------------------------------------- *) + +let HAS_PATH_INTEGRAL_WINDING_NUMBER_AC = prove + (`!g:real^1->complex z. + g absolutely_continuous_on interval[vec 0,vec 1] /\ + pathfinish g = pathstart g /\ + ~(z IN path_image g) + ==> ((\w. Cx(&1) / (w - z)) has_path_integral + (Cx(&2) * Cx(pi) * ii * winding_number(g,z))) g`, + let vec_deriv_cexp_compose = prove + (`!f:real^1->complex v t s. + (f has_vector_derivative v) (at t within s) + ==> ((\x. cexp(f x)) has_vector_derivative (cexp(f t) * v)) + (at t within s)`, + REPEAT STRIP_TAC THEN REWRITE_TAC[has_vector_derivative] THEN + SUBGOAL_THEN + `(\h:real^1. drop h % (cexp((f:real^1->complex) t) * v:complex)) = + (\h. cexp(f t) * (drop h % v))` + SUBST1_TAC THENL + [REWRITE_TAC[FUN_EQ_THM; COMPLEX_CMUL] THEN + GEN_TAC THEN SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN + `(\x:real^1. cexp((f:real^1->complex) x)) = cexp o f` + SUBST1_TAC THENL [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\h:real^1. cexp((f:real^1->complex) t) * (drop h % v:complex)) = + ((\w:complex. cexp(f t) * w) o (\h:real^1. drop h % v))` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF] THEN REFL_TAC; + ALL_TAC] THEN + MATCH_MP_TAC DIFF_CHAIN_WITHIN THEN CONJ_TAC THENL + [ASM_REWRITE_TAC[GSYM has_vector_derivative]; ALL_TAC] THEN + MATCH_MP_TAC HAS_DERIVATIVE_WITHIN_SUBSET THEN + EXISTS_TAC `(:complex)` THEN REWRITE_TAC[SUBSET_UNIV] THEN + REWRITE_TAC[WITHIN_UNIV] THEN + MP_TAC(SPEC `(f:real^1->complex) t` HAS_COMPLEX_DERIVATIVE_CEXP) THEN + REWRITE_TAC[has_complex_derivative]) in + REPEAT STRIP_TAC THEN + SUBGOAL_THEN `path(g:real^1->complex)` ASSUME_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH]; ALL_TAC] THEN + SUBGOAL_THEN + `g:real^1->complex has_bounded_variation_on interval[vec 0,vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]; ALL_TAC] THEN + FIRST_ASSUM(MP_TAC o MATCH_MP + ABSOLUTELY_INTEGRABLE_BOUNDED_VARIATION_DERIVATIVE) THEN + REWRITE_TAC[LEFT_IMP_EXISTS_THM] THEN + MAP_EVERY X_GEN_TAC [`gd:real^1->complex`; `s1:real^1->bool`] THEN + STRIP_TAC THEN + MP_TAC(ISPECL [`g:real^1->complex`; `z:complex`] + WINDING_NUMBER_AS_CONTINUOUS_LOGARITHM) THEN + ASM_REWRITE_TAC[] THEN REWRITE_TAC[LEFT_IMP_EXISTS_THM] THEN + X_GEN_TAC `q:real^1->complex` THEN STRIP_TAC THEN + SUBGOAL_THEN + `(\t:real^1. inv((g:real^1->complex) t - z)) + continuous_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_COMPLEX_INV THEN CONJ_TAC THENL + [MATCH_MP_TAC CONTINUOUS_ON_SUB THEN + REWRITE_TAC[CONTINUOUS_ON_CONST] THEN ASM_MESON_TAC[path]; + REWRITE_TAC[COMPLEX_SUB_0] THEN + X_GEN_TAC `t:real^1` THEN DISCH_TAC THEN DISCH_TAC THEN + UNDISCH_TAC `~(z IN path_image(g:real^1->complex))` THEN + REWRITE_TAC[path_image; IN_IMAGE] THEN + ASM_MESON_TAC[]]; ALL_TAC] THEN + SUBGOAL_THEN + `bounded(IMAGE (\t:real^1. inv((g:real^1->complex) t - z)) + (interval[vec 0, vec 1]))` + ASSUME_TAC THENL + [MATCH_MP_TAC COMPACT_IMP_BOUNDED THEN + MATCH_MP_TAC COMPACT_CONTINUOUS_IMAGE THEN + ASM_REWRITE_TAC[COMPACT_INTERVAL]; ALL_TAC] THEN + SUBGOAL_THEN + `(\t:real^1. inv((g:real^1->complex) t - z) * (gd:real^1->complex) t) + absolutely_integrable_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC(ISPEC `( * ):complex->complex->complex` + ABSOLUTELY_INTEGRABLE_BOUNDED_MEASURABLE_PRODUCT) THEN + ASM_REWRITE_TAC[BILINEAR_COMPLEX_MUL] THEN + MATCH_MP_TAC CONTINUOUS_IMP_MEASURABLE_ON_CLOSED_SUBSET THEN + ASM_REWRITE_TAC[CLOSED_INTERVAL]; ALL_TAC] THEN + ABBREV_TAC `ef = \t:real^1. integral(interval[vec 0,t]) + (\s:real^1. inv((g:real^1->complex) s - z) * + (gd:real^1->complex) s)` THEN + SUBGOAL_THEN + `(ef:real^1->complex) absolutely_continuous_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [EXPAND_TAC "ef" THEN + MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_RIGHT THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN + `?k1:real^1->bool. negligible k1 /\ + !t. t IN interval[vec 0:real^1, vec 1] DIFF k1 + ==> ((ef:real^1->complex) has_vector_derivative + inv((g:real^1->complex) t - z) * (gd:real^1->complex) t) + (at t within interval[vec 0, vec 1])` + STRIP_ASSUME_TAC THENL + [MP_TAC(ISPECL + [`\s:real^1. inv((g:real^1->complex) s - z) * + (gd:real^1->complex) s`; + `vec 0:real^1`; `vec 1:real^1`] + HAS_VECTOR_DERIVATIVE_INDEFINITE_INTEGRAL) THEN + ANTS_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + ASM_REWRITE_TAC[]; ALL_TAC] THEN + MATCH_MP_TAC MONO_EXISTS THEN X_GEN_TAC `k1:real^1->bool` THEN + MATCH_MP_TAC MONO_AND THEN REWRITE_TAC[] THEN + MATCH_MP_TAC MONO_FORALL THEN X_GEN_TAC `t:real^1` THEN + MATCH_MP_TAC(TAUT `(q ==> r) ==> (p ==> q) ==> (p ==> r)`) THEN + DISCH_TAC THEN + SUBGOAL_THEN `(ef:real^1->complex) = (\t:real^1. integral(interval[vec 0,t]) + (\s:real^1. inv((g:real^1->complex) s - z) * (gd:real^1->complex) s))` + SUBST1_TAC THENL + [EXPAND_TAC "ef" THEN REFL_TAC; ASM_REWRITE_TAC[]]; + ALL_TAC] THEN + SUBGOAL_THEN `(ef:real^1->complex) continuous_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_ON_IMP_CONTINUOUS; + IS_INTERVAL_INTERVAL]; ALL_TAC] THEN + SUBGOAL_THEN + `?B. !t. t IN interval[vec 0:real^1, vec 1] + ==> norm((ef:real^1->complex) t) <= B` + STRIP_ASSUME_TAC THENL + [SUBGOAL_THEN + `bounded(IMAGE (ef:real^1->complex) (interval[vec 0:real^1, vec 1]))` + MP_TAC THENL + [MATCH_MP_TAC COMPACT_IMP_BOUNDED THEN + MATCH_MP_TAC COMPACT_CONTINUOUS_IMAGE THEN + ASM_REWRITE_TAC[COMPACT_INTERVAL]; ALL_TAC] THEN + REWRITE_TAC[BOUNDED_POS; FORALL_IN_IMAGE] THEN MESON_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\t:real^1. cexp(--((ef:real^1->complex) t))) + absolutely_continuous_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MP_TAC(ISPECL + [`\w:complex. cexp(--w)`; + `ef:real^1->complex`; + `interval[vec 0:real^1, vec 1]`; + `exp(B:real)`] + ABSOLUTELY_CONTINUOUS_LIPSCHITZ_COMPOSE) THEN + REWRITE_TAC[o_DEF] THEN + DISCH_THEN MATCH_MP_TAC THEN CONJ_TAC THENL + [ASM_REWRITE_TAC[]; ALL_TAC] THEN + REWRITE_TAC[FORALL_IN_IMAGE_2] THEN + MAP_EVERY X_GEN_TAC [`u:real^1`; `v:real^1`] THEN STRIP_TAC THEN + MATCH_MP_TAC REAL_LE_TRANS THEN + EXISTS_TAC `exp(B) * + norm(--((ef:real^1->complex) u) - --((ef:real^1->complex) v))` THEN + CONJ_TAC THENL + [MATCH_MP_TAC CEXP_LIPSCHITZ_BOUNDED THEN CONJ_TAC THEN + REWRITE_TAC[NORM_NEG] THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]; + MATCH_MP_TAC REAL_LE_LMUL THEN + REWRITE_TAC[REAL_EXP_POS_LE] THEN NORM_ARITH_TAC]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\t:real^1. (g:real^1->complex) t - z) + absolutely_continuous_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_SUB THEN + ASM_REWRITE_TAC[ABSOLUTELY_CONTINUOUS_ON_CONST]; ALL_TAC] THEN + SUBGOAL_THEN + `(\t:real^1. cexp(--((ef:real^1->complex) t)) * + ((g:real^1->complex) t - z)) + absolutely_continuous_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MP_TAC(ISPECL + [`( * ):complex->complex->complex`; + `\t:real^1. cexp(--((ef:real^1->complex) t))`; + `\t:real^1. (g:real^1->complex) t - z`; + `interval[vec 0:real^1, vec 1]`] + ABSOLUTELY_CONTINUOUS_ON_BILINEAR) THEN + ASM_REWRITE_TAC[BILINEAR_COMPLEX_MUL] THEN + DISCH_THEN MATCH_MP_TAC THEN + ASM_REWRITE_TAC[IS_INTERVAL_INTERVAL; BOUNDED_INTERVAL]; ALL_TAC] THEN + SUBGOAL_THEN `(ef:real^1->complex)(vec 0) = Cx(&0)` ASSUME_TAC THENL + [EXPAND_TAC "ef" THEN CONV_TAC(DEPTH_CONV BETA_CONV) THEN + REWRITE_TAC[INTEGRAL_REFL; COMPLEX_VEC_0]; ALL_TAC] THEN + SUBGOAL_THEN + `!t. t IN interval[vec 0:real^1, vec 1] + ==> ~((g:real^1->complex) t - z = Cx(&0))` + ASSUME_TAC THENL + [GEN_TAC THEN DISCH_TAC THEN + REWRITE_TAC[COMPLEX_SUB_0] THEN DISCH_TAC THEN + UNDISCH_TAC `~(z IN path_image(g:real^1->complex))` THEN + REWRITE_TAC[path_image; IN_IMAGE] THEN ASM_MESON_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `!t. t IN interval[vec 0:real^1, vec 1] DIFF (s1 UNION k1) + ==> ((\t. cexp(--((ef:real^1->complex) t)) * + ((g:real^1->complex) t - z)) + has_vector_derivative (vec 0:complex)) + (at t within interval[vec 0, vec 1])` + ASSUME_TAC THENL + [X_GEN_TAC `t':real^1` THEN REWRITE_TAC[IN_DIFF; IN_UNION] THEN + REWRITE_TAC[DE_MORGAN_THM] THEN STRIP_TAC THEN + REWRITE_TAC[COMPLEX_VEC_0] THEN + SUBGOAL_THEN + `Cx(&0) = cexp(--((ef:real^1->complex) t')) * + (gd:real^1->complex) t' + + (cexp(--((ef:real^1->complex) t')) * + (--(inv((g:real^1->complex) t' - z) * + (gd:real^1->complex) t'))) * + ((g:real^1->complex) t' - z)` + SUBST1_TAC THENL + [FIRST_X_ASSUM(MP_TAC o SPEC `t':real^1`) THEN + ANTS_TAC THENL [ASM_REWRITE_TAC[IN_DIFF]; ALL_TAC] THEN + DISCH_TAC THEN MATCH_MP_TAC(COMPLEX_FIELD + `~(w = Cx(&0)) ==> Cx(&0) = a * b + (a * --(inv(w) * b)) * w`) THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + MATCH_MP_TAC(ISPEC `( * ):complex->complex->complex` + HAS_VECTOR_DERIVATIVE_BILINEAR_WITHIN) THEN + REWRITE_TAC[BILINEAR_COMPLEX_MUL] THEN CONJ_TAC THENL + [MATCH_MP_TAC vec_deriv_cexp_compose THEN + MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_NEG THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_DIFF]; + SUBGOAL_THEN `(gd:real^1->complex) t' = gd t' - (vec 0:complex)` + SUBST1_TAC THENL [VECTOR_ARITH_TAC; ALL_TAC] THEN + MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_SUB THEN CONJ_TAC THENL + [MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_AT_WITHIN THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_DIFF]; + REWRITE_TAC[HAS_VECTOR_DERIVATIVE_CONST]]]; + ALL_TAC] THEN + SUBGOAL_THEN + `!t. t IN interval[vec 0:real^1, vec 1] + ==> cexp(--((ef:real^1->complex) t)) * ((g:real^1->complex) t - z) = + cexp(--(ef(vec 0))) * (g(vec 0) - z)` + ASSUME_TAC THENL + [X_GEN_TAC `t':real^1` THEN DISCH_TAC THEN + MP_TAC(ISPECL + [`\t:real^1. cexp(--((ef:real^1->complex) t)) * + ((g:real^1->complex) t - z)`; + `\t:real^1. (vec 0:complex)`; + `s1 UNION k1:real^1->bool`; + `vec 0:real^1`; `t':real^1`] + FUNDAMENTAL_THEOREM_OF_CALCULUS_ABSOLUTELY_CONTINUOUS) THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ANTS_TAC THENL + [REPEAT CONJ_TAC THENL + [ASM_REWRITE_TAC[NEGLIGIBLE_UNION_EQ]; + UNDISCH_TAC `t' IN interval[vec 0:real^1, vec 1]` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN REAL_ARITH_TAC; + MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_SUBSET THEN + EXISTS_TAC `interval[vec 0:real^1, vec 1]` THEN ASM_REWRITE_TAC[] THEN + REWRITE_TAC[SUBSET_INTERVAL_1] THEN + UNDISCH_TAC `t' IN interval[vec 0:real^1, vec 1]` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN REAL_ARITH_TAC; + X_GEN_TAC `u:real^1` THEN DISCH_TAC THEN + MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_WITHIN_SUBSET THEN + EXISTS_TAC `interval[vec 0:real^1, vec 1]` THEN CONJ_TAC THENL + [FIRST_X_ASSUM MATCH_MP_TAC THEN + REWRITE_TAC[IN_DIFF] THEN CONJ_TAC THENL + [UNDISCH_TAC `u IN interval[vec 0:real^1,t'] DIFF (s1 UNION k1)` THEN + UNDISCH_TAC `t' IN interval[vec 0:real^1, vec 1]` THEN + REWRITE_TAC[IN_DIFF; IN_INTERVAL_1; DROP_VEC] THEN REAL_ARITH_TAC; + UNDISCH_TAC `u IN interval[vec 0:real^1,t'] DIFF (s1 UNION k1)` THEN + REWRITE_TAC[IN_DIFF] THEN MESON_TAC[]]; + REWRITE_TAC[SUBSET_INTERVAL_1] THEN + UNDISCH_TAC `t' IN interval[vec 0:real^1, vec 1]` THEN + REWRITE_TAC[IN_INTERVAL_1; DROP_VEC] THEN REAL_ARITH_TAC]]; + ALL_TAC] THEN + DISCH_THEN(MP_TAC o REWRITE_RULE[HAS_INTEGRAL_0_EQ]) THEN + REWRITE_TAC[VECTOR_SUB_EQ]; + ALL_TAC] THEN + SUBGOAL_THEN + `!t. t IN interval[vec 0:real^1, vec 1] + ==> (g:real^1->complex) t - z = cexp((q:real^1->complex) t)` + ASSUME_TAC THENL + [X_GEN_TAC `t':real^1` THEN DISCH_TAC THEN + SUBGOAL_THEN `(g:real^1->complex) t' = z + cexp((q:real^1->complex) t')` + MP_TAC THENL [ASM_MESON_TAC[]; SIMPLE_COMPLEX_ARITH_TAC]; + ALL_TAC] THEN + SUBGOAL_THEN + `!t. t IN interval[vec 0:real^1, vec 1] + ==> cexp((q:real^1->complex) t - (ef:real^1->complex) t) = + cexp(q(vec 0:real^1))` + ASSUME_TAC THENL + [X_GEN_TAC `t':real^1` THEN DISCH_TAC THEN + SUBGOAL_THEN + `cexp((q:real^1->complex) t' - (ef:real^1->complex) t') = + cexp(--ef t') * cexp(q t')` + SUBST1_TAC THENL + [REWRITE_TAC[GSYM CEXP_ADD] THEN AP_TERM_TAC THEN + SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + SUBGOAL_THEN + `(g:real^1->complex) t' - z = cexp((q:real^1->complex) t')` + (SUBST1_TAC o SYM) THENL [ASM_MESON_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN + `cexp(--(ef:real^1->complex) t') * ((g:real^1->complex) t' - z) = + cexp(--ef(vec 0)) * (g(vec 0) - z)` + SUBST1_TAC THENL [ASM_MESON_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN + `(g:real^1->complex)(vec 0) - z = cexp((q:real^1->complex)(vec 0))` + SUBST1_TAC THENL + [ASM_MESON_TAC[ENDS_IN_UNIT_INTERVAL]; ALL_TAC] THEN + SUBGOAL_THEN `--((ef:real^1->complex)(vec 0)) = Cx(&0)` SUBST1_TAC THENL + [ASM_REWRITE_TAC[] THEN SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[CEXP_0; COMPLEX_MUL_LID]; + ALL_TAC] THEN + SUBGOAL_THEN + `!t. t IN interval[vec 0:real^1, vec 1] + ==> (q:real^1->complex) t - (ef:real^1->complex) t = q(vec 0)` + ASSUME_TAC THENL + [MP_TAC(ISPECL + [`\t:real^1. (q:real^1->complex) t - (ef:real^1->complex) t`; + `interval[vec 0:real^1, vec 1]`] + CONTINUOUS_DISCRETE_RANGE_CONSTANT) THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + ANTS_TAC THENL + [REPEAT CONJ_TAC THENL + [REWRITE_TAC[CONNECTED_INTERVAL]; + MATCH_MP_TAC CONTINUOUS_ON_SUB THEN CONJ_TAC THENL + [UNDISCH_TAC `path(q:real^1->complex)` THEN REWRITE_TAC[path]; + ASM_REWRITE_TAC[]]; + X_GEN_TAC `x':real^1` THEN DISCH_TAC THEN + EXISTS_TAC `&2 * pi` THEN CONJ_TAC THENL + [MP_TAC PI_POS THEN REAL_ARITH_TAC; ALL_TAC] THEN + X_GEN_TAC `y':real^1` THEN STRIP_TAC THEN + SUBGOAL_THEN + `?n. integer n /\ + (q:real^1->complex) y' - (ef:real^1->complex) y' = + (q x' - ef x') + Cx(&2 * n * pi) * ii` + STRIP_ASSUME_TAC THENL + [MP_TAC(ISPECL + [`(q:real^1->complex) y' - (ef:real^1->complex) y'`; + `(q:real^1->complex) x' - (ef:real^1->complex) x'`] + CEXP_EQ) THEN + ASM_SIMP_TAC[] THEN MESON_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN `~(n = &0)` ASSUME_TAC THENL + [DISCH_TAC THEN + UNDISCH_TAC `~((q:real^1->complex) y' - ef y' = q x' - ef x')` THEN + ASM_REWRITE_TAC[REAL_MUL_RZERO; REAL_MUL_LZERO] THEN + REWRITE_TAC[COMPLEX_MUL_LZERO; COMPLEX_ADD_RID]; + ALL_TAC] THEN + SUBGOAL_THEN + `(q:real^1->complex) y' - ef y' - (q x' - ef x') = + Cx(&2 * n * pi) * ii` + SUBST1_TAC THENL + [ASM_REWRITE_TAC[] THEN SIMPLE_COMPLEX_ARITH_TAC; ALL_TAC] THEN + REWRITE_TAC[COMPLEX_NORM_MUL; COMPLEX_NORM_CX; COMPLEX_NORM_II; + REAL_MUL_RID; REAL_ABS_MUL; REAL_ABS_NUM] THEN + SUBGOAL_THEN `abs(pi) = pi` SUBST1_TAC THENL + [MP_TAC PI_POS THEN REAL_ARITH_TAC; ALL_TAC] THEN + MATCH_MP_TAC REAL_LE_LMUL THEN CONJ_TAC THENL + [REAL_ARITH_TAC; + GEN_REWRITE_TAC LAND_CONV [GSYM REAL_MUL_LID] THEN + MATCH_MP_TAC REAL_LE_RMUL THEN CONJ_TAC THENL + [MATCH_MP_TAC REAL_ABS_INTEGER_LEMMA THEN ASM_REWRITE_TAC[]; + MP_TAC PI_POS THEN REAL_ARITH_TAC]]]; + ALL_TAC] THEN + DISCH_THEN(X_CHOOSE_TAC `a:complex`) THEN + X_GEN_TAC `t':real^1` THEN DISCH_TAC THEN + SUBGOAL_THEN + `(q:real^1->complex) t' - (ef:real^1->complex) t' = a:complex` + ASSUME_TAC THENL + [ASM_MESON_TAC[]; ALL_TAC] THEN + SUBGOAL_THEN `a:complex = (q:real^1->complex)(vec 0)` ASSUME_TAC THENL + [SUBGOAL_THEN + `(q:real^1->complex)(vec 0) - (ef:real^1->complex)(vec 0) = a` + MP_TAC THENL + [ASM_MESON_TAC[ENDS_IN_UNIT_INTERVAL]; ALL_TAC] THEN + ASM_REWRITE_TAC[] THEN SIMPLE_COMPLEX_ARITH_TAC; + ALL_TAC] THEN + ASM_MESON_TAC[]; + ALL_TAC] THEN + SUBGOAL_THEN + `(ef:real^1->complex)(vec 1) = + Cx(&2) * Cx(pi) * ii * winding_number(g:real^1->complex,z)` + ASSUME_TAC THENL + [SUBGOAL_THEN + `(q:real^1->complex)(vec 1) - (ef:real^1->complex)(vec 1) = q(vec 0)` + MP_TAC THENL + [ASM_MESON_TAC[ENDS_IN_UNIT_INTERVAL]; ALL_TAC] THEN + SUBGOAL_THEN + `(q:real^1->complex)(vec 1) - q(vec 0) = + Cx(&2) * Cx(pi) * ii * winding_number(g:real^1->complex,z)` + MP_TAC THENL + [UNDISCH_TAC + `pathfinish(q:real^1->complex) - pathstart q = + Cx(&2) * Cx(pi) * ii * winding_number(g:real^1->complex,z)` THEN + REWRITE_TAC[pathfinish; pathstart]; ALL_TAC] THEN + SIMPLE_COMPLEX_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `!x. x IN interval[vec 0:real^1, vec 1] DIFF s1 + ==> vector_derivative (g:real^1->complex) + (at x within interval[vec 0, vec 1]) = + (gd:real^1->complex) x` + ASSUME_TAC THENL + [X_GEN_TAC `u:real^1` THEN REWRITE_TAC[IN_DIFF] THEN STRIP_TAC THEN + MATCH_MP_TAC VECTOR_DERIVATIVE_WITHIN_CLOSED_INTERVAL THEN + REWRITE_TAC[DROP_VEC] THEN CONV_TAC REAL_RAT_REDUCE_CONV THEN + ASM_REWRITE_TAC[] THEN + MATCH_MP_TAC HAS_VECTOR_DERIVATIVE_AT_WITHIN THEN + ASM_MESON_TAC[IN_DIFF]; + ALL_TAC] THEN + SUBGOAL_THEN + `((\s:real^1. inv((g:real^1->complex) s - z) * (gd:real^1->complex) s) + has_integral + (Cx(&2) * Cx(pi) * ii * winding_number(g:real^1->complex,z))) + (interval[vec 0, vec 1])` + ASSUME_TAC THENL + [SUBGOAL_THEN + `Cx(&2) * Cx(pi) * ii * winding_number(g:real^1->complex,z) = + integral(interval[vec 0:real^1, vec 1]) + (\s:real^1. inv((g:real^1->complex) s - z) * (gd:real^1->complex) s)` + SUBST1_TAC THENL + [SUBGOAL_THEN + `integral(interval[vec 0:real^1, vec 1]) + (\s:real^1. inv((g:real^1->complex) s - z) * + (gd:real^1->complex) s) = + (ef:real^1->complex)(vec 1)` + SUBST1_TAC THENL + [EXPAND_TAC "ef" THEN CONV_TAC(DEPTH_CONV BETA_CONV) THEN REFL_TAC; + ALL_TAC] THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + MATCH_MP_TAC INTEGRABLE_INTEGRAL THEN + MATCH_MP_TAC ABSOLUTELY_INTEGRABLE_IMP_INTEGRABLE THEN + ASM_REWRITE_TAC[]; + ALL_TAC] THEN + REWRITE_TAC[has_path_integral] THEN + REWRITE_TAC[complex_div; COMPLEX_MUL_LID] THEN + MATCH_MP_TAC HAS_INTEGRAL_SPIKE THEN + EXISTS_TAC + `\x:real^1. inv((g:real^1->complex) x - z) * (gd:real^1->complex) x` THEN + EXISTS_TAC `s1:real^1->bool` THEN + CONV_TAC(DEPTH_CONV BETA_CONV) THEN + CONJ_TAC THENL [ASM_REWRITE_TAC[]; ALL_TAC] THEN + CONJ_TAC THENL + [X_GEN_TAC `u:real^1` THEN REWRITE_TAC[IN_DIFF] THEN STRIP_TAC THEN + AP_TERM_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN + ASM_REWRITE_TAC[IN_DIFF]; + ASM_REWRITE_TAC[]]);; + (* ------------------------------------------------------------------------- *) (* Winding number equality is the same as path/loop homotopy in C - {0}. *) (* ------------------------------------------------------------------------- *) diff --git a/Multivariate/complex_database.ml b/Multivariate/complex_database.ml index 8eb6462e..720fa70d 100644 --- a/Multivariate/complex_database.ml +++ b/Multivariate/complex_database.ml @@ -68,12 +68,15 @@ theorems := "ABSOLUTELY_CONTINUOUS_DIFFERENTIABLE_BV_GEN",ABSOLUTELY_CONTINUOUS_DIFFERENTIABLE_BV_GEN; "ABSOLUTELY_CONTINUOUS_EXTENDS_TO_CLOSURE",ABSOLUTELY_CONTINUOUS_EXTENDS_TO_CLOSURE; "ABSOLUTELY_CONTINUOUS_IMP_BANACH_SPROPERTY",ABSOLUTELY_CONTINUOUS_IMP_BANACH_SPROPERTY; +"ABSOLUTELY_CONTINUOUS_IMP_PATH",ABSOLUTELY_CONTINUOUS_IMP_PATH; +"ABSOLUTELY_CONTINUOUS_IMP_RECTIFIABLE_PATH",ABSOLUTELY_CONTINUOUS_IMP_RECTIFIABLE_PATH; "ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_EQ",ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_EQ; "ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_LEFT",ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_LEFT; "ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_RIGHT",ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_RIGHT; "ABSOLUTELY_CONTINUOUS_INTEGRAL",ABSOLUTELY_CONTINUOUS_INTEGRAL; "ABSOLUTELY_CONTINUOUS_ISOMETRIC",ABSOLUTELY_CONTINUOUS_ISOMETRIC; "ABSOLUTELY_CONTINUOUS_ISOMETRIC_COMPOSE",ABSOLUTELY_CONTINUOUS_ISOMETRIC_COMPOSE; +"ABSOLUTELY_CONTINUOUS_JOINPATHS",ABSOLUTELY_CONTINUOUS_JOINPATHS; "ABSOLUTELY_CONTINUOUS_LIPSCHITZ_COMPOSE",ABSOLUTELY_CONTINUOUS_LIPSCHITZ_COMPOSE; "ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE",ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE; "ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE_GEN",ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE_GEN; @@ -114,6 +117,7 @@ theorems := "ABSOLUTELY_CONTINUOUS_ON_VMUL_EQ",ABSOLUTELY_CONTINUOUS_ON_VMUL_EQ; "ABSOLUTELY_CONTINUOUS_ON_VSUM",ABSOLUTELY_CONTINUOUS_ON_VSUM; "ABSOLUTELY_CONTINUOUS_RECTIFIABLE_VALID_PATH",ABSOLUTELY_CONTINUOUS_RECTIFIABLE_VALID_PATH; +"ABSOLUTELY_CONTINUOUS_REVERSEPATH",ABSOLUTELY_CONTINUOUS_REVERSEPATH; "ABSOLUTELY_CONTINUOUS_VECTOR_VARIATION",ABSOLUTELY_CONTINUOUS_VECTOR_VARIATION; "ABSOLUTELY_INTEGRABLE_0",ABSOLUTELY_INTEGRABLE_0; "ABSOLUTELY_INTEGRABLE_ABS",ABSOLUTELY_INTEGRABLE_ABS; @@ -211,6 +215,7 @@ theorems := "ABSOLUTELY_INTEGRABLE_TRANSLATION",ABSOLUTELY_INTEGRABLE_TRANSLATION; "ABSOLUTELY_INTEGRABLE_TWIZZLE_EQ",ABSOLUTELY_INTEGRABLE_TWIZZLE_EQ; "ABSOLUTELY_INTEGRABLE_UNION",ABSOLUTELY_INTEGRABLE_UNION; +"ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS",ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS; "ABSOLUTELY_INTEGRABLE_VSUM",ABSOLUTELY_INTEGRABLE_VSUM; "ABSOLUTELY_REAL_INTEGRABLE_0",ABSOLUTELY_REAL_INTEGRABLE_0; "ABSOLUTELY_REAL_INTEGRABLE_ABS",ABSOLUTELY_REAL_INTEGRABLE_ABS; @@ -2022,6 +2027,7 @@ theorems := "CEXP_II_PI",CEXP_II_PI; "CEXP_INTEGER_2PI",CEXP_INTEGER_2PI; "CEXP_LIMIT",CEXP_LIMIT; +"CEXP_LIPSCHITZ_BOUNDED",CEXP_LIPSCHITZ_BOUNDED; "CEXP_MUL_CPOW",CEXP_MUL_CPOW; "CEXP_N",CEXP_N; "CEXP_NEG",CEXP_NEG; @@ -8317,6 +8323,7 @@ theorems := "HAS_PATH_INTEGRAL_UNIQUE",HAS_PATH_INTEGRAL_UNIQUE; "HAS_PATH_INTEGRAL_VSUM",HAS_PATH_INTEGRAL_VSUM; "HAS_PATH_INTEGRAL_WINDING_NUMBER",HAS_PATH_INTEGRAL_WINDING_NUMBER; +"HAS_PATH_INTEGRAL_WINDING_NUMBER_AC",HAS_PATH_INTEGRAL_WINDING_NUMBER_AC; "HAS_REAL_COMPLEX_DERIVATIVE_AT",HAS_REAL_COMPLEX_DERIVATIVE_AT; "HAS_REAL_COMPLEX_DERIVATIVE_WITHIN",HAS_REAL_COMPLEX_DERIVATIVE_WITHIN; "HAS_REAL_DERIVATIVE_ACS",HAS_REAL_DERIVATIVE_ACS; @@ -11305,6 +11312,7 @@ theorems := "LEBESGUE_MEASURABLE_PREIMAGE_OPEN",LEBESGUE_MEASURABLE_PREIMAGE_OPEN; "LEBESGUE_MEASURABLE_REGULAR_INNER",LEBESGUE_MEASURABLE_REGULAR_INNER; "LEBESGUE_MEASURABLE_REGULAR_OUTER",LEBESGUE_MEASURABLE_REGULAR_OUTER; +"LEBESGUE_MEASURABLE_SING",LEBESGUE_MEASURABLE_SING; "LEBESGUE_MEASURABLE_SMALL_IMP_NEGLIGIBLE",LEBESGUE_MEASURABLE_SMALL_IMP_NEGLIGIBLE; "LEBESGUE_MEASURABLE_TRANSLATION",LEBESGUE_MEASURABLE_TRANSLATION; "LEBESGUE_MEASURABLE_UNION",LEBESGUE_MEASURABLE_UNION; @@ -12842,6 +12850,7 @@ theorems := "MEASURABLE_ON_BANACH_INDICATRIX",MEASURABLE_ON_BANACH_INDICATRIX; "MEASURABLE_ON_BILINEAR",MEASURABLE_ON_BILINEAR; "MEASURABLE_ON_CASES",MEASURABLE_ON_CASES; +"MEASURABLE_ON_CLOG",MEASURABLE_ON_CLOG; "MEASURABLE_ON_CMUL",MEASURABLE_ON_CMUL; "MEASURABLE_ON_CMUL_EQ",MEASURABLE_ON_CMUL_EQ; "MEASURABLE_ON_COMBINE",MEASURABLE_ON_COMBINE; @@ -12866,6 +12875,7 @@ theorems := "MEASURABLE_ON_CONTINUOUS_COMPOSE_REV",MEASURABLE_ON_CONTINUOUS_COMPOSE_REV; "MEASURABLE_ON_CONVOLUTION",MEASURABLE_ON_CONVOLUTION; "MEASURABLE_ON_COUNTABLE_UNIONS",MEASURABLE_ON_COUNTABLE_UNIONS; +"MEASURABLE_ON_CPOW",MEASURABLE_ON_CPOW; "MEASURABLE_ON_CPRODUCT",MEASURABLE_ON_CPRODUCT; "MEASURABLE_ON_DET_JACOBIAN",MEASURABLE_ON_DET_JACOBIAN; "MEASURABLE_ON_DIFF",MEASURABLE_ON_DIFF; @@ -13441,6 +13451,7 @@ theorems := "NEGLIGIBLE_OUTER_LE",NEGLIGIBLE_OUTER_LE; "NEGLIGIBLE_PCROSS",NEGLIGIBLE_PCROSS; "NEGLIGIBLE_POINTS_OF_AMBIGUOUS_DERIVATIVE",NEGLIGIBLE_POINTS_OF_AMBIGUOUS_DERIVATIVE; +"NEGLIGIBLE_REAL",NEGLIGIBLE_REAL; "NEGLIGIBLE_RECTIFIABLE_PATH_IMAGE",NEGLIGIBLE_RECTIFIABLE_PATH_IMAGE; "NEGLIGIBLE_SCALING",NEGLIGIBLE_SCALING; "NEGLIGIBLE_SCALING_EQ",NEGLIGIBLE_SCALING_EQ; diff --git a/Multivariate/lpspaces.ml b/Multivariate/lpspaces.ml index fb8d379d..e394ab7e 100644 --- a/Multivariate/lpspaces.ml +++ b/Multivariate/lpspaces.ml @@ -13,6 +13,14 @@ let lspace = new_definition {f:real^M->real^N | f measurable_on s /\ (\x. lift(norm(f x) rpow p)) integrable_on s}`;; +let LSPACE_ALT = prove + (`lspace s p = + {f | (f:real^M->real^N) measurable_on s /\ + (\x. lift(norm(f x) rpow p)) absolutely_integrable_on s}`, + REWRITE_TAC[lspace] THEN + SIMP_TAC[ABSOLUTELY_INTEGRABLE_EQ_INTEGRABLE_POS; + LIFT_DROP; RPOW_POS_LE; NORM_POS_LE]);; + let LSPACE_ZERO = prove (`!s. lspace s (&0) = if measurable s then {f:real^M->real^N | f measurable_on s} else {}`, @@ -278,6 +286,14 @@ let LSPACE_INCLUSION = prove MATCH_MP_TAC LSPACE_MONO THEN EXISTS_TAC `q:real` THEN ASM_REWRITE_TAC[]);; +let LSPACE_SUBSET = prove + (`!(f:real^M->real^N) s t p. + f IN lspace t p /\ lebesgue_measurable s /\ s SUBSET t + ==> f IN lspace s p`, + REWRITE_TAC[LSPACE_ALT; IN_ELIM_THM] THEN REPEAT STRIP_TAC THEN + ASM_MESON_TAC[MEASURABLE_ON_LEBESGUE_MEASURABLE_SUBSET; + ABSOLUTELY_INTEGRABLE_ON_LEBESGUE_MEASURABLE_SUBSET]);; + (* ------------------------------------------------------------------------- *) (* The corresponding seminorm; Hoelder and Minkowski inequalities. *) (* ------------------------------------------------------------------------- *) diff --git a/Multivariate/measure.ml b/Multivariate/measure.ml index 22b28216..d88842b5 100644 --- a/Multivariate/measure.ml +++ b/Multivariate/measure.ml @@ -9648,6 +9648,11 @@ let LEBESGUE_MEASURABLE_DELETE = prove EXISTS_TAC `{a:real^N}` THEN REWRITE_TAC[NEGLIGIBLE_SING] THEN SET_TAC[]);; +let LEBESGUE_MEASURABLE_SING = prove + (`!a:real^N. lebesgue_measurable {a}`, + GEN_TAC THEN MATCH_MP_TAC NEGLIGIBLE_IMP_LEBESGUE_MEASURABLE THEN + REWRITE_TAC[NEGLIGIBLE_SING]);; + let ABSOLUTELY_INTEGRABLE_ON_LEBESGUE_MEASURABLE_INTER = prove (`!f:real^M->real^N s t. f absolutely_integrable_on s /\ lebesgue_measurable t @@ -27600,6 +27605,262 @@ let CONVERGENCE_IN_MEASURE_UNIQUE = prove CONJ_TAC THENL [MATCH_MP_TAC LIM_SUBSEQUENCE; ALL_TAC] THEN ASM_SIMP_TAC[o_DEF]);; +(* ------------------------------------------------------------------------- *) +(* Basic path notions for AC paths; these can be useful for working *) +(* with rectifiable paths where we want to explicitly pick an AC *) +(* parametrization, e.g. by ARC_LENGTH_REPARAMETRIZATION *) +(* ------------------------------------------------------------------------- *) + +let ABSOLUTELY_CONTINUOUS_IMP_PATH = prove + (`!g:real^1->real^N. + g absolutely_continuous_on interval[vec 0,vec 1] ==> path g`, + REWRITE_TAC[path] THEN + MESON_TAC[ABSOLUTELY_CONTINUOUS_ON_IMP_CONTINUOUS; IS_INTERVAL_INTERVAL]);; + +let ABSOLUTELY_CONTINUOUS_IMP_RECTIFIABLE_PATH = prove + (`!g:real^1->real^N. + g absolutely_continuous_on interval[vec 0,vec 1] ==> rectifiable_path g`, + GEN_TAC THEN DISCH_TAC THEN REWRITE_TAC[rectifiable_path] THEN CONJ_TAC THENL + [ASM_MESON_TAC[ABSOLUTELY_CONTINUOUS_IMP_PATH; path]; + MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]]);; + +let ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS = prove + (`!g:real^1->real^N. + g absolutely_continuous_on interval[vec 0,vec 1] + ==> (\t. vector_derivative g (at t)) + absolutely_integrable_on interval[vec 0,vec 1]`, + GEN_TAC THEN DISCH_TAC THEN + SUBGOAL_THEN + `g:real^1->real^N has_bounded_variation_on interval[vec 0,vec 1]` + MP_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]; ALL_TAC] THEN + DISCH_THEN(MP_TAC o MATCH_MP + ABSOLUTELY_INTEGRABLE_BOUNDED_VARIATION_DERIVATIVE) THEN + REWRITE_TAC[LEFT_IMP_EXISTS_THM] THEN + MAP_EVERY X_GEN_TAC [`gd:real^1->real^N`; `s1:real^1->bool`] THEN + STRIP_TAC THEN + REWRITE_TAC[absolutely_integrable_on] THEN CONJ_TAC THENL + [MP_TAC(ISPECL + [`gd:real^1->real^N`; + `\t:real^1. vector_derivative (g:real^1->real^N) (at t)`; + `s1:real^1->bool`; + `interval[vec 0:real^1,vec 1]`] + INTEGRABLE_SPIKE) THEN + CONV_TAC(LAND_CONV(DEPTH_CONV BETA_CONV)) THEN + REWRITE_TAC[IMP_IMP] THEN DISCH_THEN MATCH_MP_TAC THEN + ASM_REWRITE_TAC[] THEN CONJ_TAC THENL + [X_GEN_TAC `t:real^1` THEN REWRITE_TAC[IN_DIFF] THEN STRIP_TAC THEN + MATCH_MP_TAC VECTOR_DERIVATIVE_AT THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_DIFF]; + ASM_MESON_TAC[absolutely_integrable_on]]; + MP_TAC(ISPECL + [`\t:real^1. lift(norm((gd:real^1->real^N) t))`; + `\t:real^1. lift(norm(vector_derivative (g:real^1->real^N) (at t)))`; + `s1:real^1->bool`; + `interval[vec 0:real^1,vec 1]`] + INTEGRABLE_SPIKE) THEN + CONV_TAC(LAND_CONV(DEPTH_CONV BETA_CONV)) THEN + REWRITE_TAC[IMP_IMP] THEN DISCH_THEN MATCH_MP_TAC THEN + ASM_REWRITE_TAC[] THEN CONJ_TAC THENL + [X_GEN_TAC `t:real^1` THEN REWRITE_TAC[IN_DIFF] THEN STRIP_TAC THEN + AP_TERM_TAC THEN AP_TERM_TAC THEN + MATCH_MP_TAC VECTOR_DERIVATIVE_AT THEN + FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[IN_DIFF]; + ASM_MESON_TAC[absolutely_integrable_on]]]);; + +let ABSOLUTELY_CONTINUOUS_REVERSEPATH = prove + (`!g:real^1->real^N. + g absolutely_continuous_on interval[vec 0,vec 1] + ==> (reversepath g) absolutely_continuous_on interval[vec 0,vec 1]`, + GEN_TAC THEN DISCH_TAC THEN + REWRITE_TAC[reversepath] THEN + SUBGOAL_THEN + `(\t:real^1. (g:real^1->real^N)(vec 1 - t)) = + (g:real^1->real^N) o (\t:real^1. vec 1 - t)` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\t:real^1. vec 1 - t) + absolutely_continuous_on interval[vec 0:real^1, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_SUB THEN + REWRITE_TAC[ABSOLUTELY_CONTINUOUS_ON_CONST; ABSOLUTELY_CONTINUOUS_ON_ID]; + ALL_TAC] THEN + MP_TAC(ISPECL + [`g:real^1->real^N`; + `\t:real^1. vec 1 - t`; + `interval[vec 0:real^1, vec 1]`; + `interval[vec 0:real^1, vec 1]`] + ABSOLUTELY_CONTINUOUS_ON_COMPOSE) THEN + REWRITE_TAC[o_DEF] THEN + ASM_REWRITE_TAC[IS_INTERVAL_INTERVAL; BOUNDED_INTERVAL] THEN + ANTS_TAC THENL + [REWRITE_TAC[SUBSET; FORALL_IN_IMAGE; IN_INTERVAL_1; DROP_VEC; DROP_SUB] THEN + REAL_ARITH_TAC; ALL_TAC] THEN + DISCH_THEN SUBST1_TAC THEN + SUBGOAL_THEN + `g:real^1->real^N has_bounded_variation_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]; ALL_TAC] THEN + MP_TAC(ISPECL + [`\t:real^1. vec 1 - t`; + `g:real^1->real^N`; + `vec 0:real^1`; `vec 1:real^1`] + HAS_BOUNDED_VARIATION_COMPOSE_DECREASING) THEN + REWRITE_TAC[o_DEF] THEN + ANTS_TAC THENL + [CONJ_TAC THENL + [REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; DROP_SUB] THEN REAL_ARITH_TAC; + REWRITE_TAC[VECTOR_SUB_RZERO; VECTOR_SUB_REFL] THEN + ASM_REWRITE_TAC[]]; + SIMP_TAC[]]);; + +let ABSOLUTELY_CONTINUOUS_JOINPATHS = prove + (`!g1 g2:real^1->real^N. + pathfinish g1 = pathstart g2 /\ + g1 absolutely_continuous_on interval[vec 0,vec 1] /\ + g2 absolutely_continuous_on interval[vec 0,vec 1] + ==> (g1 ++ g2) absolutely_continuous_on interval[vec 0,vec 1]`, + REPEAT GEN_TAC THEN STRIP_TAC THEN + MP_TAC(ISPECL + [`(g1 ++ g2):real^1->real^N`; + `vec 0:real^1`; `vec 1:real^1`; `lift(&1 / &2)`] + ABSOLUTELY_CONTINUOUS_ON_COMBINE) THEN + REWRITE_TAC[DROP_VEC; LIFT_DROP] THEN + ANTS_TAC THENL [REAL_ARITH_TAC; DISCH_THEN SUBST1_TAC] THEN + CONJ_TAC THENL + [(* First half: (g1 ++ g2) AC on [0, 1/2] *) + MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_EQ THEN + EXISTS_TAC `\x:real^1. (g1:real^1->real^N)(&2 % x)` THEN + CONJ_TAC THENL + [REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; LIFT_DROP; joinpaths] THEN + REPEAT STRIP_TAC THEN + COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN + `(\x:real^1. (g1:real^1->real^N)(&2 % x)) = + g1 o (\x:real^1. &2 % x)` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\x:real^1. &2 % x) + absolutely_continuous_on interval[vec 0, lift(&1 / &2)]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_CMUL THEN + REWRITE_TAC[ABSOLUTELY_CONTINUOUS_ON_ID]; + ALL_TAC] THEN + MP_TAC(ISPECL + [`g1:real^1->real^N`; + `\x:real^1. &2 % x`; + `interval[vec 0:real^1, lift(&1 / &2)]`; + `interval[vec 0:real^1, vec 1]`] + ABSOLUTELY_CONTINUOUS_ON_COMPOSE) THEN + REWRITE_TAC[o_DEF] THEN + ASM_REWRITE_TAC[IS_INTERVAL_INTERVAL; BOUNDED_INTERVAL] THEN + ANTS_TAC THENL + [REWRITE_TAC[SUBSET; FORALL_IN_IMAGE; IN_INTERVAL_1; + DROP_VEC; DROP_CMUL; LIFT_DROP] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + DISCH_THEN SUBST1_TAC THEN + SUBGOAL_THEN + `(g1:real^1->real^N) has_bounded_variation_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]; + ALL_TAC] THEN + MP_TAC(ISPECL + [`\x:real^1. &2 % x`; + `g1:real^1->real^N`; + `vec 0:real^1`; `lift(&1 / &2)`] + HAS_BOUNDED_VARIATION_COMPOSE_INCREASING) THEN + REWRITE_TAC[o_DEF] THEN + ANTS_TAC THENL + [CONJ_TAC THENL + [REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; DROP_CMUL; LIFT_DROP] THEN + REPEAT STRIP_TAC THEN MATCH_MP_TAC REAL_LE_LMUL THEN + ASM_REAL_ARITH_TAC; + REWRITE_TAC[VECTOR_MUL_RZERO; GSYM LIFT_CMUL] THEN + CONV_TAC REAL_RAT_REDUCE_CONV THEN + REWRITE_TAC[LIFT_NUM] THEN ASM_REWRITE_TAC[]]; + SIMP_TAC[]]; + + (* Second half: (g1 ++ g2) AC on [1/2, 1] *) + MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_EQ THEN + EXISTS_TAC `\x:real^1. (g2:real^1->real^N)(&2 % x - vec 1)` THEN + CONJ_TAC THENL + [REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; LIFT_DROP; joinpaths] THEN + REPEAT STRIP_TAC THEN + COND_CASES_TAC THENL + [SUBGOAL_THEN `&2 % x - vec 1 = vec 0:real^1` SUBST1_TAC THENL + [REWRITE_TAC[GSYM DROP_EQ; DROP_CMUL; DROP_SUB; DROP_VEC] THEN + ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + SUBGOAL_THEN `&2 % x = vec 1:real^1` SUBST1_TAC THENL + [REWRITE_TAC[GSYM DROP_EQ; DROP_CMUL; DROP_VEC] THEN + ASM_REAL_ARITH_TAC; + ALL_TAC] THEN + UNDISCH_TAC `pathfinish g1 = pathstart (g2:real^1->real^N)` THEN + REWRITE_TAC[pathfinish; pathstart] THEN MESON_TAC[]; + REFL_TAC]; + ALL_TAC] THEN + SUBGOAL_THEN + `(\x:real^1. (g2:real^1->real^N)(&2 % x - vec 1)) = + g2 o (\x:real^1. &2 % x - vec 1)` + SUBST1_TAC THENL + [REWRITE_TAC[o_DEF]; ALL_TAC] THEN + SUBGOAL_THEN + `(\x:real^1. &2 % x - vec 1) + absolutely_continuous_on interval[lift(&1 / &2), vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_SUB THEN + REWRITE_TAC[ABSOLUTELY_CONTINUOUS_ON_CONST] THEN + MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_CMUL THEN + REWRITE_TAC[ABSOLUTELY_CONTINUOUS_ON_ID]; + ALL_TAC] THEN + MP_TAC(ISPECL + [`g2:real^1->real^N`; + `\x:real^1. &2 % x - vec 1`; + `interval[lift(&1 / &2):real^1, vec 1]`; + `interval[vec 0:real^1, vec 1]`] + ABSOLUTELY_CONTINUOUS_ON_COMPOSE) THEN + REWRITE_TAC[o_DEF] THEN + ASM_REWRITE_TAC[IS_INTERVAL_INTERVAL; BOUNDED_INTERVAL] THEN + ANTS_TAC THENL + [REWRITE_TAC[SUBSET; FORALL_IN_IMAGE; IN_INTERVAL_1; + DROP_VEC; DROP_CMUL; DROP_SUB; LIFT_DROP] THEN + REAL_ARITH_TAC; + ALL_TAC] THEN + DISCH_THEN SUBST1_TAC THEN + SUBGOAL_THEN + `(g2:real^1->real^N) has_bounded_variation_on interval[vec 0, vec 1]` + ASSUME_TAC THENL + [MATCH_MP_TAC ABSOLUTELY_CONTINUOUS_ON_IMP_HAS_BOUNDED_VARIATION_ON THEN + ASM_REWRITE_TAC[BOUNDED_INTERVAL]; + ALL_TAC] THEN + MP_TAC(ISPECL + [`\x:real^1. &2 % x - vec 1`; + `g2:real^1->real^N`; + `lift(&1 / &2)`; `vec 1:real^1`] + HAS_BOUNDED_VARIATION_COMPOSE_INCREASING) THEN + REWRITE_TAC[o_DEF] THEN + ANTS_TAC THENL + [CONJ_TAC THENL + [REWRITE_TAC[IN_INTERVAL_1; DROP_VEC; DROP_CMUL; DROP_SUB; LIFT_DROP] THEN + ASM_REAL_ARITH_TAC; + SUBGOAL_THEN + `&2 % lift(&1 / &2) - vec 1 = vec 0:real^1 /\ + &2 % vec 1 - vec 1 = vec 1:real^1` + (fun th -> REWRITE_TAC[th]) THENL + [REWRITE_TAC[GSYM DROP_EQ; DROP_CMUL; DROP_SUB; DROP_VEC; + LIFT_DROP] THEN + REAL_ARITH_TAC; + ASM_REWRITE_TAC[]]]; + SIMP_TAC[]]]);; + (* ------------------------------------------------------------------------- *) (* Fubini-type results for measure. *) (* ------------------------------------------------------------------------- *) diff --git a/Multivariate/multivariate_database.ml b/Multivariate/multivariate_database.ml index 1728e68f..de4b29d3 100644 --- a/Multivariate/multivariate_database.ml +++ b/Multivariate/multivariate_database.ml @@ -63,12 +63,15 @@ theorems := "ABSOLUTELY_CONTINUOUS_DIFFERENTIABLE_BV_GEN",ABSOLUTELY_CONTINUOUS_DIFFERENTIABLE_BV_GEN; "ABSOLUTELY_CONTINUOUS_EXTENDS_TO_CLOSURE",ABSOLUTELY_CONTINUOUS_EXTENDS_TO_CLOSURE; "ABSOLUTELY_CONTINUOUS_IMP_BANACH_SPROPERTY",ABSOLUTELY_CONTINUOUS_IMP_BANACH_SPROPERTY; +"ABSOLUTELY_CONTINUOUS_IMP_PATH",ABSOLUTELY_CONTINUOUS_IMP_PATH; +"ABSOLUTELY_CONTINUOUS_IMP_RECTIFIABLE_PATH",ABSOLUTELY_CONTINUOUS_IMP_RECTIFIABLE_PATH; "ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_EQ",ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_EQ; "ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_LEFT",ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_LEFT; "ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_RIGHT",ABSOLUTELY_CONTINUOUS_INDEFINITE_INTEGRAL_RIGHT; "ABSOLUTELY_CONTINUOUS_INTEGRAL",ABSOLUTELY_CONTINUOUS_INTEGRAL; "ABSOLUTELY_CONTINUOUS_ISOMETRIC",ABSOLUTELY_CONTINUOUS_ISOMETRIC; "ABSOLUTELY_CONTINUOUS_ISOMETRIC_COMPOSE",ABSOLUTELY_CONTINUOUS_ISOMETRIC_COMPOSE; +"ABSOLUTELY_CONTINUOUS_JOINPATHS",ABSOLUTELY_CONTINUOUS_JOINPATHS; "ABSOLUTELY_CONTINUOUS_LIPSCHITZ_COMPOSE",ABSOLUTELY_CONTINUOUS_LIPSCHITZ_COMPOSE; "ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE",ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE; "ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE_GEN",ABSOLUTELY_CONTINUOUS_MEASURE_DIFFERENTIABLE_IMAGE_GEN; @@ -108,6 +111,7 @@ theorems := "ABSOLUTELY_CONTINUOUS_ON_VMUL",ABSOLUTELY_CONTINUOUS_ON_VMUL; "ABSOLUTELY_CONTINUOUS_ON_VMUL_EQ",ABSOLUTELY_CONTINUOUS_ON_VMUL_EQ; "ABSOLUTELY_CONTINUOUS_ON_VSUM",ABSOLUTELY_CONTINUOUS_ON_VSUM; +"ABSOLUTELY_CONTINUOUS_REVERSEPATH",ABSOLUTELY_CONTINUOUS_REVERSEPATH; "ABSOLUTELY_CONTINUOUS_VECTOR_VARIATION",ABSOLUTELY_CONTINUOUS_VECTOR_VARIATION; "ABSOLUTELY_INTEGRABLE_0",ABSOLUTELY_INTEGRABLE_0; "ABSOLUTELY_INTEGRABLE_ABS",ABSOLUTELY_INTEGRABLE_ABS; @@ -200,6 +204,7 @@ theorems := "ABSOLUTELY_INTEGRABLE_TRANSLATION",ABSOLUTELY_INTEGRABLE_TRANSLATION; "ABSOLUTELY_INTEGRABLE_TWIZZLE_EQ",ABSOLUTELY_INTEGRABLE_TWIZZLE_EQ; "ABSOLUTELY_INTEGRABLE_UNION",ABSOLUTELY_INTEGRABLE_UNION; +"ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS",ABSOLUTELY_INTEGRABLE_VECTOR_DERIVATIVE_ABSOLUTELY_CONTINUOUS; "ABSOLUTELY_INTEGRABLE_VSUM",ABSOLUTELY_INTEGRABLE_VSUM; "ABSOLUTELY_SETCONTINUOUS_COMPARISON",ABSOLUTELY_SETCONTINUOUS_COMPARISON; "ABSOLUTELY_SETCONTINUOUS_INDEFINITE_INTEGRAL",ABSOLUTELY_SETCONTINUOUS_INDEFINITE_INTEGRAL; @@ -9690,6 +9695,7 @@ theorems := "LEBESGUE_MEASURABLE_PREIMAGE_OPEN",LEBESGUE_MEASURABLE_PREIMAGE_OPEN; "LEBESGUE_MEASURABLE_REGULAR_INNER",LEBESGUE_MEASURABLE_REGULAR_INNER; "LEBESGUE_MEASURABLE_REGULAR_OUTER",LEBESGUE_MEASURABLE_REGULAR_OUTER; +"LEBESGUE_MEASURABLE_SING",LEBESGUE_MEASURABLE_SING; "LEBESGUE_MEASURABLE_SMALL_IMP_NEGLIGIBLE",LEBESGUE_MEASURABLE_SMALL_IMP_NEGLIGIBLE; "LEBESGUE_MEASURABLE_TRANSLATION",LEBESGUE_MEASURABLE_TRANSLATION; "LEBESGUE_MEASURABLE_UNION",LEBESGUE_MEASURABLE_UNION; diff --git a/Multivariate/realanalysis.ml b/Multivariate/realanalysis.ml index 8991ed71..766daae6 100644 --- a/Multivariate/realanalysis.ml +++ b/Multivariate/realanalysis.ml @@ -15707,6 +15707,11 @@ let COMPLEX_EULER_MACLAURIN_ANTIDERIVATIVE = prove (* Specific properties of complex measurable functions. *) (* ------------------------------------------------------------------------- *) +let NEGLIGIBLE_REAL = prove + (`negligible real`, + ONCE_REWRITE_TAC[SET_RULE `real = {z | real z}`] THEN + REWRITE_TAC[real; IM_DEF; NEGLIGIBLE_STANDARD_HYPERPLANE]);; + let MEASURABLE_ON_COMPLEX_MUL = prove (`!f g:real^N->complex s. f measurable_on s /\ g measurable_on s @@ -15815,6 +15820,37 @@ let MEASURABLE_ON_CPRODUCT = prove MATCH_MP_TAC MEASURABLE_ON_COMPLEX_MUL THEN ASM_REWRITE_TAC[ETA_AX] THEN FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]);; +let MEASURABLE_ON_CLOG = prove + (`!s. lebesgue_measurable s ==> clog measurable_on s`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC MEASURABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + ASM_REWRITE_TAC[SUBSET_UNIV; LEBESGUE_MEASURABLE_UNIV] THEN + MATCH_MP_TAC + CONTINUOUS_AE_IMP_MEASURABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `real` THEN + REWRITE_TAC[LEBESGUE_MEASURABLE_UNIV; NEGLIGIBLE_REAL] THEN + MATCH_MP_TAC CONTINUOUS_ON_CLOG THEN + REWRITE_TAC[SET_RULE + `(z:complex) IN UNIV DIFF (r:complex->bool) <=> ~(r z)`] THEN + REWRITE_TAC[real] THEN MESON_TAC[]);; + +let MEASURABLE_ON_CPOW = prove + (`!s w. lebesgue_measurable s ==> (\z. z cpow w) measurable_on s`, + REPEAT STRIP_TAC THEN + MATCH_MP_TAC MEASURABLE_ON_LEBESGUE_MEASURABLE_SUBSET THEN + EXISTS_TAC `(:complex)` THEN + ASM_REWRITE_TAC[SUBSET_UNIV; LEBESGUE_MEASURABLE_UNIV] THEN + REWRITE_TAC[cpow] THEN MATCH_MP_TAC MEASURABLE_ON_CASES THEN + ASM_REWRITE_TAC[MEASURABLE_ON_CONST] THEN + REWRITE_TAC[SING_GSPEC; LEBESGUE_MEASURABLE_SING] THEN + GEN_REWRITE_TAC LAND_CONV [GSYM o_DEF] THEN + MATCH_MP_TAC MEASURABLE_ON_COMPOSE_CONTINUOUS THEN + REWRITE_TAC[CONTINUOUS_ON_CEXP] THEN + MATCH_MP_TAC MEASURABLE_ON_COMPLEX_MUL THEN + REWRITE_TAC[MEASURABLE_ON_CONST] THEN + SIMP_TAC[MEASURABLE_ON_CLOG; LEBESGUE_MEASURABLE_UNIV]);; + (* ------------------------------------------------------------------------- *) (* Measurable real->real functions. *) (* ------------------------------------------------------------------------- *) diff --git a/Multivariate/transcendentals.ml b/Multivariate/transcendentals.ml index a38f8579..ff20dbaa 100644 --- a/Multivariate/transcendentals.ml +++ b/Multivariate/transcendentals.ml @@ -1653,6 +1653,24 @@ let CEXP_INTEGER_2PI = prove REWRITE_TAC[CEXP_EQ_1; IM_MUL_II; RE_MUL_II; RE_CX; IM_CX] THEN REWRITE_TAC[REAL_NEG_0] THEN MESON_TAC[]);; +let CEXP_LIPSCHITZ_BOUNDED = prove + (`!M a b. norm(a) <= M /\ norm(b) <= M + ==> norm(cexp a - cexp b) <= exp(M) * norm(a - b)`, + REPEAT STRIP_TAC THEN + MP_TAC(ISPECL [`cexp`; `cexp`; `cball(Cx(&0):complex,M)`; `exp(M)`] + COMPLEX_DIFFERENTIABLE_BOUND) THEN + REWRITE_TAC[CONVEX_CBALL] THEN ANTS_TAC THENL + [REWRITE_TAC[IN_CBALL; dist; COMPLEX_SUB_LZERO; NORM_NEG] THEN + X_GEN_TAC `x:complex` THEN DISCH_TAC THEN CONJ_TAC THENL + [SIMP_TAC[HAS_COMPLEX_DERIVATIVE_AT_WITHIN; + HAS_COMPLEX_DERIVATIVE_CEXP]; + REWRITE_TAC[NORM_CEXP; REAL_EXP_MONO_LE] THEN + MP_TAC(SPEC `x:complex` COMPLEX_NORM_GE_RE_IM) THEN + ASM_REAL_ARITH_TAC]; + DISCH_THEN(MP_TAC o SPECL [`a:complex`; `b:complex`]) THEN + REWRITE_TAC[IN_CBALL; dist; COMPLEX_SUB_LZERO; NORM_NEG] THEN + ASM_REWRITE_TAC[]]);; + let SIN_COS_EQ = prove (`!x y. sin y = sin x /\ cos y = cos x <=> ?n. integer n /\ y = x + &2 * n * pi`, diff --git a/holtest.mk b/holtest.mk index fd657b6d..eb81ac56 100644 --- a/holtest.mk +++ b/holtest.mk @@ -22,6 +22,7 @@ STANDALONE_EXAMPLES:=\ Examples/digit_serial_methods \ Examples/division_algebras \ Examples/dlo \ + Examples/doomsday \ Library/fieldtheory \ Library/floor \ Examples/forster \ @@ -153,6 +154,7 @@ GREAT_100_THEOREMS:= \ 100/friendship \ 100/fta \ 100/gcd \ + 100/green \ 100/heron \ 100/isoperimetric \ 100/inclusion_exclusion \ diff --git a/mcp/.python-version b/mcp/.python-version new file mode 100644 index 00000000..2c073331 --- /dev/null +++ b/mcp/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/mcp/README.md b/mcp/README.md new file mode 100644 index 00000000..86c85116 --- /dev/null +++ b/mcp/README.md @@ -0,0 +1,161 @@ +# HOL Light MCP Server + +MCP server for the [HOL Light](https://github.com/jrh13/hol-light) theorem prover, designed for LLM-assisted proof search. + +## Quick Start + +After setup (below), here's a complete proof session: + +``` +set_goal "`!n. EVEN n ==> EVEN(n * n)`" +→ {"goals":[{"hypotheses":[],"conclusion":"forall n. EVEN n ==> EVEN (n * n)"}], ...} + +apply_tactic "GEN_TAC THEN STRIP_TAC" +→ {"goals":[{"hypotheses":[{"label":"","term":"EVEN n"}],"conclusion":"EVEN (n * n)"}], ...} + +search_theorems name="EVEN_MULT" +→ [{"name":"EVEN_MULT","statement":"|- forall m n. EVEN (m * n) <=> EVEN m \\/ EVEN n"}] + +apply_tactic "ASM_REWRITE_TAC[EVEN_MULT]" +→ {"proved":true,"theorem":"|- forall n. EVEN n ==> EVEN (n * n)"} +``` + +Or as a one-shot proof: + +``` +prove goal="`!n. EVEN n ==> EVEN(n * n)`" tactic="GEN_TAC THEN STRIP_TAC THEN ASM_REWRITE_TAC[EVEN_MULT]" +→ {"proved":true,"theorem":"|- forall n. EVEN n ==> EVEN (n * n)"} +``` + +See [TUTORIAL.md](TUTORIAL.md) for more examples (including s2n-bignum ARM proofs) and [SKILL.md](SKILL.md) for a tactic reference. + +## Tools + +| Tool | Description | Output | +|------|-------------|--------| +| `eval` | Evaluate arbitrary OCaml/HOL Light code | Structured JSON (truncated) | +| `set_goal` | Set a proof goal, return initial state | Structured JSON | +| `goal_state` | Return current proof goals | Structured JSON | +| `apply_tactic` | Apply a tactic, return new state or proved theorem | Structured JSON | +| `apply_tactics` | Apply a list of tactics in one round-trip | Structured JSON | +| `prove` | One-shot prove: goal + tactic → theorem | Structured JSON | +| `backtrack` | Undo tactic steps | Structured JSON | +| `search_theorems` | Search theorem database by name | Structured JSON | +| `hol_type` | Get the type of a term | Raw text | +| `hol_load` | Load a HOL Light file via `needs` | Structured JSON | +| `hol_interrupt` | Cancel a long-running command | Status message | +| `hol_restart` | Kill and restart the HOL Light subprocess | Status message | +| `hol_status` | Check process health, uptime, config, checkpoint | Structured JSON | +| `hol_help` | Return tactic reference and proof guide (SKILL.md) | Markdown text | +| `start_recording` | Start recording proof tactics to a JSONL file | Status message | +| `stop_recording` | Stop recording and return the file path | Status message | + +`eval` returns `{"success", "output", "output_truncated", "full_output_chars", "time_seconds"}`. Large outputs are truncated to `max_output_chars` (default 4000, configurable). Override per-call with `max_output_chars=N`. + +`hol_load` returns `{"success", "file", "time_seconds"}` (plus `"error"` on failure). Intermediate output is suppressed — use `eval` with `needs "file.ml"` if you need verbose output. + +## Setup + +```bash +cd mcp && uv sync +``` + +Requires HOL Light built in the parent directory (`make switch && eval $(opam env) && make`). + +## Checkpointing (optional, recommended) + +HOL Light takes ~75s to cold start. DMTCP checkpointing reduces this to ~2s. + +Requires DMTCP installed (`dmtcp_launch` and `dmtcp_restart` on PATH). To build from source: + +```bash +git clone https://github.com/dmtcp/dmtcp.git /tmp/dmtcp +cd /tmp/dmtcp && ./configure --prefix=$HOME/.local && make -j$(nproc) && make install +``` + +Create named checkpoints: + +```bash +export LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" + +# Base HOL Light (~75s) +python3 mcp/make_checkpoint.py --name base + +# With s2n-bignum ARM infrastructure (~5-10min) +python3 mcp/make_checkpoint.py --name s2n -I /path/to/s2n-bignum 'needs "arm/proofs/base.ml"' +``` + +## Configuration + +By default, the server loads `/path/to/hol-light/mcp/hol-mcp.toml`. To use a different config, pass `--config /absolute/path/to/hol-mcp.toml`. + +```toml +# Which checkpoint to use (looks for /path/to/hol-light/hol-.ckpt/). +checkpoint = "s2n" + +# Timeout in seconds for HOL Light commands. +timeout = 600 + +# Maximum characters for eval output before truncation. +max_output_chars = 4000 +``` + +Use `hol_status` to verify which config file and checkpoint are active. + +## Usage + +```bash +uv run hol-light-mcp +uv run hol-light-mcp --config /path/to/hol-mcp.toml +``` + +The server speaks MCP over stdio. + +## Client Configuration + +### Kiro CLI (`~/.kiro/settings/mcp.json`) + +```json +{ + "mcpServers": { + "hol-light": { + "command": "uv", + "args": ["run", "--directory", "/path/to/hol-light/mcp", "hol-light-mcp", + "--config", "/path/to/your/project/hol-mcp.toml"], + "env": { + "PATH": "/home/user/.local/bin:/usr/bin:/bin" + } + } + } +} +``` + +### Skill / Tactic Guide + +The server includes a built-in `hol_help` tool that returns the full tactic reference and proof guide ([SKILL.md](SKILL.md)). The LLM can call it before its first proof — no extra configuration needed. + +## Tests + +```bash +cd mcp +uv run pytest test_server.py -v # 48 unit tests +uv run python smoke_test.py # 37 MCP integration checks +``` + +First run includes HOL Light startup (~75s cold, ~2s with checkpoint). + +## Security Considerations + +The `eval` tool executes arbitrary OCaml code, which has full access to the host system. This is inherent to theorem proving. However, the server uses stdio transport (no network sockets), so it is only accessible to the MCP client process that spawned it. It is not exposed to the network, even on an EC2 instance with open ports. + +Treat DMTCP checkpoint files (`hol-.ckpt/`) as executable code---don't restore untrusted checkpoints. + +## Related Work + +[mkannwischer/mcp-hol-light](https://github.com/mkannwischer/mcp-hol-light) is another MCP server for HOL Light. It connects to a separate [hol-server](https://github.com/monadius/hol_server) TCP process and exposes a similar set of tools (`hol_eval`, `hol_tactic`, `hol_back`, `hol_search`, etc.). It implements the MCP protocol directly rather than using an SDK, and returns raw REPL text from all tools. + +We take a different approach: we embed HOL Light as a subprocess (no external server needed), use OCaml-side JSON serialization to return structured goal states and search results, and support DMTCP checkpointing for instant startup. We find that structured output is particularly useful for automated proof search, where an LLM agent needs to programmatically inspect goals, detect proof completion, and implement backtracking strategies without parsing REPL text. + +## Acknowledgments + +Thanks to kceren for advice on HOL Light tactics and proof patterns, to nebeid for suggesting named checkpoints, and to the s2n-bignum team for encouragement and tutorial examples. diff --git a/mcp/SKILL.md b/mcp/SKILL.md new file mode 100644 index 00000000..b7d08b9a --- /dev/null +++ b/mcp/SKILL.md @@ -0,0 +1,207 @@ +--- +name: hol-light +description: Prove theorems in HOL Light via MCP tools. Use when the user asks to prove a theorem, verify a proof, or work with HOL Light tactics, goals, or lemmas. +compatibility: Requires the hol-light MCP server running (uv run hol-light-mcp). +--- + +# HOL Light MCP — LLM Skill Guide + +You have access to a HOL Light theorem prover via MCP tools. This document teaches you how to use them effectively. + +HOL Light is a classical higher-order logic theorem prover. The law of excluded middle is available, and proof by contradiction is fine. Only use `(* ... *)` for OCaml comments. + +## Proof workflow + +**One-shot proofs:** If you know the full tactic, use **prove**(goal, tactic) to get the theorem in a single call. + +**Interactive proofs:** +1. **set_goal** — state the theorem to prove +2. **goal_state** — inspect current goals (check hypotheses and conclusion) +3. **search_theorems** — find relevant lemmas by name substring +4. **apply_tactic** — apply a tactic; check response for `"proved":true` +5. **apply_tactics** — apply multiple tactics in one round-trip (faster for straightforward sequences) +6. **backtrack** — undo if a tactic made things worse +7. Repeat 2–6 until proved +8. **hol_status** — check if HOL Light is alive (useful for debugging) +9. **hol_restart** — restart HOL Light if it has died or is in a bad state + +Always read the goal state carefully before choosing a tactic. The structured JSON tells you exactly what hypotheses you have and what you need to show. + +## Core tactics + +### Logical structure +| Tactic | Use when | +|--------|----------| +| `GEN_TAC` | Goal is `!x. P(x)` — strips one universal quantifier | +| `X_GEN_TAC \`n:num\`` | Like `GEN_TAC` but names the variable | +| `STRIP_TAC` | Goal has `==>`, `/\`, or `!` at the top — strips one connective | +| `REPEAT STRIP_TAC` | Strip all top-level connectives at once | +| `CONJ_TAC` | Goal is `P /\ Q` — splits into two subgoals | +| `DISJ1_TAC` / `DISJ2_TAC` | Goal is `P \/ Q` — choose which disjunct to prove | +| `EQ_TAC` | Goal is `P <=> Q` — splits into `P ==> Q` and `Q ==> P` | +| `DISCH_TAC` | Move antecedent of `P ==> Q` into hypotheses | +| `EXISTS_TAC \`witness\`` | Goal is `?x. P(x)` — provide the witness | + +### Rewriting +| Tactic | Use when | +|--------|----------| +| `REWRITE_TAC[thm1; thm2]` | Rewrite goal using equations (left-to-right) | +| `ASM_REWRITE_TAC[thms]` | Rewrite using hypotheses AND given theorems | +| `ONCE_REWRITE_TAC[thm]` | Rewrite one pass, all topmost matches (not just one match!) | +| `GEN_REWRITE_TAC I [thm]` | Rewrite at top level only | +| `GEN_REWRITE_TAC (RAND_CONV) [thm]` | Rewrite only the operand of the top-level application | +| `GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) [thm]` | Rewrite inside the LHS of a binary operator | +| `SIMP_TAC[thms]` | Conditional rewriting (handles side conditions) | +| `ASM_SIMP_TAC[thms]` | Conditional rewriting with hypotheses | + +### Automation +| Tactic | Solves | +|--------|--------| +| `ARITH_TAC` | Linear arithmetic over naturals and integers (goal only, ignores hypotheses) | +| `REAL_ARITH_TAC` | Linear arithmetic over reals | +| `MESON_TAC[thms]` | First-order logic with given lemmas | +| `ASM_MESON_TAC[thms]` | First-order logic with hypotheses + lemmas | +| `TAUT` (as a rule) | Propositional tautologies: `TAUT \`p /\ q ==> q\`` | +| `SET_TAC[thms]` | Set-theoretic goals | +| `RING_TAC` | Ring equalities | +| `WORD_RULE` | Word (bitvector) equalities | +| `CONV_TAC WORD_RULE` | Word equalities as a tactic | + +### Induction +| Tactic | Use when | +|--------|----------| +| `INDUCT_TAC` | Induction on the outermost `!n.` (natural number) | +| `LIST_INDUCT_TAC` | Induction on a list | +| `MATCH_MP_TAC thm` | Apply a theorem backwards (modus ponens in reverse) | + +### Hypothesis management +| Tactic | Effect | +|--------|--------| +| `ASM_REWRITE_TAC[]` | Rewrite goal using all hypotheses | +| `FIRST_X_ASSUM MATCH_MP_TAC` | Use first matching hypothesis backwards (consumes it) | +| `FIRST_ASSUM MATCH_MP_TAC` | Same but keeps the hypothesis | +| `UNDISCH_TAC \`term\`` | Move a hypothesis back to the goal as antecedent | +| `SUBGOAL_THEN \`P\` ASSUME_TAC` | Assert and prove an intermediate fact | +| `SUBGOAL_THEN \`P\` MP_TAC` | Assert, prove, and add as antecedent of goal | +| `SUBGOAL_THEN \`P\` SUBST1_TAC` | Assert, prove, and substitute the equality in goal | +| `SUBGOAL_THEN \`P\` STRIP_ASSUME_TAC` | Assert, prove, and split conjunctions into separate assumptions | +| `ABBREV_TAC \`x = expr\`` | Replace `expr` with `x` in goal, add `expr = x` as assumption | +| `EXPAND_TAC "x"` | Expand an abbreviation introduced by `ABBREV_TAC` | + +### Combinators +| Combinator | Meaning | +|------------|---------| +| `tac1 THEN tac2` | Apply tac1, then tac2 to all resulting subgoals | +| `tac1 THENL [tac2; tac3]` | Apply tac1, then tac2 to first subgoal, tac3 to second | +| `REPEAT tac` | Apply tac repeatedly until it fails | +| `TRY tac` | Try tac, succeed even if it fails | + +## Searching for theorems + +`search_theorems` searches by name substring. The search is cheap — use it freely. Tips: +- Search for the main constant: `search_theorems name="EVEN"` for theorems about `EVEN` +- Search for the operation: `search_theorems name="ADD"` for addition theorems +- Common prefixes: `ADD_`, `MULT_`, `LE_`, `LT_`, `EVEN_`, `ODD_`, `DIV_`, `MOD_` +- For rewriting, look for theorems with `<=>` or `=` in the statement + +You can also use `eval` for more powerful searches: +``` +eval 'search [`EVEN`]' -- search by subterm pattern +eval 'search [`EVEN`; `ODD`]' -- multiple patterns +``` + +## Common proof patterns + +### Prove by simplification +``` +apply_tactic "SIMP_TAC[thm1; thm2; thm3]" +``` + +### Prove by rewriting then arithmetic +``` +apply_tactic "REWRITE_TAC[DEF1; DEF2] THEN ARITH_TAC" +``` + +### Prove by induction +``` +apply_tactic "INDUCT_TAC" -- creates base case + inductive step +apply_tactic "base case tactic" -- solve base case +apply_tactic "ASM_REWRITE_TAC[...] THEN inductive step tactic" +``` + +### Prove using a key lemma +``` +search_theorems name="KEY_LEMMA" +apply_tactic "MATCH_MP_TAC KEY_LEMMA THEN ..." +``` + +### Case split +``` +apply_tactic "ASM_CASES_TAC `P` THEN ASM_REWRITE_TAC[]" +``` + +### Declarative sub-lemma +``` +apply_tactic "SUBGOAL_THEN `intermediate_fact` ASSUME_TAC" +-- prove the sub-lemma first, then use it +``` + +## Pitfalls + +- **`REWRITE_TAC[GSYM thm]` can loop.** Use `ONCE_REWRITE_TAC[GSYM thm]` instead. +- **`ONCE_REWRITE_TAC` rewrites ALL topmost matches, not just one.** If the goal has `f a b` and `f c d`, and the theorem matches `f x y`, both get rewritten in one pass. For targeted single-occurrence rewriting, use `GEN_REWRITE_TAC` with conversionals like `RAND_CONV`, `LAND_CONV`. +- **`FIRST_X_ASSUM` consumes the hypothesis.** Use `FIRST_ASSUM` when you need to reuse it. +- **`ARITH_TAC` ignores hypotheses.** Use `UNDISCH_TAC \`needed_fact\`` to bring assumptions into the goal first. `ASM_ARITH_TAC` uses hypotheses but can hang with many `val(...)` terms. +- **`ASM_ARITH_TAC` hangs with many assumptions.** Discard irrelevant ones first with `REPEAT (FIRST_X_ASSUM (K ALL_TAC))` or targeted `UNDISCH_TAC` + `DISCH_TAC`. +- **`SUBST1_TAC` silently succeeds even when the LHS doesn't appear in the goal.** It just does nothing. This makes `FIRST_ASSUM(fun th -> SUBST1_TAC(SYM th))` unreliable — it picks the first assumption where the tactic "succeeds" (which is all of them). Use `EXPAND_TAC "name"` to expand abbreviations instead. +- **`THEN` vs `THENL` after `SUBGOAL_THEN ... SUBST1_TAC`.** `THEN` applies the proof to ALL subgoals (both the equality proof and the main goal). Use `THENL [equality_proof; ALL_TAC]` to target only the first subgoal. +- **`WORD_RULE` hangs on `val(word(...))`.** Normalize via `VAL_WORD_EQ` first. +- **Natural number subtraction is truncating.** `n - m = 0` when `m >= n`. Use `ARITH_TAC` for goals involving subtraction. +- **`*` is right-associative for `num`.** Use explicit parentheses to avoid surprises. +- **When a tactic hangs**, backtrack and try a different approach. Automation tactics (`MESON_TAC`, `ARITH_TAC`) can diverge on hard goals. + +## s2n-bignum ARM proof tactics + +For proving properties of AArch64 machine code (requires `arm/proofs/base.ml`): + +| Tactic | Use when | +|--------|----------| +| `ARM_MK_EXEC_RULE mc` | Decode a machine code byte list into instruction theorems | +| `ENSURES_INIT_TAC "s0"` | Initialize symbolic state for an `ensures` proof | +| `ARM_STEPS_TAC EXEC (1--n)` | Symbolically execute n instructions | +| `ARM_SIM_TAC EXEC (1--n)` | INIT + STEPS + FINAL in one shot | +| `ENSURES_FINAL_STATE_TAC` | Close postcondition and frame | +| `ENSURES_WHILE_PAUP_TAC a b pc_body pc_back inv` | Declare a counting-up loop with invariant | +| `COND_CASES_TAC` | Case split on a conditional branch | +| `CONV_TAC WORD_RULE` | Solve word (bitvector) equalities | +| `WORD_BLAST` | Bit-blasting for word goals | + +Typical ARM proof pattern: +``` +REPEAT STRIP_TAC THEN ENSURES_INIT_TAC "s0" THEN +ARM_STEPS_TAC EXEC (1--n) THEN +ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN +CONV_TAC WORD_RULE +``` + +## Utility tools + +- **hol_type** — get the type of a HOL Light term (e.g., `hol_type` with term `` `1 + 1` `` returns `num`) +- **hol_load** — load a HOL Light file via `needs` (e.g., `hol_load` with file `"Library/words.ml"`) +- **hol_interrupt** — send SIGINT to cancel a hung tactic (e.g., when `MESON_TAC` diverges) +- **hol_help** — return this tactic reference (SKILL.md). Call before your first proof. +- **start_recording** — record all tactic applications to a JSONL file for later replay +- **stop_recording** — stop recording and return the file path + +## General advice + +- **Try automation first.** `ARITH_TAC`, `MESON_TAC[]`, or `ASM_MESON_TAC[]` often solve goals outright. +- **Read the goal.** The JSON tells you exactly what you have (hypotheses) and what you need (conclusion). Don't guess. +- **Search before you rewrite.** Use `search_theorems` to find the right lemma name rather than guessing. +- **Backtrack freely.** If a tactic doesn't simplify the goal, undo it and try something else. +- **Strip first.** `REPEAT STRIP_TAC` is almost always a good opening move. +- **`ASM_REWRITE_TAC[]` is your friend.** It rewrites with all hypotheses. Use it after `STRIP_TAC`. +- **Work incrementally.** Apply one tactic at a time, inspect the goal state, then proceed. +- **Use `SUBGOAL_THEN` for declarative proofs.** Clear intermediate goals make proofs easier to follow. +- **Structure longer proofs into smaller lemmas.** It's fine for a proof to have 100+ tactic steps, but breaking into lemmas is preferred. +- **Avoid exotic tactics** unless you know they're needed. diff --git a/mcp/TUTORIAL.md b/mcp/TUTORIAL.md new file mode 100644 index 00000000..5b2e64af --- /dev/null +++ b/mcp/TUTORIAL.md @@ -0,0 +1,264 @@ +# HOL Light MCP Tutorial + +Worked examples of increasing difficulty, showing different proof techniques. + +## 1. Commutativity of conjunction + +A one-shot proof — `STRIP_TAC` decomposes the goal, `ASM_REWRITE_TAC` finishes it. + +``` +set_goal "`!p q. p /\ q ==> q /\ p`" +→ {"goals":[{"hypotheses":[],"conclusion":"forall p q. p /\\ q ==> q /\\ p"}], ...} + +apply_tactic "REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[]" +→ {"proved":true,"theorem":"|- forall p q. p /\\ q ==> q /\\ p"} +``` + +## 2. EVEN squares + +Using `search_theorems` to find a lemma, then rewriting with it. + +``` +set_goal "`!n. EVEN n ==> EVEN(n * n)`" + +apply_tactic "GEN_TAC THEN STRIP_TAC" +→ hypotheses: ["EVEN n"], conclusion: "EVEN (n * n)" + +search_theorems name="EVEN_MULT" +→ [{"name":"EVEN_MULT","statement":"|- forall m n. EVEN (m * n) <=> EVEN m \\/ EVEN n"}] + +apply_tactic "ASM_REWRITE_TAC[EVEN_MULT]" +→ proved +``` + +## 3. Gauss sum by induction + +Proving `∑ᵢ₌₀ⁿ i = n(n+1)/2`. Induction creates two subgoals — base case and step. + +``` +set_goal "`!n. nsum(0..n) (\i. i) = (n * (n + 1)) DIV 2`" + +apply_tactic "INDUCT_TAC" +→ 2 subgoals: + [0] conclusion: "nsum (0..0) (\i. i) = (0 * (0 + 1)) DIV 2" + [1] hypotheses: ["nsum (0..n) (\i. i) = (n * (n + 1)) DIV 2"] + conclusion: "nsum (0..SUC n) (\i. i) = (SUC n * (SUC n + 1)) DIV 2" +``` + +Base case — expand nsum and compute: +``` +apply_tactic "REWRITE_TAC[NSUM_CLAUSES_NUMSEG] THEN ARITH_TAC" +→ 1 subgoal remaining (inductive step) +``` + +Inductive step — expand nsum, apply IH, arithmetic: +``` +apply_tactic "REWRITE_TAC[NSUM_CLAUSES_NUMSEG] THEN ASM_REWRITE_TAC[LE_0] THEN ARITH_TAC" +→ proved: |- forall n. nsum (0..n) (\i. i) = (n * (n + 1)) DIV 2 +``` + +## 4. Backtracking + +When a tactic doesn't help, `backtrack` undoes it. + +``` +set_goal "`!n. 0 < n ==> (n - 1) + 1 = n`" + +apply_tactic "REWRITE_TAC[ADD_SUB]" +→ goal unchanged (wrong lemma — ADD_SUB is `n + m - m = n`) + +backtrack steps=1 + +apply_tactic "ARITH_TAC" +→ proved +``` + +## 5. Composition of injections + +Pure first-order logic — `MESON_TAC` handles it automatically. + +``` +set_goal "`!f g. (!x y. f x = f y ==> x = y) /\ (!x y. g x = g y ==> x = y) + ==> (!x y. g(f x) = g(f y) ==> x = y)`" + +apply_tactic "MESON_TAC[]" +→ proved +``` + +## 6. Product of consecutive numbers is even + +Case splitting on parity, with lemma search. + +``` +set_goal "`!n. (n * (n + 1)) MOD 2 = 0`" +``` + +First, find relevant theorems: +``` +search_theorems name="EVEN_MOD" +→ [{"name":"EVEN_MOD","statement":"|- forall n. EVEN n <=> n MOD 2 = 0"}, ...] + +search_theorems name="EVEN_MULT" +→ [{"name":"EVEN_MULT","statement":"|- forall m n. EVEN (m * n) <=> EVEN m \\/ EVEN n"}] + +search_theorems name="EVEN_OR_ODD" +→ [{"name":"EVEN_OR_ODD","statement":"|- forall n. EVEN n \\/ ODD n"}] +``` + +Strategy: rewrite `MOD 2 = 0` to `EVEN`, then case split on whether `n` is even or odd. + +``` +apply_tactic "GEN_TAC THEN REWRITE_TAC[GSYM EVEN_MOD; EVEN_MULT] THEN DISJ_CASES_TAC(SPEC `n:num` EVEN_OR_ODD) THEN ASM_REWRITE_TAC[]" +→ hypotheses: ["ODD n"], conclusion: "EVEN n \\/ EVEN (n + 1)" +``` + +When `n` is even, the left disjunct is immediate. When `n` is odd, we need `EVEN(n+1)`: + +``` +search_theorems name="EVEN_ADD" +→ [{"name":"EVEN_ADD","statement":"|- forall m n. EVEN (m + n) <=> EVEN m <=> EVEN n"}] + +search_theorems name="NOT_EVEN" +→ [{"name":"NOT_EVEN","statement":"|- forall n. ~EVEN n <=> ODD n"}] + +apply_tactic "DISJ2_TAC THEN REWRITE_TAC[EVEN_ADD] THEN ASM_REWRITE_TAC[NOT_EVEN; ARITH]" +→ proved: |- forall n. (n * (n + 1)) MOD 2 = 0 +``` + +## Proof workflow summary + +**One-shot:** Use **prove**(goal, tactic) when you know the full tactic. + +**Interactive:** +1. **set_goal** — state the theorem +2. **goal_state** — inspect hypotheses and conclusion +3. **search_theorems** — find relevant lemmas +4. **apply_tactic** — try automation first (`ARITH_TAC`, `MESON_TAC[]`), then targeted tactics +5. **apply_tactics** — batch multiple tactics in one call for straightforward sequences +6. **backtrack** — undo if a tactic didn't help +7. Repeat until `"proved":true` +8. **hol_status** — check if HOL Light is alive (useful for debugging) +9. **hol_restart** — restart HOL Light if it has died or is in a bad state + +**Utility tools:** **hol_type** (get term types), **hol_load** (load files), **hol_interrupt** (cancel hung tactics), **hol_help** (tactic reference), **start_recording** / **stop_recording** (record tactics to JSONL for replay) + +--- + +## s2n-bignum ARM proofs + +The following examples prove properties of AArch64 machine code using the s2n-bignum framework. They require a checkpoint with `arm/proofs/base.ml` preloaded (see README). + +## 7. Straight-line: add and subtract + +Two instructions (`add x2, x1, x0; sub x2, x2, x1`) — proves `x2 = x0`. + +``` +eval 'let simple_mc = new_definition `simple_mc = [ + word 0x22; word 0x00; word 0x00; word 0x8b; + word 0x42; word 0x00; word 0x01; word 0xcb + ]:((8)word)list`;; +let EXEC = ARM_MK_EXEC_RULE simple_mc' + +set_goal "`forall pc a b. + ensures arm + (\s. aligned_bytes_loaded s (word pc) simple_mc /\ + read PC s = word pc /\ read X0 s = word a /\ read X1 s = word b) + (\s. read PC s = word (pc+8) /\ read X2 s = word a) + (MAYCHANGE [PC;X2])`" + +apply_tactic "REPEAT STRIP_TAC THEN ENSURES_INIT_TAC \"s0\"" +apply_tactic "ARM_STEPS_TAC EXEC (1--2)" +apply_tactic "ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN CONV_TAC WORD_RULE" +→ proved +``` + +## 8. Conditional branch: max(x1, x2) + +A program with `cmp; b.hi` that copies `max(x1, x2)` to `x0`. Requires case splitting on the branch condition. + +``` +eval 'let branch_mc = new_definition `branch_mc = [ + word 0x3f; word 0x00; word 0x02; word 0xeb; + word 0x68; word 0x00; word 0x00; word 0x54; + word 0xe0; word 0x03; word 0x02; word 0xaa; + word 0xc0; word 0x03; word 0x5f; word 0xd6; + word 0xe0; word 0x03; word 0x01; word 0xaa; + word 0xc0; word 0x03; word 0x5f; word 0xd6 + ]:((8)word)list`;; +let EXEC = ARM_MK_EXEC_RULE branch_mc' + +set_goal "`forall pc pcret a b. + ensures arm + (\s. aligned_bytes_loaded s (word pc) branch_mc /\ + read X30 s = word pcret /\ read PC s = word pc /\ + read X1 s = word a /\ read X2 s = word b) + (\s. read PC s = word pcret /\ read X0 s = word_umax (word a) (word b)) + (MAYCHANGE [PC;X0] ,, MAYCHANGE SOME_FLAGS ,, MAYCHANGE [events])`" +``` + +Execute up to the branch, then case split: +``` +apply_tactic "REPEAT STRIP_TAC THEN REWRITE_TAC[SOME_FLAGS] THEN ENSURES_INIT_TAC \"s0\" THEN ARM_STEPS_TAC EXEC (1--2)" + +apply_tactic "FIRST_X_ASSUM MP_TAC THEN COND_CASES_TAC THENL [ + POP_ASSUM (LABEL_TAC \"Hcond\") THEN DISCH_TAC THEN + ARM_STEPS_TAC EXEC (3--4) THEN ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN + REMOVE_THEN \"Hcond\" MP_TAC THEN REWRITE_TAC[WORD_UMAX;VAL_WORD_SUB_EQ_0] THEN ARITH_TAC; + POP_ASSUM (LABEL_TAC \"Hcond\") THEN DISCH_TAC THEN + ARM_STEPS_TAC EXEC (3--4) THEN ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN + REMOVE_THEN \"Hcond\" MP_TAC THEN REWRITE_TAC[WORD_UMAX;VAL_WORD_SUB_EQ_0] THEN ARITH_TAC]" +→ proved +``` + +## 9. Loop with invariant: count to 20 + +A loop that increments x1 by 1 and x0 by 2 until x1=10, proving x0=20. Uses `ENSURES_WHILE_PAUP_TAC` for the loop invariant. + +``` +eval 'let loop_mc = new_definition `loop_mc = [ + word 0xe1; word 0x03; word 0x1f; word 0xaa; + word 0xe0; word 0x03; word 0x1f; word 0xaa; + word 0x21; word 0x04; word 0x00; word 0x91; + word 0x00; word 0x08; word 0x00; word 0x91; + word 0x3f; word 0x28; word 0x00; word 0xf1; + word 0xa1; word 0xff; word 0xff; word 0x54; + word 0xc0; word 0x03; word 0x5f; word 0xd6 +]:((8)word)list`;; +let EXEC = ARM_MK_EXEC_RULE loop_mc' + +set_goal "`forall pc retpc. + ensures arm + (\s. aligned_bytes_loaded s (word pc) loop_mc /\ + read PC s = word pc /\ read X30 s = word retpc) + (\s. read PC s = word retpc /\ read X0 s = word 20) + (MAYCHANGE [PC;X0;X1] ,, MAYCHANGE SOME_FLAGS ,, MAYCHANGE [events])`" +``` + +Declare the loop with invariant `x1 = i, x0 = 2*i`: +``` +apply_tactic "REWRITE_TAC[SOME_FLAGS] THEN REPEAT STRIP_TAC THEN + ENSURES_WHILE_PAUP_TAC `0` `10` `pc + 8` `pc + 0x14` + `\\i s. (read X1 s = word i /\\ read X0 s = word (i*2) /\\ read X30 s = word retpc) /\\ + (read ZF s <=> i = 10)`" +``` + +Solve the preamble, backedge, and exit (leaving loop body): +``` +apply_tactic "REPEAT CONJ_TAC THENL [ + ARITH_TAC; + ARM_SIM_TAC EXEC (1--2) THEN CONV_TAC WORD_RULE; + ALL_TAC; + REPEAT STRIP_TAC THEN ARM_SIM_TAC EXEC [1]; + ARM_SIM_TAC EXEC (1--2) THEN CONV_TAC WORD_RULE]" +``` + +Prove the loop body — simulate 3 instructions, then word arithmetic: +``` +apply_tactic "REPEAT STRIP_TAC THEN ARM_SIM_TAC EXEC (1--3) THEN REPEAT CONJ_TAC THENL [ + CONV_TAC WORD_RULE; + CONV_TAC WORD_RULE; + REWRITE_TAC[WORD_BLAST `word_add x (word 18446744073709551607):int64 = word_sub x (word 9)`] THEN + REWRITE_TAC[VAL_WORD_SUB_EQ_0] THEN REWRITE_TAC[VAL_WORD;DIMINDEX_64] THEN + IMP_REWRITE_TAC[MOD_LT; ARITH_RULE`9 < 2 EXP 64`] THEN CONJ_TAC THEN ASM_ARITH_TAC]" +→ proved +``` diff --git a/mcp/hol-mcp.toml b/mcp/hol-mcp.toml new file mode 100644 index 00000000..280db1ce --- /dev/null +++ b/mcp/hol-mcp.toml @@ -0,0 +1,24 @@ +# HOL Light MCP server configuration. +# This file is loaded by default from /path/to/hol-light/mcp/hol-mcp.toml. +# To use a different config, pass --config /absolute/path/to/hol-mcp.toml. + +# Which checkpoint to use (looks for hol-.ckpt/ in HOL Light root). +# Create with: python3 mcp/make_checkpoint.py --name [options] +checkpoint = "base" + +# Timeout in seconds for HOL Light commands. +timeout = 600 + +# Maximum characters for eval output before truncation. +max_output_chars = 4000 + +# Auto-record proof tactics to this directory (recording.jsonl). +# Relative paths resolved against CWD. Also settable via HOL_RECORDING_DIR env var. +# recording_dir = "/path/to/work" + +# Replay a tactic prefix on startup to restore proof state. +# replay_init: ML file to #use before replaying (e.g., load project dependencies). +# replay_prefix: JSONL file of tactics to replay (same format as recording.jsonl). +# Also settable via HOL_REPLAY_INIT and HOL_REPLAY_PREFIX env vars. +# replay_init = "/path/to/init.ml" +# replay_prefix = "/path/to/prefix.jsonl" diff --git a/mcp/make_checkpoint.py b/mcp/make_checkpoint.py new file mode 100644 index 00000000..9faf8a94 --- /dev/null +++ b/mcp/make_checkpoint.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Create a DMTCP checkpoint of HOL Light. + +Examples: + python3 mcp/make_checkpoint.py # base HOL Light + python3 mcp/make_checkpoint.py --name s2n \\ + -I /path/to/s2n-bignum 'needs "arm/proofs/base.ml"' +""" +import argparse +import glob +import os +import shutil +import socket +import subprocess +import sys +import time + +HOL_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SENTINEL = "HOL_MCP_CKPT_READY" + + +def fatal(msg): + print(f"ERROR: {msg}", file=sys.stderr, flush=True) + sys.exit(1) + + +def parse_args(): + p = argparse.ArgumentParser( + description="Create a DMTCP checkpoint of HOL Light.", + epilog="Extra positional arguments are OCaml expressions evaluated after HOL Light loads " + "(e.g., 'needs \"arm/proofs/base.ml\"').", + ) + p.add_argument("--name", default="base", + help="Checkpoint name (creates hol-.ckpt/). Default: base") + p.add_argument("-I", dest="include_dirs", action="append", default=[], + help="Add OCaml include directory (can be repeated)") + p.add_argument("extra_loads", nargs="*", metavar="EXPR", + help="OCaml expressions to evaluate before checkpointing") + return p.parse_args() + + +def build_env(): + """Build environment with opam switch and LD_LIBRARY_PATH.""" + env = os.environ.copy() + ld_path = os.path.expanduser("~/.local/lib") + if os.path.isdir(ld_path): + env["LD_LIBRARY_PATH"] = ld_path + ":" + env.get("LD_LIBRARY_PATH", "") + try: + r = subprocess.run( + ["opam", "env", "--switch", HOL_DIR + "/", "--set-switch"], + capture_output=True, text=True, + ) + for line in r.stdout.strip().split("\n"): + if "=" in line and "'" in line: + key = line.split("=", 1)[0].strip() + val = line.split("'")[1] + env[key] = val + except FileNotFoundError: + pass + return env + + +def wait_for_line(proc, marker, error_msg): + """Read stdout until a line contains marker. Dies on EOF.""" + while True: + line = proc.stdout.readline() + if not line: + fatal(error_msg) + if marker in line: + return + + +def send_and_wait(proc, code, error_msg): + """Send OCaml code and wait for sentinel.""" + proc.stdin.write(f'{code};;\nPrintf.printf "{SENTINEL}\\n%!";;\n') + proc.stdin.flush() + wait_for_line(proc, SENTINEL, error_msg) + + +def main(): + args = parse_args() + ckpt_dir = os.path.join(HOL_DIR, f"hol-{args.name}.ckpt") + ocaml_hol = os.path.join(HOL_DIR, "ocaml-hol") + + # Validate prerequisites + if not os.path.isfile(ocaml_hol): + fatal(f"ocaml-hol not found at {ocaml_hol}\n Run: make switch && eval $(opam env) && make") + if not shutil.which("dmtcp_launch"): + fatal("dmtcp_launch not found on PATH\n See mcp/README.md for install instructions") + for d in args.include_dirs: + if not os.path.isdir(d): + fatal(f"Include directory not found: {d}") + + # Clean and recreate checkpoint dir + if os.path.exists(ckpt_dir): + shutil.rmtree(ckpt_dir) + os.makedirs(ckpt_dir) + + # Find a free port for DMTCP coordinator + s = socket.socket() + s.bind(("", 0)) + port = s.getsockname()[1] + s.close() + os.environ["DMTCP_COORD_PORT"] = str(port) + + env = build_env() + + # Build command + cmd = [ + "dmtcp_launch", "--new-coordinator", "--ckptdir", ckpt_dir, + ocaml_hol, "-init", os.path.join(HOL_DIR, "hol.ml"), "-I", HOL_DIR, + ] + for d in args.include_dirs: + cmd.extend(["-I", d]) + + if args.include_dirs: + existing = env.get("HOLLIGHT_LOAD_PATH", "") + extra = ":".join(args.include_dirs) + env["HOLLIGHT_LOAD_PATH"] = f"{extra}:{existing}" if existing else extra + + # Print plan + print(f"Checkpoint: hol-{args.name}.ckpt/", flush=True) + if args.include_dirs: + print(f"Include dirs: {args.include_dirs}", flush=True) + if args.extra_loads: + print(f"Extra loads: {args.extra_loads}", flush=True) + + # Launch HOL Light under DMTCP + p = subprocess.Popen( + cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, bufsize=1, cwd=HOL_DIR, env=env, + ) + + print("Waiting for HOL Light to load (~75s)...", flush=True) + wait_for_line(p, "Camlp5", "HOL Light died before loading. Check that 'make' succeeded.") + print("HOL Light loaded.", flush=True) + + for expr in args.extra_loads: + print(f"Loading: {expr}", flush=True) + send_and_wait(p, expr, f"HOL Light died while loading: {expr}") + print(" done.", flush=True) + + print("Compacting GC...", flush=True) + send_and_wait(p, "Gc.compact ()", "HOL Light died during Gc.compact") + print(" done.", flush=True) + + time.sleep(2) + + print("Checkpointing...", flush=True) + r = subprocess.run( + ["dmtcp_command", "--port", str(port), "-bc"], + capture_output=True, text=True, env=env, + ) + if r.returncode != 0: + fatal(f"dmtcp_command failed (rc={r.returncode}): {r.stderr.strip()}") + + # Wait for checkpoint file to appear + for _ in range(10): + files = glob.glob(os.path.join(ckpt_dir, "ckpt_*.dmtcp")) + if files: + break + time.sleep(1) + else: + fatal("No checkpoint files created") + + subprocess.run( + ["dmtcp_command", "--port", str(port), "-k"], + capture_output=True, text=True, env=env, + ) + + size_mb = sum(os.path.getsize(f) for f in files) / (1024 * 1024) + print(f"Done. {ckpt_dir}/ ({size_mb:.0f}MB)", flush=True) + + +if __name__ == "__main__": + main() diff --git a/mcp/mcp_helpers.ml b/mcp/mcp_helpers.ml new file mode 100644 index 00000000..8262a201 --- /dev/null +++ b/mcp/mcp_helpers.ml @@ -0,0 +1,174 @@ +(* MCP helpers: JSON serialization for HOL Light goal states. + Loaded via #use after HOL Light starts. No external dependencies. *) + +let mcp_json_escape s = + let buf = Buffer.create (String.length s + 16) in + String.iter (fun c -> match c with + | '"' -> Buffer.add_string buf "\\\"" + | '\\' -> Buffer.add_string buf "\\\\" + | '\n' -> Buffer.add_string buf "\\n" + | '\r' -> Buffer.add_string buf "\\r" + | '\t' -> Buffer.add_string buf "\\t" + | c when Char.code c < 0x20 -> + Buffer.add_string buf (Printf.sprintf "\\u%04x" (Char.code c)) + | c -> Buffer.add_char buf c) s; + Buffer.contents buf;; + +let mcp_json_string s = "\"" ^ mcp_json_escape s ^ "\"";; + +let mcp_json_error msg = "{\"error\":" ^ mcp_json_string msg ^ "}";; + +let mcp_buf_json_string buf s = + Buffer.add_char buf '"'; + String.iter (fun c -> match c with + | '"' -> Buffer.add_string buf "\\\"" + | '\\' -> Buffer.add_string buf "\\\\" + | '\n' -> Buffer.add_string buf "\\n" + | '\r' -> Buffer.add_string buf "\\r" + | '\t' -> Buffer.add_string buf "\\t" + | c when Char.code c < 0x20 -> + Buffer.add_string buf (Printf.sprintf "\\u%04x" (Char.code c)) + | c -> Buffer.add_char buf c) s; + Buffer.add_char buf '"';; + +let mcp_buf_goal buf ((asl, w) : goal) = + Buffer.add_string buf "{\"hypotheses\":["; + let first = ref true in + List.iter (fun (label, th) -> + if !first then first := false else Buffer.add_char buf ','; + Buffer.add_string buf "{\"label\":"; + mcp_buf_json_string buf label; + Buffer.add_string buf ",\"term\":"; + mcp_buf_json_string buf (string_of_term (concl th)); + Buffer.add_char buf '}' + ) (List.rev asl); + Buffer.add_string buf "],\"conclusion\":"; + mcp_buf_json_string buf (string_of_term w); + Buffer.add_char buf '}';; + +let mcp_buf_goals buf gl = + Buffer.add_char buf '['; + let first = ref true in + List.iter (fun g -> + if !first then first := false else Buffer.add_char buf ','; + mcp_buf_goal buf g + ) gl; + Buffer.add_char buf ']';; + +let mcp_json_goalstate () = + let buf = Buffer.create 256 in + (match !current_goalstack with + | [] -> + Buffer.add_string buf "{\"goals\":[],\"num_subgoals\":0,\"total_goals\":0}" + | [_, gl, _] -> + let n = List.length gl in + Buffer.add_string buf "{\"goals\":"; + mcp_buf_goals buf gl; + Buffer.add_string buf ",\"num_subgoals\":"; + Buffer.add_string buf (string_of_int (min 1 n)); + Buffer.add_string buf ",\"total_goals\":"; + Buffer.add_string buf (string_of_int n); + Buffer.add_char buf '}' + | (_, gl, _) :: (_, gl0, _) :: _ -> + let n = List.length gl in + let p = n - List.length gl0 in + let num_sub = if p < 1 then 1 else p + 1 in + Buffer.add_string buf "{\"goals\":"; + mcp_buf_goals buf gl; + Buffer.add_string buf ",\"num_subgoals\":"; + Buffer.add_string buf (string_of_int num_sub); + Buffer.add_string buf ",\"total_goals\":"; + Buffer.add_string buf (string_of_int n); + Buffer.add_char buf '}'); + Buffer.contents buf;; + +let mcp_json_after_tactic () = + match !current_goalstack with + | (_, [], f) :: _ -> + let th = f null_inst [] in + "{\"proved\":true,\"theorem\":" ^ mcp_json_string (string_of_thm th) ^ "}" + | _ -> mcp_json_goalstate ();; + +let mcp_json_backtrack n = + try + for _ = 1 to n do ignore (b ()) done; + mcp_json_goalstate () + with + | Failure msg -> mcp_json_error msg + | e -> mcp_json_error (Printexc.to_string e);; + +let mcp_json_search pat limit = + let results = search [name pat] in + let buf = Buffer.create 512 in + Buffer.add_char buf '['; + let first = ref true in + let count = ref 0 in + List.iter (fun (n, th) -> + if !count < limit then begin + if !first then first := false else Buffer.add_char buf ','; + Buffer.add_string buf "{\"name\":"; + mcp_buf_json_string buf n; + Buffer.add_string buf ",\"statement\":"; + mcp_buf_json_string buf (string_of_thm th); + Buffer.add_char buf '}'; + incr count + end + ) results; + Buffer.add_char buf ']'; + Buffer.contents buf;; + +let mcp_json_apply_tactics (tacs : tactic list) = + let steps = ref 0 in + try + let proved = ref false in + List.iter (fun tac -> + if not !proved then begin + ignore (e tac); + incr steps; + match !current_goalstack with + | (_, [], _) :: _ -> proved := true + | _ -> () + end + ) tacs; + if !proved then + let th = match !current_goalstack with + | (_, [], f) :: _ -> f null_inst [] + | _ -> failwith "unreachable" in + "{\"proved\":true,\"theorem\":" ^ mcp_json_string (string_of_thm th) ^ + ",\"steps\":" ^ string_of_int !steps ^ "}" + else + let buf = Buffer.create 256 in + (match !current_goalstack with + | [] -> + Buffer.add_string buf "{\"goals\":[],\"num_subgoals\":0,\"total_goals\":0" + | [_, gl, _] -> + let n = List.length gl in + Buffer.add_string buf "{\"goals\":"; + mcp_buf_goals buf gl; + Buffer.add_string buf ",\"num_subgoals\":"; + Buffer.add_string buf (string_of_int (min 1 n)); + Buffer.add_string buf ",\"total_goals\":"; + Buffer.add_string buf (string_of_int n) + | (_, gl, _) :: (_, gl0, _) :: _ -> + let n = List.length gl in + let p = n - List.length gl0 in + let num_sub = if p < 1 then 1 else p + 1 in + Buffer.add_string buf "{\"goals\":"; + mcp_buf_goals buf gl; + Buffer.add_string buf ",\"num_subgoals\":"; + Buffer.add_string buf (string_of_int num_sub); + Buffer.add_string buf ",\"total_goals\":"; + Buffer.add_string buf (string_of_int n)); + Buffer.add_string buf ",\"steps\":"; + Buffer.add_string buf (string_of_int !steps); + Buffer.add_char buf '}'; + Buffer.contents buf + with + | Failure msg -> + "{\"error\":" ^ mcp_json_string msg ^ + ",\"step\":" ^ string_of_int !steps ^ "}" + | e -> + "{\"error\":" ^ mcp_json_string (Printexc.to_string e) ^ + ",\"step\":" ^ string_of_int !steps ^ "}";; + +Printf.printf "MCP helpers loaded.\n%!";; diff --git a/mcp/pyproject.toml b/mcp/pyproject.toml new file mode 100644 index 00000000..28a9f86e --- /dev/null +++ b/mcp/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "hol-light-mcp" +version = "0.1.0" +description = "MCP server for HOL Light theorem prover" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "mcp>=1.26.0", +] + +[project.scripts] +hol-light-mcp = "server:main" + +[tool.setuptools] +py-modules = ["server"] + +[dependency-groups] +dev = [ + "pytest>=9.0.2", +] + +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/mcp/server.py b/mcp/server.py new file mode 100644 index 00000000..af297daf --- /dev/null +++ b/mcp/server.py @@ -0,0 +1,807 @@ +#!/usr/bin/env python3 +"""MCP server for HOL Light theorem prover.""" + +import os +import queue +import re +import subprocess +import sys +import threading +import time + +HOL_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +MCP_DIR = os.path.dirname(os.path.abspath(__file__)) +SENTINEL = "HOL_MCP_DONE_a7f3b2e1" +ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") + + +def _load_config(): + """Load config from hol-mcp.toml. Search order: + 1. --config CLI arg 2. HOL_MCP_CONFIG env 3. CWD 4. MCP_DIR""" + import tomllib + config_path = None + for i, arg in enumerate(sys.argv): + if arg == "--config" and i + 1 < len(sys.argv): + config_path = sys.argv[i + 1] + break + if not config_path: + config_path = os.environ.get("HOL_MCP_CONFIG") + if not config_path: + for d in [os.getcwd(), MCP_DIR]: + p = os.path.join(d, "hol-mcp.toml") + if os.path.isfile(p): + config_path = p + break + if config_path and os.path.isfile(config_path): + with open(config_path, "rb") as f: + return tomllib.load(f), os.path.abspath(config_path) + return {}, None + + +_config, CONFIG_PATH = _load_config() +TIMEOUT = _config.get("timeout", int(os.environ.get("HOL_TIMEOUT", "600"))) +CHECKPOINT_NAME = _config.get("checkpoint", os.environ.get("HOL_CHECKPOINT", "base")) +MAX_OUTPUT_CHARS = _config.get("max_output_chars", int(os.environ.get("HOL_MAX_OUTPUT", "4000"))) + +from mcp.server.fastmcp import FastMCP +mcp = FastMCP("hol-light", + instructions="HOL Light theorem prover. Call hol_help() for a tactic reference and proof guide.") + + +def _read_skill(): + path = os.path.join(MCP_DIR, "SKILL.md") + if os.path.isfile(path): + with open(path) as f: + return f.read() + return "SKILL.md not found." + +_proc = None +_lock = threading.Lock() +_helpers_loaded = False +_start_time = None + +# Proof recording state +_recording_path = None # path to JSONL file; None = not recording +_recording = [] # list of {"action": "tactic", "tactic": ..., "total_goals": ...} + +# Auto-recording: if recording_dir is set in config or env, enable recording at startup. +_auto_record_dir = _config.get("recording_dir") or os.environ.get("HOL_RECORDING_DIR") +if _auto_record_dir: + _auto_record_dir = os.path.abspath(_auto_record_dir) + os.makedirs(_auto_record_dir, exist_ok=True) + _recording_path = os.path.join(_auto_record_dir, "recording.jsonl") + +# Queue-based sentinel signaling: reader thread produces results, eval consumes. +# Eliminates race conditions — queue.get() is atomic consumption. +_result_queue = queue.Queue(maxsize=1) +_reader_buf = [] + + +def _reader_thread(proc): + while True: + line = proc.stdout.readline() + if not line: + # Process died — signal immediately so callers don't hang + _result_queue.put("[HOL Light process died unexpectedly]") + break + if SENTINEL in line: + _result_queue.put("".join(_reader_buf).strip()) + _reader_buf.clear() + else: + _reader_buf.append(line) + + +def _opam_env(): + env = os.environ.copy() + # Ensure DMTCP can find libatomic + ld_path = os.path.expanduser("~/.local/lib") + if os.path.isdir(ld_path): + env["LD_LIBRARY_PATH"] = ld_path + ":" + env.get("LD_LIBRARY_PATH", "") + if not os.path.isdir(os.path.join(HOL_DIR, "_opam")): + return env + try: + r = subprocess.run( + ["opam", "env", "--switch", HOL_DIR + "/", "--set-switch"], + capture_output=True, text=True, + ) + for line in r.stdout.strip().split("\n"): + if "=" in line and "'" in line: + key = line.split("=", 1)[0].strip() + val = line.split("'")[1] + env[key] = val + except FileNotFoundError: + pass + return env + + +def _start_hol(): + global _proc + if _proc is not None: + return + ckpt_dir = os.path.join(HOL_DIR, f"hol-{CHECKPOINT_NAME}.ckpt") + ckpt_files = sorted( + f for f in os.listdir(ckpt_dir) if f.startswith("ckpt_") and f.endswith(".dmtcp") + ) if os.path.isdir(ckpt_dir) else [] + if ckpt_files: + _proc = subprocess.Popen( + ["dmtcp_restart", "--no-strict-checking", "--coord-port", "0"] + + [os.path.join(ckpt_dir, f) for f in ckpt_files], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=HOL_DIR, + text=True, + bufsize=1, + env=_opam_env(), + ) + else: + _proc = subprocess.Popen( + [os.path.join(HOL_DIR, "ocaml-hol"), "-init", + os.path.join(HOL_DIR, "hol.ml"), "-I", HOL_DIR], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=HOL_DIR, + text=True, + bufsize=1, + env=_opam_env(), + ) + global _start_time + _start_time = time.time() + t = threading.Thread(target=_reader_thread, args=(_proc,), daemon=True) + t.start() + + +def _wait_for_sentinel(timeout=None): + if timeout is None: + timeout = TIMEOUT + try: + return _result_queue.get(timeout=timeout) + except queue.Empty: + return "[timeout waiting for HOL Light response]" + + +def _drain_queue(): + """Discard any stale results in the queue.""" + while not _result_queue.empty(): + try: + _result_queue.get_nowait() + except queue.Empty: + break + + +def _load_helpers(): + global _helpers_loaded + if _helpers_loaded: + return + helpers_path = os.path.join(MCP_DIR, "mcp_helpers.ml") + _drain_queue() + _reader_buf.clear() + cmd = f'#use "{helpers_path}";;\nPrintf.printf "{SENTINEL}\\n%!";;\n' + _proc.stdin.write(cmd) + _proc.stdin.flush() + result = _wait_for_sentinel() + if "MCP helpers loaded" in result: + _helpers_loaded = True + else: + raise RuntimeError(f"Failed to load MCP helpers: {result}") + _replay_prefix() + + +def _replay_prefix(): + """Replay a tactic prefix on startup to restore proof state. + + Loads replay_init (ML file) then replays replay_prefix (JSONL of tactics). + Both are optional; configured via hol-mcp.toml or env vars. + """ + init_path = _config.get("replay_init") or os.environ.get("HOL_REPLAY_INIT") + prefix_path = _config.get("replay_prefix") or os.environ.get("HOL_REPLAY_PREFIX") + if not init_path and not prefix_path: + return + if init_path: + result, _ = _eval_raw(f'#use "{_ocaml_escape(init_path)}"') + if _is_error_output(_strip_ansi(result)): + return + if not prefix_path or not os.path.exists(prefix_path): + return + import json + replayed = [] + try: + with open(prefix_path) as f: + for line in f: + line = line.strip() + if not line: + continue + entry = json.loads(line) + if entry.get("action") == "backtrack": + steps = entry.get("steps", 1) + removed = 0 + while removed < steps and replayed: + replayed.pop() + _eval_raw("b()") + removed += 1 + elif entry.get("action") == "tactic": + result, _ = _eval_raw(f'e({entry["tactic"]})') + if _is_error_output(_strip_ansi(result)): + for _ in range(len(replayed)): + _eval_raw("b()") + return + replayed.append(entry) + except (json.JSONDecodeError, KeyError, OSError): + for _ in range(len(replayed)): + _eval_raw("b()") + return + global _recording, _recording_flushed + _recording = replayed + _recording_flushed = len(replayed) # entries already on disk + + +def _eval_raw(code: str, timeout: int = None) -> tuple[str, float]: + """Eval code, return (output, elapsed_seconds). Caller must hold _lock.""" + _drain_queue() + _reader_buf.clear() + full = code.rstrip() + if not full.endswith(";;"): + full += ";;" + full += f'\nPrintf.printf "{SENTINEL}\\n%!";;\n' + t0 = time.time() + _proc.stdin.write(full) + _proc.stdin.flush() + result = _wait_for_sentinel(timeout) + return result, round(time.time() - t0, 3) + + +def _eval_code(code: str, timeout: int = None) -> tuple[str, float]: + with _lock: + _start_hol() + _load_helpers() + return _eval_raw(code, timeout) + + +def _eval_json(code: str, timeout: int = None) -> tuple[str, float]: + """Eval OCaml code that produces a string, print it to stdout, return it. + Uses print_string to avoid OCaml's string truncation in REPL output.""" + return _eval_code(f'print_string ({code}); print_newline ()', timeout) + + +def _strip_ansi(s: str) -> str: + return ANSI_RE.sub("", s) + + +def _truncate(s: str, limit: int) -> tuple[str, bool]: + """Truncate string to limit chars. Returns (result, was_truncated).""" + if len(s) <= limit: + return s, False + return s[:limit] + "... [truncated]", True + + +def _is_error_output(s: str) -> bool: + """Heuristic: check if OCaml output indicates an error.""" + for marker in ("Error:", "Exception:", "Failure", "Unbound", "Parse error", + "Syntax error", "Type error", "This expression has type"): + if marker in s: + return True + return False + + +@mcp.tool() +def eval(code: str, timeout: int = None, max_output_chars: int = None) -> str: + """Evaluate OCaml/HOL Light code and return structured JSON. + + Args: + code: OCaml/HOL Light code to evaluate. + timeout: Optional timeout in seconds. + max_output_chars: Max chars for output field (default from config, typically 4000). + + Returns JSON: + {"success": bool, "output": str, "output_truncated": bool, + "full_output_chars": int, "time_seconds": float} + """ + import json as _json + # Detect recording patterns before eval + is_bt = _is_backtrack(code) if _recording_path else None + tac = _extract_e_tactic(code) if (_recording_path and not is_bt) else None + + with _lock: + _start_hol() + _load_helpers() + raw, elapsed = _eval_raw(code, timeout) + raw = _strip_ansi(raw) + # Record e(...) and b() calls while still holding the lock + if _recording_path: + if is_bt: + _record_backtrack(1) + elif tac and not _is_error_output(raw): + gs_raw, _ = _eval_raw("print_string (mcp_json_after_tactic ()); print_newline ()") + _record_tactic(tac, _extract_json(gs_raw)) + + limit = max_output_chars if max_output_chars is not None else MAX_OUTPUT_CHARS + full_len = len(raw) + output, truncated = _truncate(raw, limit) + return _json.dumps({ + "success": not _is_error_output(raw), + "output": output, + "output_truncated": truncated, + "full_output_chars": full_len, + "time_seconds": elapsed, + }) + + +@mcp.tool() +def goal_state() -> str: + """Return the current goal state as JSON. + + Returns JSON: {"goals": [{"hypotheses": [...], "conclusion": "..."}], + "num_subgoals": N, "total_goals": M} + Returns empty goals list if no proof is in progress. + """ + return _extract_json(_eval_json("mcp_json_goalstate ()")[0]) + + +@mcp.tool() +def apply_tactic(tactic: str, timeout: int = None) -> str: + """Apply a tactic to the current goal and return the resulting state as JSON. + + The tactic should be a valid HOL Light tactic expression, e.g.: + ARITH_TAC + GEN_TAC THEN REWRITE_TAC[ADD] + MESON_TAC[] + + Returns JSON with either: + - New goal state: {"goals": [...], "num_subgoals": N, "total_goals": M} + - Proof complete: {"proved": true, "theorem": "..."} + - Error: {"error": "..."} + """ + code = (f'(try ignore(e({tactic})); ' + f'print_string (mcp_json_after_tactic ()) ' + f'with Failure s -> print_string (mcp_json_error s) ' + f'| e -> print_string (mcp_json_error (Printexc.to_string e))); ' + f'print_newline ()') + with _lock: + _start_hol() + _load_helpers() + result = _extract_json(_eval_raw(code, timeout)[0]) + _record_tactic(tactic, result) + return result + + +@mcp.tool() +def apply_tactics(tactics: list[str], timeout: int = None) -> str: + """Apply a list of tactics sequentially in a single round-trip. + + Stops at the first error or when the proof is complete. + + Args: + tactics: List of HOL Light tactic expressions. + timeout: Optional timeout in seconds for the entire batch. + + Returns JSON with: + - Proof complete: {"proved": true, "theorem": "...", "steps": N} + - Error: {"error": "...", "step": N} + - Goal state after all tactics: goal state JSON with added "steps" field + """ + if not tactics: + return '{"error":"empty tactic list"}' + tac_list = "[" + "; ".join(tactics) + "]" + code = (f'print_string (mcp_json_apply_tactics {tac_list}); print_newline ()') + with _lock: + _start_hol() + _load_helpers() + result = _extract_json(_eval_raw(code, timeout)[0]) + _record_tactics_batch(tactics, result) + return result + + +@mcp.tool() +def prove(goal: str, tactic: str, timeout: int = None) -> str: + """Prove a theorem in one shot using a goal and tactic. + + This is a convenience wrapper around HOL Light's prove() function. + Use for simple proofs that don't need interactive stepping. + + Args: + goal: HOL Light term to prove (e.g., "`!n. n + 0 = n`") + tactic: Complete tactic to prove the goal (e.g., "GEN_TAC THEN ARITH_TAC") + timeout: Optional timeout in seconds. + + Returns JSON: + - Success: {"proved": true, "theorem": "..."} + - Error: {"error": "..."} + """ + code = (f'(try let th = prove({goal}, {tactic}) in ' + f'print_string ("{{\\"proved\\":true,\\"theorem\\":" ^ ' + f'mcp_json_string (string_of_thm th) ^ "}}") ' + f'with Failure s -> print_string (mcp_json_error s) ' + f'| e -> print_string (mcp_json_error (Printexc.to_string e))); ' + f'print_newline ()') + return _extract_json(_eval_code(code, timeout)[0]) + + +@mcp.tool() +def backtrack(steps: int = 1) -> str: + """Undo tactic steps and return the resulting goal state as JSON. + + Args: + steps: Number of steps to undo (default 1). + + Returns JSON goal state or {"error": "..."} if can't back up. + """ + with _lock: + _start_hol() + _load_helpers() + result = _extract_json(_eval_raw(f'print_string (mcp_json_backtrack {steps}); print_newline ()')[0]) + _record_backtrack(steps) + return result + + +@mcp.tool() +def search_theorems(name: str, limit: int = 20) -> str: + """Search the theorem database by name and return results as JSON. + + Args: + name: Substring to search for in theorem names. + limit: Maximum results to return (default 20). + + Returns JSON array: [{"name": "...", "statement": "..."}, ...] + """ + return _extract_json(_eval_json(f'mcp_json_search "{_ocaml_escape(name)}" {limit}')[0]) + + +@mcp.tool() +def set_goal(goal: str) -> str: + """Set a new proof goal and return the initial goal state as JSON. + + Args: + goal: HOL Light term to prove (e.g., "`!n. n + 0 = n`") + + Returns JSON goal state. + """ + code = (f'ignore(g({goal})); ' + f'print_string (mcp_json_goalstate ()); print_newline ()') + return _extract_json(_eval_code(code)[0]) + + +@mcp.tool() +def hol_type(term: str) -> str: + """Get the type of a HOL Light term. + + Args: + term: Term to get type of (e.g., "`x + y`") + + Returns the type as a string. + """ + return _strip_ansi(_eval_code(f"type_of {term}")[0]) + + +@mcp.tool() +def hol_load(file: str) -> str: + """Load a HOL Light file using 'needs'. + + Args: + file: File path to load (e.g., "Library/words.ml") + + Returns JSON: + {"success": bool, "file": str, "time_seconds": float} + On failure: {"success": false, "file": str, "error": str, "time_seconds": float} + """ + import json as _json + raw, elapsed = _eval_code(f'needs "{_ocaml_escape(file)}"') + raw = _strip_ansi(raw) + if _is_error_output(raw): + return _json.dumps({ + "success": False, "file": file, "error": raw.strip(), + "time_seconds": elapsed, + }) + return _json.dumps({ + "success": True, "file": file, "time_seconds": elapsed, + }) + + +@mcp.tool() +def hol_interrupt() -> str: + """Send an interrupt signal to cancel a long-running HOL Light command. + + Use when a tactic hangs (e.g., MESON_TAC on a hard goal). + After interrupting, the goal state is preserved and you can try + a different tactic. + """ + import signal + with _lock: + if _proc and _proc.poll() is None: + _proc.send_signal(signal.SIGINT) + time.sleep(0.5) + _drain_queue() + _reader_buf.clear() + return "Interrupt sent." + return "No HOL Light process running." + + +@mcp.tool() +def hol_restart() -> str: + """Kill and restart the HOL Light subprocess. + + Use when HOL Light has died or is in a bad state. + Any in-progress proof state will be lost. + """ + global _proc, _helpers_loaded + with _lock: + if _proc is not None: + try: + _proc.kill() + _proc.wait(timeout=5) + except Exception: + pass + _proc = None + _helpers_loaded = False + global _recording_flushed + _recording_flushed = len(_recording) + _drain_queue() + _reader_buf.clear() + _start_hol() + _load_helpers() + return "HOL Light restarted." + + +@mcp.tool() +def hol_status() -> str: + """Check whether the HOL Light subprocess is alive. + + Returns JSON: {"alive": bool, "pid": int|null, "checkpoint": str, + "config": str|null, "uptime_seconds": float|null, + "timeout": int, "max_output_chars": int} + """ + import json + alive = _proc is not None and _proc.poll() is None + return json.dumps({ + "alive": alive, + "pid": _proc.pid if alive else None, + "checkpoint": CHECKPOINT_NAME, + "config": CONFIG_PATH, + "uptime_seconds": round(time.time() - _start_time, 1) if alive and _start_time else None, + "timeout": TIMEOUT, + "max_output_chars": MAX_OUTPUT_CHARS, + }) + + +@mcp.tool() +def hol_help() -> str: + """Return the HOL Light tactic reference and proof guide (SKILL.md). + + Call this before your first proof to learn available tactics, + proof patterns, and common pitfalls. + """ + return _read_skill() + + +def _ocaml_escape(s: str) -> str: + return s.replace("\\", "\\\\").replace('"', '\\"') + + +def _json_quote(s: str) -> str: + return '"' + s.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") + '"' + + +# --- Proof recording helpers --- + +def _flush_recording(): + """Append new entries to the recording file. + Only writes entries added since the last flush (tracked by _recording_flushed). + Backtrack writes a marker so replay can skip undone tactics. + """ + import json + if not _recording_path: + return + with open(_recording_path, 'a') as f: + global _recording_flushed + for entry in _recording[_recording_flushed:]: + f.write(json.dumps(entry) + "\n") + _recording_flushed = len(_recording) + + +_recording_flushed = 0 # index of first unflushed entry + + +def _record_tactic(tactic_str, result_json_str): + """Record a successful tactic application.""" + import json + if not _recording_path: + return + try: + result = json.loads(result_json_str) + except (json.JSONDecodeError, TypeError): + return + if "error" in result: + return + total = 0 if result.get("proved") else result.get("total_goals", 0) + _recording.append({"action": "tactic", "tactic": tactic_str, "total_goals": total}) + _flush_recording() + + +def _record_backtrack(steps): + """Record a backtrack marker and remove entries from in-memory list.""" + global _recording_flushed + if not _recording_path: + return + removed = 0 + while removed < steps and _recording: + if _recording[-1]["action"] == "tactic": + _recording.pop() + if _recording_flushed > len(_recording): + _recording_flushed = len(_recording) + removed += 1 + else: + break + if removed > 0: + _recording.append({"action": "backtrack", "steps": removed}) + _flush_recording() + + +def _record_tactics_batch(tactics, result_json_str): + """Record successful tactics from an apply_tactics batch.""" + import json + if not _recording_path: + return + try: + result = json.loads(result_json_str) + except (json.JSONDecodeError, TypeError): + return + if "error" in result and "step" in result: + # step = number of tactics that succeeded before the error + succeeded = result["step"] + elif "steps" in result: + succeeded = result["steps"] + else: + return + for i, tac in enumerate(tactics[:succeeded]): + if i == succeeded - 1: + total = 0 if result.get("proved") else result.get("total_goals", 0) + else: + total = 0 + _recording.append({"action": "tactic", "tactic": tac, "total_goals": total}) + if succeeded > 0: + _flush_recording() + + +def _extract_e_tactic(code: str) -> str | None: + """Extract tactic string from 'e(TACTIC);;' pattern using paren counting.""" + stripped = code.strip() + m = re.match(r'\s*e\s*\(', stripped) + if not m: + return None + start = m.end() + depth = 1 + in_str = False + in_backtick = False + in_comment = 0 + esc = False + i = start + while i < len(stripped): + c = stripped[i] + if esc: + esc = False + i += 1 + continue + if in_comment > 0: + if c == '(' and i + 1 < len(stripped) and stripped[i + 1] == '*': + in_comment += 1 + i += 2 + elif c == '*' and i + 1 < len(stripped) and stripped[i + 1] == ')': + in_comment -= 1 + i += 2 + else: + i += 1 + continue + if c == '\\' and in_str: + esc = True + i += 1 + continue + if c == '"' and not in_backtick: + in_str = not in_str + i += 1 + continue + if c == '`' and not in_str: + in_backtick = not in_backtick + i += 1 + continue + if in_str or in_backtick: + i += 1 + continue + if c == '(' and i + 1 < len(stripped) and stripped[i + 1] == '*': + in_comment = 1 + i += 2 + continue + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + if depth == 0: + return stripped[start:i].strip() + i += 1 + return None + + +def _is_backtrack(code: str) -> bool: + """Check if code is a b() call.""" + stripped = code.strip().rstrip(';').strip() + return bool(re.match(r'^b\s*\(\s*\)\s*$', stripped)) + + +def _extract_json(output: str) -> str: + """Extract JSON from print_string output. The output contains the JSON + printed to stdout, followed by 'val it : unit = ()'.""" + stripped = _strip_ansi(output).strip() + if stripped.startswith("# "): + stripped = stripped[2:] + # Find the earliest JSON start + obj_idx = stripped.find('{') + arr_idx = stripped.find('[') + candidates = [] + if obj_idx != -1: + candidates.append((obj_idx, '{', '}')) + if arr_idx != -1: + candidates.append((arr_idx, '[', ']')) + if not candidates: + return '{"error":' + _json_quote(f"Unexpected output: {stripped[:200]}") + '}' + candidates.sort() + idx, start_char, end_char = candidates[0] + depth, in_str, esc = 0, False, False + for i in range(idx, len(stripped)): + c = stripped[i] + if esc: + esc = False + elif c == '\\': + esc = in_str + elif c == '"': + in_str = not in_str + elif not in_str: + if c == start_char: + depth += 1 + elif c == end_char: + depth -= 1 + if depth == 0: + return stripped[idx:i+1] + return '{"error":' + _json_quote(f"Unexpected output: {stripped[:200]}") + '}' + + +@mcp.tool() +def start_recording(path: str) -> str: + """Start recording proof tactics to a JSONL file. + + Args: + path: File path for the recording (e.g., "/tmp/recording.jsonl") + + Returns confirmation message. + """ + global _recording_path, _recording + with _lock: + os.makedirs(os.path.dirname(path) or ".", exist_ok=True) + _recording_path = path + _recording = [] + global _recording_flushed + _recording_flushed = 0 + open(path, 'w').close() # truncate any existing file + return f"Recording started: {path}" + + +@mcp.tool() +def stop_recording() -> str: + """Stop recording proof tactics and return the recording path. + + Returns the path to the recording file. + """ + global _recording_path + with _lock: + path = _recording_path + _recording_path = None + if path: + return f"Recording stopped: {path}" + return "No recording was active." + + +def main(): + _start_hol() + mcp.run(transport="stdio") + + +if __name__ == "__main__": + main() diff --git a/mcp/smoke_test.py b/mcp/smoke_test.py new file mode 100644 index 00000000..dff2dc0e --- /dev/null +++ b/mcp/smoke_test.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +"""Smoke test: starts hol-light-mcp as a real MCP server and runs all tools.""" + +import asyncio +import json +import sys +from mcp.client.stdio import stdio_client, StdioServerParameters +from mcp.client.session import ClientSession + +SERVER = StdioServerParameters( + command="uv", + args=["run", "--directory", __import__("os").path.dirname(__import__("os").path.abspath(__file__)), "hol-light-mcp"], +) + +passed = 0 +failed = 0 + + +def check(name, condition, detail=""): + global passed, failed + if condition: + passed += 1 + print(f" ✓ {name}") + else: + failed += 1 + print(f" ✗ {name}") + if detail: + print(f" {detail[:200]}") + + +async def main(): + print("Starting hol-light-mcp server...") + async with stdio_client(SERVER) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + + # Check tools registered + tools = await session.list_tools() + tool_names = set(t.name for t in tools.tools) + expected_tools = {"apply_tactic", "apply_tactics", "backtrack", "eval", "goal_state", "hol_help", "hol_interrupt", "hol_load", "hol_restart", "hol_status", "hol_type", "prove", "search_theorems", "set_goal", "start_recording", "stop_recording"} + check("tools registered", expected_tools.issubset(tool_names), + f"missing: {expected_tools - tool_names}") + + # eval — basic arithmetic (now returns JSON) + r = await session.call_tool("eval", {"code": "ARITH_RULE `1 + 1 = 2`"}) + ev = json.loads(r.content[0].text) + check("eval: ARITH_RULE success", ev["success"] is True, str(ev)) + check("eval: ARITH_RULE output", "|- 1 + 1 = 2" in ev["output"], ev["output"]) + check("eval: has time_seconds", isinstance(ev["time_seconds"], float), str(ev)) + check("eval: has full_output_chars", isinstance(ev["full_output_chars"], int), str(ev)) + + # eval — truncation + r = await session.call_tool("eval", {"code": 'search [name "ADD"]', "max_output_chars": 100}) + ev = json.loads(r.content[0].text) + check("eval: truncation works", ev["output_truncated"] is True and ev["full_output_chars"] > 100, str(ev)) + + # goal_state — no goal set + r = await session.call_tool("goal_state", {}) + gs = json.loads(r.content[0].text) + check("goal_state: empty", gs["goals"] == [] and gs["total_goals"] == 0, str(gs)) + + # Set a goal via set_goal + r = await session.call_tool("set_goal", {"goal": "`!n. n + 0 = n`"}) + gs = json.loads(r.content[0].text) + check("set_goal: returns goal", len(gs["goals"]) == 1, str(gs)) + + # goal_state — should have one goal + r = await session.call_tool("goal_state", {}) + gs = json.loads(r.content[0].text) + check("goal_state: has goal", len(gs["goals"]) == 1, str(gs)) + check("goal_state: conclusion", "n + 0 = n" in gs["goals"][0]["conclusion"], str(gs)) + + # apply_tactic — GEN_TAC + r = await session.call_tool("apply_tactic", {"tactic": "GEN_TAC"}) + gs = json.loads(r.content[0].text) + check("apply_tactic: GEN_TAC", len(gs["goals"]) >= 1, str(gs)) + + # backtrack + r = await session.call_tool("backtrack", {"steps": 1}) + gs = json.loads(r.content[0].text) + check("backtrack: restored", "n + 0 = n" in gs["goals"][0]["conclusion"], str(gs)) + + # apply_tactic — complete the proof + r = await session.call_tool("apply_tactic", {"tactic": "GEN_TAC THEN ARITH_TAC"}) + result = json.loads(r.content[0].text) + check("apply_tactic: proof complete", result.get("proved") == True, str(result)) + check("apply_tactic: theorem", "n + 0 = n" in result.get("theorem", ""), str(result)) + + # apply_tactic — error case + await session.call_tool("eval", {"code": "g `T`"}) + r = await session.call_tool("apply_tactic", {"tactic": "FAKE_NONEXISTENT_TAC"}) + result = json.loads(r.content[0].text) + check("apply_tactic: error", "error" in result, str(result)) + + # search_theorems + r = await session.call_tool("search_theorems", {"name": "ADD_SYM", "limit": 5}) + results = json.loads(r.content[0].text) + check("search_theorems: found", len(results) > 0, str(results)) + check("search_theorems: has name", any("ADD_SYM" in entry["name"] for entry in results), str(results)) + + # hol_type + r = await session.call_tool("hol_type", {"term": "`1 + 1`"}) + check("hol_type", "num" in r.content[0].text, r.content[0].text) + + # hol_load (now returns JSON) + r = await session.call_tool("hol_load", {"file": "Library/iter.ml"}) + hl = json.loads(r.content[0].text) + check("hol_load: success", hl["success"] is True, str(hl)) + check("hol_load: has time", isinstance(hl["time_seconds"], float), str(hl)) + + # hol_status + r = await session.call_tool("hol_status", {}) + status = json.loads(r.content[0].text) + check("hol_status: alive", status["alive"] is True, str(status)) + check("hol_status: has pid", isinstance(status["pid"], int), str(status)) + check("hol_status: has max_output_chars", isinstance(status["max_output_chars"], int), str(status)) + + # hol_help + r = await session.call_tool("hol_help", {}) + check("hol_help", "## Core tactics" in r.content[0].text, r.content[0].text[:200]) + + # hol_interrupt + r = await session.call_tool("hol_interrupt", {}) + check("hol_interrupt", "Interrupt sent" in r.content[0].text or "No HOL Light process" in r.content[0].text, r.content[0].text) + + # prove tool + r = await session.call_tool("prove", {"goal": "`!n. 0 + n = n`", "tactic": "GEN_TAC THEN REWRITE_TAC[ADD]"}) + result = json.loads(r.content[0].text) + check("prove: success", result.get("proved") == True, str(result)) + check("prove: theorem", "0 + n = n" in result.get("theorem", ""), str(result)) + + r = await session.call_tool("prove", {"goal": "`!n. 0 + n = n`", "tactic": "REWRITE_TAC[]"}) + result = json.loads(r.content[0].text) + check("prove: error", "error" in result, str(result)) + + # apply_tactics tool + await session.call_tool("eval", {"code": "g `!n. n + 0 = n`"}) + r = await session.call_tool("apply_tactics", {"tactics": ["GEN_TAC", "ARITH_TAC"]}) + result = json.loads(r.content[0].text) + check("apply_tactics: proof complete", result.get("proved") == True, str(result)) + check("apply_tactics: steps", result.get("steps") == 2, str(result)) + + # eval with per-call timeout + r = await session.call_tool("eval", {"code": "1 + 1", "timeout": 30}) + ev = json.loads(r.content[0].text) + check("eval: custom timeout", "2" in ev["output"], ev["output"]) + + # start_recording / stop_recording + import tempfile, os + rec_path = os.path.join(tempfile.mkdtemp(), "test_recording.jsonl") + r = await session.call_tool("start_recording", {"path": rec_path}) + check("start_recording", "Recording started" in r.content[0].text, r.content[0].text) + await session.call_tool("eval", {"code": "g `!n. n + 0 = n`"}) + await session.call_tool("apply_tactic", {"tactic": "GEN_TAC THEN ARITH_TAC"}) + r = await session.call_tool("stop_recording", {}) + check("stop_recording", "Recording stopped" in r.content[0].text, r.content[0].text) + check("recording file exists", os.path.exists(rec_path), rec_path) + with open(rec_path) as f: + entries = [json.loads(line) for line in f if line.strip()] + check("recording has entries", len(entries) > 0 and entries[0]["action"] == "tactic", str(entries[:2])) + + # hol_restart (run last — kills the process) + r = await session.call_tool("hol_restart", {}) + check("hol_restart", "restarted" in r.content[0].text.lower(), r.content[0].text) + r = await session.call_tool("eval", {"code": "1 + 1"}) + ev = json.loads(r.content[0].text) + check("eval after restart", "2" in ev["output"], ev["output"]) + + print(f"\n{passed}/{passed + failed} passed") + return failed == 0 + + +if __name__ == "__main__": + success = asyncio.run(main()) + sys.exit(0 if success else 1) diff --git a/mcp/test_server.py b/mcp/test_server.py new file mode 100644 index 00000000..e15b7ba2 --- /dev/null +++ b/mcp/test_server.py @@ -0,0 +1,415 @@ +"""Integration tests for HOL Light MCP server. + +These tests start a real HOL Light process (~75s cold start, ~2s with checkpoint), +shared across all tests via module-scoped fixture. +""" + +import json +import os +import pytest +import server + + +@pytest.fixture(scope="module", autouse=True) +def hol_process(): + """Start HOL Light once for all tests.""" + result, _ = server._eval_code("1 + 1") + assert "2" in result, f"HOL Light failed to start: {result[:200]}" + yield + if server._proc and server._proc.poll() is None: + server._proc.terminate() + + +# --- helpers --- + +def _eval_output(code: str, **kwargs) -> str: + """Call eval tool and return the output field from the JSON response.""" + return json.loads(server.eval(code, **kwargs))["output"] + + +# --- eval --- + +def test_arith_rule(): + r = json.loads(server.eval("ARITH_RULE `1 + 1 = 2`")) + assert r["success"] is True + assert "|- 1 + 1 = 2" in r["output"] + assert r["output_truncated"] is False + assert isinstance(r["time_seconds"], float) + assert isinstance(r["full_output_chars"], int) + + +def test_taut(): + r = json.loads(server.eval("TAUT `p /\\ q ==> q /\\ p`")) + assert r["success"] is True + assert "|- p /\\ q ==> q /\\ p" in r["output"] + + +def test_auto_appends_double_semicolon(): + assert "5" in _eval_output("2 + 3") + + +def test_error_handling(): + r = json.loads(server.eval("this_does_not_exist")) + assert r["success"] is False + assert "Unbound" in r["output"] or "Error" in r["output"] + + +def test_multi_statement(): + assert "43" in _eval_output("let x = 42;;\nx + 1") + + +def test_search(): + assert "ADD_SYM" in _eval_output('search [name "ADD_SYM"]') + + +def test_prove(): + output = _eval_output('prove(`!n. 0 + n = n`, GEN_TAC THEN REWRITE_TAC[ADD])') + assert "val it : thm" in output + assert "0 + n = n" in output + + +def test_eval_truncation(): + # Generate large output and verify truncation + r = json.loads(server.eval('search [name "ADD"]', max_output_chars=100)) + assert r["output_truncated"] is True + assert r["full_output_chars"] > 100 + assert r["output"].endswith("... [truncated]") + assert len(r["output"]) <= 200 # 100 + marker + + +def test_eval_default_truncation(): + # Default limit should be MAX_OUTPUT_CHARS from config + r = json.loads(server.eval("1 + 1")) + assert "full_output_chars" in r + + +# --- structured tools --- + +def test_goal_state_empty(): + result = server._eval_json("mcp_json_goalstate ()")[0] + gs = json.loads(server._extract_json(result)) + assert gs["goals"] == [] + + +def test_set_goal_and_goal_state(): + server._eval_code("g `!n. n + 0 = n`") + result = server._eval_json("mcp_json_goalstate ()")[0] + gs = json.loads(server._extract_json(result)) + assert len(gs["goals"]) == 1 + assert "n + 0 = n" in gs["goals"][0]["conclusion"] + + +def test_apply_tactic_and_prove(): + server._eval_code("g `!n. n + 0 = n`") + server._eval_code("e(GEN_TAC THEN ARITH_TAC)") + result = server._eval_json("mcp_json_after_tactic ()")[0] + parsed = json.loads(server._extract_json(result)) + assert parsed.get("proved") is True + assert "n + 0 = n" in parsed["theorem"] + + +def test_backtrack(): + server._eval_code("g `!n. n + 0 = n`") + server._eval_code("e(GEN_TAC)") + result = server._eval_json("mcp_json_backtrack 1")[0] + gs = json.loads(server._extract_json(result)) + assert "n + 0 = n" in gs["goals"][0]["conclusion"] + + +def test_search_theorems(): + result = server._eval_json('mcp_json_search "ADD_SYM" 5')[0] + results = json.loads(server._extract_json(result)) + assert len(results) > 0 + assert any("ADD_SYM" in r["name"] for r in results) + + +# --- hol_status --- + +def test_hol_status_alive(): + result = json.loads(server.hol_status()) + assert result["alive"] is True + assert isinstance(result["pid"], int) + assert isinstance(result["checkpoint"], str) + assert result["config"] is None or isinstance(result["config"], str) + assert result["uptime_seconds"] > 0 + assert isinstance(result["timeout"], int) + assert isinstance(result["max_output_chars"], int) + + +def test_hol_status_reports_checkpoint_name(): + result = json.loads(server.hol_status()) + assert result["checkpoint"] == server.CHECKPOINT_NAME + + +# --- per-call timeout --- + +def test_eval_with_custom_timeout(): + r = json.loads(server.eval("1 + 1", timeout=30)) + assert r["success"] is True + assert "2" in r["output"] + + +def test_apply_tactic_with_custom_timeout(): + server._eval_code("g `!n. n + 0 = n`") + result = json.loads(server.apply_tactic("GEN_TAC THEN ARITH_TAC", timeout=30)) + assert result.get("proved") is True + + +def test_eval_timeout_expires(): + r = json.loads(server.eval("let rec loop () = loop () in loop ()", timeout=1)) + assert r["success"] is False or "timeout" in r["output"].lower() + server.hol_restart() + + +# --- hol_restart --- + +def test_hol_restart(): + old_pid = server._proc.pid + result = server.hol_restart() + assert "restarted" in result.lower() + assert server._proc.poll() is None + assert server._proc.pid != old_pid + r = json.loads(server.eval("1 + 1")) + assert "2" in r["output"] + + +# --- prove tool --- + +def test_prove_tool_success(): + result = json.loads(server.prove("`!n. 0 + n = n`", "GEN_TAC THEN REWRITE_TAC[ADD]")) + assert result["proved"] is True + assert "0 + n = n" in result["theorem"] + + +def test_prove_tool_error(): + result = json.loads(server.prove("`!n. 0 + n = n`", "REWRITE_TAC[]")) + assert "error" in result + + +# --- apply_tactics tool --- + +def test_apply_tactics_completes_proof(): + server._eval_code("g `!n. n + 0 = n`") + result = json.loads(server.apply_tactics(["GEN_TAC", "ARITH_TAC"])) + assert result["proved"] is True + assert "n + 0 = n" in result["theorem"] + assert result["steps"] == 2 + + +def test_apply_tactics_partial(): + server._eval_code("g `!m n. m + n = n + m`") + result = json.loads(server.apply_tactics(["GEN_TAC", "GEN_TAC"])) + assert "goals" in result + assert result["steps"] == 2 + + +def test_apply_tactics_error_stops(): + server._eval_code("g `T /\\ T`") + result = json.loads(server.apply_tactics(["CONJ_TAC", "CONJ_TAC"])) + assert "error" in result + assert result["step"] == 1 + + +def test_apply_tactics_empty(): + result = json.loads(server.apply_tactics([])) + assert "error" in result + + +# --- goal_state tool --- + +def test_goal_state_tool_empty(): + server._eval_code("g `T`") + server._eval_code("e(REWRITE_TAC[])") + result = json.loads(server.goal_state()) + assert "goals" in result + assert "total_goals" in result + + +def test_goal_state_tool_with_goal(): + server.set_goal("`T /\\ T`") + result = json.loads(server.goal_state()) + assert len(result["goals"]) >= 1 + + +# --- set_goal tool --- + +def test_set_goal_tool(): + result = json.loads(server.set_goal("`!n. n + 0 = n`")) + assert len(result["goals"]) == 1 + assert "n + 0 = n" in result["goals"][0]["conclusion"] + + +# --- backtrack tool --- + +def test_backtrack_tool(): + server.set_goal("`!n. n + 0 = n`") + server.apply_tactic("GEN_TAC") + result = json.loads(server.backtrack()) + assert "n + 0 = n" in result["goals"][0]["conclusion"] + assert "n" in result["goals"][0]["conclusion"] + + +# --- search_theorems tool --- + +def test_search_theorems_tool(): + results = json.loads(server.search_theorems("ADD_SYM", limit=5)) + assert len(results) > 0 + assert any("ADD_SYM" in r["name"] for r in results) + + +def test_search_theorems_tool_limit(): + results = json.loads(server.search_theorems("ADD", limit=3)) + assert len(results) <= 3 + + +# --- hol_type tool --- + +def test_hol_type_tool(): + result = server.hol_type("`1 + 1`") + assert "num" in result + + +def test_hol_type_tool_bool(): + result = server.hol_type("`T`") + assert "bool" in result + + +# --- hol_load tool --- + +def test_hol_load_tool(): + result = json.loads(server.hol_load("Library/iter.ml")) + assert result["success"] is True + assert result["file"] == "Library/iter.ml" + assert isinstance(result["time_seconds"], float) + + +def test_hol_load_tool_error(): + result = json.loads(server.hol_load("nonexistent_file_12345.ml")) + assert result["success"] is False + assert "error" in result + assert result["file"] == "nonexistent_file_12345.ml" + + +# --- hol_interrupt tool --- + +def test_hol_interrupt_no_hang(): + result = server.hol_interrupt() + assert "Interrupt sent" in result or "No HOL Light process" in result + + +# --- hol_help tool --- + +def test_hol_help_tool(): + result = server.hol_help() + assert "## Core tactics" in result + assert "prove" in result.lower() + + +# --- start_recording / stop_recording tools --- + +def test_start_recording(tmp_path): + path = str(tmp_path / "rec.jsonl") + result = server.start_recording(path) + assert "Recording started" in result + assert os.path.exists(path) + server.stop_recording() + + +def test_stop_recording_returns_path(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + result = server.stop_recording() + assert path in result + + +def test_stop_recording_when_inactive(): + result = server.stop_recording() + assert "No recording" in result + + +def test_recording_captures_apply_tactic(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + server.set_goal("`!n. n + 0 = n`") + server.apply_tactic("GEN_TAC THEN ARITH_TAC") + server.stop_recording() + with open(path) as f: + entries = [json.loads(line) for line in f if line.strip()] + assert len(entries) == 1 + assert entries[0]["action"] == "tactic" + assert entries[0]["tactic"] == "GEN_TAC THEN ARITH_TAC" + + +def test_recording_backtrack_removes_entry(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + server.set_goal("`!n. n + 0 = n`") + server.apply_tactic("GEN_TAC") + server.backtrack() + server.stop_recording() + with open(path) as f: + entries = [json.loads(line) for line in f if line.strip()] + assert len(entries) == 2 + assert entries[0]["action"] == "tactic" + assert entries[1]["action"] == "backtrack" + assert entries[1]["steps"] == 1 + + +def test_recording_skips_failed_tactic(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + server.set_goal("`T`") + server.apply_tactic("FAKE_NONEXISTENT_TAC") + server.stop_recording() + with open(path) as f: + entries = [json.loads(line) for line in f if line.strip()] + assert len(entries) == 0 + + +def test_start_recording_creates_parent_dirs(tmp_path): + path = str(tmp_path / "nested" / "dir" / "rec.jsonl") + server.start_recording(path) + assert os.path.exists(path) + server.stop_recording() + + +def test_recording_captures_apply_tactics_batch(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + server.set_goal("`!n. n + 0 = n`") + server.apply_tactics(["GEN_TAC", "ARITH_TAC"]) + server.stop_recording() + with open(path) as f: + entries = [json.loads(line) for line in f if line.strip()] + assert len(entries) == 2 + assert entries[0]["tactic"] == "GEN_TAC" + assert entries[1]["tactic"] == "ARITH_TAC" + + +def test_recording_captures_eval_e_tactic(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + server.set_goal("`!n. n + 0 = n`") + server.eval("e(GEN_TAC);;") + server.stop_recording() + with open(path) as f: + entries = [json.loads(line) for line in f if line.strip()] + assert len(entries) == 1 + assert entries[0]["tactic"] == "GEN_TAC" + + +def test_recording_backtrack_removes_last(tmp_path): + path = str(tmp_path / "rec.jsonl") + server.start_recording(path) + server.set_goal("`!m n. m + n = n + m`") + server.apply_tactic("GEN_TAC") + server.apply_tactic("GEN_TAC") + server.backtrack() + server.stop_recording() + with open(path) as f: + entries = [json.loads(line) for line in f if line.strip()] + assert len(entries) == 3 + assert entries[0]["tactic"] == "GEN_TAC" + assert entries[1]["tactic"] == "GEN_TAC" + assert entries[2]["action"] == "backtrack" + assert entries[2]["steps"] == 1 diff --git a/mcp/uv.lock b/mcp/uv.lock new file mode 100644 index 00000000..0f131ddf --- /dev/null +++ b/mcp/uv.lock @@ -0,0 +1,739 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "click" +version = "8.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, + { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, + { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" }, + { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, + { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, + { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, + { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, + { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, + { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, + { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" }, + { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, + { url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" }, + { url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hol-light-mcp" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "mcp" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [{ name = "mcp", specifier = ">=1.26.0" }] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=9.0.2" }] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "mcp" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/eb/c0cfc62075dc6e1ec1c64d352ae09ac051d9334311ed226f1f425312848a/mcp-1.27.0.tar.gz", hash = "sha256:d3dc35a7eec0d458c1da4976a48f982097ddaab87e278c5511d5a4a56e852b83", size = 607509, upload-time = "2026-04-02T14:48:08.88Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/46/f6b4ad632c67ef35209a66127e4bddc95759649dd595f71f13fba11bdf9a/mcp-1.27.0-py3-none-any.whl", hash = "sha256:5ce1fa81614958e267b21fb2aa34e0aea8e2c6ede60d52aba45fd47246b4d741", size = 215967, upload-time = "2026-04-02T14:48:07.24Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.24" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/45/e23b5dc14ddb9918ae4a625379506b17b6f8fc56ca1d82db62462f59aea6/python_multipart-0.0.24.tar.gz", hash = "sha256:9574c97e1c026e00bc30340ef7c7d76739512ab4dfd428fec8c330fa6a5cc3c8", size = 37695, upload-time = "2026-04-05T20:49:13.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/73/89930efabd4da63cea44a3f438aeb753d600123570e6d6264e763617a9ce/python_multipart-0.0.24-py3-none-any.whl", hash = "sha256:9b110a98db707df01a53c194f0af075e736a770dc5058089650d70b4a182f950", size = 24420, upload-time = "2026-04-05T20:49:12.555Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/8c/f9290339ef6d79badbc010f067cd769d6601ec11a57d78569c683fb4dd87/sse_starlette-3.3.4.tar.gz", hash = "sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1", size = 32427, upload-time = "2026-03-29T09:00:23.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/7f/3de5402f39890ac5660b86bcf5c03f9d855dad5c4ed764866d7b592b46fd/sse_starlette-3.3.4-py3-none-any.whl", hash = "sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1", size = 14330, upload-time = "2026-03-29T09:00:21.846Z" }, +] + +[[package]] +name = "starlette" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl", hash = "sha256:ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89", size = 69425, upload-time = "2026-04-06T09:23:21.524Z" }, +] diff --git a/unit_tests.ml b/unit_tests.ml index a3601771..56dc8fc0 100644 --- a/unit_tests.ml +++ b/unit_tests.ml @@ -224,6 +224,58 @@ let test_fn = function set_jrh_lexer;; +(* ------------------------------------------------------------------------- *) +(* Test record types (Library/components.ml and Library/records.ml). *) +(* ------------------------------------------------------------------------- *) + +needs "Library/records.ml";; + +let point_INDUCT,point_RECURSION,point_COMPONENTS = + define_auto_record_type + "test_point = { xcoord: num; ycoord: num }";; + +(* Check that the induction, recursion and component theorems exist *) +let _ = concl point_INDUCT;; +let _ = concl point_RECURSION;; +let _ = concl point_COMPONENTS;; + +(* Check that read/write theorems were generated *) +assert (length (get_record_components point_COMPONENTS) = 2);; + +(* Check read-write laws *) +let rw_thms = record_read_write_thms (point_INDUCT, point_COMPONENTS);; +assert (length rw_thms = 2);; + +(* Check strongly_valid_component theorems *) +let sv_thms = + record_strongly_valid_component_thms (point_INDUCT, point_COMPONENTS);; +assert (length sv_thms = 2);; + +(* Check orthogonality theorems (2 fields => 2 ordered pairs) *) +let orth_thms = record_orthogonality_thms (point_INDUCT, point_COMPONENTS);; +assert (length orth_thms = 2);; + +(* Prove: writing xcoord updates it correctly *) +let XCOORD_READ_WRITE = prove + (`!x s:test_point. read xcoord (write xcoord x s) = x`, + GEN_TAC THEN MATCH_MP_TAC point_INDUCT THEN + REWRITE_TAC[point_COMPONENTS]);; + +(* Prove: writing xcoord leaves ycoord intact *) +let YCOORD_INTACT = prove + (`!x s:test_point. read ycoord (write xcoord x s) = read ycoord s`, + GEN_TAC THEN MATCH_MP_TAC point_INDUCT THEN + REWRITE_TAC[point_COMPONENTS]);; + +(* Test a record with 3 fields *) +let triple_INDUCT,triple_RECURSION,triple_COMPONENTS = + define_auto_record_type + "test_triple = { first: num; second: bool; third: num }";; + +assert (length (get_record_components triple_COMPONENTS) = 3);; +assert (length (record_orthogonality_thms + (triple_INDUCT, triple_COMPONENTS)) = 6);; + (* ------------------------------------------------------------------------- *) (* Test check_axioms. *) (* ------------------------------------------------------------------------- *)