Skip to content

Commit

Permalink
[New Exercise]: Complex Numbers (#516)
Browse files Browse the repository at this point in the history
* Test template, but it's terrible

* A little cleaner

* Much simpler

* Add example

* Added append, decreased difficulty rating

* Add flattened_cases helper
  • Loading branch information
ryanplusplus authored Oct 14, 2023
1 parent ef20bcf commit cdc2bb4
Show file tree
Hide file tree
Showing 10 changed files with 521 additions and 0 deletions.
20 changes: 20 additions & 0 deletions bin/generator_help.cr
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,25 @@ class GeneratorHelp
"pending"
end

def flattened_cases(cases : JSON::Any)
flattened_cases = [] of JSON::Any

cases.as_a.each do |cases|
if cases["cases"]?
cases["cases"].as_a.each do |cases|
if cases["cases"]?
flattened_cases.concat(cases["cases"].as_a)
else
flattened_cases << cases
end
end
else
flattened_cases << cases
end
end

flattened_cases
end

ECR.def_to_s "./bin/templates/template.ecr"
end
9 changes: 9 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,15 @@
"prerequisites": [],
"difficulty": 4
},
{
"slug": "complex-numbers",
"name": "Complex Numbers",
"uuid": "bc681ef9-c0ef-46bc-ac67-8720131161c0",
"practices": [],
"prerequisites": [],
"difficulty": 4,
"topics": []
},
{
"slug": "sum-of-multiples",
"name": "Sum of Multiples",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
In this exercise you will need to add methods to an existing struct (`Number`).
You can see an example of this in the Crystal documentation for [getters and setters][getters-and-setters]

[getters-and-setters]: https://crystal-lang.org/reference/1.9/syntax_and_semantics/methods_and_instance_variables.html#getters-and-setters
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.
17 changes: 17 additions & 0 deletions exercises/practice/complex-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"authors": ["ryanplusplus"],
"files": {
"solution": [
"src/complex_numbers.cr"
],
"test": [
"spec/complex_numbers_spec.cr"
],
"example": [
".meta/src/example.cr"
]
},
"blurb": "Implement complex numbers.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Complex_number"
}
84 changes: 84 additions & 0 deletions exercises/practice/complex-numbers/.meta/src/example.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
class Complex
getter real, imaginary

def initialize(@real : Float64, @imaginary : Float64)
end

def +(other : Complex)
self.class.new(
@real + other.real,
@imaginary + other.imaginary
)
end

def +(f : Float64)
self + self.class.new(f, 0)
end

def -(other : Complex)
self.class.new(
@real - other.real,
@imaginary - other.imaginary
)
end

def -(f : Float64)
self - self.class.new(f, 0)
end

def *(other : Complex)
self.class.new(
@real * other.real - @imaginary * other.imaginary,
@real * other.imaginary + @imaginary * other.real
)
end

def *(f : Float64)
self * self.class.new(f, 0)
end

def /(other : Complex)
self.class.new(
(real * other.real + imaginary * other.imaginary) / (other.real ** 2 + other.imaginary ** 2),
(imaginary * other.real - real * other.imaginary) / (other.real ** 2 + other.imaginary ** 2)
)
end

def /(f : Float64)
self / self.class.new(f, 0)
end

def abs
Math.sqrt(real ** 2 + imaginary ** 2)
end

def conjugate
self.class.new(real, -imaginary)
end

def exp
self.class.new(Math.exp(real), 0) * self.class.new(Math.cos(imaginary), Math.sin(imaginary))
end

def ==(other : Complex)
(real - other.real).abs < 1e-9 && (imaginary - other.imaginary).abs < 1e-9
end
end

struct Number
def +(complex : Complex)
Complex.new(self, 0) + complex
end

def -(complex : Complex)
Complex.new(self, 0) - complex
end

def *(complex : Complex)
Complex.new(self, 0) * complex
end

def /(complex : Complex)
Complex.new(self, 0) / complex
end
end
61 changes: 61 additions & 0 deletions exercises/practice/complex-numbers/.meta/test_template.ecr
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require "spec"
require "../src/*"

<% property_map = {
".real" => ".real",
".imaginary" => ".imaginary",
".abs" => ".abs",
".conjugate" => ".conjugate",
".exp" => ".exp",
".add" => "+",
".sub" => "-",
".mul" => "*",
".div" => "/"
} %>

<%
complex_args = ->(json : JSON::Any) {
json
.as_a
.map(&.to_s)
.map(&.gsub("e", "Math::E"))
.map(&.gsub("pi", "Math::PI"))
.map(&.gsub("ln", "Math.log"))
.join(",")
}
%>

describe <%= to_capitalized(@json["exercise"].to_s).inspect %> do
<% flattened_cases(@json["cases"]).each do |cases| %>
<%= status()%> "<%-= cases["description"] %>" do
<% if cases["input"]["z"]? %>
Complex.new(<%= complex_args.call(cases["input"]["z"]) %>)<%= property_map["." + cases["property"].to_s] -%>
.should eq <% -%>
<% if cases["expected"].as_a? -%>
Complex.new(<%= complex_args.call(cases["expected"]) %>)<% -%>
<% else -%>
<%= cases["expected"] -%>
<% end -%>
<% else %>
(<% -%>
<% if cases["input"]["z1"].as_a? -%>
Complex.new(<%= complex_args.call(cases["input"]["z1"]) %>)<% -%>
<% else -%>
<%= cases["input"]["z1"] -%><% -%>
<% end -%>
<%= property_map["." + cases["property"].to_s] -%><% -%>
<% if cases["input"]["z2"].as_a? -%>
Complex.new(<%= complex_args.call(cases["input"]["z2"]) %>)<% -%>
<% else -%>
<%= cases["input"]["z2"] -%><% -%>
<% end -%>
).should eq<% -%>
<% if cases["expected"].as_a? -%>
Complex.new(<%= complex_args.call(cases["expected"]) %>)<% -%>
<% else -%>
<%= cases["expected"] -%>
<% end -%>
<% end %>
end
<% end %>
end
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"
Loading

0 comments on commit cdc2bb4

Please sign in to comment.