Today’s problem was to implement a function that can validate and determine the type of a triangle based on side lengths passed in as arguments. For example, `(1, 1, 1)`

should tell us that this is a valid triangle and of type `equilateral`

.

## Live solution video

This is my daily live stream video solution to this problem.

- Video available here: https://youtu.be/mkeZfue5F8M
- Also streaming on Twitch: https://www.twitch.tv/percygrunwald

## Explanation of the solution

### Functions to determine whether a triangle is equilateral, isosceles or scalene

It’s fairly straightforward to implement functions to determine whether a triangle is equilateral, isosceles or scalene. All of them are one-liners using simple equality comparison operators and some boolean `and/or`

.

Equilateral triangles have 3 sides of equal length:

```
defp is_equilateral?(a, b, c) do
a == b and b == c
end
```

Isosceles triangles have 2 sides of equal length:

```
defp is_isosceles?(a, b, c) do
a == b or a == c or b == c
end
```

Scalene triangles have 3 sides of different lengths:

```
defp is_scalene?(a, b, c) do
a != b and a != c and b != c
end
```

### Validation: are all side lengths positive?

Part of the validation we needed to implement was to check if all the sides of the triangle are positive - i.e. greater than 0. This is also easy to implement as a one-liner:

```
defp all_side_lengths_positive?(a, b, c) do
a > 0 and b > 0 and c > 0
end
```

### Validation: does the triangle meet the triangle inequality?

This validation is a little more complex. The *triangle inequality* states that for all triangles with non-zero area (i.e. other than a line) each side must be less than the sum of the other sides.

This makes sense, because if any side is *equal* to the sum of the other sides, it means that the triangle is just two lines end to end. If any side is *greater* than the sum of the other sides, we’re no longer talking about a triangle at all, since two lines end to end can’t be longer than the sum of their lengths.

Initially, I went down a path of trying to implement a function to get the longest side and compare it to the sum of the other 2 sides. While working on this function, Twitch user `SegFaultAx`

helpfully pointed out in Twitch chat that this function can also be implemented as a combination of inequalities:

```
defp triangle_meets_triangle_inequality?(a, b, c) do
b + c > a and a + c > b and a + b > c
end
```

### Main function to tie everything together

Now that we have all the functions to validate and categorize the triangle, we just need to tie everything together.

I opted to use a `cond`

statement since I think it’s the most readable way to express the flow of this function:

```
@doc """
Return the kind of triangle of a triangle with 'a', 'b' and 'c' as lengths.
"""
@spec kind(number, number, number) :: {:ok, kind} | {:error, String.t()}
def kind(a, b, c) do
cond do
not all_side_lengths_positive?(a, b, c) ->
{:error, "all side lengths must be positive"}
not triangle_meets_triangle_inequality?(a, b, c) ->
{:error, "side lengths violate triangle inequality"}
is_equilateral?(a, b, c) ->
{:ok, :equilateral}
is_isosceles?(a, b, c) ->
{:ok, :isosceles}
is_scalene?(a, b, c) ->
{:ok, :scalene}
true ->
{:error, "Not a valid triangle"}
end
end
```

## Full solution in text form

Here’s the full solution in text form, in case you want to look over the final product:

```
defmodule Triangle do
@type kind :: :equilateral | :isosceles | :scalene
@doc """
Return the kind of triangle of a triangle with 'a', 'b' and 'c' as lengths.
"""
@spec kind(number, number, number) :: {:ok, kind} | {:error, String.t()}
def kind(a, b, c) do
cond do
not all_side_lengths_positive?(a, b, c) ->
{:error, "all side lengths must be positive"}
not triangle_meets_triangle_inequality?(a, b, c) ->
{:error, "side lengths violate triangle inequality"}
is_equilateral?(a, b, c) ->
{:ok, :equilateral}
is_isosceles?(a, b, c) ->
{:ok, :isosceles}
is_scalene?(a, b, c) ->
{:ok, :scalene}
true ->
{:error, "Not a valid triangle"}
end
end
defp all_side_lengths_positive?(a, b, c) do
a > 0 and b > 0 and c > 0
end
defp triangle_meets_triangle_inequality?(a, b, c) do
b + c > a and a + c > b and a + b > c
end
defp is_equilateral?(a, b, c) do
a == b and b == c
end
defp is_isosceles?(a, b, c) do
a == b or a == c or b == c
end
defp is_scalene?(a, b, c) do
a != b and a != c and b != c
end
end
```