MODULE GenerateTestVectors

USE set_precision, ONLY : sp=>skind, dp => dkind
USE randMod, ONLY : rand=>dpPortRand, randomInt
USE oracle, ONLY : vecOracle
USE quicksort, ONLY : qsort
USE, INTRINSIC :: iso_fortran_env, ONLY: output_unit

! Global variables used to provide range information 
! for user defined data values.
! Allocatable arrays used for multiple interval data
      INTEGER, ALLOCATABLE :: spUsrMinExp(:), spUsrMaxExp(:)
      INTEGER, ALLOCATABLE :: dpUsrMinExp(:), dpUsrMaxExp(:)

! All the following generic routines have, as their first
! argument, a vector (either single or double precision),
! x, which is returned filled with values. The actual values
! returned may be dependent on the routine called and the values
! of any extra arguments.
!
! RandVec: random values in the range (0,1)
!
! UserDefRandVec: random values in the range (low, high)
!    where low and high are user provided input arguments 
!    of the same type as x.
!
! GenTestVec: random values are of the form a*2^r where a is
!    a random value in the range (1,2) and r is a random integer
!    in the range [minExp,maxExp].
!
!    minExp and maxExp may be:
!
!    (a) simple integers in which case all the elements of a
!        are in the same range, or
!    
!    (b) a pair of integer arrays of the same length,
!        nRanges. In this case the vector is divided into nRanges
!        sections; the length of the first (nRanges-1) sections
!        is nsec = floor(size(x)/nRanges) and the length of the
!        last (nRanges) section is size(x)-(nRanges-1)*nsec.
!        The defined intervals do not need to be disjoint
!        and no checks for any overlap are applied

!    Also , no checks are performed on the values provided for
!    minExp and maxExp; values which would cause an overflow
!    to occur in the chosen precision are allowed and will
!    probably cause the computation to fail.
!
! GenNrm2dataVec: takes two additional arguments,
!    descData which selects a set form of data to be
!    assigned to x and signElts which determines the
!    signs pattern of the elements. Details of the choices
!    available are given in the manual.

INTERFACE FixSigns
  MODULE PROCEDURE dpFixSigns, spFixSigns
END INTERFACE FixSigns

INTERFACE RandVec
  MODULE PROCEDURE dpRandVec, spRandVec
END INTERFACE RandVec

INTERFACE UserDefRandVec
  MODULE PROCEDURE dpUserDefRandVec, spUserDefRandVec
END INTERFACE UserDefRandVec

INTERFACE GenTestVec
  MODULE PROCEDURE dpGenTestVec, spGenTestVec
  MODULE PROCEDURE dpGenMultIntTestVec, spGenMultIntTestVec
END INTERFACE GenTestVec

INTERFACE GenNrm2DataVec
  MODULE PROCEDURE spGenRealNrm2Vec, dpGenRealNrm2Vec
  MODULE PROCEDURE spGenComplexNrm2Vec, dpGenComplexNrm2Vec
END INTERFACE GenNrm2DataVec

