/* glpfhv.h */

/*----------------------------------------------------------------------
-- Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@mai2.rcnet.ru>,
--               Department for Applied Informatics, Moscow Aviation
--               Institute, Moscow, Russia. All rights reserved.
--
-- This file is a part of GLPK (GNU Linear Programming Kit).
--
-- 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 2, 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; see the file COPYING. If not, write to the Free
-- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
----------------------------------------------------------------------*/

#ifndef _GLPFHV_H
#define _GLPFHV_H

#define fhv_create            _glp_fhv_create
#define fhv_decomp            _glp_fhv_decomp
#define fhv_ftran             _glp_fhv_ftran
#define fhv_btran             _glp_fhv_btran
#define fhv_update            _glp_fhv_update
#define fhv_delete            _glp_fhv_delete

/*----------------------------------------------------------------------
-- The structure FHV defines FHV-factorization of the basis matrix B,
-- which is the following quintet:
--
--    [B] = (F, H, V, P, Q),                                         (1)
--
-- where F, H, and V are such matrices that
--
--    B = F * H * V,                                                 (2)
--
-- and P and Q are such permutation matrices that the matrix
--
--    U = P * V * Q                                                  (3)
--
-- is upper triangular.
--
-- The matrix F is built when the factorization is computed for a given
-- basis matrix. This matrix is stored in the form of eta file in both
-- row-like and column-like formats. In the row-like format the matrix F
-- has the form
--
--    F = FR[1] * FR[2] * ... * FR[fr_nfs],                          (4)
--
-- where FR[k], k = 1, 2, ..., fr_nfs, is a row_like factor that differs
-- from the unity matrix only in one row, fr_nfs is number of row-like
-- factors. Analagously, in the column-like format the same matrix F has
-- the form
--
--    F = FC[1] * FC[2] * ... * FC[fc_nfs],                          (5)
--
-- where FC[k], k = 1, 2, ..., fc_nfs, is a column-like factor that
-- differs from the unity matrix only in one column, fc_nfs is number of
-- column-like factors.
--
-- The matrix H is stored in the form of eta file using row-like format
-- and has the form
--
--    H = HR[1] * HR[2] * ... * HR[hr_nfs],                          (6)
--
-- where HR[k], k = 1, 2, ..., hr_nfs, is a row-like factor that differs
-- from the unity matrix only in one row, hr_nfs is number of row-like
-- factors. After the factorization has been built for some given basis
-- matrix the matrix H has no factors and thus it is the unity matrix.
-- Then each time when the factorization is recomputed for an adjacent
-- basis matrix, the next factor HR[k], k = 1, 2, ... is built and added
-- to the end of the eta file H.
--
-- The matrix V is stored using both row-wise and column-wise sparse
-- formats. Pivot elements of the matrix V (that correspond to diagonal
-- elements of the matrix U) are missing from row and column lists and
-- stored separately in an ordinary array.
--
-- Note that the matrix U = P*V*Q, where P and Q are known permutation
-- matrices, is not stored. Therefore all operations on the matrix U are
-- performed implictly, i.e. actually they are performed on the matrices
-- V, P, and Q. */

typedef struct FHV FHV;

