essenceDocumentation
essenceDocs
on this pageAt a glance
Coming from JavaScript?

Rational

type

Exact fractions in lowest terms: arithmetic, rounding, and the forms a Rational prints in.

Taught on: Method calls and Numbers.

At a glance

Declared
namespace Rational for Rational
Refinements
Modes
  • NumberFormatThe forms Rational::toString writes: #Fraction is "3/4", #Decimal is "0.75", #Percent is "75%" and #Scientific is "7.5e-1".
  • RoundingWhich step round reaches for a value that is not already on one.
  • SignStyleWhether toString(showingSign:) writes a sign in front of a value that is not negative.

Creating

of

on Rational

Builds a Rational from one Integer over another.

This is the way to write a ratio of computed values, where the literal form 3/4 is not available. A zero denominator answers empty, and the defaultingTo: entry answers the given Rational instead.

static of(
	_ numerator: Integer,
	over denominator: Integer,
) -> Optional<Rational>

Builds the Rational from two Integers, with nothing known about either.

NameTypeDescription
_Integerthe numerator
overIntegerthe denominator

the Rational, or nothing when the denominator is zero.

Example
constant guests: Integer = 4

expect Rational.of(3, over guests)::is(3/4)
expect Rational.of(3, over 0)::isEmpty()
static of(_ numerator: Integer, over denominator: NonZeroInteger) -> Rational

Builds the Rational over a denominator proven not to be zero.

There is no failure to report, so the Rational itself is the answer.

NameTypeDescription
_Integerthe numerator
overNonZeroIntegerthe denominator, proven not to be zero

the Rational.

Example
expect Rational.of(6, over 8)::is(3/4)
static of(
	_ numerator: Integer,
	over denominator: Integer,
	defaultingTo fallback: Rational,
) -> Rational

Builds the Rational from two Integers, with a value to answer when the denominator is zero.

NameTypeDescription
_Integerthe numerator
overIntegerthe denominator
defaultingToRationalthe value to answer with when there is no Rational

the Rational, or the given value in its place.

Example
constant guests: Integer = 4

expect Rational.of(3, over guests, defaultingTo 0/1)::is(3/4)
expect Rational.of(3, over 0, defaultingTo 0/1)::is(0)

parse

on Rational

Reads a Rational from its text form.

The text is a fraction like 3/4, a decimal like 0.75, or a whole number like 3. Each form takes an optional sign in front, and an underscore between two digits, which is the notation the language writes. Text of another shape, or a fraction over zero, answers empty, and the defaultingTo: entry answers the given Rational instead.

Example
expect Rational.parse("+1_000.5")::is(2001/2)
expect Rational.parse(".5")::isEmpty()
static parse(_ text: String) -> Optional<Rational>
NameTypeDescription
_Stringthe text to read

the Rational, or nothing when the text has any other shape or divides by zero.

static parse(_ text: String, defaultingTo fallback: Rational) -> Rational

Reads a Rational from its text form, with a value to answer when the text has another shape.

NameTypeDescription
_Stringthe text to read
defaultingToRationalthe value to answer with when the text is no Rational

the Rational, or the given value in its place.

Equality, ordering and printing

is

on Rational

Answers whether the Rational has the same value as a number.

The comparison is in lowest terms, so 1/2 equals 2/4. A Rational equals an Integer when it is whole and its numerator is that Integer.

is(_ other: Rational) -> Boolean

Answers whether the Rational has the same value as another Rational.

NameTypeDescription
_Rationalthe Rational to compare against

true when both are equal.

Example
expect 1/2::is(2/4)
expect 1/2::is(1/3)::negate()
is(_ other: Integer) -> Boolean

Answers whether the Rational has the same value as an Integer.

NameTypeDescription
_Integerthe Integer to compare against

true when the Rational is whole and its numerator is that Integer.

Example
expect 6/3::is(2)
expect 5/2::is(2)::negate()

isNot

on Rational

Answers whether the Rational differs from a number.

A Rational that is not whole differs from every Integer.

isNot(_ other: Rational) -> Boolean

Answers whether the Rational differs from another Rational.

NameTypeDescription
_Rationalthe Rational to compare against

true when the two differ.

Example
expect 1/2::isNot(1/3)
expect 1/2::isNot(2/4)::negate()
isNot(_ other: Integer) -> Boolean

Answers whether the Rational differs from an Integer.

NameTypeDescription
_Integerthe Integer to compare against

true when the two differ.

Example
expect 5/2::isNot(2)
expect 6/3::isNot(2)::negate()

compare

on Rational

compare(to other: Rational) -> Ordering

Orders the Rational against another Rational.

NameTypeDescription
toRationalthe Rational to order against

Ordering#Less, Ordering#Equal or Ordering#Greater.

