Skip to main content

oxilean_std/algebraic_geometry/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::functions::*;
8use std::collections::HashMap;
9
10/// Divisor on an algebraic variety.
11#[allow(dead_code)]
12#[derive(Debug, Clone)]
13pub struct Divisor {
14    pub components: Vec<(String, i32)>,
15    pub is_effective: bool,
16    pub is_prime: bool,
17}
18#[allow(dead_code)]
19impl Divisor {
20    /// Zero divisor.
21    pub fn zero() -> Self {
22        Self {
23            components: Vec::new(),
24            is_effective: true,
25            is_prime: false,
26        }
27    }
28    /// Prime divisor.
29    pub fn prime(name: &str) -> Self {
30        Self {
31            components: vec![(name.to_string(), 1)],
32            is_effective: true,
33            is_prime: true,
34        }
35    }
36    /// Total degree.
37    pub fn degree(&self) -> i32 {
38        self.components.iter().map(|(_, c)| c).sum()
39    }
40    /// Linear equivalence description.
41    pub fn linear_equiv_description(&self) -> String {
42        format!("Div class of degree {}", self.degree())
43    }
44}
45/// Chern class data.
46#[allow(dead_code)]
47#[derive(Debug, Clone)]
48pub struct ChernClass {
49    pub vector_bundle: String,
50    pub rank: usize,
51    pub classes: Vec<String>,
52}
53#[allow(dead_code)]
54impl ChernClass {
55    /// Chern classes of a rank r bundle.
56    pub fn new(bundle: &str, rank: usize) -> Self {
57        let classes = (0..=rank).map(|i| format!("c_{}({})", i, bundle)).collect();
58        Self {
59            vector_bundle: bundle.to_string(),
60            rank,
61            classes,
62        }
63    }
64    /// Total Chern class.
65    pub fn total_chern_class(&self) -> String {
66        self.classes.join(" + ")
67    }
68    /// Grothendieck-Riemann-Roch theorem applies.
69    pub fn grr_applies(&self) -> bool {
70        true
71    }
72}
73/// An affine variety V(I) ⊆ A^n defined by polynomial equations.
74///
75/// Represented by a list of polynomial equations as strings (for display/testing),
76/// together with a set of sample points for membership testing.
77#[derive(Debug, Clone)]
78pub struct AffineVariety {
79    /// Dimension of the ambient affine space.
80    pub ambient_dim: usize,
81    /// Defining polynomial equations (as strings for display).
82    pub equations: Vec<String>,
83}
84impl AffineVariety {
85    /// Create a new affine variety in A^n defined by given polynomial equations.
86    pub fn new(ambient_dim: usize, equations: Vec<String>) -> Self {
87        Self {
88            ambient_dim,
89            equations,
90        }
91    }
92    /// The empty variety in A^n (defined by 1 = 0).
93    pub fn empty(n: usize) -> Self {
94        Self::new(n, vec!["1".to_string()])
95    }
96    /// The whole affine space A^n (no equations).
97    pub fn affine_space(n: usize) -> Self {
98        Self::new(n, vec![])
99    }
100    /// Number of defining equations.
101    pub fn num_equations(&self) -> usize {
102        self.equations.len()
103    }
104    /// Codimension estimate: min(n, number of equations).
105    pub fn codimension_estimate(&self) -> usize {
106        self.equations.len().min(self.ambient_dim)
107    }
108    /// Dimension estimate (ambient dim minus codimension estimate).
109    pub fn dimension_estimate(&self) -> usize {
110        self.ambient_dim.saturating_sub(self.codimension_estimate())
111    }
112    /// Test if the variety is the empty variety (has equation "1").
113    pub fn is_empty_variety(&self) -> bool {
114        self.equations.iter().any(|eq| eq.trim() == "1")
115    }
116    /// Test if the variety is the whole affine space (no equations).
117    pub fn is_full_space(&self) -> bool {
118        self.equations.is_empty()
119    }
120}
121/// A point in projective space P^n represented by homogeneous coordinates.
122///
123/// The coordinates are stored as a vector of `i64`; the actual point is the
124/// equivalence class [x₀ : x₁ : … : xₙ] where not all xᵢ are zero.
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct ProjectivePoint {
127    /// Homogeneous coordinates [x₀ : x₁ : … : xₙ].
128    pub coords: Vec<i64>,
129}
130impl ProjectivePoint {
131    /// Create a new projective point from homogeneous coordinates.
132    /// Returns `None` if all coordinates are zero.
133    pub fn new(coords: Vec<i64>) -> Option<Self> {
134        if coords.iter().all(|&c| c == 0) {
135            None
136        } else {
137            Some(Self { coords })
138        }
139    }
140    /// The projective dimension (length of coords minus one).
141    pub fn dim(&self) -> usize {
142        self.coords.len().saturating_sub(1)
143    }
144    /// Normalize by dividing by the GCD of the coordinates (keeping the first nonzero positive).
145    pub fn normalize(&self) -> Self {
146        let g = self
147            .coords
148            .iter()
149            .fold(0i64, |acc, &x| gcd(acc.abs(), x.abs()));
150        if g == 0 {
151            return self.clone();
152        }
153        let sign = self
154            .coords
155            .iter()
156            .find(|&&c| c != 0)
157            .map(|&c| if c < 0 { -1i64 } else { 1i64 })
158            .unwrap_or(1);
159        Self {
160            coords: self.coords.iter().map(|&c| sign * c / g).collect(),
161        }
162    }
163    /// Check if two projective points represent the same element of P^n.
164    pub fn equiv(&self, other: &ProjectivePoint) -> bool {
165        if self.coords.len() != other.coords.len() {
166            return false;
167        }
168        let n = self.coords.len();
169        for i in 0..n {
170            for j in 0..n {
171                if self.coords[i] * other.coords[j] != self.coords[j] * other.coords[i] {
172                    return false;
173                }
174            }
175        }
176        true
177    }
178}
179/// A divisor class on a curve of genus g, represented by its degree.
180///
181/// The Riemann-Roch theorem operates on divisor classes.
182#[derive(Debug, Clone, PartialEq, Eq)]
183pub struct DivisorClass {
184    /// The degree of the divisor class.
185    pub degree: i64,
186    /// The genus of the underlying curve.
187    pub genus: i64,
188    /// A label for identification.
189    pub label: String,
190}
191impl DivisorClass {
192    /// Create a new divisor class.
193    pub fn new(degree: i64, genus: i64, label: impl Into<String>) -> Self {
194        Self {
195            degree,
196            genus,
197            label: label.into(),
198        }
199    }
200    /// The zero divisor class (degree 0).
201    pub fn zero(genus: i64) -> Self {
202        Self::new(0, genus, "O")
203    }
204    /// The canonical divisor class has degree 2g - 2.
205    pub fn canonical(genus: i64) -> Self {
206        Self::new(2 * genus - 2, genus, "K")
207    }
208    /// Add two divisor classes (sum of degrees).
209    pub fn add(&self, other: &DivisorClass) -> Self {
210        assert_eq!(self.genus, other.genus, "genus must match");
211        Self::new(
212            self.degree + other.degree,
213            self.genus,
214            format!("{}+{}", self.label, other.label),
215        )
216    }
217    /// Negate a divisor class.
218    pub fn neg(&self) -> Self {
219        Self::new(-self.degree, self.genus, format!("-{}", self.label))
220    }
221    /// Subtract two divisor classes.
222    pub fn sub(&self, other: &DivisorClass) -> Self {
223        self.add(&other.neg())
224    }
225    /// Check if this divisor class is effective (degree ≥ 0).
226    pub fn is_effective(&self) -> bool {
227        self.degree >= 0
228    }
229    /// Check if this is the canonical class.
230    pub fn is_canonical(&self) -> bool {
231        self.degree == 2 * self.genus - 2
232    }
233}
234/// Projective variety data.
235#[allow(dead_code)]
236#[derive(Debug, Clone)]
237pub struct ProjectiveVariety {
238    pub name: String,
239    pub ambient_dim: usize,
240    pub degree: usize,
241    pub is_smooth: bool,
242    pub is_irreducible: bool,
243}
244#[allow(dead_code)]
245impl ProjectiveVariety {
246    /// Projective space P^n.
247    pub fn projective_space(n: usize) -> Self {
248        Self {
249            name: format!("P^{}", n),
250            ambient_dim: n,
251            degree: 1,
252            is_smooth: true,
253            is_irreducible: true,
254        }
255    }
256    /// Smooth hypersurface of degree d in P^n.
257    pub fn hypersurface(n: usize, d: usize) -> Self {
258        Self {
259            name: format!("V_{}(P^{})", d, n),
260            ambient_dim: n,
261            degree: d,
262            is_smooth: true,
263            is_irreducible: true,
264        }
265    }
266    /// Dimension (ambient - 1 for hypersurface, etc.).
267    pub fn expected_dim(&self) -> usize {
268        if self.ambient_dim > 0 {
269            self.ambient_dim - 1
270        } else {
271            0
272        }
273    }
274    /// Bezout's theorem: intersection number bound.
275    pub fn bezout_bound(&self, other_degree: usize) -> usize {
276        self.degree * other_degree
277    }
278}
279/// An affine scheme represented by the name of its coordinate ring.
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct AffineScheme {
282    /// Name of the coordinate ring (e.g., "k[x, y]" for affine n-space).
283    pub ring: String,
284}
285impl AffineScheme {
286    /// Create a new affine scheme from a ring name.
287    pub fn new(ring: impl Into<String>) -> Self {
288        Self { ring: ring.into() }
289    }
290    /// The spectrum of the integers — Spec ℤ.
291    pub fn spec_z() -> Self {
292        Self::new("Int")
293    }
294    /// The spectrum of a polynomial ring k[x₁, …, xₙ] — affine n-space.
295    pub fn affine_n_space(base_ring: &str, n: usize) -> Self {
296        let vars: Vec<String> = (1..=n).map(|i| format!("x{}", i)).collect();
297        Self::new(format!("{}[{}]", base_ring, vars.join(", ")))
298    }
299    /// Dimension of the affine scheme (Krull dimension of the ring).
300    pub fn krull_dim_estimate(&self) -> Option<usize> {
301        if self.ring.contains('[') {
302            let inner = self.ring.split('[').nth(1)?;
303            let vars = inner.trim_end_matches(']');
304            Some(vars.split(',').count())
305        } else if self.ring == "Int" || self.ring == "Z" {
306            Some(1)
307        } else {
308            None
309        }
310    }
311}
312/// A sheaf on a topological space, represented by sections over open sets.
313///
314/// `T` is the type of sections (typically a ring or module).
315#[derive(Debug, Clone)]
316pub struct Sheaf<T: Clone> {
317    /// Sections over open sets, keyed by a string label for the open set.
318    sections: std::collections::HashMap<String, T>,
319    /// Restriction maps: (source_open, target_open) → index into restriction functions.
320    restriction_labels: Vec<(String, String)>,
321}
322impl<T: Clone> Sheaf<T> {
323    /// Create a new empty sheaf.
324    pub fn new() -> Self {
325        Self {
326            sections: std::collections::HashMap::new(),
327            restriction_labels: Vec::new(),
328        }
329    }
330    /// Add a section over an open set.
331    pub fn add_section(&mut self, open_set: impl Into<String>, section: T) {
332        self.sections.insert(open_set.into(), section);
333    }
334    /// Get the section over a given open set.
335    pub fn section(&self, open_set: &str) -> Option<&T> {
336        self.sections.get(open_set)
337    }
338    /// Record a restriction map (source open ⊇ target open).
339    pub fn add_restriction(&mut self, source: impl Into<String>, target: impl Into<String>) {
340        self.restriction_labels.push((source.into(), target.into()));
341    }
342    /// Number of open sets with sections.
343    pub fn num_sections(&self) -> usize {
344        self.sections.len()
345    }
346}
347/// Projective space P^n.
348#[derive(Debug, Clone, PartialEq, Eq)]
349pub struct ProjectiveSpace {
350    /// The dimension n of the projective space P^n.
351    pub dim: usize,
352}
353impl ProjectiveSpace {
354    /// Create n-dimensional projective space.
355    pub fn new(dim: usize) -> Self {
356        Self { dim }
357    }
358    /// The projective line P^1.
359    pub fn projective_line() -> Self {
360        Self::new(1)
361    }
362    /// The projective plane P^2.
363    pub fn projective_plane() -> Self {
364        Self::new(2)
365    }
366    /// Number of homogeneous coordinates (dim + 1).
367    pub fn num_coordinates(&self) -> usize {
368        self.dim + 1
369    }
370    /// Euler characteristic of P^n: χ(P^n) = n + 1.
371    pub fn euler_characteristic(&self) -> i64 {
372        (self.dim as i64) + 1
373    }
374    /// The Betti numbers of P^n: b_{2k} = 1 for 0 ≤ k ≤ n, all others 0.
375    pub fn betti_numbers(&self) -> Vec<u64> {
376        let mut betti = vec![0u64; 2 * self.dim + 1];
377        for k in 0..=self.dim {
378            betti[2 * k] = 1;
379        }
380        betti
381    }
382}
383/// A morphism of schemes, identified by source and target names.
384#[derive(Debug, Clone, PartialEq, Eq)]
385pub struct Morphism {
386    /// The source scheme of the morphism.
387    pub source: String,
388    /// The target scheme of the morphism.
389    pub target: String,
390}
391impl Morphism {
392    /// Create a new morphism.
393    pub fn new(source: impl Into<String>, target: impl Into<String>) -> Self {
394        Self {
395            source: source.into(),
396            target: target.into(),
397        }
398    }
399    /// The identity morphism on a scheme.
400    pub fn identity(scheme: impl Into<String>) -> Self {
401        let s = scheme.into();
402        Self {
403            source: s.clone(),
404            target: s,
405        }
406    }
407    /// Check if this is the identity morphism.
408    pub fn is_identity(&self) -> bool {
409        self.source == self.target
410    }
411    /// Compose two morphisms: `self` followed by `other` (other ∘ self).
412    /// Returns `None` if the target of `self` does not match the source of `other`.
413    pub fn compose(&self, other: &Morphism) -> Option<Morphism> {
414        if self.target == other.source {
415            Some(Morphism::new(self.source.clone(), other.target.clone()))
416        } else {
417            None
418        }
419    }
420}
421/// A point on an elliptic curve over a prime field F_p in short Weierstrass form
422/// y² = x³ + ax + b.
423///
424/// The point can be either an affine point (x, y) or the point at infinity O.
425#[derive(Debug, Clone, PartialEq, Eq)]
426pub enum EllipticCurvePoint {
427    /// The point at infinity O (identity element of the group).
428    Infinity,
429    /// An affine point (x, y) on the curve.
430    Affine { x: i64, y: i64 },
431}
432/// Moduli problem data.
433#[allow(dead_code)]
434#[derive(Debug, Clone)]
435pub struct ModuliProblem {
436    pub objects: String,
437    pub equivalence: String,
438    pub coarse_moduli_space: Option<String>,
439    pub has_fine_moduli: bool,
440}
441#[allow(dead_code)]
442impl ModuliProblem {
443    /// Moduli of elliptic curves.
444    pub fn elliptic_curves() -> Self {
445        Self {
446            objects: "elliptic curves over k".to_string(),
447            equivalence: "isomorphism".to_string(),
448            coarse_moduli_space: Some("A^1_j".to_string()),
449            has_fine_moduli: false,
450        }
451    }
452    /// Moduli of vector bundles.
453    pub fn vector_bundles(n: usize, degree: i32) -> Self {
454        Self {
455            objects: format!("rank-{} bundles of degree {}", n, degree),
456            equivalence: "S-equivalence".to_string(),
457            coarse_moduli_space: Some(format!("M({},{})", n, degree)),
458            has_fine_moduli: false,
459        }
460    }
461    /// Does a fine moduli space exist?
462    pub fn fine_moduli_description(&self) -> String {
463        if self.has_fine_moduli {
464            format!("Fine moduli space exists for {}", self.objects)
465        } else {
466            format!("Only coarse moduli exists for {}", self.objects)
467        }
468    }
469}
470/// An elliptic curve y² = x³ + ax + b over F_p (for prime p).
471#[derive(Debug, Clone)]
472pub struct EllipticCurveF {
473    /// Coefficient a in y² = x³ + ax + b.
474    pub a: i64,
475    /// Coefficient b in y² = x³ + ax + b.
476    pub b: i64,
477    /// Prime modulus p (the field F_p).
478    pub p: i64,
479}
480impl EllipticCurveF {
481    /// Create a new elliptic curve y² = x³ + ax + b over F_p.
482    /// Does not check the discriminant condition.
483    pub fn new(a: i64, b: i64, p: i64) -> Self {
484        Self { a, b, p }
485    }
486    /// Check if the curve is non-singular (discriminant Δ = -16(4a³ + 27b²) ≠ 0 mod p).
487    pub fn is_nonsingular(&self) -> bool {
488        let disc = mod_reduce(
489            4 * pow_mod(self.a, 3, self.p) + 27 * pow_mod(self.b, 2, self.p),
490            self.p,
491        );
492        disc != 0
493    }
494    /// Check if a given affine point lies on the curve (y² ≡ x³ + ax + b mod p).
495    pub fn on_curve(&self, x: i64, y: i64) -> bool {
496        let lhs = mod_reduce(y * y, self.p);
497        let rhs = mod_reduce(pow_mod(x, 3, self.p) + self.a * x + self.b, self.p);
498        lhs == rhs
499    }
500    /// Enumerate all affine points on the curve over F_p.
501    pub fn points(&self) -> Vec<EllipticCurvePoint> {
502        let mut pts = vec![EllipticCurvePoint::Infinity];
503        for x in 0..self.p {
504            for y in 0..self.p {
505                if self.on_curve(x, y) {
506                    pts.push(EllipticCurvePoint::Affine { x, y });
507                }
508            }
509        }
510        pts
511    }
512    /// The number of points on the curve over F_p (including the point at infinity).
513    pub fn point_count(&self) -> usize {
514        self.points().len()
515    }
516    /// Add two points P and Q on the curve (elliptic curve group law over F_p).
517    pub fn add(&self, p_pt: &EllipticCurvePoint, q_pt: &EllipticCurvePoint) -> EllipticCurvePoint {
518        match (p_pt, q_pt) {
519            (EllipticCurvePoint::Infinity, q) => q.clone(),
520            (p, EllipticCurvePoint::Infinity) => p.clone(),
521            (
522                EllipticCurvePoint::Affine { x: x1, y: y1 },
523                EllipticCurvePoint::Affine { x: x2, y: y2 },
524            ) => {
525                if x1 == x2 && mod_reduce(y1 + y2, self.p) == 0 {
526                    return EllipticCurvePoint::Infinity;
527                }
528                let lambda = if x1 == x2 && y1 == y2 {
529                    let num = mod_reduce(3 * pow_mod(*x1, 2, self.p) + self.a, self.p);
530                    let den = mod_reduce(2 * y1, self.p);
531                    mod_mul(num, mod_inv(den, self.p), self.p)
532                } else {
533                    let num = mod_reduce(y2 - y1, self.p);
534                    let den = mod_reduce(x2 - x1, self.p);
535                    mod_mul(num, mod_inv(den, self.p), self.p)
536                };
537                let x3 = mod_reduce(pow_mod(lambda, 2, self.p) - x1 - x2, self.p);
538                let y3 = mod_reduce(lambda * (x1 - x3) - y1, self.p);
539                EllipticCurvePoint::Affine { x: x3, y: y3 }
540            }
541        }
542    }
543    /// Scalar multiplication [n]P on the elliptic curve using double-and-add.
544    pub fn scalar_mul(&self, n: u64, pt: &EllipticCurvePoint) -> EllipticCurvePoint {
545        let mut result = EllipticCurvePoint::Infinity;
546        let mut addend = pt.clone();
547        let mut k = n;
548        while k > 0 {
549            if k & 1 == 1 {
550                result = self.add(&result, &addend);
551            }
552            addend = self.add(&addend, &addend);
553            k >>= 1;
554        }
555        result
556    }
557    /// Compute the order of a point P (the smallest n > 0 such that [n]P = O).
558    /// Returns `None` if the order exceeds `max_order`.
559    pub fn point_order(&self, pt: &EllipticCurvePoint, max_order: u64) -> Option<u64> {
560        if matches!(pt, EllipticCurvePoint::Infinity) {
561            return Some(1);
562        }
563        let mut current = pt.clone();
564        for n in 1..=max_order {
565            current = self.add(&current, pt);
566            if matches!(current, EllipticCurvePoint::Infinity) {
567                return Some(n + 1);
568            }
569        }
570        None
571    }
572}
573/// Affine scheme data.
574#[allow(dead_code)]
575#[derive(Debug, Clone)]
576pub struct AffineSchemeData {
577    pub ring: String,
578    pub is_integral: bool,
579    pub is_noetherian: bool,
580    pub dimension: Option<usize>,
581}
582#[allow(dead_code)]
583impl AffineSchemeData {
584    /// Spec of polynomial ring k[x1,...,xn].
585    pub fn affine_space(n: usize) -> Self {
586        Self {
587            ring: format!("k[x1,...,x{}]", n),
588            is_integral: true,
589            is_noetherian: true,
590            dimension: Some(n),
591        }
592    }
593    /// Spec of a field.
594    pub fn point() -> Self {
595        Self {
596            ring: "k".to_string(),
597            is_integral: true,
598            is_noetherian: true,
599            dimension: Some(0),
600        }
601    }
602    /// Is this a variety (integral + finite type over k)?
603    pub fn is_variety(&self) -> bool {
604        self.is_integral && self.is_noetherian
605    }
606}