Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
delphidabbler opened this issue Jul 20, 2023 · 8 comments
Open
Assignees
Labels
accepted Issue will be actioned enhancement New feature or request

Comments

@delphidabbler
Copy link
Owner

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 delphidabbler self-assigned this Jul 20, 2023
@delphidabbler
Copy link
Owner Author

delphidabbler commented Jul 20, 2023

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 delphidabbler changed the title Add version of DigitCount that works for all bases Add versions of DigitCount & DigitSum that work for all bases Jul 20, 2023
@delphidabbler
Copy link
Owner Author

DigitSumBase

Screenshot_20230720-151207_Chrome

Source: Wikipedia

@delphidabbler
Copy link
Owner Author

delphidabbler commented Jul 21, 2023

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
Copy link
Owner Author

delphidabbler commented Jul 21, 2023

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 delphidabbler added enhancement New feature or request accepted Issue will be actioned labels Jul 21, 2023
@delphidabbler
Copy link
Owner Author

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
Copy link
Owner Author

delphidabbler commented Jul 24, 2023

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
Copy link
Owner Author

delphidabbler commented Jul 24, 2023

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
Copy link
Owner Author

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;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted Issue will be actioned enhancement New feature or request
Projects
Status: Accepted
Development

No branches or pull requests

1 participant