This file is indexed.

/usr/include/trilinos/AbstractLinAlgPack_MatrixOp.hpp is in libtrilinos-dev 10.4.0.dfsg-1ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
// @HEADER
// ***********************************************************************
// 
// Moocho: Multi-functional Object-Oriented arCHitecture for Optimization
//                  Copyright (2003) Sandia Corporation
// 
// Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
// license for use of this work by or on behalf of the U.S. Government.
// 
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//  
// This library 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
// Lesser General Public License for more details.
//  
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
// Questions? Contact Roscoe A. Bartlett (rabartl@sandia.gov) 
// 
// ***********************************************************************
// @HEADER

#ifndef ABSTRACT_LINALG_PACK_MATRIX_WITH_OP_H
#define ABSTRACT_LINALG_PACK_MATRIX_WITH_OP_H

#include <iosfwd>

#include "AbstractLinAlgPack_MatrixBase.hpp"
#include "Teuchos_RCP.hpp"

namespace AbstractLinAlgPack {

/** \brief Base class for all matrices that support basic matrix operations.
 * 
 * These basic operations are:
 *
 * Level-1 BLAS
 *
 * <tt>mwo_lhs += alpha * op(M_rhs)</tt> (BLAS xAXPY)<br>
 * <tt>mwo_lhs += alpha * op(M_rhs)  * op(P_rhs)</tt><br>
 * <tt>mwo_lhs += alpha * op(P_rhs)  * op(M_rhs)</tt><br>
 * <tt>mwo_lhs += alpha * op(P1_rhs) * op(M_rhs) * op(P2_rhs)</tt><br>
 *
 * <tt>M_lhs += alpha * op(mwo_rhs)</tt> (BLAS xAXPY)<br>
 * <tt>M_lhs += alpha * op(mwo_rhs) * op(P_rhs)</tt><br>
 * <tt>M_lhs += alpha * op(P_rhs)   * op(mwo_rhs)</tt><br>
 * <tt>M_lhs += alpha * op(P1_rhs)  * op(mwo_rhs) * op(P2_rhs)</tt><br>
 *
 * Level-2 BLAS
 *
 * <tt>v_lhs = alpha * op(M_rhs1) * v_rhs2 + beta * v_lhs</tt> (BLAS xGEMV)<br>
 * <tt>v_lhs = alpha * op(M_rhs1) * sv_rhs2 + beta * v_lhs</tt> (BLAS xGEMV)<br>
 * <tt>v_lhs = alpha * op(P_rhs1) * op(M_rhs2) * v_rhs3 + beta * v_lhs</tt><br>
 * <tt>v_lhs = alpha * op(P_rhs1) * op(M_rhs2) * sv_rhs3 + beta * v_lhs</tt><br>
 * <tt>result = v_rhs1' * op(M_rhs2) * v_rhs3</tt><br>
 * <tt>result = sv_rhs1' * op(M_rhs2) * sv_rhs3</tt><br>
 *
 * Level-3 BLAS
 *
 * <tt>mwo_lhs = alpha * op(M_rhs1)   * op(mwo_rhs2) + beta * mwo_lhs</tt> (right) (xGEMM)<br>
 * <tt>mwo_lhs = alpha * op(mwo_rhs1) * op(M_rhs2)   + beta * mwo_lhs</tt> (left)  (xGEMM)<br>
 * <tt>M_lhs   = alpha * op(mwo_rhs1) * op(mwo_rhs2) + beta * M_lhs</tt>           (xGEMM)<br>
 *
 * All of the Level-1, Level-2 and Level-3 BLAS operations have default implementations
 * based on the Level-2 BLAS operation:<br>
 *
 * <tt>v_lhs = alpha * op(M_rhs1) * v_rhs2 + beta * v_lhs</tt> (BLAS xGEMV)<br>
 *
 * The only methods that have to be overridden are \c space_cols(), \c space_rows()
 * and the single \c Vp_StMtV() method shown above.  This is to allow fast prototyping of
 * matrix subclasses and for postponement of writting specialized methods of other time
 * critical operations until later if they are needed.
 *
 * The vector space objects returned by the methods \c space_cols() and \c space_rows() 
 * are specifically bound to this matrix object.  The vector space objects returned should
 * only be considered to be transient and may become invalid if \c this is modified in some
 * significant way (but not through this <tt>%MatrixOp</tt> interface obviously).
 *
 * Most of the Level-1 through Level-3 BLAS methods should not be called directly by clients,
 * but instead be called through the \ref MatrixWithOp_func_grp "provided non-member functions".
 * The Level-1 and Level-3 matrix methods of this class have a special protocal in order to deal
 * with the multiple dispatch problem.  In essence, a poor man's multiple dispatch
 * is used to allow each of the participating matrix objects a chance to implement
 * an operation.  In each case, a non-member function must be called by the client
 * which calls the virtual methods on the matrix arguments one at a time.
 * All of the Level-1 and Level-3 matrix methods are implemented for the case where
 * the lhs matrix supports the <tt>MultiVectorMutable</tt> interface.  These matrix
 * operations are then implemented in terms of <tt>AbstractLinAlgPack::Vp_StMtV(...)</tt>
 * which must be implemented for every matrix subclass.  Therefore, any combination of
 * rhs matrices can always be used in any matrix operation as long as a compatible
 * (i.e. vector spaces match up) <tt>MultiVectorMutable</tt> object is used as the
 * lhs matrix argument.
 *
 * Note, this behavior is only implemented by the *nonmember* functions
 * <tt>AbstractLinAlgPack::Mp_StM(...)</tt> or <tt>AbstractLinAlgPack::Mp_StMtM(...)</tt>.
 * All of the default virtual implementations of <tt>Mp_StM(...)</tt> and
 * <tt>Mp_StMtM(...)</tt> return false.
 *
 * This form of multiple dispatach is not ideal in the sense that the first matrix
 * argument that *can* implement the method will do so instead of perhaps the *best*
 * implementation that could be provided by another matrix argument.  Therefore,
 * a matrix subclass should only override one of these matrix methods if it can
 * provide a significantly better implementation than the default.  If a client
 * needs exact control of the implementation of a matrix operation, then they
 * should consider using a ``Strategy'' object.
 *
 * ToDo: Add more detailed documentation for the default Level-1 and Level-3 BLAS
 * methods.
 */
class MatrixOp : public virtual MatrixBase {
public:

