// ---------------------------------------------------------------------
//
// Copyright (C) 2016 - 2017 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

#ifndef dealii_differentiation_ad_ad_number_traits_h
#define dealii_differentiation_ad_ad_number_traits_h

#include <deal.II/base/exceptions.h>
#include <deal.II/differentiation/ad/ad_number_types.h>

#include <boost/type_traits.hpp>

#include <complex>
#include <type_traits>

DEAL_II_NAMESPACE_OPEN

namespace Differentiation
{
  namespace AD
  {

    /**
     * A number traits class to help describe some characteristic
     * information about auto-differentiable numbers.
     *
     * @tparam ScalarType A real or complex floating point number.
     * @tparam ADNumberTypeCode An enumeration specifying the type
     *         code for the supported auto-differentiable counterpart
     *         to the given @p ScalarType.
     * @tparam T An arbitrary type resulting from the application of
     *         the SFINAE idiom to selectively specialize this class.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template<typename ScalarType, enum NumberTypes ADNumberTypeCode, typename T = void>
    struct NumberTraits;



    /**
     * A number traits class to help describe some characteristic
     * information about auto-differentiable numbers. This class is only
     * specialized (and, therefore, only made constructible) if the
     * @p ADNumberType template parameter is indeed a supported
     * auto-differentiable number.
     *
     * @tparam ADNumberType A type corresponding to a supported
     *         auto-differentiable number.
     * @tparam T An arbitrary type resulting from the application of
     *         the SFINAE idiom to selectively specialize this class.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template<typename ADNumberType, typename T = void>
    struct ADNumberTraits;


    /**
     * This namespace defines the classes that help provide a unified interface
     * to all auto-differentiation numbers.
     */
    namespace internal
    {

      // The following three classes, namely ADNumberInfoFromEnum, Marking, and
      // ExtractData, are those that need to be implemented for each new
      // auto-differentiable number type. This information is then used by
      // NumberTraits and ADNumberTraits to provide a uniform interface, as used
      // by our drivers, to the underlying number types.


      /**
       * A struct that defines some fundamental information about a
       * auto-differentiable number based on the @p ScalarType and the
       * AD-enumeration selected by @p ADNumberTypeCode. This information is
       * used in other convenience classes and templated functions to
       * automatically determine information about the auto-differentiable
       * number that has been selected to wrap the @p ScalarType.
       *
       * The specializations of this class have to implement the following
       * member data and type definitions:
       * @code
       *
       *   // State whether the auto-differentiable number uses taping or not.
       *   static const bool             is_taped;
       *   // The real-type for the auto-differentiable number
       *   typedef <ADNumberType>        real_type;
       *   // The type of number returned when taking the first derivative of the @p real_type.
       *   typedef <Scalar/ADNumberType> derivative_type;
       *   // The number of derivative levels computable from the @p real_type.
       *   static const unsigned int     n_supported_derivative_levels;
       *
       * @endcode
       *
       * @author Jean-Paul Pelteret, 2017
       */
      template<typename ScalarType, enum NumberTypes ADNumberTypeCode, typename = void>
      struct ADNumberInfoFromEnum;


      /**
       * A struct to assist with the marking of AD numbers that represent
       * independent and dependent variables.
       *
       * The specializations of this class have to implement the following
       * member functions:
       * @code
       *
       *  // Initialize the state of an independent variable.
       *  static void
       *  independent_variable(const ScalarType   &in,
       *                       const unsigned int  index,
       *                       const unsigned int  n_independent_variables,
       *                       ADNumberType       &out)
       *
       *  // Initialize the state of a dependent variable.
       *  static void
       *  dependent_variable(ADNumberType       &out,
       *                     const ADNumberType &func);
       *
       * @endcode
       * where @p ADNumberType is the auto-differentiable number type and
       * @p ScalarType is its floating point counterpart.
       *
       * @tparam ADNumberType A type corresponding to a supported
       *         auto-differentiable number.
       * @tparam T An arbitrary type resulting from the application of
       *         the SFINAE idiom to selectively specialize this class.
       *
       * @author Jean-Paul Pelteret, 2017
       */
      template<typename ADNumberType, typename T = void>
      struct Marking;


