Last time we spent some time writing a function to generate permutations of length `k`

of a given `list`

. Our final solution was fairly concise, but there are quite a few places where we could have made a mistake in our implementation.

Our first instinct might be to check our work using unit tests. Unfortunately using unit tests to check the correctness of a permutation generator leaves something to be desired. The length of our resulting set of permutations grows rapidly when `k`

and `list`

increase in size, making it feasible to only manually calculate and test the smallest possible permutations.

Thankfully there’s another way. We can use property testing to test the underlying properties of our solution and the permutations we’re generating. This will give us quite a bit more variation on the inputs we test and might uncover some hidden bugs!

## Our Permutation Generator

The permutation generator that we’ll be testing looks like this:

```
defmodule Permutation do
def generate(list, k \\ nil, repetitions \\ false)
def generate([], _k, _repetitions), do: [[]]
def generate(_list, 0, _repetitions), do: [[]]
def generate(list, k, repetitions) do
for head <- list,
tail <- generate(next_list(list, head, repetitions), next_k(k)),
do: [head | tail]
end
defp next_k(k) when is_number(k),
do: k - 1
defp next_k(k),
do: k
defp next_list(list, _head, true),
do: list
defp next_list(list, head, false),
do: list -- [head]
end
```

You’ll notice that it’s a little different than the generator we built last time. This generator supports the creation of permutations both with and without repetitions of elements from `list`

, and lets you optionally pass in the length of the final permutations, `k`

.

We can use our `Permutation.generate/3`

function like so:

```
iex(1)> Permutation.generate([1, 2, 3])
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
```

We can also specify a value for `k`

:

```
iex(2)> Permutation.generate([1, 2, 3], 2)
[[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
```

And we can tell our generator to allow repetitions in the final permutations:

```
iex(3)> Permutation.generate([1, 2, 3], 2, true)
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
```

All looks good so far, but dragons hide in the depths. Let’s dig deeper.

## List of Lists

Permutations have some fairly well-known and easily testable properties. For example, we know that the results of our calls to `Permutation.generate/3`

should always have the structure of a list of lists.

Using Elixir’s `StreamData`

package, we can easily model and check that this property holds for our `Permuatation.generate/3`

function across a wide range of inputs. Let’s start by creating a new property test to verify this for us:

```
property "returns a list of lists" do
end
```

We start by telling `StreamData`

that we want it to generate lists of, at most, `@max_length`

(which we’ll define at `5`

for now) integers.

```
property "returns a list of lists" do
+ check all list <- list_of(integer(), max_length: @max_length) do
+ end
end
```

Next, we call our `Permutation.generate/3`

function to create the permutations of the `list`

that was just generated for us:

```
property "returns a list of lists" do
check all list <- list_of(integer(), max_length: @max_length) do
+ permutations = Permutation.generate(list)
end
end
```

Finally we’ll make assertions about the structure of our resulting `permutations`

. We want to assert that the result of our call to `Permutation.generate/3`

is a list, but also that every element in that result is a list as well:

```
property "returns a list of lists" do
check all list <- list_of(integer(), max_length: @max_length) do
permutations = Permutation.generate(list)
+ assert permutations |> is_list
+ Enum.map(permutations, &assert(&1 |> is_list))
end
end
```

And that’s all there is to it. Running out test suite, we’ll see that our first property test passed with flying colors (well, mostly green).

```
.
Finished in 0.06 seconds
1 property, 0 failures
```

## Correct Number of Permutations

Now that we know that the structure of our resulting list of permutations is correct, the next obvious property that we can test is that the number of permutations returned by our `Permutation.generate/3`

function is what we’d expect.

Permutations are a well-defined mathematical concept, and so a nice equation exists to determine how many `k`

-length permutations exist for a list of `n`

elements:

```
P(n, k) = n! / (n - k)!
```

Let’s write a quick factorial function to help calculate this value:

```
defp factorial(n) when n <= 0,
do: 1
defp factorial(n),
do: n * factorial(n - 1)
```

Let’s also rewrite our `P(n, k)`

calculation as an Elixir helper function:

```
defp pnk(list, k),
do: div(factorial(length(list)), factorial(length(list) - k))
```

Great!

Now we’re set up to test that our `Permutation.generate/3`

function is giving us the correct number of permutations for a given `list`

and value of `k`

.

```
property "returns the correct number of permutations" do
end
```

This time we’ll generate our `list`

, along with a value for `k`

that ranges from `0`

to the length of `list`

:

```
property "returns the correct number of permutations" do
+ check all list <- list_of(integer(), max_length: @max_length),
+ k <- integer(0..length(list)) do
end
end
```

