# CSCI-142 Project 1: Functions

Due Friday, March 8, 2019, 11:59 PM

## Project description

This project is concerned with representing mathematical expressions in software so they can be manipulated in various ways and evaluated.

We define the abstraction called Function that represents any calculation that can be treated as the definition of a mathematical function of one variable. Your job is first to express Function as an abstract Java class (or interface) and then to build concrete implementors of the Function abstraction and for each one implement Function's operations:

• evaluate: Given a double floating point value of x, compute the value of the function.
• toString: Return a human-readable expression of the function as a Java String. Functions using an infix operator with two or more operands must include parentheses.
• derivative: Return the Function that is the (first) derivative of the function with respect to x.
• integral: Compute the (double) value of the definite integral of the function with respect to x over a given interval. The interval is expressed by two double arguments. For all but the simplest functions the integral has to be computed numerically using the trapezoid rule, so the number of trapezoids (i.e. the granularity of the calculation) is also given in case it is needed.
• isConstant: Answer whether or not this function is constant (including if it is a combination of other constants).

## Design Overview

The set of classes in the Function hierarchy form a classic Composite design pattern. Some functions are primitive; they have no smaller parts. The simple constant and the expression x are the most common examples of primitive equations. Most other functions are composites, meaning that they are composed of other "smaller" functions. For example, 2x is a product composite containing the primitives 2, a constant, and x, a simple variable. Observe the following figure.

sin(x) is a transcendental function that is actually a composite but containing only one subordinate. sin(2x) is an instance of the sine composite with a multiplication composite inside it, which in turn contains two primitives.

2x·sin(x) would at the top level be a product containing three functions:

## Examples

Now let's look at what the operations listed above do to some of these functions.

2x:

• Calling evaluate with argument 7.5 would yield the double 15.0.
• Calling toString would yield ( 2 * x ).
• Calling derivative would yield a Constant object representing the value 2.0.
• Calling integral from 1.0 to 5.0 would hopefully yield something close to the value of x2 at 5.0 minus the value of x2 at 1.0, or 24.0. However, since there is no universal closed form for integrating products, it has to be calculated by numerical means. The larger the 3rd argument is, the closer to 24.0 the answer should be.
• isConstant would yield false.

sin(2x):

• Calling evaluate with argument 7.5 would yield the double 0.65028784016.
• Calling toString would yield sin( ( 2 * x ) ) .
• Calling derivative would yield a Product object representing the function 2·cos(2x)
• Calling integral from 1.0 to 5.0 would yield something close to the value of -cos(2x)/2 at 5.0 minus the value of -cos(2x)/2 at 1.0, or 0.21146234626.
• Calling isConstant would yield false.

The third example 2x·sin(x) would of course be even more complex. However, it is important to remember that each function only deals with its immediate descendants. The full answer is computed by using recursion.

## Hints / tips

#### Derivatives Refresher

The derivative is always calculable in closed form. Here are some things to remember.

• The derivative of a sum is the sum of the derivatives.
• The derivative of a product yz is y times the derivative of z plus z times the derivative of y. (You must generalize this to a product of more than two factors. Think recursively!)
• The derivative of f(g(x)) with respect to x is the derivative of f(g(x)) with respect to g(x) times the derivative of g(x) with respect to x. (This is the chain rule.)
• The integral of a sum is the sum of the integrals.
• Functions containing products and trigonometric functions cannot be integrated to a closed form. (Remember, you cannot assume the argument of sine or cosine is simply x.) It is for these cases that you must approximate the integral with the trapezoid rule. This occurs in multiple functions, but you should figure out how to implement it only once!

#### Constants

isConstant() can be defined recursively, if you look at functions as forming trees, as they did in the figures above. A Function object is constant if:

• It is an instance of Constant (base case), or
• all of its "child" Function objects are constant.

In the discussion below the term "constant" refers to any object that answers true to isConstant().

• If several constants are provided to the Sum or Product constructor, they should be combined into a single Constant instance. Example: 2.0 * sin( x ) * 3.0 should be sin( x ) * 6.0.
• An empty (size 0) Sum or Product should represent 0.0 or 1.0, respectively.
• If in the end the Sum or Product contains a Constant equal to 0 or 1, respectively, it must be eliminated altogether from the sequence of terms stored unless it is the only one left.

