Skip to content

new exercise: complex-numbers #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@
"prerequisites": [],
"difficulty": 3
},
{
"slug": "complex-numbers",
"name": "Complex Numbers",
"uuid": "5591f4e1-6f72-415e-a789-12eebd712f9a",
"practices": [],
"prerequisites": [],
"difficulty": 8
},
{
"slug": "darts",
"name": "Darts",
Expand Down
21 changes: 21 additions & 0 deletions exercises/practice/complex-numbers/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Instruction append

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.

## Exponential function

```
exp(x) ≃ 1 + x + x ^ 2 / 2! + x ^ 3 / 3! + x ^ 4 / 4! + ... + x ^ n / n!
```

## Sine function

```
sin(x) ≃ x - x ^ 3 / 3! + x ^ 5 / 5! - x ^ 7 / 7! + ... - x ^ n / n!
```

## Cosine function

```
cos(x) ≃ 1 - x ^ 2 / 2! + x ^ 4 / 4! - x ^ 6 / 6! + ... - x ^ n / n!
```
100 changes: 100 additions & 0 deletions exercises/practice/complex-numbers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Instructions

A **complex number** is expressed in the form `z = a + b * i`, where:

- `a` is the **real part** (a real number),

- `b` is the **imaginary part** (also a real number), and

- `i` is the **imaginary unit** satisfying `i^2 = -1`.

## Operations on Complex Numbers

### Conjugate

The conjugate of the complex number `z = a + b * i` is given by:

```text
zc = a - b * i
```

### Absolute Value

The absolute value (or modulus) of `z` is defined as:

```text
|z| = sqrt(a^2 + b^2)
```

The square of the absolute value is computed as the product of `z` and its conjugate `zc`:

```text
|z|^2 = z * zc = a^2 + b^2
```

### Addition

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:

```text
z1 + z2 = (a + b * i) + (c + d * i)
= (a + c) + (b + d) * i
```

### Subtraction

The difference of two complex numbers is obtained by subtracting their respective parts:

```text
z1 - z2 = (a + b * i) - (c + d * i)
= (a - c) + (b - d) * i
```

### Multiplication

The product of two complex numbers is defined as:

```text
z1 * z2 = (a + b * i) * (c + d * i)
= (a * c - b * d) + (b * c + a * d) * i
```

### Reciprocal

The reciprocal of a non-zero complex number is given by:

```text
1 / z = 1 / (a + b * i)
= a / (a^2 + b^2) - b / (a^2 + b^2) * i
```

### Division

The division of one complex number by another is given by:

```text
z1 / z2 = z1 * (1 / z2)
= (a + b * i) / (c + d * i)
= (a * c + b * d) / (c^2 + d^2) + (b * c - a * d) / (c^2 + d^2) * i
```

### Exponentiation

Raising _e_ (the base of the natural logarithm) to a complex exponent can be expressed using Euler's formula:

```text
e^(a + b * i) = e^a * e^(b * i)
= e^a * (cos(b) + i * sin(b))
```

## Implementation Requirements

Given that you should not use built-in support for complex numbers, implement the following operations:

- **addition** of two complex numbers
- **subtraction** of two complex numbers
- **multiplication** of two complex numbers
- **division** of two complex numbers
- **conjugate** of a complex number
- **absolute value** of a complex number
- **exponentiation** of _e_ (the base of the natural logarithm) to a complex number
14 changes: 14 additions & 0 deletions exercises/practice/complex-numbers/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"root": true,
"extends": "@exercism/eslint-config-javascript",
"env": {
"jest": true
},
"overrides": [
{
"files": ["*.spec.js"],
"excludedFiles": ["custom.spec.js"],
"extends": "@exercism/eslint-config-javascript/maintainers"
}
]
}
28 changes: 28 additions & 0 deletions exercises/practice/complex-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"authors": [
"atk"
],
"files": {
"solution": [
"complex-numbers.wat"
],
"test": [
"complex-numbers.spec.js"
],
"example": [
".meta/proof.ci.wat"
],
"invalidator": [
"package.json"
]
},
"blurb": "Implement complex numbers.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Complex_number",
"custom": {
"version.tests.compatibility": "jest-27",
"flag.tests.task-per-describe": false,
"flag.tests.may-run-long": false,
"flag.tests.includes-optional": false
}
}
199 changes: 199 additions & 0 deletions exercises/practice/complex-numbers/.meta/proof.ci.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
(module
;; precision value p => 1/p!
(global $precision f64 (f64.const 26.0))

;;
;; adds two complex numbers
;;
;; @param $realA {f64} - the real part of the first number
;; @param $imagA {f64} - the imaginary part of the first number
;; @param $realB {f64} - the real part of the second number
;; @param $imagB {f64} - the imaginary part of the second number
;;
;; @returns {(f64,f64)} - the real and imaginary parts of the complex sum
;;
(func (export "add") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
(f64.add (local.get $realA) (local.get $realB))
(f64.add (local.get $imagA) (local.get $imagB))
)

;;
;; subtracts two complex numbers
;;
;; @param $realA {f64} - the real part of the first number
;; @param $imagA {f64} - the imaginary part of the first number
;; @param $realB {f64} - the real part of the second number
;; @param $imagB {f64} - the imaginary part of the second number
;;
;; @returns {(f64,f64)} - the real and imaginary parts of the complex difference
;;
(func (export "sub") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
(f64.sub (local.get $realA) (local.get $realB))
(f64.sub (local.get $imagA) (local.get $imagB))
)

;;
;; multiplicates two complex numbers
;;
;; @param $realA {f64} - the real part of the first number
;; @param $imagA {f64} - the imaginary part of the first number
;; @param $realB {f64} - the real part of the second number
;; @param $imagB {f64} - the imaginary part of the second number
;;
;; @returns {(f64,f64)} - the real and imaginary parts of the complex product
;;
(func (export "mul") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
(f64.sub (f64.mul (local.get $realA) (local.get $realB))
(f64.mul (local.get $imagA) (local.get $imagB)))
(f64.add (f64.mul (local.get $imagA) (local.get $realB))
(f64.mul (local.get $imagB) (local.get $realA)))
)

;;
;; divides two complex numbers
;;
;; @param $realA {f64} - the real part of the first number
;; @param $imagA {f64} - the imaginary part of the first number
;; @param $realB {f64} - the real part of the second number
;; @param $imagB {f64} - the imaginary part of the second number
;;
;; @returns {(f64,f64)} - the real and imaginary parts of the complex quotient
;;
(func (export "div") (param $realA f64) (param $imagA f64) (param $realB f64) (param $imagB f64) (result f64 f64)
(f64.div (f64.add (f64.mul (local.get $realA) (local.get $realB))
(f64.mul (local.get $imagA) (local.get $imagB)))
(f64.add (f64.mul (local.get $realB) (local.get $realB))
(f64.mul (local.get $imagB) (local.get $imagB))))
(f64.div (f64.sub (f64.mul (local.get $imagA) (local.get $realB))
(f64.mul (local.get $realA) (local.get $imagB)))
(f64.add (f64.mul (local.get $realB) (local.get $realB))
(f64.mul (local.get $imagB) (local.get $imagB))))
)

;;
;; returns the absolute of a complex number
;;
;; @param $real {f64} - the real part of the number
;; @param $imag {f64} - the imaginary part of the number
;;
;; @returns {f64} - the absolute value of the number
;;
(func (export "abs") (param $real f64) (param $imag f64) (result f64)
(f64.sqrt (f64.add (f64.mul (local.get $real) (local.get $real))
(f64.mul (local.get $imag) (local.get $imag))))
)

;;
;; returns the conjugate of a complex number
;;
;; @param $real {f64} - the real part of the number
;; @param $imag {f64} - the imaginary part of the number
;;
;; @returns {(f64,f64)} - the real and imaginary parts of the conjugate of the number
;;
(func (export "conj") (param $real f64) (param $imag f64) (result f64 f64)
(local.get $real) (f64.sub (f64.const 0.0) (local.get $imag))
)

;;
;; exponential function
;;
;; @param $num {f64} - the number for which the exponent should be calculated
;;
;; @returns {f64} - the exponential e^num
;;
(func $exp (param $num f64) (result f64)
(local $product f64)
(local $sum f64)
(local $fac f64)
(local $step f64)
(local.set $sum (f64.add (f64.const 1.0) (local.get $num)))
(local.set $product (local.get $num))
(local.set $step (f64.const 3.0))
(local.set $fac (f64.const 2.0))
(loop $series
(local.set $product (f64.mul (local.get $product) (local.get $num)))
(local.set $sum (f64.add (local.get $sum) (f64.div (local.get $product) (local.get $fac))))
(local.set $fac (f64.mul (local.get $fac) (local.get $step)))
(local.set $step (f64.add (local.get $step) (f64.const 1.0)))
(br_if $series (f64.lt (local.get $step) (global.get $precision))))
(local.get $sum)
)

;;
;; radial sine of a number
;;
;; @param $num {f64} - the number for which the sine should be calculated
;;
;; @returns {f64} - the sine of the number
;;
(func $sin (param $num f64) (result f64)
(local $sum f64)
(local $square f64)
(local $product f64)
(local $fac f64)
(local $step f64)
(local.set $sum (local.get $num))
(local.set $square (f64.mul (local.get $num) (local.get $num)))
(local.set $product (local.get $num))
(local.set $fac (f64.const 6.0))
(local.set $step (f64.const 4.0))
(loop $series
(local.set $product (f64.mul (local.get $product) (local.get $square)))
(local.set $sum (f64.add (local.get $sum) (f64.div
(select (local.get $product) (f64.sub (f64.const 0) (local.get $product))
(i32.and (i32.trunc_f64_u (local.get $step)) (i32.const 2)))
(local.get $fac))))
(local.set $fac (f64.mul (f64.mul (local.get $fac) (local.get $step))
(f64.add (local.get $step) (f64.const 1.0))))
(local.set $step (f64.add (local.get $step) (f64.const 2)))
(br_if $series (f64.lt (local.get $step) (global.get $precision))))
(local.get $sum)
)

;;
;; radial cosine of a number
;;
;; @param $num {f64} - the number for which the cosine should be calculated
;;
;; @returns {f64} - the cosine of the number
;;
(func $cos (param $num f64) (result f64)
(local $sum f64)
(local $square f64)
(local $product f64)
(local $fac f64)
(local $step f64)
(local.set $sum (f64.const 1))
(local.set $square (f64.mul (local.get $num) (local.get $num)))
(local.set $product (f64.const 1))
(local.set $fac (f64.const 2.0))
(local.set $step (f64.const 3.0))
(loop $series
(local.set $product (f64.mul (local.get $product) (local.get $square)))
(local.set $sum (f64.add (local.get $sum) (f64.div
(select (f64.sub (f64.const 0) (local.get $product)) (local.get $product)
(i32.and (i32.trunc_f64_u (local.get $step)) (i32.const 2)))
(local.get $fac))))
(local.set $fac (f64.mul (f64.mul (local.get $fac) (local.get $step))
(f64.add (local.get $step) (f64.const 1.0))))
(local.set $step (f64.add (local.get $step) (f64.const 2)))
(br_if $series (f64.lt (local.get $step) (global.get $precision))))
(local.get $sum)
)

;;
;; returns the exponentiation of a complex number
;;
;; @param $real {f64} - the real part of the number
;; @param $imag {f64} - the imaginary part of the number
;;
;; @returns {(f64,f64}} - exponentiation of the complex number
;;
(func (export "exp") (param $real f64) (param $imag f64) (result f64 f64)
(local $expReal f64)
(local.set $expReal (call $exp (local.get $real)))
(f64.mul (local.get $expReal) (call $cos (local.get $imag)))
(f64.mul (local.get $expReal) (call $sin (local.get $imag)))
)
)
Loading