      /**
       * A struct to help extract certain information associated with
       * auto-differentiable numbers.
       *
       * The specializations of this class have to implement the following
       * member data and type definitions:
       * @code
       *
       *   // Extract the real scalar value.
       *   static scalar_type
       *   value (const ADNumberType &x);
       *
       *   // Extract the number of directional derivatives.
       *   static unsigned int
       *   n_directional_derivatives (const ADNumberType &x);
       *
       *   // Extract the directional derivative in the specified @p direction.
       *   static derivative_type
       *   directional_derivative (const ADNumberType &x,
       *                           const unsigned int  direction);
       *
       * @endcode
       * where @p ADNumberType is the auto-differentiable number type.
       *
       * @tparam ADNumberType A type corresponding to a supported
       *         auto-differentiable number.
       * @tparam T An arbitrary type resulting from the application of
       *         the SFINAE idiom to selectively specialize this class.
       *
       * @author Jean-Paul Pelteret, 2017
       */
      template<typename ADNumberType, typename T = void>
      struct ExtractData;


      /**
       * A struct that checks that the data expected to be stored in a specialization
       * of the ADNumberInfoFromEnum struct has been supplied. By default it is
       * assumed that the input type does not satisfy the necessary conditions to
       * construct this class.
       *
       * @tparam ADNumberTrait A class that examined whether it contains the necessary
       *         information to satisfy the requirements for being an internally
       *         supported auto-differentiable number.
       * @tparam T An arbitrary type resulting from the application of
       *         the SFINAE idiom to selectively specialize this class.
       *
       * @author Jean-Paul Pelteret, 2017
       */
      template <typename ADNumberTrait, typename T = void>
      struct HasRequiredADInfo;


      /**
       * Provide a convenience function to assist in the casting of some
       * number types to other number types. On top of the standard class
       * definition given in @p base/numbers.h , this extension allows the
       * conversion of automatic-differentiation numbers to generic floats.
       *
       * This is necessary because Adol-C doesn't provide a convenient
       * way to convert from an @p ADNumberType to floats (@p T) other than
       * the real-type equivalent that its associated with. For Sacado, and
       * likely other AD number types,  the floating point value stored in
       * an @p ADNumberType must be extracted through some function that is
       * specific to each type of AD number. This requires some specialist
       * intervention to get at this data.
       *
       * @author Jean-Paul Pelteret, 2017
       */
      template <typename T>
      struct NumberType;


      /**
       * A small struct to remove the @p std::complex wrapper
       * around a number.
       *
       * @author Jean-Paul Pelteret, 2017
       */
      template<typename Number>
      struct RemoveComplexWrapper;

    } // namespace internal


    /**
     * A struct to indicate whether a given @p NumberType is a supported
     * auto-differentiable number or not. By default, numbers are not
     * considered to have the necessary characteristics to fulfill this
     * condition.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template <typename NumberType>
    struct is_ad_number;


    /**
     * A struct to indicate whether a given @p NumberType is a taped
     * auto-differentiable number or not. By default, numbers are not
     * considered to have the necessary characteristics to fulfill this
     * condition.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template <typename NumberType, typename = void>
    struct is_taped_ad_number;


    /**
     * A struct to indicate whether a given @p NumberType is a tapeless
     * auto-differentiable number or not. By default, numbers are not
     * considered to have the necessary characteristics to fulfill this
     * condition.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template <typename NumberType, typename = void>
    struct is_tapeless_ad_number;


    /**
     * A struct to indicate whether a given @p NumberType is a real-valued
     * auto-differentiable number or not. By default, numbers are not
     * considered to have the necessary characteristics to fulfill this
     * condition.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template <typename NumberType, typename = void>
    struct is_real_valued_ad_number;


    /**
     * A struct to indicate whether a given @p NumberType is a complex-valued
     * auto-differentiable number or not. By default, numbers are not
     * considered to have the necessary characteristics to fulfill this
     * condition.
     *
     * @author Jean-Paul Pelteret, 2017
     */
    template <typename NumberType, typename = void>
    struct is_complex_valued_ad_number;

  } // namespace AD
} // namespace Differentiation


/* --------------------------- inline and template functions and specializations ------------------------- */


#ifndef DOXYGEN


namespace Differentiation
{
  namespace AD
  {
    namespace internal
    {
      template <typename ADNumberTrait, typename>
      struct HasRequiredADInfo
        : std::false_type
      { };


