Add several elixir solutions
This commit is contained in:
parent
57bc7b0289
commit
56734c60b9
39
elixir/grains/.exercism/config.json
Normal file
39
elixir/grains/.exercism/config.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"authors": [
|
||||
"rubysolo"
|
||||
],
|
||||
"contributors": [
|
||||
"andrewsardone",
|
||||
"angelikatyborska",
|
||||
"bunnymatic",
|
||||
"Cohen-Carlisle",
|
||||
"dalexj",
|
||||
"devonestes",
|
||||
"elasticdog",
|
||||
"henrik",
|
||||
"herminiotorres",
|
||||
"jinyeow",
|
||||
"lpil",
|
||||
"neenjaw",
|
||||
"parkerl",
|
||||
"petehuang",
|
||||
"pminten",
|
||||
"sotojuan",
|
||||
"Teapane",
|
||||
"waiting-for-dev"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/grains.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/grains_test.exs"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.ex"
|
||||
]
|
||||
},
|
||||
"blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.",
|
||||
"source": "JavaRanch Cattle Drive, exercise 6",
|
||||
"source_url": "http://www.javaranch.com/grains.jsp"
|
||||
}
|
1
elixir/grains/.exercism/metadata.json
Normal file
1
elixir/grains/.exercism/metadata.json
Normal file
@ -0,0 +1 @@
|
||||
{"track":"elixir","exercise":"grains","id":"323e0439aa1d482b91d857e239fa16b7","url":"https://exercism.org/tracks/elixir/exercises/grains","handle":"halfdan","is_requester":true,"auto_approve":false}
|
4
elixir/grains/.formatter.exs
Normal file
4
elixir/grains/.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
||||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
24
elixir/grains/.gitignore
vendored
Normal file
24
elixir/grains/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
grains-*.tar
|
||||
|
75
elixir/grains/HELP.md
Normal file
75
elixir/grains/HELP.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be skipped.
|
||||
|
||||
Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.
|
||||
|
||||
For example:
|
||||
|
||||
```elixir
|
||||
# @tag :pending
|
||||
test "shouting" do
|
||||
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
|
||||
end
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/grains.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
64
elixir/grains/README.md
Normal file
64
elixir/grains/README.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Grains
|
||||
|
||||
Welcome to Grains on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Calculate the number of grains of wheat on a chessboard given that the number
|
||||
on each square doubles.
|
||||
|
||||
There once was a wise servant who saved the life of a prince. The king
|
||||
promised to pay whatever the servant could dream up. Knowing that the
|
||||
king loved chess, the servant told the king he would like to have grains
|
||||
of wheat. One grain on the first square of a chess board, with the number
|
||||
of grains doubling on each successive square.
|
||||
|
||||
There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on).
|
||||
|
||||
Write code that shows:
|
||||
|
||||
- how many grains were on a given square, and
|
||||
- the total number of grains on the chessboard
|
||||
|
||||
## For bonus points
|
||||
|
||||
Did you get the tests passing and the code clean? If you want to, these
|
||||
are some additional things you could try:
|
||||
|
||||
- Optimize for speed.
|
||||
- Optimize for readability.
|
||||
|
||||
Then please share your thoughts in a comment on the submission. Did this
|
||||
experiment make the code better? Worse? Did you learn anything from it?
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @rubysolo
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @andrewsardone
|
||||
- @angelikatyborska
|
||||
- @bunnymatic
|
||||
- @Cohen-Carlisle
|
||||
- @dalexj
|
||||
- @devonestes
|
||||
- @elasticdog
|
||||
- @henrik
|
||||
- @herminiotorres
|
||||
- @jinyeow
|
||||
- @lpil
|
||||
- @neenjaw
|
||||
- @parkerl
|
||||
- @petehuang
|
||||
- @pminten
|
||||
- @sotojuan
|
||||
- @Teapane
|
||||
- @waiting-for-dev
|
||||
|
||||
### Based on
|
||||
|
||||
JavaRanch Cattle Drive, exercise 6 - http://www.javaranch.com/grains.jsp
|
23
elixir/grains/lib/grains.ex
Normal file
23
elixir/grains/lib/grains.ex
Normal file
@ -0,0 +1,23 @@
|
||||
defmodule Grains do
|
||||
@doc """
|
||||
Calculate two to the power of the input minus one.
|
||||
"""
|
||||
@spec square(pos_integer()) :: {:ok, pos_integer()} | {:error, String.t()}
|
||||
def square(number) when number < 1 or number > 64 do
|
||||
{:error, "The requested square must be between 1 and 64 (inclusive)"}
|
||||
end
|
||||
def square(number), do: {:ok, 2**(number-1)}
|
||||
|
||||
@doc """
|
||||
Adds square of each number from 1 to 64.
|
||||
"""
|
||||
@spec total :: {:ok, pos_integer()}
|
||||
def total do
|
||||
Enum.reduce_while(1..64, {:ok, 0}, fn n, {:ok, sum} ->
|
||||
case square(n) do
|
||||
{:ok, squared} -> {:cont, {:ok, squared + sum}}
|
||||
{:error, error} -> {:halt, {:error, error}}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
28
elixir/grains/mix.exs
Normal file
28
elixir/grains/mix.exs
Normal file
@ -0,0 +1,28 @@
|
||||
defmodule Grains.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :grains,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.8",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
61
elixir/grains/test/grains_test.exs
Normal file
61
elixir/grains/test/grains_test.exs
Normal file
@ -0,0 +1,61 @@
|
||||
defmodule GrainsTest do
|
||||
use ExUnit.Case
|
||||
|
||||
# @tag :pending
|
||||
test "square 1" do
|
||||
assert Grains.square(1) === {:ok, 1}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 2" do
|
||||
assert Grains.square(2) === {:ok, 2}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 3" do
|
||||
assert Grains.square(3) === {:ok, 4}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 4" do
|
||||
assert Grains.square(4) === {:ok, 8}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 16" do
|
||||
assert Grains.square(16) === {:ok, 32768}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 32" do
|
||||
assert Grains.square(32) === {:ok, 2_147_483_648}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 64" do
|
||||
assert Grains.square(64) === {:ok, 9_223_372_036_854_775_808}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "total grains" do
|
||||
assert Grains.total() === {:ok, 18_446_744_073_709_551_615}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square greater than 64 returns an error" do
|
||||
assert Grains.square(65) ===
|
||||
{:error, "The requested square must be between 1 and 64 (inclusive)"}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "negative square returns an error" do
|
||||
assert Grains.square(-1) ===
|
||||
{:error, "The requested square must be between 1 and 64 (inclusive)"}
|
||||
end
|
||||
|
||||
@tag :pending
|
||||
test "square 0 returns an error" do
|
||||
assert Grains.square(0) ===
|
||||
{:error, "The requested square must be between 1 and 64 (inclusive)"}
|
||||
end
|
||||
end
|
2
elixir/grains/test/test_helper.exs
Normal file
2
elixir/grains/test/test_helper.exs
Normal file
@ -0,0 +1,2 @@
|
||||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true)
|
21
elixir/lucas-numbers/.exercism/config.json
Normal file
21
elixir/lucas-numbers/.exercism/config.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"authors": [
|
||||
"neenjaw"
|
||||
],
|
||||
"contributors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/lucas_numbers.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/lucas_numbers_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"language_versions": ">=1.10",
|
||||
"blurb": "Learn about streams by generating the Lucas number sequence."
|
||||
}
|
1
elixir/lucas-numbers/.exercism/metadata.json
Normal file
1
elixir/lucas-numbers/.exercism/metadata.json
Normal file
@ -0,0 +1 @@
|
||||
{"track":"elixir","exercise":"lucas-numbers","id":"843df89f0b464117ac83a3f4120e8404","url":"https://exercism.org/tracks/elixir/exercises/lucas-numbers","handle":"halfdan","is_requester":true,"auto_approve":false}
|
4
elixir/lucas-numbers/.formatter.exs
Normal file
4
elixir/lucas-numbers/.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
||||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
27
elixir/lucas-numbers/.gitignore
vendored
Normal file
27
elixir/lucas-numbers/.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
lucas_numbers-*.tar
|
||||
|
||||
|
||||
# Temporary files for e.g. tests
|
||||
/tmp
|
75
elixir/lucas-numbers/HELP.md
Normal file
75
elixir/lucas-numbers/HELP.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be skipped.
|
||||
|
||||
Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.
|
||||
|
||||
For example:
|
||||
|
||||
```elixir
|
||||
# @tag :pending
|
||||
test "shouting" do
|
||||
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
|
||||
end
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/lucas_numbers.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
29
elixir/lucas-numbers/HINTS.md
Normal file
29
elixir/lucas-numbers/HINTS.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- Use the built-in [(linked) list type][list].
|
||||
- Use the built-in [`Stream`][stream] module functions to create a stream
|
||||
|
||||
## 1. Generate the base cases
|
||||
|
||||
- You can use multiple [function clauses][multiple-fn-clauses] and [pattern-matching][pattern-matching] to create the base case functions.
|
||||
|
||||
## 2. Create the generalized case
|
||||
|
||||
- Use the [`Stream.iterate/2`][stream-iterate] function to generate a sequence of numbers, with the next being created from the previous.
|
||||
- The starting numbers are `2` then `1`, which you can pass in together using a tuple to make a pair `{2, 1}`
|
||||
- Make sure the next number is the sum of the two numbers previous to it.
|
||||
- To evaluate the stream to a list, use an [`Enum`][enum] function.
|
||||
|
||||
## 3. Catch bad arguments
|
||||
|
||||
- Use a [guard][guards] to catch the cases when an integer isn't passed as an argument to `generate/1`.
|
||||
|
||||
[enum]: https://hexdocs.pm/elixir/Enum.html#content
|
||||
[guards]: https://hexdocs.pm/elixir/master/patterns-and-guards.html#guards
|
||||
[list]: https://elixir-lang.org/getting-started/basic-types.html#linked-lists
|
||||
[multiple-fn-clauses]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||
[pattern-matching]: https://elixir-lang.org/getting-started/pattern-matching.html#pattern-matching-1
|
||||
[stream]: https://hexdocs.pm/elixir/Stream.html#content
|
||||
[stream-iterate]: https://hexdocs.pm/elixir/Stream.html#iterate/2
|
67
elixir/lucas-numbers/README.md
Normal file
67
elixir/lucas-numbers/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# Lucas Numbers
|
||||
|
||||
Welcome to Lucas Numbers on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Streams
|
||||
|
||||
All functions in the [`Enum` module][exercism-enum] are _eager_. When performing multiple operations on enumerables with the `Enum` module, each operation is going to generate an intermediate result.
|
||||
|
||||
The `Stream` module is a _lazy_ alternative to the _eager_ `Enum` module. It offers many of the same functions as `Enum`, but instead of generating intermediate results, it builds a series of computations that are only executed once the stream is passed to a function from the `Enum` module.
|
||||
|
||||
Streams implement the _Enumerable [protocol][exercism-protocols]_ and are composable -- you can chain them together to create more complex functionality.
|
||||
|
||||
[exercism-enum]: https://exercism.org/tracks/elixir/concepts/enum
|
||||
[exercism-protocols]: https://exercism.org/tracks/elixir/concepts/protocols
|
||||
|
||||
## Instructions
|
||||
|
||||
You are a huge fan of the [Numberphile Youtube channel](https://www.youtube.com/watch?v=PeUbRXnbmms) and you just saw a cool video about the _Lucas Number Sequence_. You want to create this sequence using Elixir.
|
||||
|
||||
While designing your function, you want to make use of _lazy evaluation_, so that you can generate as many numbers as you want, but only if you need to -- So you decide to use a stream:
|
||||
|
||||
## 1. Generate the base cases
|
||||
|
||||
You know that the sequence has two starting numbers which don't follow the same rule. Write two base case clauses to return these numbers:
|
||||
|
||||
```elixir
|
||||
LucasNumbers.generate(1)
|
||||
# => [2]
|
||||
|
||||
LucasNumbers.generate(2)
|
||||
# => [2, 1]
|
||||
```
|
||||
|
||||
## 2. Create the generalized case
|
||||
|
||||
For any sequence longer than 2, you know that you need to add the previous two numbers to get the next number and so on. Write the generalized case.
|
||||
|
||||
```elixir
|
||||
LucasNumbers.generate(3)
|
||||
# => [2, 1, 3]
|
||||
|
||||
LucasNumbers.generate(4)
|
||||
# => [2, 1, 3, 4]
|
||||
```
|
||||
|
||||
## 3. Catch bad arguments
|
||||
|
||||
Later, you find someone is using your function and having problems because they are using incorrect arguments. Add a guard clause to raise an error if a non-integer or an integer less than 1 is used to generate the sequence:
|
||||
|
||||
```elixir
|
||||
LucasNumbers.generate("Hello World")
|
||||
# => ** (ArgumentError) count must be specified as an integer >= 1
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @neenjaw
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
18
elixir/lucas-numbers/lib/lucas_numbers.ex
Normal file
18
elixir/lucas-numbers/lib/lucas_numbers.ex
Normal file
@ -0,0 +1,18 @@
|
||||
defmodule LucasNumbers do
|
||||
defguardp is_positive(k) when is_integer(k) and k > 0
|
||||
@moduledoc """
|
||||
Lucas numbers are an infinite sequence of numbers which build progressively
|
||||
which hold a strong correlation to the golden ratio (φ or ϕ)
|
||||
|
||||
E.g.: 2, 1, 3, 4, 7, 11, 18, 29, ...
|
||||
"""
|
||||
def generate(1), do: [2]
|
||||
def generate(2), do: [2, 1]
|
||||
def generate(count) when is_positive(count) do
|
||||
Stream.iterate({2,1}, fn {x,y} -> {y, x+y} end)
|
||||
|> Stream.map(fn {x,_} -> x end)
|
||||
|> Enum.take(count)
|
||||
end
|
||||
|
||||
def generate(_), do: raise ArgumentError, "count must be specified as an integer >= 1"
|
||||
end
|
28
elixir/lucas-numbers/mix.exs
Normal file
28
elixir/lucas-numbers/mix.exs
Normal file
@ -0,0 +1,28 @@
|
||||
defmodule LucasNumbers.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :lucas_numbers,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
112
elixir/lucas-numbers/test/lucas_numbers_test.exs
Normal file
112
elixir/lucas-numbers/test/lucas_numbers_test.exs
Normal file
@ -0,0 +1,112 @@
|
||||
defmodule LucasNumbersTest do
|
||||
use ExUnit.Case
|
||||
|
||||
@tag task_id: 1
|
||||
test "generates a sequence of length 1" do
|
||||
assert LucasNumbers.generate(1) == [2]
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "generates a sequence of length 2" do
|
||||
assert LucasNumbers.generate(2) == [2, 1]
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 3" do
|
||||
assert LucasNumbers.generate(3) == [2, 1, 3]
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 4" do
|
||||
assert LucasNumbers.generate(4) == [2, 1, 3, 4]
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 5" do
|
||||
sequence = [2, 1, 3, 4, 7]
|
||||
|
||||
assert LucasNumbers.generate(5) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 6" do
|
||||
sequence = [2, 1, 3, 4, 7, 11]
|
||||
|
||||
assert LucasNumbers.generate(6) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 7" do
|
||||
sequence = [2, 1, 3, 4, 7, 11, 18]
|
||||
|
||||
assert LucasNumbers.generate(7) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 8" do
|
||||
sequence = [2, 1, 3, 4, 7, 11, 18, 29]
|
||||
|
||||
assert LucasNumbers.generate(8) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 9" do
|
||||
sequence = [2, 1, 3, 4, 7, 11, 18, 29, 47]
|
||||
|
||||
assert LucasNumbers.generate(9) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 10" do
|
||||
sequence = [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]
|
||||
|
||||
assert LucasNumbers.generate(10) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "generates a sequence of length 25" do
|
||||
sequence = [
|
||||
2,
|
||||
1,
|
||||
3,
|
||||
4,
|
||||
7,
|
||||
11,
|
||||
18,
|
||||
29,
|
||||
47,
|
||||
76,
|
||||
123,
|
||||
199,
|
||||
322,
|
||||
521,
|
||||
843,
|
||||
1364,
|
||||
2207,
|
||||
3571,
|
||||
5778,
|
||||
9349,
|
||||
15127,
|
||||
24476,
|
||||
39603,
|
||||
64079,
|
||||
103_682
|
||||
]
|
||||
|
||||
assert LucasNumbers.generate(25) == sequence
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "catch incorrect non-integer arguments" do
|
||||
assert_raise ArgumentError, "count must be specified as an integer >= 1", fn ->
|
||||
LucasNumbers.generate("Hello world!")
|
||||
end
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "catch incorrect integer arguments" do
|
||||
assert_raise ArgumentError, "count must be specified as an integer >= 1", fn ->
|
||||
LucasNumbers.generate(-1)
|
||||
end
|
||||
end
|
||||
end
|
2
elixir/lucas-numbers/test/test_helper.exs
Normal file
2
elixir/lucas-numbers/test/test_helper.exs
Normal file
@ -0,0 +1,2 @@
|
||||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
21
elixir/new-passport/.exercism/config.json
Normal file
21
elixir/new-passport/.exercism/config.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"authors": [
|
||||
"jiegillet"
|
||||
],
|
||||
"contributors": [
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lib/new_passport.ex"
|
||||
],
|
||||
"test": [
|
||||
"test/new_passport_test.exs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.ex"
|
||||
]
|
||||
},
|
||||
"language_versions": ">=1.10",
|
||||
"blurb": "Learn about `with` to concentrate on the happy path and manage a stressful day of facing bureaucracy."
|
||||
}
|
1
elixir/new-passport/.exercism/metadata.json
Normal file
1
elixir/new-passport/.exercism/metadata.json
Normal file
@ -0,0 +1 @@
|
||||
{"track":"elixir","exercise":"new-passport","id":"674520f3b67548a8907ce0871db7f77c","url":"https://exercism.org/tracks/elixir/exercises/new-passport","handle":"halfdan","is_requester":true,"auto_approve":false}
|
4
elixir/new-passport/.formatter.exs
Normal file
4
elixir/new-passport/.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
||||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
24
elixir/new-passport/.gitignore
vendored
Normal file
24
elixir/new-passport/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
match_binary-*.tar
|
||||
|
75
elixir/new-passport/HELP.md
Normal file
75
elixir/new-passport/HELP.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
From the terminal, change to the base directory of the exercise then execute the tests with:
|
||||
|
||||
```bash
|
||||
$ mix test
|
||||
```
|
||||
|
||||
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
|
||||
|
||||
Documentation:
|
||||
|
||||
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
|
||||
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
|
||||
|
||||
## Pending tests
|
||||
|
||||
In test suites of practice exercises, all but the first test have been tagged to be skipped.
|
||||
|
||||
Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.
|
||||
|
||||
For example:
|
||||
|
||||
```elixir
|
||||
# @tag :pending
|
||||
test "shouting" do
|
||||
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
|
||||
end
|
||||
```
|
||||
|
||||
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
|
||||
|
||||
```bash
|
||||
$ mix test --include pending
|
||||
```
|
||||
|
||||
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
|
||||
|
||||
```elixir
|
||||
# ExUnit.configure(exclude: :pending, trace: true)
|
||||
```
|
||||
|
||||
## Useful `mix test` options
|
||||
|
||||
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
* `--max-failures` - the suite stops evaluating tests when this number of test failures
|
||||
is reached
|
||||
* `--seed 0` - disables randomization so the tests in a single file will always be ran
|
||||
in the same order they were defined in
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lib/new_passport.ex` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
|
||||
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).
|
38
elixir/new-passport/HINTS.md
Normal file
38
elixir/new-passport/HINTS.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- Read about using `with` in the [official Getting Started guide][getting-started-with].
|
||||
- Review the functions available in the [`NaiveDateTime` module][naive-date-time], the [`Date` module][date], and the [`Time` module][time].
|
||||
|
||||
## 1. Get into the building
|
||||
|
||||
- Match the `:ok` tuple returned by `enter_building/1` in `with` with `<-`.
|
||||
- In the `do` part of `with`, return an `:ok` tuple with the value you just matched.
|
||||
- Since you don't need to modify the error, you don't need an `else` block.
|
||||
|
||||
## 2. Go to the information desk and find which counter you should go to
|
||||
|
||||
- Match the `:ok` tuple returned by `find_counter_information/1` in `with` with `<-`.
|
||||
- Apply the anonymous function your just matched and match the result with `<-`.
|
||||
- In the `do` part of `with`, return an `:ok` tuple with the counter you obtained.
|
||||
- Add an `else` block that will expect a `:coffee_break` tuple and return a `:retry` tuple with a `NaiveDateTime`.
|
||||
- A minute has `60` seconds.
|
||||
- There is a [built-in function][naive-date-time-add] that adds a given number of seconds to a `NaiveDateTime` struct.
|
||||
- Other errors should be returned as they are.
|
||||
|
||||
## 3. Go to the counter and get your form stamped
|
||||
|
||||
- Match the `:ok` tuple returned by `stamp_form/3` in `with` with `<-`.
|
||||
- In the `do` part of `with`, return an `:ok` tuple with the checksum.
|
||||
|
||||
## 4. Receive your new passport
|
||||
|
||||
- In the `do` part of `with`, use `get_new_passport_number/3` and return the result in an `:ok` tuple.
|
||||
|
||||
[with]: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1
|
||||
[getting-started-with]: https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html#with
|
||||
[naive-date-time]: https://hexdocs.pm/elixir/NaiveDateTime.html
|
||||
[time]: https://hexdocs.pm/elixir/Time.html
|
||||
[date]: https://hexdocs.pm/elixir/Date.html
|
||||
[naive-date-time-add]: https://hexdocs.pm/elixir/NaiveDateTime.html#add/3
|
78
elixir/new-passport/README.md
Normal file
78
elixir/new-passport/README.md
Normal file
@ -0,0 +1,78 @@
|
||||
# New Passport
|
||||
|
||||
Welcome to New Passport on Exercism's Elixir Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## With
|
||||
|
||||
The [special form with][with] provides a way to focus on the "happy path" of a series of potentially failing steps and deal with the failures later.
|
||||
|
||||
```elixir
|
||||
with {:ok, id} <- get_id(username),
|
||||
{:ok, avatar} <- fetch_avatar(id),
|
||||
{:ok, image_type} <- check_valid_image_type(avatar) do
|
||||
{:ok, image_type, avatar}
|
||||
else
|
||||
:not_found ->
|
||||
{:error, "invalid username"}
|
||||
|
||||
{:error, "not an image"} ->
|
||||
{:error, "avatar associated to #{username} is not an image"}
|
||||
|
||||
err ->
|
||||
err
|
||||
end
|
||||
```
|
||||
|
||||
At each step, if a clause matches, the chain will continue until the `do` block is executed. If one match fails, the chain stops and the non-matching clause is returned. You have the option of using an `else` block to catch failed matches and modify the return value.
|
||||
|
||||
[with]: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1
|
||||
|
||||
## Instructions
|
||||
|
||||
Your passport is about to expire, so you need to drop by the city office to renew it. You know from previous experience that your city office is not necessarily the easiest to deal with, so you decide to do your best to always "focus on the happy path".
|
||||
|
||||
You print out the form you need to get your new passport, fill it out, jump into your car, drive around the block, park and head to the office.
|
||||
|
||||
All the following tasks will require implementing and extending `get_new_passport/3`.
|
||||
|
||||
## 1. Get into the building
|
||||
|
||||
It turns out that the building is only open in the afternoon, and not at the same time everyday.
|
||||
|
||||
Call the function `enter_building/1` with the current time (given to you as first argument of `get_new_passport/3`). If the building is open, the function will return a tuple with `:ok` and a timestamp that you will need later, otherwise a tuple with `:error` and a message. For now, the happy path can return the `:ok` tuple.
|
||||
|
||||
If you get an `:error` tuple, use the `else` block to return it.
|
||||
|
||||
## 2. Go to the information desk and find which counter you should go to
|
||||
|
||||
The information desk is notorious for taking long coffee breaks. If you are lucky enough to find someone there, they will give you an instruction manual which will explain which counter you need to go to depending on your birth date.
|
||||
|
||||
Call the function `find_counter_information/1` with the current time. You will get either a tuple with `:ok` and a manual, represented by an anonymous function, or a tuple with `:coffee_break` and more instructions. In your happy path where you receive the manual, apply it to you birthday (second argument of `get_new_passport/3`). It will return the number of the counter where you need to go. Return an `:ok` tuple with that counter number.
|
||||
|
||||
If you get a `:coffee_break` message, return a tuple with `:retry` and a `NaiveDateTime` pointing to 15 minutes after the current time. As before, if you get an `:error` tuple, return it.
|
||||
|
||||
## 3. Go to the counter and get your form stamped
|
||||
|
||||
For some reason, different counters require forms of different colors. Of course, you printed the first one you found on the website, so you focus on your happy path and hope for the best.
|
||||
|
||||
Call the function `stamp_form/3` with the timestamp you received at the entrance, the counter and the form you brought (last argument of `get_new_passport/3`). You will get either a tuple with `:ok` and a checksum that will be used to verify your passport number or a tuple with `:error` and a message. Have your happy path return an `:ok` tuple with the checksum. If you get an `:error` tuple, return it.
|
||||
|
||||
## 4. Receive your new passport
|
||||
|
||||
Finally, you have all the documents you need.
|
||||
|
||||
Call `get_new_passport_number/3` with the timestamp, the counter and the checksum you received earlier. You will receive a string with your final passport number, all that is left to do is to return that string in a tuple with `:ok` and go home.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @jiegillet
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @angelikatyborska
|
60
elixir/new-passport/lib/new_passport.ex
Normal file
60
elixir/new-passport/lib/new_passport.ex
Normal file
@ -0,0 +1,60 @@
|
||||
defmodule NewPassport do
|
||||
def get_new_passport(now, birthday, form) do
|
||||
with {:ok, entrance} <- enter_building(now),
|
||||
{:ok, manual} <- find_counter_information(now),
|
||||
{:ok, counter} <- {:ok, manual.(birthday)},
|
||||
{:ok, checksum} <- stamp_form(entrance, counter, form) do
|
||||
{:ok, get_new_passport_number(entrance, counter, checksum)}
|
||||
else
|
||||
{:coffee_break, _} -> {:retry, NaiveDateTime.add(now, 60*15, :second)}
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
# Do not modify the functions below
|
||||
|
||||
defp enter_building(%NaiveDateTime{} = datetime) do
|
||||
day = Date.day_of_week(datetime)
|
||||
time = NaiveDateTime.to_time(datetime)
|
||||
|
||||
cond do
|
||||
day <= 4 and time_between(time, ~T[13:00:00], ~T[15:30:00]) ->
|
||||
{:ok, datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix()}
|
||||
|
||||
day == 5 and time_between(time, ~T[13:00:00], ~T[14:30:00]) ->
|
||||
{:ok, datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix()}
|
||||
|
||||
true ->
|
||||
{:error, "city office is closed"}
|
||||
end
|
||||
end
|
||||
|
||||
@eighteen_years 18 * 365
|
||||
defp find_counter_information(%NaiveDateTime{} = datetime) do
|
||||
time = NaiveDateTime.to_time(datetime)
|
||||
|
||||
if time_between(time, ~T[14:00:00], ~T[14:20:00]) do
|
||||
{:coffee_break, "information counter staff on coffee break, come back in 15 minutes"}
|
||||
else
|
||||
{:ok, fn %Date{} = birthday -> 1 + div(Date.diff(datetime, birthday), @eighteen_years) end}
|
||||
end
|
||||
end
|
||||
|
||||
defp stamp_form(timestamp, counter, :blue) when rem(counter, 2) == 1 do
|
||||
{:ok, 3 * (timestamp + counter) + 1}
|
||||
end
|
||||
|
||||
defp stamp_form(timestamp, counter, :red) when rem(counter, 2) == 0 do
|
||||
{:ok, div(timestamp + counter, 2)}
|
||||
end
|
||||
|
||||
defp stamp_form(_timestamp, _counter, _form), do: {:error, "wrong form color"}
|
||||
|
||||
defp get_new_passport_number(timestamp, counter, checksum) do
|
||||
"#{timestamp}-#{counter}-#{checksum}"
|
||||
end
|
||||
|
||||
defp time_between(time, from, to) do
|
||||
Time.compare(from, time) != :gt and Time.compare(to, time) == :gt
|
||||
end
|
||||
end
|
28
elixir/new-passport/mix.exs
Normal file
28
elixir/new-passport/mix.exs
Normal file
@ -0,0 +1,28 @@
|
||||
defmodule NewPassport.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :new_passport,
|
||||
version: "0.1.0",
|
||||
# elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
155
elixir/new-passport/test/new_passport_test.exs
Normal file
155
elixir/new-passport/test/new_passport_test.exs
Normal file
@ -0,0 +1,155 @@
|
||||
defmodule NewPassportTest do
|
||||
use ExUnit.Case
|
||||
|
||||
describe "getting into the building" do
|
||||
@tag task_id: 1
|
||||
test "building is closed in the morning" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 10:30:00], ~D[1984-09-14], :blue) ==
|
||||
{:error, "city office is closed"}
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "building is closed early on Friday afternoon" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-08 15:00:00], ~D[1984-09-14], :blue) ==
|
||||
{:error, "city office is closed"}
|
||||
end
|
||||
|
||||
@tag task_id: 1
|
||||
test "entering during business hour" do
|
||||
assert {:ok, _} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 15:00:00], ~D[1984-09-14], :blue)
|
||||
end
|
||||
end
|
||||
|
||||
describe "find the right counter" do
|
||||
@tag task_id: 2
|
||||
test "information staff on coffee break" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:10:00], ~D[1984-09-14], :blue) ==
|
||||
{:retry, ~N[2021-10-11 14:25:00]}
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "information staff on coffee break, retry at given time" do
|
||||
assert {:ok, _} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 14:25:00], ~D[1984-09-14], :blue)
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "information staff on coffee break on Friday 15 minutes before closing time" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-08 14:15:00], ~D[1984-09-14], :blue) ==
|
||||
{:retry, ~N[2021-10-08 14:30:00]}
|
||||
end
|
||||
|
||||
@tag task_id: 2
|
||||
test "retry after previous attempt, hit closing time" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-08 14:30:00], ~D[1984-09-14], :blue) ==
|
||||
{:error, "city office is closed"}
|
||||
end
|
||||
end
|
||||
|
||||
describe "get the passport form stamped" do
|
||||
@tag task_id: 3
|
||||
test "illegal form color" do
|
||||
assert NewPassport.get_new_passport(
|
||||
~N[2021-10-11 14:25:00],
|
||||
~D[1984-09-14],
|
||||
:orange_and_purple
|
||||
) == {:error, "wrong form color"}
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "wrong form color" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:25:00], ~D[1984-09-14], :red) ==
|
||||
{:error, "wrong form color"}
|
||||
end
|
||||
|
||||
@tag task_id: 3
|
||||
test "correct form color" do
|
||||
assert {:ok, _} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 14:25:00], ~D[1984-09-14], :blue)
|
||||
end
|
||||
end
|
||||
|
||||
describe "receive the new passport number" do
|
||||
@tag task_id: 4
|
||||
test "get the right timestamp" do
|
||||
assert {:ok, passport_number} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 13:00:00], ~D[1984-09-14], :blue)
|
||||
|
||||
[timestamp, _counter, _checksum] = String.split(passport_number, "-")
|
||||
assert timestamp == "1633957200"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "get the right timestamp after waiting for coffee break" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:15:00], ~D[1984-09-14], :blue) ==
|
||||
{:retry, ~N[2021-10-11 14:30:00]}
|
||||
|
||||
assert {:ok, passport_number} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 14:30:00], ~D[1984-09-14], :blue)
|
||||
|
||||
[timestamp, _counter, _checksum] = String.split(passport_number, "-")
|
||||
assert timestamp == "1633962600"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "get the right timestamp after waiting twice for coffee break" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:00:00], ~D[1984-09-14], :blue) ==
|
||||
{:retry, ~N[2021-10-11 14:15:00]}
|
||||
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:15:00], ~D[1984-09-14], :blue) ==
|
||||
{:retry, ~N[2021-10-11 14:30:00]}
|
||||
|
||||
assert {:ok, passport_number} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 14:30:00], ~D[1984-09-14], :blue)
|
||||
|
||||
[timestamp, _counter, _checksum] = String.split(passport_number, "-")
|
||||
assert timestamp == "1633962600"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "16 year old finds the right counter" do
|
||||
assert {:ok, passport_number} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 14:30:00], ~D[2005-09-14], :blue)
|
||||
|
||||
[_timestamp, counter, _checksum] = String.split(passport_number, "-")
|
||||
assert counter == "1"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "34 year old finds the right counter" do
|
||||
assert {:ok, passport_number} =
|
||||
NewPassport.get_new_passport(~N[2021-10-11 14:30:00], ~D[1987-09-14], :red)
|
||||
|
||||
[_timestamp, counter, _checksum] = String.split(passport_number, "-")
|
||||
assert counter == "2"
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "get the right passport number" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 15:00:00], ~D[1984-09-14], :blue) ==
|
||||
{:ok, "1633964400-3-4901893210"}
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "get a passport number after waiting for a coffee break" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:15:00], ~D[1984-09-14], :blue) ==
|
||||
{:retry, ~N[2021-10-11 14:30:00]}
|
||||
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:30:00], ~D[1984-09-14], :blue) ==
|
||||
{:ok, "1633962600-3-4901887810"}
|
||||
end
|
||||
|
||||
@tag task_id: 4
|
||||
test "get a passport number after two coffee breaks" do
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:00:00], ~D[1964-09-14], :red) ==
|
||||
{:retry, ~N[2021-10-11 14:15:00]}
|
||||
|
||||
assert NewPassport.get_new_passport(~N[2021-10-11 14:15:00], ~D[1964-09-14], :red) ==
|
||||
{:retry, ~N[2021-10-11 14:30:00]}
|
||||
|
||||
assert NewPassport.get_new_passport(~N[2021-10-12 14:30:00], ~D[1964-09-14], :red) ==
|
||||
{:ok, "1634049000-4-817024502"}
|
||||
end
|
||||
end
|
||||
end
|
2
elixir/new-passport/test/test_helper.exs
Normal file
2
elixir/new-passport/test/test_helper.exs
Normal file
@ -0,0 +1,2 @@
|
||||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
Loading…
x
Reference in New Issue
Block a user