///|
pub(all) struct ValidationIssue {
code : String
message : String
value : Double
} derive(Debug, ToJson)
///|
pub(all) struct ValidationReport {
valid : Bool
issues : Array[ValidationIssue]
} derive(Debug, ToJson)
///|
fn valid_report() -> ValidationReport {
{ valid: true, issues: [] }
}
///|
fn invalid_report(
code : String,
message : String,
value : Double,
) -> ValidationReport {
{ valid: false, issues: [{ code, message, value }] }
}
///|
pub fn validate_grid(cells : Int, length : Double) -> ValidationReport {
let issues : Array[ValidationIssue] = []
if cells < 2 {
issues.push({
code: "cells",
message: "grid needs at least two cells",
value: cells.to_double(),
})
}
if length <= 0.0 {
issues.push({
code: "length",
message: "domain length must be positive",
value: length,
})
}
{ valid: issues.length() == 0, issues }
}
///|
pub fn validate_positive(value : Double, name : String) -> ValidationReport {
if value > 0.0 {
valid_report()
} else {
invalid_report(name, "value must be positive", value)
}
}
///|
pub fn validate_non_negative(value : Double, name : String) -> ValidationReport {
if value >= 0.0 {
valid_report()
} else {
invalid_report(name, "value must not be negative", value)
}
}
///|
pub fn validate_range(
value : Double,
low : Double,
high : Double,
name : String,
) -> ValidationReport {
if low <= high && value >= low && value <= high {
valid_report()
} else {
invalid_report(name, "value is outside the requested range", value)
}
}
///|
pub fn validate_count(value : Int, name : String) -> ValidationReport {
if value > 0 {
valid_report()
} else {
invalid_report(name, "count must be positive", value.to_double())
}
}
///|
pub fn validation_message(report : ValidationReport) -> String {
if report.valid {
"valid"
} else if report.issues.length() == 0 {
"invalid"
} else {
report.issues[0].message
}
}
///|
pub fn clamp(value : Double, low : Double, high : Double) -> Double {
if low > high {
clamp(value, high, low)
} else if value < low {
low
} else if value > high {
high
} else {
value
}
}
///|
pub fn clamp_int(value : Int, low : Int, high : Int) -> Int {
if low > high {
clamp_int(value, high, low)
} else if value < low {
low
} else if value > high {
high
} else {
value
}
}
///|
pub fn lerp(a : Double, b : Double, t : Double) -> Double {
a + (b - a) * t
}
///|
pub fn inverse_lerp(a : Double, b : Double, value : Double) -> Double {
if a == b {
0.0
} else {
(value - a) / (b - a)
}
}
///|
pub fn modulo_index(index : Int, count : Int) -> Int {
if count <= 0 {
0
} else {
let remainder = index % count
if remainder < 0 {
remainder + count
} else {
remainder
}
}
}
///|
pub fn safe_ratio(
numerator : Double,
denominator : Double,
fallback : Double,
) -> Double {
if denominator == 0.0 {
fallback
} else {
numerator / denominator
}
}
///|
pub fn sum_values(values : ArrayView[Double]) -> Double {
values.fold(init=0.0, fn(acc, value) { acc + value })
}
///|
pub fn weighted_sum(
values : ArrayView[Double],
weights : ArrayView[Double],
) -> Double {
let count = if values.length() < weights.length() {
values.length()
} else {
weights.length()
}
let mut total = 0.0
for i in 0.. Double {
let mut current = 0.0
for value in values {
if value.abs() > current {
current = value.abs()
}
}
current
}
///|
pub fn all_close(
a : ArrayView[Double],
b : ArrayView[Double],
absolute : Double,
relative : Double,
) -> Bool {
if a.length() != b.length() {
false
} else {
let mut result = true
for i in 0.. absolute + relative * scale {
result = false
}
}
result
}
}
///|
pub fn fill_like(values : ArrayView[Double], value : Double) -> Array[Double] {
Array::make(values.length(), value)
}
///|
pub fn copy_values(values : ArrayView[Double]) -> Array[Double] {
values.to_owned()
}
///|
pub fn approximate_log(value : Double) -> Double {
if value <= 0.0 {
0.0
} else {
let mut reduced = value
let mut exponent = 0
while reduced > 2.0 {
reduced = reduced * 0.5
exponent = exponent + 1
}
while reduced < 1.0 {
reduced = reduced * 2.0
exponent = exponent - 1
}
let x = (reduced - 1.0) / (reduced + 1.0)
let x2 = x * x
let mut power = x
let mut total = 0.0
let mut denominator = 1.0
for _ in 0..<12 {
total = total + power / denominator
power = power * x2
denominator = denominator + 2.0
}
2.0 * total + exponent.to_double() * 0.6931471805599453
}
}