Project #2 Notes, Spring 2006

This is a collection of (hopefully) helpful notes concerning the Spatial Database project.

Changes from the orginal assignment

Changes from 21 March 2006:
  1. Use doubles for all coordinates, lengths, velocties, and times (that is, ignore the instruction in the original directions about type float).
  2. The description of write has been corrected to mention the rad command rather than the (nonexistent) size command. Also, it asks that you print commands one per line, and print add commands in order of increasing ID.
  3. The near command asks for the objects to be printed in order of increasing ID. This will make testing a little easier. You should be able to do this with just a few lines of Java code; do use the sort methods provided in java.util.Collections and java.util.Arrays.
  4. We've clarified the within command to make it clear that we are talking about the distances of their centers from each other. As for near, we ask that you print pairs in increasing order by ID1 and (for equal values of ID1) by increasing order by ID2.
  5. The "Algorithms" section now suggests that to move objects to their new positions, you should put the objects at their new positions into a brand new QuadTree, and then replace the old QuadTree with the new one. The reason for this is there are cases where one object is "following" another and moves to the other object's former position before the program has gotten around to moving the other object. Our QuadTree representation does not take well to having two objects at the same place. Since we won't really have need for the set method in Set2D and QuadTree, we have filled in a simple-minded implementation for you (see ~cs61b/code/proj2/util).
  6. We have corrected the testcase in the testing subdirectory. Make sure to get the updated version from ~cs61b/code/proj2/testing.
  7. Please don't move code out of the tracker package in order to avoid dealing with packages. We will be testing that your tracker package works with our util package. In order for this to work, you must leave track.java pretty much as it is (basically just a call to tracker.Main.main).

Using staff versions

We are maintaining two JAR (Java Archive Repository) files containing the staff's versions of the packages tracker and util. You can use these to supply one half of the assignment while you work on the other. We are not supplying source, of course, so when you encounter an error that occurs in one of the staff's classes, you will have to step upwards in the call stack to find where your part most recently called the staff's. Don't overuse the staff packages; you should rely prinicipally on your own unit testing. The staff package, however, can serve as a "sanity check" that you haven't seriously misunderstood the assignment.

The staff version of the util package is in ~cs61b/lib/proj2-util.jar and the tracker is in ~cs61b/lib/proj2-tracker.jar. To use either of these, you must include the appropriate JAR file in your "class path".

For example, to use the staff's version of tracker with your util package, start an Eclipse project containing just the track.java class and your util source directory. Then add ~cs61b/lib/proj2-tracker.jar to what Eclipse calls the "build path":

Reading commands from a file

[3/24/2006] I have added some lines to ~cs61b/code/proj2/track.java so that you may take the command input from a named file, as in

    java track FILEFULLOFCOMMANDS

It seems that Eclipse is not very good at input redirection. On the UNIX command line, you could simply write

     java track < FILEFULLOFCOMMANDS
for this purpose, so you wouldn't need the extra argument.

This feature is entirely optional, since it is not mentioned in the spec.

A few practices to avoid

  1. Here is how one student wrote Main.main:
        public void main () {
    
          System.out.print("> ");
          Scanner inp = new Scanner (System.in);
    
          String input = inp.findWithinHorizon(".*", 0).trim();
    
          if (!input.contentEquals("quit"))
    	{
    	   processVal(input);
    	   main();
    	 }
          else exit(0);
        }
    
    There are several problems here:
    • This program reads and processes input one line at a time (.* matches one line). Commands can span multiple lines and multiple commands can appear on one line, so this clearly won't work.
    • The program creates a new Scanner for each line (a surprisingly common practice among beginning students). It is completely undefined what happens when you have multiple Scanners reading from the same input source. On our system, Java happens to handle interactive sources differently from non-interactive ones. As a result, on our system, when entering text from the keyboard, the newline at the end of a line gets "eaten" by the Scanner that reads that line, and the next Scanner thus skips the newline, causing things to work pretty much as expected. But when entering lines from a file (as the autograder will do), using input indirection:
          java track < test5.in 
      
      it is the next Scanner that gets the newline, causing findWithinHorizon to return "" on the second and subsequent lines forever. Again, just create one Scanner per file.

      Another problem is that Java implementations do not deal well with tail recursion. Unlike Scheme, you cannot expect a tail recursive procedure to use constant space. It matters here because the number of commands to be entered can be arbitrarily large. Use a loop instead.

  2. We haven't talked about floating-point arithmetic. The important point is that it is approximate: it keeps a limited number of digits, and uses binary fractions rather than decimal ones. As a result, 0.125 is exact, but 0.1 is not. The upshot is that you must be careful about using == tests to stop loops or detect collisions. I suggest instead >= or <= as apppriate.

    Contrary to what you'll read in some books, there are perfectly good uses for == in floating point, such as

    if (x == 0.0) 
        y = 1.0;
    else
        y = Math.sin (x) / x;
    
    but they probably won't come up in this project.

Dealing with approximation

Because floating-point arithmetic is approximate, we have to take a few precautions. One student encountered this problem:
The following call returns 6.574491741640564E-17 ucb.proj2.Physics.collide([0.0730206927947019, 3.360736212390728, 1.2, 2.1], [-0.17774220073960237, 3.2762901868696694, 3.232, 1.132], 0.1323). I'm not sure whether or not this value is correct, but it is so small that when subtracted from the time the simulation is supposed to run, the value does not change. That is t-6.574491741640564E-17==t is true. And so the simulator runs into an infinite loop because t is not decreasing. Is this a correct result from Physics? Or else what should I do about this problem?

Our response: It is correct, and the looping problem is not caused by the problem you mention. If you compute the new positions of the particles after this period of time, you will find that they are touching to within the precision of floating-point numbers. What should happen is that you call rebound, and it adjusts the velocities so that the next call to collide for these particles returns Infinity. The fact that this particular round did not advance t is irrelevant.

  1. It's OK to call rebound when objects are slightly more than 2*radius apart. There's a right way to do this, but for our purposes, I suggest testing that the squared distance between the two centers is <= 4.0 * δ * radius2 where δ is 1.0+1.0/(1L<<50). Don't leave out the "L" after 1. δ is a 51-bit floating-point value; double carries 52. I suggest testing the square value (len2) here because it avoids an unnecessary square root.

    Yeah, this is kludgy, but our physics here isn't exactly "physical".

  2. Don't do anything like this:
      oldTime = t; t = t-collisionTime; 
      advance things to their new positions oldTime-t from now;
    
    for the reasons you've already observed in your problem report.
PNH


[CS61B Home Page]

Page was last modified on Mon Apr 3 20:02:42 2006.
Address comments and questions to cs61b@cory.eecs.berkeley.edu