! Define the parameter values used to identify each possible
! type of random data available signified by the value of
! dataDesc.
!
! All descriptions refer to the absolute value of the elements
! to be generated. The signs of the elements are controlled
! by signElts.
!
! zeroOneRandom:: random elements in the range (0,1)
! zeroOneAscending:: random elements in the range (0,1) sorted
!                    into ascending order
! zeroOneDescending:: random elements in the range (0,1) sorted
!                    into decending order
! userDefRandom:: random elements in the range a*2^r where a
!                 is a random value in the range (1,2)
!                 and r is a random integer in the range
!                 [minExp, maxExp] where minExp and
!                 maxExp are set by the user.
! userDefAscending:: random elements in the range a*2^r where a
!                 is a random value in the range (1,2)
!                 and r is a random integer in the range
!                 [minExp, maxExp] where minExp and
!                 maxExp are set by the user and sorted into
!                 ascending order.
! userDefDescending:: random elements in the range a*2^r where a
!                 is a random value in the range (1,2)
!                 and r is a random integer in the range
!                 [minExp, maxExp] where minExp and
!                 maxExp are set by the user and sorted into
!                 decending order.
! simpleAscending:: x(i) = 1 + (i-1)/size(x) i = 1, ..., size(x)
! simpleDescending:: x(i) = 1 + (n-i)/size(x) i = 1, ..., size(x)
! spuriousUflow:: many versions will cause harmless underflows
!                 as it processes and scales elements.
! noOflow:: random elements using as full a range of absolute
!           values as possible in order to be guaranteed
!           that the Euclidean norm of the result and vector
!           will be representable.
! aroundOne:: random elements around one; no overflows or
!             underflows to worry about
! reallySmall:: All values are very small although the interval
!               used does span the low and middle ranges of
!               both Blue and Kahan's algorithms.
! normGradUflow:: All elements are denormalized numbers
! fullExpRange:: random values over the full floating point
!                range; overflows are therefore possible 
! lowHybridScale:: data vectors that will cause the hybrid
!                  method to call the Kahan routine (for
!                  scaling) due to the underflow flag
!                  signalling.
! highHybridScale:: data vectors that will cause the hybrid
!                  method to call the Kahan routine (for
!                  scaling) due to the overflow flag
!                  signalling.
! bothHybridScale:: data vectors that will cause the hybrid
!                  method to call the Kahan routine (for
!                  scaling) due to the overflow and underflow
!                  flags signalling.
! KahanComparison:: data vectors that will cause the original
!                  Kahan method to waste the most arithmetic
!                  operations before scaling.

INTEGER, PARAMETER:: zeroOneRandom=1, zeroOneAscending=2, &
                     zeroOneDescending=3, &
                     userDefRandom=4, userDefAscending=5, &
                     userDefDescending=6, &
                     simpleAscending = 7, simpleDescending = 8, &
                     spuriousUflow = 9, &
                     noOflow = 10, aroundOne = 11, &
                     reallySmall = 12, normGradUflow = 13, &
                     fullExpRange = 14, lowHybridScale = 15, &
                     highHybridScale = 16, bothHybridScale = 17, &
                     KahanComparison = 18

! signElts determines what signs the data elements take
! Three examples are given more may be added as necessary
!  noChangeSigns :; leave element signs as they are
!  randomSigns :: sign of each element is decided on randomly
!  allPosSigns :: all elements are set positive
!  allNegSigns :: all elements are set negative
INTEGER, PARAMETER :: noChangeSigns = 0, randomSigns = 1, &
                      allPosSigns = 2,  allNegSigns = 3

! Exponents used to generate intervals for the defined
! types of data vectors
INTEGER, PARAMETER :: dpExpMin=-1074, dpExpMax=1023,&
                    dpExpMinOne=-5, dpExpMaxOne=5, &
                    dpExpSmallRange=-400, dpExpLeastNormal=-1022, &
                    dpLowScale=-513, dpHighScale=513, &
                    dpScaleRange=10
INTEGER, PARAMETER :: spExpMin=-149, spExpmax=127,&
                    spExpMinOne=-5, spExpMaxOne=5, &
                    spExpSmallRange=-35, spExpLeastNormal=-126, &
                    spLowScale=-65, spHighScale=65, &
                    spScaleRange=10

REAL(sp) :: spOne = 1.0E0_sp, spHalf = 0.5E0_sp
REAL(dp) :: dpOne = 1.0E0_dp, dpHalf = 0.5E0_dp

CONTAINS

  SUBROUTINE dpRandVec(x)
  REAL(KIND=dp), INTENT(OUT) :: x(:)
  INTEGER :: i
! Fill a dp vector x (assumed shape) with double precision
! random numbers in the range (0,1).
  DO i = 1, SIZE(x)
    x(i) = rand()
  END DO
  END SUBROUTINE dpRandVec

  SUBROUTINE spRandVec(x)
  REAL(KIND=sp), INTENT(OUT) :: x(:)
! Fill a sp vector x (assumed shape) with single precision
! random numbers in the range (0,1).
  DO i = 1, SIZE(x)
    x(i) = REAL(rand(),KIND=sp)
  END DO
  END SUBROUTINE spRandVec

  SUBROUTINE spGenTestVec(x, minExp, maxExp)
  REAL(KIND=sp), INTENT(OUT) :: x(:)
  INTEGER, INTENT(IN) :: minExp, maxExp
  INTEGER :: i

