///|
pub(all) enum ConflictPolicy {
Reject
PreferFirst
PreferLast
}
///|
pub struct CellSource {
run : Int
interpolated : Bool
}
///|
pub struct Conflict {
row : Int
curve : String
depth : Double
kept_run : Int
incoming_run : Int
kept_value : Double
incoming_value : Double
}
///|
pub struct Assembly {
log : Log
sources : Array[Array[CellSource?]]
conflicts : Array[Conflict]
run_names : Array[String]
}
///|
fn exact_depth(log : Log, depth : Double) -> Bool {
let mut lo = 0
let mut hi = log.rows.length()
while lo < hi {
let mid = lo + (hi - lo) / 2
if log.rows[mid][0].unwrap() < depth {
lo = mid + 1
} else {
hi = mid
}
}
lo < log.rows.length() && log.rows[lo][0].unwrap() == depth
}
///|
/// Assemble named runs on an explicit common grid. The first run sets target units.
/// Case-insensitive curve identity, no alias guessing, source retained per non-null cell.
pub fn assemble(
runs : Array[Log],
names : Array[String],
grid : Array[Double],
max_gap : Double,
policy : ConflictPolicy,
tolerance? : Double = 0.0,
) -> Assembly raise WellError {
if runs.length() == 0 || runs.length() > 64 || names.length() != runs.length() {
fail("assembly needs 1..64 uniquely named runs")
}
if !finite(tolerance) || tolerance < 0.0 {
fail("conflict tolerance must be finite and nonnegative")
}
for i in 0.. 512 ||
grid.length().to_double() *
curves.length().to_double() *
runs.length().to_double() >
20000000.0 {
fail("assembly cell-work budget exceeded")
}
// Validate the grid and max_gap even for runs with no non-depth curves.
ignore(resample(select(runs[0], []), grid, max_gap))
let rows : Array[Array[Double?]] = []
let sources : Array[Array[CellSource?]] = []
for depth in grid {
let row : Array[Double?] = [Some(depth)]
let provenance : Array[CellSource?] = [None]
for _ in 1.. row.copy())
for c in 1.. {
rows[i][output] = incoming
sources[i][output] = Some(provenance)
}
Some(old) =>
if (old - value).abs() > tolerance {
let kept = sources[i][output].unwrap().run
conflicts.push({
row: i,
curve: curves[output].name,
depth: grid[i],
kept_run: kept,
incoming_run: run,
kept_value: old,
incoming_value: value,
})
match policy {
Reject =>
fail(
"conflicting run samples at depth " +
grid[i].to_string() +
" curve " +
curves[output].name,
)
PreferFirst => ()
PreferLast => {
rows[i][output] = incoming
sources[i][output] = Some(provenance)
}
}
}
}
}
}
}
}
let log = rebuild(runs[0], curves, rows)
Assembly::{ log, sources, conflicts, run_names: names.copy(), }
}