use num_bigint::BigInt;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
pub(crate) enum DomainKind {
Integer,
Rational,
FiniteField(BigInt),
}
impl DomainKind {
pub(crate) fn from_py(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
if let Ok(s) = obj.extract::<String>() {
match s.as_str() {
"integer" | "int" | "Z" => Ok(DomainKind::Integer),
"rational" | "rat" | "Q" => Ok(DomainKind::Rational),
other => Err(PyValueError::new_err(format!(
"unknown domain string: {other:?} (expected one of \
'integer'/'int'/'Z', 'rational'/'rat'/'Q', or a FiniteField)"
))),
}
} else if let Ok(fq) = obj.extract::<PyRef<'_, PyFiniteField>>() {
Ok(DomainKind::FiniteField(fq.modulus.clone()))
} else {
Err(PyValueError::new_err(
"domain must be a string ('integer'/'rational') or a FiniteField instance",
))
}
}
}
#[pyclass(name = "IntegerDomain", skip_from_py_object)]
#[derive(Clone)]
pub struct PyIntegerDomain;
#[pymethods]
impl PyIntegerDomain {
#[new]
fn new() -> Self {
PyIntegerDomain
}
fn __repr__(&self) -> String {
"IntegerDomain()".to_string()
}
}
#[pyclass(name = "RationalDomain", skip_from_py_object)]
#[derive(Clone)]
pub struct PyRationalDomain;
#[pymethods]
impl PyRationalDomain {
#[new]
fn new() -> Self {
PyRationalDomain
}
fn __repr__(&self) -> String {
"RationalDomain()".to_string()
}
}
#[pyclass(name = "FiniteField", from_py_object)]
#[derive(Clone)]
pub struct PyFiniteField {
pub(crate) modulus: BigInt,
}
#[pymethods]
impl PyFiniteField {
#[new]
fn new(modulus: i64) -> PyResult<Self> {
if modulus < 2 {
return Err(PyValueError::new_err(format!(
"finite-field modulus must be a prime ≥ 2, got {modulus}"
)));
}
Ok(PyFiniteField {
modulus: BigInt::from(modulus),
})
}
#[getter]
fn modulus(&self) -> String {
self.modulus.to_string()
}
fn __repr__(&self) -> String {
format!("FiniteField({})", self.modulus)
}
}