ChatGPT code generation

I asked ChatGPT to “Write pascal code to manage a sorted symbol table using two tables the main table and a secondary table, when the secondary table becomes more then 10% of the main table do a merge sort into the main table”

This is something I wrote back in the late eighties, and aside from a few syntax errors which ChatGPT corrected when asked it compiled and ran perfectly

program SymbolTableManager;

uses
  SysUtils;

const
  MaxSize = 1000;

type
  Symbol = record
    Key: string[30];
    Value: string[100];
  end;

  SymbolTable = record
    Data: array[1..MaxSize] of Symbol;
    Count: Integer;
  end;

var
  MainTable, SecondaryTable: SymbolTable;

procedure InitTable(var Table: SymbolTable);
begin
  Table.Count := 0;
end;

function CompareSymbols(const A, B: Symbol): Integer;
begin
  if A.Key < B.Key then
    CompareSymbols := -1
  else if A.Key > B.Key then
    CompareSymbols := 1
  else
    CompareSymbols := 0;
end;

procedure AddToSecondaryTableSorted(Key, Value: string);
var
  i, pos: Integer;
  NewSymbol: Symbol;
begin
  if SecondaryTable.Count >= MaxSize then
  begin
    writeln('Secondary Table is full!');
    Exit;
  end;

  // Create the new symbol
  NewSymbol.Key := Key;
  NewSymbol.Value := Value;

  // Find insertion position
  pos := SecondaryTable.Count + 1;
  for i := SecondaryTable.Count downto 1 do
  begin
    if CompareSymbols(SecondaryTable.Data[i], NewSymbol) > 0 then
    begin
      SecondaryTable.Data[i + 1] := SecondaryTable.Data[i]; // shift right
      pos := i;
    end
    else
      Break;
  end;

  // Insert the new symbol
  SecondaryTable.Data[pos] := NewSymbol;
  Inc(SecondaryTable.Count);
end;

procedure MergeTables;
var
  TempTable: SymbolTable;
  i, j, k: Integer;
begin
  writeln('Merging tables...');

  i := 1;
  j := 1;
  k := 0;

  while (i <= MainTable.Count) and (j <= SecondaryTable.Count) do
  begin
    if CompareSymbols(MainTable.Data[i], SecondaryTable.Data[j]) <= 0 then
    begin
      Inc(k);
      TempTable.Data[k] := MainTable.Data[i];
      Inc(i);
    end
    else
    begin
      Inc(k);
      TempTable.Data[k] := SecondaryTable.Data[j];
      Inc(j);
    end;
  end;

  while i <= MainTable.Count do
  begin
    Inc(k);
    TempTable.Data[k] := MainTable.Data[i];
    Inc(i);
  end;

  while j <= SecondaryTable.Count do
  begin
    Inc(k);
    TempTable.Data[k] := SecondaryTable.Data[j];
    Inc(j);
  end;

  TempTable.Count := k;
  MainTable := TempTable;
  InitTable(SecondaryTable);
end;

procedure InsertSymbol(Key, Value: string);
begin
  AddToSecondaryTableSorted(Key, Value);

  if MainTable.Count = 0 then
    Exit;

  if SecondaryTable.Count > (MainTable.Count div 10) then
    MergeTables;
end;

procedure PrintTable(const Table: SymbolTable; const LabelText: string);
var
  i: Integer;
begin
  writeln(LabelText);
  for i := 1 to Table.Count do
    writeln(i:3, ': ', Table.Data[i].Key, ' -> ', Table.Data[i].Value);
end;

procedure QuickSort(var A: array of Symbol; L, R: Integer);
var
  i, j: Integer;
  pivot, tmp: Symbol;
begin
  i := L;
  j := R;
  pivot := A[(L + R) div 2];

  repeat
    while CompareSymbols(A[i], pivot) < 0 do Inc(i);
    while CompareSymbols(A[j], pivot) > 0 do Dec(j);

    if i <= j then
    begin
      tmp := A[i];
      A[i] := A[j];
      A[j] := tmp;
      Inc(i);
      Dec(j);
    end;
  until i > j;

  if L < j then QuickSort(A, L, j);
  if i < R then QuickSort(A, i, R);
end;

procedure PreloadMainTable;
var
  i: Integer;
begin
  for i := 1 to 20 do
  begin
    Inc(MainTable.Count);
    with MainTable.Data[MainTable.Count] do
    begin
      Key := 'Key' + Chr(64 + i); // KeyA, KeyB, ...
      Value := 'Value' + IntToStr(i);
    end;
  end;

  QuickSort(MainTable.Data, 1, MainTable.Count);
