diff --git a/doc/python/corrade.containers.rst b/doc/python/corrade.containers.rst index 32b4e91..e96bf1b 100644 --- a/doc/python/corrade.containers.rst +++ b/doc/python/corrade.containers.rst @@ -104,6 +104,10 @@ :raise IndexError: if :p:`dimension` is not :py:`0` .. py:function:: corrade.containers.StridedArrayView1D.broadcasted :raise IndexError: if :p:`dimension` is not :py:`0` +.. py:function:: corrade.containers.StridedArrayView1D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.MutableStridedArrayView1D @@ -114,6 +118,10 @@ :raise IndexError: if :p:`dimension` is not :py:`0` .. py:function:: corrade.containers.MutableStridedArrayView1D.broadcasted :raise IndexError: if :p:`dimension` is not :py:`0` +.. py:function:: corrade.containers.MutableStridedArrayView1D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.StridedArrayView2D @@ -126,6 +134,10 @@ .. py:function:: corrade.containers.StridedArrayView2D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0` or :py:`1` or if they're the same +.. py:function:: corrade.containers.StridedArrayView2D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` or :py:`1` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.MutableStridedArrayView2D @@ -139,6 +151,10 @@ .. py:function:: corrade.containers.MutableStridedArrayView2D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0` or :py:`1` or if they're the same +.. py:function:: corrade.containers.MutableStridedArrayView2D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` or :py:`1` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.StridedArrayView3D @@ -151,6 +167,10 @@ .. py:function:: corrade.containers.StridedArrayView3D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0`, :py:`1` or :py:`2` or if they're the same +.. py:function:: corrade.containers.StridedArrayView3D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0`, :py:`1` or :py:`2` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.MutableStridedArrayView3D @@ -164,6 +184,10 @@ .. py:function:: corrade.containers.MutableStridedArrayView3D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0`, :py:`1` or :py:`2` or if they're the same +.. py:function:: corrade.containers.MutableStridedArrayView3D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0`, :py:`1` or :py:`2` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.StridedArrayView4D @@ -245,6 +269,10 @@ :raise IndexError: if :p:`dimension` is not :py:`0` .. py:function:: corrade.containers.StridedBitArrayView1D.broadcasted :raise IndexError: if :p:`dimension` is not :py:`0` +.. py:function:: corrade.containers.StridedBitArrayView1D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.MutableStridedBitArrayView1D @@ -255,6 +283,10 @@ :raise IndexError: if :p:`dimension` is not :py:`0` .. py:function:: corrade.containers.MutableStridedBitArrayView1D.broadcasted :raise IndexError: if :p:`dimension` is not :py:`0` +.. py:function:: corrade.containers.MutableStridedBitArrayView1D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.StridedBitArrayView2D @@ -267,6 +299,10 @@ .. py:function:: corrade.containers.StridedBitArrayView2D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0` or :py:`1` or if they're the same +.. py:function:: corrade.containers.StridedBitArrayView2D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` or :py:`1` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.MutableStridedBitArrayView2D @@ -280,6 +316,10 @@ .. py:function:: corrade.containers.MutableStridedBitArrayView2D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0` or :py:`1` or if they're the same +.. py:function:: corrade.containers.MutableStridedBitArrayView2D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0` or :py:`1` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.StridedBitArrayView3D @@ -292,6 +332,10 @@ .. py:function:: corrade.containers.StridedBitArrayView3D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0`, :py:`1` or :py:`2` or if they're the same +.. py:function:: corrade.containers.StridedBitArrayView3D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0`, :py:`1` or :py:`2` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.MutableStridedBitArrayView3D @@ -305,6 +349,10 @@ .. py:function:: corrade.containers.MutableStridedBitArrayView3D.transposed :raise IndexError: if :p:`a` or :p:`b` is not :py:`0`, :py:`1` or :py:`2` or if they're the same +.. py:function:: corrade.containers.MutableStridedBitArrayView3D.expanded + :raise IndexError: if :p:`dimension` is not :py:`0`, :py:`1` or :py:`2` + :raise ValueError: if product of :p:`size` is not equal to size in + :p:`dimension` .. py:class:: corrade.containers.StridedBitArrayView4D diff --git a/src/Corrade/Containers/StridedArrayViewPythonBindings.h b/src/Corrade/Containers/StridedArrayViewPythonBindings.h index ff6e603..cc82cc2 100644 --- a/src/Corrade/Containers/StridedArrayViewPythonBindings.h +++ b/src/Corrade/Containers/StridedArrayViewPythonBindings.h @@ -133,6 +133,10 @@ template class PyStridedArrayView: public StridedA return PyStridedArrayView{StridedArrayView::template broadcasted(size), format, itemsize, getitem, setitem}; } + template PyStridedArrayView expanded(const Containers::Size& size) const { + return PyStridedArrayView{StridedArrayView::template expanded(size), format, itemsize, getitem, setitem}; + } + /* has to be public as it's accessed by the bindings directly */ const char* format; std::size_t itemsize; diff --git a/src/python/corrade/containers.cpp b/src/python/corrade/containers.cpp index 92cc6f1..261ad70 100644 --- a/src/python/corrade/containers.cpp +++ b/src/python/corrade/containers.cpp @@ -374,6 +374,23 @@ template<> struct StridedOperation<1> { PyErr_Format(PyExc_IndexError, "dimension %u out of range for a %iD view", dimension, dimensions); throw py::error_already_set{}; } + template class View, unsigned dimensions, class T> static View expanded(const View& view, unsigned dimension, const typename DimensionsTuple::Type& size) { + if(dimension == 0) { + Containers::Size iSize{NoInit}; + std::size_t totalSize = 1; + for(std::size_t i = 0; i != count; ++i) { + iSize[i] = dimensionsTupleGet(size, i); + totalSize *= iSize[i]; + } + if(totalSize != Containers::Size{view.size()}[0]) { + PyErr_Format(PyExc_ValueError, "total size %zu doesn't match dimension %u with %zu elements", totalSize, dimension, Containers::Size{view.size()}[0]); + throw py::error_already_set{}; + } + return view.template expanded<0>(iSize); + } + PyErr_Format(PyExc_IndexError, "dimension %u out of range for a %iD view", dimension, dimensions); + throw py::error_already_set{}; + } }; template<> struct StridedOperation<2> { template class View, unsigned dimensions, class T> static View transposed(const View& view, unsigned a, unsigned b) { @@ -398,6 +415,22 @@ template<> struct StridedOperation<2> { } return StridedOperation<1>::broadcasted(view, dimension, size); } + template class View, unsigned dimensions, class T> static View expanded(const View& view, unsigned dimension, const typename DimensionsTuple::Type& size) { + if(dimension == 1) { + Containers::Size iSize{NoInit}; + std::size_t totalSize = 1; + for(std::size_t i = 0; i != count; ++i) { + iSize[i] = dimensionsTupleGet(size, i); + totalSize *= iSize[i]; + } + if(totalSize != view.size()[1]) { + PyErr_Format(PyExc_ValueError, "total size %zu doesn't match dimension %u with %zu elements", totalSize, dimension, view.size()[1]); + throw py::error_already_set{}; + } + return view.template expanded<1>(iSize); + } + return StridedOperation<1>::expanded(view, dimension, size); + } }; template<> struct StridedOperation<3> { template class View, unsigned dimensions, class T> static View transposed(const View& view, unsigned a, unsigned b) { @@ -424,6 +457,22 @@ template<> struct StridedOperation<3> { } return StridedOperation<2>::broadcasted(view, dimension, size); } + template class View, unsigned dimensions, class T> static View expanded(const View& view, unsigned dimension, const typename DimensionsTuple::Type& size) { + if(dimension == 2) { + Containers::Size iSize{NoInit}; + std::size_t totalSize = 1; + for(std::size_t i = 0; i != count; ++i) { + iSize[i] = dimensionsTupleGet(size, i); + totalSize *= iSize[i]; + } + if(totalSize != view.size()[2]) { + PyErr_Format(PyExc_ValueError, "total size %zu doesn't match dimension %u with %zu elements", totalSize, dimension, view.size()[2]); + throw py::error_already_set{}; + } + return view.template expanded<2>(iSize); + } + return StridedOperation<2>::expanded(view, dimension, size); + } }; template<> struct StridedOperation<4> { template class View, unsigned dimensions, class T> static View transposed(const View& view, unsigned a, unsigned b) { @@ -453,6 +502,22 @@ template<> struct StridedOperation<4> { } return StridedOperation<3>::broadcasted(view, dimension, size); } + template class View, unsigned dimensions, class T> static View expanded(const View& view, unsigned dimension, const typename DimensionsTuple::Type& size) { + if(dimension == 3) { + Containers::Size iSize{NoInit}; + std::size_t totalSize = 1; + for(std::size_t i = 0; i != count; ++i) { + iSize[i] = dimensionsTupleGet(size, i); + totalSize *= iSize[i]; + } + if(totalSize != view.size()[3]) { + PyErr_Format(PyExc_ValueError, "total size %zu doesn't match dimension %u with %zu elements", totalSize, dimension, view.size()[3]); + throw py::error_already_set{}; + } + return view.template expanded<3>(iSize); + } + return StridedOperation<3>::expanded(view, dimension, size); + } }; template class Steps, class T> Containers::PyArrayViewHolder stridedArrayViewSlice(const T& self, const typename DimensionsTuple::Type& slice, py::object owner) { @@ -589,7 +654,37 @@ template void stridedArrayView1D(py::class_& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<1>::expanded<2>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")) + .def("expanded", [](const Containers::PyStridedArrayView<1, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<1>::expanded<3>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")) + .def("expanded", [](const Containers::PyStridedArrayView<1, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<1>::expanded<4>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")); +} + +template void stridedArrayView2D(py::class_, Containers::PyArrayViewHolder>>& c) { + c + /* Fancy operations */ + .def("expanded", [](const Containers::PyStridedArrayView<2, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<2>::expanded<2>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")) + .def("expanded", [](const Containers::PyStridedArrayView<2, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<2>::expanded<3>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")); +} + +template void stridedArrayView3D(py::class_, Containers::PyArrayViewHolder>>& c) { + c + /* Fancy operations */ + .def("expanded", [](const Containers::PyStridedArrayView<3, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<3>::expanded<2>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")); } template void stridedArrayViewND(py::class_, Containers::PyArrayViewHolder>>& c) { @@ -695,7 +790,37 @@ template void stridedBitArrayView1D(py::class_& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<1>::expanded<2>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")) + .def("expanded", [](const Containers::BasicStridedBitArrayView<1, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<1>::expanded<3>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")) + .def("expanded", [](const Containers::BasicStridedBitArrayView<1, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<1>::expanded<4>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")); +} + +template void stridedBitArrayView2D(py::class_, Containers::PyArrayViewHolder>>& c) { + c + /* Fancy operations */ + .def("expanded", [](const Containers::BasicStridedBitArrayView<2, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<2>::expanded<2>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")) + .def("expanded", [](const Containers::BasicStridedBitArrayView<2, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<2>::expanded<3>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")); +} + +template void stridedBitArrayView3D(py::class_, Containers::PyArrayViewHolder>>& c) { + c + /* Fancy operations */ + .def("expanded", [](const Containers::BasicStridedBitArrayView<3, T>& self, unsigned dimension, const std::tuple& size) { + return Containers::pyArrayViewHolder(StridedOperation<3>::expanded<2>(self, dimension, size), pyObjectHolderFor(self).owner); + }, "Expand a dimension", py::arg("dimension"), py::arg("size")); } template void stridedBitArrayViewND(py::class_, Containers::PyArrayViewHolder>>& c) { @@ -852,8 +977,10 @@ void containers(py::module_& m) { return pyArrayViewHolder(Containers::StridedBitArrayView1D{other}, pyObjectHolderFor(other).owner); }), "Construct from a bit array view", py::arg("view")); stridedBitArrayView(stridedBitArrayView2D_); + stridedBitArrayView2D(stridedBitArrayView2D_); stridedBitArrayViewND(stridedBitArrayView2D_); stridedBitArrayView(stridedBitArrayView3D_); + stridedBitArrayView3D(stridedBitArrayView3D_); stridedBitArrayViewND(stridedBitArrayView3D_); stridedBitArrayView(stridedBitArrayView4D_); stridedBitArrayViewND(stridedBitArrayView4D_); @@ -869,8 +996,10 @@ void containers(py::module_& m) { stridedBitArrayView(mutableStridedBitArrayView1D_); stridedBitArrayView1D(mutableStridedBitArrayView1D_); stridedBitArrayView(mutableStridedBitArrayView2D_); + stridedBitArrayView2D(mutableStridedBitArrayView2D_); stridedBitArrayViewND(mutableStridedBitArrayView2D_); stridedBitArrayView(mutableStridedBitArrayView3D_); + stridedBitArrayView3D(mutableStridedBitArrayView3D_); stridedBitArrayViewND(mutableStridedBitArrayView3D_); stridedBitArrayView(mutableStridedBitArrayView4D_); stridedBitArrayViewND(mutableStridedBitArrayView4D_); @@ -897,8 +1026,10 @@ void containers(py::module_& m) { stridedArrayView(stridedArrayView1D_); stridedArrayView1D(stridedArrayView1D_); stridedArrayView(stridedArrayView2D_); + stridedArrayView2D(stridedArrayView2D_); stridedArrayViewND(stridedArrayView2D_); stridedArrayView(stridedArrayView3D_); + stridedArrayView3D(stridedArrayView3D_); stridedArrayViewND(stridedArrayView3D_); stridedArrayView(stridedArrayView4D_); stridedArrayViewND(stridedArrayView4D_); @@ -914,8 +1045,10 @@ void containers(py::module_& m) { stridedArrayView(mutableStridedArrayView1D_); stridedArrayView1D(mutableStridedArrayView1D_); stridedArrayView(mutableStridedArrayView2D_); + stridedArrayView2D(mutableStridedArrayView2D_); stridedArrayViewND(mutableStridedArrayView2D_); stridedArrayView(mutableStridedArrayView3D_); + stridedArrayView3D(mutableStridedArrayView3D_); stridedArrayViewND(mutableStridedArrayView3D_); stridedArrayView(mutableStridedArrayView4D_); stridedArrayViewND(mutableStridedArrayView4D_); diff --git a/src/python/corrade/test/test_containers.py b/src/python/corrade/test/test_containers.py index 519c201..63d799c 100644 --- a/src/python/corrade/test/test_containers.py +++ b/src/python/corrade/test/test_containers.py @@ -410,6 +410,24 @@ class StridedArrayView1D(unittest.TestCase): self.assertEqual(c.stride, (0,)) self.assertEqual(bytes(c), b'33333') + d2 = containers.StridedArrayView1D(a).expanded(0, (2, 4)) + self.assertIsInstance(d2, containers.StridedArrayView2D) + self.assertEqual(d2.size, (2, 4)) + self.assertEqual(d2.stride, (4, 1)) + self.assertEqual(bytes(d2), b'01234567') + + d3 = containers.StridedArrayView1D(a).expanded(0, (2, 2, 2)) + self.assertIsInstance(d3, containers.StridedArrayView3D) + self.assertEqual(d3.size, (2, 2, 2)) + self.assertEqual(d3.stride, (4, 2, 1)) + self.assertEqual(bytes(d3), b'01234567') + + d4 = containers.StridedArrayView1D(a).expanded(0, (2, 1, 2, 2)) + self.assertIsInstance(d4, containers.StridedArrayView4D) + self.assertEqual(d4.size, (2, 1, 2, 2)) + self.assertEqual(d4.stride, (4, 4, 2, 1)) + self.assertEqual(bytes(d4), b'01234567') + def test_ops_invalid(self): a = b'00' @@ -419,8 +437,12 @@ class StridedArrayView1D(unittest.TestCase): containers.StridedArrayView1D().flipped(1) with self.assertRaisesRegex(IndexError, "dimension 1 out of range for a 1D view"): containers.StridedArrayView1D(a).broadcasted(1, 3) + with self.assertRaisesRegex(IndexError, "dimension 1 out of range for a 1D view"): + containers.StridedArrayView1D(a).expanded(1, (1, 2)) with self.assertRaisesRegex(ValueError, "can't broadcast dimension 0 with 2 elements"): containers.StridedArrayView1D(a).broadcasted(0, 3) + with self.assertRaisesRegex(ValueError, "total size 3 doesn't match dimension 0 with 2 elements"): + containers.StridedArrayView1D(a).expanded(0, (1, 3)) def test_convert_memoryview(self): a = b'World is hell!' @@ -722,6 +744,18 @@ class StridedArrayView2D(unittest.TestCase): self.assertEqual(d.stride, (8, 0)) self.assertEqual(bytes(d), b'3377bb') + e3 = containers.StridedArrayView2D(v).expanded(1, (2, 4)) + self.assertIsInstance(e3, containers.StridedArrayView3D) + self.assertEqual(e3.size, (3, 2, 4)) + self.assertEqual(e3.stride, (8, 4, 1)) + self.assertEqual(bytes(e3), b'01234567456789ab89abcdef') + + e4 = containers.StridedArrayView2D(v).expanded(0, (1, 3, 1)) + self.assertIsInstance(e4, containers.StridedArrayView4D) + self.assertEqual(e4.size, (1, 3, 1, 8)) + self.assertEqual(e4.stride, (24, 8, 8, 1)) + self.assertEqual(bytes(e4), b'01234567456789ab89abcdef') + def test_ops_invalid(self): a = b'00' v = memoryview(a).cast('b', shape=[1, 2]) @@ -732,10 +766,16 @@ class StridedArrayView2D(unittest.TestCase): containers.StridedArrayView2D().flipped(2) with self.assertRaisesRegex(IndexError, "dimension 2 out of range for a 2D view"): containers.StridedArrayView2D(v).broadcasted(2, 3) + with self.assertRaisesRegex(IndexError, "dimension 2 out of range for a 2D view"): + containers.StridedArrayView2D(v).expanded(2, (1, 2)) with self.assertRaisesRegex(ValueError, "can't broadcast dimension 1 with 2 elements"): containers.StridedArrayView2D(v).broadcasted(1, 3) with self.assertRaisesRegex(IndexError, "dimensions 0, 2 can't be transposed in a 2D view"): containers.StridedArrayView2D().transposed(0, 2) + with self.assertRaisesRegex(ValueError, "total size 2 doesn't match dimension 0 with 1 elements"): + containers.StridedArrayView2D(v).expanded(0, (1, 2)) + with self.assertRaisesRegex(ValueError, "total size 4 doesn't match dimension 1 with 2 elements"): + containers.StridedArrayView2D(v).expanded(1, (2, 1, 2)) def test_convert_memoryview(self): a = memoryview(b'01234567' @@ -838,6 +878,12 @@ class StridedArrayView3D(unittest.TestCase): self.assertEqual(f.stride, (24, 8, 0)) self.assertEqual(bytes(f), b'000004444488888ccccc0000044444') + g4 = containers.StridedArrayView3D(v).expanded(2, (2, 4)) + self.assertIsInstance(g4, containers.StridedArrayView4D) + self.assertEqual(g4.size, (2, 3, 2, 4)) + self.assertEqual(g4.stride, (24, 8, 4, 1)) + self.assertEqual(bytes(g4), b'01234567456789ab89abcdefcdef012301234567456789ab') + def test_ops_invalid(self): a = b'00' v = memoryview(a).cast('b', shape=[1, 1, 2]) @@ -848,10 +894,16 @@ class StridedArrayView3D(unittest.TestCase): containers.StridedArrayView3D().flipped(3) with self.assertRaisesRegex(IndexError, "dimension 3 out of range for a 3D view"): containers.StridedArrayView3D(v).broadcasted(3, 3) + with self.assertRaisesRegex(IndexError, "dimension 3 out of range for a 3D view"): + containers.StridedArrayView3D(v).expanded(3, (1, 2)) with self.assertRaisesRegex(ValueError, "can't broadcast dimension 2 with 2 elements"): containers.StridedArrayView3D(v).broadcasted(2, 3) with self.assertRaisesRegex(IndexError, "dimensions 1, 3 can't be transposed in a 3D view"): containers.StridedArrayView3D().transposed(1, 3) + with self.assertRaisesRegex(ValueError, "total size 2 doesn't match dimension 0 with 1 elements"): + containers.StridedArrayView3D(v).expanded(0, (1, 2)) + with self.assertRaisesRegex(ValueError, "total size 3 doesn't match dimension 2 with 2 elements"): + containers.StridedArrayView3D(v).expanded(2, (3, 1)) # This is just a dumb copy of the above with one dimension inserted at the # second place. @@ -1713,6 +1765,45 @@ class StridedBitArrayView1D(unittest.TestCase): self.assertEqual(c[3], True) self.assertEqual(c[4], True) + d2 = v.expanded(0, (2, 4)) + self.assertIsInstance(d2, containers.StridedBitArrayView2D) + self.assertEqual(d2.size, (2, 4)) + self.assertEqual(d2.stride, (32, 8)) + self.assertEqual(d2[0][0], True) + self.assertEqual(d2[0][1], True) + self.assertEqual(d2[0][2], True) + self.assertEqual(d2[0][3], True) + self.assertEqual(d2[1][0], False) + self.assertEqual(d2[1][1], False) + self.assertEqual(d2[1][2], True) + self.assertEqual(d2[1][3], False) + + d3 = v.expanded(0, (2, 2, 2)) + self.assertIsInstance(d3, containers.StridedBitArrayView3D) + self.assertEqual(d3.size, (2, 2, 2)) + self.assertEqual(d3.stride, (32, 16, 8)) + self.assertEqual(d3[0][0][0], True) + self.assertEqual(d3[0][0][1], True) + self.assertEqual(d3[0][1][0], True) + self.assertEqual(d3[0][1][1], True) + self.assertEqual(d3[1][0][0], False) + self.assertEqual(d3[1][0][1], False) + self.assertEqual(d3[1][1][0], True) + self.assertEqual(d3[1][1][1], False) + + d4 = v.expanded(0, (2, 1, 2, 2)) + self.assertIsInstance(d4, containers.StridedBitArrayView4D) + self.assertEqual(d4.size, (2, 1, 2, 2)) + self.assertEqual(d4.stride, (32, 32, 16, 8)) + self.assertEqual(d4[0][0][0][0], True) + self.assertEqual(d4[0][0][0][1], True) + self.assertEqual(d4[0][0][1][0], True) + self.assertEqual(d4[0][0][1][1], True) + self.assertEqual(d4[1][0][0][0], False) + self.assertEqual(d4[1][0][0][1], False) + self.assertEqual(d4[1][0][1][0], True) + self.assertEqual(d4[1][0][1][1], False) + def test_ops_invalid(self): v = containers.StridedArrayView1D(b'00').slice_bit(0) @@ -1720,8 +1811,12 @@ class StridedBitArrayView1D(unittest.TestCase): containers.StridedBitArrayView1D().flipped(1) with self.assertRaisesRegex(IndexError, "dimension 1 out of range for a 1D view"): v.broadcasted(1, 3) + with self.assertRaisesRegex(IndexError, "dimension 1 out of range for a 1D view"): + v.expanded(1, (1, 2)) with self.assertRaisesRegex(ValueError, "can't broadcast dimension 0 with 2 elements"): v.broadcasted(0, 3) + with self.assertRaisesRegex(ValueError, "total size 3 doesn't match dimension 0 with 2 elements"): + v.expanded(0, (1, 3)) class StridedBitArrayView2D(unittest.TestCase): def test_init(self): @@ -1993,6 +2088,32 @@ class StridedBitArrayView2D(unittest.TestCase): self.assertEqual(d[2, 1], False) self.assertEqual(d[3, 1], False) + d3 = a.expanded(1, (2, 2)) + self.assertIsInstance(d3, containers.StridedBitArrayView3D) + self.assertEqual(d3.size, (2, 2, 2)) + self.assertEqual(d3.stride, (32, 16, 8)) + self.assertEqual(d3[0][0][0], True) + self.assertEqual(d3[0][0][1], True) + self.assertEqual(d3[0][1][0], True) + self.assertEqual(d3[0][1][1], True) + self.assertEqual(d3[1][0][0], False) + self.assertEqual(d3[1][0][1], False) + self.assertEqual(d3[1][1][0], True) + self.assertEqual(d3[1][1][1], False) + + d4 = a.expanded(1, (1, 2, 2)) + self.assertIsInstance(d4, containers.StridedBitArrayView4D) + self.assertEqual(d4.size, (2, 1, 2, 2)) + self.assertEqual(d4.stride, (32, 32, 16, 8)) + self.assertEqual(d4[0][0][0][0], True) + self.assertEqual(d4[0][0][0][1], True) + self.assertEqual(d4[0][0][1][0], True) + self.assertEqual(d4[0][0][1][1], True) + self.assertEqual(d4[1][0][0][0], False) + self.assertEqual(d4[1][0][0][1], False) + self.assertEqual(d4[1][0][1][0], True) + self.assertEqual(d4[1][0][1][1], False) + def test_ops_invalid(self): v = containers.StridedArrayView2D(memoryview(b'00').cast('b', shape=[1, 2])).slice_bit(0) @@ -2002,8 +2123,14 @@ class StridedBitArrayView2D(unittest.TestCase): containers.StridedBitArrayView2D().transposed(2, 1) with self.assertRaisesRegex(IndexError, "dimension 2 out of range for a 2D view"): v.broadcasted(2, 3) + with self.assertRaisesRegex(IndexError, "dimension 2 out of range for a 2D view"): + v.expanded(2, (1, 2)) with self.assertRaisesRegex(ValueError, "can't broadcast dimension 1 with 2 elements"): v.broadcasted(1, 3) + with self.assertRaisesRegex(ValueError, "total size 2 doesn't match dimension 0 with 1 elements"): + v.expanded(0, (1, 2)) + with self.assertRaisesRegex(ValueError, "total size 4 doesn't match dimension 1 with 2 elements"): + v.expanded(1, (2, 1, 2)) # Multi-dimensional behavior is tested extensively for StridedBitArrayView2D, # this checks just what differs, like constructors and fancy operations @@ -2072,6 +2199,19 @@ class StridedBitArrayView3D(unittest.TestCase): self.assertEqual(d[2, 0, 1], False) self.assertEqual(d[3, 0, 1], False) + e4 = a.expanded(2, (2, 2)) + self.assertIsInstance(e4, containers.StridedBitArrayView4D) + self.assertEqual(e4.size, (2, 1, 2, 2)) + self.assertEqual(e4.stride, (32, 32, 16, 8)) + self.assertEqual(e4[0][0][0][0], True) + self.assertEqual(e4[0][0][0][1], True) + self.assertEqual(e4[0][0][1][0], True) + self.assertEqual(e4[0][0][1][1], True) + self.assertEqual(e4[1][0][0][0], False) + self.assertEqual(e4[1][0][0][1], False) + self.assertEqual(e4[1][0][1][0], True) + self.assertEqual(e4[1][0][1][1], False) + def test_ops_invalid(self): v = containers.StridedArrayView3D(memoryview(b'00').cast('b', shape=[1, 1, 2])).slice_bit(0) @@ -2081,8 +2221,14 @@ class StridedBitArrayView3D(unittest.TestCase): containers.StridedBitArrayView3D().transposed(2, 3) with self.assertRaisesRegex(IndexError, "dimension 3 out of range for a 3D view"): v.broadcasted(3, 3) + with self.assertRaisesRegex(IndexError, "dimension 3 out of range for a 3D view"): + v.expanded(3, (1, 2)) with self.assertRaisesRegex(ValueError, "can't broadcast dimension 2 with 2 elements"): v.broadcasted(2, 3) + with self.assertRaisesRegex(ValueError, "total size 3 doesn't match dimension 0 with 1 elements"): + v.expanded(0, (1, 3)) + with self.assertRaisesRegex(ValueError, "total size 6 doesn't match dimension 2 with 2 elements"): + v.expanded(2, (2, 3)) # This is just a dumb copy of the above with one dimension inserted at the # second place.