Ada Programming/Mathematical calculations

Ada is very well suited for all kinds of calculations. You can define your own fixed point and floating point types and &mdash; with the aid of generic packages call all the mathematical functions you need. In that respect Ada is on par with Fortran. This module will show you how to use them and while we progress we create a simple RPN calculator.

Addition
Additions can be done using the predefined operator. The operator is predefined for all numeric types and the following, working code, demonstrates its use:

; Numeric_1 Value_Type  12 -999_999_999_999.0e999 .. 999_999_999_999.0e999; T_IO Ada.Text_IO; F_IO   Ada.Text_IO.Float_IO (Value_Type); Value_1 : Value_Type; Value_2 : Value_Type; T_IO.Put ("First Value : "); F_IO.Get (Value_1); T_IO.Put ("Second Value : "); F_IO.Get (Value_2); F_IO.Put (Value_1); T_IO.Put (" + "); F_IO.Put (Value_2); T_IO.Put (" = "); F_IO.Put (Value_1 Value_2); Numeric_1;

Subtraction
Subtractions can be done using the predefined operator. The following extended demo shows the use of + and - operator together:

Numeric_2 Value_Type 12     -999_999_999_999.0e999 .. 999_999_999_999.0e999; T_IO ; F_IO   Ada.Text_IO.Float_IO (Value_Type); Value_1  : Value_Type; Value_2  : Value_Type; Result   : Value_Type; Operation : Character; T_IO.Put ("First Value : "); F_IO.Get (Value_1); T_IO.Put ("Second Value : "); F_IO.Get (Value_2); T_IO.Put ("Operation   : "); T_IO.Get (Operation); Operation '+' =>        Result := Value_1 + Value_2; '-' =>        Result := Value_1 - Value_2; =>        T_IO.Put_Line ("Illegal Operation."); Exit_Numeric_2; ;  F_IO.Put (Value_1); T_IO.Put (" "); T_IO.Put (Operation); T_IO.Put (" "); F_IO.Put (Value_2); T_IO.Put (" = "); F_IO.Put (Result); Exit_Numeric_2 ; Numeric_2;

Purists might be surprised about the use of goto &mdash; but some people prefer the use of goto over the use of multiple return statements if inside functions &mdash; given that, the opinions on this topic vary strongly. See the isn't goto evil article.

Multiplication
Multiplication can be done using the predefined operator. For a demo see the next chapter about Division.

Division
Divisions can be done using the predefined operators, ,. The operator performs a normal division,  returns a modulus division and  returns the remainder of the modulus division.

The following extended demo shows the use of the, , and  operators together as well as the use of a four number wide stack to store intermediate results:

The operators and  are not part of the demonstration as they are only defined for integer types.

; Numeric_3 Pop_Value; Push_Value; Value_Type  12 -999_999_999_999.0e999 .. 999_999_999_999.0e999; Value_Array  (Natural  1 .. 4)  Value_Type; T_IO Ada.Text_IO; F_IO  Ada.Text_IO.Float_IO (Value_Type); Values   : Value_Array := ( => 0.0); Operation : String (1 .. 40); Last     : Natural; Pop_Value Values (Values' + 1 .. Values') := Values (Values' + 2 .. Values') & 0.0; Pop_Value; Push_Value Values (Values' + 1 .. Values') := Values (Values' .. Values' - 1); Push_Value; Main_Loop: T_IO.Put (">"); T_IO.Get_Line (Operation, Last); Last = 1  Operation (1) = '+' Values (1) := Values (1) + Values (2); Pop_Value; Last = 1  Operation (1) = '-' Values (1) := Values (1) + Values (2); Pop_Value; Last = 1  Operation (1) = '*' Values (1) := Values (1) * Values (2); Pop_Value; Last = 1  Operation (1) = '/' Values (1) := Values (1) / Values (2); Pop_Value; Last = 4  Operation (1 .. 4) = "exit" Main_Loop; Push_Value; F_IO.Get (From => Operation, Item => Values (1), Last => Last); ;      Display_Loop: I  Value_Array' F_IO.Put (Item => Values (I),            Fore => F_IO.Default_Fore,             Aft  => F_IO.Default_Aft,             Exp  => 4); T_IO.New_Line; Display_Loop; Main_Loop; ; Numeric_3;

Exponential calculations
All exponential functions are defined inside the generic package.

Power of
Calculation of the form $$x^y$$ are performed by the operator. Beware: There are two versions of this operator. The predefined operator allows only for Standard.Integer to be used as exponent. If you need to use a floating point type as exponent you need to use the defined in.

Root
The square root $$ \sqrt{x} $$ is calculated with the function. There is no function defined to calculate an arbitrary root $$\sqrt[n]{x}$$. However you can use logarithms to calculate an arbitrary root using the mathematical identity: $$ \sqrt[b]{a} = e^{\log_e (a) / b} $$ which will become  in Ada. Alternatively, use $$\sqrt[b]{a}=a^{\frac1b}$$ which, in Ada, is.

