Skip to content

[#724] New practice exercise rational-numbers #755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2398,6 +2398,22 @@
"cond"
],
"difficulty": 9
},
{
"slug": "rational-numbers",
"name": "Rational Numbers",
"uuid": "e2777a03-d890-453e-af5b-d61f60adbbf5",
"prerequisites": [
"pattern-matching",
"basics",
"integers",
"floating-point-numbers",
"guards"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the solution requires :math.pow, we have those options:

  1. Add prerequisite erlang-libraries, assuming the student will use :math.pow
  2. Add prerequisites that allow reimplementing :math.pow yourself, I'm guessing that would be either recursion or enum with ranges?
  3. Do nothing assuming most students will use Elixir 1.12 where Integer.pow and Float.pow are available

@neenjaw opinions? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think option 2 is the weakest, because Float.pow would be too hard to implement, people would search for it and find either :math.pow or the 1.12 functions.

I also think that not all students will have 1.12, that being said, one could understand basics as including basic math functions, so I don't really know which option to pick either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern when Elixir was a lot smaller was that distro's didn't update their binaries very often. I think with Elixir's growing popularity distro , I'm personally of a stance not worrying too much about supporting legacy versions. I think options 1/3 are both reasonable. If we are looking at v3 in august, by then 1.12 will have been out for a few months making it much more reasonable to assume new comers would be using it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's go with option 3. I'm convinced by:

If we are looking at v3 in august, by then 1.12 will have been out for a few months

For the record:

one could understand basics as including basic math functions

The way we defined concepts, basics includes using integer literals and basic math operators defined in the Kernel module (+, -, *, /), but using the Integer module is part of the integers concept 🙂.

],
"practices": [
"basics"
],
"difficulty": 4
}
],
"foregone": [
Expand Down
30 changes: 30 additions & 0 deletions exercises/practice/rational-numbers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Description

A rational number is defined as the quotient of two integers `a` and `b`, called the numerator and denominator, respectively, where `b != 0`.

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.

4 changes: 4 additions & 0 deletions exercises/practice/rational-numbers/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
22 changes: 22 additions & 0 deletions exercises/practice/rational-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"blurb": "Implement rational numbers.",
"authors": [
"jiegillet"
],
"contributors": [
"angelikatyborska"
],
"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"
}
59 changes: 59 additions & 0 deletions exercises/practice/rational-numbers/.meta/example.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule RationalNumbers do
@type rational :: {integer, integer}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


@doc """
Add two rational numbers
"""
@spec add(a :: rational, b :: rational) :: rational
def add({a, b}, {c, d}), do: {a * d + c * b, b * d} |> reduce

@doc """
Subtract two rational numbers
"""
@spec subtract(a :: rational, b :: rational) :: rational
def subtract(a, {x, y}), do: add(a, {-x, y})

@doc """
Multiply two rational numbers
"""
@spec multiply(a :: rational, b :: rational) :: rational
def multiply({a, b}, {c, d}), do: {a * c, b * d} |> reduce

@doc """
Divide two rational numbers
"""
@spec divide_by(num :: rational, den :: rational) :: rational
def divide_by(num, {a, b}), do: multiply(num, {b, a})

@doc """
Absolute value of a rational number
"""
@spec abs(a :: rational) :: rational
def abs({a, b}), do: {Kernel.abs(a), Kernel.abs(b)} |> reduce

@doc """
Exponentiation of a rational number by an integer
"""
@spec pow_rational(a :: rational, n :: integer) :: rational
def pow_rational({a, b}, n) when n < 0, do: pow_rational({b, a}, -n)

def pow_rational({a, b}, n),
do: {:math.pow(a, n) |> round, :math.pow(b, n) |> round} |> reduce

@doc """
Exponentiation of a real number by a rational number
"""
@spec pow_real(x :: float, n :: rational) :: float
def pow_real(x, {a, b}), do: :math.pow(x, a / b)

@doc """
Reduce a rational number to its lowest terms
"""
@spec reduce(a :: rational) :: rational
def reduce({a, b}) when b < 0, do: reduce({-a, -b})

def reduce({a, b}) do
gcd = Integer.gcd(a, b)
{div(a, gcd), div(b, gcd)}
end
end
117 changes: 117 additions & 0 deletions exercises/practice/rational-numbers/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# This is an auto-generated file. Regular comments will be removed when this
# file is regenerated. Regenerating will not touch any manually added keys,
# so comments can be added in a "comment" key.

[0ba4d988-044c-4ed5-9215-4d0bb8d0ae9f]
description = "Add two positive rational numbers"

[88ebc342-a2ac-4812-a656-7b664f718b6a]
description = "Add a positive rational number and a negative rational number"

[92ed09c2-991e-4082-a602-13557080205c]
description = "Add two negative rational numbers"

[6e58999e-3350-45fb-a104-aac7f4a9dd11]
description = "Add a rational number to its additive inverse"

[47bba350-9db1-4ab9-b412-4a7e1f72a66e]
description = "Subtract two positive rational numbers"

[93926e2a-3e82-4aee-98a7-fc33fb328e87]
description = "Subtract a positive rational number and a negative rational number"

[a965ba45-9b26-442b-bdc7-7728e4b8d4cc]
description = "Subtract two negative rational numbers"

