///|
pub(all) suberror SpectralError {
Invalid(String)
Numerical(String)
Budget(String)
}
///|
/// Owned arrays; construction rejects nonfinite samples and unordered axes.
pub struct Spectrum {
axis : Array[Double]
values : Array[Double]
}
///|
pub(all) struct BaselineResult {
baseline : Array[Double]
corrected : Array[Double]
weights : Array[Double]
iterations : Int32
converged : Bool
relative_change : Double
}
///|
pub(all) struct IterationOptions {
lambda : Double
max_iterations : Int32
tolerance : Double
}
///|
pub fn iteration_options(
lambda~ : Double = 10000.0,
max_iterations~ : Int32 = 50,
tolerance~ : Double = 0.001,
) -> IterationOptions {
{ lambda, max_iterations, tolerance, }
}
///|
fn finite(x : Double, label : String) -> Unit raise SpectralError {
if x.is_nan() || x.is_inf() {
raise Invalid(label + " must be finite")
}
}
///|
fn validate_values(
y : Array[Double],
minimum : Int32,
) -> Unit raise SpectralError {
if y.length() < minimum {
raise Invalid("insufficient samples")
}
if y.length() > 65536 {
raise Budget("at most 65536 samples")
}
for v in y {
finite(v, "sample")
if v.abs() > 1.0e100 {
raise Invalid("sample magnitude exceeds 1e100")
}
}
}
///|
pub fn spectrum(
axis : Array[Double],
values : Array[Double],
) -> Spectrum raise SpectralError {
validate_values(values, 3)
if axis.length() != values.length() {
raise Invalid("axis and values lengths differ")
}
for i in 0.. 1.0e100 {
raise Invalid("axis magnitude exceeds 1e100")
}
if i > 0 && axis[i] <= axis[i - 1] {
raise Invalid("axis must be strictly increasing")
}
}
{ axis: axis.copy(), values: values.copy(), }
}
///|
pub fn Spectrum::axis(self : Spectrum) -> Array[Double] {
self.axis.copy()
}
///|
pub fn Spectrum::values(self : Spectrum) -> Array[Double] {
self.values.copy()
}
///|
pub fn Spectrum::length(self : Spectrum) -> Int32 {
self.values.length()
}
///|
pub fn Spectrum::with_values(
self : Spectrum,
values : Array[Double],
) -> Spectrum raise SpectralError {
spectrum(self.axis, values)
}
///|
/// Validate bounded options before allocating iterative workspaces.
pub fn validate_iteration_options(
o : IterationOptions,
) -> Unit raise SpectralError {
finite(o.lambda, "lambda")
finite(o.tolerance, "tolerance")
if o.lambda <= 0.0 || o.lambda > 1.0e12 {
raise Invalid("lambda must lie in (0,1e12]")
}
if o.max_iterations < 1 || o.max_iterations > 200 {
raise Budget("iteration count must lie in [1,200]")
}
if o.tolerance <= 0.0 || o.tolerance > 1.0 {
raise Invalid("tolerance must lie in (0,1]")
}
}
///|
pub impl Show for SpectralError with fn output(self, logger) {
logger.write_string(error_message(self))
}
///|
pub extend SpectralError with Show::{to_string, output}