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 = {
16 kind: "tensor",
17 shape: [...t.shape],
18 data: Float64Array.from(t.data),
19 };
20 if (t.imag) out.imag = Float64Array.from(t.imag);
21 return out;
22 }
23 case "char":
24 return (v as { value: string }).value;
25 case "struct": {
26 const fields: Record<string, PlainValue> = {};
27 for (const [name, val] of (v as { fields: Map<string, RuntimeValue> }).fields)
28 fields[name] = toPlain(val);
29 return { kind: "struct", fields };
30 }
31 case "cell": {
32 const c = v as { data: RuntimeValue[]; shape: number[] };
33 return { kind: "cell", shape: [...c.shape], items: c.data.map(toPlain) };
34 }
35 case "struct_array": {
36 // struct(...) with a cell-valued field builds a struct array in MATLAB; a single
37 // element is the common case (e.g. 'notes', {notes}) and reads as a plain struct.
38 const sa = v as { elements: RuntimeValue[] };
39 if (sa.elements.length === 1) return toPlain(sa.elements[0]);
40 return { kind: "cell", shape: [1, sa.elements.length], items: sa.elements.map(toPlain) };
41 }
42 case "complex":
43 return { kind: "unsupported", description: "complex scalar" };
44 default:
45 return { kind: "unsupported", description: k ?? typeof v };
46 }
47}
49/** PlainValue -> numbl RuntimeValue, for passing the modes of stage 1 into stage 2. */
50export function toRuntime(v: PlainValue): RuntimeValue {
51 if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") return v;
52 switch (v.kind) {
53 case "tensor":
54 return RTV.tensor(
55 Float64Array.from(v.data),
56 [...v.shape],
57 v.imag ? Float64Array.from(v.imag) : undefined,
58 );
59 case "struct": {
60 const fields: Record<string, RuntimeValue> = {};
61 for (const [name, val] of Object.entries(v.fields)) fields[name] = toRuntime(val);
62 return RTV.struct(fields);
63 }
64 case "cell":
65 return RTV.cell(v.items.map(toRuntime), [...v.shape]);
66 case "unsupported":
67 throw new Error(`cannot convert unsupported value (${v.description}) to numbl`);
68 }
69}