CSCI-142 Project 1: Functions

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

Note: this project must be done individually.

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:

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.

Figure 1

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.

Figure 2

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

Figure 3

Examples

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

2x:

sin(2x):

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.

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:

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

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:

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:

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.

Grading

Your grade is based on the following aspects of your work.

* The Part 2 Test must test all functionality, not just that added since Part 1.