=encoding utf8

=head1 NAME

Sidef::Types::Number::Quaternion - Quaternion number implementation

=head1 DESCRIPTION

This class implements quaternions, which are an extension of complex numbers. A quaternion has four components and can be written as C<a + bi + cj + dk>, where C<a, b, c, d> are real numbers and C<i, j, k> are the fundamental quaternion units satisfying:

    i² = j² = k² = ijk = -1

Quaternions are particularly useful in 3D graphics, robotics, and physics for representing rotations and orientations. Note that quaternion multiplication is non-commutative (C<a*b ≠ b*a> in general).

=head1 SYNOPSIS

    var a = Quaternion(1, 2, 3, 4)
    var b = Quaternion(5, 6, 7, 8)

    say a+b     #=> Quaternion(6, 8, 10, 12)
    say a-b     #=> Quaternion(-4, -4, -4, -4)
    say a*b     #=> Quaternion(-60, 12, 30, 24)
    say b*a     #=> Quaternion(-60, 20, 14, 32)  # Note: multiplication is non-commutative
    say a/b     #=> Quaternion(35/87, 4/87, 0, 8/87)
    
    # Basic operations
    say a.abs        #=> 5.477225575051661  (magnitude)
    say a.norm       #=> 30                  (squared magnitude)
    say a.conj       #=> Quaternion(1, -2, -3, -4)  (conjugate)
    say a.inv        #=> multiplicative inverse
    
    # Component access
    say a.a          #=> 1  (real part)
    say a.b          #=> 2  (i coefficient)
    say a.c          #=> 3  (j coefficient)
    say a.d          #=> 4  (k coefficient)
    say a.parts      #=> [1, 2, 3, 4]
    
    # Power and modular operations
    say a**3         #=> a cubed
    say a.sqr        #=> a squared
    say a.powmod(5, 100)  #=> (a^5) mod 100

=head1 INHERITS

Inherits methods from:

       * Sidef::Types::Number::Number

=head1 METHODS

=head2 !=

    a != b

Returns true if quaternions C<a> and C<b> are not equal, false otherwise. Two quaternions are equal if all four components are equal.

Aliases: I<ne>

=cut

=head2 %

    a % b

Returns the remainder of C<a> divided by C<b>. When C<b> is a scalar, applies modulo operation to each component. When C<b> is a quaternion, computes C<a - b * floor(a/b)>.

Aliases: I<mod>

=cut

=head2 &

    a & b

Returns the bitwise AND of quaternions C<a> and C<b>. If C<b> is a quaternion, applies AND component-wise. If C<b> is a scalar, applies AND only to the real part.

Aliases: I<and>

=cut

=head2 *

    a * b

Returns the product of C<a> and C<b> using quaternion multiplication. Note that quaternion multiplication is non-commutative (C<a*b> may not equal C<b*a>). If C<b> is a scalar, scales all components by C<b>.

The quaternion product formula is:
    (a₁ + b₁i + c₁j + d₁k)(a₂ + b₂i + c₂j + d₂k) = 
    (a₁a₂ - b₁b₂ - c₁c₂ - d₁d₂) + 
    (a₁b₂ + b₁a₂ + c₁d₂ - d₁c₂)i +
    (a₁c₂ - b₁d₂ + c₁a₂ + d₁b₂)j +
    (a₁d₂ + b₁c₂ - c₁b₂ + d₁a₂)k

Aliases: I<mul>

=cut

=head2 **

    a ** b

Returns C<a> raised to the power C<b>. For integer exponents, uses efficient binary exponentiation. For non-integer exponents, converts to complex number representation first.

Aliases: I<pow>

=cut

=head2 +

    a + b

Returns the sum of C<a> and C<b>. Addition is performed component-wise. If C<b> is a scalar, adds it only to the real part.

Aliases: I<add>

=cut

=head2 ++

    a++

Returns the quaternion C<a> incremented by 1 (adds 1 to the real part only).

Aliases: I<inc>

=cut

=head2 -

    a - b

Returns the difference of C<a> and C<b>. Subtraction is performed component-wise. If C<b> is a scalar, subtracts it only from the real part.

Aliases: I<sub>

=cut

=head2 --

    a--

Returns the quaternion C<a> decremented by 1 (subtracts 1 from the real part only).

Aliases: I<dec>

=cut

=head2 /

    a / b

Returns the quotient of C<a> divided by C<b>, computed as C<a * b.inv()>.

Aliases: I<÷>, I<div>

=cut

=head2 <

    a < b

Returns true if quaternion C<a> is less than C<b> based on lexicographic ordering (comparing components in order: a, b, c, d).

Aliases: I<lt>

=cut

=head2 <<

    a << b

Returns C<a> multiplied by C<2^b> (left shift, equivalent to C<a * 2**b>).

Aliases: I<lsft>, I<shift_left>

=cut

=head2 <=>

    a <=> b

Returns -1, 0, or 1 depending on whether C<a> is less than, equal to, or greater than C<b> using lexicographic ordering. Returns undef if comparison is not defined.

Aliases: I<cmp>

=cut

