Skip to content

Commit fb53ba7

Browse files
committed
Merge branch 'feature/39-add-digitpowersum-routine' into develop
Fixes #39
2 parents cfadac8 + 6b31fb0 commit fb53ba7

File tree

5 files changed

+163
-11
lines changed

5 files changed

+163
-11
lines changed

collection/634.dat

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
function PowNZN(const X: Integer; const N: Cardinal): Int64;
22
var
33
I: Integer;
4+
OverflowGuard: Int64;
45
begin
5-
if (X = 0) and (N > 0) then
6-
Exit(0);
7-
if X = 1 then
6+
if N = 0 then
7+
// IEEE: pown(x, 0) is 1, even when X is zero
88
Exit(1);
9+
if X = 0 then
10+
// pown(0, n) = 0, for all positive n
11+
Exit(0);
12+
OverflowGuard := High(Int64) div Abs(X);
913
Result := 1;
1014
for I := 1 to N do
15+
begin
16+
if OverflowGuard < Abs(Result) then
17+
raise SysUtils.EOverflow.CreateFmt(
18+
'Overflow calculating %d to the power %d', [X, N]
19+
);
1120
Result := Result * X;
21+
end;
1222
end;

collection/661.dat

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function DigitPowerSum(N: Integer; const Base: Byte; const Exponent: Byte):
2+
Int64;
3+
var
4+
SignOfN: Math.TValueSign;
5+
Digit: Integer;
6+
PowerDigit: Int64;
7+
begin
8+
if Base < 2 then
9+
raise SysUtils.EArgumentException.Create(
10+
'Base must be in the range 2..255'
11+
);
12+
if N = 0 then
13+
Exit(0);
14+
SignOfN := Math.Sign(N);
15+
N := Abs(N);
16+
Result := 0;
17+
repeat
18+
Digit := N mod Base;
19+
PowerDigit := PowNZN(Digit, Exponent);
20+
if High(Int64) - PowerDigit < Abs(Result) then
21+
raise SysUtils.EOverflow.Create('Overflow calculating digit power sum');
22+
Result := Result + PowerDigit;
23+
N := N div Base;
24+
until N = 0;
25+
if SignOfN = Math.NegativeValue then
26+
Result := -1 * Result;
27+
end;

collection/maths.ini

+18-3
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ Extra="<p>Sums of digits of negative numbers are negative, for example <mono>Dig
620620
TestInfo=advanced
621621
AdvancedTest.Level=unit-tests
622622
AdvancedTest.URL="https://github.com/delphidabbler/code-snippets/tree/master/tests/Cat-Maths"
623-
SeeAlso=DigitSumBase
623+
SeeAlso=DigitSumBase,DigitPowerSum
624624
Snip=418.dat
625625
Delphi2=N
626626
Delphi3=N
@@ -1391,8 +1391,9 @@ Delphi11A=Y
13911391
Delphi12A=Y
13921392

13931393
[PowNZN]
1394-
DescEx="<p>Raises integer <var>X</var> to non-negative integer power <var>N</var>.</p>"
1394+
DescEx="<p>Raises integer <var>X</var> to non-negative integer power <var>N</var>.</p><p>If the result is too large to be represented as an <var>Int64</var> value then an <var>EOverflow</var> exception is raised.</p>"
13951395
Extra="<p>Returns an <var>integer</var> value because the power <var>N</var> is non-negative which guarantees that the result is integral.</p><p>Based on IEEE standard 754-2008 for Floating-Point Arithmetic, page 44, but which restricts <var>X</var> to an integer and <var>N</var> to a non-negative integer.</p>"
1396+
Units=SysUtils
13961397
SeeAlso=Pow,PowN,PowNZZ
13971398
TestInfo=advanced
13981399
AdvancedTest.Level=unit-tests
@@ -1762,7 +1763,7 @@ DisplayName="DigitSumBase"
17621763
DescEx="<p>Calculates the sum of all the digits of integer <var>N</var> when epxressed in base <var>Base</var>. The returned value has the same sign as <var>N</var>.</p><p>Bases up to 255 are supported. If <var>Base</var> &lt; 2 then an <var>EArgumentException</var> exception is raised.</p>"
17631764
Kind=routine
17641765
Units=SysUtils,Math
1765-
SeeAlso=DigitSum
1766+
SeeAlso=DigitSum,DigitPowerSum
17661767
TestInfo=advanced
17671768
AdvancedTest.Level=unit-tests
17681769
AdvancedTest.URL="https://github.com/delphidabbler/code-snippets/tree/master/tests/Cat-Maths"
@@ -1797,3 +1798,17 @@ AdvancedTest.URL="https://github.com/delphidabbler/code-snippets/tree/master/tes
17971798
Snip=659.dat
17981799
DelphiXE=Y
17991800
Delphi12A=Y
1801+
1802+
[DigitPowerSum]
1803+
DisplayName=DigitPowerSum
1804+
DescEx="<p>Calculates the sum of all the digits of integer <var>N</var> in base <var>Base</var> where each digit is raised to the power <var>Exponent</var>. The returned value has the same sign as <var>N</var>.</p><p>If the result is too large to be represented as an <var>Int64</var> value then an <var>EOverflow</var> exception is raised.</p><p>Bases up to 255 are supported. If <var>Base</var> &lt;= 2 then an <var>EArgumentException</var> exception is raised.</p>"
1805+
Kind=routine
1806+
Units=SysUtils,Math
1807+
Depends=PowNZN
1808+
SeeAlso=DigitSum,DigitSumBase
1809+
TestInfo=advanced
1810+
AdvancedTest.Level=unit-tests
1811+
AdvancedTest.URL="https://github.com/delphidabbler/code-snippets/tree/master/tests/Cat-Maths"
1812+
Snip=661.dat
1813+
DelphiXE=Y
1814+
Delphi12A=Y

