Coverage for src / toolbox_python / generators.py: 100%
18 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-02 22:56 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-02 22:56 +0000
1# ============================================================================ #
2# #
3# Title : Generators #
4# Purpose : Generate information as needed. #
5# #
6# ============================================================================ #
9# ---------------------------------------------------------------------------- #
10# #
11# Overview ####
12# #
13# ---------------------------------------------------------------------------- #
16# ---------------------------------------------------------------------------- #
17# Description ####
18# ---------------------------------------------------------------------------- #
21"""
22!!! note "Summary"
23 This module provides functions to generate information as needed. Such functions are typically used to generate data that is not stored in a database or file, but rather computed on-the-fly based on input parameters.
24"""
27# ---------------------------------------------------------------------------- #
28# #
29# Setup ####
30# #
31# ---------------------------------------------------------------------------- #
34## --------------------------------------------------------------------------- #
35## Import ####
36## --------------------------------------------------------------------------- #
39# ## Python Third Party Imports ----
40from typeguard import typechecked
42# ## Local First Party Imports ----
43from toolbox_python.checkers import assert_is_valid
46## --------------------------------------------------------------------------- #
47## Exports ####
48## --------------------------------------------------------------------------- #
51__all__: list[str] = ["generate_group_cutoffs"]
54# ---------------------------------------------------------------------------- #
55# #
56# Groupings ####
57# #
58# ---------------------------------------------------------------------------- #
61@typechecked
62def generate_group_cutoffs(
63 total_number: int,
64 num_groups: int,
65) -> tuple[tuple[int, int], ...]:
66 """
67 !!! note "Summary"
68 Generate group cutoffs for a given total number and number of groups.
70 !!! abstract "Details"
71 This function divides a total number of items into a specified number of groups, returning a `#!py tuple` of `#!py tuple`'s where each inner `#!py tuple` contains the start and end indices for each group. The last group may contain fewer items if the total number is not evenly divisible by the number of groups.
73 Params:
74 total_number (int):
75 The total number of items to be divided into groups.
76 num_groups (int):
77 The number of groups to create.
79 Raises:
80 (TypeCheckError):
81 If any of the inputs parsed to the parameters of this function are not the correct type. Uses the [`@typeguard.typechecked`](https://typeguard.readthedocs.io/en/stable/api.html#typeguard.typechecked) decorator.
82 (ValueError):
83 If `total_number` is less than 1, or if `num_groups` is less than 1, or if `total_number` is less than `num_groups`. Uses the [`assert_is_valid`][toolbox_python.checkers.assert_is_valid] function to validate the inputs.
85 Returns:
86 (tuple[tuple[int, int], ...]):
87 A tuple of tuples, where each inner tuple contains the start and end indices for each group. The last group may have a different size if the total number is not evenly divisible by the number of groups.
89 ???+ example "Examples"
91 ```pycon {.py .python linenums="1" title="Prepare data"}
92 >>> from toolbox_python.generators import generate_group_cutoffs
93 ```
95 ```pycon {.py .python linenums="1" title="Example 1: Basic usage"}
96 >>> generate_group_cutoffs(10, 3)
97 ```
98 <div class="result" markdown>
99 ```{.sh .shell title="Output"}
100 ((0, 3), (3, 6), (6, 11))
101 ```
102 !!! success "Conclusion: Successfully split 10 items into 3 groups."
103 </div>
105 ```pycon {.py .python linenums="1" title="Example 2: Uneven groups"}
106 >>> generate_group_cutoffs(10, 4)
107 ```
108 <div class="result" markdown>
109 ```{.sh .shell title="Output"}
110 ((0, 3), (3, 6), (6, 9), (9, 11))
111 ```
112 !!! success "Conclusion: Successfully split 10 items into 4 groups, with the last group having fewer items."
113 </div>
115 ```pycon {.py .python linenums="1" title="Example 3: Single group"}
116 >>> generate_group_cutoffs(10, 1)
117 ```
118 <div class="result" markdown>
119 ```{.sh .shell title="Output"}
120 ((0, 11),)
121 ```
122 !!! success "Conclusion: Successfully created a single group containing all items."
123 </div>
125 ```pycon {.py .python linenums="1" title="Example 4: Zero groups"}
126 >>> generate_group_cutoffs(10, 0)
127 ```
128 <div class="result" markdown>
129 ```{.sh .shell title="Output"}
130 ValueError: Validation failed: 'num_groups > 0' is not True
131 ```
132 !!! failure "Conclusion: Cannot create groups with zero groups specified."
133 </div>
135 ```pycon {.py .python linenums="1" title="Example 5: Negative total number"}
136 >>> generate_group_cutoffs(-10, 3)
137 ```
138 <div class="result" markdown>
139 ```{.sh .shell title="Output"}
140 ValueError: Validation failed: 'total_number > 0' is not True
141 ```
142 !!! failure "Conclusion: Total number must be greater than 0."
143 </div>
145 ```pycon {.py .python linenums="1" title="Example 6: Total number less than groups"}
146 >>> generate_group_cutoffs(3, 5)
147 ```
148 <div class="result" markdown>
149 ```{.sh .shell title="Output"}
150 ValueError: Validation failed: 'total_number >= num_groups' is not True
151 ```
152 !!! failure "Conclusion: Total number must be greater than or equal to the number of groups."
153 </div>
154 """
156 # Validations
157 assert_is_valid(total_number, ">", 0)
158 assert_is_valid(num_groups, ">", 0)
159 assert_is_valid(total_number, ">=", num_groups)
161 # Calculate the size of each group
162 group_size: int = total_number // num_groups
164 # List to store all group cutoffs
165 cutoffs: list[tuple[int, int]] = []
167 # Calculate the number of items that will be in the last group
168 current_start: int = 0
170 # Loop through the number of groups to calculate start and end indices
171 for group in range(num_groups):
173 # For the last group, end is total_number + 1
174 if group == num_groups - 1:
175 current_end: int = total_number + 1
176 else:
177 current_end: int = current_start + group_size
179 # Add the current group cutoff to the list
180 cutoffs.append((current_start, current_end))
182 # Update the start index for the next group
183 current_start: int = current_end
185 # Convert the list to a tuple of tuples
186 return tuple(cutoffs)