// ---------------------------------------------------------------------------
// - Cblock.hpp                                                              -
// - afnix:mth module - real block matrix definitions                        -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2023 amaury darsch                                   -
// ---------------------------------------------------------------------------

#ifndef  AFNIX_CBLOCK_HPP
#define  AFNIX_CBLOCK_HPP

#ifndef  AFNIX_CMI_HPP
#include "Cmi.hpp"
#endif
 
#ifndef  AFNIX_CTRACE_HPP
#include "Ctrace.hpp"
#endif

#ifndef  AFNIX_CVECTOR_HPP
#include "Cvector.hpp"
#endif
 
namespace afnix {

  /// This Cblock class is the block implementation of the real matrix
  /// interface. The matrix is designed to support an array representation
  /// without any optimization for sparsity. Although the class is very
  /// efficient, it cannot grow as much as needed. Use with caution.
  /// @author amaury darsch

  class Cblock : public Cmi, public Viewable {
  public:
    /// add a matrix with another one
    /// @param mx the matrix argument
    /// @param my the matrix argument
    friend Cblock operator + (const Cblock& mx, const Cblock& my);

    /// substract a matrix with another one
    /// @param mx the matrix argument
    /// @param my the matrix argument
    friend Cblock operator - (const Cblock& mx, const Cblock& my);

    /// multiply a vector with a matrix
    /// @param m the matrix argument
    /// @param x the vector argument
    friend Cvector operator * (const Cblock& m, const Cvector& x);

    /// multiply two matrices
    /// @param mx the matrix argument
    /// @param my the matrix argument
    friend Cblock operator * (const Cblock& mx, const Cblock& my);

    /// compute the matrix tensor product
    /// @param mx the matrix argument
    /// @param my the matrix argument
    static Cblock product (const Cblock& mx, const Cblock& my);

  private:
    /// the matrix block
    t_real* p_blok;

  public:
    /// create a null matrix
    Cblock (void);

    /// create a square matrix by size
    /// @param size the matrix size
    Cblock (const t_long size);

    /// create a matrix by size
    /// @param rsiz the row size
    /// @param csiz the column size
    Cblock (const t_long rsiz, const t_long csiz);

    /// create a matrix by vector product
    /// @param u the vector argument
    /// @param v the vector argument
    Cblock (const Cvi& u, const Cvi& v);

    /// create a matrix by trace
    /// @param t the trace argument
    Cblock (const Cti& t);

    /// create a matrix by tensor product
    /// @param mx the matrix argument
    /// @param my the matrix argument
    Cblock (const Cmi& mx, const Cmi& my);

    /// copy construct this matrix
    /// @param that the matrix to copy
    Cblock (const Cblock& that);

    /// destroy this matrix
    ~Cblock (void);

    /// assign a matrix to this one
    /// @param that the matrix to assign
    Cblock& operator = (const Cblock& that);
    
    /// @return the class name
    String repr (void) const override;

    /// @return a clone of this object
    Object* clone (void) const override;

    /// @return the serial did
    t_word getdid (void) const override;

    /// @return the serial sid
    t_word getsid (void) const override;

    /// return true if the matrix is null
    bool isnil (void) const override;

    /// reset this matrix
    void reset (void) override;

    /// clear this matrix
    void clear (void) override;

    /// resize this matrix
    /// @param rsiz the new matrix row size
    /// @param csiz the new matrix column size
    void resize (const t_long rsiz, const t_long csiz) override;

    /// create a zero identical matrix
    Cmi* zeros (void) const override;

    /// copy a matrix diagonal into a trace
    Cti* cpt (void) const override;

    /// copy a matrix row into a vector
    /// @param row the row to copy
    Cvi* cpr (const t_long row) const override;

    /// copy a matrix column into a vector
    /// @param col the column to copy
    Cvi* cpc (const t_long col) const override;

    /// @return a new iterator for this matrix
    Iterator* makeit (void) override;

    /// permutate this matrix
    /// @param p the permutation object
    Cmi* permutate (const Cpi& p) const override;