      /**
       * Specialization to detect whether the input AD number
       * is internally supported or not. In particular, we
       * check to see that it has been assigned a type_code and
       * has other basic characteristics necessary for the
       * internal interface of both the AD and SD drivers.
       *
       * The implementation of this struct follows this suggestion:
       *  https://stackoverflow.com/a/16000226
       */
      template <typename ADNumberTrait>
      struct HasRequiredADInfo <ADNumberTrait, decltype(
        (void) ADNumberTrait::type_code,
        (void) ADNumberTrait::is_taped,
        (void) std::declval<typename ADNumberTrait::real_type>(),
        (void) std::declval<typename ADNumberTrait::derivative_type>(),
  void() )> : std::true_type
      { };


      /**
       * A dummy specialization for floating point numbers. This is helpful
       * for nested auto-differentiable numbers, where a recursive marking
       * mechanism can be employed (e.g. Sacado types).
       */
      template<typename ScalarType>
      struct Marking<ScalarType, typename std::enable_if<
        std::is_arithmetic<ScalarType>::value
        >::type>
      {
        /**
         * Initialize the state of an independent variable.
         *
         * With a nested marking approach it is sometimes
         * necessary to initialise the value of an intermediate
         * value that may be a floating point number.
         */
        template<typename ADNumberType>
        static void
        independent_variable(const ScalarType   &in,
                             const unsigned int ,
                             const unsigned int ,
                             ADNumberType       &out)
        {
          out = in;
        }

        /*
         * Initialize the state of a dependent variable.
         */
        template<typename ADNumberType>
        static void
        dependent_variable(ADNumberType &,
                           const ScalarType &)
        {
          AssertThrow(false, ExcMessage("Floating point numbers cannot be marked as dependent variables."));
        }
      };


      /**
       * A specialization of the marking strategy for complex numbers.
       */
      template<typename ADNumberType>
      struct Marking<ADNumberType, typename std::enable_if<
        boost::is_complex<ADNumberType>::value
        >::type>
      {

        /*
         * Initialize the state of an independent variable.
         */
        template<typename ScalarType>
        static void
        independent_variable(const ScalarType   &in,
                             const unsigned int ,
                             const unsigned int ,
                             ADNumberType       &out)
        {
          AssertThrow(false, ExcMessage("Marking for complex numbers has not yet been implemented."));
          out = in;
        }

        /*
         * Initialize the state of a dependent variable.
         */
        template<typename ScalarType>
        static void
        dependent_variable(ADNumberType &,
                           const ScalarType &)
        {
          AssertThrow(false, ExcMessage("Marking for complex numbers has not yet been implemented."));
        }
      };

    } // namespace internal


    template <typename NumberType, typename>
    struct is_taped_ad_number
      : std::false_type
    {};


    template <typename NumberType, typename>
    struct is_tapeless_ad_number
      : std::false_type
    {};


    template <typename NumberType, typename>
    struct is_real_valued_ad_number
      : std::false_type
    {};


    template <typename NumberType, typename>
    struct is_complex_valued_ad_number
      : std::false_type
    {};


    /**
     * We use the specialization of the HasRequiredADInfo struct
     * to ensure that only internally supported numbers are
     * considered AD numbers.
     */
    template <typename NumberType>
    struct is_ad_number
      : internal::HasRequiredADInfo< ADNumberTraits<typename std::decay<NumberType>::type> >
    {};


    /**
     * Specialization of the struct for the case when the input template
     * parameter is a (real or complex) taped auto-differentiable number.
     */
    template <typename NumberType>
    struct is_taped_ad_number<NumberType, typename std::enable_if<
      ADNumberTraits<typename std::decay<NumberType>::type>::is_taped
      >::type>
      : std::true_type
    {};


    /**
     * Specialization of the struct for the case when the input template
     * parameter is a (real or complex) tapeless auto-differentiable number.
     */
    template <typename NumberType>
    struct is_tapeless_ad_number<NumberType, typename std::enable_if<
      ADNumberTraits<typename std::decay<NumberType>::type>::is_tapeless
      >::type>
      : std::true_type
    {};


    /**
     * Specialization of the struct for the case when the input template
     * parameter is a (taped or tapeless) real-valued auto-differentiable number.
     */
    template <typename NumberType>
    struct is_real_valued_ad_number<NumberType, typename std::enable_if<
      ADNumberTraits<typename std::decay<NumberType>::type>::is_real_valued
      >::type>
      : std::true_type
    {};


