jugaadcode 0.1.10__tar.gz → 0.1.11__tar.gz

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 (80) hide show
  1. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/PKG-INFO +1 -1
  2. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/pyproject.toml +1 -1
  3. jugaadcode-0.1.11/src/jugaadcode/anagram.py +115 -0
  4. jugaadcode-0.1.11/src/jugaadcode/armstrong.py +93 -0
  5. jugaadcode-0.1.11/src/jugaadcode/binary_search.py +119 -0
  6. jugaadcode-0.1.11/src/jugaadcode/bubble_sort.py +94 -0
  7. jugaadcode-0.1.11/src/jugaadcode/capitalize_words.py +77 -0
  8. jugaadcode-0.1.11/src/jugaadcode/character_frequency.py +85 -0
  9. jugaadcode-0.1.11/src/jugaadcode/climbing_stairs.py +104 -0
  10. jugaadcode-0.1.11/src/jugaadcode/coin_change.py +122 -0
  11. jugaadcode-0.1.11/src/jugaadcode/count_vowels.py +70 -0
  12. jugaadcode-0.1.11/src/jugaadcode/counting_sort.py +100 -0
  13. jugaadcode-0.1.11/src/jugaadcode/duplicate_number.py +71 -0
  14. jugaadcode-0.1.11/src/jugaadcode/even_odd.py +74 -0
  15. jugaadcode-0.1.11/src/jugaadcode/factorial.py +78 -0
  16. jugaadcode-0.1.11/src/jugaadcode/factorial_trailing_zeros_direct.py +81 -0
  17. jugaadcode-0.1.11/src/jugaadcode/factorial_with_trailing_zeros.py +71 -0
  18. jugaadcode-0.1.11/src/jugaadcode/fibonacci.py +82 -0
  19. jugaadcode-0.1.11/src/jugaadcode/find_max.py +75 -0
  20. jugaadcode-0.1.11/src/jugaadcode/find_min.py +82 -0
  21. jugaadcode-0.1.11/src/jugaadcode/first_non_repeating.py +91 -0
  22. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode.egg-info/PKG-INFO +1 -1
  23. jugaadcode-0.1.10/src/jugaadcode/anagram.py +0 -12
  24. jugaadcode-0.1.10/src/jugaadcode/armstrong.py +0 -11
  25. jugaadcode-0.1.10/src/jugaadcode/binary_search.py +0 -27
  26. jugaadcode-0.1.10/src/jugaadcode/bubble_sort.py +0 -21
  27. jugaadcode-0.1.10/src/jugaadcode/capitalize_words.py +0 -6
  28. jugaadcode-0.1.10/src/jugaadcode/character_frequency.py +0 -13
  29. jugaadcode-0.1.10/src/jugaadcode/climbing_stairs.py +0 -16
  30. jugaadcode-0.1.10/src/jugaadcode/coin_change.py +0 -29
  31. jugaadcode-0.1.10/src/jugaadcode/count_vowels.py +0 -15
  32. jugaadcode-0.1.10/src/jugaadcode/counting_sort.py +0 -26
  33. jugaadcode-0.1.10/src/jugaadcode/duplicate_number.py +0 -17
  34. jugaadcode-0.1.10/src/jugaadcode/even_odd.py +0 -6
  35. jugaadcode-0.1.10/src/jugaadcode/factorial.py +0 -14
  36. jugaadcode-0.1.10/src/jugaadcode/factorial_trailing_zeros_direct.py +0 -20
  37. jugaadcode-0.1.10/src/jugaadcode/factorial_with_trailing_zeros.py +0 -29
  38. jugaadcode-0.1.10/src/jugaadcode/fibonacci.py +0 -20
  39. jugaadcode-0.1.10/src/jugaadcode/find_max.py +0 -15
  40. jugaadcode-0.1.10/src/jugaadcode/find_min.py +0 -15
  41. jugaadcode-0.1.10/src/jugaadcode/first_non_repeating.py +0 -18
  42. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/README.md +0 -0
  43. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/setup.cfg +0 -0
  44. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/__init__.py +0 -0
  45. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/gcd.py +0 -0
  46. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/harshad_number.py +0 -0
  47. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/input_utils.py +0 -0
  48. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/insertion_sort.py +0 -0
  49. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/kth_largest.py +0 -0
  50. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/lcm.py +0 -0
  51. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/linear_search.py +0 -0
  52. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/longest_consecutive.py +0 -0
  53. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/longest_word.py +0 -0
  54. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/matrix_transpose.py +0 -0
  55. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/maximum_subarray.py +0 -0
  56. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/merge_sort.py +0 -0
  57. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/move_zeros.py +0 -0
  58. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/pair_with_difference.py +0 -0
  59. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/palindrome.py +0 -0
  60. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/perfect_number.py +0 -0
  61. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/power.py +0 -0
  62. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/prime.py +0 -0
  63. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/quick_sort.py +0 -0
  64. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/remove_duplicates_array.py +0 -0
  65. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/remove_duplicates_string.py +0 -0
  66. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/reverse_linked_list.py +0 -0
  67. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/reverse_number.py +0 -0
  68. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/reverse_string.py +0 -0
  69. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/second_largest.py +0 -0
  70. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/selection_sort.py +0 -0
  71. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/strong_number.py +0 -0
  72. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/subsequence.py +0 -0
  73. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/sum_of_digits.py +0 -0
  74. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/trailing_zeros.py +0 -0
  75. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/two_sum.py +0 -0
  76. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/valid_parentheses.py +0 -0
  77. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode/word_count.py +0 -0
  78. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode.egg-info/SOURCES.txt +0 -0
  79. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode.egg-info/dependency_links.txt +0 -0
  80. {jugaadcode-0.1.10 → jugaadcode-0.1.11}/src/jugaadcode.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jugaadcode
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: A Python library for common programming logic and algorithms
5
5
  Author: Mohamed Faraazman Bin Farooq S, Surya Deepta
