!    Implementation of the IEEE_ARITHMETIC standard intrinsic module
!    Copyright (C) 2013-2015 Free Software Foundation, Inc.
!    Contributed by Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
! 
! This file is part of the GNU Fortran runtime library (libgfortran).
! 
! Libgfortran is free software; you can redistribute it and/or
! modify it under the terms of the GNU General Public
! License as published by the Free Software Foundation; either
! version 3 of the License, or (at your option) any later version.
! 
! Libgfortran 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.  See the
! GNU General Public License for more details.
! 
! Under Section 7 of GPL version 3, you are granted additional
! permissions described in the GCC Runtime Library Exception, version
! 3.1, as published by the Free Software Foundation.
! 
! You should have received a copy of the GNU General Public License and
! a copy of the GCC Runtime Library Exception along with this program;
! see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
! <http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "kinds.inc"
#include "c99_protos.inc"
#include "fpu-target.inc"

module IEEE_ARITHMETIC

  use IEEE_EXCEPTIONS
  implicit none
  private

  ! Every public symbol from IEEE_EXCEPTIONS must be made public here
  public :: IEEE_FLAG_TYPE, IEEE_INVALID, IEEE_OVERFLOW, &
    IEEE_DIVIDE_BY_ZERO, IEEE_UNDERFLOW, IEEE_INEXACT, IEEE_USUAL, &
    IEEE_ALL, IEEE_STATUS_TYPE, IEEE_GET_FLAG, IEEE_GET_HALTING_MODE, &
    IEEE_GET_STATUS, IEEE_SET_FLAG, IEEE_SET_HALTING_MODE, &
    IEEE_SET_STATUS, IEEE_SUPPORT_FLAG, IEEE_SUPPORT_HALTING

  ! Derived types and named constants

  type, public :: IEEE_CLASS_TYPE
    private
    integer :: hidden
  end type

  type(IEEE_CLASS_TYPE), parameter, public :: &
    IEEE_OTHER_VALUE       = IEEE_CLASS_TYPE(0), &
    IEEE_SIGNALING_NAN     = IEEE_CLASS_TYPE(1), &
    IEEE_QUIET_NAN         = IEEE_CLASS_TYPE(2), &
    IEEE_NEGATIVE_INF      = IEEE_CLASS_TYPE(3), &
    IEEE_NEGATIVE_NORMAL   = IEEE_CLASS_TYPE(4), &
    IEEE_NEGATIVE_DENORMAL = IEEE_CLASS_TYPE(5), &
    IEEE_NEGATIVE_ZERO     = IEEE_CLASS_TYPE(6), &
    IEEE_POSITIVE_ZERO     = IEEE_CLASS_TYPE(7), &
    IEEE_POSITIVE_DENORMAL = IEEE_CLASS_TYPE(8), &
    IEEE_POSITIVE_NORMAL   = IEEE_CLASS_TYPE(9), &
    IEEE_POSITIVE_INF      = IEEE_CLASS_TYPE(10)

  type, public :: IEEE_ROUND_TYPE
    private
    integer :: hidden
  end type

  type(IEEE_ROUND_TYPE), parameter, public :: &
    IEEE_NEAREST           = IEEE_ROUND_TYPE(GFC_FPE_TONEAREST), &
    IEEE_TO_ZERO           = IEEE_ROUND_TYPE(GFC_FPE_TOWARDZERO), &
    IEEE_UP                = IEEE_ROUND_TYPE(GFC_FPE_UPWARD), &
    IEEE_DOWN              = IEEE_ROUND_TYPE(GFC_FPE_DOWNWARD), &
    IEEE_OTHER             = IEEE_ROUND_TYPE(0)


  ! Equality operators on the derived types
  interface operator (==)
    module procedure IEEE_CLASS_TYPE_EQ, IEEE_ROUND_TYPE_EQ
  end interface
  public :: operator(==)

  interface operator (/=)
    module procedure IEEE_CLASS_TYPE_NE, IEEE_ROUND_TYPE_NE
  end interface
  public :: operator (/=)


  ! IEEE_IS_FINITE

  interface
    elemental logical function _gfortran_ieee_is_finite_4(X)
      real(kind=4), intent(in) :: X
    end function
    elemental logical function _gfortran_ieee_is_finite_8(X)
      real(kind=8), intent(in) :: X
    end function
  end interface

  interface IEEE_IS_FINITE
    procedure _gfortran_ieee_is_finite_4, _gfortran_ieee_is_finite_8
  end interface
  public :: IEEE_IS_FINITE

  ! IEEE_IS_NAN

  interface
    elemental logical function _gfortran_ieee_is_nan_4(X)
      real(kind=4), intent(in) :: X
    end function
    elemental logical function _gfortran_ieee_is_nan_8(X)
      real(kind=8), intent(in) :: X
    end function
  end interface

  interface IEEE_IS_NAN
    procedure _gfortran_ieee_is_nan_4, _gfortran_ieee_is_nan_8
  end interface
  public :: IEEE_IS_NAN

  ! IEEE_IS_NEGATIVE

  interface
    elemental logical function _gfortran_ieee_is_negative_4(X)
      real(kind=4), intent(in) :: X
    end function
    elemental logical function _gfortran_ieee_is_negative_8(X)
      real(kind=8), intent(in) :: X
    end function
  end interface

  interface IEEE_IS_NEGATIVE
    procedure _gfortran_ieee_is_negative_4, _gfortran_ieee_is_negative_8
  end interface
  public :: IEEE_IS_NEGATIVE

  ! IEEE_IS_NORMAL

  interface
    elemental logical function _gfortran_ieee_is_normal_4(X)
      real(kind=4), intent(in) :: X
    end function
    elemental logical function _gfortran_ieee_is_normal_8(X)
      real(kind=8), intent(in) :: X
    end function
  end interface

  interface IEEE_IS_NORMAL
    procedure _gfortran_ieee_is_normal_4, _gfortran_ieee_is_normal_8
  end interface
  public :: IEEE_IS_NORMAL

  ! IEEE_COPY_SIGN

  interface
    elemental real(kind=4) function _gfortran_ieee_copy_sign_4_4 (X,Y)
      real(kind=4), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental real(kind=4) function _gfortran_ieee_copy_sign_4_8 (X,Y)
      real(kind=4), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_copy_sign_8_4 (X,Y)
      real(kind=8), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_copy_sign_8_8 (X,Y)
      real(kind=8), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
  end interface

  interface IEEE_COPY_SIGN
    procedure _gfortran_ieee_copy_sign_4_4, _gfortran_ieee_copy_sign_4_8, &
              _gfortran_ieee_copy_sign_8_4, _gfortran_ieee_copy_sign_8_8
  end interface
  public :: IEEE_COPY_SIGN

  ! IEEE_UNORDERED

  interface
    elemental logical function _gfortran_ieee_unordered_4_4 (X,Y)
      real(kind=4), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental logical function _gfortran_ieee_unordered_4_8 (X,Y)
      real(kind=4), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
    elemental logical function _gfortran_ieee_unordered_8_4 (X,Y)
      real(kind=8), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental logical function _gfortran_ieee_unordered_8_8 (X,Y)
      real(kind=8), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
  end interface

  interface IEEE_UNORDERED
    procedure _gfortran_ieee_unordered_4_4, _gfortran_ieee_unordered_4_8, &
              _gfortran_ieee_unordered_8_4, _gfortran_ieee_unordered_8_8
  end interface
  public :: IEEE_UNORDERED

  ! IEEE_LOGB

  interface
    elemental real(kind=4) function _gfortran_ieee_logb_4 (X)
      real(kind=4), intent(in) :: X
    end function
    elemental real(kind=8) function _gfortran_ieee_logb_8 (X)
      real(kind=8), intent(in) :: X
    end function
  end interface

  interface IEEE_LOGB
    procedure _gfortran_ieee_logb_4, _gfortran_ieee_logb_8
  end interface
  public :: IEEE_LOGB

  ! IEEE_NEXT_AFTER

  interface
    elemental real(kind=4) function _gfortran_ieee_next_after_4_4 (X, Y)
      real(kind=4), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental real(kind=4) function _gfortran_ieee_next_after_4_8 (X, Y)
      real(kind=4), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_next_after_8_4 (X, Y)
      real(kind=8), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_next_after_8_8 (X, Y)
      real(kind=8), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
  end interface

  interface IEEE_NEXT_AFTER
    procedure _gfortran_ieee_next_after_4_4, _gfortran_ieee_next_after_4_8, &
              _gfortran_ieee_next_after_8_4, _gfortran_ieee_next_after_8_8
  end interface
  public :: IEEE_NEXT_AFTER

  ! IEEE_REM

  interface
    elemental real(kind=4) function _gfortran_ieee_rem_4_4 (X, Y)
      real(kind=4), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_rem_4_8 (X, Y)
      real(kind=4), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_rem_8_4 (X, Y)
      real(kind=8), intent(in) :: X
      real(kind=4), intent(in) :: Y
    end function
    elemental real(kind=8) function _gfortran_ieee_rem_8_8 (X, Y)
      real(kind=8), intent(in) :: X
      real(kind=8), intent(in) :: Y
    end function
  end interface

  interface IEEE_REM
    procedure _gfortran_ieee_rem_4_4, _gfortran_ieee_rem_4_8, &
              _gfortran_ieee_rem_8_4, _gfortran_ieee_rem_8_8
  end interface
  public :: IEEE_REM

  ! IEEE_RINT

  interface
    elemental real(kind=4) function _gfortran_ieee_rint_4 (X)
      real(kind=4), intent(in) :: X
    end function
    elemental real(kind=8) function _gfortran_ieee_rint_8 (X)
      real(kind=8), intent(in) :: X
    end function
  end interface

  interface IEEE_RINT
    procedure _gfortran_ieee_rint_4, _gfortran_ieee_rint_8
  end interface
  public :: IEEE_RINT

  ! IEEE_SCALB

  interface
    elemental real(kind=4) function _gfortran_ieee_scalb_4 (X, I)
      real(kind=4), intent(in) :: X
      integer, intent(in) :: I
    end function
    elemental real(kind=8) function _gfortran_ieee_scalb_8 (X, I)
      real(kind=8), intent(in) :: X
      integer, intent(in) :: I
    end function
  end interface

  interface IEEE_SCALB
    procedure _gfortran_ieee_scalb_4, _gfortran_ieee_scalb_8
  end interface
  public :: IEEE_SCALB

  ! IEEE_VALUE

  interface IEEE_VALUE
    module procedure IEEE_VALUE_4, IEEE_VALUE_8
  end interface
  public :: IEEE_VALUE

  ! IEEE_CLASS

  interface IEEE_CLASS
    module procedure IEEE_CLASS_4, IEEE_CLASS_8
  end interface
  public :: IEEE_CLASS

  ! Public declarations for contained procedures
  public :: IEEE_GET_ROUNDING_MODE, IEEE_SET_ROUNDING_MODE
  public :: IEEE_GET_UNDERFLOW_MODE, IEEE_SET_UNDERFLOW_MODE
  public :: IEEE_SELECTED_REAL_KIND

  ! IEEE_SUPPORT_ROUNDING

  interface IEEE_SUPPORT_ROUNDING
    module procedure IEEE_SUPPORT_ROUNDING_4, IEEE_SUPPORT_ROUNDING_8, &
