About SICP The following Ruby 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 #01 Examples in Ruby
# 1.1.1 The Elements of Programming - Expressions
486
137 + 349
1000 - 334
5 * 99
10 / 5
2.7 + 10.0
21 + 35 + 12 + 7
25 * 4 * 12
3 * 5 + 10 - 6
3 * (2 * 4 + 3 + 5) + 10 - 7 + 6

# 1.1.2 The Elements of Programming - Naming and the Environment
size = 2
puts size
puts 5 * size
pi = 3.14159
radius = 10.0
puts pi * radius * radius
circumference = 2.0 * pi * radius
puts circumference

# 1.1.3 The Elements of Programming - Evaluating Combinations
puts (2 + (4 * 6)) * (3 + 5 + 7)

# 1.1.4 The Elements of Programming - Compound Procedures
def square(x) x * x end
puts square(21)
puts square(2 + 5)
puts square(square(3))
def sum_of_squares(x, y) square(x) + square(y) end
puts sum_of_squares(3, 4)
def f(a) sum_of_squares(a + 1, a * 2) end
puts f(5)

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

puts ((5 + 1) * (5 + 1)) + ((5 * 2) * (5 * 2))
puts (6 * 6) + (10 * 10)
puts 36 + 100
puts 136

# 1.1.6 The Elements of Programming - Conditional Expressions and Predicates
def abs(x)
   if x > 0 then
      x
   elsif x == 0 then
      0
   else
      -x
   end
end
def abs(x)
   if x < 0 then
      -x
   else
      x
   end
end
x = 6
puts (x > 5) && (x < 10)
def ge(x, y)
   (x > y) || (x = y)
end
def ge(x, y)
   !(x < y)
end

# Exercise 1.1
puts 10
puts 5 + 3 + 4
puts 9 - 1
puts 6 / 2
puts (2 * 4) + (4 - 6)
a = 3
b = a + 1
puts a + b + a * b
puts a == b
puts (if b > a && b < a * b then b else a end)
puts (if a == 4 then 6 elsif b == 4 then 6 + 7 + a else 25 end)
puts 2 + (if b > a then b else a end)
puts ((a > b && a) || (a < b && b) || -1) * (a + 1)

# Exercise 1.2
puts ((5.0 + 4.0 + (2.0 - (3.0 - (6.0 + (4.0 / 5.0))))) /
         (3.0 * (6.0 - 2.0) * (2.0 - 7.0)))

# Exercise 1.3
def three_n(n1, n2, n3)
   if n1 > n2 then
      if n1 > n3 then
         if n2 > n3 then
            n1*n1 + n2*n2
         else
            n1*n1 + n3*n3
         end
      else
         n1*n1 + n3*n3
      end
   else
      if n2 > n3 then
         if n1 > n3 then
            n2*n2 + n1*n1
         else
            n2*n2 + n3*n3
         end
      else
         n2*n2 + n3*n3
      end
   end
end

# Exercise 1.4
def a_plus_abs_b(a, b)
   if b > 0 then
      a + b
   else
      a - b
   end
end

# Exercise 1.5
def p() p() end
def test(x, y)
   if x == 0 then
      0
   else
      y
   end
end
# commented out as this is in infinite loop
# test(0, p())

# 1.1.7 The Elements of Programming - Example: Square Roots by Newton's Method
def square(x) x * x end

def good_enough(guess, x)
   abs(square(guess) - x) < 0.001
end

def average(x, y)
   Float(x + y) / 2.0
end

def improve(guess, x)
   average(guess, Float(x) / guess)
end

def sqrt_iter(guess, x)
   if good_enough(guess, x) then
      guess
   else
      sqrt_iter(improve(guess, x), x)
   end
end

def sqrt(x)
   sqrt_iter(1.0, Float(x))
end

puts sqrt(9.0)
puts sqrt(100.0 + 37.0)
puts sqrt(sqrt(2.0)+sqrt(3.0))
puts square(sqrt(1000.0))

