//
// abstract.h
//
// Copyright (C) 1996 Limit Point Systems, Inc.
//
// Author: Curtis Janssen <cljanss@limitpt.com>
// Maintainer: LPS
//
// This file is part of the SC Toolkit.
//
// The SC Toolkit is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// The SC Toolkit 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 Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with the SC Toolkit; see the file COPYING.LIB.  If not, write to
// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
// The U.S. Government is granted a limited license as per AL 91-7.
//

#ifndef _math_scmat_abstract_h
#define _math_scmat_abstract_h

#ifdef __GNUC__
#pragma interface
#endif

#include <util/group/message.h>

#include <util/state/state.h>
#include <math/scmat/dim.h>
#include <math/scmat/block.h>
#include <iostream>

namespace sc {

class SCMatrix;
class SymmSCMatrix;
class DiagSCMatrix;
class SCVector;

class SCElementOp;
class SCElementOp2;
class SCElementOp3;

class RefSCDimension;

/** The SCMatrixKit abstract class acts as a factory for producing
matrices.  By using one of these, the program makes sure that all of the
matrices are consistent.  */
class SCMatrixKit: public DescribedClass {
  protected:
    Ref<MessageGrp> grp_;
    
  public:
    SCMatrixKit();
    SCMatrixKit(const Ref<KeyVal>&);
    ~SCMatrixKit();

    // these members are default in local.cc
    /** This returns a LocalSCMatrixKit, unless the
        default has been changed with set_default_matrixkit. */
    static SCMatrixKit* default_matrixkit();
    static void set_default_matrixkit(const Ref<SCMatrixKit> &);

    Ref<MessageGrp> messagegrp() const;

    /// Given the dimensions, create matrices or vectors.
    virtual SCMatrix* matrix(const RefSCDimension&,const RefSCDimension&) = 0;
    virtual SymmSCMatrix* symmmatrix(const RefSCDimension&) = 0;
    virtual DiagSCMatrix* diagmatrix(const RefSCDimension&) = 0;
    virtual SCVector* vector(const RefSCDimension&) = 0;

    /** Given the dimensions and a StateIn object,
        restore matrices or vectors. */
    SCMatrix* restore_matrix(StateIn&,
                             const RefSCDimension&,
                             const RefSCDimension&);
    SymmSCMatrix* restore_symmmatrix(StateIn&,
                                     const RefSCDimension&);
    DiagSCMatrix* restore_diagmatrix(StateIn&,             
                                     const RefSCDimension&);
    SCVector* restore_vector(StateIn&,
                             const RefSCDimension&);
};


/** The SCVector class is the abstract base class for
    double valued vectors. */
class SCVector: public DescribedClass {
  protected:
    RefSCDimension d;
    Ref<SCMatrixKit> kit_;
  public:
    SCVector(const RefSCDimension&, SCMatrixKit *);

    /// Save and restore this in an implementation independent way.
    virtual void save(StateOut&);
    virtual void restore(StateIn&);

    /// Return the SCMatrixKit used to create this object.
    Ref<SCMatrixKit> kit() const { return kit_; }

    // concrete functions (some can be overridden)
    /// Return a vector with the same dimension and same elements.
    virtual SCVector* copy();
    /// Return a vector with the same dimension but uninitialized memory.
    virtual SCVector* clone();

    virtual ~SCVector();
    /// Return the length of the vector.
    int n() const { return d->n(); }
    /// Return the maximum absolute value element of this vector.
    virtual double maxabs() const;
    /// Normalize this.
    virtual void normalize();
    /// Assign each element to a random number between -1 and 1
    virtual void randomize();
    /// Assign all elements of this to val.
    void assign(double val) { assign_val(val); }
    /// Assign element i to v[i] for all i.
    void assign(const double* v) { assign_p(v); }
    /** Make this have the same elements as v.  The dimensions must
        match. */
    void assign(SCVector* v) { assign_v(v); }
    /// Overridden to implement the assign functions.
    virtual void assign_val(double val);
    virtual void assign_p(const double* v);
    virtual void assign_v(SCVector *v);
    /// Assign v[i] to element i for all i.
    virtual void convert(double* v) const;
    /** Convert an SCVector of a different specialization
        to this specialization and possibly accumulate the data. */
    virtual void convert(SCVector*);
    virtual void convert_accumulate(SCVector*);
    /// Multiply each element by val.
    virtual void scale(double val);