! Generate a vector of n=SIZE(x) elements whose absolute values
! are all in the range 
! (2^minExp, (1 + 1-epsilon))*2^maxExp]
! We start with a vector of n values that are in the range (0,1)
! and transform it. We use the intrinsic function SCALE to do
! this where the value scaled is a random number in the range
! (1,2)
  CALL RandVec(x)
  x(:) = x(:) + spOne

  DO i = 1, SIZE(x)
    x(i) = SCALE(x(i), randomInt(minExp,maxExp))
  END DO

  END SUBROUTINE spGenTestVec

  SUBROUTINE dpGenTestVec(x, minExp, maxExp)
  REAL(KIND=dp), INTENT(OUT) :: x(:)
  INTEGER, INTENT(IN) :: minExp, maxExp
  INTEGER :: i

! Generate a vector of n=SIZE(x) elements whose absolute values
! are all in the range 
! (2^minExp, (1 + 1-epsilon))*2^maxExp]
! We start with a vector of n values that are in the range (0,1)
! and transform it. We use the intrinsic function SCALE to do
! this where the value scaled is a random number in the range
! (1,2)
  CALL RandVec(x)
  x(:) = x(:) + dpOne

  DO i = 1, SIZE(x)
    x(i) = SCALE(x(i), randomInt(minExp,maxExp))
  END DO

  END SUBROUTINE dpGenTestVec

  SUBROUTINE spGenMultIntTestVec(x, minExp, maxExp)
  REAL(KIND=sp), INTENT(OUT) :: x(:)
  INTEGER, INTENT(IN) :: minExp(:), maxExp(:)
  INTEGER :: i, nints, n, nperint, nst

  nints = SIZE(minExp)
  n = SIZE(x)
  nperint = FLOOR(REAL(n/nints,KIND=sp))
  nst = 1

  DO i = 1, nints-1
    CALL GenTestVec(x(nst:nst+nperint-1), minExp(i), maxExp(i))
    nst = nst+ nperint
  END DO

  CALL GenTestVec(x(nst:), minExp(nints), maxExp(nints))

  END SUBROUTINE spGenMultIntTestVec

  SUBROUTINE dpGenMultIntTestVec(x, minExp, maxExp)
  REAL(KIND=dp), INTENT(OUT) :: x(:)
  INTEGER, INTENT(IN) :: minExp(:), maxExp(:)
  INTEGER :: i, nints, n, nperint, nst

  nints = SIZE(minExp)
  n = SIZE(x)
  nperint = FLOOR(REAL(n/nints,KIND=dp))
  nst = 1

  DO i = 1, nints-1
    CALL GenTestVec(x(nst:nst+nperint-1), minExp(i), maxExp(i))
    nst = nst+ nperint
  END DO

  CALL GenTestVec(x(nst:), minExp(nints), maxExp(nints))

  END SUBROUTINE dpGenMultIntTestVec


  SUBROUTINE dpUserDefRandVec(x, low, high)
  REAL(KIND=dp), INTENT(OUT) :: x(:)
  REAL(KIND=dp), INTENT(IN) :: low, high
! Generate a test vector, x (assumed shape) containing
! random numbers in the range (low,high)
! 
! Currently just uses a simple linear transformation
  CALL RandVec(x)

  x(:) = (high - low)*x(:) + low

  END SUBROUTINE dpUserDefRandVec

  SUBROUTINE spUserDefRandVec(x, low, high)
  REAL(KIND=sp), INTENT(OUT) :: x(:)
  REAL(KIND=sp), INTENT(IN) :: low, high
