Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • update_ATHENAtoEPIC
2 results

.clang-format

Blame
  • func_pattern.cxx 12.44 KiB
    #include <array>
    #include <functional>
    #include <iostream>
    #include <string>
    #include <tuple>
    #include <type_traits>
    #include <utility>
    #include <vector>
    #include <cmath>
    
    // See the excellent Fluentcpp blog series on strong types.
    // https://www.fluentcpp.com/2017/05/23/strong-types-inheriting-functionalities-from-underlying/
    #include "NamedType/named_type.hpp"
    using namespace fluent;
    
    
    
    namespace func {
    
      //@{
      /** Helpers.
       */
      template <typename T>
      struct is_tuple_impl : std::false_type {};
      template <typename... Ts>
      struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
      template <typename T>
      struct is_tuple : is_tuple_impl<std::decay_t<T>> {};
    
      //@}
    
      /** Named strong type helper.
       *
       */
      template <typename Tag>
      using Var = NamedType<double, Tag, Addable, Multiplicable, Printable,
                            ImplicitlyConvertibleTo<double>::templ>;
    
      /** Forward declaration of mixin.
       *
       */
      template <typename>
      struct ConstructedFunctionSet;
    
      /** FunctionSet: new variables defined by functions.
       *
       *  \param FVars should be a FVariables template class.
       *  \param Fs    is a tuple of lambdas that calculate values of NewVars_t
       * types.
       *
       */
      template <typename FVars, typename Fs,
                typename = typename std::enable_if<is_tuple<Fs>::value>::type>
      struct FunctionSet : FVars {
        using IndependentVariables_t = typename FVars::IndependentVariables_t;
        using InputVars_t            = typename FVars::InputVars_t;
        using NewVars_t              = typename FVars::NewVars_t;
        using Vars_t                 = typename FVars::Vars_t;
        using VariableFuncs_t        = Fs;
    
        static const size_t N_input_vars = FVars::N_input_vars;
        static const size_t N_new_vars   = FVars::N_new_vars;
        static const size_t N_vars       = FVars::N_vars;
    
        VariableFuncs_t functions;
    
        FunctionSet(const FVars& u, const Fs& f) : FVars(u), functions(f) {}
        FunctionSet(const FunctionSet& fs) : FVars(fs), functions(fs.functions) {}
    
        //void Print() const {}
    
        constexpr Vars_t ComputeValues(const IndependentVariables_t& v) const {
          if constexpr (std::is_same<IndependentVariables_t, InputVars_t>::value) {
            return std::tuple_cat(v, ComputeNewVars(v));
          } else {
            auto vars = FVars::input_variable_set.ComputeValues(v);
            return std::tuple_cat(vars, ComputeNewVars(vars));
          }
        }
    
        template <std::size_t I, typename F, typename Args>
        static constexpr auto eval_vars2(const F& f, const Args& a) {
          using Return_t = typename std::tuple_element<I, NewVars_t>::type;
          return Return_t{f(a)};
        }
        template <typename Funcs, typename Args, std::size_t... I>
        static constexpr auto eval_vars(const Funcs& f, const Args& a, std::index_sequence<I...>) {
          return std::make_tuple(eval_vars2<I>(std::get<I>(f), a)...);
        }
        constexpr auto ComputeNewVars(const InputVars_t& v) const {
          return eval_vars(functions, v, std::make_index_sequence<N_new_vars>{});
        }
        constexpr auto operator()(const InputVars_t& v) const { return ComputeNewVars(v); }
      };
    
      /** Definable Function Variables.
       *  Used to construct a FunctionSet from lambdas and wraps the lambda
       * arguments with a set of standard variables.
       *
       * \param FVars Function variables.
       * \param Mixin Mixin which adds the ability to declare/define more variables
       * to FVariables..
       *
       */
      template <typename FVars, template <typename> class Mixin>
      struct Definable : FVars {
        using InputVars_t = typename FVars::InputVars_t;
        using NewVars_t   = typename FVars::NewVars_t;
        //using FVars::Print;
    
        template <typename... Ts>
        Definable(Ts&&... t) : FVars(std::forward<Ts>(t)...) {}
    
        template <typename... Funcs>
        constexpr auto operator()(const Funcs&... fs) const noexcept {
          return t2t_impl(*this, std::make_tuple(fs...), std::make_index_sequence<sizeof...(fs)>{});
        }
    
        template <typename TUP, std::size_t... I>
        static constexpr auto t2t_impl(const Definable<FVars, Mixin>& fd, const TUP& t,
                                       std::index_sequence<I...>) noexcept {
          constexpr auto l_wrap = [](const auto& f) constexpr {
            return [&](const InputVars_t& args) { return f(args); };
          };
          using type = decltype(std::make_tuple(l_wrap(std::get<I>(t))...));
          return Mixin<FunctionSet<FVars, type>>(FVars{fd}, std::make_tuple(l_wrap(std::get<I>(t))...));
        }
      };
    
      /** Function Variables (to be defined with a lambda).
       *
       *  \param VarSet     is the previously defined variables
       *  \param NewVars... is a pack of new types computed from the InputVars.
       *
       *  Note the functions relating the new to the old have not yet been
       * specified..
       *
       */
      template <typename VarSet, typename... NewVars>
      struct FVariables {
        using VarSet_t               = VarSet;
        using IndependentVariables_t = typename VarSet::IndependentVariables_t;
        using InputVars_t            = typename VarSet::Vars_t;
        using NewVars_t              = std::tuple<NewVars...>;
        using Vars_t = decltype(std::tuple_cat(std::declval<InputVars_t>(), std::declval<NewVars_t>()));
        static const size_t N_input_vars = std::tuple_size<InputVars_t>::value;
        static const size_t N_new_vars   = std::tuple_size<NewVars_t>::value;
        static const size_t N_vars       = std::tuple_size<Vars_t>::value;
    
        VarSet_t input_variable_set;
    
        FVariables() noexcept {}
        FVariables(const VarSet& f) noexcept : input_variable_set(f) {}
        FVariables(const FVariables& t) noexcept : input_variable_set(t.input_variable_set) {}
    
        //void Print() const {
        //  // std::cout << __PRETTY_FUNCTION__ << "\n";
        //}
      };
    
      /** Function variable specialization where the variable is an independent variable nad thus has no
       * lambda to calculate it. The pack Vs should be left empty.
       */
      template <typename... InVars, typename... Vs>
      struct FVariables<std::tuple<InVars...>, Vs...> {
        using IndependentVariables_t = std::tuple<InVars...>;
        using InputVars_t            = IndependentVariables_t;
        using NewVars_t              = std::tuple<Vs...>;
        using Vars_t = decltype(std::tuple_cat(std::declval<InputVars_t>(), std::declval<NewVars_t>()));
        static const size_t N_input_vars = std::tuple_size<InputVars_t>::value;
        static const size_t N_new_vars   = std::tuple_size<NewVars_t>::value;
        static const size_t N_vars       = std::tuple_size<Vars_t>::value;
    
        constexpr FVariables() noexcept {}
    
        //void Print() const {
        //  // std::cout << __PRETTY_FUNCTION__ << "\n";
        //}
      };
    
      /** Mixin class for FunctionSet that adds the ability to add more variables.
       * \param M is a FunctionSet< FV<FS,NewVars>, lambdas... >
       */
      template <typename M>
      struct ConstructedFunctionSet : M {
    
        ConstructedFunctionSet(const ConstructedFunctionSet& fs) : M(fs) {}
    
        template <typename... Ts>
        constexpr ConstructedFunctionSet(Ts&&... t) noexcept : M(std::forward<Ts>(t)...) {}
    
        template <typename... Vs>
        constexpr auto make_vars() const noexcept {
          return Definable<FVariables<M, Vs...>, ConstructedFunctionSet>(M(*this));
        }
        template <typename... Vs, typename... Fs>
        constexpr auto add(const Fs&... fs) const noexcept {
          return make_vars<Vs...>()(fs...);
        }
      };
    
      template <typename T, typename... Vs>
      auto make_vars() {
        return Definable<FVariables<T, Vs...>, ConstructedFunctionSet>{};
      }
    
      /** Helper making independent vars using FVariables specialization.
       */
      template <typename... Vs>
      auto make_independent_vars() {
        return Definable<FVariables<std::tuple<Vs...>>, ConstructedFunctionSet>{}();
      }
    
      template <class...>
      struct is_FunctionSet : public std::false_type {};
    
      template <class... Vs>
      struct is_FunctionSet<ConstructedFunctionSet<Vs...>> : public std::true_type {};
    
      template <class... Vs>
      struct is_FunctionSet<FunctionSet<Vs...>> : public std::true_type {};
    
    } // namespace func
    
    
    struct XYZ_vec {
      double x = 0.0;
      double y = 0.0;
      double z = 0.0;
    };
    
    template <typename Tag>
    using Vec_Var = NamedType<XYZ_vec, Tag>;
    
    int main() {
      using namespace func;
    
      using X_coord = Var<struct X_coord_tag>;
      using Y_coord = Var<struct Y_coord_tag>;
      using Z_coord = Var<struct Z_coord_tag>;
    
      using A_coord = Var<struct A_coord_tag>;
      using B_coord = Var<struct B_coord_tag>;
      using C_coord = Var<struct C_coord_tag>;
      using D_coord = Var<struct D_coord_tag>;
      using E_coord = Var<struct E_coord_tag>;
      using F_coord = Var<struct F_coord_tag>;
    
      using XYZ_coord = Vec_Var<struct test_vector_variable_tag>;
      using XBD_coord = Vec_Var<struct xbd_vector_variable_tag>;
    
      // X_coord and Y_coord are the independent variables that must be provided
      // to compute all the subsquently defined variables.
      auto vars0 = make_independent_vars<X_coord, Y_coord>();
    
      // Add Z_coord = x+y
      auto vars1 = vars0.add<Z_coord>([](const auto& v) constexpr  {
        const auto& x = std::get<X_coord>(v);
        const auto& y = std::get<Y_coord>(v);
        return x + y;
      });
    
      auto vars2 = vars1.add<A_coord, B_coord, C_coord>(
          [](const auto& v) constexpr {
            const auto& x = std::get<X_coord>(v);
            const auto& y = std::get<Y_coord>(v);
            const auto& z = std::get<Z_coord>(v);
            return x * y * z;
          },
          [](const auto& v) constexpr {
            const auto& x = std::get<X_coord>(v);
            const auto& y = std::get<Y_coord>(v);
            const auto& z = std::get<Z_coord>(v);
            return x + y + z;
          },
          [](const auto& v) constexpr {
            const auto&  x = std::get<X_coord>(v);
            const auto&  y = std::get<Y_coord>(v);
            const auto&  z = std::get<Z_coord>(v);
            return 1.0/(x * y * z);
          });
    
    
    
      auto vars3 = vars2.add<D_coord, E_coord, F_coord>(
          [](const auto& v) constexpr {
            const auto&  x = std::get<X_coord>(v);
            const auto&  y = std::get<Y_coord>(v);
            const auto&  z = std::get<Z_coord>(v);
            const auto&  a = std::get<A_coord>(v);
            const auto&  b = std::get<B_coord>(v);
            const auto&  c = std::get<C_coord>(v);
            return x * y * z;
          },
          [](const auto& v) constexpr {
            const auto& x = std::get<X_coord>(v);
            const auto& y = std::get<Y_coord>(v);
            const auto& z = std::get<Z_coord>(v);
            const auto& a = std::get<A_coord>(v);
            const auto& b = std::get<B_coord>(v);
            const auto& c = std::get<C_coord>(v);
            return (x + y + z)/b;
          },
          [](const auto& v) constexpr {
            const auto&  x = std::get<X_coord>(v);
            const auto&  y = std::get<Y_coord>(v);
            const auto&  z = std::get<Z_coord>(v);
            const auto&  a = std::get<A_coord>(v);
            const auto&  b = std::get<B_coord>(v);
            const auto&  c = std::get<C_coord>(v);
            return c/(x * y * z);
          });
    
    
      auto values3 = vars3.ComputeValues(std::make_tuple(X_coord{2.0}, Y_coord{77.0}));
    
      // You could do this:
      //std::cout << std::get<0>(values3) << "\n";
      //std::cout << std::get<1>(values3) << "\n";
      //std::cout << std::get<2>(values3) << "\n";
      
      // but this is better:
      std::cout  << std::get<X_coord>(values3) << "\n";
      std::cout  << std::get<Y_coord>(values3) << "\n";
      std::cout  << std::get<Z_coord>(values3) << "\n";
      std::cout  << std::get<A_coord>(values3) << "\n";
      std::cout  << std::get<B_coord>(values3) << "\n";
      std::cout  << std::get<C_coord>(values3) << "\n";
      std::cout  << std::get<D_coord>(values3) << "\n";
      std::cout  << std::get<E_coord>(values3) << "\n";
      std::cout  << std::get<F_coord>(values3) << "\n";
    
      // Add a variable which is not a plain data type.
      auto vars4 = vars3.add<XYZ_coord,XBD_coord>(
          [](const auto& v) constexpr {
            const auto&  x = std::get<X_coord>(v);
            const auto&  y = std::get<Y_coord>(v);
            const auto&  z = std::get<Z_coord>(v);
            return XYZ_vec{x, y, z};
          },
          [](const auto& v) constexpr {
            const auto&  x = std::get<X_coord>(v);
            const auto&  y = std::get<B_coord>(v);
            const auto&  z = std::get<D_coord>(v);
            return XYZ_vec{x, y, z};
          }); 
    
      auto values4 = vars4.ComputeValues(std::make_tuple(X_coord{2.0}, Y_coord{77.0}));
      std::cout  << "\nThese should be the same as the first 3 variables above.\n";
      std::cout  << std::get<XYZ_coord>(values4).get().x << "\n";
      std::cout  << std::get<XYZ_coord>(values4).get().y << "\n";
      std::cout  << std::get<XYZ_coord>(values4).get().z << "\n";
    
      std::cout  << "\nXBD\n";
      std::cout  << std::get<XBD_coord>(values4).get().x << "\n";
      std::cout  << std::get<XBD_coord>(values4).get().y << "\n";
      std::cout  << std::get<XBD_coord>(values4).get().z << "\n";
    
    }