1// Copyright ©2019 The Gonum Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package mat
6
7import "gonum.org/v1/gonum/blas/cblas128"
8
9// Dense is a dense matrix representation with complex data.
10type CDense struct {
11	mat cblas128.General
12
13	capRows, capCols int
14}
15
16// Dims returns the number of rows and columns in the matrix.
17func (m *CDense) Dims() (r, c int) {
18	return m.mat.Rows, m.mat.Cols
19}
20
21// H performs an implicit conjugate transpose by returning the receiver inside a
22// Conjugate.
23func (m *CDense) H() CMatrix {
24	return Conjugate{m}
25}
26
27// NewCDense creates a new complex Dense matrix with r rows and c columns.
28// If data == nil, a new slice is allocated for the backing slice.
29// If len(data) == r*c, data is used as the backing slice, and changes to the
30// elements of the returned CDense will be reflected in data.
31// If neither of these is true, NewCDense will panic.
32// NewCDense will panic if either r or c is zero.
33//
34// The data must be arranged in row-major order, i.e. the (i*c + j)-th
35// element in the data slice is the {i, j}-th element in the matrix.
36func NewCDense(r, c int, data []complex128) *CDense {
37	if r <= 0 || c <= 0 {
38		if r == 0 || c == 0 {
39			panic(ErrZeroLength)
40		}
41		panic("mat: negative dimension")
42	}
43	if data != nil && r*c != len(data) {
44		panic(ErrShape)
45	}
46	if data == nil {
47		data = make([]complex128, r*c)
48	}
49	return &CDense{
50		mat: cblas128.General{
51			Rows:   r,
52			Cols:   c,
53			Stride: c,
54			Data:   data,
55		},
56		capRows: r,
57		capCols: c,
58	}
59}
60
61// reuseAs resizes an empty matrix to a r×c matrix,
62// or checks that a non-empty matrix is r×c.
63//
64// reuseAs must be kept in sync with reuseAsZeroed.
65func (m *CDense) reuseAs(r, c int) {
66	if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
67		// Panic as a string, not a mat.Error.
68		panic("mat: caps not correctly set")
69	}
70	if r == 0 || c == 0 {
71		panic(ErrZeroLength)
72	}
73	if m.IsZero() {
74		m.mat = cblas128.General{
75			Rows:   r,
76			Cols:   c,
77			Stride: c,
78			Data:   useC(m.mat.Data, r*c),
79		}
80		m.capRows = r
81		m.capCols = c
82		return
83	}
84	if r != m.mat.Rows || c != m.mat.Cols {
85		panic(ErrShape)
86	}
87}
88
89func (m *CDense) reuseAsZeroed(r, c int) {
90	// This must be kept in-sync with reuseAs.
91	if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
92		// Panic as a string, not a mat.Error.
93		panic("mat: caps not correctly set")
94	}
95	if r == 0 || c == 0 {
96		panic(ErrZeroLength)
97	}
98	if m.IsZero() {
99		m.mat = cblas128.General{
100			Rows:   r,
101			Cols:   c,
102			Stride: c,
103			Data:   useZeroedC(m.mat.Data, r*c),
104		}
105		m.capRows = r
106		m.capCols = c
107		return
108	}
109	if r != m.mat.Rows || c != m.mat.Cols {
110		panic(ErrShape)
111	}
112	m.Zero()
113}
114
115// Reset zeros the dimensions of the matrix so that it can be reused as the
116// receiver of a dimensionally restricted operation.
117//
118// See the Reseter interface for more information.
119func (m *CDense) Reset() {
120	// Row, Cols and Stride must be zeroed in unison.
121	m.mat.Rows, m.mat.Cols, m.mat.Stride = 0, 0, 0
122	m.capRows, m.capCols = 0, 0
123	m.mat.Data = m.mat.Data[:0]
124}
125
126// IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
127// receiver for size-restricted operations. CDense matrices can be zeroed using Reset.
128func (m *CDense) IsZero() bool {
129	// It must be the case that m.Dims() returns
130	// zeros in this case. See comment in Reset().
131	return m.mat.Stride == 0
132}
133
134// Zero sets all of the matrix elements to zero.
135func (m *CDense) Zero() {
136	r := m.mat.Rows
137	c := m.mat.Cols
138	for i := 0; i < r; i++ {
139		zeroC(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c])
140	}
141}
142
143// Copy makes a copy of elements of a into the receiver. It is similar to the
144// built-in copy; it copies as much as the overlap between the two matrices and
145// returns the number of rows and columns it copied. If a aliases the receiver
146// and is a transposed Dense or VecDense, with a non-unitary increment, Copy will
147// panic.
148//
149// See the Copier interface for more information.
150func (m *CDense) Copy(a CMatrix) (r, c int) {
151	r, c = a.Dims()
152	if a == m {
153		return r, c
154	}
155	r = min(r, m.mat.Rows)
156	c = min(c, m.mat.Cols)
157	if r == 0 || c == 0 {
158		return 0, 0
159	}
160	// TODO(btracey): Check for overlap when complex version exists.
161	// TODO(btracey): Add fast-paths.
162	for i := 0; i < r; i++ {
163		for j := 0; j < c; j++ {
164			m.set(i, j, a.At(i, j))
165		}
166	}
167	return r, c
168}
169