Category talk:Wren-rat: Difference between revisions
Content added Content deleted
(→Source code: Typo in comment.) |
m (→Source code: Now uses Wren S/H lexer.) |
||
(13 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
===Source code=== |
===Source code=== |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
/* Rat represents a rational number as an integral numerator and (non-zero) denominator |
/* Rat represents a rational number as an integral numerator and (non-zero) denominator |
||
Line 9: | Line 8: | ||
*/ |
*/ |
||
class Rat is Comparable { |
class Rat is Comparable { |
||
// Maximum safe rational number = 2^53 - 1. |
|||
⚫ | |||
// Private helper function to check that 'o' is a suitable type and throw an error otherwise. |
// Private helper function to check that 'o' is a suitable type and throw an error otherwise. |
||
// Numbers and numeric strings are returned as rationals. |
// Numbers and numeric strings are returned as rationals. |
||
Line 17: | Line 13: | ||
if (o is Rat) return o |
if (o is Rat) return o |
||
if (o is Num) return Rat.fromFloat(o) |
if (o is Num) return Rat.fromFloat(o) |
||
if (o is String) return |
if (o is String) return (o.contains("_") && o.contains("/")) ? fromMixedString(o) : |
||
o.contains("/") ? fromRationalString(o) : fromString(o) |
|||
Fiber.abort("Argument must either be a rational number, a number or a numeric string.") |
Fiber.abort("Argument must either be a rational number, a number or a numeric string.") |
||
} |
} |
||
Line 42: | Line 39: | ||
// Constants. |
// Constants. |
||
static |
static minusOne { Rat.new( -1, 1) } |
||
static |
static zero { Rat.new( 0, 1) } |
||
static |
static one { Rat.new( 1, 1) } |
||
static |
static two { Rat.new( 2, 1) } |
||
static |
static three { Rat.new( 3, 1) } |
||
static |
static four { Rat.new( 4, 1) } |
||
static five { Rat.new( 5, 1) } |
|||
static six { Rat.new( 6, 1) } |
|||
static seven { Rat.new( 7, 1) } |
|||
static eight { Rat.new( 8, 1) } |
|||
static nine { Rat.new( 9, 1) } |
|||
static ten { Rat.new( 10, 1) } |
|||
static half { Rat.new( 1, 2) } |
|||
static third { Rat.new( 1, 3) } |
|||
static quarter { Rat.new( 1, 4) } |
|||
static fifth { Rat.new( 1, 5) } |
|||
static sixth { Rat.new( 1, 6) } |
|||
static seventh { Rat.new( 1, 7) } |
|||
static eighth { Rat.new( 1, 8) } |
|||
static ninth { Rat.new( 1, 9) } |
|||
static tenth { Rat.new( 1, 10) } |
|||
// Constructs a new Rat object by passing it a numerator and a denominator. |
// Constructs a new Rat object by passing it a numerator and a denominator. |
||
Line 71: | Line 83: | ||
_d = d |
_d = d |
||
} |
} |
||
// Convenience method which constructs a new Rat object by passing it just a numerator. |
|||
⚫ | |||
// Constructs a rational number from an integer. |
// Constructs a rational number from an integer. |
||
Line 86: | Line 101: | ||
static fromString(s) { |
static fromString(s) { |
||
var n |
var n |
||
s = s.trim() |
|||
if (!(n = Num.fromString(s))) Fiber.abort("Argument must be a numeric string.") |
if (!(n = Num.fromString(s))) Fiber.abort("Argument must be a numeric string.") |
||
if (n.isInteger) return Rat.new(n, 1) |
if (n.isInteger) return Rat.new(n, 1) |
||
return fromDecimalString_(s |
return fromDecimalString_(s.trimEnd("0")) |
||
} |
} |
||
// Constructs a rational number from a string of the form "n/d". |
// Constructs a rational number from a string of the form "n/d". |
||
// Improper fractions are allowed. |
|||
static fromRationalString(s) { |
static fromRationalString(s) { |
||
s = s.trim() |
s = s.trim() |
||
Line 101: | Line 118: | ||
return Rat.new(n, d) |
return Rat.new(n, d) |
||
} |
} |
||
// Constructs a rational number from a string of the form "i_n/d" where 'i' is an integer. |
|||
// Improper and negative fractional parts are allowed. |
|||
static fromMixedString(s) { |
|||
var ind = s.split("_") |
|||
if (ind.count != 2) Fiber.abort("Argument is not a suitable string.") |
|||
var nd = fromRationalString(ind[1]) |
|||
var i = Rat.fromString(ind[0]) |
|||
var neg = i.isNegative || (i.isZero && ind[0][0] == "-") |
|||
return neg ? i - nd : i + nd |
|||
} |
|||
// Returns the greater of two rational numbers. |
|||
static max(r1, r2) { (r1 < r2) ? r2 : r1 } |
|||
// Returns the smaller of two rational numbers. |
|||
static min(r1, r2) { (r1 < r2) ? r1 : r2 } |
|||
// Private helper method to compare two integers. |
|||
static compareInts_(i, j) { (i - j).sign } |
|||
// Determines whether a Rat object is always shown as such or, if integral, as an integer. |
// Determines whether a Rat object is always shown as such or, if integral, as an integer. |
||
Line 107: | Line 144: | ||
// Basic properties. |
// Basic properties. |
||
num { _n } // numerator |
num { _n } // numerator |
||
den { _d } // denominator |
den { _d } // denominator |
||
ratio { [_n, _d] } // a two element list of the above |
ratio { [_n, _d] } // a two element list of the above |
||
isInteger { toFloat.isInteger } // checks if integral or not |
|||
isPositive { _n > 0 } // checks if positive |
|||
isNegative { _n < 0 } // checks if negative |
|||
isUnit { _n.abs == 1 } // checks if plus or minus one |
|||
isZero { _n == 0 } // checks if zero |
|||
// Rounding methods (similar to those in Num class). |
// Rounding methods (similar to those in Num class). |
||
ceil { Rat.fromInt( |
ceil { Rat.fromInt(toFloat.ceil) } // higher integer |
||
floor { Rat.fromInt( |
floor { Rat.fromInt(toFloat.floor) } // lower integer |
||
truncate { Rat.fromInt( |
truncate { Rat.fromInt(toFloat.truncate) } // lower integer, towards zero |
||
round { Rat.fromInt( |
round { Rat.fromInt(toFloat.round) } // nearer integer |
||
roundUp { this >= 0 ? ceil : floor } // higher integer, away from zero |
|||
fraction { this - truncate } // fractional part (same sign as this.num) |
|||
// Reciprocal |
// Reciprocal |
||
Line 133: | Line 174: | ||
-(o) { (o = Rat.check_(o)) && (this + (-o)) } |
-(o) { (o = Rat.check_(o)) && (this + (-o)) } |
||
*(o) { (o = Rat.check_(o)) && Rat.new(_n * o.num, _d * o.den) } |
*(o) { (o = Rat.check_(o)) && Rat.new(_n * o.num, _d * o.den) } |
||
/(o) { (o = Rat.check_(o)) && ( |
/(o) { (o = Rat.check_(o)) && Rat.new(_n * o.den, _d * o.num) } |
||
%(o) { (o = Rat.check_(o)) && (this |
%(o) { (o = Rat.check_(o)) && (this - idiv(o) * o) } |
||
// Computes integral powers. |
// Computes integral powers. |
||
pow(i) { |
pow(i) { |
||
if (!((i is Num) && i.isInteger)) Fiber.abort("Argument must be an |
if (!((i is Num) && i.isInteger)) Fiber.abort("Argument must be an integer.") |
||
if (i == 0) return |
if (i == 0) return Rat.one |
||
if (i == 1) return this.copy() |
|||
if (i == -1) return this.inverse |
|||
var np = _n.pow(i.abs).round |
|||
var dp = _d.pow(i.abs).round |
|||
return (i > 0) ? Rat.new(np, dp) : Rat.new(dp, np) |
return (i > 0) ? Rat.new(np, dp) : Rat.new(dp, np) |
||
} |
} |
||
// Returns the square of the current instance. |
|||
square { Rat.new(_n * _n , _d *_d) } |
|||
// Other methods. |
// Other methods. |
||
inc { this + Rat.one } // increment |
inc { this + Rat.one } // increment |
||
dec { this - Rat.one } // decrement |
dec { this - Rat.one } // decrement |
||
abs { (_n >= 0) ? |
abs { (_n >= 0) ? copy() : -this } // absolute value |
||
sign { _n.sign } // sign |
sign { _n.sign } // sign |
||
// The inherited 'clone' method just returns 'this' as Rat objects are immutable. |
// The inherited 'clone' method just returns 'this' as Rat objects are immutable. |
||
Line 156: | Line 202: | ||
// Compares this Rat with another one to enable comparison operators via Comparable trait. |
// Compares this Rat with another one to enable comparison operators via Comparable trait. |
||
compare(other) { |
compare(other) { |
||
if ((other is Num) && other.isInfinity) return -other.sign |
|||
other = Rat.check_(other) |
|||
if (_d == other.den) return Rat.compareInts_(_n, other.num) |
|||
return Rat.compareInts_(_n * other.den, other.num * _d) |
|||
} |
|||
// As above but compares the absolute values of the Rats. |
|||
compareAbs(other) { this.abs.compare(other.abs) } |
|||
// Converts the current instance to a Num. |
|||
toFloat { _n/_d } |
|||
// Converts the current instance to an integer with any fractional part truncated. |
|||
toInt { this.toFloat.truncate } |
|||
// Returns a string represenation of this instance in the form "i_n/d" where 'i' is an integer. |
|||
toMixedString { |
|||
var q = _n / _d |
|||
var sign = q < 0 ? "-" : "" |
|||
q = q.abs.truncate |
|||
var r = _n.abs % _d |
|||
return sign + q.toString + "_" + r.toString + "/" + _d.toString |
|||
} |
|||
// Returns the string representation of this Rat object depending on 'showAsInt'. |
// Returns the string representation of this Rat object depending on 'showAsInt'. |
||
Line 169: | Line 238: | ||
static max(a) { a.reduce { |acc, x| (x > acc) ? x : acc } } |
static max(a) { a.reduce { |acc, x| (x > acc) ? x : acc } } |
||
static min(a) { a.reduce { |acc, x| (x < acc) ? x : acc } } |
static min(a) { a.reduce { |acc, x| (x < acc) ? x : acc } } |
||
}</syntaxhighlight> |
|||
} |
|||
// Type aliases for classes in case of any name clashes with other modules. |
|||
var Rat_Rat = Rat |
|||
var Rat_Rats = Rats |
|||
var Rat_Comparable = Comparable // in case imported indirectly</lang> |
Latest revision as of 11:59, 3 November 2023
Source code
/* Module "rat.wren" */
import "./trait" for Comparable
/* Rat represents a rational number as an integral numerator and (non-zero) denominator
expressed in their lowest terms. Rat objects are immutable.
*/
class Rat is Comparable {
// Private helper function to check that 'o' is a suitable type and throw an error otherwise.
// Numbers and numeric strings are returned as rationals.
static check_(o) {
if (o is Rat) return o
if (o is Num) return Rat.fromFloat(o)
if (o is String) return (o.contains("_") && o.contains("/")) ? fromMixedString(o) :
o.contains("/") ? fromRationalString(o) : fromString(o)
Fiber.abort("Argument must either be a rational number, a number or a numeric string.")
}
// Private helper function which returns the greatest common divisor of 'n' and 'd'.
static gcd_(n, d) {
while (d != 0) {
var t = d
d = n % d
n = t
}
return n
}
// Private helper method which constructs a Rat object from a non-integral numeric string.
static fromDecimalString_(s) {
if (s.contains("e")) Fiber.abort("Argument is out of range.")
var ix = s.indexOf(".")
var dp = s[ix+1..-1]
var den = (10.pow(dp.count)).round
var num = Num.fromString(s[0...ix] + dp)
return Rat.new(num, den)
}
// Constants.
static minusOne { Rat.new( -1, 1) }
static zero { Rat.new( 0, 1) }
static one { Rat.new( 1, 1) }
static two { Rat.new( 2, 1) }
static three { Rat.new( 3, 1) }
static four { Rat.new( 4, 1) }
static five { Rat.new( 5, 1) }
static six { Rat.new( 6, 1) }
static seven { Rat.new( 7, 1) }
static eight { Rat.new( 8, 1) }
static nine { Rat.new( 9, 1) }
static ten { Rat.new( 10, 1) }
static half { Rat.new( 1, 2) }
static third { Rat.new( 1, 3) }
static quarter { Rat.new( 1, 4) }
static fifth { Rat.new( 1, 5) }
static sixth { Rat.new( 1, 6) }
static seventh { Rat.new( 1, 7) }
static eighth { Rat.new( 1, 8) }
static ninth { Rat.new( 1, 9) }
static tenth { Rat.new( 1, 10) }
// Constructs a new Rat object by passing it a numerator and a denominator.
construct new(n, d) {
if (!(n is Num && n.isInteger)) Fiber.abort("Numerator must be an integer.")
if (!(d is Num && d.isInteger && d != 0)) {
Fiber.abort("Denominator must be a non-zero integer.")
}
if (n.abs > 9007199254740991) Fiber.abort("Numerator is out of range.")
if (d.abs > 9007199254740991) Fiber.abort("Denominator is out of range.")
if (n == 0) {
d = 1
} else if (d < 0) {
n = -n
d = -d
}
var g = Rat.gcd_(n, d).abs
if (g > 1) {
n = (n/g).truncate
d = (d/g).truncate
}
_n = n
_d = d
}
// Convenience method which constructs a new Rat object by passing it just a numerator.
static new(n) { Rat.new(n, 1) }
// Constructs a rational number from an integer.
static fromInt(i) { Rat.new(i, 1) }
// Constructs a rational number from a floating point number.
static fromFloat(f) {
if (!(f is Num)) Fiber.abort("Argument must be a number.")
if (f.isInteger) return Rat.new(f, 1)
var s = "%(f)"
return fromDecimalString_(s)
}
// Constructs a rational number from a numeric string.
static fromString(s) {
var n
s = s.trim()
if (!(n = Num.fromString(s))) Fiber.abort("Argument must be a numeric string.")
if (n.isInteger) return Rat.new(n, 1)
return fromDecimalString_(s.trimEnd("0"))
}
// Constructs a rational number from a string of the form "n/d".
// Improper fractions are allowed.
static fromRationalString(s) {
s = s.trim()
var nd = s.split("/")
if (nd.count != 2) Fiber.abort("Argument is not a suitable string.")
var n = Num.fromString(nd[0])
var d = Num.fromString(nd[1])
if (!n || !d) Fiber.abort("Argument is not a suitable string.")
return Rat.new(n, d)
}
// Constructs a rational number from a string of the form "i_n/d" where 'i' is an integer.
// Improper and negative fractional parts are allowed.
static fromMixedString(s) {
var ind = s.split("_")
if (ind.count != 2) Fiber.abort("Argument is not a suitable string.")
var nd = fromRationalString(ind[1])
var i = Rat.fromString(ind[0])
var neg = i.isNegative || (i.isZero && ind[0][0] == "-")
return neg ? i - nd : i + nd
}
// Returns the greater of two rational numbers.
static max(r1, r2) { (r1 < r2) ? r2 : r1 }
// Returns the smaller of two rational numbers.
static min(r1, r2) { (r1 < r2) ? r1 : r2 }
// Private helper method to compare two integers.
static compareInts_(i, j) { (i - j).sign }
// Determines whether a Rat object is always shown as such or, if integral, as an integer.
static showAsInt { __showAsInt }
static showAsInt=(b) { __showAsInt = b }
// Basic properties.
num { _n } // numerator
den { _d } // denominator
ratio { [_n, _d] } // a two element list of the above
isInteger { toFloat.isInteger } // checks if integral or not
isPositive { _n > 0 } // checks if positive
isNegative { _n < 0 } // checks if negative
isUnit { _n.abs == 1 } // checks if plus or minus one
isZero { _n == 0 } // checks if zero
// Rounding methods (similar to those in Num class).
ceil { Rat.fromInt(toFloat.ceil) } // higher integer
floor { Rat.fromInt(toFloat.floor) } // lower integer
truncate { Rat.fromInt(toFloat.truncate) } // lower integer, towards zero
round { Rat.fromInt(toFloat.round) } // nearer integer
roundUp { this >= 0 ? ceil : floor } // higher integer, away from zero
fraction { this - truncate } // fractional part (same sign as this.num)
// Reciprocal
inverse { Rat.new(_d, _n) }
// Integer division.
idiv(o) { (this/o).truncate }
// Negation.
-{ Rat.new(-_n, _d) }
// Arithmetic operators (work with numbers and numeric strings as well as other rationals).
+(o) { (o = Rat.check_(o)) && Rat.new(_n * o.den + _d * o.num, _d * o.den) }
-(o) { (o = Rat.check_(o)) && (this + (-o)) }
*(o) { (o = Rat.check_(o)) && Rat.new(_n * o.num, _d * o.den) }
/(o) { (o = Rat.check_(o)) && Rat.new(_n * o.den, _d * o.num) }
%(o) { (o = Rat.check_(o)) && (this - idiv(o) * o) }
// Computes integral powers.
pow(i) {
if (!((i is Num) && i.isInteger)) Fiber.abort("Argument must be an integer.")
if (i == 0) return Rat.one
if (i == 1) return this.copy()
if (i == -1) return this.inverse
var np = _n.pow(i.abs).round
var dp = _d.pow(i.abs).round
return (i > 0) ? Rat.new(np, dp) : Rat.new(dp, np)
}
// Returns the square of the current instance.
square { Rat.new(_n * _n , _d *_d) }
// Other methods.
inc { this + Rat.one } // increment
dec { this - Rat.one } // decrement
abs { (_n >= 0) ? copy() : -this } // absolute value
sign { _n.sign } // sign
// The inherited 'clone' method just returns 'this' as Rat objects are immutable.
// If you need an actual copy use this method instead.
copy() { Rat.new(_n, _d) }
// Compares this Rat with another one to enable comparison operators via Comparable trait.
compare(other) {
if ((other is Num) && other.isInfinity) return -other.sign
other = Rat.check_(other)
if (_d == other.den) return Rat.compareInts_(_n, other.num)
return Rat.compareInts_(_n * other.den, other.num * _d)
}
// As above but compares the absolute values of the Rats.
compareAbs(other) { this.abs.compare(other.abs) }
// Converts the current instance to a Num.
toFloat { _n/_d }
// Converts the current instance to an integer with any fractional part truncated.
toInt { this.toFloat.truncate }
// Returns a string represenation of this instance in the form "i_n/d" where 'i' is an integer.
toMixedString {
var q = _n / _d
var sign = q < 0 ? "-" : ""
q = q.abs.truncate
var r = _n.abs % _d
return sign + q.toString + "_" + r.toString + "/" + _d.toString
}
// Returns the string representation of this Rat object depending on 'showAsInt'.
toString { (Rat.showAsInt && _d == 1) ? "%(_n)" : "%(_n)/%(_d)" }
}
/* Rats contains various routines applicable to lists of rational numbers */
class Rats {
static sum(a) { a.reduce(Rat.zero) { |acc, x| acc + x } }
static mean(a) { sum(a)/a.count }
static prod(a) { a.reduce(Rat.one) { |acc, x| acc * x } }
static max(a) { a.reduce { |acc, x| (x > acc) ? x : acc } }
static min(a) { a.reduce { |acc, x| (x < acc) ? x : acc } }
}