Once we have values for `list`

and `k`

, we can generate our set of permutations and make an assertion about its length:

```
property "returns the correct number of permutations" do
check all list <- list_of(integer(), max_length: @max_length),
k <- integer(0..length(list)) do
+ assert pnk(list, k) ==
+ list
+ |> Permutation.generate(k)
+ |> length
end
end
```

Once again, our tests pass.

```
..
Finished in 0.06 seconds
2 properties, 0 failures
```

## Only Include Elements From the List

Another neatly testable property of the permutations we’re generating is that they should only contain values from the `list`

being permutated. Once again, we’ll start by defining the property we’ll be testing and generate our values for `list`

and `k`

:

```
property "permutations only include elements from list" do
check all list <- list_of(integer(), max_length: @max_length),
k <- integer(0..length(list)) do
end
end
```

Next, we’ll want to generate our set of permutations for `list`

and `k`

, and reject any permutations from that set that include values not found in `list`

:

```
property "permutations only include elements from list" do
check all list <- list_of(integer(), max_length: @max_length),
k <- integer(0..length(list)) do
+ assert [] ==
+ list
+ |> Permutation.generate(k)
+ |> Enum.reject(fn permutation ->
+ [] ==
+ permutation
+ |> Enum.reject(&Enum.member?(list, &1))
+ end)
end
end
```

We’re asserting that the resulting list of permutations should be an empty list (`[]`

). There should be no permutations left that contain elements not found in `list`

!

And, as expected, our suite still passes.

```
...
Finished in 0.08 seconds
3 properties, 0 failures
```

## Use Each Item Once

Our current implementation of `Permutation.generate/3`

allows for duplicate items to be passed in through `list`

. When we generate each possible permutation, it’s important that each of these duplicate items, and more generally any item in the list, only be used once.

That is, if `list`

is `[1, 2, 2]`

, our set of possible permutations should look like this:

```
[[1, 2, 2], [1, 2, 2], [2, 1, 2], [2, 2, 1], [2, 1, 2], [2, 2, 1]]
```

Note that `2`

is used twice in each permutation, but never more than twice.

At first, it seems like we might need a new test to verify this property of our permutation generator. It’s conceivable that we could group each set of equal elements, count them, and verify that the resulting permutation have the correct count of each element group. But that sounds complicated, and an added test just introduces more code into our codebase that we need to maintain.

It turns out that we can tweak our previous property test to verify this new property.

Instead of identifying duplicates, counting them, and verifying the correct counts in the final set of permutations, let’s take a simpler approach. Let’s ensure that each element in `list`

is unique by using Elixir’s `Enum.with_index/1`

function to bundle the element with its index value.

For example, our previous `[1, 2, 2]`

value for `list`

would be transformed into:

```
[{1, 0}, {2, 1}, {2, 2}]
```

Now both of our `2`

elements are unique. The first is `{2, 1}`

, and the second is `{2, 2}`

. Using this technique, we can recycle our “permutations only include elements from list” with a few slight tweaks:

```
property "permutations only include elements from list" do
check all list <- list_of(integer(), max_length: @max_length),
k <- integer(0..length(list)) do
assert [] ==
list
+ |> Enum.with_index()
|> Permutation.generate(k)
|> Enum.reject(fn permutation ->
[] ==
permutation
- |> Enum.reject(&Enum.member?(list, &1))
+ |> Enum.reject(&Enum.member?(list |> Enum.with_index(), &1))
end)
end
end
```

And once again, our suite passes:

```
...
Finished in 0.08 seconds
3 properties, 0 failures
```

## Final Thoughts

The current version of our property tests currently only test values of `list`

composed entirely of integers. This doesn’t necessarily need to be the case. The `list`

being permutated can contain *literally anything*, in theory. Expanding our property tests to support a wider range of types might be a great opportunity to try out writing a custom generator function!

Our current test suite also defines `@max_length`

as `5`

. While testing, I noticed that values of `@max_length`

up to `8`

were very performant and finished in under a second on my machine. Running the suite with a `@max_length`

of `9`

took several seconds to complete, and using a value of `10`

took nearly two minutes to come back green. I’m not sure if these performance problems can easily be improved, but I’m happy about how obvious they became through property testing.

You’ll also note that none of this testing covers generating permutations that allow infinite repetitions of elements from `list`

. The properties for these sets of permutations are completely different, so I’m going to leave this as an exercise for the truly motivated reader.

I’m enjoying my experiences with property testing so far. Hopefully you find these write-ups useful as well. If so, let me know on Twitter!