# Exercise 1.6
def new_if(predicate, then_clause, else_clause)
   if predicate then
      then_clause
   else
      else_clause
   end
end
puts new_if((2==3), 0, 5)
puts new_if((1==1), 0, 5)
def sqrt_iter(guess, x)
   new_if(
      good_enough(guess, x),
      guess,
      sqrt_iter(improve(guess, x), x))
end

# Exercse 1.7
def good_enough_gp(guess, prev)
   abs(guess - prev) / guess < 0.001
end
def sqrt_iter_gp(guess, prev, x)
   if good_enough_gp(guess, prev) then
      guess
   else
      sqrt_iter_gp(improve(guess, x), guess, x)
   end
end
def sqrt_gp(x)
   sqrt_iter_gp(4.0, 1.0, x)
end

# Exercise 1.8
def improve_cube(guess, x)
   (2.0*guess + Float(x)/(guess * guess)) / 3.0
end
def cube_iter(guess, prev, x)
   if good_enough_gp(guess, prev) then
      guess
   else
      cube_iter(improve_cube(guess, x), guess, x)
   end
end
def cube_root_0(x)
   cube_iter(27.0, 1.0, x)
end

# 1.1.8 The Elements of Programming - Procedures as Black-Box Abstractions
def square(x) x * x end

def double(x) x + x end

def square_real(x) Math.exp(double(Math.log(x))) end

def good_enough(guess, x)
   abs(square(guess) - x) < 0.001
end

def improve(guess, x)
   average(guess, Float(x) / guess)
end

def sqrt_iter(guess, x)
   if good_enough(guess, x) then
      guess
   else
      sqrt_iter(improve(guess, x), x)
   end
end

def sqrt(x)
   sqrt_iter(1.0, x)
end

puts square(5.0)

# Block-structured
def sqrt(x)
   def good_enough(guess, x)
      abs(square(guess) - x) < 0.001
   end

   def improve(guess, x)
      average(guess, Float(x) / guess)
   end

   def sqrt_iter(guess, x)
      if (good_enough(guess, x)) then
         guess
      else
         sqrt_iter(improve(guess, x), x)
      end
   end

   sqrt_iter(1.0, x)
end

# Taking advantage of lexical scoping
# Not sure how to get lexical scoping for nested functions in Ruby.
# Will use lambdas to get around the problem
def sqrt(x)
   good_enough = lambda{|guess|
      abs(square(guess) - x) < 0.001
   }

   improve = lambda{|guess|
      average(guess, Float(x) / guess)
   }

   sqrt_iter = lambda{|guess|
      if good_enough.call(guess) then
         guess
      else
         sqrt_iter.call(improve.call(guess))
      end
   }

   sqrt_iter.call(1.0)
end

# 1.2.1 Procedures and the Processes They Generate - Linear Recursion and Iteration

# Recursive
def factorial(n)
   if n == 1 then
      1
   else
      n * factorial(n - 1)
   end
end

puts factorial(6)

# Iterative
def fact_iter(product, counter, max_count)
   if counter > max_count then
      product
   else
      fact_iter(counter * product, counter + 1, max_count)
   end
end

def factorial(n)
   fact_iter(1, 1, n)
end

# Iterative, block-structured (from footnote)
def factorial(n)
   # Note: sending n as parameter so I don't have to use lambda
   def iter(product, counter, n)
      if counter > n then
         product
      else
         iter(counter * product, counter + 1, n)
      end
   end
   iter(1, 1, n)
end

# Exercise 1.9
def inc(a) a + 1 end
def dec(a) a - 1 end
def plus(a, b)
   if a == 0 then
      b
   else
      inc(plus(dec(a), b))
   end
end
def plus(a, b)
   if a == 0 then
      b
   else
      plus(dec(a), inc(b))
   end