    /// reverse permutate this matrix
    /// @param p the permutation object
    Cmi* reverse (const Cpi& p) const override;

    /// @return the viewable size
    long tosize (void) const override;

    /// @return the viewable data
    t_byte* tobyte (void) override;
    
    /// @return the viewable data
    const t_byte* tobyte (void) const override;
    
  public:
    /// no lock - set a matrix by position
    /// @param row the row position
    /// @param col the column position
    /// @param val the value to set
    void nlset (const t_long row, const t_long col,
		const Complex& val) override;

    /// no lock - get a matrix value by position
    /// @param row the row position
    /// @param col the column position
    Complex nlget (const t_long row, const t_long col) const override;

  private:
    // make the matrix iterator a friend
    friend class Cblockit;

  public:
    /// create a new object in a generic way
    /// @param argv the argument vector
    static Object* mknew (Vector* argv);

    /// @return true if the given quark is defined
    bool isquark (const long quark, const bool hflg) const override;

    /// operate this object with another object
    /// @param type   the operator type
    /// @param object the operand object
    Object* oper (t_oper type, Object* object) override;

    /// apply this object with a set of arguments and a quark
    /// @param zobj  the current evaluable
    /// @param nset  the current nameset    
    /// @param quark the quark to apply these arguments
    /// @param argv  the arguments to apply
    Object* apply (Evaluable* zobj, Nameset* nset, const long quark,
                   Vector* argv) override;
  };

  /// The Cblockit class is the iterator for the block real matrix class.
  /// Such iterator is constructed with the "makeit" method. The iterator
  /// is reset to the beginning of the matrix.
  /// @author amaury darsch

  class Cblockit : public Cmit {
  private:
    /// the matrix to iterate
    Cblock* p_mobj;
    /// the matrix row
    t_long  d_mrow;
    /// the matrix column
    t_long d_mcol;
    
  public:
    /// create a new iterator from a matrix
    /// @param mobj the matrix to iterate
    Cblockit (Cblock* vobj);

    /// create a new iterator by type
    /// @param mobj the matrix to iterate
    /// @param cmit the matrix iterator type
    Cblockit (Cblock* vobj, const t_cmit cmit);
    
    /// destroy this iterator
    ~Cblockit (void);
    
    /// @return the class name
    String repr (void) const override;
    
    /// reset the iterator to the begining
    void begin (void) override;
    
    /// reset the iterator to the end
    void end (void) override;
        
    /// @return the object at the current position
    Object* getobj (void) const override;
    
  public:
    /// no lock - move the matrix iterator to the next position
    void nlnext (void) override;
    
    /// no lock - move the matrix iterator to the previous position
    void nlprev (void) override;

    /// @return true if the iterator is at the end (no lock)
    bool nlend (void) const override;
    
    /// no lock - move the iterator to a point
    /// @param row the matrix row coordinate
    /// @param col the matrix col coordinate
    void nlmove (const t_long row, const t_long col) override;

    /// @return the row coordinate at the iterator position (no lock)
    t_long nlgrow (void) const override;

    /// @return the column coordinate at the iterator position (no lock)
    t_long nlgcol (void) const override;

    /// set the matrix at the iterator position (no lock)
    /// @param val the value to set
    void nlsval (const Complex& val) override;

    /// @return the value at the iterator position (no lock)
    Complex nlgval (void) const override;

  private:
    // move the iterator to the next position (no lock)
    void   mvnext (void);
    // move the iterator to the next row position (no lock)
    void   mrnext (void);
    // move the iterator to the next column position (no lock)
    void   mcnext (void);
    // move the iterator to the previous position (no lock)
    void   mvprev (void);
    // move the iterator to the previous row position (no lock)
    void   mrprev (void);
    // move the iterator to the previous column position (no lock)
    void   mcprev (void);

  private:
    // make the copy constructor private
    Cblockit (const Cblockit&) =delete;
    // make the assignment operator private
    Cblockit& operator = (const Cblockit&) =delete;
  };
}

#endif
