{ 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; // Returns the number of indices to iterate over, must return positive (non-zero) value. 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.