RosettaCodeData/Task/Self-numbers/Pluto/self-numbers.pluto

84 lines
3.0 KiB
Plaintext

local start_index, end_index, counter
local current_self
local output
local function done_after_adding(interval, n)
-- Advance to the next self number in the sequence, append it to the output if required, indicate if finished.
for _ = 1, n do
current_self += interval
++counter
if counter >= start_index then
output:insert(current_self)
end
if counter == end_index then return true end
end
return false
end
local function self_numbers(index_range)
start_index = index_range[1]
end_index = index_range:back()
counter = 0
current_self = -1
output = {}
-- Main process. Start with the single-digit odd numbers and first run.
if done_after_adding(2, 5) then return output end
if done_after_adding(11, 9) then return output end
-- If necessary, fast forward to last self number before the lowest-order block containing first number required.
if counter < start_index then
-- The highest-order blocks whose ends this handles correctly contain 9,777,777,778 self numbers.
-- The difference between equivalently positioned numbers in these blocks is 100,000,000,001.
-- The figures for successively lower-order blocks have successively fewer 7s and 0s!
local index_diff = 9777777778
local numeric_diff = 100000000001
while index_diff >= 98 and counter != start_index do
if counter + index_diff < start_index then
counter += index_diff
current_self += numeric_diff
else
index_diff = (index_diff + 2) / 10 -- (..78->80->8)
numeric_diff = (numeric_diff + 9) /10 -- (..01->10->1)
end
end
end
-- Sequencing loop, per lowest-order block.
while true do
-- Eight ten-number runs, each at a numeric interval of 2 from the end of the previous one.
for _ = 1, 8 do
if done_after_adding(2, 1) then return output end
if done_after_adding(11, 9) then return output end
end
-- Two shorter runs, the second at an interval inversely related to their length.
local shorter_run_length = 8
local temp = current_self // 1000
-- Work out a shorter run length based on the most significant digit change about to happen.
while temp % 10 == 9 do
shorter_run_length -= 1
temp //= 10
end
local interval = 2
for _ = 1, 2 do
if done_after_adding(interval, 1) then return output end
if done_after_adding(11, shorter_run_length) then return output end
interval += (9 - shorter_run_length) * 13
end
end
end
print("The first 50 self numbers are:\n")
for i, sn in self_numbers({1, 50}) do
io.write(string.format("%3d ", sn))
if i % 10 == 0 then print() end
end
print()
for p = 8, 9 do
local n = math.round(10 ^ p)
local ord = string.formatint(n)
local sn = string.formatint(math.round(self_numbers({n})[1]))
print($"The {ord}th self number is {sn}")
end