6
6
  Requires-Python: >=3.9
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jugaadcode"
3
- version = "0.1.10"
3
+ version = "0.1.11"
4
4
  description = "A Python library for common programming logic and algorithms"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -0,0 +1,115 @@
1
+ from collections import Counter
2
+
3
+
4
+ def is_anagram(*args):
5
+ """
6
+ Check whether two or more strings form anagrams.
7
+
8
+ Supports:
9
+ is_anagram("listen", "silent")
10
+ is_anagram(["listen", "silent"])
11
+ is_anagram(("listen", "silent"))
12
+ is_anagram(("listen", "silent"), ("hello", "world"))
13
+ is_anagram("listen", "silent", "hello", "world")
14
+
15
+ Spaces and letter case are ignored.
16
+
17
+ Parameters
18
+ ----------
19
+ *args : str or collections of strings
20
+ Two strings for a single comparison, or multiple pairs
21
+ supplied as separate arguments or collections.
22
+
23
+ Returns
24
+ -------
25
+ bool or list[bool]
26
+ A boolean for one comparison, or a list of booleans
27
+ for multiple comparisons.
28
+
29
+ Raises
30
+ ------
31
+ TypeError
32
+ If an input is not a string or a supported collection.
33
+ ValueError
34
+ If a comparison does not contain exactly two strings,
35
+ or either string is empty after removing whitespace.
36
+ """
37
+
38
+ if not args:
39
+ first = input("Enter first string: ")
40
+ second = input("Enter second string: ")
41
+ args = (first, second)
42
+
43
+ def normalize(text):
44
+ if not isinstance(text, str):
45
+ raise TypeError("Every string must be of type str.")
46
+
47
+ normalized = "".join(text.split()).casefold()
48
+
49
+ if not normalized:
50
+ raise ValueError(
51
+ "Strings must contain at least one non-whitespace character."
52
+ )
53
+
54
+ return Counter(normalized)
55
+
56
+ def compare_pair(pair):
57
+ if len(pair) != 2:
58
+ raise ValueError(
59
+ "Each anagram comparison must contain exactly two strings."
60
+ )
61
+
62
+ first, second = pair
63
+ return normalize(first) == normalize(second)
64
+
65
+ # Two strings represent one comparison.
66
+ if len(args) == 2 and all(isinstance(arg, str) for arg in args):
67
+ return compare_pair(args)
68
+
69
+ # Multiple strings represent consecutive pairs.
70
+ if all(isinstance(arg, str) for arg in args):
71
+ if len(args) % 2 != 0:
72
+ raise ValueError(
73
+ "Multiple strings must be supplied in pairs."
74
+ )
75
+
76
+ return [
77
+ compare_pair(args[index:index + 2])
78
+ for index in range(0, len(args), 2)
79
+ ]
80
+
81
+ # A single collection may contain a pair of strings
82
+ # or multiple pairs of strings.
83
+ if len(args) == 1:
84
+ collection = args[0]
85
+
86
+ if isinstance(collection, (list, tuple)):
87
+ if len(collection) == 2 and all(
88
+ isinstance(item, str) for item in collection
89
+ ):
90
+ return compare_pair(collection)
91
+
92
+ results = []
93
+
94
+ for pair in collection:
95
+ if not isinstance(pair, (list, tuple)):
96
+ raise TypeError(
97
+ "Multiple comparisons must be supplied as pairs."
98
+ )
99
+
100
+ results.append(compare_pair(pair))
101
+
102
+ return results
103
+
104
+ # Multiple pairs passed as separate arguments.
105
+ results = []
106
+
107
+ for pair in args:
108
+ if not isinstance(pair, (list, tuple)):
109
+ raise TypeError(
110
+ "Supply strings in pairs or provide collections of string pairs."
111
+ )
112
+
113
+ results.append(compare_pair(pair))
114
+
115
+ return results
@@ -0,0 +1,93 @@
1
+ def is_armstrong(*args):
2
+ """
3
+ Check whether one or more integers are Armstrong numbers.
4
+
5
+ Supports:
6
+ is_armstrong(153)
7
+ is_armstrong(153, 370, 123)
8
+ is_armstrong([153, 370, 123])
9
+ is_armstrong((153, 370, 123))
10
+ is_armstrong({153, 370, 123})
11
+ is_armstrong(range(100, 200))
12
+ is_armstrong()
13
+
14
+ Returns
15
+ -------
16
+ bool
17
+ For a single integer.
18
+ list[bool] or tuple[bool, ...] or set[bool]
19
+ For multiple integers, according to the input format.
20
+
21
+ Raises
22
+ ------
23
+ TypeError
24
+ If an input is not an integer or supported collection.
25
+ ValueError
26
+ If an input contains a negative integer or no values.
27
+ """
28
+
29
+ if not args:
30
+ value = input(
31
+ "Enter number(s) to check, separated by commas: "
32
+ ).strip()
33
+
34
+ if not value:
35
+ raise ValueError("Input cannot be empty.")
36
+
37
+ try:
38
+ args = tuple(int(item.strip()) for item in value.split(","))
39
+ except ValueError as exc:
40
+ raise ValueError(
41
+ "Enter valid integers separated by commas."
42
+ ) from exc
43
+
44
+ # Determine whether the caller supplied a collection or
45
+ # multiple individual arguments.
46
+ if len(args) == 1:
47
+ value = args[0]
48
+
49
+ if isinstance(value, (list, tuple, set, range)):
50
+ numbers = list(value)
51
+ input_type = type(value)
52
+ else:
53
+ numbers = [value]
54
+ input_type = None
55
+ else:
56
+ numbers = list(args)
57
+ input_type = tuple
58
+
59
+ if not numbers:
60
+ raise ValueError("At least one number must be provided.")
61
+
62
+ def check_number(n):
63
+ if isinstance(n, bool) or not isinstance(n, int):
64
+ raise TypeError("Every value must be an integer.")
65
+
66
+ if n < 0:
67
+ raise ValueError(
68
+ "Armstrong number checking requires non-negative integers."
69
+ )
70
+
71
+ digits = str(n)
72
+ power = len(digits)
73
+
74
+ total = sum(int(digit) ** power for digit in digits)
75
+
76
+ return total == n
77
+
78
+ results = [check_number(n) for n in numbers]
79
+
80
+ # Preserve a single result for a single integer.
81
+ if input_type is None:
82
+ return results[0]
83
+
84
+ if input_type is list or input_type is range:
85
+ return results
86
+
87
+ if input_type is tuple:
88
+ return tuple(results)
89
+
90
+ if input_type is set:
91
+ return set(results)
92
+
93
+ return results
@@ -0,0 +1,119 @@
1
+ from collections.abc import Iterable
2
+
3
+
4
+ def binary_search(numbers=None, target=None, *additional_targets):
5
+ """
6
+ Search for one or more targets in a sorted sequence using binary search.
7
+
8
+ Supports:
9
+ binary_search([1, 3, 5, 7], 5)
10
+ binary_search((1, 3, 5, 7), 5)
11
+ binary_search([1, 3, 5, 7], [3, 7, 9])
12
+ binary_search([1, 3, 5, 7], 3, 7, 9)
13
+
14
+ If the sequence or target is omitted, prompts the user.
15
+
16
+ Parameters
17
+ ----------
18
+ numbers : iterable of int, optional
19
+ Sorted sequence to search.
20
+ target : int or iterable of int, optional
21
+ Target value or values to find.
22
+ *additional_targets : int
23
+ Additional targets to search for.
24
+
25
+ Returns
26
+ -------
27
+ int
28
+ Index of the target when a single target is provided.
29
+ Returns -1 if the target is not found.
30
+ list[int]
31
+ Indices for multiple targets, using -1 for missing values.
32
+
33
+ Raises
34
+ ------
35
+ TypeError
36
+ If the sequence or target contains unsupported values.
37
+ ValueError
38
+ If the sequence is not sorted in ascending order.
39
+ """
40
+
41
+ if numbers is None:
42
+ raw = input(
43
+ "Enter sorted numbers separated by spaces: "
44
+ ).strip()
45
+
46
+ if not raw:
47
+ raise ValueError("The numbers sequence cannot be empty.")
48
+
49
+ try:
50
+ numbers = list(map(int, raw.split()))
51
+ except ValueError as exc:
52
+ raise ValueError("Enter valid integers.") from exc
53
+
54
+ if isinstance(numbers, (str, bytes)):
55
+ raise TypeError("numbers must be a sequence of numeric values.")
56
+
57
+ if not isinstance(numbers, Iterable):
58
+ raise TypeError("numbers must be an iterable of integers.")
59
+
60
+ numbers = list(numbers)
61
+
62
+ if any(
63
+ isinstance(number, bool) or not isinstance(number, int)
64
+ for number in numbers
65
+ ):
66
+ raise TypeError("Every element in numbers must be an integer.")
67
+
68
+ if any(numbers[i] > numbers[i + 1] for i in range(len(numbers) - 1)):
69
+ raise ValueError("numbers must be sorted in ascending order.")
70
+
71
+ if target is None:
72
+ raw = input("Enter target (or targets separated by spaces): ").strip()
73
+
74
+ if not raw:
75
+ raise ValueError("At least one target must be provided.")
76
+
77
+ try:
78
+ parsed = list(map(int, raw.split()))
79
+ except ValueError as exc:
80
+ raise ValueError("Targets must be valid integers.") from exc
81
+
82
+ if len(parsed) == 1:
83
+ target = parsed[0]
84
+ else:
85
+ target = parsed
86
+
87
+ def search_one(value):
88
+ if isinstance(value, bool) or not isinstance(value, int):
89
+ raise TypeError("Every target must be an integer.")
90
+
91
+ left, right = 0, len(numbers) - 1
92
+
93
+ while left <= right:
94
+ middle = (left + right) // 2
95
+
96
+ if numbers[middle] == value:
97
+ return middle
98
+
99
+ if numbers[middle] < value:
100
+ left = middle + 1
101
+ else:
102
+ right = middle - 1
103
+
104
+ return -1
105
+
106
+ # Collect targets while preserving the original single-target behaviour.
107
+ if additional_targets:
108
+ targets = [target, *additional_targets]
109
+ multiple = True
110
+ elif isinstance(target, (list, tuple, set, range)):
111
+ targets = list(target)
112
+ multiple = True
113
+ else:
114
+ targets = [target]
115
+ multiple = False
116
+
117
+ results = [search_one(value) for value in targets]
118
+
119
+ return results if multiple else results[0]
@@ -0,0 +1,94 @@
1
+ from collections.abc import Iterable
2
+
3
+
4
+ def bubble_sort(*args):
5
+ """
6
+ Sort numbers in ascending order using the Bubble Sort algorithm.
7
+
8
+ Supports:
9
+ bubble_sort([5, 2, 8, 1])
10
+ bubble_sort((5, 2, 8, 1))
11
+ bubble_sort(range(5, 0, -1))
12
+ bubble_sort(5, 2, 8, 1)
13
+ bubble_sort()
14
+
15
+ Parameters
16
+ ----------
17
+ *args
18
+ A collection of numbers, multiple numeric arguments,
19
+ or no arguments to enable interactive input.
20
+
21
+ Returns
22
+ -------
23
+ list
24
+ A new list containing the sorted numbers.
25
+
26
+ Raises
27
+ ------
28
+ TypeError
29
+ If the input is not a supported collection of comparable numbers.
30
+ ValueError
31
+ If interactive input is empty or contains invalid numbers.
32
+
33
+ Examples
34
+ --------
35
+ >>> bubble_sort([5, 2, 8, 1])
36
+ [1, 2, 5, 8]
37
+
38
+ >>> bubble_sort(5, 2, 8, 1)
39
+ [1, 2, 5, 8]
40
+ """
41
+
42
+ if not args:
43
+ raw = input("Enter numbers separated by spaces: ").strip()
44
+
45
+ if not raw:
46
+ raise ValueError("At least one number must be provided.")
47
+
48
+ try:
49
+ args = ([
50
+ float(value) if "." in value else int(value)
51
+ for value in raw.split()
52
+ ],)
53
+ except ValueError as exc:
54
+ raise ValueError("Enter valid numeric values.") from exc
55
+
56
+ # Accept a collection as the first argument or multiple arguments.
57
+ if len(args) == 1:
58
+ values = args[0]
59
+
60
+ if isinstance(values, (str, bytes)):
61
+ raise TypeError("Input must contain numbers, not a string.")
62
+
63
+ if isinstance(values, Iterable):
64
+ numbers = list(values)
65
+ else:
66
+ numbers = [values]
67
+ else:
68
+ numbers = list(args)
69
+
70
+ # Validate elements.
71
+ for number in numbers:
72
+ if isinstance(number, bool) or not isinstance(number, (int, float)):
73
+ raise TypeError("All elements must be integers or floats.")
74
+
75
+ if isinstance(number, float) and not __import__("math").isfinite(number):
76
+ raise ValueError("NaN and infinity are not supported.")
77
+
78
+ # Bubble Sort.
79
+ numbers = numbers.copy()
80
+ n = len(numbers)
81
+
82
+ for i in range(n):
83
+ swapped = False
84
+
85
+ for j in range(n - i - 1):
86
+ if numbers[j] > numbers[j + 1]:
87
+ numbers[j], numbers[j + 1] = numbers[j + 1], numbers[j]
88
+ swapped = True
89
+
90
+ # Stop early if the list is already sorted.
91
+ if not swapped:
92
+ break
93
+
94
+ return numbers
@@ -0,0 +1,77 @@
1
+ from collections.abc import Iterable
2
+
3
+
4
+ def capitalize_words(*args):
5
+ """
6
+ Capitalize the first character of each word in one or more strings.
7
+
8
+ Supports:
9
+ capitalize_words("hello world")
10
+ capitalize_words("hello world", "python programming")
11
+ capitalize_words(["hello world", "python programming"])
12
+ capitalize_words(("hello world", "python programming"))
13
+ capitalize_words()
14
+
15
+ Parameters
16
+ ----------
17
+ *args : str or iterable of str
18
+ One string, multiple strings, or a collection of strings.
19
+ If no arguments are supplied, prompts for interactive input.
20
+
21
+ Returns
22
+ -------
23
+ str or list[str] or tuple[str, ...]
24
+ A capitalized string for a single string.
25
+ A list or tuple for multiple strings, matching the input style.
26
+
27
+ Raises
28
+ ------
29
+ TypeError
30
+ If an input is not a string or supported collection of strings.
31
+
32
+ Examples
33
+ --------
34
+ >>> capitalize_words("hello world")
35
+ 'Hello World'
36
+
37
+ >>> capitalize_words(["hello world", "python programming"])
38
+ ['Hello World', 'Python Programming']
39
+ """
40
+
41
+ if not args:
42
+ text = input("Enter a sentence to capitalize words in: ")
43
+ return text.title()
44
+
45
+ # Handle multiple positional strings.
46
+ if all(isinstance(arg, str) for arg in args):
47
+ if len(args) == 1:
48
+ return args[0].title()
49
+
50
+ return tuple(text.title() for text in args)
51
+
52
+ # Handle a single collection of strings.
53
+ if len(args) == 1:
54
+ values = args[0]
55
+
56
+ if isinstance(values, str):
57
+ return values.title()
58
+
59
+ if isinstance(values, Iterable):
60
+ if isinstance(values, (bytes, bytearray)):
61
+ raise TypeError("Input must contain strings, not bytes.")
62
+
63
+ texts = list(values)
64
+
65
+ if not all(isinstance(text, str) for text in texts):
66
+ raise TypeError("Every element must be a string.")
67
+
68
+ results = [text.title() for text in texts]
69
+
70
+ if isinstance(values, tuple):
71
+ return tuple(results)
72
+
73
+ return results
74
+
75
+ raise TypeError(
76
+ "Provide a string, multiple strings, or a collection of strings."
77
+ )
@@ -0,0 +1,85 @@
1
+ from collections.abc import Iterable
2
+ from collections import Counter
3
+
4
+
5
+ def character_frequency(*args):
6
+ """
7
+ Count the frequency of each character in one or more strings.
8
+
9
+ Spaces are ignored. Other whitespace characters, such as tabs
10
+ and newlines, are counted.
11
+
12
+ Supports:
13
+ character_frequency("hello")
14
+ character_frequency("hello", "world")
15
+ character_frequency(["hello", "world"])
16
+ character_frequency(("hello", "world"))
17
+ character_frequency()
18
+
19
+ Parameters
20
+ ----------
21
+ *args : str or iterable of str
22
+ A string, multiple strings, or a collection of strings.
23
+ If no arguments are supplied, prompts for interactive input.
24
+
25
+ Returns
26
+ -------
27
+ dict[str, int] or list[dict[str, int]] or tuple[dict[str, int], ...]
28
+ A frequency dictionary for one string.
29
+ A collection of frequency dictionaries for multiple strings.
30
+
31
+ Raises
32
+ ------
33
+ TypeError
34
+ If an input is not a string or a supported collection of strings.
35
+
36
+ Examples
37
+ --------
38
+ >>> character_frequency("hello")
39
+ {'h': 1, 'e': 1, 'l': 2, 'o': 1}
40
+
41
+ >>> character_frequency(["hello", "world"])
42
+ [{'h': 1, 'e': 1, 'l': 2, 'o': 1},
43
+ {'w': 1, 'o': 1, 'r': 1, 'l': 1, 'd': 1}]
44
+ """
45
+
46
+ if not args:
47
+ text = input("Enter a string to analyze character frequency: ")
48
+ args = (text,)
49
+
50
+ def count_characters(text):
51
+ if not isinstance(text, str):
52
+ raise TypeError("Every input must be a string.")
53
+
54
+ return dict(Counter(character for character in text if character != " "))
55
+
56
+ # A single string produces one frequency dictionary.
57
+ if len(args) == 1 and isinstance(args[0], str):
58
+ return count_characters(args[0])
59
+
60
+ # Multiple positional strings produce multiple dictionaries.
61
+ if all(isinstance(arg, str) for arg in args):
62
+ return tuple(count_characters(text) for text in args)
63
+
64
+ # A collection of strings produces one dictionary per string.
65
+ if len(args) == 1 and isinstance(args[0], Iterable):
66
+ values = args[0]
67
+
68
+ if isinstance(values, (bytes, bytearray)):
69
+ raise TypeError("Input must contain strings, not bytes.")
70
+
71
+ texts = list(values)
72
+
73
+ if not all(isinstance(text, str) for text in texts):
74
+ raise TypeError("Every element in the collection must be a string.")
75
+
76
+ results = [count_characters(text) for text in texts]
77
+
78
+ if isinstance(values, tuple):
79
+ return tuple(results)
80
+
81
+ return results
82
+
83
+ raise TypeError(
84
+ "Provide a string, multiple strings, or a collection of strings."
85
+ )
@@ -0,0 +1,104 @@
1
+ from collections.abc import Iterable
2
+
3
+
4
+ def climbing_stairs(*args):
5
+ """
6
+ Calculate the number of distinct ways to climb stairs,
7
+ taking either 1 or 2 steps at a time.
8
+
9
+ Supports:
10
+ climbing_stairs(5)
11
+ climbing_stairs(3, 5, 7)
12
+ climbing_stairs([3, 5, 7])
13
+ climbing_stairs((3, 5, 7))
14
+ climbing_stairs(range(1, 6))
15
+ climbing_stairs()
16
+
17
+ Parameters
18
+ ----------
19
+ *args : int or iterable of int
20
+ One stair count, multiple stair counts, or a collection
21
+ of stair counts. If omitted, prompts for interactive input.
22
+
23
+ Returns
24
+ -------
25
+ int or list[int] or tuple[int, ...]
26
+ The number of ways for one input or multiple inputs.
27
+ For n = 0, returns 1 because there is one way to climb
28
+ zero stairs: take no steps.
29
+
30
+ Raises
31
+ ------
32
+ TypeError
33
+ If an input is not an integer or supported collection.
34
+ ValueError
35
+ If any stair count is negative.
36
+
37
+ Examples
38
+ --------
39
+ >>> climbing_stairs(2)
40
+ 2
41
+ >>> climbing_stairs(5)
42
+ 8
43
+ >>> climbing_stairs([2, 3, 5])
44
+ [2, 3, 8]
45
+ """
46
+
47
+ if not args:
48
+ raw = input("Enter number(s) of stairs, separated by spaces: ").strip()
49
+
50
+ if not raw:
51
+ raise ValueError("At least one stair count must be provided.")
52
+
53
+ try:
54
+ args = tuple(int(value) for value in raw.split())
55
+ except ValueError as exc:
56
+ raise ValueError("Stair counts must be integers.") from exc
57
+
58
+ # Determine whether input is a single value, collection,
59
+ # or multiple positional values.
60
+ if len(args) == 1:
61
+ value = args[0]
62
+
63
+ if isinstance(value, (list, tuple, range)):
64
+ numbers = list(value)
65
+ input_type = type(value)
66
+ else:
67
+ numbers = [value]
68
+ input_type = None
69
+ else:
70
+ numbers = list(args)
71
+ input_type = tuple
72
+
73
+ if not numbers:
74
+ raise ValueError("At least one stair count must be provided.")
75
+
76
+ def count_ways(n):
77
+ if isinstance(n, bool) or not isinstance(n, int):
78
+ raise TypeError("Every stair count must be an integer.")
79
+
80
+ if n < 0:
81
+ raise ValueError("The number of stairs cannot be negative.")
82
+
83
+ if n == 0:
84
+ return 1
85
+
86
+ if n <= 2:
87
+ return n
88
+
89
+ first, second = 1, 2
90
+
91
+ for _ in range(3, n + 1):
92
+ first, second = second, first + second
93
+
94
+ return second
95
+
96
+ results = [count_ways(n) for n in numbers]
97
+
98
+ if input_type is None:
99
+ return results[0]
100
+
101
+ if input_type is tuple:
102
+ return tuple(results)
103
+
104
+ return results