npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@slnknrr/num-ez

v1.0.0

Published

The friendly face of @slnknrr/num-im: the same exact, any-base number engine behind familiar names, plain booleans and ready arrays. Exact arithmetic past 2^53, base conversion, canonical validation, modular arithmetic, number theory, bit metrics, digit s

Downloads

114

Readme

@slnknrr/num-ez

The friendly face of @slnknrr/num-im: the same exact, any-base number engine behind familiar names, plain booleans and ready arrays.

Floats leave you num-b? Take num-ez. Want every digit num-bered, exactly, past 2⁵³? Take num-im. Same engine, two accents: num-ez says equals and returns true; num-im says eq and returns -1 because it has more to tell you.

num-im is precise and terse: isnum, powmod, rotdig, a tri-state eq that separates value from written form, lazy digit generators, libc-style names throughout. That is the right contract for an engine. It is not the contract you want in application code, where a number utility should read like isInteger, return true, and hand you an array you can map over.

num-ez is that layer — and nothing more. Every function is one call into the engine plus a translation at the boundary. No arithmetic of its own, no state, no configuration.

import { add, equals, toHex, isInteger, sortAscending, factorial, format } from '@slnknrr/num-ez';

add('9007199254740992', '1');           // '9007199254740993' — 2⁵³ + 1, exact where + starts lying
add('0.1', '0.2');                      // '0.3' — not 0.30000000000000004
equals('0', '0.0');                     // true — equal in value (the engine says -1: same value, different form)
toHex('18446744073709551616');          // '10000000000000000' — 2⁶⁴, exact
isInteger('007');                       // false — a leading zero is a costume, not a number
sortAscending(['10', '9', '100']);      // ['9', '10', '100'] — plain sort gives ['10', '100', '9']
factorial('25');                        // '15511210043330985984000000' — 26 digits, 10 past what a double holds exactly
format('1234567.891', { group: true, fracDigits: 2 }); // '1,234,567.89'

What changes at the boundary

Six rules. Everything else is the engine's behavior, unchanged.

| | num-im | num-ez | |---|---|---| | Equality | eq is 1 same value & form · -1 same value only · 0 unequal | equals — boolean, 1 and -1 are both a yes; identical keeps the strict 1 | | Order | cmp is -1 / 0 / 1 | compare — unchanged: that is the familiar shape, sort wants it; comparator() packs it, greaterThan & co. give the booleans | | Lazy digit generators | ladd … yield one digit per next() | addDigits … return string[] | | Result objects | { q, r }, { sign, int, frac } | { quotient, remainder }, { sign, integer, fraction } | | Values | digit-strings in, digit-strings out | unchanged — a Number would round past 2⁵³; toNumber is the one exit, and it throws rather than lose digits | | SyntaxError / RangeError / TypeError | thrown at the call | unchanged — the caller is wrong, and hiding it is the opposite of easy; check input with isNumber first |

undefined for an empty aggregate, null for a missing modular inverse and NaN for the sign of a non-number pass through too; the boolean companions (isZero, isPositive, isNegative, isSafeInteger) answer the yes/no form directly. Options travel in the same trailing object the engine takes: { base } (default 10), { precision } (default 20 fraction digits for a non-terminating division), and the per-method extras listed below. A numeric argument is always a decimal value; only strings are read in base.

What it is NOT

  • Not a second engine. Every algorithm lives in num-im. If you need the tri-state, the generators or the raw regex machinery, import num-im directly; both packages can be used side by side.
  • Not a Number replacement. For eager, in-range, base-10 math, Number and BigInt are a compiled C++ path and they win. Reach for this where the answer must be exact, in a non-decimal base, or in a canonical, validatable form.
  • Not a decimal / money / units type. format does grouping, fixed fraction and sign style; a locale engine is your layer.
  • Not constant-time crypto. powerMod is correct, not hardened. Do not use it on secret exponents.

Install

npm install @slnknrr/num-ez
import ez from '@slnknrr/num-ez';                      // one frozen object with every function
import { add, isInteger, toHex } from '@slnknrr/num-ez'; // or named imports — tree-shakeable

Requirements: Node ≥ 20, ESM only. @slnknrr/num-im ≥ 1.0.1 is the single dependency and is installed with the package.


API

123 functions: 105 wrappers, one per engine method, and 18 compositions built from them (marked +). The num-im column names the engine method behind each wrapper.

Every value argument (a, b, n, k, m, width, …) is a Numeric: a digit-string read in opts.base, or a number / bigint understood as a decimal value. xs is an Enumerable: an Array, Set, Map (its values) or array-like. opts is optional everywhere and carries base unless stated otherwise.

1 · Validators — the regex is the grammar

| Function | num-im | Returns | |---|---|---| | numberPattern(base = 10, { sign, float }?) | re | anchored RegExp for a canonical number; sign / float: true require, false forbid, default optional | | integerPattern(base = 10) | reint | integers only | | floatPattern(base = 10) | refloat | a fractional part is required |

base is positional here, as in the engine: a radix 2–36, a custom alphabet string, or an array whose entries may be glyph-sets (['0', '1', ['a', 'A']]).