end;

begin
  InitTable(MainTable);
  InitTable(SecondaryTable);

  PreloadMainTable;

  writeln('Initial Main Table:');
  PrintTable(MainTable, 'Main Table:');

  // Insert symbols to Secondary Table
  InsertSymbol('KeyM', 'ExtraM');
  InsertSymbol('KeyZ', 'ExtraZ');
  InsertSymbol('KeyA', 'ExtraA');
  InsertSymbol('KeyC', 'ExtraC');
  InsertSymbol('KeyY', 'ExtraY');
  InsertSymbol('KeyG', 'ExtraG');
  InsertSymbol('KeyX', 'ExtraX');
  InsertSymbol('KeyH', 'ExtraH');
  InsertSymbol('KeyW', 'ExtraW');
  InsertSymbol('KeyE', 'ExtraE');
  InsertSymbol('KeyT', 'ExtraT');  // Should trigger merge

  writeln;
  PrintTable(MainTable, 'Main Table After Merge:');
end.

After some more prompting we have a program that uses a memory mapped file and supports up to 1 million records in the symbol table.

program MMapSymbolTable;

uses
  BaseUnix, SysUtils;

const
  FileName = 'symbols.mmap';
  MaxSymbols = 1000000;

type
  Symbol = packed record
    Key: string[30];
    Value: string[100];
    Deleted: Boolean;
  end;

  PSymbol = ^Symbol;

  SymbolHeader = packed record
    MainCount: Int64;
    SecondaryCount: Int64;
  end;

  PSymbolHeader = ^SymbolHeader;

procedure Merge; forward;

var
  fd: cint;
  MapSize: cint;
  FilePtr: Pointer;
  Header: PSymbolHeader;
  Records: PSymbol;

procedure EnsureFileSize(Filename: string; Size: cint);
var
  f: file;
  Dummy: Byte = 0;
begin
  Assign(f, Filename);
  {$I-} Reset(f, 1); {$I+}
  if IOResult <> 0 then Rewrite(f, 1);
  Seek(f, Size - 1);
  BlockWrite(f, Dummy, 1);
  Close(f);
end;

procedure MMapFile;
begin
  MapSize := SizeOf(SymbolHeader) + SizeOf(Symbol) * MaxSymbols;
  EnsureFileSize(FileName, MapSize);

  fd := fpopen(FileName, O_RDWR);
  if fd = -1 then
  begin writeln('Failed to open file'); Halt(1); end;

  FilePtr := fpmmap(nil, MapSize, PROT_READ or PROT_WRITE, MAP_SHARED, fd, 0);
  if PtrUInt(FilePtr) = PtrUInt(MAP_FAILED) then
  begin writeln('mmap failed'); Halt(2); end;

  Header := PSymbolHeader(FilePtr);
  Records := PSymbol(Pointer(PChar(FilePtr) + SizeOf(SymbolHeader)));

  if (Header^.MainCount = 0) and (Header^.SecondaryCount = 0) then
  begin
    Header^.MainCount := 0;
    Header^.SecondaryCount := 0;
  end;
end;

procedure UnmapAndClose;
begin
  fpmunmap(FilePtr, MapSize);
  fpclose(fd);
end;

procedure InsertSortedIntoSecondary(Key, Value: string);
var
  i, insertPos, secStart, secEnd: Int64;
  totalCount: Int64;
  NewSym: Symbol;
begin
  totalCount := Header^.MainCount + Header^.SecondaryCount;
  if totalCount >= MaxSymbols then
  begin writeln('Table full'); Exit; end;

  NewSym.Key := Key;
  NewSym.Value := Value;
  NewSym.Deleted := False;

  secStart := Header^.MainCount;
  secEnd := secStart + Header^.SecondaryCount;

  // Find insert position in secondary
  insertPos := secEnd;
  for i := secStart to secEnd - 1 do
  begin
    if Records[i].Key > Key then
    begin
      insertPos := i;
      Break;
    end;
  end;

  // Shift everything from insertPos to end of secondary forward
  for i := secEnd - 1 downto insertPos do
    Records[i + 1] := Records[i];

  // Insert new record
  Records[insertPos] := NewSym;
  Inc(Header^.SecondaryCount);

  // Auto-merge if secondary > 5% of main
  if (Header^.SecondaryCount > 1000) and (Header^.MainCount > 0) and (Header^.SecondaryCount * 100 > Header^.MainCount * 5) then
  begin
    writeln('Auto-merging: secondary is greater than 1000 and more than 5% of main');
    Merge;
      Merge;
  end;
