@@ -144,6 +144,129 @@ create_quad_unary_ufunc(PyObject *numpy, const char *ufunc_name)
144144 return 0 ;
145145}
146146
147+ // Logical NOT - returns bool instead of QuadPrecision
148+ static NPY_CASTING
149+ quad_unary_logical_op_resolve_descriptors (PyObject *self, PyArray_DTypeMeta *const dtypes[],
150+ PyArray_Descr *const given_descrs[], PyArray_Descr *loop_descrs[],
151+ npy_intp *NPY_UNUSED (view_offset))
152+ {
153+ Py_INCREF (given_descrs[0 ]);
154+ loop_descrs[0 ] = given_descrs[0 ];
155+
156+ // Output is always bool
157+ loop_descrs[1 ] = PyArray_DescrFromType (NPY_BOOL);
158+ if (!loop_descrs[1 ]) {
159+ return (NPY_CASTING)-1 ;
160+ }
161+
162+ return NPY_NO_CASTING;
163+ }
164+
165+ template <unary_logical_quad_def sleef_op, unary_logical_longdouble_def longdouble_op>
166+ int
167+ quad_logical_not_strided_loop_unaligned (PyArrayMethod_Context *context, char *const data[],
168+ npy_intp const dimensions[], npy_intp const strides[],
169+ NpyAuxData *auxdata)
170+ {
171+ npy_intp N = dimensions[0 ];
172+ char *in_ptr = data[0 ];
173+ char *out_ptr = data[1 ];
174+ npy_intp in_stride = strides[0 ];
175+ npy_intp out_stride = strides[1 ];
176+
177+ QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
178+ QuadBackendType backend = descr->backend ;
179+ size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof (Sleef_quad) : sizeof (long double );
180+
181+ quad_value in;
182+ while (N--) {
183+ memcpy (&in, in_ptr, elem_size);
184+ npy_bool result;
185+
186+ if (backend == BACKEND_SLEEF) {
187+ result = sleef_op (&in.sleef_value );
188+ }
189+ else {
190+ result = longdouble_op (&in.longdouble_value );
191+ }
192+
193+ memcpy (out_ptr, &result, sizeof (npy_bool));
194+
195+ in_ptr += in_stride;
196+ out_ptr += out_stride;
197+ }
198+ return 0 ;
199+ }
200+
201+ template <unary_logical_quad_def sleef_op, unary_logical_longdouble_def longdouble_op>
202+ int
203+ quad_logical_not_strided_loop_aligned (PyArrayMethod_Context *context, char *const data[],
204+ npy_intp const dimensions[], npy_intp const strides[],
205+ NpyAuxData *auxdata)
206+ {
207+ npy_intp N = dimensions[0 ];
208+ char *in_ptr = data[0 ];
209+ char *out_ptr = data[1 ];
210+ npy_intp in_stride = strides[0 ];
211+ npy_intp out_stride = strides[1 ];
212+
213+ QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
214+ QuadBackendType backend = descr->backend ;
215+
216+ while (N--) {
217+ npy_bool result;
218+
219+ if (backend == BACKEND_SLEEF) {
220+ result = sleef_op ((Sleef_quad *)in_ptr);
221+ }
222+ else {
223+ result = longdouble_op ((long double *)in_ptr);
224+ }
225+
226+ *(npy_bool *)out_ptr = result;
227+
228+ in_ptr += in_stride;
229+ out_ptr += out_stride;
230+ }
231+ return 0 ;
232+ }
233+
234+ template <unary_logical_quad_def sleef_op, unary_logical_longdouble_def longdouble_op>
235+ int
236+ create_quad_logical_not_ufunc (PyObject *numpy, const char *ufunc_name)
237+ {
238+ PyObject *ufunc = PyObject_GetAttrString (numpy, ufunc_name);
239+ if (ufunc == NULL ) {
240+ return -1 ;
241+ }
242+
243+ PyArray_DTypeMeta *dtypes[2 ] = {&QuadPrecDType, &PyArray_BoolDType};
244+
245+ PyType_Slot slots[] = {
246+ {NPY_METH_resolve_descriptors, (void *)&quad_unary_logical_op_resolve_descriptors},
247+ {NPY_METH_strided_loop,
248+ (void *)&quad_logical_not_strided_loop_aligned<sleef_op, longdouble_op>},
249+ {NPY_METH_unaligned_strided_loop,
250+ (void *)&quad_logical_not_strided_loop_unaligned<sleef_op, longdouble_op>},
251+ {0 , NULL }};
252+
253+ PyArrayMethod_Spec Spec = {
254+ .name = " quad_logical_not" ,
255+ .nin = 1 ,
256+ .nout = 1 ,
257+ .casting = NPY_NO_CASTING,
258+ .flags = NPY_METH_SUPPORTS_UNALIGNED,
259+ .dtypes = dtypes,
260+ .slots = slots,
261+ };
262+
263+ if (PyUFunc_AddLoopFromSpec (ufunc, &Spec) < 0 ) {
264+ return -1 ;
265+ }
266+
267+ return 0 ;
268+ }
269+
147270int
148271init_quad_unary_ops (PyObject *numpy)
149272{
@@ -262,5 +385,11 @@ init_quad_unary_ops(PyObject *numpy)
262385 if (create_quad_unary_ufunc<quad_radians, ld_radians>(numpy, " deg2rad" ) < 0 ) {
263386 return -1 ;
264387 }
388+
389+ // Logical operations (unary: not)
390+ if (create_quad_logical_not_ufunc<quad_logical_not, ld_logical_not>(numpy, " logical_not" ) < 0 ) {
391+ return -1 ;
392+ }
393+
265394 return 0 ;
266395}
0 commit comments