CS 334
Programming Languages
Spring 2002

Assignment 10
Due Thursday, 5/9/2002

  1. Do problem 14-22 on page 14.38 of the text.

  2. Do problem 14-26, parts a and b only, on page 14.39 of the text.

  3. Do problem 14-29 on page 14.39 of the text.

  4. Do problem 14-36 on page 14.40 of the text.

  5. The ML function, primesto n, given below, can be used to calculate the list of all primes less than or equal to n by using the classic "Sieve of Eratosthenes".

    (* Sieve of Eratosthenes: Remove all multiples of first element from list,
       then repeat sieving with the tail of the list.  If start with list [2..n]
       then will find all primes from 2 up to and including n. *)
    fun sieve [] = []
      | sieve (fst::rest) = let
           fun filter p [] = []
             | filter p (h::tail) = if (h mod p) = 0 then filter p tail
                                                else h::(filter p tail);
           val nurest = filter fst rest
            fst::(sieve nurest)
    (* returns list of integers from i to j *)
    fun fromto i j = if j < i then [] else i::(fromto (i+1) j);
    (* return list of primes from 2 to n *)
    fun primesto n = sieve(fromto 2 n);

    Notice that each time through the sieve we first filter all of the multiples of the first element from the tail of the list, and then perform the sieve on the reduced tail. In ML, one must wait until the entire tail has been filtered before you can start the sieve on the resulting list. However, one could use parallelism to have one process start sieving the result before the entire tail had been completely filtered by the original process.

    Here is a good way to think of this concurrent algorithm which will use a slightly modifed version of the Java Buffer class defined in the lecture notes. The only modifications necessary are to have the buf instance variable hold an array of int, rather than char, and then change the appropriate types of parameters, return types, and local variables in the put and get methods from char to int.

    The main program should begin by creating a Buffer object (say with 5 slots) and then should successively put the numbers from 2 to n (for some fixed n) into the Buffer using the put method, and finally put in -1 to signal that it is the last element. After the creation of the Buffer object, but before starting to put the numbers into the Buffer, the program should create a Sieve object (using the Sieve class described below) and pass it the Buffer object (as a parameter to Sieve's constructor). The Sieve object should then begin running in a separate thread while the main program inserts the numbers in the buffer.

    After the Sieve object has been constructed and the Buffer object stored in an instance variable, in, its run method should get the first item from in using the get method. If that number is negative then the run method should terminate. Otherwise it should print out the number (System.out.println is fine) and then create a new Buffer object, out. A new Sieve should be created with Buffer out and started running in a new thread. Meanwhile the current Sieve object should start filtering the elements from the in buffer. That is, the run method should successively grab numbers from the in buffer, checking to see if each is divisible by the first number that was obtained from in. If a number is divisible, then it is discarded, otherwise it is put on buffer out. This reading and filtering continues until a negative number is read. When the negative number is read then it is put into the out buffer and then the run method terminates.

    If all of this works successfully then the program will eventually have created a total of p + 1 objects of class Sieve (all running in separate threads), where p is the number of primes between 2 and n. The instances of Sieve will be working in a pipeline, using the buffers to pass numbers from one Sieve object to the next.

    Please write this program in Java using the Buffer class in Lecture 23, modified as suggested above so that it holds ints rather than chars. Each of the buffers used should be able to hold at most 5 items.

    Truth in Advertising: Because these threads will all be running on a single-processor machine, the program will ultimatively run much, much slower than a sequential program doing the same task!