Example
expect 1/2::compare(to 2/3)::is(#Less)
expect 2/4::compare(to 1/2)::is(#Equal)

toString

on Rational

Answers the Rational as a String, in lowest terms.

The form is 3/4 when no format is named, and the named format otherwise. A whole Rational prints its numerator alone, so 1/2::add(1/2) prints 1 and 10::divide(by 2) prints 5. The Rational.parse Method reads the fraction form and the decimal form. The fraction form reads back as the same Rational. A decimal form does too when its expansion ends within 80 digits, and reads back as the rounded value otherwise. A count of one or more places writes exactly that many digits after the point.

the String representation of the Rational.

toString() -> String

Answers the Rational as a fraction, in lowest terms.

The form is 3/4. A whole Rational prints its numerator alone, so 1/2::add(1/2) prints 1.

the String representation of the Rational.

Example
expect 3/4::toString()::is("3/4")
expect 1/2::add(1/2)::toString()::is("1")
toString(as format: NumberFormat) -> String

Answers the Rational in the named format.

The #Decimal format writes the expansion, to at most 80 digits, and the #Fraction format writes 3/4. A whole Rational prints its numerator alone in either. The #Percent format writes that same expansion of one hundred times the value, with a % after it. The #Scientific format writes one digit before the point and the power of ten as an exponent, so 1234 is 1.234e3 and zero is 0e0.

NameTypeDescription
asNumberFormatthe form to represent the Rational in

the String representation of the Rational.

Example
expect 3/4::toString(as #Decimal)::is("0.75")
expect 3/4::toString(as #Percent)::is("75%")
expect 1234/1::toString(as #Scientific)::is("1.234e3")
toString(
	as format: NumberFormat,
	toPlaces count: Integer,
	toward direction: Rounding = #Nearest,
) -> String

Answers the Rational in the named format, with exactly that many places.

The digits are padded with zeroes where the expansion is shorter, so 1/2 over two places is 0.50. The last digit kept is rounded in the named direction, and #Nearest is what a call that names none is given, so 1/8 is 0.13. A count below one rounds to a whole number, and no point is written. The count has no ceiling. The #Percent and #Scientific formats take the count the same way, so 1/8 over one place is 12.5% and 1234 over two is 1.23e3. The #Fraction format ignores both the count and the direction.

NameTypeDescription
asNumberFormatthe form to represent the Rational in
toPlacesIntegerhow many digits to write after the point
towardRounding = #Nearestthe direction to round the last digit in, #Nearest when it is left out

the String representation of the Rational.

Example
expect 1/2::toString(as #Decimal, toPlaces 2)::is("0.50")
expect 1/8::toString(as #Decimal, toPlaces 2)::is("0.13")
expect 1/8::toString(as #Decimal, toPlaces 2, toward #Down)::is("0.12")
toString(as format: NumberFormat, groupingWith separator: String) -> String

Answers the Rational in the named format, with a separator between the digit groups.

The separator goes between every three digits before the point, counted from the right, so 1234567 is 1,234,567 under a comma. The scientific form writes one digit before the point and takes no separator. The fraction form groups the numerator and the denominator alike.

NameTypeDescription
asNumberFormatthe form to represent the Rational in
groupingWithStringthe text to write between the digit groups

the String representation of the Rational.

Example
constant price = 12345/2

expect price::toString(as #Decimal, groupingWith ",")::is("6,172.5")
expect price::toString(as #Fraction, groupingWith ",")::is("12,345/2")
toString(
	as format: NumberFormat,
	toPlaces count: Integer,
	groupingWith separator: String,
	toward direction: Rounding = #Nearest,
) -> String

Answers the Rational in the named format, with that many places and a separator between the digit groups.

The count and the direction reach the format exactly as they do without a separator, so 1234567/1 over two places is 1,234,567.00 under a comma. Only the digits before the point are grouped.

NameTypeDescription
asNumberFormatthe form to represent the Rational in
toPlacesIntegerhow many digits to write after the point
groupingWithStringthe text to write between the digit groups
towardRounding = #Nearestthe direction to round the last digit in, #Nearest when it is left out

the String representation of the Rational.

Example
constant label = 12345/2::toString(
	as #Decimal,
	toPlaces 2,
	groupingWith ",",
)

expect label::is("6,172.50")
toString(showingSign style: SignStyle) -> String

Answers the Rational as a fraction, with its sign written in the named style.

The #Always Case writes a + in front of a value that is not negative, so 3/4 is +3/4 and zero is +0. The #Negative Case writes what the plain toString() writes.

NameTypeDescription
showingSignSignStylewhether to write a sign in front of a value that is not negative

the String representation of the Rational.

Example
expect 3/4::toString(showingSign #Always)::is("+3/4")
expect 0/1::toString(showingSign #Always)::is("+0")
expect 3/4::toString(showingSign #Negative)::is("3/4")

Arithmetic

add

on Rational

Adds a number to the Rational, and stays exact for every member of the numeric tower.

add(_ other: Rational) -> Rational
NameTypeDescription
_Rationalthe Rational to add

the sum.

Example
expect 1/2::add(-1/3)::is(1/6)
add(_ other: Integer) -> Rational

Adds an Integer to the Rational.

The sum is a Rational, since a whole number added to a fraction need not be whole.

NameTypeDescription
_Integerthe Integer to add

the sum, as a Rational.

Example
expect 1/2::add(1)::is(3/2)
add(_ other: Algebraic) -> Algebraic

Adds an Algebraic to the Rational.

The sum is an Algebraic, since adding a Rational leaves the radical part in place.

NameTypeDescription
_Algebraicthe Algebraic to add

the sum, as an Algebraic.

Example
expect 1/2::add(Number.GoldenRatio)::toString()::is("1 + 1/2·√5")
add(_ other: Transcendental) -> Transcendental

Adds a Transcendental to the Rational.

The sum is a Transcendental, since adding a Rational leaves every base term in place.

NameTypeDescription
_Transcendentalthe Transcendental to add

the sum, as a Transcendental.

Example
expect 1/2::add(Number.Pi)::toString()::is("1/2 + π")

on NonNegativeRational

Adds a Rational proven not to be negative to this NonNegativeRational.

A sum of two non-negative Rationals is never negative, and a positive summand makes it positive.

Adds a Rational proven to be positive.

The receiver is not negative and the other summand is above zero, so the sum is above zero.

NameTypeDescription
_PositiveRationalthe PositiveRational to add

the sum, which is above zero.

Example
constant tip: NonNegativeRational = 1/10

expect tip::add(1/5)::is(3/10)

Adds a Rational proven not to be negative.

Neither summand is negative, so the sum is not negative either.

NameTypeDescription
_NonNegativeRationalthe NonNegativeRational to add

the sum, which is never negative.

Example
constant tip: NonNegativeRational = 1/10
constant tax: NonNegativeRational = 1/5

expect tip::add(tax)::is(3/10)

on PositiveRational

Adds a Rational proven not to be negative to this PositiveRational.

The receiver is above zero and the other summand is not below it, so the sum is above zero.

NameTypeDescription
_NonNegativeRationalthe NonNegativeRational to add

the sum, which is above zero.

Example
expect 1/2::add(1/3)::is(5/6)
expect 1/2::add(0/1)::is(1/2)

subtract

on Rational

Subtracts a number from the Rational, and stays exact for every member of the numeric tower.

subtract(_ other: Rational) -> Rational
NameTypeDescription
_Rationalthe Rational to subtract

the difference.

Example
expect 3/4::subtract(1/4)::is(1/2)
subtract(_ other: Integer) -> Rational

Subtracts an Integer from the Rational.

The difference is a Rational, since a whole number taken from a fraction need not be whole.

NameTypeDescription
_Integerthe Integer to subtract

the difference, as a Rational.

Example
expect 5/2::subtract(1)::is(3/2)
subtract(_ other: Algebraic) -> Algebraic

Subtracts an Algebraic from the Rational.

The difference is an Algebraic, since the radical part stays and only its sign flips.

NameTypeDescription
_Algebraicthe Algebraic to subtract

the difference, as an Algebraic.

Example
expect 1/2::subtract(Number.GoldenRatio)::toString()::is("-1/2·√5")
subtract(_ other: Transcendental) -> Transcendental

Subtracts a Transcendental from the Rational.

The difference is a Transcendental, since every base term stays with its sign flipped.

NameTypeDescription
_Transcendentalthe Transcendental to subtract

the difference, as a Transcendental.

Example
expect 1/2::subtract(Number.Pi)::toString()::is("1/2 − π")

multiply

on Rational

Multiplies the Rational with a number, and stays exact for every member of the numeric tower.

multiply(with other: Rational) -> Rational
NameTypeDescription
withRationalthe Rational to multiply with

the product.

Example
constant rate: Rational = 3/4

expect rate::multiply(with 2/3)::is(1/2)
multiply(with other: Integer) -> Rational

Multiplies the Rational with an Integer.

The product is a Rational, since scaling a fraction by a whole number need not leave a whole number.

NameTypeDescription
withIntegerthe Integer to multiply with

the product, as a Rational.

Example
expect 3/4::multiply(with 2)::is(3/2)
multiply(with other: Algebraic) -> Algebraic | Rational

Multiplies the Rational with an Algebraic.

The product is an Algebraic, since scaling keeps the radical part. A zero receiver answers zero, which is a Rational.

NameTypeDescription
withAlgebraicthe Algebraic to multiply with

the product, as an Algebraic or a Rational.

Example
expect 2/1::multiply(with Number.GoldenRatio)::toString()::is("1 + √5")
expect 0/1::multiply(with Number.GoldenRatio)::is(0)
multiply(with other: Transcendental) -> Transcendental | Rational

Multiplies the Rational with a Transcendental.

The product is a Transcendental, since scaling keeps every base term. A zero receiver answers zero, which is a Rational.

NameTypeDescription
withTranscendentalthe Transcendental to multiply with

the product, as a Transcendental or a Rational.

Example
expect 1/2::multiply(with Number.Tau)::toString()::is("π")
expect 0/1::multiply(with Number.Pi)::is(0)

on NonZeroRational

multiply(with other: NonZeroRational) -> NonZeroRational

Multiplies this NonZeroRational with another.

The product is never zero, so the answer is a NonZeroRational too.

NameTypeDescription
withNonZeroRationalthe NonZeroRational to multiply with

the product, which is not zero.

Example
expect -1/2::multiply(with 2/3)::is(-1/3)

on NonNegativeRational

Multiplies this NonNegativeRational with a Rational proven not to be negative.

Neither factor is negative, so the product is not negative either.

NameTypeDescription
withNonNegativeRationalthe NonNegativeRational to multiply with

the product, which is never negative.

Example
constant tip: NonNegativeRational = 1/10
constant share: NonNegativeRational = 1/2

expect tip::multiply(with share)::is(1/20)

on PositiveRational

multiply(with other: PositiveRational) -> PositiveRational

Multiplies this PositiveRational with another.

Both factors are above zero, so the product is above zero.

NameTypeDescription
withPositiveRationalthe PositiveRational to multiply with

the product, which is above zero.

Example
expect 1/2::multiply(with 3/4)::is(3/8)

divide

on Rational

Divides the Rational by a number, exactly.

Dividing by an Integer or a Rational answers empty for a zero divisor. With defaultingTo, the given value stands in place of empty. Dividing by an Algebraic always answers, because an Algebraic is irrational and so never zero. Dividing by an Integer proven not to be zero always answers too. A Rational proven not to be zero answers as well.

divide(by other: Rational) -> Optional<Rational>
NameTypeDescription
byRationalthe Rational to divide by

the quotient, or nothing when the divisor is zero.

Example
constant share: Rational = 3/4

expect 1/2::divide(by share)::is(2/3)
expect 1/2::divide(by 0/1)::isEmpty()
divide(by other: Integer) -> Optional<Rational>

Divides the Rational by an Integer.

The quotient is a Rational. A zero divisor answers empty, and the defaultingTo: entry answers the given Rational instead.

NameTypeDescription
byIntegerthe Integer to divide by

the quotient, or nothing when the divisor is zero.

Example
constant people: Integer = 3

expect 3/4::divide(by people)::is(1/4)
expect 3/4::divide(by 0)::isEmpty()
divide(by other: Algebraic) -> Algebraic | Rational

Divides the Rational by an Algebraic.

An Algebraic is irrational and so never zero, which is why the quotient itself is the answer. The quotient is an Algebraic, and a zero receiver answers zero, which is a Rational.

NameTypeDescription
byAlgebraicthe Algebraic to divide by

the quotient, as an Algebraic or a Rational.

Example
constant phi = Number.GoldenRatio

expect 1/1::divide(by phi)::is(phi::subtract(1))
expect 0/1::divide(by phi)::is(0)
divide(by other: Rational, defaultingTo fallback: Rational) -> Rational

Divides by a Rational, and answers the given value when the divisor is zero.

NameTypeDescription
byRationalthe divisor
defaultingToRationalthe value to answer with when there is no quotient

the quotient, or the given value in its place.

Example
constant share: Rational = 3/4

expect 1/2::divide(by share, defaultingTo 0/1)::is(2/3)
expect 1/2::divide(by 0/1, defaultingTo 0/1)::is(0)
divide(by other: Integer, defaultingTo fallback: Rational) -> Rational

Divides by an Integer, and answers the given value when the divisor is zero.

NameTypeDescription
byIntegerthe divisor
defaultingToRationalthe value to answer with when there is no quotient

the quotient, or the given value in its place.

Example
constant people: Integer = 3

expect 3/4::divide(by people, defaultingTo 0/1)::is(1/4)
expect 3/4::divide(by 0, defaultingTo 0/1)::is(0)
divide(by other: NonZeroInteger) -> Rational

Divides by an Integer proven not to be zero.

There is no failure to report, so the quotient itself is the answer.

NameTypeDescription
byNonZeroIntegerthe divisor, proven not to be zero

the quotient.

Example
expect 3/4::divide(by 3)::is(1/4)
divide(by other: NonZeroRational) -> Rational

Divides by a Rational proven not to be zero.

There is no failure to report, so the quotient itself is the answer.

NameTypeDescription
byNonZeroRationalthe divisor, proven not to be zero

the quotient.

Example
expect 1/2::divide(by 3/4)::is(2/3)

remainder

on Rational

Answers what is left over after taking out every whole divisor that fits.

The division is Euclidean, so the remainder is never negative and always below the divisor's magnitude. For example, 7/2::remainder(dividingBy 1/3) is 1/6. A zero divisor answers empty, and the defaultingTo: entry answers the given Rational instead.

remainder(dividingBy divisor: Rational) -> Optional<Rational>

Answers the remainder over a divisor nothing is known about.

NameTypeDescription
dividingByRationalthe divisor

the remainder, or nothing when dividing by zero.

Example
constant step: Rational = 1/3

expect 7/2::remainder(dividingBy step)::is(1/6)
expect 7/2::remainder(dividingBy 0/1)::isEmpty()
remainder(dividingBy divisor: NonZeroRational) -> Rational

Answers the remainder over a divisor proven not to be zero.

The division can not fail, so the answer is the remainder itself rather than an Optional.

NameTypeDescription
dividingByNonZeroRationalthe divisor, proven not to be zero

the remainder.

Example
expect 7/2::remainder(dividingBy 1/3)::is(1/6)
expect -7/2::remainder(dividingBy 1/3)::is(1/6)
remainder(
	dividingBy divisor: Rational,
	defaultingTo fallback: Rational,
) -> Rational

Answers the remainder, with a value to answer when the divisor is zero.

NameTypeDescription
dividingByRationalthe divisor
defaultingToRationalthe value to answer with when there is no remainder

the remainder, or the given value in its place.

Example
constant step: Rational = 1/3

expect 7/2::remainder(dividingBy step, defaultingTo 0/1)::is(1/6)
expect 7/2::remainder(dividingBy 0/1, defaultingTo 0/1)::is(0)

quotient

on Rational

Answers how many whole divisors fit.

This is the other half of the same Euclidean division as remainder, and the two agree: quotient · divisor + remainder is the original Rational. For example, 7/2::quotient(dividingBy 1/3) is 10. A zero divisor answers empty, and the defaultingTo: entry answers the given Integer instead.

quotient(dividingBy divisor: Rational) -> Optional<Integer>

Answers the quotient over a divisor nothing is known about.

NameTypeDescription
dividingByRationalthe divisor

the quotient, or nothing when dividing by zero.

Example
constant step: Rational = 1/3

expect 7/2::quotient(dividingBy step)::is(10)
expect 7/2::quotient(dividingBy 0/1)::isEmpty()
quotient(dividingBy divisor: NonZeroRational) -> Integer

Answers the quotient over a divisor proven not to be zero.

The division can not fail, so the answer is the quotient itself rather than an Optional.

NameTypeDescription
dividingByNonZeroRationalthe divisor, proven not to be zero

the quotient.

Example
expect 7/2::quotient(dividingBy 1/3)::is(10)
expect -7/2::quotient(dividingBy 1/3)::is(-11)
quotient(
	dividingBy divisor: Rational,
	defaultingTo fallback: Integer,
) -> Integer

Answers the quotient, with a value to answer when the divisor is zero.

NameTypeDescription
dividingByRationalthe divisor
defaultingToIntegerthe value to answer with when there is no quotient

the quotient, or the given value in its place.

Example
constant step: Rational = 1/3

expect 7/2::quotient(dividingBy step, defaultingTo 0)::is(10)
expect 7/2::quotient(dividingBy 0/1, defaultingTo 0)::is(0)

raise

on Rational

Raises the Rational to the given power.

A negative exponent answers the exact reciprocal power. Zero raised to the power of zero is one. Raising this Rational to an exponent proven not to be negative can not fail. Zero raised to a negative power answers empty, and the defaultingTo: entry answers the given Rational instead.

raise(to exponent: Integer) -> Optional<Rational>
NameTypeDescription
toIntegerthe exponent

the power, or nothing when raising zero to a negative power.

Example
expect 2/3::raise(to -2)::is(9/4)
expect 0/1::raise(to -1)::isEmpty()
raise(
	to exponent: Integer,
	defaultingTo fallback: Rational,
) -> Rational

Raises the Rational to the given power, and answers the given value when there is no power.

NameTypeDescription
toIntegerthe exponent
defaultingToRationalthe value to answer with when there is no power

the power, or the given value in its place.

Example
expect 2/3::raise(to -2, defaultingTo 0/1)::is(9/4)
expect 0/1::raise(to -1, defaultingTo 0/1)::is(0)
raise(to exponent: NonNegativeInteger) -> Rational

Raises the Rational to an exponent proven not to be negative.

Every Rational has such a power, the reciprocal is never taken, and no denominator can reach zero. So the answer is the power itself rather than an Optional.

NameTypeDescription
toNonNegativeIntegerthe exponent, proven not to be negative

the power.

Example
expect 2/3::raise(to 2)::is(4/9)
expect 0/1::raise(to 0)::is(1)

squareRoot

on Rational

Answers the exact square root of the Rational.

A perfect square answers a Rational. Any other value that is not negative answers an exact Algebraic. A negative Rational answers empty, and the defaultingTo: entry answers the given value instead.

squareRoot() -> Optional<Rational | Algebraic>

the root, or nothing for a negative Rational.

Example
constant area: Rational = 9/4

expect area::squareRoot()::is(3/2)
expect -1/4::squareRoot()::isEmpty()
squareRoot(
	defaultingTo fallback: Rational | Algebraic,
) -> Rational | Algebraic

Answers the exact square root, with a value to answer for a negative Rational.

NameTypeDescription
defaultingToRational | Algebraicthe value to answer with when there is no root

the root, or the given value in its place.

Example
constant area: Rational = 9/4

expect area::squareRoot(defaultingTo 0/1)::is(3/2)
expect -1/4::squareRoot(defaultingTo 0/1)::is(0)

on NonNegativeRational

squareRoot() -> Rational | Algebraic

Answers the exact square root of this NonNegativeRational.

A negative Rational is the one with no real root, and the receiver is proven not to be one. So the answer is the root itself rather than an Optional. A ratio of two perfect squares answers a Rational, and every other value answers an exact Algebraic.

the root.

Example
constant area: NonNegativeRational = 9/4
constant half: NonNegativeRational = 1/2

expect area::squareRoot()::is(3/2)
expect half::squareRoot()::toString()::is("1/2·√2")

on PositiveRational

squareRoot() -> PositiveRational | Algebraic

Answers the exact square root of this PositiveRational.

A ratio of two perfect squares above zero has a root above zero, and every other value answers an exact Algebraic.

the root.

Example
expect 9/4::squareRoot()::is(3/2)
expect 1/2::squareRoot()::toString()::is("1/2·√2")

Comparing

isLessThan

on Rational

Answers whether the Rational is strictly below the given number.

isLessThan(_ other: Rational) -> Boolean

Answers whether the Rational is strictly below another Rational.

The comparison is in lowest terms, so 1/2 is not below 2/4.

NameTypeDescription
_Rationalthe Rational to compare against

true when the receiver is below the other Rational.

Example
expect 1/2::isLessThan(2/3)
expect 1/2::isLessThan(2/4)::negate()
isLessThan(_ other: Integer) -> Boolean

Answers whether the Rational is strictly below an Integer.

The Integer is read as n/1, so 1/2 is below 1.

NameTypeDescription
_Integerthe Integer to compare against

true when the Rational is below the Integer.

Example
expect 1/2::isLessThan(1)
expect 3/2::isLessThan(1)::negate()

isLessThanOrEqualTo

on Rational

Answers whether the Rational is below the given number, or equal to it.

isLessThanOrEqualTo(_ other: Rational) -> Boolean

Answers whether the Rational is below another Rational, or equal to it.

Equal Rationals answer true. The comparison is in lowest terms, so 1/2 answers true against 2/4.

NameTypeDescription
_Rationalthe Rational to compare against

true when the receiver is below the other Rational or equal to it.

Example
expect 1/2::isLessThanOrEqualTo(2/4)
expect 2/3::isLessThanOrEqualTo(1/2)::negate()
isLessThanOrEqualTo(_ other: Integer) -> Boolean

Answers whether the Rational is below an Integer, or equal to it.

The Integer is read as n/1, so 2/2 answers true against 1.

NameTypeDescription
_Integerthe Integer to compare against

true when the Rational is below the Integer or equal to it.

Example
expect 2/2::isLessThanOrEqualTo(1)
expect 3/2::isLessThanOrEqualTo(1)::negate()

isGreaterThan

on Rational

Answers whether the Rational is strictly above the given number.

isGreaterThan(_ other: Rational) -> Boolean

Answers whether the Rational is strictly above another Rational.

The comparison is in lowest terms, so 2/4 is not above 1/2.

NameTypeDescription
_Rationalthe Rational to compare against

true when the receiver is above the other Rational.

Example
expect 2/3::isGreaterThan(1/2)
expect 2/4::isGreaterThan(1/2)::negate()
isGreaterThan(_ other: Integer) -> Boolean

Answers whether the Rational is strictly above an Integer.

The Integer is read as n/1, so 3/2 is above 1.

NameTypeDescription
_Integerthe Integer to compare against

true when the Rational is above the Integer.

Example
expect 3/2::isGreaterThan(1)
expect 1/2::isGreaterThan(1)::negate()

isGreaterThanOrEqualTo

on Rational

Answers whether the Rational is above the given number, or equal to it.

isGreaterThanOrEqualTo(_ other: Rational) -> Boolean

Answers whether the Rational is above another Rational, or equal to it.

Equal Rationals answer true. The comparison is in lowest terms, so 2/4 answers true against 1/2.

NameTypeDescription
_Rationalthe Rational to compare against

true when the receiver is above the other Rational or equal to it.

Example
expect 2/4::isGreaterThanOrEqualTo(1/2)
expect 1/2::isGreaterThanOrEqualTo(2/3)::negate()
isGreaterThanOrEqualTo(_ other: Integer) -> Boolean

Answers whether the Rational is above an Integer, or equal to it.

The Integer is read as n/1, so 2/2 answers true against 1.

NameTypeDescription
_Integerthe Integer to compare against

true when the Rational is above the Integer or equal to it.

Example
expect 2/2::isGreaterThanOrEqualTo(1)
expect 1/2::isGreaterThanOrEqualTo(1)::negate()

isBetween

on Rational · provided by Orderable

isBetween(_ lower: Rational, and upper: Rational) -> Boolean

Answers whether the value lies between the two given ones, both included.

The two bounds name the same range in either order: 7::isBetween(10, and 1) is true, and 15::isBetween(10, and 1) is false.

NameTypeDescription
_Rationalone bound of the range, included
andRationalthe other bound of the range, included

true when the value is within the bounds.

Example
expect 7::isBetween(1, and 10)
expect 7::isBetween(10, and 1)
expect 15::isBetween(10, and 1)::negate()

clamp

on Rational · provided by Orderable

clamp(between lowest: Rational, and highest: Rational) -> Rational

Answers the value, pulled into the given bounds.

The answer is the lower bound when the value is below it. It is the upper bound when the value is above it, and the value itself otherwise. The two bounds name the same range in either order: 7::clamp(between 10, and 1) is 7, and 15::clamp(between 10, and 1) is 10.

NameTypeDescription
betweenRationalone bound of the range
andRationalthe other bound of the range

the clamped value.

Example
expect 7::clamp(between 1, and 10)::is(7)
expect 15::clamp(between 1, and 10)::is(10)
expect -4::clamp(between 10, and 1)::is(1)

Testing

isWholeNumber

on Rational

isWholeNumber() -> Boolean

Answers whether the Rational is a whole number.

A whole number has the denominator one in lowest terms.

Example
expect 4/2::isWholeNumber()
expect 3/4::isWholeNumber()::negate()

isPositive

on Rational

isPositive() -> Boolean

Answers whether the Rational is above zero.

Zero is neither positive nor negative.

Example
expect 3/4::isPositive()
expect 0/1::isPositive()::negate()

isNegative

on Rational

isNegative() -> Boolean

Answers whether the Rational is below zero.

Zero is neither positive nor negative.

Example
expect -3/4::isNegative()
expect 0/1::isNegative()::negate()

isZero

on Rational

isZero() -> Boolean

Answers whether the Rational is exactly zero.

Example
expect 0/1::isZero()
expect 3/4::isZero()::negate()

Parts

numerator

on Rational

numerator() -> Integer

Answers the numerator of the Rational in lowest terms.

The numerator carries the sign, because the denominator is always positive.

Example
constant ratio: Rational = -6/8

expect ratio::numerator()::is(-3)

on NonZeroRational

numerator() -> NonZeroInteger

Answers the numerator of this NonZeroRational in lowest terms.

A Rational is zero exactly when its numerator is, and the receiver is proven not to be zero. So the answer is a NonZeroInteger.

the numerator, which is not zero.

Example
expect -6/8::numerator()::is(-3)

denominator

on Rational

denominator() -> PositiveInteger

Answers the denominator of the Rational in lowest terms.

The denominator is always positive, and never zero. So the answer is a PositiveInteger, which fits every position that asks for a NonZeroInteger or a NonNegativeInteger.

Example
expect 6/8::denominator()::is(4)
expect -1/2::denominator()::is(2)

absolute

on Rational

Answers the Rational without its sign, which is its distance from zero.

the distance, which is never negative.

Example
constant change: Rational = -3/4

expect change::absolute()::is(3/4)

on NonZeroRational

absolute() -> PositiveRational

Answers this NonZeroRational without its sign, which is its distance from zero.

The receiver is not zero, so its distance from zero is above zero. So the answer is a PositiveRational.

the distance, which is above zero.

Example
expect -3/4::absolute()::is(3/4)

Sign and rounding

negate

on Rational

negate() -> Rational

Answers the Rational with its sign flipped.

Example
constant change: Rational = 3/4

expect change::negate()::is(-3/4)

on NonZeroRational

negate() -> NonZeroRational

Answers this NonZeroRational with its sign flipped.

Negation keeps a value away from zero, so the answer is a NonZeroRational too.

the negation, which is not zero.

Example
expect 3/4::negate()::is(-3/4)

reciprocal

on Rational

Answers the reciprocal of the Rational.

The reciprocal exchanges the numerator and the denominator. Zero has no reciprocal, and the defaultingTo: entry answers the given Rational instead. A receiver proven not to be zero answers the reciprocal itself.

reciprocal() -> Optional<Rational>

the reciprocal, or nothing for zero.

Example
constant ratio: Rational = 3/4

expect ratio::reciprocal()::is(4/3)
expect 0/1::reciprocal()::isEmpty()
reciprocal(defaultingTo fallback: Rational) -> Rational

Answers the reciprocal, with a value to answer for zero.

NameTypeDescription
defaultingToRationalthe value to answer with when there is no reciprocal

the reciprocal, or the given value in its place.

Example
constant ratio: Rational = 3/4

expect ratio::reciprocal(defaultingTo 0/1)::is(4/3)
expect 0/1::reciprocal(defaultingTo 0/1)::is(0)

on NonZeroRational

reciprocal() -> NonZeroRational

Answers the reciprocal of this NonZeroRational.

Zero is the one Rational with no reciprocal, and the receiver is proven not to be one. So the answer is the reciprocal itself rather than an Optional. Exchanging the two parts of a value that is not zero leaves a value that is not zero, so the answer is proven as well.

the reciprocal, which is not zero.

Example
expect 3/4::reciprocal()::is(4/3)
expect -1/2::reciprocal()::is(-2)

round

on Rational

Answers the Rational rounded in the named direction.

The direction is #Nearest when a call names none. A value exactly halfway between two steps rounds away from zero, so 1/2 answers 1 and -1/2 answers -1. Under #NearestEven a value exactly halfway between two steps reaches the even one, so 1/2 answers 0 and 3/2 answers 2. The other directions answer the floor for #Down, the ceiling for #Up, and the value towards zero for #TowardZero. Naming a count of places rounds to a decimal grid of that width instead, and answers a Rational.

round(toward direction: Rounding = #Nearest) -> Integer

Answers the Rational as an Integer, rounded in the named direction.

NameTypeDescription
towardRounding = #Nearestthe direction to round in, #Nearest when it is left out

the rounded Integer.

Example
expect 5/2::round()::is(3)
expect 5/2::round(toward #NearestEven)::is(2)
expect -5/2::round(toward #Down)::is(-3)
round(
	toPlaces places: Integer,
	toward direction: Rounding = #Nearest,
) -> Rational

Answers the Rational rounded to a decimal grid of the given width.

Two places round to hundredths, so 5/3 answers 167/100. The answer is exact, and it is a Rational rather than text: toString(as #Decimal, toPlaces 2) is what writes 1.67. A count below one rounds to a whole number, answered as a Rational. The count has no ceiling, as it has none on toString.

NameTypeDescription
toPlacesIntegerhow many decimal places to keep
towardRounding = #Nearestthe direction to round in, #Nearest when it is left out

the rounded Rational.

Example
expect 5/3::round(toPlaces 2)::is(167/100)
expect 5/3::round(toPlaces 0)::is(2)

approximate

on Rational

approximate(
	toPlaces places: NonNegativeInteger,
	toward direction: Rounding = #Nearest,
) -> Rational

Answers the Rational read off a decimal grid of the given width.

A Rational is exact, so the answer is the same one round(toPlaces:toward:) gives: two places answer hundredths, and 5/3 answers 167/100. The entries of this name on Algebraic and on Transcendental are what make an irrational readable as a number. This one is what lets a Number receiver reach the family.

NameTypeDescription
toPlacesNonNegativeIntegerhow many decimal places to keep
towardRounding = #Nearestthe direction to round in, #Nearest when it is left out

the Rational on that grid.

Example
expect 5/3::approximate(toPlaces 2)::is(167/100)

Converting

toRational

on Rational

toRational() -> Rational

Answers the Rational as a Rational.

A Rational is one already, so the answer is the receiver itself. The entry is what lets a Scalar receiver reach the crossing that an Integer receiver reaches.

the receiver itself.

Example
expect 3/4::toRational()::is(3/4)

Refinements

Each type below is a checked refinement: a type with a condition. A value has the type only once the compiler knows the condition holds, and Checked refinements shows how it finds out. Under each one, Tighter here lists the methods above that return a more precise type when called on it. Returned by lists the methods that return it.

NonZeroRational

type NonZeroRational = Rational where @::isNot(0/1)

The Rationals that are not zero, as a checked refinement of Rational, and the sister of NonZeroInteger.

The proof is what lets reciprocal answer a Rational rather than an Optional. The numerator is a NonZeroInteger, and absolute, negate and multiplication answer one of these again.

Tighter here multiply, numerator, absolute, negate, reciprocal

Returned by NonZeroInteger::divide(by:), NonZeroRational::multiply(with:), NonZeroRational::reciprocal(), NonZeroRational::negate()

NonNegativeRational

type NonNegativeRational = Rational where @::isGreaterThanOrEqualTo(0/1)

The Rationals from zero upward, as a checked refinement of Rational.

A receiver proven to be one of these has a real square root, which the bare squareRoot answers an Optional for. Addition and multiplication stay within the proof.

Tighter here add, multiply, squareRoot

Returned by IntegerList::variance(), IntegerList::variance(defaultingTo:), RationalList::variance(), RationalList::variance(defaultingTo:), NumberList::variance(), NumberList::variance(defaultingTo:), NonEmptyIntegerList::variance(), NonEmptyRationalList::variance(), NonEmptyNumberList::variance(), Rational::absolute(), NonNegativeRational::add(_:), NonNegativeRational::multiply(with:)

PositiveRational

type PositiveRational = Rational where @::isPositive()

The Rationals above zero, as a checked refinement of Rational.

A value above zero is neither zero nor below it, so one of these is accepted wherever a NonZeroRational or a NonNegativeRational is wanted. Addition with a value that is not negative, and multiplication with another of these, answer one of these again.

Tighter here add, multiply, squareRoot

Returned by NonZeroRational::absolute(), NonNegativeRational::add(_:), PositiveRational::add(_:), PositiveRational::multiply(with:), PositiveRational::squareRoot()

Modes

Each mode below is a choice whose cases carry no payload. You pass one to a method to choose how the method works. Every mode has is, isNot and toString, and cases() on its name lists its cases, as in NumberFormat.cases(). None of them is written out for a mode: Protocols says where each one comes from.

NumberFormat

choice NumberFormat {
	Fraction,
	Decimal,
	Percent,
	Scientific,
}

The forms Rational::toString writes: #Fraction is "3/4", #Decimal is "0.75", #Percent is "75%" and #Scientific is "7.5e-1".

The plain toString() entry writes the fraction form, and the as: entries name one of the four. The scientific form writes one digit before the point, and the power of ten it took out as e and an exponent. Only the fraction form and the decimal form read back through Rational.parse.

Cases #Fraction, #Decimal, #Percent, #Scientific

Taken by

Rounding

choice Rounding {
	Nearest,
	NearestEven,
	Down,
	Up,
	TowardZero,
}

Which step round reaches for a value that is not already on one.

#Nearest goes to the closest step, and away from zero for a value exactly halfway between two. The #NearestEven Case goes to the closest step as well. A value exactly halfway between two steps reaches the even one of them. The #Down Case answers the floor, #Up the ceiling, and #TowardZero the step towards zero. A call that names no rounding gets #Nearest.

Cases #Nearest, #NearestEven, #Down, #Up, #TowardZero

Taken by

SignStyle

choice SignStyle {
	Negative,
	Always,
}

Whether toString(showingSign:) writes a sign in front of a value that is not negative.

A negative value is written with its minus sign under both Cases. Under #Always a value that is not negative is written with a + in front, and zero is +0. A call that names #Negative gets what the plain toString() writes.

Cases #Negative, #Always

Taken by

esc