Skip to content
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
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,14 @@
"logic"
]
},
{
"slug": "rational-numbers",
"name": "Rational Numbers",
"uuid": "c129f1a1-851b-4261-8da3-44224cc5564d",
"practices": [],
"prerequisites": [],
"difficulty": 4
},
{
"slug": "resistor-color-trio",
"name": "Resistor Color Trio",
Expand Down
42 changes: 42 additions & 0 deletions exercises/practice/rational-numbers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 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`.
The reduced form of a rational number should be in "standard form" (the denominator should always be a positive integer).
If a denominator with a negative integer is present, multiply both numerator and denominator by `-1` to ensure standard form is reached.
For example, `3/-4` should be reduced to `-3/4`

Assume that the programming language you are using does not have an implementation of rational numbers.
19 changes: 19 additions & 0 deletions exercises/practice/rational-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"IsaacG"
],
"files": {
"solution": [
"rational_numbers.go"
],
"test": [
"rational_numbers_test.go"
],
"example": [
".meta/example.go"
]
},
"blurb": "Implement rational numbers.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Rational_number"
}
73 changes: 73 additions & 0 deletions exercises/practice/rational-numbers/.meta/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package rationalnumbers

import "math"

func abs(a int) int {
if a < 0 {
return -a
}
return a
}

func pow(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
}

// gcd calculates the Greatest Common Divisor of two integers using the Euclidean algorithm.
func gcd(a, b int) int {
for b != 0 {
a, b = b, a%b // This swaps a and b and updates b with the remainder
}
return a
}

type Rational struct {
numerator, denominator int
}

// Reduce simplifies a Rational, eg changing Rational{4, 2} into Rational{2, 1}.
func (r Rational) Reduce() Rational {
gcd := abs(gcd(r.numerator, r.denominator))
if r.denominator < 0 {
gcd *= -1
}
return Rational{r.numerator / gcd, r.denominator / gcd}
}

func (r Rational) Add(s Rational) Rational {
numerator := r.numerator*s.denominator + s.numerator*r.denominator
denominator := r.denominator * s.denominator
return Rational{numerator, denominator}.Reduce()
}

func (r Rational) Sub(s Rational) Rational {
numerator := r.numerator*s.denominator - s.numerator*r.denominator
denominator := r.denominator * s.denominator
return Rational{numerator, denominator}.Reduce()
}

func (r Rational) Mul(s Rational) Rational {
return Rational{r.numerator * s.numerator, r.denominator * s.denominator}.Reduce()
}

func (r Rational) Div(s Rational) Rational {
return Rational{r.numerator * s.denominator, s.numerator * r.denominator}.Reduce()
}

func (r Rational) Abs() Rational {
return Rational{abs(r.numerator), abs(r.denominator)}.Reduce()
}

// Compute r ^ power, a rational raised to an int exponent.
func (r Rational) Exprational(power int) Rational {
if power >= 0 {
return Rational{pow(r.numerator, power), pow(r.denominator, power)}.Reduce()
}
power = -power
return Rational{pow(r.denominator, power), pow(r.numerator, power)}.Reduce()
}

// Compute base ^ r, an int raised to a rational.
func (r Rational) Expreal(base int) float64 {
return math.Pow(math.Pow(float64(base), float64(r.numerator)), 1/float64(r.denominator))
}
186 changes: 186 additions & 0 deletions exercises/practice/rational-numbers/.meta/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package main

import (
"../../../../gen"
"fmt"
"log"
"text/template"
)

func main() {
t, err := template.New("").Parse(tmpl)
if err != nil {
log.Fatal(err)
}
j := map[string]any{
"abs": &[]testCaseUnaryRationalToRational{},
"add": &[]testCaseBinaryRationalToRational{},
"div": &[]testCaseBinaryRationalToRational{},
"mul": &[]testCaseBinaryRationalToRational{},
"sub": &[]testCaseBinaryRationalToRational{},
"exprational": &[]testCaseExprational{},
"expreal": &[]testCaseExpreal{},
"reduce": &[]testCaseUnaryRationalToRational{},
}
if err := gen.Gen("rational-numbers", j, t); err != nil {
log.Fatal(err)
}
}

type Rational [2]int

func (r Rational) String() string {
return fmt.Sprintf("Rational{%d, %d}", r[0], r[1])
}

type RationalPair struct {
R1 Rational `json:"r1"`
R2 Rational `json:"r2"`
}

func (r RationalPair) String() string {
return fmt.Sprintf("[2]Rational{{%d, %d}, {%d, %d}}", r.R1[0], r.R1[1], r.R2[0], r.R2[1])
}

type testCaseUnaryRationalToRational struct {
Description string `json:"description"`
Input struct {
R Rational `json:"r"`
} `json:"input"`
Expected Rational `json:"expected"`
}

type testCaseBinaryRationalToRational struct {
Description string `json:"description"`
Input RationalPair `json:"input"`
Expected Rational `json:"expected"`
}

type testCaseExprational struct {
Description string `json:"description"`
Input struct {
R Rational `json:"r"`
N int `json:"n"`
} `json:"input"`
Expected Rational `json:"expected"`
}

type testCaseExpreal struct {
Description string `json:"description"`
Input struct {
X int `json:"x"`
R Rational `json:"r"`
} `json:"input"`
Expected float64 `json:"expected"`
}

var tmpl = `{{.Header}}

var testCasesAbs = []struct {
description string
input Rational
expected Rational
}{
{{range .J.abs}}{
description: {{printf "%q" .Description}},
input: {{.Input.R}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesAdd = []struct {
description string
input [2]Rational
expected Rational
}{
{{range .J.add}}{
description: {{printf "%q" .Description}},
input: {{.Input}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesSub = []struct {
description string
input [2]Rational
expected Rational
}{
{{range .J.sub}}{
description: {{printf "%q" .Description}},
input: {{.Input}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesMul = []struct {
description string
input [2]Rational
expected Rational
}{
{{range .J.mul}}{
description: {{printf "%q" .Description}},
input: {{.Input}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesDiv = []struct {
description string
input [2]Rational
expected Rational
}{
{{range .J.div}}{
description: {{printf "%q" .Description}},
input: {{.Input}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesExprational = []struct {
description string
inputR Rational
inputInt int
expected Rational
}{
{{range .J.exprational}}{
description: {{printf "%q" .Description}},
inputR: {{.Input.R}},
inputInt: {{.Input.N}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesExpreal = []struct {
description string
inputInt int
inputR Rational
expected float64
}{
{{range .J.expreal}}{
description: {{printf "%q" .Description}},
inputInt: {{.Input.X}},
inputR: {{.Input.R}},
expected: {{.Expected}},
},
{{end}}
}

var testCasesReduce = []struct {
description string
input Rational
expected Rational
}{
{{range .J.reduce}}{
description: {{printf "%q" .Description}},
input: {{.Input.R}},
expected: {{.Expected}},
},
{{end}}
}
`
Loading
Loading