About SICP The following Erlang code is derived from the examples provided in the book:
      "Structure and Interpretation of Computer Programs, Second Edition" by Harold Abelson and Gerald Jay Sussman with Julie Sussman.
      http://mitpress.mit.edu/sicp/

SICP Chapter #1 Examples in Erlang

-module(sicp01).
-export([start/0]).

print(X) ->
   io:write(X),
   io:format("~n").

start() ->
   section_1_1_1(),
   section_1_1_2(),
   section_1_1_3(),
   section_1_1_4(),
   section_1_1_5(),
   section_1_1_6(),
   section_1_1_7(),
   section_1_1_8(),
   section_1_2_1(),
   section_1_2_2(),
   section_1_2_3(),
   section_1_2_4(),
   section_1_2_5(),
   section_1_2_6(),
   section_1_3_1(),
   section_1_3_2(),
   section_1_3_3(),
   section_1_3_4().


% 1.1.1 The Elements of Programming - Expressions
section_1_1_1() ->
   print (486),
   print (137 + 349),
   print (1000 - 334),
   print (5 * 99),
   print (10 div 5),
   print (2.7 + 10),
   print (21 + 35 + 12 + 7),
   print (25 * 4 * 12),
   print (3 * 5 + 10 - 6),
   print (3 * (2 * 4 + 3 + 5) + 10 - 7 + 6).


% 1.1.2 The Elements of Programming - Naming and the Environment
section_1_1_2() ->
   Size = 2,
   print (Size),
   5 * Size,
   Pi = 3.14159,
   Radius = 10,
   print (Pi * Radius * Radius),
   Circumference = 2 * Pi * Radius,
   print (Circumference).


% 1.1.3 The Elements of Programming - Evaluating Combinations
section_1_1_3() ->
   print ((2 + 4 * 6) * (3 + 5 + 7)).


% 1.1.4 The Elements of Programming - Compound Procedures
section_1_1_4() ->
   print (square(21)),
   print (square(2 + 5)),
   print (square(square(3))),
   sum_of_squares(3, 4),
   print (f(5)).

square(X) -> X * X.

sum_of_squares(X, Y) -> square(X) + square(Y).

f(A) -> sum_of_squares(A + 1, A * 2).


% 1.1.5 The Elements of Programming - The Substitution Model for Procedure Application
section_1_1_5() ->
   print (f(5)),
   print (sum_of_squares(5 + 1, 5 * 2)),
   print (square(6) + square(10)),
   print (6*6 + 10*10),
   print (36 + 100),
   print (f(5)),
   print (sum_of_squares(5 + 1, 5 * 2)),
   print (square(5 + 1) + square(5 * 2)),

   print (((5 + 1) * (5 + 1)) + ((5 * 2) * (5 * 2))),
   print ((6 * 6) + (10 * 10)),
   print (36 + 100),
   print (136).


% 1.1.6 The Elements of Programming - Conditional Expressions and Predicates
section_1_1_6() ->
   X = 6,
   print ((X > 5) andalso (X < 10)),

   % Exercise 1.1
   print (10),
   print (5 + 3 + 4),
   print (9 - 1),
   print (6 div 2),
   print (2*4 + 4 - 6),
   A = 3,
   B = A + 1,
   print (A + B + A*B),
   print (A =:= B),
   print (if ((B > A) andalso (B < A * B)) -> B; true -> A end),
   print (
      if
         (A =:= 4) -> 6;
         (B =:= 4) -> 6 + 7 + A;
         true      -> 25
      end),
   print (2 + (if (B > A) -> B; true -> A end)),
   print (
      (if
         (A > B) -> A;
         (A < B) -> B;
         true    -> -1
       end) * (A + 1)),

   % Exercise 1.2
   print ((5 + 4 + (2 - (3 - (6 + (4 / 5))))) /
            (3 * (6 - 2) * (2 - 7))).

   % Exercise 1.5
   % commented out as this is in infinite loop
   % test(0, p()).

abs_0(X) ->
   if
      (X > 0) -> X;
      true ->
         if
            (X =:= 0) -> 0;
            true -> -X
         end
   end.

abs_1(X) ->
   if
      (X > 0) -> X;
      (X =:= 0) -> 0;
      true -> -X
   end.