! Generate a test vector, x (assumed shape) containing
! random numbers in the range (low,high)
! 
! Currently just uses a simple linear transformation
  CALL RandVec(x)

  x(:) = (high - low)*x(:) + low
  END SUBROUTINE spUserDefRandVec

      SUBROUTINE spGenRealNrm2Vec(x, dataDesc, signElts)
      REAL(sp), INTENT(OUT) :: x(:)
      INTEGER, INTENT(IN) :: dataDesc, signElts

      INTEGER :: i, j, sizeX
      REAL(sp) :: temp

      sizeX = SIZE(x)

      SELECT CASE (dataDesc)

      CASE (zeroOneRandom)
        CALL RandVec(x)

      CASE (zeroOneAscending)
        CALL RandVec(x)
        CALL qsort(x, 1, sizeX)

      CASE (zeroOneDescending)
        CALL RandVec(x)
        CALL qsort(x, 1, sizeX)
        j = sizeX
        DO i = 1, sizeX/2
          temp = x(i)
          x(i) = x(j)
          x(j) = temp
          j = j - 1
        END DO

      CASE (userDefRandom)
        CALL GenTestVec(x, spUsrMinExp, spUsrMaxExp)

      CASE (userDefAscending)
        CALL GenTestVec(x,spUsrMinExp, spUsrMaxExp)
        CALL qsort(x, 1, sizeX)

      CASE (userDefDescending)
        CALL GenTestVec(x,spUsrMinExp, spUsrMaxExp)
        CALL qsort(x, 1, sizeX)
        j = sizeX
        DO i = 1, sizeX/2
          temp = x(i)
          x(i) = x(j)
          x(j) = temp
          j = j - 1
        END DO

      CASE (simpleAscending)
        DO i = 1, sizeX
          x(i) = spOne + REAL(i-1, KIND=sp)/REAL(sizeX, KIND=sp)
        END DO

      CASE (simpleDescending)
        DO i = 1, sizeX
          x(i) = spOne + REAL(sizeX-i, KIND=sp)/REAL(sizeX, KIND=sp)
        END DO

      CASE (spuriousUflow)
        IF (sizeX > 2) THEN
          x(1) = SCALE(spOne,maxexponent(spOne)-1)
          x(2) = SCALE(spOne,maxexponent(spOne)-digits(spOne)-1)
          CALL GenTestVec(x(3:sizeX),spExpMin, -1)
        END IF

      CASE (noOflow)
        CALL GenTestVec(x, spExpMin, spExpMax-log2int(size(x))-2)

      CASE (aroundOne)
        CALL GenTestVec(x, spExpMinOne, spExpMaxOne)

      CASE (reallysmall)
        CALL GenTestVec(x, spExpMin, spExpSmallRange)

      CASE (normGradUflow)
        CALL GenTestVec(x, spExpMin, &
                        spExpLeastNormal-log2int(sizeX)-2)

      CASE (fullExpRange)
        CALL GenTestVec(x, spExpMin, spExpMax)

! The next three data definitions are constructed so that the 
! hybrid method will signal either an overflow or an underflow
! (or both) resulting in a call to Kahan's routine to use
! scaling. We do this by placing a value that will cause
! signalling in element 1 for underflow and overflow only and
! in elements 1 and 2 for both under and overflow.
!
! We could use just random values in the range (0,1) for the
! elements 2 to n but we choose to use values that are in the
! low or high ranges (or both) to provide greater code coverage.
      CASE (lowHybridScale)
        CALL GenTestVec(x(2:), spLowScale-spScaleRange, &
                        spLowScale)
        x(1) = SCALE(spOne, spLowScale-spScaleRange)

      CASE (highHybridScale)
        CALL GenTestVec(x(2:), spHighScale, &
                        spHighScale+spScaleRange)
        x(1) = SCALE(spOne, spHighScale+spScaleRange)

      CASE (bothHybridScale)
        CALL GenTestVec(x(3:), &
                        [spLowScale-spScaleRange,spHighScale], &
                        [spLowScale,spHighScale+spScaleRange])
        x(1) = SCALE(spOne, spLowScale-spScaleRange)
        x(2) = SCALE(spOne, spHighScale+spScaleRange)

      CASE (KahanComparison)
        CALL RandVec(x(1:sizeX-1))
        x(sizeX) = SCALE(spOne, spHighScale+spScaleRange)

      END SELECT

