diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 71cbad7..74c9eb4 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -161,6 +161,10 @@ + + + + diff --git a/UBinomialCoefficients.pas b/UBinomialCoefficients.pas new file mode 100644 index 0000000..1d4e875 --- /dev/null +++ b/UBinomialCoefficients.pas @@ -0,0 +1,93 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2024 Stefan Müller + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . +} + +unit UBinomialCoefficients; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Generics.Collections; + +type + TCardinalArray = array of Cardinal; + TCardinalArrays = specialize TList; + + { TBinomialCoefficientCache } + + TBinomialCoefficientCache = class + private + FCache: TCardinalArrays; + procedure AddRow; + public + constructor Create; + destructor Destroy; override; + function Get(const AN, AK: Cardinal): Cardinal; + function GetCachedRowsCount: Cardinal; + end; + +implementation + +{ TBinomialCoefficientCache } + +procedure TBinomialCoefficientCache.AddRow; +var + row: TCardinalArray; + i: Cardinal; +begin + SetLength(row, FCache.Count + 1); + row[0] := 1; + if FCache.Count > 0 then + begin + row[FCache.Count] := 1; + for i := 1 to FCache.Count - 1 do + row[i] := FCache.Last[i - 1] + FCache.Last[i]; + end; + FCache.Add(row); +end; + +constructor TBinomialCoefficientCache.Create; +begin + FCache := TCardinalArrays.Create; +end; + +destructor TBinomialCoefficientCache.Destroy; +begin + FCache.Free; + inherited Destroy; +end; + +function TBinomialCoefficientCache.Get(const AN, AK: Cardinal): Cardinal; +var + i: Cardinal; +begin + if AN < AK then + raise ERangeError.Create('Cannot calculate binomial coefficient "n choose k" with k larger than n.'); + + for i := FCache.Count to AN do + AddRow; + Result := FCache[AN][AK]; +end; + +function TBinomialCoefficientCache.GetCachedRowsCount: Cardinal; +begin + Result := FCache.Count; +end; + +end. + diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi index 0d0c019..7ee1927 100644 --- a/tests/AdventOfCodeFPCUnit.lpi +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -152,6 +152,10 @@ + + + + diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr index 2c2a60e..4b270c1 100644 --- a/tests/AdventOfCodeFPCUnit.lpr +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -10,7 +10,7 @@ uses UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases, UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases, UNeverTellMeTheOddsTestCases, USnowverloadTestCases, UBigIntTestCases, UPolynomialTestCases, - UPolynomialRootsTestCases; + UPolynomialRootsTestCases, UBinomialCoefficientsTestCases; {$R *.res} diff --git a/tests/UBinomialCoefficientsTestCases.pas b/tests/UBinomialCoefficientsTestCases.pas new file mode 100644 index 0000000..4de87b4 --- /dev/null +++ b/tests/UBinomialCoefficientsTestCases.pas @@ -0,0 +1,138 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2024 Stefan Müller + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . +} + +unit UBinomialCoefficientsTestCases; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, UBinomialCoefficients; + +type + + { TBinomialCoefficientsTestCase } + + TBinomialCoefficientsTestCase = class(TTestCase) + private + FBinomialCoefficientCache: TBinomialCoefficientCache; + procedure RunRangeError; + procedure AssertEqualsCalculation(const AN, AK, AExpected: Cardinal); + procedure AssertEqualsCachedRowsCount(const AExpected: Cardinal); + protected + procedure SetUp; override; + procedure TearDown; override; + published + procedure TestZeroChooseZero; + procedure TestNChooseZero; + procedure TestNChooseN; + procedure TestNChooseK; + procedure TestCombined; + procedure TestFullRow; + procedure TestRangeError; + end; + +implementation + +{ TBinomialCoefficientsTestCase } + +procedure TBinomialCoefficientsTestCase.RunRangeError; +begin + FBinomialCoefficientCache.Get(1, 5); +end; + +procedure TBinomialCoefficientsTestCase.AssertEqualsCalculation(const AN, AK, AExpected: Cardinal); +begin + AssertEquals('Unexpected calculation result', AExpected, FBinomialCoefficientCache.Get(AN, AK)); +end; + +procedure TBinomialCoefficientsTestCase.AssertEqualsCachedRowsCount(const AExpected: Cardinal); +begin + AssertEquals('Unexpected cached rows count', AExpected, FBinomialCoefficientCache.GetCachedRowsCount); +end; + +procedure TBinomialCoefficientsTestCase.SetUp; +begin + FBinomialCoefficientCache := TBinomialCoefficientCache.Create; +end; + +procedure TBinomialCoefficientsTestCase.TearDown; +begin + FBinomialCoefficientCache.Free; +end; + +procedure TBinomialCoefficientsTestCase.TestZeroChooseZero; +begin + AssertEqualsCalculation(0, 0, 1); + AssertEqualsCachedRowsCount(1); +end; + +procedure TBinomialCoefficientsTestCase.TestNChooseZero; +begin + AssertEqualsCalculation(15, 0, 1); + AssertEqualsCachedRowsCount(16); +end; + +procedure TBinomialCoefficientsTestCase.TestNChooseN; +begin + AssertEqualsCalculation(11, 11, 1); + AssertEqualsCachedRowsCount(12); +end; + +procedure TBinomialCoefficientsTestCase.TestNChooseK; +begin + AssertEqualsCalculation(8, 3, 56); + AssertEqualsCachedRowsCount(9); +end; + +procedure TBinomialCoefficientsTestCase.TestCombined; +begin + AssertEqualsCalculation(5, 1, 5); + AssertEqualsCachedRowsCount(6); + AssertEqualsCalculation(8, 4, 70); + AssertEqualsCachedRowsCount(9); + AssertEqualsCalculation(3, 1, 3); + AssertEqualsCachedRowsCount(9); +end; + +procedure TBinomialCoefficientsTestCase.TestFullRow; +begin + AssertEqualsCalculation(5, 0, 1); + AssertEqualsCachedRowsCount(6); + AssertEqualsCalculation(5, 1, 5); + AssertEqualsCachedRowsCount(6); + AssertEqualsCalculation(5, 2, 10); + AssertEqualsCachedRowsCount(6); + AssertEqualsCalculation(5, 3, 10); + AssertEqualsCachedRowsCount(6); + AssertEqualsCalculation(5, 4, 5); + AssertEqualsCachedRowsCount(6); + AssertEqualsCalculation(5, 5, 1); + AssertEqualsCachedRowsCount(6); +end; + +procedure TBinomialCoefficientsTestCase.TestRangeError; +begin + AssertException(ERangeError, @RunRangeError); +end; + +initialization + + RegisterTest('Helper', TBinomialCoefficientsTestCase); +end. +