///|
fn research_object(
text : String,
kind : String,
allowed : Array[String],
) -> Map[String, Json] raise TopologyError {
if text.length() > 1000000 {
raise TopologyError("research request exceeds 1 MB")
}
let parsed = @json.parse(text) catch {
_ => raise TopologyError("invalid research JSON")
}
let fields = batch_object(parsed, "research request")
if batch_string(fields, "kind") != kind {
raise TopologyError("expected research kind " + kind)
}
for key in fields.keys() {
if key != "kind" && !allowed.contains(key) {
raise TopologyError("unknown research field: " + key)
}
}
fields
}
///|
fn research_number(
fields : Map[String, Json],
name : String,
fallback : Double,
) -> Double raise TopologyError {
let v = match fields.get(name) {
Some(_) => number_field(fields, name)
None => fallback
}
if !finite(v) || v.abs() > 1.0e100 {
raise TopologyError("invalid research number " + name)
}
v
}
///|
fn research_string(
fields : Map[String, Json],
name : String,
fallback : String,
) -> String raise TopologyError {
match fields.get(name) {
Some(_) => batch_string(fields, name)
None => fallback
}
}
///|
fn research_numbers(
fields : Map[String, Json],
name : String,
fallback : Array[Double],
) -> Array[Double] raise TopologyError {
let values : Array[Double] = match fields.get(name) {
None => fallback
Some(v) =>
@json.from_json(v) catch {
_ => raise TopologyError("expected number array " + name)
}
}
for v in values {
if !finite(v) || v.abs() > 1.0e100 {
raise TopologyError("invalid number in " + name)
}
}
values
}
///|
fn research_next(state : Ref[Int], n : Int) -> Int {
state.val = (state.val * 25173 + 13849) % 65536
(state.val.to_double() * n.to_double() / 65536.0).to_int()
}
///|
fn research_quantile(values : Array[Double], q : Double) -> Double {
let sorted = values.copy()
sorted.sort()
let pos = q * (sorted.length() - 1).to_double()
let low = @math.floor(pos).to_int()
let high = minimum_int(low + 1, sorted.length() - 1)
sorted[low] + (sorted[high] - sorted[low]) * (pos - low.to_double())
}
///|
fn research_vector(
a : Analysis,
dimension : Int,
start : Double,
end : Double,
samples : Int,
) -> Array[Double] raise TopologyError {
persistence_landscape(a, dimension~, start~, end~, samples~, layers=1)[0]
}
///|
fn research_l2(a : Array[Double], b : Array[Double]) -> Double {
let mut norm = 0.0
for i = 0; i < a.length(); i = i + 1 {
norm = @math.hypot(norm, a[i] - b[i])
}
norm / a.length().to_double().sqrt()
}
///|
fn research_request(value : Json) -> Analysis raise TopologyError {
let a = analyze_json(value.stringify())
if a.filtration.cells.length() > 10000 {
raise TopologyError("research case cell budget exceeded")
}
a
}
///|
fn research_csv_rows(
header : Array[String],
rows : Array[Array[Json]],
) -> String {
let mut text = header.join(",") + "\n"
for row in rows {
let cells = row.map(fn(value) {
match value {
Null => ""
String(s) => "\"" + s.replace_all(old="\"", new="\"\"") + "\""
_ => value.stringify()
}
})
text += cells.join(",") + "\n"
}
text
}