! Set signs as requested via signElts
      CALL FixSigns(x,signElts)

      CALL VecOracle(x)

      END SUBROUTINE spGenRealNrm2Vec

      SUBROUTINE spGenComplexNrm2Vec(x, dataDesc, signElts)
      COMPLEX(sp), INTENT(OUT) :: x(:)
      INTEGER, INTENT(IN) :: dataDesc, signElts
      REAL(sp), ALLOCATABLE :: y(:)
      INTEGER :: sizeX, i

      sizeX = SIZE(x)

      ALLOCATE(y(2*sizeX))

      CALL spGenRealNrm2Vec(y(1:2*sizeX), dataDesc, signElts)
      DO i = 1, sizeX
        x(i) = CMPLX(y(2*i-1), y(2*i), KIND=sp)
      END DO

      DEALLOCATE(y)

      END SUBROUTINE spGenComplexNrm2Vec

      SUBROUTINE dpGenRealNrm2Vec(x, dataDesc, signElts)
      REAL(dp), INTENT(OUT) :: x(:)
      INTEGER, INTENT(IN) :: dataDesc, signElts
! Generate all allowable types of data vectors
      INTEGER :: sizeX, i, j
      REAL(dp) :: temp

      sizeX = SIZE(x)

      SELECT CASE (dataDesc)

      CASE (zeroOneRandom)
        CALL RandVec(x)

      CASE (zeroOneAscending)
        CALL RandVec(x)
        CALL qsort(x, 1, sizeX)

      CASE (zeroOneDescending)
        CALL RandVec(x)
        CALL qsort(x, 1, sizeX)
        j = sizeX
        DO i = 1, sizeX/2
          temp = x(i)
          x(i) = x(j)
          x(j) = temp
          j = j - 1
        END DO

      CASE (userDefRandom)
        CALL GenTestVec(x, dpUsrMinExp, dpUsrMaxExp)

      CASE (userDefAscending)
        CALL GenTestVec(x, dpUsrMinExp, dpUsrMaxExp)
        CALL qsort(x, 1, sizeX)

      CASE (userDefDescending)
        CALL GenTestVec(x, dpUsrMinExp, dpUsrMaxExp)
        CALL qsort(x, 1, sizeX)
        j = sizeX
        DO i = 1, sizeX/2
          temp = x(i)
          x(i) = x(j)
          x(j) = temp
          j = j - 1
        END DO

      CASE (simpleAscending)
        DO i = 1, sizeX
          x(i) = dpOne + REAL(i-1, KIND=dp)/REAL(sizeX, KIND=dp)
        END DO

      CASE (simpleDescending)
        DO i = 1, sizeX
          x(i) = dpOne + REAL(sizeX-i, KIND=dp)/REAL(sizeX, KIND=dp)
        END DO

      CASE (spuriousUflow)
        IF (sizeX > 2) THEN
          x(1) = SCALE(dpOne,maxexponent(dpOne)-1)
          x(2) = SCALE(dpOne,maxexponent(dpOne)-digits(dpOne)-1)
          CALL GenTestVec(x(3:sizeX),dpExpMin, -1)
        END IF

      CASE (noOflow)
        CALL GenTestVec(x, dpExpMin, dpExpMax-log2int(size(x)-2))

      CASE (aroundOne)
        CALL GenTestVec(x, dpExpMinOne, dpExpMaxOne)

      CASE (reallysmall)
        CALL GenTestVec(x, dpExpMin, dpExpSmallRange)

      CASE (normGradUflow)
        CALL GenTestVec(x, dpExpMin, &
                        dpExpLeastNormal-log2int(sizeX)-2)

      CASE (fullExpRange)
        CALL GenTestVec(x, dpExpMin, dpExpMax)

! The next three data definitions are constructed so that the 
! hybrid method will signal either an overflow or an underflow
! (or both) resulting in a call to Kahan's routine to use
! scaling. We do this by placing a value that will cause
! signalling in element 1 for underflow and overflow only and
! in elements 1 and 2 for both under and overflow.
!
! We could use just random values in the range (0,1) for the
! elements 2 to n but we choose to use values that are in the
! low or high ranges (or both) to provide greater code coverage.
      CASE (lowHybridScale)
        CALL GenTestVec(x(2:), dpLowScale-dpScaleRange, &
                        dpLowScale)
        x(1) = SCALE(dpOne, dpLowScale-dpScaleRange)

      CASE (highHybridScale)
        CALL GenTestVec(x(2:), dpHighScale, &
                        dpHighScale+dpScaleRange)
        x(1) = SCALE(dpOne, dpHighScale+dpScaleRange)

      CASE (bothHybridScale)
        CALL GenTestVec(x(3:), &
                        [dpLowScale-dpScaleRange,dpHighScale], &
                        [dpLowScale,dpHighScale+dpScaleRange])
        x(1) = SCALE(dpOne, dpLowScale-dpScaleRange)
        x(2) = SCALE(dpOne, dpHighScale+dpScaleRange)

      CASE (KahanComparison)
        CALL RandVec(x(1:sizeX-1))
        x(sizeX) = SCALE(dpOne, dpHighScale+dpScaleRange)

      END SELECT