end;



procedure PrintTable;
var
  i: Integer;
begin
  writeln('--- Main Region (Sorted) ---');
  for i := 0 to Header^.MainCount - 1 do
    if not Records[i].Deleted then
      writeln(i:3, ': ', Records[i].Key, ' -> ', Records[i].Value);

  writeln('--- Secondary Region (Sorted) ---');
  for i := Header^.MainCount to Header^.MainCount + Header^.SecondaryCount - 1 do
    if not Records[i].Deleted then
      writeln(i:3, ': ', Records[i].Key, ' -> ', Records[i].Value);
end;

procedure Merge;
var
  All: array of Symbol;
  Count, i, j: NativeInt;
  temp: Symbol;
begin
  Count := 0;
  SetLength(All, 0);
  for i := 0 to Header^.MainCount + Header^.SecondaryCount - 1 do
    if not Records[i].Deleted then
    begin
      SetLength(All, Length(All) + 1);
      All[High(All)] := Records[i];
    end;
  Count := Length(All);

  SetLength(All, Count);

  for i := 1 to Count - 1 do
  begin
    temp := All[i];
    j := i - 1;
    while (j >= 0) and (All[j].Key > temp.Key) do
    begin
      All[j + 1] := All[j];
      Dec(j);
    end;
    All[j + 1] := temp;
  end;

  for i := 0 to Count - 1 do
    Records[i] := All[i];

  for i := Count to Header^.MainCount + Header^.SecondaryCount - 1 do
  begin
    Records[i].Key := '';
    Records[i].Value := '';
    Records[i].Deleted := True;
  end;

  Header^.MainCount := Count;
  Header^.SecondaryCount := 0;

  writeln('Merge complete. MainCount = ', Header^.MainCount);
end;

procedure DeleteSymbol(Key: string);
var
  i, total: Int64;
begin
  total := Header^.MainCount + Header^.SecondaryCount;
  for i := 0 to total - 1 do
    if (Records[i].Key = Key) and (not Records[i].Deleted) then
    begin
      Records[i].Deleted := True;
      writeln('Deleted: ', Key);
      Exit;
    end;
  writeln('Not found: ', Key);
end;

procedure SearchSymbol(Key: string);
var
  L, R, M: Int64;
begin
  // Search Main Region
  L := 0;
  R := Header^.MainCount - 1;
  while L <= R do
  begin
    M := (L + R) div 2;
    if Records[M].Deleted then begin R := M - 1; Continue; end;
    if Records[M].Key = Key then
    begin writeln('[MAIN] Found: ', Records[M].Key, ' -> ', Records[M].Value); Exit; end
    else if Records[M].Key < Key then
      L := M + 1
    else
      R := M - 1;
  end;

  // Search Secondary Region
  L := Header^.MainCount;
  R := Header^.MainCount + Header^.SecondaryCount - 1;
  while L <= R do
  begin
    M := (L + R) div 2;
    if Records[M].Deleted then begin R := M - 1; Continue; end;
    if Records[M].Key = Key then
    begin writeln('[SECONDARY] Found: ', Records[M].Key, ' -> ', Records[M].Value); Exit; end
    else if Records[M].Key < Key then
      L := M + 1
    else
      R := M - 1;
  end;

  writeln('Not found: ', Key);
end;

procedure Demo;
begin
  InsertSortedIntoSecondary('KeyM', 'ValueM');
  InsertSortedIntoSecondary('KeyB', 'ValueB');
  InsertSortedIntoSecondary('KeyD', 'ValueD');
  InsertSortedIntoSecondary('KeyE', 'ValueE');
  InsertSortedIntoSecondary('KeyF', 'ValueF');
  InsertSortedIntoSecondary('KeyG', 'ValueG');
  InsertSortedIntoSecondary('KeyH', 'ValueH');
  InsertSortedIntoSecondary('KeyI', 'ValueI');
  InsertSortedIntoSecondary('KeyJ', 'ValueJ');
  InsertSortedIntoSecondary('KeyK', 'ValueK');
  InsertSortedIntoSecondary('KeyA', 'ValueA');
  InsertSortedIntoSecondary('KeyZ', 'ValueZ');
  InsertSortedIntoSecondary('KeyC', 'ValueC');

  writeln;
  PrintTable;
  writeln;

  Merge;
  PrintTable;
  writeln;

  SearchSymbol('KeyM');
  DeleteSymbol('KeyM');
  writeln;

  PrintTable;
end;

begin
  MMapFile;
  Demo;
  UnmapAndClose;
end.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.