=head2 ==

    a == b

Returns true if quaternions C<a> and C<b> are equal (all components match), false otherwise.

Aliases: I<eq>

=cut

=head2 >

    a > b

Returns true if quaternion C<a> is greater than C<b> based on lexicographic ordering.

Aliases: I<gt>

=cut

=head2 >>

    a >> b

Returns C<a> divided by C<2^b> (right shift, equivalent to C<a / 2**b>).

Aliases: I<rsft>, I<shift_right>

=cut

=head2 ^

    a ^ b

Returns the bitwise XOR of quaternions C<a> and C<b>. If C<b> is a quaternion, applies XOR component-wise. If C<b> is a scalar, applies XOR only to the real part.

Aliases: I<xor>

=cut

=head2 |

    a | b

Returns the bitwise OR of quaternions C<a> and C<b>. If C<b> is a quaternion, applies OR component-wise. If C<b> is a scalar, applies OR only to the real part.

Aliases: I<or>

=cut

=head2 ≤

    a ≤ b

Returns true if quaternion C<a> is less than or equal to C<b> based on lexicographic ordering.

Aliases: I<E<lt>=>, I<le>

=cut

=head2 ≥

    a ≥ b

Returns true if quaternion C<a> is greater than or equal to C<b> based on lexicographic ordering.

Aliases: I<E<gt>=>, I<ge>

=cut

=head2 a

    self.a

Returns the real component (first component) of the quaternion. This is the coefficient of the scalar part.

    Quaternion(1, 2, 3, 4).a  #=> 1

Aliases: I<re>, I<real>

=cut

=head2 abs

    x.abs

Returns the absolute value (magnitude or modulus) of the quaternion, computed as the square root of the norm:

    |q| = √(a² + b² + c² + d²)

    Quaternion(1, 2, 3, 4).abs  #=> 5.477225575051661

=cut

=head2 b

    self.b

Returns the second component of the quaternion (coefficient of i).

    Quaternion(1, 2, 3, 4).b  #=> 2

=cut

=head2 c

    self.c

Returns the third component of the quaternion (coefficient of j).

    Quaternion(1, 2, 3, 4).c  #=> 3

=cut

=head2 ceil

    x.ceil

Returns a new quaternion with each component rounded up to the nearest integer.

    Quaternion(1.2, 2.7, 3.1, 4.9).ceil  #=> Quaternion(2, 3, 4, 5)

=cut

=head2 conj

    x.conj

Returns the conjugate of the quaternion. The conjugate negates the i, j, and k components while keeping the real part unchanged:

    conj(a + bi + cj + dk) = a - bi - cj - dk

    Quaternion(1, 2, 3, 4).conj  #=> Quaternion(1, -2, -3, -4)

The conjugate is useful for division and finding the inverse.

=cut

=head2 d

    self.d

Returns the fourth component of the quaternion (coefficient of k).

    Quaternion(1, 2, 3, 4).d  #=> 4

=cut

=head2 dump

    x.dump

Returns a string representation of the quaternion in the form C<"Quaternion(a, b, c, d)">.

    Quaternion(1, 2, 3, 4).dump  #=> "Quaternion(1, 2, 3, 4)"

=cut

=head2 eval

    x.eval(v)

Evaluates the quaternion by substituting value C<v> into each component (useful when components are symbolic expressions or polynomials).

=cut

=head2 float

    x.float

Returns a new quaternion with each component converted to floating-point representation.

=cut

=head2 floor

    x.floor

Returns a new quaternion with each component rounded down to the nearest integer.

    Quaternion(1.2, 2.7, 3.1, 4.9).floor  #=> Quaternion(1, 2, 3, 4)

=cut

=head2 inv

    x.inv

Returns the multiplicative inverse of the quaternion. The inverse is computed as:

    q⁻¹ = conj(q) / norm(q)

where norm(q) = a² + b² + c² + d².

    var q = Quaternion(1, 2, 3, 4)
    say q * q.inv  #=> Quaternion(1, 0, 0, 0)  (approximately)

=cut

=head2 invmod

    x.invmod(m)

Returns the modular multiplicative inverse of quaternion C<x> modulo C<m>. This finds a quaternion C<y> such that C<(x * y) mod m = 1>.

=cut

=head2 is_coprime

    n.is_coprime(k)

Returns true if quaternions C<n> and C<k> are coprime, meaning C<gcd(norm(n), norm(k)) = 1>.

=cut

=head2 is_mone

    x.is_mone

Returns true if the quaternion equals -1 (i.e., C<Quaternion(-1, 0, 0, 0)>).

=cut

=head2 is_one

    x.is_one

Returns true if the quaternion equals 1 (i.e., C<Quaternion(1, 0, 0, 0)>).

=cut

=head2 is_zero

    x.is_zero

Returns true if the quaternion equals 0 (i.e., C<Quaternion(0, 0, 0, 0)>).

=cut

=head2 lift

    x.lift

Applies the lift operation to each component of the quaternion (useful for symbolic computations).

=cut

=head2 neg

    x.neg