! Set signs as requested via signElts
      CALL FixSigns(x,signElts)

      CALL VecOracle(x)

      END SUBROUTINE dpGenRealNrm2Vec

      SUBROUTINE dpGenComplexNrm2Vec(x, dataDesc, signElts)
      COMPLEX(dp), INTENT(OUT) :: x(:)
      INTEGER, INTENT(IN) :: dataDesc, signElts
      REAL(dp), ALLOCATABLE :: y(:)
      INTEGER :: sizeX, i

      sizeX = SIZE(x)

      ALLOCATE(y(2*sizeX))

      CALL  dpGenRealNrm2Vec(y(1:2*sizeX), dataDesc, signElts)
      DO i = 1, sizeX
        x(i) = CMPLX(y(2*i-1), y(2*i), KIND=dp)
      END DO

      DEALLOCATE(y)

      END SUBROUTINE dpGenComplexNrm2Vec

      SUBROUTINE dpFixSigns(x, signElts)
      REAL(dp), INTENT(INOUT) :: x(:)
      INTEGER, INTENT(IN) :: signElts
! Set the signs of a general data vector x.
      INTEGER :: i

      SELECT CASE (signElts)

      CASE(noChangeSigns)
! Nothing to do
        CONTINUE

      CASE(randomSigns)
! Generate random number and set negative if value
! is <= half.
        DO i = 1, SIZE(x)
          IF (rand() <= dpHalf) THEN
            x(i) = -ABS(x(i))
          ELSE
            x(i) = ABS(x(i))
          END IF
        END DO

      CASE(allPosSigns)
        DO i = 1, SIZE(x)
          x(i) = ABS(x(i))
        END DO

      CASE(allNegSigns)
        DO i = 1, SIZE(x)
          x(i) = -ABS(x(i))
        END DO

      END SELECT

      END SUBROUTINE dpFixSigns

      SUBROUTINE spFixSigns(x, signElts)
      REAL(sp), INTENT(INOUT) :: x(:)
      INTEGER, INTENT(IN) :: signElts
! Set the signs of a general data vector x.
      INTEGER :: i

      SELECT CASE (signElts)

      CASE(noChangeSigns)
! Nothing to do
        CONTINUE

      CASE(randomSigns)
! Generate random number and set negative if value
! is <= half.
        DO i = 1, SIZE(x)
          IF (rand() <= spHalf) THEN
            x(i) = -ABS(x(i))
          ELSE
            x(i) = ABS(x(i))
          END IF
        END DO

      CASE(allPosSigns)
        DO i = 1, SIZE(x)
          x(i) = ABS(x(i))
        END DO

      CASE(allNegSigns)
        DO i = 1, SIZE(x)
          x(i) = -ABS(x(i))
        END DO

      END SELECT

      END SUBROUTINE spFixSigns
      
      FUNCTION log2int(n) RESULT(res)
