phil-lib
Version:
Blazingly fast TypeScript library for Node.js and the browser.
498 lines • 14.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.countMap = exports.Random = exports.phi = exports.radiansPerDegree = exports.degreesPerRadian = exports.FULL_CIRCLE = exports.FIGURE_SPACE = exports.NON_BREAKING_SPACE = exports.MIN_DATE = exports.MAX_DATE = exports.csvStringToArray = void 0;
exports.assertClass = assertClass;
exports.assertNonNullable = assertNonNullable;
exports.sleep = sleep;
exports.testXml = testXml;
exports.parseXml = parseXml;
exports.followPath = followPath;
exports.getAttribute = getAttribute;
exports.parseFloatX = parseFloatX;
exports.parseIntX = parseIntX;
exports.parseTimeT = parseTimeT;
exports.pickAny = pickAny;
exports.pick = pick;
exports.take = take;
exports.filterMap = filterMap;
exports.makePromise = makePromise;
exports.dateIsValid = dateIsValid;
exports.angleBetween = angleBetween;
exports.positiveModulo = positiveModulo;
exports.rotateArray = rotateArray;
exports.rectUnion = rectUnion;
exports.rectAddPoint = rectAddPoint;
exports.dateToFileName = dateToFileName;
exports.lerp = lerp;
exports.assertFinite = assertFinite;
exports.shuffleArray = shuffleArray;
exports.zip = zip;
exports.count = count;
exports.initializedArray = initializedArray;
exports.sum = sum;
exports.makeLinear = makeLinear;
exports.makeBoundedLinear = makeBoundedLinear;
exports.polarToRectangular = polarToRectangular;
exports.permutations = permutations;
exports.gcd = gcd;
exports.lcm = lcm;
function assertClass(item, ty, notes = "Assertion Failed.") {
const failed = (typeFound) => {
throw new Error(`${notes} Expected type: ${ty.name}. Found type: ${typeFound}.`);
};
if (item === null) {
failed("null");
}
else if (typeof item != "object") {
failed(typeof item);
}
else if (!(item instanceof ty)) {
failed(item.constructor.name);
}
else {
return item;
}
throw new Error("wtf");
}
function assertNonNullable(value) {
if (value === undefined || value === null) {
throw new Error("wtf");
}
return value;
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function testXml(xmlStr) {
const parser = new DOMParser();
const dom = parser.parseFromString(xmlStr, "application/xml");
for (const element of Array.from(dom.querySelectorAll("parsererror"))) {
if (element instanceof HTMLElement) {
return { error: element };
}
}
return { parsed: dom };
}
function parseXml(bytes) {
if (bytes === undefined) {
return undefined;
}
else {
const { parsed } = testXml(bytes);
return parsed?.documentElement;
}
}
function followPath(from, ...path) {
for (const transition of path) {
if (from === undefined) {
return undefined;
}
else if (typeof transition === "number") {
from = from.children[transition];
}
else {
const hasCorrectName = from.getElementsByTagName(transition);
if (hasCorrectName.length != 1) {
return undefined;
}
else {
from = hasCorrectName[0];
}
}
}
return from;
}
function getAttribute(attributeName, from, ...path) {
from = followPath(from, ...path);
if (from === undefined) {
return undefined;
}
if (!from.hasAttribute(attributeName)) {
return undefined;
}
return from.getAttribute(attributeName) ?? undefined;
}
function parseFloatX(source) {
if (source === undefined || source === null) {
return undefined;
}
const result = +source;
if (isFinite(result)) {
return result;
}
else {
return undefined;
}
}
function parseIntX(source) {
const result = parseFloatX(source);
if (result === undefined) {
return undefined;
}
else if (result > Number.MAX_SAFE_INTEGER ||
result < Number.MIN_SAFE_INTEGER ||
result != Math.floor(result)) {
return undefined;
}
else {
return result;
}
}
function parseTimeT(source) {
if (typeof source === "string") {
source = parseIntX(source);
}
if (source === undefined || source === null) {
return undefined;
}
if (source <= 0) {
return undefined;
}
return new Date(source * 1000);
}
const csvStringToArray = (data) => {
const re = /(,|\r?\n|\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\r\n]*))/gi;
const result = [[]];
let matches;
while ((matches = re.exec(data))) {
if (matches[1].length && matches[1] !== ",")
result.push([]);
result[result.length - 1].push(matches[2] !== undefined ? matches[2].replace(/""/g, '"') : matches[3]);
}
return result;
};
exports.csvStringToArray = csvStringToArray;
function pickAny(container) {
const first = container.values().next();
if (first.done) {
return undefined;
}
else {
return first.value;
}
}
function pick(array) {
if (array.length == 0) {
throw new Error("wtf");
}
return array[(Math.random() * array.length) | 0];
}
function take(array) {
if (array.length < 1) {
throw new Error("wtf");
}
const index = (Math.random() * array.length) | 0;
const removed = array.splice(index, 1);
return removed[0];
}
function filterMap(input, transform) {
const result = [];
input.forEach((input, index) => {
const possibleElement = transform(input, index);
if (undefined !== possibleElement) {
result.push(possibleElement);
}
});
return result;
}
function makePromise() {
let resolve;
let reject;
const promise = new Promise((resolve1, reject1) => {
resolve = resolve1;
reject = reject1;
});
return { promise, resolve, reject };
}
exports.MAX_DATE = new Date(8640000000000000);
exports.MIN_DATE = new Date(-8640000000000000);
function dateIsValid(date) {
return isFinite(date.getTime());
}
exports.NON_BREAKING_SPACE = "\xa0";
exports.FIGURE_SPACE = "\u2007";
exports.FULL_CIRCLE = 2 * Math.PI;
exports.degreesPerRadian = 360 / exports.FULL_CIRCLE;
exports.radiansPerDegree = exports.FULL_CIRCLE / 360;
exports.phi = (1 + Math.sqrt(5)) / 2;
function angleBetween(angle1, angle2) {
const angle1p = positiveModulo(angle1, exports.FULL_CIRCLE);
const angle2p = positiveModulo(angle2, exports.FULL_CIRCLE);
let difference = angle2p - angle1p;
const maxDifference = exports.FULL_CIRCLE / 2;
if (difference > maxDifference) {
difference -= exports.FULL_CIRCLE;
}
else if (difference < -maxDifference) {
difference += exports.FULL_CIRCLE;
}
if (Math.abs(difference) > maxDifference) {
throw new Error("wtf");
}
return difference;
}
function positiveModulo(numerator, denominator) {
const simpleAnswer = numerator % denominator;
if (simpleAnswer < 0) {
return simpleAnswer + Math.abs(denominator);
}
else {
return simpleAnswer;
}
}
function rotateArray(input, by) {
if ((by | 0) != by) {
throw new Error(`invalid input: ${by}`);
}
by = positiveModulo(by, input.length);
if (by == 0) {
return input;
}
else {
return [...input.slice(by), ...input.slice(0, by)];
}
}
class Random {
constructor() {
throw new Error("wtf");
}
static sfc32(a, b, c, d) {
function random() {
a |= 0;
b |= 0;
c |= 0;
d |= 0;
let t = (((a + b) | 0) + d) | 0;
d = (d + 1) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
}
const result = random;
Object.defineProperty(result, "currentSeed", {
get() {
return JSON.stringify([a, b, c, d]);
},
});
return result;
}
static #nextSeedInt = 42;
static seedIsValid(seed) {
try {
this.create(seed);
return true;
}
catch {
return false;
}
}
static create(seed = this.newSeed()) {
console.info(seed);
const seedObject = JSON.parse(seed);
if (!(seedObject instanceof Array)) {
throw new Error("invalid input");
}
if (seedObject.length != 4) {
throw new Error("invalid input");
}
const [a, b, c, d] = seedObject;
if (!(typeof a == "number" &&
typeof b == "number" &&
typeof c == "number" &&
typeof d == "number")) {
throw new Error("invalid input");
}
return this.sfc32(a, b, c, d);
}
static newSeed() {
const ints = [];
ints.push(Date.now() | 0);
ints.push(this.#nextSeedInt++ | 0);
ints.push((Math.random() * 2 ** 31) | 0);
ints.push((performance.now() * 10000) | 0);
const seed = JSON.stringify(ints);
return seed;
}
static fromString(s) {
try {
return this.create(s);
}
catch {
return this.create(this.anyStringToSeed(s));
}
}
static anyStringToSeed(input) {
function rotateLeft32(value, shift) {
return ((value << shift) | (value >>> (32 - shift))) >>> 0;
}
const ints = [0x9e3779b9, 0x243f6a88, 0x85a308d3, 0x13198a2e];
const data = new TextEncoder().encode(input);
data.forEach((byte) => {
ints[0] ^= byte;
ints[0] = rotateLeft32(ints[0], 3);
ints[1] ^= byte;
ints[1] = rotateLeft32(ints[1], 5);
ints[2] ^= byte;
ints[2] = rotateLeft32(ints[2], 7);
ints[3] ^= byte;
ints[3] = rotateLeft32(ints[3], 11);
});
ints[0] ^= rotateLeft32(ints[1], 7);
ints[1] ^= rotateLeft32(ints[2], 11);
ints[2] ^= rotateLeft32(ints[3], 13);
ints[3] ^= rotateLeft32(ints[0], 17);
return JSON.stringify(ints);
}
static test() {
const maxGenerators = 10;
const iterationsPerCycle = 20;
const generators = [this.create()];
while (generators.length <= maxGenerators) {
for (let iteration = 0; iteration < iterationsPerCycle; iteration++) {
const results = generators.map((generator) => generator());
for (let i = 1; i < results.length; i++) {
if (results[i] !== results[0]) {
debugger;
throw new Error("wtf");
}
}
}
const currentSeed = pick(generators).currentSeed;
generators.forEach((generator) => {
if (generator.currentSeed != currentSeed) {
debugger;
throw new Error("wtf");
}
});
generators.push(this.create(currentSeed));
}
}
}
exports.Random = Random;
function rectUnion(r1, r2) {
const x = Math.min(r1.x, r2.x);
const y = Math.min(r1.y, r2.y);
const right = Math.max(r1.x + r1.width, r2.x + r2.width);
const bottom = Math.max(r1.y + r1.height, r2.y + r2.height);
const width = right - x;
const height = bottom - y;
return { x, y, width, height };
}
function rectAddPoint(r, x, y) {
return rectUnion(r, { x, y, width: 0, height: 0 });
}
function dateToFileName(date) {
if (isNaN(date.getTime())) {
return "0000⸱00⸱00 00⦂00⦂00";
}
else {
return `${date.getFullYear().toString().padStart(4, "0")}⸱${(date.getMonth() + 1)
.toString()
.padStart(2, "0")}⸱${date.getDate().toString().padStart(2, "0")} ${date
.getHours()
.toString()
.padStart(2, "0")}⦂${date.getMinutes().toString().padStart(2, "0")}⦂${date
.getSeconds()
.toString()
.padStart(2, "0")}`;
}
}
function lerp(at0, at1, where) {
return at0 + (at1 - at0) * where;
}
function assertFinite(...values) {
values.forEach((value) => {
if (!Number.isFinite(value)) {
throw new Error("wtf");
}
});
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function* zip(...toZip) {
const iterators = toZip.map((i) => i[Symbol.iterator]());
while (true) {
const results = iterators.map((i) => i.next());
if (results.some(({ done }) => done)) {
break;
}
yield results.map(({ value }) => value);
}
}
function* count(start = 0, end = Infinity, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
function initializedArray(count, callback) {
const result = [];
for (let i = 0; i < count; i++) {
result.push(callback(i));
}
return result;
}
exports.countMap = initializedArray;
function sum(items) {
return items.reduce((accumulator, current) => accumulator + current, 0);
}
function makeLinear(x1, y1, x2, y2) {
const slope = (y2 - y1) / (x2 - x1);
return function (x) {
return (x - x1) * slope + y1;
};
}
function makeBoundedLinear(x1, y1, x2, y2) {
if (x2 < x1) {
[x1, y1, x2, y2] = [x2, y2, x1, y1];
}
const slope = (y2 - y1) / (x2 - x1);
return function (x) {
if (x <= x1) {
return y1;
}
else if (x >= x2) {
return y2;
}
else {
return (x - x1) * slope + y1;
}
};
}
function polarToRectangular(r, θ) {
return { x: Math.cos(θ) * r, y: Math.sin(θ) * r };
}
function* permutations(toPermute, prefix = []) {
if (toPermute.length == 0) {
yield prefix;
}
else {
for (let index = 0; index < toPermute.length; index++) {
const nextItem = toPermute[index];
const newPrefix = [...prefix, nextItem];
const stillNeedToPermute = [
...toPermute.slice(0, index),
...toPermute.slice(index + 1),
];
yield* permutations(stillNeedToPermute, newPrefix);
}
}
}
function gcd(a, b) {
if (!b) {
return a;
}
return gcd(b, a % b);
}
function lcm(a, b) {
return (a * b) / gcd(a, b);
}
//# sourceMappingURL=misc.js.map