///|
pub(all) struct FeasibilityRequest {
model : ConstraintModel
box : Array[Interval]
options : Options
}
///|
pub fn decode_feasibility_request(
source : String,
) -> FeasibilityRequest raise RootError {
decode_feasibility_object(j_document(source))
}
///|
fn decode_feasibility_object(
o : Map[String, Json],
) -> FeasibilityRequest raise RootError {
j_only(o, ["version", "mode", "variables", "constraints", "box", "options"])
guard request_mode(o) == "feasibility" else {
raise Invalid("expected feasibility mode")
}
guard j_integer(j_required(o, "version"), 1) == 1 else {
raise Invalid("unsupported request version")
}
let names = j_array(j_required(o, "variables")).map(j_text)
let cs = j_array(j_required(o, "constraints"))
guard cs.length() >= 1 && cs.length() <= 32 else {
raise Invalid("require 1..32 constraints")
}
let constraints = cs.map(v => {
let c = j_object(v)
j_only(c, ["expression", "range"])
let r = j_array(j_required(c, "range"))
guard r.length() == 2 else {
raise Invalid("each observation range requires two endpoints")
}
{
polynomial: parse_polynomial(j_text(j_required(c, "expression")), names),
allowed: interval(j_number(r[0]), j_number(r[1])),
}
})
let model = { names, constraints, }
validate_constraints(model)
let box = j_array(j_required(o, "box")).map(v => {
let a = j_array(v)
guard a.length() == 2 else {
raise Invalid("each input interval requires two endpoints")
}
interval(j_number(a[0]), j_number(a[1]))
})
check_box(box, names.length())
{ model, box, options: request_options(o), }
}
///|
fn paving_json(request : FeasibilityRequest, result : PavingResult) -> Json {
let inside = result.inside.map(b => {
let value : Json = { "box": box_json(b.box), "ranges": box_json(b.ranges) }
value
})
let unknown = result.unresolved.map(b => {
let value : Json = {
"box": box_json(b.box),
"reason": Json::string(b.reason),
}
value
})
{
"version": 1,
"mode": "feasibility",
"variables": Json::array(request.model.names.map(x => Json::string(x))),
"initial_box": box_json(request.box),
"complete": Json::boolean(result.complete),
"resolution_met": Json::boolean(result.resolution_met),
"definitely_empty": Json::boolean(
result.complete && result.inside.is_empty(),
),
"inner_box_count": Json::number(result.inside.length().to_double()),
"inside": Json::array(inside),
"unresolved": Json::array(unknown),
"stats": {
"assessments": Json::number(result.assessments.to_double()),
"excluded": Json::number(result.excluded.to_double()),
"splits": Json::number(result.splits.to_double()),
"contractions": 0,
},
}
}