├── .github └── workflows │ └── black.yml ├── .gitignore ├── README.md ├── dynamic_programming ├── Viterbi_HMM.py ├── coin_changing.py ├── edit_distance.py └── seam_carving.py ├── graph_algos ├── FloydWarshall.py ├── bfs.py ├── dfs.py └── dijkstra.py ├── misc └── maxheap.py ├── parsing_algos ├── cyk.py ├── input.txt ├── ll1.py └── out.md ├── poetry.lock ├── pyproject.toml └── sorting_algos ├── bubble_sort.py ├── heap_sort.py ├── insertion_sort.py ├── merge_sort.py └── quick_sort.py /.github/workflows/black.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: psf/black@stable -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/social-anthrax/IADS_algorithms/ca3e19715f679c41dac14083ad65fe0dcc0648e9/.gitignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IADS 2021-22 Algorithm and Data structure collection 2 | 3 | To contribute please make a new branch and make a pull request! Code will only be accepted in python, and please make sure to use type annotations when possible. 4 | Current algorithms and data structures in repo: 5 | 6 | - Bubble Sort 7 | - Insertion Sort 8 | - Merge Sort 9 | - Heap Sort 10 | - Quick Sort 11 | - BFS 12 | - DFS 13 | - Dijkstra (borrowed from https://stackabuse.com/dijkstras-algorithm-in-python/) 14 | - Floyd-Warshall (Including backtracking) 15 | - Seam-Carving 16 | - Coin Counting 17 | - Viterbi Algorithm (Including backtracking) 18 | - Edit Distance (with string reconstruct borrowed from published solution) 19 | - CYK 20 | - LL(1) 21 | - Max-Heap (with basic visualisation) An additional implementation for the Max-Heap presented in the lectures can be found here: https://github.com/when-q/Heap 22 | 23 | ## Dependancies. 24 | We use poetry for dependancy management, please make sure to use it, as otherwise you could be in dependancy hell! 25 | Current dependancies are: 26 | - python = "^3.8" 27 | - pandas = "^1.4.2" 28 | - networkx = "^2.8" 29 | - matplotlib = "^3.5.2" 30 | - pydot = "^1.4.2" 31 | -------------------------------------------------------------------------------- /dynamic_programming/Viterbi_HMM.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | 3 | 4 | def viterbi( 5 | obs: "list[int]", 6 | states: "list[int]", 7 | start_p: "list[list[float]]", 8 | trans_p: "list[list[float]]", 9 | emit_p: "list[list[float]]", 10 | ) -> "list[int]": 11 | """ 12 | params: 13 | obs: observed sentence. Passed as a list of indices referring to the language. 14 | states: list of possible states 15 | start_p: list of probabilities for each start state 16 | trans_p: 2d array representing the transmition matrix 17 | emit_p: 2d array representing the emition matrix 18 | """ 19 | 20 | n = len(obs) 21 | # initialise our most likely path and table for reverse lookup. 22 | mlp = [[None for _ in range(0, len(states))] for _ in range(0, len(obs))] 23 | prev = [[None for _ in range(0, len(states))] for _ in range(0, len(obs))] 24 | 25 | # initialise our base cases 26 | for state in states: 27 | mlp[0][state] = start_p[state] * emit_p[state][obs[0]] 28 | prev[0][state] = -1 29 | 30 | # calculate the transition costs. 31 | for i in range(1, n): 32 | for state in states: 33 | mlp[i][state] = 0 34 | prev[i][state] = -1 35 | for state_star in states: 36 | # Transition cost holds the probability of transitioning 37 | # from state_star to state multiplied by the probability of the observation 38 | transition_cost = trans_p[state_star][state] * emit_p[state][obs[i]] 39 | if transition_cost * mlp[i - 1][state_star] > mlp[i][state]: 40 | mlp[i][state] = transition_cost * mlp[i - 1][state_star] 41 | prev[i][state] = state_star 42 | 43 | backtrack_states = [max(enumerate(mlp[n - 1]), key=lambda x: x[1])[0]] 44 | for i in range(n - 1, 0, -1): 45 | prev_node = prev[i][backtrack_states[0]] 46 | backtrack_states = [prev_node] + backtrack_states 47 | 48 | # # Uncomment to return max probability 49 | # for state in states: 50 | # if mlp[n, state] > max: 51 | # max = mlp[n, state] 52 | 53 | return backtrack_states 54 | 55 | 56 | def viterbi_traced( 57 | obs: "list[int]", 58 | states: "list[int]", 59 | start_p: "list[list[float]]", 60 | trans_p: "list[list[float]]", 61 | emit_p: "list[list[float]]", 62 | ) -> "list[int]": 63 | """ 64 | params: 65 | obs: observed sentence. Passed as a list of indices referring to the language. 66 | states: list of possible states 67 | start_p: list of probabilities for each start state 68 | trans_p: 2d array representing the transmition matrix 69 | emit_p: 2d array representing the emition matrix 70 | """ 71 | 72 | print("Start:") 73 | pp = pprint.PrettyPrinter(indent=2) 74 | 75 | n = len(obs) 76 | # initialise our most likely path and table for reverse lookup. 77 | mlp = [[None for _ in range(0, len(states))] for _ in range(0, len(obs))] 78 | prev = [[None for _ in range(0, len(states))] for _ in range(0, len(obs))] 79 | 80 | # initialise our base cases 81 | for state in states: 82 | mlp[0][state] = start_p[state] * emit_p[state][obs[0]] 83 | prev[0][state] = -1 84 | 85 | print("mlp") 86 | pp.pprint(mlp) 87 | print("prev") 88 | pp.pprint(prev) 89 | 90 | print("loop") 91 | # calculate the transition costs. 92 | for i in range(1, n): 93 | for state in states: 94 | mlp[i][state] = 0 95 | prev[i][state] = -1 96 | for state_star in states: 97 | # Transition cost holds the probability of transitioning 98 | # from state_star to state multiplied by the probability of the observation 99 | transition_cost = trans_p[state_star][state] * emit_p[state][obs[i]] 100 | if transition_cost * mlp[i - 1][state_star] > mlp[i][state]: 101 | mlp[i][state] = transition_cost * mlp[i - 1][state_star] 102 | prev[i][state] = state_star 103 | print("mlp") 104 | pp.pprint(mlp) 105 | print("prev") 106 | pp.pprint(prev) 107 | 108 | backtrack_states = [max(enumerate(mlp[n - 1]), key=lambda x: x[1])[0]] 109 | for i in range(n - 1, 0, -1): 110 | prev_node = prev[i][backtrack_states[0]] 111 | backtrack_states = [prev_node] + backtrack_states 112 | 113 | # # Uncomment to return max probability 114 | # for state in states: 115 | # if mlp[n, state] > max: 116 | # max = mlp[n, state] 117 | 118 | return backtrack_states 119 | 120 | 121 | if __name__ == "__main__": 122 | state_names = ["DT", "N", "V", "Adj"] 123 | state_indices = list(range(0, len(state_names))) 124 | sentence = "The old man the lifeboats" 125 | language = ["lifeboats", "man", "old", "the", "other"] 126 | trans_p = [ 127 | [0, 0.6, 0, 0.4], 128 | [0.05, 0.3, 0.4, 0.25], 129 | [0.4, 0.3, 0.1, 0.2], 130 | [0.1, 0.5, 0.2, 0.2], 131 | ] 132 | 133 | emit_p = [ 134 | [0, 0, 0, 0.5, 0.5], 135 | [0.2, 0.3, 0.2, 0, 0.3], 136 | [0, 0.1, 0, 0, 0.9], 137 | [0, 0, 0.4, 0, 0.6], 138 | ] 139 | 140 | start_p = [0.4, 0.3, 0.2, 0.1] 141 | 142 | indices = [] 143 | for word in sentence.lower().split(" "): 144 | indices.append(language.index(word)) 145 | 146 | # as viterbi returns a list of indices, we need to convert it to a list of words 147 | grammar = [] 148 | for i in viterbi_traced(indices, state_indices, start_p, trans_p, emit_p): 149 | grammar.append(state_names[i]) 150 | print(grammar) 151 | -------------------------------------------------------------------------------- /dynamic_programming/coin_changing.py: -------------------------------------------------------------------------------- 1 | # File: coin_changing.py 2 | # COIN CHANGING PROBLEMS. 3 | 4 | import sys 5 | import time 6 | 7 | sys.setrecursionlimit(1000000) 8 | 9 | # Some coin systems: 10 | 11 | lecture_coins = [1, 5, 7] 12 | 13 | sterling_coins = [1, 2, 5, 10, 20, 50, 100, 200] 14 | 15 | 16 | # Problem (1): Minimum number of coins (as in Lecture 17). 17 | 18 | c_list = lecture_coins # global variable, can set as desired 19 | 20 | 21 | # Plain recursive implementation. 22 | # fewest_coins should be implemented recursively, returning just smallest number of coins. 23 | 24 | 25 | def fewest_coins(v: int) -> int: 26 | if v in c_list: 27 | return 1 28 | return 1 + min(fewest_coins(v - c_i) for c_i in c_list if c_i < v) 29 | 30 | 31 | # slightly different method which returns a list of actual coins (which constitute a 32 | # minimum-sized solution). 33 | 34 | 35 | def fewest_coins_list(v: int) -> "list[int]": 36 | if v in c_list: 37 | return [v] 38 | return min( 39 | ([c_i] + fewest_coins_list(v - c_i) for c_i in c_list if c_i < v), key=len 40 | ) 41 | 42 | 43 | # Memoization operation, exactly as in our lecture: 44 | 45 | 46 | def memoize(f): 47 | memo = {} 48 | 49 | def check(v): 50 | if v not in memo: 51 | memo[v] = f(v) 52 | return memo[v] 53 | 54 | return check 55 | 56 | 57 | # memoize : (int->int) -> (int->int) 58 | # f : int->int, check : int->int 59 | 60 | # To get the optimization of the recursion: 61 | 62 | # fewest_coins = memoize(fewest_coins) 63 | # fewest_coins_list = memoize(fewest_coins_list) 64 | 65 | # NB. Can't change c_list after doing this! 66 | # We would need to reload the file within the Python interpreter to use with new c_list. 67 | 68 | # You should also implement and experiment with a dynamic programming solution, 69 | # as given towards the end of slide-set 17. 70 | 71 | 72 | INF = 1_000_000_000 73 | 74 | 75 | def fewest_coins_dp(v): 76 | k = len(c_list) 77 | c = c_list # 78 | C = [INF for _ in range(v + 1)] 79 | P = [INF for _ in range(v + 1)] 80 | C[0] = 0 81 | for w in range(1, v + 1): 82 | for i in range(1, k): 83 | if c[i] <= w and (C[w - c[i]] + 1) < C[w]: 84 | C[w] = 1 + C[w - c[i]] 85 | P[w] = i 86 | return C[v] 87 | 88 | 89 | def fewest_coins_list_dp(v): 90 | k = len(c_list) 91 | c = c_list 92 | S = [0 for _ in range(k)] 93 | C = [INF for _ in range(v + 1)] 94 | P = [INF for _ in range(v + 1)] 95 | C[0] = 0 96 | for w in range(1, v + 1): 97 | for i in range(k): 98 | if c[i] <= w and (C[w - c[i]] + 1) < C[w]: 99 | C[w] = 1 + C[w - c[i]] 100 | P[w] = i 101 | while v > 0: 102 | i = P[v] 103 | S[i] = S[i] + 1 104 | v = v - c[i] 105 | return S 106 | 107 | 108 | def fewest_coins_list_dp_traced(v): 109 | k = len(c_list) 110 | c = c_list 111 | S = [0 for _ in range(k)] 112 | # fewest coins needed for each v 113 | C = [INF for _ in range(v + 1)] 114 | # which coin is added here last (from this we can also find which cell in C is the predecessor of the corresponding cell in C) 115 | P = [INF for _ in range(v + 1)] 116 | C[0] = 0 117 | for w in range(1, v + 1): 118 | for i in range(k): 119 | if c[i] <= w and (C[w - c[i]] + 1) < C[w]: 120 | C[w] = 1 + C[w - c[i]] 121 | P[w] = i 122 | # the first cells of C and P are insignificant; only there to make the indexing nicer 123 | print("C", C) 124 | print("P", P) 125 | temp = v # stores the quantity 126 | while v > 0: 127 | i = P[v] 128 | S[i] = S[i] + 1 129 | v = v - c[i] 130 | print("C", C[1:]) 131 | print("P", P[1:]) 132 | return S 133 | 134 | 135 | if __name__ == "__main__": 136 | small_target = 50 137 | medium_target = 2_500 138 | target = 10_000_000 139 | start = time.time() 140 | sol = fewest_coins(small_target) 141 | print(f"naive counting {small_target} takes {time.time() - start:.2f} seconds.") 142 | print(f" ==> {sol} coins are needed.") 143 | # start = time.time() 144 | # print(fewest_coins_list(small_target)) 145 | # print(f"naive listing {small_target} takes {time.time() - start:.2f} seconds.") 146 | 147 | fewest_coins = memoize(fewest_coins) 148 | # fewest_coins_list = memoize(fewest_coins_list) 149 | 150 | # start = time.time() 151 | # sol = fewest_coins(medium_target) 152 | # print(f"memoised counting {medium_target} takes {time.time() - start:.2f} seconds.") 153 | # print(f" ==> {sol} coins are needed.") 154 | # start = time.time() 155 | # print(fewest_coins_list(medium_target)) 156 | # print(f"memoised listing {medium_target} takes {time.time() - start:.2f} seconds.") 157 | 158 | # start = time.time() 159 | # sol = fewest_coins_dp(target) 160 | # print( 161 | # f"dynamic programming counting {target} takes {time.time() - start:.2f} seconds." 162 | # ) 163 | # print(f" ==> {sol} coins are needed.") 164 | # start = time.time() 165 | # print(fewest_coins_list_dp(target)) 166 | # print(f"dynamic programming listing {target} takes {time.time() - start:.2f} seconds.") 167 | 168 | start = time.time() 169 | sol = fewest_coins_list_dp_traced(small_target) 170 | print( 171 | f"dynamic programming counting {small_target} takes {time.time() - start:.2f} seconds." 172 | ) 173 | print(f" ==> {sol} coins are needed.") 174 | -------------------------------------------------------------------------------- /dynamic_programming/edit_distance.py: -------------------------------------------------------------------------------- 1 | # Anonymous ArRay 2022 02 08 2 | # Artemis Livingstone 2022 05 06 3 | 4 | 5 | def prettyPrint(a: list[list]): 6 | for i in a: 7 | print(i) 8 | 9 | 10 | def edit_distance(s: str, t: str): # s : 1 to m -> 0 to m-1, t : 1 to n -> 0 to n-1 11 | m = len(s) 12 | n = len(t) 13 | 14 | d = [[-1 for _ in range(n + 1)] for _ in range(m + 1)] 15 | a = [[-1 for _ in range(n + 1)] for _ in range(m + 1)] 16 | 17 | for i in range(0, m + 1): 18 | d[i][0] = i 19 | a[i][0] = 3 20 | 21 | for j in range(0, n + 1): 22 | d[0][j] = j 23 | a[0][j] = 2 24 | 25 | # put marker to allow for easier reconstruction. - is used in the textbook 26 | a[0][0] = -1 27 | 28 | for i in range(1, m + 1): 29 | for j in range(1, n + 1): 30 | if s[i - 1] == t[j - 1]: # as strings are zero indexed 31 | d[i][j] = d[i - 1][j - 1] 32 | a[i][j] = 0 33 | else: 34 | d[i][j] = 1 + min(d[i][j - 1], d[i - 1][j], d[i - 1][j - 1]) 35 | if d[i][j] == d[i - 1][j - 1] + 1: 36 | a[i][j] = 1 37 | elif d[i][j] == d[i][j - 1] + 1: 38 | a[i][j] = 2 39 | else: 40 | a[i][j] = 3 41 | 42 | print("s1 : " + s) 43 | print("s2 : " + t) 44 | 45 | print("d:") 46 | prettyPrint(d) 47 | print("") 48 | print("a:") 49 | prettyPrint(a) 50 | 51 | b, c, distance = reconstruct(s, t, a) 52 | print(f"b: {b}") 53 | print(f"c: {c}") 54 | print(f"Edit distance: {distance}") 55 | 56 | 57 | def reconstruct(s: str, t: str, a: list[list]): 58 | b = "" 59 | c = "" 60 | i = len(s) 61 | j = len(t) 62 | 63 | marker = a[i][j] 64 | distance = 0 65 | while marker != -1: 66 | if marker == 0 or marker == 1: 67 | b += s[i - 1] 68 | c += t[j - 1] 69 | i -= 1 70 | j -= 1 71 | if marker == 1: 72 | distance += 1 73 | elif marker == 2: 74 | b += "-" 75 | c += t[j - 1] 76 | j -= 1 77 | distance += 1 78 | elif marker == 3: 79 | b += s[i - 1] 80 | c += "-" 81 | i -= 1 82 | distance += 1 83 | else: 84 | assert False 85 | 86 | marker = a[i][j] 87 | 88 | return (b[::-1], c[::-1], distance) 89 | 90 | 91 | if __name__ == "__main__": 92 | # edit_distance("ACCGGTATCCTAGGAC", "ACCTATCT--TAGGAC") 93 | # edit_distance_traced("ACCGGTATCCTAGGAC", "ACCTATCT--TAGGAC") 94 | # edit_distance("ABCDE", "BCDED") 95 | # edit_distance_traced("ABCDE", "BCDED") 96 | # edit_distance("ACTGGT", "ATGGCT") 97 | import sys 98 | 99 | if len(sys.argv) != 3: 100 | print("Run with edit_distance.py string1 string2") 101 | 102 | edit_distance(sys.argv[1], sys.argv[2]) 103 | -------------------------------------------------------------------------------- /dynamic_programming/seam_carving.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | 3 | 4 | def vertical_seam_carving(energies: "list[list[int]]", width: int, height: int): 5 | history = [[0 for i in range(width)] for j in range(height)] 6 | dp = energies 7 | 8 | # Start from second last row and iterate upwards 9 | for i in range(height - 2, -1, -1): 10 | for j in range(width): 11 | predecessors = [] 12 | 13 | # if position is at the left edge 14 | if j == 0: 15 | predecessors = [ 16 | (dp[i + 1][j], (i + 1, j)), 17 | (dp[i + 1][j + 1], (i + 1, j + 1)), 18 | ] 19 | # if position is at the right edge 20 | elif j == len(dp[0]) - 1: 21 | predecessors = [ 22 | (dp[i + 1][j], (i + 1, j)), 23 | (dp[i + 1][j - 1], (i + 1, j - 1)), 24 | ] 25 | # if position is in the middle 26 | else: 27 | predecessors = [ 28 | (dp[i + 1][j - 1], (i + 1, j - 1)), 29 | (dp[i + 1][j], (i + 1, j)), 30 | (dp[i + 1][j + 1], (i + 1, j + 1)), 31 | ] 32 | 33 | dp[i][j] += min(predecessors)[0] 34 | # Link the current position to the predecessor with the lowest energy 35 | history[i][j] = min(predecessors)[1] 36 | 37 | # Initialise a trail array to reconstruct the path 38 | trail = [[0 for i in range(width)] for j in range(height)] 39 | 40 | end_point = dp[0].index(min(dp[0])) 41 | trail[0][end_point] = 1 42 | path = history[0][end_point] 43 | 44 | # Iterate through the predecessors in the history array and mark optimal path with 1 45 | while type(path) == tuple: 46 | i, j = path 47 | trail[i][j] = 1 48 | path = history[i][j] 49 | 50 | print("Best Vertical Seam:") 51 | for row in trail: 52 | print(row) 53 | print("") 54 | 55 | 56 | def vertical_seam_carving_traced(energies: "list[list[int]]", width: int, height: int): 57 | pp = pprint.PrettyPrinter(indent=2) 58 | 59 | print("Start:") 60 | history = [[0 for i in range(width)] for j in range(height)] 61 | print("history") 62 | pp.pprint(history) 63 | dp = energies 64 | print("dp") 65 | pp.pprint(dp) 66 | 67 | print("start loop") 68 | # Start from second last row and iterate upwards 69 | for i in range(height - 2, -1, -1): 70 | for j in range(width): 71 | predecessors = [] 72 | 73 | # if position is at the left edge 74 | if j == 0: 75 | predecessors = [ 76 | (dp[i + 1][j], (i + 1, j)), 77 | (dp[i + 1][j + 1], (i + 1, j + 1)), 78 | ] 79 | # if position is at the right edge 80 | elif j == len(dp[0]) - 1: 81 | predecessors = [ 82 | (dp[i + 1][j], (i + 1, j)), 83 | (dp[i + 1][j - 1], (i + 1, j - 1)), 84 | ] 85 | # if position is in the middle 86 | else: 87 | predecessors = [ 88 | (dp[i + 1][j - 1], (i + 1, j - 1)), 89 | (dp[i + 1][j], (i + 1, j)), 90 | (dp[i + 1][j + 1], (i + 1, j + 1)), 91 | ] 92 | 93 | dp[i][j] += min(predecessors)[0] 94 | print("dp") 95 | pp.pprint(dp) 96 | # Link the current position to the predecessor with the lowest energy 97 | history[i][j] = min(predecessors)[1] 98 | print("history") 99 | pp.pprint(history) 100 | 101 | # Initialise a trail array to reconstruct the path 102 | trail = [[0 for i in range(width)] for j in range(height)] 103 | 104 | end_point = dp[0].index(min(dp[0])) 105 | trail[0][end_point] = 1 106 | path = history[0][end_point] 107 | 108 | # Iterate through the predecessors in the history array and mark optimal path with 1 109 | while type(path) == tuple: 110 | i, j = path 111 | trail[i][j] = 1 112 | path = history[i][j] 113 | 114 | print("Best Vertical Seam:") 115 | for row in trail: 116 | print(row) 117 | print("") 118 | 119 | 120 | def horizontal_seam_carving(energies: "list[list[int]]", width: int, height: int): 121 | history = [[0 for i in range(width)] for j in range(height)] 122 | dp = energies 123 | 124 | # Start from second last column and iterate upwards 125 | for i in range(width - 2, -1, -1): 126 | for j in range(height): 127 | predecessors = [] 128 | # if position is at the top edge 129 | if j == 0: 130 | predecessors = [ 131 | (dp[j][i + 1], (j, i + 1)), 132 | (dp[j + 1][i + 1], (j + 1, i + 1)), 133 | ] 134 | # if position is at the bottom edge 135 | elif j == height - 1: 136 | predecessors = [ 137 | (dp[j][i + 1], (j, i + 1)), 138 | (dp[j - 1][i + 1], (j - 1, i + 1)), 139 | ] 140 | # if position is in the middle 141 | else: 142 | predecessors = [ 143 | (dp[j][i + 1], (j, i + 1)), 144 | (dp[j - 1][i + 1], (j - 1, i + 1)), 145 | (dp[j + 1][i + 1], (j + 1, i + 1)), 146 | ] 147 | 148 | dp[j][i] += min(predecessors)[0] 149 | # Link the current position to the predecessor with the lowest energy 150 | history[j][i] = min(predecessors)[1] 151 | 152 | # Initialise a trail array to reconstruct the path 153 | trail = [[0 for i in range(width)] for j in range(height)] 154 | 155 | smallest_exit = min([row[0] for row in dp]) 156 | end_point = [row[0] for row in dp].index(smallest_exit) 157 | trail[end_point][0] = 1 158 | path = history[end_point][0] 159 | 160 | # Iterate through the predecessors in the history array and mark optimal path with 1 161 | while type(path) == tuple: 162 | i, j = path 163 | trail[i][j] = 1 164 | path = history[i][j] 165 | 166 | print("Best Horizontal Seam:") 167 | for row in trail: 168 | print(row) 169 | print("") 170 | 171 | 172 | def horizontal_seam_carving_traced( 173 | energies: "list[list[int]]", width: int, height: int 174 | ): 175 | pp = pprint.PrettyPrinter(indent=2) 176 | 177 | print("Start:") 178 | history = [[0 for i in range(width)] for j in range(height)] 179 | print("history") 180 | pp.pprint(history) 181 | dp = energies 182 | print("dp") 183 | pp.pprint(dp) 184 | 185 | # Start from second last column and iterate upwards 186 | for i in range(width - 2, -1, -1): 187 | for j in range(height): 188 | predecessors = [] 189 | # if position is at the top edge 190 | if j == 0: 191 | predecessors = [ 192 | (dp[j][i + 1], (j, i + 1)), 193 | (dp[j + 1][i + 1], (j + 1, i + 1)), 194 | ] 195 | # if position is at the bottom edge 196 | elif j == height - 1: 197 | predecessors = [ 198 | (dp[j][i + 1], (j, i + 1)), 199 | (dp[j - 1][i + 1], (j - 1, i + 1)), 200 | ] 201 | # if position is in the middle 202 | else: 203 | predecessors = [ 204 | (dp[j][i + 1], (j, i + 1)), 205 | (dp[j - 1][i + 1], (j - 1, i + 1)), 206 | (dp[j + 1][i + 1], (j + 1, i + 1)), 207 | ] 208 | 209 | dp[j][i] += min(predecessors)[0] 210 | print("dp") 211 | pp.pprint(dp) 212 | # Link the current position to the predecessor with the lowest energy 213 | history[j][i] = min(predecessors)[1] 214 | print("history") 215 | pp.pprint(history) 216 | 217 | # Initialise a trail array to reconstruct the path 218 | trail = [[0 for i in range(width)] for j in range(height)] 219 | 220 | smallest_exit = min([row[0] for row in dp]) 221 | end_point = [row[0] for row in dp].index(smallest_exit) 222 | trail[end_point][0] = 1 223 | path = history[end_point][0] 224 | 225 | # Iterate through the predecessors in the history array and mark optimal path with 1 226 | while type(path) == tuple: 227 | i, j = path 228 | trail[i][j] = 1 229 | path = history[i][j] 230 | 231 | print("Best Horizontal Seam:") 232 | for row in trail: 233 | print(row) 234 | print("") 235 | 236 | 237 | # Example. 238 | if __name__ == "__main__": 239 | energies = [ 240 | [0, 75, 6, 3, 75], 241 | [3, 0, 75, 75, 75], 242 | [75, 75, 0, 2, 75], 243 | [-50, 1, 1, 0, -5], 244 | [75, 4, 75, 2, 7], 245 | ] 246 | vertical_seam_carving_traced(energies, len(energies[0]), len(energies)) 247 | horizontal_seam_carving_traced(energies, len(energies[0]), len(energies)) 248 | -------------------------------------------------------------------------------- /graph_algos/FloydWarshall.py: -------------------------------------------------------------------------------- 1 | from asyncore import loop 2 | import math 3 | from random import randrange 4 | import pprint 5 | 6 | 7 | class GraphAdjacencyMatrix: 8 | def __init__(self, vertices, edges) -> None: 9 | self.vertices = vertices 10 | self.edges = edges 11 | 12 | 13 | def floyd_warshall( 14 | G: GraphAdjacencyMatrix, 15 | ) -> "tuple[list[list[int]], list[list[int]]]": 16 | # this assumes that the unreachable sections are initialised to math.inf 17 | d_curr = G.edges 18 | 19 | d_next = d_curr 20 | 21 | # Initialise each previous node to -1 22 | pi_curr = [ 23 | [-1 for _ in range(0, len(G.vertices))] for _ in range(0, len(G.vertices)) 24 | ] 25 | 26 | # Set up distance from each node to itself to 0. 27 | # Set up the previous node to itself to itself. 28 | for i in range(0, len(G.vertices)): 29 | d_curr[i][i] = 0 30 | pi_curr[i][i] = i 31 | 32 | # For each edge, initialise the previously found edge 33 | for i in range(len(G.vertices)): 34 | for j in range(len(G.vertices)): 35 | if G.edges[i][j] != math.inf and G.edges[i][j] != 0: 36 | pi_curr[i][j] = i 37 | 38 | # Copy pi_curr 39 | pi_next = pi_curr 40 | 41 | # as seen in lectures. 42 | for k in range(0, len(G.vertices)): 43 | for i in range(0, len(G.vertices)): 44 | for j in range(0, len(G.vertices)): 45 | d_next[i][j] = d_curr[i][j] 46 | pi_next[i][j] = pi_curr[i][j] 47 | if j != i and (d_curr[i][k] + d_curr[k][j]) < d_next[i][j]: 48 | d_next[i][j] = d_curr[i][k] + d_curr[k][j] 49 | pi_next[i][j] = pi_curr[k][j] 50 | 51 | d_curr = [row[:] for row in d_next] # deep copy 52 | 53 | return (d_next, pi_next) 54 | 55 | 56 | def floyd_warshall_traced( 57 | G: GraphAdjacencyMatrix, 58 | ) -> "tuple[list[list[int]], list[list[int]]]": 59 | pp = pprint.PrettyPrinter(indent=2) 60 | print("Start:") 61 | # this assumes that the unreachable sections are initialised to math.inf 62 | d_curr = G.edges 63 | 64 | d_next = d_curr 65 | 66 | # Initialise each previous node to -1 67 | pi_curr = [ 68 | [-1 for _ in range(0, len(G.vertices))] for _ in range(0, len(G.vertices)) 69 | ] 70 | 71 | # Set up distance from each node to itself to 0. 72 | # Set up the previous node to itself to itself. 73 | for i in range(0, len(G.vertices)): 74 | d_curr[i][i] = 0 75 | pi_curr[i][i] = i 76 | 77 | # For each edge, initialise the previously found edge 78 | for i in range(len(G.vertices)): 79 | for j in range(len(G.vertices)): 80 | if G.edges[i][j] != math.inf and G.edges[i][j] != 0: 81 | pi_curr[i][j] = i 82 | 83 | # Copy pi_curr 84 | pi_next = pi_curr 85 | 86 | print("d_curr") 87 | pp.pprint(d_curr) 88 | print("pi_curr") 89 | pp.pprint(pi_curr) 90 | print("d_next") 91 | pp.pprint(d_next) 92 | print("pi_next") 93 | pp.pprint(pi_next) 94 | 95 | # as seen in lectures. 96 | for k in range(0, len(G.vertices)): 97 | print("step k") 98 | for i in range(0, len(G.vertices)): 99 | print("step i") 100 | for j in range(0, len(G.vertices)): 101 | print("step j") 102 | d_next[i][j] = d_curr[i][j] 103 | pi_next[i][j] = pi_curr[i][j] 104 | if j != i and (d_curr[i][k] + d_curr[k][j]) < d_next[i][j]: 105 | d_next[i][j] = d_curr[i][k] + d_curr[k][j] 106 | pi_next[i][j] = pi_curr[k][j] 107 | print("d_next") 108 | pp.pprint(d_next) 109 | print("pi_next") 110 | pp.pprint(pi_next) 111 | 112 | d_curr = [row[:] for row in d_next] # deep copy 113 | 114 | return (d_next, pi_next) 115 | 116 | 117 | def reconstruct_path(pi: "list[list[int]]", start: int, end: int) -> "list[int]": 118 | """ 119 | Given the pi matrix returns the **Indices** for the vertices in the graph. 120 | You may need to loop through the graph's indices to get the values for path. 121 | """ 122 | 123 | if pi[start][end] == -1: 124 | return [] 125 | 126 | path = [end] 127 | while start != end: 128 | end = pi[start][end] 129 | path.append(end) 130 | 131 | return path[::-1] 132 | 133 | 134 | def reconstruct_path_traced(pi: "list[list[int]]", start: int, end: int) -> "list[int]": 135 | """ 136 | Given the pi matrix returns the **Indices** for the vertices in the graph. 137 | You may need to loop through the graph's indices to get the values for path. 138 | """ 139 | 140 | if pi[start][end] == -1: 141 | return [] 142 | 143 | path = [end] 144 | print("Start: \npath", path) 145 | while start != end: 146 | end = pi[start][end] 147 | path.append(end) 148 | print(path) 149 | 150 | return path[::-1] 151 | 152 | 153 | # Example. 154 | if __name__ == "__main__": 155 | weights = [ 156 | [0, math.inf, 6, 3, math.inf], 157 | [3, 0, math.inf, math.inf, math.inf], 158 | [math.inf, math.inf, 0, 2, math.inf], 159 | [math.inf, 1, 1, 0, math.inf], 160 | [math.inf, 4, math.inf, 2, 0], 161 | ] 162 | 163 | vertices = [1, 2, 3, 4, 5] 164 | graph = GraphAdjacencyMatrix(vertices, weights) 165 | path = [] 166 | 167 | (d, pi) = floyd_warshall_traced(graph) 168 | 169 | # reconstruct to print the correct vertices 170 | # we use 4 and 1 when referring to 5 and 2, as the vertices are 1 indexed 171 | for x in reconstruct_path_traced(pi, 4, 1): 172 | path.append(graph.vertices[x]) 173 | print(path) # expected [5,4,2] 174 | -------------------------------------------------------------------------------- /graph_algos/bfs.py: -------------------------------------------------------------------------------- 1 | from queue import Queue 2 | import math 3 | 4 | 5 | class GraphAdjacencyMatrix: 6 | def __init__(self, vertices, edges) -> None: 7 | self.vertices = vertices 8 | self.edges = edges 9 | 10 | def __str__(self): 11 | return ( 12 | "Vertices: " 13 | + str(self.vertices) 14 | + "\n" 15 | + "\n".join([str(edge) for edge in self.edges]) 16 | ) 17 | 18 | def adjacents(self, i): 19 | return [i for i, w in enumerate(self.edges[i]) if w != math.inf] 20 | 21 | 22 | def bfs(G: GraphAdjacencyMatrix): 23 | visited = [None] * len(G.vertices) 24 | Q = Queue() 25 | for v in range(len(G.vertices)): 26 | if visited[v] is None: 27 | bfsFromVertex(G, v, visited, Q) 28 | 29 | 30 | def bfsFromVertex(G, v, visited, Q): 31 | visited[v] = True 32 | Q.put(v) 33 | while not Q.empty(): 34 | u = Q.get() # dequeue 35 | for w in G.adjacents(u): 36 | if visited[w] is None: 37 | visited[w] = True 38 | Q.put(w) 39 | 40 | 41 | def bfs_traced(G: GraphAdjacencyMatrix): 42 | print("Start:") 43 | print(str(G)) 44 | visited = [None] * len(G.vertices) 45 | Q = Queue() 46 | print("Q", list(Q.queue)) 47 | print("visited", visited) 48 | for v in range(len(G.vertices)): 49 | if visited[v] is None: 50 | bfsFromVertex_traced(G, v, visited, Q) 51 | 52 | 53 | def bfsFromVertex_traced(G, v, visited, Q): 54 | print("bfsFromVertex") 55 | visited[v] = True 56 | Q.put(v) 57 | print("1 visited", visited) 58 | print("1 Q", list(Q.queue)) 59 | while not Q.empty(): 60 | u = Q.get() # dequeue 61 | print("2 u", u) 62 | print("2 Q", list(Q.queue)) 63 | for w in G.adjacents(u): 64 | if visited[w] is None: 65 | visited[w] = True 66 | Q.put(w) 67 | print("3 w, visited", w, visited) 68 | print("3 Q", list(Q.queue)) 69 | 70 | 71 | if __name__ == "__main__": 72 | weights = [ 73 | [0, math.inf, 6, 3, math.inf], 74 | [3, 0, math.inf, math.inf, math.inf], 75 | [math.inf, math.inf, 0, 2, math.inf], 76 | [math.inf, 1, 1, 0, math.inf], 77 | [math.inf, 4, math.inf, 2, 0], 78 | ] 79 | 80 | vertices = [1, 2, 3, 4, 5] 81 | graph = GraphAdjacencyMatrix(vertices, weights) 82 | 83 | bfs_traced(graph) 84 | -------------------------------------------------------------------------------- /graph_algos/dfs.py: -------------------------------------------------------------------------------- 1 | from queue import LifoQueue 2 | import math 3 | 4 | 5 | class GraphAdjacencyMatrix: 6 | def __init__(self, vertices, edges) -> None: 7 | self.vertices = vertices 8 | self.edges = edges 9 | 10 | def __str__(self): 11 | return ( 12 | "Vertices: " 13 | + str(self.vertices) 14 | + "\n" 15 | + "\n".join([str(edge) for edge in self.edges]) 16 | ) 17 | 18 | def adjacents(self, i): 19 | return [i for i, w in enumerate(self.edges[i]) if w != math.inf] 20 | 21 | 22 | def dfs(G: GraphAdjacencyMatrix): 23 | visited = [None] * len(G.vertices) 24 | # Stack! 25 | S = LifoQueue() 26 | for v in range(len(G.vertices)): 27 | if visited[v] is None: 28 | dfsFromVertex(G, v, visited, S) 29 | 30 | 31 | def dfsFromVertex(G, v, visited, S): 32 | visited[v] = True 33 | S.put(v) 34 | while not S.empty(): 35 | u = S.get() # dequeue 36 | for w in G.adjacents(u): 37 | if visited[w] is None: 38 | visited[w] = True 39 | S.put(w) 40 | 41 | 42 | def dfs_traced(G: GraphAdjacencyMatrix): 43 | print("Start:") 44 | print(str(G)) 45 | visited = [None] * len(G.vertices) 46 | # Stack! 47 | S = LifoQueue() 48 | print("Q", list(S.queue)) 49 | print("visited", visited) 50 | for v in range(len(G.vertices)): 51 | if visited[v] is None: 52 | dfsFromVertex_traced(G, v, visited, S) 53 | 54 | 55 | def dfsFromVertex_traced(G, v, visited, S): 56 | print("dfsFromVertex") 57 | visited[v] = True 58 | S.put(v) 59 | print("1 visited", visited) 60 | print("1 Q", list(S.queue)) 61 | while not S.empty(): 62 | u = S.get() # dequeue 63 | print("2 u", u) 64 | print("2 Q", list(S.queue)) 65 | print("2 adjacents", G.adjacents(u)) 66 | for w in G.adjacents(u): 67 | if visited[w] is None: 68 | visited[w] = True 69 | S.put(w) 70 | print("3 w, visited", w, visited) 71 | print("3 Q", list(S.queue)) 72 | 73 | 74 | if __name__ == "__main__": 75 | weights = [ 76 | [0, math.inf, 6, 3, math.inf], 77 | [3, 0, math.inf, math.inf, math.inf], 78 | [math.inf, math.inf, 0, 2, math.inf], 79 | [math.inf, 1, 1, 0, math.inf], 80 | [math.inf, 4, math.inf, 2, 0], 81 | ] 82 | 83 | vertices = [1, 2, 3, 4, 5] 84 | graph = GraphAdjacencyMatrix(vertices, weights) 85 | dfs_traced(graph) 86 | 87 | weights2 = [ 88 | [0, math.inf, math.inf, math.inf, 1], 89 | [1, 0, math.inf, 1, 1], 90 | [1, math.inf, 0, 1, 1], 91 | [math.inf, math.inf, math.inf, 0, 1], 92 | [math.inf, math.inf, math.inf, math.inf, math.inf], 93 | ] 94 | 95 | vertices2 = [1, 2, 3, 4, 5] 96 | graph2 = GraphAdjacencyMatrix(vertices2, weights2) 97 | 98 | dfsFromVertex_traced(graph2, 0, [None] * len(vertices2), LifoQueue()) 99 | -------------------------------------------------------------------------------- /graph_algos/dijkstra.py: -------------------------------------------------------------------------------- 1 | # Source: https://stackabuse.com/dijkstras-algorithm-in-python/ 2 | 3 | from queue import PriorityQueue 4 | 5 | 6 | class Graph: 7 | def __init__(self, num_of_vertices): 8 | self.v = num_of_vertices 9 | self.edges = [ 10 | [-1 for i in range(num_of_vertices)] for j in range(num_of_vertices) 11 | ] 12 | self.visited = [] 13 | 14 | def add_edge(self, u, v, weight): 15 | self.edges[u][v] = weight 16 | self.edges[v][u] = weight 17 | 18 | def dijkstra(self, start_vertex): 19 | D = {v: float("inf") for v in range(self.v)} 20 | pi = {v: None for v in range(self.v)} 21 | D[start_vertex] = 0 22 | 23 | pq = PriorityQueue() 24 | pq.put((0, start_vertex)) 25 | 26 | while not pq.empty(): 27 | (dist, current_vertex) = pq.get() 28 | self.visited.append(current_vertex) 29 | 30 | for neighbor in range(self.v): 31 | if self.edges[current_vertex][neighbor] != -1: 32 | distance = self.edges[current_vertex][neighbor] 33 | if neighbor not in self.visited: 34 | old_cost = D[neighbor] 35 | new_cost = D[current_vertex] + distance 36 | if new_cost < old_cost: 37 | pi[neighbor] = current_vertex 38 | pq.put((new_cost, neighbor)) 39 | D[neighbor] = new_cost 40 | return D 41 | 42 | def dijkstra_traced(self, start_vertex): 43 | print("Start:") 44 | 45 | # InitializeSingleSource 46 | D = {v: float("inf") for v in range(self.v)} 47 | pi = {v: None for v in range(self.v)} 48 | D[start_vertex] = 0 49 | # 50 | 51 | print("D", D) 52 | print("pi", pi) 53 | 54 | pq = PriorityQueue() 55 | pq.put((0, start_vertex)) 56 | print("priority queue", pq) 57 | 58 | while not pq.empty(): 59 | (dist, current_vertex) = pq.get() 60 | self.visited.append(current_vertex) 61 | 62 | # for x in Out(u) -> x == v == neighbor 63 | for neighbor in range(self.v): 64 | # u == current_vertex 65 | if self.edges[current_vertex][neighbor] != -1: 66 | # 67 | # Relax 68 | distance = self.edges[current_vertex][neighbor] 69 | if neighbor not in self.visited: 70 | old_cost = D[neighbor] 71 | if old_cost == float("inf"): 72 | print("inf at", neighbor) 73 | new_cost = D[current_vertex] + distance 74 | if new_cost < old_cost: 75 | # Q.insertItem(d[v], v) or Q.reduceKey(d[v], v) 76 | pi[neighbor] = current_vertex 77 | pq.put((new_cost, neighbor)) 78 | D[neighbor] = new_cost 79 | # 80 | print("step") 81 | print("D", D) 82 | print("pi", pi) 83 | print("priority queue", pq.queue) 84 | return D 85 | 86 | 87 | if __name__ == "__main__": 88 | g = Graph(9) 89 | g.add_edge(0, 1, 4) 90 | g.add_edge(0, 6, 7) 91 | g.add_edge(1, 6, 11) 92 | g.add_edge(1, 7, 20) 93 | g.add_edge(1, 2, 9) 94 | g.add_edge(2, 3, 6) 95 | g.add_edge(2, 4, 2) 96 | g.add_edge(3, 4, 10) 97 | g.add_edge(3, 5, 5) 98 | g.add_edge(4, 5, 15) 99 | g.add_edge(4, 7, 1) 100 | g.add_edge(4, 8, 5) 101 | g.add_edge(5, 8, 12) 102 | g.add_edge(6, 7, 1) 103 | g.add_edge(7, 8, 3) 104 | 105 | D = g.dijkstra_traced(0) 106 | print(D) 107 | # {0: 0, 1: 4, 2: 11, 3: 17, 4: 9, 5: 22, 6: 7, 7: 8, 8: 11} 108 | -------------------------------------------------------------------------------- /misc/maxheap.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import networkx as nx 3 | from networkx.drawing.nx_pydot import graphviz_layout 4 | import os 5 | 6 | 7 | def maxHeapify(arr, i): 8 | l = 2 * i + 1 9 | r = 2 * i + 2 10 | largest = i 11 | if l < len(arr) and arr[l] > arr[i]: 12 | largest = l 13 | if r < len(arr) and arr[r] > arr[largest]: 14 | largest = r 15 | if largest != i: 16 | arr[i], arr[largest] = arr[largest], arr[i] 17 | maxHeapify(arr, largest) 18 | 19 | 20 | def heapExtractMax(arr): 21 | ret = arr[0] 22 | arr[0], arr[-1] = arr[-1], arr[0] 23 | maxHeapify(arr, 0) 24 | return ret 25 | 26 | 27 | def heapMaximum(A): 28 | return A[0] 29 | 30 | 31 | def parent(j): 32 | return (j - 1) // 2 33 | 34 | 35 | def left(i): 36 | return 2 * i + 1 37 | 38 | 39 | def right(i): 40 | return 2 * i + 2 41 | 42 | 43 | def maxHeapInsert(A, k): 44 | A.append(k) 45 | j = len(A) - 1 46 | while j != 0 and A[j] > A[(j - 1) // 2]: 47 | A[j], A[(j - 1) // 2] = A[(j - 1) // 2], A[j] 48 | 49 | 50 | def buildMaxHeap(A): 51 | for i in range(len(A) // 2, -1, -1): 52 | maxHeapify(A, i) 53 | 54 | 55 | def draw_tree(list): 56 | G = nx.DiGraph() 57 | G.add_nodes_from(list) 58 | for i in range(len(list) // 2): 59 | G.add_edge(list[i], list[(i << 1) + 1]) 60 | G.add_edge(list[i], list[(i << 1) + 2]) 61 | 62 | plt1, ax = plt.subplots(figsize=(10, 10)) 63 | pos = graphviz_layout(G, prog="dot") 64 | nx.draw(G, pos, with_labels=True, ax=ax) 65 | plt1.show() 66 | input() 67 | # plt1.savefig(os.path.join(os.getcwd(), "maxheap.png")) # uncommment to save tree. 68 | 69 | 70 | if __name__ == "__main__": 71 | list = [1, 2, 3, 4, 5, 6, 7] # put ur list here 72 | 73 | buildMaxHeap(list) 74 | print(list) 75 | draw_tree( 76 | list 77 | ) # put this anywhere during runtime to visualise tree during intermediary steps. 78 | 79 | # os.system("maxheap.png") 80 | # set up visualisation 81 | -------------------------------------------------------------------------------- /parsing_algos/cyk.py: -------------------------------------------------------------------------------- 1 | from ast import arg, parse 2 | from lib2to3.pgen2 import grammar 3 | from typing import Dict, List 4 | from enum import Enum, auto 5 | import pprint 6 | import pandas as pd 7 | 8 | 9 | class NT: 10 | def __init__(self, l: Enum, r: Enum): 11 | self.l = l 12 | self.r = r 13 | 14 | 15 | class Rule: 16 | def __init__(self, LHS: Enum, RHS, terminal: bool = False): # RHS: NT or str 17 | self.LHS = LHS 18 | self.RHS = RHS 19 | self.terminal = terminal # flag to mark node as terminal 20 | 21 | 22 | def cyk( 23 | s: List, G: "list[Rule]" 24 | ) -> "list[list[list[Enum]]]": # G should really be a set of Rules but I cba to fix it rn 25 | n = len(s) 26 | table = [[[] for _ in range(n - 1)] for _ in range(n - 1)] 27 | 28 | for j in range(1, n + 1): # columns 29 | for rule in G: 30 | if rule.terminal: 31 | if s[j - 1] == rule.RHS: 32 | table[j - 1][j].append(rule.LHS) # diagonal cell 33 | 34 | for i in range(j - 2, -1, -1): # rows 35 | for k in range(i + 1, j): # possible splits 36 | for rule in G: 37 | if not rule.terminal: 38 | if rule.RHS.l in table[i][k] and rule.RHS.r in table[k][j]: 39 | table[i][j].append(rule.LHS) 40 | 41 | for i in range(len(table)): 42 | del table[i][0] 43 | del table[-1] 44 | 45 | return table 46 | 47 | 48 | def cyk_parser( 49 | s: List, G: "list[Rule]" 50 | ) -> "list[list[list[tuple[Enum, tuple[int, int], tuple[int, int]]]]]": # G should really be a set of Rules but I cba to fix it rn 51 | n = len(s) 52 | table = [[[] for _ in range(n + 1)] for _ in range(n + 1)] 53 | 54 | for j in range(1, n + 1): # columns 55 | for rule in G: 56 | if rule.terminal: 57 | if s[j - 1] == rule.RHS: 58 | table[j - 1][j].append( 59 | (rule.LHS, (j - 1, j), (j - 1, j)) 60 | ) # diagonal cell 61 | 62 | for i in range(j - 2, -1, -1): # rows 63 | for k in range(i + 1, j): # possible splits 64 | for rule in G: 65 | if not rule.terminal: 66 | if any([rule.RHS.l == x[0] for x in table[i][k]]) and any( 67 | [rule.RHS.r == x[0] for x in table[k][j]] 68 | ): 69 | table[i][j].append((rule.LHS, (i, k), (k, j))) 70 | 71 | for i in range(len(table)): 72 | del table[i][0] 73 | del table[-1] 74 | 75 | return table 76 | 77 | 78 | def cyk_traced(s: List, G: "list[Rule]") -> "list[list[list[Enum]]]": 79 | print("Start:", s) 80 | n = len(s) 81 | table = [[[] for _ in range(n + 1)] for _ in range(n + 1)] 82 | 83 | for j in range(1, n + 1): # columns 84 | for rule in G: 85 | if rule.terminal: 86 | if s[j - 1] == rule.RHS: 87 | print(n, j) 88 | table[j - 1][j].append(rule.LHS) # diagonal cell 89 | print("diag", rule.LHS, j - 1, j) 90 | 91 | for i in range(j - 2, -1, -1): # rows 92 | for k in range(i + 1, j): # possible splits 93 | for rule in G: 94 | if not rule.terminal: 95 | if rule.RHS.l in table[i][k] and rule.RHS.r in table[k][j]: 96 | table[i][j].append(rule.LHS) 97 | print("non-diag", rule.LHS, i, j) 98 | print("non-diag l", rule.RHS.l, i, k) 99 | print("non-diag r", rule.RHS.r, k, j) 100 | 101 | for i in range(len(table)): 102 | del table[i][0] 103 | del table[-1] 104 | 105 | table = pd.DataFrame(table, s, s) 106 | print(table) 107 | return table 108 | 109 | 110 | def cyk_parser_traced(s: List, G: "list[Rule]"): 111 | print("Start:", s) 112 | n = len(s) 113 | table = [[[] for _ in range(n + 1)] for _ in range(n + 1)] 114 | 115 | for j in range(1, n + 1): # columns 116 | for rule in G: 117 | if rule.terminal: 118 | if s[j - 1] == rule.RHS: 119 | print(n, j) 120 | table[j - 1][j].append( 121 | (rule.LHS, (j - 1, j), (j - 1, j)) 122 | ) # diagonal cell 123 | print("diag", rule.LHS, j - 1, j) 124 | 125 | for i in range(j - 2, -1, -1): # rows 126 | for k in range(i + 1, j): # possible splits 127 | for rule in G: 128 | if not rule.terminal: 129 | if any([rule.RHS.l == x[0] for x in table[i][k]]) and any( 130 | [rule.RHS.r == x[0] for x in table[k][j]] 131 | ): 132 | table[i][j].append((rule.LHS, (i, k), (k, j))) 133 | print("non-diag", rule.LHS, i, j) 134 | print("non-diag l", rule.RHS.l, i, k) 135 | print("non-diag r", rule.RHS.r, k, j) 136 | 137 | for i in range(len(table)): 138 | del table[i][0] 139 | del table[-1] 140 | 141 | table = pd.DataFrame(table, s, s) 142 | print(table) 143 | return table 144 | 145 | 146 | def parseFromFile(file: str, parse: str): 147 | grammarRules = extractGrammar(file) 148 | 149 | return cyk_parser_traced(parse.split(), grammarRules) 150 | 151 | def cykFromFile(file: str, parse:str): 152 | grammarRules = extractGrammar(file) 153 | 154 | return cyk_traced(parse.split(), grammarRules) 155 | 156 | 157 | def extractGrammar(file): 158 | content = [] 159 | with open(file, "r") as f: 160 | content = f.readlines() 161 | 162 | ntsStrings = dict() 163 | rules = [] 164 | for line in content: 165 | split = line.split("->") 166 | ntsStrings[split[0].strip()] = split[0].strip() 167 | rules.append([split[0].strip()] + split[1].split()) 168 | 169 | NTs = Enum("NTs", ntsStrings) 170 | 171 | grammarRules = [] 172 | for rule in rules: 173 | # case that rule is a terminal 174 | if len(rule) == 2: 175 | grammarRules.append(Rule(NTs(rule[0]), rule[1], True)) 176 | else: 177 | grammarRules.append(Rule(NTs(rule[0]), NT(NTs(rule[1]), NTs(rule[2])))) 178 | return grammarRules 179 | 180 | 181 | 182 | 183 | 184 | if __name__ == "__main__": 185 | from sys import argv 186 | 187 | # out = cyk_parser(["my", "very", "heavy", "orange", "book"], grammarRules) 188 | # print(pprint.pformat(out)) 189 | # cyk_traced(["my", "very", "heavy", "orange", "book"], grammarRules) 190 | if len(argv) == 1: 191 | print("Call function using python3 cyk.py inputfile.txt \"String to parse\" outfile.md") 192 | print("For example") 193 | print("python3 cyk.py input.txt \"I ate the salad with a fork\" out1.md") 194 | print("An example input file is provided. The numbers next the terminal/ non terminals indicate where they were taken from") 195 | print("Remember that the left side is 0 indexed, the top is 1 indexed.") 196 | else: 197 | out = parseFromFile(argv[1], argv[2]) 198 | if len(argv) == 3: 199 | 200 | out.to_markdown(argv[3]) 201 | else: 202 | out.to_markdown("out.md") 203 | 204 | -------------------------------------------------------------------------------- /parsing_algos/input.txt: -------------------------------------------------------------------------------- 1 | S -> NP VP 2 | PP -> Pre NP 3 | S -> I X 4 | X -> VP PP 5 | NP -> Det N 6 | VP -> Ate NP 7 | VP -> ate 8 | I -> I 9 | Det -> the 10 | Det -> a 11 | N -> fork 12 | N -> salad 13 | Pre -> with 14 | Ate -> ate -------------------------------------------------------------------------------- /parsing_algos/ll1.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from queue import LifoQueue 3 | from enum import Enum, auto 4 | import pprint 5 | 6 | 7 | class LngElem: 8 | def __init__(self, val, terminal: bool = False): 9 | self.val = val 10 | self.terminal = terminal 11 | 12 | 13 | class Rule: 14 | def __init__( 15 | self, LHS: LngElem, RHS: "list[LngElem]", terminal: bool = False 16 | ): # different from the CYK version! 17 | self.LHS = LHS 18 | self.RHS = RHS 19 | self.terminal = terminal 20 | 21 | 22 | def ll1_parse( 23 | table: "dict[Enum, list[Rule]]", S: Enum, in_str: str, parse_cols: dict 24 | ) -> bool: 25 | terminals = parse_cols 26 | if S in terminals: 27 | return Exception("Error: start symbol is terminal") 28 | pos = 0 29 | stack = LifoQueue() 30 | stack.put(S) 31 | while not stack.empty(): 32 | x = stack.queue[-1] 33 | if x not in terminals: # Lookup case 34 | if table[x][parse_cols[in_str[pos]]] == None: 35 | return Exception("Error 1: looked up blank cell") 36 | else: # rule x -> beta 37 | stack.get() 38 | for symbol in reversed(table[x][parse_cols[in_str[pos]]].RHS): 39 | stack.put(symbol) 40 | else: # Match case (x is terminal) 41 | if x == in_str[pos]: 42 | stack.get() 43 | pos += 1 44 | else: 45 | if not x in terminals: 46 | return Exception("Error 2: terminal expected", x, "found") 47 | return Exception("Error 2: expected", x, "found", in_str[pos]) 48 | if in_str[pos] == "$": 49 | return True 50 | else: 51 | return Exception("Error 3: stack empty before string empty") 52 | 53 | 54 | def ll1_parse_traced( 55 | table: "dict[Enum, list[Rule]]", S: Enum, in_str: str, parse_cols: dict 56 | ) -> bool: 57 | terminals = parse_cols 58 | if S in terminals: 59 | return Exception("Error: start symbol is terminal") 60 | pos = 0 61 | stack = LifoQueue() 62 | stack.put(S) 63 | while not stack.empty(): 64 | x = stack.queue[-1] 65 | if x not in terminals: # Lookup case 66 | if table[x][parse_cols[in_str[pos]]] == None: 67 | return Exception("Error 1: looked up blank cell") 68 | else: # rule x -> beta 69 | got = stack.get() 70 | print( 71 | f"Lookup: {in_str[pos]}, {got} => {table[x][parse_cols[in_str[pos]]].RHS}" 72 | ) 73 | for symbol in reversed(table[x][parse_cols[in_str[pos]]].RHS): 74 | stack.put(symbol) 75 | else: # Match case (x is terminal) 76 | if x == in_str[pos]: 77 | print(f"matched: {stack.get()}") # removes item from stack 78 | pos += 1 79 | else: 80 | if not x in terminals: 81 | return Exception("Error 2: terminal expected", x, "found") 82 | return Exception("Error 2: expected", x, "found", in_str[pos]) 83 | print(f"input: {in_str[pos:]}") 84 | print("stack", stack.queue[::-1]) 85 | print("-" * 10) 86 | if in_str[pos] == "$": 87 | return True 88 | else: 89 | return Exception("Error 3: stack empty before string empty") 90 | 91 | 92 | def ll1_parse_lightly_traced( 93 | table: "dict[Enum, list[Rule]]", S: Enum, in_str: str, parse_cols: dict 94 | ) -> bool: 95 | terminals = parse_cols 96 | if S in terminals: 97 | return Exception("Error: start symbol is terminal") 98 | pos = 0 99 | stack = LifoQueue() 100 | stack.put(S) 101 | while not stack.empty(): 102 | x = stack.queue[-1] 103 | if x not in terminals: # Lookup case (X is a non terminal) 104 | if table[x][parse_cols[in_str[pos]]] == None: 105 | return Exception("Error 1: looked up blank cell") 106 | else: # rule x -> beta 107 | got = stack.get() 108 | for symbol in reversed(table[x][parse_cols[in_str[pos]]].RHS): 109 | stack.put(symbol) 110 | else: # Match case (x is terminal) 111 | if x == in_str[pos]: 112 | stack.get() 113 | pos += 1 114 | else: 115 | if not x in terminals: 116 | return Exception("Error 2: terminal expected", x, "found") 117 | return Exception("Error 2: expected", x, "found", in_str[pos]) 118 | print("stack", stack.queue) 119 | if in_str[pos] == "$": 120 | return True 121 | else: 122 | return Exception("Error 3: stack empty before string empty") 123 | 124 | 125 | if __name__ == "__main__": 126 | 127 | class NTs(Enum): 128 | S = auto() 129 | T = auto() 130 | 131 | parse_cols = {"(": 0, ")": 1, "$": 2} 132 | 133 | """ 134 | e = epsilon 135 | __|____(_____|____)___|___$____| 136 | S | S -> TS | S -> e | S -> e | 137 | T | T -> (S) | | | 138 | """ 139 | 140 | parseTable = { 141 | NTs.S: [Rule(NTs.S, [NTs.T, NTs.S]), Rule(NTs.S, []), Rule(NTs.S, [])], 142 | NTs.T: [Rule(NTs.T, ["(", NTs.S, ")"]), None, None], 143 | } 144 | 145 | print(ll1_parse_traced(parseTable, NTs.S, "(())$", parse_cols)) 146 | # print(ll1_parse_traced(parseTable, NTs.S, ")$", parse_cols)) 147 | # print(ll1_parse_traced(parseTable, NTs.S, "($", parse_cols)) 148 | # print(ll1_parse_lightly_traced(parseTable, NTs.S, "(())$", parse_cols)) 149 | -------------------------------------------------------------------------------- /parsing_algos/out.md: -------------------------------------------------------------------------------- 1 | | | I | ate | the | salad | with | a | fork | 2 | |:------|:---------------------------------|:-----------------------------------------------------------------------|:-------------------------------------|:-----------------------------------|:-------------------------------------|:-------------------------------------|:-----------------------------------| 3 | | I | [(, (0, 1), (0, 1))] | [] | [] | [] | [] | [] | [(, (0, 1), (1, 7))] | 4 | | ate | [] | [(, (1, 2), (1, 2)), (, (1, 2), (1, 2))] | [] | [(, (1, 2), (2, 4))] | [] | [] | [(, (1, 4), (4, 7))] | 5 | | the | [] | [] | [(, (2, 3), (2, 3))] | [(, (2, 3), (3, 4))] | [] | [] | [] | 6 | | salad | [] | [] | [] | [(, (3, 4), (3, 4))] | [] | [] | [] | 7 | | with | [] | [] | [] | [] | [(, (4, 5), (4, 5))] | [] | [(, (4, 5), (5, 7))] | 8 | | a | [] | [] | [] | [] | [] | [(, (5, 6), (5, 6))] | [(, (5, 6), (6, 7))] | 9 | | fork | [] | [] | [] | [] | [] | [] | [(, (6, 7), (6, 7))] | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "black" 3 | version = "22.3.0" 4 | description = "The uncompromising code formatter." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=3.6.2" 8 | 9 | [package.dependencies] 10 | click = ">=8.0.0" 11 | mypy-extensions = ">=0.4.3" 12 | pathspec = ">=0.9.0" 13 | platformdirs = ">=2" 14 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 15 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 16 | 17 | [package.extras] 18 | colorama = ["colorama (>=0.4.3)"] 19 | d = ["aiohttp (>=3.7.4)"] 20 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 21 | uvloop = ["uvloop (>=0.15.2)"] 22 | 23 | [[package]] 24 | name = "click" 25 | version = "8.1.3" 26 | description = "Composable command line interface toolkit" 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=3.7" 30 | 31 | [package.dependencies] 32 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 33 | 34 | [[package]] 35 | name = "colorama" 36 | version = "0.4.4" 37 | description = "Cross-platform colored terminal text." 38 | category = "dev" 39 | optional = false 40 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 41 | 42 | [[package]] 43 | name = "cycler" 44 | version = "0.11.0" 45 | description = "Composable style cycles" 46 | category = "main" 47 | optional = false 48 | python-versions = ">=3.6" 49 | 50 | [[package]] 51 | name = "fonttools" 52 | version = "4.33.3" 53 | description = "Tools to manipulate font files" 54 | category = "main" 55 | optional = false 56 | python-versions = ">=3.7" 57 | 58 | [package.extras] 59 | all = ["fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "zopfli (>=0.1.4)", "lz4 (>=1.7.4.2)", "matplotlib", "sympy", "skia-pathops (>=0.5.0)", "uharfbuzz (>=0.23.0)", "brotlicffi (>=0.8.0)", "scipy", "brotli (>=1.0.1)", "munkres", "unicodedata2 (>=14.0.0)", "xattr"] 60 | graphite = ["lz4 (>=1.7.4.2)"] 61 | interpolatable = ["scipy", "munkres"] 62 | lxml = ["lxml (>=4.0,<5)"] 63 | pathops = ["skia-pathops (>=0.5.0)"] 64 | plot = ["matplotlib"] 65 | repacker = ["uharfbuzz (>=0.23.0)"] 66 | symfont = ["sympy"] 67 | type1 = ["xattr"] 68 | ufo = ["fs (>=2.2.0,<3)"] 69 | unicode = ["unicodedata2 (>=14.0.0)"] 70 | woff = ["zopfli (>=0.1.4)", "brotlicffi (>=0.8.0)", "brotli (>=1.0.1)"] 71 | 72 | [[package]] 73 | name = "kiwisolver" 74 | version = "1.4.2" 75 | description = "A fast implementation of the Cassowary constraint solver" 76 | category = "main" 77 | optional = false 78 | python-versions = ">=3.7" 79 | 80 | [[package]] 81 | name = "matplotlib" 82 | version = "3.5.2" 83 | description = "Python plotting package" 84 | category = "main" 85 | optional = false 86 | python-versions = ">=3.7" 87 | 88 | [package.dependencies] 89 | cycler = ">=0.10" 90 | fonttools = ">=4.22.0" 91 | kiwisolver = ">=1.0.1" 92 | numpy = ">=1.17" 93 | packaging = ">=20.0" 94 | pillow = ">=6.2.0" 95 | pyparsing = ">=2.2.1" 96 | python-dateutil = ">=2.7" 97 | setuptools_scm = ">=4" 98 | 99 | [[package]] 100 | name = "mypy-extensions" 101 | version = "0.4.3" 102 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 103 | category = "dev" 104 | optional = false 105 | python-versions = "*" 106 | 107 | [[package]] 108 | name = "networkx" 109 | version = "2.8" 110 | description = "Python package for creating and manipulating graphs and networks" 111 | category = "main" 112 | optional = false 113 | python-versions = ">=3.8" 114 | 115 | [package.extras] 116 | default = ["numpy (>=1.19)", "scipy (>=1.8)", "matplotlib (>=3.4)", "pandas (>=1.3)"] 117 | developer = ["pre-commit (>=2.18)", "mypy (>=0.942)"] 118 | doc = ["sphinx (>=4.5)", "pydata-sphinx-theme (>=0.8.1)", "sphinx-gallery (>=0.10)", "numpydoc (>=1.2)", "pillow (>=9.1)", "nb2plots (>=0.6)", "texext (>=0.6.6)"] 119 | extra = ["lxml (>=4.6)", "pygraphviz (>=1.9)", "pydot (>=1.4.2)", "sympy (>=1.10)"] 120 | test = ["pytest (>=7.1)", "pytest-cov (>=3.0)", "codecov (>=2.1)"] 121 | 122 | [[package]] 123 | name = "numpy" 124 | version = "1.22.3" 125 | description = "NumPy is the fundamental package for array computing with Python." 126 | category = "main" 127 | optional = false 128 | python-versions = ">=3.8" 129 | 130 | [[package]] 131 | name = "packaging" 132 | version = "21.3" 133 | description = "Core utilities for Python packages" 134 | category = "main" 135 | optional = false 136 | python-versions = ">=3.6" 137 | 138 | [package.dependencies] 139 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 140 | 141 | [[package]] 142 | name = "pandas" 143 | version = "1.4.2" 144 | description = "Powerful data structures for data analysis, time series, and statistics" 145 | category = "main" 146 | optional = false 147 | python-versions = ">=3.8" 148 | 149 | [package.dependencies] 150 | numpy = [ 151 | {version = ">=1.18.5", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, 152 | {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""}, 153 | {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""}, 154 | {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, 155 | ] 156 | python-dateutil = ">=2.8.1" 157 | pytz = ">=2020.1" 158 | 159 | [package.extras] 160 | test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] 161 | 162 | [[package]] 163 | name = "pathspec" 164 | version = "0.9.0" 165 | description = "Utility library for gitignore style pattern matching of file paths." 166 | category = "dev" 167 | optional = false 168 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 169 | 170 | [[package]] 171 | name = "pillow" 172 | version = "9.1.0" 173 | description = "Python Imaging Library (Fork)" 174 | category = "main" 175 | optional = false 176 | python-versions = ">=3.7" 177 | 178 | [package.extras] 179 | docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"] 180 | tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] 181 | 182 | [[package]] 183 | name = "platformdirs" 184 | version = "2.5.2" 185 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 186 | category = "dev" 187 | optional = false 188 | python-versions = ">=3.7" 189 | 190 | [package.extras] 191 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] 192 | test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] 193 | 194 | [[package]] 195 | name = "pydot" 196 | version = "1.4.2" 197 | description = "Python interface to Graphviz's Dot" 198 | category = "main" 199 | optional = false 200 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 201 | 202 | [package.dependencies] 203 | pyparsing = ">=2.1.4" 204 | 205 | [[package]] 206 | name = "pyparsing" 207 | version = "3.0.8" 208 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 209 | category = "main" 210 | optional = false 211 | python-versions = ">=3.6.8" 212 | 213 | [package.extras] 214 | diagrams = ["railroad-diagrams", "jinja2"] 215 | 216 | [[package]] 217 | name = "python-dateutil" 218 | version = "2.8.2" 219 | description = "Extensions to the standard Python datetime module" 220 | category = "main" 221 | optional = false 222 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 223 | 224 | [package.dependencies] 225 | six = ">=1.5" 226 | 227 | [[package]] 228 | name = "pytz" 229 | version = "2022.1" 230 | description = "World timezone definitions, modern and historical" 231 | category = "main" 232 | optional = false 233 | python-versions = "*" 234 | 235 | [[package]] 236 | name = "setuptools-scm" 237 | version = "6.4.2" 238 | description = "the blessed package to manage your versions by scm tags" 239 | category = "main" 240 | optional = false 241 | python-versions = ">=3.6" 242 | 243 | [package.dependencies] 244 | packaging = ">=20.0" 245 | tomli = ">=1.0.0" 246 | 247 | [package.extras] 248 | test = ["pytest (>=6.2)", "virtualenv (>20)"] 249 | toml = ["setuptools (>=42)"] 250 | 251 | [[package]] 252 | name = "six" 253 | version = "1.16.0" 254 | description = "Python 2 and 3 compatibility utilities" 255 | category = "main" 256 | optional = false 257 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 258 | 259 | [[package]] 260 | name = "tomli" 261 | version = "2.0.1" 262 | description = "A lil' TOML parser" 263 | category = "main" 264 | optional = false 265 | python-versions = ">=3.7" 266 | 267 | [[package]] 268 | name = "typing-extensions" 269 | version = "4.2.0" 270 | description = "Backported and Experimental Type Hints for Python 3.7+" 271 | category = "dev" 272 | optional = false 273 | python-versions = ">=3.7" 274 | 275 | [metadata] 276 | lock-version = "1.1" 277 | python-versions = "^3.8" 278 | content-hash = "5628b1c6dcb7fba2257cba53a82df24926a29730c3dfe402f25cea9e18ac186f" 279 | 280 | [metadata.files] 281 | black = [ 282 | {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, 283 | {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, 284 | {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, 285 | {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, 286 | {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, 287 | {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, 288 | {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, 289 | {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, 290 | {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, 291 | {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, 292 | {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, 293 | {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, 294 | {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, 295 | {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, 296 | {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, 297 | {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, 298 | {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, 299 | {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, 300 | {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, 301 | {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, 302 | {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, 303 | {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, 304 | {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, 305 | ] 306 | click = [ 307 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 308 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 309 | ] 310 | colorama = [ 311 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 312 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 313 | ] 314 | cycler = [ 315 | {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, 316 | {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, 317 | ] 318 | fonttools = [ 319 | {file = "fonttools-4.33.3-py3-none-any.whl", hash = "sha256:f829c579a8678fa939a1d9e9894d01941db869de44390adb49ce67055a06cc2a"}, 320 | {file = "fonttools-4.33.3.zip", hash = "sha256:c0fdcfa8ceebd7c1b2021240bd46ef77aa8e7408cf10434be55df52384865f8e"}, 321 | ] 322 | kiwisolver = [ 323 | {file = "kiwisolver-1.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e395ece147f0692ca7cdb05a028d31b83b72c369f7b4a2c1798f4b96af1e3d8"}, 324 | {file = "kiwisolver-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b7f50a1a25361da3440f07c58cd1d79957c2244209e4f166990e770256b6b0b"}, 325 | {file = "kiwisolver-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c032c41ae4c3a321b43a3650e6ecc7406b99ff3e5279f24c9b310f41bc98479"}, 326 | {file = "kiwisolver-1.4.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1dcade8f6fe12a2bb4efe2cbe22116556e3b6899728d3b2a0d3b367db323eacc"}, 327 | {file = "kiwisolver-1.4.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e45e780a74416ef2f173189ef4387e44b5494f45e290bcb1f03735faa6779bf"}, 328 | {file = "kiwisolver-1.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d2bb56309fb75a811d81ed55fbe2208aa77a3a09ff5f546ca95e7bb5fac6eff"}, 329 | {file = "kiwisolver-1.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b2d6c12f2ad5f55104a36a356192cfb680c049fe5e7c1f6620fc37f119cdc2"}, 330 | {file = "kiwisolver-1.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:262c248c60f22c2b547683ad521e8a3db5909c71f679b93876921549107a0c24"}, 331 | {file = "kiwisolver-1.4.2-cp310-cp310-win32.whl", hash = "sha256:1008346a7741620ab9cc6c96e8ad9b46f7a74ce839dbb8805ddf6b119d5fc6c2"}, 332 | {file = "kiwisolver-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:6ece2e12e4b57bc5646b354f436416cd2a6f090c1dadcd92b0ca4542190d7190"}, 333 | {file = "kiwisolver-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b978afdb913ca953cf128d57181da2e8798e8b6153be866ae2a9c446c6162f40"}, 334 | {file = "kiwisolver-1.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f88c4b8e449908eeddb3bbd4242bd4dc2c7a15a7aa44bb33df893203f02dc2d"}, 335 | {file = "kiwisolver-1.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e348f1904a4fab4153407f7ccc27e43b2a139752e8acf12e6640ba683093dd96"}, 336 | {file = "kiwisolver-1.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c839bf28e45d7ddad4ae8f986928dbf5a6d42ff79760d54ec8ada8fb263e097c"}, 337 | {file = "kiwisolver-1.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8ae5a071185f1a93777c79a9a1e67ac46544d4607f18d07131eece08d415083a"}, 338 | {file = "kiwisolver-1.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c222f91a45da9e01a9bc4f760727ae49050f8e8345c4ff6525495f7a164c8973"}, 339 | {file = "kiwisolver-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:a4e8f072db1d6fb7a7cc05a6dbef8442c93001f4bb604f1081d8c2db3ca97159"}, 340 | {file = "kiwisolver-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:be9a650890fb60393e60aacb65878c4a38bb334720aa5ecb1c13d0dac54dd73b"}, 341 | {file = "kiwisolver-1.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ec2e55bf31b43aabe32089125dca3b46fdfe9f50afbf0756ae11e14c97b80ca"}, 342 | {file = "kiwisolver-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d1078ba770d6165abed3d9a1be1f9e79b61515de1dd00d942fa53bba79f01ae"}, 343 | {file = "kiwisolver-1.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbb5eb4a2ea1ffec26268d49766cafa8f957fe5c1b41ad00733763fae77f9436"}, 344 | {file = "kiwisolver-1.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e6cda72db409eefad6b021e8a4f964965a629f577812afc7860c69df7bdb84a"}, 345 | {file = "kiwisolver-1.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1605c7c38cc6a85212dfd6a641f3905a33412e49f7c003f35f9ac6d71f67720"}, 346 | {file = "kiwisolver-1.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81237957b15469ea9151ec8ca08ce05656090ffabc476a752ef5ad7e2644c526"}, 347 | {file = "kiwisolver-1.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:240009fdf4fa87844f805e23f48995537a8cb8f8c361e35fda6b5ac97fcb906f"}, 348 | {file = "kiwisolver-1.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:240c2d51d098395c012ddbcb9bd7b3ba5de412a1d11840698859f51d0e643c4f"}, 349 | {file = "kiwisolver-1.4.2-cp38-cp38-win32.whl", hash = "sha256:8b6086aa6936865962b2cee0e7aaecf01ab6778ce099288354a7229b4d9f1408"}, 350 | {file = "kiwisolver-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:0d98dca86f77b851350c250f0149aa5852b36572514d20feeadd3c6b1efe38d0"}, 351 | {file = "kiwisolver-1.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:91eb4916271655dfe3a952249cb37a5c00b6ba68b4417ee15af9ba549b5ba61d"}, 352 | {file = "kiwisolver-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa4d97d7d2b2c082e67907c0b8d9f31b85aa5d3ba0d33096b7116f03f8061261"}, 353 | {file = "kiwisolver-1.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:71469b5845b9876b8d3d252e201bef6f47bf7456804d2fbe9a1d6e19e78a1e65"}, 354 | {file = "kiwisolver-1.4.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8ff3033e43e7ca1389ee59fb7ecb8303abb8713c008a1da49b00869e92e3dd7c"}, 355 | {file = "kiwisolver-1.4.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89b57c2984f4464840e4b768affeff6b6809c6150d1166938ade3e22fbe22db8"}, 356 | {file = "kiwisolver-1.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffbdb9a96c536f0405895b5e21ee39ec579cb0ed97bdbd169ae2b55f41d73219"}, 357 | {file = "kiwisolver-1.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a830a03970c462d1a2311c90e05679da56d3bd8e78a4ba9985cb78ef7836c9f"}, 358 | {file = "kiwisolver-1.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f74f2a13af201559e3d32b9ddfc303c94ae63d63d7f4326d06ce6fe67e7a8255"}, 359 | {file = "kiwisolver-1.4.2-cp39-cp39-win32.whl", hash = "sha256:e677cc3626287f343de751e11b1e8a5b915a6ac897e8aecdbc996cd34de753a0"}, 360 | {file = "kiwisolver-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b3e251e5c38ac623c5d786adb21477f018712f8c6fa54781bd38aa1c60b60fc2"}, 361 | {file = "kiwisolver-1.4.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c380bb5ae20d829c1a5473cfcae64267b73aaa4060adc091f6df1743784aae0"}, 362 | {file = "kiwisolver-1.4.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:484f2a5f0307bc944bc79db235f41048bae4106ffa764168a068d88b644b305d"}, 363 | {file = "kiwisolver-1.4.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8afdf533b613122e4bbaf3c1e42c2a5e9e2d1dd3a0a017749a7658757cb377"}, 364 | {file = "kiwisolver-1.4.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42f6ef9b640deb6f7d438e0a371aedd8bef6ddfde30683491b2e6f568b4e884e"}, 365 | {file = "kiwisolver-1.4.2.tar.gz", hash = "sha256:7f606d91b8a8816be476513a77fd30abe66227039bd6f8b406c348cb0247dcc9"}, 366 | ] 367 | matplotlib = [ 368 | {file = "matplotlib-3.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:03bbb3f5f78836855e127b5dab228d99551ad0642918ccbf3067fcd52ac7ac5e"}, 369 | {file = "matplotlib-3.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49a5938ed6ef9dda560f26ea930a2baae11ea99e1c2080c8714341ecfda72a89"}, 370 | {file = "matplotlib-3.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:77157be0fc4469cbfb901270c205e7d8adb3607af23cef8bd11419600647ceed"}, 371 | {file = "matplotlib-3.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5844cea45d804174bf0fac219b4ab50774e504bef477fc10f8f730ce2d623441"}, 372 | {file = "matplotlib-3.5.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c87973ddec10812bddc6c286b88fdd654a666080fbe846a1f7a3b4ba7b11ab78"}, 373 | {file = "matplotlib-3.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a05f2b37222319753a5d43c0a4fd97ed4ff15ab502113e3f2625c26728040cf"}, 374 | {file = "matplotlib-3.5.2-cp310-cp310-win32.whl", hash = "sha256:9776e1a10636ee5f06ca8efe0122c6de57ffe7e8c843e0fb6e001e9d9256ec95"}, 375 | {file = "matplotlib-3.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:b4fedaa5a9aa9ce14001541812849ed1713112651295fdddd640ea6620e6cf98"}, 376 | {file = "matplotlib-3.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ee175a571e692fc8ae8e41ac353c0e07259113f4cb063b0ec769eff9717e84bb"}, 377 | {file = "matplotlib-3.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e8bda1088b941ead50caabd682601bece983cadb2283cafff56e8fcddbf7d7f"}, 378 | {file = "matplotlib-3.5.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9480842d5aadb6e754f0b8f4ebeb73065ac8be1855baa93cd082e46e770591e9"}, 379 | {file = "matplotlib-3.5.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6c623b355d605a81c661546af7f24414165a8a2022cddbe7380a31a4170fa2e9"}, 380 | {file = "matplotlib-3.5.2-cp37-cp37m-win32.whl", hash = "sha256:a91426ae910819383d337ba0dc7971c7cefdaa38599868476d94389a329e599b"}, 381 | {file = "matplotlib-3.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c4b82c2ae6d305fcbeb0eb9c93df2602ebd2f174f6e8c8a5d92f9445baa0c1d3"}, 382 | {file = "matplotlib-3.5.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ebc27ad11df3c1661f4677a7762e57a8a91dd41b466c3605e90717c9a5f90c82"}, 383 | {file = "matplotlib-3.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a32ea6e12e80dedaca2d4795d9ed40f97bfa56e6011e14f31502fdd528b9c89"}, 384 | {file = "matplotlib-3.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a0967d4156adbd0d46db06bc1a877f0370bce28d10206a5071f9ecd6dc60b79"}, 385 | {file = "matplotlib-3.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2b696699386766ef171a259d72b203a3c75d99d03ec383b97fc2054f52e15cf"}, 386 | {file = "matplotlib-3.5.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7f409716119fa39b03da3d9602bd9b41142fab7a0568758cd136cd80b1bf36c8"}, 387 | {file = "matplotlib-3.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b8d3f4e71e26307e8c120b72c16671d70c5cd08ae412355c11254aa8254fb87f"}, 388 | {file = "matplotlib-3.5.2-cp38-cp38-win32.whl", hash = "sha256:b6c63cd01cad0ea8704f1fd586e9dc5777ccedcd42f63cbbaa3eae8dd41172a1"}, 389 | {file = "matplotlib-3.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:75c406c527a3aa07638689586343f4b344fcc7ab1f79c396699eb550cd2b91f7"}, 390 | {file = "matplotlib-3.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4a44cdfdb9d1b2f18b1e7d315eb3843abb097869cd1ef89cfce6a488cd1b5182"}, 391 | {file = "matplotlib-3.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d8e129af95b156b41cb3be0d9a7512cc6d73e2b2109f82108f566dbabdbf377"}, 392 | {file = "matplotlib-3.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:364e6bca34edc10a96aa3b1d7cd76eb2eea19a4097198c1b19e89bee47ed5781"}, 393 | {file = "matplotlib-3.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea75df8e567743207e2b479ba3d8843537be1c146d4b1e3e395319a4e1a77fe9"}, 394 | {file = "matplotlib-3.5.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:44c6436868186564450df8fd2fc20ed9daaef5caad699aa04069e87099f9b5a8"}, 395 | {file = "matplotlib-3.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d7705022df2c42bb02937a2a824f4ec3cca915700dd80dc23916af47ff05f1a"}, 396 | {file = "matplotlib-3.5.2-cp39-cp39-win32.whl", hash = "sha256:ee0b8e586ac07f83bb2950717e66cb305e2859baf6f00a9c39cc576e0ce9629c"}, 397 | {file = "matplotlib-3.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:c772264631e5ae61f0bd41313bbe48e1b9bcc95b974033e1118c9caa1a84d5c6"}, 398 | {file = "matplotlib-3.5.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:751d3815b555dcd6187ad35b21736dc12ce6925fc3fa363bbc6dc0f86f16484f"}, 399 | {file = "matplotlib-3.5.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:31fbc2af27ebb820763f077ec7adc79b5a031c2f3f7af446bd7909674cd59460"}, 400 | {file = "matplotlib-3.5.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fa28ca76ac5c2b2d54bc058b3dad8e22ee85d26d1ee1b116a6fd4d2277b6a04"}, 401 | {file = "matplotlib-3.5.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:24173c23d1bcbaed5bf47b8785d27933a1ac26a5d772200a0f3e0e38f471b001"}, 402 | {file = "matplotlib-3.5.2.tar.gz", hash = "sha256:48cf850ce14fa18067f2d9e0d646763681948487a8080ec0af2686468b4607a2"}, 403 | ] 404 | mypy-extensions = [ 405 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 406 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 407 | ] 408 | networkx = [ 409 | {file = "networkx-2.8-py3-none-any.whl", hash = "sha256:1a1e8fe052cc1b4e0339b998f6795099562a264a13a5af7a32cad45ab9d4e126"}, 410 | {file = "networkx-2.8.tar.gz", hash = "sha256:4a52cf66aed221955420e11b3e2e05ca44196b4829aab9576d4d439212b0a14f"}, 411 | ] 412 | numpy = [ 413 | {file = "numpy-1.22.3-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75"}, 414 | {file = "numpy-1.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab"}, 415 | {file = "numpy-1.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e"}, 416 | {file = "numpy-1.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4"}, 417 | {file = "numpy-1.22.3-cp310-cp310-win32.whl", hash = "sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430"}, 418 | {file = "numpy-1.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4"}, 419 | {file = "numpy-1.22.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce"}, 420 | {file = "numpy-1.22.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe"}, 421 | {file = "numpy-1.22.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5"}, 422 | {file = "numpy-1.22.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1"}, 423 | {file = "numpy-1.22.3-cp38-cp38-win32.whl", hash = "sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62"}, 424 | {file = "numpy-1.22.3-cp38-cp38-win_amd64.whl", hash = "sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676"}, 425 | {file = "numpy-1.22.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123"}, 426 | {file = "numpy-1.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802"}, 427 | {file = "numpy-1.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d"}, 428 | {file = "numpy-1.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168"}, 429 | {file = "numpy-1.22.3-cp39-cp39-win32.whl", hash = "sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa"}, 430 | {file = "numpy-1.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a"}, 431 | {file = "numpy-1.22.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f"}, 432 | {file = "numpy-1.22.3.zip", hash = "sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18"}, 433 | ] 434 | packaging = [ 435 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 436 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 437 | ] 438 | pandas = [ 439 | {file = "pandas-1.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be67c782c4f1b1f24c2f16a157e12c2693fd510f8df18e3287c77f33d124ed07"}, 440 | {file = "pandas-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5a206afa84ed20e07603f50d22b5f0db3fb556486d8c2462d8bc364831a4b417"}, 441 | {file = "pandas-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0010771bd9223f7afe5f051eb47c4a49534345dfa144f2f5470b27189a4dd3b5"}, 442 | {file = "pandas-1.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3228198333dd13c90b6434ddf61aa6d57deaca98cf7b654f4ad68a2db84f8cfe"}, 443 | {file = "pandas-1.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b79af3a69e5175c6fa7b4e046b21a646c8b74e92c6581a9d825687d92071b51"}, 444 | {file = "pandas-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5586cc95692564b441f4747c47c8a9746792e87b40a4680a2feb7794defb1ce3"}, 445 | {file = "pandas-1.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:061609334a8182ab500a90fe66d46f6f387de62d3a9cb9aa7e62e3146c712167"}, 446 | {file = "pandas-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b8134651258bce418cb79c71adeff0a44090c98d955f6953168ba16cc285d9f7"}, 447 | {file = "pandas-1.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df82739e00bb6daf4bba4479a40f38c718b598a84654cbd8bb498fd6b0aa8c16"}, 448 | {file = "pandas-1.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:385c52e85aaa8ea6a4c600a9b2821181a51f8be0aee3af6f2dcb41dafc4fc1d0"}, 449 | {file = "pandas-1.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295872bf1a09758aba199992c3ecde455f01caf32266d50abc1a073e828a7b9d"}, 450 | {file = "pandas-1.4.2-cp38-cp38-win32.whl", hash = "sha256:95c1e422ced0199cf4a34385ff124b69412c4bc912011ce895582bee620dfcaa"}, 451 | {file = "pandas-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5c54ea4ef3823108cd4ec7fb27ccba4c3a775e0f83e39c5e17f5094cb17748bc"}, 452 | {file = "pandas-1.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c072c7f06b9242c855ed8021ff970c0e8f8b10b35e2640c657d2a541c5950f59"}, 453 | {file = "pandas-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f549097993744ff8c41b5e8f2f0d3cbfaabe89b4ae32c8c08ead6cc535b80139"}, 454 | {file = "pandas-1.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff08a14ef21d94cdf18eef7c569d66f2e24e0bc89350bcd7d243dd804e3b5eb2"}, 455 | {file = "pandas-1.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c5bf555b6b0075294b73965adaafb39cf71c312e38c5935c93d78f41c19828a"}, 456 | {file = "pandas-1.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51649ef604a945f781105a6d2ecf88db7da0f4868ac5d45c51cb66081c4d9c73"}, 457 | {file = "pandas-1.4.2-cp39-cp39-win32.whl", hash = "sha256:d0d4f13e4be7ce89d7057a786023c461dd9370040bdb5efa0a7fe76b556867a0"}, 458 | {file = "pandas-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:09d8be7dd9e1c4c98224c4dfe8abd60d145d934e9fc1f5f411266308ae683e6a"}, 459 | {file = "pandas-1.4.2.tar.gz", hash = "sha256:92bc1fc585f1463ca827b45535957815b7deb218c549b7c18402c322c7549a12"}, 460 | ] 461 | pathspec = [ 462 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 463 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 464 | ] 465 | pillow = [ 466 | {file = "Pillow-9.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:af79d3fde1fc2e33561166d62e3b63f0cc3e47b5a3a2e5fea40d4917754734ea"}, 467 | {file = "Pillow-9.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55dd1cf09a1fd7c7b78425967aacae9b0d70125f7d3ab973fadc7b5abc3de652"}, 468 | {file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66822d01e82506a19407d1afc104c3fcea3b81d5eb11485e593ad6b8492f995a"}, 469 | {file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5eaf3b42df2bcda61c53a742ee2c6e63f777d0e085bbc6b2ab7ed57deb13db7"}, 470 | {file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01ce45deec9df310cbbee11104bae1a2a43308dd9c317f99235b6d3080ddd66e"}, 471 | {file = "Pillow-9.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aea7ce61328e15943d7b9eaca87e81f7c62ff90f669116f857262e9da4057ba3"}, 472 | {file = "Pillow-9.1.0-cp310-cp310-win32.whl", hash = "sha256:7a053bd4d65a3294b153bdd7724dce864a1d548416a5ef61f6d03bf149205160"}, 473 | {file = "Pillow-9.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:97bda660702a856c2c9e12ec26fc6d187631ddfd896ff685814ab21ef0597033"}, 474 | {file = "Pillow-9.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21dee8466b42912335151d24c1665fcf44dc2ee47e021d233a40c3ca5adae59c"}, 475 | {file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b6d4050b208c8ff886fd3db6690bf04f9a48749d78b41b7a5bf24c236ab0165"}, 476 | {file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5cfca31ab4c13552a0f354c87fbd7f162a4fafd25e6b521bba93a57fe6a3700a"}, 477 | {file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed742214068efa95e9844c2d9129e209ed63f61baa4d54dbf4cf8b5e2d30ccf2"}, 478 | {file = "Pillow-9.1.0-cp37-cp37m-win32.whl", hash = "sha256:c9efef876c21788366ea1f50ecb39d5d6f65febe25ad1d4c0b8dff98843ac244"}, 479 | {file = "Pillow-9.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:de344bcf6e2463bb25179d74d6e7989e375f906bcec8cb86edb8b12acbc7dfef"}, 480 | {file = "Pillow-9.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:17869489de2fce6c36690a0c721bd3db176194af5f39249c1ac56d0bb0fcc512"}, 481 | {file = "Pillow-9.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25023a6209a4d7c42154073144608c9a71d3512b648a2f5d4465182cb93d3477"}, 482 | {file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8782189c796eff29dbb37dd87afa4ad4d40fc90b2742704f94812851b725964b"}, 483 | {file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:463acf531f5d0925ca55904fa668bb3461c3ef6bc779e1d6d8a488092bdee378"}, 484 | {file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f42364485bfdab19c1373b5cd62f7c5ab7cc052e19644862ec8f15bb8af289e"}, 485 | {file = "Pillow-9.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3fddcdb619ba04491e8f771636583a7cc5a5051cd193ff1aa1ee8616d2a692c5"}, 486 | {file = "Pillow-9.1.0-cp38-cp38-win32.whl", hash = "sha256:4fe29a070de394e449fd88ebe1624d1e2d7ddeed4c12e0b31624561b58948d9a"}, 487 | {file = "Pillow-9.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c24f718f9dd73bb2b31a6201e6db5ea4a61fdd1d1c200f43ee585fc6dcd21b34"}, 488 | {file = "Pillow-9.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fb89397013cf302f282f0fc998bb7abf11d49dcff72c8ecb320f76ea6e2c5717"}, 489 | {file = "Pillow-9.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c870193cce4b76713a2b29be5d8327c8ccbe0d4a49bc22968aa1e680930f5581"}, 490 | {file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69e5ddc609230d4408277af135c5b5c8fe7a54b2bdb8ad7c5100b86b3aab04c6"}, 491 | {file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35be4a9f65441d9982240e6966c1eaa1c654c4e5e931eaf580130409e31804d4"}, 492 | {file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82283af99c1c3a5ba1da44c67296d5aad19f11c535b551a5ae55328a317ce331"}, 493 | {file = "Pillow-9.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a325ac71914c5c043fa50441b36606e64a10cd262de12f7a179620f579752ff8"}, 494 | {file = "Pillow-9.1.0-cp39-cp39-win32.whl", hash = "sha256:a598d8830f6ef5501002ae85c7dbfcd9c27cc4efc02a1989369303ba85573e58"}, 495 | {file = "Pillow-9.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0c51cb9edac8a5abd069fd0758ac0a8bfe52c261ee0e330f363548aca6893595"}, 496 | {file = "Pillow-9.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a336a4f74baf67e26f3acc4d61c913e378e931817cd1e2ef4dfb79d3e051b481"}, 497 | {file = "Pillow-9.1.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb1b89b11256b5b6cad5e7593f9061ac4624f7651f7a8eb4dfa37caa1dfaa4d0"}, 498 | {file = "Pillow-9.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:255c9d69754a4c90b0ee484967fc8818c7ff8311c6dddcc43a4340e10cd1636a"}, 499 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5a3ecc026ea0e14d0ad7cd990ea7f48bfcb3eb4271034657dc9d06933c6629a7"}, 500 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5b0ff59785d93b3437c3703e3c64c178aabada51dea2a7f2c5eccf1bcf565a3"}, 501 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7110ec1701b0bf8df569a7592a196c9d07c764a0a74f65471ea56816f10e2c8"}, 502 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d79c6f468215d1a8415aa53d9868a6b40c4682165b8cb62a221b1baa47db458"}, 503 | {file = "Pillow-9.1.0.tar.gz", hash = "sha256:f401ed2bbb155e1ade150ccc63db1a4f6c1909d3d378f7d1235a44e90d75fb97"}, 504 | ] 505 | platformdirs = [ 506 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 507 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 508 | ] 509 | pydot = [ 510 | {file = "pydot-1.4.2-py2.py3-none-any.whl", hash = "sha256:66c98190c65b8d2e2382a441b4c0edfdb4f4c025ef9cb9874de478fb0793a451"}, 511 | {file = "pydot-1.4.2.tar.gz", hash = "sha256:248081a39bcb56784deb018977e428605c1c758f10897a339fce1dd728ff007d"}, 512 | ] 513 | pyparsing = [ 514 | {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, 515 | {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, 516 | ] 517 | python-dateutil = [ 518 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 519 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 520 | ] 521 | pytz = [ 522 | {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, 523 | {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, 524 | ] 525 | setuptools-scm = [ 526 | {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"}, 527 | {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"}, 528 | ] 529 | six = [ 530 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 531 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 532 | ] 533 | tomli = [ 534 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 535 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 536 | ] 537 | typing-extensions = [ 538 | {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, 539 | {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, 540 | ] 541 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "iads_algorithms" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Artemis Livingstone "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | pandas = "^1.4.2" 10 | networkx = "^2.8" 11 | matplotlib = "^3.5.2" 12 | pydot = "^1.4.2" 13 | 14 | [tool.poetry.dev-dependencies] 15 | black = {version = "^22.3.0", allow-prereleases = true} 16 | 17 | [build-system] 18 | requires = ["poetry-core>=1.0.0"] 19 | build-backend = "poetry.core.masonry.api" 20 | -------------------------------------------------------------------------------- /sorting_algos/bubble_sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | def bubble_sort(arr: List): # O(n**2) 5 | for n in range(len(arr), 1, -1): # O(n) iterations 6 | for i in range(n - 1): # O(n) iterations 7 | if arr[i] > arr[i + 1]: 8 | arr[i], arr[i + 1] = arr[i + 1], arr[i] 9 | 10 | 11 | def bubble_sort_traced(arr: List): # O(n**2) 12 | print("Start:", arr) 13 | for n in range(len(arr), 1, -1): # O(n) iterations 14 | for i in range(n - 1): # O(n) iterations 15 | if arr[i] > arr[i + 1]: 16 | arr[i], arr[i + 1] = arr[i + 1], arr[i] 17 | print(arr) 18 | print("final", arr) 19 | 20 | 21 | if __name__ == "__main__": 22 | bubble_sort_traced([1, 2, 3, 4, 5]) 23 | bubble_sort_traced([4, 3, 3, 2, 4, 5, 1]) 24 | -------------------------------------------------------------------------------- /sorting_algos/heap_sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from heapq import heappush, heappop, heapify 3 | 4 | # using minheap 5 | def heapsort(arr: List) -> List: 6 | h = [] 7 | for value in arr: 8 | heappush(h, value) 9 | return [heappop(h) for i in range(len(h))] 10 | 11 | 12 | def heapsort_traced(arr: List) -> List: 13 | print("Start:", arr) 14 | arr = [-x for x in arr] 15 | n = len(arr) 16 | 17 | heapify(arr) 18 | print("Heapified:", [-x for x in arr]) 19 | 20 | final = [] 21 | print(final) 22 | for i in range(n - 1, -1, -1): 23 | v = heappop(arr) 24 | final.append(v) 25 | print(list(reversed([-x for x in final]))) 26 | 27 | print("heapsort done", list(reversed([-x for x in final]))) 28 | return final 29 | 30 | 31 | if __name__ == "__main__": 32 | heapsort_traced([1, 2, 3, 4, 5]) 33 | heapsort_traced([4, 3, 3, 2, 4, 5, 1]) 34 | 35 | # def heap_sort(arr): # O(n*log(n)) 36 | # n = len(arr) 37 | # heapify(arr, n) # O(n) 38 | # for i in range(n): # O(n) iterations 39 | # arr[-i] = extract_max(arr, n-1) # O(log(n)) 40 | -------------------------------------------------------------------------------- /sorting_algos/insertion_sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | def insert_sort(arr: List): # O(n**2) 5 | for i in range(1, len(arr)): # O(n) iterations 6 | x = arr[i] 7 | for j in range(i - 1, 0, -1): # O(n) iterations 8 | if arr[j] > x: 9 | break 10 | arr[j + 1] = arr[j] 11 | arr[j + 1] = x 12 | 13 | 14 | def insert_sort_traced(arr: List): # O(n**2) 15 | print("Start:", arr) 16 | for i in range(1, len(arr)): # O(n) iterations 17 | x = arr[i] 18 | j = i - 1 19 | print("while >") 20 | while j >= 0 and arr[j] > x: 21 | # for j in range(i-1,0,-1): # O(n) iterations 22 | # if arr[j] > x: 23 | # break 24 | arr[j + 1] = arr[j] 25 | j = j - 1 26 | print("inner >", arr) 27 | arr[j + 1] = x 28 | print("outer", arr) 29 | print("final", arr) 30 | 31 | 32 | if __name__ == "__main__": 33 | insert_sort_traced([1, 2, 3, 4, 5]) 34 | insert_sort_traced([4, 3, 3, 2, 4, 5, 1]) 35 | -------------------------------------------------------------------------------- /sorting_algos/merge_sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | def merge_sort(arr: List): # O(n*log(n)), Master Theorem 5 | n = len(arr) 6 | if n < 2: 7 | return arr 8 | left = merge_sort(arr[: n // 2]) # O(n//2*log(n//2)) 9 | right = merge_sort(arr[n // 2 :]) # O(n//2*log(n//2)) 10 | return merge(left, right) # O(n) 11 | 12 | 13 | # We're merging backwards and then reversing the list because 14 | # popping from/appending to the front is very inefficient. 15 | def merge(left: List, right: List) -> List: # O(n) 16 | merged = [None] * (len(left) + len(right)) # n = len(left) + len(right) 17 | i = 0 18 | j = 0 19 | for k in range(len(left) + len(right)): 20 | if len(left) > i and len(right) > j: 21 | if left[i] < right[j]: 22 | merged[k] = left[i] 23 | i = i + 1 24 | else: 25 | merged[k] = right[j] 26 | j = j + 1 27 | elif len(left) > i: 28 | merged[k] = left[i] 29 | i = i + 1 30 | elif len(right) > j: 31 | merged[k] = right[j] 32 | j = j + 1 33 | else: 34 | print("Uh oh, this shouldn't happen: i + j > len(left) + len(right)") 35 | return merged 36 | # while len(left) > 0 and len(right) > 0: # O(n) iterations 37 | # if left[-1] > right[-1]: 38 | # merged.append(left.pop()) 39 | # else: 40 | # merged.append(right.pop()) 41 | # merged.extend(left) # O(len(left)) = O(n) 42 | # merged.extend(right) # O(len(right)) = O(n) 43 | # return reversed(merged) # O(n) 44 | 45 | 46 | def merge_sort_traced(arr: List): # O(n*log(n)), Master Theorem 47 | print("Start:", arr) 48 | n = len(arr) 49 | if n < 2: 50 | print("final:", arr) 51 | return arr 52 | print("left from:", arr[: n // 2]) 53 | left = merge_sort_traced(arr[: n // 2]) # O(n//2*log(n//2)) 54 | print("right from:", arr[n // 2 :]) 55 | right = merge_sort_traced(arr[n // 2 :]) # O(n//2*log(n//2)) 56 | return merge_traced(left, right) # O(n) 57 | 58 | 59 | # We're merging backwards and then reversing the list because 60 | # popping from/appending to the front is very inefficient. 61 | def merge_traced(left: List, right: List) -> List: # O(n) 62 | print("merge (L/R)") 63 | print(left) 64 | print(right) 65 | merged = [None] * (len(left) + len(right)) # n = len(left) + len(right) 66 | i = 0 67 | j = 0 68 | # A1 or B1 each mean 1 new comparison in the lecture implementation (not counting comparisons against infinity) 69 | for k in range(len(left) + len(right)): 70 | if len(left) > i and len(right) > j: 71 | if left[i] < right[j]: 72 | merged[k] = left[i] 73 | i = i + 1 74 | print("A1", merged) 75 | else: 76 | merged[k] = right[j] 77 | j = j + 1 78 | print("B1", merged) 79 | elif len(left) > i: 80 | merged[k] = left[i] 81 | i = i + 1 82 | print("A2", merged) 83 | elif len(right) > j: 84 | merged[k] = right[j] 85 | j = j + 1 86 | print("B2", merged) 87 | else: 88 | print("Uh oh, this shouldn't happen: i + j > len(left) + len(right)") 89 | print("merge done", merged) 90 | return merged 91 | # while len(left) > 0 and len(right) > 0: # O(n) iterations 92 | # if left[-1] > right[-1]: 93 | # merged.append(left.pop()) 94 | # else: 95 | # merged.append(right.pop()) 96 | # merged.extend(left) # O(len(left)) = O(n) 97 | # merged.extend(right) # O(len(right)) = O(n) 98 | # return reversed(merged) # O(n) 99 | 100 | 101 | # def merge_traced(left: List, right: List) -> List: # O(n) 102 | # print("merge (L/R)") 103 | # print(left) 104 | # print(right) 105 | # merged = [] # n = len(left) + len(right) 106 | # while len(left) > 0 and len(right) > 0: # O(n) iterations 107 | # if left[-1] > right[-1]: 108 | # merged.append(left.pop()) 109 | # print("A", merged) 110 | # else: 111 | # merged.append(right.pop()) 112 | # print("B1", merged) 113 | # merged.extend(left) # O(len(left)) = O(n) 114 | # merged.extend(right) # O(len(right)) = O(n) 115 | # print("B2", merged) 116 | # print("merge done", list(reversed(merged))) 117 | # return list(reversed(merged)) # O(n) 118 | 119 | if __name__ == "__main__": 120 | merge_sort_traced([1, 2, 3, 4, 5]) 121 | merge_sort_traced([4, 3, 3, 2, 4, 5, 1]) 122 | -------------------------------------------------------------------------------- /sorting_algos/quick_sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | def QuickSort(array: List, left: int, right: int): 5 | if left < right: 6 | split = partition(array, left, right) 7 | QuickSort(array, left, split - 1) 8 | QuickSort(array, split + 1, right) 9 | 10 | 11 | def partition(array: List, low: int, high: int): 12 | pivot = array[high] 13 | i = low - 1 14 | for j in range(low, high, 1): 15 | if array[j] <= pivot: 16 | i = i + 1 17 | array[i], array[j] = array[j], array[i] 18 | array[i + 1], array[high] = array[high], array[i + 1] # swap A[i+1] and A[high] 19 | return i + 1 20 | 21 | 22 | def driver(A: List): 23 | QuickSort(A, 0, len(A) - 1) 24 | return A 25 | 26 | 27 | def QuickSort_traced(array: List, left: int, right: int): 28 | print("qs", left, right, array) 29 | if left < right: 30 | split = partition_traced(array, left, right) 31 | QuickSort_traced(array, left, split - 1) 32 | QuickSort_traced(array, split + 1, right) 33 | 34 | 35 | def partition_traced(array: List, low: int, high: int): 36 | pivot = array[high] 37 | print("pivot index, pivot", high, pivot) 38 | print("array", array) 39 | i = low - 1 40 | for j in range(low, high, 1): 41 | if array[j] <= pivot: 42 | i = i + 1 43 | print("swap", i) 44 | array[i], array[j] = array[j], array[i] 45 | print("after loop step", i, j, array) 46 | print("swap: final, after loop") 47 | array[i + 1], array[high] = array[high], array[i + 1] # swap A[i+1] and A[high] 48 | print(array) 49 | return i + 1 50 | 51 | 52 | def driver_traced(A: List): 53 | print("Start:", A) 54 | QuickSort_traced(A, 0, len(A) - 1) 55 | return A 56 | 57 | 58 | print(driver([9, 4, 2, 5, 1, 8, 4])) 59 | print(driver([4, 3, 2, 1, 8, 3, 9, 4, 3, 2, 1, 9, 4, 103, 34, 1234, 4])) 60 | 61 | print(driver_traced([9, 4, 2, 5, 1, 8, 4])) 62 | print(driver_traced([4, 3, 2, 1, 8, 3, 9, 4, 3, 2, 1, 9, 4, 103, 34, 1234, 4])) 63 | 64 | print(partition_traced([1, 3, 11, 6, 15, 7], 0, 5)) 65 | --------------------------------------------------------------------------------