Skip to content

Commit

Permalink
Add complex-numbers exercise (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
ageron authored Sep 21, 2024
1 parent 42a5829 commit ad09ee5
Show file tree
Hide file tree
Showing 9 changed files with 691 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "complex-numbers",
"name": "Complex Numbers",
"uuid": "b7934691-ac3e-4aee-acd2-5e01536c43e8",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "darts",
"name": "Darts",
Expand Down
29 changes: 29 additions & 0 deletions exercises/practice/complex-numbers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Instructions

A complex number is a number in the form `a + b * i` where `a` and `b` are real and `i` satisfies `i^2 = -1`.

`a` is called the real part and `b` is called the imaginary part of `z`.
The conjugate of the number `a + b * i` is the number `a - b * i`.
The absolute value of a complex number `z = a + b * i` is a real number `|z| = sqrt(a^2 + b^2)`. The square of the absolute value `|z|^2` is the result of multiplication of `z` by its complex conjugate.

The sum/difference of two complex numbers involves adding/subtracting their real and imaginary parts separately:
`(a + i * b) + (c + i * d) = (a + c) + (b + d) * i`,
`(a + i * b) - (c + i * d) = (a - c) + (b - d) * i`.

Multiplication result is by definition
`(a + i * b) * (c + i * d) = (a * c - b * d) + (b * c + a * d) * i`.

The reciprocal of a non-zero complex number is
`1 / (a + i * b) = a/(a^2 + b^2) - b/(a^2 + b^2) * i`.

Dividing a complex number `a + i * b` by another `c + i * d` gives:
`(a + i * b) / (c + i * d) = (a * c + b * d)/(c^2 + d^2) + (b * c - a * d)/(c^2 + d^2) * i`.

Raising e to a complex exponent can be expressed as `e^(a + i * b) = e^a * e^(i * b)`, the last term of which is given by Euler's formula `e^(i * b) = cos(b) + i * sin(b)`.

Implement the following operations:

- addition, subtraction, multiplication and division of two complex numbers,
- conjugate, absolute value, exponent of a given complex number.

Assume the programming language you are using does not have an implementation of complex numbers.
53 changes: 53 additions & 0 deletions exercises/practice/complex-numbers/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module [real, imaginary, add, sub, mul, div, conjugate, abs, exp]

Complex : { re : F64, im : F64 }

real : Complex -> F64
real = \z -> z.re

imaginary : Complex -> F64
imaginary = \z -> z.im

add : Complex, Complex -> Complex
add = \z1, z2 -> {
re: z1.re + z2.re,
im: z1.im + z2.im,
}

sub : Complex, Complex -> Complex
sub = \z1, z2 -> {
re: z1.re - z2.re,
im: z1.im - z2.im,
}

mul : Complex, Complex -> Complex
mul = \z1, z2 -> {
re: z1.re * z2.re - z1.im * z2.im,
im: z1.re * z2.im + z1.im * z2.re,
}

div : Complex, Complex -> Complex
div = \z1, z2 ->
denominator = z2.re * z2.re + z2.im * z2.im
{
re: (z1.re * z2.re + z1.im * z2.im) / denominator,
im: (z1.im * z2.re - z1.re * z2.im) / denominator,
}

conjugate : Complex -> Complex
conjugate = \z -> {
re: z.re,
im: -z.im,
}

abs : Complex -> F64
abs = \z ->
z.re * z.re + z.im * z.im |> Num.sqrt

exp : Complex -> Complex
exp = \z ->
factor = Num.e |> Num.pow z.re
{
re: factor * Num.cos z.im,
im: factor * Num.sin z.im,
}
19 changes: 19 additions & 0 deletions exercises/practice/complex-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"ageron"
],
"files": {
"solution": [
"ComplexNumbers.roc"
],
"test": [
"complex-numbers-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"blurb": "Implement complex numbers.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Complex_number"
}
25 changes: 25 additions & 0 deletions exercises/practice/complex-numbers/.meta/plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
float_map = {
"e": "Num.e",
"ln(2)": "Num.log 2f64",
"ln(2)/2": "Num.log 2f64 / 2",
"pi": "Num.pi",
"pi/4": "Num.pi / 4",
}


def to_roc(value):
if isinstance(value, str):
return float_map[value]
else:
return f"{value}"


def to_complex_number(value):
if isinstance(value, list):
assert len(value) == 2
re = to_roc(value[0])
im = to_roc(value[1])
else:
re = to_roc(value)
im = 0
return f"{{ re: {re}, im: {im} }}"
51 changes: 51 additions & 0 deletions exercises/practice/complex-numbers/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.canonical_ref() }}
{{ macros.header() }}

