UNPKG

@alexaegis/advent-of-code-lib

Version:
341 lines (340 loc) 9.84 kB
import { isNotNullish } from "@alexaegis/common"; import { N as gcd, X as invModBigInt, az as Vec2, U as Interval } from "./array.polyfill-r5d1OMxR.js"; import "js-sdsl"; import "./index-41yGvuWH.js"; import "./map.js"; import "node:perf_hooks"; import "node:fs/promises"; import "node:path"; import "node:fs"; import "kolorist"; import "./set.js"; import { f as frequencyMap } from "./frequency-map.function-8lKbf4vK.js"; const cartesianCombinations = (...arrays) => { const r = []; const max = arrays.length - 1; const cartesianHelper = (arr, i) => { const row = arrays[i]; if (isNotNullish(row)) { for (let j = 0, l = row.length; j < l; j++) { const a = [...arr]; a.push(row[j]); if (i === max) r.push(a); else cartesianHelper(a, i + 1); } } }; cartesianHelper([], 0); return r; }; const clamp = (a, high = 1, low = -high, mid = 0) => { if (a > mid) return high; else if (a < mid) return low; else return mid; }; const divisible = (a, b) => a % b === 0; const hzToMs = (hz) => 1 / hz * 1e3; const lcmOverTwo = (x, y) => !x || !y ? 0 : Math.abs(x * y / gcd(x, y)); const lcm = (x, y) => Array.isArray(x) ? x.reduce((a, n) => lcmOverTwo(a, n), 1) : lcmOverTwo(x, y); const quadratic = (a, b, c) => { const sqrt = Math.sqrt(Math.pow(b, 2) - 4 * a * c); return [(-b - sqrt) / 2 * a, (-b + sqrt) / 2 * a]; }; const crt = (mods) => { return Number( crtBigInt( mods.map(({ remainder, modulo }) => ({ remainder: BigInt(remainder), modulo: BigInt(modulo) })) ) ); }; const crtBigInt = (mods) => { let p = 1n; let sm = 0n; const prod = mods.reduce((acc, { modulo }) => acc * modulo, 1n); for (const { remainder, modulo } of mods) { p = prod / modulo; sm = sm + remainder * invModBigInt(p, modulo) * p; } return sm % prod; }; class ManhattanCircle { constructor(center, radius) { this.center = center; this.radius = radius; } contains(point) { return this.center.manhattan(point) <= this.radius; } isOnEdge(point) { return this.center.manhattan(point) === this.radius; } vertices() { return this.radius === 0 ? [this.center] : [ new Vec2(this.center.x - this.radius, this.center.y), // left new Vec2(this.center.x + this.radius, this.center.y), // right new Vec2(this.center.x, this.center.y + this.radius), // top new Vec2(this.center.x, this.center.y - this.radius) // bottom ]; } intersect(other) { return ManhattanCircle.intersect(this, other); } /** * The 'range' of S is 3 in manhattan distance. The effective range at point 'e' * is then 1, because from 'e', at most at 1 manhattan distance is every point * covered by the range of S. * * ...#.... * ..###... * .#####.. * ###S###. * .###e#.. * ..###... * ...#..f. * * Similarly the effective range of S at f is 0 because it's outside the range * of S */ getEffectiveRange(pos) { return Math.max(this.radius - this.center.manhattan(pos), -1); } rowAt(y) { const effectiveRange = this.getEffectiveRange({ x: this.center.x, y }); return effectiveRange >= 0 ? Interval.closed(this.center.x - effectiveRange, this.center.x + effectiveRange) : Interval.open(this.center.x, this.center.x); } heightAt(x) { const effectiveRange = this.getEffectiveRange({ x, y: this.center.y }); return effectiveRange >= 0 ? Interval.closed(this.center.y - effectiveRange, this.center.y + effectiveRange) : Interval.open(this.center.y, this.center.y); } /** * Returns two points so that both points satisfy * result.center.manhattan(a) === a.radius && result.center.manhattan(b) === b.radius * * If two manhattan circles intersect they have exactly one of their points inside the other * this defines a rectangle, and their opposite ends distance is the same as the other opposing ends * so the distance between the two points that are inside the other, is the same as the distance of the intersections * * taking the half of that, */ static intersect(ac, bc) { const verticesOfAInsideB = ac.vertices().filter((vertex) => bc.contains(vertex)); const verticesOfBInsideA = bc.vertices().filter((vertex) => ac.contains(vertex)); const verticesInsideEachother = [...verticesOfAInsideB, ...verticesOfBInsideA]; if (verticesInsideEachother.length === 2) { const [av, bv] = verticesInsideEachother; const d = av.manhattan(bv); const areFromTheSameCircle = verticesOfAInsideB.length === 2 || verticesOfBInsideA.length === 2; const halver = areFromTheSameCircle ? 4 : 2; const hd = d / halver; const y1 = av.y + hd; const y2 = av.y - hd; const y3 = bv.y + hd; const y4 = bv.y - hd; const x1 = av.x - hd; const x2 = av.x + hd; const x3 = bv.x - hd; const x4 = bv.x + hd; return [ new Vec2(x1, y1), // new Vec2(x1, y2), // new Vec2(x1, y3), // new Vec2(x1, y4), new Vec2(x2, y1), new Vec2(x2, y2), // new Vec2(x2, y3), // new Vec2(x2, y4), // new Vec2(x3, y1), // new Vec2(x3, y2), new Vec2(x3, y3), new Vec2(x3, y4), // new Vec2(x4, y1), new Vec2(x4, y2), // new Vec2(x4, y3), new Vec2(x4, y4) ].filter((v) => ac.isOnEdge(v) && bc.isOnEdge(v)).reduce((a, n) => { if (!a.some((v) => v.equals(n))) { a.push(n); } return a; }, []); } else { return void 0; } } static *walkIntersections(circles, onlyIntegerIntersections = true) { for (const [a, b] of circles.walkPairs()) { const intersecion = a.intersect(b); if (intersecion) { for (const v of intersecion) { if (!onlyIntegerIntersections || v.isInt()) { yield v; } } } } } static *walkEdges(sensorData) { for (const data of sensorData) { yield* data.center.generateVectorsAroundInManhattanRadius(data.radius + 1); } } clone() { return new ManhattanCircle(this.center.clone(), this.radius); } setRadius(radius) { this.radius = radius; return this; } } class HuffmannNode { constructor(frequency, code) { this.frequency = frequency; this.code = code; } left; right; toString(prefix = "") { let s = `${prefix}${this.code ?? "("}:${this.frequency} `; if (this.left) { s += this.left.toString(prefix + " "); } if (this.right) { s += this.right.toString(prefix + " "); } return s; } *codeTable(prefix = "") { if (this.left) { yield* this.left.codeTable(prefix + "1"); } if (this.code) { yield [this.code, prefix]; } if (this.right) { yield* this.right.codeTable(prefix + "0"); } } } class Huffmann { constructor(input) { this.input = input; this.frequencies = frequencyMap(input); for (const [code, frequency] of this.frequencies) { this.forest.push(new HuffmannNode(frequency, code)); } } frequencies; forest = []; deforest() { while (this.forest.length > 1) { const sortedForest = this.forest.sort((an, bn) => an.frequency - bn.frequency); const a = sortedForest.shift(); const b = sortedForest.shift(); if (a && b) { const n = new HuffmannNode(a.frequency + b.frequency); n.left = a; n.right = b; this.forest = [...sortedForest, n]; } } return this.forest[0]; } } class LZPointer { constructor(jump, length) { this.jump = jump; this.length = length; } } class LZW { tape; dictionary = /* @__PURE__ */ new Map(); symbolSize = 0; constructor(input) { this.tape = input.map((i) => i.toString()); for (const [i, fragment] of [ ...this.tape.reduce((a, n) => a.add(n), /* @__PURE__ */ new Set()).values() ].entries()) { this.dictionary.set(fragment, i); } this.symbolSize = this.dictionary.size; } multiPass(untilDistinctResultSize = 3) { const res = this.compress(1); const lzw = new LZW(res); if (res.reduce((a, n) => a.add(n), /* @__PURE__ */ new Set()).size <= untilDistinctResultSize) { return [res, res.map((r) => r.toString())]; } else { const [r, look] = lzw.multiPass(untilDistinctResultSize); return [r, look]; } } reverse(result) { return result.map((re) => [...this.dictionary.keys()][re]); } compress(rounds = 1, maxDistinctKeys = Number.POSITIVE_INFINITY) { let p = this.tape[0]; let result = []; let i = 0; let r = rounds; let prevDictLength = this.dictionary.size; while (i < r && (maxDistinctKeys === Number.POSITIVE_INFINITY || result.reduce((a, n) => a.add(n), /* @__PURE__ */ new Set()).size > maxDistinctKeys)) { const localTape = [...this.tape]; p = localTape.shift(); result = []; for (const c of localTape) { const pc = p + c; if (this.dictionary.has(pc)) { p = pc; } else if (p !== void 0) { result.push(this.dictionary.get(p)); this.dictionary.set(pc, this.symbolSize++); p = c; } } if (maxDistinctKeys !== Number.POSITIVE_INFINITY && this.dictionary.size > prevDictLength) { prevDictLength = this.dictionary.size; r++; } if (p) { result.push(this.dictionary.get(p)); } i++; } return result; } } export { HuffmannNode as H, LZPointer as L, ManhattanCircle as M, clamp as a, crt as b, cartesianCombinations as c, divisible as d, crtBigInt as e, Huffmann as f, LZW as g, hzToMs as h, lcm as l, quadratic as q };