! Return the largest value res such that 2**res < ABS(n).
! This is equivalent to the number of bits after the leading
! zeros have been removed (unless n is negative which it isn't)

      INTEGER, INTENT(IN) :: n
      INTEGER :: res
! leadz(i) is a 2008 standard intrinsic that returns the number of
! leading zero bits in i
      res = DIGITS(ABS(n)) - LEADZ(ABS(n))
! If your compiler isn't 2008 compatible then the above statement
! may be replaced by:
! 2003 compatible:
!     INTEGER :: i
!     res = 0
!     i = ABS(n)
!     DO WHILE (i>0)
!       i = SHIFTR(i,1)
!       res = res + 1
!     END DO
!
! or pre-2003:
!     INTEGER :: i
!     res = 0
!     i = ABS(n)
!     DO WHILE (i>0)
!       i = i/2
!       res = res + 1
!     END DO
     
      END FUNCTION log2int

!
! Routines to generate descriptions of the data option and sign
! choices currently being used. These are useful to help
! annotate results files when using the data file option to
! change the default characteristics. All output on stdout.
      
      SUBROUTINE outputVecHeading(dataDesc)
      INTEGER, INTENT(IN) :: dataDesc

      SELECT CASE (dataDesc)

      CASE (zeroOneRandom)
        CALL outputVecOption('random in (0,1)')

      CASE (zeroOneAscending)
        CALL outputVecOption('random in (0,1); ascending order')

      CASE (zeroOneDescending)
        CALL outputVecOption('random in (0,1); decending order')

      CASE (userDefRandom)
        CALL outputVecOption('random user defined range')

      CASE (userDefAscending)
        CALL outputVecOption('random user defined range; ascending order')

      CASE (userDefDescending)
        CALL outputVecOption('random user defined range; decending order')

      CASE (simpleAscending)
        CALL outputVecOption('simple monotone increasing')

      CASE (simpleDescending)
        CALL outputVecOption('simple monotone decreasing')

      CASE (spuriousUflow)
        CALL outputVecOption('random (after first two elements) chosen to cause spurious but harmless underflows')

      CASE (noOflow)
        CALL outputVecOption('random; result guaranteed not to overflow')

      CASE (aroundOne)
        CALL outputVecOption('random; small range around one')

      CASE (reallysmall)
        CALL outputVecOption('random; very small')

      CASE (normGradUflow)
        CALL outputVecOption('random denormalised values')

      CASE (fullExpRange)
        CALL outputVecOption('random over full exponent range')

      CASE (lowHybridScale)
        CALL outputVecOption('hybrid, simple loop underflow,signals all values in low range')

      CASE (highHybridScale)
        CALL outputVecOption('hybrid, simple loop overflow signals, all values in low range')

      CASE (bothHybridScale)
        CALL outputVecOption('hybrid, simple loop underflow and overflow signal, all values in low or high ranges')

      CASE (KahanComparison)
        CALL outputVecOption('designed to cause the original Kahan algorithm most work before scaling')

      END SELECT

    END SUBROUTINE outputVecHeading

    SUBROUTINE outputVecOption(descript)
    CHARACTER(LEN=*), INTENT(IN) :: descript

    WRITE(output_unit, '(2a)') 'Data vector option used:  ', descript

    END SUBROUTINE outputVecOption

    SUBROUTINE outputRanges(prec,minExp, maxExp)
    CHARACTER(LEN=1) :: prec
    INTEGER, INTENT(IN) :: minExp(:), maxExp(:)
    INTEGER :: i

    IF (prec == 'D' .OR. prec == 'd') THEN
      WRITE(output_unit, '(a)') 'Double precision'
    ELSE IF (prec == 'S' .OR. prec == 's') THEN
      WRITE(output_unit, '(a)') 'Single precision'
    END IF
      
    IF (SIZE(minExp) == 1) THEN
      WRITE(output_unit, '(2(a,i0),a)') &
           &'Single data range defined as: (2^', &
            & minExp(1),', 2^',maxExp(1),')'
    ELSE
      WRITE(output_unit, '(a)') 'Multiple data ranges defined:'
      DO i = 1, SIZE(minExp)
        WRITE(output_unit, '(3(a,i0),a)')'Range ',i,': (2^', &
              & minExp(i),', 2^',maxExp(i),')'
      END DO
    END IF

    END SUBROUTINE outputRanges

    SUBROUTINE outputSignDesc(signElts)
    INTEGER, INTENT(IN) :: signElts
!
! Output details of the sign opton chosen
!
    SELECT CASE (signElts)

    CASE(noChangeSigns)
      WRITE(output_unit, '(a)') 'Signs of elements left unchanged'

    CASE(randomSigns)
      WRITE(output_unit, '(a)') 'Random sign given to each element'

    CASE(allPosSigns)
      WRITE(output_unit, '(a)') 'All signs set positive'

    CASE(allNegSigns)
      WRITE(output_unit, '(a)') 'All signs set negative'

    END SELECT

    END SUBROUTINE outputSignDesc


END MODULE GenerateTestVectors
