67 lines
1.9 KiB
Elixir
67 lines
1.9 KiB
Elixir
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)
|