Skip to content

Commit bb32795

Browse files
Merge pull request #283 from PolyMathOrg/refactor-quaternions
Refactor: Quaternion multiplication
2 parents 1bef3d8 + ea559d7 commit bb32795

File tree

6 files changed

+165
-73
lines changed

6 files changed

+165
-73
lines changed

src/Math-Quaternion/Number.extension.st

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,8 @@ Number >> k [
4141
qj: 0
4242
qk: self
4343
]
44+
45+
{ #category : #'*Math-Quaternion' }
46+
Number >> multiplyByQuaternion: quaternion [
47+
^ quaternion timesNumber: self.
48+
]

src/Math-Quaternion/PMComplexNumber.extension.st

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@ PMComplexNumber >> k [
3636
qj: imaginary negated
3737
qk: real
3838
]
39+
40+
{ #category : #'*Math-Quaternion' }
41+
PMComplexNumber >> multiplyByQuaternion: quaternion [
42+
43+
^ quaternion timesComplexNumber: self
44+
]

src/Math-Quaternion/PMPolynomial.extension.st

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ Extension { #name : #PMPolynomial }
44
PMPolynomial >> adaptToQuaternion: rcvr andSend: selector [
55
^(self class coefficients: (Array with: rcvr) ) perform: selector with: self
66
]
7+
8+
{ #category : #'*Math-Quaternion' }
9+
PMPolynomial >> multiplyByQuaternion: quaternion [
10+
11+
^ (self class coefficients: (Array with: quaternion)) * self
12+
]

src/Math-Quaternion/PMQuaternion.class.st

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,29 +78,12 @@ PMQuaternion class >> zero [
7878
]
7979

8080
{ #category : #arithmetic }
81-
PMQuaternion >> * aQuaternion [
81+
PMQuaternion >> * multiplier [
82+
8283
"Answer the result of multiplying self with aQuaternion.
8384
Distribute the product (qr + qi i +qj j + qk k)*(tr+ti i + tj j + tk k)
8485
with rules i*i=j*j=k*k=-1 and i*j=-k, j*k=-i, k*i=-j"
85-
^ aQuaternion isQuaternion
86-
ifTrue: [| tr ti tj tk |
87-
tr := aQuaternion qr.
88-
ti := aQuaternion qi.
89-
tj := aQuaternion qj.
90-
tk := aQuaternion qk.
91-
^ self species
92-
qr: qr * tr - (qi * ti) - (qj * tj) - (qk * tk)
93-
qi: qr * ti + (qi * tr) + (qj * tk) - (qk * tj)
94-
qj: qr * tj + (qj * tr) + (qk * ti) - (qi * tk)
95-
qk: qr * tk + (qk * tr) + (qi * tj) - (qj * ti)]
96-
ifFalse: [(aQuaternion isNumber and: [aQuaternion isComplexNumber not])
97-
ifTrue: ["handle case much faster than adapt..."
98-
self species
99-
qr: qr * aQuaternion
100-
qi: qi * aQuaternion
101-
qj: qj * aQuaternion
102-
qk: qk * aQuaternion]
103-
ifFalse: [aQuaternion adaptToQuaternion: self andSend: #*]]
86+
^ multiplier multiplyByQuaternion: self.
10487
]
10588

10689
{ #category : #arithmetic }
@@ -372,6 +355,22 @@ PMQuaternion >> log [
372355
^self ln / 10 ln
373356
]
374357

358+
{ #category : #arithmetic }
359+
PMQuaternion >> multiplyByQuaternion: multiplier [
360+
361+
| tr ti tj tk |
362+
tr := multiplier qr.
363+
ti := multiplier qi.
364+
tj := multiplier qj.
365+
tk := multiplier qk.
366+
367+
^ self species
368+
qr: tr * qr - (ti * qi) - (tj * qj) - (tk * qk)
369+
qi: tr * qi + (ti * qr) + (tj * qk) - (tk * qj)
370+
qj: tr * qj - (ti * qk) + (tj * qr) + (tk * qi)
371+
qk: tr * qk + (tk * qr)- (tj * qi) + (ti * qj)
372+
]
373+
375374
{ #category : #arithmetic }
376375
PMQuaternion >> negated [
377376
^self species
@@ -591,6 +590,25 @@ PMQuaternion >> tanh [
591590
^(ep - em) / (ep + em)
592591
]
593592

593+
{ #category : #arithmetic }
594+
PMQuaternion >> timesComplexNumber: complexNumber [
595+
596+
| x y |
597+
x := complexNumber real.
598+
y := complexNumber imaginary.
599+
600+
^ self species
601+
qr: x * qr - (y * qi)
602+
qi: x * qi + (y * qr)
603+
qj: x * qj + (y * qk)
604+
qk: x * qk - (y * qj)
605+
]
606+
607+
{ #category : #arithmetic }
608+
PMQuaternion >> timesNumber: multiplier [
609+
^ self class qr: (qr * multiplier) qi: (qi * multiplier) qj: (qj * multiplier) qk: (qk * multiplier).
610+
]
611+
594612
{ #category : #'*Math-Quaternion' }
595613
PMQuaternion >> timesPolynomial: aPolynomial [
596614
^aPolynomial timesNumber: self

src/Math-Quaternion/PMVector.extension.st

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ Extension { #name : #PMVector }
44
PMVector >> adaptToQuaternion: aQuaternion andSend: aByteSymbol [
55
^ self collect: [ :ea | aQuaternion perform: aByteSymbol with: ea ]
66
]
7+
8+
{ #category : #'*Math-Quaternion' }
9+
PMVector >> multiplyByQuaternion: quaternion [
10+
11+
^ self * quaternion.
12+
]

src/Math-Tests-Quaternion/PMQuaternionTest.class.st

Lines changed: 104 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ PMQuaternionTest >> testDividePolynomial [
8484
self assert: ((poly / q12) coefficients allSatisfy: [ :ea | ea = 1 ])
8585
]
8686

87+
{ #category : #running }
88+
PMQuaternionTest >> testDivision [
89+
self assert: (0 / q1234) isZero.
90+
self assert: 1 / q1234 equals: q1234 reciprocal.
91+
self assert: q1234 / q1234 equals: 1
92+
]
93+
8794
{ #category : #running }
8895
PMQuaternionTest >> testEquality [
8996
self assert: q1234 equals: q1234 copy.
@@ -153,6 +160,103 @@ PMQuaternionTest >> testLog [
153160
self assert: (qln qk closeTo: qlg10ln qk)
154161
]
155162

163+
{ #category : #'testing - arithmetic' }
164+
PMQuaternionTest >> testMultiplicationByComplexNumberIsNonCommutative [
165+
| quaternion complexNumber |
166+
quaternion := 2 + 3 i + 4 j + 5 k.
167+
complexNumber := 1 - 1 i.
168+
169+
self assert: (quaternion * complexNumber) equals: 5 + 1 i - 1 j + 9 k.
170+
self assert: (complexNumber * quaternion) equals: 5 + 1 i + 9 j + 1 k.
171+
]
172+
173+
{ #category : #'testing - arithmetic' }
174+
PMQuaternionTest >> testMultiplicationByConjugate [
175+
176+
| quaternion |
177+
quaternion := 1 + 2 i + 3 j + 4 k.
178+
self assert: quaternion * (quaternion conjugated) equals: 30
179+
]
180+
181+
{ #category : #'testing - arithmetic' }
182+
PMQuaternionTest >> testMultiplicationByIntegersIsCommutative [
183+
184+
| quaternion |
185+
quaternion := 1 + 2 i + 3 j + 4 k.
186+
187+
self assert: quaternion * 5 equals: 5 + 10 i + 15 j + 20 k.
188+
self assert: 5 * quaternion equals: 5 + 10 i + 15 j + 20 k.
189+
]
190+
191+
{ #category : #'testing - arithmetic' }
192+
PMQuaternionTest >> testMultiplicationByPolynomialIsCommutative [
193+
194+
| poly quaternion |
195+
poly := PMPolynomial coefficients: #( 1 1 1 ).
196+
quaternion := 1 + 2 i + 0 j + 0 k.
197+
self assert: (poly * quaternion at: 0) equals: q12.
198+
self assert: (quaternion * poly at: 0) equals: q12
199+
]
200+
201+
{ #category : #tests }
202+
PMQuaternionTest >> testMultiplicationByTheBasisElements [
203+
204+
| quaternion |
205+
quaternion := 1 + 2 i + 3 j + 4 k.
206+
207+
self assert: quaternion * 1 i equals: (-2 + 1 i + 4 j - 3 k).
208+
self assert: quaternion * 1 j equals: (-3 - 4 i + 1 j + 2 k).
209+
self assert: quaternion * 1 k equals: (-4 + 3 i - 2 j + 1 k)
210+
]
211+
212+
{ #category : #'testing - arithmetic' }
213+
PMQuaternionTest >> testMultiplicationByVectorIsCommutative [
214+
| vec quaternion |
215+
vec := PMVector new: 2.
216+
vec at: 1 put: 1.
217+
vec at: 2 put: 2.
218+
quaternion := 1 + 2 i + 3 j + 4 k.
219+
"This uses productWithVector"
220+
self assert: (vec * quaternion at: 1) equals: (1 + 2 i + 3 j + 4 k).
221+
self assert: (vec * quaternion at: 2) equals: (2 + 4 i + 6 j + 8 k).
222+
223+
"This uses adaptToQuaternion:andSend:"
224+
self assert: (quaternion * vec at: 1) equals: 1 + 2 i + 3 j + 4 k.
225+
self assert: (quaternion * vec at: 2) equals: 2 + 4 i + 6 j + 8 k.
226+
]
227+
228+
{ #category : #'testing - arithmetic' }
229+
PMQuaternionTest >> testMultiplicationByZero [
230+
| quaternion |
231+
quaternion := 1 + 2 i + 3 j + 4 k.
232+
233+
self assert: quaternion * 0 equals: 0 + 0 i + 0 j + 0 k.
234+
]
235+
236+
{ #category : #'testing - arithmetic' }
237+
PMQuaternionTest >> testMultiplicationOfBasisElements [
238+
"https://en.wikipedia.org/wiki/Quaternion#Multiplication_of_basis_elements"
239+
240+
self assert: 1 i * 1 i equals: -1.
241+
self assert: 1 j * 1 j equals: -1.
242+
self assert: 1 k * 1 k equals: -1.
243+
self assert: 1 i * 1 j equals: 1 k.
244+
self assert: 1 j * 1 k equals: 1 i.
245+
self assert: 1 k * 1 i equals: 1 j.
246+
self assert: 1 j * 1 i equals: -1 k.
247+
self assert: 1 k * 1 j equals: -1 i.
248+
self assert: 1 i * 1 k equals: -1 j.
249+
self assert: 1 i i equals: -1.
250+
self assert: 1 j j equals: -1.
251+
self assert: 1 k k equals: -1.
252+
self assert: 1 i j equals: 1 k.
253+
self assert: 1 j k equals: 1 i.
254+
self assert: 1 k i equals: 1 j.
255+
self assert: 1 j i equals: -1 k.
256+
self assert: 1 k j equals: -1 i.
257+
self assert: 1 i k equals: -1 j.
258+
]
259+
156260
{ #category : #running }
157261
PMQuaternionTest >> testNaturalLogOfQuaternion [
158262

@@ -227,51 +331,6 @@ PMQuaternionTest >> testPrintOn [
227331
self assert: s equals: '(1 i: 2 j: 3 k: 4)'
228332
]
229333

230-
{ #category : #running }
231-
PMQuaternionTest >> testProduct [
232-
self assert: q1234 * 5 equals: (5 i: 10 j: 15 k: 20).
233-
self assert: 5 * q1234 equals: (5 i: 10 j: 15 k: 20).
234-
self assert: 1 i * 1 i equals: -1.
235-
self assert: 1 j * 1 j equals: -1.
236-
self assert: 1 k * 1 k equals: -1.
237-
self assert: 1 i * 1 j equals: 1 k.
238-
self assert: 1 j * 1 k equals: 1 i.
239-
self assert: 1 k * 1 i equals: 1 j.
240-
self assert: 1 j * 1 i equals: -1 k.
241-
self assert: 1 k * 1 j equals: -1 i.
242-
self assert: 1 i * 1 k equals: -1 j.
243-
self assert: 1 i i equals: -1.
244-
self assert: 1 j j equals: -1.
245-
self assert: 1 k k equals: -1.
246-
self assert: 1 i j equals: 1 k.
247-
self assert: 1 j k equals: 1 i.
248-
self assert: 1 k i equals: 1 j.
249-
self assert: 1 j i equals: -1 k.
250-
self assert: 1 k j equals: -1 i.
251-
self assert: 1 i k equals: -1 j.
252-
self assert: q1234 * 1 i equals: q1234 i.
253-
self assert: q1234 * 1 j equals: q1234 j.
254-
self assert: q1234 * 1 k equals: q1234 k.
255-
self assert: (q1234 * 0) isZero.
256-
self assert: (0 / q1234) isZero.
257-
self assert: q1234 * q1234 conjugated equals: q1234 squaredNorm.
258-
self assert: 1 / q1234 equals: q1234 reciprocal.
259-
self assert: q1234 / q1234 equals: 1
260-
]
261-
262-
{ #category : #running }
263-
PMQuaternionTest >> testProductWithVector [
264-
| vec |
265-
vec := PMVector new: 2.
266-
vec at: 1 put: 1.
267-
vec at: 2 put: 2.
268-
"This uses productWithVector"
269-
self assert: (vec * q1234 at: 2) equals: 2 * q1234.
270-
271-
"This uses adaptToQuaternion:andSend:"
272-
self assert: (q1234 * vec at: 1) equals: q1234
273-
]
274-
275334
{ #category : #running }
276335
PMQuaternionTest >> testRaisedTo [
277336
| eps |
@@ -363,14 +422,6 @@ PMQuaternionTest >> testTanh [
363422
self assert: ((1 + 2 i) tanh - (1 + 2 j k) tanh) abs < eps
364423
]
365424

366-
{ #category : #running }
367-
PMQuaternionTest >> testTimesPolynomial [
368-
| poly |
369-
poly := PMPolynomial coefficients: #(1 1 1).
370-
self assert: (poly * q12 at: 0) equals: q12.
371-
self assert: (q12 * poly at: 0) equals: q12
372-
]
373-
374425
{ #category : #running }
375426
PMQuaternionTest >> testUnreal [
376427
self assert: q1234 unreal real equals: 0.

0 commit comments

Comments
 (0)