  /** @name Friends */
  //@{

  /** \brief . */
  friend
  void Mt_S( MatrixOp* mwo_lhs, value_type alpha );
  /** \brief . */
  friend
  void Mp_StM(
    MatrixOp* mwo_lhs, value_type alpha, const MatrixOp& M_rhs
    , BLAS_Cpp::Transp trans_rhs);
  /** \brief . */
  friend
  void Mp_StMtP(
    MatrixOp* mwo_lhs, value_type alpha
    ,const MatrixOp& M_rhs, BLAS_Cpp::Transp M_trans
    ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
    );
  /** \brief . */
  friend
  void Mp_StPtM(
    MatrixOp* mwo_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
    ,const MatrixOp& M_rhs, BLAS_Cpp::Transp M_trans
    );
  /** \brief . */
  friend
  void Mp_StPtMtP(
    MatrixOp* mwo_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,const MatrixOp& M_rhs, BLAS_Cpp::Transp trans_rhs
    ,const GenPermMatrixSlice& P_rhs2, BLAS_Cpp::Transp P_rhs2_trans
    );
  /** \brief . */
  friend
  void Vp_StMtV(
    VectorMutable* v_lhs, value_type alpha, const MatrixOp& M_rhs1
    ,BLAS_Cpp::Transp trans_rhs1, const Vector& v_rhs2, value_type beta
    );
  /** \brief . */
  friend
  void Vp_StMtV(
    VectorMutable* v_lhs, value_type alpha, const MatrixOp& M_rhs1
    ,BLAS_Cpp::Transp trans_rhs1, const SpVectorSlice& sv_rhs2, value_type beta
    );
  /** \brief . */
  friend
  void Vp_StPtMtV(
    VectorMutable* v_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,const MatrixOp& M_rhs2, BLAS_Cpp::Transp M_rhs2_trans
    ,const Vector& v_rhs3, value_type beta
    );
  /** \brief . */
  friend
  void Vp_StPtMtV(
    VectorMutable* v_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,const MatrixOp& M_rhs2, BLAS_Cpp::Transp M_rhs2_trans
    ,const SpVectorSlice& sv_rhs3, value_type beta
    );
  /** \brief . */
  friend
  value_type transVtMtV(
    const Vector& v_rhs1, const MatrixOp& M_rhs2
    ,BLAS_Cpp::Transp trans_rhs2, const Vector& v_rhs3
    );
  /** \brief . */
  friend
  value_type transVtMtV(
    const SpVectorSlice& sv_rhs1, const MatrixOp& M_rhs2
    ,BLAS_Cpp::Transp trans_rhs2, const SpVectorSlice& sv_rhs3
    );
  /** \brief . */
  friend
  void syr2k(
    const MatrixOp& M, BLAS_Cpp::Transp M_trans, value_type alpha
    ,const GenPermMatrixSlice& P1, BLAS_Cpp::Transp P1_trans
    ,const GenPermMatrixSlice& P2, BLAS_Cpp::Transp P2_trans
    ,value_type beta, MatrixSymOp* symwo_lhs
    );
  /** \brief . */
  friend
  void Mp_StMtM(
    MatrixOp* mwo_lhs, value_type alpha
    ,const MatrixOp& mwo_rhs1, BLAS_Cpp::Transp trans_rhs1
    ,const MatrixOp& mwo_rhs2, BLAS_Cpp::Transp trans_rhs2
    ,value_type beta
    );
  /** \brief . */
  friend
  void syrk(
    const MatrixOp  &mwo_rhs
    ,BLAS_Cpp::Transp   M_trans
    ,value_type         alpha
    ,value_type         beta
    ,MatrixSymOp    *sym_lhs
    );

  //@}

  /** @name Public types */
  //@{

#ifndef DOXYGEN_COMPILE
  /** \brief . */
  typedef Teuchos::RCP<const MatrixOp>    mat_ptr_t;
  /** \brief . */
  typedef Teuchos::RCP<MatrixOp>          mat_mut_ptr_t;
#endif

