From c72f27a8018b7d7b2561bdbe9d32ba4f82c97671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nenad=20Misi=C4=87?= Date: Wed, 3 Jul 2024 14:06:10 +0200 Subject: [PATCH] Add rational-numbers exercise (#57) * Add rational-numbers example and lib.cairo scaffold * implement working example * make denom u128 by default * Refactor and turn lib.cairo back into scaffold * refactor slightly + add concepts & difficulty to the practice in config --- config.json | 12 + .../rational-numbers/.docs/instructions.md | 42 ++++ .../rational-numbers/.meta/config.json | 18 ++ .../rational-numbers/.meta/example.cairo | 149 +++++++++++ .../rational-numbers/.meta/tests.toml | 139 +++++++++++ .../practice/rational-numbers/Scarb.toml | 7 + .../practice/rational-numbers/src/lib.cairo | 93 +++++++ .../practice/rational-numbers/src/tests.cairo | 234 ++++++++++++++++++ 8 files changed, 694 insertions(+) create mode 100644 exercises/practice/rational-numbers/.docs/instructions.md create mode 100644 exercises/practice/rational-numbers/.meta/config.json create mode 100644 exercises/practice/rational-numbers/.meta/example.cairo create mode 100644 exercises/practice/rational-numbers/.meta/tests.toml create mode 100644 exercises/practice/rational-numbers/Scarb.toml create mode 100644 exercises/practice/rational-numbers/src/lib.cairo create mode 100644 exercises/practice/rational-numbers/src/tests.cairo diff --git a/config.json b/config.json index 99182641..0c490ade 100644 --- a/config.json +++ b/config.json @@ -195,6 +195,18 @@ ], "prerequisites": [], "difficulty": 5 + }, + { + "slug": "rational-numbers", + "name": "Rational Numbers", + "uuid": "9858a357-b935-437d-b4e0-c3aac20859cf", + "practices": [ + "operator-overload", + "mutability", + "integers" + ], + "prerequisites": [], + "difficulty": 3 } ], "foregone": [ diff --git a/exercises/practice/rational-numbers/.docs/instructions.md b/exercises/practice/rational-numbers/.docs/instructions.md new file mode 100644 index 00000000..f64fc0f2 --- /dev/null +++ b/exercises/practice/rational-numbers/.docs/instructions.md @@ -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. diff --git a/exercises/practice/rational-numbers/.meta/config.json b/exercises/practice/rational-numbers/.meta/config.json new file mode 100644 index 00000000..a78c8d78 --- /dev/null +++ b/exercises/practice/rational-numbers/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [], + "files": { + "solution": [ + "src/lib.cairo", + "Scarb.toml" + ], + "test": [ + "src/tests.cairo" + ], + "example": [ + ".meta/example.cairo" + ] + }, + "blurb": "Implement rational numbers.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Rational_number" +} diff --git a/exercises/practice/rational-numbers/.meta/example.cairo b/exercises/practice/rational-numbers/.meta/example.cairo new file mode 100644 index 00000000..26d32a6d --- /dev/null +++ b/exercises/practice/rational-numbers/.meta/example.cairo @@ -0,0 +1,149 @@ +use alexandria_math::gcd_of_n_numbers::gcd_two_numbers; +use alexandria_math::fast_power::fast_power; +use alexandria_math::fast_root::fast_nr_optimize; +use core::fmt::{Debug, Formatter, Error}; + +#[derive(Drop, Debug, Copy)] +struct Rational { + numer: i128, + denom: u128, +} + +#[generate_trait] +impl RationalImpl of RationalTrait { + fn new(numer: i128, denom: i128) -> Rational { + assert!(denom != 0, "denominator cannot be 0"); + + let sign: i128 = if (numer < 0 && denom > 0) || (numer > 0 && denom < 0) { + -1 + } else { + 1 + }; + + let numer = abs(numer); + let denom = abs(denom); + let gcd_num = gcd_two_numbers(numer, denom); + + let numer = to_i128(numer / gcd_num) * sign; + let denom = denom / gcd_num; + + Rational { numer, denom } + } +} + +impl RationalPartialEq of PartialEq { + fn eq(lhs: @Rational, rhs: @Rational) -> bool { + lhs.numer == rhs.numer && lhs.denom == rhs.denom + } + + fn ne(lhs: @Rational, rhs: @Rational) -> bool { + !(lhs == rhs) + } +} + +impl RationalNeg of Neg { + fn neg(a: Rational) -> Rational { + RationalTrait::new(a.numer * -1, to_i128(a.denom)) + } +} + +impl RationalAdd of Add { + fn add(lhs: Rational, rhs: Rational) -> Rational { + let numer = (lhs.numer * to_i128(rhs.denom)) + (to_i128(lhs.denom) * rhs.numer); + let denom = to_i128(lhs.denom * rhs.denom); + RationalTrait::new(numer, denom) + } +} + +impl RationalSub of Sub { + fn sub(lhs: Rational, rhs: Rational) -> Rational { + lhs + (-rhs) + } +} + +impl RationalMul of Mul { + fn mul(lhs: Rational, rhs: Rational) -> Rational { + RationalTrait::new(lhs.numer * rhs.numer, to_i128(lhs.denom * rhs.denom)) + } +} + +impl RationalDiv of Div { + fn div(lhs: Rational, rhs: Rational) -> Rational { + RationalTrait::new(lhs.numer * to_i128(rhs.denom), to_i128(lhs.denom) * rhs.numer) + } +} + +#[generate_trait] +impl RationalAbs of RationalAbsTrait { + fn abs(self: @Rational) -> Rational { + RationalTrait::new(to_i128(abs(*self.numer)), to_i128(*self.denom)) + } +} + +#[generate_trait] +impl RationalPow of RationalPowTrait { + fn pow(self: @Rational, power: i128) -> Rational { + if *self.numer == 0 { + return *self; + }; + + let power_abs = abs(power); + + // determine the new number's sign + let sign: i128 = if *self.numer < 0 && power_abs % 2 == 1 { + -1 + } else { + 1 + }; + + let numer = to_i128(fast_power(abs(*self.numer), power_abs)) * sign; + let denom = to_i128(fast_power(*self.denom, power_abs)); + + if power < 0 { + RationalTrait::new(denom, numer) + } else { + RationalTrait::new(numer, denom) + } + } + + fn rpow(self: @u128, power: Rational) -> u128 { + // Cairo only supports integers, so a negative rational exponent + // will always return the result 0 + if power.numer < 0 { + return 0; + }; + fast_nr_optimize(fast_power(*self, to_u128(power.numer)), power.denom, 30) + } +} + +// Enables printing i128 values in tests. +// Note that this will soon be added to the core library. +impl I128Debug of Debug { + fn fmt(self: @i128, ref f: Formatter) -> Result<(), Error> { + if *self < 0 { + f.buffer.append(@"-"); + }; + f.buffer.append(@format!("{}", abs(*self))); + Result::Ok(()) + } +} + +fn abs(n: i128) -> u128 { + let val = if n < 0 { + n * -1 + } else { + n + }; + to_u128(val) +} + +fn to_i128(n: u128) -> i128 { + n.try_into().unwrap() +} + +fn to_u128(n: i128) -> u128 { + n.try_into().unwrap() +} + +#[cfg(test)] +mod tests; diff --git a/exercises/practice/rational-numbers/.meta/tests.toml b/exercises/practice/rational-numbers/.meta/tests.toml new file mode 100644 index 00000000..ddea7145 --- /dev/null +++ b/exercises/practice/rational-numbers/.meta/tests.toml @@ -0,0 +1,139 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[0ba4d988-044c-4ed5-9215-4d0bb8d0ae9f] +description = "Arithmetic -> Addition -> Add two positive rational numbers" + +[88ebc342-a2ac-4812-a656-7b664f718b6a] +description = "Arithmetic -> Addition -> Add a positive rational number and a negative rational number" + +[92ed09c2-991e-4082-a602-13557080205c] +description = "Arithmetic -> Addition -> Add two negative rational numbers" + +[6e58999e-3350-45fb-a104-aac7f4a9dd11] +description = "Arithmetic -> Addition -> Add a rational number to its additive inverse" + +[47bba350-9db1-4ab9-b412-4a7e1f72a66e] +description = "Arithmetic -> Subtraction -> Subtract two positive rational numbers" + +[93926e2a-3e82-4aee-98a7-fc33fb328e87] +description = "Arithmetic -> Subtraction -> Subtract a positive rational number and a negative rational number" + +[a965ba45-9b26-442b-bdc7-7728e4b8d4cc] +description = "Arithmetic -> Subtraction -> Subtract two negative rational numbers" + +[0df0e003-f68e-4209-8c6e-6a4e76af5058] +description = "Arithmetic -> Subtraction -> Subtract a rational number from itself" + +[34fde77a-75f4-4204-8050-8d3a937958d3] +description = "Arithmetic -> Multiplication -> Multiply two positive rational numbers" + +[6d015cf0-0ea3-41f1-93de-0b8e38e88bae] +description = "Arithmetic -> Multiplication -> Multiply a negative rational number by a positive rational number" + +[d1bf1b55-954e-41b1-8c92-9fc6beeb76fa] +description = "Arithmetic -> Multiplication -> Multiply two negative rational numbers" + +[a9b8f529-9ec7-4c79-a517-19365d779040] +description = "Arithmetic -> Multiplication -> Multiply a rational number by its reciprocal" + +[d89d6429-22fa-4368-ab04-9e01a44d3b48] +description = "Arithmetic -> Multiplication -> Multiply a rational number by 1" + +[0d95c8b9-1482-4ed7-bac9-b8694fa90145] +description = "Arithmetic -> Multiplication -> Multiply a rational number by 0" + +[1de088f4-64be-4e6e-93fd-5997ae7c9798] +description = "Arithmetic -> Division -> Divide two positive rational numbers" + +[7d7983db-652a-4e66-981a-e921fb38d9a9] +description = "Arithmetic -> Division -> Divide a positive rational number by a negative rational number" + +[1b434d1b-5b38-4cee-aaf5-b9495c399e34] +description = "Arithmetic -> Division -> Divide two negative rational numbers" + +[d81c2ebf-3612-45a6-b4e0-f0d47812bd59] +description = "Arithmetic -> Division -> Divide a rational number by 1" + +[5fee0d8e-5955-4324-acbe-54cdca94ddaa] +description = "Absolute value -> Absolute value of a positive rational number" + +[3cb570b6-c36a-4963-a380-c0834321bcaa] +description = "Absolute value -> Absolute value of a positive rational number with negative numerator and denominator" + +[6a05f9a0-1f6b-470b-8ff7-41af81773f25] +description = "Absolute value -> Absolute value of a negative rational number" + +[5d0f2336-3694-464f-8df9-f5852fda99dd] +description = "Absolute value -> Absolute value of a negative rational number with negative denominator" + +[f8e1ed4b-9dca-47fb-a01e-5311457b3118] +description = "Absolute value -> Absolute value of zero" + +[4a8c939f-f958-473b-9f88-6ad0f83bb4c4] +description = "Absolute value -> Absolute value of a rational number is reduced to lowest terms" + +[ea2ad2af-3dab-41e7-bb9f-bd6819668a84] +description = "Exponentiation of a rational number -> Raise a positive rational number to a positive integer power" + +[8168edd2-0af3-45b1-b03f-72c01332e10a] +description = "Exponentiation of a rational number -> Raise a negative rational number to a positive integer power" + +[c291cfae-cfd8-44f5-aa6c-b175c148a492] +description = "Exponentiation of a rational number -> Raise a positive rational number to a negative integer power" + +[45cb3288-4ae4-4465-9ae5-c129de4fac8e] +description = "Exponentiation of a rational number -> Raise a negative rational number to an even negative integer power" + +[2d47f945-ffe1-4916-a399-c2e8c27d7f72] +description = "Exponentiation of a rational number -> Raise a negative rational number to an odd negative integer power" + +[e2f25b1d-e4de-4102-abc3-c2bb7c4591e4] +description = "Exponentiation of a rational number -> Raise zero to an integer power" + +[431cac50-ab8b-4d58-8e73-319d5404b762] +description = "Exponentiation of a rational number -> Raise one to an integer power" + +[7d164739-d68a-4a9c-b99f-dd77ce5d55e6] +description = "Exponentiation of a rational number -> Raise a positive rational number to the power of zero" + +[eb6bd5f5-f880-4bcd-8103-e736cb6e41d1] +description = "Exponentiation of a rational number -> Raise a negative rational number to the power of zero" + +[30b467dd-c158-46f5-9ffb-c106de2fd6fa] +description = "Exponentiation of a real number to a rational number -> Raise a real number to a positive rational number" + +[6e026bcc-be40-4b7b-ae22-eeaafc5a1789] +description = "Exponentiation of a real number to a rational number -> Raise a real number to a negative rational number" + +[9f866da7-e893-407f-8cd2-ee85d496eec5] +description = "Exponentiation of a real number to a rational number -> Raise a real number to a zero rational number" + +[0a63fbde-b59c-4c26-8237-1e0c73354d0a] +description = "Reduction to lowest terms -> Reduce a positive rational number to lowest terms" + +[5ed6f248-ad8d-4d4e-a545-9146c6727f33] +description = "Reduction to lowest terms -> Reduce places the minus sign on the numerator" + +[f87c2a4e-d29c-496e-a193-318c503e4402] +description = "Reduction to lowest terms -> Reduce a negative rational number to lowest terms" + +[3b92ffc0-5b70-4a43-8885-8acee79cdaaf] +description = "Reduction to lowest terms -> Reduce a rational number with a negative denominator to lowest terms" + +[c9dbd2e6-5ac0-4a41-84c1-48b645b4f663] +description = "Reduction to lowest terms -> Reduce zero to lowest terms" + +[297b45ad-2054-4874-84d4-0358dc1b8887] +description = "Reduction to lowest terms -> Reduce an integer to lowest terms" + +[a73a17fe-fe8c-4a1c-a63b-e7579e333d9e] +description = "Reduction to lowest terms -> Reduce one to lowest terms" diff --git a/exercises/practice/rational-numbers/Scarb.toml b/exercises/practice/rational-numbers/Scarb.toml new file mode 100644 index 00000000..6c90f428 --- /dev/null +++ b/exercises/practice/rational-numbers/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "rational_numbers" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +alexandria_math = { git = "https://github.com/keep-starknet-strange/alexandria.git" } diff --git a/exercises/practice/rational-numbers/src/lib.cairo b/exercises/practice/rational-numbers/src/lib.cairo new file mode 100644 index 00000000..0a1fe75a --- /dev/null +++ b/exercises/practice/rational-numbers/src/lib.cairo @@ -0,0 +1,93 @@ +use core::fmt::{Debug, Formatter, Error}; + +#[derive(Drop, Debug)] +struct Rational {} + +#[generate_trait] +impl RationalImpl of RationalTrait { + fn new(numer: i128, denom: i128) -> Rational { + panic!() + } +} + +impl RationalPartialEq of PartialEq { + fn eq(lhs: @Rational, rhs: @Rational) -> bool { + panic!() + } + + fn ne(lhs: @Rational, rhs: @Rational) -> bool { + panic!() + } +} + +impl RationalNeg of Neg { + fn neg(a: Rational) -> Rational { + panic!() + } +} + +impl RationalAdd of Add { + fn add(lhs: Rational, rhs: Rational) -> Rational { + panic!() + } +} + +impl RationalSub of Sub { + fn sub(lhs: Rational, rhs: Rational) -> Rational { + panic!() + } +} + +impl RationalMul of Mul { + fn mul(lhs: Rational, rhs: Rational) -> Rational { + panic!() + } +} + +impl RationalDiv of Div { + fn div(lhs: Rational, rhs: Rational) -> Rational { + panic!() + } +} + +#[generate_trait] +impl RationalAbs of RationalAbsTrait { + fn abs(self: @Rational) -> Rational { + panic!() + } +} + +#[generate_trait] +impl RationalPow of RationalPowTrait { + fn pow(self: @Rational, power: i128) -> Rational { + panic!() + } + + fn rpow(self: @u128, power: Rational) -> u128 { + panic!() + } +} + +// Enables printing i128 values in tests. +// Note that this will soon be added to the core library. +impl I128Debug of Debug { + fn fmt(self: @i128, ref f: Formatter) -> Result<(), Error> { + if *self < 0 { + f.buffer.append(@"-"); + }; + f.buffer.append(@format!("{}", abs(*self))); + Result::Ok(()) + } +} + +fn abs(n: i128) -> u128 { + let val = if n < 0 { + n * -1 + } else { + n + }; + val.try_into().unwrap() +} + +#[cfg(test)] +mod tests; diff --git a/exercises/practice/rational-numbers/src/tests.cairo b/exercises/practice/rational-numbers/src/tests.cairo new file mode 100644 index 00000000..28d067d9 --- /dev/null +++ b/exercises/practice/rational-numbers/src/tests.cairo @@ -0,0 +1,234 @@ +use rational_numbers::{RationalTrait as Rational, RationalAbsTrait, RationalPowTrait, I128Debug}; + +// Tests of type: Arithmetic + +// Addition + +#[test] +fn add_two_positive_rational_numbers() { + assert_eq!(Rational::new(1, 2) + Rational::new(2, 3), Rational::new(7, 6)); +} + +#[test] +fn add_a_positive_rational_number_and_a_negative_rational_number() { + assert_eq!(Rational::new(1, 2) + Rational::new(-2, 3), Rational::new(-1, 6)); +} + +#[test] +fn add_two_negative_rational_numbers() { + assert_eq!(Rational::new(-1, 2) + Rational::new(-2, 3), Rational::new(-7, 6)); +} + +#[test] +fn add_a_rational_number_to_its_additive_inverse() { + assert_eq!(Rational::new(1, 2) + Rational::new(-1, 2), Rational::new(0, 1)); +} + +// Subtraction + +#[test] +fn subtract_two_positive_rational_numbers() { + assert_eq!(Rational::new(1, 2) - Rational::new(2, 3), Rational::new(-1, 6)); +} + +#[test] +fn subtract_a_positive_rational_number_and_a_negative_rational_number() { + assert_eq!(Rational::new(1, 2) - Rational::new(-2, 3), Rational::new(7, 6)); +} + +#[test] +fn subtract_two_negative_rational_numbers() { + assert_eq!(Rational::new(-1, 2) - Rational::new(-2, 3), Rational::new(1, 6)); +} + +#[test] +fn subtract_a_rational_number_from_itself() { + assert_eq!(Rational::new(1, 2) - Rational::new(1, 2), Rational::new(0, 1)); +} + +// Multiplication + +#[test] +fn multiply_two_positive_rational_numbers() { + assert_eq!(Rational::new(1, 2) * Rational::new(2, 3), Rational::new(1, 3)); +} + +#[test] +fn multiply_a_negative_rational_number_by_a_positive_rational_number() { + assert_eq!(Rational::new(-1, 2) * Rational::new(2, 3), Rational::new(-1, 3)); +} + +#[test] +fn multiply_two_negative_rational_numbers() { + assert_eq!(Rational::new(-1, 2) * Rational::new(-2, 3), Rational::new(1, 3)); +} + +#[test] +fn multiply_a_rational_number_by_its_reciprocal() { + assert_eq!(Rational::new(1, 2) * Rational::new(2, 1), Rational::new(1, 1)); +} + +#[test] +fn multiply_a_rational_number_by_1() { + assert_eq!(Rational::new(1, 2) * Rational::new(1, 1), Rational::new(1, 2)); +} + +#[test] +fn multiply_a_rational_number_by_0() { + assert_eq!(Rational::new(1, 2) * Rational::new(0, 1), Rational::new(0, 1)); +} + +// Division + +#[test] +fn divide_two_positive_rational_numbers() { + assert_eq!(Rational::new(1, 2) / Rational::new(2, 3), Rational::new(3, 4)); +} + +#[test] +fn divide_a_positive_rational_number_by_a_negative_rational_number() { + assert_eq!(Rational::new(1, 2) / Rational::new(-2, 3), Rational::new(-3, 4)); +} + +#[test] +fn divide_two_negative_rational_numbers() { + assert_eq!(Rational::new(-1, 2) / Rational::new(-2, 3), Rational::new(3, 4)); +} + +#[test] +fn divide_a_rational_number_by_1() { + assert_eq!(Rational::new(1, 2) / Rational::new(1, 1), Rational::new(1, 2)); +} + +// Tests of type: Absolute value + +#[test] +fn absolute_value_of_a_positive_rational_number() { + assert_eq!(Rational::new(1, 2).abs(), Rational::new(1, 2)); +} + +#[test] +fn absolute_value_of_a_positive_rational_number_with_negative_numerator_and_denominator() { + assert_eq!(Rational::new(-1, -2).abs(), Rational::new(1, 2)); +} + +#[test] +fn absolute_value_of_a_negative_rational_number() { + assert_eq!(Rational::new(-1, 2).abs(), Rational::new(1, 2)); +} + +#[test] +fn absolute_value_of_a_negative_rational_number_with_negative_denominator() { + assert_eq!(Rational::new(1, -2).abs(), Rational::new(1, 2)); +} + +#[test] +fn absolute_value_of_zero() { + assert_eq!(Rational::new(0, 1).abs(), Rational::new(0, 1)); +} + +#[test] +fn absolute_value_of_a_rational_number_is_reduced_to_lowest_terms() { + assert_eq!(Rational::new(2, 4).abs(), Rational::new(1, 2)); +} + +// Tests of type: Exponentiation of a rational number + +#[test] +fn raise_a_positive_rational_number_to_a_positive_integer_power() { + assert_eq!(Rational::new(1, 2).pow(3), Rational::new(1, 8)); +} + +#[test] +fn raise_a_negative_rational_number_to_a_positive_integer_power() { + assert_eq!(Rational::new(-1, 2).pow(3), Rational::new(-1, 8)); +} + +#[test] +fn raise_a_positive_rational_number_to_a_negative_integer_power() { + assert_eq!(Rational::new(3, 5).pow(-2), Rational::new(25, 9)); +} + +#[test] +fn raise_a_negative_rational_number_to_an_even_negative_integer_power() { + assert_eq!(Rational::new(-3, 5).pow(-2), Rational::new(25, 9)); +} + +#[test] +fn raise_a_negative_rational_number_to_an_odd_negative_integer_power() { + assert_eq!(Rational::new(-3, 5).pow(-3), Rational::new(-125, 27)); +} + +#[test] +fn raise_zero_to_an_integer_power() { + assert_eq!(Rational::new(0, 1).pow(5), Rational::new(0, 1)); +} + +#[test] +fn raise_one_to_an_integer_power() { + assert_eq!(Rational::new(1, 1).pow(4), Rational::new(1, 1)); +} + +#[test] +fn raise_a_positive_rational_number_to_the_power_of_zero() { + assert_eq!(Rational::new(1, 2).pow(0), Rational::new(1, 1)); +} + +#[test] +fn raise_a_negative_rational_number_to_the_power_of_zero() { + assert_eq!(Rational::new(-1, 2).pow(0), Rational::new(1, 1)); +} + +// Tests of type: Exponentiation of a real number to a rational number + +#[test] +fn raise_a_real_number_to_a_positive_rational_number() { + assert_eq!(8_u128.rpow(Rational::new(4, 3)), 16); +} + +#[test] +fn raise_a_real_number_to_a_negative_rational_number() { + assert_eq!(9_u128.rpow(Rational::new(-1, 2)), 0); +} + +#[test] +fn raise_a_real_number_to_a_zero_rational_number() { + assert_eq!(2_u128.rpow(Rational::new(0, 1)), 1); +} + +// Tests of type: Reduction to lowest terms + +#[test] +fn reduce_a_positive_rational_number_to_lowest_terms() { + assert_eq!(Rational::new(2, 4), Rational::new(1, 2)); +} + +#[test] +fn reduce_places_the_minus_sign_on_the_numerator() { + assert_eq!(Rational::new(3, -4), Rational::new(-3, 4)); +} + +#[test] +fn reduce_a_negative_rational_number_to_lowest_terms() { + assert_eq!(Rational::new(-4, 6), Rational::new(-2, 3)); +} + +#[test] +fn reduce_a_rational_number_with_a_negative_denominator_to_lowest_terms() { + assert_eq!(Rational::new(3, -9), Rational::new(-1, 3)); +} + +#[test] +fn reduce_zero_to_lowest_terms() { + assert_eq!(Rational::new(0, 6), Rational::new(0, 1)); +} + +#[test] +fn reduce_an_integer_to_lowest_terms() { + assert_eq!(Rational::new(-14, 7), Rational::new(-2, 1)); +} + +#[test] +fn reduce_one_to_lowest_terms() { + assert_eq!(Rational::new(13, 13), Rational::new(1, 1)); +}