@nshiab/simple-data-analysis-core 2.1.6 → 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/esm/class/SimpleTable.d.ts +399 -90
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +393 -90
  4. package/esm/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
  5. package/esm/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
  6. package/esm/helpers/buildGraphTemporalCostStateSql.js +23 -9
  7. package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
  8. package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
  9. package/esm/helpers/graphRouteResultSchema.js +4 -3
  10. package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
  11. package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
  12. package/esm/helpers/graphRouteResultSelect.js +5 -2
  13. package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
  14. package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  15. package/esm/helpers/prepareGraphMetricOptions.js +41 -0
  16. package/esm/helpers/prepareGraphRouteSql.d.ts +2 -2
  17. package/esm/helpers/prepareGraphRouteSql.d.ts.map +1 -1
  18. package/esm/helpers/prepareGraphRouteSql.js +4 -4
  19. package/esm/helpers/prepareGraphTemporalSql.d.ts +3 -0
  20. package/esm/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  21. package/esm/helpers/prepareGraphTemporalSql.js +7 -0
  22. package/esm/helpers/validateGraphRouteResultSchema.d.ts +1 -1
  23. package/esm/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
  24. package/esm/helpers/validateGraphRouteResultSchema.js +3 -1
  25. package/esm/methods/distances.d.ts +2 -0
  26. package/esm/methods/distances.d.ts.map +1 -1
  27. package/esm/methods/distances.js +274 -20
  28. package/esm/methods/findCycles.d.ts +1 -0
  29. package/esm/methods/findCycles.d.ts.map +1 -1
  30. package/esm/methods/findCycles.js +22 -15
  31. package/esm/methods/paths.d.ts +1 -0
  32. package/esm/methods/paths.d.ts.map +1 -1
  33. package/esm/methods/paths.js +21 -14
  34. package/esm/methods/shortestPath.d.ts +2 -0
  35. package/esm/methods/shortestPath.d.ts.map +1 -1
  36. package/esm/methods/shortestPath.js +155 -34
  37. package/llm.md +428 -108
  38. package/llms.txt +1 -1
  39. package/package.json +1 -1
  40. package/script/class/SimpleTable.d.ts +399 -90
  41. package/script/class/SimpleTable.d.ts.map +1 -1
  42. package/script/class/SimpleTable.js +393 -90
  43. package/script/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
  44. package/script/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
  45. package/script/helpers/buildGraphTemporalCostStateSql.js +23 -9
  46. package/script/helpers/graphRouteResultSchema.d.ts +1 -1
  47. package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
  48. package/script/helpers/graphRouteResultSchema.js +4 -3
  49. package/script/helpers/graphRouteResultSelect.d.ts +1 -1
  50. package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
  51. package/script/helpers/graphRouteResultSelect.js +5 -2
  52. package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
  53. package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  54. package/script/helpers/prepareGraphMetricOptions.js +44 -0
  55. package/script/helpers/prepareGraphRouteSql.d.ts +2 -2
  56. package/script/helpers/prepareGraphRouteSql.d.ts.map +1 -1
  57. package/script/helpers/prepareGraphRouteSql.js +4 -4
  58. package/script/helpers/prepareGraphTemporalSql.d.ts +3 -0
  59. package/script/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  60. package/script/helpers/prepareGraphTemporalSql.js +7 -0
  61. package/script/helpers/validateGraphRouteResultSchema.d.ts +1 -1
  62. package/script/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
  63. package/script/helpers/validateGraphRouteResultSchema.js +3 -1
  64. package/script/methods/distances.d.ts +2 -0
  65. package/script/methods/distances.d.ts.map +1 -1
  66. package/script/methods/distances.js +274 -20
  67. package/script/methods/findCycles.d.ts +1 -0
  68. package/script/methods/findCycles.d.ts.map +1 -1
  69. package/script/methods/findCycles.js +22 -15
  70. package/script/methods/paths.d.ts +1 -0
  71. package/script/methods/paths.d.ts.map +1 -1
  72. package/script/methods/paths.js +21 -14
  73. package/script/methods/shortestPath.d.ts +2 -0
  74. package/script/methods/shortestPath.d.ts.map +1 -1
  75. package/script/methods/shortestPath.js +155 -34
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = distances;
40
40
  const buildGraphTemporalCostStateSql_js_1 = __importDefault(require("../helpers/buildGraphTemporalCostStateSql.js"));
41
41
  const getGraphWeightColumn_js_1 = __importDefault(require("../helpers/getGraphWeightColumn.js"));
42
+ const prepareGraphMetricOptions_js_1 = __importDefault(require("../helpers/prepareGraphMetricOptions.js"));
42
43
  const prepareGraphTemporalSql_js_1 = __importStar(require("../helpers/prepareGraphTemporalSql.js"));
43
44
  const prepareGraphStarts_js_1 = __importDefault(require("../helpers/prepareGraphStarts.js"));
44
45
  const prepareGraphTraversal_js_1 = __importStar(require("../helpers/prepareGraphTraversal.js"));
@@ -67,6 +68,7 @@ function distances(simpleTable, sourceColumn, targetColumn, startNodes, options
67
68
  typeof options.outputTable !== "boolean") {
68
69
  throw new TypeError("distances() options.outputTable must be a string or boolean.");
69
70
  }