    /// Return the RefSCDimension corresponding to this vector.
    RefSCDimension dim() const { return d; }
    /// Set element i to val.
    virtual void set_element(int i,double val) = 0;
    /// Add val to element i.
    virtual void accumulate_element(int,double) = 0;
    /// Return the value of element i.
    virtual double get_element(int i) const = 0;
    /// Sum the result of m times v into this.
    void accumulate_product(SymmSCMatrix* m, SCVector* v)
        { accumulate_product_sv(m,v); }
    void accumulate_product(SCMatrix* m, SCVector* v)
        {  accumulate_product_rv(m,v); }
    virtual void accumulate_product_sv(SymmSCMatrix* m, SCVector* v);
    virtual void accumulate_product_rv(SCMatrix* m, SCVector* v) = 0;
    /// Sum v into this.
    virtual void accumulate(const SCVector*v) = 0;
    /// Sum m into this.  One of m's dimensions must be 1.
    virtual void accumulate(const SCMatrix*m) = 0;
    /// Return the dot product.
    virtual double scalar_product(SCVector*) = 0;
    /// Perform the element operation op on each element of this.
    virtual void element_op(const Ref<SCElementOp>&) = 0;
    virtual void element_op(const Ref<SCElementOp2>&,
                            SCVector*) = 0;
    virtual void element_op(const Ref<SCElementOp3>&,
                            SCVector*,SCVector*) = 0;
    /// Print out the vector.
    void print(std::ostream&o=ExEnv::out0()) const;
    void print(const char* title=0,std::ostream&out=ExEnv::out0(),int=10) const;
    virtual void vprint(const char*title=0,std::ostream&out=ExEnv::out0(),
                        int=10) const = 0;

    /// Returns the message group used by the matrix kit
    Ref<MessageGrp> messagegrp() const;
    
    /** Returns iterators for the local (rapidly accessible) blocks used in
        this vector.  Only one iterator is allowed for a matrix is it has
        Accum or Write access is allowed.  Multiple Read iterators are
        permitted. */
    virtual Ref<SCMatrixSubblockIter> local_blocks(
        SCMatrixSubblockIter::Access) = 0;
    /// Returns iterators for the all blocks used in this vector.
    virtual Ref<SCMatrixSubblockIter> all_blocks(SCMatrixSubblockIter::Access) = 0;
};

/** The SCMatrix class is the abstract base class for general double valued
    n by m matrices.  For symmetric matrices use SymmSCMatrix and for
    diagonal matrices use DiagSCMatrix. */
class SCMatrix: public DescribedClass {
  protected:
    RefSCDimension d1,d2;
    Ref<SCMatrixKit> kit_;
  public:
    // used to control transformations
    enum Transform { NormalTransform = 0, TransposeTransform = 1 };
    
    // concrete functions (some can be overridden)
    SCMatrix(const RefSCDimension&, const RefSCDimension&, SCMatrixKit *);
    virtual ~SCMatrix();

    /// Save and restore this in an implementation independent way.
    virtual void save(StateOut&);
    virtual void restore(StateIn&);

    /// Return the SCMatrixKit used to create this object.
    Ref<SCMatrixKit> kit() const { return kit_; }