#ifdef HAVE_GFC_REAL_10
                     IEEE_SUPPORT_ROUNDING_10, &
#endif
#ifdef HAVE_GFC_REAL_16
                     IEEE_SUPPORT_ROUNDING_16, &
#endif
                     IEEE_SUPPORT_ROUNDING_NOARG
  end interface
  public :: IEEE_SUPPORT_ROUNDING
  
  ! Interface to the FPU-specific function
  interface
    pure integer function support_rounding_helper(flag) &
        bind(c, name="_gfortrani_support_fpu_rounding_mode")
      integer, intent(in), value :: flag
    end function
  end interface

  ! IEEE_SUPPORT_UNDERFLOW_CONTROL

  interface IEEE_SUPPORT_UNDERFLOW_CONTROL
    module procedure IEEE_SUPPORT_UNDERFLOW_CONTROL_4, &
                     IEEE_SUPPORT_UNDERFLOW_CONTROL_8, &
#ifdef HAVE_GFC_REAL_10
                     IEEE_SUPPORT_UNDERFLOW_CONTROL_10, &
#endif
#ifdef HAVE_GFC_REAL_16
                     IEEE_SUPPORT_UNDERFLOW_CONTROL_16, &
#endif
                     IEEE_SUPPORT_UNDERFLOW_CONTROL_NOARG
  end interface
  public :: IEEE_SUPPORT_UNDERFLOW_CONTROL
  
  ! Interface to the FPU-specific function
  interface
    pure integer function support_underflow_control_helper(kind) &
        bind(c, name="_gfortrani_support_fpu_underflow_control")
      integer, intent(in), value :: kind
    end function
  end interface

