!
!&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
!
!         <<<<<---------------------------------------------->>>>>
!         <<<<<  5.  A R R A Y   F E A T U R E S             >>>>>
!         <<<<<---------------------------------------------->>>>>
!
!&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

!
! ASSUMED SHAPE ARRAYS
!
! It is possible to require that ACTUAL and DUMMY array arguments 
! have the same SHAPE
!

  REAL, DIMENSION (10,10) :: A_act
  ...
  
  CALL EXEMP(A_act,...)

  END PROGRAM

  SUBROUTINE EXEMP(A_dummy,...)
  REAL, DIMENSION (:,:) A_dummy

! Attention, if lower bounds are omitted, the default value is 1
! Hence if the above example is

  REAL, DIMENSION (0:10,0:10) :: A_act
  ...
  
  CALL EXEMP(A_act,...)

  END PROGRAM

  SUBROUTINE EXEMP(A_dummy,...)
  REAL, DIMENSION (:,:) A_dummy  

! In this case the correspondence is 
!
! A_dummy(I+1) = A_actual(I), I=0,1,...
!
! so to get the natural correspondence, one should make the lower
! bound on A_dummy explicit
!

  SUBROUTINE EXEMP(A_dummy,...)
  REAL, DIMENSION (0:,0:) A_dummy  


!***********************************************************************
! AUTOMATIC OBJECTS
!***********************************************************************

! A procedure with dummy arguments that are arrays whose size varies 
! from call to call may also need arrays whose size varies. 
! Example

  SUBROUTINE SWAP(A,B)                  ! swap array A and B
      REAL, DIMENSION(:)       :: A,B   ! The arrays to be swapped
      REAL, DIMENSION(SIZE(A)) :: WORK  ! The temporary array
      WORK = A
      A = B
      B = WORK
  END SUBROUTINE SWAP

! The command SIZE(.) returns the size of an array
!
! Arrays whose extent vary in this way are called AUTOMATIC OBJECTS

! The other way an AUTMATIC object arises is with varying 
! character length 
!
! 

  SUBROUTINE EXAMPLE(WORD1)
     CHARACTER (LEN = *)           WORD1
     CHARACTER (LEN = LEN(WORD1))  WORD2


!***********************************************************************
! ALLOCATABLE  OBJECTS
!***********************************************************************      

! Often an array is required to be of a size hat is known only
! after some data have been read or some calculations performed
!
! An array that is NOT 
!
! 1. A dummy argument
! 2. A function result
!
! can be declared as ALLOCATABLE
!
! Then it can have essentially two states
!
! 1. Allocated 
! 2. Not currently allocated
!

 REAL, DIMENSION(:,:), ALLOCATABLE :: A
 
! Only its RANK is specified.
! Once the dimension is known, the array can be ALLOCATED
!
 ALLOCATE ( A(N,0:N-1) )
 
! An important example is the following 

 MODULE WORK_ARRAY
     INTEGER N
     REAL, DIMENSION(:,:), ALLOCATABLE :: WORK
 END MODULE WORK_ARRAY 
 PROGRAM MAIN 
     USE WORK_ARRAY
     ....
     READ (*,*) N
     ALLOCATE ( WORK (N,2*N) )
 END PROGRAM MAIN 

! The module WORK_ARRAY provides a temporary stirage array that 
! can be used throughout the program main and in ay subprogram that 
! have a 

  USE WORK_ARRAY

! statement
!
! Whenever an allocatable array is no longer needed it can be
! deallocated 
!
  DEALLOCATE (A)

! following which the array returns to 'not currently allocated' state
!
!
! The general form of ALLOCATE statements is 
!

 ALLOCATE ( allocation-list [, STAT=stat] )

! where allocation-list is a list of allocations of the for,
!

  allocate-objects [(array-bounds-list)]

! each array bound has the form

  [lower-bound:] upper-bound

! and stat is a scalar integer variable that must not be part of 
! an object being allocated
! When STAT is specified, stat is given
!
! 1. The value zero following successful allocation
! 2. A positive value if the allocation failed (insufficient storage, 
!    or other reasons). 
!
! Each allocate-object is an allocatable array
! Each lower-bound and each upper-bound are integer scalar expressions
! The defaul value for lower-bound is 1