    /**
     * Specialization of the struct for the case when the input template
     * parameter is a (taped or tapeless) complex-valued auto-differentiable number.
     */
    template <typename NumberType>
    struct is_complex_valued_ad_number<NumberType, typename std::enable_if<
      ADNumberTraits<typename std::decay<NumberType>::type>::is_complex_valued
      >::type>
      : std::true_type
    {};


    namespace internal
    {

      /**
       * Specialization of the selection struct which sets the type as being
       * one that is the value type of the underlying complex number.
       */
      template<typename Number>
      struct RemoveComplexWrapper
      {
        typedef Number type;
      };


      /**
       * Specialization of the selection struct which sets the value type
       * to that resulting from the recursive removal of the complex
       * number wrapper.
       */
      template<typename Number>
      struct RemoveComplexWrapper<std::complex<Number> >
      {
        typedef typename RemoveComplexWrapper<Number>::type type;
      };


      /**
       * A dummy specialization for floating point numbers. This is helpful
       * for nested auto-differentiable numbers, where a recursive marking
       * mechanism can be employed (e.g. Sacado types).
       */
      template<typename NumberType>
      struct ExtractData<NumberType, typename std::enable_if<
        std::is_arithmetic<NumberType>::value
        >::type>
      {
        /**
         * Extract the floating point value.
         */
        static const NumberType &
        value (const NumberType &x)
        {
          return x;
        }


        /**
         * Extract the number of directional derivatives.
         */
        static unsigned int
        n_directional_derivatives (const NumberType &)
        {
          return 0;
        }


        /**
         * Extract the directional derivative in the specified @p direction.
         */
        static NumberType
        directional_derivative  (const NumberType &,
                                 const unsigned int )
        {
          return 0.0;
        }
      };



      /**
       * A struct specialization to help extract certain information associated
       * with complex auto-differentiable numbers.
       */
      template<typename ADNumberType>
      struct ExtractData< std::complex<ADNumberType> >
      {
        static_assert(
          Differentiation::AD::is_ad_number<ADNumberType>::value,
          "Expected an auto-differentiable number."
        );


        /**
         * Extract the floating point value.
         */
        static std::complex<typename ADNumberTraits<ADNumberType>::scalar_type>
        value (const std::complex<ADNumberType> &x)
        {
          return std::complex<typename ADNumberTraits<ADNumberType>::scalar_type>(
                   ExtractData<ADNumberType>::value(x.real()),
                   ExtractData<ADNumberType>::value(x.imag()) );
        }


        /**
         * Extract the number of directional derivatives.
         */
        static unsigned int
        n_directional_derivatives (const std::complex<ADNumberType> &x)
        {
          return ExtractData<ADNumberType>::n_directional_derivatives(x.real());
        }


        /**
         * Extract the directional derivative in the specified @p direction.
         */
        static std::complex<typename ADNumberTraits<ADNumberType>::derivative_type>
        directional_derivative  (const std::complex<ADNumberType> &x,
                                 const unsigned int                direction)
        {
          return std::complex<typename ADNumberTraits<ADNumberType>::derivative_type>(
                   ExtractData<ADNumberType>::directional_derivative(x.real(), direction),
                   ExtractData<ADNumberType>::directional_derivative(x.imag(), direction) );
        }
      };


      template <typename T>
      struct NumberType
      {
        /**
         * Standard number conversion
         */
        template<typename F>
        static auto
        value (const F &f,
               typename std::enable_if<
               !is_ad_number<F>::value
               >::type * = nullptr)
        -> decltype (dealii::internal::NumberType<T>::value(f))
        {
          // We call the other function defined in the numbers
          // header to take care of all of the usual cases.
          return dealii::internal::NumberType<T>::value(f);
        }

        /**
         * Conversion from an AD number to a scalar number.
         * We wish to ensure that @p T is a scalar type because,
         * in general, the conversion between AD numbers is either
         * non-trivial or an invalid operation.
         */
        template<typename F>
        static T
        value (const F &f,
               typename std::enable_if<
               is_ad_number<F>::value &&
               std::is_arithmetic<T>::value
               >::type * = nullptr)
        {
          // We recursively call this function in case the AD number is a
          // nested one. The recursion ends when the extracted value is
          // a floating point number.
          return NumberType<T>::value(ExtractData<F>::value(f));
        }