! IEEE_SUPPORT_* generic functions

#if defined(HAVE_GFC_REAL_10) && defined(HAVE_GFC_REAL_16)
# define MACRO1(NAME) NAME/**/_4, NAME/**/_8, NAME/**/_10, NAME/**/_16, NAME/**/_NOARG
#elif defined(HAVE_GFC_REAL_10)
# define MACRO1(NAME) NAME/**/_4, NAME/**/_8, NAME/**/_10, NAME/**/_NOARG
#elif defined(HAVE_GFC_REAL_16)
# define MACRO1(NAME) NAME/**/_4, NAME/**/_8, NAME/**/_16, NAME/**/_NOARG
#else
# define MACRO1(NAME) NAME/**/_4, NAME/**/_8, NAME/**/_NOARG
#endif

#define SUPPORTGENERIC(NAME) \
  interface NAME ; module procedure MACRO1(NAME) ; end interface ; \
  public :: NAME

SUPPORTGENERIC(IEEE_SUPPORT_DATATYPE)
SUPPORTGENERIC(IEEE_SUPPORT_DENORMAL)
SUPPORTGENERIC(IEEE_SUPPORT_DIVIDE)
SUPPORTGENERIC(IEEE_SUPPORT_INF)
SUPPORTGENERIC(IEEE_SUPPORT_IO)
SUPPORTGENERIC(IEEE_SUPPORT_NAN)
SUPPORTGENERIC(IEEE_SUPPORT_SQRT)
SUPPORTGENERIC(IEEE_SUPPORT_STANDARD)

