use hyperreal::Real;
use hypersolve::{
AlgebraicRootKind, AlgebraicRootPolynomialImageReport, AlgebraicRootPolynomialImageStatus,
AlgebraicRootRationalImageReport, AlgebraicRootRationalImageStatus,
AlgebraicRootRepresentation, AlgebraicRootValidationReport, AlgebraicRootValidationStatus,
IsolatedRootInterval, SymbolId, transform_algebraic_root_polynomial_image,
transform_algebraic_root_rational_image, validate_algebraic_root_representation,
};
use crate::classify::compare_reals;
use crate::{
BezierAlgebraicParameter2, CubicBezier2, CurvePolicy, CurveResult, QuadraticBezier2,
RationalQuadraticBezier2,
};
use std::cmp::Ordering;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BezierAlgebraicImageStatus {
Transformed,
InvalidParameterEvidence,
XImageFailed,
YImageFailed,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BezierAlgebraicCoordinateImage {
coefficients: Vec<Real>,
report: AlgebraicRootPolynomialImageReport,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BezierAlgebraicRationalCoordinateImage {
numerator_coefficients: Vec<Real>,
denominator_coefficients: Vec<Real>,
report: AlgebraicRootRationalImageReport,
}
impl BezierAlgebraicRationalCoordinateImage {
pub fn numerator_coefficients(&self) -> &[Real] {
&self.numerator_coefficients
}
pub fn denominator_coefficients(&self) -> &[Real] {
&self.denominator_coefficients
}
pub const fn report(&self) -> &AlgebraicRootRationalImageReport {
&self.report
}
pub fn representation(&self) -> Option<&AlgebraicRootRepresentation> {
self.report.representation.as_ref()
}
}
impl BezierAlgebraicCoordinateImage {
pub fn coefficients(&self) -> &[Real] {
&self.coefficients
}
pub const fn report(&self) -> &AlgebraicRootPolynomialImageReport {
&self.report
}
pub fn representation(&self) -> Option<&AlgebraicRootRepresentation> {
self.report.representation.as_ref()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus,
parameter: AlgebraicRootRepresentation,
x: Option<BezierAlgebraicCoordinateImage>,
y: Option<BezierAlgebraicCoordinateImage>,
message: Option<String>,
}
impl BezierAlgebraicPointImage2 {
pub const fn status(&self) -> BezierAlgebraicImageStatus {
self.status
}
pub const fn parameter(&self) -> &AlgebraicRootRepresentation {
&self.parameter
}
pub const fn x(&self) -> Option<&BezierAlgebraicCoordinateImage> {
self.x.as_ref()
}
pub const fn y(&self) -> Option<&BezierAlgebraicCoordinateImage> {
self.y.as_ref()
}
pub fn message(&self) -> Option<&str> {
self.message.as_deref()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus,
parameter: AlgebraicRootRepresentation,
dx: Option<BezierAlgebraicCoordinateImage>,
dy: Option<BezierAlgebraicCoordinateImage>,
message: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RationalBezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus,
parameter: AlgebraicRootRepresentation,
x: Option<BezierAlgebraicRationalCoordinateImage>,
y: Option<BezierAlgebraicRationalCoordinateImage>,
message: Option<String>,
}
impl RationalBezierAlgebraicPointImage2 {
pub const fn status(&self) -> BezierAlgebraicImageStatus {
self.status
}
pub const fn parameter(&self) -> &AlgebraicRootRepresentation {
&self.parameter
}
pub const fn x(&self) -> Option<&BezierAlgebraicRationalCoordinateImage> {
self.x.as_ref()
}
pub const fn y(&self) -> Option<&BezierAlgebraicRationalCoordinateImage> {
self.y.as_ref()
}
pub fn message(&self) -> Option<&str> {
self.message.as_deref()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RationalBezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus,
parameter: AlgebraicRootRepresentation,
dx: Option<BezierAlgebraicRationalCoordinateImage>,
dy: Option<BezierAlgebraicRationalCoordinateImage>,
message: Option<String>,
}
impl RationalBezierAlgebraicTangentImage2 {
pub const fn status(&self) -> BezierAlgebraicImageStatus {
self.status
}
pub const fn parameter(&self) -> &AlgebraicRootRepresentation {
&self.parameter
}
pub const fn dx(&self) -> Option<&BezierAlgebraicRationalCoordinateImage> {
self.dx.as_ref()
}
pub const fn dy(&self) -> Option<&BezierAlgebraicRationalCoordinateImage> {
self.dy.as_ref()
}
pub fn message(&self) -> Option<&str> {
self.message.as_deref()
}
}
impl BezierAlgebraicTangentImage2 {
pub const fn status(&self) -> BezierAlgebraicImageStatus {
self.status
}
pub const fn parameter(&self) -> &AlgebraicRootRepresentation {
&self.parameter
}
pub const fn dx(&self) -> Option<&BezierAlgebraicCoordinateImage> {
self.dx.as_ref()
}
pub const fn dy(&self) -> Option<&BezierAlgebraicCoordinateImage> {
self.dy.as_ref()
}
pub fn message(&self) -> Option<&str> {
self.message.as_deref()
}
}
impl QuadraticBezier2 {
pub fn point_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicPointImage2> {
point_image(parameter, quadratic_point_coefficients(self), policy)
}
pub fn tangent_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicTangentImage2> {
tangent_image(parameter, quadratic_tangent_coefficients(self), policy)
}
pub fn second_derivative_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicTangentImage2> {
tangent_image(
parameter,
second_derivative_polynomials(quadratic_tangent_coefficients(self)),
policy,
)
}
}
impl CubicBezier2 {
pub fn point_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicPointImage2> {
point_image(parameter, cubic_point_coefficients(self), policy)
}
pub fn tangent_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicTangentImage2> {
tangent_image(parameter, cubic_tangent_coefficients(self), policy)
}
pub fn second_derivative_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicTangentImage2> {
tangent_image(
parameter,
second_derivative_polynomials(cubic_tangent_coefficients(self)),
policy,
)
}
pub fn third_derivative_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicTangentImage2> {
tangent_image(
parameter,
second_derivative_polynomials(second_derivative_polynomials(
cubic_tangent_coefficients(self),
)),
policy,
)
}
}
impl RationalQuadraticBezier2 {
pub fn point_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<RationalBezierAlgebraicPointImage2> {
rational_point_image(parameter, rational_point_coefficients(self), policy)
}
pub fn tangent_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<RationalBezierAlgebraicTangentImage2> {
rational_tangent_image(parameter, rational_tangent_coefficients(self), policy)
}
pub fn second_derivative_at_algebraic_parameter(
&self,
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> CurveResult<RationalBezierAlgebraicTangentImage2> {
rational_tangent_image(
parameter,
rational_second_derivative_coefficients(self),
policy,
)
}
}
fn point_image(
parameter: &BezierAlgebraicParameter2,
coefficients: CoordinatePolynomials,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicPointImage2> {
let parameter_root = parameter_representation(parameter, policy);
if !parameter_root.is_valid() {
return Ok(BezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::InvalidParameterEvidence,
parameter: parameter_root,
x: None,
y: None,
message: Some("Bezier algebraic parameter evidence did not validate".to_owned()),
});
}
let Some(x) = coordinate_image(¶meter_root, coefficients.x, policy) else {
return Ok(BezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::XImageFailed,
parameter: parameter_root,
x: None,
y: None,
message: Some("x coordinate polynomial image failed".to_owned()),
});
};
let Some(y) = coordinate_image(¶meter_root, coefficients.y, policy) else {
return Ok(BezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::YImageFailed,
parameter: parameter_root,
x: Some(x),
y: None,
message: Some("y coordinate polynomial image failed".to_owned()),
});
};
Ok(BezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::Transformed,
parameter: parameter_root,
x: Some(x),
y: Some(y),
message: None,
})
}
fn rational_point_image(
parameter: &BezierAlgebraicParameter2,
coefficients: RationalCoordinatePolynomials,
policy: &CurvePolicy,
) -> CurveResult<RationalBezierAlgebraicPointImage2> {
let parameter_root = parameter_representation(parameter, policy);
if !parameter_root.is_valid() {
return Ok(RationalBezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::InvalidParameterEvidence,
parameter: parameter_root,
x: None,
y: None,
message: Some("Bezier algebraic parameter evidence did not validate".to_owned()),
});
}
let Some(x) = rational_coordinate_image(
¶meter_root,
coefficients.x_numerator,
coefficients.denominator.clone(),
policy,
) else {
return Ok(RationalBezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::XImageFailed,
parameter: parameter_root,
x: None,
y: None,
message: Some("x rational coordinate image failed".to_owned()),
});
};
let Some(y) = rational_coordinate_image(
¶meter_root,
coefficients.y_numerator,
coefficients.denominator,
policy,
) else {
return Ok(RationalBezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::YImageFailed,
parameter: parameter_root,
x: Some(x),
y: None,
message: Some("y rational coordinate image failed".to_owned()),
});
};
Ok(RationalBezierAlgebraicPointImage2 {
status: BezierAlgebraicImageStatus::Transformed,
parameter: parameter_root,
x: Some(x),
y: Some(y),
message: None,
})
}
fn tangent_image(
parameter: &BezierAlgebraicParameter2,
coefficients: CoordinatePolynomials,
policy: &CurvePolicy,
) -> CurveResult<BezierAlgebraicTangentImage2> {
let parameter_root = parameter_representation(parameter, policy);
if !parameter_root.is_valid() {
return Ok(BezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::InvalidParameterEvidence,
parameter: parameter_root,
dx: None,
dy: None,
message: Some("Bezier algebraic parameter evidence did not validate".to_owned()),
});
}
let Some(dx) = coordinate_image(¶meter_root, coefficients.x, policy) else {
return Ok(BezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::XImageFailed,
parameter: parameter_root,
dx: None,
dy: None,
message: Some("dx coordinate polynomial image failed".to_owned()),
});
};
let Some(dy) = coordinate_image(¶meter_root, coefficients.y, policy) else {
return Ok(BezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::YImageFailed,
parameter: parameter_root,
dx: Some(dx),
dy: None,
message: Some("dy coordinate polynomial image failed".to_owned()),
});
};
Ok(BezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::Transformed,
parameter: parameter_root,
dx: Some(dx),
dy: Some(dy),
message: None,
})
}
fn rational_tangent_image(
parameter: &BezierAlgebraicParameter2,
coefficients: RationalTangentPolynomials,
policy: &CurvePolicy,
) -> CurveResult<RationalBezierAlgebraicTangentImage2> {
let parameter_root = parameter_representation(parameter, policy);
if !parameter_root.is_valid() {
return Ok(RationalBezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::InvalidParameterEvidence,
parameter: parameter_root,
dx: None,
dy: None,
message: Some("Bezier algebraic parameter evidence did not validate".to_owned()),
});
}
let Some(dx) = rational_coordinate_image(
¶meter_root,
coefficients.dx_numerator,
coefficients.denominator.clone(),
policy,
) else {
return Ok(RationalBezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::XImageFailed,
parameter: parameter_root,
dx: None,
dy: None,
message: Some("dx rational coordinate image failed".to_owned()),
});
};
let Some(dy) = rational_coordinate_image(
¶meter_root,
coefficients.dy_numerator,
coefficients.denominator,
policy,
) else {
return Ok(RationalBezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::YImageFailed,
parameter: parameter_root,
dx: Some(dx),
dy: None,
message: Some("dy rational coordinate image failed".to_owned()),
});
};
Ok(RationalBezierAlgebraicTangentImage2 {
status: BezierAlgebraicImageStatus::Transformed,
parameter: parameter_root,
dx: Some(dx),
dy: Some(dy),
message: None,
})
}
fn coordinate_image(
parameter: &AlgebraicRootRepresentation,
coefficients: Vec<Real>,
policy: &CurvePolicy,
) -> Option<BezierAlgebraicCoordinateImage> {
if let Some(parameter_value) = parameter.exact_rational_witness() {
let value = evaluate_power_polynomial(&coefficients, parameter_value);
let representation = exact_real_representation(&value);
return Some(BezierAlgebraicCoordinateImage {
report: AlgebraicRootPolynomialImageReport {
status: AlgebraicRootPolynomialImageStatus::Transformed,
image_coefficients: coefficients.clone(),
representation: Some(representation),
message: None,
},
coefficients,
});
}
if coefficients.len() == 1 {
let representation = exact_real_representation(&coefficients[0]);
return Some(BezierAlgebraicCoordinateImage {
report: AlgebraicRootPolynomialImageReport {
status: AlgebraicRootPolynomialImageStatus::Transformed,
image_coefficients: coefficients.clone(),
representation: Some(representation),
message: None,
},
coefficients,
});
}
let report = transform_algebraic_root_polynomial_image(
parameter,
&coefficients,
policy.predicate_policy,
);
(report.status == AlgebraicRootPolynomialImageStatus::Transformed).then_some(
BezierAlgebraicCoordinateImage {
coefficients,
report,
},
)
}
fn rational_coordinate_image(
parameter: &AlgebraicRootRepresentation,
numerator_coefficients: Vec<Real>,
denominator_coefficients: Vec<Real>,
policy: &CurvePolicy,
) -> Option<BezierAlgebraicRationalCoordinateImage> {
let report = transform_algebraic_root_rational_image(
parameter,
&numerator_coefficients,
&denominator_coefficients,
policy.predicate_policy,
);
(report.status == AlgebraicRootRationalImageStatus::Transformed).then_some(
BezierAlgebraicRationalCoordinateImage {
numerator_coefficients,
denominator_coefficients,
report,
},
)
}
fn exact_real_representation(value: &Real) -> AlgebraicRootRepresentation {
AlgebraicRootRepresentation {
constraint_index: 0,
symbol: SymbolId(0),
interval_index: 0,
polynomial_coefficients: vec![Real::zero() - value, Real::one()],
interval: IsolatedRootInterval {
lower: value.clone(),
upper: value.clone(),
exact_root: Some(value.clone()),
distinct_root_count: 1,
},
kind: AlgebraicRootKind::ExactRationalWitness,
validation: AlgebraicRootValidationReport {
status: AlgebraicRootValidationStatus::Valid,
message: None,
},
}
}
fn evaluate_power_polynomial(coefficients: &[Real], parameter: &Real) -> Real {
coefficients
.iter()
.rev()
.fold(Real::zero(), |accumulator, coefficient| {
(accumulator * parameter) + coefficient
})
}
fn parameter_representation(
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> AlgebraicRootRepresentation {
let interval = parameter.interval();
let exact_root = linear_parameter_witness(parameter, policy);
let mut representation = AlgebraicRootRepresentation {
constraint_index: 0,
symbol: SymbolId(0),
interval_index: 0,
polynomial_coefficients: parameter.polynomial().coefficients().to_vec(),
interval: IsolatedRootInterval {
lower: interval.start().clone(),
upper: interval.end().clone(),
exact_root: exact_root.clone(),
distinct_root_count: parameter.root_count(),
},
kind: if exact_root.is_some() {
AlgebraicRootKind::ExactRationalWitness
} else {
AlgebraicRootKind::IsolatingInterval
},
validation: AlgebraicRootValidationReport {
status: AlgebraicRootValidationStatus::Valid,
message: None,
},
};
representation.validation =
validate_algebraic_root_representation(&representation, policy.predicate_policy);
representation
}
fn linear_parameter_witness(
parameter: &BezierAlgebraicParameter2,
policy: &CurvePolicy,
) -> Option<Real> {
let coefficients = parameter.polynomial().coefficients();
if coefficients.len() != 2 {
return None;
}
let root = (Real::zero() - coefficients[0].clone()) / coefficients[1].clone();
let root = root.ok()?;
let interval = parameter.interval();
let starts_after_root = compare_reals(interval.start(), &root, policy)? != Ordering::Greater;
let ends_before_root = compare_reals(&root, interval.end(), policy)? != Ordering::Greater;
(starts_after_root && ends_before_root).then_some(root)
}
fn quadratic_point_coefficients(curve: &QuadraticBezier2) -> CoordinatePolynomials {
let two = Real::from(2_i8);
CoordinatePolynomials {
x: quadratic_power_coefficients(
curve.start().x(),
curve.control().x(),
curve.end().x(),
&two,
),
y: quadratic_power_coefficients(
curve.start().y(),
curve.control().y(),
curve.end().y(),
&two,
),
}
}
fn quadratic_tangent_coefficients(curve: &QuadraticBezier2) -> CoordinatePolynomials {
let two = Real::from(2_i8);
CoordinatePolynomials {
x: quadratic_derivative_coefficients(
curve.start().x(),
curve.control().x(),
curve.end().x(),
&two,
),
y: quadratic_derivative_coefficients(
curve.start().y(),
curve.control().y(),
curve.end().y(),
&two,
),
}
}
fn cubic_point_coefficients(curve: &CubicBezier2) -> CoordinatePolynomials {
let three = Real::from(3_i8);
CoordinatePolynomials {
x: cubic_power_coefficients(
curve.start().x(),
curve.control1().x(),
curve.control2().x(),
curve.end().x(),
&three,
),
y: cubic_power_coefficients(
curve.start().y(),
curve.control1().y(),
curve.control2().y(),
curve.end().y(),
&three,
),
}
}
fn cubic_tangent_coefficients(curve: &CubicBezier2) -> CoordinatePolynomials {
derivative_polynomials(cubic_point_coefficients(curve))
}
fn rational_point_coefficients(curve: &RationalQuadraticBezier2) -> RationalCoordinatePolynomials {
let weighted_x = [
curve.start().x() * curve.start_weight(),
curve.control().x() * curve.control_weight(),
curve.end().x() * curve.end_weight(),
];
let weighted_y = [
curve.start().y() * curve.start_weight(),
curve.control().y() * curve.control_weight(),
curve.end().y() * curve.end_weight(),
];
let weights = [
curve.start_weight().clone(),
curve.control_weight().clone(),
curve.end_weight().clone(),
];
RationalCoordinatePolynomials {
x_numerator: rational_quadratic_power_coefficients(&weighted_x),
y_numerator: rational_quadratic_power_coefficients(&weighted_y),
denominator: rational_quadratic_power_coefficients(&weights),
}
}
fn rational_tangent_coefficients(curve: &RationalQuadraticBezier2) -> RationalTangentPolynomials {
let point = rational_point_coefficients(curve);
let denominator_derivative = derivative_coefficients(&point.denominator);
let denominator_squared = multiply_polynomials(&point.denominator, &point.denominator);
let dx_numerator = rational_derivative_numerator(
&point.x_numerator,
&point.denominator,
&denominator_derivative,
);
let dy_numerator = rational_derivative_numerator(
&point.y_numerator,
&point.denominator,
&denominator_derivative,
);
RationalTangentPolynomials {
dx_numerator,
dy_numerator,
denominator: denominator_squared,
}
}
fn rational_second_derivative_coefficients(
curve: &RationalQuadraticBezier2,
) -> RationalTangentPolynomials {
let point = rational_point_coefficients(curve);
let denominator_derivative = derivative_coefficients(&point.denominator);
let denominator_squared = multiply_polynomials(&point.denominator, &point.denominator);
let denominator_cubed = multiply_polynomials(&denominator_squared, &point.denominator);
let dx_first_numerator = rational_derivative_numerator(
&point.x_numerator,
&point.denominator,
&denominator_derivative,
);
let dy_first_numerator = rational_derivative_numerator(
&point.y_numerator,
&point.denominator,
&denominator_derivative,
);
let dx_numerator = rational_second_derivative_numerator(
&dx_first_numerator,
&point.denominator,
&denominator_derivative,
);
let dy_numerator = rational_second_derivative_numerator(
&dy_first_numerator,
&point.denominator,
&denominator_derivative,
);
RationalTangentPolynomials {
dx_numerator,
dy_numerator,
denominator: denominator_cubed,
}
}
fn quadratic_power_coefficients(p0: &Real, p1: &Real, p2: &Real, two: &Real) -> Vec<Real> {
vec![p0.clone(), two * &(p1 - p0), p0 - &(two * p1) + p2]
}
fn quadratic_derivative_coefficients(p0: &Real, p1: &Real, p2: &Real, two: &Real) -> Vec<Real> {
vec![two * &(p1 - p0), two * &(p0 - &(two * p1) + p2)]
}
fn rational_quadratic_power_coefficients(bernstein: &[Real; 3]) -> Vec<Real> {
let two = Real::from(2_i8);
quadratic_power_coefficients(&bernstein[0], &bernstein[1], &bernstein[2], &two)
}
fn cubic_power_coefficients(p0: &Real, p1: &Real, p2: &Real, p3: &Real, three: &Real) -> Vec<Real> {
vec![
p0.clone(),
three * &(p1 - p0),
three * &(p0 - &(Real::from(2_i8) * p1) + p2),
Real::zero() - p0 + &(three * p1) - &(three * p2) + p3,
]
}
fn derivative_polynomials(polynomials: CoordinatePolynomials) -> CoordinatePolynomials {
CoordinatePolynomials {
x: derivative_coefficients(&polynomials.x),
y: derivative_coefficients(&polynomials.y),
}
}
fn second_derivative_polynomials(polynomials: CoordinatePolynomials) -> CoordinatePolynomials {
derivative_polynomials(polynomials)
}
fn derivative_coefficients(coefficients: &[Real]) -> Vec<Real> {
coefficients
.iter()
.enumerate()
.skip(1)
.map(|(degree, coefficient)| coefficient * &Real::from(degree as i64))
.collect()
}
fn rational_derivative_numerator(
numerator: &[Real],
denominator: &[Real],
denominator_derivative: &[Real],
) -> Vec<Real> {
subtract_polynomials(
&multiply_polynomials(&derivative_coefficients(numerator), denominator),
&multiply_polynomials(numerator, denominator_derivative),
)
}
fn rational_second_derivative_numerator(
first_derivative_numerator: &[Real],
denominator: &[Real],
denominator_derivative: &[Real],
) -> Vec<Real> {
subtract_polynomials(
&multiply_polynomials(
&derivative_coefficients(first_derivative_numerator),
denominator,
),
&scale_polynomial(
&multiply_polynomials(first_derivative_numerator, denominator_derivative),
Real::from(2_i8),
),
)
}
fn multiply_polynomials(left: &[Real], right: &[Real]) -> Vec<Real> {
let mut result = vec![Real::zero(); left.len() + right.len() - 1];
for (left_degree, left_coefficient) in left.iter().enumerate() {
for (right_degree, right_coefficient) in right.iter().enumerate() {
result[left_degree + right_degree] =
result[left_degree + right_degree].clone() + left_coefficient * right_coefficient;
}
}
result
}
fn subtract_polynomials(left: &[Real], right: &[Real]) -> Vec<Real> {
let mut result = vec![Real::zero(); left.len().max(right.len())];
for (index, coefficient) in left.iter().enumerate() {
result[index] = result[index].clone() + coefficient;
}
for (index, coefficient) in right.iter().enumerate() {
result[index] = result[index].clone() - coefficient;
}
result
}
fn scale_polynomial(coefficients: &[Real], scale: Real) -> Vec<Real> {
coefficients
.iter()
.map(|coefficient| coefficient * &scale)
.collect()
}
#[derive(Clone, Debug)]
struct CoordinatePolynomials {
x: Vec<Real>,
y: Vec<Real>,
}
#[derive(Clone, Debug)]
struct RationalCoordinatePolynomials {
x_numerator: Vec<Real>,
y_numerator: Vec<Real>,
denominator: Vec<Real>,
}
#[derive(Clone, Debug)]
struct RationalTangentPolynomials {
dx_numerator: Vec<Real>,
dy_numerator: Vec<Real>,
denominator: Vec<Real>,
}