import {{ exercise | to_pascal }} exposing [real, imaginary, add, sub, mul, div, conjugate, abs, exp]

isApproxEq = \z1, z2 ->
z1.re |> Num.isApproxEq z2.re {} && z1.im |> Num.isApproxEq z2.im {}

{% for supercase in cases %}
###
### {{ supercase["description"] }}
###

{% for case in supercase["cases"] -%}
{%- if case["cases"] %}
## {{ case["description"] }}
{%- set subcases = case["cases"] %}
{%- else %}
{%- set subcases = [case] %}
{%- endif %}

{% for subcase in subcases -%}
# {{ subcase["description"] }}
{%- if subcase["input"]["z"] %}
expect
z = {{ plugins.to_complex_number(subcase["input"]["z"]) }}
result = {{ subcase["property"] }} z
{%- if subcase["expected"] is iterable and subcase["expected"] is not string %}
expected = {{ plugins.to_complex_number(subcase["expected"]) }}
result |> isApproxEq expected
{%- else %}
result |> Num.isApproxEq {{ subcase["expected"] | to_roc }} {}
{%- endif %}

{%- elif subcase["input"]["z1"] %}
expect
z1 = {{ plugins.to_complex_number(subcase["input"]["z1"]) }}
z2 = {{ plugins.to_complex_number(subcase["input"]["z2"]) }}
result = z1 |> {{ subcase["property"] }} z2
expected = {{ plugins.to_complex_number(subcase["expected"]) }}
result |> isApproxEq expected
{%- else %}
# This test case is not implemented: perhaps you can implement it?
{%- endif %}

{% endfor %}

{% endfor %}
{% endfor %}

130 changes: 130 additions & 0 deletions exercises/practice/complex-numbers/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[9f98e133-eb7f-45b0-9676-cce001cd6f7a]
description = "Real part -> Real part of a purely real number"

[07988e20-f287-4bb7-90cf-b32c4bffe0f3]
description = "Real part -> Real part of a purely imaginary number"

[4a370e86-939e-43de-a895-a00ca32da60a]
description = "Real part -> Real part of a number with real and imaginary part"

[9b3fddef-4c12-4a99-b8f8-e3a42c7ccef6]
description = "Imaginary part -> Imaginary part of a purely real number"

[a8dafedd-535a-4ed3-8a39-fda103a2b01e]
description = "Imaginary part -> Imaginary part of a purely imaginary number"

[0f998f19-69ee-4c64-80ef-01b086feab80]
description = "Imaginary part -> Imaginary part of a number with real and imaginary part"

[a39b7fd6-6527-492f-8c34-609d2c913879]
description = "Imaginary unit"

[9a2c8de9-f068-4f6f-b41c-82232cc6c33e]
description = "Arithmetic -> Addition -> Add purely real numbers"

[657c55e1-b14b-4ba7-bd5c-19db22b7d659]
description = "Arithmetic -> Addition -> Add purely imaginary numbers"

[4e1395f5-572b-4ce8-bfa9-9a63056888da]
description = "Arithmetic -> Addition -> Add numbers with real and imaginary part"

[1155dc45-e4f7-44b8-af34-a91aa431475d]
description = "Arithmetic -> Subtraction -> Subtract purely real numbers"

[f95e9da8-acd5-4da4-ac7c-c861b02f774b]
description = "Arithmetic -> Subtraction -> Subtract purely imaginary numbers"

[f876feb1-f9d1-4d34-b067-b599a8746400]
description = "Arithmetic -> Subtraction -> Subtract numbers with real and imaginary part"

[8a0366c0-9e16-431f-9fd7-40ac46ff4ec4]
description = "Arithmetic -> Multiplication -> Multiply purely real numbers"

[e560ed2b-0b80-4b4f-90f2-63cefc911aaf]
description = "Arithmetic -> Multiplication -> Multiply purely imaginary numbers"

[4d1d10f0-f8d4-48a0-b1d0-f284ada567e6]
description = "Arithmetic -> Multiplication -> Multiply numbers with real and imaginary part"

[b0571ddb-9045-412b-9c15-cd1d816d36c1]
description = "Arithmetic -> Division -> Divide purely real numbers"

[5bb4c7e4-9934-4237-93cc-5780764fdbdd]
description = "Arithmetic -> Division -> Divide purely imaginary numbers"

