mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-03-17 03:11:01 +00:00
Docs: Update benchmark for openai/gpt-5.3-codex
This commit is contained in:
@@ -1,4 +1,12 @@
|
|||||||
|
async function hashPassword(password, salt) {
|
||||||
|
if (typeof password !== 'string' || typeof salt !== 'string') throw new TypeError('password and salt must be strings')
|
||||||
|
const { scrypt: named, default: def } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/+esm')
|
||||||
|
const scrypt = named || def?.scrypt || def
|
||||||
|
if (typeof scrypt !== 'function') throw new Error('Failed to load scrypt function')
|
||||||
|
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;
|
export default hashPassword;
|
||||||
// Generation time: 0.499s
|
// Generation time: 7.962s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,28 @@
|
|||||||
|
const getTurf = (() => {
|
||||||
|
let mod;
|
||||||
|
return () => (mod ??= import("https://cdn.jsdelivr.net/npm/@turf/turf@7.2.0/+esm"));
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function analyzeGPS(readings, boundary) {
|
||||||
|
if (!Array.isArray(readings) || !boundary) return 0;
|
||||||
|
const turf = await getTurf();
|
||||||
|
const coords = readings
|
||||||
|
.filter(
|
||||||
|
r =>
|
||||||
|
r &&
|
||||||
|
Number.isFinite(r.ts) &&
|
||||||
|
Array.isArray(r.loc) &&
|
||||||
|
r.loc.length === 2 &&
|
||||||
|
r.loc.every(Number.isFinite) &&
|
||||||
|
turf.booleanPointInPolygon(turf.point(r.loc), boundary, { ignoreBoundary: true })
|
||||||
|
)
|
||||||
|
.sort((a, b) => a.ts - b.ts)
|
||||||
|
.map(r => r.loc);
|
||||||
|
|
||||||
|
if (coords.length < 2) return 0;
|
||||||
|
const km = turf.length(turf.lineString(coords), { units: "kilometers" });
|
||||||
|
return Math.round(km * 100) / 100;
|
||||||
|
}
|
||||||
export default analyzeGPS;
|
export default analyzeGPS;
|
||||||
// Generation time: 0.407s
|
// Generation time: 10.244s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,77 @@
|
|||||||
|
async function computeMST(tomlString) {
|
||||||
|
if (typeof tomlString !== 'string') throw new TypeError('computeMST expects a TOML string');
|
||||||
|
|
||||||
|
const [tomlMod, heapMod, tableMod] = await Promise.all([
|
||||||
|
import('https://esm.sh/smol-toml'),
|
||||||
|
import('https://esm.sh/mnemonist/heap'),
|
||||||
|
import('https://esm.sh/text-table')
|
||||||
|
]);
|
||||||
|
|
||||||
|
const parse = tomlMod.parse ?? tomlMod.default?.parse;
|
||||||
|
const Heap = heapMod.default ?? heapMod.Heap;
|
||||||
|
const textTable = tableMod.default ?? tableMod;
|
||||||
|
|
||||||
|
if (typeof parse !== 'function') throw new Error('Failed to load smol-toml parser');
|
||||||
|
if (typeof Heap !== 'function') throw new Error('Failed to load mnemonist Heap');
|
||||||
|
if (typeof textTable !== 'function') throw new Error('Failed to load text-table');
|
||||||
|
|
||||||
|
const cfg = parse(tomlString) ?? {};
|
||||||
|
const rawEdges = Array.isArray(cfg.edges) ? cfg.edges : [];
|
||||||
|
const edges = rawEdges.map((e, i) => {
|
||||||
|
const from = e?.from;
|
||||||
|
const to = e?.to;
|
||||||
|
const weight = Number(e?.weight);
|
||||||
|
if (typeof from !== 'string' || !from || typeof to !== 'string' || !to || !Number.isFinite(weight)) {
|
||||||
|
throw new TypeError(`Invalid edge at index ${i}`);
|
||||||
|
}
|
||||||
|
return { from, to, weight };
|
||||||
|
});
|
||||||
|
|
||||||
|
const nodes = [...new Set(edges.flatMap(e => [e.from, e.to]))];
|
||||||
|
const target = Math.max(0, nodes.length - 1);
|
||||||
|
|
||||||
|
class UnionFind {
|
||||||
|
constructor(items) {
|
||||||
|
this.parent = new Map(items.map(x => [x, x]));
|
||||||
|
this.rank = new Map(items.map(x => [x, 0]));
|
||||||
|
}
|
||||||
|
find(x) {
|
||||||
|
let p = this.parent.get(x);
|
||||||
|
if (p !== x) {
|
||||||
|
p = this.find(p);
|
||||||
|
this.parent.set(x, p);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
union(a, b) {
|
||||||
|
let ra = this.find(a), rb = this.find(b);
|
||||||
|
if (ra === rb) return false;
|
||||||
|
let rka = this.rank.get(ra), rkb = this.rank.get(rb);
|
||||||
|
if (rka < rkb) [ra, rb] = [rb, ra], [rka, rkb] = [rkb, rka];
|
||||||
|
this.parent.set(rb, ra);
|
||||||
|
if (rka === rkb) this.rank.set(ra, rka + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const heap = new Heap((a, b) => a.weight - b.weight);
|
||||||
|
for (const e of edges) heap.push(e);
|
||||||
|
|
||||||
|
const uf = new UnionFind(nodes);
|
||||||
|
const mst = [];
|
||||||
|
let totalWeight = 0;
|
||||||
|
|
||||||
|
while (heap.size && mst.length < target) {
|
||||||
|
const e = heap.pop();
|
||||||
|
if (uf.union(e.from, e.to)) {
|
||||||
|
mst.push(e);
|
||||||
|
totalWeight += e.weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = [['From', 'To', 'Weight'], ...mst.map(e => [e.from, e.to, String(e.weight)])];
|
||||||
|
return { table: textTable(rows), totalWeight };
|
||||||
|
}
|
||||||
export default computeMST;
|
export default computeMST;
|
||||||
// Generation time: 0.998s
|
// Generation time: 18.070s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,42 @@
|
|||||||
|
const loadPriorityQueue = (() => {
|
||||||
|
let p;
|
||||||
|
return () =>
|
||||||
|
p ??= import("https://esm.sh/js-priority-queue").then(m => m.default || m);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function findShortestPath(graph, start, end) {
|
||||||
|
if (start === end) return 0;
|
||||||
|
if (!graph || typeof graph !== "object") return Infinity;
|
||||||
|
|
||||||
|
const PriorityQueue = await loadPriorityQueue();
|
||||||
|
const dist = { [start]: 0 };
|
||||||
|
const seen = new Set();
|
||||||
|
const q = new PriorityQueue({ comparator: (a, b) => a[1] - b[1] });
|
||||||
|
|
||||||
|
q.queue([start, 0]);
|
||||||
|
|
||||||
|
while (q.length) {
|
||||||
|
const [node, d] = q.dequeue();
|
||||||
|
if (seen.has(node)) continue;
|
||||||
|
seen.add(node);
|
||||||
|
if (node === end) return d;
|
||||||
|
|
||||||
|
const next = graph[node];
|
||||||
|
if (!next || typeof next !== "object") continue;
|
||||||
|
|
||||||
|
for (const n in next) {
|
||||||
|
const w = next[n];
|
||||||
|
if (!Number.isFinite(w) || w < 0) continue;
|
||||||
|
const nd = d + w;
|
||||||
|
if (nd < (dist[n] ?? Infinity)) {
|
||||||
|
dist[n] = nd;
|
||||||
|
q.queue([n, nd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
export default findShortestPath;
|
export default findShortestPath;
|
||||||
// Generation time: 0.986s
|
// Generation time: 15.189s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,40 @@
|
|||||||
|
async function findConvexHull(points = []) {
|
||||||
|
if (!Array.isArray(points)) throw new TypeError("Expected an array of points");
|
||||||
|
|
||||||
|
const { sortBy, uniqBy } = await import("https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js");
|
||||||
|
|
||||||
|
const pts = uniqBy(
|
||||||
|
sortBy(
|
||||||
|
points.filter(p => p && Number.isFinite(p.x) && Number.isFinite(p.y)),
|
||||||
|
["x", "y"]
|
||||||
|
),
|
||||||
|
p => `${p.x},${p.y}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const n = pts.length;
|
||||||
|
if (n < 2) return pts.slice();
|
||||||
|
|
||||||
|
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
||||||
|
|
||||||
|
const lower = [];
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const p = pts[i];
|
||||||
|
while (lower.length > 1 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) lower.pop();
|
||||||
|
lower.push(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
const upper = [];
|
||||||
|
for (let i = n - 1; i >= 0; i--) {
|
||||||
|
const p = pts[i];
|
||||||
|
while (upper.length > 1 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) upper.pop();
|
||||||
|
upper.push(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
lower.pop();
|
||||||
|
upper.pop();
|
||||||
|
|
||||||
|
return lower.concat(upper);
|
||||||
|
}
|
||||||
export default findConvexHull;
|
export default findConvexHull;
|
||||||
// Generation time: 0.773s
|
// Generation time: 16.419s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,13 @@
|
|||||||
|
async function findLISLength(arr) {
|
||||||
|
if (!Array.isArray(arr)) throw new TypeError("Expected an array of numbers");
|
||||||
|
const { bisectLeft } = await import("https://cdn.jsdelivr.net/npm/d3-array@3/+esm");
|
||||||
|
const tails = [];
|
||||||
|
for (const n of arr) {
|
||||||
|
if (typeof n !== "number" || Number.isNaN(n)) throw new TypeError("Array must contain only valid numbers");
|
||||||
|
tails[bisectLeft(tails, n)] = n;
|
||||||
|
}
|
||||||
|
return tails.length;
|
||||||
|
}
|
||||||
export default findLISLength;
|
export default findLISLength;
|
||||||
// Generation time: 0.821s
|
// Generation time: 7.363s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,16 @@
|
|||||||
|
const loadHandlebars = (() => {
|
||||||
|
let p;
|
||||||
|
return () =>
|
||||||
|
p ??= import('https://cdn.jsdelivr.net/npm/handlebars@4.7.8/+esm').then(
|
||||||
|
m => m.default ?? m
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function renderTemplate(template, data = {}) {
|
||||||
|
if (typeof template !== 'string') throw new TypeError('template must be a string');
|
||||||
|
if (!data || typeof data !== 'object' || Array.isArray(data)) throw new TypeError('data must be an object');
|
||||||
|
return (await loadHandlebars()).compile(template)(data);
|
||||||
|
}
|
||||||
export default renderTemplate;
|
export default renderTemplate;
|
||||||
// Generation time: 0.438s
|
// Generation time: 7.680s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,16 @@
|
|||||||
|
async function parseMarkdown(markdown) {
|
||||||
|
const src = typeof markdown === 'string' ? markdown : `${markdown ?? ''}`;
|
||||||
|
parseMarkdown._deps ??= Promise.all([
|
||||||
|
import('https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js'),
|
||||||
|
import('https://cdn.jsdelivr.net/npm/dompurify@3.2.6/+esm')
|
||||||
|
]);
|
||||||
|
const [{ marked }, { default: DOMPurify }] = await parseMarkdown._deps;
|
||||||
|
if (!parseMarkdown._ready) {
|
||||||
|
marked.setOptions({ gfm: true, breaks: false, headerIds: false, mangle: false });
|
||||||
|
parseMarkdown._ready = true;
|
||||||
|
}
|
||||||
|
return DOMPurify.sanitize(marked.parse(src), { USE_PROFILES: { html: true } });
|
||||||
|
}
|
||||||
export default parseMarkdown;
|
export default parseMarkdown;
|
||||||
// Generation time: 0.421s
|
// Generation time: 11.720s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,40 @@
|
|||||||
|
async function processCSV(csv, config = {}) {
|
||||||
|
const [{ default: Papa }, { groupBy, sumBy }] = await Promise.all([
|
||||||
|
import('https://esm.sh/papaparse@5.4.1'),
|
||||||
|
import('https://esm.sh/lodash-es@4.17.21')
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (typeof csv !== 'string') throw new TypeError('csv must be a string');
|
||||||
|
|
||||||
|
const {
|
||||||
|
filterColumn,
|
||||||
|
filterValue,
|
||||||
|
groupBy: groupKey,
|
||||||
|
aggregateColumn,
|
||||||
|
operation
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
if (!filterColumn || !groupKey || !aggregateColumn) throw new TypeError('Invalid configuration');
|
||||||
|
if (!['sum', 'avg', 'count'].includes(operation)) throw new TypeError('Invalid operation');
|
||||||
|
|
||||||
|
const parsed = Papa.parse(csv, { header: true, skipEmptyLines: true });
|
||||||
|
if (parsed.errors?.length) throw new Error(parsed.errors[0].message);
|
||||||
|
|
||||||
|
const rows = parsed.data.filter(r => r?.[filterColumn] == filterValue);
|
||||||
|
if (!rows.length) return [];
|
||||||
|
|
||||||
|
const num = v => {
|
||||||
|
const n = Number(v);
|
||||||
|
return Number.isNaN(n) ? 0 : n;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.entries(groupBy(rows, r => r?.[groupKey])).map(([k, g]) => {
|
||||||
|
const count = g.length;
|
||||||
|
const sum = sumBy(g, r => num(r?.[aggregateColumn]));
|
||||||
|
const result = operation === 'count' ? count : operation === 'avg' ? sum / count : sum;
|
||||||
|
return { [groupKey]: k, result };
|
||||||
|
});
|
||||||
|
}
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
// Generation time: 0.674s
|
// Generation time: 21.127s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,73 @@
|
|||||||
|
async function findAvailableSlots(calendarA = [], calendarB = [], constraints = {}) {
|
||||||
|
const { DateTime } = await import('https://cdn.jsdelivr.net/npm/luxon@3.4.4/+esm')
|
||||||
|
const toUtc = v => DateTime.fromISO(v, { zone: 'utc' }).toUTC()
|
||||||
|
const asMillis = d => d.toMillis()
|
||||||
|
const maxDt = (a, b) => (asMillis(a) > asMillis(b) ? a : b)
|
||||||
|
const minDt = (a, b) => (asMillis(a) < asMillis(b) ? a : b)
|
||||||
|
const parseHm = s => {
|
||||||
|
const [h, m] = String(s || '').split(':').map(Number)
|
||||||
|
return Number.isInteger(h) && Number.isInteger(m) && h >= 0 && h < 24 && m >= 0 && m < 60 ? [h, m] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const durationMinutes = Number(constraints.durationMinutes)
|
||||||
|
const range = constraints.searchRange || {}
|
||||||
|
const workHours = constraints.workHours || {}
|
||||||
|
const whStart = parseHm(workHours.start)
|
||||||
|
const whEnd = parseHm(workHours.end)
|
||||||
|
|
||||||
|
if (!(durationMinutes > 0) || !whStart || !whEnd) return []
|
||||||
|
if (whEnd[0] < whStart[0] || (whEnd[0] === whStart[0] && whEnd[1] <= whStart[1])) return []
|
||||||
|
|
||||||
|
const searchStart = toUtc(range.start || '')
|
||||||
|
const searchEnd = toUtc(range.end || '')
|
||||||
|
if (!searchStart.isValid || !searchEnd.isValid || asMillis(searchEnd) <= asMillis(searchStart)) return []
|
||||||
|
|
||||||
|
const busyRaw = [...calendarA, ...calendarB]
|
||||||
|
.map(x => ({ s: toUtc(x?.start || ''), e: toUtc(x?.end || '') }))
|
||||||
|
.filter(x => x.s.isValid && x.e.isValid && asMillis(x.e) > asMillis(x.s))
|
||||||
|
.map(x => ({ s: maxDt(x.s, searchStart), e: minDt(x.e, searchEnd) }))
|
||||||
|
.filter(x => asMillis(x.e) > asMillis(x.s))
|
||||||
|
.sort((a, b) => asMillis(a.s) - asMillis(b.s))
|
||||||
|
|
||||||
|
const merged = []
|
||||||
|
for (const it of busyRaw) {
|
||||||
|
const last = merged[merged.length - 1]
|
||||||
|
if (!last || asMillis(it.s) > asMillis(last.e)) merged.push({ ...it })
|
||||||
|
else if (asMillis(it.e) > asMillis(last.e)) last.e = it.e
|
||||||
|
}
|
||||||
|
|
||||||
|
const free = []
|
||||||
|
let cursor = searchStart
|
||||||
|
for (const b of merged) {
|
||||||
|
if (asMillis(b.s) > asMillis(cursor)) free.push({ s: cursor, e: b.s })
|
||||||
|
if (asMillis(b.e) > asMillis(cursor)) cursor = b.e
|
||||||
|
}
|
||||||
|
if (asMillis(searchEnd) > asMillis(cursor)) free.push({ s: cursor, e: searchEnd })
|
||||||
|
|
||||||
|
const out = []
|
||||||
|
for (const f of free) {
|
||||||
|
for (
|
||||||
|
let day = f.s.startOf('day'), endDay = f.e.startOf('day');
|
||||||
|
asMillis(day) <= asMillis(endDay);
|
||||||
|
day = day.plus({ days: 1 })
|
||||||
|
) {
|
||||||
|
const wStart = day.set({ hour: whStart[0], minute: whStart[1], second: 0, millisecond: 0 })
|
||||||
|
const wEnd = day.set({ hour: whEnd[0], minute: whEnd[1], second: 0, millisecond: 0 })
|
||||||
|
const s = maxDt(f.s, wStart)
|
||||||
|
const e = minDt(f.e, wEnd)
|
||||||
|
if (asMillis(e) <= asMillis(s)) continue
|
||||||
|
|
||||||
|
for (let t = s, n = t.plus({ minutes: durationMinutes }); asMillis(n) <= asMillis(e); t = n, n = t.plus({ minutes: durationMinutes })) {
|
||||||
|
out.push({
|
||||||
|
start: t.toISO({ suppressMilliseconds: true }),
|
||||||
|
end: n.toISO({ suppressMilliseconds: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
// Generation time: 1.062s
|
// Generation time: 42.899s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,33 @@
|
|||||||
|
let ajvReady;
|
||||||
|
|
||||||
|
const getAjv = () =>
|
||||||
|
ajvReady ??= (async () => {
|
||||||
|
const { default: Ajv } = await import("https://esm.sh/ajv@8");
|
||||||
|
return new Ajv({ allErrors: true, strict: false });
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function validateJSON(data, schema) {
|
||||||
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
||||||
|
return { valid: false, errors: ["Schema must be a JSON object"] };
|
||||||
|
}
|
||||||
|
|
||||||
|
let validate;
|
||||||
|
try {
|
||||||
|
const ajv = await getAjv();
|
||||||
|
validate = ajv.compile(schema);
|
||||||
|
} catch (e) {
|
||||||
|
return { valid: false, errors: [`Invalid schema: ${e?.message || String(e)}`] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const valid = !!validate(data);
|
||||||
|
const errors = valid
|
||||||
|
? []
|
||||||
|
: (validate.errors || []).map(
|
||||||
|
e => `${e.instancePath || "/"} ${e.message || "is invalid"}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return { valid, errors };
|
||||||
|
}
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
// Generation time: 0.319s
|
// Generation time: 20.276s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
@@ -1,4 +1,53 @@
|
|||||||
|
export async function createStreamVisualizer(asyncIterable, options = {}) {
|
||||||
|
const d3Promise = import("https://cdn.jsdelivr.net/npm/d3@7/+esm");
|
||||||
|
|
||||||
|
const cap = Math.max(0, Number.isFinite(+options.maxPoints) ? +options.maxPoints : 500);
|
||||||
|
const a = Math.min(1, Math.max(0, Number.isFinite(+options.alpha) ? +options.alpha : 0.2));
|
||||||
|
const width = Number.isFinite(+options.width) ? +options.width : 640;
|
||||||
|
const height = Number.isFinite(+options.height) ? +options.height : 320;
|
||||||
|
|
||||||
|
const yd = Array.isArray(options.yDomain) ? options.yDomain : [0, 1];
|
||||||
|
const y0 = Number.isFinite(+yd[0]) ? +yd[0] : 0;
|
||||||
|
const y1Raw = Number.isFinite(+yd[1]) ? +yd[1] : 1;
|
||||||
|
const y1 = y1Raw === y0 ? y0 + 1 : y1Raw;
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
let ema;
|
||||||
|
|
||||||
|
for await (const item of asyncIterable) {
|
||||||
|
const rawValue = item && typeof item === "object" ? item.value : item;
|
||||||
|
const rawTs = item && typeof item === "object" && "timestamp" in item ? item.timestamp : Date.now();
|
||||||
|
|
||||||
|
const value = +rawValue;
|
||||||
|
const ts = rawTs instanceof Date ? +rawTs : +rawTs;
|
||||||
|
|
||||||
|
if (!Number.isFinite(value) || !Number.isFinite(ts)) continue;
|
||||||
|
|
||||||
|
ema = ema === undefined ? value : a * value + (1 - a) * ema;
|
||||||
|
|
||||||
|
if (cap) {
|
||||||
|
data.push({ timestamp: ts, value, ema });
|
||||||
|
if (data.length > cap) data.splice(0, data.length - cap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.length) return { data, path: "" };
|
||||||
|
|
||||||
|
const { scaleLinear, line } = await d3Promise;
|
||||||
|
|
||||||
|
const first = data[0].timestamp;
|
||||||
|
const lastRaw = data[data.length - 1].timestamp;
|
||||||
|
const last = lastRaw === first ? first + 1 : lastRaw;
|
||||||
|
|
||||||
|
const x = scaleLinear().domain([first, last]).range([0, width]);
|
||||||
|
const y = scaleLinear().domain([y0, y1]).range([height, 0]);
|
||||||
|
|
||||||
|
const path = line()
|
||||||
|
.x(d => x(d.timestamp))
|
||||||
|
.y(d => y(d.ema))(data) || "";
|
||||||
|
|
||||||
|
return { data, path };
|
||||||
|
}
|
||||||
export default createStreamVisualizer;
|
export default createStreamVisualizer;
|
||||||
// Generation time: 0.423s
|
// Generation time: 20.596s
|
||||||
// Result: FAIL
|
// Result: PASS
|
||||||
Reference in New Issue
Block a user