struct FHV
{     /* FHV-factorization of the basis matrix */
      int m;
      /* order of the basis matrix */
      int valid;
      /* if this flag is not set, the factorization is invalid and can't
         be updated nor used in ftran and btran operations */
      /*--------------------------------------------------------------*/
      /* matrix F in row-like format */
      int fr_nfs;
      /* number of row-like factors (0 <= fr_nfs <= m) */
      int *fr_ndx; /* int fr_ndx[1+m]; */
      /* fr_ndx[0] is not used;
         fr_ndx[k], k = 1, ..., fr_nfs, is number of a non-trivial row
         of the k-th factor */
      int *fr_ptr; /* int fr_ptr[1+m]; */
      /* fr_ptr[0] is not used;
         fr_ptr[k], k = 1, ..., fr_nfs, is a pointer to the first
         element of the non-trivial row of the k-th factor in the sparse
         vector area */
      int *fr_cnt; /* int fr_cnt[1+m]; */
      /* fr_cnt[0] is not used;
         fr_cnt[k], k = 1, ..., fr_nfs, is number of elements in the
         non-trivial row of the k-th factor */
      /*--------------------------------------------------------------*/
      /* matrix F in column-like format */
      int fc_nfs;
      /* number of column-like factors (0 <= fc_nfs <= m) */
      int *fc_ndx; /* int fc_ndx[1+m]; */
      /* fc_ndx[0] is not used;
         fc_ndx[k], k = 1, ..., fc_nfs, is number of a non-trivial
         column of the k-th factor */
      int *fc_ptr; /* int fc_ptr[1+m]; */
      /* fc_ptr[0] is not used;
         fc_ptr[k], k = 1, ..., fc_nfs, is a pointer to the first
         element of the non-trivial column of the k-th factor in the
         sparse vector area */
      int *fc_cnt; /* int fc_cnt[1+m]; */
      /* fc_cnt[0] is not used;
         fc_cnt[k], k = 1, ..., fc_nfs, is number of elements in the
         non-trivial column of the k-th factor */
      /*--------------------------------------------------------------*/
      /* matrix H in row-like format */
      int hr_size;
      /* maximal number of row-like factors (this limits maximal number
         of updates of the factorization) */
      int hr_nfs;
      /* current number of row-like factors (0 <= hr_nfs <= hr_size) */
      int *hr_ndx; /* int hr_ndx[1+hr_size]; */
      /* hr_ndx[0] is not used;
         hr_ndx[k], k = 1, ..., hr_nfs, is number of a non-trivial row
         of the k-th factor */
      int *hr_ptr; /* int hr_ptr[1+hr_size]; */
      /* hr_ptr[0] is not used;
         hr_ptr[k], k = 1, ..., hr_nfs, is a pointer to the first
         element of the non-trivial row of the k-th factor in the sparse
         vector area */
      int *hr_cnt; /* int hr_cnt[1+hr_size]; */
      /* hr_cnt[0] is not used;
         hr_cnt[k], k = 1, ..., hr_nfs, is number of elements in the
         non-trivial row of the k-th factor */
      /*--------------------------------------------------------------*/
      /* matrix V in row-wise format */
      int *vr_ptr; /* int vr_ptr[1+m]; */
      /* vr_ptr[0] is not used;
         vr_ptr[i], i = 1, ..., m, is a pointer to the first element of
         the i-th row in the sparse vector area */
      int *vr_cnt; /* int vr_cnt[1+m]; */
      /* vr_cnt[0] is not used;
         vr_cnt[i], i = 1, ..., m, is number of elements in the i-th
         row */
      int *vr_cap; /* int vr_cap[1+m]; */
      /* vr_cap[0] is not used;
         vr_cap[i], i = 1, ..., m, is capacity of the i-th row, i.e.
         maximal number of elements, which can be stored there without
         relocating the row, vr_cap[i] >= vr_cnt[i] */
      double *vr_piv; /* double vr_piv[1+m]; */
      /* vr_piv[0] is not used;
         vr_piv[p], p = 1, ..., m, is value of the pivot element v[p,q],
         which corresponds to some diagonal element u[k,k] of the matrix
         U; note that the pivot elements of the matrix V are missing in
         rows of this matrix */
      /*--------------------------------------------------------------*/
      /* matrix V in column-wise format */
      int *vc_ptr; /* int vc_ptr[1+m]; */
      /* vc_ptr[0] is not used;
         vc_ptr[j], j = 1, ..., m, is a pointer to the first element of
         the j-th column in the sparse vector area */
      int *vc_cnt; /* int vc_cnt[1+m]; */
      /* vc_cnt[0] is not used;
         vc_cnt[j], j = 1, ..., m, is number of elements in the j-th
         column */
      int *vc_cap; /* int vc_cap[1+m]; */
      /* vc_cap[0] is not used;
         vc_cap[j], j = 1, ..., m, is capacity of the j-th column, i.e.
         maximal number of elements, which can be stored there without
         relocating the column, vc_cap[j] >= vc_cnt[j] */
      /* note that the pivot elements of the matrix V are stored in the
         array vr_piv (see above) and therefore they are missing in the
         columns of this matrix */
      /*--------------------------------------------------------------*/
      /* permutation matrices P and Q */
      int *pp_row; /* int pp_row[1+m]; */
      /* the matrix P in row-wise format;
         if p[i,j] = 1 then pp_row[i] = j (pp_row[0] is not used);
         if i-th row of the matrix V corresponds to i'-th row of the
         matrix U = P*V*Q then p_row[i'] = i */
      int *pp_col; /* int pp_col[1+m]; */
      /* the matrix P in column-wise format;
         if p[i,j] = 1 then pp_col[j] = i (pp_col[0] is not used);
         if i-th row of the matrix V corresponds to i'-th row of the
         matrix U = P*V*Q then p_col[i] = i' */
      int *qq_row; /* int qq_row[1+m]; */
      /* the matrix Q in row-wise format;
         if q[i,j] = 1 then qq_row[i] = j (qq_row[0] is not used);
         if j-th column of the matrix V corresponds to j'-th column of
         the matrix U = P*V*Q then q_row[j] = j' */
      int *qq_col; /* int qq_col[1+m]; */
      /* the matrix Q in column-wise format;
         if q[i,j] = 1 then qq_col[j] = i (qq_col[0] is not used);
         if j-th column of the matrix V corresponds to j'-th column of
         the matrix U = P*V*Q then q_col[j'] = j */
      /*--------------------------------------------------------------*/
      /* the sparse vector area is a set of locations and intended to
         store sparse vectors, which represent rows and columns of the
         matrices F, H, and V; each location is the doublet (ndx, val),
         where ndx is an index and val is a numerical value of a sparse
         vector element; in the whole each sparse vector is a set of
         adjacent locations defined by a pointer to the first element
         and number of elements; these pointer and number are stored in
         the corresponding matrix data structure (see above); the left
         part of sva is used to store rows and columns of the matrix V,
         the right part is used to store non-trivial row and columns of
         the matrices F and H; between the left part and the right part
         there is the middle part, locations of which are free */
      int sv_size;
      /* total size of the sparse vector area, in locations; locations
         are numbered by integers 1, 2, ..., sv_size, and the location
         with the number 0 is not used; if it is necessary, the size of
         sva is automatically increased */
      int sv_beg, sv_end;
      /* sva partitioning pointers:
         locations 1, ..., sv_beg-1 belong to the left part;
         locations sv_beg, ..., sv_end-1 belong to the middle part;
         locations sv_end, ..., sv_size belong to the right part;
         number of free locations, i.e. locations that belong to the
         middle part, is (sv_end - sv_beg) */
      int *sv_ndx; /* int sv_ndx[1+sv_size]; */
      /* sv_ndx[0] is not used;
         sv_ndx[k], 1 <= k <= sv_size, is the index field of the k-th
         location */
      double *sv_val; /* double sv_val[1+sv_size]; */
      /* sv_val[0] is not used;
         sv_val[k], 1 <= k <= sv_size, is the value field of the k-th
         location */
      /* in order to efficiently defragment the left part of sva there
         is a doubly linked list of rows and columns of the matrix V; in
         this linked list rows have numbers 1, ..., m, and columns have
         numbers m+1, ..., m+m, so each row or column of the matrix V is
         uniquely identified by one integer; the order of rows/columns
         corresponds to increasing their pointers vr_ptr[i]/vc_ptr[j] */
      int sv_head;
      /* number of row/column with lowest pointer */
      int sv_tail;
      /* number of row/column with greatest pointer */
      int *sv_prev; /* int sv_prev[1+m+m]; */
      /* sv_prev[k] is number of row/column, whose pointer is less than
         the pointer of the k-th row/column */
      int *sv_next; /* int sv_next[1+m+m]; */
      /* sv_next[k] is number of row/column, whose pointer is greater
         than the pointer of the k-th row/column */
      /*--------------------------------------------------------------*/
      /* in order to efficiently implement Markowitz strategy and Duff
         search technique there are two families {R[0], R[1], ..., R[m]}
         and {C[0], C[1], ..., C[m]}. Member R[k] is a set of active
         rows of the matrix V, which have k non-zeros. Similarly, member
         C[k] is a set of active columns of the matrix V, which have k
         non-zeros (in the active submatrix); each set R[k] and C[k] is
         implemented as a separate doubly linked list */
      int *rs_head; /* int rs_head[1+m]; */
      /* rs_head[k], 0 <= k <= m, is number of the first active row,
         which has k non-zeros */
      int *rs_prev; /* int rs_prev[1+m]; */
      /* rs_prev[0] is not used;
         rs_prev[i], 1 <= i <= m, is number of the previous active row,
         which has the same number of non-zeros as the i-th row */
      int *rs_next; /* int rs_next[1+m]; */
      /* rs_next[0] is not used;
         rs_next[i], 1 <= i <= m, is number of the next active row,
         which has the same number of non-zeros as the i-th row */
      double *rs_max; /* double rs_max[1+m]; */
      /* rs_max[0] is not used;
         rs_max[i], 1 <= i <= m, is used only if the i-th row is active
         and is the largest of absolute values of elements in the i-th
         row; rs_max[i] < 0.0 means that the largest value is not known
         yet and should be found (by the pivot selection routine) */
      int *cs_head; /* int cs_head[1+m]; */
      /* cs_head[k], 0 <= k <= m, is number of the first active column,
         which has k non-zeros (in the active submatrix) */
      int *cs_prev; /* int cs_prev[1+m]; */
      /* cs_prev[0] is not used;
         cs_prev[j], 1 <= j <= m, is number of the previous active
         column, which has the same number of non-zeros (in the active
         submatrix) as the j-th column */
      int *cs_next; /* int cs_next[1+m]; */
      /* cs_next[0] is not used;
         cs_next[j], 1 <= j <= m, is number of the next active column,
         which has the same number of non-zeros (in the active submat-
         rix) as the j-th column */
      /*--------------------------------------------------------------*/
      /* partially transformed column is inv(F*H)*B[j], where B[j] is a
         new column, which will replace the existing j-th column of the
         basis matrix */
      int cc_cnt;
      /* number of non-zeros of the partially transformed column; if
         cc_cnt is negative, the column is not prepared yet */
      int *cc_ndx; /* int cc_ndx[1+m]; */
      /* cc_ndx[0] is not used;
         cc_ndx[k], k = 1, ..., cc_cnt, is a row index of the partially
         transformed column element */
      double *cc_val; /* double cc_val[1+m]; */
      /* cc_val[0] is not used;
         cc_val[k], k = 1, ..., cc_cnt, is a numerical (non-zero) value
         of the column element */
      /*--------------------------------------------------------------*/
      /* working arrays */
      int *flag; /* int flag[1+m]; */
      double *work; /* double work[1+m]; */
      /*--------------------------------------------------------------*/
      /* control parameters */
      double eps_tol;
      /* epsilon tolerance; each numerical quantity with absolute value
         less than eps_tol is replaced by zero */
      double piv_tol;
      /* threshold pivoting tolerance, 0 < piv_tol < 1; element v[i,j]
         of the active submatrix fits to be pivot if it satisfies to the
         stability condition |v[i,j]| >= piv_tol * max|v[i,*]|, i.e. if
         this element is not very small (in absolute value) among other
         elements in the same row */
      double max_gro;
      /* maximal allowable growth of elements of the matrix V during all
         the elimination process; if on some elimination step the ratio
         big_v / max_b becomes greater than max_gro, the basis matrix is
         considered as ill-conditioned (it is assumed that the tolerance
         piv_tol has adequate value) */
      double upd_tol;
      /* update tolerance; if on updating the factorization absolute
         value of some diagonal element of the matrix U = P*V*Q is less
         than upd_tol, the factorization is considered as inaccurate */
      int new_sva;
      /* new required size of the sparse vector area, in locations;
         this parameter is automatically set by the routines fhv_decomp
         and fhv_update */
      /*--------------------------------------------------------------*/
      /* statistics */
      int nnz_b;
      /* number of non-zeros in the matrix B at the last reinversion */
      int nnz_f;
      /* current number of non-zeros in the matrix F */
      int nnz_h;
      /* current number of non-zeros in the matrix H */
      int nnz_v;
      /* current number of non-zeros in the matrix V (except pivots that
         correspond to diagonal elements of the matrix U = P*V*Q) */
      double max_b;
      /* the largest of absolute values of elements in the matrix B at
         the last reinversion */
      double big_v;
      /* estimated largest of absolute values of elements that appeared
         in the active submatrix during all the elimination process at
         the last reinversion */
      int rank;
      /* estimated rank of the matrix B at the last reinversion; set by
         the routine fhv_decomp */
};

extern FHV *fhv_create(int m, int max_upd);
/* create FHV-factorization */

extern int fhv_decomp(FHV *fhv,
      int (*column)(void *info, int j, int rn[], double bj[]),
      void *info);
/* compute FHV-factorization for given basis matrix */

extern void fhv_ftran(FHV *fhv, double z[], int save);
/* perform FTRAN using FHV-factorization */

extern void fhv_btran(FHV *fhv, double z[]);
/* perform BTRAN using FHV-factorization */

extern int fhv_update(FHV *fhv, int j);
/* update FHV-factorization for adjacent basis matrix */

extern void fhv_delete(FHV *fhv);
/* delete FHV-factorization */

#endif

/* eof */