end

# Exercise 1.10
def a(x, y)
   if y == 0 then
      0
   elsif x == 0 then
      2 * y
   elsif y == 1 then
      2
   else
      a(x - 1, a(x, y - 1))
   end
end
puts a(1, 10)
puts a(2, 4)
puts a(3, 3)
def f(n) a(0, n) end
def g(n) a(1, n) end
def h(n) a(2, n) end
def k(n) 5 * n * n end

# 1.2.2 Procedures and the Processes They Generate - Tree Recursion

# Recursive
def fib(n)
   if n == 0 then
      0
   elsif n == 1 then
      1
   else
      fib(n - 1) + fib(n - 2)
   end
end

# Iterative
def fib_iter(a, b, count)
   if count == 0 then
      b
   else
      fib_iter(a + b, a, count - 1)
   end
end
def fib(n)
   fib_iter(1, 0, n)
end

# Counting change
def first_denomination(x)
   if x == 1 then
      1
   elsif x == 2 then
      5
   elsif x == 3 then
      10
   elsif x == 4 then
      25
   elsif x == 5 then
      50
   end
end

def cc(amount, kinds_of_coins)
   if amount == 0 then
      1
   elsif amount < 0 then
      0
   elsif kinds_of_coins == 0 then
      0
   else
      cc(amount, kinds_of_coins - 1) +
        cc(amount - first_denomination(kinds_of_coins), kinds_of_coins)
   end
end

def count_change(amount)
   cc(amount, 5)
end

puts count_change(100)

# Exercise 1.11
def f(n)
   if n < 3 then
      n
   else
      f(n-1) + 2*f(n-2) + 3*f(n-3)
   end
end
def f_iter(a, b, c, count)
   if count == 0 then
      c
   else
      f_iter(a + 2*b + 3*c, a, b, count-1)
   end
end
def f(n)
   f_iter(2, 1, 0, n)
end

# Exercise 1.12
def pascals_triangle(n, k)
   if n == 0 then
      1
   elsif n == k then
      1
   else
      pascals_triangle(n-1, k-1) + pascals_triangle(n-1, k)
   end
end

# 1.2.3 Procedures and the Processes They Generate - Orders of Growth

# Exercise 1.15
def cube(x) x * x * x end
def p(x) (3.0 * x) - (4.0 * cube(x)) end
def sine(angle)
   if !(abs(angle) > 0.1) then
      angle
   else
      p(sine(angle / 3.0))
   end
end

# 1.2.4 Procedures and the Processes They Generate - Exponentiation

# Linear recursion
def expt(b, n)
   if n == 0 then
      1
   else
      b * expt(b, n - 1)
   end
end

# Linear iteration
def expt_iter(b, counter, product)
   if counter == 0 then
      product
   else
      expt_iter(b, counter - 1, b * product)
   end
end
def expt(b, n)
   expt_iter(b, n, 1)
end

## Logarithmic iteration
def even(n) (n % 2) == 0 end

def fast_expt(b, n)
   if n == 0 then
      1
   else
      if even(n) then
         square(fast_expt(b, n / 2))
      else
         b * fast_expt(b, n - 1)
      end
   end
end

# Exercise 1.17
def multiply(a, b)
   if b == 0 then
      0
   else
      plus(a, multiply(a, dec(b)))
   end
end

# Exercise 1.19
# exercise left to reader to solve for p' and q'
# def fib_iter(a, b, p, q, count)
#    if count == 0 then
#       b
#    else
#       if even(count) then
#          fib_iter(a, b, p', q', count / 2)
#       else
#          fib_iter((b * q) + (a * q) + (a * p), (b * p) + (a * q), p, q, count - 1)
#       end
#    end
# end
# def fib(n)
#    fib_iter(1, 0, 0, 1, n)
# end

# 1.2.5 Procedures and the Processes They Generate - Greatest Common Divisors
def gcd(a, b)
   if b == 0 then
      a
   else
      gcd(b, a % b)
   end
