Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Algorithms Stacks and queues R OBERT S EDGEWICK | K EVIN W AYNE Fundamental data types. ・Value: collection of objects. ・Operations: insert, remove, iterate, test if empty. ・Intent is clear when we insert. ・Which item do we remove? 1.3 B AGS, QUEUES, AND STACKS ‣ stacks stack push ‣ resizing arrays Algorithms F O U R T H E D I T I O N R OBERT S EDGEWICK | K EVIN W AYNE http://algs4.cs.princeton.edu pop ‣ queues queue dequeue enqueue ‣ generics ‣ iterators ‣ applications Stack. Examine the item most recently added. LIFO = "last in first out" Queue. Examine the item least recently added. FIFO = "first in first out" 2 Client, implementation, interface Separate interface and implementation. Ex: stack, queue, bag, priority queue, symbol table, union-find, .… Benefits. ・Client can't know details of implementation 1.3 B AGS, QUEUES, AND S TACKS ⇒ client has many implementation from which to choose. ・Implementation can't know details of client needs ‣ stacks ⇒ many clients can re-use the same implementation. ・ ・Performance: ‣ resizing arrays Design: creates modular, reusable libraries. Algorithms use optimized implementation where it matters. R OBERT S EDGEWICK | K EVIN W AYNE Client: program using operations defined in interface. http://algs4.cs.princeton.edu Implementation: actual code implementing operations. Interface: description of data type, basic operations. 3 ‣ queues ‣ generics ‣ iterators ‣ applications Stack API How to implement a stack with a linked list? Warmup API. Stack of strings data type. A. push Can't be done efficiently with a singly-linked list. pop public class StackOfStrings StackOfStrings() top of stack create an empty stack B. void push(String item) it was the best of null best the was it null insert a new string onto stack remove and return the string most recently added String pop() is the stack empty? boolean isEmpty() number of strings on the stack int size() top of stack C. of Warmup client. Reverse sequence of strings from standard input. 5 Stack: linked-list implementation 6 Stack pop: linked-list implementation ・Maintain pointer first to first node in a singly-linked list. ・Push new item before first. ・Pop item from first. save item to return String item = first.item; delete first node inner class private class Node { String item; Node next; } first = first.next; first or best the was it null to null first or top of stack of be be to null return saved item return item; first Removing the first node in a linked list 7 8 Stack push: linked-list implementation Stack: linked-list implementation in Java public class LinkedStackOfStrings { private Node first = null; save a link to the list Node oldfirst = first; oldfirst first inner class or be private class Node { String item; Node next; } to null create a new node for the beginning public boolean isEmpty() { return first == null; first = new Node(); private class Node { oldfirst String item; Node next; first or be } to null first.item = "not"; first.next = oldfirst; not or be } public void push(String item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; } set the instance variables in the new node first private inner class (access modifiers for instance variables don't matter) public String pop() { String item = first.item; first = first.next; return item; } to null } Inserting a new node at the beginning of a linked list 9 10 Stack: linked-list implementation performance How to implement a fixed-capacity stack with an array? Proposition. Every operation takes constant time in the worst case. A. Can't be done efficiently with an array. Proposition. A stack with N items uses ~ 40 N bytes. node object (inner class) public class Node { String item; inner class Node next; ... private class Node } { String item; Node next; } top of stack 40 bytes object overhead 16 bytes (object overhead) extra overhead B. 8 bytes (inner class extra overhead) it was the best of times null null null null 0 1 2 3 4 5 6 7 8 9 times of best the was it null null null null 0 1 2 3 4 5 6 7 8 9 8 bytes (reference to String) item references next 8 bytes (reference to Node) 40 bytes per stack node top of stack Remark. This accounts for the memory for the stack C. (but not the memory for strings themselves, which the client owns). 11 12 Fixed-capacity stack: array implementation Fixed-capacity stack: array implementation ・Use array s[] to store N items on stack. ・push(): add new item at s[N]. ・pop(): remove item from s[N-1]. public class FixedCapacityStackOfStrings { private String[] s; private int N = 0; a cheat (stay tuned) top of stack public FixedCapacityStackOfStrings(int capacity) { s[] it was the best of times null null null null 0 1 2 3 4 5 6 7 8 9 N s = new String[capacity]; } public boolean isEmpty() { return N == 0; } capacity = 10 public void push(String item) { s[N++] = item; } public String pop() { return s[--N]; } use to index into array; then increment N } decrement N; then use to index into array Defect. Stack overflows when N exceeds capacity. [stay tuned] 13 14 Stack considerations Overflow and underflow. ・Underflow: throw exception if pop from an empty stack. ・Overflow: use resizing array for array implementation. [stay tuned] Null items. We allow null items to be inserted. 1.3 B AGS, QUEUES, AND S TACKS Loitering. Holding a reference to an object when it is no longer needed. ‣ stacks ‣ resizing arrays public String pop() { return s[--N]; } loitering Algorithms public String pop() { String item = s[--N]; s[N] = null; return item; } R OBERT S EDGEWICK | K EVIN W AYNE http://algs4.cs.princeton.edu this version avoids "loitering": garbage collector can reclaim memory for an object only if no outstanding references 15 ‣ queues ‣ generics ‣ iterators ‣ applications Stack: resizing-array implementation Stack: resizing-array implementation Problem. Requiring client to provide capacity does not implement API! Q. How to grow array? Q. How to grow and shrink array? A. If array is full, create a new array of twice the size, and copy items. public ResizingArrayStackOfStrings() { s = new String[1]; } First try. ・push(): increase size of array s[] by 1. ・pop(): decrease size of array s[] by 1. Too expensive. public void push(String item) { if (N == s.length) resize(2 * s.length); s[N++] = item; } infeasible for large N ・Need to copy all items to a new array, for each operation. ・Array accesses to insert first N items = N + (2 + 4 + … + 2(N – 1)) ~ 1 array access per push "repeated doubling" private void resize(int capacity) { String[] copy = new String[capacity]; for (int i = 0; i < N; i++) copy[i] = s[i]; s = copy; } N 2. 2(k–1) array accesses to expand to size k (ignoring cost to create new array) Array accesses to insert first N = 2i items. N + (2 + 4 + 8 + … + N) ~ 3N. Challenge. Ensure that array resizing happens infrequently. 1 array access per push 17 Stack: resizing-array implementation Stack: resizing-array implementation Q. How to shrink array? Q. How to shrink array? First try. Efficient solution. ・push(): ・pop(): ・push(): ・pop(): double size of array s[] when array is full. halve size of array s[] when array is one-half full. Too expensive in worst case. ・ ・Each operation takes time proportional to N. to be or not N = 4 to be or not N = 5 to be or not N = 4 to be or not 18 double size of array s[] when array is full. halve size of array s[] when array is one-quarter full. public String pop() { Consider push-pop-push-pop-… sequence when array is full. N = 5 k array accesses to double to size k (ignoring cost to create new array) String item = s[--N]; s[N] = null; if (N > 0 && N == s.length/4) resize(s.length/2); return item; to null null null to null null null } Invariant. Array is between 25% and 100% full. 19 20 Stack resizing-array implementation: performance Stack resizing-array implementation: memory usage Amortized analysis. Starting from an empty data structure, average Proposition. Uses between ~ 8 N and ~ 32 N bytes to represent a stack running time per operation over a worst-case sequence of operations. with N items. Proposition. Starting from an empty stack, any sequence of M push and ・~ 8 N when full. ・~ 32 N when one-quarter full. pop operations takes time proportional to M. best worst amortized construct 1 1 1 push 1 N 1 pop 1 N 1 1 1 1 size public class ResizingArrayStackOfStrings { 8 bytes × array size private String[] s; private int N = 0; … } doubling and halving operations order of growth of running time for resizing stack with N items Remark. This accounts for the memory for the stack (but not the memory for strings themselves, which the client owns). 21 22 Stack implementations: resizing array vs. linked list Tradeoffs. Can implement a stack with either resizing array or linked list; save a link to the list client can use Nodeinterchangeably. oldfirst = first; Which one is better? oldfirst Linked-list implementation. first or be ・ extra time and space to deal with the links. ・Usescreate a new node for the beginning 1.3 B AGS, QUEUES, AND S TACKS to Every operation takes constant time in the worst case. null ‣ stacks first = new Node(); Resizing-array implementation. oldfirst ・ ・Less wasted space. ‣ resizing arrays Every operation takes constant amortized time. first or be Algorithms to null set the instance variables in the new node N = 4 to= "not"; be or first.item not null null null R OBERT S EDGEWICK | K EVIN W AYNE null http://algs4.cs.princeton.edu first.next = oldfirst; first not or be to null Inserting a new node at the beginning of a linked list 23 ‣ queues ‣ generics ‣ iterators ‣ applications Queue API How to implement a queue with a linked list? enqueue A. Can't be done efficiently with a singly-linked list. public class QueueOfStrings QueueOfStrings() void enqueue(String item) create an empty queue back of queue B. insert a new string onto queue times front of queue of best the was it null remove and return the string least recently added String dequeue() is the queue empty? boolean isEmpty() number of strings on the queue int size() front of queue back of queue dequeue C. it was the best of times 25 Queue: linked-list implementation null 26 Queue dequeue: linked-list implementation ・Maintain one pointer first to first node in a singly-linked list. ・Maintain another pointer last to last node. ・Dequeue from first. ・Enqueue after last. save item to return String item = first.item; delete first node first = first.next; inner class last first private class Node { String item; Node next; } front of queue it first to be or null first last to be or null back of queue was the best of times return saved item null return item; last Removing the first node in a linked list Remark. Identical code to linked-list stack pop(). 27 28 Queue enqueue: linked-list implementation Queue: linked-list implementation in Java save a link to the last node public class LinkedQueueOfStrings { private Node first, last; Node oldlast = last; oldlast private class Node { /* same as in LinkedStackOfStrings */ last first to be or null public boolean isEmpty() { return first == null; create a new node for the end inner class oldlast first to be last or null } public void enqueue(String item) { Node oldlast = last; last = new Node(); last.item = item; last.next = null; if (isEmpty()) first = last; else oldlast.next = last; } last = new Node(); last.item = "not"; private class Node { String item; Node next; } } not null special cases for empty queue link the new node to the end of the list public String dequeue() { String item = first.item; first = first.next; if (isEmpty()) last = null; return item; } oldlast.next = last; oldlast to first be last or not null } Inserting a new node at the end of a linked list 29 30 How to implement a fixed-capacity queue with an array? Queue: resizing-array implementation A. ・Use array q[] to store items in queue. ・enqueue(): add new item at q[tail]. ・dequeue(): remove item from q[head]. ・Update head and tail modulo the capacity. ・Add resizing array. Can't be done efficiently with an array. front of queue B. back of queue it was the best of times null null null null 0 1 2 3 4 5 6 7 8 9 front of queue q[] back of queue null null the best of times null null null null 0 1 2 3 4 5 6 7 8 9 front of queue head C. back of queue times of best the was it null null null null 0 1 2 3 4 5 6 7 8 9 tail capacity = 10 Q. How to resize? 31 32 Parameterized stack We implemented: StackOfStrings. We also want: StackOfURLs, StackOfInts, StackOfVans, …. Attempt 1. Implement a separate stack class for each type. ・Rewriting code is tedious and error-prone. ・Maintaining cut-and-pasted code is tedious and error-prone. 1.3 B AGS, QUEUES, AND STACKS ‣ stacks ‣ resizing arrays ‣ queues Algorithms @#$*! most reasonable approach until Java 1.5. ‣ generics ‣ iterators R OBERT S EDGEWICK | K EVIN W AYNE ‣ applications http://algs4.cs.princeton.edu 34 Parameterized stack Parameterized stack We implemented: StackOfStrings. We implemented: StackOfStrings. We also want: StackOfURLs, StackOfInts, StackOfVans, …. We also want: StackOfURLs, StackOfInts, StackOfVans, …. Attempt 2. Implement a stack with items of type Object. Attempt 3. Java generics. ・Casting is required in client. ・Casting is error-prone: run-time error if types mismatch. ・Avoid casting in client. ・Discover type mismatch errors at compile-time instead of run-time. type parameter StackOfObjects s = new StackOfObjects(); Stack<Apple> s = new Stack<Apple>(); Apple a = new Apple(); Orange b = new Orange(); Apple a = new Apple(); Orange b = new Orange(); s.push(a); s.push(b); s.push(a); s.push(b); a = (Apple) (s.pop()); a = s.pop(); compile-time error run-time error Guiding principles. Welcome compile-time errors; avoid run-time errors. 35 36 Generic stack: linked-list implementation public class LinkedStackOfStrings { private Node first = null; the way it should be public class Stack<Item> { private Node first = null; private class Node { String item; Node next; } public boolean isEmpty() { return first == null; Generic stack: array implementation private class Node { Item item; Node next; } generic type name public boolean isEmpty() { return first == null; } public class FixedCapacityStack<Item> { private Item[] s; private int N = 0; public ..StackOfStrings(int capacity) { s = new String[capacity]; } public FixedCapacityStack(int capacity) { s = new Item[capacity]; } public boolean isEmpty() { return N == 0; } public boolean isEmpty() { return N == 0; } public void push(String item) { s[N++] = item; } public void push(Item item) { s[N++] = item; } public String pop() { return s[--N]; } public Item pop() { return s[--N]; } public void push(String item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; } public void push(Item item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; } public String pop() { String item = first.item; first = first.next; return item; } public Item pop() { Item item = first.item; first = first.next; return item; } } public class FixedCapacityStackOfStrings { private String[] s; private int N = 0; } } } } @#$*! generic array creation not allowed in Java 37 Generic stack: array implementation 38 Unchecked cast the way it is public class FixedCapacityStackOfStrings { private String[] s; private int N = 0; public class FixedCapacityStack<Item> { private Item[] s; private int N = 0; public ..StackOfStrings(int capacity) { s = new String[capacity]; } public FixedCapacityStack(int capacity) { s = (Item[]) new Object[capacity]; } public boolean isEmpty() { return N == 0; } public boolean isEmpty() { return N == 0; } public void push(String item) { s[N++] = item; } public void push(Item item) { s[N++] = item; } public String pop() { return s[--N]; } public Item pop() { return s[--N]; } % javac FixedCapacityStack.java Note: FixedCapacityStack.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. % javac -Xlint:unchecked FixedCapacityStack.java FixedCapacityStack.java:26: warning: [unchecked] unchecked cast found : java.lang.Object[] required: Item[] a = (Item[]) new Object[capacity]; ^ 1 warning } } Q. Why does Java make me cast (or use reflection)? Short answer. Backward compatibility. Long answer. Need to learn about type erasure and covariant arrays. the ugly cast 39 40 Generic data types: autoboxing Q. What to do about primitive types? Wrapper type. ・Each primitive type has a wrapper object type. ・Ex: Integer is wrapper type for int. 1.3 B AGS, QUEUES, AND S TACKS ‣ stacks Autoboxing. Automatic cast between a primitive type and its wrapper. ‣ resizing arrays Algorithms Stack<Integer> s = new Stack<Integer>(); s.push(17); int a = s.pop(); // s.push(Integer.valueOf(17)); // int a = s.pop().intValue(); R OBERT S EDGEWICK | K EVIN W AYNE http://algs4.cs.princeton.edu ‣ queues ‣ generics ‣ iterators ‣ applications Bottom line. Client code can use generic stack for any type of data. 41 Iteration Iterators java.lang.Iterable interface Design challenge. Support iteration over stack items by client, Q. What is an Iterable ? without revealing the internal representation of the stack. A. Has a method that returns an Iterator. i s[] it was 0 1 first the best 2 3 N of 4 times null null null null 6 7 8 9 5 Q. What is an Iterator ? java.util.Iterator interface A. Has methods hasNext() and next(). current Q. Why make data structures Iterable ? times of best public interface Iterable<Item> { Iterator<Item> iterator(); } the was it A. Java supports elegant client code. null “foreach” statement (shorthand) for (String s : stack) StdOut.println(s); Java solution. Make stack implement the java.lang.Iterable interface. 43 public interface Iterator<Item> { boolean hasNext(); Item next(); optional; use void remove(); at your own risk } equivalent code (longhand) Iterator<String> i = stack.iterator(); while (i.hasNext()) { String s = i.next(); StdOut.println(s); } 44 Stack iterator: linked-list implementation Stack iterator: array implementation import java.util.Iterator; import java.util.Iterator; public class Stack<Item> implements Iterable<Item> { ... public class Stack<Item> implements Iterable<Item> { … public Iterator<Item> iterator() { return new ListIterator(); } public Iterator<Item> iterator() { return new ReverseArrayIterator(); } private class ListIterator implements Iterator<Item> { private Node current = first; private class ReverseArrayIterator implements Iterator<Item> { private int i = N; public boolean hasNext() { return current != null; } public void remove() { /* not supported */ } public Item next() { throw UnsupportedOperationException Item item = current.item; throw NoSuchElementException if no more items in iteration current = current.next; return item; } public boolean hasNext() { public void remove() { public Item next() { return i > 0; /* not supported */ return s[--i]; } } } } } } } i first s[] times N current of best the was it null it was the best of times null null null null 0 1 2 3 4 5 6 7 8 9 45 46 Iteration: concurrent modification Q. What if client modifies the data structure while iterating? A. A fail-fast iterator throws a java.util.ConcurrentModificationException. concurrent modification for (String s : stack) stack.push(s); 1.3 B AGS, QUEUES, AND S TACKS ‣ stacks ‣ resizing arrays Algorithms Q. How to detect? A. ・Count total number of push() and pop() operations in Stack. ・Save counts in *Iterator subclass upon creation. ・If, when calling next() and hasNext(), the current counts do not equal R OBERT S EDGEWICK | K EVIN W AYNE http://algs4.cs.princeton.edu the saved counts, throw exception. 47 ‣ queues ‣ generics ‣ iterators ‣ applications Java collections library Java collections library List interface. java.util.List is API for an sequence of items. java.util.Stack. ・Supports push(), pop(), and iteration. ・Extends java.util.Vector, which implements java.util.List public interface List<Item> implements Iterable<Item> interface from previous slide, including get() and remove(). create an empty list List() ・Bloated and poorly-designed API (why?) is the list empty? boolean isEmpty() number of items int size() void add(Item item) append item to the end Item get(int index) return item at given index Java 1.3 bug report (June 27, 2001) The iterator method on java.util.Stack iterates through a Stack from the bottom up. One would think that it should iterate as if it were popping off the top of the Stack. return and delete item at given index Item remove(int index) does the list contain the given item? boolean contains(Item item) iterator over all items in the list Iterator<Item> iterator() status (closed, will not fix) ... It was an incorrect design decision to have Stack extend Vector ("is-a" rather than "has-a"). We sympathize with the submitter but cannot fix this because of compatibility. Implementations. java.util.ArrayList uses resizing array; java.util.LinkedList uses linked list. caveat: only some operations are efficient 49 50 Java collections library War story (from Assignment 1) java.util.Stack. Generate random open sites in an N-by-N percolation system. ・Supports push(), pop(), and iteration. ・Extends java.util.Vector, which implements java.util.List ・Jenny: percolates blocked pick (i, j) at random; if already open, repeat. does not percolate site Takes ~ c1 N 2 seconds. ・Kenny: interface from previous slide, including get() and remove(). ・Bloated and poorly-designed API (why?) create a java.util.ArrayList of N 2 closed sites. Pick an index at random and delete. open Takes ~ c2 N 4 seconds. site open site connected to top no open site connected to top Why is my program so slow? Kenny java.util.Queue. An interface, not an implementation of a queue. Lesson. Don't use a library until you understand its API! Best practices. Use our implementations of Stack, Queue, and Bag. This course. Can't use a library until we've implemented it in class. 51 52 Stack applications Function calls ・Parsing in a compiler. ・Java virtual machine. ・Undo in a word processor. ・Back button in a Web browser. ・PostScript language for printers. ・Implementing function calls in a compiler. ・... How a compiler implements a function. ・Function call: push local environment and return address. ・Return: pop return address and local environment. Recursive function. Function that calls itself. Note. Can always use an explicit stack to remove recursion. gcd (216, 192) static int gcd(int p, int q) { if (q == 0) return p; else return gcd(q,gcd p %(192, q); 24) } static int gcd(int p, int q) { p = 192, q = 24 if (q == 0) return p; gcd else return gcd(q, p % (24, q); 0) } static int gcd(int p, int q) { if (q == 0) return p; p = 24, q = 0 else return gcd(q, p % q); } p = 216, q = 192 4.3 Stacks and Queues 573 53 it is easy to convince yourself that it computes the proper value: any time the algorithm encounters a subexpression consisting of two operands separated by an opArithmetic expressionerator, evaluation all surrounded by parentheses, it leaves the result of performing that operation on those operands on the operand stack. The result is the same as if that value had appeared in the input instead of the subexpression, so we can think of replacing valuethe stack Goal. Evaluate infix expressions. (1+((2+3)*(4*5))) subexpression by the value to get an expression operator stack that would yield the same result. We can apply 1 +((2+3)*(4*5))) this argument again and again until we get a ( 1 + ( ( 2 + 3 ) * ( single 4 * 5value. ) )For) example, the algorithm com1 ((2+3)*(4*5))) putes the same value of all of these expres+ sions: 1 2 operand operator ( 1 ( 1 ( 1 ( 1 101 + + + + ( ( ( 5 ( 5 100 2 + 3 ) * ( 4 * 5 ) ) ) * ( 4 * 5 ) ) ) * 20 ) ) ) Two-stack algorithm. [E. W. Dijkstra] + +3)*(4*5))) 1 2 + + 3)*(4*5))) 1 2 3 + + )*(4*5))) (PROGRAM 4.3.5) is an implemen1 5 Value: push onto the stack.This code is a simple tationvalue of this method. *(4*5))) + example of an interpreter : a program that in1 5 Operator: push onto the stack.by a given terprets theoperator computation specified (4*5))) + * string and performs the computation to ar1 5 4 Left parenthesis: ignore. rive at the result. A compiler is a program that *5))) + * the string into code on a lower-level Right parenthesis: converts pop operator and two values; 1 5 4 machine that can do the job. This conversion 5))) + * * is a more complicated process than the steppush the result of applying that operator 1 5 4 5 by-step conversion used by an interpreter, but ))) + * * it is based the same underlying to those values onto the on operand stack.mechanism. 1 5 20 Initially, Java was based on using an interpret)) + * er. Now, however, the Java system includes a 1 100 compiler that converts arithmetic expressions ) + (and, more generally, Java programs) into code 101 for the Java virtual machine, an imaginary maContext. An interpreter! chine that is easy to simulate on an actual computer. Trace of expression evaluation (Program 4.3.5) ・ ・ ・ ・ 54 Dijkstra's two-stack algorithm demo Evaluate value stack infix expression operator stack (fully parenthesized) ( 1 + ( ( 2 + operand 55 3 ) * ( 4 * 5 ) ) ) operator 56 Arithmetic expression evaluation Correctness Q. Why correct? public class Evaluate { public static void main(String[] args) { Stack<String> ops = new Stack<String>(); Stack<Double> vals = new Stack<Double>(); while (!StdIn.isEmpty()) { String s = StdIn.readString(); if (s.equals("(")) ; else if (s.equals("+")) ops.push(s); else if (s.equals("*")) ops.push(s); else if (s.equals(")")) { String op = ops.pop(); if (op.equals("+")) vals.push(vals.pop() + vals.pop()); else if (op.equals("*")) vals.push(vals.pop() * vals.pop()); } else vals.push(Double.parseDouble(s)); } StdOut.println(vals.pop()); } % java Evaluate } ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) ) 101.0 A. When algorithm encounters an operator surrounded by two values within parentheses, it leaves the result on the value stack. ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) ) as if the original input were: ( 1 + ( 5 * ( 4 * 5 ) ) ) Repeating the argument: ( 1 + ( 5 * 20 ) ) ( 1 + 100 ) 101 Extensions. More ops, precedence order, associativity. 57 Stack-based programming languages Observation 1. Dijkstra's two-stack algorithm computes the same value if the operator occurs after the two values. ( 1 ( ( 2 3 + ) ( 4 5 * ) * ) + ) Observation 2. All of the parentheses are redundant! 1 2 3 + 4 5 * * + Jan Lukasiewicz Bottom line. Postfix or "reverse Polish" notation. Applications. Postscript, Forth, calculators, Java virtual machine, … 59 58