Repository: xtensor-stack/xtensor (language: python)

Issue:
reshape view doesn't infer shape from -1
https://github.com/xtensor-stack/xtensor/issues/926

behold my code:
```
int main() {                   
    xt::xarray<float> a = xt::zeros<float>({3, 3, 3});
    auto b = xt::reshape_view(a, {3, -1});
    cout << xt::adapt(a.shape()) << endl;
    cout << xt::adapt(b.shape()) << endl;
}
```
and it's unexpected output
```
{3, 3, 3}
{                   3, 18446744073709551615}
```
While nowhere in the documentation does it claim to handle -1 as numpy does, and this is probably just my fault for assuming this, it would be very convenient if it did.


Discussion / hints:
As method `reshape` seems to support having one entry `-1`, see [docs](https://xtensor.readthedocs.io/en/latest/quickref/basic.html?highlight=reshape#reshape). In my opinion `reshape_view` should also support this. However, in your case `-1` seems to be casted to a signed integer. So I think this is a bug. But maybe others disagree?
@tdegeus  I agree with @JosephSummerhays1 I've done this several times and it's a nightmare to find when `xt::xarray<T>::reshape` does it. I also find myself doing `xt::reshape_view(data, {data.size() / lastAxisLength, lastAxisLength})` this is pretty ugly.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c87df8d70..ae11f8c5c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -170,7 +170,7 @@ jobs:
           ninja
 
     - name: Configure using CMake
-      run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DBUILD_TESTS=ON -G Ninja
+      run: cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DBUILD_TESTS=ON -G Ninja
 
     - name: Install
       working-directory: build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 693be0142..92c886dfc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,7 +7,7 @@
 # The full license is in the file LICENSE, distributed with this software. #
 ############################################################################
 
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
 project(xtensor CXX)
 
 set(XTENSOR_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt
index 412d03644..c5fa3c407 100644
--- a/benchmark/CMakeLists.txt
+++ b/benchmark/CMakeLists.txt
@@ -6,7 +6,7 @@
 # The full license is in the file LICENSE, distributed with this software. #
 ############################################################################
 
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
 
 if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
     project(xtensor-benchmark)
diff --git a/include/xtensor/xstrided_view.hpp b/include/xtensor/xstrided_view.hpp
index 35689774c..f16ace56d 100644
--- a/include/xtensor/xstrided_view.hpp
+++ b/include/xtensor/xstrided_view.hpp
@@ -807,6 +807,53 @@ namespace xt
         );
     }
 
+    namespace detail
+    {
+        template <typename S>
+        struct rebind_shape;
+
+        template <std::size_t... X>
+        struct rebind_shape<xt::fixed_shape<X...>>
+        {
+            using type = xt::fixed_shape<X...>;
+        };
+
+        template <class S>
+        struct rebind_shape
+        {
+            using type = rebind_container_t<size_t, S>;
+        };
+
+        template <
+            class S,
+            std::enable_if_t<std::is_signed<get_value_type_t<typename std::decay<S>::type>>::value, bool> = true>
+        inline void recalculate_shape_impl(S& shape, size_t size)
+        {
+            using value_type = get_value_type_t<typename std::decay_t<S>>;
+            XTENSOR_ASSERT(std::count(shape.cbegin(), shape.cend(), -1) <= 1);
+            auto iter = std::find(shape.begin(), shape.end(), -1);
+            if (iter != std::end(shape))
+            {
+                const auto total = std::accumulate(shape.cbegin(), shape.cend(), -1, std::multiplies<int>{});
+                const auto missing_dimension = size / total;
+                (*iter) = static_cast<value_type>(missing_dimension);
+            }
+        }
+
+        template <
+            class S,
+            std::enable_if_t<!std::is_signed<get_value_type_t<typename std::decay<S>::type>>::value, bool> = true>
+        inline void recalculate_shape_impl(S&, size_t)
+        {
+        }
+
+        template <class S>
+        inline auto recalculate_shape(S&& shape, size_t size)
+        {
+            return recalculate_shape_impl(shape, size);
+        }
+    }
+
     template <layout_type L = XTENSOR_DEFAULT_TRAVERSAL, class E, class S>
     inline auto reshape_view(E&& e, S&& shape)
     {
@@ -815,18 +862,26 @@ namespace xt
             "traversal has to be row or column major"
         );
 
