Imported Upstream version 3.1.9
[platform/upstream/Imath.git] / src / python / PyImath / PyImathFixedArray2D.h
1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright Contributors to the OpenEXR Project.
4 //
5
6 // clang-format off
7
8 #ifndef _PyImathFixedArray2D_h_
9 #define _PyImathFixedArray2D_h_
10
11 #define BOOST_BIND_GLOBAL_PLACEHOLDERS
12 #include <boost/python.hpp>
13 #include <boost/operators.hpp>
14 #include <boost/shared_array.hpp>
15 #include <boost/any.hpp>
16 #include <iostream>
17 #include <ImathVec.h>
18 #include "PyImathFixedArray.h"
19 #include "PyImathOperators.h"
20
21 namespace PyImath {
22
23 template <class T>
24 class FixedArray2D
25 {
26     T *                 _ptr;
27     IMATH_NAMESPACE::Vec2<size_t> _length;
28     IMATH_NAMESPACE::Vec2<size_t> _stride;
29     size_t              _size; //flattened size of the array
30
31     // this handle optionally stores a shared_array to allocated array data
32     // so that everything is freed properly on exit.
33     boost::any _handle;
34
35   public:
36
37     FixedArray2D(T *ptr, Py_ssize_t lengthX, Py_ssize_t lengthY, Py_ssize_t strideX = 1)
38         : _ptr(ptr), _length(lengthX, lengthY), _stride(strideX, lengthX), _handle()
39     {
40         if (lengthX < 0 || lengthY < 0)
41             throw std::domain_error("Fixed array 2d lengths must be non-negative");
42         if (strideX <= 0)
43             throw std::domain_error("Fixed array 2d strides must be positive");
44         initializeSize();
45         //std::cout << "fixed array external construct" << std::endl;
46         // nothing
47     }
48
49     FixedArray2D(T *ptr, Py_ssize_t lengthX, Py_ssize_t lengthY, Py_ssize_t strideX, Py_ssize_t strideY)
50         : _ptr(ptr), _length(lengthX, lengthY), _stride(strideX, strideY), _handle()
51     {
52         if (lengthX < 0 || lengthY < 0)
53             throw std::domain_error("Fixed array 2d lengths must be non-negative");
54         if (strideX <= 0 || strideY < 0)
55             throw std::domain_error("Fixed array 2d strides must be positive");
56         initializeSize();
57         //std::cout << "fixed array external construct" << std::endl;
58         // nothing
59     }
60
61     FixedArray2D(T *ptr, Py_ssize_t lengthX, Py_ssize_t lengthY, Py_ssize_t strideX, Py_ssize_t strideY, boost::any handle) 
62         : _ptr(ptr), _length(lengthX, lengthY), _stride(strideX, strideY), _handle(handle)
63     {
64         initializeSize();
65         //std::cout << "fixed array external construct with handle" << std::endl;
66         // nothing
67     }
68
69     explicit FixedArray2D(Py_ssize_t lengthX, Py_ssize_t lengthY)
70         : _ptr(0), _length(lengthX, lengthY), _stride(1, lengthX), _handle()
71     {
72         if (lengthX < 0 || lengthY < 0)
73             throw std::domain_error("Fixed array 2d lengths must be non-negative");
74         initializeSize();
75         T tmp = FixedArrayDefaultValue<T>::value();
76         boost::shared_array<T> a(new T[_size]);
77         for (size_t i=0; i<_size; ++i) a[i] = tmp;
78         _handle = a;
79         _ptr = a.get();
80     }
81
82     explicit FixedArray2D(const IMATH_NAMESPACE::V2i& length)
83         : _ptr(0), _length(length), _stride(1, length.x), _handle()
84     {
85         if (length.x < 0 || length.y < 0)
86             throw std::domain_error("Fixed array 2d lengths must be non-negative");
87         initializeSize();
88         T tmp = FixedArrayDefaultValue<T>::value();
89         boost::shared_array<T> a(new T[_size]);
90         for (size_t i=0; i<_size; ++i) a[i] = tmp;
91         _handle = a;
92         _ptr = a.get();
93     }
94
95     FixedArray2D(const T &initialValue, Py_ssize_t lengthX, Py_ssize_t lengthY)
96         : _ptr(0), _length(lengthX, lengthY), _stride(1, lengthX), _handle()
97     {
98         if (lengthX < 0 || lengthY < 0)
99             throw std::domain_error("Fixed array 2d lengths must be non-negative");
100         initializeSize();
101         boost::shared_array<T> a(new T[_size]);
102         for (size_t i=0; i<_size; ++i) a[i] = initialValue;
103         _handle = a;
104         _ptr = a.get();
105     }
106     void initializeSize()
107     {
108         _size = _length.x*_length.y;
109     }
110
111     template <class S>
112     explicit FixedArray2D(const FixedArray2D<S> &other)
113         : _ptr(0), _length(other.len()), _stride(1, other.len().x), _handle()
114     {
115         initializeSize();
116         boost::shared_array<T> a(new T[_size]);
117         size_t z = 0;
118         for (size_t j = 0; j < _length.y; ++j)
119             for (size_t i = 0; i < _length.x; ++i)
120                 a[z++] = T(other(i,j));
121         _handle = a;
122         _ptr = a.get();
123     }
124
125     FixedArray2D(const FixedArray2D &other)
126         : _ptr(other._ptr), _length(other._length), _stride(other._stride), _size(other._size), _handle(other._handle)
127     {
128         //std::cout << "fixed array copy consturct construct" << std::endl;
129         // nothing
130     }
131         
132     const FixedArray2D &
133     operator = (const FixedArray2D &other)
134     {
135         if (&other == this) return *this;
136
137         //std::cout << "fixed array assign" << std::endl;
138
139         _ptr = other._ptr;
140         _length = other._length;
141         _stride = other._stride;
142         _handle = other._handle;
143
144         _size = _length.x*_length.y;
145
146         return *this;
147     }
148
149     ~FixedArray2D()
150     {
151         //std::cout << "fixed array delete" << std::endl;
152     }
153
154     const boost::any & handle() { return _handle; }
155
156     size_t canonical_index(Py_ssize_t index, size_t length) const
157     {
158         if (index < 0) index += length;
159         if ((size_t) index >= length || index < 0) {
160             PyErr_SetString(PyExc_IndexError, "Index out of range");
161             boost::python::throw_error_already_set();
162         }
163         return index;
164     }
165
166     void extract_slice_indices(PyObject *index, size_t length, size_t &start, size_t &end, Py_ssize_t &step, size_t &slicelength) const
167     {
168         if (PySlice_Check(index)) {
169 #if PY_MAJOR_VERSION > 2
170             PyObject *slice = index;
171 #else
172             PySliceObject *slice = reinterpret_cast<PySliceObject *>(index);
173 #endif
174             Py_ssize_t s, e, sl;
175             if (PySlice_GetIndicesEx(slice,length,&s,&e,&step,&sl) == -1) {
176                 boost::python::throw_error_already_set();
177             }
178             if (s < 0 || e < 0 || sl < 0) {
179                 throw std::domain_error("Slice extraction produced invalid start, end, or length indices");
180             }
181             start = s;
182             end = e;
183             slicelength = sl;
184         } else if (PyInt_Check(index)) {
185             size_t i = canonical_index(PyInt_AsSsize_t(index), length);
186             start = i; end = i+1; step = 1; slicelength = 1;
187         } else {
188             PyErr_SetString(PyExc_TypeError, "Object is not a slice");
189             boost::python::throw_error_already_set();
190         }
191         //std::cout << "Slice indices are " << start << " " << end << " " << step << " " << slicelength << std::endl;
192     }
193
194     // return_internal_reference doesn't seem to work with non-class types
195     typedef typename boost::mpl::if_<boost::is_class<T>,T&,T>::type get_type;
196 //    get_type    getitem(Py_ssize_t index) const { return _ptr[canonical_index(index)*_stride]; }
197     //FIXME: const does not work here with at least IMATH_NAMESPACE::Color4, why it works for V3fArray?
198     get_type getitem(Py_ssize_t i, Py_ssize_t j) //const
199     {
200         return (*this)(canonical_index(i, _length.x), canonical_index(j, _length.y));
201     }
202
203     //FIXME: anyway to seperate 2:3,4:5 from 2,4? we'd like to return int for the second one, and also 1d array for 2, 4:5 or 2:3, 4
204     FixedArray2D getslice(PyObject *index) const
205     {
206         if (PyTuple_Check(index) && PyTuple_Size(index) == 2)
207         {
208             size_t startx=0, endx=0, slicelengthx=0;
209             size_t starty=0, endy=0, slicelengthy=0;
210             Py_ssize_t stepx=0;
211             Py_ssize_t stepy=0;
212             extract_slice_indices(PyTuple_GetItem(index, 0),_length.x,startx,endx,stepx,slicelengthx);
213             extract_slice_indices(PyTuple_GetItem(index, 1),_length.y,starty,endy,stepy,slicelengthy);
214             FixedArray2D f(slicelengthx, slicelengthy);
215             for (size_t j=0,z=0; j<slicelengthy; j++)
216                 for (size_t i=0; i<slicelengthx; ++i)
217                     f._ptr[z++] = (*this)(startx+i*stepx, starty+j*stepy);
218             return f;
219         }
220         else
221         {
222             PyErr_SetString(PyExc_TypeError, "Slice syntax error");
223             boost::python::throw_error_already_set();
224         }
225         return FixedArray2D(0,0);
226     }
227
228     //FIXME: for 2D array, cannot reduce the size, or maybe returning 1D array?
229     FixedArray2D getslice_mask(const FixedArray2D<int> &mask) const
230     {
231 //         size_t len = match_dimension(mask);
232 //         size_t slicelength = 0;
233 //         for (size_t i=0; i<len; ++i) if (mask[i]) slicelength++;
234 //         FixedArray2D f(slicelength, _length.y);
235 //         for (size_t i=0,z=0; i<len; ++i) {
236 //             if (mask[i]) {
237 //                 for (size_t j = 0; j < _length.y; j++)
238 //                     f._ptr[z++] = (*this)(i,j);
239 //             }
240 //         }
241 //         return f;
242         IMATH_NAMESPACE::Vec2<size_t> len = match_dimension(mask);
243         FixedArray2D f(len);
244         for (size_t j=0; j<len.y; j++)
245             for (size_t i=0; i<len.x; i++)
246                 if (mask(i,j))
247                     f(i,j) = (*this)(i,j);
248         return f;
249     }
250
251 //     void setitem(const boost::python::tuple& index, const T &data)
252 //     {
253 //         Py_ssize_t i = boost::python::extract<Py_ssize_t>(index[0]);
254 //         Py_ssize_t j = boost::python::extract<Py_ssize_t>(index[1]);
255 //         (*this)(i,j) = data;
256 //     }
257     void
258     setitem_scalar(PyObject *index, const T &data)
259     {
260         if (!PyTuple_Check(index) || PyTuple_Size(index) != 2)
261         {
262             PyErr_SetString(PyExc_TypeError, "Slice syntax error");
263             boost::python::throw_error_already_set();
264         }
265
266         size_t startx=0, endx=0, slicelengthx=0;
267         size_t starty=0, endy=0, slicelengthy=0;
268         Py_ssize_t stepx=0;
269         Py_ssize_t stepy=0;
270         extract_slice_indices(PyTuple_GetItem(index, 0),_length.x,startx,endx,stepx,slicelengthx);
271         extract_slice_indices(PyTuple_GetItem(index, 1),_length.y,starty,endy,stepy,slicelengthy);
272         for (size_t j=0; j<slicelengthy; j++)
273             for (size_t i=0; i<slicelengthx; ++i)
274                 (*this)(startx+i*stepx, starty+j*stepy) = data;
275     }
276
277     void
278     setitem_scalar_mask(const FixedArray2D<int> &mask, const T &data)
279     {
280         IMATH_NAMESPACE::Vec2<size_t> len = match_dimension(mask);
281         for (size_t j = 0; j < len.y; j++)
282             for (size_t i=0; i<len.x; ++i)
283                 if (mask(i,j))
284                     (*this)(i,j) = data;
285     }
286
287     void
288     setitem_vector(PyObject *index, const FixedArray2D &data)
289     {
290         //TODO:sanity check
291         size_t startx=0, endx=0, slicelengthx=0;
292         size_t starty=0, endy=0, slicelengthy=0;
293         Py_ssize_t stepx=0;
294         Py_ssize_t stepy=0;
295         extract_slice_indices(PyTuple_GetItem(index, 0),_length.x,startx,endx,stepx,slicelengthx);
296         extract_slice_indices(PyTuple_GetItem(index, 1),_length.y,starty,endy,stepy,slicelengthy);
297         // we have a valid range of indices
298         if (data.len() != IMATH_NAMESPACE::Vec2<size_t>(slicelengthx, slicelengthy)) {
299             PyErr_SetString(PyExc_IndexError, "Dimensions of source do not match destination");
300             boost::python::throw_error_already_set();
301         }
302         for (size_t i=0; i<slicelengthx; ++i)
303             for (size_t j=0; j<slicelengthy; ++j)
304                 (*this)(startx+i*stepx, starty+j*stepy) = data(i,j);
305     }
306
307     void
308     setitem_vector_mask(const FixedArray2D<int> &mask, const FixedArray2D &data)
309     {
310         IMATH_NAMESPACE::Vec2<size_t> len = match_dimension(mask);
311         if (data.len() == len) {
312             for (size_t j = 0; j < len.y; j++)
313                 for (size_t i=0; i<len.x; ++i)
314                     if (mask(i,j))
315                         (*this)(i,j) = data(i,j);
316         } else {
317             PyErr_SetString(PyExc_IndexError, "Dimensions of source data do not match destination");
318             boost::python::throw_error_already_set();
319         }
320     }
321
322     void
323     setitem_array1d_mask(const FixedArray2D<int> &mask, const FixedArray<T> &data)
324     {
325         IMATH_NAMESPACE::Vec2<size_t> len = match_dimension(mask);
326         if ((size_t) data.len() == len.x*len.y) {
327             for (size_t j = 0, z = 0; j < len.y; j++)
328                 for (size_t i=0; i<len.x; ++i, ++z)
329                     if (mask(i,j))
330                         (*this)(i,j) = data[z];
331         } else {
332             size_t count = 0;
333             for (size_t j = 0, z = 0; j < len.y; j++)
334                 for (size_t i=0; i<len.x; ++i, ++z)
335                     if (mask(i,j)) count++;
336
337             if ((size_t) data.len() != count) {
338                 PyErr_SetString(PyExc_IndexError, "Dimensions of source data do not match destination either masked or unmasked");
339                 boost::python::throw_error_already_set();
340             }
341
342             for (size_t j = 0, z = 0; j < len.y; j++)
343                 for (size_t i=0; i<len.x; ++i)
344                     if (mask(i,j))
345                         (*this)(i,j) = data[z++];
346         }
347     }
348
349     void
350     setitem_array1d(PyObject *index, const FixedArray<T> &data)
351     {
352         //TODO:sanity check
353         size_t startx=0, endx=0, slicelengthx=0;
354         size_t starty=0, endy=0, slicelengthy=0;
355         Py_ssize_t stepx=0;
356         Py_ssize_t stepy=0;
357         extract_slice_indices(PyTuple_GetItem(index, 0),_length.x,startx,endx,stepx,slicelengthx);
358         extract_slice_indices(PyTuple_GetItem(index, 1),_length.y,starty,endy,stepy,slicelengthy);
359         // we have a valid range of indices
360         if ((size_t) data.len() != slicelengthx*slicelengthy) {
361             PyErr_SetString(PyExc_IndexError, "Dimensions of source data do not match destination");
362             boost::python::throw_error_already_set();
363         }
364         for (size_t j=0, z=0; j<slicelengthy; ++j)
365             for (size_t i=0; i<slicelengthx; ++i, ++z)
366                 (*this)(startx+i*stepx, starty+j*stepy) = data[z];
367     }
368
369     IMATH_NAMESPACE::Vec2<size_t> len() const { return _length; }
370     IMATH_NAMESPACE::Vec2<size_t> stride() const { return _stride; }
371     T       & operator () (size_t i, size_t j)       { return _ptr[_stride.x*(j*_stride.y + i)]; }
372     const T & operator () (size_t i, size_t j) const { return _ptr[_stride.x*(j*_stride.y + i)]; }
373     size_t totalLen() const { return _size; }
374     boost::python::tuple size() const
375     {
376         return boost::python::make_tuple(_length.x, _length.y);
377     }
378
379     static boost::python::class_<FixedArray2D<T> > register_(const char *name, const char *doc)
380     {
381         // a little tricky, but here we go - class types return internal references
382         // but fundemental types just get copied.  this typedef sets up the appropriate
383         // call policy for each type.
384         typedef typename boost::mpl::if_<
385             boost::is_class<T>,
386             boost::python::return_internal_reference<>,
387             boost::python::default_call_policies>::type call_policy;
388
389         boost::python::class_<FixedArray2D<T> > c(name,doc, boost::python::init<size_t, size_t>(
390             "construct an array of the specified length initialized to the default value for the type"));
391         c
392             .def(boost::python::init<const FixedArray2D<T> &>("construct an array with the same values as the given array"))
393             .def(boost::python::init<const T &,size_t,size_t>("construct an array of the specified length initialized to the specified default value"))
394             .def("__getitem__", &FixedArray2D<T>::getslice)
395             .def("__getitem__", &FixedArray2D<T>::getslice_mask)
396 //             .def("__getitem__", &FixedArray2D<T>::getitem, call_policy())
397             .def("item", &FixedArray2D<T>::getitem, call_policy())
398 //             .def("__setitem__", &FixedArray2D<T>::setitem)
399             .def("__setitem__", &FixedArray2D<T>::setitem_scalar)
400             .def("__setitem__", &FixedArray2D<T>::setitem_scalar_mask)
401             .def("__setitem__", &FixedArray2D<T>::setitem_vector)
402             .def("__setitem__", &FixedArray2D<T>::setitem_vector_mask)
403             .def("__setitem__", &FixedArray2D<T>::setitem_array1d)
404             .def("__setitem__", &FixedArray2D<T>::setitem_array1d_mask)
405             .def("__len__",&FixedArray2D<T>::totalLen)
406             .def("size",&FixedArray2D<T>::size)
407             .def("ifelse",&FixedArray2D<T>::ifelse_scalar)
408             .def("ifelse",&FixedArray2D<T>::ifelse_vector)
409             ;
410         return c;
411     }
412
413 //     template <class T2>
414 //     size_t match_dimension(const FixedArray<T2> &a1) const
415 //     {
416 //         if (_length.x != a1.len()) {
417 //             PyErr_SetString(PyExc_IndexError, "Dimensions of source do not match destination");
418 //             boost::python::throw_error_already_set();
419 //         }
420 //         return _length.x;
421 //     }
422
423     template <class T2>
424     IMATH_NAMESPACE::Vec2<size_t> match_dimension(const FixedArray2D<T2> &a1) const
425     {
426         if (len() != a1.len()) {
427             PyErr_SetString(PyExc_IndexError, "Dimensions of source do not match destination");
428             boost::python::throw_error_already_set();
429         }
430         return len();
431     }
432
433     FixedArray2D<T> ifelse_vector(const FixedArray2D<int> &choice, const FixedArray2D<T> &other) {
434         IMATH_NAMESPACE::Vec2<size_t> len = match_dimension(choice);
435         match_dimension(other);
436         FixedArray2D<T> tmp(len); // should use default construction but V3f doens't initialize
437         for (size_t j = 0; j < len.y; ++j)
438             for (size_t i = 0; i < len.x; ++i)
439                 tmp(i,j) = choice(i,j) ? (*this)(i,j) : other(i,j);
440         return tmp;
441     }
442
443     FixedArray2D<T> ifelse_scalar(const FixedArray2D<int> &choice, const T &other) {
444         IMATH_NAMESPACE::Vec2<size_t> len = match_dimension(choice);
445         FixedArray2D<T> tmp(len); // should use default construction but V3f doens't initialize
446         for (size_t j = 0; j < len.y; ++j)
447             for (size_t i = 0; i < len.x; ++i)
448                 tmp(i,j) = choice(i,j) ? (*this)(i,j) : other;
449         return tmp;
450     }
451
452 };
453  
454 // unary operation application
455 template <template <class,class> class Op, class T1, class Ret>
456 FixedArray2D<Ret> apply_array2d_unary_op(const FixedArray2D<T1> &a1)
457 {
458     IMATH_NAMESPACE::Vec2<size_t> len = a1.len();
459     FixedArray2D<Ret> retval(len.x,len.y);
460     for (size_t j=0; j<len.y; ++j) {
461         for (size_t i=0;i<len.x;++i) {
462             retval(i,j) = Op<T1,Ret>::apply(a1(i,j));
463         }
464     }
465     return retval;
466 }
467
468 // binary operation application
469 template <template <class,class,class> class Op, class T1, class T2, class Ret>
470 FixedArray2D<Ret> apply_array2d_array2d_binary_op(const FixedArray2D<T1> &a1, const FixedArray2D<T2> &a2)
471 {
472     IMATH_NAMESPACE::Vec2<size_t> len = a1.match_dimension(a2);
473     FixedArray2D<Ret> retval(len.x,len.y);
474     for (size_t j=0; j<len.y; ++j) {
475         for (size_t i=0;i<len.x;++i) {
476             retval(i,j) = Op<T1,T2,Ret>::apply(a1(i,j),a2(i,j));
477         }
478     }
479     return retval;
480 }
481
482 template <template <class,class,class> class Op, class T1, class T2, class Ret>
483 FixedArray2D<Ret> apply_array2d_scalar_binary_op(const FixedArray2D<T1> &a1, const T2 &a2)
484 {
485     IMATH_NAMESPACE::Vec2<size_t> len = a1.len();
486     FixedArray2D<Ret> retval(len.x,len.y);
487     for (size_t j=0; j<len.y; ++j) {
488         for (size_t i=0;i<len.x;++i) {
489             retval(i,j) = Op<T1,T2,Ret>::apply(a1(i,j),a2);
490         }
491     }
492     return retval;
493 }
494
495 template <template <class,class,class> class Op, class T1, class T2, class Ret>
496 FixedArray2D<Ret> apply_array2d_scalar_binary_rop(const FixedArray2D<T1> &a1, const T2 &a2)
497 {
498     IMATH_NAMESPACE::Vec2<size_t> len = a1.len();
499     FixedArray2D<Ret> retval(len.x,len.y);
500     for (size_t j=0; j<len.y; ++j) {
501         for (size_t i=0;i<len.x;++i) {
502             retval(i,j) = Op<T2,T1,Ret>::apply(a2,a1(i,j));
503         }
504     }
505     return retval;
506 }
507
508 // in-place binary operation application
509 template <template <class,class> class Op, class T1, class T2>
510 FixedArray2D<T1> & apply_array2d_array2d_ibinary_op(FixedArray2D<T1> &a1, const FixedArray2D<T2> &a2)
511 {
512     IMATH_NAMESPACE::Vec2<size_t> len = a1.match_dimension(a2);
513     for (size_t j=0; j<len.y; ++j) {
514         for (size_t i=0;i<len.x;++i) {
515             Op<T1,T2>::apply(a1(i,j),a2(i,j));
516         }
517     }
518     return a1;
519 }
520
521 // in-place binary operation application
522 template <template <class,class> class Op, class T1, class T2>
523 FixedArray2D<T1> & apply_array2d_scalar_ibinary_op(FixedArray2D<T1> &a1, const T2 &a2)
524 {
525     IMATH_NAMESPACE::Vec2<size_t> len = a1.len();
526     for (size_t j=0; j<len.y; ++j) {
527         for (size_t i=0;i<len.x;++i) {
528             Op<T1,T2>::apply(a1(i,j),a2);
529         }
530     }
531     return a1;
532 }
533
534     
535 // PyObject* PyNumber_Add(      PyObject *o1, PyObject *o2)
536 template <class T> static FixedArray2D<T> operator + (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_add,T,T,T>(a0,a1); }
537 template <class T> static FixedArray2D<T> operator + (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_add,T,T,T>(a0,v1); }
538 template <class T> static FixedArray2D<T> operator + (const T &v1, const FixedArray2D<T> &a0)               { return a0+v1; }
539
540 // PyObject* PyNumber_Subtract( PyObject *o1, PyObject *o2)
541 template <class T> static FixedArray2D<T> operator - (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_sub,T,T,T>(a0,a1); }
542 template <class T> static FixedArray2D<T> operator - (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_sub,T,T,T>(a0,v1); }
543 template <class T> static FixedArray2D<T> operator - (const T &v1, const FixedArray2D<T> &a0)               { return apply_array2d_scalar_binary_op<op_rsub,T,T,T>(a0,v1); }
544
545 // PyObject* PyNumber_Multiply( PyObject *o1, PyObject *o2)
546 template <class T> static FixedArray2D<T> operator * (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_mul,T,T,T>(a0,a1); }
547 template <class T> static FixedArray2D<T> operator * (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_mul,T,T,T>(a0,v1); }
548 template <class T> static FixedArray2D<T> operator * (const T &v1, const FixedArray2D<T> &a0)               { return a0*v1; }
549
550 // PyObject* PyNumber_Divide(   PyObject *o1, PyObject *o2)
551 template <class T> static FixedArray2D<T> operator / (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_div,T,T,T>(a0,a1); }
552 template <class T> static FixedArray2D<T> operator / (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_div,T,T,T>(a0,v1); }
553 // no reversed scalar/array2d divide - no meaning
554
555 // PyObject* PyNumber_FloorDivide(      PyObject *o1, PyObject *o2)
556 // PyObject* PyNumber_TrueDivide(       PyObject *o1, PyObject *o2)
557 // PyObject* PyNumber_Remainder(        PyObject *o1, PyObject *o2)
558 template <class T> static FixedArray2D<T> operator % (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_mod,T,T,T>(a0,a1); }
559 template <class T> static FixedArray2D<T> operator % (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_mod,T,T,T>(a0,v1); }
560 // no reversed scalar%array2d remainder - no meaning
561
562 // PyObject* PyNumber_Divmod(   PyObject *o1, PyObject *o2)
563
564 // PyObject* PyNumber_Power(    PyObject *o1, PyObject *o2, PyObject *o3)
565 template <class T> static FixedArray2D<T> pow_array2d_array2d (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_pow,T,T,T>(a0,a1); }
566 template <class T> static FixedArray2D<T> pow_array2d_scalar (const FixedArray2D<T> &a0, const T &v1)                { return apply_array2d_scalar_binary_op<op_pow,T,T,T>(a0,v1); }
567 // no reversed scalar/array2d pow - no meaning
568
569 // PyObject* PyNumber_Negative( PyObject *o)
570 template <class T> static FixedArray2D<T> operator - (const FixedArray2D<T> &a0) { return apply_array2d_unary_op<op_neg,T,T>(a0); }
571
572 // PyObject* PyNumber_Positive( PyObject *o)
573
574 // PyObject* PyNumber_Absolute( PyObject *o)
575 template <class T> static FixedArray2D<T> abs (const FixedArray2D<T> &a0)        { return apply_array2d_unary_op<op_abs,T,T>(a0); }
576
577 // PyObject* PyNumber_Invert(   PyObject *o)
578 template <class T> static FixedArray2D<T> operator ~ (const FixedArray2D<T> &a0) { return apply_array2d_unary_op<op_inverse,T,T>(a0); }
579
580 // PyObject* PyNumber_Lshift(   PyObject *o1, PyObject *o2)
581 template <class T> static FixedArray2D<T> operator << (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_lshift,T,T,T>(a0,a1); }
582 template <class T> static FixedArray2D<T> operator << (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_lshift,T,T,T>(a0,v1); }
583 // no reversed
584
585 // PyObject* PyNumber_Rshift(   PyObject *o1, PyObject *o2)
586 template <class T> static FixedArray2D<T> operator >> (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_rshift,T,T,T>(a0,a1); }
587 template <class T> static FixedArray2D<T> operator >> (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_rshift,T,T,T>(a0,v1); }
588 // no reversed
589
590 // PyObject* PyNumber_And(      PyObject *o1, PyObject *o2)
591 template <class T> static FixedArray2D<T> operator & (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_bitand,T,T,T>(a0,a1); }
592 template <class T> static FixedArray2D<T> operator & (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_bitand,T,T,T>(a0,v1); }
593 template <class T> static FixedArray2D<T> operator & (const T &v1, const FixedArray2D<T> &a0)               { return a0&v1; }
594
595 // PyObject* PyNumber_Xor(      PyObject *o1, PyObject *o2)
596 template <class T> static FixedArray2D<T> operator ^ (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_xor,T,T,T>(a0,a1); }
597 template <class T> static FixedArray2D<T> operator ^ (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_xor,T,T,T>(a0,v1); }
598 template <class T> static FixedArray2D<T> operator ^ (const T &v1, const FixedArray2D<T> &a0)               { return a0^v1; }
599
600 // PyObject* PyNumber_Or(       PyObject *o1, PyObject *o2)
601 template <class T> static FixedArray2D<T> operator | (const FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_binary_op<op_bitor,T,T,T>(a0,a1); }
602 template <class T> static FixedArray2D<T> operator | (const FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_binary_op<op_bitor,T,T,T>(a0,v1); }
603 template <class T> static FixedArray2D<T> operator | (const T &v1, const FixedArray2D<T> &a0)               { return a0|v1; }
604
605
606 // PyObject* PyNumber_InPlaceAdd(       PyObject *o1, PyObject *o2)
607 template <class T> static FixedArray2D<T> & operator += (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_iadd,T,T>(a0,a1); }
608 template <class T> static FixedArray2D<T> & operator += (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_iadd,T,T>(a0,v1); }
609
610 // PyObject* PyNumber_InPlaceSubtract(  PyObject *o1, PyObject *o2)
611 template <class T> static FixedArray2D<T> & operator -= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_isub,T,T>(a0,a1); }
612 template <class T> static FixedArray2D<T> & operator -= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_isub,T,T>(a0,v1); }
613
614 // PyObject* PyNumber_InPlaceMultiply(  PyObject *o1, PyObject *o2)
615 template <class T> static FixedArray2D<T> & operator *= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_imul,T,T>(a0,a1); }
616 template <class T> static FixedArray2D<T> & operator *= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_imul,T,T>(a0,v1); }
617
618 // PyObject* PyNumber_InPlaceDivide(    PyObject *o1, PyObject *o2)
619 template <class T> static FixedArray2D<T> & operator /= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_idiv,T,T>(a0,a1); }
620 template <class T> static FixedArray2D<T> & operator /= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_idiv,T,T>(a0,v1); }
621
622 // PyObject* PyNumber_InPlaceFloorDivide(       PyObject *o1, PyObject *o2)
623 // not implemented
624
625 // PyObject* PyNumber_InPlaceTrueDivide(        PyObject *o1, PyObject *o2)
626 // not implemented
627
628 // PyObject* PyNumber_InPlaceRemainder( PyObject *o1, PyObject *o2)
629 template <class T> static FixedArray2D<T> & operator %= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_imod,T,T>(a0,a1); }
630 template <class T> static FixedArray2D<T> & operator %= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_imod,T,T>(a0,v1); }
631
632 // PyObject* PyNumber_InPlacePower(     PyObject *o1, PyObject *o2, PyObject *o3)
633 template <class T> static FixedArray2D<T> & ipow_array2d_array2d (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_ipow,T,T>(a0,a1); }
634 template <class T> static FixedArray2D<T> & ipow_array2d_scalar (FixedArray2D<T> &a0, const T &v1)                { return apply_array2d_scalar_ibinary_op<op_ipow,T,T>(a0,v1); }
635
636 // PyObject* PyNumber_InPlaceLshift(    PyObject *o1, PyObject *o2)
637 template <class T> static FixedArray2D<T> & operator <<= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_ilshift,T,T>(a0,a1); }
638 template <class T> static FixedArray2D<T> & operator <<= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_ilshift,T,T>(a0,v1); }
639
640 // PyObject* PyNumber_InPlaceRshift(    PyObject *o1, PyObject *o2)
641 template <class T> static FixedArray2D<T> & operator >>= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_irshift,T,T>(a0,a1); }
642 template <class T> static FixedArray2D<T> & operator >>= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_irshift,T,T>(a0,v1); }
643
644 // PyObject* PyNumber_InPlaceAnd(       PyObject *o1, PyObject *o2)
645 template <class T> static FixedArray2D<T> & operator &= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_ibitand,T,T>(a0,a1); }
646 template <class T> static FixedArray2D<T> & operator &= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_ibitand,T,T>(a0,v1); }
647
648 // PyObject* PyNumber_InPlaceXor(       PyObject *o1, PyObject *o2)
649 template <class T> static FixedArray2D<T> & operator ^= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_ixor,T,T>(a0,a1); }
650 template <class T> static FixedArray2D<T> & operator ^= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_ixor,T,T>(a0,v1); }
651
652 // PyObject* PyNumber_InPlaceOr(        PyObject *o1, PyObject *o2)
653 template <class T> static FixedArray2D<T> & operator |= (FixedArray2D<T> &a0, const FixedArray2D<T> &a1) { return apply_array2d_array2d_ibinary_op<op_ibitor,T,T>(a0,a1); }
654 template <class T> static FixedArray2D<T> & operator |= (FixedArray2D<T> &a0, const T &v1)               { return apply_array2d_scalar_ibinary_op<op_ibitor,T,T>(a0,v1); }
655
656 template <class T>
657 static void add_arithmetic_math_functions(boost::python::class_<FixedArray2D<T> > &c) {
658     using namespace boost::python;
659     c
660         .def("__add__",&apply_array2d_array2d_binary_op<op_add,T,T,T>)
661         .def("__add__",&apply_array2d_scalar_binary_op<op_add,T,T,T>)
662         .def("__radd__",&apply_array2d_scalar_binary_rop<op_add,T,T,T>)
663         .def("__sub__",&apply_array2d_array2d_binary_op<op_sub,T,T,T>)
664         .def("__sub__",&apply_array2d_scalar_binary_op<op_sub,T,T,T>)
665         .def("__rsub__",&apply_array2d_scalar_binary_op<op_rsub,T,T,T>)
666         .def("__mul__",&apply_array2d_array2d_binary_op<op_mul,T,T,T>)
667         .def("__mul__",&apply_array2d_scalar_binary_op<op_mul,T,T,T>)
668         .def("__rmul__",&apply_array2d_scalar_binary_rop<op_mul,T,T,T>)
669         .def("__div__",&apply_array2d_array2d_binary_op<op_div,T,T,T>)
670         .def("__div__",&apply_array2d_scalar_binary_op<op_div,T,T,T>)
671         .def("__truediv__",&apply_array2d_array2d_binary_op<op_div,T,T,T>)
672         .def("__truediv__",&apply_array2d_scalar_binary_op<op_div,T,T,T>)
673         .def("__neg__",&apply_array2d_unary_op<op_neg,T,T>)
674         .def("__iadd__",&apply_array2d_array2d_ibinary_op<op_iadd,T,T>,return_internal_reference<>())
675         .def("__iadd__",&apply_array2d_scalar_ibinary_op<op_iadd,T,T>,return_internal_reference<>())
676         .def("__isub__",&apply_array2d_array2d_ibinary_op<op_isub,T,T>,return_internal_reference<>())
677         .def("__isub__",&apply_array2d_scalar_ibinary_op<op_isub,T,T>,return_internal_reference<>())
678         .def("__imul__",&apply_array2d_array2d_ibinary_op<op_imul,T,T>,return_internal_reference<>())
679         .def("__imul__",&apply_array2d_scalar_ibinary_op<op_imul,T,T>,return_internal_reference<>())
680         .def("__idiv__",&apply_array2d_array2d_ibinary_op<op_idiv,T,T>,return_internal_reference<>())
681         .def("__idiv__",&apply_array2d_scalar_ibinary_op<op_idiv,T,T>,return_internal_reference<>())
682         .def("__itruediv__",&apply_array2d_array2d_ibinary_op<op_idiv,T,T>,return_internal_reference<>())
683         .def("__itruediv__",&apply_array2d_scalar_ibinary_op<op_idiv,T,T>,return_internal_reference<>())
684         ;
685 }
686
687
688 template <class T>
689 static void add_pow_math_functions(boost::python::class_<FixedArray2D<T> > &c) {
690     using namespace boost::python;
691     c
692         .def("__pow__",&apply_array2d_array2d_binary_op<op_pow,T,T,T>)
693         .def("__pow__",&apply_array2d_scalar_binary_op<op_pow,T,T,T>)
694         .def("__rpow__",&apply_array2d_scalar_binary_rop<op_rpow,T,T,T>)
695         .def("__ipow__",&apply_array2d_array2d_ibinary_op<op_ipow,T,T>,return_internal_reference<>())
696         .def("__ipow__",&apply_array2d_scalar_ibinary_op<op_ipow,T,T>,return_internal_reference<>())
697         ;
698 }
699
700 template <class T>
701 static void add_mod_math_functions(boost::python::class_<FixedArray2D<T> > &c) {
702     using namespace boost::python;
703     c
704         .def("__mod__",&apply_array2d_array2d_binary_op<op_mod,T,T,T>)
705         .def("__mod__",&apply_array2d_scalar_binary_op<op_mod,T,T,T>)
706         .def("__imod__",&apply_array2d_array2d_ibinary_op<op_imod,T,T>,return_internal_reference<>())
707         .def("__imod__",&apply_array2d_scalar_ibinary_op<op_imod,T,T>,return_internal_reference<>())
708         ;
709 }
710
711 template <class T>
712 static void add_shift_math_functions(boost::python::class_<FixedArray2D<T> > &c) {
713     using namespace boost::python;
714     c
715         .def("__lshift__",&apply_array2d_array2d_binary_op<op_lshift,T,T,T>)
716         .def("__lshift__",&apply_array2d_scalar_binary_op<op_lshift,T,T,T>)
717         .def("__ilshift__",&apply_array2d_array2d_ibinary_op<op_ilshift,T,T>,return_internal_reference<>())
718         .def("__ilshift__",&apply_array2d_scalar_ibinary_op<op_ilshift,T,T>,return_internal_reference<>())
719         .def("__rshift__",&apply_array2d_array2d_binary_op<op_rshift,T,T,T>)
720         .def("__rshift__",&apply_array2d_scalar_binary_op<op_rshift,T,T,T>)
721         .def("__irshift__",&apply_array2d_array2d_ibinary_op<op_irshift,T,T>,return_internal_reference<>())
722         .def("__irshift__",&apply_array2d_scalar_ibinary_op<op_irshift,T,T>,return_internal_reference<>())
723         ;
724 }
725
726 template <class T>
727 static void add_bitwise_math_functions(boost::python::class_<FixedArray2D<T> > &c) {
728     using namespace boost::python;
729     c
730         .def("__and__",&apply_array2d_array2d_binary_op<op_bitand,T,T,T>)
731         .def("__and__",&apply_array2d_scalar_binary_op<op_bitand,T,T,T>)
732         .def("__iand__",&apply_array2d_array2d_ibinary_op<op_ibitand,T,T>,return_internal_reference<>())
733         .def("__iand__",&apply_array2d_scalar_ibinary_op<op_ibitand,T,T>,return_internal_reference<>())
734         .def("__or__",&apply_array2d_array2d_binary_op<op_bitor,T,T,T>)
735         .def("__or__",&apply_array2d_scalar_binary_op<op_bitor,T,T,T>)
736         .def("__ior__",&apply_array2d_array2d_ibinary_op<op_ibitor,T,T>,return_internal_reference<>())
737         .def("__ior__",&apply_array2d_scalar_ibinary_op<op_ibitor,T,T>,return_internal_reference<>())
738         .def("__xor__",&apply_array2d_array2d_binary_op<op_xor,T,T,T>)
739         .def("__xor__",&apply_array2d_scalar_binary_op<op_xor,T,T,T>)
740         .def("__ixor__",&apply_array2d_array2d_ibinary_op<op_ixor,T,T>,return_internal_reference<>())
741         .def("__ixor__",&apply_array2d_scalar_ibinary_op<op_ixor,T,T>,return_internal_reference<>())
742         ;
743 }
744
745 template <class T>
746 static void add_comparison_functions(boost::python::class_<FixedArray2D<T> > &c) {
747     using namespace boost::python;
748     c
749         .def("__eq__",&apply_array2d_array2d_binary_op<op_eq,T,T,int>)
750         .def("__eq__",&apply_array2d_scalar_binary_op<op_eq,T,T,int>)
751         .def("__ne__",&apply_array2d_array2d_binary_op<op_ne,T,T,int>)
752         .def("__ne__",&apply_array2d_scalar_binary_op<op_ne,T,T,int>)
753         ;
754 }
755
756 template <class T>
757 static void add_ordered_comparison_functions(boost::python::class_<FixedArray2D<T> > &c) {
758     using namespace boost::python;
759     c
760         .def("__lt__",&apply_array2d_array2d_binary_op<op_lt,T,T,int>)
761         .def("__lt__",&apply_array2d_scalar_binary_op<op_lt,T,T,int>)
762         .def("__gt__",&apply_array2d_array2d_binary_op<op_gt,T,T,int>)
763         .def("__gt__",&apply_array2d_scalar_binary_op<op_gt,T,T,int>)
764         .def("__le__",&apply_array2d_array2d_binary_op<op_le,T,T,int>)
765         .def("__le__",&apply_array2d_scalar_binary_op<op_le,T,T,int>)
766         .def("__ge__",&apply_array2d_array2d_binary_op<op_ge,T,T,int>)
767         .def("__ge__",&apply_array2d_scalar_binary_op<op_ge,T,T,int>)
768         ;
769 }
770
771 template <class S,class T>
772 static void add_explicit_construction_from_type(boost::python::class_<FixedArray2D<T> > &c) {
773     using namespace boost::python;
774     c.def(boost::python::init<FixedArray2D<S> >("copy contents of other array into this one"));
775 }
776
777 }
778
779 #endif