#### No Other Simplifications

Clearly one can do many other things to reduce the size of the Sum and Product objects' string representations. Do not do any other simplifications besides the ones explained above for Constants.

#### Unusual Method Declaration for Varying the Number of Parameters

You will notice that some class's constructors, such as Sum's, accept a variable number of arguments. Here is how you allow it. If you define a method in this way

public void foo( Thing... all ) { … }

It means that you have a choice on how to call the method. First, you can pass a primitive array of the expected type:

Thing[] things = … ;
foo( things );

And in fact, inside the method, the parameter (all) will be a reference to that array.

However the caller can choose to list elements explicitly.

Thing t1 = …;
Thing t2 = …;
Thing t3 = …;
foo( t1, t2, t3 );

They will still be packaged up into the all array inside the method. So for the Sum class you might write

public Sum( Function... terms ) { … }

#### Design Choices

You are free to implement your Function class as either an interface or an abstract class. The advantage of using an abstract class is that you can remove some of the redundancy you'll encounter with an interface.

## Source Code

We are providing three test programs here. The expected output is in comments at the bottom of the program. You should make sure your output matches exactly.

Keep in mind these are not complete. Your own tests in FunctionTest should be much more rigorous.

## Part One

Although this project only has one due date, you can use the requirements in Part One to make sure you are not too far behind. Set a goal to get this part finished two weeks before the due date. Note that some points are awarded based only on the Part One instructions.

Write Java code to declare Function as described in section 2, but without the integral method, then write the following implementing classes:

• Constant -- The constructor takes the value as its argument.
• Variable -- This constructor takes no argument, since the only variable allowed is x. There should only be one instance of Variable. Therefore, the constructor is private and there is a public final static instance of Variable within the Variable class. The instance's name should be X.
• Sum -- The constructor takes an argument list of functions to be added together.

Example: the function 2x + 7 could be constructed as follows: new Sum( Variable.X, Variable.X, new Constant( 7 ) )

NOTE: The above function will print out as (x + x + 7) - do not attempt to make special code to have it print as 2x + 7 as you could have many different complicated Sums and should not make special cases for every case!

Important: The Sum constructor is declared as follows: public Sum( Function... terms ) { ... } This way, it can accept either an array of Functions or a sequence of separate Function arguments (as in the 2x+7 example above). In either case, the number of Function terms is not fixed. Inside the method, terms is treated as if it were declared as Function[]. This rule also applies to the Product constructor.

Finally, you must also write a test program FunctionTest1 that thoroughly exercises the above code.

## Part Two

Add the integral method to the Function interface/class. Override the method only in the classes where the integral is bound to have a closed form, i.e., Constant, Variable, and Sum. Next, write the following additional descendants of Function:

• Product -- constructor takes an argument list of functions to be multiplied together. Product should treat Constants like Sum does, but there are two differences. First, if all the Constant factors multiply to 1, then it can be eliminated unless it's the only one left. Second, if they all multiply to 0, then the Product should be made up of a single Constant factor 0.
• Sine -- constructor takes a function to which the sine function is to be applied.
• Cosine -- constructor takes a function to which the cosine function is to be applied.
Example: the function x2 + cos( 2x ) + 7 would be constructed as follows:
new Sum(
new Product( Variable.X, Variable.X ),
new Cosine( new Product( new Constant(2), Variable.X ) ),
new Constant( 7 )
)

Again, note that this should be printed as ( ( x * x ) + cos( ( 2 * x ) ) + 7.0 ) .

Finally, you must extend your test program so that it thoroughly exercises all of your code. Call the new one FunctionTest2.

## Submission

When you are done your IntelliJ IDEA™ project source directory should show these contents.

To submit, do the following.

1. Go to your project directory.
2. Zip up your src/ folder and name the file project1.zip.
(Make sure the zip file does not contain the .idea directory or any .iml file.)
3. Upload your zip file to the MyCourses dropbox for Project 1.
Don't forget to verify that your submission went through and that the zip file's contents are correct.