Repository: keon/algorithms
Pull request #489 (2019-03-28 08:02:23)

fix bugs, add descriptions, and add tests

coveralls: ## Pull Request Test Coverage Report for [Build 878](https://coveralls.io/builds/22451375)

* **48** of **49**   **(97.96%)**  changed or added relevant lines in **4** files are covered.
* **1** unchanged line in **1** file lost coverage.
* Overall coverage increased (+**1.4%**) to **69.184%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [tests/test_dfs.py](https://coveralls.io/builds/22451375/source?filename=tests%2Ftest_dfs.py#L55) | 28 | 29 | 96.55%
<!-- | **Total:** | **48** | **49** | **97.96%** | -->

|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [algorithms/linkedlist/is_palindrome.py](https://coveralls.io/builds/22451375/source?filename=algorithms%2Flinkedlist%2Fis_palindrome.py#L88) | 1 | 93.33% |
<!-- | **Total:** | **1** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/22451375/badge)](https://coveralls.io/builds/22451375) |
| :-- | --: |
| Change from base [Build 877](https://coveralls.io/builds/22400172): |  1.4% |
| Covered Lines: | 5121 |
| Relevant Lines: | 7402 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/algorithms/bfs/maze_search.py b/algorithms/bfs/maze_search.py
index efa65c3..436b3ef 100644
--- a/algorithms/bfs/maze_search.py
+++ b/algorithms/bfs/maze_search.py
@@ -7,6 +7,21 @@ do BFS from (0,0) of the grid and get the minimum number of steps needed to get
 only step on the columns whose value is 1
 
 if there is no path, it returns -1
+
+Ex 1)
+If grid is
+[[1,0,1,1,1,1],
+ [1,0,1,0,1,0],
+ [1,0,1,0,1,1],
+ [1,1,1,0,1,1]], 
+the answer is: 14
+
+Ex 2)
+If grid is
+[[1,0,0],
+ [0,1,1],
+ [0,1,1]], 
+the answer is: -1
 '''
 
 def maze_search(grid):
diff --git a/algorithms/bfs/word_ladder.py b/algorithms/bfs/word_ladder.py
index 2fdbf29..6767571 100644
--- a/algorithms/bfs/word_ladder.py
+++ b/algorithms/bfs/word_ladder.py
@@ -13,13 +13,12 @@ end_word = "cog"
 word_list = ["hot","dot","dog","lot","log"]
 As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
 return its length 5.
-.
+
 Note:
 Return -1 if there is no such transformation sequence.
 All words have the same length.
 All words contain only lowercase alphabetic characters.
 """
-import unittest
 
 
 def ladder_length(begin_word, end_word, word_list):
@@ -71,29 +70,3 @@ def word_range(word):
         for c in [chr(x) for x in range(ord('a'), ord('z') + 1)]:
             if c != temp:
                 yield word[:ind] + c + word[ind + 1:]
-
-
-class TestSuite(unittest.TestCase):
-
-    def test_ladder_length(self):
-
-        # hit -> hot -> dot -> dog -> cog
-        self.assertEqual(5, ladder_length('hit', 'cog', ["hot", "dot", "dog", "lot", "log"]))
-
-        # pick -> sick -> sink -> sank -> tank == 5
-        self.assertEqual(5, ladder_length('pick', 'tank',
-                                          ['tock', 'tick', 'sank', 'sink', 'sick']))
-
-        # live -> life == 1, no matter what is the word_list.
-        self.assertEqual(1, ladder_length('live', 'life', ['hoho', 'luck']))
-
-        # 0 length from ate -> ate
-        self.assertEqual(0, ladder_length('ate', 'ate', []))
-
-        # not possible to reach !
-        self.assertEqual(-1, ladder_length('rahul', 'coder', ['blahh', 'blhah']))
-
-
-if __name__ == '__main__':
-
-    unittest.main()
diff --git a/algorithms/bit/__init__.py b/algorithms/bit/__init__.py
index 617f500..fa31d7e 100644
--- a/algorithms/bit/__init__.py
+++ b/algorithms/bit/__init__.py
@@ -16,3 +16,4 @@ from .remove_bit import *
 from .count_flips_to_convert import *
 from .flip_bit_longest_sequence import *
 from .binary_gap import *
+from .bytes_int_conversion import *
\ No newline at end of file
diff --git a/algorithms/bit/count_flips_to_convert.py b/algorithms/bit/count_flips_to_convert.py
index 6ba1d11..375e68f 100644
--- a/algorithms/bit/count_flips_to_convert.py
+++ b/algorithms/bit/count_flips_to_convert.py
@@ -1,5 +1,5 @@
 """
-Write a function to determine the number of bits you would need to
+Write a function to determine the minimal number of bits you would need to
 flip to convert integer A to integer B.
 For example:
 Input: 29 (or: 11101), 15 (or: 01111)
diff --git a/algorithms/bit/count_ones.py b/algorithms/bit/count_ones.py
index 700efe9..e96161e 100644
--- a/algorithms/bit/count_ones.py
+++ b/algorithms/bit/count_ones.py
@@ -1,9 +1,9 @@
 """
 Write a function that takes an unsigned integer and
-returns the number of ’1' bits it has
+returns the number of '1' bits it has
 (also known as the Hamming weight).
 
-For example, the 32-bit integer ’11' has binary
+For example, the 32-bit integer '1' has binary
 representation 00000000000000000000000000001011,
 so the function should return 3.
 
@@ -15,7 +15,7 @@ only 2 iterations are required.
 Number of loops is
 equal to the number of 1s in the binary representation."""
 def count_ones_recur(n):
-    """Using Brian Kernighan’s Algorithm. (Recursive Approach)"""
+    """Using Brian Kernighan's Algorithm. (Recursive Approach)"""
 
     if not n:
         return 0
@@ -23,7 +23,7 @@ def count_ones_recur(n):
 
 
 def count_ones_iter(n):
-    """Using Brian Kernighan’s Algorithm. (Iterative Approach)"""
+    """Using Brian Kernighan's Algorithm. (Iterative Approach)"""
 
     count = 0
     while n:
diff --git a/algorithms/bit/find_missing_number.py b/algorithms/bit/find_missing_number.py
index 9aedc50..2e47df4 100644
--- a/algorithms/bit/find_missing_number.py
+++ b/algorithms/bit/find_missing_number.py
@@ -3,6 +3,10 @@
     in range [0..n] in O(n) time and space. The difference between
     consecutive integers cannot be more than 1. If the sequence is
     already complete, the next integer in the sequence will be returned.
+
+    For example:
+    Input: nums = [4, 1, 3, 0, 6, 5, 2]
+    Output: 7
 """
 def find_missing_number(nums):
 
diff --git a/algorithms/bit/insert_bit.py b/algorithms/bit/insert_bit.py
index f799489..a86e042 100644
--- a/algorithms/bit/insert_bit.py
+++ b/algorithms/bit/insert_bit.py
@@ -6,7 +6,7 @@ For example:
 
 Input: num = 10101 (21)
 insert_one_bit(num, 1, 2): 101101 (45)
-insert_one_bit(num, 0 ,2): 101001 (41)
+insert_one_bit(num, 0, 2): 101001 (41)
 insert_one_bit(num, 1, 5): 110101 (53)
 insert_one_bit(num, 1, 0): 101011 (43)
 
@@ -25,7 +25,7 @@ Insert exact one bit at specific position
 Algorithm:
 1. Create a mask having bit from i to the most significant bit, and append the new bit at 0 position
 2. Keep the bit from 0 position to i position ( like 000...001111)
-3) Merge mask and num
+3. Merge mask and num
 """
 def insert_one_bit(num, bit, i):
     # Create mask
diff --git a/algorithms/dfs/__init__.py b/algorithms/dfs/__init__.py
new file mode 100644
index 0000000..03af129
--- /dev/null
+++ b/algorithms/dfs/__init__.py
@@ -0,0 +1,5 @@
+from .all_factors import *
+from .count_islands import *
+from .pacific_atlantic import *
+from .sudoku_solver import *
+from .walls_and_gates import *
diff --git a/algorithms/dfs/all_factors.py b/algorithms/dfs/all_factors.py
index 6c692f3..13438d4 100644
--- a/algorithms/dfs/all_factors.py
+++ b/algorithms/dfs/all_factors.py
@@ -27,8 +27,6 @@ output:
   [2, 2, 2, 4],
   [2, 2, 2, 2, 2],
 """
-import unittest
-
 def get_factors(n):
     """[summary]
     
@@ -111,17 +109,3 @@ def get_factors_iterative2(n):
             n //= x
         else:
             x += 1
-
-class TestAllFactors(unittest.TestCase):
-    def test_get_factors(self):
-        self.assertEqual([[2, 16], [2, 2, 8], [2, 2, 2, 4], [2, 2, 2, 2, 2], [2, 4, 4], [4, 8]],
-        get_factors(32))
-    def test_get_factors_iterative1(self):
-        self.assertEqual([[2, 16], [4, 8], [2, 2, 8], [2, 4, 4], [2, 2, 2, 4], [2, 2, 2, 2, 2]],
-        get_factors_iterative1(32))
-    def test_get_factors_iterative2(self):
-        self.assertEqual([[2, 2, 2, 2, 2], [2, 2, 2, 4], [2, 2, 8], [2, 4, 4], [2, 16], [4, 8]],
-        get_factors_iterative2(32))
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/algorithms/dfs/count_islands.py b/algorithms/dfs/count_islands.py
index 60f6802..21bcee8 100644
--- a/algorithms/dfs/count_islands.py
+++ b/algorithms/dfs/count_islands.py
@@ -24,20 +24,20 @@ Answer: 3
 
 def num_islands(grid):
     count = 0
-    for i, row in enumerate(grid):
+    for i in range(len(grid)):
         for j, col in enumerate(grid[i]):
-            if col == '1':
+            if col == 1:
                 dfs(grid, i, j)
                 count += 1
     return count
 
 
 def dfs(grid, i, j):
-    if (i < 0 or i >= len(grid)) or (j < 0 or len(grid[0])):
+    if (i < 0 or i >= len(grid)) or (j < 0 or j >= len(grid[0])):
         return
-    if grid[i][j] != '1':
+    if grid[i][j] != 1:
         return
-    grid[i][j] = '0'
+    grid[i][j] = 0
     dfs(grid, i+1, j)
     dfs(grid, i-1, j)
     dfs(grid, i, j+1)
diff --git a/algorithms/dfs/sudoku_solver.py b/algorithms/dfs/sudoku_solver.py
index 387cdca..e7ba9aa 100644
--- a/algorithms/dfs/sudoku_solver.py
+++ b/algorithms/dfs/sudoku_solver.py
@@ -20,9 +20,6 @@ The PossibleVals function may be further simplified/optimized, but it works just
 fine for now. (it would look less lengthy if we are allowed to use numpy array
 for the board lol).
 """
-
-import unittest
-
 class Sudoku: 
     def __init__ (self, board, row, col):
         self.board = board
@@ -96,15 +93,3 @@ class Sudoku:
                 resp += " {0} ".format(self.board[i][j])
             resp += "\n"
         return resp
-
-
-class TestSudoku(unittest.TestCase):
-    def test_sudoku_solver(self):
-        board = [["5","3","."], ["6",".", "."],[".","9","8"]]
-        test_obj = Sudoku(board, 3, 3)
-        test_obj.solve()
-        self.assertEqual([['5', '3', '1'], ['6', '1', '2'], ['1', '9', '8']],test_obj.board)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/algorithms/dfs/walls_and_gates.py b/algorithms/dfs/walls_and_gates.py
index 7d10b4d..0369203 100644
--- a/algorithms/dfs/walls_and_gates.py
+++ b/algorithms/dfs/walls_and_gates.py
@@ -1,7 +1,23 @@
+"""
+You are given a m x n 2D grid initialized with these three possible values:
+    -1: A wall or an obstacle.
+    0: A gate.
+    INF: Infinity means an empty room. We use the value 2^31 - 1 = 2147483647 to represent INF 
+         as you may assume that the distance to a gate is less than 2147483647.
+Fill the empty room with distance to its nearest gate.
+If it is impossible to reach a gate, it should be filled with INF.
 
-
-# fill the empty room with distance to its nearest gate
-
+For example, given the 2D grid:
+    INF -1  0   INF
+    INF INF INF -1
+    INF -1  INF -1
+    0   -1  INF INF
+After running your function, the 2D grid should be:
+    3   -1  0   1
+    2   2   1   -1
+    1   -1  2   -1
+    0   -1  3   4
+"""
 
 def walls_and_gates(rooms):
     for i in range(len(rooms)):


Test patch:
diff --git a/tests/test_bfs.py b/tests/test_bfs.py
index 129d6d0..62c179b 100644
--- a/tests/test_bfs.py
+++ b/tests/test_bfs.py
@@ -1,11 +1,12 @@
 from algorithms.bfs import (
     maze_search,
     shortest_distance_from_all_buildings,
-    word_ladder
+    ladder_length
 )
 
 import unittest
 
+
 class TestMazeSearch(unittest.TestCase):
     
     def test_maze_search(self):
@@ -13,6 +14,28 @@ class TestMazeSearch(unittest.TestCase):
         self.assertEqual(14, maze_search(grid_1))
         grid_2 = [[1,0,0],[0,1,1],[0,1,1]]
         self.assertEqual(-1, maze_search(grid_2))
-        
+
+
+class TestWordLadder(unittest.TestCase):
+
+    def test_ladder_length(self):
+
+        # hit -> hot -> dot -> dog -> cog
+        self.assertEqual(5, ladder_length('hit', 'cog', ["hot", "dot", "dog", "lot", "log"]))
+
+        # pick -> sick -> sink -> sank -> tank == 5
+        self.assertEqual(5, ladder_length('pick', 'tank',
+                                          ['tock', 'tick', 'sank', 'sink', 'sick']))
+
+        # live -> life == 1, no matter what is the word_list.
+        self.assertEqual(1, ladder_length('live', 'life', ['hoho', 'luck']))
+
+        # 0 length from ate -> ate
+        self.assertEqual(0, ladder_length('ate', 'ate', []))
+
+        # not possible to reach !
+        self.assertEqual(-1, ladder_length('rahul', 'coder', ['blahh', 'blhah']))
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_bit.py b/tests/test_bit.py
index acfb4b0..23e9d94 100644
--- a/tests/test_bit.py
+++ b/tests/test_bit.py
@@ -11,6 +11,8 @@ from algorithms.bit import (
     single_number3,
     subsets,
     get_bit, set_bit, clear_bit, update_bit,
+    int_to_bytes_big_endian, int_to_bytes_little_endian, 
+    bytes_big_endian_to_int, bytes_little_endian_to_int,
     swap_pair,
     find_difference,
     has_alternative_bit, has_alternative_bit_fast,
@@ -183,6 +185,18 @@ class TestSuite(unittest.TestCase):
         # 22 = 10110 --> after update bit at 2nd position with value 0: 20 = 10010
         self.assertEqual(18, update_bit(22, 2, 0))
 
+    def test_int_to_bytes_big_endian(self):
+        self.assertEqual(b'\x11', int_to_bytes_big_endian(17))
+    
+    def test_int_to_bytes_little_endian(self):
+        self.assertEqual(b'\x11', int_to_bytes_little_endian(17))
+
+    def test_bytes_big_endian_to_int(self):
+        self.assertEqual(17, bytes_big_endian_to_int(b'\x11'))
+    
+    def test_bytes_little_endian_to_int(self):
+        self.assertEqual(17, bytes_little_endian_to_int(b'\x11'))
+
     def test_swap_pair(self):
         # 22: 10110  --> 41: 101001
         self.assertEqual(41, swap_pair(22))
diff --git a/tests/test_dfs.py b/tests/test_dfs.py
new file mode 100644
index 0000000..ed1c3f2
--- /dev/null
+++ b/tests/test_dfs.py
@@ -0,0 +1,55 @@
+from algorithms.dfs import (
+    get_factors, get_factors_iterative1, get_factors_iterative2,
+    num_islands,
+    pacific_atlantic,
+    Sudoku,
+    walls_and_gates
+)
+
+import unittest
+
+
+class TestAllFactors(unittest.TestCase):
+    def test_get_factors(self):
+        self.assertEqual([[2, 16], [2, 2, 8], [2, 2, 2, 4], [2, 2, 2, 2, 2], [2, 4, 4], [4, 8]],
+        get_factors(32))
+    def test_get_factors_iterative1(self):
+        self.assertEqual([[2, 16], [4, 8], [2, 2, 8], [2, 4, 4], [2, 2, 2, 4], [2, 2, 2, 2, 2]],
+        get_factors_iterative1(32))
+    def test_get_factors_iterative2(self):
+        self.assertEqual([[2, 2, 2, 2, 2], [2, 2, 2, 4], [2, 2, 8], [2, 4, 4], [2, 16], [4, 8]],
+        get_factors_iterative2(32))
+
+
+class TestCountIslands(unittest.TestCase):
+    def test_num_islands(self):
+        self.assertEqual(1, num_islands([[1, 1, 1, 1, 0], [1, 1, 0, 1, 0], [1, 1, 0, 0, 0], [0, 0, 0, 0, 0]]))
+        self.assertEqual(3, num_islands([[1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1]]))
+
+
+class TestPacificAtlantic(unittest.TestCase):
+    def test_pacific_atlantic(self):
+        self.assertEqual([[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]], 
+            pacific_atlantic([[1, 2, 2, 3, 5], [3, 2, 3, 4, 4], [2, 4, 5, 3, 1], [6, 7, 1, 4, 5], [5, 1, 1, 2, 4]]))
+
+
+class TestSudoku(unittest.TestCase):
+    def test_sudoku_solver(self):
+        board = [["5","3","."], ["6",".", "."],[".","9","8"]]
+        test_obj = Sudoku(board, 3, 3)
+        test_obj.solve()
+        self.assertEqual([['5', '3', '1'], ['6', '1', '2'], ['1', '9', '8']],test_obj.board)
+
+
+class TestWallsAndGates(unittest.TestCase):
+    def test_walls_and_gates(self):
+        rooms = [[float("inf"), -1, 0, float("inf")], 
+                [float("inf"), float("inf"), float("inf"), -1], 
+                [float("inf"), -1, float("inf"), -1], 
+                [0, -1, float("inf"), float("inf")]]
+        walls_and_gates(rooms)
+        self.assertEqual([[3, -1, 0, 1], [2, 2, 1, -1], [1, -1, 2, -1], [0, -1, 3, 4]], rooms)
+
+
+if __name__ == "__main__":
+    unittest.main()
\ No newline at end of file