! Note the the bounds of an array being allocated are regarded as 
! UNDEFINED during allocation. Hence
!

  ALLOCATE ( A(N), B( SIZE(A) )    

! is ILLEGAL
!

  ALLOCATE ( A(N) )
  ALLOCATE ( B( SIZE(A) ) ) 

! This is legal

! To recover the storage of an array that is no longer needed, the
! DEALLOCATE statement should be used
! 
! Its general use is
!

  DEALLOCATE ( allocate-object-list [,STAT=stat] ) 

! where the list of argument is obviuos. If present the STAT argument
! is given a value 1 if the deallocation was successful and a positive 
! value otherwise. If no STAT argument is present and the 
! deallocation was not successful, the program stops. This is true 
! for the ALLOCATE statement as well.


!***********************************************************************   
!  WHERE STATEMENT AND CONSTRUCT
!*********************************************************************** 

! It is often desired to perform an array opeation only for 
! certain elements, say those whose value is positive
! The WHERE statement provides this facility. A simple example is

 WHERE ( A > 0.0 ) A = 1.0/A         ! A is a real array

! This reciprocates the positive elements of A and leaves the 
! rest unaltered.

! the general form is 

 WHERE ( logical-array-expression ) array-variable = array-expr

! The logical expression 'logical-array-expression' should have 
! the same SHAPE as the variable 'array-variable'. This is evaluated 
! first and then just those elements of 'array-expr' that correspond
! to elements of  'logical-array-expression' that have the value 
! .TRUE. are evaluated and assigned to the corresponding elemenst 
! of 'array-variable'

! If one wants a sequence (block) of array expression to be evaluated 
! for a given logical array condition, a WHERE block can be used

 WHERE ( logical-array-expression ) 
     array-assignments
 END WHERE    

! More generally, the array construct may also take the form 

 WHERE ( logical-array-expression ) 
     array-assignments1
 ELSEWHERE 
     array-assignments2
 END WHERE

! Here the first series of array assignments are evaluated under 
! the control of the first logical mask 
!
! logical-array-expression = .TRUE. 
!
! while the second set of array expressions are evaluated under the 
! control of 
!
! .NOT. logical-array-expression = .TRUE. 
!
! EXAMPLE
!

  WHERE ( PRESSURE <= 1.0 ) 
      PRESSURE = PRESSURE + DELTA_PRESSURE
      TEMP     = TEMP + 5.0
  ELSEWHERE 
      RAINING = .TRUE.
  END WHERE 

!  
! Here PRESSURE, DELTA_PRESSURE, TEMP and RAINING are all arrays
! of the same shape.
!
! A Note in passing
!
! Of course the same effect could be achieved by the following 
! code ( N = size of the arrays )
!

   DO I=1, N
      IF ( PRESSURE(I) .LE. 1.0 ) THEN 
         PRESSURE(I) = PRESSURE(I) + DELTA_PRESSURE(I)
         TEMP(I)     = TEMP(I)
    ELSE
         RAINING(I) = .TRUE.  
      END IF
   END DO

!
! It is assumed that the NATIVE construct WHERE is built-in and 
! hence it is optimized directly at the compiler level. That is 
! why it is preferable to use it (especially for large arrays).
! Plus array operations afforded by FORTRAN 90 afford much 
! simpler constructs, more readability of the code and are therefore
! preferable also for these reasons.

     
            
 
!***********************************************************************
!  ARRAY SUB-OBJECTS
!***********************************************************************

! Fortran 90 provides a convenient way to access sub-arrays, such 
! as rows and columns or portions of them 

! Example 

  REAL, DIMENSION(10,10) :: A

  A(I, 1:N)    ! Elements 1 to N of row I
  A(1:M, J)    ! Elements 1 to N of column J

! Bounds can be omitted when the corresponding bound of the array is
! wanted

! Example 

 A(I,:)   ! The whole of row I 

! Strides other than one may be specified. 

! Example 

 A(I, 1:10:2)   ! Elements 1,3,5,7,9 of column I 
!     ^  ^ ^
!     |  | |
!     |  | stride
!     |  | 
!     |  upper bound 
!     | 
!     lower bound
!          

! NEGATIVE strides are allowed so that array elements can be 
! ================
! taken in reverse order 
!
! Example 

 REAL, DIMENSION(10) :: V_EXTR
 
 V_EXTR = A (I, 10:1:-1 )   

! V_EXTR(1) = A(I,10)
! V_EXTR(2) = A(I,9)
! ................

!=======================================================================
! VECTOR SUB-SCRIPTS
!=======================================================================
!
! One can specify a specific set of indexes through a "vector subscript"
!
! For example 

 REAL, DIMENSION(100) :: V

 V( (/ 1, 23, 54, 55, 67, 90 /) )

! this is a 6-component vector, whose components are V(1), V(23), V(54),
! V(55), V(67) and V(90). It may be assigned to another vector
! for example though a statement like the following 
!

 REAL, DIMENSION(100) :: V
 REAL, DIMENSION(6)   :: V_SUB  

 V_SUB = V( (/ 1, 23, 54, 55, 67, 90 /) ) 
   
! By doing this, one has the obvious correspondence:
!
!  V_SUB(1) = V(1)
!  V_SUB(2) = V(23)
!  V_SUB(3) = V(54)
!  ...............  

