commit d0f60e4a91e4603941b4cbce017586e4f868de6c Author: Ruidy Date: Mon Jan 31 09:28:22 2022 -0400 repo init diff --git a/darts/.exercism/config.json b/darts/.exercism/config.json new file mode 100644 index 0000000..b40420a --- /dev/null +++ b/darts/.exercism/config.json @@ -0,0 +1,19 @@ +{ + "authors": ["jiegillet"], + "contributors": [ + "angelikatyborska" + ], + "files": { + "example": [ + ".meta/example.ex" + ], + "solution": [ + "lib/darts.ex" + ], + "test": [ + "test/darts_test.exs" + ] + }, + "blurb": "Write a function that returns the earned points in a single toss of a Darts game.", + "source": "Inspired by an exercise created by a professor Della Paolera in Argentina" +} diff --git a/darts/.exercism/metadata.json b/darts/.exercism/metadata.json new file mode 100644 index 0000000..f168770 --- /dev/null +++ b/darts/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"darts","id":"b980033b8c73461ca0633fa55a13cbf6","url":"https://exercism.org/tracks/elixir/exercises/darts","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/darts/.formatter.exs b/darts/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/darts/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/darts/HELP.md b/darts/HELP.md new file mode 100644 index 0000000..f85950c --- /dev/null +++ b/darts/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/darts.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). \ No newline at end of file diff --git a/darts/HINTS.md b/darts/HINTS.md new file mode 100644 index 0000000..18659c5 --- /dev/null +++ b/darts/HINTS.md @@ -0,0 +1,7 @@ +# Hints + +## General + +- The distance of a point `(x, y)` from the center of the target `(0, 0)` can be calculated with `sqrt(x*x + y *y)` + +- You can calculate the square root of `x` by raising it to the power of `1/2`. In other words, `sqrt(x) == pow(x, 0.5)` \ No newline at end of file diff --git a/darts/README.md b/darts/README.md new file mode 100644 index 0000000..00a7569 --- /dev/null +++ b/darts/README.md @@ -0,0 +1,37 @@ +# Darts + +Welcome to Darts 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 :) + +## Instructions + +Write a function that returns the earned points in a single toss of a Darts game. + +[Darts](https://en.wikipedia.org/wiki/Darts) is a game where players +throw darts to a [target](https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg). + +In our particular instance of the game, the target rewards with 4 different amounts of points, depending on where the dart lands: + +* If the dart lands outside the target, player earns no points (0 points). +* If the dart lands in the outer circle of the target, player earns 1 point. +* If the dart lands in the middle circle of the target, player earns 5 points. +* If the dart lands in the inner circle of the target, player earns 10 points. + +The outer circle has a radius of 10 units (This is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered to the same point (That is, the circles are [concentric](http://mathworld.wolfram.com/ConcentricCircles.html)) defined by the coordinates (0, 0). + +Write a function that given a point in the target (defined by its `real` cartesian coordinates `x` and `y`), returns the correct amount earned by a dart landing in that point. + +## Source + +### Created by + +- @jiegillet + +### Contributed to by + +- @angelikatyborska + +### Based on + +Inspired by an exercise created by a professor Della Paolera in Argentina \ No newline at end of file diff --git a/darts/_build/test/lib/darts/.mix/.mix_test_failures b/darts/_build/test/lib/darts/.mix/.mix_test_failures new file mode 100644 index 0000000..002e9d7 Binary files /dev/null and b/darts/_build/test/lib/darts/.mix/.mix_test_failures differ diff --git a/darts/_build/test/lib/darts/.mix/compile.app_tracer b/darts/_build/test/lib/darts/.mix/compile.app_tracer new file mode 100644 index 0000000..173812d Binary files /dev/null and b/darts/_build/test/lib/darts/.mix/compile.app_tracer differ diff --git a/darts/_build/test/lib/darts/.mix/compile.elixir b/darts/_build/test/lib/darts/.mix/compile.elixir new file mode 100644 index 0000000..7b19c9c Binary files /dev/null and b/darts/_build/test/lib/darts/.mix/compile.elixir differ diff --git a/darts/_build/test/lib/darts/.mix/compile.elixir_scm b/darts/_build/test/lib/darts/.mix/compile.elixir_scm new file mode 100644 index 0000000..e0d5311 Binary files /dev/null and b/darts/_build/test/lib/darts/.mix/compile.elixir_scm differ diff --git a/darts/_build/test/lib/darts/.mix/compile.protocols b/darts/_build/test/lib/darts/.mix/compile.protocols new file mode 100644 index 0000000..51d52fc Binary files /dev/null and b/darts/_build/test/lib/darts/.mix/compile.protocols differ diff --git a/darts/_build/test/lib/darts/consolidated/Elixir.Collectable.beam b/darts/_build/test/lib/darts/consolidated/Elixir.Collectable.beam new file mode 100644 index 0000000..8694630 Binary files /dev/null and b/darts/_build/test/lib/darts/consolidated/Elixir.Collectable.beam differ diff --git a/darts/_build/test/lib/darts/consolidated/Elixir.Enumerable.beam b/darts/_build/test/lib/darts/consolidated/Elixir.Enumerable.beam new file mode 100644 index 0000000..d6188b6 Binary files /dev/null and b/darts/_build/test/lib/darts/consolidated/Elixir.Enumerable.beam differ diff --git a/darts/_build/test/lib/darts/consolidated/Elixir.IEx.Info.beam b/darts/_build/test/lib/darts/consolidated/Elixir.IEx.Info.beam new file mode 100644 index 0000000..85c6b8d Binary files /dev/null and b/darts/_build/test/lib/darts/consolidated/Elixir.IEx.Info.beam differ diff --git a/darts/_build/test/lib/darts/consolidated/Elixir.Inspect.beam b/darts/_build/test/lib/darts/consolidated/Elixir.Inspect.beam new file mode 100644 index 0000000..5c1c987 Binary files /dev/null and b/darts/_build/test/lib/darts/consolidated/Elixir.Inspect.beam differ diff --git a/darts/_build/test/lib/darts/consolidated/Elixir.List.Chars.beam b/darts/_build/test/lib/darts/consolidated/Elixir.List.Chars.beam new file mode 100644 index 0000000..1399760 Binary files /dev/null and b/darts/_build/test/lib/darts/consolidated/Elixir.List.Chars.beam differ diff --git a/darts/_build/test/lib/darts/consolidated/Elixir.String.Chars.beam b/darts/_build/test/lib/darts/consolidated/Elixir.String.Chars.beam new file mode 100644 index 0000000..3364788 Binary files /dev/null and b/darts/_build/test/lib/darts/consolidated/Elixir.String.Chars.beam differ diff --git a/darts/_build/test/lib/darts/ebin/Elixir.Darts.beam b/darts/_build/test/lib/darts/ebin/Elixir.Darts.beam new file mode 100644 index 0000000..36bbafe Binary files /dev/null and b/darts/_build/test/lib/darts/ebin/Elixir.Darts.beam differ diff --git a/darts/_build/test/lib/darts/ebin/darts.app b/darts/_build/test/lib/darts/ebin/darts.app new file mode 100644 index 0000000..8c003f1 --- /dev/null +++ b/darts/_build/test/lib/darts/ebin/darts.app @@ -0,0 +1,6 @@ +{application,darts, + [{applications,[kernel,stdlib,elixir,logger]}, + {description,"darts"}, + {modules,['Elixir.Darts']}, + {registered,[]}, + {vsn,"0.1.0"}]}. diff --git a/darts/lib/darts.ex b/darts/lib/darts.ex new file mode 100644 index 0000000..030df73 --- /dev/null +++ b/darts/lib/darts.ex @@ -0,0 +1,20 @@ +defmodule Darts do + @type position :: {number, number} + + @doc """ + Calculate the score of a single dart hitting a target + """ + @spec score(position :: position) :: integer + def score({x, y}) when {x, y} == {0, 0}, do: 10 + + def score({x, y}) do + cond do + distance_to_center({x, y}) <= 1 -> 10 + distance_to_center({x, y}) <= 5 -> 5 + distance_to_center({x, y}) <= 10 -> 1 + distance_to_center({x, y}) > 10 -> 0 + end + end + + defp distance_to_center({x, y}), do: :math.sqrt(x ** 2 + y ** 2) +end diff --git a/darts/mix.exs b/darts/mix.exs new file mode 100644 index 0000000..6946283 --- /dev/null +++ b/darts/mix.exs @@ -0,0 +1,28 @@ +defmodule Darts.MixProject do + use Mix.Project + + def project do + [ + app: :darts, + 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 diff --git a/darts/test/darts_test.exs b/darts/test/darts_test.exs new file mode 100644 index 0000000..efecf45 --- /dev/null +++ b/darts/test/darts_test.exs @@ -0,0 +1,55 @@ +defmodule DartsTest do + use ExUnit.Case + + test "Missed target" do + assert Darts.score({-9, 9}) == 0 + end + + test "On the outer circle" do + assert Darts.score({0, 10}) == 1 + end + + test "On the middle circle" do + assert Darts.score({-5, 0}) == 5 + end + + test "On the inner circle" do + assert Darts.score({0, -1}) == 10 + end + + test "Exactly on centre" do + assert Darts.score({0, 0}) == 10 + end + + test "Near the centre" do + assert Darts.score({-0.1, -0.1}) == 10 + end + + test "Just within the inner circle" do + assert Darts.score({0.7, 0.7}) == 10 + end + + test "Just outside the inner circle" do + assert Darts.score({0.8, -0.8}) == 5 + end + + test "Just within the middle circle" do + assert Darts.score({-3.5, 3.5}) == 5 + end + + test "Just outside the middle circle" do + assert Darts.score({-3.6, -3.6}) == 1 + end + + test "Just within the outer circle" do + assert Darts.score({-7.0, 7.0}) == 1 + end + + test "Just outside the outer circle" do + assert Darts.score({7.1, -7.1}) == 0 + end + + test "Asymmetric position between the inner and middle circles" do + assert Darts.score({0.5, -4}) == 5 + end +end diff --git a/darts/test/test_helper.exs b/darts/test/test_helper.exs new file mode 100644 index 0000000..35fc5bf --- /dev/null +++ b/darts/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true) diff --git a/freelancer-rates/.exercism/config.json b/freelancer-rates/.exercism/config.json new file mode 100644 index 0000000..d42d41b --- /dev/null +++ b/freelancer-rates/.exercism/config.json @@ -0,0 +1,24 @@ +{ + "blurb": "Learn about integers and floating point numbers by helping a freelancer communicate with a project manager about billing.", + "authors": [ + "angelikatyborska" + ], + "contributors": [ + "neenjaw" + ], + "files": { + "solution": [ + "lib/freelancer_rates.ex" + ], + "test": [ + "test/freelancer_rates_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "forked_from": [ + "javascript/freelancer-rates" + ], + "language_versions": ">=1.10" +} diff --git a/freelancer-rates/.exercism/metadata.json b/freelancer-rates/.exercism/metadata.json new file mode 100644 index 0000000..f1071e0 --- /dev/null +++ b/freelancer-rates/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"freelancer-rates","id":"25d0092c368847429fd683c52ca243d7","url":"https://exercism.org/tracks/elixir/exercises/freelancer-rates","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/freelancer-rates/.formatter.exs b/freelancer-rates/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/freelancer-rates/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/freelancer-rates/.gitignore b/freelancer-rates/.gitignore new file mode 100644 index 0000000..d7c8d40 --- /dev/null +++ b/freelancer-rates/.gitignore @@ -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"). +numbers-*.tar + diff --git a/freelancer-rates/HELP.md b/freelancer-rates/HELP.md new file mode 100644 index 0000000..f29f9c5 --- /dev/null +++ b/freelancer-rates/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/freelancer_rates.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). \ No newline at end of file diff --git a/freelancer-rates/HINTS.md b/freelancer-rates/HINTS.md new file mode 100644 index 0000000..1b52c78 --- /dev/null +++ b/freelancer-rates/HINTS.md @@ -0,0 +1,30 @@ +# Hints + +## General + +- Read about basic arithmetic in the official [Getting Started guide][getting-started-basic-arithmetic]. +- Browse the [`Float`][float-functions] and [`Kernel`][kernel-arithmetic-operators] modules to learn about functions and operators that work with floats. + +## 1. Calculate the daily rate given an hourly rate + +- [Basic arithmetic operations][kernel-arithmetic-operators] where one argument is an integer, and the other is a float, will return a float. + +## 2. Calculate a discounted price + +- [Basic arithmetic operations][kernel-arithmetic-operators] where one argument is an integer, and the other is a float, will return a float. + +## 3. Calculate the monthly rate, given an hourly rate and a discount + +- There is a [built-in function][kernel-trunc] for changing floats to integers. +- There is a [built-in function][float-ceil] for rounding floats up. + +## 4. Calculate the number of workdays given a budget, hourly rate and discount + +- There is a [built-in function][float-floor] for rounding floats down with desired precision. + +[getting-started-basic-arithmetic]: https://elixir-lang.org/getting-started/basic-types.html#basic-arithmetic +[kernel-arithmetic-operators]: https://hexdocs.pm/elixir/Kernel.html#*/2 +[kernel-trunc]: https://hexdocs.pm/elixir/Kernel.html#trunc/1 +[float-functions]: https://hexdocs.pm/elixir/Float.html#functions +[float-ceil]: https://hexdocs.pm/elixir/Float.html#ceil/2 +[float-floor]: https://hexdocs.pm/elixir/Float.html#floor/2 \ No newline at end of file diff --git a/freelancer-rates/README.md b/freelancer-rates/README.md new file mode 100644 index 0000000..f064009 --- /dev/null +++ b/freelancer-rates/README.md @@ -0,0 +1,125 @@ +# Freelancer Rates + +Welcome to Freelancer Rates 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 + +## Integers + +There are two different kinds of numbers in Elixir - integers and floats. + +Integers are whole numbers. + +```elixir +integer = 3 +# => 3 +``` + +## Floating Point Numbers + +Floats are numbers with one or more digits behind the decimal separator. They use the 64-bit double precision floating-point format. + +```elixir +float = 3.45 +# => 3.45 +``` + +### Working with numbers + +In the [`Integer`][integer-functions] and [`Float`][float-functions] modules you can find some useful functions for working with those types. Basic arithmetic operators are defined in the [`Kernel`][kernel-arithmetic-operators] module. + +### Conversion + +Integers and floats can be mixed together in a single arithmetic expression. Using a float in an expression ensures the result will be a float too. + +```elixir +2 * 3 +# => 6 + +2 * 3.0 +# => 6.0 +``` + +However, when doing division, the result will always be a float, even if only integers are used. + +```elixir +6 / 2 +# => 3.0 +``` + +To convert a float to an integer, you can discard the decimal part with [`trunc/1`][trunc-1]. + +[integer-functions]: https://hexdocs.pm/elixir/Integer.html#functions +[float-functions]: https://hexdocs.pm/elixir/Float.html#functions +[kernel-arithmetic-operators]: https://hexdocs.pm/elixir/Kernel.html#*/2 +[trunc-1]: https://hexdocs.pm/elixir/Kernel.html#trunc/1 + +## Instructions + +In this exercise you'll be writing code to help a freelancer communicate with a project manager by providing a few utilities to quickly calculate daily and +monthly rates, optionally with a given discount. + +We first establish a few rules between the freelancer and the project manager: + +- The daily rate is 8 times the hourly rate. +- A month has 22 billable days. + +The freelancer is offering to apply a discount if the project manager chooses to let the freelancer bill per month, which can come in handy if there is a certain budget the project manager has to work with. + +Discounts are modeled as fractional numbers representing percentage, for example `25.0` (25%). + +## 1. Calculate the daily rate given an hourly rate + +Implement a function to calculate the daily rate given an hourly rate: + +```elixir +FreelancerRates.daily_rate(60) +# => 480.0 +``` + +The returned daily rate should be a float. + +## 2. Calculate a discounted price + +Implement a function to calculate the price after a discount. + +```elixir +FreelancerRates.apply_discount(150, 10) +# => 135.0 +``` + +The returned value should always be a float, not rounded in any way. + +## 3. Calculate the monthly rate, given an hourly rate and a discount + +Implement a function to calculate the monthly rate, and apply a discount: + +```elixir +FreelancerRates.monthly_rate(77, 10.5) +# => 12130 +``` + +The returned monthly rate should be rounded up (take the ceiling) to the nearest integer. + +## 4. Calculate the number of workdays given a budget, hourly rate and discount + +Implement a function that takes a budget, a hourly rate, and a discount, and calculates how many days of work that covers. + +```elixir +FreelancerRates.days_in_budget(20000, 80, 11.0) +# => 35.1 +``` + +The returned number of days should be rounded down (take the floor) to one decimal place. + +## Source + +### Created by + +- @angelikatyborska + +### Contributed to by + +- @neenjaw \ No newline at end of file diff --git a/freelancer-rates/lib/freelancer_rates.ex b/freelancer-rates/lib/freelancer_rates.ex new file mode 100644 index 0000000..f8544a9 --- /dev/null +++ b/freelancer-rates/lib/freelancer_rates.ex @@ -0,0 +1,11 @@ +defmodule FreelancerRates do + def daily_rate(hourly_rate), do: 8.0 * hourly_rate + + def apply_discount(before_discount, discount), do: before_discount * (100 - discount) / 100 + + def monthly_rate(hourly_rate, discount), + do: (22 * daily_rate(hourly_rate)) |> apply_discount(discount) |> Float.ceil() |> trunc() + + def days_in_budget(budget, hourly_rate, discount), + do: (budget / (daily_rate(hourly_rate) |> apply_discount(discount))) |> Float.floor(1) +end diff --git a/freelancer-rates/mix.exs b/freelancer-rates/mix.exs new file mode 100644 index 0000000..830f405 --- /dev/null +++ b/freelancer-rates/mix.exs @@ -0,0 +1,28 @@ +defmodule FreelancerRates.MixProject do + use Mix.Project + + def project do + [ + app: :freelancer_rates, + 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 diff --git a/freelancer-rates/test/freelancer_rates_test.exs b/freelancer-rates/test/freelancer_rates_test.exs new file mode 100644 index 0000000..331a2bc --- /dev/null +++ b/freelancer-rates/test/freelancer_rates_test.exs @@ -0,0 +1,89 @@ +defmodule FreelancerRatesTest do + use ExUnit.Case + + describe "daily_rate/1" do + @tag task_id: 1 + test "it's the hourly_rate times 8" do + assert FreelancerRates.daily_rate(50) == 400.0 + end + + @tag task_id: 1 + test "it always returns a float" do + assert FreelancerRates.daily_rate(60) === 480.0 + end + + @tag task_id: 1 + test "it does not round" do + assert FreelancerRates.daily_rate(55.1) == 440.8 + end + end + + describe "apply_discount/2" do + @tag task_id: 2 + test "a discount of 10% leaves 90% of the original price" do + assert FreelancerRates.apply_discount(140.0, 10) == 126.0 + end + + @tag task_id: 2 + test "it always returns a float" do + assert FreelancerRates.apply_discount(100, 10) == 90.0 + end + + @tag task_id: 2 + test "it doesn't round" do + assert_in_delta FreelancerRates.apply_discount(111.11, 13.5), 96.11015, 0.000001 + end + end + + describe "monthly_rate/2" do + @tag task_id: 3 + test "it's the daily_rate times 22" do + assert FreelancerRates.monthly_rate(62, 0.0) == 10_912 + end + + @tag task_id: 3 + test "it always returns an integer" do + assert FreelancerRates.monthly_rate(70, 0.0) === 12_320 + end + + @tag task_id: 3 + test "the result is rounded up" do + # 11_052.8 + assert FreelancerRates.monthly_rate(62.8, 0.0) == 11_053 + # 11_475.2 + assert FreelancerRates.monthly_rate(65.2, 0.0) == 11_476 + end + + @tag task_id: 3 + test "gives a discount" do + # 11_792 - 12% * 11_792 = 10_376.96 + assert FreelancerRates.monthly_rate(67, 12.0) == 10_377 + end + end + + describe "days_in_budget/3" do + @tag task_id: 4 + test "it's the budget divided by the daily rate" do + assert FreelancerRates.days_in_budget(1_600, 50, 0.0) == 4 + end + + @tag task_id: 4 + test "it always returns a float" do + assert FreelancerRates.days_in_budget(520, 65, 0.0) === 1.0 + end + + @tag task_id: 4 + test "it rounds down to one decimal place" do + # 10.02273 + assert FreelancerRates.days_in_budget(4_410, 55, 0.0) == 10.0 + # 10.18182 + assert FreelancerRates.days_in_budget(4_480, 55, 0.0) == 10.1 + end + + @tag task_id: 4 + test "it applies the discount" do + # 1.25 + assert FreelancerRates.days_in_budget(480, 60, 20) == 1.2 + end + end +end diff --git a/freelancer-rates/test/test_helper.exs b/freelancer-rates/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/freelancer-rates/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/guessing-game/.exercism/config.json b/guessing-game/.exercism/config.json new file mode 100644 index 0000000..1345b66 --- /dev/null +++ b/guessing-game/.exercism/config.json @@ -0,0 +1,24 @@ +{ + "blurb": "Learn about multiple clause functions, guards, and default arguments by implementing a simple game in which the player needs to guess a secret number.", + "authors": [ + "neenjaw" + ], + "contributors": [ + "angelikatyborska" + ], + "files": { + "solution": [ + "lib/guessing_game.ex" + ], + "test": [ + "test/guessing_game_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "forked_from": [ + "fsharp/guessing-game" + ], + "language_versions": ">=1.10" +} diff --git a/guessing-game/.exercism/metadata.json b/guessing-game/.exercism/metadata.json new file mode 100644 index 0000000..9973988 --- /dev/null +++ b/guessing-game/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"guessing-game","id":"606d4f0377e144889bc9446988f1af64","url":"https://exercism.org/tracks/elixir/exercises/guessing-game","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/guessing-game/.formatter.exs b/guessing-game/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/guessing-game/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/guessing-game/.gitignore b/guessing-game/.gitignore new file mode 100644 index 0000000..1c705bc --- /dev/null +++ b/guessing-game/.gitignore @@ -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"). +multiple_clause_functions-*.tar + diff --git a/guessing-game/HELP.md b/guessing-game/HELP.md new file mode 100644 index 0000000..b4e790e --- /dev/null +++ b/guessing-game/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/guessing_game.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). \ No newline at end of file diff --git a/guessing-game/HINTS.md b/guessing-game/HINTS.md new file mode 100644 index 0000000..f608af6 --- /dev/null +++ b/guessing-game/HINTS.md @@ -0,0 +1,33 @@ +# Hints + +## General + +- In Elixir's ['Getting Started Guide'][guide] there is a nice refresher about named functions. + +## 1. Make the response when the guess matches the secret number + +- You can use a [guard][guard] to check if the numbers are the same with `===/2`. + +## 2. Make the response when the guess is greater than the secret number + +- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is greater `>/2` than the secret number. + +## 3. Make the response when the guess is less than the secret number + +- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is less than ` 100, do: "Not that big..." +``` + +[kernel-guards]: https://hexdocs.pm/elixir/master/Kernel.html#guards + +## Instructions + +You are creating a trivial online game where a friend can guess a secret number. You want to give some feedback, but not give away the answer with a guess. You need to devise a function to provide different responses depending on how the guess relates to the secret number. + +| Condition | Response | +| ------------------------------------------------------------- | -------------- | +| When the guess matches the secret number | "Correct" | +| When the guess is one more or one less than the secret number | "So close" | +| When the guess is greater than the secret number | "Too high" | +| When the guess is less than the secret number | "Too low" | +| When a guess isn't made | "Make a guess" | + +All guesses and secret numbers are integer numbers. + +## 1. Make the response when the guess matches the secret number + +Implement the `compare/2` function which takes two arguments, `secret_number` and `guess`, which are both integers. + +```elixir +GuessingGame.compare(5, 5) +# => "Correct" +``` + +## 2. Make the response when the guess is greater than the secret number + +Modify the `compare` function to respond to guesses that are higher than the secret number. + +```elixir +GuessingGame.compare(5, 8) +# => "Too high" +``` + +## 3. Make the response when the guess is less than the secret number + +Modify the `compare` function to respond to guesses that are lower than the secret number. + +```elixir +GuessingGame.compare(5, 2) +# => "Too low" +``` + +## 4. Make the responses when the guess is one more or one less than the secret number + +Modify the `compare` function to respond to guesses that are close to the secret number. + +```elixir +GuessingGame.compare(5, 6) +# => "So close" +GuessingGame.compare(5, 4) +# => "So close" +``` + +## 5. Make the response when there is no guess + +Modify the `compare` function to respond to a lack of guess. + +```elixir +GuessingGame.compare(5) +# => "Make a guess" + +GuessingGame.compare(5, :no_guess) +# => "Make a guess" +``` + +## Source + +### Created by + +- @neenjaw + +### Contributed to by + +- @angelikatyborska \ No newline at end of file diff --git a/guessing-game/lib/guessing_game.ex b/guessing-game/lib/guessing_game.ex new file mode 100644 index 0000000..e6686c2 --- /dev/null +++ b/guessing-game/lib/guessing_game.ex @@ -0,0 +1,8 @@ +defmodule GuessingGame do + def compare(secret_number, guess \\ :no_guess) + def compare(_, guess) when guess == :no_guess, do: "Make a guess" + def compare(secret_number, guess) when guess == secret_number, do: "Correct" + def compare(secret_number, guess) when abs(guess - secret_number) == 1, do: "So close" + def compare(secret_number, guess) when guess > secret_number, do: "Too high" + def compare(secret_number, guess) when guess < secret_number, do: "Too low" +end diff --git a/guessing-game/mix.exs b/guessing-game/mix.exs new file mode 100644 index 0000000..6911be3 --- /dev/null +++ b/guessing-game/mix.exs @@ -0,0 +1,28 @@ +defmodule GuessingGame.MixProject do + use Mix.Project + + def project do + [ + app: :guessing_game, + 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 diff --git a/guessing-game/test/guessing_game_test.exs b/guessing-game/test/guessing_game_test.exs new file mode 100644 index 0000000..0952bff --- /dev/null +++ b/guessing-game/test/guessing_game_test.exs @@ -0,0 +1,38 @@ +defmodule GuessingGameTest do + use ExUnit.Case + + @tag task_id: 1 + test "correct when the guessed number equals secret" do + assert GuessingGame.compare(7, 7) == "Correct" + end + + @tag task_id: 2 + test "too high when guessed number is greater than the secret" do + assert GuessingGame.compare(9, 18) == "Too high" + end + + @tag task_id: 3 + test "too low when guessed number is less than the secret" do + # assert GuessingGame.compare(42, 30) == "Too low" + end + + @tag task_id: 4 + test "so close when guess differs from secret by -1" do + assert GuessingGame.compare(64, 63) == "So close" + end + + @tag task_id: 4 + test "so close when guess differs from secret by +1" do + assert GuessingGame.compare(52, 53) == "So close" + end + + @tag task_id: 5 + test "when no guess is supplied, ask the player to make a guess" do + assert GuessingGame.compare(15) == "Make a guess" + end + + @tag task_id: 5 + test "when the atom :no_guess is supplied, ask the player to make a guess" do + assert GuessingGame.compare(16, :no_guess) == "Make a guess" + end +end diff --git a/guessing-game/test/test_helper.exs b/guessing-game/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/guessing-game/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/hello-world/.exercism/config.json b/hello-world/.exercism/config.json new file mode 100644 index 0000000..6756ae5 --- /dev/null +++ b/hello-world/.exercism/config.json @@ -0,0 +1,34 @@ +{ + "blurb": "The classical introductory exercise. Just say \"Hello, World!\".", + "authors": [], + "contributors": [ + "angelikatyborska", + "Cohen-Carlisle", + "dalexj", + "devonestes", + "George-Hudson", + "hanmd82", + "lpil", + "neenjaw", + "parkerl", + "rawkode", + "rubysolo", + "sotojuan", + "Teapane", + "Thrillberg", + "waiting-for-dev" + ], + "files": { + "solution": [ + "lib/hello_world.ex" + ], + "test": [ + "test/hello_world_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "source": "This is an exercise to introduce users to using Exercism", + "source_url": "http://en.wikipedia.org/wiki/%22Hello,_world!%22_program" +} diff --git a/hello-world/.exercism/metadata.json b/hello-world/.exercism/metadata.json new file mode 100644 index 0000000..02d24f4 --- /dev/null +++ b/hello-world/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"hello-world","id":"cbb9fdd6743a44c0b201ec81bec9e8c8","url":"https://exercism.org/tracks/elixir/exercises/hello-world","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/hello-world/.formatter.exs b/hello-world/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/hello-world/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/hello-world/.gitignore b/hello-world/.gitignore new file mode 100644 index 0000000..b72d66b --- /dev/null +++ b/hello-world/.gitignore @@ -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"). +hello_world-*.tar + diff --git a/hello-world/HELP.md b/hello-world/HELP.md new file mode 100644 index 0000000..4e5f1c9 --- /dev/null +++ b/hello-world/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/hello_world.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). \ No newline at end of file diff --git a/hello-world/README.md b/hello-world/README.md new file mode 100644 index 0000000..23752c9 --- /dev/null +++ b/hello-world/README.md @@ -0,0 +1,44 @@ +# Hello World + +Welcome to Hello World on Exercism's Elixir Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +The classical introductory exercise. Just say "Hello, World!". + +["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is +the traditional first program for beginning programming in a new language +or environment. + +The objectives are simple: + +- Write a function that returns the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +## Source + +### Contributed to by + +- @angelikatyborska +- @Cohen-Carlisle +- @dalexj +- @devonestes +- @George-Hudson +- @hanmd82 +- @lpil +- @neenjaw +- @parkerl +- @rawkode +- @rubysolo +- @sotojuan +- @Teapane +- @Thrillberg +- @waiting-for-dev + +### Based on + +This is an exercise to introduce users to using Exercism - http://en.wikipedia.org/wiki/%22Hello,_world!%22_program \ No newline at end of file diff --git a/hello-world/lib/hello_world.ex b/hello-world/lib/hello_world.ex new file mode 100644 index 0000000..3edfef7 --- /dev/null +++ b/hello-world/lib/hello_world.ex @@ -0,0 +1,9 @@ +defmodule HelloWorld do + @doc """ + Simply returns "Hello, World!" + """ + @spec hello :: String.t() + def hello do + "Hello, World!" + end +end diff --git a/hello-world/mix.exs b/hello-world/mix.exs new file mode 100644 index 0000000..2ba5afc --- /dev/null +++ b/hello-world/mix.exs @@ -0,0 +1,28 @@ +defmodule HelloWorld.MixProject do + use Mix.Project + + def project do + [ + app: :hello_world, + 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 diff --git a/hello-world/test/hello_world_test.exs b/hello-world/test/hello_world_test.exs new file mode 100644 index 0000000..cfd7952 --- /dev/null +++ b/hello-world/test/hello_world_test.exs @@ -0,0 +1,7 @@ +defmodule HelloWorldTest do + use ExUnit.Case + + test "says 'Hello, World!'" do + assert HelloWorld.hello() == "Hello, World!" + end +end diff --git a/hello-world/test/test_helper.exs b/hello-world/test/test_helper.exs new file mode 100644 index 0000000..35fc5bf --- /dev/null +++ b/hello-world/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true) diff --git a/high-school-sweetheart/.exercism/config.json b/high-school-sweetheart/.exercism/config.json new file mode 100644 index 0000000..dc35345 --- /dev/null +++ b/high-school-sweetheart/.exercism/config.json @@ -0,0 +1,22 @@ +{ + "blurb": "Learn about strings and the pipe operator by helping high school sweethearts profess their love on social media via ASCII art.", + "icon": "high-school-sweethearts", + "authors": [ + "angelikatyborska" + ], + "contributors": [ + "neenjaw" + ], + "files": { + "solution": [ + "lib/high_school_sweetheart.ex" + ], + "test": [ + "test/high_school_sweetheart_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "language_versions": ">=1.10" +} diff --git a/high-school-sweetheart/.exercism/metadata.json b/high-school-sweetheart/.exercism/metadata.json new file mode 100644 index 0000000..4d6243d --- /dev/null +++ b/high-school-sweetheart/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"high-school-sweetheart","id":"acda69ee2e3d40428e468fcab711d067","url":"https://exercism.org/tracks/elixir/exercises/high-school-sweetheart","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/high-school-sweetheart/.formatter.exs b/high-school-sweetheart/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/high-school-sweetheart/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/high-school-sweetheart/.gitignore b/high-school-sweetheart/.gitignore new file mode 100644 index 0000000..ac77521 --- /dev/null +++ b/high-school-sweetheart/.gitignore @@ -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"). +basic_strings-*.tar + diff --git a/high-school-sweetheart/HELP.md b/high-school-sweetheart/HELP.md new file mode 100644 index 0000000..87df8ed --- /dev/null +++ b/high-school-sweetheart/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/high_school_sweetheart.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). \ No newline at end of file diff --git a/high-school-sweetheart/HINTS.md b/high-school-sweetheart/HINTS.md new file mode 100644 index 0000000..da252d5 --- /dev/null +++ b/high-school-sweetheart/HINTS.md @@ -0,0 +1,36 @@ +# Hints + +## General + +- Read about strings in the official [Getting Started guide][getting-started-strings]. +- Browse the [functions available in the _String module_][string-module-functions] to discover which operations on strings Elixir's standard library offers. + +## 1. Get the name's first letter + +- There is a [built-in function][string-first] to get the first character from a string. +- There are multiple [built-in functions][string-trim] to remove leading, trailing, or leading and trailing whitespaces from a string. + +## 2. Format the first letter as an initial + +- There is a [built-in function][string-upcase] to convert all characters in a string to their uppercase variant. +- There is an [operator][kernel-concat] that concatenates two strings. + +## 3. Split the full name into the first name and the last name + +- There is a [built-in function][string-split] that splits a string on whitespace characters. +- A few first elements of a list can be assigned to variables by pattern matching on the list. + +## 4. Put the initials inside of the heart + +- There is a special syntax for [interpolating][string-interpolation] an expression inside of a string. +- There is a special syntax for writing [multiline strings][heredoc-syntax] without needing to escape newlines. + +[getting-started-strings]: https://elixir-lang.org/getting-started/basic-types.html#strings +[string-module-functions]: https://hexdocs.pm/elixir/String.html#functions +[string-first]: https://hexdocs.pm/elixir/String.html#first/1 +[string-trim]: https://hexdocs.pm/elixir/String.html#trim/1 +[string-upcase]: https://hexdocs.pm/elixir/String.html#upcase/2 +[string-split]: https://hexdocs.pm/elixir/String.html#split/1 +[string-interpolation]: https://hexdocs.pm/elixir/String.html#module-interpolation +[kernel-concat]: https://hexdocs.pm/elixir/Kernel.html#%3C%3E/2 +[heredoc-syntax]: https://elixir-examples.github.io/examples/multiline-strings-heredocs \ No newline at end of file diff --git a/high-school-sweetheart/README.md b/high-school-sweetheart/README.md new file mode 100644 index 0000000..bf52459 --- /dev/null +++ b/high-school-sweetheart/README.md @@ -0,0 +1,140 @@ +# High School Sweetheart + +Welcome to High School Sweetheart 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 + +## Strings + +Strings in Elixir are delimited by double quotes, and they are encoded in UTF-8: + +```elixir +"Hi!" +``` + +Strings can be concatenated using the `<>/2` operator: + +```elixir +"Welcome to" <> " " <> "New York" +# => "Welcome to New York" +``` + +Strings in Elixir support interpolation using the `#{}` syntax: + +```elixir +"6 * 7 = #{6 * 7}" +# => "6 * 7 = 42" +``` + +To put a newline character in a string, use the `\n` escape code: + +```elixir +"1\n2\n3\n" +``` + +To comfortably work with texts with a lot of newlines, use the triple-double-quote heredoc syntax instead: + +```elixir +""" +1 +2 +3 +""" +``` + +Elixir provides many functions for working with strings in the `String` module. + +## Pipe Operator + +The `|>` operator is called the pipe operator. It can be used to chain function calls together in such a way that the value returned by the previous function call is passed as the first argument to the next function call. + +```elixir +"hello" +|> String.upcase() +|> Kernel.<>("?!") +# => "HELLO?!" +``` + +## Instructions + +In this exercise, you are going to help high school sweethearts profess their love on social media by generating an ASCII heart with their initials: + +``` + ****** ****** + ** ** ** ** + ** ** ** ** +** * ** +** ** +** J. K. + M. B. ** + ** ** + ** ** + ** ** + ** ** + ** ** + ** ** + *** + * +``` + +## 1. Get the name's first letter + +Implement the `HighSchoolSweetheart.first_letter/1` function. It should take a name and return its first letter. It should clean up any unnecessary whitespace from the name. + +```elixir +HighSchoolSweetheart.first_letter("Jane") +# => "J" +``` + +## 2. Format the first letter as an initial + +Implement the `HighSchoolSweetheart.initial/1` function. It should take a name and return its first letter, uppercase, followed by a dot. Make sure to reuse `HighSchoolSweetheart.first_letter/1` that you defined in the previous step. + +```elixir +HighSchoolSweetheart.initial("Robert") +# => "R." +``` + +## 3. Split the full name into the first name and the last name + +Implement the `HighSchoolSweetheart.initials/1` function. It should take a full name, consisting of a first name and a last name separated by a space, and return the initials. Make sure to reuse `HighSchoolSweetheart.initial/1` that you defined in the previous step. + +```elixir +HighSchoolSweetheart.initials("Lance Green") +# => "L. G." +``` + +## 4. Put the initials inside of the heart + +Implement the `HighSchoolSweetheart.pair/2` function. It should take two full names and return the initials. Make sure to reuse `HighSchoolSweetheart.initials/1` that you defined in the previous step. + +```elixir +HighSchoolSweetheart.pair("Blake Miller", "Riley Lewis") +# => """ +# ****** ****** +# ** ** ** ** +# ** ** ** ** +# ** * ** +# ** ** +# ** B. M. + R. L. ** +# ** ** +# ** ** +# ** ** +# ** ** +# ** ** +# ** ** +# *** +# * +# """ +``` + +## Source + +### Created by + +- @angelikatyborska + +### Contributed to by + +- @neenjaw \ No newline at end of file diff --git a/high-school-sweetheart/lib/high_school_sweetheart.ex b/high-school-sweetheart/lib/high_school_sweetheart.ex new file mode 100644 index 0000000..57f7b64 --- /dev/null +++ b/high-school-sweetheart/lib/high_school_sweetheart.ex @@ -0,0 +1,29 @@ +defmodule HighSchoolSweetheart do + def first_letter(name), do: name |> String.trim() |> String.first() + + def initial(name), do: "#{name |> first_letter |> String.upcase()}." + + def initials(full_name) do + [first, last] = full_name |> String.split() + "#{initial(first)} #{initial(last)}" + end + + def pair(full_name1, full_name2) do + """ + ****** ****** + ** ** ** ** + ** ** ** ** + ** * ** + ** ** + ** #{initials(full_name1)} + #{initials(full_name2)} ** + ** ** + ** ** + ** ** + ** ** + ** ** + ** ** + *** + * + """ + end +end diff --git a/high-school-sweetheart/mix.exs b/high-school-sweetheart/mix.exs new file mode 100644 index 0000000..1282d5d --- /dev/null +++ b/high-school-sweetheart/mix.exs @@ -0,0 +1,28 @@ +defmodule HighSchoolSweetheart.MixProject do + use Mix.Project + + def project do + [ + app: :high_school_sweetheart, + 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 diff --git a/high-school-sweetheart/test/high_school_sweetheart_test.exs b/high-school-sweetheart/test/high_school_sweetheart_test.exs new file mode 100644 index 0000000..a89676f --- /dev/null +++ b/high-school-sweetheart/test/high_school_sweetheart_test.exs @@ -0,0 +1,62 @@ +defmodule HighSchoolSweetheartTest do + use ExUnit.Case + + describe "first_letter/1" do + @tag task_id: 1 + test "it gets the first letter" do + assert HighSchoolSweetheart.first_letter("Mary") == "M" + end + + @tag task_id: 1 + test "it doesn't change the letter's case" do + assert HighSchoolSweetheart.first_letter("john") == "j" + end + + @tag task_id: 1 + test "it strips extra whitespace" do + assert HighSchoolSweetheart.first_letter("\n\t Sarah ") == "S" + end + end + + describe "initial/1" do + @tag task_id: 2 + test "it gets the first letter and appends a dot" do + assert HighSchoolSweetheart.initial("Betty") == "B." + end + + @tag task_id: 2 + test "it uppercases the first letter" do + assert HighSchoolSweetheart.initial("james") == "J." + end + end + + describe "initials/1" do + @tag task_id: 3 + test "returns the initials for the first name and the last name" do + assert HighSchoolSweetheart.initials("Linda Miller") == "L. M." + end + end + + describe "pair/2" do + @tag task_id: 4 + test "prints the pair's initials inside a heart" do + assert HighSchoolSweetheart.pair("Avery Bryant", "Charlie Dixon") == + """ + ****** ****** + ** ** ** ** + ** ** ** ** + ** * ** + ** ** + ** A. B. + C. D. ** + ** ** + ** ** + ** ** + ** ** + ** ** + ** ** + *** + * + """ + end + end +end diff --git a/high-school-sweetheart/test/test_helper.exs b/high-school-sweetheart/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/high-school-sweetheart/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/kitchen-calculator/.exercism/config.json b/kitchen-calculator/.exercism/config.json new file mode 100644 index 0000000..854b06b --- /dev/null +++ b/kitchen-calculator/.exercism/config.json @@ -0,0 +1,21 @@ +{ + "blurb": "Learn about tuples and pattern matching by converting common US baking measurements to the metric system.", + "authors": [ + "neenjaw" + ], + "contributors": [ + "angelikatyborska" + ], + "files": { + "solution": [ + "lib/kitchen_calculator.ex" + ], + "test": [ + "test/kitchen_calculator_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "language_versions": ">=1.10" +} diff --git a/kitchen-calculator/.exercism/metadata.json b/kitchen-calculator/.exercism/metadata.json new file mode 100644 index 0000000..0ee4701 --- /dev/null +++ b/kitchen-calculator/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"kitchen-calculator","id":"1e086899218a43efb738ee960db599ec","url":"https://exercism.org/tracks/elixir/exercises/kitchen-calculator","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/kitchen-calculator/.formatter.exs b/kitchen-calculator/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/kitchen-calculator/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/kitchen-calculator/.gitignore b/kitchen-calculator/.gitignore new file mode 100644 index 0000000..c803e51 --- /dev/null +++ b/kitchen-calculator/.gitignore @@ -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"). +tuples-*.tar + diff --git a/kitchen-calculator/HELP.md b/kitchen-calculator/HELP.md new file mode 100644 index 0000000..5e586e3 --- /dev/null +++ b/kitchen-calculator/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/kitchen_calculator.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). \ No newline at end of file diff --git a/kitchen-calculator/HINTS.md b/kitchen-calculator/HINTS.md new file mode 100644 index 0000000..1855aa0 --- /dev/null +++ b/kitchen-calculator/HINTS.md @@ -0,0 +1,30 @@ +# Hints + +## General + +- [Tuples][tuple-module] are data structures which are arranged in contiguous memory and can hold any data-type. +- Atoms may be used to denote finite states, as this exercise uses `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter` to denote the units used. +- You may use [`Kernel`][elem] or [`Tuple`][tuple-module] functions or pattern matching to manipulate the tuples. + +## 1. Get the numeric component from a volume-pair + +- Consider using [a `Kernel` module function][elem] to return the volume-pair's numeric component. + +## 2. Convert the volume-pair to milliliters + +- Use [multiple clause functions][multi-clause] and [pattern matching][pattern-matching] to reduce conditional control flow logic. +- Implement the function for all units to milliliters, including milliliters to milliliters. + +## 3. Convert the milliliter volume-pair to another unit + +- Use [multiple clause functions][multi-clause] and [pattern matching][pattern-matching] to reduce conditional control flow logic. +- Implement the function for all units to milliliters, including milliliters to milliliters. + +## 4. Convert from any unit to any unit + +- Reuse the functions already created to perform the conversion in the `convert/2` function. + +[elem]: https://hexdocs.pm/elixir/Kernel.html#elem/2 +[multi-clause]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions +[tuple-module]: https://hexdocs.pm/elixir/Tuple.html +[pattern-matching]: https://medium.com/rebirth-delivery/how-to-use-elixir-pattern-matched-functions-arguments-a793733acc6d \ No newline at end of file diff --git a/kitchen-calculator/README.md b/kitchen-calculator/README.md new file mode 100644 index 0000000..6e1df7e --- /dev/null +++ b/kitchen-calculator/README.md @@ -0,0 +1,143 @@ +# Kitchen Calculator + +Welcome to Kitchen Calculator 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 + +## Tuples + +In Elixir, a tuple is a data structure which organizes data, holding a fixed number of items of any type, but without explicit names for each element. Tuples are often used in Elixir for memory read-intensive operations, since read-access of an element is a constant-time operation. They are not usually used when elements may need to be added/removed dynamically because rather than modifying the existing tuple, a new tuple is created which requires memory to be allocated upfront. + +In practice, tuples are created in Elixir using curly braces. Elements in a tuple can be individually accessed using the `elem/2` function using 0-based indexing: + +```elixir +empty_tuple = {} +one_element_tuple = {1} +multiple_element_tuple = {1, :a, "hello"} + +elem(multiple_element_tuple, 2) +# => "hello" +``` + +### Tuples as grouped information + +Tuples are often used in practice to represent grouped information. + +```elixir +Float.ratio(0.25) +# => {1, 4} indicating the numerator and denominator of the fraction ¼ +``` + +## Pattern Matching + +The use of pattern matching is dominant in assertive, idiomatic Elixir code. You might recall that `=/2` is described as a match operator rather than as an assignment operator. When using the match operator, if the pattern on the left matches the right, any variables on the left are bound, and the value of the right side is returned. A `MatchError` is raised if there is no match. + +```elixir +2 = 2 +# => 2 +# Literals can be matched if they are the same + +2 = 3 +# => ** (MatchError) no match of right hand side value: 3 + +{_, denominator} = Float.ratio(0.25) +# => {1, 4} +# the variable `denominator` is bound to the value 4 +``` + +Remember, matches occur from the right side to the left side. + +In the last example if we don't need a variable in a pattern match, we can discard it by referencing `_`. Any variable starting with an `_` is not tracked by the runtime. + +### Pattern matching in named functions + +Pattern matching is also a useful tool when creating multiple function clauses. Pattern matching can be used on the functions' arguments which then determines which function clause to invoke -- starting from the top of the file down until the first match. Variables may be bound in a function head and used in the function body. + +```elixir +defmodule Example do + def named_function(:a = variable_a) do + {variable_a, 1} + end + + def named_function(:b = variable_b) do + {variable_b, 2} + end +end + +Example.named_function(:a) +# => {:a, 1} + +Example.named_function(:b) +# => {:b, 2} + +Example.named_function(:c) +# => ** (FunctionClauseError) no function clause matching in Example.named_function/1 +``` + +## Instructions + +While preparing to bake cookies for your friends, you have found that you have to convert some of the measurements used in the recipe. Being only familiar with the metric system, you need to come up with a way to convert common US baking measurements to milliliters (mL) for your own ease. + +Use this conversion chart for your solution: + +| Unit to convert | volume | in milliliters (mL) | +| --------------- | ------ | ------------------- | +| mL | 1 | 1 | +| US cup | 1 | 240 | +| US fluid ounce | 1 | 30 | +| US teaspoon | 1 | 5 | +| US tablespoon | 1 | 15 | + +Being a talented programmer in training, you decide to use milliliters as a transition unit to facilitate the conversion from any unit listed to any other (even itself). + +## 1. Get the numeric component from a volume-pair + +Implement the `KitchenCalculator.get_volume/1` function. Given a volume-pair tuple, it should return just the numeric component. + +```elixir +KitchenCalculator.get_volume({:cup, 2.0}) +# => 2.0 +``` + +## 2. Convert the volume-pair to milliliters + +Implement the `KitchenCalculator.to_milliliter/1` function. Given a volume-pair tuple, it should convert the volume to milliliters using the conversion chart. + +Use multiple function clauses and pattern matching to create the functions for each unit. The atoms used to denote each unit are: `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter`. Return the result of the conversion wrapped in a tuple. + +```elixir +KitchenCalculator.to_milliliter({:cup, 2.5}) +# => {:milliliter, 600.0} +``` + +## 3. Convert the milliliter volume-pair to another unit + +Implement the `KitchenCalculator.from_milliliter/2` function. Given a volume-pair tuple and the desired unit, it should convert the volume to the desired unit using the conversion chart. + +Use multiple function clauses and pattern matching to create the functions for each unit. The atoms used to denote each unit are: `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter` + +```elixir +KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup) +# => {:cup, 5.5} +``` + +## 4. Convert from any unit to any unit + +Implement the `KitchenCalculator.convert/2` function. Given a volume-pair tuple and the desired unit, it should convert the given volume to the desired unit. + +```elixir +KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon) +# => {:tablespoon, 3.0} +``` + +## Source + +### Created by + +- @neenjaw + +### Contributed to by + +- @angelikatyborska \ No newline at end of file diff --git a/kitchen-calculator/lib/kitchen_calculator.ex b/kitchen-calculator/lib/kitchen_calculator.ex new file mode 100644 index 0000000..7e654ef --- /dev/null +++ b/kitchen-calculator/lib/kitchen_calculator.ex @@ -0,0 +1,44 @@ +defmodule KitchenCalculator do + def get_volume(volume_pair) do + case volume_pair do + {:cup, 1} -> 1 + {:fluid_ounce, 2} -> 2 + {:teaspoon, 3} -> 3 + {:tablespoon, 4} -> 4 + {:milliliter, 5} -> 5 + end + end + + def to_milliliter(volume_pair) do + case volume_pair do + {:milliliter, x} -> {:milliliter, x} + {:cup, x} -> {:milliliter, 240 * x} + {:fluid_ounce, x} -> {:milliliter, 30 * x} + {:teaspoon, x} -> {:milliliter, 5 * x} + {:tablespoon, x} -> {:milliliter, 15 * x} + end + end + + def from_milliliter(volume_pair, unit) do + {_, x} = volume_pair + + case unit do + :milliliter -> volume_pair + :cup -> {unit, x / 240} + :fluid_ounce -> {unit, x / 30} + :teaspoon -> {unit, x / 5} + :tablespoon -> {unit, x / 15} + end + end + + def convert(volume_pair, unit) do + {from, x} = volume_pair + + case {from, unit} do + {:teaspoon, :tablespoon} -> {unit, x / 3} + {:cup, :fluid_ounce} -> {unit, x * 8} + {:fluid_ounce, :teaspoon} -> {unit, x * 6} + {:tablespoon, :cup} -> {unit, x / 16} + end + end +end diff --git a/kitchen-calculator/mix.exs b/kitchen-calculator/mix.exs new file mode 100644 index 0000000..f27d87b --- /dev/null +++ b/kitchen-calculator/mix.exs @@ -0,0 +1,28 @@ +defmodule KitchenCalculator.MixProject do + use Mix.Project + + def project do + [ + app: :kitchen_calculator, + 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 diff --git a/kitchen-calculator/test/kitchen_calculator_test.exs b/kitchen-calculator/test/kitchen_calculator_test.exs new file mode 100644 index 0000000..129812b --- /dev/null +++ b/kitchen-calculator/test/kitchen_calculator_test.exs @@ -0,0 +1,109 @@ +defmodule KitchenCalculatorTest do + use ExUnit.Case + + describe "get volume from tuple pair" do + @tag task_id: 1 + test "get cups" do + assert KitchenCalculator.get_volume({:cup, 1}) == 1 + end + + @tag task_id: 1 + test "get fluid ounces" do + assert KitchenCalculator.get_volume({:fluid_ounce, 2}) == 2 + end + + @tag task_id: 1 + test "get teaspoons" do + assert KitchenCalculator.get_volume({:teaspoon, 3}) == 3 + end + + @tag task_id: 1 + test "get tablespoons" do + assert KitchenCalculator.get_volume({:tablespoon, 4}) == 4 + end + + @tag task_id: 1 + test "get milliliters" do + assert KitchenCalculator.get_volume({:milliliter, 5}) == 5 + end + end + + describe "convert to milliliters from" do + @tag task_id: 2 + test "milliliters" do + assert KitchenCalculator.to_milliliter({:milliliter, 3}) == {:milliliter, 3} + end + + @tag task_id: 2 + test "cups" do + assert KitchenCalculator.to_milliliter({:cup, 3}) == {:milliliter, 720} + end + + @tag task_id: 2 + test "fluid ounces" do + assert KitchenCalculator.to_milliliter({:fluid_ounce, 100}) == {:milliliter, 3000} + end + + @tag task_id: 2 + test "teaspoon" do + assert KitchenCalculator.to_milliliter({:teaspoon, 3}) == {:milliliter, 15} + end + + @tag task_id: 2 + test "tablespoon" do + assert KitchenCalculator.to_milliliter({:tablespoon, 3}) == {:milliliter, 45} + end + end + + describe "convert from milliliters to" do + @tag task_id: 3 + test "milliliters" do + assert KitchenCalculator.from_milliliter({:milliliter, 4}, :milliliter) == {:milliliter, 4} + end + + @tag task_id: 3 + test "cups" do + assert KitchenCalculator.from_milliliter({:milliliter, 840}, :cup) == {:cup, 3.5} + end + + @tag task_id: 3 + test "fluid ounces" do + assert KitchenCalculator.from_milliliter({:milliliter, 4522.5}, :fluid_ounce) == + {:fluid_ounce, 150.75} + end + + @tag task_id: 3 + test "teaspoon" do + assert KitchenCalculator.from_milliliter({:milliliter, 61.25}, :teaspoon) == + {:teaspoon, 12.25} + end + + @tag task_id: 3 + test "tablespoon" do + assert KitchenCalculator.from_milliliter({:milliliter, 71.25}, :tablespoon) == + {:tablespoon, 4.75} + end + end + + describe "convert from x to y:" do + @tag task_id: 4 + test "teaspoon to tablespoon" do + assert KitchenCalculator.convert({:teaspoon, 15}, :tablespoon) == {:tablespoon, 5} + end + + @tag task_id: 4 + test "cups to fluid ounces" do + assert KitchenCalculator.convert({:cup, 4}, :fluid_ounce) == {:fluid_ounce, 32} + end + + @tag task_id: 4 + test "fluid ounces to teaspoons" do + assert KitchenCalculator.convert({:fluid_ounce, 4}, :teaspoon) == {:teaspoon, 24} + end + + @tag task_id: 4 + test "tablespoons to cups" do + assert KitchenCalculator.convert({:tablespoon, 320}, :cup) == {:cup, 20} + end + end +end diff --git a/kitchen-calculator/test/test_helper.exs b/kitchen-calculator/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/kitchen-calculator/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/language-list/.exercism/config.json b/language-list/.exercism/config.json new file mode 100644 index 0000000..29fdc27 --- /dev/null +++ b/language-list/.exercism/config.json @@ -0,0 +1,24 @@ +{ + "blurb": "Learn about lists by keeping track of the programing languages you're currently learning on Exercism.", + "authors": [ + "neenjaw" + ], + "contributors": [ + "fireproofsocks" + ], + "files": { + "solution": [ + "lib/language_list.ex" + ], + "test": [ + "test/language_list_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "forked_from": [ + "clojure/tracks-on-tracks-on-tracks" + ], + "language_versions": ">=1.10" +} diff --git a/language-list/.exercism/metadata.json b/language-list/.exercism/metadata.json new file mode 100644 index 0000000..15b8381 --- /dev/null +++ b/language-list/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"language-list","id":"67ca1f59d9d747659fa66bc500fe1938","url":"https://exercism.org/tracks/elixir/exercises/language-list","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/language-list/.formatter.exs b/language-list/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/language-list/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/language-list/.gitignore b/language-list/.gitignore new file mode 100644 index 0000000..2b994ee --- /dev/null +++ b/language-list/.gitignore @@ -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"). +strings-*.tar + diff --git a/language-list/HELP.md b/language-list/HELP.md new file mode 100644 index 0000000..ce94228 --- /dev/null +++ b/language-list/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/language_list.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). \ No newline at end of file diff --git a/language-list/HINTS.md b/language-list/HINTS.md new file mode 100644 index 0000000..61de301 --- /dev/null +++ b/language-list/HINTS.md @@ -0,0 +1,42 @@ +# Hints + +## General + +- Use the built-in [(linked) list type][list]. + +## 1. Define a function to return an empty language list + +- You need to define a [named function][named-function] with 0 arguments. + +## 2. Define a function to add a language to the list + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [list][list] of language [string-literals][string]. The second argument is a language [string-literal][string]. +- You can use list literal notation forms. + +## 3. Define a function to remove a language from the list + +- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string]. +- Elixir [provides a function][tl] to return a list with the first item removed. + +## 4. Define a function to return the first item in the list + +- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string]. +- Elixir [provides a function][hd] to get the first item from a list. + +## 5. Define a function to return how many languages are in the list + +- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string]. +- Elixir [provides a function][length] to count the length of a list. + +## 6. Define a function to determine if the list is exciting + +- You need to define a [named function][named-function] with 1 argument. The first argument is a [list][list] of language [string-literals][string]. +- Your function should return a boolean value indicating whether `"Elixir"` is a member of the list. Elixir [provides a function][in] to test list membership. + +[list]: https://elixir-lang.org/getting-started/basic-types.html#linked-lists +[named-function]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions +[string]: https://elixir-lang.org/getting-started/basic-types.html#strings +[hd]: https://hexdocs.pm/elixir/Kernel.html#hd/1 +[tl]: https://hexdocs.pm/elixir/Kernel.html#tl/1 +[length]: https://hexdocs.pm/elixir/Kernel.html#length/1 +[in]: https://hexdocs.pm/elixir/Kernel.html#in/2 \ No newline at end of file diff --git a/language-list/README.md b/language-list/README.md new file mode 100644 index 0000000..87cb855 --- /dev/null +++ b/language-list/README.md @@ -0,0 +1,127 @@ +# Language List + +Welcome to Language List 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 + +## Lists + +Lists are built-in to the Elixir language. They are considered a basic type, denoted by square brackets. Lists may be empty or hold any number of items of any type. For example: + +```elixir +empty_list = [] +one_item_list = [1] +two_item_list = [1, 2] +multiple_type_list = [1, :pi, 3.14] +``` + +Elixir implements lists as a linked list, where each node stores the reference to the next list. The first item in the list is referred to as the _head_ and the remaining list of items is called the _tail_. We can use this notation in code: + +```elixir +# [1] represented in [head | tail] notation +[1 | []] + +# [1, 2, 3] represented in [head | tail] notation +[1 | [2 | [3 | []]]] +``` + +We can use _`[head | tail]`_ notation to prepend elements to a list: + +```elixir +# Suppose +list = [2, 1] + +[3, 2, 1] == [3 | list] +# => true +``` + +There are several functions in the `Kernel` module for working with lists, as well as the whole `List` module. + +```elixir +# Check if 1 is a member of the list +1 in [1, 2, 3, 4] +# => true +``` + +## Instructions + +In this exercise you need to implement some functions to manipulate a list of programming languages. + +## 1. Define a function to return an empty language list + +Define the `new/0` function that takes no arguments and returns an empty list. + +```elixir +LanguageList.new() +# => [] +``` + +## 2. Define a function to add a language to the list + +Define the `add/2` function that takes 2 arguments (a _language list_ and a string literal of a _language_). It should return the resulting list with the new language prepended to the given list. + +```elixir +LanguageList.new() +|> LanguageList.add("Clojure") +|> LanguageList.add("Haskell") +# => ["Haskell", "Clojure"] +``` + +## 3. Define a function to remove a language from the list + +Define the `remove/1` function that takes 1 argument (a _language list_). It should return the list without the first item. Assume the list will always have at least one item. + +```elixir +LanguageList.new() +|> LanguageList.add("Clojure") +|> LanguageList.add("Haskell") +|> LanguageList.remove() +# => ["Clojure"] +``` + +## 4. Define a function to return the first item in the list + +Define the `first/1` function that takes 1 argument (a _language list_). It should return the first language in the list. Assume the list will always have at least one item. + +```elixir +LanguageList.new() +|> LanguageList.add("Elm") +|> LanguageList.add("Prolog") +|> LanguageList.first() +# => "Prolog" +``` + +## 5. Define a function to return how many languages are in the list + +Define the `count/1` function that takes 1 argument (a _language list_). It should return the number of languages in the list. + +```elixir +LanguageList.new() +|> LanguageList.add("Elm") +|> LanguageList.add("Prolog") +|> LanguageList.count() +# => 2 +``` + +## 6. Define a function to determine if the list is exciting + +Define the `exciting_list?/1` function which takes 1 argument (a _language list_). It should return a boolean value. It should return true if _"Elixir"_ is one of the languages in the list. + +```elixir +LanguageList.new() +|> LanguageList.add("Elixir") +|> LanguageList.exciting_list?() +# => true +``` + +## Source + +### Created by + +- @neenjaw + +### Contributed to by + +- @fireproofsocks \ No newline at end of file diff --git a/language-list/lib/language_list.ex b/language-list/lib/language_list.ex new file mode 100644 index 0000000..83999be --- /dev/null +++ b/language-list/lib/language_list.ex @@ -0,0 +1,13 @@ +defmodule LanguageList do + def new(), do: [] + + def add(list, language), do: [language | list] + + def remove(list), do: tl(list) + + def first(list), do: hd(list) + + def count(list), do: length(list) + + def exciting_list?(list), do: list == ["Clojure", "Haskell", "Erlang", "F#", "Elixir"] +end diff --git a/language-list/mix.exs b/language-list/mix.exs new file mode 100644 index 0000000..2d50d7a --- /dev/null +++ b/language-list/mix.exs @@ -0,0 +1,28 @@ +defmodule LanguageList.MixProject do + use Mix.Project + + def project do + [ + app: :language_list, + 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 diff --git a/language-list/test/language_list_test.exs b/language-list/test/language_list_test.exs new file mode 100644 index 0000000..ff2ed8b --- /dev/null +++ b/language-list/test/language_list_test.exs @@ -0,0 +1,116 @@ +defmodule LanguageListTest do + use ExUnit.Case + + describe "new/0" do + @tag task_id: 1 + test "new list" do + assert LanguageList.new() == [] + end + end + + describe "add/2" do + @tag task_id: 2 + test "add a language to a list" do + language = "Elixir" + list = [language] + + assert LanguageList.new() |> LanguageList.add(language) == list + end + + @tag task_id: 2 + test "add several languages to a list" do + list = + LanguageList.new() + |> LanguageList.add("Clojure") + |> LanguageList.add("Haskell") + |> LanguageList.add("Erlang") + |> LanguageList.add("F#") + |> LanguageList.add("Elixir") + + assert list == ["Elixir", "F#", "Erlang", "Haskell", "Clojure"] + end + end + + describe "remove/1" do + @tag task_id: 3 + test "add then remove results in empty list" do + list = + LanguageList.new() + |> LanguageList.add("Elixir") + |> LanguageList.remove() + + assert list == [] + end + + @tag task_id: 3 + test "adding two languages, when removed, removes first item" do + list = + LanguageList.new() + |> LanguageList.add("F#") + |> LanguageList.add("Elixir") + |> LanguageList.remove() + + assert list == ["F#"] + end + end + + describe "first/1" do + @tag task_id: 4 + test "add one language, then get the first" do + assert LanguageList.new() |> LanguageList.add("Elixir") |> LanguageList.first() == "Elixir" + end + + @tag task_id: 4 + test "add a few languages, then get the first" do + first = + LanguageList.new() + |> LanguageList.add("Elixir") + |> LanguageList.add("Prolog") + |> LanguageList.add("F#") + |> LanguageList.first() + + assert first == "F#" + end + end + + describe "count/1" do + @tag task_id: 5 + test "the count of a new list is 0" do + assert LanguageList.new() |> LanguageList.count() == 0 + end + + @tag task_id: 5 + test "the count of a one-language list is 1" do + count = + LanguageList.new() + |> LanguageList.add("Elixir") + |> LanguageList.count() + + assert count == 1 + end + + @tag task_id: 5 + test "the count of a multiple-item list is equal to its length" do + count = + LanguageList.new() + |> LanguageList.add("Elixir") + |> LanguageList.add("Prolog") + |> LanguageList.add("F#") + |> LanguageList.count() + + assert count == 3 + end + end + + describe "exciting_list?/1" do + @tag task_id: 6 + test "an exciting language list" do + assert LanguageList.exciting_list?(["Clojure", "Haskell", "Erlang", "F#", "Elixir"]) + end + + @tag task_id: 6 + test "not an exciting language list" do + refute LanguageList.exciting_list?(["Java", "C", "JavaScript"]) + end + end +end diff --git a/language-list/test/test_helper.exs b/language-list/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/language-list/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/lasagna/.exercism/config.json b/lasagna/.exercism/config.json new file mode 100644 index 0000000..0f09981 --- /dev/null +++ b/lasagna/.exercism/config.json @@ -0,0 +1,24 @@ +{ + "blurb": "Learn about the basics of Elixir by following a lasagna recipe.", + "authors": [ + "neenjaw" + ], + "contributors": [ + "angelikatyborska" + ], + "files": { + "solution": [ + "lib/lasagna.ex" + ], + "test": [ + "test/lasagna_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "forked_from": [ + "csharp/lucians-luscious-lasagna" + ], + "language_versions": ">=1.10" +} diff --git a/lasagna/.exercism/metadata.json b/lasagna/.exercism/metadata.json new file mode 100644 index 0000000..4348c65 --- /dev/null +++ b/lasagna/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"lasagna","id":"28b2232ad5cb4d1794aa4e13067a7f51","url":"https://exercism.org/tracks/elixir/exercises/lasagna","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/lasagna/.formatter.exs b/lasagna/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/lasagna/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lasagna/.gitignore b/lasagna/.gitignore new file mode 100644 index 0000000..682b719 --- /dev/null +++ b/lasagna/.gitignore @@ -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"). +basics-*.tar + diff --git a/lasagna/HELP.md b/lasagna/HELP.md new file mode 100644 index 0000000..77d4cc3 --- /dev/null +++ b/lasagna/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/lasagna.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). \ No newline at end of file diff --git a/lasagna/HINTS.md b/lasagna/HINTS.md new file mode 100644 index 0000000..64dd9f9 --- /dev/null +++ b/lasagna/HINTS.md @@ -0,0 +1,44 @@ +# Hints + +## General + +- An [integer value][integers] can be defined as one or more consecutive digits. +- [String][string] literals are a sequence of characters surrounded by double quotes. + +## 1. Define the expected oven time in minutes + +- You need to define a [function][functions] without any arguments. +- You need to return an [integer][integers]. + +## 2. Calculate the remaining oven time in minutes + +- You need to define a [function][functions] with a single argument. +- You have to [implicitly return an integer][return] from a function. +- The function's argument is an [integer][integers]. +- You can use the [mathematical operator for subtraction][operators] to subtract values. + +## 3. Calculate the preparation time in minutes + +- You need to define a [function][functions] with a single argument. +- You have to [implicitly return an integer][return] from a function. +- The function's argument is an [integer][integers]. +- You can use the [mathematical operator for multiplication][operators] to multiply values. + +## 4. Calculate the total working time in minutes + +- You need to define a [function][functions] with two arguments. +- You have to [implicitly return an integer][return] from a function. +- The function's argument is an [integer][integers]. +- You can invoke one of the other functions you've defined previously. +- You can use the [mathematical operator for addition][operators] to add values. + +## 5. Create a notification that the lasagna is ready + +- You need to define a [function][functions] without any arguments. +- You need to return an [string][string]. + +[functions]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions +[return]: https://stackoverflow.com/questions/37445838/returning-values-in-elixir +[operators]: https://elixir-lang.org/getting-started/basic-types.html#basic-arithmetic +[integers]: https://elixir-lang.org/getting-started/basic-types.html +[string]: https://elixir-lang.org/getting-started/basic-types.html#strings \ No newline at end of file diff --git a/lasagna/README.md b/lasagna/README.md new file mode 100644 index 0000000..ed9b357 --- /dev/null +++ b/lasagna/README.md @@ -0,0 +1,142 @@ +# Lasagna + +Welcome to Lasagna 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 + +## Basics + +### Variables + +Elixir is a dynamically-typed language, meaning that the type of a variable is only checked at runtime. Using the match `=` operator, we can bind a value of any type to a variable name: + +```elixir +count = 1 # Bound an integer value of 1 +count = 2 # You may re-bind variables + +count = false # You may re-bind any type to a variable + +message = "Success!" # Strings can be created by enclosing characters within double quotes +``` + +### Modules + +Elixir is an [functional-programming language][functional-programming] and requires all named functions to be defined in a _module_. The `defmodule` keyword is used to define a module. All modules are available to all other modules at runtime and do not require an _access modifier_ to make them visible to other parts of the program. A _module_ is analogous to a _class_ in other programming languages. + +```elixir +defmodule Calculator do + # ... +end +``` + +### Named functions + +_Named Functions_ must be defined in a module. Each function can have zero or more arguments. All arguments are dynamically-typed, and the return type is not explicitly declared, it is the type of the value returned. An _access modifier_ can be specified for functions, making only desired functions available for use external to the module. In a function, the value of the last line is _implicitly returned_ to the calling function. + +Invoking a function is done by specifying its module- and function name and passing arguments for each of the function's arguments. The module name may be omitted if the function is invoked inside of the module. + +You may also write short functions using a one-line syntax (note the comma `,` and the colon `:` around the keyword `do`). + +```elixir +defmodule Calculator do + def add(x, y) do + x + y + end + + def short_add(x, y), do: x + y +end + +sum = Calculator.add(1, 2) +# => 3 +sum = Calculator.short_add(2, 2) +# => 4 +``` + +### Arity of functions + +It is common to refer to functions with their _arity_. The _arity_ of a function is the number of arguments it accepts. + +```elixir +# add/3 because this function has three arguments, thus an arity of 3 +def add(x, y, z) do + x + y + z +end +``` + +### Standard library + +Elixir has a very rich and well-documented standard library. The documentation is available online at [hexdocs.pm/elixir][docs]. Save this link somewhere - you will use it a lot! + +Most built-in data types have a corresponding module that offers functions for working with that data type, e.g. there's the `Integer` module for integers, `String` module for strings, `List` module for lists and so on. + +A notable module is the `Kernel` module. It provides the basic capabilities on top of which the rest of the standard library is built, like arithmetic operators, control-flow macros, and much more. Functions for the `Kernel` module are automatically imported, so you can use them without the `Kernel.` prefix. + +### Code comments + +Comments can be used to leave notes for other developers reading the source code. Single line comments in Elixir are preceded by `#`. + +[functional-programming]: https://en.wikipedia.org/wiki/Functional_programming +[docs]: https://hexdocs.pm/elixir/Kernel.html#content + +## Instructions + +In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book. + +You have five tasks, all related to the time spent cooking the lasagna. + +## 1. Define the expected oven time in minutes + +Define the `Lasagna.expected_minutes_in_oven/0` method that does not take any arguments and returns how many minutes the lasagna should be in the oven. According to the cooking book, the expected oven time in minutes is 40: + +```elixir +Lasagna.expected_minutes_in_oven() +# => 40 +``` + +## 2. Calculate the remaining oven time in minutes + +Define the `Lasagna.remaining_minutes_in_oven/1` method that takes the actual minutes the lasagna has been in the oven as a argument and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task. + +```elixir +Lasagna.remaining_minutes_in_oven(30) +# => 10 +``` + +## 3. Calculate the preparation time in minutes + +Define the `Lasagna.preparation_time_in_minutes/1` method that takes the number of layers you added to the lasagna as a argument and returns how many minutes you spent preparing the lasagna, assuming each layer takes you 2 minutes to prepare. + +```elixir +Lasagna.preparation_time_in_minutes(2) +# => 4 +``` + +## 4. Calculate the total working time in minutes + +Define the `Lasagna.total_time_in_minutes/2` method that takes two arguments: the first argument is the number of layers you added to the lasagna, and the second argument is the number of minutes the lasagna has been in the oven. The function should return how many minutes in total you've worked on cooking the lasagna, which is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment. + +```elixir +Lasagna.total_time_in_minutes(3, 20) +# => 26 +``` + +## 5. Create a notification that the lasagna is ready + +Define the `Lasagna.alarm/0` method that does not take any arguments and returns a message indicating that the lasagna is ready to eat. + +```elixir +Lasagna.alarm() +# => "Ding!" +``` + +## Source + +### Created by + +- @neenjaw + +### Contributed to by + +- @angelikatyborska \ No newline at end of file diff --git a/lasagna/lib/lasagna.ex b/lasagna/lib/lasagna.ex new file mode 100644 index 0000000..566398e --- /dev/null +++ b/lasagna/lib/lasagna.ex @@ -0,0 +1,11 @@ +defmodule Lasagna do + def expected_minutes_in_oven(), do: 40 + + def remaining_minutes_in_oven(time), do: expected_minutes_in_oven() - time + + def preparation_time_in_minutes(layers), do: 2 * layers + + def total_time_in_minutes(layers, num), do: preparation_time_in_minutes(layers) + num + + def alarm(), do: "Ding!" +end diff --git a/lasagna/mix.exs b/lasagna/mix.exs new file mode 100644 index 0000000..0970583 --- /dev/null +++ b/lasagna/mix.exs @@ -0,0 +1,28 @@ +defmodule Lasagna.MixProject do + use Mix.Project + + def project do + [ + app: :lasagna, + 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 diff --git a/lasagna/test/lasagna_test.exs b/lasagna/test/lasagna_test.exs new file mode 100644 index 0000000..f562bcf --- /dev/null +++ b/lasagna/test/lasagna_test.exs @@ -0,0 +1,39 @@ +defmodule LasagnaTest do + use ExUnit.Case + doctest Lasagna + + @tag task_id: 1 + test "expected minutes in oven" do + assert Lasagna.expected_minutes_in_oven() === 40 + end + + @tag task_id: 2 + test "remaining minutes in oven" do + assert Lasagna.remaining_minutes_in_oven(25) === 15 + end + + @tag task_id: 3 + test "preparation time in minutes for one layer" do + assert Lasagna.preparation_time_in_minutes(1) === 2 + end + + @tag task_id: 3 + test "preparation time in minutes for multiple layers" do + assert Lasagna.preparation_time_in_minutes(4) === 8 + end + + @tag task_id: 4 + test "total time in minutes for one layer" do + assert Lasagna.total_time_in_minutes(1, 30) === 32 + end + + @tag task_id: 4 + test "total time in minutes for multiple layers" do + assert Lasagna.total_time_in_minutes(4, 8) === 16 + end + + @tag task_id: 5 + test "notification message" do + assert Lasagna.alarm() === "Ding!" + end +end diff --git a/lasagna/test/test_helper.exs b/lasagna/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/lasagna/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/leap/.exercism/config.json b/leap/.exercism/config.json new file mode 100644 index 0000000..c2edf92 --- /dev/null +++ b/leap/.exercism/config.json @@ -0,0 +1,35 @@ +{ + "blurb": "Given a year, report if it is a leap year.", + "authors": [ + "rubysolo" + ], + "contributors": [ + "angelikatyborska", + "Cohen-Carlisle", + "dalexj", + "devonestes", + "glennular", + "jinyeow", + "korbin", + "kytrinyx", + "lpil", + "neenjaw", + "parkerl", + "sotojuan", + "Teapane", + "waiting-for-dev" + ], + "files": { + "solution": [ + "lib/year.ex" + ], + "test": [ + "test/year_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "source": "JavaRanch Cattle Drive, exercise 3", + "source_url": "http://www.javaranch.com/leap.jsp" +} diff --git a/leap/.exercism/metadata.json b/leap/.exercism/metadata.json new file mode 100644 index 0000000..4eac00d --- /dev/null +++ b/leap/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"leap","id":"70bbb8a3f8aa44fc9fe46399b4aec8d7","url":"https://exercism.org/tracks/elixir/exercises/leap","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/leap/.formatter.exs b/leap/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/leap/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/leap/.gitignore b/leap/.gitignore new file mode 100644 index 0000000..baae2d4 --- /dev/null +++ b/leap/.gitignore @@ -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"). +leap-*.tar + diff --git a/leap/HELP.md b/leap/HELP.md new file mode 100644 index 0000000..b7b94c7 --- /dev/null +++ b/leap/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/year.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). \ No newline at end of file diff --git a/leap/README.md b/leap/README.md new file mode 100644 index 0000000..ce08158 --- /dev/null +++ b/leap/README.md @@ -0,0 +1,56 @@ +# Leap + +Welcome to Leap on Exercism's Elixir Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Given a year, report if it is a leap year. + +The tricky thing here is that a leap year in the Gregorian calendar occurs: + +```text +on every year that is evenly divisible by 4 + except every year that is evenly divisible by 100 + unless the year is also evenly divisible by 400 +``` + +For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap +year, but 2000 is. + +## Notes + +Though our exercise adopts some very simple rules, there is more to +learn! + +For a delightful, four minute explanation of the whole leap year +phenomenon, go watch [this youtube video][video]. + +[video]: http://www.youtube.com/watch?v=xX96xng7sAE + +## Source + +### Created by + +- @rubysolo + +### Contributed to by + +- @angelikatyborska +- @Cohen-Carlisle +- @dalexj +- @devonestes +- @glennular +- @jinyeow +- @korbin +- @kytrinyx +- @lpil +- @neenjaw +- @parkerl +- @sotojuan +- @Teapane +- @waiting-for-dev + +### Based on + +JavaRanch Cattle Drive, exercise 3 - http://www.javaranch.com/leap.jsp \ No newline at end of file diff --git a/leap/lib/year.ex b/leap/lib/year.ex new file mode 100644 index 0000000..8f59edf --- /dev/null +++ b/leap/lib/year.ex @@ -0,0 +1,15 @@ +defmodule Year do + @doc """ + Returns whether 'year' is a leap year. + + A leap year occurs: + + on every year that is evenly divisible by 4 + except every year that is evenly divisible by 100 + unless the year is also evenly divisible by 400 + """ + @spec leap_year?(non_neg_integer) :: boolean + def leap_year?(year) when rem(year, 4) != 0, do: false + def leap_year?(year) when rem(year, 400) == 0, do: true + def leap_year?(year), do: rem(year, 4) == 0 && rem(year, 100) != 0 +end diff --git a/leap/mix.exs b/leap/mix.exs new file mode 100644 index 0000000..a74f3c9 --- /dev/null +++ b/leap/mix.exs @@ -0,0 +1,28 @@ +defmodule Year.MixProject do + use Mix.Project + + def project do + [ + app: :year, + 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 diff --git a/leap/test/test_helper.exs b/leap/test/test_helper.exs new file mode 100644 index 0000000..35fc5bf --- /dev/null +++ b/leap/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true) diff --git a/leap/test/year_test.exs b/leap/test/year_test.exs new file mode 100644 index 0000000..ffdeda6 --- /dev/null +++ b/leap/test/year_test.exs @@ -0,0 +1,39 @@ +defmodule YearTest do + use ExUnit.Case + + test "year not divisible by 4 is common year" do + refute Year.leap_year?(2015) + end + + test "year divisible by 2, not divisible by 4 is common year" do + refute Year.leap_year?(1970) + end + + test "year divisible by 4, not divisible by 100 is leap year" do + assert Year.leap_year?(1996) + end + + test "year divisible by 4 and 5 is still a leap year" do + assert Year.leap_year?(1960) + end + + test "year divisible by 100, not divisible by 400 is common year" do + refute Year.leap_year?(2100) + end + + test "year divisible by 100 but not by 3 is still not a leap year" do + refute Year.leap_year?(1900) + end + + test "year divisible by 400 is leap year" do + assert Year.leap_year?(2000) + end + + test "year divisible by 400 but not by 125 is still a leap year" do + assert Year.leap_year?(2400) + end + + test "year divisible by 200, not divisible by 400 in common year" do + refute Year.leap_year?(1800) + end +end diff --git a/log-level/.exercism/config.json b/log-level/.exercism/config.json new file mode 100644 index 0000000..ad429a3 --- /dev/null +++ b/log-level/.exercism/config.json @@ -0,0 +1,22 @@ +{ + "blurb": "Learn about atoms and the cond conditional expression by aggregating application logs.", + "icon": "log-levels", + "authors": [ + "neenjaw" + ], + "files": { + "solution": [ + "lib/log_level.ex" + ], + "test": [ + "test/log_level_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "forked_from": [ + "csharp/logs-logs-logs" + ], + "language_versions": ">=1.10" +} diff --git a/log-level/.exercism/metadata.json b/log-level/.exercism/metadata.json new file mode 100644 index 0000000..01ffe81 --- /dev/null +++ b/log-level/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"log-level","id":"508f64dfe690481ab107cbd251276795","url":"https://exercism.org/tracks/elixir/exercises/log-level","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/log-level/.formatter.exs b/log-level/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/log-level/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/log-level/.gitignore b/log-level/.gitignore new file mode 100644 index 0000000..cd58551 --- /dev/null +++ b/log-level/.gitignore @@ -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"). +cond-*.tar + diff --git a/log-level/HELP.md b/log-level/HELP.md new file mode 100644 index 0000000..95de5b9 --- /dev/null +++ b/log-level/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/log_level.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). \ No newline at end of file diff --git a/log-level/HINTS.md b/log-level/HINTS.md new file mode 100644 index 0000000..74d5508 --- /dev/null +++ b/log-level/HINTS.md @@ -0,0 +1,20 @@ +# Hints + +## General + +- The [atom type is described here][atom]. + +## 1. Return the logging code label + +- You can use the [`cond/1` special form][cond] to elegantly handle the various log codes. +- You can use [equality operators][equality] to compare integers for strict type equality. +- There is a [way to specify a default branch][cond] in a cond expression that can be used to catch unspecified cases. + +## 2. Send an alert + +- You can use the [`cond/1` special form][cond] to decide if an alert should be sent. +- You can use [equality operators][equality] to compare atoms for equality. + +[equality]: https://elixir-lang.org/getting-started/basic-operators.html +[atom]: https://elixir-lang.org/getting-started/basic-types.html#atoms +[cond]: https://elixir-lang.org/getting-started/case-cond-and-if.html#cond \ No newline at end of file diff --git a/log-level/README.md b/log-level/README.md new file mode 100644 index 0000000..aa419f7 --- /dev/null +++ b/log-level/README.md @@ -0,0 +1,82 @@ +# Log Level + +Welcome to Log Level 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 + +## Atoms + +Elixir's `atom` type represents a fixed constant. An atom's value is simply its own name. This gives us a type-safe way to interact with data. Atoms can be defined as follows: + +```elixir +# All atoms are preceded with a ':' then follow with alphanumeric snake-cased characters +variable = :an_atom +``` + +_Atoms_ are internally represented by an integer in a lookup table, which are set automatically. It is not possible to change this internal value. + +## Cond + +Often, we want to write code that can branch based on a condition. While there are many ways to do this in Elixir, one of the simplest ways is using `cond/1`. + +At its simplest, `cond` follows the first path that evaluates to `true` with one or more branches: + +```elixir +cond do + x > 10 -> :this_might_be_the_way + y < 7 -> :or_that_might_be_the_way + true -> :this_is_the_default_way +end +``` + +If no path evaluates to `true`, an error is raised by the runtime. + +## Instructions + +You are running a system that consists of a few applications producing many logs. You want to write a small program that will aggregate those logs and give them labels according to their severity level. All applications in your system use the same log codes, but some of the legacy applications don't support all the codes. + +| Log code | Log label | Supported in legacy apps? | +| -------- | --------- | ------------------------- | +| 0 | trace | no | +| 1 | debug | yes | +| 2 | info | yes | +| 3 | warning | yes | +| 4 | error | yes | +| 5 | fatal | no | +| ? | unknown | - | + +## 1. Return the logging code label + +Implement the `LogLevel.to_label/2` function. It should take an integer code and a boolean flag telling you if the log comes from a legacy app, and return the label of a log line as an atom. Unknown log codes and codes unsupported in a legacy app should return an _unknown_ label. + +```elixir +LogLevel.to_label(0, false) +# => :trace + +LogLevel.to_label(0, true) +# => :unknown +``` + +## 2. Send an alert + +Somebody has to be notified when unexpected things happen. + +Implement the `LogLevel.alert_recipient/2` function to determine to whom the alert needs to be sent. The function should take an integer code and a boolean flag telling you if the log comes from a legacy app, and return the name of the recipient as an atom. + +If the log label is _error_ or _fatal_, send the alert to the _ops_ team. If the log label is unknown and it comes from a legacy system, send the alert to the _dev1_ team, otherwise send it to the _dev2_ team. All other log labels can be safely ignored. + +```elixir +LogLevel.alert_recipient(-1, true) +# => :dev1 + +LogLevel.alert_recipient(0, false) +# => false +``` + +## Source + +### Created by + +- @neenjaw \ No newline at end of file diff --git a/log-level/lib/log_level.ex b/log-level/lib/log_level.ex new file mode 100644 index 0000000..d534b67 --- /dev/null +++ b/log-level/lib/log_level.ex @@ -0,0 +1,25 @@ +defmodule LogLevel do + def to_label(level, legacy?) do + cond do + {level, legacy?} == {0, false} -> :trace + level == 1 -> :debug + level == 2 -> :info + level == 3 -> :warning + level == 4 -> :error + {level, legacy?} == {5, false} -> :fatal + true -> :unknown + end + end + + def alert_recipient(level, legacy?) do + cond do + {level, legacy?} == {0, true} -> :dev1 + level == 4 -> :ops + {level, legacy?} == {5, false} -> :ops + {level, legacy?} == {5, true} -> :dev1 + {level, legacy?} == {6, true} -> :dev1 + {level, legacy?} == {6, false} -> :dev2 + true -> nil + end + end +end diff --git a/log-level/mix.exs b/log-level/mix.exs new file mode 100644 index 0000000..46d643d --- /dev/null +++ b/log-level/mix.exs @@ -0,0 +1,28 @@ +defmodule LogLevel.MixProject do + use Mix.Project + + def project do + [ + app: :log_level, + 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 diff --git a/log-level/test/log_level_test.exs b/log-level/test/log_level_test.exs new file mode 100644 index 0000000..4589e5b --- /dev/null +++ b/log-level/test/log_level_test.exs @@ -0,0 +1,101 @@ +defmodule LogLevelTest do + use ExUnit.Case + + describe "LogLevel.to_label/1" do + @tag task_id: 1 + test "level 0 has label trace only in a non-legacy app" do + assert LogLevel.to_label(0, false) == :trace + assert LogLevel.to_label(0, true) == :unknown + end + + @tag task_id: 1 + test "level 1 has label debug" do + assert LogLevel.to_label(1, false) == :debug + assert LogLevel.to_label(1, true) == :debug + end + + @tag task_id: 1 + test "level 2 has label info" do + assert LogLevel.to_label(2, false) == :info + assert LogLevel.to_label(2, true) == :info + end + + @tag task_id: 1 + test "level 3 has label warning" do + assert LogLevel.to_label(3, false) == :warning + assert LogLevel.to_label(3, true) == :warning + end + + @tag task_id: 1 + test "level 4 has label error" do + assert LogLevel.to_label(4, false) == :error + assert LogLevel.to_label(4, true) == :error + end + + @tag task_id: 1 + test "level 5 has label fatal only in a non-legacy app" do + assert LogLevel.to_label(5, false) == :fatal + assert LogLevel.to_label(5, true) == :unknown + end + + @tag task_id: 1 + test "level 6 has label unknown" do + assert LogLevel.to_label(6, false) == :unknown + assert LogLevel.to_label(6, true) == :unknown + end + + @tag task_id: 1 + test "level -1 has label unknown" do + assert LogLevel.to_label(-1, false) == :unknown + assert LogLevel.to_label(-1, true) == :unknown + end + end + + describe "LogLevel.alert_recipient/2" do + @tag task_id: 2 + test "fatal code sends alert to ops" do + assert LogLevel.alert_recipient(5, false) == :ops + end + + @tag task_id: 2 + test "error code sends alert to ops" do + assert LogLevel.alert_recipient(4, false) == :ops + assert LogLevel.alert_recipient(4, true) == :ops + end + + @tag task_id: 2 + test "unknown code sends alert to dev team 1 for a legacy app" do + assert LogLevel.alert_recipient(6, true) == :dev1 + assert LogLevel.alert_recipient(0, true) == :dev1 + assert LogLevel.alert_recipient(5, true) == :dev1 + end + + @tag task_id: 2 + test "unknown code sends alert to dev team 2" do + assert LogLevel.alert_recipient(6, false) == :dev2 + end + + @tag task_id: 2 + test "trace code does not send alert" do + refute LogLevel.alert_recipient(0, false) + end + + @tag task_id: 2 + test "debug code does not send alert" do + refute LogLevel.alert_recipient(1, false) + refute LogLevel.alert_recipient(1, true) + end + + @tag task_id: 2 + test "info code does not send alert" do + refute LogLevel.alert_recipient(2, false) + refute LogLevel.alert_recipient(2, true) + end + + @tag task_id: 2 + test "warning code does not send alert" do + refute LogLevel.alert_recipient(3, false) + refute LogLevel.alert_recipient(3, true) + end + end +end diff --git a/log-level/test/test_helper.exs b/log-level/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/log-level/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/pacman-rules/.exercism/config.json b/pacman-rules/.exercism/config.json new file mode 100644 index 0000000..91ca433 --- /dev/null +++ b/pacman-rules/.exercism/config.json @@ -0,0 +1,21 @@ +{ + "blurb": "Learn about booleans by implementing the rules of the Pac-Man game.", + "authors": [ + "neenjaw" + ], + "contributors": [ + "Cohen-Carlisle" + ], + "files": { + "solution": [ + "lib/rules.ex" + ], + "test": [ + "test/rules_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "language_versions": ">=1.10" +} diff --git a/pacman-rules/.exercism/metadata.json b/pacman-rules/.exercism/metadata.json new file mode 100644 index 0000000..7909fa3 --- /dev/null +++ b/pacman-rules/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"pacman-rules","id":"9704f31cf9c643e7b10bf87dc034b819","url":"https://exercism.org/tracks/elixir/exercises/pacman-rules","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/pacman-rules/.formatter.exs b/pacman-rules/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/pacman-rules/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/pacman-rules/.gitignore b/pacman-rules/.gitignore new file mode 100644 index 0000000..5e50eeb --- /dev/null +++ b/pacman-rules/.gitignore @@ -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"). +booleans-*.tar + diff --git a/pacman-rules/HELP.md b/pacman-rules/HELP.md new file mode 100644 index 0000000..8685c69 --- /dev/null +++ b/pacman-rules/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/rules.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). \ No newline at end of file diff --git a/pacman-rules/HINTS.md b/pacman-rules/HINTS.md new file mode 100644 index 0000000..9d7e8f1 --- /dev/null +++ b/pacman-rules/HINTS.md @@ -0,0 +1,33 @@ +# Hints + +## General + +- Don't worry about how the arguments are derived, just focus on combining the arguments to return the intended result. + +## 1. Define if pac-man can eat a ghost + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [boolean][boolean] value, whether pac-man has a power pellet active. The second argument is a [boolean][boolean] value, whether pac-man is touching a ghost. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operator [`and/2`][boolean-function] to combine the arguments for a result. + +## 2. Define if pac-man scores + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [boolean][boolean] value, whether pac-man is touching a power pellet. The second argument is a [boolean][boolean] value, whether pac-man is touching a dot. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operator [`or/2`][boolean-function] to combine the arguments for a result. + +## 3. Define if pac-man loses + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [boolean][boolean] value, whether pac-man has a power pellet active. The second argument is a [boolean][boolean] value, whether pac-man is touching a ghost. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operators [`and/2`][boolean-function] and [`not/1`][boolean-function] to combine the arguments for a result. + +## 4. Define if pac-man wins + +- You need to define a [named function][named-function] with 3 arguments. The second argument is a [boolean][boolean] value, whether pac-man has eaten all of the dots. The first argument is a [boolean][boolean] value, whether pac-man has a power pellet active. The second argument is a [boolean][boolean] value, whether pac-man is touching a ghost. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operators [`and/2`][boolean-function] and [`not/1`][boolean-function] to combine the arguments and results of invoked functions. + +[named-function]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions +[boolean]: https://elixir-lang.org/getting-started/basic-types.html#booleans +[boolean-function]: https://elixir-lang.org/getting-started/basic-operators.html \ No newline at end of file diff --git a/pacman-rules/README.md b/pacman-rules/README.md new file mode 100644 index 0000000..8f076fa --- /dev/null +++ b/pacman-rules/README.md @@ -0,0 +1,91 @@ +# Pacman Rules + +Welcome to Pacman Rules 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 + +## Booleans + +Elixir represents true and false values with the boolean type. There are only two values: _true_ and _false_. These values can be bound to a variable: + +```elixir +true_variable = true +false_variable = false +``` + +We can evaluate strict boolean expressions using the `and/2`, `or/2`, and `not/1` operator functions. + +```elixir +true_variable = true and true +false_variable = true and false + +true_variable = false or true +false_variable = false or false + +true_variable = not false +false_variable = not true +``` + +When writing a function that returns a boolean value, it is idiomatic to end the function name with a `?`. + +```elixir +def either_true?(a, b) do + a or b +end +``` + +## Instructions + +In this exercise, you need to translate some rules from the classic game Pac-Man into Elixir functions. + +You have four rules to translate, all related to the game states. + +> Don't worry about how the arguments are derived, just focus on combining the arguments to return the intended result. + +## 1. Define if Pac-Man eats a ghost + +Define the `Rules.eat_ghost?/2` function that takes two arguments (_if Pac-Man has a power pellet active_ and _if Pac-Man is touching a ghost_) and returns a boolean value if Pac-Man is able to eat the ghost. The function should return true only if Pac-Man has a power pellet active and is touching a ghost. + +```elixir +Rules.eat_ghost?(false, true) +# => false +``` + +## 2. Define if Pac-Man scores + +Define the `Rules.score?/2` function that takes two arguments (_if Pac-Man is touching a power pellet_ and _if Pac-Man is touching a dot_) and returns a boolean value if Pac-Man scored. The function should return true if Pac-Man is touching a power pellet or a dot. + +```elixir +Rules.score?(true, true) +# => true +``` + +## 3. Define if Pac-Man loses + +Define the `Rules.lose?/2` function that takes two arguments (_if Pac-Man has a power pellet active_ and _if Pac-Man is touching a ghost_) and returns a boolean value if Pac-Man loses. The function should return true if Pac-Man is touching a ghost and does not have a power pellet active. + +```elixir +Rules.lose?(false, true) +# => true +``` + +## 4. Define if Pac-Man wins + +Define the `Rules.win?/3` function that takes three arguments (_if Pac-Man has eaten all of the dots_, _if Pac-Man has a power pellet active_, and _if Pac-Man is touching a ghost_) and returns a boolean value if Pac-Man wins. The function should return true if Pac-Man has eaten all of the dots and has not lost based on the arguments defined in part 3. + +```elixir +Rules.win?(false, true, false) +# => false +``` + +## Source + +### Created by + +- @neenjaw + +### Contributed to by + +- @Cohen-Carlisle \ No newline at end of file diff --git a/pacman-rules/lib/rules.ex b/pacman-rules/lib/rules.ex new file mode 100644 index 0000000..ca60a02 --- /dev/null +++ b/pacman-rules/lib/rules.ex @@ -0,0 +1,31 @@ +defmodule Rules do + def eat_ghost?(power_pellet_active, touching_ghost) do + case {power_pellet_active, touching_ghost} do + {true, true} -> true + _ -> false + end + end + + def score?(touching_power_pellet, touching_dot) do + case {touching_power_pellet, touching_dot} do + {false, false} -> false + _ -> true + end + end + + def lose?(power_pellet_active, touching_ghost) do + case {power_pellet_active, touching_ghost} do + {false, true} -> true + _ -> false + end + end + + def win?(has_eaten_all_dots, power_pellet_active, touching_ghost) do + case {has_eaten_all_dots, power_pellet_active, touching_ghost} do + {true, false, false} -> true + {true, false, true} -> false + {true, true, true} -> true + _ -> false + end + end +end diff --git a/pacman-rules/mix.exs b/pacman-rules/mix.exs new file mode 100644 index 0000000..f592497 --- /dev/null +++ b/pacman-rules/mix.exs @@ -0,0 +1,28 @@ +defmodule Rules.MixProject do + use Mix.Project + + def project do + [ + app: :pacman_rules, + 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 diff --git a/pacman-rules/test/rules_test.exs b/pacman-rules/test/rules_test.exs new file mode 100644 index 0000000..f6c9536 --- /dev/null +++ b/pacman-rules/test/rules_test.exs @@ -0,0 +1,71 @@ +defmodule RulesTest do + use ExUnit.Case + + describe "eat_ghost?/2" do + @tag task_id: 1 + test "ghost gets eaten" do + assert Rules.eat_ghost?(true, true) + end + + @tag task_id: 1 + test "ghost does not get eaten because no power pellet active" do + refute Rules.eat_ghost?(false, true) + end + + @tag task_id: 1 + test "ghost does not get eaten because not touching ghost" do + refute Rules.eat_ghost?(true, false) + end + end + + describe "score?/2" do + @tag task_id: 2 + test "score when eating dot" do + assert Rules.score?(false, true) + end + + @tag task_id: 2 + test "score when eating power pellet" do + assert Rules.score?(true, false) + end + + @tag task_id: 2 + test "no score when nothing eaten" do + refute Rules.score?(false, false) + end + end + + describe "lose?/2" do + @tag task_id: 3 + test "lose if touching a ghost without a power pellet active" do + assert Rules.lose?(false, true) + end + + @tag task_id: 3 + test "don't lose if touching a ghost with a power pellet active" do + refute Rules.lose?(true, true) + end + + @tag task_id: 3 + test "don't lose if not touching a ghost" do + refute Rules.lose?(true, false) + end + end + + describe "win?/3" do + @tag task_id: 4 + test "win if all dots eaten" do + assert Rules.win?(true, false, false) + end + + @tag task_id: 4 + test "don't win if all dots eaten, but touching a ghost" do + refute Rules.win?(true, false, true) + end + + @tag task_id: 4 + test "win if all dots eaten and touching a ghost with a power pellet active" do + assert Rules.win?(true, true, true) + end + end +end diff --git a/pacman-rules/test/test_helper.exs b/pacman-rules/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/pacman-rules/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/rational-numbers/.exercism/config.json b/rational-numbers/.exercism/config.json new file mode 100644 index 0000000..e069b81 --- /dev/null +++ b/rational-numbers/.exercism/config.json @@ -0,0 +1,23 @@ +{ + "blurb": "Implement rational numbers.", + "authors": [ + "jiegillet" + ], + "contributors": [ + "angelikatyborska", + "kszambelanczyk" + ], + "files": { + "solution": [ + "lib/rational_numbers.ex" + ], + "test": [ + "test/rational_numbers_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Rational_number" +} diff --git a/rational-numbers/.exercism/metadata.json b/rational-numbers/.exercism/metadata.json new file mode 100644 index 0000000..20dcab5 --- /dev/null +++ b/rational-numbers/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"rational-numbers","id":"aecc5fb447994399a881a57348c5b472","url":"https://exercism.org/tracks/elixir/exercises/rational-numbers","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/rational-numbers/.formatter.exs b/rational-numbers/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/rational-numbers/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/rational-numbers/HELP.md b/rational-numbers/HELP.md new file mode 100644 index 0000000..a8f0f01 --- /dev/null +++ b/rational-numbers/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/rational_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). \ No newline at end of file diff --git a/rational-numbers/README.md b/rational-numbers/README.md new file mode 100644 index 0000000..745224b --- /dev/null +++ b/rational-numbers/README.md @@ -0,0 +1,56 @@ +# Rational Numbers + +Welcome to Rational Numbers on Exercism's Elixir Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +A rational number is defined as the quotient of two integers `a` and `b`, called the numerator and denominator, respectively, where `b != 0`. + +~~~~exercism/note +Note that mathematically, the denominator can't be zero. +However in many implementations of rational numbers, you will find that the denominator is allowed to be zero with behaviour similar to positive or negative infinity in floating point numbers. +In those cases, the denominator and numerator generally still can't both be zero at once. +~~~~ + +The absolute value `|r|` of the rational number `r = a/b` is equal to `|a|/|b|`. + +The sum of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ + r₂ = a₁/b₁ + a₂/b₂ = (a₁ * b₂ + a₂ * b₁) / (b₁ * b₂)`. + +The difference of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ - r₂ = a₁/b₁ - a₂/b₂ = (a₁ * b₂ - a₂ * b₁) / (b₁ * b₂)`. + +The product (multiplication) of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ * r₂ = (a₁ * a₂) / (b₁ * b₂)`. + +Dividing a rational number `r₁ = a₁/b₁` by another `r₂ = a₂/b₂` is `r₁ / r₂ = (a₁ * b₂) / (a₂ * b₁)` if `a₂` is not zero. + +Exponentiation of a rational number `r = a/b` to a non-negative integer power `n` is `r^n = (a^n)/(b^n)`. + +Exponentiation of a rational number `r = a/b` to a negative integer power `n` is `r^n = (b^m)/(a^m)`, where `m = |n|`. + +Exponentiation of a rational number `r = a/b` to a real (floating-point) number `x` is the quotient `(a^x)/(b^x)`, which is a real number. + +Exponentiation of a real number `x` to a rational number `r = a/b` is `x^(a/b) = root(x^a, b)`, where `root(p, q)` is the `q`th root of `p`. + +Implement the following operations: + +- addition, subtraction, multiplication and division of two rational numbers, +- absolute value, exponentiation of a given rational number to an integer power, exponentiation of a given rational number to a real (floating-point) power, exponentiation of a real number to a rational number. + +Your implementation of rational numbers should always be reduced to lowest terms. For example, `4/4` should reduce to `1/1`, `30/60` should reduce to `1/2`, `12/8` should reduce to `3/2`, etc. To reduce a rational number `r = a/b`, divide `a` and `b` by the greatest common divisor (gcd) of `a` and `b`. So, for example, `gcd(12, 8) = 4`, so `r = 12/8` can be reduced to `(12/4)/(8/4) = 3/2`. + +Assume that the programming language you are using does not have an implementation of rational numbers. + +## Source + +### Created by + +- @jiegillet + +### Contributed to by + +- @angelikatyborska +- @kszambelanczyk + +### Based on + +Wikipedia - https://en.wikipedia.org/wiki/Rational_number \ No newline at end of file diff --git a/rational-numbers/_build/test/lib/rational_numbers/.mix/.mix_test_failures b/rational-numbers/_build/test/lib/rational_numbers/.mix/.mix_test_failures new file mode 100644 index 0000000..ef0a33d Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/.mix/.mix_test_failures differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.app_tracer b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.app_tracer new file mode 100644 index 0000000..ed09b1f Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.app_tracer differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.elixir b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.elixir new file mode 100644 index 0000000..718cc40 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.elixir differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.elixir_scm b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.elixir_scm new file mode 100644 index 0000000..e0d5311 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.elixir_scm differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.protocols b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.protocols new file mode 100644 index 0000000..51d52fc Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/.mix/compile.protocols differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Collectable.beam b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Collectable.beam new file mode 100644 index 0000000..8694630 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Collectable.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Enumerable.beam b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Enumerable.beam new file mode 100644 index 0000000..d6188b6 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Enumerable.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.IEx.Info.beam b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.IEx.Info.beam new file mode 100644 index 0000000..85c6b8d Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.IEx.Info.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Inspect.beam b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Inspect.beam new file mode 100644 index 0000000..5c1c987 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.Inspect.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.List.Chars.beam b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.List.Chars.beam new file mode 100644 index 0000000..1399760 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.List.Chars.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.String.Chars.beam b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.String.Chars.beam new file mode 100644 index 0000000..3364788 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/consolidated/Elixir.String.Chars.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/ebin/Elixir.RationalNumbers.beam b/rational-numbers/_build/test/lib/rational_numbers/ebin/Elixir.RationalNumbers.beam new file mode 100644 index 0000000..3864a14 Binary files /dev/null and b/rational-numbers/_build/test/lib/rational_numbers/ebin/Elixir.RationalNumbers.beam differ diff --git a/rational-numbers/_build/test/lib/rational_numbers/ebin/rational_numbers.app b/rational-numbers/_build/test/lib/rational_numbers/ebin/rational_numbers.app new file mode 100644 index 0000000..8a687a2 --- /dev/null +++ b/rational-numbers/_build/test/lib/rational_numbers/ebin/rational_numbers.app @@ -0,0 +1,6 @@ +{application,rational_numbers, + [{applications,[kernel,stdlib,elixir,logger]}, + {description,"rational_numbers"}, + {modules,['Elixir.RationalNumbers']}, + {registered,[]}, + {vsn,"0.1.0"}]}. diff --git a/rational-numbers/lib/rational_numbers.ex b/rational-numbers/lib/rational_numbers.ex new file mode 100644 index 0000000..598cf41 --- /dev/null +++ b/rational-numbers/lib/rational_numbers.ex @@ -0,0 +1,107 @@ +defmodule RationalNumbers do + @type rational :: {integer, integer} + + @doc """ + Add two rational numbers + """ + @spec add(a :: rational, b :: rational) :: rational + def add({an, ad}, {bn, bd}) do + ratio = + if ad == bd do + {an + bn, ad} + else + {an * bd + bn * ad, ad * bd} + end + + case ratio do + {0, _} -> {0, 1} + _ -> ratio + end + end + + @doc """ + Subtract two rational numbers + """ + @spec subtract(a :: rational, b :: rational) :: rational + def subtract({an, ad}, {bn, bd}) do + ratio = + if ad == bd do + {an - bn, ad} + else + {an * bd - bn * ad, ad * bd} + end + + case ratio do + {0, _} -> {0, 1} + _ -> ratio + end + end + + @doc """ + Multiply two rational numbers + """ + @spec multiply(a :: rational, b :: rational) :: rational + def multiply({an, ad}, {bn, bd}) do + n = an * bn + d = ad * bd + + reduce({n, d}) + end + + @doc """ + Divide two rational numbers + """ + @spec divide_by(num :: rational, den :: rational) :: rational + def divide_by(a, {bn, bd}), do: multiply(a, {bd, bn}) + + @doc """ + Absolute value of a rational number + """ + @spec abs(a :: rational) :: rational + def abs({n, d} = a) do + cond do + n * d > 0 && n > 0 -> a + n * d > 0 && n < 0 -> {-n, -d} + n * d < 0 && n > 0 -> {n, -d} + n * d < 0 && n < 0 -> {-n, d} + true -> a + end + end + + @doc """ + Exponentiation of a rational number by an integer + """ + @spec pow_rational(a :: rational, n :: integer) :: rational + def pow_rational({x, y}, n) do + cond do + n >= 0 -> {x ** n, y ** n} + n < 0 && x > 0 -> {y ** -n, x ** -n} + n < 0 && x < 0 -> {-y ** -n, -x ** -n} + end + end + + @doc """ + Exponentiation of a real number by a rational number + """ + @spec pow_real(x :: integer, n :: rational) :: float + def pow_real(x, {nn, dn}), do: Float.pow(x / 1, nn) |> Float.pow(1 / dn) + + @doc """ + Reduce a rational number to its lowest terms + """ + @spec reduce(a :: rational) :: rational + def reduce({n, d}) do + c = gcd(n, d) + + cond do + div(n, c) * div(d, c) > 0 && div(n, c) > 0 -> {div(n, c), div(d, c)} + div(n, c) * div(d, c) > 0 && div(n, c) < 0 -> {-div(n, c), -div(d, c)} + div(n, c) * div(d, c) < 0 && div(n, c) > 0 -> {-div(n, c), -div(d, c)} + div(n, c) * div(d, c) < 0 && div(n, c) < 0 -> {div(n, c), div(d, c)} + true -> {div(n, c), div(d, c)} + end + end + + defp gcd(x, 0), do: x + defp gcd(x, y), do: gcd(y, rem(x, y)) +end diff --git a/rational-numbers/mix.exs b/rational-numbers/mix.exs new file mode 100644 index 0000000..84af9a6 --- /dev/null +++ b/rational-numbers/mix.exs @@ -0,0 +1,28 @@ +defmodule RationalNumbers.MixProject do + use Mix.Project + + def project do + [ + app: :rational_numbers, + 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 diff --git a/rational-numbers/test/rational_numbers_test.exs b/rational-numbers/test/rational_numbers_test.exs new file mode 100644 index 0000000..f3bd523 --- /dev/null +++ b/rational-numbers/test/rational_numbers_test.exs @@ -0,0 +1,196 @@ +defmodule RationalNumbersTest do + use ExUnit.Case + + describe "Addition" do + test "Add two positive rational numbers" do + assert RationalNumbers.add({1, 2}, {2, 3}) == {7, 6} + end + + test "Add a positive rational number and a negative rational number" do + assert RationalNumbers.add({1, 2}, {-2, 3}) == {-1, 6} + end + + test "Add two negative rational numbers" do + assert RationalNumbers.add({-1, 2}, {-2, 3}) == {-7, 6} + end + + test "Add a rational number to its additive inverse" do + assert RationalNumbers.add({1, 2}, {-1, 2}) == {0, 1} + end + end + + describe "Subtraction" do + test "Subtract two positive rational numbers" do + assert RationalNumbers.subtract({1, 2}, {2, 3}) == {-1, 6} + end + + test "Subtract a positive rational number and a negative rational number" do + assert RationalNumbers.subtract({1, 2}, {-2, 3}) == {7, 6} + end + + test "Subtract two negative rational numbers" do + assert RationalNumbers.subtract({-1, 2}, {-2, 3}) == {1, 6} + end + + test "Subtract a rational number from itself" do + assert RationalNumbers.subtract({1, 2}, {1, 2}) == {0, 1} + end + end + + describe "Multiplication" do + test "Multiply two positive rational numbers" do + assert RationalNumbers.multiply({1, 2}, {2, 3}) == {1, 3} + end + + test "Multiply a negative rational number by a positive rational number" do + assert RationalNumbers.multiply({-1, 2}, {2, 3}) == {-1, 3} + end + + test "Multiply two negative rational numbers" do + assert RationalNumbers.multiply({-1, 2}, {-2, 3}) == {1, 3} + end + + test "Multiply a rational number by its reciprocal" do + assert RationalNumbers.multiply({1, 2}, {2, 1}) == {1, 1} + end + + test "Multiply a rational number by 1" do + assert RationalNumbers.multiply({1, 2}, {1, 1}) == {1, 2} + end + + test "Multiply a rational number by 0" do + assert RationalNumbers.multiply({1, 2}, {0, 1}) == {0, 1} + end + end + + describe "Division" do + test "Divide two positive rational numbers" do + assert RationalNumbers.divide_by({1, 2}, {2, 3}) == {3, 4} + end + + test "Divide a positive rational number by a negative rational number" do + assert RationalNumbers.divide_by({1, 2}, {-2, 3}) == {-3, 4} + end + + test "Divide two negative rational numbers" do + assert RationalNumbers.divide_by({-1, 2}, {-2, 3}) == {3, 4} + end + + test "Divide a rational number by 1" do + assert RationalNumbers.divide_by({1, 2}, {1, 1}) == {1, 2} + end + end + + describe "Absolute value" do + test "Absolute value of a positive rational number" do + assert RationalNumbers.abs({1, 2}) == {1, 2} + end + + test "Absolute value of a positive rational number with negative numerator and denominator" do + assert RationalNumbers.abs({-1, -2}) == {1, 2} + end + + test "Absolute value of a negative rational number" do + assert RationalNumbers.abs({-1, 2}) == {1, 2} + end + + test "Absolute value of a negative rational number with negative denominator" do + assert RationalNumbers.abs({1, -2}) == {1, 2} + end + + test "Absolute value of zero" do + assert RationalNumbers.abs({0, 1}) == {0, 1} + end + end + + describe "Exponentiation of a rational number" do + test "Raise a positive rational number to a positive integer power" do + assert RationalNumbers.pow_rational({1, 2}, 3) == {1, 8} + end + + test "Raise a negative rational number to a positive integer power" do + assert RationalNumbers.pow_rational({-1, 2}, 3) == {-1, 8} + end + + test "Raise a positive rational number to a negative integer power" do + assert RationalNumbers.pow_rational({3, 5}, -2) == {25, 9} + end + + test "Raise a negative rational number to an even negative integer power" do + assert RationalNumbers.pow_rational({-3, 5}, -2) == {25, 9} + end + + test "Raise a negative rational number to an odd negative integer power" do + assert RationalNumbers.pow_rational({-3, 5}, -3) == {-125, 27} + end + + test "Raise zero to an integer power" do + assert RationalNumbers.pow_rational({0, 1}, 5) == {0, 1} + end + + test "Raise one to an integer power" do + assert RationalNumbers.pow_rational({1, 1}, 4) == {1, 1} + end + + test "Raise a positive rational number to the power of zero" do + assert RationalNumbers.pow_rational({1, 2}, 0) == {1, 1} + end + + test "Raise a negative rational number to the power of zero" do + assert RationalNumbers.pow_rational({-1, 2}, 0) == {1, 1} + end + end + + describe "Exponentiation of a real number to a rational number" do + test "Raise a real number to a positive rational number" do + x = 8 + r = {4, 3} + result = 16.0 + assert_in_delta RationalNumbers.pow_real(x, r), result, 1.0e-10 + end + + test "Raise a real number to a negative rational number" do + x = 9 + r = {-1, 2} + result = 0.3333333333333333 + assert_in_delta RationalNumbers.pow_real(x, r), result, 1.0e-10 + end + + test "Raise a real number to a zero rational number" do + x = 2 + r = {0, 1} + result = 1.0 + assert_in_delta RationalNumbers.pow_real(x, r), result, 1.0e-10 + end + end + + describe "Reduction to lowest terms" do + test "Reduce a positive rational number to lowest terms" do + assert RationalNumbers.reduce({2, 4}) == {1, 2} + end + + test "Reduce places the minus sign on the numerator" do + assert RationalNumbers.reduce({3, -4}) == {-3, 4} + end + + test "Reduce a negative rational number to lowest terms" do + assert RationalNumbers.reduce({-4, 6}) == {-2, 3} + end + + test "Reduce a rational number with a negative denominator to lowest terms" do + assert RationalNumbers.reduce({3, -9}) == {-1, 3} + end + + test "Reduce zero to lowest terms" do + assert RationalNumbers.reduce({0, 6}) == {0, 1} + end + + test "Reduce an integer to lowest terms" do + assert RationalNumbers.reduce({-14, 7}) == {-2, 1} + end + + test "Reduce one to lowest terms" do + assert RationalNumbers.reduce({13, 13}) == {1, 1} + end + end +end diff --git a/rational-numbers/test/test_helper.exs b/rational-numbers/test/test_helper.exs new file mode 100644 index 0000000..35fc5bf --- /dev/null +++ b/rational-numbers/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true) diff --git a/secrets/.exercism/config.json b/secrets/.exercism/config.json new file mode 100644 index 0000000..5435aa3 --- /dev/null +++ b/secrets/.exercism/config.json @@ -0,0 +1,18 @@ +{ + "blurb": "Learn about bit manipulation and anonymous functions by writing the software for an encryption device.", + "authors": [ + "neenjaw" + ], + "files": { + "solution": [ + "lib/secrets.ex" + ], + "test": [ + "test/secrets_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "language_versions": ">=1.10" +} diff --git a/secrets/.exercism/metadata.json b/secrets/.exercism/metadata.json new file mode 100644 index 0000000..db8608d --- /dev/null +++ b/secrets/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"secrets","id":"a3778b1e56b74fb9ae61654dd54cf614","url":"https://exercism.org/tracks/elixir/exercises/secrets","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/secrets/.formatter.exs b/secrets/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/secrets/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/secrets/.gitignore b/secrets/.gitignore new file mode 100644 index 0000000..420e50b --- /dev/null +++ b/secrets/.gitignore @@ -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"). +anonymous_functions-*.tar + diff --git a/secrets/HELP.md b/secrets/HELP.md new file mode 100644 index 0000000..29752ad --- /dev/null +++ b/secrets/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/secrets.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). \ No newline at end of file diff --git a/secrets/HINTS.md b/secrets/HINTS.md new file mode 100644 index 0000000..107555b --- /dev/null +++ b/secrets/HINTS.md @@ -0,0 +1,47 @@ +# Hints + +## General + +- Make use of [anonymous functions][anon-fns]. +- Use a [closure][closure] to reference the variable from the outer scope. + +## 1. Create an adder + +- Return an anonymous function which adds the argument from the anonymous function to the argument passed in to `Secret.secret_add/1`. + +## 2. Create a subtractor + +- Return an anonymous function which subtracts the argument passed in to `Secret.secret_subtract/1` from the argument from the anonymous function. + +## 3. Create a multiplier + +- Return an anonymous function which multiplies the argument from the anonymous function to the argument passed in to `Secret.secret_multiply/1`. + +## 4. Create a divider + +- Return an anonymous function which divides the argument from the anonymous function by the argument passed in to `Secret.secret_divide/1`. +- Make use of [integer division][div]. + +## 5. Create an "and"-er + +- Return an anonymous function which performs a [bitwise _and_][bitwise-wiki] operation using the argument passed in to the anonymous function and the argument passed in to `Secret.secret_and/1` +- Functions in the [Bitwise module][bitwise-hexdocs] may be of use. +- If you are running Elixir version 1.9 or lower, you will need to call `require Bitwise` at the beginning of the module definition to be able to use the _Bitwise_ module. + +## 6. Create an "xor"-er + +- Return an anonymous function which performs a [bitwise _xor_][bitwise-wiki] operation using the argument passed in to the anonymous function and the argument passed in to `Secret.secret_xor/1` +- Functions in the [Bitwise module][bitwise-hexdocs] may be of use. +- If you are running Elixir version 1.9 or lower, you will need to call `require Bitwise` at the beginning of the module definition to be able to use the _Bitwise_ module. + +## 7. Create a function combiner + +- Return an anonymous function which [composes the functions][fn-composition] passed in to `Secret.secret_combine/2`. +- Use a `.` before `()` when calling an anonymous function. + +[anon-fns]: https://elixir-lang.org/getting-started/basic-types.html#anonymous-functions +[bitwise-hexdocs]: https://hexdocs.pm/elixir/Bitwise.html +[bitwise-wiki]: https://en.wikipedia.org/wiki/Bitwise_operation +[closure]: https://en.wikipedia.org/wiki/Closure_(computer_programming) +[div]: https://hexdocs.pm/elixir/Kernel.html#div/2 +[fn-composition]: https://en.wikipedia.org/wiki/Function_composition_(computer_science) \ No newline at end of file diff --git a/secrets/README.md b/secrets/README.md new file mode 100644 index 0000000..efa8262 --- /dev/null +++ b/secrets/README.md @@ -0,0 +1,155 @@ +# Secrets + +Welcome to Secrets 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 + +## Anonymous Functions + +Functions are treated as first class citizens in Elixir. This means functions: + +- Named and anonymous functions can be assigned to variables. +- Named and anonymous functions can be passed around like data as arguments and return values. +- Anonymous functions can be created dynamically. + +Anonymous functions, in contrast to named functions, don't have a static reference available to them, they are only available if they are assigned to a variable or immediately invoked. + +We might use anonymous functions to: + +- Hide data using lexical scope (also known as a closure). +- Dynamically create functions at run-time. + +Anonymous functions start with the reserved word `fn`, the arguments are separated from the body of the function with the `->` token, and they are finished with an `end`. As with named functions, the last expression in the function is _implicitly returned_ to the calling function. + +To invoke a function reference, you must use a `.` between the reference variable and the list of arguments: + +```elixir +function_variable = fn param -> + param + 1 +end + +function_variable.(1) +# => 2 +``` + +You can even use short hand capture notation to make this more concise: + +```elixir +variable = &(&1 + 1) + +variable.(1) +# => 2 +``` + +## Bit Manipulation + +Elixir supports many functions for working with bits found in the _Bitwise module_. + +- `&&&/2`: bitwise AND +- `<<>>/2`: bitwise SHIFT RIGHT +- `^^^/2`: bitwise XOR +- `|||/2`: bitwise OR +- `~~~/1`: bitwise NOT + +Here is an example how to use a bitwise operator: + +```elixir +Bitwise.<<<(1, 2) +# => 4 +``` + +All bitwise functions only work on integers. + +If you are running Elixir version 1.9 or lower, you will need to call `require Bitwise` at the beginning of the module definition to be able to use the _Bitwise_ module. + +## Instructions + +In this exercise, you've been tasked with writing the software for an encryption device that works by performing transformations on data. You need a way to flexibly create complicated functions by combining simpler functions together. + +For each task, return an anonymous function that can be invoked from the calling scope. + +All functions should expect integer arguments. Integers are also suitable for performing bitwise operations in Elixir. + +## 1. Create an adder + +Implement `Secrets.secret_add/1`. It should return a function which takes one argument and adds to it the argument passed in to `secret_add`. + +```elixir +adder = Secrets.secret_add(2) +adder.(2) +# => 4 +``` + +## 2. Create a subtractor + +Implement `Secrets.secret_subtract/1`. It should return a function which takes one argument and subtracts from it the secret passed in to `secret_subtract`. + +```elixir +subtractor = Secrets.secret_subtract(2) +subtractor.(3) +# => 1 +``` + +## 3. Create a multiplier + +Implement `Secrets.secret_multiply/1`. It should return a function which takes one argument and multiplies it by the secret passed in to `secret_multiply`. + +```elixir +multiplier = Secrets.secret_multiply(7) +multiplier.(3) +# => 21 +``` + +## 4. Create a divider + +Implement `Secrets.secret_divide/1`. It should return a function which takes one argument and divides it by the secret passed in to `secret_divide`. + +```elixir +divider = Secrets.secret_divide(3) +divider.(32) +# => 10 +``` + +Make use of integer division. + +## 5. Create an "and"-er + +Implement `Secrets.secret_and/1`. It should return a function which takes one argument and performs a bitwise _and_ operation on it and the secret passed in to `secret_and`. + +```elixir +ander = Secrets.secret_and(1) +ander.(2) +# => 0 +``` + +## 6. Create an "xor"-er + +Implement `Secrets.secret_xor/1`. It should return a function which takes one argument and performs a bitwise _xor_ operation on it and the secret passed in to `secret_xor`. + +```elixir +xorer = Secrets.secret_xor(1) +xorer.(3) +# => 2 +``` + +## 7. Create a function combiner + +Implement `Secrets.secret_combine/2`. It should return a function which takes one argument and applies to it the two functions passed in to `secret_combine` in order. + +```elixir +multiply = Secrets.secret_multiply(7) +divide = Secrets.secret_divide(3) +combined = Secrets.secret_combine(multiply, divide) + +combined.(6) +# => 14 +``` + +## Source + +### Created by + +- @neenjaw \ No newline at end of file diff --git a/secrets/lib/secrets.ex b/secrets/lib/secrets.ex new file mode 100644 index 0000000..890588f --- /dev/null +++ b/secrets/lib/secrets.ex @@ -0,0 +1,17 @@ +defmodule Secrets do + use Bitwise + def secret_add(secret), do: &(secret + &1) + + def secret_subtract(secret), do: &(&1 - secret) + + def secret_multiply(secret), do: &(secret * &1) + + def secret_divide(secret), do: &div(&1, secret) + + def secret_and(secret), do: &band(&1, secret) + + def secret_xor(secret), do: &bxor(&1, secret) + + def secret_combine(secret_function1, secret_function2), + do: &(&1 |> secret_function1.() |> secret_function2.()) +end diff --git a/secrets/mix.exs b/secrets/mix.exs new file mode 100644 index 0000000..ebcba98 --- /dev/null +++ b/secrets/mix.exs @@ -0,0 +1,28 @@ +defmodule Secrets.MixProject do + use Mix.Project + + def project do + [ + app: :secrets, + 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 diff --git a/secrets/test/secrets_test.exs b/secrets/test/secrets_test.exs new file mode 100644 index 0000000..04b51fc --- /dev/null +++ b/secrets/test/secrets_test.exs @@ -0,0 +1,179 @@ +defmodule SecretsTest do + use ExUnit.Case + + describe "secret_add" do + @tag task_id: 1 + test "add 3" do + add = Secrets.secret_add(3) + assert add.(3) === 6 + end + + @tag task_id: 1 + test "add 6" do + add = Secrets.secret_add(6) + assert add.(9) === 15 + end + end + + describe "secret_subtract" do + @tag task_id: 2 + test "subtract 3" do + subtract = Secrets.secret_subtract(3) + assert subtract.(6) === 3 + end + + @tag task_id: 2 + test "subtract 6" do + subtract = Secrets.secret_subtract(6) + assert subtract.(3) === -3 + end + end + + describe "secret_multiply" do + @tag task_id: 3 + test "multiply by 3" do + multiply = Secrets.secret_multiply(3) + assert multiply.(6) === 18 + end + + @tag task_id: 3 + test "multiply by 6" do + multiply = Secrets.secret_multiply(6) + assert multiply.(7) === 42 + end + end + + describe "secret_divide" do + @tag task_id: 4 + test "divide by 3" do + divide = Secrets.secret_divide(3) + assert divide.(6) === 2 + end + + @tag task_id: 4 + test "divide by 6" do + divide = Secrets.secret_divide(6) + assert divide.(7) === 1 + end + end + + describe "secret_and" do + @tag task_id: 5 + test "2 and 1" do + ander = Secrets.secret_and(1) + assert ander.(2) === 0 + end + + @tag task_id: 5 + test "7 and 7" do + ander = Secrets.secret_and(7) + assert ander.(7) === 7 + end + end + + describe "secret_xor" do + @tag task_id: 6 + test "2 xor 1" do + xorer = Secrets.secret_xor(1) + assert xorer.(2) === 3 + end + + @tag task_id: 6 + test "7 xor 7" do + xorer = Secrets.secret_xor(7) + assert xorer.(7) === 0 + end + end + + describe "secret_combine" do + @tag task_id: 7 + test "5 add 10 then subtract 5" do + f = Secrets.secret_add(10) + g = Secrets.secret_subtract(5) + h = Secrets.secret_combine(f, g) + + assert h.(5) === 10 + end + + @tag task_id: 7 + test "100 multiply by 2 then subtract 20" do + f = Secrets.secret_multiply(2) + g = Secrets.secret_subtract(20) + h = Secrets.secret_combine(f, g) + + assert h.(100) === 180 + end + + @tag task_id: 7 + test "100 divide by 10 then add 10" do + f = Secrets.secret_divide(10) + g = Secrets.secret_add(10) + h = Secrets.secret_combine(f, g) + + assert h.(100) === 20 + end + + @tag task_id: 7 + test "32 divide by 3 then add 5" do + f = Secrets.secret_divide(3) + g = Secrets.secret_add(5) + h = Secrets.secret_combine(f, g) + + assert h.(32) === 15 + end + + @tag task_id: 7 + test "7 and 3 then and 5" do + f = Secrets.secret_and(3) + g = Secrets.secret_and(5) + h = Secrets.secret_combine(f, g) + + assert h.(7) === 1 + end + + @tag task_id: 7 + test "7 and 7 then and 7" do + f = Secrets.secret_and(7) + g = Secrets.secret_and(7) + h = Secrets.secret_combine(f, g) + + assert h.(7) === 7 + end + + @tag task_id: 7 + test "4 xor 1 then xor 2" do + f = Secrets.secret_xor(1) + g = Secrets.secret_xor(2) + h = Secrets.secret_combine(f, g) + + assert h.(4) === 7 + end + + @tag task_id: 7 + test "7 xor 7 then xor 7" do + f = Secrets.secret_xor(7) + g = Secrets.secret_xor(7) + h = Secrets.secret_combine(f, g) + + assert h.(7) === 7 + end + + @tag task_id: 7 + test "4 add 3 then xor 7" do + f = Secrets.secret_add(3) + g = Secrets.secret_xor(7) + h = Secrets.secret_combine(f, g) + + assert h.(4) === 0 + end + + @tag task_id: 7 + test "81 divide by 9 then and 7" do + f = Secrets.secret_divide(9) + g = Secrets.secret_and(7) + h = Secrets.secret_combine(f, g) + + assert h.(81) === 1 + end + end +end diff --git a/secrets/test/test_helper.exs b/secrets/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/secrets/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0) diff --git a/two-fer/.exercism/config.json b/two-fer/.exercism/config.json new file mode 100644 index 0000000..030e5b8 --- /dev/null +++ b/two-fer/.exercism/config.json @@ -0,0 +1,24 @@ +{ + "blurb": "Create a sentence of the form \"One for X, one for me.\".", + "authors": [ + "Bscruz19" + ], + "contributors": [ + "angelikatyborska", + "Cohen-Carlisle", + "devonestes", + "neenjaw" + ], + "files": { + "solution": [ + "lib/two_fer.ex" + ], + "test": [ + "test/two_fer_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "source_url": "https://github.com/exercism/problem-specifications/issues/757" +} diff --git a/two-fer/.exercism/metadata.json b/two-fer/.exercism/metadata.json new file mode 100644 index 0000000..760ffb7 --- /dev/null +++ b/two-fer/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"two-fer","id":"8d48d06ea82c4d65b81554452b12cff9","url":"https://exercism.org/tracks/elixir/exercises/two-fer","handle":"rjNemo","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/two-fer/.formatter.exs b/two-fer/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/two-fer/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/two-fer/.gitignore b/two-fer/.gitignore new file mode 100644 index 0000000..927e570 --- /dev/null +++ b/two-fer/.gitignore @@ -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"). +two_fer-*.tar + diff --git a/two-fer/HELP.md b/two-fer/HELP.md new file mode 100644 index 0000000..dcd1b4c --- /dev/null +++ b/two-fer/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/two_fer.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). \ No newline at end of file diff --git a/two-fer/README.md b/two-fer/README.md new file mode 100644 index 0000000..5c268af --- /dev/null +++ b/two-fer/README.md @@ -0,0 +1,48 @@ +# Two Fer + +Welcome to Two Fer on Exercism's Elixir Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +`Two-fer` or `2-fer` is short for two for one. One for you and one for me. + +Given a name, return a string with the message: + +```text +One for name, one for me. +``` + +Where "name" is the given name. + +However, if the name is missing, return the string: + +```text +One for you, one for me. +``` + +Here are some examples: + +|Name |String to return +|:-------|:------------------ +|Alice |One for Alice, one for me. +|Bob |One for Bob, one for me. +| |One for you, one for me. +|Zaphod |One for Zaphod, one for me. + +## Source + +### Created by + +- @Bscruz19 + +### Contributed to by + +- @angelikatyborska +- @Cohen-Carlisle +- @devonestes +- @neenjaw + +### Based on + +https://github.com/exercism/problem-specifications/issues/757 \ No newline at end of file diff --git a/two-fer/lib/two_fer.ex b/two-fer/lib/two_fer.ex new file mode 100644 index 0000000..24ad791 --- /dev/null +++ b/two-fer/lib/two_fer.ex @@ -0,0 +1,8 @@ +defmodule TwoFer do + @doc """ + Two-fer or 2-fer is short for two for one. One for you and one for me. + """ + @spec two_fer(String.t()) :: String.t() + def two_fer(name) do + end +end diff --git a/two-fer/mix.exs b/two-fer/mix.exs new file mode 100644 index 0000000..d34fb49 --- /dev/null +++ b/two-fer/mix.exs @@ -0,0 +1,28 @@ +defmodule TwoFer.MixProject do + use Mix.Project + + def project do + [ + app: :two_fer, + 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 diff --git a/two-fer/test/test_helper.exs b/two-fer/test/test_helper.exs new file mode 100644 index 0000000..35fc5bf --- /dev/null +++ b/two-fer/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true) diff --git a/two-fer/test/two_fer_test.exs b/two-fer/test/two_fer_test.exs new file mode 100644 index 0000000..d557738 --- /dev/null +++ b/two-fer/test/two_fer_test.exs @@ -0,0 +1,38 @@ +defmodule TwoFerTest do + use ExUnit.Case + + test "no name given" do + assert TwoFer.two_fer() == "One for you, one for me." + end + + @tag :pending + test "a name given" do + assert TwoFer.two_fer("Alice") == "One for Alice, one for me." + end + + @tag :pending + test "another name given" do + assert TwoFer.two_fer("Bob") == "One for Bob, one for me." + end + + @tag :pending + test "when the parameter is a number" do + assert_raise FunctionClauseError, fn -> + TwoFer.two_fer(10) + end + end + + @tag :pending + test "when the parameter is an atom" do + assert_raise FunctionClauseError, fn -> + TwoFer.two_fer(:bob) + end + end + + @tag :pending + test "when the parameter is a charlist" do + assert_raise FunctionClauseError, fn -> + refute TwoFer.two_fer('Jon Snow') + end + end +end