-        using shape_type = std::decay_t<S>;
-        get_strides_t<shape_type> strides;
+        using shape_type = std::decay_t<decltype(shape)>;
+        using unsigned_shape_type = typename detail::rebind_shape<shape_type>::type;
+        get_strides_t<unsigned_shape_type> strides;
 
+        detail::recalculate_shape(shape, e.size());
         xt::resize_container(strides, shape.size());
         compute_strides(shape, L, strides);
         constexpr auto computed_layout = std::decay_t<E>::static_layout == L ? L : layout_type::dynamic;
         using view_type = xstrided_view<
             xclosure_t<E>,
-            shape_type,
+            unsigned_shape_type,
             computed_layout,
             detail::flat_adaptor_getter<xclosure_t<E>, L>>;
-        return view_type(std::forward<E>(e), std::forward<S>(shape), std::move(strides), 0, e.layout());
+        return view_type(
+            std::forward<E>(e),
+            xtl::forward_sequence<unsigned_shape_type, S>(shape),
+            std::move(strides),
+            0,
+            e.layout()
+        );
     }
 
     /**
@@ -858,7 +913,7 @@ namespace xt
     template <layout_type L = XTENSOR_DEFAULT_TRAVERSAL, class E, class I, std::size_t N>
     inline auto reshape_view(E&& e, const I (&shape)[N])
     {
-        using shape_type = std::array<std::size_t, N>;
+        using shape_type = std::array<I, N>;
         return reshape_view<L>(std::forward<E>(e), xtl::forward_sequence<shape_type, decltype(shape)>(shape));
     }
 }


Test patch:
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e6331f759..46127597e 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -7,7 +7,7 @@
 # The full license is in the file LICENSE, distributed with this software. #
 ############################################################################
 
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
 
 if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
     project(xtensor-test CXX)
diff --git a/test/test_xstrided_view.cpp b/test/test_xstrided_view.cpp
index d327ba703..157a29a89 100644
--- a/test/test_xstrided_view.cpp
+++ b/test/test_xstrided_view.cpp
@@ -696,17 +696,20 @@ namespace xt
         EXPECT_EQ(av, e);
         EXPECT_EQ(av, a);
 
-        bool truthy;
-        truthy = std::is_same<
-            typename decltype(xv)::temporary_type,
-            xtensor_fixed<double, xshape<3, 3>, XTENSOR_DEFAULT_LAYOUT>>();
-        EXPECT_TRUE(truthy);
-
-        truthy = std::is_same<typename decltype(av)::temporary_type, xtensor<double, 2, XTENSOR_DEFAULT_LAYOUT>>(
+        static_assert(
+            std::is_same<
+                typename decltype(xv)::temporary_type,
+                xtensor_fixed<double, xshape<3, 3>, XTENSOR_DEFAULT_LAYOUT>>::value,
+            "Container types do not match"
+        );
+        static_assert(
+            std::is_same<typename decltype(av)::temporary_type, xtensor<double, 2, XTENSOR_DEFAULT_LAYOUT>>::value,
+            "Container types do not match"
+        );
+        static_assert(
+            std::is_same<typename decltype(av)::shape_type, typename decltype(e)::shape_type>::value,
+            "Shape types do not match"
         );
-        EXPECT_TRUE(truthy);
-        truthy = std::is_same<typename decltype(av)::shape_type, typename decltype(e)::shape_type>::value;
-        EXPECT_TRUE(truthy);
 
         xarray<int> xa = {{1, 2, 3}, {4, 5, 6}};
         std::vector<std::size_t> new_shape = {3, 2};
@@ -714,6 +717,10 @@ namespace xt
 
         xarray<int> xres = {{1, 2}, {3, 4}, {5, 6}};
         EXPECT_EQ(xrv, xres);
+
+        auto nv = xt::reshape_view<XTENSOR_DEFAULT_LAYOUT>(a, {-1, 3});
+        std::vector<size_t> expected_shape({3, 3});
+        EXPECT_TRUE(std::equal(nv.shape().begin(), nv.shape().end(), expected_shape.begin()));
     }
 
     TEST(xstrided_view, reshape_view_assign)