  /// Type of matrix norm
  enum EMatNormType {
    MAT_NORM_INF        ///< The induced infinity norm ||M||inf, i.e. max abs row sum
    ,MAT_NORM_2         ///< The induced two (i.e. Euclidean norm) norm ||M||2 
    ,MAT_NORM_1         ///< The induced one norm ||M||1, i.e. max abs col sum
    ,MAT_NORM_FORB      ///< The Forbenious norm, i.e. max abs element
  };

  /// Returned form <tt>calc_norm()</tt>.
  struct MatNorm {
    MatNorm(value_type _value, EMatNormType _type) : value(_value), type(_type) {}
    value_type    value;
    EMatNormType  type;
  };

  /// Thrown if a method is not implemented
  class MethodNotImplemented : public std::runtime_error
  {public: MethodNotImplemented(const std::string& what_arg) : std::runtime_error(what_arg) {}};

  /// Thrown if matrices are not compatible
  class IncompatibleMatrices : public std::logic_error
  {public: IncompatibleMatrices(const std::string& what_arg) : std::logic_error(what_arg) {}};

  //@}

  /** @name Minimal modifying methods */
  //@{

  /** \brief M_lhs = 0 :  Zero out the matrix.
   *
   * The default implementation throws an exception.  This is not
   * the best design but it meets some needs.  Any matrix implementation
   * could implement this method and mimic the behavior (i.e. see the
   * matrix subclass  <tt>MatrixZero</tt>).  However, only matrices that are
   * going to be on the lhs (non-const) of a Level-1 or Level-3 BLAS
   * operation need every implement this method.
   */
  virtual void zero_out();

  /** \brief M_lhs *= alpha : Multiply a matrix by a scalar.
   *
   * The default implementation throws an exception.  This is not
   * the best design but it meets some needs.  Any matrix implementation
   * could implement this method and mimic the behavior (i.e.
   * simply implement the matrix \c M as (<tt>alpha * M</tt>).
   * This method is only called in a few specialized situations. 
   */
  virtual void Mt_S( value_type alpha );

  /** \brief M_lhs = mwo_rhs : Virtual assignment operator.
    *
    * The default implementation just throws a std::logic_error
    * exception if it is not assignment to self.  A more specialized
    * implementation could use this to copy the state to <tt>this</tt> matrix
    * object from a compatible <tt>M</tt> matrix object.
    */
  virtual MatrixOp& operator=(const MatrixOp& mwo_rhs);

  //@}

  /** @name Clone */
  //@{

  /** \brief Clone the non-const matrix object (if supported).
   *
   * The primary purpose for this method is to allow a client to capture the
   * current state of a matrix object and be guaranteed that some other client
   * will not alter its behavior.  A smart implementation will use reference
   * counting and lazy evaluation internally and will not actually copy any
   * large amount of data unless it has to.
   *
   * The default implementation returns NULL which is perfectly acceptable.
   * A matrix object is not required to return a non-NULL value but almost
   * every good matrix implementation will.
   */
  virtual mat_mut_ptr_t clone();

  /** \brief Clone the const matrix object (if supported).
   *
   * The behavior of this method is the same as for the non-const version
   * above except it returns a smart pointer to a const matrix object.
   */
  virtual mat_ptr_t clone() const;

  //@}

  /** @name Output */
  //@{

  /** \brief Virtual output function.
    *
    * The default implementaion just extracts rows one at
    * a time by calling <tt>this->Vp_StMtV()</tt> with 
    * <tt>EtaVector</tt> objects and then prints the rows.
    */
  virtual std::ostream& output(std::ostream& out) const;

  //@}

  /** @name Norms */
  //@{

  /** \brief Compute a norm of this matrix.
   *
   * @param  requested_norm_type
   *                    [in] Determines the requested type of norm to be computed.
   * @param  allow_replacement
   *                    [in] Determines if the requested norm in specified in <tt>norm_type</tt>
   *                    can be replaced with another norm that can be computed by the matrix.
   *
   * @return If a norm is computed, then <tt>return.value</tt> gives the value of the norm
   * of type <tt>return.type</tt>.
   *
   * Postconditions:<ul>
   * <li> If <tt>allow_replacement==true</tt>, the matrix object must return a computted norm
   *      who's type is given in <tt>return.type</tt>.
   * <li> If <tt>allow_replacement==false</tt> and the underlying matrix object can not compute
   *      the norm requested in <tt>norm_type</tt>, then a <tt>MethodNotImplemented</tt> exception
   *      will be thrown.  If the matrix object can compute this norm, then <tt>return.type</tt>
   *      will be equal to <tt>requested_norm_type</tt>.
   * </ul>
   *
   * The default implementation of this method uses Algorithm 2.5 in "Applied Numerical Linear Algebra"
   * by James Demmel (1997) to estimate ||M||1 or ||M||inf.  The algorithm uses some of the refinements in the
   * referenced algorithm by Highman.  This algorithm only requires mat-vecs and transposed
   * mat-vecs so every matrix object can implement this method.  The main purpose of this default
   * implementation is to allow a default implementation of the estimation of the <tt>||.||1</tt>
   * or <tt>||.||inf</tt> normed condition number in the class <tt>MatrixOpNonsing</tt>.
   * The default arguments for this function will compute a norm and will not thrown an
   * exception.  The default implementation will throw an exception for any other norm type than
   * <tt>requested_norm_type == MAT_NORM_1</tt> or <tt>requested_norm_type = MAT_NORM_INF</tt>.
   */
  const MatNorm calc_norm(
    EMatNormType  requested_norm_type = MAT_NORM_1
    ,bool         allow_replacement   = false
    ) const;

