diff options
| author | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
|---|---|---|
| committer | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
| commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
| tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/old/vlib/math/fractions | |
| download | cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2 cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.zip  | |
Diffstat (limited to 'v_windows/v/old/vlib/math/fractions')
| -rw-r--r-- | v_windows/v/old/vlib/math/fractions/approximations.v | 119 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/math/fractions/approximations_test.v | 189 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/math/fractions/fraction.v | 259 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/math/fractions/fraction_test.v | 269 | 
4 files changed, 836 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/math/fractions/approximations.v b/v_windows/v/old/vlib/math/fractions/approximations.v new file mode 100644 index 0000000..dd4f855 --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/approximations.v @@ -0,0 +1,119 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fractions + +import math + +const ( +	default_eps    = 1.0e-4 +	max_iterations = 50 +	zero           = fraction(0, 1) +) + +// ------------------------------------------------------------------------ +// Unwrapped evaluation methods for fast evaluation of continued fractions. +// ------------------------------------------------------------------------ +// We need these functions because the evaluation of continued fractions +// always has to be done from the end. Also, the numerator-denominator pairs +// are generated from front to end. This means building a result from a +// previous one isn't possible. So we need unrolled versions to ensure that +// we don't take too much of a performance penalty by calling eval_cf +// several times. +// ------------------------------------------------------------------------ +// eval_1 returns the result of evaluating a continued fraction series of length 1 +fn eval_1(whole i64, d []i64) Fraction { +	return fraction(whole * d[0] + 1, d[0]) +} + +// eval_2 returns the result of evaluating a continued fraction series of length 2 +fn eval_2(whole i64, d []i64) Fraction { +	den := d[0] * d[1] + 1 +	return fraction(whole * den + d[1], den) +} + +// eval_3 returns the result of evaluating a continued fraction series of length 3 +fn eval_3(whole i64, d []i64) Fraction { +	d1d2_plus_n2 := d[1] * d[2] + 1 +	den := d[0] * d1d2_plus_n2 + d[2] +	return fraction(whole * den + d1d2_plus_n2, den) +} + +// eval_cf evaluates a continued fraction series and returns a Fraction. +fn eval_cf(whole i64, den []i64) Fraction { +	count := den.len +	// Offload some small-scale calculations +	// to dedicated functions +	match count { +		1 { +			return eval_1(whole, den) +		} +		2 { +			return eval_2(whole, den) +		} +		3 { +			return eval_3(whole, den) +		} +		else { +			last := count - 1 +			mut n := i64(1) +			mut d := den[last] +			// The calculations are done from back to front +			for index := count - 2; index >= 0; index-- { +				t := d +				d = den[index] * d + n +				n = t +			} +			return fraction(d * whole + n, d) +		} +	} +} + +// approximate returns a Fraction that approcimates the given value to +// within the default epsilon value (1.0e-4). This means the result will +// be accurate to 3 places after the decimal. +pub fn approximate(val f64) Fraction { +	return approximate_with_eps(val, fractions.default_eps) +} + +// approximate_with_eps returns a Fraction +pub fn approximate_with_eps(val f64, eps f64) Fraction { +	if val == 0.0 { +		return fractions.zero +	} +	if eps < 0.0 { +		panic('Epsilon value cannot be negative.') +	} +	if math.fabs(val) > math.max_i64 { +		panic('Value out of range.') +	} +	// The integer part is separated first. Then we process the fractional +	// part to generate numerators and denominators in tandem. +	whole := i64(val) +	mut frac := val - f64(whole) +	// Quick exit for integers +	if frac == 0.0 { +		return fraction(whole, 1) +	} +	mut d := []i64{} +	mut partial := fractions.zero +	// We must complete the approximation within the maximum number of +	// itertations allowed. If we can't panic. +	// Empirically tested: the hardest constant to approximate is the +	// golden ratio (math.phi) and for f64s, it only needs 38 iterations. +	for _ in 0 .. fractions.max_iterations { +		// We calculate the reciprocal. That's why the numerator is +		// always 1. +		frac = 1.0 / frac +		den := i64(frac) +		d << den +		// eval_cf is called often so it needs to be performant +		partial = eval_cf(whole, d) +		// Check if we're done +		if math.fabs(val - partial.f64()) < eps { +			return partial +		} +		frac -= f64(den) +	} +	panic("Couldn't converge. Please create an issue on https://github.com/vlang/v") +} diff --git a/v_windows/v/old/vlib/math/fractions/approximations_test.v b/v_windows/v/old/vlib/math/fractions/approximations_test.v new file mode 100644 index 0000000..5ee92bf --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/approximations_test.v @@ -0,0 +1,189 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import math.fractions +import math + +fn test_half() { +	float_val := 0.5 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(1, 2)) +} + +fn test_third() { +	float_val := 1.0 / 3.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(1, 3)) +} + +fn test_minus_one_twelfth() { +	float_val := -1.0 / 12.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(-1, 12)) +} + +fn test_zero() { +	float_val := 0.0 +	println('Pre') +	fract_val := fractions.approximate(float_val) +	println('Post') +	assert fract_val.equals(fractions.fraction(0, 1)) +} + +fn test_minus_one() { +	float_val := -1.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(-1, 1)) +} + +fn test_thirty_three() { +	float_val := 33.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(33, 1)) +} + +fn test_millionth() { +	float_val := 1.0 / 1000000.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(1, 1000000)) +} + +fn test_minus_27_by_57() { +	float_val := -27.0 / 57.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(-27, 57)) +} + +fn test_29_by_104() { +	float_val := 29.0 / 104.0 +	fract_val := fractions.approximate(float_val) +	assert fract_val.equals(fractions.fraction(29, 104)) +} + +fn test_140710_232() { +	float_val := 140710.232 +	fract_val := fractions.approximate(float_val) +	// Approximation will match perfectly for upto 3 places after the decimal +	// The result will be within default_eps of original value +	assert fract_val.f64() == float_val +} + +fn test_pi_1_digit() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-2).equals(fractions.fraction(22, +		7)) +} + +fn test_pi_2_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-3).equals(fractions.fraction(22, +		7)) +} + +fn test_pi_3_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-4).equals(fractions.fraction(333, +		106)) +} + +fn test_pi_4_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-5).equals(fractions.fraction(355, +		113)) +} + +fn test_pi_5_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-6).equals(fractions.fraction(355, +		113)) +} + +fn test_pi_6_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-7).equals(fractions.fraction(355, +		113)) +} + +fn test_pi_7_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-8).equals(fractions.fraction(103993, +		33102)) +} + +fn test_pi_8_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-9).equals(fractions.fraction(103993, +		33102)) +} + +fn test_pi_9_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-10).equals(fractions.fraction(104348, +		33215)) +} + +fn test_pi_10_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-11).equals(fractions.fraction(312689, +		99532)) +} + +fn test_pi_11_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-12).equals(fractions.fraction(1146408, +		364913)) +} + +fn test_pi_12_digits() { +	assert fractions.approximate_with_eps(math.pi, 5.0e-13).equals(fractions.fraction(4272943, +		1360120)) +} + +fn test_phi_1_digit() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-2).equals(fractions.fraction(5, +		3)) +} + +fn test_phi_2_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-3).equals(fractions.fraction(21, +		13)) +} + +fn test_phi_3_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-4).equals(fractions.fraction(55, +		34)) +} + +fn test_phi_4_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-5).equals(fractions.fraction(233, +		144)) +} + +fn test_phi_5_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-6).equals(fractions.fraction(610, +		377)) +} + +fn test_phi_6_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-7).equals(fractions.fraction(1597, +		987)) +} + +fn test_phi_7_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-8).equals(fractions.fraction(6765, +		4181)) +} + +fn test_phi_8_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-9).equals(fractions.fraction(17711, +		10946)) +} + +fn test_phi_9_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-10).equals(fractions.fraction(75025, +		46368)) +} + +fn test_phi_10_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-11).equals(fractions.fraction(196418, +		121393)) +} + +fn test_phi_11_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-12).equals(fractions.fraction(514229, +		317811)) +} + +fn test_phi_12_digits() { +	assert fractions.approximate_with_eps(math.phi, 5.0e-13).equals(fractions.fraction(2178309, +		1346269)) +} diff --git a/v_windows/v/old/vlib/math/fractions/fraction.v b/v_windows/v/old/vlib/math/fractions/fraction.v new file mode 100644 index 0000000..2e0b7bd --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/fraction.v @@ -0,0 +1,259 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fractions + +import math +import math.bits + +// Fraction Struct +// --------------- +// A Fraction has a numerator (n) and a denominator (d). If the user uses +// the helper functions in this module, then the following are guaranteed: +// 1. If the user provides n and d with gcd(n, d) > 1, the fraction will +// not be reduced automatically. +// 2. d cannot be set to zero. The factory function will panic. +// 3. If provided d is negative, it will be made positive. n will change as well. +struct Fraction { +	n i64 +	d i64 +pub: +	is_reduced bool +} + +// A factory function for creating a Fraction, adds a boundary condition +// to ensure that the denominator is non-zero. It automatically converts +// the negative denominator to positive and adjusts the numerator. +// NOTE: Fractions created are not reduced by default. +pub fn fraction(n i64, d i64) Fraction { +	if d == 0 { +		panic('Denominator cannot be zero') +	} +	// The denominator is always guaranteed to be positive (and non-zero). +	if d < 0 { +		return fraction(-n, -d) +	} +	return Fraction{ +		n: n +		d: d +		is_reduced: math.gcd(n, d) == 1 +	} +} + +// To String method +pub fn (f Fraction) str() string { +	return '$f.n/$f.d' +} + +// +// + ---------------------+ +// | Arithmetic functions.| +// + ---------------------+ +// +// These are implemented from Knuth, TAOCP Vol 2. Section 4.5 +// +// Returns a correctly reduced result for both addition and subtraction +// NOTE: requires reduced inputs +fn general_addition_result(f1 Fraction, f2 Fraction, addition bool) Fraction { +	d1 := math.gcd(f1.d, f2.d) +	// d1 happens to be 1 around 600/(pi)^2 or 61 percent of the time (Theorem 4.5.2D) +	if d1 == 1 { +		num1n2d := f1.n * f2.d +		num1d2n := f1.d * f2.n +		n := if addition { num1n2d + num1d2n } else { num1n2d - num1d2n } +		return Fraction{ +			n: n +			d: f1.d * f2.d +			is_reduced: true +		} +	} +	// Here d1 > 1. +	f1den := f1.d / d1 +	f2den := f2.d / d1 +	term1 := f1.n * f2den +	term2 := f2.n * f1den +	t := if addition { term1 + term2 } else { term1 - term2 } +	d2 := math.gcd(t, d1) +	return Fraction{ +		n: t / d2 +		d: f1den * (f2.d / d2) +		is_reduced: true +	} +} + +// Fraction add using operator overloading +pub fn (f1 Fraction) + (f2 Fraction) Fraction { +	return general_addition_result(f1.reduce(), f2.reduce(), true) +} + +// Fraction subtract using operator overloading +pub fn (f1 Fraction) - (f2 Fraction) Fraction { +	return general_addition_result(f1.reduce(), f2.reduce(), false) +} + +// Returns a correctly reduced result for both multiplication and division +// NOTE: requires reduced inputs +fn general_multiplication_result(f1 Fraction, f2 Fraction, multiplication bool) Fraction { +	// * Theorem: If f1 and f2 are reduced i.e. gcd(f1.n, f1.d) ==  1 and gcd(f2.n, f2.d) == 1, +	// then gcd(f1.n * f2.n, f1.d * f2.d) == gcd(f1.n, f2.d) * gcd(f1.d, f2.n) +	// * Knuth poses this an exercise for 4.5.1. - Exercise 2 +	// * Also, note that: +	// The terms are flipped for multiplication and division, so the gcds must be calculated carefully +	// We do multiple divisions in order to prevent any possible overflows. +	// * One more thing: +	// if d = gcd(a, b) for example, then d divides both a and b +	if multiplication { +		d1 := math.gcd(f1.n, f2.d) +		d2 := math.gcd(f1.d, f2.n) +		return Fraction{ +			n: (f1.n / d1) * (f2.n / d2) +			d: (f2.d / d1) * (f1.d / d2) +			is_reduced: true +		} +	} else { +		d1 := math.gcd(f1.n, f2.n) +		d2 := math.gcd(f1.d, f2.d) +		return Fraction{ +			n: (f1.n / d1) * (f2.d / d2) +			d: (f2.n / d1) * (f1.d / d2) +			is_reduced: true +		} +	} +} + +// Fraction multiply using operator overloading +pub fn (f1 Fraction) * (f2 Fraction) Fraction { +	return general_multiplication_result(f1.reduce(), f2.reduce(), true) +} + +// Fraction divide using operator overloading +pub fn (f1 Fraction) / (f2 Fraction) Fraction { +	if f2.n == 0 { +		panic('Cannot divide by zero') +	} +	// If the second fraction is negative, it will +	// mess up the sign. We need positive denominator +	if f2.n < 0 { +		return f1.negate() / f2.negate() +	} +	return general_multiplication_result(f1.reduce(), f2.reduce(), false) +} + +// Fraction negate method +pub fn (f Fraction) negate() Fraction { +	return Fraction{ +		n: -f.n +		d: f.d +		is_reduced: f.is_reduced +	} +} + +// Fraction reciprocal method +pub fn (f Fraction) reciprocal() Fraction { +	if f.n == 0 { +		panic('Denominator cannot be zero') +	} +	return Fraction{ +		n: f.d +		d: f.n +		is_reduced: f.is_reduced +	} +} + +// Fraction method which reduces the fraction +pub fn (f Fraction) reduce() Fraction { +	if f.is_reduced { +		return f +	} +	cf := math.gcd(f.n, f.d) +	return Fraction{ +		n: f.n / cf +		d: f.d / cf +		is_reduced: true +	} +} + +// f64 converts the Fraction to 64-bit floating point +pub fn (f Fraction) f64() f64 { +	return f64(f.n) / f64(f.d) +} + +// +// + ------------------+ +// | Utility functions.| +// + ------------------+ +// +// Returns the absolute value of an i64 +fn abs(num i64) i64 { +	if num < 0 { +		return -num +	} else { +		return num +	} +} + +// cmp_i64s compares the two arguments, returns 0 when equal, 1 when the first is bigger, -1 otherwise +fn cmp_i64s(a i64, b i64) int { +	if a == b { +		return 0 +	} else if a > b { +		return 1 +	} else { +		return -1 +	} +} + +// cmp_f64s compares the two arguments, returns 0 when equal, 1 when the first is bigger, -1 otherwise +fn cmp_f64s(a f64, b f64) int { +	// V uses epsilon comparison internally +	if a == b { +		return 0 +	} else if a > b { +		return 1 +	} else { +		return -1 +	} +} + +// Two integers are safe to multiply when their bit lengths +// sum up to less than 64 (conservative estimate). +fn safe_to_multiply(a i64, b i64) bool { +	return (bits.len_64(u64(abs(a))) + bits.len_64(u64(abs(b)))) < 64 +} + +// cmp compares the two arguments, returns 0 when equal, 1 when the first is bigger, -1 otherwise +fn cmp(f1 Fraction, f2 Fraction) int { +	if safe_to_multiply(f1.n, f2.d) && safe_to_multiply(f2.n, f1.d) { +		return cmp_i64s(f1.n * f2.d, f2.n * f1.d) +	} else { +		return cmp_f64s(f1.f64(), f2.f64()) +	} +} + +// +-----------------------------+ +// | Public comparison functions | +// +-----------------------------+ +// equals returns true if both the Fractions are equal +pub fn (f1 Fraction) equals(f2 Fraction) bool { +	return cmp(f1, f2) == 0 +} + +// ge returns true if f1 >= f2 +pub fn (f1 Fraction) ge(f2 Fraction) bool { +	return cmp(f1, f2) >= 0 +} + +// gt returns true if f1 > f2 +pub fn (f1 Fraction) gt(f2 Fraction) bool { +	return cmp(f1, f2) > 0 +} + +// le returns true if f1 <= f2 +pub fn (f1 Fraction) le(f2 Fraction) bool { +	return cmp(f1, f2) <= 0 +} + +// lt returns true if f1 < f2 +pub fn (f1 Fraction) lt(f2 Fraction) bool { +	return cmp(f1, f2) < 0 +} diff --git a/v_windows/v/old/vlib/math/fractions/fraction_test.v b/v_windows/v/old/vlib/math/fractions/fraction_test.v new file mode 100644 index 0000000..4928b7c --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/fraction_test.v @@ -0,0 +1,269 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import math.fractions + +// (Old) results are verified using https://www.calculatorsoup.com/calculators/math/fractions.php +// Newer ones are contrived for corner cases or prepared by hand. +fn test_4_by_8_f64_and_str() { +	f := fractions.fraction(4, 8) +	assert f.f64() == 0.5 +	assert f.str() == '4/8' +} + +fn test_10_by_5_f64_and_str() { +	f := fractions.fraction(10, 5) +	assert f.f64() == 2.0 +	assert f.str() == '10/5' +} + +fn test_9_by_3_f64_and_str() { +	f := fractions.fraction(9, 3) +	assert f.f64() == 3.0 +	assert f.str() == '9/3' +} + +fn test_4_by_minus_5_f64_and_str() { +	f := fractions.fraction(4, -5) +	assert f.f64() == -0.8 +	assert f.str() == '-4/5' +} + +fn test_minus_7_by_minus_92_str() { +	f := fractions.fraction(-7, -5) +	assert f.str() == '7/5' +} + +fn test_4_by_8_plus_5_by_10() { +	f1 := fractions.fraction(4, 8) +	f2 := fractions.fraction(5, 10) +	sum := f1 + f2 +	assert sum.f64() == 1.0 +	assert sum.str() == '1/1' +	assert sum.equals(fractions.fraction(1, 1)) +} + +fn test_5_by_5_plus_8_by_8() { +	f1 := fractions.fraction(5, 5) +	f2 := fractions.fraction(8, 8) +	sum := f1 + f2 +	assert sum.f64() == 2.0 +	assert sum.str() == '2/1' +	assert sum.equals(fractions.fraction(2, 1)) +} + +fn test_9_by_3_plus_1_by_3() { +	f1 := fractions.fraction(9, 3) +	f2 := fractions.fraction(1, 3) +	sum := f1 + f2 +	assert sum.str() == '10/3' +	assert sum.equals(fractions.fraction(10, 3)) +} + +fn test_3_by_7_plus_1_by_4() { +	f1 := fractions.fraction(3, 7) +	f2 := fractions.fraction(1, 4) +	sum := f1 + f2 +	assert sum.str() == '19/28' +	assert sum.equals(fractions.fraction(19, 28)) +} + +fn test_36529_by_12409100000_plus_418754901_by_9174901000() { +	f1 := fractions.fraction(i64(36529), i64(12409100000)) +	f2 := fractions.fraction(i64(418754901), i64(9174901000)) +	sum := f1 + f2 +	assert sum.str() == '5196706591957729/113852263999100000' +} + +fn test_4_by_8_plus_minus_5_by_10() { +	f1 := fractions.fraction(4, 8) +	f2 := fractions.fraction(-5, 10) +	diff := f2 + f1 +	assert diff.f64() == 0 +	assert diff.str() == '0/1' +} + +fn test_4_by_8_minus_5_by_10() { +	f1 := fractions.fraction(4, 8) +	f2 := fractions.fraction(5, 10) +	diff := f2 - f1 +	assert diff.f64() == 0 +	assert diff.str() == '0/1' +} + +fn test_5_by_5_minus_8_by_8() { +	f1 := fractions.fraction(5, 5) +	f2 := fractions.fraction(8, 8) +	diff := f2 - f1 +	assert diff.f64() == 0 +	assert diff.str() == '0/1' +} + +fn test_9_by_3_minus_1_by_3() { +	f1 := fractions.fraction(9, 3) +	f2 := fractions.fraction(1, 3) +	diff := f1 - f2 +	assert diff.str() == '8/3' +} + +fn test_3_by_7_minus_1_by_4() { +	f1 := fractions.fraction(3, 7) +	f2 := fractions.fraction(1, 4) +	diff := f1 - f2 +	assert diff.str() == '5/28' +} + +fn test_36529_by_12409100000_minus_418754901_by_9174901000() { +	f1 := fractions.fraction(i64(36529), i64(12409100000)) +	f2 := fractions.fraction(i64(418754901), i64(9174901000)) +	sum := f1 - f2 +	assert sum.str() == '-5196036292040471/113852263999100000' +} + +fn test_4_by_8_times_5_by_10() { +	f1 := fractions.fraction(4, 8) +	f2 := fractions.fraction(5, 10) +	product := f1 * f2 +	assert product.f64() == 0.25 +	assert product.str() == '1/4' +} + +fn test_5_by_5_times_8_by_8() { +	f1 := fractions.fraction(5, 5) +	f2 := fractions.fraction(8, 8) +	product := f1 * f2 +	assert product.f64() == 1.0 +	assert product.str() == '1/1' +} + +fn test_9_by_3_times_1_by_3() { +	f1 := fractions.fraction(9, 3) +	f2 := fractions.fraction(1, 3) +	product := f1 * f2 +	assert product.f64() == 1.0 +	assert product.str() == '1/1' +} + +fn test_3_by_7_times_1_by_4() { +	f1 := fractions.fraction(3, 7) +	f2 := fractions.fraction(1, 4) +	product := f2 * f1 +	assert product.f64() == (3.0 / 28.0) +	assert product.str() == '3/28' +} + +fn test_4_by_8_over_5_by_10() { +	f1 := fractions.fraction(4, 8) +	f2 := fractions.fraction(5, 10) +	q := f1 / f2 +	assert q.f64() == 1.0 +	assert q.str() == '1/1' +} + +fn test_5_by_5_over_8_by_8() { +	f1 := fractions.fraction(5, 5) +	f2 := fractions.fraction(8, 8) +	q := f1 / f2 +	assert q.f64() == 1.0 +	assert q.str() == '1/1' +} + +fn test_9_by_3_over_1_by_3() { +	f1 := fractions.fraction(9, 3) +	f2 := fractions.fraction(1, 3) +	q := f1 / f2 +	assert q.f64() == 9.0 +	assert q.str() == '9/1' +} + +fn test_3_by_7_over_1_by_4() { +	f1 := fractions.fraction(3, 7) +	f2 := fractions.fraction(1, 4) +	q := f1 / f2 +	assert q.str() == '12/7' +} + +fn test_reciprocal_4_by_8() { +	f := fractions.fraction(4, 8) +	assert f.reciprocal().str() == '8/4' +} + +fn test_reciprocal_5_by_10() { +	f := fractions.fraction(5, 10) +	assert f.reciprocal().str() == '10/5' +} + +fn test_reciprocal_5_by_5() { +	f := fractions.fraction(5, 5) +	assert f.reciprocal().str() == '5/5' +} + +fn test_reciprocal_8_by_8() { +	f := fractions.fraction(8, 8) +	assert f.reciprocal().str() == '8/8' +} + +fn test_reciprocal_9_by_3() { +	f := fractions.fraction(9, 3) +	assert f.reciprocal().str() == '3/9' +} + +fn test_reciprocal_1_by_3() { +	f := fractions.fraction(1, 3) +	assert f.reciprocal().str() == '3/1' +} + +fn test_reciprocal_7_by_3() { +	f := fractions.fraction(7, 3) +	assert f.reciprocal().str() == '3/7' +} + +fn test_reciprocal_1_by_4() { +	f := fractions.fraction(1, 4) +	assert f.reciprocal().str() == '4/1' +} + +fn test_4_by_8_equals_5_by_10() { +	f1 := fractions.fraction(4, 8) +	f2 := fractions.fraction(5, 10) +	assert f1.equals(f2) +} + +fn test_1_by_2_does_not_equal_3_by_4() { +	f1 := fractions.fraction(1, 2) +	f2 := fractions.fraction(3, 4) +	assert !f1.equals(f2) +} + +fn test_reduce_3_by_9() { +	f := fractions.fraction(3, 9) +	assert f.reduce().equals(fractions.fraction(1, 3)) +} + +fn test_1_by_3_less_than_2_by_4() { +	f1 := fractions.fraction(1, 3) +	f2 := fractions.fraction(2, 4) +	assert f1.lt(f2) +	assert f1.le(f2) +} + +fn test_2_by_3_greater_than_2_by_4() { +	f1 := fractions.fraction(2, 3) +	f2 := fractions.fraction(2, 4) +	assert f1.gt(f2) +	assert f1.ge(f2) +} + +fn test_5_by_7_not_less_than_2_by_4() { +	f1 := fractions.fraction(5, 7) +	f2 := fractions.fraction(2, 4) +	assert !f1.lt(f2) +	assert !f1.le(f2) +} + +fn test_49_by_75_not_greater_than_2_by_3() { +	f1 := fractions.fraction(49, 75) +	f2 := fractions.fraction(2, 3) +	assert !f1.gt(f2) +	assert !f1.ge(f2) +}  | 