        /**
         * Conversion from an AD number another AD number.
         *
         * Since this is the most generic case we'll assume that
         * the return type is constructible from the input type.
         */
        template<typename F>
        static T
        value (const F &f,
               typename std::enable_if<
               is_ad_number<F>::value &&
               is_ad_number<T>::value
               >::type * = nullptr)
        {
          return T(f);
        }
      };

      template <typename T>
      struct NumberType< std::complex<T> >
      {
        /**
         * Standard complex number conversion
         */
        template<typename F>
        static auto
        value (const F &f,
               typename std::enable_if<
               !is_ad_number<F>::value
               >::type * = nullptr)
        -> decltype (dealii::internal::NumberType< std::complex<T> >::value(f))
        {
          // We call the other function defined in the numbers
          // header to take care of all of the usual cases.
          return dealii::internal::NumberType< std::complex<T> >::value(f);
        }


        /**
         * Conversion from a complex AD number to another
         * complex number templated on a scalar number.
         */
        template<typename F>
        static std::complex<T>
        value (const F &f,
               typename std::enable_if<
               is_ad_number<F>::value &&
               std::is_arithmetic<T>::value
               >::type * = nullptr)
        {
          // We recursively call this function in case the AD number is a
          // nested one. The recursion ends when the extracted value is
          // a floating point number.
          return std::complex<T>(NumberType<T>::value(ExtractData<F>::value(f)));
        }

        template <typename F>
        static std::complex<T>
        value (const std::complex<F> &f)
        {
          // Deal with the two parts of the input complex
          // number individually.
          return std::complex<T>(
                   NumberType<T>::value(f.real()),
                   NumberType<T>::value(f.imag()));
        }
      };

    } // namespace internal