  //@}

  /** @name Sub-matrix views */
  //@{

  /** \brief Create a transient constant sub-matrix view of this matrix (if supported).
   *
   * This view is to be used immediatly and then discarded.
   *
   * This method can only be expected to return <tt>return.get() != NULL</tt> if
   * <tt>this->space_cols().sub_space(row_rng) != NULL</tt> and
   * <tt>this->space_rows().sub_space(col_rng) != NULL</tt>.
   *
   * It is allows for a matrix implementation to return <tt>return.get() == NULL</tt>
   * for any arbitrary subview.
   *
   * The default implementation uses the matrix subclass \c MatrixOpSubView
   * and therefore, can return any arbitrary subview.  More specialized implementations
   * may want to restrict the subview that can be created somewhat.
   */
  virtual mat_ptr_t sub_view(const Range1D& row_rng, const Range1D& col_rng) const;
  
  /** \brief Inlined implementation calls <tt>this->sub_view(Range1D(rl,ru),Range1D(cl,cu))</tt>.
   */
  mat_ptr_t sub_view(
    const index_type& rl, const index_type& ru
    ,const index_type& cl, const index_type& cu
    ) const;

  //@}
  
  /** @name Permuted views */
  //@{

  /** \brief Create a permuted view: <tt>M_perm = P_row' * M * P_col</tt>.
   *
   * @param  P_row  [in] Row permutation.  If <tt>P_row == NULL</tt> then the
   *                indentity permutation is used.
   * @param row_part
   *                [in] Array (length <tt>num_row_part+1</tt>) storing the row indexes
   *                that may be passed to <tt>return->sub_view(r1,r2,...)</tt>.  If
   *                <tt>row_part == NULL</tt> then the assumed array is <tt>{ 1, this->rows() }</tt>. 
   * @param  num_row_part
   *                [in] Length of the array \c row_part.  If <tt>row_part == NULL</tt> then this
   *                argument is ignored.
   * @param  P_col  [in] Column permutation.  If <tt>P_col == NULL</tt> then the
   *                indentity permutation is used.
   * @param col_part
   *                [in] Array (length <tt>num_col_part+1</tt>) storing the column indexes
   *                that may be passed to <tt>return->sub_view(...,c1,c2)</tt>.  If
   *                <tt>col_part == NULL</tt> then the assumed array is <tt>{ 1, this->cols() }</tt>. 
   * @param  num_col_part
   *                [in] Length of the array \c col_part.  If <tt>col_part == NULL</tt> then this
   *                argument is ignored.
   *
   * Preconditions:<ul>
   * <li> [<tt>P_row != NULL</tt>] <tt>P_row->space().is_compatible(this->space_cols())</tt>
   *      (throw <tt>VectorSpace::IncompatibleVectorSpaces</tt>)
   * <li> [<tt>P_col != NULL</tt>] <tt>P_col->space().is_compatible(this->space_rows())</tt>
   *      (throw <tt>VectorSpace::IncompatibleVectorSpaces</tt>)
   * <li> [<tt>row_part != NULL</tt>] <tt>1 <= row_part[i-1] < row_part[i] <= this->rows(), for i = 1...num_row_part</tt>
   *      (throw ???)
   * <li> [<tt>col_part != NULL</tt>] <tt>1 <= col_part[i-1] < col_part[i] <= this->cols(), for i = 1...num_col_part</tt>
   *      (throw ???)
   * </ul>
   *
   * Postconditions:<ul>
   * <li> The subviews <tt>return->sub_view(R,C)</tt> should be able to be created efficiently where
   *      <tt>R = [row_part[kr-1],row_part[kr]-1], for kr = 1...num_row_part</tt> and
   *      <tt>C = [col_part[kc-1],col_part[kc]-1], for kc = 1...num_col_part</tt>.
   * </ul>
   *
   * The default implementation returns a <tt>MatrixPermAggr</tt> object.
   */
  virtual mat_ptr_t perm_view(
    const Permutation          *P_row
    ,const index_type          row_part[]
    ,int                       num_row_part
    ,const Permutation         *P_col
    ,const index_type          col_part[]
    ,int                       num_col_part
    ) const;

