/*
    DFT++ is a density functional package developed by the research group
    of Professor Tomas Arias

    Copyright 1996-2003 Sohrab Ismail-Beigi

    This file is part of DFT++.

    DFT++ 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 of the License, or
    (at your option) any later version.

    DFT++ 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 DFT++; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Please see the file CREDITS for a list of authors.

    For academic users, we request that publications using results obtained with
    this software reference

    "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi
    and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000).

    and, if using the wavelet basis, further reference

    "Multiresolution analysis of electronic structure: semicardinal and wavelet bases,"
    T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999).

    and 

    "Robust ab initio calculation of condensed matter: transparent convergence through
    semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and
    Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003).

    For your convenience, preprints of the above articles may be obtained from
    http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively.
*/

/*----------------------  ColumnBundle  --------------------------------*
 *                                                                       *
 * ColumnBundle class:  a collection of vectors composed of scalars--   *
 *                                                                       *
 * scalar_column's put into a larger overall structure (ColumnBundle.c) *
 *                                                                       *
 *-----------------------------------------------------------------------*/
#ifndef DFT_COLUMN_BUNDLE_H
#define DFT_COLUMN_BUNDLE_H

#include "parallel.h"   // include MPI related info.

class BlochState;

class ColumnBundle
{
 public:
  int tot_ncols, my_ncols;
  int start_ncol, end_ncol;
  int col_length;
  int distributed;

  CoeffSpaceWavefunctionData data; //ipd: 
  CoeffSpaceWavefunctionColumn **col;
  
  // default constructor
  ColumnBundle(){
    col = NULL;
    tot_ncols = my_ncols = 0;
    start_ncol= end_ncol = 0;
    col_length = 0;
    distributed = 0;
  }
  // copy constructor 
  ColumnBundle(const ColumnBundle &inbundle);
  // constructor
  ColumnBundle(int length, int ncol, Basis *basis = NULL);

  ColumnBundle(int ncols, Basis *basis, const char *disr);

  // Destructor:
  ~ColumnBundle();


/*    void init_temp(int, int); */
/*    void free_temp(); */
  
  void collectColumns(int ncols, CoeffSpaceWavefunctionColumn **col,
                      char *str);

 
  
  /* member functions: */
  void distribute_cols();  // generate my_ncols, etc. from tot_ncols
  int ncol_to_ID(int col) const; // get processor ID from column number
  /* Do what the constructor does, but as a member function */
  void init(int ncols, Basis *basis, const char *str);
    /* Free up the memory used by the structure by hand */
  void freemem(void);
  /* read and write ColumnBundles from/to binary format named files */
  void write(char *fname);
  void read(char *fname);
  /* read and write column_bunldes from/to binary format to already
   * opened IO streams...these functions don't close the streams. */
  void write_stream(FILE *stream);
  void read_stream(FILE *stream);
  /* zero out all the columns */
  void zero_out(void);
  /* Fill with random numbers */
  void randomize(void);
  /* Flips the sign of every entry in the ColumnBundle: i.e. y = (-1)*y */
  void negate(void);


  void allocate_memory();
  void copy_structure_to(ColumnBundle &) const;


  // Linear Algebra operators - should call array operators
  void operator=(const ColumnBundle &);
  void operator+=(const ColumnBundle &);
  void operator-=(const ColumnBundle &);

  void operator*=(const double &); //multiply by a double scalar
  void operator/=(const double &); //multiply by a double scalar
  
  //multiply by a complex scalar - just warn and exit in the real case
  void operator*=(const complex &);
  void operator/=(const complex &);
  

  // Binary operators
  friend  ColumnBundle  operator+(const ColumnBundle &, const ColumnBundle &);
  friend  ColumnBundle  operator-(const ColumnBundle &, const ColumnBundle &);
  	 
  friend  ColumnBundle  operator*(const double &, const ColumnBundle &);
  friend  ColumnBundle  operator*(const ColumnBundle &, const double &);
  friend  ColumnBundle  operator/(const ColumnBundle &, const double &);
  	 
  friend  ColumnBundle  operator*(const complex &, const ColumnBundle &);
  friend  ColumnBundle  operator*(const ColumnBundle &, const complex &);
  friend  ColumnBundle  operator/(const ColumnBundle &, const complex &);
  
  friend Matrix operator^(const ColumnBundle &, const ColumnBundle &);
  // Matrix Algebra - get the TMatrix too
//  friend class TMatrix<myData> operator^(const ColumnBundle&,const ColumnBundle&);
//  void operator*=(const class TMatrix<myData>); //multiply by a matrix
  friend class ColumnBundle operator*(const ColumnBundle &,
                                 const Matrix &);

  private:
  void get_scratch_memory();
  void free_scratch_memory();  
};


/* Does YM = Y*M multiplication 
 * accum == 1 does YM += Y*M instead. */
void do_ColumnBundle_matrix_mult(const ColumnBundle &Y,
                                 const Matrix &M,
                                 ColumnBundle &YM,
                                 int accum);


/* Read/write an array of ColumnBundles from/to a file */
void read_ColumnBundle_array(char *fname,int nbundles,ColumnBundle **Y);
void write_ColumnBundle_array(char *fname,int nbundles,ColumnBundle **Y);

/* Allocate/free an array of column bundles */
ColumnBundle **alloc_ColumnBundle_array(int nbundles,BlochState *states);
void free_ColumnBundle_array(int nbundles,ColumnBundle **Y);

/* Fill ColumnBundle_array with random numbers */
//void randomize_ColumnBundle_array(int nbundles,ColumnBundle *Y);

/* Sum of absolute squares of all members in a ColumnBundle */
real abs2(const ColumnBundle &Y);

/* Sum of absolute squares of all members in an array of ColumnBundles */
real abs2(int nbundles,ColumnBundle **Y);

/* Take "dot-product" of two ColumnBundles:  sum the diagonals of Y1^Y2 */
real dot(const ColumnBundle &Y1,const ColumnBundle &Y2);

/* Take "dot-product" of nbundles pairs of ColumnBundles: i.e.
 * do dot_ColumnBundles does, but just loop over the pairs. */
real dot(int nbundles,ColumnBundle **Y1,ColumnBundle **Y2);

/* Does Yout += s*Yin */
void scale_accumulate(real s,const ColumnBundle &Yin,ColumnBundle &Yout);
void scale_accumulate(int nbundles,
                      real s,ColumnBundle **Yin,ColumnBundle **Yout);

/* Does Yout = s1*Y1 + s2*Y2 */
void scaled_sum(real s1,ColumnBundle &Y1,real s2,ColumnBundle &Y2,
		ColumnBundle &Yout);
void scaled_sum(int nbundles, real s1,ColumnBundle **Y1,
                real s2,ColumnBundle **Y2, ColumnBundle **Yout);


void
create_bands(int ncols, CoeffSpaceWavefunctionColumn **cols, Basis *b);


void
destroy_bands(int ncols, CoeffSpaceWavefunctionColumn **cols);

#endif // DFT_COLUMN_BUNDLE_H
