1use super::functions::*;
6
7pub struct FirstFundamentalForm {
14 pub e: f64,
15 pub f: f64,
16 pub g: f64,
17}
18impl FirstFundamentalForm {
19 pub fn new(e: f64, f: f64, g: f64) -> Self {
20 Self { e, f, g }
21 }
22 pub fn det(&self) -> f64 {
24 self.e * self.g - self.f * self.f
25 }
26 pub fn area_element(&self) -> f64 {
28 self.det().sqrt()
29 }
30 pub fn is_positive_definite(&self) -> bool {
32 self.det() > 0.0 && self.e > 0.0
33 }
34}
35pub struct GeodesicIntegrator {
40 pub pos: [f64; 2],
42 pub vel: [f64; 2],
44}
45impl GeodesicIntegrator {
46 pub fn new(pos: [f64; 2], vel: [f64; 2]) -> Self {
47 Self { pos, vel }
48 }
49 pub fn step(&mut self, metric: &RiemannianMetric2D, gamma: &[[[f64; 2]; 2]; 2], dt: f64) {
53 let acc = metric.geodesic_acceleration(gamma, &self.vel);
54 self.pos[0] += self.vel[0] * dt;
55 self.pos[1] += self.vel[1] * dt;
56 self.vel[0] += acc[0] * dt;
57 self.vel[1] += acc[1] * dt;
58 }
59 pub fn integrate(
61 &mut self,
62 metric: &RiemannianMetric2D,
63 gamma: &[[[f64; 2]; 2]; 2],
64 dt: f64,
65 n: usize,
66 ) -> Vec<[f64; 2]> {
67 let mut traj = Vec::with_capacity(n + 1);
68 traj.push(self.pos);
69 for _ in 0..n {
70 self.step(metric, gamma, dt);
71 traj.push(self.pos);
72 }
73 traj
74 }
75}
76pub struct Torus {
78 pub major_radius: f64,
79 pub minor_radius: f64,
80}
81impl Torus {
82 pub fn gaussian_curvature_at(&self, theta: f64) -> f64 {
86 let r = self.minor_radius;
87 let big_r = self.major_radius;
88 theta.cos() / (r * (big_r + r * theta.cos()))
89 }
90 pub fn euler_characteristic(&self) -> i32 {
92 0
93 }
94 pub fn area(&self) -> f64 {
96 4.0 * std::f64::consts::PI * std::f64::consts::PI * self.major_radius * self.minor_radius
97 }
98}
99pub struct Sphere {
101 pub radius: f64,
102}
103impl Sphere {
104 pub fn gaussian_curvature(&self) -> f64 {
106 1.0 / (self.radius * self.radius)
107 }
108 pub fn euler_characteristic(&self) -> i32 {
110 2
111 }
112 pub fn area(&self) -> f64 {
114 4.0 * std::f64::consts::PI * self.radius * self.radius
115 }
116 pub fn first_fundamental_form_equator(&self) -> FirstFundamentalForm {
119 let r2 = self.radius * self.radius;
120 FirstFundamentalForm::new(r2, 0.0, r2)
121 }
122 pub fn second_fundamental_form_equator(&self) -> SecondFundamentalForm {
124 SecondFundamentalForm::new(self.radius, 0.0, self.radius)
125 }
126}
127pub struct SecondFundamentalForm {
134 pub l: f64,
135 pub m: f64,
136 pub n: f64,
137}
138impl SecondFundamentalForm {
139 pub fn new(l: f64, m: f64, n: f64) -> Self {
140 Self { l, m, n }
141 }
142 pub fn gaussian_curvature(&self, first: &FirstFundamentalForm) -> f64 {
144 let det_first = first.det();
145 if det_first.abs() < 1e-12 {
146 return 0.0;
147 }
148 (self.l * self.n - self.m * self.m) / det_first
149 }
150 pub fn mean_curvature(&self, first: &FirstFundamentalForm) -> f64 {
152 let det_first = first.det();
153 if det_first.abs() < 1e-12 {
154 return 0.0;
155 }
156 (first.e * self.n - 2.0 * first.f * self.m + first.g * self.l) / (2.0 * det_first)
157 }
158 pub fn principal_curvatures(&self, first: &FirstFundamentalForm) -> (f64, f64) {
162 let h = self.mean_curvature(first);
163 let k = self.gaussian_curvature(first);
164 let discriminant = (h * h - k).max(0.0);
165 let sqrt_disc = discriminant.sqrt();
166 (h - sqrt_disc, h + sqrt_disc)
167 }
168}
169#[allow(dead_code)]
174pub struct LorentzianMetric2D {
175 pub c_sq: f64,
177}
178#[allow(dead_code)]
179impl LorentzianMetric2D {
180 pub fn minkowski() -> Self {
182 Self { c_sq: 1.0 }
183 }
184 pub fn metric_tensor(&self) -> [[f64; 2]; 2] {
186 [[-self.c_sq, 0.0], [0.0, 1.0]]
187 }
188 pub fn inner_product(&self, u: &[f64; 2], v: &[f64; 2]) -> f64 {
190 -self.c_sq * u[0] * v[0] + u[1] * v[1]
191 }
192 pub fn is_timelike(&self, v: &[f64; 2]) -> bool {
194 self.inner_product(v, v) < 0.0
195 }
196 pub fn is_spacelike(&self, v: &[f64; 2]) -> bool {
198 self.inner_product(v, v) > 0.0
199 }
200 pub fn is_null(&self, v: &[f64; 2]) -> bool {
202 self.inner_product(v, v).abs() < 1e-12
203 }
204 pub fn proper_time_element(&self, vx: f64) -> f64 {
209 let c_sq = self.c_sq;
210 let val = c_sq - vx * vx;
211 if val <= 0.0 {
212 0.0
213 } else {
214 val.sqrt() / c_sq.sqrt()
215 }
216 }
217}
218pub struct HodgeStar {
224 pub dim: usize,
226}
227impl HodgeStar {
228 pub fn new(dim: usize) -> Self {
229 Self { dim }
230 }
231 pub fn apply(&self, form: &DifferentialFormWedge) -> DifferentialFormWedge {
237 assert_eq!(
238 form.dim, self.dim,
239 "form dimension must match HodgeStar dimension"
240 );
241 let n = self.dim;
242 let mut result_terms = Vec::new();
243 for (coeff, indices) in &form.terms {
244 let mut complement: Vec<usize> = (0..n).filter(|j| !indices.contains(j)).collect();
245 complement.sort();
246 let mut perm: Vec<usize> = indices.clone();
247 perm.extend_from_slice(&complement);
248 let sign = permutation_sign(&perm);
249 result_terms.push((coeff * sign, complement));
250 }
251 DifferentialFormWedge::new(self.dim, result_terms)
252 }
253 pub fn inner_product(
258 &self,
259 alpha: &DifferentialFormWedge,
260 beta: &DifferentialFormWedge,
261 ) -> f64 {
262 let star_beta = self.apply(beta);
263 let wedge = alpha.wedge(&star_beta);
264 let full_indices: Vec<usize> = (0..self.dim).collect();
265 wedge
266 .terms
267 .iter()
268 .find(|(_, i)| *i == full_indices)
269 .map_or(0.0, |(c, _)| *c)
270 }
271}
272#[allow(dead_code)]
278pub struct WeylTensorComputer {
279 pub dim: usize,
281 pub scalar_curvature: f64,
283}
284#[allow(dead_code)]
285impl WeylTensorComputer {
286 pub fn new(dim: usize, scalar_curvature: f64) -> Self {
287 Self {
288 dim,
289 scalar_curvature,
290 }
291 }
292 pub fn weyl_vanishes_in_dim3(&self) -> bool {
294 self.dim == 3
295 }
296 pub fn ricci_coefficient(&self) -> f64 {
298 if self.dim < 3 {
299 return 0.0;
300 }
301 -1.0 / (self.dim as f64 - 2.0)
302 }
303 pub fn metric_coefficient(&self) -> f64 {
305 let n = self.dim as f64;
306 if n < 3.0 {
307 return 0.0;
308 }
309 self.scalar_curvature / ((n - 1.0) * (n - 2.0))
310 }
311 pub fn is_conformally_flat(&self, weyl_norm: f64) -> bool {
313 weyl_norm < 1e-10
314 }
315}
316pub struct Curve3D {
318 pub points: Vec<[f64; 3]>,
319}
320impl Curve3D {
321 pub fn new(points: Vec<[f64; 3]>) -> Self {
322 Self { points }
323 }
324 pub fn length(&self) -> f64 {
326 if self.points.len() < 2 {
327 return 0.0;
328 }
329 self.points
330 .windows(2)
331 .map(|w| norm3(&sub3(&w[1], &w[0])))
332 .sum()
333 }
334 pub fn curvature_at(&self, i: usize) -> f64 {
336 let n = self.points.len();
337 if i == 0 || i + 1 >= n {
338 return 0.0;
339 }
340 let prev = &self.points[i - 1];
341 let curr = &self.points[i];
342 let next = &self.points[i + 1];
343 let d1 = scale3(&sub3(next, prev), 0.5);
344 let d2 = sub3(&add3(next, prev), &scale3(curr, 2.0));
345 let cross = cross3(&d2, &d1);
346 let cross_norm = norm3(&cross);
347 let d1_norm = norm3(&d1);
348 if d1_norm < 1e-12 {
349 return 0.0;
350 }
351 cross_norm / (d1_norm * d1_norm * d1_norm)
352 }
353 pub fn torsion_at(&self, i: usize) -> f64 {
355 let n = self.points.len();
356 if i < 2 || i + 2 >= n {
357 return 0.0;
358 }
359 let prev2 = &self.points[i - 2];
360 let prev1 = &self.points[i - 1];
361 let curr = &self.points[i];
362 let next1 = &self.points[i + 1];
363 let next2 = &self.points[i + 2];
364 let d1 = scale3(&sub3(next1, prev1), 0.5);
365 let d2 = sub3(&add3(next1, prev1), &scale3(curr, 2.0));
366 let d2_prev = sub3(&add3(curr, prev2), &scale3(prev1, 2.0));
367 let d2_next = sub3(&add3(next2, curr), &scale3(next1, 2.0));
368 let d3 = scale3(&sub3(&d2_next, &d2_prev), 0.5);
369 let cross_d1_d2 = cross3(&d1, &d2);
370 let denom = dot3(&cross_d1_d2, &cross_d1_d2);
371 if denom < 1e-12 {
372 return 0.0;
373 }
374 dot3(&cross_d1_d2, &d3) / denom
375 }
376 pub fn is_closed(&self) -> bool {
378 if self.points.len() < 2 {
379 return false;
380 }
381 let first = self
382 .points
383 .first()
384 .expect("points has at least 2 elements: checked by early return");
385 let last = self
386 .points
387 .last()
388 .expect("points has at least 2 elements: checked by early return");
389 norm3(&sub3(last, first)) < 1e-6
390 }
391 pub fn frenet_frame_at(&self, i: usize) -> Option<([f64; 3], [f64; 3], [f64; 3])> {
393 let n = self.points.len();
394 if i == 0 || i + 1 >= n {
395 return None;
396 }
397 let prev = &self.points[i - 1];
398 let next = &self.points[i + 1];
399 let curr = &self.points[i];
400 let d1 = scale3(&sub3(next, prev), 0.5);
401 let tangent = normalize3(&d1);
402 if norm3(&tangent) < 1e-12 {
403 return None;
404 }
405 let d2 = sub3(&add3(next, prev), &scale3(curr, 2.0));
406 let d2_dot_t = dot3(&d2, &tangent);
407 let d2_perp = sub3(&d2, &scale3(&tangent, d2_dot_t));
408 let normal = normalize3(&d2_perp);
409 if norm3(&normal) < 1e-12 {
410 return None;
411 }
412 let binormal = cross3(&tangent, &normal);
413 Some((tangent, normal, binormal))
414 }
415}
416#[allow(dead_code)]
418#[derive(Debug, Clone)]
419pub struct ChristoffelSymbols {
420 pub dim: usize,
421 pub gamma: Vec<Vec<Vec<f64>>>,
422}
423#[allow(dead_code)]
424impl ChristoffelSymbols {
425 pub fn new(dim: usize) -> Self {
426 let gamma = vec![vec![vec![0.0; dim]; dim]; dim];
427 ChristoffelSymbols { dim, gamma }
428 }
429 pub fn set(&mut self, k: usize, i: usize, j: usize, val: f64) {
430 self.gamma[k][i][j] = val;
431 self.gamma[k][j][i] = val;
432 }
433 pub fn get(&self, k: usize, i: usize, j: usize) -> f64 {
434 self.gamma[k][i][j]
435 }
436 pub fn is_flat(&self) -> bool {
438 self.gamma
439 .iter()
440 .all(|r| r.iter().all(|c| c.iter().all(|&v| v.abs() < 1e-12)))
441 }
442}
443#[allow(dead_code)]
445#[derive(Debug, Clone)]
446pub struct CurvatureTensor {
447 pub dim: usize,
448 pub scalar_curvature: f64,
449 pub ricci: Vec<Vec<f64>>,
450}
451#[allow(dead_code)]
452impl CurvatureTensor {
453 pub fn flat(dim: usize) -> Self {
454 CurvatureTensor {
455 dim,
456 scalar_curvature: 0.0,
457 ricci: vec![vec![0.0; dim]; dim],
458 }
459 }
460 pub fn sphere_unit(dim: usize) -> Self {
461 let r = (dim * (dim - 1)) as f64;
462 let ricci: Vec<Vec<f64>> = (0..dim)
463 .map(|i| {
464 (0..dim)
465 .map(|j| if i == j { r / dim as f64 } else { 0.0 })
466 .collect()
467 })
468 .collect();
469 CurvatureTensor {
470 dim,
471 scalar_curvature: r,
472 ricci,
473 }
474 }
475 pub fn is_einstein(&self, lambda: f64) -> bool {
476 for i in 0..self.dim {
477 for j in 0..self.dim {
478 let expected = if i == j { lambda } else { 0.0 };
479 if (self.ricci[i][j] - expected).abs() > 1e-9 {
480 return false;
481 }
482 }
483 }
484 true
485 }
486 pub fn ricci_scalar(&self) -> f64 {
487 (0..self.dim).map(|i| self.ricci[i][i]).sum()
488 }
489}
490#[allow(dead_code)]
494pub struct CalibrationChecker {
495 pub form: [[f64; 3]; 3],
497}
498#[allow(dead_code)]
499impl CalibrationChecker {
500 pub fn new(form: [[f64; 3]; 3]) -> Self {
501 Self { form }
502 }
503 pub fn area_form_r3() -> Self {
507 Self {
508 form: [[0.0, 1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
509 }
510 }
511 pub fn evaluate(&self, u: &[f64; 3], v: &[f64; 3]) -> f64 {
513 let mut sum = 0.0;
514 for i in 0..3 {
515 for j in 0..3 {
516 sum += self.form[i][j] * u[i] * v[j];
517 }
518 }
519 sum
520 }
521 pub fn area(&self, u: &[f64; 3], v: &[f64; 3]) -> f64 {
523 let c = cross3(u, v);
524 norm3(&c)
525 }
526 pub fn is_calibrated(&self, u: &[f64; 3], v: &[f64; 3]) -> bool {
528 let phi_uv = self.evaluate(u, v);
529 let area_uv = self.area(u, v);
530 if area_uv < 1e-12 {
531 return true;
532 }
533 (phi_uv / area_uv - 1.0).abs() < 1e-9
534 }
535}
536#[derive(Clone, Debug)]
542pub struct DifferentialFormWedge {
543 pub terms: Vec<(f64, Vec<usize>)>,
545 pub dim: usize,
547}
548impl DifferentialFormWedge {
549 pub fn new(dim: usize, terms: Vec<(f64, Vec<usize>)>) -> Self {
551 let mut form = Self { dim, terms };
552 form.normalize();
553 form
554 }
555 pub fn basis_1form(dim: usize, i: usize) -> Self {
557 Self::new(dim, vec![(1.0, vec![i])])
558 }
559 fn normalize(&mut self) {
561 let mut normalized: Vec<(f64, Vec<usize>)> = Vec::new();
562 for (coeff, indices) in &self.terms {
563 if *coeff == 0.0 {
564 continue;
565 }
566 let mut idx = indices.clone();
567 let mut sign = 1.0_f64;
568 let n = idx.len();
569 let mut has_dup = false;
570 for i in 0..n {
571 for j in 0..n - 1 - i {
572 if idx[j] > idx[j + 1] {
573 idx.swap(j, j + 1);
574 sign *= -1.0;
575 } else if idx[j] == idx[j + 1] {
576 has_dup = true;
577 }
578 }
579 }
580 if has_dup {
581 continue;
582 }
583 if let Some(existing) = normalized.iter_mut().find(|(_, idxs)| *idxs == idx) {
584 existing.0 += coeff * sign;
585 } else {
586 normalized.push((coeff * sign, idx));
587 }
588 }
589 normalized.retain(|(c, _)| c.abs() > 1e-14);
590 self.terms = normalized;
591 }
592 pub fn wedge(&self, other: &DifferentialFormWedge) -> DifferentialFormWedge {
594 assert_eq!(self.dim, other.dim, "dimension mismatch in wedge product");
595 let mut result_terms = Vec::new();
596 for (ca, ia) in &self.terms {
597 for (cb, ib) in &other.terms {
598 let mut indices = ia.clone();
599 indices.extend_from_slice(ib);
600 result_terms.push((ca * cb, indices));
601 }
602 }
603 DifferentialFormWedge::new(self.dim, result_terms)
604 }
605 pub fn scale(&self, s: f64) -> DifferentialFormWedge {
607 let terms = self.terms.iter().map(|(c, i)| (c * s, i.clone())).collect();
608 DifferentialFormWedge::new(self.dim, terms)
609 }
610 pub fn add(&self, other: &DifferentialFormWedge) -> DifferentialFormWedge {
612 assert_eq!(self.dim, other.dim, "dimension mismatch in form addition");
613 let mut terms = self.terms.clone();
614 terms.extend_from_slice(&other.terms);
615 DifferentialFormWedge::new(self.dim, terms)
616 }
617 pub fn degree(&self) -> usize {
619 self.terms.first().map_or(0, |(_, i)| i.len())
620 }
621 pub fn is_zero(&self) -> bool {
623 self.terms.is_empty()
624 }
625}
626#[allow(dead_code)]
631pub struct RandersFinsler {
632 pub alpha: f64,
634 pub beta: f64,
636}
637#[allow(dead_code)]
638impl RandersFinsler {
639 pub fn new(alpha: f64, beta: f64) -> Option<Self> {
641 if beta.abs() < alpha && alpha > 0.0 {
642 Some(Self { alpha, beta })
643 } else {
644 None
645 }
646 }
647 pub fn norm(&self, v: f64) -> f64 {
649 self.alpha * v.abs() + self.beta * v
650 }
651 pub fn is_strongly_convex(&self) -> bool {
653 self.alpha + self.beta > 0.0 && self.alpha - self.beta > 0.0
654 }
655 pub fn fundamental_tensor(&self, v: f64) -> f64 {
657 if v.abs() < 1e-12 {
658 return self.alpha * self.alpha;
659 }
660 let sign_v = if v > 0.0 { 1.0 } else { -1.0 };
661 let factor = self.alpha * sign_v + self.beta;
662 factor * factor
663 }
664}
665pub struct SurfacePoint {
667 pub u: f64,
668 pub v: f64,
669 pub position: [f64; 3],
670}
671#[allow(dead_code)]
673#[derive(Debug, Clone)]
674pub struct RiemannMetric {
675 pub dim: usize,
676 pub components: Vec<Vec<f64>>,
677}
678#[allow(dead_code)]
679impl RiemannMetric {
680 pub fn new(dim: usize) -> Self {
681 let components = (0..dim)
682 .map(|i| (0..dim).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
683 .collect();
684 RiemannMetric { dim, components }
685 }
686 pub fn euclidean(dim: usize) -> Self {
687 Self::new(dim)
688 }
689 pub fn set_component(&mut self, i: usize, j: usize, val: f64) {
690 self.components[i][j] = val;
691 self.components[j][i] = val;
692 }
693 pub fn det_2d(&self) -> Option<f64> {
695 if self.dim == 2 {
696 Some(
697 self.components[0][0] * self.components[1][1]
698 - self.components[0][1] * self.components[1][0],
699 )
700 } else {
701 None
702 }
703 }
704 pub fn inner_product(&self, u: &[f64], v: &[f64]) -> f64 {
705 assert_eq!(u.len(), self.dim);
706 assert_eq!(v.len(), self.dim);
707 let mut result = 0.0;
708 for i in 0..self.dim {
709 for j in 0..self.dim {
710 result += self.components[i][j] * u[i] * v[j];
711 }
712 }
713 result
714 }
715 pub fn norm(&self, v: &[f64]) -> f64 {
716 self.inner_product(v, v).sqrt()
717 }
718}
719#[allow(dead_code)]
721#[derive(Debug, Clone)]
722pub struct LieDerivative {
723 pub vector_field: String,
724 pub tensor_name: String,
725 pub result_name: String,
726}
727#[allow(dead_code)]
728impl LieDerivative {
729 pub fn new(v: &str, t: &str) -> Self {
730 LieDerivative {
731 vector_field: v.to_string(),
732 tensor_name: t.to_string(),
733 result_name: format!("L_{}({})", v, t),
734 }
735 }
736 pub fn cartan_formula() -> &'static str {
738 "L_X = i_X ∘ d + d ∘ i_X"
739 }
740 pub fn on_function(v: &str, f: &str) -> String {
742 format!("{}({})", v, f)
743 }
744}
745#[derive(Clone, Debug)]
747pub struct LieGroupSO3 {
748 pub matrix: [[f64; 3]; 3],
750}
751impl LieGroupSO3 {
752 pub fn identity() -> Self {
754 Self {
755 matrix: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
756 }
757 }
758 pub fn from_axis_angle(omega: &[f64; 3]) -> Self {
762 let theta = norm3(omega);
763 if theta < 1e-12 {
764 return Self::identity();
765 }
766 let k = [omega[0] / theta, omega[1] / theta, omega[2] / theta];
767 let s = theta.sin();
768 let c = theta.cos();
769 let one_minus_c = 1.0 - c;
770 let matrix = [
771 [
772 c + k[0] * k[0] * one_minus_c,
773 k[0] * k[1] * one_minus_c - k[2] * s,
774 k[0] * k[2] * one_minus_c + k[1] * s,
775 ],
776 [
777 k[1] * k[0] * one_minus_c + k[2] * s,
778 c + k[1] * k[1] * one_minus_c,
779 k[1] * k[2] * one_minus_c - k[0] * s,
780 ],
781 [
782 k[2] * k[0] * one_minus_c - k[1] * s,
783 k[2] * k[1] * one_minus_c + k[0] * s,
784 c + k[2] * k[2] * one_minus_c,
785 ],
786 ];
787 Self { matrix }
788 }
789 pub fn apply(&self, v: &[f64; 3]) -> [f64; 3] {
791 let r = &self.matrix;
792 [
793 r[0][0] * v[0] + r[0][1] * v[1] + r[0][2] * v[2],
794 r[1][0] * v[0] + r[1][1] * v[1] + r[1][2] * v[2],
795 r[2][0] * v[0] + r[2][1] * v[1] + r[2][2] * v[2],
796 ]
797 }
798 pub fn compose(&self, other: &LieGroupSO3) -> LieGroupSO3 {
800 let a = &self.matrix;
801 let b = &other.matrix;
802 let mut c = [[0.0f64; 3]; 3];
803 for i in 0..3 {
804 for j in 0..3 {
805 for k in 0..3 {
806 c[i][j] += a[i][k] * b[k][j];
807 }
808 }
809 }
810 LieGroupSO3 { matrix: c }
811 }
812 pub fn transpose(&self) -> LieGroupSO3 {
814 let m = &self.matrix;
815 LieGroupSO3 {
816 matrix: [
817 [m[0][0], m[1][0], m[2][0]],
818 [m[0][1], m[1][1], m[2][1]],
819 [m[0][2], m[1][2], m[2][2]],
820 ],
821 }
822 }
823 pub fn log_map(&self) -> [f64; 3] {
827 let m = &self.matrix;
828 let trace = m[0][0] + m[1][1] + m[2][2];
829 let cos_theta = ((trace - 1.0) / 2.0).clamp(-1.0, 1.0);
830 let theta = cos_theta.acos();
831 if theta.abs() < 1e-12 {
832 return [0.0, 0.0, 0.0];
833 }
834 let factor = theta / (2.0 * theta.sin());
835 [
836 factor * (m[2][1] - m[1][2]),
837 factor * (m[0][2] - m[2][0]),
838 factor * (m[1][0] - m[0][1]),
839 ]
840 }
841 pub fn is_valid(&self) -> bool {
843 let rt = self.transpose();
844 let prod = self.compose(&rt);
845 let m = &prod.matrix;
846 let identity_err = (m[0][0] - 1.0).abs()
847 + (m[1][1] - 1.0).abs()
848 + (m[2][2] - 1.0).abs()
849 + m[0][1].abs()
850 + m[0][2].abs()
851 + m[1][0].abs()
852 + m[1][2].abs()
853 + m[2][0].abs()
854 + m[2][1].abs();
855 identity_err < 1e-10
856 }
857}
858#[allow(dead_code)]
860#[derive(Debug, Clone)]
861pub struct Geodesic {
862 pub start: Vec<f64>,
863 pub end: Vec<f64>,
864 pub n_steps: usize,
865}
866#[allow(dead_code)]
867impl Geodesic {
868 pub fn new(start: Vec<f64>, end: Vec<f64>, steps: usize) -> Self {
869 assert_eq!(start.len(), end.len());
870 Geodesic {
871 start,
872 end,
873 n_steps: steps,
874 }
875 }
876 pub fn dim(&self) -> usize {
877 self.start.len()
878 }
879 pub fn point_at(&self, t: f64) -> Vec<f64> {
880 self.start
881 .iter()
882 .zip(self.end.iter())
883 .map(|(s, e)| s + t * (e - s))
884 .collect()
885 }
886 pub fn euclidean_length(&self) -> f64 {
887 self.start
888 .iter()
889 .zip(self.end.iter())
890 .map(|(s, e)| (e - s).powi(2))
891 .sum::<f64>()
892 .sqrt()
893 }
894 pub fn sample_points(&self) -> Vec<Vec<f64>> {
895 (0..=self.n_steps)
896 .map(|i| {
897 let t = i as f64 / self.n_steps as f64;
898 self.point_at(t)
899 })
900 .collect()
901 }
902}
903pub struct RiemannianMetric3D {
905 pub g: [[f64; 3]; 3],
907}
908impl RiemannianMetric3D {
909 pub fn new(g: [[f64; 3]; 3]) -> Self {
910 Self { g }
911 }
912 pub fn euclidean() -> Self {
914 Self {
915 g: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
916 }
917 }
918 pub fn det(&self) -> f64 {
920 let g = &self.g;
921 g[0][0] * (g[1][1] * g[2][2] - g[1][2] * g[2][1])
922 - g[0][1] * (g[1][0] * g[2][2] - g[1][2] * g[2][0])
923 + g[0][2] * (g[1][0] * g[2][1] - g[1][1] * g[2][0])
924 }
925 pub fn inverse(&self) -> [[f64; 3]; 3] {
927 let g = &self.g;
928 let d = self.det();
929 if d.abs() < 1e-15 {
930 return [[0.0; 3]; 3];
931 }
932 let mut inv = [[0.0f64; 3]; 3];
933 inv[0][0] = (g[1][1] * g[2][2] - g[1][2] * g[2][1]) / d;
934 inv[0][1] = (g[0][2] * g[2][1] - g[0][1] * g[2][2]) / d;
935 inv[0][2] = (g[0][1] * g[1][2] - g[0][2] * g[1][1]) / d;
936 inv[1][0] = (g[1][2] * g[2][0] - g[1][0] * g[2][2]) / d;
937 inv[1][1] = (g[0][0] * g[2][2] - g[0][2] * g[2][0]) / d;
938 inv[1][2] = (g[0][2] * g[1][0] - g[0][0] * g[1][2]) / d;
939 inv[2][0] = (g[1][0] * g[2][1] - g[1][1] * g[2][0]) / d;
940 inv[2][1] = (g[0][1] * g[2][0] - g[0][0] * g[2][1]) / d;
941 inv[2][2] = (g[0][0] * g[1][1] - g[0][1] * g[1][0]) / d;
942 inv
943 }
944 pub fn christoffel(&self, dg: &[[[f64; 3]; 3]; 3]) -> [[[f64; 3]; 3]; 3] {
948 let g_inv = self.inverse();
949 let mut gamma = [[[0.0f64; 3]; 3]; 3];
950 for k in 0..3 {
951 for i in 0..3 {
952 for j in 0..3 {
953 let mut val = 0.0;
954 for l in 0..3 {
955 let term = dg[j][l][i] + dg[i][l][j] - dg[i][j][l];
956 val += g_inv[k][l] * term;
957 }
958 gamma[k][i][j] = 0.5 * val;
959 }
960 }
961 }
962 gamma
963 }
964 pub fn volume_element(&self) -> f64 {
966 self.det().abs().sqrt()
967 }
968}
969#[allow(dead_code)]
971#[derive(Debug, Clone)]
972pub struct DifferentialForm {
973 pub degree: usize,
974 pub ambient_dim: usize,
975 pub is_closed: bool,
976 pub is_exact: bool,
977}
978#[allow(dead_code)]
979impl DifferentialForm {
980 pub fn new(degree: usize, n: usize) -> Self {
981 DifferentialForm {
982 degree,
983 ambient_dim: n,
984 is_closed: false,
985 is_exact: false,
986 }
987 }
988 pub fn zero_form(n: usize) -> Self {
989 DifferentialForm::new(0, n)
990 }
991 pub fn volume_form(n: usize) -> Self {
992 let mut f = DifferentialForm::new(n, n);
993 f.is_closed = true;
994 f
995 }
996 pub fn mark_exact(&mut self) {
998 self.is_exact = true;
999 self.is_closed = true;
1000 }
1001 pub fn space_dimension(&self) -> usize {
1003 let n = self.ambient_dim;
1004 let k = self.degree;
1005 if k > n {
1006 return 0;
1007 }
1008 let mut result = 1usize;
1009 for i in 0..k {
1010 result = result * (n - i) / (i + 1);
1011 }
1012 result
1013 }
1014 pub fn is_top_form(&self) -> bool {
1015 self.degree == self.ambient_dim
1016 }
1017}
1018#[allow(dead_code)]
1023pub struct HolonomyComputer {
1024 pub christoffel: [[[f64; 2]; 2]; 2],
1026}
1027#[allow(dead_code)]
1028impl HolonomyComputer {
1029 pub fn new(christoffel: [[[f64; 2]; 2]; 2]) -> Self {
1030 Self { christoffel }
1031 }
1032 pub fn parallel_transport_step(&self, v: &[f64; 2], dx: &[f64; 2]) -> [f64; 2] {
1036 let gamma = &self.christoffel;
1037 let mut dv = [0.0f64; 2];
1038 for k in 0..2 {
1039 for i in 0..2 {
1040 for j in 0..2 {
1041 dv[k] -= gamma[k][i][j] * v[i] * dx[j];
1042 }
1043 }
1044 }
1045 [v[0] + dv[0], v[1] + dv[1]]
1046 }
1047 pub fn holonomy_angle_square_loop(&self, eps: f64) -> f64 {
1052 let gamma = &self.christoffel;
1053 let v0 = [1.0_f64, 0.0_f64];
1054 let steps = [
1055 [eps, 0.0_f64],
1056 [0.0_f64, eps],
1057 [-eps, 0.0_f64],
1058 [0.0_f64, -eps],
1059 ];
1060 let mut v = v0;
1061 for dx in &steps {
1062 v = self.parallel_transport_step(&v, dx);
1063 }
1064 let cos_phi = v[0] * v0[0] + v[1] * v0[1];
1065 let sin_phi = v[1] * v0[0] - v[0] * v0[1];
1066 let _ = gamma;
1067 sin_phi.atan2(cos_phi)
1068 }
1069}
1070pub struct RiemannianMetric2D {
1075 pub g00: f64,
1077 pub g01: f64,
1079 pub g11: f64,
1081}
1082impl RiemannianMetric2D {
1083 pub fn new(g00: f64, g01: f64, g11: f64) -> Self {
1084 Self { g00, g01, g11 }
1085 }
1086 pub fn euclidean() -> Self {
1088 Self::new(1.0, 0.0, 1.0)
1089 }
1090 pub fn det(&self) -> f64 {
1092 self.g00 * self.g11 - self.g01 * self.g01
1093 }
1094 pub fn inverse(&self) -> [[f64; 2]; 2] {
1096 let d = self.det();
1097 if d.abs() < 1e-15 {
1098 return [[0.0; 2]; 2];
1099 }
1100 [[self.g11 / d, -self.g01 / d], [-self.g01 / d, self.g00 / d]]
1101 }
1102 pub fn christoffel(&self, dg: &[[[f64; 2]; 2]; 2]) -> [[[f64; 2]; 2]; 2] {
1107 let g_inv = self.inverse();
1108 let mut gamma = [[[0.0f64; 2]; 2]; 2];
1109 for k in 0..2 {
1110 for i in 0..2 {
1111 for j in 0..2 {
1112 let mut val = 0.0;
1113 for l in 0..2 {
1114 let term = dg[j][l][i] + dg[i][l][j] - dg[i][j][l];
1115 val += g_inv[k][l] * term;
1116 }
1117 gamma[k][i][j] = 0.5 * val;
1118 }
1119 }
1120 }
1121 gamma
1122 }
1123 pub fn geodesic_acceleration(&self, gamma: &[[[f64; 2]; 2]; 2], vel: &[f64; 2]) -> [f64; 2] {
1125 let mut acc = [0.0f64; 2];
1126 for k in 0..2 {
1127 for i in 0..2 {
1128 for j in 0..2 {
1129 acc[k] -= gamma[k][i][j] * vel[i] * vel[j];
1130 }
1131 }
1132 }
1133 acc
1134 }
1135}