import { RTV } from "numbl"; import type { RuntimeValue } from "numbl"; import type { PlainValue } from "./plain"; type Kinded = { kind?: string }; /** numbl RuntimeValue -> PlainValue. Tensor data is copied so the result owns its memory. */ export function toPlain(v: RuntimeValue | undefined): PlainValue { if (v === undefined) return { kind: "unsupported", description: "undefined" }; if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v; const k = (v as Kinded).kind; switch (k) { case "tensor": { const t = v as { data: Float64Array; imag?: Float64Array; shape: number[] }; const out: PlainValue = { kind: "tensor", shape: [...t.shape], data: Float64Array.from(t.data), }; if (t.imag) out.imag = Float64Array.from(t.imag); return out; } case "char": return (v as { value: string }).value; case "struct": { const fields: Record = {}; for (const [name, val] of (v as { fields: Map }).fields) fields[name] = toPlain(val); return { kind: "struct", fields }; } case "cell": { const c = v as { data: RuntimeValue[]; shape: number[] }; return { kind: "cell", shape: [...c.shape], items: c.data.map(toPlain) }; } case "struct_array": { // struct(...) with a cell-valued field builds a struct array in MATLAB; a single // element is the common case (e.g. 'notes', {notes}) and reads as a plain struct. const sa = v as { elements: RuntimeValue[] }; if (sa.elements.length === 1) return toPlain(sa.elements[0]); return { kind: "cell", shape: [1, sa.elements.length], items: sa.elements.map(toPlain) }; } case "complex": return { kind: "unsupported", description: "complex scalar" }; default: return { kind: "unsupported", description: k ?? typeof v }; } } /** PlainValue -> numbl RuntimeValue, for passing results back into a script (e.g. plate_compare). */ export function toRuntime(v: PlainValue): RuntimeValue { if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v; switch (v.kind) { case "tensor": return RTV.tensor( Float64Array.from(v.data), [...v.shape], v.imag ? Float64Array.from(v.imag) : undefined, ); case "struct": { const fields: Record = {}; for (const [name, val] of Object.entries(v.fields)) fields[name] = toRuntime(val); return RTV.struct(fields); } case "cell": return RTV.cell(v.items.map(toRuntime), [...v.shape]); case "unsupported": throw new Error(`cannot convert unsupported value (${v.description}) to numbl`); } }