chunky v0.13.0 Chunky.Geometry.Triangle View Source

Functions for working with triangles. For predicate functions related to Triangles, see Chunky.Geometry.Triangle.Predicates.

Triangles in Chunky are represented as a tuple of three positive integers, with each integer greater than or equal to 1. So {3, 4, 5} is a triangle, as is {145, 7, 139}. Not every 3-tuple of integers is a valid triangle - {3, 5, 10} doesn't describe a realizable triangle. You can test for a valid triangle with Chunky.Geometry.is_valid_triangle?/1.

Some functions will raise argument errors for invalid triangles, while others will return an error tuple, where possible.

Triangle Functions

Measurements

These functions provide measurements of triangles, their angles, or their shape.

Construction and Deconstructing Triangles

Create new triangles, break existing triangles into smaller triangles, or recombine smaller triangles:

  • compose/2 - Create a new triangle from two compatible right triangles
  • decompose/1 - Break a triangle into two smaller right triangles
  • normalize/1 - Normalize the ordering of sides of a triangle
  • triangles_from_hypotenuse/2 - Generate integer triangles given a hypotenuse and optional filter

Meta-inspection of triangles

Metadata about triangles:

  • type/1 - Determine the basic type, or form, of a triangle

Finding Triangles

Finding specific shapes of triangles (like Heronian, m-heronian, scalenes, etc) can be useful. For instance, finding triangles that are decomposable or 19-heronian triangles. Constructing these by hand can be tedious - instead we can combine a series of functions from the Triangle and Predicates modules to help find what we're looking for.

The heart of searching for triangles is the triangles_from_hypotenuse/2 function, which generates the set of all valid triangles for a given hypotenuse edge:

iex> Triangle.triangles_from_hypotenuse(3)
[{1, 3, 3}, {2, 2, 3}, {2, 3, 3}, {3, 3, 3}]

All of the valid triangles for the given hypotenuse are generated, without duplicates, edge order independent (so {3, 2, 3} and {2, 3, 3} would be the same, and only one included in the output). For a small hypotenuse this output by itself can be useful for visual inspection, but the number of valid triangles grows fairly quickly. For a hypotenuse of 15, there are 64 valid triangles. For a hypotenuse of 100, there are 2550 triangles. On to the next step: filter functions.

The second parameter of triangles_from_hypotenuse/2 is a predicate function - any function that takes a triangle as it's only parameter, and returns a boolean. The output of triangles_from_hypotenuse/2 will be filtered to contain only those triangles that pass the filter. For instance, we can filter our list of triangles with a hypotenuse of 3 to only the equilateral triangles:

iex> Triangle.triangles_from_hypotenuse(3, filter: &Triangle.Predicates.is_equilateral?/1)
[{3, 3, 3}]

Let's look at the first example we cited, finding decomposable triangles. Constructing these by hand can be tedious. But using the above technique, we can quickly find what we're looking for. How about triangles with a hypotenuse of 30 that are decomposable:

iex> Triangle.triangles_from_hypotenuse(30, filter: &Triangle.Predicates.is_decomposable?/1)
[{8, 26, 30}, {11, 25, 30}, {17, 17, 30}, {25, 25, 30}, {26, 28, 30}]

If we want to tackle the second example we cited, finding 19-heronian triangles (these are triangles whose area is exactly 19 times their perimeter), we'll need to expand our search: we don't know exactly what the hypotenuse will be, so we check multiple:

150..250 
|> Enum.map(
    fn h -> 
        Triangle.triangles_from_hypotenuse(
            h, 
            filter: fn t -> 
                Triangle.is_multiple_heronian_triangle?(t, 19) 
            end) 
    end
) 
|> List.flatten()

Here we've take the range of 150 to 250, and used each of those in order as the hypotenuse of triangles_from_hypotenuse/2. The filter function is an anonymous function, where we use the is_multiple_heronian?/2 function to filter down to only those triangles that are an exact multiple of perimeter and area.

Link to this section Summary

Functions

Find the angles of a triangle.

Find the area of a triangle, returning either an integer, float, or fraction value.

Compose two pythagorean (right) triangles into a larger triangle, if possible.

Decompose an integer triangle into two smaller integer right triangles.

Find the height of a triangle from the hypotenuse.

Is a triangle a heronian triangle where the area is a specific multiple of the perimeter?

Normalize the order of edges of a triangle.

Generate all of the integer triangles from a given hypotenuse h.

Determine the characteristic type of a triangle, like right, scalene, or isoceles.

Link to this section Functions

Find the angles of a triangle.

The resulting 3-tuple will be floats, in order such that each angle is opposite it's edge. So for a triangle with sides {a, b, c}, the resulting angles {θa, θb, θc} are such that θa is opposite edge a, θb opposite edge b, and θc opposite edge c:

Triangle

Examples

iex> Triangle.angles({3, 4, 5})
{36.86989764584401, 53.13010235415599, 90.0}

iex> Triangle.angles({13, 13, 13})
{59.99999999999999, 59.99999999999999, 59.99999999999999}

iex> Triangle.angles({10, 5, 10})
{75.52248781407008, 28.95502437185985, 75.52248781407008}

iex> Triangle.angles({30, 16, 17})
{130.73591716163173, 23.83600707762401, 25.428075760744253}

Find the area of a triangle, returning either an integer, float, or fraction value.

This function uses the heronian formula for calculating the area of any triangle.

Examples

iex> Triangle.area({3, 4, 5})
{:integer, 6}

iex> Triangle.area({5, 15, 15})
{:float, 36.9754986443726}

iex> Triangle.area({5, 5, 5})
{:float, 10.825317547305483}

