**Approach (brief)**- Sort requests descending (largest first). For each host count compute the minimal prefix length (smallest CIDR) that fits hosts (+2 for network/broadcast). Walk available free subnets inside base CIDR and allocate the first-fitting aligned block. Fail if any request cannot be satisfied.**Code**python
#!/usr/bin/env python3
import ipaddress, math
from typing import List, Tuple
def hosts_to_prefix(hosts: int) -> int:
needed = hosts + 2
bits = math.ceil(math.log2(needed))
return 32 - bits
def split_space(base: ipaddress.IPv4Network, requests: List[int]) -> List[Tuple[int, ipaddress.IPv4Network]]:
# available list of networks (non-overlapping), start with base
available = [base]
out = []
# pair requests with original index to report in same order if needed
indexed = sorted(enumerate(requests), key=lambda x: -x[1])
for idx, req in indexed:
pref = hosts_to_prefix(req)
found = None
for i, net in enumerate(available):
# iterate subnets of net with desired prefix
if pref < net.prefixlen:
# cannot satisfy (want bigger network than container)
continue
for candidate in net.subnets(new_prefix=pref):
# candidate fits and aligned by construction
found = (i, candidate)
break
if found:
break
if not found:
raise ValueError(f"Insufficient space for {req} hosts")
i, cand = found
out.append((idx, cand))
# remove used candidate from available and add leftovers
container = available.pop(i)
# container minus cand -> at most two pieces (before and after). derive by comparing ranges
for sub in container.address_exclude(cand):
available.append(sub)
# keep available sorted by network address ascending (helps deterministic)
available.sort(key=lambda n: int(n.network_address))
# return allocations in same order as requests
allocations = [None]*len(requests)
for idx, net in out:
allocations[idx] = net
return allocations
# CLI wrapper omitted for brevity; core logic above.
# Unit tests
def test_simple():
base = ipaddress.ip_network("10.0.0.0/24")
allocs = split_space(base, [50, 20, 5])
assert str(allocs[0]) == "10.0.0.0/26" # 62 hosts
assert str(allocs[1]) == "10.0.0.64/27" # 30 hosts
assert str(allocs[2]) == "10.0.0.96/29" # 6 hosts
def test_insufficient():
base = ipaddress.ip_network("192.0.2.0/30")
try:
split_space(base, [2])
raise AssertionError("Expected failure")
except ValueError:
pass
if __name__ == "__main__":
test_simple()
test_insufficient()
print("tests passed")
**Complexity**- Sorting requests: O(n log n)- For each request, scanning available list and generating subnets: in worst case available grows O(n), and generating subnets for a container is O(2^(depth)) but bounded by constant 32; overall O(n^2) worst-case with small constant factors.- Space: O(n) for allocations and available list.**Notes / edge cases**- Aligns allocations to CIDR boundaries by using ipaddress.subnets.- Accounts for network/broadcast by adding +2 hosts.- Could optimize by using interval trees for huge n.