# Mathematics with Python and Ruby/Quaternions in Ruby

# Complex numbers[edit | edit source]

As was seen in the preceding chapter, a complex number is an object comprising 2 real numbers (called *real* and *imag* by *Ruby*). This is the Cayley-Dickson construction of the complex numbers. In a very similar manner, a quaternion can be considered as made of 2 complex numbers.

In all the following, *cmath* will be used as it handles fractions automatically. This chapter is in some way different from the preceding ones, as it shows how to create brand new objects in *Ruby*, and not how to use already available objects.

# Quaternions[edit | edit source]

## Definition and display[edit | edit source]

### Definition[edit | edit source]

The definition of a quaternion finds its shelter in a class which is called *Quaternion*:

```
class Quaternion
end
```

The first method of a quaternion will be its instantiation:

#### Instantiation[edit | edit source]

```
def initialize(a,b)
@a,@b = a,b
end
```

From now on, *a* and *b* (which will be complex numbers) will be the 2 quaternion's attributes

#### Attributes a and b[edit | edit source]

As the two numbers which define a quaternion are complex, it is not appropriate to call them the *real* and *imaginary* parts. Besides, an other stage will be necessary with the octonions later on. So the shortest names have been chosen, and they will be called the *a* of the quaternion, and its *b*.

```
def a
@a
end
def b
@b
end
```

From now on it is possible to access to the *a* and *b* part of a quaternion *q* with *q.a* and *q.b*.

### Display[edit | edit source]

In order that it be easy to display a quaternion *q* with *puts(q)* it is necessary to redefine a method *to_s* for it (a case of polymorphism). There are several choices but this one works OK:

```
def to_s
'('+a.real.to_s+')+('+a.imag.to_s+')i+('+b.real.to_s+')j+('+b.imag.to_s+')k'
end
```

To read it loud it is better to read from right to left. For example, *a.real* denotes *the real part of a* and *q.a.real* denotes *the real part of the a part of q*.

## Functions[edit | edit source]

### Modulus[edit | edit source]

The absolute value of a quaternion is a (positive) real number.

```
def abs
Math.hypot(@a.abs,@b.abs)
end
```

### Conjugate[edit | edit source]

The conjugate of a quaternion is another quaternion, having the same modulus.

```
def conj
Quaternion.new(@a.conj,-@b)
end
```

## Operations[edit | edit source]

### Addition[edit | edit source]

To add two quaternions, just add their *a*s together, and their *b*s together:

```
def +(q)
Quaternion.new(@a+q.a,@b+q.b)
end
```

### Subtraction[edit | edit source]

The use of the *-* symbol is an other case of polymorphism, which allows to write rather simply the subtraction.

```
def -(q)
Quaternion.new(@a-q.a,@b-q.b)
end
```

### Multiplication[edit | edit source]

Multiplication of the quaternions is more complex (!):

```
def *(q)
Quaternion.new(@a*q.a-@b*q.b.conj,@a*q.b+@b*q.a.conj)
end
```

This multiplication is not commutative, as can be checked by the following examples:

```
p=Quaternion.new(Complex(2,1),Complex(3,4))
q=Quaternion.new(Complex(2,5),Complex(-3,-5))
puts(p*q)
puts(q*p)
```

### Division[edit | edit source]

The division can be defined as this:

```
def /(q)
d=q.abs**2
Quaternion.new((@a*q.a.conj+@b*q.b.conj)/d,(-@a*q.b+@b*q.a)/d)
end
```

As they have the same modulus, the quotient of a quaternion by its conjugate has modulus one:

```
p=Quaternion.new(Complex(2,1),Complex(3,4))
puts((p/p.conj).abs)
```

This last example digs that , or , which is a decomposition of as a sum of 4 squares.

## Quaternion class in Ruby[edit | edit source]

The complete class is here:

```
require 'cmath'
class Quaternion
def initialize(a,b)
@a,@b = a,b
end
def a
@a
end
def b
@b
end
def to_s
'('+a.real.to_s+')+('+a.imag.to_s+')i+('+b.real.to_s+')j+('+b.imag.to_s+')k'
end
def +(q)
Quaternion.new(@a+q.a,@b+q.b)
end
def -(q)
Quaternion.new(@a-q.a,@b-q.b)
end
def *(q)
Quaternion.new(@a*q.a-@b*q.b.conj,@a*q.b+@b*q.a.conj)
end
def abs
Math.hypot(@a.abs,@b.abs)
end
def conj
Quaternion.new(@a.conj,-@b)
end
def /(q)
d=q.abs**2
Quaternion.new((@a*q.a.conj+@b*q.b.conj)/d,(-@a*q.b+@b*q.a.conj)/d)
end
end
```