contains

  ! Equality operators for IEEE_CLASS_TYPE and IEEE_ROUNDING_MODE
  elemental logical function IEEE_CLASS_TYPE_EQ (X, Y) result(res)
    implicit none
    type(IEEE_CLASS_TYPE), intent(in) :: X, Y
    res = (X%hidden == Y%hidden)
  end function

  elemental logical function IEEE_CLASS_TYPE_NE (X, Y) result(res)
    implicit none
    type(IEEE_CLASS_TYPE), intent(in) :: X, Y
    res = (X%hidden /= Y%hidden)
  end function

  elemental logical function IEEE_ROUND_TYPE_EQ (X, Y) result(res)
    implicit none
    type(IEEE_ROUND_TYPE), intent(in) :: X, Y
    res = (X%hidden == Y%hidden)
  end function

  elemental logical function IEEE_ROUND_TYPE_NE (X, Y) result(res)
    implicit none
    type(IEEE_ROUND_TYPE), intent(in) :: X, Y
    res = (X%hidden /= Y%hidden)
  end function

  ! IEEE_SELECTED_REAL_KIND
  integer function IEEE_SELECTED_REAL_KIND (P, R, RADIX) result(res)
    implicit none
    integer, intent(in), optional :: P, R, RADIX
    integer :: p2, r2

    p2 = 0 ; r2 = 0
    if (present(p)) p2 = p
    if (present(r)) r2 = r

    ! The only IEEE types we support right now are binary
    if (present(radix)) then
      if (radix /= 2) then
        res = -5
        return
      endif
    endif

    ! Does IEEE float fit?
    if (precision(0.) >= p2 .and. range(0.) >= r2) then
      res = kind(0.)
      return
    endif

    ! Does IEEE double fit?
    if (precision(0.d0) >= p2 .and. range(0.d0) >= r2) then
      res = kind(0.d0)
      return
    endif

    if (precision(0.d0) < p2 .and. range(0.d0) < r2) then
      res = -3
      return
    endif

    if (precision(0.d0) < p2) then
      res = -1
      return
    endif

   res = -2
  end function


  ! IEEE_CLASS

  elemental function IEEE_CLASS_4 (X) result(res)
    implicit none
    real(kind=4), intent(in) :: X
    type(IEEE_CLASS_TYPE) :: res

    interface
      pure integer function _gfortrani_ieee_class_helper_4(val)
        real(kind=4), intent(in) :: val
      end function
    end interface

    res = IEEE_CLASS_TYPE(_gfortrani_ieee_class_helper_4(X))
  end function

  elemental function IEEE_CLASS_8 (X) result(res)
    implicit none
    real(kind=8), intent(in) :: X
    type(IEEE_CLASS_TYPE) :: res

    interface
      pure integer function _gfortrani_ieee_class_helper_8(val)
        real(kind=8), intent(in) :: val
      end function
    end interface

    res = IEEE_CLASS_TYPE(_gfortrani_ieee_class_helper_8(X))
  end function

  ! IEEE_VALUE

  elemental real(kind=4) function IEEE_VALUE_4(X, C) result(res)
    implicit none
    real(kind=4), intent(in) :: X
    type(IEEE_CLASS_TYPE), intent(in) :: C

    select case (C%hidden)
      case (1)     ! IEEE_SIGNALING_NAN
        res = -1
        res = sqrt(res)
      case (2)     ! IEEE_QUIET_NAN
        res = -1
        res = sqrt(res)
      case (3)     ! IEEE_NEGATIVE_INF
        res = huge(res)
        res = (-res) * res
      case (4)     ! IEEE_NEGATIVE_NORMAL
        res = -42
      case (5)     ! IEEE_NEGATIVE_DENORMAL
        res = -tiny(res)
        res = res / 2
      case (6)     ! IEEE_NEGATIVE_ZERO
        res = 0
        res = -res
      case (7)     ! IEEE_POSITIVE_ZERO
        res = 0
      case (8)     ! IEEE_POSITIVE_DENORMAL
        res = tiny(res)
        res = res / 2
      case (9)     ! IEEE_POSITIVE_NORMAL
        res = 42
      case (10)    ! IEEE_POSITIVE_INF
        res = huge(res)
        res = res * res
      case default ! IEEE_OTHER_VALUE, should not happen
        res = 0
     end select
  end function

  elemental real(kind=8) function IEEE_VALUE_8(X, C) result(res)
    implicit none
    real(kind=8), intent(in) :: X
    type(IEEE_CLASS_TYPE), intent(in) :: C

    select case (C%hidden)
      case (1)     ! IEEE_SIGNALING_NAN
        res = -1
        res = sqrt(res)
      case (2)     ! IEEE_QUIET_NAN
        res = -1
        res = sqrt(res)
      case (3)     ! IEEE_NEGATIVE_INF
        res = huge(res)
        res = (-res) * res
      case (4)     ! IEEE_NEGATIVE_NORMAL
        res = -42
      case (5)     ! IEEE_NEGATIVE_DENORMAL
        res = -tiny(res)
        res = res / 2
      case (6)     ! IEEE_NEGATIVE_ZERO
        res = 0
        res = -res
      case (7)     ! IEEE_POSITIVE_ZERO
        res = 0
      case (8)     ! IEEE_POSITIVE_DENORMAL
        res = tiny(res)
        res = res / 2
      case (9)     ! IEEE_POSITIVE_NORMAL
        res = 42
      case (10)    ! IEEE_POSITIVE_INF
        res = huge(res)
        res = res * res
      case default ! IEEE_OTHER_VALUE, should not happen
        res = 0
     end select
  end function


  ! IEEE_GET_ROUNDING_MODE

  subroutine IEEE_GET_ROUNDING_MODE (ROUND_VALUE)
    implicit none
    type(IEEE_ROUND_TYPE), intent(out) :: ROUND_VALUE

    interface
      integer function helper() &
        bind(c, name="_gfortrani_get_fpu_rounding_mode")
      end function
    end interface

    ROUND_VALUE = IEEE_ROUND_TYPE(helper())
  end subroutine


  ! IEEE_SET_ROUNDING_MODE

  subroutine IEEE_SET_ROUNDING_MODE (ROUND_VALUE)
    implicit none
    type(IEEE_ROUND_TYPE), intent(in) :: ROUND_VALUE

    interface
      subroutine helper(val) &
          bind(c, name="_gfortrani_set_fpu_rounding_mode")
        integer, value :: val
      end subroutine
    end interface
    
    call helper(ROUND_VALUE%hidden)
  end subroutine


  ! IEEE_GET_UNDERFLOW_MODE

  subroutine IEEE_GET_UNDERFLOW_MODE (GRADUAL)
    implicit none
    logical, intent(out) :: GRADUAL

    interface
      integer function helper() &
        bind(c, name="_gfortrani_get_fpu_underflow_mode")
      end function
    end interface

    GRADUAL = (helper() /= 0)
  end subroutine


  ! IEEE_SET_UNDERFLOW_MODE

  subroutine IEEE_SET_UNDERFLOW_MODE (GRADUAL)
    implicit none
    logical, intent(in) :: GRADUAL

    interface
      subroutine helper(val) &
          bind(c, name="_gfortrani_set_fpu_underflow_mode")
        integer, value :: val
      end subroutine
    end interface

    call helper(merge(1, 0, GRADUAL))
  end subroutine

