👔Number+ext
🚧 under construction
JS ⟩ value ⟩ primitive ⟩ number ⟩ +ext
extend Number with custom methods.
replit ⟩ Number extension (2)
// 2023.03.04 - 06:50 (+) Quaternion, n.asQuaternion
// 2023.03.01 - 13:30 (/) isEqualTo -> equal, Number.EPSILON -> 1e-10
// (-) areEqualNumbers()
// (+) .round()
// 2023.01.20 - 16:45 (+) deg
// 2023.01.20 - 11:47 (+) .clamp(), .isEqualTo (*first recorded)
// -----------------------------------------------------
const { Random } = require('./Random.js');
const { sqrt, abs } = Math;
const {log} = console;
// deg
const deg = Math.PI / 180;
// ⭐ Number+ext
// -----------------------------------------------------
// (constants)
// - deg // degree = PI/180
// -----------------------------------------------------
// (quaternion)
// 🔸 n.asQuaternion
//
// -----------------------------------------------------
// (integer)
// 🔸 n.isEven
// 🔸 n.isOdd
// 🔸 n.binary
// 🔸 n.octal
// 🔸 n.hex
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
// 🔹 n.toOctal({prefix:, pad: {length:, char:})
// 🔹 n.toHex({prefix:, pad: {length:, char:})
// -----------------------------------------------------
// 🔹 .clamp()
// 🔹 .equal()
// 🔹 .round()
//
Object.defineProperties(Number.prototype, {
// ---------------
// quaternion
// ---------------
// 🔸 n.asQuaternion
asQuaternion: {
get() { return new Quaternion(this) },
},
// ---------------
// integer
// ---------------
// 🔸 n.isEven
isEven: {
get() { return this % 2 === 0 },
},
// 🔸 n.isOdd
isOdd: {
get() { return Math.abs(this % 2) === 1 },
},
// 🔸 n.binary
binary: {
get() { return this.toString(2) },
},
// 🔸 n.octal
octal: {
get() { return this.toString(8) },
},
// 🔸 n.hex
hex: {
get() { return this.toString(16).toUpperCase() },
},
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
toBinary: {
value: formatFunc('0b', 2),
},
// 🔹 n.toOctal({prefix:, pad: {length:, char:}})
toOctal: {
value: formatFunc('0o', 8),
},
// 🔹 n.toHex({prefix:, pad: {length:, char:}})
toHex: {
value: formatFunc('0x', 16),
},
// ---------------
// range
// ---------------
// 🔹 .clamp(min, max)
clamp: {
value: function(min, max){
if (min > max) [min, max] = [max, min];
return Math.min(max, Math.max(min, this));
},
},
// 🔹 .equal()
equal: {
value: function(y, {threshold=1e-10}={}){
return abs(this - y) < threshold;
},
},
// 🔹 .round()
round: {
value: function(places){
return +this.toFixed(places);
},
},
});
// ⭐ helpers
// -----------------------------------------------------
// general format function
function formatFunc(pre, base) {
return function format({
prefix = pre,
pad = { length: 0, char: '0' },
} = {}) {
// ⭐ `pad` might be overridden, better destructure again
const { length = 0, char = '0' } = pad;
return prefix +
this.toString(base).padStart(length, char).toUpperCase();
}
}
// 2023.03.04 - 07:19 (+) .scalarPart, .vectorPart
// 2023.03.04 - 06:50 (•) -------- merged into "Number+ext" --------
// 2023.03.03 - 17:08 (+) .dot, .cross
// 2023.03.01 - 13:42 (+) .randomInt, .randomFloat, .toString
// 2023.02.24 - 15:33 (+) static members, .conjugate, .norm, .inverse, ...
// 2023.02.24 - 13:35 (•) first draft (by chatGPT)
// -------------------------------------------------
// ⭐ Quaternion
// --------------------------------------------------
// - Quaternion.zero, .one, .i, .j, .k
// - Quaternion.randomInt()
// - Quaternion.randomFloat()
// --------------------------------------------------
// - .scalarPart, vectorPart
// - .conjugate
// - .normSquare
// - .norm
// - .inverse
// --------------------------------------------------
// - .add() p + q
// - .subtract() p - q
// - .multiply() pq
// - .divide() p/q = p q^(-1)
// - .scale() kp
// - .dot() p • q
// - .cross() p x q
// --------------------------------------------------
// - .distance() |p-q|
// - .equal() p == q
//
class Quaternion {
// i, j, k
static get zero() { return new Quaternion(0, 0, 0, 0) }
static get one() { return new Quaternion(1, 0, 0, 0) }
static get i() { return new Quaternion(0, 1, 0, 0) }
static get j() { return new Quaternion(0, 0, 1, 0) }
static get k() { return new Quaternion(0, 0, 0, 1) }
// init
constructor(a=0, b=0, c=0, d=0) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
// ----------------------
// static methods
// ----------------------
// Quaternion.randomInt()
static randomInt(min, max) {
return new Quaternion(
Random.int(min, max),
Random.int(min, max),
Random.int(min, max),
Random.int(min, max),
)
}
// Quaternion.randomFloat()
static randomFloat(min, max) {
return new Quaternion(
Random.float(min, max),
Random.float(min, max),
Random.float(min, max),
Random.float(min, max),
)
}
// ----------------------
// getters
// ----------------------
// scalarPart
get scalarPart() {
return this.a;
}
// vectorPart
get vectorPart() {
const { b, c, d } = this;
return new Quaternion(0, b, c, d);
}
// conjugate
get conjugate() {
const { a, b, c, d } = this;
return new Quaternion(a, -b, -c, -d);
}
// inverse (reciprocal)
get inverse() {
const n = this.normSquare;
return this.conjugate.scale(1 / n);
}
// |p|^2 = p • p
get normSquare() {
return this.dot(this);
}
// norm
get norm() {
const { a, b, c, d } = this;
return sqrt(this.normSquare);
}
// ---------------------------
// ⭐ + - * / operations
// ---------------------------
add(q) {
const { a, b, c, d } = this;
return new Quaternion(
a + q.a, b + q.b, c + q.c, d + q.d
);
}
subtract(q) {
const { a, b, c, d } = this;
return new Quaternion(
a - q.a, b - q.b, c - q.c, d - q.d
);
}
multiply(q) {
const { a, b, c, d } = this;
return new Quaternion(
a * q.a - b * q.b - c * q.c - d * q.d,
a * q.b + b * q.a + c * q.d - d * q.c,
a * q.c - b * q.d + c * q.a + d * q.b,
a * q.d + b * q.c - c * q.b + d * q.a,
);
}
// p/q = p q^(-1)
// ❗ note: p q^(-1) !== q^(-1) p
divide(q) {
return this.multiply(q.inverse);
}
// kq
scale(k) {
const { a, b, c, d } = this;
return new Quaternion(k * a, k * b, k * c, k * d);
}
// p • q
dot(q) {
const { a, b, c, d } = q;
return this.a * a + this.b * b + this.c * c + this.d * d;
}
// p x q = (p • q) - bar(p) q
cross(q) {
return this.dot(q).asQuaternion.subtract(
this.conjugate.multiply(q)
);
}
// ---------------------------
// ⭐ helpers
// ---------------------------
// distance
distance(q) {
return this.subtract(q).norm;
}
// equal
equal(q, {threshold=1e-10}={}) {
return this.distance(q).equal(0, {threshold});
}
// toString
toString(places=3) {
const { a, b, c, d } = this;
return `(${a.round(places)},${b.round(places)},${c.round(places)},${d.round(places)})`;
}
}
// ⭐ CommonJS export
module.exports = {
Quaternion,
deg,
};
// export {deg}; // ES module export
'use strict'; // ⭐ toggle sloppy/strict mode
const { log } = console;
// ⭐ import
const _Number = require('./ext/Number+ext.js'); // Number+ext
// ---------------------------------------------------------------------------
const e = Number.EPSILON; // 2^(-52) (very small)
const BIG = Math.pow(10, 300); // 10^300 (very big)
// ✅ log expressions that never throw
// ---------------------------------------------------------------------------
;[
// without prefix
(45).binary, // '101101'
(88).octal, // '130'
(123).hex, // '7B'
// with prefix
(123).toBinary(), // '0b1111011'
(123).toOctal(), // '0o173'
(123).toHex(), // '0x7B'
// with options
(123).toHex({pad: {length: 4}}), // '0x007B'
(123).toHex({pad: {char: '_'}}), // '0x7B' (lenth = 0, by default)
(123).toHex({pad: {char: '_', length: 6}}), // '0x____7B'
(123).toHex({prefix: '#', pad: {length: 3}}), // '#07B'
// equality
Number.EPSILON, // 2.220446049250313e-16 = 2^(-52)
(0.1 + 0.2) - 0.3, // 5.551115123125783e-17 < e
0.1 + 0.2 === 0.3, // ❗️ false (should be true)
(0.1 + 0.2).isEqualTo(0.3), // true
1.5 + e > 1.5, // true
2.5 + e > 2.5, // ❗️ false (should be true)
BIG + 1 > BIG, // ❗️ false (should be true)
BIG + 1 === BIG, // ❗️ true (should be false)
// test threshold
(0.1).isEqualTo(0.11, {threshold: 0.05}), // true
].forEach(x => log(x));
Vector - use
.isEqualTo()
.Canvas+ext - use
deg
.
parseInt() - global function
Number.parseInt() === parseInt()
Number.parseFloat() === parseFloat()
History
0 (•) first version
1 (+) .clamp(), .isEqualTo()
2 (/) .isEqualTo() renamed to .equal()
3 (/) Number.EPSILON -> 1e-10
// ----------------------------------
// Number.prototype extension
// ----------------------------------
// 🔸 n.isEven
// 🔸 n.isOdd
// 🔸 n.binary
// 🔸 n.octal
// 🔸 n.hex
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
// 🔹 n.toOctal({prefix:, pad: {length:, char:})
// 🔹 n.toHex({prefix:, pad: {length:, char:})
// ⭐ multiple of
Object.defineProperties(Number.prototype, {
// 🔸 n.isEven
isEven: {
get() { return this % 2 === 0 },
},
// 🔸 n.isOdd
isOdd: {
get() { return Math.abs(this % 2) === 1 },
},
// 🔸 n.binary
binary: {
get() { return this.toString(2) },
},
// 🔸 n.octal
octal: {
get() { return this.toString(8) },
},
// 🔸 n.hex
hex: {
get() { return this.toString(16).toUpperCase() },
},
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
toBinary: {
value: formatFunc('0b', 2),
},
// 🔹 n.toOctal({prefix:, pad: {length:, char:}})
toOctal: {
value: formatFunc('0o', 8),
},
// 🔹 n.toHex({prefix:, pad: {length:, char:}})
toHex: {
value: formatFunc('0x', 16),
},
});
// ⭐ general function
function formatFunc(pre, base) {
return function format({
prefix = pre,
pad = { length: 0, char: '0' },
} = {}) {
// ⭐ `pad` might be overridden, better destructure again
const { length = 0, char = '0' } = pad;
return prefix +
this.toString(base).padStart(length, char).toUpperCase();
}
}
// CommonJS export
module.exports = {};
// 2023.01.20 - 11:47 (+) .clamp(), .isEqualTo
// -----------------------------------------------------
// ⭐ Number+ext
// -----------------------------------------------------
// (integer-related)
// 🔸 n.isEven
// 🔸 n.isOdd
// 🔸 n.binary
// 🔸 n.octal
// 🔸 n.hex
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
// 🔹 n.toOctal({prefix:, pad: {length:, char:})
// 🔹 n.toHex({prefix:, pad: {length:, char:})
// -----------------------------------------------------
// 🔹 .clamp()
// 🔹 .isEqualTo()
//
Object.defineProperties(Number.prototype, {
// ---------------
// integer-related
// ---------------
// 🔸 n.isEven
isEven: {
get() { return this % 2 === 0 },
},
// 🔸 n.isOdd
isOdd: {
get() { return Math.abs(this % 2) === 1 },
},
// 🔸 n.binary
binary: {
get() { return this.toString(2) },
},
// 🔸 n.octal
octal: {
get() { return this.toString(8) },
},
// 🔸 n.hex
hex: {
get() { return this.toString(16).toUpperCase() },
},
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
toBinary: {
value: formatFunc('0b', 2),
},
// 🔹 n.toOctal({prefix:, pad: {length:, char:}})
toOctal: {
value: formatFunc('0o', 8),
},
// 🔹 n.toHex({prefix:, pad: {length:, char:}})
toHex: {
value: formatFunc('0x', 16),
},
// ---------------
// range
// ---------------
// 🔹 .clamp(min, max)
clamp: {
value: function(min, max){
if (min > max) [min, max] = [max, min];
return Math.min(max, Math.max(min, this));
},
},
// 🔹 .isEqualTo()
isEqualTo: {
value: function(n, {threshold = Number.EPSILON}={}){
return areEqualNumbers(this, n, {threshold});
},
},
});
// ⭐ helpers
// -----------------------------------------------------
// general format function
function formatFunc(pre, base) {
return function format({
prefix = pre,
pad = { length: 0, char: '0' },
} = {}) {
// ⭐ `pad` might be overridden, better destructure again
const { length = 0, char = '0' } = pad;
return prefix +
this.toString(base).padStart(length, char).toUpperCase();
}
}
// areEqualNumbers(x, y {threshold})
function areEqualNumbers(x, y, {threshold = Number.EPSILON}={}) {
return Math.abs(x - y) < threshold;
}
// ⭐ export
module.exports = {}; // CommonJS export
// export default {}; // ES module export
// 2023.01.20 - 16:45 (+) deg
// 2023.01.20 - 11:47 (+) .clamp(), .isEqualTo (*first recorded)
// -----------------------------------------------------
// ⭐ Number+ext
// -----------------------------------------------------
// (constants)
// - deg // degree = PI/180
// -----------------------------------------------------
// (integer-related)
// 🔸 n.isEven
// 🔸 n.isOdd
// 🔸 n.binary
// 🔸 n.octal
// 🔸 n.hex
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
// 🔹 n.toOctal({prefix:, pad: {length:, char:})
// 🔹 n.toHex({prefix:, pad: {length:, char:})
// -----------------------------------------------------
// 🔹 .clamp()
// 🔹 .isEqualTo()
//
Object.defineProperties(Number.prototype, {
// ---------------
// integer-related
// ---------------
// 🔸 n.isEven
isEven: {
get() { return this % 2 === 0 },
},
// 🔸 n.isOdd
isOdd: {
get() { return Math.abs(this % 2) === 1 },
},
// 🔸 n.binary
binary: {
get() { return this.toString(2) },
},
// 🔸 n.octal
octal: {
get() { return this.toString(8) },
},
// 🔸 n.hex
hex: {
get() { return this.toString(16).toUpperCase() },
},
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
toBinary: {
value: formatFunc('0b', 2),
},
// 🔹 n.toOctal({prefix:, pad: {length:, char:}})
toOctal: {
value: formatFunc('0o', 8),
},
// 🔹 n.toHex({prefix:, pad: {length:, char:}})
toHex: {
value: formatFunc('0x', 16),
},
// ---------------
// range
// ---------------
// 🔹 .clamp(min, max)
clamp: {
value: function(min, max){
if (min > max) [min, max] = [max, min];
return Math.min(max, Math.max(min, this));
},
},
// 🔹 .isEqualTo()
isEqualTo: {
value: function(n, {threshold = Number.EPSILON}={}){
return areEqualNumbers(this, n, {threshold});
},
},
});
// ⭐ helpers
// -----------------------------------------------------
// general format function
function formatFunc(pre, base) {
return function format({
prefix = pre,
pad = { length: 0, char: '0' },
} = {}) {
// ⭐ `pad` might be overridden, better destructure again
const { length = 0, char = '0' } = pad;
return prefix +
this.toString(base).padStart(length, char).toUpperCase();
}
}
// areEqualNumbers(x, y {threshold})
function areEqualNumbers(x, y, {threshold = Number.EPSILON}={}) {
return Math.abs(x - y) < threshold;
}
// deg
const deg = Math.PI / 180;
// ⭐ export
module.exports = {deg}; // CommonJS export
// export {deg}; // ES module export
// 2023.03.01 - 13:30 (/) isEqualTo -> equal, Number.EPSILON -> 1e-10
// (-) areEqualNumbers()
// (+) .round()
// 2023.01.20 - 16:45 (+) deg
// 2023.01.20 - 11:47 (+) .clamp(), .isEqualTo (*first recorded)
// -----------------------------------------------------
const {abs} = Math;
const {log} = console;
// deg
const deg = Math.PI / 180;
// ⭐ Number+ext
// -----------------------------------------------------
// (constants)
// - deg // degree = PI/180
// -----------------------------------------------------
// (integer-related)
// 🔸 n.isEven
// 🔸 n.isOdd
// 🔸 n.binary
// 🔸 n.octal
// 🔸 n.hex
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
// 🔹 n.toOctal({prefix:, pad: {length:, char:})
// 🔹 n.toHex({prefix:, pad: {length:, char:})
// -----------------------------------------------------
// 🔹 .clamp()
// 🔹 .equal()
//
Object.defineProperties(Number.prototype, {
// ---------------
// integer-related
// ---------------
// 🔸 n.isEven
isEven: {
get() { return this % 2 === 0 },
},
// 🔸 n.isOdd
isOdd: {
get() { return Math.abs(this % 2) === 1 },
},
// 🔸 n.binary
binary: {
get() { return this.toString(2) },
},
// 🔸 n.octal
octal: {
get() { return this.toString(8) },
},
// 🔸 n.hex
hex: {
get() { return this.toString(16).toUpperCase() },
},
// 🔹 n.toBinary({prefix:, pad: {length:, char:}})
toBinary: {
value: formatFunc('0b', 2),
},
// 🔹 n.toOctal({prefix:, pad: {length:, char:}})
toOctal: {
value: formatFunc('0o', 8),
},
// 🔹 n.toHex({prefix:, pad: {length:, char:}})
toHex: {
value: formatFunc('0x', 16),
},
// ---------------
// range
// ---------------
// 🔹 .clamp(min, max)
clamp: {
value: function(min, max){
if (min > max) [min, max] = [max, min];
return Math.min(max, Math.max(min, this));
},
},
// 🔹 .equal()
equal: {
value: function(y, {threshold=1e-10}={}){
return abs(this - y) < threshold;
},
},
// 🔹 .round()
round: {
value: function(places){
return +this.toFixed(places);
},
},
});
// ⭐ helpers
// -----------------------------------------------------
// general format function
function formatFunc(pre, base) {
return function format({
prefix = pre,
pad = { length: 0, char: '0' },
} = {}) {
// ⭐ `pad` might be overridden, better destructure again
const { length = 0, char = '0' } = pad;
return prefix +
this.toString(base).padStart(length, char).toUpperCase();
}
}
// ⭐ export
module.exports = {deg}; // CommonJS export
// export {deg}; // ES module export
Last updated