/* * lu_file.c * * Copyright (C) 2016-2018 ERGO-Code * * Data file implementation * * A data file stores lines of (index,value) pairs. Entries of each line are * contiguous in memory. Lines can be in any order in memory and there can be * gaps between consecutive lines. * * The data file implementation uses arrays * * index[0:fmem-1], value[0:fmem-1], * begin[0:nlines], end[0:nlines], * next[0:nlines], prev[0:nlines] * * index, value storing (index,value) pairs * begin[k] pointer to first element in line 0 <= k < nlines, * end[k] pointer to one past the last element in line k. * begin[nlines] pointer to the first element of unused space * end[nlines] holds fmem * * next, prev hold the line numbers (0..nlines-1) in a double linked list in * the order in which they appear in memory. That is, for line 0 <= k < nlines, * next[k] and prev[k] are the line which comes before respectively after line * k in memory. next[nlines] and prev[nlines] are the first respectively last * line in memory order. * * Methods: * * lu_file_empty * lu_file_reappend * lu_file_compress * lu_file_diff * */ #include "lu_def.h" #include "lu_list.h" #include "lu_file.h" /* ========================================================================== * lu_file_empty * * Initialize empty file with @fmem memory space. * ========================================================================== */ void lu_file_empty( lu_int nlines, lu_int *begin, lu_int *end, lu_int *next, lu_int *prev, lu_int fmem) { lu_int i; begin[nlines] = 0; end[nlines] = fmem; for (i = 0; i < nlines; i++) begin[i] = end[i] = 0; for (i = 0; i < nlines; i++) { next[i] = i+1; prev[i+1] = i; } next[nlines] = 0; prev[0] = nlines; } /* ========================================================================== * lu_file_reappend * * Reappend line to file end and add @extra_space elements room. The file must * have at least length(line) + @extra_space elements free space. * ========================================================================== */ void lu_file_reappend( lu_int line, lu_int nlines, lu_int *begin, lu_int *end, lu_int *next, lu_int *prev, lu_int *index, double *value, lu_int extra_space) { lu_int fmem, used, room, ibeg, iend, pos; fmem = end[nlines]; used = begin[nlines]; room = fmem-used; ibeg = begin[line]; /* old beginning of line */ iend = end[line]; begin[line] = used; /* new beginning of line */ assert(iend-ibeg <= room); for (pos = ibeg; pos < iend; pos++) { index[used] = index[pos]; value[used++] = value[pos]; } end[line] = used; room = fmem-used; assert(room >= extra_space); used += extra_space; begin[nlines] = used; /* beginning of unused space */ lu_list_move(line, 0, next, prev, nlines, NULL); } /* ========================================================================== * lu_file_compress * * Compress file to reuse memory gaps. The ordering of lines in the file is * unchanged. To each line with nz entries add @stretch*nz+@pad elements extra * space. Chop extra space if it would overlap the following line in memory. * * Return: number of entries in file * ========================================================================== */ lu_int lu_file_compress( lu_int nlines, lu_int *begin, lu_int *end, const lu_int *next, lu_int *index, double *value, double stretch, lu_int pad) { lu_int i, pos, ibeg, iend, used, extra_space, nz = 0; used = 0; extra_space = 0; for (i = next[nlines]; i < nlines; i = next[i]) /* move line i */ { ibeg = begin[i]; iend = end[i]; assert(ibeg >= used); used += extra_space; if (used > ibeg) used = ibeg; /* chop extra space added before */ begin[i] = used; for (pos = ibeg; pos < iend; pos++) { index[used] = index[pos]; value[used++] = value[pos]; } end[i] = used; extra_space = stretch*(iend-ibeg) + pad; nz += iend-ibeg; } assert(used <= begin[nlines]); used += extra_space; if (used > begin[nlines]) used = begin[nlines]; /* never use more space than before */ begin[nlines] = used; return nz; } /* ========================================================================== * lu_file_diff (for debugging) * * @begin_row, @end_row, @begin_col, @end_col are pointer into @index, @value, * defining lines of the "row file" and the "column file". * * Task: * * val == NULL: count row file entries that are missing in column file. * val != NULL: count row file entries that are missing in column file * or which values are different. * * The method does a column file search for each row file entry. To check * consistency of rowwise and columnwise storage, the method must be called * twice with row pointers and column pointers swapped. * ========================================================================== */ lu_int lu_file_diff( lu_int nrow, const lu_int *begin_row, const lu_int *end_row, const lu_int *begin_col, const lu_int *end_col, const lu_int *index, const double *value) { lu_int i, j, pos, where, ndiff = 0; for (i = 0; i < nrow; i++) { for (pos = begin_row[i]; pos < end_row[i]; pos++) { j = index[pos]; for (where = begin_col[j]; where < end_col[j] && index[where] != i; where++) ; if (where == end_col[j] || (value && value[pos] != value[where])) ndiff++; } } return ndiff; }