    /// Return the number of rows.
    int nrow() const { return d1->n(); }
    /// Return the number of columns.
    int ncol() const { return d2->n(); }
    /// Return the maximum absolute value element.
    virtual double maxabs() const;
    /// Assign each element to a random number between -1 and 1
    virtual void randomize();
    /// Set all elements to val.
    void assign(double val) { assign_val(val); }
    /// Assign element i, j to m[ir*nrow()+j].
    void assign(const double* m) { assign_p(m); }
    /// Assign element i, j to m[i][j].
    void assign(const double** m) { assign_pp(m); }
    /// Make this have the same elements as m. The dimensions must match.
    void assign(SCMatrix* m) { assign_r(m); }
    /// Overridden to implement to assign members.
    virtual void assign_val(double val);
    virtual void assign_p(const double* m);
    virtual void assign_pp(const double** m);
    virtual void assign_r(SCMatrix* m);
    /** Like the assign members, but these write values to the
        arguments. */
    virtual void convert(double*) const;
    virtual void convert(double**) const;
    /** Convert an SCMatrix of a different specialization to this
        specialization and possibly accumulate the data. */
    virtual void convert(SCMatrix*);
    virtual void convert_accumulate(SCMatrix*);
    /// Multiply all elements by val.
    virtual void scale(double val);
    /// Scale the diagonal elements by val.
    virtual void scale_diagonal(double val);
    /// Shift the diagonal elements by val.
    virtual void shift_diagonal(double val);
    /// Make this equal to the unit matrix.
    virtual void unit();
    /// Return a matrix with the same dimension and same elements.
    virtual SCMatrix* copy();
    /// Return a matrix with the same dimension but uninitialized memory.
    virtual SCMatrix* clone();

    // pure virtual functions
    /// Return the row or column dimension.
    RefSCDimension rowdim() const { return d1; }
    RefSCDimension coldim() const { return d2; }
    /// Return or modify an element.
    virtual double get_element(int,int) const = 0;
    virtual void set_element(int,int,double) = 0;
    virtual void accumulate_element(int,int,double) = 0;
    
    /** Return a subblock of this.  The subblock is defined as
        the rows starting at br and ending at er, and the
        columns beginning at bc and ending at ec. */
    virtual SCMatrix * get_subblock(int br, int er, int bc, int ec) =0;

    /// Assign m to a subblock of this.
    virtual void assign_subblock(SCMatrix *m, int, int, int, int, int=0, int=0) =0;

    /// Sum m into a subblock of this.
    virtual void accumulate_subblock(SCMatrix *m, int, int, int, int, int=0,int=0) =0;
    
    /// Return a row or column of this.
    virtual SCVector * get_row(int i) =0;
    virtual SCVector * get_column(int i) =0;

    /// Assign v to a row or column of this.
    virtual void assign_row(SCVector *v, int i) =0;
    virtual void assign_column(SCVector *v, int i) =0;
    
    /// Sum v to a row or column of this.
    virtual void accumulate_row(SCVector *v, int i) =0;
    virtual void accumulate_column(SCVector *v, int i) =0;
    
    /// Sum m into this.
    virtual void accumulate(const SCMatrix* m) = 0;
    virtual void accumulate(const SymmSCMatrix* m) = 0;
    virtual void accumulate(const DiagSCMatrix* m) = 0;
    virtual void accumulate(const SCVector*) = 0;
    /// Sum into this the products of various vectors or matrices.
    virtual void accumulate_outer_product(SCVector*,SCVector*) = 0;
    void accumulate_product(SCMatrix*m1,SCMatrix*m2)
        { accumulate_product_rr(m1,m2); }
    void accumulate_product(SCMatrix*m1,SymmSCMatrix*m2)
        { accumulate_product_rs(m1,m2); }
    void accumulate_product(SCMatrix*m1,DiagSCMatrix*m2)
        { accumulate_product_rd(m1,m2); }
    void accumulate_product(SymmSCMatrix*m1,SCMatrix*m2)
        { accumulate_product_sr(m1,m2); }
    void accumulate_product(DiagSCMatrix*m1,SCMatrix*m2)
        { accumulate_product_dr(m1,m2); }
    void accumulate_product(SymmSCMatrix*m1,SymmSCMatrix*m2)
        { accumulate_product_ss(m1,m2); }
    virtual void accumulate_product_rr(SCMatrix*,SCMatrix*) = 0;
    virtual void accumulate_product_rs(SCMatrix*,SymmSCMatrix*);
    virtual void accumulate_product_rd(SCMatrix*,DiagSCMatrix*);
    virtual void accumulate_product_sr(SymmSCMatrix*,SCMatrix*);
    virtual void accumulate_product_dr(DiagSCMatrix*,SCMatrix*);
    virtual void accumulate_product_ss(SymmSCMatrix*,SymmSCMatrix*);
    /// Transpose this.
    virtual void transpose_this() = 0;
    /// Return the trace.
    virtual double trace() =0;
    /// Invert this.
    virtual double invert_this() = 0;
    /// Return the determinant of this.  this is overwritten.
    virtual double determ_this() = 0;

