use rithm::{big_int, fraction};
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::ops::{Add, Mul, Sub};
use traiter::numbers::{Endianness, FromBytes, Sign, Signed, ToBytes, Zero};
use crate::locatable::Location;
use crate::operations::{
CrossMultiply, DotMultiply, LocatePointInPointPointPointCircle, Square,
SquaredMetric,
};
use crate::traits::{
Elemental, Multipolygonal, Multisegmental, Multivertexal, Polygonal,
};
use super::impl_box_wrapper::impl_box_wrapper;
use super::impl_constrained_delaunay_triangulation_wrapper::impl_constrained_delaunay_triangulation_wrapper;
use super::impl_contour_wrapper::impl_contour_wrapper;
use super::impl_delaunay_triangulation_wrapper::impl_delaunay_triangulation_wrapper;
use super::impl_empty_wrapper::impl_empty_wrapper;
use super::impl_multipolygon_wrapper::impl_multipolygon_wrapper;
use super::impl_multisegment_wrapper::impl_multisegment_wrapper;
use super::impl_point_wrapper::impl_point_wrapper;
use super::impl_polygon_wrapper::impl_polygon_wrapper;
use super::impl_py_sequence::impl_py_sequence;
use super::impl_segment_wrapper::impl_segment_wrapper;
use super::impl_trapezoidation_wrapper::impl_trapezoidation_wrapper;
use super::reference;
use super::traits::{TryFromPyAny, TryToPyAny};
#[pyo3::pymodule]
fn _cexact(
py: pyo3::Python<'_>,
module: &pyo3::Bound<'_, pyo3::types::PyModule>,
) -> pyo3::PyResult<()> {
use pyo3::types::PyModuleMethods;
module.add_class::<PyBox>()?;
module.add_class::<PyConstrainedDelaunayTriangulation>()?;
module.add_class::<PyContour>()?;
module.add_class::<PyDelaunayTriangulation>()?;
module.add_class::<PyEmpty>()?;
module.add_class::<PyTrapezoidation>()?;
module.add_class::<PyMultipolygon>()?;
module.add_class::<PyMultisegment>()?;
module.add_class::<PyPoint>()?;
module.add_class::<PyPolygon>()?;
module.add_class::<PySegment>()?;
pyo3::types::PySequence::register::<PyContourSegments>(py)?;
pyo3::types::PySequence::register::<PyContourVertices>(py)?;
pyo3::types::PySequence::register::<PyMultipolygonPolygons>(py)?;
pyo3::types::PySequence::register::<PyMultisegmentSegments>(py)?;
pyo3::types::PySequence::register::<PyPolygonHoles>(py)?;
Ok(())
}
#[cfg(target_arch = "x86")]
type Digit = u16;
#[cfg(not(target_arch = "x86"))]
type Digit = u32;
const DIGIT_BITNESS: usize = (Digit::BITS - 1) as usize;
const _: () =
assert!(big_int::is_valid_digit_bitness::<Digit, DIGIT_BITNESS>());
type BigInt = big_int::BigInt<Digit, DIGIT_BITNESS>;
type Fraction = fraction::Fraction<BigInt>;
impl From<Box> for PyBox {
fn from(value: Box) -> Self {
Self(value)
}
}
impl From<Contour> for PyContour {
fn from(value: Contour) -> Self {
Self(value)
}
}
impl From<Vec<Point>> for PyContour {
fn from(value: Vec<Point>) -> Self {
Self(Contour::new(value))
}
}
impl From<Vec<Polygon>> for PyMultipolygon {
fn from(value: Vec<Polygon>) -> Self {
Self(Multipolygon::new(value))
}
}
impl From<Vec<Segment>> for PyMultisegment {
fn from(value: Vec<Segment>) -> Self {
Self(Multisegment::new(value))
}
}
impl From<Point> for PyPoint {
fn from(value: Point) -> Self {
Self(value)
}
}
impl From<Polygon> for PyPolygon {
fn from(value: Polygon) -> Self {
Self(value)
}
}
impl From<Segment> for PySegment {
fn from(value: Segment) -> Self {
Self(value)
}
}
impl<Point> CrossMultiply for &Point
where
Fraction: Mul<Output = Fraction> + Sub<Output = Fraction>,
for<'a> &'a Fraction: Sub<Output = Fraction>,
for<'a> &'a Point: Elemental<Coordinate = &'a Fraction>,
{
type Output = Fraction;
fn cross_multiply(
first_start: Self,
first_end: Self,
second_start: Self,
second_end: Self,
) -> Self::Output {
let (first_start_x, first_start_y) = first_start.coordinates();
let (first_end_x, first_end_y) = first_end.coordinates();
let (second_start_x, second_start_y) = second_start.coordinates();
let (second_end_x, second_end_y) = second_end.coordinates();
(first_end_x - first_start_x) * (second_end_y - second_start_y)
- (first_end_y - first_start_y) * (second_end_x - second_start_x)
}
}
impl<Point> DotMultiply for &Point
where
Fraction: Add<Output = Fraction> + Mul<Output = Fraction>,
for<'a> &'a Fraction: Sub<Output = Fraction>,
for<'a> &'a Point: Elemental<Coordinate = &'a Fraction>,
{
type Output = Fraction;
fn dot_multiply(
first_start: Self,
first_end: Self,
second_start: Self,
second_end: Self,
) -> Self::Output {
let (first_start_x, first_start_y) = first_start.coordinates();
let (first_end_x, first_end_y) = first_end.coordinates();
let (second_start_x, second_start_y) = second_start.coordinates();
let (second_end_x, second_end_y) = second_end.coordinates();
(first_end_x - first_start_x) * (second_end_x - second_start_x)
+ (first_end_y - first_start_y) * (second_end_y - second_start_y)
}
}
impl<'a, Point> LocatePointInPointPointPointCircle for &'a Point
where
&'a Point: Elemental<Coordinate = &'a Fraction>,
Fraction: Add<Output = Fraction>
+ Mul<Output = Fraction>
+ Sub<Output = Fraction>,
for<'b> &'b Fraction:
Mul<Output = Fraction> + Signed + Sub<Output = Fraction>,
{
fn locate_point_in_point_point_point_circle(
self,
first: Self,
second: Self,
third: Self,
) -> Location {
let (first_dx, first_dy) =
(first.x() - self.x(), first.y() - self.y());
let (second_dx, second_dy) =
(second.x() - self.x(), second.y() - self.y());
let (third_dx, third_dy) =
(third.x() - self.x(), third.y() - self.y());
match ((&first_dx * &first_dx + &first_dy * &first_dy)
* (&second_dx * &third_dy - &second_dy * &third_dx)
- (&second_dx * &second_dx + &second_dy * &second_dy)
* (&first_dx * &third_dy - &first_dy * &third_dx)
+ (&third_dx * &third_dx + &third_dy * &third_dy)
* (first_dx * second_dy - first_dy * second_dx))
.sign()
{
Sign::Negative => Location::Exterior,
Sign::Positive => Location::Interior,
Sign::Zero => Location::Boundary,
}
}
}
impl Square for Fraction
where
Fraction: Clone + Mul<Output = Fraction>,
{
type Output = Self;
fn square(self) -> Self::Output {
self.clone() * self
}
}
impl<Point> SquaredMetric for &Point
where
Fraction: Add<Output = Fraction> + Square<Output = Fraction>,
for<'a> &'a Fraction: Sub<Output = Fraction>,
for<'a> &'a Point: Elemental<Coordinate = &'a Fraction>,
{
type Output = Fraction;
fn squared_distance_to(self, other: Self) -> Self::Output {
let (start_x, start_y) = self.coordinates();
let (other_start_x, other_start_y) = other.coordinates();
(start_x - other_start_x).square() + (start_y - other_start_y).square()
}
}
const INVALID_SCALAR_TYPE_ERROR_MESSAGE: &str =
"Scalar should be a rational number.";
const UNDEFINED_DIVISION_ERROR_MESSAGE: &str =
"Division by zero is undefined.";
impl TryFromPyAny for Fraction {
fn try_from_py_any(
value: &pyo3::Bound<'_, pyo3::PyAny>,
py: pyo3::Python<'_>,
) -> pyo3::PyResult<Self> {
use pyo3::types::PyAnyMethods;
if value.is_instance(&<pyo3::types::PyFloat as pyo3::type_object::PyTypeInfo>::type_object(py))? {
Fraction::try_from(value.extract::<f64>()?).map_err(|error| {
match error {
fraction::FromFloatConstructionError::Infinity => {
pyo3::exceptions::PyOverflowError::new_err(error.to_string())
}
_ => pyo3::exceptions::PyValueError::new_err(error.to_string()),
}
})
} else {
let numerator = try_py_integral_to_big_int(
value.getattr(pyo3::intern!(py, "numerator")).map_err(
|_| {
pyo3::exceptions::PyTypeError::new_err(
INVALID_SCALAR_TYPE_ERROR_MESSAGE,
)
},
)?,
)?;
let denominator = try_py_integral_to_big_int(
value.getattr(pyo3::intern!(py, "denominator")).map_err(
|_| {
pyo3::exceptions::PyTypeError::new_err(
INVALID_SCALAR_TYPE_ERROR_MESSAGE,
)
},
)?,
)?;
match Fraction::new(numerator, denominator) {
Some(value) => Ok(value),
None => Err(pyo3::exceptions::PyZeroDivisionError::new_err(
UNDEFINED_DIVISION_ERROR_MESSAGE,
)),
}
}
}
}
impl TryToPyAny for &Fraction {
fn try_to_py_any(
self,
py: pyo3::Python<'_>,
) -> pyo3::PyResult<pyo3::Bound<'_, pyo3::PyAny>> {
use pyo3::types::PyAnyMethods;
static FRACTION_CLS: pyo3::sync::PyOnceLock<pyo3::Py<pyo3::PyAny>> =
pyo3::sync::PyOnceLock::new();
FRACTION_CLS
.get_or_try_init(py, || {
py.import("rithm.fraction")?
.getattr(pyo3::intern!(py, "Fraction"))
.map(|value| {
pyo3::IntoPyObject::into_pyobject(value, py)
.unwrap()
.into_any()
.unbind()
})
})?
.call(
py,
(
big_int_to_py_long(self.numerator(), py),
big_int_to_py_long(self.denominator(), py),
),
None,
)
.map(|value| value.into_bound(py))
}
}
impl From<PyContour> for Contour {
fn from(value: PyContour) -> Self {
value.0
}
}
impl From<PyPoint> for Point {
fn from(value: PyPoint) -> Self {
value.0
}
}
impl From<PyPolygon> for Polygon {
fn from(value: PyPolygon) -> Self {
value.0
}
}
impl From<PySegment> for Segment {
fn from(value: PySegment) -> Self {
value.0
}
}
impl<'py> pyo3::IntoPyObject<'py> for Box {
type Target = <PyBox as pyo3::IntoPyObject<'py>>::Target;
type Output = <PyBox as pyo3::IntoPyObject<'py>>::Output;
type Error = <PyBox as pyo3::IntoPyObject<'py>>::Error;
fn into_pyobject(
self,
py: pyo3::Python<'py>,
) -> Result<Self::Output, Self::Error> {
pyo3::IntoPyObject::into_pyobject(PyBox(self), py)
}
}
impl<'py> pyo3::IntoPyObject<'py> for Contour {
type Target = <PyContour as pyo3::IntoPyObject<'py>>::Target;
type Output = <PyContour as pyo3::IntoPyObject<'py>>::Output;
type Error = <PyContour as pyo3::IntoPyObject<'py>>::Error;
fn into_pyobject(
self,
py: pyo3::Python<'py>,
) -> Result<Self::Output, Self::Error> {
pyo3::IntoPyObject::into_pyobject(PyContour(self), py)
}
}
impl<'py> pyo3::IntoPyObject<'py> for Multipolygon {
type Target = <PyMultipolygon as pyo3::IntoPyObject<'py>>::Target;
type Output = <PyMultipolygon as pyo3::IntoPyObject<'py>>::Output;
type Error = <PyMultipolygon as pyo3::IntoPyObject<'py>>::Error;
fn into_pyobject(
self,
py: pyo3::Python<'py>,
) -> Result<Self::Output, Self::Error> {
pyo3::IntoPyObject::into_pyobject(PyMultipolygon(self), py)
}
}
impl<'py> pyo3::IntoPyObject<'py> for Point {
type Target = <PyPoint as pyo3::IntoPyObject<'py>>::Target;
type Output = <PyPoint as pyo3::IntoPyObject<'py>>::Output;
type Error = <PyPoint as pyo3::IntoPyObject<'py>>::Error;
fn into_pyobject(
self,
py: pyo3::Python<'py>,
) -> Result<Self::Output, Self::Error> {
pyo3::IntoPyObject::into_pyobject(PyPoint(self), py)
}
}
impl<'py> pyo3::IntoPyObject<'py> for Polygon {
type Target = <PyPolygon as pyo3::IntoPyObject<'py>>::Target;
type Output = <PyPolygon as pyo3::IntoPyObject<'py>>::Output;
type Error = <PyPolygon as pyo3::IntoPyObject<'py>>::Error;
fn into_pyobject(
self,
py: pyo3::Python<'py>,
) -> Result<Self::Output, Self::Error> {
pyo3::IntoPyObject::into_pyobject(PyPolygon(self), py)
}
}
impl<'py> pyo3::IntoPyObject<'py> for Segment {
type Target = <PySegment as pyo3::IntoPyObject<'py>>::Target;
type Output = <PySegment as pyo3::IntoPyObject<'py>>::Output;
type Error = <PySegment as pyo3::IntoPyObject<'py>>::Error;
fn into_pyobject(
self,
py: pyo3::Python<'py>,
) -> Result<Self::Output, Self::Error> {
pyo3::IntoPyObject::into_pyobject(PySegment(self), py)
}
}
type Box = crate::bounded::Box<Fraction>;
type ConstrainedDelaunayTriangulation =
crate::triangulation::ConstrainedDelaunayTriangulation<Point>;
type Contour = crate::geometries::Contour<Fraction>;
type DelaunayTriangulation =
crate::triangulation::DelaunayTriangulation<Point>;
type Empty = crate::geometries::Empty;
type Multipolygon = crate::geometries::Multipolygon<Fraction>;
type Multisegment = crate::geometries::Multisegment<Fraction>;
type Point = crate::geometries::Point<Fraction>;
type Polygon = crate::geometries::Polygon<Fraction>;
type Segment = crate::geometries::Segment<Fraction>;
type Trapezoidation = crate::seidel::Trapezoidation<Point>;
#[pyo3::pyclass(name = "Box", module = "rene.exact", skip_from_py_object)]
#[derive(Clone)]
pub struct PyBox(Box);
#[pyo3::pyclass(
name = "ConstrainedDelaunayTriangulation",
module = "rene.exact",
skip_from_py_object
)]
#[derive(Clone)]
struct PyConstrainedDelaunayTriangulation(ConstrainedDelaunayTriangulation);
#[pyo3::pyclass(name = "Contour", module = "rene.exact", from_py_object)]
#[derive(Clone)]
pub struct PyContour(Contour);
#[pyo3::pyclass(
name = "DelaunayTriangulation",
module = "rene.exact",
skip_from_py_object
)]
#[derive(Clone)]
struct PyDelaunayTriangulation(DelaunayTriangulation);
#[pyo3::pyclass(name = "Empty", module = "rene.exact", skip_from_py_object)]
#[derive(Clone, Default)]
struct PyEmpty(Empty);
#[pyo3::pyclass(
name = "Multipolygon",
module = "rene.exact",
skip_from_py_object
)]
#[derive(Clone)]
pub struct PyMultipolygon(Multipolygon);
#[pyo3::pyclass(
name = "Multisegment",
module = "rene.exact",
skip_from_py_object
)]
#[derive(Clone)]
struct PyMultisegment(Multisegment);
#[pyo3::pyclass(name = "Polygon", module = "rene.exact", from_py_object)]
#[derive(Clone)]
pub struct PyPolygon(Polygon);
#[pyo3::pyclass(name = "Point", module = "rene.exact", from_py_object)]
#[derive(Clone)]
pub struct PyPoint(Point);
#[pyo3::pyclass(name = "Segment", module = "rene.exact", from_py_object)]
#[derive(Clone)]
pub struct PySegment(Segment);
#[pyo3::pyclass(
name = "Trapezoidation",
module = "rene.exact",
skip_from_py_object
)]
#[derive(Clone)]
struct PyTrapezoidation(Trapezoidation);
impl_box_wrapper!();
impl_constrained_delaunay_triangulation_wrapper!();
impl_contour_wrapper!();
impl_delaunay_triangulation_wrapper!();
impl_empty_wrapper!();
impl_multipolygon_wrapper!();
impl_multisegment_wrapper!();
impl_point_wrapper!();
impl_polygon_wrapper!();
impl_segment_wrapper!();
impl_trapezoidation_wrapper!();
type PyContourReference = reference::Reference<PyContour>;
type PyMultisegmentReference = reference::Reference<PyMultisegment>;
type PyMultipolygonReference = reference::Reference<PyMultipolygon>;
type PyPolygonReference = reference::Reference<PyPolygon>;
#[pyo3::pyclass(module = "rene.exact", name = "_ContourSegments", sequence)]
struct PyContourSegments {
contour: PyContourReference,
start: isize,
stop: isize,
step: isize,
}
#[pyo3::pyclass(module = "rene.exact", name = "_ContourVertices", sequence)]
struct PyContourVertices {
contour: PyContourReference,
start: isize,
stop: isize,
step: isize,
}
#[pyo3::pyclass(
module = "rene.exact",
name = "_MultisegmentSegments",
sequence
)]
struct PyMultisegmentSegments {
multisegment: PyMultisegmentReference,
start: isize,
stop: isize,
step: isize,
}
#[pyo3::pyclass(
module = "rene.exact",
name = "_MultipolygonPolygons",
sequence
)]
struct PyMultipolygonPolygons {
multipolygon: PyMultipolygonReference,
start: isize,
stop: isize,
step: isize,
}
#[pyo3::pyclass(module = "rene.exact", name = "_PolygonHoles", sequence)]
struct PyPolygonHoles {
polygon: PyPolygonReference,
start: isize,
stop: isize,
step: isize,
}
impl_py_sequence!(
PyContourSegments,
contour,
segment,
segments,
PySegment,
Segment
);
impl_py_sequence!(PyContourVertices, contour, point, vertices, PyPoint, Point);
impl_py_sequence!(
PyMultisegmentSegments,
multisegment,
segment,
segments,
PySegment,
Segment
);
impl_py_sequence!(
PyMultipolygonPolygons,
multipolygon,
polygon,
polygons,
PyPolygon,
Polygon
);
impl_py_sequence!(PyPolygonHoles, polygon, contour, holes, PyContour, Contour);
fn big_int_to_py_long<'py>(
value: &BigInt,
py: pyo3::Python<'py>,
) -> pyo3::Bound<'py, pyo3::PyAny> {
let buffer = value.to_bytes(Endianness::Little);
unsafe {
pyo3::Bound::<'py, pyo3::PyAny>::from_owned_ptr(
py,
pyo3::ffi::_PyLong_FromByteArray(
buffer.as_ptr(),
buffer.len(),
1,
1,
),
)
}
}
fn try_py_integral_to_big_int<'py>(
value: pyo3::Bound<'py, pyo3::PyAny>,
) -> pyo3::PyResult<BigInt> {
let ptr = value.as_ptr();
let py = value.py();
unsafe {
let ptr = pyo3::ffi::PyNumber_Long(ptr);
if ptr.is_null() {
return Err(pyo3::PyErr::fetch(py));
}
let bits_count = pyo3::ffi::_PyLong_NumBits(ptr);
match bits_count.cmp(&0) {
Ordering::Less => Err(pyo3::PyErr::fetch(py)),
Ordering::Equal => Ok(BigInt::zero()),
Ordering::Greater => {
let bytes_count = bits_count / (u8::BITS as usize) + 1;
let mut buffer = vec![0u8; bytes_count];
if pyo3::ffi::_PyLong_AsByteArray(
pyo3::Bound::<'py, pyo3::PyAny>::from_owned_ptr(py, ptr)
.as_ptr()
.cast::<pyo3::ffi::PyLongObject>(),
buffer.as_mut_ptr(),
buffer.len(),
1,
1,
) < 0
{
Err(pyo3::PyErr::fetch(py))
} else {
Ok(BigInt::from_bytes(
buffer.as_mut_slice(),
Endianness::Little,
))
}
}
}
}
}