use super::{IntegrableFloat, IntegrationError, IntegrationOutput};
use nalgebra::ComplexField;
use num_traits::ToPrimitive;
use ordered_float::NotNan;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::binary_heap::Iter;
use std::collections::BinaryHeap;
use std::ops::Range;
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct SegmentHeap<I, O, F: PartialEq + PartialOrd> {
inner: BinaryHeap<Segment<I, O, F>>,
}
impl<I, O, F> SegmentHeap<I, O, F>
where
O: IntegrationOutput<Float = F>,
I: ComplexField<RealField = F>,
F: IntegrableFloat,
{
pub fn empty() -> Self {
Self {
inner: BinaryHeap::new(),
}
}
pub fn iter(&self) -> Iter<'_, Segment<I, O, F>> {
self.inner.iter()
}
pub fn push(&mut self, item: Segment<I, O, F>) {
self.inner.push(item);
}
pub fn pop(&mut self) -> Option<Segment<I, O, F>> {
self.inner.pop()
}
pub fn into_input_ordered(self) -> Vec<Segment<I, O, F>> {
let mut segments = self.inner.into_vec();
segments.sort_by(|a, b| {
a.range
.start
.clone()
.real()
.partial_cmp(&b.range.start.clone().real())
.unwrap()
});
segments
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Segment<I, O, F: PartialOrd + PartialEq> {
pub range: Range<I>,
pub result: O,
pub error: F,
pub data: Option<SegmentData<I, O, F>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SegmentData<I, O, F> {
pub points: Vec<I>,
pub weights: Vec<F>,
pub values: Vec<O>,
}
impl<I, O, F> SegmentData<I, O, F>
where
I: ComplexField<RealField = F> + Copy,
O: IntegrationOutput<Float = F>,
F: IntegrableFloat,
{
pub(crate) fn from_gauss_kronrod_data(
output_left: &[O],
_output_center: O,
output_right: &[O],
bare_weights: &[F],
center: I,
half_length: I,
xgk: &[F],
) -> Self {
let mut values = output_left.to_vec();
let output_right = output_right.iter().rev().cloned().collect::<Vec<_>>();
values.extend_from_slice(&output_right[1..]);
let integration_order = bare_weights.len();
let mut weights = bare_weights.to_vec();
weights.extend_from_within(..integration_order - 1);
let right_weights = &mut weights[integration_order..];
right_weights.reverse();
weights
.iter_mut()
.for_each(|w| *w = *w * half_length.modulus());
let mut points = xgk
.iter()
.map(|x| half_length.scale(*x))
.map(|abscissa| center - abscissa)
.collect::<Vec<_>>();
points.extend_from_within(..integration_order - 1);
let right_points = &mut points[integration_order..];
let segment_right = center + half_length;
let segment_left = center - half_length;
right_points.reverse();
right_points.iter_mut().for_each(|v| {
let delta = *v - segment_left;
*v = segment_right - delta;
});
assert_eq!(points.len(), values.len());
assert_eq!(points.len(), weights.len());
Self {
points,
weights,
values,
}
}
pub(crate) fn integral(&self) -> O {
let vals = self
.weights
.iter()
.zip(&self.values)
.map(|(w, v)| v.mul(&<O as IntegrationOutput>::Scalar::from_real(*w)));
let mut res = self.values[0].mul(&<O as IntegrationOutput>::Scalar::from_real(F::zero()));
for val in vals {
res = res.add(&val);
}
res
}
}
pub trait Segments<O, F>
where
O: IntegrationOutput<Float = F>,
{
fn error(&self) -> NotNan<F>;
fn result(&self) -> O;
}
impl<I, O, F> Segment<I, O, F>
where
I: ComplexField<RealField = F> + Copy + ToPrimitive,
O: IntegrationOutput<Float = F>,
F: IntegrableFloat,
{
pub fn is_wide_enough(&self, minimum_width: &F) -> Result<(), IntegrationError<I>> {
if (self.range.end - self.range.start).abs() < *minimum_width {
Err(IntegrationError::PossibleSingularity {
singularity: { (self.range.start + self.range.end) / I::from_f64(2.).unwrap() },
})
} else {
Ok(())
}
}
}
impl<I, O, F> Segments<O, F> for SegmentHeap<I, O, F>
where
I: ComplexField<RealField = F> + Copy,
O: IntegrationOutput<Float = F>,
F: IntegrableFloat,
{
fn error(&self) -> NotNan<F> {
self.iter().fold(NotNan::new(F::zero()).unwrap(), |x, y| {
x + NotNan::new(y.error).unwrap()
})
}
fn result(&self) -> O {
let mut iter = self.iter();
let first = iter.next().unwrap().result.clone();
iter.fold(first, |a, b| a.add(&b.result))
}
}
impl<I, O, F> PartialOrd for Segment<I, O, F>
where
F: PartialEq + PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<I, O, F> Ord for Segment<I, O, F>
where
F: PartialEq + PartialOrd,
{
fn cmp(&self, other: &Self) -> Ordering {
self.error.partial_cmp(&other.error).unwrap()
}
}
impl<I, O, F> PartialEq for Segment<I, O, F>
where
F: PartialEq + PartialOrd,
{
fn eq(&self, other: &Self) -> bool {
self.error == other.error
}
}
impl<I, O, F> Eq for Segment<I, O, F> where F: PartialEq + PartialOrd {}