comp.lang.ada
 help / color / mirror / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* Generic library design
@ 2018-04-06 13:22  5% Marius Amado-Alves
  0 siblings, 0 replies; 35+ results
From: Marius Amado-Alves @ 2018-04-06 13:22 UTC (permalink / raw)


Hello hive mind. Designing a few generic libraries involving real and complex number types and arrays thereof. Undecided as to the type of generic parameter(s). Namely:

When should the parameter(s) be the base real type like (examples drawn from the standard, please extrapolate)

   generic
      type Real is digits <>;
   package Ada.Numerics.Generic_Real_Arrays is
   ...

with the necessary sidekicks e.g. Generic_Elementary_Functions instantiated internally,

when should it be one or more packages like 

   generic
      with package Real_Arrays   is new
         Ada.Numerics.Generic_Real_Arrays   (<>);
      use Real_Arrays;
      with package Complex_Types is new
         Ada.Numerics.Generic_Complex_Types (Real);
      use Complex_Types;
   package Ada.Numerics.Generic_Complex_Arrays is
   ...

with the set or a subset of the necessary entities "pre-instantiated" by the user? Is there a design "rule"?

Consider the running example: why is Ada.Numerics.Generic_Complex_Arrays parametrized by packages, instead of just a Real type and then instantiate the packages intarnally?

And are compilers smart enough to merge identical instances w.r.t. to their generic arguments? Maybe with help of qualification pragmas telling there is no state?

No need to answer to all questions at once:-)

Thanks a lot.


^ permalink raw reply	[relevance 5%]

* Re: Is this a bug in Ada.Numerics?
  2012-10-21  2:32  6%   ` Leo Brewin
@ 2012-10-21  3:39  0%     ` Ludovic Brenta
  2012-10-23 15:48  0%       ` Vincent Marciante
  2012-10-23 22:52  5%       ` Leo Brewin
  0 siblings, 2 replies; 35+ results
From: Ludovic Brenta @ 2012-10-21  3:39 UTC (permalink / raw)


Leo Brewin <leo.brewin@internode.on.net> writes:
> But having digested your answer I'm still a bit confused (sorry). Here
> is the "procedure" version of the above package,
>
>    with Ada.Numerics.Generic_Complex_Types;
>    with Ada.Numerics.Generic_Complex_Elementary_Functions;
>
>    procedure foo is
>
>       type Real is digits 18;
> 		
>       package Complex_Types is new 
>          Ada.Numerics.Generic_Complex_Types (Real);
>
>       use foo.Complex_Types;
>       subtype Complex is foo.Complex_Types.Complex;
>       
>       procedure bar (z : in out Complex);
>    
>       package Complex_Maths is new 
>          Ada.Numerics.Generic_Complex_Elementary_Functions (Complex_Types);
>    
>       procedure bar (z : in out Complex) is
>       begin
>          z := Complex'(Re(z),0.0);
>       end bar;
>
>    begin
>       null;
>    end foo;
>
> This does compile and yet (by my reading of your reply) the "use foo"
> and "subtype complex" lines should introduce two distinct versions of
> Complex and thus should produce a compiler error.

The difference is that, in the procedure, the subtype Foo.Complex is
declared in the immediate scope where Complex_Maths is declared, so it
hides Foo.Complex_Types.Complex, so there is no ambiguity anymore.  You
get the same effect with the package if you move the declaration of
Complex_Maths to the package spec.

These rules are quite subtle and the error messages from GNAT less than
helpful.  Maybe that's why Ada has a reputation for being "difficult to
learn".

-- 
Ludovic Brenta.



^ permalink raw reply	[relevance 0%]

* Re: Is this a bug in Ada.Numerics?
  2012-10-21  0:37  5% Is this a bug in Ada.Numerics? Leo Brewin
@ 2012-10-21  1:54  0% ` Yannick Duchêne (Hibou57)
  2012-10-21  1:55  5% ` Ludovic Brenta
  1 sibling, 0 replies; 35+ results
From: Yannick Duchêne (Hibou57) @ 2012-10-21  1:54 UTC (permalink / raw)


Hi,

Just reading your snippet (not tried to compile)

Le Sun, 21 Oct 2012 02:37:36 +0200, Leo Brewin  
<leo.brewin@internode.on.net> a écrit:

> In the course of my dabblings in Ada I've come across a curious  
> "behaviour" that seems to me, with my limited knowledge of Ada, to be a  
> bug.
>
> Here is a simple package spec
>
>    with Ada.Numerics.Generic_Complex_Types;
>    with Ada.Numerics.Generic_Complex_Elementary_Functions;
>
>    package foo is
>
>       type Real is digits 18;
> 		
>       package Complex_Types is new
>          Ada.Numerics.Generic_Complex_Types (Real);
>
>       use foo.Complex_Types;
>       -- subtype Complex is foo.Complex_Types.Complex;
>      procedure bar (z : in out Complex);
>   end foo;

When you do “use foo.Complex_Types;”, you introduce from then in the  
current scope, all definitions from “foo.Complex_Types”. (by the way, you  
don't need “use foo.Complex_Types;”, as “use Complex_Types;” would be  
enough). This means that “foo.Complex_Types.Complex” [1] is introduced in  
the scope, so there is a clash with the subtype you define right after.  
May be you did not understood what exactly “use” do?

[1] See [G.1.1 Complex  
Types](http://www.adaic.org/resources/add_content/standards/05rm/html/RM-G-1-1.html),  
which contains the following:

     type Complex is […]


> and the package body
>
>    package body foo is
>
>       package Complex_Maths is new
>          Ada.Numerics.Generic_Complex_Elementary_Functions  
> (Complex_Types);
>      procedure bar (z : in out Complex) is
>       begin
>          z := Complex'(Re(z),0.0);
>       end bar;
>
>    end foo;

Not part of the case, but here, you are instantiating a generic package  
which you don't use (must be an error).


-- 
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University



^ permalink raw reply	[relevance 0%]

* Re: Is this a bug in Ada.Numerics?
  2012-10-21  3:39  0%     ` Ludovic Brenta
  2012-10-23 15:48  0%       ` Vincent Marciante
@ 2012-10-23 22:52  5%       ` Leo Brewin
  1 sibling, 0 replies; 35+ results
From: Leo Brewin @ 2012-10-23 22:52 UTC (permalink / raw)


Hi Ludovic,

I thought I was getting on top of this but then I ran a small experiment that doesn't make sense (to me).

I decided to make my own generic pacakges, ada_complex_types and ada_complex_functions, as clones of
Ada.Numerics.Generic_Complex_Types and 
Ada.Numerics.Generic_Complex_Elementary_Functions.

I had to add a couple of lines to the body of my packages, in particular I added these lines to
ada_complex_functions.adb

   Argument_Error : Excpetion;
   PI  : constant := 3.14159;
   
and this line to ada_complex_types.adb

   Argument_Error : Exception;

These changes were needed to allow compilation of the packages. Othe than the above small changes my generic packages are indetical to their Ada.Numerics counterparts.

I then used these pacakges in my previous example. I had expected no change in the errors reported by the compiler. But that is not what happens. I find that with or without the "subtype Complex ..." declaration the compiler reports no errors.

Am I missing something here?

Sorry to be a pain...

Cheers,
Leo




^ permalink raw reply	[relevance 5%]

* Re: Is this a bug in Ada.Numerics?
  2012-10-21  3:39  0%     ` Ludovic Brenta
@ 2012-10-23 15:48  0%       ` Vincent Marciante
  2012-10-23 22:52  5%       ` Leo Brewin
  1 sibling, 0 replies; 35+ results
From: Vincent Marciante @ 2012-10-23 15:48 UTC (permalink / raw)


"Ludovic Brenta" <ludovic@ludovic-brenta.org> wrote in message 
news:<87r4oscx42.fsf@ludovic-brenta.org>...

> Leo Brewin <leo.brewin@internode.on.net> writes:

> > But having digested your answer I'm still a bit confused (sorry). Here

> > is the "procedure" version of the above package,

> >

> >    with Ada.Numerics.Generic_Complex_Types;

> >    with Ada.Numerics.Generic_Complex_Elementary_Functions;

> >

> >    procedure foo is

> >

> >       type Real is digits 18;

> >

> >       package Complex_Types is new

> >          Ada.Numerics.Generic_Complex_Types (Real);

> >

> >       use foo.Complex_Types;

> >       subtype Complex is foo.Complex_Types.Complex;

> >

> >       procedure bar (z : in out Complex);

> >

> >       package Complex_Maths is new

> >          Ada.Numerics.Generic_Complex_Elementary_Functions 
> > (Complex_Types);

> >

> >       procedure bar (z : in out Complex) is

> >       begin

> >          z := Complex'(Re(z),0.0);

> >       end bar;

> >

> >    begin

> >       null;

> >    end foo;

> >

> > This does compile and yet (by my reading of your reply) the "use foo"

> > and "subtype complex" lines should introduce two distinct versions of

> > Complex and thus should produce a compiler error.

>

> The difference is that, in the procedure, the subtype Foo.Complex is

> declared in the immediate scope where Complex_Maths is declared, so it

> hides Foo.Complex_Types.Complex, so there is no ambiguity anymore.  You

> get the same effect with the package if you move the declaration of

> Complex_Maths to the package spec.

>

> These rules are quite subtle and the error messages from GNAT less than

> helpful.





This is a type of comment that AdaCore's Robert Dewar really likes to 
address.
I think that there is good chance that an improvement would be made in GNAT
to address this issue if you or the original poster send a report to 
AdaCore.



Vinny





> Maybe that's why Ada has a reputation for being "difficult to

> learn".

>

> -- 

> Ludovic Brenta.

. 





^ permalink raw reply	[relevance 0%]

* Re: Is this a bug in Ada.Numerics?
  2012-10-21  1:55  5% ` Ludovic Brenta
@ 2012-10-21  2:32  6%   ` Leo Brewin
  2012-10-21  3:39  0%     ` Ludovic Brenta
  0 siblings, 1 reply; 35+ results
From: Leo Brewin @ 2012-10-21  2:32 UTC (permalink / raw)


Hi Ludovic,

Thanks for the quick and detailed response. Your suggestion (3) and (4) certainly do fix the problem but I was posing the original code (in its less than ideal form) as a way to understand how the compiler sorts out these scoping/visibility issues. In my real code I would do as you suggest.

But having digested your answer I'm still a bit confused (sorry). Here is the "procedure" version of the above package,

   with Ada.Numerics.Generic_Complex_Types;
   with Ada.Numerics.Generic_Complex_Elementary_Functions;

   procedure foo is

      type Real is digits 18;
		
      package Complex_Types is new 
         Ada.Numerics.Generic_Complex_Types (Real);

      use foo.Complex_Types;
      subtype Complex is foo.Complex_Types.Complex;
      
      procedure bar (z : in out Complex);
   
      package Complex_Maths is new 
         Ada.Numerics.Generic_Complex_Elementary_Functions (Complex_Types);
   
      procedure bar (z : in out Complex) is
      begin
         z := Complex'(Re(z),0.0);
      end bar;

   begin
      null;
   end foo;

This does compile and yet (by my reading of your reply) the "use foo" and "subtype complex" lines should introduce two distinct versions of Complex and thus should produce a compiler error.

I'm sure I've got this wrong, my apologies for wasting your time...

Cheers,
Leo



^ permalink raw reply	[relevance 6%]

* Re: Is this a bug in Ada.Numerics?
  2012-10-21  0:37  5% Is this a bug in Ada.Numerics? Leo Brewin
  2012-10-21  1:54  0% ` Yannick Duchêne (Hibou57)
@ 2012-10-21  1:55  5% ` Ludovic Brenta
  2012-10-21  2:32  6%   ` Leo Brewin
  1 sibling, 1 reply; 35+ results
From: Ludovic Brenta @ 2012-10-21  1:55 UTC (permalink / raw)


Leo Brewin writes on comp.lang.ada:
> In the course of my dabblings in Ada I've come across a curious
> "behaviour" that seems to me, with my limited knowledge of Ada, to be
> a bug.
>
> Here is a simple package spec
>
>    with Ada.Numerics.Generic_Complex_Types;
>    with Ada.Numerics.Generic_Complex_Elementary_Functions;
>
>    package foo is
>
>       type Real is digits 18;
> 		
>       package Complex_Types is new
>          Ada.Numerics.Generic_Complex_Types (Real);
>
>       use foo.Complex_Types;
>       -- subtype Complex is foo.Complex_Types.Complex;
>       
>       procedure bar (z : in out Complex);
>    
>    end foo;
>
> and the package body
>
>    package body foo is
>
>       package Complex_Maths is new
>          Ada.Numerics.Generic_Complex_Elementary_Functions (Complex_Types);
>    
>       procedure bar (z : in out Complex) is
>       begin
>          z := Complex'(Re(z),0.0);
>       end bar;
>
>    end foo;
>
> As it stands this package compiles under GNAT GPL 2012. But if the
> "subtype" declaration in the package spec is un-commented then the
> compile fails with the following errors
>
> 04: instantiation error at a-ngcefu.ads:24
> 04: "Complex" is not visible (more references follow)
> 04: instantiation error at a-ngcefu.ads:24
> 04: non-visible declaration at foo.ads:11
> 04: instantiation error at a-ngcefu.ads:24
> 04: non-visible declaration at a-ngcoty.ads:42, instance at foo.ads:8
> 04: instantiation error at a-ngcefu.ads:24
> 04: non-visible declaration at a-ngcoty.ads:42, instance at a-ngcefu.ads:18
>
> Is this behaviour correct? My limited understanding is that the "use
> foo.Complex_Types" should have made visible all types and operations
> on "Complex" and thus the extra "subtype" should be redundant and
> should not cause an error.

The file a-ngcefu.ads starts with:

with Ada.Numerics.Generic_Complex_Types;
generic
   with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (<>);
   use Complex_Types;

package Ada.Numerics.Generic_Complex_Elementary_Functions is
   pragma Pure;

   function Sqrt (X : Complex)   return Complex; -- line 24

This last line, which causes all the error messages, involves the
subtype Complex.  The compiler tries to resolve this and finds two
possible solutions: Foo.Complex and Foo.Complex_Types.Complex, both of
which are directly visible.  Foo.Complex is directly visible because of
your use clause at foo.ads:11 and Foo.Complex_Types.Complex is directly
visible because of the use clause at a-ngcefu.ads:19.

The compiler cannot decide which subtype is meant, so it reports an
error.  I think the error is justified but the error message is cryptic.
A hint is:

> 04: non-visible declaration at a-ngcoty.ads:42, instance at foo.ads:8
> 04: non-visible declaration at a-ngcoty.ads:42, instance at a-ngcefu.ads:18

Since both subtypes are directly visible and clash, the compiler reports
both as non-visible :/

> I've tried a few variations on the above and I found that if I
> 1) Comment out the "Package Complex_Maths" declaration, OR
> 2) Create a single procedure from the above package spec/body
> then the code compiles happily with or without the "subtype"
> declaration.

You could try:

3) remove your "use Complex_Types" clause or move it to the body of your
procedure Bar.
4) remove your "subtype Foo.Complex is foo.Complex_Types.Complex"
altogether; why did you think you needed it?

> If anybody could explain this behaviour to me I would be very
> grateful.

You may think that Foo.Complex and Foo.Complex_Types.Complex are the
same thing.  They are not; they are two different subtypes of the same
type; they are declared in different packages and hence have different
scopes.  The fact that they have the same constraints is incidental and
irrelevant.  A relevant fact is that you are allowed to add
representation clauses (or aspects) to Foo.Complex, since you declared
it, but not to Foo.Complex_Types.Complex, since it is declared by (an
instance of) a standard package.

HTH

-- 
Ludovic Brenta.



^ permalink raw reply	[relevance 5%]

* Is this a bug in Ada.Numerics?
@ 2012-10-21  0:37  5% Leo Brewin
  2012-10-21  1:54  0% ` Yannick Duchêne (Hibou57)
  2012-10-21  1:55  5% ` Ludovic Brenta
  0 siblings, 2 replies; 35+ results
From: Leo Brewin @ 2012-10-21  0:37 UTC (permalink / raw)


In the course of my dabblings in Ada I've come across a curious "behaviour" that seems to me, with my limited knowledge of Ada, to be a bug.

Here is a simple package spec

   with Ada.Numerics.Generic_Complex_Types;
   with Ada.Numerics.Generic_Complex_Elementary_Functions;

   package foo is

      type Real is digits 18;
		
      package Complex_Types is new
         Ada.Numerics.Generic_Complex_Types (Real);

      use foo.Complex_Types;
      -- subtype Complex is foo.Complex_Types.Complex;
      
      procedure bar (z : in out Complex);
   
   end foo;

and the package body

   package body foo is

      package Complex_Maths is new
         Ada.Numerics.Generic_Complex_Elementary_Functions (Complex_Types);
   
      procedure bar (z : in out Complex) is
      begin
         z := Complex'(Re(z),0.0);
      end bar;

   end foo;

As it stands this package compiles under GNAT GPL 2012. But if the "subtype" declaration in the package spec is un-commented then the compile fails with the following errors

04: instantiation error at a-ngcefu.ads:24
04: "Complex" is not visible (more references follow)
04: instantiation error at a-ngcefu.ads:24
04: non-visible declaration at foo.ads:11
04: instantiation error at a-ngcefu.ads:24
04: non-visible declaration at a-ngcoty.ads:42, instance at foo.ads:8
04: instantiation error at a-ngcefu.ads:24
04: non-visible declaration at a-ngcoty.ads:42, instance at a-ngcefu.ads:18

Is this behaviour correct? My limited understanding is that the "use foo.Complex_Types" should have made visible all types and operations on "Complex" and thus the extra "subtype" should be redundant and should not cause an error.

I've tried a few variations on the above and I found that if I
1) Comment out the "Package Complex_Maths" declaration, OR
2) Create a single procedure from the above package spec/body
then the code compiles happily with or without the "subtype" declaration.

If anybody could explain this behaviour to me I would be very grateful.

Cheers,
Leo



^ permalink raw reply	[relevance 5%]

* Lapack Ada binding matrices/vectors issue, how to best to resolve?
@ 2012-07-12  0:38  5% Nasser M. Abbasi
  0 siblings, 0 replies; 35+ results
From: Nasser M. Abbasi @ 2012-07-12  0:38 UTC (permalink / raw)



The Ada lapack binding defines its own Matrix types. However, it
does not define operators (multiply, add, etc.. ) to work on these
types similar to Ada's Real Vectors and Matrices in the
Ada.Numerics.Real_Arrays package

http://www.ada-auth.org/standards/12rm/html/RM-G-3-1.html

What this means, is that one can't even multiply a number by the
matrix if the Matrix is Fortran_Real_Matrix like one can with
Real_Matrix. Here is a simple example

---------------------------
with Interfaces.Fortran; use Interfaces.Fortran;
with Ada.Numerics.Real_Arrays; use Ada.Numerics.Real_Arrays;
with labase; use labase;  -- LAPACK binding

procedure foo3 is
      
   A1 : constant Fortran_Real_Matrix(1..2,1..2):=
         12.0*((12.0,  6.0),(-12.0, 6.0)); -- ERROR
          
   A2 : constant Real_Matrix(1..2,1..2) :=
          12.0*((12.0,  6.0),(-12.0,  6.0)); -- OK
   begin
     null;
end foo3;
--------------------

>gnatmake -gnat2012 -I/lapada/ada  foo2.adb -largs -lblas
gcc -c -gnat2012 -I/lapada/ada foo2.adb
foo2.adb:24:20: expected type "Fortran_Real_Matrix" defined at labase.ads:94
foo2.adb:24:20: found type "Interfaces.Fortran.Complex"
gnatmake: "foo2.adb" compilation error
>

I tried to do conversion of the Fortran_Real_Matrix to
Real_Matrix but the compiler does not like it as it is
aggregate.

So, this means this binding as it stands is too limited to
use as is.

What would be a good way to fix this whole issue? Make a
pacakge similar to Numerics.Generic_Real_Arrays for
the Fortran_Real_Matrix so that all the operators '*','+', etc..
are available now for this Matrix type?

The Ada lapack package with's :

with Ada.Numerics.Generic_Complex_Types;
with Interfaces.Fortran; use  Interfaces.Fortran;

Then it defines many types, such as
----------------------
type Fortran_Integer_Matrix is array (Fortran_Integer range <>,
                                       Fortran_Integer range <>)
          of Fortran_Integer;
pragma Convention (Fortran, Fortran_Integer_Matrix);
etc..
----------------------

But it does NOT define operators to work on these types like
the Ada package Ada.Numerics.Real_Arrays  does:

