Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 31

unit MathsLib;

{Copyright 2005, Gary Darby, Intellitech Systems Inc., www.DelphiForFun.org

This program may be used or modified for any non-commercial purpose


so long as this original notice remains in place.
All other rights are reserved
}

{Assortment of math related functions and procedure in various states}

{Revision Copyright 2006, Charles Doumar,  January 2006


Added:
   Tprimes.BSPrime ... Binary search function to find index of prime in prime table.
   Tprimes.MaxPrimeInTable ... Returns Max Prime in prime table.
   Tprimes.GetNthPrime ... Returns Nth prime in table (returns -1 if not in table).
Optimized:
    Tprimes.IsPrime ... Optimized for values greater than MaxPrimeInTable squared.
    Tprimes.NextPrime ... Speed up lookup of values within table range, added large value support
    Tprime.PrevPrime ... Speed up lookup of values within table range, added large value support
    Tprime.GetFactors ... Now returns factors for numbers greater than MaxPrimeInTable Squared
 }

interface

uses Classes, SysUtils, Windows, Dialogs, UBigIntsV2;

type
  intset = set of byte;

  TPoint64=record
    x,y:int64;
  end;

function GetNextPandigital(size: integer; var Digits: array of integer): boolean;


function IsPolygonal(T: int64; var rank: array of integer): boolean;
function GeneratePentagon(n: integer): integer;
function IsPentagon(p: integer): boolean;
function isSquare(const N: int64): boolean;
function isCube(const N: int64): boolean;

function isPalindrome(n: int64): boolean;


function GetEulerPhi(n: int64): int64;
procedure SortStrDown(var s: string);
procedure rotatestrleft(var s: string);

function IntPower(a, b: int64): int64;


function gcd2(a, b: int64): int64;
function GCDMany(A: array of integer): integer;
function LCMMany(A: array of integer): integer;
procedure ContinuedFraction(A: array of int64; const wholepart: integer;
  var numerator, denominator: int64);
function Factorial(n: int64): int64;

type
  TPrimes = class(TObject)
  protected
    function BSPrime(const n: int64; var index: integer): boolean;
  public
    Prime:    array of int64;  {Array of primes - 0th entry is not used}
    nbrprimes, nbrfactors, nbrcanonicalfactors, nbrdivisors: integer;
    Factors:  array of int64; {array of factors - 0th entry is not used}
    CanonicalFactors: array of TPoint64;
    Divisors: array of int64;
    function GetNextPrime(n: int64): int64;
    function GetPrevPrime(n: int64): int64;
    function IsPrime(n: int64): boolean;
    procedure GetFactors(const n: int64);  {get all prime factors}
    function MaxPrimeInTable: int64;
    function GetNthPrime(const n: integer): int64;
    procedure GetCanonicalFactors(const n: int64);  {get ccanonical prime factors}
    procedure GetDivisors(const n: int64);          {get all divisors}
    function Getnbrdivisors(n: int64): integer;

    constructor Create;


    destructor Destroy; override;
  end;

var
  Primes:        TPrimes;
  maxprimes:     integer = 50000; {initial size of primes array}
  maxfactors:    integer = 200;   {initial size of factors array}
  maxval:        int64 = 1000000000000;
  {10^12 - 10^6 is  max prime to be tested from tables}
  {primes up to the sqrt of maxval will be tabled}
  continuants:   array of int64;
  maxcontinuant: integer;

implementation

uses Math;

var
  nbrdiv: int64;

{************* Nbrfactors *************}


function nbrfactors(n: integer): integer;
var
  i: integer;
begin
  Result := 0;
  for i := 1 to trunc(0.0 + sqrt(n)) - 1 do
    if n mod i = 0 then
      Result := Result + 2;
  if n mod trunc(0.0 + sqrt(n)) = 0 then
    Inc(Result); {perfect square}
end;

{************ IsPrime **********}


function isprime(f: int64): boolean;
(*
var
  i:    int64;
  stop: int64;
*)
begin
  result:=primes.isprime(f);
end;

{************* IsSquare *************}


function isSquare(const N: int64): boolean;
var
  ex: extended;
  x:  int64;
begin
  ex     := sqrt(N + 0.0);
  x      := trunc(ex + 1e-10);
  Result := x * x = N;
end;

{************* IsCube *************}


function isCube(const N: int64): boolean;
var
  ex: extended;
  x:  int64;
begin
  ex     := (exp(ln(0.0 + N) / 3));
  x      := trunc(ex + 1e-6);
  Result := x * x * x = N;
end;

function gcd2(a, b: int64): int64;


  {return gcd of a and b}
  {Euclids method}
var
  g, z: int64;
begin
  g := b;
  if b <> 0 then
    while g <> 0 do
    begin
      z := a mod g;
      a := g;
      g := z;
    end;
  Result := a;
end;

