Category talk:Wren-long: Difference between revisions

Content added Content deleted
(→‎Source code: Added a second divisors method.)
(→‎Source code: Added support for signed 64 bit arithmetic (Long class).)
Line 60: Line 60:
static smallest { lohi_(0, 0) }
static smallest { lohi_(0, 0) }


// Private method to determine whether a number is a short unsigned integer or not.
// Private method to determine whether a number is a short unsigned integer or not.
static isShort_(n) { (n is Num) && n.isInteger && n >= 0 && n <= 4294967295 }
static isShort_(n) { (n is Num) && n.isInteger && n >= 0 && n <= 4294967295 }
// Private method to determine whether a number is a small unsigned integer or not.
// Private method to determine whether a number is a small unsigned integer or not.
static isSmall_(n) { (n is Num) && n.isInteger && n >= 0 && n <= 9007199254740991 }
static isSmall_(n) { (n is Num) && n.isInteger && n >= 0 && n <= 9007199254740991 }

// Private method to determine whether a number is a non-negative Long or not.
static isNonNegLong_(n) { (n is Long) && !n.isNegative }


// Private method to calculate the base 2 logarithm of a number.
// Private method to calculate the base 2 logarithm of a number.
Line 343: Line 346:
}
}


// Creates a ULong object from either a numeric base 10 string or an unsigned 'small' integer.
// Creates a ULong object from either a numeric base 10 string, an unsigned 'small' integer or a non-negative Long.
static new(value) {
static new(value) {
if (!(value is String) && !isSmall_(value)) {
if (!(value is String) && !isSmall_(value) && !isNonNegLong_(value)) {
Fiber.abort("Value must be a base 10 numeric string or an unsigned small integer.")
Fiber.abort("Value must be a base 10 numeric string, an unsigned small integer or a non-negative Long.")
}
}
return (value is String) ? fromString_(value) : fromSmall_(value)
return (value is String) ? fromString_(value) : (value is Num) ? fromSmall_(value) : value.toULong
}
}


Line 588: Line 591:
min(n) { ULong.min(this, n) }
min(n) { ULong.min(this, n) }


// Clamps this instance into the range [a, b].
// Clamps this instance into the range [a, b].
// If this instance is less than min, min is returned.
// If this instance is less than min, min is returned.
// If it's more than max, max is returned. Otherwise, this instance is returned.
// If it's more than max, max is returned. Otherwise, this instance is returned.
clamp(a, b) {
clamp(a, b) {
Line 618: Line 621:
}
}


// Returns the integer n'th root of the current instance
// Returns the integer n'th root of the current instance
// i.e. the precise n'th root truncated towards zero if not an integer.
// i.e. the precise n'th root truncated towards zero if not an integer.
iroot(n) {
iroot(n) {
Line 746: Line 749:
// Expresses the current instance as a list of 8 unsigned bytes in little-endian format.
// Expresses the current instance as a list of 8 unsigned bytes in little-endian format.
toBytes {
toBytes {
return [_lo & 0xff, _lo >> 8 & 0xff, _lo >> 16 & 0xff, _lo >> 24 ,
return [_lo & 0xff, _lo >> 8 & 0xff, _lo >> 16 & 0xff, _lo >> 24 ,
_hi & 0xff, _hi >> 8 & 0xff, _hi >> 16 & 0xff, _hi >> 24]
_hi & 0xff, _hi >> 8 & 0xff, _hi >> 16 & 0xff, _hi >> 24]
}
}

// Converts the current instance to a Long.
toLong { Long.sigma_(sign, this) }


// Private worker method for toBaseString, toHexString and toString.
// Private worker method for toBaseString, toHexString and toString.
Line 924: Line 930:
divs.add(i)
divs.add(i)
var j = n / i
var j = n / i
if (j != i) divs2.add(j)
if (j != i) divs2.add(j)
}
}
i = i + k
i = i + k
Line 971: Line 977:
static mean(a) { sum(a)/a.count }
static mean(a) { sum(a)/a.count }
static prod(a) { a.reduce(ULong.one) { |acc, x| acc * x } }
static prod(a) { a.reduce(ULong.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 } }
}

