% CSci 555: Functional Programming \
  Recursion Styles, Correctness, and Efficiency \
  --- Scala Version ---
% **H. Conrad Cunningham**
% **7 February 2019**

---
lang: en
---

| Copyright (C) 2016, 2019,
  [H. Conrad Cunningham ](<http://www.cs.olemiss.edu/~hcc>)
| Professor of 
  [Computer and Information Science ](<https://www.cs.olemiss.edu>)
| [University of Mississippi ](<http://www.olemiss.edu>)
| 211 Weir Hall
| P.O. Box 1848
| University, MS 38677
| (662) 915-5358


**Browser Advisory:** The HTML version of this textbook requires a
browser that supports the display of MathML. A good choice as of
February 2019 is a recent version of Firefox from Mozilla.


\newpage

# Recursion Styles, Correctness, and Efficiency

## Introduction

This set of notes introduces basic recursive programming styles and
examines issues of termination, correctness, and efficiency.

The goals of the chapter are to:

-   explore several recursive programming styles---linear and
    nonlinear, backward and forward, tail, and logarithmic---and their
    implementation using Scala

-   examine how to analyze Scala functions to determine under what
    conditions they terminate with the correct result and how
    efficient they are
  
-   explore methods for developing recursive Scala programs that
    terminate with the correct result and are efficient in both time
    and space usage


Note: The source code for the functions in these notes are in the
Scala file [`RecursionStyles.scala`{.bash} ](<RecursionStyles.scala>).


## Linear and Nonlinear Recursion

In this section, we examine the concepts of linear and nonlinear 
recursion. The following two sections examine other styles. 


### Linear recursion

A function definition is *linear recursive* if at most one recursive
application of the function occurs in a leg of the definition (i.e.
along a path from an entry to a return). The various function clauses
and branches of conditional expressions (e.g. `if`{.scala} and
`match`{.scala}) introduce paths.

The definition of the function `factorial`{.scala} below is linear
recursive because the expression in the second leg of the definition
(i.e. `n * factorial(n-1)`{.scala}) involves a single recursive
application. The other leg is nonrecursive; it is the base case of the
recursive definition.

~~~{.scala}
    def factorial(n: Int): Int = n match {
        case 0          => 1
        case m if m > 0 => m * factorial(m-1)  // linear rec.
        case _          =>
            sys.error(s"Factorial undefined for $n")
    }
~~~

Scala checks for pattern matches for the clauses in the order given in
the function definition. It executes the leg corresponding to the
first successful match. If no pattern matches, then the function
aborts and displays an error message.


### Termination of recursion

How do we know that function `factorial`{.scala} terminates?

To show that evaluation of a recursive function terminates, we must
show that each recursive application *always* gets closer to a normal
termination condition represented by a base case.

For a call `factorial(n)`{.scala} with `n > 0`{.scala}, the argument
of the recursive application always decreases to
`n - 1`{.scala}. Because the argument always decreases in integer steps,
it must eventually reach 0 and, hence, terminate in the first leg of
the definition.


### Preconditions and postconditions

The *precondition* of a function is what the caller (i.e. the client
of the function) must ensure holds when calling the function. A
precondition may specify the valid combinations of values of the
arguments. It may also record any constraints on the values of
"global" data structures that the function accesses or modifies. (By
"global" we mean any entity that is not a parameter or local variable
of the function.)

If the precondition holds, the supplier (i.e. developer) of the
function must ensure that the function terminates with the
*postcondition* satisfied. That is, the function returns the
required values and/or alters the "global" data structures in the
required manner.

The precondition of the `factorial`{.scala} function requires that
argument `n`{.scala} be a nonnegative integer value. We could use
Scala's predefined `requires`{.scala} method to ensure this
precondition holds, but, in this version, if all pattern matches fail,
the function call aborts with a standard error message.

The postcondition of `factorial`{.scala} is that the result returned
is the correct mathematical value of `n`{.scala} factorial. The
function `factorial`{.scala} neither accesses nor modifies any global
data structures.


### Time and space complexity

How efficient is function `factorial`{.scala}?

Function `factorial`{.scala} recurses to a depth of `n`{.scala}. It
thus has *time complexity* O(`n`{.scala}), if we count either the
recursive calls or the multiplication at each level. 

The *space complexity* is also O(`n`{.scala}) because a new runtime
stack frame is needed for each recursive call.


### Nonlinear recursion

A *nonlinear recursion* is a recursive function in which the
evaluation of some leg requires more than one recursive
application. 

For example, the naive Fibonacci number function `fib`{.scala} shown
below has two recursive applications in its third leg. When we apply
this function to a nonnegative integer argument greater than 1, we
generate a pattern of recursive applications that has the "shape" of a
binary tree. Some call this a *tree recursion*.

~~~{.scala}
    def fib(n: Int): Int = n match {
        case 0           => 0
        case 1           => 1
        case m if m >= 2 => fib(m-1) + fib(m-2) // double rec.
        case _           =>
            sys.error(s"Fibonacci undefined for $n")
    }
~~~

What are the precondition and postcondition for `fib(n)`{.scala}?

For `fib(n)`{.scala}, the precondition `n >= 0`{.scala} ensures
that the function is defined. When called with the precondition
satisfied, the postcondition is:

>    `fib(n)`{.scala} *= Fibonacci*`(n)`
 
How do we know that `fib`{.scala} terminates?

For the recursive case n >= 2, the two recursive calls have arguments
that are 1 or 2 less than `n`{.scala}. Thus every call gets closer to
one of the two base cases.

What are the time and space complexities of function `fib`{.scala}?

Function `fib`{.scala} is combinatorially explosive, having a time
complexity O(`fib(n)`{.scala}).

The space complexity is O(`n`{.scala}) because a new runtime stack
frame is needed for each recursive call and the calls recurse to a
depth of `n`{.scala}.

An advantage of a linear recursion over a nonlinear one is that a
linear recursion can be compiled into a *loop* in a straightforward
manner.  Converting a nonlinear recursion to a loop is, in general,
difficult.


## Backward and Forward Recursion

In this section, we examine the concepts of backward and forward
recursion.

### Backward recursion

A function definition is *backward recursive* if the recursive
application is *embedded within another expression*. During execution,
the program must complete the evaluation of the expression after the
recursive call returns. Thus, the program must preserve sufficient
information from the outer call's environment to complete the
evaluation.

The definition for the function `factorial`{.scala} above is backward
recursive because the recursive application `factorial(n-1)`{.scala}
in the second leg is embedded within the expression 
`n * factorial(n-1)`{.scala}.  During execution, the multiplication
must be done after return. The program must "remember" (at least) the
value of parameter `n`{.scala} for that call.

A compiler can translate a backward linear recursion into a loop, but
the translation may require the use of a stack to store the program's
*state* (i.e. the values of the variables and execution location)
needed to complete the evaluation of the expression.

Often when we design an algorithm, the first functions we come up with
are backward recursive. They often correspond directly to a convenient
recurrence relation. It is often useful to convert the function into
an equivalent one that evaluates more efficiently.


### Forward recursion

A function definition is *forward recursive* if the recursive
application is *not embedded within another expression*. That is, the
*outermost expression is the recursive application* and any other
subexpressions appear in the argument lists. During execution,
significant work is done as the recursive calls are made (e.g. in the
argument list of the recursive call).

The definition for the auxiliary function `factIter`{.scala} within
the `factorial2`{.scala} definition below is forward recursive. The
recursive application `factIter(m-1,m*r)`{.scala} in the second leg is
on the outside of the expression evaluated for return. The other legs
are nonrecursive.

~~~{.scala}
    def factorial2(n: Int): Int = {

        def factIter(n: Int, r: Int): Int = n match {
            case 0          => r
            case m if m > 0 => factIter(m-1,m*r)
        }

        if (n >= 0)
            factIter(n,1)
        else
            sys.error(s"Factorial undefined for $n")
    }
~~~

What are the precondition and postcondition for
`factIter(n,r)`{.scala}?

To avoid termination, `factIter(n,r)`{.scala} requires 
`n >= 0`{.scala}. Its postcondition is that:

>   `factIter(n,r) = r *`{.scala} *fact*`(n)`{.scala}

How do we know that `factIter`{.scala} terminates?

Argument `n`{.scala} of the recursive call is at least 1 and decreases
by 1 on each recursive call; it eventually reaches the base case.

What is the time complexity of function `factorial2`{.scala}?

Function `factIter(n,r)`{.scala} has a time complexity of
O(`n`{.scala}). But, because, tail call optimization converts the
`factIter`{.scala} recursion to a loop, the time complexity's constant
factor should be smaller than that of `factorial(n)`{.scala}.

As shown, `factIter(n,r)`{.scala} seems to have a space complexity of
O(`n`{.scala}). But tail call optimization converts the recursion to a
loop. Thus the space complexity of `factIter(n,r)`{.scala} becomes
O(1).


### Tail Recursion

A function definition is *tail recursive* if it is *both forward
recursive and linear recursive*. In a tail recursion, the last action
performed before the return is a recursive call.

The definition of the function `factIter`{.scala} above is tail
recursive because it is both forward recursive and linear recursive.

Tail recursive definitions are easy to compile into efficient loops.
There is no need to save the states of unevaluated expressions for
higher level calls; the result of a recursive call can be returned
directly as the caller's result. This is sometimes called *tail call
optimization* (or "tail call elimination" or "proper tail calls").

In converting the backward recursive function `factorial`{.scala} to a
tail recursive auxiliary function, we added the parameter `r`{.scala}
to `factIter`{.scala}.  This parameter is sometimes called an
*accumulating parameter* (or just an *accumulator*).

We typically use an accumulating parameter to "accumulate" the result
of the computation incrementally for return when the recursion
terminates.  In `factIter`{.scala}, this "state" passed from one
"iteration" to the next enables us to convert a backward recursive
function to an "equivalent" tail recursive one.

Function `factIter(n,r)`{.scala} defines a more general function than
`factorial`{.scala}.  It computes a factorial when we initialize the
accumulator to 1, but it can compute some multiple of the factorial if
we initialize the accumulator to another value. However, the
application of `factIter`{.scala} in `factorial2`{.scala} gives the
initial value of 1 needed for factorial.

Consider auxiliary function `fibIter`{.scala} used by function
`fib2`{.scala} below. This function adds two "accumulating parameters"
to the backward nonlinear recursive function `fib`{.scala} to convert
the nonlinear (tree) recursion into a tail recursion. This technique
works for Fibonacci numbers, but the same technique will not work in
all cases.

~~~{.scala}
    def fib2(n: Int): Int = {

        def fibIter(n: Int, p: Int, q: Int): Int = n match {
            case 0 => p
            case m => fibIter(m-1,q,p+q)
        }

        if (n >= 0)
            fibIter(n,0,1)
        else
            sys.error(s"Fibonacci undefined for $n")
    }
~~~

What are the precondition and postcondition for 
`fibIter(n,p,q)`{.scala}?
 
To avoid abnormal termination, `fibIter(n,p,q)`{.scala} requires 
`n >= 0`{.scala}. When the precondition holds, its postcondition is:

>   `fibIter(n,p,q) =`{.scala} 
    *Fibonacci*`(n) + (p + q - 1)`{.scala}
	
How do we know that `fibIter`{.scala} terminates?

The recursive leg of `fibIter(n,p,q)`{.scala} is only evaluated when
`n1 > 0`{.scala}. On the recursive call, that argument decreases
by 1. So eventually the computation reaches the base case.

What are the time and space complexities of `fibIter`{.scala}?

Function `fibIter`{.scala} has a time complexity of O(`n`{.scala}) in
contrast to O(`fib(n)`{.scala}) for `fib`{.scala}. This algorithmic
speedup results from the replacement of the very expensive operation
`fib(n-1) + fib(n-2)`{.scala} at each level in `fib`{.scala} by the
inexpensive operation `p + q`{.scala} (i.e. addition of two numbers)
in `fib2`{.scala}. 

Without tail call optimization, `fibIter(n,p,q)`{.scala} has space
complexity of O(`n`{.scala}). However, tail call optimization can
convert the recursion to a loop, giving O(1) space complexity.

When combined with tail-call optimization, a tail recursive function
may be more efficient than the equivalent backward recursive
function. However, the backward recursive function is often easier to
understand and to reason about.


## Logarithmic Recursive

We can define the exponentiation operator `^`{.scala} in terms of
multiplication as follows for integers `b`{.scala} and 
`n >= 0`{.scala}:

>   `b^n =` $\prod_{i=1}^{i=n} b$ 

The backward recursive exponentiation function `expt1`{.scala} below
raises a number to a nonnegative integer power.  It has time
complexity O(`n`{.scala}) and space complexity O(`n`{.scala}).

~~~{.scala}
    def expt1(b: Double, n: Int): Double = n match {
        case 0          => 1
        case m if m > 0 => b * expt1(b,m-1)
        case _          =>
            sys.error(s"Cannot raise to a negative power $n")
    }
~~~

Consider the following questions relative to `expt1(b,n)`{.scala}.

-   What are the precondition and postcondition for
    `expt1(b,n)`{.scala}?
	
-   How do we know that `expt1(b,n)`{.scala} terminates?

-   What are the time and space complexities for `expt1(b,n)`{.scala}?

We can define a tail recursive auxiliary function `exptIter`{.scala}
by adding a new parameter `p`{.scala} to accumulate the value of the
exponentiation incrementally. We can define `exptIter`{.scala} within
a function `expt2`{.scala}, taking advantage of the fact that the base
`b`{.scala} does not change. This is shown below.

~~~{.scala}
    def expt2(b: Double, n: Int): Double = {
	
        def exptIter(n: Int, p: Double): Double = 
	        n match {
                case 0 => p
                case m => exptIter(m-1,b*p)
            }
			
        if (n >= 0)
            exptIter(n,1)
        else
            sys.error(s"Cannot raise to negative power $n")
    }
~~~

Consider the following questions relative to `expt1(b,n)`{.scala}.

-   What are the precondition and postcondition for
    `exptIter(n,p)`{.scala}?
	
-   How do we know that `exptIter(n,p)`{.scala} terminates?

-   What are the time and space complexities for
    `exptIter(n,p)`{.scala}?

The exponentiation function can be made computationally more efficient
by squaring the intermediate values instead of iteratively multiplying.
We observe that:

~~~{.scala}
    b^n = b^(n/2)^2   if n is even
    b^n = b * b^(n-1) if n is odd
~~~

Function `expt3`{.scala} below incorporates this observation in an
improved algorithm. Its time complexity is O(`log(n)`{.scala}) and
space complexity is O(`log(n)`{.scala}).

~~~{.scala}
    def expt3(b: Double, n: Int): Double = {

        def exptAux(n: Int): Double = n match {
            case 0                 => 1
            case m if (m % 2 == 0) => // i.e. even
                val exp = exptAux(m/2)
                exp * exp             // backward recursion
            case m                 => // i.e. odd
                b * exptAux(m-1)      // backward recursion
        }

        if (n >= 0)
            exptAux(n)
        else
            sys.error(s"Cannot raise to negative power $n")
    }
~~~

Consider the following questions relative to `expt3`{.scala}.

-   What are the precondition and postcondition of
    `expt3(b,n)`{.scala}?

-   How do we know that `exptAux(n)`{.scala} terminates?

-   What are the time and space complexities of `exptAux(n)`{.scala}?


## Exercises

TODO: I adapted many of these exercise descriptions from similar
Haskell exercises in ELIFP \[Cunningham 2018\] Chapters 5 and 9. They
should be reconsidered, refined, and tested better for use in a
Scala-based functional programming course. The order may also need to
be modified and some exercises are probably better placed with
different notes.

1.  Answer the questions (precondition, postcondition, termination,
    time complexity, space complexity) in the discussion of
    `expt1`{.scala}.

#.  Answer the questions in the discussion of `expt2`{.scala} and
    `exptIter`{.scala}.

#.  Answer the questions in the discussion of `expt3`{.scala} and
    `exptAux`{.scala}.

#.  Develop a Scala function `sumSqBig` that takes three
    `Double`{.scala} arguments and returns the sum of the squares of
    the two larger numbers.
	
	For example, `sumSqBig(2.0,1.0,3.0)`{.scala} yields
    `13.0`{.scala}.

#.  Develop a Scala function `prodSqSmall`{.scala} that takes three
    `Double`{.scala} arguments and returns the product of the squares
    of the two smaller numbers.

    For example, `prodSqSmall(2.0,4.0,3.0)`{.scala} yields
	`36.0`{.scala}.

#.  Develop a Scala function `xor`{.scala} that takes two Boolean
    arguments and returns the "exclusive-or" of the two values. An
    exclusive-or operation returns `true`{.scala} when exactly one of
    its arguments is `true`{.scala} and returns `false`{.scala}
    otherwise.
	
#.  Develop a Scala function `implies`{.scala} that takes two Boolean
    arguments `p`{.scala} and `q`{.scala} and returns the Boolean
    result `p`{.scala} $\Rightarrow$ `q`{.scala} (i.e. logical
    implication).  That is, if `p`{.scala} is `true`{.scala} and
    `q`{.scala} is `false`{.scala}, then the result is
    `false`{.scala}; otherwise, the result is `true`{.scala}.
	
	Note: This function is sometimes called `nand`{.scala}.
	
#.  Develop a Scala function `div23n5`{.scala} that takes an
    `Int`{.scala} and returns the Boolean `true`{.scala} if and only
    if the integer is divisible by 2 or divisible by 3, but is not
    divisible by 5.
	
	For example, `div23n5(4)`{.scala}, `div23n5(6)`{.scala}, and
	`div23n5(9)`{.scala} yield `true`{.scala} and 
    `div23n5(5)`{.scala}, `div23n5(7_`{.scala}, 
	`div23n5(10)`{.scala}, `div23n5(15)`{.scala}, 
	`div23n5(30)`{.scala} yield `false`{.scala}.

#.  Develop a Scala function `notDiv`{.scala} such that 
    `notDiv(n,d)`{.scala} returns `true`{.scala} if and only if
    integer `n`{.scala} is not divisible by integer `d`{.scala}. 
	
	For example, `notDiv(10,5)`{.scala} yields `false`{.scala} and
    `notDiv(11,5)`{.scala} yields `true`{.scala}.

#.  Develop a Scala function `ccArea`{.scala} that takes the
    *diameters* of two concentric circles (i.e. with the same center
    point) as `Double`{.scala} values and returns the area of the
    space between the circles. That is, compute the area of the larger
    circle minus the area of the smaller circle.
	
	For example, `ccArea(2.0,4.0)` yields approximately
    `9.42477796`{.scala}.
	
#.  Develop a Scala function `addTax`{.scala} that takes 
    two `Double`{.scala} values such that `addTax(c,p)`{.scala}
    returns `c`{.scala} with a sales tax of `p`{.scala} *percent*
    added. For example, `addTax(2.0,9.0)`{.scala} returns
    `2.18`{.scala}.

    Also develop a function `subTax`{.scala} that is the inverse of
    `addTax`{.scala}. That is, `subTax((addTax(c,p)), p)`{.scala}
    yields `c`{.scala}. For example, 
	`subTax(2.18,9.0) = 2.0`{.scala}.
	
#.  Develop a backward recursive Scala function
    `sumTo`{.scala} such that `sumTo(n)`{.scala} computes the sum of
    the integers from 1 to `n`{.scala} for `n > 0`{.scala}.

#.  Develop a Scala function `sumTo2`{.scala} such that 
    `sumTo2(n)`{.scala} computes the sum of the integers from 1 to
    `n`{.scala} for `n > 0`{.scala}. Use a tail recursive auxilliary
    function.

#.  Develop a backward recursive Scala function `sumFromTo`{.scala} 
    such that `sumFromTo(m,n)`{.scala} computes the sum of the
    integers from `m`{.scala} to `n`{.scala} for `n >= m`{.scala}.

#.  Develop a Scala function `sumFromTo2`{.scala} such that
    `sumFromTo2(m,n)`{.scala} computes the sum of the integers 
	from `m`{.scala} to `n`{.scala} `n >= m`{.scala}. Use a tail
	recursive auxilliary function.

#.  Suppose we have Scala functions `succ`{.scala} (successor) and
    `pred`{.scala} (predecessor) defined as follows:

    ~~~{.scala}
        def succ(n: Int): Int = n + 1
        def pred(n: Int): Int = n - 1
    ~~~

    Develop a recursive Scala function `add`{.scala} such that
    `add(m,n)`{.scala} computes `m + n`{.scala} for two integers
    `m`{.scala} and `n`{.scala}. Function `add`{.scala} *cannot* use
    addition or subtraction operators but *can* use unary negation,
    comparisons between integers, and the `succ`{.scala} and
    `pred`{.scala} functions defined above.

#.  Develop a recursive Scala function `mult`{.scala} such that
    `mult(m,n)`{.scala} computes `m * n`{.scala} for two integers
    `m`{.scala} and `n`{.scala}. The function *cannot* use the
    multiplication (`*`{.scala}) or division (`/`{.scala}) operators
    but *can* use unary negation, comparisons between integers, and
    the `succ`{.scala}, `pred`{.scala}, and `add`{.scala} function
    from the previous exercise.

#.  Develop a recursive Scala function `acker`{.scala} to compute
    Ackermann's function, which is a function $A$ defined as follows
    for integers `m`{.scala} and `n`{.scala}:

    ---------  ----- ------------------- ------------------------
    $A(m,n)$   $=$   $n+1,$              if $m = 0$
    $A(m,n)$   $=$   $A(m-1,1),$         if $m > 0$ and $n = 0$
    $A(m,n)$   $=$   $A(m-1,A(m,m-1)),$  if $m > 0$ and $n > 0$
    ---------  ----- ------------------- ------------------------

#.  Develop a recursive Scala function `hailstone`{.scala} to
    implement the following function:

    ---------------  -----  -------------------  ----------------------
    $hailstone(n)$   $=$    $1$,                 if $n = 1$
    $hailstone(n)$   $=$    $hailstone(n/2)$,    if $n > 1$, even $n$ 
    $hailstone(n)$   $=$    $hailstone(3*n+1)$,  if $n > 1$, odd $n$
    ---------------  -----  -------------------  ----------------------

    Note that an application of the `hailstone`{.scala} function to
    the argument `3` would result in the following "sequence" of
    "calls" and would ultimately return the result `1`{.scala}.

    ~~~{.scala}
        hailstone(3)
          hailstone(10)
            hailstone(5)
              hailstone(16)
                hailstone(8)
                  hailstone(4)
                    hailstone(2)
                      hailstone(1)
    ~~~

    What is the domain of the *hailstone* function? How do we know the
    function terminates?

#.  Develop a Scala exponentiation function `expt4`{.scala} that is
    similar to `expt3`{.scala} but is tail recursive as well as
    logarithmic recursive.

#.  Develop the following group of recursive Scala functions:

    -   `test`{.scala} such that `test(a,b,c)`{.scala} is
        `true`{.scala} if and only if `a <= b`{.scala} and no
        integer is the range from `a`{.scala} to `b`{.scala}
        inclusive is divisible by `c`{.scala}.
		
	-   `prime`{.scala} such that `prime(n)`{.scala} is
        `true`{.scala} if and only if `n`{.scala} is a prime
        integer.

    -   `nextPrime`{.scala} such that `nextPrime(n)`{.scala}
         returns the next prime integer greater than `n`{.scala}

#.  Develop a recursive Scala function `binom`{.scala} to compute
    *binomial coefficients*. That is, `binom(n,k)`{.scala} returns
    $\binom{n}{k}$ for integers `n >= 0`{.scala} and 
	`0 <= k <= n`{.scala}.

#. The time of day can be represented in Scala by the definitions

    ~~~{.scala}
	    sealed trait APM 
	    case object AM extends APM 
	    case object PM extends APM 
	    case class Time12(hours: Int, minutes: Int, apm: APM) 
	~~~
	
    where `hours`{.scala} and `minutes`{.scala} are integers such that
    `1 <= hours <= 12`{.scala} and `0 <= minutes <= 59`{.scala}.
	
	Develop a Boolean Scala function `comesBefore`{.scala} that takes
    two `Time12`{.scala} objects and determines whether the first is
    an earlier time than the second.
	
	TODO: Perhaps modify the exercise above to use the `Ord`{.scala}
    trait as in the exercise below.

#.  A date on the *proleptic Gregorian calendar* (see note below) can
    be represented in Scala by the definition

    ~~~{.scala}
	    case class PGDate(year: Int, month: Int, day: Int) 
	~~~
	
    with the following constraints on *valid* objects:
	
	-   `year`{.scala} is any integer
	-   `1 <= month <= 12`{.scala}
	-   `1 <= day <= days_in_month(year,month)`{.scala}
	
	Here `days_in_month(year,month)`{.scala} represents the number of
	days in the the given `month`{.scala} (i.e. 28, 29, 30, or 31) for
	the given `year`{.scala}. Remember that the number of days in
	February varies between regular and leap years.
	
	For the items below, write your own Scala functions. Do not use a
    date library.
	

	a.  Extend class `PGdate`{.scala} to implement trait `Ord`{.scala}
        as defined below (and in the 
        [*Notes on Scala for Java Programmers* 
		](<../ScalaForJava/ScalaForJava.html>)): 
	
        ~~~{.scala}
	        trait Ord {
                def < (that: Any): Boolean 
                def <=(that: Any): Boolean = 
				    (this < that) || (this == that) 
                def > (that: Any): Boolean = !(this <= that) 
                def >=(that: Any): Boolean = !(this < that) 
            }
		~~~
		
        If needed, redefine the method `equals`{.scala}.
		
		The interpretation of `d1 < d2`{.scala} is that
        `d1`{.scala} is an earlier date than `d2`{.scala}.

    #.  Redefine method `toString`{.scala} appropriately for
        `PGDate`{.scala}.
	
    #.  Develop a Scala function `validPGDate(d)`{.scala} that takes a
	    `PGDate`{.scala} object `d`{.scala} and returns `true`{.scala}
	    if and only if `d`{.scala} satisfies the constraints given
	    above.
	
	    For example:
		
		-   `validPGDate(PGDate(2019,2,1)) == true`{.scala}
        -   `validPGDate(PGDate(2016,2,29)) == true`{.scala}
        -   `validPGDate(PGDate(2017,2,29)) == false`{.scala}
		-   `validPGDate(PGDate(0,0,0)) == false`{.scala}
		
        You may need to develop one or more other functions to
        implement the `validPGDate`{.scala} function.


    #.  For any `PGDate`{.scala} beginning with (i.e. `>=`{.scala})
        `PGDate(-4712,1,1)`{.scala}, develop Scala functions:
		
		-   `daysBetween(d1,d2)`{.scala} that takes two valid
            `PGDate`{.scala} objects `d1`{.scala} and `d2`{.scala} and
            returns the number of days between them. The difference
            value is positive if `d1 < d2`{.scala} and negative if 
			`d1 > d2`{.scala}.
			
		-   `addDays(d,days)`{.scala} takes a `PGDate`{.scala} object
            `d`{.scala} and an integer number of days and returns a
            valid `PGDate`{.scala} object that is offset by that
            number of days. A positive offset results in a later date.
			

	Note: The Gregorian calendar \[Wikipedia 2019\] was introduced by
    Pope Gregory of the Roman Catholic Church in October 1582. It
    replaced the Julian calendar system, which had been instituted in
    the Roman Empire by Julius Caesar in 46 BC. The goal of the change
    was to align the calendar year with the astronomical year.
	
	Some countries adopted the Gregorian calendar at that time.  Other
    countries adopted it later. Some countries may never have adopted
    it officially.
	
	However, the Gregorian calendar system became the common calendar
    used worldwide for most civil matters. The *proleptic Gregorian
    calendar* \[Wikipedia 2019\] extends the calendar backward in
    time from 1582. The year 1 BC becomes year 0, 2 BC becomes year
    -1, etc. The proleptic Gregorian calendar underlies the ISO 8601
    standard used for dates and times in software systems 
	\[Wikipedia 2019\].
	
	Arithmetic on calendar dates is often done by converting a date to
    the Julian Day Number (JDN), doing the arithmetic on those values,
    and then converting back to the calendar date \[Wikipedia 2019\].


#.  Develop a Scala function `roman`{.scala} that takes an
    `Int`{.scala}) in the range from 0 to 3999 (inclusive) and
    returns the corresponding Roman numeral as a string (using capital
    letters). The function should halt with an appropriate
    `sys.error`{.scala} messages if the argument is below or above the
    range. Roman numbers use the following symbols and are combined by
    addition or subtraction of symbols.

    ---- --------
    I    1
    V    5
    X    10
    L    50
    C    100
    D    500
    M    1000
    ---- --------

    *For the purposes of this exercise, we represent the Roman numeral
	for 0 as the empty string.*  The Roman numbers for integers 1-20
	are `I`, `II`, `III`, `IV`, `V`, `VI`, `VII`, `VIII`, `IX`, `X`,
	`XI`, `XII`, `XIII`, `XIV`, `XV`, `XVI`, `XVII`, `XVII`, `XIX`,
	and `XX`.  Integers 40, 90, 400, and 900 are `XL`, `XC`, `CD`, and
	`CM`.

#. Develop a Scala function

    ~~~{.scala}
        def minf(g: (Int => Int)): Int
    ~~~

    that takes a function `g`{.scala} and returns the smallest integer
    `m`{.scala} such that `0 <= m <= 10000000`{.scala} and 
	`g(m) == 0`{.scala}. It should throw a `sys.error`{.scala} if
    there is no such integer.


## Acknowledgements

I wrote the first version of these notes in Fall 2013 to accompany my
lectures on recursion concepts and programming techniques for a
Lua-based course. I adapted some aspects of my earlier notes on
functional programming using Haskell \[Cunningham 2014\].

I adapted the factorial, Fibonacci number, and exponentiation
functions from similar Scheme functions in the classic textbook
[SICP ](http://mitpress.mit.edu/sicp/) \[Abelson 1996\].

I subsequently adapted these notes for use in functional or
multiparadigm programming classes using Elixir (Spring 2015), Scala
(Spring 2016), and Haskell (Summer 2016) \[Cunningham 2016\].

In Summer 2016, I also incorporated the Haskell version in what is now
Chapter 9 of my Haskell-based textbook *Exploring Languages using
Interpreters and Functional Programming* (ELIFP) \[Cunningham 2018\].

In Spring 2019, I merged parts of ELIFP Chapter 9 and the earlier
Scala version of the notes to create the current document. I also
included some exercises from ELIFP Chapter 5.


## References

\[Abelson 1996\]:
:   Harold Abelson and Gerald Jay Sussman. 
    *Structure and Interpretation of Computer Programs*
    ([SICP ](http://mitpress.mit.edu/sicp/)), Second Edition, 
	MIT Press, 1996. 
	
\[Bird 1988\]:
:   Richard Bird and Philip Wadler. 
    *Introduction to Functional Programming*, 
    [First Edition 
	](<https://usi-pl.github.io/lc/sp-2015/doc/Bird\_Wadler.%20Introduction%20to%20Functional%20Programming.1ed.pdf>),
	Prentice Hall, 1988.
    
\[Cunningham 2014\]: 
:   H. Conrad Cunningham. 
    [*Notes on Functional Programming with Haskell* 
    ](<https://john.cs.olemiss.edu/~hcc/csci450/notes/haskell\_notes.pdf>),
    1993-2014.
	
\[Cunningham 2016\]: 
:   H. Conrad Cunningham.
    [Recursion Concepts and Terminology 
	](<https://john.cs.olemiss.edu/~hcc/csci555/notes/RecursionConcepts/RecursionConceptsScala.html>),
    2013-2016.

\[Cunningham 2018\]:
:   H. Conrad Cunningham.  Exploring Languages with Interpreters `and
    Functional Programming, 2018. Available at 
    <https://john.cs.olemiss.edu/~hcc/csci450/ELIFP/ExploringLanguages.html>.

\[Wikipedia 2019\]:
:   *Wikipedia*, articles on "Gregorian Calendar", "Proleptic Gregorian
    Calendar", "Julian Day", and "ISO 8601", accessed on 31 January
    2019.

## Terms and Concepts

Recursion styles (linear vs. nonlinear, backward vs. forward, tail,
and logarithmic), correctness (precondition, postcondition, and
termination), efficiency estimation (time and space complexity),
transformations to improve efficiency (auxiliary function,
accumulator).
