Ad
Code
Diff
  • #include <string>
    #include <string_view>
    #include <range/v3/view/join.hpp>
    #include <range/v3/range/conversion.hpp>
    
    std::string Jointup(const std::vector<std::string>& name, std::string_view delim = " , ")
    {
        return name | ranges::views::join(delim) | ranges::to<std::string>;
    }
    • #include <string>
    • #include <string_view>
    • #include <vector>
    • #include <numeric>
    • #include <sstream>
    • #include <range/v3/view/join.hpp>
    • #include <range/v3/range/conversion.hpp>
    • std::string Jointup(const std::vector<std::string> &name , const std::string_view delim = " , ")
    • std::string Jointup(const std::vector<std::string>& name, std::string_view delim = " , ")
    • {
    • if (name.empty()) return "";
    • std::stringstream ss;
    • ss << name.front();
    • std::for_each(std::next(name.cbegin()), name.cend(), [&ss, delim](auto const& s) { ss << delim << s; });
    • return ss.str();
    • return name | ranges::views::join(delim) | ranges::to<std::string>;
    • }
Fundamentals
Language Features
Metaprogramming
Recursion

Background

This task assumes familiarity with template metaprogramming and std::ratio.

Overview

In this task, you’ll represent and manipulate polynomials through C++'s type system by means of template metaprogramming and the std::ratio type. The key idea is to define a Polynomial class template, where each term in the polynomial is represented by a coefficient using std::ratio.

Polynomial Structure

Create a class Polynomial that takes, as a parameter pack, a series of std::ratios representing the coefficients. Each ratio corresponds to the coefficient of a term, starting from the highest degree on the left and down to the constant term on the right.

For example, the polynomial

$ 10x^3 + 2.5x^2 + 5x + 0.5 $

Is represented by the type

Polynomial<std::ratio<10>, std::ratio<5, 2>, std::ratio<5>, std::ratio<1, 2>>

Each std::ratio represents a coefficient (e.g., std::ratio<5, 2> represents 2.5).

The rightmost ratio is the constant term (degree 0), and the terms to the left represent higher degree terms (e.g., the std::ratio<10> stands for $ 10x^3 $ here).

One important rule: your Polynomials shouldn't have leading zero coefficients. After all, $ 0x^4 + 0x^3 + x^2 + x $ is simply $ x^2 + x $. The only exception is the degree 0 polynomial (i.e., constant), where a lone zero is valid and is represented as Polynomial<std::ratio<0>>.

Objective

You'll implement three main operations (as type aliases) for this compile-time polynomial class: differentiation, integration, and addition.

  1. Polynomial<...>::differentiated

    Type alias to the polynomial type representing the result of differentiating the current.

    Example:

    Polynomial<std::ratio<30>, std::ratio<20>, std::ratio<10>>::differentiated
    // Differentiating 30x^2 + 20x + 10. Alias for type Polynomial<std::ratio<60>, std::ratio<20>>.
    
    Polynomial<std::ratio<60>, std::ratio<20>>::differentiated
    // Differentiating 60x + 20. Alias for type Polynomial<std::ratio<60>>.
    
    Polynomial<std::ratio<60>>::differentiated
    // Differentiating 60. Alias for type Polynomial<std::ratio<0>> (i.e., constant term 0).
    
  2. Polynomial<...>::integrated<std::ratio<...>>

    Type alias to the polynomial type representing the result of integrating the current, including an integration constant (a std::ratio passed as a template argument).

    Example:

    Polynomial<std::ratio<10>>::integrated<std::ratio<20>>
    // 20 is the integration constant.
    // Alias for type Polynomial<std::ratio<10>, std::ratio<20>>.
    
    Polynomial<std::ratio<10>, std::ratio<20>>::integrated<std::ratio<5, 2>> 
    // 2.5 is the integration constant.
    // Alias for type Polynomial<std::ratio<5>, std::ratio<20>, std::ratio<5, 2>>.
    

    You may assume that that the template argument provided to integrated by the tests will always be a std::ratio. Hence, there is no need to validate the type.

  3. Polynomial<...>::add<Polynomial<...>>

    Type alias template that accepts, as a template argument, another Polynomial, and represents the polynomial type resulting from adding the two together.

    The polynomials may possibly be of different degrees.

    Example:

    Polynomial<std::ratio<7>, std::ratio<5>>::add<Polynomial<std::ratio<3>, std::ratio<1>>>
    // (7x + 5) + (3x + 1). Alias for type Polynomial<std::ratio<10>, std::ratio<6>>.
    
    Polynomial<std::ratio<10>, std::ratio<20>>::add<Polynomial<std::ratio<30>>>
    // (10x + 20) + (30). Alias for type Polynomial<std::ratio<10>, std::ratio<50>>.
    
    Polynomial<std::ratio<25>, std::ratio<3>>::add<Polynomial<std::ratio<-25>, std::ratio<10>>>
    // (25x + 3) + (-25x + 10). Alias for type Polynomial<std::ratio<13>>.
    // (NOT Polynomial<std::ratio<0>, std::ratio<13>>)!
    

    Remember, the coefficients can be negative, and it is your responsibility to ensure your resulting polynomial doesn't have leading zeroes after addition.

