Skip to content

Instantly share code, notes, and snippets.

@mrc
Created March 4, 2017 08:10
Show Gist options
  • Save mrc/eb78056fa0ae93b42efe16fbf0ccd3fa to your computer and use it in GitHub Desktop.
Save mrc/eb78056fa0ae93b42efe16fbf0ccd3fa to your computer and use it in GitHub Desktop.
-module(assignment).
-export([area/1, perimeter/1, enclose/1,
testArea/0, testPerimeter/0, testEnclose/0,
bits/1, bitsDirect/1, bitsTailRecursive/1,
testBits/0,
testAll/0, allTestsPass/0]).
%% circle: {circle, Radius}
%% square: {square, Length}
%% rectangle: {rectangle, Length, Width}
%% triangle: {triangle, A, B, C} (triple of side lengths)
area({circle, R}) ->
math:pi()*R*R;
area({square, L}) ->
L*L;
area({rectangle, L, W}) ->
L*W;
area({triangle, A, B, C}) ->
S = (A+B+C) / 2,
math:sqrt(S * (S-A) * (S-B) * (S-C)).
perimeter({circle, R}) ->
2*math:pi()*R;
perimeter({square, L}) ->
L*4;
perimeter({rectangle, L, W}) ->
L*2 + W*2;
perimeter({triangle, A, B, C}) ->
A + B + C.
enclose({circle, R}) ->
{rectangle, R*2, R*2};
enclose({square, L}) ->
{rectangle, L, L};
enclose({rectangle, L, W}) ->
{rectangle, L, W};
enclose({triangle, A, B, C}) ->
%% the bounding rectangle is as wide as one of the sides, and as
%% high as the height of the triangle if that side is considered
%% the base.
%%
%% for this calculation, the base (width) of the bounding
%% rectangle is parallel with C, so the height is the distance
%% between C and the point AB.
%%
%% theorum: no matter which side I use as the base, the area of
%% the bounding rectangle is the same, therefore the bounding
%% rectangle is the smallest possible no matter which side I use
%% as the base.
H = 2*(area({triangle, A, B, C}) / C),
{rectangle, H, C}.
almost(A, B) ->
Epsilon = 1.0e-5,
abs(A - B) < Epsilon.
testArea() ->
[almost(math:pi()*25, area({circle, 5})),
almost(25, area({square, 5})),
almost(30, area({rectangle, 5, 6})),
almost(6, area({triangle, 3, 4, 5}))].
testPerimeter() ->
[almost(math:pi()*10, perimeter({circle, 5})),
almost(20, perimeter({square, 5})),
almost(22, perimeter({rectangle, 5, 6})),
almost(12, perimeter({triangle, 3, 4, 5}))].
almostEquivalentRectangle(R1, R2) ->
almost(area(R1), area(R2)).
testEnclose() ->
[almostEquivalentRectangle({rectangle, 10, 10}, enclose({circle, 5})),
almostEquivalentRectangle({rectangle, 5, 5}, enclose({square, 5})),
almostEquivalentRectangle({rectangle, 5, 6}, enclose({rectangle, 5, 6})),
almostEquivalentRectangle({rectangle, 10, 10}, enclose({circle, 5})),
almostEquivalentRectangle({rectangle, 3, 4}, enclose({triangle, 3, 4, 5})),
almostEquivalentRectangle({rectangle, 3, 4}, enclose({triangle, 3, 5, 4})),
almostEquivalentRectangle({rectangle, 3, 4}, enclose({triangle, 4, 3, 5})),
almostEquivalentRectangle({rectangle, 3, 4}, enclose({triangle, 4, 5, 3})),
almostEquivalentRectangle({rectangle, 3, 4}, enclose({triangle, 5, 3, 4})),
almostEquivalentRectangle({rectangle, 3, 4}, enclose({triangle, 5, 4, 3}))].
%% bits: direct recursion
bitsDirect(0) ->
0;
bitsDirect(1) ->
1;
bitsDirect(N) ->
B = N band 1,
B + bitsDirect(N bsr 1).
%% bits: tail recursion
bitsHelper(0, Acc) ->
Acc;
bitsHelper(1, Acc) ->
Acc + 1;
bitsHelper(N, Acc) ->
bitsHelper(N bsr 1, Acc + N band 1).
bitsTailRecursive(N) ->
bitsHelper(N, 0).
%% assignment said "define bits/1".
%%
%% commentary: the tail recursive function is more efficient because
%% it doesn't use the stack to build the result, but the direct
%% function is easier to read (and was easier to construct).
bits(N) ->
bitsTailRecursive(N).
testBits() ->
[3 == bits(7),
1 == bits(8),
8 == bits(255),
1 == bits(256)].
testAll() ->
[testArea(), testPerimeter(), testEnclose(), testBits()].
allTestsPass() ->
lists:min(lists:flatten(testAll())).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment