Rebol3 Code Examplex


Babylonian spiral

Plot a square-root spiral using geometric construction.

Rebol [
    title: "Rosetta code: Babylonian spiral"
    file:  %Babylonian_spiral.r3
    url:   https://rosettacode.org/wiki/Babylonian_spiral
]

babylonian-spiral: function/with [
    "Get points along a Babylonian spiral of nsteps steps."
    nsteps [integer!]
][
    ;; Grow the shared square-cache if needed so index i holds i*i
    if nsteps > s: length? square-cache [
        for i s nsteps + 1 1 [
            append square-cache i * i
        ]
    ]
    ;; Start with the origin and the first unit step along +y
    output: reduce [0x0 0x1]
    delta-squared: 1

    loop nsteps - 2 [
        ;; Heading of the last delta vector, in degrees
        theta: arctangent2 last output

        ;; Search increasing vector lengths until at least one integer candidate is found
        candidates: clear []
        while [empty? candidates] [
            ++ delta-squared
            ;; Enumerate all pairs (i,j) where i²+j²=delta-squared
            for i 0 (-1 + length? square-cache) 1 [
                a: pickz square-cache i
                if a > delta-squared / 2 [break]  ;; a <= delta-squared/2 by symmetry
                j: 1 + to integer! square-root delta-squared
                while [j >= 1] [
                    b: square-cache/(j + 1)
                    if delta-squared > a + b [break]  ;; j only decreases from here
                    if delta-squared = a + b [
                        ;; Add all 8 reflections of the (i,j) solution
                        -i: negate i
                        -j: negate j
                        repend candidates [
                            as-pair i  j  as-pair -i  j
                            as-pair i -j  as-pair -i -j
                            as-pair j  i  as-pair -j  i
                            as-pair j -i  as-pair -j -i
                        ]
                    ]
                    -- j
                ]
            ]
        ]
        ;; Pick the candidate whose heading deviates least clockwise from theta
        p: first sort/compare candidates func [da db] [
            angle-a: theta - arctangent2 da
            angle-b: theta - arctangent2 db
            ; Wrap into [0, 360) so smallest clockwise turn sorts first
            angle-a: angle-a % 360   if angle-a < 0 [angle-a: angle-a + 360]
            angle-b: angle-b % 360   if angle-b < 0 [angle-b: angle-b + 360]
            angle-a < angle-b
        ]
        append output p
    ]
    ;; Convert delta sequence in-place to absolute positions by cumulative summation
    pos: 0x0 forall output [ change output pos: pos + output/1 ]
    output
][
    ;; Persistent square-cache shared across calls, initialised empty
    square-cache: clear []
]

; --- Main ---
print as-yellow "The first 40 points of the Babylonian spiral are:"
points: babylonian-spiral 40
while [not empty? points][ print [TAB take/part points 10] ]