Rebol3 Code Examplex


Lychrel numbers

Explore numbers that may not become palindromes under reverse-and-add iteration.

Rebol [
    title: "Rosetta code: Lychrel numbers"
    file:  %Lychrel_numbers.r3
    url:   https://rosettacode.org/wiki/Lychrel_numbers
]

big-add: function [
    "Big integer addition using strings"
    a [string!] b [string!]
][
    ;; Ensure we're working on independent copies so we don't mutate the originals
    a: append clear "" a
    b: append clear "" b

    ;; Left-pad the shorter number with zeros so both strings are the same length
    n: (length? a) - length? b
    insert/dup either n < 0 [a][b] #"0" absolute n

    result: copy ""
    c: 0  ; carry digit
    ;; Walk digits right-to-left, summing each column and propagating carry
    for i length? a 1 -1 [
        d: (a/:i - #"0") + (b/:i - #"0") + c  ;; numeric value of column sum
        c: d / 10                             ;; carry into next column
        insert result (#"0" + (d % 10))       ;; prepend the ones digit to result
    ]
    ;; If a final carry remains, prepend it (e.g. 999 + 1 = 1000)
    if #"0" != c: #"0" + c [insert result c]
    result
]

lychrel: function/with [
    "Determine whether an integer is a Lychrel number candidate"
    ;; Returns the Lychrel seed: the lowest related number in the chain,
    ;; or none if num resolves to a palindrome within 1000 steps (i.e. not Lychrel).
    ;; Results are memoised in the `cache` map shared across all calls.
    num [integer!]
][
    ;; Return cached result immediately if this number was seen before
    if val: cache/:num [return val]

    ns:  to string! num      ;; current value as string (grows with each iteration)
    rv:  reverse copy ns     ;; its digit-reversal
    res: num                 ;; default: num is its own seed
    seen: clear []           ;; numbers encountered during this chain, not yet cached

    loop 1000 [
        ns: big-add ns rv    ;; next value = current + reverse
        rv: reverse copy ns  ;; reverse of the new value

        ;; Palindrome found -> num is NOT a Lychrel number
        if ns == rv [ res: none break ]

        ;; Hit a number whose Lychrel status is already known -> inherit its result
        if val: cache/:ns [ res: val break ]

        append seen ns     ;; record this intermediate value for bulk caching below
    ]

    ;; Backfill the cache for every intermediate number encountered in this chain
    foreach x seen [cache/:x: res]
    res
][
    ;; local context - cache persists across calls
    cache: make map! []

]

seeds:   copy []  ;; Lychrel seeds:   numbers that are their own chain origin
related: copy []  ;; Lychrel related: numbers that lead to a known Lychrel seed
palin:   copy []  ;; Lychrel palindromes: Lychrel numbers that are also palindromes
;; Run the search over the first 10 000 positive integers and time it
repeat num 10000 [
    s: lychrel num  ;; none = not Lychrel, otherwise the seed of num's chain
    if s [
        ;; Seeds are numbers equal to their own chain origin; others are related
        append either num == s [seeds][related] num
        ;; Check whether num is also a palindrome in its own right
        if (to string! num) == reverse to string! num [append palin num]
    ]
]
print [as-green pad length? seeds   -6 "Lychrel seeds:" as-yellow seeds]
print [as-green pad length? related -6 "Lychrel related"]
print [as-green pad length? palin   -6 "Lychrel palindromes:" as-yellow palin]