abs_2(X) ->
   if
      (X < 0) -> -X;
      true    -> X
   end.

abs_3(X) when (X < 0) -> -X;
abs_3(X) -> X.

ge(X, Y) -> (X > Y) orelse (X =:= Y).

ge_1(X, Y) -> not(X < Y).

% Exercise 1.3
three_n(N1, N2, N3) ->
   if
      (N1 > N2) ->
         if
            (N1 > N3) ->
               if
                  (N2 > N3) -> N1*N1 + N2*N2;
                  true      -> N1*N1 + N3*N3
               end;
            true -> N1*N1 + N3*N3
         end;
      true ->
         if
            (N2 > N3) ->
               if
                  (N1 > N3) -> N2*N2 + N1*N1;
                  true      -> N2*N2 + N3*N3
               end;
            true -> N2*N2 + N3*N3
         end
   end.

% Exercise 1.4
a_plus_abs_b(A, B) ->
   if
      (B > 0) -> A + B;
      true -> A - B
   end.

% Exercise 1.5
p() -> p().
test(X, Y) ->
   if
      (X =:= 0) -> 0;
      true      -> Y
   end.


% 1.1.7 The Elements of Programming - Example: Square Roots by Newton's Method
section_1_1_7() ->
   print (sqrt(9)),
   print (sqrt(100 + 37)),
   print (sqrt(sqrt(2) + sqrt(3))),
   print (square(sqrt(1000))),

   % Exercise 1.6
   print (new_if((2=:=3), 0, 5)),
   print (new_if((1=:=1), 0, 5)).

good_enough(Guess, X) ->
   abs(square(Guess) - X) < 0.001.

average(X, Y) ->
   (X + Y) / 2.

improve(Guess, X) ->
   average(Guess, X / Guess).

sqrt_iter(Guess, X) ->
   case good_enough(Guess, X) of
      true  -> Guess;
      false -> sqrt_iter(improve(Guess, X), X)
   end.

sqrt(X) ->
   sqrt_iter(1, X).

% Exercise 1.6
new_if(Predicate, Then_Clause, Else_Clause) ->
   case (Predicate) of
      true  -> Then_Clause;
      false -> Else_Clause
   end.

sqrt_iter_0(Guess, X) ->
   new_if(
      good_enough(Guess, X),
      Guess,
      sqrt_iter(improve(Guess, X), X)).

% from wadler paper
newif_1(true, X, _)  -> X;
newif_1(false, _, Y) -> Y.

% Exercise 1.7
good_enough_gp(Guess, Prev) ->
   (abs(Guess-Prev) / Guess) < 0.001.

sqrt_iter_gp(Guess, Prev, X) ->
   case good_enough_gp(Guess, Prev) of
      true  -> Guess;
      false -> sqrt_iter_gp(improve(Guess, X), Guess, X)
   end.

sqrt_gp(X) ->
   sqrt_iter_gp(4, 1, X).

% Exercise 1.8
improve_cube(Guess, X) ->
   (2*Guess + X/(Guess * Guess)) / 3.

cube_iter(Guess, Prev, X) ->
   case good_enough_gp(Guess, Prev) of
      true  -> Guess;
      false -> cube_iter(improve_cube(Guess, X), Guess, X)
   end.

cube_root_0(X) ->
   cube_iter(27, 1, X).


% 1.1.8 The Elements of Programming - Procedures as Black-Box Abstractions
section_1_1_8() ->
   print (square(5)),
   print (sqrt_2(25)),
   print (sqrt_3(25)).

% same as above
% square(X) -> X * X.

double(X) -> X + X.

square_2(X) -> math:exp(double(math:log(X))).

good_enough_1(Guess, X) ->
   abs(square(Guess) - X) < 0.001.

improve_1(Guess, X) ->
   average(Guess, X / Guess).

sqrt_iter_1(Guess, X) ->
   case (good_enough_1(Guess, X)) of
      true  -> Guess;
      false -> sqrt_iter_1(improve_1(Guess, X), X)
   end.

sqrt_1(X) ->
   sqrt_iter_1(1.0, X).