end

puts gcd(40, 6)

# Exercise 1.20
puts gcd(206, 40)

# 1.2.6 Procedures and the Processes They Generate - Example: Testing for Primality

# prime
def divides(a, b) (b % a) == 0 end

def find_divisor(n, test_divisor)
   if square(test_divisor) > n then
      n
   elsif divides(test_divisor, n) then
      test_divisor
   else
      find_divisor(n, test_divisor + 1)
   end
end

def smallest_divisor(n) find_divisor(n, 2) end

def prime(n) n == smallest_divisor(n) end

# fast_prime
def expmod(nbase, nexp, m)
   if nexp == 0 then
      1
   else
      if even(nexp) then
         square(expmod(nbase, nexp / 2, m)) % m
      else
         (nbase * (expmod(nbase, (nexp - 1), m))) % m
      end
   end
end

def fermat_test(n)
   # Note: sending n as parameter so I don't have to use lambda
   def try_it(a, n) (expmod(a, n, n) == a) end
   try_it(rand(n), n)
end

def fast_prime(n, ntimes)
   if ntimes == 0 then
      true
   else
      if fermat_test(n) then
         fast_prime(n, ntimes - 1)
      else
         false
      end
   end
end

# Exercise 1.21
puts smallest_divisor(199)
puts smallest_divisor(1999)
puts smallest_divisor(19999)

# Exercise 1.22
def report_prime(elapsed_time)
   puts " *** " + String(elapsed_time)
end
def start_prime_test(n, start_time)
   if prime(n) then
      report_prime(Time.now - start_time)
   end
end
def timed_prime_test(n)
   puts "\n" + String(n)
   start_prime_test(n, Time.now)
end

# Exercise 1.25
def expmod(nbase, nexp, m)
   fast_expt(nbase, nexp) % m
end

# Exercise 1.26
def expmod(nbase, nexp, m)
   if nexp == 0 then
      1
   else
      if even(nexp) then
         (expmod(nbase, (nexp / 2), m) * expmod(nbase, (nexp / 2), m)) % m
      else
         (nbase * expmod(nbase, nexp - 1, m)) % m
      end
   end
end

# Exercise 1.27
def carmichael(n)
   fast_prime(n, 100) && !prime(n)
end

puts carmichael(561)
puts carmichael(1105)
puts carmichael(1729)
puts carmichael(2465)
puts carmichael(2821)
puts carmichael(6601)

# 1.3 Formulating Abstractions with Higher-Order Procedures
def cube(x) x * x * x end

# 1.3.1 Formulating Abstractions with Higher-Order Procedures - Procedures as Arguments
def sum_integers(a, b)
   if a > b then
      0
   else
      a + sum_integers(a + 1, b)
   end
end

def sum_cubes(a, b)
   if a > b then
      0
   else
      cube(a) + sum_cubes(a + 1, b)
   end
end

def pi_sum(a, b)
   if a > b then
      0.0
   else
      (1.0 / (a * (a + 2.0))) + pi_sum(a + 4.0, b)
   end
end

def sum(term, a, nxt, b)
   if a > b then
      0
   else
      term.call(a) + sum(term, nxt.call(a), nxt, b)
   end
end

# Using sum
def inc(n) n + 1 end

def sum_cubes(a, b)
   sum(lambda{|x| cube(x)}, a, lambda{|y| inc(y)}, b)
end

puts sum_cubes(1, 10)

def identity(x) x end

def sum_integers(a, b)
   sum(lambda{|x| identity(x)}, a, lambda{|y| inc(y)}, b)
end

puts sum_integers(1, 10)

def pi_sum(a, b)
   def pi_term(x) 1.0 / (x * (x + 2.0)) end
   def pi_next(x) x + 4.0 end
   sum(lambda{|x| pi_term(x)}, a, lambda{|y| pi_next(y)}, b)
