(*$JPPDEFINEMACRO JCLBUCKETARRAYLISTITRIMP(SELFCLASSNAME, ITRINTERFACENAME, BUCKETLISTTYPENAME,
  BUCKETTYPENAME,CONSTKEYWORD, PARAMETERNAME, TYPENAME, GETTERNAME, SETTERNAME)
//=== { SELFCLASSNAME } ===============================================================

constructor SELFCLASSNAME.Create(AOwnList: BUCKETLISTTYPENAME; ABucketCursor, AItemCursor: Integer; AValid: Boolean; AStart: TItrStart);
begin
  inherited Create(AValid);
  FOwnList := AOwnList;
  FStart := AStart;
  FBucketCursor := ABucketCursor;
  FItemCursor := ABucketCursor;
end;

function SELFCLASSNAME.Add(CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
begin
  Result := FOwnList.Add(PARAMETERNAME);
end;

procedure SELFCLASSNAME.AssignPropertiesTo(Dest: TJclAbstractIterator);
var
  ADest: SELFCLASSNAME;
begin
  inherited AssignPropertiesTo(Dest);
  if Dest is SELFCLASSNAME then
  begin
    ADest := SELFCLASSNAME(Dest);
    ADest.FOwnList := FOwnList;
    ADest.FBucketCursor := FBucketCursor;
    ADest.FItemCursor := FItemCursor;
    ADest.FStart := FStart;
  end;
end;

function SELFCLASSNAME.CreateEmptyIterator: TJclAbstractIterator;
begin
  Result := SELFCLASSNAME.Create(FOwnList, FBucketCursor, FItemCursor, Valid, FStart);
end;

procedure SELFCLASSNAME.Extract;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEWRITEBEGIN(FOwnList)}
    CheckValid;
    Valid := False;
    Bucket := FOwnList.FBuckets[FBucketCursor];
    FOwnList.ExtractIndex(Bucket,FItemCursor);
  {$JPPEXPANDMACRO DELEGATEWRITEEND(FOwnList)}
end;

function SELFCLASSNAME.GETTERNAME: TYPENAME;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    CheckValid;
    Bucket := FOwnList.FBuckets[FBucketCursor];
    Result := Bucket.BucketElements[FItemCursor];
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;

function SELFCLASSNAME.HasNext: Boolean;
var
  Bucket: BUCKETTYPENAME;
  I: Integer;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    Bucket := FOwnList.FBuckets[FBucketCursor];
    if Valid then
    begin
      Result := Assigned(Bucket) and (FItemCursor < (Bucket.BucketSize - 1));
      if not Result then
        for I := FBucketCursor + 1 to FOwnList.FCapacity - 1 do
      begin
        Bucket := FOwnList.FBuckets[I];
        Result := Assigned(Bucket) and (Bucket.BucketSize > 0);
        if Result then
          Break;
      end;
    end
    else
      Result := Assigned(Bucket) and (FItemCursor < Bucket.BucketSize);
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;

function SELFCLASSNAME.HasPrevious: Boolean;
var
  Bucket: BUCKETTYPENAME;
  I: Integer;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    Bucket := FOwnList.FBuckets[FBucketCursor];
    if Valid then
    begin
      Result := Assigned(Bucket) and (FItemCursor > 0);
      if not Result then
        for I := FBucketCursor - 1 downto 0 do
      begin
        Bucket := FOwnList.FBuckets[I];
        Result := Assigned(Bucket) and (Bucket.BucketSize > 0);
        if Result then
          Break;
      end;
    end
    else
      Result := Assigned(Bucket) and (FItemCursor >= 0);
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;

function SELFCLASSNAME.Insert(CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEWRITEBEGIN(FOwnList)}
    CheckValid;
    Bucket := FOwnList.FBuckets[FBucketCursor];
    Result := FOwnList.Insert(Bucket, FItemCursor, PARAMETERNAME);
  {$JPPEXPANDMACRO DELEGATEWRITEEND(FOwnList)}
end;

function SELFCLASSNAME.IteratorEquals(const AIterator: ITRINTERFACENAME): Boolean;
var
  Obj: TObject;
  ItrObj: SELFCLASSNAME;
begin
  Result := False;
  if AIterator = nil then
    Exit;
  Obj := AIterator.GetIteratorReference;
  if Obj is SELFCLASSNAME then
  begin
    ItrObj := SELFCLASSNAME(Obj);
    Result := (FOwnList = ItrObj.FOwnList) and (FBucketCursor = ItrObj.FBucketCursor) and (FItemCursor = ItrObj.FItemCursor) and (Valid = ItrObj.Valid);
  end;
end;

{$IFDEF SUPPORTS_FOR_IN}
function SELFCLASSNAME.MoveNext: Boolean;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    if Valid then
    begin
      Bucket := FOwnList.FBuckets[FBucketCursor];
      while Assigned(Bucket) do
      begin
        // the next item is inside the same bucket
        if FItemCursor < (Bucket.BucketSize - 1) then
        begin
          Inc(FItemCursor);
          Break;
        end;
        // move to the next bucket
        FItemCursor := -1;
        Bucket := nil;
        while (FBucketCursor < (FOwnList.FCapacity - 1)) and not Assigned(Bucket) do
        begin
          Inc(FBucketCursor);
          Bucket := FOwnList.FBuckets[FBucketCursor];
        end;
      end;
      Result := FItemCursor >= 0;
    end
    else
    begin
      Valid := True;
      Bucket := FOwnList.FBuckets[FBucketCursor];
      Result := Assigned(Bucket) and (FItemCursor >= 0) and (FItemCursor < Bucket.BucketSize);
    end;
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;
{$ENDIF SUPPORTS_FOR_IN}

function SELFCLASSNAME.Next: TYPENAME;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    if Valid then
    begin
      Bucket := FOwnList.FBuckets[FBucketCursor];
      while Assigned(Bucket) do
      begin
        // the next item is inside the same bucket
        if FItemCursor < (Bucket.BucketSize - 1) then
        begin
          Inc(FItemCursor);
          Break;
        end;
        // move to the next bucket
        FItemCursor := -1;
        Bucket := nil;
        while (FBucketCursor < (FOWnList.FCapacity - 1)) and not Assigned(Bucket) do
        begin
          Inc(FBucketCursor);
          Bucket := FOwnList.FBuckets[FBucketCursor];
        end;
      end;
    end
    else
    begin
      Valid := True;
      Bucket := FOwnList.FBuckets[FBucketCursor];
    end;
    if Assigned(Bucket) and (FItemCursor >= 0) and (FItemCursor < Bucket.BucketSize) then
      Result := Bucket.BucketElements[FItemCursor]
    else
      Result := FOwnList.RaiseOutOfBoundsError;
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;

function SELFCLASSNAME.NextIndex: Integer;
begin
  // No Index
  raise EJclOperationNotSupportedError.Create;
end;

function SELFCLASSNAME.Previous: TYPENAME;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    if Valid then
    begin
      Bucket := FOwnList.FBuckets[FBucketCursor];
      while Assigned(Bucket) do
      begin
        // the previous item is inside the same bucket
        if FItemCursor > 0 then
        begin
          Dec(FItemCursor);
          Break;
        end;
        // move to the previous bucket
        FItemCursor := -1;
        Bucket := nil;
        while (FBucketCursor > 0) and not Assigned(Bucket) do
        begin
          Dec(FBucketCursor);
          Bucket := FOwnList.FBuckets[FBucketCursor];
        end;
      end;
    end
    else
    begin
      Valid := True;
      Bucket := FOwnList.FBuckets[FBucketCursor];
    end;
    if Assigned(Bucket) and (FItemCursor >= 0) and (FItemCursor < Bucket.BucketSize) then
      Result := Bucket.BucketElements[FItemCursor]
    else
      Result := FOwnList.RaiseOutOfBoundsError;
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;

function SELFCLASSNAME.PreviousIndex: Integer;
begin
  // No Index
  raise EJclOperationNotSupportedError.Create;
end;

procedure SELFCLASSNAME.Remove;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEWRITEBEGIN(FOwnList)}
    CheckValid;
    Valid := False;
    Bucket := FOwnList.FBuckets[FBucketCursor];
    FOwnList.Delete(Bucket, FItemCursor);
  {$JPPEXPANDMACRO DELEGATEWRITEEND(FOwnList)}
