diff --git a/pydatastructs/__init__.py b/pydatastructs/__init__.py index 41f351945..197fce03d 100644 --- a/pydatastructs/__init__.py +++ b/pydatastructs/__init__.py @@ -1,8 +1,8 @@ -__version__ = "1.0.1-dev" - from .linear_data_structures import * from .trees import * from .miscellaneous_data_structures import * from .utils import * from .graphs import * from .strings import * + +__version__ = "1.0.1-dev" diff --git a/pydatastructs/graphs/adjacency_list.py b/pydatastructs/graphs/adjacency_list.py index 541ff3a1e..e4313d3c7 100644 --- a/pydatastructs/graphs/adjacency_list.py +++ b/pydatastructs/graphs/adjacency_list.py @@ -1,6 +1,6 @@ from pydatastructs.graphs.graph import Graph -from pydatastructs.linear_data_structures import DynamicOneDimensionalArray -from pydatastructs.utils.misc_util import GraphEdge +from pydatastructs.utils.misc_util import ( + GraphEdge, Backend, raise_if_backend_is_not_python) __all__ = [ 'AdjacencyList' @@ -15,7 +15,9 @@ class AdjacencyList(Graph): pydatastructs.graphs.graph.Graph """ - def __new__(cls, *vertices): + def __new__(cls, *vertices, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) for vertex in vertices: obj.__setattr__(vertex.name, vertex) diff --git a/pydatastructs/graphs/adjacency_matrix.py b/pydatastructs/graphs/adjacency_matrix.py index 64a39494c..82457b3da 100644 --- a/pydatastructs/graphs/adjacency_matrix.py +++ b/pydatastructs/graphs/adjacency_matrix.py @@ -1,6 +1,7 @@ from pydatastructs.graphs.graph import Graph -from pydatastructs.linear_data_structures import OneDimensionalArray -from pydatastructs.utils.misc_util import AdjacencyMatrixGraphNode, GraphEdge +from pydatastructs.utils.misc_util import ( + GraphEdge, raise_if_backend_is_not_python, + Backend) __all__ = [ 'AdjacencyMatrix' @@ -15,7 +16,9 @@ class AdjacencyMatrix(Graph): pydatastructs.graphs.graph.Graph """ - def __new__(cls, *vertices): + def __new__(cls, *vertices, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.vertices = [vertex.name for vertex in vertices] for vertex in vertices: diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index 4997245ac..234255008 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -4,8 +4,8 @@ """ from collections import deque from concurrent.futures import ThreadPoolExecutor -from pydatastructs.utils import GraphEdge -from pydatastructs.utils.misc_util import _comp +from pydatastructs.utils.misc_util import ( + _comp, raise_if_backend_is_not_python, Backend) from pydatastructs.miscellaneous_data_structures import ( DisjointSetForest, PriorityQueue) from pydatastructs.graphs.graph import Graph @@ -51,6 +51,10 @@ def breadth_first_search( current node and the node next to current node. The rest of the arguments are optional and you can provide your own stuff there. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Note ==== @@ -75,6 +79,8 @@ def breadth_first_search( >>> G.add_edge(V2.name, V3.name) >>> breadth_first_search(G, V1.name, f, V3.name) """ + raise_if_backend_is_not_python( + breadth_first_search, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_breadth_first_search_" + graph._impl if not hasattr(algorithms, func): @@ -133,6 +139,10 @@ def breadth_first_search_parallel( current node and the node next to current node. The rest of the arguments are optional and you can provide your own stuff there. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Note ==== @@ -157,6 +167,8 @@ def breadth_first_search_parallel( >>> G.add_edge(V2.name, V3.name) >>> breadth_first_search_parallel(G, V1.name, 3, f, V3.name) """ + raise_if_backend_is_not_python( + breadth_first_search_parallel, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_breadth_first_search_parallel_" + graph._impl if not hasattr(algorithms, func): @@ -256,7 +268,7 @@ def _minimum_spanning_tree_prim_adjacency_list(graph): e[w] = vw return mst -def minimum_spanning_tree(graph, algorithm): +def minimum_spanning_tree(graph, algorithm, **kwargs): """ Computes a minimum spanning tree for the given graph and algorithm. @@ -276,6 +288,10 @@ def minimum_spanning_tree(graph, algorithm): 'kruskal' -> Kruskal's algorithm as given in [1]. 'prim' -> Prim's algorithm as given in [2]. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -312,6 +328,8 @@ def minimum_spanning_tree(graph, algorithm): should be used only for such graphs. Using with other types of graphs may lead to unwanted results. """ + raise_if_backend_is_not_python( + minimum_spanning_tree, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_minimum_spanning_tree_" + algorithm + "_" + graph._impl if not hasattr(algorithms, func): @@ -390,7 +408,7 @@ def _minimum_spanning_tree_parallel_prim_adjacency_list(graph, num_threads): return mst -def minimum_spanning_tree_parallel(graph, algorithm, num_threads): +def minimum_spanning_tree_parallel(graph, algorithm, num_threads, **kwargs): """ Computes a minimum spanning tree for the given graph and algorithm using the given number of threads. @@ -412,6 +430,10 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads): 'prim' -> Prim's algorithm as given in [2]. num_threads: int The number of threads to be used. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -448,6 +470,8 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads): should be used only for such graphs. Using with other types of graphs will lead to unwanted results. """ + raise_if_backend_is_not_python( + minimum_spanning_tree_parallel, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_minimum_spanning_tree_parallel_" + algorithm + "_" + graph._impl if not hasattr(algorithms, func): @@ -505,7 +529,7 @@ def _strongly_connected_components_kosaraju_adjacency_list(graph): _strongly_connected_components_kosaraju_adjacency_matrix = \ _strongly_connected_components_kosaraju_adjacency_list -def strongly_connected_components(graph, algorithm): +def strongly_connected_components(graph, algorithm, **kwargs): """ Computes strongly connected components for the given graph and algorithm. @@ -523,6 +547,10 @@ def strongly_connected_components(graph, algorithm): supported, 'kosaraju' -> Kosaraju's algorithm as given in [1]. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -550,6 +578,8 @@ def strongly_connected_components(graph, algorithm): .. [1] https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm """ + raise_if_backend_is_not_python( + strongly_connected_components, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_strongly_connected_components_" + algorithm + "_" + graph._impl if not hasattr(algorithms, func): @@ -583,6 +613,10 @@ def depth_first_search( current node and the node next to current node. The rest of the arguments are optional and you can provide your own stuff there. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Note ==== @@ -612,6 +646,8 @@ def depth_first_search( .. [1] https://en.wikipedia.org/wiki/Depth-first_search """ + raise_if_backend_is_not_python( + depth_first_search, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_depth_first_search_" + graph._impl if not hasattr(algorithms, func): @@ -646,7 +682,8 @@ def _depth_first_search_adjacency_list( _depth_first_search_adjacency_matrix = _depth_first_search_adjacency_list def shortest_paths(graph: Graph, algorithm: str, - source: str, target: str="") -> tuple: + source: str, target: str="", + **kwargs) -> tuple: """ Finds shortest paths in the given graph from a given source. @@ -668,6 +705,10 @@ def shortest_paths(graph: Graph, algorithm: str, The name of the target node. Optional, by default, all pair shortest paths are returned. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -701,6 +742,8 @@ def shortest_paths(graph: Graph, algorithm: str, .. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm .. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm """ + raise_if_backend_is_not_python( + shortest_paths, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_" + algorithm + "_" + graph._impl if not hasattr(algorithms, func): @@ -767,7 +810,8 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str): _dijkstra_adjacency_matrix = _dijkstra_adjacency_list -def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple: +def all_pair_shortest_paths(graph: Graph, algorithm: str, + **kwargs) -> tuple: """ Finds shortest paths between all pairs of vertices in the given graph. @@ -781,6 +825,10 @@ def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple: are implemented, 'floyd_warshall' -> Floyd Warshall algorithm as given in [1]. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -810,6 +858,8 @@ def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple: .. [1] https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm """ + raise_if_backend_is_not_python( + all_pair_shortest_paths, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_" + algorithm + "_" + graph._impl if not hasattr(algorithms, func): @@ -849,7 +899,8 @@ def _floyd_warshall_adjacency_list(graph: Graph): _floyd_warshall_adjacency_matrix = _floyd_warshall_adjacency_list -def topological_sort(graph: Graph, algorithm: str) -> list: +def topological_sort(graph: Graph, algorithm: str, + **kwargs) -> list: """ Performs topological sort on the given graph using given algorithm. @@ -863,6 +914,10 @@ def topological_sort(graph: Graph, algorithm: str) -> list: Currently, following are supported, 'kahn' -> Kahn's algorithm as given in [1]. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -886,6 +941,8 @@ def topological_sort(graph: Graph, algorithm: str) -> list: .. [1] https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm """ + raise_if_backend_is_not_python( + topological_sort, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_" + algorithm + "_" + graph._impl if not hasattr(algorithms, func): @@ -920,7 +977,8 @@ def _kahn_adjacency_list(graph: Graph) -> list: raise ValueError("Graph is not acyclic.") return L -def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int) -> list: +def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int, + **kwargs) -> list: """ Performs topological sort on the given graph using given algorithm using given number of threads. @@ -937,6 +995,10 @@ def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int) -> 'kahn' -> Kahn's algorithm as given in [1]. num_threads: int The maximum number of threads to be used. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -960,6 +1022,8 @@ def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int) -> .. [1] https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm """ + raise_if_backend_is_not_python( + topological_sort_parallel, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.graphs.algorithms as algorithms func = "_" + algorithm + "_" + graph._impl + '_parallel' if not hasattr(algorithms, func): diff --git a/pydatastructs/graphs/graph.py b/pydatastructs/graphs/graph.py index 0c1b195a8..2624b371a 100644 --- a/pydatastructs/graphs/graph.py +++ b/pydatastructs/graphs/graph.py @@ -1,4 +1,6 @@ +from pydatastructs.utils.misc_util import Backend, raise_if_backend_is_not_python + __all__ = [ 'Graph' ] @@ -24,6 +26,10 @@ class Graph(object): vertices: GraphNode(s) For AdjacencyList implementation vertices can be passed for initializing the graph. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -64,6 +70,8 @@ class Graph(object): __slots__ = ['_impl'] def __new__(cls, *args, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) default_impl = args[0]._impl if args else 'adjacency_list' implementation = kwargs.get('implementation', default_impl) if implementation == 'adjacency_list': diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index 5c8de546a..cd7e40d80 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -1,6 +1,8 @@ from pydatastructs.linear_data_structures.arrays import ( OneDimensionalArray, DynamicArray, DynamicOneDimensionalArray, Array) -from pydatastructs.utils.misc_util import _check_type, _comp +from pydatastructs.utils.misc_util import ( + _check_type, _comp, Backend, + raise_if_backend_is_not_python) from concurrent.futures import ThreadPoolExecutor from math import log, floor @@ -81,6 +83,10 @@ def merge_sort_parallel(array, num_threads, **kwargs): Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -99,6 +105,8 @@ def merge_sort_parallel(array, num_threads, **kwargs): .. [1] https://en.wikipedia.org/wiki/Merge_sort """ + raise_if_backend_is_not_python( + merge_sort_parallel, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) comp = kwargs.get("comp", lambda u, v: u <= v) @@ -143,6 +151,10 @@ def brick_sort(array, **kwargs): Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -159,6 +171,8 @@ def brick_sort(array, **kwargs): ========== .. [1] https://www.geeksforgeeks.org/odd-even-sort-brick-sort/ """ + raise_if_backend_is_not_python( + brick_sort, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) comp = kwargs.get("comp", lambda u, v: u <= v) @@ -211,6 +225,10 @@ def brick_sort_parallel(array, num_threads, **kwargs): Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -229,7 +247,8 @@ def brick_sort_parallel(array, num_threads, **kwargs): .. [1] https://en.wikipedia.org/wiki/Odd%E2%80%93even_sort """ - + raise_if_backend_is_not_python( + brick_sort_parallel, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) comp = kwargs.get("comp", lambda u, v: u <= v) @@ -265,6 +284,10 @@ def heapsort(array, **kwargs): is to be sorted. Optional, by default the index of the last position filled. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -286,6 +309,8 @@ def heapsort(array, **kwargs): This function does not support custom comparators as is the case with other sorting functions in this file. """ + raise_if_backend_is_not_python( + heapsort, kwargs.get('backend', Backend.PYTHON)) from pydatastructs.trees.heaps import BinaryHeap start = kwargs.get('start', 0) @@ -305,7 +330,7 @@ def heapsort(array, **kwargs): if _check_type(array, DynamicArray): array._modify(force=True) -def counting_sort(array: Array) -> Array: +def counting_sort(array: Array, **kwargs) -> Array: """ Performs counting sort on the given array. @@ -314,6 +339,10 @@ def counting_sort(array: Array) -> Array: array: Array The array which is to be sorted. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -346,6 +375,8 @@ def counting_sort(array: Array) -> Array: custom comparators aren't allowed. The ouput array doesn't contain any `None` value. """ + raise_if_backend_is_not_python( + counting_sort, kwargs.get('backend', Backend.PYTHON)) max_val, min_val = array[0], array[0] none_count = 0 for i in range(len(array)): @@ -397,13 +428,15 @@ def matrix_multiply_parallel(matrix_1, matrix_2, num_threads): matrix_1: Any matrix representation Left matrix - matrix_2: Any matrix representation Right matrix - num_threads: int The maximum number of threads to be used for multiplication. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Raises ====== @@ -477,6 +510,10 @@ def bucket_sort(array: Array, **kwargs) -> Array: is to be sorted. Optional, by default the index of the last position filled. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -508,6 +545,8 @@ def bucket_sort(array: Array, **kwargs) -> Array: This function does not support custom comparators as is the case with other sorting functions in this file. """ + raise_if_backend_is_not_python( + bucket_sort, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) @@ -580,6 +619,10 @@ def cocktail_shaker_sort(array: Array, **kwargs) -> Array: Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -605,6 +648,8 @@ def cocktail_shaker_sort(array: Array, **kwargs) -> Array: .. [1] https://en.wikipedia.org/wiki/Cocktail_shaker_sort """ + raise_if_backend_is_not_python( + cocktail_shaker_sort, kwargs.get('backend', Backend.PYTHON)) def swap(i, j): array[i], array[j] = array[j], array[i] @@ -667,6 +712,10 @@ def quick_sort(array: Array, **kwargs) -> Array: the original input array to `quick_sort` function. Optional, by default, picks the element at `high` index of the current partition as pivot. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -692,6 +741,8 @@ def quick_sort(array: Array, **kwargs) -> Array: .. [1] https://en.wikipedia.org/wiki/Quicksort """ + raise_if_backend_is_not_python( + quick_sort, kwargs.get('backend', Backend.PYTHON)) from pydatastructs import Stack comp = kwargs.get("comp", lambda u, v: u <= v) pick_pivot_element = kwargs.get("pick_pivot_element", @@ -730,7 +781,8 @@ def partition(low, high, pick_pivot_element): return array -def longest_common_subsequence(seq1: OneDimensionalArray, seq2: OneDimensionalArray) -> OneDimensionalArray: +def longest_common_subsequence(seq1: OneDimensionalArray, seq2: OneDimensionalArray, + **kwargs) -> OneDimensionalArray: """ Finds the longest common subsequence between the two given sequences. @@ -742,6 +794,10 @@ def longest_common_subsequence(seq1: OneDimensionalArray, seq2: OneDimensionalAr The first sequence. seq2: OneDimensionalArray The second sequence. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -775,6 +831,8 @@ def longest_common_subsequence(seq1: OneDimensionalArray, seq2: OneDimensionalAr The data types of elements across both the sequences should be same and should be comparable. """ + raise_if_backend_is_not_python( + longest_common_subsequence, kwargs.get('backend', Backend.PYTHON)) row = len(seq1) col = len(seq2) check_mat = {0: [(0, []) for _ in range(col + 1)]} @@ -819,6 +877,10 @@ def is_ordered(array, **kwargs): Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -836,8 +898,9 @@ def is_ordered(array, **kwargs): >>> arr1 = OneDimensionalArray(int, [1, 2, 3]) >>> is_ordered(arr1, start=0, end=1, comp=lambda u, v: u > v) False - """ + raise_if_backend_is_not_python( + is_ordered, kwargs.get('backend', Backend.PYTHON)) lower = kwargs.get('start', 0) upper = kwargs.get('end', len(array) - 1) comp = kwargs.get("comp", lambda u, v: u <= v) @@ -874,6 +937,10 @@ def upper_bound(array, value, **kwargs): Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -899,6 +966,8 @@ def upper_bound(array, value, **kwargs): DynamicOneDimensionalArray objects may not work as expected. """ + raise_if_backend_is_not_python( + upper_bound, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array)) comp = kwargs.get('comp', lambda x, y: x < y) @@ -941,6 +1010,10 @@ def lower_bound(array, value, **kwargs): Optional, by default, less than or equal to is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -966,6 +1039,8 @@ def lower_bound(array, value, **kwargs): DynamicOneDimensionalArray objects may not work as expected. """ + raise_if_backend_is_not_python( + lower_bound, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array)) comp = kwargs.get('comp', lambda x, y: x < y) @@ -982,7 +1057,7 @@ def lower_bound(array, value, **kwargs): inclusive_end = mid - 1 return index -def longest_increasing_subsequence(array): +def longest_increasing_subsequence(array, **kwargs): """ Returns the longest increasing subsequence (as a OneDimensionalArray) that can be obtained from a given OneDimensionalArray. A subsequence @@ -995,6 +1070,10 @@ def longest_increasing_subsequence(array): array: OneDimensionalArray The given array in the form of a OneDimensionalArray + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -1017,6 +1096,9 @@ def longest_increasing_subsequence(array): >>> str(longest_inc_subsequence) '[-1, 2, 3, 7, 9, 10]' """ + raise_if_backend_is_not_python( + longest_increasing_subsequence, + kwargs.get('backend', Backend.PYTHON)) n = len(array) dp = OneDimensionalArray(int, n) dp.fill(0) @@ -1096,6 +1178,10 @@ def next_permutation(array, **kwargs): desired lexicographical ordering. Optional, by default, less than is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns @@ -1126,6 +1212,8 @@ def next_permutation(array, **kwargs): .. [1] http://www.cplusplus.com/reference/algorithm/next_permutation/ """ + raise_if_backend_is_not_python( + next_permutation, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) comp = kwargs.get('comp', lambda x, y: x < y) @@ -1163,6 +1251,10 @@ def prev_permutation(array, **kwargs): desired lexicographical ordering. Optional, by default, less than is used for comparing two values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns @@ -1193,6 +1285,8 @@ def prev_permutation(array, **kwargs): .. [1] http://www.cplusplus.com/reference/algorithm/prev_permutation/ """ + raise_if_backend_is_not_python( + prev_permutation, kwargs.get('backend', Backend.PYTHON)) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) comp = kwargs.get('comp', lambda x, y: x < y) diff --git a/pydatastructs/linear_data_structures/arrays.py b/pydatastructs/linear_data_structures/arrays.py index e4bc24223..8ad8c57b3 100644 --- a/pydatastructs/linear_data_structures/arrays.py +++ b/pydatastructs/linear_data_structures/arrays.py @@ -1,4 +1,6 @@ -from pydatastructs.utils.misc_util import _check_type, NoneType +from pydatastructs.utils.misc_util import ( + _check_type, NoneType, Backend, + raise_if_backend_is_not_python) __all__ = [ 'OneDimensionalArray', @@ -7,14 +9,14 @@ ] class Array(object): - ''' + """ Abstract class for arrays in pydatastructs. - ''' + """ def __str__(self) -> str: return str(self._data) class OneDimensionalArray(Array): - ''' + """ Represents one dimensional static arrays of fixed size. @@ -32,6 +34,10 @@ class OneDimensionalArray(Array): The inital value with which the element has to be initialized. By default none, used only when the data is not given. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Raises ====== @@ -64,11 +70,13 @@ class OneDimensionalArray(Array): ========== .. [1] https://en.wikipedia.org/wiki/Array_data_structure#One-dimensional_arrays - ''' + """ __slots__ = ['_size', '_data', '_dtype'] def __new__(cls, dtype=NoneType, *args, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if dtype is NoneType: raise ValueError("Data type is not defined.") if len(args) not in (1, 2): @@ -152,6 +160,10 @@ class MultiDimensionalArray(Array): A valid object type. *args: int The dimensions of the array. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Raises ====== @@ -185,6 +197,8 @@ class MultiDimensionalArray(Array): __slots__ = ['_sizes', '_data', '_dtype'] def __new__(cls, dtype: type = NoneType, *args, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if dtype is NoneType: raise ValueError("Data type is not defined.") elif not args: @@ -291,6 +305,10 @@ class DynamicOneDimensionalArray(DynamicArray, OneDimensionalArray): The number below which if the ratio, Num(T)/Size(T) falls then the array is contracted such that at most only half the positions are filled. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Raises ====== @@ -338,6 +356,8 @@ class DynamicOneDimensionalArray(DynamicArray, OneDimensionalArray): __slots__ = ['_load_factor', '_num', '_last_pos_filled', '_size'] def __new__(cls, dtype=NoneType, *args, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = super().__new__(cls, dtype, *args, **kwargs) obj._load_factor = float(kwargs.get('load_factor', 0.25)) obj._num = 0 if obj._size == 0 or obj[0] is None else obj._size @@ -413,6 +433,14 @@ class ArrayForTrees(DynamicOneDimensionalArray): """ Utility dynamic array for storing nodes of a tree. + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + See Also ======== diff --git a/pydatastructs/linear_data_structures/linked_lists.py b/pydatastructs/linear_data_structures/linked_lists.py index 7ed9cdb82..09178daf1 100644 --- a/pydatastructs/linear_data_structures/linked_lists.py +++ b/pydatastructs/linear_data_structures/linked_lists.py @@ -1,5 +1,7 @@ -from pydatastructs.utils.misc_util import _check_type, LinkedListNode, SkipNode import math, random +from pydatastructs.utils.misc_util import _check_type, LinkedListNode, SkipNode +from pydatastructs.utils.misc_util import ( + Backend, raise_if_backend_is_not_python) __all__ = [ 'SinglyLinkedList', @@ -208,6 +210,14 @@ class DoublyLinkedList(LinkedList): """ Represents Doubly Linked List + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -236,7 +246,9 @@ class DoublyLinkedList(LinkedList): """ __slots__ = ['head', 'tail', 'size'] - def __new__(cls): + def __new__(cls, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = LinkedList.__new__(cls) obj.head = None obj.tail = None @@ -348,6 +360,14 @@ class SinglyLinkedList(LinkedList): """ Represents Singly Linked List + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -376,7 +396,9 @@ class SinglyLinkedList(LinkedList): """ __slots__ = ['head', 'tail', 'size'] - def __new__(cls): + def __new__(cls, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = LinkedList.__new__(cls) obj.head = None obj.tail = None @@ -466,6 +488,14 @@ class SinglyCircularLinkedList(SinglyLinkedList): """ Represents Singly Circular Linked List. + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -526,6 +556,14 @@ class DoublyCircularLinkedList(DoublyLinkedList): """ Represents Doubly Circular Linked List + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -597,6 +635,14 @@ class SkipList(object): """ Represents Skip List + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -619,7 +665,9 @@ class SkipList(object): __slots__ = ['head', 'tail', '_levels', '_num_nodes', 'seed'] - def __new__(cls): + def __new__(cls, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.head, obj.tail = None, None obj._num_nodes = 0 @@ -751,7 +799,6 @@ def __str__(self): curr_node = curr_node.next walk = walk.down curr_level -= 1 - print(self._num_nodes, self._levels) sl_mat = [[str(None) for _ in range(self._num_nodes)] for _ in range(self._levels)] walk = self.head while walk is not None: diff --git a/pydatastructs/miscellaneous_data_structures/algorithms.py b/pydatastructs/miscellaneous_data_structures/algorithms.py index 1a4ffc56a..debc8cd0b 100644 --- a/pydatastructs/miscellaneous_data_structures/algorithms.py +++ b/pydatastructs/miscellaneous_data_structures/algorithms.py @@ -1,5 +1,7 @@ from pydatastructs.miscellaneous_data_structures.sparse_table import SparseTable -from pydatastructs.utils.misc_util import _check_range_query_inputs +from pydatastructs.utils.misc_util import ( + _check_range_query_inputs, Backend, + raise_if_backend_is_not_python) __all__ = ['RangeQueryStatic'] @@ -45,6 +47,10 @@ class RangeQueryStatic: asymptotically. By default, 'sparse_table'. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -69,7 +75,9 @@ class RangeQueryStatic: `RangeQueryStatic` object with this updated array. """ - def __new__(cls, array, func, data_structure='sparse_table'): + def __new__(cls, array, func, data_structure='sparse_table', **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if len(array) == 0: raise ValueError("Input %s array is empty."%(array)) @@ -107,7 +115,9 @@ class RangeQueryStaticSparseTable(RangeQueryStatic): __slots__ = ["sparse_table", "bounds"] - def __new__(cls, array, func): + def __new__(cls, array, func, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) sparse_table = SparseTable(array, func) obj.bounds = (0, len(array)) diff --git a/pydatastructs/miscellaneous_data_structures/binomial_trees.py b/pydatastructs/miscellaneous_data_structures/binomial_trees.py index 627c40fe7..9ea91d828 100644 --- a/pydatastructs/miscellaneous_data_structures/binomial_trees.py +++ b/pydatastructs/miscellaneous_data_structures/binomial_trees.py @@ -1,4 +1,6 @@ -from pydatastructs.utils.misc_util import BinomialTreeNode, _check_type +from pydatastructs.utils.misc_util import ( + BinomialTreeNode, _check_type, Backend, + raise_if_backend_is_not_python) __all__ = [ 'BinomialTree' @@ -17,6 +19,10 @@ class BinomialTree(object): order: int The order of the binomial tree. By default, None + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -34,7 +40,9 @@ class BinomialTree(object): """ __slots__ = ['root', 'order'] - def __new__(cls, root=None, order=None): + def __new__(cls, root=None, order=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if root is not None and \ not _check_type(root, BinomialTreeNode): raise TypeError("%s i.e., root should be of " diff --git a/pydatastructs/miscellaneous_data_structures/disjoint_set.py b/pydatastructs/miscellaneous_data_structures/disjoint_set.py index cac4f80e4..bc75b9958 100644 --- a/pydatastructs/miscellaneous_data_structures/disjoint_set.py +++ b/pydatastructs/miscellaneous_data_structures/disjoint_set.py @@ -1,4 +1,6 @@ from pydatastructs.utils import Set +from pydatastructs.utils.misc_util import ( + Backend, raise_if_backend_is_not_python) __all__ = ['DisjointSetForest'] @@ -6,6 +8,14 @@ class DisjointSetForest(object): """ Represents a forest of disjoint set trees. + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -28,7 +38,9 @@ class DisjointSetForest(object): __slots__ = ['tree'] - def __new__(cls): + def __new__(cls, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.tree = dict() return obj diff --git a/pydatastructs/miscellaneous_data_structures/queue.py b/pydatastructs/miscellaneous_data_structures/queue.py index cc9a50e67..033ef9af3 100644 --- a/pydatastructs/miscellaneous_data_structures/queue.py +++ b/pydatastructs/miscellaneous_data_structures/queue.py @@ -1,5 +1,6 @@ from pydatastructs.linear_data_structures import DynamicOneDimensionalArray, SinglyLinkedList -from pydatastructs.utils.misc_util import NoneType, LinkedListNode, _check_type +from pydatastructs.utils.misc_util import ( + NoneType, Backend, raise_if_backend_is_not_python) from pydatastructs.trees.heaps import BinaryHeap, BinomialHeap from copy import deepcopy as dc @@ -29,6 +30,10 @@ class Queue(object): Set to True if the queue should support additional, appendleft and pop operations from left and right sides respectively. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -50,6 +55,8 @@ class Queue(object): """ def __new__(cls, implementation='array', **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if implementation == 'array': return ArrayQueue( kwargs.get('items', None), @@ -100,7 +107,10 @@ class ArrayQueue(Queue): __slots__ = ['_front', '_rear', '_double_ended'] - def __new__(cls, items=None, dtype=NoneType, double_ended=False): + def __new__(cls, items=None, dtype=NoneType, double_ended=False, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if items is None: items = DynamicOneDimensionalArray(dtype, 0) else: @@ -204,7 +214,10 @@ class LinkedListQueue(Queue): __slots__ = ['queue', '_double_ended'] - def __new__(cls, items=None, double_ended=False): + def __new__(cls, items=None, double_ended=False, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.queue = SinglyLinkedList() if items is None: @@ -273,11 +286,15 @@ class PriorityQueue(object): used for supporting operations of priority queue. The following implementations are supported, + 'linked_list' -> Linked list implementation. + 'binary_heap' -> Binary heap implementation. + 'binomial_heap' -> Binomial heap implementation. Doesn't support custom comparators, minimum key data is extracted in every pop. + Optional, by default, 'binary_heap' implementation is used. comp: function @@ -286,6 +303,10 @@ class PriorityQueue(object): By default, `lambda u, v: u < v` is used to compare priorities i.e., minimum priority elements are extracted by pop operation. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -309,6 +330,8 @@ class PriorityQueue(object): """ def __new__(cls, implementation='binary_heap', **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) comp = kwargs.get("comp", lambda u, v: u < v) if implementation == 'linked_list': return LinkedListPriorityQueue(comp) @@ -370,7 +393,9 @@ class LinkedListPriorityQueue(PriorityQueue): def methods(cls): return ['__new__', 'push', 'pop', 'peek', 'is_empty'] - def __new__(cls, comp): + def __new__(cls, comp, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.items = SinglyLinkedList() obj.comp = comp @@ -416,7 +441,9 @@ class BinaryHeapPriorityQueue(PriorityQueue): def methods(cls): return ['__new__', 'push', 'pop', 'peek', 'is_empty'] - def __new__(cls, comp): + def __new__(cls, comp, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.items = BinaryHeap() obj.items._comp = comp @@ -447,7 +474,9 @@ class BinomialHeapPriorityQueue(PriorityQueue): def methods(cls): return ['__new__', 'push', 'pop', 'peek', 'is_empty'] - def __new__(cls): + def __new__(cls, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.items = BinomialHeap() return obj diff --git a/pydatastructs/miscellaneous_data_structures/sparse_table.py b/pydatastructs/miscellaneous_data_structures/sparse_table.py index 972612eb1..9d04c8ad2 100644 --- a/pydatastructs/miscellaneous_data_structures/sparse_table.py +++ b/pydatastructs/miscellaneous_data_structures/sparse_table.py @@ -1,6 +1,6 @@ -from pydatastructs.linear_data_structures.arrays import ( - MultiDimensionalArray, OneDimensionalArray) -from pydatastructs.utils.misc_util import NoneType +from pydatastructs.linear_data_structures.arrays import OneDimensionalArray +from pydatastructs.utils.misc_util import ( + Backend, raise_if_backend_is_not_python) import math __all__ = ['SparseTable'] @@ -31,6 +31,10 @@ class SparseTable(object): common divisor of a range. `summation` - For range sum queries. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -50,7 +54,9 @@ class SparseTable(object): __slots__ = ['_table', 'func'] - def __new__(cls, array, func): + def __new__(cls, array, func, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if len(array) == 0: raise ValueError("Input %s array is empty."%(array)) diff --git a/pydatastructs/miscellaneous_data_structures/stack.py b/pydatastructs/miscellaneous_data_structures/stack.py index 96df6cf02..cafbc7809 100644 --- a/pydatastructs/miscellaneous_data_structures/stack.py +++ b/pydatastructs/miscellaneous_data_structures/stack.py @@ -1,5 +1,7 @@ from pydatastructs.linear_data_structures import DynamicOneDimensionalArray, SinglyLinkedList -from pydatastructs.utils.misc_util import _check_type, NoneType +from pydatastructs.utils.misc_util import ( + _check_type, NoneType, Backend, + raise_if_backend_is_not_python) from copy import deepcopy as dc __all__ = [ @@ -26,6 +28,10 @@ class Stack(object): is None, otherwise takes the data type of DynamicOneDimensionalArray For array implementation. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -47,6 +53,8 @@ class Stack(object): """ def __new__(cls, implementation='array', **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if implementation == 'array': return ArrayStack( kwargs.get('items', None), @@ -84,7 +92,10 @@ class ArrayStack(Stack): __slots__ = ['items'] - def __new__(cls, items=None, dtype=NoneType): + def __new__(cls, items=None, dtype=NoneType, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if items is None: items = DynamicOneDimensionalArray(dtype, 0) else: @@ -133,7 +144,9 @@ class LinkedListStack(Stack): __slots__ = ['stack'] - def __new__(cls, items=None): + def __new__(cls, items=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.stack = SinglyLinkedList() if items is None: diff --git a/pydatastructs/strings/algorithms.py b/pydatastructs/strings/algorithms.py index a982acbf0..15463c12f 100644 --- a/pydatastructs/strings/algorithms.py +++ b/pydatastructs/strings/algorithms.py @@ -1,5 +1,7 @@ from pydatastructs.linear_data_structures.arrays import ( DynamicOneDimensionalArray, OneDimensionalArray) +from pydatastructs.utils.misc_util import ( + Backend, raise_if_backend_is_not_python) __all__ = [ 'find' @@ -7,7 +9,7 @@ PRIME_NUMBER, MOD = 257, 1000000007 -def find(text, query, algorithm): +def find(text, query, algorithm, **kwargs): """ Finds occurrence of a query string within the text string. @@ -27,6 +29,10 @@ def find(text, query, algorithm): 'kmp' -> Knuth-Morris-Pratt as given in [1]. 'rabin_karp' -> Rabin–Karp algorithm as given in [2]. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Returns ======= @@ -59,6 +65,8 @@ def find(text, query, algorithm): .. [1] https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm .. [2] https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm """ + raise_if_backend_is_not_python( + find, kwargs.get('backend', Backend.PYTHON)) import pydatastructs.strings.algorithms as algorithms func = "_" + algorithm if not hasattr(algorithms, func): diff --git a/pydatastructs/strings/trie.py b/pydatastructs/strings/trie.py index 5c9f39a4e..4975444e1 100644 --- a/pydatastructs/strings/trie.py +++ b/pydatastructs/strings/trie.py @@ -1,4 +1,6 @@ -from pydatastructs.utils.misc_util import TrieNode +from pydatastructs.utils.misc_util import ( + TrieNode, Backend, + raise_if_backend_is_not_python) from collections import deque import copy @@ -12,6 +14,14 @@ class Trie(object): """ Represents the trie data structure for storing strings. + Parameters + ========== + + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + Examples ======== @@ -41,7 +51,9 @@ def methods(cls): return ['__new__', 'insert', 'is_present', 'delete', 'strings_with_prefix'] - def __new__(cls): + def __new__(cls, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.root = TrieNode() return obj diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 14d01403e..34b265144 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1,11 +1,11 @@ +import random +from collections import deque as Queue from pydatastructs.utils import TreeNode, CartesianTreeNode, RedBlackTreeNode from pydatastructs.miscellaneous_data_structures import Stack -from pydatastructs.linear_data_structures import ( - OneDimensionalArray, DynamicOneDimensionalArray) +from pydatastructs.linear_data_structures import OneDimensionalArray from pydatastructs.linear_data_structures.arrays import ArrayForTrees -from collections import deque as Queue -import random -from copy import deepcopy +from pydatastructs.utils.misc_util import ( + Backend, raise_if_backend_is_not_python) __all__ = [ 'AVLTree', @@ -29,22 +29,23 @@ class BinaryTree(object): key Required if tree is to be instantiated with root otherwise not needed. - root_data Optional, the root node of the binary tree. If not of type TreeNode, it will consider root as data and a new root node will be created. - comp: lambda/function Optional, A lambda function which will be used for comparison of keys. Should return a bool value. By default it implements less than operator. - is_order_statistic: bool Set it to True, if you want to use the order statistic features of the tree. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. References ========== @@ -56,7 +57,9 @@ class BinaryTree(object): 'is_order_statistic'] def __new__(cls, key=None, root_data=None, comp=None, - is_order_statistic=False): + is_order_statistic=False, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) if key is None and root_data is not None: raise ValueError('Key required.') @@ -106,7 +109,6 @@ def delete(self, key, **kwargs): key The key of the node which is to be deleted. - balancing_info: bool Optional, by default, False The information needed for updating @@ -141,7 +143,6 @@ def search(self, key, **kwargs): key The key for searching. - parent: bool If true then returns index of the parent of the node with the passed @@ -1411,6 +1412,10 @@ class BinaryTreeTraversal(object): tree: BinaryTree The binary tree for whose traversal is to be done. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Traversals ========== @@ -1449,7 +1454,9 @@ def methods(cls): __slots__ = ['tree'] - def __new__(cls, tree): + def __new__(cls, tree, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if not isinstance(tree, BinaryTree): raise TypeError("%s is not a binary tree"%(tree)) obj = object.__new__(cls) @@ -1557,7 +1564,6 @@ def depth_first_search(self, order='in_order', node=None): return getattr(self, '_' + order)(node) def breadth_first_search(self, node=None, strategy='queue'): - # TODO: IMPLEMENT ITERATIVE DEEPENING-DEPTH FIRST SEARCH STRATEGY """ Computes the breadth first search traversal of a binary tree. @@ -1578,6 +1584,7 @@ def breadth_first_search(self, node=None, strategy='queue'): list Each element of the list is of type `TreeNode`. """ + # TODO: IMPLEMENT ITERATIVE DEEPENING-DEPTH FIRST SEARCH STRATEGY strategies = ('queue',) if strategy not in strategies: raise NotImplementedError( @@ -1606,6 +1613,10 @@ class BinaryIndexedTree(object): array: list/tuple The array whose elements are to be considered for the queries. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -1626,8 +1637,9 @@ class BinaryIndexedTree(object): __slots__ = ['tree', 'array', 'flag'] - def __new__(cls, array): - + def __new__(cls, array, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.array = OneDimensionalArray(type(array[0]), array) obj.tree = [0] * (obj.array._size + 2) diff --git a/pydatastructs/trees/heaps.py b/pydatastructs/trees/heaps.py index bded04c05..12133a6f1 100644 --- a/pydatastructs/trees/heaps.py +++ b/pydatastructs/trees/heaps.py @@ -1,5 +1,7 @@ -from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode, BinomialTreeNode -from pydatastructs.linear_data_structures.arrays import (ArrayForTrees, +from pydatastructs.utils.misc_util import ( + _check_type, TreeNode, BinomialTreeNode, + Backend, raise_if_backend_is_not_python) +from pydatastructs.linear_data_structures.arrays import ( DynamicOneDimensionalArray, Array) from pydatastructs.miscellaneous_data_structures.binomial_trees import BinomialTree @@ -24,12 +26,10 @@ class DHeap(Heap): Parameters ========== - elements : list, tuple, Array + elements: list, tuple, Array Optional, by default 'None'. list/tuple/Array of initial TreeNode in Heap. - - - heap_property : str + heap_property: str If the key stored in each node is either greater than or equal to the keys in the node's children @@ -40,6 +40,10 @@ class DHeap(Heap): then pass 'min'. By default, the heap property is set to 'min'. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -72,7 +76,10 @@ class DHeap(Heap): """ __slots__ = ['_comp', 'heap', 'd', 'heap_property', '_last_pos_filled'] - def __new__(cls, elements=None, heap_property="min", d=4): + def __new__(cls, elements=None, heap_property="min", d=4, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = Heap.__new__(cls) obj.heap_property = heap_property obj.d = d @@ -142,7 +149,6 @@ def insert(self, key, data=None): key The key for comparison. - data The data to be inserted. @@ -172,7 +178,7 @@ def extract(self): Returns ======= - root_element : TreeNode + root_element: TreeNode The TreeNode at the root of the heap, if the heap is not empty. @@ -218,11 +224,10 @@ class BinaryHeap(DHeap): Parameters ========== - elements : list, tuple + elements: list, tuple Optional, by default 'None'. List/tuple of initial elements in Heap. - - heap_property : str + heap_property: str If the key stored in each node is either greater than or equal to the keys in the node's children @@ -233,6 +238,10 @@ class BinaryHeap(DHeap): then pass 'min'. By default, the heap property is set to 'min'. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -263,7 +272,10 @@ class BinaryHeap(DHeap): .. [1] https://en.m.wikipedia.org/wiki/Binary_heap """ - def __new__(cls, elements=None, heap_property="min"): + def __new__(cls, elements=None, heap_property="min", + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = DHeap.__new__(cls, elements, heap_property, 2) return obj @@ -279,11 +291,10 @@ class TernaryHeap(DHeap): Parameters ========== - elements : list, tuple + elements: list, tuple Optional, by default 'None'. List/tuple of initial elements in Heap. - - heap_property : str + heap_property: str If the key stored in each node is either greater than or equal to the keys in the node's children @@ -294,6 +305,10 @@ class TernaryHeap(DHeap): then pass 'min'. By default, the heap property is set to 'min'. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -327,7 +342,10 @@ class TernaryHeap(DHeap): .. [1] https://en.wikipedia.org/wiki/D-ary_heap .. [2] https://ece.uwaterloo.ca/~dwharder/aads/Algorithms/d-ary_heaps/Ternary_heaps/ """ - def __new__(cls, elements=None, heap_property="min"): + def __new__(cls, elements=None, heap_property="min", + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = DHeap.__new__(cls, elements, heap_property, 3) return obj @@ -347,6 +365,10 @@ class BinomialHeap(Heap): By default, [] The list of BinomialTree object references in sorted order. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -367,7 +389,9 @@ class BinomialHeap(Heap): """ __slots__ = ['root_list'] - def __new__(cls, root_list=None): + def __new__(cls, root_list=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if root_list is None: root_list = [] if not all((_check_type(root, BinomialTree)) @@ -528,7 +552,6 @@ def decrease_key(self, node, new_key): node: BinomialTreeNode The node whose key is to be reduced. - new_key The new key of the given node, should be less than the current key. diff --git a/pydatastructs/trees/m_ary_trees.py b/pydatastructs/trees/m_ary_trees.py index 97c90bb28..a06fda9ee 100644 --- a/pydatastructs/trees/m_ary_trees.py +++ b/pydatastructs/trees/m_ary_trees.py @@ -1,5 +1,7 @@ from pydatastructs.utils import MAryTreeNode from pydatastructs.linear_data_structures.arrays import ArrayForTrees +from pydatastructs.utils.misc_util import ( + Backend, raise_if_backend_is_not_python) __all__ = [ 'MAryTree' @@ -15,23 +17,19 @@ class MAryTree(object): key Required if tree is to be instantiated with root otherwise not needed. - root_data Optional, the root node of the binary tree. If not of type MAryTreeNode, it will consider root as data and a new root node will be created. - comp: lambda Optional, A lambda function which will be used for comparison of keys. Should return a bool value. By default it implements less than operator. - is_order_statistic: bool Set it to True, if you want to use the order statistic features of the tree. - max_children Optional, specifies the maximum number of children a node can have. Defaults to 2 in case nothing is @@ -48,7 +46,10 @@ class MAryTree(object): def __new__(cls, key=None, root_data=None, comp=None, - is_order_statistic=False, max_children=2): + is_order_statistic=False, max_children=2, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) if key is None and root_data is not None: raise ValueError('Key required.') @@ -77,7 +78,6 @@ def insert(self, key, data=None): key The key for comparison. - data The data to be inserted. @@ -127,7 +127,6 @@ def search(self, key, **kwargs): key The key for searching. - parent: bool If true then returns index of the parent of the node with the passed diff --git a/pydatastructs/trees/space_partitioning_trees.py b/pydatastructs/trees/space_partitioning_trees.py index af5651be1..f13c1f280 100644 --- a/pydatastructs/trees/space_partitioning_trees.py +++ b/pydatastructs/trees/space_partitioning_trees.py @@ -1,6 +1,8 @@ from pydatastructs.utils import TreeNode from collections import deque as Queue -from pydatastructs.linear_data_structures.arrays import _check_type +from pydatastructs.utils.misc_util import ( + _check_type, Backend, + raise_if_backend_is_not_python) __all__ = [ 'OneDimensionalSegmentTree' @@ -16,6 +18,10 @@ class OneDimensionalSegmentTree(object): segs: list/tuple/set The segs should contains tuples/list/set of size 2 denoting the start and end points of the intervals. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Examples ======== @@ -44,7 +50,9 @@ class OneDimensionalSegmentTree(object): __slots__ = ['segments', 'tree', 'root_idx', 'cache'] - def __new__(cls, segs): + def __new__(cls, segs, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) if any((not isinstance(seg, (tuple, list, set)) or len(seg) != 2) for seg in segs): diff --git a/pydatastructs/utils/__init__.py b/pydatastructs/utils/__init__.py index e0b6067cf..9f2fee894 100644 --- a/pydatastructs/utils/__init__.py +++ b/pydatastructs/utils/__init__.py @@ -16,6 +16,7 @@ SkipNode, summation, greatest_common_divisor, - minimum + minimum, + Backend ) __all__.extend(misc_util.__all__) diff --git a/pydatastructs/utils/misc_util.py b/pydatastructs/utils/misc_util.py index 845b02db6..8c6d9dc5c 100644 --- a/pydatastructs/utils/misc_util.py +++ b/pydatastructs/utils/misc_util.py @@ -1,3 +1,6 @@ +import math, pydatastructs +from enum import Enum + __all__ = [ 'TreeNode', 'MAryTreeNode', @@ -13,11 +16,22 @@ 'SkipNode', 'minimum', 'summation', - 'greatest_common_divisor' + 'greatest_common_divisor', + 'Backend' ] -import math +class Backend(Enum): + + PYTHON = 'Python' + + def __str__(self): + return self.value + +def raise_if_backend_is_not_python(api, backend): + if backend != Backend.PYTHON: + raise ValueError("As of {} version, only {} backend is supported for {} API".format( + pydatastructs.__version__, str(Backend.PYTHON), api)) _check_type = lambda a, t: isinstance(a, t) NoneType = type(None) @@ -43,6 +57,10 @@ class TreeNode(Node): Optional, index of the left child node. right: int Optional, index of the right child node. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['key', 'data', 'left', 'right', 'is_root', @@ -52,7 +70,9 @@ class TreeNode(Node): def methods(cls): return ['__new__', '__str__'] - def __new__(cls, key, data=None): + def __new__(cls, key, data=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = Node.__new__(cls) obj.data, obj.key = data, key obj.left, obj.right, obj.parent, obj.height, obj.size = \ @@ -79,11 +99,16 @@ class CartesianTreeNode(TreeNode): Any valid data to be stored in the node. priority: int An integer value for heap property. - + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['key', 'data', 'priority'] - def __new__(cls, key, priority, data=None): + def __new__(cls, key, priority, data=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = TreeNode.__new__(cls, key, data) obj.priority = priority return obj @@ -107,7 +132,10 @@ class RedBlackTreeNode(TreeNode): Any valid data to be stored in the node. color 0 for black and 1 for red. - + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['key', 'data', 'color'] @@ -115,7 +143,9 @@ class RedBlackTreeNode(TreeNode): def methods(cls): return ['__new__'] - def __new__(cls, key, data=None): + def __new__(cls, key, data=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = TreeNode.__new__(cls, key, data) obj.color = 1 return obj @@ -131,6 +161,10 @@ class BinomialTreeNode(TreeNode): Required for comparison operations. data Any valid data to be stored in the node. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Note ==== @@ -146,6 +180,10 @@ class BinomialTreeNode(TreeNode): is_root: bool, by default, False If the current node is a root of the tree then set it to True otherwise False. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['parent', 'key', 'children', 'data', 'is_root'] @@ -153,7 +191,9 @@ class BinomialTreeNode(TreeNode): def methods(cls): return ['__new__', 'add_children', '__str__'] - def __new__(cls, key, data=None): + def __new__(cls, key, data=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) from pydatastructs.linear_data_structures.arrays import DynamicOneDimensionalArray obj = Node.__new__(cls) obj.data, obj.key = data, key @@ -189,6 +229,10 @@ class MAryTreeNode(TreeNode): Required for comparison operations. data Any valid data to be stored in the node. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. Note ==== @@ -208,7 +252,9 @@ class MAryTreeNode(TreeNode): def methods(cls): return ['__new__', 'add_children', '__str__'] - def __new__(cls, key, data=None): + def __new__(cls, key, data=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) from pydatastructs.linear_data_structures.arrays import DynamicOneDimensionalArray obj = Node.__new__(cls) obj.data = data @@ -241,15 +287,24 @@ class LinkedListNode(Node): data Any valid data to be stored in the node. links - List of names of attributes which should be used as links to other nodes. + List of names of attributes which should + be used as links to other nodes. addrs - List of address of nodes to be assigned to each of the attributes in links. + List of address of nodes to be assigned to + each of the attributes in links. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ @classmethod def methods(cls): return ['__new__', '__str__'] - def __new__(cls, key, data=None, links=None, addrs=None): + def __new__(cls, key, data=None, links=None, addrs=None, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) if links is None: links = ['next'] if addrs is None: @@ -278,16 +333,25 @@ class SkipNode(Node): data Any valid data to be stored in the node. next - Reference to the node lying just forward to the current node. + Reference to the node lying just forward + to the current node. Optional, by default, None. down - Reference to the node lying just below the current node. + Reference to the node lying just below the + current node. Optional, by default, None. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['key', 'data', 'next', 'down'] - def __new__(cls, key, data=None, next=None, down=None): + def __new__(cls, key, data=None, next=None, down=None, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = Node.__new__(cls) obj.key, obj.data = key, data obj.next, obj.down = next, down @@ -320,13 +384,20 @@ class AdjacencyListGraphNode(GraphNode): Any valid iterator to initialize the adjacent nodes of the current node. Optional, by default, None + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ @classmethod def methods(cls): return ['__new__', 'add_adjacent_node', 'remove_adjacent_node'] - def __new__(cls, name, data=None, adjacency_list=None): + def __new__(cls, name, data=None, adjacency_list=None, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = GraphNode.__new__(cls) obj.name, obj.data = str(name), data obj._impl = 'adjacency_list' @@ -371,6 +442,10 @@ class AdjacencyMatrixGraphNode(GraphNode): The index of the node in the AdjacencyMatrix. data The data to be stored at each graph node. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['name', 'data'] @@ -378,7 +453,10 @@ class AdjacencyMatrixGraphNode(GraphNode): def methods(cls): return ['__new__'] - def __new__(cls, name, data=None): + def __new__(cls, name, data=None, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = GraphNode.__new__(cls) obj.name, obj.data, obj.is_connected = \ str(name), data, None @@ -396,12 +474,19 @@ class GraphEdge(object): The source node of the edge. node2: GraphNode or it's child classes The target node of the edge. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ @classmethod def methods(cls): return ['__new__', '__str__'] - def __new__(cls, node1, node2, value=None): + def __new__(cls, node1, node2, value=None, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.source, obj.target = node1, node2 obj.value = value @@ -422,6 +507,10 @@ class Set(object): the set. data: Python object The data to be stored in the set. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['parent', 'size', 'key', 'data'] @@ -430,7 +519,10 @@ class Set(object): def methods(cls): return ['__new__'] - def __new__(cls, key, data=None): + def __new__(cls, key, data=None, + **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = object.__new__(cls) obj.key = key obj.data = data @@ -446,6 +538,10 @@ class TrieNode(Node): char: The character stored in the current node. Optional, by default None. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. """ __slots__ = ['char', '_children', 'is_terminal'] @@ -454,7 +550,9 @@ class TrieNode(Node): def methods(cls): return ['__new__', 'add_child', 'get_child', 'remove_child'] - def __new__(cls, char=None): + def __new__(cls, char=None, **kwargs): + raise_if_backend_is_not_python( + cls, kwargs.get('backend', Backend.PYTHON)) obj = Node.__new__(cls) obj.char = char obj._children = {} diff --git a/pydatastructs/utils/tests/test_code_quality.py b/pydatastructs/utils/tests/test_code_quality.py index 138a0750f..9a5ee418b 100644 --- a/pydatastructs/utils/tests/test_code_quality.py +++ b/pydatastructs/utils/tests/test_code_quality.py @@ -1,4 +1,5 @@ import os, re, sys, pydatastructs, inspect +from typing import Type def _list_files(): root_path = os.path.abspath( @@ -128,3 +129,26 @@ def test_public_api(): "have %s method implemented."%( _class, method )) + +def test_backend_argument_message(): + + def call_and_raise(api, pos_args_count=0): + try: + if pos_args_count == 0: + api(backend=None) + elif pos_args_count == 1: + api(None, backend=None) + elif pos_args_count == 2: + api(None, None, backend=None) + except ValueError as value_error: + assert str(api) in value_error.args[0] + except TypeError as type_error: + max_pos_args_count = 2 + if pos_args_count <= max_pos_args_count: + call_and_raise(api, pos_args_count + 1) + else: + raise type_error + + apis = _apis() + for api in apis: + call_and_raise(api, 0)