Assumptions

  • Single-variable polynomials: All polynomials involve only one variable (x).
  • No omitted terms: All coefficients are explicitly represented, even if zero. A polynomial of degree n will have all n + 1 coefficients embedded in the type. However, there should be no unnecessary leading zeroes.

Note

The tests are forgiving in the sense that unimplemented parts of your solution will be registered as a failed test rather than a compilation error. Also, any two std::ratios representing the same quantity will be marked as correct. So, std::ratio<3> and std::ratio<6, 2> are equivalent when testing. If the tests time out, it's likely because your solution contains unintendeed infinite recursion.

Code
Diff
  • #include <ratio>
    #include <utility>
    #include <cstddef>
    #include <tuple>
    #include <type_traits>
    
    template <typename... Coeffs>
    struct Polynomial;
    
    // Helpers to help "trim" Polynomials of leading zeroes.
    template<typename...> struct trim_impl;
    template<> struct trim_impl<> : std::common_type<Polynomial<>> {};
    template<typename R1, typename... Rs> struct trim_impl<R1, Rs...> : std::conditional<sizeof...(Rs) and std::is_same_v<R1, std::ratio<0>>, typename trim_impl<Rs...>::type, Polynomial<R1, Rs...>> {};
    
    template <typename... Coeffs>
    struct Polynomial {
        // Degree of Polynomial.
        static constexpr auto degree = sizeof...(Coeffs) - 1;
        // Alias for type of i-th std::ratio in the parameter pack. Pre-C++26 workaround for pack indexing.
        template <std::size_t I> using at = std::tuple_element_t<I, std::tuple<Coeffs...>>;
        // Alias for Polynomial type with leading zeroes removed.
        using trim = typename trim_impl<Coeffs...>::type;
        // Pads by adding leading zeroes until the parameter pack becomes of size I.
        template <std::size_t... Is> static constexpr auto pad_impl(std::index_sequence<Is...> seq) -> Polynomial<std::ratio<Is & 0>..., Coeffs...>;
        template <std::size_t I>     static constexpr auto pad = decltype(pad_impl(std::make_index_sequence<std::max(I, degree) - degree>{})){};
        
        // Private (but not really) helpers:
        template <typename... R1, typename... Rs> static constexpr auto add_impl(Polynomial<R1...>, Polynomial<Rs...>)
            -> Polynomial<std::ratio_add<R1, Rs>...>;
    
        template <typename... Rs, std::size_t... Is> static constexpr auto diff_impl(std::index_sequence<Is...>)
            -> Polynomial<std::ratio_multiply<at<Is>, std::ratio<degree - Is>>...>;
        
        template <typename C, std::size_t... Is> static constexpr auto integrate_impl(std::index_sequence<Is...>)
            -> Polynomial<std::ratio_divide<Coeffs, std::ratio<degree + 1 - Is>>..., C>;
            
        // Public aliases.
        template <typename Other> using add = typename decltype(add_impl(pad<Other::degree>, Other::template pad<degree>))::trim;
        using differentiated = decltype(diff_impl(std::make_index_sequence<degree + not degree>{}));
        template<typename C> using integrated = typename decltype(integrate_impl<C>(std::make_index_sequence<degree + 1>{}))::trim;
    };
    • #include <ratio>
    • #include <utility>
    • #include <cstddef>
    • #include <tuple>
    • #include <type_traits>
    • // INTIAL SOLUTION TEMPLATE:
    • template <typename... Coeffs>
    • struct Polynomial;
    • // Helpers to help "trim" Polynomials of leading zeroes.
    • template<typename...> struct trim_impl;
    • template<> struct trim_impl<> : std::common_type<Polynomial<>> {};
    • template<typename R1, typename... Rs> struct trim_impl<R1, Rs...> : std::conditional<sizeof...(Rs) and std::is_same_v<R1, std::ratio<0>>, typename trim_impl<Rs...>::type, Polynomial<R1, Rs...>> {};
    • template <typename... Coeffs>
    • struct Polynomial {
    • // using differentiated = ???;
    • // template <???> using add = ???;
    • // template <???> using integrated = ???;
    • };
    • // Degree of Polynomial.
    • static constexpr auto degree = sizeof...(Coeffs) - 1;
    • // Alias for type of i-th std::ratio in the parameter pack. Pre-C++26 workaround for pack indexing.
    • template <std::size_t I> using at = std::tuple_element_t<I, std::tuple<Coeffs...>>;
    • // Alias for Polynomial type with leading zeroes removed.
    • using trim = typename trim_impl<Coeffs...>::type;
    • // Pads by adding leading zeroes until the parameter pack becomes of size I.
    • template <std::size_t... Is> static constexpr auto pad_impl(std::index_sequence<Is...> seq) -> Polynomial<std::ratio<Is & 0>..., Coeffs...>;
    • template <std::size_t I> static constexpr auto pad = decltype(pad_impl(std::make_index_sequence<std::max(I, degree) - degree>{})){};
    • // Private (but not really) helpers:
    • template <typename... R1, typename... Rs> static constexpr auto add_impl(Polynomial<R1...>, Polynomial<Rs...>)
    • -> Polynomial<std::ratio_add<R1, Rs>...>;
    • template <typename... Rs, std::size_t... Is> static constexpr auto diff_impl(std::index_sequence<Is...>)
    • -> Polynomial<std::ratio_multiply<at<Is>, std::ratio<degree - Is>>...>;
    • template <typename C, std::size_t... Is> static constexpr auto integrate_impl(std::index_sequence<Is...>)
    • -> Polynomial<std::ratio_divide<Coeffs, std::ratio<degree + 1 - Is>>..., C>;
    • // Public aliases.
    • template <typename Other> using add = typename decltype(add_impl(pad<Other::degree>, Other::template pad<degree>))::trim;
    • using differentiated = decltype(diff_impl(std::make_index_sequence<degree + not degree>{}));
    • template<typename C> using integrated = typename decltype(integrate_impl<C>(std::make_index_sequence<degree + 1>{}))::trim;
    • };
