@@ -267,6 +267,147 @@ create_quad_logical_not_ufunc(PyObject *numpy, const char *ufunc_name)
267267    return  0 ;
268268}
269269
270+ //  Resolver for unary ufuncs with 2 outputs (like modf)
271+ static  NPY_CASTING
272+ quad_unary_op_2out_resolve_descriptors (PyObject *self, PyArray_DTypeMeta *const  dtypes[],
273+                                        PyArray_Descr *const  given_descrs[], PyArray_Descr *loop_descrs[],
274+                                        npy_intp *NPY_UNUSED (view_offset))
275+ {
276+     //  Input descriptor
277+     Py_INCREF (given_descrs[0 ]);
278+     loop_descrs[0 ] = given_descrs[0 ];
279+ 
280+     //  Output descriptors (2 outputs)
281+     for  (int  i = 1 ; i < 3 ; i++) {
282+         if  (given_descrs[i] == NULL ) {
283+             Py_INCREF (given_descrs[0 ]);
284+             loop_descrs[i] = given_descrs[0 ];
285+         }
286+         else  {
287+             Py_INCREF (given_descrs[i]);
288+             loop_descrs[i] = given_descrs[i];
289+         }
290+     }
291+ 
292+     QuadPrecDTypeObject *descr_in = (QuadPrecDTypeObject *)given_descrs[0 ];
293+     QuadPrecDTypeObject *descr_out1 = (QuadPrecDTypeObject *)loop_descrs[1 ];
294+     QuadPrecDTypeObject *descr_out2 = (QuadPrecDTypeObject *)loop_descrs[2 ];
295+ 
296+     if  (descr_in->backend  != descr_out1->backend  || descr_in->backend  != descr_out2->backend ) {
297+         return  NPY_UNSAFE_CASTING;
298+     }
299+ 
300+     return  NPY_NO_CASTING;
301+ }
302+ 
303+ //  Strided loop for unary ops with 2 outputs (unaligned)
304+ template  <unary_op_2out_quad_def sleef_op, unary_op_2out_longdouble_def longdouble_op>
305+ int 
306+ quad_generic_unary_op_2out_strided_loop_unaligned (PyArrayMethod_Context *context, char  *const  data[],
307+                                                   npy_intp const  dimensions[], npy_intp const  strides[],
308+                                                   NpyAuxData *auxdata)
309+ {
310+     npy_intp N = dimensions[0 ];
311+     char  *in_ptr = data[0 ];
312+     char  *out1_ptr = data[1 ];
313+     char  *out2_ptr = data[2 ];
314+     npy_intp in_stride = strides[0 ];
315+     npy_intp out1_stride = strides[1 ];
316+     npy_intp out2_stride = strides[2 ];
317+ 
318+     QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
319+     QuadBackendType backend = descr->backend ;
320+     size_t  elem_size = (backend == BACKEND_SLEEF) ? sizeof (Sleef_quad) : sizeof (long  double );
321+ 
322+     quad_value in, out1, out2;
323+     while  (N--) {
324+         memcpy (&in, in_ptr, elem_size);
325+         if  (backend == BACKEND_SLEEF) {
326+             sleef_op (&in.sleef_value , &out1.sleef_value , &out2.sleef_value );
327+         }
328+         else  {
329+             longdouble_op (&in.longdouble_value , &out1.longdouble_value , &out2.longdouble_value );
330+         }
331+         memcpy (out1_ptr, &out1, elem_size);
332+         memcpy (out2_ptr, &out2, elem_size);
333+ 
334+         in_ptr += in_stride;
335+         out1_ptr += out1_stride;
336+         out2_ptr += out2_stride;
337+     }
338+     return  0 ;
339+ }
340+ 
341+ //  Strided loop for unary ops with 2 outputs (aligned)
342+ template  <unary_op_2out_quad_def sleef_op, unary_op_2out_longdouble_def longdouble_op>
343+ int 
344+ quad_generic_unary_op_2out_strided_loop_aligned (PyArrayMethod_Context *context, char  *const  data[],
345+                                                npy_intp const  dimensions[], npy_intp const  strides[],
346+                                                NpyAuxData *auxdata)
347+ {
348+     npy_intp N = dimensions[0 ];
349+     char  *in_ptr = data[0 ];
350+     char  *out1_ptr = data[1 ];
351+     char  *out2_ptr = data[2 ];
352+     npy_intp in_stride = strides[0 ];
353+     npy_intp out1_stride = strides[1 ];
354+     npy_intp out2_stride = strides[2 ];
355+ 
356+     QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
357+     QuadBackendType backend = descr->backend ;
358+ 
359+     while  (N--) {
360+         if  (backend == BACKEND_SLEEF) {
361+             sleef_op ((Sleef_quad *)in_ptr, (Sleef_quad *)out1_ptr, (Sleef_quad *)out2_ptr);
362+         }
363+         else  {
364+             longdouble_op ((long  double  *)in_ptr, (long  double  *)out1_ptr, (long  double  *)out2_ptr);
365+         }
366+         in_ptr += in_stride;
367+         out1_ptr += out1_stride;
368+         out2_ptr += out2_stride;
369+     }
370+     return  0 ;
371+ }
372+ 
373+ //  Create unary ufunc with 2 outputs
374+ template  <unary_op_2out_quad_def sleef_op, unary_op_2out_longdouble_def longdouble_op>
375+ int 
376+ create_quad_unary_2out_ufunc (PyObject *numpy, const  char  *ufunc_name)
377+ {
378+     PyObject *ufunc = PyObject_GetAttrString (numpy, ufunc_name);
379+     if  (ufunc == NULL ) {
380+         return  -1 ;
381+     }
382+ 
383+     //  1 input, 2 outputs
384+     PyArray_DTypeMeta *dtypes[3 ] = {&QuadPrecDType, &QuadPrecDType, &QuadPrecDType};
385+ 
386+     PyType_Slot slots[] = {
387+             {NPY_METH_resolve_descriptors, (void  *)&quad_unary_op_2out_resolve_descriptors},
388+             {NPY_METH_strided_loop,
389+              (void  *)&quad_generic_unary_op_2out_strided_loop_aligned<sleef_op, longdouble_op>},
390+             {NPY_METH_unaligned_strided_loop,
391+              (void  *)&quad_generic_unary_op_2out_strided_loop_unaligned<sleef_op, longdouble_op>},
392+             {0 , NULL }};
393+ 
394+     PyArrayMethod_Spec Spec = {
395+             .name  = " quad_unary_2out" 
396+             .nin  = 1 ,
397+             .nout  = 2 ,
398+             .casting  = NPY_NO_CASTING,
399+             .flags  = NPY_METH_SUPPORTS_UNALIGNED,
400+             .dtypes  = dtypes,
401+             .slots  = slots,
402+     };
403+ 
404+     if  (PyUFunc_AddLoopFromSpec (ufunc, &Spec) < 0 ) {
405+         return  -1 ;
406+     }
407+ 
408+     return  0 ;
409+ }
410+ 
270411int 
271412init_quad_unary_ops (PyObject *numpy)
272413{
@@ -394,5 +535,10 @@ init_quad_unary_ops(PyObject *numpy)
394535        return  -1 ;
395536    }
396537
538+     //  Unary operations with 2 outputs
539+     if  (create_quad_unary_2out_ufunc<quad_modf, ld_modf>(numpy, " modf" 0 ) {
540+         return  -1 ;
541+     }
542+     
397543    return  0 ;
398544}
0 commit comments