1 /** \file
2  * \brief Declaration and implementation of class Array2D which implements
3  * dynamic two dimensional arrays.
4  *
5  * \author Carsten Gutwenger
6  *
7  * \par License:
8  * This file is part of the Open Graph Drawing Framework (OGDF).
9  *
10  * \par
11  * Copyright (C)<br>
12  * See README.md in the OGDF root directory for details.
13  *
14  * \par
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * Version 2 or 3 as published by the Free Software Foundation;
18  * see the file LICENSE.txt included in the packaging of this file
19  * for details.
20  *
21  * \par
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * \par
28  * You should have received a copy of the GNU General Public
29  * License along with this program; if not, see
30  * http://www.gnu.org/copyleft/gpl.html
31  */
32 
33 #pragma once
34 
35 #include <ogdf/basic/exceptions.h>
36 
37 namespace ogdf {
38 
39 
40 //! The parameterized class Array2D implements dynamic two-dimensional arrays.
41 /**
42  * @ingroup containers
43  *
44  * @tparam E denotes the element type.
45  */
46 template<class E> class Array2D
47 {
48 public:
49 	// constructors
50 
51 	//! Creates a two-dimensional array with empty index set.
Array2D()52 	Array2D() { construct(0,-1,0,-1); }
53 
54 	//! Creates a two-dimensional array with index set [\p a, ..., \p b]*[\p c, ..., \p d].
Array2D(int a,int b,int c,int d)55 	Array2D(int a, int b, int c, int d) {
56 		construct(a,b,c,d); initialize();
57 	}
58 
59 	//! Creates a two-dimensional array with index set [\p a, ..., \p b]*[\p c, ..., \p d] and initailizes all elements with \p x.
Array2D(int a,int b,int c,int d,const E & x)60 	Array2D(int a, int b, int c, int d, const E &x) {
61 		construct(a,b,c,d); initialize(x);
62 	}
63 
64 	//! Creates a two-dimensional array that is a copy of \p A.
Array2D(const Array2D<E> & A)65 	Array2D(const Array2D<E> &A) {
66 		copy(A);
67 	}
68 
69 	//! Creates a two-dimensional array containing the elements of \p A (move semantics).
70 	/**
71 	 * The array \p A is empty afterwards.
72 	 */
Array2D(Array2D<E> && A)73 	Array2D(Array2D<E> &&A)
74 		: m_vpStart(A.m_vpStart), m_lenDim2(A.m_lenDim2), m_pStart(A.m_pStart), m_pStop(A.m_pStop),
75 		  m_a(A.m_a), m_b(A.m_b), m_c(A.m_c), m_d(A.m_d)
76 	{
77 		A.construct(0,-1,0,-1);
78 	}
79 
80 	//! Destructor
~Array2D()81 	~Array2D() {
82 		deconstruct();
83 	}
84 
85 	//! Returns the minimal array index in dimension 1.
low1()86 	int low1() const { return m_a; }
87 
88 	//! Returns the maximal array index in dimension 1.
high1()89 	int high1() const { return m_b; }
90 
91 	//! Returns the minimal array index in dimension 2.
low2()92 	int low2() const { return m_c; }
93 
94 	//! Returns the maximal array index in dimension 2.
high2()95 	int high2() const { return m_d; }
96 
97 	//! Returns the size (number of elements) of the array.
size()98 	int size() const { return size1() * size2(); }
99 
100 	//! Returns the length of the index interval (number of entries) in dimension 1.
size1()101 	int size1() const { return m_b - m_a + 1; }
102 
103 	//! Returns the length of the index interval (number of entries) in dimension 2.
size2()104 	int size2() const { return m_lenDim2; }
105 
106 	//! Returns the determinant of the matrix
107 	/*! \note use only for square matrices and floating point values */
108 	float det() const;
109 
110 	//! Returns a reference to the element with index (\p i,\p j).
operator()111 	const E &operator()(int i, int j) const {
112 		OGDF_ASSERT(m_a <= i);
113 		OGDF_ASSERT(i <= m_b);
114 		OGDF_ASSERT(m_c <= j);
115 		OGDF_ASSERT(j <= m_d);
116 		return m_vpStart[size_t(i-m_a)*m_lenDim2+j];
117 	}
118 
119 	//! Returns a reference to the element with index (\p i,\p j).
operator()120 	E &operator()(int i, int j) {
121 		OGDF_ASSERT(m_a <= i);
122 		OGDF_ASSERT(i <= m_b);
123 		OGDF_ASSERT(m_c <= j);
124 		OGDF_ASSERT(j <= m_d);
125 		return m_vpStart[size_t(i-m_a)*m_lenDim2+j];
126 	}
127 
128 	//! Reinitializes the array to an array with empty index set.
init()129 	void init() { init(0,-1,0,-1); }
130 
131 	//! Reinitializes the array to an array with index set [\p a, ..., \p b]*[\p c, ..., \p d].
init(int a,int b,int c,int d)132 	void init(int a, int b, int c, int d) {
133 		deconstruct();
134 		construct(a,b,c,d);
135 		initialize();
136 	}
137 
138 	//! Reinitializes the array to an array with index set [\p a, ..., \p b]*[\p c, ..., \p d] and initializes all entries with \p x.
init(int a,int b,int c,int d,const E & x)139 	void init(int a, int b, int c, int d, const E &x) {
140 		deconstruct();
141 		construct(a,b,c,d);
142 		initialize(x);
143 	}
144 
145 	//! Assignment operator.
146 	Array2D<E> &operator=(const Array2D<E> &array2) {
147 		deconstruct();
148 		copy(array2);
149 		return *this;
150 	}
151 
152 	//! Assignment operator (move semantics).
153 	/**
154 	 * Array \p A is empty afterwards.
155 	 */
156 	Array2D<E> &operator=(Array2D<E> &&A) {
157 		deconstruct();
158 
159 		m_vpStart = A.m_vpStart;
160 		m_pStart  = A.m_pStart;
161 		m_pStop   = A.m_pStop;
162 		m_lenDim2 = A.m_lenDim2;
163 		m_a       = A.m_a;
164 		m_b       = A.m_b;
165 		m_c       = A.m_c;
166 		m_d       = A.m_d;
167 
168 		A.construct(0,-1,0,-1);
169 		return *this;
170 	}
171 
172 	//! Sets all elements to \p x.
fill(const E & x)173 	void fill(const E &x) {
174 		E *pDest = m_pStop;
175 		while(pDest > m_pStart)
176 			*--pDest = x;
177 	}
178 
179 private:
180 	E   *m_vpStart; //!< The virtual start of the array (address of A[0,0]).
181 	int  m_lenDim2; //!< The  number of elements in dimension 2.
182 	E   *m_pStart; //!< The real start of the array (address of A[low1,low2]).
183 	E   *m_pStop; //!< Successor of last element (address of A[high1,high2+1]).
184 
185 	int  m_a; //!< The lowest index in dimension 1.
186 	int  m_b; //!< The highest index in dimension 1.
187 	int  m_c; //!< The lowest index in dimension 2.
188 	int  m_d; //!< The highest index in dimension 2.
189 
190 	void construct(int a, int b, int c, int d);
191 
192 	void initialize();
193 	void initialize(const E &x);
194 
195 	void deconstruct();
196 
197 	void copy(const Array2D<E> &array2);
198 };
199 
200 
201 //! Constructs the array with index set [\p a, ..., \p b]*[\p c, ..., \p d].
202 template<class E>
construct(int a,int b,int c,int d)203 void Array2D<E>::construct(int a, int b, int c, int d)
204 {
205 	m_a = a;
206 	m_b = b;
207 	m_c = c;
208 	m_d = d;
209 
210 	size_t lenDim1 = b-a+1;
211 	m_lenDim2   = d-c+1;
212 
213 	if (lenDim1 < 1 || m_lenDim2 < 1) {
214 		m_pStart = m_vpStart = m_pStop = nullptr;
215 
216 	} else {
217 		size_t len = lenDim1*m_lenDim2;
218 		m_pStart = static_cast<E *>( malloc(len*sizeof(E)) );
219 		if (m_pStart == nullptr)
220 			OGDF_THROW(InsufficientMemoryException);
221 
222 		m_vpStart = m_pStart - c;
223 		m_pStop   = m_pStart + len;
224 	}
225 }
226 
227 
228 //! Initializes the array with default constructor of \a E.
229 template<class E>
initialize()230 void Array2D<E>::initialize()
231 {
232 	E *pDest = m_pStart;
233 	try {
234 		for (; pDest < m_pStop; pDest++)
235 			new(pDest) E;
236 	} catch (...) {
237 		while(--pDest >= m_pStart)
238 			pDest->~E();
239 		free(m_pStart);
240 		throw;
241 	}
242 }
243 
244 
245 //! Initializes the array with \p x.
246 template<class E>
initialize(const E & x)247 void Array2D<E>::initialize(const E &x)
248 {
249 	E *pDest = m_pStart;
250 	try {
251 		for (; pDest < m_pStop; pDest++)
252 			new(pDest) E(x);
253 	} catch (...) {
254 		while(--pDest >= m_pStart)
255 			pDest->~E();
256 		free(m_pStart);
257 		throw;
258 	}
259 }
260 
261 
262 //! Call destructor of all elements.
263 template<class E>
deconstruct()264 void Array2D<E>::deconstruct()
265 {
266 	if (!std::is_trivially_destructible<E>::value) {
267 		for (E *pDest = m_pStart; pDest < m_pStop; pDest++)
268 			pDest->~E();
269 	}
270 	free(m_pStart);
271 }
272 
273 //! Copy \p array2.
274 template<class E>
copy(const Array2D<E> & array2)275 void Array2D<E>::copy(const Array2D<E> &array2)
276 {
277 	construct(array2.m_a, array2.m_b, array2.m_c, array2.m_d);
278 
279 	if (m_pStart != 0) {
280 		E *pSrc  = array2.m_pStop;
281 		E *pDest = m_pStop;
282 		while(pDest > m_pStart)
283 			new (--pDest) E(*--pSrc);
284 	}
285 }
286 
287 
288 //! Computes the determinant via row expansion.
289 template<class E>
det()290 float Array2D<E>::det() const
291 {
292 	// matrix must be quadratic
293 	OGDF_ASSERT(size1() == size2());
294 
295 	int a = m_a;
296 	int b = m_b;
297 	int c = m_c;
298 	int d = m_d;
299 	int n = m_lenDim2;
300 
301 	int i, j;
302 	int column;
303 
304 	float determinant = 0.0;
305 
306 	switch(n) {
307 	case 0:
308 		break;
309 	case 1:
310 		determinant = (float)((*this)(a, c));
311 		break;
312 	case 2:
313 		determinant = (float)((*this)(a, c) * (*this)(b, d) - (*this)(a, d) * (*this)(b, c));
314 		break;
315 
316 		// Expanding along the first row (Laplace's Formula)
317 	default:
318 		Array2D<E> remMatrix(0, n-2, 0, n-2);             // the remaining matrix
319 		for(column = c; column <= d; column++) {
320 			int rem_i = 0;
321 			int rem_j = 0;
322 			for(i = a; i <= b; i++) {
323 				for(j = c; j <= d; j++) {
324 					if(i != a && j != column) {
325 						remMatrix(rem_i, rem_j) = (*this)(i, j);
326 						if(rem_j < n-2) {
327 							rem_j++;
328 						}
329 						else {
330 							rem_i++;
331 							rem_j = 0;
332 						}
333 					}
334 				}
335 			}
336 			determinant += pow(-1.0,(a+column)) * (*this)(a,column) * remMatrix.det();
337 		}
338 	}
339 
340 	return determinant;
341 }
342 
343 }
344