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.
+