    /** Compute the singular value decomposition for this,
        possibly destroying this. */
    virtual void svd_this(SCMatrix *U, DiagSCMatrix *sigma, SCMatrix *V);
    virtual double solve_this(SCVector*) = 0;
    virtual void gen_invert_this();

    /** Schmidt orthogonalize this.  S is the overlap matrix.
        n is the number of columns to orthogonalize. */
    virtual void schmidt_orthog(SymmSCMatrix*, int n) =0;

    /** Schmidt orthogonalize this.  S is the overlap matrix.  tol is the
        tolerance.  The number of linearly independent vectors is
        returned. */
    virtual int schmidt_orthog_tol(SymmSCMatrix*, double tol, double*res=0)=0;
    
    /// Perform the element operation op on each element of this.
    virtual void element_op(const Ref<SCElementOp>&) = 0;
    virtual void element_op(const Ref<SCElementOp2>&,
                            SCMatrix*) = 0;
    virtual void element_op(const Ref<SCElementOp3>&,
                            SCMatrix*,SCMatrix*) = 0;
    /// Print out the matrix.
    void print(std::ostream&o=ExEnv::out0()) const;
    void print(const char* title=0,std::ostream& out=ExEnv::out0(),
               int =10) const;
    virtual void vprint(const char*title=0,
                        std::ostream&out=ExEnv::out0(),int =10) const = 0;

    /// Returns the message group used by the matrix kit
    Ref<MessageGrp> messagegrp() const;
    
    /** Returns iterators for the local (rapidly accessible)
        blocks used in this matrix. */
    virtual Ref<SCMatrixSubblockIter> local_blocks(
        SCMatrixSubblockIter::Access) = 0;
    /// Returns iterators for the all blocks used in this matrix.
    virtual Ref<SCMatrixSubblockIter> all_blocks(
        SCMatrixSubblockIter::Access) = 0;
};

/** The SymmSCMatrix class is the abstract base class for symmetric
    double valued matrices. */
class SymmSCMatrix: public DescribedClass {
  protected:
    RefSCDimension d;
    Ref<SCMatrixKit> kit_;
  public:
    SymmSCMatrix(const RefSCDimension&, SCMatrixKit *);
    ~SymmSCMatrix();

    /// Return the SCMatrixKit object that created this object.
    Ref<SCMatrixKit> kit() const { return kit_; }

    /// Save and restore this in an implementation independent way.
    virtual void save(StateOut&);
    virtual void restore(StateIn&);
    /// Return the maximum absolute value element of this vector.
    virtual double maxabs() const;
    /// Assign each element to a random number between -1 and 1
    virtual void randomize();
    /// Set all elements to val.
    void assign(double val) { assign_val(val); }
    /** Assign element i, j to m[i*(i+1)/2+j]. */
    void assign(const double* m) { assign_p(m); }
    /// Assign element i, j to m[i][j].
    void assign(const double** m) { assign_pp(m); }
    /** Make this have the same elements as m.  The dimensions must
        match. */
    void assign(SymmSCMatrix* m) { assign_s(m); }
    /// Overridden to implement the assign functions
    virtual void assign_val(double val);
    virtual void assign_p(const double* m);
    virtual void assign_pp(const double** m);
    virtual void assign_s(SymmSCMatrix* m);
    /// Like the assign members, but these write values to the arguments.
    virtual void convert(double*) const;
    virtual void convert(double**) const;
    /** Convert an SCSymmSCMatrix of a different specialization
        to this specialization and possibly accumulate the data. */
    virtual void convert(SymmSCMatrix*);
    virtual void convert_accumulate(SymmSCMatrix*);
    /// Multiply all elements by val.
    virtual void scale(double);
    /// Scale the diagonal elements by val.
    virtual void scale_diagonal(double);
    /// Shift the diagonal elements by val.
    virtual void shift_diagonal(double);
    /// Make this equal to the unit matrix.
    virtual void unit();
    /// Return the dimension.
    int n() const { return d->n(); }
    /// Return a matrix with the same dimension and same elements.
    virtual SymmSCMatrix* copy();
    /// Return a matrix with the same dimension but uninitialized memory.
    virtual SymmSCMatrix* clone();

