1 /*
2  * Copyright (C) 2005-2011 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19 
20 /**
21  * @file
22  * @brief   Sparse matrix storage with linked lists for rows and cols.
23  * @author  Daniel Grund
24  *
25  * Sparse matrix storage with linked lists for rows and cols.
26  * Matrix is optimized for left-to-right and top-to-bottom access.
27  * Complexity is O(1) then.
28  * Random access or right-to-left and bottom-to-top is O(m*n).
29  */
30 #include "config.h"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <assert.h>
35 #include <math.h>
36 
37 #include "sp_matrix.h"
38 
39 #include "util.h"
40 #include "bitset.h"
41 #include "xmalloc.h"
42 
43 typedef enum iter_direction_t {
44 	down, right, all
45 } iter_direction_t;
46 
47 /**
48  * Embedded list pointer.
49  */
50 typedef struct sp_matrix_list_head_t {
51 	struct sp_matrix_list_head_t *next;
52 } sp_matrix_list_head_t;
53 
54 /**
55  * A matrix entry.
56  */
57 typedef struct entry_t {
58 	sp_matrix_list_head_t col_chain; /**< points to next element in same column */
59 	sp_matrix_list_head_t row_chain; /**< points to next element in same row */
60 	matrix_elem_t         e;         /**< The actual element */
61 } entry_t;
62 
63 struct sp_matrix_t {
64 	/* These specify the dimensions of the matrix.
65 	 * They equal the largest values ever used in matrix_set */
66 	int maxrow, maxcol;
67 	/* These are the dimensions of allocated arrays below.
68 	 * rowc >= maxrow and colc >= maxcol hold. */
69 	int rowc, colc;
70 	/* number of entries */
71 	int entries;
72 	/* arrays of sp_matrix_list_head* as entry-points to rows and cols */
73 	sp_matrix_list_head_t **rows, **cols;
74 	/* for iteration: first is to remember start-point;
75 	 *                last was returned just before
76 	 *                next is used in case last was removed from list */
77 	iter_direction_t dir;
78 	sp_matrix_list_head_t *first, *last, *next;
79 	int iter_row; /* used for iteration over all elements */
80 	/* for each column the last inserted element in col list */
81 	sp_matrix_list_head_t **last_col_el;
82 	/* for each row the last inserted element in row list */
83 	sp_matrix_list_head_t **last_row_el;
84 };
85 
86 #define SP_MATRIX_INIT_LIST_HEAD(ptr) do { (ptr)->next = NULL; } while (0)
87 
88 #define _offsetof(type,member) ((char *) &(((type *) 0)->member) - (char *) 0)
89 #define _container_of(ptr,type,member) ((type *) ((char *) (ptr) - _offsetof(type, member)))
90 
91 #define is_empty_row(row) (row > m->maxrow || (m->rows[row])->next == NULL)
92 #define is_empty_col(col) (col > m->maxcol || (m->cols[col])->next == NULL)
93 
94 #define list_entry_by_col(h) (&_container_of(h, entry_t, col_chain)->e)
95 #define list_entry_by_row(h) (&_container_of(h, entry_t, row_chain)->e)
96 
97 /**
98  * Returns the size of a single matrix element.
99  */
matrix_get_elem_size(void)100 unsigned matrix_get_elem_size(void)
101 {
102 	return sizeof(entry_t);
103 }
104 
105 /**
106  * Returns the new size for an array of size old_size,
107  *  which must at least store an entry at position min.
108  */
m_new_size(int old_size,int min)109 static inline int m_new_size(int old_size, int min)
110 {
111 	unsigned bits = 0;
112 	assert(min >= old_size);
113 	while (min > 0) {
114 		min >>= 1;
115 		bits++;
116 	}
117 	assert(bits < sizeof(min) * 8 - 1);
118 	return 1 << bits;
119 }
120 
121 /**
122  * Allocates space for @p count entries in the rows array and
123  * initializes all entries from @p start to the end.
124  */
m_alloc_row(sp_matrix_t * m,int start,int count)125 static inline void m_alloc_row(sp_matrix_t *m, int start, int count)
126 {
127 	int p;
128 
129 	m->rowc        = count;
130 	m->rows        = XREALLOC(m->rows, sp_matrix_list_head_t *, m->rowc);
131 	m->last_row_el = XREALLOC(m->last_row_el, sp_matrix_list_head_t *, m->rowc);
132 
133 	for (p = start; p < m->rowc; ++p) {
134 		m->rows[p] = XMALLOC(sp_matrix_list_head_t);
135 		SP_MATRIX_INIT_LIST_HEAD(m->rows[p]);
136 		m->last_row_el[p] = m->rows[p];
137 	}
138 }
139 
140 /**
141  * Allocates space for @p count entries in the cols array and
142  * initializes all entries from @p start to the end.
143  */
m_alloc_col(sp_matrix_t * m,int start,int count)144 static inline void m_alloc_col(sp_matrix_t *m, int start, int count)
145 {
146 	int p;
147 
148 	m->colc        = count;
149 	m->cols        = XREALLOC(m->cols, sp_matrix_list_head_t*, m->colc);
150 	m->last_col_el = XREALLOC(m->last_col_el, sp_matrix_list_head_t*, m->colc);
151 
152 	for (p = start; p < m->colc; ++p) {
153 		m->cols[p] = XMALLOC(sp_matrix_list_head_t);
154 		SP_MATRIX_INIT_LIST_HEAD(m->cols[p]);
155 		m->last_col_el[p] = m->cols[p];
156 	}
157 }
158 
159 /**
160  * Searches in row @p row for the matrix element m[row, col], starting at element @p start.
161  * @return If the element exists:
162  *            Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
163  *         Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
164  *         @p prev_prev always points to the previous element of @p prev
165  */
m_search_in_row_from(const sp_matrix_t * m,int row,int col,sp_matrix_list_head_t * start,sp_matrix_list_head_t ** prev,sp_matrix_list_head_t ** prev_prev)166 static inline matrix_elem_t *m_search_in_row_from(const sp_matrix_t *m,
167 			int row, int col, sp_matrix_list_head_t *start, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
168 {
169 	sp_matrix_list_head_t *row_start;
170 	matrix_elem_t         *res = NULL;
171 
172 	row_start = m->rows[row];
173 	*prev     = start;
174 
175 	while ((*prev)->next != NULL && list_entry_by_row((*prev)->next)->col <= col) {
176 		(*prev_prev) = (*prev);
177 		*prev        = (*prev)->next;
178 	}
179 
180 	if (*prev != row_start) {
181 		matrix_elem_t *me = list_entry_by_row(*prev);
182 
183 		if (me->row == row && me->col == col)
184 			res = me;
185 	}
186 
187 	if (res) {
188 		m->last_row_el[row] = *prev;
189 	}
190 
191 	return res;
192 }
193 
194 /**
195  * Searches in row @p row for the matrix element m[row, col].
196  * @return If the element exists:
197  *            Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
198  *         Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
199  *         @p prev_prev always points to the previous element of @p prev
200  */
m_search_in_row(const sp_matrix_t * m,int row,int col,sp_matrix_list_head_t ** prev,sp_matrix_list_head_t ** prev_prev)201 static inline matrix_elem_t *m_search_in_row(const sp_matrix_t *m,
202 			int row, int col, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
203 {
204 	sp_matrix_list_head_t *start = m->rows[row];
205 
206 	*prev_prev = NULL;
207 
208 	if (m->last_row_el[row] != start) {
209 		matrix_elem_t *el = list_entry_by_row(m->last_row_el[row]);
210 		if (el->col < col) {
211 			*prev_prev = start = m->last_row_el[row];
212 		}
213 	}
214 
215 	return m_search_in_row_from(m, row, col, start, prev, prev_prev);
216 }
217 
218 /**
219  * Searches in col @p col for the matrix element m[row, col], starting at @p start.
220  * @return If the element exists:
221  *            Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
222  *         Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
223  *         @p prev_prev always points to the previous element of @p prev
224  */
m_search_in_col_from(const sp_matrix_t * m,int row,int col,sp_matrix_list_head_t * start,sp_matrix_list_head_t ** prev,sp_matrix_list_head_t ** prev_prev)225 static inline matrix_elem_t *m_search_in_col_from(const sp_matrix_t *m,
226 			int row, int col, sp_matrix_list_head_t *start, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
227 {
228 	sp_matrix_list_head_t *col_start;
229 	matrix_elem_t         *res = NULL;
230 
231 	col_start = m->cols[col];
232 	*prev     = start;
233 
234 	while ((*prev)->next != NULL && list_entry_by_col((*prev)->next)->row <= row) {
235 		*prev_prev = (*prev);
236 		*prev      = (*prev)->next;
237 	}
238 
239 	if (*prev != col_start) {
240 		matrix_elem_t *me = list_entry_by_col(*prev);
241 
242 		if (me->row == row && me->col == col)
243 			res = me;
244 	}
245 
246 	if (res) {
247 		m->last_col_el[col] = *prev;
248 	}
249 
250 	return res;
251 }
252 
253 /**
254  * Searches in col @p col for the matrix element m[row, col].
255  * @return If the element exists:
256  *            Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
257  *         Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
258  *         @p prev_prev always points to the previous element of @p prev
259  */
m_search_in_col(const sp_matrix_t * m,int row,int col,sp_matrix_list_head_t ** prev,sp_matrix_list_head_t ** prev_prev)260 static inline matrix_elem_t *m_search_in_col(const sp_matrix_t *m,
261 			int row, int col, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
262 {
263 	sp_matrix_list_head_t *start = m->cols[col];
264 
265 	*prev_prev = NULL;
266 
267 	if (m->last_col_el[col] != start) {
268 		matrix_elem_t *el = list_entry_by_col(m->last_col_el[col]);
269 		if (el->row < row) {
270 			*prev_prev = start = m->last_col_el[col];
271 		}
272 	}
273 
274 	return m_search_in_col_from(m, row, col, start, prev, prev_prev);
275 }
276 
new_matrix(int row_init,int col_init)277 sp_matrix_t *new_matrix(int row_init, int col_init)
278 {
279 	sp_matrix_t *res = XMALLOCZ(sp_matrix_t);
280 	res->maxrow = -1;
281 	res->maxcol = -1;
282 	m_alloc_row(res, 0, MAX(0, row_init));
283 	m_alloc_col(res, 0, MAX(0, col_init));
284 	return res;
285 }
286 
del_matrix(sp_matrix_t * m)287 void del_matrix(sp_matrix_t *m)
288 {
289 	int i;
290 
291 	for (i = 0; i < m->rowc; ++i) {
292 		if (! is_empty_row(i)) {
293 			entry_t *e;
294 			sp_matrix_list_head_t *n;
295 
296 			n = m->rows[i]->next;
297 			do {
298 				/* get current matrix element */
299 				e = _container_of(n, entry_t, row_chain);
300 				n = n->next;
301 				xfree(e);
302 			} while (n != NULL);
303 
304 		}
305 		xfree(m->rows[i]);
306 	}
307 	for (i = 0; i < m->colc; ++i)
308 		xfree(m->cols[i]);
309 	xfree(m->last_col_el);
310 	xfree(m->last_row_el);
311 	xfree(m->rows);
312 	xfree(m->cols);
313 	xfree(m);
314 }
315 
matrix_set(sp_matrix_t * m,int row,int col,double val)316 void matrix_set(sp_matrix_t *m, int row, int col, double val)
317 {
318 	matrix_elem_t *me = NULL;
319 	entry_t       *entr;
320 	sp_matrix_list_head_t *leftof, *above;
321 	sp_matrix_list_head_t *prev_leftof, *prev_above;
322 
323 	/* if necessary enlarge the matrix */
324 	if (row > m->maxrow) {
325 		m->maxrow = row;
326 		if (row >= m->rowc)
327 			m_alloc_row(m, m->rowc, m_new_size(m->rowc, row));
328 	}
329 	if (col > m->maxcol) {
330 		m->maxcol = col;
331 		if (col >= m->colc)
332 			m_alloc_col(m, m->colc, m_new_size(m->colc, col));
333 	}
334 
335 	/* search for existing entry */
336 	if (m->maxrow < m->maxcol)
337 		me = m_search_in_col(m, row, col, &above, &prev_above);
338 	else
339 		me = m_search_in_row(m, row, col, &leftof, &prev_leftof);
340 
341 	/* if it exists, set the value and return */
342 	if (me) {
343 		if (val != 0) {
344 			me->val = (float)val;
345 		} else {
346 			entr = _container_of(me, entry_t, e);
347 
348 			/* remove row_chain entry */
349 			if (prev_leftof)
350 				prev_leftof->next = entr->row_chain.next;
351 			else
352 				m->rows[row]->next = entr->row_chain.next;
353 
354 			/* remove col_chain entry */
355 			if (prev_above)
356 				prev_above->next = entr->col_chain.next;
357 			else
358 				m->cols[col]->next = entr->col_chain.next;
359 
360 			entr->row_chain.next = NULL;
361 			entr->col_chain.next = NULL;
362 
363 			/* set the last pointer to the "previous" element */
364 			if (m->last_col_el[col] == &entr->col_chain ||
365 				m->last_row_el[row] == &entr->row_chain)
366 			{
367 				m->last_col_el[col] = prev_above  ? prev_above  : m->cols[col];
368 				m->last_row_el[row] = prev_leftof ? prev_leftof : m->rows[row];
369 			}
370 
371 			free(entr);
372 			m->entries--;
373 		}
374 		return;
375 	}
376 
377 	/* if it does not exist and 0 should be set just quit */
378 	if (val == 0)
379 		return;
380 
381 	/* if it does not exist and val != 0 search the other direction */
382 	if (m->maxrow >= m->maxcol)
383 		m_search_in_col(m, row, col, &above, &prev_above);
384 	else
385 		m_search_in_row(m, row, col, &leftof, &prev_leftof);
386 	/* now leftof and above are the entry_t's prior the new one in each direction */
387 
388 	/* insert new entry */
389 	entr        = XMALLOC(entry_t);
390 	entr->e.row = row;
391 	entr->e.col = col;
392 	entr->e.val = (float)val;
393 
394 	/* add row_chain entry */
395 	entr->row_chain.next = leftof->next;
396 	leftof->next = &entr->row_chain;
397 
398 	/* add col_chain entry */
399 	entr->col_chain.next = above->next;
400 	above->next = &entr->col_chain;
401 
402 	m->last_col_el[col] = &entr->col_chain;
403 	m->last_row_el[row] = &entr->row_chain;
404 
405 	m->entries++;
406 }
407 
matrix_set_row_bulk(sp_matrix_t * m,int row,int * cols,int num_cols,double val)408 void matrix_set_row_bulk(sp_matrix_t *m, int row, int *cols, int num_cols, double val)
409 {
410 	matrix_elem_t *me = NULL;
411 	entry_t       *entr;
412 	int           i;
413 	sp_matrix_list_head_t *leftof, *above;
414 	sp_matrix_list_head_t *prev_leftof, *prev_above;
415 
416 	/* if necessary enlarge the matrix */
417 	if (row > m->maxrow) {
418 		m->maxrow = row;
419 		if (row >= m->rowc)
420 			m_alloc_row(m, m->rowc, m_new_size(m->rowc, row));
421 	}
422 	if (cols[num_cols - 1] > m->maxcol) {
423 		m->maxcol = cols[num_cols - 1];
424 		if (cols[num_cols - 1] >= m->colc)
425 			m_alloc_col(m, m->colc, m_new_size(m->colc, cols[num_cols - 1]));
426 	}
427 
428     /* set start values */
429 	prev_above  = NULL;
430 	prev_leftof = NULL;
431 
432 	for (i = 0; i < num_cols; ++i) {
433 		/* search for existing entry */
434 		me = m_search_in_row(m, row, cols[i], &leftof, &prev_leftof);
435 
436 		/* if it exists, set the value and return */
437 		if (me) {
438 			if (val != 0) {
439 				me->val = (float)val;
440 			} else {
441 				entr = _container_of(me, entry_t, e);
442 
443 				/* remove row_chain entry */
444 				if (prev_leftof)
445 					prev_leftof->next = entr->row_chain.next;
446 				else
447 					m->rows[row]->next = entr->row_chain.next;
448 
449 				/* remove col_chain entry */
450 				if (prev_above)
451 					prev_above->next = entr->col_chain.next;
452 				else
453 					m->cols[cols[i]]->next = entr->col_chain.next;
454 
455 				entr->row_chain.next = NULL;
456 				entr->col_chain.next = NULL;
457 
458 				/* set the last pointer to the "previous" element */
459 				if (m->last_col_el[cols[i]] == &entr->col_chain ||
460 					m->last_row_el[row]     == &entr->row_chain)
461 				{
462 					m->last_col_el[cols[i]] = prev_above  ? prev_above  : m->cols[cols[i]];
463 					m->last_row_el[row]     = prev_leftof ? prev_leftof : m->rows[row];
464 				}
465 
466 				free(entr);
467 				m->entries--;
468 			}
469 
470 			continue;
471 		}
472 
473 		/* if it does not exist and 0 should be set just quit */
474 		if (val == 0)
475 			continue;
476 
477 		/* we have to search the col list as well, to get the above pointer */
478 		m_search_in_col(m, row, cols[i], &above, &prev_above);
479 
480 		/* now leftof and above are the entry_t's prior the new one in each direction */
481 
482 		/* insert new entry */
483 		entr        = XMALLOC(entry_t);
484 		entr->e.row = row;
485 		entr->e.col = cols[i];
486 		entr->e.val = (float)val;
487 
488 		m->last_col_el[cols[i]] = &entr->col_chain;
489 		m->last_row_el[row]     = &entr->row_chain;
490 
491 		/* add row_chain entry */
492 		entr->row_chain.next = leftof->next;
493 		leftof->next = &entr->row_chain;
494 
495 		/* add col_chain entry */
496 		entr->col_chain.next = above->next;
497 		above->next = &entr->col_chain;
498 
499 		m->entries++;
500 	}
501 }
502 
matrix_get(const sp_matrix_t * m,int row,int col)503 double matrix_get(const sp_matrix_t *m, int row, int col)
504 {
505 	sp_matrix_list_head_t *dummy, *dummy2;
506 	matrix_elem_t *me;
507 
508 	if (is_empty_row(row) || is_empty_col(col))
509 		return 0.0;
510 
511 	if (m->maxrow < m->maxcol)
512 		me = m_search_in_col(m, row, col, &dummy, &dummy2);
513 	else
514 		me = m_search_in_row(m, row, col, &dummy, &dummy2);
515 
516 	if (me)
517 		assert(me->col == col && me->row == row);
518 
519 	return me ? me->val : 0.0;
520 }
521 
matrix_get_entries(const sp_matrix_t * m)522 int matrix_get_entries(const sp_matrix_t *m)
523 {
524 	return m->entries;
525 }
526 
matrix_get_rowcount(const sp_matrix_t * m)527 int matrix_get_rowcount(const sp_matrix_t *m)
528 {
529 	return m->maxrow + 1;
530 }
531 
matrix_get_colcount(const sp_matrix_t * m)532 int matrix_get_colcount(const sp_matrix_t *m)
533 {
534 	return m->maxcol + 1;
535 }
536 
matrix_row_first(sp_matrix_t * m,int row)537 const matrix_elem_t *matrix_row_first(sp_matrix_t *m, int row)
538 {
539 	if (is_empty_row(row))
540 		return NULL;
541 
542 	m->dir   = right;
543 	m->first = m->rows[row];
544 	m->last  = m->first->next;
545 	m->next  = m->last ? m->last->next : NULL;
546 
547 	assert (list_entry_by_row(m->last)->row == row);
548 
549 	return list_entry_by_row(m->last);
550 }
551 
matrix_col_first(sp_matrix_t * m,int col)552 const matrix_elem_t *matrix_col_first(sp_matrix_t *m, int col)
553 {
554 	if (is_empty_col(col))
555 		return NULL;
556 
557 	m->dir   = down;
558 	m->first = m->cols[col];
559 	m->last  = m->first->next;
560 	m->next  = m->last ? m->last->next : NULL;
561 
562 	assert (list_entry_by_col(m->last)->col == col);
563 
564 	return list_entry_by_col(m->last);
565 }
566 
matrix_first_from(sp_matrix_t * m,int startrow)567 static inline const matrix_elem_t *matrix_first_from(sp_matrix_t *m, int startrow)
568 {
569 	const matrix_elem_t *res;
570 	int i;
571 
572 	for (i = startrow; i <= m->maxrow; ++i) {
573 		res = matrix_row_first(m, i);
574 		if (res) {
575 			m->iter_row = i;
576 			m->dir      = all;
577 			return res;
578 		}
579 	}
580 
581 	return NULL;
582 }
583 
matrix_first(sp_matrix_t * m)584 const matrix_elem_t *matrix_first(sp_matrix_t *m)
585 {
586 	return matrix_first_from(m, 0);
587 }
588 
matrix_next(sp_matrix_t * m)589 const matrix_elem_t *matrix_next(sp_matrix_t *m)
590 {
591 	assert(m->first && "Start iteration with matrix_???_first, before calling me!");
592 
593 	if (m->next == NULL) {
594 		if (m->dir == all)
595 			return matrix_first_from(m, ++m->iter_row);
596 		else
597 			return NULL;
598 	}
599 
600 	m->last = m->next;
601 	m->next = m->next->next;
602 
603 	if (m->dir == down)
604 		return list_entry_by_col(m->last);
605 	else /* right or all */
606 		return list_entry_by_row(m->last);
607 }
608 
cmp_count(const void * e1,const void * e2)609 static int cmp_count(const void *e1, const void *e2)
610 {
611 	return (int *)e2 - (int *)e1;
612 }
613 
matrix_fill_row(sp_matrix_t * m,int row,bitset_t * fullrow)614 static inline void matrix_fill_row(sp_matrix_t *m, int row, bitset_t *fullrow)
615 {
616 	bitset_set(fullrow, row);
617 	matrix_foreach_in_col(m, row, e) {
618 		if (! bitset_is_set(fullrow, e->row)) {
619 			assert(0.0 == matrix_get(m, e->col, e->row));
620 			matrix_set(m, e->col, e->row, e->val);
621 			matrix_set(m, e->row, e->col, 0.0);
622 		}
623 	}
624 }
625 
matrix_optimize(sp_matrix_t * m)626 void matrix_optimize(sp_matrix_t *m)
627 {
628 	int i, size, redo;
629 	int *c;
630 	bitset_t *fullrow;
631 
632 	size = MAX(m->maxcol, m->maxrow)+1;
633 
634 	/* kill all double-entries (Mij and Mji are set) */
635 	matrix_foreach(m, e) {
636 		double t_val;
637 
638 		assert(e->row != e->col && "Root has itself as arg. Ok. But the arg (=root) will always have the same color as root");
639 		t_val = matrix_get(m, e->col, e->row);
640 		if (fabs(t_val) > 1e-10) {
641 			matrix_set(m, e->col, e->row, 0);
642 			matrix_set(m, e->row, e->col, e->val + t_val);
643 		}
644 	}
645 
646 	c       = ALLOCAN(int, size);
647 	redo    = 1;
648 	fullrow = bitset_alloca(size);
649 
650 	/* kill 'all' rows containing only 1 entry */
651 	while (redo) {
652 		redo = 0;
653 		/* count elements in rows */
654 		memset(c, 0, size * sizeof(*c));
655 
656 		matrix_foreach(m, e)
657 			c[e->row]++;
658 
659 		for (i = 0; i<size; ++i)
660 			if (c[i] == 1 && ! bitset_is_set(fullrow, i)) {
661 				redo = 1;
662 				/* if the other row isn't empty move the e in there, else fill e's row */
663 				matrix_elem_t const *const e = matrix_row_first(m, i);
664 				if (e) {
665 					if (c[e->col] > 0)
666 						matrix_fill_row(m, e->col, fullrow);
667 					else
668 						matrix_fill_row(m, e->row, fullrow);
669 				}
670 			}
671 	}
672 
673 
674 	memset(c, 0, size * sizeof(*c));
675 	matrix_foreach(m, e)
676 		c[e->row]++;
677 
678 	qsort(c, size, sizeof(*c), cmp_count);
679 
680 	for (i = 0; i < size; ++i) {
681 		if (! bitset_is_set(fullrow, i))
682 			matrix_fill_row(m, i, fullrow);
683 	}
684 }
685 
matrix_dump(sp_matrix_t * m,FILE * out,int factor)686 void matrix_dump(sp_matrix_t *m, FILE *out, int factor)
687 {
688 	int i, o, last_idx;
689 
690 	for (i = 0; i <= m->maxrow; ++i) {
691 		last_idx = -1;
692 		matrix_foreach_in_row(m, i, e) {
693 			for (o = last_idx + 1; o < e->col; ++o)
694 				fprintf(out, " %4.1f" , 0.0);
695 
696 			fprintf(out, " %4.1f", factor * e->val);
697 			last_idx = e->col;
698 		}
699 
700 		for (o = last_idx + 1; o <= m->maxcol; ++o)
701 			fprintf(out, " %4.1f" , 0.0);
702 
703 		fprintf(out, "\n");
704 	}
705 }
706 
matrix_self_test(int d)707 void matrix_self_test(int d)
708 {
709 	int i, o;
710 	sp_matrix_t *m = new_matrix(10, 10);
711 
712 	for (i = 0; i < d; ++i)
713 		for (o = 0; o < d; ++o)
714 			matrix_set(m, i, o, i*o);
715 
716 	for (i = 0; i < d; ++i)
717 		for (o = 0; o<d; ++o)
718 			assert(matrix_get(m, i, o) == i*o);
719 
720 	i = 1;
721 	matrix_foreach_in_row(m,1,e) {
722 		assert(e->val == i);
723 		i++;
724 	}
725 	assert(!matrix_next(m)); /*iter must finish */
726 
727 	i = d-1;
728 	matrix_foreach_in_col(m,d-1,e) {
729 		assert(e->val == i);
730 		i += d-1;
731 	}
732 	assert(!matrix_next(m));
733 	del_matrix(m);
734 	m = new_matrix(16,16);
735 	matrix_set(m, 1,1,9);
736 	matrix_set(m, 1,2,8);
737 	matrix_set(m, 1,3,7);
738 
739 	matrix_set(m, 1,3,6);
740 	matrix_set(m, 1,2,5);
741 	matrix_set(m, 1,1,4);
742 	i = 1;
743 	matrix_foreach_in_row(m, 1, e) {
744 		assert(e->row == 1 && e->col == i && e->val == i+3);
745 		i++;
746 	}
747 	assert(i == 4);
748 	del_matrix(m);
749 
750 	m = new_matrix(5,5);
751 	matrix_set(m, 1,1,1);
752 	matrix_set(m, 2,2,2);
753 	matrix_set(m, 3,3,3);
754 	matrix_set(m, 3,5,4);
755 	matrix_set(m, 4,4,5);
756 	matrix_set(m, 5,5,6);
757 	i = 0;
758 	matrix_foreach(m, e)
759 		assert(e->val == ++i);
760 	assert(i == 6);
761 	matrix_set(m, 1,1,0);
762 	assert(5 == matrix_get_entries(m));
763 	del_matrix(m);
764 }
765