71
+ const metrics = (0, prepareGraphMetricOptions_js_1.default)(options, "distances()", true);
70
72
  const preparedStarts = (0, prepareGraphStarts_js_1.default)(startNodes, "distances()", "startNodes");
71
73
  const direction = options.direction ?? "outgoing";
72
74
  const temporalOptions = (0, prepareGraphTemporalSql_js_1.prepareGraphTemporalOptions)(options, direction, "distances()");
@@ -90,13 +92,17 @@ function distances(simpleTable, sourceColumn, targetColumn, startNodes, options
90
92
  ? preparedStarts.values
91
93
  : [temporal.gapParameter, ...preparedStarts.values];
92
94
  },
93
- buildSelect: (input, schema) => distancesSelect(input, schema, sourceColumn, targetColumn, preparedStarts, direction, options.weight, temporalOptions),
95
+ buildSelect: (input, schema) => distancesSelect(input, schema, sourceColumn, targetColumn, preparedStarts, direction, options.weight, temporalOptions, metrics),
94
96
  outputSchema: (schema) => {
95
97
  const validated = validateDistanceInputs(schema, sourceColumn, targetColumn, preparedStarts, options.weight, temporalOptions);
96
98
  return {
97
99
  start: validated.idType,
98
100
  node: validated.idType,
99
- distance: validated.distanceType,
101
+ steps: "BIGINT",
102
+ ...(options.weight === undefined
103
+ ? {}
104
+ : { total: validated.distanceType }),
105
+ ...(metrics.elapsedTime ? { elapsedTimeMs: "DOUBLE" } : {}),
100
106
  };
101
107
  },
102
108
  });
@@ -111,7 +117,7 @@ function validateDistanceInputs(schema, source, target, starts, weight, temporal
111
117
  : (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "distances()");
112
118
  return { idType: endpoints.idType, distanceType, temporal };
113
119
  }
114
- function distancesSelect(input, schema, source, target, starts, direction, weight, temporalOptions) {
120
+ function distancesSelect(input, schema, source, target, starts, direction, weight, temporalOptions, metrics) {
115
121
  const prepared = (0, prepareGraphTraversal_js_1.default)(input, schema, source, target, starts, "distances()", "startNodes");
116
122
  const weightColumn = weight === undefined
117
123
  ? undefined
@@ -120,9 +126,14 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
120
126
  const edgeWeight = weightColumn === undefined
121
127
  ? `CAST(1 AS ${distanceType})`
122
128
  : `CAST(${(0, quoteIdentifier_js_1.default)("edges")}.${(0, quoteIdentifier_js_1.default)(weightColumn.column)} AS ${distanceType})`;
123
- if (temporalOptions !== undefined) {
124
- const temporal = (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "distances()");
125
- return temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight);
129
+ const temporal = temporalOptions === undefined
130
+ ? undefined
131
+ : (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "distances()");
132
+ if (weight !== undefined || metrics.elapsedTime) {
133
+ return summaryDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, metrics, weight !== undefined, weightColumn?.type === "FLOAT" || weightColumn?.type === "DOUBLE");
134
+ }
135
+ if (temporal !== undefined) {
136
+ return temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, "steps", false);
126
137
  }
127
138
  const relations = prepared.relationNames([
128
139
  "graph_start_values",
@@ -146,6 +157,14 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
146
157
  const edgeCost = `${(0, quoteIdentifier_js_1.default)("edges")}.${(0, quoteIdentifier_js_1.default)("__weight")}`;
147
158
  const bestDistance = `${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("distance")}`;
148
159
  const candidateDistance = `CAST(${reachedDistance} + ${edgeCost} AS ${distanceType})`;
160
+ const candidateSteps = `${(0, quoteIdentifier_js_1.default)("reached")}.${(0, quoteIdentifier_js_1.default)("steps")} + 1`;
161
+ const bestSteps = `${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("steps")}`;
162
+ const order = (distance, steps) => metrics.minimize === "steps"
163
+ ? `ROW(${steps}, ${distance})`
164
+ : `ROW(${distance}, ${steps})`;
165
+ const candidateOrder = order(candidateDistance, candidateSteps);
166
+ const selectedDistance = `arg_min(${candidateDistance}, ${candidateOrder})`;
167
+ const selectedSteps = `arg_min(${candidateSteps}, ${candidateOrder})`;
149
168
  return `WITH RECURSIVE ${startValuesRelation}(${(0, quoteIdentifier_js_1.default)("start")}) AS (
150
169
  VALUES ${prepared.startValues}
151
170
  ), ${startsRelation} AS (
@@ -159,19 +178,20 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
159
178
  ), ${distancesRelation}(
160
179
  ${(0, quoteIdentifier_js_1.default)("start")}, ${(0, quoteIdentifier_js_1.default)("node")},
161
180
  ${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)("__node_key")},
162
- ${(0, quoteIdentifier_js_1.default)("distance")}
181
+ ${(0, quoteIdentifier_js_1.default)("distance")}, ${(0, quoteIdentifier_js_1.default)("steps")}
163
182
  ) USING KEY(
164
183
  ${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)("__node_key")}
165
184
  ) AS (
166
185
  SELECT ${start}, ${edgeTo}, ${startKey}, ${edgeToKey},
167
- MIN(${edgeCost}) AS ${(0, quoteIdentifier_js_1.default)("distance")}
186
+ MIN(${edgeCost}) AS ${(0, quoteIdentifier_js_1.default)("distance")}, CAST(1 AS BIGINT)
168
187
  FROM ${startsRelation} AS ${(0, quoteIdentifier_js_1.default)("starts")}
169
188
  INNER JOIN ${edgesRelation} AS ${(0, quoteIdentifier_js_1.default)("edges")}
170
189
  ON ${startKey} = ${edgeFromKey}
171
190
  GROUP BY ${start}, ${edgeTo}, ${startKey}, ${edgeToKey}
172
191
  UNION
173
192
  SELECT ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
174
- MIN(${candidateDistance}) AS ${(0, quoteIdentifier_js_1.default)("distance")}
193
+ ${selectedDistance} AS ${(0, quoteIdentifier_js_1.default)("distance")},
194
+ ${selectedSteps} AS ${(0, quoteIdentifier_js_1.default)("steps")}
175
195
  FROM ${distancesRelation} AS ${(0, quoteIdentifier_js_1.default)("reached")}
176
196
  INNER JOIN ${edgesRelation} AS ${(0, quoteIdentifier_js_1.default)("edges")}
177
197
  ON ${reachedNodeKey} = ${edgeFromKey}
@@ -179,25 +199,259 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
179
199
  ON ${reachedStartKey} = ${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("__start_key")}
180
200
  AND ${edgeToKey} = ${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("__node_key")}
181
201
  GROUP BY ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
182
- ${bestDistance}
183
- HAVING ${bestDistance} IS NULL OR MIN(${candidateDistance}) < ${bestDistance}
202
+ ${bestDistance}, ${bestSteps}
203
+ HAVING ${bestDistance} IS NULL OR
204
+ ${order(selectedDistance, selectedSteps)} < ${order(bestDistance, bestSteps)}
184
205
  )
185
206
  SELECT ${(0, quoteIdentifier_js_1.default)("start")}, ${(0, quoteIdentifier_js_1.default)("node")},
186
- ${(0, quoteIdentifier_js_1.default)("distance")}
207
+ ${(0, quoteIdentifier_js_1.default)("steps")}${weight === undefined
208
+ ? ""
209
+ : `, ${(0, quoteIdentifier_js_1.default)("distance")} AS ${(0, quoteIdentifier_js_1.default)("total")}`}
187
210
  FROM ${distancesRelation}
188
- ORDER BY ${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)("distance")}, ${(0, quoteIdentifier_js_1.default)("__node_key")}`;
211
+ ORDER BY ${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)(metrics.minimize === "steps" ? "steps" : "distance")}, ${(0, quoteIdentifier_js_1.default)("__node_key")}`;
189
212
  }
190
- function temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight) {
213
+ function temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, minimize, includeTotal) {
191
214
  const q = quoteIdentifier_js_1.default;
192
215
  const startsSelect = `SELECT ${q("start")},
193
216
  ${prepared.key(q("start"))} AS ${q("__key")}
194
217
  FROM (VALUES ${prepared.startValues}) AS ${q("start_values")}(${q("start")})`;
195
- const costStates = (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, distanceType, edgeWeight);
218
+ const costStates = (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, distanceType, edgeWeight, [], minimize);
219
+ const order = minimize === "steps"
220
+ ? `${q("steps")}, ${q("distance")}`
221
+ : `${q("distance")}, ${q("steps")}`;
196
222
  return `${costStates.withClause}
197
- SELECT ${q("start")}, ${q("node")},
198
- MIN(${q("distance")}) AS ${q("distance")}
223
+ SELECT ${q("start")}, ${q("node")}, ${q("steps")}${includeTotal ? `, ${q("distance")} AS ${q("total")}` : ""}
199
224
  FROM ${costStates.costRelation}
200
- GROUP BY ${q("start")}, ${q("node")}, ${q("__start_key")},
201
- ${q("__node_key")}
202
- ORDER BY ${q("__start_key")}, ${q("distance")}, ${q("__node_key")}`;
225
+ QUALIFY row_number() OVER (
226
+ PARTITION BY ${q("__start_key")}, ${q("__node_key")}
227
+ ORDER BY ${order}
228
+ ) = 1
229
+ ORDER BY ${q("__start_key")}, ${q(minimize === "steps" ? "steps" : "distance")}, ${q("__node_key")}`;
230
+ }
231
+ // Distinct visited sets, metrics, and temporal boundaries are enough to extend
232
+ // summaries. No edge IDs or full connection sequences are needed. Keeping the
233
+ // visited set is necessary: equal summaries can have different legal extensions.
234
+ function summaryDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, metrics, includeTotal, floatingWeight) {
235
+ const q = quoteIdentifier_js_1.default;
236
+ const relations = prepared.relationNames([
237
+ "graph_starts",
238
+ "graph_temporal_settings",
239
+ "graph_event_rows",
240
+ "graph_edges",
241
+ "graph_summaries",
242
+ "graph_metric_results",
243
+ "graph_tied_summaries",
244
+ "graph_best_metrics",
245
+ "graph_metric_bounds",
246
+ ]);
247
+ const starts = relations.graph_starts;
248
+ const settings = relations.graph_temporal_settings;
249
+ const events = relations.graph_event_rows;
250
+ const edges = relations.graph_edges;
251
+ const summaries = relations.graph_summaries;
252
+ const results = relations.graph_metric_results;
253
+ const tied = relations.graph_tied_summaries;
254
+ const ref = (alias, column) => `${q(alias)}.${q(column)}`;
255
+ const searchDirection = direction;
256
+ const firstAnchor = temporal?.journeyAnchor("edges", searchDirection);
257
+ const sum = `CAST(${ref("reached", "distance")} + ${ref("edges", "__weight")} AS ${distanceType})`;
258
+ // Reported costs must not reject routes before the primary objective has
259
+ // selected its results. NULL marks overflow and propagates through sums;
260
+ // non-negative weights cannot bring an overflowing total back into range.
261
+ const nextDistance = metrics.minimize === "weight" ? sum : `TRY(${sum})`;
262
+ const temporalColumns = temporal === undefined
263
+ ? ""
264
+ : `, ${q("__event_start")}, ${q("__event_end")}`;
265
+ const eventValues = temporal === undefined
266
+ ? ""
267
+ : `, ${ref("edges", "__event_start")}, ${ref("edges", "__event_end")}`;
268
+ const metric = metrics.minimize === "steps"
269
+ ? q("steps")
270
+ : metrics.minimize === "weight"
271
+ ? q("distance")
272
+ : q("__elapsed");
273
+ // Use native time precision for selection. Collapse equal reported values
274
+ // after conversion to milliseconds, since these are summary rows.
275
+ const reportedElapsed = metrics.elapsedTime
276
+ ? `, ${temporal.elapsedMilliseconds(q("__elapsed"))} AS ${q("elapsedTimeMs")}`
277
+ : "";
278
+ const order = metrics.minimize === "steps"
279
+ ? [
280
+ q("steps"),
281
+ q("__node_key"),
282
+ ...(includeTotal ? [q("total")] : []),
283
+ ...(metrics.elapsedTime ? [q("elapsedTimeMs")] : []),
284
+ ]
285
+ : metrics.minimize === "weight"
286
+ ? [
287
+ q("total"),
288
+ q("__node_key"),
289
+ q("steps"),
290
+ ...(metrics.elapsedTime ? [q("elapsedTimeMs")] : []),
291
+ ]
292
+ : [
293
+ q("elapsedTimeMs"),
294
+ q("__node_key"),
295
+ q("steps"),
296
+ ...(includeTotal ? [q("total")] : []),
297
+ ];
298
+ const bounds = summaryBoundsSql(relations.graph_best_metrics, relations.graph_metric_bounds, starts, edges, settings, temporal, searchDirection, metrics.minimize, distanceType, floatingWeight);
299
+ return `WITH RECURSIVE ${settings} AS (
300
+ ${temporal === undefined
301
+ ? `SELECT 0 AS ${q("__gap")}`
302
+ : `SELECT CAST(? AS HUGEINT) AS ${q("__gap")}`}
303
+ ), ${events} AS MATERIALIZED (
304
+ ${prepared.edges(direction, [
305
+ `${edgeWeight} AS ${q("__weight")}`,
306
+ ...(temporal?.eventSelections("edges") ?? []),
307
+ ])}
308
+ ), ${edges} AS MATERIALIZED (
309
+ SELECT DISTINCT ${q("__from")}, ${q("__to")}, ${q("__from_key")},
310
+ ${q("__to_key")}, ${q("__weight")}${temporalColumns}
311
+ FROM ${events} AS ${q("events")}
312
+ ${temporal === undefined ? "" : `WHERE ${temporal.eventValidity("events")}`}
313
+ ), ${starts} AS (
314
+ SELECT ${q("start")}, ${prepared.key(q("start"))} AS ${q("__key")}
315
+ FROM (VALUES ${prepared.startValues}) AS ${q("start_values")}(${q("start")})
316
+ ), ${bounds.ctes}, ${summaries}(
317
+ ${q("start")}, ${q("node")}, ${q("__start_key")}, ${q("__node_key")},
318
+ ${q("__visited")}, ${q("steps")}, ${q("distance")}${temporalColumns}${metrics.elapsedTime ? `, ${q("__anchor")}` : ""}
319
+ ) AS (
320
+ SELECT ${ref("starts", "start")}, ${ref("edges", "__to")},
321
+ ${ref("starts", "__key")}, ${ref("edges", "__to_key")},
322
+ list_sort(list_distinct([${ref("starts", "__key")}, ${ref("edges", "__to_key")}])),
323
+ CAST(1 AS BIGINT), ${ref("edges", "__weight")}${eventValues}${metrics.elapsedTime ? `, ${firstAnchor}` : ""}
324
+ FROM ${starts} AS ${q("starts")}
325
+ INNER JOIN ${edges} AS ${q("edges")}
326
+ ON ${ref("starts", "__key")} = ${ref("edges", "__from_key")}
327
+ ${bounds.join(ref("starts", "__key"))}
328
+ WHERE ${bounds.test("CAST(1 AS BIGINT)", ref("edges", "__weight"), firstAnchor)}
329
+ UNION
330
+ SELECT ${ref("reached", "start")}, ${ref("edges", "__to")},
331
+ ${ref("reached", "__start_key")}, ${ref("edges", "__to_key")},
332
+ list_sort(list_distinct(list_append(${ref("reached", "__visited")}, ${ref("edges", "__to_key")}))),
333
+ ${ref("reached", "steps")} + 1, ${nextDistance}${eventValues}${metrics.elapsedTime ? `, ${ref("reached", "__anchor")}` : ""}
334
+ FROM ${summaries} AS ${q("reached")}
335
+ INNER JOIN ${edges} AS ${q("edges")}
336
+ ON ${ref("reached", "__node_key")} = ${ref("edges", "__from_key")}
337
+ CROSS JOIN ${settings} AS ${q("settings")}
338
+ ${bounds.join(ref("reached", "__start_key"))}
339
+ WHERE ${bounds.test(`${ref("reached", "steps")} + 1`, nextDistance, ref("reached", "__anchor"))}
340
+ AND ${ref("reached", "__node_key")} <> ${ref("reached", "__start_key")}
341
+ AND (NOT list_contains(${ref("reached", "__visited")}, ${ref("edges", "__to_key")})
342
+ OR ${ref("edges", "__to_key")} = ${ref("reached", "__start_key")})
343
+ ${temporal === undefined ? "" : `AND ${temporal.transition("reached", "edges", searchDirection, ref("settings", "__gap"))}`}
344
+ ), ${results} AS (
345
+ SELECT *${metrics.elapsedTime
346
+ ? `, ${temporal.journeyElapsed("summaries", ref("summaries", "__anchor"), searchDirection)} AS ${q("__elapsed")}`
347
+ : ""}
348
+ FROM ${summaries} AS ${q("summaries")}
349
+ ), ${tied} AS MATERIALIZED (
350
+ SELECT DISTINCT ${q("start")}, ${q("node")}, ${q("__start_key")}, ${q("__node_key")},
351
+ ${q("steps")}${includeTotal ? `, ${q("distance")} AS ${q("total")}` : ""}${reportedElapsed}
352
+ FROM ${results}
353
+ QUALIFY ${metric} = MIN(${metric}) OVER (
354
+ PARTITION BY ${q("__start_key")}, ${q("__node_key")}
355
+ )
356
+ )
357
+ SELECT ${q("start")}, ${q("node")}, ${q("steps")}${includeTotal
358
+ ? `, CASE WHEN ${q("total")} IS NULL
359
+ THEN error('distances() total exceeds the supported ${distanceType} range for an optimal route.')
360
+ ELSE ${q("total")} END AS ${q("total")}`
361
+ : ""}${metrics.elapsedTime ? `, ${q("elapsedTimeMs")}` : ""}
362
+ FROM ${tied}
363
+ ORDER BY ${q("__start_key")}, ${order.join(", ")}`;
364
+ }
365
+ // Compute primary optima without enumerating visited sets. Exact additive
366
+ // objectives have optimal prefixes (at the same event boundary in temporal
367
+ // traversal). Floating addition can erase prefix differences, and elapsed
368
+ // optima can depend on a later departure, so those use a per-start upper bound.
369
+ function summaryBoundsSql(best, limits, starts, edges, settings, temporal, direction, minimize, distanceType, floatingWeight) {
370
+ const q = quoteIdentifier_js_1.default;
371
+ const ref = (alias, column) => `${q(alias)}.${q(column)}`;
372
+ const elapsed = minimize === "elapsedTime";
373
+ const globalBound = elapsed || (minimize === "weight" && floatingWeight);
374
+ const boundaries = temporal === undefined
375
+ ? []
376
+ : ["__event_start", "__event_end"];
377
+ const keys = [
378
+ "__start_key",
379
+ "__node_key",
380
+ ...boundaries,
381
+ ...(elapsed ? ["__anchor"] : []),
382
+ ];
383
+ const firstAnchor = elapsed
384
+ ? temporal.journeyAnchor("edges", direction)
385
+ : undefined;
386
+ const firstKeys = [
387
+ ref("starts", "__key"),
388
+ ref("edges", "__to_key"),
389
+ ...boundaries.map((key) => ref("edges", key)),
390
+ ...(elapsed ? [firstAnchor] : []),
391
+ ];
392
+ const nextKeys = [
393
+ ref("reached", "__start_key"),
394
+ ref("edges", "__to_key"),
395
+ ...boundaries.map((key) => ref("edges", key)),
396
+ ...(elapsed ? [ref("reached", "__anchor")] : []),
397
+ ];
398
+ const firstMetric = minimize === "steps"
399
+ ? "CAST(1 AS BIGINT)"
400
+ : elapsed
401
+ ? temporal.journeyElapsed("edges", firstAnchor, direction)
402
+ : ref("edges", "__weight");
403
+ const nextMetric = minimize === "steps"
404
+ ? `${ref("reached", "__metric")} + 1`
405
+ : elapsed
406
+ ? temporal.journeyElapsed("edges", ref("reached", "__anchor"), direction)
407
+ : `CAST(${ref("reached", "__metric")} + ${ref("edges", "__weight")} AS ${distanceType})`;
408
+ const minimum = `MIN(${nextMetric})`;
409
+ const bestMetric = ref("best", "__metric");
410
+ const ctes = `${best}(${keys.map(q).join(", ")}, ${q("__metric")})
411
+ USING KEY(${keys.map(q).join(", ")}) AS (
412
+ SELECT ${firstKeys.join(", ")}, MIN(${firstMetric})
413
+ FROM ${starts} AS ${q("starts")}
414
+ INNER JOIN ${edges} AS ${q("edges")}
415
+ ON ${ref("starts", "__key")} = ${ref("edges", "__from_key")}
416
+ GROUP BY ${firstKeys.join(", ")}
417
+ UNION
418
+ SELECT ${nextKeys.join(", ")}, ${minimum}
419
+ FROM ${best} AS ${q("reached")}
420
+ INNER JOIN ${edges} AS ${q("edges")}
421
+ ON ${ref("reached", "__node_key")} = ${ref("edges", "__from_key")}
422
+ CROSS JOIN ${settings} AS ${q("settings")}
423
+ LEFT JOIN recurring.${best} AS ${q("best")}
424
+ ON ${keys.map((key, i) => `${ref("best", key)} = ${nextKeys[i]}`).join(" AND ")}
425
+ WHERE ${ref("reached", "__node_key")} <> ${ref("reached", "__start_key")}
426
+ ${temporal === undefined ? "" : `AND ${temporal.transition("reached", "edges", direction, ref("settings", "__gap"))}`}
427
+ GROUP BY ${nextKeys.join(", ")}, ${bestMetric}
428
+ HAVING ${bestMetric} IS NULL OR ${minimum} < ${bestMetric}
429
+ )${globalBound
430
+ ? `, ${limits} AS (
431
+ SELECT ${q("__start_key")}, MAX(${q("__minimum")}) AS ${q("__metric")}
432
+ FROM (
433
+ SELECT ${q("__start_key")}, ${q("__node_key")}, MIN(${q("__metric")}) AS ${q("__minimum")}
434
+ FROM ${best}
435
+ GROUP BY ${q("__start_key")}, ${q("__node_key")}
436
+ ) AS ${q("minima")}
437
+ GROUP BY ${q("__start_key")}
438
+ )`
439
+ : ""}`;
440
+ return {
441
+ ctes,
442
+ join: (startKey) => `INNER JOIN ${globalBound ? limits : best} AS ${q("bounds")}
443
+ ON ${ref("bounds", "__start_key")} = ${startKey}
444
+ ${globalBound
445
+ ? ""
446
+ : `AND ${ref("bounds", "__node_key")} = ${ref("edges", "__to_key")}
447
+ ${boundaries.map((key) => `AND ${ref("bounds", key)} = ${ref("edges", key)}`).join(" ")}`}`,
448
+ test: (steps, distance, anchor) => {
449
+ const metric = minimize === "steps"
450
+ ? steps
451
+ : elapsed
452
+ ? temporal.journeyElapsed("edges", anchor, direction)
453
+ : distance;
454
+ return `${metric} ${globalBound ? "<=" : "="} ${ref("bounds", "__metric")}`;
455
+ },
456
+ };
203
457
  }