! IEEE_SUPPORT_ROUNDING

  pure logical function IEEE_SUPPORT_ROUNDING_4 (ROUND_VALUE, X) result(res)
    implicit none
    real(kind=4), intent(in) :: X
    type(IEEE_ROUND_TYPE), intent(in) :: ROUND_VALUE
    res = (support_rounding_helper(ROUND_VALUE%hidden) /= 0)
  end function

  pure logical function IEEE_SUPPORT_ROUNDING_8 (ROUND_VALUE, X) result(res)
    implicit none
    real(kind=8), intent(in) :: X
    type(IEEE_ROUND_TYPE), intent(in) :: ROUND_VALUE
    res = (support_rounding_helper(ROUND_VALUE%hidden) /= 0)
  end function

#ifdef HAVE_GFC_REAL_10
  pure logical function IEEE_SUPPORT_ROUNDING_10 (ROUND_VALUE, X) result(res)
    implicit none
    real(kind=10), intent(in) :: X
    type(IEEE_ROUND_TYPE), intent(in) :: ROUND_VALUE
    res = .false.
  end function
#endif

#ifdef HAVE_GFC_REAL_16
  pure logical function IEEE_SUPPORT_ROUNDING_16 (ROUND_VALUE, X) result(res)
    implicit none
    real(kind=16), intent(in) :: X
    type(IEEE_ROUND_TYPE), intent(in) :: ROUND_VALUE
    res = .false.
  end function
