Skip to main content

aocl_sparse/
complex.rs

1//! Complex-precision sparse operations (`Complex32` / `Complex64`).
2//!
3//! These mirror the real-precision API: sparse vector AXPY / gather /
4//! scatter, plus a [`ComplexSparseMatrix`] handle wrapping AOCL's
5//! `aoclsparse_matrix` and operations against it: matrix–vector product
6//! [`mv`], triangular solve [`trsv`], sparse-sparse product [`spmmd`],
7//! ILU(0) smoother [`ilu_smoother`], and the iterative-solver suite via
8//! [`ComplexIterSolver`].
9
10use std::ffi::CString;
11use std::marker::PhantomData;
12
13use aocl_sparse_sys as sys;
14use aocl_types::{sealed::Sealed, Complex32, Complex64};
15
16use crate::{check_status, trans_raw, Error, IndexBase, MatDescr, Order, Result, SorType, Trans};
17
18/// Scalar element type for the complex sparse routines (`c32`, `c64`).
19pub trait ComplexScalar: Copy + Sized + Sealed {
20    /// `y[indx] := α·x + y[indx]` (sparse vector AXPY).
21    fn axpyi(alpha: Self, x: &[Self], indx: &[sys::aoclsparse_int], y: &mut [Self]) -> Result<()>;
22
23    /// `x[i] := y[indx[i]]` (gather).
24    fn gthr(y: &[Self], indx: &[sys::aoclsparse_int], x: &mut [Self]) -> Result<()>;
25
26    /// `y[indx[i]] := x[i]` (scatter).
27    fn sctr(x: &[Self], indx: &[sys::aoclsparse_int], y: &mut [Self]) -> Result<()>;
28
29    /// Wrap raw CSR pointers in an `aoclsparse_matrix` handle.
30    #[allow(clippy::too_many_arguments)]
31    fn create_csr(
32        base: IndexBase,
33        m: usize,
34        n: usize,
35        nnz: usize,
36        row_ptr: *mut sys::aoclsparse_int,
37        col_idx: *mut sys::aoclsparse_int,
38        val: *mut Self,
39    ) -> Result<sys::aoclsparse_matrix>;
40
41    /// Read out a library-owned CSR matrix's metadata and array pointers.
42    fn export_csr(
43        mat: sys::aoclsparse_matrix,
44    ) -> Result<(
45        IndexBase,
46        usize,
47        usize,
48        usize,
49        *mut sys::aoclsparse_int,
50        *mut sys::aoclsparse_int,
51        *mut Self,
52    )>;
53
54    /// `y := α · op(A) · x + β · y` via the high-level matrix handle.
55    #[allow(clippy::too_many_arguments)]
56    fn mv(
57        op: Trans,
58        alpha: Self,
59        mat: sys::aoclsparse_matrix,
60        descr: &MatDescr,
61        x: &[Self],
62        beta: Self,
63        y: &mut [Self],
64    ) -> Result<()>;
65
66    /// Solve `op(A) · x = α · b` where `A` is sparse triangular.
67    #[allow(clippy::too_many_arguments)]
68    fn trsv(
69        op: Trans,
70        alpha: Self,
71        mat: sys::aoclsparse_matrix,
72        descr: &MatDescr,
73        b: &[Self],
74        x: &mut [Self],
75    ) -> Result<()>;
76
77    /// One ILU(0) smoothing step against the matrix handle.
78    fn ilu_smoother(
79        op: Trans,
80        a: sys::aoclsparse_matrix,
81        descr: &MatDescr,
82        x: &mut [Self],
83        b: &[Self],
84    ) -> Result<()>;
85
86    /// Initialise an iterative-solver handle for this scalar type.
87    fn itsol_init(handle: &mut sys::aoclsparse_itsol_handle) -> Result<()>;
88
89    /// Run the iterative solver's forward (direct) interface.
90    #[allow(clippy::too_many_arguments)]
91    fn itsol_solve(
92        handle: sys::aoclsparse_itsol_handle,
93        n: usize,
94        mat: sys::aoclsparse_matrix,
95        descr: &MatDescr,
96        b: &[Self],
97        x: &mut [Self],
98        rinfo: &mut [Self::Real; 100],
99    ) -> Result<()>;
100
101    /// `C := α · op(A) · B + β · C` where `A` is sparse (CSR via the
102    /// matrix handle) and `B`, `C` are dense.
103    #[allow(clippy::too_many_arguments)]
104    fn csrmm(
105        op: Trans,
106        alpha: Self,
107        a: sys::aoclsparse_matrix,
108        descr: &MatDescr,
109        order: Order,
110        b: &[Self],
111        n: usize,
112        ldb: usize,
113        beta: Self,
114        c: &mut [Self],
115        ldc: usize,
116    ) -> Result<()>;
117
118    /// `C := op(A) · B` where both `A` and `B` are sparse and `C` is dense.
119    #[allow(clippy::too_many_arguments)]
120    fn spmmd(
121        op: Trans,
122        a: sys::aoclsparse_matrix,
123        b: sys::aoclsparse_matrix,
124        layout: Order,
125        c: &mut [Self],
126        ldc: usize,
127    ) -> Result<()>;
128
129    /// `C := α · op_A(A) · op_B(B) + β · C` (sparse·sparse → dense).
130    #[allow(clippy::too_many_arguments)]
131    fn sp2md(
132        op_a: Trans,
133        descr_a: &MatDescr,
134        a: sys::aoclsparse_matrix,
135        op_b: Trans,
136        descr_b: &MatDescr,
137        b: sys::aoclsparse_matrix,
138        alpha: Self,
139        beta: Self,
140        c: &mut [Self],
141        layout: Order,
142        ldc: usize,
143    ) -> Result<()>;
144
145    /// Sparse-sparse `C := α · op(A) + B`.
146    ///
147    /// # Safety
148    /// Caller must adopt the resulting handle (e.g. via
149    /// [`ComplexSparseMatrix::from_library_owned`]).
150    unsafe fn add_ffi(
151        op: sys::aoclsparse_operation,
152        a: sys::aoclsparse_matrix,
153        alpha: Self,
154        b: sys::aoclsparse_matrix,
155        out: *mut sys::aoclsparse_matrix,
156    ) -> sys::aoclsparse_status;
157
158    /// One step of (S)SOR / forward / backward Gauss-Seidel relaxation.
159    #[allow(clippy::too_many_arguments)]
160    fn sorv(
161        sor_type: SorType,
162        descr: &MatDescr,
163        a: sys::aoclsparse_matrix,
164        omega: Self,
165        alpha: Self,
166        x: &mut [Self],
167        b: &[Self],
168    ) -> Result<()>;
169
170    /// Underlying real type — `f32` for `Complex32`, `f64` for `Complex64`.
171    /// Used to type the `rinfo[100]` array returned by iterative solvers,
172    /// which is real-valued even for complex problems.
173    type Real: Copy + Default;
174}
175
176// =========================================================================
177//   Complex32 (c32) impl
178// =========================================================================
179
180impl ComplexScalar for Complex32 {
181    type Real = f32;
182
183    fn axpyi(alpha: Self, x: &[Self], indx: &[sys::aoclsparse_int], y: &mut [Self]) -> Result<()> {
184        let status = unsafe {
185            sys::aoclsparse_caxpyi(
186                x.len() as sys::aoclsparse_int,
187                &alpha as *const _ as *const std::os::raw::c_void,
188                x.as_ptr() as *const std::os::raw::c_void,
189                indx.as_ptr(),
190                y.as_mut_ptr() as *mut std::os::raw::c_void,
191            )
192        };
193        check_status("sparse", status)
194    }
195
196    fn gthr(y: &[Self], indx: &[sys::aoclsparse_int], x: &mut [Self]) -> Result<()> {
197        let status = unsafe {
198            sys::aoclsparse_cgthr(
199                x.len() as sys::aoclsparse_int,
200                y.as_ptr() as *const std::os::raw::c_void,
201                x.as_mut_ptr() as *mut std::os::raw::c_void,
202                indx.as_ptr(),
203            )
204        };
205        check_status("sparse", status)
206    }
207
208    fn sctr(x: &[Self], indx: &[sys::aoclsparse_int], y: &mut [Self]) -> Result<()> {
209        let status = unsafe {
210            sys::aoclsparse_csctr(
211                x.len() as sys::aoclsparse_int,
212                x.as_ptr() as *const std::os::raw::c_void,
213                indx.as_ptr(),
214                y.as_mut_ptr() as *mut std::os::raw::c_void,
215            )
216        };
217        check_status("sparse", status)
218    }
219
220    fn create_csr(
221        base: IndexBase,
222        m: usize,
223        n: usize,
224        nnz: usize,
225        row_ptr: *mut sys::aoclsparse_int,
226        col_idx: *mut sys::aoclsparse_int,
227        val: *mut Self,
228    ) -> Result<sys::aoclsparse_matrix> {
229        let mut raw: sys::aoclsparse_matrix = std::ptr::null_mut();
230        let status = unsafe {
231            sys::aoclsparse_create_ccsr(
232                &mut raw,
233                base.raw_for_complex(),
234                m as sys::aoclsparse_int,
235                n as sys::aoclsparse_int,
236                nnz as sys::aoclsparse_int,
237                row_ptr,
238                col_idx,
239                val as *mut sys::aoclsparse_float_complex,
240            )
241        };
242        check_status("sparse", status)?;
243        if raw.is_null() {
244            return Err(Error::AllocationFailed("sparse"));
245        }
246        Ok(raw)
247    }
248
249    fn export_csr(
250        mat: sys::aoclsparse_matrix,
251    ) -> Result<(
252        IndexBase,
253        usize,
254        usize,
255        usize,
256        *mut sys::aoclsparse_int,
257        *mut sys::aoclsparse_int,
258        *mut Self,
259    )> {
260        let mut base: sys::aoclsparse_index_base = 0;
261        let mut m: sys::aoclsparse_int = 0;
262        let mut n: sys::aoclsparse_int = 0;
263        let mut nnz: sys::aoclsparse_int = 0;
264        let mut row_ptr: *mut sys::aoclsparse_int = std::ptr::null_mut();
265        let mut col_ind: *mut sys::aoclsparse_int = std::ptr::null_mut();
266        let mut val: *mut sys::aoclsparse_float_complex = std::ptr::null_mut();
267        let status = unsafe {
268            sys::aoclsparse_export_ccsr(
269                mat,
270                &mut base,
271                &mut m,
272                &mut n,
273                &mut nnz,
274                &mut row_ptr,
275                &mut col_ind,
276                &mut val,
277            )
278        };
279        check_status("sparse", status)?;
280        let base_e = if base == sys::aoclsparse_index_base__aoclsparse_index_base_one {
281            IndexBase::One
282        } else {
283            IndexBase::Zero
284        };
285        Ok((
286            base_e,
287            m as usize,
288            n as usize,
289            nnz as usize,
290            row_ptr,
291            col_ind,
292            val as *mut Self,
293        ))
294    }
295
296    fn mv(
297        op: Trans,
298        alpha: Self,
299        mat: sys::aoclsparse_matrix,
300        descr: &MatDescr,
301        x: &[Self],
302        beta: Self,
303        y: &mut [Self],
304    ) -> Result<()> {
305        let status = unsafe {
306            sys::aoclsparse_cmv(
307                trans_raw(op),
308                &alpha as *const _ as *const sys::aoclsparse_float_complex,
309                mat,
310                descr.as_raw(),
311                x.as_ptr() as *const sys::aoclsparse_float_complex,
312                &beta as *const _ as *const sys::aoclsparse_float_complex,
313                y.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
314            )
315        };
316        check_status("sparse", status)
317    }
318
319    fn trsv(
320        op: Trans,
321        alpha: Self,
322        mat: sys::aoclsparse_matrix,
323        descr: &MatDescr,
324        b: &[Self],
325        x: &mut [Self],
326    ) -> Result<()> {
327        // aoclsparse_ctrsv takes alpha by-value (struct), unlike mv.
328        let alpha_raw = sys::aoclsparse_float_complex {
329            real: alpha.re,
330            imag: alpha.im,
331        };
332        let status = unsafe {
333            sys::aoclsparse_ctrsv(
334                trans_raw(op),
335                alpha_raw,
336                mat,
337                descr.as_raw(),
338                b.as_ptr() as *const sys::aoclsparse_float_complex,
339                x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
340            )
341        };
342        check_status("sparse", status)
343    }
344
345    fn ilu_smoother(
346        op: Trans,
347        a: sys::aoclsparse_matrix,
348        descr: &MatDescr,
349        x: &mut [Self],
350        b: &[Self],
351    ) -> Result<()> {
352        let mut precond_csr_val: *mut sys::aoclsparse_float_complex = std::ptr::null_mut();
353        let status = unsafe {
354            sys::aoclsparse_cilu_smoother(
355                trans_raw(op),
356                a,
357                descr.as_raw(),
358                &mut precond_csr_val,
359                std::ptr::null(),
360                x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
361                b.as_ptr() as *const sys::aoclsparse_float_complex,
362            )
363        };
364        check_status("sparse", status)
365    }
366
367    fn itsol_init(handle: &mut sys::aoclsparse_itsol_handle) -> Result<()> {
368        let status = unsafe { sys::aoclsparse_itsol_c_init(handle) };
369        check_status("sparse", status)
370    }
371
372    fn itsol_solve(
373        handle: sys::aoclsparse_itsol_handle,
374        n: usize,
375        mat: sys::aoclsparse_matrix,
376        descr: &MatDescr,
377        b: &[Self],
378        x: &mut [Self],
379        rinfo: &mut [f32; 100],
380    ) -> Result<()> {
381        if b.len() < n || x.len() < n {
382            return Err(Error::InvalidArgument(format!(
383                "itsol_solve: b.len()={}, x.len()={}, n={n}",
384                b.len(),
385                x.len()
386            )));
387        }
388        let status = unsafe {
389            sys::aoclsparse_itsol_c_solve(
390                handle,
391                n as sys::aoclsparse_int,
392                mat,
393                descr.as_raw(),
394                b.as_ptr() as *const sys::aoclsparse_float_complex,
395                x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
396                rinfo.as_mut_ptr(),
397                None,
398                None,
399                std::ptr::null_mut(),
400            )
401        };
402        check_status("sparse", status)
403    }
404
405    fn csrmm(
406        op: Trans,
407        alpha: Self,
408        a: sys::aoclsparse_matrix,
409        descr: &MatDescr,
410        order: Order,
411        b: &[Self],
412        n: usize,
413        ldb: usize,
414        beta: Self,
415        c: &mut [Self],
416        ldc: usize,
417    ) -> Result<()> {
418        let alpha_raw = sys::aoclsparse_float_complex {
419            real: alpha.re,
420            imag: alpha.im,
421        };
422        let beta_raw = sys::aoclsparse_float_complex {
423            real: beta.re,
424            imag: beta.im,
425        };
426        let status = unsafe {
427            sys::aoclsparse_ccsrmm(
428                trans_raw(op),
429                alpha_raw,
430                a,
431                descr.as_raw(),
432                order.raw(),
433                b.as_ptr() as *const sys::aoclsparse_float_complex,
434                n as sys::aoclsparse_int,
435                ldb as sys::aoclsparse_int,
436                beta_raw,
437                c.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
438                ldc as sys::aoclsparse_int,
439            )
440        };
441        check_status("sparse", status)
442    }
443
444    fn spmmd(
445        op: Trans,
446        a: sys::aoclsparse_matrix,
447        b: sys::aoclsparse_matrix,
448        layout: Order,
449        c: &mut [Self],
450        ldc: usize,
451    ) -> Result<()> {
452        let status = unsafe {
453            sys::aoclsparse_cspmmd(
454                trans_raw(op),
455                a,
456                b,
457                layout.raw(),
458                c.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
459                ldc as sys::aoclsparse_int,
460            )
461        };
462        check_status("sparse", status)
463    }
464
465    fn sp2md(
466        op_a: Trans,
467        descr_a: &MatDescr,
468        a: sys::aoclsparse_matrix,
469        op_b: Trans,
470        descr_b: &MatDescr,
471        b: sys::aoclsparse_matrix,
472        alpha: Self,
473        beta: Self,
474        c: &mut [Self],
475        layout: Order,
476        ldc: usize,
477    ) -> Result<()> {
478        let alpha_raw = sys::aoclsparse_float_complex {
479            real: alpha.re,
480            imag: alpha.im,
481        };
482        let beta_raw = sys::aoclsparse_float_complex {
483            real: beta.re,
484            imag: beta.im,
485        };
486        let status = unsafe {
487            sys::aoclsparse_csp2md(
488                trans_raw(op_a),
489                descr_a.as_raw(),
490                a,
491                trans_raw(op_b),
492                descr_b.as_raw(),
493                b,
494                alpha_raw,
495                beta_raw,
496                c.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
497                layout.raw(),
498                ldc as sys::aoclsparse_int,
499            )
500        };
501        check_status("sparse", status)
502    }
503
504    unsafe fn add_ffi(
505        op: sys::aoclsparse_operation,
506        a: sys::aoclsparse_matrix,
507        alpha: Self,
508        b: sys::aoclsparse_matrix,
509        out: *mut sys::aoclsparse_matrix,
510    ) -> sys::aoclsparse_status {
511        let alpha_raw = sys::aoclsparse_float_complex {
512            real: alpha.re,
513            imag: alpha.im,
514        };
515        sys::aoclsparse_cadd(op, a, alpha_raw, b, out)
516    }
517
518    fn sorv(
519        sor_type: SorType,
520        descr: &MatDescr,
521        a: sys::aoclsparse_matrix,
522        omega: Self,
523        alpha: Self,
524        x: &mut [Self],
525        b: &[Self],
526    ) -> Result<()> {
527        let omega_raw = sys::aoclsparse_float_complex {
528            real: omega.re,
529            imag: omega.im,
530        };
531        let alpha_raw = sys::aoclsparse_float_complex {
532            real: alpha.re,
533            imag: alpha.im,
534        };
535        let status = unsafe {
536            sys::aoclsparse_csorv(
537                sor_type.raw(),
538                descr.as_raw(),
539                a,
540                omega_raw,
541                alpha_raw,
542                x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
543                b.as_ptr() as *const sys::aoclsparse_float_complex,
544            )
545        };
546        check_status("sparse", status)
547    }
548}
549
550// =========================================================================
551//   Complex64 (c64) impl
552// =========================================================================
553
554impl ComplexScalar for Complex64 {
555    type Real = f64;
556
557    fn axpyi(alpha: Self, x: &[Self], indx: &[sys::aoclsparse_int], y: &mut [Self]) -> Result<()> {
558        let status = unsafe {
559            sys::aoclsparse_zaxpyi(
560                x.len() as sys::aoclsparse_int,
561                &alpha as *const _ as *const std::os::raw::c_void,
562                x.as_ptr() as *const std::os::raw::c_void,
563                indx.as_ptr(),
564                y.as_mut_ptr() as *mut std::os::raw::c_void,
565            )
566        };
567        check_status("sparse", status)
568    }
569
570    fn gthr(y: &[Self], indx: &[sys::aoclsparse_int], x: &mut [Self]) -> Result<()> {
571        let status = unsafe {
572            sys::aoclsparse_zgthr(
573                x.len() as sys::aoclsparse_int,
574                y.as_ptr() as *const std::os::raw::c_void,
575                x.as_mut_ptr() as *mut std::os::raw::c_void,
576                indx.as_ptr(),
577            )
578        };
579        check_status("sparse", status)
580    }
581
582    fn sctr(x: &[Self], indx: &[sys::aoclsparse_int], y: &mut [Self]) -> Result<()> {
583        let status = unsafe {
584            sys::aoclsparse_zsctr(
585                x.len() as sys::aoclsparse_int,
586                x.as_ptr() as *const std::os::raw::c_void,
587                indx.as_ptr(),
588                y.as_mut_ptr() as *mut std::os::raw::c_void,
589            )
590        };
591        check_status("sparse", status)
592    }
593
594    fn create_csr(
595        base: IndexBase,
596        m: usize,
597        n: usize,
598        nnz: usize,
599        row_ptr: *mut sys::aoclsparse_int,
600        col_idx: *mut sys::aoclsparse_int,
601        val: *mut Self,
602    ) -> Result<sys::aoclsparse_matrix> {
603        let mut raw: sys::aoclsparse_matrix = std::ptr::null_mut();
604        let status = unsafe {
605            sys::aoclsparse_create_zcsr(
606                &mut raw,
607                base.raw_for_complex(),
608                m as sys::aoclsparse_int,
609                n as sys::aoclsparse_int,
610                nnz as sys::aoclsparse_int,
611                row_ptr,
612                col_idx,
613                val as *mut sys::aoclsparse_double_complex,
614            )
615        };
616        check_status("sparse", status)?;
617        if raw.is_null() {
618            return Err(Error::AllocationFailed("sparse"));
619        }
620        Ok(raw)
621    }
622
623    fn export_csr(
624        mat: sys::aoclsparse_matrix,
625    ) -> Result<(
626        IndexBase,
627        usize,
628        usize,
629        usize,
630        *mut sys::aoclsparse_int,
631        *mut sys::aoclsparse_int,
632        *mut Self,
633    )> {
634        let mut base: sys::aoclsparse_index_base = 0;
635        let mut m: sys::aoclsparse_int = 0;
636        let mut n: sys::aoclsparse_int = 0;
637        let mut nnz: sys::aoclsparse_int = 0;
638        let mut row_ptr: *mut sys::aoclsparse_int = std::ptr::null_mut();
639        let mut col_ind: *mut sys::aoclsparse_int = std::ptr::null_mut();
640        let mut val: *mut sys::aoclsparse_double_complex = std::ptr::null_mut();
641        let status = unsafe {
642            sys::aoclsparse_export_zcsr(
643                mat,
644                &mut base,
645                &mut m,
646                &mut n,
647                &mut nnz,
648                &mut row_ptr,
649                &mut col_ind,
650                &mut val,
651            )
652        };
653        check_status("sparse", status)?;
654        let base_e = if base == sys::aoclsparse_index_base__aoclsparse_index_base_one {
655            IndexBase::One
656        } else {
657            IndexBase::Zero
658        };
659        Ok((
660            base_e,
661            m as usize,
662            n as usize,
663            nnz as usize,
664            row_ptr,
665            col_ind,
666            val as *mut Self,
667        ))
668    }
669
670    fn mv(
671        op: Trans,
672        alpha: Self,
673        mat: sys::aoclsparse_matrix,
674        descr: &MatDescr,
675        x: &[Self],
676        beta: Self,
677        y: &mut [Self],
678    ) -> Result<()> {
679        let status = unsafe {
680            sys::aoclsparse_zmv(
681                trans_raw(op),
682                &alpha as *const _ as *const sys::aoclsparse_double_complex,
683                mat,
684                descr.as_raw(),
685                x.as_ptr() as *const sys::aoclsparse_double_complex,
686                &beta as *const _ as *const sys::aoclsparse_double_complex,
687                y.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
688            )
689        };
690        check_status("sparse", status)
691    }
692
693    fn trsv(
694        op: Trans,
695        alpha: Self,
696        mat: sys::aoclsparse_matrix,
697        descr: &MatDescr,
698        b: &[Self],
699        x: &mut [Self],
700    ) -> Result<()> {
701        let alpha_raw = sys::aoclsparse_double_complex_ {
702            real: alpha.re,
703            imag: alpha.im,
704        };
705        let status = unsafe {
706            sys::aoclsparse_ztrsv(
707                trans_raw(op),
708                alpha_raw,
709                mat,
710                descr.as_raw(),
711                b.as_ptr() as *const sys::aoclsparse_double_complex,
712                x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
713            )
714        };
715        check_status("sparse", status)
716    }
717
718    fn ilu_smoother(
719        op: Trans,
720        a: sys::aoclsparse_matrix,
721        descr: &MatDescr,
722        x: &mut [Self],
723        b: &[Self],
724    ) -> Result<()> {
725        let mut precond_csr_val: *mut sys::aoclsparse_double_complex = std::ptr::null_mut();
726        let status = unsafe {
727            sys::aoclsparse_zilu_smoother(
728                trans_raw(op),
729                a,
730                descr.as_raw(),
731                &mut precond_csr_val,
732                std::ptr::null(),
733                x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
734                b.as_ptr() as *const sys::aoclsparse_double_complex,
735            )
736        };
737        check_status("sparse", status)
738    }
739
740    fn itsol_init(handle: &mut sys::aoclsparse_itsol_handle) -> Result<()> {
741        let status = unsafe { sys::aoclsparse_itsol_z_init(handle) };
742        check_status("sparse", status)
743    }
744
745    fn itsol_solve(
746        handle: sys::aoclsparse_itsol_handle,
747        n: usize,
748        mat: sys::aoclsparse_matrix,
749        descr: &MatDescr,
750        b: &[Self],
751        x: &mut [Self],
752        rinfo: &mut [f64; 100],
753    ) -> Result<()> {
754        if b.len() < n || x.len() < n {
755            return Err(Error::InvalidArgument(format!(
756                "itsol_solve: b.len()={}, x.len()={}, n={n}",
757                b.len(),
758                x.len()
759            )));
760        }
761        let status = unsafe {
762            sys::aoclsparse_itsol_z_solve(
763                handle,
764                n as sys::aoclsparse_int,
765                mat,
766                descr.as_raw(),
767                b.as_ptr() as *const sys::aoclsparse_double_complex,
768                x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
769                rinfo.as_mut_ptr(),
770                None,
771                None,
772                std::ptr::null_mut(),
773            )
774        };
775        check_status("sparse", status)
776    }
777
778    fn csrmm(
779        op: Trans,
780        alpha: Self,
781        a: sys::aoclsparse_matrix,
782        descr: &MatDescr,
783        order: Order,
784        b: &[Self],
785        n: usize,
786        ldb: usize,
787        beta: Self,
788        c: &mut [Self],
789        ldc: usize,
790    ) -> Result<()> {
791        let alpha_raw = sys::aoclsparse_double_complex_ {
792            real: alpha.re,
793            imag: alpha.im,
794        };
795        let beta_raw = sys::aoclsparse_double_complex_ {
796            real: beta.re,
797            imag: beta.im,
798        };
799        let status = unsafe {
800            sys::aoclsparse_zcsrmm(
801                trans_raw(op),
802                alpha_raw,
803                a,
804                descr.as_raw(),
805                order.raw(),
806                b.as_ptr() as *const sys::aoclsparse_double_complex,
807                n as sys::aoclsparse_int,
808                ldb as sys::aoclsparse_int,
809                beta_raw,
810                c.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
811                ldc as sys::aoclsparse_int,
812            )
813        };
814        check_status("sparse", status)
815    }
816
817    fn spmmd(
818        op: Trans,
819        a: sys::aoclsparse_matrix,
820        b: sys::aoclsparse_matrix,
821        layout: Order,
822        c: &mut [Self],
823        ldc: usize,
824    ) -> Result<()> {
825        let status = unsafe {
826            sys::aoclsparse_zspmmd(
827                trans_raw(op),
828                a,
829                b,
830                layout.raw(),
831                c.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
832                ldc as sys::aoclsparse_int,
833            )
834        };
835        check_status("sparse", status)
836    }
837
838    fn sp2md(
839        op_a: Trans,
840        descr_a: &MatDescr,
841        a: sys::aoclsparse_matrix,
842        op_b: Trans,
843        descr_b: &MatDescr,
844        b: sys::aoclsparse_matrix,
845        alpha: Self,
846        beta: Self,
847        c: &mut [Self],
848        layout: Order,
849        ldc: usize,
850    ) -> Result<()> {
851        let alpha_raw = sys::aoclsparse_double_complex_ {
852            real: alpha.re,
853            imag: alpha.im,
854        };
855        let beta_raw = sys::aoclsparse_double_complex_ {
856            real: beta.re,
857            imag: beta.im,
858        };
859        let status = unsafe {
860            sys::aoclsparse_zsp2md(
861                trans_raw(op_a),
862                descr_a.as_raw(),
863                a,
864                trans_raw(op_b),
865                descr_b.as_raw(),
866                b,
867                alpha_raw,
868                beta_raw,
869                c.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
870                layout.raw(),
871                ldc as sys::aoclsparse_int,
872            )
873        };
874        check_status("sparse", status)
875    }
876
877    unsafe fn add_ffi(
878        op: sys::aoclsparse_operation,
879        a: sys::aoclsparse_matrix,
880        alpha: Self,
881        b: sys::aoclsparse_matrix,
882        out: *mut sys::aoclsparse_matrix,
883    ) -> sys::aoclsparse_status {
884        let alpha_raw = sys::aoclsparse_double_complex_ {
885            real: alpha.re,
886            imag: alpha.im,
887        };
888        sys::aoclsparse_zadd(op, a, alpha_raw, b, out)
889    }
890
891    fn sorv(
892        sor_type: SorType,
893        descr: &MatDescr,
894        a: sys::aoclsparse_matrix,
895        omega: Self,
896        alpha: Self,
897        x: &mut [Self],
898        b: &[Self],
899    ) -> Result<()> {
900        let omega_raw = sys::aoclsparse_double_complex_ {
901            real: omega.re,
902            imag: omega.im,
903        };
904        let alpha_raw = sys::aoclsparse_double_complex_ {
905            real: alpha.re,
906            imag: alpha.im,
907        };
908        let status = unsafe {
909            sys::aoclsparse_zsorv(
910                sor_type.raw(),
911                descr.as_raw(),
912                a,
913                omega_raw,
914                alpha_raw,
915                x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
916                b.as_ptr() as *const sys::aoclsparse_double_complex,
917            )
918        };
919        check_status("sparse", status)
920    }
921}
922
923// =========================================================================
924//   Complex sparse matrix handle
925// =========================================================================
926
927enum CsrStorage<T: ComplexScalar> {
928    Owned {
929        _row_ptr: Vec<sys::aoclsparse_int>,
930        _col_ind: Vec<sys::aoclsparse_int>,
931        _val: Vec<T>,
932    },
933    LibraryOwned,
934}
935
936/// RAII wrapper for a complex `aoclsparse_matrix` in CSR format.
937pub struct ComplexSparseMatrix<T: ComplexScalar> {
938    raw: sys::aoclsparse_matrix,
939    #[allow(dead_code)]
940    storage: CsrStorage<T>,
941    base: IndexBase,
942    m: usize,
943    n: usize,
944    nnz: usize,
945}
946
947impl<T: ComplexScalar> ComplexSparseMatrix<T> {
948    /// Build from CSR arrays. Values are copied into the wrapper.
949    pub fn from_csr(
950        base: IndexBase,
951        m: usize,
952        n: usize,
953        row_ptr: &[sys::aoclsparse_int],
954        col_ind: &[sys::aoclsparse_int],
955        val: &[T],
956    ) -> Result<Self> {
957        if row_ptr.len() != m + 1 {
958            return Err(Error::InvalidArgument(format!(
959                "complex from_csr: row_ptr length {} != m+1 = {}",
960                row_ptr.len(),
961                m + 1
962            )));
963        }
964        let nnz = val.len();
965        if col_ind.len() != nnz {
966            return Err(Error::InvalidArgument(format!(
967                "complex from_csr: col_ind length {} != val length {nnz}",
968                col_ind.len()
969            )));
970        }
971        let mut row_ptr = row_ptr.to_vec();
972        let mut col_ind = col_ind.to_vec();
973        let mut val = val.to_vec();
974        let raw = T::create_csr(
975            base,
976            m,
977            n,
978            nnz,
979            row_ptr.as_mut_ptr(),
980            col_ind.as_mut_ptr(),
981            val.as_mut_ptr(),
982        )?;
983        Ok(Self {
984            raw,
985            storage: CsrStorage::Owned {
986                _row_ptr: row_ptr,
987                _col_ind: col_ind,
988                _val: val,
989            },
990            base,
991            m,
992            n,
993            nnz,
994        })
995    }
996
997    /// Adopt a library-allocated matrix handle (e.g. the result of
998    /// [`spmmd`]).
999    ///
1000    /// # Safety
1001    /// `raw` must be a valid `aoclsparse_matrix` whose precision matches
1002    /// `T` and whose internal storage is library-owned.
1003    pub unsafe fn from_library_owned(raw: sys::aoclsparse_matrix) -> Result<Self> {
1004        if raw.is_null() {
1005            return Err(Error::AllocationFailed("sparse"));
1006        }
1007        let (base, m, n, nnz, _, _, _) = T::export_csr(raw)?;
1008        Ok(Self {
1009            raw,
1010            storage: CsrStorage::LibraryOwned,
1011            base,
1012            m,
1013            n,
1014            nnz,
1015        })
1016    }
1017
1018    /// `(m, n)` dimensions.
1019    pub fn dims(&self) -> (usize, usize) {
1020        (self.m, self.n)
1021    }
1022    /// Number of explicitly stored non-zeros.
1023    pub fn nnz(&self) -> usize {
1024        self.nnz
1025    }
1026    /// Index base for row-pointer / column-index arrays.
1027    pub fn base(&self) -> IndexBase {
1028        self.base
1029    }
1030
1031    /// Borrow the raw `aoclsparse_matrix` handle. Do not call
1032    /// `aoclsparse_destroy` on it; the wrapper does.
1033    pub fn as_raw(&self) -> sys::aoclsparse_matrix {
1034        self.raw
1035    }
1036
1037    /// Read out the CSR contents as freshly allocated `Vec`s.
1038    pub fn export_csr(
1039        &self,
1040    ) -> Result<(
1041        IndexBase,
1042        Vec<sys::aoclsparse_int>,
1043        Vec<sys::aoclsparse_int>,
1044        Vec<T>,
1045    )> {
1046        let (base, m, _, nnz, row_ptr, col_ind, val) = T::export_csr(self.raw)?;
1047        let row_ptr = unsafe { std::slice::from_raw_parts(row_ptr, m + 1).to_vec() };
1048        let col_ind = unsafe { std::slice::from_raw_parts(col_ind, nnz).to_vec() };
1049        let val = unsafe { std::slice::from_raw_parts(val, nnz).to_vec() };
1050        Ok((base, row_ptr, col_ind, val))
1051    }
1052}
1053
1054impl<T: ComplexScalar> Drop for ComplexSparseMatrix<T> {
1055    fn drop(&mut self) {
1056        if !self.raw.is_null() {
1057            unsafe {
1058                let _ = sys::aoclsparse_destroy(&mut self.raw);
1059            }
1060            self.raw = std::ptr::null_mut();
1061        }
1062    }
1063}
1064
1065impl<T: ComplexScalar> std::fmt::Debug for ComplexSparseMatrix<T> {
1066    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1067        f.debug_struct("ComplexSparseMatrix")
1068            .field("m", &self.m)
1069            .field("n", &self.n)
1070            .field("nnz", &self.nnz)
1071            .field("base", &self.base)
1072            .finish()
1073    }
1074}
1075
1076// =========================================================================
1077//   Free functions
1078// =========================================================================
1079
1080/// `y[indx] := α·x + y[indx]`. `x` and `indx` must have equal length.
1081pub fn axpyi<T: ComplexScalar>(
1082    alpha: T,
1083    x: &[T],
1084    indx: &[sys::aoclsparse_int],
1085    y: &mut [T],
1086) -> Result<()> {
1087    if x.len() != indx.len() {
1088        return Err(Error::InvalidArgument(format!(
1089            "axpyi: x.len()={}, indx.len()={}",
1090            x.len(),
1091            indx.len()
1092        )));
1093    }
1094    T::axpyi(alpha, x, indx, y)
1095}
1096
1097/// `x[i] := y[indx[i]]` (sparse gather).
1098pub fn gthr<T: ComplexScalar>(y: &[T], indx: &[sys::aoclsparse_int], x: &mut [T]) -> Result<()> {
1099    if x.len() != indx.len() {
1100        return Err(Error::InvalidArgument(format!(
1101            "gthr: x.len()={}, indx.len()={}",
1102            x.len(),
1103            indx.len()
1104        )));
1105    }
1106    T::gthr(y, indx, x)
1107}
1108
1109/// `y[indx[i]] := x[i]` (sparse scatter).
1110pub fn sctr<T: ComplexScalar>(x: &[T], indx: &[sys::aoclsparse_int], y: &mut [T]) -> Result<()> {
1111    if x.len() != indx.len() {
1112        return Err(Error::InvalidArgument(format!(
1113            "sctr: x.len()={}, indx.len()={}",
1114            x.len(),
1115            indx.len()
1116        )));
1117    }
1118    T::sctr(x, indx, y)
1119}
1120
1121/// Compute `y := α · op(A) · x + β · y` for a complex CSR matrix `A`.
1122#[allow(clippy::too_many_arguments)]
1123pub fn mv<T: ComplexScalar>(
1124    op: Trans,
1125    alpha: T,
1126    a: &ComplexSparseMatrix<T>,
1127    descr: &MatDescr,
1128    x: &[T],
1129    beta: T,
1130    y: &mut [T],
1131) -> Result<()> {
1132    let (x_len, y_len) = match op {
1133        Trans::No => (a.n, a.m),
1134        Trans::T | Trans::C => (a.m, a.n),
1135    };
1136    if x.len() < x_len || y.len() < y_len {
1137        return Err(Error::InvalidArgument(format!(
1138            "mv: x.len()={}, y.len()={}, expected ({x_len}, {y_len})",
1139            x.len(),
1140            y.len()
1141        )));
1142    }
1143    T::mv(op, alpha, a.raw, descr, x, beta, y)
1144}
1145
1146/// Solve `op(A) · x = α · b` where `A` is sparse triangular.
1147#[allow(clippy::too_many_arguments)]
1148pub fn trsv<T: ComplexScalar>(
1149    op: Trans,
1150    alpha: T,
1151    a: &ComplexSparseMatrix<T>,
1152    descr: &MatDescr,
1153    b: &[T],
1154    x: &mut [T],
1155) -> Result<()> {
1156    if b.len() < a.m || x.len() < a.m {
1157        return Err(Error::InvalidArgument(format!(
1158            "trsv: b.len()={}, x.len()={}, m={}",
1159            b.len(),
1160            x.len(),
1161            a.m
1162        )));
1163    }
1164    T::trsv(op, alpha, a.raw, descr, b, x)
1165}
1166
1167/// Compute `C := α · op(A) · B + β · C` where `A` is sparse complex
1168/// (CSR) and `B`, `C` are dense complex matrices laid out per `order`.
1169#[allow(clippy::too_many_arguments)]
1170pub fn csrmm<T: ComplexScalar>(
1171    op: Trans,
1172    alpha: T,
1173    a: &ComplexSparseMatrix<T>,
1174    descr: &MatDescr,
1175    order: Order,
1176    b: &[T],
1177    n: usize,
1178    ldb: usize,
1179    beta: T,
1180    c: &mut [T],
1181    ldc: usize,
1182) -> Result<()> {
1183    T::csrmm(op, alpha, a.as_raw(), descr, order, b, n, ldb, beta, c, ldc)
1184}
1185
1186/// Compute `C := op(A) · B` where both `A` and `B` are sparse complex
1187/// matrices and `C` is dense.
1188pub fn spmmd<T: ComplexScalar>(
1189    op: Trans,
1190    a: &ComplexSparseMatrix<T>,
1191    b: &ComplexSparseMatrix<T>,
1192    layout: Order,
1193    c: &mut [T],
1194    ldc: usize,
1195) -> Result<()> {
1196    T::spmmd(op, a.as_raw(), b.as_raw(), layout, c, ldc)
1197}
1198
1199/// Compute `C := α · op_A(A) · op_B(B) + β · C` for complex sparse `A`
1200/// and `B`, dense complex `C`.
1201#[allow(clippy::too_many_arguments)]
1202pub fn sp2md<T: ComplexScalar>(
1203    op_a: Trans,
1204    descr_a: &MatDescr,
1205    a: &ComplexSparseMatrix<T>,
1206    op_b: Trans,
1207    descr_b: &MatDescr,
1208    b: &ComplexSparseMatrix<T>,
1209    alpha: T,
1210    beta: T,
1211    c: &mut [T],
1212    layout: Order,
1213    ldc: usize,
1214) -> Result<()> {
1215    T::sp2md(
1216        op_a,
1217        descr_a,
1218        a.as_raw(),
1219        op_b,
1220        descr_b,
1221        b.as_raw(),
1222        alpha,
1223        beta,
1224        c,
1225        layout,
1226        ldc,
1227    )
1228}
1229
1230/// Compute `C := α · op(A) + B` returning a fresh complex CSR matrix.
1231pub fn add<T: ComplexScalar>(
1232    op: Trans,
1233    a: &ComplexSparseMatrix<T>,
1234    alpha: T,
1235    b: &ComplexSparseMatrix<T>,
1236) -> Result<ComplexSparseMatrix<T>> {
1237    let mut c_raw: sys::aoclsparse_matrix = std::ptr::null_mut();
1238    let status = unsafe { T::add_ffi(trans_raw(op), a.as_raw(), alpha, b.as_raw(), &mut c_raw) };
1239    check_status("sparse", status)?;
1240    unsafe { ComplexSparseMatrix::from_library_owned(c_raw) }
1241}
1242
1243/// One step of (S)SOR / forward / backward Gauss-Seidel relaxation for a
1244/// complex sparse matrix.
1245pub fn sorv<T: ComplexScalar>(
1246    sor_type: SorType,
1247    descr: &MatDescr,
1248    a: &ComplexSparseMatrix<T>,
1249    omega: T,
1250    alpha: T,
1251    x: &mut [T],
1252    b: &[T],
1253) -> Result<()> {
1254    if x.len() < a.dims().1 || b.len() < a.dims().0 {
1255        return Err(Error::InvalidArgument(format!(
1256            "sorv: x.len()={}, b.len()={}, dims=({}, {})",
1257            x.len(),
1258            b.len(),
1259            a.dims().0,
1260            a.dims().1
1261        )));
1262    }
1263    T::sorv(sor_type, descr, a.as_raw(), omega, alpha, x, b)
1264}
1265
1266/// One ILU(0) smoothing step on a complex matrix.
1267pub fn ilu_smoother<T: ComplexScalar>(
1268    op: Trans,
1269    a: &ComplexSparseMatrix<T>,
1270    descr: &MatDescr,
1271    x: &mut [T],
1272    b: &[T],
1273) -> Result<()> {
1274    if x.len() < a.n || b.len() < a.m {
1275        return Err(Error::InvalidArgument(format!(
1276            "ilu_smoother: x.len()={}, b.len()={}, dims=({}, {})",
1277            x.len(),
1278            b.len(),
1279            a.m,
1280            a.n
1281        )));
1282    }
1283    T::ilu_smoother(op, a.raw, descr, x, b)
1284}
1285
1286// =========================================================================
1287//   Iterative solver (CG / GMRES) for complex
1288// =========================================================================
1289
1290/// Iterative-solver handle for complex sparse systems.
1291pub struct ComplexIterSolver<T: ComplexScalar> {
1292    handle: sys::aoclsparse_itsol_handle,
1293    _marker: PhantomData<T>,
1294}
1295
1296impl<T: ComplexScalar> ComplexIterSolver<T> {
1297    /// Initialise a new handle.
1298    pub fn new() -> Result<Self> {
1299        let mut handle: sys::aoclsparse_itsol_handle = std::ptr::null_mut();
1300        T::itsol_init(&mut handle)?;
1301        if handle.is_null() {
1302            return Err(Error::AllocationFailed("sparse"));
1303        }
1304        Ok(Self {
1305            handle,
1306            _marker: PhantomData,
1307        })
1308    }
1309
1310    /// Set a string-keyed solver option (see `aoclsparse_itsol_option_set`).
1311    pub fn set_option(&mut self, name: &str, value: &str) -> Result<()> {
1312        let c_name = CString::new(name)
1313            .map_err(|_| Error::InvalidArgument("set_option: name has interior NUL".into()))?;
1314        let c_value = CString::new(value)
1315            .map_err(|_| Error::InvalidArgument("set_option: value has interior NUL".into()))?;
1316        let status = unsafe {
1317            sys::aoclsparse_itsol_option_set(self.handle, c_name.as_ptr(), c_value.as_ptr())
1318        };
1319        check_status("sparse", status)
1320    }
1321
1322    /// Solve `A · x = b`. `rinfo[100]` of solver stats is real-typed.
1323    pub fn solve(
1324        &mut self,
1325        mat: &ComplexSparseMatrix<T>,
1326        descr: &MatDescr,
1327        b: &[T],
1328        x: &mut [T],
1329    ) -> Result<Box<[T::Real; 100]>> {
1330        let n = mat.n;
1331        if mat.m != mat.n {
1332            return Err(Error::InvalidArgument(format!(
1333                "iterative solve requires square matrix; got ({}, {})",
1334                mat.m, mat.n
1335            )));
1336        }
1337        let mut rinfo: Box<[T::Real; 100]> = Box::new([T::Real::default(); 100]);
1338        T::itsol_solve(self.handle, n, mat.raw, descr, b, x, &mut rinfo)?;
1339        Ok(rinfo)
1340    }
1341}
1342
1343impl<T: ComplexScalar> Drop for ComplexIterSolver<T> {
1344    fn drop(&mut self) {
1345        if !self.handle.is_null() {
1346            unsafe {
1347                sys::aoclsparse_itsol_destroy(&mut self.handle);
1348            }
1349            self.handle = std::ptr::null_mut();
1350        }
1351    }
1352}
1353
1354impl<T: ComplexScalar> std::fmt::Debug for ComplexIterSolver<T> {
1355    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1356        f.debug_struct("ComplexIterSolver").finish_non_exhaustive()
1357    }
1358}
1359
1360// =========================================================================
1361//   Complex extras: symgs, update_values, set_value, trsm, dotmv,
1362//   dotui/dotci, csr2csc, csr2dense, syrkd, syprd, gthrs/gthrz/sctrs,
1363//   TCSR/CSC creators
1364// =========================================================================
1365
1366// ----- Symmetric Gauss-Seidel (complex) -----
1367
1368/// Symmetric Gauss-Seidel sweep against a complex-symmetric handle. (`Complex64`)
1369pub fn symgs_c64(
1370    op: Trans,
1371    a: &ComplexSparseMatrix<Complex64>,
1372    descr: &MatDescr,
1373    alpha: Complex64,
1374    b: &[Complex64],
1375    x: &mut [Complex64],
1376) -> Result<()> {
1377    let alpha_raw = sys::aoclsparse_double_complex_ {
1378        real: alpha.re,
1379        imag: alpha.im,
1380    };
1381    let status = unsafe {
1382        sys::aoclsparse_zsymgs(
1383            trans_raw(op),
1384            a.as_raw(),
1385            descr.as_raw(),
1386            alpha_raw,
1387            b.as_ptr() as *const sys::aoclsparse_double_complex,
1388            x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1389        )
1390    };
1391    check_status("sparse", status)
1392}
1393
1394/// `Complex32` symmetric Gauss-Seidel. See [`symgs_c64`].
1395pub fn symgs_c32(
1396    op: Trans,
1397    a: &ComplexSparseMatrix<Complex32>,
1398    descr: &MatDescr,
1399    alpha: Complex32,
1400    b: &[Complex32],
1401    x: &mut [Complex32],
1402) -> Result<()> {
1403    let alpha_raw = sys::aoclsparse_float_complex {
1404        real: alpha.re,
1405        imag: alpha.im,
1406    };
1407    let status = unsafe {
1408        sys::aoclsparse_csymgs(
1409            trans_raw(op),
1410            a.as_raw(),
1411            descr.as_raw(),
1412            alpha_raw,
1413            b.as_ptr() as *const sys::aoclsparse_float_complex,
1414            x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1415        )
1416    };
1417    check_status("sparse", status)
1418}
1419
1420/// Fused symgs + mat-vec. (`Complex64`)
1421#[allow(clippy::too_many_arguments)]
1422pub fn symgs_mv_c64(
1423    op: Trans,
1424    a: &ComplexSparseMatrix<Complex64>,
1425    descr: &MatDescr,
1426    alpha: Complex64,
1427    b: &[Complex64],
1428    x: &mut [Complex64],
1429    y: &mut [Complex64],
1430) -> Result<()> {
1431    let alpha_raw = sys::aoclsparse_double_complex_ {
1432        real: alpha.re,
1433        imag: alpha.im,
1434    };
1435    let status = unsafe {
1436        sys::aoclsparse_zsymgs_mv(
1437            trans_raw(op),
1438            a.as_raw(),
1439            descr.as_raw(),
1440            alpha_raw,
1441            b.as_ptr() as *const sys::aoclsparse_double_complex,
1442            x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1443            y.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1444        )
1445    };
1446    check_status("sparse", status)
1447}
1448
1449/// `Complex32` fused symgs + mat-vec. See [`symgs_mv_c64`].
1450#[allow(clippy::too_many_arguments)]
1451pub fn symgs_mv_c32(
1452    op: Trans,
1453    a: &ComplexSparseMatrix<Complex32>,
1454    descr: &MatDescr,
1455    alpha: Complex32,
1456    b: &[Complex32],
1457    x: &mut [Complex32],
1458    y: &mut [Complex32],
1459) -> Result<()> {
1460    let alpha_raw = sys::aoclsparse_float_complex {
1461        real: alpha.re,
1462        imag: alpha.im,
1463    };
1464    let status = unsafe {
1465        sys::aoclsparse_csymgs_mv(
1466            trans_raw(op),
1467            a.as_raw(),
1468            descr.as_raw(),
1469            alpha_raw,
1470            b.as_ptr() as *const sys::aoclsparse_float_complex,
1471            x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1472            y.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1473        )
1474    };
1475    check_status("sparse", status)
1476}
1477
1478// ----- update_values / set_value (complex) -----
1479
1480/// Set a single non-zero entry on a complex CSR handle. (`Complex64`)
1481pub fn set_value_c64(
1482    a: &mut ComplexSparseMatrix<Complex64>,
1483    row_idx: i32,
1484    col_idx: i32,
1485    val: Complex64,
1486) -> Result<()> {
1487    let val_raw = sys::aoclsparse_double_complex_ {
1488        real: val.re,
1489        imag: val.im,
1490    };
1491    let status = unsafe {
1492        sys::aoclsparse_zset_value(
1493            a.as_raw(),
1494            row_idx as sys::aoclsparse_int,
1495            col_idx as sys::aoclsparse_int,
1496            val_raw,
1497        )
1498    };
1499    check_status("sparse", status)
1500}
1501/// `Complex32` set_value. See [`set_value_c64`].
1502pub fn set_value_c32(
1503    a: &mut ComplexSparseMatrix<Complex32>,
1504    row_idx: i32,
1505    col_idx: i32,
1506    val: Complex32,
1507) -> Result<()> {
1508    let val_raw = sys::aoclsparse_float_complex {
1509        real: val.re,
1510        imag: val.im,
1511    };
1512    let status = unsafe {
1513        sys::aoclsparse_cset_value(
1514            a.as_raw(),
1515            row_idx as sys::aoclsparse_int,
1516            col_idx as sys::aoclsparse_int,
1517            val_raw,
1518        )
1519    };
1520    check_status("sparse", status)
1521}
1522
1523/// Replace the values array on a complex CSR handle in place. (`Complex64`)
1524pub fn update_values_c64(
1525    a: &mut ComplexSparseMatrix<Complex64>,
1526    val: &mut [Complex64],
1527) -> Result<()> {
1528    let status = unsafe {
1529        sys::aoclsparse_zupdate_values(
1530            a.as_raw(),
1531            val.len() as sys::aoclsparse_int,
1532            val.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1533        )
1534    };
1535    check_status("sparse", status)
1536}
1537/// `Complex32` update_values. See [`update_values_c64`].
1538pub fn update_values_c32(
1539    a: &mut ComplexSparseMatrix<Complex32>,
1540    val: &mut [Complex32],
1541) -> Result<()> {
1542    let status = unsafe {
1543        sys::aoclsparse_cupdate_values(
1544            a.as_raw(),
1545            val.len() as sys::aoclsparse_int,
1546            val.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1547        )
1548    };
1549    check_status("sparse", status)
1550}
1551
1552// ----- trsm (complex) -----
1553
1554/// Multi-RHS triangular solve `op(A) · X = α · B` for complex sparse
1555/// triangular `A`. (`Complex64`)
1556#[allow(clippy::too_many_arguments)]
1557pub fn trsm_c64(
1558    op: Trans,
1559    alpha: Complex64,
1560    a: &ComplexSparseMatrix<Complex64>,
1561    descr: &MatDescr,
1562    b: &[Complex64],
1563    n_rhs: usize,
1564    ldb: usize,
1565    x: &mut [Complex64],
1566    ldx: usize,
1567) -> Result<()> {
1568    let alpha_raw = sys::aoclsparse_double_complex_ {
1569        real: alpha.re,
1570        imag: alpha.im,
1571    };
1572    let status = unsafe {
1573        sys::aoclsparse_ztrsm(
1574            trans_raw(op),
1575            alpha_raw,
1576            a.as_raw(),
1577            descr.as_raw(),
1578            sys::aoclsparse_order__aoclsparse_order_row,
1579            b.as_ptr() as *const sys::aoclsparse_double_complex,
1580            n_rhs as sys::aoclsparse_int,
1581            ldb as sys::aoclsparse_int,
1582            x.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1583            ldx as sys::aoclsparse_int,
1584        )
1585    };
1586    check_status("sparse", status)
1587}
1588
1589/// `Complex32` multi-RHS triangular solve. See [`trsm_c64`].
1590#[allow(clippy::too_many_arguments)]
1591pub fn trsm_c32(
1592    op: Trans,
1593    alpha: Complex32,
1594    a: &ComplexSparseMatrix<Complex32>,
1595    descr: &MatDescr,
1596    b: &[Complex32],
1597    n_rhs: usize,
1598    ldb: usize,
1599    x: &mut [Complex32],
1600    ldx: usize,
1601) -> Result<()> {
1602    let alpha_raw = sys::aoclsparse_float_complex {
1603        real: alpha.re,
1604        imag: alpha.im,
1605    };
1606    let status = unsafe {
1607        sys::aoclsparse_ctrsm(
1608            trans_raw(op),
1609            alpha_raw,
1610            a.as_raw(),
1611            descr.as_raw(),
1612            sys::aoclsparse_order__aoclsparse_order_row,
1613            b.as_ptr() as *const sys::aoclsparse_float_complex,
1614            n_rhs as sys::aoclsparse_int,
1615            ldb as sys::aoclsparse_int,
1616            x.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1617            ldx as sys::aoclsparse_int,
1618        )
1619    };
1620    check_status("sparse", status)
1621}
1622
1623// ----- dotmv (fused dot + mv) and sparse complex dot (dotui/dotci) -----
1624
1625/// Fused dot + mat-vec: `y := α A x + β y` and `d := xᵀ y`. (`Complex64`)
1626#[allow(clippy::too_many_arguments)]
1627pub fn dotmv_c64(
1628    op: Trans,
1629    alpha: Complex64,
1630    a: &ComplexSparseMatrix<Complex64>,
1631    descr: &MatDescr,
1632    x: &[Complex64],
1633    beta: Complex64,
1634    y: &mut [Complex64],
1635    d: &mut Complex64,
1636) -> Result<()> {
1637    let alpha_raw = sys::aoclsparse_double_complex_ {
1638        real: alpha.re,
1639        imag: alpha.im,
1640    };
1641    let beta_raw = sys::aoclsparse_double_complex_ {
1642        real: beta.re,
1643        imag: beta.im,
1644    };
1645    let status = unsafe {
1646        sys::aoclsparse_zdotmv(
1647            trans_raw(op),
1648            alpha_raw,
1649            a.as_raw(),
1650            descr.as_raw(),
1651            x.as_ptr() as *const sys::aoclsparse_double_complex,
1652            beta_raw,
1653            y.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1654            d as *mut _ as *mut sys::aoclsparse_double_complex,
1655        )
1656    };
1657    check_status("sparse", status)
1658}
1659
1660/// `Complex32` fused dot + mat-vec. See [`dotmv_c64`].
1661#[allow(clippy::too_many_arguments)]
1662pub fn dotmv_c32(
1663    op: Trans,
1664    alpha: Complex32,
1665    a: &ComplexSparseMatrix<Complex32>,
1666    descr: &MatDescr,
1667    x: &[Complex32],
1668    beta: Complex32,
1669    y: &mut [Complex32],
1670    d: &mut Complex32,
1671) -> Result<()> {
1672    let alpha_raw = sys::aoclsparse_float_complex {
1673        real: alpha.re,
1674        imag: alpha.im,
1675    };
1676    let beta_raw = sys::aoclsparse_float_complex {
1677        real: beta.re,
1678        imag: beta.im,
1679    };
1680    let status = unsafe {
1681        sys::aoclsparse_cdotmv(
1682            trans_raw(op),
1683            alpha_raw,
1684            a.as_raw(),
1685            descr.as_raw(),
1686            x.as_ptr() as *const sys::aoclsparse_float_complex,
1687            beta_raw,
1688            y.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1689            d as *mut _ as *mut sys::aoclsparse_float_complex,
1690        )
1691    };
1692    check_status("sparse", status)
1693}
1694
1695/// Sparse-vector unconjugated dot `Σ x[i] · y[indx[i]]`. (`Complex64`)
1696pub fn dotui_c64(
1697    x: &[Complex64],
1698    indx: &[sys::aoclsparse_int],
1699    y: &[Complex64],
1700) -> Result<Complex64> {
1701    let mut out = Complex64::new(0.0, 0.0);
1702    let status = unsafe {
1703        sys::aoclsparse_zdotui(
1704            x.len() as sys::aoclsparse_int,
1705            x.as_ptr() as *const std::os::raw::c_void,
1706            indx.as_ptr(),
1707            y.as_ptr() as *const std::os::raw::c_void,
1708            &mut out as *mut _ as *mut std::os::raw::c_void,
1709        )
1710    };
1711    check_status("sparse", status)?;
1712    Ok(out)
1713}
1714/// `Complex32` unconjugated sparse dot. See [`dotui_c64`].
1715pub fn dotui_c32(
1716    x: &[Complex32],
1717    indx: &[sys::aoclsparse_int],
1718    y: &[Complex32],
1719) -> Result<Complex32> {
1720    let mut out = Complex32::new(0.0, 0.0);
1721    let status = unsafe {
1722        sys::aoclsparse_cdotui(
1723            x.len() as sys::aoclsparse_int,
1724            x.as_ptr() as *const std::os::raw::c_void,
1725            indx.as_ptr(),
1726            y.as_ptr() as *const std::os::raw::c_void,
1727            &mut out as *mut _ as *mut std::os::raw::c_void,
1728        )
1729    };
1730    check_status("sparse", status)?;
1731    Ok(out)
1732}
1733
1734/// Sparse-vector conjugated dot `Σ conj(x[i]) · y[indx[i]]`. (`Complex64`)
1735pub fn dotci_c64(
1736    x: &[Complex64],
1737    indx: &[sys::aoclsparse_int],
1738    y: &[Complex64],
1739) -> Result<Complex64> {
1740    let mut out = Complex64::new(0.0, 0.0);
1741    let status = unsafe {
1742        sys::aoclsparse_zdotci(
1743            x.len() as sys::aoclsparse_int,
1744            x.as_ptr() as *const std::os::raw::c_void,
1745            indx.as_ptr(),
1746            y.as_ptr() as *const std::os::raw::c_void,
1747            &mut out as *mut _ as *mut std::os::raw::c_void,
1748        )
1749    };
1750    check_status("sparse", status)?;
1751    Ok(out)
1752}
1753/// `Complex32` conjugated sparse dot. See [`dotci_c64`].
1754pub fn dotci_c32(
1755    x: &[Complex32],
1756    indx: &[sys::aoclsparse_int],
1757    y: &[Complex32],
1758) -> Result<Complex32> {
1759    let mut out = Complex32::new(0.0, 0.0);
1760    let status = unsafe {
1761        sys::aoclsparse_cdotci(
1762            x.len() as sys::aoclsparse_int,
1763            x.as_ptr() as *const std::os::raw::c_void,
1764            indx.as_ptr(),
1765            y.as_ptr() as *const std::os::raw::c_void,
1766            &mut out as *mut _ as *mut std::os::raw::c_void,
1767        )
1768    };
1769    check_status("sparse", status)?;
1770    Ok(out)
1771}
1772
1773// ----- gthrs / gthrz / sctrs (complex) -----
1774
1775/// `Complex64` strided sparse gather. See real `gthrs_f64`.
1776pub fn gthrs_c64(y: &[Complex64], x: &mut [Complex64], stride: i32) -> Result<()> {
1777    let status = unsafe {
1778        sys::aoclsparse_zgthrs(
1779            x.len() as sys::aoclsparse_int,
1780            y.as_ptr() as *const std::os::raw::c_void,
1781            x.as_mut_ptr() as *mut std::os::raw::c_void,
1782            stride as sys::aoclsparse_int,
1783        )
1784    };
1785    check_status("sparse", status)
1786}
1787/// `Complex32` strided sparse gather.
1788pub fn gthrs_c32(y: &[Complex32], x: &mut [Complex32], stride: i32) -> Result<()> {
1789    let status = unsafe {
1790        sys::aoclsparse_cgthrs(
1791            x.len() as sys::aoclsparse_int,
1792            y.as_ptr() as *const std::os::raw::c_void,
1793            x.as_mut_ptr() as *mut std::os::raw::c_void,
1794            stride as sys::aoclsparse_int,
1795        )
1796    };
1797    check_status("sparse", status)
1798}
1799
1800/// `Complex64` gather-and-zero.
1801pub fn gthrz_c64(
1802    y: &mut [Complex64],
1803    indx: &[sys::aoclsparse_int],
1804    x: &mut [Complex64],
1805) -> Result<()> {
1806    let status = unsafe {
1807        sys::aoclsparse_zgthrz(
1808            x.len() as sys::aoclsparse_int,
1809            y.as_mut_ptr() as *mut std::os::raw::c_void,
1810            x.as_mut_ptr() as *mut std::os::raw::c_void,
1811            indx.as_ptr(),
1812        )
1813    };
1814    check_status("sparse", status)
1815}
1816/// `Complex32` gather-and-zero.
1817pub fn gthrz_c32(
1818    y: &mut [Complex32],
1819    indx: &[sys::aoclsparse_int],
1820    x: &mut [Complex32],
1821) -> Result<()> {
1822    let status = unsafe {
1823        sys::aoclsparse_cgthrz(
1824            x.len() as sys::aoclsparse_int,
1825            y.as_mut_ptr() as *mut std::os::raw::c_void,
1826            x.as_mut_ptr() as *mut std::os::raw::c_void,
1827            indx.as_ptr(),
1828        )
1829    };
1830    check_status("sparse", status)
1831}
1832
1833/// `Complex64` strided sparse scatter.
1834pub fn sctrs_c64(x: &[Complex64], y: &mut [Complex64], stride: i32) -> Result<()> {
1835    let status = unsafe {
1836        sys::aoclsparse_zsctrs(
1837            x.len() as sys::aoclsparse_int,
1838            x.as_ptr() as *const std::os::raw::c_void,
1839            stride as sys::aoclsparse_int,
1840            y.as_mut_ptr() as *mut std::os::raw::c_void,
1841        )
1842    };
1843    check_status("sparse", status)
1844}
1845/// `Complex32` strided sparse scatter.
1846pub fn sctrs_c32(x: &[Complex32], y: &mut [Complex32], stride: i32) -> Result<()> {
1847    let status = unsafe {
1848        sys::aoclsparse_csctrs(
1849            x.len() as sys::aoclsparse_int,
1850            x.as_ptr() as *const std::os::raw::c_void,
1851            stride as sys::aoclsparse_int,
1852            y.as_mut_ptr() as *mut std::os::raw::c_void,
1853        )
1854    };
1855    check_status("sparse", status)
1856}
1857
1858// ----- syrkd / syprd (complex) -----
1859
1860/// Symmetric rank-k update from a complex sparse matrix. (`Complex64`)
1861#[allow(clippy::too_many_arguments)]
1862pub fn syrkd_c64(
1863    op_a: Trans,
1864    a: &ComplexSparseMatrix<Complex64>,
1865    alpha: Complex64,
1866    beta: Complex64,
1867    c: &mut [Complex64],
1868    order_c: Order,
1869    ldc: usize,
1870) -> Result<()> {
1871    let alpha_raw = sys::aoclsparse_double_complex_ {
1872        real: alpha.re,
1873        imag: alpha.im,
1874    };
1875    let beta_raw = sys::aoclsparse_double_complex_ {
1876        real: beta.re,
1877        imag: beta.im,
1878    };
1879    let status = unsafe {
1880        sys::aoclsparse_zsyrkd(
1881            trans_raw(op_a),
1882            a.as_raw(),
1883            alpha_raw,
1884            beta_raw,
1885            c.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1886            order_c.raw(),
1887            ldc as sys::aoclsparse_int,
1888        )
1889    };
1890    check_status("sparse", status)
1891}
1892/// `Complex32` symmetric rank-k update. See [`syrkd_c64`].
1893#[allow(clippy::too_many_arguments)]
1894pub fn syrkd_c32(
1895    op_a: Trans,
1896    a: &ComplexSparseMatrix<Complex32>,
1897    alpha: Complex32,
1898    beta: Complex32,
1899    c: &mut [Complex32],
1900    order_c: Order,
1901    ldc: usize,
1902) -> Result<()> {
1903    let alpha_raw = sys::aoclsparse_float_complex {
1904        real: alpha.re,
1905        imag: alpha.im,
1906    };
1907    let beta_raw = sys::aoclsparse_float_complex {
1908        real: beta.re,
1909        imag: beta.im,
1910    };
1911    let status = unsafe {
1912        sys::aoclsparse_csyrkd(
1913            trans_raw(op_a),
1914            a.as_raw(),
1915            alpha_raw,
1916            beta_raw,
1917            c.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1918            order_c.raw(),
1919            ldc as sys::aoclsparse_int,
1920        )
1921    };
1922    check_status("sparse", status)
1923}
1924
1925/// Symmetric triple product `C := α · op(A) · B · op(A)ᵀ + β · C` for
1926/// complex sparse `A`, dense `B`/`C`. (`Complex64`)
1927#[allow(clippy::too_many_arguments)]
1928pub fn syprd_c64(
1929    op_a: Trans,
1930    a: &ComplexSparseMatrix<Complex64>,
1931    b: &[Complex64],
1932    order_b: Order,
1933    ldb: usize,
1934    alpha: Complex64,
1935    beta: Complex64,
1936    c: &mut [Complex64],
1937    order_c: Order,
1938    ldc: usize,
1939) -> Result<()> {
1940    let alpha_raw = sys::aoclsparse_double_complex_ {
1941        real: alpha.re,
1942        imag: alpha.im,
1943    };
1944    let beta_raw = sys::aoclsparse_double_complex_ {
1945        real: beta.re,
1946        imag: beta.im,
1947    };
1948    let status = unsafe {
1949        sys::aoclsparse_zsyprd(
1950            trans_raw(op_a),
1951            a.as_raw(),
1952            b.as_ptr() as *const sys::aoclsparse_double_complex,
1953            order_b.raw(),
1954            ldb as sys::aoclsparse_int,
1955            alpha_raw,
1956            beta_raw,
1957            c.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
1958            order_c.raw(),
1959            ldc as sys::aoclsparse_int,
1960        )
1961    };
1962    check_status("sparse", status)
1963}
1964/// `Complex32` symmetric triple product. See [`syprd_c64`].
1965#[allow(clippy::too_many_arguments)]
1966pub fn syprd_c32(
1967    op_a: Trans,
1968    a: &ComplexSparseMatrix<Complex32>,
1969    b: &[Complex32],
1970    order_b: Order,
1971    ldb: usize,
1972    alpha: Complex32,
1973    beta: Complex32,
1974    c: &mut [Complex32],
1975    order_c: Order,
1976    ldc: usize,
1977) -> Result<()> {
1978    let alpha_raw = sys::aoclsparse_float_complex {
1979        real: alpha.re,
1980        imag: alpha.im,
1981    };
1982    let beta_raw = sys::aoclsparse_float_complex {
1983        real: beta.re,
1984        imag: beta.im,
1985    };
1986    let status = unsafe {
1987        sys::aoclsparse_csyprd(
1988            trans_raw(op_a),
1989            a.as_raw(),
1990            b.as_ptr() as *const sys::aoclsparse_float_complex,
1991            order_b.raw(),
1992            ldb as sys::aoclsparse_int,
1993            alpha_raw,
1994            beta_raw,
1995            c.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
1996            order_c.raw(),
1997            ldc as sys::aoclsparse_int,
1998        )
1999    };
2000    check_status("sparse", status)
2001}
2002
2003// ----- Complex CSC and TCSR creators -----
2004
2005/// Build a complex CSC matrix handle. (`Complex64`)
2006#[allow(clippy::too_many_arguments)]
2007pub fn create_csc_c64(
2008    base: IndexBase,
2009    m: usize,
2010    n: usize,
2011    nnz: usize,
2012    col_ptr: &mut [sys::aoclsparse_int],
2013    row_idx: &mut [sys::aoclsparse_int],
2014    val: &mut [Complex64],
2015) -> Result<sys::aoclsparse_matrix> {
2016    let mut raw: sys::aoclsparse_matrix = std::ptr::null_mut();
2017    let status = unsafe {
2018        sys::aoclsparse_create_zcsc(
2019            &mut raw,
2020            base.raw_for_complex(),
2021            m as sys::aoclsparse_int,
2022            n as sys::aoclsparse_int,
2023            nnz as sys::aoclsparse_int,
2024            col_ptr.as_mut_ptr(),
2025            row_idx.as_mut_ptr(),
2026            val.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
2027        )
2028    };
2029    check_status("sparse", status)?;
2030    if raw.is_null() {
2031        return Err(Error::AllocationFailed("sparse"));
2032    }
2033    Ok(raw)
2034}
2035/// `Complex32` CSC creator. See [`create_csc_c64`].
2036#[allow(clippy::too_many_arguments)]
2037pub fn create_csc_c32(
2038    base: IndexBase,
2039    m: usize,
2040    n: usize,
2041    nnz: usize,
2042    col_ptr: &mut [sys::aoclsparse_int],
2043    row_idx: &mut [sys::aoclsparse_int],
2044    val: &mut [Complex32],
2045) -> Result<sys::aoclsparse_matrix> {
2046    let mut raw: sys::aoclsparse_matrix = std::ptr::null_mut();
2047    let status = unsafe {
2048        sys::aoclsparse_create_ccsc(
2049            &mut raw,
2050            base.raw_for_complex(),
2051            m as sys::aoclsparse_int,
2052            n as sys::aoclsparse_int,
2053            nnz as sys::aoclsparse_int,
2054            col_ptr.as_mut_ptr(),
2055            row_idx.as_mut_ptr(),
2056            val.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
2057        )
2058    };
2059    check_status("sparse", status)?;
2060    if raw.is_null() {
2061        return Err(Error::AllocationFailed("sparse"));
2062    }
2063    Ok(raw)
2064}
2065
2066/// Build a complex TCSR matrix handle (parallel L / U triangular CSRs).
2067/// (`Complex64`)
2068#[allow(clippy::too_many_arguments)]
2069pub fn create_tcsr_c64(
2070    base: IndexBase,
2071    m: usize,
2072    n: usize,
2073    nnz: usize,
2074    row_ptr_l: &mut [sys::aoclsparse_int],
2075    row_ptr_u: &mut [sys::aoclsparse_int],
2076    col_idx_l: &mut [sys::aoclsparse_int],
2077    col_idx_u: &mut [sys::aoclsparse_int],
2078    val_l: &mut [Complex64],
2079    val_u: &mut [Complex64],
2080) -> Result<sys::aoclsparse_matrix> {
2081    let mut raw: sys::aoclsparse_matrix = std::ptr::null_mut();
2082    let status = unsafe {
2083        sys::aoclsparse_create_ztcsr(
2084            &mut raw,
2085            base.raw_for_complex(),
2086            m as sys::aoclsparse_int,
2087            n as sys::aoclsparse_int,
2088            nnz as sys::aoclsparse_int,
2089            row_ptr_l.as_mut_ptr(),
2090            row_ptr_u.as_mut_ptr(),
2091            col_idx_l.as_mut_ptr(),
2092            col_idx_u.as_mut_ptr(),
2093            val_l.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
2094            val_u.as_mut_ptr() as *mut sys::aoclsparse_double_complex,
2095        )
2096    };
2097    check_status("sparse", status)?;
2098    if raw.is_null() {
2099        return Err(Error::AllocationFailed("sparse"));
2100    }
2101    Ok(raw)
2102}
2103/// `Complex32` TCSR creator. See [`create_tcsr_c64`].
2104#[allow(clippy::too_many_arguments)]
2105pub fn create_tcsr_c32(
2106    base: IndexBase,
2107    m: usize,
2108    n: usize,
2109    nnz: usize,
2110    row_ptr_l: &mut [sys::aoclsparse_int],
2111    row_ptr_u: &mut [sys::aoclsparse_int],
2112    col_idx_l: &mut [sys::aoclsparse_int],
2113    col_idx_u: &mut [sys::aoclsparse_int],
2114    val_l: &mut [Complex32],
2115    val_u: &mut [Complex32],
2116) -> Result<sys::aoclsparse_matrix> {
2117    let mut raw: sys::aoclsparse_matrix = std::ptr::null_mut();
2118    let status = unsafe {
2119        sys::aoclsparse_create_ctcsr(
2120            &mut raw,
2121            base.raw_for_complex(),
2122            m as sys::aoclsparse_int,
2123            n as sys::aoclsparse_int,
2124            nnz as sys::aoclsparse_int,
2125            row_ptr_l.as_mut_ptr(),
2126            row_ptr_u.as_mut_ptr(),
2127            col_idx_l.as_mut_ptr(),
2128            col_idx_u.as_mut_ptr(),
2129            val_l.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
2130            val_u.as_mut_ptr() as *mut sys::aoclsparse_float_complex,
2131        )
2132    };
2133    check_status("sparse", status)?;
2134    if raw.is_null() {
2135        return Err(Error::AllocationFailed("sparse"));
2136    }
2137    Ok(raw)
2138}
2139
2140// IndexBase helper used above. Mirrors the real API's internal `raw()`
2141// accessor without making the function public to outside crates.
2142impl IndexBase {
2143    pub(crate) fn raw_for_complex(self) -> sys::aoclsparse_index_base {
2144        match self {
2145            IndexBase::Zero => sys::aoclsparse_index_base__aoclsparse_index_base_zero,
2146            IndexBase::One => sys::aoclsparse_index_base__aoclsparse_index_base_one,
2147        }
2148    }
2149}
2150
2151#[cfg(test)]
2152mod tests {
2153    use super::*;
2154
2155    #[test]
2156    fn axpyi_c64_round_trip() {
2157        // y = [1+0i, 2+0i, 3+0i, 4+0i]; x = [10+1i, 20+2i] at indx [0, 2]; α = (3+0i).
2158        // → y[0] += 3·(10+1i) = 30+3i; y[0] = 31+3i; y[2] += 3·(20+2i) = 60+6i; y[2] = 63+6i.
2159        let mut y = [
2160            Complex64::new(1.0, 0.0),
2161            Complex64::new(2.0, 0.0),
2162            Complex64::new(3.0, 0.0),
2163            Complex64::new(4.0, 0.0),
2164        ];
2165        let x = [Complex64::new(10.0, 1.0), Complex64::new(20.0, 2.0)];
2166        let indx: [sys::aoclsparse_int; 2] = [0, 2];
2167        axpyi(Complex64::new(3.0, 0.0), &x, &indx, &mut y).unwrap();
2168        assert!((y[0].re - 31.0).abs() < 1e-12);
2169        assert!((y[0].im - 3.0).abs() < 1e-12);
2170        assert!((y[2].re - 63.0).abs() < 1e-12);
2171        assert!((y[2].im - 6.0).abs() < 1e-12);
2172        assert!((y[1].re - 2.0).abs() < 1e-12);
2173        assert!((y[3].re - 4.0).abs() < 1e-12);
2174    }
2175
2176    #[test]
2177    fn complex_sparse_matrix_round_trip_c64() {
2178        // 2×2 identity with imaginary-part = 0
2179        let val = [Complex64::new(1.0, 0.0), Complex64::new(1.0, 0.0)];
2180        let col: [sys::aoclsparse_int; 2] = [0, 1];
2181        let rp: [sys::aoclsparse_int; 3] = [0, 1, 2];
2182        let mat =
2183            ComplexSparseMatrix::<Complex64>::from_csr(IndexBase::Zero, 2, 2, &rp, &col, &val)
2184                .unwrap();
2185        assert_eq!(mat.dims(), (2, 2));
2186        assert_eq!(mat.nnz(), 2);
2187        let (base, rp2, col2, val2) = mat.export_csr().unwrap();
2188        assert_eq!(base, IndexBase::Zero);
2189        assert_eq!(rp2, [0, 1, 2]);
2190        assert_eq!(col2, [0, 1]);
2191        assert!((val2[0].re - 1.0).abs() < 1e-12);
2192    }
2193
2194    #[test]
2195    fn complex_mv_identity_c64() {
2196        // 2×2 identity matrix; A·x should equal x.
2197        let val = [Complex64::new(1.0, 0.0), Complex64::new(1.0, 0.0)];
2198        let col: [sys::aoclsparse_int; 2] = [0, 1];
2199        let rp: [sys::aoclsparse_int; 3] = [0, 1, 2];
2200        let mat =
2201            ComplexSparseMatrix::<Complex64>::from_csr(IndexBase::Zero, 2, 2, &rp, &col, &val)
2202                .unwrap();
2203        let descr = MatDescr::new().unwrap();
2204        let x = [Complex64::new(3.0, 1.0), Complex64::new(4.0, -2.0)];
2205        let mut y = [Complex64::new(0.0, 0.0); 2];
2206        mv(
2207            Trans::No,
2208            Complex64::new(1.0, 0.0),
2209            &mat,
2210            &descr,
2211            &x,
2212            Complex64::new(0.0, 0.0),
2213            &mut y,
2214        )
2215        .unwrap();
2216        assert!((y[0].re - 3.0).abs() < 1e-12);
2217        assert!((y[0].im - 1.0).abs() < 1e-12);
2218        assert!((y[1].re - 4.0).abs() < 1e-12);
2219        assert!((y[1].im + 2.0).abs() < 1e-12);
2220    }
2221
2222    #[test]
2223    fn complex_mv_identity_c32() {
2224        let val = [Complex32::new(1.0, 0.0), Complex32::new(1.0, 0.0)];
2225        let col: [sys::aoclsparse_int; 2] = [0, 1];
2226        let rp: [sys::aoclsparse_int; 3] = [0, 1, 2];
2227        let mat =
2228            ComplexSparseMatrix::<Complex32>::from_csr(IndexBase::Zero, 2, 2, &rp, &col, &val)
2229                .unwrap();
2230        let descr = MatDescr::new().unwrap();
2231        let x = [Complex32::new(3.0, 1.0), Complex32::new(4.0, -2.0)];
2232        let mut y = [Complex32::new(0.0, 0.0); 2];
2233        mv(
2234            Trans::No,
2235            Complex32::new(1.0, 0.0),
2236            &mat,
2237            &descr,
2238            &x,
2239            Complex32::new(0.0, 0.0),
2240            &mut y,
2241        )
2242        .unwrap();
2243        assert!((y[0].re - 3.0).abs() < 1e-6);
2244        assert!((y[0].im - 1.0).abs() < 1e-6);
2245        assert!((y[1].re - 4.0).abs() < 1e-6);
2246        assert!((y[1].im + 2.0).abs() < 1e-6);
2247    }
2248
2249    #[test]
2250    fn complex_csrmm_identity_c64() {
2251        // 2x2 identity sparse A, 2x2 dense B; C = A * B should equal B.
2252        let val = [Complex64::new(1.0, 0.0), Complex64::new(1.0, 0.0)];
2253        let col: [sys::aoclsparse_int; 2] = [0, 1];
2254        let rp: [sys::aoclsparse_int; 3] = [0, 1, 2];
2255        let a = ComplexSparseMatrix::<Complex64>::from_csr(IndexBase::Zero, 2, 2, &rp, &col, &val)
2256            .unwrap();
2257        let descr = MatDescr::new().unwrap();
2258        // 2x2 dense B (row-major, ldb = 2):
2259        // [(1, 1), (2, -1)]
2260        // [(3, 0), (4,  2)]
2261        let b = [
2262            Complex64::new(1.0, 1.0),
2263            Complex64::new(2.0, -1.0),
2264            Complex64::new(3.0, 0.0),
2265            Complex64::new(4.0, 2.0),
2266        ];
2267        let mut c = [Complex64::new(0.0, 0.0); 4];
2268        crate::complex::csrmm(
2269            Trans::No,
2270            Complex64::new(1.0, 0.0),
2271            &a,
2272            &descr,
2273            Order::RowMajor,
2274            &b,
2275            2,
2276            2,
2277            Complex64::new(0.0, 0.0),
2278            &mut c,
2279            2,
2280        )
2281        .unwrap();
2282        for (got, want) in c.iter().zip(b.iter()) {
2283            assert!((got.re - want.re).abs() < 1e-12);
2284            assert!((got.im - want.im).abs() < 1e-12);
2285        }
2286    }
2287
2288    #[test]
2289    fn complex_add_identity_plus_identity_c64() {
2290        let val = [Complex64::new(1.0, 0.0), Complex64::new(1.0, 0.0)];
2291        let col: [sys::aoclsparse_int; 2] = [0, 1];
2292        let rp: [sys::aoclsparse_int; 3] = [0, 1, 2];
2293        let a = ComplexSparseMatrix::<Complex64>::from_csr(IndexBase::Zero, 2, 2, &rp, &col, &val)
2294            .unwrap();
2295        let b = ComplexSparseMatrix::<Complex64>::from_csr(IndexBase::Zero, 2, 2, &rp, &col, &val)
2296            .unwrap();
2297        let c = crate::complex::add(Trans::No, &a, Complex64::new(1.0, 0.0), &b).unwrap();
2298        let (_, _, _, val_c) = c.export_csr().unwrap();
2299        assert_eq!(val_c.len(), 2);
2300        for v in &val_c {
2301            assert!((v.re - 2.0).abs() < 1e-12);
2302            assert!(v.im.abs() < 1e-12);
2303        }
2304    }
2305}