unit QuadraticEquationSolver;
interface
uses
SysUtils;
type
EInvalidAccessException =
class(
Exception);
TQuadraticEquationSolutionType = (stUndefined, stReal, stSingle, stComplex);
TQuadraticEquationSolver =
class
private
FSolutionType: TQuadraticEquationSolutionType;
FSolution2: Double;
FDiscriminant: Double;
FSolution1: Double;
FRealPart: Double;
FComplexPart: Double;
procedure SetSingleSolution(solution: Double);
procedure SetRealSolutions(solution1, solution2: Double);
procedure SetComplexSolution(realPart, complexPart: Double);
procedure SolveUsingVietaFormula(p, q: Double);
procedure SolveUsingTransformedFormula(p, q: Double);
function IsAPositiveAndVeryLargeNumber(n: Double): Boolean;
function GetSolution1: Double;
function GetSolution2: Double;
function GetRealPartOfSolution: Double;
function GetComplexPartOfSolution: Double;
public
constructor Create;
procedure Solve(a, b, c: Double);
property SolutionType: TQuadraticEquationSolutionType
read FSolutionType;
property Solution1: Double
read GetSolution1;
property Solution2: Double
read GetSolution2;
property RealPartOfComplexSolition: Double
read GetRealPartOfSolution;
property ComplexPartOfComplexsolution: Double
read GetComplexPartOfSolution;
end;
implementation
uses Math;
{ TQuadraticEquationSolver }
{$Region 'Solving and Mathematics'}
procedure TQuadraticEquationSolver.Solve(a, b, c: Double);
var
p,q : Double;
begin
Assert (
Not IsZero(a), '
a must be nonzero');
p := b / a;
q := c / a;
if IsAPositiveAndVeryLargeNumber(p)
then
SolveUsingTransformedFormula(p, q)
else begin
FDiscriminant := sqr(p / 2) - q;
case Sign(FDiscriminant)
of
+1: SolveUsingVietaFormula(p, q);
0 : SetSingleSolution(-p / 2);
-1: SetComplexSolution(-p / 2, Sqrt(abs(FDiscriminant)));
end;
end
end;
procedure TQuadraticEquationSolver.SolveUsingVietaFormula(p, q: Double);
var
solution: Double;
begin
solution := -p / 2 - sign(p) * sqrt(FDiscriminant);
SetRealSolutions(solution, q / solution);
end;
procedure TQuadraticEquationSolver.SolveUsingTransformedFormula(p, q: Double);
var
root: Double;
begin
FDiscriminant := (1 / 4 - (q / p) / p);
root := sqrt(FDiscriminant);
SetRealSolutions(abs(p) - root, Abs(p) + root);
end;
function TQuadraticEquationSolver.IsAPositiveAndVeryLargeNumber(n: Double): Boolean;
begin
Result := (abs(n) > sqrt(Math.MaxDouble));
end;
{$EndRegion}
{$Region 'Routines for setting the individual solutions'}
procedure TQuadraticEquationSolver.SetComplexSolution(realPart, complexPart: Double);
begin
FSolutionType := stComplex;
FRealPart := realPart;
FComplexPart := complexPart;
end;
procedure TQuadraticEquationSolver.SetRealSolutions(solution1, solution2: Double);
begin
FSolutionType := stReal;
FSolution1 := solution1;
FSolution2 := solution2;
end;
procedure TQuadraticEquationSolver.SetSingleSolution(solution: Double);
begin
FSolutionType := stSingle;
FSolution1 := solution;
FSolution2 := solution;
end;
{$EndRegion}
{$Region 'Property access'}
function TQuadraticEquationSolver.GetSolution1: Double;
begin
if SolutionType = stComplex
then
raise EInvalidAccessException.Create('
Access only if solution is not complex');
Result := FSolution1;
end;
function TQuadraticEquationSolver.GetSolution2: Double;
begin
if SolutionType = stComplex
then
raise EInvalidAccessException.Create('
Access only if solution is not complex');
Result := FSolution2;
end;
function TQuadraticEquationSolver.GetRealPartOfSolution: Double;
begin
if SolutionType <> stComplex
then
raise EInvalidAccessException.Create('
Access only if solution is complex');
Result := FRealPart;
end;
constructor TQuadraticEquationSolver.Create;
begin
FSolutionType := stUndefined;
end;
function TQuadraticEquationSolver.GetComplexPartOfSolution: Double;
begin
result := FComplexPart;
end;
{$EndRegion}
end.