use super::functions::*;
use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct KVVanishingData {
pub variety: String,
pub dim: usize,
pub is_nef_big: bool,
pub vanishing_degrees: Vec<usize>,
}
#[allow(dead_code)]
impl KVVanishingData {
pub fn new(variety: &str, dim: usize) -> Self {
KVVanishingData {
variety: variety.to_string(),
dim,
is_nef_big: false,
vanishing_degrees: Vec::new(),
}
}
pub fn nef_and_big(mut self) -> Self {
self.is_nef_big = true;
self.vanishing_degrees = (1..=self.dim).collect();
self
}
pub fn vanishing_statement(&self) -> String {
if self.is_nef_big {
format!("H^i(K_X + L) = 0 for i > 0 on {}", self.variety)
} else {
format!(
"KV vanishing not applicable: L not nef+big on {}",
self.variety
)
}
}
pub fn vanishes_at(&self, i: usize) -> bool {
self.vanishing_degrees.contains(&i)
}
}
#[derive(Debug, Clone)]
pub struct LogPair {
pub variety: String,
pub boundary_coeffs: Vec<f64>,
pub boundary_components: Vec<String>,
}
impl LogPair {
pub fn trivial(variety: impl Into<String>) -> Self {
LogPair {
variety: variety.into(),
boundary_coeffs: vec![],
boundary_components: vec![],
}
}
pub fn with_boundary(mut self, name: impl Into<String>, coeff: f64) -> Self {
assert!(
(0.0..=1.0).contains(&coeff),
"Boundary coefficients must be in [0, 1]"
);
self.boundary_components.push(name.into());
self.boundary_coeffs.push(coeff);
self
}
pub fn is_log_canonical(&self) -> bool {
self.boundary_coeffs.iter().all(|&a| a <= 1.0 + 1e-10)
}
pub fn is_klt(&self) -> bool {
self.boundary_coeffs.iter().all(|&a| a < 1.0 - 1e-10)
}
pub fn is_plt(&self) -> bool {
self.is_log_canonical()
}
pub fn boundary_degree(&self) -> f64 {
self.boundary_coeffs.iter().sum()
}
}
#[derive(Debug, Clone)]
pub struct BlowUpData {
pub original: String,
pub center: String,
pub center_codim: usize,
pub exceptional_divisor: String,
}
impl BlowUpData {
pub fn new(original: impl Into<String>, center: impl Into<String>, codim: usize) -> Self {
let orig = original.into();
let c = center.into();
BlowUpData {
exceptional_divisor: format!("E = P(N_{{{}/{}}})", c, orig),
original: orig,
center: c,
center_codim: codim,
}
}
pub fn exceptional_self_intersection(&self) -> i64 {
let r = self.center_codim;
if r == 0 {
return 1;
}
if (r - 1) % 2 == 0 {
1i64
} else {
-1i64
}
}
pub fn exceptional_discrepancy(&self) -> i64 {
self.center_codim as i64 - 1
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum MMPOperation {
DivisorialContraction,
Flip,
MoriFiberSpace,
MinimalModel,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MMPStep {
DivisorialContraction { contracted_divisor: String },
Flip { flipping_locus: String },
MinimalModel,
FanoFibration { base: String },
}
impl MMPStep {
pub fn description(&self) -> String {
match self {
MMPStep::DivisorialContraction { contracted_divisor } => {
format!("Divisorial contraction: contract {}", contracted_divisor)
}
MMPStep::Flip { flipping_locus } => {
format!("Flip: flip over {}", flipping_locus)
}
MMPStep::MinimalModel => "Minimal model reached: K_X is nef".to_string(),
MMPStep::FanoFibration { base } => format!("Fano fibration over {}", base),
}
}
pub fn is_terminal(&self) -> bool {
matches!(self, MMPStep::MinimalModel | MMPStep::FanoFibration { .. })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContractionType {
Divisorial,
Small,
FiberType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MmpStep {
Contraction {
divisor: String,
singularity_type: SingularityType,
},
Flip {
locus: String,
},
Flop {
locus: String,
},
MinimalModel,
FiberSpace {
base: String,
fiber_dim: usize,
},
}
impl MmpStep {
pub fn description(&self) -> String {
match self {
MmpStep::Contraction {
divisor,
singularity_type,
} => {
format!(
"Divisorial contraction of {} (→ {} singularities)",
divisor,
singularity_type.name()
)
}
MmpStep::Flip { locus } => format!("Flip over {}", locus),
MmpStep::Flop { locus } => format!("Flop over {}", locus),
MmpStep::MinimalModel => "Minimal model reached (K_X nef)".to_string(),
MmpStep::FiberSpace { base, fiber_dim } => {
format!("Mori fiber space over {} (fiber dim = {})", base, fiber_dim)
}
}
}
pub fn is_terminal(&self) -> bool {
matches!(self, MmpStep::MinimalModel | MmpStep::FiberSpace { .. })
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LogPairData {
pub variety: String,
pub boundary_components: Vec<(String, f64)>,
pub log_discrepancies: Vec<f64>,
}
#[allow(dead_code)]
impl LogPairData {
pub fn new(variety: &str) -> Self {
LogPairData {
variety: variety.to_string(),
boundary_components: Vec::new(),
log_discrepancies: Vec::new(),
}
}
pub fn add_boundary(&mut self, divisor: &str, coeff: f64) {
self.boundary_components.push((divisor.to_string(), coeff));
}
pub fn add_log_discrepancy(&mut self, a: f64) {
self.log_discrepancies.push(a);
}
pub fn total_coefficient(&self) -> f64 {
self.boundary_components.iter().map(|(_, a)| a).sum()
}
pub fn is_klt(&self) -> bool {
self.log_discrepancies.iter().all(|&a| a > -1.0)
}
pub fn is_log_canonical(&self) -> bool {
self.log_discrepancies.iter().all(|&a| a >= -1.0)
}
pub fn singularity_type(&self) -> &str {
if self.log_discrepancies.is_empty() {
"smooth"
} else if self.is_klt() {
"klt (Kawamata log terminal)"
} else if self.is_log_canonical() {
"lc (log canonical)"
} else {
"worse than log canonical"
}
}
}
#[derive(Debug, Clone)]
pub struct ZariskiDecomp {
pub divisor: String,
pub nef_part: Vec<(String, f64)>,
pub neg_part: Vec<(String, f64)>,
}
impl ZariskiDecomp {
pub fn new(
divisor: impl Into<String>,
nef_part: Vec<(String, f64)>,
neg_part: Vec<(String, f64)>,
) -> Self {
ZariskiDecomp {
divisor: divisor.into(),
nef_part,
neg_part,
}
}
pub fn is_negative_part_effective(&self) -> bool {
self.neg_part.iter().all(|(_, c)| *c >= 0.0)
}
pub fn nef_degree(&self) -> f64 {
self.nef_part.iter().map(|(_, c)| c).sum()
}
pub fn neg_degree(&self) -> f64 {
self.neg_part.iter().map(|(_, c)| c).sum()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProjectiveVariety {
pub dim: usize,
pub degree: u64,
pub name: String,
}
impl ProjectiveVariety {
pub fn new(dim: usize, degree: u64, name: impl Into<String>) -> Self {
ProjectiveVariety {
dim,
degree,
name: name.into(),
}
}
pub fn projective_space(n: usize) -> Self {
ProjectiveVariety::new(n, 1, format!("P^{}", n))
}
pub fn quadric(n: usize) -> Self {
ProjectiveVariety::new(n, 2, format!("Q^{}", n))
}
pub fn del_pezzo(d: usize) -> Self {
assert!((1..=9).contains(&d), "Del Pezzo degree must be 1..=9");
ProjectiveVariety::new(2, d as u64, format!("S_{}", d))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KodairaDim {
NegInfinity,
Finite(i64),
}
impl KodairaDim {
pub fn zero() -> Self {
KodairaDim::Finite(0)
}
pub fn product(self, other: KodairaDim) -> KodairaDim {
match (self, other) {
(KodairaDim::NegInfinity, _) | (_, KodairaDim::NegInfinity) => KodairaDim::NegInfinity,
(KodairaDim::Finite(a), KodairaDim::Finite(b)) => KodairaDim::Finite(a + b),
}
}
pub fn is_general_type(self, dim: usize) -> bool {
matches!(self, KodairaDim::Finite(k) if k == dim as i64)
}
pub fn is_uniruled(self) -> bool {
self == KodairaDim::NegInfinity
}
pub fn classify(self, dim: usize) -> &'static str {
match self {
KodairaDim::NegInfinity => "uniruled",
KodairaDim::Finite(0) => "Kodaira dim 0 (CY / K3 / abelian type)",
KodairaDim::Finite(k) if k == dim as i64 => "general type",
KodairaDim::Finite(_) => "intermediate Kodaira dimension",
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MMPStepData {
pub operation: MMPOperation,
pub result_variety: String,
pub exceptional_divisor: Option<String>,
}
#[allow(dead_code)]
impl MMPStepData {
pub fn new(op: MMPOperation, result: &str) -> Self {
MMPStepData {
operation: op,
result_variety: result.to_string(),
exceptional_divisor: None,
}
}
pub fn with_exceptional(mut self, div: &str) -> Self {
self.exceptional_divisor = Some(div.to_string());
self
}
pub fn description(&self) -> String {
let op_name = match &self.operation {
MMPOperation::DivisorialContraction => "Divisorial contraction",
MMPOperation::Flip => "Flip",
MMPOperation::MoriFiberSpace => "Mori fiber space",
MMPOperation::MinimalModel => "Minimal model",
};
format!("{} → {}", op_name, self.result_variety)
}
pub fn is_final(&self) -> bool {
matches!(
&self.operation,
MMPOperation::MoriFiberSpace | MMPOperation::MinimalModel
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SarkisovLinkType {
TypeI,
TypeII,
TypeIII,
TypeIV,
}
impl SarkisovLinkType {
pub fn description(&self) -> &'static str {
match self {
SarkisovLinkType::TypeI => "Type I: blow-up (left), fiber space change",
SarkisovLinkType::TypeII => "Type II: blow-up (both), same base",
SarkisovLinkType::TypeIII => "Type III: blow-down (right), base change",
SarkisovLinkType::TypeIV => "Type IV: blow-down (right), flip base",
}
}
pub fn code(&self) -> u8 {
match self {
SarkisovLinkType::TypeI => 1,
SarkisovLinkType::TypeII => 2,
SarkisovLinkType::TypeIII => 3,
SarkisovLinkType::TypeIV => 4,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct IitakaFibration {
pub source: String,
pub base: String,
pub fiber: String,
pub kodaira_dim: i64,
pub base_dim: usize,
}
#[allow(dead_code)]
impl IitakaFibration {
pub fn new(source: &str, base: &str, fiber: &str, kappa: i64) -> Self {
let base_dim = kappa.max(0) as usize;
IitakaFibration {
source: source.to_string(),
base: base.to_string(),
fiber: fiber.to_string(),
kodaira_dim: kappa,
base_dim,
}
}
pub fn addition_formula(&self, kappa_fiber: i64, kappa_base: i64) -> bool {
self.kodaira_dim <= kappa_fiber + kappa_base
}
pub fn description(&self) -> String {
format!(
"{} → {} (base, κ={}) with fiber {}",
self.source, self.base, self.kodaira_dim, self.fiber
)
}
pub fn is_general_type(&self, dim: usize) -> bool {
self.kodaira_dim == dim as i64
}
}
#[derive(Debug, Clone)]
pub struct MoriCone {
pub dim: usize,
pub extremal_rays: Vec<i64>,
}
impl MoriCone {
pub fn projective_space(n: usize) -> Self {
MoriCone {
dim: n,
extremal_rays: vec![-(n as i64 + 1)],
}
}
pub fn del_pezzo(d: usize) -> Self {
let blown_up = 9 - d;
let mut rays: Vec<i64> = vec![-3];
rays.extend(std::iter::repeat(-1).take(blown_up));
MoriCone {
dim: 2,
extremal_rays: rays,
}
}
pub fn num_extremal_rays(&self) -> usize {
self.extremal_rays.len()
}
pub fn is_fano(&self) -> bool {
self.extremal_rays.iter().all(|&r| r < 0)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct AbundanceData {
pub variety: String,
pub kodaira_dim: Option<i64>,
pub kx_nef: bool,
pub kx_semi_ample: bool,
pub dim: usize,
}
#[allow(dead_code)]
impl AbundanceData {
pub fn new(variety: &str, dim: usize) -> Self {
AbundanceData {
variety: variety.to_string(),
kodaira_dim: None,
kx_nef: false,
kx_semi_ample: false,
dim,
}
}
pub fn with_kodaira_dim(mut self, kappa: i64) -> Self {
self.kodaira_dim = Some(kappa);
self
}
pub fn nef(mut self) -> Self {
self.kx_nef = true;
self
}
pub fn abundant(mut self) -> Self {
self.kx_semi_ample = true;
self
}
pub fn abundance_status(&self) -> String {
if self.kx_nef && self.kx_semi_ample {
format!(
"Abundance holds for {}: K_X nef and semi-ample",
self.variety
)
} else if self.kx_nef {
format!("Abundance not yet verified for {}", self.variety)
} else {
format!("{} does not have nef canonical class", self.variety)
}
}
pub fn abundance_known(&self) -> bool {
self.dim <= 3
}
}
pub struct MmpFlowchart {
pub pair: LogPair,
pub history: Vec<MmpStep>,
pub picard_number: usize,
}
impl MmpFlowchart {
pub fn new(pair: LogPair, initial_picard: usize) -> Self {
MmpFlowchart {
pair,
history: Vec::new(),
picard_number: initial_picard,
}
}
pub fn apply(&mut self, step: MmpStep) -> bool {
let done = step.is_terminal();
match &step {
MmpStep::Contraction { .. } => {
if self.picard_number > 0 {
self.picard_number -= 1;
}
}
MmpStep::Flip { .. } | MmpStep::Flop { .. } => {}
MmpStep::MinimalModel | MmpStep::FiberSpace { .. } => {}
}
self.history.push(step);
done
}
pub fn run(&mut self, steps: Vec<MmpStep>) -> &[MmpStep] {
for step in steps {
let done = self.apply(step);
if done {
break;
}
}
&self.history
}
pub fn summary(&self) -> String {
let mut s = format!(
"MMP run on '{}' ({} steps, final Picard number: {}):\n",
self.pair.variety,
self.history.len(),
self.picard_number
);
for (i, step) in self.history.iter().enumerate() {
s.push_str(&format!(" Step {}: {}\n", i + 1, step.description()));
}
s
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SingularityType {
Smooth,
Terminal,
Canonical,
Klt,
Dlt,
LogCanonical,
}
impl SingularityType {
pub fn name(&self) -> &'static str {
match self {
SingularityType::Smooth => "smooth",
SingularityType::Terminal => "terminal",
SingularityType::Canonical => "canonical",
SingularityType::Klt => "klt",
SingularityType::Dlt => "dlt",
SingularityType::LogCanonical => "lc",
}
}
pub fn at_least_as_mild_as(&self, other: &SingularityType) -> bool {
let rank = |s: &SingularityType| -> usize {
match s {
SingularityType::Smooth => 0,
SingularityType::Terminal => 1,
SingularityType::Canonical => 2,
SingularityType::Klt => 3,
SingularityType::Dlt => 4,
SingularityType::LogCanonical => 5,
}
};
rank(self) <= rank(other)
}
}
#[derive(Debug, Clone)]
pub struct ExtremeRay {
pub generator: String,
pub k_degree: i64,
pub contraction_type: ContractionType,
}
impl ExtremeRay {
pub fn new(generator: impl Into<String>, k_degree: i64, ty: ContractionType) -> Self {
ExtremeRay {
generator: generator.into(),
k_degree,
contraction_type: ty,
}
}
pub fn is_k_negative(&self) -> bool {
self.k_degree < 0
}
pub fn mmp_step(&self) -> MmpStep {
match &self.contraction_type {
ContractionType::Divisorial => MmpStep::Contraction {
divisor: self.generator.clone(),
singularity_type: SingularityType::Terminal,
},
ContractionType::Small => MmpStep::Flip {
locus: self.generator.clone(),
},
ContractionType::FiberType => MmpStep::FiberSpace {
base: format!("base({})", self.generator),
fiber_dim: 1,
},
}
}
}