tests/Cat-Maths/TestUMathsCatSnippets.pas

+38-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ TestMathsCatSnippets = class(TTestCase)
2020
procedure TestWeightedArithMean_Double_Except4;
2121
procedure TestDigitSumBase_Except;
2222
procedure TestDigitsOf_ArgExcept;
23+
procedure TestPowNZN_EOverflow;
24+
procedure TestDigitPowerSum_EOverflow;
25+
procedure TestDigitPowerSum_EArgumentException;
2326
function EqualArrays(const Left, Right: TBytes): Boolean;
2427
function ReverseArray(const A: TBytes): TBytes;
2528
published
@@ -51,7 +54,7 @@ TestMathsCatSnippets = class(TTestCase)
5154
procedure TestMaxOfArray_Single;
5255
procedure TestMaxOfArray_Double;
5356
procedure TestMaxOfArray_Extended;
54-
procedure TestPowNZN;
57+
procedure TestPowNZN; // required by DigitPowerSum
5558
procedure TestPowNZZ;
5659
procedure TestPowN;
5760
procedure TestArraySum_Single;
@@ -77,6 +80,7 @@ TestMathsCatSnippets = class(TTestCase)
7780
procedure TestDigitCountBase;
7881
procedure TestDigitSumBase;
7982
procedure TestDigitsOf;
83+
procedure TestDigitPowerSum;
8084
end;
8185

8286
implementation
@@ -416,6 +420,33 @@ procedure TestMathsCatSnippets.TestDigitCountR;
416420
CheckEquals(5, DigitCountR(-12345), 'DigitCountR(-12345)');
417421
end;
418422

423+
procedure TestMathsCatSnippets.TestDigitPowerSum;
424+
begin
425+
CheckEquals(35, DigitPowerSum(135, 10, 2), '#1');
426+
CheckEquals(0, DigitPowerSum(0, 8, 5), '#2');
427+
CheckEquals(3, DigitPowerSum(510, 10, 0), '#3');
428+
CheckEquals(30, DigitPowerSum($FF, 16, 1), '#4');
429+
CheckEquals(12613, DigitPowerSum(1685237180, 10, 4), '#5');
430+
CheckEquals(77907, DigitPowerSum(1685237180 {6472ADBC hex}, 16, 4), '#6');
431+
CheckEquals(6740, DigitPowerSum(1685237180 {14434526674 oct}, 8, 4), '#7');
432+
CheckEquals(-6740, DigitPowerSum(-1685237180 {14434526674 oct}, 8, 4), '#8');
433+
CheckEquals(17, DigitPowerSum(1685237180 {1100100011100101010110110111100 bin}, 2, 4), '#9');
434+
CheckEquals(2409140909625644483, DigitPowerSum(MaxInt {C87E66B7 base 15}, 15, 16), '#10');
435+
CheckException(TestDigitPowerSum_EArgumentException, EArgumentException, 'EArgumentException');
436+
CheckException(TestDigitPowerSum_EOverflow, EOverflow, 'EOverflow');
437+
// EOverflow can also be raised by PowNZN, not tested here
438+
end;
439+
440+
procedure TestMathsCatSnippets.TestDigitPowerSum_EArgumentException;
441+
begin
442+
DigitPowerSum(42, 1, 2); // Base = 1 => EArgumentException
443+
end;
444+
445+
procedure TestMathsCatSnippets.TestDigitPowerSum_EOverflow;
446+
begin
447+
DigitPowerSum(88888888, 10, 20); // overflows High(Int64) by 1
448+
end;
449+
419450
procedure TestMathsCatSnippets.TestDigitsOf;
420451
var
421452
E: TBytes;
@@ -930,6 +961,12 @@ procedure TestMathsCatSnippets.TestPowNZN;
930961
CheckEquals(10000, PowNZN(10, 4), 'PowNZN(10,2)');
931962
CheckEquals(-1000, PowNZN(-10, 3), 'PowNZN(-10,3)');
932963
CheckEquals(10000, PowNZN(-10, 4), 'PowNZN(-10,4)');
964+
CheckException(TestPowNZN_EOverflow, EOverflow, 'EOverflow');
965+
end;
966+
967+
procedure TestMathsCatSnippets.TestPowNZN_EOverflow;
968+
begin
969+
PowNZN(2, 63);
933970
end;
934971