2 · Equality and order — cross-base, no float math

| Function | num-im | Returns | |---|---|---| | equals(a, b, { sign }?) | eq | boolean — equal in value; sign: false ignores the sign | | + identical(a, b, opts?) | | boolean — equal in value and written the same way | | notEquals(a, b) | ne | boolean — provably different | | compare(a, b) | cmp | -1 / 0 / 1 | | greaterOrEqual(a, b) / lessOrEqual(a, b) | ge / le | boolean | | + greaterThan(a, b) / lessThan(a, b) | | boolean | | + comparator(opts?) | | (a, b) => -1 / 0 / 1 for sort, with the base fixed once | | + sortAscending(xs) / sortDescending(xs) | | a sorted copy by numeric value; the items come back as given | | + isZero(n) / isPositive(n) / isNegative(n) | | boolean; all three are false for a non-number | | + isBetween(n, min, max) | | min ≤ n ≤ max, inclusive; min > max throws |

3 · Bit-weight order — by how it packs, not how big it is

| Function | num-im | Returns | |---|---|---| | compareBitWeight(a, b) | cmpbw | -1 / 0 / 1 by a signed bit-count of the integer part | | equalsBitWeight / notEqualsBitWeight / greaterOrEqualBitWeight / lessOrEqualBitWeight | eqbw / nebw / gebw / lebw | boolean |

4 · Digit streams — the lazy arithmetic, as arrays

| Function | num-im | Returns | |---|---|---| | addDigits / subtractDigits / multiplyDigits (a, b) | ladd / lsub / lmul | string[] — one glyph per element, least-significant first | | divideDigits(a, b) | ldiv | string[] — most-significant first, fraction digits included |

5 · Arithmetic — exact past 2⁵³ and across bases

| Function | num-im | Returns | |---|---|---| | add / subtract / multiply / divide (a, b, { precision }?) | add / sub / mul / div | canonical string; a non-terminating divide stops at precision (default 20) | | negate(n) / increment(n) / decrement(n) | neg / inc / dec | never '-0' | | remainder(a, b) | rem | truncated remainder — the sign of the dividend, like % | | modulo(a, b) | mod | Euclidean — always in [0, |b|) | | divideWithRemainder(a, b) | divrem | { quotient, remainder } | | power(a, k) | pow | non-negative integer exponent, exact | | integerSqrt(n) / integerRoot(n, k) | isqrt / root | floor(√n) / floor(n^(1/k)) | | gcd(a, b) / lcm(a, b) | gcd / lcm | |

6 · Modular arithmetic

| Function | num-im | Returns | |---|---|---| | multiplyMod(a, b, m) | mulmod | (a·b) mod m, non-negative | | powerMod(a, k, m) | powmod | a^k mod m — the RSA/DH primitive (⚠ not constant-time) | | modInverse(a, m) | modinv | the inverse, or null when gcd(a, m) ≠ 1 | | isPrime(n) | isprime | boolean, deterministic Miller–Rabin |

7 · Rounding — to precision fraction digits (default 0)

| Function | num-im | Notes | |---|---|---| | round(n, { precision, mode }?) | round | mode: 'half-up' (default) · 'half-even' · 'half-odd' · 'floor' · 'ceil' · 'trunc' | | floor(n) / ceil(n) / truncate(n) | floor / ceil / trunc | toward −∞ · +∞ · zero |

"Half" means base/2, so ties exist only in even bases.

8 · Shifts and bitwise

| Function | num-im | Returns | |---|---|---| | shiftLeft(n, k) / shiftRight(n, k) | shl / shr | multiply / divide by base^k — the count k is itself read in-base | | bitAnd / bitOr / bitXor (a, b) | and / or / xor | on the magnitudes, base-2 semantics | | bitNot(n, width) | not | ones-complement inside a width-digit field; width is mandatory, overflow throws |

9 · Bit metrics — from the magnitude, never via Number

| Function | num-im | Returns | |---|---|---| | bitWidth(n) | bw | bits needed for |n|; 0 for 0 | | bitCeil(n) | bc | smallest power of two ≥ bitWidth(n) | | fitsBits(n, width) | fits | boolean | | leadingZeros(n, width?) / leadingOnes(n, width?) | clz / clo | in a width-bit view | | trailingZeros(n) | ctz | trailing zero digits in the base | | trailingTopDigits(n) | cto | trailing top-glyph digits: ones in binary, nines in decimal | | popCount(n, { digit }?) | popcnt | set bits in base 2; occurrences of digit (default: the top glyph) elsewhere |

10 · Digit surgery — zeros are not silently trimmed

| Function | num-im | Returns | |---|---|---| | rotateDigits(n, k) | rotdig | circular rotation; negative k rotates right | | reverseDigits(n, { keepLeadingZeros }?) | mirror | '1200' → '21', or '0021' | | digitCount(n, base = 10) | digits | digits of the integer part — base is positional here, as in the engine | | digitAt(n, k, { from }?) | digitat | value of the k-th digit; from: 'lsd' (default) or 'msd' | | setDigit(n, k, d) | setdigit | replace the k-th (least-significant-indexed) digit, re-canonicalized |