% Block-structured
sqrt_2(X) ->
   Good_Enough =
      fun (Guess, X) ->
         abs(square(Guess) - X) < 0.001
      end,
   Improve =
      fun (Guess, X) ->
         average(Guess, X / Guess)
      end,
   Sqrt_Iter =
      fun (Self, Guess, X) ->
         case (Good_Enough(Guess, X)) of
            true  -> Guess;
            false -> Self(Self, Improve(Guess, X), X)
         end
      end,
   Sqrt_Iter(Sqrt_Iter, 1.0, X).

% Taking advantage of lexical scoping
sqrt_3(X) ->
   Good_Enough =
      fun (Guess) ->
         abs(square(Guess) - X) < 0.001
      end,
   Improve =
      fun (Guess) ->
         average(Guess, X / Guess)
      end,
   Sqrt_Iter =
      fun (Self, Guess) ->
         case (Good_Enough(Guess)) of
            true  -> Guess;
            false -> Self(Self, Improve(Guess))
         end
      end,
   Sqrt_Iter(Sqrt_Iter, 1.0).


% 1.2.1 Procedures and the Processes They Generate - Linear Recursion and Iteration
section_1_2_1() ->
   print (factorial(6)),
   print (factorial_0(6)),
   print (factorial_1(6)),
   print (a(1, 10)),
   print (a(2, 4)),
   print (a(3, 3)).

% Recursive
factorial(N) ->
   case (N =:= 1) of
      true  -> 1;
      false -> N * factorial(N - 1)
   end.

% alternate translation
factorial_0(1) -> 1;
factorial_0(N) -> N * factorial(N - 1).

% Iterative
fact_iter_1(Product, Counter, Max_Count) ->
   case (Counter > Max_Count) of
      true  -> Product;
      false -> fact_iter_1(Counter * Product, Counter + 1, Max_Count)
   end.

factorial_1(N) ->
   fact_iter_1(1, 1, N).

% Iterative, block-structured (from footnote)
factorial_2(N) ->
   Iter =
      fun (Self, Product, Counter) ->
         case (Counter > N) of
            true  -> Product;
            false -> Self(Self, Counter * Product, Counter + 1)
         end
      end,
   Iter(Iter, 1, 1).

% Exercise 1.9
inc(A) -> A + 1.
dec(A) -> A - 1.
plus(A, B) ->
   case (A =:= 0) of
      true  -> B;
      false -> inc(plus(dec(A), B))
   end.
plus_1(A, B) ->
   case (A =:= 0) of
      true  -> B;
      false -> plus(dec(A), inc(b))
   end.

% Exercise 1.10
a(_, 0) -> 0;
a(0, Y) -> 2 * Y;
a(_, 1) -> 2;
a(X, Y) -> a(X - 1, a(X, Y - 1)).

fx(N) -> a(0, N).
g_1(N) -> a(1, N).
h_1(N) -> a(2, N).
k_1(N) -> 5 * N * N.


% 1.2.2 Procedures and the Processes They Generate - Tree Recursion
section_1_2_2() ->
   print (count_change(100)),
   print (fi(5)),
   print (fi_1(5)),
   print (pascals_triangle(5, 3)).

% Recursive
fib(0) -> 0;
fib(1) -> 1;
fib(N) -> fib(N - 1) + fib(N - 2).

% Iterative
fib_iter(_, B, 0) -> B;
fib_iter(A, B, Count) -> fib_iter(A + B, A, Count - 1).

fib_1(N) -> fib_iter(1, 0, N).

% Counting change
first_denomination(1) -> 1;
first_denomination(2) -> 5;
first_denomination(3) -> 10;
first_denomination(4) -> 25;
first_denomination(5) -> 50.

cc(Amount, Kinds_Of_Coins) ->
   case (Amount =:= 0) of
      true -> 1;
      false ->
         case (Amount < 0) of
            true  -> 0;
            false ->
               case (Kinds_Of_Coins =:= 0) of
                  true  -> 0;
                  false ->
                     cc(Amount, Kinds_Of_Coins - 1) +
                     cc(Amount - first_denomination(Kinds_Of_Coins), Kinds_Of_Coins)
               end
         end
   end.

count_change(Amount) ->
   cc(Amount, 5).

% Exercise 1.11
fi(N) ->
   if
      (N < 3) -> N;
      true    -> fi(N - 1) + 2 * fi(N - 2) + 3 * fi(N - 3)
   end.