[0df0e003-f68e-4209-8c6e-6a4e76af5058]
description = "Subtract a rational number from itself"

[34fde77a-75f4-4204-8050-8d3a937958d3]
description = "Multiply two positive rational numbers"

[6d015cf0-0ea3-41f1-93de-0b8e38e88bae]
description = "Multiply a negative rational number by a positive rational number"

[d1bf1b55-954e-41b1-8c92-9fc6beeb76fa]
description = "Multiply two negative rational numbers"

[a9b8f529-9ec7-4c79-a517-19365d779040]
description = "Multiply a rational number by its reciprocal"

[d89d6429-22fa-4368-ab04-9e01a44d3b48]
description = "Multiply a rational number by 1"

[0d95c8b9-1482-4ed7-bac9-b8694fa90145]
description = "Multiply a rational number by 0"

[1de088f4-64be-4e6e-93fd-5997ae7c9798]
description = "Divide two positive rational numbers"

[7d7983db-652a-4e66-981a-e921fb38d9a9]
description = "Divide a positive rational number by a negative rational number"

[1b434d1b-5b38-4cee-aaf5-b9495c399e34]
description = "Divide two negative rational numbers"

[d81c2ebf-3612-45a6-b4e0-f0d47812bd59]
description = "Divide a rational number by 1"

[5fee0d8e-5955-4324-acbe-54cdca94ddaa]
description = "Absolute value of a positive rational number"

[3cb570b6-c36a-4963-a380-c0834321bcaa]
description = "Absolute value of a positive rational number with negative numerator and denominator"

[6a05f9a0-1f6b-470b-8ff7-41af81773f25]
description = "Absolute value of a negative rational number"

[5d0f2336-3694-464f-8df9-f5852fda99dd]
description = "Absolute value of a negative rational number with negative denominator"

[f8e1ed4b-9dca-47fb-a01e-5311457b3118]
description = "Absolute value of zero"

[ea2ad2af-3dab-41e7-bb9f-bd6819668a84]
description = "Raise a positive rational number to a positive integer power"

[8168edd2-0af3-45b1-b03f-72c01332e10a]
description = "Raise a negative rational number to a positive integer power"

[e2f25b1d-e4de-4102-abc3-c2bb7c4591e4]
description = "Raise zero to an integer power"

[431cac50-ab8b-4d58-8e73-319d5404b762]
description = "Raise one to an integer power"

[7d164739-d68a-4a9c-b99f-dd77ce5d55e6]
description = "Raise a positive rational number to the power of zero"

[eb6bd5f5-f880-4bcd-8103-e736cb6e41d1]
description = "Raise a negative rational number to the power of zero"

[30b467dd-c158-46f5-9ffb-c106de2fd6fa]
description = "Raise a real number to a positive rational number"

[6e026bcc-be40-4b7b-ae22-eeaafc5a1789]
description = "Raise a real number to a negative rational number"

[9f866da7-e893-407f-8cd2-ee85d496eec5]
description = "Raise a real number to a zero rational number"

[0a63fbde-b59c-4c26-8237-1e0c73354d0a]
description = "Reduce a positive rational number to lowest terms"

[f87c2a4e-d29c-496e-a193-318c503e4402]
description = "Reduce a negative rational number to lowest terms"

[3b92ffc0-5b70-4a43-8885-8acee79cdaaf]
description = "Reduce a rational number with a negative denominator to lowest terms"

[c9dbd2e6-5ac0-4a41-84c1-48b645b4f663]
description = "Reduce zero to lowest terms"

[297b45ad-2054-4874-84d4-0358dc1b8887]
description = "Reduce an integer to lowest terms"

[a73a17fe-fe8c-4a1c-a63b-e7579e333d9e]
description = "Reduce one to lowest terms"
59 changes: 59 additions & 0 deletions exercises/practice/rational-numbers/lib/rational_numbers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule RationalNumbers do
@type rational :: {integer, integer}

@doc """
Add two rational numbers
"""
@spec add(a :: rational, b :: rational) :: rational
def add(a, b) do
end

@doc """
Subtract two rational numbers
"""
@spec subtract(a :: rational, b :: rational) :: rational
def subtract(a, b) do
end

@doc """
Multiply two rational numbers
"""
@spec multiply(a :: rational, b :: rational) :: rational
def multiply(a, b) do
end

@doc """
Divide two rational numbers
"""
@spec divide_by(num :: rational, den :: rational) :: rational
def divide_by(num, den) do
end

@doc """
Absolute value of a rational number
"""
@spec abs(a :: rational) :: rational
def abs(a) do
end

@doc """
Exponentiation of a rational number by an integer
"""
@spec pow_rational(a :: rational, n :: integer) :: rational
def pow_rational(a, n) do
end

@doc """
Exponentiation of a real number by a rational number
"""
@spec pow_real(x :: float, n :: rational) :: float
def pow_real(x, n) do
end

@doc """
Reduce a rational number to its lowest terms
"""
@spec reduce(a :: rational) :: rational
def reduce(a) do
end
end
28 changes: 28 additions & 0 deletions exercises/practice/rational-numbers/mix.exs
Original file line number Diff line number Diff line change
@@ -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
Loading