https://github.com/ericrovell/radix
Radix is a JS library for radix number transformations and manipulations.
https://github.com/ericrovell/radix
math number-base-conversion numbers radix
Last synced: 9 months ago
JSON representation
Radix is a JS library for radix number transformations and manipulations.
- Host: GitHub
- URL: https://github.com/ericrovell/radix
- Owner: EricRovell
- License: mit
- Created: 2021-08-22T14:37:05.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2022-10-22T21:46:16.000Z (about 3 years ago)
- Last Synced: 2024-04-26T11:01:13.208Z (over 1 year ago)
- Topics: math, number-base-conversion, numbers, radix
- Language: TypeScript
- Homepage: https://radix.vercel.app/
- Size: 247 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Radix
Radix is JavaScript library for [radix](https://en.wikipedia.org/wiki/Radix) transformations and manipulations.
## Features:
- Extendable;
- Immutable;
- No dependencies;
- Simple API;
- Types included;
- Works in a browser and Node.js;
## Motivation
JavaScript already has the [utility function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) to parse and transform numbers between different radix. But it has the limitations:
- radix value should be in range [ 2, 36 ];
- JS integers has safe treshold as [2^53 - 1](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger).
Of course, it is possible to use [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) instead, but coercion between numbers and BigInts are not that great.
This library use ranks array to represent a number: `[ 1, 2, 3 ] // same as 123` which makes it easier to work with such limitation. `BigInt` used for internal calculations so values above safe integer threshold won't be a problem.
## Getting started
The package is available via [npm](https://www.npmjs.com/package/@ericrovell/radix):
```
npm i @ericrovell/radix
```
```ts
import { radix } from "@ericrovell/radix";
radix([ 1, 0, 1, 0], 2).decimal // -> 10
```
## API
### Constructor
radix(input = [ 0 ], radix = 10, options?)
Constructs a number from given ranks and specified radix.
The input is validated, more about the validation rules in `.valid` property description.
In case of invalid input the fallback is number 0 in binary system.
```ts
radix().decimal // -> 0
radix([ 1, 0, 0 ]).decimal // -> 4
radix([ 1, 0, 0, 1, 1, 0, 1 ], 2).decimal // -> 77
radix([ 5, 0 ], 2).decimal // -> 0, invalid input
```
#### Input options
The valid types of input are:
- `number`: Any positive integer number, float values are considered invalid;
- `bigint`;
- `string`;
- `number[]`: Homogeneous array of integer numbers;
- `string[]`: Homogeneous array of string symbols;
`number` and `bigint` are considered to have the `radix = 10`, and this `radix` value is default.
`number[]` and `string[]` input decoded first (if such option is passed, more about soon), and after parsed as number ranks.
```ts
// integer
radix(132).getRanks(); // -> [ 1, 3, 2 ]
// bigint
radix(456n).getRanks(); // -> [ 4, 5, 6 ]
// string
radix("1234").getRanks(); // -> [ 1, 2, 3, 4 ]
// number[]
radix([ 5, 6, 7, 8 ]).getRanks(); // -> [ 5, 6, 7, 8 ]
// string[]
radix([ "1", "2", "3" ]).getRanks(); // -> [ 1, 2, 3 ]
```
#### Options
All constructor options are optional.
`decoding`
To define custom ranks decoding, provide a decodings object:
```ts
import type { Decodings } from "@ericrovell/radix";
const decodings: Decodings = {
"A": 0,
"B": 1,
};
radix([ "A", "B" ], 2, { decode: decodings }).getRanks(); // -> [ 1, 0 ]
```
Also, the decoder function can be provided instead:
```ts
import type { Decoder } from "@ericrovell/radix";
const decoder: Decoder = rank => rank ? "A" : rank;
radix([ "A", 1 ], 2, { decoder }).getRanks(); // -> [ 0, 1 ]
```
`minRanks`
Sets the minimal number of ranks.
```ts
radix([ 1 ], 2, { minRanks: 5 }).getRanks(); // -> [ 0, 0, 0, 0, 1 ]
radix([ 1 ], 2, { minRanks: -5 }).getRanks(); // -> [ 1 ]
radix([ 1, 2, 3, 4 ], 10, { minRanks: 3 }); // -> [ 1, 2, 3, 4 ]
```
### Methods and properties
.decimal
Returns the numeric decimal representation.
```ts
radix([ 1, 0, 1, 0 ], 2).decimal // -> 10
radix([ 2, 4, 5 ], 8).decimal // -> 165
```
Do not use if the decimal value may exceed the safe integer value as it returns `Number` instance which is not safe.
Use `.valueOf()` instead.
.getRanks(encode?: Encode)
Returns ranks the number consists of.
```ts
radix([ 1, 0, 1 ], 2).getRanks() // -> [ 1, 0, 1 ]
```
The output may be encoded using the `encode` argument.
Encoding using the the encodings object:
```ts
import type { Encodings } from "@ericrovell/radix";
const binary = {
0: "A",
1: "B"
};
radix([ 1, 0, 1, 0 ], 2).toString(binary) // -> [ "B", "A, "B", "A ]
```
Encoding using the the encoder function:
```ts
import type { Encoder } from "@ericrovell/radix";
const binaryEncoder: Encoder = rank => {
return rank === 0 ? "A" : "B"
};
radix([ 1, 0, 1, 0 ], 2).toString(binaryEncoder) // -> [ "B", "A, "B", "A ]
```
.getRank(index = 0)
Returns the rank value at specified index.
Index is tied to the rank's power:
$$ 1234 = 1 * 10^3 + 2 * 10^2 + 3 * 10^1 + 4 * 10^0$$
Here, the last rank value *4* has a power of *0*, that's how index is calculated.
```ts
const number = radix([ 1, 2, 3, 4 ], 10)
number.rank(0); // -> 4
number.rank(3); // -> 1
```
.radix
Returns number's [radix](https://en.wikipedia.org/wiki/Radix) value.
```ts
radix([ 1, 0, 1 ], 2).radix // -> 2
```
.setRadix(radix)
Changes the number's radix and returns a new `Radix` instance.
```ts
radix([ 1, 0, 1, 0 ], 2).setRadix(10).getRanks(); // [ 1, 0 ]
radix([ 1, 0, 1, 0 ], 2).setRadix(8).getRanks(); // [ 1, 2 ]
radix([ 1, 0, 1, 0 ], 2).setRadix(2).getRanks(); // [ 1, 0, 1, 0 ]
```
.setRank(value = 0, rank = 0)
Changes the value of specific rank and returns the number as new `Radix` instance.
Note: The index is tied to the power, read more at `.getRank()` method.
```ts
radix([ 1, 0, 1 ], 2).setRank(0).getRanks() // -> [ 1, 0, 0 ]);
radix([ 1, 0, 1 ], 2).setRank(1, 1).getRanks() // -> [ 1, 1, 1 ]);
radix([ 4, 0, 5, 7 ], 8).setRank(7, 3).getRanks() // -> [ 7, 0, 5, 7 ]);
radix([ 1, 0, 1, 0, 1, 1, 1, 0, 1 ], 2).setRank(1, 5).getRanks() // -> [ 1, 0, 1, 1, 1, 1, 1, 0, 1 ]);
```
.toString(encode?: Encode, sep = "")
Constructs a number's string representation.
```ts
radix([ 2, 3, 4 ], 10).toString() // -> "234"
```
The custom encoding can be specified using the encodings object or encoder function, same as `.getRanks()` method.
```ts
import type { Encodings, Encoder } from "@ericrovell/radix";
const binary = {
0: "A",
1: "B"
};
const binaryEncoder: Encoder = rank => {
return rank === 0 ? "A" : "B"
};
radix([ 1, 0, 1, 0 ], 2).toString(binary) // -> "BABA"
radix([ 1, 0, 1, 0 ], 2).toString(binaryEncoder) // -> "BABA"
```
To define a separator, provide a second argument:
```ts
radix([ 1, 0, 1, 0 ], 2).toString(undefined, "+") // -> "1+0+1+0"
```
.valid
Returns the boolean indicating whether or not the input was valid.
Radix should be positive integer equal or larger than 2. Unary base system's are not supported.
It complicated the code too much and too primitive to be practical.
Each rank should be non-negative integer and have a value less than radix.
Valid input options are covered [here](#input-options).
```ts
radix([ 1, 1, 0 ], 2).valid // -> true
radix([ 0, 1, 2, 8 ], 8).valid // -> false, rank can't be 8 for the base 8
radix([ 1, 1, 0 ], 2).valid // -> true
radix([ 1, 1, 0 ], 1.5).valid // -> false, radix should be an integer
radix([ 0, 1, 2, 8 ], 0).valid // -> false, radix should be a positive integer
radix(2.5, 10).valid // -> false, unsupported input
radix([ 0, "1", 2, 8 ], 10).valid // -> false, array should be homogeneous
```
.valueOf()
Returns the primitive value as decimal radix `BigInt` value.
```ts
radix([ 2, 3 ], 10).valueOf() // -> 23n
```
Method may be useful for coercion:
```ts
radix([ 1, 2 ], 10) + radix([ 2, 3 ], 10) // -> 35n
```
## Other features
### Coercion
The `Radix` instance supports coercion via `toString()` and `valueOf()` methods. The latter returns a `bigint` decimal representation.
```ts
radix([ 1, 2, 3 ], 10) + radix([ 7, 7 ], 10) // 200n
```
### Extedibility
To extend functionality for your needs, extend the `Radix` class available at the root path:
```ts
import { Radix } from "@ericrovell/radix";
class RadixExtended extends Radix {
constructor(ranks, radix) {
super();
// ...
}
getRanksSum() {
return this.digits.reduce(( acc, digit ) => acc + digit, 0);
}
}
const extended = new RadixExtended([ 1, 0, 1, 0 ], 2);
extended.getRanksSum() // -> 2
```
### Iterability
The `Radix` instance can be iterated via `for ... of` loop to loop through the ranks in powers order:
```ts
import { radix } from "@ericrovell/radix";
for (const [ rank, power ] of radix([ 1, 2, 3 ])) {
console.log(rank, power)
// -> [ 3, 0 ], [ 2, 1, ], [ 1, 2 ]
}
for (const [ rank, power ] of radix([ 5, 4 ], 10).setRadix(2)) {
console.log(rank, power)
// -> [ 0, 0 ], [ 1, 1 ], [ 1, 2 ], [ 0, 3 ], [ 1, 4 ], [ 1, 5 ]
}
```
The same way the `spread` operator can be used, `Array.from()`, and all other methods and functions that operates on iterables.