RosettaCodeData/Task/Arithmetic-Rational/Elixir/arithmetic-rational.elixir

67 lines
1.9 KiB
Plaintext

defmodule Rational do
import Kernel, except: [div: 2]
defstruct numerator: 0, denominator: 1
def new(numerator), do: %Rational{numerator: numerator, denominator: 1}
def new(numerator, denominator) do
sign = if numerator * denominator < 0, do: -1, else: 1
{numerator, denominator} = {abs(numerator), abs(denominator)}
gcd = gcd(numerator, denominator)
%Rational{numerator: sign * Kernel.div(numerator, gcd),
denominator: Kernel.div(denominator, gcd)}
end
def add(a, b) do
{a, b} = convert(a, b)
new(a.numerator * b.denominator + b.numerator * a.denominator,
a.denominator * b.denominator)
end
def sub(a, b) do
{a, b} = convert(a, b)
new(a.numerator * b.denominator - b.numerator * a.denominator,
a.denominator * b.denominator)
end
def mult(a, b) do
{a, b} = convert(a, b)
new(a.numerator * b.numerator, a.denominator * b.denominator)
end
def div(a, b) do
{a, b} = convert(a, b)
new(a.numerator * b.denominator, a.denominator * b.numerator)
end
defp convert(a), do: if is_integer(a), do: new(a), else: a
defp convert(a, b), do: {convert(a), convert(b)}
defp gcd(a, 0), do: a
defp gcd(a, b), do: gcd(b, rem(a, b))
end
defimpl Inspect, for: Rational do
def inspect(r, _opts) do
"%Rational<#{r.numerator}/#{r.denominator}>"
end
end
Enum.each(2..trunc(:math.pow(2,19)), fn candidate ->
sum = 2 .. round(:math.sqrt(candidate))
|> Enum.reduce(Rational.new(1, candidate), fn factor,sum ->
if rem(candidate, factor) == 0 do
Rational.add(sum, Rational.new(1, factor))
|> Rational.add(Rational.new(1, div(candidate, factor)))
else
sum
end
end)
if sum.denominator == 1 do
:io.format "Sum of recipr. factors of ~6w = ~w exactly ~s~n",
[candidate, sum.numerator, (if sum.numerator == 1, do: "perfect!", else: "")]
end
end)