From cfd09c9d04bc8b1ddfb834149e60fc5400c0cf7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E5=98=89=E5=B0=89?= Date: Tue, 20 Oct 2026 02:01:26 +0800 Subject: [PATCH 1/2] Accept reordered neighbors in C graph clones The graph contract ignores neighbor order, but C results depended on it and rejected valid deep copies. Normalize neighbor IDs so equivalent graphs receive the same verdict while retaining deep-copy validation. --- tests/browser/compiler-explorer.test.js | 88 ++++++++++++++++++++----- web/compiler-explorer.js | 3 +- 2 files changed, 74 insertions(+), 17 deletions(-) diff --git a/tests/browser/compiler-explorer.test.js b/tests/browser/compiler-explorer.test.js index a024e535..fa5b2f46 100644 --- a/tests/browser/compiler-explorer.test.js +++ b/tests/browser/compiler-explorer.test.js @@ -395,20 +395,42 @@ describe("toolchain harnesses", { concurrency: true }, () => { }, ); - it( - "C Clone Graph harness accepts the numNeighbors field its starter names", - { skip: noCc }, - async () => { - const dir = mkdtempSync(join(tmpdir(), "codetrial-c-")); - try { - const source = join(dir, "clonegraph.c"); - const binary = join(dir, "clonegraph"); - writeFileSync( - source, - generateHarness( - "c", - judges["clone-graph"], - `static struct Node* copies[101]; + // A degree-three node distinguishes rotation from simple reversal. + const cloneGraphSpec = { + ...judges["clone-graph"], + cases: [ + ...judges["clone-graph"].cases, + { + label: "three-neighbor star", + input: [[[2, 3, 4], [1], [1], [1]]], + expected: [[2, 3, 4], [1], [1], [1]], + }, + ], + }; + + for (const [order, neighborIndex] of [ + ["original neighbor order", "i"], + ["reversed neighbors", "s->numNeighbors - 1 - i"], + [ + "reversed neighbors on odd nodes", + "s->val % 2 ? s->numNeighbors - 1 - i : i", + ], + ["rotated neighbors", "(i + 1) % s->numNeighbors"], + ]) { + it( + `C Clone Graph harness accepts numNeighbors with ${order}`, + { skip: noCc }, + async () => { + const dir = mkdtempSync(join(tmpdir(), "codetrial-c-")); + try { + const source = join(dir, "clonegraph.c"); + const binary = join(dir, "clonegraph"); + writeFileSync( + source, + generateHarness( + "c", + cloneGraphSpec, + `static struct Node* copies[101]; static struct Node* copyNode(struct Node* s) { if (!s) return NULL; if (copies[s->val]) return copies[s->val]; @@ -418,13 +440,43 @@ static struct Node* copyNode(struct Node* s) { copy->numNeighbors = s->numNeighbors; copy->neighbors = calloc(s->numNeighbors + 1, sizeof(*copy->neighbors)); for (int i = 0; i < s->numNeighbors; i++) - copy->neighbors[i] = copyNode(s->neighbors[i]); + copy->neighbors[i] = copyNode(s->neighbors[${neighborIndex}]); return copy; } struct Node* replicateTopology(struct Node* s) { memset(copies, 0, sizeof(copies)); return copyNode(s); }`, + ), + ); + await run("cc", ["-std=c17", source, "-o", binary]); + assert.deepEqual( + parseCompilerResults( + (await run(binary, [], { encoding: "utf8" })).stdout, + ).results.map((result) => result.actual), + cloneGraphSpec.cases.map((testCase) => testCase.expected), + ); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }, + ); + } + + it( + "C Clone Graph harness rejects original nodes", + { skip: noCc }, + async () => { + const dir = mkdtempSync(join(tmpdir(), "codetrial-c-")); + try { + const source = join(dir, "clonegraph-original.c"); + const binary = join(dir, "clonegraph-original"); + writeFileSync( + source, + generateHarness( + "c", + cloneGraphSpec, + "struct Node* replicateTopology(struct Node* s) { return s; }", ), ); await run("cc", ["-std=c17", source, "-o", binary]); @@ -432,7 +484,11 @@ struct Node* replicateTopology(struct Node* s) { parseCompilerResults( (await run(binary, [], { encoding: "utf8" })).stdout, ).results.map((result) => result.actual), - judges["clone-graph"].cases.map((testCase) => testCase.expected), + cloneGraphSpec.cases.map((testCase) => + testCase.input[0].length + ? "Returned graph must be a deep copy." + : [], + ), ); } finally { rmSync(dir, { recursive: true, force: true }); diff --git a/web/compiler-explorer.js b/web/compiler-explorer.js index ce0840bc..81b78ae7 100644 --- a/web/compiler-explorer.js +++ b/web/compiler-explorer.js @@ -379,7 +379,8 @@ static void json_string_matrix(FILE* out, char*** values, int size, int* colSize static void json_list(FILE* out, struct ListNode* node) { for (struct ListNode *slow = node, *fast = node; fast && fast->next; ) { slow = slow->next; fast = fast->next->next; if (slow == fast) { fputs("null,\\"error\\":", out); json_string(out, "Returned list must not reach a node twice."); return; } } fputc('[', out); for (int i = 0; node; node = node->next, i++) { if (i) fputc(',', out); fprintf(out, "%d", node->val); } fputc(']', out); } static void json_tree(FILE* out, struct TreeNode* root) { if (!root) { fputs("[]", out); return; } struct TreeNode* queue[4096]; const char* values[4096]; char numbers[4096][32]; int head = 0, tail = 0, count = 0; queue[tail++] = root; while (head < tail && tail < 4096) { struct TreeNode* node = queue[head++]; if (!node) { values[count++] = "null"; continue; } snprintf(numbers[count], sizeof(numbers[count]), "%d", node->val); values[count] = numbers[count]; count++; queue[tail++] = node->left; queue[tail++] = node->right; } while (count > 0 && strcmp(values[count - 1], "null") == 0) count--; fputc('[', out); for (int i = 0; i < count; i++) { if (i) fputc(',', out); fputs(values[i], out); } fputc(']', out); } static void json_random_list(FILE* out, struct Node* node) { struct Node* nodes[10000]; int count = 0; for (struct Node* cur = node; cur && count < 10000; cur = cur->next) { if (isOriginalRandomNode(cur)) { json_string(out, "Returned list must be a deep copy."); return; } nodes[count++] = cur; } fputc('[', out); for (int i = 0; i < count; i++) { if (i) fputc(',', out); int random = -1; for (int j = 0; j < count; j++) if (nodes[i]->random == nodes[j]) random = j; fprintf(out, "[%d,", nodes[i]->val); if (nodes[i]->random && random < 0) { json_string(out, "Returned random pointer must stay inside the copied list."); fputc(']', out); continue; } if (random < 0) fputs("null", out); else fprintf(out, "%d", random); fputc(']', out); } fputc(']', out); } -static void json_graph(FILE* out, struct Node* node) { if (!node) { fputs("[]", out); return; } struct Node* nodes[1024]; bool seen[1024] = {0}; int count = 0; nodes[count++] = node; for (int i = 0; i < count && count < 1024; i++) { struct Node* cur = nodes[i]; if (isOriginalGraphNode(cur)) { json_string(out, "Returned graph must be a deep copy."); return; } if (cur->val > 0 && cur->val < 1024) seen[cur->val] = true; for (int j = 0; j < cur->numNeighbors; j++) { struct Node* next = cur->neighbors[j]; if (next && next->val > 0 && next->val < 1024 && !seen[next->val]) { seen[next->val] = true; nodes[count++] = next; } } } fputc('[', out); for (int value = 1, printed = 0; value < 1024; value++) { struct Node* cur = NULL; for (int i = 0; i < count; i++) if (nodes[i]->val == value) cur = nodes[i]; if (!cur) continue; if (printed++) fputc(',', out); fputc('[', out); for (int j = 0; j < cur->numNeighbors; j++) { if (j) fputc(',', out); fprintf(out, "%d", cur->neighbors[j]->val); } fputc(']', out); } fputc(']', out); } +static int compare_graph_neighbor_ids(const void* a, const void* b) { int left = *(const int*)a; int right = *(const int*)b; return (left > right) - (left < right); } +static void json_graph(FILE* out, struct Node* node) { if (!node) { fputs("[]", out); return; } struct Node* nodes[1024]; bool seen[1024] = {0}; int count = 0; nodes[count++] = node; for (int i = 0; i < count && count < 1024; i++) { struct Node* cur = nodes[i]; if (isOriginalGraphNode(cur)) { json_string(out, "Returned graph must be a deep copy."); return; } if (cur->val > 0 && cur->val < 1024) seen[cur->val] = true; for (int j = 0; j < cur->numNeighbors; j++) { struct Node* next = cur->neighbors[j]; if (next && next->val > 0 && next->val < 1024 && !seen[next->val]) { seen[next->val] = true; nodes[count++] = next; } } } fputc('[', out); for (int value = 1, printed = 0; value < 1024; value++) { struct Node* cur = NULL; for (int i = 0; i < count; i++) if (nodes[i]->val == value) cur = nodes[i]; if (!cur) continue; if (printed++) fputc(',', out); int degree = cur->numNeighbors; int* neighborIds = NULL; if (degree > 0) { neighborIds = malloc((size_t)degree * sizeof(*neighborIds)); if (!neighborIds) exit(EXIT_FAILURE); for (int j = 0; j < degree; j++) neighborIds[j] = cur->neighbors[j]->val; qsort(neighborIds, (size_t)degree, sizeof(*neighborIds), compare_graph_neighbor_ids); } json_int_array(out, neighborIds, degree); free(neighborIds); } fputc(']', out); } static void json_next_tree(FILE* out, struct Node* root) { if (!root) { fputs("[]", out); return; } struct Node* queue[4096]; int head = 0, tail = 0, firstLevel = 1; queue[tail++] = root; fputc('[', out); while (head < tail) { int end = tail; if (!firstLevel) fputc(',', out); firstLevel = 0; fputc('[', out); for (int i = head; i < end; i++) { if (i > head) fputc(',', out); fprintf(out, "%d", queue[i]->val); if (queue[i]->next != (i + 1 < end ? queue[i + 1] : NULL)) { fputs("]", out); fputs("]", out); return; } if (queue[i]->left) queue[tail++] = queue[i]->left; if (queue[i]->right) queue[tail++] = queue[i]->right; } fputc(']', out); head = end; } fputc(']', out); } static void json_quad_tree_visit(FILE* out, struct Node* node, int* printed) { if (!node) return; if ((*printed)++) fputc(',', out); fprintf(out, "[%d,%d]", node->isLeaf ? 1 : 0, node->isLeaf ? (node->val ? 1 : 0) : 1); if (!node->isLeaf) { json_quad_tree_visit(out, node->topLeft, printed); json_quad_tree_visit(out, node->topRight, printed); json_quad_tree_visit(out, node->bottomLeft, printed); json_quad_tree_visit(out, node->bottomRight, printed); } } static void json_quad_tree(FILE* out, struct Node* root) { int printed = 0; fputc('[', out); json_quad_tree_visit(out, root, &printed); fputc(']', out); } From faa7c102d5b27b2d2fe751f9c0ba76e27960159d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E5=98=89=E5=B0=89?= Date: Thu, 22 Oct 2026 00:51:51 +0800 Subject: [PATCH 2/2] Reject NULL neighbors in C and C++ graphs Incomplete graph clones can leave NULL neighbor slots and crash the harness. Reject them during traversal so malformed answers receive a diagnostic and the remaining test cases can still run. --- tests/browser/compiler-explorer.test.js | 119 ++++++++++++++++++++++++ web/compiler-explorer.js | 4 +- 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/tests/browser/compiler-explorer.test.js b/tests/browser/compiler-explorer.test.js index fa5b2f46..9de27260 100644 --- a/tests/browser/compiler-explorer.test.js +++ b/tests/browser/compiler-explorer.test.js @@ -496,6 +496,125 @@ struct Node* replicateTopology(struct Node* s) { }, ); + // A non-root NULL must be rejected before any adjacency rows are emitted. + for (const [position, nodeValue] of [ + ["the root node", 1], + ["a reachable non-root node", 2], + ]) { + it( + `C Clone Graph harness reports a NULL neighbor on ${position}`, + { skip: noCc }, + async () => { + const dir = mkdtempSync(join(tmpdir(), "codetrial-c-graph-null-")); + try { + const source = join(dir, "clonegraph-null.c"); + const binary = join(dir, "clonegraph-null"); + writeFileSync( + source, + generateHarness( + "c", + cloneGraphSpec, + `static struct Node* copies[101]; +static struct Node* copyNode(struct Node* s) { + if (!s) return NULL; + if (copies[s->val]) return copies[s->val]; + struct Node* copy = calloc(1, sizeof(*copy)); + copy->val = s->val; + copies[s->val] = copy; + copy->numNeighbors = s->numNeighbors; + copy->neighbors = calloc(s->numNeighbors, sizeof(*copy->neighbors)); + for (int i = 0; i < s->numNeighbors; i++) { + if (s->val == ${nodeValue} && i == s->numNeighbors - 1) continue; + copy->neighbors[i] = copyNode(s->neighbors[i]); + } + return copy; +} +struct Node* replicateTopology(struct Node* s) { + memset(copies, 0, sizeof(copies)); + return copyNode(s); +}`, + ), + ); + await run("cc", ["-std=c17", source, "-o", binary]); + assert.deepEqual( + parseCompilerResults( + (await run(binary, [], { encoding: "utf8" })).stdout, + ).results.map((result) => result.actual), + cloneGraphSpec.cases.map((testCase) => + testCase.input[0][nodeValue - 1]?.length + ? "Returned graph has a NULL neighbor." + : testCase.expected, + ), + ); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }, + ); + } + + for (const [behavior, nullNeighborValue] of [ + ["accepts reordered neighbors", null], + ["reports a NULL neighbor on the root node", 1], + ["reports a NULL neighbor on a reachable non-root node", 2], + ]) { + it(`C++ Clone Graph harness ${behavior}`, { skip: noGpp }, async () => { + const dir = mkdtempSync(join(tmpdir(), "codetrial-cpp-graph-")); + try { + const source = join(dir, "clonegraph.cpp"); + const binary = join(dir, "clonegraph"); + writeFileSync( + source, + generateHarness( + "cpp", + cloneGraphSpec, + `class Solution { + map copies; + Node* copyNode(Node* node) { + if (!node) return nullptr; + if (copies.count(node)) return copies[node]; + auto* copy = new Node(node->val); + copies[node] = copy; + copy->neighbors.resize(node->neighbors.size(), nullptr); + for (size_t i = 0; i < node->neighbors.size(); i++) { + if (node->val == ${nullNeighborValue ?? 0} && i + 1 == node->neighbors.size()) continue; + copy->neighbors[i] = copyNode(node->neighbors[node->neighbors.size() - 1 - i]); + } + return copy; + } +public: + Node* replicateTopology(Node* node) { return copyNode(node); } +};`, + ), + ); + await run("g++", ["-std=c++20", source, "-o", binary]); + const { results } = parseCompilerResults( + (await run(binary, [], { encoding: "utf8" })).stdout, + ); + assert.equal(results.length, cloneGraphSpec.cases.length); + cloneGraphSpec.cases.forEach((testCase, index) => { + const result = results[index]; + if ( + nullNeighborValue !== null && + testCase.input[0][nullNeighborValue - 1]?.length + ) { + assert.equal( + result.error, + "Returned graph has a NULL neighbor.", + testCase.label, + ); + assert.equal(result.actual, undefined, testCase.label); + } else { + assert.equal(result.error, undefined, testCase.label); + assert.deepEqual(result.actual, testCase.expected, testCase.label); + } + }); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); + } + it( "C Merge k Sorted Lists harness can produce passing Compiler Explorer JSON", { skip: noCc }, diff --git a/web/compiler-explorer.js b/web/compiler-explorer.js index 81b78ae7..b11e4cc4 100644 --- a/web/compiler-explorer.js +++ b/web/compiler-explorer.js @@ -380,7 +380,7 @@ static void json_list(FILE* out, struct ListNode* node) { for (struct ListNode * static void json_tree(FILE* out, struct TreeNode* root) { if (!root) { fputs("[]", out); return; } struct TreeNode* queue[4096]; const char* values[4096]; char numbers[4096][32]; int head = 0, tail = 0, count = 0; queue[tail++] = root; while (head < tail && tail < 4096) { struct TreeNode* node = queue[head++]; if (!node) { values[count++] = "null"; continue; } snprintf(numbers[count], sizeof(numbers[count]), "%d", node->val); values[count] = numbers[count]; count++; queue[tail++] = node->left; queue[tail++] = node->right; } while (count > 0 && strcmp(values[count - 1], "null") == 0) count--; fputc('[', out); for (int i = 0; i < count; i++) { if (i) fputc(',', out); fputs(values[i], out); } fputc(']', out); } static void json_random_list(FILE* out, struct Node* node) { struct Node* nodes[10000]; int count = 0; for (struct Node* cur = node; cur && count < 10000; cur = cur->next) { if (isOriginalRandomNode(cur)) { json_string(out, "Returned list must be a deep copy."); return; } nodes[count++] = cur; } fputc('[', out); for (int i = 0; i < count; i++) { if (i) fputc(',', out); int random = -1; for (int j = 0; j < count; j++) if (nodes[i]->random == nodes[j]) random = j; fprintf(out, "[%d,", nodes[i]->val); if (nodes[i]->random && random < 0) { json_string(out, "Returned random pointer must stay inside the copied list."); fputc(']', out); continue; } if (random < 0) fputs("null", out); else fprintf(out, "%d", random); fputc(']', out); } fputc(']', out); } static int compare_graph_neighbor_ids(const void* a, const void* b) { int left = *(const int*)a; int right = *(const int*)b; return (left > right) - (left < right); } -static void json_graph(FILE* out, struct Node* node) { if (!node) { fputs("[]", out); return; } struct Node* nodes[1024]; bool seen[1024] = {0}; int count = 0; nodes[count++] = node; for (int i = 0; i < count && count < 1024; i++) { struct Node* cur = nodes[i]; if (isOriginalGraphNode(cur)) { json_string(out, "Returned graph must be a deep copy."); return; } if (cur->val > 0 && cur->val < 1024) seen[cur->val] = true; for (int j = 0; j < cur->numNeighbors; j++) { struct Node* next = cur->neighbors[j]; if (next && next->val > 0 && next->val < 1024 && !seen[next->val]) { seen[next->val] = true; nodes[count++] = next; } } } fputc('[', out); for (int value = 1, printed = 0; value < 1024; value++) { struct Node* cur = NULL; for (int i = 0; i < count; i++) if (nodes[i]->val == value) cur = nodes[i]; if (!cur) continue; if (printed++) fputc(',', out); int degree = cur->numNeighbors; int* neighborIds = NULL; if (degree > 0) { neighborIds = malloc((size_t)degree * sizeof(*neighborIds)); if (!neighborIds) exit(EXIT_FAILURE); for (int j = 0; j < degree; j++) neighborIds[j] = cur->neighbors[j]->val; qsort(neighborIds, (size_t)degree, sizeof(*neighborIds), compare_graph_neighbor_ids); } json_int_array(out, neighborIds, degree); free(neighborIds); } fputc(']', out); } +static void json_graph(FILE* out, struct Node* node) { if (!node) { fputs("[]", out); return; } struct Node* nodes[1024]; bool seen[1024] = {0}; int count = 0; nodes[count++] = node; for (int i = 0; i < count && count < 1024; i++) { struct Node* cur = nodes[i]; if (isOriginalGraphNode(cur)) { json_string(out, "Returned graph must be a deep copy."); return; } if (cur->val > 0 && cur->val < 1024) seen[cur->val] = true; for (int j = 0; j < cur->numNeighbors; j++) { struct Node* next = cur->neighbors[j]; if (!next) { json_string(out, "Returned graph has a NULL neighbor."); return; } if (next->val > 0 && next->val < 1024 && !seen[next->val]) { seen[next->val] = true; nodes[count++] = next; } } } fputc('[', out); for (int value = 1, printed = 0; value < 1024; value++) { struct Node* cur = NULL; for (int i = 0; i < count; i++) if (nodes[i]->val == value) cur = nodes[i]; if (!cur) continue; if (printed++) fputc(',', out); int degree = cur->numNeighbors; int* neighborIds = NULL; if (degree > 0) { neighborIds = malloc((size_t)degree * sizeof(*neighborIds)); if (!neighborIds) exit(EXIT_FAILURE); for (int j = 0; j < degree; j++) neighborIds[j] = cur->neighbors[j]->val; qsort(neighborIds, (size_t)degree, sizeof(*neighborIds), compare_graph_neighbor_ids); } json_int_array(out, neighborIds, degree); free(neighborIds); } fputc(']', out); } static void json_next_tree(FILE* out, struct Node* root) { if (!root) { fputs("[]", out); return; } struct Node* queue[4096]; int head = 0, tail = 0, firstLevel = 1; queue[tail++] = root; fputc('[', out); while (head < tail) { int end = tail; if (!firstLevel) fputc(',', out); firstLevel = 0; fputc('[', out); for (int i = head; i < end; i++) { if (i > head) fputc(',', out); fprintf(out, "%d", queue[i]->val); if (queue[i]->next != (i + 1 < end ? queue[i + 1] : NULL)) { fputs("]", out); fputs("]", out); return; } if (queue[i]->left) queue[tail++] = queue[i]->left; if (queue[i]->right) queue[tail++] = queue[i]->right; } fputc(']', out); head = end; } fputc(']', out); } static void json_quad_tree_visit(FILE* out, struct Node* node, int* printed) { if (!node) return; if ((*printed)++) fputc(',', out); fprintf(out, "[%d,%d]", node->isLeaf ? 1 : 0, node->isLeaf ? (node->val ? 1 : 0) : 1); if (!node->isLeaf) { json_quad_tree_visit(out, node->topLeft, printed); json_quad_tree_visit(out, node->topRight, printed); json_quad_tree_visit(out, node->bottomLeft, printed); json_quad_tree_visit(out, node->bottomRight, printed); } } static void json_quad_tree(FILE* out, struct Node* root) { int printed = 0; fputc('[', out); json_quad_tree_visit(out, root, &printed); fputc(']', out); } @@ -541,7 +541,7 @@ TreeNode* findTreeNode(TreeNode* root, int value) { queue q; if (root Node* randomList(vector>> items) { vector nodes; for (auto item : items) nodes.push_back(new Node(item.first)); originalRandomNodes = set(nodes.begin(), nodes.end()); for (size_t i = 0; i < nodes.size(); ++i) { nodes[i]->next = i + 1 < nodes.size() ? nodes[i + 1] : nullptr; if (items[i].second) nodes[i]->random = nodes[*items[i].second]; } return nodes.empty() ? nullptr : nodes[0]; } string randomListToJson(Node* node) { vector nodes; map index; set seen; for (auto* cur = node; cur && !seen.count(cur); cur = cur->next) { if (originalRandomNodes.count(cur)) throw runtime_error("Returned list must be a deep copy."); seen.insert(cur); index[cur] = nodes.size(); nodes.push_back(cur); } string out = "["; for (size_t i = 0; i < nodes.size(); ++i) { if (i) out += ","; if (nodes[i]->random && !index.count(nodes[i]->random)) throw runtime_error("Returned random pointer must stay inside the copied list."); out += "[" + to_string(nodes[i]->val) + "," + (nodes[i]->random ? to_string(index[nodes[i]->random]) : string("null")) + "]"; } return out + "]"; } Node* graphNode(vector> adjacency) { if (adjacency.empty()) { originalGraphNodes.clear(); return nullptr; } vector nodes; for (size_t i = 0; i < adjacency.size(); ++i) nodes.push_back(new Node(i + 1)); originalGraphNodes = set(nodes.begin(), nodes.end()); for (size_t i = 0; i < adjacency.size(); ++i) for (int value : adjacency[i]) nodes[i]->neighbors.push_back(nodes[value - 1]); return nodes[0]; } -string graphToJson(Node* node) { if (!node) return "[]"; map byValue; queue q; q.push(node); while (!q.empty()) { auto* cur = q.front(); q.pop(); if (!cur) continue; if (originalGraphNodes.count(cur)) throw runtime_error("Returned graph must be a deep copy."); if (byValue.count(cur->val)) continue; byValue[cur->val] = cur; for (auto* next : cur->neighbors) q.push(next); } string out = "["; bool first = true; for (auto& [value, cur] : byValue) { if (!first) out += ","; first = false; vector neighbors; for (auto* next : cur->neighbors) neighbors.push_back(next->val); sort(neighbors.begin(), neighbors.end()); out += toJson(neighbors); } return out + "]"; } +string graphToJson(Node* node) { if (!node) return "[]"; map byValue; queue q; q.push(node); while (!q.empty()) { auto* cur = q.front(); q.pop(); if (!cur) continue; if (originalGraphNodes.count(cur)) throw runtime_error("Returned graph must be a deep copy."); if (byValue.count(cur->val)) continue; byValue[cur->val] = cur; for (auto* next : cur->neighbors) { if (!next) throw runtime_error("Returned graph has a NULL neighbor."); q.push(next); } } string out = "["; bool first = true; for (auto& [value, cur] : byValue) { if (!first) out += ","; first = false; vector neighbors; for (auto* next : cur->neighbors) neighbors.push_back(next->val); sort(neighbors.begin(), neighbors.end()); out += toJson(neighbors); } return out + "]"; } Node* nextTree(vector> values) { if (values.empty() || !values[0]) return nullptr; auto* root = new Node(*values[0]); queue q; q.push(root); size_t i = 1; while (!q.empty() && i < values.size()) { auto* node = q.front(); q.pop(); if (i < values.size() && values[i]) { node->left = new Node(*values[i]); q.push(node->left); } ++i; if (i < values.size() && values[i]) { node->right = new Node(*values[i]); q.push(node->right); } ++i; } return root; } string nextTreeToJson(Node* root) { vector levels; queue q; if (root) q.push(root); while (!q.empty()) { size_t count = q.size(); vector level; for (size_t i = 0; i < count; ++i) { auto* node = q.front(); q.pop(); level.push_back(node); if (node->left) q.push(node->left); if (node->right) q.push(node->right); } vector row; for (size_t i = 0; i < level.size(); ++i) { if (level[i]->next != (i + 1 < level.size() ? level[i + 1] : nullptr)) throw runtime_error("next pointers do not match the tree level order."); row.push_back(level[i]->val); } levels.push_back(toJson(row)); } string out = "["; for (size_t i = 0; i < levels.size(); ++i) { if (i) out += ","; out += levels[i]; } return out + "]"; } string quadTreeToJson(Node* root) { vector rows; function visit = [&](Node* node) { if (!node) return; rows.push_back("[" + to_string(node->isLeaf ? 1 : 0) + "," + to_string(node->isLeaf ? (node->val ? 1 : 0) : 1) + "]"); if (!node->isLeaf) { visit(node->topLeft); visit(node->topRight); visit(node->bottomLeft); visit(node->bottomRight); } }; visit(root); string out = "["; for (size_t i = 0; i < rows.size(); ++i) { if (i) out += ","; out += rows[i]; } return out + "]"; }`;