Logarithm
defines a function for both the arbitrary logarithm $$log_n(x)$$ and the natural logarithm $$log_e(x)$$, both of which have the same name  distinguished by the number of parameters.

Demonstration
The following extended demo shows the how to use the exponential functions in Ada. The new demo also uses Unbounded_String instead of Strings which make the comparisons easier.

Please note that from now on we won't copy the full sources any more. Do follow the download links to see the full program.

; ;  ;  Numeric_4 Str Ada.Strings.Unbounded; T_IO Ada.Text_IO; Pop_Value; Push_Value; Get_Line Str.Unbounded_String; Value_Type  12 -999_999_999_999.0e999 .. 999_999_999_999.0e999; Value_Array  (Natural  1 .. 4)  Value_Type; F_IO  Ada.Text_IO.Float_IO (Value_Type); Value_Functions  Ada.Numerics.Generic_Elementary_Functions (      Value_Type); Value_Functions; Str.Unbounded_String; Values   : Value_Array := ( => 0.0); Operation : Str.Unbounded_String; Dummy    : Natural; Get_Line Str.Unbounded_String BufferSize : := 2000; Retval    : Str.Unbounded_String := Str.Null_Unbounded_String; Item      : String (1 .. BufferSize); Last      : Natural; Get_Whole_Line : T_IO.Get_Line (Item => Item, Last => Last); Str.Append (Source => Retval, New_Item => Item (1 .. Last)); Get_Whole_Line Last < Item'; Get_Whole_Line; Retval; Get_Line; ...  Main_Loop : T_IO.Put (">"); Operation := Get_Line; ...         Operation = "e" Push_Value; Values (1) := Ada.Numerics.e;         Operation = "**"   Operation = "^" Values (1) := Values (1) ** Values (2); Pop_Value; Operation = "sqr" Values (1) := Sqrt (Values (1)); Operation = "root" Values (1) := Exp (Log (Values (2)) / Values (1)); Pop_Value; Operation = "ln" Values (1) := Log (Values (1)); Operation = "log" Values (1) := Log (Base => Values (1), X => Values (2)); Pop_Value; Operation = "" Main_Loop; Push_Value; F_IO.Get (From => Str.To_String (Operation),              Item => Values (1),               Last => Dummy); ; ...       Main_Loop; ; Numeric_4;

Trigonometric calculations
The full set of trigonometric functions are defined inside the generic package. All functions are defined for 2π and an arbitrary cycle value (a full cycle of revolution).

Please note the difference of calling the  function.

; ;  ;  Numeric_5 ...   Put_Line (Value :  Value_Type); Value_Functions; Str.Unbounded_String; Values   : Value_Array := ( => 0.0); Cycle    : Value_Type  := Ada.Numerics.Pi; Operation : Str.Unbounded_String; Dummy    : Natural; ...   Put_Line (Value :  Value_Type) Value_Type' (Value) >= Value_Type' (10.0 ** F_IO.Default_Aft) F_IO.Put (Item => Value,           Fore => F_IO.Default_Aft,            Aft  => F_IO.Default_Aft,            Exp  => 4); F_IO.Put (Item => Value,           Fore => F_IO.Default_Aft,            Aft  => F_IO.Default_Aft,            Exp  => 0); ;     T_IO.New_Line; ;   Put_Line; ...  Main_Loop : Display_Loop : I   Value_Array' Put_Line (Values (I)); Display_Loop; T_IO.Put (">"); Operation := Get_Line; ...         Operation = "deg" Cycle := 360.0; Operation = "rad" Cycle := Ada.Numerics.Pi; Operation = "grad" Cycle := 400.0; Operation = "pi"  Operation = "π" Push_Value; Values (1) := Ada.Numerics.Pi; Operation = "sin" Values (1) := Sin (X => Values (1), Cycle => Cycle); Operation = "cos" Values (1) := Cos (X => Values (1), Cycle => Cycle); Operation = "tan" Values (1) := Tan (X => Values (1), Cycle => Cycle); Operation = "cot" Values (1) := Cot (X => Values (1), Cycle => Cycle); Operation = "asin" Values (1) := Arcsin (X => Values (1), Cycle => Cycle); Operation = "acos" Values (1) := Arccos (X => Values (1), Cycle => Cycle); Operation = "atan" Values (1) := Arctan (Y => Values (1), Cycle => Cycle); Operation = "acot" Values (1) := Arccot (X => Values (1), Cycle => Cycle); ...       Main_Loop; ; Numeric_5;

The Demo also contains an improved numeric output which behaves more like a normal calculator.

Hyperbolic calculations
You guessed it: The full set of hyperbolic functions is defined inside the generic package.