function GCDMany(A: array of integer): integer;


  {Greatest common denominator}

var
  i: integer;
  {m:integer;}
  g: integer;
begin
  {   result:=0; }
   (*
   m:=minintvalue(a);
   g:=gcd2(m,a[0]);
   For i := 1 to length(a)-1 do g:=min(gcd2(m,a[i]),g);
   *)
  g := a[0];
  if length(a) >= 2 then
  begin
    g := gcd2(g, a[1]);
    if length(a) > 2 then
      for i := 2 to length(a) - 1 do
        g := gcd2(g, a[i]);
  end;
  Result := g;
end;

function LCMMany(A: array of integer): integer;


var
  i, x: integer;
begin
  x := a[0];
  for i := 1 to length(a) - 1 do
    x := (x * a[i]) div gcd2(x, a[i]);
  Result := x;
end;
function intpower(a, b: int64): int64;
var
  i: integer;
begin
  Result := 1;
  for i := 1 to b do
    Result := Result * a;
end;

function getEulerPhi(n: int64): int64;


var
  i: integer;
  p: int64;
  k: int64;
begin
  primes.getfactors(n);
  Result := 1;
  p      := primes.factors[1];
  k      := 0;
  for i := 1 to primes.nbrfactors do
  begin
    if p = primes.factors[i] then
      Inc(k)
    else
    begin
      Result := Result * (p - 1) * intpower(p, k - 1);
      k      := 1;
      p      := primes.factors[i];
    end;
  end;
  Result := Result * (p - 1) * intpower(p, k - 1);
end;

{**************** LoadCommaText **********}


procedure loadcommatext(list: TStringList; filename: string);
var
  f:    Textfile;
  line: string;
begin
  assignfile(f, filename);
  reset(f);
  readln(f, line);
  list.commatext := line;
  closefile(f);
end;
{**************** GetBigComboCount *************}
procedure GetBigCombocount(const r, n: integer; var ccount: TInteger);
{Return number of combinations -- n things taken r at a time
without replacement}
{Return number of combinations -- n things taken r at a time
without replacement}
var
  work: TInteger;
begin
  work := TInteger.Create;
  if (r > 0) and (r < n) then
  begin
    ccount.Assign(N);
    ccount.Factorial;
    work.Assign(r);
    work.Factorial;
    ccount.Divide(work);
    work.Assign(n - r);
    work.Factorial;
    ccount.Divide(work);
  end
  else if r = n then
    ccount.Assign(1)
  else
    ccount.Assign(0);
  work.Free;
end;

{************** SortStrDown ************}


procedure sortstrDown(var s: string);
var
  i, j: integer;
  ch:   char;
begin
  for i := 1 to length(s) - 1 do
    for j := i + 1 to length(s) do
      if s[j] > s[i] then
      begin  {swap}
        ch   := s[i];
        s[i] := s[j];
        s[j] := ch;
      end;
end;

{************** SortStrUp ************}


procedure sortstrUp(var s: string);
var
  i, j: integer;
  ch:   char;
begin
  for i := 1 to length(s) - 1 do
    for j := i + 1 to length(s) do
      if s[j] < s[i] then
      begin  {swap}
        ch   := s[i];
        s[i] := s[j];
        s[j] := ch;
      end;
end;

{*************** IsPandigital *******}


function ispandigital(const n, Base: int64;
  const includezero, exactlyOnce: boolean): boolean;
var
  i:      integer;
  Digits: array of integer;
begin
  SetLength(Digits, Base);
  for i := 0 to Base - 1 do
    Digits[i] := 0;
  Result := True;
  i := n;
  while i > 0 do
  begin
    Inc(Digits[i mod Base]);
    i := i div Base;
  end;
  {sure that all digit from 1 to max digit found occured exactly once and no higher digit occurred a all}
  for i := 0 to Base - 1 do
  begin
    if exactlyonce then
    begin
      if ((i = 0) and includezero and (Digits[i] <> 1)) or
        ((i > 0) and (Digits[i] <> 1)) then
      begin
        Result := False;
        break;
      end;
    end
    else
    begin
      if ((i = 0) and includezero and (Digits[i] = 0)) or ((i > 0) and (Digits[i] = 0))
      then
      begin
        Result := False;
        break;
      end;
    end;
  end;
end;

{************** GetNextPandigital}
function GetNextPandigital(size: integer; var Digits: array of integer): boolean;
  {Generates 9 or 10 digit permutations of digits in decreasing sequence,
   Input parameter "size" is the number of digits to generate (2 to 10).
   Output placed in open array "digits",  so index value of k refers
   to (k+1)th entry.
   Result is true until all values have been returned.
   Initialize "digits" array with 9,8,7,6,5,4,3,2,1,0 (10 digit pandigitals) or
   9,8,7,6,5,4,3,2,1 (9 digit "almost" pandigitals) before first call.
 }
  procedure swap(i: integer; j: integer);
  {swap digits[i] and digits[j]}
  var
    temp: integer;
  begin
    temp      := Digits[i];
    Digits[i] := Digits[j];
    Digits[j] := temp;
  end;

