Introduction to Computer Science Stacks and Queues More Recursion and Pointers Unit 19.
-
date post
19-Dec-2015 -
Category
Documents
-
view
224 -
download
1
Transcript of Introduction to Computer Science Stacks and Queues More Recursion and Pointers Unit 19.
Introduction to Computer Science
• Stacks and Queues
• More Recursion and Pointers
Unit 19Unit 19
19- 2
Stacks
• Stacks: new records added to one end and taken off from the same end; last-in, first-out, LIFO
• We can talk about the abstract data structure “stack” without knowing anything about the “values” that it stores:–Push means “save a value”
–Pop means “get the value that was saved most recently”
–Empty means “there are no values to pop”
19- 3
More Stack Operations
• There are some more primitive operations that might be relevant, depending on how we implement the stack:–Remove means “remove all of the stack’s
contents” returning space to memory (relevant for pointer-based stack)
–Full means “there’s no more room to push values” (relevant for an array-based stack)
19- 4
The Stack Name Layer (no commitment as to implementation)
Objects of class DataNode, of stored items, doesn’t have to be defined yet; these are methods of the Stack class:
boolean empty ( ) {…}
// true if there are no pushed values
void push (DataNode newItem) {…}
// Add newItem to stack
DataNode pop ( ) {…}
// Remove the top item from the stack and return it
void remove ( ) {…}
// Dispose of any current stack contents
19- 5
Question
• pop, push, etc. are primitives; how would we define, in terms of the primitives, top( ), which gets a copy of the top value of a stack but doesn’t remove that value?
We pop the top value, copy it, then push it back onto the stack (assume copyData( ) copies one DataNode object to another):
if ( !stack.empty( ) ) {item = stack.pop( ); // Pop the top value…newItem.copyData(item); // …copy it…stack.push(item); //then push it back onto the
stack}
19- 6
The Implementation Layer
• Now we have to implement it• Pointers are often used to build stacks
because (as I said) a linked list can get longer, while arrays are limited to their defined size
_valueThird
<<<<
Data
_last
_valueFirst
null
_valueSecond
<<<<_last _last
head
A Stack
push
pop
empty
remove
19- 7
Two-class Encapsulation of a Linked List Stack (here's the node class, for inside)
class DataNode {private int _value;private DataNode _last;
public DataNode (int val, DataNode node) {_value = val;_last = node;
}
public DataNode getLast( ) { return _last; }
public void setLast(DataNode node) { _last = node; }
public int getValue( ) { return _value; }
public void setValue(int val) { _value = val; } }
19- 8
The Actual Stack Class
class DataStack {private DataNode _head;
DataStack ( ) { _head = null; }
boolean empty ( ) {…}
void push (DataNode newItem) {…}
DataNode pop ( ) {…}
void remove ( ) {…}}
19- 9
The Use of a Backwards Pointer
• We defined the DataNode to have a “last” pointer (rather than a “next” one); the pointer will always point to an existing past node:
_valueThird
<<<<
Data
bottom of the stack
_last
_valueFirst
null
_valueSecond
<<<<
top of the stack
_last _lasthead
A Stack
push
pop
empty
remove
19- 10
empty( )(for class DataStack)
class DataStack {private DataNode _head;
DataStack ( ) { _head = null; }
boolean empty ( ) {return (head == null); }
void push (DataNode newItem) {…}
DataNode pop ( ) {…}
void remove ( ) {…}}
19- 11
push( )(for class DataStack)
class DataStack {private DataNode _head;DataStack ( ) { _head = null; }boolean empty ( ) {…}void push (DataNode newItem) {
newItem.setLast(_head);_head = newItem;
}DataNode pop ( ) {…}void remove ( ) {…}
}
19- 12
pop( )(for class DataStack)
class DataStack {private DataNode _head;…DataNode pop ( ) {
if ( empty( ) )return null;
else {DataNode temp = _head;_head = _head.getLast( );return temp;
}}…
}
19- 13
remove( )(for class DataStack)
class DataStack {private DataNode _head;
DataStack ( ) { _head = null; }
boolean empty ( ) {…}
void push (DataNode newItem) {…}
DataNode pop ( ) {…}
void remove ( ) { _head = null; }}
19- 14
Stack Applications
• One use of a stack is to reverse input:DataStack stack = new DataStack( ); //create the stackwhile ( not eof ) {
read from user, create a DataNode newItem;stack.push(newItem);
} // end the stacking loopwhile ( !stack.empty( ) ) { // print the stack
print stack.pop( );} // end the unstacking loop
19- 15
Another Stack Application
• Another use is to evaluate postfix or Reverse Polish Notation expressions:
Infix Postfix
(A+ B) * C A B + C *
(A – B) / (C * D) A B – C D * /
( (A * (B / C) ) – (D * E) ) A B C / * D E * –
Arguments are pushed onto a stack, operators act on the top two popped stack items and push the result onto the stack.
19- 16
The Pseudocode for Postfix Evaluation (no error checking)
while ( not eof ) {read the next data;if data was an operand {
create DataNode newItem with data in it;stack.push(newItem);
}else it’s an operator, so {
termNode1 = stack.pop( );termNode2 = stack.pop( );carry out the appropriate operation;create DataNode theResult;stack.push(theResult);
}} // end of the while loopremainingTerm = stack.pop( );print data from remainingTerm;
19- 17
Other Uses of Stacks
• Language Translation (from one computer language level to another)
• Method calls
• Recursive method calls (delayed evaluation)
• Delimiter matching
• Maintenance of free storage space (unused array indexes); order is irrelevant, a stack just holds the information conveniently
19- 18
Easy Question
• Use our primitives to reverse the order of a stack’s contents, placing the items into another stack
19- 19
Easy Question
• Use our primitives to reverse the order of originalStack’s contents, placing the items into another stack
DataStack copyStack = new DataStack( );while ( !originalStack.empty( ) ) {
copyStack.push(originalStack.pop( ));}// Postcondition: originalStack is empty
19- 20
Queues
• Queues: new records added to one end and taken off from the other end; first-in, first-out, FIFO
• We can talk about the abstract data structure “queue” without knowing anything about the “values” that it stores:
–Enqueue means “save a value”
–Retrieve means “get the oldest value that remains”
–Empty means “there are no values in the queue”
19- 21
More Queue Operations
• There are some more primitive operations that might be relevant, depending on how we implement the queue:–Remove means “remove all of the queue’s
contents” returning space to memory (relevant for pointer-based queue)
–FullQ means “the queue can’t hold any more values” (more relevant for an array-based queue)
19- 22
The Queue Name Layer (no commitment as to implementation)
Objects of class DataNode, of stored items, doesn’t have to be defined yet; these are methods of the Queue class:
boolean empty ( ) {…}
// true if the queue is empty
void enqueue (DataNode newItem) {…}
// Add newItem to end of the queue
DataNode retrieve ( ) {…}
// Remove oldest item from the queue and return it
void remove ( ) {…}
// Dispose of any current queue contents
19- 23
Question
• enqueue, retrieve, etc. are primitives; how would we define, in terms of the primitives, “nextItem”, which gets a copy of the front value of the queue but doesn’t remove that value?
19- 24
Question
• enqueue, retrieve, etc. are primitives; how would we define, in terms of the primitives, “nextItem”, which gets a copy of the front value of the queue but doesn’t remove that value?
Not so easy. When an item is removed it can only be added back onto the end of the queue. Just to get the value of the first item, we’d need to copy the entire queue.If we really want something like “nextItem”, it should be implemented as a primitive method of the Queue class.
19- 25
The Implementation Layer
• Now we have to implement it• Pointers are often used to build
queues because (as I keep saying) a linked list can get longer, while arrays are limited to their defined size
_value
?
Data
_last
_valueFirst
?
_valueSecond
?_last _last
front
A Queue
enqueue
retrieve
empty
remove
rearThird
? ?
19- 26
Which of these is better for us?
_valueOldest
<<<<
Data
rear
_last
_valueNewest
null
_valueSecond
<<<<
front
_last _last front of the line
_valueOldest
Data
>>>>
rear
_Last
_valueNewest
null
_valueSecond
>>>>
front
_Last _Last front of the line>>>>
19- 27
First Alternative
•Adding a node to the rear is easy;
•Removing a node from the front is hard.
_valueOldest
Data
>>>>
rear
_Last
_valueNewest
null
_valueSecond
>>>>
front
_Last _Last front of the line>>>>
19- 28
Second Alternative
•Adding a node to the rear is easy;
•Removing a node from the front is also easy!
_valueOldest
<<<<
Data
rear
_last
_valueNewest
null
_valueSecond
<<<<
front
_last _last front of the line
19- 29
Two-class Encapsulation of a Linked List Queue (here's the node class, for inside)
class DataNode {private int _value;private DataNode _last;
public DataNode (int val, DataNode node) {_value = val;_last = node;
}
public DataNode getLast( ) { return _last; }
public void setLast(DataNode node) { _last = node; }
public int getValue( ) { return _value; }
public void setValue(int val) { _value = val; } }
19- 30
The Actual Queue Class
class DataQueue {private DataNode _rear, _front;DataQueue ( ) {
_rear = new DataNode(0, null);_front = _rear;
}boolean empty ( ) {…}void enqueue (DataNode newItem) {…}DataNode retrieve ( ) {…}void remove ( ) {…}
}
19- 31
empty( )(for class DataQueue)
class DataQueue {private DataNode _rear, _front;DataQueue ( ) {…}
boolean empty() {return( _front == _rear);}
void enqueue (DataNode newItem) {…}
DataNode retrieve ( ) {…}
void remove ( ) {…}}
19- 32
enqueue( )(for class DataQueue)
class DataQueue {private DataNode _rear, _front;DataQueue ( ) {…}boolean empty ( ) {…}void enqueue (DataNode newItem) {
DataNode temp = new DataNode(0, null);_rear.setValue(newItem.getValue( ));_rear.setLast(temp);_rear = temp; //"advance" rear pointer
} // copying newItem's value into queueDataNode retrieve ( ) {…}void remove ( ) {…}
}
19- 33
retrieve( )(for class DataQueue)
class DataQueue {private DataNode _rear, _front;…DataNode retrieve ( ) {
if ( empty( ) )return null;
else {DataNode temp = _front;_front = _front.getLast( );return temp;
}}
…}
19- 34
remove( )(for class DataQueue)
class DataQueue {private DataNode _rear, _front;DataQueue ( ) {
_rear = new DataNode(0, null);_front = _rear;
}boolean empty ( ) {…}void enqueue (DataNode newItem) {…}DataNode retrieve ( ) {…}void remove ( ) { _front = _rear; }
}
19- 35
Queue Applications
• Queues are found whenever supply and demand (servers and clients) cannot be assured to stay in lockstep
• Data may come in too quickly, or in irregular spurts, and have to be held for processing later
• Example: Time-shared computer system; tasks (like users’ programs or print jobs) are put in a queue to be processed each in turn
19- 36
Example: Grab First Lettersof Succession of Words
• Just a white shark
• Space, time, and relativity with a ridiculous script
• A nauseating nitwit ineffably embalmed
Write a program that grabs the first letter of each word, prints each word as it comes in, then prints the acronyms: Jaws, Starwars, Annie
19- 37
Code Segment (assume DataNode _value is of type char)
DataNode newItem; newWord = true; ch = sinp.readChar( );while ( !sinp.eoln( ) ) {newItem = new DataNode(ch, null);if (newWord) {// true when first letter of a new word is read
queue.enqueue(newItem);newWord = false;
}else newWord = (ch == ‘ ’); //Assume 1 space betweenwordsch = sinp.readChar( );
} // while// Postcond.: No more input. Queue holds first letters.while ( !queue.empty( ) ) { // until the queue is emptynewItem = queue.retrieve( );System.out.print(newItem.getValue( ));
} // while
19- 38
Another Example:Palindromic Sentences
So patient a doctor to doctor a patient so.
while ( not eof ) { // stack and queue the wordsread a word, ignoring capitalization and punctuation;push the word onto a stack;enqueue the word in a queue;
}do { // compare the stored values---assume
non-empty listspop a word from the stack;retrieve a word from the queue;
} while ( they match && not empty( ) stack or queue );
decide why we left the loop, and report the results
19- 39
A Few Definition Exercises
Define a class suited to building a text editor. Each line object must hold up to 80 characters, but the total number of lines shouldn’t be restricted. Common editing operations, like deleting and moving lines, and printing subsections of the text, should be convenient to perform.
19- 40
A Solution
class Line {Line _next;StringBuffer _data;
…}
Line first, current, last, other auxiliary pointers;
first
current
_next _datao f t
_next _datap e r f
…
…
…
_next _datam o v i
19- 41
Another Problem
A manufacturer has labeled product lines with a letter, followed by the product number (e.g., A1, C7, M4). Define a class definition that allows quick access to any product line, without limiting the number of products in each line. Product information is stored in an object of type ProductData.
19- 42
A Solution—An Array with Buckets
class Node {Node _next;ProductData _data;
…}
Node[ ] info = new Node[26];Node current;
_next
_data
null
null
_next
_datanull
null0
1>>>>
24
25>>>>
_next
_data
null
null
…
array"info";
one cellfor eachletter ofalphabet
Objects oftype Node
19- 43
Another Problem
A company has records describing its employees, but different groups in the company want to keep the records in different orders (e.g., length of employment, frequency of absence, teudat zehut number). If duplicate sets cannot be kept, define a data type that allows each division reasonable access to the database.
19- 44
A Solution
class EmployeeInfo {whatever the employee records contain
…}
class BaseNode {BaseNode _next;EmployeeInfo _employee;
…}
BaseNode mainList, watchList, healthList, payrollList, current;
19- 45
One set of data,two very different lists
Data
>>>>
>>>>
>>>>
>>null
Data
DataData
DataData
DataData
mainList
<<<<
<<<<
<<<<
<<null
healthList
BaseNodes BaseNodesEmployeeInfo
19- 46
Don’t Search Past the End of a Linked List
• When searching an input stream, check for not eof
• When searching an array, watch for the last component
• When searching a linked list, look for a null-valued pointer
// A loop with a potential bug–sought might not be therecurrent = head; // Start current at head of the listwhile ( current.getValue( ) != sought) {
current = current.getNext( );}
19- 47
Better, but Still NotNecessarily Correct
//A possibly correct, but probably buggy, version of the// same loopcurrent = head; // Start current at head of the list// What precondition has to exist here, before the loop?
while ( (current.getValue( ) != sought) && (current.getNext( ) != null) ) {current = current.getNext( );
}//Postcondition: if sought is there, current addresses itif ( current.getValue( ) == sought )
System.out.println("Found the value.");else System.out.println("Did not find the value.");
19- 48
Another Version, Maybe Safer
// A different version of the same loopsearching = true; //Use an auxiliary boolean variablecurrent = head; //Start current at head of the list// Now, current might be nullwhile ( (current != null) && searching ) {
if (current.getValue( ) == sought)searching = false; // since we’ve found it
else current = current.getNext( );}// Postcond.: if current isn’t null, it addresses soughtif ( current != null )
System.out.println("Found the value.");else System.out.println("Did not find the value.");
19- 49
Trees
• A tree is another data structure that can be built using pointers
• It can be defined recursively: a tree is a node that’s linked to one or more trees, or to nothing.
• Branches lead to finer branches, but never lead back to the root. Subtrees must be distinct (no two trees share the same node).
19- 50
Nobody Nodes theTrouble I Nodes
• The root of a tree is the first (top) node
• The nodes an element points to are its children; it is the parent
• A node with no children is called a leafroot
parent
child
leaf
19- 51
Binary Tree
• A tree whose nodes have at most two children is called a binary tree.
class BinaryNode {ProblemData _data;BinaryNode _left, _right;
}
BinaryNode current;
19- 52
Recursive Tree Searching(one method)
• If current’s left child isn’t null, point current at the left child and search the (sub)tree.
• If current’s right child isn’t null, point current at the right child and search the (sub)tree.
• Print the value stored in the current node.Using recursion allows backtracking without backward pointers.
Goal: Print every node’s stored value.
Stacking Plan: Visit the left subtree.
Visit the right subtree.
Bound: Reaching a leaf, or node that has no subtrees.
Unstacking Plan: Print the current node’s stored value.
19- 53
The Java Code (acting at the BinaryNode level)
void inspectTree (BinaryNode current) {// Visits every node of a non-empty binary tree
if ( current.left != null )inspectTree(current.left);
if ( current.right != null )inspectTree(current.right);
printProblemData(current.data);} // inspectTree
19- 54
Using inspectTree — Postorder
+
/
+
*
–
inspectTree searches the tree in postorder:
A B C + / D E F – * +
Postfix notation and reverse Polish notation are other names for this (as we’ve seen).
A
B C
D
E F
19- 55
Preorder Search
First variation: Preorder search:printProblemData(current.data);if (current.left != null) inspectTree(current.left);if (current.right != null) inspectTree(current.right);
Search then goes as: + / A + B C * D – E F
+
/
+
*
–A
B C
D
E F
19- 56
Inorder Search
Second variation: Inorder search:if (current.left != null) inspectTree(current.left);printProblemData(current.data);if (current.right != null) inspectTree(current.right);
Search then goes as: A / B + C + D * E – F
+
/
+
*
–A
B C
D
E F
19- 57
Programming Binary Trees
Let’s use a binary tree to represent Morse code:
E
A
T
MI
F
W
N
G OS U D K
H V L P J B X Z QC Y
R
19- 58
The Type Definition
class CodeNode {char _letter;CodeNode _dot, _dash;
}
CodeNode root;
E
A
T
MI
F
W
N
G OS U D K
H V L P J B X Z QC Y
R
19- 59
How We Can Use It
void decode(CodeNode root) { // Decodes Morse code; each letter must be followed by a blank
CodeNode current;char inputCharacter;current = root;SimpleInput sinp = new SimpleInput(System.in);inputCharacter = sinp.readChar( );while ( !sinp.eof( ) ) {
if ( inputCharacter == '.')current = current._dot;
else if ( inputCharacter == ‘-‘ )current=current._dash;
else if ( inputCharacter == ‘ ’ ) {Sytem.out.print(current._letter);current = root;
}inputCharacter = sinp.readChar( );
} // whileSystem.out.println( );
} // decode
19- 60
Relationship Between Data and Name is Stored (Implicitly) in the Tree
E
A
T
MI
F
W
N
G OS U D K
H V L P J B X Z QC Y
R
.--. .- -.-. -.-PACK
Other yes/no questions can similarly lead to an answer, stored implicitly in some binary tree.
19- 61
Trees of Words
gregor
awoke samsa
a discover
been giant
cockroach
one to
transformedmorning only
he
had intoWhat is going on?
19- 62
Storing Items in Alphabetical Order
gregor samsa awoke one morning only to discover he had been transformed into a giant cockroach
gregor
awoke samsa
a discover
been giant
cockroach
one to
transformedmorning only
he
had into
19- 63
To Build an Alphabetically Ordered Tree (recursive specification)
• If the current node is null, store the new word there and stop
• If the new word precedes the word in the current node, point to the left child and build an alphabetically ordered tree.
• If the new word follows the word in the current node, point to the right child and build an alphabetically ordered tree.
• If the new word is the same as the word in the current node, stop.
class WordStore {private StringBuffer _word;WordStore _before, _after;public WordStore (StringBuffer newWord,
WordStore bef, aft) { _word = newWord; _before = bef; _after = aft; }public StringBuffer getWord( ) { return _word; }public WordStore getAfter( ) { return _after; }public WordStore getBefore( ) { return _before; }
}
void addAWord (WordStore current, StringBuffer newWord) {//Adds string newWord to alphabetically ordered binary tree
if ( current == null )current = new WordStore(newWord, null, null);
else if ( newWord comes before current.getWord( ) )addAWord the newWord to current.before;
else if ( current.getWord( ) comes before newWord )addAWord the newWord to current.after;
// else the word is a duplicate, do nothing} // addAWord
addAWord is an end recursion; the stackdoesn’t store values or pending statements
19- 65
Binary Search Tree(from the targilim)
ab
d c
e f
root
Definitions:* The root of a tree is the only node which doesn’t have a father node.* A leaf is a node which doesn’t have any son nodes. * A binary tree is a tree whose nodes have at most two son nodes.* A binary search tree is: 1. A binary tree; 2. All the nodes which are descendents of the right son of a specific node including the right son itself have values greater or equal to the value in the node; 3. All the nodes which are descendents of the left son of a specific node including the left son itself have values less than the value in the node.
In the example: b,c,d,e,f > a b > d,e,f c > b leaves
19- 66
Binary Tree Nodepublic class Node { protected Comparable _data; protected Node _left,_right;
public Node(Comparable data) { _data = data; _left = _right = null; }
public void addRight(Node node) { _right = node; }
public void addLeft(Node node) { _left = node; }
public Node getRight() { return _right; }
public Node getLeft() { return _left; }
public Comparable getData() { return _data; }}
19- 67
Comparable interface(used as the data object)
/** * This interface defines the way two objects should be compared. */public interface Comparable { /** * This function does the comparison between two objects. * @return -1 second object is greater than this one.<br> * 1 second object is smaller than this one.<br> * 0 second object is equal to this one. * @param right the object to which we compare. */ public int compare(Comparable right);}
19- 68
Example of class implementing the Comparable interface
/** * This is an example class that implements the Comparable interface. * I would have extended Integer but Integer is declared final , so my class * is just a wrapper around the Integer class. */public class MyInteger implements Comparable { private Integer _data;
public MyInteger(int data) { _data = new Integer(data); }
public MyInteger(String data) throws NumberFormatException { _data = new Integer(data); }
public int intValue() { return _data.intValue(); }
continued…
19- 69
Example of class implementing the Comparable interface (continued)
public String toString() { return _data.toString(); }
public int compare(Comparable right) { int leftVal = _data.intValue(); int rightVal = ((MyInteger)right).intValue();
if(rightVal>leftVal) return -1; else if(rightVal<leftVal) return 1; return 0; }}
19- 70
Binary Search Tree Implementation
public class BinTree { protected Node _root; protected int _size; public BinTree() { _size = 0; }
public void add(Comparable data) { add(data,_root,_root); _size++; }
19- 71
Implementation (continued);Recursive add method
private void add(Comparable data, Node son, Node father) { //stop the recursion, we have reached a leaf if (son == null) { if (father == null) //this leaf is the root
_root = new Node(data); else { //just a regular leaf
if ((father.getData()).compare(data) == 1)father.addLeft(new Node(data));
else father.addRight(new Node(data)); } } else { if ((son.getData()).compare(data) == 1)
add(data, son.getLeft(), son); else add(data, son.getRight(), son); } }
19- 72
Implementation (continued)
/** * Recursive method: * Traversal of the tree (inorder). */ private void traverse(Node node) { if (node != null) { traverse(node.getLeft()); System.out.println(node.getData()); traverse(node.getRight()); } }
19- 73
Implementation (continued)
What's the result of this main:
public static void main(String args[]) { BinTree myTree = new BinTree(); myTree.add(new MyInteger(7)); myTree.add(new MyInteger(1)); myTree.add(new MyInteger(3)); myTree.add(new MyInteger(2)); myTree.add(new MyInteger(4)); myTree.add(new MyInteger(5)); myTree.add(new MyInteger(8)); myTree.add(new MyInteger(6)); myTree.traverse(myTree._root); }
The result : 1 2 : 8The numbers are sorted!!!
19- 74
Now We Want to Print the Contents of the Tree in Alphabetical Order—Easy
void inOrder (WordStore currentWord) {// Prints nodes of a non-null alphabetically// ordered binary tree in order
if ( current.getBefore( ) != null )inOrder(current.getBefore( ) );
Sytem.out.print(current.getWord( ));if ( current.getAfter( ) != null )
inOrder(current.getAfter( ) );} // inOrder
19- 75
InOrder Traversal
gregor
awoke samsa
a discover
been giant
cockroach
one to
transformedmorning only
he
had into
19- 76
InOrder Traversal
a awoke been cockroach discover giant gregor had he into morning one only samsa to transformed
gregor
awoke samsa
a discover
been giant
cockroach
one to
transformedmorning only
he
had into
19- 77
More AddAWord
What structures are built by AddAWord when presented with:
a) a big cat did everything
19- 78
More AddAWord
What structures are built by AddAWord when presented with:
a) a big cat did everything
a
big
cat
did
everything
19- 79
More AddAWord
What structures are built by AddAWord when presented with:
a) a big cat did everything
b) zesty young xylophones wed violins
a
big
cat
did
everything
19- 80
More AddAWord
What structures are built by AddAWord when presented with:
a) a big cat did everything
b) zesty young xylophones wed violins
zesty
young
xylophones
wed
violins
a
big
cat
did
everything
19- 81
This is Not the End
• We have covered a lot of subjects in Computer Science (e.g., sorting, searching, loops, recursion, simple data structures like arrays and linked lists)
• We have covered a lot in Java, but it's only the beginning
• You'll be carrying this forward in the next semester in Data Structures
19- 82
The End, for Now
Good Luck on the Exam!