  /** \brief Reinitialize a permuted view: <tt>M_perm = P_row' * M * P_col</tt>.
   *
   * @param  P_row  [in] Same as input to \c perm_view().
   * @param  row_part
   *                [in] Same as input to \c perm_view().
   * @param  num_row_part
   *                [in] Same as input to \c perm_view().
   * @param  P_col  [in] Same as input to \c perm_view().
   * @param  col_part
   *                [in] Same as input to \c perm_view().
   * @param  num_col_part
   *                [in] Same as input to \c perm_view().
   * @param  perm_view
   *                [in] Smart pointer to a permuted view
   *                returned from <tt>this->perm_view()</tt>.
   *
   * Preconditions:<ul>
   * <li> [<tt>P_row != NULL</tt>] <tt>P_row->space().is_compatible(this->space_cols())</tt>
   *      (throw <tt>VectorSpace::IncompatibleVectorSpaces</tt>)
   * <li> [<tt>P_col != NULL</tt>] <tt>P_col->space().is_compatible(this->space_rows())</tt>
   *      (throw <tt>VectorSpace::IncompatibleVectorSpaces</tt>)
   * <li> [<tt>row_part != NULL</tt>] <tt>1 <= row_part[i-1] < row_part[i] <= this->rows(), for i = 1...num_row_part</tt>
   *      (throw ???)
   * <li> [<tt>col_part != NULL</tt>] <tt>1 <= col_part[i-1] < col_part[i] <= this->cols(), for i = 1...num_col_part</tt>
   *      (throw ???)
   * </ul>
   *
   * The default implementation simply returns <tt>this->perm_view()</tt>
   */
  virtual mat_ptr_t perm_view_update(
    const Permutation          *P_row
    ,const index_type          row_part[]
    ,int                       num_row_part
    ,const Permutation         *P_col
    ,const index_type          col_part[]
    ,int                       num_col_part
    ,const mat_ptr_t           &perm_view
    ) const;

  //@}

#ifdef TEMPLATE_FRIENDS_NOT_SUPPORTED
public:
#else
protected:
#endif

  /** @name Level-1 BLAS */
  //@{

  /** \brief mwo_lhs += alpha * op(M_rhs) (BLAS xAXPY).
   *
   * The default implementation does nothing returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StM()</tt>.
   */
  virtual bool Mp_StM(
    MatrixOp* mwo_lhs, value_type alpha
    ,BLAS_Cpp::Transp trans_rhs
    ) const;

  /** \brief M_lhs += alpha * op(mwo_rhs) (BLAS xAXPY).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StM()</tt>.
   */
  virtual bool Mp_StM(
    value_type alpha,const MatrixOp& M_rhs, BLAS_Cpp::Transp trans_rhs
    );

  /** \brief mwo_lhs += alpha * op(M_rhs) * op(P_rhs).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StMtP()</tt>.
   */
  virtual bool Mp_StMtP(
    MatrixOp* mwo_lhs, value_type alpha
    ,BLAS_Cpp::Transp M_trans
    ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
    ) const;

  /** \brief M_lhs += alpha * op(mwo_rhs) * op(P_rhs).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StMtP()</tt>.
   */
  virtual bool Mp_StMtP(
    value_type alpha
    ,const MatrixOp& mwo_rhs, BLAS_Cpp::Transp M_trans
    ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
    );

  /** \brief mwo_lhs += alpha * op(P_rhs) * op(M_rhs).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StPtM()</tt>.
   */
  virtual bool Mp_StPtM(
    MatrixOp* mwo_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
    ,BLAS_Cpp::Transp M_trans
    ) const;

  /** \brief M_lhs += alpha * op(P_rhs) * op(mwo_rhs).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StPtM()</tt>.
   */
  virtual bool Mp_StPtM(
    value_type alpha
    ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
    ,const MatrixOp& mwo_rhs, BLAS_Cpp::Transp M_trans
    );

  /** \brief mwo_lhs += alpha * op(P_rhs1) * op(M_rhs) * op(P_rhs2).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StPtMtP()</tt>.
   */
  virtual bool Mp_StPtMtP(
    MatrixOp* mwo_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,BLAS_Cpp::Transp M_trans
    ,const GenPermMatrixSlice& P_rhs2, BLAS_Cpp::Transp P_rhs2_trans
    ) const;

  /** \brief M_lhs += alpha * op(P_rhs1) * op(mwo_rhs) * op(P_rhs2).
   *
   * The default implementation does nothing and returns false.
   *
   * A client can not call this method call this method directly.
   * Instead, use <tt>AbstractLinAlgPack::Mp_StPtMtP()</tt>.
   */
  virtual bool Mp_StPtMtP(
    value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,const MatrixOp& mwo_rhs, BLAS_Cpp::Transp M_trans
    ,const GenPermMatrixSlice& P_rhs2, BLAS_Cpp::Transp P_rhs2_trans
    );

  //		end Level-1 BLAS
  //@}

  /** @name Level-2 BLAS */
  //@{

  /// v_lhs = alpha * op(M_rhs1) * v_rhs2 + beta * v_lhs (BLAS xGEMV)
  virtual void Vp_StMtV(
    VectorMutable* v_lhs, value_type alpha, BLAS_Cpp::Transp trans_rhs1
    ,const Vector& v_rhs2, value_type beta
    ) const = 0;

  /// v_lhs = alpha * op(M_rhs1) * sv_rhs2 + beta * v_lhs (BLAS xGEMV)
  virtual void Vp_StMtV(
    VectorMutable* v_lhs, value_type alpha, BLAS_Cpp::Transp trans_rhs1
    ,const SpVectorSlice& sv_rhs2, value_type beta
    ) const;