    /**
     * Specialization of the general NumberTraits class that
     * provides relevant information for auto-differentiable numbers.
     *
     * For each new @p ADNumberTypeCode enumeration, a data structure called
     * internal::ADNumberInfoFromEnum is to be defined, from which we require
     * the following basic information determine all of the required information
     * and type traits for our helper classes:
     *   - A typedef called @p scalar_type, which defines the scalar or
     *     floating-point valued counterpart to the auto-differentiable number type.
     *     This can be real or complex valued.
     *   - A typedef called @p derivative_type, which defines the
     *     number type for directional derivatives.
     *   - A boolean called @p is_taped, which defines whether the auto-differentiable
     *     number is a taped number or not.
     *
     * This specific version specializes the generic case where the template
     * @p ScalarType represents a floating or complex number that is templated
     * on a floating point type.
     */
    template<typename ScalarType, enum NumberTypes ADNumberTypeCode>
    struct NumberTraits<ScalarType, ADNumberTypeCode, typename std::enable_if<
      std::is_floating_point<ScalarType>::value ||
  (boost::is_complex<ScalarType>::value &&
     std::is_floating_point<typename internal::RemoveComplexWrapper<ScalarType>::type>::value)
    >::type>
    {
      /**
       * The type of taping used
       */
      static constexpr enum NumberTypes  type_code
      = ADNumberTypeCode;

      // The clang compiler does not seem to like these
      // variables being defined as constant expressions
      // (the tests <adolc|sacado>/ad_number_traits_02 will
      // fail with linking errors). However, GCC complains
      // about the use of non-constant expressions in
      // std::conditional.
#ifdef __clang__

      /**
       * A flag to indicate whether the number is of
       * the taped variety or not
       */
      static const bool                 is_taped;


      /**
       * A flag to indicate whether the number is of
       * the tapeless variety or not
       */
      static const bool                 is_tapeless;


      /**
       * A flag to indicate whether the number represents
       * a real value
       */
      static const bool                 is_real_valued;


      /**
       * A flag to indicate whether the number represents
       * a complex value
       */
      static const bool                 is_complex_valued;


      /**
       * The number of directional derivatives that can be
       * taken with this auto-differentiable number
       */
      static const unsigned int         n_supported_derivative_levels;

#else

      /**
       * A flag to indicate whether the number is of
       * the taped variety or not
       */
      static constexpr bool             is_taped
      = internal::ADNumberInfoFromEnum<
        typename internal::RemoveComplexWrapper<ScalarType>::type, ADNumberTypeCode
      >::is_taped;


      /**
       * A flag to indicate whether the number is of
       * the tapeless variety or not
       */
      static constexpr bool             is_tapeless
      = !(NumberTraits<ScalarType,ADNumberTypeCode>::is_taped);


      /**
       * A flag to indicate whether the number represents
       * a real value
       */
      static constexpr bool             is_real_valued
      = (!boost::is_complex<ScalarType>::value);


      /**
       * A flag to indicate whether the number represents
       * a complex value
       */
      static constexpr bool             is_complex_valued
      = !(NumberTraits<ScalarType,ADNumberTypeCode>::is_real_valued);


      /**
       * The number of directional derivatives that can be
       * taken with this auto-differentiable number
       */
      static constexpr unsigned int     n_supported_derivative_levels
      = internal::ADNumberInfoFromEnum<
        typename internal::RemoveComplexWrapper<ScalarType>::type, ADNumberTypeCode
      >::n_supported_derivative_levels;

#endif


      /**
       * Underlying floating point value type.
       * This could real-valued or complex-valued.
       */
      typedef ScalarType                scalar_type;


      /**
       * Type for real numbers
       */
      typedef typename internal::ADNumberInfoFromEnum<
      typename internal::RemoveComplexWrapper<ScalarType>::type,ADNumberTypeCode
      >::real_type                      real_type;


      /**
       * Type for complex numbers
       */
      typedef std::complex<real_type>   complex_type;


      /**
       * The actual auto-differentiable number type
       */
      typedef typename std::conditional<
      is_real_valued,real_type,complex_type
      >::type                            ad_type;

      /**
       * The actual auto-differentiable number directional derivative type
       */
      typedef typename std::conditional<
      is_real_valued,
      typename internal::ADNumberInfoFromEnum<
      typename internal::RemoveComplexWrapper<ScalarType>::type,ADNumberTypeCode
      >::derivative_type,
      std::complex<typename internal::ADNumberInfoFromEnum<
      typename internal::RemoveComplexWrapper<ScalarType>::type,ADNumberTypeCode
      >::derivative_type>
      >::type          derivative_type;


      /**
       * Extract the value of an auto-differentiable number
       */
      static scalar_type
      get_scalar_value(const ad_type &x)
    {
      // Some tricky conversion cases to consider here:
      // - Nested AD numbers
      // - std::complex<double> --> std::complex<float>
      //   e.g. when ScalarType = float and ADNumberTypeCode = adolc_taped
      // Therefore, we use the internal casting mechanism
      // provided by the internal::NumberType struct.
      return internal::NumberType<scalar_type>::value(internal::ExtractData<ad_type>::value(x));
    }


    /**
     * Extract the derivative value of an auto-differentiable number
     */
    static derivative_type
    get_directional_derivative(const ad_type      &x,
                               const unsigned int  direction)
    {
      return internal::ExtractData<ad_type>::directional_derivative(x, direction);
    }


    /**
     * Extract the number of directional derivatives value tracked by
     * an auto-differentiable number
     */
    static unsigned int
    n_directional_derivatives(const ad_type &x)
    {
      return internal::ExtractData<ad_type>::n_directional_derivatives(x);
    }


    static_assert(
      (is_real_valued==true ?
       std::is_same<ad_type,real_type>::value :
       std::is_same<ad_type,complex_type>::value),
      "Incorrect template type selected for ad_type");

    static_assert(
      (is_complex_valued==true ?
       boost::is_complex<scalar_type>::value :
       true),
      "Expected a complex float_type");

    static_assert(
      (is_complex_valued==true ?
       boost::is_complex<ad_type>::value :
       true),
      "Expected a complex ad_type");

    };

#ifdef __clang__

    template<typename ScalarType, enum NumberTypes ADNumberTypeCode>
    const bool
    NumberTraits<ScalarType, ADNumberTypeCode, typename std::enable_if<
    std::is_floating_point<ScalarType>::value ||
    (boost::is_complex<ScalarType>::value &&
     std::is_floating_point<typename internal::RemoveComplexWrapper<ScalarType>::type>::value)
    >::type>::is_taped
      = internal::ADNumberInfoFromEnum<
        typename internal::RemoveComplexWrapper<ScalarType>::type, ADNumberTypeCode
        >::is_taped;


