defmodule Caustic.Explain do
alias Caustic.Utils
alias Caustic.Format
@moduledoc """
Step-by-step explanation of the computation in `Caustic.Utils`.
"""
def totient(1) do
IO.puts "The only positive number less than or equal to 1 which is relatively prime to 1 is 1 itself."
IO.puts "Therefore, φ(1) = 1"
end
def totient(m) do
factors = Utils.factorize_grouped(m)
factor_count = Enum.count(factors)
factors_str = factors
|> Enum.map(fn
{a, 1} -> "#{a}"
{a, e} -> "#{a}^#{e}"
end)
factors_str_joined = factors_str |> Enum.join(" . ")
totient_expanded = factors_str |> Enum.map(&"φ(#{&1})") |> Enum.join(" ")
big_bracket_opening = if factor_count > 1, do: "[", else: ""
big_bracket_closing = if factor_count > 1, do: "]", else: ""
totient_expanded_2 = factors |> Enum.map(fn
{a, 1} -> "(#{a}-1)"
{a, e} -> "#{big_bracket_opening}#{a}^(#{e}-1) . (#{a}-1)#{big_bracket_closing}"
end) |> Enum.join(" . ")
totient_expanded_3 = factors |> Enum.map(fn
{a, 1} -> "#{a - 1}"
{a, e} -> "#{big_bracket_opening}#{a}^#{e - 1} . #{a - 1}#{big_bracket_closing}"
end) |> Enum.join(" . ")
if factor_count > 1 do
IO.puts "φ(m) is multiplicative, so φ(ab) = φ(a) φ(b) if a and b are relatively prime."
end
IO.puts "#{if factor_count > 1, do: "Also, ", else: ""}φ(p^n) = p^(n-1) . (p-1) for any prime p and positive integer n."
IO.puts "#{m} can be factorized as #{factors_str_joined}. Therefore,"
intro = "φ(#{m})"
steps = Enum.uniq([intro, "φ(#{factors_str_joined})", totient_expanded, totient_expanded_2, totient_expanded_3, "#{Utils.totient(m)}"])
Format.print_equations steps
end
def linear_congruence_solve(a, b, m) do
result = Utils.linear_congruence_solve a, b, m
a_str = if a == 1, do: "", else: "#{a}"
eq_str = "#{a_str}x = #{b} (mod #{m})"
if result == [] do
IO.puts "The equation #{eq_str} has no solutions."
else if length(result) == 1 do
[n] = result
IO.puts "The only solution of #{eq_str} is x = #{n}."
else
sol_str = Enum.join result, ", "
IO.puts "The solutions of #{eq_str} are x = #{sol_str}."
end end
end
end