/*
* Copyright (c) 2019 Frank Fischer <frank-fischer@shadow-soft.de>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
//! Objects that can be combined linearly.
use super::Real;
use std::borrow::Borrow;
/// An aggregatable object.
pub trait Aggregatable: Default {
/// Return a scaled version of `other`, i.e. `alpha * other`.
fn new_scaled<A>(alpha: Real, other: A) -> Self
where
A: Borrow<Self>;
/// Add a scaled version of `other` to `self`.
///
/// This sets `self = self + alpha * other`.
fn add_scaled<A>(&mut self, alpha: Real, other: A)
where
A: Borrow<Self>;
/// Return $\sum\_{i=1}\^n alpha_i m_i$.
///
/// If `aggregates` is empty return the default value.
fn combine<I, A>(aggregates: I) -> Self
where
I: IntoIterator<Item = (Real, A)>,
A: Borrow<Self>,
{
let mut it = aggregates.into_iter();
let mut x;
if let Some((alpha, y)) = it.next() {
x = Self::new_scaled(alpha, y);
} else {
return Self::default();
}
for (alpha, y) in it {
x.add_scaled(alpha, y);
}
x
}
}
/// Implement for empty tuples.
impl Aggregatable for () {
fn new_scaled<A>(_alpha: Real, _other: A) -> Self
where
A: Borrow<Self>,
{
}
fn add_scaled<A>(&mut self, _alpha: Real, _other: A)
where
A: Borrow<Self>,
{
}
}
/// Implement for scalar values.
impl Aggregatable for Real {
fn new_scaled<A>(alpha: Real, other: A) -> Self
where
A: Borrow<Self>,
{
alpha * other.borrow()
}
fn add_scaled<A>(&mut self, alpha: Real, other: A)
where
A: Borrow<Self>,
{
*self += alpha * other.borrow()
}
}
/// Implement for vectors of aggregatable objects.
impl<T> Aggregatable for Vec<T>
where
T: Aggregatable,
{
fn new_scaled<A>(alpha: Real, other: A) -> Self
where
A: std::borrow::Borrow<Self>,
{
other
.borrow()
.iter()
.map(|y| Aggregatable::new_scaled(alpha, y))
.collect()
}
fn add_scaled<A>(&mut self, alpha: Real, other: A)
where
A: std::borrow::Borrow<Self>,
{
debug_assert_eq!(self.len(), other.borrow().len(), "Vectors must have the same size");
for (ref mut x, y) in self.iter_mut().zip(other.borrow()) {
x.add_scaled(alpha, y)
}
}
}