Post Reply 
Pi digits (again) - an unbounded spigot program
05-20-2024, 09:33 AM
Post: #23
RE: Pi digits (again) - an unbounded spigot program
(05-18-2024 08:03 AM)EdS2 Wrote:  I never had a clue how to rewrite Haskell into something I could understand.

These are translations to Python that are closer to the original Haskell programs.
They use typing and itertools which is missing in MicroPython.
So I had to remove them in the program for the Prime.

I'm using version 3.12.2.
There were some changes in the area of typing, so it might not work with older versions of Python.

Rabinowitz and Wagon
Code:
from fractions import Fraction
from itertools import count, islice
from sys import argv
from typing import Callable, Generator

State = tuple[int, int, int, int]


def main(n: int, k: int):

    def stream(
        next: Callable[[State], int],
        safe: Callable[[State, int], bool],
        prod: Callable[[State, int], State],
        cons: Callable[[State, State], State],
        z: State,
        lfts: Generator[State, None, None],
    ) -> Generator[int, None, None]:
        while True:
            y = next(z)
            if safe(z, y):
                yield y
                z = prod(z, y)
            else:
                [x] = list(islice(lfts, 1))
                z = cons(z, x)

    def next(z: State) -> int:
        return int(extr(z, 3))

    def safe(z: State, n: int) -> bool:
        return n == int(extr(z, 4))

    def extr(lft: State, x: int) -> Fraction:
        """
        | q r | | x | = | q*x+r |
        | s t | | 1 |   | s*x+t |
        """
        q, r, s, t = lft
        return Fraction(q * x + r, s * x + t)

    def prod(z: State, n: int) -> State:
        """
        | 10 -10n | * z -> z
        |  0   1  |
        """
        return comp((10, -10 * n, 0, 1), z)

    def comp(a: State, b: State) -> State:
        """
        | q r | | u v | = | q*u+r*w q*v+r*x |
        | s t | | w x |   | s*u+t*w s*v+t*x |
        """
        q, r, s, t = a
        u, v, w, x = b
        return q * u + r * w, q * v + r * x, s * u + t * w, s * v + t * x

    cons = comp
    init = (1, 0, 0, 1)
    lfts = ((k, 4 * k + 2, 0, 2 * k + 1) for k in count(1))
    pi = stream(next, safe, prod, cons, init, lfts)
    for _ in range(n):
        print("".join(map(str, islice(pi, k))))


if __name__ == "__main__":
    n, k, *_ = argv[1:]
    main(int(n), int(k))

Lambert
Code:
from fractions import Fraction
from itertools import count, islice
from sys import argv
from typing import Callable, Generator

LFT = tuple[int, int, int, int]
State = tuple[LFT, int]


def main(n: int, k: int):

    def stream(
        next: Callable[[State], int],
        safe: Callable[[State, int], bool],
        prod: Callable[[State, int], State],
        cons: Callable[[State, LFT], State],
        z: State,
        lfts: Generator[LFT, None, None],
    ) -> Generator[int, None, None]:
        while True:
            y = next(z)
            if safe(z, y):
                yield y
                z = prod(z, y)
            else:
                [x] = list(islice(lfts, 1))
                z = cons(z, x)

    def next(z: State) -> int:
        [[q, r, s, t], i] = z
        x = 2 * i - 1
        return int(Fraction(q * x + r, s * x + t))

    def safe(z: State, n: int) -> bool:
        [[q, r, s, t], i] = z
        x = 5 * i - 2
        return n == int(Fraction(q * x + 2 * r, s * x + 2 * t))

    def prod(s: State, n: int) -> State:
        """
        | 10 -10n | * z -> z
        |  0   1  |
        """
        z, i = s
        return comp((10, -10 * n, 0, 1), z), i

    def cons(s: State, x: LFT) -> State:
        z, i = s
        return comp(z, x), i + 1

    def comp(a: LFT, b: LFT) -> LFT:
        """
        | q r | | u v | = | q*u+r*w q*v+r*x |
        | s t | | w x |   | s*u+t*w s*v+t*x |
        """
        q, r, s, t = a
        u, v, w, x = b
        return q * u + r * w, q * v + r * x, s * u + t * w, s * v + t * x

    init = ((0, 4, 1, 0), 1)
    lfts = ((2 * i - 1, i**2, 1, 0) for i in count(1))
    pi = stream(next, safe, prod, cons, init, lfts)
    for _ in range(n):
        print("".join(map(str, islice(pi, k))))


if __name__ == "__main__":
    n, k, *_ = argv[1:]
    main(int(n), int(k))

Gosper
Code:
from itertools import count, islice
from sys import argv
from typing import Callable, Generator

LFT = tuple[int, int, int, int]
State = tuple[LFT, int]


def main(n: int, k: int):

    def stream(
        next: Callable[[State], int],
        prod: Callable[[State, int], State],
        cons: Callable[[State, LFT], State],
        u: State,
        lfts: Generator[LFT, None, None],
    ) -> Generator[int, None, None]:
        while True:
            [x] = list(islice(lfts, 1))
            v = cons(u, x)
            y = next(v)
            yield y
            u = prod(v, y)

    def next(z: State) -> int:
        [[q, r, s, t], i] = z
        x = 27 * i + 15
        return (q * x + 5 * r) // (s * x + 5 * t)

    def prod(s: State, n: int) -> State:
        """
        | 10 -10n | * z -> z
        |  0   1  |
        """
        z, i = s
        return comp((10, -10 * n, 0, 1), z), i

    def cons(s: State, x: LFT) -> State:
        z, i = s
        return comp(z, x), i + 1

    def comp(a: LFT, b: LFT) -> LFT:
        """
        | q r | | u v | = | q*u+r*w q*v+r*x |
        | s t | | w x |   | s*u+t*w s*v+t*x |
        """
        q, r, s, t = a
        u, v, w, x = b
        return q * u + r * w, q * v + r * x, s * u + t * w, s * v + t * x

    init = ((1, 0, 0, 1), 1)
    lfts = (
        (i * (2 * i - 1), j * (5 * i - 2), 0, j)
        for i, j in ((i, 3 * (3 * i + 1) * (3 * i + 2)) for i in count(1))
    )
    pi = stream(next, prod, cons, init, lfts)
    for _ in range(n):
        print("".join(map(str, islice(pi, k))))


if __name__ == "__main__":
    n, k, *_ = argv[1:]
    main(int(n), int(k))

You may have noticed though, that I replaced the recursive call to stream by an infinite loop.
Well, Python lacks tail call optimization, so we could end up with a stack overflow.

Archive:  unbounded-spigot.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     1790  05-20-2024 11:17   pi-rabinowitz-wagon.py
     1840  05-20-2024 11:17   pi-lambert.py
     1630  05-20-2024 11:17   pi-gosper.py
---------                     -------
     5260                     3 files


Attached File(s)
.zip  unbounded-spigot.zip (Size: 2.55 KB / Downloads: 4)
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
RE: Pi digits (again) - an unbounded spigot program - Thomas Klemm - 05-20-2024 09:33 AM



User(s) browsing this thread: 4 Guest(s)