I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Self numbers\Phix

Translation of Go. Replacing the problematic sv[a+b+... line with a bit of dirty inline assembly improved performance by 90%
(Of course you lose bounds checking, type checking, negative subscripts, fraction handling, and all that jazz.)
We use a string of Y/N for the sieve to force one byte per element ('\0' and 1 would be equally valid).

`if machine_bits()=32 then crash("requires 64 bit") end if function sieve()    string sv = repeat('N',2*1e9+9*9+1) -- (1.86GB)    integer n = 0    for a=0 to 1 do        for b=0 to 9 do            for c=0 to 9 do                for d=0 to 9 do                                     for e=0 to 9 do                        for f=0 to 9 do                                                     for g=0 to 9 do                                for h=0 to 9 do                                    for i=0 to 9 do                                        for j=0 to 9 do                                         --                                         n += 1--                                         sv[a+b+c+d+e+f+g+h+i+j+n] = 'Y'#ilASM{    -- (allows clean compilation on 32 bit, before crash as above)      mov rax,[a]    mov r12,[b]    mov r13,[c]    mov r14,[d]    mov r15,[e]    add r12,r13    add r14,r15    add rax,r12     mov rdi,[sv]    add rax,r14     mov r12,[f]    mov r13,[g]    mov r14,[h]    mov r15,[i]    add r12,r13    shl rdi,2    mov rcx,[n]    mov r13,[j]    add r14,r15    add rax,r12     add rax,r14     add r13,rcx    add rax,r13     add rcx,1    mov byte[rdi+rax],'Y'    mov [n],rcx }                                        end for                                    end for                                                                  end for                            end for                          end for                    end for                end for            end for            printf(1,"%d,%d\r",{a,b}) -- (show progress)        end for    end for    return svend function atom t0 = time()string sv = sieve()printf(1,"sieve build took %s\n",{elapsed(time()-t0)})integer count = 0printf(1,"The first 50 self numbers are:\n")for i=1 to length(sv) do    if sv[i]='N' then        count += 1        if count <= 50 then            printf(1,"%3d ",i-1)            if remainder(count,10)=0 then                printf(1,"\n")            end if        end if        if count == 1e8 then            printf(1,"\nThe %,dth self number is %,d (%s)\n",                     {count,i-1,elapsed(time()-t0)})            exit        end if    end ifend for`
Output:
```sieve build took 6.6s
The first 50 self numbers are:
1   3   5   7   9  20  31  42  53  64
75  86  97 108 110 121 132 143 154 165
176 187 198 209 211 222 233 244 255 266
277 288 299 310 312 323 334 345 356 367
378 389 400 411 413 424 435 446 457 468

The 100,000,000th self number is 1,022,727,208 (11.0s)
```

### generator dictionary

While this is dog-slow (see shocking estimate below), it is interesting to note that even by the time it generates the 10,000,000th number, it is only having to juggle a mere 27 generators. Just a shame that we had to push over 10,000,000 generators onto that stack, and tried to push quite a few more. Memory use is pretty low, around ~4MB.
[unlike the above, this is perfectly happy on both 32 and 64 bit]
Long story short: this works much the same as a prime sieve, in which you only need to eliminate multiples of previous primes. Here, you only need to eliminate digital additions of previous safe numbers. Only after writing this did I understand how to write a sliding sieve, which it turns out is a much better idea (see below). Still, this is pretty interesting and quite neat.

`integer gd = new_dict(), gdhead = 2, n = 0 function ng(integer n)    integer r = n    while r do        n += remainder(r,10)        r = floor(r/10)    end while    return nend function function self(integer /*i*/)-- note: assumes sequentially invoked (arg i unused)    n += 1    while n=gdhead do        gdhead = pop_dict(gd)        setd(ng(gdhead),0,gd)        n += (n!=gdhead)    end while    setd(ng(n),0,gd)    return nend function atom t0 = time()printf(1,"The first 50 self numbers are:\n")pp(apply(tagset(50),self),{pp_IntFmt,"%3d",pp_IntCh,false}) constant limit = 10000000integer chk = 100printf(1,"\n          i           n   size time\n")for i=51 to limit do    n = self(i)    if i=chk then        printf(1,"%,11d %,11d %6d %s\n",{i,n,dict_size(gd),elapsed(time()-t0)})        chk *= 10    end ifend forprintf(1,"\nEstimated time for %,d :%s\n",{1e8,elapsed((time()-t0)*1e8/limit)})`
Output:
```The first 50 self numbers are:
{  1,  3,  5,  7,  9, 20, 31, 42, 53, 64, 75, 86, 97,108,110,121,132,143,
154,165,176,187,198,209,211,222,233,244,255,266,277,288,299,310,312,323,
334,345,356,367,378,389,400,411,413,424,435,446,457,468}

i           n   size time
100         973     18 0.1s
1,000      10,188     13 0.2s
10,000     102,225     10 1.0s
100,000   1,022,675     20 9.3s
1,000,000  10,227,221     17 1 minute and 37s
10,000,000 102,272,662     27 16 minutes and 40s

Estimated time for 100,000,000 :2 hours, 46 minutes and 37s
```

For the record, I would not be at all surprised should a translation of this beat 20 minutes (for 1e8)

### sliding sieve

Mid-speed, perhaps helped by a slightly smarter way of calculating/updating the digit sums
Similar to some other entries, we (only) need a sieve of 9*9, +1 here as I test an entry after the slide.

`--sequence sieve = repeat(0,82), -- (~25% slower)sequence sieve = repeat(0,8192),         digits = repeat(0,10)integer offset = 0,        digit_sum = 0,        n = 0 procedure next_self()    while true do        n += 1        for i=length(digits) to 1 by -1 do            integer d = digits[i]            if d!=9 then                digits[i] = d+1                digit_sum += 1                exit            end if            digits[i] = 0            digit_sum -= 9        end for        integer k = n+digit_sum-offset        if k>length(sieve) then            integer j = 1            for i=n-offset to length(sieve) do                sieve[j] = sieve[i]                j += 1            end for            sieve[j..\$] = 0            offset = n-1            k = digit_sum+1        end if        sieve[k] = 1        if sieve[n-offset]=0 then exit end if    end while    -- (result in n)end procedure constant limit = 100000000atom t0 = time()printf(1,"The first 50 self numbers are:\n")integer chk = 100for i=1 to limit do    next_self()    if i<=50 then        printf(1," %3d%s",{n,iff(mod(i,25)=0?"\n":"")})    elsif i=chk then        if chk=100 then            printf(1,"\n           i             n time\n")        end if        printf(1,"%,12d %,13d %s\n",{i,n,elapsed(time()-t0)})        chk *= 10    end ifend forprintf(1,"\nEstimated time for %,d :%s\n",{1e9,elapsed((time()-t0)*1e9/limit)})`
Output:
```The first 50 self numbers are:
1   3   5   7   9  20  31  42  53  64  75  86  97 108 110 121 132 143 154 165 176 187 198 209 211
222 233 244 255 266 277 288 299 310 312 323 334 345 356 367 378 389 400 411 413 424 435 446 457 468

i             n time
100           973 0.1s
1,000        10,188 0.1s
10,000       102,225 0.1s
100,000     1,022,675 0.1s
1,000,000    10,227,221 0.5s
10,000,000   102,272,662 4.5s
100,000,000 1,022,727,208 44.5s

Estimated time for 1,000,000,000 :7 minutes and 25s
```