diff --git a/.gitignore b/.gitignore index b6012c7..4f7a773 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ erl_crash.dump # Also ignore archive artifacts (built via "mix archive.build"). *.ez + +# asdf-vm version file +/.tool-versions diff --git a/lib/ex_junk.ex b/lib/ex_junk.ex index de89021..1a66923 100644 --- a/lib/ex_junk.ex +++ b/lib/ex_junk.ex @@ -35,6 +35,21 @@ defmodule Junk do post_op(output, opts) end + def junk(:long_npi, opts) do + opts = construct_opts(opts) + |> Map.put(:size, 8) + # grab 8 digits, and put a 1 or 2 on the front + junk(Integer, opts) + |> Integer.digits + |> (fn digits -> [8,0,8,4,0] ++ [Enum.random(1..2)] ++ digits end).() + |> Integer.undigits + |> Junk.luhn + end + + def junk(:npi, opts) do + junk(:long_npi, opts) - trunc(80840 * :math.pow(10, 10)) + end + def junk(f, opts) when is_function(f) do opts = construct_opts(opts) apply(f, opts.parameters) @@ -49,11 +64,38 @@ defmodule Junk do end end + def luhn(number) do + base_digits = Integer.digits(number) + check_sum = base_digits + |> Enum.reverse + |> Junk.map_every(2, fn(n) ->Integer.digits(n*2) end) + |> List.flatten + |> Enum.sum + + # calcs the check digit + check_digit = case Kernel.rem(check_sum, 10) do + 0 -> 0 + n -> 10 - n + end + + (base_digits ++ [check_digit]) |> Integer.undigits + end + + def map_every(enumerable, nth, mapper) do + {res, _acc} = Enum.map_reduce(enumerable, 0, fn(x, i) -> if (rem(i, nth) == 0) do + {mapper.(x), i+1} + else + {x, i+1} + end + end) + res + end + defp construct_opts(opts) do Enum.into(opts, Map.from_struct(%Junk{})) end defp post_op(output, opts) do - output = if opts.prefix, do: "#{opts.prefix}-#{output}", else: output + if opts.prefix, do: "#{opts.prefix}-#{output}", else: output end end diff --git a/test/ex_junk_test.exs b/test/ex_junk_test.exs index 3078cc6..418893a 100644 --- a/test/ex_junk_test.exs +++ b/test/ex_junk_test.exs @@ -23,6 +23,13 @@ defmodule JunkTest do assert output != Junk.junk(String, byte_size: 5) end + test "unnecessary options are ignored" do + string = Junk.junk(String, size: 10) + integer = Junk.junk(Integer, byte_size: "asdf") + assert is_binary(string) == true + assert integer |> is_integer == true + end + test "returns unique Strings" do output = Junk.junk(String) assert is_binary(output) == true @@ -79,6 +86,23 @@ defmodule JunkTest do assert "123-45-6789" = output end + test ":npi returns a short NPI" do + digits = Junk.junk(:npi) |> Integer.digits + assert Kernel.length(digits) == 10 + assert Enum.member?([1,2], List.first(digits)) + end + + test ":long_npi returns a long NPI" do + digits = Junk.junk(:long_npi) |> Integer.digits + assert Kernel.length(digits) == 15 + first_6 = digits |> Enum.take(6) |> Integer.undigits + assert Enum.member?([808401,808402], first_6) + end + + test "luhn returns a luhn'd number" do + assert Junk.luhn(80840268496713) == 808402684967138 + end + def ssn do "123-45-6789" end