mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 00:27:55 +00:00
Docs: Update benchmark results
This commit is contained in:
141
results.json
141
results.json
@@ -1,79 +1,80 @@
|
|||||||
{
|
{
|
||||||
"openai/gpt-5.1-codex": {
|
"google/gemini-3-pro-preview": {
|
||||||
"1_dijkstra": 15.241279893,
|
"10_scrypt_hash": 39.295109482,
|
||||||
"2_convex_hull": 13.933983282,
|
"11_geospatial": 38.59770959899999,
|
||||||
"3_lis": 3.980165064000001,
|
"1_dijkstra": 34.202315816,
|
||||||
"4_determinant": 6.332550646999996,
|
"2_convex_hull": 34.06182440299999,
|
||||||
"5_markdown_parser": 3.2380716380000014,
|
"3_lis": 25.023427584999997,
|
||||||
"6_csv_processor": 26.09038364200001,
|
"4_determinant": 28.093709440000005,
|
||||||
"7_scheduler": 130.18603798900003,
|
"5_markdown_parser": 28.463938538000015,
|
||||||
"8_json_validator": 8.38715121099999,
|
"6_csv_processor": 38.290726488999994,
|
||||||
"9_stream_visualizer": 12.031535295999987
|
"7_scheduler": 67.523956824,
|
||||||
},
|
"8_json_validator": 16.506907749000007,
|
||||||
"openai/gpt-5.1-chat": {
|
"9_stream_visualizer": 51.923362084000026
|
||||||
"1_dijkstra": 2.65463601799999,
|
|
||||||
"2_convex_hull": 3.1764218759999787,
|
|
||||||
"3_lis": 2.5808361240000233,
|
|
||||||
"4_determinant": 1.857067280999996,
|
|
||||||
"5_markdown_parser": 2.1500550130000047,
|
|
||||||
"6_csv_processor": 2.8134082990000024,
|
|
||||||
"7_scheduler": 6.116577822999999,
|
|
||||||
"8_json_validator": 1.477971265,
|
|
||||||
"9_stream_visualizer": 1.7076946140000073
|
|
||||||
},
|
|
||||||
"google/gemini-2.5-pro": {
|
|
||||||
"1_dijkstra": 33.989065073000006,
|
|
||||||
"2_convex_hull": 56.566219838000016,
|
|
||||||
"3_lis": 52.14045403000002,
|
|
||||||
"4_determinant": 14.913826469999972,
|
|
||||||
"5_markdown_parser": 26.86344819299999,
|
|
||||||
"6_csv_processor": 49.397067434999975,
|
|
||||||
"7_scheduler": 52.55558026300004,
|
|
||||||
"8_json_validator": 19.59284627099999,
|
|
||||||
"9_stream_visualizer": 36.41860035799991
|
|
||||||
},
|
},
|
||||||
"anthropic/claude-sonnet-4.5 TEMP:0.7": {
|
"anthropic/claude-sonnet-4.5 TEMP:0.7": {
|
||||||
"1_dijkstra": 4.49742717099993,
|
"10_scrypt_hash": 2.994402775000024,
|
||||||
"2_convex_hull": 4.570548665000009,
|
"11_geospatial": 3.173007857000048,
|
||||||
"3_lis": 2.375867489000084,
|
"1_dijkstra": 4.721320241999987,
|
||||||
"4_determinant": 1.5574152070001,
|
"2_convex_hull": 4.782739678999991,
|
||||||
"5_markdown_parser": 1.8310976730000694,
|
"3_lis": 2.566319416000042,
|
||||||
"6_csv_processor": 4.079893573999987,
|
"4_determinant": 2.087922611000016,
|
||||||
"7_scheduler": 12.446870330999955,
|
"5_markdown_parser": 2.1563547050000054,
|
||||||
"8_json_validator": 2.8953664760000537,
|
"6_csv_processor": 4.368516923999996,
|
||||||
"9_stream_visualizer": 5.022439357000054
|
"7_scheduler": 10.690530350000016,
|
||||||
|
"8_json_validator": 2.9041606949999696,
|
||||||
|
"9_stream_visualizer": 4.411488517000049
|
||||||
|
},
|
||||||
|
"openai/gpt-5.1-codex": {
|
||||||
|
"10_scrypt_hash": 19.945625629000016,
|
||||||
|
"11_geospatial": 7.752952860000019,
|
||||||
|
"1_dijkstra": 18.825615109000005,
|
||||||
|
"2_convex_hull": 24.519327066999978,
|
||||||
|
"3_lis": 13.449256432000023,
|
||||||
|
"4_determinant": 3.5313777560000306,
|
||||||
|
"5_markdown_parser": 4.871686004000018,
|
||||||
|
"6_csv_processor": 31.731031262999984,
|
||||||
|
"7_scheduler": 91.32724592999998,
|
||||||
|
"8_json_validator": 11.784968072999968,
|
||||||
|
"9_stream_visualizer": 17.32446443499997
|
||||||
},
|
},
|
||||||
"moonshotai/kimi-k2-thinking": {
|
"moonshotai/kimi-k2-thinking": {
|
||||||
"1_dijkstra": 34.72660079400001,
|
"10_scrypt_hash": 54.48318050600006,
|
||||||
"2_convex_hull": 42.421906253,
|
"11_geospatial": 123.94316572699998,
|
||||||
"3_lis": 52.291474074000014,
|
"1_dijkstra": 20.75571812199999,
|
||||||
"4_determinant": 37.432558806,
|
"2_convex_hull": 281.26114928399994,
|
||||||
"5_markdown_parser": 35.437114885999996,
|
"3_lis": 78.25152572599984,
|
||||||
"6_csv_processor": 91.85763109999999,
|
"4_determinant": 67.81623509099987,
|
||||||
"7_scheduler": 184.65146969499997,
|
"5_markdown_parser": 154.2897356459999,
|
||||||
"8_json_validator": 81.96185510699998,
|
"6_csv_processor": 85.1170599280002,
|
||||||
"9_stream_visualizer": 31.27018206100003
|
"7_scheduler": 389.4384002489999,
|
||||||
|
"8_json_validator": 17.501368902000134,
|
||||||
|
"9_stream_visualizer": 98.03424570999994
|
||||||
|
},
|
||||||
|
"google/gemini-2.5-pro": {
|
||||||
|
"10_scrypt_hash": 33.5175050580001,
|
||||||
|
"11_geospatial": 31.009794073000318,
|
||||||
|
"1_dijkstra": 45.908529550999866,
|
||||||
|
"2_convex_hull": 85.32745476700039,
|
||||||
|
"3_lis": 32.75355649500014,
|
||||||
|
"4_determinant": 27.239392394999975,
|
||||||
|
"5_markdown_parser": 27.228569728999865,
|
||||||
|
"6_csv_processor": 69.48256337699992,
|
||||||
|
"7_scheduler": 54.015430929000026,
|
||||||
|
"8_json_validator": 29.125379850999916,
|
||||||
|
"9_stream_visualizer": 36.110917992000005
|
||||||
},
|
},
|
||||||
"openrouter/sherlock-think-alpha": {
|
"openrouter/sherlock-think-alpha": {
|
||||||
"1_dijkstra": 34.012272204,
|
"10_scrypt_hash": 9.486372194000054,
|
||||||
"2_convex_hull": 47.377176928,
|
"11_geospatial": 15.555460506999864,
|
||||||
"3_lis": 18.49963934400001,
|
"1_dijkstra": 21.514197755000087,
|
||||||
"4_determinant": 20.327296623999995,
|
"2_convex_hull": 35.37556943300041,
|
||||||
"5_markdown_parser": 34.069916891999995,
|
"3_lis": 17.00031832400011,
|
||||||
"6_csv_processor": 19.902450771000005,
|
"4_determinant": 8.939955472999719,
|
||||||
"7_scheduler": 82.133111714,
|
"5_markdown_parser": 15.81524256400019,
|
||||||
"8_json_validator": 31.42142794699996,
|
"6_csv_processor": 23.44287434099987,
|
||||||
"9_stream_visualizer": 35.74114956699999
|
"7_scheduler": 68.04158863600017,
|
||||||
},
|
"8_json_validator": 19.484740249000023,
|
||||||
"openrouter/sherlock-dash-alpha": {
|
"9_stream_visualizer": 28.426358369000255
|
||||||
"1_dijkstra": 2.7058817040000003,
|
|
||||||
"2_convex_hull": 3.9343546879999995,
|
|
||||||
"3_lis": 1.2894778209999995,
|
|
||||||
"4_determinant": 0.611742537,
|
|
||||||
"5_markdown_parser": 4.020213965999999,
|
|
||||||
"6_csv_processor": 3.107556931000001,
|
|
||||||
"7_scheduler": 7.806581231,
|
|
||||||
"8_json_validator": 1.4850442509999993,
|
|
||||||
"9_stream_visualizer": 2.5679872179999985
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
async function hashPassword(password, salt) {
|
||||||
|
const { scrypt } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/+esm');
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const hash = await scrypt(
|
||||||
|
enc.encode(password),
|
||||||
|
enc.encode(salt),
|
||||||
|
1024,
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
32
|
||||||
|
);
|
||||||
|
return Array.from(hash, b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
}
|
||||||
|
export default hashPassword;
|
||||||
19
tests/10_scrypt_hash/outputs/google_gemini-2.5-pro.js
Normal file
19
tests/10_scrypt_hash/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const hashPassword = async (password, salt) => {
|
||||||
|
const { default: scrypt } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/scrypt.min.js');
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
const hashBytes = await scrypt(
|
||||||
|
encoder.encode(password),
|
||||||
|
encoder.encode(salt),
|
||||||
|
1024,
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
32
|
||||||
|
);
|
||||||
|
|
||||||
|
return [...hashBytes]
|
||||||
|
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
};
|
||||||
|
export default hashPassword;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const hashPassword = async (pwd, salt) => {
|
||||||
|
const { scrypt } = await import('https://unpkg.com/scrypt-js@3.0.1/scrypt.js')
|
||||||
|
const e = new TextEncoder()
|
||||||
|
const h = await scrypt(e.encode(pwd), e.encode(salt), 1024, 8, 1, 32)
|
||||||
|
return Array.from(h, b => b.toString(16).padStart(2, '0')).join('')
|
||||||
|
}
|
||||||
|
export default hashPassword;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
async function hashPassword(password, salt) {
|
||||||
|
const { scrypt } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/+esm');
|
||||||
|
const e = new TextEncoder();
|
||||||
|
const hash = await scrypt(e.encode(password), e.encode(salt), 1024, 8, 1, 32);
|
||||||
|
return Array.from(hash, b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
}
|
||||||
|
export default hashPassword;
|
||||||
9
tests/10_scrypt_hash/outputs/openai_gpt-5.1-codex.js
Normal file
9
tests/10_scrypt_hash/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const enc = new TextEncoder;
|
||||||
|
let scryptLoader;
|
||||||
|
const hashPassword = async (password, salt) => {
|
||||||
|
scryptLoader ||= import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/+esm').then(({ scrypt }) => scrypt);
|
||||||
|
const scrypt = await scryptLoader;
|
||||||
|
const hash = await scrypt(enc.encode(password), enc.encode(salt), 1024, 8, 1, 32);
|
||||||
|
return [...hash].map(v => v.toString(16).padStart(2, '0')).join('');
|
||||||
|
};
|
||||||
|
export default hashPassword;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
async function hashPassword(password,salt){const{scrypt}=await import('https://cdn.skypack.dev/scrypt-js');const e=new TextEncoder();const h=await scrypt(e.encode(password),e.encode(salt),1024,8,1,32);return Array.from(h).map(b=>b.toString(16).padStart(2,'0')).join('')}
|
||||||
|
export default hashPassword;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
const analyzeGPS = async (readings, boundary) => {
|
||||||
|
const turf = await import('https://esm.sh/@turf/turf@7.1.0');
|
||||||
|
|
||||||
|
const validReadings = readings
|
||||||
|
.filter(r => turf.booleanPointInPolygon(turf.point(r.loc), boundary))
|
||||||
|
.sort((a, b) => a.ts - b.ts);
|
||||||
|
|
||||||
|
if (validReadings.length < 2) return 0;
|
||||||
|
|
||||||
|
const lineString = turf.lineString(validReadings.map(r => r.loc));
|
||||||
|
|
||||||
|
return Math.round(turf.length(lineString) * 100) / 100;
|
||||||
|
};
|
||||||
|
export default analyzeGPS;
|
||||||
18
tests/11_geospatial/outputs/google_gemini-2.5-pro.js
Normal file
18
tests/11_geospatial/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const analyzeGPS = async (readings, boundary) => {
|
||||||
|
const t = await import('https://cdn.jsdelivr.net/npm/@turf/turf');
|
||||||
|
|
||||||
|
const sortedLocations = readings
|
||||||
|
.filter(r => t.booleanPointInPolygon(r.loc, boundary, { ignoreBoundary: true }))
|
||||||
|
.sort((a, b) => a.ts - b.ts)
|
||||||
|
.map(r => r.loc);
|
||||||
|
|
||||||
|
if (sortedLocations.length < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = t.lineString(sortedLocations);
|
||||||
|
const length = t.length(path, { units: 'kilometers' });
|
||||||
|
|
||||||
|
return Math.round(length * 100) / 100;
|
||||||
|
};
|
||||||
|
export default analyzeGPS;
|
||||||
13
tests/11_geospatial/outputs/google_gemini-3-pro-preview.js
Normal file
13
tests/11_geospatial/outputs/google_gemini-3-pro-preview.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const analyzeGPS = async (readings, boundary) => {
|
||||||
|
const { point, booleanPointInPolygon, lineString, length } = await import('https://esm.sh/@turf/turf');
|
||||||
|
|
||||||
|
const path = readings
|
||||||
|
.filter(({ loc }) => booleanPointInPolygon(point(loc), boundary, { ignoreBoundary: true }))
|
||||||
|
.sort((a, b) => a.ts - b.ts)
|
||||||
|
.map(({ loc }) => loc);
|
||||||
|
|
||||||
|
return path.length < 2
|
||||||
|
? 0
|
||||||
|
: +length(lineString(path), { units: 'kilometers' }).toFixed(2);
|
||||||
|
};
|
||||||
|
export default analyzeGPS;
|
||||||
12
tests/11_geospatial/outputs/moonshotai_kimi-k2-thinking.js
Normal file
12
tests/11_geospatial/outputs/moonshotai_kimi-k2-thinking.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
async function analyzeGPS(readings, boundary) {
|
||||||
|
try {
|
||||||
|
if (!Array.isArray(readings) || !boundary) return 0;
|
||||||
|
const turf = await import('https://cdn.skypack.dev/@turf/turf'),
|
||||||
|
v = readings.filter(r => r?.loc && turf.booleanPointInPolygon(r.loc, boundary, {ignoreBoundary: true}))
|
||||||
|
.sort((a, b) => a.ts - b.ts);
|
||||||
|
return v.length < 2 ? 0 : Math.round(turf.length(turf.lineString(v.map(r => r.loc)), {units: 'kilometers'}) * 100) / 100;
|
||||||
|
} catch {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default analyzeGPS;
|
||||||
8
tests/11_geospatial/outputs/openai_gpt-5.1-codex.js
Normal file
8
tests/11_geospatial/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
async function analyzeGPS(readings,boundary){
|
||||||
|
const{booleanPointInPolygon,lineString,length}=await import('https://cdn.jsdelivr.net/npm/@turf/turf@6.5.0/dist/turf.esm.min.js');
|
||||||
|
const pts=readings.filter(r=>booleanPointInPolygon({type:'Point',coordinates:r.loc},boundary,{ignoreBoundary:true})).sort((a,b)=>a.ts-b.ts);
|
||||||
|
if(pts.length<2)return 0;
|
||||||
|
const km=length(lineString(pts.map(r=>r.loc)),{units:'kilometers'});
|
||||||
|
return Math.round(km*100)/100;
|
||||||
|
}
|
||||||
|
export default analyzeGPS;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
async function analyzeGPS(readings,boundary){
|
||||||
|
const{turf}=await import('https://cdn.skypack.dev/@turf/turf');
|
||||||
|
const valid=readings
|
||||||
|
.filter(({loc})=>turf.booleanPointInPolygon([loc[0],loc[1]],boundary,{ignoreBoundary:true}))
|
||||||
|
.sort((a,b)=>a.ts-b.ts);
|
||||||
|
return valid.length<2?0:+turf.length(turf.lineString(valid.map(({loc})=>loc)),{units:'kilometers'}).toFixed(2);
|
||||||
|
}
|
||||||
|
export default analyzeGPS;
|
||||||
@@ -1,24 +1,27 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
async function findShortestPath(graph, start, end) {
|
||||||
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/+esm');
|
const { default: PriorityQueue } = await import('https://cdn.skypack.dev/js-priority-queue');
|
||||||
|
|
||||||
const distances = Object.keys(graph).reduce((acc, node) => ({ ...acc, [node]: Infinity }), {});
|
|
||||||
distances[start] = 0;
|
|
||||||
|
|
||||||
|
const distances = {};
|
||||||
|
const visited = new Set();
|
||||||
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
||||||
|
|
||||||
|
for (const node in graph) distances[node] = Infinity;
|
||||||
|
distances[start] = 0;
|
||||||
pq.queue({ node: start, dist: 0 });
|
pq.queue({ node: start, dist: 0 });
|
||||||
|
|
||||||
const visited = new Set();
|
while (pq.length) {
|
||||||
|
|
||||||
while (pq.length > 0) {
|
|
||||||
const { node, dist } = pq.dequeue();
|
const { node, dist } = pq.dequeue();
|
||||||
|
|
||||||
if (visited.has(node)) continue;
|
if (visited.has(node)) continue;
|
||||||
visited.add(node);
|
|
||||||
|
|
||||||
if (node === end) return dist;
|
if (node === end) return dist;
|
||||||
|
|
||||||
for (const [neighbor, weight] of Object.entries(graph[node] || {})) {
|
visited.add(node);
|
||||||
const newDist = dist + weight;
|
|
||||||
|
for (const neighbor in graph[node]) {
|
||||||
|
if (visited.has(neighbor)) continue;
|
||||||
|
|
||||||
|
const newDist = dist + graph[node][neighbor];
|
||||||
|
|
||||||
if (newDist < distances[neighbor]) {
|
if (newDist < distances[neighbor]) {
|
||||||
distances[neighbor] = newDist;
|
distances[neighbor] = newDist;
|
||||||
pq.queue({ node: neighbor, dist: newDist });
|
pq.queue({ node: neighbor, dist: newDist });
|
||||||
@@ -26,6 +29,6 @@ async function findShortestPath(graph, start, end) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return distances[end];
|
return Infinity;
|
||||||
}
|
}
|
||||||
export default findShortestPath;
|
export default findShortestPath;
|
||||||
@@ -1,36 +1,34 @@
|
|||||||
const findShortestPath = async (graph, startNode, endNode) => {
|
const findShortestPath = async (graph, start, end) => {
|
||||||
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/priority-queue.min.js');
|
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/priority-queue.min.js');
|
||||||
|
|
||||||
const distances = Object.fromEntries(
|
const dists = Object.fromEntries(
|
||||||
Object.keys(graph).map(node => [node, 1 / 0])
|
Object.keys(graph).map(node => [node, Infinity])
|
||||||
);
|
);
|
||||||
distances[startNode] = 0;
|
|
||||||
|
|
||||||
const pq = new PriorityQueue({
|
const pq = new PriorityQueue({ comparator: (a, b) => a.priority - b.priority });
|
||||||
comparator: (a, b) => a.priority - b.priority
|
|
||||||
});
|
dists[start] = 0;
|
||||||
pq.queue({ node: startNode, priority: 0 });
|
pq.queue({ node: start, priority: 0 });
|
||||||
|
|
||||||
while (pq.length) {
|
while (pq.length) {
|
||||||
const { node: u, priority: dist } = pq.dequeue();
|
const { node: u, priority: currentDist } = pq.dequeue();
|
||||||
|
|
||||||
if (dist > distances[u]) {
|
if (currentDist > dists[u]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u === endNode) {
|
if (u === end) break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [v, weight] of Object.entries(graph[u] ?? {})) {
|
for (const [v, weight] of Object.entries(graph[u])) {
|
||||||
const newDist = distances[u] + weight;
|
const distThroughU = dists[u] + weight;
|
||||||
if (newDist < distances[v]) {
|
|
||||||
distances[v] = newDist;
|
if (distThroughU < dists[v]) {
|
||||||
pq.queue({ node: v, priority: newDist });
|
dists[v] = distThroughU;
|
||||||
|
pq.queue({ node: v, priority: distThroughU });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return distances[endNode];
|
return dists[end];
|
||||||
};
|
};
|
||||||
export default findShortestPath;
|
export default findShortestPath;
|
||||||
19
tests/1_dijkstra/outputs/google_gemini-3-pro-preview.js
Normal file
19
tests/1_dijkstra/outputs/google_gemini-3-pro-preview.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const findShortestPath = async (g, s, e) => {
|
||||||
|
const { default: P } = await import('https://cdn.skypack.dev/js-priority-queue')
|
||||||
|
const d = { [s]: 0 }, q = new P({ comparator: (a, b) => a.w - b.w })
|
||||||
|
q.queue({ w: 0, n: s })
|
||||||
|
while (q.length) {
|
||||||
|
const { w, n } = q.dequeue()
|
||||||
|
if (n === e) return w
|
||||||
|
if (w > (d[n] ?? 1 / 0)) continue
|
||||||
|
for (const v in g[n]) {
|
||||||
|
const t = w + g[n][v]
|
||||||
|
if (t < (d[v] ?? 1 / 0)) {
|
||||||
|
d[v] = t
|
||||||
|
q.queue({ w: t, n: v })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1 / 0
|
||||||
|
}
|
||||||
|
export default findShortestPath;
|
||||||
@@ -1,35 +1,24 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
async function findShortestPath(graph, start, end) {
|
||||||
let PriorityQueue;
|
const { default: Q } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/index.min.js');
|
||||||
try {
|
const dist = { [start]: 0 }, visited = new Set();
|
||||||
const mod = await import('https://esm.sh/js-priority-queue@1.0.0');
|
const pq = new Q({ comparator: (a, b) => dist[a] - dist[b] });
|
||||||
PriorityQueue = mod.default || mod;
|
pq.queue(start);
|
||||||
} catch {
|
|
||||||
throw new Error('Failed to load priority queue library');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(start in graph) || !(end in graph)) return Infinity;
|
|
||||||
if (start === end) return 0;
|
|
||||||
|
|
||||||
const dist = { [start]: 0 };
|
|
||||||
const pq = new PriorityQueue({
|
|
||||||
comparator: (a, b) => a[1] - b[1],
|
|
||||||
initialValues: [[start, 0]]
|
|
||||||
});
|
|
||||||
|
|
||||||
while (pq.length) {
|
while (pq.length) {
|
||||||
const [node, d] = pq.dequeue();
|
const node = pq.dequeue();
|
||||||
if (node === end) return d;
|
if (node === end) return dist[end];
|
||||||
if (d > (dist[node] ?? Infinity)) continue;
|
if (visited.has(node)) continue;
|
||||||
|
visited.add(node);
|
||||||
for (const [neighbor, weight] of Object.entries(graph[node])) {
|
const nodeDist = dist[node];
|
||||||
const newDist = d + weight;
|
for (const [adj, weight] of Object.entries(graph[node] || {})) {
|
||||||
if (newDist < (dist[neighbor] ?? Infinity)) {
|
if (visited.has(adj)) continue;
|
||||||
dist[neighbor] = newDist;
|
const newDist = nodeDist + weight;
|
||||||
pq.queue([neighbor, newDist]);
|
if (newDist < (dist[adj] ?? Infinity)) {
|
||||||
|
dist[adj] = newDist;
|
||||||
|
pq.queue(adj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Infinity;
|
return Infinity;
|
||||||
}
|
}
|
||||||
export default findShortestPath;
|
export default findShortestPath;
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
async function findShortestPath(g,s,e){
|
|
||||||
const {default:Q}=await import('https://cdn.jsdelivr.net/npm/js-priority-queue@latest/dist/priority-queue.min.js')
|
|
||||||
const d={},v={}
|
|
||||||
for(const k in g)d[k]=Infinity
|
|
||||||
d[s]=0
|
|
||||||
const q=new Q({comparator:(a,b)=>a[1]-b[1]})
|
|
||||||
q.queue([s,0])
|
|
||||||
while(q.length){
|
|
||||||
const [n,w]=q.dequeue()
|
|
||||||
if(v[n])continue
|
|
||||||
v[n]=1
|
|
||||||
if(n===e)return w
|
|
||||||
for(const k in g[n]){
|
|
||||||
const nw=w+g[n][k]
|
|
||||||
if(nw<d[k]){
|
|
||||||
d[k]=nw
|
|
||||||
q.queue([k,nw])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Infinity
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
const findShortestPath=async(g,s,t)=>{
|
const src='https://esm.sh/js-priority-queue@1'
|
||||||
if(s===t)return 0
|
let load
|
||||||
const{default:PriorityQueue}=await import('https://cdn.skypack.dev/js-priority-queue')
|
const getPQ=()=>load||(load=import(src).then(m=>m.default))
|
||||||
if(!g[s]||!g[t])return Infinity
|
const findShortestPath=async(g,s,e)=>{
|
||||||
const seen=new Map([[s,0]])
|
const PQ=await getPQ()
|
||||||
const pq=new PriorityQueue({comparator:(a,b)=>a[1]-b[1]})
|
const pq=new PQ({comparator:(a,b)=>a[0]-b[0]})
|
||||||
pq.queue([s,0])
|
const dist=new Map([[s,0]])
|
||||||
|
const done=new Set
|
||||||
|
pq.queue([0,s])
|
||||||
while(pq.length){
|
while(pq.length){
|
||||||
const[n,d]=pq.dequeue()
|
const [d,n]=pq.dequeue()
|
||||||
if(d>(seen.get(n)??Infinity))continue
|
if(done.has(n))continue
|
||||||
if(n===t)return d
|
done.add(n)
|
||||||
const nbrs=g[n]||{}
|
if(n===e)return d
|
||||||
for(const k in nbrs){
|
const edges=g[n]
|
||||||
const nd=d+nbrs[k]
|
if(!edges)continue
|
||||||
if(nd<(seen.get(k)??Infinity)){
|
for(const k in edges){
|
||||||
seen.set(k,nd)
|
const w=d+edges[k]
|
||||||
pq.queue([k,nd])
|
if(w<(dist.get(k)??Infinity)){
|
||||||
|
dist.set(k,w)
|
||||||
|
pq.queue([w,k])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
|
||||||
const { PriorityQueue } = await import('https://cdn.skypack.dev/js-priority-queue');
|
|
||||||
const pq = new PriorityQueue({ comparator: (a, b) => a[1] - b[1] });
|
|
||||||
const dist = {}; Object.keys(graph).forEach(n => dist[n] = Infinity); dist[start] = 0;
|
|
||||||
pq.queue([start, 0]);
|
|
||||||
while (!pq.isEmpty()) {
|
|
||||||
const [u, d] = pq.dequeue();
|
|
||||||
if (u === end) return d;
|
|
||||||
if (d > dist[u]) continue;
|
|
||||||
for (const [v, w] of Object.entries(graph[u] || {})) {
|
|
||||||
const nd = d + w;
|
|
||||||
if (nd < dist[v]) {
|
|
||||||
dist[v] = nd;
|
|
||||||
pq.queue([v, nd]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Infinity;
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
async function findShortestPath(graph,start,end){
|
async function findShortestPath(graph,start,end){
|
||||||
const{PriorityQueue}=await import('https://cdn.skypack.dev/js-priority-queue');
|
const{default:PriorityQueue}=await import('https://cdn.skypack.dev/js-priority-queue');
|
||||||
const dist={};
|
const dist={};
|
||||||
for(let n in graph)dist[n]=Infinity;
|
const pq=new PriorityQueue({comparator:(x,y)=>x.d-y.d});
|
||||||
|
for(const n of Object.keys(graph))dist[n]=Infinity;
|
||||||
dist[start]=0;
|
dist[start]=0;
|
||||||
const pq=new PriorityQueue({comparator:(a,b)=>a.d-b.d});
|
pq.queue({n:start,d:0});
|
||||||
pq.queue({node:start,d:0});
|
|
||||||
while(!pq.isEmpty()){
|
while(!pq.isEmpty()){
|
||||||
const{node,d}=pq.dequeue();
|
const{n:n,d:d}=pq.dequeue();
|
||||||
if(d>dist[node])continue;
|
if(d>dist[n])continue;
|
||||||
if(node===end)return d;
|
if(n===end)return d;
|
||||||
for(let nei in graph[node]){
|
for(let nei in graph[n]){
|
||||||
const alt=d+graph[node][nei];
|
const w=graph[n][nei];
|
||||||
|
const alt=d+w;
|
||||||
if(alt<dist[nei]){
|
if(alt<dist[nei]){
|
||||||
dist[nei]=alt;
|
dist[nei]=alt;
|
||||||
pq.queue({node:nei,d:alt});
|
pq.queue({n:nei,d:alt});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ async function findConvexHull(points) {
|
|||||||
|
|
||||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
||||||
|
|
||||||
const buildHull = pts => {
|
const buildHull = (pts) => {
|
||||||
const hull = [];
|
const hull = [];
|
||||||
for (const p of pts) {
|
for (const p of pts) {
|
||||||
while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
||||||
|
|||||||
@@ -1,29 +1,27 @@
|
|||||||
const findConvexHull = async (points) => {
|
const findConvexHull = async (points) => {
|
||||||
const { sortBy, uniqWith, isEqual } =
|
const _ = await import('https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js');
|
||||||
await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js');
|
|
||||||
|
|
||||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
const uniquePts = _.uniqWith(points, _.isEqual);
|
||||||
|
if (uniquePts.length < 3) {
|
||||||
const pts = sortBy(uniqWith(points, isEqual), ['x', 'y']);
|
return uniquePts;
|
||||||
|
|
||||||
if (pts.length < 3) return pts;
|
|
||||||
|
|
||||||
const lower = [];
|
|
||||||
for (const p of pts) {
|
|
||||||
while (lower.length >= 2 && cross(lower.at(-2), lower.at(-1), p) <= 0) {
|
|
||||||
lower.pop();
|
|
||||||
}
|
|
||||||
lower.push(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const upper = [];
|
const crossProduct = (o, a, b) =>
|
||||||
for (let i = pts.length - 1; i >= 0; i--) {
|
(a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
||||||
const p = pts[i];
|
|
||||||
while (upper.length >= 2 && cross(upper.at(-2), upper.at(-1), p) <= 0) {
|
const sortedPts = _.sortBy(uniquePts, ['x', 'y']);
|
||||||
upper.pop();
|
|
||||||
}
|
const buildHalfHull = (pts) =>
|
||||||
upper.push(p);
|
pts.reduce((hull, p) => {
|
||||||
}
|
while (hull.length >= 2 && crossProduct(hull.at(-2), hull.at(-1), p) <= 0) {
|
||||||
|
hull.pop();
|
||||||
|
}
|
||||||
|
hull.push(p);
|
||||||
|
return hull;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const lower = buildHalfHull(sortedPts);
|
||||||
|
const upper = buildHalfHull([...sortedPts].reverse());
|
||||||
|
|
||||||
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
||||||
};
|
};
|
||||||
|
|||||||
22
tests/2_convex_hull/outputs/google_gemini-3-pro-preview.js
Normal file
22
tests/2_convex_hull/outputs/google_gemini-3-pro-preview.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const findConvexHull = async (pts) => {
|
||||||
|
const { default: _ } = await import('https://cdn.skypack.dev/lodash');
|
||||||
|
|
||||||
|
const P = _.sortBy(_.uniqWith(pts, _.isEqual), ['x', 'y']);
|
||||||
|
const n = P.length, k = [], C = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
||||||
|
|
||||||
|
if (n < 3) return P;
|
||||||
|
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
while (k.length >= 2 && C(k[k.length - 2], k[k.length - 1], P[i]) <= 0) k.pop();
|
||||||
|
k.push(P[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = n - 2, t = k.length + 1; i >= 0; i--) {
|
||||||
|
while (k.length >= t && C(k[k.length - 2], k[k.length - 1], P[i]) <= 0) k.pop();
|
||||||
|
k.push(P[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
k.pop();
|
||||||
|
return k;
|
||||||
|
};
|
||||||
|
export default findConvexHull;
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
async function findConvexHull(points) {
|
const findConvexHull = async (pts) => {
|
||||||
const { default: _ } = await import('https://cdn.jsdelivr.net/npm/lodash@4/esm/lodash.js');
|
const { sortBy, uniqBy } = await import('https://esm.sh/lodash-es@4');
|
||||||
|
const cross = (a, b, c) => (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
|
||||||
if (!points || points.length < 3) return [...(points || [])];
|
const sorted = sortBy(uniqBy(pts, p => `${p.x},${p.y}`), ['x', 'y']);
|
||||||
|
if (sorted.length < 3) return sorted;
|
||||||
const pts = _.sortBy(_.uniqWith(points, (a, b) => a.x === b.x && a.y === b.y), ['x', 'y']);
|
const lower = [], upper = [];
|
||||||
|
for (const p of sorted) {
|
||||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
while (lower.length > 1 && cross(lower[lower.length-2], lower[lower.length-1], p) <= 0) lower.pop();
|
||||||
|
lower.push(p);
|
||||||
const build = arr => {
|
}
|
||||||
const h = [];
|
for (let i = sorted.length - 1; i >= 0; i--) {
|
||||||
for (const p of arr) {
|
const p = sorted[i];
|
||||||
while (h.length >= 2 && cross(h[h.length - 2], h[h.length - 1], p) <= 0) h.pop();
|
while (upper.length > 1 && cross(upper[upper.length-2], upper[upper.length-1], p) <= 0) upper.pop();
|
||||||
h.push(p);
|
upper.push(p);
|
||||||
}
|
}
|
||||||
return h;
|
lower.pop(); upper.pop();
|
||||||
};
|
return [...lower, ...upper];
|
||||||
|
};
|
||||||
const lower = build(pts);
|
|
||||||
const upper = build([...pts].reverse());
|
|
||||||
|
|
||||||
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
export default findConvexHull;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
export async function findConvexHull(p){
|
|
||||||
const _=(await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js')).default
|
|
||||||
p=_.uniqWith(p,_.isEqual)
|
|
||||||
p=_.sortBy(p,['x','y'])
|
|
||||||
const c=(a,b,d)=>(b.x-a.x)*(d.y-a.y)-(b.y-a.y)*(d.x-a.x)
|
|
||||||
const h=[]
|
|
||||||
for(const v of p){
|
|
||||||
while(h.length>1&&c(h[h.length-2],h[h.length-1],v)<=0)h.pop()
|
|
||||||
h.push(v)
|
|
||||||
}
|
|
||||||
const u=[]
|
|
||||||
for(const v of [...p].reverse()){
|
|
||||||
while(u.length>1&&c(u[u.length-2],u[u.length-1],v)<=0)u.pop()
|
|
||||||
u.push(v)
|
|
||||||
}
|
|
||||||
h.pop()
|
|
||||||
u.pop()
|
|
||||||
return [...h,...u]
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
async function findConvexHull(points) {
|
async function findConvexHull(points){
|
||||||
const {sortBy, uniqWith} = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js')
|
const m=await import('https://cdn.skypack.dev/lodash?min')
|
||||||
const ps = uniqWith(sortBy(points.map(({x, y}) => ({x: +x, y: +y})), ['x', 'y']), (a, b) => a.x === b.x && a.y === b.y)
|
const _=m.default||m
|
||||||
if (ps.length < 2) return ps
|
const pts=_.sortBy(_.uniqWith(points,(a,b)=>a.x===b.x&&a.y===b.y),['x','y'])
|
||||||
const c = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
|
if(pts.length<3)return pts.slice()
|
||||||
const lower = []
|
const h=[]
|
||||||
for (const p of ps) {
|
const c=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x)
|
||||||
while (lower.length > 1 && c(lower.at(-2), lower.at(-1), p) <= 0) lower.pop()
|
for(const p of pts){
|
||||||
lower.push(p)
|
while(h.length>1&&c(h.at(-2),h.at(-1),p)<=0)h.pop()
|
||||||
|
h.push(p)
|
||||||
}
|
}
|
||||||
const upper = []
|
const t=h.length+1
|
||||||
for (const p of [...ps].reverse()) {
|
for(let i=pts.length-2;i>=0;i--){
|
||||||
while (upper.length > 1 && c(upper.at(-2), upper.at(-1), p) <= 0) upper.pop()
|
const p=pts[i]
|
||||||
upper.push(p)
|
while(h.length>=t&&c(h.at(-2),h.at(-1),p)<=0)h.pop()
|
||||||
|
h.push(p)
|
||||||
}
|
}
|
||||||
return lower.slice(0, -1).concat(upper.slice(0, -1))
|
h.pop()
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
export default findConvexHull;
|
export default findConvexHull;
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
async function findConvexHull(points) {
|
|
||||||
const {_, default: _} = await import('https://cdn.skypack.dev/lodash-es');
|
|
||||||
const pts = _([...new Set(points.map(p=>`${p.x},${p.y}`))].map(s=>s.split(',').map(Number)).map(([x,y])=>({x,y})));
|
|
||||||
const ccw=(a,b,c)=> (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
|
|
||||||
const build=(pts,low)=> {
|
|
||||||
const hull=[];
|
|
||||||
for(let p of pts){
|
|
||||||
while(hull.length>1&&ccw(hull[hull.length-2],hull[hull.length-1],p)<=0) hull.pop();
|
|
||||||
hull.push(p);
|
|
||||||
}
|
|
||||||
return hull.slice(0,low?hull.length:hull.length-1);
|
|
||||||
};
|
|
||||||
if(pts.length<3) return pts;
|
|
||||||
return [...build(pts.sort((a,b)=>a.x-b.x||a.y-b.y),1),...build(pts.slice().reverse(),0)];
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
@@ -1,19 +1,22 @@
|
|||||||
async function findConvexHull(pts){
|
async function findConvexHull(points){
|
||||||
const _=(await import('https://esm.run/lodash@4.17.21')).default;
|
const{default:L}=await import('https://cdn.skypack.dev/lodash-es');
|
||||||
let p=_.uniqBy(_.sortBy(pts,['x','y']),q=>[q.x,q.y]);
|
const{sortBy,uniqBy}=L;
|
||||||
if(p.length<=2)return p;
|
let p=uniqBy(points,({x,y})=>`${x},${y}`);
|
||||||
const cross=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
|
if(p.length<2)return p;
|
||||||
let lower=[];
|
p=sortBy(p,['x','y']);
|
||||||
for(let q of p){
|
const cross=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
|
||||||
while(lower.length>=2&&cross(lower[lower.length-2],lower[lower.length-1],q)<=0)lower.pop();
|
let l=[];
|
||||||
lower.push(q);
|
for(let i=0;i<p.length;i++){
|
||||||
}
|
let pt=p[i];
|
||||||
let upper=[];
|
while(l.length>=2&&cross(l[l.length-2],l[l.length-1],pt)<=0)l.pop();
|
||||||
let rp=[...p].reverse();
|
l.push(pt);
|
||||||
for(let q of rp){
|
}
|
||||||
while(upper.length>=2&&cross(upper[upper.length-2],upper[upper.length-1],q)<=0)upper.pop();
|
let u=[];
|
||||||
upper.push(q);
|
for(let i=p.length-1;i>=0;i--){
|
||||||
}
|
let pt=p[i];
|
||||||
return lower.slice(0,-1).concat(upper.slice(0,-1));
|
while(u.length>=2&&cross(u[u.length-2],u[u.length-1],pt)<=0)u.pop();
|
||||||
|
u.push(pt);
|
||||||
|
}
|
||||||
|
return[...l.slice(0,-1),...u.slice(0,-1)];
|
||||||
}
|
}
|
||||||
export default findConvexHull;
|
export default findConvexHull;
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
const findLISLength = async (arr) => {
|
async function findLISLength(arr) {
|
||||||
|
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||||
|
|
||||||
if (!arr?.length) return 0;
|
if (!arr?.length) return 0;
|
||||||
|
|
||||||
const { bisectLeft } = await import('https://cdn.skypack.dev/d3-array');
|
|
||||||
const tails = [];
|
const tails = [];
|
||||||
|
|
||||||
for (const num of arr) {
|
for (const num of arr) {
|
||||||
@@ -10,5 +11,5 @@ const findLISLength = async (arr) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return tails.length;
|
return tails.length;
|
||||||
};
|
}
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
const findLISLength = async (nums) => {
|
async function findLISLength(nums) {
|
||||||
if (!nums?.length) {
|
if (!nums?.length) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bisectLeft } = await import('https://esm.sh/d3-array');
|
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||||
|
|
||||||
const tails = [];
|
const sub = [];
|
||||||
|
|
||||||
for (const num of nums) {
|
for (const num of nums) {
|
||||||
tails[bisectLeft(tails, num)] = num;
|
sub[bisectLeft(sub, num)] = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tails.length;
|
return sub.length;
|
||||||
};
|
}
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
7
tests/3_lis/outputs/google_gemini-3-pro-preview.js
Normal file
7
tests/3_lis/outputs/google_gemini-3-pro-preview.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const findLISLength = async arr => {
|
||||||
|
const { bisectLeft } = await import('https://esm.sh/d3-array')
|
||||||
|
const tails = []
|
||||||
|
for (const n of arr) tails[bisectLeft(tails, n)] = n
|
||||||
|
return tails.length
|
||||||
|
}
|
||||||
|
export default findLISLength;
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
async function findLISLength(nums){
|
export const findLISLength = async (arr) => {
|
||||||
const{bisectLeft}=await import('https://cdn.skypack.dev/d3-array');
|
if (!Array.isArray(arr) || !arr.length) return 0;
|
||||||
const tails=[];
|
try {
|
||||||
for(const n of nums)tails[bisectLeft(tails,n)]=n;
|
const { bisectLeft } = await import('https://cdn.skypack.dev/d3-array@3');
|
||||||
return tails.length;
|
const t = [];
|
||||||
}
|
for (const v of arr) t[bisectLeft(t, v)] = v;
|
||||||
|
return t.length;
|
||||||
|
} catch {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
async function findLISLength(a) {
|
|
||||||
const {bisectLeft} = await import("https://cdn.jsdelivr.net/npm/d3-array@3/+esm");
|
|
||||||
const t = [];
|
|
||||||
for (const x of a) {
|
|
||||||
const i = bisectLeft(t, x);
|
|
||||||
t[i] = x;
|
|
||||||
}
|
|
||||||
return t.length;
|
|
||||||
}
|
|
||||||
export default findLISLength;
|
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
async function findLISLength(a){
|
const findLISLength = async nums => {
|
||||||
const {bisectLeft:b}=await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
if (!Array.isArray(nums)) throw new TypeError('Expected array')
|
||||||
const t=[]
|
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
||||||
for(const n of a)t[b(t,n)]=n
|
const tails = []
|
||||||
return t.length
|
for (const v of nums) {
|
||||||
|
const i = bisectLeft(tails, v)
|
||||||
|
i === tails.length ? tails.push(v) : tails[i] = v
|
||||||
|
}
|
||||||
|
return tails.length
|
||||||
}
|
}
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
async function findLISLength(arr) {
|
|
||||||
const {bisectLeft} = await import('https://cdn.skypack.dev/d3-array');
|
|
||||||
if(!arr.length) return 0;
|
|
||||||
const tails = [];
|
|
||||||
for(const x of arr) {
|
|
||||||
const i = bisectLeft(tails, x);
|
|
||||||
if(i === tails.length) tails.push(x);
|
|
||||||
else tails[i] = x;
|
|
||||||
}
|
|
||||||
return tails.length;
|
|
||||||
}
|
|
||||||
export default findLISLength;
|
|
||||||
@@ -1,14 +1,2 @@
|
|||||||
async function findLISLength(arr) {
|
async function findLISLength(arr){const{bisectLeft}=await import('https://cdn.skypack.dev/d3-array');let tails=[];for(const num of arr){const i=bisectLeft(tails,num);i===tails.length?tails.push(num):tails[i]=num;}return tails.length;}
|
||||||
const {bisectLeft} = await import('https://cdn.skypack.dev/d3-array');
|
|
||||||
let tails = [];
|
|
||||||
for (let num of arr) {
|
|
||||||
let i = bisectLeft(tails, num);
|
|
||||||
if (i === tails.length) {
|
|
||||||
tails.push(num);
|
|
||||||
} else {
|
|
||||||
tails[i] = num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tails.length;
|
|
||||||
}
|
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const calculateDeterminant = async (matrix) => {
|
const calculateDeterminant = async (matrix) => {
|
||||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@13.0.0/+esm');
|
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@13.0.0/es/index.js');
|
||||||
return det(matrix);
|
return det(matrix);
|
||||||
};
|
};
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const calculateDeterminant = async m => {
|
||||||
|
const { det } = await import('https://esm.sh/mathjs')
|
||||||
|
return det(m)
|
||||||
|
}
|
||||||
|
export default calculateDeterminant;
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
let math;
|
const URL = 'https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm';
|
||||||
|
let det;
|
||||||
|
|
||||||
async function calculateDeterminant(m) {
|
async function calculateDeterminant(m) {
|
||||||
const n = m?.length;
|
const v = Array.isArray(m) && m.length && m.every(r => Array.isArray(r) && r.length === m.length);
|
||||||
if (!n || m.some(r => !Array.isArray(r) || r.length !== n)) throw new Error('Invalid matrix');
|
if (!v) throw new TypeError('Invalid matrix');
|
||||||
math ||= await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm');
|
|
||||||
return math.det(m);
|
det ||= await import(URL).then(m => m.det).catch(() => { throw new Error('Load failed'); });
|
||||||
|
|
||||||
|
try { return det(m); }
|
||||||
|
catch { throw new Error('Calc failed'); }
|
||||||
}
|
}
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
async function calculateDeterminant(m){
|
|
||||||
const {det}=await import('https://cdn.jsdelivr.net/npm/mathjs@14.0.0/+esm')
|
|
||||||
return det(m)
|
|
||||||
}
|
|
||||||
export default calculateDeterminant;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const calculateDeterminant=async m=>{
|
async function calculateDeterminant(m){
|
||||||
const {det}=await import('https://cdn.skypack.dev/mathjs')
|
const {det}=await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.1/+esm')
|
||||||
return det(m)
|
return det(m)
|
||||||
}
|
}
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const calculateDeterminant=async m=>{
|
|
||||||
const{math}=await import('https://cdn.jsdelivr.net/npm/mathjs@12.4.2/lib/browser/math.js');
|
|
||||||
return math.det(m);
|
|
||||||
};
|
|
||||||
export default calculateDeterminant;
|
|
||||||
@@ -1,5 +1,2 @@
|
|||||||
async function calculateDeterminant(matrix) {
|
async function calculateDeterminant(matrix){const math=await import('https://esm.sh/mathjs');return math.det(matrix);}
|
||||||
const { det } = await import('https://esm.sh/mathjs');
|
|
||||||
return det(matrix);
|
|
||||||
}
|
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const parseMarkdown = async (md) => {
|
const parseMarkdown = async (md) => {
|
||||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11.1.1/+esm');
|
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11/+esm');
|
||||||
return marked.parse(md);
|
return marked.parse(md);
|
||||||
};
|
};
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
const parseMarkdown = async (markdown) => {
|
const parseMarkdown = async (markdownText) => {
|
||||||
const cdnUrl = 'https://cdn.jsdelivr.net/npm/marked@13.0.0/lib/marked.esm.js';
|
const cdnUrl = 'https://cdn.jsdelivr.net/npm/marked@13.0.2/lib/marked.esm.js';
|
||||||
const { marked } = await import(cdnUrl);
|
|
||||||
return marked.parse(markdown ?? '');
|
try {
|
||||||
|
const { marked } = await import(cdnUrl);
|
||||||
|
return marked.parse(markdownText || '');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to parse markdown: ${err}`);
|
||||||
|
return markdownText || '';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const parseMarkdown = async md => {
|
||||||
|
const { parse } = await import('https://cdn.jsdelivr.net/npm/marked@12.0.1/lib/marked.esm.js')
|
||||||
|
return parse(md)
|
||||||
|
}
|
||||||
|
export default parseMarkdown;
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
const parseMarkdown = async m => {
|
async function parseMarkdown(md) {
|
||||||
|
const u = 'https://cdn.jsdelivr.net/npm/marked@9/lib/marked.esm.js';
|
||||||
|
const o = {gfm:true,breaks:true,headerIds:false,mangle:false};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {marked} = await import('https://esm.sh/marked');
|
const {marked} = await import(u);
|
||||||
return marked.parse(m);
|
marked.setOptions(o);
|
||||||
} catch (e) {
|
return marked.parse(md);
|
||||||
return `<pre>${e.message}</pre>`;
|
} catch(e) {
|
||||||
|
return `<p>${e.message}</p>`;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export async function parseMarkdown(m){
|
|
||||||
const {marked}=await import('https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js')
|
|
||||||
return marked.parse(m)
|
|
||||||
}
|
|
||||||
export default parseMarkdown;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const parseMarkdown=async(md='')=>{
|
const parseMarkdown=async m=>{
|
||||||
const{marked}=await import('https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js');
|
const {marked}=await import('https://cdn.jsdelivr.net/npm/marked@11.1.0/lib/marked.esm.js')
|
||||||
return marked.parse(md);
|
return marked.parse(m)
|
||||||
};
|
}
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
async function parseMarkdown(md) {
|
|
||||||
const { default: marked } = await import('https://cdn.skypack.dev/marked@14.1.2');
|
|
||||||
marked.use({
|
|
||||||
breaks: true,
|
|
||||||
gfm: true,
|
|
||||||
async tokenize(src) {
|
|
||||||
const tokens = marked.lexer(src);
|
|
||||||
for (const token of tokens) {
|
|
||||||
if (token.type === 'list') {
|
|
||||||
token.ordered = token.ordered || false;
|
|
||||||
token.loose = !!token.items.find(item => item.text.includes('\n'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
},
|
|
||||||
renderer: {
|
|
||||||
heading(text, level) { return `<h${level}>${text}</h${level}>`; },
|
|
||||||
strong(text) { return `<strong>${text}</strong>`; },
|
|
||||||
em(text) { return `<em>${text}</em>`; },
|
|
||||||
codespan(text) { return `<code>${text}</code>`; },
|
|
||||||
link(href, title, text) { return `<a href="${href}"${title ? ` title="${title}"` : ''}>${text}</a>`; },
|
|
||||||
code(code, infostring) {
|
|
||||||
const lang = (infostring || '').match(/^([a-z0-9]+)/i);
|
|
||||||
return `<pre><code class="${lang ? `language-${lang[1]}` : ''}">${code}</code></pre>`;
|
|
||||||
},
|
|
||||||
list(body, ordered) {
|
|
||||||
const type = ordered ? 'ol' : 'ul';
|
|
||||||
return `<${type}>${body}</${type}>`;
|
|
||||||
},
|
|
||||||
blockquote(quote) { return `<blockquote>${quote}</blockquote>`; }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return marked.parse(md);
|
|
||||||
}
|
|
||||||
export default parseMarkdown;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
async function parseMarkdown(text) {
|
async function parseMarkdown(md){
|
||||||
const {marked} = await import('https://unpkg.com/marked@14.1.2?module');
|
const{marked}=await import('https://esm.sh/marked');
|
||||||
return marked(text);
|
return marked.parse(md);
|
||||||
}
|
}
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
const processCSV = async (csv, { filterColumn, filterValue, groupBy, aggregateColumn, operation }) => {
|
const processCSV = async (csvString, { filterColumn, filterValue, groupBy, aggregateColumn, operation }) => {
|
||||||
const { parse } = await import('https://cdn.skypack.dev/csv-parse/sync');
|
const { default: Papa } = await import('https://cdn.jsdelivr.net/npm/[email protected]/+esm');
|
||||||
|
|
||||||
const records = parse(csv, { columns: true, skip_empty_lines: true });
|
const { data } = Papa.parse(csvString, { header: true, skipEmptyLines: true });
|
||||||
|
|
||||||
const filtered = records.filter(r => r[filterColumn] == filterValue);
|
const filtered = data.filter(row => row[filterColumn] == filterValue);
|
||||||
|
|
||||||
const groups = filtered.reduce((acc, row) => {
|
const groups = filtered.reduce((acc, row) => {
|
||||||
const key = row[groupBy];
|
const key = row[groupBy];
|
||||||
@@ -12,12 +12,12 @@ const processCSV = async (csv, { filterColumn, filterValue, groupBy, aggregateCo
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
return Object.entries(groups).map(([key, rows]) => {
|
return Object.entries(groups).map(([key, rows]) => {
|
||||||
const vals = rows.map(r => Number(r[aggregateColumn]) || 0);
|
const values = rows.map(r => +r[aggregateColumn] || 0);
|
||||||
const sum = vals.reduce((a, b) => a + b, 0);
|
const sum = values.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[groupBy]: key,
|
[groupBy]: key,
|
||||||
result: operation === 'sum' ? sum : operation === 'avg' ? sum / vals.length : vals.length
|
result: operation === 'sum' ? sum : operation === 'avg' ? sum / values.length : rows.length
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,39 +1,38 @@
|
|||||||
async function processCSV(csv, {
|
async function processCSV(csvString, config) {
|
||||||
filterColumn,
|
const {
|
||||||
filterValue,
|
filterColumn,
|
||||||
groupBy,
|
filterValue,
|
||||||
aggregateColumn,
|
groupBy,
|
||||||
operation,
|
aggregateColumn,
|
||||||
}) {
|
operation
|
||||||
const { default: Papa } = await import(
|
} = config;
|
||||||
'https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data } = Papa.parse(csv, { header: true, skipEmptyLines: true });
|
const { default: Papa } = await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm');
|
||||||
|
|
||||||
const groups = data.reduce((acc, row) => {
|
const { data } = Papa.parse(csvString, {
|
||||||
if (row[filterColumn] != filterValue) {
|
header: true,
|
||||||
|
skipEmptyLines: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const groups = data
|
||||||
|
.filter(row => row[filterColumn] == filterValue)
|
||||||
|
.reduce((acc, row) => {
|
||||||
|
const key = row[groupBy];
|
||||||
|
const val = Number(row[aggregateColumn]) || 0;
|
||||||
|
|
||||||
|
if (operation === 'avg') {
|
||||||
|
const group = (acc[key] ??= { sum: 0, count: 0 });
|
||||||
|
group.sum += val;
|
||||||
|
group.count++;
|
||||||
|
} else {
|
||||||
|
acc[key] = (acc[key] ?? 0) + (operation === 'sum' ? val : 1);
|
||||||
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}, {});
|
||||||
|
|
||||||
const key = row[groupBy];
|
return Object.entries(groups).map(([groupValue, aggData]) => ({
|
||||||
const stats = acc.get(key) || { sum: 0, count: 0 };
|
[groupBy]: groupValue,
|
||||||
|
result: operation === 'avg' ? aggData.sum / aggData.count : aggData,
|
||||||
stats.sum += Number(row[aggregateColumn]) || 0;
|
|
||||||
stats.count++;
|
|
||||||
|
|
||||||
return acc.set(key, stats);
|
|
||||||
}, new Map());
|
|
||||||
|
|
||||||
const aggregators = {
|
|
||||||
sum: ({ sum }) => sum,
|
|
||||||
avg: ({ sum, count }) => (count ? sum / count : 0),
|
|
||||||
count: ({ count }) => count,
|
|
||||||
};
|
|
||||||
|
|
||||||
return Array.from(groups, ([key, stats]) => ({
|
|
||||||
[groupBy]: key,
|
|
||||||
result: aggregators[operation](stats),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
22
tests/6_csv_processor/outputs/google_gemini-3-pro-preview.js
Normal file
22
tests/6_csv_processor/outputs/google_gemini-3-pro-preview.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const processCSV = async (csv, { filterColumn: fKey, filterValue: fVal, groupBy: gKey, aggregateColumn: aKey, operation: op }) => {
|
||||||
|
const { parse } = await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm')
|
||||||
|
const { data } = parse(csv, { header: true, skipEmptyLines: true })
|
||||||
|
|
||||||
|
const groups = data.reduce((acc, row) => {
|
||||||
|
if (row[fKey] == fVal) {
|
||||||
|
const key = row[gKey]
|
||||||
|
const val = +row[aKey] || 0
|
||||||
|
const rec = acc[key] || { sum: 0, count: 0 }
|
||||||
|
rec.sum += val
|
||||||
|
rec.count++
|
||||||
|
acc[key] = rec
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return Object.entries(groups).map(([key, { sum, count }]) => ({
|
||||||
|
[gKey]: key,
|
||||||
|
result: op === 'count' ? count : op === 'avg' ? sum / count : sum
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
export default processCSV;
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
async function processCSV(csv, cfg) {
|
async function processCSV(csv, { filterColumn:f, filterValue:v, groupBy:g, aggregateColumn:a, operation:o }) {
|
||||||
const [{ default: Papa }, { default: _ }] = await Promise.all([
|
const [{ default: Papa }, { default: _ }] = await Promise.all([
|
||||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
||||||
import('https://cdn.jsdelivr.net/npm/lodash@4.17.21/+esm')
|
import('https://cdn.jsdelivr.net/npm/lodash@4.17.21/+esm')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const filtered = Papa.parse(csv, { header: true, dynamicTyping: true }).data
|
const data = Papa.parse(csv, { header:true, dynamicTyping:true }).data.filter(r => r[f] == v);
|
||||||
.filter(r => r[cfg.filterColumn] == cfg.filterValue);
|
|
||||||
|
|
||||||
return Object.entries(_.groupBy(filtered, cfg.groupBy)).map(([k, v]) => {
|
return Object.entries(_.groupBy(data, g)).map(([k, d]) => {
|
||||||
const sum = v.reduce((s, r) => s + (Number(r[cfg.aggregateColumn]) || 0), 0);
|
const n = d.map(r => (x = Number(r[a]), isNaN(x) ? 0 : x));
|
||||||
return {
|
const s = n.reduce((p, c) => p + c, 0);
|
||||||
[cfg.groupBy]: k,
|
return { [g]: k, result: o === 'count' ? d.length : o === 'avg' ? s / n.length : s };
|
||||||
result: cfg.operation === 'count' ? v.length :
|
|
||||||
cfg.operation === 'avg' ? sum / v.length : sum
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
async function processCSV(csv, cfg) {
|
|
||||||
const p = await import("https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js");
|
|
||||||
const f = p.parse(csv, { header: true }).data.filter(r => r[cfg.filterColumn] == cfg.filterValue);
|
|
||||||
const g = {};
|
|
||||||
for (const r of f) {
|
|
||||||
const k = r[cfg.groupBy];
|
|
||||||
if (!g[k]) g[k] = [];
|
|
||||||
g[k].push(r);
|
|
||||||
}
|
|
||||||
const out = [];
|
|
||||||
for (const k in g) {
|
|
||||||
const rows = g[k];
|
|
||||||
let nums = rows.map(r => +r[cfg.aggregateColumn] || 0);
|
|
||||||
let res = 0;
|
|
||||||
if (cfg.operation === "sum") res = nums.reduce((a, b) => a + b, 0);
|
|
||||||
else if (cfg.operation === "avg") res = nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
||||||
else if (cfg.operation === "count") res = rows.length;
|
|
||||||
out.push({ [cfg.groupBy]: k, result: res });
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
export default processCSV;
|
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
async function processCSV(csv,cfg){
|
async function processCSV(csv,cfg){
|
||||||
const [{Papa},{groupBy:gb}]=await Promise.all([
|
const[{default:Papa},{groupBy}]=await Promise.all([
|
||||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.mjs'),
|
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
||||||
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js')
|
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm')
|
||||||
])
|
]);
|
||||||
const {filterColumn:fc,filterValue:fv,groupBy:gbk,aggregateColumn:ac,operation:op}=cfg
|
const{filterColumn:f,filterValue:v,groupBy:g,aggregateColumn:a,operation:o}=cfg;
|
||||||
const rows=Papa.parse(csv,{header:!0,skipEmptyLines:!0}).data.filter(r=>r&&r[fc]==fv)
|
const rows=Papa.parse(csv,{header:true,skipEmptyLines:true}).data.filter(r=>r[f]==v);
|
||||||
const grouped=gb(rows,r=>r?.[gbk]??'')
|
const groups=groupBy(rows,r=>r[g]);
|
||||||
return Object.entries(grouped).map(([k,arr])=>{
|
return Object.entries(groups).map(([k,list])=>{
|
||||||
const nums=arr.map(r=>{const n=+r[ac];return Number.isFinite(n)?n:0})
|
const nums=list.map(r=>{
|
||||||
const sum=nums.reduce((a,b)=>a+b,0)
|
const n=Number(r[a]);
|
||||||
const result=op==='sum'?sum:op==='avg'?sum/nums.length:arr.length
|
return Number.isFinite(n)?n:0;
|
||||||
return {[gbk]:k,result}
|
});
|
||||||
})
|
const sum=nums.reduce((t,n)=>t+n,0);
|
||||||
|
const count=list.length;
|
||||||
|
const result=o==='sum'?sum:o==='avg'?sum/count:count;
|
||||||
|
return{[g]:k,result};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
async function processCSV(csv,cfg){const{Papa}=await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js');const{groupBy,mean,sum}=await import('https://cdn.jsdelivr.net/npm/@jazzband/lodash-deep@1.1.3/lodash-deep.min.js');const{data,meta}=Papa.parse(csv,{header:!0});const rows=data.filter(r=>r[cfg.filterColumn]==cfg.filterValue);const num=(v)=>+v||0;const grouped=groupBy(rows,r=>r[cfg.groupBy]);return Object.entries(grouped).map(([k,v])=>({ [cfg.groupBy]:k,result:cfg.operation==='sum'?sum(v.map(r=>num(r[cfg.aggregateColumn]))):cfg.operation==='avg'?mean(v.map(r=>num(r[cfg.aggregateColumn]))):v.length}))}
|
|
||||||
export default processCSV;
|
|
||||||
@@ -1,19 +1,27 @@
|
|||||||
async function processCSV(csvString,config){
|
async function processCSV(csv,{filterColumn,filterValue,groupBy,aggregateColumn,operation}){
|
||||||
const{default:Papa}=await import('https://esm.sh/papaparse');
|
const Papa=(await import('https://cdn.jsdelivr.net/npm/papaparse@5/+esm')).default;
|
||||||
const{filterColumn:fc,filterValue:fv,groupBy:gb,aggregateColumn:ac,operation:op}=config;
|
const{data}=Papa.parse(csv,{header:true,skipEmptyLines:true});
|
||||||
const parsed=Papa.parse(csvString,{header:true}).data;
|
const groups=new Map();
|
||||||
const filtered=parsed.filter(r=>r[fc]==fv);
|
for(const row of data){
|
||||||
const groups=filtered.reduce((acc,r)=>{
|
if(row[filterColumn]==filterValue){
|
||||||
const k=r[gb];
|
const key=row[groupBy];
|
||||||
const v=Number(r[ac])||0;
|
const val=Number(row[aggregateColumn])||0;
|
||||||
if(!acc[k])acc[k]={s:0,c:0};
|
const g=groups.get(key)||{sum:0,count:0};
|
||||||
acc[k].s+=v;
|
g.sum+=val;
|
||||||
acc[k].c++;
|
g.count++;
|
||||||
return acc;
|
groups.set(key,g);
|
||||||
},{});
|
}
|
||||||
return Object.entries(groups).map(([k,g])=>{
|
}
|
||||||
const res=op==='count'?g.c:op==='sum'?g.s:g.s/g.c;
|
const result=[];
|
||||||
return{[gb]:k,result:res};
|
for(const[key,g]of groups){
|
||||||
});
|
let res;
|
||||||
|
switch(operation){
|
||||||
|
case'sum':res=g.sum;break;
|
||||||
|
case'avg':res=g.sum/g.count;break;
|
||||||
|
case'count':res=g.count;break;
|
||||||
|
}
|
||||||
|
result.push({[groupBy]:key,result:res});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
@@ -1,81 +1,71 @@
|
|||||||
async function findAvailableSlots(cal1, cal2, constraints) {
|
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||||
const { parse, format, addMinutes, isWithinInterval, isBefore, isAfter, startOfDay, setHours, setMinutes } = await import('https://cdn.jsdelivr.net/npm/date-fns@3.0.0/+esm');
|
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
||||||
|
const { default: isBetween } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/isBetween.js/+esm');
|
||||||
|
const { default: customParseFormat } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/customParseFormat.js/+esm');
|
||||||
|
|
||||||
|
dayjs.extend(isBetween);
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
const { durationMinutes, searchRange, workHours } = constraints;
|
const { durationMinutes, searchRange, workHours } = constraints;
|
||||||
const duration = durationMinutes;
|
const searchStart = dayjs(searchRange.start);
|
||||||
|
const searchEnd = dayjs(searchRange.end);
|
||||||
const parseTime = (dateStr) => parse(dateStr, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx", new Date());
|
|
||||||
const toISO = (date) => format(date, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx");
|
const allBusy = [...cal1, ...cal2]
|
||||||
|
.map(({ start, end }) => ({ start: dayjs(start), end: dayjs(end) }))
|
||||||
const rangeStart = parseTime(searchRange.start);
|
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
||||||
const rangeEnd = parseTime(searchRange.end);
|
|
||||||
|
|
||||||
const [whStart, whEnd] = workHours.start.split(':').map(Number);
|
|
||||||
const [whEndH, whEndM] = workHours.end.split(':').map(Number);
|
|
||||||
|
|
||||||
const allBusy = [...cal1, ...cal2].map(slot => ({
|
|
||||||
start: parseTime(slot.start),
|
|
||||||
end: parseTime(slot.end)
|
|
||||||
})).sort((a, b) => a.start - b.start);
|
|
||||||
|
|
||||||
const merged = [];
|
const merged = [];
|
||||||
for (const slot of allBusy) {
|
for (const slot of allBusy) {
|
||||||
if (!merged.length || isBefore(merged[merged.length - 1].end, slot.start)) {
|
if (!merged.length || merged[merged.length - 1].end.isBefore(slot.start)) {
|
||||||
merged.push({ ...slot });
|
merged.push({ start: slot.start, end: slot.end });
|
||||||
} else {
|
} else {
|
||||||
merged[merged.length - 1].end = isAfter(slot.end, merged[merged.length - 1].end)
|
merged[merged.length - 1].end = dayjs.max(merged[merged.length - 1].end, slot.end);
|
||||||
? slot.end
|
|
||||||
: merged[merged.length - 1].end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const freePeriods = [];
|
const freePeriods = [];
|
||||||
let current = rangeStart;
|
let current = searchStart;
|
||||||
|
|
||||||
for (const busy of merged) {
|
for (const busy of merged) {
|
||||||
if (isBefore(current, busy.start)) {
|
if (current.isBefore(busy.start)) {
|
||||||
freePeriods.push({ start: current, end: busy.start });
|
freePeriods.push({ start: current, end: busy.start });
|
||||||
}
|
}
|
||||||
current = isAfter(busy.end, current) ? busy.end : current;
|
current = dayjs.max(current, busy.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBefore(current, rangeEnd)) {
|
if (current.isBefore(searchEnd)) {
|
||||||
freePeriods.push({ start: current, end: rangeEnd });
|
freePeriods.push({ start: current, end: searchEnd });
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInWorkHours = (date) => {
|
const isWithinWorkHours = (slotStart, slotEnd) => {
|
||||||
const day = startOfDay(date);
|
const date = slotStart.format('YYYY-MM-DD');
|
||||||
const workStart = setMinutes(setHours(day, whStart), whEnd > 0 ? 0 : 0);
|
const workStart = dayjs(`${date} ${workHours.start}`, 'YYYY-MM-DD HH:mm');
|
||||||
const workEnd = setMinutes(setHours(day, whEndH), whEndM);
|
const workEnd = dayjs(`${date} ${workHours.end}`, 'YYYY-MM-DD HH:mm');
|
||||||
return isWithinInterval(date, { start: workStart, end: workEnd });
|
|
||||||
|
return !slotStart.isBefore(workStart) && !slotEnd.isAfter(workEnd);
|
||||||
};
|
};
|
||||||
|
|
||||||
const slots = [];
|
const slots = [];
|
||||||
|
|
||||||
for (const period of freePeriods) {
|
for (const period of freePeriods) {
|
||||||
let slotStart = period.start;
|
let slotStart = period.start;
|
||||||
|
|
||||||
while (true) {
|
while (slotStart.add(durationMinutes, 'minute').isSameOrBefore(period.end)) {
|
||||||
const slotEnd = addMinutes(slotStart, duration);
|
const slotEnd = slotStart.add(durationMinutes, 'minute');
|
||||||
|
|
||||||
if (isAfter(slotEnd, period.end)) break;
|
if (isWithinWorkHours(slotStart, slotEnd) &&
|
||||||
|
!slotStart.isBefore(searchStart) &&
|
||||||
if (isInWorkHours(slotStart) && isInWorkHours(slotEnd)) {
|
!slotEnd.isAfter(searchEnd)) {
|
||||||
const day1 = startOfDay(slotStart);
|
slots.push({
|
||||||
const day2 = startOfDay(slotEnd);
|
start: slotStart.toISOString(),
|
||||||
|
end: slotEnd.toISOString()
|
||||||
if (day1.getTime() === day2.getTime()) {
|
});
|
||||||
slots.push({
|
|
||||||
start: toISO(slotStart),
|
|
||||||
end: toISO(slotEnd)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
slotStart = slotEnd;
|
slotStart = slotEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slots;
|
return slots;
|
||||||
}
|
};
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -1,72 +1,58 @@
|
|||||||
const findAvailableSlots = async (calendar1, calendar2, constraints) => {
|
async function findAvailableSlots(calendar1, calendar2, constraints) {
|
||||||
const [{ default: dayjs }, { default: utc }] = await Promise.all([
|
const {
|
||||||
import('https://cdn.skypack.dev/dayjs'),
|
parseISO, addMinutes, formatISO, max, setHours, setMinutes,
|
||||||
import('https://cdn.skypack.dev/dayjs/plugin/utc.js'),
|
setSeconds, setMilliseconds, startOfDay, endOfDay, eachDayOfInterval,
|
||||||
]);
|
} = await import('https://cdn.jsdelivr.net/npm/date-fns@2/esm/index.js');
|
||||||
dayjs.extend(utc);
|
|
||||||
|
|
||||||
const { durationMinutes, searchRange, workHours } = constraints;
|
const { durationMinutes: duration, searchRange, workHours } = constraints;
|
||||||
|
const searchStart = parseISO(searchRange.start);
|
||||||
const toDayjs = (t) => dayjs.utc(t);
|
const searchEnd = parseISO(searchRange.end);
|
||||||
const toDayjsRange = ({ start, end }) => ({ start: toDayjs(start), end: toDayjs(end) });
|
const [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
||||||
|
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
||||||
|
|
||||||
const search = toDayjsRange(searchRange);
|
const setTime = (date, h, m) =>
|
||||||
const allBusy = [...calendar1, ...calendar2]
|
setMilliseconds(setSeconds(setMinutes(setHours(date, h), m), 0), 0);
|
||||||
.map(toDayjsRange)
|
|
||||||
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
|
||||||
|
|
||||||
const mergedBusy = allBusy.reduce((acc, current) => {
|
const busySlots = [...calendar1, ...calendar2].map(({ start, end }) => ({
|
||||||
const last = acc[acc.length - 1];
|
start: parseISO(start),
|
||||||
if (last && current.start.valueOf() < last.end.valueOf()) {
|
end: parseISO(end),
|
||||||
if (current.end.isAfter(last.end)) {
|
}));
|
||||||
last.end = current.end;
|
|
||||||
}
|
const nonWorkSlots = eachDayOfInterval({ start: searchStart, end: searchEnd })
|
||||||
|
.flatMap(day => [
|
||||||
|
{ start: startOfDay(day), end: setTime(day, workStartH, workStartM) },
|
||||||
|
{ start: setTime(day, workEndH, workEndM), end: endOfDay(day) }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const allUnavailable = [...busySlots, ...nonWorkSlots]
|
||||||
|
.sort((a, b) => a.start - b.start);
|
||||||
|
|
||||||
|
const merged = allUnavailable.reduce((acc, current) => {
|
||||||
|
const last = acc.at(-1);
|
||||||
|
if (last && current.start <= last.end) {
|
||||||
|
last.end = max(last.end, current.end);
|
||||||
} else {
|
} else {
|
||||||
acc.push({ ...current });
|
acc.push({ ...current });
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const boundaryPoints = [
|
|
||||||
search.start,
|
|
||||||
...mergedBusy.flatMap(b => [b.start, b.end]),
|
|
||||||
search.end,
|
|
||||||
];
|
|
||||||
|
|
||||||
const freeGaps = [];
|
|
||||||
for (let i = 0; i < boundaryPoints.length - 1; i += 2) {
|
|
||||||
const start = boundaryPoints[i];
|
|
||||||
const end = boundaryPoints[i + 1];
|
|
||||||
if (end.isAfter(start)) {
|
|
||||||
freeGaps.push({ start, end });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const availableSlots = [];
|
const availableSlots = [];
|
||||||
const [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
let cursor = searchStart;
|
||||||
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
|
||||||
|
|
||||||
for (const gap of freeGaps) {
|
[...merged, { start: searchEnd, end: searchEnd }].forEach(block => {
|
||||||
let cursor = gap.start.startOf('day');
|
let slotStart = cursor;
|
||||||
while (cursor.isBefore(gap.end)) {
|
while (addMinutes(slotStart, duration) <= block.start) {
|
||||||
const workWindowStart = cursor.hour(workStartH).minute(workStartM);
|
const slotEnd = addMinutes(slotStart, duration);
|
||||||
const workWindowEnd = cursor.hour(workEndH).minute(workEndM);
|
availableSlots.push({ start: slotStart, end: slotEnd });
|
||||||
|
slotStart = slotEnd;
|
||||||
let effectiveStart = dayjs.max(gap.start, workWindowStart);
|
|
||||||
const effectiveEnd = dayjs.min(gap.end, workWindowEnd);
|
|
||||||
|
|
||||||
while (effectiveStart.add(durationMinutes, 'minute').valueOf() <= effectiveEnd.valueOf()) {
|
|
||||||
const slotEnd = effectiveStart.add(durationMinutes, 'minute');
|
|
||||||
availableSlots.push({ start: effectiveStart, end: slotEnd });
|
|
||||||
effectiveStart = slotEnd;
|
|
||||||
}
|
|
||||||
cursor = cursor.add(1, 'day');
|
|
||||||
}
|
}
|
||||||
}
|
cursor = max(cursor, block.end);
|
||||||
|
});
|
||||||
|
|
||||||
return availableSlots.map(({ start, end }) => ({
|
return availableSlots.map(({ start, end }) => ({
|
||||||
start: start.toISOString(),
|
start: formatISO(start),
|
||||||
end: end.toISOString(),
|
end: formatISO(end),
|
||||||
}));
|
}));
|
||||||
};
|
}
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
48
tests/7_scheduler/outputs/google_gemini-3-pro-preview.js
Normal file
48
tests/7_scheduler/outputs/google_gemini-3-pro-preview.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const findAvailableSlots = async (calA, calB, { durationMinutes: dm, searchRange: sr, workHours: wh }) => {
|
||||||
|
const { addMinutes } = await import('https://cdn.jsdelivr.net/npm/date-fns@2.30.0/esm/index.js');
|
||||||
|
const T = d => new Date(d).getTime(), I = d => new Date(d).toISOString();
|
||||||
|
|
||||||
|
let busy = [...calA, ...calB]
|
||||||
|
.map(x => ({ s: T(x.start), e: T(x.end) }))
|
||||||
|
.sort((a, b) => a.s - b.s)
|
||||||
|
.reduce((acc, c) => {
|
||||||
|
const l = acc[acc.length - 1];
|
||||||
|
(l && c.s <= l.e) ? l.e = Math.max(l.e, c.e) : acc.push(c);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const slots = [], [sh, sm] = wh.start.split(':'), [eh, em] = wh.end.split(':');
|
||||||
|
let cur = new Date(sr.start); cur.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
while (T(cur) <= T(sr.end)) {
|
||||||
|
const wS = new Date(cur); wS.setHours(sh, sm, 0, 0);
|
||||||
|
const wE = new Date(cur); wE.setHours(eh, em, 0, 0);
|
||||||
|
const winS = Math.max(T(wS), T(sr.start)), winE = Math.min(T(wE), T(sr.end));
|
||||||
|
|
||||||
|
if (winS < winE) {
|
||||||
|
let ptr = winS;
|
||||||
|
const dBusy = busy.filter(b => b.s < winE && b.e > winS);
|
||||||
|
|
||||||
|
for (const b of dBusy) {
|
||||||
|
if (ptr < b.s) {
|
||||||
|
while (T(addMinutes(ptr, dm)) <= b.s) {
|
||||||
|
const nxt = addMinutes(ptr, dm);
|
||||||
|
slots.push({ start: I(ptr), end: I(nxt) });
|
||||||
|
ptr = T(nxt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr = Math.max(ptr, b.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (T(addMinutes(ptr, dm)) <= winE) {
|
||||||
|
const nxt = addMinutes(ptr, dm);
|
||||||
|
slots.push({ start: I(ptr), end: I(nxt) });
|
||||||
|
ptr = T(nxt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = addMinutes(cur, 1440);
|
||||||
|
}
|
||||||
|
|
||||||
|
return slots;
|
||||||
|
};
|
||||||
|
export default findAvailableSlots;
|
||||||
@@ -1,35 +1,57 @@
|
|||||||
export async function findAvailableSlots(a, b, { durationMinutes: d, searchRange: r, workHours: h }) {
|
async function findAvailableSlots(cal1, cal2, constraints) {
|
||||||
const { parseISO, addMinutes, isWithinInterval, compareAsc, startOfDay, setHours, setMinutes } = await import('https://esm.sh/date-fns');
|
const { parseISO, addMinutes, set, max, min, startOfDay } = await import('https://cdn.skypack.dev/date-fns');
|
||||||
|
|
||||||
const toDate = (s) => parseISO(s);
|
const { durationMinutes, searchRange, workHours } = constraints;
|
||||||
const all = [...a, ...b].map(({ start: s, end: e }) => ({ s: toDate(s), e: toDate(e) }));
|
const toDate = d => parseISO(d);
|
||||||
all.sort((a, b) => compareAsc(a.s, b.s));
|
const [whStart, whEnd] = [workHours.start, workHours.end].map(t => t.split(':').map(Number));
|
||||||
|
const durMs = durationMinutes * 60000;
|
||||||
|
const rng = { s: toDate(searchRange.start), e: toDate(searchRange.end) };
|
||||||
|
|
||||||
const busy = [];
|
const merged = [...cal1, ...cal2]
|
||||||
for (const i of all) {
|
.map(({ start, end }) => ({ s: toDate(start), e: toDate(end) }))
|
||||||
if (!busy.length || compareAsc(i.s, busy.at(-1).e) > 0) busy.push({ s: i.s, e: i.e });
|
.sort((a, b) => a.s - b.s)
|
||||||
else if (compareAsc(i.e, busy.at(-1).e) > 0) busy.at(-1).e = i.e;
|
.reduce((acc, slot) => {
|
||||||
|
const last = acc.at(-1);
|
||||||
|
if (!last || slot.s > last.e) acc.push({ ...slot });
|
||||||
|
else last.e = max([last.e, slot.e]);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const gaps = [];
|
||||||
|
let cur = rng.s;
|
||||||
|
for (let i = 0; i <= merged.length; i++) {
|
||||||
|
const slot = merged[i];
|
||||||
|
const end = slot ? slot.s : rng.e;
|
||||||
|
if (end > cur && cur < rng.e) gaps.push({ s: cur, e: end });
|
||||||
|
cur = slot && cur < slot.e ? slot.e : cur;
|
||||||
|
if (cur >= rng.e) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const range = { s: toDate(r.start), e: toDate(r.end) };
|
|
||||||
const [[sh, sm], [eh, em]] = [h.start, h.end].map(t => t.split(':').map(Number));
|
|
||||||
|
|
||||||
const slots = [];
|
const slots = [];
|
||||||
let cur = range.s;
|
for (const { s, e } of gaps) {
|
||||||
|
let day = startOfDay(s);
|
||||||
while (compareAsc(cur, range.e) < 0) {
|
const lastDay = startOfDay(e);
|
||||||
const end = addMinutes(cur, d);
|
|
||||||
if (compareAsc(end, range.e) > 0) break;
|
|
||||||
|
|
||||||
const day = startOfDay(cur);
|
while (day <= lastDay) {
|
||||||
const work = { s: setHours(setMinutes(day, sm), sh), e: setHours(setMinutes(day, em), eh) };
|
const ws = set(day, { hours: whStart[0], minutes: whStart[1], seconds: 0, milliseconds: 0 });
|
||||||
|
const we = set(day, { hours: whEnd[0], minutes: whEnd[1], seconds: 0, milliseconds: 0 });
|
||||||
const inWork = isWithinInterval(cur, work) && isWithinInterval(end, work);
|
|
||||||
const isFree = !busy.some(b => compareAsc(cur, b.e) < 0 && compareAsc(end, b.s) > 0);
|
if (ws < we) {
|
||||||
|
const effS = max([ws, s]);
|
||||||
if (inWork && isFree) slots.push({ start: cur.toISOString(), end: end.toISOString() });
|
const effE = min([we, e]);
|
||||||
|
|
||||||
cur = end;
|
if (effS < effE) {
|
||||||
|
let slotS = effS;
|
||||||
|
while (slotS.getTime() + durMs <= effE.getTime()) {
|
||||||
|
const slotE = addMinutes(slotS, durationMinutes);
|
||||||
|
slots.push({ start: slotS.toISOString(), end: slotE.toISOString() });
|
||||||
|
slotS = slotE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
day = addMinutes(day, 1440);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slots;
|
return slots;
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
async function findAvailableSlots(a,b,c){
|
|
||||||
const f=await import('https://esm.sh/date-fns')
|
|
||||||
const p=f.parseISO,d=f.addMinutes,x=f.max,y=f.min,i=f.isBefore,j=f.isAfter
|
|
||||||
const {durationMinutes:r,searchRange:s,workHours:w}=c
|
|
||||||
const h=t=>t.split(':').map(n=>+n)
|
|
||||||
const [ws,we]=[h(w.start),h(w.end)]
|
|
||||||
const z=(d0,[H,M])=>{d0=new Date(d0);d0.setHours(H,M,0,0);return d0.toISOString()}
|
|
||||||
const S=p(s.start),E=p(s.end)
|
|
||||||
let u=[...a,...b].map(o=>({start:p(o.start),end:p(o.end)}))
|
|
||||||
u=u.sort((A,B)=>A.start-B.start)
|
|
||||||
let m=[]
|
|
||||||
for(let k of u){
|
|
||||||
if(!m.length||k.start>m[m.length-1].end)m.push({start:k.start,end:k.end})
|
|
||||||
else m[m.length-1].end=new Date(Math.max(m[m.length-1].end,k.end))
|
|
||||||
}
|
|
||||||
let q=[]
|
|
||||||
let st=S
|
|
||||||
for(let k of m){
|
|
||||||
if(i(st,k.start)){
|
|
||||||
let fs=x(st,S),fe=y(k.start,E)
|
|
||||||
if(i(fs,fe))q.push({start:fs,end:fe})
|
|
||||||
}
|
|
||||||
st=k.end
|
|
||||||
}
|
|
||||||
if(i(st,E))q.push({start:x(st,S),end:E})
|
|
||||||
let out=[]
|
|
||||||
for(let v of q){
|
|
||||||
let cs=z(v.start,ws),ce=z(v.start,we)
|
|
||||||
cs=p(cs);ce=p(ce)
|
|
||||||
let ss=x(v.start,cs),ee=y(v.end,ce)
|
|
||||||
for(let t=ss;i(d(t,r),ee)||+d(t,r)==+ee;t=d(t,r)){
|
|
||||||
let e=d(t,r)
|
|
||||||
if(!j(t,ss)||i(e,ee))out.push({start:t.toISOString(),end:e.toISOString()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
export default findAvailableSlots;
|
|
||||||
@@ -1,52 +1,44 @@
|
|||||||
let luxon$
|
async function findAvailableSlots(a,b,c){
|
||||||
|
const {DateTime}=await import('https://cdn.skypack.dev/luxon')
|
||||||
const findAvailableSlots = async (calA, calB, cfg) => {
|
const {durationMinutes:d,searchRange:s,workHours:w}=c
|
||||||
const {DateTime, Interval} = await (luxon$ ||= import('https://cdn.skypack.dev/luxon'))
|
const r=[DateTime.fromISO(s.start),DateTime.fromISO(s.end)]
|
||||||
const {durationMinutes: d, searchRange: r, workHours: w} = cfg
|
const hm=q=>q.split(':').map(Number)
|
||||||
const s = DateTime.fromISO(r.start)
|
const [sh,sm]=hm(w.start)
|
||||||
const e = DateTime.fromISO(r.end)
|
const [eh,em]=hm(w.end)
|
||||||
const range = Interval.fromDateTimes(s, e)
|
const clamp=o=>{
|
||||||
const [sh, sm] = w.start.split(':').map(Number)
|
let x=DateTime.fromISO(o.start)
|
||||||
const [eh, em] = w.end.split(':').map(Number)
|
let y=DateTime.fromISO(o.end)
|
||||||
const busy = [...calA, ...calB]
|
if(+y<=+r[0]||+x>=+r[1])return
|
||||||
.map(({start, end}) => ({start: DateTime.fromISO(start), end: DateTime.fromISO(end)}))
|
if(+x<+r[0])x=r[0]
|
||||||
.filter(v => v.end > s && v.start < e)
|
if(+y>+r[1])y=r[1]
|
||||||
.map(v => ({start: v.start < s ? s : v.start, end: v.end > e ? e : v.end}))
|
if(+y<=+x)return
|
||||||
.sort((a, b) => a.start.valueOf() - b.start.valueOf())
|
return{ s:x,e:y}
|
||||||
const merged = []
|
}
|
||||||
for (const slot of busy) {
|
const merged=[]
|
||||||
const last = merged.at(-1)
|
;[...a,...b].map(clamp).filter(Boolean).sort((x,y)=>+x.s-+y.s).forEach(v=>{
|
||||||
if (!last || slot.start > last.end) merged.push({...slot})
|
const last=merged.at(-1)
|
||||||
else if (slot.end > last.end) last.end = slot.end
|
if(!last||+v.s>+last.e)merged.push({s:v.s,e:v.e})
|
||||||
|
else if(+v.e>+last.e)last.e=v.e
|
||||||
|
})
|
||||||
|
const gaps=[]
|
||||||
|
let cur=r[0]
|
||||||
|
merged.forEach(v=>{
|
||||||
|
if(+v.s>+cur)gaps.push({s:cur,e:v.s})
|
||||||
|
if(+v.e>+cur)cur=v.e
|
||||||
|
})
|
||||||
|
if(+cur<+r[1])gaps.push({s:cur,e:r[1]})
|
||||||
|
const slots=[]
|
||||||
|
const step={minutes:d}
|
||||||
|
gaps.forEach(g=>{
|
||||||
|
for(let day=g.s.startOf('day');+day<+g.e;day=day.plus({days:1})){
|
||||||
|
const ws=day.set({hour:sh,minute:sm,second:0,millisecond:0})
|
||||||
|
const we=day.set({hour:eh,minute:em,second:0,millisecond:0})
|
||||||
|
let u=+ws>+g.s?ws:g.s
|
||||||
|
const limit=+we<+g.e?we:g.e
|
||||||
|
if(+limit<=+u)continue
|
||||||
|
for(;+u.plus(step)<=+limit;u=u.plus(step))slots.push({start:u.toISO(),end:u.plus(step).toISO()})
|
||||||
}
|
}
|
||||||
const out = []
|
})
|
||||||
const emit = (from, to) => {
|
return slots
|
||||||
if (!(to > from)) return
|
|
||||||
for (let st = from, en = st.plus({minutes: d}); en <= to; st = en, en = st.plus({minutes: d}))
|
|
||||||
out.push({start: st.toISO(), end: en.toISO()})
|
|
||||||
}
|
|
||||||
let i = 0
|
|
||||||
for (let day = s.startOf('day'); day < e; day = day.plus({days: 1})) {
|
|
||||||
const ws = day.set({hour: sh, minute: sm, second: 0, millisecond: 0})
|
|
||||||
const we = day.set({hour: eh, minute: em, second: 0, millisecond: 0})
|
|
||||||
const block = Interval.fromDateTimes(ws, we).intersection(range)
|
|
||||||
if (!block) continue
|
|
||||||
while (i < merged.length && merged[i].end <= block.start) i++
|
|
||||||
let cursor = block.start
|
|
||||||
for (let j = i; j < merged.length && merged[j].start < block.end; j++) {
|
|
||||||
const bs = merged[j].start > block.start ? merged[j].start : block.start
|
|
||||||
if (bs > cursor) {
|
|
||||||
emit(cursor, bs)
|
|
||||||
cursor = bs
|
|
||||||
}
|
|
||||||
if (merged[j].end > cursor) {
|
|
||||||
const be = merged[j].end < block.end ? merged[j].end : block.end
|
|
||||||
cursor = be
|
|
||||||
}
|
|
||||||
if (cursor >= block.end) break
|
|
||||||
}
|
|
||||||
if (cursor < block.end) emit(cursor, block.end)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
async function findAvailableSlots(cal1, cal2, {durationMinutes, searchRange, workHours}) {
|
|
||||||
const {luxon: {DateTime, Interval}} = await import('https://cdn.skypack.dev/luxon');
|
|
||||||
|
|
||||||
const workStart = DateTime.fromFormat(workHours.start, 'HH:mm');
|
|
||||||
const workEnd = DateTime.fromFormat(workHours.end, 'HH:mm');
|
|
||||||
const searchStart = DateTime.fromISO(searchRange.start);
|
|
||||||
const searchEnd = DateTime.fromISO(searchRange.end);
|
|
||||||
const duration = Duration.fromObject({minutes: durationMinutes});
|
|
||||||
|
|
||||||
const parseCal = cal => cal.map(s => ({
|
|
||||||
start: DateTime.fromISO(s.start),
|
|
||||||
end: DateTime.fromISO(s.end)
|
|
||||||
})).filter(s => s.start < s.end);
|
|
||||||
|
|
||||||
const mergeOverlaps = slots => slots
|
|
||||||
.sort((a,b) => a.start - b.end)
|
|
||||||
.reduce((merged, curr) => {
|
|
||||||
const last = merged[merged.length - 1];
|
|
||||||
if (!last || last.end < curr.start) return [...merged, curr];
|
|
||||||
last.end = Math.max(last.end, curr.end);
|
|
||||||
return merged;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const allBusy = mergeOverlaps([...parseCal(cal1), ...parseCal(cal2)]);
|
|
||||||
const freePeriods = [
|
|
||||||
{start: searchStart, end: allBusy[0]?.start || searchEnd},
|
|
||||||
...allBusy.flatMap((b,i) =>
|
|
||||||
i < allBusy.length - 1 && b.end < allBusy[i+1].start
|
|
||||||
? [{start: b.end, end: allBusy[i+1].start}]
|
|
||||||
: []
|
|
||||||
),
|
|
||||||
...(allBusy.at(-1)?.end && allBusy.at(-1).end < searchEnd
|
|
||||||
? [{start: allBusy.at(-1).end, end: searchEnd}]
|
|
||||||
: [])
|
|
||||||
].filter(p => p.start < p.end);
|
|
||||||
|
|
||||||
const slots = [];
|
|
||||||
for (const period of freePeriods) {
|
|
||||||
const int = Interval.fromDateTimes(period.start, period.end);
|
|
||||||
let curr = int.start;
|
|
||||||
|
|
||||||
while (curr.plus(duration) <= int.end) {
|
|
||||||
const slotStart = curr;
|
|
||||||
const slotEnd = curr.plus(duration);
|
|
||||||
|
|
||||||
const dayStart = slotStart.set({hour: workStart.hour, minute: workStart.minute, second: 0, millisecond: 0});
|
|
||||||
const dayEnd = slotStart.set({hour: workEnd.hour, minute: workEnd.minute, second: 0, millisecond: 0});
|
|
||||||
|
|
||||||
if (Interval.fromDateTimes(slotStart, slotEnd).containedBy(
|
|
||||||
Interval.fromDateTimes(
|
|
||||||
Math.max(slotStart, dayStart),
|
|
||||||
Math.min(slotEnd, dayEnd)
|
|
||||||
)
|
|
||||||
)) {
|
|
||||||
slots.push({
|
|
||||||
start: slotStart.toISO(),
|
|
||||||
end: slotEnd.toISO()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
curr = slotEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slots;
|
|
||||||
}
|
|
||||||
export default findAvailableSlots;
|
|
||||||
@@ -1,77 +1,71 @@
|
|||||||
async function findAvailableSlots(cal1, cal2, constraints) {
|
async function findAvailableSlots(cal1, cal2, cons) {
|
||||||
const { DateTime, Interval } = await import('https://cdn.skypack.dev/luxon');
|
const { DateTime: DT, Interval: IV, Duration: D } = await import('https://esm.sh/luxon@3.4.4');
|
||||||
const { durationMinutes, searchRange, workHours } = constraints;
|
const dur = D.fromObject({ minutes: cons.durationMinutes });
|
||||||
const searchStart = DateTime.fromISO(searchRange.start);
|
const sr = IV.fromISO(`${cons.searchRange.start}/${cons.searchRange.end}`);
|
||||||
const searchEnd = DateTime.fromISO(searchRange.end);
|
const [h1, m1] = cons.workHours.start.split(':').map(Number);
|
||||||
const searchInterval = Interval.fromDateTimes(searchStart, searchEnd);
|
const [h2, m2] = cons.workHours.end.split(':').map(Number);
|
||||||
const zone = searchStart.zone;
|
let busies = [...cal1, ...cal2].map(e => IV.fromISO(`${e.start}/${e.end}`))
|
||||||
const workStart = DateTime.fromFormat(workHours.start, 'HH:mm', { zone });
|
.filter(iv => iv?.overlaps(sr))
|
||||||
const workEnd = DateTime.fromFormat(workHours.end, 'HH:mm', { zone });
|
.map(iv => iv.intersection(sr))
|
||||||
let busies = [];
|
.filter(iv => iv && !iv.isEmpty)
|
||||||
for (const slot of [...cal1, ...cal2]) {
|
.sort((a, b) => a.start.toMillis() - b.start.toMillis());
|
||||||
const iv = Interval.fromISO(`${slot.start}/${slot.end}`);
|
let merged = [];
|
||||||
const i = iv.intersection(searchInterval);
|
for (let iv of busies) {
|
||||||
if (i?.isValid) busies.push(i);
|
if (!merged.length) {
|
||||||
}
|
merged.push(iv);
|
||||||
let nonWork = [];
|
continue;
|
||||||
let currentDay = searchStart.startOf('day');
|
|
||||||
const endDay = searchEnd.endOf('day');
|
|
||||||
while (currentDay <= endDay) {
|
|
||||||
const dayStart = currentDay;
|
|
||||||
const dayEnd = currentDay.plus({ days: 1 });
|
|
||||||
const wStart = dayStart.set({ hour: workStart.hour, minute: workStart.minute, second: 0, millisecond: 0 });
|
|
||||||
const wEnd = dayStart.set({ hour: workEnd.hour, minute: workEnd.minute, second: 0, millisecond: 0 });
|
|
||||||
const blocks = [];
|
|
||||||
if (dayStart < wStart) blocks.push(Interval.fromDateTimes(dayStart, wStart));
|
|
||||||
if (wEnd < dayEnd) blocks.push(Interval.fromDateTimes(wEnd, dayEnd));
|
|
||||||
for (const b of blocks) {
|
|
||||||
const i = b.intersection(searchInterval);
|
|
||||||
if (i?.isValid) nonWork.push(i);
|
|
||||||
}
|
}
|
||||||
currentDay = currentDay.plus({ days: 1 });
|
let last = merged[merged.length - 1];
|
||||||
}
|
if (last.end >= iv.start) {
|
||||||
let allBlocked = [...busies, ...nonWork].sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
const newEnd = last.end.toMillis() > iv.end.toMillis() ? last.end : iv.end;
|
||||||
const mergedBlocked = [];
|
merged[merged.length - 1] = IV.fromDateTimes(last.start, newEnd);
|
||||||
for (const iv of allBlocked) {
|
|
||||||
const last = mergedBlocked.at(-1);
|
|
||||||
if (!last || !last.overlaps(iv)) {
|
|
||||||
mergedBlocked.push(iv);
|
|
||||||
} else {
|
} else {
|
||||||
mergedBlocked[mergedBlocked.length - 1] = last.union(iv);
|
merged.push(iv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const freePeriods = [];
|
let frees = [];
|
||||||
if (mergedBlocked.length === 0) {
|
let prevEnd = sr.start;
|
||||||
freePeriods.push(searchInterval);
|
for (let busy of merged) {
|
||||||
} else {
|
if (prevEnd < busy.start) {
|
||||||
if (searchStart < mergedBlocked[0].start) {
|
frees.push(IV.fromDateTimes(prevEnd, busy.start));
|
||||||
freePeriods.push(Interval.fromDateTimes(searchStart, mergedBlocked[0].start));
|
|
||||||
}
|
}
|
||||||
for (let i = 0; i < mergedBlocked.length - 1; i++) {
|
prevEnd = busy.end;
|
||||||
const gapStart = mergedBlocked[i].end;
|
}
|
||||||
const gapEnd = mergedBlocked[i + 1].start;
|
if (prevEnd < sr.end) {
|
||||||
if (gapStart < gapEnd) {
|
frees.push(IV.fromDateTimes(prevEnd, sr.end));
|
||||||
freePeriods.push(Interval.fromDateTimes(gapStart, gapEnd));
|
}
|
||||||
|
let workFrees = [];
|
||||||
|
for (let free of frees) {
|
||||||
|
let cur = free.start;
|
||||||
|
while (cur < free.end) {
|
||||||
|
let dayS = cur.startOf('day');
|
||||||
|
let dayE = dayS.plus({ days: 1 });
|
||||||
|
let dInt = IV.fromDateTimes(dayS, dayE);
|
||||||
|
let dayFree = free.intersection(dInt);
|
||||||
|
if (dayFree && !dayFree.isEmpty) {
|
||||||
|
let wS = dayS.plus({ hours: h1, minutes: m1 });
|
||||||
|
let wE = dayS.plus({ hours: h2, minutes: m2 });
|
||||||
|
let wInt = IV.fromDateTimes(wS, wE);
|
||||||
|
let wf = dayFree.intersection(wInt);
|
||||||
|
if (wf && !wf.isEmpty) {
|
||||||
|
workFrees.push(wf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
cur = dayE;
|
||||||
if (mergedBlocked.at(-1).end < searchEnd) {
|
|
||||||
freePeriods.push(Interval.fromDateTimes(mergedBlocked.at(-1).end, searchEnd));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const availableSlots = [];
|
let slots = [];
|
||||||
for (const freeIv of freePeriods) {
|
const dMs = dur.toMillis();
|
||||||
if (freeIv.length('milliseconds') < durationMinutes * 60 * 1000) continue;
|
for (let wf of workFrees) {
|
||||||
let current = freeIv.start;
|
let remMs = wf.end.toMillis() - wf.start.toMillis();
|
||||||
while (true) {
|
let n = Math.floor(remMs / dMs);
|
||||||
const slotEnd = current.plus({ minutes: durationMinutes });
|
let fs = wf.start;
|
||||||
if (slotEnd > freeIv.end) break;
|
for (let i = 0; i < n; i++) {
|
||||||
availableSlots.push({
|
let ss = fs.plus(D.fromMillis(i * dMs));
|
||||||
start: current.toISO(),
|
let se = ss.plus(dur);
|
||||||
end: slotEnd.toISO()
|
slots.push({ start: ss.toISO(), end: se.toISO() });
|
||||||
});
|
|
||||||
current = slotEnd;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return availableSlots;
|
return slots;
|
||||||
}
|
}
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -6,12 +6,9 @@ const validateJSON = async (data, schema) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
valid,
|
valid,
|
||||||
errors: valid ? [] : validate.errors.map(e => ({
|
errors: valid ? [] : validate.errors.map(e =>
|
||||||
path: e.instancePath || '/',
|
`${e.instancePath || '/'} ${e.message}`.trim()
|
||||||
message: e.message,
|
)
|
||||||
keyword: e.keyword,
|
|
||||||
params: e.params
|
|
||||||
}))
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
const validateJSON = async (data, schema) => {
|
const validateJSON = async (data, schema) => {
|
||||||
try {
|
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
||||||
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
|
||||||
const validate = new Ajv({ allErrors: true }).compile(schema);
|
|
||||||
const valid = validate(data);
|
|
||||||
|
|
||||||
return {
|
const ajv = new Ajv({ allErrors: true });
|
||||||
valid,
|
const validate = ajv.compile(schema);
|
||||||
errors: valid ? [] : (validate.errors ?? []).map(
|
const valid = validate(data);
|
||||||
({ instancePath, message }) => `${instancePath || 'root'} ${message}`.trim()
|
|
||||||
),
|
const errors = valid
|
||||||
};
|
? []
|
||||||
} catch (e) {
|
: validate.errors.map(
|
||||||
return { valid: false, errors: [e.message] };
|
({ instancePath, message }) => `${instancePath || 'object'} ${message}`.trim()
|
||||||
}
|
);
|
||||||
|
|
||||||
|
return { valid, errors };
|
||||||
};
|
};
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
const validateJSON = async (data, schema) => {
|
||||||
|
const { default: Ajv } = await import('https://esm.sh/ajv@8.17.1')
|
||||||
|
const ajv = new Ajv({ allErrors: true })
|
||||||
|
const validate = ajv.compile(schema)
|
||||||
|
const valid = validate(data)
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid,
|
||||||
|
errors: valid ? [] : validate.errors.map(({ instancePath, message }) =>
|
||||||
|
`${instancePath} ${message}`.trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default validateJSON;
|
||||||
@@ -1,17 +1,25 @@
|
|||||||
const cache = new WeakMap();
|
const validateJSON = async (data, schema) => {
|
||||||
let ajv;
|
|
||||||
|
|
||||||
async function validateJSON(json, schema) {
|
|
||||||
try {
|
try {
|
||||||
if (!ajv) {
|
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
||||||
const { default: Ajv } = await import('https://cdn.jsdelivr.net/npm/ajv@8/dist/ajv.mjs');
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
||||||
ajv = new Ajv({ allErrors: true, strict: false });
|
try {
|
||||||
}
|
const { default: addFormats } = await import('https://esm.sh/ajv-formats@2.1.1');
|
||||||
const validator = cache.get(schema) || (cache.set(schema, ajv.compile(schema)), cache.get(schema));
|
addFormats(ajv);
|
||||||
const valid = validator(json);
|
} catch {}
|
||||||
return { valid, errors: valid ? [] : validator.errors.map(e => `${e.instancePath} ${e.message}`.trim()) };
|
|
||||||
} catch (error) {
|
const validate = ajv.compile(schema);
|
||||||
return { valid: false, errors: [error.message] };
|
const valid = validate(data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid,
|
||||||
|
errors: valid ? [] : validate.errors.map(e => {
|
||||||
|
const path = (e.instancePath || 'root') + (e.keyword === 'required' ? `.${e.params.missingProperty}` : '');
|
||||||
|
const pattern = e.params?.pattern ? ` (${e.params.pattern})` : '';
|
||||||
|
return `${path} ${e.message}${pattern}`.trim();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return { valid: false, errors: [`Validator error: ${e.message}`] };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
async function validateJSON(d,s){
|
|
||||||
const {default:Ajv}=await import('https://cdn.skypack.dev/ajv');
|
|
||||||
const a=new Ajv({allErrors:true});
|
|
||||||
const v=a.compile(s);
|
|
||||||
const ok=v(d);
|
|
||||||
return{valid:ok,errors:ok?[]:(v.errors||[]).map(e=>e.message||'')};
|
|
||||||
}
|
|
||||||
export default validateJSON;
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
export async function validateJSON(data, schema) {
|
const fetchAjv=(()=>{let p;return()=>p??=import('https://cdn.jsdelivr.net/npm/ajv@8/dist/ajv.min.js?module').then(({default:A})=>new A({allErrors:true,strict:false}));})();
|
||||||
const { default: Ajv } = await import('https://esm.sh/ajv@8?bundle');
|
|
||||||
const ajv = new Ajv({ allErrors: true, strict: false });
|
async function validateJSON(data,schema){
|
||||||
const validate = ajv.compile(schema);
|
const ajv=await fetchAjv();
|
||||||
const valid = validate(data);
|
const validate=ajv.compile(schema);
|
||||||
const errors = valid ? [] : (validate.errors || []).map(e => `${e.instancePath || '/'} ${e.message || ''}`.trim());
|
const valid=validate(data);
|
||||||
return { valid, errors };
|
return {valid,errors:valid?[]:validate.errors.map(e=>e.message||'Invalid')};
|
||||||
}
|
}
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
async function validateJSON(data, schema) {
|
|
||||||
const { default: Ajv } = await import('https://cdn.skypack.dev/ajv@8');
|
|
||||||
const ajv = new Ajv({ allErrors: true, verbose: true });
|
|
||||||
const validate = ajv.compile(schema);
|
|
||||||
const valid = validate(data);
|
|
||||||
return {
|
|
||||||
valid,
|
|
||||||
errors: valid ? [] : ajv.errors?.map(e =>
|
|
||||||
`${e.instancePath || 'data'}: ${e.message} (${e.params?.type || e.schema})`
|
|
||||||
) || []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export default validateJSON;
|
|
||||||
@@ -1,20 +1,11 @@
|
|||||||
const getAjv = async () => {
|
async function validateJSON(data, schema) {
|
||||||
if (!getAjv.instance) {
|
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
||||||
const { default: Ajv } = await import('https://cdn.skypack.dev/ajv@8');
|
const ajv = new Ajv({ allErrors: true });
|
||||||
getAjv.instance = new Ajv({ allErrors: true });
|
const validate = ajv.compile(schema);
|
||||||
}
|
const valid = validate(data);
|
||||||
return getAjv.instance;
|
return {
|
||||||
};
|
valid,
|
||||||
|
errors: valid ? [] : validate.errors.map(e => e.message)
|
||||||
const validateJSON = async (data, schema) => {
|
};
|
||||||
try {
|
}
|
||||||
const ajv = await getAjv();
|
|
||||||
const validate = ajv.compile(schema);
|
|
||||||
const valid = validate(data);
|
|
||||||
const errors = valid ? [] : ajv.errorsText(ajv.errors, { separator: '\n' }).split('\n').filter(s => s.trim());
|
|
||||||
return { valid, errors };
|
|
||||||
} catch (e) {
|
|
||||||
return { valid: false, errors: [`Validation error: ${e.message}`] };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -6,9 +6,14 @@ export async function createStreamVisualizer(asyncIterable, options) {
|
|||||||
let prevEma;
|
let prevEma;
|
||||||
|
|
||||||
for await (const { timestamp, value } of asyncIterable) {
|
for await (const { timestamp, value } of asyncIterable) {
|
||||||
const ema = prevEma === undefined ? value : alpha * value + (1 - alpha) * prevEma;
|
const ema = prevEma === undefined
|
||||||
|
? value
|
||||||
|
: alpha * value + (1 - alpha) * prevEma;
|
||||||
|
|
||||||
data.push({ timestamp, value, ema });
|
data.push({ timestamp, value, ema });
|
||||||
|
|
||||||
if (data.length > maxPoints) data.shift();
|
if (data.length > maxPoints) data.shift();
|
||||||
|
|
||||||
prevEma = ema;
|
prevEma = ema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,42 +11,32 @@ async function createStreamVisualizer(asyncIterable, {
|
|||||||
} = await import('d3');
|
} = await import('d3');
|
||||||
|
|
||||||
const data = [];
|
const data = [];
|
||||||
let prevEma = null;
|
let prevEma;
|
||||||
|
|
||||||
for await (const {
|
for await (const { value, timestamp } of asyncIterable) {
|
||||||
timestamp,
|
prevEma = (prevEma === undefined) ?
|
||||||
value
|
|
||||||
} of asyncIterable) {
|
|
||||||
const ema = prevEma === null ?
|
|
||||||
value :
|
value :
|
||||||
alpha * value + (1 - alpha) * prevEma;
|
alpha * value + (1 - alpha) * prevEma;
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
timestamp,
|
timestamp,
|
||||||
value,
|
value,
|
||||||
ema
|
ema: prevEma
|
||||||
});
|
});
|
||||||
prevEma = ema;
|
|
||||||
|
|
||||||
if (data.length > maxPoints) {
|
if (data.length > maxPoints) {
|
||||||
data.shift();
|
data.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.length < 2) {
|
if (!data.length) {
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
path: ''
|
path: ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const x = scaleLinear()
|
const x = scaleLinear([data[0].timestamp, data.at(-1).timestamp], [0, width]);
|
||||||
.domain([data[0].timestamp, data.at(-1).timestamp])
|
const y = scaleLinear(yDomain, [height, 0]);
|
||||||
.range([0, width]);
|
|
||||||
|
|
||||||
const y = scaleLinear()
|
|
||||||
.domain(yDomain)
|
|
||||||
.range([height, 0]);
|
|
||||||
|
|
||||||
const path = line()
|
const path = line()
|
||||||
.x(d => x(d.timestamp))
|
.x(d => x(d.timestamp))
|
||||||
@@ -54,7 +44,7 @@ async function createStreamVisualizer(asyncIterable, {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
path
|
path: path ?? ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export default createStreamVisualizer;
|
export default createStreamVisualizer;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
export const createStreamVisualizer = async (
|
||||||
|
stream,
|
||||||
|
{ maxPoints, alpha, width, height, yDomain }
|
||||||
|
) => {
|
||||||
|
const { scaleLinear, line } = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm');
|
||||||
|
const data = [];
|
||||||
|
let prev = null;
|
||||||
|
|
||||||
|
for await (const { timestamp, value } of stream) {
|
||||||
|
const ema = prev == null ? value : alpha * value + (1 - alpha) * prev;
|
||||||
|
prev = ema;
|
||||||
|
data.push({ timestamp, value, ema });
|
||||||
|
if (data.length > maxPoints) data.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.length) return { data, path: '' };
|
||||||
|
|
||||||
|
const x = scaleLinear()
|
||||||
|
.domain([data[0].timestamp, data[data.length - 1].timestamp])
|
||||||
|
.range([0, width]);
|
||||||
|
|
||||||
|
const y = scaleLinear()
|
||||||
|
.domain(yDomain)
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
const curve = line()
|
||||||
|
.x(d => x(d.timestamp))
|
||||||
|
.y(d => y(d.ema));
|
||||||
|
|
||||||
|
return { data, path: curve(data) };
|
||||||
|
};
|
||||||
|
export default createStreamVisualizer;
|
||||||
@@ -1,9 +1,68 @@
|
|||||||
export async function createStreamVisualizer(i,o){
|
async function createStreamVisualizer(asyncIterable, options) {
|
||||||
const d=await import('d3'),{maxPoints:m,alpha:a,width:w,height:h,yDomain:y}=o,p=[],c=d.scaleLinear,f=d.line;
|
const { maxPoints, alpha, width, height, yDomain } = options;
|
||||||
let e;
|
const d3 = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm');
|
||||||
for await(const{timestamp:t,value:v}of i){e=e===undefined?v:a*v+(1-a)*e;p.push({timestamp:t,value:v,ema:e});p.length>m&&p.shift()}
|
|
||||||
if(!p.length)return{data:[],path:''};
|
if (!Number.isFinite(maxPoints) || maxPoints < 1) {
|
||||||
const x=c().domain([p[0].timestamp,p.at(-1).timestamp]).range([0,w]),z=c().domain(y).range([h,0]),g=f().x(d=>x(d.timestamp)).y(d=>z(d.ema)).curve(d.curveLinear);
|
throw new Error('maxPoints must be a positive integer');
|
||||||
return{data:p,path:g(p)}
|
}
|
||||||
|
if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) {
|
||||||
|
throw new Error('alpha must be between 0 and 1');
|
||||||
|
}
|
||||||
|
if (!Array.isArray(yDomain) || yDomain.length !== 2) {
|
||||||
|
throw new Error('yDomain must be an array of [min, max]');
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(width) || width <= 0) {
|
||||||
|
throw new Error('width must be a positive number');
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(height) || height <= 0) {
|
||||||
|
throw new Error('height must be a positive number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
let prevEma = null;
|
||||||
|
let itemCount = 0;
|
||||||
|
|
||||||
|
for await (const value of asyncIterable) {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
throw new Error('Stream must yield finite numeric values');
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const ema = prevEma === null ? value : alpha * value + (1 - alpha) * prevEma;
|
||||||
|
const point = { timestamp, value, ema };
|
||||||
|
|
||||||
|
if (itemCount < maxPoints) {
|
||||||
|
data.push(point);
|
||||||
|
itemCount++;
|
||||||
|
} else {
|
||||||
|
data.shift();
|
||||||
|
data.push(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevEma = ema;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = '';
|
||||||
|
if (data.length > 0) {
|
||||||
|
const xDomain = data.length > 1 ? [data[0].timestamp, data[data.length - 1].timestamp] : [data[0].timestamp - 1000, data[0].timestamp];
|
||||||
|
|
||||||
|
const xScale = d3.scaleLinear()
|
||||||
|
.domain(xDomain)
|
||||||
|
.range([0, width]);
|
||||||
|
|
||||||
|
const yScale = d3.scaleLinear()
|
||||||
|
.domain(yDomain)
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
const line = d3.line()
|
||||||
|
.x(d => xScale(d.timestamp))
|
||||||
|
.y(d => yScale(d.ema))
|
||||||
|
.curve(d3.curveMonotoneX)
|
||||||
|
.defined(d => Number.isFinite(d.ema));
|
||||||
|
|
||||||
|
path = line(data) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data, path };
|
||||||
}
|
}
|
||||||
export default createStreamVisualizer;
|
export default createStreamVisualizer;
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
export async function createStreamVisualizer(iter, o) {
|
|
||||||
const { maxPoints, alpha, width, height, yDomain } = o
|
|
||||||
const d3 = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm')
|
|
||||||
const data = []
|
|
||||||
let ema
|
|
||||||
for await (const { timestamp, value } of iter) {
|
|
||||||
ema = ema == null ? value : alpha * value + (1 - alpha) * ema
|
|
||||||
data.push({ timestamp, value, ema })
|
|
||||||
if (data.length > maxPoints) data.shift()
|
|
||||||
}
|
|
||||||
const xs = d3.scaleLinear()
|
|
||||||
.domain([data[0].timestamp, data[data.length - 1].timestamp])
|
|
||||||
.range([0, width])
|
|
||||||
const ys = d3.scaleLinear()
|
|
||||||
.domain(yDomain)
|
|
||||||
.range([height, 0])
|
|
||||||
const line = d3.line()
|
|
||||||
.x(d => xs(d.timestamp))
|
|
||||||
.y(d => ys(d.ema))
|
|
||||||
const path = line(data) || ''
|
|
||||||
return { data, path }
|
|
||||||
}
|
|
||||||
export default createStreamVisualizer;
|
|
||||||
@@ -1,23 +1,18 @@
|
|||||||
async function createStreamVisualizer(iterable, {
|
const createStreamVisualizer=async(iter,o)=>{
|
||||||
maxPoints = 100,
|
const{maxPoints:m,alpha:a,width:w,height:h,yDomain:Y}=o
|
||||||
alpha = .5,
|
const data=[]
|
||||||
width = 300,
|
|
||||||
height = 150,
|
|
||||||
yDomain = [0, 1]
|
|
||||||
} = {}) {
|
|
||||||
const { scaleLinear, line } = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm')
|
|
||||||
const data = []
|
|
||||||
let ema
|
let ema
|
||||||
for await (const point of iterable) {
|
for await(const {timestamp:t,value:v} of iter){
|
||||||
const { timestamp, value } = point
|
const d=new Date(t)
|
||||||
ema = ema == null ? value : alpha * value + (1 - alpha) * ema
|
ema=ema==null?v:a*v+(1-a)*ema
|
||||||
data.push({ timestamp, value, ema })
|
data.push({timestamp:d,value:v,ema})
|
||||||
data.length > maxPoints && data.shift()
|
if(data.length>m)data.shift()
|
||||||
}
|
}
|
||||||
if (!data.length) return { data, path: '' }
|
if(!data.length)return{data,path:''}
|
||||||
const x = scaleLinear().domain([data[0].timestamp, data.at(-1).timestamp]).range([0, width])
|
const{scaleTime,scaleLinear,line}=await import('https://cdn.skypack.dev/d3@7?min')
|
||||||
const y = scaleLinear().domain(yDomain).range([height, 0])
|
const x=scaleTime().domain([data[0].timestamp,data[data.length-1].timestamp]).range([0,w])
|
||||||
const path = line().x(d => x(d.timestamp)).y(d => y(d.ema))(data) || ''
|
const y=scaleLinear().domain(Y).range([h,0])
|
||||||
return { data, path }
|
const path=line().x(d=>x(d.timestamp)).y(d=>y(d.ema))(data)||''
|
||||||
|
return{data,path}
|
||||||
}
|
}
|
||||||
export default createStreamVisualizer;
|
export default createStreamVisualizer;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
async function createStreamVisualizer(asyncIterable, { maxPoints = 100, alpha = 0.3, width = 800, height = 400, yDomain = [-1, 1] } = {}) {
|
|
||||||
const { select, scaleLinear, scaleTime, line } = await import('https://cdn.skypack.dev/d3@7');
|
|
||||||
|
|
||||||
let data = [], prevEma;
|
|
||||||
|
|
||||||
for await (const { timestamp, value } of asyncIterable) {
|
|
||||||
const ema = prevEma === undefined ? value : alpha * value + (1 - alpha) * prevEma;
|
|
||||||
prevEma = ema;
|
|
||||||
|
|
||||||
data.push({ timestamp, value, ema });
|
|
||||||
if (data.length > maxPoints) data.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.length) return { data: [], path: '' };
|
|
||||||
|
|
||||||
const x = scaleLinear().domain([data[0].timestamp, data[data.length - 1].timestamp]).range([0, width]);
|
|
||||||
const y = scaleLinear().domain(yDomain).range([height, 0]);
|
|
||||||
|
|
||||||
const lineGen = line()
|
|
||||||
.x(d => x(d.timestamp))
|
|
||||||
.y(d => y(d.ema))
|
|
||||||
.curve(select.curveMonotoneX);
|
|
||||||
|
|
||||||
return { data, path: lineGen(data) };
|
|
||||||
}
|
|
||||||
export default createStreamVisualizer;
|
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
async function createStreamVisualizer(iter,opts){
|
async function createStreamVisualizer(asyncIterable,options){
|
||||||
const{maxPoints:max,alpha,width,height,yDomain}=opts;
|
const{maxPoints,alpha,width,height,yDomain}=options;
|
||||||
let data=[],prev=null;
|
const{scaleLinear:sl,line}=await import('https://cdn.skypack.dev/d3');
|
||||||
for await(const i of iter){
|
let points=[];
|
||||||
const ts=i.timestamp,v=i.value,e=prev==null?v:alpha*v+(1-alpha)*prev;
|
let prevEma=NaN;
|
||||||
prev=e;
|
for await(const{timestamp,value}of asyncIterable){
|
||||||
data.push({timestamp:ts,value:v,ema:e});
|
const ema=isNaN(prevEma)?value:alpha*value+(1-alpha)*prevEma;
|
||||||
if(data.length>max)data.shift();
|
points.push({timestamp,value,ema});
|
||||||
|
prevEma=ema;
|
||||||
|
if(points.length>maxPoints)points.shift();
|
||||||
}
|
}
|
||||||
const{scaleLinear,line}=await import('https://cdn.skypack.dev/d3');
|
if(!points.length)return{data:[],path:''};
|
||||||
if(!data.length)return{data,path:''};
|
const xDomain=[points[0].timestamp,points[points.length-1].timestamp];
|
||||||
const x=scaleLinear().domain([data[0].timestamp,data.at(-1).timestamp]).range([0,width]);
|
const x=sl().domain(xDomain).range([0,width]);
|
||||||
const y=scaleLinear().domain(yDomain).range([height,0]);
|
const y=sl().domain(yDomain).range([height,0]);
|
||||||
return{data,line().x(d=>x(d.timestamp)).y(d=>y(d.ema))(data)};
|
const lineGen=line().x(d=>x(d.timestamp)).y(d=>y(d.ema));
|
||||||
|
return{data:points,path:lineGen(points)};
|
||||||
}
|
}
|
||||||
export default createStreamVisualizer;
|
export default createStreamVisualizer;
|
||||||
Reference in New Issue
Block a user