Repository: codezonediitj/pydatastructs (2021-01-13 ~ 2021-01-23)

problem_statement: Quick Sort
#### Description of the problem
I suggest adding quicksort algorithm in linear data structures
i.e linear_data_structures.algorithms.quick_sort()

There are some sorting algorithms already there, with same nlogn complexity, but since most inbuilt sorting algorithms are implemented using quicksort, we should also add quick sort
date: 2021-01-13
patch: diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py
index dcf4816..89cca83 100644
--- a/pydatastructs/linear_data_structures/__init__.py
+++ b/pydatastructs/linear_data_structures/__init__.py
@@ -29,6 +29,7 @@ from .algorithms import (
     matrix_multiply_parallel,
     counting_sort,
     bucket_sort,
-    cocktail_shaker_sort
+    cocktail_shaker_sort,
+    quick_sort
 )
 __all__.extend(algorithms.__all__)
diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py
index 36dc169..718e9b8 100644
--- a/pydatastructs/linear_data_structures/algorithms.py
+++ b/pydatastructs/linear_data_structures/algorithms.py
@@ -12,7 +12,8 @@ __all__ = [
     'matrix_multiply_parallel',
     'counting_sort',
     'bucket_sort',
-    'cocktail_shaker_sort'
+    'cocktail_shaker_sort',
+    'quick_sort'
 ]
 
 def _merge(array, sl, el, sr, er, end, comp):
@@ -624,3 +625,100 @@ def cocktail_shaker_sort(array: Array, **kwargs) -> Array:
         array._modify(force=True)
 
     return array
+
+def quick_sort(array: Array, **kwargs) -> Array:
+    """
+    Performs quick sort on the given array.
+
+    Parameters
+    ==========
+
+    array: Array
+        The array which is to be sorted.
+    start: int
+        The starting index of the portion
+        which is to be sorted.
+        Optional, by default 0
+    end: int
+        The ending index of the portion which
+        is to be sorted.
+        Optional, by default the index
+        of the last position filled.
+    comp: lambda/function
+        The comparator which is to be used
+        for sorting. If the function returns
+        False then only swapping is performed.
+        Optional, by default, less than or
+        equal to is used for comparing two
+        values.
+    pick_pivot_element: lambda/function
+        The function implementing the pivot picking
+        logic for quick sort. Should accept, `low`,
+        `high`, and `array` in this order, where `low`
+        represents the left end of the current partition,
+        `high` represents the right end, and `array` is
+        the original input array to `quick_sort` function.
+        Optional, by default, picks the element at `high`
+        index of the current partition as pivot.
+
+    Returns
+    =======
+
+    output: Array
+        The sorted array.
+
+    Examples
+    ========
+
+    >>> from pydatastructs import OneDimensionalArray as ODA, quick_sort
+    >>> arr = ODA(int, [5, 78, 1, 0])
+    >>> out = quick_sort(arr)
+    >>> str(out)
+    '[0, 1, 5, 78]'
+    >>> arr = ODA(int, [21, 37, 5])
+    >>> out = quick_sort(arr)
+    >>> str(out)
+    '[5, 21, 37]'
+
+    References
+    ==========
+
+    .. [1] https://en.wikipedia.org/wiki/Quicksort
+    """
+    from pydatastructs import Stack
+    comp = kwargs.get("comp", lambda u, v: u <= v)
+    pick_pivot_element = kwargs.get("pick_pivot_element",
+                                    lambda low, high, array: array[high])
+
+    def partition(low, high, pick_pivot_element):
+        i = (low - 1)
+        x = pick_pivot_element(low, high, array)
+        for j in range(low , high):
+            if _comp(array[j], x, comp) is True:
+                i = i + 1
+                array[i], array[j] = array[j], array[i]
+        array[i + 1], array[high] = array[high], array[i + 1]
+        return (i + 1)
+
+    lower = kwargs.get('start', 0)
+    upper = kwargs.get('end', len(array) - 1)
+    stack = Stack()
+
+    stack.push(lower)
+    stack.push(upper)
+
+    while stack.is_empty is False:
+        high = stack.pop()
+        low = stack.pop()
+        p = partition(low, high, pick_pivot_element)
+        if p - 1 > low:
+            stack.push(low)
+            stack.push(p - 1)
+        if p + 1 < high:
+            stack.push(p + 1)
+            stack.push(high)
+
+    if _check_type(array, DynamicArray):
+        array._modify(force=True)
+
+    return array

test_patch: diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py
index d7c8c51..60effb1 100644
--- a/pydatastructs/linear_data_structures/tests/test_algorithms.py
+++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py
@@ -1,7 +1,8 @@
 from pydatastructs import (
     merge_sort_parallel, DynamicOneDimensionalArray,
     OneDimensionalArray, brick_sort, brick_sort_parallel,
-    heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort)
+    heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort, quick_sort)
+
 from pydatastructs.utils.raises_util import raises
 import random
 