end

puts 8.0 * pi_sum(1, 1000)

def integral(f, a, b, dx)
   add_dx = lambda{|x| x + dx}
   sum(f, a + (dx / 2.0), lambda{|y| add_dx.call(y)}, b) * dx
end

def cube(x) x * x * x end

puts integral(lambda{|x| cube(x)}, 0.0, 1.0, 0.01)
# exceeds maximum recursion depth
# puts integral(lambda{|x| cube(x)}, 0.0, 1.0, 0.001)

# Exercise 1.29
def simpson(f, a, b, n)
   h = Float(abs(b-a)) / n
   sum_iter = lambda{|term, start, nxt, stop, acc|
      if start > stop then
         acc
      else
         sum_iter.call(term, nxt.call(start), nxt, stop, acc + term.call(a + start*h))
      end
   }
   h * sum_iter.call(f, 1, lambda{|x| inc(x)}, n, 0)
end
puts simpson(lambda{|x| cube(x)}, 0, 1, 100)

# Exercise 1.30
def sum_iter(term, a, nxt, b, acc)
   if a > b then
      acc
   else
      sum_iter(term, nxt.call(a), nxt, b, acc + term.call(a))
   end
end
def sum_cubes(a, b)
   sum_iter(lambda{|x| cube(x)}, a, lambda{|x| inc(x)}, b, 0)
end
puts sum_cubes(1, 10)

# Exercise 1.31
def product(term, a, nxt, b)
   if a > b then
      1
   else
      term.call(a) * product(term, nxt.call(a), nxt, b)
   end
end
def factorial(n)
   product(lambda{|x| identity(x)}, 1, lambda{|x| inc(x)}, n)
end
def product_iter(term, a, nxt, b, acc)
   if a > b then
      acc
   else
      product_iter(term, nxt.call(a), nxt, b, acc * term.call(a))
   end
end

# Exercise 1.32
def accumulate(combiner, null_value, term, a, nxt, b)
   if a > b then
      null_value
   else
      combiner.call(term.call(a), accumulate(combiner, null_value, term, nxt.call(a), nxt, b))
   end
end
def sum(a, b)
   accumulate(lambda{|x,y| x+y}, 0, lambda{|x| identity(x)}, a, lambda{|x| inc(x)}, b)
end
def product(a, b)
   accumulate(lambda{|x,y| x*y}, 1, lambda{|x| identity(x)}, a, lambda{|x| inc(x)}, b)
end
def accumulate_iter(combiner, term, a, nxt, b, acc)
   if a > b then
      acc
   else
      accumulate_iter(combiner, term, nxt.call(a), nxt, b, combiner.call(acc, term.call(a)))
   end
end
def sum(a, b)
   accumulate_iter(lambda{|x,y| x+y}, lambda{|x| identity(x)}, a, lambda{|x| inc(x)}, b, 0)
end
def product(a, b)
   accumulate_iter(lambda{|x,y| return x*y}, lambda{|x| identity(x)}, a, lambda{|x| inc(x)}, b, 1)
end

# Exercise 1.33
def filtered_accumulate(combiner, null_value, term, a, nxt, b, pred)
   if a > b then
      null_value
   else
      if pred.call(a) then
         combiner.call(term.call(a), filtered_accumulate(combiner, null_value, term, nxt.call(a), nxt, b, pred))
      else
         filtered_accumulate(combiner, null_value, term, nxt.call(a), nxt, b, pred)
      end
   end
end
puts (filtered_accumulate(lambda{|x,y| x+y}, 0, lambda{|x| square(x)}, 1, lambda{|x| inc(x)}, 5, lambda{|x| prime(x)}))

# 1.3.2 Formulating Abstractions with Higher-Order Procedures - Constructing Procedures Using Lambda
def pi_sum(a, b)
  sum(lambda{|x| 1.0 / (x * (x + 2.0))}, a, lambda{|y| y + 4.0}, b)