var
  k, j, r, s: integer;
begin
  k := size - 2; {start at next-to-last}
  {find the last decreasing-order pair}
  while (k >= 0) and (Digits[k] > Digits[k + 1]) do
    Dec(k);
  if k < 0 then
    Result := False {if none in decreasing order, we're done}
  else
  begin
    j := size - 1; {find the rightmost digit less than digits[k]}
    while Digits[k] > Digits[j] do
      j := j - 1;
    swap(j, k); {and swap them}
    r := size - 1;
    s := k + 1;  {from there to the end, swap end digits toward the center}
    while r > s do
    begin
      swap(r, s);
      r := r - 1;
      s := s + 1;
    end;
    Result := True;  {magic!}
  end;
end;

{************** GetPrevPandigital}
function GetPrevPandigital(size: integer; var Digits: array of integer): boolean;
  {Generates 9 or 10 digit permutations of digits in decreasing sequence,
   Input parameter "size" is the number of digits to generate (2 to 10).
   Output placed in open array "digits",  so index value of k refers
   to (k+1)th entry.
   Result is true until all values have been returned.
   Initialize "digits" array with 9,8,7,6,5,4,3,2,1,0 (10 digit pandigitals) or
   9,8,7,6,5,4,3,2,1 (9 digit "almost" pandigitals) before first call.
 }
  procedure swap(i: integer; j: integer);
  {swap digits[i] and digits[j]}
  var
    temp: integer;
  begin
    temp      := Digits[i];
    Digits[i] := Digits[j];
    Digits[j] := temp;
  end;

var
  k, j, r, s: integer;
begin
  k := size - 2; {start at next-to-last}
  {find the last decreasing-order pair}
  while (k >= 0) and (Digits[k] < Digits[k + 1]) do
    Dec(k);
  if k < 0 then
    Result := False {if none in decreasing order, we're done}
  else
  begin
    j := size - 1; {find the rightmost digit less than digits[k]}
    while Digits[k] < Digits[j] do
      j := j - 1;
    swap(j, k); {and swap them}
    r := size - 1;
    s := k + 1;  {from there to the end, swap end digits toward the center}
    while r > s do
    begin
      swap(r, s);
      r := r - 1;
      s := s + 1;
    end;
    Result := True;  {magic!}
  end;
end;
{************ RotateStrLeft **********}
procedure rotatestrleft(var s: string);
var
  ch:     char;
  len: integer;
begin
  len := length(s);
  if len > 1 then
  begin
    ch := s[1];
    move(s[2],s[1],len-1);
    s[len] := ch;
  end;
end;

{*********** IsPalindrome *************}


function isPalindrome(n: int64): boolean;
var
  s: string;
  i: integer;
begin
  s      := IntToStr(n);
  Result := True;
  for i := 1 to length(s) div 2 do
  begin
    if not (s[i] = s[length(s) + 1 - i]) then
    begin
      Result := False;
      break;
    end;
  end;
end;

{**************** NextPermute *************}


function nextpermute(var a: array of byte): boolean;
   {
   SEPA: A Simple, Efficient Permutation Algorithm
   Jeffrey A. Johnson, Brigham Young University-Hawaii Campus
   http://www.cs.byuh.edu/~johnsonj/permute/soda_submit.html
 }
  {My new favorite - short, fast,  understandable  and requires no data
  structures or intialization, each output is generated as the
  next permutation after the permutation passed!}

var
  i, j, key, temp, rightmost: integer;
begin
    {1. Find Key, the leftmost byte of rightmost in-sequence pair
        If none found, we are done}

  {  Characters to the right of key are the "tail"}


    {  Example 1432 -
       Step 1:  check pair 3,2 - not in sequence
               check pair 4,3 - not in sequence
               check pair 1,4 - in sequence ==> key is a[0]=1, tail is 432

    }
  rightmost := high(a);
  i := rightmost - 1; {Start at right end -1}
  while (i >= 0) and (a[i] >= a[i + 1]) do
    Dec(i); {Find in-sequence pair}
  if i >= 0 then  {Found it, so there is another permutation}
  begin
    Result := True;
    key    := a[i];

    {2A. Find rightmost in tail that is > key}


    j := rightmost;
    while (j > i) and (a[j] < a[i]) do
      Dec(j);
    {2B. and swap them} a[i] := a[j];
    a[j] := key;
      {Example - 1432  1=key 432=tail
       Step 2:  check 1 vs 2,  2 > 1 so swap them producing 2431}

    {3. Sort tail characters in ascending order}


      {   By definition, the tail is in descending order now,
          so we can do a swap sort by exchanging first with last,
          second with next-to-last, etc.}
      {Example - 2431  431=tail
        Step 3:
                 compare 4 vs 1 - 4 is greater so swap producing 2134
                 tail sort is done.

                final array = 2134


     }
    Inc(i);
    j := rightmost; {point i to tail start, j to tail end}
    while j > i do
    begin
      if a[i] > a[j] then
      begin {swap}
        temp := a[i];
        a[i] := a[j];
        a[j] := temp;
      end;
      Inc(i);
      Dec(j);
    end;
  end
  else
    Result := False; {else please don't call me any more!}
end;

function GeneratePentagon(n: integer): integer;


begin
  Result := n * (3 * n - 1) div 2;
end;

(*
function IsPolygonal(T:int64):intset;
{from http://mathworld.wolfram.com/PolygonalNumber.html}
var
  test:byte;
  n:int64;
  s2,s:int64;
begin
result:=[];
  test:=3;
  while test<=8 do
  begin
    s2:=8*(test-2)*T+(test-4)*(test-4);
    s:=trunc(sqrt(0.0+s2));
    if  s*s=s2 then
    begin {s2 is a perfect square do the number is Test-ogonal};
      result:=result+[test];
    end;
    inc(test);
  end;
end;
*)
function getpolygonal(p, r: int64): int64;
begin
  case p of
    3: Result := (r * (r + 1) div 2);
    4: Result := (r * r);
    5: Result := (r * (3 * r - 1) div 2);
    6: Result := (r * (2 * r - 1));
    7: Result := (r * (5 * r - 3) div 2);
    8: Result := (r * (3 * r - 2));
    else
      Result := 0;
  end;
end;
function IsPolygonal(T: int64; var rank: array of integer): boolean;
  {from http://mathworld.wolfram.com/PolygonalNumber.html}
var
  test:  byte;
  r:     int64;
  s2, s: int64;
begin
  Result := False;
  test   := 3;
  while test <= 8 do
  begin
    s2 := 8 * (test - 2) * T + (test - 4) * (test - 4);
    s  := trunc(sqrt(0.0 + s2));
    if s * s = s2 then  {it could be a polygonal}
    begin
      {s2 is a perfect square do the number could be Test-ogonal};
      r := (s + test - 4) div (2 * (test - 2));
      if getpolygonal(test, r) <> T then
        r := 0;
      Result := True;
    end
    else
      r := 0;
    rank[test] := r;
    Inc(test);
  end;
end;

function MakePolyName(t: integer): string;


  {make polygonal figure name from numbe}
begin
  Result := '';
  case t of
    3: Result := ' triangular';
    4: Result := ' square    ';
    5: Result := ' pentagonal';
    6: Result := ' hexagonal ';
    7: Result := ' heptagonal';
    8: Result := ' octagonal ';
    else
      Result := 'Unknown';
  end;
end;

function IsPentagon(p: integer): boolean;


var
  n: integer;
begin
  n      := Round(sqrt(2 * p / 3));
  Result := p = n * (3 * n - 1) div 2;
end;

(*
Triangle   P3,n=n(n+1)/2   1, 3, 6, 10, 15, ...
Square   P4,n=n2   1, 4, 9, 16, 25, ...
Pentagonal   P5,n=n(3n–1)/2   1, 5, 12, 22, 35, ...
Hexagonal   P6,n=n(2n–1)   1, 6, 15, 28, 45, ...
Heptagonal   P7,n=n(5n–3)/2   1, 7, 18, 34, 55, ...
Octagonal   P8,n=n(3n–2)
*)

(*

procedure generateTriangle(n:integer);
var i:integer;
  oldlength:integer;
begin
  oldlength:=length(triangle);
  setlength(triangle,n);
  for i:= oldlength+1 to n do triangle[i-1]:=i*(i+1) div 2;
end;

procedure generatePentagon(n:integer);
var
  i:integer;
  oldlength:integer;
begin
  oldlength:=length(pentagon);
  setlength(Pentagon,n);
  for i:= oldlength+1 to n do pentagon[i-1]:=i*(3*i-1) div 2;
end;

procedure generateHexagon(n:integer);
var i:integer;
  oldlength:integer;
begin
  oldlength:=length(hexagon);
  setlength(hexagon,n);
  for i:= oldlength+1 to n do hexagon[i-1]:=i*(2*i-1);
  end;

function istriangle(n:integer): boolean;


var i:integer;
begin
  result:=false;
  for i:= 0 to high(i) do
  begin
    if triangle[i]=n then
    begin
      result:=true;
      break;
    end
    else if triangle[i]>n then break;
  end;
end;

function ispentagon(n:integer): boolean;


var i:integer;
begin
  result:=false;
  for i:= 0 to high(i) do
  begin
    if pentagon [i]=n then
    begin
      result:=true;
      break;
    end
    else if pentagon[i]>n then break;
  end;
end;

function ishexagon(n:integer): boolean;


var i:integer;
begin
  result:=false;
  for i:= 0 to high(i) do
  begin
    if hexagon [i]=n then
    begin
      result:=true;
      break;
    end
    else if hexagon[i]>n then break;
  end;
end;
*)

{************ IntToBinaryStr ********}


function InttoBinaryStr(nn: integer): string;
var
  n: integer;
begin
  n      := nn;
  Result := '';
  while n > 0 do
  begin
    if n mod 2 = 0 then
      Result := '0' + Result
    else
      Result := '1' + Result;
    n := n div 2;
  end;
end;

function Factorial(n: int64): int64;


var
  i: integer;
begin
  Result := 1;
  for i := 2 to n do
    Result := Result * i;
end;

{*********** NbrPrimes *********}


function nbrprimes(a, b: integer): integer;
{Return the number of primes between a and b }
var
  n:    integer;
  quit: boolean;
  w:    int64;
begin
  Result := 0;
  n      := -1;
  quit   := False;
  repeat
    Inc(n);

    w := n * n + a * n + b;
    if (w >= 0) and isprime(w) then
      Inc(Result)
    else
      quit := True;
  until quit;
end;

{************* CycleLen ************}


function cyclelen(n: integer; var s: string): integer;
{Given N return a string representation of 1/N
and the Cycle length}
var
  i, r, d: integer;
  remainders: array of integer;
  Count: integer;
  c: char;
begin
  SetLength(remainders, n);
  Result := 0;
  for i := 0 to n - 1 do
    remainders[i] := -1;
  s := '';
  r     := 1;
  Count := 0;
  repeat
    Inc(Count);
    r := r * 10;
    d := r div n;
    r := r mod n;

    if remainders[r] > 0 then


      Result := Count - remainders[r]
    else
    begin
      remainders[r] := Count;
      c := IntToStr(d)[1];
      s := s + c;
    end;
  until (Result > 0) or (r = 0);
  if r = 0 then
  begin
    Result := length(s);
  end;

end;

const
  maxdigits = 1000;

var
  Base: int64 = 10000000000;

type
  tbigint = array[0..maxdigits div 10] of int64;

  {************* AddBig *********}


function addbig(a, b: TBigint): Tbigint;
var
  i: integer;
begin
  Result := a;
  for i := 0 to high(a) do
  begin
    Result[i] := Result[i] + b[i];
    if (i < high(a)) and (Result[i] > Base) then
    begin
      Result[i + 1] := Result[i + 1] + Result[i] div Base;
      Result[i]     := Result[i] mod Base;
    end;
  end;
end;

{********** Setval ******}


procedure setval(var n: TBigint; val: integer);
var
  i: integer;
begin
  n[0] := val;
  for i := 1 to high(n) do
    n[i] := 0;
end;

{************ DigitCount ********}


function DigitCount(n: Tbigint): integer;
var
  i: integer;
  w: int64;
begin
  Result := 0;
  for i := high(n) downto 0 do
    if n[i] > 0 then
    begin
      Result := 10 * i;
      w      := n[i];
      while w > 0 do
      begin
        w := w div 10;
        Inc(Result);

      end;
      break;
    end;
end;

{*********** DivSum *********}


function divsum(n: integer): integer;
  {Return the sum of the prpoer divisors of N}
var
  i:  integer;
  sq: integer;
begin
  Result := 1; {1 and n are always divisors}
  {add to to divisor count for all divisors up to sqrt(n)}
  sq     := trunc(sqrt(0.0 + n));
  for i := 2 to sq do
    if n mod i = 0 then
      Result := Result + i + n div i;
  {If perfect square - shouldn't have added sqrt twice}
  if sq * sq = n then
    Result := Result - sq; {perfect square - shouldn't have added sqrt twicw}
end;

{********* SumDigits **********}


function SumDigits(n: integer): integer;
  {add up the digits of n}
begin
  Result := 0;
  while n > 0 do
  begin
    Result := Result + n mod 10;
    n      := n div 10;
  end;
end;

{************ ContinuedFraction ***********}


procedure Continuedfraction(A: array of int64; const wholepart: integer;
  var numerator, denominator: int64);
      {evaluate a continued fraction using standard list format
       [t1; t2,t3,t4,t5...]}

  function continuant(A: array of int64): int64;


    {recursive calc of continuants per Knuth, Vol 2, p340}

  var
    n: integer;

  begin
    n := length(a);
    if n <= maxcontinuant then
      Result := continuants[n]
    else
    begin
      if length(A) = 0 then
        Result := 1
      else if length(A) = 1 then
        Result := a[0]
      else
        Result := A[high(A)] * continuant(slice(A, length(A) - 1)) +
          continuant(slice(A, length(A) - 2));
      Inc(maxcontinuant);
      continuants[maxcontinuant] := Result;
    end;
  end;

var
  A2: array of int64;  {reversed version of A so we can use slice function}
  i:  integer;
  x:  int64;
begin
  SetLength(A2, length(A) - 1);
  {reverse the array for passing to continuant}
  if length(a2) > 0 then
  begin
    for i := 1 to high(A) do
      A2[high(A) - i] := A[i];
    x := a[0];
    denominator := continuant(A2);
    SetLength(A2, length(A2) - 1);
    numerator := x * denominator + continuant(A2);
  end
  else
  begin
    numerator   := a[0];
    denominator := 1;
  end;
end;

{*********************** TPrimes.GetNextPrime ********************}


function TPrimes.Getnextprime(n: int64): int64;
var
  I: integer;
begin
  Result := n;
  if Result < Prime[nbrprimes] then  {it's in the table already}
  begin
    if bsprime(Result, i) then
      Result := prime[i + 1]
    else
      Result := Prime[i];
    exit;
  end;
  if (Result and 1) <> 0 then
    Inc(Result, 2)
  else
    Inc(Result);
  if isprime(Result) then
    exit;
  while Result mod 3 <> 0 do
  begin
    Inc(Result, 2);
    if isprime(Result) then
      exit;
  end;
  Dec(Result, 2);
  while True do
  begin
    Inc(Result, 4);
    if isprime(Result) then
      break;
    Inc(Result, 2);
    if isprime(Result) then
      break;
  end;
end;

{**************** TPrimes.GetPrevPrime ****************}


function TPrimes.GetPrevprime(n: int64): int64;
var
  i: integer;
begin
  Result := n;
  if Result <= Prime[nbrprimes] then  {it's in the table already}
  begin
    bsprime(Result, i);
    Result := prime[i - 1];
    exit;
  end;
  if (Result and 1) <> 0 then  {it's odd}
    Dec(Result, 2)
  else  Dec(Result); {make it odd}
  while (not isprime(Result)) do
    Dec(Result, 2);
end;

{************************* TPrimes.IsPrime *************************}


function TPrimes.Isprime(n: int64): boolean;
  {Tests for primeness and returns true or false}
var
  i: integer;
  stopval, testVal: int64;
  EarlyBreak: boolean;
begin
  Result := False;
  if n <= Prime[nbrprimes] then  {if it's prime. it's in the table already}
    Result := bsprime(n, i)
  else
  begin
    i := 1;
    EarlyBreak := False;
    stopval := trunc(sqrt(0.0 + n));
    for i := 1 to nbrprimes do
    begin
      if (n mod prime[i]) = 0 then
      begin
        EarlyBreak := True;
        break;
      end;
      if prime[i] > stopval then
      begin
        Result := True;
        break;
      end;
    end;
    if (not EarlyBreak) and (Stopval > prime[nbrprimes]) then
    begin
      TestVal := prime[nbrprimes] + 2;
      while TestVal mod 15 <> 0 do
        Dec(testval, 2);
      Dec(testval, 2);
      {check for prime mod 2,3,5}  {Increments = 4,2,4,6,2,6,4,2}

      while True do


      begin
        Inc(testval, 4);
        if (n mod testval) = 0 then
          break;
        Inc(testval, 2);
        if (n mod testval) = 0 then
          break;
        Inc(testval, 4);
        if (n mod testval) = 0 then
          break;
        Inc(testval, 6);
        if testval > stopval then
        begin
          Result := True;
          break;
        end;
        if (n mod testval) = 0 then
          break;
        Inc(testval, 2);
        if (n mod testval) = 0 then
          break;
        Inc(testval, 6);
        if (n mod testval) = 0 then
          break;
        Inc(testval, 4);
        if (n mod testval) = 0 then
          break;
        Inc(testval, 2);
        if testval > stopval then
        begin
          Result := True;
          break;
        end;
        if (n mod testval) = 0 then
          break;
      end;
    end;
  end;
end;

{************************* TPrimes.GetFactors ******************}


procedure TPrimes.Getfactors(const n: int64);
{Returns prime factors}
var
  stopval, testval: int64;
  i, LenF:   integer;
  nbr : int64;
  IsPrime:   boolean;
begin
  nbr := n;
  nbrfactors := 0;
  LenF := length(factors);
  stopval := trunc(Sqrt(0.0 + nbr)) + 1;
  i := 1;
  while (i < nbrprimes) and (Prime[i] <= stopval) do
  begin
    if nbr mod Prime[i] = 0 then {'we found a factor}
    begin
      Inc(nbrfactors);
      if nbrfactors >= LenF then
      begin
        SetLength(factors, Lenf + maxfactors);
        Lenf := Lenf + maxfactors;
      end;
      factors[nbrfactors] := Prime[i];
      nbr     := nbr div Prime[i];  {and get the quotient as the new number}
      stopval := trunc(Sqrt(0.0 + nbr));   { new stopvalue is sqrt of nbr}
    end
    else
      Inc(i);
  end;
  if (nbr > stopval) then
  begin
    testval := prime[nbrprimes] + 2;
    while TestVal mod 15 <> 0 do Dec(testval, 2);
    Dec(testval, 2);
    IsPrime := False;
    while True do
    begin
      Inc(testval, 4);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 2);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 4);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 6);
      if testval > stopval then
      begin
        IsPrime := True;
        break;
      end;
      if (n mod testval) = 0 then
        break;
      Inc(testval, 2);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 6);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 4);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 2);
      if testval > stopval then
      begin
        IsPrime := True;
        break;
      end;
      if (n mod testval) = 0 then
        break;
    end;
    if Isprime = False then
    begin
      while (nbr >= testval) and (TestVal <= stopval) do
      begin
        if nbr mod testval = 0 then
        begin
          Inc(nbrfactors);
          if nbrfactors >= LenF then
          begin
            SetLength(factors, Lenf + maxfactors);
            Lenf := Lenf + maxfactors;
          end;
          factors[nbrfactors] := TestVal;
          nbr     := nbr div TestVal;  {and get the quotient as the new number}
          stopval := trunc(Sqrt(0.0 + nbr));   { new stopvalue is sqrt of nbr}
        end
        else
          TestVal := Primes.GetNextPrime(TestVal);
      end;
    end;
  end;
  Inc(nbrfactors);
  factors[nbrfactors] := nbr;
  //  setlength(factors,nbrfactors+1); {should we tidy up???}
end;

{************************* GetCanonicalFactors ******************}


procedure TPrimes.GetCanonicalfactors(const n: int64);
{Returns prime factors}
var
  stopval,testval: int64;
  i:    integer;
  nbr:  int64;
  nbrc: integer;
  isprime:boolean;
begin
  nbr := n;
  nbrc := 0;
  stopval := trunc(Sqrt(0.0 + nbr));
  i := 1;
  while (i < nbrprimes) and (Prime[i] <= stopval) do
  begin
    if nbr mod Prime[i] = 0 then {'we found a factor}
    begin
      if (nbrc > 0) and (canonicalfactors[nbrc].x = prime[i]) then
        Inc(canonicalfactors[nbrc].y)
      else
      begin
        Inc(nbrc);
        if (nbrc >= length(canonicalfactors)) then
          SetLength(canonicalfactors, length(canonicalfactors) + maxfactors);
        with canonicalfactors[nbrc] do
        begin
          x := prime[i];
          y := 1;
        end;
      end;
      nbr     := nbr div Prime[i];  {and get the quotient as the new number}
      stopval := trunc(Sqrt(0.0 + nbr));   { new stopvalue is sqrt of nbr}
    end
    else
      Inc(i);
  end;

  if (nbr > stopval) then


  begin
    testval := prime[nbrprimes] + 2;
    while TestVal mod 15 <> 0 do Dec(testval, 2);
    Dec(testval, 2);
    IsPrime := False;
    while True do
    begin
      Inc(testval, 4);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 2);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 4);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 6);
      if testval > stopval then
      begin
        IsPrime := True;
        break;
      end;
      if (n mod testval) = 0 then
        break;
      Inc(testval, 2);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 6);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 4);
      if (n mod testval) = 0 then
        break;
      Inc(testval, 2);
      if testval > stopval then
      begin
        IsPrime := True;
        break;
      end;
      if (n mod testval) = 0 then
        break;
    end;
    if Isprime = False then
    begin
      while (nbr >= testval) and (TestVal <= stopval) do
      begin
        if nbr mod testval = 0 then
        begin
           if (nbrc > 0) and (canonicalfactors[nbrc].x = testval)
           then  inc(canonicalfactors[nbrc].y)
          else
          begin
            Inc(nbrc);
            if (nbrc >= length(canonicalfactors))
            then SetLength(canonicalfactors, length(canonicalfactors) + maxfactors);
            with canonicalfactors[nbrc] do
            begin
              x := testval;
              y := 1;
            end;
          end;
          nbr     := nbr div TestVal;  {and get the quotient as the new number}
          stopval := trunc(Sqrt(0.0 + nbr));   { new stopvalue is sqrt of nbr}
        end
        else
          TestVal := Primes.GetNextPrime(TestVal);
      end;
    end;
   end;
  if (nbrc > 0) and (canonicalfactors[nbrc].x = nbr) then
    Inc(canonicalfactors[nbrc].y)
  else
  begin
    Inc(nbrc);
    if (nbrc >= length(canonicalfactors)) then
      SetLength(canonicalfactors, length(canonicalfactors) + maxfactors);
    with canonicalfactors[nbrc] do
    begin
      x := nbr;
      y := 1;
    end;
  end;
  nbrcanonicalfactors := nbrc;
end;

{************ GetNbrDivisors ***********}


function TPrimes.Getnbrdivisors(n: int64): integer;
var
  i: integer;
begin
  getcanonicalfactors(n);
  Result := 1;
  for i := 1 to nbrcanonicalfactors do
    Result := Result * (canonicalfactors[i].y + 1);
  nbrdivisors := Result;
end;

{*********** GetDivisors ************}


procedure Tprimes.GetDivisors(const n: int64);
{------- Getit ----------}
  procedure getit(num: integer; d: int64);
  {recursive procedure to iterate through prime factors & powers to get all divisors}
  var
    i:    integer;
    newd: int64;
  begin
    with canonicalfactors[num] do
      for i := 0 to y do
      begin
        if num = 0 then
        begin
          newd := intpower(x, i);
        end
        else
          newd := d * intpower(x, i);
        if num < nbrcanonicalfactors then
          getit(num + 1, newd)
        else
        begin
          Inc(nbrdiv);
          divisors[nbrdiv] := newd;
        end;
      end;
  end;

var
  i, j: integer;
  temp: int64;
begin
  SetLength(divisors, getnbrdivisors(n) + 1);
  nbrdiv := 0;
  temp   := 1;
  getit(0, temp);
  if nbrdivisors <> nbrdiv then
    ShowMessage('Divisors calc error');
  {let's sort the divisors}
  for i := 1 to nbrdivisors - 1 do
    for j := i + 1 to nbrdivisors do
      if divisors[i] > divisors[j] then
      begin
        temp := divisors[i];
        divisors[i] := divisors[j];
        divisors[j] := temp;
      end;
end;

{********************Primes.Create ***********************}
constructor TPrimes.Create;
  {Calculate primes less than maxval using Sieve of Eratosthenes}
var
  workprimes: array of integer;
  i, j: integer;
  nbr, stopval: integer;
begin
  inherited;
  SetLength(workprimes, maxprimes + 1);
  SetLength(Prime, maxprimes + 1);
  {showmessage('Max prime is '+inttostr(high(x)));}
  SetLength(factors, maxfactors + 1);
  {initialize array starting with 2,3,4,5, etc.}
  for i := 1 to maxprimes do
    workprimes[i] := i + 1;

  { now go through the array}


  for i := 1 to maxprimes div 2 do
  begin
    {if # is greater than 0, then it's a prime}
    if workprimes[i] > 0 then
    begin
      {go through the rest of the array zeroing out all multiples of this prime}
      j := i + workprimes[i];
      while j <= maxprimes do
      begin
        workprimes[j] := 0;
        j := j + workprimes[i];
      end;
    end;
  end;
  prime[0]  := -1;
  {now "pack" the primes back to the beginning positions of the array}
  nbrprimes := 0;
  for i := 1 to maxprimes do
  begin
    if workprimes[i] > 0 then
    begin
      Inc(nbrprimes);
      Prime[nbrprimes] := workprimes[i];
    end;
  end;

  {Now calculate the rest of the primes up to maxval}


  { the hard way (trial and error)}
  stopval := trunc(Sqrt(0.0 + maxval)); {largest prime factor we'll need}
  nbr     := Prime[nbrprimes];
  while (nbr <= stopval) do
  begin
    nbr := Getnextprime(nbr);
    Inc(nbrprimes);

    if nbrprimes >= length(Prime) then


      SetLength(Prime, length(Prime) + maxprimes);
    Prime[nbrprimes] := nbr;
  end;
  SetLength(prime, nbrprimes + 1); {release unused memory at end of array}
end;

{************* Primes.destroy ************}


destructor TPrimes.Destroy;
begin
  SetLength(prime, 0);   {release memory}
  SetLength(factors, 0); {ditto}
  inherited;
end;

{************ TPrimes.MaxPrimeInTable ***********}


function TPrimes.MaxPrimeInTable: int64;
begin
  Result := prime[high(prime)];
end;

{************** TPrimes.BSPrime *************}


function TPrimes.BSPrime(const n: int64; var index: integer): boolean;
{Binary search to see if given prime, n, is in the table}
var
  nbr, res: int64;
  First, last, pivot: integer;
begin
  Result := False;
  nbr    := n;
  index  := 0;
  First  := 1;
  last   := nbrprimes;
  repeat
    pivot := (First + last) div 2;
    res   := prime[pivot] - nbr;
    if res = 0 then
    begin
      index  := pivot;
      Result := True;
      exit;
    end
    else if res < 0 then
      First := pivot + 1
    else
      last  := pivot - 1;
  until last < First;
  index := First;
end;

{************** GetNthPrime *************}


function TPrimes.GetNthPrime(const n: integer): int64;
begin
  if (n >= 1) and (n <= nbrprimes) then
    Result := Prime[n]
  else
    Result := -1;
end;

initialization
  Primes := TPrimes.Create;

finalization
  Primes.Destroy;

end.

You might also like