algonest 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. algonest/__init__.py +223 -0
  2. algonest/arrays/__init__.py +21 -0
  3. algonest/arrays/dutch_flag.py +92 -0
  4. algonest/arrays/kadane.py +42 -0
  5. algonest/arrays/prefix_sum.py +115 -0
  6. algonest/arrays/sliding_window.py +90 -0
  7. algonest/arrays/two_pointers.py +119 -0
  8. algonest/dynamic_programming/__init__.py +19 -0
  9. algonest/dynamic_programming/coin_change.py +26 -0
  10. algonest/dynamic_programming/edit_distance.py +26 -0
  11. algonest/dynamic_programming/knapsack.py +23 -0
  12. algonest/dynamic_programming/lcs.py +17 -0
  13. algonest/dynamic_programming/lis.py +26 -0
  14. algonest/dynamic_programming/matrix_chain.py +27 -0
  15. algonest/graphs/__init__.py +27 -0
  16. algonest/graphs/bellman_ford.py +30 -0
  17. algonest/graphs/bfs.py +53 -0
  18. algonest/graphs/dfs.py +65 -0
  19. algonest/graphs/dijkstra.py +29 -0
  20. algonest/graphs/dsu.py +32 -0
  21. algonest/graphs/floyd_warshall.py +17 -0
  22. algonest/graphs/kruskal.py +21 -0
  23. algonest/graphs/prim.py +28 -0
  24. algonest/graphs/topological_sort.py +52 -0
  25. algonest/heap/__init__.py +7 -0
  26. algonest/heap/max_heap.py +95 -0
  27. algonest/heap/min_heap.py +95 -0
  28. algonest/heap/priority_queue.py +128 -0
  29. algonest/linked_list/__init__.py +14 -0
  30. algonest/linked_list/circular.py +120 -0
  31. algonest/linked_list/doubly.py +98 -0
  32. algonest/linked_list/node.py +32 -0
  33. algonest/linked_list/singly.py +121 -0
  34. algonest/math/__init__.py +31 -0
  35. algonest/math/bit_manipulation.py +33 -0
  36. algonest/math/fast_power.py +16 -0
  37. algonest/math/gcd_lcm.py +32 -0
  38. algonest/math/modular_arithmetic.py +46 -0
  39. algonest/math/prime_sieve.py +47 -0
  40. algonest/search/__init__.py +19 -0
  41. algonest/search/binary_search.py +223 -0
  42. algonest/sort/__init__.py +23 -0
  43. algonest/sort/sorting.py +320 -0
  44. algonest/stack_queue/__init__.py +14 -0
  45. algonest/stack_queue/deque.py +83 -0
  46. algonest/stack_queue/monotonic_stack.py +84 -0
  47. algonest/stack_queue/queue.py +69 -0
  48. algonest/stack_queue/stack.py +68 -0
  49. algonest/strings/__init__.py +18 -0
  50. algonest/strings/anagram.py +54 -0
  51. algonest/strings/kmp.py +39 -0
  52. algonest/strings/rabin_karp.py +31 -0
  53. algonest/strings/trie.py +59 -0
  54. algonest/strings/z_algorithm.py +34 -0
  55. algonest/trees/__init__.py +22 -0
  56. algonest/trees/avl_tree.py +87 -0
  57. algonest/trees/binary_tree.py +109 -0
  58. algonest/trees/bst.py +150 -0
  59. algonest/trees/fenwick_tree.py +43 -0
  60. algonest/trees/node.py +19 -0
  61. algonest/trees/segment_tree.py +101 -0
  62. algonest/trees/traversals.py +104 -0
  63. algonest/utils/__init__.py +20 -0
  64. algonest/utils/debug.py +63 -0
  65. algonest/utils/fast_io.py +67 -0
  66. algonest/utils/test_runner.py +59 -0
  67. algonest/utils/type_helpers.py +6 -0
  68. algonest/utils/validators.py +36 -0
  69. algonest-0.1.0.dist-info/METADATA +77 -0
  70. algonest-0.1.0.dist-info/RECORD +73 -0
  71. algonest-0.1.0.dist-info/WHEEL +5 -0
  72. algonest-0.1.0.dist-info/licenses/LICENSE +21 -0
  73. algonest-0.1.0.dist-info/top_level.txt +1 -0
