unit Myc.Trade.DataArray; interface type // A series is an array of values with the newest ite at index=0. Each series counts the total of added items since creation, but // it actually may contain less items, because the array size is limited by the lookback parameter, when adding items. // Series are immutable. TSeries = record private const ChunkSize = 1024; type TChunk = TArray; private FChunks: TArray; FCount: Int64; FTotalCount: Int64; function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline; function GetItems(Idx: Int64): T; inline; public constructor Create(const AChunks: TArray; ACount, ATotalCount: Int64); class operator Initialize(out Dest: TSeries); // Add a singe item function Add(const Data: T; Lookback: Int64 = -1): TSeries; overload; // Add a ranmge of items function Add(const Data: array of T; First, Count, Lookback: Int64): TSeries; overload; // Helper to create a data array from a raw TArray. class function CreateFromArray(const AData: TArray; First, Count: Integer): TSeries; static; property Count: Int64 read FCount; property TotalCount: Int64 read FTotalCount; property Items[Idx: Int64]: T read GetItems; default; end; implementation uses System.Math; { TSeries } constructor TSeries.Create(const AChunks: TArray; ACount, ATotalCount: Int64); begin FChunks := AChunks; FCount := ACount; FTotalCount := ATotalCount; end; class function TSeries.CreateFromArray(const AData: TArray; First, Count: Integer): TSeries; begin // Use the Add method on an empty array to perform the chunking logic. Result.Add(AData, First, Count, Count); end; function TSeries.Add(const Data: array of T; First, Count, Lookback: Int64): TSeries; var destPhysicalIdx, sourcePhysicalIdx: Int64; itemsToSkip: Int64; numNewChunks: Integer; newChunks: TArray; destChunkIdx, destSubIdx: Integer; sumCount, newCount: Int64; begin if Count < 0 then Count := Length(Data) - First; if Count = 0 then exit(Self); Assert(Count <= (Length(Data) - First), 'Count cannot be larger than the source array'); sumCount := FCount + Count; newCount := sumCount; if (Lookback >= 0) and (newCount > Lookback) then newCount := Lookback; itemsToSkip := sumCount - newCount; numNewChunks := 0; if newCount > 0 then numNewChunks := (newCount - 1) div ChunkSize + 1; SetLength(newChunks, numNewChunks); for destPhysicalIdx := 0 to newCount - 1 do begin destChunkIdx := destPhysicalIdx div ChunkSize; destSubIdx := destPhysicalIdx mod ChunkSize; if destSubIdx = 0 then SetLength(newChunks[destChunkIdx], ChunkSize); sourcePhysicalIdx := itemsToSkip + destPhysicalIdx; if sourcePhysicalIdx < FCount then begin newChunks[destChunkIdx][destSubIdx] := FChunks[sourcePhysicalIdx div ChunkSize][sourcePhysicalIdx mod ChunkSize]; end else begin newChunks[destChunkIdx][destSubIdx] := Data[First + (sourcePhysicalIdx - FCount)]; end; end; Result := TSeries.Create(newChunks, newCount, FTotalCount + Count); end; function TSeries.Add(const Data: T; Lookback: Int64): TSeries; begin Result := Add([Data], 0, 1, Lookback); end; function TSeries.GetItems(Idx: Int64): T; var physicalIndex: Int64; begin Assert((Idx >= 0) and (Idx < FCount), 'Logical index is out of bounds.'); physicalIndex := LogicalToPhysicalIndex(Idx); Result := FChunks[physicalIndex div ChunkSize][physicalIndex mod ChunkSize]; end; function TSeries.LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; begin Result := FCount - LogicalIndex - 1; end; class operator TSeries.Initialize(out Dest: TSeries); begin Dest.FCount := 0; Dest.FTotalCount := 0; end; end.