Arithmetic/Rational

Revision as of 11:28, 11 January 2010 by rosettacode>Dsnouck (→‎{{header|Fortran}}: expand and clean)

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.

Task
Arithmetic/Rational
You are encouraged to solve this task according to the task description, using any language you may know.

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

See Rational Arithmetic/Ada

ALGOL 68

Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
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

See Rational Arithmetic/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

Works with: Fortran version 90 and later

<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

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

Works with: Python version 3.0

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
Works with: GNU Smalltalk

<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

See Rational Arithmetic/Tcl

TI-89 BASIC

While TI-89 BASIC has built-in rational and symbolic arithmetic, it does not have user-defined data types.