If this content is saved in a text file called *quaternion.rb*, after *require 'quaternion' * one can make computations on quaternions.

# Octonions[edit | edit source]

One interesting fact about the Cayley-Dickson which has been used for the quaternions above, is that it can be generalized, for example for the octonions.

## Definition and display[edit | edit source]

### Definition[edit | edit source]

All the following methods will be enclosed in a class called *Octonion*:

```
class Octonion
def initialize(a,b)
@a,@b = a,b
end
def a
@a
end
def b
@b
end
```

At this point, there is not much difference from the quaternion object. Only, for an octonion, *a* and *b* will be quaternions, not complex numbers. *Ruby* will know it when *a* and *b* will be instantiated.

### Display[edit | edit source]

The *to_s* method of an octonion (converting it to a string object so that it can be displayed) is very similar to the quaternion equivalent, only there are 8 real numbers to display now:

```
def to_s
'('+a.a.real.to_s+')+('+a.a.imag.to_s+')i+('+a.b.real.to_s+')j+('+a.b.imag.to_s+')k+('+b.a.real.to_s+')l+('+b.a.imag.to_s+')li+('+b.b.real.to_s+')lj+('+b.b.imag.to_s+')lk'
end
```

The first of these numbers is the real part of the *a* part of the first quaternion, which is the octonions's *a*! Accessing to this *real part of the a part of the octonion's a part*, requires to go through a binary tree which depth is 3.

## Functions[edit | edit source]

Thanks to Cayley and Dickson, the methods needed for octonions computing are similar to the quaternion's.

### Modulus[edit | edit source]

Same than for the quaternions:

```
def abs
Math.hypot(@a.abs,@b.abs)
end
```

### Conjugate[edit | edit source]

```
def conj
Octonion.new(@a.conj,Quaternion.new(0,0)-@b)
end
```

## Operations[edit | edit source]

### Addition[edit | edit source]

Like for the quaternions, one has just to add the *a*s and the *b*s separately (only now the *a* and *b* part are quaternions):

```
def +(o)
Octonion.new(@a+o.a,@b+o.b)
end
```

### Subtraction[edit | edit source]

```
def -(o)
Octonion.new(@a-o.a,@b-o.b)
end
```

### Multiplication[edit | edit source]

```
def *(o)
Octonion.new(@a*o.a-o.b*@b.conj,@a.conj*o.b+o.a*@b)
end
```

This multiplication is still not commutative, but it is even not associative either!

```
m=Octonion.new(p,q)
n=Octonion.new(q,p)
o=Octonion.new(p,p)
puts((m*n)*o)
puts(m*(n*o))
```

### Division[edit | edit source]

```
def /(o)
d=1/o.abs**2
Octonion.new((@a*o.a.conj+o.b*@b.conj)*Quaternion.new(d,0),(Quaternion.new(0,0)-@a.conj*o.b+o.a.conj*@b)*Quaternion.new(d,0))
end
```

Here again, the division of an octonion by its conjugate has modulus 1:

```
puts(m/m.conj)
puts((m/m.conj).abs)
```

## The octonion class in Ruby[edit | edit source]

The file is not much heavier than the quaternion's one:

```
class Octonion
def initialize(a,b)
@a,@b = a,b
end
def a
@a
end
def b
@b
end
def to_s
'('+a.a.real.to_s+')+('+a.a.imag.to_s+')i+('+a.b.real.to_s+')j+('+a.b.imag.to_s+')k+('+b.a.real.to_s+')l+('+b.a.imag.to_s+')li+('+b.b.real.to_s+')lj+('+b.b.imag.to_s+')lk'
end
def +(o)
Octonion.new(@a+o.a,@b+o.b)
end
def -(o)
Octonion.new(@a-o.a,@b-o.b)
end
def *(o)
Octonion.new(@a*o.a-o.b*@b.conj,@a.conj*o.b+o.a*@b)
end
def abs
Math.hypot(@a.abs,@b.abs)
end
def conj
Octonion.new(@a.conj,Quaternion.new(0,0)-@b)
end
def /(o)
d=1/o.abs**2
Octonion.new((@a*o.a.conj+o.b*@b.conj)*Quaternion.new(d,0),(Quaternion.new(0,0)-@a.conj*o.b+o.a.conj*@b)*Quaternion.new(d,0))
end
end
```

Saving it as *octonions.rb*, any script beginning by

```
require 'octonions'
```

allows computing on octonions.