use crate::poly::*;
use approx::{AbsDiffEq, RelativeEq};
use arbitrary::Arbitrary;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::iter;
use std::ops::{Add, Mul, MulAssign, Neg, Sub};
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct Segment<T> {
pub end: f64,
pub poly: T,
}
impl<T: Evaluate> Evaluate for Segment<T> {
fn evaluate(&self, v: f64) -> f64 {
self.poly.evaluate(v)
}
}
impl<T> Mul<f64> for Segment<T>
where
T: Mul<f64, Output = T> + Copy,
{
type Output = Self;
fn mul(self, rhs: f64) -> Self::Output {
Segment {
end: self.end,
poly: self.poly * rhs,
}
}
}
impl<T: MulAssign<f64>> MulAssign<f64> for Segment<T> {
fn mul_assign(&mut self, rhs: f64) {
self.poly *= rhs
}
}
impl<T: MulAssign<f64>> MulAssign<f64> for &mut Segment<T> {
fn mul_assign(&mut self, rhs: f64) {
self.poly *= rhs
}
}
impl<T: HasDerivative> HasDerivative for Segment<T> {
type DerivativeOf = Segment<T::DerivativeOf>;
fn derivative(&self) -> Self::DerivativeOf {
Segment {
end: self.end,
poly: self.poly.derivative(),
}
}
}
impl<T: Translate> Translate for Segment<T> {
fn translate(&mut self, v: f64) {
self.poly.translate(v);
}
}
impl<T> HasIntegral for Segment<T>
where
T: HasIntegral,
T::IntegralOf: Translate,
{
type IntegralOf = Segment<T::IntegralOf>;
fn indefinite(&self) -> Self::IntegralOf {
Segment {
end: self.end,
poly: self.poly.indefinite(),
}
}
fn integral(&self, knot: Knot) -> Self::IntegralOf {
let mut indef = self.indefinite();
indef.translate(knot.y - indef.evaluate(knot.x));
indef
}
}
impl<T> AbsDiffEq for Segment<T>
where
T: AbsDiffEq<Epsilon = f64>,
{
type Epsilon = f64;
fn default_epsilon() -> Self::Epsilon {
<Self::Epsilon as AbsDiffEq>::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, eps: Self::Epsilon) -> bool {
self.end.abs_diff_eq(&other.end, eps) && self.poly.abs_diff_eq(&other.poly, eps)
}
}
impl<T> RelativeEq for Segment<T>
where
T: AbsDiffEq<Epsilon = f64> + RelativeEq,
{
fn default_max_relative() -> Self::Epsilon {
<Self::Epsilon as RelativeEq>::default_max_relative()
}
fn relative_eq(&self, other: &Self, eps: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
self.end.relative_eq(&other.end, eps, max_relative)
&& self.poly.relative_eq(&other.poly, eps, max_relative)
}
}
impl<T> Segment<T> {
#[inline]
pub fn integral_iter_ref<'a, I>(
segments: I,
knot0: Knot,
) -> impl Iterator<Item = Segment<T::IntegralOf>> + 'a
where
T: HasIntegral + 'a,
T::IntegralOf: Translate,
I: IntoIterator<Item = &'a Segment<T>> + 'a,
{
let mut knot = knot0;
segments.into_iter().map(move |seg| {
let int = seg.integral(knot);
knot = Knot {
x: int.end,
y: int.evaluate(int.end),
};
int
})
}
#[inline]
pub fn integral_iter<I>(
segments: I,
knot0: Knot,
) -> impl Iterator<Item = Segment<T::IntegralOf>>
where
T: HasIntegral,
T::IntegralOf: Translate,
I: IntoIterator<Item = Segment<T>>,
{
let mut knot = knot0;
segments.into_iter().map(move |seg| {
let int = seg.integral(knot);
knot = Knot {
x: int.end,
y: int.evaluate(int.end),
};
int
})
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Piecewise<T> {
pub segments: Vec<Segment<T>>,
}
impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for Piecewise<T> {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let mut ends = Vec::<f64>::arbitrary(u)?;
if ends.is_empty() || !ends.iter().all(|x| x.is_normal()) {
return Err(arbitrary::Error::IncorrectFormat);
}
ends.sort_by(|x, y| x.partial_cmp(y).unwrap());
let segments = ends
.into_iter()
.map(|end| {
Ok(Segment {
end,
poly: T::arbitrary(u)?,
})
})
.collect::<arbitrary::Result<Vec<_>>>()?;
Ok(Piecewise { segments })
}
}
impl<T> AbsDiffEq for Piecewise<T>
where
T: PartialEq + AbsDiffEq<Epsilon = f64>,
{
type Epsilon = f64;
fn default_epsilon() -> Self::Epsilon {
<Self::Epsilon as AbsDiffEq>::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, eps: Self::Epsilon) -> bool {
self.segments.abs_diff_eq(&other.segments, eps)
}
}
impl<T> RelativeEq for Piecewise<T>
where
T: AbsDiffEq<Epsilon = f64> + RelativeEq,
{
fn default_max_relative() -> Self::Epsilon {
<Self::Epsilon as RelativeEq>::default_max_relative()
}
fn relative_eq(&self, other: &Self, eps: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
self.segments
.relative_eq(&other.segments, eps, max_relative)
}
}
impl<T> Default for Piecewise<T> {
fn default() -> Self {
Piecewise {
segments: Vec::new(),
}
}
}
impl<T> Mul<f64> for Piecewise<T>
where
Segment<T>: Mul<f64, Output = Segment<T>> + Copy,
{
type Output = Self;
fn mul(mut self, rhs: f64) -> Self::Output {
for seg in &mut self.segments {
*seg = *seg * rhs;
}
self
}
}
impl<T: MulAssign<f64>> MulAssign<f64> for Piecewise<T>
where
Segment<T>: MulAssign<f64>,
{
fn mul_assign(&mut self, rhs: f64) {
for mut seg in &mut self.segments {
seg *= rhs;
}
}
}
impl<T> Neg for Piecewise<T>
where
T: Neg<Output = T> + Copy,
{
type Output = Self;
fn neg(mut self) -> Self::Output {
for seg in &mut self.segments {
seg.poly = seg.poly.neg();
}
self
}
}
impl<'a, 'b, T> Add<&'b Piecewise<T>> for &'a Piecewise<T>
where
&'a T: Add<&'b T, Output = T>,
{
type Output = Piecewise<T>;
fn add(self, other: &'b Piecewise<T>) -> Self::Output {
let mut res = Vec::with_capacity(self.segments.len() + other.segments.len() - 1);
let mut i = 0;
let mut j = 0;
let i_max = self.segments.len() - 1;
let j_max = other.segments.len() - 1;
loop {
let a = &self.segments[i];
let b = &other.segments[j];
let a_last = i >= i_max;
let b_last = j >= j_max;
let end = match a.end.partial_cmp(&b.end).unwrap() {
Ordering::Less => {
if a_last {
j += 1;
b.end
} else {
i += 1;
a.end
}
}
Ordering::Greater => {
if b_last {
i += 1;
a.end
} else {
j += 1;
b.end
}
}
Ordering::Equal => {
i = i_max.min(i + 1);
j = j_max.min(j + 1);
a.end
}
};
let ab = Segment {
end,
poly: &a.poly + &b.poly,
};
res.push(ab);
if a_last && b_last {
break;
}
}
Piecewise { segments: res }
}
}
pub struct PiecewiseEvaluator<'a, T> {
poly: &'a Piecewise<T>,
last_evaluation: f64,
last_segment: usize,
}
impl<'a, T: Evaluate> PiecewiseEvaluator<'a, T> {
pub fn new(poly: &'a Piecewise<T>) -> Self {
assert!(!poly.segments.is_empty(), "no segments to pick from");
PiecewiseEvaluator {
poly,
last_evaluation: f64::NEG_INFINITY,
last_segment: 0,
}
}
pub fn evaluate(&mut self, x: f64) -> f64 {
let last_segment = if x >= self.last_evaluation {
match self.poly.segments[self.last_segment..]
.iter()
.position(|seg| seg.end > x)
{
None => self.poly.segments.len() - 1,
Some(seg_ix) => self.last_segment + seg_ix,
}
} else {
match self.poly.segments[0..self.last_segment]
.iter()
.rev()
.position(|seg| seg.end <= x)
{
None => 0,
Some(seg_ix) => self.last_segment - seg_ix,
}
};
self.last_evaluation = x;
self.last_segment = last_segment;
self.poly.segments[last_segment].evaluate(x)
}
}
impl<T: Evaluate> Evaluate for Piecewise<T> {
fn evaluate(&self, x: f64) -> f64 {
assert!(!self.segments.is_empty(), "no segments to pick from");
match self.segments.iter().position(|seg| seg.end > x) {
None => self.segments.last().unwrap().evaluate(x),
Some(seg_ix) => self.segments[seg_ix].evaluate(x),
}
}
}
impl<'a, 'b, T> Sub<&'b Piecewise<T>> for &'a Piecewise<T>
where
&'a T: Sub<&'b T, Output = T>,
{
type Output = Piecewise<T>;
fn sub(self, other: &'b Piecewise<T>) -> Self::Output {
let mut res = Vec::with_capacity(self.segments.len() + other.segments.len() - 1);
let mut i = 0;
let mut j = 0;
let i_max = self.segments.len() - 1;
let j_max = other.segments.len() - 1;
loop {
let a = &self.segments[i];
let b = &other.segments[j];
let a_last = i >= i_max;
let b_last = j >= j_max;
let end = match a.end.partial_cmp(&b.end).unwrap() {
Ordering::Less => {
if a_last {
j += 1;
b.end
} else {
i += 1;
a.end
}
}
Ordering::Greater => {
if b_last {
i += 1;
a.end
} else {
j += 1;
b.end
}
}
Ordering::Equal => {
i = i_max.min(i + 1);
j = j_max.min(j + 1);
a.end
}
};
let ab = Segment {
end,
poly: &a.poly - &b.poly,
};
res.push(ab);
if a_last && b_last {
break;
}
}
Piecewise { segments: res }
}
}
impl<T: Evaluate> Piecewise<T> {
pub fn evaluate_v<'a, 'b, I>(&'a self, xs: I) -> impl Iterator<Item = f64> + 'a
where
I: IntoIterator<Item = f64>,
<I as IntoIterator>::IntoIter: 'b,
'b: 'a,
{
assert!(!self.segments.is_empty(), "no segments to pick from!");
let mut prev_seg = 0;
xs.into_iter().map(move |x| {
prev_seg = self.segments[prev_seg..]
.iter()
.position(|seg| x < seg.end)
.map_or(self.segments.len() - 1, |i| i + prev_seg);
self.segments[prev_seg].poly.evaluate(x)
})
}
}
impl<T> HasDerivative for Piecewise<T>
where
T: HasDerivative,
{
type DerivativeOf = Piecewise<T::DerivativeOf>;
fn derivative(&self) -> Self::DerivativeOf {
Piecewise {
segments: self.segments.iter().map(|seg| seg.derivative()).collect(),
}
}
}
impl<T> HasIntegral for Piecewise<T>
where
T: HasIntegral,
T::IntegralOf: Translate,
{
type IntegralOf = Piecewise<T::IntegralOf>;
fn indefinite(&self) -> Self::IntegralOf {
if self.segments.is_empty() {
return Default::default();
}
let indef_0 = self.segments[0].indefinite();
let res_vec = {
let p_tail_int = Segment::integral_iter_ref(
&self.segments[1..],
Knot {
x: indef_0.end,
y: indef_0.evaluate(indef_0.end),
},
);
iter::once(indef_0).chain(p_tail_int).collect()
};
Piecewise { segments: res_vec }
}
fn integral(&self, knot0: Knot) -> Self::IntegralOf {
Piecewise {
segments: Segment::integral_iter_ref(&self.segments, knot0).collect(),
}
}
}
impl<T: Translate> Translate for Piecewise<T> {
fn translate(&mut self, v: f64) {
for seg in &mut self.segments {
seg.translate(v);
}
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::*;
use crate::log_poly::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn evaluate_piecewise_poly1() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly1([2.0, 3.0]),
},
Segment {
end: 2.0,
poly: Poly1([4.0, 5.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 39.0);
}
#[test]
fn mul_assign_piecewise_poly1() {
let mut poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly1([2.0, 3.0]),
},
Segment {
end: 2.0,
poly: Poly1([4.0, 5.0]),
},
],
};
let expected = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly1([4.0, 6.0]),
},
Segment {
end: 2.0,
poly: Poly1([8.0, 10.0]),
},
],
};
assert_eq!(
{
poly *= 2.0;
poly
},
expected
);
}
#[test]
fn sub_piecewise_poly1() {
let poly1 = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLogPoly4 {
k: 3.0,
coeffs: [2.0, 3.0, 4.0, 5.0],
u: 6.0,
},
},
Segment {
end: 2.0,
poly: IntOfLogPoly4 {
k: 4.0,
coeffs: [2.5, 3.5, 4.5, 5.5],
u: 6.5,
},
},
],
};
let poly2 = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLogPoly4 {
k: 3.0,
coeffs: [2.0, 3.0, 4.0, 5.0],
u: 6.0,
},
},
Segment {
end: 2.0,
poly: IntOfLogPoly4 {
k: 5.0,
coeffs: [2.6, 4.7, 5.8, 6.5],
u: 7.5,
},
},
],
};
let expected = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLogPoly4 {
k: 0.0,
coeffs: [0.0, 0.0, 0.0, 0.0],
u: 0.0,
},
},
Segment {
end: 2.0,
poly: IntOfLogPoly4 {
k: -1.0,
coeffs: [
-0.10000000000000009,
-1.2000000000000002,
-1.2999999999999998,
-1.0,
],
u: -1.0,
},
},
],
};
assert_eq!(&poly1 - &poly2, expected);
}
#[test]
fn evaluate_piecewise_log_poly1() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly1([2.0, 3.0])),
},
Segment {
end: 2.0,
poly: Log(Poly1([4.0, 5.0])),
},
],
};
assert_eq!(poly.evaluate(7.0), 13.729550745276565);
}
#[test]
fn integral_piecewise_poly1() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly1([2.0, 3.0]),
},
Segment {
end: 2.0,
poly: Poly1([4.0, 5.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly2([-1.5, 2.0, 1.5]),
},
Segment {
end: 2.0,
poly: Poly2([-4.5, 4.0, 2.5]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly1() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly1([2.0, 3.0])),
},
Segment {
end: 2.0,
poly: Log(Poly1([4.0, 5.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLog {
k: 3.0,
poly: Poly1([-1.0, 3.0]),
},
},
Segment {
end: 2.0,
poly: IntOfLog {
k: 3.0,
poly: Poly1([-1.0, 5.0]),
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly2() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly2([2.0, 3.0, 4.0]),
},
Segment {
end: 2.0,
poly: Poly2([4.0, 5.0, 6.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 333.0);
}
#[test]
fn evaluate_piecewise_log_poly2() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly2([2.0, 3.0, 4.0])),
},
Segment {
end: 2.0,
poly: Log(Poly2([4.0, 5.0, 6.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 36.44894859445539, 1e-12);
}
#[test]
fn integral_piecewise_poly2() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly2([2.0, 3.0, 4.0]),
},
Segment {
end: 2.0,
poly: Poly2([4.0, 5.0, 6.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly3([-2.833333333333333, 2.0, 1.5, 1.3333333333333333]),
},
Segment {
end: 2.0,
poly: Poly3([-6.5, 4.0, 2.5, 2.0]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly2() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly2([2.0, 3.0, 4.0])),
},
Segment {
end: 2.0,
poly: Log(Poly2([4.0, 5.0, 6.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLog {
k: -5.0,
poly: Poly2([7.0, -5.0, 4.0]),
},
},
Segment {
end: 2.0,
poly: IntOfLog {
k: -9.0,
poly: Poly2([11.0, -7.0, 6.0]),
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly3() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly3([2.0, 3.0, 4.0, 5.0]),
},
Segment {
end: 2.0,
poly: Poly3([4.0, 5.0, 6.0, 7.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 2734.0);
}
#[test]
fn evaluate_piecewise_log_poly3() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly3([2.0, 3.0, 4.0, 5.0])),
},
Segment {
end: 2.0,
poly: Log(Poly3([4.0, 5.0, 6.0, 7.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 88.02717325878835, 1e-12);
}
#[test]
fn integral_piecewise_poly3() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly3([2.0, 3.0, 4.0, 5.0]),
},
Segment {
end: 2.0,
poly: Poly3([4.0, 5.0, 6.0, 7.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly4([-4.083333333333333, 2.0, 1.5, 1.3333333333333333, 1.25]),
},
Segment {
end: 2.0,
poly: Poly4([-8.25, 4.0, 2.5, 2.0, 1.75]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly3() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly3([2.0, 3.0, 4.0, 5.0])),
},
Segment {
end: 2.0,
poly: Log(Poly3([4.0, 5.0, 6.0, 7.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLog {
k: 25.0,
poly: Poly3([-23.0, 25.0, -11.0, 5.0]),
},
},
Segment {
end: 2.0,
poly: IntOfLog {
k: 33.0,
poly: Poly3([-31.0, 35.0, -15.0, 7.0]),
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly4() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly4([2.0, 3.0, 4.0, 5.0, 6.0]),
},
Segment {
end: 2.0,
poly: Poly4([4.0, 5.0, 6.0, 7.0, 8.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 21942.0);
}
#[test]
fn evaluate_piecewise_log_poly4() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly4([2.0, 3.0, 4.0, 5.0, 6.0])),
},
Segment {
end: 2.0,
poly: Log(Poly4([4.0, 5.0, 6.0, 7.0, 8.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 202.73184850973757, 1e-12);
}
#[test]
fn integral_piecewise_poly4() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly4([2.0, 3.0, 4.0, 5.0, 6.0]),
},
Segment {
end: 2.0,
poly: Poly4([4.0, 5.0, 6.0, 7.0, 8.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly5([-5.283333333333333, 2.0, 1.5, 1.3333333333333333, 1.25, 1.2]),
},
Segment {
end: 2.0,
poly: Poly5([-9.85, 4.0, 2.5, 2.0, 1.75, 1.6]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly4() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly4([2.0, 3.0, 4.0, 5.0, 6.0])),
},
Segment {
end: 2.0,
poly: Log(Poly4([4.0, 5.0, 6.0, 7.0, 8.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLogPoly4 {
k: 2.0,
coeffs: [-2.0, 0.5, -1.1666666666666665, 0.9583333333333334],
u: -121.0,
},
},
Segment {
end: 2.0,
poly: IntOfLogPoly4 {
k: 2.0,
coeffs: [-4.0, 0.5, -1.8333333333333333, 1.2916666666666667],
u: -161.0,
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly5() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly5([2.0, 3.0, 4.0, 5.0, 6.0, 7.0]),
},
Segment {
end: 2.0,
poly: Poly5([4.0, 5.0, 6.0, 7.0, 8.0, 9.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 173205.0);
}
#[test]
fn evaluate_piecewise_log_poly5() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly5([2.0, 3.0, 4.0, 5.0, 6.0, 7.0])),
},
Segment {
end: 2.0,
poly: Log(Poly5([4.0, 5.0, 6.0, 7.0, 8.0, 9.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 453.837464189018, 1e-12);
}
#[test]
fn integral_piecewise_poly5() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly5([2.0, 3.0, 4.0, 5.0, 6.0, 7.0]),
},
Segment {
end: 2.0,
poly: Poly5([4.0, 5.0, 6.0, 7.0, 8.0, 9.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly6([
-6.449999999999999,
2.0,
1.5,
1.3333333333333333,
1.25,
1.2,
1.1666666666666667,
]),
},
Segment {
end: 2.0,
poly: Poly6([-11.349999999999998, 4.0, 2.5, 2.0, 1.75, 1.6, 1.5]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly5() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly5([2.0, 3.0, 4.0, 5.0, 6.0, 7.0])),
},
Segment {
end: 2.0,
poly: Log(Poly5([4.0, 5.0, 6.0, 7.0, 8.0, 9.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLog {
k: 721.0,
poly: Poly5([-719.0, 721.0, -359.0, 121.0, -29.0, 7.0]),
},
},
Segment {
end: 2.0,
poly: IntOfLog {
k: 921.0,
poly: Poly5([-919.0, 923.0, -459.0, 155.0, -37.0, 9.0]),
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly6() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly6([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]),
},
Segment {
end: 2.0,
poly: Poly6([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 1349695.0);
}
#[test]
fn evaluate_piecewise_log_poly6() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly6([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])),
},
Segment {
end: 2.0,
poly: Log(Poly6([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 996.7585375613455, 1e-12);
}
#[test]
fn integral_piecewise_poly6() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly6([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]),
},
Segment {
end: 2.0,
poly: Poly6([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly7([
-7.592857142857142,
2.0,
1.5,
1.3333333333333333,
1.25,
1.2,
1.1666666666666667,
1.1428571428571428,
]),
},
Segment {
end: 2.0,
poly: Poly7([
-12.778571428571428,
4.0,
2.5,
2.0,
1.75,
1.6,
1.5,
1.4285714285714286,
]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly6() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly6([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])),
},
Segment {
end: 2.0,
poly: Log(Poly6([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLog {
k: -5039.0,
poly: Poly6([5041.0, -5039.0, 2521.0, -839.0, 211.0, -41.0, 8.0]),
},
},
Segment {
end: 2.0,
poly: IntOfLog {
k: -6279.0,
poly: Poly6([6281.0, -6277.0, 3141.0, -1045.0, 263.0, -51.0, 10.0]),
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly7() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly7([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]),
},
Segment {
end: 2.0,
poly: Poly7([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 1.0408668e7);
}
#[test]
fn evaluate_piecewise_log_poly7() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly7([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])),
},
Segment {
end: 2.0,
poly: Log(Poly7([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 2158.8817270536842, 1e-12);
}
#[test]
fn integral_piecewise_poly7() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly7([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]),
},
Segment {
end: 2.0,
poly: Poly7([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0]),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly8([
-8.717857142857142,
2.0,
1.5,
1.3333333333333333,
1.25,
1.2,
1.1666666666666667,
1.1428571428571428,
1.125,
]),
},
Segment {
end: 2.0,
poly: Poly8([
-14.153571428571428,
4.0,
2.5,
2.0,
1.75,
1.6,
1.5,
1.4285714285714286,
1.375,
]),
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn integral_piecewise_log_poly7() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly7([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])),
},
Segment {
end: 2.0,
poly: Log(Poly7([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0])),
},
],
};
let result = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: IntOfLog {
k: 40321.0,
poly: Poly7([
-40319.0, 40321.0, -20159.0, 6721.0, -1679.0, 337.0, -55.0, 9.0,
]),
},
},
Segment {
end: 2.0,
poly: IntOfLog {
k: 49161.0,
poly: Poly7([
-49159.0, 49163.0, -24579.0, 8195.0, -2047.0, 411.0, -67.0, 11.0,
]),
},
},
],
};
let knot = Knot { x: 1.0, y: 2.0 };
assert_eq!(poly.integral(knot), result);
}
#[test]
fn evaluate_piecewise_poly8() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Poly8([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
},
Segment {
end: 2.0,
poly: Poly8([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]),
},
],
};
assert_eq!(poly.evaluate(7.0), 7.958628e7);
}
#[test]
fn evaluate_piecewise_log_poly8() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly8([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])),
},
Segment {
end: 2.0,
poly: Log(Poly8([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0])),
},
],
};
assert_approx_eq!(poly.evaluate(7.0), 4625.849700383507, 1e-10);
}
#[test]
fn evaluate_piecewise_log_poly8_mid_seg() {
let poly = Piecewise {
segments: vec![
Segment {
end: 1.0,
poly: Log(Poly8([2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])),
},
Segment {
end: 5.0,
poly: Log(Poly8([4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0])),
},
Segment {
end: 10.0,
poly: Log(Poly8([
14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 110.0, 111.0, 112.0,
])),
},
],
};
assert_approx_eq!(
poly.evaluate(poly.segments[1].end),
10535.154755198095,
1e-10
);
assert_approx_eq!(
poly.evaluate(poly.segments[1].end + 0.1),
11499.374268098769,
1e-10
);
assert_approx_eq!(
poly.evaluate(poly.segments[1].end - 0.1),
1128.5314497684099,
1e-10
);
}
}