Updated day 12 WIP performance refactor
- Added TValidationsToBlockAssignments to replace the loops in TConditionRecord.GenerateBlockAssignments
This commit is contained in:
parent
ec6928679a
commit
16e7528b34
|
@ -52,6 +52,7 @@ type
|
|||
TEnumerableMultiIndexStrategy = class(TInterfacedObject, specialize IEnumerable<TIndexArray>)
|
||||
public
|
||||
function GetEnumerator: specialize IEnumerator<TIndexArray>;
|
||||
// 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;
|
||||
|
|
|
@ -35,8 +35,6 @@ const
|
|||
|
||||
type
|
||||
TValidationLengths = array of array of Integer;
|
||||
// TODO: TIntegerArray probably not needed.
|
||||
TIntegerArray = array of Integer;
|
||||
|
||||
{ TDamage }
|
||||
|
||||
|
@ -63,20 +61,57 @@ type
|
|||
end;
|
||||
TBlocks = specialize TObjectList<TBlock>;
|
||||
|
||||
{ TAccumulatedCombinationsMultiIndexStrategy }
|
||||
|
||||
// Adds accumulated combinations to the enumerable strategy to allow calculation of combinations on the fly, and
|
||||
// therefore early rejection of invalid multi-index configurations.
|
||||
TAccumulatedCombinationsMultiIndexStrategy = class(TEnumerableMultiIndexStrategy)
|
||||
private
|
||||
FAccumulatedCombinations: TInt64Array;
|
||||
protected
|
||||
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; virtual;
|
||||
abstract;
|
||||
function UpdateCombinations(const AValidationResult: TIndexValidationResult; constref ACurrentIndexArray:
|
||||
TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
|
||||
public
|
||||
function GetCombinations: Int64;
|
||||
end;
|
||||
|
||||
TConditionRecord = class;
|
||||
|
||||
{ TValidationsToBlockAssignments }
|
||||
|
||||
// Enumerable strategy that enumerates all valid assignments of ranges of validation numbers to individual blocks in
|
||||
// the form of start and stop indices.
|
||||
TValidationsToBlockAssignments = class(TAccumulatedCombinationsMultiIndexStrategy)
|
||||
private
|
||||
FConditionRecord: TConditionRecord;
|
||||
protected
|
||||
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; override;
|
||||
public
|
||||
constructor Create(constref AConditionRecord: TConditionRecord);
|
||||
function GetCardinality: Integer; override;
|
||||
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||
out AStartIndexValue: Integer): Boolean; override;
|
||||
function ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer):
|
||||
TIndexValidationResult; override;
|
||||
end;
|
||||
|
||||
{ TDamageToValidationAssignments }
|
||||
|
||||
// Enumerable strategy that enumerates all valid assignments of each damage in the block to a specific validation
|
||||
// number from the validation numbers that have been assigned to the block, as indicated by start and stop indices.
|
||||
TDamageToValidationAssignments = class(TEnumerableMultiIndexStrategy)
|
||||
private
|
||||
FValidation: TIntegerList;
|
||||
FValidationLengths: TValidationLengths;
|
||||
FDamages: TDamages;
|
||||
FConditionRecord: TConditionRecord;
|
||||
FBlock: TBlock;
|
||||
FValidationStartIndex, FValidationStopIndex: Integer;
|
||||
// Calculates "span", the length of all damages for this validation number combined.
|
||||
// Calculates "span", the length of all damages for one validation number combined.
|
||||
function CalcValidationSpan(constref ACurrentIndexArray: TIndexArray; const ALastDamageIndex, AValidationNumber:
|
||||
Integer): Integer;
|
||||
public
|
||||
constructor Create(constref AValidation: TIntegerList; constref AValidationLengths: TValidationLengths;
|
||||
constref ADamages: TDamages; const AStartIndex, AStopIndex: Integer);
|
||||
constructor Create(constref AConditionRecord: TConditionRecord; constref ABlock: TBlock;
|
||||
const AStartValidationIndex, AStopValidationIndex: Integer);
|
||||
function GetCardinality: Integer; override;
|
||||
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||
out AStartIndexValue: Integer): Boolean; override;
|
||||
|
@ -92,34 +127,21 @@ type
|
|||
|
||||
TValidationPositionInfos = specialize TList<TValidationPositionInfo>;
|
||||
|
||||
TConditionRecord = class;
|
||||
|
||||
{ TAccumulatedCombinationsMultiIndexStrategy }
|
||||
|
||||
TAccumulatedCombinationsMultiIndexStrategy = class(TEnumerableMultiIndexStrategy)
|
||||
private
|
||||
FAccumulatedCombinations: TInt64Array;
|
||||
protected
|
||||
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; virtual;
|
||||
abstract;
|
||||
function UpdateCombinations(const AValidationResult: TIndexValidationResult; constref ACurrentIndexArray:
|
||||
TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
|
||||
public
|
||||
function GetCombinations: Int64;
|
||||
end;
|
||||
|
||||
{ TValidationPositionOffsets }
|
||||
|
||||
// Enumerable strategy that enumerates all valid assignments of start positions (positions mean character indices in
|
||||
// the block patterns) of validation numbers that have been assigned to damages in the current block, as indicated by
|
||||
// provided TValidationPositionInfos.
|
||||
TValidationPositionOffsets = class(TAccumulatedCombinationsMultiIndexStrategy)
|
||||
private
|
||||
FConditionRecord: TConditionRecord;
|
||||
FPositionInfos: TValidationPositionInfos;
|
||||
FBlockLength, FStartIndex, FStopIndex: Integer;
|
||||
FBlockLength, FValidationStartIndex, FValidationStopIndex: Integer;
|
||||
protected
|
||||
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; override;
|
||||
public
|
||||
constructor Create(constref AConditionRecord: TConditionRecord; constref APositionInfos: TValidationPositionInfos;
|
||||
const ABlockLength, AStartIndex, AStopIndex: Integer);
|
||||
const ABlockLength, AValidationStartIndex, AValidationStopIndex: Integer);
|
||||
function GetCardinality: Integer; override;
|
||||
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||
out AStartIndexValue: Integer): Boolean; override;
|
||||
|
@ -131,6 +153,7 @@ type
|
|||
|
||||
TConditionRecord = class
|
||||
private
|
||||
// List of validation numbers as stated in the problem input.
|
||||
FValidation: TIntegerList;
|
||||
// List of non-empty, maximum-length parts of the pattern without operational springs ("blocks").
|
||||
FBlocks: TBlocks;
|
||||
|
@ -140,11 +163,9 @@ type
|
|||
FValidationLengths: TValidationLengths;
|
||||
// Array 'a' of minimum indices 'a[i]', such that all remaining validation numbers starting at index 'a[i] - 1'
|
||||
// cannot fit into the remaining blocks starting at 'FBlocks[i]'.
|
||||
FMinIndices: TIntegerArray;
|
||||
FMinIndices: TIndexArray;
|
||||
procedure InitValidationLengths;
|
||||
procedure InitMinIndices;
|
||||
function CalcCombinations(constref AIndices: TIntegerArray): Int64;
|
||||
function CalcCombinationsBlock(constref ABlock: TBlock; const AStartIndex, AStopIndex: Integer): Int64;
|
||||
function CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64;
|
||||
function CalcCombinationsBlockMultiValidations(constref ABlock: TBlock; constref AIndices: TIndexArray;
|
||||
const AStartIndex, AStopIndex: Integer): Int64;
|
||||
|
@ -154,8 +175,12 @@ type
|
|||
// Adds all non-empty, maximum-length parts of the pattern without operational springs ("blocks").
|
||||
procedure AddBlocks(const APattern: string);
|
||||
function GenerateBlockAssignments: Int64;
|
||||
function CalcCombinationsBlock(constref ABlock: TBlock; const AStartIndex, AStopIndex: Integer): Int64;
|
||||
function CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): Int64;
|
||||
property Validation: TIntegerList read FValidation;
|
||||
property Blocks: TBlocks read FBlocks;
|
||||
property ValidationLengths: TValidationLengths read FValidationLengths;
|
||||
property MinIndices: TIndexArray read FMinIndices;
|
||||
end;
|
||||
|
||||
{ THotSprings }
|
||||
|
@ -219,6 +244,104 @@ begin
|
|||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TAccumulatedCombinationsMultiIndexStrategy }
|
||||
|
||||
function TAccumulatedCombinationsMultiIndexStrategy.UpdateCombinations(const AValidationResult: TIndexValidationResult;
|
||||
constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
|
||||
var
|
||||
combinations: Int64;
|
||||
begin
|
||||
Result := AValidationResult;
|
||||
if Result = ivrValid then
|
||||
begin
|
||||
combinations := CalcCombinations(ACurrentIndexArray, ACurrentIndex);
|
||||
if combinations = 0 then
|
||||
Result := ivrSkip
|
||||
else if ACurrentIndex > 0 then
|
||||
FAccumulatedCombinations[ACurrentIndex] := combinations * FAccumulatedCombinations[ACurrentIndex - 1]
|
||||
else begin
|
||||
SetLength(FAccumulatedCombinations, GetCardinality);
|
||||
FAccumulatedCombinations[ACurrentIndex] := combinations;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAccumulatedCombinationsMultiIndexStrategy.GetCombinations: Int64;
|
||||
begin
|
||||
if FAccumulatedCombinations <> nil then
|
||||
Result := FAccumulatedCombinations[GetCardinality - 1]
|
||||
else
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
{ TValidationsToBlockAssignments }
|
||||
|
||||
function TValidationsToBlockAssignments.CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex:
|
||||
Integer): Int64;
|
||||
var
|
||||
block: TBlock;
|
||||
start, stop: Integer;
|
||||
begin
|
||||
// 'ACurrentIndexArray[i] - 1' denotes the index of the last validation number assigned to 'Block[i]', and the index
|
||||
// of the first validation number in 'Validation' assigned to 'Block[i + 1]'. If two consecutive values in
|
||||
// 'ACurrentIndexArray' are the same, then the block in between has no numbers assigned to it.
|
||||
block := FConditionRecord.Blocks[ACurrentIndex];
|
||||
if ACurrentIndex > 0 then
|
||||
start := ACurrentIndexArray[ACurrentIndex - 1]
|
||||
else
|
||||
start := 0;
|
||||
stop := ACurrentIndexArray[ACurrentIndex] - 1;
|
||||
if block.Damages.Count > 0 then
|
||||
Result := FConditionRecord.CalcCombinationsBlock(block, start, stop)
|
||||
else
|
||||
Result := FConditionRecord.CalcCombinationsWildcardSequence(Length(block.Pattern), start, stop);
|
||||
end;
|
||||
|
||||
constructor TValidationsToBlockAssignments.Create(constref AConditionRecord: TConditionRecord);
|
||||
begin
|
||||
FConditionRecord := AConditionRecord;
|
||||
end;
|
||||
|
||||
function TValidationsToBlockAssignments.GetCardinality: Integer;
|
||||
begin
|
||||
Result := FConditionRecord.Blocks.Count;
|
||||
end;
|
||||
|
||||
function TValidationsToBlockAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||
const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
if ACurrentIndex + 1 = GetCardinality then
|
||||
AStartIndexValue := FConditionRecord.Validation.Count
|
||||
else if ACurrentIndex > 0 then
|
||||
AStartIndexValue := Max(ACurrentIndexArray[ACurrentIndex - 1], FConditionRecord.MinIndices[ACurrentIndex])
|
||||
else
|
||||
AStartIndexValue := FConditionRecord.MinIndices[ACurrentIndex];
|
||||
end;
|
||||
|
||||
function TValidationsToBlockAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||
const ACurrentIndex: Integer): TIndexValidationResult;
|
||||
var
|
||||
start: Integer;
|
||||
begin
|
||||
if ACurrentIndexArray[ACurrentIndex] > FConditionRecord.Validation.Count then
|
||||
Result := ivrBacktrack
|
||||
else begin
|
||||
if ACurrentIndex > 0 then
|
||||
start := ACurrentIndexArray[ACurrentIndex - 1]
|
||||
else
|
||||
start := 0;
|
||||
|
||||
if FConditionRecord.ValidationLengths[start, ACurrentIndexArray[ACurrentIndex]]
|
||||
<= Length(FConditionRecord.Blocks[ACurrentIndex].Pattern) then
|
||||
Result := ivrValid
|
||||
else
|
||||
Result := ivrBacktrack;
|
||||
end;
|
||||
|
||||
Result := UpdateCombinations(Result, ACurrentIndexArray, ACurrentIndex);
|
||||
end;
|
||||
|
||||
{ TDamageToValidationAssignments }
|
||||
|
||||
function TDamageToValidationAssignments.CalcValidationSpan(constref ACurrentIndexArray: TIndexArray;
|
||||
|
@ -229,24 +352,23 @@ begin
|
|||
spanStart := ALastDamageIndex;
|
||||
while (spanStart > 0) and (ACurrentIndexArray[spanStart - 1] = AValidationNumber) do
|
||||
Dec(spanStart);
|
||||
Result := FDamages[ALastDamageIndex].Length;
|
||||
Result := FBlock.Damages[ALastDamageIndex].Length;
|
||||
if spanStart < ALastDamageIndex then
|
||||
Inc(Result, FDamages[ALastDamageIndex].Start - FDamages[spanStart].Start);
|
||||
Inc(Result, FBlock.Damages[ALastDamageIndex].Start - FBlock.Damages[spanStart].Start);
|
||||
end;
|
||||
|
||||
constructor TDamageToValidationAssignments.Create(constref AValidation: TIntegerList; constref AValidationLengths:
|
||||
TValidationLengths; constref ADamages: TDamages; const AStartIndex, AStopIndex: Integer);
|
||||
constructor TDamageToValidationAssignments.Create(constref AConditionRecord: TConditionRecord; constref ABlock: TBlock;
|
||||
const AStartValidationIndex, AStopValidationIndex: Integer);
|
||||
begin
|
||||
FValidation := AValidation;
|
||||
FValidationLengths := AValidationLengths;
|
||||
FDamages := ADamages;
|
||||
FValidationStartIndex := AStartIndex;
|
||||
FValidationStopIndex := AStopIndex;
|
||||
FConditionRecord := AConditionRecord;
|
||||
FBlock := ABlock;
|
||||
FValidationStartIndex := AStartValidationIndex;
|
||||
FValidationStopIndex := AStopValidationIndex;
|
||||
end;
|
||||
|
||||
function TDamageToValidationAssignments.GetCardinality: Integer;
|
||||
begin
|
||||
Result := FDamages.Count;
|
||||
Result := FBlock.Damages.Count;
|
||||
end;
|
||||
|
||||
function TDamageToValidationAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||
|
@ -262,78 +384,32 @@ end;
|
|||
function TDamageToValidationAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||
const ACurrentIndex: Integer): TIndexValidationResult;
|
||||
var
|
||||
i, prev, firstSkip: Integer;
|
||||
i, prev: Integer;
|
||||
begin
|
||||
i := ACurrentIndexArray[ACurrentIndex];
|
||||
prev := ACurrentIndex - 1;
|
||||
// Checks maximum index value.
|
||||
if i > FValidationStopIndex then
|
||||
begin
|
||||
Result := ivrBacktrack;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := ivrBacktrack
|
||||
// Checks if there is enough space after this damage for remaining validation numbers.
|
||||
if (i < FValidationStopIndex)
|
||||
and (FValidationLengths[i + 1, FValidationStopIndex + 1] + 1 > FDamages[ACurrentIndex].CharsRemaining) then
|
||||
begin
|
||||
Result := ivrSkip;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
else if (i < FValidationStopIndex)
|
||||
and (FConditionRecord.ValidationLengths[i + 1, FValidationStopIndex + 1] + 1 > FBlock.Damages[ACurrentIndex].CharsRemaining) then
|
||||
Result := ivrSkip
|
||||
// Checks if there is enough space before this damage for previous validation numbers.
|
||||
if (FValidationStartIndex < i)
|
||||
and (FValidationLengths[FValidationStartIndex, i] + 1 >= FDamages[ACurrentIndex].Start) then
|
||||
begin
|
||||
Result := ivrBacktrack;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
else if (FValidationStartIndex < i)
|
||||
and (FConditionRecord.ValidationLengths[FValidationStartIndex, i] + 1 >= FBlock.Damages[ACurrentIndex].Start) then
|
||||
Result := ivrBacktrack
|
||||
// Checks if there is enough space between previous and this damage for skipped validation numbers.
|
||||
if ACurrentIndex > 0 then
|
||||
begin
|
||||
prev := ACurrentIndex - 1;
|
||||
firstSkip := ACurrentIndexArray[prev] + 1;
|
||||
if (firstSkip < i) and (FValidationLengths[firstSkip, i] + 2 > FDamages[ACurrentIndex].Start - FDamages[prev].Start - FDamages[prev].Length) then
|
||||
begin
|
||||
Result := ivrBacktrack;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
else if (ACurrentIndex > 0)
|
||||
and (ACurrentIndexArray[prev] + 1 < i)
|
||||
and (FConditionRecord.ValidationLengths[ACurrentIndexArray[prev] + 1, i] + 2
|
||||
> FBlock.Damages[ACurrentIndex].Start - FBlock.Damages[prev].Start - FBlock.Damages[prev].Length) then
|
||||
Result := ivrBacktrack
|
||||
// Checks if span is small enough to fit within this validation number.
|
||||
if FValidation[i] < CalcValidationSpan(ACurrentIndexArray, ACurrentIndex, i) then
|
||||
begin
|
||||
Result := ivrSkip;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := ivrValid;
|
||||
end;
|
||||
|
||||
{ TAccumulatedCombinationsMultiIndexStrategy }
|
||||
|
||||
function TAccumulatedCombinationsMultiIndexStrategy.UpdateCombinations(const AValidationResult: TIndexValidationResult;
|
||||
constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
|
||||
var
|
||||
combinations: Int64;
|
||||
begin
|
||||
Result := AValidationResult;
|
||||
if Result = ivrValid then
|
||||
begin
|
||||
combinations := CalcCombinations(ACurrentIndexArray, ACurrentIndex);
|
||||
if combinations = 0 then
|
||||
Result := ivrBacktrack
|
||||
else if ACurrentIndex > 0 then
|
||||
FAccumulatedCombinations[ACurrentIndex] := combinations * FAccumulatedCombinations[ACurrentIndex - 1]
|
||||
else begin
|
||||
SetLength(FAccumulatedCombinations, GetCardinality);
|
||||
FAccumulatedCombinations[ACurrentIndex] := combinations;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAccumulatedCombinationsMultiIndexStrategy.GetCombinations: Int64;
|
||||
begin
|
||||
Result := FAccumulatedCombinations[GetCardinality - 1];
|
||||
else if FConditionRecord.Validation[i] < CalcValidationSpan(ACurrentIndexArray, ACurrentIndex, i) then
|
||||
Result := ivrSkip
|
||||
else
|
||||
Result := ivrValid;
|
||||
end;
|
||||
|
||||
{ TValidationPositionOffsets }
|
||||
|
@ -354,25 +430,26 @@ begin
|
|||
else begin
|
||||
// Handles first calculated offset.
|
||||
space := ACurrentIndexArray[0] - 2;
|
||||
Result := FConditionRecord.CalcCombinationsWildcardSequence(space, FStartIndex, stop);
|
||||
Result := FConditionRecord.CalcCombinationsWildcardSequence(space, FValidationStartIndex, stop);
|
||||
end;
|
||||
|
||||
if (Result > 0) and (ACurrentIndex + 1 = GetCardinality) then
|
||||
begin
|
||||
// Handles last calculated offset.
|
||||
space := FBlockLength - ACurrentIndexArray[ACurrentIndex] - FConditionRecord.Validation[FPositionInfos.Last.ValidationIndex];
|
||||
Result := Result * FConditionRecord.CalcCombinationsWildcardSequence(space, FPositionInfos.Last.ValidationIndex + 1, FStopIndex);
|
||||
start := FPositionInfos.Last.ValidationIndex + 1;
|
||||
Result := Result * FConditionRecord.CalcCombinationsWildcardSequence(space, start, FValidationStopIndex);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TValidationPositionOffsets.Create(constref AConditionRecord: TConditionRecord; constref APositionInfos:
|
||||
TValidationPositionInfos; const ABlockLength, AStartIndex, AStopIndex: Integer);
|
||||
TValidationPositionInfos; const ABlockLength, AValidationStartIndex, AValidationStopIndex: Integer);
|
||||
begin
|
||||
FConditionRecord := AConditionRecord;
|
||||
FPositionInfos := APositionInfos;
|
||||
FBlockLength := ABlockLength;
|
||||
FStartIndex := AStartIndex;
|
||||
FStopIndex := AStopIndex;
|
||||
FValidationStartIndex := AValidationStartIndex;
|
||||
FValidationStopIndex := AValidationStopIndex;
|
||||
inherited Create;
|
||||
end;
|
||||
|
||||
|
@ -387,7 +464,6 @@ var
|
|||
info: TValidationPositionInfo;
|
||||
begin
|
||||
info := FPositionInfos[ACurrentIndex];
|
||||
// Calculates start value such that the validation number just includes MinEnd.
|
||||
AStartIndexValue := info.MinStart;
|
||||
// Adjusts start value to avoid overlap of this validation number with the previous one (the one from previous
|
||||
// position info).
|
||||
|
@ -442,122 +518,6 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TConditionRecord.CalcCombinations(constref AIndices: TIntegerArray): Int64;
|
||||
var
|
||||
i, j: Integer;
|
||||
// TODO: Remove r.
|
||||
r: Int64;
|
||||
begin
|
||||
{$ifdef debug}
|
||||
for i in AIndices do
|
||||
Write(i, ' ');
|
||||
WriteLn;
|
||||
{$endif}
|
||||
|
||||
Result := 1;
|
||||
i := 0;
|
||||
while (Result > 0) and (i < FBlocks.Count) do
|
||||
begin
|
||||
if FBlocks[i].Damages.Count > 0 then
|
||||
r := CalcCombinationsBlock(FBlocks[i], AIndices[i], AIndices[i + 1] - 1)
|
||||
else begin
|
||||
{$ifdef debug}
|
||||
Write(' ', FBlocks[i].Pattern, ' ');
|
||||
for j := AIndices[i] to AIndices[i + 1] - 1 do
|
||||
Write(FValidation[j], ' ');
|
||||
WriteLn;
|
||||
Write(' count/space/freedoms: ');
|
||||
{$endif}
|
||||
r := CalcCombinationsWildcardSequence(Length(FBlocks[i].Pattern), AIndices[i], AIndices[i + 1] - 1);
|
||||
{$ifdef debug}
|
||||
WriteLn(' result: ', r);
|
||||
{$endif}
|
||||
end;
|
||||
{$ifdef debug}
|
||||
WriteLn(' Result: ', r);
|
||||
{$endif}
|
||||
Result := Result * r;
|
||||
Inc(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TConditionRecord.CalcCombinationsBlock(constref ABlock: TBlock; const AStartIndex, AStopIndex: Integer): Int64;
|
||||
var
|
||||
i, j, k: Integer;
|
||||
indices: TIndexArray;
|
||||
validationToDamageAssignments: TDamageToValidationAssignments;
|
||||
begin
|
||||
{$ifdef debug}
|
||||
Write(' ', ABlock.Pattern, ' ');
|
||||
for i := AStartIndex to AStopIndex do
|
||||
Write(FValidation[i], ' ');
|
||||
WriteLn;
|
||||
{$endif}
|
||||
|
||||
// No validation number assigned to this block.
|
||||
if AStartIndex > AStopIndex then
|
||||
begin
|
||||
if ABlock.Damages.Count = 0 then
|
||||
Result := 1
|
||||
else
|
||||
Result := 0;
|
||||
end
|
||||
// One validation number assigned to this block.
|
||||
else if AStartIndex = AStopIndex then
|
||||
Result := CalcCombinationsBlockSingleValidation(ABlock, AStartIndex)
|
||||
// Multiple validation numbers assigned to this block.
|
||||
else begin
|
||||
{$ifdef debug}
|
||||
Write(' min before: ');
|
||||
for i := AStartIndex to AStopIndex do
|
||||
Write(FValidationLengths[AStartIndex, i + 1] - FValidation[i], ' ');
|
||||
WriteLn;
|
||||
Write(' min after: ');
|
||||
for i := AStartIndex to AStopIndex do
|
||||
Write(FValidationLengths[i, AStopIndex + 1] - FValidation[i], ' ');
|
||||
WriteLn;
|
||||
|
||||
for i := 0 to ABlock.Damages.Count - 1 do
|
||||
begin
|
||||
WriteLn(' damage: start ',ABlock.Damages[i].Start, ', length ', ABlock.Damages[i].Length, ', remain ', ABlock.Damages[i].CharsRemaining);
|
||||
Write(' ');
|
||||
for j := AStartIndex to AStopIndex do
|
||||
// Enough space before damage for the other validation numbers?
|
||||
if (FValidationLengths[AStartIndex, j + 1] - FValidation[j] < ABlock.Damages[i].Start)
|
||||
// Enough space after damage for the other validation numbers?
|
||||
and (FValidationLengths[j, AStopIndex + 1] - FValidation[j] <= ABlock.Damages[i].CharsRemaining)
|
||||
// Damage itself small enough for this validation number?
|
||||
and (FValidation[j] >= ABlock.Damages[i].Length) then
|
||||
Write(j - AStartIndex, ' ');
|
||||
WriteLn;
|
||||
end;
|
||||
{$endif}
|
||||
|
||||
Result := 0;
|
||||
|
||||
// Assigns validation numbers to specific damages.
|
||||
validationToDamageAssignments := TDamageToValidationAssignments.Create(FValidation, FValidationLengths, ABlock.Damages,
|
||||
AStartIndex, AStopIndex);
|
||||
{$ifdef debug}
|
||||
WriteLn(' validation numbers (indices) per damages:');
|
||||
{$endif}
|
||||
for indices in validationToDamageAssignments do
|
||||
begin
|
||||
{$ifdef debug}
|
||||
Write(' ');
|
||||
for i := 0 to ABlock.Damages.Count - 1 do
|
||||
Write(FValidation[indices[i]], ' ');
|
||||
Write('( ');
|
||||
for i := 0 to ABlock.Damages.Count - 1 do
|
||||
Write(indices[i] - AStartIndex, ' ');
|
||||
WriteLn(')');
|
||||
{$endif}
|
||||
Result := Result + CalcCombinationsBlockMultiValidations(ABlock, indices, AStartIndex, AStopIndex);
|
||||
end;
|
||||
validationToDamageAssignments.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TConditionRecord.CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64;
|
||||
var
|
||||
len, combinedDamagesLength: Integer;
|
||||
|
@ -658,63 +618,93 @@ end;
|
|||
|
||||
function TConditionRecord.GenerateBlockAssignments: Int64;
|
||||
var
|
||||
indices: array of Integer;
|
||||
i, j, k, high: Integer;
|
||||
// TODO: Remove r, count, misses.
|
||||
r: Int64;
|
||||
count, misses: Integer;
|
||||
validationsToBlockAssignments: TValidationsToBlockAssignments;
|
||||
indices: TIndexArray;
|
||||
begin
|
||||
count := 0;
|
||||
misses := 0;
|
||||
// Each loop (each call to 'CalcCombinations') represents an independent set of arrangements, defined by 'indices',
|
||||
// where specific validation numbers are assigned to specific block patterns.
|
||||
//
|
||||
// Here, 'indices[i]' denotes the index + 1 of the last validation number assigned to 'FBlockPattern[i]', and the
|
||||
// index of the first validation number in 'FValidation' assigned to 'FBlockPattern[i + 1]'. If two consecutive values
|
||||
// in 'indices' are the same, then the block in between has no numbers assigned to it.
|
||||
//
|
||||
// Note that 'indices[0] = 0' and 'indices[FBlockPatterns.Count] = FValidation.Count' are constant. Having these two
|
||||
// numbers in the array simplifies the code a bit.
|
||||
InitValidationLengths;
|
||||
//FPatternLengths := CalcPatternLengths;
|
||||
InitMinIndices;
|
||||
|
||||
SetLength(indices, FBlocks.Count + 1);
|
||||
high := Length(indices) - 2;
|
||||
indices[0] := 0;
|
||||
indices[high + 1] := FValidation.Count;
|
||||
|
||||
// TODO: Use TMultiIndexEnumerator for this.
|
||||
Result := 0;
|
||||
k := 0;
|
||||
repeat
|
||||
i := k + 1;
|
||||
while i <= high do
|
||||
validationsToBlockAssignments := TValidationsToBlockAssignments.Create(Self);
|
||||
for indices in validationsToBlockAssignments do
|
||||
Result := Result + validationsToBlockAssignments.GetCombinations;
|
||||
validationsToBlockAssignments.Free;
|
||||
end;
|
||||
|
||||
function TConditionRecord.CalcCombinationsBlock(constref ABlock: TBlock; const AStartIndex, AStopIndex: Integer): Int64;
|
||||
var
|
||||
i, j, k: Integer;
|
||||
indices: TIndexArray;
|
||||
damageToValidationAssignments: TDamageToValidationAssignments;
|
||||
begin
|
||||
{$ifdef debug}
|
||||
Write(' ', ABlock.Pattern, ' ');
|
||||
for i := AStartIndex to AStopIndex do
|
||||
Write(FValidation[i], ' ');
|
||||
WriteLn;
|
||||
{$endif}
|
||||
|
||||
// No validation number assigned to this block.
|
||||
if AStartIndex > AStopIndex then
|
||||
begin
|
||||
if ABlock.Damages.Count = 0 then
|
||||
Result := 1
|
||||
else
|
||||
Result := 0;
|
||||
end
|
||||
// One validation number assigned to this block.
|
||||
else if AStartIndex = AStopIndex then
|
||||
Result := CalcCombinationsBlockSingleValidation(ABlock, AStartIndex)
|
||||
// Multiple validation numbers assigned to this block.
|
||||
else begin
|
||||
{$ifdef debug}
|
||||
Write(' min before: ');
|
||||
for i := AStartIndex to AStopIndex do
|
||||
Write(FValidationLengths[AStartIndex, i + 1] - FValidation[i], ' ');
|
||||
WriteLn;
|
||||
Write(' min after: ');
|
||||
for i := AStartIndex to AStopIndex do
|
||||
Write(FValidationLengths[i, AStopIndex + 1] - FValidation[i], ' ');
|
||||
WriteLn;
|
||||
|
||||
for i := 0 to ABlock.Damages.Count - 1 do
|
||||
begin
|
||||
indices[i] := Max(indices[i - 1], FMinIndices[i - 1]);
|
||||
while FValidationLengths[indices[i - 1], indices[i]] > Length(FBlocks[i - 1].Pattern) do
|
||||
begin
|
||||
Dec(i);
|
||||
Inc(indices[i]);
|
||||
end;
|
||||
|
||||
Inc(i);
|
||||
WriteLn(' damage: start ',ABlock.Damages[i].Start, ', length ', ABlock.Damages[i].Length, ', remain ', ABlock.Damages[i].CharsRemaining);
|
||||
Write(' ');
|
||||
for j := AStartIndex to AStopIndex do
|
||||
// Enough space before damage for the other validation numbers?
|
||||
if (FValidationLengths[AStartIndex, j + 1] - FValidation[j] < ABlock.Damages[i].Start)
|
||||
// Enough space after damage for the other validation numbers?
|
||||
and (FValidationLengths[j, AStopIndex + 1] - FValidation[j] <= ABlock.Damages[i].CharsRemaining)
|
||||
// Damage itself small enough for this validation number?
|
||||
and (FValidation[j] >= ABlock.Damages[i].Length) then
|
||||
Write(j - AStartIndex, ' ');
|
||||
WriteLn;
|
||||
end;
|
||||
{$endif}
|
||||
|
||||
Inc(count);
|
||||
r := CalcCombinations(indices);
|
||||
if r = 0 then
|
||||
Inc(misses);
|
||||
Result := Result + r;
|
||||
Result := 0;
|
||||
|
||||
k := high;
|
||||
while (k > 0)
|
||||
and ((indices[k] = FValidation.Count)
|
||||
or (FValidationLengths[indices[k - 1], indices[k] + 1] > Length(FBlocks[k - 1].Pattern))) do
|
||||
Dec(k);
|
||||
Inc(indices[k]);
|
||||
until k = 0;
|
||||
WriteLn(' missed: ', misses, '/', count);
|
||||
// Assigns validation numbers to specific damages.
|
||||
damageToValidationAssignments := TDamageToValidationAssignments.Create(Self, ABlock, AStartIndex, AStopIndex);
|
||||
{$ifdef debug}
|
||||
WriteLn(' validation numbers (indices) per damages:');
|
||||
{$endif}
|
||||
for indices in damageToValidationAssignments do
|
||||
begin
|
||||
{$ifdef debug}
|
||||
Write(' ');
|
||||
for i := 0 to ABlock.Damages.Count - 1 do
|
||||
Write(FValidation[indices[i]], ' ');
|
||||
Write('( ');
|
||||
for i := 0 to ABlock.Damages.Count - 1 do
|
||||
Write(indices[i] - AStartIndex, ' ');
|
||||
WriteLn(')');
|
||||
{$endif}
|
||||
Result := Result + CalcCombinationsBlockMultiValidations(ABlock, indices, AStartIndex, AStopIndex);
|
||||
end;
|
||||
damageToValidationAssignments.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TConditionRecord.CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer):
|
||||
|
|
Loading…
Reference in New Issue