You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

572 lines
19 KiB

/* sva.c (sparse vector area) */
/***********************************************************************
* This code is part of GLPK (GNU Linear Programming Kit).
*
* Copyright (C) 2012-2013 Andrew Makhorin, Department for Applied
* Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
* reserved. E-mail: <mao@gnu.org>.
*
* GLPK is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GLPK is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/
#include "env.h"
#include "sva.h"
/***********************************************************************
* sva_create_area - create sparse vector area (SVA)
*
* This routine creates the sparse vector area (SVA), which initially
* is empty.
*
* The parameter n_max specifies the initial number of vectors that can
* be allocated in the SVA, n_max > 0.
*
* The parameter size specifies the initial number of free locations in
* the SVA, size > 0.
*
* On exit the routine returns a pointer to the SVA created. */
SVA *sva_create_area(int n_max, int size)
{ SVA *sva;
xassert(0 < n_max && n_max < INT_MAX);
xassert(0 < size && size < INT_MAX);
sva = talloc(1, SVA);
sva->n_max = n_max;
sva->n = 0;
sva->ptr = talloc(1+n_max, int);
sva->len = talloc(1+n_max, int);
sva->cap = talloc(1+n_max, int);
sva->size = size;
sva->m_ptr = 1;
sva->r_ptr = size+1;
sva->head = sva->tail = 0;
sva->prev = talloc(1+n_max, int);
sva->next = talloc(1+n_max, int);
sva->ind = talloc(1+size, int);
sva->val = talloc(1+size, double);
sva->talky = 0;
return sva;
}
/***********************************************************************
* sva_alloc_vecs - allocate new vectors in SVA
*
* This routine allocates nnn new empty vectors, nnn > 0, in the sparse
* vector area (SVA).
*
* The new vectors are assigned reference numbers k, k+1, ..., k+nnn-1,
* where k is a reference number assigned to the very first new vector,
* which is returned by the routine on exit. */
int sva_alloc_vecs(SVA *sva, int nnn)
{ int n = sva->n;
int n_max = sva->n_max;
int *ptr = sva->ptr;
int *len = sva->len;
int *cap = sva->cap;
int *prev = sva->prev;
int *next = sva->next;
int k, new_n;
#if 1
if (sva->talky)
xprintf("sva_alloc_vecs: nnn = %d\n", nnn);
#endif
xassert(nnn > 0);
/* determine new number of vectors in SVA */
new_n = n + nnn;
xassert(new_n > n);
if (n_max < new_n)
{ /* enlarge the SVA arrays */
while (n_max < new_n)
{ n_max += n_max;
xassert(n_max > 0);
}
sva->n_max = n_max;
sva->ptr = ptr = trealloc(ptr, 1+n_max, int);
sva->len = len = trealloc(len, 1+n_max, int);
sva->cap = cap = trealloc(cap, 1+n_max, int);
sva->prev = prev = trealloc(prev, 1+n_max, int);
sva->next = next = trealloc(next, 1+n_max, int);
}
/* initialize new vectors */
sva->n = new_n;
for (k = n+1; k <= new_n; k++)
{ ptr[k] = len[k] = cap[k] = 0;
prev[k] = next[k] = -1;
}
#if 1
if (sva->talky)
xprintf("now sva->n_max = %d, sva->n = %d\n",
sva->n_max, sva->n);
#endif
/* return reference number of very first new vector */
return n+1;
}
/***********************************************************************
* sva_resize_area - change size of SVA storage
*
* This routine increases or decrases the size of the SVA storage by
* reallocating it.
*
* The parameter delta specifies the number of location by which the
* current size of the SVA storage should be increased (if delta > 0)
* or decreased (if delta < 0). Note that if delta is negative, it
* should not be less than the current size of the middle part.
*
* As a result of this operation the size of the middle part of SVA is
* increased/decreased by delta locations.
*
* NOTE: This operation changes ptr[k] for all vectors stored in the
* right part of SVA. */
void sva_resize_area(SVA *sva, int delta)
{ int n = sva->n;
int *ptr = sva->ptr;
int size = sva->size;
int m_ptr = sva->m_ptr;
int r_ptr = sva->r_ptr;
int k, r_size;
#if 1
if (sva->talky)
xprintf("sva_resize_area: delta = %d\n", delta);
#endif
xassert(delta != 0);
/* determine size of the right part, in locations */
r_size = size - r_ptr + 1;
/* relocate the right part in case of negative delta */
if (delta < 0)
{ xassert(delta >= m_ptr - r_ptr);
sva->r_ptr += delta;
memmove(&sva->ind[sva->r_ptr], &sva->ind[r_ptr],
r_size * sizeof(int));
memmove(&sva->val[sva->r_ptr], &sva->val[r_ptr],
r_size * sizeof(double));
}
/* reallocate the storage arrays */
xassert(delta < INT_MAX - sva->size);
sva->size += delta;
sva->ind = trealloc(sva->ind, 1+sva->size, int);
sva->val = trealloc(sva->val, 1+sva->size, double);
/* relocate the right part in case of positive delta */
if (delta > 0)
{ sva->r_ptr += delta;
memmove(&sva->ind[sva->r_ptr], &sva->ind[r_ptr],
r_size * sizeof(int));
memmove(&sva->val[sva->r_ptr], &sva->val[r_ptr],
r_size * sizeof(double));
}
/* update pointers to vectors stored in the right part */
for (k = 1; k <= n; k++)
{ if (ptr[k] >= r_ptr)
ptr[k] += delta;
}
#if 1
if (sva->talky)
xprintf("now sva->size = %d\n", sva->size);
#endif
return;
}
/***********************************************************************
* sva_defrag_area - defragment left part of SVA
*
* This routine performs "garbage" collection to defragment the left
* part of SVA.
*
* NOTE: This operation may change ptr[k] and cap[k] for all vectors
* stored in the left part of SVA. */
void sva_defrag_area(SVA *sva)
{ int *ptr = sva->ptr;
int *len = sva->len;
int *cap = sva->cap;
int *prev = sva->prev;
int *next = sva->next;
int *ind = sva->ind;
double *val = sva->val;
int k, next_k, ptr_k, len_k, m_ptr, head, tail;
#if 1
if (sva->talky)
{ xprintf("sva_defrag_area:\n");
xprintf("before defragmenting = %d %d %d\n", sva->m_ptr - 1,
sva->r_ptr - sva->m_ptr, sva->size + 1 - sva->r_ptr);
}
#endif
m_ptr = 1;
head = tail = 0;
/* walk through the linked list of vectors stored in the left
* part of SVA */
for (k = sva->head; k != 0; k = next_k)
{ /* save number of next vector in the list */
next_k = next[k];
/* determine length of k-th vector */
len_k = len[k];
if (len_k == 0)
{ /* k-th vector is empty; remove it from the left part */
ptr[k] = cap[k] = 0;
prev[k] = next[k] = -1;
}
else
{ /* determine pointer to first location of k-th vector */
ptr_k = ptr[k];
xassert(m_ptr <= ptr_k);
/* relocate k-th vector to the beginning of the left part,
* if necessary */
if (m_ptr < ptr_k)
{ memmove(&ind[m_ptr], &ind[ptr_k],
len_k * sizeof(int));
memmove(&val[m_ptr], &val[ptr_k],
len_k * sizeof(double));
ptr[k] = m_ptr;
}
/* remove unused locations from k-th vector */
cap[k] = len_k;
/* the left part of SVA has been enlarged */
m_ptr += len_k;
/* add k-th vector to the end of the new linked list */
prev[k] = tail;
next[k] = 0;
if (head == 0)
head = k;
else
next[tail] = k;
tail = k;
}
}
/* set new pointer to the middle part of SVA */
xassert(m_ptr <= sva->r_ptr);
sva->m_ptr = m_ptr;
/* set new head and tail of the linked list */
sva->head = head;
sva->tail = tail;
#if 1
if (sva->talky)
xprintf("after defragmenting = %d %d %d\n", sva->m_ptr - 1,
sva->r_ptr - sva->m_ptr, sva->size + 1 - sva->r_ptr);
#endif
return;
}
/***********************************************************************
* sva_more_space - increase size of middle (free) part of SVA
*
* This routine increases the size of the middle (free) part of the
* sparse vector area (SVA).
*
* The parameter m_size specifies the minimal size, in locations, of
* the middle part to be provided. This new size should be greater than
* the current size of the middle part.
*
* First, the routine defragments the left part of SVA. Then, if the
* size of the left part has not sufficiently increased, the routine
* increases the total size of the SVA storage by reallocating it. */
void sva_more_space(SVA *sva, int m_size)
{ int size, delta;
#if 1
if (sva->talky)
xprintf("sva_more_space: m_size = %d\n", m_size);
#endif
xassert(m_size > sva->r_ptr - sva->m_ptr);
/* defragment the left part */
sva_defrag_area(sva);
/* set, heuristically, the minimal size of the middle part to be
* not less than the size of the defragmented left part */
if (m_size < sva->m_ptr - 1)
m_size = sva->m_ptr - 1;
/* if there is still not enough room, increase the total size of
* the SVA storage */
if (sva->r_ptr - sva->m_ptr < m_size)
{ size = sva->size; /* new sva size */
for (;;)
{ delta = size - sva->size;
if (sva->r_ptr - sva->m_ptr + delta >= m_size)
break;
size += size;
xassert(size > 0);
}
sva_resize_area(sva, delta);
xassert(sva->r_ptr - sva->m_ptr >= m_size);
}
return;
}
/***********************************************************************
* sva_enlarge_cap - enlarge capacity of specified vector
*
* This routine enlarges the current capacity of the specified vector
* by relocating its content.
*
* The parameter k specifies the reference number of the vector whose
* capacity should be enlarged, 1 <= k <= n. This vector should either
* have zero capacity or be stored in the left (dynamic) part of SVA.
*
* The parameter new_cap specifies the new capacity of the vector,
* in locations. This new capacity should be greater than the current
* capacity of the vector.
*
* The parameter skip is a flag. If this flag is set, the routine does
* *not* copy numerical values of elements of the vector on relocating
* its content, i.e. only element indices are copied.
*
* NOTE: On entry to the routine the middle part of SVA should have at
* least new_cap free locations. */
void sva_enlarge_cap(SVA *sva, int k, int new_cap, int skip)
{ int *ptr = sva->ptr;
int *len = sva->len;
int *cap = sva->cap;
int *prev = sva->prev;
int *next = sva->next;
int *ind = sva->ind;
double *val = sva->val;
xassert(1 <= k && k <= sva->n);
xassert(new_cap > cap[k]);
/* there should be at least new_cap free locations */
xassert(sva->r_ptr - sva->m_ptr >= new_cap);
/* relocate the vector */
if (cap[k] == 0)
{ /* the vector is empty */
xassert(ptr[k] == 0);
xassert(len[k] == 0);
}
else
{ /* the vector has non-zero capacity */
xassert(ptr[k] + len[k] <= sva->m_ptr);
/* copy the current vector content to the beginning of the
* middle part */
if (len[k] > 0)
{ memcpy(&ind[sva->m_ptr], &ind[ptr[k]],
len[k] * sizeof(int));
if (!skip)
memcpy(&val[sva->m_ptr], &val[ptr[k]],
len[k] * sizeof(double));
}
/* remove the vector from the linked list */
if (prev[k] == 0)
sva->head = next[k];
else
{ /* preceding vector exists; increase its capacity */
cap[prev[k]] += cap[k];
next[prev[k]] = next[k];
}
if (next[k] == 0)
sva->tail = prev[k];
else
prev[next[k]] = prev[k];
}
/* set new pointer and capacity of the vector */
ptr[k] = sva->m_ptr;
cap[k] = new_cap;
/* add the vector to the end of the linked list */
prev[k] = sva->tail;
next[k] = 0;
if (sva->head == 0)
sva->head = k;
else
next[sva->tail] = k;
sva->tail = k;
/* new_cap free locations have been consumed */
sva->m_ptr += new_cap;
xassert(sva->m_ptr <= sva->r_ptr);
return;
}
/***********************************************************************
* sva_reserve_cap - reserve locations for specified vector
*
* This routine reserves locations for the specified vector in the
* right (static) part of SVA.
*
* The parameter k specifies the reference number of the vector (this
* vector should have zero capacity), 1 <= k <= n.
*
* The parameter new_cap specifies a non-zero capacity of the vector,
* in locations.
*
* NOTE: On entry to the routine the middle part of SVA should have at
* least new_cap free locations. */
void sva_reserve_cap(SVA *sva, int k, int new_cap)
{ int *ptr = sva->ptr;
int *len = sva->len;
int *cap = sva->cap;
xassert(1 <= k && k <= sva->n);
xassert(new_cap > 0);
xassert(ptr[k] == 0 && len[k] == 0 && cap[k] == 0);
/* there should be at least new_cap free locations */
xassert(sva->r_ptr - sva->m_ptr >= new_cap);
/* set the pointer and capacity of the vector */
ptr[k] = sva->r_ptr - new_cap;
cap[k] = new_cap;
/* new_cap free locations have been consumed */
sva->r_ptr -= new_cap;
return;
}
/***********************************************************************
* sva_make_static - relocate specified vector to right part of SVA
*
* Assuming that the specified vector is stored in the left (dynamic)
* part of SVA, this routine makes the vector static by relocating its
* content to the right (static) part of SVA. However, if the specified
* vector has zero capacity, the routine does nothing.
*
* The parameter k specifies the reference number of the vector to be
* relocated, 1 <= k <= n.
*
* NOTE: On entry to the routine the middle part of SVA should have at
* least len[k] free locations, where len[k] is the length of the
* vector to be relocated. */
void sva_make_static(SVA *sva, int k)
{ int *ptr = sva->ptr;
int *len = sva->len;
int *cap = sva->cap;
int *prev = sva->prev;
int *next = sva->next;
int *ind = sva->ind;
double *val = sva->val;
int ptr_k, len_k;
xassert(1 <= k && k <= sva->n);
/* if the vector has zero capacity, do nothing */
if (cap[k] == 0)
{ xassert(ptr[k] == 0);
xassert(len[k] == 0);
goto done;
}
/* there should be at least len[k] free locations */
len_k = len[k];
xassert(sva->r_ptr - sva->m_ptr >= len_k);
/* remove the vector from the linked list */
if (prev[k] == 0)
sva->head = next[k];
else
{ /* preceding vector exists; increase its capacity */
cap[prev[k]] += cap[k];
next[prev[k]] = next[k];
}
if (next[k] == 0)
sva->tail = prev[k];
else
prev[next[k]] = prev[k];
/* if the vector has zero length, make it empty */
if (len_k == 0)
{ ptr[k] = cap[k] = 0;
goto done;
}
/* copy the vector content to the beginning of the right part */
ptr_k = sva->r_ptr - len_k;
memcpy(&ind[ptr_k], &ind[ptr[k]], len_k * sizeof(int));
memcpy(&val[ptr_k], &val[ptr[k]], len_k * sizeof(double));
/* set new pointer and capacity of the vector */
ptr[k] = ptr_k;
cap[k] = len_k;
/* len[k] free locations have been consumed */
sva->r_ptr -= len_k;
done: return;
}
/***********************************************************************
* sva_check_area - check sparse vector area (SVA)
*
* This routine checks the SVA data structures for correctness.
*
* NOTE: For testing/debugging only. */
void sva_check_area(SVA *sva)
{ int n_max = sva->n_max;
int n = sva->n;
int *ptr = sva->ptr;
int *len = sva->len;
int *cap = sva->cap;
int size = sva->size;
int m_ptr = sva->m_ptr;
int r_ptr = sva->r_ptr;
int head = sva->head;
int tail = sva->tail;
int *prev = sva->prev;
int *next = sva->next;
int k;
#if 0 /* 16/II-2004; SVA may be empty */
xassert(1 <= n && n <= n_max);
#else
xassert(0 <= n && n <= n_max);
#endif
xassert(1 <= m_ptr && m_ptr <= r_ptr && r_ptr <= size+1);
/* all vectors included the linked list should have non-zero
* capacity and be stored in the left part */
for (k = head; k != 0; k = next[k])
{ xassert(1 <= k && k <= n);
xassert(cap[k] > 0);
xassert(0 <= len[k] && len[k] <= cap[k]);
if (prev[k] == 0)
xassert(k == head);
else
{ xassert(1 <= prev[k] && prev[k] <= n);
xassert(next[prev[k]] == k);
}
if (next[k] == 0)
{ xassert(k == tail);
xassert(ptr[k] + cap[k] <= m_ptr);
}
else
{ xassert(1 <= next[k] && next[k] <= n);
xassert(prev[next[k]] == k);
xassert(ptr[k] + cap[k] <= ptr[next[k]]);
}
cap[k] = -cap[k];
}
/* all other vectors should either have zero capacity or be
* stored in the right part */
for (k = 1; k <= n; k++)
{ if (cap[k] < 0)
{ /* k-th vector is stored in the left part */
cap[k] = -cap[k];
}
else if (cap[k] == 0)
{ /* k-th vector has zero capacity */
xassert(ptr[k] == 0);
xassert(len[k] == 0);
}
else /* cap[k] > 0 */
{ /* k-th vector is stored in the right part */
xassert(0 <= len[k] && len[k] <= cap[k]);
xassert(r_ptr <= ptr[k] && ptr[k] + cap[k] <= size+1);
}
}
return;
}
/***********************************************************************
* sva_delete_area - delete sparse vector area (SVA)
*
* This routine deletes the sparse vector area (SVA) freeing all the
* memory allocated to it. */
void sva_delete_area(SVA *sva)
{ tfree(sva->ptr);
tfree(sva->len);
tfree(sva->cap);
tfree(sva->prev);
tfree(sva->next);
tfree(sva->ind);
tfree(sva->val);
tfree(sva);
return;
}
/* eof */