#endif

  pure logical function IEEE_SUPPORT_ROUNDING_NOARG (ROUND_VALUE) result(res)
    implicit none
    type(IEEE_ROUND_TYPE), intent(in) :: ROUND_VALUE
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
    res = .false.
#else
    res = (support_rounding_helper(ROUND_VALUE%hidden) /= 0)
#endif
  end function

! IEEE_SUPPORT_UNDERFLOW_CONTROL

  pure logical function IEEE_SUPPORT_UNDERFLOW_CONTROL_4 (X) result(res)
    implicit none
    real(kind=4), intent(in) :: X
    res = (support_underflow_control_helper(4) /= 0)
  end function

  pure logical function IEEE_SUPPORT_UNDERFLOW_CONTROL_8 (X) result(res)
    implicit none
    real(kind=8), intent(in) :: X
    res = (support_underflow_control_helper(8) /= 0)
  end function

#ifdef HAVE_GFC_REAL_10
  pure logical function IEEE_SUPPORT_UNDERFLOW_CONTROL_10 (X) result(res)
    implicit none
    real(kind=10), intent(in) :: X
    res = .false.
  end function
#endif

#ifdef HAVE_GFC_REAL_16
  pure logical function IEEE_SUPPORT_UNDERFLOW_CONTROL_16 (X) result(res)
    implicit none
    real(kind=16), intent(in) :: X
    res = .false.
  end function
