Skip to content

Commit cbf9abd

Browse files
committed
[rational-numbers] Add new practice exercise
1 parent 617dc8f commit cbf9abd

10 files changed

Lines changed: 948 additions & 0 deletions

File tree

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,14 @@
15511551
"logic"
15521552
]
15531553
},
1554+
{
1555+
"slug": "rational-numbers",
1556+
"name": "Rational Numbers",
1557+
"uuid": "c129f1a1-851b-4261-8da3-44224cc5564d",
1558+
"practices": [],
1559+
"prerequisites": [],
1560+
"difficulty": 4
1561+
},
15541562
{
15551563
"slug": "resistor-color-trio",
15561564
"name": "Resistor Color Trio",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Instructions
2+
3+
A rational number is defined as the quotient of two integers `a` and `b`, called the numerator and denominator, respectively, where `b != 0`.
4+
5+
~~~~exercism/note
6+
Note that mathematically, the denominator can't be zero.
7+
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.
8+
In those cases, the denominator and numerator generally still can't both be zero at once.
9+
~~~~
10+
11+
The absolute value `|r|` of the rational number `r = a/b` is equal to `|a|/|b|`.
12+
13+
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₂)`.
14+
15+
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₂)`.
16+
17+
The product (multiplication) of two rational numbers `r₁ = a₁/b₁` and `r₂ = a₂/b₂` is `r₁ * r₂ = (a₁ * a₂) / (b₁ * b₂)`.
18+
19+
Dividing a rational number `r₁ = a₁/b₁` by another `r₂ = a₂/b₂` is `r₁ / r₂ = (a₁ * b₂) / (a₂ * b₁)` if `a₂` is not zero.
20+
21+
Exponentiation of a rational number `r = a/b` to a non-negative integer power `n` is `r^n = (a^n)/(b^n)`.
22+
23+
Exponentiation of a rational number `r = a/b` to a negative integer power `n` is `r^n = (b^m)/(a^m)`, where `m = |n|`.
24+
25+
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.
26+
27+
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`.
28+
29+
Implement the following operations:
30+
31+
- addition, subtraction, multiplication and division of two rational numbers,
32+
- 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.
33+
34+
Your implementation of rational numbers should always be reduced to lowest terms.
35+
For example, `4/4` should reduce to `1/1`, `30/60` should reduce to `1/2`, `12/8` should reduce to `3/2`, etc.
36+
To reduce a rational number `r = a/b`, divide `a` and `b` by the greatest common divisor (gcd) of `a` and `b`.
37+
So, for example, `gcd(12, 8) = 4`, so `r = 12/8` can be reduced to `(12/4)/(8/4) = 3/2`.
38+
The reduced form of a rational number should be in "standard form" (the denominator should always be a positive integer).
39+
If a denominator with a negative integer is present, multiply both numerator and denominator by `-1` to ensure standard form is reached.
40+
For example, `3/-4` should be reduced to `-3/4`
41+
42+
Assume that the programming language you are using does not have an implementation of rational numbers.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"IsaacG"
4+
],
5+
"files": {
6+
"solution": [
7+
"rational_numbers.go"
8+
],
9+
"test": [
10+
"rational_numbers_test.go"
11+
],
12+
"example": [
13+
".meta/example.go"
14+
]
15+
},
16+
"blurb": "Implement rational numbers.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Rational_number"
19+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package rationalnumbers
2+
3+
import "math"
4+
5+
func abs(a int) int {
6+
if a < 0 {
7+
return -a
8+
}
9+
return a
10+
}
11+
12+
func pow(a, b int) int {
13+
return int(math.Pow(float64(a), float64(b)))
14+
}
15+
16+
// gcd calculates the Greatest Common Divisor of two integers using the Euclidean algorithm.
17+
func gcd(a, b int) int {
18+
for b != 0 {
19+
a, b = b, a%b // This swaps a and b and updates b with the remainder
20+
}
21+
return a
22+
}
23+
24+
type Rational struct {
25+
numerator, denominator int
26+
}
27+
28+
func Reduce(r Rational) Rational {
29+
gcd := abs(gcd(r.numerator, r.denominator))
30+
if r.denominator < 0 {
31+
gcd *= -1
32+
}
33+
return Rational{r.numerator / gcd, r.denominator / gcd}
34+
}
35+
36+
func Add(a, b Rational) Rational {
37+
numerator := a.numerator*b.denominator + b.numerator*a.denominator
38+
denominator := a.denominator * b.denominator
39+
return Reduce(Rational{numerator, denominator})
40+
}
41+
42+
func Sub(a, b Rational) Rational {
43+
numerator := a.numerator*b.denominator - b.numerator*a.denominator
44+
denominator := a.denominator * b.denominator
45+
return Reduce(Rational{numerator, denominator})
46+
}
47+
48+
func Mul(a, b Rational) Rational {
49+
return Reduce(Rational{a.numerator * b.numerator, a.denominator * b.denominator})
50+
}
51+
52+
func Div(a, b Rational) Rational {
53+
return Reduce(Rational{a.numerator * b.denominator, b.numerator * a.denominator})
54+
}
55+
56+
func Abs(r Rational) Rational {
57+
return Reduce(Rational{abs(r.numerator), abs(r.denominator)})
58+
}
59+
60+
func Exprational(base Rational, power int) Rational {
61+
if power >= 0 {
62+
return Reduce(Rational{pow(base.numerator, power), pow(base.denominator, power)})
63+
}
64+
power = -power
65+
return Reduce(Rational{pow(base.denominator, power), pow(base.numerator, power)})
66+
}
67+
68+
func Expreal(base int, power Rational) float64 {
69+
return math.Pow(math.Pow(float64(base), float64(power.numerator)), 1/float64(power.denominator))
70+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package main
2+
3+
import (
4+
"../../../../gen"
5+
"fmt"
6+
"log"
7+
"text/template"
8+
)
9+
10+
func main() {
11+
t, err := template.New("").Parse(tmpl)
12+
if err != nil {
13+
log.Fatal(err)
14+
}
15+
j := map[string]any{
16+
"abs": &[]testCaseUnaryRationalToRational{},
17+
"add": &[]testCaseBinaryRationalToRational{},
18+
"div": &[]testCaseBinaryRationalToRational{},
19+
"mul": &[]testCaseBinaryRationalToRational{},
20+
"sub": &[]testCaseBinaryRationalToRational{},
21+
"exprational": &[]testCaseExprational{},
22+
"expreal": &[]testCaseExpreal{},
23+
"reduce": &[]testCaseUnaryRationalToRational{},
24+
}
25+
if err := gen.Gen("rational-numbers", j, t); err != nil {
26+
log.Fatal(err)
27+
}
28+
}
29+
30+
type Rational [2]int
31+
32+
func (r Rational) String() string {
33+
return fmt.Sprintf("Rational{%d, %d}", r[0], r[1])
34+
}
35+
36+
type RationalPair struct {
37+
R1 Rational `json:"r1"`
38+
R2 Rational `json:"r2"`
39+
}
40+
41+
func (r RationalPair) String() string {
42+
return fmt.Sprintf("[2]Rational{{%d, %d}, {%d, %d}}", r.R1[0], r.R1[1], r.R2[0], r.R2[1])
43+
}
44+
45+
type testCaseUnaryRationalToRational struct {
46+
Description string `json:"description"`
47+
Input struct {
48+
R Rational `json:"r"`
49+
} `json:"input"`
50+
Expected Rational `json:"expected"`
51+
}
52+
53+
type testCaseBinaryRationalToRational struct {
54+
Description string `json:"description"`
55+
Input RationalPair `json:"input"`
56+
Expected Rational `json:"expected"`
57+
}
58+
59+
type testCaseExprational struct {
60+
Description string `json:"description"`
61+
Input struct {
62+
R Rational `json:"r"`
63+
N int `json:"n"`
64+
} `json:"input"`
65+
Expected Rational `json:"expected"`
66+
}
67+
68+
type testCaseExpreal struct {
69+
Description string `json:"description"`
70+
Input struct {
71+
X int `json:"x"`
72+
R Rational `json:"r"`
73+
} `json:"input"`
74+
Expected float64 `json:"expected"`
75+
}
76+
77+
var tmpl = `{{.Header}}
78+
79+
var testCasesAbs = []struct {
80+
description string
81+
input Rational
82+
expected Rational
83+
}{
84+
{{range .J.abs}}{
85+
description: {{printf "%q" .Description}},
86+
input: {{.Input.R}},
87+
expected: {{.Expected}},
88+
},
89+
{{end}}
90+
}
91+
92+
var testCasesAdd = []struct {
93+
description string
94+
input [2]Rational
95+
expected Rational
96+
}{
97+
{{range .J.add}}{
98+
description: {{printf "%q" .Description}},
99+
input: {{.Input}},
100+
expected: {{.Expected}},
101+
},
102+
{{end}}
103+
}
104+
105+
var testCasesSub = []struct {
106+
description string
107+
input [2]Rational
108+
expected Rational
109+
}{
110+
{{range .J.sub}}{
111+
description: {{printf "%q" .Description}},
112+
input: {{.Input}},
113+
expected: {{.Expected}},
114+
},
115+
{{end}}
116+
}
117+
118+
var testCasesMul = []struct {
119+
description string
120+
input [2]Rational
121+
expected Rational
122+
}{
123+
{{range .J.mul}}{
124+
description: {{printf "%q" .Description}},
125+
input: {{.Input}},
126+
expected: {{.Expected}},
127+
},
128+
{{end}}
129+
}
130+
131+
var testCasesDiv = []struct {
132+
description string
133+
input [2]Rational
134+
expected Rational
135+
}{
136+
{{range .J.div}}{
137+
description: {{printf "%q" .Description}},
138+
input: {{.Input}},
139+
expected: {{.Expected}},
140+
},
141+
{{end}}
142+
}
143+
144+
var testCasesExprational = []struct {
145+
description string
146+
inputR Rational
147+
inputInt int
148+
expected Rational
149+
}{
150+
{{range .J.exprational}}{
151+
description: {{printf "%q" .Description}},
152+
inputR: {{.Input.R}},
153+
inputInt: {{.Input.N}},
154+
expected: {{.Expected}},
155+
},
156+
{{end}}
157+
}
158+
159+
var testCasesExpreal = []struct {
160+
description string
161+
inputInt int
162+
inputR Rational
163+
expected float64
164+
}{
165+
{{range .J.expreal}}{
166+
description: {{printf "%q" .Description}},
167+
inputInt: {{.Input.X}},
168+
inputR: {{.Input.R}},
169+
expected: {{.Expected}},
170+
},
171+
{{end}}
172+
}
173+
174+
var testCasesReduce = []struct {
175+
description string
176+
input Rational
177+
expected Rational
178+
}{
179+
{{range .J.reduce}}{
180+
description: {{printf "%q" .Description}},
181+
input: {{.Input.R}},
182+
expected: {{.Expected}},
183+
},
184+
{{end}}
185+
}
186+
`

0 commit comments

Comments
 (0)