end;

procedure SELFCLASSNAME.Reset;
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEREADBEGIN(FOwnList)}
    Valid := False;
    case FStart of
      isFirst:
        begin
          FBucketCursor := 0;
          FItemCursor := 0;
          while FBucketCursor < FOwnList.FCapacity do
          begin
            Bucket := FOwnList.FBuckets[FBucketCursor];
            if Assigned(Bucket) and (Bucket.BucketSize > 0) then
              Break;
          end;
          if FBucketCursor = FOwnList.FCapacity then
            FBucketCursor := 0;
        end;
      isLast:
        begin
          FBucketCursor := FOwnList.FCapacity - 1;
          FItemCursor := 0;
          while FBucketCursor >= 0 do
          begin
            Bucket := FOwnList.FBuckets[FBucketCursor];
            if Assigned(Bucket) and (Bucket.BucketSize > 0) then
            begin
              FItemCursor := Bucket.BucketSize - 1;
              Break;
            end;
          end;
          if FBucketCursor = FOwnList.FCapacity then
            FBucketCursor := 0;
        end;
    end;
  {$JPPEXPANDMACRO DELEGATEREADEND(FOwnList)}
end;

procedure SELFCLASSNAME.SETTERNAME(CONSTKEYWORDPARAMETERNAME: TYPENAME);
var
  Bucket: BUCKETTYPENAME;
