# Conditoinal assignment and incrementing

I’m a bit new to Elixir, and I’m trying to conditionally increment a value. This is used as part of determining the complexity of a given password. If a given password contains any digits, I want to increase the `keyspace` by `10`. If the the given password also contains a lowercase letter, I want to increase the `keyspace` by `26`.

Here is a psuedo-code version I have that is mostly Elixir syntax, but typical imperative style:

``````  def determine_password_complexity(proposed_password) do
keyspace = 0

# The password contains at least one common capital letter.

keyspace = keyspace + 26  # There are 26 common capital letters.
end
# The password contains at least one common lowercase letter.

keyspace = keyspace + 26  # There are 26 common lowercase letters.
end
# The password contains at least one digit.

keyspace = keyspace + 10  # There are 10 common digits.
end
# The password contains at least one special character.

keyspace = keyspace + 32  # There are 32 common special characters (inluding one whitespace character).
end

end
``````

The first error I run into informs me that `keyspace` won’t leak out of the conditional, so I re-wrote it as suggested, like this:

``````  defp determine_password_complexity(proposed_password) do
keyspace = 0

keyspace =
# The password contains at least one common capital letter.

keyspace + 26  # There are 26 common capital letters.
end
keyspace =
# The password contains at least one common lowercase letter.

keyspace + 26  # There are 26 common lowercase letters.
end
keyspace =
# The password contains at least one digit.

keyspace + 10  # There are 10 common digits.
end
keyspace =
# The password contains at least one special character.

keyspace + 32  # There are 32 common special characters (inluding one whitespace character).
end

end
``````

When invoked, the code above produces the error `bad argument in arithmetic expression` which happens on any of the lines that look like `keyspace + 26`. I assume it’s because I’m trying to use the `keyspace` variable out of scope? I’m a bit lost on this one…

`if false, do: true` returns `nil`, therefore for any falsy condition you set `keyspace` to `nil` in your second example. Your first example is invalid due to scoping rules, as you have already been told by the compiler.

You could rewrite your second example to have an explicit `else` clause which returns the old value of `keyspace`, eg: `ks = if is_digit(c), do: ks + 10, else: ks`.

A more idiomatic way were to do it like this:

``````def complexity(password, keyspace \\ 0, length \\ 0)
def complexity(<<>>, ks, l), do: :math.pow(ks, l)
def complexity(<<d::utf8, rest>>, ks, l) when d in ?0..?9, do: complexity(rest, ks + 10, l + 1)
def complexity(<<a::utf8, rest>>, ks, l) when a in ?a..?z or a in ?A..?Z, do: complexity(rest, ks + 26, l + 1)
def complexity(<<_::utf8, rest>>, ks, l), do: complexity(rest, ks + 32, l + 1)
``````
3 Likes

This is a great example. If you think about what your code has here, it has a set of regexes, and a weight for each regex. We want to build a value by running those rules against your password:

``````def determine_password_complexity(proposed_password) do
regexes = %{
~r/[A-Z]/ => 26,
~r/[a-z]/ => 26,
~r/[0-9]/ => 10,
~r/[^A-Za-z0-9]+/ => 32
}

keyspace = Enum.reduce(regexes, 0, fn {regex, weight}, total ->
total + weight
else
total
end
end)

PS: I just realize, that my implementation is wrong, as it will increase the `keyspace` for every occurence. I misread the specification. Therefore I think @benwilson512 reply is more appropriate.