def checkBalance(s, d={'(': ')', '[': ']', '{': '}', '<': '>'}): ''' check balanced symbols Args: s (str): string of parenthesis d (dict): dictionary key: open symbol, value = close symbol Returns: boolean: True if balanced, False if not balanced ''' S = Stack() for i in s: # push if open symbol if i in d: S.push(i) # unbalanced if no opening to match closing or mismatch open/close symbols elif S.isEmpty() or d[S.pop()] != i: return False # no need because popped in previous statment instead of peeked # there is a match in open/close so cancel out by popping # else: #S.pop() # only if empty (all canceled out) then balanced return S.isEmpty()
def dfs(problem): """ Implement depth-first search. """ start = problem.get_start_state() s = Stack() visited = {} cur = start s.push(start) visited[start] = None while not s.is_empty(): cur = s.pop() if problem.is_goal_state(cur): break for state in problem.get_successors(cur): if state not in visited: s.push(state) visited[state] = cur path = [] while cur is not None: path.append(cur) cur = visited[cur] path.reverse() print len(visited) return path
def ids(problem): """ Implement iterative deepening search. """ start = problem.get_start_state() depth = 1 while True: s = Stack() visited = {} d_map = {} cur = start s.push(cur) visited[start] = None d_map[start] = 1 while not s.is_empty(): cur = s.pop() if problem.is_goal_state(cur): path = [] while cur is not None: path.append(cur) cur = visited[cur] path.reverse() print len(visited) return path if d_map[cur] != depth: for state in problem.get_successors(cur): if state not in visited or d_map[cur] + 1 < d_map[state]: s.push(state) visited[state] = cur d_map[state] = d_map[cur] + 1 depth += 1
def checkPar(s): ''' check balanced parenthesis Args: s (str): string of parenthesis ''' S = Stack() for i in s: if S.isEmpty(): if i == '(': S.push(i) # imbalance else: return False else: if S.peek() == i: S.push(i) # cancel out parenthesis else: S.pop() # only if empty (all canceled out) then balanced return S.isEmpty()
def checkBalance(s, d = {'(':')', '[':']', '{': '}', '<':'>'}): ''' check balanced symbols Args: s (str): string of parenthesis d (dict): dictionary key: open symbol, value = close symbol Returns: boolean: True if balanced, False if not balanced ''' S = Stack() for i in s: # push if open symbol if i in d: S.push(i) # unbalanced if no opening to match closing or mismatch open/close symbols elif S.isEmpty() or d[S.pop()] != i: return False # no need because popped in previous statment instead of peeked # there is a match in open/close so cancel out by popping # else: #S.pop() # only if empty (all canceled out) then balanced return S.isEmpty()
def evaluateRPN(self): workstack = Stack() # As we pull tokens from the queue, we validate them and if neither a number # nor an operator, we abort with an error. for t in self.q.copy(): if (t in self.precedence): # As we work backwards, right value is first; validate right = workstack.pop() if (not str(right).isnumeric() and not right in self.precedence): self.error = True break # Now get left value, validate # Special case: ! only takes one argument. Make them identical if (t == "!"): left = right else: left = workstack.pop() if (not str(left).isnumeric() and not left in self.precedence): self.error = True break # Both valid, so calculate workstack.push(self.calculate(left, right, t)) else: workstack.push(int(t)) # answer is now on the stack if (not self.error): return (workstack.pop()) else: return (0)
def find_path(self, start: Vertex, end: Vertex): """return the path from start to end vertices as a graph""" vertice_stack = Stack(implementation='linked_list') edge_stack = Stack(implementation='linked_list') for i in range(self.nb_vertices): vertice_stack.push(end) if end is start: break edge = self.parent_edges[end] if edge: edge_stack.push(edge) end = edge.head else: raise Exception(f'No path found between {start} and {end}') else: raise Exception(f'No path found between {start} and {end}') vertices = [] while True: try: vertices.append(vertice_stack.pop()) except StackEmptyError: break edges = [] while True: try: edges.append(edge_stack.pop()) except StackEmptyError: break return Graph(vertices=vertices, edges=edges, directed=True)
def find_shortest_path(self, start_vetex, end_vertex): """ Return a list of vertices creating a path from the start to end vertices. Keyword arguments: start_vetex -- the starting vertex. end_vertex -- the ending vertex. Time complexity: O(V) Space complexity: O(V) """ if end_vertex.distance == float('inf'): return [] else: reverse = Stack() current_vertex = end_vertex reverse.push(current_vertex) while current_vertex != start_vetex: current_vertex = current_vertex.previous_vertex reverse.push(current_vertex) path = [] while not reverse.is_empty(): path.append(reverse.pop()) return path
class SetOfStacks: LIMIT_PER_STACK = 2 def __init__(self): self.main_stack = Stack() def pop(self): if self.is_empty(): return None elif self._top_stack().is_empty(): self.main_stack.pop() self.pop() return self._top_stack().pop() def push(self, item): if self.is_empty(): self.main_stack.push(Stack()) self._top_stack().push(item) def is_empty(self): return self.main_stack.is_empty() def peek(self): if self.is_empty(): return None return self._top_stack().peek().value def _top_stack(self): return self.main_stack.peek()
def my_pow(base, exp, modulus): from datastructures import Stack powers = Stack() var_exp = exp while var_exp > 0: powers.push(var_exp % 2) var_exp = var_exp / 2 rem = 1 while not powers.is_empty(): p = powers.pop() rem = ((base ** p) * ((rem ** 2) % modulus)) % modulus return rem
def evalpostfix(postfixexpr): nums = Stack() tokenlist = list(postfixexpr.replace(' ', '')) for token in tokenlist: if token not in prec.keys(): nums.push(token) else: right = nums.pop() left = nums.pop() expression = left + token + right result = eval(expression) nums.push(str(result)) if nums.size() == 1: return nums.pop()
class Queue2Stack(object): def __init__(self): from datastructures import Stack self.inbox = Stack() self.outbox = Stack() def push(self,item): self.inbox.push(item) def pop(self): if self.outbox.isEmpty(): while not self.inbox.isEmpty(): self.outbox.push(self.inbox.pop()) return self.outbox.pop()
def backtrack( a: list, is_a_solution: Callable[[list, int, Any], bool], construct_candidates: Callable[[list, int, Any], Iterator], inputs: Any = None, process_solution: Callable[[list, int, Any], Any] = None, make_move: Callable[[list, int, Any], Any] = None, unmake_move: Callable[[list, int, Any], Any] = None ) -> Iterator[Tuple[list, int]]: """backtracking DFS style""" if process_solution is None: process_solution = lambda a, k, inputs: None if make_move is None: make_move = lambda a, k, inputs: None if unmake_move is None: unmake_move = lambda a, k, inputs: None stack = Stack(implementation='linked_list') class StackItem: def __init__(self, k: int, candidates: Iterator = None): self.k = k self.candidates = candidates stack.push(StackItem(k=0)) while True: try: stack_item = stack.pop() try: if stack_item.candidates is None: stack_item.candidates = construct_candidates( a=a, k=stack_item.k, inputs=inputs) candidate = next(stack_item.candidates) else: unmake_move(a, stack_item.k, inputs) candidate = next(stack_item.candidates) except StopIteration: pass else: a[stack_item.k] = candidate make_move(a, stack_item.k, inputs) if is_a_solution(a, stack_item.k, inputs): process_solution(a, stack_item.k, inputs) yield a[:stack_item.k + 1] stack.push(stack_item) else: stack.push(stack_item) stack.push(StackItem(k=stack_item.k + 1)) except StackEmptyError: break
def revstring(mystr): ''' Reverse the characters in a string using a stack Args: mystr (string): string to reverse Returns: string: reversed string ''' s = Stack() revmystr = "" for ch in mystr: s.push(ch) while not s.isEmpty(): revmystr += s.pop() return revmystr
def calculate_path_volume(graph: Graph, start: Vertex, end: Vertex) -> float: stack = Stack(implementation='linked_list') parent_edges = graph.parent_edges while True: edge = parent_edges[end] if edge: stack.push(edge) end = edge.head if end is start: break else: return 0 volume = inf while True: try: volume = min(volume, stack.pop().residual) except StackEmptyError: break return volume
def mergesort(items: Sequence[KeyedItem], order=None) -> None: """O(n*log n)""" comp = lambda x, y: x > y if order == 'max' else x < y stack = Stack(implementation='linked_list') class StackItem: def __init__(self, low, high, status=0): self.low = low self.high = high self.status = status stack_item = StackItem(0, len(items) - 1) while True: low = stack_item.low high = stack_item.high if low != high: median = (low + high) // 2 if not stack_item.status: # sort left stack_item.status = 1 stack.push(stack_item) stack.push(StackItem(low, median)) elif stack_item.status == 1: # sort right stack_item.status = 2 stack.push(stack_item) stack.push(StackItem(median + 1, high)) else: # merge left_queue = Queue(implementation='doubly_linked_list') for i in range(low, median+1): left_queue.enqueue(items[i]) right_queue = Queue(implementation='doubly_linked_list') for i in range(median+1, high+1): right_queue.enqueue(items[i]) for i in range(low, high + 1): if not left_queue.head: items[i] = right_queue.dequeue() elif not right_queue.head: items[i] = left_queue.dequeue() else: if comp(left_queue.head.key, right_queue.head.key): items[i] = left_queue.dequeue() else: items[i] = right_queue.dequeue() try: stack_item = stack.pop() except StackEmptyError: break
def quicksort(items: Sequence[KeyedItem], order=None) -> None: """O(n*log n) expected - O(n**2) worst case""" comp = lambda x, y: x > y if order == 'max' else x < y stack = Stack(implementation='linked_list') class StackItem: def __init__(self, low, high, status=0): self.low = low self.high = high self.status = status stack_item = StackItem(0, len(items) - 1) while True: low = stack_item.low high = stack_item.high if low < high: if not stack_item.status: # partition according to pivot with linear scan # pick pivot pivot_index = random.randint(low, high) pivot = items[pivot_index] items[high], items[pivot_index] = items[pivot_index], items[ high] # all items between low+1 and i are partionned against the pivot # all items below pivot index are comp(item.key, pivot.key) pivot_index = low for i in range(low, high): item = items[i] if comp(item.key, pivot.key): items[i], items[pivot_index] = items[ pivot_index], items[i] pivot_index += 1 # swap pivot to its rightful position items[high], items[pivot_index] = items[pivot_index], items[ high] # sort left of pivot stack_item.status = 1 stack.push(stack_item) stack.push(StackItem(low, pivot_index - 1)) elif stack_item.status == 1: # sort right of pivot stack_item.status = 2 stack.push(stack_item) stack.push(StackItem(pivot_index + 1, high)) try: stack_item = stack.pop() except StackEmptyError: break
def infix2postfix(infixexpr): opstack = Stack() postfix_list = [] tokenlist = list(infixexpr.replace(' ', '')) for token in tokenlist: if token not in prec.keys() and token != ')': postfix_list.append(token) elif token == '(': opstack.push(token) elif token == ')': top_token = opstack.pop() while top_token != '(': postfix_list.append(top_token) top_token = opstack.pop() else: while (not opstack.isEmpty() and prec[opstack.peek()] >= prec[token]): postfix_list.append(opstack.pop()) opstack.push(token) while not opstack.isEmpty(): postfix_list.append(opstack.pop()) return ''.join(postfix_list)
def df_paths(graph: Dict[int, Dict[str, int]], starting_room: int) -> Dict[int, List[str]]: ss = Stack() visited = set() ss.push([starting_room]) paths = {(starting_room, starting_room): [starting_room]} while ss.size > 0: path: List[int] = ss.pop() room = path[-1] if room not in visited: visited.add(room) for move, nextroom in graph[room].items(): path_copy = path.copy() path_copy.append(nextroom) ss.push(path_copy) paths[(starting_room, nextroom)] = paths[(starting_room, room)] + [move] paths = {key: val[1:] for key, val in paths.items()} return paths
def augment_path(graph: Graph, start: Vertex, end: Vertex, volume: float): stack = Stack(implementation='linked_list') parent_edges = graph.parent_edges while True: edge = parent_edges[end] if edge: stack.push(edge) end = edge.head if end is start: break else: break while True: try: edge = stack.pop() except StackEmptyError: break else: edge.edgenode.flow += volume edge.edgenode.residual -= volume edge.edgenode.opposite.residual += volume
def test_linked_list_implementation(): stack = Stack(implementation='linked_list') a, b, c, d = list('abcd') stack.push(a) assert stack.pop() is a stack.push(b) stack.push(c) assert stack.pop() is c stack.push(d) assert stack.pop() is d assert stack.pop() is b
def sort_stack(stack): temp_stack = Stack() threshold = stack.pop() while not stack.is_empty(): if stack.peek() >= threshold: stack_node = stack.pop() temp_stack.push(threshold) threshold = stack_node elif stack.peek() < threshold: temp_stack.push(threshold) threshold = stack.pop() while not temp_stack.is_empty() and threshold < temp_stack.peek(): stack.push(temp_stack.pop()) temp_stack.push(threshold) if not stack.is_empty(): threshold = stack.pop() temp_stack.push(threshold) while not temp_stack.is_empty(): stack.push(temp_stack.pop()) return stack
stack.push(temp_stack.pop()) temp_stack.push(threshold) if not stack.is_empty(): threshold = stack.pop() temp_stack.push(threshold) while not temp_stack.is_empty(): stack.push(temp_stack.pop()) return stack if __name__ == '__main__': # tests stack = Stack() original_values = [8, 3, 5, 9, 7, 2] for v in original_values: stack.push(v) sorted_stack = Stack() sorted_values = sorted(original_values, reverse=True) for v in sorted_values: sorted_stack.push(v) expected_sorted_stack = sort_stack(stack) while not expected_sorted_stack.is_empty() and not sorted_stack.is_empty(): assert expected_sorted_stack.pop() == sorted_stack.pop()
# room, neighbs = rooms_neighbs.pop(0) #print(build_up) #print(traversal) visited_rooms = set() player.currentRoom = world.startingRoom visited_rooms.add(player.currentRoom) traversalPath = list() ss = Stack() qq = Queue() while len(visited_rooms) != len(roomGraph.keys()): move = random.choice(player.currentRoom.getExits()) if room_graph[player.currentRoom.id][move] not in visited_rooms: ss.push(move) while ss.size > 0: dir_move: str = ss.pop() if dir_move in room_graph[player.currentRoom.id].keys(): if room_graph[player.currentRoom.id][dir_move] not in visited_rooms: print(dir_move) player.travel(dir_move) traversalPath.append(dir_move) visited_rooms.add(player.currentRoom.id) for mv in player.currentRoom.getExits(): if mv in room_graph[player.currentRoom.id].keys(): print(" ", mv) if room_graph[player.currentRoom.id][mv] not in visited_rooms:
import sys sys.path.append('./datastructures') from datastructures import Stack if __name__ == '__main__': # tests stack = Stack() stack.push(1) assert stack.min() == 1 stack.push(2) assert stack.min() == 1 stack.push(3) assert stack.min() == 1 stack.push(0) assert stack.min() == 0 stack.pop() assert stack.min() == 1
def dfs(self, start: Vertex, process_vertex_early: Callable[[Vertex], Any] = None, process_vertex_late: Callable[[Vertex], Any] = None, process_edge: Callable[[Vertex, Edgenode], Any] = None, discovered_vertices: NodeList = None, processed_vertices: NodeList = None): """Depth-First Search returns entry and exit times for each vertex - a vertex v1 is an ancestor of vertex v2 if the time interval of v2 is nested in the one of v1 - the nb of descendants of a vertex v1 is half its time interval""" process_vertex_early = process_vertex_early if process_vertex_early else lambda v: None process_vertex_late = process_vertex_late if process_vertex_late else lambda v: None process_edge = process_edge if process_edge else lambda v, e: None vertices = [v for v in self.adjacency_lists] self.parent_edges = NodeList(vertices=vertices) # reinit parents discovered = NodeList( vertices=vertices ) if discovered_vertices is None else discovered_vertices # added to stack processed = NodeList( vertices=vertices ) if processed_vertices is None else processed_vertices # processed and out of stack discovered[start] = True class StackItem: def __init__(self, vertex: Vertex, status=0, iter_edgenodes=None): self.vertex = vertex self.status = status self.iter_edgenodes = iter_edgenodes stack = Stack(implementation='linked_list') stack.push(StackItem(vertex=start)) counter = 0 while True: try: stack_item = stack.pop() except StackEmptyError: break if not stack_item.status: stack_item.vertex.entry_time = counter counter += 1 stack_item.iter_edgenodes = iter( self.adjacency_lists[stack_item.vertex].edgenodes) process_vertex_early(stack_item.vertex) stack_item.status = 1 if stack_item.status == 1: while True: try: edgenode = next(stack_item.iter_edgenodes) next_vertex = edgenode.tail except StopIteration: stack_item.status = 2 stack.push(stack_item) break # if undiscovered vertex: add it to stack if not discovered[next_vertex]: discovered[next_vertex] = True stack.push(stack_item) # tree edge edgenode.edgetype = EdgeType.TREE # the parent of the next_vertex is the head of the edge, # i.e., stack_item.vertex self.parent_edges[next_vertex] = edgenode.to_edge( head=stack_item.vertex) process_edge(stack_item.vertex, edgenode) stack.push(StackItem(vertex=next_vertex)) break # if discovered vertex, then it has been put in stack before # if it is still unprocessed, then it is still in the stack, so no need to add it # if it has been processed but the graph is directed, the edge has not been processed yet # in both case, the edge needs to be processed # but it is a "back edge", the parent of the next_vertex is already known elif not processed[next_vertex] or self.directed: # discovered but not processed if not processed[next_vertex]: edgenode.edgetype = EdgeType.BACK # discovered, processed but earlier entry time elif stack_item.vertex.entry_time < next_vertex.entry_time: edgenode.edgetype = EdgeType.FORWARD # discovered, processed but later entry time else: edgenode.edgetype = EdgeType.CROSS process_edge(stack_item.vertex, edgenode) elif stack_item.status == 2: process_vertex_late(stack_item.vertex) processed[stack_item.vertex] = True stack_item.vertex.exit_time = counter counter += 1
class DiceResolver: def __init__(self): # BEDMAS ==> d()!^/%*+- # PEMDAS ==> d()!^*/%+- # Precedence weighting reflects rule; higher means priority # Close bracket ')' not included; it is a special case (collapses stack to '(') self.precedence = { "(": 0, "+": 3, "-": 3, "/": 5, "*": 5, "%": 5, "C": 5, "c": 5, "^": 7, "!": 8, "d": 9 } # 's' is a stack used for rearranging infix arguments self.s = Stack() # 'q' is a queue used to store the postfix arguments self.q = Queue() self.error = False # Converts a valid infix expression (mathematical expression) # to postfix using Reverse Polish Notation (RPN). Infix exp- # ression must be valid; this function can not check validi- # ty. Note that by design, this only supports integer expr- # ession (no floating point support). FP support can be add- # ed if while building numbers, the '.' character is accepted. # Example: Expression="1 + 2 * 3" --> 7, NOT 9 # RPN="1 2 3 * +" --> 7 # Note that the order of operations is preserved in the RPN. def infixToRPN(self, expression): # Since a number may be multiple characters, we start with an empty string, # and while each character is numeric, we append the number until a non- # numeric value is encountered. num = "" # Tokenize expression character by character for c in expression: token = str(c) # Case: we had accumulated a number but this character is not a # numeric value; so save accumulated number, and reset accumulator. if (num != "" and not token.isnumeric()): self.q.enqueue(num) num = "" # We aren't a number; so handle the token # '(' start brackets are simply markers of what point to return to when # a ')' close bracket is encountered. if (token == "("): self.s.push(token) # Special case; we look for this first -> it means we have to pop all # previous values off stack into the RPN queue until we find the '(' elif (token == ")"): # pop up until the bracket while (self.s.peek() != "("): self.q.enqueue(self.s.pop()) # pop the bracket / throw it away (it was just a marker, we're done with it) self.s.pop() # Casee: operator handling # we are done handling brackets, check for a valid operator elif (token in self.precedence): while self.s.size() != 0 and (self.precedence[token] <= self.precedence[self.s.peek()]): self.q.enqueue(self.s.pop()) self.s.push(token) # Case: character is numeric. # Append to accumulator and continue parsing elif (token.isnumeric()): num += token # Did token end on a number? If so store accumulated number in RPN queue if (num != ""): self.q.enqueue(num) # Now pop items from stack to the queue to cleanup while (self.s.size() != 0): self.q.enqueue(self.s.pop()) # At this point, we have a valid RPN in the 'q' queue # (if the infix expression was valid) # Let's return a string version: q_cp = self.q.copy() rpn = "" for c in q_cp: rpn += c + " " return (rpn) # Routine to calculate a factorial def factorial(self, value): if (value < 0): return (0) elif (value == 0 or value == 1): return (1) elif (value == 2): return (2) product = value for x in range(2, value): product = product * x return (product) # Routine to calculate "choose" (combinatorics) # Formula: # nCr (n Choose r) = n! / r!(n-r)! def choose(self, n, r): numerator = self.factorial(n) denominator = self.factorial(r) * self.factorial(n - r) # Sanity if (denominator == 0): return (0) # Compute # NOTE: Should always be an integer result, but cast # it anyways to be safe return (int(numerator / denominator)) # Given left value, right value, and an operator, calculate. def calculate(self, left, right, op): if (op == "+"): return (left + right) elif (op == "-"): return (left - right) elif (op == "*"): return (left * right) elif (op == "/"): return (int(left / right)) elif (op == "^"): return (left**right) elif (op == "%"): return (left % right) elif (op == "!"): return (self.factorial(left)) elif (op == "c" or op == "C"): return (self.choose(left, right)) # dice roll; handled with 'random' # NOTE: expressions without 'd' are deterministic; # expressions with 'd' are non-deterministic (variable # outcomes). elif (op == "d"): sum = 0 # Left value is number of rolls; right value is die # IE 3d6 = 3 rolls of a 6 sided die, summed. for i in range(left): sum += random.randint(1, right) return (sum) # whoops shouldn't have happened try to be graceful return (0) # Nifty little stack and queue algorithm for evaluating # the RPN. Expects a valid RPN expression. def evaluateRPN(self): workstack = Stack() # As we pull tokens from the queue, we validate them and if neither a number # nor an operator, we abort with an error. for t in self.q.copy(): if (t in self.precedence): # As we work backwards, right value is first; validate right = workstack.pop() if (not str(right).isnumeric() and not right in self.precedence): self.error = True break # Now get left value, validate # Special case: ! only takes one argument. Make them identical if (t == "!"): left = right else: left = workstack.pop() if (not str(left).isnumeric() and not left in self.precedence): self.error = True break # Both valid, so calculate workstack.push(self.calculate(left, right, t)) else: workstack.push(int(t)) # answer is now on the stack if (not self.error): return (workstack.pop()) else: return (0) # One function to handle it all. How Pythonic. def resolve(self, expression, repeat=False): if not repeat: self.error = False self.q.clear() self.s.clear() self.infixToRPN(expression) return (self.evaluateRPN()) else: # Repeat=True # This allows repeat dice rolls / calculations, without rebuilding # the RPN queue each time. return (self.evaluateRPN()) # Heuristic to calculate expression distribution, ment to be # used with dice rolls (ie, 2d6). This is done by repeating rolls # to a cap of n trials, then assessing the results. # Returns a histogram report with trial results, mean and mode. def getHistogram(self, expression, trials): # Validate min/max boundaries if (trials < 0): trials = 1 elif (trials > 1000000): trials = 1000000 # Initialize sb = "" rolls = dict() sum = 0 pct = 1.0 max = dict() result = dict() # Build for i in range(trials): if i == 0: roll = self.resolve(expression) else: # We already built the RPN, don't waste cycles # reuilding it on every iteration, set repeat=True. roll = self.resolve(expression, repeat=True) # Track the recurrences of roll values if (roll in rolls.keys()): rolls[roll] += 1 else: rolls[roll] = 1 # Nifty way to build a key sorted report keys = list(rolls.keys()) keys.sort() # Report sb = f"DISTRIBUTION HISTORGRAM ({trials:,} trials):\n" max[0] = max[1] = 0 for key in keys: result[key] = rolls[key] sum += key * rolls[key] pct = float(rolls[key]) / float(trials, ) * 100.0 if (pct > max[0]): max[0] = pct max[1] = key sb += f"[{key:3}] ==> {rolls[key]:,} ({pct:.2f}%)\n" # Stash pct for later rolls[key] = pct mean = float(sum) / float(trials) + 0.5 sb += f"Mean: {mean:.2f}\n" mode = max[1] sb += f"Mode: {mode}\n\n" # Scaling calculation uses a lambda function scale = lambda x, y: int(float(x / 100) * float(y) + 0.5) # Build histogram pictogragh pic = "PICTORIAL HISTOGRAM:\n" for key in keys: pic += f"[{key:3}] " for i in range(scale(rolls[key], 160)): pic += "*" pic += "\n" # Send back the report return (sb + pic)
def test_primitives(): stack = Stack() with pytest.raises(NotImplementedError): stack.push('a') with pytest.raises(NotImplementedError): stack.pop()