begin
  {$JPPEXPANDMACRO DELEGATEWRITEBEGIN(FOwnList)}
    CheckValid;
    Bucket := FOwnList.FBuckets[FBucketCursor];
    FOwnList.SETTERNAME(Bucket, FItemCursor, PARAMETERNAME);
  {$JPPEXPANDMACRO DELEGATEWRITEEND(FOwnList)}
end;*)
(*$JPPDEFINEMACRO JCLBUCKETARRAYLISTIMP(SELFCLASSNAME,
  OWNERSHIPDECLARATION, OWNERSHIPPARAMETER, COLLECTIONINTERFACENAME, ITRINTERFACENAME, ITRCLASSNAME, LISTINTERFACENAME,
  BUCKETTYPENAME, CONSTKEYWORD, PARAMETERNAME, GETTERNAME, SETTERNAME, RELEASERNAME, TYPENAME, DEFAULTVALUE)
//=== { SELFCLASSNAME } ======================================================

constructor SELFCLASSNAME.Create(ACapacity: IntegerOWNERSHIPDECLARATION);
begin
  inherited Create(OWNERSHIPPARAMETER);
  SetCapacity(ACapacity);
end;

constructor SELFCLASSNAME.Create(const ACollection: COLLECTIONINTERFACENAMEOWNERSHIPDECLARATION);
begin
  inherited Create(OWNERSHIPPARAMETER);
  if ACollection = nil then
    raise EJclNoCollectionError.Create;
  SetCapacity(ACollection.Size);
  AddAll(ACollection);
end;

destructor SELFCLASSNAME.Destroy;
begin
  FReadOnly := False;
  Clear;
  inherited Destroy;
end;

function SELFCLASSNAME.Add(CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex, BucketCapacity, ThresholdBucketCapacity: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := FAllowDefaultElements or not ItemsEqual(PARAMETERNAME, DEFAULTVALUE);
    if Result then
    begin
      if FDuplicates <> dupAccept then
        for BucketIndex := 0 to FCapacity - 1 do
      begin
        Bucket := FBuckets[BucketIndex];
        if Assigned(Bucket) then
          for ItemIndex := 0 to Bucket.BucketSize - 1 do
            if ItemsEqual(PARAMETERNAME, Bucket.BucketElements[ItemIndex]) then
        begin
          Result := CheckDuplicate;
          Break;
        end;
      end;

      if Result then
      begin
        ItemIndex := 0;
        Bucket := nil;
        // locate the last non empty bucket
        for BucketIndex := FCapacity - 1 downto 0 do
        begin
          Bucket := FBuckets[BucketIndex];
          if Assigned(Bucket) then
          begin
            ItemIndex := Bucket.BucketSize;
            Break;
          end;
        end;
        // when no buckets are initialized yet, create one
        if not Assigned(Bucket) then
        begin
          BucketIndex := 0;
          Bucket := BUCKETTYPENAME.Create;
          FBuckets[0] := Bucket;
        end;
        BucketCapacity := Length(Bucket.BucketElements);
        // if the bucket is too small, allocate more space
        if (ItemIndex >= BucketCapacity) then
        begin
          ThresholdBucketCapacity := (FSize div FCapacity) + 1;
          // allocate the next bucket to average bucket sizes
          if (BucketCapacity >= ThresholdBucketCapacity) and (BucketIndex < (FCapacity - 1)) then
          begin
            Inc(BucketIndex);
            ItemIndex := 0;
            Bucket := BUCKETTYPENAME.Create;
            BucketCapacity := 0;
            FBuckets[BucketIndex] := Bucket;
          end;
          // grow the bucket
          BucketCapacity := CalcGrowCapacity(BucketCapacity, Bucket.BucketSize);
          SetLength(Bucket.BucketElements, BucketCapacity);
        end;
        Result := ItemIndex < BucketCapacity;
        // store the value
        if Result then
        begin
          Bucket.BucketElements[ItemIndex] := PARAMETERNAME;
          Inc(Bucket.BucketSize);
          Inc(FSize);
        end;
      end;
    end;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.AddAll(const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  It: ITRINTERFACENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := False;
    if ACollection = nil then
      Exit;
    Result := True;
    It := ACollection.First;
    while It.HasNext do
      Result := Add(It.Next) and Result;
  {$JPPEXPANDMACRO WRITEEND}
end;

procedure SELFCLASSNAME.AssignDataTo(Dest: TJclAbstractContainerBase);
var
  ADest: SELFCLASSNAME;
  ACollection: COLLECTIONINTERFACENAME;
begin
  inherited AssignDataTo(Dest);
  if Dest is SELFCLASSNAME then
  begin
    ADest := SELFCLASSNAME(Dest);
    ADest.Clear;
    ADest.AddAll(Self);
  end
  else
  if Supports(IInterface(Dest), COLLECTIONINTERFACENAME, ACollection) then
  begin
    ACollection.Clear;
    ACollection.AddAll(Self);
  end;
end;

procedure SELFCLASSNAME.Clear;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
      begin
        for ItemIndex := 0 to Bucket.BucketSize - 1 do
          RELEASERNAME(Bucket.BucketElements[ItemIndex]);
        FreeAndNil(FBuckets[BucketIndex]);
      end;
    end;
    FSize := 0;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.CollectionEquals(const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
  It: ITRINTERFACENAME;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Result := False;
    if ACollection = nil then
      Exit;
    if FSize <> ACollection.Size then
      Exit;
    It := ACollection.First;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        for ItemIndex := 0 to Bucket.BucketSize - 1 do
          if not ItemsEqual(Bucket.BucketElements[ItemIndex], It.Next) then
            Exit;
    end;
    Result := True;
  {$JPPEXPANDMACRO READEND}
end;

function SELFCLASSNAME.Contains(CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Result := False;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        for ItemIndex := 0 to Bucket.BucketSize - 1 do
          if ItemsEqual(PARAMETERNAME, Bucket.BucketElements[ItemIndex]) then
      begin
        Result := True;
        Break;
      end;
    end;
  {$JPPEXPANDMACRO READEND}
end;

function SELFCLASSNAME.ContainsAll(const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  It: ITRINTERFACENAME;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Result := True;
    if ACollection = nil then
      Exit;
    It := ACollection.First;
    while Result and It.HasNext do
      Result := Contains(It.Next);
  {$JPPEXPANDMACRO READEND}
end;

function SELFCLASSNAME.Delete(Bucket: BUCKETTYPENAME; ItemIndex: Integer): TYPENAME;
var
  Extracted: TYPENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Extracted := ExtractIndex(Bucket, ItemIndex);
    Result := RELEASERNAME(Extracted);
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.Delete(Index: Integer): TYPENAME;
var
  Extracted: TYPENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Extracted := ExtractIndex(Index);
    Result := RELEASERNAME(Extracted);
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.Extract(CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := False;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        for ItemIndex := Bucket.BucketSize - 1 downto 0 do
          if ItemsEqual(Bucket.BucketElements[ItemIndex], PARAMETERNAME) then
      begin
        Bucket.BucketElements[ItemIndex] := DEFAULTVALUE;
        if ItemIndex < (Bucket.BucketSize - 1) then
          MoveArray(Bucket.BucketElements, ItemIndex + 1, ItemIndex, Bucket.BucketSize - ItemIndex);
        Dec(Bucket.BucketSize);
        Dec(FSize);
        Result := True;
        if FRemoveSingleElement then
          Break;
      end;
    end;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.ExtractAll(const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  It: ITRINTERFACENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := False;
    if ACollection = nil then
      Exit;
    Result := True;
    It := ACollection.First;
    while It.HasNext do
      Result := Extract(It.Next) and Result;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.ExtractIndex(Index: Integer): TYPENAME;
var
  Bucket: BUCKETTYPENAME;
  ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    TranslateIndex(Index, Bucket, ItemIndex);
    // the item is inside this bucket
    Result := ExtractIndex(Bucket, ItemIndex);
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.ExtractIndex(Bucket: BUCKETTYPENAME; ItemIndex: Integer): TYPENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := Bucket.BucketElements[ItemIndex];
    if ItemIndex < (Bucket.BucketSize - 1) then
      MoveArray(Bucket.BucketElements, ItemIndex + 1, ItemIndex, Bucket.BucketSize - ItemIndex);
    Dec(Bucket.BucketSize);
    Dec(FSize);
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.First: ITRINTERFACENAME;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) and (Bucket.BucketSize > 0) then
        Result := ITRCLASSNAME.Create(Self, BucketIndex, 0, False, isFirst);
    end;
    if not Assigned(Result) then
      Result := ITRCLASSNAME.Create(Self, 0, 0, False, isFirst);
  {$JPPEXPANDMACRO READEND}
end;

{$IFDEF SUPPORTS_FOR_IN}
function SELFCLASSNAME.GetEnumerator: ITRINTERFACENAME;
begin
  Result := First;
end;
{$ENDIF SUPPORTS_FOR_IN}

function SELFCLASSNAME.GETTERNAME(Index: Integer): TYPENAME;
var
  Bucket: BUCKETTYPENAME;
  ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Result := DEFAULTVALUE;
    if (Index >= 0) and (Index < FSize) then
    begin
      TranslateIndex(Index, Bucket, ItemIndex);
      Result := Bucket.BucketElements[ItemIndex];
    end
    else
    if not FReturnDefaultElements then
      raise EJclNoSuchElementError.Create(IntToStr(Index));
  {$JPPEXPANDMACRO READEND}
end;

function SELFCLASSNAME.IndexOf(CONSTKEYWORDPARAMETERNAME: TYPENAME): Integer;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Result := -1;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        for ItemIndex := 0 to Bucket.BucketSize - 1 do
      begin
        Inc(Result);
        if ItemsEqual(PARAMETERNAME, Bucket.BucketElements[ItemIndex]) then
          Exit;
      end;
    end;
    Result := -1;
  {$JPPEXPANDMACRO READEND}
end;

function SELFCLASSNAME.Insert(Index: Integer; CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, BucketCapacity, ThresholdBucketCapacity: Integer;
begin
  Result := False;
  {$JPPEXPANDMACRO WRITEBEGIN}
    if (Index < 0) or (Index > FSize) then
      raise EJclOutOfBoundsError.Create;

    // locate the bucket
    Bucket := nil;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
      begin
        if Index <= Bucket.BucketSize then
          Break
        else
          Dec(Index, Bucket.BucketSize);
      end;
    end;
    // when no buckets are initialized yet, create one
    if not Assigned(Bucket) then
    begin
      BucketIndex := 0;
      Bucket := BUCKETTYPENAME.Create;
      FBuckets[0] := Bucket;
    end;
    BucketCapacity := Length(Bucket.BucketElements);
    if Index >= BucketCapacity then
    begin
      ThresholdBucketCapacity := (FSize div FCapacity) + 1;
      // use the next bucket to average bucket sizes
      if (BucketCapacity >= ThresholdBucketCapacity) and (BucketIndex < (FCapacity - 1)) then
      begin
        Inc(BucketIndex);
        Dec(Index, Bucket.BucketSize);
        Bucket := FBuckets[BucketIndex];
        if not Assigned(Bucket) then
        begin
          Bucket := BUCKETTYPENAME.Create;
          FBuckets[BucketIndex] := Bucket;
        end;
      end;
    end;
    Result := Insert(Bucket, Index, PARAMETERNAME);
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.Insert(Bucket: BUCKETTYPENAME; Index: Integer; CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  BucketTest: BUCKETTYPENAME;
  BucketIndex, ItemIndex, BucketCapacity: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := FAllowDefaultElements or not ItemsEqual(PARAMETERNAME, DEFAULTVALUE);

    if Result then
    begin
      if FDuplicates <> dupAccept then
        for BucketIndex := 0 to FCapacity - 1 do
      begin
        BucketTest := FBuckets[Index];
        if Assigned(BucketTest) then
          for ItemIndex := 0 to BucketTest.BucketSize - 1 do
            if ItemsEqual(PARAMETERNAME, BucketTest.BucketElements[ItemIndex]) then
        begin
          Result := CheckDuplicate;
          Break;
        end;
      end;

      if Result then
      begin
        BucketCapacity := Length(Bucket.BucketElements);
        // grow the bucket
        if Index >= BucketCapacity then
        begin
          BucketCapacity := CalcGrowCapacity(BucketCapacity, Bucket.BucketSize);
          SetLength(Bucket.BucketElements, BucketCapacity);
        end;
        Result := Index < BucketCapacity;
        if Result then
        begin
          if Index < Bucket.BucketSize then
            MoveArray(Bucket.BucketElements, Index, Index + 1, Bucket.BucketSize - Index);
          Bucket.BucketElements[Index] := PARAMETERNAME;
          Inc(Bucket.BucketSize);
          Inc(FSize);
        end;
      end;
    end;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.InsertAll(Index: Integer; const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  It: ITRINTERFACENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := False;
    if (Index < 0) or (Index > FSize) then
      raise EJclOutOfBoundsError.Create;
    if ACollection = nil then
      Exit;

    Result := True;
    It := ACollection.Last;
    while It.HasPrevious do
      Result := Insert(Index, It.Previous) and Result;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.IsEmpty: Boolean;
begin
  Result := FSize = 0;
end;

function SELFCLASSNAME.Last: ITRINTERFACENAME;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    for BucketIndex := FCapacity - 1 downto 0 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        if Bucket.BucketSize > 0 then
      begin
        Result := ITRCLASSNAME.Create(Self, BucketIndex, Bucket.BucketSize - 1, False, isLast);
        Exit;
      end;
    end;
    Result := ITRCLASSNAME.Create(Self, 0, 0, False, isLast);
  {$JPPEXPANDMACRO READEND}
end;

function SELFCLASSNAME.LastIndexOf(CONSTKEYWORDPARAMETERNAME: TYPENAME): Integer;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Result := -1;
    for BucketIndex := FCapacity - 1 downto 0 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        for ItemIndex := Bucket.BucketSize - 1 downto 0 do
      begin
        Inc(Result);
        if ItemsEqual(PARAMETERNAME, Bucket.BucketElements[ItemIndex]) then
          Exit;
      end;
    end;
    Result := -1;
  {$JPPEXPANDMACRO READEND}
end;

procedure SELFCLASSNAME.Pack;
begin
  // TODO
end;

function SELFCLASSNAME.RaiseOutOfBoundsError: TYPENAME;
begin
  raise EJclOutOfBoundsError.Create;
end;

function SELFCLASSNAME.Remove(CONSTKEYWORDPARAMETERNAME: TYPENAME): Boolean;
var
  Extracted: TYPENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := Extract(PARAMETERNAME);
    if Result then
    begin
      Extracted := PARAMETERNAME;
      RELEASERNAME(Extracted);
    end;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.RemoveAll(const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  It: ITRINTERFACENAME;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := False;
    if ACollection = nil then
      Exit;
    Result := True;
    It := ACollection.First;
    while It.HasNext do
      Result := Remove(It.Next) and Result;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.RetainAll(const ACollection: COLLECTIONINTERFACENAME): Boolean;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    Result := False;
    if ACollection = nil then
      Exit;
    Result := True;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
        for ItemIndex := Bucket.BucketSize - 1 downto 0 do
          if not ACollection.Contains(Bucket.BucketElements[ItemIndex]) then
      begin
        RELEASERNAME(Bucket.BucketElements[ItemIndex]);
        if ItemIndex < Bucket.BucketSize - 1 then
          MoveArray(Bucket.BucketElements, ItemIndex + 1, ItemIndex, Bucket.BucketSize - ItemIndex - 1);
        Dec(Bucket.BucketSize);
        Dec(FSize);
      end;
    end;
  {$JPPEXPANDMACRO WRITEEND}
end;

procedure SELFCLASSNAME.SetCapacity(Value: Integer);
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    if FSize = 0 then
    begin
      SetLength(FBuckets, Value);
      inherited SetCapacity(Value);
    end
    else
      raise EJclOperationNotSupportedError.Create;
  {$JPPEXPANDMACRO WRITEEND}
end;

procedure SELFCLASSNAME.SETTERNAME(Index: Integer; CONSTKEYWORDPARAMETERNAME: TYPENAME);
var
  Bucket: BUCKETTYPENAME;
  ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    if (Index < 0) or (Index >= FSize) then
      raise EJclOutOfBoundsError.Create;
    TranslateIndex(Index, Bucket, ItemIndex);
    SETTERNAME(Bucket, ItemIndex, PARAMETERNAME);
  {$JPPEXPANDMACRO WRITEEND}
end;

procedure SELFCLASSNAME.SETTERNAME(Bucket: BUCKETTYPENAME; Index: Integer; CONSTKEYWORDPARAMETERNAME: TYPENAME);
var
  ReplaceItem: Boolean;
  BucketTest: BUCKETTYPENAME;
  BucketIndex, ItemIndex: Integer;
begin
  {$JPPEXPANDMACRO WRITEBEGIN}
    if (Index < 0) or (Index >= FSize) then
      raise EJclOutOfBoundsError.Create;
    ReplaceItem := FAllowDefaultElements or not ItemsEqual(PARAMETERNAME, DEFAULTVALUE);
    if ReplaceItem then
    begin
      if FDuplicates <> dupAccept then
        for BucketIndex := 0 to FCapacity - 1 do
      begin
        BucketTest := FBuckets[BucketIndex];
        if Assigned(BucketTest) then
          for ItemIndex := 0 to BucketTest.BucketSize - 1 do
            if ItemsEqual(PARAMETERNAME, BucketTest.BucketElements[ItemIndex]) then
        begin
          ReplaceItem := CheckDuplicate;
          Break;
        end;
      end;
      if (Index >= 0) and (Index < Bucket.BucketSize) then
      begin
        RELEASERNAME(Bucket.BucketElements[Index]);
        if ReplaceItem then
          Bucket.BucketElements[Index] := PARAMETERNAME
        else
        begin
          // delete item
          if Index < (Bucket.BucketSize - 1) then
            MoveArray(Bucket.BucketElements, Index + 1, Index, Bucket.BucketSize - Index);
          Dec(Bucket.BucketSize);
          Dec(FSize);
        end;
      end;
    end;
  {$JPPEXPANDMACRO WRITEEND}
end;

function SELFCLASSNAME.Size: Integer;
begin
  Result := FSize;
end;

function SELFCLASSNAME.SubList(First, Count: Integer): LISTINTERFACENAME;
var
  Bucket: BUCKETTYPENAME;
  BucketIndex, ItemIndex, I, Last: Integer;
begin
  {$JPPEXPANDMACRO READBEGIN}
    Last := First + Count - 1;
    if Last >= FSize then
      Last := FSize - 1;
    Result := CreateEmptyContainer as LISTINTERFACENAME;
    I := -1;
    for BucketIndex := 0 to FCapacity - 1 do
    begin
      Bucket := FBuckets[BucketIndex];
      if Assigned(Bucket) then
      begin
        if First < (I + Bucket.BucketSize) then
        begin
          // this bucket contains data to copy
          for ItemIndex := 0 to Bucket.BucketSize - 1 do
          begin
            Inc(I);
            if I > Last then
              // everything was added
              Break
            else
            if I >= First {and (I <= Last)} then
              Result.Add(Bucket.BucketElements[ItemIndex]);
          end;
        end
        else
          Inc(I, Bucket.BucketSize);
      end;
      if I > Last then
        // everything was added
        Break;
    end;
  {$JPPEXPANDMACRO READEND}
end;

procedure SELFCLASSNAME.TranslateIndex(Index: Integer; out Bucket: BUCKETTYPENAME; out ItemIndex: Integer);
var
  BucketIndex: Integer;
begin
  Bucket := nil;
  for BucketIndex := 0 to FCapacity - 1 do
  begin
    Bucket := FBuckets[BucketIndex];
    if Assigned(Bucket) then
    begin
      if Index < Bucket.BucketSize then
        Break
      else
        Dec(Index, Bucket.BucketSize);
    end;
  end;
  ItemIndex := Index;
  if (not Assigned(Bucket)) or (Index < 0) or (Index >= Bucket.BucketSize) then
    raise EJclOutOfBoundsError.Create;
end;*)