fi_iter(A, B, C, Count) ->
   if
      (Count =:= 0) -> C;
      true          -> fi_iter(A + 2 * B + 3 * C, A, B, Count - 1)
   end.

fi_1(N) -> fi_iter(2, 1, 0, N).

% Exercise 1.12
pascals_triangle(N, K) ->
   if
      (N =:= 0) -> 1;
      (K =:= 0) -> 1;
      (N =:= K) -> 1;
      true      -> pascals_triangle(N - 1, K - 1) + pascals_triangle(N - 1, K)
   end.

% Alternate implementation using pattern matching
pascals_triangle_2(0, _) -> 1;
pascals_triangle_2(_, 0) -> 1;
pascals_triangle_2(N, N) -> 1;
pascals_triangle_2(N, K) -> pascals_triangle_2(N - 1, K - 1) + pascals_triangle_2(N - 1, N).


% 1.2.3 Procedures and the Processes They Generate - Orders of Growth
section_1_2_3() ->
   print (sine(60.0)).

% Exercise 1.15
cube(X) -> X * X * X.
px(X) -> (3.0 * X) - (4.0 * cube(X)).
sine(Angle) ->
   case (abs(Angle) < 0.001) of
      true  -> Angle;
      false -> px(sine(Angle / 3.0))
   end.


% 1.2.4 Procedures and the Processes They Generate - Exponentiation
section_1_2_4() ->
   print (even(3)),
   print (multiply(3, 4)).

% Linear recursion
expt(_, 0) -> 1;
expt(B, N) -> B * expt(B, (N - 1)).

% Linear iteration
expt_iter(_, 0, Product) -> Product;
expt_iter(B, Counter, Product) ->
   expt_iter(B, Counter - 1, B * Product).

expt_1(B, N) ->
   expt_iter(B, N, 1).

% Logarithmic iteration
even(N) -> ((N rem 2) =:= 0).

fast_expt(_, 0) -> 1;
fast_expt(B, N) ->
  case (even(N)) of
     true  -> square(fast_expt(B, N div 2));
     false -> B * fast_expt(B, N - 1)
   end.

% Exercise 1.17
multiply(_, 0) -> 0;
multiply(A, B) ->
  plus(A, multiply(A, dec(B))).

% Exercise 1.19
fib_iter_2(_, B, _, _, 0) -> B;
fib_iter_2(A, B, P, Q, Count) ->
   case (even(Count)) of
      true  -> fib_iter_2(A, B, (P*P + Q*Q), (2*P*Q + Q*Q), Count div 2);
      false -> fib_iter_2((B * Q) + (A * Q) + (A * P), (B * P) + (A * Q), P, Q, Count - 1)
   end.

fib_2(N) ->
   fib_iter_2(1, 0, 0, 1, N).


% 1.2.5 Procedures and the Processes They Generate - Greatest Common Divisors
section_1_2_5() ->
   print (gcd(40, 6)),

   % Exercise 1.20
   print (gcd(206, 40)).

gcd(A, 0) -> A;
gcd(A, B) -> gcd(B, A rem B).


% 1.2.6 Procedures and the Processes They Generate - Example: Testing for Primality
section_1_2_6() ->
   timed_prime_test(13),

   % Exercise 1.21
   print (smallest_divisor(199)),
   print (smallest_divisor(1999)),
   print (smallest_divisor(19999)),

   % Exercise 1.27
   print (carmichael(561)),
   print (carmichael(1105)),
   print (carmichael(1729)),
   print (carmichael(2465)),
   print (carmichael(2821)),
   print (carmichael(6601)).

% prime
divides(A, B) -> ((B rem A) =:= 0).

find_divisor(N, Test_Divisor) ->
   case (square(Test_Divisor) > N) of
      true  -> N;
      false ->
         case (divides(Test_Divisor, N)) of
            true -> Test_Divisor;
            false -> find_divisor(N, Test_Divisor + 1)
         end
   end.

smallest_divisor(N) -> find_divisor(N, 2).

prime(N) -> (N =:= smallest_divisor(N)).