#endif

  pure logical function IEEE_SUPPORT_UNDERFLOW_CONTROL_NOARG () result(res)
    implicit none
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
    res = .false.
#else
    res = (support_underflow_control_helper(4) /= 0 &
           .and. support_underflow_control_helper(8) /= 0)
#endif
  end function

! IEEE_SUPPORT_* functions

#define SUPPORTMACRO(NAME, INTKIND, VALUE) \
  pure logical function NAME/**/_/**/INTKIND (X) result(res) ; \
    implicit none                                            ; \
    real(INTKIND), intent(in) :: X(..)                       ; \
    res = VALUE                                              ; \
  end function

#define SUPPORTMACRO_NOARG(NAME, VALUE) \
  pure logical function NAME/**/_NOARG () result(res) ; \
    implicit none                                     ; \
    res = VALUE                                       ; \
  end function

! IEEE_SUPPORT_DATATYPE

SUPPORTMACRO(IEEE_SUPPORT_DATATYPE,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_DATATYPE,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_DATATYPE,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_DATATYPE,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_DATATYPE,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_DATATYPE,.true.)
#endif

! IEEE_SUPPORT_DENORMAL

SUPPORTMACRO(IEEE_SUPPORT_DENORMAL,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_DENORMAL,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_DENORMAL,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_DENORMAL,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_DENORMAL,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_DENORMAL,.true.)
#endif

! IEEE_SUPPORT_DIVIDE

SUPPORTMACRO(IEEE_SUPPORT_DIVIDE,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_DIVIDE,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_DIVIDE,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_DIVIDE,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_DIVIDE,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_DIVIDE,.true.)
#endif

! IEEE_SUPPORT_INF

SUPPORTMACRO(IEEE_SUPPORT_INF,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_INF,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_INF,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_INF,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_INF,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_INF,.true.)
#endif

! IEEE_SUPPORT_IO

SUPPORTMACRO(IEEE_SUPPORT_IO,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_IO,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_IO,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_IO,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_IO,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_IO,.true.)
#endif

! IEEE_SUPPORT_NAN

SUPPORTMACRO(IEEE_SUPPORT_NAN,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_NAN,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_NAN,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_NAN,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_NAN,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_NAN,.true.)
#endif

! IEEE_SUPPORT_SQRT

SUPPORTMACRO(IEEE_SUPPORT_SQRT,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_SQRT,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_SQRT,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_SQRT,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_SQRT,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_SQRT,.true.)
#endif

! IEEE_SUPPORT_STANDARD

SUPPORTMACRO(IEEE_SUPPORT_STANDARD,4,.true.)
SUPPORTMACRO(IEEE_SUPPORT_STANDARD,8,.true.)
#ifdef HAVE_GFC_REAL_10
SUPPORTMACRO(IEEE_SUPPORT_STANDARD,10,.false.)
#endif
#ifdef HAVE_GFC_REAL_16
SUPPORTMACRO(IEEE_SUPPORT_STANDARD,16,.false.)
#endif
#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
SUPPORTMACRO_NOARG(IEEE_SUPPORT_STANDARD,.false.)
#else
SUPPORTMACRO_NOARG(IEEE_SUPPORT_STANDARD,.true.)
#endif

end module IEEE_ARITHMETIC
