///|
pub fn safe_grid(cells : Int, length : Double) -> Grid1D? {
if cells <= 0 || length <= 0.0 {
None
} else {
Some(Grid1D::new(cells, length))
}
}
///|
pub fn clamp_index(index : Int, length : Int) -> Int {
if length <= 0 {
0
} else if index < 0 {
0
} else if index >= length {
length - 1
} else {
index
}
}
///|
pub fn periodic_index(index : Int, length : Int) -> Int {
if length <= 0 {
0
} else {
let value = index % length
if value < 0 {
value + length
} else {
value
}
}
}
///|
fn periodic_value(values : ArrayView[Double], index : Int) -> Double {
values[periodic_index(index, values.length())]
}
///|
pub fn periodic_gradient(
grid : Grid1D,
values : ArrayView[Double],
) -> Array[Double] {
let result = zeros(values.length())
if values.length() == 0 {
result
} else {
for i in 0.. Array[Double] {
let result = zeros(values.length())
if values.length() == 0 {
result
} else {
for i in 0.. Double {
values.fold(init=0.0, fn(acc, value) { acc + value * grid.dx })
}
///|
pub fn weighted_mean(
values : ArrayView[Double],
weights : ArrayView[Double],
) -> Double {
let totals = values.fold(init=(0.0, 0.0), fn(acc, value) {
let (sum, weight_sum) = acc
let index = if weight_sum.to_int() < weights.length() {
weight_sum.to_int()
} else {
weights.length() - 1
}
(sum + value * weights[index], weight_sum + 1.0)
})
let (sum, _) = totals
let weight_total = weights.fold(init=0.0, fn(acc, value) { acc + value })
if weight_total == 0.0 {
0.0
} else {
sum / weight_total
}
}
///|
pub fn rms(values : ArrayView[Double]) -> Double {
if values.length() == 0 {
0.0
} else {
(values.fold(init=0.0, fn(acc, value) { acc + value * value }) /
values.length().to_double()).sqrt()
}
}
///|
pub fn min_value(values : ArrayView[Double]) -> Double {
if values.length() == 0 {
0.0
} else {
let mut result = values[0]
for value in values {
if value < result {
result = value
}
}
result
}
}
///|
pub fn max_value(values : ArrayView[Double]) -> Double {
if values.length() == 0 {
0.0
} else {
let mut result = values[0]
for value in values {
if value > result {
result = value
}
}
result
}
}
///|
pub fn normalize_sum(values : ArrayView[Double]) -> Array[Double] {
let total = values.fold(init=0.0, fn(acc, value) { acc + value })
if total == 0.0 {
zeros(values.length())
} else {
values.map(fn(value) { value / total })
}
}
///|
pub fn l2_distance(
left : ArrayView[Double],
right : ArrayView[Double],
) -> Double {
let length = if left.length() < right.length() {
left.length()
} else {
right.length()
}
(for i = 0, total = 0.0; i < length; {
continue i + 1, total + (left[i] - right[i]) * (left[i] - right[i])
} nobreak {
total
}).sqrt()
}
///|
pub fn relative_l2_error(
reference : ArrayView[Double],
actual : ArrayView[Double],
) -> Double {
let denominator = l2_norm(reference)
if denominator == 0.0 {
l2_distance(reference, actual)
} else {
l2_distance(reference, actual) / denominator
}
}
///|
pub fn interpolate_periodic(
grid : Grid1D,
values : ArrayView[Double],
x : Double,
) -> Double {
sample_linear(grid, values, x)
}