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:
123
results.json
123
results.json
@@ -13,69 +13,69 @@
|
||||
"9_stream_visualizer": 51.923362084000026
|
||||
},
|
||||
"anthropic/claude-sonnet-4.5 TEMP:0.7": {
|
||||
"10_scrypt_hash": 2.994402775000024,
|
||||
"11_geospatial": 3.173007857000048,
|
||||
"1_dijkstra": 4.721320241999987,
|
||||
"2_convex_hull": 4.782739678999991,
|
||||
"3_lis": 2.566319416000042,
|
||||
"4_determinant": 2.087922611000016,
|
||||
"5_markdown_parser": 2.1563547050000054,
|
||||
"6_csv_processor": 4.368516923999996,
|
||||
"7_scheduler": 10.690530350000016,
|
||||
"8_json_validator": 2.9041606949999696,
|
||||
"9_stream_visualizer": 4.411488517000049
|
||||
"10_scrypt_hash": 2.512005516999983,
|
||||
"11_geospatial": 3.3480960069999566,
|
||||
"1_dijkstra": 4.758364550999948,
|
||||
"2_convex_hull": 5.67630803700001,
|
||||
"3_lis": 2.4023967360000245,
|
||||
"4_determinant": 2.215544780999946,
|
||||
"5_markdown_parser": 1.9106662779999897,
|
||||
"6_csv_processor": 4.774563675000041,
|
||||
"7_scheduler": 10.645383548000012,
|
||||
"8_json_validator": 3.43886771999998,
|
||||
"9_stream_visualizer": 4.354729013999982
|
||||
},
|
||||
"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
|
||||
"10_scrypt_hash": 7.498817803999991,
|
||||
"11_geospatial": 10.405374638999987,
|
||||
"1_dijkstra": 19.193891047999962,
|
||||
"2_convex_hull": 14.263961956999964,
|
||||
"3_lis": 3.3163609489999946,
|
||||
"4_determinant": 5.17318697499996,
|
||||
"5_markdown_parser": 6.449940595000051,
|
||||
"6_csv_processor": 37.769467569000085,
|
||||
"7_scheduler": 79.01968128499995,
|
||||
"8_json_validator": 8.087602373000001,
|
||||
"9_stream_visualizer": 12.885421481000026
|
||||
},
|
||||
"moonshotai/kimi-k2-thinking": {
|
||||
"10_scrypt_hash": 54.48318050600006,
|
||||
"11_geospatial": 123.94316572699998,
|
||||
"1_dijkstra": 20.75571812199999,
|
||||
"2_convex_hull": 281.26114928399994,
|
||||
"3_lis": 78.25152572599984,
|
||||
"4_determinant": 67.81623509099987,
|
||||
"5_markdown_parser": 154.2897356459999,
|
||||
"6_csv_processor": 85.1170599280002,
|
||||
"7_scheduler": 389.4384002489999,
|
||||
"8_json_validator": 17.501368902000134,
|
||||
"9_stream_visualizer": 98.03424570999994
|
||||
"10_scrypt_hash": 53.01161044100009,
|
||||
"11_geospatial": 49.509398480999984,
|
||||
"1_dijkstra": 75.16873453799997,
|
||||
"2_convex_hull": 115.48629523699998,
|
||||
"3_lis": 43.45383791499992,
|
||||
"4_determinant": 99.70906514900003,
|
||||
"5_markdown_parser": 22.150892672999994,
|
||||
"6_csv_processor": 100.07209453999997,
|
||||
"7_scheduler": 204.68869797400012,
|
||||
"8_json_validator": 282.8534934129999,
|
||||
"9_stream_visualizer": 445.8482685430001
|
||||
},
|
||||
"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
|
||||
"10_scrypt_hash": 33.595784767000005,
|
||||
"11_geospatial": 23.084225926999935,
|
||||
"1_dijkstra": 39.2938456369997,
|
||||
"2_convex_hull": 39.61738663299987,
|
||||
"3_lis": 32.74601506800018,
|
||||
"4_determinant": 12.779542751000262,
|
||||
"5_markdown_parser": 17.71407721999986,
|
||||
"6_csv_processor": 28.51295679199975,
|
||||
"7_scheduler": 98.78428086399985,
|
||||
"8_json_validator": 17.644947614999953,
|
||||
"9_stream_visualizer": 42.817067843999716
|
||||
},
|
||||
"openrouter/sherlock-think-alpha": {
|
||||
"10_scrypt_hash": 9.486372194000054,
|
||||
"11_geospatial": 15.555460506999864,
|
||||
"1_dijkstra": 21.514197755000087,
|
||||
"2_convex_hull": 35.37556943300041,
|
||||
"3_lis": 17.00031832400011,
|
||||
"4_determinant": 8.939955472999719,
|
||||
"5_markdown_parser": 15.81524256400019,
|
||||
"6_csv_processor": 23.44287434099987,
|
||||
"7_scheduler": 68.04158863600017,
|
||||
"8_json_validator": 19.484740249000023,
|
||||
"9_stream_visualizer": 28.426358369000255
|
||||
"10_scrypt_hash": 14.262482631000225,
|
||||
"11_geospatial": 21.945668632999993,
|
||||
"1_dijkstra": 31.84612786399992,
|
||||
"2_convex_hull": 51.9222613090002,
|
||||
"3_lis": 27.120956264999695,
|
||||
"4_determinant": 8.37700194299966,
|
||||
"5_markdown_parser": 24.515614581000058,
|
||||
"6_csv_processor": 25.41584125499986,
|
||||
"7_scheduler": 93.37877358199982,
|
||||
"8_json_validator": 43.54576423399988,
|
||||
"9_stream_visualizer": 41.675588322999886
|
||||
},
|
||||
"gemini-3-pro-preview TEMP:1.1": {
|
||||
"10_scrypt_hash": 20.09197308799997,
|
||||
@@ -349,5 +349,18 @@
|
||||
"7_scheduler": 43.091859620000236,
|
||||
"8_json_validator": 24.684869859999978,
|
||||
"9_stream_visualizer": 19.26046375499992
|
||||
},
|
||||
"google/gemini-3-pro-preview TEMP:0.35": {
|
||||
"10_scrypt_hash": 17.112088825000004,
|
||||
"11_geospatial": 34.95774618,
|
||||
"1_dijkstra": 17.93846221700001,
|
||||
"2_convex_hull": 51.98661096200001,
|
||||
"3_lis": 33.88681510400001,
|
||||
"4_determinant": 17.43103938499998,
|
||||
"5_markdown_parser": 79.04342028300002,
|
||||
"6_csv_processor": 74.62322437199997,
|
||||
"7_scheduler": 79.87219575399999,
|
||||
"8_json_validator": 28.535885181999998,
|
||||
"9_stream_visualizer": 21.60573508199997
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
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 encoder = new TextEncoder();
|
||||
const hash = await scrypt(
|
||||
enc.encode(password),
|
||||
enc.encode(salt),
|
||||
encoder.encode(password),
|
||||
encoder.encode(salt),
|
||||
1024,
|
||||
8,
|
||||
1,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const hashPassword = async (password, salt) => {
|
||||
const { default: scrypt } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/scrypt.min.js');
|
||||
const { scrypt } = await import('https://esm.sh/scrypt-js@3.0.1');
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
@@ -13,7 +13,7 @@ const hashPassword = async (password, salt) => {
|
||||
);
|
||||
|
||||
return [...hashBytes]
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
};
|
||||
export default hashPassword;
|
||||
@@ -0,0 +1,7 @@
|
||||
const hashPassword = async (p, s) => {
|
||||
const { scrypt } = await import('https://esm.sh/scrypt-js')
|
||||
const e = new TextEncoder()
|
||||
const h = await scrypt(e.encode(p), e.encode(s), 1024, 8, 1, 32)
|
||||
return [...h].map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
export default hashPassword;
|
||||
@@ -1,7 +0,0 @@
|
||||
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;
|
||||
@@ -1,7 +1,9 @@
|
||||
async function hashPassword(password, salt) {
|
||||
const { scrypt } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/+esm');
|
||||
const { default: scrypt } = await import('https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/index.js');
|
||||
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('');
|
||||
const p = e.encode(password);
|
||||
const s = e.encode(salt);
|
||||
const h = await scrypt(p, s, 1024, 8, 1, 32);
|
||||
return [...h].map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
export default hashPassword;
|
||||
@@ -1,9 +1,7 @@
|
||||
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('');
|
||||
};
|
||||
const e=new TextEncoder
|
||||
const hashPassword=async(p,s)=>{
|
||||
const{ scrypt }=await import('https://cdn.skypack.dev/scrypt-js')
|
||||
const h=await scrypt(e.encode(p),e.encode(s),1024,8,1,32)
|
||||
return Array.from(h,x=>(x+256).toString(16).slice(-2)).join('')
|
||||
}
|
||||
export default hashPassword;
|
||||
@@ -1,2 +1,9 @@
|
||||
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('')}
|
||||
async function hashPassword(password, salt) {
|
||||
const { scrypt } = await import('https://cdn.skypack.dev/scrypt-js');
|
||||
const e = new TextEncoder();
|
||||
const pw = e.encode(password);
|
||||
const s = e.encode(salt);
|
||||
const h = await scrypt(pw, s, 1024, 8, 1, 32);
|
||||
return Array.from(h).map(b=>b.toString(16).padStart(2,'0')).join('');
|
||||
}
|
||||
export default hashPassword;
|
||||
@@ -1,14 +1,14 @@
|
||||
const analyzeGPS = async (readings, boundary) => {
|
||||
async function analyzeGPS(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);
|
||||
const valid = readings.filter(r =>
|
||||
turf.booleanPointInPolygon(r.loc, boundary)
|
||||
).sort((a, b) => a.ts - b.ts);
|
||||
|
||||
if (validReadings.length < 2) return 0;
|
||||
if (valid.length < 2) return 0;
|
||||
|
||||
const lineString = turf.lineString(validReadings.map(r => r.loc));
|
||||
const line = turf.lineString(valid.map(r => r.loc));
|
||||
|
||||
return Math.round(turf.length(lineString) * 100) / 100;
|
||||
};
|
||||
return Math.round(turf.length(line) * 100) / 100;
|
||||
}
|
||||
export default analyzeGPS;
|
||||
@@ -1,18 +1,18 @@
|
||||
const analyzeGPS = async (readings, boundary) => {
|
||||
const t = await import('https://cdn.jsdelivr.net/npm/@turf/turf');
|
||||
const { booleanPointInPolygon, lineString, length } = await import('https://cdn.jsdelivr.net/npm/@turf/turf/+esm');
|
||||
|
||||
const sortedLocations = readings
|
||||
.filter(r => t.booleanPointInPolygon(r.loc, boundary, { ignoreBoundary: true }))
|
||||
const sortedCoords = readings
|
||||
.filter(({ loc }) => booleanPointInPolygon(loc, boundary, { ignoreBoundary: true }))
|
||||
.sort((a, b) => a.ts - b.ts)
|
||||
.map(r => r.loc);
|
||||
.map(({ loc }) => loc);
|
||||
|
||||
if (sortedLocations.length < 2) {
|
||||
if (sortedCoords.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const path = t.lineString(sortedLocations);
|
||||
const length = t.length(path, { units: 'kilometers' });
|
||||
const path = lineString(sortedCoords);
|
||||
const dist = length(path, { units: 'kilometers' });
|
||||
|
||||
return Math.round(length * 100) / 100;
|
||||
return Math.round(dist * 1e2) / 1e2;
|
||||
};
|
||||
export default analyzeGPS;
|
||||
@@ -0,0 +1,12 @@
|
||||
const analyzeGPS = async (readings, boundary) => {
|
||||
const turf = await import('https://cdn.jsdelivr.net/npm/@turf/turf/+esm');
|
||||
|
||||
const pts = readings
|
||||
.filter(({ loc }) => turf.booleanPointInPolygon(turf.point(loc), boundary))
|
||||
.sort((a, b) => a.ts - b.ts)
|
||||
.map(r => r.loc);
|
||||
|
||||
return pts.length < 2 ? 0 :
|
||||
+turf.length(turf.lineString(pts), { units: 'kilometers' }).toFixed(2);
|
||||
};
|
||||
export default analyzeGPS;
|
||||
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
@@ -1,12 +1,11 @@
|
||||
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 async function analyzeGPS(readings, boundary) {
|
||||
const { booleanPointInPolygon, lineString, length } = await import('https://cdn.skypack.dev/@turf/turf');
|
||||
|
||||
const pts = readings
|
||||
.filter(r => booleanPointInPolygon(r.loc, boundary))
|
||||
.sort((a, b) => a.ts - b.ts)
|
||||
.map(r => r.loc);
|
||||
|
||||
return pts.length < 2 ? 0 : +length(lineString(pts)).toFixed(2);
|
||||
}
|
||||
export default analyzeGPS;
|
||||
@@ -1,8 +1,13 @@
|
||||
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;
|
||||
let turf$;
|
||||
const useTurf = () => turf$ ??= import('https://cdn.skypack.dev/@turf/turf?min');
|
||||
|
||||
async function analyzeGPS(readings, boundary) {
|
||||
const t = await useTurf();
|
||||
const pts = readings
|
||||
.filter(r => t.booleanPointInPolygon(t.point(r.loc), boundary))
|
||||
.sort((a, b) => a.ts - b.ts);
|
||||
if (pts.length < 2) return 0;
|
||||
const km = t.length(t.lineString(pts.map(r => r.loc)), { units: 'kilometers' });
|
||||
return Math.round(km * 100) / 100;
|
||||
}
|
||||
export default analyzeGPS;
|
||||
@@ -1,8 +1,8 @@
|
||||
async function analyzeGPS(readings,boundary){
|
||||
const{turf}=await import('https://cdn.skypack.dev/@turf/turf');
|
||||
const{booleanPointInPolygon,lineString,length}=await import('https://cdn.skypack.dev/@turf/turf');
|
||||
const valid=readings
|
||||
.filter(({loc})=>turf.booleanPointInPolygon([loc[0],loc[1]],boundary,{ignoreBoundary:true}))
|
||||
.filter(r=>booleanPointInPolygon({type:'Point',coordinates:r.loc},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);
|
||||
return valid.length<2?0:+length(lineString(valid.map(r=>r.loc)),{units:'kilometers'}).toFixed(2);
|
||||
}
|
||||
export default analyzeGPS;
|
||||
@@ -1,27 +1,24 @@
|
||||
async function findShortestPath(graph, start, end) {
|
||||
const { default: PriorityQueue } = await import('https://cdn.skypack.dev/js-priority-queue');
|
||||
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/+esm');
|
||||
|
||||
const distances = {};
|
||||
const visited = new Set();
|
||||
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
||||
|
||||
for (const node in graph) distances[node] = Infinity;
|
||||
const distances = Object.keys(graph).reduce((acc, node) => ({ ...acc, [node]: Infinity }), {});
|
||||
distances[start] = 0;
|
||||
|
||||
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
||||
pq.queue({ node: start, dist: 0 });
|
||||
|
||||
const visited = new Set();
|
||||
|
||||
while (pq.length) {
|
||||
const { node, dist } = pq.dequeue();
|
||||
|
||||
if (visited.has(node)) continue;
|
||||
if (node === end) return dist;
|
||||
|
||||
visited.add(node);
|
||||
|
||||
for (const neighbor in graph[node]) {
|
||||
if (visited.has(neighbor)) continue;
|
||||
|
||||
const newDist = dist + graph[node][neighbor];
|
||||
|
||||
if (node === end) return dist;
|
||||
|
||||
for (const [neighbor, weight] of Object.entries(graph[node] || {})) {
|
||||
const newDist = dist + weight;
|
||||
if (newDist < distances[neighbor]) {
|
||||
distances[neighbor] = newDist;
|
||||
pq.queue({ node: neighbor, dist: newDist });
|
||||
@@ -29,6 +26,6 @@ async function findShortestPath(graph, start, end) {
|
||||
}
|
||||
}
|
||||
|
||||
return Infinity;
|
||||
return distances[end];
|
||||
}
|
||||
export default findShortestPath;
|
||||
@@ -1,34 +1,40 @@
|
||||
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 dists = Object.fromEntries(
|
||||
Object.keys(graph).map(node => [node, Infinity])
|
||||
async function findShortestPath(graph, startNode, endNode) {
|
||||
const { default: PriorityQueue } = await import(
|
||||
'https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/priority-queue.min.js'
|
||||
);
|
||||
|
||||
const distances = {};
|
||||
const pq = new PriorityQueue({ comparator: (a, b) => a.priority - b.priority });
|
||||
|
||||
dists[start] = 0;
|
||||
pq.queue({ node: start, priority: 0 });
|
||||
for (const node in graph) {
|
||||
distances[node] = Infinity;
|
||||
}
|
||||
distances[startNode] = 0;
|
||||
|
||||
pq.queue({ node: startNode, priority: 0 });
|
||||
|
||||
while (pq.length) {
|
||||
const { node: u, priority: currentDist } = pq.dequeue();
|
||||
const { node: currentNode, priority: currentPriority } = pq.dequeue();
|
||||
|
||||
if (currentDist > dists[u]) {
|
||||
if (currentPriority > distances[currentNode]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (u === end) break;
|
||||
if (currentNode === endNode) {
|
||||
return currentPriority;
|
||||
}
|
||||
|
||||
for (const [v, weight] of Object.entries(graph[u])) {
|
||||
const distThroughU = dists[u] + weight;
|
||||
for (const neighbor in graph[currentNode]) {
|
||||
const weight = graph[currentNode][neighbor];
|
||||
const newDistance = currentPriority + weight;
|
||||
|
||||
if (distThroughU < dists[v]) {
|
||||
dists[v] = distThroughU;
|
||||
pq.queue({ node: v, priority: distThroughU });
|
||||
if (newDistance < distances[neighbor]) {
|
||||
distances[neighbor] = newDistance;
|
||||
pq.queue({ node: neighbor, priority: newDistance });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dists[end];
|
||||
};
|
||||
return Infinity;
|
||||
}
|
||||
export default findShortestPath;
|
||||
@@ -0,0 +1,22 @@
|
||||
const findShortestPath = async (g, s, e) => {
|
||||
const { default: Q } = await import('https://esm.sh/js-priority-queue')
|
||||
const q = new Q({ comparator: (a, b) => a.w - b.w })
|
||||
const d = { [s]: 0 }
|
||||
q.queue({ n: s, w: 0 })
|
||||
|
||||
while (q.length) {
|
||||
const { n, w } = q.dequeue()
|
||||
if (n === e) return w
|
||||
if (w > (d[n] ?? Infinity)) continue
|
||||
|
||||
for (const v in g[n]) {
|
||||
const k = w + g[n][v]
|
||||
if (k < (d[v] ?? Infinity)) {
|
||||
d[v] = k
|
||||
q.queue({ n: v, w: k })
|
||||
}
|
||||
}
|
||||
}
|
||||
return Infinity
|
||||
}
|
||||
export default findShortestPath;
|
||||
@@ -1,19 +0,0 @@
|
||||
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,24 +1,28 @@
|
||||
async function findShortestPath(graph, start, end) {
|
||||
const { default: Q } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/index.min.js');
|
||||
const dist = { [start]: 0 }, visited = new Set();
|
||||
const pq = new Q({ comparator: (a, b) => dist[a] - dist[b] });
|
||||
pq.queue(start);
|
||||
export async function findShortestPath(graph, start, end) {
|
||||
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/index.min.js');
|
||||
|
||||
if (!graph[start] || !graph[end]) return Infinity;
|
||||
if (start === end) return 0;
|
||||
|
||||
const dist = { [start]: 0 };
|
||||
const pq = new PriorityQueue({ comparator: (a, b) => a[0] - b[0] });
|
||||
pq.queue([0, start]);
|
||||
|
||||
while (pq.length) {
|
||||
const node = pq.dequeue();
|
||||
if (node === end) return dist[end];
|
||||
if (visited.has(node)) continue;
|
||||
visited.add(node);
|
||||
const nodeDist = dist[node];
|
||||
for (const [adj, weight] of Object.entries(graph[node] || {})) {
|
||||
if (visited.has(adj)) continue;
|
||||
const newDist = nodeDist + weight;
|
||||
if (newDist < (dist[adj] ?? Infinity)) {
|
||||
dist[adj] = newDist;
|
||||
pq.queue(adj);
|
||||
const [d, node] = pq.dequeue();
|
||||
|
||||
if (node === end) return d;
|
||||
if (d > (dist[node] ?? Infinity)) continue;
|
||||
|
||||
for (const [n, w] of Object.entries(graph[node] || {})) {
|
||||
const nd = d + w;
|
||||
if (nd < (dist[n] ?? Infinity)) {
|
||||
dist[n] = nd;
|
||||
pq.queue([nd, n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Infinity;
|
||||
}
|
||||
export default findShortestPath;
|
||||
@@ -1,27 +1,30 @@
|
||||
const src='https://esm.sh/js-priority-queue@1'
|
||||
let load
|
||||
const getPQ=()=>load||(load=import(src).then(m=>m.default))
|
||||
const findShortestPath=async(g,s,e)=>{
|
||||
const PQ=await getPQ()
|
||||
const pq=new PQ({comparator:(a,b)=>a[0]-b[0]})
|
||||
const dist=new Map([[s,0]])
|
||||
const done=new Set
|
||||
pq.queue([0,s])
|
||||
while(pq.length){
|
||||
const [d,n]=pq.dequeue()
|
||||
if(done.has(n))continue
|
||||
done.add(n)
|
||||
if(n===e)return d
|
||||
const edges=g[n]
|
||||
if(!edges)continue
|
||||
for(const k in edges){
|
||||
const w=d+edges[k]
|
||||
if(w<(dist.get(k)??Infinity)){
|
||||
dist.set(k,w)
|
||||
pq.queue([w,k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return Infinity
|
||||
let pqPromise
|
||||
const getPQ=()=>pqPromise??=import('https://cdn.jsdelivr.net/npm/js-priority-queue@latest/+esm')
|
||||
|
||||
async function findShortestPath(g,s,t){
|
||||
if(!g||!(s in g)||!(t in g)) return Infinity
|
||||
if(s===t) return 0
|
||||
const {default:PriorityQueue}=await getPQ()
|
||||
const d={[s]:0}
|
||||
const seen=new Set
|
||||
const q=new PriorityQueue({comparator:(a,b)=>a[0]-b[0]})
|
||||
q.queue([0,s])
|
||||
while(q.length){
|
||||
const [w,n]=q.dequeue()
|
||||
if(seen.has(n)) continue
|
||||
seen.add(n)
|
||||
if(n===t) return w
|
||||
const edges=g[n]
|
||||
if(!edges) continue
|
||||
for(const k in edges){
|
||||
if(seen.has(k)) continue
|
||||
const nw=w+edges[k]
|
||||
if(nw<(d[k]??Infinity)){
|
||||
d[k]=nw
|
||||
q.queue([nw,k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return Infinity
|
||||
}
|
||||
export default findShortestPath;
|
||||
@@ -1,23 +1,24 @@
|
||||
async function findShortestPath(graph,start,end){
|
||||
const{default:PriorityQueue}=await import('https://cdn.skypack.dev/js-priority-queue');
|
||||
const {PriorityQueue}=await import('https://cdn.skypack.dev/js-priority-queue');
|
||||
if(start===end)return 0;
|
||||
const dist={};
|
||||
const pq=new PriorityQueue({comparator:(x,y)=>x.d-y.d});
|
||||
for(const n of Object.keys(graph))dist[n]=Infinity;
|
||||
dist[start]=0;
|
||||
pq.queue({n:start,d:0});
|
||||
const pq=new PriorityQueue({comparator:(a,b)=>a.dist-b.dist});
|
||||
pq.queue({node:start,dist:0});
|
||||
while(!pq.isEmpty()){
|
||||
const{n:n,d:d}=pq.dequeue();
|
||||
if(d>dist[n])continue;
|
||||
if(n===end)return d;
|
||||
for(let nei in graph[n]){
|
||||
const w=graph[n][nei];
|
||||
const alt=d+w;
|
||||
if(alt<dist[nei]){
|
||||
dist[nei]=alt;
|
||||
pq.queue({n:nei,d:alt});
|
||||
const u=pq.dequeue();
|
||||
if(u.dist>dist[u.node])continue;
|
||||
const neighbors=graph[u.node];
|
||||
if(!neighbors)continue;
|
||||
for(const v in neighbors){
|
||||
const w=neighbors[v];
|
||||
const alt=dist[u.node]+w;
|
||||
if(alt<(dist[v]??Infinity)){
|
||||
dist[v]=alt;
|
||||
pq.queue({node:v,dist:alt});
|
||||
}
|
||||
}
|
||||
}
|
||||
return Infinity;
|
||||
return dist[end]??Infinity;
|
||||
}
|
||||
export default findShortestPath;
|
||||
@@ -1,29 +1,34 @@
|
||||
async function findConvexHull(points) {
|
||||
const _ = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js').then(m => m.default);
|
||||
const { sortBy, uniqWith } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js');
|
||||
|
||||
if (points.length < 3) return points;
|
||||
|
||||
const sorted = _.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);
|
||||
|
||||
const sorted = sortBy(uniqWith(points, (a, b) => a.x === b.x && a.y === b.y), ['x', 'y']);
|
||||
|
||||
if (sorted.length < 3) return sorted;
|
||||
|
||||
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 hull = [];
|
||||
for (const p of pts) {
|
||||
while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
||||
hull.pop();
|
||||
}
|
||||
hull.push(p);
|
||||
const lower = [];
|
||||
for (const p of sorted) {
|
||||
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
|
||||
lower.pop();
|
||||
}
|
||||
hull.pop();
|
||||
return hull;
|
||||
};
|
||||
lower.push(p);
|
||||
}
|
||||
|
||||
const lower = buildHull(sorted);
|
||||
const upper = buildHull([...sorted].reverse());
|
||||
const upper = [];
|
||||
for (let i = sorted.length - 1; i >= 0; i--) {
|
||||
const p = sorted[i];
|
||||
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
|
||||
upper.pop();
|
||||
}
|
||||
upper.push(p);
|
||||
}
|
||||
|
||||
return [...lower, ...upper];
|
||||
lower.pop();
|
||||
upper.pop();
|
||||
|
||||
return lower.concat(upper);
|
||||
}
|
||||
export default findConvexHull;
|
||||
@@ -1,27 +1,28 @@
|
||||
const findConvexHull = async (points) => {
|
||||
const _ = await import('https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js');
|
||||
async const findConvexHull = async (points) => {
|
||||
const { sortBy, uniqWith, isEqual } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js');
|
||||
|
||||
const uniquePts = _.uniqWith(points, _.isEqual);
|
||||
if (uniquePts.length < 3) {
|
||||
return uniquePts;
|
||||
const cross = (p1, p2, p3) =>
|
||||
(p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
|
||||
|
||||
const sortedPoints = sortBy(uniqWith(points, isEqual), ['x', 'y']);
|
||||
|
||||
if (sortedPoints.length <= 3) {
|
||||
return sortedPoints;
|
||||
}
|
||||
|
||||
const crossProduct = (o, a, b) =>
|
||||
(a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
||||
|
||||
const sortedPts = _.sortBy(uniquePts, ['x', 'y']);
|
||||
|
||||
const buildHalfHull = (pts) =>
|
||||
pts.reduce((hull, p) => {
|
||||
while (hull.length >= 2 && crossProduct(hull.at(-2), hull.at(-1), p) <= 0) {
|
||||
const buildHull = (pointSet) => {
|
||||
const hull = [];
|
||||
for (const p of pointSet) {
|
||||
while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
||||
hull.pop();
|
||||
}
|
||||
hull.push(p);
|
||||
return hull;
|
||||
}, []);
|
||||
}
|
||||
return hull;
|
||||
};
|
||||
|
||||
const lower = buildHalfHull(sortedPts);
|
||||
const upper = buildHalfHull([...sortedPts].reverse());
|
||||
const lower = buildHull(sortedPoints);
|
||||
const upper = buildHull([...sortedPoints].reverse());
|
||||
|
||||
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
const findConvexHull = async (pts) => {
|
||||
const { sortBy: S, uniqWith: U, isEqual: E } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm')
|
||||
const p = S(U(pts, E), ['x', 'y'])
|
||||
if (p.length < 3) return p
|
||||
const X = (a, b, o) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
|
||||
const B = (list) => {
|
||||
const h = []
|
||||
for (const pt of list) {
|
||||
while (h.length >= 2 && X(h[h.length - 1], pt, h[h.length - 2]) <= 0) h.pop()
|
||||
h.push(pt)
|
||||
}
|
||||
return h
|
||||
}
|
||||
const l = B(p)
|
||||
const u = B(p.reverse())
|
||||
l.pop()
|
||||
u.pop()
|
||||
return l.concat(u)
|
||||
}
|
||||
export default findConvexHull;
|
||||
@@ -1,22 +0,0 @@
|
||||
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,19 +1,18 @@
|
||||
const findConvexHull = async (pts) => {
|
||||
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);
|
||||
const sorted = sortBy(uniqBy(pts, p => `${p.x},${p.y}`), ['x', 'y']);
|
||||
if (sorted.length < 3) return sorted;
|
||||
async function findConvexHull(pts) {
|
||||
if (!Array.isArray(pts)) return [];
|
||||
const { default: _ } = await import('https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js');
|
||||
const points = _.sortBy(_.uniqWith(pts, (a, b) => a.x === b.x && a.y === b.y), ['x', 'y']);
|
||||
if (points.length < 3) return points;
|
||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
||||
const build = (h, p) => {
|
||||
while (h.length >= 2 && cross(h[h.length - 2], h[h.length - 1], p) <= 0) h.pop();
|
||||
h.push(p);
|
||||
};
|
||||
const lower = [], upper = [];
|
||||
for (const p of sorted) {
|
||||
while (lower.length > 1 && cross(lower[lower.length-2], lower[lower.length-1], p) <= 0) lower.pop();
|
||||
lower.push(p);
|
||||
}
|
||||
for (let i = sorted.length - 1; i >= 0; i--) {
|
||||
const p = sorted[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, ...upper];
|
||||
};
|
||||
for (const p of points) build(lower, p);
|
||||
for (let i = points.length; i--; ) build(upper, points[i]);
|
||||
lower.pop();
|
||||
upper.pop();
|
||||
return lower.concat(upper);
|
||||
}
|
||||
export default findConvexHull;
|
||||
@@ -1,21 +1,19 @@
|
||||
async function findConvexHull(points){
|
||||
const m=await import('https://cdn.skypack.dev/lodash?min')
|
||||
const _=m.default||m
|
||||
const pts=_.sortBy(_.uniqWith(points,(a,b)=>a.x===b.x&&a.y===b.y),['x','y'])
|
||||
if(pts.length<3)return pts.slice()
|
||||
const h=[]
|
||||
const c=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x)
|
||||
for(const p of pts){
|
||||
while(h.length>1&&c(h.at(-2),h.at(-1),p)<=0)h.pop()
|
||||
h.push(p)
|
||||
async function findConvexHull(points) {
|
||||
const { uniqWith, isEqual, sortBy } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js')
|
||||
const pts = sortBy(uniqWith(points, isEqual), ['x', 'y'])
|
||||
if (pts.length < 3) return pts
|
||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
|
||||
const scan = (src, acc = []) => {
|
||||
src.forEach(p => {
|
||||
while (acc.length > 1 && cross(acc.at(-2), acc.at(-1), p) <= 0) acc.pop()
|
||||
acc.push(p)
|
||||
})
|
||||
return acc
|
||||
}
|
||||
const t=h.length+1
|
||||
for(let i=pts.length-2;i>=0;i--){
|
||||
const p=pts[i]
|
||||
while(h.length>=t&&c(h.at(-2),h.at(-1),p)<=0)h.pop()
|
||||
h.push(p)
|
||||
}
|
||||
h.pop()
|
||||
return h
|
||||
const lower = scan(pts)
|
||||
const upper = scan([...pts].reverse())
|
||||
lower.pop()
|
||||
upper.pop()
|
||||
return lower.concat(upper)
|
||||
}
|
||||
export default findConvexHull;
|
||||
@@ -1,22 +1,12 @@
|
||||
async function findConvexHull(points){
|
||||
const{default:L}=await import('https://cdn.skypack.dev/lodash-es');
|
||||
const{sortBy,uniqBy}=L;
|
||||
let p=uniqBy(points,({x,y})=>`${x},${y}`);
|
||||
async function findConvexHull(pts){
|
||||
const{ sortBy:s,uniqBy:u }=await import('https://cdn.skypack.dev/lodash');
|
||||
if(!pts?.length)return[];
|
||||
const p=u(s(pts,['x','y']),t=>`${t.x},${t.y}`);
|
||||
if(p.length<2)return p;
|
||||
p=sortBy(p,['x','y']);
|
||||
const cross=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
|
||||
let l=[];
|
||||
for(let i=0;i<p.length;i++){
|
||||
let pt=p[i];
|
||||
while(l.length>=2&&cross(l[l.length-2],l[l.length-1],pt)<=0)l.pop();
|
||||
l.push(pt);
|
||||
}
|
||||
let u=[];
|
||||
for(let i=p.length-1;i>=0;i--){
|
||||
let pt=p[i];
|
||||
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)];
|
||||
const c=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
|
||||
let l=[];for(let t of p){while(l.length>1&&c(l[l.length-2],l[l.length-1],t)<=0)l.pop();l.push(t)}
|
||||
let r=[];for(let i=p.length-1;i>=0;i--){const t=p[i];while(r.length>1&&c(r[r.length-2],r[r.length-1],t)<=0)r.pop();r.push(t)}
|
||||
l.pop();r.pop();
|
||||
return[...l,...r];
|
||||
}
|
||||
export default findConvexHull;
|
||||
@@ -1,5 +1,5 @@
|
||||
async function findLISLength(arr) {
|
||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||
const findLISLength = async (arr) => {
|
||||
const { bisectLeft } = await import('https://cdn.skypack.dev/d3-array');
|
||||
|
||||
if (!arr?.length) return 0;
|
||||
|
||||
@@ -11,5 +11,5 @@ async function findLISLength(arr) {
|
||||
}
|
||||
|
||||
return tails.length;
|
||||
}
|
||||
};
|
||||
export default findLISLength;
|
||||
@@ -1,16 +1,16 @@
|
||||
async function findLISLength(nums) {
|
||||
const findLISLength = async (nums) => {
|
||||
if (!nums?.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||
|
||||
const sub = [];
|
||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array/+esm');
|
||||
|
||||
for (const num of nums) {
|
||||
sub[bisectLeft(sub, num)] = num;
|
||||
}
|
||||
const tails = [];
|
||||
|
||||
return sub.length;
|
||||
}
|
||||
nums.forEach((num) => {
|
||||
tails[bisectLeft(tails, num)] = num;
|
||||
});
|
||||
|
||||
return tails.length;
|
||||
};
|
||||
export default findLISLength;
|
||||
@@ -0,0 +1,9 @@
|
||||
const findLISLength = async (nums) => {
|
||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||
const tails = [];
|
||||
for (const n of nums) {
|
||||
tails[bisectLeft(tails, n)] = n;
|
||||
}
|
||||
return tails.length;
|
||||
};
|
||||
export default findLISLength;
|
||||
@@ -1,7 +0,0 @@
|
||||
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,12 +1,14 @@
|
||||
export const findLISLength = async (arr) => {
|
||||
if (!Array.isArray(arr) || !arr.length) return 0;
|
||||
try {
|
||||
const { bisectLeft } = await import('https://cdn.skypack.dev/d3-array@3');
|
||||
const t = [];
|
||||
for (const v of arr) t[bisectLeft(t, v)] = v;
|
||||
return t.length;
|
||||
} catch {
|
||||
return 0;
|
||||
export async function findLISLength(nums) {
|
||||
if (!Array.isArray(nums) || nums.length === 0) return 0;
|
||||
|
||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||
const tails = [];
|
||||
|
||||
for (const num of nums) {
|
||||
const idx = bisectLeft(tails, num);
|
||||
idx === tails.length ? tails.push(num) : tails[idx] = num;
|
||||
}
|
||||
};
|
||||
|
||||
return tails.length;
|
||||
}
|
||||
export default findLISLength;
|
||||
@@ -1,11 +1,10 @@
|
||||
const findLISLength = async nums => {
|
||||
if (!Array.isArray(nums)) throw new TypeError('Expected array')
|
||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
||||
const tails = []
|
||||
for (const v of nums) {
|
||||
const i = bisectLeft(tails, v)
|
||||
i === tails.length ? tails.push(v) : tails[i] = v
|
||||
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 tails.length
|
||||
return t.length
|
||||
}
|
||||
export default findLISLength;
|
||||
@@ -1,2 +1,2 @@
|
||||
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;}
|
||||
async function findLISLength(a){const{bisectLeft}=await import('https://cdn.skypack.dev/d3-array');let t=[];for(let n of a){let i=bisectLeft(t,n);i===t.length?t.push(n):t[i]=n}return t.length}
|
||||
export default findLISLength;
|
||||
@@ -1,5 +1,5 @@
|
||||
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/+esm');
|
||||
return det(matrix);
|
||||
};
|
||||
export default calculateDeterminant;
|
||||
@@ -0,0 +1,5 @@
|
||||
const calculateDeterminant = async m => {
|
||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs/+esm')
|
||||
return det(m)
|
||||
}
|
||||
export default calculateDeterminant;
|
||||
@@ -1,5 +0,0 @@
|
||||
const calculateDeterminant = async m => {
|
||||
const { det } = await import('https://esm.sh/mathjs')
|
||||
return det(m)
|
||||
}
|
||||
export default calculateDeterminant;
|
||||
@@ -1,13 +1,11 @@
|
||||
const URL = 'https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm';
|
||||
let det;
|
||||
let mathLib;
|
||||
|
||||
async function calculateDeterminant(m) {
|
||||
const v = Array.isArray(m) && m.length && m.every(r => Array.isArray(r) && r.length === m.length);
|
||||
if (!v) throw new TypeError('Invalid matrix');
|
||||
if (!Array.isArray(m) || !m.length || m.some(r => !Array.isArray(r) || r.length !== m.length)) {
|
||||
throw new TypeError('Invalid square matrix');
|
||||
}
|
||||
|
||||
det ||= await import(URL).then(m => m.det).catch(() => { throw new Error('Load failed'); });
|
||||
|
||||
try { return det(m); }
|
||||
catch { throw new Error('Calc failed'); }
|
||||
const {det} = await (mathLib ||= import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/lib/browser/math.js'));
|
||||
return det(m);
|
||||
}
|
||||
export default calculateDeterminant;
|
||||
@@ -1,5 +1,3 @@
|
||||
async function calculateDeterminant(m){
|
||||
const {det}=await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.1/+esm')
|
||||
return det(m)
|
||||
}
|
||||
let d
|
||||
const calculateDeterminant=async m=> (await (d??=import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm'))).det(m)
|
||||
export default calculateDeterminant;
|
||||
@@ -1,2 +1,2 @@
|
||||
async function calculateDeterminant(matrix){const math=await import('https://esm.sh/mathjs');return math.det(matrix);}
|
||||
async function calculateDeterminant(m){const{det}=await import('https://esm.sh/mathjs');return det(m);}
|
||||
export default calculateDeterminant;
|
||||
@@ -1,5 +1,5 @@
|
||||
const parseMarkdown = async (md) => {
|
||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11/+esm');
|
||||
async function parseMarkdown(md) {
|
||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11.1.1/+esm');
|
||||
return marked.parse(md);
|
||||
};
|
||||
}
|
||||
export default parseMarkdown;
|
||||
@@ -1,12 +1,24 @@
|
||||
const parseMarkdown = async (markdownText) => {
|
||||
const cdnUrl = 'https://cdn.jsdelivr.net/npm/marked@13.0.2/lib/marked.esm.js';
|
||||
|
||||
const parseMarkdown = async (md) => {
|
||||
try {
|
||||
const { marked } = await import(cdnUrl);
|
||||
return marked.parse(markdownText || '');
|
||||
const [
|
||||
{ marked },
|
||||
{ default: DOMPurify }
|
||||
] = await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/marked@13.0.0/lib/marked.esm.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/dompurify@3.1.5/dist/purify.es.mjs')
|
||||
]);
|
||||
|
||||
const rawHtml = marked.parse(md || '', {
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
});
|
||||
|
||||
return DOMPurify.sanitize(rawHtml);
|
||||
} catch (err) {
|
||||
console.error(`Failed to parse markdown: ${err}`);
|
||||
return markdownText || '';
|
||||
console.error('Failed to parse markdown:', err);
|
||||
const el = document.createElement('div');
|
||||
el.textContent = md || '';
|
||||
return el.innerHTML;
|
||||
}
|
||||
};
|
||||
export default parseMarkdown;
|
||||
@@ -0,0 +1,5 @@
|
||||
const parseMarkdown = async text => {
|
||||
const { parse } = await import('https://esm.sh/marked')
|
||||
return parse(text)
|
||||
}
|
||||
export default parseMarkdown;
|
||||
@@ -1,5 +0,0 @@
|
||||
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,13 +1,15 @@
|
||||
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};
|
||||
|
||||
if (!md?.trim()) return '';
|
||||
try {
|
||||
const {marked} = await import(u);
|
||||
marked.setOptions(o);
|
||||
return marked.parse(md);
|
||||
} catch(e) {
|
||||
return `<p>${e.message}</p>`;
|
||||
const [{ marked }, { default: DOMPurify }] = await Promise.all([
|
||||
import('https://esm.sh/marked@9'),
|
||||
import('https://esm.sh/dompurify@3')
|
||||
]);
|
||||
marked.setOptions({ gfm: true, breaks: true });
|
||||
return DOMPurify.sanitize(marked.parse(md));
|
||||
} catch (e) {
|
||||
console.error('Markdown parse error:', e);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
export default parseMarkdown;
|
||||
@@ -1,5 +1,12 @@
|
||||
const parseMarkdown=async m=>{
|
||||
const {marked}=await import('https://cdn.jsdelivr.net/npm/marked@11.1.0/lib/marked.esm.js')
|
||||
return marked.parse(m)
|
||||
}
|
||||
const parseMarkdown=(()=>{
|
||||
let m,r
|
||||
return async t=>{
|
||||
if(!r){
|
||||
({marked:m}=await import('https://cdn.jsdelivr.net/npm/marked@11.1.1/+esm'))
|
||||
m.setOptions({gfm:1,mangle:0,headerIds:0})
|
||||
r=1
|
||||
}
|
||||
return m.parse(t)
|
||||
}
|
||||
})()
|
||||
export default parseMarkdown;
|
||||
@@ -1,5 +1,6 @@
|
||||
async function parseMarkdown(md){
|
||||
const{marked}=await import('https://esm.sh/marked');
|
||||
return marked.parse(md);
|
||||
const{DOMPurify}=await import('https://esm.sh/dompurify');
|
||||
return DOMPurify.sanitize(marked.parse(md));
|
||||
}
|
||||
export default parseMarkdown;
|
||||
@@ -1,23 +1,26 @@
|
||||
const processCSV = async (csvString, { filterColumn, filterValue, groupBy, aggregateColumn, operation }) => {
|
||||
const { default: Papa } = await import('https://cdn.jsdelivr.net/npm/[email protected]/+esm');
|
||||
|
||||
const { data } = Papa.parse(csvString, { header: true, skipEmptyLines: true });
|
||||
const processCSV = async (csv, { filterColumn, filterValue, groupBy, aggregateColumn, operation }) => {
|
||||
const [{ default: Papa }] = await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm')
|
||||
]);
|
||||
|
||||
const { data } = Papa.parse(csv, { header: true, skipEmptyLines: true });
|
||||
|
||||
const filtered = data.filter(row => row[filterColumn] == filterValue);
|
||||
|
||||
const groups = filtered.reduce((acc, row) => {
|
||||
const key = row[groupBy];
|
||||
(acc[key] ??= []).push(row);
|
||||
if (!acc[key]) acc[key] = [];
|
||||
acc[key].push(row);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
|
||||
return Object.entries(groups).map(([key, rows]) => {
|
||||
const values = rows.map(r => +r[aggregateColumn] || 0);
|
||||
const values = rows.map(r => Number(r[aggregateColumn]) || 0);
|
||||
const sum = values.reduce((a, b) => a + b, 0);
|
||||
|
||||
return {
|
||||
[groupBy]: key,
|
||||
result: operation === 'sum' ? sum : operation === 'avg' ? sum / values.length : rows.length
|
||||
result: operation === 'sum' ? sum : operation === 'avg' ? sum / values.length : values.length
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
async function processCSV(csvString, config) {
|
||||
const {
|
||||
filterColumn,
|
||||
filterValue,
|
||||
groupBy,
|
||||
aggregateColumn,
|
||||
operation
|
||||
filterColumn: fc,
|
||||
filterValue: fv,
|
||||
groupBy: gb,
|
||||
aggregateColumn: ac,
|
||||
operation: op
|
||||
} = config;
|
||||
|
||||
const { default: Papa } = await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm');
|
||||
const { default: Papa } = await import('https://cdn.jsdelivr.net/npm/papaparse@5.3.2/papaparse.min.js');
|
||||
|
||||
const { data } = Papa.parse(csvString, {
|
||||
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;
|
||||
const groups = data.reduce((acc, row) => {
|
||||
if (row[fc] == fv) {
|
||||
const key = row[gb];
|
||||
const val = Number(row[ac]) || 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;
|
||||
}, {});
|
||||
acc[key] = acc[key] || { sum: 0, count: 0 };
|
||||
acc[key].sum += val;
|
||||
acc[key].count++;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.entries(groups).map(([groupValue, aggData]) => ({
|
||||
[groupBy]: groupValue,
|
||||
result: operation === 'avg' ? aggData.sum / aggData.count : aggData,
|
||||
return Object.entries(groups).map(([key, { sum, count }]) => ({
|
||||
[gb]: key,
|
||||
result: op === 'avg' ? sum / count : op === 'sum' ? sum : count,
|
||||
}));
|
||||
}
|
||||
export default processCSV;
|
||||
@@ -0,0 +1,19 @@
|
||||
export const processCSV = async (csvString, { filterColumn, filterValue, groupBy, aggregateColumn, operation }) => {
|
||||
const [{ csvParse }, { rollup, sum, mean }] = await Promise.all([
|
||||
import('https://esm.sh/d3-dsv'),
|
||||
import('https://esm.sh/d3-array')
|
||||
]);
|
||||
|
||||
const getValue = d => +(d[aggregateColumn]) || 0;
|
||||
|
||||
const grouped = rollup(
|
||||
csvParse(csvString).filter(row => row[filterColumn] == filterValue),
|
||||
group => operation === 'count'
|
||||
? group.length
|
||||
: (operation === 'sum' ? sum : mean)(group, getValue),
|
||||
row => row[groupBy]
|
||||
);
|
||||
|
||||
return Array.from(grouped, ([key, result]) => ({ [groupBy]: key, result }));
|
||||
};
|
||||
export default processCSV;
|
||||
@@ -1,22 +0,0 @@
|
||||
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,15 +1,2 @@
|
||||
async function processCSV(csv, { filterColumn:f, filterValue:v, groupBy:g, aggregateColumn:a, operation:o }) {
|
||||
const [{ default: Papa }, { default: _ }] = await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
||||
import('https://cdn.jsdelivr.net/npm/lodash@4.17.21/+esm')
|
||||
]);
|
||||
|
||||
const data = Papa.parse(csv, { header:true, dynamicTyping:true }).data.filter(r => r[f] == v);
|
||||
|
||||
return Object.entries(_.groupBy(data, g)).map(([k, d]) => {
|
||||
const n = d.map(r => (x = Number(r[a]), isNaN(x) ? 0 : x));
|
||||
const s = n.reduce((p, c) => p + c, 0);
|
||||
return { [g]: k, result: o === 'count' ? d.length : o === 'avg' ? s / n.length : s };
|
||||
});
|
||||
}
|
||||
async function processCSV(csv,{filterColumn:f,filterValue:v,groupBy:g,aggregateColumn:a,operation:o}){const{default:P}=await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm');const{data}=P.parse(csv,{header:true});const grouped=data.filter(r=>r[f]==v).reduce((c,r)=>{const k=r[g],n=Number(r[a])||0;c[k]||=(c[k]={s:0,c:0});c[k].s+=n;c[k].c++;return c},{});return Object.entries(grouped).map(([k,{s,c}])=>({[g]:k,result:o=='sum'?s:o=='avg'?s/c:c}))}
|
||||
export default processCSV;
|
||||
@@ -1,20 +1,21 @@
|
||||
const libCache={},load=u=>libCache[u]??=import(u);
|
||||
|
||||
async function processCSV(csv,cfg){
|
||||
const[{default:Papa},{groupBy}]=await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
||||
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm')
|
||||
]);
|
||||
const{filterColumn:f,filterValue:v,groupBy:g,aggregateColumn:a,operation:o}=cfg;
|
||||
const rows=Papa.parse(csv,{header:true,skipEmptyLines:true}).data.filter(r=>r[f]==v);
|
||||
const groups=groupBy(rows,r=>r[g]);
|
||||
return Object.entries(groups).map(([k,list])=>{
|
||||
const nums=list.map(r=>{
|
||||
const n=Number(r[a]);
|
||||
return Number.isFinite(n)?n:0;
|
||||
const [{csvParse},{groupBy}]=await Promise.all([
|
||||
load('https://cdn.jsdelivr.net/npm/d3-dsv@3.0.1/+esm'),
|
||||
load('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm')
|
||||
]);
|
||||
const {filterColumn:f,filterValue:v,groupBy:k,aggregateColumn:a,operation:o}=cfg;
|
||||
const rows=csvParse(csv).filter(r=>r[f]==v);
|
||||
const grouped=groupBy(rows,r=>r[k]);
|
||||
return Object.entries(grouped).map(([g,list])=>{
|
||||
const nums=list.map(r=>{
|
||||
const n=+r[a];
|
||||
return Number.isFinite(n)?n:0;
|
||||
});
|
||||
const sum=nums.reduce((s,n)=>s+n,0);
|
||||
const result=o==='count'?list.length:o==='sum'?sum:list.length?sum/list.length:0;
|
||||
return {[k]:g,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;
|
||||
@@ -1,27 +1,17 @@
|
||||
async function processCSV(csv,{filterColumn,filterValue,groupBy,aggregateColumn,operation}){
|
||||
const Papa=(await import('https://cdn.jsdelivr.net/npm/papaparse@5/+esm')).default;
|
||||
const{data}=Papa.parse(csv,{header:true,skipEmptyLines:true});
|
||||
const groups=new Map();
|
||||
for(const row of data){
|
||||
if(row[filterColumn]==filterValue){
|
||||
const key=row[groupBy];
|
||||
const val=Number(row[aggregateColumn])||0;
|
||||
const g=groups.get(key)||{sum:0,count:0};
|
||||
g.sum+=val;
|
||||
g.count++;
|
||||
groups.set(key,g);
|
||||
}
|
||||
async function processCSV(csv,config){
|
||||
const Papa=(await import('https://cdn.skypack.dev/papaparse')).default;
|
||||
const parsed=Papa.parse(csv,{header:!0}).data;
|
||||
const{filterColumn:fc,filterValue:fv,groupBy:gb,aggregateColumn:ac,operation:op}=config;
|
||||
const filtered=parsed.filter(r=>r[fc]==fv);
|
||||
const groups=new Map;
|
||||
for(const r of filtered){
|
||||
const gv=r[gb];
|
||||
const v=Number(r[ac])||0;
|
||||
const g=groups.get(gv)||{sum:0,count:0};
|
||||
g.sum+=v;
|
||||
g.count++;
|
||||
groups.set(gv,g);
|
||||
}
|
||||
const result=[];
|
||||
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;
|
||||
return Array.from(groups,(g,gv)=>(({[gb]:gv,result:op==='sum'?g.sum:op==='count'?g.count:g.sum/g.count})));
|
||||
}
|
||||
export default processCSV;
|
||||
@@ -1,71 +1,69 @@
|
||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||
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');
|
||||
const findAvailableSlots = async (calendar1, calendar2, constraints) => {
|
||||
const { startOfDay, addMinutes, parseISO, formatISO } = await import('https://cdn.jsdelivr.net/npm/date-fns@3.0.0/+esm');
|
||||
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(customParseFormat);
|
||||
|
||||
const { durationMinutes, searchRange, workHours } = constraints;
|
||||
const searchStart = dayjs(searchRange.start);
|
||||
const searchEnd = dayjs(searchRange.end);
|
||||
|
||||
const allBusy = [...cal1, ...cal2]
|
||||
.map(({ start, end }) => ({ start: dayjs(start), end: dayjs(end) }))
|
||||
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
||||
|
||||
const searchStart = parseISO(searchRange.start);
|
||||
const searchEnd = parseISO(searchRange.end);
|
||||
|
||||
const [workStartHour, workStartMin] = workHours.start.split(':').map(Number);
|
||||
const [workEndHour, workEndMin] = workHours.end.split(':').map(Number);
|
||||
const workStartMs = (workStartHour * 60 + workStartMin) * 60000;
|
||||
const workEndMs = (workEndHour * 60 + workEndMin) * 60000;
|
||||
|
||||
const allBusy = [...calendar1, ...calendar2]
|
||||
.map(({ start, end }) => ({ start: parseISO(start), end: parseISO(end) }))
|
||||
.sort((a, b) => a.start - b.start);
|
||||
|
||||
const merged = [];
|
||||
for (const slot of allBusy) {
|
||||
if (!merged.length || merged[merged.length - 1].end.isBefore(slot.start)) {
|
||||
merged.push({ start: slot.start, end: slot.end });
|
||||
if (merged.length && slot.start <= merged[merged.length - 1].end) {
|
||||
merged[merged.length - 1].end = new Date(Math.max(merged[merged.length - 1].end, slot.end));
|
||||
} else {
|
||||
merged[merged.length - 1].end = dayjs.max(merged[merged.length - 1].end, slot.end);
|
||||
merged.push({ start: slot.start, end: slot.end });
|
||||
}
|
||||
}
|
||||
|
||||
const freePeriods = [];
|
||||
|
||||
const free = [];
|
||||
let current = searchStart;
|
||||
|
||||
for (const busy of merged) {
|
||||
if (current.isBefore(busy.start)) {
|
||||
freePeriods.push({ start: current, end: busy.start });
|
||||
if (current < busy.start) {
|
||||
free.push({ start: current, end: busy.start });
|
||||
}
|
||||
current = dayjs.max(current, busy.end);
|
||||
current = new Date(Math.max(current, busy.end));
|
||||
}
|
||||
if (current < searchEnd) {
|
||||
free.push({ start: current, end: searchEnd });
|
||||
}
|
||||
|
||||
if (current.isBefore(searchEnd)) {
|
||||
freePeriods.push({ start: current, end: searchEnd });
|
||||
}
|
||||
|
||||
const isWithinWorkHours = (slotStart, slotEnd) => {
|
||||
const date = slotStart.format('YYYY-MM-DD');
|
||||
const workStart = dayjs(`${date} ${workHours.start}`, 'YYYY-MM-DD HH:mm');
|
||||
const workEnd = dayjs(`${date} ${workHours.end}`, 'YYYY-MM-DD HH:mm');
|
||||
|
||||
return !slotStart.isBefore(workStart) && !slotEnd.isAfter(workEnd);
|
||||
};
|
||||
|
||||
const slots = [];
|
||||
|
||||
for (const period of freePeriods) {
|
||||
for (const period of free) {
|
||||
let slotStart = period.start;
|
||||
|
||||
while (slotStart.add(durationMinutes, 'minute').isSameOrBefore(period.end)) {
|
||||
const slotEnd = slotStart.add(durationMinutes, 'minute');
|
||||
while (slotStart < period.end) {
|
||||
const dayStart = startOfDay(slotStart);
|
||||
const slotStartMs = slotStart - dayStart;
|
||||
const slotEnd = addMinutes(slotStart, durationMinutes);
|
||||
const slotEndMs = slotEnd - dayStart;
|
||||
|
||||
if (isWithinWorkHours(slotStart, slotEnd) &&
|
||||
!slotStart.isBefore(searchStart) &&
|
||||
!slotEnd.isAfter(searchEnd)) {
|
||||
if (slotEnd > period.end) break;
|
||||
|
||||
const endsNextDay = slotEnd - startOfDay(slotEnd) < slotEndMs % 86400000;
|
||||
|
||||
if (!endsNextDay &&
|
||||
slotStartMs >= workStartMs &&
|
||||
slotEndMs <= workEndMs &&
|
||||
slotStart >= searchStart &&
|
||||
slotEnd <= searchEnd) {
|
||||
slots.push({
|
||||
start: slotStart.toISOString(),
|
||||
end: slotEnd.toISOString()
|
||||
start: formatISO(slotStart),
|
||||
end: formatISO(slotEnd)
|
||||
});
|
||||
}
|
||||
|
||||
slotStart = slotEnd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return slots;
|
||||
};
|
||||
export default findAvailableSlots;
|
||||
@@ -1,58 +1,74 @@
|
||||
async function findAvailableSlots(calendar1, calendar2, constraints) {
|
||||
const {
|
||||
parseISO, addMinutes, formatISO, max, setHours, setMinutes,
|
||||
setSeconds, setMilliseconds, startOfDay, endOfDay, eachDayOfInterval,
|
||||
} = await import('https://cdn.jsdelivr.net/npm/date-fns@2/esm/index.js');
|
||||
parseISO, addMinutes, addDays, max, isBefore, isAfter, startOfDay,
|
||||
} = await import('https://cdn.jsdelivr.net/npm/date-fns@3.6.0/esm/index.js');
|
||||
|
||||
const { durationMinutes: duration, searchRange, workHours } = constraints;
|
||||
const { durationMinutes, searchRange, workHours } = constraints;
|
||||
const searchStart = parseISO(searchRange.start);
|
||||
const searchEnd = parseISO(searchRange.end);
|
||||
const [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
||||
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
||||
|
||||
const setTime = (date, h, m) =>
|
||||
setMilliseconds(setSeconds(setMinutes(setHours(date, h), m), 0), 0);
|
||||
|
||||
const busySlots = [...calendar1, ...calendar2].map(({ start, end }) => ({
|
||||
start: parseISO(start),
|
||||
end: parseISO(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]
|
||||
const busySlots = [...calendar1, ...calendar2]
|
||||
.map(slot => ({ start: parseISO(slot.start), end: parseISO(slot.end) }))
|
||||
.sort((a, b) => a.start - b.start);
|
||||
|
||||
const merged = allUnavailable.reduce((acc, current) => {
|
||||
const mergedBusy = busySlots.reduce((acc, current) => {
|
||||
const last = acc.at(-1);
|
||||
if (last && current.start <= last.end) {
|
||||
last.end = max(last.end, current.end);
|
||||
if (last && isBefore(current.start, last.end)) {
|
||||
last.end = max([last.end, current.end]);
|
||||
} else {
|
||||
acc.push({ ...current });
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const availableSlots = [];
|
||||
const freeIntervals = [];
|
||||
let cursor = searchStart;
|
||||
|
||||
[...merged, { start: searchEnd, end: searchEnd }].forEach(block => {
|
||||
let slotStart = cursor;
|
||||
while (addMinutes(slotStart, duration) <= block.start) {
|
||||
const slotEnd = addMinutes(slotStart, duration);
|
||||
availableSlots.push({ start: slotStart, end: slotEnd });
|
||||
slotStart = slotEnd;
|
||||
mergedBusy.forEach(busy => {
|
||||
if (isBefore(cursor, busy.start)) {
|
||||
freeIntervals.push({ start: cursor, end: busy.start });
|
||||
}
|
||||
cursor = max(cursor, block.end);
|
||||
cursor = max([cursor, busy.end]);
|
||||
});
|
||||
|
||||
return availableSlots.map(({ start, end }) => ({
|
||||
start: formatISO(start),
|
||||
end: formatISO(end),
|
||||
if (isBefore(cursor, searchEnd)) {
|
||||
freeIntervals.push({ start: cursor, end: searchEnd });
|
||||
}
|
||||
|
||||
const availableSlots = freeIntervals.flatMap(gap => {
|
||||
const slotsInGap = [];
|
||||
let slotCursor = gap.start;
|
||||
|
||||
while (isBefore(slotCursor, gap.end)) {
|
||||
const day = startOfDay(slotCursor);
|
||||
const workDayStart = new Date(day);
|
||||
workDayStart.setUTCHours(workStartH, workStartM, 0, 0);
|
||||
|
||||
const workDayEnd = new Date(day);
|
||||
workDayEnd.setUTCHours(workEndH, workEndM, 0, 0);
|
||||
|
||||
const slotStart = max([slotCursor, workDayStart]);
|
||||
const slotEnd = addMinutes(slotStart, durationMinutes);
|
||||
|
||||
const canFit = !isAfter(slotEnd, workDayEnd) && !isAfter(slotEnd, gap.end);
|
||||
|
||||
if (canFit) {
|
||||
slotsInGap.push({ start: slotStart, end: slotEnd });
|
||||
slotCursor = slotEnd;
|
||||
} else {
|
||||
const nextDayStart = addDays(day, 1);
|
||||
nextDayStart.setUTCHours(workStartH, workStartM, 0, 0);
|
||||
slotCursor = nextDayStart;
|
||||
}
|
||||
}
|
||||
return slotsInGap;
|
||||
});
|
||||
|
||||
return availableSlots.map(slot => ({
|
||||
start: slot.start.toISOString(),
|
||||
end: slot.end.toISOString(),
|
||||
}));
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
@@ -0,0 +1,52 @@
|
||||
const findAvailableSlots = async (cal1, cal2, { durationMinutes: dur, searchRange: range, workHours: work }) => {
|
||||
const { parseISO } = await import('https://cdn.jsdelivr.net/npm/date-fns@3.6.0/+esm');
|
||||
|
||||
const toMs = (d) => parseISO(d).getTime();
|
||||
const [wsH, wsM] = work.start.split(':').map(Number);
|
||||
const [weH, weM] = work.end.split(':').map(Number);
|
||||
const rangeStart = toMs(range.start);
|
||||
const rangeEnd = toMs(range.end);
|
||||
const durMs = dur * 60000;
|
||||
|
||||
const busy = [...cal1, ...cal2]
|
||||
.map(x => ({ s: toMs(x.start), e: toMs(x.end) }))
|
||||
.sort((a, b) => a.s - b.s);
|
||||
|
||||
const merged = [];
|
||||
for (const b of busy) {
|
||||
const last = merged[merged.length - 1];
|
||||
if (last && b.s < last.e) last.e = Math.max(last.e, b.e);
|
||||
else merged.push(b);
|
||||
}
|
||||
|
||||
const slots = [];
|
||||
let currDate = parseISO(range.start);
|
||||
currDate.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
while (currDate.getTime() < rangeEnd) {
|
||||
const wStart = new Date(currDate).setUTCHours(wsH, wsM, 0, 0);
|
||||
const wEnd = new Date(currDate).setUTCHours(weH, weM, 0, 0);
|
||||
|
||||
let t = Math.max(wStart, rangeStart);
|
||||
const limit = Math.min(wEnd, rangeEnd);
|
||||
|
||||
while (t + durMs <= limit) {
|
||||
const tEnd = t + durMs;
|
||||
const clash = merged.find(b => t < b.e && tEnd > b.s);
|
||||
|
||||
if (clash) {
|
||||
t = clash.e;
|
||||
} else {
|
||||
slots.push({
|
||||
start: new Date(t).toISOString(),
|
||||
end: new Date(tEnd).toISOString()
|
||||
});
|
||||
t += durMs;
|
||||
}
|
||||
}
|
||||
currDate.setUTCDate(currDate.getUTCDate() + 1);
|
||||
}
|
||||
|
||||
return slots;
|
||||
};
|
||||
export default findAvailableSlots;
|
||||
@@ -1,48 +0,0 @@
|
||||
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,59 +1,11 @@
|
||||
async function findAvailableSlots(cal1, cal2, constraints) {
|
||||
const { parseISO, addMinutes, set, max, min, startOfDay } = await import('https://cdn.skypack.dev/date-fns');
|
||||
|
||||
const { durationMinutes, searchRange, workHours } = constraints;
|
||||
const toDate = d => parseISO(d);
|
||||
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 merged = [...cal1, ...cal2]
|
||||
.map(({ start, end }) => ({ s: toDate(start), e: toDate(end) }))
|
||||
.sort((a, b) => a.s - b.s)
|
||||
.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 slots = [];
|
||||
for (const { s, e } of gaps) {
|
||||
let day = startOfDay(s);
|
||||
const lastDay = startOfDay(e);
|
||||
|
||||
while (day <= lastDay) {
|
||||
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 });
|
||||
|
||||
if (ws < we) {
|
||||
const effS = max([ws, s]);
|
||||
const effE = min([we, e]);
|
||||
|
||||
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;
|
||||
async function findAvailableSlots(a,b,{durationMinutes:d,searchRange:r,workHours:w}){
|
||||
const{parseISO:p,formatISO:f,addMinutes:m,min:n,max:x,startOfDay:s}=await import('https://esm.sh/date-fns');
|
||||
const busy=[...a,...b].map(({start:S,end:E})=>({s:p(S),e:p(E)})).sort((q,y)=>q.s-y.s).reduce((c,{s:S,e:E})=>{
|
||||
const l=c[c.length-1];if(!l||S>l.e)c.push({s:S,e:E});else l.e=x([l.e,E]);return c;
|
||||
},[]);
|
||||
const rs=p(r.start),re=p(r.end),[ws,we]=[w.start,w.end].map(t=>{const[h,m]=t.split(':').map(Number);return h*60+m;});
|
||||
const free=[];let cur=rs;for(const{s:S,e:E}of busy){S>cur&&free.push({s:cur,e:n([S,re])});cur=x([cur,E]);if(cur>=re)break;}
|
||||
cur<re&&free.push({s:cur,e:re});const slots=[];for(const{s:fs,e:fe}of free){let st=fs;while(m(st,d)<=fe){const en=m(st,d);const minutes=(st-s(st))/6e4;if(minutes>=ws&&minutes+d<=we)slots.push({start:f(st),end:f(en)});st=en;}}
|
||||
return slots;
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
@@ -1,44 +1,66 @@
|
||||
async function findAvailableSlots(a,b,c){
|
||||
const {DateTime}=await import('https://cdn.skypack.dev/luxon')
|
||||
const {durationMinutes:d,searchRange:s,workHours:w}=c
|
||||
const r=[DateTime.fromISO(s.start),DateTime.fromISO(s.end)]
|
||||
const hm=q=>q.split(':').map(Number)
|
||||
const [sh,sm]=hm(w.start)
|
||||
const [eh,em]=hm(w.end)
|
||||
const clamp=o=>{
|
||||
let x=DateTime.fromISO(o.start)
|
||||
let y=DateTime.fromISO(o.end)
|
||||
if(+y<=+r[0]||+x>=+r[1])return
|
||||
if(+x<+r[0])x=r[0]
|
||||
if(+y>+r[1])y=r[1]
|
||||
if(+y<=+x)return
|
||||
return{ s:x,e:y}
|
||||
const useDayjs=(()=>{
|
||||
let p
|
||||
return()=>p||(p=(async()=>{
|
||||
const [{default:d},{default:u}]=await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/dayjs@1/esm/index.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/dayjs@1/esm/plugin/utc/index.js')
|
||||
])
|
||||
d.extend(u)
|
||||
return d
|
||||
})())
|
||||
})()
|
||||
|
||||
async function findAvailableSlots(c1=[],c2=[],cfg={}){
|
||||
const d=await useDayjs()
|
||||
const {durationMinutes:dur,searchRange:r={},workHours:w={}}=cfg
|
||||
const {start:rs,end:re}=r
|
||||
const {start:ws,end:we}=w
|
||||
if(!dur||dur<=0||!rs||!re||!ws||!we)return []
|
||||
const s=d.utc(rs),e=d.utc(re)
|
||||
if(!s.isValid()||!e.isValid()||e.valueOf()<=s.valueOf())return []
|
||||
const rangeStart=s.valueOf(),rangeEnd=e.valueOf(),min=60000
|
||||
const clip=v=>{
|
||||
if(!v||!v.start||!v.end)return 0
|
||||
const a=d.utc(v.start),b=d.utc(v.end)
|
||||
if(!a.isValid()||!b.isValid())return 0
|
||||
const st=Math.max(rangeStart,a.valueOf()),en=Math.min(rangeEnd,b.valueOf())
|
||||
return en>st?{start:st,end:en}:0
|
||||
}
|
||||
const busy=[...c1,...c2].map(clip).filter(Boolean).sort((x,y)=>x.start-y.start)
|
||||
const merged=[]
|
||||
;[...a,...b].map(clamp).filter(Boolean).sort((x,y)=>+x.s-+y.s).forEach(v=>{
|
||||
const last=merged.at(-1)
|
||||
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()})
|
||||
for(const slot of busy){
|
||||
const last=merged[merged.length-1]
|
||||
if(!last||slot.start>last.end)merged.push({...slot})
|
||||
else if(slot.end>last.end)last.end=slot.end
|
||||
}
|
||||
const free=[]
|
||||
let cur=rangeStart
|
||||
for(const slot of merged){
|
||||
if(slot.start>cur)free.push({start:cur,end:slot.start})
|
||||
cur=Math.max(cur,slot.end)
|
||||
}
|
||||
if(cur<rangeEnd)free.push({start:cur,end:rangeEnd})
|
||||
const minutes=t=>{const [h,m]=t.split(':').map(Number);return h*60+m}
|
||||
const workStart=minutes(ws),workEnd=minutes(we)
|
||||
if(workStart>=workEnd)return []
|
||||
const out=[]
|
||||
for(const span of free){
|
||||
let day=d.utc(span.start).startOf('day')
|
||||
while(day.valueOf()<span.end){
|
||||
const dayStart=day.add(workStart,'minute'),dayEnd=day.add(workEnd,'minute')
|
||||
const winStart=Math.max(dayStart.valueOf(),span.start),winEnd=Math.min(dayEnd.valueOf(),span.end)
|
||||
if(winEnd-winStart>=dur*min){
|
||||
let slotStart=d.utc(winStart)
|
||||
while(true){
|
||||
const slotEnd=slotStart.add(dur,'minute')
|
||||
if(slotEnd.valueOf()>winEnd)break
|
||||
out.push({start:slotStart.toISOString(),end:slotEnd.toISOString()})
|
||||
slotStart=slotEnd
|
||||
}
|
||||
}
|
||||
day=day.add(1,'day')
|
||||
}
|
||||
})
|
||||
return slots
|
||||
}
|
||||
return out
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
@@ -1,69 +1,55 @@
|
||||
async function findAvailableSlots(cal1, cal2, cons) {
|
||||
const { DateTime: DT, Interval: IV, Duration: D } = await import('https://esm.sh/luxon@3.4.4');
|
||||
const dur = D.fromObject({ minutes: cons.durationMinutes });
|
||||
const sr = IV.fromISO(`${cons.searchRange.start}/${cons.searchRange.end}`);
|
||||
const [h1, m1] = cons.workHours.start.split(':').map(Number);
|
||||
const [h2, m2] = cons.workHours.end.split(':').map(Number);
|
||||
let busies = [...cal1, ...cal2].map(e => IV.fromISO(`${e.start}/${e.end}`))
|
||||
.filter(iv => iv?.overlaps(sr))
|
||||
.map(iv => iv.intersection(sr))
|
||||
.filter(iv => iv && !iv.isEmpty)
|
||||
.sort((a, b) => a.start.toMillis() - b.start.toMillis());
|
||||
let merged = [];
|
||||
for (let iv of busies) {
|
||||
if (!merged.length) {
|
||||
merged.push(iv);
|
||||
continue;
|
||||
}
|
||||
let last = merged[merged.length - 1];
|
||||
if (last.end >= iv.start) {
|
||||
const newEnd = last.end.toMillis() > iv.end.toMillis() ? last.end : iv.end;
|
||||
merged[merged.length - 1] = IV.fromDateTimes(last.start, newEnd);
|
||||
} else {
|
||||
async function findAvailableSlots(cal1, cal2, {durationMinutes:dm, searchRange, workHours}) {
|
||||
const {DateTime:DT, Interval, Duration} = await import('https://cdn.skypack.dev/luxon');
|
||||
const dur=Duration.fromObject({minutes:dm});
|
||||
const srS=DT.fromISO(searchRange.start).toUTC();
|
||||
const srE=DT.fromISO(searchRange.end).toUTC();
|
||||
const srI=Interval.fromDateTimes(srS,srE);
|
||||
let busies=[...cal1,...cal2].map(s=>{
|
||||
const a=DT.fromISO(s.start).toUTC(),b=DT.fromISO(s.end).toUTC();
|
||||
return a<b?Interval.fromDateTimes(a,b).intersection(srI):null;
|
||||
}).filter(Boolean);
|
||||
const [ws,we]=[workHours.start,workHours.end];
|
||||
const hms=+ws.slice(0,2),mms=+ws.slice(3,5),hme=+we.slice(0,2),mme=+we.slice(3,5);
|
||||
let cur=srS.startOf('day');
|
||||
while(cur<srE){
|
||||
const dayE=cur.plus({days:1});
|
||||
const dayI=Interval.fromDateTimes(cur,dayE).intersection(srI);
|
||||
if(dayI.isEmpty) {cur=cur.plus({days:1});continue;}
|
||||
const wsT=cur.set({hour:hms,minute:mms,second:0,millisecond:0});
|
||||
const weT=cur.set({hour:hme,minute:mme,second:0,millisecond:0});
|
||||
const workI=Interval.fromDateTimes(wsT,weT).intersection(dayI);
|
||||
if(!workI?.isValid||workI.isEmpty){
|
||||
busies.push(dayI);
|
||||
}else{
|
||||
if(dayI.start<workI.start)busies.push(Interval.fromDateTimes(dayI.start,workI.start));
|
||||
if(workI.end<dayI.end)busies.push(Interval.fromDateTimes(workI.end,dayI.end));
|
||||
}
|
||||
cur=cur.plus({days:1});
|
||||
}
|
||||
busies.sort((a,b)=>a.start.toMillis()-b.start.toMillis());
|
||||
const merged=[];
|
||||
for(let iv of busies){
|
||||
if(!iv?.isValid||iv.isEmpty)continue;
|
||||
if(merged.length===0||merged.at(-1).end<iv.start){
|
||||
merged.push(iv);
|
||||
}else{
|
||||
const last=merged[merged.length-1];
|
||||
merged[merged.length-1]=Interval.fromDateTimes(last.start,iv.end>last.end?iv.end:last.end);
|
||||
}
|
||||
}
|
||||
let frees = [];
|
||||
let prevEnd = sr.start;
|
||||
for (let busy of merged) {
|
||||
if (prevEnd < busy.start) {
|
||||
frees.push(IV.fromDateTimes(prevEnd, busy.start));
|
||||
}
|
||||
prevEnd = busy.end;
|
||||
const frees=[];
|
||||
let prev=srS;
|
||||
for(let b of merged){
|
||||
if(prev<b.start)frees.push(Interval.fromDateTimes(prev,b.start));
|
||||
prev=b.end>prev?b.end:prev;
|
||||
}
|
||||
if (prevEnd < sr.end) {
|
||||
frees.push(IV.fromDateTimes(prevEnd, sr.end));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
let slots = [];
|
||||
const dMs = dur.toMillis();
|
||||
for (let wf of workFrees) {
|
||||
let remMs = wf.end.toMillis() - wf.start.toMillis();
|
||||
let n = Math.floor(remMs / dMs);
|
||||
let fs = wf.start;
|
||||
for (let i = 0; i < n; i++) {
|
||||
let ss = fs.plus(D.fromMillis(i * dMs));
|
||||
let se = ss.plus(dur);
|
||||
slots.push({ start: ss.toISO(), end: se.toISO() });
|
||||
if(prev<srE)frees.push(Interval.fromDateTimes(prev,srE));
|
||||
const slots=[];
|
||||
for(let f of frees){
|
||||
let c=f.start;
|
||||
while(c.plus(dur)<=f.end){
|
||||
slots.push({start:c.toISO(),end:c.plus(dur).toISO()});
|
||||
c=c.plus(dur);
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
const validateJSON = async (data, schema) => {
|
||||
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
||||
const ajv = new Ajv({ allErrors: true });
|
||||
const { default: Ajv } = await import('https://cdn.jsdelivr.net/npm/ajv@8/dist/2020.min.js');
|
||||
const ajv = new Ajv({ allErrors: true, strict: false });
|
||||
const validate = ajv.compile(schema);
|
||||
const valid = validate(data);
|
||||
|
||||
return {
|
||||
valid,
|
||||
errors: valid ? [] : validate.errors.map(e =>
|
||||
`${e.instancePath || '/'} ${e.message}`.trim()
|
||||
)
|
||||
errors: valid ? [] : validate.errors.map(e => `${e.instancePath || '/'} ${e.message}`)
|
||||
};
|
||||
};
|
||||
export default validateJSON;
|
||||
@@ -1,16 +1,24 @@
|
||||
const validateJSON = async (data, schema) => {
|
||||
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
||||
const validateJSON = async (json, schema) => {
|
||||
let ajv;
|
||||
|
||||
const ajv = new Ajv({ allErrors: true });
|
||||
const validate = ajv.compile(schema);
|
||||
const valid = validate(data);
|
||||
try {
|
||||
const { default: Ajv } = await import('https://cdn.jsdelivr.net/npm/ajv@8.17.1/+esm');
|
||||
ajv = new Ajv({ allErrors: true });
|
||||
} catch (e) {
|
||||
return { valid: false, errors: ["Failed to load validator: " + e.message] };
|
||||
}
|
||||
|
||||
const errors = valid
|
||||
? []
|
||||
: validate.errors.map(
|
||||
({ instancePath, message }) => `${instancePath || 'object'} ${message}`.trim()
|
||||
);
|
||||
try {
|
||||
const validate = ajv.compile(schema);
|
||||
const valid = validate(json);
|
||||
|
||||
return { valid, errors };
|
||||
const errors = validate.errors?.map(
|
||||
e => `${e.instancePath || 'root'} ${e.message}`.trim()
|
||||
) ?? [];
|
||||
|
||||
return { valid, errors };
|
||||
} catch (e) {
|
||||
return { valid: false, errors: ["Invalid schema: " + e.message] };
|
||||
}
|
||||
};
|
||||
export default validateJSON;
|
||||
@@ -0,0 +1,10 @@
|
||||
const validateJSON = async (d, s) => {
|
||||
const { default: A } = await import('https://esm.sh/ajv@8.17.1')
|
||||
const v = new A({ allErrors: true }).compile(s)
|
||||
const r = v(d)
|
||||
return {
|
||||
valid: r,
|
||||
errors: r ? [] : v.errors.map(e => `${e.instancePath} ${e.message}`.trim())
|
||||
}
|
||||
}
|
||||
export default validateJSON;
|
||||
@@ -1,14 +0,0 @@
|
||||
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,25 +1,12 @@
|
||||
const validateJSON = async (data, schema) => {
|
||||
try {
|
||||
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
||||
const ajv = new Ajv({ allErrors: true, strict: false });
|
||||
try {
|
||||
const { default: addFormats } = await import('https://esm.sh/ajv-formats@2.1.1');
|
||||
addFormats(ajv);
|
||||
} catch {}
|
||||
|
||||
const validate = ajv.compile(schema);
|
||||
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}`] };
|
||||
}
|
||||
};
|
||||
let Ajv;
|
||||
async function validateJSON(json, schema) {
|
||||
if(!(json&&typeof json==='object')||!(schema===true||schema===false||(schema&&typeof schema==='object')))
|
||||
return{valid:false,errors:['Invalid input']};
|
||||
try{
|
||||
if(!Ajv){const m=await import('https://esm.sh/ajv@8');Ajv=m.default}
|
||||
const ajv=new Ajv({allErrors:true}),v=ajv.compile(schema);
|
||||
const valid=v(json);
|
||||
return{valid,errors:valid?[]:v.errors.map(e=>`${e.instancePath||'root'} ${e.message}`)};
|
||||
}catch(e){return{valid:false,errors:[e.message]}}
|
||||
}
|
||||
export default validateJSON;
|
||||
@@ -1,9 +1,2 @@
|
||||
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}));})();
|
||||
|
||||
async function validateJSON(data,schema){
|
||||
const ajv=await fetchAjv();
|
||||
const validate=ajv.compile(schema);
|
||||
const valid=validate(data);
|
||||
return {valid,errors:valid?[]:validate.errors.map(e=>e.message||'Invalid')};
|
||||
}
|
||||
const validateJSON=(()=>{let a;return async(j,s)=>{a??=(async()=>{const{default:Ajv}=await import('https://esm.run/ajv@8');return new Ajv({allErrors:1});})();const v=await a,c=v.compile(s),valid=c(j);return{valid,errors:valid?[]:(c.errors||[]).map(e=>`${e.instancePath||'/'} ${e.message}`)}}})();
|
||||
export default validateJSON;
|
||||
@@ -1,11 +1,21 @@
|
||||
let ajvInstance;
|
||||
|
||||
async function validateJSON(data, schema) {
|
||||
const { default: Ajv } = await import('https://esm.sh/ajv@8');
|
||||
const ajv = new Ajv({ allErrors: true });
|
||||
const validate = ajv.compile(schema);
|
||||
const valid = validate(data);
|
||||
return {
|
||||
valid,
|
||||
errors: valid ? [] : validate.errors.map(e => e.message)
|
||||
};
|
||||
if (!ajvInstance) {
|
||||
const { default: Ajv } = await import('https://cdn.skypack.dev/ajv@8');
|
||||
ajvInstance = new Ajv({ allErrors: true });
|
||||
}
|
||||
const ajv = ajvInstance;
|
||||
try {
|
||||
const validate = ajv.compile(schema);
|
||||
const valid = validate(data);
|
||||
const errors = valid ? [] : ajv.errorsText(validate.errors, { separator: '\n' })
|
||||
.split('\n')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean);
|
||||
return { valid, errors };
|
||||
} catch (e) {
|
||||
return { valid: false, errors: [`Schema error: ${e.message}`] };
|
||||
}
|
||||
}
|
||||
export default validateJSON;
|
||||
@@ -11,9 +11,7 @@ export async function createStreamVisualizer(asyncIterable, options) {
|
||||
: alpha * value + (1 - alpha) * prevEma;
|
||||
|
||||
data.push({ timestamp, value, ema });
|
||||
|
||||
if (data.length > maxPoints) data.shift();
|
||||
|
||||
prevEma = ema;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +1,33 @@
|
||||
async function createStreamVisualizer(asyncIterable, {
|
||||
maxPoints,
|
||||
alpha,
|
||||
width,
|
||||
height,
|
||||
yDomain
|
||||
}) {
|
||||
const {
|
||||
scaleLinear,
|
||||
line
|
||||
} = await import('d3');
|
||||
|
||||
async function createStreamVisualizer(
|
||||
asyncIterable,
|
||||
{ maxPoints, alpha, width, height, yDomain }
|
||||
) {
|
||||
const { scaleLinear, line } = await import('d3');
|
||||
const data = [];
|
||||
let prevEma;
|
||||
let ema = null;
|
||||
|
||||
for await (const { value, timestamp } of asyncIterable) {
|
||||
prevEma = (prevEma === undefined) ?
|
||||
value :
|
||||
alpha * value + (1 - alpha) * prevEma;
|
||||
|
||||
data.push({
|
||||
timestamp,
|
||||
value,
|
||||
ema: prevEma
|
||||
});
|
||||
for await (const point of asyncIterable) {
|
||||
ema = ema === null ? point.value : alpha * point.value + (1 - alpha) * ema;
|
||||
data.push({ ...point, ema });
|
||||
if (data.length > maxPoints) {
|
||||
data.shift();
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
return {
|
||||
data,
|
||||
path: ''
|
||||
};
|
||||
if (data.length < 2) {
|
||||
return { data, path: "" };
|
||||
}
|
||||
|
||||
const x = scaleLinear([data[0].timestamp, data.at(-1).timestamp], [0, width]);
|
||||
const x = scaleLinear(
|
||||
[data[0].timestamp, data.at(-1).timestamp],
|
||||
[0, width]
|
||||
);
|
||||
const y = scaleLinear(yDomain, [height, 0]);
|
||||
|
||||
const path = line()
|
||||
.x(d => x(d.timestamp))
|
||||
.y(d => y(d.ema))(data);
|
||||
.x(p => x(p.timestamp))
|
||||
.y(p => y(p.ema))(data);
|
||||
|
||||
return {
|
||||
data,
|
||||
path: path ?? ''
|
||||
};
|
||||
return { data, path };
|
||||
}
|
||||
export default createStreamVisualizer;
|
||||
@@ -0,0 +1,28 @@
|
||||
export const createStreamVisualizer = async (stream, { maxPoints, alpha, width, height, yDomain }) => {
|
||||
const { scaleLinear, scaleTime, line } = await import('https://esm.sh/d3@7');
|
||||
const data = [];
|
||||
let ema;
|
||||
|
||||
for await (const { timestamp, value } of stream) {
|
||||
ema = ema === undefined ? value : alpha * value + (1 - alpha) * ema;
|
||||
data.push({ timestamp, value, ema });
|
||||
if (data.length > maxPoints) data.shift();
|
||||
}
|
||||
|
||||
if (!data.length) return { data, path: '' };
|
||||
|
||||
const x = scaleTime()
|
||||
.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;
|
||||
@@ -1,32 +0,0 @@
|
||||
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,68 +1,20 @@
|
||||
async function createStreamVisualizer(asyncIterable, options) {
|
||||
const { maxPoints, alpha, width, height, yDomain } = options;
|
||||
const d3 = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm');
|
||||
|
||||
if (!Number.isFinite(maxPoints) || maxPoints < 1) {
|
||||
throw new Error('maxPoints must be a positive integer');
|
||||
}
|
||||
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');
|
||||
}
|
||||
|
||||
export async function createStreamVisualizer(stream, { maxPoints, alpha, width, height, yDomain }) {
|
||||
const d3 = await import('d3');
|
||||
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 ema = null;
|
||||
|
||||
for await (const { timestamp, value } of stream) {
|
||||
ema = ema === null ? value : alpha * value + (1 - alpha) * ema;
|
||||
data.push({ timestamp, value, ema });
|
||||
if (data.length > maxPoints) data.shift();
|
||||
}
|
||||
|
||||
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) || '';
|
||||
}
|
||||
|
||||
|
||||
if (!data.length) return { data, path: '' };
|
||||
|
||||
const x = d3.scaleLinear().domain([data[0].timestamp, data[data.length - 1].timestamp]).range([0, width]);
|
||||
const y = d3.scaleLinear().domain(yDomain).range([height, 0]);
|
||||
const path = d3.line().x(d => x(d.timestamp)).y(d => y(d.ema))(data);
|
||||
|
||||
return { data, path };
|
||||
}
|
||||
export default createStreamVisualizer;
|
||||
@@ -1,18 +1,20 @@
|
||||
const createStreamVisualizer=async(iter,o)=>{
|
||||
const{maxPoints:m,alpha:a,width:w,height:h,yDomain:Y}=o
|
||||
const data=[]
|
||||
const createStreamVisualizer = async (src, opt = {}) => {
|
||||
const { maxPoints = 100, alpha = 0.5, width = 600, height = 300, yDomain = [0, 1] } = opt
|
||||
const { scaleLinear, line } = await import('https://cdn.jsdelivr.net/npm/d3@7/+esm')
|
||||
const data = []
|
||||
let ema
|
||||
for await(const {timestamp:t,value:v} of iter){
|
||||
const d=new Date(t)
|
||||
ema=ema==null?v:a*v+(1-a)*ema
|
||||
data.push({timestamp:d,value:v,ema})
|
||||
if(data.length>m)data.shift()
|
||||
for await (const p of src) {
|
||||
if (!p) continue
|
||||
const { timestamp: t, value: v } = p
|
||||
if (t == null || v == null) continue
|
||||
ema = ema == null ? v : alpha * v + (1 - alpha) * ema
|
||||
data.push({ timestamp: t, value: v, ema })
|
||||
data.length > maxPoints && data.shift()
|
||||
}
|
||||
if(!data.length)return{data,path:''}
|
||||
const{scaleTime,scaleLinear,line}=await import('https://cdn.skypack.dev/d3@7?min')
|
||||
const x=scaleTime().domain([data[0].timestamp,data[data.length-1].timestamp]).range([0,w])
|
||||
const y=scaleLinear().domain(Y).range([h,0])
|
||||
const path=line().x(d=>x(d.timestamp)).y(d=>y(d.ema))(data)||''
|
||||
return{data,path}
|
||||
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;
|
||||
@@ -1,19 +1,17 @@
|
||||
async function createStreamVisualizer(asyncIterable,options){
|
||||
const{maxPoints,alpha,width,height,yDomain}=options;
|
||||
const{scaleLinear:sl,line}=await import('https://cdn.skypack.dev/d3');
|
||||
let points=[];
|
||||
let prevEma=NaN;
|
||||
for await(const{timestamp,value}of asyncIterable){
|
||||
const ema=isNaN(prevEma)?value:alpha*value+(1-alpha)*prevEma;
|
||||
points.push({timestamp,value,ema});
|
||||
prevEma=ema;
|
||||
if(points.length>maxPoints)points.shift();
|
||||
async function createStreamVisualizer(iter,opts={}){
|
||||
const{maxPoints:mp=1e3,alpha:a=.1,width:w=800,height:h=400,yDomain:yd=[0,1]}=opts;
|
||||
let data=[],pE=null;
|
||||
for await(const{timestamp:t,value:v}of iter){
|
||||
let ema=pE===null?v:a*v+(1-a)*pE;
|
||||
pE=ema;
|
||||
data.push({timestamp:t,value:v,ema});
|
||||
if(data.length>mp)data.shift();
|
||||
}
|
||||
if(!points.length)return{data:[],path:''};
|
||||
const xDomain=[points[0].timestamp,points[points.length-1].timestamp];
|
||||
const x=sl().domain(xDomain).range([0,width]);
|
||||
const y=sl().domain(yDomain).range([height,0]);
|
||||
const lineGen=line().x(d=>x(d.timestamp)).y(d=>y(d.ema));
|
||||
return{data:points,path:lineGen(points)};
|
||||
if(!data.length)return{data,path:null};
|
||||
const d3=await import('https://cdn.skypack.dev/d3');
|
||||
const x=d3.scaleLinear().domain([data[0].timestamp,data.at(-1).timestamp]).range([0,w]);
|
||||
const y=d3.scaleLinear().domain(yd).range([h,0]);
|
||||
const ln=d3.line().x(d=>x(d.timestamp)).y(d=>y(d.ema));
|
||||
return{data,path:ln(data)};
|
||||
}
|
||||
export default createStreamVisualizer;
|
||||
Reference in New Issue
Block a user