11 · Format and parse — presentation is not value

| Function | num-im | Returns | |---|---|---| | format(n, { group, groupSize, fracDigits, signStyle }?) | fmt | grouped, fixed-fraction, styled output | | scientific(n, { marker, sigdigits, eng }?) | sci | scientific / engineering notation; the exponent is a power of base | | canonical(n) | trim | cosmetic noise stripped: '007.50' → '7.5' | | parts(n) | split | { sign, integer, fraction } | | parseLeading(str, { max, sign, float }?) | getnum | the leading number: { value, end }, or null | | parse(str) | parsenum | the whole string; trailing junk throws SyntaxError | | toBigInt(str) | parsebig | a canonical decimal integer → BigInt | | stringify(n, { from, base }?) | stringify | render a value read in from into base | | convertBase(n, { from, to }) | rebase | the headline: the same value in another notation | | + toBinary / toOctal / toHex (n, { base, precision }?) | | n read in base (default 10), rendered in 2 / 8 / 16 | | + fromBinary / fromOctal / fromHex (n, { base, precision }?) | | the other way round | | + toNumber(n, opts?) | | a JS number; throws RangeError when |n| > 2⁵³ − 1 — a fraction becomes the nearest double |

12 · Predicates — canonical form, by regex

| Function | num-im | Asks | |---|---|---| | isNumber / isInteger / isFloat (str, { base, sign, float }?) | isnum / isint / isfloat | valid number / integer / float in base? | | isHex / isDecimal / isOctal / isBinary (str) | ishex / isdec / isoct / isbin | valid in base 16 / 10 / 8 / 2? | | isEven(str) / isOdd(str) | iseven / isodd | value parity; false for a fraction or a non-number | | isPower(n, k = 2) | ispow | exact k-th power? | | + isSafeInteger(str) | | a canonical integer within ±(2⁵³ − 1) — Number.isSafeInteger for digit-strings, in any base |

13 · Aggregates — exact, base-aware; offset trims the input first

| Function | num-im | Returns | |---|---|---| | min(xs) / max(xs) | min / max | undefined on empty input | | sum(xs) / product(xs) | sum / prod | | | cumulativeSum(xs) | cumsum | string[] — running totals | | mean(xs, { unique }?) / median(xs) | avg / med | undefined on empty input | | mode(xs, { all }?) | mode | the most frequent value; all: true returns every tied winner | | spread(xs) | range | max − min | | variance(xs, { sample }?) / standardDeviation(xs) | variance / stdev | population by default | | quantile(xs, q) | quantile | interpolated, q in [0, 1] | | clamp(n, max, { min }?) | clamp | the lower bound is optional, as in the engine | | abs(n) / sign(n) / signedInfinity(n) | abs / sign / infinity | sign → -1 / 0 / 1, or NaN | | quantize(n, size, { origin, rounding }?) | step | snap to a grid | | differences(xs, { order }?) | diff | string[] — the discrete derivative | | normalize(xs) | norm | string[] — min-max into [0, 1] | | rescale(n, { inLo, inHi, outLo, outHi }) | scale | linear rescale |

14 · Sequences and combinatorics — exact where a double overflows

| Function | num-im | Returns | |---|---|---| | range(n, { start, step }?) | iota | string[] — [start, start + step, …) of length n | | factorial(n) / fibonacci(n) / binomial(n, k) | fact / fib / choose | exact | | random({ base, digits, max, secure }?) | random | a random canonical number; secure uses Web Crypto |

15 · Checksums

| Function | num-im | Returns | |---|---|---| | digitSum(n) | digitsum | sum of the digit values of the integer part | | digitalRoot(n) | droot | 1 + (n − 1) mod (base − 1) |


TypeScript

Declarations ship with the package; nothing to install from @types. Option and result types are the engine's own, re-exported: Numeric, BaseSpec, Trit, Enumerable, RoundMode, every *Options interface and LeadingNumber, plus QuotientRemainder, Parts, RoundOptions, ConvertOptions and NumEz (the shape of the default export).

import ez, { compare, divideWithRemainder, toHex } from '@slnknrr/num-ez';
import type { Numeric, Trit } from '@slnknrr/num-ez';

const order: Trit = compare('10', '9');            // -1 | 0 | 1
divideWithRemainder('17', '5').quotient;           // string
toHex('255', { to: 2 });                           // error: `to` is convertBase's key, not toHex's
ez.add = () => '';                                 // error: the default export is frozen

types/num-ez.d.ts is written by hand and checked against types/api.test-d.ts with npm run types.


When to reach for num-im instead

  • You want the tri-state eq — "equal in value but written differently" as a distinct answer.
  • You want one digit at a time from a lazy generator, not an array.
  • You want the engine's own names, or the exported base-10 oracle regexes (intre, uintre, floatre, ufloatre).

Both packages can be used side by side; num-ez never shadows anything in the engine.


Scripts

| Command | Does | |---|---| | npm test | behavioral suite (node --test, zero dev dependencies) | | npm run types | type-check the shipped declarations (tsc --noEmit) |


Links

Author

Yury Slinkin (Юрий Слинкин)

License

MIT. See LICENSE.md.