unit PrimeThreads;

interface

uses
  Windows, Classes, SysUtils, BoundedBuf, Forms;

type
  TIntRec = record
    Num: integer;
  end;
  PIntRec = ^TIntRec;

  TPrimeThread = class(TThread)
  private
    FBuffer: TBoundedBuffer;
  protected
    function IsPrime(TestNum: integer): boolean;
  public
    property Buffer: TBoundedBuffer read FBuffer write FBuffer;
  end;

  TForwardPrimeThread = class(TPrimeThread)
  private
  protected
    procedure SendToBackThread(TestNum: integer);
    procedure Execute; override;
  end;

  TBackwardPrimeThread = class(TPrimeThread)
  private
    FDestSection: PRTLCriticalSection;
    FDestMsgNum: integer;
    FDestForm: TForm;
    FDestList: TStrings;
  protected
    function ReverseNumber(Input: integer): integer;
    function RecieveFromForwardThread(var TestNum: integer): boolean;
    procedure SendToVCLThread(CurrentNumber, ReversedNumber: integer);
    procedure Execute; override;
  public
    property DestSection: PRTLCriticalSection read FDestSection write FDestSection;
    property DestMsgNum: integer read FDestMsgNum write FDestMsgNum;
    property DestForm: TForm read FDestForm write FDestForm;
    property DestList: TStrings read FDestList write FDestList;
  end;

var
  ForwardThread: TForwardPrimeThread;
  BackwardThread: TBackwardPrimeThread;
  Buffer: TBoundedBuffer;

procedure StartThreads(Form: TForm;
  Section: PRTLCriticalSection;
  MsgNum: integer;
  List: TStrings);
procedure StopThreads;

implementation

const
  DefBufSize = 16;

{ Ancillary procedures }

procedure StartThreads(Form: TForm;
  Section: PRTLCriticalSection;
  MsgNum: integer;
  List: TStrings);
begin
  ForwardThread := TForwardPrimeThread.Create(true);
  BackwardThread := TBackwardPrimeThread.Create(true);
  SetThreadPriority(ForwardThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
  SetThreadPriority(BackwardThread.Handle, THREAD_PRIORITY_BELOW_NORMAL);
  Buffer := TBoundedBuffer.Create;
  Buffer.Size := DefBufSize;
  ForwardThread.Buffer := Buffer;
  BackwardThread.Buffer := Buffer;
  with BackwardThread do
  begin
    DestForm := Form;
    DestSection := Section;
    DestMsgNum := MsgNum;
    DestList := List;
  end;
  ForwardThread.Resume;
  BackwardThread.Resume;
end;

procedure StopThreads;
begin
  ForwardThread.Terminate;
  BackwardThread.Terminate;
  Buffer.ResetState;
  ForwardThread.WaitFor;
  BackwardThread.WaitFor;
  Buffer.Free;
  ForwardThread.Free;
  BackwardThread.Free;
end;

{ TPrimeThread }

function TPrimeThread.IsPrime(TestNum: integer): boolean;

var
  iter: integer;

begin
  result := true;
  if TestNum < 0 then
    result := false;
  if TestNum <= 2 then
    exit;
  iter := 2;
  while (iter < TestNum) and (not terminated) do {Line A}
  begin
    if (TestNum mod iter) = 0 then
    begin
      result := false;
      exit;
    end;
    Inc(iter);
  end;
end;

{ TForwardPrimeThread }

procedure TForwardPrimeThread.SendToBackThread(TestNum: integer);

var
  NewRec: PIntRec;

begin
  New(NewRec);
  NewRec.Num := TestNum;
  if not Buffer.PutItem(NewRec) then Dispose(NewRec);
end;

procedure TForwardPrimeThread.Execute;

var
  CurrentNumber: integer;

begin
  CurrentNumber := 2;
  while not Terminated do
  begin
    if IsPrime(CurrentNumber) then
      SendToBackThread(CurrentNumber);
    Inc(CurrentNumber);
  end;
end;

{ TBackwardPrimeThread }

function TBackwardPrimeThread.RecieveFromForwardThread(var TestNum: integer): boolean;

var
  NewRec: PIntRec;

begin
  NewRec := Buffer.GetItem;
  Result := Assigned(NewRec);
  if Result then TestNum := NewRec^.Num;
end;

procedure TBackwardPrimeThread.SendToVCLThread(CurrentNumber, ReversedNumber: integer);

var
  Msg: string;

begin
  Msg := 'Palindromic primes: ' + IntToStr(CurrentNumber) + ' and '
    + IntToStr(ReversedNumber);
  EnterCriticalSection(FDestSection^);
  DestList.Add(Msg);
  LeaveCriticalSection(FDestSection^);
  PostMessage(DestForm.Handle, DestMsgNum, 0, 0);
end;

function TBackwardPrimeThread.ReverseNumber(Input: integer): integer;

var
  InStr, OutStr: string;
  Len, Iter: integer;

begin
  Input := Abs(Input);
  InStr := IntToStr(Input);
  OutStr := '';
  Len := Length(InStr);
  for Iter := Len downto 1 do
    OutStr := OutStr + InStr[Iter];
  try
    Result := StrToInt(OutStr);
  except
    on EConvertError do Result := Input;
  end;
end;

procedure TBackwardPrimeThread.Execute;

var
  CurrentNumber,
    ReversedNumber: integer;

begin
  while not Terminated do
  begin
    if RecieveFromForwardThread(CurrentNumber) then
    begin
      ReversedNumber := ReverseNumber(CurrentNumber);
      if IsPrime(ReversedNumber) then
        SendToVCLThread(CurrentNumber, ReversedNumber);
    end;
  end;
end;

end.