% fast_prime
expmod(_, 0, _) -> 1;
expmod(Nbase, Nexp, M) ->
  case (even(Nexp)) of
     true  -> square(expmod(Nbase, Nexp div 2, M)) rem M;
     false -> (Nbase * (expmod(Nbase, (Nexp - 1), M))) rem M
   end.

fermat_test(N) ->
   Try =
      fun (A) ->
         (expmod(A, N, N) =:= A)
      end,
   Try(1 + random:uniform(N - 1)).

fast_prime(_, 0) -> true;
fast_prime(N, Ntimes) ->
  case (fermat_test(N)) of
     true -> fast_prime(N, Ntimes - 1);
     false -> false
   end.

% Exercise 1.22
report_prime(Elapsed_Time) ->
   io:format(" *** ~w~n", [Elapsed_Time]).

get_time() ->
   case (now()) of
      {_, Secs, _} -> Secs
   end.

start_prime_test(N, Start_Time) ->
   case (prime(N)) of
      true  -> report_prime(get_time() - Start_Time);
      false -> {}
   end.

timed_prime_test(N) ->
   start_prime_test(N, get_time()).

% Exercise 1.25
expmod_1(Nbase, Nexp, M) ->
   fast_expt(Nbase, Nexp) rem M.

% Exercise 1.26
expmod_2(_, 0, _) -> 1;
expmod_2(Nbase, Nexp, M) ->
   case (even(Nexp)) of
      true  -> (expmod_2(Nbase, (Nexp div 2), M) *
                expmod_2(Nbase, (Nexp div 2), M)) rem M;
      false -> (Nbase * expmod_2(Nbase, Nexp - 1, M)) rem M
   end.

% Exercise 1.27
carmichael(N) ->
   fast_prime(N, 100) andalso not(prime(N)).


% 1.3 Formulating Abstractions with Higher-Order Procedures
% same as above
% cube(X) -> X * X * X.


% 1.3.1 Formulating Abstractions with Higher-Order Procedures - Procedures as Arguments
section_1_3_1() ->
   print (sum_cubes(1, 10)),
   print (sum_cubes_1(1, 10)),
   print (sum_integers(1, 10)),
   print (sum_integers_1(1, 10)),
   print (8.0 * pi_sum(1.0, 1000.0)),
   print (8.0 * pi_sum_1(1.0, 1000.0)),
   print (integral(fun cube/1, 0.0, 1.0, 0.01)),
   print (integral(fun cube/1, 0.0, 1.0, 0.001)),
   print(simpson(fun cube/1, 0.0, 1.0, 100)),
   print(sum_cubes_2(1, 10)),
   print(factorial_3(5)),
   print(factorial_4(5)),
   print(accumulate(fun plus/2, 0, fun identity/1, 1, fun inc/1, 5)),
   print(accumulate(fun multiply/2, 1, fun identity/1, 1, fun inc/1, 5)),
   print(accumulate_iter(fun plus/2, fun identity/1, 1, fun inc/1, 5, 0)),
   print(accumulate_iter(fun multiply/2, fun identity/1, 1, fun inc/1, 5, 1)),
   print(filtered_accumulate(fun plus/2, 0, fun square/1, 1, fun inc/1, 5, fun prime/1)).

sum_integers(A, B) ->
   case (A > B) of
      true  -> 0;
      false -> A + sum_integers(A + 1, B)
   end.

sum_cubes (A, B) ->
   case (A > B) of
      true  -> 0;
      false -> cube(A) + sum_cubes(A + 1, B)
   end.

pi_sum(A, B) ->
   case (A > B) of
      true  -> 0.0;
      false -> (1.0 / (A * (A + 2.0))) + pi_sum(A + 4.0, B)
   end.

sum(Term, A, Next, B) ->
   case (A > B) of
      true  -> 0;
      false -> Term(A) + sum(Term, Next(A), Next, B)
   end.

% Using sum
% same as above
% inc(N) -> N + 1.

sum_cubes_1(A, B) ->
   sum(fun (X) -> cube(X) end, A, fun inc/1, B).

identity(X) -> X.

sum_integers_1(A, B) ->
   sum(fun identity/1, A, fun inc/1, B).

sum_float(Term, A, Next, B) ->
   case (A > B) of
      true  -> 0.0;
      false -> Term(A) + sum_float(Term, Next(A), Next, B)
   end.