iex> Triangle.area({7, 11, 13})
{:float, 38.499188303131795}

iex> Triangle.area({5, 12, 13})
{:integer, 30}

iex> Triangle.area({5, 10, 20})
** (ArgumentError) Shape of the triangle is invalid

Compose two pythagorean (right) triangles into a larger triangle, if possible.

This is the inverse of decompose/1 - two compatible right triangles can be joined on an equal, non-hypotenuse edge to form a new triangle.

The results of this function will either be a tuple of {:ok, _} with a one or two new triangles, or {:error, reason} for not being composable.

Examples

iex> Triangle.compose({12, 5, 13}, {5, 13, 12})
{:ok, {10, 13, 13}, {13, 13, 24}}

iex> Triangle.compose({12, 5, 13}, {9, 15, 12})
{:ok, {13, 14, 15}}

iex> Triangle.compose({12, 5, 13}, {3, 4, 5})
{:error, :not_composable}

iex> Triangle.compose({12, 5, 13}, {5, 7, 24})
{:error, :not_pythagorean_triangles}

Decompose an integer triangle into two smaller integer right triangles.

Examples

iex> Triangle.decompose({3, 4, 5})
{:error, :indecomposable}

iex> Triangle.decompose({6, 6, 6})
{:error, :indecomposable}

iex> Triangle.decompose({10, 13, 13})
{:ok, {5, 12, 13}, {5, 12, 13}}

iex> Triangle.decompose({13, 14, 15})
{:ok, {5, 12, 13}, {9, 12, 15}}

iex> Triangle.decompose({13, 13, 24})
{:ok, {5, 12, 13}, {5, 12, 13}}

Find the height of a triangle from the hypotenuse.

This function will return an integer, fraction, or float.

Options

  • base - Atom. Default :large. One of :small, :medium, or :large. Side to use when calculating height for a scalene triangle

Examples

iex> Triangle.height({5, 5, 5})
{:float, 4.330127018922193}

iex> Triangle.height({3, 4, 5})
{:fractional, %Fraction{num: 12, den: 5}}

iex> Triangle.height({5, 5, 8})
{:integer, 3}

iex> Triangle.height({10, 13, 13})
{:integer, 12}

iex> Triangle.height({13, 14, 15})
{:fractional, %Fraction{num: 56, den: 5}}

iex> Triangle.height({13, 14, 15}, base: :medium)
{:integer, 12}

iex> Triangle.height({3, 4, 9})
** (ArgumentError) Shape of the triangle is invalid
Link to this function

is_multiple_heronian_triangle?(t, m)

View Source

Is a triangle a heronian triangle where the area is a specific multiple of the perimeter?

A 2-heronian triangle would have an area that is 2*perimeter of the triangle, while a 3-heronian would have an area that is 3*perimeter. For each multiple size m, there are a finite number of multiple heronians triangles that are m-heronian.

Examples

iex> Triangle.is_multiple_heronian_triangle?({13, 14, 15}, 2)
true

iex> Triangle.is_multiple_heronian_triangle?({11, 25, 30}, 2)
true

iex> Triangle.is_multiple_heronian_triangle?({25, 26, 17}, 3)
true

iex> Triangle.is_multiple_heronian_triangle?({25, 28, 17}, 3)
true

iex> Triangle.is_multiple_heronian_triangle?({25, 36, 29}, 4)
true

Normalize the order of edges of a triangle.

Examples

iex> Triangle.normalize({5, 13, 7})
{5, 7, 13}

iex> Triangle.normalize({12, 8, 5})
{5, 8, 12}

iex> Triangle.normalize({55, 13, 47})
{13, 47, 55}

iex> Triangle.normalize({3, 4, 5})
{3, 4, 5}
Link to this function

triangles_from_hypotenuse(h, opts \\ [])

View Source

Generate all of the integer triangles from a given hypotenuse h.

This will create a list of triangles with integer sides and hypotenuse h, with no duplicates, with order independent sides. So the triangle {3, 4, 5} and {4, 3, 5} would be considered the same, and only one of them would be included in the output.

An optional filter function can be provided which will be used to pre filter the result list during computation, which can significantly reduce memory consumption.

Options

  • filter - Predicate function of arity 1, which returns a boolean

Examples

iex> Triangle.triangles_from_hypotenuse(5)
[{1, 5, 5}, {2, 4, 5}, {2, 5, 5}, {3, 3, 5}, {3, 4, 5}, {3, 5, 5}, {4, 4, 5}, {4, 5, 5}, {5, 5, 5}]

iex> Triangle.triangles_from_hypotenuse(5, filter: &Triangle.Predicates.is_scalene?/1)
[{2, 4, 5}, {3, 4, 5}]

iex> Triangle.triangles_from_hypotenuse(5, filter: &Triangle.Predicates.is_pythagorean_triangle?/1)
[{3, 4, 5}]

iex> Triangle.triangles_from_hypotenuse(125, filter: &Triangle.Predicates.is_pythagorean_triangle?/1)
[{35, 120, 125}, {44, 117, 125}, {75, 100, 125}]

Determine the characteristic type of a triangle, like right, scalene, or isoceles.

The possible types returned by this function are:

  • :invalid
  • :right
  • :equilateral
  • :isoceles
  • :scalene

Examples

iex> Triangle.type({3, 4, 5})
:right

iex> Triangle.type({3, 4, 9})
:invalid

iex> Triangle.type({3, 4, 4})
:isoceles

iex> Triangle.type({13, 13, 13})
:equilateral

iex> Triangle.type({7, 13, 19})
:scalene