import { assert, assertEquals, assertInstanceOf } from "https://deno.land/std@0.221.0/assert/mod.ts"; import * as path from "https://deno.land/std@0.221.0/path/mod.ts"; import { TextLineStream } from "https://deno.land/std@0.221.0/streams/mod.ts"; import { z, ZodType } from "https://deno.land/x/zod@v3.22.4/mod.ts"; export function parsed_stream(parser: ZodType) { return async function* (readable: ReadableStream) { const line_stream = readable .pipeThrough(new TextDecoderStream()) .pipeThrough(new TextLineStream()); for await (const line of line_stream) yield parser.parse(JSON.parse(line)); }; } export function serialized_stream(generator: AsyncIterable) { return async function (writable: WritableStream) { const stream = new TextEncoderStream(); stream.readable.pipeTo(writable); for await (const item of generator) { const writer = stream.writable.getWriter(); await writer.write(JSON.stringify(item) + "\n"); writer.releaseLock(); } }; } export type Consumer = (item: T) => void; // deno-lint-ignore no-explicit-any export type ParserOutput ZodType> = z.infer>; export type PromiseSplit = ReturnType>; export function split_promise() { let resolver_ = null as Consumer | null; const promise = new Promise((r) => { resolver_ = r; }); assert(resolver_ !== null); const resolver = resolver_ as Consumer; return { promise, resolver }; } export class AsyncQueue { private items; private awaiting; public constructor() { this.items = [] as T[]; this.awaiting = [] as Consumer[]; } public push(item: T) { this.items.push(item); const [first] = this.awaiting.splice(0, 1); if (first !== undefined) first(); } public async pull() { let first = this.take_first_item(); if (first !== undefined) return first; const { promise, resolver } = split_promise(); this.awaiting.push(resolver); await promise; first = this.take_first_item(); assert(first !== undefined); return first as T; } private take_first_item() { const [first] = this.items.splice(0, 1); return first as T | undefined; } } export function channel() { const queue = new AsyncQueue(); return [new Sender(queue), new Receiver(queue)] as const; } export function channel_generator(generator: AsyncIterable) { const [sender, receiver] = channel(); (async () => { for await (const item of generator) sender.send(item); })(); return receiver; } export class Sender { queue; public constructor(queue: AsyncQueue) { this.queue = queue; } public send(item: T) { this.queue.push(item); } public async send_all(iter: AsyncGenerator) { for await (const item of iter) this.send(item); } } export class Receiver { queue; public constructor(queue: AsyncQueue) { this.queue = queue; } public async receive() { return await this.queue.pull(); } public async *iter() { while (true) yield this.receive(); } } export function range(from: number, to: number) { return new Range(from, to); } export class Range { from; to; constructor(from: number, to: number) { this.from = from; this.to = to; } includes(n: number) { return (n >= this.from) && (n < this.to); } [Symbol.iterator]() { let index = this.from; const to = this.to; return (function* () { while (index < to) yield index++; })(); } } export async function launch_caught( operation: () => Promise | T, handler: null | ((error: unknown) => void) = null, ) { try { return await operation(); } catch (error) { if (handler !== null) handler(error); else console.error("Lanched Uncaught :", error); } } export function log_from(meta: ImportMeta) { return function (...args: unknown[]) { const mod = path.basename(new URL(meta.url).pathname); console.log(`[${mod}]`, ...args); }; } export class Vec2 { private x_; private y_; public constructor(x: number, y: number) { this.x_ = Math.round(x); this.y_ = Math.round(y); } public x() { return this.x_; } public y() { return this.y_; } public scale(factor: number) { return new Vec2(this.x_ * factor, this.y_ * factor); } public neg() { return this.scale(-1); } public add(other: Vec2) { return new Vec2(this.x_ + other.x_, this.y_ + other.y_); } public sub(other: Vec2) { return this.add(other.neg()); } public inside(min: Vec2, max: Vec2) { if (!range(min.x(), max.x()).includes(this.x())) return false; if (!range(min.y(), max.y()).includes(this.y())) return false; return true; } public eq(other: Vec2) { return (this.x() === other.x()) && (this.y() === other.y()); } public neq(other: Vec2) { return !this.eq(other); } public len() { return Math.sqrt(this.x() ** 2 + this.y() ** 2); } public distance(other: Vec2) { return this.sub(other).len(); } public overlaps(other: Vec2) { return this.distance(other) < 1; } public clone() { return new Vec2(this.x(), this.y()); } } export function v2(x: number, y: number) { return new Vec2(x, y); } export function* enumerate(iterator: Iterable) { let index = 0; for (const item of iterator) yield [index++, item] as [number, T]; } export function* chunks(iterator: Iterable, size: number) { let current = []; for (const item of iterator) { current.push(item); if (current.length < size) continue; yield current; current = []; } } Deno.test("test_chunk", () => { assertEquals( [...chunks([1, 2, 3, 4, 5, 6, 7], 3)], [[1, 2, 3], [4, 5, 6]], ); }); export function wait(ms: number) { return new Promise((res) => setTimeout(res, ms)); } export async function run(cmd: string, ...args: string[]) { const command = new Deno.Command(cmd, { args, stdout: "piped" }); const output = await command.output(); if (!output.success) throw new Error(); return new TextDecoder().decode(output.stdout); } export type Prototyped

