>

### CS 1323 Honors, Fall 1996

Individual Project 3
Cosines
Due Monday, September 23, 4:30pm

• Turn in a listing of the Haskell script you write for your project, following the required standards.
• Turn in a session transcript in which you show that your function operates correctly on arguments chosen to demonstrate the function's capabilities. Choose arguments with cosine values that you can remember (such as multiples of pi/4, including some that are multiples of pi/2 and pi, of course). Keep your arguments in the range -10*pi to 10*pi.
• Explain (handwritten on the transcript) what aspects of the function's behavior are illustrated by each of your choices of demonstration examples.
• Extra credit: Explain why the results that cosine delivers get more and more nonsensical as its argument gets larger (in either the positive or negative direction).

Note: pi is an intrinsic variable in Haskell. Its value is an approximation to the ratio of a circle's circumference to its diameter. The type of pi is in the Haskell class of types called Floating. Numbers in this class are represented with a finite precision. For example, Float and Double are two of the types in the class Floating. Numbers of type Float carry about seven decimal digits of precision and numbers of type Double about sixteen digits.

### The Function You Must Define

Define a function called "cosine" that delivers an approximation to the cosine of its argument.

The argument of cosine will be a number representing an angle measured in radians. As with most measurements (as opposed to numbers produced by counting things), this number will have a fractional part. So, it will be represented in Haskell by a number in the class Fractional. You may assume a more specific type if you like - Float, for example. In this case, your type specification for cosine would be:

cosine :: Float -> Float

Compute your approximation to cosine using the first fifty terms of the Maclarin series for cosine:

cosine(x) ~= 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! - x^10/10! + ...
where x^n means x raised to the power n and n! means n factorial (that is, n! = 1*2*3*...*n).

This formula approximates the cosine function to many digits of accuracy throughout the range of values of interest (-10*pi to 10*pi) when all the arithmetic is done precisely. The formula will deliver an accurate result in your computations for small arguments, but will be inaccurate for large ones. Don't worry about the inaccuracy for large arguments, but if you get wrong answers for small arguments, there is something wrong with your definition.

Note: This approximation to cosine is loosely related to the methods most computing systems use to compute cosines. But, those methods are much more ingenious. First, they all alleviate (but do not eliminate) the problem with large arguments by taking advantage of the cyclic nature of the cosine function. Second, the polynomial they use as an approximation is of much lower degree. Our approximation polynomial has 50 terms; theirs computes the cosine to six digits of precision using a polynomial with only four terms (or a seven-term polynomial for sixteen-digit precision). Such compact, high-precision approximations are the remarkable products of the work of numerical analysts, one of the fields of study in the domain of computer science.

Ground Rules

• You may use any aspects of Haskell covered in Lessons 1-12 of the Haskell textbook, plus anything permitted in previous projects. You may also use the intrinsic functions take, product, and fromIntegral and the enumeration notations [n .. m] and [n .. ]

take :: Int -> [a] -> [a]
take n xs = the first n elements of the sequence xs
For example, take 3 [1, 2, 3, 4, 5, 6, 7] is [1, 2, 3]

product :: Num x => [x] -> x
product = foldr (*) 1
For example, product[1, 2, 3, 4] is 1*2*3*4.

fromIntegral :: (Integral n, Num x) => n -> x
fromIntegral n = the number of type x with the value n
The type of the value that fromIntegral delivers depends on the context the invocation appears in. For example, in the formula fromIntegral(n)/5.0, fromIntegral will deliver a value with the same type as 5.0.

The enumeration notation [n .. m] (this is Haskell notation) denotes the sequence of numbers [n, n+1, ..., m] (this is ordinary mathematical notation), and the notation [n ..] (this is Haskell notation) denotes the infinite sequence of numbers [n, n+1, n+2, ...] (this is ordinary mathematical notation) For example, [1 .. 4] means [1, 2, 3, 4] and [1 ..] denotes the infinite sequence of all integers starting from one up.

• The Project Style Guide contains one new requirement for this project and subsequent ones. You must write type specifications to accompany each definition.

• It is likely that your definition of cosine will do a lot more computation than is necessary. Don't worry about that.

• Because some values of x^n and n! will lie outside the range of numbers representable in the floating point system your computing system uses, you will have to find a way to compute the value of the quotient x^n/n! without computing either x^n or n! - do worry about this.

• Strictly OPTIONAL (no extra credit): If you want an added challenge, you can try to define a more efficient version of cosine. If you decide to try this, the intrinsic function scanl (that's "scan"-"el", el for "from the left") will be helpful. It works like foldr, except that it associates the operands from the left instead of the right and it delivers all the partial results computed along the way:
```    scanl :: (a -> b -> a) -> a -> [b] -> [a]
scanl op z [y, x, w, v] = [z,
z `op` y,
(z `op` y) `op` x,
((z `op` y) `op` x) `op` w,
(((z `op` y) `op` x) `op` w) `op` v]
```
If you pursue this option, you may find the following function useful:
takeThemTwoByTwo :: [x] -> [[x]]
takeThemTwoByTwo xs = takeWhile (not . null) [take 2 xsSuffix | xsSuffix <- iterate (drop 2) xs]