1import { RTV } from "numbl";
2import type { RuntimeValue } from "numbl";
3import type { PlainValue } from "./plain";
5type Kinded = { kind?: string };
7/** numbl RuntimeValue -> PlainValue. Tensor data is copied so the result owns its memory. */
8export function toPlain(v: RuntimeValue | undefined): PlainValue {
9 if (v === undefined) return { kind: "unsupported", description: "undefined" };
10 if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v;
11 const k = (v as Kinded).kind;
12 switch (k) {
13 case "tensor": {
14 const t = v as { data: Float64Array; imag?: Float64Array; shape: number[] };
15 const out: PlainValue = { kind: "tensor", shape: [...t.shape], data: Float64Array.from(t.data) };
16 if (t.imag) out.imag = Float64Array.from(t.imag);
17 return out;
18 }
19 case "char":
20 return (v as { value: string }).value;
21 case "struct": {
22 const fields: Record<string, PlainValue> = {};
23 for (const [name, val] of (v as { fields: Map<string, RuntimeValue> }).fields) fields[name] = toPlain(val);
24 return { kind: "struct", fields };
25 }
26 case "cell": {
27 const c = v as { data: RuntimeValue[]; shape: number[] };
28 return { kind: "cell", shape: [...c.shape], items: c.data.map(toPlain) };
29 }
30 case "struct_array": {
31 // struct(...) with a cell-valued field builds a struct array in MATLAB; a single
32 // element is the common case (e.g. 'notes', {notes}) and reads as a plain struct.
33 const sa = v as { elements: RuntimeValue[] };
34 if (sa.elements.length === 1) return toPlain(sa.elements[0]);
35 return { kind: "cell", shape: [1, sa.elements.length], items: sa.elements.map(toPlain) };
36 }
37 case "complex":
38 return { kind: "unsupported", description: "complex scalar" };
39 default:
40 return { kind: "unsupported", description: k ?? typeof v };
41 }
42}
44/** PlainValue -> numbl RuntimeValue, for passing results back into a script (e.g. plate_compare). */
45export function toRuntime(v: PlainValue): RuntimeValue {
46 if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v;
47 switch (v.kind) {
48 case "tensor":
49 return RTV.tensor(Float64Array.from(v.data), [...v.shape], v.imag ? Float64Array.from(v.imag) : undefined);
50 case "struct": {
51 const fields: Record<string, RuntimeValue> = {};
52 for (const [name, val] of Object.entries(v.fields)) fields[name] = toRuntime(val);
53 return RTV.struct(fields);
54 }
55 case "cell":
56 return RTV.cell(v.items.map(toRuntime), [...v.shape]);
57 case "unsupported":
58 throw new Error(`cannot convert unsupported value (${v.description}) to numbl`);
59 }
60}