= { constructor: { prototype: P } }; // deno-lint-ignore no-explicit-any export type Constructible = new (...args: any[]) => I; export class ClassMap { private inner; constructor() { this.inner = new Map(); } insert(object: V) { const prototype = object.constructor.prototype; if (this.inner.has(prototype)) throw new Error(); this.inner.set(prototype, object); } get>(class_: C) { const value = this.inner.get(class_.prototype); if (value === undefined) return null; assertInstanceOf(value, class_); return value; } has>(class_: C) { return this.inner.has(class_.prototype); } } Deno.test("test_classmap", () => { { class Truc {} class Machin {} class Chose {} const map = new ClassMap(); const truc = new Truc(); map.insert(truc); const machin = new Machin(); map.insert(machin); assert(map.get(Truc) === truc); assert(map.get(Machin) !== truc); assert(map.get(Machin) === machin); assert(map.get(Chose) === null); } { interface Machinable { machin(): number; } // deno-lint-ignore no-unused-vars class Truc {} class Machin implements Machinable { machin = () => 35; } const map = new ClassMap(); map.insert(new Machin()); // map.insert(new Truc()); // note : Fails with improper typing. map.get(Machin); // map.get(Truc); // note : Fails with improper typing. } }); export function maybe(item: T) { return item as T | undefined; } export type Awaitable = T | Promise; export function first(iter: Iterable) { for (const item of iter) return item; return null; } export function take_n(iter: Iterable, n: number) { const result = [] as T[]; for (const item of iter) { if (result.length >= n) break; result.push(item); } return result; } export function* line(from: Vec2, to: Vec2) { let current = from.clone(); while (current.neq(to)) { yield current; if (current.x() - to.x() > 0.5) current = current.sub(v2(1, 0)); if (current.x() - to.x() < -0.5) current = current.add(v2(1, 0)); if (current.y() - to.y() > 0.5) current = current.sub(v2(0, 1)); if (current.y() - to.y() < -0.5) current = current.add(v2(0, 1)); } } export function* spiral(origin: Vec2) { yield origin; for (const i of range(0, 999)) { const size = i * 2 + 1; const corner_A = origin.add(v2(-size / 2, -size / 2)); const corner_B = origin.add(v2(size / 2, -size / 2)); const corner_C = origin.add(v2(size / 2, size / 2)); const corner_D = origin.add(v2(-size / 2, size / 2)); yield* line(corner_A, corner_B); yield* line(corner_B, corner_C); yield* line(corner_C, corner_D); yield* line(corner_D, corner_A); } } // Deno.test("test_spiral", () => { // // 2 | 10 11 12 13 14 // // 1 | 25 2 3 4 15 // // 0 | 24 9 1 5 16 // // -1 | 23 8 7 6 17 // // -2 | 22 21 20 19 18 // // ---+--------------- // // -2 -1 0 1 2 // const result = [...take_n(spiral(v2(0, 0)), 25)]; // const expects = [ // v2(0, 0), // v2(1, 1), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // v2(), // ] // });