@@ -73,6 +74,9 @@ def test_counting_sort():
 def test_cocktail_shaker_sort():
     _test_common_sort(cocktail_shaker_sort)
 
+def test_quick_sort():
+    _test_common_sort(quick_sort)
+
 def test_matrix_multiply_parallel():
     ODA = OneDimensionalArray
 


----

problem_statement: Longest common subsequence
Description of the problem
LCS Problem Statement: Given two sequences, find the length of longest subsequence present in both of them. A subsequence is a sequence that appears in the same relative order, but not necessarily contiguous.
Example of the problem
For example, “abc”, “abg”, “bdf”, “aeg”, ‘”acefg”, .. etc are subsequences of “abcdefg”.


date: 2021-01-16
patch: diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py
index 89cca83..d4d770c 100644
--- a/pydatastructs/linear_data_structures/__init__.py
+++ b/pydatastructs/linear_data_structures/__init__.py
@@ -30,6 +30,7 @@ from .algorithms import (
     counting_sort,
     bucket_sort,
     cocktail_shaker_sort,
-    quick_sort
+    quick_sort,
+    longest_common_subsequence
 )
 __all__.extend(algorithms.__all__)
diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py
index 718e9b8..c6022c1 100644
--- a/pydatastructs/linear_data_structures/algorithms.py
+++ b/pydatastructs/linear_data_structures/algorithms.py
@@ -13,7 +13,8 @@ __all__ = [
     'counting_sort',
     'bucket_sort',
     'cocktail_shaker_sort',
-    'quick_sort'
+    'quick_sort',
+    'longest_common_subsequence'
 ]
 
 def _merge(array, sl, el, sr, er, end, comp):
@@ -722,3 +723,67 @@ def quick_sort(array: Array, **kwargs) -> Array:
         array._modify(force=True)
 
     return array
+
+def longest_common_subsequence(seq1: OneDimensionalArray, seq2: OneDimensionalArray) -> OneDimensionalArray:
+    """
+    Finds the longest common subsequence between the
+    two given sequences.
+
+    Parameters
+    ========
+
+    seq1: OneDimensionalArray
+        The first sequence.
+    seq2: OneDimensionalArray
+        The second sequence.
+
+    Returns
+    =======
+
+    output: OneDimensionalArray
+        The longest common subsequence.
+
+    Examples
+    ========
+
+    >>> from pydatastructs import longest_common_subsequence as LCS, OneDimensionalArray as ODA
+    >>> arr1 = ODA(str, ['A', 'B', 'C', 'D', 'E'])
+    >>> arr2 = ODA(str, ['A', 'B', 'C', 'G' ,'D', 'E', 'F'])
+    >>> lcs = LCS(arr1, arr2)
+    >>> str(lcs)
+    "['A', 'B', 'C', 'D', 'E']"
+    >>> arr1 = ODA(str, ['A', 'P', 'P'])
+    >>> arr2 = ODA(str, ['A', 'p', 'P', 'S', 'P'])
+    >>> lcs = LCS(arr1, arr2)
+    >>> str(lcs)
+    "['A', 'P', 'P']"
+
+    References
+    ==========
+
+    .. [1] https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
+
+    Note
+    ====
+
+    The data types of elements across both the sequences
+    should be same and should be comparable.
+    """
+    row = len(seq1)
+    col = len(seq2)
+    check_mat = {0: [(0, []) for _ in range(col + 1)]}
+
+    for i in range(1, row + 1):
+        check_mat[i] = [(0, []) for _ in range(col + 1)]
+        for j in range(1, col + 1):
+            if seq1[i-1] == seq2[j-1]:
+                temp = check_mat[i-1][j-1][1][:]
+                temp.append(seq1[i-1])
+                check_mat[i][j] = (check_mat[i-1][j-1][0] + 1, temp)
+            else:
+                if check_mat[i-1][j][0] > check_mat[i][j-1][0]:
+                    check_mat[i][j] = check_mat[i-1][j]
+                else:
+                    check_mat[i][j] = check_mat[i][j-1]
+
+    return OneDimensionalArray(seq1._dtype, check_mat[row][col][-1])

test_patch: diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py
index 60effb1..f50a3da 100644
--- a/pydatastructs/linear_data_structures/tests/test_algorithms.py
+++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py
@@ -1,7 +1,8 @@
 from pydatastructs import (
     merge_sort_parallel, DynamicOneDimensionalArray,
     OneDimensionalArray, brick_sort, brick_sort_parallel,
-    heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort, quick_sort)
+    heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort, quick_sort, longest_common_subsequence)
+
 
 from pydatastructs.utils.raises_util import raises
 import random