pi_sum_1(A, B) ->
   Pi_Term = fun (X) -> 1 / (X * (X + 2)) end,
   Pi_Next = fun (X) -> X + 4.0 end,
   sum_float(Pi_Term, A, Pi_Next, B).

integral(F, A, B, Dx) ->
   Add_Dx = fun (X) -> X + Dx end,
   sum_float(F, A + (Dx / 2), Add_Dx, B) * Dx.

% Exercise 1.29
simpson(F, A, B, N) ->
   H = abs(B - A) / N,
   Sum_Iter = fun(Self, Term, Start, Next, Stop, Acc) ->
      if
         (Start > Stop) -> Acc;
         true           -> Self(Self, Term, Next(Start), Next, Stop, Acc + Term(A + Start * H))
      end
   end,
   H * Sum_Iter(Sum_Iter, F, 1, fun inc/1, N, 0.0).

% Exercise 1.30
sum_iter(Term, A, Next, B, Acc) ->
   if
      (A > B) -> Acc;
      true    -> sum_iter(Term, Next(A), Next, B, Acc + Term(A))
   end.

% 'sum_cubes_2' reimplements 'sum_cubes_' but uses 'sum_iter' in place of 'sum'
sum_cubes_2(A, B) -> sum_iter(fun cube/1, A, fun inc/1, B, 0).

% Exercise 1.31
% a.
product(Term, A, Next, B) ->
   if
      (A > B) -> 1;
      true    -> Term(A) * product(Term, Next(A), Next, B)
   end.

factorial_3(N) ->
   product(fun identity/1, 1, fun inc/1, N).

% b.
product_iter(Term, A, Next, B, Acc) ->
   if
      (A > B) -> Acc;
      true    -> product_iter(Term, Next(A), Next, B, Acc * Term(A))
   end.

factorial_4(N) ->
   product_iter(fun identity/1, 1, fun inc/1, N, 1).

% Exercise 1.32
% a.
accumulate(Combiner, NullValue, Term, A, Next, B) ->
   if
      (A > B) -> NullValue;
      true    -> Combiner(Term(A), accumulate(Combiner, NullValue, Term, Next(A), Next, B))
   end.

% sum:     accumulate(fun plus/2, 0, fun identity/1, A, fun inc/1, B).
% product: accumulate(fun multiply/2, 1, fun identity/1, A, fun inc/1, B).

% b.
% NOTE: starting value of 'Acc' is 'NullValue'
accumulate_iter(Combiner, Term, A, Next, B, Acc) ->
   if
      (A > B) -> Acc;
      true    -> accumulate_iter(Combiner, Term, Next(A), Next, B, Combiner(Acc, Term(A)))
   end.

% sum:     accumulate_iter(fun plus/2, fun identity/1, A, fun inc/1, B, 0).
% product: accumulate_iter(fun multiply/2, fun identity/1, A, fun inc/1, B, 1).

% Exercise 1.33
filtered_accumulate(Combiner, NullValue, Term, A, Next, B, Pred) ->
   if
      (A > B) -> NullValue;
      true    ->
         case Pred(A) of
            true  -> Combiner(Term(A), filtered_accumulate(Combiner, NullValue, Term, Next(A), Next, B, Pred));
            false -> filtered_accumulate(Combiner, NullValue, Term, Next(A), Next, B, Pred)
         end
   end.

% a.
% filtered_accumulate(fun plus/2, 0, fun square/1, 1, fun inc/1, 5, fun prime/1);  % 39

% b. Not sure how to implement this without modifying 'filtered_accumulate' to have 'Pred'
%    accept two arguments


% 1.3.2 Formulating Abstractions with Higher-Order Procedures - Constructing Procedures Using Lambda
section_1_3_2() ->
   print (8.0 * pi_sum_2(1.0, 1000.0)),
   print (integral_1(fun (X) -> cube(X) end, 0.0, 1.0, 0.01)),
   Plus4 = fun (X) -> X + 4 end,
   print ((fun (X, Y, Z) -> X + Y + square(Z) end) (1, 2, 3)),

   % I need to figure out erlang's equivalent of let
   %X = 5,
   %let
   %   val X = 3
   %in
   %   X + (X * 10)
   %end + X;

   %val X = 2;
   %let
   %   val X = 3
   %   and Y = X + 2
   %in
   %   X * Y
   %end;

   % Exercise 1.34
   print (f_5(fun (X) -> square(X) end)),
   print (f_5(fun (Z) -> Z * (Z + 1) end)).

