From fb2f8137017acf5b8da5e11ef554cb281d7fbe2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Sat, 9 Nov 2024 00:41:49 +0100 Subject: [PATCH] Added MultiIndexEnumerator --- AdventOfCode.lpi | 4 + UMultiIndexEnumerator.pas | 160 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 UMultiIndexEnumerator.pas diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 2a9af4b..71cbad7 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -157,6 +157,10 @@ + + + + diff --git a/UMultiIndexEnumerator.pas b/UMultiIndexEnumerator.pas new file mode 100644 index 0000000..ca02c55 --- /dev/null +++ b/UMultiIndexEnumerator.pas @@ -0,0 +1,160 @@ +{ + 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 UMultiIndexEnumerator; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils; + +type + TIndexArray = array of Integer; + + TIndexValidationResult = (ivrValid, ivrSkip, ivrBacktrack); + + TEnumerableMultiIndexStrategy = class; + + { TMultiIndexEnumerator } + + TMultiIndexEnumerator = class(TInterfacedObject, specialize IEnumerator) + private + FStrategy: TEnumerableMultiIndexStrategy; + FCurrent: TIndexArray; + FMustInit: Boolean; + function UpdateArray(const AInit: Boolean): Boolean; + public + constructor Create(const AStrategy: TEnumerableMultiIndexStrategy); + function GetCurrent: TIndexArray; + function MoveNext: Boolean; + procedure Reset; + property Current: TIndexArray read GetCurrent; + end; + + { TEnumerableMultiIndexStrategy } + + TEnumerableMultiIndexStrategy = class(TInterfacedObject, specialize IEnumerable) + public + function GetEnumerator: specialize IEnumerator; + function GetCardinality: Integer; virtual; abstract; + function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer; + out AStartIndexValue: Integer): Boolean; virtual; abstract; + function ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): + TIndexValidationResult; virtual; abstract; + end; + +implementation + +{ TMultiIndexEnumerator } + +function TMultiIndexEnumerator.UpdateArray(const AInit: Boolean): Boolean; +var + i, initialized: Integer; + r: TIndexValidationResult; +begin + if AInit then + begin + i := 0; + initialized := -1; + end + else begin + i := Length(FCurrent) - 1; + initialized := i; + end; + + while i < Length(FCurrent) do + begin + if initialized < i then + begin + // Checks whether start index value can be set, and backtracks or aborts if not. + if not FStrategy.TryGetStartIndexValue(FCurrent, i, FCurrent[i]) then + if i > 0 then + begin + Dec(i); + Continue; + end + else begin + Result := False; + Exit; + end + end + else + // Sets next candidate for current index value. + Inc(FCurrent[i]); + + // Checks if current index value is valid, and increases it until it is, or backtracks or aborts if so indicated. + while True do + begin + r := FStrategy.ValidateIndexValue(FCurrent, i); + case r of + ivrValid: begin + initialized := i; + Inc(i); + Break; + end; + ivrSkip: + Inc(FCurrent[i]); + ivrBacktrack: + if i > 0 then + begin + Dec(i); + Break; + end + else begin + Result := False; + Exit; + end; + end; + end; + end; + Result := True; +end; + +constructor TMultiIndexEnumerator.Create(const AStrategy: TEnumerableMultiIndexStrategy); +begin + FStrategy := AStrategy; + SetLength(FCurrent, FStrategy.GetCardinality); + Reset; +end; + +function TMultiIndexEnumerator.GetCurrent: TIndexArray; +begin + Result := FCurrent; +end; + +function TMultiIndexEnumerator.MoveNext: Boolean; +begin + Result := UpdateArray(FMustInit); + FMustInit := False; +end; + +procedure TMultiIndexEnumerator.Reset; +begin + FMustInit := True; +end; + +{ TEnumerableMultiIndexStrategy } + +function TEnumerableMultiIndexStrategy.GetEnumerator: specialize IEnumerator; +begin + Result := TMultiIndexEnumerator.Create(Self); +end; + +end. +