An esthetic number is a positive integer where every adjacent digit differs from its neighbour by 1.



E.G.
 12 is an esthetic number. One and two differ by 1.
 5654 is an esthetic number. Each digit is exactly 1 away from its neighbour.
 890 is not an esthetic number. Nine and zero differ by 9.


These examples are nominally in base 10 but the concept extends easily to numbers in other bases. Traditionally, single digit numbers are included in esthetic numbers; zero may or may not be. For our purposes, for this task, do not include zero (0) as an esthetic number. Do not include numbers with leading zeros.

Esthetic numbers are also sometimes referred to as stepping numbers.



Task
 Write a routine (function, procedure, whatever) to find esthetic numbers in a given base.
 Use that routine to find esthetic numbers in bases 2 through 16 and display, here on this page, the esthectic numbers from index (base × 4) through index (base × 6), inclusive. (E.G. for base 2: 8th through 12th, for base 6: 24th through 36th, etc.)
 Find and display, here on this page, the base 10 esthetic numbers with a magnitude between 1000 and 9999.
 Stretch: Find and display, here on this page, the base 10 esthetic numbers with a magnitude between 1.0e8 and 1.3e8.


Related task

   numbers with equal rises and falls


See also

OEIS A033075 - Positive numbers n such that all pairs of consecutive decimal digits differ by 1
Numbers Aplenty - Esthetic numbers
Geeks for Geeks - Stepping numbers


using Formatting
import Base.iterate, Base.IteratorSize, Base.IteratorEltype
 
"""
    struct Esthetic
 
Used for iteration of esthetic numbers
"""
struct Esthetic{T}
    lowerlimit::T where T <: Integer
    base::T
    upperlimit::T
    Esthetic{T}(n, bas, m=typemax(T)) where T = new{T}(nextesthetic(n, bas), bas, m)
end
 
Base.IteratorSize(n::Esthetic) = Base.IsInfinite()
Base.IteratorEltype(n::Esthetic) = Integer
function Base.iterate(es::Esthetic, state=typeof(es.lowerlimit)[])
    state = isempty(state) ? digits(es.lowerlimit, base=es.base) : increment!(state, es.base, 1)
    n = toInt(state, es.base)
    return n <= es.upperlimit ? (n, state) : nothing
end
 
isesthetic(n, b) = (d = digits(n, base=b); all(i -> abs(d[i] - d[i + 1]) == 1, 1:length(d)-1))
toInt(dig, bas) = foldr((i, j) -> i + bas * j, dig)
nextesthetic(n, b) = for i in n:typemax(typeof(n)) if isesthetic(i, b) return i end; end
 
""" Fill the digits below pos in vector with the least esthetic number fitting there """
function filldecreasing!(vec, pos)
    if pos > 1
        n = vec[pos]
        for i in pos-1:-1:1
            n = (n == 0) ? 1 : n - 1
            vec[i] = n
        end
    end
    return vec
end
 
""" Get the next esthetic number's digits from the previous number's digits """
function increment!(vec, bas, startpos = 1)
    len = length(vec)
    if len == 1
        if vec[1] < bas - 1
            vec[1] += 1
        else
            vec[1] = 0
            push!(vec, 1)
        end
    else
        pos = findfirst(i -> vec[i] < vec[i + 1], startpos:len-1)
        if pos == nothing
            if vec[end] >= bas - 1
                push!(vec, 1)
                filldecreasing!(vec, len + 1)
            else
                vec[end] += 1
                filldecreasing!(vec, len)
            end
        else
            for i in pos:len
                if i == len
                    if vec[i] < bas - 1
                        vec[i] += 1
                        filldecreasing!(vec, i)
                    else
                        push!(vec, 1)
                        filldecreasing!(vec, len + 1)
                    end
                elseif vec[i] < vec[i + 1] && vec[i] < bas - 2
                    vec[i] += 2
                    filldecreasing!(vec, i)
                    break
                end
            end
        end
    end
    return vec
end
 
for b in 2:16
    println("For base $b, the esthetic numbers indexed from $(4b) to $(6b) are:")
    printed = 0
    for (i, n) in enumerate(Iterators.take(Esthetic{Int}(1, b), 6b))
        if i >= 4b
            printed += 1
            print(string(n, base=b), printed % 21 == 20 ? "\n" : " ")
        end
    end
    println("\n")
end
 
for (bottom, top, cols, T) in [[1000, 9999, 16, Int], [100_000_000, 130_000_000, 8, Int], 
        [101_010_000_000, 130_000_000_000, 6, Int], [101_010_101_010_000_000, 130_000_000_000_000_000, 4, Int], 
        [101_010_101_010_101_000_000, 130_000_000_000_000_000_000, 4, Int128]]
    esth, count = Esthetic{T}(bottom, 10, top), 0
    println("\nBase 10 esthetic numbers between $(format(bottom, commas=true)) and $(format(top, commas=true)):")
    for n in esth
        count += 1
        if count == 64
            println(" ...")
        elseif count < 64
            print(format(n, commas=true), count % cols == 0 ? "\n" : " ")
        end
    end
    println("\nTotal esthetic numbers in interval: $count")
end
 