pi_sum_2(A, B) ->
   sum_float(fun (X) -> 1.0 / (X * (X + 2.0)) end, A, fun (X) -> X + 4.0 end, B).

integral_1(F, A, B, Dx) ->
   sum_float(F, A + (Dx / 2), fun (X) -> X + Dx end, B) * Dx.

plus4(X) -> X + 4.

% Using let
f_1(X, Y) ->
   F_Helper =
      fun (A, B) ->
         (X * square(A)) + (Y * B) + (A * B)
      end,
   F_Helper(1 + (X * Y), 1 - Y).

f_2(X, Y) ->
   (fun (A, B) -> (X * square(A)) + (Y * B) + (A * B) end)
      (1 + (X * Y), 1 - Y).

f_3(X, Y) ->
   A = 1 + (X * Y),
   B = 1 - Y,
   (X * square(A)) + (Y * B) + (A * B).

f_4(X, Y) ->
   A = 1 + (X * Y),
   B = 1 - Y,
   (X * square(A)) + (Y * B) + (A * B).

%% Exercise 1.34
f_5(G) -> G(2).


% 1.3.3 Formulating Abstractions with Higher-Order Procedures - Procedures as General Methods
section_1_3_3() ->
   print (half_interval_method(fun (X) -> math:sin(X) end, 2.0, 4.0)),
   print (half_interval_method(fun (X) -> (X * X * X) - (2.0 * X) - 3.0 end, 1.0, 2.0)),

   print (fixed_point(fun (X) -> math:cos(X) end, 1.0)),
   print (fixed_point(fun (Y) -> math:sin(Y) + math:cos(Y) end, 1.0)),

   print (sqrt_5(25)),

   print(golden_ratio()),
   print(fixed_point(fun(X) -> math:log(1000.0) / math:log(X) end, 1.5)),

   % Needs 'average_damp' from next section:
   %
   %    average_damp(F) -> fun (X) -> average(X, F(X)) end.
   %
   print(fixed_point(average_damp(fun(X) -> math:log(1000.0) / math:log(X) end), 1.5)).

% Half-interval method
close_enough(X, Y) ->
   (abs(X - Y) < 0.001).

positive(X) -> (X >= 0.0).
negative(X) -> not(positive(X)).

search(F, Neg_Point, Pos_Point) ->
   MidPoint = average(Neg_Point, Pos_Point),
   case (close_enough(Neg_Point, Pos_Point)) of
      true -> MidPoint;
      false ->
         Test_Value = F(MidPoint),
         case (positive(Test_Value)) of
            true -> search(F, Neg_Point, MidPoint);
            false ->
               case (negative(Test_Value)) of
                  true -> search(F, MidPoint, Pos_Point);
                  false -> MidPoint
               end
         end
   end.

half_interval_method(F, A, B) ->
   A_Value = F(A),
   B_Value = F(B),
   case ((negative(A_Value)) andalso (positive(B_Value))) of
      true -> search(F, A, B);
      false ->
         case ((negative(B_Value)) andalso (positive(A_Value))) of
            true -> search(F, B, A);
            false -> {} %% raise Invalid("Values are not of opposite sign" ^ Real.toString(a) ^ " " ^ Real.toString(b))
         end
   end.

% Fixed points
fixed_point(F, First_Guess) ->
   Tolerance = 0.00001,
   Close_Enough =
      fun (V1, V2) ->
         abs(V1 - V2) < Tolerance
      end,
   Try =
      fun (Self, Guess) ->
         Next = F(Guess),
         case (Close_Enough(Guess, Next)) of
            true -> Next;
            false -> Self(Self, Next)
         end
      end,
   Try(Try, First_Guess).

% note: this function does not converge
sqrt_4(X) ->
   fixed_point(fun (Y) -> X / Y end, 1.0).

sqrt_5(X) ->
   fixed_point(fun (Y) -> average(Y, X / Y) end, 1.0).

