Homework Assignment 8 CS 340d Unique Number: 50960 Spring, 2019 Given: March 25, 2019 Due: April 3, 2019 This homework assignment concerns improving your understanding of the garbage collection algorithm presented in Henry Baker's April, 1978, CACM article: "List Processing in Real Time on a Serial Computer". Conceptually, a garbage collector is a simple thing. But, to implement the abstraction of an ``infinite'' memory is not so simple. Why is having such an abstraction useful? It goes to the heart of specification and verification. When using memory, a programmer doesn't want to have to worry that memory is about to ``run out'' -- a programmer wants to work as if memory is infinite. With conventional programming languages (e.g., C, C++), the programmer is responsible for managing the memory. With Java, a garbage collector is offered, and so is a way (assignment) of making it use it very complex. A call/return stack is a beautiful abstraction. Most programmers use it without even thinking about it. For instance, when programming in many languages, one calls functions without being concerned whether there is remaining stack space. For many programming tasks, one uses the stack without ever thinking about how much memory has been allocated, reclaimed, and repeatedly reused. The dynamic memory described by Baker is as elegant as a stack, but it provides the flexibility of dynamically allocated and managed binary trees. In essence, CONS is a binary tree constructor; it takes two objects and makes them one. CAR returns the left sub-tree of a CONS pair; CDR returns the right sub-tree of a CONS pair. With Baker's algorithm (and there are many other algorithms providing essentially the same functionality), one can allocate trees with the same ease as using the stack. These trees persist so long as they are ``in scope'' just like storage allocated on a stack. Once a reference to a tree is lost, then the storage unique to that tree is reclaimed automatically by a garbage collector -- just like the stack space being used by a function is automatically reclaimed when a function exits. And, this is a magical thing. This homework is designed to help you understand this tree-based memory abstraction and how it can be implemented on a conventional (e.g., x86-based) computer. Below are four primitive functions signatures and three functions defined in terms of these primitives. long atom( long x ); // Recognizes atoms (non trees) long car( long x ); // Returns the left element from a pair long cdr( long x ); // Returns the right element from a pair long cons( long x, long y ) // Return a reference to a new pair long app( long x, long y ) { if( atom( x ) ) return( y ); return( cons( car( x ), app( cdr( x ), y ) ) ); long rev( long x ) { if( atom( x ) ) return( NIL ); return( app( rev( cdr( x ) ), cons( car( x ), NIL ))); } long make_lst1( long n ) { if( n <= 0 ) return( NIL ); else return( cons( NIL, make_lst1( n - 1 ) ) ); In this homework, you are being asked to animate the operation of the app and rev functions. First, we create two trees using the make_list1 function. 1. Place the trees identified below into your tree-based ``memory''. Look at the homework page for a file called "memory-sheet.txt" long t1 = make_lst1( 1 ); long t2 = make_lst1( 2 ); Place these two trees in your heap memory. Using your "memory-sheet.txt", calculate the following: long t3 = app( t1, t2 ); and show what cells are allocated. Now, given that we perform the following operations: t1 = NIL; t2 = NIL; how many CONS cells can we reach? Can we "clean" our memory of any CONS cells? If so, how many remain? 2. Given the configuration of your memory at the end of problem 1 (that is, after cleaning unreachable cells), calculate: long rev_t3 = rev( t3 ); Can this computation be fit in your memory? Starting again from the memory available at the end of problem 1, can these two computations be performed within your memory? long t4 = cons( NIL, t3 ); long rev_t4 = rev( t4 ); If so, how? If not, why? 3. This problem involves hand animation of garbage collector operation. And, this is where things get tricky. During the calculation of rev_t4, memory will become scarce. How scarce? Well, scarce enough that if we don't garbage collect unreachable cells, we will run out of space. Given your implementation of garbage collection, can you animate the calculation of rev_t4? You only need to show the configuration of your memory when you start, and each time you have to garbage collect. Use as many "memory-sheet.txt" pages as you need.