• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Wurst] Rational

Level 6
Joined
Aug 26, 2009
Messages
192
[WurstScript] Rational 1.2

Rational is a library for WurstScript which provides an easy way to make exact calculations with rational numbers. For this a tuple of integers is used. You should always keep in mind that the range of integers is much smaller than the range of reals.

There are no requirements except for the obligatory WurstScript StandardLib.

Code:
Wurst:
/**
 * A package containing a tuple for rational numbers. This gives the
 * possibility to make exact calculations with the loss of very small
 * or big numbers. The possible numbers are of the form
 * a / b with a and b being integers in the interval [-2^31, 2^31].
 * Reducing the fractions usually yields that b > 0, hence we have tuples
 * [-2^31, 2^31 - 1] x [1, 2^31 - 1] where x is the cartesian product.
 * Therefore the smallest positive number is 1 / (2^31 - 1) = 4.66e-10. The
 * greatest positive number is 2^31 / 1 = 2147483648. Keep in mind
 * that reals can become much bigger and smaller than rationals.
 */
package Rational

/** 
 * Tuple for rational numbers. Using this constructor
 * won't reduce the fraction, therefore you should use
 * reduce(int p, int q).
 *
 * int p - the numerator
 * int q - the denominator
 *
 * returns the unreduced fraction p/q
 */
public tuple rational(int p, int q)

/**
 * Reduces and returns the fraction created by p/q.
 *
 * int p - the numerator
 * int q - the denominator
 *
 * returns the reduced fraction p/q
 */
public function reduce(int p, int q) returns rational
	if q == 0
		error("Denominator must be unequal to 0!")
	int gcd = gcd(p, q)
	return rational(p div gcd, q div gcd)

/**
 * Creates a rational number from this integer.
 *
 * returns a rational representing this integer
 */
public function int.toRational() returns rational
	return rational(this, 1)

/**
 * Creates a rational number from an integer.
 * This uses the int.toRational() function internally.
 *
 * int i the integer to be converted
 *
 * returns a rational representing the given integer
 */
public function rational(int p) returns rational
	return p.toRational()

constant int pow15 = 32768
constant int pow30 = 1073741824

/**
 * Creates a rational number from this real. This conversion
 * might not be correct since reals can be much smaller or
 * greater then integers.
 *
 * returns a rational representing this real
 */
public function real.toRational() returns rational
	int sign = 1
	real r = this
	if (this < 0)
		sign = -1
		r = -this
	if (r <= 1)
		return reduce(sign * (r * pow30).toInt(),pow30)
	if (this <= pow15)
		return reduce(sign * (r * pow15).toInt(),pow15)
	return reduce(sign * r.toInt(), 1)

/**
 * Creates a rational number from a real.
 * This uses the real.toRational() function internally.
 *
 * real r the real to be converted
 *
 * returns a rational representing the given real
 */
public function rational(real r) returns rational
	return r.toRational()

/**
 * Overloaded + operation
 */
public function rational.op_plus(rational r) returns rational
	int gcd = gcd(this.q, r.q)
	return reduce(this.p * (r.q div gcd) + r.p * (this.q div gcd),
                    this.q div gcd * r.q)

/**
 * Overloaded + operation for adding reals. This uses real.toRational().
 */
public function rational.op_plus(real r) returns rational
	return this + r.toRational()

/**
 * Overloaded + operation for adding reals. This uses real.toRational().
 */
public function real.op_plus(rational r) returns rational
	return this.toRational() + r

/**
 * Overloaded - operation
 */
public function rational.op_minus(rational r) returns rational
	int gcd = gcd(this.q, r.q)
	return reduce(this.p * (r.q div gcd) - r.p * (this.q div gcd),
                    this.q div gcd * r.q)

/**
 * Overloaded - operation for subtracting reals. This uses real.toRational()
 * internally.
 */
public function rational.op_minus(real r) returns rational
	return this - r.toRational()

/**
 * Overloaded - operation for subtracting rationals from reals. This uses 
 * real.toRational() internally.
 */
public function real.op_minus(rational r) returns rational
	return this.toRational() - r
	
/**
 * Negates this rational number. The rational obtained by this usually
 * has the property res + this = (0,1). An exception is when this = (-2^31,q),
 * since 2^31-1 is the maximal integer.
 *
 * returns -this
 */
public function rational.negate() returns rational
	return rational(-this.p, this.q)

/**
 * Overloaded * operation
 */
public function rational.op_mult(rational r) returns rational
	int gcd1 = gcd(this.p, r.q)
	int gcd2 = gcd(this.q, r.p)
	return rational((this.p div gcd1) * (r.p div gcd2), 
					(this.q div gcd2) * (r.q div gcd1))

/**
 * Overloaded * operation for multiplying reals. This uses real.toRational()
 * internally.
 */
public function rational.op_mult(real r) returns rational
	return this * r.toRational()

/**
 * Overloaded * operation for multiplying reals. This uses real.toRational()
 * internally.
 */	
public function real.op_mult(rational r) returns rational
	return this.toRational() * r

/**
 * Overloaded / operation
 */
