## Documentation ¶

### Overview ¶

Package dualquat provides the dual quaternion numeric type and functions.

Dual quaternions provide a system for rigid transformation with interpolation and blending in ℝ³. See https://www.cs.utah.edu/~ladislav/kavan06dual/kavan06dual.pdf and https://en.wikipedia.org/wiki/Dual_quaternion for more details.

Example
```package main

import (
"fmt"
"math"

"gonum.org/v1/gonum/floats/scalar"
"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)

// point is a 3-dimensional point/vector.
type point struct {
x, y, z float64
}

// raise raises the dimensionality of a point to a quaternion.
func raise(p point) quat.Number {
return quat.Number{Imag: p.x, Jmag: p.y, Kmag: p.z}
}

// raiseDual raises the dimensionality of a point to a dual quaternion.
func raiseDual(p point) dualquat.Number {
return dualquat.Number{
Real: quat.Number{Real: 1},
Dual: raise(p),
}
}

// transform performs the transformation of p by the given dual quaternions.
// The transformations are normalized to unit vectors.
func transform(p point, by ...dualquat.Number) point {
if len(by) == 0 {
return p
}

// Ensure the modulus of by is correctly scaled.
for i := range by {
if len := quat.Abs(by[i].Real); len != 1 {
by[i].Real = quat.Scale(1/len, by[i].Real)
}
}

// Perform the transformations.
q := by[0]
for _, o := range by[1:] {
q = dualquat.Mul(o, q)
}
pp := dualquat.Mul(dualquat.Mul(q, raiseDual(p)), dualquat.Conj(q))

// Extract the point.
return point{x: pp.Dual.Imag, y: pp.Dual.Jmag, z: pp.Dual.Kmag}
}

func main() {
// Translate a 1×1×1 cube by [3, 4, 5] and rotate it 120° around the
// diagonal vector [1, 1, 1].
fmt.Println("cube:")

// Construct a displacement.
displace := dualquat.Number{
Real: quat.Number{Real: 1},
Dual: quat.Scale(0.5, raise(point{3, 4, 5})),
}

// Construct a rotations.
alpha := 2 * math.Pi / 3
axis := raise(point{1, 1, 1})
rotate := dualquat.Number{Real: axis}
rotate.Real = quat.Scale(math.Sin(alpha/2)/quat.Abs(rotate.Real), rotate.Real)
rotate.Real.Real += math.Cos(alpha / 2)

for i, p := range []point{
{x: 0, y: 0, z: 0},
{x: 0, y: 0, z: 1},
{x: 0, y: 1, z: 0},
{x: 0, y: 1, z: 1},
{x: 1, y: 0, z: 0},
{x: 1, y: 0, z: 1},
{x: 1, y: 1, z: 0},
{x: 1, y: 1, z: 1},
} {
pp := transform(p,
displace, rotate,
)

// Clean up floating point error for clarity.
pp.x = scalar.Round(pp.x, 2)
pp.y = scalar.Round(pp.y, 2)
pp.z = scalar.Round(pp.z, 2)

fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
}

// Rotate a line segment from {[2, 1, 1], [2, 1, 2]} 120° around
// the diagonal vector [1, 1, 1] at its lower end.
fmt.Println("\nline segment:")

// Construct an displacement to the origin from the lower end...
origin := dualquat.Number{
Real: quat.Number{Real: 1},
Dual: quat.Scale(0.5, raise(point{-2, -1, -1})),
}
// ... and back from the origin to the lower end.
replace := dualquat.Number{
Real: quat.Number{Real: 1},
Dual: quat.Scale(-1, origin.Dual),
}

for i, p := range []point{
{x: 2, y: 1, z: 1},
{x: 2, y: 1, z: 2},
} {
pp := transform(p,
origin,  // Displace to origin.
rotate,  // Rotate around axis.
replace, // Displace back to original location.
)

// Clean up floating point error for clarity.
pp.x = scalar.Round(pp.x, 2)
pp.y = scalar.Round(pp.y, 2)
pp.z = scalar.Round(pp.z, 2)

fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
}

}
```
```Output:

cube:
0 {x:0 y:0 z:0} -> {x:5 y:3 z:4}
1 {x:0 y:0 z:1} -> {x:6 y:3 z:4}
2 {x:0 y:1 z:0} -> {x:5 y:3 z:5}
3 {x:0 y:1 z:1} -> {x:6 y:3 z:5}
4 {x:1 y:0 z:0} -> {x:5 y:4 z:4}
5 {x:1 y:0 z:1} -> {x:6 y:4 z:4}
6 {x:1 y:1 z:0} -> {x:5 y:4 z:5}
7 {x:1 y:1 z:1} -> {x:6 y:4 z:5}

line segment:
0 {x:2 y:1 z:1} -> {x:2 y:1 z:1}
1 {x:2 y:1 z:2} -> {x:3 y:1 z:1}
```
Example (Displace)
```package main

import (
"fmt"

"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)

func main() {
// Displace a point [3, 4, 5] by [4, 2, 6].
// See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion/index.htm

// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}

// Displacement vector, half [4, 2, 6], in the dual imaginary vector.
d := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 2, Jmag: 1, Kmag: 3}}

fmt.Println(dualquat.Mul(dualquat.Mul(d, p), dualquat.Conj(d)).Dual)

}
```
```Output:

(0+7i+6j+11k)
```
Example (DisplaceAndRotate)
```package main

import (
"fmt"

"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)

func main() {
// Displace a point [3, 4, 5] by [4, 2, 6] and then rotate
// by 180° around the x axis.
// See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion/index.htm

// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}

// Displacement vector, half [4, 2, 6], in the dual imaginary vector.
d := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 2, Jmag: 1, Kmag: 3}}

// Rotation in the real quaternion.
r := dualquat.Number{Real: quat.Number{Real: 0, Imag: 1}}

// Combine the rotation and displacement so
// the displacement is performed first.
q := dualquat.Mul(r, d)

fmt.Println(dualquat.Mul(dualquat.Mul(q, p), dualquat.Conj(q)).Dual)

}
```
```Output:

(0+7i-6j-11k)
```
Example (Rotate)
```package main

import (
"fmt"

"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)

func main() {
// Rotate a point [3, 4, 5] by 180° around the x axis.
// See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion/index.htm

// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}

// Rotation in the real quaternion.
r := dualquat.Number{Real: quat.Number{Real: 0, Imag: 1}}

fmt.Println(dualquat.Mul(dualquat.Mul(r, p), dualquat.Conj(r)).Dual)

}
```
```Output:

(0+3i-4j-5k)
```
Example (RotateAndDisplace)
```package main

import (
"fmt"

"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)

func main() {
// Rotate a point [3, 4, 5] by 180° around the x axis and then
// displace by [4, 2, 6]

// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}

// Displacement vector, half [4, 2, 6], in the dual imaginary vector.
d := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 2, Jmag: 1, Kmag: 3}}

// Rotation in the real quaternion.
r := dualquat.Number{Real: quat.Number{Real: 0, Imag: 1}}

// Combine the rotation and displacement so
// the rotations is performed first.
q := dualquat.Mul(d, r)

fmt.Println(dualquat.Mul(dualquat.Mul(q, p), dualquat.Conj(q)).Dual)

}
```
```Output:

(0+7i-2j+1k)
```