Returns the negation of the quaternion (negates all four components).

    Quaternion(1, 2, 3, 4).neg  #=> Quaternion(-1, -2, -3, -4)

=cut

=head2 new

    Quaternion.new(a, b, c, d)
    Quaternion(a, b, c, d)

Creates a new quaternion with components C<a, b, c, d>. All parameters default to 0 if not provided.

    var q1 = Quaternion(1, 2, 3, 4)     # creates 1 + 2i + 3j + 4k
    var q2 = Quaternion(5)              # creates 5 + 0i + 0j + 0k
    var q3 = Quaternion()               # creates 0 + 0i + 0j + 0k

Aliases: I<call>

=cut

=head2 norm

    x.norm

Returns the squared magnitude (norm) of the quaternion:

    norm(q) = a² + b² + c² + d²

    Quaternion(1, 2, 3, 4).norm  #=> 30

Note: C<norm(q) = abs(q)²>

=cut

=head2 parts

    self.parts

Returns an array containing all four components of the quaternion C<[a, b, c, d]>.

    Quaternion(1, 2, 3, 4).parts  #=> [1, 2, 3, 4]

=cut

=head2 powmod

    x.powmod(n, m)

Returns C<(x^n) mod m> using efficient modular exponentiation. Uses binary exponentiation algorithm for integer exponents.

=cut

=head2 pretty

    x.pretty

Returns a formatted string representation of the quaternion, converting it to Gaussian complex form for display.

Aliases: I<stringify>

=cut

=head2 reals

    self.reals

Returns the four components as a list: C<(a, b, c, d)>.

    var (w, x, y, z) = Quaternion(1, 2, 3, 4).reals

=cut

=head2 round

    x.round(r)

Returns a new quaternion with each component rounded to C<r> decimal places (or to nearest integer if C<r> is not specified).

    Quaternion(1.234, 2.567, 3.891, 4.123).round(1)  #=> Quaternion(1.2, 2.6, 3.9, 4.1)

=cut

=head2 sgn

    x.sgn

Returns the sign (versor) of the quaternion, which is the unit quaternion in the same direction:

    sgn(q) = q / |q|

This is useful for extracting rotation information.

=cut

=head2 sqr

    x.sqr

Returns the square of the quaternion (C<x * x>).

    Quaternion(1, 2, 3, 4).sqr  #=> Quaternion(-28, 4, 6, 8)

=cut

=head2 to_c

    x.to_c

Converts the quaternion to a complex number representation (via Gaussian integers). Returns a nested complex structure.

Aliases: I<to_n>

=cut

=head2 to_gauss

    x.to_gauss

Converts the quaternion to a representation using Gaussian integers (complex numbers with integer real and imaginary parts):

    Quaternion(a, b, c, d) → Gauss(Gauss(a, b), Gauss(c, d))

=cut

=head2 to_s

    x.to_s

Returns a string representation of the quaternion.

=cut

=head1 MATHEMATICAL PROPERTIES

=head2 Quaternion Multiplication

Quaternion multiplication follows these rules for the fundamental units:

    i² = j² = k² = -1
    ij = k,  jk = i,  ki = j
    ji = -k, kj = -i, ik = -j

This makes quaternion multiplication non-commutative.

=head2 Norm Properties

    norm(q₁ * q₂) = norm(q₁) * norm(q₂)
    norm(q) = q * conj(q) = conj(q) * q

=head2 Inverse

    q * q⁻¹ = q⁻¹ * q = 1
    q⁻¹ = conj(q) / norm(q)

=head1 EXAMPLES

    # Creating quaternions
    var q1 = Quaternion(1, 0, 0, 0)    # Real quaternion
    var q2 = Quaternion(0, 1, 0, 0)    # Pure imaginary (i)
    var q3 = Quaternion(0, 0, 1, 0)    # Pure imaginary (j)
    var q4 = Quaternion(0, 0, 0, 1)    # Pure imaginary (k)
    
    # Verifying fundamental properties
    say q2**2                           #=> Quaternion(-1, 0, 0, 0)
    say q2*q3                           #=> Quaternion(0, 0, 0, 1)  (= k)
    say q3*q2                           #=> Quaternion(0, 0, 0, -1) (= -k)
    
    # Unit quaternion (rotation)
    var angle = 90.deg2rad
    var axis = [0, 0, 1]  # Z-axis
    var w = cos(angle/2)
    var (x, y, z) = axis.map { _ * sin(angle/2) }...
    var rotation = Quaternion(w, x, y, z)
    say rotation.norm      #=> 1 (unit quaternion)
    
    # Quaternion division
    var a = Quaternion(1, 2, 3, 4)
    var b = Quaternion(5, 6, 7, 8)
    say a/b                #=> Quaternion(35/87, 4/87, 0, 8/87)
    say (a/b)*b            #=> approximately equal to a

=head1 SEE ALSO

L<Sidef::Types::Number::Number>, L<Sidef::Types::Number::Gauss>, L<Sidef::Types::Number::Complex>

=head1 REFERENCES

L<https://en.wikipedia.org/wiki/Quaternion>

=cut