algonest/__init__.py ADDED
@@ -0,0 +1,223 @@
1
+ """Public API for algonest Phase 5."""
2
+
3
+ from algonest.arrays import (
4
+ container_with_water,
5
+ longest_unique_substring,
6
+ max_subarray_sum,
7
+ max_sum_subarray_k,
8
+ partition_by_pivot,
9
+ prefix_sum,
10
+ range_sum,
11
+ remove_duplicates,
12
+ sort_three_values,
13
+ subarray_sum_equals_k,
14
+ two_sum_sorted,
15
+ )
16
+ from algonest.search import (
17
+ binary_search,
18
+ jump_search,
19
+ linear_search,
20
+ lower_bound,
21
+ ternary_search,
22
+ upper_bound,
23
+ )
24
+ from algonest.heap import MaxHeap, MinHeap, PriorityQueue
25
+ from algonest.linked_list import (
26
+ CircularLinkedList,
27
+ DoublyLinkedList,
28
+ DoublyListNode,
29
+ ListNode,
30
+ SinglyLinkedList,
31
+ )
32
+ from algonest.sort import (
33
+ bubble_sort,
34
+ counting_sort,
35
+ heap_sort,
36
+ insertion_sort,
37
+ merge_sort,
38
+ quick_sort,
39
+ radix_sort,
40
+ selection_sort,
41
+ )
42
+ from algonest.stack_queue import (
43
+ DequeDS,
44
+ Queue,
45
+ Stack,
46
+ largest_rectangle_in_histogram,
47
+ next_greater_element,
48
+ )
49
+ from algonest.dynamic_programming import (
50
+ coin_change_ways,
51
+ edit_distance,
52
+ knapsack_01,
53
+ lcs_length,
54
+ lis_length,
55
+ matrix_chain_order,
56
+ min_coins,
57
+ unbounded_knapsack,
58
+ )
59
+ from algonest.graphs import (
60
+ DSU,
61
+ bellman_ford,
62
+ bfs_traversal,
63
+ connected_components,
64
+ dfs_topological_sort,
65
+ dfs_traversal,
66
+ dijkstra_shortest_paths,
67
+ floyd_warshall,
68
+ has_cycle_undirected,
69
+ kahn_topological_sort,
70
+ kruskal_mst,
71
+ prim_mst,
72
+ shortest_path_unweighted,
73
+ )
74
+ from algonest.trees import (
75
+ AVLTree,
76
+ BST,
77
+ BinaryTree,
78
+ FenwickTree,
79
+ SegmentTree,
80
+ TreeNode,
81
+ inorder,
82
+ level_order,
83
+ postorder,
84
+ preorder,
85
+ )
86
+ from algonest.strings import (
87
+ Trie,
88
+ find_all_anagrams,
89
+ group_anagrams,
90
+ is_anagram,
91
+ kmp_search,
92
+ rabin_karp_search,
93
+ z_array,
94
+ z_search,
95
+ )
96
+ from algonest.math import (
97
+ chinese_remainder_theorem,
98
+ clear_bit,
99
+ count_bits,
100
+ extended_gcd,
101
+ fast_power,
102
+ gcd,
103
+ is_power_of_two,
104
+ is_prime,
105
+ lcm,
106
+ mod_exp,
107
+ mod_inverse,
108
+ prime_factors,
109
+ set_bit,
110
+ sieve_of_eratosthenes,
111
+ xor_trick,
112
+ )
113
+ from algonest.utils import (
114
+ assert_sorted,
115
+ build_pytest_command,
116
+ read_ints_from_text,
117
+ run_tests,
118
+ time_function,
119
+ validate_iterable,
120
+ write_lines,
121
+ )
122
+
123
+ __all__ = [
124
+ "lower_bound",
125
+ "upper_bound",
126
+ "binary_search",
127
+ "linear_search",
128
+ "ternary_search",
129
+ "jump_search",
130
+ "max_sum_subarray_k",
131
+ "longest_unique_substring",
132
+ "two_sum_sorted",
133
+ "remove_duplicates",
134
+ "container_with_water",
135
+ "prefix_sum",
136
+ "range_sum",
137
+ "subarray_sum_equals_k",
138
+ "max_subarray_sum",
139
+ "sort_three_values",
140
+ "partition_by_pivot",
141
+ "bubble_sort",
142
+ "selection_sort",
143
+ "insertion_sort",
144
+ "merge_sort",
145
+ "quick_sort",
146
+ "heap_sort",
147
+ "counting_sort",
148
+ "radix_sort",
149
+ "ListNode",
150
+ "DoublyListNode",
151
+ "SinglyLinkedList",
152
+ "DoublyLinkedList",
153
+ "CircularLinkedList",
154
+ "Stack",
155
+ "Queue",
156
+ "DequeDS",
157
+ "next_greater_element",
158
+ "largest_rectangle_in_histogram",
159
+ "MinHeap",
160
+ "MaxHeap",
161
+ "PriorityQueue",
162
+ "TreeNode",
163
+ "BinaryTree",
164
+ "BST",
165
+ "inorder",
166
+ "preorder",
167
+ "postorder",
168
+ "level_order",
169
+ "AVLTree",
170
+ "SegmentTree",
171
+ "FenwickTree",
172
+ "bfs_traversal",
173
+ "shortest_path_unweighted",
174
+ "dfs_traversal",
175
+ "connected_components",
176
+ "has_cycle_undirected",
177
+ "dijkstra_shortest_paths",
178
+ "bellman_ford",
179
+ "floyd_warshall",
180
+ "kahn_topological_sort",
181
+ "dfs_topological_sort",
182
+ "kruskal_mst",
183
+ "prim_mst",
184
+ "DSU",
185
+ "knapsack_01",
186
+ "unbounded_knapsack",
187
+ "lcs_length",
188
+ "lis_length",
189
+ "min_coins",
190
+ "coin_change_ways",
191
+ "matrix_chain_order",
192
+ "edit_distance",
193
+ "kmp_search",
194
+ "rabin_karp_search",
195
+ "z_array",
196
+ "z_search",
197
+ "Trie",
198
+ "group_anagrams",
199
+ "is_anagram",
200
+ "find_all_anagrams",
201
+ "gcd",
202
+ "lcm",
203
+ "extended_gcd",
204
+ "sieve_of_eratosthenes",
205
+ "is_prime",
206
+ "prime_factors",
207
+ "mod_exp",
208
+ "mod_inverse",
209
+ "chinese_remainder_theorem",
210
+ "fast_power",
211
+ "count_bits",
212
+ "is_power_of_two",
213
+ "set_bit",
214
+ "clear_bit",
215
+ "xor_trick",
216
+ "validate_iterable",
217
+ "read_ints_from_text",
218
+ "write_lines",
219
+ "time_function",
220
+ "assert_sorted",
221
+ "build_pytest_command",
222
+ "run_tests",
223
+ ]
@@ -0,0 +1,21 @@
1
+ """Array algorithms package."""
2
+
3
+ from algonest.arrays.dutch_flag import partition_by_pivot, sort_three_values
4
+ from algonest.arrays.kadane import max_subarray_sum
5
+ from algonest.arrays.prefix_sum import prefix_sum, range_sum, subarray_sum_equals_k
6
+ from algonest.arrays.sliding_window import longest_unique_substring, max_sum_subarray_k
7
+ from algonest.arrays.two_pointers import container_with_water, remove_duplicates, two_sum_sorted
8
+
9
+ __all__ = [
10
+ "max_sum_subarray_k",
11
+ "longest_unique_substring",
12
+ "two_sum_sorted",
13
+ "remove_duplicates",
14
+ "container_with_water",
15
+ "prefix_sum",
16
+ "range_sum",
17
+ "subarray_sum_equals_k",
18
+ "max_subarray_sum",
19
+ "sort_three_values",
20
+ "partition_by_pivot",
21
+ ]
@@ -0,0 +1,92 @@
1
+ """Dutch flag partitioning algorithms."""
2
+
3
+ from typing import Any, Iterable, List
4
+
5
+ from algonest.utils import _validate_iterable
6
+
7
+
8
+ def sort_three_values(
9
+ arr: Iterable[Any],
10
+ low_value: Any,
11
+ mid_value: Any,
12
+ high_value: Any,
13
+ ) -> List[Any]:
14
+ """Return values sorted by low/mid/high categories in one pass.
15
+
16
+ Args:
17
+ arr (Iterable[Any]): Iterable containing only three category values.
18
+ low_value (Any): Category that should appear first.
19
+ mid_value (Any): Category that should appear second.
20
+ high_value (Any): Category that should appear third.
21
+
22
+ Returns:
23
+ List[Any]: Newly sorted list by category order.
24
+
25
+ Raises:
26
+ TypeError: If arr is not iterable.
27
+ ValueError: If arr contains values outside the three categories.
28
+
29
+ Time Complexity: O(n)
30
+ Space Complexity: O(n)
31
+
32
+ Example:
33
+ >>> sort_three_values([2, 0, 1, 2, 1, 0], 0, 1, 2)
34
+ [0, 0, 1, 1, 2, 2]
35
+ """
36
+ values = _validate_iterable(arr)
37
+ allowed = {low_value, mid_value, high_value}
38
+ for value in values:
39
+ if value not in allowed:
40
+ raise ValueError("arr must contain only the three provided values")
41
+
42
+ out = values[:]
43
+ low = 0
44
+ mid = 0
45
+ high = len(out) - 1
46
+ while mid <= high:
47
+ if out[mid] == low_value:
48
+ out[low], out[mid] = out[mid], out[low]
49
+ low += 1
50
+ mid += 1
51
+ elif out[mid] == mid_value:
52
+ mid += 1
53
+ else:
54
+ out[mid], out[high] = out[high], out[mid]
55
+ high -= 1
56
+ return out
57
+
58
+
59
+ def partition_by_pivot(arr: Iterable[Any], pivot: Any) -> List[Any]:
60
+ """Return values partitioned into < pivot, == pivot, > pivot.
61
+
62
+ Args:
63
+ arr (Iterable[Any]): Iterable input values.
64
+ pivot (Any): Pivot for partitioning.
65
+
66
+ Returns:
67
+ List[Any]: Partitioned list.
68
+
69
+ Raises:
70
+ TypeError: If arr is not iterable.
71
+
72
+ Time Complexity: O(n)
73
+ Space Complexity: O(n)
74
+
75
+ Example:
76
+ >>> partition_by_pivot([3, 5, 2, 5, 1], 3)
77
+ [2, 1, 3, 5, 5]
78
+ """
79
+ values = _validate_iterable(arr)
80
+ lower: List[Any] = []
81
+ equal: List[Any] = []
82
+ higher: List[Any] = []
83
+
84
+ for value in values:
85
+ if value < pivot:
86
+ lower.append(value)
87
+ elif value == pivot:
88
+ equal.append(value)
89
+ else:
90
+ higher.append(value)
91
+
92
+ return lower + equal + higher
@@ -0,0 +1,42 @@
1
+ """Kadane algorithm for maximum subarray sum."""
2
+
3
+ from numbers import Real
4
+ from typing import Iterable
5
+
6
+ from algonest.utils import _validate_iterable
7
+
8
+
9
+ def max_subarray_sum(arr: Iterable[Real]) -> Real:
10
+ """Return the maximum sum among all contiguous subarrays.
11
+
12
+ Args:
13
+ arr (Iterable[Real]): Iterable of numeric values.
14
+
15
+ Returns:
16
+ Real: Maximum contiguous subarray sum.
17
+
18
+ Raises:
19
+ TypeError: If arr is not iterable or contains non-numeric values.
20
+ ValueError: If input is empty.
21
+
22
+ Time Complexity: O(n)
23
+ Space Complexity: O(n)
24
+
25
+ Example:
26
+ >>> max_subarray_sum([-2, 1, -3, 4, -1, 2, 1, -5, 4])
27
+ 6
28
+ """
29
+ values = _validate_iterable(arr)
30
+ if not values:
31
+ raise ValueError("arr cannot be empty")
32
+
33
+ for value in values:
34
+ if not isinstance(value, Real) or isinstance(value, bool):
35
+ raise TypeError("All elements in arr must be numeric")
36
+
37
+ current = values[0]
38
+ best = values[0]
39
+ for value in values[1:]:
40
+ current = max(value, current + value)
41
+ best = max(best, current)
42
+ return best
@@ -0,0 +1,115 @@
1
+ """Prefix-sum based array algorithms."""
2
+
3
+ from numbers import Real
4
+ from typing import Any, Iterable, List
5
+
6
+ from algonest.utils import _validate_iterable
7
+
8
+
9
+ def prefix_sum(arr: Iterable[Real]) -> List[Real]:
10
+ """Return prefix sums for numeric iterable input.
11
+
12
+ Args:
13
+ arr (Iterable[Real]): Iterable of numeric values.
14
+
15
+ Returns:
16
+ List[Real]: Prefix sums where out[i] = sum(arr[0:i+1]).
17
+
18
+ Raises:
19
+ TypeError: If arr is not iterable or contains non-numeric values.
20
+
21
+ Time Complexity: O(n)
22
+ Space Complexity: O(n)
23
+
24
+ Example:
25
+ >>> prefix_sum([3, 5, 2, 7])
26
+ [3, 8, 10, 17]
27
+ """
28
+ values = _validate_iterable(arr)
29
+ out: List[Real] = []
30
+ running: Real = 0
31
+
32
+ for value in values:
33
+ if not isinstance(value, Real) or isinstance(value, bool):
34
+ raise TypeError("All elements in arr must be numeric")
35
+ running += value
36
+ out.append(running)
37
+ return out
38
+
39
+
40
+ def range_sum(prefix: Iterable[Real], left: int, right: int) -> Real:
41
+ """Return inclusive sum from left to right using prefix sums.
42
+
43
+ Args:
44
+ prefix (Iterable[Real]): Prefix-sum iterable.
45
+ left (int): Left index, inclusive.
46
+ right (int): Right index, inclusive.
47
+
48
+ Returns:
49
+ Real: Sum over [left, right].
50
+
51
+ Raises:
52
+ TypeError: If prefix is not iterable or bounds are not integers.
53
+ ValueError: If prefix is empty or left > right.
54
+ IndexError: If bounds are out of range.
55
+
56
+ Time Complexity: O(1)
57
+ Space Complexity: O(n)
58
+
59
+ Example:
60
+ >>> p = prefix_sum([3, 5, 2, 7])
61
+ >>> range_sum(p, 1, 3)
62
+ 14
63
+ """
64
+ values = _validate_iterable(prefix)
65
+ if not values:
66
+ raise ValueError("prefix cannot be empty")
67
+ if not isinstance(left, int) or isinstance(left, bool):
68
+ raise TypeError("left must be an integer")
69
+ if not isinstance(right, int) or isinstance(right, bool):
70
+ raise TypeError("right must be an integer")
71
+ if left > right:
72
+ raise ValueError("left cannot be greater than right")
73
+ if left < 0 or right < 0 or left >= len(values) or right >= len(values):
74
+ raise IndexError("left and right must be valid indices")
75
+
76
+ return values[right] if left == 0 else values[right] - values[left - 1]
77
+
78
+
79
+ def subarray_sum_equals_k(arr: Iterable[Real], k: Real) -> int:
80
+ """Return count of contiguous subarrays with sum exactly equal to k.
81
+
82
+ Args:
83
+ arr (Iterable[Real]): Iterable of numeric values.
84
+ k (Real): Target sum.
85
+
86
+ Returns:
87
+ int: Number of matching subarrays.
88
+
89
+ Raises:
90
+ TypeError: If arr is not iterable or contains non-numeric values.
91
+ TypeError: If k is not numeric.
92
+
93
+ Time Complexity: O(n)
94
+ Space Complexity: O(n)
95
+
96
+ Example:
97
+ >>> subarray_sum_equals_k([1, 1, 1], 2)
98
+ 2
99
+ """
100
+ values = _validate_iterable(arr)
101
+ if not isinstance(k, Real) or isinstance(k, bool):
102
+ raise TypeError("k must be numeric")
103
+
104
+ for value in values:
105
+ if not isinstance(value, Real) or isinstance(value, bool):
106
+ raise TypeError("All elements in arr must be numeric")
107
+
108
+ count = 0
109
+ running: Real = 0
110
+ seen = {0: 1}
111
+ for value in values:
112
+ running += value
113
+ count += seen.get(running - k, 0)
114
+ seen[running] = seen.get(running, 0) + 1
115
+ return count
@@ -0,0 +1,90 @@
1
+ """Sliding window algorithms."""
2
+
3
+ from numbers import Real
4
+ from typing import Iterable
5
+
6
+ from algonest.utils import _validate_iterable
7
+
8
+
9
+ def max_sum_subarray_k(arr: Iterable[Real], k: int) -> Real:
10
+ """Return maximum sum over all contiguous subarrays of size k.
11
+
12
+ Args:
13
+ arr (Iterable[Real]): Iterable numeric input.
14
+ k (int): Window size.
15
+
16
+ Returns:
17
+ Real: Maximum subarray sum of length k.
18
+
19
+ Raises:
20
+ TypeError: If arr is not an iterable.
21
+ TypeError: If array values are not numeric.
22
+ ValueError: If k is not a valid positive integer in bounds.
23
+
24
+ Time Complexity: O(n)
25
+ Space Complexity: O(n) due to list conversion
26
+
27
+ Example:
28
+ >>> max_sum_subarray_k([2, 1, 5, 1, 3, 2], 3)
29
+ 9
30
+ """
31
+ values = _validate_iterable(arr)
32
+
33
+ if not isinstance(k, int) or isinstance(k, bool):
34
+ raise ValueError("k must be an integer")
35
+ if k <= 0:
36
+ raise ValueError("k must be a positive integer")
37
+ if k > len(values):
38
+ raise ValueError("k cannot be greater than the length of input")
39
+
40
+ for element in values:
41
+ if not isinstance(element, Real) or isinstance(element, bool):
42
+ raise TypeError("All elements in arr must be numeric")
43
+
44
+ window_sum = sum(values[:k])
45
+ best_sum = window_sum
46
+
47
+ for right in range(k, len(values)):
48
+ window_sum += values[right]
49
+ window_sum -= values[right - k]
50
+ if window_sum > best_sum:
51
+ best_sum = window_sum
52
+
53
+ return best_sum
54
+
55
+
56
+ def longest_unique_substring(s: str) -> int:
57
+ """Return length of the longest substring without repeated characters.
58
+
59
+ Args:
60
+ s (str): Input string.
61
+
62
+ Returns:
63
+ int: Longest unique substring length.
64
+
65
+ Raises:
66
+ TypeError: If s is not a string.
67
+
68
+ Time Complexity: O(n)
69
+ Space Complexity: O(min(n, alphabet_size))
70
+
71
+ Example:
72
+ >>> longest_unique_substring("abcabcbb")
73
+ 3
74
+ """
75
+ if not isinstance(s, str):
76
+ raise TypeError("longest_unique_substring expects 's' to be a string")
77
+
78
+ last_seen = {}
79
+ left = 0
80
+ max_length = 0
81
+
82
+ for right, char in enumerate(s):
83
+ if char in last_seen and last_seen[char] >= left:
84
+ left = last_seen[char] + 1
85
+ last_seen[char] = right
86
+ current_length = right - left + 1
87
+ if current_length > max_length:
88
+ max_length = current_length
89
+
90
+ return max_length
@@ -0,0 +1,119 @@
1
+ """Two-pointer algorithms for array problems."""
2
+
3
+ from numbers import Real
4
+ from typing import Any, Iterable, Tuple
5
+
6
+ from algonest.utils import _validate_iterable
7
+
8
+
9
+ def two_sum_sorted(arr: Iterable[Real], target: Real) -> Tuple[int, int]:
10
+ """Return indices of two values in sorted input whose sum equals target.
11
+
12
+ Args:
13
+ arr (Iterable[Real]): Sorted iterable of numeric values.
14
+ target (Real): Desired sum.
15
+
16
+ Returns:
17
+ Tuple[int, int]: Pair of indices or (-1, -1) if not found.
18
+
19
+ Raises:
20
+ TypeError: If arr is not iterable or contains non-numeric values.
21
+
22
+ Time Complexity: O(n)
23
+ Space Complexity: O(n)
24
+
25
+ Example:
26
+ >>> two_sum_sorted([1, 2, 4, 7, 11], 9)
27
+ (1, 3)
28
+ """
29
+ values = _validate_iterable(arr)
30
+ if not isinstance(target, Real) or isinstance(target, bool):
31
+ raise TypeError("target must be numeric")
32
+
33
+ for value in values:
34
+ if not isinstance(value, Real) or isinstance(value, bool):
35
+ raise TypeError("All elements in arr must be numeric")
36
+
37
+ left = 0
38
+ right = len(values) - 1
39
+ while left < right:
40
+ current = values[left] + values[right]
41
+ if current == target:
42
+ return left, right
43
+ if current < target:
44
+ left += 1
45
+ else:
46
+ right -= 1
47
+ return -1, -1
48
+
49
+
50
+ def remove_duplicates(arr: Iterable[Any]) -> int:
51
+ """Return number of unique values in sorted iterable input.
52
+
53
+ Args:
54
+ arr (Iterable[Any]): Sorted iterable input values.
55
+
56
+ Returns:
57
+ int: Count of unique values.
58
+
59
+ Raises:
60
+ TypeError: If arr is not iterable.
61
+
62
+ Time Complexity: O(n)
63
+ Space Complexity: O(n)
64
+
65
+ Example:
66
+ >>> remove_duplicates([1, 1, 2, 2, 3])
67
+ 3
68
+ """
69
+ values = _validate_iterable(arr)
70
+ if not values:
71
+ return 0
72
+
73
+ unique = 1
74
+ for i in range(1, len(values)):
75
+ if values[i] != values[i - 1]:
76
+ unique += 1
77
+ return unique
78
+
79
+
80
+ def container_with_water(arr: Iterable[Any]) -> int:
81
+ """Return the maximum area formed by two lines and the x-axis.
82
+
83
+ Args:
84
+ arr (Iterable[Any]): Iterable of non-negative integer heights.
85
+
86
+ Returns:
87
+ int: Maximum container area.
88
+
89
+ Raises:
90
+ TypeError: If arr is not iterable or contains non-integer heights.
91
+ ValueError: If any height is negative.
92
+
93
+ Time Complexity: O(n)
94
+ Space Complexity: O(n)
95
+
96
+ Example:
97
+ >>> container_with_water([1, 8, 6, 2, 5, 4, 8, 3, 7])
98
+ 49
99
+ """
100
+ values = _validate_iterable(arr)
101
+ for value in values:
102
+ if not isinstance(value, int) or isinstance(value, bool):
103
+ raise TypeError("All heights must be integers")
104
+ if value < 0:
105
+ raise ValueError("Heights must be non-negative")
106
+
107
+ left = 0
108
+ right = len(values) - 1
109
+ best = 0
110
+ while left < right:
111
+ width = right - left
112
+ area = width * min(values[left], values[right])
113
+ if area > best:
114
+ best = area
115
+ if values[left] < values[right]:
116
+ left += 1
117
+ else:
118
+ right -= 1
119
+ return best