-------------------------------
function "*" (Left : Real'Base;   Right : Real_Matrix)
       return Real_Matrix;
---------------------------

I was thinking of just copying all these functions as the above to the
Lapack package and edit things and change all the Real_Matrix to
Fortran_Real_Matrix etc..  but this seems like not the right way
to do this.

The main Lapack package is lapada/ada/labase.ads in
the tar file

ftp://ftp.cs.kuleuven.be/pub/Ada-Belgium/mirrors/gnu-ada/OLD/contrib/lapack-ada/

Any suggestion how to resolve this from experts will be great as I am
a newbie in Ada.

--Nasser




^ permalink raw reply	[relevance 5%]

* Re: Eigenvalues to find roots of polynomials
  @ 2011-07-26 15:40  6%   ` marius63
  0 siblings, 0 replies; 35+ results
From: marius63 @ 2011-07-26 15:40 UTC (permalink / raw)


And the winner is...

> <http://home.roadrunner.com/~jbmatthews/misc/groots.html>

John B. Matthews Ada...Generic_Roots works like a charm.
It passed the simple test case, and solved my quintic problem x^5 -
4178x + 4177

$ ./find_roots_mathews 1 0 0 0 -4178 4177
Index =>  0, Position =>  6, Value => 4177
Index =>  1, Position =>  5, Value => -4178
Index =>  2, Position =>  4, Value => 0
Index =>  3, Position =>  3, Value => 0
Index =>  4, Position =>  2, Value => 0
Index =>  5, Position =>  1, Value => 1
Root 1 => (Re=> 1.00000000000000E+00, Im=> 0.00000000000000E+00)
Root 2 => (Re=>-2.47582467311450E-01, Im=>-8.05881611194044E+00)
Root 3 => (Re=> 7.76752669636581E+00, Im=>-1.40129846432482E-45)
Root 4 => (Re=>-2.47582467311450E-01, Im=> 8.05881611194044E+00)
Root 5 => (Re=>-8.27236176174291E+00, Im=> 0.00000000000000E+00)

I know from previous mathematical derivation (the polynomial comes
from extracting a variable in a summation), that the solution is a non-
negative scalar different from one, so Root 3 is my number, with the
non-zero but very small value of the imaginary part certainly only a
residue of computation done inside Matthews's magic machine. Thanks a
very great lot. Source code follows. Sorry for the mispelling,
"Mathews".

--  Trying to compute the roots of a polynomial using John Mathews
procedure.

with Ada.Command_Line;
with Ada.Numerics.Generic_Real_Arrays;
with Ada.Text_IO;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Complex_Arrays;
with Ada.Numerics.Generic_Complex_Arrays.Generic_Roots;

procedure Find_Roots_Mathews is
   type Real is digits 15;
   package Real_Arrays is new Ada.Numerics.Generic_Real_Arrays (Real);
   package Complex_Types is new Ada.Numerics.Generic_Complex_Types
(Real);
   package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
(Real_Arrays, Complex_Types);
   procedure Complex_Roots is new Complex_Arrays.Generic_Roots;
   M, N : Natural;
begin
   M := Ada.Command_Line.Argument_Count;
   N := M - 1;
   --  N = number of arguments = degree of the polynomial + 1.
   --  The input consists of all coficients in descending degree,
   --  e.g. for polynomial
   --     x^3 - 2x^2 - x + 2
   --  the input is
   --     1 -2 -1 2
   --  Roots of the above (for testing):
   --     -1, 1, 2

   declare
      P : Complex_Arrays.Complex_Vector (0 .. N);
      R : Complex_Arrays.Complex_Vector (1 .. N);
      use Ada.Text_IO;
   begin
      for I in 0 .. N loop
         Put_Line ("Index => " & I'img &
                   ", Position => " & Integer'Image(M - I) &
                   ", Value => " & Ada.Command_Line.Argument (M - I));
         P (I) := (Re => Real'Value (Ada.Command_Line.Argument (M -
I)), Im => 0.0);
      end loop;
      Complex_Roots (P, R);
      for I in R'Range loop
         Put_Line ("Root" & I'img &
                   " => (Re=>" & R(I).Re'img &
                   ", Im=>" & R(I).Im'img & ")");
      end loop;
   end;
end;



^ permalink raw reply	[relevance 6%]

* Eigenvalues to find roots of polynomials
@ 2011-07-25 17:36  6% marius63
    0 siblings, 1 reply; 35+ results
From: marius63 @ 2011-07-25 17:36 UTC (permalink / raw)


I am trying to use Eigenvalues methods to find the roots of
polynomials... and failing miserably!
My math ability is not stellar. I used the information at

(1)
http://mathworld.wolfram.com/PolynomialRoots.html (equation 12)

and

(2)
http://en.wikipedia.org/wiki/Root_finding
(One possibility is to form the companion matrix of the polynomial.
Since the eigenvalues of this matrix coincide with the roots of the
polynomial, one can use any eigenvalue algorithm to find the roots of
the polynomial.)
http://en.wikipedia.org/wiki/Companion_matrix

I tried both schemes with the test data in (1). The output is first
the matrix, for verification, then the (wrong) roots (they should be
-1, 1, 2) and the result of applying them to the polynomial (and
yielding miserably non-zero values of course). The source code
follows. What am I missing? Thanks a lot.

$ ./find_roots 1 -2 -1 2
 5.00000E-01 1.00000E+00-5.00000E-01
 1.00000E+00 0.00000E+00 0.00000E+00
 0.00000E+00 1.00000E+00 0.00000E+00
-7.6156E-01=> 4.2003E-01
 4.1249E+00=>-1.6015E+01
 6.3667E-01=> 5.9465E-01

$ ./find_roots_wikipedia -2 -1 2
 0.00000000000000E+00,  0.00000000000000E+00, -2.00000000000000E+00,
 1.00000000000000E+00,  0.00000000000000E+00,  1.00000000000000E+00,
 0.00000000000000E+00,  1.00000000000000E+00,  2.00000000000000E+00,
-1.17008648662603E+00 => -3.38499151386795E-02
 6.88892182534018E-01 =>  1.96496630422799E+00
 2.48119430409202E+00 =>  3.00688836109107E+01


--  Trying to compute the roots of a polynomial using the method
--  described in eq. (12) of http://mathworld.wolfram.com/PolynomialRoots.html
--  (consulted July 2011)

with Ada.Command_Line;
with Ada.Numerics.Generic_Real_Arrays;
with Ada.Text_IO;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Complex_Arrays;

procedure Find_Roots is
   type Real is digits 5;
   package Real_Arrays is new Ada.Numerics.Generic_Real_Arrays (Real);
   package Complex_Types is new Ada.Numerics.Generic_Complex_Types
(Real);
   package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
(Real_Arrays, Complex_Types);
   N, M : Natural;
begin
   N := Ada.Command_Line.Argument_Count;
   --  N = number of arguments = degree of the polynomial.
   --  The input consists of all coficients in descending degree,
   --  e.g. for polynomial
   --     x^3 - 2x^2 - x + 2
   --  the input is
   --     1 -2 -1 2
   --  Roots of the above (for testing):
   --     -1, 1, 2

   declare
      C : Complex_Arrays.Complex_Matrix (1 .. N - 1, 1 .. N - 1) :=
(others => (others => (0.0, 0.0)));
      --  Using a complex matrix because real matrices for eigenvalues
must be symmetric.
      --  Below we convert all real values to complex (with null Im
part).
      --  The complex matrix must be Hamiltonian;
      --  I thought a non-symmetric converted to complex would
      --  yield a non-Hamiltonian matrix, but I tried and it
worked :-)... hmm... NOT :-(
      A0 : Real := Real'Value (Ada.Command_Line.Argument (N));
      use Ada.Text_IO;
   begin
      for I in 1 .. N - 1 loop
         C (1, I).Re := - Real'Value (Ada.Command_Line.Argument (N -
I)) / A0;
         if I < N - 1 then
            C (I + 1, I).Re := 1.0;
         end if;
      end loop;

      --  inspect matrix
      for i in c'range(1) loop
         for j in c'range(2) loop
            put (c(i,j).re'img);
         end loop;
         new_line;
      end loop;

      declare
         V : Real_Arrays.Real_Vector := Complex_Arrays.Eigenvalues
(C);
         use Real_Arrays;
         Z : Real;
         use Ada.Text_IO;
      begin
         for I in V'Range loop
            V (I) := 1.0 / V (I);
            Put (Real'Image (V (I)));
            Z := 0.0;
            for J in 1 .. N - 1 loop
               Z := Z + C (1, J).Re * V (I) ** J;
            end loop;
            Put ("=>" & Real'Image (Z));
            New_Line;
         end loop;
      end;
   end;
end;

--  Build with:
--  gnatmake find_roots -largs /Developer/SDKs/MacOSX10.4u.sdk/usr/lib/
libblas.dylib /Developer/SDKs/MacOSX10.4u.sdk/usr/lib/liblapack.dylib


--  Given an Nth degree polynomial A(N)X**N + ... + A(1)X + A(0),
--  the roots can be found by finding the eigenvalues L(I) of the
matrix
--     _                                                           _
--    |   -A(1)/A(0)   -A(2)/A(0)   -A(3)/A(0)   ...   -A(N)/A(0)   |
--    |        1            0            0       ...        0       |
--    |        0            1            0       ...        0       |
--    |       ...          ...           1       ...        0       |
--    |_       0            0            0       ...        0      _|
--
--  and taking R(i) = 1/L(I). This method can be computationally
expensive,
--  but is fairly robust at finding close and multiple roots.
--    (wolphram)


--  This program computes the roots of a polynomial using the method
--  described in http://en.wikipedia.org/wiki/Companion_matrix

--  This method is for monic polynomials, i.e. with coficient of the
term
--  of greater degree equal to 1, so the input is for the remainder
terms,
--  in descending order e.g. for polynomial
--     x^3 - 2x^2 - x + 2
--  the input is
--     -2 -1 2

with Ada.Command_Line;
with Ada.Numerics.Generic_Real_Arrays;
with Ada.Text_IO;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Complex_Arrays;

procedure Find_Roots_Wikipedia is
   type Real is digits 15;
   package Real_Arrays is new Ada.Numerics.Generic_Real_Arrays (Real);
   package Complex_Types is new Ada.Numerics.Generic_Complex_Types
(Real);
   package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
(Real_Arrays, Complex_Types);
   N : Natural;
begin
   N := Ada.Command_Line.Argument_Count;
   declare
      C : Complex_Arrays.Complex_Matrix (1 .. N, 1 .. N) := (others =>
(others => (0.0, 0.0)));
      --  Using a complex matrix because real matrices for eigenvalues
must be symmetric.
      --  Below we convert all real values to complex (with null Im
part).
      --  (Still, the complex matrix must be Hamiltonian...)
      use Ada.Text_IO;
   begin
      for I in 1 .. N loop
         C (I, N).Re := - Real'Value (Ada.Command_Line.Argument (N - I
+ 1));
         if I > 1 then
            C (I, I - 1).Re := 1.0;
         end if;
      end loop;

      --  inspect matrix
      for i in c'range(1) loop
         for j in c'range(2) loop
            put (c(i,j).re'img & ", ");
         end loop;
         new_line;
      end loop;

      declare
         V : Real_Arrays.Real_Vector := Complex_Arrays.Eigenvalues
(C);
         use Real_Arrays;
         Z : Real;
         use Ada.Text_IO;
      begin
         for I in V'Range loop
            Put (Real'Image (V (I)));
            Z := V (I) ** N;
            for J in 1 .. N loop
               Z := Z + C (N, J).Re * V (I) ** (J - 1);
            end loop;
            Put (" => " & Real'Image (Z));
            New_Line;
         end loop;
      end;
   end;
end;

--  Build with:
--  gnatmake find_roots_wikipedia -largs /Developer/SDKs/
MacOSX10.4u.sdk/usr/lib/libblas.dylib /Developer/SDKs/MacOSX10.4u.sdk/
usr/lib/liblapack.dylib



^ permalink raw reply	[relevance 6%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-14  1:05  7%       ` Stephen Leake
@ 2010-08-14 16:26  0%         ` Simon Wright
  0 siblings, 0 replies; 35+ results
From: Simon Wright @ 2010-08-14 16:26 UTC (permalink / raw)


Stephen Leake <stephen_leake@stephe-leake.org> writes:

> sjw <simon.j.wright@mac.com> writes:

>>    generic
>>       with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (Real);
>>       with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
>>         (Generic_Real_Arrays, Complex_Types);
>
> I see the problem. Generic_Real_Arrays is the name of the parent generic
> package; that's not a concrete package, so it can't be used here.

Exactly so.

> This compiles:
>
> with Ada.Numerics.Generic_Complex_Arrays;
> with Ada.Numerics.Generic_Complex_Types;
> with Ada.Numerics.Generic_Real_Arrays;
> generic
>    type Real is digits <>;
>    with package Real_Arrays is new Ada.Numerics.Generic_Real_Arrays (Real);
>    with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (Real);
>    with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays (Real_Arrays, Complex_Types);
> package Extensions is
>
>    function Eigenvalues (A : Real_Arrays.Real_Matrix) return Complex_Arrays.Complex_Vector;
>
> end Extensions;
>
> Is there some reason you want Extensions to be a child of
> Generic_Real_Arrays?

Umm.

In the past, I've often introduced "extensions' in child (generic)
packages, usually because the extension needed visibility of the private
part of the original. That's not so in this case, as it happens, but I
find the idiom comfortable.

Given that, the function taking Complex_Matrix and returning
Complex_Vector has "obviously" to be in a child of
Generic_Complex_Arrays.

So I thought that the function taking Real_Matrix should be in a child
of Generic_Real_Arrays, in spite of the fact it returns a Complex_Vector.

Clearly this is not the way Nature intended!



^ permalink raw reply	[relevance 0%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-12 12:48  7%     ` sjw
@ 2010-08-14  1:05  7%       ` Stephen Leake
  2010-08-14 16:26  0%         ` Simon Wright
  0 siblings, 1 reply; 35+ results
From: Stephen Leake @ 2010-08-14  1:05 UTC (permalink / raw)


sjw <simon.j.wright@mac.com> writes:

> On Aug 12, 11:36 am, Stephen Leake <stephen_le...@stephe-leake.org>
> wrote:
>
>> Post complete code, I'll try it on my version of gnat.
>
> Thanks.
>
> Remember -gnatpg!
>
> The code I posted above is the complete spec (not going to bother to
> write the body if can't get the spec to compile!):
>
>    with Ada.Numerics.Generic_Complex_Arrays;
>    with Ada.Numerics.Generic_Complex_Types;
>
>    generic
>       with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (Real);
>       with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
>         (Generic_Real_Arrays, Complex_Types);

I see the problem. Generic_Real_Arrays is the name of the parent generic
package; that's not a concrete package, so it can't be used here.


>    use Complex_Arrays; 
>    package Ada.Numerics.Generic_Real_Arrays.Extensions is
>
>       function Eigenvalues (A : Real_Matrix) return Complex_Vector;
>
>    end Ada.Numerics.Generic_Real_Arrays.Extensions;

This compiles:

with Ada.Numerics.Generic_Complex_Arrays;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Real_Arrays;
generic
   type Real is digits <>;
   with package Real_Arrays is new Ada.Numerics.Generic_Real_Arrays (Real);
   with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (Real);
   with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays (Real_Arrays, Complex_Types);
package Extensions is

   function Eigenvalues (A : Real_Arrays.Real_Matrix) return Complex_Arrays.Complex_Vector;

end Extensions;

Is there some reason you want Extensions to be a child of Generic_Real_Arrays?

-- 
-- Stephe



^ permalink raw reply	[relevance 7%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-12 19:45  5%     ` Simon Wright
@ 2010-08-13  6:27  0%       ` Dmitry A. Kazakov
  0 siblings, 0 replies; 35+ results
From: Dmitry A. Kazakov @ 2010-08-13  6:27 UTC (permalink / raw)


On Thu, 12 Aug 2010 20:45:33 +0100, Simon Wright wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> Further I would name it
>>
>> generic
>> package Ada.Numerics.Generic_Complex_Arrays.
>>    Generic_Real_Extensions;
>>    ^^^^
>> or Generic_Real_Valued_Extensions;
>>    ^^^^^
>>
>> It is always helpful to keep all name spaces clean of generic names.
> 
> Not quite sure what you mean here?
> 
> I think you're recommending that all names of generic units should
> include an indication that they're generic, so that people don't get
> confused as to generic vs instantiation?

More important is that names of generics do not hide other names. Sooner or
later it becomes a problem.

> In other places I've tacked _G on the end of the 'obvious' name:
> 
>    generic
>    package ColdFrame.Events_G.Standard_G.Callback_Manager_G is
> 
> and I saw someone else (Ludovic?) doing the same here recently.

I used plural suffix 's' before, but it Generic is clearer and it seems
like a semi-standard now.

> I think that people are going to get confused instantiating children of
> generic packages, and that there's nothing like a helpful example to
> clarify.
> 
> Currently I have (with elsewhere, "type Real is digits <>;")
> 
>    package Real_Arrays
>    is new Ada.Numerics.Generic_Real_Arrays (Real);
>    package Complex_Types
>    is new Ada.Numerics.Generic_Complex_Types (Real);
>    package Complex_Arrays
>    is new Ada.Numerics.Generic_Complex_Arrays (Real_Arrays, Complex_Types);
>    package Extensions
>    is new Complex_Arrays.Extensions;
> 
> Do people feel this would be clearer if the last 2 lines read instead
> 
>    package Extensions
>    is new Complex_Arrays.Generic_Extensions;
> 
> (and if so, how much clearer?!)

Yes, especially because of two "Extensions". I had immense problems with
names. Look at this:

   http://www.dmitry-kazakov.de/ada/fuzzy_packages.gif

Somewhere down deep the hierarchy the user starts to become incomprehensive
error messages upon instantiation within the generic(!) bodies. It is
practically impossible to understand what is wrong, is it a compiler bug
(in GNAT case) or something else. For the user it would be a nightmare to
fight this.

Another rule of thumb (I don't know if this is going to be fixed in Ada
2012), is that a child should always rename formal generic arguments in its
specifications. Under certain circumstances, usually in children, they
might become unavailable. I mean this:

generic
   with package P is new Generic_P (<>);
package Generic_S is
   package P_Of renames P; -- You will need this!

Sometimes you need to "rename" formal types as well:

generic
   type Foo is ...
package Generic_S is
   subtype Foo_Of is Foo; -- What a mess!

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[relevance 0%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-12  5:57  0%   ` Dmitry A. Kazakov
@ 2010-08-12 19:45  5%     ` Simon Wright
  2010-08-13  6:27  0%       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 35+ results
From: Simon Wright @ 2010-08-12 19:45 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:

> Further I would name it
>
> generic
> package Ada.Numerics.Generic_Complex_Arrays.
>    Generic_Real_Extensions;
>    ^^^^
> or Generic_Real_Valued_Extensions;
>    ^^^^^
>
> It is always helpful to keep all name spaces clean of generic names.

Not quite sure what you mean here?

I think you're recommending that all names of generic units should
include an indication that they're generic, so that people don't get
confused as to generic vs instantiation?

In other places I've tacked _G on the end of the 'obvious' name:

   generic
   package ColdFrame.Events_G.Standard_G.Callback_Manager_G is

and I saw someone else (Ludovic?) doing the same here recently.


I think that people are going to get confused instantiating children of
generic packages, and that there's nothing like a helpful example to
clarify.

Currently I have (with elsewhere, "type Real is digits <>;")

   package Real_Arrays
   is new Ada.Numerics.Generic_Real_Arrays (Real);
   package Complex_Types
   is new Ada.Numerics.Generic_Complex_Types (Real);
   package Complex_Arrays
   is new Ada.Numerics.Generic_Complex_Arrays (Real_Arrays, Complex_Types);
   package Extensions
   is new Complex_Arrays.Extensions;

Do people feel this would be clearer if the last 2 lines read instead

   package Extensions
   is new Complex_Arrays.Generic_Extensions;

(and if so, how much clearer?!)



^ permalink raw reply	[relevance 5%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-12 10:36  0%   ` Stephen Leake
@ 2010-08-12 12:48  7%     ` sjw
  2010-08-14  1:05  7%       ` Stephen Leake
  0 siblings, 1 reply; 35+ results
From: sjw @ 2010-08-12 12:48 UTC (permalink / raw)


On Aug 12, 11:36 am, Stephen Leake <stephen_le...@stephe-leake.org>
wrote:

> Post complete code, I'll try it on my version of gnat.

Thanks.

Remember -gnatpg!

The code I posted above is the complete spec (not going to bother to
write the body if can't get the spec to compile!):

   with Ada.Numerics.Generic_Complex_Arrays;
   with Ada.Numerics.Generic_Complex_Types;

   generic
      with package Complex_Types is new
Ada.Numerics.Generic_Complex_Types
        (Real);
      with package Complex_Arrays is new
Ada.Numerics.Generic_Complex_Arrays
        (Generic_Real_Arrays, Complex_Types);            --
<<<<<<<<<<<<<<<<<<
      use Complex_Arrays;
   package Ada.Numerics.Generic_Real_Arrays.Extensions is

      function Eigenvalues (A : Real_Matrix) return Complex_Vector;

   end Ada.Numerics.Generic_Real_Arrays.Extensions;



^ permalink raw reply	[relevance 7%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-11 21:38  6% ` Simon Wright
  2010-08-12  5:57  0%   ` Dmitry A. Kazakov
@ 2010-08-12 10:36  0%   ` Stephen Leake
  2010-08-12 12:48  7%     ` sjw
  1 sibling, 1 reply; 35+ results
From: Stephen Leake @ 2010-08-12 10:36 UTC (permalink / raw)


Simon Wright <simon@pushface.org> writes:

> Starting from the declaration of Generic_Complex_Arrays, I arrived at
>
>    with Ada.Numerics.Generic_Complex_Arrays;
>    with Ada.Numerics.Generic_Complex_Types;
>
>    generic
>       with package Complex_Types is new Ada.Numerics.Generic_Complex_Types
>         (Real);
>       with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
>         (Generic_Real_Arrays, Complex_Types);            -- <<<<<<<<<<<<<<<<<<
>       use Complex_Arrays;
>    package Ada.Numerics.Generic_Real_Arrays.Extensions is
>
>       function Eigenvalues (A : Real_Matrix) return Complex_Vector;
>
>    end Ada.Numerics.Generic_Real_Arrays.Extensions;
>
> but GNAT complains about Generic_Real_Arrays where I've indicated,
> saying it expects a package instance to instantiate a formal.

What is the actual error message? this idiom works for me in SAL:

generic
   with package Elementary is new Ada.Numerics.Generic_Elementary_Functions (Real_Type);
   with package Math_Scalar is new SAL.Gen_Math.Gen_Scalar (Elementary);
package SAL.Gen_Math.Gen_DOF_3 is


> I'd been hoping that -- in the context of a child of
> Generic_Real_Arrays -- the name would mean 'the current instantiation',
> but clearly not (GNAT GPL 2010, GCC 4.5.0).

Post complete code, I'll try it on my version of gnat.

-- 
-- Stephe



^ permalink raw reply	[relevance 0%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  2010-08-11 21:38  6% ` Simon Wright
@ 2010-08-12  5:57  0%   ` Dmitry A. Kazakov
  2010-08-12 19:45  5%     ` Simon Wright
  2010-08-12 10:36  0%   ` Stephen Leake
  1 sibling, 1 reply; 35+ results
From: Dmitry A. Kazakov @ 2010-08-12  5:57 UTC (permalink / raw)


On Wed, 11 Aug 2010 22:38:46 +0100, Simon Wright wrote:

> I was thinking about the next release, and contemplating
> 
>    --  Obtain the eigenvalues of a non-symmetric real matrix.
>    function Eigenvalues (A : Real_Matrix) return Complex_Vector;
> 
> It's easy enough to implement this in
> Ada.Numerics.Generic_Complex_Arrays.Extensions, but it seems slightly
> more natural to put it into Ada.Numerics.Generic_Real_Arrays.Extensions.
> 
> However, I'm puzzled about how to get Complex_Vector in there, because
> it's declared in Generic_Complex_Arrays (and of course you'd want to use
> the same instantiation throughout, not a local one).
> 
> Starting from the declaration of Generic_Complex_Arrays, I arrived at
> 
>    with Ada.Numerics.Generic_Complex_Arrays;
>    with Ada.Numerics.Generic_Complex_Types;
> 
>    generic
>       with package Complex_Types is new Ada.Numerics.Generic_Complex_Types
>         (Real);
>       with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
>         (Generic_Real_Arrays, Complex_Types);            -- <<<<<<<<<<<<<<<<<<
>       use Complex_Arrays;
>    package Ada.Numerics.Generic_Real_Arrays.Extensions is
> 
>       function Eigenvalues (A : Real_Matrix) return Complex_Vector;
> 
>    end Ada.Numerics.Generic_Real_Arrays.Extensions;
> 
> but GNAT complains about Generic_Real_Arrays where I've indicated,
> saying it expects a package instance to instantiate a formal.
>
> I'd been hoping that -- in the context of a child of
> Generic_Real_Arrays -- the name would mean 'the current instantiation',
> but clearly not (GNAT GPL 2010, GCC 4.5.0).

AFAIK it does not work, I tried something similar before. You cannot name
the parent package instance in the generic child declarations.
 
> Any thoughts?

(as always the first thought is: what a mess generics are! (:-))

I would not do that. It would only make instantiation tricky.
Generic_Complex_Arrays already has all types involved, so for the end user
it is much simpler when the extension package were its child.

Further I would name it 

generic
package Ada.Numerics.Generic_Complex_Arrays.
   Generic_Real_Extensions;
   ^^^^
or Generic_Real_Valued_Extensions;
   ^^^^^

It is always helpful to keep all name spaces clean of generic names.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[relevance 0%]

* Re: ANN: Ada 2005 Math Extensions, 20100810 release
  @ 2010-08-11 21:38  6% ` Simon Wright
  2010-08-12  5:57  0%   ` Dmitry A. Kazakov
  2010-08-12 10:36  0%   ` Stephen Leake
  0 siblings, 2 replies; 35+ results
From: Simon Wright @ 2010-08-11 21:38 UTC (permalink / raw)


I was thinking about the next release, and contemplating

   --  Obtain the eigenvalues of a non-symmetric real matrix.
   function Eigenvalues (A : Real_Matrix) return Complex_Vector;

It's easy enough to implement this in
Ada.Numerics.Generic_Complex_Arrays.Extensions, but it seems slightly
more natural to put it into Ada.Numerics.Generic_Real_Arrays.Extensions.

However, I'm puzzled about how to get Complex_Vector in there, because
it's declared in Generic_Complex_Arrays (and of course you'd want to use
the same instantiation throughout, not a local one).

Starting from the declaration of Generic_Complex_Arrays, I arrived at

   with Ada.Numerics.Generic_Complex_Arrays;
   with Ada.Numerics.Generic_Complex_Types;

   generic
      with package Complex_Types is new Ada.Numerics.Generic_Complex_Types
        (Real);
      with package Complex_Arrays is new Ada.Numerics.Generic_Complex_Arrays
        (Generic_Real_Arrays, Complex_Types);            -- <<<<<<<<<<<<<<<<<<
      use Complex_Arrays;
   package Ada.Numerics.Generic_Real_Arrays.Extensions is

      function Eigenvalues (A : Real_Matrix) return Complex_Vector;

   end Ada.Numerics.Generic_Real_Arrays.Extensions;

but GNAT complains about Generic_Real_Arrays where I've indicated,
saying it expects a package instance to instantiate a formal.

I'd been hoping that -- in the context of a child of
Generic_Real_Arrays -- the name would mean 'the current instantiation',
but clearly not (GNAT GPL 2010, GCC 4.5.0).

Any thoughts?



^ permalink raw reply	[relevance 6%]

* Re: Interfacing Ada with C
  @ 2010-08-05 20:25  5%                                     ` Simon Wright
  0 siblings, 0 replies; 35+ results
From: Simon Wright @ 2010-08-05 20:25 UTC (permalink / raw)


[-- Attachment #1: Type: text/plain, Size: 666 bytes --]

Jeffrey Carter <spam.jrcarter.not@spam.not.acm.org> writes:

> On 08/05/2010 10:24 AM, Ada novice wrote:
>>
>> They are much more accurate and better results of course. I wonder why
>> Matlab tends to be give very "accurate" answers. Normally 15 digits of
>> precision is also used there.
>
> You requested 15 decimal digits; your results are zero to 15 decimal
> places. If you output in non-scientific notation you'd get zero;
> presumably that's what Matlab is doing.

The attached update uses fixed notation, looks rather better.

Note that the reason for the lack of precision in the Test16 results is
likely because the inputs are only specified to 6 digits.


[-- Attachment #2: New test program --]
[-- Type: text/plain, Size: 5011 bytes --]

--  This package is free software; you can redistribute it and/or
--  modify it under terms of the GNU General Public License as
--  published by the Free Software Foundation; either version 3, or
--  (at your option) any later version.  It is distributed in the
--  hope that it will be useful, but WITHOUT ANY WARRANTY; without
--  even the implied warranty of MERCHANTABILITY or FITNESS FOR A
--  PARTICULAR PURPOSE.
--
--  You should have received a copy of the GNU General Public License
--  along with this program; see the file COPYING3.  If not, see
--  <http://www.gnu.org/licenses/>.
--
--  Copyright Simon Wright <simon@pushface.org>

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Generic_Real_Arrays;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Complex_Arrays.Extensions;

procedure Test_Extensions is

   subtype My_Float is Long_Float;
   package My_Float_IO is new Float_IO (My_Float);
   use My_Float_IO;

   package Real_Arrays
   is new Ada.Numerics.Generic_Real_Arrays (My_Float);
   package Complex_Types
   is new Ada.Numerics.Generic_Complex_Types (My_Float);
   package Complex_Arrays
   is new Ada.Numerics.Generic_Complex_Arrays (Real_Arrays, Complex_Types);
   package Extensions
   is new Complex_Arrays.Extensions;

   use Real_Arrays;
   use Complex_Types;
   use Complex_Arrays;

begin

   declare
   --  Values in yc's example
      Input : constant Complex_Matrix
        := (((8.0, 0.0), (-1.0, 0.0), (-5.0, 0.0)),
            ((-4.0, 0.0), (4.0, 0.0), (-2.0, 0.0)),
            ((18.0, 0.0), (-5.0, 0.0), (-7.0, 0.0)));
      Result : Complex_Vector (1 .. Input'Length (1));
   begin

      Put_Line ("Values from <143ef70b-7e74-426b-a621-a5fd157849be"
                  & "@x21g2000yqa.googlegroups.com>");

      Result := Extensions.Eigenvalues (Input);

      for J in Result'Range loop
         Put (Result (J).Re, Exp => 0);
         Put (" ");
         Put (Result (J).Im, Exp => 0);
         New_Line;
      end loop;

      New_Line;

   end;

   declare
   --  Values in Test 16 of
   --  http://people.sc.fsu.edu/~jburkardt/f_src/lapack/lapack_OSX_prb_output.txt
      Z : constant Complex := (0.0, 0.0);
      A : constant Complex := (2.44949, 0.0);
      B : constant Complex := (3.16228, 0.0);
      C : constant Complex := (3.46410, 0.0);
      Input : constant Complex_Matrix
        := (
            1 => (Z, A, Z, Z, Z, Z, Z),
            2 => (A, Z, B, Z, Z, Z, Z),
            3 => (Z, B, Z, C, Z, Z, Z),
            4 => (Z, Z, C, Z, C, Z, Z),
            5 => (Z, Z, Z, C, Z, B, Z),
            6 => (Z, Z, Z, Z, B, Z, A),
            7 => (Z, Z, Z, Z, Z, A, Z)
           );
   begin

      Put_Line ("Values in Test16 of "
                  & "http://people.sc.fsu.edu/~jburkardt/f_src/lapack"
                  & "/lapack_OSX_prb_output.txt");

      -- GNAT: Eigenvalues of symmetrix complex matrix are real
      Put_Line ("using Complex_Arrays.Eigenvalues");
      declare
         Result : constant Real_Vector := Complex_Arrays.Eigenvalues (Input);
      begin
         for J in Result'Range loop
            Put (Result (J), Exp => 0);
            New_Line;
         end loop;
      end;
      New_Line;

      --  Extension: Eigenvalues of general complex matrix are complex.
      Put_Line ("using Extensions.Eigenvalues");
      declare
         Result : constant Complex_Vector := Extensions.Eigenvalues (Input);
      begin
         for J in Result'Range loop
            Put (Result (J).Re, Exp => 0);
            Put (" ");
            Put (Result (J).Im, Exp => 0);
            New_Line;
         end loop;
      end;
      New_Line;

   end;

   declare
   --  Values from http://en.wikipedia.org/wiki/Skew-symmetric_matrix
      Input : constant Complex_Matrix
        := (((0.0, 0.0), (2.0, 0.0), (-1.0, 0.0)),
            ((-2.0, 0.0), (0.0, 0.0), (-4.0, 0.0)),
            ((1.0, 0.0), (4.0, 0.0), (0.0, 0.0)));
      Result : Complex_Vector (1 .. Input'Length (1));
   begin

      Put_Line
        ("Values from http://en.wikipedia.org/wiki/Skew-symmetric_matrix");

      Result := Extensions.Eigenvalues (Input);

      for J in Result'Range loop
         Put (Result (J).Re, Exp => 0);
         Put (" ");
         Put (Result (J).Im, Exp => 0);
         New_Line;
      end loop;
      New_Line;

   end;

   declare
   --  Values from http://en.wikipedia.org/wiki/Orthogonal_matrix
      Input : constant Complex_Matrix
        := (((0.0, 0.0), (-0.8, 0.0), (-0.6, 0.0)),
            ((0.8, 0.0), (-0.36, 0.0), (0.48, 0.0)),
            ((0.6, 0.0), (0.48, 0.0), (-0.64, 0.0)));
      Result : Complex_Vector (1 .. Input'Length (1));
   begin

      Put_Line
        ("Values from http://en.wikipedia.org/wiki/Orthogonal_matrix");

      Result := Extensions.Eigenvalues (Input);

      for J in Result'Range loop
         Put (Result (J).Re, Exp => 0);
         Put (" ");
         Put (Result (J).Im, Exp => 0);
         New_Line;
      end loop;
      New_Line;

   end;

end Test_Extensions;

^ permalink raw reply	[relevance 5%]

* Re: Interfacing Ada with C
  @ 2010-07-28 22:26  6%               ` Simon Wright
    0 siblings, 1 reply; 35+ results
From: Simon Wright @ 2010-07-28 22:26 UTC (permalink / raw)


Simon Wright <simon@pushface.org> writes:

> Ada novice <posts@gmx.us> writes:
>> On Jul 26, 1:21 am, Simon Wright <si...@pushface.org> wrote:
>>
>>> I've encoded a general complex eigenvalues function, interfacing to the
>>> LAPACK procedure zgeev
>
>> Thank you very much for your commendable efforts. This is a very good
>> example to demonstrate how Ada can be binded with LAPACK.
>
> Well, it's a start!

I've taken the plunge and started a SourceForge project for this. It's
at http://sourceforge.net/projects/gnat-math-extn/ -- under "Ada 2005
Math Extensions", click on [Develop] then on [Code].

I've chosen to use Mercurial (Hg) as the VCS, mainly to get a real-world
feel for using a DVCS (Distributed Version Control System). To downoad
the code, you'll need to install Hg - http://mercurial.selenic.com/ -
because I haven't actually made a code release yet!

If anyone feels moved to join in, just say (of course you need a SF
account to update the SF repository, but with Hg it should be possible
to work via patchsets .. )

For interest, the test program now looks like

   with Ada.Text_IO; use Ada.Text_IO;
   with Ada.Numerics.Generic_Real_Arrays;
   with Ada.Numerics.Generic_Complex_Types;
   with Ada.Numerics.Generic_Complex_Arrays.Extensions;

   procedure Test_Extensions is

      package Real_Arrays
      is new Ada.Numerics.Generic_Real_Arrays (Long_Float);
      package Complex_Types
      is new Ada.Numerics.Generic_Complex_Types (Long_Float);
      package Complex_Arrays
      is new Ada.Numerics.Generic_Complex_Arrays (Real_Arrays,
                                                  Complex_Types);
      package Extensions
      is new Complex_Arrays.Extensions;

      use Complex_Arrays;

      Input : Complex_Matrix (1 .. 3, 1 .. 3);
      Result : Complex_Vector (1 .. Input'Length (1));

   begin

      --  Values in yc's example
      Input := (((8.0, 0.0), (-1.0, 0.0), (-5.0, 0.0)),
                ((-4.0, 0.0), (4.0, 0.0), (-2.0, 0.0)),
                ((18.0, 0.0), (-5.0, 0.0), (-7.0, 0.0)));

      Result := Extensions.Eigenvalues (Input);

      for J in Result'Range loop
         Put_Line (Long_Float'Image (Result (J).Re)
                     & " "
                     & Long_Float'Image (Result (J).Im));
      end loop;

   end Test_Extensions;

(results as before)



^ permalink raw reply	[relevance 6%]

* Re: Inferring array index type from array object
  @ 2010-06-29 20:28  5%                   ` Damien Carbonne
  0 siblings, 0 replies; 35+ results
From: Damien Carbonne @ 2010-06-29 20:28 UTC (permalink / raw)


Le 29/06/2010 21:31, Warren a �crit :
> 1) The "inverse" of a complex number.

Why don't you use 1.0 / Z ?
It is defined in Ada.Numerics.Generic_Complex_Types:

    function "/" (Left : Real'Base; Right : Complex) return Complex;

Damien



^ permalink raw reply	[relevance 5%]

* Re: Ada noob here! Is Ada widely used?
  @ 2010-06-05 16:02  4%         ` Nasser M. Abbasi
  0 siblings, 0 replies; 35+ results
From: Nasser M. Abbasi @ 2010-06-05 16:02 UTC (permalink / raw)


On 6/5/2010 5:59 AM, Dmitry A. Kazakov wrote:


> Sorry guys, maybe I missed the point, but Ada does have complex types. See
> ARM G.1.
>

I meant complex type in ada is not an elementary type. as in

http://www.adaic.org/standards/05rm/html/RM-3-2.html

"The elementary types are the scalar types (discrete and real) and the 
access  types (whose values provide access to objects or subprograms). 
Discrete types are either integer types or are defined by enumeration of 
their values (enumeration types). Real types are either floating point 
types or fixed point types."

and

http://en.wikibooks.org/wiki/Ada_Programming/Type_System

I copied the list from above:

"Here is a broad overview of each category of types; please follow the 
links for detailed explanations. Inside parenthesis there are 
equivalences in C and Pascal for readers familiar with those languages."

Signed Integers (int, INTEGER)
Unsigned Integers (unsigned, CARDINAL)
unsigned they also have wrap-around functionality.
Enumerations (enum, char, bool, BOOLEAN)
Floating point (float, double, REAL)
Ordinary and Decimal Fixed Point (DECIMAL)
Arrays ( [ ], ARRAY [ ] OF, STRING )
Record (struct, class, RECORD OF)
Access (*, ^, POINTER TO)
Task & Protected (no equivalence in C or Pascal)
Interfaces (no equivalence in C or Pascal)

I do not see complex type there :)

Ofcourse, a standard generic package for complex type, I knew that.

In FORTRAN:

http://www.fortran.com/F77_std/rjcnf-4.html#sh-4

"4.1 Data Types
The six types of data are:

    1. Integer
    2. Real
    3. Double precision
    4. Complex
    5. Logical
    6. Character

"

So, complex is an elementary type, like an integer is.

I am learning to use complex numbers in Ada from wiki Ada book, was 
looking at the examples here:

http://en.wikibooks.org/wiki/Ada_Programming/Mathematical_calculations#Complex_arithmethic

and it seem many packages need to be instantiated just to use complex 
numbers.

with Ada.Text_IO.Complex_IO;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Complex_Elementary_Functions;

etc..

I just meant it seems "easier" to use complex numbers in FORTRAN than 
Ada, just because one does not to do all this instantiating every where. 
But I hope to learn to use complex numbers better in Ada, I have very 
little experiences with this in Ada.

--Nasser




^ permalink raw reply	[relevance 4%]

* Tasking for Mandelbrot program
@ 2009-09-27  1:08  4% Georg Bauhaus
  0 siblings, 0 replies; 35+ results
From: Georg Bauhaus @ 2009-09-27  1:08 UTC (permalink / raw)


A Mandelbrot program has been submitted to the language
Shootout by Jim Rogers, Pascal Obry, and
Gautier de Montmollin.  Given the no tricks approach
that it shares with other entries, it is performing
well, I'd think.  But, it is sequential.

The patch below adds tasking.

A few notes on patch:
The computation is split into parts, one per task;
many entries seem to use a similar approach.
The number of tasks is set high, task switching does
not seem to matter much in this program.
The alternative of making the environment task perform
the same subprogram as the tasks concurrently
and matching the number of tasks to the number of cores
was not significantly faster.  The alternative
spoils the simplicity of the main block, so I set
a higher number of tasks and got basically the same
performance.

Maybe these are possible improvements:

- Ada.Streams.Stream_IO? (In total absence of Text_IO,
  GNAT.IO performs well.)

- adjust compilation options to match the FPT hardware
  (type Real is now digits 16 because of this, cf.
  the spectral-norm entry at the Shootout)

Comments?  Can I ask what the authors think?


pragma Restrictions (No_Abort_Statements);
pragma Restrictions (Max_Asynchronous_Select_Nesting => 0);

with Ada.Command_Line; use Ada.Command_Line;
with Ada.Numerics.Generic_Complex_Types;

with Interfaces;       use Interfaces;
with GNAT.IO;          use GNAT.IO;


procedure Mandelbrot is
   type Real is digits 16;
   package R_Complex is new Ada.Numerics.Generic_Complex_Types (Real);
   use R_Complex;
   Iter           : constant := 50;
   Limit          : constant := 4.0;
   Size           : Positive;
   Zero           : constant Complex := (0.0, 0.0);
   Two_on_Size    : Real;

   subtype Output_Queue is String;
   type Output is access Output_Queue;

   task type X_Step is
      entry Compute_Z (Y1, Y2 : Natural);
      entry Get_Output (Result : out Output; Last : out Natural);
   end X_Step;

   procedure Allocate_Output_Queue (Y1, Y2 : Natural; Result : out Output);

   procedure Compute
     (Y1, Y2 : Natural; Result : Output; Last : out Natural)
   is
      Bit_Num     : Natural    := 0;
      Byte_Acc    : Unsigned_8 := 0;
      Z, C        : Complex;
   begin
      Last := 0;
      for Y in Y1 .. Y2 - 1 loop
         for X in 0 .. Size - 1 loop
            Z := Zero;
            C := Two_on_Size * (Real (X), Real (Y)) - (1.5, 1.0);

            declare
               Z2 : Complex;
            begin
               for I in 1 .. Iter + 1 loop
                  Z2 := (Z.re ** 2, Z.im ** 2);
                  Z  := (Z2.re - Z2.im, 2.0 * Z.re * Z.im) + C;
                  exit when Z2.re + Z2.im > Limit;
               end loop;

               if Z2.re + Z2.im > Limit then
                  Byte_Acc := Shift_Left (Byte_Acc, 1) or 16#00#;
               else
                  Byte_Acc := Shift_Left (Byte_Acc, 1) or 16#01#;
               end if;
            end;

            Bit_Num := Bit_Num + 1;

            if Bit_Num = 8 then
               Last := Last + 1;
               Result (Last) := Character'Val (Byte_Acc);
               Byte_Acc := 0;
               Bit_Num  := 0;
            elsif X = Size - 1 then
               Byte_Acc := Shift_Left (Byte_Acc, 8 - (Size mod 8));
               Last := Last + 1;
               Result (Last) := Character'Val (Byte_Acc);
               Byte_Acc := 0;
               Bit_Num  := 0;
            end if;
         end loop;
      end loop;
   end Compute;

   task body X_Step is
      Data        : Output;
      Pos         : Natural;
      Y1, Y2      : Natural;
   begin
      accept Compute_Z (Y1, Y2 : Natural) do
         X_Step.Y1 := Y1;
         X_Step.Y2 := Y2;
      end Compute_Z;

      Allocate_Output_Queue (Y1, Y2, Data);
      Compute (Y1, Y2, Data, Pos);

      accept Get_Output (Result : out Output; Last : out Natural) do
         Result := Data;
         Last := Pos;
      end Get_Output;
   end X_Step;

   procedure Allocate_Output_Queue (Y1, Y2 : Natural; Result : out
Output) is
   begin
      Result := new Output_Queue (1 .. (Y2 - Y1 + 8) * Size / 8);
   end Allocate_Output_Queue;


begin
   Size := Positive'Value (Argument (1));
   Two_on_Size := 2.0 / Real (Size);

   Put_Line ("P4");
   Put_Line (Argument (1) & " " & Argument (1));

   declare
      No_Of_Workers : constant := 16;
      Chunk_Size    : constant Positive :=
        (Size + No_Of_Workers) / No_Of_Workers;
      Pool          : array (0 .. No_Of_Workers) of X_Step;
      pragma          Assert (Pool'Length * Chunk_Size >= Size);
      Buffer        : Output;
      Last          : Natural;
   begin
      pragma Assert (Pool'First = 0);

      for P in Pool'Range loop
         Pool (P).Compute_Z
           (Y1 => P * Chunk_Size,
            Y2 => Positive'Min ((P + 1) * Chunk_Size, Size));
      end loop;

      for P in Pool'Range loop
         Pool (P).Get_Output (Buffer, Last);
         Put (Buffer (Buffer'First .. Last));
      end loop;
   end;

end Mandelbrot;



^ permalink raw reply	[relevance 4%]

* A question relating to package interfaces.fortran
  @ 2003-03-01 10:02  6%     ` Zheng Long Gen
  0 siblings, 0 replies; 35+ results
From: Zheng Long Gen @ 2003-03-01 10:02 UTC (permalink / raw)
  To: comp.lang.ada mail to news gateway

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb18030", Size: 1954 bytes --]

Hi, all,
    Forgive my ignorance, if this is a repeated post.
    In the following code, one tries to do math calculation on values of 
complex types defined in interfaces.fortran. 
-----------------------------------------------------------------------
with Interfaces.Fortran;
use Interfaces.Fortran;
with Ada.Numerics.Generic_Complex_Elementary_Functions;
procedure Complex_Type is
   package Complex_Elementary_Functions is 
      new Ada.Numerics.Generic_Complex_Elementary_Functions
     (Single_Precision_Complex_Types);
   use Complex_Elementary_Functions;
   C1,c2:Complex:=(0.5,0.0);
begin
   C2:=Sin(C1);
end;
------------------------------------------------------------------------
Compiling this piece of code(gnat 3.13p, Redhat 7.3.2)  will result in the 
following message:
gnatgcc -c -g -gnatq complex_type.adb
complex_type.adb:10:12: expected type 
"Ada.Numerics.Generic_Complex_Types.Complex" from instance at i-fortra.ads:37
complex_type.adb:10:12: found type "Interfaces.Fortran.Complex"

     package interfaces.fortran defines types to interface with fortran 
subprograms.  The complex  type is defined in the following way: (from RM 
B.5)
---------------------------------------------------------------------------
   package Single_Precision_Complex_Types is
      new Ada.Numerics.Generic_Complex_Types (Real);

   type Complex is new Single_Precision_Complex_Types.Complex;

   subtype Imaginary is Single_Precision_Complex_Types.Imaginary;
----------------------------------------------------------------------------
The problem is : 
    Type complex is a new type. It is different from its ancester defined in 
single_precision_complex_types. It should be a subtype of 
single_precision_complex_types.complex , just like the definition of "subtype 
Imaginary". Otherwise, it is totally useless. What do you think of it?

    Many thanks in advance.
    zhenggen
    20020301



^ permalink raw reply	[relevance 6%]

* Problematic type definition in Interfaces.Fortran
@ 2001-11-26 15:15  5% Jacob Sparre Andersen
  0 siblings, 0 replies; 35+ results
From: Jacob Sparre Andersen @ 2001-11-26 15:15 UTC (permalink / raw)


I have noticed what I consider a rather problematic type
definition in package Interfaces.Fortran. RM B.5(9):

type Complex is new Single_Precision_Complex_Types.Complex;

Since package
Ada.Numerics.Generic_Complex_Elementary_Functions is
instantiated using an instantiation of package
Ada.Numerics.Generic_Complex_Types, it is not possible to
instantiate it for type Interfaces.Fortran.Complex.

And authors of Fortran bindings (such as "lapada") have a
habit of using type Interfaces.Fortran.Complex. :-(

Here is a code example that illustrates the problem:

----------
with Ada.Numerics.Generic_Complex_Elementary_Functions;
with Interfaces.Fortran; use Interfaces.Fortran;

procedure Hack_Virker is
   package KomplekseFunktioner is
     new Ada.Numerics.Generic_Complex_Elementary_Functions
       (Interfaces.Fortran.Single_Precision_Complex_Types);
   use KomplekseFunktioner;

   Psi :
Interfaces.Fortran.Single_Precision_Complex_Types.Complex;
   II  : constant
Interfaces.Fortran.Single_Precision_Complex_Types.Complex
           := (0.0, 1.0);
begin -- Hack_Virker
   Psi := Exp(II);
end Hack_Virker;
----------

The problem is that since "Interfaces.Fortran.Complex" is
derived from
"Interfaces.Fortran.Single_Precision_Complex_Types.Complex",
and not just a subtype of
"Interfaces.Fortran.Single_Precision_Complex_Types.Complex",
there is no package to use for instantiating package
Ada.Numerics.Generic_Complex_Elementary_Functions.

Is there a very good reason for not making
"Interfaces.Fortran.Complex" a subtype of
"Interfaces.Fortran.Single_Precision_Complex_Types.Complex"?

Jacob
-- 
Sk�ne Sj�lland Linux User Group - http://www.sslug.dk/
N�ste m�de: IT-lovgivning.
Tirsdag den 27. november 2001 i Symbion, Fruebjergvej 3.



^ permalink raw reply	[relevance 5%]

* Re: Record type with check?
  @ 2001-01-10 17:05  3%     ` Nick Roberts
  0 siblings, 0 replies; 35+ results
From: Nick Roberts @ 2001-01-10 17:05 UTC (permalink / raw)


I'd like to throw in a couple of thoughts.

First, although it is generally good to define abstract types to enforce the
properties expected, occasionally it is better not to, either because it
would be overkill for a simple program, or (very unusual) because it would
slow down the program too much.

The other thought is that you may need to consider solitaire and null
ranges. If you enforce A < B, you will not be able to have a range that
consists of one integer. Even if you enforce only A <= B, how will you
represent a null range? If your problem does not require solitaire or null
ranges, then this point might not matter; otherwise, it will!

One further, perhaps rather esoteric, thought. You may - possibly, but not
necessarily - find that it is better to define an abstract data type (ADT)
at a level higher than for type Datum. For example, you might find it better
to define an ADT for a Datum_List - which holds a list of ranges - and have
operations for it such as below.

   type Datum_List is limited private;

   procedure Delete_All (List: in out Datum_List); -- makes empty
   procedure Add_Datum (List: in out Datum_List; Lo, Hi: in Integer);

   procedure Restart (List: in out Datum_List);
   procedure Advance (List: in out Datum_List);
   function At_End (List: in Datum_List) return Boolean;
   function Lo (List: in Datum_List) return Integer;
   function Hi (List: in Datum_List) return Integer;

Here, it is the procedure Add_Datum which checks that Lo and Hi have
mutually acceptable values. The essence of this example is that the
abstraction is at the level of the list of data, rather than the individual
datum. Sometimes it makes sense to do this, and sometimes it doesn't,
depending on the problem and the circumstances of the program. You might
like to compare this example with the way complex numbers are handled within
Ada.Numerics.Generic_Complex_Types (see RM95 Annex G).

Good luck,

--
Nick Roberts
http://www.AdaOS.org






^ permalink raw reply	[relevance 3%]

* Is Interfaces.Fortran Mandatory or Optional?
@ 1997-10-02  0:00  6% John Harbaugh
  0 siblings, 0 replies; 35+ results
From: John Harbaugh @ 1997-10-02  0:00 UTC (permalink / raw)



RM 1.1.2 identifies Annex B as part of the core language, and Annex G as
a Specialized Needs Annex.  Furthermore

17   All implementations shall conform to the core language.  In
addition, an
implementation may conform separately to one or more Specialized Needs
Annexes.

RM B.5 (mandatory) specifies Interfaces.Fortran as
4   with Ada.Numerics.Generic_Complex_Types;  -- see G.1.1
    pragma Elaborate_All(Ada.Numerics.Generic_Complex_Types);
    package Interfaces.Fortran is ...

My question is: How can an implementation claim to support Annex B if it
does not also support Annex G?

Any clarification would be greatly appreciated.

	- John




^ permalink raw reply	[relevance 6%]

* Package parameters in generic packages
@ 1996-07-08  0:00  4% Chris Papademetrious
  0 siblings, 0 replies; 35+ results
From: Chris Papademetrious @ 1996-07-08  0:00 UTC (permalink / raw)



 I am seriously confused about how package parameters are supposed to
work.  Everyone is familiar with the syntax:

generic
  with package P is new Q(<>);
package X is
...
end X;

 where a generic package itself is passed as a parameter for X so that
P inherits the generic parameter that this parameter package was
created with.  I read in "Programming in Ada 95" by Barnes that it is
a special case that when the default form (<>) is used, the formal
parameter in package Q is also visible in P.  Thus, if Q was defined
as:

generic
  type Real is digits (<>);
package Q is
..
end Q;

 then the type Real is able to be referenced as P.Real *inside* of X.
However, I was not able to reference P.Real inside of the package body
of X, only in the package *specification* of X.  Is this normal?  To
get around this, I had to do something like this in the specification:

 subtype Foo is P.Real;

 then use this Foo type in the package body of X!  Messy, messy.  What
am I not understanding?  Incidentally, this all comes into play
because I am trying to create a vectors/matrix package that supports
complex numbers, but I need access to

  Ada.Numerics.Generic_Complex_Types.Real

 inside of the package for some of the vector/matrix operations.  Any
help appreciated.  I am hoping that the entire Ada 95 community will
benefit from my work when this package is released.


-=-=-=-=-=-=-=-=-=-=-=-=-
 Chris Papademetrious
 Data Fusion Laboratory
 Drexel University
 Philadelphia, PA
-=-=-=-=-=-=-=-=-=-=-=-=-





^ permalink raw reply	[relevance 4%]

* Ada FAQ: Programming with Ada (part 3 of 4)
@ 1996-05-30  0:00  2% Magnus Kempe
  0 siblings, 0 replies; 35+ results
From: Magnus Kempe @ 1996-05-30  0:00 UTC (permalink / raw)



Archive-name: computer-lang/Ada/programming/part3
Comp-lang-ada-archive-name: programming/part3
Posting-Frequency: monthly
Last-modified: 22 May 1996
Last-posted: 23 April 1996

                               Ada Programmer's
                       Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

    Recent changes to this FAQ are listed in the first section after the table
    of contents. This document is under explicit copyright.

This is part 3 of a 4-part posting; part 1 contains the table of contents.
Part 2 begins with question 5.
Part 4 begins with question 9.
Parts 1 and 2 should be the previous postings in this thread.
Part 4 should be the next posting in this thread.


6: Ada Numerics


6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?


   ftp.rational.com
          Freeware version of the ISO math packages on Rational's FTP
          server. It's a binding over the C Math library, in
          public/apex/freeware/math_lib.tar.Z

   archimedes.nosc.mil
          Stuff of high quality in pub/ada The random number generator
          and random deviates are recommended. These are mirrored at the
          next site, wuarchive.

   wuarchive.wustl.edu
          Site of PAL, the Public Ada Library: math routines scattered
          about in the directories under languages/ada in particular, in
          subdirectory swcomps

   source.asset.com
          This is not an anonymous ftp site for math software. What you
          should do is log on anonymously under ftp, and download the
          file asset.faq from the directory pub. This will tell you how
          to get an account.

   ftp.cs.kuleuven.ac.be
          Go to directory pub/Ada-Belgium/cdrom. There's a collection of
          math intensive software in directory swcomps. Mirrors some of
          PAL at wuarchive.wustl.edu.

   sw-eng.falls-church.va.us
          Go to directory public/AdaIC/source-code/bindings/ADAR-bindings
          to find extended-precision decimal arithmetic (up to 18
          digits). Includes facilities for COBOL-like formatted output.


6.2: How can I write portable code in Ada 83 using predefined types like Float
and Long_Float? Likewise, how can I write portable code that uses Math
functions like Sin and Log that are defined for Float and Long_Float?

   (from Jonathan Parker)

   Ada 83 was slow to arrive at a standard naming convention for
   elementary math functions and complex numbers. Furthermore, you'll
   find that some compilers call the 64-bit floating point type
   Long_Float; other compilers call it Float. Fortunately, it is easy to
   write programs in Ada that are independent of the naming conventions
   for floating point types and independent of the naming conventions of
   math functions defined on those types.

   One of the cleanest ways is to make the program generic:

     generic
       type Real is digits <>;
       with function Arcsin (X : Real) return Real is <>;
       with function    Log (X : Real) return Real is <>;
       --  This is the natural log, inverse of Exp(X), sometimes written Ln(X).
     package Example_1 is
       ...
     end Example_1;


   So the above package doesn't care what the name of the floating point
   type is, or what package the Math functions are defined in, just as
   long as the floating point type has the right attributes (precision
   and range) for the algorithm, and likewise the functions. Everything
   in the body of Example_1 is written in terms of the abstract names,
   Real, Arcsin, and Log, even though you instantiate it with compiler
   specific names that can look very different:

      package Special_Case is new Example_1 (Long_Float, Asin, Ln);


   The numerical algorithms implemented by generics like Example_1 can
   usually be made to work for a range of floating point precisions. A
   well written program will perform tests on Real to reject
   instantiations of Example_1 if the floating points type is judged
   inadequate. The tests may check the number of digits of precision in
   Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
   largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
   These tests are often placed after the begin statement of package
   body, as in:

     package body Example_1 is
       ...
     begin
       if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
         raise Program_Error;
       end if;
     end Example_1;


   Making an algorithm as abstract as possible, (independent of data
   types as much as possible) can do a lot to improve the quality of the
   code. Support for abstraction is one of the many things Ada-philes
   find so attractive about the language. The designers of Ada 95
   recognized the value of abstraction in the design of numeric
   algorithms and have generalized many of the features of the '83 model.
   For example, no matter what floating point type you instantiate
   Example_1 with, Ada 95 provides you with functions for examining the
   exponent and the mantissas of the numbers, for truncating, determining
   exact remainders, scaling exponents, and so on. (In the body of
   Example_1, and in its spec also of course, these functions are
   written, respectively: Real'Exponent(X), Real'Fraction(X),
   Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
   others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
   on the base type of Real (called Real'Base) which is liable to have
   greater precision and range than type Real.

   It is rare to see a performance loss when using generics like this.
   However, if there is an unacceptable performance hit, or if generics
   cannot be used for some other reason, then subtyping and renaming will
   do the job. Here is an example of renaming:

     with Someones_Math_Lib;
     procedure Example_2 is

       subtype Real is Long_Float;

       package  Math renames Someones_Math_Lib;
       function Arcsin(X : Real) return Real renames Math.Asin
       function   Log (X : Real) return Real renames Math.  Ln;

       --  Everything beyond this point is abstract with respect to
       --  the names of the floating point (Real), the functions (Arcsin
       --  and Log), and the package that exported them (Math).
       ...
     end Example_2;


   I prefer to make every package and subprogram (even test procedures)
   as compiler independent and machine portable as possible. To do this
   you move all of the renaming of compiler dependent functions and all
   of the "withing" of compiler dependent packages to a single package.
   In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
   the 8-byte floating point type to Real_8, and makes sure the math
   functions follow the Ada 95 standard, at least in name. In this
   approach Math_Lib_8 is the only compiler dependent component.

   There are other, perhaps better, ways also. See for example, "Ada In
   Action", by Do-While Jones for a generic solution.

   Here's the spec of Math_Lib_8, which is a perfect subset of package
   Math_Env_8, available by FTP in file
   ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada


--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries.  It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61.  These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics.   It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments.  The issues are the same with most math libraries.
--***************************************************************

--with Math_Lib;                                  -- Meridian DOS Ada.
  with Long_Float_Math_Lib;                       -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is

--subtype Real_8 is Float;                        -- Meridian 8-byte Real
  subtype Real_8 is Long_Float;                   -- Dec VMS  8-byte Real

 --package Math renames Math_Lib;                 -- Meridian DOS Ada
   package Math renames Long_Float_Math_Lib;      -- Dec VMS
 --package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);

   --  The above instantiation of the Ada.Numerics child package works on
   --  GNAT, or any other Ada 95 compiler.  Its here if you want to use
   --  an Ada 95 compiler to compile Ada 83 programs based on this package.

   function Cos (X : Real_8) return Real_8 renames Math.Cos;
   function Sin (X : Real_8) return Real_8 renames Math.Sin;
   function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
   function Exp (X : Real_8) return Real_8 renames Math.Exp;

 --function Log (X : Real_8) return Real_8 renames Math.Ln;        -- Meridian
   function Log (X : Real_8) return Real_8 renames Math.Log;       -- Dec VMS
 --function Log (X : Real_8) return Real_8 renames Math.Log;       -- Ada 95

 --function Arcsin (X : Real_8) return Real_8 renames Math.Asin;   -- Dec VMS
 --function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
   function Arcsin (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Arccos (X : Real_8) return Real_8 renames Math.Acos;   -- Dec VMS
 --function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
   function Arccos (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
   function Real_8_Floor (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

end Math_Lib_8;


6.3: Is Ada any good at numerics, and where can I learn more about it?

   First of all, a lot of people find the general Ada philosophy
   (modularity, strong-typing, readable syntax, rigorous definition and
   standardization, etc.) to be a real benefit in numerical programming,
   as well as in many other types of programming. But Ada --and
   especially Ada 95-- was also designed to meet the special requirements
   of number-crunching applications.

   The following sketches out some of these features. Hopefully a little
   of the flavor of the Ada philosophy will get through, but the best
   thing you can do at present is to read the two standard reference
   documents, the Ada 95 Rationale and Reference Manual. Below the GNU
   Ada 95 compiler is referred to several times. This compiler can be
   obtained by anonymous FTP from cs.nyu.edu, and at mirror sites
   declared in the README file of directory pub/gnat.

   1. Machine portable floating point declarations. (Ada 83 and Ada 95)
          If you declare "type Real is digits 14", then type Real will
          guarantee you (at least) 14 digits of precision independently
          of machine or compiler. In this case the base type of type Real
          will usually be the machine's 8-byte floating point type. If an
          appropriate base type is unavailable (very rare), then the
          declaration is rejected by the compiler.

   2. Extended precision for initialization of floating point. (Ada 83
          and Ada 95)
          Compilers are required to employ
          extended-precision/rational-arithmetic routines so that
          floating point variables and constants can be correctly
          initialized to their full precision.

   3. Generic packages and subprograms. (Ada 83 and Ada 95)
          Algorithms can be written so that they perform on abstract
          representations of the data structure. Support for this is
          provided by Ada's generic facilities (what C++ programmers
          would call templates).

   4. User-defined operators and overloaded subprograms. (Ada 83 and Ada
          95)
          The programmer can define his own operators (functions like
          "*", "+", "abs", "xor", "or", etc.) and define any number of
          subprograms with the same name (provided they have different
          argument profiles).

   5. Multitasking. (Ada 83 and Ada 95)
          Ada facilities for concurrent programming (multitasking) have
          traditionally found application in simulations and
          distributed/parallel programming. Ada tasking is an especially
          useful ingredient in the Ada 95 distributed programming model,
          and the combination of the two makes it possible to design
          parallel applications that have a high degree of operating
          system independence and portability. (More on this in item 6
          below.)

   6. Direct support for distributed/parallel computing in the language.
          (Ada 95)
          Ada 95 is probably the first internationally standardized
          language to combine in the same design complete facilities for
          multitasking and parallel programming. Communication between
          the distributed partitions is via synchronous and asynchronous
          remote procedure calls.

          Good discussion, along with code examples, is found in the
          Rationale, Part III E, and in the Ada 95 Reference Manual,
          Annex E. See also "Ada Letters", Vol. 13, No. 2 (1993), pp. 54
          and 78, and Vol. 14, No. 2 (1994), p. 80. (Full support for
          these features is provided by compilers that conform to the Ada
          95 distributed computing Annex. This conformance is optional,
          but for instance GNAT, the Gnu Ada 95 compiler, will meet these
          requirements.)

   7. Attributes of floating point types. (Ada 83 and Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that return the essential
          characteristics of the type. For example, if you declare "type
          Real is digits 15" then you can get the max exponent of objects
          of type Real from Real'Machine_Emax. Similarly, the size of the
          Mantissa, the Radix, the largest Real, and the Rounding policy
          of the arithmetic are given by Real'Machine_Mantissa,
          Real'Machine_Radix, Real'Last, and Real'Machine_Rounds. There
          are many others.

          (See Ada 95 Reference Manual, clause 3.5, subclause 3.5.8 and
          A.5.3, as well as Part III sections G.2 and G.4.1 of the Ada 95
          Rationale.)

   8. Attribute functions for floating point types. (Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that operate on objects of that
          type. For example, if you declare "type Real is digits 15" then
          Real'Remainder (X, Y) returns the exact remainder of X and Y: X
          - n*Y where n is the integer nearest X/Y. Real'Truncation(X),
          Real'Max(X,Y), Real'Rounding(X) have the usual meanings.
          Real'Fraction(X) and Real'Exponent(X) break X into mantissa and
          exponent; Real'Scaling(X, N) is exact scaling: multiplies X by
          Radix**N, which can be done by incrementing the exponent by N,
          etc. (See citations in item 7.)

   9. Modular arithmetic on integer types. (Ada 95)
          If you declare "type My_Unsigned is mod N", for arbitrary N,
          then arithmetic ("*", "+", etc.) on objects of type My_Unsigned
          returns the results modulo N. Boolean operators "and", "or",
          "xor", and "not" are defined on the objects as though they were
          arrays of bits (and likewise return results modulo N). For N a
          power of 2, the semantics are similar to those of C unsigned
          types.

   10. Generic elementary math functions for floating point types. (Ada
          95)
          Required of all compilers, and provided for any floating point
          type: Sqrt, Cos, Sin, Tan, Cot, Exp, Sinh, Cosh, Tanh, Coth,
          and the inverse functions of each of these, Arctan, Log,
          Arcsinh, etc. Also, X**Y for floating point X and Y. Compilers
          that conform to the Numerics Annex meet additional accuracy
          requirements.

          (See subclause A.5.1 of the Ada 95 RM, and Part III, Section
          A.3 of the Ada 95 Rationale.)

   11. Complex numbers. (Ada 95)
          Fortran-like, but with a new type called Imaginary. Type
          "Imaginary" allows programmers to write expressions in such a
          way that they are easier to optimize, more readable and appear
          in code as they appear on paper. Also, the ability to declare
          object of pure imaginary type reduces the number of cases in
          which premature type conversion of real numbers to complex
          causes floating point exceptions to occur. (Provided by
          compilers that conform to the Numerics Annex. The Gnu Ada 95
          compiler supports this annex, so the source code is freely
          available.)

   12. Generic elementary math functions for complex number types. (Ada
          95)
          Same functions supported for real types, but with complex
          arguments. Standard IO is provided for floating point types and
          Complex types. (Only required of compilers that support the
          Numerics Annex, like Gnu Ada.)

   13. Pseudo-random numbers for discrete and floating point types. (Ada
          95)
          A floating point pseudo-random number generator (PRNG) provides
          output in the range 0.0 .. 1.0. Discrete: A generic PRNG
          package is provided that can be instantiated with any discrete
          type: Boolean, Integer, Modular etc. The floating point PRNG
          package and instances of the (discrete) PRNG package are
          individually capable of producing independent streams of random
          numbers. Streams may be interrupted, stored, and resumed at
          later times (generally an important requirement in
          simulations). In Ada it is considered important that multiple
          tasks, engaged for example in simulations, have easy access to
          independent streams of pseudo random numbers. The Gnu Ada 95
          compiler provides the cryptographically secure X**2 mod N
          generator of Blum, Blum and Shub.

          (See subclause A.5.2 of the Ada 95 Reference Manual, and part
          III, section A.3.2 of the Ada Rationale.)

   14. Well-defined interfaces to Fortran and other languages. (Ada 83
          and Ada 95)
          It has always been a basic requirement of the language that it
          provide users a way to interface Ada programs with foreign
          languages, operating system services, GUI's, etc. Ada can be
          viewed as an interfacing language: its module system is
          composed of package specifications and separate package bodies.
          The package specifications can be used as strongly-type
          interfaces to libraries implemented in foreign languages, as
          well as to package bodies written in Ada. Ada 95 extends on
          these facilities with package interfaces to the basic data
          structures of C, Fortran, and COBOL and with new pragmas. For
          example, "pragma Convention(Fortran, M)" tells the compiler to
          store the elements of matrices of type M in the Fortran
          column-major order. (This pragma has already been implemented
          in the Gnu Ada 95 compiler. Multi- lingual programming is also
          a basic element of the Gnu compiler project.) As a result,
          assembly language BLAS and other high performance linear
          algebra and communications libraries will be accessible to Ada
          programs.

          (See Ada 95 Reference Manual: clause B.1 and B.5 of Annex B,
          and Ada 95 Rationale: Part III B.)


6.4: How do I get Real valued and Complex valued math functions in Ada 95?

   (from Jonathan Parker)

   Complex type and functions are provided by compilers that support the
   numerics Annex. The packages that use Float for the Real number and
   for the Complex number are:

     Ada.Numerics.Elementary_Functions;
     Ada.Numerics.Complex_Types;
     Ada.Numerics.Complex_Elementary_Functions;


   The packages that use Long_Float for the Real number and for the
   Complex number are:

     Ada.Numerics.Long_Elementary_Functions;
     Ada.Numerics.Long_Complex_Types;
     Ada.Numerics.Long_Complex_Elementary_Functions;


   The generic versions are demonstrated in the following example. Keep
   in mind that the non-generic packages may have been better tuned for
   speed or accuracy. In practice you won't always instantiate all three
   packages at the same time, but here is how you do it:

     with Ada.Numerics.Generic_Complex_Types;
     with Ada.Numerics.Generic_Elementary_Functions;
     with Ada.Numerics.Generic_Complex_Elementary_Functions;

     procedure Do_Something_Numerical is

       type Real_8 is digits 15;

       package Real_Functions_8 is
         new Ada.Numerics.Generic_Elementary_Functions (Real_8);

       package Complex_Nums_8 is
         new Ada.Numerics.Generic_Complex_Types (Real_8);

       package Complex_Functions_8 is
         new Ada.Numerics.Generic_Complex_Elementary_Functions
           (Complex_Nums_8);

       use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
       ...
       ... -- Do something
       ...
     end Do_Something_Numerical;


6.5: What libraries or public algorithms exist for Ada?

   An Ada version of Fast Fourier Transform is available. It's in
   journal "Computers & Mathematics with Applications," vol. 26, no. 2,
   pp. 61-65, 1993, with the title:

   "Analysis of an Ada Based Version of Glassman's General N Point Fast
   Fourier Transform"

   The package is now available in the AdaNET repository, object #: 6728,
   in collection: Transforms. If you're not an AdaNET user, contact Peggy
   Lacey (lacey@rbse.mountain.net).

     _________________________________________________________________


7: Efficiency of Ada Constructs


7.1: How much extra overhead do generics have?

   If you overgeneralize the generic, there will be more work to do for
   the compiler. How do you know when you have overgeneralized? For
   instance, passing arithmetic operations as parameters is a bad sign.
   So are boolean or enumeration type generic formal parameters. If you
   never override the defaults for a parameter, you probably
   overengineered.

   Code sharing (if implemented and requested) will cause an additional
   overhead on some calls, which will be partially offset by improved
   locality of reference. (Translation, code sharing may win most when
   cache misses cost most.) If a generic unit is only used once in a
   program, code sharing always loses.

   R.R. Software chose code sharing as the implementation for generics
   because 2 or more instantiations of Float_Io in a macro implementation
   would have made a program too large to run in the amount of memory
   available on the PC machines that existed in 1983 (usually a 128k or
   256k machine).

   Generics in Ada can also result in loss of information which could
   have helped the optimizer. Since the compiler is not restricted by Ada
   staticness rules within a single module, you can often avoid penalties
   by declaring (or redeclaring) bounds so that they are local:

     package Global is
       subtype Global_Int is
         Integer range X..Y;

       ...
     end Global;


     with Global;
     package Local is
       subtype Global_Int is
         Global.Global_Int;

       package Some_Instance is
         new Foo (Global_Int);

       ...
     end Local;


   Ada rules say that having the subtype redeclared locally does not
   affect staticness, but on a few occasions optimizers have been caught
   doing a much better job. Since optimizers are constantly changing,
   they may have been caught just at the wrong time.


7.2: How does Ada compare to other languages in efficiency of code?

   Ada vs. C: An analysis at Tartan found that Ada and C had fairly
   similar performance, with Ada having a slight edge. See "C vs. Ada:
   Arguing Performance Religion" by David Syiek, ACM Ada Letters, Nov/Dec
   1995 (Volume XV Number 6), pp. 67-69.

   Ada vs. assembly language: There is a documented case where an Ada
   compiler and a novice Ada programmer did better than experienced
   assembly language programmers. See "Ada Whips Assembly" by Elam and
   Lawlis, Crosstalk, March 1992. Published by the Software Technology
   Support Center, Hill Air Force Base, Utah: Defense Printing Service.

     _________________________________________________________________

8: Advanced Programming Techniques with Ada


8.1: How can I redefine the assignment operation?

   The general answer is: use controlled types (RM95-7.6).

   For detailed explanations, read the following papers:
     * "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour,
       HTML at http://www.acm.org/~bbalfour/tips_no_1.html
     * "Abstract Data Types Are Under Full Control with Ada 9X" by Magnus
       Kempe, Postscript file at
       http://lglwww.epfl.ch/Ada/Resources/Papers/OO/ADT_Control-revised.ps


8.2: Does Ada have automatic constructors and destructors?

   Yes, controlled types have special, user-definable operations that
   control the construction and destruction of objects and values of
   those types (see question 8.1, above).

   (Also: Tucker Taft replies)
   At least in Ada 9X, functions with controlling results are inherited
   (even if overriding is required), allowing their use with dynamic
   binding and class-wide types. In most other OOPs, constructors can
   only be called if you know at compile time the "tag" (or equivalent)
   of the result you want. In Ada 9X, you can use the tag determined by
   the context to control dispatching to a function with a controlling
   result. For example:

     type Set is abstract tagged private;
     function  Empty return Set is abstract;
     function  Unit_Set(Element : Element_Type) return Set is abstract;
     procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
     function  Union(Left, Right : Set) return Set is abstract;
  ...

     procedure Convert(Source : Set'Class; Target : out Set'Class) is
       -- class-wide "convert" routine, can convert one representation
       --   of a set into another, so long as both set types are
       --   derived from "Set," either directly or indirectly.

       -- Algorithm:  Initialize Target to the empty set, and then
       --             copy all elements from Source set to Target set.

        Copy_Of_Source : Set'Class := Source;
        Element : Element_Type;
     begin
        Target := Empty;  -- Dispatching for Empty determined by Target'Tag.

        while Copy_Of_Source /= Empty loop
                       -- Dispatching for Empty based on Copy_Of_Source'Tag

            Remove_Element(Copy_Of_Source, Element);

            Target := Union(Target, Unit_Set(Element));
                       -- Dispatching for Unit_Set based on Target'Tag
        end loop;
     end Convert;


   The functions Unit_Set and Empty are essentially "constructors" and
   hence must be overridden in every extension of the abstract type Set.
   However, these operations can still be called with a class-wide
   expected type, and the controlling tag for the function calls will be
   determined at run-time by the context, analogous to the kind of
   (compile-time) overload resolution that uses context to disambiguate
   enumeration literals and aggregates.


8.3: Should I stick to a one package, one type approach while writing Ada
software?

   (Robb Nebbe responds)

   Offhand I can think of a couple of advantages arising from Ada's
   separation of the concepts of type and module.

   Separation of visibility and inheritance allows a programmer to
   isolate a derived type from the implementation details of its parent.
   To put it another way information hiding becomes a design decision
   instead of a decision that the programming language has already made
   for you.

   Another advantage that came "for free" is the distinction between
   subtyping and implementation inheritance. Since modules and types are
   independent concepts the interaction of the facilities for information
   hiding already present in Ada83 with inheritance provide an elegant
   solution to separating subtyping from implementation inheritance. (In
   my opinion more elegant than providing multiple forms of inheritance
   or two distinct language constructs.)


8.4: What is the "Beaujolais Effect"?

   The "Beaujolais Effect" is detrimental, and language designers should
   try to avoid it. But what is it?

   (from Tucker Taft)

   The term "Beaujolais Effect" comes from a prize (a bottle of
   Beaujolais) offered by Jean Ichbiah during the original Ada design
   process to anyone who could find a situation where adding or removing
   a single "use" clause could change a program from one legal
   interpretation to a different legal interpretation. (Or equivalently,
   adding or removing a single declaration from a "use"d package.)

   At least one bottle was awarded, and if the offer was still open, a
   few more might have been awarded during the Ada 9X process. However,
   thanks to some very nice analysis by the Ada 9X Language Precision
   Team (based at Odyssey Research Associates) we were able to identify
   the remaining cases of this effect in Ada 83, and remove them as part
   of the 9X process.

   The existing cases in Ada 83 had to do with implicit conversion of
   expressions of a universal type to a non-universal type. The rules in
   Ada 9X are subtly different, making any case that used to result in a
   Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.

   The Beaujolais effect is considered "harmful" because it is expected
   that during maintenance, declarations may be added or removed from
   packages without being able to do an exhaustive search for all places
   where the package is "use"d. If there were situations in the language
   which resulted in Beaujolais effects, then certain kinds of changes in
   "use"d packages might have mysterious effects in unexpected places.

   (from Jean D. Ichbiah)

   It is worth pointing that many popular languages have Beaujolais
   effect: e.g. the Borland Pascal "uses" clause, which takes an
   additive, layer-after-layer, interpretation of what you see in the
   used packages (units) definitely exhibits a Beaujolais effect.

   Last time I looked at C++, my impression was that several years of
   Beaujolais vintage productions would be required.

   For component-based software development, such effects are undesirable
   since your application may stop working when you recompile it with the
   new -- supposedly improved -- version of a component.


8.5: What about the "Ripple Effect"?

   (Tucker Taft explains)

   We have eliminated all remnants of the Beaujolais Effect, but we did
   debate various instances of the "Ripple" effect during the language
   revision process (apologies to Gallo Ripple Wine enthusiasts ;-).

   In brief, the (undesirable) Ripple effect was related to whether the
   legality of a compilation unit could be affected by adding or removing
   an otherwise unneeded "with" clause on some compilation unit on which
   the unit depended, directly or indirectly.

   This issue came up at least twice. One when we were considering rules
   relating to use of attributes like 'Address. In Ada 83 as interpreted
   by the ARG, if a compilation unit contains a use of 'Address, then
   there must be a "with" of package System somewhere in the set of
   library unit specs "with"ed by the compilation unit (directly or
   indirectly).

   In Ada 9X, we have eliminated this rule, as it was for some compilers
   an unnecessary implementation burden, and didn't really provide any
   value to the user (if anything, it created some confusion). The rule
   now is that the use of an attibute that returns a value of some
   particular type makes the compilation unit semantically dependent on
   the library unit in which the type is declared (whether or not it is
   "with"ed).

   The second place the Ripple effect came up was when we were trying to
   provide automatic direct visibility to (primitive) operators.
   Ultimately we ended up with an explicit "use type" clause for making
   operators directly visible. For a while we considered various rules
   that would make all primitive operators directly visible; some of the
   rules considered created the undesirable "Ripple" effects; others
   created annoying incompatibilities; all were quite tricky to implement
   correctly and efficiently.


8.6: How to write an Ada program to compute when one has had too much alcohol
to legally drive? 

   Someone asked if there is an Ada archive of this sort of program. Each
   drink has a number of units of alcohol, max legal level, etc.

   (from Bob Kitzberger :-)

   Oh, this is much to vague. Don't touch that whizzy development
   environment until you fully analyze the problem domain (unless that
   whizzy development environment includes Rose, in which case, you get
   to avoid paper and pencil from the git-go).

   Let's see, we have several classes to describe before we get to the
   implementation:

   Person
          subclass Drinker

          attributes: weight, age, timeline for amount consumed

   Drink
          attributes: percentage of alcohol, quantity of drink

   Country
          attributes: legal age to drink; max legal level of alcohol in
          blood


   Turn on the stereo, perhaps the Brandenburg Concertos. Then, flesh out
   the domain classes. Then, have a Belgian beer and consider what to do
   next. You decide on implementing these classes in a simple way,
   leading to your first successful prototype. Then, have another beer
   and decide what to do next. "Identify risk areas" you mutter to
   yourself, and off you go...

   If the beer wasn't too strong, you'd probably realize that the only
   thing of any difficulty in this is the amount consumed / rate of
   decay. Decide on investigating this aspect further. Create
   implementation classes for this and include a reference from the
   Drinker class to this new timeline/decay Class. Have another beer.
   Implement your second prototype. Congratulate yourself for making
   progress so quickly.

   Have another beer. Wander over to the stereo and change the CD to
   something more in the mood, maybe some Hendrix or Stevie Ray Vaughn.
   Back in front of the computer; pop another beer. Decide that it would
   be very cool if each drink was its own subclass of drink, and start
   cataloguing every drink out of your "Pocket Bartender's Guide". Have a
   slightly muddled epiphany that you really should create a class for
   each kind of alcohol (vodka, tequila, etc.) and the individual drink
   classes should each multiply inherit from all relevant Alcohol
   classes. Ooh, this is going to be a bit rough, so you have another
   beer. Draw a few of the hundreds of new class relationships needed,
   put that on the back burner when you think "persistence! that's what's
   missing!" Change the CD to Kraftwerk. Start your PPP connection, ask
   the people on comp.object for recommendations on a good OODBMS to use
   to keep track of all of those persistent objects. Make many many typos
   in your posting; everyone ignores it. Fall asleep on the keyboard.


8.7: Does Ada have macros?

   No, neither Ada 83 nor Ada 95 do. There was a Steelman requirement
   that the language developed NOT have a macro capability. This was a
   well thought-out requirement. What you see in a piece of Ada code is
   what you get (within a debugger for example). This does not hold true
   for macro languages.

   General text-substitution macros like those in the C preprocessor are
   thought to be too unsafe. For example, a macro can refer to a variable
   X and depending where the macro is expanded X may or may not be
   visible. Ada programs are supposed to be readable and in many cases C
   macros are the main culprits in producing unreadable C programs.

   Compile time macro facilities tend to be dreadfully over- and misused,
   resulting in horrible maintenance problems. Furthermore, there is a
   tendency to use macros to patch up glaring omissions in the language.
   For example, C has no named constants, a very bad omission, but
   #define is used to patch over this gap.

   In C, three "legitimate" uses of macros are for defining compile-time
   constants, types, and inline functions. Ada has all three of these
   facilities, without macros.

   If one wants macros to handle conditional compilation, the better way
   to achieve the equivalent is in most instances to isolate the system
   dependent parts and then put them in separate units with multiple
   system-specific implementations.




^ permalink raw reply	[relevance 2%]

* Ada FAQ: Programming with Ada (part 3 of 4)
@ 1996-04-23  0:00  2% Magnus Kempe
  0 siblings, 0 replies; 35+ results
From: Magnus Kempe @ 1996-04-23  0:00 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part3
Comp-lang-ada-archive-name: programming/part3
Posting-Frequency: monthly
Last-modified: 20 March 1996
Last-posted: 17 March 1996

                               Ada Programmer's
                       Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

    Recent changes to this FAQ are listed in the first section after the table
    of contents. This document is under explicit copyright.

This is part 3 of a 4-part posting; part 1 contains the table of contents.
Part 2 begins with question 5.
Part 4 begins with question 9.
Parts 1 and 2 should be the previous postings in this thread.
Part 4 should be the next posting in this thread.


6: Ada Numerics


6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?


   ftp.rational.com
          Freeware version of the ISO math packages on Rational's FTP
          server. It's a binding over the C Math library, in
          public/apex/freeware/math_lib.tar.Z

   archimedes.nosc.mil
          Stuff of high quality in pub/ada The random number generator
          and random deviates are recommended. These are mirrored at the
          next site, wuarchive.

   wuarchive.wustl.edu
          Site of PAL, the Public Ada Library: math routines scattered
          about in the directories under languages/ada in particular, in
          subdirectory swcomps

   source.asset.com
          This is not an anonymous ftp site for math software. What you
          should do is log on anonymously under ftp, and download the
          file asset.faq from the directory pub. This will tell you how
          to get an account.

   ftp.cs.kuleuven.ac.be
          Go to directory pub/Ada-Belgium/cdrom. There's a collection of
          math intensive software in directory swcomps. Mirrors some of
          PAL at wuarchive.wustl.edu.

   sw-eng.falls-church.va.us
          Go to directory public/AdaIC/source-code/bindings/ADAR-bindings
          to find extended-precision decimal arithmetic (up to 18
          digits). Includes facilities for COBOL-like formatted output.


6.2: How can I write portable code in Ada 83 using predefined types like Float
and Long_Float? Likewise, how can I write portable code that uses Math
functions like Sin and Log that are defined for Float and Long_Float?

   (from Jonathan Parker)

   Ada 83 was slow to arrive at a standard naming convention for
   elementary math functions and complex numbers. Furthermore, you'll
   find that some compilers call the 64-bit floating point type
   Long_Float; other compilers call it Float. Fortunately, it is easy to
   write programs in Ada that are independent of the naming conventions
   for floating point types and independent of the naming conventions of
   math functions defined on those types.

   One of the cleanest ways is to make the program generic:

     generic
       type Real is digits <>;
       with function Arcsin (X : Real) return Real is <>;
       with function    Log (X : Real) return Real is <>;
       --  This is the natural log, inverse of Exp(X), sometimes written Ln(X).
     package Example_1 is
       ...
     end Example_1;


   So the above package doesn't care what the name of the floating point
   type is, or what package the Math functions are defined in, just as
   long as the floating point type has the right attributes (precision
   and range) for the algorithm, and likewise the functions. Everything
   in the body of Example_1 is written in terms of the abstract names,
   Real, Arcsin, and Log, even though you instantiate it with compiler
   specific names that can look very different:

      package Special_Case is new Example_1 (Long_Float, Asin, Ln);


   The numerical algorithms implemented by generics like Example_1 can
   usually be made to work for a range of floating point precisions. A
   well written program will perform tests on Real to reject
   instantiations of Example_1 if the floating points type is judged
   inadequate. The tests may check the number of digits of precision in
   Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
   largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
   These tests are often placed after the begin statement of package
   body, as in:

     package body Example_1 is
       ...
     begin
       if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
         raise Program_Error;
       end if;
     end Example_1;


   Making an algorithm as abstract as possible, (independent of data
   types as much as possible) can do a lot to improve the quality of the
   code. Support for abstraction is one of the many things Ada-philes
   find so attractive about the language. The designers of Ada 95
   recognized the value of abstraction in the design of numeric
   algorithms and have generalized many of the features of the '83 model.
   For example, no matter what floating point type you instantiate
   Example_1 with, Ada 95 provides you with functions for examining the
   exponent and the mantissas of the numbers, for truncating, determining
   exact remainders, scaling exponents, and so on. (In the body of
   Example_1, and in its spec also of course, these functions are
   written, respectively: Real'Exponent(X), Real'Fraction(X),
   Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
   others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
   on the base type of Real (called Real'Base) which is liable to have
   greater precision and range than type Real.

   It is rare to see a performance loss when using generics like this.
   However, if there is an unacceptable performance hit, or if generics
   cannot be used for some other reason, then subtyping and renaming will
   do the job. Here is an example of renaming:

     with Someones_Math_Lib;
     procedure Example_2 is

       subtype Real is Long_Float;

       package  Math renames Someones_Math_Lib;
       function Arcsin(X : Real) return Real renames Math.Asin
       function   Log (X : Real) return Real renames Math.  Ln;

       --  Everything beyond this point is abstract with respect to
       --  the names of the floating point (Real), the functions (Arcsin
       --  and Log), and the package that exported them (Math).
       ...
     end Example_2;


   I prefer to make every package and subprogram (even test procedures)
   as compiler independent and machine portable as possible. To do this
   you move all of the renaming of compiler dependent functions and all
   of the "withing" of compiler dependent packages to a single package.
   In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
   the 8-byte floating point type to Real_8, and makes sure the math
   functions follow the Ada 95 standard, at least in name. In this
   approach Math_Lib_8 is the only compiler dependent component.

   There are other, perhaps better, ways also. See for example, "Ada In
   Action", by Do-While Jones for a generic solution.

   Here's the spec of Math_Lib_8, which is a perfect subset of package
   Math_Env_8, available by FTP in file
   ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada


--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries.  It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61.  These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics.   It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments.  The issues are the same with most math libraries.
--***************************************************************

--with Math_Lib;                                  -- Meridian DOS Ada.
  with Long_Float_Math_Lib;                       -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is

--subtype Real_8 is Float;                        -- Meridian 8-byte Real
  subtype Real_8 is Long_Float;                   -- Dec VMS  8-byte Real

 --package Math renames Math_Lib;                 -- Meridian DOS Ada
   package Math renames Long_Float_Math_Lib;      -- Dec VMS
 --package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);

   --  The above instantiation of the Ada.Numerics child package works on
   --  GNAT, or any other Ada 95 compiler.  Its here if you want to use
   --  an Ada 95 compiler to compile Ada 83 programs based on this package.

   function Cos (X : Real_8) return Real_8 renames Math.Cos;
   function Sin (X : Real_8) return Real_8 renames Math.Sin;
   function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
   function Exp (X : Real_8) return Real_8 renames Math.Exp;

 --function Log (X : Real_8) return Real_8 renames Math.Ln;        -- Meridian
   function Log (X : Real_8) return Real_8 renames Math.Log;       -- Dec VMS
 --function Log (X : Real_8) return Real_8 renames Math.Log;       -- Ada 95

 --function Arcsin (X : Real_8) return Real_8 renames Math.Asin;   -- Dec VMS
 --function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
   function Arcsin (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Arccos (X : Real_8) return Real_8 renames Math.Acos;   -- Dec VMS
 --function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
   function Arccos (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
   function Real_8_Floor (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

end Math_Lib_8;


6.3: Is Ada any good at numerics, and where can I learn more about it?

   First of all, a lot of people find the general Ada philosophy
   (modularity, strong-typing, readable syntax, rigorous definition and
   standardization, etc.) to be a real benefit in numerical programming,
   as well as in many other types of programming. But Ada --and
   especially Ada 95-- was also designed to meet the special requirements
   of number-crunching applications.

   The following sketches out some of these features. Hopefully a little
   of the flavor of the Ada philosophy will get through, but the best
   thing you can do at present is to read the two standard reference
   documents, the Ada 95 Rationale and Reference Manual. Below the GNU
   Ada 95 compiler is referred to several times. This compiler can be
   obtained by anonymous FTP from cs.nyu.edu, and at mirror sites
   declared in the README file of directory pub/gnat.

   1. Machine portable floating point declarations. (Ada 83 and Ada 95)
          If you declare "type Real is digits 14", then type Real will
          guarantee you (at least) 14 digits of precision independently
          of machine or compiler. In this case the base type of type Real
          will usually be the machine's 8-byte floating point type. If an
          appropriate base type is unavailable (very rare), then the
          declaration is rejected by the compiler.

   2. Extended precision for initialization of floating point. (Ada 83
          and Ada 95)
          Compilers are required to employ
          extended-precision/rational-arithmetic routines so that
          floating point variables and constants can be correctly
          initialized to their full precision.

   3. Generic packages and subprograms. (Ada 83 and Ada 95)
          Algorithms can be written so that they perform on abstract
          representations of the data structure. Support for this is
          provided by Ada's generic facilities (what C++ programmers
          would call templates).

   4. User-defined operators and overloaded subprograms. (Ada 83 and Ada
          95)
          The programmer can define his own operators (functions like
          "*", "+", "abs", "xor", "or", etc.) and define any number of
          subprograms with the same name (provided they have different
          argument profiles).

   5. Multitasking. (Ada 83 and Ada 95)
          Ada facilities for concurrent programming (multitasking) have
          traditionally found application in simulations and
          distributed/parallel programming. Ada tasking is an especially
          useful ingredient in the Ada 95 distributed programming model,
          and the combination of the two makes it possible to design
          parallel applications that have a high degree of operating
          system independence and portability. (More on this in item 6
          below.)

   6. Direct support for distributed/parallel computing in the language.
          (Ada 95)
          Ada 95 is probably the first internationally standardized
          language to combine in the same design complete facilities for
          multitasking and parallel programming. Communication between
          the distributed partitions is via synchronous and asynchronous
          remote procedure calls.

          Good discussion, along with code examples, is found in the
          Rationale, Part III E, and in the Ada 95 Reference Manual,
          Annex E. See also "Ada Letters", Vol. 13, No. 2 (1993), pp. 54
          and 78, and Vol. 14, No. 2 (1994), p. 80. (Full support for
          these features is provided by compilers that conform to the Ada
          95 distributed computing Annex. This conformance is optional,
          but for instance GNAT, the Gnu Ada 95 compiler, will meet these
          requirements.)

   7. Attributes of floating point types. (Ada 83 and Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that return the essential
          characteristics of the type. For example, if you declare "type
          Real is digits 15" then you can get the max exponent of objects
          of type Real from Real'Machine_Emax. Similarly, the size of the
          Mantissa, the Radix, the largest Real, and the Rounding policy
          of the arithmetic are given by Real'Machine_Mantissa,
          Real'Machine_Radix, Real'Last, and Real'Machine_Rounds. There
          are many others.

          (See Ada 95 Reference Manual, clause 3.5, subclause 3.5.8 and
          A.5.3, as well as Part III sections G.2 and G.4.1 of the Ada 95
          Rationale.)

   8. Attribute functions for floating point types. (Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that operate on objects of that
          type. For example, if you declare "type Real is digits 15" then
          Real'Remainder (X, Y) returns the exact remainder of X and Y: X
          - n*Y where n is the integer nearest X/Y. Real'Truncation(X),
          Real'Max(X,Y), Real'Rounding(X) have the usual meanings.
          Real'Fraction(X) and Real'Exponent(X) break X into mantissa and
          exponent; Real'Scaling(X, N) is exact scaling: multiplies X by
          Radix**N, which can be done by incrementing the exponent by N,
          etc. (See citations in item 7.)

   9. Modular arithmetic on integer types. (Ada 95)
          If you declare "type My_Unsigned is mod N", for arbitrary N,
          then arithmetic ("*", "+", etc.) on objects of type My_Unsigned
          returns the results modulo N. Boolean operators "and", "or",
          "xor", and "not" are defined on the objects as though they were
          arrays of bits (and likewise return results modulo N). For N a
          power of 2, the semantics are similar to those of C unsigned
          types.

   10. Generic elementary math functions for floating point types. (Ada
          95)
          Required of all compilers, and provided for any floating point
          type: Sqrt, Cos, Sin, Tan, Cot, Exp, Sinh, Cosh, Tanh, Coth,
          and the inverse functions of each of these, Arctan, Log,
          Arcsinh, etc. Also, X**Y for floating point X and Y. Compilers
          that conform to the Numerics Annex meet additional accuracy
          requirements.

          (See subclause A.5.1 of the Ada 95 RM, and Part III, Section
          A.3 of the Ada 95 Rationale.)

   11. Complex numbers. (Ada 95)
          Fortran-like, but with a new type called Imaginary. Type
          "Imaginary" allows programmers to write expressions in such a
          way that they are easier to optimize, more readable and appear
          in code as they appear on paper. Also, the ability to declare
          object of pure imaginary type reduces the number of cases in
          which premature type conversion of real numbers to complex
          causes floating point exceptions to occur. (Provided by
          compilers that conform to the Numerics Annex. The Gnu Ada 95
          compiler supports this annex, so the source code is freely
          available.)

   12. Generic elementary math functions for complex number types. (Ada
          95)
          Same functions supported for real types, but with complex
          arguments. Standard IO is provided for floating point types and
          Complex types. (Only required of compilers that support the
          Numerics Annex, like Gnu Ada.)

   13. Pseudo-random numbers for discrete and floating point types. (Ada
          95)
          A floating point pseudo-random number generator (PRNG) provides
          output in the range 0.0 .. 1.0. Discrete: A generic PRNG
          package is provided that can be instantiated with any discrete
          type: Boolean, Integer, Modular etc. The floating point PRNG
          package and instances of the (discrete) PRNG package are
          individually capable of producing independent streams of random
          numbers. Streams may be interrupted, stored, and resumed at
          later times (generally an important requirement in
          simulations). In Ada it is considered important that multiple
          tasks, engaged for example in simulations, have easy access to
          independent streams of pseudo random numbers. The Gnu Ada 95
          compiler provides the cryptographically secure X**2 mod N
          generator of Blum, Blum and Shub.

          (See subclause A.5.2 of the Ada 95 Reference Manual, and part
          III, section A.3.2 of the Ada Rationale.)

   14. Well-defined interfaces to Fortran and other languages. (Ada 83
          and Ada 95)
          It has always been a basic requirement of the language that it
          provide users a way to interface Ada programs with foreign
          languages, operating system services, GUI's, etc. Ada can be
          viewed as an interfacing language: its module system is
          composed of package specifications and separate package bodies.
          The package specifications can be used as strongly-type
          interfaces to libraries implemented in foreign languages, as
          well as to package bodies written in Ada. Ada 95 extends on
          these facilities with package interfaces to the basic data
          structures of C, Fortran, and COBOL and with new pragmas. For
          example, "pragma Convention(Fortran, M)" tells the compiler to
          store the elements of matrices of type M in the Fortran
          column-major order. (This pragma has already been implemented
          in the Gnu Ada 95 compiler. Multi- lingual programming is also
          a basic element of the Gnu compiler project.) As a result,
          assembly language BLAS and other high performance linear
          algebra and communications libraries will be accessible to Ada
          programs.

          (See Ada 95 Reference Manual: clause B.1 and B.5 of Annex B,
          and Ada 95 Rationale: Part III B.)


6.4: How do I get Real valued and Complex valued math functions in Ada 95?

   (from Jonathan Parker)

   Complex type and functions are provided by compilers that support the
   numerics Annex. The packages that use Float for the Real number and
   for the Complex number are:

     Ada.Numerics.Elementary_Functions;
     Ada.Numerics.Complex_Types;
     Ada.Numerics.Complex_Elementary_Functions;


   The packages that use Long_Float for the Real number and for the
   Complex number are:

     Ada.Numerics.Long_Elementary_Functions;
     Ada.Numerics.Long_Complex_Types;
     Ada.Numerics.Long_Complex_Elementary_Functions;


   The generic versions are demonstrated in the following example. Keep
   in mind that the non-generic packages may have been better tuned for
   speed or accuracy. In practice you won't always instantiate all three
   packages at the same time, but here is how you do it:

     with Ada.Numerics.Generic_Complex_Types;
     with Ada.Numerics.Generic_Elementary_Functions;
     with Ada.Numerics.Generic_Complex_Elementary_Functions;

     procedure Do_Something_Numerical is

       type Real_8 is digits 15;

       package Real_Functions_8 is
         new Ada.Numerics.Generic_Elementary_Functions (Real_8);

       package Complex_Nums_8 is
         new Ada.Numerics.Generic_Complex_Types (Real_8);

       package Complex_Functions_8 is
         new Ada.Numerics.Generic_Complex_Elementary_Functions
           (Complex_Nums_8);

       use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
       ...
       ... -- Do something
       ...
     end Do_Something_Numerical;


6.5: What libraries or public algorithms exist for Ada?

   An Ada version of Fast Fourier Transform is available. It's in
   journal "Computers & Mathematics with Applications," vol. 26, no. 2,
   pp. 61-65, 1993, with the title:

   "Analysis of an Ada Based Version of Glassman's General N Point Fast
   Fourier Transform"

   The package is now available in the AdaNET repository, object #: 6728,
   in collection: Transforms. If you're not an AdaNET user, contact Peggy
   Lacey (lacey@rbse.mountain.net).

     _________________________________________________________________


7: Efficiency of Ada Constructs


7.1: How much extra overhead do generics have?

   If you overgeneralize the generic, there will be more work to do for
   the compiler. How do you know when you have overgeneralized? For
   instance, passing arithmetic operations as parameters is a bad sign.
   So are boolean or enumeration type generic formal parameters. If you
   never override the defaults for a parameter, you probably
   overengineered.

   Code sharing (if implemented and requested) will cause an additional
   overhead on some calls, which will be partially offset by improved
   locality of reference. (Translation, code sharing may win most when
   cache misses cost most.) If a generic unit is only used once in a
   program, code sharing always loses.

   R.R. Software chose code sharing as the implementation for generics
   because 2 or more instantiations of Float_Io in a macro implementation
   would have made a program too large to run in the amount of memory
   available on the PC machines that existed in 1983 (usually a 128k or
   256k machine).

   Generics in Ada can also result in loss of information which could
   have helped the optimizer. Since the compiler is not restricted by Ada
   staticness rules within a single module, you can often avoid penalties
   by declaring (or redeclaring) bounds so that they are local:

     package Global is
       subtype Global_Int is
         Integer range X..Y;

       ...
     end Global;


     with Global;
     package Local is
       subtype Global_Int is
         Global.Global_Int;

       package Some_Instance is
         new Foo (Global_Int);

       ...
     end Local;


   Ada rules say that having the subtype redeclared locally does not
   affect staticness, but on a few occasions optimizers have been caught
   doing a much better job. Since optimizers are constantly changing,
   they may have been caught just at the wrong time.


7.2: How does Ada compare to other languages in efficiency of code?

   Ada vs. C: An analysis at Tartan found that Ada and C had fairly
   similar performance, with Ada having a slight edge. See "C vs. Ada:
   Arguing Performance Religion" by David Syiek, ACM Ada Letters, Nov/Dec
   1995 (Volume XV Number 6), pp. 67-69.

   Ada vs. assembly language: There is a documented case where an Ada
   compiler and a novice Ada programmer did better than experienced
   assembly language programmers. See "Ada Whips Assembly" by Elam and
   Lawlis, Crosstalk, March 1992. Published by the Software Technology
   Support Center, Hill Air Force Base, Utah: Defense Printing Service.

     _________________________________________________________________

8: Advanced Programming Techniques with Ada


8.1: How can I redefine the assignment operation?

   The general answer is: use controlled types (RM95-7.6).

   For detailed explanations, read the following papers:
     * "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour,
       HTML at http://www.acm.org/~bbalfour/tips_no_1.html
     * "Abstract Data Types Are Under Full Control with Ada 9X" by Magnus
       Kempe, Postscript file at
       http://lglwww.epfl.ch/Ada/Resources/Papers/OO/ADT_Control-revised.ps


8.2: Does Ada have automatic constructors and destructors?

   Yes, controlled types have special, user-definable operations that
   control the construction and destruction of objects and values of
   those types (see question 8.1, above).

   (Also: Tucker Taft replies)
   At least in Ada 9X, functions with controlling results are inherited
   (even if overriding is required), allowing their use with dynamic
   binding and class-wide types. In most other OOPs, constructors can
   only be called if you know at compile time the "tag" (or equivalent)
   of the result you want. In Ada 9X, you can use the tag determined by
   the context to control dispatching to a function with a controlling
   result. For example:

     type Set is abstract tagged private;
     function  Empty return Set is abstract;
     function  Unit_Set(Element : Element_Type) return Set is abstract;
     procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
     function  Union(Left, Right : Set) return Set is abstract;
  ...

     procedure Convert(Source : Set'Class; Target : out Set'Class) is
       -- class-wide "convert" routine, can convert one representation
       --   of a set into another, so long as both set types are
       --   derived from "Set," either directly or indirectly.

       -- Algorithm:  Initialize Target to the empty set, and then
       --             copy all elements from Source set to Target set.

        Copy_Of_Source : Set'Class := Source;
        Element : Element_Type;
     begin
        Target := Empty;  -- Dispatching for Empty determined by Target'Tag.

        while Copy_Of_Source /= Empty loop
                       -- Dispatching for Empty based on Copy_Of_Source'Tag

            Remove_Element(Copy_Of_Source, Element);

            Target := Union(Target, Unit_Set(Element));
                       -- Dispatching for Unit_Set based on Target'Tag
        end loop;
     end Convert;


   The functions Unit_Set and Empty are essentially "constructors" and
   hence must be overridden in every extension of the abstract type Set.
   However, these operations can still be called with a class-wide
   expected type, and the controlling tag for the function calls will be
   determined at run-time by the context, analogous to the kind of
   (compile-time) overload resolution that uses context to disambiguate
   enumeration literals and aggregates.


8.3: Should I stick to a one package, one type approach while writing Ada
software?

   (Robb Nebbe responds)

   Offhand I can think of a couple of advantages arising from Ada's
   separation of the concepts of type and module.

   Separation of visibility and inheritance allows a programmer to
   isolate a derived type from the implementation details of its parent.
   To put it another way information hiding becomes a design decision
   instead of a decision that the programming language has already made
   for you.

   Another advantage that came "for free" is the distinction between
   subtyping and implementation inheritance. Since modules and types are
   independent concepts the interaction of the facilities for information
   hiding already present in Ada83 with inheritance provide an elegant
   solution to separating subtyping from implementation inheritance. (In
   my opinion more elegant than providing multiple forms of inheritance
   or two distinct language constructs.)


8.4: What is the "Beaujolais Effect"?

   The "Beaujolais Effect" is detrimental, and language designers should
   try to avoid it. But what is it?

   (from Tucker Taft)

   The term "Beaujolais Effect" comes from a prize (a bottle of
   Beaujolais) offered by Jean Ichbiah during the original Ada design
   process to anyone who could find a situation where adding or removing
   a single "use" clause could change a program from one legal
   interpretation to a different legal interpretation. (Or equivalently,
   adding or removing a single declaration from a "use"d package.)

   At least one bottle was awarded, and if the offer was still open, a
   few more might have been awarded during the Ada 9X process. However,
   thanks to some very nice analysis by the Ada 9X Language Precision
   Team (based at Odyssey Research Associates) we were able to identify
   the remaining cases of this effect in Ada 83, and remove them as part
   of the 9X process.

   The existing cases in Ada 83 had to do with implicit conversion of
   expressions of a universal type to a non-universal type. The rules in
   Ada 9X are subtly different, making any case that used to result in a
   Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.

   The Beaujolais effect is considered "harmful" because it is expected
   that during maintenance, declarations may be added or removed from
   packages without being able to do an exhaustive search for all places
   where the package is "use"d. If there were situations in the language
   which resulted in Beaujolais effects, then certain kinds of changes in
   "use"d packages might have mysterious effects in unexpected places.

   (from Jean D. Ichbiah)

   It is worth pointing that many popular languages have Beaujolais
   effect: e.g. the Borland Pascal "uses" clause, which takes an
   additive, layer-after-layer, interpretation of what you see in the
   used packages (units) definitely exhibits a Beaujolais effect.

   Last time I looked at C++, my impression was that several years of
   Beaujolais vintage productions would be required.

   For component-based software development, such effects are undesirable
   since your application may stop working when you recompile it with the
   new -- supposedly improved -- version of a component.


8.5: What about the "Ripple Effect"?

   (Tucker Taft explains)

   We have eliminated all remnants of the Beaujolais Effect, but we did
   debate various instances of the "Ripple" effect during the language
   revision process (apologies to Gallo Ripple Wine enthusiasts ;-).

   In brief, the (undesirable) Ripple effect was related to whether the
   legality of a compilation unit could be affected by adding or removing
   an otherwise unneeded "with" clause on some compilation unit on which
   the unit depended, directly or indirectly.

   This issue came up at least twice. One when we were considering rules
   relating to use of attributes like 'Address. In Ada 83 as interpreted
   by the ARG, if a compilation unit contains a use of 'Address, then
   there must be a "with" of package System somewhere in the set of
   library unit specs "with"ed by the compilation unit (directly or
   indirectly).

   In Ada 9X, we have eliminated this rule, as it was for some compilers
   an unnecessary implementation burden, and didn't really provide any
   value to the user (if anything, it created some confusion). The rule
   now is that the use of an attibute that returns a value of some
   particular type makes the compilation unit semantically dependent on
   the library unit in which the type is declared (whether or not it is
   "with"ed).

   The second place the Ripple effect came up was when we were trying to
   provide automatic direct visibility to (primitive) operators.
   Ultimately we ended up with an explicit "use type" clause for making
   operators directly visible. For a while we considered various rules
   that would make all primitive operators directly visible; some of the
   rules considered created the undesirable "Ripple" effects; others
   created annoying incompatibilities; all were quite tricky to implement
   correctly and efficiently.


8.6: How to write an Ada program to compute when one has had too much alcohol
to legally drive? 

   Someone asked if there is an Ada archive of this sort of program. Each
   drink has a number of units of alcohol, max legal level, etc.

   (from Bob Kitzberger :-)

   Oh, this is much to vague. Don't touch that whizzy development
   environment until you fully analyze the problem domain (unless that
   whizzy development environment includes Rose, in which case, you get
   to avoid paper and pencil from the git-go).

   Let's see, we have several classes to describe before we get to the
   implementation:

   Person
          subclass Drinker

          attributes: weight, age, timeline for amount consumed

   Drink
          attributes: percentage of alcohol, quantity of drink

   Country
          attributes: legal age to drink; max legal level of alcohol in
          blood


   Turn on the stereo, perhaps the Brandenburg Concertos. Then, flesh out
   the domain classes. Then, have a Belgian beer and consider what to do
   next. You decide on implementing these classes in a simple way,
   leading to your first successful prototype. Then, have another beer
   and decide what to do next. "Identify risk areas" you mutter to
   yourself, and off you go...

   If the beer wasn't too strong, you'd probably realize that the only
   thing of any difficulty in this is the amount consumed / rate of
   decay. Decide on investigating this aspect further. Create
   implementation classes for this and include a reference from the
   Drinker class to this new timeline/decay Class. Have another beer.
   Implement your second prototype. Congratulate yourself for making
   progress so quickly.

   Have another beer. Wander over to the stereo and change the CD to
   something more in the mood, maybe some Hendrix or Stevie Ray Vaughn.
   Back in front of the computer; pop another beer. Decide that it would
   be very cool if each drink was its own subclass of drink, and start
   cataloguing every drink out of your "Pocket Bartender's Guide". Have a
   slightly muddled epiphany that you really should create a class for
   each kind of alcohol (vodka, tequila, etc.) and the individual drink
   classes should each multiply inherit from all relevant Alcohol
   classes. Ooh, this is going to be a bit rough, so you have another
   beer. Draw a few of the hundreds of new class relationships needed,
   put that on the back burner when you think "persistence! that's what's
   missing!" Change the CD to Kraftwerk. Start your PPP connection, ask
   the people on comp.object for recommendations on a good OODBMS to use
   to keep track of all of those persistent objects. Make many many typos
   in your posting; everyone ignores it. Fall asleep on the keyboard.


8.7: Does Ada have macros?

   No, neither Ada 83 nor Ada 95 do. There was a Steelman requirement
   that the language developed NOT have a macro capability. This was a
   well thought-out requirement. What you see in a piece of Ada code is
   what you get (within a debugger for example). This does not hold true
   for macro languages.

   General text-substitution macros like those in the C preprocessor are
   thought to be too unsafe. For example, a macro can refer to a variable
   X and depending where the macro is expanded X may or may not be
   visible. Ada programs are supposed to be readable and in many cases C
   macros are the main culprits in producing unreadable C programs.

   Compile time macro facilities tend to be dreadfully over- and misused,
   resulting in horrible maintenance problems. Furthermore, there is a
   tendency to use macros to patch up glaring omissions in the language.
   For example, C has no named constants, a very bad omission, but
   #define is used to patch over this gap.

   In C, three "legitimate" uses of macros are for defining compile-time
   constants, types, and inline functions. Ada has all three of these
   facilities, without macros.

   If one wants macros to handle conditional compilation, the better way
   to achieve the equivalent is in most instances to isolate the system
   dependent parts and then put them in separate units with multiple
   system-specific implementations.




^ permalink raw reply	[relevance 2%]

* Ada FAQ: Programming with Ada (part 3 of 4)
@ 1996-03-17  0:00  2% Magnus Kempe
  0 siblings, 0 replies; 35+ results
From: Magnus Kempe @ 1996-03-17  0:00 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part3
Comp-lang-ada-archive-name: programming/part3
Posting-Frequency: monthly
Last-modified: 28 February 1996
Last-posted: 26 January 1996

                               Ada Programmer's
                       Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

This is part 3 of a 4-part posting.
Part 2 begins with question 5.
Part 4 begins with question 9.
Parts 1 and 2 should be the previous postings in this thread.
Part 4 should be the next posting in this thread.

    Recent changes to this FAQ are listed in the first section after the table
    of contents (in part 1). This document is under explicit copyright.


6: Ada Numerics

6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?


   ftp.rational.com
          Freeware version of the ISO math packages on Rational's FTP
          server. It's a binding over the C Math library, in
          public/apex/freeware/math_lib.tar.Z

   archimedes.nosc.mil
          Stuff of high quality in pub/ada The random number generator
          and random deviates are recommended. These are mirrored at the
          next site, wuarchive.

   wuarchive.wustl.edu
          Site of PAL, the Public Ada Library: math routines scattered
          about in the directories under languages/ada in particular, in
          subdirectory swcomps

   source.asset.com
          This is not an anonymous ftp site for math software. What you
          should do is log on anonymously under ftp, and download the
          file asset.faq from the directory pub. This will tell you how
          to get an account.

   ftp.cs.kuleuven.ac.be
          Go to directory pub/Ada-Belgium/cdrom. There's a collection of
          math intensive software in directory swcomps. Mirrors some of
          PAL at wuarchive.wustl.edu.

   sw-eng.falls-church.va.us
          Go to directory public/AdaIC/source-code/bindings/ADAR-bindings
          to find extended-precision decimal arithmetic (up to 18
          digits). Includes facilities for COBOL-like formatted output.


6.2: How can I write portable code in Ada 83 using predefined types like Float
and Long_Float? Likewise, how can I write portable code that uses Math
functions like Sin and Log that are defined for Float and Long_Float?

   (from Jonathan Parker)

   Ada 83 was slow to arrive at a standard naming convention for
   elementary math functions and complex numbers. Furthermore, you'll
   find that some compilers call the 64-bit floating point type
   Long_Float; other compilers call it Float. Fortunately, it is easy to
   write programs in Ada that are independent of the naming conventions
   for floating point types and independent of the naming conventions of
   math functions defined on those types.

   One of the cleanest ways is to make the program generic:

     generic
       type Real is digits <>;
       with function Arcsin (X : Real) return Real is <>;
       with function    Log (X : Real) return Real is <>;
       --  This is the natural log, inverse of Exp(X), sometimes written Ln(X).
     package Example_1 is
       ...
     end Example_1;


   So the above package doesn't care what the name of the floating point
   type is, or what package the Math functions are defined in, just as
   long as the floating point type has the right attributes (precision
   and range) for the algorithm, and likewise the functions. Everything
   in the body of Example_1 is written in terms of the abstract names,
   Real, Arcsin, and Log, even though you instantiate it with compiler
   specific names that can look very different:

      package Special_Case is new Example_1 (Long_Float, Asin, Ln);


   The numerical algorithms implemented by generics like Example_1 can
   usually be made to work for a range of floating point precisions. A
   well written program will perform tests on Real to reject
   instantiations of Example_1 if the floating points type is judged
   inadequate. The tests may check the number of digits of precision in
   Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
   largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
   These tests are often placed after the begin statement of package
   body, as in:

     package body Example_1 is
       ...
     begin
       if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
         raise Program_Error;
       end if;
     end Example_1;


   Making an algorithm as abstract as possible, (independent of data
   types as much as possible) can do a lot to improve the quality of the
   code. Support for abstraction is one of the many things Ada-philes
   find so attractive about the language. The designers of Ada 95
   recognized the value of abstraction in the design of numeric
   algorithms and have generalized many of the features of the '83 model.
   For example, no matter what floating point type you instantiate
   Example_1 with, Ada 95 provides you with functions for examining the
   exponent and the mantissas of the numbers, for truncating, determining
   exact remainders, scaling exponents, and so on. (In the body of
   Example_1, and in its spec also of course, these functions are
   written, respectively: Real'Exponent(X), Real'Fraction(X),
   Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
   others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
   on the base type of Real (called Real'Base) which is liable to have
   greater precision and range than type Real.

   It is rare to see a performance loss when using generics like this.
   However, if there is an unacceptable performance hit, or if generics
   cannot be used for some other reason, then subtyping and renaming will
   do the job. Here is an example of renaming:

     with Someones_Math_Lib;
     procedure Example_2 is

       subtype Real is Long_Float;

       package  Math renames Someones_Math_Lib;
       function Arcsin(X : Real) return Real renames Math.Asin
       function   Log (X : Real) return Real renames Math.  Ln;

       --  Everything beyond this point is abstract with respect to
       --  the names of the floating point (Real), the functions (Arcsin
       --  and Log), and the package that exported them (Math).
       ...
     end Example_2;


   I prefer to make every package and subprogram (even test procedures)
   as compiler independent and machine portable as possible. To do this
   you move all of the renaming of compiler dependent functions and all
   of the "withing" of compiler dependent packages to a single package.
   In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
   the 8-byte floating point type to Real_8, and makes sure the math
   functions follow the Ada 95 standard, at least in name. In this
   approach Math_Lib_8 is the only compiler dependent component.

   There are other, perhaps better, ways also. See for example, "Ada In
   Action", by Do-While Jones for a generic solution.

   Here's the spec of Math_Lib_8, which is a perfect subset of package
   Math_Env_8, available by FTP in file
   ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada


--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries.  It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61.  These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics.   It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments.  The issues are the same with most math libraries.
--***************************************************************

--with Math_Lib;                                  -- Meridian DOS Ada.
  with Long_Float_Math_Lib;                       -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is

--subtype Real_8 is Float;                        -- Meridian 8-byte Real
  subtype Real_8 is Long_Float;                   -- Dec VMS  8-byte Real

 --package Math renames Math_Lib;                 -- Meridian DOS Ada
   package Math renames Long_Float_Math_Lib;      -- Dec VMS
 --package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);

   --  The above instantiation of the Ada.Numerics child package works on
   --  GNAT, or any other Ada 95 compiler.  Its here if you want to use
   --  an Ada 95 compiler to compile Ada 83 programs based on this package.

   function Cos (X : Real_8) return Real_8 renames Math.Cos;
   function Sin (X : Real_8) return Real_8 renames Math.Sin;
   function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
   function Exp (X : Real_8) return Real_8 renames Math.Exp;

 --function Log (X : Real_8) return Real_8 renames Math.Ln;        -- Meridian
   function Log (X : Real_8) return Real_8 renames Math.Log;       -- Dec VMS
 --function Log (X : Real_8) return Real_8 renames Math.Log;       -- Ada 95

 --function Arcsin (X : Real_8) return Real_8 renames Math.Asin;   -- Dec VMS
 --function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
   function Arcsin (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Arccos (X : Real_8) return Real_8 renames Math.Acos;   -- Dec VMS
 --function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
   function Arccos (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
   function Real_8_Floor (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

end Math_Lib_8;


6.3: Is Ada any good at numerics, and where can I learn more about it?

   First of all, a lot of people find the general Ada philosophy
   (modularity, strong-typing, readable syntax, rigorous definition and
   standardization, etc.) to be a real benefit in numerical programming,
   as well as in many other types of programming. But Ada --and
   especially Ada 95-- was also designed to meet the special requirements
   of number-crunching applications.

   The following sketches out some of these features. Hopefully a little
   of the flavor of the Ada philosophy will get through, but the best
   thing you can do at present is to read the two standard reference
   documents, the Ada 95 Rationale and Reference Manual. Below the GNU
   Ada 95 compiler is referred to several times. This compiler can be
   obtained by anonymous FTP from cs.nyu.edu, and at mirror sites
   declared in the README file of directory pub/gnat.

   1. Machine portable floating point declarations. (Ada 83 and Ada 95)
          If you declare "type Real is digits 14", then type Real will
          guarantee you (at least) 14 digits of precision independently
          of machine or compiler. In this case the base type of type Real
          will usually be the machine's 8-byte floating point type. If an
          appropriate base type is unavailable (very rare), then the
          declaration is rejected by the compiler.

   2. Extended precision for initialization of floating point. (Ada 83
          and Ada 95)
          Compilers are required to employ
          extended-precision/rational-arithmetic routines so that
          floating point variables and constants can be correctly
          initialized to their full precision.

   3. Generic packages and subprograms. (Ada 83 and Ada 95)
          Algorithms can be written so that they perform on abstract
          representations of the data structure. Support for this is
          provided by Ada's generic facilities (what C++ programmers
          would call templates).

   4. User-defined operators and overloaded subprograms. (Ada 83 and Ada
          95)
          The programmer can define his own operators (functions like
          "*", "+", "abs", "xor", "or", etc.) and define any number of
          subprograms with the same name (provided they have different
          argument profiles).

   5. Multitasking. (Ada 83 and Ada 95)
          Ada facilities for concurrent programming (multitasking) have
          traditionally found application in simulations and
          distributed/parallel programming. Ada tasking is an especially
          useful ingredient in the Ada 95 distributed programming model,
          and the combination of the two makes it possible to design
          parallel applications that have a high degree of operating
          system independence and portability. (More on this in item 6
          below.)

   6. Direct support for distributed/parallel computing in the language.
          (Ada 95)
          Ada 95 is probably the first internationally standardized
          language to combine in the same design complete facilities for
          multitasking and parallel programming. Communication between
          the distributed partitions is via synchronous and asynchronous
          remote procedure calls.

          Good discussion, along with code examples, is found in the
          Rationale, Part III E, and in the Ada 95 Reference Manual,
          Annex E. See also "Ada Letters", Vol. 13, No. 2 (1993), pp. 54
          and 78, and Vol. 14, No. 2 (1994), p. 80. (Full support for
          these features is provided by compilers that conform to the Ada
          95 distributed computing Annex. This conformance is optional,
          but for instance GNAT, the Gnu Ada 95 compiler, will meet these
          requirements.)

   7. Attributes of floating point types. (Ada 83 and Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that return the essential
          characteristics of the type. For example, if you declare "type
          Real is digits 15" then you can get the max exponent of objects
          of type Real from Real'Machine_Emax. Similarly, the size of the
          Mantissa, the Radix, the largest Real, and the Rounding policy
          of the arithmetic are given by Real'Machine_Mantissa,
          Real'Machine_Radix, Real'Last, and Real'Machine_Rounds. There
          are many others.

          (See Ada 95 Reference Manual, clause 3.5, subclause 3.5.8 and
          A.5.3, as well as Part III sections G.2 and G.4.1 of the Ada 95
          Rationale.)

   8. Attribute functions for floating point types. (Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that operate on objects of that
          type. For example, if you declare "type Real is digits 15" then
          Real'Remainder (X, Y) returns the exact remainder of X and Y: X
          - n*Y where n is the integer nearest X/Y. Real'Truncation(X),
          Real'Max(X,Y), Real'Rounding(X) have the usual meanings.
          Real'Fraction(X) and Real'Exponent(X) break X into mantissa and
          exponent; Real'Scaling(X, N) is exact scaling: multiplies X by
          Radix**N, which can be done by incrementing the exponent by N,
          etc. (See citations in item 7.)

   9. Modular arithmetic on integer types. (Ada 95)
          If you declare "type My_Unsigned is mod N", for arbitrary N,
          then arithmetic ("*", "+", etc.) on objects of type My_Unsigned
          returns the results modulo N. Boolean operators "and", "or",
          "xor", and "not" are defined on the objects as though they were
          arrays of bits (and likewise return results modulo N). For N a
          power of 2, the semantics are similar to those of C unsigned
          types.

   10. Generic elementary math functions for floating point types. (Ada
          95)
          Required of all compilers, and provided for any floating point
          type: Sqrt, Cos, Sin, Tan, Cot, Exp, Sinh, Cosh, Tanh, Coth,
          and the inverse functions of each of these, Arctan, Log,
          Arcsinh, etc. Also, X**Y for floating point X and Y. Compilers
          that conform to the Numerics Annex meet additional accuracy
          requirements.

          (See subclause A.5.1 of the Ada 95 RM, and Part III, Section
          A.3 of the Ada 95 Rationale.)

   11. Complex numbers. (Ada 95)
          Fortran-like, but with a new type called Imaginary. Type
          "Imaginary" allows programmers to write expressions in such a
          way that they are easier to optimize, more readable and appear
          in code as they appear on paper. Also, the ability to declare
          object of pure imaginary type reduces the number of cases in
          which premature type conversion of real numbers to complex
          causes floating point exceptions to occur. (Provided by
          compilers that conform to the Numerics Annex. The Gnu Ada 95
          compiler supports this annex, so the source code is freely
          available.)

   12. Generic elementary math functions for complex number types. (Ada
          95)
          Same functions supported for real types, but with complex
          arguments. Standard IO is provided for floating point types and
          Complex types. (Only required of compilers that support the
          Numerics Annex, like Gnu Ada.)

   13. Pseudo-random numbers for discrete and floating point types. (Ada
          95)
          A floating point pseudo-random number generator (PRNG) provides
          output in the range 0.0 .. 1.0. Discrete: A generic PRNG
          package is provided that can be instantiated with any discrete
          type: Boolean, Integer, Modular etc. The floating point PRNG
          package and instances of the (discrete) PRNG package are
          individually capable of producing independent streams of random
          numbers. Streams may be interrupted, stored, and resumed at
          later times (generally an important requirement in
          simulations). In Ada it is considered important that multiple
          tasks, engaged for example in simulations, have easy access to
          independent streams of pseudo random numbers. The Gnu Ada 95
          compiler provides the cryptographically secure X**2 mod N
          generator of Blum, Blum and Shub.

          (See subclause A.5.2 of the Ada 95 Reference Manual, and part
          III, section A.3.2 of the Ada Rationale.)

   14. Well-defined interfaces to Fortran and other languages. (Ada 83
          and Ada 95)
          It has always been a basic requirement of the language that it
          provide users a way to interface Ada programs with foreign
          languages, operating system services, GUI's, etc. Ada can be
          viewed as an interfacing language: its module system is
          composed of package specifications and separate package bodies.
          The package specifications can be used as strongly-type
          interfaces to libraries implemented in foreign languages, as
          well as to package bodies written in Ada. Ada 95 extends on
          these facilities with package interfaces to the basic data
          structures of C, Fortran, and COBOL and with new pragmas. For
          example, "pragma Convention(Fortran, M)" tells the compiler to
          store the elements of matrices of type M in the Fortran
          column-major order. (This pragma has already been implemented
          in the Gnu Ada 95 compiler. Multi- lingual programming is also
          a basic element of the Gnu compiler project.) As a result,
          assembly language BLAS and other high performance linear
          algebra and communications libraries will be accessible to Ada
          programs.

          (See Ada 95 Reference Manual: clause B.1 and B.5 of Annex B,
          and Ada 95 Rationale: Part III B.)


6.4: How do I get Real valued and Complex valued math functions in Ada 95?

   (from Jonathan Parker)

   Complex type and functions are provided by compilers that support the
   numerics Annex. The packages that use Float for the Real number and
   for the Complex number are:

     Ada.Numerics.Elementary_Functions;
     Ada.Numerics.Complex_Types;
     Ada.Numerics.Complex_Elementary_Functions;


   The packages that use Long_Float for the Real number and for the
   Complex number are:

     Ada.Numerics.Long_Elementary_Functions;
     Ada.Numerics.Long_Complex_Types;
     Ada.Numerics.Long_Complex_Elementary_Functions;


   The generic versions are demonstrated in the following example. Keep
   in mind that the non-generic packages may have been better tuned for
   speed or accuracy. In practice you won't always instantiate all three
   packages at the same time, but here is how you do it:

     with Ada.Numerics.Generic_Complex_Types;
     with Ada.Numerics.Generic_Elementary_Functions;
     with Ada.Numerics.Generic_Complex_Elementary_Functions;

     procedure Do_Something_Numerical is

       type Real_8 is digits 15;

       package Real_Functions_8 is
         new Ada.Numerics.Generic_Elementary_Functions (Real_8);

       package Complex_Nums_8 is
         new Ada.Numerics.Generic_Complex_Types (Real_8);

       package Complex_Functions_8 is
         new Ada.Numerics.Generic_Complex_Elementary_Functions
           (Complex_Nums_8);

       use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
       ...
       ... -- Do something
       ...
     end Do_Something_Numerical;


6.5: What libraries or public algorithms exist for Ada?

   An Ada version of Fast Fourier Transform is available. It's in
   journal "Computers & Mathematics with Applications," vol. 26, no. 2,
   pp. 61-65, 1993, with the title:

   "Analysis of an Ada Based Version of Glassman's General N Point Fast
   Fourier Transform"

   The package is now available in the AdaNET repository, object #: 6728,
   in collection: Transforms. If you're not an AdaNET user, contact Peggy
   Lacey (lacey@rbse.mountain.net).

     _________________________________________________________________


7: Efficiency of Ada Constructs


7.1: How much extra overhead do generics have?

   If you overgeneralize the generic, there will be more work to do for
   the compiler. How do you know when you have overgeneralized? For
   instance, passing arithmetic operations as parameters is a bad sign.
   So are boolean or enumeration type generic formal parameters. If you
   never override the defaults for a parameter, you probably
   overengineered.

   Code sharing (if implemented and requested) will cause an additional
   overhead on some calls, which will be partially offset by improved
   locality of reference. (Translation, code sharing may win most when
   cache misses cost most.) If a generic unit is only used once in a
   program, code sharing always loses.

   R.R. Software chose code sharing as the implementation for generics
   because 2 or more instantiations of Float_Io in a macro implementation
   would have made a program too large to run in the amount of memory
   available on the PC machines that existed in 1983 (usually a 128k or
   256k machine).

   Generics in Ada can also result in loss of information which could
   have helped the optimizer. Since the compiler is not restricted by Ada
   staticness rules within a single module, you can often avoid penalties
   by declaring (or redeclaring) bounds so that they are local:

     package Global is
       subtype Global_Int is
         Integer range X..Y;

       ...
     end Global;


     with Global;
     package Local is
       subtype Global_Int is
         Global.Global_Int;

       package Some_Instance is
         new Foo (Global_Int);

       ...
     end Local;


   Ada rules say that having the subtype redeclared locally does not
   affect staticness, but on a few occasions optimizers have been caught
   doing a much better job. Since optimizers are constantly changing,
   they may have been caught just at the wrong time.

     _________________________________________________________________

8: Advanced Programming Techniques with Ada


8.1: How can I redefine the assignment operation?

   The general answer is: use controlled types (RM95-7.6).

   For detailed explanations, read the following papers:
     * "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour,
       HTML at http://www.acm.org/~bbalfour/tips_no_1.html
     * "Abstract Data Types Are Under Full Control with Ada 9X" by Magnus
       Kempe, Postscript file at
       http://lglwww.epfl.ch/Ada/Resources/Papers/OO/ADT_Control-revised.ps


8.2: Does Ada have automatic constructors and destructors?

   Yes, controlled types have special, user-definable operations that
   control the construction and destruction of objects and values of
   those types (see question 8.1, above).

   (Also: Tucker Taft replies)
   At least in Ada 9X, functions with controlling results are inherited
   (even if overriding is required), allowing their use with dynamic
   binding and class-wide types. In most other OOPs, constructors can
   only be called if you know at compile time the "tag" (or equivalent)
   of the result you want. In Ada 9X, you can use the tag determined by
   the context to control dispatching to a function with a controlling
   result. For example:

     type Set is abstract tagged private;
     function  Empty return Set is abstract;
     function  Unit_Set(Element : Element_Type) return Set is abstract;
     procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
     function  Union(Left, Right : Set) return Set is abstract;
  ...

     procedure Convert(Source : Set'Class; Target : out Set'Class) is
       -- class-wide "convert" routine, can convert one representation
       --   of a set into another, so long as both set types are
       --   derived from "Set," either directly or indirectly.

       -- Algorithm:  Initialize Target to the empty set, and then
       --             copy all elements from Source set to Target set.

        Copy_Of_Source : Set'Class := Source;
        Element : Element_Type;
     begin
        Target := Empty;  -- Dispatching for Empty determined by Target'Tag.

        while Copy_Of_Source /= Empty loop
                       -- Dispatching for Empty based on Copy_Of_Source'Tag

            Remove_Element(Copy_Of_Source, Element);

            Target := Union(Target, Unit_Set(Element));
                       -- Dispatching for Unit_Set based on Target'Tag
        end loop;
     end Convert;


   The functions Unit_Set and Empty are essentially "constructors" and
   hence must be overridden in every extension of the abstract type Set.
   However, these operations can still be called with a class-wide
   expected type, and the controlling tag for the function calls will be
   determined at run-time by the context, analogous to the kind of
   (compile-time) overload resolution that uses context to disambiguate
   enumeration literals and aggregates.


8.3: Should I stick to a one package, one type approach while writing Ada
software?

   (Robb Nebbe responds)

   Offhand I can think of a couple of advantages arising from Ada's
   separation of the concepts of type and module.

   Separation of visibility and inheritance allows a programmer to
   isolate a derived type from the implementation details of its parent.
   To put it another way information hiding becomes a design decision
   instead of a decision that the programming language has already made
   for you.

   Another advantage that came "for free" is the distinction between
   subtyping and implementation inheritance. Since modules and types are
   independent concepts the interaction of the facilities for information
   hiding already present in Ada83 with inheritance provide an elegant
   solution to separating subtyping from implementation inheritance. (In
   my opinion more elegant than providing multiple forms of inheritance
   or two distinct language constructs.)


8.4: What is the "Beaujolais Effect"?

   The "Beaujolais Effect" is detrimental, and language designers should
   try to avoid it. But what is it?

   (from Tucker Taft)

   The term "Beaujolais Effect" comes from a prize (a bottle of
   Beaujolais) offered by Jean Ichbiah during the original Ada design
   process to anyone who could find a situation where adding or removing
   a single "use" clause could change a program from one legal
   interpretation to a different legal interpretation. (Or equivalently,
   adding or removing a single declaration from a "use"d package.)

   At least one bottle was awarded, and if the offer was still open, a
   few more might have been awarded during the Ada 9X process. However,
   thanks to some very nice analysis by the Ada 9X Language Precision
   Team (based at Odyssey Research Associates) we were able to identify
   the remaining cases of this effect in Ada 83, and remove them as part
   of the 9X process.

   The existing cases in Ada 83 had to do with implicit conversion of
   expressions of a universal type to a non-universal type. The rules in
   Ada 9X are subtly different, making any case that used to result in a
   Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.

   The Beaujolais effect is considered "harmful" because it is expected
   that during maintenance, declarations may be added or removed from
   packages without being able to do an exhaustive search for all places
   where the package is "use"d. If there were situations in the language
   which resulted in Beaujolais effects, then certain kinds of changes in
   "use"d packages might have mysterious effects in unexpected places.

   (from Jean D. Ichbiah)

   It is worth pointing that many popular languages have Beaujolais
   effect: e.g. the Borland Pascal "uses" clause, which takes an
   additive, layer-after-layer, interpretation of what you see in the
   used packages (units) definitely exhibits a Beaujolais effect.

   Last time I looked at C++, my impression was that several years of
   Beaujolais vintage productions would be required.

   For component-based software development, such effects are undesirable
   since your application may stop working when you recompile it with the
   new -- supposedly improved -- version of a component.


8.5: What about the "Ripple Effect"?

   (Tucker Taft explains)

   We have eliminated all remnants of the Beaujolais Effect, but we did
   debate various instances of the "Ripple" effect during the language
   revision process (apologies to Gallo Ripple Wine enthusiasts ;-).

   In brief, the (undesirable) Ripple effect was related to whether the
   legality of a compilation unit could be affected by adding or removing
   an otherwise unneeded "with" clause on some compilation unit on which
   the unit depended, directly or indirectly.

   This issue came up at least twice. One when we were considering rules
   relating to use of attributes like 'Address. In Ada 83 as interpreted
   by the ARG, if a compilation unit contains a use of 'Address, then
   there must be a "with" of package System somewhere in the set of
   library unit specs "with"ed by the compilation unit (directly or
   indirectly).

   In Ada 9X, we have eliminated this rule, as it was for some compilers
   an unnecessary implementation burden, and didn't really provide any
   value to the user (if anything, it created some confusion). The rule
   now is that the use of an attibute that returns a value of some
   particular type makes the compilation unit semantically dependent on
   the library unit in which the type is declared (whether or not it is
   "with"ed).

   The second place the Ripple effect came up was when we were trying to
   provide automatic direct visibility to (primitive) operators.
   Ultimately we ended up with an explicit "use type" clause for making
   operators directly visible. For a while we considered various rules
   that would make all primitive operators directly visible; some of the
   rules considered created the undesirable "Ripple" effects; others
   created annoying incompatibilities; all were quite tricky to implement
   correctly and efficiently.


8.6: How to write an Ada program to compute when one has had too much alcohol
to legally drive? 

   Someone asked if there is an Ada archive of this sort of program. Each
   drink has a number of units of alcohol, max legal level, etc.

   (from Bob Kitzberger :-)

   Oh, this is much to vague. Don't touch that whizzy development
   environment until you fully analyze the problem domain (unless that
   whizzy development environment includes Rose, in which case, you get
   to avoid paper and pencil from the git-go).

   Let's see, we have several classes to describe before we get to the
   implementation:

   Person
          subclass Drinker

          attributes: weight, age, timeline for amount consumed

   Drink
          attributes: percentage of alcohol, quantity of drink

   Country
          attributes: legal age to drink; max legal level of alcohol in
          blood


   Turn on the stereo, perhaps the Brandenburg Concertos. Then, flesh out
   the domain classes. Then, have a Belgian beer and consider what to do
   next. You decide on implementing these classes in a simple way,
   leading to your first successful prototype. Then, have another beer
   and decide what to do next. "Identify risk areas" you mutter to
   yourself, and off you go...

   If the beer wasn't too strong, you'd probably realize that the only
   thing of any difficulty in this is the amount consumed / rate of
   decay. Decide on investigating this aspect further. Create
   implementation classes for this and include a reference from the
   Drinker class to this new timeline/decay Class. Have another beer.
   Implement your second prototype. Congratulate yourself for making
   progress so quickly.

   Have another beer. Wander over to the stereo and change the CD to
   something more in the mood, maybe some Hendrix or Stevie Ray Vaughn.
   Back in front of the computer; pop another beer. Decide that it would
   be very cool if each drink was its own subclass of drink, and start
   cataloguing every drink out of your "Pocket Bartender's Guide". Have a
   slightly muddled epiphany that you really should create a class for
   each kind of alcohol (vodka, tequila, etc.) and the individual drink
   classes should each multiply inherit from all relevant Alcohol
   classes. Ooh, this is going to be a bit rough, so you have another
   beer. Draw a few of the hundreds of new class relationships needed,
   put that on the back burner when you think "persistence! that's what's
   missing!" Change the CD to Kraftwerk. Start your PPP connection, ask
   the people on comp.object for recommendations on a good OODBMS to use
   to keep track of all of those persistent objects. Make many many typos
   in your posting; everyone ignores it. Fall asleep on the keyboard.


8.7: Does Ada have macros?

   No, neither Ada 83 nor Ada 95 do. There was a Steelman requirement
   that the language developed NOT have a macro capability. This was a
   well thought-out requirement. What you see in a piece of Ada code is
   what you get (within a debugger for example). This does not hold true
   for macro languages.

   General text-substitution macros like those in the C preprocessor are
   thought to be too unsafe. For example, a macro can refer to a variable
   X and depending where the macro is expanded X may or may not be
   visible. Ada programs are supposed to be readable and in many cases C
   macros are the main culprits in producing unreadable C programs.

   Compile time macro facilities tend to be dreadfully over- and misused,
   resulting in horrible maintenance problems. Furthermore, there is a
   tendency to use macros to patch up glaring omissions in the language.
   For example, C has no named constants, a very bad omission, but
   #define is used to patch over this gap.

   In C, three "legitimate" uses of macros are for defining compile-time
   constants, types, and inline functions. Ada has all three of these
   facilities, without macros.

   If one wants macros to handle conditional compilation, the better way
   to achieve the equivalent is in most instances to isolate the system
   dependent parts and then put them in separate units with multiple
   system-specific implementations.




^ permalink raw reply	[relevance 2%]

* Ada FAQ: Programming with Ada (part 2 of 3)
@ 1995-04-20  0:00  2% Magnus Kempe
  0 siblings, 0 replies; 35+ results
From: Magnus Kempe @ 1995-04-20  0:00 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part2
Comp-lang-ada-archive-name: programming/part2
Posting-Frequency: monthly
Last-modified: 20 April 1995
Last-posted: 21 March 1995

                               Ada Programmer's
                      Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

    Recent changes to this FAQ are listed in the first section after the table
    of contents. This document is under explicit copyright.

This is part 2 of a 3-part posting.
Part 3 begins with question 8.5; it should be the next posting in this thread.
Part 1 should be the previous posting in this thread.


5.6: What do "covariance" and "contravariance" mean, and does Ada support
either or both?

   (From Robert Martin) [This is C++ stuff, it must be completely
   re-written for Ada. --MK]


 R> covariance:  "changes with"
 R> contravariance: "changes against"

 R> class A
 R> {
 R>    public:
 R>      A* f(A*);   // method of class A, takes A argument and returns A
 R>      A* g(A*);   // same.
 R> };

 R> class B : public A // class B is a subclass of class A
 R> {
 R>   public:
 R>     B* f(B*);  // method of class B overrides f and is covariant.
 R>     A* g(A*);  // method of class B overrides g and is contravariant.
 R> };

 R> The function f is covariant because the type of its return value and
 R> argument changes with the class it belongs to.  The function g is
 R> contravariant because the types of its return value and arguments does not
 R> change with the class it belongs to.


   Actually, I would call g() invariant. If you look in Sather, (one of
   the principle languages with contravariance), you will see that the
   method in the decendent class actually can have aruments that are
   superclasses of the arguments of its parent. So for example:

class A : public ROOT
{
   public:
     A* f(A*);   // method of class A, takes A argument and returns A
     A* g(A*);   // same.
};

class B : public A // class B is a subclass of class A
{
  public:
    B* f(B*);  // method of class B overrides f and is covariant.
    ROOT* g(ROOT*);  // method of class B overrides g and is contravariant.
};


   To my knowledge the uses for contravariance are rare or nonexistent.
   (Anyone?). It just makes the rules easy for the compiler to type
   check. On the other hand, co-variance is extremely useful. Suppose you
   want to test for equality, or create a new object of the same type as
   the one in hand:

class A
{
   public:
      BOOLEAN equal(A*);
      A* create();
}

class B: public A
{
   public:
      BOOLEAN equal(B*);
      B* create();
}


   Here covariance is exactly what you want. Eiffel gives this to you,
   but the cost is giving up 100% compile time type safety. This seem
   necessary in cases like these.

   In fact, Eiffel gives you automatic ways to make a method covariant,
   called "anchored types". So you could declare, (in C++/eiffese):

class A
{
   public:
      BOOLEAN equal(like Current *);
      like Current * create();
}


   Which says equal takes an argument the same type as the current
   object, and create returns an object of the same type as current. Now,
   there is not even any need to redeclare these in class B. Those
   transformations happen for free!


5.7: What is meant by upcasting/expanding and downcasting/narrowing?

   (Tucker Taft replies):

   Here is the symmetric case to illustrate upcasting and downcasting.

     type A is tagged ...;   -- one parent type

     type B is tagged ...;   -- another parent type

     ...

     type C;   -- the new type, to be a mixture of A and B

     type AC (Obj : access C'Class) is
       new A
       with ...;
       -- an extension of A to be mixed into C

     type BC (Obj : access C'Class) is
       new B
       with ...;
       -- an extension of B to be mixed into C

     type C is
       tagged limited record
         A : AC (C'Access);
         B : BC (C'Access);
         ... -- other stuff if desired
       end record;


   We can now pass an object of type C to anything that takes an A or B
   as follows (this presumes that Foobar and QBert are primitives of A
   and B, respectively, so they are inherited; if not, then an explicit
   conversion (upcast) to A and B could be used to call the original
   Foobar and QBert).

     XC : C;
   ...
     Foobar (XC.A);
     QBert (XC.B);


   If we want to override what Foobar does, then we override Foobar on
   AC. If we want to override what QBert does, then we override QBert on
   BC.

   Note that there are no naming conflicts, since AC and BC are distinct
   types, so even if A and B have same-named components or operations, we
   can talk about them and/or override them individually using AC and BC.


   Upcasting (from C to A or C to B) is trivial -- A(XC.A) upcasts to A;
   B(XC.B) upcasts to B.

   Downcasting (narrowing) is also straightforward and safe. Presuming XA
   of type A'Class, and XB of type B'Class:

     AC(XA).Obj.all downcasts to C'Class (and verifies XA in AC'Class)
     BC(XB).Obj.all downcasts to C'Class (and verifies XB in BC'Class)


   You can check before the downcast to avoid a Constraint_Error:

     if XA not in AC'Class then -- appropriate complaint

     if XB not in BC'Class then -- ditto


   The approach is slightly simpler (though less symmetric) if we choose
   to make A the "primary" parent and B a "secondary" parent:

     type A is ...
     type B is ...

     type C;

     type BC (Obj : access C'Class) is
       new B
       with ...

     type C is
       new A
       with record
         B : BC (C'Access);
         ... -- other stuff if desired
       end record;


   Now C is a "normal" extension of A, and upcasting from C to A and
   (checked) downcasting from C'Class to A (or A'Class) is done with
   simple type conversions. The relationship between C and B is as above
   in the symmetric approach.

   Not surprisingly, using building blocks is more work than using a
   "builtin" approach for simple cases that happen to match the builtin
   approach, but having building blocks does ultimately mean more
   flexibility for the programmer -- there are many other structures that
   are possible in addition to the two illustrated above, using the
   access discriminant building block.

   For example, for mixins, each mixin "flavor" would have an access
   discriminant already:

     type Window is ...  -- The basic "vanilla" window

     -- Various mixins
     type Win_Mixin_1 (W : access Window'Class) is ...

     type Win_Mixin_2 (W : access Window'Class) is ...

     type Win_Mixin_3 (W : access Window'Class) is ...


   Given the above vanilla window, plus any number of window mixins, one
   can construct a desired window by including as many mixins as wanted:

     type My_Window is
       new Window
       with record
         M1 : Win_Mixin_1 (My_Window'access);
         M3 : Win_Mixin_3 (My_Window'access);
         M11 : Win_Mixin_1(My_Window'access);
         ... -- plus additional stuff, as desired.
       end record;


   As illustrated above, you can incorporate the same "mixin" multiple
   times, with no naming conflicts. Every mixin can get access to the
   enclosing object. Operations of individual mixins can be overridden by
   creating an extension of the mixin first, overriding the operation in
   that, and then incorporating that tweaked mixin into the ultimate
   window.

   I hope the above helps better illustrate the use and flexibility of
   the Ada 9X type composition building blocks.


5.8: How does Ada do "narrowing"?

   Dave Griffith said

     . . . Nonetheless, The Ada9x committee chose a structure-based
     subtyping, with all of the problems that that is known to cause. As
     the problems of structure based subtyping usually manifest only in
     large projects maintained by large groups, this is _precisely_ the
     subtype paradigm that Ada9x should have avoided. Ada9x's model is,
     as Tucker Taft pointed out, quite easy to use for simple OO
     programming. There is, however, no good reason to _do_ simple OO
     programming. OO programmings gains click in somewhere around 10,000
     LOC, with greatest gains at over 100,000. At these sizes, "just
     declare it tagged" will result in unmaintainable messes. OO
     programming in the large rapidly gets difficult with structure based
     subtyping. Allowing by-value semantics for objects compounds these
     problems. All of this is known. All of this was, seemingly, ignored
     by Ada9x.


   (Tucker Taft answers)

   As explained in a previous note, Ada 9X supports the ability to hide
   the implementation heritage of a type, and only expose the desired
   interface heritage. So we are not stuck with strictly "structure-based
   subtyping." Secondly, by-reference semantics have many "well known"
   problems as well, and the designers of Modula-3 chose to, seemingly,
   ignore those ;-) ;-). Of course, in reality, neither set of language
   designers ignored either of these issues. Language design involves
   tradeoffs. You can complain we made the wrong tradeoff, but to
   continue to harp on the claim that we "ignored" things is silly. We
   studied every OOP language under the sun on which we could find any
   written or electronic material. We chose value-based semantics for
   what we believe are good reasons, based on reasonable tradeoffs.

   First of all, in the absence of an integrated garbage collector,
   by-reference semantics doesn't make much sense. Based on various
   tradeoffs, we decided against requiring an integrated garbage
   collector for Ada 9X.

   Secondly, many of the "known" problems with by-value semantics we
   avoided, by eliminating essentially all cases of "implicit
   truncation." One of the problems with the C++ version of "value
   semantics" is that on assignment and parameter passing, implicit
   truncation can take place mysteriously, meaning that a value that
   started its life representing one kind of thing gets truncated
   unintentionally so that it looks like a value of some ancestor type.
   This is largely because the name of a C++ class means differnt things
   depending on the context. When you declare an object, the name of the
   class determines the "exact class" of the object. The same thing
   applies to a by-value parameter. However, for references and pointers,
   the name of a class stands for that class and all of its derivatives.
   But since, in C++, a value of a subclass is always acceptable where a
   value of a given class is expected, you can get implicit truncation as
   part of assignment and by-value parameter passing. In Ada 9X, we avoid
   the implicit truncation because we support assignment for "class-wide"
   types, which never implicitly truncates, and one must do an explicit
   conversion to do an assignment that truncates. Parameter passing never
   implicitly truncates, even if an implicit conversion is performed as
   part of calling an inherited subprogram.

   In any case, why not either ignore Ada 9X or give it a fair shot? It
   is easy to criticize any particular design decision, but it is much
   harder to actually put together a complete integrated language design
   that meets the requirements of its user community, doesn't bankrupt
   the vendor community, and provides interesting fodder for the academic
   community ;-).

     _________________________________________________________________


6: Ada Numerics


6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?


   ftp.rational.com
          Freeware version of the ISO math packages on Rational's FTP
          server. It's a binding over the C Math library, in
          public/apex/freeware/math_lib.tar.Z

   archimedes.nosc.mil
          Stuff of high quality in pub/ada The random number generator
          and random deviates are recommended. These are mirrored at the
          next site, wuarchive.

   wuarchive.wustl.edu
          Site of PAL, the Public Ada Library: math routines scattered
          about in the directories under languages/ada in particular, in
          subdirectory swcomps

   source.asset.com
          This is not an anonymous ftp site for math software. What you
          should do is log on anonymously under ftp, and download the
          file asset.faq from the directory pub. This will tell you how
          to get an account.

   ftp.cs.kuleuven.ac.be
          Go to directory pub/Ada-Belgium/cdrom. There's a collection of
          math intensive software in directory swcomps. Mirrors some of
          PAL at wuarchive.wustl.edu.

   sw-eng.falls-church.va.us
          Go to directory public/adaic/tools/atip/adar to find
          extended-precision decimal arithmetic (up to 18 digits).
          Includes facilities for COBOL-like formatted output.


6.2: How can I write portable code in Ada 83 using predefined types like Float
and Long_Float? Likewise, how can I write portable code that uses Math
functions like Sin and Log that are defined for Float and Long_Float?

   (from Jonathan Parker)

   Ada 83 was slow to arrive at a standard naming convention for
   elementary math functions and complex numbers. Furthermore, you'll
   find that some compilers call the 64-bit floating point type
   Long_Float; other compilers call it Float. Fortunately, it is easy to
   write programs in Ada that are independent of the naming conventions
   for floating point types and independent of the naming conventions of
   math functions defined on those types.

   One of the cleanest ways is to make the program generic:

     generic
       type Real is digits <>;
       with function Arcsin (X : Real) return Real is <>;
       with function    Log (X : Real) return Real is <>;
       --  This is the natural log, inverse of Exp(X), sometimes written Ln(X).
     package Example_1 is
       ...
     end Example_1;


   So the above package doesn't care what the name of the floating point
   type is, or what package the Math functions are defined in, just as
   long as the floating point type has the right attributes (precision
   and range) for the algorithm, and likewise the functions. Everything
   in the body of Example_1 is written in terms of the abstract names,
   Real, Arcsin, and Log, even though you instantiate it with compiler
   specific names that can look very different:

      package Special_Case is new Example_1 (Long_Float, Asin, Ln);


   The numerical algorithms implemented by generics like Example_1 can
   usually be made to work for a range of floating point precisions. A
   well written program will perform tests on Real to reject
   instantiations of Example_1 if the floating points type is judged
   inadequate. The tests may check the number of digits of precision in
   Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
   largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
   These tests are often placed after the begin statement of package
   body, as in:

     package body Example_1 is
       ...
     begin
       if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
         raise Program_Error;
       end if;
     end Example_1;


   Making an algorithm as abstract as possible, (independent of data
   types as much as possible) can do a lot to improve the quality of the
   code. Support for abstraction is one of the many things Ada-philes
   find so attractive about the language. The designers of Ada 95
   recognized the value of abstraction in the design of numeric
   algorithms and have generalized many of the features of the '83 model.
   For example, no matter what floating point type you instantiate
   Example_1 with, Ada 95 provides you with functions for examining the
   exponent and the mantissas of the numbers, for truncating, determining
   exact remainders, scaling exponents, and so on. (In the body of
   Example_1, and in its spec also of course, these functions are
   written, respectively: Real'Exponent(X), Real'Fraction(X),
   Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
   others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
   on the base type of Real (called Real'Base) which is liable to have
   greater precision and range than type Real.

   It is rare to see a performance loss when using generics like this.
   However, if there is an unacceptable performance hit, or if generics
   cannot be used for some other reason, then subtyping and renaming will
   do the job. Here is an example of renaming:

     with Someones_Math_Lib;
     procedure Example_2 is

       subtype Real is Long_Float;

       package  Math renames Someones_Math_Lib;
       function Arcsin(X : Real) return Real renames Math.Asin
       function   Log (X : Real) return Real renames Math.  Ln;

       --  Everything beyond this point is abstract with respect to
       --  the names of the floating point (Real), the functions (Arcsin
       --  and Log), and the package that exported them (Math).
       ...
     end Example_2;


   I prefer to make every package and subprogram (even test procedures)
   as compiler independent and machine portable as possible. To do this
   you move all of the renaming of compiler dependent functions and all
   of the "withing" of compiler dependent packages to a single package.
   In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
   the 8-byte floating point type to Real_8, and makes sure the math
   functions follow the Ada 95 standard, at least in name. In this
   approach Math_Lib_8 is the only compiler dependent component.

   There are other, perhaps better, ways also. See for example, "Ada In
   Action", by Do-While Jones for a generic solution.

   Here's the spec of Math_Lib_8, which is a perfect subset of package
   Math_Env_8, available by FTP in file
   ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada


--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries.  It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61.  These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics.   It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments.  The issues are the same with most math libraries.
--***************************************************************

--with Math_Lib;                                  -- Meridian DOS Ada.
  with Long_Float_Math_Lib;                       -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is

--subtype Real_8 is Float;                        -- Meridian 8-byte Real
  subtype Real_8 is Long_Float;                   -- Dec VMS  8-byte Real

 --package Math renames Math_Lib;                 -- Meridian DOS Ada
   package Math renames Long_Float_Math_Lib;      -- Dec VMS
 --package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);

   --  The above instantiation of the Ada.Numerics child package works on
   --  GNAT, or any other Ada 95 compiler.  Its here if you want to use
   --  an Ada 95 compiler to compile Ada 83 programs based on this package.

   function Cos (X : Real_8) return Real_8 renames Math.Cos;
   function Sin (X : Real_8) return Real_8 renames Math.Sin;
   function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
   function Exp (X : Real_8) return Real_8 renames Math.Exp;

 --function Log (X : Real_8) return Real_8 renames Math.Ln;        -- Meridian
   function Log (X : Real_8) return Real_8 renames Math.Log;       -- Dec VMS
 --function Log (X : Real_8) return Real_8 renames Math.Log;       -- Ada 95

 --function Arcsin (X : Real_8) return Real_8 renames Math.Asin;   -- Dec VMS
 --function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
   function Arcsin (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Arccos (X : Real_8) return Real_8 renames Math.Acos;   -- Dec VMS
 --function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
   function Arccos (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
   function Real_8_Floor (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

end Math_Lib_8;


6.3: Is Ada any good at numerics, and where can I learn more about it?

   First of all, a lot of people find the general Ada philosophy
   (modularity, strong-typing, readable syntax, rigorous definition and
   standardization, etc.) to be a real benefit in numerical programming,
   as well as in many other types of programming. Further, Ada--and
   especially Ada 95--was designed to also meet the special requirements
   of number-crunching applications.

   The following sketches out some of these features. Hopefully a little
   of the flavor of the Ada philosophy will get through, but the best
   thing you can do at present is to read the two standard reference
   documents, the Ada 95 Rationale and Reference Manual.

   1. Machine portable floating point declarations. (Ada 83 and Ada 95)
          If you declare "type Real is digits 14", then type Real will
          guarantee you (at least) 14 digits of precision independently
          of machine or compiler. In this case the base type of type Real
          will usually be the machine's 8-byte floating point type. If an
          appropriate base type is unavailable (very rare), then the
          declaration is rejected by the compiler.

   2. Extended precision for initialization of floating point. (Ada 83
          and Ada 95)
          Compilers are required to employ
          extended-precision/rational-arithmetic routines so that
          floating point variables and constants can be correctly
          initialized to their full precision.

   3. Generic packages and subprograms. (Ada 83 and Ada 95)
          Algorithms can be written so that they perform on abstract
          representations of the data structure. Support for this is
          provided by Ada's generic facilities (what C++ programmers
          would call templates).

   4. User-defined operators and overloaded subprograms. (Ada 83 and Ada
          95)
          The programmer can define his own operators (functions like
          "*", "+", "abs", "xor", "or", etc.) and define any number of
          subprograms with the same name (provided they have different
          argument profiles).

   5. Multitasking. (Ada 83 and Ada 95)
          Ada facilities for concurrent programming (multitasking) have
          traditionally found application in simulations and
          distributed/parallel programming. Ada tasking is an especially
          useful ingredient in the Ada 95 distributed programming model,
          and the combination of the two makes it possible to design
          parallel applications that have a high degree of operating
          system independence and portability. (More on this in item 6
          below.)

   6. Direct support for distributed/parallel computing in the language.
          (Ada 95)
          Ada 95 is probably the first internationally standardized
          language to combine in the same design complete facilities for
          multitasking and parallel programming. Communication between
          the distributed partitions is via synchronous and asynchronous
          remote procedure calls.

          Good discussion, along with code examples, is found in the
          Rationale, Part III E, and in the Ada 95 Reference Manual,
          Annex E. See also "Ada Letters", Vol. 13, No. 2 (1993), pp. 54
          and 78, and Vol. 14, No. 2 (1994), p. 80. (Full support for
          these features is provided by compilers that conform to the Ada
          95 distributed computing Annex. This conformance is optional,
          but for instance GNAT, the Gnu Ada 95 compiler, will meet these
          requirements.)

   7. Attributes of floating point types. (Ada 83 and Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that return the essential
          characteristics of the type. For example, if you declare "type
          Real is digits 15" then you can get the max exponent of objects
          of type Real from Real'Machine_Emax. Similarly, the size of the
          Mantissa, the Radix, the largest Real, and the Rounding policy
          of the arithmetic are given by Real'Machine_Mantissa,
          Real'Machine_Radix, Real'Last, and Real'Machine_Rounds. There
          are many others.

          (See Ada 95 Reference Manual, clause 3.5, subclause 3.5.8 and
          A.5.3, as well as Part III sections G.2 and G.4.1 of the Ada 95
          Rationale.)

   8. Attribute functions for floating point types. (Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that operate on objects of that
          type. For example, if you declare "type Real is digits 15" then
          Real'Remainder (X, Y) returns the exact remainder of X and Y: X
          - n*Y where n is the integer nearest X/Y. Real'Truncation(X),
          Real'Max(X,Y), Real'Rounding(X) have the usual meanings.
          Real'Fraction(X) and Real'Exponent(X) break X into mantissa and
          exponent; Real'Scaling(X, N) is exact scaling: multiplies X by
          Radix**N, which can be done by incrementing the exponent by N,
          etc. (See citations in item 7.)

   9. Modular arithmetic on integer types. (Ada 95)
          If you declare "type My_Unsigned is mod N", for arbitrary N,
          then arithmetic ("*", "+", etc.) on objects of type My_Unsigned
          returns the results modulo N. Boolean operators "and", "or",
          "xor", and "not" are defined on the objects as though they were
          arrays of bits (and likewise return results modulo N). For N a
          power of 2, the semantics are similar to those of C unsigned
          types.

   10. Generic elementary math functions for floating point types. (Ada
          95)
          Required of all compilers, and provided for any floating point
          type: Sqrt, Cos, Sin, Tan, Cot, Exp, Sinh, Cosh, Tanh, Coth,
          and the inverse functions of each of these, Arctan, Log,
          Arcsinh, etc. Also, X**Y for floating point X and Y. Compilers
          that conform to the Numerics Annex meet additional accuracy
          requirements.

          (See subclause A.5.1 of the Ada 95 RM, and Part III, Section
          A.3 of the Ada 95 Rationale.)

   11. Complex numbers. (Ada 95)
          Fortran-like, but with a new type called Imaginary. Type
          "Imaginary" allows programmers to write expressions in such a
          way that they are easier to optimize, more readable and appear
          in code as they appear on paper. Also, the ability to declare
          object of pure imaginary type reduces the number of cases in
          which premature type conversion of real numbers to complex
          causes floating point exceptions to occur. (Provided by
          compilers that conform to the Numerics Annex. The Gnu Ada 95
          compiler supports this annex, so the source code is freely
          available.)

   12. Generic elementary math functions for complex number types. (Ada
          95)
          Same functions supported for real types, but with complex
          arguments. Standard IO is provided for floating point types and
          Complex types. (Only required of compilers that support the
          Numerics Annex, like Gnu Ada.)

   13. Pseudo-random numbers for discrete and floating point types. (Ada
          95)
          A floating point pseudo-random number generator (PRNG) provides
          output in the range 0.0 .. 1.0. Discrete: A generic PRNG
          package is provided that can be instantiated with any discrete
          type: Boolean, Integer, Modular etc. The floating point PRNG
          package and instances of the (discrete) PRNG package are
          individually capable of producing independent streams of random
          numbers. Streams may be interrupted, stored, and resumed at
          later times (generally an important requirement in
          simulations). In Ada it is considered important that multiple
          tasks, engaged for example in simulations, have easy access to
          independent streams of pseudo random numbers. The Gnu Ada 95
          compiler provides the cryptographically secure X**2 mod N
          generator of Blum, Blum and Shub.

          (See subclause A.5.2 of the Ada 95 Reference Manual, and part
          III, section A.3.2 of the Ada Rationale.)

   14. Well-defined interfaces to Fortran and other languages. (Ada 83
          and Ada 95)
          It has always been a basic requirement of the language that it
          provide users a way to interface Ada programs with foreign
          languages, operating system services, GUI's, etc. Ada can be
          viewed as an interfacing language: its module system is
          composed of package specifications and separate package bodies.
          The package specifications can be used as strongly-type
          interfaces to libraries implemented in foreign languages, as
          well as to package bodies written in Ada. Ada 95 extends on
          these facilities with package interfaces to the basic data
          structures of C, Fortran, and COBOL and with new pragmas. For
          example, "pragma Convention(Fortran, M)" tells the compiler to
          store the elements of matrix M in the Fortran column-major
          order. (This pragma has already been implemented in the Gnu Ada
          95 compiler. Multi- lingual programming is also a basic element
          of the Gnu compiler project.) As a result, assembly language
          BLAS and other high performance linear algebra and
          communications libraries will be accessible to Ada programs.

          (See Ada 95 Reference Manual: clause B.1 and B.5 of Annex B,
          and Ada 95 Rationale: Part III B.)


6.4: How do I get Real valued and Complex valued math functions in Ada 95?

   (from Jonathan Parker)

   Complex type and functions are provided by compilers that support the
   numerics Annex. The packages that use Float for the Real number and
   for the Complex number are:

     Ada.Numerics.Elementary_Functions;
     Ada.Numerics.Complex_Types;
     Ada.Numerics.Complex_Elementary_Functions;


   The packages that use Long_Float for the Real number and for the
   Complex number are:

     Ada.Numerics.Long_Elementary_Functions;
     Ada.Numerics.Long_Complex_Types;
     Ada.Numerics.Long_Complex_Elementary_Functions;


   The generic versions are demonstrated in the following example. Keep
   in mind that the non-generic packages may have been better tuned for
   speed or accuracy. In practice you won't always instantiate all three
   packages at the same time, but here is how you do it:

     with Ada.Numerics.Generic_Complex_Types;
     with Ada.Numerics.Generic_Elementary_Functions;
     with Ada.Numerics.Generic_Complex_Elementary_Functions;

     procedure Do_Something_Numerical is

       type Real_8 is digits 15;

       package Real_Functions_8 is
         new Ada.Numerics.Generic_Elementary_Functions (Real_8);

       package Complex_Nums_8 is
         new Ada.Numerics.Generic_Complex_Types (Real_8);

       package Complex_Functions_8 is
         new Ada.Numerics.Generic_Complex_Elementary_Functions
           (Complex_Nums_8);

       use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
       ...
       ... -- Do something
       ...
     end Do_Something_Numerical;


6.5: What libraries or public algorithms exist for Ada?

   An Ada version of Fast Fourier Transform is available. It's in
   journal "Computers & Mathematics with Applications," vol. 26, no. 2,
   pp. 61-65, 1993, with the title:

   "Analysis of an Ada Based Version of Glassman's General N Point Fast
   Fourier Transform"

   The package is now available in the AdaNET repository, object #: 6728,
   in collection: Transforms. If you're not an AdaNET user, contact Peggy
   Lacey (lacey@rbse.mountain.net).

     _________________________________________________________________


7: Efficiency of Ada Constructs


7.1: How much extra overhead do generics have?

   If you overgeneralize the generic, there will be more work to do for
   the compiler. How do you know when you have overgeneralized? For
   instance, passing arithmetic operations as parameters is a bad sign.
   So are boolean or enumeration type generic formal parameters. If you
   never override the defaults for a parameter, you probably
   overengineered.

   Code sharing (if implemented and requested) will cause an additional
   overhead on some calls, which will be partially offset by improved
   locality of reference. (Translation, code sharing may win most when
   cache misses cost most.) If a generic unit is only used once in a
   program, code sharing always loses.

   R.R. Software chose code sharing as the implementation for generics
   because 2 or more instantiations of Float_Io in a macro implementation
   would have made a program too large to run in the amount of memory
   available on the PC machines that existed in 1983 (usually a 128k or
   256k machine).

   Generics in Ada can also result in loss of information which could
   have helped the optimizer. Since the compiler is not restricted by Ada
   staticness rules within a single module, you can often avoid penalties
   by declaring (or redeclaring) bounds so that they are local:

     package Global is
       subtype Global_Int is
         Integer range X..Y;

       ...
     end Global;


     with Global;
     package Local is
       subtype Global_Int is
         Global.Global_Int;

       package Some_Instance is
         new Foo (Global_Int);

       ...
     end Local;


   Ada rules say that having the subtype redeclared locally does not
   affect staticness, but on a few occasions optimizers have been caught
   doing a much better job. Since optimizers are constantly changing,
   they may have been caught just at the wrong time.

     _________________________________________________________________


8: Advanced Programming Techniques with Ada


8.1: Does Ada have automatic constructors and destructors?

   (Tucker Taft replies)

   At least in Ada 9X, functions with controlling results are inherited
   (even if overriding is required), allowing their use with dynamic
   binding and class-wide types. In most other OOPs, constructors can
   only be called if you know at compile time the "tag" (or equivalent)
   of the result you want. In Ada 9X, you can use the tag determined by
   the context to control dispatching to a function with a controlling
   result. For example:

     type Set is abstract tagged private;
     function  Empty return Set is abstract;
     function  Unit_Set(Element : Element_Type) return Set is abstract;
     procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
     function  Union(Left, Right : Set) return Set is abstract;
  ...

     procedure Convert(Source : Set'Class; Target : out Set'Class) is
       -- class-wide "convert" routine, can convert one representation
       --   of a set into another, so long as both set types are
       --   derived from "Set," either directly or indirectly.

       -- Algorithm:  Initialize Target to the empty set, and then
       --             copy all elements from Source set to Target set.

        Copy_Of_Source : Set'Class := Source;
        Element : Element_Type;
     begin
        Target := Empty;  -- Dispatching for Empty determined by Target'Tag.

        while Copy_Of_Source /= Empty loop
                       -- Dispatching for Empty based on Copy_Of_Source'Tag

            Remove_Element(Copy_Of_Source, Element);

            Target := Union(Target, Unit_Set(Element));
                       -- Dispatching for Unit_Set based on Target'Tag
        end loop;
     end Convert;


   The functions Unit_Set and Empty are essentially "constructors" and
   hence must be overridden in every extension of the abstract type Set.
   However, these operations can still be called with a class-wide
   expected type, and the controlling tag for the function calls will be
   determined at run-time by the context, analogous to the kind of
   (compile-time) overload resolution that uses context to disambiguate
   enumeration literals and aggregates.


8.2: How can I redefine assignment operations?

   See "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour
   (where is this located?)


8.3: Should I stick to a one package, one type approach while writing Ada
software?

   (Robb Nebbe responds)

   Offhand I can think of a couple of advantages arising from Ada's
   separation of the concepts of type and module.

   Separation of visibility and inheritance allows a programmer to
   isolate a derived type from the implementation details of its parent.
   To put it another way information hiding becomes a design decision
   instead of a decision that the programming language has already made
   for you.

   Another advantage that came "for free" is the distinction between
   subtyping and implementation inheritance. Since modules and types are
   independent concepts the interaction of the facilities for information
   hiding already present in Ada83 with inheritance provide an elegant
   solution to separating subtyping from implementation inheritance. (In
   my opinion more elegant than providing multiple forms of inheritance
   or two distinct language constructs.)


8.4: What is the "Beaujolais Effect"?

   The "Beaujolais Effect" is detrimental, and language designers should
   try to avoid it. But what is it?

   (from Tucker Taft)

   The term "Beaujolais Effect" comes from a prize (a bottle of
   Beaujolais) offered by Jean Ichbiah during the original Ada design
   process to anyone who could find a situation where adding or removing
   a single "use" clause could change a program from one legal
   interpretation to a different legal interpretation. (Or equivalently,
   adding or removing a single declaration from a "use"d package.)

   At least one bottle was awarded, and if the offer was still open, a
   few more might have been awarded during the Ada 9X process. However,
   thanks to some very nice analysis by the Ada 9X Language Precision
   Team (based at Odyssey Research Associates) we were able to identify
   the remaining cases of this effect in Ada 83, and remove them as part
   of the 9X process.

   The existing cases in Ada 83 had to do with implicit conversion of
   expressions of a universal type to a non-universal type. The rules in
   Ada 9X are subtly different, making any case that used to result in a
   Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.

   The Beaujolais effect is considered "harmful" because it is expected
   that during maintenance, declarations may be added or removed from
   packages without being able to do an exhaustive search for all places
   where the package is "use"d. If there were situations in the language
   which resulted in Beaujolais effects, then certain kinds of changes in
   "use"d packages might have mysterious effects in unexpected places.

   (from Jean D. Ichbiah)

   It is worth pointing that many popular languages have Beaujolais
   effect: e.g. the Borland Pascal "uses" clause, which takes an
   additive, layer-after-layer, interpretation of what you see in the
   used packages (units) definitely exhibits a Beaujolais effect.

   Last time I looked at C++, my impression was that several years of
   Beaujolais vintage productions would be required.

   For component-based software development, such effects are undesirable
   since your application may stop working when you recompile it with the
   new -- supposedly improved -- version of a component.




^ permalink raw reply	[relevance 2%]

* Ada FAQ: Programming with Ada (part 2 of 3)
@ 1995-03-21 18:10  2% Magnus Kempe
  0 siblings, 0 replies; 35+ results
From: Magnus Kempe @ 1995-03-21 18:10 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part2
Comp-lang-ada-archive-name: programming/part2
Posting-Frequency: monthly
Last-modified: 20 March 1995
Last-posted: 20 February 1995

                               Ada Programmer's
                      Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

This is part 2 of a 3-part posting.
Part 3 begins with question 8.6; it should be the next posting in this thread.
Part 1 should be the previous posting in this thread.


5.6: What do "covariance" and "contravariance" mean, and does Ada support
either or both?

   (From Robert Martin) [This is C++ stuff, it must be completely
   re-written for Ada. --MK]


 R> covariance:  "changes with"
 R> contravariance: "changes against"

 R> class A
 R> {
 R>    public:
 R>      A* f(A*);   // method of class A, takes A argument and returns A
 R>      A* g(A*);   // same.
 R> };

 R> class B : public A // class B is a subclass of class A
 R> {
 R>   public:
 R>     B* f(B*);  // method of class B overrides f and is covariant.
 R>     A* g(A*);  // method of class B overrides g and is contravariant.
 R> };

 R> The function f is covariant because the type of its return value and
 R> argument changes with the class it belongs to.  The function g is
 R> contravariant because the types of its return value and arguments does not
 R> change with the class it belongs to.


   Actually, I would call g() invariant. If you look in Sather, (one of
   the principle languages with contravariance), you will see that the
   method in the decendent class actually can have aruments that are
   superclasses of the arguments of its parent. So for example:

class A : public ROOT
{
   public:
     A* f(A*);   // method of class A, takes A argument and returns A
     A* g(A*);   // same.
};

class B : public A // class B is a subclass of class A
{
  public:
    B* f(B*);  // method of class B overrides f and is covariant.
    ROOT* g(ROOT*);  // method of class B overrides g and is contravariant.
};


   To my knowledge the uses for contravariance are rare or nonexistent.
   (Anyone?). It just makes the rules easy for the compiler to type
   check. On the other hand, co-variance is extremely useful. Suppose you
   want to test for equality, or create a new object of the same type as
   the one in hand:

class A
{
   public:
      BOOLEAN equal(A*);
      A* create();
}

class B: public A
{
   public:
      BOOLEAN equal(B*);
      B* create();
}


   Here covariance is exactly what you want. Eiffel gives this to you,
   but the cost is giving up 100% compile time type safety. This seem
   necessary in cases like these.

   In fact, Eiffel gives you automatic ways to make a method covariant,
   called "anchored types". So you could declare, (in C++/eiffese):

class A
{
   public:
      BOOLEAN equal(like Current *);
      like Current * create();
}


   Which says equal takes an argument the same type as the current
   object, and create returns an object of the same type as current. Now,
   there is not even any need to redeclare these in class B. Those
   transformations happen for free!


5.7: What is meant by upcasting/expanding and downcasting/narrowing?

   (Tucker Taft replies):

   Here is the symmetric case to illustrate upcasting and downcasting.

     type A is tagged ...;   -- one parent type

     type B is tagged ...;   -- another parent type

     ...

     type C;   -- the new type, to be a mixture of A and B

     type AC (Obj : access C'Class) is
       new A
       with ...;
       -- an extension of A to be mixed into C

     type BC (Obj : access C'Class) is
       new B
       with ...;
       -- an extension of B to be mixed into C

     type C is
       tagged limited record
         A : AC (C'Access);
         B : BC (C'Access);
         ... -- other stuff if desired
       end record;


   We can now pass an object of type C to anything that takes an A or B
   as follows (this presumes that Foobar and QBert are primitives of A
   and B, respectively, so they are inherited; if not, then an explicit
   conversion (upcast) to A and B could be used to call the original
   Foobar and QBert).

     XC : C;
   ...
     Foobar (XC.A);
     QBert (XC.B);


   If we want to override what Foobar does, then we override Foobar on
   AC. If we want to override what QBert does, then we override QBert on
   BC.

   Note that there are no naming conflicts, since AC and BC are distinct
   types, so even if A and B have same-named components or operations, we
   can talk about them and/or override them individually using AC and BC.


   Upcasting (from C to A or C to B) is trivial -- A(XC.A) upcasts to A;
   B(XC.B) upcasts to B.

   Downcasting (narrowing) is also straightforward and safe. Presuming XA
   of type A'Class, and XB of type B'Class:

     AC(XA).Obj.all downcasts to C'Class (and verifies XA in AC'Class)
     BC(XB).Obj.all downcasts to C'Class (and verifies XB in BC'Class)


   You can check before the downcast to avoid a Constraint_Error:

     if XA not in AC'Class then -- appropriate complaint

     if XB not in BC'Class then -- ditto


   The approach is slightly simpler (though less symmetric) if we choose
   to make A the "primary" parent and B a "secondary" parent:

     type A is ...
     type B is ...

     type C;

     type BC (Obj : access C'Class) is
       new B
       with ...

     type C is
       new A
       with record
         B : BC (C'Access);
         ... -- other stuff if desired
       end record;


   Now C is a "normal" extension of A, and upcasting from C to A and
   (checked) downcasting from C'Class to A (or A'Class) is done with
   simple type conversions. The relationship between C and B is as above
   in the symmetric approach.

   Not surprisingly, using building blocks is more work than using a
   "builtin" approach for simple cases that happen to match the builtin
   approach, but having building blocks does ultimately mean more
   flexibility for the programmer -- there are many other structures that
   are possible in addition to the two illustrated above, using the
   access discriminant building block.

   For example, for mixins, each mixin "flavor" would have an access
   discriminant already:

     type Window is ...  -- The basic "vanilla" window

     -- Various mixins
     type Win_Mixin_1 (W : access Window'Class) is ...

     type Win_Mixin_2 (W : access Window'Class) is ...

     type Win_Mixin_3 (W : access Window'Class) is ...


   Given the above vanilla window, plus any number of window mixins, one
   can construct a desired window by including as many mixins as wanted:

     type My_Window is
       new Window
       with record
         M1 : Win_Mixin_1 (My_Window'access);
         M3 : Win_Mixin_3 (My_Window'access);
         M11 : Win_Mixin_1(My_Window'access);
         ... -- plus additional stuff, as desired.
       end record;


   As illustrated above, you can incorporate the same "mixin" multiple
   times, with no naming conflicts. Every mixin can get access to the
   enclosing object. Operations of individual mixins can be overridden by
   creating an extension of the mixin first, overriding the operation in
   that, and then incorporating that tweaked mixin into the ultimate
   window.

   I hope the above helps better illustrate the use and flexibility of
   the Ada 9X type composition building blocks.


5.8: How does Ada do "narrowing"?

   Dave Griffith said

     . . . Nonetheless, The Ada9x committee chose a structure-based
     subtyping, with all of the problems that that is known to cause. As
     the problems of structure based subtyping usually manifest only in
     large projects maintained by large groups, this is _precisely_ the
     subtype paradigm that Ada9x should have avoided. Ada9x's model is,
     as Tucker Taft pointed out, quite easy to use for simple OO
     programming. There is, however, no good reason to _do_ simple OO
     programming. OO programmings gains click in somewhere around 10,000
     LOC, with greatest gains at over 100,000. At these sizes, "just
     declare it tagged" will result in unmaintainable messes. OO
     programming in the large rapidly gets difficult with structure based
     subtyping. Allowing by-value semantics for objects compounds these
     problems. All of this is known. All of this was, seemingly, ignored
     by Ada9x.


   (Tucker Taft answers)

   As explained in a previous note, Ada 9X supports the ability to hide
   the implementation heritage of a type, and only expose the desired
   interface heritage. So we are not stuck with strictly "structure-based
   subtyping." Secondly, by-reference semantics have many "well known"
   problems as well, and the designers of Modula-3 chose to, seemingly,
   ignore those ;-) ;-). Of course, in reality, neither set of language
   designers ignored either of these issues. Language design involves
   tradeoffs. You can complain we made the wrong tradeoff, but to
   continue to harp on the claim that we "ignored" things is silly. We
   studied every OOP language under the sun on which we could find any
   written or electronic material. We chose value-based semantics for
   what we believe are good reasons, based on reasonable tradeoffs.

   First of all, in the absence of an integrated garbage collector,
   by-reference semantics doesn't make much sense. Based on various
   tradeoffs, we decided against requiring an integrated garbage
   collector for Ada 9X.

   Secondly, many of the "known" problems with by-value semantics we
   avoided, by eliminating essentially all cases of "implicit
   truncation." One of the problems with the C++ version of "value
   semantics" is that on assignment and parameter passing, implicit
   truncation can take place mysteriously, meaning that a value that
   started its life representing one kind of thing gets truncated
   unintentionally so that it looks like a value of some ancestor type.
   This is largely because the name of a C++ class means differnt things
   depending on the context. When you declare an object, the name of the
   class determines the "exact class" of the object. The same thing
   applies to a by-value parameter. However, for references and pointers,
   the name of a class stands for that class and all of its derivatives.
   But since, in C++, a value of a subclass is always acceptable where a
   value of a given class is expected, you can get implicit truncation as
   part of assignment and by-value parameter passing. In Ada 9X, we avoid
   the implicit truncation because we support assignment for "class-wide"
   types, which never implicitly truncates, and one must do an explicit
   conversion to do an assignment that truncates. Parameter passing never
   implicitly truncates, even if an implicit conversion is performed as
   part of calling an inherited subprogram.

   In any case, why not either ignore Ada 9X or give it a fair shot? It
   is easy to criticize any particular design decision, but it is much
   harder to actually put together a complete integrated language design
   that meets the requirements of its user community, doesn't bankrupt
   the vendor community, and provides interesting fodder for the academic
   community ;-).

     _________________________________________________________________


6: Ada Numerics


6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?


   bugs.nosc.mil (128.49.4.117)
          Stuff of high quality in pub/ada The random number generator
          and random deviates are recommended. These are mirrored at the
          next site, wuarchive.

   ftp.rational.com
          Freeware version of the ISO math packages on Rational's FTP
          server. It's a binding over the C Math library, in
          public/apex/freeware/math_lib.tar.Z

   wuarchive.wustl.edu
          Site of PAL, the Public Ada Library: math routines scattered
          about in the directories under languages/ada in particular, in
          subdirectory swcomps

   source.asset.com
          This is not an anonymous ftp site for math software. What you
          should do is log on anonymously under ftp, and download the
          file asset.faq from the directory pub. This will tell you how
          to get an account.

   ftp.cs.kuleuven.ac.be
          Go to directory pub/Ada-Belgium/cdrom. There's a collection of
          math intensive software in directory swcomps. Mirrors some of
          PAL at wuarchive.wustl.edu.

   sw-eng.falls-church.va.us
          Go to directory public/adaic/tools/atip/adar to find
          extended-precision decimal arithmetic (up to 18 digits).
          Includes facilities for COBOL-like formatted output.


6.2: How can I write portable code in Ada 83 using predefined types like Float
and Long_Float? Likewise, how can I write portable code that uses Math
functions like Sin and Log that are defined for Float and Long_Float?

   (from Jonathan Parker)

   Ada 83 was slow to arrive at a standard naming convention for
   elementary math functions and complex numbers. Furthermore, you'll
   find that some compilers call the 64-bit floating point type
   Long_Float; other compilers call it Float. Fortunately, it is easy to
   write programs in Ada that are independent of the naming conventions
   for floating point types and independent of the naming conventions of
   math functions defined on those types.

   One of the cleanest ways is to make the program generic:

     generic
       type Real is digits <>;
       with function Arcsin (X : Real) return Real is <>;
       with function    Log (X : Real) return Real is <>;
       --  This is the natural log, inverse of Exp(X), sometimes written Ln(X).
     package Example_1 is
       ...
     end Example_1;


   So the above package doesn't care what the name of the floating point
   type is, or what package the Math functions are defined in, just as
   long as the floating point type has the right attributes (precision
   and range) for the algorithm, and likewise the functions. Everything
   in the body of Example_1 is written in terms of the abstract names,
   Real, Arcsin, and Log, even though you instantiate it with compiler
   specific names that can look very different:

      package Special_Case is new Example_1 (Long_Float, Asin, Ln);


   The numerical algorithms implemented by generics like Example_1 can
   usually be made to work for a range of floating point precisions. A
   well written program will perform tests on Real to reject
   instantiations of Example_1 if the floating points type is judged
   inadequate. The tests may check the number of digits of precision in
   Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
   largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
   These tests are often placed after the begin statement of package
   body, as in:

     package body Example_1 is
       ...
     begin
       if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
         raise Program_Error;
       end if;
     end Example_1;


   Making an algorithm as abstract as possible, (independent of data
   types as much as possible) can do a lot to improve the quality of the
   code. Support for abstraction is one of the many things Ada-philes
   find so attractive about the language. The designers of Ada 95
   recognized the value of abstraction in the design of numeric
   algorithms and have generalized many of the features of the '83 model.
   For example, no matter what floating point type you instantiate
   Example_1 with, Ada 95 provides you with functions for examining the
   exponent and the mantissas of the numbers, for truncating, determining
   exact remainders, scaling exponents, and so on. (In the body of
   Example_1, and in its spec also of course, these functions are
   written, respectively: Real'Exponent(X), Real'Fraction(X),
   Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
   others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
   on the base type of Real (called Real'Base) which is liable to have
   greater precision and range than type Real.

   It is rare to see a performance loss when using generics like this.
   However, if there is an unacceptable performance hit, or if generics
   cannot be used for some other reason, then subtyping and renaming will
   do the job. Here is an example of renaming:

     with Someones_Math_Lib;
     procedure Example_2 is

       subtype Real is Long_Float;

       package  Math renames Someones_Math_Lib;
       function Arcsin(X : Real) return Real renames Math.Asin
       function   Log (X : Real) return Real renames Math.  Ln;

       --  Everything beyond this point is abstract with respect to
       --  the names of the floating point (Real), the functions (Arcsin
       --  and Log), and the package that exported them (Math).
       ...
     end Example_2;


   I prefer to make every package and subprogram (even test procedures)
   as compiler independent and machine portable as possible. To do this
   you move all of the renaming of compiler dependent functions and all
   of the "withing" of compiler dependent packages to a single package.
   In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
   the 8-byte floating point type to Real_8, and makes sure the math
   functions follow the Ada 95 standard, at least in name. In this
   approach Math_Lib_8 is the only compiler dependent component.

   There are other, perhaps better, ways also. See for example, "Ada In
   Action", by Do-While Jones for a generic solution.

   Here's the spec of Math_Lib_8, which is a perfect subset of package
   Math_Env_8, available by FTP in file
   ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada


--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries.  It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61.  These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics.   It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments.  The issues are the same with most math libraries.
--***************************************************************

--with Math_Lib;                                  -- Meridian DOS Ada.
  with Long_Float_Math_Lib;                       -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is

--subtype Real_8 is Float;                        -- Meridian 8-byte Real
  subtype Real_8 is Long_Float;                   -- Dec VMS  8-byte Real

 --package Math renames Math_Lib;                 -- Meridian DOS Ada
   package Math renames Long_Float_Math_Lib;      -- Dec VMS
 --package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);

   --  The above instantiation of the Ada.Numerics child package works on
   --  GNAT, or any other Ada 95 compiler.  Its here if you want to use
   --  an Ada 95 compiler to compile Ada 83 programs based on this package.

   function Cos (X : Real_8) return Real_8 renames Math.Cos;
   function Sin (X : Real_8) return Real_8 renames Math.Sin;
   function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
   function Exp (X : Real_8) return Real_8 renames Math.Exp;

 --function Log (X : Real_8) return Real_8 renames Math.Ln;        -- Meridian
   function Log (X : Real_8) return Real_8 renames Math.Log;       -- Dec VMS
 --function Log (X : Real_8) return Real_8 renames Math.Log;       -- Ada 95

 --function Arcsin (X : Real_8) return Real_8 renames Math.Asin;   -- Dec VMS
 --function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
   function Arcsin (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Arccos (X : Real_8) return Real_8 renames Math.Acos;   -- Dec VMS
 --function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
   function Arccos (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
   function Real_8_Floor (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

end Math_Lib_8;


6.3: Is Ada any good at numerics, and where can I learn more about it?

   First of all, a lot of people find the general Ada philosophy
   (modularity, strong-typing, readable syntax, rigorous definition and
   standardization, etc.) to be a real benefit in numerical programming,
   as well as in many other types of programming. Further, Ada--and
   especially Ada 95--was designed to also meet the special requirements
   of number-crunching applications.

   The following sketches out some of these features. Hopefully a little
   of the flavor of the Ada philosophy will get through, but the best
   thing you can do at present is to read the two standard reference
   documents, the Ada 95 Rationale and Reference Manual.

   1. Machine portable floating point declarations. (Ada 83 and Ada 95)
          If you declare "type Real is digits 14", then type Real will
          guarantee you (at least) 14 digits of precision independently
          of machine or compiler. In this case the base type of type Real
          will usually be the machine's 8-byte floating point type. If an
          appropriate base type is unavailable (very rare), then the
          declaration is rejected by the compiler.

   2. Extended precision for initialization of floating point. (Ada 83
          and Ada 95)
          Compilers are required to employ
          extended-precision/rational-arithmetic routines so that
          floating point variables and constants can be correctly
          initialized to their full precision.

   3. Generic packages and subprograms. (Ada 83 and Ada 95)
          Algorithms can be written so that they perform on abstract
          representations of the data structure. Support for this is
          provided by Ada's generic facilities (what C++ programmers
          would call templates).

   4. User-defined operators and overloaded subprograms. (Ada 83 and Ada
          95)
          The programmer can define his own operators (functions like
          "*", "+", "abs", "xor", "or", etc.) and define any number of
          subprograms with the same name (provided they have different
          argument profiles).

   5. Multitasking. (Ada 83 and Ada 95)
          Ada facilities for concurrent programming (multitasking) have
          traditionally found application in simulations and
          distributed/parallel programming. Ada tasking is an especially
          useful ingredient in the Ada 95 distributed programming model,
          and the combination of the two makes it possible to design
          parallel applications that have a high degree of operating
          system independence and portability. (More on this in item 6
          below.)

   6. Direct support for distributed/parallel computing in the language.
          (Ada 95)
          Ada 95 is probably the first internationally standardized
          language to combine in the same design complete facilities for
          multitasking and parallel programming. Communication between
          the distributed partitions is via synchronous and asynchronous
          remote procedure calls.

          Good discussion, along with code examples, is found in the
          Rationale, Part III E, and in the Ada 95 Reference Manual,
          Annex E. See also "Ada Letters", Vol. 13, No. 2 (1993), pp. 54
          and 78, and Vol. 14, No. 2 (1994), p. 80. (Full support for
          these features is provided by compilers that conform to the Ada
          95 distributed computing Annex. This conformance is optional,
          but for instance GNAT, the Gnu Ada 95 compiler, will meet these
          requirements.)

   7. Attributes of floating point types. (Ada 83 and Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that return the essential
          characteristics of the type. For example, if you declare "type
          Real is digits 15" then you can get the max exponent of objects
          of type Real from Real'Machine_Emax. Similarly, the size of the
          Mantissa, the Radix, the largest Real, and the Rounding policy
          of the arithmetic are given by Real'Machine_Mantissa,
          Real'Machine_Radix, Real'Last, and Real'Machine_Rounds. There
          are many others.

          (See Ada 95 Reference Manual, clause 3.5, subclause 3.5.8 and
          A.5.3, as well as Part III sections G.2 and G.4.1 of the Ada 95
          Rationale.)

   8. Attribute functions for floating point types. (Ada 95)
          For every floating point type (including user defined types),
          there are built-in functions that operate on objects of that
          type. For example, if you declare "type Real is digits 15" then
          Real'Remainder (X, Y) returns the exact remainder of X and Y: X
          - n*Y where n is the integer nearest X/Y. Real'Truncation(X),
          Real'Max(X,Y), Real'Rounding(X) have the usual meanings.
          Real'Fraction(X) and Real'Exponent(X) break X into mantissa and
          exponent; Real'Scaling(X, N) is exact scaling: multiplies X by
          Radix**N, which can be done by incrementing the exponent by N,
          etc. (See citations in item 7.)

   9. Modular arithmetic on integer types. (Ada 95)
          If you declare "type My_Unsigned is mod N", for arbitrary N,
          then arithmetic ("*", "+", etc.) on objects of type My_Unsigned
          returns the results modulo N. Boolean operators "and", "or",
          "xor", and "not" are defined on the objects as though they were
          arrays of bits (and likewise return results modulo N). For N a
          power of 2, the semantics are similar to those of C unsigned
          types.

   10. Generic elementary math functions for floating point types. (Ada
          95)
          Required of all compilers, and provided for any floating point
          type: Sqrt, Cos, Sin, Tan, Cot, Exp, Sinh, Cosh, Tanh, Coth,
          and the inverse functions of each of these, Arctan, Log,
          Arcsinh, etc. Also, X**Y for floating point X and Y. Compilers
          that conform to the Numerics Annex meet additional accuracy
          requirements.

          (See subclause A.5.1 of the Ada 95 RM, and Part III, Section
          A.3 of the Ada 95 Rationale.)

   11. Complex numbers. (Ada 95)
          Fortran-like, but with a new type called Imaginary. Type
          "Imaginary" allows programmers to write expressions in such a
          way that they are easier to optimize, more readable and appear
          in code as they appear on paper. Also, the ability to declare
          object of pure imaginary type reduces the number of cases in
          which premature type conversion of real numbers to complex
          causes floating point exceptions to occur. (Provided by
          compilers that conform to the Numerics Annex. The Gnu Ada 95
          compiler supports this annex, so the source code is freely
          available.)

   12. Generic elementary math functions for complex number types. (Ada
          95)
          Same functions supported for real types, but with complex
          arguments. Standard IO is provided for floating point types and
          Complex types. (Only required of compilers that support the
          Numerics Annex, like Gnu Ada.)

   13. Pseudo-random numbers for discrete and floating point types. (Ada
          95)
          A floating point pseudo-random number generator (PRNG) provides
          output in the range 0.0 .. 1.0. Discrete: A generic PRNG
          package is provided that can be instantiated with any discrete
          type: Boolean, Integer, Modular etc. The floating point PRNG
          package and instances of the (discrete) PRNG package are
          individually capable of producing independent streams of random
          numbers. Streams may be interrupted, stored, and resumed at
          later times (generally an important requirement in
          simulations). In Ada it is considered important that multiple
          tasks, engaged for example in simulations, have easy access to
          independent streams of pseudo random numbers. The Gnu Ada 95
          compiler provides the cryptographically secure X**2 mod N
          generator of Blum, Blum and Shub.

          (See subclause A.5.2 of the Ada 95 Reference Manual, and part
          III, section A.3.2 of the Ada Rationale.)

   14. Well-defined interfaces to Fortran and other languages. (Ada 83
          and Ada 95)
          It has always been a basic requirement of the language that it
          provide users a way to interface Ada programs with foreign
          languages, operating system services, GUI's, etc. Ada can be
          viewed as an interfacing language: its module system is
          composed of package specifications and separate package bodies.
          The package specifications can be used as strongly-type
          interfaces to libraries implemented in foreign languages, as
          well as to package bodies written in Ada. Ada 95 extends on
          these facilities with package interfaces to the basic data
          structures of C, Fortran, and COBOL and with new pragmas. For
          example, "pragma Convention(Fortran, M)" tells the compiler to
          store the elements of matrix M in the Fortran column-major
          order. (This pragma has already been implemented in the Gnu Ada
          95 compiler. Multi- lingual programming is also a basic element
          of the Gnu compiler project.) As a result, assembly language
          BLAS and other high performance linear algebra and
          communications libraries will be accessible to Ada programs.

          (See Ada 95 Reference Manual: clause B.1 and B.5 of Annex B,
          and Ada 95 Rationale: Part III B.)


6.4: How do I get Real valued and Complex valued math functions in Ada 95?

   (from Jonathan Parker)

   Complex type and functions are provided by compilers that support the
   numerics Annex. The packages that use Float for the Real number and
   for the Complex number are:

     Ada.Numerics.Elementary_Functions;
     Ada.Numerics.Complex_Types;
     Ada.Numerics.Complex_Elementary_Functions;


   The packages that use Long_Float for the Real number and for the
   Complex number are:

     Ada.Numerics.Long_Elementary_Functions;
     Ada.Numerics.Long_Complex_Types;
     Ada.Numerics.Long_Complex_Elementary_Functions;


   The generic versions are demonstrated in the following example. Keep
   in mind that the non-generic packages may have been better tuned for
   speed or accuracy. In practice you won't always instantiate all three
   packages at the same time, but here is how you do it:

     with Ada.Numerics.Generic_Complex_Types;
     with Ada.Numerics.Generic_Elementary_Functions;
     with Ada.Numerics.Generic_Complex_Elementary_Functions;

     procedure Do_Something_Numerical is

       type Real_8 is digits 15;

       package Real_Functions_8 is
         new Ada.Numerics.Generic_Elementary_Functions (Real_8);

       package Complex_Nums_8 is
         new Ada.Numerics.Generic_Complex_Types (Real_8);

       package Complex_Functions_8 is
         new Ada.Numerics.Generic_Complex_Elementary_Functions
           (Complex_Nums_8);

       use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
       ...
       ... -- Do something
       ...
     end Do_Something_Numerical;


6.5: What libraries or public algorithms exist for Ada?

   An Ada version of Fast Fourier Transform is available. It's in
   journal "Computers & Mathematics with Applications," vol. 26, no. 2,
   pp. 61-65, 1993, with the title:

   "Analysis of an Ada Based Version of Glassman's General N Point Fast
   Fourier Transform"

   The package is now available in the AdaNET repository, object #: 6728,
   in collection: Transforms. If you're not an AdaNET user, contact Peggy
   Lacey (lacey@rbse.mountain.net).

     _________________________________________________________________


7: Efficiency of Ada Constructs


7.1: How much extra overhead do generics have?

   If you overgeneralize the generic, there will be more work to do for
   the compiler. How do you know when you have overgeneralized? For
   instance, passing arithmetic operations as parameters is a bad sign.
   So are boolean or enumeration type generic formal parameters. If you
   never override the defaults for a parameter, you probably
   overengineered.

   Code sharing (if implemented and requested) will cause an additional
   overhead on some calls, which will be partially offset by improved
   locality of reference. (Translation, code sharing may win most when
   cache misses cost most.) If a generic unit is only used once in a
   program, code sharing always loses.

   R.R. Software chose code sharing as the implementation for generics
   because 2 or more instantiations of Float_Io in a macro implementation
   would have made a program too large to run in the amount of memory
   available on the PC machines that existed in 1983 (usually a 128k or
   256k machine).

   Generics in Ada can also result in loss of information which could
   have helped the optimizer. Since the compiler is not restricted by Ada
   staticness rules within a single module, you can often avoid penalties
   by declaring (or redeclaring) bounds so that they are local:

     package Global is
       subtype Global_Int is
         Integer range X..Y;

       ...
     end Global;


     with Global;
     package Local is
       subtype Global_Int is
         Global.Global_Int;

       package Some_Instance is
         new Foo (Global_Int);

       ...
     end Local;


   Ada rules say that having the subtype redeclared locally does not
   affect staticness, but on a few occasions optimizers have been caught
   doing a much better job. Since optimizers are constantly changing,
   they may have been caught just at the wrong time.

     _________________________________________________________________


8: Advanced Programming Techniques with Ada


8.1: Does Ada have automatic constructors and destructors?

   (Tucker Taft replies)

   At least in Ada 9X, functions with controlling results are inherited
   (even if overriding is required), allowing their use with dynamic
   binding and class-wide types. In most other OOPs, constructors can
   only be called if you know at compile time the "tag" (or equivalent)
   of the result you want. In Ada 9X, you can use the tag determined by
   the context to control dispatching to a function with a controlling
   result. For example:

     type Set is abstract tagged private;
     function  Empty return Set is abstract;
     function  Unit_Set(Element : Element_Type) return Set is abstract;
     procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
     function  Union(Left, Right : Set) return Set is abstract;
  ...

     procedure Convert(Source : Set'Class; Target : out Set'Class) is
       -- class-wide "convert" routine, can convert one representation
       --   of a set into another, so long as both set types are
       --   derived from "Set," either directly or indirectly.

       -- Algorithm:  Initialize Target to the empty set, and then
       --             copy all elements from Source set to Target set.

        Copy_Of_Source : Set'Class := Source;
        Element : Element_Type;
     begin
        Target := Empty;  -- Dispatching for Empty determined by Target'Tag.

        while Copy_Of_Source /= Empty loop
                       -- Dispatching for Empty based on Copy_Of_Source'Tag

            Remove_Element(Copy_Of_Source, Element);

            Target := Union(Target, Unit_Set(Element));
                       -- Dispatching for Unit_Set based on Target'Tag
        end loop;
     end Convert;


   The functions Unit_Set and Empty are essentially "constructors" and
   hence must be overridden in every extension of the abstract type Set.
   However, these operations can still be called with a class-wide
   expected type, and the controlling tag for the function calls will be
   determined at run-time by the context, analogous to the kind of
   (compile-time) overload resolution that uses context to disambiguate
   enumeration literals and aggregates.


8.2: How can I redefine assignment operations?

   See "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour
   (where is this located?)


8.3: Should I stick to a one package, one type approach while writing Ada
software?

   (Robb Nebbe responds)

   Offhand I can think of a couple of advantages arising from Ada's
   separation of the concepts of type and module.

   Separation of visibility and inheritance allows a programmer to
   isolate a derived type from the implementation details of its parent.
   To put it another way information hiding becomes a design decision
   instead of a decision that the programming language has already made
   for you.

   Another advantage that came "for free" is the distinction between
   subtyping and implementation inheritance. Since modules and types are
   independent concepts the interaction of the facilities for information
   hiding already present in Ada83 with inheritance provide an elegant
   solution to separating subtyping from implementation inheritance. (In
   my opinion more elegant than providing multiple forms of inheritance
   or two distinct language constructs.)


8.4: What is the "Beaujolais Effect"?

   The "Beaujolais Effect" is detrimental, and language designers should
   try to avoid it. But what is it?

   (from Tucker Taft)

   The term "Beaujolais Effect" comes from a prize (a bottle of
   Beaujolais) offered by Jean Ichbiah during the original Ada design
   process to anyone who could find a situation where adding or removing
   a single "use" clause could change a program from one legal
   interpretation to a different legal interpretation. (Or equivalently,
   adding or removing a single declaration from a "use"d package.)

   At least one bottle was awarded, and if the offer was still open, a
   few more might have been awarded during the Ada 9X process. However,
   thanks to some very nice analysis by the Ada 9X Language Precision
   Team (based at Odyssey Research Associates) we were able to identify
   the remaining cases of this effect in Ada 83, and remove them as part
   of the 9X process.

   The existing cases in Ada 83 had to do with implicit conversion of
   expressions of a universal type to a non-universal type. The rules in
   Ada 9X are subtly different, making any case that used to result in a
   Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.

   The Beaujolais effect is considered "harmful" because it is expected
   that during maintenance, declarations may be added or removed from
   packages without being able to do an exhaustive search for all places
   where the package is "use"d. If there were situations in the language
   which resulted in Beaujolais effects, then certain kinds of changes in
   "use"d packages might have mysterious effects in unexpected places.

   (from Jean D. Ichbiah)

   It is worth pointing that many popular languages have Beaujolais
   effect: e.g. the Borland Pascal "uses" clause, which takes an
   additive, layer-after-layer, interpretation of what you see in the
   used packages (units) definitely exhibits a Beaujolais effect.

   Last time I looked at C++, my impression was that several years of
   Beaujolais vintage productions would be required.

   For component-based software development, such effects are undesirable
   since your application may stop working when you recompile it with the
   new -- supposedly improved -- version of a component.


8.5: What about the "Ripple Effect"?

   (Tucker Taft explains)

   We have eliminated all remnants of the Beaujolais Effect, but we did
   debate various instances of the "Ripple" effect during the language
   revision process (apologies to Gallo Ripple Wine enthusiasts ;-).

   In brief, the (undesirable) Ripple effect was related to whether the
   legality of a compilation unit could be affected by adding or removing
   an otherwise unneeded "with" clause on some compilation unit on which
   the unit depended, directly or indirectly.

   This issue came up at least twice. One when we were considering rules
   relating to use of attributes like 'Address. In Ada 83 as interpreted
   by the ARG, if a compilation unit contains a use of 'Address, then
   there must be a "with" of package System somewhere in the set of
   library unit specs "with"ed by the compilation unit (directly or
   indirectly).

   In Ada 9X, we have eliminated this rule, as it was for some compilers
   an unnecessary implementation burden, and didn't really provide any
   value to the user (if anything, it created some confusion). The rule
   now is that the use of an attibute that returns a value of some
   particular type makes the compilation unit semantically dependent on
   the library unit in which the type is declared (whether or not it is
   "with"ed).

   The second place the Ripple effect came up was when we were trying to
   provide automatic direct visibility to (primitive) operators.
   Ultimately we ended up with an explicit "use type" clause for making
   operators directly visible. For a while we considered various rules
   that would make all primitive operators directly visible; some of the
   rules considered created the undesirable "Ripple" effects; others
   created annoying incompatibilities; all were quite tricky to implement
   correctly and efficiently.



^ permalink raw reply	[relevance 2%]

* Ada FAQ: Programming with Ada (part 2 of 3)
@ 1995-01-19 18:10  2% Magnus Kempe
  0 siblings, 0 replies; 35+ results
From: Magnus Kempe @ 1995-01-19 18:10 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part2
Comp-lang-ada-archive-name: programming/part2
Posting-Frequency: monthly
Last-modified: 19 January 1995
Last-posted: the epoch

                               Ada Programmer'S
                       Frequently Asked Questions (FAQ)

This is part 2 of a 3-part posting.
Part 3 begins with question 9.6; it should be the next posting in this thread.
Part 1 should be the previous posting in this thread.


5.7: What is meant by upcasting/expanding and downcasting/narrowing?

   (Tucker Taft replies):

   Here is the symmetric case to illustrate upcasting and downcasting.

     type A is tagged ...;   -- one parent type

     type B is tagged ...;   -- another parent type

     ...

     type C;   -- the new type, to be a mixture of A and B

     type AC (Obj : access C'Class) is
       new A
       with ...;
       -- an extension of A to be mixed into C

     type BC (Obj : access C'Class) is
       new B
       with ...;
       -- an extension of B to be mixed into C

     type C is
       tagged limited record
         A : AC (C'Access);
         B : BC (C'Access);
         ... -- other stuff if desired
       end record;


   We can now pass an object of type C to anything that takes an A or B
   as follows (this presumes that Foobar and QBert are primitives of A
   and B, respectively, so they are inherited; if not, then an explicit
   conversion (upcast) to A and B could be used to call the original
   Foobar and QBert).

     XC : C;
   ...
     Foobar (XC.A);
     QBert (XC.B);


   If we want to override what Foobar does, then we override Foobar on
   AC. If we want to override what QBert does, then we override QBert on
   BC.

   Note that there are no naming conflicts, since AC and BC are distinct
   types, so even if A and B have same-named components or operations, we
   can talk about them and/or override them individually using AC and BC.

   Upcasting (from C to A or C to B) is trivial -- A(XC.A) upcasts to A;
   B(XC.B) upcasts to B.

   Downcasting (narrowing) is also straightforward and safe. Presuming XA
   of type A'Class, and XB of type B'Class:

     AC(XA).Obj.all downcasts to C'Class (and verifies XA in AC'Class)
     BC(XB).Obj.all downcasts to C'Class (and verifies XB in BC'Class)


   You can check before the downcast to avoid a Constraint_Error:

     if XA not in AC'Class then -- appropriate complaint

     if XB not in BC'Class then -- ditto


   The approach is slightly simpler (though less symmetric) if we choose
   to make A the "primary" parent and B a "secondary" parent:

     type A is ...
     type B is ...

     type C;

     type BC (Obj : access C'Class) is
       new B
       with ...

     type C is
       new A
       with record
         B : BC (C'Access);
         ... -- other stuff if desired
       end record;


   Now C is a "normal" extension of A, and upcasting from C to A and
   (checked) downcasting from C'Class to A (or A'Class) is done with
   simple type conversions. The relationship between C and B is as above
   in the symmetric approach.

   Not surprisingly, using building blocks is more work than using a
   "builtin" approach for simple cases that happen to match the builtin
   approach, but having building blocks does ultimately provide mean more
   flexibility for the programmer -- there are many other structures that
   are possible in addition to the two illustrated above, using the
   access discriminant building block.

   For example, for mixins, each mixin "flavor" would have an access
   discriminant already:

     type Window is ...  -- The basic "vanilla" window

     -- Various mixins
     type Win_Mixin_1 (W : access Window'Class) is ...

     type Win_Mixin_2 (W : access Window'Class) is ...

     type Win_Mixin_3 (W : access Window'Class) is ...


   Given the above vanilla window, plus any number of window mixins, one
   can construct a desired window by including as many mixins as wanted:

     type My_Window is Window with
        M1 : Win_Mixin_1 (My_Window'access);
        M3 : Win_Mixin_3 (My_Window'access);
        M11 : Win_Mixin_1(My_Window'access);
       ... -- plus additional stuff, as desired.
     end record;


   As illustrated above, you can incorporate the same "mixin" multiple
   times, with no naming conflicts. Every mixin can get access to the
   enclosing object. Operations of individual mixins can be overridden by
   creating an extension of the mixin first, overriding the operation in
   that, and then incorporating that tweaked mixin into the ultimate
   window.

   I hope the above helps better illustrate the use and flexibility of
   the Ada 9X type composition building blocks.


5.8: How does Ada do "narrowing"?

   Dave Griffith said

     . . . Nonetheless, The Ada9x committee chose a structure-based
     subtyping, with all of the problems that that is known to cause. As
     the problems of structure based subtyping usually manifest only in
     large projects maintained by large groups, this is _precisely_ the
     subtype paradigm that Ada9x should have avoided. Ada9x's model is,
     as Tucker Taft pointed out, quite easy to use for simple OO
     programming. There is, however, no good reason to _do_ simple OO
     programming. OO programmings gains click in somewhere around 10,000
     LOC, with greatest gains at over 100,000. At these sizes, "just
     declare it tagged" will result in unmaintainable messes. OO
     programming in the large rapidly gets difficult with structure based
     subtyping. Allowing by-value semantics for objects compounds these
     problems. All of this is known. All of this was, seemingly, ignored
     by Ada9x.


   (Tucker Taft answers)

   As explained in a previous note, Ada 9X supports the ability to hide
   the implementation heritage of a type, and only expose the desired
   interface heritage. So we are not stuck with strictly "structure-based
   subtyping." Secondly, by-reference semantics have many "well known"
   problems as well, and the designers of Modula-3 chose to, seemingly,
   ignore those ;-) ;-). Of course, in reality, neither set of language
   designers ignored either of these issues. Language design involves
   tradeoffs. You can complain we made the wrong tradeoff, but to
   continue to harp on the claim that we "ignored" things is silly. We
   studied every OOP language under the sun on which we could find any
   written or electronic material. We chose value-based semantics for
   what we believe are good reasons, based on reasonable tradeoffs.

   First of all, in the absence of an integrated garbage collector,
   by-reference semantics doesn't make much sense. Based on various
   tradeoffs, we decided against requiring an integrated garbage
   collector for Ada 9X.

   Secondly, many of the "known" problems with by-value semantics we
   avoided, by eliminating essentially all cases of "implicit
   truncation." One of the problems with the C++ version of "value
   semantics" is that on assignment and parameter passing, implicit
   truncation can take place mysteriously, meaning that a value that
   started its life representing one kind of thing gets truncated
   unintentionally so that it looks like a value of some ancestor type.
   This is largely because the name of a C++ class means differnt things
   depending on the context. When you declare an object, the name of the
   class determines the "exact class" of the object. The same thing
   applies to a by-value parameter. However, for references and pointers,
   the name of a class stands for that class and all of its derivatives.
   But since, in C++, a value of a subclass is always acceptable where a
   value of a given class is expected, you can get implicit truncation as
   part of assignment and by-value parameter passing. In Ada 9X, we avoid
   the implicit truncation because we support assignment for "class-wide"
   types, which never implicitly truncates, and one must do an explicit
   conversion to do an assignment that truncates. Parameter passing never
   implicitly truncates, even if an implicit conversion is performed as
   part of calling an inherited subprogram.

   In any case, why not either ignore Ada 9X or give it a fair shot? It
   is easy to criticize any particular design decision, but it is much
   harder to actually put together a complete integrated language design
   that meets the requirements of its user community, doesn't bankrupt
   the vendor community, and provides interesting fodder for the academic
   community ;-).

     _________________________________________________________________


6: Ada Numerics


6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?


   bugs.nosc.mil (128.49.4.117)
          Stuff of high quality in pub/ada The random number generator
          and random deviates are recommended. These are mirrored at the
          next site, wuarchive.

   ftp.rational.com
          Freeware version of the ISO math packages on Rational's FTP
          server. It's a binding over the C Math library, in
          public/apex/freeware/math_lib.tar.Z

   wuarchive.wustl.edu
          Site of PAL, the Public Ada Library: math routines scattered
          about in the directories under languages/ada in particular, in
          subdirectory swcomps

   source.asset.com
          This is not an anonymous ftp site for math software. What you
          should do is log on anonymously under ftp, and download the
          file asset.faq from the directory pub. This will tell you how
          to get an account.

   ftp.cs.kuleuven.ac.be
          Go to directory pub/Ada-Belgium/cdrom. There's a collection of
          math intensive software in directory swcomps. Mirrors some of
          PAL at wuarchive.wustl.edu.

   ajpo.sei.cmu.edu
          Go to directory public/atip/adar to find extended-precision
          decimal arithmetic. Includes facilities for COBOL-like IO.


6.2: How can I write portable code in Ada 83 using predefined types like Float
and Long_Float? Likewise, how can I write portable code that uses Math
functions like Sin and Log that are defined for Float and Long_Float?

   (from Jonathan Parker)

   Ada 83 was slow to arrive at a standard naming convention for
   elementary math functions and complex numbers. Furthermore, you'll
   find that some compilers call the 64-bit floating point type
   Long_Float; other compilers call it Float. Fortunately, it is easy to
   write programs in Ada that are independent of the naming conventions
   for floating point types and independent of the naming conventions of
   math functions defined on those types.

   One of the cleanest ways is to make the program generic:

     generic
       type Real is digits <>;
       with function Arcsin (X : Real) return Real is <>;
       with function    Log (X : Real) return Real is <>;
       --  This is the natural log, inverse of Exp(X), sometimes written Ln(X).
     package Example_1 is
       ...
     end Example_1;


   So the above package doesn't care what the name of the floating point
   type is, or what package the Math functions are defined in, just as
   long as the floating point type has the right attributes (precision
   and range) for the algorithm, and likewise the functions. Everything
   in the body of Example_1 is written in terms of the abstract names,
   Real, Arcsin, and Log, even though you instantiate it with compiler
   specific names that can look very different:

      package Special_Case is new Example_1 (Long_Float, Asin, Ln);


   The numerical algorithms implemented by generics like Example_1 can
   usually be made to work for a range of floating point precisions. A
   well written program will perform tests on Real to reject
   instantiations of Example_1 if the floating points type is judged
   inadequate. The tests may check the number of digits of precision in
   Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
   largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
   These tests are often placed after the begin statement of package
   body, as in:

     package body Example_1 is
       ...
     begin
       if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
         raise Program_Error;
       end if;
     end Example_1;


   Making an algorithm as abstract as possible, (independent of data
   types as much as possible) can do a lot to improve the quality of the
   code. Support for abstraction is one of the many things Ada-philes
   find so attractive about the language. The designers of Ada 95
   recognized the value of abstraction in the design of numeric
   algorithms and have generalized many of the features of the '83 model.
   For example, no matter what floating point type you instantiate
   Example_1 with, Ada 95 provides you with functions for examining the
   exponent and the mantissas of the numbers, for truncating, determining
   exact remainders, scaling exponents, and so on. (In the body of
   Example_1, and in its spec also of course, these functions are
   written, respectively: Real'Exponent(X), Real'Fraction(X),
   Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
   others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
   on the base type of Real (called Real'Base) which is liable to have
   greater precision and range than type Real.

   It is rare to see a performance loss when using generics like this.
   However, if there is an unacceptable performance hit, or if generics
   cannot be used for some other reason, then subtyping and renaming will
   do the job. Here is an example of renaming:

     with Someones_Math_Lib;
     procedure Example_2 is

       subtype Real is Long_Float;

       package  Math renames Someones_Math_Lib;
       function Arcsin(X : Real) return Real renames Math.Asin
       function   Log (X : Real) return Real renames Math.  Ln;

       --  Everything beyond this point is abstract with respect to
       --  the names of the floating point (Real), the functions (Arcsin
       --  and Log), and the package that exported them (Math).
       ...
     end Example_2;


   I prefer to make every package and subprogram (even test procedures)
   as compiler independent and machine portable as possible. To do this
   you move all of the renaming of compiler dependent functions and all
   of the "withing" of compiler dependent packages to a single package.
   In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
   the 8-byte floating point type to Real_8, and makes sure the math
   functions follow the Ada 95 standard, at least in name. In this
   approach Math_Lib_8 is the only compiler dependent component.

   There are other, perhaps better, ways also. See for example, "Ada In
   Action", by Do-While Jones for a generic solution.

   Here's the spec of Math_Lib_8, which is a perfect subset of package
   Math_Env_8, available by FTP in file
   ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada


--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries.  It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61.  These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics.   It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments.  The issues are the same with most math libraries.
--***************************************************************

--with Math_Lib;                                  -- Meridian DOS Ada.
  with Long_Float_Math_Lib;                       -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is

--subtype Real_8 is Float;                        -- Meridian 8-byte Real
  subtype Real_8 is Long_Float;                   -- Dec VMS  8-byte Real

 --package Math renames Math_Lib;                 -- Meridian DOS Ada
   package Math renames Long_Float_Math_Lib;      -- Dec VMS
 --package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);

   --  The above instantiation of the Ada.Numerics child package works on
   --  GNAT, or any other Ada 95 compiler.  Its here if you want to use
   --  an Ada 95 compiler to compile Ada 83 programs based on this package.

   function Cos (X : Real_8) return Real_8 renames Math.Cos;
   function Sin (X : Real_8) return Real_8 renames Math.Sin;
   function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
   function Exp (X : Real_8) return Real_8 renames Math.Exp;

 --function Log (X : Real_8) return Real_8 renames Math.Ln;        -- Meridian
   function Log (X : Real_8) return Real_8 renames Math.Log;       -- Dec VMS
 --function Log (X : Real_8) return Real_8 renames Math.Log;       -- Ada 95

 --function Arcsin (X : Real_8) return Real_8 renames Math.Asin;   -- Dec VMS
 --function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
   function Arcsin (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Arccos (X : Real_8) return Real_8 renames Math.Acos;   -- Dec VMS
 --function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
   function Arccos (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

 --function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
   function Real_8_Floor (X : Real_8) return Real_8;
   --  Implemented in the body.  Should work with any compiler.

end Math_Lib_8;


6.3: Where's a good place to start learning the Ada 95 numerics model?

   (from Jonathan Parker)

   Start with the Ada 95 Rationale. Part 3 of the Rationale, Section 1.3
   provides a good introduction, with examples on use of elementary
   function packages. Part 3 of the Rationale, Section 6.1 discusses the
   design of the Complex number and Complex function packages. Section 6
   reviews the numerics annex, especially attributes and accuracy
   requirement.

   The Rationale can be obtained by anonymous ftp from ajpo.sei.cmu.edu.
   Another site to get the Ada 95 Rationale from is the Ada WWW homepage
   at http://lglwww.epfl.ch/Ada/ .

   The Ada 95 Reference Manual describes the Real valued elementary Math
   functions, the Random number packages, and the floating point
   attributes in section A5 (page 295). The Ada 95 Reference Manual may
   be obtained by anonymous ftp from ajpo.sei.cmu.edu, in directory
   public/ada9x/rm9x.


6.4: How do I get Real valued and Complex valued math functions in Ada 95?

   (from Jonathan Parker)

   Complex type and functions are provided by compilers that support the
   numerics Annex. The packages that use Float for the Real number and
   for the Complex number are:

     Ada.Numerics.Elementary_Functions;
     Ada.Numerics.Complex_Types;
     Ada.Numerics.Complex_Elementary_Functions;


   The packages that use Long_Float for the Real number and for the
   Complex number are:

     Ada.Numerics.Long_Elementary_Functions;
     Ada.Numerics.Long_Complex_Types;
     Ada.Numerics.Long_Complex_Elementary_Functions;


   The generic versions are demonstrated in the following example. Keep
   in mind that the non-generic packages may have been better tuned for
   speed or accuracy. In practice you won't always instantiate all three
   packages at the same time, but here is how you do it:

     with Ada.Numerics.Generic_Complex_Types;
     with Ada.Numerics.Generic_Elementary_Functions;
     with Ada.Numerics.Generic_Complex_Elementary_Functions;

     procedure Do_Something_Numerical is

       type Real_8 is digits 15;

       package Real_Functions_8 is
         new Ada.Numerics.Generic_Elementary_Functions (Real_8);

       package Complex_Nums_8 is
         new Ada.Numerics.Generic_Complex_Types (Real_8);

       package Complex_Functions_8 is
         new Ada.Numerics.Generic_Complex_Elementary_Functions
           (Complex_Nums_8);

       use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
       ...
       ... -- Do something
       ...
     end Do_Something_Numerical;


6.5: What libraries or public algorithms exist for Ada?

   An Ada version of Fast Fourier Transform is available. It's in
   journal "Computers & Mathematics with Applications," vol. 26, no. 2,
   pp. 61-65, 1993, with the title:

   "Analysis of an Ada Based Version of Glassman's General N Point Fast
   Fourier Transform"

   The package is now available in the AdaNET repository, object #: 6728,
   in collection: Transforms. If you're not an AdaNET user, contact Peggy
   Lacey (lacey@rbse.mountain.net).

     _________________________________________________________________


7: Efficiency of Ada Constructs


7.1: How much extra overhead do generics have?

   (Submitted by Robert Eachus)

   If you overgeneralize the generic, there really will be more work to
   do. How do you know when you have overgeneralized? You don't. But
   passing arithmetic operations as parameters is a bad sign. So are
   boolean or enumeration type generic formal parameters. If you never
   override the defaults for a parameter, you almost certainly
   overengineered. (The last statement is not absolute only because I
   have created cases where the defaults were different for different
   instantiations and never needed to be overridden. I'd call that
   overelegant instead.)

   Code sharing (if implemented and requested) will cause an additional
   overhead on some calls, which will be partially offset by improved
   locality of reference. (Translation, code sharing wins most when cache
   misses cost most.) Of course, if a generic is only used once in a
   program, code sharing always loses.

   Generics in Ada can also result in loss of information which can help
   the optimizer. Since the compiler is not restricted by Ada staticness
   rules within a single module, you can often avoid penalties by
   declaring (or redeclaring) bounds so that they are local:

     with Lots_of_Things;
     package Global is
       subtype Global_Int is
         Integer range X..Y;

       ...
     end Global;


     with Global;
     package Local is
       subtype Global_Int is
         Global.Global_Int;

       package Some_Instance is
         new Foo(Global_Int);

       ...
     end Local;


   Ada rules say that having the subtype redeclared locally does not
   affect staticness, but on a few occasions I have caught optimizers
   doing a much better job. Of course, optimizers are constantly
   changing, so I may have just caught one at the wrong time.

     _________________________________________________________________


8: Advanced Programming Techniques with Ada


8.1: Does Ada have automatic constructors and destructors?

   (Tucker Taft replies)

   At least in Ada 9X, functions with controlling results are inherited
   (even if overriding is required), allowing their use with dynamic
   binding and class-wide types. In most other OOPs, constructors can
   only be called if you know at compile time the "tag" (or equivalent)
   of the result you want. In Ada 9X, you can use the tag determined by
   the context to control dispatching to a function with a controlling
   result. For example:

     type Set is abstract tagged private;
     function  Empty return Set is abstract;
     function  Unit_Set(Element : Element_Type) return Set is abstract;
     procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
     function  Union(Left, Right : Set) return Set is abstract;
  ...

     procedure Convert(Source : Set'Class; Target : out Set'Class) is
       -- class-wide "convert" routine, can convert one representation
       --   of a set into another, so long as both set types are
       --   derived from "Set," either directly or indirectly.

       -- Algorithm:  Initialize Target to the empty set, and then
       --             copy all elements from Source set to Target set.

        Copy_Of_Source : Set'Class := Source;
        Element : Element_Type;
     begin
        Target := Empty;  -- Dispatching for Empty determined by Target'Tag.

        while Copy_Of_Source /= Empty loop
                       -- Dispatching for Empty based on Copy_Of_Source'Tag

            Remove_Element(Copy_Of_Source, Element);

            Target := Union(Target, Unit_Set(Element));
                       -- Dispatching for Unit_Set based on Target'Tag
        end loop;
     end Convert;


   The functions Unit_Set and Empty are essentially "constructors" and
   hence must be overridden in every extension of the abstract type Set.
   However, these operations can still be called with a class-wide
   expected type, and the controlling tag for the function calls will be
   determined at run-time by the context, analogous to the kind of
   (compile-time) overload resolution that uses context to disambiguate
   enumeration literals and aggregates.


8.2: How can I redefine assignment operations?

   See "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour
   (where is this located?)


8.3: Should I stick to a one package, one type approach while writing Ada
software?

   (Robb Nebbe responds)

   Off hand I can think of a couple of advantages from separating the
   concepts of type and module in Ada.

   Separation of visibility and inheritance allows a programmer to
   isolate a derived type from the implementation details of its parent.
   To put it another way information hiding becomes a design decision
   instead of a decision that the programming language has already made
   for you.

   Another advantage that came "for free" is the distinction between
   subtyping and implementation inheritance. Since modules and types are
   independent concepts the interaction of the facilities for information
   hiding already present in Ada83 with inheritance provide an elegant
   solution to separating subtyping from implementation inheritance. (In
   my opinion more elegant than providing multiple forms of inheritance
   or two distinct language constructs.)


8.4: What is the "Beaujolais Effect"?

   The "Beaujolais Effect" is detrimental, and language designers should
   try to avoid it. But what is it?

   (from Tucker Taft)

   The term "Beaujolais Effect" comes from a prize (a bottle of
   Beaujolais) offered by Jean Ichbiah during the original Ada design
   process to anyone who could find a situation where adding or removing
   a single "use" clause could change a program from one legal
   interpretation to a different legal interpretation. (Or equivalently,
   adding or removing a single declaration from a "use"d package.)

   At least one bottle was awarded, and if the offer was still open, a
   few more might have been awarded during the Ada 9X process. However,
   thanks to some very nice analysis by the Ada 9X Language Precision
   Team (based at Odyssey Research Associates) we were able to identify
   the remaining cases of this effect in Ada 83, and remove them as part
   of the 9X process.

   The existing cases in Ada 83 had to do with implicit conversion of
   expressions of a universal type to a non-universal type. The rules in
   Ada 9X are subtly different, making any case that used to result in a
   Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.

   The Beaujolais effect is considered "harmful" because it is expected
   that during maintenance, declarations may be added or removed from
   packages without being able to do an exhaustive search for all places
   where the package is "use"d. If there were situations in the language
   which resulted in Beaujolais effects, then certain kinds of changes in
   "use"d packages might have mysterious effects in unexpected places.

   (from Jean D. Ichbiah)

   It is worth pointing that many popular languages have Beaujolais
   effect: e.g. the Borland Pascal "uses" clause, which takes an
   additive, layer-after-layer, interpretation of what you see in the
   used packages (units) definitely exhibits a Beaujolais effect.

   Last time I looked at C++, my impression was that several years of
   Beaujolais vintage productions would be required.

   For component-based software development, such effects are undesirable
   since your application may stop working when you recompile it with the
   new -- supposedly improved -- version of a component.


8.5: What about the "Ripple Effect"?

   (Tucker Taft explains)

   We have eliminated all remnants of the Beaujolais Effect, but we did
   debate various instances of the "Ripple" effect during the language
   revision process (apologies to Gallo Ripple Wine enthusiasts ;-).

   In brief, the (undesirable) Ripple effect was related to whether the
   legality of a compilation unit could be affected by adding or removing
   an otherwise unneeded "with" clause on some compilation unit on which
   the unit depended, directly or indirectly.

   This issue came up at least twice. One when we were considering rules
   relating to use of attributes like 'Address. In Ada 83 as interpreted
   by the ARG, if a compilation unit contains a use of 'Address, then
   there must be a "with" of package System somewhere in the set of
   library unit specs "with"ed by the compilation unit (directly or
   indirectly).

   In Ada 9X, we have eliminated this rule, as it was for some compilers
   an unnecessary implementation burden, and didn't really provide any
   value to the user (if anything, it created some confusion). The rule
   now is that the use of an attibute that returns a value of some
   particular type makes the compilation unit semantically dependent on
   the library unit in which the type is declared (whether or not it is
   "with"ed).

   The second place the Ripple effect came up was when we were trying to
   provide automatic direct visibility to (primitive) operators.
   Ultimately we ended up with an explicit "use type" clause for making
   operators directly visible. For a while we considered various rules
   that would make all primitive operators directly visible; some of the
   rules considered created the undesirable "Ripple" effects; others
   created annoying incompatibilities; all were quite tricky to implement
   correctly and efficiently.

     _________________________________________________________________


9: Ada and Other Programming Languages


9.1: Where can I find programs that will translate from (some language) to
Ada?

   (Job Honig)

   Probably not the answer you like to hear, but my advice would be to
   redesign the code, employing your knowledge of the current system, of
   course. I have done this twice, once for Coco, a parser generator for
   LALR left attributed grammars, and once for Flex, the well known
   scanner generator. Both attempts revealed errors in the original
   software, that were uncovered by designing the new system using the
   higher abstraction level allowed by Ada...

   So I would support your requirements analysis (transition to Ada), but
   not your proposed implementation (using a source code translator).

   (no longer Job Honig :-)

   Otherwise, it is generally advisable to simply interface to code that
   already works. Still, you may have compelling reasons to translate
   your existing source to Ada. In that case, here is a list of available
   translators:

     * Pascal to Ada:

       To see the differences in programming style, see "Ada for
       Experienced Programmers", by A. Nico Habermann and Dewayne E.
       Perry (Addison-Wesley Pub. Co., Reading, Mass., 1983). Covers Ada
       and Pascal.

     * Fortran to Ada: ???

     * COBOL to Ada: ???

     * C++ to Ada: ???

     * C to Ada: ???

     * Modula-2 to Ada:

       (from Wayne R. Lawton)
       The Idaho National Engineering Laboratory (INEL), a Dept of Energy
       Lab has a basic capability for Modula-2 to Ada-83. The tool is
       "research grade" quality, but may provide a starting point for
       what you need. This is the same group of people who brought you
       AdaSAGE. Give them a ring at (208) 526-0656. This is an answer
       desk hotline in the section that wrote the tool.

       If you are looking for commercial quality, I wish you the best of
       luck. If you just need something to perform 80% of the grunt code
       translation, I think this might meet your needs. I know of two
       systems comprising about 250,000 lines of code that were
       originally developed in Modula-2 then translated and cleaned up in
       Ada 83 after Alsys 1.0 for the PC came out back around 1987.

     * Visual Basic to Ada: NOT! :-)


9.2: How can I convert Ada 83 sources to Ada 9X?

   First you should read the following document, which will provide you
   with much useful information: "Changes to Ada -- 1987 to 1995", file
   ch83.{ps,doc}, in directory
   ftp://ajpo.sei.cmu.edu/public/ada9x/mrtcomments/rm9x/v5.95

   If you're using GNAT, the tool you are probably looking for is
   "gnatchop". In csh you could use something like this to quickly
   process existing files:

     cd dest_dir                    # The destination directory
     foreach f ( ../src_dir/*.a )   # ../src_dir is the source directory
       gnatchop $f
     end

   gnatchop will show you what sources are causing problems.


9.3: I hear that Ada is slower than Fortran or C, is that true?

   First, note that you are comparing compilers, not languages. There is
   no such thing as "fast" Ada code any more than there is "fast" C++ or
   Fortran code. Now, when comparing execution speeds on similar
   platforms, you must keep in mind the optimization levels, OS tuning,
   etc. while making the comparisons. The bottom line is that
   benchmarking, especially between two different languages, requires
   _very_ careful measurement. In general, such results should be viewed
   with caution.

   (A message from Bevin Brett of DEC)

   I have been asked to comment on the relative performance of algorithms
   coded in Ada and in Fortran.

   This question has come up repeatedly over the years, and deserves a
   complete answer, rather than a simplistic one.

   There are many factors which influence the size and execution speed of
   the running program, and they all play together to get a full answer.
   I shall then discuss an exact Ada v. Fortran comparison that Digital
   was involved in.

   First, a position statement: The variation between Ada and Fortran is
   less than the variation within the language caused by the exact
   implementation details. A person versed in the Ada issues should do as
   well in Ada as a person versed in the Fortran issues will do in
   Fortran. The size and execution speed of the result should be within a
   few percent of each other.

   (a) Differences due to the compiler

   In the case of the DEC Ada and Fortran compilers, the optimizer and
       code generator are the same. Never-the-less, the exact inputs into
       the optimizer and code generator may differ slightly when the same
       algorithm is compiled by the Ada and Fortran compilers, and this
       can result in major differences in the generated code. In these
       cases the compiler front ends can usually be modified to correct
       the slower one.

       We have not observed any major differences in generated code
       quality between the DEC Ada and DEC Fortran compilers caused by
       such issues.


   (b) Differences due to the language

   It is very important that the same algorithm be written in the two
       languages. The biggest differences we have observed are
         1. Having the wrong dimension varying fastest, since it is
            desireable to have the first dimension changing fastest in
            Fortran, and the last dimension in Ada. Thus when an
            algorithm is transliterated, the array indexes must be
            reversed.

         2. Using compile-time-known bounds for arrays in Fortran, and
            using unconstrained arrays in the Ada code. Knowing the exact
            values of the dimensions at compile-time results in much
            better code.

         3. Not suppressing all the runtime checks in Ada. The Fortran
            compiler assumes all array bounds are in range, and all
            arithmetic operations do not overflow. You must use a pragma
            Suppress to tell this to the Ada compiler as well.

         4. Don't use arrays of Ada Booleans to match arrays of Fortran
            Integers, because accessing bytes on a RISC system might be
            much worse than accessing fullwords.


   (c) Differences due to the bindings

   The biggest bindings differences are related to Fortran's built-in
       support for complex types, and for various math routines such as
       SQRT and SIN, compared with Ada code that often uses hand-coded or
       ISO standardised versions of these functions with different
       requirements than are imposed on the Fortran versions.

       DEC Ada has built-in support for complex types, and also has
       bindings directly to the same primitives that Fortran uses for its
       math routines and so gets the same performance as Fortran does.


   (d) Differences due to the author

   The use of good Ada and Fortran style can also effect the generated
       code. Provided the author writes in good Ada style, and follows
       the above guidelines, the generated code should do as well as
       Fortran.


    The Ada Performance Benchmark

   A DEC Ada customer had a Fortran benchmark that had been translated
   into Ada without awareness of the above issues, and was running
   substantially slower with DEC Ada than the original was with DEC
   Fortran.

   Bevin Brett, a DEC Ada team member, developed the above guidelines in
   the process of retranslating the code into Ada.

   Portions of this translation are shown here (a) as an illustration of
   the application of the above rules, and (b) as an illustration of the
   kind of operations that were present in the benchmark.

   The whole benchmark has not been provided to avoid possible issues of
   ownership.

   The resulting Ada benchmark components each ran within a few percent
   of their Fortran counterparts. The Ada code is available by FTP, in
   file ftp://lglftp.epfl.ch/pub/Ada/FAQ/ada-vs-fortran.ada


9.4: Isn't Ada less "elegant" than Eiffel?

   While it is true that programming-language support for "assertions"
   is an important contribution of Eiffel to software construction, this
   is not an issue of "elegance", and there are many other important
   factors to consider.

   Note also that preconditions and postconditions can be fairly easily
   and efficiently included in Ada code. Invariants seem difficult to
   emulate directly in Ada. If you're really interested in the formal use
   of assertions with Ada, maybe Anna is a solution for you.

   (Tucker Taft comments)

   I guess one thing that bothers me a little is that people are quick to
   say that Eiffel is "elegant" without really looking at it. I fear that
   such statements will become self-fulfilling prophecies, with those
   programmers interested in elegance migrating over to Eiffel rather
   than sticking with Ada.

   In particular, although I like the assertion stuff in Eiffel, I think
   the language has a number of "inelegant" aspects. For example:

    1. exception handlers only at the top level of a routine, with the
       only way to "handle" an exception being by retrying the whole
       routine.

    2. No way to return from a routine in the middle. This makes it a
       pain in the neck to search through a list for something in a loop,
       and then return immediately when you find what you want. (I have
       never found the addition of extra boolean control variable a help
       to the understanding of an algorithm.)

    3. Namespace control handled by a separate sublanguage, and no real
       higher level concept of "module" or "subsystem."

    4. An obscure notation like "!!" being used for an important and
       frequent operation (construction).

    5. No way to conveniently "use" another abstraction without
       inheriting from it.

    6. No strong distinctions between integer types used for array
       indexing.

    7. Using the same operator ":=" for both (aliasing) pointer
       assignment, and for value assignment, depending on whether the
       type is "expanded." (Simula's solution was far preferable, IMHO).

    And most critically:


    8. No separate interface for an abstraction. You can view a interface
       by running a tool, but this misses completely the importance of
       having a physical module that represents the interface, and acts
       as a contract between the specifier or user of an abstraction and
       its implementor. In Eiffel, one might not even be truly aware when
       one is changing the interface to an abstraction, because there is
       no particular physical separation between interface and
       implementation.


   I consider many of the above problems quite serious, with some of them
   being real throwbacks to the old style of programming languages where
   there were no well defined interfaces or modules.

   Hence, I cringe a bit when people say that Eiffel is the "most
   elegant" OOP and that they would use it if only it were practical to
   do so. In many ways, I think Ada is much better human-engineered than
   Eiffel, with important things like range constraints built into the
   language in a way that makes them convenient to use. Although general
   assertions are nice, they don't give you the kind of line-by-line
   consistency checks that Ada can give you.

   To summarize --
   Although Eiffel certainly has a number of nice features, I don't
   consider it ready for prime time as far as building and maintaining
   large systems with large numbers of programmers. And from a human
   engineering point of view, I think Ada is significantly better.


9.5: Are there any papers detailing the differences between Ada and C++?

   Below are two references. Bear in mind that it is difficult to make
   such a comparison without exposing biases. However, the two papers
   below are well worth reading.

   "A Comparison of the OO features of Ada9x and C++" in Springer Lecture
   Notes in CS: "Ada Europe 93" pp.125-141 (short paper, good reading,
   enlightens idioms)

   ftp ajpo.sei.cmu.edu in directory: /public/ada9x, document:
   9x_cplus.hlp



^ permalink raw reply	[relevance 2%]

Results 1-35 of 35 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
1995-01-19 18:10  2% Ada FAQ: Programming with Ada (part 2 of 3) Magnus Kempe
1995-03-21 18:10  2% Magnus Kempe
1995-04-20  0:00  2% Magnus Kempe
1996-03-17  0:00  2% Ada FAQ: Programming with Ada (part 3 of 4) Magnus Kempe
1996-04-23  0:00  2% Magnus Kempe
1996-05-30  0:00  2% Magnus Kempe
1996-07-08  0:00  4% Package parameters in generic packages Chris Papademetrious
1997-10-02  0:00  6% Is Interfaces.Fortran Mandatory or Optional? John Harbaugh
2001-01-09  8:12     Record type with check? Matthias Teege
2001-01-09 22:12     ` Stephen Leake
2001-01-10  7:47       ` Matthias Teege
2001-01-10 17:05  3%     ` Nick Roberts
2001-11-26 15:15  5% Problematic type definition in Interfaces.Fortran Jacob Sparre Andersen
2003-02-25 11:17     Container libraries Jano
2003-02-26 17:51     ` Jean-Pierre Rosen
2003-02-27 12:04       ` Preben Randhol
2003-03-01 10:02  6%     ` A question relating to package interfaces.fortran Zheng Long Gen
2009-09-27  1:08  4% Tasking for Mandelbrot program Georg Bauhaus
2010-05-20 12:53     Ada noob here! Is Ada widely used? Duke Normandin
2010-05-20 18:49     ` Gautier write-only
2010-06-05  8:04       ` Nasser M. Abbasi
2010-06-05  9:24         ` Yannick Duchêne (Hibou57)
2010-06-05 12:59           ` Dmitry A. Kazakov
2010-06-05 16:02  4%         ` Nasser M. Abbasi
2010-06-23  7:30     Inferring array index type from array object Maciej Sobczak
2010-06-23  8:01     ` Dmitry A. Kazakov
2010-06-23 12:13       ` Niklas Holsti
2010-06-23 16:33         ` Warren
2010-06-23 20:39           ` Niklas Holsti
2010-06-28 13:44             ` Warren
2010-06-28 22:18               ` Niklas Holsti
2010-06-29 16:56                 ` Warren
2010-06-29 17:50                   ` John B. Matthews
2010-06-29 19:31                     ` Warren
2010-06-29 20:28  5%                   ` Damien Carbonne
2010-07-24 11:57     Interfacing Ada with C Ada novice
2010-07-24 16:38     ` Simon Wright
2010-07-24 17:58       ` Ada novice
2010-07-25  8:29         ` Simon Wright
2010-07-25 12:21           ` Ada novice
2010-07-25 23:21             ` Simon Wright
     [not found]               ` <da987804-3948-4871-ab52-4a8e95f06d44@k39g2000yqb.googlegroups.com>
2010-07-26 19:46                 ` Simon Wright
2010-07-28 22:26  6%               ` Simon Wright
2010-08-01 10:47                     ` John B. Matthews
2010-08-01 17:08                       ` Simon Wright
2010-08-02  1:08                         ` John B. Matthews
2010-08-02 16:36                           ` Simon Wright
2010-08-02 16:55                             ` Ada novice
2010-08-05  9:14                               ` Ada novice
2010-08-05 13:23                                 ` John B. Matthews
2010-08-05 13:57                                   ` sjw
2010-08-05 17:24                                     ` Ada novice
2010-08-05 17:59                                       ` Jeffrey Carter
2010-08-05 20:25  5%                                     ` Simon Wright
2010-08-10 21:38     ANN: Ada 2005 Math Extensions, 20100810 release Simon Wright
2010-08-11 21:38  6% ` Simon Wright
2010-08-12  5:57  0%   ` Dmitry A. Kazakov
2010-08-12 19:45  5%     ` Simon Wright
2010-08-13  6:27  0%       ` Dmitry A. Kazakov
2010-08-12 10:36  0%   ` Stephen Leake
2010-08-12 12:48  7%     ` sjw
2010-08-14  1:05  7%       ` Stephen Leake
2010-08-14 16:26  0%         ` Simon Wright
2011-07-25 17:36  6% Eigenvalues to find roots of polynomials marius63
2011-07-26  6:21     ` John B. Matthews
2011-07-26 15:40  6%   ` marius63
2012-07-12  0:38  5% Lapack Ada binding matrices/vectors issue, how to best to resolve? Nasser M. Abbasi
2012-10-21  0:37  5% Is this a bug in Ada.Numerics? Leo Brewin
2012-10-21  1:54  0% ` Yannick Duchêne (Hibou57)
2012-10-21  1:55  5% ` Ludovic Brenta
2012-10-21  2:32  6%   ` Leo Brewin
2012-10-21  3:39  0%     ` Ludovic Brenta
2012-10-23 15:48  0%       ` Vincent Marciante
2012-10-23 22:52  5%       ` Leo Brewin
2018-04-06 13:22  5% Generic library design Marius Amado-Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox