///|
pub fn decode(f : Factor, coded : Double) -> Double raise DesignError {
ignore(factor(f.name, f.low, f.high))
require_number(coded)
let value = f.low / 2.0 + f.high / 2.0 + coded * (f.high / 2.0 - f.low / 2.0)
require_number(value)
value
}
///|
pub fn encode(f : Factor, actual : Double) -> Double raise DesignError {
ignore(factor(f.name, f.low, f.high))
require_number(actual)
let value = (actual - (f.low / 2.0 + f.high / 2.0)) /
(f.high / 2.0 - f.low / 2.0)
require_number(value)
value
}
///|
pub fn actual_points(p : Plan) -> Array[Array[Double]] raise DesignError {
validate_plan(p)
[
for r in p.runs => [ for j, x in r.point => decode(p.factors[j], x) ]
]
}
///|
pub fn physical_plan(
factors : Array[Factor],
points : Array[Array[Double]],
) -> Plan raise DesignError {
validate_factors(factors)
if points.length() == 0 || points.length() > 4096 {
raise Budget("run count must be 1..4096")
}
let rows : Array[Array[Double]] = []
for point in points {
if point.length() != factors.length() {
raise Invalid("point dimension mismatch")
}
rows.push([ for j, x in point => encode(factors[j], x) ])
}
coded_plan(factors, rows)
}
///|
pub fn within_bounds(p : Plan) -> Array[String] raise DesignError {
validate_plan(p)
let outside = []
for r in p.runs {
if r.point.any(fn(x) { x < -1.0 || x > 1.0 }) {
outside.push(r.id)
}
}
outside
}