unit Myc.Trade.DataArray; interface uses Myc.Trade.DataPoint; type TMycDataArray = record private const ChunkSize = 1024; type TChunk = TArray; private FChunks: TArray; FCount: Int64; function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline; function GetItems(Idx: Int64): T; inline; public constructor Create(const AChunks: TArray; ACount: Int64); function Add(const Data: T; Lookback: Int64): TMycDataArray; overload; function Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray; overload; class function CreateEmpty: TMycDataArray; static; // Helper to create a data array from a raw TArray. class function CreateFromArray(const AData: TArray; First, Count: Integer): TMycDataArray; static; property Count: Int64 read FCount; property Items[Idx: Int64]: T read GetItems; default; end; implementation { TMycDataArray } constructor TMycDataArray.Create(const AChunks: TArray; ACount: Int64); begin FChunks := AChunks; FCount := ACount; end; class function TMycDataArray.CreateEmpty: TMycDataArray; begin Result.FChunks := nil; Result.FCount := 0; end; class function TMycDataArray.CreateFromArray(const AData: TArray; First, Count: Integer): TMycDataArray; begin // Use the Add method on an empty array to perform the chunking logic. Result := CreateEmpty.Add(AData, First, Count, Count); end; function TMycDataArray.Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray; 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 (Lookback <= 0) or (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 := TMycDataArray.Create(newChunks, newCount); end; function TMycDataArray.Add(const Data: T; Lookback: Int64): TMycDataArray; begin Result := Add([Data], 0, 1, Lookback); end; function TMycDataArray.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 TMycDataArray.LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; begin Result := FCount - LogicalIndex - 1; end; end.