Fundamentals
Language Features
Metaprogramming
Recursion

Background

This task assumes familiarity with template metaprogramming and std::ratio.

Overview

In this task, you’ll represent and manipulate polynomials through C++'s type system by means of template metaprogramming and the std::ratio type. The key idea is to define a Polynomial class template, where each term in the polynomial is represented by a coefficient using std::ratio.

Polynomial Structure

Create a class Polynomial that takes, as a parameter pack, a series of std::ratios representing the coefficients. Each ratio corresponds to the coefficient of a term, starting from the highest degree on the left and down to the constant term on the right.

For example, the polynomial

$ 10x^3 + 2.5x^2 + 5x + 0.5 $

Is represented by the type

Polynomial<std::ratio<10>, std::ratio<5, 2>, std::ratio<5>, std::ratio<1, 2>>

Each std::ratio represents a coefficient (e.g., std::ratio<5, 2> represents 2.5).

The rightmost ratio is the constant term (degree 0), and the terms to the left represent higher degree terms (e.g., the std::ratio<10> stands for $ 10x^3 $ here).

One important rule: your Polynomials shouldn't have leading zero coefficients. After all, $ 0x^4 + 0x^3 + x^2 + x $ is simply $ x^2 + x $. The only exception is the degree 0 polynomial (i.e., constant), where a lone zero is valid and is represented as Polynomial<std::ratio<0>>.