### Constants ¶

This section is empty.

### Variables ¶

This section is empty.

### Functions ¶

#### func Abs ¶

`func Abs(d Number) dual.Number`

Abs returns the absolute value of d.

### Types ¶

#### type Number ¶

```type Number struct {
Real, Dual quat.Number
}```

Number is a float64 precision dual quaternion. A dual quaternion is a hypercomplex number composed of two quaternions, q₀+q₂ϵ, where ϵ²=0, but ϵ≠0. Here, q₀ is termed the real and q₂ the dual.

`func Add(x, y Number) Number`

Add returns the sum of x and y.

#### func Conj ¶

`func Conj(d Number) Number`

Conj returns the dual quaternion conjugate of d₁+d₂ϵ, d̅₁-d̅₂ϵ.

#### func ConjDual ¶

`func ConjDual(d Number) Number`

ConjDual returns the dual conjugate of d₁+d₂ϵ, d₁-d₂ϵ.

#### func ConjQuat ¶

`func ConjQuat(d Number) Number`

ConjQuat returns the quaternion conjugate of d₁+d₂ϵ, d̅₁+d̅₂ϵ.

#### func Exp ¶

`func Exp(d Number) Number`

Exp returns e**d, the base-e exponential of d.

Special cases are:

```Exp(+Inf) = +Inf
Exp(NaN) = NaN
```