    // pure virtual functions
    /// Return the dimension.
    RefSCDimension dim() const { return d; }
    /// Return or modify an element.
    virtual double get_element(int,int) const = 0;
    virtual void set_element(int,int,double) = 0;
    virtual void accumulate_element(int,int,double) = 0;

    /** Return a subblock of this.  The subblock is defined as the rows
        starting at br and ending at er, and the columns beginning at bc
        and ending at ec. */
    virtual SCMatrix * get_subblock(int br, int er, int bc, int ec) =0;
    virtual SymmSCMatrix * get_subblock(int br, int er) =0;

    /// Assign m to a subblock of this.
    virtual void assign_subblock(SCMatrix *m, int, int, int, int) =0;
    virtual void assign_subblock(SymmSCMatrix *m, int, int) =0;

    /// Sum m into a subblock of this.
    virtual void accumulate_subblock(SCMatrix *m, int, int, int, int) =0;
    virtual void accumulate_subblock(SymmSCMatrix *m, int, int) =0;

    /// Return a row of this.
    virtual SCVector * get_row(int i) =0;

    /// Assign v to a row of this.
    virtual void assign_row(SCVector *v, int i) =0;
    
    /// Sum v to a row of this.
    virtual void accumulate_row(SCVector *v, int i) =0;

    /** Diagonalize this, placing the eigenvalues in d and the eigenvectors
        in m. */
    virtual void diagonalize(DiagSCMatrix*d,SCMatrix*m) = 0;
    /// Sum m into this.
    virtual void accumulate(const SymmSCMatrix* m) = 0;
    /// Sum into this the products of various vectors or matrices.
    virtual void accumulate_symmetric_sum(SCMatrix*) = 0;
    virtual void accumulate_symmetric_product(SCMatrix*);
    virtual void accumulate_transform(SCMatrix*,SymmSCMatrix*,
                            SCMatrix::Transform = SCMatrix::NormalTransform);
    virtual void accumulate_transform(SCMatrix*,DiagSCMatrix*, 
                            SCMatrix::Transform = SCMatrix::NormalTransform);
    virtual void accumulate_transform(SymmSCMatrix*,SymmSCMatrix*);
    virtual void accumulate_symmetric_outer_product(SCVector*);
    /** Return the scalar obtained by multiplying this on the
        left and right by v. */
    virtual double scalar_product(SCVector* v);
    /// Return the trace.
    virtual double trace() = 0;
    /// Invert this.
    virtual double invert_this() = 0;
    /// Return the determinant of this.  this is overwritten.
    virtual double determ_this() = 0;

    virtual double solve_this(SCVector*) = 0;
    virtual void gen_invert_this() = 0;

    /// Perform the element operation op on each element of this.
    virtual void element_op(const Ref<SCElementOp>&) = 0;
    virtual void element_op(const Ref<SCElementOp2>&,
                            SymmSCMatrix*) = 0;
    virtual void element_op(const Ref<SCElementOp3>&,
                            SymmSCMatrix*,SymmSCMatrix*) = 0;
    /// Print out the matrix.
    void print(std::ostream&o=ExEnv::out0()) const;
    void print(const char* title=0,std::ostream& out=ExEnv::out0(),
               int =10) const;
    virtual void vprint(const char* title=0,
                        std::ostream& out=ExEnv::out0(), int =10) const;

    /// Returns the message group used by the matrix kit
    Ref<MessageGrp> messagegrp() const;
    