Objective

You'll implement three main operations (as type aliases) for this compile-time polynomial class: differentiation, integration, and addition.

  1. Polynomial<...>::differentiated

    Type alias to the polynomial type representing the result of differentiating the current.

    Example:

    Polynomial<std::ratio<30>, std::ratio<20>, std::ratio<10>>::differentiated
    // Differentiating 30x^2 + 20x + 10. Alias for type Polynomial<std::ratio<60>, std::ratio<20>>.
    
    Polynomial<std::ratio<60>, std::ratio<20>>::differentiated
    // Differentiating 60x + 20. Alias for type Polynomial<std::ratio<60>>.
    
    Polynomial<std::ratio<60>>::differentiated
    // Differentiating 60. Alias for type Polynomial<std::ratio<0>> (i.e., constant term 0).
    
  2. Polynomial<...>::integrated<std::ratio<...>>

    Type alias to the polynomial type representing the result of integrating the current, including an integration constant (a std::ratio passed as a template argument).

    Example:

    Polynomial<std::ratio<10>>::integrated<std::ratio<20>>
    // 20 is the integration constant.
    // Alias for type Polynomial<std::ratio<10>, std::ratio<20>>.
    
    Polynomial<std::ratio<10>, std::ratio<20>>::integrated<std::ratio<5, 2>> 
    // 2.5 is the integration constant.
    // Alias for type Polynomial<std::ratio<5>, std::ratio<20>, std::ratio<5, 2>>.
    

    You may assume that that the template argument provided to integrated by the tests will always be a std::ratio. Hence, there is no need to validate the type.

  3. Polynomial<...>::add<Polynomial<...>>

    Type alias template that accepts, as a template argument, another Polynomial, and represents the polynomial type resulting from adding the two together.

    The polynomials may possibly be of different degrees.

    Example:

    Polynomial<std::ratio<7>, std::ratio<5>>::add<Polynomial<std::ratio<3>, std::ratio<1>>>
    // (7x + 5) + (3x + 1). Alias for type Polynomial<std::ratio<10>, std::ratio<6>>.
    
    Polynomial<std::ratio<10>, std::ratio<20>>::add<Polynomial<std::ratio<30>>>
    // (10x + 20) + (30). Alias for type Polynomial<std::ratio<10>, std::ratio<50>>.
    
    Polynomial<std::ratio<25>, std::ratio<3>>::add<Polynomial<std::ratio<-25>, std::ratio<10>>>
    // (25x + 3) + (-25x + 10). Alias for type Polynomial<std::ratio<13>>.
    // (NOT Polynomial<std::ratio<0>, std::ratio<13>>)!
    

    Remember, the coefficients can be negative, and it is your responsibility to ensure your resulting polynomial doesn't have leading zeroes after addition.

Assumptions

  • Single-variable polynomials: All polynomials involve only one variable (x).
  • No omitted terms: All coefficients are explicitly represented, even if zero. A polynomial of degree n will have all n + 1 coefficients embedded in the type. However, there should be no unnecessary leading zeroes.

Note

The tests are forgiving in the sense that unimplemented parts of your solution will be registered as a failed test rather than a compilation error. Also, any two std::ratios representing the same quantity will be marked as correct. So, std::ratio<3> and std::ratio<6, 2> are equivalent when testing. If the tests time out, it's likely because your solution contains unintendeed infinite recursion.

#include <ratio>

// INTIAL SOLUTION TEMPLATE:

template <typename... Coeffs>
struct Polynomial {
  // using differentiated = ???;
  // template <???> using add = ???;
  // template <???> using integrated = ???;
};

Constructor situation is a bit better now. Thunks are now copyable and are more flexibly constructible.

Background

C++ recreation of the Thunk API from this Kata.

Test cases are included to verify if it works as expected. The full extent of the class is used in those test cases.

