ID photo of Ciro Santilli taken in 2013 right eyeCiro Santilli OurBigBook logoOurBigBook.com  Sponsor 中国独裁统治 China Dictatorship 新疆改造中心、六四事件、法轮功、郝海东、709大抓捕、2015巴拿马文件 邓家贵、低端人口、西藏骚乱
euler/974.py
#!/usr/bin/env python

'''
By GPT-5.2. Runtime: 0m0.497s on pypy3 3.11.13, Ubuntu 25.10, Lenovo ThinkPad P14s.
'''

from functools import lru_cache

DIGITS = [1, 3, 5, 7, 9]
BIT = {1: 0, 3: 1, 5: 2, 7: 3, 9: 4}
ALL = (1 << 5) - 1

def count_len(L: int) -> int:
    @lru_cache(None)
    def dp(pos: int, mod3: int, mod7: int, mask: int) -> int:
        if pos == L:
            return 1 if (mod3 == 0 and mod7 == 0 and mask == ALL) else 0
        choices = (5,) if pos == L - 1 else tuple(DIGITS)
        total = 0
        for d in choices:
            total += dp(
                pos + 1,
                (mod3 * 10 + d) % 3,
                (mod7 * 10 + d) % 7,
                mask ^ (1 << BIT[d]),
            )
        return total
    return dp(0, 0, 0, 0)

def unrank(L: int, k: int) -> str:
    @lru_cache(None)
    def dp(pos: int, mod3: int, mod7: int, mask: int) -> int:
        if pos == L:
            return 1 if (mod3 == 0 and mod7 == 0 and mask == ALL) else 0
        choices = (5,) if pos == L - 1 else tuple(DIGITS)
        total = 0
        for d in choices:
            total += dp(
                pos + 1,
                (mod3 * 10 + d) % 3,
                (mod7 * 10 + d) % 7,
                mask ^ (1 << BIT[d]),
            )
        return total
    mod3 = 0
    mod7 = 0
    mask = 0
    out = []
    for pos in range(L):
        choices = [5] if pos == L - 1 else DIGITS
        for d in choices:
            cnt = dp(
                pos + 1,
                (mod3 * 10 + d) % 3,
                (mod7 * 10 + d) % 7,
                mask ^ (1 << BIT[d]),
            )
            if k > cnt:
                k -= cnt
            else:
                out.append(str(d))
                mod3 = (mod3 * 10 + d) % 3
                mod7 = (mod7 * 10 + d) % 7
                mask ^= (1 << BIT[d])
                break
        else:
            raise RuntimeError
    return "".join(out)

def theta(n: int, maxL: int = 200) -> str:
    cum = 0
    for L in range(1, maxL + 1, 2):
        c = count_len(L)
        if cum + c >= n:
            return unrank(L, n - cum)
        cum += c
    raise ValueError

if __name__ == "__main__":
    assert theta(1, maxL=40) == "1117935"
    assert theta(10**3, maxL=40) == "11137955115"
    print(theta(10**16, maxL=200))