1use std::cmp::Ordering;
26
27use hyperreal::{Real, RealSign};
28
29use crate::classify::{compare_reals, in_closed_unit_interval, is_zero, real_sign};
30use crate::{
31 BezierMonotoneSpan, Classification, CurveError, CurvePolicy, CurveResult, UncertaintyReason,
32};
33
34#[derive(Clone, Debug, PartialEq)]
42pub struct BezierParameterPolynomial {
43 coefficients: Vec<Real>,
44}
45
46#[derive(Clone, Debug, PartialEq)]
53pub struct BezierParameterInterval {
54 start: Real,
55 end: Real,
56}
57
58#[derive(Clone, Debug, PartialEq)]
66pub struct BezierAlgebraicParameter2 {
67 polynomial: BezierParameterPolynomial,
68 interval: BezierParameterInterval,
69 root_count: usize,
70}
71
72#[allow(clippy::large_enum_variant)]
74#[derive(Clone, Debug, PartialEq)]
75pub enum BezierParameter2 {
76 Exact(Real),
78 Algebraic(BezierAlgebraicParameter2),
80}
81
82impl BezierParameterPolynomial {
83 pub fn try_new_power_basis(
85 coefficients: Vec<Real>,
86 policy: &CurvePolicy,
87 ) -> CurveResult<Classification<Self>> {
88 match normalize_coefficients(coefficients, policy)? {
89 Classification::Decided(Some(coefficients)) => {
90 Ok(Classification::Decided(Self { coefficients }))
91 }
92 Classification::Decided(None) => Err(CurveError::InvalidBezierPolynomial),
93 Classification::Uncertain(reason) => Ok(Classification::Uncertain(reason)),
94 }
95 }
96
97 pub fn coefficients(&self) -> &[Real] {
99 &self.coefficients
100 }
101
102 pub fn degree(&self) -> usize {
104 self.coefficients.len() - 1
105 }
106
107 pub fn evaluate(&self, parameter: &Real) -> Real {
109 evaluate_coefficients(&self.coefficients, parameter)
110 }
111
112 pub fn root_count_in_interval(
119 &self,
120 interval: &BezierParameterInterval,
121 policy: &CurvePolicy,
122 ) -> CurveResult<Classification<usize>> {
123 let start_value = self.evaluate(interval.start());
124 let end_value = self.evaluate(interval.end());
125 match (
126 real_sign(&start_value, policy),
127 real_sign(&end_value, policy),
128 ) {
129 (Some(RealSign::Zero), _) | (_, Some(RealSign::Zero)) => {
130 return Err(CurveError::InvalidBezierAlgebraicParameter);
131 }
132 (Some(_), Some(_)) => {}
133 _ => return Ok(Classification::Uncertain(UncertaintyReason::RealSign)),
134 }
135
136 let sequence = match sturm_sequence(&self.coefficients, policy)? {
137 Classification::Decided(sequence) => sequence,
138 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
139 };
140 let start_variations = sign_variations_at(&sequence, interval.start(), policy)?;
141 let end_variations = sign_variations_at(&sequence, interval.end(), policy)?;
142 match (start_variations, end_variations) {
143 (Classification::Decided(start), Classification::Decided(end)) => {
144 Ok(Classification::Decided(start.saturating_sub(end)))
145 }
146 (Classification::Uncertain(reason), _) | (_, Classification::Uncertain(reason)) => {
147 Ok(Classification::Uncertain(reason))
148 }
149 }
150 }
151}
152
153impl BezierParameterInterval {
154 pub fn try_new(
156 start: Real,
157 end: Real,
158 policy: &CurvePolicy,
159 ) -> CurveResult<Classification<Self>> {
160 let in_start = in_closed_unit_interval(&start, policy);
161 let in_end = in_closed_unit_interval(&end, policy);
162 match (in_start, in_end) {
163 (Some(false), _) | (_, Some(false)) => return Err(CurveError::InvalidBezierParameter),
164 (Some(true), Some(true)) => {}
165 _ => return Ok(Classification::Uncertain(UncertaintyReason::Ordering)),
166 }
167
168 match compare_reals(&start, &end, policy) {
169 Some(Ordering::Greater) => Err(CurveError::InvalidBezierRange),
170 Some(_) => Ok(Classification::Decided(Self { start, end })),
171 None => Ok(Classification::Uncertain(UncertaintyReason::Ordering)),
172 }
173 }
174
175 pub fn from_monotone_span(
177 span: &BezierMonotoneSpan,
178 policy: &CurvePolicy,
179 ) -> CurveResult<Classification<Self>> {
180 Self::try_new(span.start().clone(), span.end().clone(), policy)
181 }
182
183 pub const fn start(&self) -> &Real {
185 &self.start
186 }
187
188 pub const fn end(&self) -> &Real {
190 &self.end
191 }
192}
193
194impl BezierAlgebraicParameter2 {
195 pub fn try_isolate(
197 polynomial: BezierParameterPolynomial,
198 interval: BezierParameterInterval,
199 policy: &CurvePolicy,
200 ) -> CurveResult<Classification<Self>> {
201 let count = match polynomial.root_count_in_interval(&interval, policy)? {
202 Classification::Decided(count) => count,
203 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
204 };
205 if count != 1 {
206 return Err(CurveError::InvalidBezierAlgebraicParameter);
207 }
208
209 Ok(Classification::Decided(Self {
210 polynomial,
211 interval,
212 root_count: count,
213 }))
214 }
215
216 pub const fn polynomial(&self) -> &BezierParameterPolynomial {
218 &self.polynomial
219 }
220
221 pub const fn interval(&self) -> &BezierParameterInterval {
223 &self.interval
224 }
225
226 pub const fn root_count(&self) -> usize {
228 self.root_count
229 }
230
231 pub fn represented_linear_root(
244 &self,
245 policy: &CurvePolicy,
246 ) -> CurveResult<Classification<Option<Real>>> {
247 if self.polynomial.degree() != 1 {
248 return Ok(Classification::Decided(None));
249 }
250
251 let constant = &self.polynomial.coefficients()[0];
252 let slope = &self.polynomial.coefficients()[1];
253 if is_zero(slope, policy) != Some(false) {
254 return Ok(Classification::Uncertain(UncertaintyReason::RealSign));
255 }
256 let root = ((Real::zero() - constant) / slope.clone())?;
257 match in_closed_unit_interval(&root, policy) {
258 Some(true) => {}
259 Some(false) => return Ok(Classification::Decided(None)),
260 None => return Ok(Classification::Uncertain(UncertaintyReason::Ordering)),
261 }
262 match (
263 compare_reals(self.interval.start(), &root, policy),
264 compare_reals(&root, self.interval.end(), policy),
265 ) {
266 (Some(Ordering::Greater), _) | (_, Some(Ordering::Greater)) => {
267 return Ok(Classification::Decided(None));
268 }
269 (Some(_), Some(_)) => {}
270 _ => return Ok(Classification::Uncertain(UncertaintyReason::Ordering)),
271 }
272 match real_sign(&self.polynomial.evaluate(&root), policy) {
273 Some(RealSign::Zero) => Ok(Classification::Decided(Some(root))),
274 Some(_) => Ok(Classification::Uncertain(UncertaintyReason::RealSign)),
275 None => Ok(Classification::Uncertain(UncertaintyReason::RealSign)),
276 }
277 }
278}
279
280impl BezierParameter2 {
281 pub fn exact(value: Real, policy: &CurvePolicy) -> CurveResult<Classification<Self>> {
283 match in_closed_unit_interval(&value, policy) {
284 Some(true) => Ok(Classification::Decided(Self::Exact(value))),
285 Some(false) => Err(CurveError::InvalidBezierParameter),
286 None => Ok(Classification::Uncertain(UncertaintyReason::Ordering)),
287 }
288 }
289
290 pub const fn algebraic(value: BezierAlgebraicParameter2) -> Self {
292 Self::Algebraic(value)
293 }
294
295 pub const fn as_exact(&self) -> Option<&Real> {
297 match self {
298 Self::Exact(value) => Some(value),
299 Self::Algebraic(_) => None,
300 }
301 }
302
303 pub const fn is_exact(&self) -> bool {
305 matches!(self, Self::Exact(_))
306 }
307
308 pub fn promote_represented_linear_root(
315 self,
316 policy: &CurvePolicy,
317 ) -> CurveResult<Classification<Self>> {
318 match self {
319 Self::Exact(_) => Ok(Classification::Decided(self)),
320 Self::Algebraic(parameter) => match parameter.represented_linear_root(policy)? {
321 Classification::Decided(Some(root)) => {
322 Ok(Classification::Decided(Self::Exact(root)))
323 }
324 Classification::Decided(None) => {
325 Ok(Classification::Decided(Self::Algebraic(parameter)))
326 }
327 Classification::Uncertain(reason) => Ok(Classification::Uncertain(reason)),
328 },
329 }
330 }
331
332 pub fn known_interval(
334 &self,
335 policy: &CurvePolicy,
336 ) -> CurveResult<Classification<BezierParameterInterval>> {
337 match self {
338 Self::Exact(value) => {
339 BezierParameterInterval::try_new(value.clone(), value.clone(), policy)
340 }
341 Self::Algebraic(value) => Ok(Classification::Decided(value.interval().clone())),
342 }
343 }
344
345 pub fn cmp_by_interval(
347 &self,
348 other: &Self,
349 policy: &CurvePolicy,
350 ) -> CurveResult<Classification<Ordering>> {
351 if let (Self::Exact(left), Self::Exact(right)) = (self, other) {
352 return Ok(compare_reals(left, right, policy)
353 .map(Classification::Decided)
354 .unwrap_or(Classification::Uncertain(UncertaintyReason::Ordering)));
355 }
356
357 let left = match self.known_interval(policy)? {
358 Classification::Decided(interval) => interval,
359 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
360 };
361 let right = match other.known_interval(policy)? {
362 Classification::Decided(interval) => interval,
363 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
364 };
365
366 if compare_reals(left.end(), right.start(), policy) == Some(Ordering::Less) {
367 return Ok(Classification::Decided(Ordering::Less));
368 }
369 if compare_reals(right.end(), left.start(), policy) == Some(Ordering::Less) {
370 return Ok(Classification::Decided(Ordering::Greater));
371 }
372
373 Ok(Classification::Uncertain(UncertaintyReason::Ordering))
374 }
375}
376
377fn sturm_sequence(
378 coefficients: &[Real],
379 policy: &CurvePolicy,
380) -> CurveResult<Classification<Vec<Vec<Real>>>> {
381 let p0 = coefficients.to_vec();
382 let p1 = derivative_coefficients(coefficients);
383 let p1 = match normalize_coefficients(p1, policy)? {
384 Classification::Decided(Some(coefficients)) => coefficients,
385 Classification::Decided(None) => return Ok(Classification::Decided(vec![p0])),
386 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
387 };
388
389 let mut sequence = vec![p0, p1];
390 while sequence.len() < 64 {
391 let last = sequence[sequence.len() - 1].clone();
392 let previous = sequence[sequence.len() - 2].clone();
393 let remainder = match polynomial_remainder(previous, last, policy)? {
394 Classification::Decided(Some(remainder)) => remainder,
395 Classification::Decided(None) => break,
396 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
397 };
398 sequence.push(negate_coefficients(remainder));
399 }
400
401 Ok(Classification::Decided(sequence))
402}
403
404fn sign_variations_at(
405 sequence: &[Vec<Real>],
406 parameter: &Real,
407 policy: &CurvePolicy,
408) -> CurveResult<Classification<usize>> {
409 let mut previous = None;
410 let mut variations = 0_usize;
411
412 for polynomial in sequence {
413 let value = evaluate_coefficients(polynomial, parameter);
414 let sign = match real_sign(&value, policy) {
415 Some(RealSign::Zero) => continue,
416 Some(sign) => sign,
417 None => return Ok(Classification::Uncertain(UncertaintyReason::RealSign)),
418 };
419 if let Some(previous) = previous
420 && previous != sign
421 {
422 variations += 1;
423 }
424 previous = Some(sign);
425 }
426
427 Ok(Classification::Decided(variations))
428}
429
430fn polynomial_remainder(
431 mut remainder: Vec<Real>,
432 divisor: Vec<Real>,
433 policy: &CurvePolicy,
434) -> CurveResult<Classification<Option<Vec<Real>>>> {
435 let divisor = match normalize_coefficients(divisor, policy)? {
436 Classification::Decided(Some(coefficients)) => coefficients,
437 Classification::Decided(None) => {
438 return Ok(Classification::Uncertain(UncertaintyReason::Unsupported));
439 }
440 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
441 };
442
443 loop {
444 remainder = match normalize_coefficients(remainder, policy)? {
445 Classification::Decided(Some(coefficients)) => coefficients,
446 Classification::Decided(None) => return Ok(Classification::Decided(None)),
447 Classification::Uncertain(reason) => return Ok(Classification::Uncertain(reason)),
448 };
449 if remainder.len() < divisor.len() {
450 return Ok(Classification::Decided(Some(remainder)));
451 }
452
453 let shift = remainder.len() - divisor.len();
454 let factor = (remainder[remainder.len() - 1].clone() / divisor[divisor.len() - 1].clone())?;
455 for (index, divisor_coefficient) in divisor.iter().enumerate() {
456 let product = &factor * divisor_coefficient;
457 remainder[shift + index] = &remainder[shift + index] - &product;
458 }
459 }
460}
461
462fn normalize_coefficients(
463 mut coefficients: Vec<Real>,
464 policy: &CurvePolicy,
465) -> CurveResult<Classification<Option<Vec<Real>>>> {
466 while let Some(last) = coefficients.last() {
467 match is_zero(last, policy) {
468 Some(true) => {
469 coefficients.pop();
470 }
471 Some(false) => return Ok(Classification::Decided(Some(coefficients))),
472 None => return Ok(Classification::Uncertain(UncertaintyReason::RealSign)),
473 }
474 }
475
476 Ok(Classification::Decided(None))
477}
478
479fn derivative_coefficients(coefficients: &[Real]) -> Vec<Real> {
480 let mut derivative = Vec::with_capacity(coefficients.len().saturating_sub(1));
481 for (degree, coefficient) in coefficients.iter().enumerate().skip(1) {
482 let scale = Real::from(degree as i64);
483 derivative.push(coefficient * &scale);
484 }
485 derivative
486}
487
488fn evaluate_coefficients(coefficients: &[Real], parameter: &Real) -> Real {
489 coefficients
490 .iter()
491 .rev()
492 .fold(Real::zero(), |accumulator, coefficient| {
493 (&accumulator * parameter) + coefficient
494 })
495}
496
497fn negate_coefficients(coefficients: Vec<Real>) -> Vec<Real> {
498 coefficients
499 .into_iter()
500 .map(|coefficient| Real::zero() - coefficient)
501 .collect()
502}