From e2940183f1d73647abaebb44eec4ed699897e30e Mon Sep 17 00:00:00 2001 From: Max Richter Date: Wed, 10 Apr 2024 21:57:03 +0200 Subject: [PATCH] feat: add flat_tree allgorithm --- app/package.json | 4 +- app/src/lib/helpers/flat_tree.test.ts | 61 +++++++++++++++++++++++++++ app/src/lib/helpers/flat_tree.ts | 60 ++++++++++++++++++++++++++ app/src/lib/node-registry.ts | 5 +++ app/src/lib/runtime-executor.ts | 5 +++ app/src/routes/+page.svelte | 20 +++++---- app/src/routes/test/+page.svelte | 20 +++++++++ packages/types/index.ts | 10 +++++ pnpm-lock.yaml | 3 ++ 9 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 app/src/lib/helpers/flat_tree.test.ts create mode 100644 app/src/lib/helpers/flat_tree.ts create mode 100644 app/src/routes/test/+page.svelte diff --git a/app/package.json b/app/package.json index 5549419..e1514cd 100644 --- a/app/package.json +++ b/app/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite --host", "build": "vite build", + "test": "vitest", "preview": "vite preview", "tauri:dev": "tauri dev", "story:dev": "histoire dev", @@ -40,6 +41,7 @@ "typescript": "^5.0.2", "vite": "^5.1.4", "vite-plugin-glsl": "^1.2.1", - "vite-plugin-wasm": "^3.3.0" + "vite-plugin-wasm": "^3.3.0", + "vitest": "^1.2.0" } } diff --git a/app/src/lib/helpers/flat_tree.test.ts b/app/src/lib/helpers/flat_tree.test.ts new file mode 100644 index 0000000..4dd6a3a --- /dev/null +++ b/app/src/lib/helpers/flat_tree.test.ts @@ -0,0 +1,61 @@ +import { expect, test } from 'vitest' +import { decode, encode } from './flat_tree' + +// Original test case +test('it correctly decodes/encodes complex nested arrays', () => { + const input = [5, [6, 1], 1, 5, [5], [7, 2, [5, 1]]]; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with empty array +test('it correctly handles an empty array', () => { + const input: number[] = []; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with nested empty arrays +test('it correctly handles nested empty arrays', () => { + const input = [5, [], [6, []], []]; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with single-element array +test('it correctly handles a single-element array', () => { + const input = [42]; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with deeply nested array +test('it correctly handles deeply nested arrays', () => { + const input = [[[[[1]]]]]; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with large numbers +test('it correctly handles large numbers', () => { + const input = [2147483647, [-2147483648, 1234567890]]; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with sequential nesting +test('it correctly handles sequential nesting', () => { + const input = [1, [2, [3, [4, [5]]]]]; + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); + +// Test with mixed data types (if supported) +// Note: This test assumes your implementation supports mixed types. +// If not, you can ignore or remove this test. +test('it correctly handles arrays with mixed data types', () => { + const input = [1, 'text', [true, [null, ['another text']]]]; + // @ts-ignore + const decoded = decode(encode(input)); + expect(decoded).toEqual(input); +}); diff --git a/app/src/lib/helpers/flat_tree.ts b/app/src/lib/helpers/flat_tree.ts new file mode 100644 index 0000000..e7ad64f --- /dev/null +++ b/app/src/lib/helpers/flat_tree.ts @@ -0,0 +1,60 @@ + +type SparseArray = (T | T[] | SparseArray)[]; + +// Encodes a nested array into a flat array with bracket and distance notation +export function encode(array: SparseArray): number[] { + const encoded = [0, 0]; // Initialize encoded array with root bracket notation + let missingBracketIndex = 1; // Track where to insert the distance to the next bracket + + for (let index = 0; index < array.length; index++) { + const item = array[index]; + if (Array.isArray(item)) { + // Update the distance to the next bracket for the last opened bracket + encoded[missingBracketIndex] = encoded.length - missingBracketIndex; + if (item.length === 0) { + // Handle empty arrays by directly adding bracket notation + encoded.push(0, 1, 1, 1); + } else { + // Recursively encode non-empty arrays + const child = encode(item); + encoded.push(...child, 1, 0); // Note: The trailing comma after 0 can be removed + } + // Update missingBracketIndex to the position of the newly added bracket + missingBracketIndex = encoded.length - 1; + } else { + // Handle non-array items + encoded.push(item); + // Update the distance for the last opened bracket + if (missingBracketIndex) encoded[missingBracketIndex] = index + 2; + } + } + return encoded; +}; + +function decode_recursive(dense: number[], index = 0) { + const decoded: (number | number[])[] = []; + let nextBracketIndex = dense[index + 1] + index + 1; // Calculate the index of the next bracket + + index += 2; // Skip the initial bracket notation + while (index < dense.length) { + if (index === nextBracketIndex) { + if (dense[index] === 0) { // Opening bracket detected + const [p, nextIndex, _nextBracketIndex] = decode_recursive(dense, index); + decoded.push(p); + index = nextIndex + 1; + nextBracketIndex = _nextBracketIndex; + } else { // Closing bracket detected + nextBracketIndex = dense[index + 1] + index + 1; + return [decoded, index, nextBracketIndex] as const; + } + } else if (index < nextBracketIndex) { + decoded.push(dense[index]); // Add regular number to decoded array + } + index++; + } + return [decoded, index, nextBracketIndex] as const; +} + +export function decode(dense: number[]) { + return decode_recursive(dense, 0)[0]; +} diff --git a/app/src/lib/node-registry.ts b/app/src/lib/node-registry.ts index 3c9e787..501126e 100644 --- a/app/src/lib/node-registry.ts +++ b/app/src/lib/node-registry.ts @@ -41,6 +41,8 @@ const nodeTypes: NodeType[] = [ const log = createLogger("node-registry"); export class RemoteNodeRegistry implements NodeRegistry { + + status: "loading" | "ready" | "error" = "loading"; private nodes: Map = new Map(); constructor(private url: string) { } @@ -55,6 +57,7 @@ export class RemoteNodeRegistry implements NodeRegistry { const wasmResponse = await fetch(`${nodeUrl}/wasm`); const wrapperReponse = await fetch(`${nodeUrl}/wrapper`); if (!wrapperReponse.ok) { + this.status = "error"; throw new Error(`Failed to load node ${id}`); } @@ -68,6 +71,7 @@ wasm = val;`); wasmWrapper.__wbg_set_wasm(instance.exports); if (!response.ok) { + this.status = "error"; throw new Error(`Failed to load node ${id}`); } const node = await response.json(); @@ -78,6 +82,7 @@ wasm = val;`); const duration = performance.now() - a; log.log("loaded nodes in", duration, "ms"); + this.status = "ready"; } getNode(id: string) { diff --git a/app/src/lib/runtime-executor.ts b/app/src/lib/runtime-executor.ts index e226044..ab11ad1 100644 --- a/app/src/lib/runtime-executor.ts +++ b/app/src/lib/runtime-executor.ts @@ -6,6 +6,11 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { constructor(private registry: NodeRegistry) { } private getNodeTypes(graph: Graph) { + + if (this.registry.status !== "ready") { + throw new Error("Node registry is not ready"); + } + const typeMap = new Map(); for (const node of graph.nodes) { if (!typeMap.has(node.type)) { diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index d50e84a..a2d56d2 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -1,27 +1,29 @@ @@ -30,11 +32,13 @@
header
- {res} + result: {res} +
+ time: {time}ms
{#key graph} - + import { decode, encode } from "$lib/helpers/flat_tree"; + + const input = [5, [6, 1], [7, 2, [5, 1]]]; + // const input = [5, [], [6, []], []]; + // const input = [52]; + + console.log("INPUT"); + console.log(input); + + const encoded = encode(input); + console.log("ENCODED"); + console.log(encoded); + + const decoded = decode(encoded); + console.log("DECODED"); + console.log(decoded); + + console.log("EQUALS", JSON.stringify(input) === JSON.stringify(decoded)); + diff --git a/packages/types/index.ts b/packages/types/index.ts index b053ea2..3779c01 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -45,6 +45,11 @@ export type Socket = { export interface NodeRegistry { + /** + * The status of the node registry + * @remarks The status should be "loading" when the registry is loading, "ready" when the registry is ready, and "error" if an error occurred while loading the registry + */ + status: "loading" | "ready" | "error"; /** * Load the nodes with the given ids * @param nodeIds - The ids of the nodes to load @@ -67,6 +72,11 @@ export interface NodeRegistry { } export interface RuntimeExecutor { + /** + * Execute the given graph + * @param graph - The graph to execute + * @returns The result of the execution + */ execute: (graph: Graph) => void; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da7a25b..260ab67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: vite-plugin-wasm: specifier: ^3.3.0 version: 3.3.0(vite@5.1.4) + vitest: + specifier: ^1.2.0 + version: 1.4.0 nodes/max/plantarium/float: {}