///|
fn sample_sorted(
log : Log,
column : Int,
depth : Double,
max_gap : Double,
) -> Double? {
let n = log.rows.length()
if n == 0 {
return None
}
let mut lo = 0
let mut hi = n
while lo < hi {
let mid = lo + (hi - lo) / 2
if log.rows[mid][0].unwrap() < depth {
lo = mid + 1
} else {
hi = mid
}
}
if lo < n && log.rows[lo][0].unwrap() == depth {
return log.rows[lo][column]
}
if lo == 0 || lo == n {
return None
}
let a = log.rows[lo - 1]
let b = log.rows[lo]
let gap = b[0].unwrap() - a[0].unwrap()
if gap > max_gap {
return None
}
match (a[column], b[column]) {
(Some(x), Some(y)) => {
let weight = (depth - a[0].unwrap()) / gap
let value = (1.0 - weight) * x + weight * y
if finite(value) {
Some(value)
} else {
None
}
}
_ => None
}
}
///|
/// Interpolate only between adjacent valid endpoints within max_gap, never extrapolate.
pub fn sample(
log : Log,
name : String,
depth : Double,
max_gap : Double,
) -> Double? raise WellError {
if !finite(depth) || !finite(max_gap) || max_gap <= 0.0 {
fail("sample requires finite depth and positive max_gap")
}
let sorted = ascending(log)
let column = curve_index(sorted, name)
sample_sorted(sorted, column, depth, max_gap)
}
///|
/// Output grid must be finite, unique and ascending; preserves explicit null spans.
pub fn resample(
log : Log,
grid : Array[Double],
max_gap : Double,
) -> Log raise WellError {
if !finite(max_gap) || max_gap <= 0.0 {
fail("max_gap must be finite and positive")
}
if grid.length() > 1000000 {
fail("resample row budget exceeded")
}
let sorted = ascending(log)
if grid.length().to_double() * sorted.curves.length().to_double() > 10000000.0 {
fail("resample cell budget is 10000000")
}
let rows : Array[Array[Double?]] = []
for i in 0.. 0 && depth <= grid[i - 1]) {
fail("invalid output grid")
}
let row : Array[Double?] = [Some(depth)]
for column in 1..