end

def integral(f, a, b, dx)
   sum(f, a + (dx / 2.0), lambda{|x| x + dx}, b) * dx
end

def plus4(x) x + 4 end

plus4 = lambda{|x| x + 4}

puts lambda{|x,y,z| x + y + square(z)}.call(1, 2, 3)

# Using let
def f(x, y)
   f_helper = lambda{|a, b|
      x*square(a) + y*b + a*b
   }
   f_helper.call(1 + x*y, 1 - y)
end

def f(x, y)
   lambda{|a,b| x*square(a) + y*b + a*b}.call(1 + x*y, 1 - y)
end

def f(x, y)
   a = 1 + x*y
   b = 1 - y
   x*square(a) + y*b + a*b
end

# Ruby does not have let binding and lambdas can't seem to be scoped
# There is a proposal in 1.9 to have local scoping:
#     http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l7
# Note: Ruby gives incorrect result here of 36 (different than Scheme)
x = 5
puts lambda{x = 3; x + x*10}.call() + x

# Note: have to declare y first here
x = 2
puts lambda{y=x+2; x=3; x*y}.call()

def f(x, y)
   a = 1 + x*y
   b = 1 - y
   x*square(a) + y*b + a*b
end

# Exercise 1.34
def f(g) g.call(2) end
puts f(lambda{|x| square(x)})
puts f(lambda{|z| z * (z + 1)})

# 1.3.3 Formulating Abstractions with Higher-Order Procedures - Procedures as General Methods

# Half-interval method
def close_enough(x, y)
   abs(x - y) < 0.001
end

def positive(x) x >= 0.0 end
def negative(x) !positive(x) end

def search(f, neg_point, pos_point)
   midpoint = average(neg_point, pos_point)
   if close_enough(neg_point, pos_point) then
      midpoint
   else
      begin
         test_value = f.call(midpoint)
         if positive(test_value) then
            search(f, neg_point, midpoint)
         elsif negative(test_value) then
            search(f, midpoint, pos_point)
         else
            midpoint
         end
      end
   end
end

def half_interval_method(f, a, b)
   a_value = f.call(a)
   b_value = f.call(b)
   if negative(a_value) && positive(b_value) then
      search(f, a, b)
   elsif negative(b_value) && positive(a_value) then
      search(f, b, a)
   else
      raise Exception.new("Values are not of opposite sign " + String(a) + " " + String(b))
   end
end

puts half_interval_method(lambda{|x| Math.sin(x)}, 2.0, 4.0)

puts half_interval_method(lambda{|x| (x * x * x) - (2.0 * x) - 3.0}, 1.0, 2.0)

## Fixed points
Tolerance = 0.00001

def fixed_point(f, first_guess)
   def close_enough(v1, v2)
      abs(v1 - v2) < Tolerance
   end
   # Note: sending f as parameter so I don't have to use lambda
   def tryit(guess, f)
      nxt = f.call(guess)
      if close_enough(guess, nxt) then
         nxt
      else
         tryit(nxt, f)
      end
   end
   tryit(first_guess, f)
end

puts fixed_point(lambda{|x| Math.cos(x)}, 1.0)

puts fixed_point(lambda{|y| Math.sin(y) + Math.cos(y)}, 1.0)

# note: this function does not converge
def sqrt(x)
   fixed_point(lambda{|y| Float(x) / y}, 1.0)
end

def sqrt(x)
   fixed_point(lambda{|y| average(y, Float(x) / y)}, 1.0)
end

# Exercise 1.35
def golden_ratio()
   fixed_point(lambda{|x| 1.0 + 1.0/x}, 1.0)
end

# Exercise 1.36
# 35 guesses before convergence
puts fixed_point(lambda{|x| Math.log(1000.0) / Math.log(x)}, 1.5)
# 11 guesses before convergence (average_damp defined below)
# puts fixed_point(average_damp(lambda{|x| Math.log(1000.0) / Math.log(x)}), 1.5)