[c4e7fef5-64ac-4537-91c2-c6529707701f]
description = "Arithmetic -> Division -> Divide numbers with real and imaginary part"

[c56a7332-aad2-4437-83a0-b3580ecee843]
description = "Absolute value -> Absolute value of a positive purely real number"

[cf88d7d3-ee74-4f4e-8a88-a1b0090ecb0c]
description = "Absolute value -> Absolute value of a negative purely real number"

[bbe26568-86c1-4bb4-ba7a-da5697e2b994]
description = "Absolute value -> Absolute value of a purely imaginary number with positive imaginary part"

[3b48233d-468e-4276-9f59-70f4ca1f26f3]
description = "Absolute value -> Absolute value of a purely imaginary number with negative imaginary part"

[fe400a9f-aa22-4b49-af92-51e0f5a2a6d3]
description = "Absolute value -> Absolute value of a number with real and imaginary part"

[fb2d0792-e55a-4484-9443-df1eddfc84a2]
description = "Complex conjugate -> Conjugate a purely real number"

[e37fe7ac-a968-4694-a460-66cb605f8691]
description = "Complex conjugate -> Conjugate a purely imaginary number"

[f7704498-d0be-4192-aaf5-a1f3a7f43e68]
description = "Complex conjugate -> Conjugate a number with real and imaginary part"

[6d96d4c6-2edb-445b-94a2-7de6d4caaf60]
description = "Complex exponential function -> Euler's identity/formula"

[2d2c05a0-4038-4427-a24d-72f6624aa45f]
description = "Complex exponential function -> Exponential of 0"

[ed87f1bd-b187-45d6-8ece-7e331232c809]
description = "Complex exponential function -> Exponential of a purely real number"

[08eedacc-5a95-44fc-8789-1547b27a8702]
description = "Complex exponential function -> Exponential of a number with real and imaginary part"

[d2de4375-7537-479a-aa0e-d474f4f09859]
description = "Complex exponential function -> Exponential resulting in a number with real and imaginary part"

[06d793bf-73bd-4b02-b015-3030b2c952ec]
description = "Operations between real numbers and complex numbers -> Add real number to complex number"

[d77dbbdf-b8df-43f6-a58d-3acb96765328]
description = "Operations between real numbers and complex numbers -> Add complex number to real number"

[20432c8e-8960-4c40-ba83-c9d910ff0a0f]
description = "Operations between real numbers and complex numbers -> Subtract real number from complex number"

[b4b38c85-e1bf-437d-b04d-49bba6e55000]
description = "Operations between real numbers and complex numbers -> Subtract complex number from real number"

[dabe1c8c-b8f4-44dd-879d-37d77c4d06bd]
description = "Operations between real numbers and complex numbers -> Multiply complex number by real number"

[6c81b8c8-9851-46f0-9de5-d96d314c3a28]
description = "Operations between real numbers and complex numbers -> Multiply real number by complex number"

[8a400f75-710e-4d0c-bcb4-5e5a00c78aa0]
description = "Operations between real numbers and complex numbers -> Divide complex number by real number"

[9a867d1b-d736-4c41-a41e-90bd148e9d5e]
description = "Operations between real numbers and complex numbers -> Divide real number by complex number"
39 changes: 39 additions & 0 deletions exercises/practice/complex-numbers/ComplexNumbers.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module [real, imaginary, add, sub, mul, div, conjugate, abs, exp]

Complex : { re : F64, im : F64 }

real : Complex -> F64
real = \z ->
crash "Please implement the 'real' function"

imaginary : Complex -> F64
imaginary = \z ->
crash "Please implement the 'imaginary' function"

add : Complex, Complex -> Complex
add = \z1, z2 ->
crash "Please implement the 'add' function"

sub : Complex, Complex -> Complex
sub = \z1, z2 ->
crash "Please implement the 'sub' function"

mul : Complex, Complex -> Complex
mul = \z1, z2 ->
crash "Please implement the 'mul' function"

div : Complex, Complex -> Complex
div = \z1, z2 ->
crash "Please implement the 'div' function"

conjugate : Complex -> Complex
conjugate = \z ->
crash "Please implement the 'conjugate' function"

abs : Complex -> F64
abs = \z ->
crash "Please implement the 'abs' function"

exp : Complex -> Complex
exp = \z ->
crash "Please implement the 'exp' function"
Loading

0 comments on commit ad09ee5

Please sign in to comment.