    template<typename ScalarType, enum NumberTypes ADNumberTypeCode>
    const bool
    NumberTraits<ScalarType, ADNumberTypeCode, typename std::enable_if<
    std::is_floating_point<ScalarType>::value ||
    (boost::is_complex<ScalarType>::value &&
     std::is_floating_point<typename internal::RemoveComplexWrapper<ScalarType>::type>::value)
    >::type>::is_tapeless
      = !(NumberTraits<ScalarType,ADNumberTypeCode>::is_taped);


    template<typename ScalarType, enum NumberTypes ADNumberTypeCode>
    const bool
    NumberTraits<ScalarType, ADNumberTypeCode, typename std::enable_if<
    std::is_floating_point<ScalarType>::value ||
    (boost::is_complex<ScalarType>::value &&
     std::is_floating_point<typename internal::RemoveComplexWrapper<ScalarType>::type>::value)
    >::type>::is_real_valued
      = (!boost::is_complex<ScalarType>::value);


    template<typename ScalarType, enum NumberTypes ADNumberTypeCode>
    const bool
    NumberTraits<ScalarType, ADNumberTypeCode, typename std::enable_if<
    std::is_floating_point<ScalarType>::value ||
    (boost::is_complex<ScalarType>::value &&
     std::is_floating_point<typename internal::RemoveComplexWrapper<ScalarType>::type>::value)
    >::type>::is_complex_valued
      = !(NumberTraits<ScalarType,ADNumberTypeCode>::is_real_valued);


    template<typename ScalarType, enum NumberTypes ADNumberTypeCode>
    const unsigned int
    NumberTraits<ScalarType, ADNumberTypeCode, typename std::enable_if<
    std::is_floating_point<ScalarType>::value ||
    (boost::is_complex<ScalarType>::value &&
     std::is_floating_point<typename internal::RemoveComplexWrapper<ScalarType>::type>::value)
    >::type>::n_supported_derivative_levels
      = internal::ADNumberInfoFromEnum<
        typename internal::RemoveComplexWrapper<ScalarType>::type, ADNumberTypeCode
        >::n_supported_derivative_levels;

#endif


    /**
     * A dummy specialization for floating point numbers. This is necessary to
     * deal with the tricky case of higher-order derivative extraction from
     * AD numbers.
     *
     * Sacado nests the directional derivatives within Sacado numbers, while Adol-C
     * does not. So, starting off from a scalar function f(x), the number type resulting
     * from the computation of a directional derivative f'(x) of that function is a floating
     * point number for Adol-C number types and Sacado::FAD<double>, but that of a
     * Sacado::FAD<Sacado::FAD<double>> is a Sacado::FAD<double>.
     *
     * For this reason, when trying to extract higher-order derivatives of a number that
     * does not support them the input value to this function may be a scalar type.
     */
    template<typename ScalarType>
    struct ADNumberTraits<ScalarType, typename std::enable_if<
      std::is_arithmetic<ScalarType>::value
      >::type>
    {
      /**
       * Underlying floating point value type.
       * This could real-valued or complex-valued.
       */
      typedef ScalarType                scalar_type;

      static ScalarType
      get_directional_derivative(const ScalarType   &/*x*/,
                                 const unsigned int  /*direction*/)
      {
        // If the AD drivers are correctly implemented then we should not get here.
        // This is essentially a dummy for when the ADNumberTypeCode for the original
        // AD number (from which one is getting a derivative >= 2) is one that specified
        // Adol-C taped and tapeless numbers, or a non-nested Sacado number.
        AssertThrow(false, ExcMessage("Floating point numbers have no directional derivatives."));
        return 0.0;
      }
    };

  } // namespace AD
} // namespace Differentiation

#endif // DOXYGEN


namespace numbers
{

  template<typename ADNumberType>
  bool
  is_nan (const typename std::enable_if<
          Differentiation::AD::is_ad_number<ADNumberType>::value,
          ADNumberType>::type &x)
  {
    return is_nan(Differentiation::AD::ADNumberTraits<ADNumberType>::get_value(x));
  }

} // namespace numbers


DEAL_II_NAMESPACE_CLOSE

#endif