@@ -100,3 +101,28 @@ def test_matrix_multiply_parallel():
     J = [[2, 1, 2], [1, 2, 1], [2, 2, 2]]
     output = matrix_multiply_parallel(I, J, num_threads=1)
     assert expected_result == output
+
+def test_longest_common_sequence():
+    ODA = OneDimensionalArray
+    expected_result = "['A', 'S', 'C', 'I', 'I']"
+
+    str1 = ODA(str, ['A', 'A', 'S', 'C', 'C', 'I', 'I'])
+    str2 = ODA(str, ['A', 'S', 'S', 'C', 'I', 'I', 'I', 'I'])
+    output = longest_common_subsequence(str1, str2)
+    assert str(output) == expected_result
+
+    expected_result = "['O', 'V', 'A']"
+
+    I = ODA(str, ['O', 'V', 'A', 'L'])
+    J = ODA(str, ['F', 'O', 'R', 'V', 'A', 'E', 'W'])
+    output = longest_common_subsequence(I, J)
+    assert str(output) == expected_result
+
+    X = ODA(int, [1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1])
+    Y = ODA(int, [1, 2, 3, 4, 4, 3, 2, 1])
+    output = longest_common_subsequence(X, Y)
+    assert str(output) == '[1, 2, 3, 4, 4, 3, 2, 1]'
+
+    Z = ODA(int, [])
+    output = longest_common_subsequence(Y, Z)
+    assert str(output) == '[]'


----

problem_statement: Array implementation of doubly ended queue (deque)
#### Description of the problem
I propose the implementation of a doubly ended queue (deque) using arrays. A deque allows insertions and deletions from both the front and the rear end.  The various methods available will be insert front, insert rear, delete front , delete rear in addition to returning the element at front or rear.

Hope this task will be assigned to me
date: 2021-01-23
patch: diff --git a/pydatastructs/__init__.py b/pydatastructs/__init__.py
--- a/pydatastructs/__init__.py
+++ b/pydatastructs/__init__.py
@@ -1,4 +1,5 @@
 from .linear_data_structures import *
+# noop comment for filter
 from .trees import *
 from .miscellaneous_data_structures import *
 from .utils import *

test_patch: diff --git a/pydatastructs/miscellaneous_data_structures/tests/test_queue.py b/pydatastructs/miscellaneous_data_structures/tests/test_queue.py
index b8ef5ee..3ae9370 100644
--- a/pydatastructs/miscellaneous_data_structures/tests/test_queue.py
+++ b/pydatastructs/miscellaneous_data_structures/tests/test_queue.py
@@ -30,6 +30,23 @@ def test_ArrayQueue():
     assert q1.popleft() == 3
     assert len(q1) == 0
 
+    q2 = Queue(implementation='array', items=[0], double_ended=True)
+    q2.append(1)
+    q2.append(2)
+    q2.appendleft(3)
+    assert str(q2) == '[3, 0, 1, 2]'
+    assert len(q2) == 4
+    assert q2.popleft() == 3
+    assert q2.pop() == 2
+    assert len(q2) == 2
+    assert q2.popleft() == 0
+    assert q2.pop() == 1
+    assert len(q2) == 0
+
+    q1 = Queue(implementation='array', items=[0])
+    assert raises(NotImplementedError, lambda: q1.appendleft(2))
+
+
 def test_LinkedListQueue():
     q1 = Queue(implementation='linked_list')
     q1.append(1)
@@ -49,7 +66,6 @@ def test_LinkedListQueue():
 
     q1 = Queue(implementation='linked_list',items=['a',None,type,{}])
     assert len(q1) == 4
-    assert q1.size == 4
 
     front = q1.front
     assert front.key == q1.popleft().key
@@ -60,6 +76,23 @@ def test_LinkedListQueue():
 
     assert rear.key == q1.popleft().key
 
+    q1 = Queue(implementation='linked_list', double_ended=True)
+    q1.appendleft(1)
+    q2 = Queue(implementation='linked_list', items=[0, 1])
+    assert raises(NotImplementedError, lambda: q2.appendleft(1))
+    q1 = Queue(implementation='linked_list', items = [0, 1], double_ended=True)
+    q1.appendleft(2)
+    q1.append(3)
+    assert str(q1) == "['2', '0', '1', '3']"
+    assert len(q1) == 4
+    assert q1.popleft().key == 2
+    assert q1.pop().key == 3
+    assert len(q1) == 2
+    assert q1.pop().key == 1
+    assert q1.popleft().key == 0
+    assert len(q1) == 0
+    assert raises(IndexError, lambda: q1.popleft())
+
 def test_PriorityQueue():
     pq1 = PriorityQueue(implementation='linked_list')
     assert _check_type(pq1, LinkedListPriorityQueue) is True