/*
Long represents a 64-bit signed integer together with arithmetic operations thereon.
Long objects, which are immutable, are stored as a magnitude (ULong) and a sign
+1 for positive numbers, 0 for zero and -1 for negative numbers.
*/
class Long is Comparable {
// Constants
static minusOne { sigma_(-1, ULong.one) }
static zero { sigma_( 0, ULong.zero) }
static one { sigma_( 1, ULong.one) }
static two { sigma_( 1, ULong.two) }
static three { sigma_( 1, ULong.three) }
static four { sigma_( 1, ULong.four) }
static five { sigma_( 1, ULong.five) }
static ten { sigma_( 1, ULong.ten) }

// Returns the maximum 'short' Long = 2^32-1 = 4294967295
static maxShort { sigma_(1, ULong.maxShort) }

// Returns the minimum 'short' Long = -(2^32-1) = -4294967295
static minShort { sigma_(-1, ULong.maxShort) }

// Returns the maximum 'small' Long = 2^53-1 = 9007199254740991
static maxSmall { sigma_(1, ULong.maxSmall) }

// Returns the minimum 'small' Long = -(2^53-1) = -9007199254740991
static minSmall { sigma_(-1, ULong.maxSmall) }

// Returns the largest Long = 2^64-1 = 18446744073709551615
static largest { sigma_(1, ULong.largest) }

// Returns the smallest Long = -(2^64-1) = -18446744073709551615
static smallest { sigma_(-1, ULong.smallest) }

// Private method to determine whether a number is a short integer or not.
static isShort_(n) { (n is Num) && n.isInteger && n.abs <= 4294967295 }

// Private method to determine whether a number is a small integer or not.
static isSmall_(n) { (n is Num) && n.isInteger && n.abs <= 9007199254740991 }

// Returns the greater of two Longs.
static max(a, b) {
if (!(a is Long)) a = new(a)
if (!(b is Long)) b = new(b)
return (a > b) ? a : b
}

// Returns the lesser of two Longs.
static min(a, b) {
if (!(a is Long)) a = new(a)
if (!(b is Long)) b = new(b)
return (a < b) ? a : b
}

// Returns the greatest common divisor of a and b. The result is never negative.
static gcd(a, b) {
if (!(a is Long)) a = new(a)
if (!(b is Long)) b = new(b)
while (!b.isZero) {
var t = b
b = a % b
a = t
}
return a.abs
}

// Returns the least common multiple of a and b. The result is never negative.
static lcm(a, b) {
if (!(a is Long)) a = new(a)
if (!(b is Long)) b = new(b)
if (a.isZero && b.isZero) return Long.zero
return (a * b).abs / gcd(a, b)
}

// Returns whether or not 'n' is an instance of Long.
static isInstance(n) { n is Long }

// Private constructor which creates a Long object from a sign and a ULong.
construct sigma_(sig, mag) {
_sig = sig
_mag = mag
if (mag.isZero && sig != 0) _sig = 0
}

// Private constructor which creates a Long object from a 'small' integer.
construct fromSmall_(v) {
_sig = v.sign
_mag = ULong.fromSmall_(v.abs)
}

// Private method which creates a Long object from a Num.
// If 'v' is not small, will probably lose accuracy.
static fromNum_(v) { sigma_(v.sign, ULong.fromNum_(v.abs)) }

// Private method which creates a Long object from a base 10 numeric string.
// A leading sign is permitted and so is scientific notation.
// Raises an error if the result is out of bounds.
static fromString_(v) {
v = v.trim()
if (v.count == 0) Fiber.abort("Invalid integer.")
var sig
var mag
if (v[0] == "-") {
sig = -1
mag = ULong.fromString_(v[1..-1])
} else {
sig = 1
mag = ULong.fromString_(v)
}
if (mag == ULong.zero) sig = 0
return sigma_(sig, mag)
}

// Creates a Long object from an (unprefixed) numeric string in a given base (2 to 36).
// A leading sign is permitted but scientific notation is not.
// Wraps out of range values.
static fromBaseString(v, base) {
v = v.trim()
if (v.count == 0) Fiber.abort("Invalid integer.")
var sig
var mag
if (v[0] == "-") {
sig = -1
mag = ULong.fromBaseString(v[1..-1], base)
} else {
sig = 1
mag = ULong.fromBaseString(v, base)
}
if (mag == ULong.zero) sig = 0
return sigma_(sig, mag)
}

// Creates a Long object from either a numeric base 10 string, a 'small' integer or a ULong.
static new(value) {
if (!(value is String) && !isSmall_(value) && !(value is ULong)) {
Fiber.abort("Value must be a base 10 numeric string, a small integer or a ULong.")
}
return (value is String) ? fromString_(value) : (value is Num) ? fromSmall_(value) : value.toLong
}

// Creates a Long object from an (unprefixed) hexadecimal string. Wraps out of range values.
static fromHexString(v) { fromBaseString(v, 16) }

// Properties to return the sign and magnitude of this instance.
sign { _sig }
magnitude { _mag }
mag { _mag } // synonym for magnitude

// Public self-evident properties.
isShort { _mag.isShort }
isSmall { _mag.isSmall }
isEven { _mag.isEven }
isOdd { _mag.isOdd }
isOne { _mag.isOne && _sig == 1 }
isUnit { _mag.isOne }
isZero { _sig == 0 }
isPositive { _sig == 1 }
isNegative { _sig == -1 }

// Returns true if 'n' is a divisor of the current instance, false otherwise
isDivisibleBy(n) { (this % n).isZero }

// negates 'this'
- { Long.sigma_(-_sig, _mag) }

// Adds a Long to the current instance. Wraps on overflow.
+ (n) {
if (!(n is Long)) n = Long.new(n)
var res
if (_sig >= 0 && n.sign >= 0) {
res = Long.sigma_(1, _mag + n.mag)
} else if (_sig >= 0 && n.sign < 0) {
res = (_mag >= n.mag) ? Long.sigma_(1, _mag - n.mag) : Long.sigma_(-1, n.mag - _mag)
} else if (_sig < 0 && n.sign >= 0) {
res = (n.mag >= _mag) ? Long.sigma_(1, n.mag - _mag) : Long.sigma_(-1, _mag - n.mag)
} else { // _sig < 0 && n.sign < 0)
res = Long.sigma_(-1, _mag + n.mag)
}
return res
}

// Subtracts a Long from the current instance. Wraps on underflow.
- (n) { this + (-n) }

// Multiplies the current instance by a Long. Wraps on overflow.
* (n) {
if (!(n is Long)) n = Long.new(n)
return Long.sigma_(_sig * n.sign, _mag * n.mag)
}

// Returns a list containing the quotient and the remainder after dividing
// the current instance by a Long. The remainder always has the same sign as 'this'.
divMod(n) {
if (!(n is Long)) n = Long.new(n)
if (n.isZero) Fiber.abort("Cannot divide by zero.")
var dm = _mag.divMod(n.mag)
return [Long.sigma_(_sig / n.sign, dm[0]), Long.sigma_(_sig, dm[1])]
}

// Divides the current instance by a Long.
/ (n) { divMod(n)[0] }

// Returns the remainder after dividing the current instance by a Long.
% (n) { divMod(n)[1] }

// Returns the absolute value of 'this'.
abs { isNegative ? -this : this }

// The inherited 'clone' method just returns 'this' as Long objects are immutable.
// If you need an actual copy use this method instead.
copy() { Long.sigma_(_sig, _mag) }

// Compares the current instance with a Long. If they are equal returns 0.
// If 'this' is greater, returns 1. Otherwise returns -1.
// Also allows a comparison with positive or negative infinity.
compare(n) {
if ((n is Num) && n.isInfinity && n > 0) return -1
if ((n is Num) && n.isInfinity && n < 0) return 1
if (!(n is Long)) n = Long.new(n)
if (_sig == n.sign && _mag == n.mag) return 0
if (_sig >= 0 && n.sign >= 0) return (_mag > n.mag) ? 1 : -1
if (_sig >= 0 && n.sign < 0) return 1
if (_sig < 0 && n.sign >= 0) return -1
return (n.mag > _mag) ? 1 : -1
}

// Returns the greater of this instance and another Long instance.
max(n) { Long.max(this, n) }

// Returns the smaller of this instance and another Long instance.
min(n) { Long.min(this, n) }

// Clamps this instance into the range [a, b].
// If this instance is less than min, min is returned.
// If it's more than max, max is returned. Otherwise, this instance is returned.
clamp(a, b) {
if (!(a is Long)) a = Long.new(a)
if (!(b is Long)) b = Long.new(b)
if (a > b) Fiber.abort("Range cannot be decreasing.")
if (this < a) return a
if (this > b) return b
return this.copy()
}

// Squares the current instance. Wraps on overflow.
square { this * this }

// Cubes the current instance. Wraps on overflow.
cube { this * this * this }

// Returns true if the current instance is a perfect square, false otherwise.
isSquare {
if (isNegative) System.print("A negative real number cannot be a perfect square.")
return _mag.isSquare
}

// Returns true if the current instance is a perfect cube, false otherwise.
isCube { _mag.isCube }

// Returns the integer n'th root of the current instance
// i.e. the precise n'th root truncated towards zero if not an integer.
iroot(n) {
if (!((n is Num) && n.isInteger && n > 0)) {
Fiber.abort("Argument must be a positive integer.")
}
if (isNegative && n.isEven) Fiber.abort("A negative real number cannot have an even root.")
return Long.sigma_(_sig, _mag.iroot(n))
}

// Returns the integer cube root of the current instance
// i.e. the precise cube root truncated towards zero if not an integer.
icbrt { Long.sigma_(_sig, _mag.icbrt) }

// Returns the integer square root of the current instance
// i.e. the precise sqaure root truncated towards zero if not an integer.
isqrt {
if (isNegative) System.print("A negative real number cannot have a square root.")
return Long.sigma_(_sig, _mag.isqrt)
}

// Returns the current instance raised to the power of a 'small' ULong. Wraps on overflow.
// If the exponent is less than 0, returns 0. O.pow(0) returns one.
pow(n) {
var mag = _mag.pow(n)
var sign
if (mag == ULong.zero) {
sign = 0
} else if (isNegative && n % 2 == 1) {
sign = -1
} else {
sign = 1
}
return Long.sigma_(sign, mag)
}

// Returns the current instance multiplied by 'n' modulo 'mod'.
modMul(n, mod) { Long.sigma_(_sig, _mag.modMul(n, mod)) }

// Returns the current instance to the power 'exp' modulo 'mod'.
modPow(exp, mod) {
var mag = _mag.modPow(exp, mod)
var sign
if (mag == ULong.zero) {
sign = 0
} else if (isNegative && (exp % mod).isOdd) {
sign = -1
} else {
sign = 1
}
return Long.sigma_(sign, mag)
}

// Increments the current instance by one.
inc { this + Long.one }

// Decrements the current instance by one.
dec { this - Long.one }

// Converts the current instance to a Num where possible.
// Will probably lose accuracy if the current instance is not 'small'.
toNum { _sig * _mag.toNum }

// Converts the current instance to a 'small' integer where possible.
// Otherwise returns null.
toSmall { isSmall ? toNum : null }

// Converts the current instance to a ULong where possible.
// Otherwise returns null.
toULong { !isNegative ? _mag : null }

// Returns the string representation of the current instance in a given base (2 to 36).
toBaseString(base) {
var bs = _mag.toBaseString(base)
return isNegative ? "-" + bs : bs
}

// Returns the string representation of the current instance in base 16.
toHexString { toBaseString(16) }

// Returns the string representation of the current instance in base 10.
toString { toBaseString(10) }
}

/* Longs contains various routines applicable to lists of signed 64-bit integers. */
class Longs {
static sum(a) { a.reduce(Long.zero) { |acc, x| acc + x } }
static mean(a) { sum(a)/a.count }
static prod(a) { a.reduce(Long.one) { |acc, x| acc * x } }
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 } }