# Exercise 1.37
# exercise left to reader to define cont_frac
# cont_frac(lambda{|i| 1.0}, lambda{|j| 1.0}, k)

# 1.3.4 Formulating Abstractions with Higher-Order Procedures - Procedures as Returned Values
def average_damp(f)
   lambda{|x| average(Float(x), f.call(x)) }
end

puts average_damp(lambda{|x| square(x)}).call(10.0)

def sqrt(x)
   fixed_point(average_damp(lambda{|y| Float(x) / y}), 1.0)
end

def cube_root(x)
   fixed_point(average_damp(lambda{|y| Float(x) / square(y)}), 1.0)
end

puts cube_root(8)

# Newton's method
def deriv(g)
   dx = 0.00001
   lambda{|x| Float(g.call(x + dx) - g.call(x)) / dx}
end

def cube(x) x * x * x end

puts deriv(lambda{|x| cube(x)}).call(5.0)

def newton_transform(g)
   lambda{|x| x - (Float(g.call(x)) / (deriv(g).call(x)))}
end

def newtons_method(g, guess)
   fixed_point(newton_transform(g), guess)
end

def sqrt(x)
   newtons_method(lambda{|y| square(y) - x}, 1.0)
end

# Fixed point of transformed function
def fixed_point_of_transform(g, transform, guess)
   fixed_point(transform.call(g), guess)
end

def sqrt(x)
   fixed_point_of_transform(lambda{|y| puts x; puts y; (x / y)}, lambda{|z| average_damp(z)}, 1.0)
end

def sqrt(x)
   fixed_point_of_transform(lambda{|y| square(y) - x}, lambda{|z| newton_transform(z)}, 1.0)
end

# Exercise 1.40
def cubic(a, b, c)
   lambda{|x| cube(x) + a*x*x + b*x + c}
end
puts newtons_method(cubic(5.0, 3.0, 2.5), 1.0)

# Exercise 1.41
double = lambda{|f|
   lambda{|x| f.call(f.call(x))}
}
puts (double.call(lambda{|x| inc(x)}).call(5))
puts (double.call(double).call(lambda{|x| inc(x)}).call(5))
puts (double.call(double).call(double).call(lambda{|x| inc(x)}).call(5))

# Exercise 1.42
def compose(f, g)
   lambda{|x| f.call(g.call(x))}
end
puts compose(lambda{|x| square(x)}, lambda{|x| inc(x)}).call(6)

# Exercise 1.43
def repeated(f, n)
   iterate = lambda{|arg, i|
      if i > n then
         arg
      else
         iterate.call(f.call(arg), i+1)
      end
   }
   lambda{|x| iterate.call(x, 1)}
end
puts repeated(lambda{|x| square(x)}, 2).call(5)

# Exercise 1.44
def smooth(f, dx)
   lambda{|x| average(x, (f.call(x-dx) + f.call(x) + f.call(x+dx)) / 3.0)}
end
puts fixed_point(smooth(lambda{|x| Math.log(1000.0) / Math.log(x)}, 0.05), 1.5)

# Exercise 1.46
def iterative_improve(good_enough, improve)
   iterate = lambda{|guess|
      nxt = improve.call(guess)
      if good_enough(guess, nxt) then
         nxt
      else
         iterate.call(nxt)
      end
   }
   return lambda{|x| iterate.call(x)}
end
def fixed_point(f, first_guess)
   def good_enough(v1, v2)
      tolerance = 0.00001
      abs(v1-v2) < tolerance
   end
   iterative_improve(lambda{|x,y| good_enough(x,y)}, f).call(first_guess)
end
puts fixed_point(average_damp(lambda{|x| Math.log(1000.0) / Math.log(x)}), 1.5)

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