Skip to content

Instantly share code, notes, and snippets.

@Liru
Created May 3, 2019 07:06
Show Gist options
  • Save Liru/c06b44a9d30fa378fec9a6427d9bbfa2 to your computer and use it in GitHub Desktop.
Save Liru/c06b44a9d30fa378fec9a6427d9bbfa2 to your computer and use it in GitHub Desktop.
defmodule GreekTax.Helper do
@moduledoc false
import NimbleParsec
@number [?0..?9]
def sep_number(sep) do
ascii_string(@number, 3)
|> ignore(string(sep))
|> ascii_string(@number, 5)
|> ignore(string(sep))
|> integer(1)
|> label("Greek tax number with \"#{sep}\" separator")
end
end
defmodule GreekTax do
@moduledoc """
Documentation for GreekTax.
## Examples
iex> GreekTax.valid?("000000000")
false
iex> GreekTax.valid?("000000001")
false
iex> GreekTax.valid?("094277965")
true
iex> GreekTax.valid?("094 27796 5")
true
iex> GreekTax.valid?("094-27796-5")
true
iex> GreekTax.valid?("09427796")
false
iex> GreekTax.valid?("094 27796 a")
false
"""
import NimbleParsec
import GreekTax.Helper
defcombinatorp(
:tax_number,
choice([sep_number(""), sep_number(" "), sep_number("-")])
)
defparsec(:parse, parsec(:tax_number))
def valid?(tax_number) do
case parse(tax_number) do
{:error, _, _, _, _, _} ->
false
{:ok, ["000", "00000", 0], _, _, _, _} ->
false
{:ok, [num1, num2, check_digit], _, _, _, _} ->
Enum.flat_map([num1, num2], &String.graphemes/1)
|> Enum.map(&String.to_integer/1)
|> valid_checksum?(check_digit)
end
end
@multipliers 8..1 |> Enum.map(&(:math.pow(2, &1) |> trunc))
defp valid_checksum?(numbers, check_digit) do
check_digit ==
Enum.zip(numbers, @multipliers)
|> Enum.map(&mult/1)
|> Enum.sum()
|> rem(11)
|> case do
10 -> 0
x -> x
end
end
defp mult({x, y}), do: x * y
end
@spapas
Copy link

spapas commented May 3, 2019

Thanks @Liru! I've adopted some of your recommendations, i.e breaking the code to more functions, the usage of multipliers and zip to make the algorith logic easier and piping the final case (I didn't really know that case could be piped): https://github.com/spapas/validate_greek_tax_number/blob/master/lib/validate_gr_tax_num.ex

For the NimbleParsec I'll prefer my own (simpler) logic because I don't really want any dependencies and I think that using the parsing lib makes the code more difficult to understand. Also notice that the space or dash does not need to be in a specific position i.e the string 0 9 4 27 796 5 would also be valid (i.e I don't care where that whitespace is).

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment