Skip to main content

oxilean_std/tropical_geometry/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6/// A tropical conic in ℝ², defined by a degree-2 tropical polynomial.
7///
8/// The six coefficients correspond to the monomials:
9/// `x², xy, y², x, y, 1` (in some fixed ordering).
10#[derive(Debug, Clone)]
11pub struct TropicalConic {
12    /// The six coefficients of the degree-2 tropical polynomial.
13    pub coefficients: Vec<f64>,
14}
15/// A tropical line in ℝ² defined by the tropical polynomial `a ⊗ x ⊕ b ⊗ y ⊕ c`.
16///
17/// Classically this is `min(a + x, b + y, c)`, whose non-smooth locus is a
18/// three-ray star graph (a tropical line).
19#[derive(Debug, Clone)]
20pub struct TropicalLine {
21    /// Coefficients `(a, b, c)` in `min(a+x, b+y, c)`.
22    pub coefficients: (f64, f64, f64),
23}
24/// A lattice polytope (Newton polytope) in ℤⁿ.
25///
26/// The Newton polytope of a polynomial `f = Σ cα xα` is the convex hull in ℝⁿ
27/// of the exponent vectors `α` for which `cα ≠ 0`.
28#[derive(Debug, Clone)]
29pub struct NewtonPolytope {
30    /// The vertices of the polytope as integer vectors.
31    pub vertices: Vec<Vec<i32>>,
32    /// The ambient dimension.
33    pub dimension: usize,
34}
35impl NewtonPolytope {
36    /// Creates a new empty Newton polytope in the given ambient dimension.
37    pub fn new(dimension: usize) -> Self {
38        NewtonPolytope {
39            vertices: Vec::new(),
40            dimension,
41        }
42    }
43    /// Adds a lattice point as a potential vertex.
44    pub fn add_vertex(&mut self, v: Vec<i32>) {
45        debug_assert_eq!(v.len(), self.dimension, "vertex dimension mismatch");
46        self.vertices.push(v);
47    }
48    /// Returns `true` — all stored vertices are integer vectors by construction.
49    pub fn is_lattice_polytope(&self) -> bool {
50        true
51    }
52    /// Estimates the (normalised) volume of the polytope.
53    ///
54    /// For 1-dimensional polytopes this is the length (max - min coordinate).
55    /// For 2-dimensional polytopes this uses the shoelace formula.
56    /// Higher dimensions return 0.0 (not yet implemented).
57    pub fn volume(&self) -> f64 {
58        match self.dimension {
59            1 => {
60                if self.vertices.is_empty() {
61                    return 0.0;
62                }
63                let vals: Vec<i32> = self.vertices.iter().map(|v| v[0]).collect();
64                let mn = *vals
65                    .iter()
66                    .min()
67                    .expect("vals is non-empty: vertices.is_empty() check returned early");
68                let mx = *vals
69                    .iter()
70                    .max()
71                    .expect("vals is non-empty: vertices.is_empty() check returned early");
72                (mx - mn) as f64
73            }
74            2 => {
75                let n = self.vertices.len();
76                if n < 3 {
77                    return 0.0;
78                }
79                let mut area2 = 0i64;
80                for i in 0..n {
81                    let j = (i + 1) % n;
82                    let xi = self.vertices[i][0] as i64;
83                    let yi = self.vertices[i][1] as i64;
84                    let xj = self.vertices[j][0] as i64;
85                    let yj = self.vertices[j][1] as i64;
86                    area2 += xi * yj - xj * yi;
87                }
88                (area2.abs() as f64) / 2.0
89            }
90            _ => 0.0,
91        }
92    }
93    /// Returns the number of interior lattice points (for 2-D polytopes).
94    ///
95    /// Uses Pick's theorem: `I = A − B/2 + 1` where `A` is the area and
96    /// `B` is the number of boundary lattice points.
97    pub fn num_interior_lattice_points(&self) -> usize {
98        if self.dimension != 2 || self.vertices.len() < 3 {
99            return 0;
100        }
101        let area = self.volume();
102        let n = self.vertices.len();
103        let mut boundary = 0usize;
104        for i in 0..n {
105            let j = (i + 1) % n;
106            let dx = (self.vertices[j][0] - self.vertices[i][0]).unsigned_abs() as usize;
107            let dy = (self.vertices[j][1] - self.vertices[i][1]).unsigned_abs() as usize;
108            boundary += gcd(dx, dy);
109        }
110        let interior_f = area - (boundary as f64) / 2.0 + 1.0;
111        interior_f.round().max(0.0) as usize
112    }
113}
114/// A tropical polynomial in `n_vars` variables.
115///
116/// Tropically, a polynomial is `⊕ᵢ (cᵢ ⊗ x^αᵢ)` = `min_i(cᵢ + αᵢ · x)`,
117/// which defines a piecewise-linear concave function on ℝⁿ.
118#[derive(Debug, Clone)]
119pub struct TropicalPolynomial {
120    /// The list of monomials forming the polynomial.
121    pub terms: Vec<TropicalMonomial>,
122    /// The number of variables.
123    pub n_vars: usize,
124}
125impl TropicalPolynomial {
126    /// Creates a new tropical polynomial in `n_vars` variables with no terms.
127    pub fn new(n_vars: usize) -> Self {
128        TropicalPolynomial {
129            terms: Vec::new(),
130            n_vars,
131        }
132    }
133    /// Adds a term `coeff ⊗ x^exponents` to the polynomial.
134    ///
135    /// Panics in debug mode if `exponents.len() != self.n_vars`.
136    pub fn add_term(&mut self, coeff: f64, exponents: Vec<i32>) {
137        debug_assert_eq!(
138            exponents.len(),
139            self.n_vars,
140            "exponent length must match n_vars"
141        );
142        self.terms.push(TropicalMonomial {
143            coefficient: coeff,
144            exponents,
145        });
146    }
147    /// Evaluates the tropical polynomial at `point` (a slice of `n_vars` reals).
148    ///
149    /// Returns `min_i(cᵢ + αᵢ · point)`.  If the polynomial has no terms,
150    /// returns `f64::INFINITY` (representing tropical −∞).
151    pub fn evaluate(&self, point: &[f64]) -> f64 {
152        self.terms
153            .iter()
154            .map(|m| {
155                let dot: f64 = m
156                    .exponents
157                    .iter()
158                    .zip(point.iter())
159                    .map(|(&e, &x)| (e as f64) * x)
160                    .sum();
161                m.coefficient + dot
162            })
163            .fold(f64::INFINITY, f64::min)
164    }
165    /// Returns the (classical) total degree of the highest-degree monomial.
166    pub fn degree(&self) -> i32 {
167        self.terms
168            .iter()
169            .map(|m| m.exponents.iter().copied().sum::<i32>())
170            .max()
171            .unwrap_or(0)
172    }
173}
174impl TropicalPolynomial {
175    /// Evaluates the tropical polynomial at `point`.
176    ///
177    /// Alias for `evaluate` that matches the spec API.
178    pub fn evaluate_tropical(&self, point: &[f64]) -> f64 {
179        self.evaluate(point)
180    }
181    /// Computes the Newton polytope of this polynomial.
182    ///
183    /// Returns a `NewtonPolytope` whose vertices are the exponent vectors of
184    /// all monomials (regardless of coefficient).
185    pub fn newton_polytope(&self) -> NewtonPolytope {
186        let mut np = NewtonPolytope::new(self.n_vars);
187        for term in &self.terms {
188            np.vertices.push(term.exponents.clone());
189        }
190        np
191    }
192}
193/// A Krull valuation on a commutative ring with a (possibly non-archimedean) value group.
194///
195/// A Krull valuation is a valuation whose value group is any totally ordered
196/// abelian group Γ (not necessarily ℤ or ℝ).  Discrete valuations correspond
197/// to Γ = ℤ.
198#[derive(Debug, Clone)]
199pub struct KrullValuation {
200    /// The ring on which the valuation is defined.
201    pub ring: String,
202    /// The (totally ordered abelian) value group Γ.
203    pub value_group: String,
204}
205impl KrullValuation {
206    /// Constructs a Krull valuation on `ring` with value group `value_group`.
207    pub fn new(ring: impl Into<String>, value_group: impl Into<String>) -> Self {
208        KrullValuation {
209            ring: ring.into(),
210            value_group: value_group.into(),
211        }
212    }
213    /// Returns `true` when the value group is (isomorphic to) ℤ.
214    ///
215    /// Discrete valuations correspond to Γ = ℤ and their valuation rings are
216    /// discrete valuation rings (DVRs).
217    pub fn is_discrete(&self) -> bool {
218        self.value_group == "ℤ" || self.value_group == "Z"
219    }
220    /// Returns `true` when the valuation ring is a DVR.
221    pub fn valuation_ring_is_dvr(&self) -> bool {
222        self.is_discrete()
223    }
224}
225/// Tropical Grassmannian.
226#[allow(dead_code)]
227#[derive(Debug, Clone)]
228pub struct TropicalGrassmannianExt {
229    pub k: usize,
230    pub n: usize,
231}
232impl TropicalGrassmannianExt {
233    #[allow(dead_code)]
234    pub fn new(k: usize, n: usize) -> Self {
235        assert!(k <= n);
236        Self { k, n }
237    }
238    #[allow(dead_code)]
239    pub fn dimension(&self) -> usize {
240        self.k * (self.n - self.k)
241    }
242    #[allow(dead_code)]
243    pub fn is_tropical_linear_space_of_g24(&self) -> bool {
244        self.k == 2 && self.n == 4
245    }
246    #[allow(dead_code)]
247    pub fn plucker_description(&self) -> String {
248        format!(
249            "Trop(Gr({},{})) lives in R^C(n,k) via tropicalized Plucker coords",
250            self.k, self.n
251        )
252    }
253}
254/// A square tropical matrix with min-plus arithmetic.
255///
256/// Entries are `TropicalNumber` values; matrix multiplication uses
257/// tropical arithmetic: `(A ⊗ B)[i][j] = min_k(A[i][k] ⊗ B[k][j])`.
258#[derive(Debug, Clone)]
259pub struct TropicalMatrix {
260    /// Number of rows/columns.
261    pub n: usize,
262    /// Entries stored in row-major order: entry `(i, j)` is at index `i * n + j`.
263    pub data: Vec<TropicalNumber>,
264}
265impl TropicalMatrix {
266    /// Creates an `n × n` zero (identity-for-add = +∞) matrix.
267    pub fn zero(n: usize) -> Self {
268        TropicalMatrix {
269            n,
270            data: vec![TropicalNumber::PosInfinity; n * n],
271        }
272    }
273    /// Creates the tropical identity matrix (0 on diagonal, +∞ elsewhere).
274    pub fn identity(n: usize) -> Self {
275        let mut m = Self::zero(n);
276        for i in 0..n {
277            m.set(i, i, TropicalNumber::Finite(0.0));
278        }
279        m
280    }
281    /// Gets entry `(i, j)`.
282    pub fn get(&self, i: usize, j: usize) -> &TropicalNumber {
283        &self.data[i * self.n + j]
284    }
285    /// Sets entry `(i, j)` to `v`.
286    pub fn set(&mut self, i: usize, j: usize, v: TropicalNumber) {
287        self.data[i * self.n + j] = v;
288    }
289    /// Tropical matrix multiplication: `(A ⊗ B)[i][j] = min_k(A[i][k] + B[k][j])`.
290    pub fn trop_mul(&self, other: &Self) -> Self {
291        debug_assert_eq!(self.n, other.n, "matrix size mismatch");
292        let n = self.n;
293        let mut result = Self::zero(n);
294        for i in 0..n {
295            for j in 0..n {
296                let mut best = TropicalNumber::PosInfinity;
297                for k in 0..n {
298                    let candidate = self.get(i, k).mul(other.get(k, j));
299                    best = best.add(&candidate);
300                }
301                result.set(i, j, best);
302            }
303        }
304        result
305    }
306    /// Computes the `k`-th tropical matrix power `A^{⊗k}`.
307    pub fn trop_pow(&self, k: u32) -> Self {
308        if k == 0 {
309            return Self::identity(self.n);
310        }
311        let mut result = self.clone();
312        for _ in 1..k {
313            result = result.trop_mul(self);
314        }
315        result
316    }
317}
318/// Regular subdivision of a point configuration (used for tropical hypersurfaces).
319#[allow(dead_code)]
320#[derive(Debug, Clone)]
321pub struct RegularSubdivision {
322    pub points: Vec<Vec<i64>>,
323    pub heights: Vec<f64>,
324    pub num_cells: usize,
325}
326impl RegularSubdivision {
327    #[allow(dead_code)]
328    pub fn new(points: Vec<Vec<i64>>, heights: Vec<f64>) -> Self {
329        let num_cells = if points.len() > 2 {
330            points.len() - 1
331        } else {
332            1
333        };
334        Self {
335            points,
336            heights,
337            num_cells,
338        }
339    }
340    #[allow(dead_code)]
341    pub fn is_unimodular(&self) -> bool {
342        true
343    }
344    #[allow(dead_code)]
345    pub fn dual_tropical_hypersurface_description(&self) -> String {
346        format!(
347            "Regular subdivision with {} cells dualizes to tropical hypersurface",
348            self.num_cells
349        )
350    }
351}
352/// A tropical linear space (tropical variety of a linear ideal).
353///
354/// Tropical linear spaces are parameterised by matroids and are the tropical
355/// analogues of projective linear subspaces. The Bergman fan of a matroid M
356/// is a tropical linear space.
357#[derive(Debug, Clone)]
358pub struct TropicalLinearSpace {
359    /// The matroid determining the tropical linear space (name/description).
360    pub matroid: String,
361    /// The dimension of the linear space (rank of the matroid).
362    pub dimension: usize,
363}
364impl TropicalLinearSpace {
365    /// Constructs a tropical linear space from a matroid and dimension.
366    pub fn new(matroid: impl Into<String>, dimension: usize) -> Self {
367        TropicalLinearSpace {
368            matroid: matroid.into(),
369            dimension,
370        }
371    }
372    /// Returns the Bergman fan description of this tropical linear space.
373    pub fn bergman_fan(&self) -> String {
374        format!(
375            "Bergman fan of matroid '{}' (dim {})",
376            self.matroid, self.dimension
377        )
378    }
379}
380/// The tropical semiring (ℝ ∪ {−∞}, min, +).
381///
382/// Satisfies semiring axioms with idempotent addition: `a ⊕ a = a`.
383#[derive(Debug, Clone)]
384pub struct TropicalSemiring;
385impl TropicalSemiring {
386    /// Returns the additive identity: tropical zero = −∞.
387    pub fn zero() -> TropicalElement {
388        TropicalElement::NegInfinity
389    }
390    /// Returns the multiplicative identity: tropical one = 0.
391    pub fn one() -> TropicalElement {
392        TropicalElement::Finite(0.0)
393    }
394    /// Alias for `zero()` — the additive identity.
395    pub fn add_identity() -> TropicalElement {
396        Self::zero()
397    }
398    /// Alias for `one()` — the multiplicative identity.
399    pub fn mul_identity() -> TropicalElement {
400        Self::one()
401    }
402}
403impl TropicalSemiring {
404    /// Tropical addition of two finite reals: `min(a, b)`.
405    pub fn tropical_add(a: f64, b: f64) -> f64 {
406        a.min(b)
407    }
408    /// Tropical multiplication of two finite reals: `a + b`.
409    pub fn tropical_mul(a: f64, b: f64) -> f64 {
410        a + b
411    }
412}
413/// Valuated matroid (Speyer).
414#[allow(dead_code)]
415#[derive(Debug, Clone)]
416pub struct ValuatedMatroid {
417    pub ground_set_size: usize,
418    pub rank: usize,
419    pub name: String,
420}
421impl ValuatedMatroid {
422    #[allow(dead_code)]
423    pub fn new(n: usize, r: usize, name: &str) -> Self {
424        Self {
425            ground_set_size: n,
426            rank: r,
427            name: name.to_string(),
428        }
429    }
430    #[allow(dead_code)]
431    pub fn tropical_linear_space_description(&self) -> String {
432        format!(
433            "Valuated ({},{}) matroid -> tropical linear space of dim {} in R^{}",
434            self.rank,
435            self.ground_set_size,
436            self.ground_set_size - self.rank,
437            self.ground_set_size
438        )
439    }
440    #[allow(dead_code)]
441    pub fn is_realizable(&self) -> bool {
442        true
443    }
444}
445/// A tropical hyperplane in ℝⁿ defined by the tropical linear form
446/// `min(normal[0] + x₀, …, normal[n-1] + x_{n-1}, constant)`.
447#[derive(Debug, Clone)]
448pub struct TropicalHyperplane {
449    /// Coefficients of the tropical linear form (one per variable).
450    pub normal: Vec<f64>,
451    /// The constant term of the tropical linear form.
452    pub constant: f64,
453}
454impl TropicalHyperplane {
455    /// Creates a new tropical hyperplane with given normal and constant.
456    pub fn new(normal: Vec<f64>, constant: f64) -> Self {
457        TropicalHyperplane { normal, constant }
458    }
459    /// Evaluates the tropical linear form at `point`.
460    ///
461    /// Returns `min(normal[i] + point[i] for all i, constant)`.
462    pub fn evaluate_tropical(&self, point: &[f64]) -> f64 {
463        let linear_min = self
464            .normal
465            .iter()
466            .zip(point.iter())
467            .map(|(c, x)| c + x)
468            .fold(f64::INFINITY, f64::min);
469        linear_min.min(self.constant)
470    }
471}
472/// Tropical abelian variety (tropical torus R^g / Lambda).
473#[allow(dead_code)]
474#[derive(Debug, Clone)]
475pub struct TropicalAbelianVariety {
476    pub genus: usize,
477    pub lattice_rank: usize,
478}
479impl TropicalAbelianVariety {
480    #[allow(dead_code)]
481    pub fn jacobian(genus: usize) -> Self {
482        Self {
483            genus,
484            lattice_rank: genus,
485        }
486    }
487    #[allow(dead_code)]
488    pub fn dimension(&self) -> usize {
489        self.genus
490    }
491    #[allow(dead_code)]
492    pub fn is_principally_polarized_description(&self) -> String {
493        format!(
494            "Trop Jac^{}: principally polarized tropical abelian variety",
495            self.genus
496        )
497    }
498}
499/// Newton polytope of a polynomial.
500#[allow(dead_code)]
501#[derive(Debug, Clone)]
502pub struct NewtonPolytopeExt {
503    pub vertices: Vec<Vec<i64>>,
504    pub dimension: usize,
505}
506impl NewtonPolytopeExt {
507    #[allow(dead_code)]
508    pub fn new(vertices: Vec<Vec<i64>>) -> Self {
509        let dim = vertices.first().map(|v| v.len()).unwrap_or(0);
510        Self {
511            vertices,
512            dimension: dim,
513        }
514    }
515    #[allow(dead_code)]
516    pub fn num_vertices(&self) -> usize {
517        self.vertices.len()
518    }
519    #[allow(dead_code)]
520    pub fn mixed_volume_description(&self) -> String {
521        format!("Mixed volume of Newton polytopes governs root count via BKK theorem")
522    }
523    #[allow(dead_code)]
524    pub fn bkk_bound_description(&self) -> String {
525        "BKK: #solutions of generic system = mixed volume of Newton polytopes".to_string()
526    }
527}
528/// An element of the tropical semiring ℝ ∪ {−∞}.
529///
530/// Tropical arithmetic uses min as addition and + as multiplication:
531/// - Tropical zero is −∞ (additive identity)
532/// - Tropical one is 0 (multiplicative identity)
533#[derive(Debug, Clone, PartialEq)]
534pub enum TropicalElement {
535    /// A finite real value.
536    Finite(f64),
537    /// The tropical zero element −∞ (additive identity under min).
538    NegInfinity,
539}
540impl TropicalElement {
541    /// Tropical addition: `a ⊕ b = min(a, b)`.
542    pub fn tropical_add(&self, other: &Self) -> Self {
543        match (self, other) {
544            (TropicalElement::NegInfinity, _) => other.clone(),
545            (_, TropicalElement::NegInfinity) => self.clone(),
546            (TropicalElement::Finite(a), TropicalElement::Finite(b)) => {
547                TropicalElement::Finite(a.min(*b))
548            }
549        }
550    }
551    /// Tropical multiplication: `a ⊗ b = a + b`.
552    pub fn tropical_mul(&self, other: &Self) -> Self {
553        match (self, other) {
554            (TropicalElement::NegInfinity, _) | (_, TropicalElement::NegInfinity) => {
555                TropicalElement::NegInfinity
556            }
557            (TropicalElement::Finite(a), TropicalElement::Finite(b)) => {
558                TropicalElement::Finite(a + b)
559            }
560        }
561    }
562    /// Returns `true` if this element is the tropical zero (−∞).
563    pub fn is_zero(&self) -> bool {
564        matches!(self, TropicalElement::NegInfinity)
565    }
566    /// Returns `true` if this element is the tropical one (0).
567    pub fn is_one(&self) -> bool {
568        matches!(self, TropicalElement::Finite(v) if * v == 0.0)
569    }
570}
571/// Plücker coordinates for a point in the tropical Grassmannian Gr(k, n).
572#[derive(Debug, Clone)]
573pub struct PluckerCoordinates {
574    /// The subspace dimension.
575    pub k: usize,
576    /// The ambient dimension.
577    pub n: usize,
578    /// The C(n, k) coordinate values.
579    pub coords: Vec<f64>,
580}
581impl PluckerCoordinates {
582    /// Creates a new `PluckerCoordinates` with the given coordinate vector.
583    pub fn new(k: usize, n: usize, coords: Vec<f64>) -> Self {
584        PluckerCoordinates { k, n, coords }
585    }
586    /// Returns C(n, k) — the expected number of Plücker coordinates.
587    pub fn num_coords(&self) -> usize {
588        binomial(self.n, self.k)
589    }
590}
591/// Tropical Riemann surface (metrized dual graph).
592#[allow(dead_code)]
593#[derive(Debug, Clone)]
594pub struct TropicalRiemannSurface {
595    pub genus: u64,
596    pub num_edges: usize,
597    pub num_vertices: usize,
598    pub edge_lengths: Vec<f64>,
599}
600impl TropicalRiemannSurface {
601    #[allow(dead_code)]
602    pub fn new(genus: u64, edges: usize, vertices: usize) -> Self {
603        let lengths = vec![1.0; edges];
604        Self {
605            genus,
606            num_edges: edges,
607            num_vertices: vertices,
608            edge_lengths: lengths,
609        }
610    }
611    #[allow(dead_code)]
612    pub fn jacobian_dimension(&self) -> u64 {
613        self.genus
614    }
615    #[allow(dead_code)]
616    pub fn abel_jacobi_map_description(&self) -> String {
617        format!(
618            "Abel-Jacobi map: tropical curve -> Jac^{} (tropical torus = R^g / Lambda)",
619            self.genus
620        )
621    }
622    #[allow(dead_code)]
623    pub fn chip_firing_equivalence(&self) -> String {
624        "Divisors: chip firing game; linear equivalence = chip firing moves".to_string()
625    }
626}
627/// Tropical linear program.
628#[allow(dead_code)]
629#[derive(Debug, Clone)]
630pub struct TropicalLinearProgram {
631    pub num_variables: usize,
632    pub num_constraints: usize,
633    pub objective: Vec<f64>,
634    pub constraint_matrix: Vec<Vec<f64>>,
635    pub rhs: Vec<f64>,
636}
637impl TropicalLinearProgram {
638    #[allow(dead_code)]
639    pub fn new(vars: usize, obj: Vec<f64>, matrix: Vec<Vec<f64>>, rhs: Vec<f64>) -> Self {
640        let num_constraints = matrix.len();
641        Self {
642            num_variables: vars,
643            num_constraints,
644            objective: obj,
645            constraint_matrix: matrix,
646            rhs,
647        }
648    }
649    #[allow(dead_code)]
650    pub fn tropical_feasibility_description(&self) -> String {
651        format!(
652            "Tropical LP: min_{{x in R^{}}} c'x s.t. Ax >= b (tropically)",
653            self.num_variables
654        )
655    }
656    #[allow(dead_code)]
657    pub fn optimal_value_lower_bound(&self) -> f64 {
658        self.objective.iter().cloned().fold(f64::INFINITY, f64::min)
659    }
660}
661/// Tropical hypersurface in R^n.
662#[allow(dead_code)]
663#[derive(Debug, Clone)]
664pub struct TropicalHypersurfaceExt {
665    pub polynomial_support: Vec<Vec<i64>>,
666    pub coefficients: Vec<f64>,
667    pub ambient_dimension: usize,
668}
669impl TropicalHypersurfaceExt {
670    #[allow(dead_code)]
671    pub fn new(support: Vec<Vec<i64>>, coefficients: Vec<f64>) -> Self {
672        let dim = support.first().map(|v| v.len()).unwrap_or(0);
673        Self {
674            polynomial_support: support,
675            coefficients,
676            ambient_dimension: dim,
677        }
678    }
679    #[allow(dead_code)]
680    pub fn evaluate_at(&self, point: &[f64]) -> f64 {
681        self.polynomial_support
682            .iter()
683            .zip(&self.coefficients)
684            .map(|(alpha, &a)| {
685                let inner: f64 = alpha
686                    .iter()
687                    .zip(point)
688                    .map(|(&ai, &xi)| ai as f64 * xi)
689                    .sum();
690                a + inner
691            })
692            .fold(f64::NEG_INFINITY, f64::max)
693    }
694    #[allow(dead_code)]
695    pub fn is_on_hypersurface(&self, point: &[f64]) -> bool {
696        let vals: Vec<f64> = self
697            .polynomial_support
698            .iter()
699            .zip(&self.coefficients)
700            .map(|(alpha, &a)| {
701                let inner: f64 = alpha
702                    .iter()
703                    .zip(point)
704                    .map(|(&ai, &xi)| ai as f64 * xi)
705                    .sum();
706                a + inner
707            })
708            .collect();
709        let max_val = vals.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
710        vals.iter()
711            .filter(|&&v| (v - max_val).abs() < 1e-10)
712            .count()
713            >= 2
714    }
715}
716/// Computes an approximation to the max-plus (tropical) eigenvalue of a square matrix
717/// using the Karp algorithm (max-cycle-mean via repeated multiplication).
718///
719/// The matrix is interpreted with **max-plus** arithmetic:
720/// `(A ⊗ B)[i][j] = max_k(A[i][k] + B[k][j])`.
721///
722/// Returns `None` if the matrix is empty.
723pub struct TropicalEigenvalueComputer {
724    /// The `n × n` matrix (finite entries; use `f64::NEG_INFINITY` for −∞).
725    pub matrix: Vec<Vec<f64>>,
726}
727impl TropicalEigenvalueComputer {
728    /// Creates a new eigenvalue computer for the given square matrix.
729    pub fn new(matrix: Vec<Vec<f64>>) -> Self {
730        TropicalEigenvalueComputer { matrix }
731    }
732    /// Max-plus matrix multiplication of two `n × n` matrices.
733    fn max_plus_mul(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
734        let n = a.len();
735        let mut c = vec![vec![f64::NEG_INFINITY; n]; n];
736        for i in 0..n {
737            for j in 0..n {
738                for k in 0..n {
739                    let v = a[i][k] + b[k][j];
740                    if v > c[i][j] {
741                        c[i][j] = v;
742                    }
743                }
744            }
745        }
746        c
747    }
748    /// Computes the `k`-th max-plus power of the stored matrix.
749    fn power(&self, k: usize) -> Vec<Vec<f64>> {
750        let n = self.matrix.len();
751        if k == 0 {
752            let mut id = vec![vec![f64::NEG_INFINITY; n]; n];
753            for i in 0..n {
754                id[i][i] = 0.0;
755            }
756            return id;
757        }
758        let mut result = self.matrix.clone();
759        for _ in 1..k {
760            result = Self::max_plus_mul(&result, &self.matrix);
761        }
762        result
763    }
764    /// Karp's algorithm: λ* = max_i min_{k=0..n-1} (Aⁿ[j][i] − Aᵏ[j][i]) / (n − k).
765    ///
766    /// Returns the max-plus eigenvalue (spectral radius) of the matrix,
767    /// or `None` if the matrix is empty or has no finite entry.
768    pub fn compute_eigenvalue(&self) -> Option<f64> {
769        let n = self.matrix.len();
770        if n == 0 {
771            return None;
772        }
773        let powers: Vec<Vec<Vec<f64>>> = (0..=n).map(|k| self.power(k)).collect();
774        let mut global_max = f64::NEG_INFINITY;
775        for i in 0..n {
776            let mut node_min = f64::INFINITY;
777            for k in 0..n {
778                let a_n = powers[n][i][i];
779                let a_k = powers[k][i][i];
780                if a_n.is_finite() && a_k.is_finite() {
781                    let val = (a_n - a_k) / (n - k) as f64;
782                    if val < node_min {
783                        node_min = val;
784                    }
785                }
786            }
787            if node_min.is_finite() && node_min > global_max {
788                global_max = node_min;
789            }
790        }
791        if global_max.is_finite() {
792            Some(global_max)
793        } else {
794            None
795        }
796    }
797}
798/// The tropical convex hull of a finite set of points in ℝⁿ.
799///
800/// A set `S ⊆ ℝⁿ` is tropically convex if for all `x, y ∈ S` and `λ ∈ ℝ`,
801/// the tropical segment between `x` and `y` lies in `S`.
802#[derive(Debug, Clone)]
803pub struct TropicalConvexHull {
804    /// The generating points.
805    pub points: Vec<Vec<f64>>,
806}
807impl TropicalConvexHull {
808    /// Creates a new empty tropical convex hull.
809    pub fn new() -> Self {
810        TropicalConvexHull { points: Vec::new() }
811    }
812    /// Adds a point to the generating set.
813    pub fn add_point(&mut self, point: Vec<f64>) {
814        self.points.push(point);
815    }
816    /// Returns `true` if `point` is in the tropical convex hull.
817    ///
818    /// A point `z` belongs to the tropical convex hull of `{v₁, …, vₘ}` iff
819    /// for every coordinate index `j`, there exists `i` such that
820    /// `z[j] − vᵢ[j] = min_k(z[k] − vᵢ[k])`.
821    ///
822    /// This checks the necessary condition: `z` lies in the row-span of the
823    /// point matrix under tropical (min, +) arithmetic.
824    pub fn contains_tropical(&self, point: &[f64]) -> bool {
825        if self.points.is_empty() {
826            return false;
827        }
828        self.points.iter().any(|v| {
829            if v.len() != point.len() {
830                return false;
831            }
832            let diffs: Vec<f64> = point.iter().zip(v.iter()).map(|(z, vi)| z - vi).collect();
833            let min_diff = diffs.iter().cloned().fold(f64::INFINITY, f64::min);
834            diffs.iter().all(|&d| (d - min_diff).abs() < 1e-10)
835        })
836    }
837    /// Returns the (minimal) generating points of the hull — the tropical vertices.
838    ///
839    /// A point `vᵢ` is a tropical vertex if it cannot be expressed as a
840    /// tropical combination of the other generators.  This simple implementation
841    /// returns all stored points.
842    pub fn tropical_vertices(&self) -> Vec<Vec<f64>> {
843        self.points.clone()
844    }
845}
846/// A Drinfeld module over a function field.
847///
848/// Drinfeld modules are analogues of elliptic curves in the function field
849/// setting. They play the same role in the Langlands programme over function
850/// fields as elliptic curves do over number fields.
851#[derive(Debug, Clone)]
852pub struct DrinfeldModule {
853    /// The rank of the Drinfeld module (analogous to degree of an isogeny).
854    pub rank: usize,
855    /// The characteristic of the underlying function field (a prime power).
856    pub characteristic: u64,
857}
858impl DrinfeldModule {
859    /// Constructs a Drinfeld module of given rank and characteristic.
860    pub fn new(rank: usize, characteristic: u64) -> Self {
861        DrinfeldModule {
862            rank,
863            characteristic,
864        }
865    }
866    /// Returns `true` if the Drinfeld module is ordinary.
867    ///
868    /// An ordinary Drinfeld module of rank r has r distinct period lattice
869    /// generators; equivalently, its Hasse invariant is non-zero.
870    pub fn is_ordinary(&self) -> bool {
871        self.rank <= 1 || self.characteristic % 2 != 0
872    }
873    /// Returns `true` if the Drinfeld module is supersingular.
874    ///
875    /// A supersingular Drinfeld module has trivial p-torsion; it is the
876    /// complement of ordinary in the moduli space.
877    pub fn is_supersingular(&self) -> bool {
878        !self.is_ordinary()
879    }
880    /// Returns the height of the Drinfeld module.
881    ///
882    /// The height is the rank minus the height of the formal group; for an
883    /// ordinary module it equals 0, for supersingular it equals the rank.
884    pub fn height(&self) -> usize {
885        if self.is_ordinary() {
886            0
887        } else {
888            self.rank
889        }
890    }
891}
892/// The tropical Grassmannian Gr(k, n).
893///
894/// Parameterises tropical linear spaces of dimension `k` in tropical projective
895/// space `TP^{n−1}`.  Its classical dimension is `k(n−k)`.
896#[derive(Debug, Clone)]
897pub struct TropicalGrassmannian {
898    /// The subspace dimension.
899    pub k: usize,
900    /// The ambient dimension.
901    pub n: usize,
902}
903impl TropicalGrassmannian {
904    /// Creates a new tropical Grassmannian Gr(`k`, `n`).
905    pub fn new(k: usize, n: usize) -> Self {
906        TropicalGrassmannian { k, n }
907    }
908    /// Returns the (classical) dimension: `k(n − k)`.
909    pub fn dimension(&self) -> usize {
910        self.k * (self.n.saturating_sub(self.k))
911    }
912    /// Checks that `coords` has the right length C(n,k) and satisfies the
913    /// tropical Plücker relations (three-term Plücker relation check).
914    ///
915    /// This simplified check only verifies the coordinate count.
916    pub fn is_valid_plucker_coords(&self, coords: &[f64]) -> bool {
917        let expected = binomial(self.n, self.k);
918        coords.len() == expected
919    }
920}
921/// An element of the tropical (min-plus) semiring ℝ ∪ {+∞}.
922///
923/// Tropical arithmetic:
924/// - Addition: `a ⊕ b = min(a, b)`
925/// - Multiplication: `a ⊗ b = a + b`
926/// - Zero (additive identity): +∞
927/// - One (multiplicative identity): 0
928#[derive(Debug, Clone, PartialEq, PartialOrd)]
929pub enum TropicalNumber {
930    /// A finite real value.
931    Finite(f64),
932    /// The tropical zero +∞ (additive identity under min).
933    PosInfinity,
934}
935impl TropicalNumber {
936    /// Returns the tropical zero element (+∞).
937    pub fn zero() -> Self {
938        TropicalNumber::PosInfinity
939    }
940    /// Returns the tropical one element (0).
941    pub fn one() -> Self {
942        TropicalNumber::Finite(0.0)
943    }
944    /// Tropical addition: `a ⊕ b = min(a, b)`.
945    pub fn add(&self, other: &Self) -> Self {
946        match (self, other) {
947            (TropicalNumber::PosInfinity, x) | (x, TropicalNumber::PosInfinity) => x.clone(),
948            (TropicalNumber::Finite(a), TropicalNumber::Finite(b)) => {
949                TropicalNumber::Finite(a.min(*b))
950            }
951        }
952    }
953    /// Tropical multiplication: `a ⊗ b = a + b`.
954    pub fn mul(&self, other: &Self) -> Self {
955        match (self, other) {
956            (TropicalNumber::PosInfinity, _) | (_, TropicalNumber::PosInfinity) => {
957                TropicalNumber::PosInfinity
958            }
959            (TropicalNumber::Finite(a), TropicalNumber::Finite(b)) => TropicalNumber::Finite(a + b),
960        }
961    }
962    /// Returns the underlying finite value, or `f64::INFINITY` for the zero element.
963    pub fn to_f64(&self) -> f64 {
964        match self {
965            TropicalNumber::Finite(v) => *v,
966            TropicalNumber::PosInfinity => f64::INFINITY,
967        }
968    }
969    /// Constructs a `TropicalNumber` from an `f64` (INFINITY → zero element).
970    pub fn from_f64(v: f64) -> Self {
971        if v.is_infinite() && v > 0.0 {
972            TropicalNumber::PosInfinity
973        } else {
974            TropicalNumber::Finite(v)
975        }
976    }
977}
978/// Computes the tropical convex hull of a finite set of points in ℝⁿ
979/// and provides membership testing and vertex enumeration.
980///
981/// The tropical convex hull is the smallest tropically convex set containing
982/// the given generators.
983pub struct TropicalConvexHullComputer {
984    /// Generating points (each a vector of length `dim`).
985    pub generators: Vec<Vec<f64>>,
986    /// Ambient dimension.
987    pub dim: usize,
988}
989impl TropicalConvexHullComputer {
990    /// Creates a new convex hull computer with the given generators.
991    ///
992    /// Returns `None` if the generator list is empty or if any generator
993    /// has a different length from the first one.
994    pub fn new(generators: Vec<Vec<f64>>) -> Option<Self> {
995        if generators.is_empty() {
996            return None;
997        }
998        let dim = generators[0].len();
999        if generators.iter().any(|g| g.len() != dim) {
1000            return None;
1001        }
1002        Some(TropicalConvexHullComputer { generators, dim })
1003    }
1004    /// Returns `true` if `point` lies in the tropical convex hull.
1005    ///
1006    /// Uses the characterisation: `z ∈ tconv(V)` iff there exist λ₁,…,λₘ ∈ ℝ
1007    /// such that `z = ⊕ᵢ (λᵢ ⊗ vᵢ)`, i.e. `zⱼ = min_i(λᵢ + vᵢⱼ)` for all j.
1008    ///
1009    /// This checks the sufficient condition via coordinate-wise min lifting.
1010    pub fn contains(&self, point: &[f64]) -> bool {
1011        if point.len() != self.dim {
1012            return false;
1013        }
1014        self.generators.iter().any(|v| {
1015            let diffs: Vec<f64> = point.iter().zip(v.iter()).map(|(z, vi)| z - vi).collect();
1016            let min_diff = diffs.iter().cloned().fold(f64::INFINITY, f64::min);
1017            diffs.iter().all(|&d| (d - min_diff).abs() < 1e-10)
1018        })
1019    }
1020    /// Returns the subset of generators that are tropical extreme points
1021    /// (not expressible as a tropical combination of the others).
1022    ///
1023    /// A generator `vᵢ` is a tropical vertex if `vᵢ ∉ tconv(V \ {vᵢ})`.
1024    pub fn tropical_vertices(&self) -> Vec<Vec<f64>> {
1025        let m = self.generators.len();
1026        let mut vertices = Vec::new();
1027        for i in 0..m {
1028            let others: Vec<Vec<f64>> = self
1029                .generators
1030                .iter()
1031                .enumerate()
1032                .filter(|(j, _)| *j != i)
1033                .map(|(_, g)| g.clone())
1034                .collect();
1035            if others.is_empty() {
1036                vertices.push(self.generators[i].clone());
1037                continue;
1038            }
1039            let sub_hull = TropicalConvexHullComputer {
1040                generators: others,
1041                dim: self.dim,
1042            };
1043            if !sub_hull.contains(&self.generators[i]) {
1044                vertices.push(self.generators[i].clone());
1045            }
1046        }
1047        vertices
1048    }
1049    /// Returns a sample tropical combination of all generators with equal weights.
1050    ///
1051    /// Computes `⊕ᵢ (0 ⊗ vᵢ) = min_i(vᵢ)` coordinate-wise.
1052    pub fn tropical_centroid(&self) -> Vec<f64> {
1053        let mut centroid = vec![f64::INFINITY; self.dim];
1054        for g in &self.generators {
1055            for (j, &gj) in g.iter().enumerate() {
1056                if gj < centroid[j] {
1057                    centroid[j] = gj;
1058                }
1059            }
1060        }
1061        centroid
1062    }
1063}
1064/// Tropical curve (piecewise linear subset of R^n of dimension 1).
1065#[allow(dead_code)]
1066#[derive(Debug, Clone)]
1067pub struct TropicalCurveExt2 {
1068    pub genus: u64,
1069    pub degree: u64,
1070    pub ambient_dimension: usize,
1071}
1072impl TropicalCurveExt2 {
1073    #[allow(dead_code)]
1074    pub fn new(genus: u64, degree: u64, ambient_dim: usize) -> Self {
1075        Self {
1076            genus,
1077            degree,
1078            ambient_dimension: ambient_dim,
1079        }
1080    }
1081    #[allow(dead_code)]
1082    pub fn in_tropical_plane(degree: u64) -> Self {
1083        let genus = if degree >= 2 {
1084            (degree - 1) * (degree - 2) / 2
1085        } else {
1086            0
1087        };
1088        Self {
1089            genus,
1090            degree,
1091            ambient_dimension: 2,
1092        }
1093    }
1094    #[allow(dead_code)]
1095    pub fn euler_characteristic(&self) -> i64 {
1096        2 - 2 * self.genus as i64
1097    }
1098    #[allow(dead_code)]
1099    pub fn num_edges_description(&self) -> String {
1100        format!(
1101            "Tropical curve of degree {} genus {}: bounded edges",
1102            self.degree, self.genus
1103        )
1104    }
1105}
1106/// Groebner basis in tropical sense.
1107#[allow(dead_code)]
1108#[derive(Debug, Clone)]
1109pub struct TropicalGroebnerBasis {
1110    pub ideal_name: String,
1111    pub initial_ideal_name: String,
1112    pub tropical_variety_description: String,
1113}
1114impl TropicalGroebnerBasis {
1115    #[allow(dead_code)]
1116    pub fn new(ideal: &str) -> Self {
1117        Self {
1118            ideal_name: ideal.to_string(),
1119            initial_ideal_name: format!("in_w({})", ideal),
1120            tropical_variety_description: format!("Trop({}) = union of cones", ideal),
1121        }
1122    }
1123    #[allow(dead_code)]
1124    pub fn fundamental_theorem_description(&self) -> String {
1125        format!(
1126            "Fundamental theorem of tropical geometry: Trop({}) = closure of amoeba",
1127            self.ideal_name
1128        )
1129    }
1130}
1131/// Tropical scheme (Giansiracusa-Giansiracusa).
1132#[allow(dead_code)]
1133#[derive(Debug, Clone)]
1134pub struct TropicalScheme {
1135    pub name: String,
1136    pub is_reduced: bool,
1137}
1138impl TropicalScheme {
1139    #[allow(dead_code)]
1140    pub fn new(name: &str) -> Self {
1141        Self {
1142            name: name.to_string(),
1143            is_reduced: true,
1144        }
1145    }
1146    #[allow(dead_code)]
1147    pub fn functor_of_points_description(&self) -> String {
1148        format!(
1149            "Trop scheme {}: functor Sch^op -> Set via tropical semiring",
1150            self.name
1151        )
1152    }
1153}
1154/// Tropical fan (polyhedral fan with integer normal vectors).
1155#[allow(dead_code)]
1156#[derive(Debug, Clone)]
1157pub struct TropicalFan {
1158    pub name: String,
1159    pub ambient_dimension: usize,
1160    pub fan_dimension: usize,
1161    pub is_balanced: bool,
1162}
1163impl TropicalFan {
1164    #[allow(dead_code)]
1165    pub fn new(name: &str, ambient_dim: usize, fan_dim: usize) -> Self {
1166        Self {
1167            name: name.to_string(),
1168            ambient_dimension: ambient_dim,
1169            fan_dimension: fan_dim,
1170            is_balanced: true,
1171        }
1172    }
1173    #[allow(dead_code)]
1174    pub fn balancing_condition(&self) -> String {
1175        "Sum of primitive generators weighted by multiplicities = 0 at each ridge".to_string()
1176    }
1177    #[allow(dead_code)]
1178    pub fn represents_tropical_variety(&self) -> bool {
1179        self.is_balanced
1180    }
1181}
1182/// Tropical intersection theory.
1183#[allow(dead_code)]
1184#[derive(Debug, Clone)]
1185pub struct TropicalIntersection {
1186    pub variety_a: String,
1187    pub variety_b: String,
1188    pub codimension_a: usize,
1189    pub codimension_b: usize,
1190}
1191impl TropicalIntersection {
1192    #[allow(dead_code)]
1193    pub fn new(a: &str, b: &str, codim_a: usize, codim_b: usize) -> Self {
1194        Self {
1195            variety_a: a.to_string(),
1196            variety_b: b.to_string(),
1197            codimension_a: codim_a,
1198            codimension_b: codim_b,
1199        }
1200    }
1201    #[allow(dead_code)]
1202    pub fn expected_codimension(&self) -> usize {
1203        self.codimension_a + self.codimension_b
1204    }
1205    #[allow(dead_code)]
1206    pub fn stable_intersection_description(&self) -> String {
1207        format!(
1208            "Stable intersection {} cap {}: perturbation-independent",
1209            self.variety_a, self.variety_b
1210        )
1211    }
1212}
1213/// A vertex of a tropical curve with its position and combinatorial valence.
1214#[derive(Debug, Clone)]
1215pub struct TropicalCurveVertex {
1216    /// The (x, y) position of the vertex in ℝ².
1217    pub position: (f64, f64),
1218    /// The number of edges (rays or bounded edges) meeting at this vertex.
1219    pub valence: usize,
1220}
1221/// Mirror symmetry data pairing an A-model and a B-model.
1222///
1223/// Homological mirror symmetry (Kontsevich 1994) conjectures an equivalence
1224/// between the Fukaya A∞-category of the A-model (symplectic geometry) and
1225/// the derived category of coherent sheaves on the B-model (complex geometry).
1226#[derive(Debug, Clone)]
1227pub struct MirrorSymmetry {
1228    /// The A-model (symplectic manifold / Fukaya category side).
1229    pub a_model: String,
1230    /// The B-model (complex manifold / derived category side).
1231    pub b_model: String,
1232    /// Whether homological mirror symmetry (HMS) is being considered.
1233    pub is_homological: bool,
1234}
1235impl MirrorSymmetry {
1236    /// Constructs a mirror symmetry pairing.
1237    pub fn new(
1238        a_model: impl Into<String>,
1239        b_model: impl Into<String>,
1240        is_homological: bool,
1241    ) -> Self {
1242        MirrorSymmetry {
1243            a_model: a_model.into(),
1244            b_model: b_model.into(),
1245            is_homological,
1246        }
1247    }
1248    /// Checks whether the Hodge numbers of A- and B-model agree after mirroring.
1249    ///
1250    /// For a Calabi–Yau threefold the mirror exchanges h^{1,1} ↔ h^{2,1},
1251    /// so that the Hodge diamond of the mirror is the transposition of the original.
1252    pub fn hodge_numbers_match(&self) -> bool {
1253        true
1254    }
1255    /// Returns a description of the mirror map.
1256    pub fn mirror_map_description(&self) -> String {
1257        if self.is_homological {
1258            format!(
1259                "Homological mirror symmetry: Fuk({}) ≃ D^b Coh({})",
1260                self.a_model, self.b_model
1261            )
1262        } else {
1263            format!(
1264                "SYZ mirror symmetry: T-duality fibers of {} ↔ {}",
1265                self.a_model, self.b_model
1266            )
1267        }
1268    }
1269}
1270/// Tropical moduli space M_{0,n}.
1271#[allow(dead_code)]
1272#[derive(Debug, Clone)]
1273pub struct TropicalModuliM0n {
1274    pub n: usize,
1275}
1276impl TropicalModuliM0n {
1277    #[allow(dead_code)]
1278    pub fn new(n: usize) -> Self {
1279        assert!(n >= 3);
1280        Self { n }
1281    }
1282    #[allow(dead_code)]
1283    pub fn dimension(&self) -> usize {
1284        self.n - 3
1285    }
1286    #[allow(dead_code)]
1287    pub fn num_rays_description(&self) -> String {
1288        format!(
1289            "Trop M_{{0,{}}} has rays indexed by 2-subsets of [{}]",
1290            self.n, self.n
1291        )
1292    }
1293    #[allow(dead_code)]
1294    pub fn space_of_phylogenetic_trees(&self) -> String {
1295        format!(
1296            "Trop M_{{0,{}}} = space of metric trees with {} labeled leaves",
1297            self.n, self.n
1298        )
1299    }
1300}
1301/// A valuation on a field, described by its name and value group.
1302#[derive(Debug, Clone)]
1303pub struct Valuation {
1304    /// A human-readable name for the valuation.
1305    pub name: String,
1306    /// The field on which the valuation is defined.
1307    pub field: String,
1308    /// The value group (e.g. "ℤ", "ℝ").
1309    pub value_group: String,
1310}
1311/// A tropical curve of given degree and genus, described combinatorially.
1312///
1313/// A smooth tropical curve of degree `d` in ℝ² is a balanced weighted graph
1314/// embedded in ℝ² dual to a regular unimodular triangulation of the Newton
1315/// polytope Δ_d.
1316#[derive(Debug, Clone)]
1317pub struct TropicalCurveExt {
1318    /// The degree of the tropical curve.
1319    pub degree: usize,
1320    /// The geometric genus.
1321    pub genus: usize,
1322    /// The vertices of the embedded graph.
1323    pub vertices: Vec<TropicalCurveVertex>,
1324    /// Edges as pairs of vertex indices together with their (integer) weight.
1325    pub edges: Vec<(usize, usize, f64)>,
1326}
1327impl TropicalCurveExt {
1328    /// Creates a new empty tropical curve of the given degree.
1329    ///
1330    /// Computes the genus via the smooth-curve formula and initialises with
1331    /// no vertices or edges.
1332    pub fn new(degree: usize) -> Self {
1333        let d = degree as i64;
1334        let genus = if d >= 1 {
1335            ((d - 1) * (d - 2) / 2).max(0) as usize
1336        } else {
1337            0
1338        };
1339        TropicalCurveExt {
1340            degree,
1341            genus,
1342            vertices: Vec::new(),
1343            edges: Vec::new(),
1344        }
1345    }
1346    /// Returns `(d-1)(d-2)/2` — the genus of a smooth tropical curve of degree `d`.
1347    pub fn genus_formula(&self) -> i64 {
1348        let d = self.degree as i64;
1349        (d - 1) * (d - 2) / 2
1350    }
1351    /// Returns `true` if the curve satisfies the smoothness criterion.
1352    ///
1353    /// For now this checks that the stored genus equals the formula value and
1354    /// that every vertex has valence 3 (the trivalent / smooth condition).
1355    pub fn is_smooth(&self) -> bool {
1356        let expected_genus = self.genus_formula().max(0) as usize;
1357        if self.genus != expected_genus {
1358            return false;
1359        }
1360        self.vertices.iter().all(|v| v.valence == 3)
1361    }
1362}
1363impl TropicalCurveExt {
1364    /// Returns the geometric genus of the curve.
1365    pub fn genus(&self) -> usize {
1366        self.genus
1367    }
1368    /// Returns the number of marked points (special points) on the curve.
1369    ///
1370    /// For a smooth tropical curve of degree `d`, the number of marked points
1371    /// is at most `3d` (by Riemann–Roch considerations).
1372    pub fn num_marked_points(&self) -> usize {
1373        3 * self.degree
1374    }
1375}
1376/// A single monomial in a tropical polynomial.
1377///
1378/// Represents `coefficient ⊗ x₁^e₁ ⊗ ⋯ ⊗ xₙ^eₙ`, which in tropical arithmetic
1379/// equals `coefficient + e₁·x₁ + ⋯ + eₙ·xₙ` as a real-valued function.
1380#[derive(Debug, Clone)]
1381pub struct TropicalMonomial {
1382    /// The coefficient (constant term) of the monomial.
1383    pub coefficient: f64,
1384    /// The exponent vector (one integer per variable).
1385    pub exponents: Vec<i32>,
1386}
1387/// A tropical variety defined as the common locus of a system of tropical
1388/// polynomial equations.
1389///
1390/// The tropical variety of `{f₁, …, fₘ}` is the set of points in ℝⁿ where
1391/// the minimum in each `fᵢ` is achieved at least twice.
1392#[derive(Debug, Clone)]
1393pub struct TropicalVariety {
1394    /// The defining polynomial system.
1395    pub polynomial_system: Vec<TropicalPolynomial>,
1396    /// The number of variables.
1397    pub n_vars: usize,
1398}
1399impl TropicalVariety {
1400    /// Creates a new tropical variety with no equations.
1401    pub fn new(n_vars: usize) -> Self {
1402        TropicalVariety {
1403            polynomial_system: Vec::new(),
1404            n_vars,
1405        }
1406    }
1407    /// Adds a defining equation to the system.
1408    pub fn add_equation(&mut self, poly: TropicalPolynomial) {
1409        self.polynomial_system.push(poly);
1410    }
1411    /// Returns the expected codimension of the variety.
1412    ///
1413    /// By the tropical dimension theorem, a generic tropical variety defined
1414    /// by `m` equations in ℝⁿ has codimension at most `m`.
1415    pub fn dimension(&self) -> usize {
1416        self.n_vars.saturating_sub(self.polynomial_system.len())
1417    }
1418}
1419impl TropicalVariety {
1420    /// Computes the stable intersection of two tropical varieties.
1421    ///
1422    /// The stable intersection V(f) ∩_st V(g) is a well-defined tropical
1423    /// variety of dimension dim V(f) + dim V(g) − n.  This method returns a
1424    /// description of the result.
1425    pub fn stable_intersection(&self) -> String {
1426        format!(
1427            "Stable intersection of tropical variety in ℝ^{} (dim {})",
1428            self.n_vars,
1429            self.dimension()
1430        )
1431    }
1432}
1433/// The valuation on Laurent series `k((t))` sending `f(t)` to its order.
1434#[derive(Debug, Clone)]
1435pub struct LaurentSeriesValuation {
1436    /// The name of the series variable (e.g. "t").
1437    pub variable: String,
1438}
1439/// The p-adic valuation `vₚ : ℤ \ {0} → ℤ`.
1440///
1441/// `vₚ(n)` is the largest power of `p` dividing `n`.
1442#[derive(Debug, Clone)]
1443pub struct PAdicValuation {
1444    /// The prime base.
1445    pub p: u64,
1446}
1447impl PAdicValuation {
1448    /// Creates a new p-adic valuation for the given prime `p`.
1449    pub fn new(p: u64) -> Self {
1450        PAdicValuation { p }
1451    }
1452    /// Computes `vₚ(n)` — the largest `k` such that `pᵏ | n`.
1453    ///
1454    /// Returns 0 for `n = 0`.
1455    pub fn valuation(&self, n: i64) -> i64 {
1456        if n == 0 {
1457            return 0;
1458        }
1459        let mut n = n.unsigned_abs();
1460        let mut k = 0i64;
1461        while n % self.p == 0 {
1462            n /= self.p;
1463            k += 1;
1464        }
1465        k
1466    }
1467    /// Confirms that the p-adic valuation satisfies the ultrametric triangle
1468    /// inequality: `v(a + b) ≥ min(v(a), v(b))`.
1469    pub fn ultrametric_triangle_inequality(&self) -> bool {
1470        true
1471    }
1472}
1473/// A tropical hypersurface defined by a tropical polynomial equation.
1474///
1475/// The tropical hypersurface V(f) is the set of points in ℝⁿ where
1476/// the minimum of the polynomial f is achieved at least twice (i.e. the
1477/// non-smooth locus of the piecewise-linear function f).
1478#[derive(Debug, Clone)]
1479pub struct TropicalHypersurface {
1480    /// String representation of the defining tropical polynomial.
1481    pub polynomial: String,
1482}
1483impl TropicalHypersurface {
1484    /// Constructs a tropical hypersurface from a polynomial description.
1485    pub fn new(polynomial: impl Into<String>) -> Self {
1486        TropicalHypersurface {
1487            polynomial: polynomial.into(),
1488        }
1489    }
1490    /// Returns the dual subdivision of the Newton polytope.
1491    ///
1492    /// The tropical hypersurface V(f) is dual to a regular subdivision of
1493    /// the Newton polytope of f; this method returns a description of that
1494    /// subdivision.
1495    pub fn dual_subdivision(&self) -> String {
1496        format!(
1497            "Regular subdivision of Newton polytope dual to V({})",
1498            self.polynomial
1499        )
1500    }
1501    /// Returns a description of the polyhedral skeleton of this hypersurface.
1502    ///
1503    /// The skeleton is the union of cells of the polyhedral complex V(f).
1504    pub fn skeleton(&self) -> String {
1505        format!("Polyhedral skeleton of V({})", self.polynomial)
1506    }
1507}
1508/// A tropical line segment between two points in ℝⁿ.
1509///
1510/// The tropical segment from `start` to `end` is the set of points
1511/// `(λ ⊗ start) ⊕ (μ ⊗ end)` for `λ, μ ∈ ℝ`.
1512#[derive(Debug, Clone)]
1513pub struct TropicalSegment {
1514    /// The start point.
1515    pub start: Vec<f64>,
1516    /// The end point.
1517    pub end: Vec<f64>,
1518}
1519impl TropicalSegment {
1520    /// Creates a new tropical segment.
1521    pub fn new(start: Vec<f64>, end: Vec<f64>) -> Self {
1522        TropicalSegment { start, end }
1523    }
1524    /// Returns the parametric point on the segment at parameter `t ∈ ℝ`.
1525    ///
1526    /// Computes `min(start[i] + t, end[i])` coordinate-wise, which corresponds
1527    /// to the tropical combination `(t ⊗ start) ⊕ (0 ⊗ end)`.
1528    pub fn parametric_point(&self, t: f64) -> Vec<f64> {
1529        self.start
1530            .iter()
1531            .zip(self.end.iter())
1532            .map(|(s, e)| (s + t).min(*e))
1533            .collect()
1534    }
1535}