import { Milliseconds, Timestamp } from './types'; /** * PhantomData akin to Rust, because sometimes you need to be smarter than * the compiler. */ export abstract class PhantomData

{ public __PHANTOM__: P } /** * Opaque type, similar to `opaque type` in Flow, or new types in Rust/C. * These should be produced only by manually casting `t as Opaque`. * * `P` can be anything as it's never actually used. Using strings is okay: * * ``` * type MyType = Opaque; * ``` */ export type Opaque = T & PhantomData

; /** * Just a readable shorthand for null-ish-able types, akin to `T?` in Flow. */ export type Maybe = T | null | undefined; /** * Asynchronous sleep */ export function sleep(time: Milliseconds): Promise { return new Promise((resolve, _reject) => { setTimeout(() => resolve(), time); }); } export const timestamp = Date.now as () => Timestamp; export function noop() {} /** * Keep track of last N numbers pushed onto internal stack. * Provides means to get an average of said numbers. */ export class NumStats { private readonly stack: Array; private readonly history: number; private index = 0; constructor(history: number) { if (history < 1) { throw new Error('Must track at least one number'); } this.history = history; this.stack = new Array(history); } public push(val: T) { this.stack[this.index++ % this.history] = val; } /** * Get average value of all values on the stack. * * @return {T} average value */ public average(): T { if (this.index === 0) { return 0 as T; } const list = this.nonEmpty(); let sum = 0; for (const n of list as Array) { sum += n; } return (sum / list.length) as T; } /** * Get average value of all values of the stack after filtering * out a number of highest and lowest values * * @param {number} extremes number of high/low values to ignore * @return {T} average value */ public averageWithoutExtremes(extremes: number): T { if (this.index === 0) { return 0 as T; } const list = this.nonEmpty(); const count = list.length - extremes * 2; if (count < 1) { // Not enough entries to remove desired number of extremes, // fall back to regular average return this.average(); } let sum = 0; for (const n of list.sort((a, b) => a - b).slice(extremes, -extremes)) { sum += n; } return (sum / count) as T; } private nonEmpty(): Readonly> { return this.index < this.history ? this.stack.slice(0, this.index) : this.stack; } }