public function rational.op_divReal(rational r) returns rational
	int gcd1 = gcd(this.p, r.p)
	int gcd2 = gcd(this.q, r.q)
	return rational((this.p div gcd1) * (r.q div gcd2),
					(this.q div gcd2) * (r.p div gcd1))

/**
 * Overloaded / operation for dividing by reals. This uses real.toRational()
 * internally.
 */	
public function rational.op_divReal(real r) returns rational
	return this / r.toRational()

/**
 * Overloaded / operation for dividing reals by rationals. This uses
 * real.toRational() internally.
 */		
public function real.op_divReal(rational r) returns rational
	return this.toRational() / r
	
/**
 * Inverts this rational number. The rational obtained by this has
 * the property res * this = (1,1).
 *
 * returns 1/this
 */
public function rational.invert() returns rational
	if (this.p < 0)
		return rational(-this.q, -this.p)
	return rational(this.q, this.p)

/**
 * Calculates the remainder of this rational.
 * This is equivalent to this.p mod this.q.
 *
 * returns the remainder of this fraction
 */
public function rational.remainder() returns int
	return this.p mod this.q

/**
 * Calculates the absolute value of this rational
 *
 * returns |this|
 */
public function rational.abs() returns rational
	if this.p < 0
		return rational(-this.p, this.q)
	return this

/**
 * Compares this rational with the given rational. It
 * calculates this.p * r.q - this.q * r.p and returns the
 * result. Hence for this < r the result is negative, for
 * this > r the result is positive and for this == r the result
 * is 0.
 *
 * rational r - the rational to compared with
 *
 * returns an integer showing if this is greater, smaller or equal to r
 */
public function rational.compareTo(rational r) returns int
	rational res = this / r
	return res.p - res.q

/**
 * Compares this rational with the given rational. It
 * calculates this.p * r.q - this.q * r.p and returns true
 * if and only if this == r.
 *
 * rational r - the rational to compared with
 *
 * returns true if this rational is the same as r; false otherwise 
 */
public function rational.equals(rational r) returns boolean
	return this.compareTo(r) == 0

/**
 * Creates the real representation of this rational.
 *
 * returns a real represented by this rational
 */
public function rational.toReal() returns real
	return this.p / this.q

/**
 * Creates the integer representation of this rational.
 * This is equivalent to this.p div this.q.
 *
 * returns an int represented by this rational
 */
public function rational.toInt() returns int
	return this.p div this.q

/**
 * Creates a string representation of this rational in the
 * form "p/q".
 *
 * returns a string representing this rational
 */
public function rational.toString() returns string
	return this.p.toString() + "/" + this.q.toString()

/**
 * Calculates the greatest common divisor of p and q using
 * the euclidean algorithm.
 *
 * int p - the first integer
 * int q - the second integer
 *
 * returns the greatest common divisor of p and q
 */	
public function gcd(int p, int q) returns int
	int h
	int a = p
	int b = q
	while b != 0
		h = a mod b
		a = b
		b = h
	return a
Tests:
Wurst:
package RationalTest
import NoWurst
import Rational
import Wurstunit

@test function reduceTest()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	
	r1.p.assertEquals(-10)
	r1.q.assertEquals(1)
	r2.p.assertEquals(823)
	r2.q.assertEquals(4526)
	
@test function plusTest()
	rational r1 = reduce(11, 15)
	rational r2 = reduce(77, 20)
	
	rational res = r1 + r2
	res.p.assertEquals(55)
	res.q.assertEquals(12)
	
@test function minusTest()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	
	rational res = r1 - r2
	res.p.assertEquals(-46083)
	res.q.assertEquals(4526)
	
@test function multiplyTest()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	
	rational res = r1 * r2
	res.p.assertEquals(-4115)
	res.q.assertEquals(2263)
	
@test function divideTest()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	
	rational res = r1 / r2
	res.p.assertEquals(-45260)
	res.q.assertEquals(823)

@test function negateTest()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	
	rational res = r1 + r1.negate()
	res.p.assertEquals(0)
	res.q.assertEquals(1)
	
	res = r2.negate()
	res.p.assertEquals(-823)
	res.q.assertEquals(4526)

@test function invertTest()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	
	rational res = r1.invert()
	res.p.assertEquals(-1)
	res.q.assertEquals(10)
	
	res = r2 * r2.invert()
	res.p.assertEquals(1)
	res.q.assertEquals(1)
	
@test function someOtherStuff()
	rational r1 = reduce(100, -10)
	rational r2 = reduce(12345, 67890)
	rational res1
	int res2
	boolean bool
	
	res1 = r1.abs()
	res1.p.assertEquals(10)
	res1.q.assertEquals(1)
	
	res2 = r2.invert().remainder()
	res2.assertEquals(411)
	
	res2 = r1.compareTo(r2)
	res2.assertEquals(-46083)
	
	bool = r1.equals(r2)
	bool.assertFalse()
GitHub:
Rational

Changelog:
1.0 - Initial release
1.1 - Added errorhandling
1.2 - Improved addition and subtraction and made it compatible with the current Wurst version

Credits:
- Penguins
- muzzel
 
Last edited:
Top