import { fEqOr } from "./bop" import { also } from "./effect" import { MathEx } from "./math" import { Nums } from "./number" declare const TheRange: unique symbol interface RangeBase { [TheRange]: true } class RangeBase { } export type Ranges = RangeAll | TRange | TRangeTo | TRangeFrom | TRangeEq | TRangeToEq export type NRanges = RangeAll | NRange | NRangeTo | NRangeFrom | NRangeEq | NRangeToEq export type IRanges = RangeAll | IRange | IRangeTo | IRangeFrom | IRangeEq | IRangeToEq /** `..` */ export class RangeAll extends RangeBase { constructor() { super() } } /** `from..to` */ export class TRange extends RangeBase { constructor(public from: T, public to: T) { super() } } /** `..to` */ export class TRangeTo extends RangeBase { constructor(public to: T) { super() } } /** `from..` */ export class TRangeFrom extends RangeBase { constructor(public from: T) { super() } } /** `from..=to` */ export class TRangeEq extends RangeBase { constructor(public from: T, public to: T) { super() } } /** `..=to` */ export class TRangeToEq extends RangeBase { constructor(public to: T) { super() } } function rangeIter(step: number, from: number, to: number) { if (from === -Infinity) from = Number.MIN_SAFE_INTEGER if (to === Infinity) to = Number.MAX_SAFE_INTEGER let c = -1 if (from > to) { step = -step c = 1 } return { [Symbol.iterator](): IterableIterator { return this }, next(): IteratorResult { if (MathEx.cmp(from, to) === c) return also({ done: false, value: from }, () => from += step) return { done: true } as IteratorResult } } } function rangeEqIter(step: number, from: number, to: number) { if (from === -Infinity) from = Number.MIN_SAFE_INTEGER if (to === Infinity) to = Number.MAX_SAFE_INTEGER let c = -1 if (from > to) { step = -step c = 1 } return { [Symbol.iterator](): IterableIterator { return this }, next(): IteratorResult { if (fEqOr(MathEx.cmp(from, to), c, 0)) return also({ done: false, value: from }, () => from += step) return { done: true } as IteratorResult } } } /** `from..to` */ export class NRange extends TRange implements Iterable { constructor(from: number, to: number) { super(from, to) } [Symbol.iterator]() { return rangeIter(1, this.from, this.to) } } /** `..to` */ export class NRangeTo extends TRangeTo implements Iterable { constructor(to: number) { super(to) } [Symbol.iterator]() { return rangeIter(1, 0, this.to) } } /** `from..` */ export class NRangeFrom extends TRangeFrom implements Iterable { constructor(from: number) { super(from) } [Symbol.iterator]() { return rangeIter(1, this.from, Infinity) } } /** `from..=to` */ export class NRangeEq extends TRangeEq implements Iterable { constructor(from: number, to: number) { super(from, to) } [Symbol.iterator]() { return rangeEqIter(1, this.from, this.to) } } /** `..=to` */ export class NRangeToEq extends TRangeToEq implements Iterable{ constructor(to: number) { super(to) } [Symbol.iterator]() { return rangeEqIter(1, 0, this.to) } } function rangeIntIter(step: bigint, from: bigint, to?: bigint) { if (to == null) { return { next(): IteratorResult { return also({ done: false, value: from }, () => from += step) } } } let c = -1n if (from > to) { step = -step c = 1n } return { [Symbol.iterator](): IterableIterator { return this }, next(): IteratorResult { if (MathEx.cmp(from, to) === c) return also({ done: false, value: from }, () => from += step) return { done: true } as IteratorResult } } } function rangeIntEqIter(step: bigint, from: bigint, to: bigint) { let c = -1n if (from > to) { step = -step c = 1n } return { [Symbol.iterator](): IterableIterator { return this }, next(): IteratorResult { if (fEqOr(MathEx.cmp(from, to), c, 0n)) return also({ done: false, value: from }, () => from += step) return { done: true } as IteratorResult } } } /** `from..to` */ export class IRange extends TRange implements Iterable { constructor(from: bigint, to: bigint) { super(from, to) } [Symbol.iterator]() { return rangeIntIter(1n, this.from, this.to) } } /** `..to` */ export class IRangeTo extends TRangeTo { constructor(to: bigint) { super(to) } [Symbol.iterator]() { return rangeIntIter(1n, 0n, this.to) } } /** `from..` */ export class IRangeFrom extends TRangeFrom implements Iterable{ constructor(from: bigint) { super(from) } [Symbol.iterator]() { return rangeIntIter(1n, this.from) } } /** `from..=to` */ export class IRangeEq extends TRangeEq implements Iterable{ constructor(from: bigint, to: bigint) { super(from, to) } [Symbol.iterator]() { return rangeIntEqIter(1n, this.from, this.to) } } /** `..=to` */ export class IRangeToEq extends TRangeToEq { constructor(to: bigint) { super(to) } [Symbol.iterator]() { return rangeIntEqIter(1n, 0n, this.to) } } const all = new RangeAll export function rangeAll(): RangeAll { return all } export function range(from: number, to: number): NRange export function range(from: bigint, to: bigint): IRange export function range(from: T, to: T): TRange export function range(from: any, to: any): TRange { if (typeof from === 'number') return new NRange(from, to) else if (typeof from === 'bigint') return new IRange(from, to) return new TRange(from, to) } export function rangeTo(to: number): NRangeTo export function rangeTo(to: bigint): IRangeTo export function rangeTo(to: T): TRangeTo export function rangeTo(to: any): TRangeTo { if (typeof to === 'number') return new NRangeTo(to) else if (typeof to === 'bigint') return new IRangeTo(to) return new TRangeTo(to) } export function rangeFrom(from: number): NRangeFrom export function rangeFrom(from: bigint): IRangeFrom export function rangeFrom(from: T): TRangeFrom export function rangeFrom(from: any): TRangeFrom { if (typeof from === 'number') return new NRangeFrom(from) else if (typeof from === 'bigint') return new IRangeFrom(from) return new TRangeFrom(from) } export function rangeEq(from: number, to: number): NRangeEq export function rangeEq(from: bigint, to: bigint): IRangeEq export function rangeEq(from: T, to: T): TRangeEq export function rangeEq(from: any, to: any): TRangeEq { if (typeof from === 'number') return new NRangeEq(from, to) else if (typeof from === 'bigint') return new IRangeEq(from, to) return new TRangeEq(from, to) } export function rangeToEq(to: number): NRangeToEq export function rangeToEq(to: bigint): IRangeToEq export function rangeToEq(to: T): TRangeToEq export function rangeToEq(to: any): TRangeToEq { if (typeof to === 'number') return new NRangeToEq(to) else if (typeof to === 'bigint') return new IRangeToEq(to) return new TRangeToEq(to) } export function isRanges(v: any): v is Ranges { return v instanceof RangeBase } export namespace Ranges { export function toTuple(range: RangeAll): [] export function toTuple(range: TRange | TRangeEq): [from: number, to: number] export function toTuple(range: TRange | TRangeEq): [from: bigint, to: bigint] export function toTuple(range: TRangeFrom): [from: number] export function toTuple(range: TRangeFrom): [from: bigint] export function toTuple(range: TRangeTo | TRangeToEq): [from: 0, to: number] export function toTuple(range: TRangeTo | TRangeToEq): [from: 0n, to: bigint] export function toTuple(range: Ranges): [from?: number, to?: number] export function toTuple(range: Ranges): [from?: bigint, to?: bigint] export function toTuple(range: Ranges): [from?: any, to?: any] { if (range instanceof RangeAll) { return [] } else if (range instanceof TRange) { return [range.from, range.to] } else if (range instanceof TRangeEq) { return [range.from, range.to + Nums.one(range.to)] } else if (range instanceof TRangeFrom) { return [range.from] } else if (range instanceof TRangeTo) { return [Nums.zero(range.to), range.to] } else if (range instanceof TRangeToEq) { return [Nums.zero(range.to), range.to + Nums.one(range.to)] } else { throw new TypeError('Unsupported range') } } }