delphidabbler / code-snippets

Collections of reusable code snippets, mainly in Pascal.
MIT License
27 stars 2 forks source link

Add versions of `DigitCount` & `DigitSum` that work for all bases #17

Open delphidabbler opened 1 year ago

delphidabbler commented 1 year ago

If n ∈ ℕ (n ≠ 0) and b is the number base (b>1) then the number of digits, d, of n is

d = ⌊logb(n)⌋ + 1
delphidabbler commented 1 year ago
function DigitCountBase(X: Int64; Base: Byte): Cardinal:
begin
  Assert(Base > 1);
  if X <> 0 then
    Result := Math.Floor(Math.LogN(Base, Abs(X))) + 1
  else
    Result := 1;
end;
delphidabbler commented 1 year ago

DigitSumBase

Screenshot_20230720-151207_Chrome

Source: Wikipedia

delphidabbler commented 1 year ago
function DigitSumBase(X: Int64; Base: Byte): Integer;
begin
  Assert(Base > 1);
  if X = 0 then
    Exit(0);
  var SignOfX := Math.Sign(X);
  X := Abs(X);
  var K := Math.Floor(Math.LogN(X));
  Result := 0;
  for var I := 0 to K do
  begin
    var BToPowerI := PowNZN9(Base, I);
    var BToPowerIPlusOne := PowNZN9(Base, I + 1);
    var D := ((X mod BToPowerIPlusOne) - (X mod BToPowerI)) div BToPowerI;
    Result := Result + D;
  end;
  if SignOfX < 0 then
    Result : -1 * Result;
end;
delphidabbler commented 1 year ago
function DigitArray(N: UInt64; Base: TByte): TArray<Byte>;
begin
  Assert(Base > 1);
  SetLength(Result, DigitCountBase(N, Base));
  if N > 0 then
    for I := 0 to High(Result) do
    begin
      var BToPowerI := PowNZN9(Base, I);
      var BToPowerIPlusOne := PowNZN9(Base, I + 1);
      var Result[I] := ((X mod BToPowerIPlusOne) - (X mod BToPowerI)) div BToPowerI;
    end
  else
    Result[0] := 0;
end;
delphidabbler commented 1 year ago

Alternative DigitSumBase that should be quicker:

function DigitSumBase(X: Int64; Base: Byte): Integer;
begin
  Assert(Base > 1);
  if X = 0 then
    Exit(0);
  var SignOfX := Math.Sign(X);
  X := Abs(X);
  var K := Math.Floor(Math.LogN(X));
  Result := 0;
  var BToPowerI := 1; // B to power I when I = 0
  for var I := 0 to K do
  begin
    var BToPowerIPlus1 := Base * BToPowerI;
    var D := ((X mod BToPowerIPlus1) - (X mod BToPowerI)) div BToPowerI;
    Result := Result + D;
    BToPowerI := BToPowerIPlus1;
  end;
  if SignOfX < 0 then
    Result : -1 * Result;
end;
delphidabbler commented 1 year ago

Alternative DigitArray that should be quicker:

function DigitArray(N: UInt64; Base: TByte): TArray<Byte>;
begin
  Assert(Base > 1);
  SetLength(Result, DigitCountBase(N, Base));
  if N > 0 then
  begin
    var BToPowerI := 1; // B to power I when I = 0 
    for I := 0 to High(Result) do
    begin
      var BToPowerIPlus1 := Base * BToPowerI;
      Result[I] := ((X mod BToPowerIPlus1) - (X mod BToPowerI)) div BToPowerI;
      BToPowerI := BToPowerIPlus1;
    end;
  end
  else
    Result[0] := 0;
end;
delphidabbler commented 1 year ago

Can have overloads of all above functions for UInt64 parameter that are same except don't need to worry about sign.

In fact the above functions could be rewritten to handle sign then call unsigned overload. E.g.:

function DigitSumBase(const X: UInt64; const Base: Byte): Cardinal; overload;
begin
  Assert(Base > 1);
  if X = 0 then
    Exit(0);
  var K := Math.Floor(Math.LogN(X)); // digit count - 1
  Result := 0;
  var BToPowerI := 1; // B to power I when I = 0
  for var I := 0 to K do
  begin
    var BToPowerIPlus1 := Base * BToPowerI;
    var D := ((X mod BToPowerIPlus1) - (X mod BToPowerI)) div BToPowerI;
    Result := Result + D;
    BToPowerI := BToPowerIPlus1;
  end;
end;
function DigitSumBase(X: Int64; Base: Byte): Integer; overload;
begin
  Assert(Base > 1);
  // safe to convert Cardinal to Integer since max possible Result is much lower than MaxInt
  Result := Integer(DigitSumBase(UInt64(Abs(X)), Base));
  if X < 0 then
    Result : -1 * Result;
end;
delphidabbler commented 1 year ago

Version of DigitSumBase that raises each digit to given natural number power.

function DigitPowerSum(X: UInt64; Base: Byte; Exponent: Cardinal): Cardinal;
begin
  Assert(Base > 1);
  if X = 0 then
    Exit(0);
  var K := Math.Floor(Math.LogN(X)); // digit count - 1
  Result := 0;
  var BToPowerI := 1; // B to power I when I = 0
  for var I := 0 to K do
  begin
    var BToPowerIPlus1 := Base * BToPowerI;
    var D := ((X mod BToPowerIPlus1) - (X mod BToPowerI)) div BToPowerI;
    Result := Result + PowNZN(D, Exponent);
    BToPowerI := BToPowerIPlus1;
  end;
end;
function DigitPowerSum(X: Int64; Base: Byte; Exponent: Cardinal): Integer; overload;
begin
  Assert(Base > 1);
  var UResult :≈ DigitSumBase(UInt64(Abs(X)), Base, Exponent);
  if UResult > MaxInt then
    raise EOutOfRange('DigitPowerSum: Result exceeds MaxInt');
  Result := Integer(UResult);
  if X < 0 then
    Result : -1 * Result;
end;