Arithmetic/Rational
You are encouraged to solve this task according to the task description, using any language you may know.
The objective of this task is to create a reasonably complete implementation of rational arithmetic in the particular language using the idioms of the language.
For example: Define an new type called frac with binary operator "//" of two integers that returns a structure made up of the numerator and the denominator (as per a rational number).
Further define the appropriate rational unary operators abs and '-', with the binary operators for addition '+', subtraction '-', multiplication '×', division '/', integer division '÷', modulo division, the comparison operators (e.g. '<', '≤', '>', & '≥') and equality operators (e.g. '=' & '≠').
Define standard coercion operators for casting int to frac etc.
If space allows, define standard increment and decrement operators (e.g. '+:=' & '-:=' etc.).
Finally test the operators: Use the new type frac to find all perfect numbers less then 219 by summing the reciprocal of the factors.
See also
Ada
ALGOL 68
MODE FRAC = STRUCT( INT num #erator#, den #ominator#); FORMAT frac repr = $g(-0)"//"g(-0)$; PROC gcd = (INT a, b) INT: # greatest common divisor # (a = 0 | b |: b = 0 | a |: ABS a > ABS b | gcd(b, a MOD b) | gcd(a, b MOD a)); PROC lcm = (INT a, b)INT: # least common multiple # a OVER gcd(a, b) * b; PROC raise not implemented error = ([]STRING args)VOID: ( put(stand error, ("Not implemented error: ",args, newline)); stop ); PRIO // = 9; # higher then the ** operator # OP // = (INT num, den)FRAC: ( # initialise and normalise # INT common = gcd(num, den); IF den < 0 THEN ( -num OVER common, -den OVER common) ELSE ( num OVER common, den OVER common) FI ); OP + = (FRAC a, b)FRAC: ( INT common = lcm(den OF a, den OF b); FRAC result := ( common OVER den OF a * num OF a + common OVER den OF b * num OF b, common ); num OF result//den OF result ); OP - = (FRAC a, b)FRAC: a + -b, * = (FRAC a, b)FRAC: ( INT num = num OF a * num OF b, den = den OF a * den OF b; INT common = gcd(num, den); (num OVER common) // (den OVER common) ); OP / = (FRAC a, b)FRAC: a * FRAC(den OF b, num OF b),# real division # % = (FRAC a, b)INT: ENTIER (a / b), # integer divison # %* = (FRAC a, b)FRAC: a/b - FRACINIT ENTIER (a/b), # modulo division # ** = (FRAC a, INT exponent)FRAC: IF exponent >= 0 THEN (num OF a ** exponent, den OF a ** exponent ) ELSE (den OF a ** exponent, num OF a ** exponent ) FI; OP REALINIT = (FRAC frac)REAL: num OF frac / den OF frac, FRACINIT = (INT num)FRAC: num // 1, FRACINIT = (REAL num)FRAC: ( # express real number as a fraction # # a future execise! # raise not implemented error(("Convert a REAL to a FRAC","!")); SKIP ); OP < = (FRAC a, b)BOOL: num OF (a - b) < 0, > = (FRAC a, b)BOOL: num OF (a - b) > 0, <= = (FRAC a, b)BOOL: NOT ( a > b ), >= = (FRAC a, b)BOOL: NOT ( a < b ), = = (FRAC a, b)BOOL: (num OF a, den OF a) = (num OF b, den OF b), /= = (FRAC a, b)BOOL: (num OF a, den OF a) /= (num OF b, den OF b); # Unary operators # OP - = (FRAC frac)FRAC: (-num OF frac, den OF frac), ABS = (FRAC frac)FRAC: (ABS num OF frac, ABS den OF frac), ENTIER = (FRAC frac)INT: (num OF frac OVER den OF frac) * den OF frac; COMMENT Operators for extended characters set, and increment/decrement "commented out" to save space. COMMENT
Example: searching for Perfect Numbers.
FRAC sum:= FRACINIT 0; FORMAT perfect = $b(" perfect!","")$; FOR i FROM 2 TO 2**19 DO INT candidate := i; FRAC sum := 1 // candidate; REAL real sum := 1 / candidate; FOR factor FROM 2 TO ENTIER sqrt(candidate) DO IF candidate MOD factor = 0 THEN sum := sum + 1 // factor + 1 // ( candidate OVER factor); real sum +:= 1 / factor + 1 / ( candidate OVER factor) FI OD; IF den OF sum = 1 THEN printf(($"Sum of reciprocal factors of "g(-0)" = "g(-0)" exactly, about "g(0,real width) f(perfect)l$, candidate, ENTIER sum, real sum, ENTIER sum = 1)) FI OD
Output:
Sum of reciprocal factors of 6 = 1 exactly, about 1.0000000000000000000000000001 perfect! Sum of reciprocal factors of 28 = 1 exactly, about 1.0000000000000000000000000001 perfect! Sum of reciprocal factors of 120 = 2 exactly, about 2.0000000000000000000000000002 Sum of reciprocal factors of 496 = 1 exactly, about 1.0000000000000000000000000001 perfect! Sum of reciprocal factors of 672 = 2 exactly, about 2.0000000000000000000000000001 Sum of reciprocal factors of 8128 = 1 exactly, about 1.0000000000000000000000000001 perfect! Sum of reciprocal factors of 30240 = 3 exactly, about 3.0000000000000000000000000002 Sum of reciprocal factors of 32760 = 3 exactly, about 3.0000000000000000000000000003 Sum of reciprocal factors of 523776 = 2 exactly, about 2.0000000000000000000000000005
C
Common Lisp
Common Lisp has rational numbers built-in and integrated with all other number types. Common Lisp's number system is not extensible so reimplementing rational arithmetic would require all-new operator names.
<lang lisp>(loop for candidate from 2 below (expt 2 19)
for sum = (+ (/ candidate) (loop for factor from 2 to (isqrt candidate) when (zerop (mod candidate factor)) sum (+ (/ factor) (/ (floor candidate factor))))) when (= sum 1) collect candidate)</lang>
Fortran
<lang fortran>module module_rational
implicit none private public :: rational public :: rational_simplify public :: assignment (=) public :: operator (//) public :: operator (+) public :: operator (-) public :: operator (*) public :: operator (/) public :: operator (<) public :: operator (<=) public :: operator (>) public :: operator (>=) public :: operator (==) public :: operator (/=) public :: abs public :: int public :: modulo type rational integer :: numerator integer :: denominator end type rational interface assignment (=) module procedure assign_rational_int, assign_rational_real end interface interface operator (//) module procedure make_rational end interface interface operator (+) module procedure rational_add end interface interface operator (-) module procedure rational_minus, rational_subtract end interface interface operator (*) module procedure rational_multiply end interface interface operator (/) module procedure rational_divide end interface interface operator (<) module procedure rational_lt end interface interface operator (<=) module procedure rational_le end interface interface operator (>) module procedure rational_gt end interface interface operator (>=) module procedure rational_ge end interface interface operator (==) module procedure rational_eq end interface interface operator (/=) module procedure rational_ne end interface interface abs module procedure rational_abs end interface interface int module procedure rational_int end interface interface modulo module procedure rational_modulo end interface
contains
recursive function gcd (i, j) result (res) integer, intent (in) :: i integer, intent (in) :: j integer :: res if (j == 0) then res = i else res = gcd (j, modulo (i, j)) end if end function gcd
function rational_simplify (r) result (res) type (rational), intent (in) :: r type (rational) :: res integer :: g g = gcd (r % numerator, r % denominator) res = r % numerator / g // r % denominator / g end function rational_simplify
function make_rational (numerator, denominator) result (res) integer, intent (in) :: numerator integer, intent (in) :: denominator type (rational) :: res res = rational (numerator, denominator) end function make_rational
subroutine assign_rational_int (res, i) type (rational), intent (out), volatile :: res integer, intent (in) :: i res = i // 1 end subroutine assign_rational_int
subroutine assign_rational_real (res, x) type (rational), intent(out), volatile :: res real, intent (in) :: x integer :: x_floor real :: x_frac x_floor = floor (x) x_frac = x - x_floor if (x_frac == 0) then res = x_floor // 1 else res = (x_floor // 1) + (1 // floor (1 / x_frac)) end if end subroutine assign_rational_real
function rational_add (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: res res = r % numerator * s % denominator + r % denominator * s % numerator // & & r % denominator * s % denominator end function rational_add
function rational_minus (r) result (res) type (rational), intent (in) :: r type (rational) :: res res = - r % numerator // r % denominator end function rational_minus
function rational_subtract (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: res res = r % numerator * s % denominator - r % denominator * s % numerator // & & r % denominator * s % denominator end function rational_subtract
function rational_multiply (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: res res = r % numerator * s % numerator // r % denominator * s % denominator end function rational_multiply
function rational_divide (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: res res = r % numerator * s % denominator // r % denominator * s % numerator end function rational_divide
function rational_lt (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: r_simple type (rational) :: s_simple logical :: res r_simple = rational_simplify (r) s_simple = rational_simplify (s) res = r_simple % numerator * s_simple % denominator < & & s_simple % numerator * r_simple % denominator end function rational_lt
function rational_le (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: r_simple type (rational) :: s_simple logical :: res r_simple = rational_simplify (r) s_simple = rational_simplify (s) res = r_simple % numerator * s_simple % denominator <= & & s_simple % numerator * r_simple % denominator end function rational_le
function rational_gt (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: r_simple type (rational) :: s_simple logical :: res r_simple = rational_simplify (r) s_simple = rational_simplify (s) res = r_simple % numerator * s_simple % denominator > & & s_simple % numerator * r_simple % denominator end function rational_gt
function rational_ge (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s type (rational) :: r_simple type (rational) :: s_simple logical :: res r_simple = rational_simplify (r) s_simple = rational_simplify (s) res = r_simple % numerator * s_simple % denominator >= & & s_simple % numerator * r_simple % denominator end function rational_ge
function rational_eq (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s logical :: res res = r % numerator * s % denominator == s % numerator * r % denominator end function rational_eq
function rational_ne (r, s) result (res) type (rational), intent (in) :: r type (rational), intent (in) :: s logical :: res res = r % numerator * s % denominator /= s % numerator * r % denominator end function rational_ne
function rational_abs (r) result (res) type (rational), intent (in) :: r type (rational) :: res res = sign (r % numerator, r % denominator) // r % denominator end function rational_abs
function rational_int (r) result (res) type (rational), intent (in) :: r integer :: res res = r % numerator / r % denominator end function rational_int
function rational_modulo (r) result (res) type (rational), intent (in) :: r integer :: res res = modulo (r % numerator, r % denominator) end function rational_modulo
end module module_rational</lang> Example: <lang fortran>program perfect_numbers
use module_rational implicit none integer, parameter :: n_min = 2 integer, parameter :: n_max = 2 ** 19 - 1 integer :: n integer :: factor type (rational) :: sum
do n = n_min, n_max sum = 1 // n factor = 2 do if (factor * factor >= n) then exit end if if (modulo (n, factor) == 0) then sum = rational_simplify (sum + (1 // factor) + (factor // n)) end if factor = factor + 1 end do if (sum % numerator == 1 .and. sum % denominator == 1) then write (*, '(i0)') n end if end do
end program perfect_numbers</lang> Output: <lang>6 28 496 8128</lang>
Haskell
Haskell provides a Rational
type, which is really an alias for Ratio Integer
(Ratio
being a polymorphic type implementing rational numbers for any Integral
type of numerators and denominators). The fraction is constructed using the %
operator.
<lang haskell>import Data.Ratio
-- simply prints all the perfect numbers main = mapM_ print [candidate
| candidate <- [2 .. 2^19], getSum candidate == 1] where getSum candidate = 1 % candidate + sum [1 % factor + 1 % (candidate `div` factor) | factor <- [2 .. floor(sqrt(fromIntegral(candidate)))], candidate `mod` factor == 0]</lang>
For a sample implementation of Ratio
, see the Haskell 98 Report.
J
J implements rational numbers:
<lang j> 3r4*2r5 3r10</lang>
That said, note that J's floating point numbers work just fine for the stated problem: <lang j> is_perfect_rational=: 2 = (1 + i.) +/@:%@([ #~ 0 = |) ]</lang>
faster version (but the problem, as stated, is still tremendously inefficient): <lang j>factors=: */&>@{@((^ i.@>:)&.>/)@q:~&__ is_perfect_rational=: 2= +/@:%@,@factors</lang>
Exhaustive testing would take forever: <lang j> I.is_perfect_rational@"0 i.2^19 6 28 496 8128
I.is_perfect_rational@x:@"0 i.2^19x
6 28 496 8128</lang>
More limited testing takes reasonable amounts of time: <lang j> (#~ is_perfect_rational"0) (* <:@+:) 2^i.10x 6 28 496 8128</lang>
JavaScript
See Rational Arithmetic/JavaScript
Lua
<lang lua>function gcd(a,b) return a == 0 and b or gcd(b % a, a) end
do
local function coerce(a, b) if type(a) == "number" then return rational(a, 1), b end if type(b) == "number" then return a, rational(b, 1) end return a, b end rational = setmetatable({ __add = function(a, b) local a, b = coerce(a, b) return rational(a.num * b.den + a.den * b.num, a.den * b.den) end, __sub = function(a, b) local a, b = coerce(a, b) return rational(a.num * b.den - a.den * b.num, a.den * b.den) end, __mul = function(a, b) local a, b = coerce(a, b) return rational(a.num * b.num, a.den * b.den) end, __div = function(a, b) local a, b = coerce(a, b) return rational(a.num * b.den, a.den * b.num) end, __pow = function(a, b) if type(a) == "number" then return a ^ (b.num / b.den) end return rational(a.num ^ b, a.den ^ b) --runs into a problem if these aren't integers end, __concat = function(a, b) if getmetatable(a) == rational then return a.num .. "/" .. a.den .. b end return a .. b.num .. "/" .. b.den end, __unm = function(a) return rational(-a.num, -a.den) end}, { __call = function(z, a, b) return setmetatable({num = a / gcd(a, b),den = b / gcd(a, b)}, z) end} )
end
print(rational(2, 3) + rational(3, 5) - rational(1, 10) .. "") --> 7/6 print((rational(4, 5) * rational(5, 9)) ^ rational(1, 2) .. "") --> 2/3 print(rational(45, 60) / rational(5, 2) .. "") --> 3/10 print(5 + rational(1, 3) .. "") --> 16/3
function findperfs(n)
local ret = {} for i = 1, n do sum = rational(1, i) for fac = 2, i^.5 do if i % fac == 0 then sum = sum + rational(1, fac) + rational(fac, i) end end if sum.den == sum.num then ret[#ret + 1] = i end end return table.concat(ret, '\n')
end print(findperfs(2^19))</lang>
Mathematica
Mathematica has full support for fractions built-in. If one divides two exact numbers it will be left as a fraction if it can't be simplified. Comparison, addition, division, product et cetera are built-in: <lang Mathematica>4/16 3/8 8/4 4Pi/2 16!/10! Sqrt[9/16] Sqrt[3/4] (23/12)^5 2 + 1/(1 + 1/(3 + 1/4))
1/2+1/3+1/5 8/Pi+Pi/8 //Together 13/17 + 7/31 Sum[1/n,{n,1,100}] (*summation of 1/1 + 1/2 + 1/3 + 1/4+ .........+ 1/99 + 1/100*)
1/2-1/3 a=1/3;a+=1/7
1/4==2/8 1/4>3/8 Pi/E >23/20 1/3!=123/370 Sin[3]/Sin[2]>3/20
Numerator[6/9] Denominator[6/9]</lang> gives back: <lang Mathematica>1/4 3/8 2 2 Pi 5765760 3/4 Sqrt[3]/2 6436343 / 248832 47/17
31/30 (64+Pi^2) / (8 Pi) 522 / 527 14466636279520351160221518043104131447711 / 2788815009188499086581352357412492142272
1/6 10/21
True False True True True
2 3</lang> As you can see, Mathematica automatically handles fraction as exact things, it doesn't evaluate the fractions to a float. It only does this when either the numerator or the denominator is not exact. I only showed integers above, but Mathematica can handle symbolic fraction in the same and complete way: <lang Mathematica>c/(2 c) (b^2 - c^2)/(b - c) // Cancel 1/2 + b/c // Together</lang> gives back: <lang Mathematica>1/2 b+c (2 b+c) / (2 c)</lang> Moreover it does simplification like Sin[x]/Cos[x] => Tan[x]. Division, addition, subtraction, powering and multiplication of a list (of any dimension) is automatically threaded over the elements: <lang Mathematica>1+2*{1,2,3}^3</lang> gives back: <lang Mathematica>{3, 17, 55}</lang> To check for perfect numbers in the range 1 to 2^25 we can use: <lang Mathematica>found={}; CheckPerfect[num_Integer]:=If[Total[1/Divisors[num]]==2,AppendTo[found,num]]; Do[CheckPerfect[i],{i,1,2^25}]; found</lang> gives back: <lang Mathematica>{6, 28, 496, 8128, 33550336}</lang> Final note; approximations of fractions to any precision can be found using the function N.
Objective-C
See Rational Arithmetic/Objective-C
OCaml
OCaml's Num library implements arbitrary-precision rational numbers:
<lang ocaml>#load "nums.cma";; open Num;;
for candidate = 2 to 1 lsl 19 do
let sum = ref (num_of_int 1 // num_of_int candidate) in for factor = 2 to truncate (sqrt (float candidate)) do if candidate mod factor = 0 then sum := !sum +/ num_of_int 1 // num_of_int factor +/ num_of_int 1 // num_of_int (candidate / factor) done; if is_integer_num !sum then Printf.printf "Sum of recipr. factors of %d = %d exactly %s\n%!" candidate (int_of_num !sum) (if int_of_num !sum = 1 then "perfect!" else "")
done;;</lang>
It might be implemented like this:
[insert implementation here]
Perl
Perl's Math::BigRat
core module implements arbitrary-precision rational numbers. The bigrat
pragma can be used to turn on transparent BigRat support:
<lang perl>use bigrat;
foreach my $candidate (2 .. 2**19) {
my $sum = 1 / $candidate; foreach my $factor (2 .. sqrt($candidate)+1) { if ($candidate % $factor == 0) { $sum += 1 / $factor + 1 / ($candidate / $factor); } } if ($sum->denominator() == 1) { print "Sum of recipr. factors of $candidate = $sum exactly ", ($sum == 1 ? "perfect!" : ""), "\n"; }
}</lang>
It might be implemented like this:
[insert implementation here]
Python
Python 3's standard library already implements a Fraction class:
<lang python>from fractions import Fraction
for candidate in range(2, 2**19):
sum = Fraction(1, candidate) for factor in range(2, int(candidate**0.5)+1): if candidate % factor == 0: sum += Fraction(1, factor) + Fraction(1, candidate // factor) if sum.denominator == 1: print("Sum of recipr. factors of %d = %d exactly %s" % (candidate, int(sum), "perfect!" if sum == 1 else ""))</lang>
It might be implemented like this:
<lang python>def lcm(a, b):
return a // gcd(a,b) * b
def gcd(u, v):
return gcd(v, u%v) if v else abs(u)
class Fraction:
def __init__(self, numerator, denominator): common = gcd(numerator, denominator) self.numerator = numerator//common self.denominator = denominator//common def __add__(self, frac): common = lcm(self.denominator, frac.denominator) n = common // self.denominator * self.numerator + common // frac.denominator * frac.numerator return Fraction(n, common) def __sub__(self, frac): return self.__add__(-frac) def __neg__(self): return Fraction(-self.numerator, self.denominator) def __abs__(self): return Fraction(abs(self.numerator), abs(self.denominator)) def __mul__(self, frac): return Fraction(self.numerator * frac.numerator, self.denominator * frac.denominator) def __div__(self, frac): return self.__mul__(frac.reciprocal()) def reciprocal(self): return Fraction(self.denominator, self.numerator) def __cmp__(self, n): return int(float(self) - float(n)) def __float__(self): return float(self.numerator / self.denominator) def __int__(self): return (self.numerator // self.denominator)</lang>
Ruby
Ruby's standard library already implements a Rational class:
<lang ruby>require 'rational'
for candidate in 2 .. 2**19:
sum = Rational(1, candidate) for factor in 2 ... candidate**0.5 if candidate % factor == 0 sum += Rational(1, factor) + Rational(1, candidate / factor) end end if sum.denominator == 1 puts "Sum of recipr. factors of %d = %d exactly %s" % [candidate, sum.to_i, sum == 1 ? "perfect!" : ""] end
end</lang>
It might be implemented like this:
[insert implementation here]
Slate
Slate uses infinite-precision fractions transparently.
<lang slate>54 / 7. 20 reciprocal. (5 / 6) reciprocal. (5 / 6) as: Float.</lang>
Smalltalk
Smalltalk uses naturally and transparently fractions (through the class Fraction):
st> 54/7 54/7 st> 54/7 + 1 61/7 st> 54/7 < 50 true st> 20 reciprocal 1/20 st> (5/6) reciprocal 6/5 st> (5/6) asFloat 0.8333333333333334
<lang smalltalk>| sum | 2 to: (2 raisedTo: 19) do: [ :candidate |
sum := candidate reciprocal. 2 to: (candidate sqrt) do: [ :factor | ( (candidate \\ factor) = 0 ) ifTrue: [ sum := sum + (factor reciprocal) + ((candidate / factor) reciprocal) ] ]. ( (sum denominator) = 1 ) ifTrue: [ ('Sum of recipr. factors of %1 = %2 exactly %3' % { candidate printString . (sum asInteger) printString . ( sum = 1 ) ifTrue: [ 'perfect!' ] ifFalse: [ ' ' ] }) displayNl ]
].</lang>
Tcl
TI-89 BASIC
While TI-89 BASIC has built-in rational and symbolic arithmetic, it does not have user-defined data types.