; ;  ;  ;  Numeric_6 Str Ada.Strings.Unbounded; T_IO Ada.Text_IO; Exept Ada.Exceptions; ...  Main_Loop : Try : Display_Loop : ...               Operation = "sinh" Values (1) := Sinh (Values (1)); Operation = "cosh" Values (1) := Coth (Values (1)); Operation = "tanh" Values (1) := Tanh (Values (1)); Operation = "coth" Values (1) := Coth (Values (1)); Operation = "asinh" Values (1) := Arcsinh (Values (1)); Operation = "acosh" Values (1) := Arccosh (Values (1)); Operation = "atanh" Values (1) := Arctanh (Values (1)); Operation = "acoth" Values (1) := Arccoth (Values (1)); ...               An_Exception :  => T_IO.Put_Line (Exept.Exception_Information (An_Exception)); Try; Main_Loop; ; Numeric_6;

As added bonus this version supports error handling and therefore won't just crash when an illegal calculation is performed.

Complex arithmethic
For complex arithmetic Ada provides the package. This package is part of the "special need Annexes" which means it is optional. The open source Ada compiler GNAT implements all "special need Annexes" and therefore has complex arithmetic available.

Since Ada supports user defined operators, all operators have their usual meaning as soon as the package  has been instantiated ( ...   ...) and the type has been made visible (  ...)

Ada also provides the packages and  which provide similar functionality to their normal counterparts. But there are some differences:


 * supports only the exponential and trigonometric functions which make sense in complex arithmetic.


 * is a child package of and therefore needs its own . Note: the   function is pretty fault tolerant - if you forget the "," or the "" pair it will still parse the input correctly.

So, with only a very few modifications you can convert your "normals" calculator to a calculator for complex arithmetic:

Ada.Text_IO.Complex_IO; Ada.Numerics.Generic_Complex_Types; Ada.Numerics.Generic_Complex_Elementary_Functions; Ada.Strings.Unbounded; Ada.Exceptions; Numeric_7 ...   Complex_Types   Ada.Numerics.Generic_Complex_Types (      Value_Type); Complex_Functions Ada.Numerics.Generic_Complex_Elementary_Functions (     Complex_Types); C_IO  Ada.Text_IO.Complex_IO (Complex_Types); Value_Array (Natural 1 .. 4)  Complex_Types.Complex; Put_Line (Value : Complex_Types.Complex); Complex_Types.Complex; Str.Unbounded_String; Complex_Functions; Values   : Value_Array := ( => Complex_Types.Complex'(Re => 0.0, Im => 0.0)); ...   Put_Line (Value :  Complex_Types.Complex) ( Value_Type' (Value.Re) >=          Value_Type' (10.0 ** C_IO.Default_Aft)) ( Value_Type' (Value.Im) >=                 Value_Type' (10.0 ** C_IO.Default_Aft)) C_IO.Put (Item => Value,           Fore => C_IO.Default_Aft,            Aft  => C_IO.Default_Aft,            Exp  => 4); C_IO.Put (Item => Value,           Fore => C_IO.Default_Aft,            Aft  => C_IO.Default_Aft,            Exp  => 0); ;     T_IO.New_Line; ;   Put_Line; ...               Operation = "e" Push_Value; Values (1) := Complex_Types.Complex'(Re => Ada.Numerics.e, Im => 0.0); ...               Operation = "pi"   Operation = "π" Push_Value; Values (1) := Complex_Types.Complex'(Re => Ada.Numerics.Pi, Im => 0.0); Operation = "sin" Values (1) := Sin (Values (1)); Operation = "cos" Values (1) := Cot (Values (1)); Operation = "tan" Values (1) := Tan (Values (1)); Operation = "cot" Values (1) := Cot (Values (1)); Operation = "asin" Values (1) := Arcsin (Values (1)); Operation = "acos" Values (1) := Arccos (Values (1)); Operation = "atan" Values (1) := Arctan (Values (1)); Operation = "acot" Values (1) := Arccot (Values (1)); ...  ;  Numeric_7;

Vector and Matrix Arithmetic
Ada supports vector and matrix Arithmetic for both normal real types and complex types. For those, the generic packages Ada.Numerics.Generic_Real_Arrays and Ada.Numerics.Generic_Complex_Arrays are used. Both packages offer the usual set of operations, however there is no I/O package and understandably, no package for elementary functions.

Since there is no I/O package for vector and matrix I/O creating a demo is by far more complex &mdash; and hence not ready yet. You can have a look at the current progress which will be a universal calculator merging all feature.

Status: Stalled - for a Vector and Matrix stack we need Indefinite_Vectors &mdash; which are currently not part of GNAT/Pro. Well I could use the booch components ...

Wikibook

 * Ada Programming
 * Ada Programming/Delimiters/-
 * Ada Programming/Libraries/Ada.Numerics.Generic Complex Types
 * Ada Programming/Libraries/Ada.Numerics.Generic Elementary Functions

Ada 2005 Reference Manual


|Mathematical calculations