87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
from collections import deque
|
|
from itertools import dropwhile, islice, takewhile
|
|
from textwrap import wrap
|
|
from typing import Iterable, Iterator
|
|
|
|
|
|
Digits = str # Alias for the return type of to_digits()
|
|
|
|
|
|
def esthetic_nums(base: int) -> Iterator[int]:
|
|
"""Generate the esthetic number sequence for a given base
|
|
|
|
>>> list(islice(esthetic_nums(base=10), 20))
|
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 21, 23, 32, 34, 43, 45, 54, 56, 65]
|
|
"""
|
|
queue: deque[tuple[int, int]] = deque()
|
|
queue.extendleft((d, d) for d in range(1, base))
|
|
while True:
|
|
num, lsd = queue.pop()
|
|
yield num
|
|
new_lsds = (d for d in (lsd - 1, lsd + 1) if 0 <= d < base)
|
|
num *= base # Shift num left one digit
|
|
queue.extendleft((num + d, d) for d in new_lsds)
|
|
|
|
|
|
def to_digits(num: int, base: int) -> Digits:
|
|
"""Return a representation of an integer as digits in a given base
|
|
|
|
>>> to_digits(0x3def84f0ce, base=16)
|
|
'3def84f0ce'
|
|
"""
|
|
digits: list[str] = []
|
|
while num:
|
|
num, d = divmod(num, base)
|
|
digits.append("0123456789abcdef"[d])
|
|
return "".join(reversed(digits)) if digits else "0"
|
|
|
|
|
|
def pprint_it(it: Iterable[str], indent: int = 4, width: int = 80) -> None:
|
|
"""Pretty print an iterable which returns strings
|
|
|
|
>>> pprint_it(map(str, range(20)), indent=0, width=40)
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
|
12, 13, 14, 15, 16, 17, 18, 19
|
|
<BLANKLINE>
|
|
"""
|
|
joined = ", ".join(it)
|
|
lines = wrap(joined, width=width - indent)
|
|
for line in lines:
|
|
print(f"{indent*' '}{line}")
|
|
print()
|
|
|
|
|
|
def task_2() -> None:
|
|
nums: Iterator[int]
|
|
for base in range(2, 16 + 1):
|
|
start, stop = 4 * base, 6 * base
|
|
nums = esthetic_nums(base)
|
|
nums = islice(nums, start - 1, stop) # start and stop are 1-based indices
|
|
print(
|
|
f"Base-{base} esthetic numbers from "
|
|
f"index {start} through index {stop} inclusive:\n"
|
|
)
|
|
pprint_it(to_digits(num, base) for num in nums)
|
|
|
|
|
|
def task_3(lower: int, upper: int, base: int = 10) -> None:
|
|
nums: Iterator[int] = esthetic_nums(base)
|
|
nums = dropwhile(lambda num: num < lower, nums)
|
|
nums = takewhile(lambda num: num <= upper, nums)
|
|
print(
|
|
f"Base-{base} esthetic numbers with "
|
|
f"magnitude between {lower:,} and {upper:,}:\n"
|
|
)
|
|
pprint_it(to_digits(num, base) for num in nums)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("======\nTask 2\n======\n")
|
|
task_2()
|
|
|
|
print("======\nTask 3\n======\n")
|
|
task_3(1_000, 9_999)
|
|
|
|
print("======\nTask 4\n======\n")
|
|
task_3(100_000_000, 130_000_000)
|