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 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 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)
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 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 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 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 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 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 critical(self, start="START"): """ 1. for each node follow the predecessors if the node has no slack. 2. traverse predecessor and repeat step 1. The function will return one critical path. If there are more critical paths it will only return one arbitrary of them. """ top = self.topological() path = Stack([self.tasks[start]]) bad = set() def _visits(task): if len(task.successors.values()) == 0: return True for t in task.successors.values(): if t.earliest_start == t.latest_start and t not in bad: path.push(t) return _visits(t) return False for t in top: if _visits(t): break pop = path.pop() bad.add(t) return path
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
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 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 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 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 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 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 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
def topological_sort(graph: Graph) -> List[Vertex]: """DFS: returns the list of topologically-ordered vertices in a Directed Acyclic Graph (DAG)""" if not graph.directed: raise GraphDirectionTypeError vertices = [v for v in graph.adjacency_lists] stack = Stack(implementation='linked_list') discovered_vertices = NodeList(vertices, default=False) processed_vertices = NodeList(vertices, default=False) sorted_vertices = [] def process_vertex_early(vertex: Vertex): nonlocal discovered_vertices discovered_vertices[vertex] = True def process_vertex_late(vertex: Vertex): nonlocal stack, processed_vertices processed_vertices[vertex] = True stack.push(vertex) def process_edge(head: Vertex, edgenode: Edgenode): # check that the edge is not a back edge if edgenode.edgetype is EdgeType.BACK: raise CycleInGraphError for v in vertices: if not discovered_vertices[v]: graph.dfs(start=v, process_vertex_early=process_vertex_early, process_vertex_late=process_vertex_late, process_edge=process_edge, discovered_vertices=discovered_vertices, processed_vertices=processed_vertices) while True: try: sorted_vertices.append(stack.pop()) except StackEmptyError: break return sorted_vertices
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 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 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
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()
def test_array_implementation_empty_stack(): stack = Stack(implementation='array') with pytest.raises(StackEmptyError): stack.pop()
#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: ss.push(mv) continue
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 test_linked_list_implementation_empty_stack(): stack = Stack(implementation='linked_list') with pytest.raises(StackEmptyError): stack.pop()
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()