Skip to content
Closed
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
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@
],
"status": "wip"
},
{
"slug": "strange-stopwatch",
"name": "Strange Stopwatch",
"uuid": "6428590f-2bf3-4ca3-b4e9-dea11aceea83",
"concepts": [
"complex-numbers"
],
"prerequisites": [
"numbers", "strings", "function-composition"
],
"status": "wip"
},
{
"slug": "old-annalyns-infiltration",
"name": "Old Annalyn's Infiltration",
Expand Down
36 changes: 36 additions & 0 deletions exercises/concept/strange-stopwatch/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Hints

Using complex numbers for rotations in 2D can leave things much cleaner and numerically more precise.

## 1. Define a 2D vector rotation function

- [Euler's formula][euler] is your friend here.
- The inbuilt [`complex(x, y)`][complex] method is more efficient than assigning `x + im*y`.
- There are methods for retrieving the [real][real] part and [imaginary][imaginary] part of a complex number, or both!

## 2. Define a function to find the angle of the stopwatch's hand

- The inbulit [`angle`][angle] method can be used to quickly find an argument from a complex number.
- You may want to use your `rotation` function with `angle` because you will need a rotation to redefine the zero point, since `angle` uses the convention of having zero on the negative x-axis.
- A translation would also be needed when using `angle` since the convention used has angles between -π and π.

## 3. Define a function to tell the time on the stopwatch

- This is a good opportunity to use your `timearg` function.
- There is an inbuilt method [rad2deg][rad2deg] to convert from radians to degrees.
- Minutes are the [`abs`][abs] value of the vector.

## 4. Define a function to set a timer

- Consider using the [polar form][euler] of a complex number (e.g. `m*ℯ^(iθ)`).
- There is an inbuilt method [deg2rad][deg2rad] to convert from degrees to radians.
- A rotation may be needed to redefine the zero point.

[euler]: https://docs.julialang.org/en/v1/base/math/#Base.cis
[complex]: https://docs.julialang.org/en/v1/base/numbers/#Base.complex-Tuple{Complex}
[real]: https://docs.julialang.org/en/v1/base/math/#Base.real
[imaginary]: https://docs.julialang.org/en/v1/base/math/#Base.imag
[angle]: https://docs.julialang.org/en/v1/base/math/#Base.angle
[abs]: https://docs.julialang.org/en/v1/base/math/#Base.abs
[rad2deg]: https://docs.julialang.org/en/v1/base/math/#Base.Math.rad2deg
[deg2rad]: https://docs.julialang.org/en/v1/base/math/#Base.Math.deg2rad
69 changes: 69 additions & 0 deletions exercises/concept/strange-stopwatch/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Instructions

You've been given a strange stopwatch which has no markings on it but takes 60 seconds to go around once.
What's stranger is that the hand grows by one unit with each passing minute.

To read the time on stopwatch you can place it on some graph paper and read off the x-y coordinates.
Then we will have to convert these coordinates into minutes and seconds.

On the other hand, we may want to use it as a timer, so we need to be able to put a mark on the paper as a reference time.
Here we will have to convert the minutes and seconds into the x-y coordinates.

These operations can be done through trigonometric functions and/or rotation matrices, but they can be made simpler (and more fun, I assure you) with the use of complex numbers.
This ease, which can make rotations quite straightforward, results from Euler's elegant formula, `ℯ^(iθ) = cos(θ) + isin(θ) = x + iy`, where `i = √-1` is the imaginary unit.
For example, the complex number `z = x + iy`, can be rotated about the origin with a simple multiplication `z * ℯ^(-iθ)`.
Here the `x` and `y` are just the coordnates on a real 2D plane.
**Caveat:** A *clockwise* rotation takes a negative exponent, `ℯ^(-iθ) = cos(θ) - isin(θ)`, and a *counterclockwise* rotation takes a positive one, `ℯ^(iθ) = cos(θ) + isin(θ)`.

## 1. Define a 2D vector rotation function

Implement the `rotate` function which takes an x coordinate, a y coordinate and an angle θ (in radians).
The function should rotate the point about the origin by the given angle θ and return the new coordinates as a tuple.
While our stopwatch hand will only have integer lengths, this function should rotate a vector of any length.

```julia-repl
julia> rotate(0, 1, π)
(0, -1)

julia> rotate(1, 1, π)
(-1, -1)
```

## 2. Define a function to find the angle of the stopwatch's hand

Implement the function `timearg` which takes the x-y coordinates the hand and returns the angle in radians.
Since we are dealing with a clock, the zero point will be on the y-axis (i.e. imaginary axis).


```julia-repl
julia> timearg(0, 1)
0

julia> timearg(1, 0)
1.5707963267948966 # equal to π/2
```

## 3. Define a function to tell the time on the stopwatch

Implement a function `readtime` which takes the x-y coordinates of the hand and returns the time as `"Mm Ss"`.


```julia-repl
julia> readtime(1, 0)
"1m 15.0s"

julia> readtime(√2, √2)
"2m 7.5s"
```

## 4. Define a function to set a timer

Implement a function `getcoords` which takes a time, in minutes and seconds, and outputs the x-y coordinates of the tip of the stopwatch's hand at that time.

```julia-repl
julia> getcoords(1, 0)
(0, 1)

julia> getcoords(2, 15)
(2, 0)
```
Empty file.
17 changes: 17 additions & 0 deletions exercises/concept/strange-stopwatch/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"authors": [
"depial"
],
"files": {
"solution": [
"strange-stopwatch.jl"
],
"test": [
"runtests.jl"
],
"exemplar": [
".meta/exemplar.jl"
]
},
"blurb": "Learn about simple rotations of 2D vectors using complex numbers"
}
26 changes: 26 additions & 0 deletions exercises/concept/strange-stopwatch/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Design

## Goal

The goal of this exercise is to introduce the student to the predefined type of complex numbers.

## Learning objectives

- Understand the predefined type of complex numbers and their construction.
- Become familiar with a basic subset of functions, such as `complex`, `abs`, `angle`, `reim`, etc.

## Out of scope

- Modular Arithmetic

## Concepts

The Concepts this exercise unlocks are:

- `complex-numbers`

## Prerequisites

- `numbers`
- `strings`
- `function-composition`
4 changes: 4 additions & 0 deletions exercises/concept/strange-stopwatch/.meta/exemplar.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
rotate(x, y, θ) = reim(complex(x, y)cispi(θ/π))
timearg(x, y) = π - angle(complex(rotate(x, y, π/2)...))
readtime(x, y) = "$(floor(Int, abs(complex(x, y))))m $(rad2deg(timearg(x, y))/6)s"
getcoords(m, s) = rotate(reim(m * cispi(-deg2rad(6s)/π))..., π/2)
61 changes: 61 additions & 0 deletions exercises/concept/strange-stopwatch/runtests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Test

include("strange-stopwatch.jl")

@testset verbose = true "tests" begin
@testset "rotations" begin
x, y = rotate(1, 0, π/2)
@test isapprox(x, 0; atol=1e-2) && isapprox(y, 1; atol=1e-2)

x, y = rotate(1, 0, -π/2)
@test isapprox(x, 0; atol=1e-2) && isapprox(y, -1; atol=1e-2)

x, y = rotate(1, 1, π)
@test isapprox(x, -1; atol=1e-2) && isapprox(y, -1; atol=1e-2)

x, y = rotate(2, 2, -π/3)
@test isapprox(x, 2.73205; atol=1e-2) && isapprox(y, -0.73205; atol=1e-2)
end

@testset "time argument" begin
@test isapprox(timearg(0, 1), 0; atol=1e-2)

@test isapprox(timearg(2, 2), π/4; atol=1e-2)

@test isapprox(timearg(0, -7), π; atol=1e-2)

@test isapprox(timearg(-4, 0), 3π/2; atol=1e-2)

@test isapprox(timearg(1, √3), π/6; atol=1e-2)
end

@testset "read time" begin
minsec(strtime) = parse.(Float64, match(r"(\d+\.?\d*)m (\d+\.?\d*)s", strtime).captures)

m, s = minsec(readtime(1, 0))
@test isapprox(m, 1.0; atol=1e-9) && isapprox(s, 15.0; atol=1e-2)

m, s = minsec(readtime(-4, 0))
@test isapprox(m, 4.0; atol=1e-9) && isapprox(s, 45.0; atol=1e-2)

m, s = minsec(readtime(0, -3))
@test isapprox(m, 3.0; atol=1e-9) && isapprox(s, 30.0; atol=1e-2)

m, s = minsec(readtime(√2, -√2))
@test isapprox(m, 2.0; atol=1e-9) && isapprox(s, 22.5; atol=1e-2)
end

@testset "find timer coordinates" begin
x, y = getcoords(1, 0)
@test isapprox(x, 0; atol=1e-2) && isapprox(y, 1; atol=1e-2)

x, y = getcoords(2, 15)
@test isapprox(x, 2; atol=1e-2) && isapprox(y, 0; atol=1e-2)

x, y = getcoords(3, 45)
@test isapprox(x, -3; atol=1e-2) && isapprox(y, 0; atol=1e-2)

x, y = getcoords(4, 37.5)
@test isapprox(x, -2.8284; atol=1e-2) && isapprox(y, -2.8284; atol=1e-2)
end
end
15 changes: 15 additions & 0 deletions exercises/concept/strange-stopwatch/strange-stopwatch.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function rotate(x, y, θ)

end

function timearg(z)

end

function readtime((a, b))

end

function getcoords(m, s)

end