% Exercise 1.35
golden_ratio() -> fixed_point(fun (X) -> 1.0 + 1.0 / X end, 1.0).

% Exercise 1.36
% Add the following line to function, 'fixed_point':
%  ... Next = F(Guess),
%  print(Next),
%  ... case ...

% Exercise 1.37
% exercise left to reader to define cont_frac
% cont_frac(fun (I) -> 1.0 end, fun (I) -> 1.0 end, K).

% Exercise 1.38 - unfinished

% Exercise 1.39 - unfinished


% 1.3.4 Formulating Abstractions with Higher-Order Procedures - Procedures as Returned Values
section_1_3_4() ->
   print ((average_damp(fun square/1)) (10.0)),
   print (sqrt_6(25)),
   print (cube_root(27)),
   print ((deriv(fun cube/1)) (5.0)),
   print (sqrt_7(25)),
   print (sqrt_8(25)),
   print (sqrt_9(25)),
   print(newtons_method(cubic(5.0, 3.0, 2.5), 1.0)),
   print((double_(fun inc/1))(5)),
   print((double_(double_(fun inc/1)))(5)),
   print((double_(double_(double_(fun inc/1))))(5)),
   print((compose(fun square/1, fun inc/1))(6)),
   print((repeated(fun square/1, 2))(5)),
   print(fixed_point(smooth(fun (X) -> math:log(1000.0) / math:log(X) end, 0.05), 1.5)),
   print(fixed_point_(average_damp(fun (X) -> math:log(1000.0) / math:log(X) end), 1.5)).

average_damp(F) ->
   fun (X) -> average(X, F(X)) end.

sqrt_6(X) ->
   fixed_point(average_damp(fun (Y) -> X / Y end), 1.0).

cube_root(X) ->
   fixed_point(average_damp(fun (Y) -> X / square(Y) end), 1.0).

% Newton's method
deriv(G) ->
   Dx = 0.00001,
   fun (X) -> (G(X + Dx) - G(X)) / Dx end.

% same as before
% cube(X) -> X * X * X.

newton_transform(G) ->
   fun (X) -> X - (G(X) / ((deriv(G))(X))) end.

newtons_method(G, Guess) ->
   fixed_point(newton_transform(G), Guess).

sqrt_7(X) ->
   newtons_method(fun (Y) -> square(Y) - X end, 1.0).

% Fixed point of transformed function
fixed_point_of_transform (G, Transform, Guess) ->
   fixed_point(Transform(G), Guess).

sqrt_8(X) ->
   fixed_point_of_transform(fun (Y) -> X / Y end, fun average_damp/1, 1.0).

sqrt_9(X) ->
   fixed_point_of_transform(fun (Y) -> square(Y) - X end, fun newton_transform/1, 1.0).

% Exercise 1.40
cubic(A, B, C) ->
   fun (X) -> X*X*X + A*X*X + B*X + C end.

% Exercise 1.41
double_(F) -> fun (X) -> F(F(X)) end.

% Exercise 1.42
compose(F, G) -> fun (X) -> F(G(X)) end.

% Exercise 1.43
repeated(F, N) ->
   Iterate = fun (Self, Arg, I) ->
      if
         (I > N) -> Arg;
         true    -> Self(Self, F(Arg), I + 1)
      end
   end,
   fun (X) -> Iterate(Iterate, X, 1) end.

% Exercise 1.44 ('n-fold-smooth' not implemented)
smooth(F, Dx) ->
   fun (X) -> average(X, (F(X - Dx) + F(X) + F(X + Dx)) / 3.0) end.

% Exercise 1.45 - unfinished

% Exercise 1.46 ('sqrt' not implemented)
iterative_improve(Good_enough, Improve) ->
   Iterate = fun (Self, Guess) ->
      Next = Improve(Guess),
      case Good_enough(Guess, Next) of
         true  -> Next;
         false -> Self(Self, Next)
      end
   end,
   fun (X) -> Iterate(Iterate, X) end.

fixed_point_(F, First_guess) ->
   Tolerance = 0.00001,
   Good_enough = fun (V1, V2) -> abs(V1 - V2) < Tolerance end,
   (iterative_improve(Good_enough, F))(First_guess).

Chris Rathman / Chris.Rathman@tx.rr.com