Current section
Files
Jump to
Current section
Files
lib/cpf.ex
defmodule CPF do
@moduledoc """
CPF mobulde provides functions to verify if a CPF is valid.
"""
@doc """
Returns `true` the given `cpf` is valid, otherwise `false`.
## Examples
iex> CPF.valid?(563_606_676_73)
true
iex> CPF.valid?(563_606_676_72)
false
"""
@spec valid?(integer) :: boolean
def valid?(cpf) when is_integer(cpf) do
cpf_digits = Integer.digits(cpf)
padding = 11 - length(cpf_digits)
same_digits? = cpf_digits |> Enum.uniq() |> length() == 1
if padding >= 0 && !same_digits? do
cpf_digits = cpf_digits |> add_padding(padding) |> List.to_tuple()
{v1, v2} = calculate_verifier_digits(cpf_digits)
{given_v1, given_v2} = extract_given_verifier_digits(cpf_digits)
v1 == given_v1 && v2 == given_v2
else
false
end
end
def valid?(_cpf), do: false
defp add_padding(digits, 0), do: digits
defp add_padding(digits, padding) do
add_padding([0 | digits], padding - 1)
end
defp calculate_verifier_digits(digits) do
{v1_sum, v2_sum} = sum_digits(digits)
v1 = digit_verifier(v1_sum)
v2_sum = v2_sum + 2 * v1
v2 = digit_verifier(v2_sum)
{v1, v2}
end
defp sum_digits(digits) do
v1_weight = 10
v2_weight = 11
Enum.reduce(0..8, {0, 0}, fn i, {v1_sum, v2_sum} ->
v1 = (v1_weight - i) * elem(digits, i)
v2 = (v2_weight - i) * elem(digits, i)
{v1_sum + v1, v2_sum + v2}
end)
end
defp digit_verifier(sum) do
rem = rem(sum, 11)
if rem in [0, 1], do: 0, else: 11 - rem
end
defp extract_given_verifier_digits(digits) do
{elem(digits, 9), elem(digits, 10)}
end
end