283 lines
5.7 KiB
ObjectPascal
283 lines
5.7 KiB
ObjectPascal
program SmithNum;
|
|
{$IFDEF FPC}
|
|
{$MODE objFPC} //result and useful for x64
|
|
{$CODEALIGN PROC=64}
|
|
{$ENDIF}
|
|
uses
|
|
sysutils;
|
|
type
|
|
tdigit = byte;
|
|
tSum = LongInt;
|
|
const
|
|
base = 10;
|
|
//maxDigitCnt *(base-1) <= High(tSum)
|
|
//maxDigitCnt <= High(tSum) DIV (base-1);
|
|
maxDigitCnt = 16;
|
|
|
|
StartPrimNo = 6;
|
|
csegsieveSIze = 2*3*5*7*11*13;//prime 0..5
|
|
type
|
|
tDgtSum = record
|
|
dgtNum : LongInt;
|
|
dgtSum : tSum;
|
|
dgts : array[0..maxDigitCnt-1] of tdigit;
|
|
end;
|
|
tNumFactype = word;
|
|
tnumFactor = record
|
|
numfacCnt: tNumFactype;
|
|
numfacts : array[1..15] of tNumFactype;
|
|
end;
|
|
tpnumFactor= ^tnumFactor;
|
|
|
|
tsieveprim = record
|
|
spPrim : Word;
|
|
spDgtsum : Word;
|
|
spOffset : LongWord;
|
|
end;
|
|
tpsieveprim = ^tsieveprim;
|
|
|
|
tsievePrimarr = array[0..6542-1] of tsieveprim;
|
|
tsegmSieve = array[1..csegsieveSIze] of tnumFactor;
|
|
|
|
var
|
|
Primarr:tsievePrimarr;
|
|
copySieve,
|
|
actSieve : tsegmSieve;
|
|
PrimDgtSum :tDgtSum;
|
|
PrimCnt : NativeInt;
|
|
|
|
function IncDgtSum(var ds:tDgtSum):boolean;
|
|
//add 1 to dgts and corrects sum of Digits
|
|
//return if overflow happens
|
|
var
|
|
i : NativeInt;
|
|
Begin
|
|
i := High(ds.dgts);
|
|
inc(ds.dgtNum);
|
|
repeat
|
|
IF ds.dgts[i] < Base-1 then
|
|
//add one and done
|
|
Begin
|
|
inc(ds.dgts[i]);
|
|
inc(ds.dgtSum);
|
|
BREAK;
|
|
end
|
|
else
|
|
Begin
|
|
ds.dgts[i] := 0;
|
|
dec(ds.dgtSum,Base-1);
|
|
end;
|
|
dec(i);
|
|
until i < Low(ds.dgts);
|
|
result := i < Low(ds.dgts)
|
|
end;
|
|
|
|
procedure OutDgtSum(const ds:tDgtSum);
|
|
var
|
|
i : NativeInt;
|
|
Begin
|
|
i := Low(ds.dgts);
|
|
repeat
|
|
write(ds.dgts[i]:3);
|
|
inc(i);
|
|
until i > High(ds.dgts);
|
|
writeln(' sum of digits : ',ds.dgtSum:3);
|
|
end;
|
|
|
|
procedure OutSieve(var s:tsegmSieve);
|
|
var
|
|
i,j : NativeInt;
|
|
Begin
|
|
For i := Low(s) to High(s) do
|
|
with s[i] do
|
|
Begin
|
|
write(i:6,numfacCnt:4);
|
|
For j := 1 to numfacCnt do
|
|
write(numFacts[j]:5);
|
|
writeln;
|
|
end;
|
|
end;
|
|
|
|
procedure SieveForPrimes;
|
|
// sieve for all primes < High(Word)
|
|
var
|
|
sieve : array of byte;
|
|
pS : pByte;
|
|
p,i : NativeInt;
|
|
Begin
|
|
setlength(sieve,High(Word));
|
|
Fillchar(sieve[Low(sieve)],length(sieve),#0);
|
|
pS:= @sieve[0]; //zero based
|
|
dec(pS);// make it one based
|
|
//sieve
|
|
p := 2;
|
|
repeat
|
|
i := p*p;
|
|
IF i> High(Word) then
|
|
BREAK;
|
|
repeat pS[i] := 1; inc(i,p); until i > High(Word);
|
|
repeat inc(p) until pS[p] = 0;
|
|
until false;
|
|
//now fill array of primes
|
|
fillchar(PrimDgtSum,SizeOf(PrimDgtSum),#0);
|
|
IncDgtSum(PrimDgtSum);//1
|
|
i := 0;
|
|
For p := 2 to High(Word) do
|
|
Begin
|
|
IncDgtSum(PrimDgtSum);
|
|
if pS[p] = 0 then
|
|
Begin
|
|
with PrimArr[i] do
|
|
Begin
|
|
spOffset := 2*p;//start at 2*prime
|
|
spPrim := p;
|
|
spDgtsum := PrimDgtSum.dgtSum;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
end;
|
|
PrimCnt := i-1;
|
|
end;
|
|
|
|
procedure MarkWithPrime(SpIdx:NativeInt;var sf:tsegmSieve);
|
|
var
|
|
i : NativeInt;
|
|
pSf :^tnumFactor;
|
|
MarkPrime : NativeInt;
|
|
Begin
|
|
with Primarr[SpIdx] do
|
|
Begin
|
|
MarkPrime := spPrim;
|
|
i := spOffSet;
|
|
IF i <= csegsieveSize then
|
|
Begin
|
|
pSf := @sf[i];
|
|
repeat
|
|
pSf^.numFacts[pSf^.numfacCnt+1] := SpIdx;
|
|
inc(pSf^.numfacCnt);
|
|
inc(pSf,MarkPrime);
|
|
inc(i,MarkPrime);
|
|
until i > csegsieveSize;
|
|
end;
|
|
spOffset := i-csegsieveSize;
|
|
end;
|
|
end;
|
|
|
|
procedure InitcopySieve(var cs:tsegmSieve);
|
|
var
|
|
pr: NativeInt;
|
|
Begin
|
|
fillchar(cs[Low(cs)],sizeOf(cs),#0);
|
|
For Pr := 0 to 5 do
|
|
Begin
|
|
with Primarr[pr] do
|
|
spOffset := spPrim;//mark the prime too
|
|
MarkWithPrime(pr,cs);
|
|
end;
|
|
end;
|
|
|
|
procedure MarkNextSieve(var s:tsegmSieve);
|
|
var
|
|
idx: NativeInt;
|
|
Begin
|
|
s:= copySieve;
|
|
For idx := StartPrimNo to PrimCnt do
|
|
MarkWithPrime(idx,s);
|
|
end;
|
|
|
|
function DgtSumInt(n: NativeUInt):NativeUInt;
|
|
var
|
|
r : NativeUInt;
|
|
Begin
|
|
result := 0;
|
|
repeat
|
|
r := n div base;
|
|
inc(result,n-base*r);
|
|
n := r
|
|
until r = 0;
|
|
end;
|
|
|
|
{function DgtSumOfFac(pN: tpnumFactor;dgtNo:tDgtSum):boolean;}
|
|
function TestSmithNum(pN: tpnumFactor;dgtNo:tDgtSum):boolean;
|
|
var
|
|
i,k,r,dgtSumI,dgtSumTarget : NativeUInt;
|
|
pSp:tpsieveprim;
|
|
pNumFact : ^tNumFactype;
|
|
Begin
|
|
i := dgtNo.dgtNum;
|
|
dgtSumTarget :=dgtNo.dgtSum;
|
|
|
|
dgtSumI := 0;
|
|
with pN^ do
|
|
Begin
|
|
k := numfacCnt;
|
|
pNumFact := @numfacts[k];
|
|
end;
|
|
|
|
For k := k-1 downto 0 do
|
|
Begin
|
|
pSp := @PrimArr[pNumFact^];
|
|
r := i DIV pSp^.spPrim;
|
|
repeat
|
|
i := r;
|
|
r := r DIV pSp^.spPrim;
|
|
inc(dgtSumI,pSp^.spDgtsum);
|
|
until (i - r* pSp^.spPrim) <> 0;
|
|
IF dgtSumI > dgtSumTarget then
|
|
Begin
|
|
result := false;
|
|
EXIT;
|
|
end;
|
|
dec(pNumFact);
|
|
end;
|
|
If i <> 1 then
|
|
inc(dgtSumI,DgtSumInt(i));
|
|
result := dgtSumI = dgtSumTarget
|
|
end;
|
|
|
|
function CheckSmithNo(var s:tsegmSieve;var dgtNo:tDgtSum;Lmt:NativeInt=csegsieveSIze):NativeUInt;
|
|
var
|
|
pNumFac : tpNumFactor;
|
|
i : NativeInt;
|
|
Begin
|
|
result := 0;
|
|
i := low(s);
|
|
pNumFac := @s[i];
|
|
For i := i to lmt do
|
|
Begin
|
|
incDgtSum(dgtNo);
|
|
IF pNumFac^.numfacCnt<> 0 then
|
|
IF TestSmithNum(pNumFac,dgtNo) then
|
|
Begin
|
|
inc(result);
|
|
//Mark as smith number
|
|
inc(pNumFac^.numfacCnt,1 shl 15);
|
|
end;
|
|
inc(pNumFac);
|
|
end;
|
|
end;
|
|
|
|
const
|
|
limit = 100*1000*1000;
|
|
var
|
|
actualNo :tDgtSum;
|
|
i,s : NativeInt;
|
|
Begin
|
|
SieveForPrimes;
|
|
InitcopySieve(copySieve);
|
|
i := 1;
|
|
s:= -6;//- 2,3,5,7,11,13
|
|
|
|
fillchar(actualNo,SizeOf(actualNo),#0);
|
|
while i < Limit-csegsieveSize do
|
|
Begin
|
|
MarkNextSieve(actSieve);
|
|
inc(s,CheckSmithNo(actSieve,actualNo));
|
|
inc(i, csegsieveSize);
|
|
end;
|
|
//check the rest
|
|
MarkNextSieve(actSieve);
|
|
inc(s,CheckSmithNo(actSieve,actualNo,Limit-i+1));
|
|
write(s:8,' smith-numbers up to ',actualNo.dgtnum:10);
|
|
end.
|