# Quaternion

[JS](../../..) ⟩ [value](../..) ⟩ [primitive](..) ⟩ [number]() ⟩ Quaternion

{% tabs %}
{% tab title="💾 程式" %}
* replit ⟩ [Quaternion](https://replit.com/@pegasusroe/Quaternions#Quaternion.js), require ⟩  [number+ext](number+ext "mention"), [random](random "mention")

```javascript
// 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)
// -------------------------------------------------

const _Number = require('./Number+ext.js');        // 2023.03.01 - 10:11
const { Random } = require('./Random.js');
const { sqrt } = Math;

// ⭐ Quaternion
// --------------------------------------------------
// - Quaternion.zero, .one, .i, .j, .k
// - Quaternion.randomInt()
// - Quaternion.randomFloat()
// --------------------------------------------------
// - .conjugate
// - .normSquare
// - .norm
// - .inverse
// --------------------------------------------------
// - .add()
// - .subtract()
// - .multiply()
// - .divide()
// - .scale()
// --------------------------------------------------
// - .distance()
// - .equal()
//
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, b, c, d) {
        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),
        )
    }


    // ----------------------
    //     static methods
    // ----------------------

    // 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);
    }

    // normSquare
    get normSquare() {
        const { a, b, c, d } = this;
        return (a * a + b * b + c * c + d * d);
    }

    // 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);
    }

    // ---------------------------
    //   ⭐ 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)})`;
    }
}


// export
module.exports = {
    Quaternion,
};
```
{% endtab %}
{% endtabs %}