935972
procedure TestMathsCatSnippets.TestPowNZZ;

tests/Cat-Maths/UMathsCatSnippets.pas

+67-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* The unit is copyright © 2005-2024 by Peter Johnson & Contributors and is
77
* licensed under the MIT License (https://opensource.org/licenses/MIT).
88
*
9-
* Generated on : Tue, 07 Jan 2025 13:13:01 GMT.
9+
* Generated on : Thu, 09 Jan 2025 10:55:06 GMT.
1010
* Generated by : DelphiDabbler CodeSnip Release 4.24.0.
1111
*
1212
* The latest version of CodeSnip is available from the CodeSnip GitHub project
@@ -129,6 +129,18 @@ function DigitCountBase(N: Int64; const Base: Byte): Cardinal;
129129
}
130130
function DigitCountR(AValue: Int64): Integer;
131131

132+
{
133+
Calculates the sum of all the digits of integer N in base Base where each
134+
digit is raised to the power Exponent. The returned value has the same sign as
135+
N.
136+
If the result is too large to be represented as an Int64 value then an
137+
EOverflow exception is raised.
138+
Bases up to 255 are supported. If Base <= 2 then an EArgumentException
139+
exception is raised.
140+
}
141+
function DigitPowerSum(N: Integer; const Base: Byte; const Exponent: Byte):
142+
Int64;
143+
132144
{
133145
Returns an array containing the digits of integer N when expressed in base
134146
Base. The array is ordered with the least significant digit first.
@@ -358,6 +370,8 @@ function PowN(const X: Extended; const N: Integer): Extended;
358370

359371
{
360372
Raises integer X to non-negative integer power N.
373+
If the result is too large to be represented as an Int64 value then an
374+
EOverflow exception is raised.
361375
}
362376
function PowNZN(const X: Integer; const N: Cardinal): Int64;
363377

@@ -894,6 +908,43 @@ function DigitCountR(AValue: Int64): Integer;
894908
Result := 1 + DigitCountR(AValue div 10)
895909
end;
896910

911+
{
912+
Calculates the sum of all the digits of integer N in base Base where each
913+
digit is raised to the power Exponent. The returned value has the same sign as
914+
N.
915+
If the result is too large to be represented as an Int64 value then an
916+
EOverflow exception is raised.
917+
Bases up to 255 are supported. If Base <= 2 then an EArgumentException
918+
exception is raised.
919+
}
920+
function DigitPowerSum(N: Integer; const Base: Byte; const Exponent: Byte):
921+
Int64;
922+
var
923+
SignOfN: Math.TValueSign;
924+
Digit: Integer;
925+
PowerDigit: Int64;
926+
begin
927+
if Base < 2 then
928+
raise SysUtils.EArgumentException.Create(
929+
'Base must be in the range 2..255'
930+
);
931+
if N = 0 then
932+
Exit(0);
933+
SignOfN := Math.Sign(N);
934+
N := Abs(N);
935+
Result := 0;
936+
repeat
937+
Digit := N mod Base;
938+
PowerDigit := PowNZN(Digit, Exponent);
939+
if High(Int64) - PowerDigit < Abs(Result) then
940+
raise SysUtils.EOverflow.Create('Overflow calculating digit power sum');
941+
Result := Result + PowerDigit;
942+
N := N div Base;
943+
until N = 0;
944+
if SignOfN = Math.NegativeValue then
945+
Result := -1 * Result;
946+
end;
947+
897948
{
898949
Returns an array containing the digits of integer N when expressed in base
899950
Base. The array is ordered with the least significant digit first.
@@ -1610,18 +1661,30 @@ function PowN(const X: Extended; const N: Integer): Extended;
16101661

16111662
{
16121663
Raises integer X to non-negative integer power N.
1664+
If the result is too large to be represented as an Int64 value then an
1665+
EOverflow exception is raised.
16131666
}
16141667
function PowNZN(const X: Integer; const N: Cardinal): Int64;
16151668
var
16161669
I: Integer;
1670+
OverflowGuard: Int64;
16171671
begin
1618-
if (X = 0) and (N > 0) then
1619-
Exit(0);
1620-
if X = 1 then
1672+
if N = 0 then
1673+
// IEEE: pown(x, 0) is 1, even when X is zero
16211674
Exit(1);
1675+
if X = 0 then
1676+
// pown(0, n) = 0, for all positive n
1677+
Exit(0);
1678+
OverflowGuard := High(Int64) div Abs(X);
16221679
Result := 1;
16231680
for I := 1 to N do
1681+
begin
1682+
if OverflowGuard < Abs(Result) then
1683+
raise SysUtils.EOverflow.CreateFmt(
1684+
'Overflow calculating %d to the power %d', [X, N]
1685+
);
16241686
Result := Result * X;
1687+
end;
16251688
end;
16261689

16271690
{

0 commit comments

Comments
 (0)