  /// v_lhs = alpha * op(P_rhs1) * op(M_rhs2) * v_rhs3 + beta * v_rhs
  virtual void Vp_StPtMtV(
    VectorMutable* v_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,BLAS_Cpp::Transp M_rhs2_trans
    ,const Vector& v_rhs3, value_type beta
    ) const;

  /// v_lhs = alpha * op(P_rhs1) * op(M_rhs2) * sv_rhs3 + beta * v_rhs
  virtual void Vp_StPtMtV(
    VectorMutable* v_lhs, value_type alpha
    ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
    ,BLAS_Cpp::Transp M_rhs2_trans
    ,const SpVectorSlice& sv_rhs3, value_type beta
    ) const;

  /// result = v_rhs1' * op(M_rhs2) * v_rhs3
  virtual value_type transVtMtV(
    const Vector& v_rhs1, BLAS_Cpp::Transp trans_rhs2
    ,const Vector& v_rhs3
    ) const;

  /// result = sv_rhs1' * op(M_rhs2) * sv_rhs3
  virtual value_type transVtMtV(
    const SpVectorSlice& sv_rhs1, BLAS_Cpp::Transp trans_rhs2
    ,const SpVectorSlice& sv_rhs3
    ) const;

  /** \brief Perform a specialized rank-2k update of a dense symmetric matrix of the form:
   *
   * <tt>symwo_lhs += alpha*op(P1')*op(M)*op(P2) + alpha*op(P2')*op(M')*op(P1) + beta*symwo_lhs</tt>
   *
   * The reason that this operation is being classified as a level-2 operation is that the
   * total flops should be of <tt>O(n^2)</tt> and not <tt>O(n^2*k)</tt>.
   *
   * The default implementation is based on <tt>Mp_StMtP(...)</tt> and <tt>Mp_StPtM(...)</tt>.
   * Of course in situations where this default implemention is inefficient the subclass should
   * override this method.
   */
  virtual void syr2k(
    BLAS_Cpp::Transp M_trans, value_type alpha
    ,const GenPermMatrixSlice& P1, BLAS_Cpp::Transp P1_trans
    ,const GenPermMatrixSlice& P2, BLAS_Cpp::Transp P2_trans
    ,value_type beta, MatrixSymOp* symwo_lhs
    ) const;

  //@}

  /** @name Level-3 BLAS */
  //@{

  /** \brief mwo_lhs = alpha * op(M_rhs1) * op(mwo_rhs2) + beta * mwo_lhs (left) (xGEMM).
   *
   * The default implementation does nothing and returns false.
   *
   * Warning! A client should never call this method
   * call this method directly.  Instead, use
   * <tt>AbstractLinAlgPack::Mp_StMtM()</tt>.
   */
  virtual bool Mp_StMtM(
    MatrixOp* mwo_lhs, value_type alpha
    ,BLAS_Cpp::Transp trans_rhs1
    ,const MatrixOp& mwo_rhs2, BLAS_Cpp::Transp trans_rhs2
    ,value_type beta
    ) const;

  /** \brief mwo_lhs = alpha * op(mwo_rhs1) * op(M_rhs2) + beta * mwo_lhs (right) (xGEMM)
   *
   * The default implementation does nothing and returns false.
   *
   * Warning! A client should never call this method
   * call this method directly.  Instead, use
   * <tt>AbstractLinAlgPack::Mp_StMtM()</tt>.
   */
  virtual bool Mp_StMtM(
    MatrixOp* mwo_lhs, value_type alpha
    ,const MatrixOp& mwo_rhs1, BLAS_Cpp::Transp trans_rhs1
    ,BLAS_Cpp::Transp trans_rhs2
    ,value_type beta
    ) const;

  /** \brief M_lhs = alpha * op(mwo_rhs1) * op(mwo_rhs2) + beta * mwo_lhs (left) (xGEMM)
   *
   * The default implementation does nothing and returns false.
   *
   * Warning! A client should never call this method
   * call this method directly.  Instead, use
   * <tt>AbstractLinAlgPack::Mp_StMtM()</tt>.
   */
  virtual bool Mp_StMtM(
    value_type alpha
    ,const MatrixOp& mwo_rhs1, BLAS_Cpp::Transp trans_rhs1
    ,const MatrixOp& mwo_rhs2,BLAS_Cpp::Transp trans_rhs2
    ,value_type beta
    );
  
  /** \brief Perform a rank-k update of a symmetric matrix of the form:
   *
   * <tt>symwo_lhs += alpha*op(M)*op(M') + beta*symwo_lhs</tt>
   *
   * where <tt>this</tt> is the rhs matrix argument.
   *
   * Never call this method directly.  Instead use the nonmember function
   * <tt>AbstractLinAlgPack::syrk()</tt>.
   *
   * The default implementation returns <tt>false</tt> and does nothing.
   */
  virtual bool syrk(
    BLAS_Cpp::Transp   M_trans
    ,value_type        alpha
    ,value_type        beta
    ,MatrixSymOp      *sym_lhs
    ) const;
  