Very large values overflow to 0 or +Inf. Very small values underflow to 1.

#### func Inv ¶

`func Inv(d Number) Number`

Inv returns the dual inverse of d.

#### func Log ¶

`func Log(d Number) Number`

Log returns the natural logarithm of d.

Special cases are:

```Log(+Inf) = (+Inf+0ϵ)
Log(0) = (-Inf±Infϵ)
Log(x < 0) = NaN
Log(NaN) = NaN
```

#### func Mul ¶

`func Mul(x, y Number) Number`

Mul returns the dual product of x and y.

#### func Pow ¶

`func Pow(d, p Number) Number`

Pow return d**p, the base-d exponential of p.

#### func PowReal ¶

`func PowReal(d Number, p float64) Number`

PowReal returns d**p, the base-d exponential of p.

Special cases are (in order):

```PowReal(NaN+xϵ, ±0) = 1+NaNϵ for any x
PowReal(x, ±0) = 1 for any x
PowReal(1+xϵ, y) = 1+xyϵ for any y
PowReal(x, 1) = x for any x
PowReal(NaN+xϵ, y) = NaN+NaNϵ
PowReal(x, NaN) = NaN+NaNϵ
PowReal(±0, y) = ±Inf for y an odd integer < 0
PowReal(±0, -Inf) = +Inf
PowReal(±0, +Inf) = +0
PowReal(±0, y) = +Inf for finite y < 0 and not an odd integer
PowReal(±0, y) = ±0 for y an odd integer > 0
PowReal(±0, y) = +0 for finite y > 0 and not an odd integer
PowReal(-1, ±Inf) = 1
PowReal(x+0ϵ, +Inf) = +Inf+NaNϵ for |x| > 1
PowReal(x+yϵ, +Inf) = +Inf for |x| > 1
PowReal(x, -Inf) = +0+NaNϵ for |x| > 1
PowReal(x, +Inf) = +0+NaNϵ for |x| < 1
PowReal(x+0ϵ, -Inf) = +Inf+NaNϵ for |x| < 1
PowReal(x, -Inf) = +Inf-Infϵ for |x| < 1
PowReal(+Inf, y) = +Inf for y > 0
PowReal(+Inf, y) = +0 for y < 0
PowReal(-Inf, y) = Pow(-0, -y)
```

#### func Scale ¶

`func Scale(f float64, d Number) Number`

Scale returns d scaled by f.

#### func Sqrt ¶

`func Sqrt(d Number) Number`

Sqrt returns the square root of d

Special cases are:

```Sqrt(+Inf) = +Inf
Sqrt(±0) = (±0+Infϵ)
Sqrt(x < 0) = NaN
Sqrt(NaN) = NaN
```

#### func Sub ¶

`func Sub(x, y Number) Number`

Sub returns the difference of x and y, x-y.

#### func (Number) Format ¶

`func (d Number) Format(fs fmt.State, c rune)`

Format implements fmt.Formatter.