mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 08:37:56 +00:00
Docs: Update benchmark results
This commit is contained in:
68
results.json
68
results.json
@@ -1,42 +1,46 @@
|
|||||||
{
|
{
|
||||||
"openai/gpt-5.1-codex": {
|
"openai/gpt-5.1-codex": {
|
||||||
"1_dijkstra": null,
|
"1_dijkstra": 15.241279893,
|
||||||
"2_convex_hull": null,
|
"2_convex_hull": 13.933983282,
|
||||||
"3_lis": 4.134811772999999,
|
"3_lis": 3.980165064000001,
|
||||||
"4_determinant": 3.128456531999999,
|
"4_determinant": 6.332550646999996,
|
||||||
"5_markdown_parser": 2.4388916820000004,
|
"5_markdown_parser": 3.2380716380000014,
|
||||||
"6_csv_processor": 43.478935838999995,
|
"6_csv_processor": 26.09038364200001,
|
||||||
"7_scheduler": 84.317196593,
|
"7_scheduler": 130.18603798900003,
|
||||||
"8_json_validator": 6.765057070999989
|
"8_json_validator": 8.38715121099999,
|
||||||
|
"9_stream_visualizer": 12.031535295999987
|
||||||
},
|
},
|
||||||
"openai/gpt-5.1-chat": {
|
"openai/gpt-5.1-chat": {
|
||||||
"1_dijkstra": null,
|
"1_dijkstra": 2.65463601799999,
|
||||||
"2_convex_hull": 4.03333652099999,
|
"2_convex_hull": 3.1764218759999787,
|
||||||
"3_lis": 3.2548832980000006,
|
"3_lis": 2.5808361240000233,
|
||||||
"4_determinant": 3.480435989999998,
|
"4_determinant": 1.857067280999996,
|
||||||
"5_markdown_parser": 12.157845766999992,
|
"5_markdown_parser": 2.1500550130000047,
|
||||||
"6_csv_processor": null,
|
"6_csv_processor": 2.8134082990000024,
|
||||||
"7_scheduler": 3.6146848039999897,
|
"7_scheduler": 6.116577822999999,
|
||||||
"8_json_validator": 3.9886151490000192
|
"8_json_validator": 1.477971265,
|
||||||
|
"9_stream_visualizer": 1.7076946140000073
|
||||||
},
|
},
|
||||||
"google/gemini-2.5-pro": {
|
"google/gemini-2.5-pro": {
|
||||||
"1_dijkstra": 44.864160204000015,
|
"1_dijkstra": 33.989065073000006,
|
||||||
"2_convex_hull": 33.892027145,
|
"2_convex_hull": 56.566219838000016,
|
||||||
"3_lis": 36.906535906,
|
"3_lis": 52.14045403000002,
|
||||||
"4_determinant": 11.497210719000025,
|
"4_determinant": 14.913826469999972,
|
||||||
"5_markdown_parser": 13.62311761299998,
|
"5_markdown_parser": 26.86344819299999,
|
||||||
"6_csv_processor": 42.61058959099999,
|
"6_csv_processor": 49.397067434999975,
|
||||||
"7_scheduler": 12.12732526100002,
|
"7_scheduler": 52.55558026300004,
|
||||||
"8_json_validator": 50.54006011399999
|
"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.8069697199999935,
|
"1_dijkstra": 4.49742717099993,
|
||||||
"2_convex_hull": 4.475060508999974,
|
"2_convex_hull": 4.570548665000009,
|
||||||
"3_lis": 2.835424330000009,
|
"3_lis": 2.375867489000084,
|
||||||
"4_determinant": 2.3172859399999726,
|
"4_determinant": 1.5574152070001,
|
||||||
"5_markdown_parser": 2.080127800999966,
|
"5_markdown_parser": 1.8310976730000694,
|
||||||
"6_csv_processor": 5.218456623000034,
|
"6_csv_processor": 4.079893573999987,
|
||||||
"7_scheduler": 11.33699588100001,
|
"7_scheduler": 12.446870330999955,
|
||||||
"8_json_validator": 3.298284380999976
|
"8_json_validator": 2.8953664760000537,
|
||||||
|
"9_stream_visualizer": 5.022439357000054
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
async function findShortestPath(graph, start, end) {
|
||||||
const { default: PQ } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/+esm');
|
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/+esm');
|
||||||
|
|
||||||
const distances = Object.keys(graph).reduce((acc, node) => ({ ...acc, [node]: Infinity }), {});
|
const distances = Object.keys(graph).reduce((acc, node) => ({ ...acc, [node]: Infinity }), {});
|
||||||
distances[start] = 0;
|
distances[start] = 0;
|
||||||
|
|
||||||
const pq = new PQ({ comparator: (a, b) => a.dist - b.dist });
|
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
||||||
pq.queue({ node: start, dist: 0 });
|
pq.queue({ node: start, dist: 0 });
|
||||||
|
|
||||||
const visited = new Set();
|
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;
|
||||||
|
|||||||
@@ -1,33 +1,36 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
const findShortestPath = async (graph, startNode, endNode) => {
|
||||||
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 distances = Object.fromEntries(
|
||||||
Object.keys(graph).map(node => [node, Infinity])
|
Object.keys(graph).map(node => [node, 1 / 0])
|
||||||
);
|
);
|
||||||
distances[start] = 0;
|
distances[startNode] = 0;
|
||||||
|
|
||||||
const pq = new PriorityQueue({
|
const pq = new PriorityQueue({
|
||||||
comparator: (a, b) => distances[b] - distances[a]
|
comparator: (a, b) => a.priority - b.priority
|
||||||
});
|
});
|
||||||
pq.queue(start);
|
pq.queue({ node: startNode, priority: 0 });
|
||||||
|
|
||||||
while (pq.length) {
|
while (pq.length) {
|
||||||
const currentNode = pq.dequeue();
|
const { node: u, priority: dist } = pq.dequeue();
|
||||||
|
|
||||||
if (currentNode === end) break;
|
if (dist > distances[u]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!graph[currentNode]) continue;
|
if (u === endNode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for (const [neighbor, weight] of Object.entries(graph[currentNode])) {
|
for (const [v, weight] of Object.entries(graph[u] ?? {})) {
|
||||||
const newDist = distances[currentNode] + weight;
|
const newDist = distances[u] + weight;
|
||||||
|
if (newDist < distances[v]) {
|
||||||
if (newDist < distances[neighbor]) {
|
distances[v] = newDist;
|
||||||
distances[neighbor] = newDist;
|
pq.queue({ node: v, priority: newDist });
|
||||||
pq.queue(neighbor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return distances[end];
|
return distances[endNode];
|
||||||
}
|
};
|
||||||
export default findShortestPath;
|
export default findShortestPath;
|
||||||
23
tests/1_dijkstra/outputs/openai_gpt-5.1-chat.js
Normal file
23
tests/1_dijkstra/outputs/openai_gpt-5.1-chat.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
23
tests/1_dijkstra/outputs/openai_gpt-5.1-codex.js
Normal file
23
tests/1_dijkstra/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const findShortestPath=async(g,s,t)=>{
|
||||||
|
if(s===t)return 0
|
||||||
|
const{default:PriorityQueue}=await import('https://cdn.skypack.dev/js-priority-queue')
|
||||||
|
if(!g[s]||!g[t])return Infinity
|
||||||
|
const seen=new Map([[s,0]])
|
||||||
|
const pq=new PriorityQueue({comparator:(a,b)=>a[1]-b[1]})
|
||||||
|
pq.queue([s,0])
|
||||||
|
while(pq.length){
|
||||||
|
const[n,d]=pq.dequeue()
|
||||||
|
if(d>(seen.get(n)??Infinity))continue
|
||||||
|
if(n===t)return d
|
||||||
|
const nbrs=g[n]||{}
|
||||||
|
for(const k in nbrs){
|
||||||
|
const nd=d+nbrs[k]
|
||||||
|
if(nd<(seen.get(k)??Infinity)){
|
||||||
|
seen.set(k,nd)
|
||||||
|
pq.queue([k,nd])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Infinity
|
||||||
|
}
|
||||||
|
export default findShortestPath;
|
||||||
@@ -21,6 +21,9 @@ async function findConvexHull(points) {
|
|||||||
return hull;
|
return hull;
|
||||||
};
|
};
|
||||||
|
|
||||||
return [...buildHull(sorted), ...buildHull([...sorted].reverse())];
|
const lower = buildHull(sorted);
|
||||||
|
const upper = buildHull([...sorted].reverse());
|
||||||
|
|
||||||
|
return [...lower, ...upper];
|
||||||
}
|
}
|
||||||
export default findConvexHull;
|
export default findConvexHull;
|
||||||
@@ -1,28 +1,29 @@
|
|||||||
const findConvexHull = async (points) => {
|
const findConvexHull = async (points) => {
|
||||||
const _ = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js');
|
const { sortBy, uniqWith, isEqual } =
|
||||||
|
await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js');
|
||||||
|
|
||||||
const sortedPoints = _.sortBy(_.uniqWith(points, _.isEqual), ['x', 'y']);
|
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
||||||
|
|
||||||
if (sortedPoints.length < 3) {
|
const pts = sortBy(uniqWith(points, isEqual), ['x', 'y']);
|
||||||
return sortedPoints;
|
|
||||||
|
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 crossProduct = (p1, p2, p3) =>
|
const upper = [];
|
||||||
(p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
|
for (let i = pts.length - 1; i >= 0; i--) {
|
||||||
|
const p = pts[i];
|
||||||
const buildHalfHull = (pointSet) => {
|
while (upper.length >= 2 && cross(upper.at(-2), upper.at(-1), p) <= 0) {
|
||||||
const hull = [];
|
upper.pop();
|
||||||
for (const p of pointSet) {
|
|
||||||
while (hull.length >= 2 && crossProduct(hull.at(-2), hull.at(-1), p) <= 0) {
|
|
||||||
hull.pop();
|
|
||||||
}
|
}
|
||||||
hull.push(p);
|
upper.push(p);
|
||||||
}
|
}
|
||||||
return hull;
|
|
||||||
};
|
|
||||||
|
|
||||||
const lower = buildHalfHull(sortedPoints);
|
|
||||||
const upper = buildHalfHull([...sortedPoints].reverse());
|
|
||||||
|
|
||||||
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
export async function findConvexHull(a){
|
export async function findConvexHull(p){
|
||||||
const _=(await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js')).default
|
const _=(await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js')).default
|
||||||
a = _.uniqWith(a,_.isEqual)
|
p=_.uniqWith(p,_.isEqual)
|
||||||
a = _.sortBy(a,['x','y'])
|
p=_.sortBy(p,['x','y'])
|
||||||
const c=(o,p,q)=>((p.x-o.x)*(q.y-o.y)-(p.y-o.y)*(q.x-o.x))
|
const c=(a,b,d)=>(b.x-a.x)*(d.y-a.y)-(b.y-a.y)*(d.x-a.x)
|
||||||
const b=[]
|
const h=[]
|
||||||
for(const p of a){
|
for(const v of p){
|
||||||
while(b.length>1 && c(b[b.length-2],b[b.length-1],p)<=0) b.pop()
|
while(h.length>1&&c(h[h.length-2],h[h.length-1],v)<=0)h.pop()
|
||||||
b.push(p)
|
h.push(v)
|
||||||
}
|
}
|
||||||
const u=[]
|
const u=[]
|
||||||
for(const p of [...a].reverse()){
|
for(const v of [...p].reverse()){
|
||||||
while(u.length>1 && c(u[u.length-2],u[u.length-1],p)<=0) u.pop()
|
while(u.length>1&&c(u[u.length-2],u[u.length-1],v)<=0)u.pop()
|
||||||
u.push(p)
|
u.push(v)
|
||||||
}
|
}
|
||||||
|
h.pop()
|
||||||
u.pop()
|
u.pop()
|
||||||
b.pop()
|
return [...h,...u]
|
||||||
return [...b,...u]
|
|
||||||
}
|
}
|
||||||
export default findConvexHull;
|
export default findConvexHull;
|
||||||
18
tests/2_convex_hull/outputs/openai_gpt-5.1-codex.js
Normal file
18
tests/2_convex_hull/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
async function findConvexHull(points) {
|
||||||
|
const {sortBy, uniqWith} = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js')
|
||||||
|
const ps = uniqWith(sortBy(points.map(({x, y}) => ({x: +x, y: +y})), ['x', 'y']), (a, b) => a.x === b.x && a.y === b.y)
|
||||||
|
if (ps.length < 2) return ps
|
||||||
|
const c = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
|
||||||
|
const lower = []
|
||||||
|
for (const p of ps) {
|
||||||
|
while (lower.length > 1 && c(lower.at(-2), lower.at(-1), p) <= 0) lower.pop()
|
||||||
|
lower.push(p)
|
||||||
|
}
|
||||||
|
const upper = []
|
||||||
|
for (const p of [...ps].reverse()) {
|
||||||
|
while (upper.length > 1 && c(upper.at(-2), upper.at(-1), p) <= 0) upper.pop()
|
||||||
|
upper.push(p)
|
||||||
|
}
|
||||||
|
return lower.slice(0, -1).concat(upper.slice(0, -1))
|
||||||
|
}
|
||||||
|
export default findConvexHull;
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
const findLISLength = async (arr) => {
|
const findLISLength = async (arr) => {
|
||||||
if (!arr?.length) return 0;
|
if (!arr?.length) return 0;
|
||||||
|
|
||||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
const { bisectLeft } = await import('https://cdn.skypack.dev/d3-array');
|
||||||
|
|
||||||
const tails = [];
|
const tails = [];
|
||||||
|
|
||||||
for (const num of arr) {
|
for (const num of arr) {
|
||||||
const pos = bisectLeft(tails, num);
|
const idx = bisectLeft(tails, num);
|
||||||
tails[pos] = num;
|
tails[idx] = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tails.length;
|
return tails.length;
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
async function findLISLength(nums) {
|
const findLISLength = async (nums) => {
|
||||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
if (!nums?.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { bisectLeft } = await import('https://esm.sh/d3-array');
|
||||||
|
|
||||||
const tails = [];
|
const tails = [];
|
||||||
|
|
||||||
for (const num of nums || []) {
|
for (const num of nums) {
|
||||||
tails[bisectLeft(tails, num)] = num;
|
tails[bisectLeft(tails, num)] = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tails.length;
|
return tails.length;
|
||||||
}
|
};
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
async function findLISLength(a) {
|
async function findLISLength(a) {
|
||||||
const {bisectLeft:b} = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
const {bisectLeft} = await import("https://cdn.jsdelivr.net/npm/d3-array@3/+esm");
|
||||||
const t = []
|
const t = [];
|
||||||
for (const x of a) {
|
for (const x of a) {
|
||||||
const i = b(t, x)
|
const i = bisectLeft(t, x);
|
||||||
t[i] = x
|
t[i] = x;
|
||||||
}
|
}
|
||||||
return t.length
|
return t.length;
|
||||||
}
|
}
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
async function findLISLength(nums){
|
async function findLISLength(a){
|
||||||
const {bisectLeft}=await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
const {bisectLeft:b}=await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
||||||
const tails=[]
|
const t=[]
|
||||||
for(const n of nums){
|
for(const n of a)t[b(t,n)]=n
|
||||||
const i=bisectLeft(tails,n)
|
return t.length
|
||||||
tails[i]=n
|
|
||||||
}
|
|
||||||
return tails.length
|
|
||||||
}
|
}
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
async function calculateDeterminant(matrix) {
|
const calculateDeterminant = async (matrix) => {
|
||||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm');
|
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm');
|
||||||
return det(matrix);
|
return det(matrix);
|
||||||
}
|
};
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const calculateDeterminant = async matrix => {
|
const calculateDeterminant = async (matrix) => {
|
||||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@12.4.2/es/index.js');
|
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@13.0.0/+esm');
|
||||||
return det(matrix);
|
return det(matrix);
|
||||||
};
|
};
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
async function calculateDeterminant(m){
|
async function calculateDeterminant(m){
|
||||||
const {det}=await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm')
|
const {det}=await import('https://cdn.jsdelivr.net/npm/mathjs@14.0.0/+esm')
|
||||||
return det(m)
|
return det(m)
|
||||||
}
|
}
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
async function calculateDeterminant(m){
|
const calculateDeterminant=async m=>{
|
||||||
const {det}=await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm')
|
const {det}=await import('https://cdn.skypack.dev/mathjs')
|
||||||
return det(m)
|
return det(m)
|
||||||
}
|
}
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
async function parseMarkdown(md) {
|
const parseMarkdown = async (md) => {
|
||||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11/+esm');
|
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11.1.1/+esm');
|
||||||
return marked.parse(md);
|
return marked.parse(md);
|
||||||
}
|
};
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
const parseMarkdown = async (md) => {
|
const parseMarkdown = async (markdown) => {
|
||||||
const cdn = 'https://cdn.jsdelivr.net/npm/marked@13.0.0/lib/marked.esm.js';
|
const cdnUrl = 'https://cdn.jsdelivr.net/npm/marked@13.0.0/lib/marked.esm.js';
|
||||||
const { marked } = await import(cdn);
|
const { marked } = await import(cdnUrl);
|
||||||
return marked.parse(md);
|
return marked.parse(markdown ?? '');
|
||||||
};
|
};
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
|
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;
|
export default parseMarkdown;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const parseMarkdown = async m => {
|
const parseMarkdown=async(md='')=>{
|
||||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js')
|
const{marked}=await import('https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js');
|
||||||
return marked.parse(m)
|
return marked.parse(md);
|
||||||
}
|
};
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
@@ -1,32 +1,24 @@
|
|||||||
async function processCSV(csvString, config) {
|
const processCSV = async (csv, { filterColumn, filterValue, groupBy, aggregateColumn, operation }) => {
|
||||||
const { parse } = await import('https://cdn.skypack.dev/papaparse@5.4.1');
|
const { parse } = await import('https://cdn.skypack.dev/csv-parse/sync');
|
||||||
|
|
||||||
const { data } = parse(csvString, { header: true, skipEmptyLines: true });
|
const records = parse(csv, { columns: true, skip_empty_lines: true });
|
||||||
|
|
||||||
const filtered = data.filter(row =>
|
const filtered = records.filter(r => r[filterColumn] == filterValue);
|
||||||
String(row[config.filterColumn]) === String(config.filterValue)
|
|
||||||
);
|
|
||||||
|
|
||||||
const grouped = filtered.reduce((acc, row) => {
|
const groups = filtered.reduce((acc, row) => {
|
||||||
const key = row[config.groupBy];
|
const key = row[groupBy];
|
||||||
if (!acc[key]) acc[key] = [];
|
(acc[key] ??= []).push(row);
|
||||||
acc[key].push(row);
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
return Object.entries(grouped).map(([key, rows]) => {
|
return Object.entries(groups).map(([key, rows]) => {
|
||||||
let result;
|
const vals = rows.map(r => Number(r[aggregateColumn]) || 0);
|
||||||
const vals = rows.map(r => parseFloat(r[config.aggregateColumn]) || 0);
|
const sum = vals.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
if (config.operation === 'sum') {
|
return {
|
||||||
result = vals.reduce((a, b) => a + b, 0);
|
[groupBy]: key,
|
||||||
} else if (config.operation === 'avg') {
|
result: operation === 'sum' ? sum : operation === 'avg' ? sum / vals.length : vals.length
|
||||||
result = vals.reduce((a, b) => a + b, 0) / vals.length;
|
};
|
||||||
} else {
|
|
||||||
result = rows.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { [config.groupBy]: key, result };
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
@@ -1,44 +1,39 @@
|
|||||||
async function processCSV(csvString, config) {
|
async function processCSV(csv, {
|
||||||
const {
|
|
||||||
filterColumn,
|
filterColumn,
|
||||||
filterValue,
|
filterValue,
|
||||||
groupBy,
|
groupBy,
|
||||||
aggregateColumn,
|
aggregateColumn,
|
||||||
operation,
|
operation,
|
||||||
} = config;
|
}) {
|
||||||
|
const { default: Papa } = await import(
|
||||||
|
'https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js'
|
||||||
|
);
|
||||||
|
|
||||||
const { default: Papa } = await import('https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js');
|
const { data } = Papa.parse(csv, { header: true, skipEmptyLines: true });
|
||||||
|
|
||||||
const { data } = Papa.parse(csvString, {
|
const groups = data.reduce((acc, row) => {
|
||||||
header: true,
|
if (row[filterColumn] != filterValue) {
|
||||||
dynamicTyping: true,
|
return acc;
|
||||||
skipEmptyLines: true,
|
}
|
||||||
});
|
|
||||||
|
const key = row[groupBy];
|
||||||
|
const stats = acc.get(key) || { sum: 0, count: 0 };
|
||||||
|
|
||||||
|
stats.sum += Number(row[aggregateColumn]) || 0;
|
||||||
|
stats.count++;
|
||||||
|
|
||||||
|
return acc.set(key, stats);
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
const aggregators = {
|
const aggregators = {
|
||||||
sum: (acc, val) => (acc || 0) + val,
|
sum: ({ sum }) => sum,
|
||||||
count: (acc, _val) => (acc || 0) + 1,
|
avg: ({ sum, count }) => (count ? sum / count : 0),
|
||||||
avg: (acc, val) => {
|
count: ({ count }) => count,
|
||||||
const state = acc || { sum: 0, count: 0 };
|
|
||||||
state.sum += val;
|
|
||||||
state.count += 1;
|
|
||||||
return state;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const groups = data
|
return Array.from(groups, ([key, stats]) => ({
|
||||||
.filter(row => row[filterColumn] == filterValue)
|
|
||||||
.reduce((acc, row) => {
|
|
||||||
const key = row[groupBy];
|
|
||||||
acc[key] = aggregators[operation](acc[key], row[aggregateColumn]);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return Object.entries(groups).map(([key, value]) => ({
|
|
||||||
[groupBy]: key,
|
[groupBy]: key,
|
||||||
result: operation === 'avg'
|
result: aggregators[operation](stats),
|
||||||
? (value.count ? value.sum / value.count : 0)
|
|
||||||
: value,
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
22
tests/6_csv_processor/outputs/openai_gpt-5.1-chat.js
Normal file
22
tests/6_csv_processor/outputs/openai_gpt-5.1-chat.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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,21 +1,16 @@
|
|||||||
const libs=(()=>{let c;return()=>c||(c=Promise.all([
|
async function processCSV(csv,cfg){
|
||||||
import('https://cdn.jsdelivr.net/npm/d3-dsv@3/+esm'),
|
const [{Papa},{groupBy:gb}]=await Promise.all([
|
||||||
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/groupBy.js')
|
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.mjs'),
|
||||||
]).then(([dsv,gb])=>({parse:dsv.csvParse,groupBy:gb.default||gb})));})();
|
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js')
|
||||||
|
])
|
||||||
const toNum=v=>{const n=+v;return Number.isFinite(n)?n:0};
|
const {filterColumn:fc,filterValue:fv,groupBy:gbk,aggregateColumn:ac,operation:op}=cfg
|
||||||
|
const rows=Papa.parse(csv,{header:!0,skipEmptyLines:!0}).data.filter(r=>r&&r[fc]==fv)
|
||||||
async function processCSV(csv,opts){
|
const grouped=gb(rows,r=>r?.[gbk]??'')
|
||||||
const {parse,groupBy}=await libs();
|
return Object.entries(grouped).map(([k,arr])=>{
|
||||||
const rows=parse(csv);
|
const nums=arr.map(r=>{const n=+r[ac];return Number.isFinite(n)?n:0})
|
||||||
const {filterColumn:fc,filterValue:fv,groupBy:gc,aggregateColumn:ac,operation:op}=opts;
|
const sum=nums.reduce((a,b)=>a+b,0)
|
||||||
const pool=groupBy(rows.filter(r=>r[fc]==fv),r=>r[gc]??'');
|
const result=op==='sum'?sum:op==='avg'?sum/nums.length:arr.length
|
||||||
const calc={
|
return {[gbk]:k,result}
|
||||||
sum:x=>x.reduce((t,r)=>t+toNum(r[ac]),0),
|
})
|
||||||
avg:x=>x.length?x.reduce((t,r)=>t+toNum(r[ac]),0)/x.length:0,
|
|
||||||
count:x=>x.length
|
|
||||||
}[op];
|
|
||||||
if(!calc) throw new Error('Unsupported operation');
|
|
||||||
return Object.entries(pool).map(([k,x])=>({[gc]:k,result:calc(x)}));
|
|
||||||
}
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
@@ -1,71 +1,81 @@
|
|||||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
async function findAvailableSlots(cal1, cal2, constraints) {
|
||||||
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
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: utc }, { default: isBetween }] = await Promise.all([
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/utc.js/+esm'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/isBetween.js/+esm')
|
|
||||||
]);
|
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(isBetween);
|
|
||||||
|
|
||||||
const { durationMinutes, searchRange, workHours } = constraints;
|
const { durationMinutes, searchRange, workHours } = constraints;
|
||||||
const [whStart, whEnd] = [workHours.start.split(':'), workHours.end.split(':')];
|
const duration = durationMinutes;
|
||||||
|
|
||||||
const merged = [...cal1, ...cal2]
|
const parseTime = (dateStr) => parse(dateStr, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx", new Date());
|
||||||
.map(s => ({ start: dayjs(s.start), end: dayjs(s.end) }))
|
const toISO = (date) => format(date, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx");
|
||||||
.sort((a, b) => a.start - b.start);
|
|
||||||
|
|
||||||
const busy = merged.reduce((acc, curr) => {
|
const rangeStart = parseTime(searchRange.start);
|
||||||
if (!acc.length || curr.start > acc[acc.length - 1].end) {
|
const rangeEnd = parseTime(searchRange.end);
|
||||||
acc.push(curr);
|
|
||||||
|
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 = [];
|
||||||
|
for (const slot of allBusy) {
|
||||||
|
if (!merged.length || isBefore(merged[merged.length - 1].end, slot.start)) {
|
||||||
|
merged.push({ ...slot });
|
||||||
} else {
|
} else {
|
||||||
acc[acc.length - 1].end = dayjs.max(acc[acc.length - 1].end, curr.end);
|
merged[merged.length - 1].end = isAfter(slot.end, merged[merged.length - 1].end)
|
||||||
|
? slot.end
|
||||||
|
: merged[merged.length - 1].end;
|
||||||
}
|
}
|
||||||
return acc;
|
}
|
||||||
}, []);
|
|
||||||
|
const freePeriods = [];
|
||||||
|
let current = rangeStart;
|
||||||
|
|
||||||
|
for (const busy of merged) {
|
||||||
|
if (isBefore(current, busy.start)) {
|
||||||
|
freePeriods.push({ start: current, end: busy.start });
|
||||||
|
}
|
||||||
|
current = isAfter(busy.end, current) ? busy.end : current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBefore(current, rangeEnd)) {
|
||||||
|
freePeriods.push({ start: current, end: rangeEnd });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInWorkHours = (date) => {
|
||||||
|
const day = startOfDay(date);
|
||||||
|
const workStart = setMinutes(setHours(day, whStart), whEnd > 0 ? 0 : 0);
|
||||||
|
const workEnd = setMinutes(setHours(day, whEndH), whEndM);
|
||||||
|
return isWithinInterval(date, { start: workStart, end: workEnd });
|
||||||
|
};
|
||||||
|
|
||||||
const slots = [];
|
const slots = [];
|
||||||
let current = dayjs(searchRange.start);
|
|
||||||
const rangeEnd = dayjs(searchRange.end);
|
|
||||||
|
|
||||||
while (current < rangeEnd) {
|
for (const period of freePeriods) {
|
||||||
const dayStart = current.hour(+whStart[0]).minute(+whStart[1]).second(0);
|
let slotStart = period.start;
|
||||||
const dayEnd = current.hour(+whEnd[0]).minute(+whEnd[1]).second(0);
|
|
||||||
|
|
||||||
let slotStart = dayStart < current ? current : dayStart;
|
while (true) {
|
||||||
|
const slotEnd = addMinutes(slotStart, duration);
|
||||||
|
|
||||||
for (const b of busy) {
|
if (isAfter(slotEnd, period.end)) break;
|
||||||
if (b.end <= slotStart || b.start >= dayEnd) continue;
|
|
||||||
|
|
||||||
if (b.start > slotStart) {
|
if (isInWorkHours(slotStart) && isInWorkHours(slotEnd)) {
|
||||||
const slotEnd = dayjs.min(b.start, dayEnd);
|
const day1 = startOfDay(slotStart);
|
||||||
let probe = slotStart;
|
const day2 = startOfDay(slotEnd);
|
||||||
|
|
||||||
while (probe.add(durationMinutes, 'minute') <= slotEnd) {
|
if (day1.getTime() === day2.getTime()) {
|
||||||
slots.push({
|
slots.push({
|
||||||
start: probe.toISOString(),
|
start: toISO(slotStart),
|
||||||
end: probe.add(durationMinutes, 'minute').toISOString()
|
end: toISO(slotEnd)
|
||||||
});
|
});
|
||||||
probe = probe.add(durationMinutes, 'minute');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slotStart = dayjs.max(slotStart, b.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slotStart < dayEnd) {
|
|
||||||
let probe = slotStart;
|
|
||||||
while (probe.add(durationMinutes, 'minute') <= dayEnd) {
|
|
||||||
slots.push({
|
|
||||||
start: probe.toISOString(),
|
|
||||||
end: probe.add(durationMinutes, 'minute').toISOString()
|
|
||||||
});
|
|
||||||
probe = probe.add(durationMinutes, 'minute');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current = current.add(1, 'day').startOf('day');
|
slotStart = slotEnd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slots;
|
return slots;
|
||||||
};
|
}
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -1,2 +1,72 @@
|
|||||||
|
const findAvailableSlots = async (calendar1, calendar2, constraints) => {
|
||||||
|
const [{ default: dayjs }, { default: utc }] = await Promise.all([
|
||||||
|
import('https://cdn.skypack.dev/dayjs'),
|
||||||
|
import('https://cdn.skypack.dev/dayjs/plugin/utc.js'),
|
||||||
|
]);
|
||||||
|
dayjs.extend(utc);
|
||||||
|
|
||||||
|
const { durationMinutes, searchRange, workHours } = constraints;
|
||||||
|
|
||||||
|
const toDayjs = (t) => dayjs.utc(t);
|
||||||
|
const toDayjsRange = ({ start, end }) => ({ start: toDayjs(start), end: toDayjs(end) });
|
||||||
|
|
||||||
|
const search = toDayjsRange(searchRange);
|
||||||
|
const allBusy = [...calendar1, ...calendar2]
|
||||||
|
.map(toDayjsRange)
|
||||||
|
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
||||||
|
|
||||||
|
const mergedBusy = allBusy.reduce((acc, current) => {
|
||||||
|
const last = acc[acc.length - 1];
|
||||||
|
if (last && current.start.valueOf() < last.end.valueOf()) {
|
||||||
|
if (current.end.isAfter(last.end)) {
|
||||||
|
last.end = current.end;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push({ ...current });
|
||||||
|
}
|
||||||
|
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 [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
||||||
|
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
||||||
|
|
||||||
|
for (const gap of freeGaps) {
|
||||||
|
let cursor = gap.start.startOf('day');
|
||||||
|
while (cursor.isBefore(gap.end)) {
|
||||||
|
const workWindowStart = cursor.hour(workStartH).minute(workStartM);
|
||||||
|
const workWindowEnd = cursor.hour(workEndH).minute(workEndM);
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableSlots.map(({ start, end }) => ({
|
||||||
|
start: start.toISOString(),
|
||||||
|
end: end.toISOString(),
|
||||||
|
}));
|
||||||
|
};
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -1,48 +1,36 @@
|
|||||||
async function findAvailableSlots(a,b,c){
|
async function findAvailableSlots(a,b,c){
|
||||||
const d=await import('https://esm.sh/date-fns@3.6.0')
|
const f=await import('https://esm.sh/date-fns')
|
||||||
const {parseISO,formatISO,max,min,addMinutes,isBefore}=d
|
const p=f.parseISO,d=f.addMinutes,x=f.max,y=f.min,i=f.isBefore,j=f.isAfter
|
||||||
const p=t=>parseISO(t)
|
const {durationMinutes:r,searchRange:s,workHours:w}=c
|
||||||
const wh=(dt,s)=>{
|
const h=t=>t.split(':').map(n=>+n)
|
||||||
const [H,M]=s.split(':')
|
const [ws,we]=[h(w.start),h(w.end)]
|
||||||
const x=new Date(dt)
|
const z=(d0,[H,M])=>{d0=new Date(d0);d0.setHours(H,M,0,0);return d0.toISOString()}
|
||||||
x.setHours(H,M,0,0)
|
const S=p(s.start),E=p(s.end)
|
||||||
return x
|
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))
|
||||||
}
|
}
|
||||||
const rng=[p(c.searchRange.start),p(c.searchRange.end)]
|
let q=[]
|
||||||
const dur=c.durationMinutes
|
let st=S
|
||||||
const norm=x=>x.map(v=>[p(v.start),p(v.end)])
|
for(let k of m){
|
||||||
const A=norm(a),B=norm(b)
|
if(i(st,k.start)){
|
||||||
const merge=x=>{
|
let fs=x(st,S),fe=y(k.start,E)
|
||||||
let r=x.sort((x,y)=>x[0]-y[0]),o=[r[0]]
|
if(i(fs,fe))q.push({start:fs,end:fe})
|
||||||
for(let i=1;i<r.length;i++){
|
|
||||||
let [s,e]=r[i],l=o[o.length-1]
|
|
||||||
if(s<=l[1]) l[1]=new Date(Math.max(e,l[1]))
|
|
||||||
else o.push([s,e])
|
|
||||||
}
|
}
|
||||||
return o
|
st=k.end
|
||||||
}
|
}
|
||||||
const busy=merge(A.concat(B))
|
if(i(st,E))q.push({start:x(st,S),end:E})
|
||||||
const free=[]
|
let out=[]
|
||||||
let cur=rng[0]
|
for(let v of q){
|
||||||
for(let i=0;i<=busy.length;i++){
|
let cs=z(v.start,ws),ce=z(v.start,we)
|
||||||
let s=i<busy.length?max(cur,busy[i][1]):cur
|
cs=p(cs);ce=p(ce)
|
||||||
let e=i<busy.length?max(cur,busy[i][0]):rng[1]
|
let ss=x(v.start,cs),ee=y(v.end,ce)
|
||||||
if(i<busy.length){
|
for(let t=ss;i(d(t,r),ee)||+d(t,r)==+ee;t=d(t,r)){
|
||||||
if(busy[i][0]>cur) free.push([cur,busy[i][0]])
|
let e=d(t,r)
|
||||||
cur=max(cur,busy[i][1])
|
if(!j(t,ss)||i(e,ee))out.push({start:t.toISOString(),end:e.toISOString()})
|
||||||
}else free.push([s,e])
|
|
||||||
}
|
|
||||||
const out=[]
|
|
||||||
for(let [s,e]of free){
|
|
||||||
s=max(s,rng[0])
|
|
||||||
e=min(e,rng[1])
|
|
||||||
let ws=wh(s,c.workHours.start)
|
|
||||||
let we=wh(s,c.workHours.end)
|
|
||||||
if(ws>we){let t=ws;ws=we;we=t}
|
|
||||||
let ss=max(s,ws),ee=min(e,we)
|
|
||||||
while(isBefore(addMinutes(ss,dur),ee)){
|
|
||||||
out.push({start:formatISO(ss),end:formatISO(addMinutes(ss,dur))})
|
|
||||||
ss=addMinutes(ss,dur)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -1,55 +1,52 @@
|
|||||||
const luxon=import('https://cdn.skypack.dev/luxon');
|
let luxon$
|
||||||
|
|
||||||
const findAvailableSlots=async(c1,c2,k)=>{
|
const findAvailableSlots = async (calA, calB, cfg) => {
|
||||||
const {DateTime}=await luxon;
|
const {DateTime, Interval} = await (luxon$ ||= import('https://cdn.skypack.dev/luxon'))
|
||||||
const {durationMinutes:d,searchRange:r,workHours:w}=k;
|
const {durationMinutes: d, searchRange: r, workHours: w} = cfg
|
||||||
const zone=DateTime.fromISO(r.start).zoneName;
|
const s = DateTime.fromISO(r.start)
|
||||||
const iso=v=>DateTime.fromISO(v,{zone});
|
const e = DateTime.fromISO(r.end)
|
||||||
const rangeStart=iso(r.start);
|
const range = Interval.fromDateTimes(s, e)
|
||||||
const rangeEnd=iso(r.end);
|
const [sh, sm] = w.start.split(':').map(Number)
|
||||||
if(rangeEnd<=rangeStart)return[];
|
const [eh, em] = w.end.split(':').map(Number)
|
||||||
const [hs,ms]=w.start.split(':').map(Number);
|
const busy = [...calA, ...calB]
|
||||||
const [he,me]=w.end.split(':').map(Number);
|
.map(({start, end}) => ({start: DateTime.fromISO(start), end: DateTime.fromISO(end)}))
|
||||||
const daysEnd=rangeEnd.startOf('day');
|
.filter(v => v.end > s && v.start < e)
|
||||||
const windows=[];
|
.map(v => ({start: v.start < s ? s : v.start, end: v.end > e ? e : v.end}))
|
||||||
for(let day=rangeStart.startOf('day');day<=daysEnd;day=day.plus({days:1})){
|
.sort((a, b) => a.start.valueOf() - b.start.valueOf())
|
||||||
let s=day.set({hour:hs,minute:ms,second:0,millisecond:0});
|
const merged = []
|
||||||
let e=day.set({hour:he,minute:me,second:0,millisecond:0});
|
|
||||||
if(e<=s||e<=rangeStart||s>=rangeEnd)continue;
|
|
||||||
if(s<rangeStart)s=rangeStart;
|
|
||||||
if(e>rangeEnd)e=rangeEnd;
|
|
||||||
windows.push({start:s,end:e});
|
|
||||||
}
|
|
||||||
if(!windows.length)return[];
|
|
||||||
const busy=[...c1,...c2].map(v=>{
|
|
||||||
let s=iso(v.start),e=iso(v.end);
|
|
||||||
if(e<=s||e<=rangeStart||s>=rangeEnd)return null;
|
|
||||||
if(s<rangeStart)s=rangeStart;
|
|
||||||
if(e>rangeEnd)e=rangeEnd;
|
|
||||||
return{start:s,end:e};
|
|
||||||
}).filter(Boolean).sort((a,b)=>a.start.valueOf()-b.start.valueOf());
|
|
||||||
const merged=[];
|
|
||||||
for (const slot of busy) {
|
for (const slot of busy) {
|
||||||
const last=merged[merged.length-1];
|
const last = merged.at(-1)
|
||||||
if(last&&slot.start<=last.end){
|
if (!last || slot.start > last.end) merged.push({...slot})
|
||||||
if(slot.end>last.end)last.end=slot.end;
|
else if (slot.end > last.end) last.end = slot.end
|
||||||
}else merged.push({start:slot.start,end:slot.end});
|
|
||||||
}
|
}
|
||||||
const out=[];
|
const out = []
|
||||||
const push=(s,e)=>e.diff(s,'minutes').minutes>=d&&out.push({start:s.toISO(),end:e.toISO()});
|
const emit = (from, to) => {
|
||||||
for(const wSlot of windows){
|
if (!(to > from)) return
|
||||||
let cur=wSlot.start;
|
for (let st = from, en = st.plus({minutes: d}); en <= to; st = en, en = st.plus({minutes: d}))
|
||||||
for(const b of merged){
|
out.push({start: st.toISO(), end: en.toISO()})
|
||||||
if(b.start>=wSlot.end)break;
|
|
||||||
if(b.end<=wSlot.start)continue;
|
|
||||||
const bs=b.start>wSlot.start?b.start:wSlot.start;
|
|
||||||
const be=b.end<wSlot.end?b.end:wSlot.end;
|
|
||||||
if(bs>cur)push(cur,bs);
|
|
||||||
if(be>cur)cur=be;
|
|
||||||
if(cur>=wSlot.end)break;
|
|
||||||
}
|
}
|
||||||
if(cur<wSlot.end)push(cur,wSlot.end);
|
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
|
||||||
}
|
}
|
||||||
return out;
|
|
||||||
};
|
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
const validateJSON = async (data, schema) => {
|
const validateJSON = async (data, schema) => {
|
||||||
const { default: Ajv } = await import('https://cdn.jsdelivr.net/npm/ajv@8/dist/ajv.bundle.js');
|
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
||||||
|
const ajv = new Ajv({ allErrors: true });
|
||||||
const ajv = new Ajv({ allErrors: true, verbose: true });
|
|
||||||
const validate = ajv.compile(schema);
|
const validate = ajv.compile(schema);
|
||||||
const valid = validate(data);
|
const valid = validate(data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid,
|
valid,
|
||||||
errors: valid ? [] : validate.errors.map(e =>
|
errors: valid ? [] : validate.errors.map(e => ({
|
||||||
`${e.instancePath || '/'} ${e.message}${e.params ? ': ' + JSON.stringify(e.params) : ''}`
|
path: e.instancePath || '/',
|
||||||
)
|
message: e.message,
|
||||||
|
keyword: e.keyword,
|
||||||
|
params: e.params
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,35 +1,17 @@
|
|||||||
const validateJSON = async (jsonObject, schemaObject) => {
|
const validateJSON = async (data, schema) => {
|
||||||
try {
|
try {
|
||||||
const [{
|
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
||||||
default: Ajv
|
const validate = new Ajv({ allErrors: true }).compile(schema);
|
||||||
}, {
|
const valid = validate(data);
|
||||||
default: addFormats
|
|
||||||
}] = await Promise.all([
|
|
||||||
import('https://esm.sh/ajv'),
|
|
||||||
import('https://esm.sh/ajv-formats')
|
|
||||||
]);
|
|
||||||
|
|
||||||
const ajv = new Ajv({
|
|
||||||
allErrors: true
|
|
||||||
});
|
|
||||||
addFormats(ajv);
|
|
||||||
|
|
||||||
const validate = ajv.compile(schemaObject);
|
|
||||||
const valid = validate(jsonObject);
|
|
||||||
|
|
||||||
const errors = valid ? [] : validate.errors.map(
|
|
||||||
e => `${e.instancePath || '<root>'}: ${e.message}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid,
|
valid,
|
||||||
errors
|
errors: valid ? [] : (validate.errors ?? []).map(
|
||||||
|
({ instancePath, message }) => `${instancePath || 'root'} ${message}`.trim()
|
||||||
|
),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return { valid: false, errors: [e.message] };
|
||||||
valid: false,
|
|
||||||
errors: [`Validation setup failed: ${e.message}`]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
async function validateJSON(o, s) {
|
async function validateJSON(d,s){
|
||||||
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
const {default:Ajv}=await import('https://cdn.skypack.dev/ajv');
|
||||||
const a=new Ajv({allErrors:true});
|
const a=new Ajv({allErrors:true});
|
||||||
const v=a.compile(s);
|
const v=a.compile(s);
|
||||||
const ok = v(o);
|
const ok=v(d);
|
||||||
return { valid: ok, errors: ok ? [] : v.errors.map(e => e.message) };
|
return{valid:ok,errors:ok?[]:(v.errors||[]).map(e=>e.message||'')};
|
||||||
}
|
}
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
const validateJSON=async(j,s)=>{
|
export async function validateJSON(data, schema) {
|
||||||
const{default:Ajv}=await import('https://esm.sh/ajv@8')
|
const { default: Ajv } = await import('https://esm.sh/ajv@8?bundle');
|
||||||
const{default:f}=await import('https://esm.sh/ajv-formats@2')
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
||||||
const a=new Ajv({allErrors:1,strict:false})
|
const validate = ajv.compile(schema);
|
||||||
f(a)
|
const valid = validate(data);
|
||||||
const v=a.compile(s)
|
const errors = valid ? [] : (validate.errors || []).map(e => `${e.instancePath || '/'} ${e.message || ''}`.trim());
|
||||||
const ok=v(j)
|
return { valid, errors };
|
||||||
return{valid:ok,errors:ok?[]:(v.errors||[]).map(e=>`${e.instancePath||'/'} ${e.message}`.trim())}
|
|
||||||
}
|
}
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export async function createStreamVisualizer(asyncIterable, options) {
|
||||||
|
const { maxPoints, alpha, width, height, yDomain } = options;
|
||||||
|
const d3 = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm');
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
let prevEma;
|
||||||
|
|
||||||
|
for await (const { timestamp, value } of asyncIterable) {
|
||||||
|
const ema = prevEma === undefined ? value : alpha * value + (1 - alpha) * prevEma;
|
||||||
|
data.push({ timestamp, value, ema });
|
||||||
|
if (data.length > maxPoints) data.shift();
|
||||||
|
prevEma = ema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const xScale = d3.scaleLinear()
|
||||||
|
.domain([data[0].timestamp, data[data.length - 1].timestamp])
|
||||||
|
.range([0, width]);
|
||||||
|
|
||||||
|
const yScale = d3.scaleLinear()
|
||||||
|
.domain(yDomain)
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
const path = d3.line()
|
||||||
|
.x(d => xScale(d.timestamp))
|
||||||
|
.y(d => yScale(d.ema))
|
||||||
|
(data);
|
||||||
|
|
||||||
|
return { data, path };
|
||||||
|
}
|
||||||
|
export default createStreamVisualizer;
|
||||||
60
tests/9_stream_visualizer/outputs/google_gemini-2.5-pro.js
Normal file
60
tests/9_stream_visualizer/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
async function createStreamVisualizer(asyncIterable, {
|
||||||
|
maxPoints,
|
||||||
|
alpha,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
yDomain
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
scaleLinear,
|
||||||
|
line
|
||||||
|
} = await import('d3');
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
let prevEma = null;
|
||||||
|
|
||||||
|
for await (const {
|
||||||
|
timestamp,
|
||||||
|
value
|
||||||
|
} of asyncIterable) {
|
||||||
|
const ema = prevEma === null ?
|
||||||
|
value :
|
||||||
|
alpha * value + (1 - alpha) * prevEma;
|
||||||
|
|
||||||
|
data.push({
|
||||||
|
timestamp,
|
||||||
|
value,
|
||||||
|
ema
|
||||||
|
});
|
||||||
|
prevEma = ema;
|
||||||
|
|
||||||
|
if (data.length > maxPoints) {
|
||||||
|
data.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.length < 2) {
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
path: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = scaleLinear()
|
||||||
|
.domain([data[0].timestamp, data.at(-1).timestamp])
|
||||||
|
.range([0, width]);
|
||||||
|
|
||||||
|
const y = scaleLinear()
|
||||||
|
.domain(yDomain)
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
const path = line()
|
||||||
|
.x(d => x(d.timestamp))
|
||||||
|
.y(d => y(d.ema))(data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default createStreamVisualizer;
|
||||||
23
tests/9_stream_visualizer/outputs/openai_gpt-5.1-chat.js
Normal file
23
tests/9_stream_visualizer/outputs/openai_gpt-5.1-chat.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
23
tests/9_stream_visualizer/outputs/openai_gpt-5.1-codex.js
Normal file
23
tests/9_stream_visualizer/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
async function createStreamVisualizer(iterable, {
|
||||||
|
maxPoints = 100,
|
||||||
|
alpha = .5,
|
||||||
|
width = 300,
|
||||||
|
height = 150,
|
||||||
|
yDomain = [0, 1]
|
||||||
|
} = {}) {
|
||||||
|
const { scaleLinear, line } = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm')
|
||||||
|
const data = []
|
||||||
|
let ema
|
||||||
|
for await (const point of iterable) {
|
||||||
|
const { timestamp, value } = point
|
||||||
|
ema = ema == null ? value : alpha * value + (1 - alpha) * ema
|
||||||
|
data.push({ timestamp, value, ema })
|
||||||
|
data.length > maxPoints && data.shift()
|
||||||
|
}
|
||||||
|
if (!data.length) return { data, path: '' }
|
||||||
|
const x = scaleLinear().domain([data[0].timestamp, data.at(-1).timestamp]).range([0, width])
|
||||||
|
const y = scaleLinear().domain(yDomain).range([height, 0])
|
||||||
|
const path = line().x(d => x(d.timestamp)).y(d => y(d.ema))(data) || ''
|
||||||
|
return { data, path }
|
||||||
|
}
|
||||||
|
export default createStreamVisualizer;
|
||||||
Reference in New Issue
Block a user