  /** \brief Perform a rank-k update of a symmetric matrix of the form:
   *
   * <tt>M += alpha*op(mwo_rhs)*op(mwo_rhs') + beta*M</tt>
   *
   * where <tt>this</tt> is the lhs matrix argument.
   *
   * Never call this method directly.  Instead use the nonmember function
   * <tt>AbstractLinAlgPack::syrk()</tt>.
   *
   * The default implementation returns <tt>false</tt> and does nothing.
   */
  virtual bool syrk(
    const MatrixOp      &mwo_rhs
    ,BLAS_Cpp::Transp   M_trans
    ,value_type         alpha
    ,value_type         beta
    );

  //		end Level-3 BLAS
  //@}

};	// end class MatrixOp

// ////////////////////////////////////////////////////////////////////////////////////////////////
/** \defgroup MatrixWithOp_func_grp MatrixOp non-member functions that call virtual functions.
  *
  * These allow nonmember functions to act like virtual functions.  If any of these methods
  * on the subclasses are not implemented for a particular set of matrix arguments, then the
  * exception <tt>AbstractLinAlgPack::MatrixOp::MethodNotImplemented</tt> is thrown.
  * This will not happen as long as a compatible (vector spaces are compatible) lhs matrix
  * argument is passed in and <tt>dynamic_cast<MultiVectorMatrix*>(lhs) != NULL</tt>.
  */
//@{

/** @name Level-1 BLAS */
//@{

//
/** mwo_lhs *= alpha.
 *
 * If <tt>alpha == 0.0</tt> then <tt>mwo_lhs->zero_out()</tt> will be called,
 * otherwise <tt>mwo_lhs->Mt_S(alpha)</tt> will be called.  If <tt>alpha == 1.0</tt>
 * then nothing is done.
 */
void Mt_S( MatrixOp* mwo_lhs, value_type alpha );

//
/** mwo_lhs += alpha * op(M_rhs) (BLAS xAXPY).
 *
 * Entry point for (poor man's) multiple dispatch.
 *
 * This method first calls <tt>M_rhs->Mp_StM(mwo_lhs,alpha,trans_rhs)</tt>
 * to give the rhs argument a chance to implement the operation.  If
 * <tt>M_rhs->Mp_StM(...)</tt> returns false, then
 * <tt>mwo_lhs->Mp_StM(alpha,*this,trans_rhs)</tt>
 * is called to give the lhs matrix argument a chance to implement the method.
 * If <tt>mwo_lhs->Mp_StM(...)</tt> returns false, then
 * an attempt to perform a dynamic cast the lhs matrix argument to
 * <tt>MultiVectorMutable</tt> is attempted.  If this cast failes,
 * then an exception is thrown.
 */
void Mp_StM(
  MatrixOp* mwo_lhs, value_type alpha, const MatrixOp& M_rhs
  , BLAS_Cpp::Transp trans_rhs);

/** \brief mwo_lhs += alpha * op(M_rhs) * op(P_rhs).
 *
 * Entry point for (poor man's) multiple dispatch.
 * 
 * ToDo: Finish documentation!
 */
void Mp_StMtP(
  MatrixOp* mwo_lhs, value_type alpha
  ,const MatrixOp& M_rhs, BLAS_Cpp::Transp M_trans
  ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
  );

/** \brief mwo_lhs += alpha * op(P) * op(M_rhs).
 *
 * Entry point for (poor man's) multiple dispatch.
 * 
 * ToDo: Finish documentation!
 */
void Mp_StPtM(
  MatrixOp* mwo_lhs, value_type alpha
  ,const GenPermMatrixSlice& P_rhs, BLAS_Cpp::Transp P_rhs_trans
  ,const MatrixOp& M_rhs, BLAS_Cpp::Transp M_trans
  );

/** \brief mwo_lhs += alpha * op(P_rhs1) * op(M_rhs) * op(P_rhs2).
 *
 * Entry point for (poor man's) multiple dispatch.
 * 
 * ToDo: Finish documentation!
 */
void Mp_StPtMtP(
  MatrixOp* mwo_lhs, value_type alpha
  ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
  ,const MatrixOp& M_rhs, BLAS_Cpp::Transp trans_rhs
  ,const GenPermMatrixSlice& P_rhs2, BLAS_Cpp::Transp P_rhs2_trans
  );

//		end Level-1 BLAS
//@}

/** @name Level-2 BLAS */
//@{

/// v_lhs = alpha * op(M_rhs1) * v_rhs2 + beta * v_lhs (BLAS xGEMV)
inline void Vp_StMtV(
  VectorMutable* v_lhs, value_type alpha, const MatrixOp& M_rhs1
  ,BLAS_Cpp::Transp trans_rhs1, const Vector& v_rhs2, value_type beta = 1.0
  )
{
  M_rhs1.Vp_StMtV(v_lhs,alpha,trans_rhs1,v_rhs2,beta);
}

/// v_lhs = alpha * op(M_rhs1) * sv_rhs2 + beta * v_lhs (BLAS xGEMV)
inline void Vp_StMtV(
  VectorMutable* v_lhs, value_type alpha, const MatrixOp& M_rhs1
  ,BLAS_Cpp::Transp trans_rhs1, const SpVectorSlice& sv_rhs2, value_type beta = 1.0
  )
{
  M_rhs1.Vp_StMtV(v_lhs,alpha,trans_rhs1,sv_rhs2,beta);
}

/// v_lhs = alpha * op(P_rhs1) * op(M_rhs2) * v_rhs3 + beta * v_rhs
inline void Vp_StPtMtV(
  VectorMutable* v_lhs, value_type alpha
  ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
  ,const MatrixOp& M_rhs2, BLAS_Cpp::Transp M_rhs2_trans
  ,const Vector& v_rhs3, value_type beta = 1.0
  ) 
{
  M_rhs2.Vp_StPtMtV(v_lhs,alpha,P_rhs1,P_rhs1_trans,M_rhs2_trans,v_rhs3,beta);
}

/// v_lhs = alpha * op(P_rhs1) * op(M_rhs2) * sv_rhs3 + beta * v_rhs
inline void Vp_StPtMtV(
  VectorMutable* v_lhs, value_type alpha
  ,const GenPermMatrixSlice& P_rhs1, BLAS_Cpp::Transp P_rhs1_trans
  ,const MatrixOp& M_rhs2, BLAS_Cpp::Transp M_rhs2_trans
  ,const SpVectorSlice& sv_rhs3, value_type beta = 1.0
  )
{
  M_rhs2.Vp_StPtMtV(v_lhs,alpha,P_rhs1,P_rhs1_trans,M_rhs2_trans,sv_rhs3,beta);
}

/// result = v_rhs1' * op(M_rhs2) * v_rhs3
inline value_type transVtMtV(
  const Vector& v_rhs1, const MatrixOp& M_rhs2
  ,BLAS_Cpp::Transp trans_rhs2, const Vector& v_rhs3
  )
{
  return M_rhs2.transVtMtV(v_rhs1,trans_rhs2,v_rhs3);
}

/// result = sv_rhs1' * op(M_rhs2) * sv_rhs3
inline value_type transVtMtV(
  const SpVectorSlice& sv_rhs1, const MatrixOp& M_rhs2
  ,BLAS_Cpp::Transp trans_rhs2, const SpVectorSlice& sv_rhs3
  )
{
  return M_rhs2.transVtMtV(sv_rhs1,trans_rhs2,sv_rhs3);
}

/// <tt>symwo_lhs += alpha*op(P1')*op(M)*op(P2) + alpha*op(P2')*op(M')*op(P1) + beta*symwo_lhs</tt>
inline void syr2k(
  const MatrixOp& M, BLAS_Cpp::Transp M_trans, value_type alpha
  ,const GenPermMatrixSlice& P1, BLAS_Cpp::Transp P1_trans
  ,const GenPermMatrixSlice& P2, BLAS_Cpp::Transp P2_trans
  ,value_type beta, MatrixSymOp* symwo_lhs
  )
{
  M.syr2k(M_trans,alpha,P1,P1_trans,P2,P2_trans,beta,symwo_lhs);
}

//		end Level-2 BLAS
//@}

/** @name Level-3 BLAS */
//@{

/** \brief mwo_lhs = alpha * op(mwo_rhs1) * op(mwo_rhs2) + beta * mwo_lhs (right) (xGEMM).
 *
 * This method first calls <tt>mwo_rhs1.Mp_StMtM(...)</tt> to perform the opeation.
 * If <tt>mwo_rhs1.Mp_StMtM(...)</tt> returns false, then <tt>mwo_rhs2.Mp_StMtM(...)</tt>
 * is called.  If <tt>mwo_rhs2.Mp_StMtM(...)</tt> returns false, then
 * <tt>mwo_lhs.Mp_StMtM(...)</tt> is called.
 *
 * As a last resort, the function
 * attempts to cast <tt>dynamic_cast<MultiVectorMutable*>(mwo_lhs)</tt>.
 * If this dynamic cast fails, the this function throws an exception.
 * Otherwise, the operation is implemented in terms of <tt>Vp_StMtV()</tt>.
 */
void Mp_StMtM(
  MatrixOp* mwo_lhs, value_type alpha
  ,const MatrixOp& mwo_rhs1, BLAS_Cpp::Transp trans_rhs1
  ,const MatrixOp& mwo_rhs2, BLAS_Cpp::Transp trans_rhs2
  ,value_type beta = 1.0
  );

/** \brief Perform a rank-k update of a symmetric matrix of the form:
 *
 * <tt>symwo_lhs += alpha*op(mwo_rhs)*op(mwo_rhs') + beta*symwo_lhs</tt>
 *
 * The default implementation returns <tt>false</tt> and does nothing.
 */
void syrk(
  const MatrixOp  &mwo_rhs
  ,BLAS_Cpp::Transp   M_trans
  ,value_type         alpha
  ,value_type         beta
  ,MatrixSymOp    *sym_lhs
  );

//		end Level-3 BLAS
//@}

//		end non-member functions
//@}

// //////////////////////////////////////////////////
// Inlined methods for MatrixOp

inline
MatrixOp::mat_ptr_t
MatrixOp::sub_view(
  const index_type& rl, const index_type& ru
  ,const index_type& cl, const index_type& cu
  ) const
{
  return this->sub_view(Range1D(rl,ru),Range1D(cl,cu));
}

}	// end namespace AbstractLinAlgPack

#endif	// ABSTRACT_LINALG_PACK_MATRIX_WITH_OP_H