Code
Diff
  • ;
    • //
    • ;

Refactors + bug fixes + using std::variant now.

C++ recreation of the Thunk API from this Kata.

Test cases are included to verify if it works as expected.

The usage of the class happens in the test cases.

TODO:

Next I want to allow copying. The constructor situation is making this so difficult :/

Code
Diff
  • //
    • #include <memory>
    • #include <optional>
    • #include <functional>
    • #include <type_traits>
    • // Forward declaration.
    • template <typename T>
    • struct Thunk;
    • // Factory for Thunks. C++ disallows non-templated static methods for class templates,
    • // so this is its own namespace.
    • namespace MakeThunk {
    • template <typename T>
    • auto now(T&& t) -> Thunk<std::decay_t<T>>
    • {
    • return { std::forward<T>(t), {} };
    • }
    • template <typename Func>
    • auto delay(Func&& func) -> Thunk<std::decay_t<std::invoke_result_t<Func>>>
    • {
    • return { {}, std::forward<Func>(func) };
    • }
    • }
    • template <typename T>
    • struct Thunk {
    • public:
    • Thunk(std::optional<T> value = {}, std::function<T()> supplier = {}) : data{ std::make_shared<ThunkData>(ThunkData{ value, supplier }) } {}
    • T get() const
    • {
    • return data->get_data();
    • }
    • template <typename Func>
    • auto chain(const Func& func) const -> Thunk<std::decay_t<std::invoke_result_t<Func, T>>>
    • {
    • // Data is a shared pointer that is copied in the closure ==> extends the lifetime of the data
    • // as needed.
    • return MakeThunk::delay([=, shared_data=data]() { return func(shared_data->get_data()); });
    • }
    • private:
    • // Actual private API of Thunk.
    • struct ThunkData
    • {
    • T get_data()
    • {
    • if (not value) value = std::exchange(supplier, {})();
    • return *value;
    • }
    • std::optional<T> value{};
    • std::function<T()> supplier{};
    • };
    • // Data is in a shared pointer ==> prevents lifetime issues.
    • std::shared_ptr<ThunkData> data{};
    • };
    • //

C++ recreation of the Thunk API from this Kata.

Test cases are included to verify if it works as expected.

#include <memory>
#include <optional>
#include <functional>
#include <type_traits>

// Forward declaration.
template <typename T>
struct Thunk;

// Factory for Thunks. C++ disallows non-templated static methods for class templates,
// so this is its own namespace.
namespace MakeThunk {
    template <typename T>
    auto now(T&& t) -> Thunk<std::decay_t<T>>
    {
        return { std::forward<T>(t), {} };
    }
  
    template <typename Func>
    auto delay(Func&& func) -> Thunk<std::decay_t<std::invoke_result_t<Func>>> 
    {
        return { {}, std::forward<Func>(func) };
    }
}

template <typename T>
struct Thunk {
public:
    Thunk(std::optional<T> value = {}, std::function<T()> supplier = {}) : data{ std::make_shared<ThunkData>(ThunkData{ value, supplier }) } {}
  
    T get() const
    {
        return data->get_data();
    }
    
    template <typename Func>
    auto chain(const Func& func) const -> Thunk<std::decay_t<std::invoke_result_t<Func, T>>> 
    {
        // Data is a shared pointer that is copied in the closure ==> extends the lifetime of the data
        // as needed.
        return MakeThunk::delay([=, shared_data=data]() { return func(shared_data->get_data()); });
    }
private:
    // Actual private API of Thunk.
    struct ThunkData
    {
        T get_data()
        {
            if (not value) value = std::exchange(supplier, {})();
            return *value;
        }

        std::optional<T> value{};
        std::function<T()> supplier{};
    };
  
    // Data is in a shared pointer ==> prevents lifetime issues.
    std::shared_ptr<ThunkData> data{};
};

The logs in the test output should hopefully make a case that recommending this syntax in the guidelines can be very problematic: std::bind(distrib, engine).

Depending on the testing code, this can actually turn into just glorified fixed-case testing.

// Look through the test output logs and expand to reveal commentary. No need to check the test code.