This is a follow-up question for [Two dimensional bicubic interpolation implementation in C](https://codereview.stackexchange.com/q/263183/231235) and [A recursive_transform Template Function with Unwrap Level for Various Type Arbitrary Nested Iterable Implementation in C++](https://codereview.stackexchange.com/q/257757/231235). Besides the C version code, I am attempting to make a C++ version bicubic interpolation function `bicubicInterpolation` which can be applied on two dimensional nested vectors `std::vector<std::vector<>>` structure.

Example input matrix:

```
1       1       1
1       100     1
1       1       1
```

Output matrix (bicubic interpolation result from the input matrix above):

```
1       1       1       1       1       1       1       1       1       1       1       1
1       9       19      27      30      27      19      9       1       0       0       0
1       19      39      56      63      56      39      19      1       0       0       0
1       27      56      79      89      79      56      27      1       0       0       0
1       30      63      89      100     89      63      30      1       0       0       0
1       27      56      79      89      79      56      27      1       0       0       0
1       19      39      56      63      56      39      19      1       0       0       0
1       9       19      27      30      27      19      10      1       0       0       0
1       1       1       1       1       1       1       1       1       1       1       1
1       0       0       0       0       0       0       0       1       3       2       1
1       0       0       0       0       0       0       0       1       2       2       1
1       0       0       0       0       0       0       0       1       1       1       1
```

**The experimental implementation**

- namespace: `TinyDIP`
  
- `bicubicInterpolation` function implementation:
  
  ```C++
  constexpr auto bicubicInterpolation(const int newSizeX, const int newSizeY)
  {
      auto output = Image<ElementT>(newSizeX, newSizeY);
      auto ratiox = (float)this->getSizeX() / (float)newSizeX;
      auto ratioy = (float)this->getSizeY() / (float)newSizeY;
      
      for (size_t y = 0; y < newSizeY; y++)
      {
          for (size_t x = 0; x < newSizeX; x++)
          {
              float xMappingToOrigin = (float)x * ratiox;
              float yMappingToOrigin = (float)y * ratioy;
              float xMappingToOriginFloor = floor(xMappingToOrigin);
              float yMappingToOriginFloor = floor(yMappingToOrigin);
              float xMappingToOriginFrac = xMappingToOrigin - xMappingToOriginFloor;
              float yMappingToOriginFrac = yMappingToOrigin - yMappingToOriginFloor;
              
              ElementT ndata[4 * 4];
              for (int ndatay = -1; ndatay < 2; ndatay++)
              {
                  for (int ndatax = -1; ndatax < 2; ndatax++)
                  {
                      ndata[(ndatay + 1) * 4 + (ndatax + 1)] = this->get(
                          clip(xMappingToOriginFloor + ndatax, 0, this->getSizeX() - 1), 
                          clip(yMappingToOriginFloor + ndatay, 0, this->getSizeY() - 1));
                  }
                  
              }
              output.set(x, y, bicubicPolate(ndata, xMappingToOriginFrac, yMappingToOriginFrac));
          }
      }
      return output;
  }
  ```

- Helper functions for `bicubicInterpolation` function:
  
  ```C++
  template<class InputT>
  constexpr auto bicubicPolate(const ElementT* const ndata, const InputT fracx, const InputT fracy)
  {
      auto x1 = cubicPolate( ndata[0], ndata[1], ndata[2], ndata[3], fracx );
      auto x2 = cubicPolate( ndata[4], ndata[5], ndata[6], ndata[7], fracx );
      auto x3 = cubicPolate( ndata[8], ndata[9], ndata[10], ndata[11], fracx );
      auto x4 = cubicPolate( ndata[12], ndata[13], ndata[14], ndata[15], fracx );
  
      return clip(cubicPolate( x1, x2, x3, x4, fracy ), 0.0, 255.0);
  }
  
  template<class InputT1, class InputT2>
  constexpr auto cubicPolate(const InputT1 v0, const InputT1 v1, const InputT1 v2, const InputT1 v3, const InputT2 fracy)
  {
      auto A = (v3-v2)-(v0-v1);
      auto B = (v0-v1)-A;
      auto C = v2-v0;
      auto D = v1;
      return D + fracy * (C + fracy * (B + fracy * A));
  }
  
  template<class InputT1, class InputT2, class InputT3>
  constexpr auto clip(const InputT1 input, const InputT2 lowerbound, const InputT3 upperbound)
  {
      if (input < lowerbound)
      {
          return static_cast<InputT1>(lowerbound);
      }
      if (input > upperbound)
      {
          return static_cast<InputT1>(upperbound);
      }
      return input;
  }
  ```
  
- `Image` template class implementation (`image.h`):
  
  ```C++
  /* Develop by Jimmy Hu */
  
  #ifndef Image_H
  #define Image_H
  
  #include <algorithm>
  #include <array>
  #include <chrono>
  #include <complex>
  #include <concepts>
  #include <functional>
  #include <iostream>
  #include <iterator>
  #include <list>
  #include <numeric>
  #include <string>
  #include <type_traits>
  #include <variant>
  #include <vector>
  #include "basic_functions.h"
  
  namespace TinyDIP
  {
      template <typename ElementT>
      class Image
      {
      public:
          Image()
          {
          }
  
          Image(const int newWidth, const int newHeight)
          {
              this->image_data.resize(newHeight);
              for (int i = 0; i < newHeight; ++i) {
                  this->image_data[i].resize(newWidth);
              }
              this->image_data = recursive_transform<2>(this->image_data, [](ElementT element) { return ElementT{}; });
              return;
          }
  
          Image(const int newWidth, const int newHeight, ElementT initVal)
          {
              this->image_data.resize(newHeight);
              for (int i = 0; i < newHeight; ++i) {
                  this->image_data[i].resize(newWidth);
              }
              this->image_data = recursive_transform<2>(this->image_data, [initVal](ElementT element) { return initVal; });
              return;
          }
  
          Image(const std::vector<std::vector<ElementT>>& input)
          {
              this->image_data = recursive_transform<2>(input, [](ElementT element) {return element; } ); //  Deep copy
              return;
          }
  
          template<class OutputT>
          constexpr auto cast()
          {
              return this->transform([](ElementT element) { return static_cast<OutputT>(element); });
          }
  
          constexpr auto get(const unsigned int locationx, const unsigned int locationy)
          {
              return this->image_data[locationy][locationx];
          }
  
          constexpr auto set(const unsigned int locationx, const unsigned int locationy, const ElementT& element)
          {
              this->image_data[locationy][locationx] = element;
              return *this;
          }
  
          template<class InputT>
          constexpr auto set(const unsigned int locationx, const unsigned int locationy, const InputT& element)
          {
              this->image_data[locationy][locationx] = static_cast<ElementT>(element);
              return *this;
          }
  
          constexpr auto getSizeX()
          {
              return this->image_data[0].size();
          }
  
          constexpr auto getSizeY()
          {
              return this->image_data.size();
          }
  
          constexpr auto getData()
          {
              return this->transform([](ElementT element) { return element; });   //  Deep copy
          }
  
          void print()
          {
              for (auto& row_element : this->toString())
              {
                  for (auto& element : row_element)
                  {
                      std::cout << element << "\t";
                  }
                  std::cout << "\n";
              }
              std::cout << "\n";
              return;
          }
  
          constexpr auto toString()
          {
              return this->transform([](ElementT element) { return std::to_string(element); });
          }
  
          Image<ElementT>& operator=(Image<ElementT> const& input)  //  Copy Assign
          {
              this->image_data = input.getData();
              return *this;
          }
  
          Image<ElementT>& operator=(Image<ElementT>&& other)       //  Move Assign
          {
              this->image_data = std::move(other.image_data);
              std::cout << "move assigned\n";
              return *this;
          }
  
          Image(const Image<ElementT> &input)                      //  Copy Constructor
          {
              this->image_data = input.getData();
          }
  
          /*    Move Constructor
           */
          Image(Image<ElementT> &&input) : image_data(std::move(input.image_data))
          {
          }
          
      private:
          std::vector<std::vector<ElementT>> image_data;
  
          template<class F>
          constexpr auto transform(const F& f)
          {
              return recursive_transform<2>(this->image_data, f);
          }
      };
  }
  
  #endif
  ```
  
- `base_types.h`: The base types
  
  ```C++
  /* Develop by Jimmy Hu */
  
  #ifndef BASE_H
  #define BASE_H
  
  #include <cmath>
  #include <cstdbool>
  #include <cstdio>
  #include <cstdlib>
  #include <string>
  
  #define MAX_PATH 256
  #define FILE_ROOT_PATH "./"
  
  #define True true
  #define False false
  
  typedef unsigned char BYTE;
  
  typedef struct RGB
  {
      unsigned char channels[3];
  } RGB;
  
  typedef BYTE GrayScale;
  
  typedef struct HSV
  {
      long double channels[3];    //  Range: 0 <= H < 360, 0 <= S <= 1, 0 <= V <= 255
  }HSV;
  
  #endif
  ```
  
- `basic_functions.h`: The basic functions
  
  ```C++
  /* Develop by Jimmy Hu */
  
  #ifndef BasicFunctions_H
  #define BasicFunctions_H
  
  #include <algorithm>
  #include <array>
  #include <cassert>
  #include <chrono>
  #include <complex>
  #include <concepts>
  #include <deque>
  #include <execution>
  #include <exception>
  #include <functional>
  #include <iostream>
  #include <iterator>
  #include <list>
  #include <map>
  #include <mutex>
  #include <numeric>
  #include <optional>
  #include <ranges>
  #include <stdexcept>
  #include <string>
  #include <tuple>
  #include <type_traits>
  #include <utility>
  #include <variant>
  #include <vector>
  
  namespace TinyDIP
  {
      template<typename T>
      concept is_back_inserterable = requires(T x)
      {
          std::back_inserter(x);
      };
  
      template<typename T>
      concept is_inserterable = requires(T x)
      {
          std::inserter(x, std::ranges::end(x));
      };
  
      //  recursive_invoke_result_t implementation
      template<typename, typename>
      struct recursive_invoke_result { };
  
      template<typename T, std::invocable<T> F>
      struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };
  
      template<typename F, template<typename...> typename Container, typename... Ts>
      requires (
          !std::invocable<F, Container<Ts...>>&&
          std::ranges::input_range<Container<Ts...>>&&
          requires { typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type; })
          struct recursive_invoke_result<F, Container<Ts...>>
      {
          using type = Container<typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type>;
      };
  
      template<typename F, typename T>
      using recursive_invoke_result_t = typename recursive_invoke_result<F, T>::type;
  
      //  recursive_transform implementation (the version with unwrap_level)
      template<std::size_t unwrap_level = 1, class T, class F>
      constexpr auto recursive_transform(const T& input, const F& f)
      {
          if constexpr (unwrap_level > 0)
          {
              recursive_invoke_result_t<F, T> output{};
              std::ranges::transform(
                  std::ranges::cbegin(input),
                  std::ranges::cend(input),
                  std::inserter(output, std::ranges::end(output)),
                  [&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); }
              );
              return output;
          }
          else
          {
              return f(input);
          }
      }
  
      template<std::size_t dim, class T>
      constexpr auto n_dim_vector_generator(T input, std::size_t times)
      {
          if constexpr (dim == 0)
          {
              return input;
          }
          else
          {
              auto element = n_dim_vector_generator<dim - 1>(input, times);
              std::vector<decltype(element)> output(times, element);
              return output;
          }
      }
  
      template<std::size_t dim, std::size_t times, class T>
      constexpr auto n_dim_array_generator(T input)
      {
          if constexpr (dim == 0)
          {
              return input;
          }
          else
          {
              auto element = n_dim_array_generator<dim - 1, times>(input);
              std::array<decltype(element), times> output;
              std::fill(std::ranges::begin(output), std::ranges::end(output), element);
              return output;
          }
      }
  
      template<std::size_t dim, class T>
      constexpr auto n_dim_deque_generator(T input, std::size_t times)
      {
          if constexpr (dim == 0)
          {
              return input;
          }
          else
          {
              auto element = n_dim_deque_generator<dim - 1>(input, times);
              std::deque<decltype(element)> output(times, element);
              return output;
          }
      }
  
      template<std::size_t dim, class T>
      constexpr auto n_dim_list_generator(T input, std::size_t times)
      {
          if constexpr (dim == 0)
          {
              return input;
          }
          else
          {
              auto element = n_dim_list_generator<dim - 1>(input, times);
              std::list<decltype(element)> output(times, element);
              return output;
          }
      }
  
      template<std::size_t dim, template<class...> class Container = std::vector, class T>
      constexpr auto n_dim_container_generator(T input, std::size_t times)
      {
          if constexpr (dim == 0)
          {
              return input;
          }
          else
          {
              return Container(times, n_dim_container_generator<dim - 1, Container, T>(input, times));
          }
      }
  }
  #endif
  ```
  
**The full testing code**

The [grayscale](https://en.wikipedia.org/wiki/Grayscale) type data has been tested here.

```C++
/* Develop by Jimmy Hu */

#include "base_types.h"
#include "basic_functions.h"
#include "image.h"

void bicubicInterpolationTest();

int main()
{
    bicubicInterpolationTest();
    return 0;
}

void bicubicInterpolationTest()
{
    TinyDIP::Image<GrayScale> image1(3, 3, 1);
    std::cout << "Width: " + std::to_string(image1.getSizeX()) + "\n";
    std::cout << "Height: " + std::to_string(image1.getSizeY()) + "\n";
    image1 = image1.set(1, 1, 100);
    image1.print();

    auto image2 = image1.bicubicInterpolation(12, 12);
    image2.print();
}
```

All suggestions are welcome.

The summary information:

- Which question it is a follow-up to?
  
  [Two dimensional bicubic interpolation implementation in C](https://codereview.stackexchange.com/q/263183/231235) and
  
  [A recursive_transform Template Function with Unwrap Level for Various Type Arbitrary Nested Iterable Implementation in C++](https://codereview.stackexchange.com/q/257757/231235).
  
- What changes has been made in the code since last question?
  
  I am attempting to make a C++ version bicubic interpolation function `bicubicInterpolation` which can be applied on two dimensional nested vectors `std::vector<std::vector<>>` structure.
  
- Why a new review is being asked for?
  
  If there is any possible improvement, please let me know.