@@ -3,6 +3,7 @@ import { type GraphTemporalOptions } from "../helpers/prepareGraphTemporalSql.js
3
3
  import { type GraphId } from "../helpers/prepareGraphStarts.js";
4
4
  import { type GraphDirection } from "../helpers/prepareGraphTraversal.js";
5
5
  type FindCyclesOptions = GraphTemporalOptions & {
6
+ elapsedTime?: boolean;
6
7
  direction?: GraphDirection;
7
8
  outputTable?: string | boolean;
8
9
  weight?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"findCycles.d.ts","sourceRoot":"","sources":["../../src/methods/findCycles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAA2B,EACzB,KAAK,OAAO,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAA8B,EAC5B,KAAK,cAAc,EAEpB,MAAM,qCAAqC,CAAC;AAM7C,KAAK,iBAAiB,GAAG,oBAAoB,GAAG;IAC9C,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,OAAO,GAAG,OAAO,EAAE,EAC/B,OAAO,GAAE,iBAAsB,GAC9B,WAAW,CAuHb"}
1
+ {"version":3,"file":"findCycles.d.ts","sourceRoot":"","sources":["../../src/methods/findCycles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAA2B,EACzB,KAAK,OAAO,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAA8B,EAC5B,KAAK,cAAc,EAEpB,MAAM,qCAAqC,CAAC;AAM7C,KAAK,iBAAiB,GAAG,oBAAoB,GAAG;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,OAAO,GAAG,OAAO,EAAE,EAC/B,OAAO,GAAE,iBAAsB,GAC9B,WAAW,CAgIb"}
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = findCycles;
40
+ const prepareGraphMetricOptions_js_1 = __importDefault(require("../helpers/prepareGraphMetricOptions.js"));
40
41
  const getGraphEdgeIdColumn_js_1 = __importDefault(require("../helpers/getGraphEdgeIdColumn.js"));
41
42
  const getGraphWeightColumn_js_1 = __importDefault(require("../helpers/getGraphWeightColumn.js"));
42
43
  const graphRouteResultSchema_js_1 = __importDefault(require("../helpers/graphRouteResultSchema.js"));
@@ -74,6 +75,7 @@ function findCycles(simpleTable, sourceColumn, targetColumn, edgeId, startNodes,
74
75
  typeof options.outputTable !== "boolean") {
75
76
  throw new TypeError("findCycles() options.outputTable must be a string or boolean.");
76
77
  }
78
+ const metric = (0, prepareGraphMetricOptions_js_1.default)(options, "findCycles()", false);
77
79
  const direction = options.direction ?? "outgoing";
78
80
  const temporalOptions = (0, prepareGraphTemporalSql_js_1.prepareGraphTemporalOptions)(options, direction, "findCycles()");
79
81
  options = structuredClone(options);
@@ -92,7 +94,8 @@ function findCycles(simpleTable, sourceColumn, targetColumn, edgeId, startNodes,
92
94
  ? undefined
93
95
  : (input) => (0, validateGraphTemporalEvents_js_1.default)(input, temporalOptions, "findCycles()", parameters),
94
96
  values: (schema) => {
95
- validateFindCyclesInputs(schema, sourceColumn, targetColumn, edgeId, preparedStarts, options.weight);
97
+ const validated = validateFindCyclesInputs(schema, sourceColumn, targetColumn, edgeId, preparedStarts, options.weight);
98
+ (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "findCycles()", metric.elapsedTime);
96
99
  const temporal = temporalOptions === undefined
97
100
  ? undefined
98
101
  : (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "findCycles()");
@@ -100,7 +103,7 @@ function findCycles(simpleTable, sourceColumn, targetColumn, edgeId, startNodes,
100
103
  ? preparedStarts.values
101
104
  : [temporal.gapParameter, ...preparedStarts.values];
102
105
  },
103
- buildSelect: (input, schema) => findCyclesSelect(input, schema, sourceColumn, targetColumn, edgeId, preparedStarts, direction, options.weight, temporalOptions),
106
+ buildSelect: (input, schema) => findCyclesSelect(input, schema, sourceColumn, targetColumn, edgeId, preparedStarts, direction, options.weight, temporalOptions, metric.elapsedTime),
104
107
  outputSchema: (schema) => {
105
108
  const validated = validateFindCyclesInputs(schema, sourceColumn, targetColumn, edgeId, preparedStarts, options.weight);
106
109
  if (temporalOptions !== undefined) {
@@ -108,7 +111,7 @@ function findCycles(simpleTable, sourceColumn, targetColumn, edgeId, startNodes,
108
111
  }
109
112
  return {
110
113
  start: validated.nodeIdType,
111
- ...(0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "findCycles()"),
114
+ ...(0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "findCycles()", metric.elapsedTime),
112
115
  };
113
116
  },
114
117
  });
@@ -126,7 +129,7 @@ function validateFindCyclesInputs(schema, source, target, edgeId, starts, weight
126
129
  nodeIdType: endpoints.idType,
127
130
  };
128
131
  }
129
- function findCyclesSelect(input, schema, source, target, edgeId, starts, direction, weight, temporalOptions) {
132
+ function findCyclesSelect(input, schema, source, target, edgeId, starts, direction, weight, temporalOptions, elapsedTime) {
130
133
  const prepared = (0, prepareGraphTraversal_js_1.default)(input, schema, source, target, starts, "findCycles()", "startNodes");
131
134
  const edgeIdColumn = (0, getGraphEdgeIdColumn_js_1.default)(schema, edgeId, "findCycles()");
132
135
  const weightColumn = weight === undefined
@@ -142,10 +145,10 @@ function findCyclesSelect(input, schema, source, target, edgeId, starts, directi
142
145
  const edgeWeight = weightColumn === undefined
143
146
  ? `CAST(1 AS ${distanceType})`
144
147
  : `CAST(${q("edges")}.${q(weightColumn.column)} AS ${distanceType})`;
145
- const stepType = `STRUCT(${q("edgeId")} ${edgeIdColumn.idType}, ${q("source")} ${prepared.endpoints.idType}, ${q("target")} ${prepared.endpoints.idType}, ${q("weight")} ${distanceType}, ${q("distance")} ${distanceType})`;
148
+ const stepType = `STRUCT(${q("edgeId")} ${edgeIdColumn.idType}, ${q("source")} ${prepared.endpoints.idType}, ${q("target")} ${prepared.endpoints.idType}, ${q("weight")} ${distanceType}, ${q("distance")} ${distanceType}${elapsedTime ? `, ${q("elapsedTimeMs")} DOUBLE` : ""})`;
146
149
  if (temporalOptions !== undefined) {
147
150
  const temporal = (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "findCycles()");
148
- return temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn);
151
+ return temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn, elapsedTime);
149
152
  }
150
153
  const relations = prepared.relationNames([
151
154
  "graph_start_values",
@@ -249,7 +252,7 @@ function findCyclesSelect(input, schema, source, target, edgeId, starts, directi
249
252
  )
250
253
  ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, input, edgeIdColumn, true)}`;
251
254
  }
252
- function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn) {
255
+ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn, elapsedTime) {
253
256
  const q = quoteIdentifier_js_1.default;
254
257
  const relations = prepared.relationNames([
255
258
  "graph_start_values",
@@ -279,12 +282,16 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
279
282
  const walkDistance = `${q("walks")}.${q("distance")}`;
280
283
  const candidateDistance = `CAST(${walkDistance} + ${edgeCost} AS ${distanceType})`;
281
284
  const gap = `${q("settings")}.${q("__gap")}`;
282
- const step = (distance) => `struct_pack(
285
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
286
+ const anchor = `${q("walks")}.${q("__journey_anchor")}`;
287
+ const step = (distance, journeyAnchor) => `struct_pack(
283
288
  ${q("edgeId")} := ${edgeIdentity},
284
289
  ${q("source")} := ${edgeFrom},
285
290
  ${q("target")} := ${edgeTo},
286
291
  ${q("weight")} := ${edgeCost},
287
- ${q("distance")} := ${distance}
292
+ ${q("distance")} := ${distance}${elapsedTime
293
+ ? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(temporal.journeyElapsed("edges", journeyAnchor, direction))}`
294
+ : ""}
288
295
  )`;
289
296
  return `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
290
297
  SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
@@ -310,7 +317,7 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
310
317
  ${q("__node_key")}, ${q("__visited")}, ${q("__event_ids")},
311
318
  ${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
312
319
  ${q("closed")}, ${q("__event_id")}, ${q("__event_start")},
313
- ${q("__event_end")}
320
+ ${q("__event_end")}${elapsedTime ? `, ${q("__journey_anchor")}` : ""}
314
321
  ) AS (
315
322
  SELECT ${q("starts")}.${q("start")},
316
323
  ${q("starts")}.${q("__start_key")}, ${edgeTo}, ${edgeToKey},
@@ -319,11 +326,11 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
319
326
  ELSE [${edgeFromKey}, ${edgeToKey}]
320
327
  END,
321
328
  [${q("edges")}.${q("__event_id")}], [${edgeIdentityKey}],
322
- [${step(edgeCost)}]::${stepType}[], ${edgeCost},
329
+ [${step(edgeCost, firstAnchor)}]::${stepType}[], ${edgeCost},
323
330
  ${edgeFromKey} = ${edgeToKey},
324
331
  ${q("edges")}.${q("__event_id")},
325
332
  ${q("edges")}.${q("__event_start")},
326
- ${q("edges")}.${q("__event_end")}
333
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${firstAnchor}` : ""}
327
334
  FROM ${edgesRelation} AS ${q("edges")}
328
335
  INNER JOIN ${startsRelation} AS ${q("starts")}
329
336
  ON ${q("starts")}.${q("__start_key")} = ${edgeFromKey}
@@ -334,12 +341,12 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
334
341
  list_append(${q("walks")}.${q("__event_ids")},
335
342
  ${q("edges")}.${q("__event_id")}),
336
343
  list_append(${q("walks")}.${q("__edge_keys")}, ${edgeIdentityKey}),
337
- list_append(${q("walks")}.${q("steps")}, ${step(candidateDistance)}),
344
+ list_append(${q("walks")}.${q("steps")}, ${step(candidateDistance, anchor)}),
338
345
  ${candidateDistance},
339
346
  ${edgeToKey} = ${q("walks")}.${q("__start_key")},
340
347
  ${q("edges")}.${q("__event_id")},
341
348
  ${q("edges")}.${q("__event_start")},
342
- ${q("edges")}.${q("__event_end")}
349
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${anchor}` : ""}
343
350
  FROM ${walksRelation} AS ${q("walks")}
344
351
  INNER JOIN ${edgesRelation} AS ${q("edges")}
345
352
  ON ${q("walks")}.${q("__node_key")} = ${edgeFromKey}
@@ -365,5 +372,5 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
365
372
  AS BIGINT) AS ${q("pathId")}, ${q("steps")}
366
373
  FROM ${completeRelation}
367
374
  )
368
- ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, input, edgeIdColumn, true)}`;
375
+ ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, input, edgeIdColumn, true, elapsedTime)}`;
369
376
  }
@@ -3,6 +3,7 @@ import type { GraphId } from "../helpers/prepareGraphStarts.js";
3
3
  import { type GraphTemporalOptions } from "../helpers/prepareGraphTemporalSql.js";
4
4
  import type { GraphDirection } from "../helpers/prepareGraphTraversal.js";
5
5
  type PathsOptions = GraphTemporalOptions & {
6
+ elapsedTime?: boolean;
6
7
  direction?: GraphDirection;
7
8
  outputTable?: string | boolean;
8
9
  weight?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/methods/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAM1E,KAAK,YAAY,GAAG,oBAAoB,GAAG;IACzC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,KAAK,CAC3B,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,YAAiB,GACzB,WAAW,CA+Gb"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/methods/paths.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAM1E,KAAK,YAAY,GAAG,oBAAoB,GAAG;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,KAAK,CAC3B,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,YAAiB,GACzB,WAAW,CA4Hb"}