Skip to content

Commit 6888e69

Browse files
authored
new exercise: complex-numbers (#149)
1 parent c8734ec commit 6888e69

13 files changed

+942
-0
lines changed

config.json

+8
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@
143143
"prerequisites": [],
144144
"difficulty": 3
145145
},
146+
{
147+
"slug": "complex-numbers",
148+
"name": "Complex Numbers",
149+
"uuid": "5591f4e1-6f72-415e-a789-12eebd712f9a",
150+
"practices": [],
151+
"prerequisites": [],
152+
"difficulty": 8
153+
},
146154
{
147155
"slug": "darts",
148156
"name": "Darts",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Instruction append
2+
3+
The exponentiation of complex numbers requires an exponential function, a sine and a cosine function, none of which are available in WebAssembly. Fortunately, one can use a [Taylor Series](https://en.wikipedia.org/wiki/Taylor_series) to calculate these. You should at least have `1/26!` precision to align with the expected values.
4+
5+
## Exponential function
6+
7+
```
8+
exp(x) ≃ 1 + x + x ^ 2 / 2! + x ^ 3 / 3! + x ^ 4 / 4! + ... + x ^ n / n!
9+
```
10+
11+
## Sine function
12+
13+
```
14+
sin(x) ≃ x - x ^ 3 / 3! + x ^ 5 / 5! - x ^ 7 / 7! + ... - x ^ n / n!
15+
```
16+
17+
## Cosine function
18+
19+
```
20+
cos(x) ≃ 1 - x ^ 2 / 2! + x ^ 4 / 4! - x ^ 6 / 6! + ... - x ^ n / n!
21+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Instructions
2+
3+
A **complex number** is expressed in the form `z = a + b * i`, where:
4+
5+
- `a` is the **real part** (a real number),
6+
7+
- `b` is the **imaginary part** (also a real number), and
8+
9+
- `i` is the **imaginary unit** satisfying `i^2 = -1`.
10+
11+
## Operations on Complex Numbers
12+
13+
### Conjugate
14+
15+
The conjugate of the complex number `z = a + b * i` is given by:
16+
17+
```text
18+
zc = a - b * i
19+
```
20+
21+
### Absolute Value
22+
23+
The absolute value (or modulus) of `z` is defined as:
24+
25+
```text
26+
|z| = sqrt(a^2 + b^2)
27+
```
28+
29+
The square of the absolute value is computed as the product of `z` and its conjugate `zc`:
30+
31+
```text
32+
|z|^2 = z * zc = a^2 + b^2
33+
```
34+
35+
### Addition
36+
37+
The sum of two complex numbers `z1 = a + b * i` and `z2 = c + d * i` is computed by adding their real and imaginary parts separately:
38+
39+
```text
40+
z1 + z2 = (a + b * i) + (c + d * i)
41+
= (a + c) + (b + d) * i
42+
```
43+
44+
### Subtraction
45+
46+
The difference of two complex numbers is obtained by subtracting their respective parts:
47+
48+
```text
49+
z1 - z2 = (a + b * i) - (c + d * i)
50+
= (a - c) + (b - d) * i
51+
```
52+
53+
### Multiplication
54+
55+
The product of two complex numbers is defined as:
56+
57+
```text
58+
z1 * z2 = (a + b * i) * (c + d * i)
59+
= (a * c - b * d) + (b * c + a * d) * i
60+
```
61+
62+
### Reciprocal
63+
64+
The reciprocal of a non-zero complex number is given by:
65+
66+
```text
67+
1 / z = 1 / (a + b * i)
68+
= a / (a^2 + b^2) - b / (a^2 + b^2) * i
69+
```
70+
71+
### Division
72+
73+
The division of one complex number by another is given by:
74+
75+
```text
76+
z1 / z2 = z1 * (1 / z2)
77+
= (a + b * i) / (c + d * i)
78+
= (a * c + b * d) / (c^2 + d^2) + (b * c - a * d) / (c^2 + d^2) * i
79+
```
80+
81+
### Exponentiation
82+
83+
Raising _e_ (the base of the natural logarithm) to a complex exponent can be expressed using Euler's formula:
84+
85+
```text
86+
e^(a + b * i) = e^a * e^(b * i)
87+
= e^a * (cos(b) + i * sin(b))
88+
```
89+
90+
## Implementation Requirements
91+
92+
Given that you should not use built-in support for complex numbers, implement the following operations:
93+
94+
- **addition** of two complex numbers
95+
- **subtraction** of two complex numbers
96+
- **multiplication** of two complex numbers
97+
- **division** of two complex numbers
98+
- **conjugate** of a complex number
99+
- **absolute value** of a complex number
100+
- **exponentiation** of _e_ (the base of the natural logarithm) to a complex number
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"root": true,
3+
"extends": "@exercism/eslint-config-javascript",
4+
"env": {
5+
"jest": true
6+
},
7+
"overrides": [
8+
{
9+
"files": ["*.spec.js"],
10+
"excludedFiles": ["custom.spec.js"],
11+
"extends": "@exercism/eslint-config-javascript/maintainers"
12+
}
13+
]
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"authors": [
3+
"atk"
4+
],
5+
"files": {
6+
"solution": [
7+
"complex-numbers.wat"
8+
],
9+
"test": [
10+
"complex-numbers.spec.js"
11+
],
12+
"example": [
13+
".meta/proof.ci.wat"
14+
],
15+
"invalidator": [
16+
"package.json"
17+
]
18+
},
19+
"blurb": "Implement complex numbers.",
20+
"source": "Wikipedia",
21+
"source_url": "https://en.wikipedia.org/wiki/Complex_number",
22+
"custom": {
23+
"version.tests.compatibility": "jest-27",
24+
"flag.tests.task-per-describe": false,
25+
"flag.tests.may-run-long": false,
26+
"flag.tests.includes-optional": false
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
(module
2+
;; precision value p => 1/p!
3+
(global $precision f64 (f64.const 26.0))
4+
5+
;;
6+
;; adds two complex numbers
7+
;;
8+
;; @param $realA {f64} - the real part of the first number
9+
;; @param $imagA {f64} - the imaginary part of the first number
10+
;; @param $realB {f64} - the real part of the second number
11+
;; @param $imagB {f64} - the imaginary part of the second number
12+
;;
13+
;; @returns {(f64,f64)} - the real and imaginary parts of the complex sum
14+
;;
15+
(func (export "add") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
16+
(f64.add (local.get $realA) (local.get $realB))
17+
(f64.add (local.get $imagA) (local.get $imagB))
18+
)
19+
20+
;;
21+
;; subtracts two complex numbers
22+
;;
23+
;; @param $realA {f64} - the real part of the first number
24+
;; @param $imagA {f64} - the imaginary part of the first number
25+
;; @param $realB {f64} - the real part of the second number
26+
;; @param $imagB {f64} - the imaginary part of the second number
27+
;;
28+
;; @returns {(f64,f64)} - the real and imaginary parts of the complex difference
29+
;;
30+
(func (export "sub") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
31+
(f64.sub (local.get $realA) (local.get $realB))
32+
(f64.sub (local.get $imagA) (local.get $imagB))
33+
)
34+
35+
;;
36+
;; multiplicates two complex numbers
37+
;;
38+
;; @param $realA {f64} - the real part of the first number
39+
;; @param $imagA {f64} - the imaginary part of the first number
40+
;; @param $realB {f64} - the real part of the second number
41+
;; @param $imagB {f64} - the imaginary part of the second number
42+
;;
43+
;; @returns {(f64,f64)} - the real and imaginary parts of the complex product
44+
;;
45+
(func (export "mul") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
46+
(f64.sub (f64.mul (local.get $realA) (local.get $realB))
47+
(f64.mul (local.get $imagA) (local.get $imagB)))
48+
(f64.add (f64.mul (local.get $imagA) (local.get $realB))
49+
(f64.mul (local.get $imagB) (local.get $realA)))
50+
)
51+
52+
;;
53+
;; divides two complex numbers
54+
;;
55+
;; @param $realA {f64} - the real part of the first number
56+
;; @param $imagA {f64} - the imaginary part of the first number
57+
;; @param $realB {f64} - the real part of the second number
58+
;; @param $imagB {f64} - the imaginary part of the second number
59+
;;
60+
;; @returns {(f64,f64)} - the real and imaginary parts of the complex quotient
61+
;;
62+
(func (export "div") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
63+
(f64.div (f64.add (f64.mul (local.get $realA) (local.get $realB))
64+
(f64.mul (local.get $imagA) (local.get $imagB)))
65+
(f64.add (f64.mul (local.get $realB) (local.get $realB))
66+
(f64.mul (local.get $imagB) (local.get $imagB))))
67+
(f64.div (f64.sub (f64.mul (local.get $imagA) (local.get $realB))
68+
(f64.mul (local.get $realA) (local.get $imagB)))
69+
(f64.add (f64.mul (local.get $realB) (local.get $realB))
70+
(f64.mul (local.get $imagB) (local.get $imagB))))
71+
)
72+
73+
;;
74+
;; returns the absolute of a complex number
75+
;;
76+
;; @param $real {f64} - the real part of the number
77+
;; @param $imag {f64} - the imaginary part of the number
78+
;;
79+
;; @returns {f64} - the absolute value of the number
80+
;;
81+
(func (export "abs") (param $real f64) (param $imag f64) (result f64)
82+
(f64.sqrt (f64.add (f64.mul (local.get $real) (local.get $real))
83+
(f64.mul (local.get $imag) (local.get $imag))))
84+
)
85+
86+
;;
87+
;; returns the conjugate of a complex number
88+
;;
89+
;; @param $real {f64} - the real part of the number
90+
;; @param $imag {f64} - the imaginary part of the number
91+
;;
92+
;; @returns {(f64,f64)} - the real and imaginary parts of the conjugate of the number
93+
;;
94+
(func (export "conj") (param $real f64) (param $imag f64) (result f64 f64)
95+
(local.get $real) (f64.sub (f64.const 0.0) (local.get $imag))
96+
)
97+
98+
;;
99+
;; exponential function
100+
;;
101+
;; @param $num {f64} - the number for which the exponent should be calculated
102+
;;
103+
;; @returns {f64} - the exponential e^num
104+
;;
105+
(func $exp (param $num f64) (result f64)
106+
(local $product f64)
107+
(local $sum f64)
108+
(local $fac f64)
109+
(local $step f64)
110+
(local.set $sum (f64.add (f64.const 1.0) (local.get $num)))
111+
(local.set $product (local.get $num))
112+
(local.set $step (f64.const 3.0))
113+
(local.set $fac (f64.const 2.0))
114+
(loop $series
115+
(local.set $product (f64.mul (local.get $product) (local.get $num)))
116+
(local.set $sum (f64.add (local.get $sum) (f64.div (local.get $product) (local.get $fac))))
117+
(local.set $fac (f64.mul (local.get $fac) (local.get $step)))
118+
(local.set $step (f64.add (local.get $step) (f64.const 1.0)))
119+
(br_if $series (f64.lt (local.get $step) (global.get $precision))))
120+
(local.get $sum)
121+
)
122+
123+
;;
124+
;; radial sine of a number
125+
;;
126+
;; @param $num {f64} - the number for which the sine should be calculated
127+
;;
128+
;; @returns {f64} - the sine of the number
129+
;;
130+
(func $sin (param $num f64) (result f64)
131+
(local $sum f64)
132+
(local $square f64)
133+
(local $product f64)
134+
(local $fac f64)
135+
(local $step f64)
136+
(local.set $sum (local.get $num))
137+
(local.set $square (f64.mul (local.get $num) (local.get $num)))
138+
(local.set $product (local.get $num))
139+
(local.set $fac (f64.const 6.0))
140+
(local.set $step (f64.const 4.0))
141+
(loop $series
142+
(local.set $product (f64.mul (local.get $product) (local.get $square)))
143+
(local.set $sum (f64.add (local.get $sum) (f64.div
144+
(select (local.get $product) (f64.sub (f64.const 0) (local.get $product))
145+
(i32.and (i32.trunc_f64_u (local.get $step)) (i32.const 2)))
146+
(local.get $fac))))
147+
(local.set $fac (f64.mul (f64.mul (local.get $fac) (local.get $step))
148+
(f64.add (local.get $step) (f64.const 1.0))))
149+
(local.set $step (f64.add (local.get $step) (f64.const 2)))
150+
(br_if $series (f64.lt (local.get $step) (global.get $precision))))
151+
(local.get $sum)
152+
)
153+
154+
;;
155+
;; radial cosine of a number
156+
;;
157+
;; @param $num {f64} - the number for which the cosine should be calculated
158+
;;
159+
;; @returns {f64} - the cosine of the number
160+
;;
161+
(func $cos (param $num f64) (result f64)
162+
(local $sum f64)
163+
(local $square f64)
164+
(local $product f64)
165+
(local $fac f64)
166+
(local $step f64)
167+
(local.set $sum (f64.const 1))
168+
(local.set $square (f64.mul (local.get $num) (local.get $num)))
169+
(local.set $product (f64.const 1))
170+
(local.set $fac (f64.const 2.0))
171+
(local.set $step (f64.const 3.0))
172+
(loop $series
173+
(local.set $product (f64.mul (local.get $product) (local.get $square)))
174+
(local.set $sum (f64.add (local.get $sum) (f64.div
175+
(select (f64.sub (f64.const 0) (local.get $product)) (local.get $product)
176+
(i32.and (i32.trunc_f64_u (local.get $step)) (i32.const 2)))
177+
(local.get $fac))))
178+
(local.set $fac (f64.mul (f64.mul (local.get $fac) (local.get $step))
179+
(f64.add (local.get $step) (f64.const 1.0))))
180+
(local.set $step (f64.add (local.get $step) (f64.const 2)))
181+
(br_if $series (f64.lt (local.get $step) (global.get $precision))))
182+
(local.get $sum)
183+
)
184+
185+
;;
186+
;; returns the exponentiation of a complex number
187+
;;
188+
;; @param $real {f64} - the real part of the number
189+
;; @param $imag {f64} - the imaginary part of the number
190+
;;
191+
;; @returns {(f64,f64}} - exponentiation of the complex number
192+
;;
193+
(func (export "exp") (param $real f64) (param $imag f64) (result f64 f64)
194+
(local $expReal f64)
195+
(local.set $expReal (call $exp (local.get $real)))
196+
(f64.mul (local.get $expReal) (call $cos (local.get $imag)))
197+
(f64.mul (local.get $expReal) (call $sin (local.get $imag)))
198+
)
199+
)

0 commit comments

Comments
 (0)