1use super::functions::*;
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7
8#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct KVVanishingData {
12 pub variety: String,
14 pub dim: usize,
16 pub is_nef_big: bool,
18 pub vanishing_degrees: Vec<usize>,
20}
21#[allow(dead_code)]
22impl KVVanishingData {
23 pub fn new(variety: &str, dim: usize) -> Self {
25 KVVanishingData {
26 variety: variety.to_string(),
27 dim,
28 is_nef_big: false,
29 vanishing_degrees: Vec::new(),
30 }
31 }
32 pub fn nef_and_big(mut self) -> Self {
34 self.is_nef_big = true;
35 self.vanishing_degrees = (1..=self.dim).collect();
36 self
37 }
38 pub fn vanishing_statement(&self) -> String {
40 if self.is_nef_big {
41 format!("H^i(K_X + L) = 0 for i > 0 on {}", self.variety)
42 } else {
43 format!(
44 "KV vanishing not applicable: L not nef+big on {}",
45 self.variety
46 )
47 }
48 }
49 pub fn vanishes_at(&self, i: usize) -> bool {
51 self.vanishing_degrees.contains(&i)
52 }
53}
54#[derive(Debug, Clone)]
56pub struct LogPair {
57 pub variety: String,
59 pub boundary_coeffs: Vec<f64>,
61 pub boundary_components: Vec<String>,
63}
64impl LogPair {
65 pub fn trivial(variety: impl Into<String>) -> Self {
67 LogPair {
68 variety: variety.into(),
69 boundary_coeffs: vec![],
70 boundary_components: vec![],
71 }
72 }
73 pub fn with_boundary(mut self, name: impl Into<String>, coeff: f64) -> Self {
75 assert!(
76 (0.0..=1.0).contains(&coeff),
77 "Boundary coefficients must be in [0, 1]"
78 );
79 self.boundary_components.push(name.into());
80 self.boundary_coeffs.push(coeff);
81 self
82 }
83 pub fn is_log_canonical(&self) -> bool {
85 self.boundary_coeffs.iter().all(|&a| a <= 1.0 + 1e-10)
86 }
87 pub fn is_klt(&self) -> bool {
89 self.boundary_coeffs.iter().all(|&a| a < 1.0 - 1e-10)
90 }
91 pub fn is_plt(&self) -> bool {
93 self.is_log_canonical()
94 }
95 pub fn boundary_degree(&self) -> f64 {
97 self.boundary_coeffs.iter().sum()
98 }
99}
100#[derive(Debug, Clone)]
102pub struct BlowUpData {
103 pub original: String,
105 pub center: String,
107 pub center_codim: usize,
109 pub exceptional_divisor: String,
111}
112impl BlowUpData {
113 pub fn new(original: impl Into<String>, center: impl Into<String>, codim: usize) -> Self {
115 let orig = original.into();
116 let c = center.into();
117 BlowUpData {
118 exceptional_divisor: format!("E = P(N_{{{}/{}}})", c, orig),
119 original: orig,
120 center: c,
121 center_codim: codim,
122 }
123 }
124 pub fn exceptional_self_intersection(&self) -> i64 {
126 let r = self.center_codim;
127 if r == 0 {
128 return 1;
129 }
130 if (r - 1) % 2 == 0 {
131 1i64
132 } else {
133 -1i64
134 }
135 }
136 pub fn exceptional_discrepancy(&self) -> i64 {
138 self.center_codim as i64 - 1
139 }
140}
141#[allow(dead_code)]
143#[derive(Debug, Clone, PartialEq)]
144pub enum MMPOperation {
145 DivisorialContraction,
147 Flip,
149 MoriFiberSpace,
151 MinimalModel,
153}
154#[derive(Debug, Clone, PartialEq, Eq)]
156pub enum MMPStep {
157 DivisorialContraction { contracted_divisor: String },
159 Flip { flipping_locus: String },
161 MinimalModel,
163 FanoFibration { base: String },
165}
166impl MMPStep {
167 pub fn description(&self) -> String {
169 match self {
170 MMPStep::DivisorialContraction { contracted_divisor } => {
171 format!("Divisorial contraction: contract {}", contracted_divisor)
172 }
173 MMPStep::Flip { flipping_locus } => {
174 format!("Flip: flip over {}", flipping_locus)
175 }
176 MMPStep::MinimalModel => "Minimal model reached: K_X is nef".to_string(),
177 MMPStep::FanoFibration { base } => format!("Fano fibration over {}", base),
178 }
179 }
180 pub fn is_terminal(&self) -> bool {
182 matches!(self, MMPStep::MinimalModel | MMPStep::FanoFibration { .. })
183 }
184}
185#[derive(Debug, Clone, PartialEq, Eq)]
187pub enum ContractionType {
188 Divisorial,
190 Small,
192 FiberType,
194}
195#[derive(Debug, Clone, PartialEq, Eq)]
200pub enum MmpStep {
201 Contraction {
203 divisor: String,
205 singularity_type: SingularityType,
207 },
208 Flip {
210 locus: String,
212 },
213 Flop {
215 locus: String,
217 },
218 MinimalModel,
220 FiberSpace {
222 base: String,
224 fiber_dim: usize,
226 },
227}
228impl MmpStep {
229 pub fn description(&self) -> String {
231 match self {
232 MmpStep::Contraction {
233 divisor,
234 singularity_type,
235 } => {
236 format!(
237 "Divisorial contraction of {} (→ {} singularities)",
238 divisor,
239 singularity_type.name()
240 )
241 }
242 MmpStep::Flip { locus } => format!("Flip over {}", locus),
243 MmpStep::Flop { locus } => format!("Flop over {}", locus),
244 MmpStep::MinimalModel => "Minimal model reached (K_X nef)".to_string(),
245 MmpStep::FiberSpace { base, fiber_dim } => {
246 format!("Mori fiber space over {} (fiber dim = {})", base, fiber_dim)
247 }
248 }
249 }
250 pub fn is_terminal(&self) -> bool {
252 matches!(self, MmpStep::MinimalModel | MmpStep::FiberSpace { .. })
253 }
254}
255#[allow(dead_code)]
257#[derive(Debug, Clone)]
258pub struct LogPairData {
259 pub variety: String,
261 pub boundary_components: Vec<(String, f64)>,
263 pub log_discrepancies: Vec<f64>,
265}
266#[allow(dead_code)]
267impl LogPairData {
268 pub fn new(variety: &str) -> Self {
270 LogPairData {
271 variety: variety.to_string(),
272 boundary_components: Vec::new(),
273 log_discrepancies: Vec::new(),
274 }
275 }
276 pub fn add_boundary(&mut self, divisor: &str, coeff: f64) {
278 self.boundary_components.push((divisor.to_string(), coeff));
279 }
280 pub fn add_log_discrepancy(&mut self, a: f64) {
282 self.log_discrepancies.push(a);
283 }
284 pub fn total_coefficient(&self) -> f64 {
286 self.boundary_components.iter().map(|(_, a)| a).sum()
287 }
288 pub fn is_klt(&self) -> bool {
290 self.log_discrepancies.iter().all(|&a| a > -1.0)
291 }
292 pub fn is_log_canonical(&self) -> bool {
294 self.log_discrepancies.iter().all(|&a| a >= -1.0)
295 }
296 pub fn singularity_type(&self) -> &str {
298 if self.log_discrepancies.is_empty() {
299 "smooth"
300 } else if self.is_klt() {
301 "klt (Kawamata log terminal)"
302 } else if self.is_log_canonical() {
303 "lc (log canonical)"
304 } else {
305 "worse than log canonical"
306 }
307 }
308}
309#[derive(Debug, Clone)]
316pub struct ZariskiDecomp {
317 pub divisor: String,
319 pub nef_part: Vec<(String, f64)>,
321 pub neg_part: Vec<(String, f64)>,
323}
324impl ZariskiDecomp {
325 pub fn new(
327 divisor: impl Into<String>,
328 nef_part: Vec<(String, f64)>,
329 neg_part: Vec<(String, f64)>,
330 ) -> Self {
331 ZariskiDecomp {
332 divisor: divisor.into(),
333 nef_part,
334 neg_part,
335 }
336 }
337 pub fn is_negative_part_effective(&self) -> bool {
339 self.neg_part.iter().all(|(_, c)| *c >= 0.0)
340 }
341 pub fn nef_degree(&self) -> f64 {
343 self.nef_part.iter().map(|(_, c)| c).sum()
344 }
345 pub fn neg_degree(&self) -> f64 {
347 self.neg_part.iter().map(|(_, c)| c).sum()
348 }
349}
350#[derive(Debug, Clone, PartialEq, Eq)]
352pub struct ProjectiveVariety {
353 pub dim: usize,
355 pub degree: u64,
357 pub name: String,
359}
360impl ProjectiveVariety {
361 pub fn new(dim: usize, degree: u64, name: impl Into<String>) -> Self {
363 ProjectiveVariety {
364 dim,
365 degree,
366 name: name.into(),
367 }
368 }
369 pub fn projective_space(n: usize) -> Self {
371 ProjectiveVariety::new(n, 1, format!("P^{}", n))
372 }
373 pub fn quadric(n: usize) -> Self {
375 ProjectiveVariety::new(n, 2, format!("Q^{}", n))
376 }
377 pub fn del_pezzo(d: usize) -> Self {
380 assert!((1..=9).contains(&d), "Del Pezzo degree must be 1..=9");
381 ProjectiveVariety::new(2, d as u64, format!("S_{}", d))
382 }
383}
384#[derive(Debug, Clone, Copy, PartialEq, Eq)]
386pub enum KodairaDim {
387 NegInfinity,
389 Finite(i64),
391}
392impl KodairaDim {
393 pub fn zero() -> Self {
395 KodairaDim::Finite(0)
396 }
397 pub fn product(self, other: KodairaDim) -> KodairaDim {
399 match (self, other) {
400 (KodairaDim::NegInfinity, _) | (_, KodairaDim::NegInfinity) => KodairaDim::NegInfinity,
401 (KodairaDim::Finite(a), KodairaDim::Finite(b)) => KodairaDim::Finite(a + b),
402 }
403 }
404 pub fn is_general_type(self, dim: usize) -> bool {
406 matches!(self, KodairaDim::Finite(k) if k == dim as i64)
407 }
408 pub fn is_uniruled(self) -> bool {
410 self == KodairaDim::NegInfinity
411 }
412 pub fn classify(self, dim: usize) -> &'static str {
414 match self {
415 KodairaDim::NegInfinity => "uniruled",
416 KodairaDim::Finite(0) => "Kodaira dim 0 (CY / K3 / abelian type)",
417 KodairaDim::Finite(k) if k == dim as i64 => "general type",
418 KodairaDim::Finite(_) => "intermediate Kodaira dimension",
419 }
420 }
421}
422#[allow(dead_code)]
424#[derive(Debug, Clone)]
425pub struct MMPStepData {
426 pub operation: MMPOperation,
428 pub result_variety: String,
430 pub exceptional_divisor: Option<String>,
432}
433#[allow(dead_code)]
434impl MMPStepData {
435 pub fn new(op: MMPOperation, result: &str) -> Self {
437 MMPStepData {
438 operation: op,
439 result_variety: result.to_string(),
440 exceptional_divisor: None,
441 }
442 }
443 pub fn with_exceptional(mut self, div: &str) -> Self {
445 self.exceptional_divisor = Some(div.to_string());
446 self
447 }
448 pub fn description(&self) -> String {
450 let op_name = match &self.operation {
451 MMPOperation::DivisorialContraction => "Divisorial contraction",
452 MMPOperation::Flip => "Flip",
453 MMPOperation::MoriFiberSpace => "Mori fiber space",
454 MMPOperation::MinimalModel => "Minimal model",
455 };
456 format!("{} → {}", op_name, self.result_variety)
457 }
458 pub fn is_final(&self) -> bool {
460 matches!(
461 &self.operation,
462 MMPOperation::MoriFiberSpace | MMPOperation::MinimalModel
463 )
464 }
465}
466#[derive(Debug, Clone, PartialEq, Eq)]
471pub enum SarkisovLinkType {
472 TypeI,
474 TypeII,
476 TypeIII,
478 TypeIV,
480}
481impl SarkisovLinkType {
482 pub fn description(&self) -> &'static str {
484 match self {
485 SarkisovLinkType::TypeI => "Type I: blow-up (left), fiber space change",
486 SarkisovLinkType::TypeII => "Type II: blow-up (both), same base",
487 SarkisovLinkType::TypeIII => "Type III: blow-down (right), base change",
488 SarkisovLinkType::TypeIV => "Type IV: blow-down (right), flip base",
489 }
490 }
491 pub fn code(&self) -> u8 {
493 match self {
494 SarkisovLinkType::TypeI => 1,
495 SarkisovLinkType::TypeII => 2,
496 SarkisovLinkType::TypeIII => 3,
497 SarkisovLinkType::TypeIV => 4,
498 }
499 }
500}
501#[allow(dead_code)]
503#[derive(Debug, Clone)]
504pub struct IitakaFibration {
505 pub source: String,
507 pub base: String,
509 pub fiber: String,
511 pub kodaira_dim: i64,
513 pub base_dim: usize,
515}
516#[allow(dead_code)]
517impl IitakaFibration {
518 pub fn new(source: &str, base: &str, fiber: &str, kappa: i64) -> Self {
520 let base_dim = kappa.max(0) as usize;
521 IitakaFibration {
522 source: source.to_string(),
523 base: base.to_string(),
524 fiber: fiber.to_string(),
525 kodaira_dim: kappa,
526 base_dim,
527 }
528 }
529 pub fn addition_formula(&self, kappa_fiber: i64, kappa_base: i64) -> bool {
531 self.kodaira_dim <= kappa_fiber + kappa_base
532 }
533 pub fn description(&self) -> String {
535 format!(
536 "{} → {} (base, κ={}) with fiber {}",
537 self.source, self.base, self.kodaira_dim, self.fiber
538 )
539 }
540 pub fn is_general_type(&self, dim: usize) -> bool {
542 self.kodaira_dim == dim as i64
543 }
544}
545#[derive(Debug, Clone)]
549pub struct MoriCone {
550 pub dim: usize,
552 pub extremal_rays: Vec<i64>,
554}
555impl MoriCone {
556 pub fn projective_space(n: usize) -> Self {
558 MoriCone {
559 dim: n,
560 extremal_rays: vec![-(n as i64 + 1)],
561 }
562 }
563 pub fn del_pezzo(d: usize) -> Self {
566 let blown_up = 9 - d;
567 let mut rays: Vec<i64> = vec![-3];
568 rays.extend(std::iter::repeat(-1).take(blown_up));
569 MoriCone {
570 dim: 2,
571 extremal_rays: rays,
572 }
573 }
574 pub fn num_extremal_rays(&self) -> usize {
576 self.extremal_rays.len()
577 }
578 pub fn is_fano(&self) -> bool {
580 self.extremal_rays.iter().all(|&r| r < 0)
581 }
582}
583#[allow(dead_code)]
585#[derive(Debug, Clone)]
586pub struct AbundanceData {
587 pub variety: String,
589 pub kodaira_dim: Option<i64>,
591 pub kx_nef: bool,
593 pub kx_semi_ample: bool,
595 pub dim: usize,
597}
598#[allow(dead_code)]
599impl AbundanceData {
600 pub fn new(variety: &str, dim: usize) -> Self {
602 AbundanceData {
603 variety: variety.to_string(),
604 kodaira_dim: None,
605 kx_nef: false,
606 kx_semi_ample: false,
607 dim,
608 }
609 }
610 pub fn with_kodaira_dim(mut self, kappa: i64) -> Self {
612 self.kodaira_dim = Some(kappa);
613 self
614 }
615 pub fn nef(mut self) -> Self {
617 self.kx_nef = true;
618 self
619 }
620 pub fn abundant(mut self) -> Self {
622 self.kx_semi_ample = true;
623 self
624 }
625 pub fn abundance_status(&self) -> String {
627 if self.kx_nef && self.kx_semi_ample {
628 format!(
629 "Abundance holds for {}: K_X nef and semi-ample",
630 self.variety
631 )
632 } else if self.kx_nef {
633 format!("Abundance not yet verified for {}", self.variety)
634 } else {
635 format!("{} does not have nef canonical class", self.variety)
636 }
637 }
638 pub fn abundance_known(&self) -> bool {
640 self.dim <= 3
641 }
642}
643pub struct MmpFlowchart {
648 pub pair: LogPair,
650 pub history: Vec<MmpStep>,
652 pub picard_number: usize,
654}
655impl MmpFlowchart {
656 pub fn new(pair: LogPair, initial_picard: usize) -> Self {
658 MmpFlowchart {
659 pair,
660 history: Vec::new(),
661 picard_number: initial_picard,
662 }
663 }
664 pub fn apply(&mut self, step: MmpStep) -> bool {
667 let done = step.is_terminal();
668 match &step {
669 MmpStep::Contraction { .. } => {
670 if self.picard_number > 0 {
671 self.picard_number -= 1;
672 }
673 }
674 MmpStep::Flip { .. } | MmpStep::Flop { .. } => {}
675 MmpStep::MinimalModel | MmpStep::FiberSpace { .. } => {}
676 }
677 self.history.push(step);
678 done
679 }
680 pub fn run(&mut self, steps: Vec<MmpStep>) -> &[MmpStep] {
683 for step in steps {
684 let done = self.apply(step);
685 if done {
686 break;
687 }
688 }
689 &self.history
690 }
691 pub fn summary(&self) -> String {
693 let mut s = format!(
694 "MMP run on '{}' ({} steps, final Picard number: {}):\n",
695 self.pair.variety,
696 self.history.len(),
697 self.picard_number
698 );
699 for (i, step) in self.history.iter().enumerate() {
700 s.push_str(&format!(" Step {}: {}\n", i + 1, step.description()));
701 }
702 s
703 }
704}
705#[derive(Debug, Clone, PartialEq, Eq)]
707pub enum SingularityType {
708 Smooth,
710 Terminal,
712 Canonical,
714 Klt,
716 Dlt,
718 LogCanonical,
720}
721impl SingularityType {
722 pub fn name(&self) -> &'static str {
724 match self {
725 SingularityType::Smooth => "smooth",
726 SingularityType::Terminal => "terminal",
727 SingularityType::Canonical => "canonical",
728 SingularityType::Klt => "klt",
729 SingularityType::Dlt => "dlt",
730 SingularityType::LogCanonical => "lc",
731 }
732 }
733 pub fn at_least_as_mild_as(&self, other: &SingularityType) -> bool {
736 let rank = |s: &SingularityType| -> usize {
737 match s {
738 SingularityType::Smooth => 0,
739 SingularityType::Terminal => 1,
740 SingularityType::Canonical => 2,
741 SingularityType::Klt => 3,
742 SingularityType::Dlt => 4,
743 SingularityType::LogCanonical => 5,
744 }
745 };
746 rank(self) <= rank(other)
747 }
748}
749#[derive(Debug, Clone)]
755pub struct ExtremeRay {
756 pub generator: String,
758 pub k_degree: i64,
760 pub contraction_type: ContractionType,
762}
763impl ExtremeRay {
764 pub fn new(generator: impl Into<String>, k_degree: i64, ty: ContractionType) -> Self {
766 ExtremeRay {
767 generator: generator.into(),
768 k_degree,
769 contraction_type: ty,
770 }
771 }
772 pub fn is_k_negative(&self) -> bool {
774 self.k_degree < 0
775 }
776 pub fn mmp_step(&self) -> MmpStep {
778 match &self.contraction_type {
779 ContractionType::Divisorial => MmpStep::Contraction {
780 divisor: self.generator.clone(),
781 singularity_type: SingularityType::Terminal,
782 },
783 ContractionType::Small => MmpStep::Flip {
784 locus: self.generator.clone(),
785 },
786 ContractionType::FiberType => MmpStep::FiberSpace {
787 base: format!("base({})", self.generator),
788 fiber_dim: 1,
789 },
790 }
791 }
792}