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:
@@ -0,0 +1,78 @@
|
||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
||||
const [{ default: utc }, { default: customParseFormat }, { default: isBetween }] = await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/utc.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/customParseFormat.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/isBetween.js')
|
||||
]);
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(isBetween);
|
||||
|
||||
const { durationMinutes, searchRange, workHours } = constraints;
|
||||
const duration = durationMinutes;
|
||||
|
||||
const mergedBusy = [...cal1, ...cal2]
|
||||
.map(({ start, end }) => ({ start: dayjs(start), end: dayjs(end) }))
|
||||
.sort((a, b) => a.start - b.start)
|
||||
.reduce((acc, curr) => {
|
||||
if (!acc.length) return [curr];
|
||||
const last = acc[acc.length - 1];
|
||||
if (curr.start <= last.end) {
|
||||
last.end = dayjs.max(last.end, curr.end);
|
||||
return acc;
|
||||
}
|
||||
return [...acc, curr];
|
||||
}, []);
|
||||
|
||||
const searchStart = dayjs(searchRange.start);
|
||||
const searchEnd = dayjs(searchRange.end);
|
||||
const [workStart, workEnd] = [workHours.start, workHours.end];
|
||||
|
||||
const slots = [];
|
||||
let current = searchStart;
|
||||
|
||||
while (current < searchEnd) {
|
||||
const dayStart = dayjs(`${current.format('YYYY-MM-DD')}T${workStart}`);
|
||||
const dayEnd = dayjs(`${current.format('YYYY-MM-DD')}T${workEnd}`);
|
||||
|
||||
const workStart_ = dayjs.max(current, dayStart);
|
||||
const workEnd_ = dayjs.min(searchEnd, dayEnd);
|
||||
|
||||
if (workStart_ < workEnd_) {
|
||||
const dayBusy = mergedBusy.filter(b =>
|
||||
b.start < workEnd_ && b.end > workStart_
|
||||
);
|
||||
|
||||
let pointer = workStart_;
|
||||
|
||||
for (const busy of dayBusy) {
|
||||
const gapEnd = dayjs.min(busy.start, workEnd_);
|
||||
|
||||
while (pointer.add(duration, 'minute') <= gapEnd) {
|
||||
slots.push({
|
||||
start: pointer.toISOString(),
|
||||
end: pointer.add(duration, 'minute').toISOString()
|
||||
});
|
||||
pointer = pointer.add(duration, 'minute');
|
||||
}
|
||||
|
||||
pointer = dayjs.max(pointer, busy.end);
|
||||
}
|
||||
|
||||
while (pointer.add(duration, 'minute') <= workEnd_) {
|
||||
slots.push({
|
||||
start: pointer.toISOString(),
|
||||
end: pointer.add(duration, 'minute').toISOString()
|
||||
});
|
||||
pointer = pointer.add(duration, 'minute');
|
||||
}
|
||||
}
|
||||
|
||||
current = current.add(1, 'day').startOf('day');
|
||||
}
|
||||
|
||||
return slots;
|
||||
};
|
||||
export default findAvailableSlots;
|
||||
@@ -0,0 +1,93 @@
|
||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||
const { default: dayjs } = await import('https://cdn.skypack.dev/dayjs@1.11.10');
|
||||
const { default: utc } = await import('https://cdn.skypack.dev/dayjs@1.11.10/plugin/utc');
|
||||
const { default: customParseFormat } = await import('https://cdn.skypack.dev/dayjs@1.11.10/plugin/customParseFormat');
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(customParseFormat);
|
||||
|
||||
const { durationMinutes, searchRange, workHours } = constraints;
|
||||
const duration = durationMinutes * 60000;
|
||||
|
||||
const merged = [...cal1, ...cal2]
|
||||
.map(({ start, end }) => ({ start: new Date(start).getTime(), end: new Date(end).getTime() }))
|
||||
.sort((a, b) => a.start - b.start);
|
||||
|
||||
const busy = merged.reduce((acc, slot) => {
|
||||
if (!acc.length) return [slot];
|
||||
const last = acc[acc.length - 1];
|
||||
if (slot.start <= last.end) {
|
||||
last.end = Math.max(last.end, slot.end);
|
||||
return acc;
|
||||
}
|
||||
acc.push(slot);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const rangeStart = new Date(searchRange.start).getTime();
|
||||
const rangeEnd = new Date(searchRange.end).getTime();
|
||||
const [whStart, whEnd] = [workHours.start, workHours.end];
|
||||
|
||||
const isWithinWorkHours = (ts) => {
|
||||
const d = dayjs(ts);
|
||||
const time = d.format('HH:mm');
|
||||
return time >= whStart && time <= whEnd;
|
||||
};
|
||||
|
||||
const getWorkDayBounds = (ts) => {
|
||||
const d = dayjs(ts);
|
||||
const [h1, m1] = whStart.split(':').map(Number);
|
||||
const [h2, m2] = whEnd.split(':').map(Number);
|
||||
return {
|
||||
start: d.hour(h1).minute(m1).second(0).millisecond(0).valueOf(),
|
||||
end: d.hour(h2).minute(m2).second(0).millisecond(0).valueOf()
|
||||
};
|
||||
};
|
||||
|
||||
const slots = [];
|
||||
let current = rangeStart;
|
||||
|
||||
for (let ts = rangeStart; ts < rangeEnd; ts += 86400000) {
|
||||
const { start: dayStart, end: dayEnd } = getWorkDayBounds(ts);
|
||||
current = Math.max(current, dayStart);
|
||||
|
||||
busy.forEach(({ start: bStart, end: bEnd }) => {
|
||||
if (bStart > current && bStart <= dayEnd) {
|
||||
let slotStart = current;
|
||||
while (slotStart + duration <= Math.min(bStart, dayEnd)) {
|
||||
const slotEnd = slotStart + duration;
|
||||
if (isWithinWorkHours(slotStart) && isWithinWorkHours(slotEnd)) {
|
||||
slots.push({
|
||||
start: new Date(slotStart).toISOString(),
|
||||
end: new Date(slotEnd).toISOString()
|
||||
});
|
||||
}
|
||||
slotStart = slotEnd;
|
||||
}
|
||||
current = Math.max(current, bEnd);
|
||||
}
|
||||
if (bEnd > current && bEnd <= dayEnd) {
|
||||
current = Math.max(current, bEnd);
|
||||
}
|
||||
});
|
||||
|
||||
if (current < dayEnd) {
|
||||
let slotStart = current;
|
||||
while (slotStart + duration <= dayEnd) {
|
||||
const slotEnd = slotStart + duration;
|
||||
if (isWithinWorkHours(slotStart) && isWithinWorkHours(slotEnd)) {
|
||||
slots.push({
|
||||
start: new Date(slotStart).toISOString(),
|
||||
end: new Date(slotEnd).toISOString()
|
||||
});
|
||||
}
|
||||
slotStart = slotEnd;
|
||||
}
|
||||
}
|
||||
|
||||
current = dayStart + 86400000;
|
||||
}
|
||||
|
||||
return slots;
|
||||
};
|
||||
export default findAvailableSlots;
|
||||
74
tests/7_scheduler/outputs/anthropic_claude-sonnet-4.5.js
Normal file
74
tests/7_scheduler/outputs/anthropic_claude-sonnet-4.5.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||
const { DateTime, Interval } = await import('https://cdn.skypack.dev/luxon');
|
||||
|
||||
const { durationMinutes: dur, searchRange: sr, workHours: wh } = constraints;
|
||||
const [srStart, srEnd] = [DateTime.fromISO(sr.start), DateTime.fromISO(sr.end)];
|
||||
const [whStart, whEnd] = wh.start.split(':').map(Number);
|
||||
|
||||
const toInterval = ({ start, end }) =>
|
||||
Interval.fromDateTimes(DateTime.fromISO(start), DateTime.fromISO(end));
|
||||
|
||||
const busySlots = [...cal1, ...cal2]
|
||||
.map(toInterval)
|
||||
.sort((a, b) => a.start - b.start);
|
||||
|
||||
const merged = busySlots.reduce((acc, curr) => {
|
||||
if (!acc.length) return [curr];
|
||||
const last = acc[acc.length - 1];
|
||||
return last.overlaps(curr) || last.abutsStart(curr)
|
||||
? [...acc.slice(0, -1), last.union(curr)]
|
||||
: [...acc, curr];
|
||||
}, []);
|
||||
|
||||
const isWorkHours = dt => {
|
||||
const h = dt.hour, m = dt.minute;
|
||||
const mins = h * 60 + m;
|
||||
const whStartMins = whStart[0] * 60 + whStart[1];
|
||||
const whEndMins = whEnd[0] * 60 + whEnd[1];
|
||||
return mins >= whStartMins && mins < whEndMins;
|
||||
};
|
||||
|
||||
const slots = [];
|
||||
let curr = srStart;
|
||||
|
||||
while (curr < srEnd) {
|
||||
const dayStart = curr.set({ hour: whStart[0], minute: whStart[1], second: 0 });
|
||||
const dayEnd = curr.set({ hour: whEnd[0], minute: whEnd[1], second: 0 });
|
||||
|
||||
if (dayStart >= srEnd) break;
|
||||
|
||||
const effectiveStart = dayStart > srStart ? dayStart : srStart;
|
||||
const effectiveEnd = dayEnd < srEnd ? dayEnd : srEnd;
|
||||
|
||||
let pointer = effectiveStart;
|
||||
|
||||
while (pointer.plus({ minutes: dur }) <= effectiveEnd) {
|
||||
const slotEnd = pointer.plus({ minutes: dur });
|
||||
const slotInterval = Interval.fromDateTimes(pointer, slotEnd);
|
||||
|
||||
const isFree = !merged.some(busy =>
|
||||
busy.overlaps(slotInterval) || slotInterval.overlaps(busy)
|
||||
);
|
||||
|
||||
if (isFree && isWorkHours(pointer) && isWorkHours(slotEnd.minus({ minutes: 1 }))) {
|
||||
slots.push({
|
||||
start: pointer.toISO(),
|
||||
end: slotEnd.toISO()
|
||||
});
|
||||
pointer = slotEnd;
|
||||
} else {
|
||||
const nextBusy = merged.find(busy => busy.start > pointer);
|
||||
if (nextBusy && nextBusy.start < effectiveEnd) {
|
||||
pointer = nextBusy.end > pointer ? nextBusy.end : pointer.plus({ minutes: 1 });
|
||||
} else {
|
||||
pointer = pointer.plus({ minutes: 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curr = curr.plus({ days: 1 }).startOf('day');
|
||||
}
|
||||
|
||||
return slots;
|
||||
};
|
||||
export default findAvailableSlots;
|
||||
56
tests/7_scheduler/outputs/google_gemini-2.5-pro TEMP_0.7.js
Normal file
56
tests/7_scheduler/outputs/google_gemini-2.5-pro TEMP_0.7.js
Normal file
@@ -0,0 +1,56 @@
|
||||
async function findAvailableSlots(
|
||||
calendar1,
|
||||
calendar2,
|
||||
{ durationMinutes: dur, searchRange: sr, workHours: wh }
|
||||
) {
|
||||
const dayjs = (await import('https://cdn.skypack.dev/dayjs')).default;
|
||||
const utc = (await import('https://cdn.skypack.dev/dayjs/plugin/utc')).default;
|
||||
dayjs.extend(utc);
|
||||
|
||||
const d = (s) => dayjs.utc(s);
|
||||
const rangeStart = d(sr.start);
|
||||
const rangeEnd = d(sr.end);
|
||||
const [whsH, whsM] = wh.start.split(':');
|
||||
const [wheH, wheM] = wh.end.split(':');
|
||||
|
||||
const busySlots = [...calendar1, ...calendar2].map(({ start, end }) => ({
|
||||
start: d(start),
|
||||
end: d(end),
|
||||
}));
|
||||
|
||||
for (let day = rangeStart.clone().startOf('d'); day.isBefore(rangeEnd); day = day.add(1, 'd')) {
|
||||
busySlots.push({ start: day.startOf('d'), end: day.hour(whsH).minute(whsM) });
|
||||
busySlots.push({ start: day.hour(wheH).minute(wheM), end: day.endOf('d') });
|
||||
}
|
||||
|
||||
const merged = busySlots
|
||||
.filter(({ start, end }) => start.isBefore(end))
|
||||
.sort((a, b) => a.start - b.start)
|
||||
.reduce((acc, cur) => {
|
||||
const last = acc.at(-1);
|
||||
if (!last || cur.start.isAfter(last.end)) {
|
||||
acc.push(cur);
|
||||
} else if (cur.end.isAfter(last.end)) {
|
||||
last.end = cur.end;
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const available = [];
|
||||
let cursor = rangeStart;
|
||||
|
||||
[...merged, { start: rangeEnd, end: rangeEnd }].forEach((busy) => {
|
||||
const gapEnd = busy.start;
|
||||
for (let slotStart = cursor; !slotStart.add(dur, 'm').isAfter(gapEnd); slotStart = slotStart.add(dur, 'm')) {
|
||||
const slotEnd = slotStart.add(dur, 'm');
|
||||
available.push({
|
||||
start: slotStart.toISOString(),
|
||||
end: slotEnd.toISOString(),
|
||||
});
|
||||
}
|
||||
cursor = cursor.isAfter(busy.end) ? cursor : busy.end;
|
||||
});
|
||||
|
||||
return available;
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
55
tests/7_scheduler/outputs/openai_gpt-5-codex.js
Normal file
55
tests/7_scheduler/outputs/openai_gpt-5-codex.js
Normal file
@@ -0,0 +1,55 @@
|
||||
let df
|
||||
async function findAvailableSlots(a,b,c){
|
||||
df??=await import('https://cdn.jsdelivr.net/npm/date-fns@3.6.0/+esm')
|
||||
const {parseISO:pi,formatISO:fi,eachDayOfInterval:ed,set:st}=df
|
||||
const sr=pi(c.searchRange.start),er=pi(c.searchRange.end),srT=+sr,erT=+er
|
||||
const [hs,ms]=c.workHours.start.split(':').map(Number)
|
||||
const [he,me]=c.workHours.end.split(':').map(Number)
|
||||
const step=c.durationMinutes*6e4
|
||||
const norm=u=>{
|
||||
const arr=u.map(v=>({s:+pi(v.start),e:+pi(v.end)})).filter(v=>v.e>srT&&v.s<erT).map(v=>({s:Math.max(v.s,srT),e:Math.min(v.e,erT)})).filter(v=>v.e>v.s).sort((x,y)=>x.s-y.s)
|
||||
const out=[]
|
||||
arr.forEach(v=>{
|
||||
const w=out[out.length-1]
|
||||
if(!w||v.s>w.e)out.push({s:v.s,e:v.e})
|
||||
else w.e=Math.max(w.e,v.e)
|
||||
})
|
||||
return out
|
||||
}
|
||||
const free=u=>{
|
||||
const busy=norm(u),days=ed({start:sr,end:er}),out=[]
|
||||
let i=0
|
||||
for(const day of days){
|
||||
const d0=Math.max(+st(day,{hours:hs,minutes:ms,seconds:0,milliseconds:0}),srT)
|
||||
const d1=Math.min(+st(day,{hours:he,minutes:me,seconds:0,milliseconds:0}),erT)
|
||||
if(d0>=d1)continue
|
||||
while(i<busy.length&&busy[i].e<=d0)i++
|
||||
let cur=d0,j=i
|
||||
while(j<busy.length&&busy[j].s<d1){
|
||||
if(busy[j].s>cur)out.push({s:cur,e:Math.min(busy[j].s,d1)})
|
||||
cur=Math.max(cur,busy[j].e)
|
||||
if(cur>=d1)break
|
||||
j++
|
||||
}
|
||||
if(cur<d1)out.push({s:cur,e:d1})
|
||||
i=j
|
||||
}
|
||||
return out
|
||||
}
|
||||
const x=free(a),y=free(b),res=[]
|
||||
let i=0,j=0
|
||||
while(i<x.length&&j<y.length){
|
||||
const s=Math.max(x[i].s,y[j].s)
|
||||
const e=Math.min(x[i].e,y[j].e)
|
||||
if(e-s>=step){
|
||||
const k=Math.floor((e-s)/step)
|
||||
for(let n=0;n<k;n++){
|
||||
const start=s+n*step,end=start+step
|
||||
res.push({start:fi(new Date(start)),end:fi(new Date(end))})
|
||||
}
|
||||
}
|
||||
x[i].e<y[j].e?i++:x[i].e>y[j].e?j++:(i++,j++)
|
||||
}
|
||||
return res
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
115
tests/7_scheduler/outputs/openrouter_polaris-alpha.js
Normal file
115
tests/7_scheduler/outputs/openrouter_polaris-alpha.js
Normal file
@@ -0,0 +1,115 @@
|
||||
async function findAvailableSlots(calA, calB, c) {
|
||||
const { DateTime, Interval } = await import('https://cdn.skypack.dev/luxon').then(m => m);
|
||||
const d = c.durationMinutes;
|
||||
const z = DateTime.utc().zoneName;
|
||||
const parseIso = s => DateTime.fromISO(s, { zone: 'utc' });
|
||||
const parseHm = (hm, base) => {
|
||||
const [H, M] = hm.split(':').map(Number);
|
||||
return base.set({ hour: H, minute: M, second: 0, millisecond: 0 });
|
||||
};
|
||||
const toIso = dt => dt.toUTC().toISO();
|
||||
|
||||
const rStart = parseIso(c.searchRange.start);
|
||||
const rEnd = parseIso(c.searchRange.end);
|
||||
|
||||
const dayRange = [];
|
||||
for (let d0 = rStart.startOf('day'); d0 < rEnd; d0 = d0.plus({ days: 1 })) {
|
||||
const ws = parseHm(c.workHours.start, d0);
|
||||
const we = parseHm(c.workHours.end, d0);
|
||||
const s = ws < rStart ? rStart : ws;
|
||||
const e = we > rEnd ? rEnd : we;
|
||||
if (e > s) dayRange.push(Interval.fromDateTimes(s, e));
|
||||
}
|
||||
|
||||
const normalizeBusy = cal =>
|
||||
cal
|
||||
.map(({ start, end }) => {
|
||||
const s = parseIso(start), e = parseIso(end);
|
||||
return e > s ? Interval.fromDateTimes(s, e) : null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => a.start - b.start);
|
||||
|
||||
const mergeBusy = busy => {
|
||||
const m = [];
|
||||
for (const i of busy) {
|
||||
const last = m[m.length - 1];
|
||||
if (!last || i.start > last.end) m.push(i);
|
||||
else if (i.end > last.end) last.end = i.end;
|
||||
}
|
||||
return m;
|
||||
};
|
||||
|
||||
const intersectWithRange = (busy, ranges) => {
|
||||
const res = [];
|
||||
let j = 0;
|
||||
for (const r of ranges) {
|
||||
while (j < busy.length && busy[j].end <= r.start) j++;
|
||||
let k = j;
|
||||
while (k < busy.length && busy[k].start < r.end) {
|
||||
const s = busy[k].start < r.start ? r.start : busy[k].start;
|
||||
const e = busy[k].end > r.end ? r.end : busy[k].end;
|
||||
if (e > s) res.push(Interval.fromDateTimes(s, e));
|
||||
k++;
|
||||
}
|
||||
}
|
||||
return mergeBusy(res);
|
||||
};
|
||||
|
||||
const invertBusy = (busy, ranges) => {
|
||||
const free = [];
|
||||
for (const r of ranges) {
|
||||
let cur = r.start;
|
||||
for (const b of busy) {
|
||||
if (b.end <= r.start || b.start >= r.end) continue;
|
||||
if (b.start > cur) free.push(Interval.fromDateTimes(cur, b.start));
|
||||
if (b.end > cur) cur = b.end;
|
||||
if (cur >= r.end) break;
|
||||
}
|
||||
if (cur < r.end) free.push(Interval.fromDateTimes(cur, r.end));
|
||||
}
|
||||
return free;
|
||||
};
|
||||
|
||||
const intersectFree = (fa, fb) => {
|
||||
const res = [];
|
||||
let i = 0, j = 0;
|
||||
while (i < fa.length && j < fb.length) {
|
||||
const a = fa[i], b = fb[j];
|
||||
const s = a.start > b.start ? a.start : b.start;
|
||||
const e = a.end < b.end ? a.end : b.end;
|
||||
if (e > s) res.push(Interval.fromDateTimes(s, e));
|
||||
if (a.end < b.end) i++;
|
||||
else j++;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const filterByDuration = (slots, minutes) => {
|
||||
const out = [];
|
||||
const dur = { minutes };
|
||||
for (const s of slots) {
|
||||
let cur = s.start;
|
||||
while (cur.plus(dur) <= s.end) {
|
||||
const end = cur.plus(dur);
|
||||
out.push({ start: toIso(cur), end: toIso(end) });
|
||||
cur = end;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
const busyA = mergeBusy(normalizeBusy(calA));
|
||||
const busyB = mergeBusy(normalizeBusy(calB));
|
||||
|
||||
const busyAIn = intersectWithRange(busyA, dayRange);
|
||||
const busyBIn = intersectWithRange(busyB, dayRange);
|
||||
|
||||
const freeA = invertBusy(busyAIn, dayRange);
|
||||
const freeB = invertBusy(busyBIn, dayRange);
|
||||
|
||||
const mutualFree = intersectFree(freeA, freeB);
|
||||
|
||||
return filterByDuration(mutualFree, d);
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
Reference in New Issue
Block a user