    /** Returns iterators for the local (rapidly accessible)
        blocks used in this matrix. */
    virtual Ref<SCMatrixSubblockIter> local_blocks(
        SCMatrixSubblockIter::Access) = 0;
    /// Returns iterators for the all blocks used in this matrix.
    virtual Ref<SCMatrixSubblockIter> all_blocks(
        SCMatrixSubblockIter::Access) = 0;
};

/** The SymmSCMatrix class is the abstract base class for diagonal double
    valued matrices.  */
class DiagSCMatrix: public DescribedClass {
  protected:
    RefSCDimension d;
    Ref<SCMatrixKit> kit_;
  public:
    DiagSCMatrix(const RefSCDimension&, SCMatrixKit *);
    ~DiagSCMatrix();

    /// Return the SCMatrixKit used to create this object.
    Ref<SCMatrixKit> kit() const { return kit_; }

    /// Save and restore this in an implementation independent way.
    virtual void save(StateOut&);
    virtual void restore(StateIn&);

    /// Return the maximum absolute value element of this vector.
    virtual double maxabs() const;
    /// Assign each element to a random number between -1 and 1
    virtual void randomize();
    /// Set all elements to val.
    void assign(double val) { assign_val(val); }
    /// Assign element i, i to m[i].
    void assign(const double*p) { assign_p(p); }
    /** Make this have the same elements as m.  The dimensions must
        match. */
    void assign(DiagSCMatrix*d_a) { assign_d(d_a); }
    /// Overridden to implement the assign members.
    virtual void assign_val(double val);
    virtual void assign_p(const double*);
    virtual void assign_d(DiagSCMatrix*);
    /// Like the assign member, but this writes values to the argument.
    virtual void convert(double*) const;
    /** Convert an SCDiagSCMatrix of a different specialization
        to this specialization and possibly accumulate the data. */
    virtual void convert(DiagSCMatrix*);
    virtual void convert_accumulate(DiagSCMatrix*);
    /// Multiply all elements by val.
    virtual void scale(double);
    /// Return the dimension.
    int n() const { return d->n(); }
    /// Return a matrix with the same dimension and same elements.
    virtual DiagSCMatrix* copy();
    /// Return a matrix with the same dimension but uninitialized memory.
    virtual DiagSCMatrix* clone();

    // pure virtual functions
    /// Return the dimension.
    RefSCDimension dim() const { return d; }
    /// Return or modify an element.
    virtual double get_element(int) const = 0;
    virtual void set_element(int,double) = 0;
    virtual void accumulate_element(int,double) = 0;
    /// Sum m into this.
    virtual void accumulate(const DiagSCMatrix* m) = 0;
    /// Return the trace.
    virtual double trace() = 0;
    /// Return the determinant of this.  this is overwritten.
    virtual double determ_this() = 0;
    /// Invert this.
    virtual double invert_this() = 0;
    /// Do a generalized inversion of this.
    virtual void gen_invert_this() = 0;
    /// Perform the element operation op on each element of this.
    virtual void element_op(const Ref<SCElementOp>&) = 0;
    virtual void element_op(const Ref<SCElementOp2>&,
                            DiagSCMatrix*) = 0;
    virtual void element_op(const Ref<SCElementOp3>&,
                            DiagSCMatrix*,DiagSCMatrix*) = 0;
    /// Print out the matrix.
    void print(std::ostream&o=ExEnv::out0()) const;
    void print(const char* title=0,
               std::ostream& out=ExEnv::out0(), int =10) const;
    virtual void vprint(const char* title=0,
                        std::ostream& out=ExEnv::out0(), int =10) const;

    /// Returns the message group used by the matrix kit
    Ref<MessageGrp> messagegrp() const;
    
    /** Returns iterators for the local (rapidly accessible)
        blocks used in this matrix. */
    virtual Ref<SCMatrixSubblockIter> local_blocks(
        SCMatrixSubblockIter::Access) = 0;
    /// Returns iterators for the all blocks used in this matrix.
    virtual Ref<SCMatrixSubblockIter> all_blocks(
        SCMatrixSubblockIter::Access) = 0;
};

}

#endif

// Local Variables:
// mode: c++
// c-file-style: "CLJ"
// End:
