#!/usr/bin/ruby

#
## https://rosettacode.org/wiki/Quaternion_type
#

class Quaternion(r, i, j, k) {

    func qu(*r) { __CLASS__(r...) }

    method to_s  { "#{r} + #{i}i + #{j}j + #{k}k" }
    method reals { [r, i, j, k] }
    method conj  { qu(r, -i, -j, -k) }
    method norm  { self.reals.map { _*_ }.sum.sqrt }

    method ==(__CLASS__ b) { self.reals == b.reals }

    method +(Number     b) { qu(b+r, i, j, k) }
    method +(__CLASS__ b) { qu((self.reals ~Z+ b.reals)...) }

    method neg { qu(self.reals.map{ .neg }...) }

    method *(Number     b) { qu((self.reals»*»b)...) }
    method *(__CLASS__ b) {
        var (r,i,j,k) = b.reals...
        qu(sum(self.reals ~Z* [r, -i, -j, -k]),
           sum(self.reals ~Z* [i,  r,  k, -j]),
           sum(self.reals ~Z* [j, -k,  r,  i]),
           sum(self.reals ~Z* [k,  j, -i,  r]))
    }
}

var q  = main::Quaternion(1, 2, 3, 4)
var q1 = main::Quaternion(2, 3, 4, 5)
var q2 = main::Quaternion(3, 4, 5, 6)
var r  = 7

say "1) q norm  = #{q.norm}"
say "2) -q      = #{-q}"
say "3) q conj  = #{q.conj}"
say "4) q  + r  = #{q + r}"
say "5) q1 + q2 = #{q1 + q2}"
say "6) q  * r  = #{q  * r}"
say "7) q1 * q2 = #{q1 * q2}"
say "8) q1q2 #{ q1*q2 == q2*q1 ? '==' : '!=' } q2q1"