1 //=================================================================================================
2 /*!
3 //  \file blaze/math/expressions/DMatDMatSubExpr.h
4 //  \brief Header file for the dense matrix/dense matrix subtraction expression
5 //
6 //  Copyright (C) 2012-2020 Klaus Iglberger - All Rights Reserved
7 //
8 //  This file is part of the Blaze library. You can redistribute it and/or modify it under
9 //  the terms of the New (Revised) BSD License. Redistribution and use in source and binary
10 //  forms, with or without modification, are permitted provided that the following conditions
11 //  are met:
12 //
13 //  1. Redistributions of source code must retain the above copyright notice, this list of
14 //     conditions and the following disclaimer.
15 //  2. Redistributions in binary form must reproduce the above copyright notice, this list
16 //     of conditions and the following disclaimer in the documentation and/or other materials
17 //     provided with the distribution.
18 //  3. Neither the names of the Blaze development group nor the names of its contributors
19 //     may be used to endorse or promote products derived from this software without specific
20 //     prior written permission.
21 //
22 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 //  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 //  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 //  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 //  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 //  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 //  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 //  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 //  DAMAGE.
32 */
33 //=================================================================================================
34 
35 #ifndef _BLAZE_MATH_EXPRESSIONS_DMATDMATSUBEXPR_H_
36 #define _BLAZE_MATH_EXPRESSIONS_DMATDMATSUBEXPR_H_
37 
38 
39 //*************************************************************************************************
40 // Includes
41 //*************************************************************************************************
42 
43 #include <iterator>
44 #include <utility>
45 #include <blaze/math/Aliases.h>
46 #include <blaze/math/constraints/DenseMatrix.h>
47 #include <blaze/math/constraints/MatMatSubExpr.h>
48 #include <blaze/math/constraints/RequiresEvaluation.h>
49 #include <blaze/math/constraints/StorageOrder.h>
50 #include <blaze/math/Exception.h>
51 #include <blaze/math/expressions/Computation.h>
52 #include <blaze/math/expressions/DenseMatrix.h>
53 #include <blaze/math/expressions/Forward.h>
54 #include <blaze/math/expressions/MatMatSubExpr.h>
55 #include <blaze/math/shims/Serial.h>
56 #include <blaze/math/SIMD.h>
57 #include <blaze/math/traits/SubTrait.h>
58 #include <blaze/math/typetraits/HasSIMDSub.h>
59 #include <blaze/math/typetraits/IsAligned.h>
60 #include <blaze/math/typetraits/IsExpression.h>
61 #include <blaze/math/typetraits/IsOperation.h>
62 #include <blaze/math/typetraits/IsPadded.h>
63 #include <blaze/math/typetraits/IsTemporary.h>
64 #include <blaze/math/typetraits/RequiresEvaluation.h>
65 #include <blaze/system/HostDevice.h>
66 #include <blaze/system/Inline.h>
67 #include <blaze/system/Thresholds.h>
68 #include <blaze/util/Assert.h>
69 #include <blaze/util/EnableIf.h>
70 #include <blaze/util/FunctionTrace.h>
71 #include <blaze/util/IntegralConstant.h>
72 #include <blaze/util/mpl/If.h>
73 #include <blaze/util/Types.h>
74 
75 
76 namespace blaze {
77 
78 //=================================================================================================
79 //
80 //  CLASS DMATDMATSUBEXPR
81 //
82 //=================================================================================================
83 
84 //*************************************************************************************************
85 /*!\brief Expression object for dense matrix-dense matrix subtractions.
86 // \ingroup dense_matrix_expression
87 //
88 // The DMatDMatSubExpr class represents the compile time expression for subtractions between
89 // dense matrices with identical storage order.
90 */
91 template< typename MT1  // Type of the left-hand side dense matrix
92         , typename MT2  // Type of the right-hand side dense matrix
93         , bool SO >     // Storage order
94 class DMatDMatSubExpr
95    : public MatMatSubExpr< DenseMatrix< DMatDMatSubExpr<MT1,MT2,SO>, SO > >
96    , private Computation
97 {
98  private:
99    //**Type definitions****************************************************************************
100    using RT1 = ResultType_t<MT1>;     //!< Result type of the left-hand side dense matrix expression.
101    using RT2 = ResultType_t<MT2>;     //!< Result type of the right-hand side dense matrix expression.
102    using RN1 = ReturnType_t<MT1>;     //!< Return type of the left-hand side dense matrix expression.
103    using RN2 = ReturnType_t<MT2>;     //!< Return type of the right-hand side dense matrix expression.
104    using CT1 = CompositeType_t<MT1>;  //!< Composite type of the left-hand side dense matrix expression.
105    using CT2 = CompositeType_t<MT2>;  //!< Composite type of the right-hand side dense matrix expression.
106    using ET1 = ElementType_t<MT1>;    //!< Element type of the left-hand side dense matrix expression.
107    using ET2 = ElementType_t<MT2>;    //!< Element type of the right-hand side dense matrix expression.
108    //**********************************************************************************************
109 
110    //**Return type evaluation**********************************************************************
111    //! Compilation switch for the selection of the subscript operator return type.
112    /*! The \a returnExpr compile time constant expression is a compilation switch for the
113        selection of the \a ReturnType. If either matrix operand returns a temporary vector
114        or matrix, \a returnExpr will be set to \a false and the subscript operator will
115        return it's result by value. Otherwise \a returnExpr will be set to \a true and
116        the subscript operator may return it's result as an expression. */
117    static constexpr bool returnExpr = ( !IsTemporary_v<RN1> && !IsTemporary_v<RN2> );
118 
119    //! Expression return type for the subscript operator.
120    using ExprReturnType = decltype( std::declval<RN1>() - std::declval<RN2>() );
121    //**********************************************************************************************
122 
123    //**Serial evaluation strategy******************************************************************
124    //! Compilation switch for the serial evaluation strategy of the subtraction expression.
125    /*! The \a useAssign compile time constant expression represents a compilation switch for the
126        serial evaluation strategy of the subtraction expression. In case either of the two dense
127        matrix operands requires an intermediate evaluation or the subscript operator can only
128        return by value, \a useAssign will be set to 1 and the subtraction expression will be
129        evaluated via the \a assign function family. Otherwise \a useAssign will be set to 0 and
130        the expression will be evaluated via the function call operator. */
131    static constexpr bool useAssign =
132       ( RequiresEvaluation_v<MT1> || RequiresEvaluation_v<MT2> || !returnExpr );
133 
134    /*! \cond BLAZE_INTERNAL */
135    //! Helper variable template for the explicit application of the SFINAE principle.
136    template< typename MT >
137    static constexpr bool UseAssign_v = useAssign;
138    /*! \endcond */
139    //**********************************************************************************************
140 
141    //**Parallel evaluation strategy****************************************************************
142    /*! \cond BLAZE_INTERNAL */
143    //! Helper variable template for the explicit application of the SFINAE principle.
144    /*! This variable template is a helper for the selection of the parallel evaluation strategy.
145        In case at least one of the two matrix operands is not SMP assignable and at least one of
146        the two operands requires an intermediate evaluation, the variable is set to 1 and the
147        expression specific evaluation strategy is selected. Otherwise the variable is set to 0
148        and the default strategy is chosen. */
149    template< typename MT >
150    static constexpr bool UseSMPAssign_v =
151       ( ( !MT1::smpAssignable || !MT2::smpAssignable ) && useAssign );
152    /*! \endcond */
153    //**********************************************************************************************
154 
155  public:
156    //**Type definitions****************************************************************************
157    //! Type of this DMatDMatSubExpr instance.
158    using This = DMatDMatSubExpr<MT1,MT2,SO>;
159 
160    //! Base type of this DMatDMatSubExpr instance.
161    using BaseType = MatMatSubExpr< DenseMatrix<This,SO> >;
162 
163    using ResultType    = SubTrait_t<RT1,RT2>;          //!< Result type for expression template evaluations.
164    using OppositeType  = OppositeType_t<ResultType>;   //!< Result type with opposite storage order for expression template evaluations.
165    using TransposeType = TransposeType_t<ResultType>;  //!< Transpose type for expression template evaluations.
166    using ElementType   = ElementType_t<ResultType>;    //!< Resulting element type.
167 
168    //! Return type for expression template evaluations.
169    using ReturnType = const If_t< returnExpr, ExprReturnType, ElementType >;
170 
171    //! Data type for composite expression templates.
172    using CompositeType = If_t< useAssign, const ResultType, const DMatDMatSubExpr& >;
173 
174    //! Composite type of the left-hand side dense matrix expression.
175    using LeftOperand = If_t< IsExpression_v<MT1>, const MT1, const MT1& >;
176 
177    //! Composite type of the right-hand side dense matrix expression.
178    using RightOperand = If_t< IsExpression_v<MT2>, const MT2, const MT2& >;
179    //**********************************************************************************************
180 
181    //**ConstIterator class definition**************************************************************
182    /*!\brief Iterator over the elements of the dense matrix.
183    */
184    class ConstIterator
185    {
186     public:
187       //**Type definitions*************************************************************************
188       using IteratorCategory = std::random_access_iterator_tag;  //!< The iterator category.
189       using ValueType        = ElementType;                      //!< Type of the underlying elements.
190       using PointerType      = ElementType*;                     //!< Pointer return type.
191       using ReferenceType    = ElementType&;                     //!< Reference return type.
192       using DifferenceType   = ptrdiff_t;                        //!< Difference between two iterators.
193 
194       // STL iterator requirements
195       using iterator_category = IteratorCategory;  //!< The iterator category.
196       using value_type        = ValueType;         //!< Type of the underlying elements.
197       using pointer           = PointerType;       //!< Pointer return type.
198       using reference         = ReferenceType;     //!< Reference return type.
199       using difference_type   = DifferenceType;    //!< Difference between two iterators.
200 
201       //! ConstIterator type of the left-hand side dense matrix expression.
202       using LeftIteratorType = ConstIterator_t<MT1>;
203 
204       //! ConstIterator type of the right-hand side dense matrix expression.
205       using RightIteratorType = ConstIterator_t<MT2>;
206       //*******************************************************************************************
207 
208       //**Constructor******************************************************************************
209       /*!\brief Constructor for the ConstIterator class.
210       //
211       // \param left Iterator to the initial left-hand side element.
212       // \param right Iterator to the initial right-hand side element.
213       */
ConstIterator(LeftIteratorType left,RightIteratorType right)214       inline BLAZE_DEVICE_CALLABLE ConstIterator( LeftIteratorType left, RightIteratorType right )
215          : left_ ( left  )  // Iterator to the current left-hand side element
216          , right_( right )  // Iterator to the current right-hand side element
217       {}
218       //*******************************************************************************************
219 
220       //**Addition assignment operator*************************************************************
221       /*!\brief Addition assignment operator.
222       //
223       // \param inc The increment of the iterator.
224       // \return The incremented iterator.
225       */
226       inline BLAZE_DEVICE_CALLABLE ConstIterator& operator+=( size_t inc ) {
227          left_  += inc;
228          right_ += inc;
229          return *this;
230       }
231       //*******************************************************************************************
232 
233       //**Subtraction assignment operator**********************************************************
234       /*!\brief Subtraction assignment operator.
235       //
236       // \param dec The decrement of the iterator.
237       // \return The decremented iterator.
238       */
239       inline BLAZE_DEVICE_CALLABLE ConstIterator& operator-=( size_t dec ) {
240          left_  -= dec;
241          right_ -= dec;
242          return *this;
243       }
244       //*******************************************************************************************
245 
246       //**Prefix increment operator****************************************************************
247       /*!\brief Pre-increment operator.
248       //
249       // \return Reference to the incremented iterator.
250       */
251       inline BLAZE_DEVICE_CALLABLE ConstIterator& operator++() {
252          ++left_;
253          ++right_;
254          return *this;
255       }
256       //*******************************************************************************************
257 
258       //**Postfix increment operator***************************************************************
259       /*!\brief Post-increment operator.
260       //
261       // \return The previous position of the iterator.
262       */
263       inline BLAZE_DEVICE_CALLABLE const ConstIterator operator++( int ) {
264          return ConstIterator( left_++, right_++ );
265       }
266       //*******************************************************************************************
267 
268       //**Prefix decrement operator****************************************************************
269       /*!\brief Pre-decrement operator.
270       //
271       // \return Reference to the decremented iterator.
272       */
273       inline BLAZE_DEVICE_CALLABLE ConstIterator& operator--() {
274          --left_;
275          --right_;
276          return *this;
277       }
278       //*******************************************************************************************
279 
280       //**Postfix decrement operator***************************************************************
281       /*!\brief Post-decrement operator.
282       //
283       // \return The previous position of the iterator.
284       */
285       inline BLAZE_DEVICE_CALLABLE const ConstIterator operator--( int ) {
286          return ConstIterator( left_--, right_-- );
287       }
288       //*******************************************************************************************
289 
290       //**Element access operator******************************************************************
291       /*!\brief Direct access to the element at the current iterator position.
292       //
293       // \return The resulting value.
294       */
295       inline ReturnType operator*() const {
296          return (*left_) - (*right_);
297       }
298       //*******************************************************************************************
299 
300       //**Load function****************************************************************************
301       /*!\brief Access to the SIMD elements of the matrix.
302       //
303       // \return The resulting SIMD element.
304       */
load()305       inline auto load() const noexcept {
306          return left_.load() - right_.load();
307       }
308       //*******************************************************************************************
309 
310       //**Equality operator************************************************************************
311       /*!\brief Equality comparison between two ConstIterator objects.
312       //
313       // \param rhs The right-hand side iterator.
314       // \return \a true if the iterators refer to the same element, \a false if not.
315       */
316       inline BLAZE_DEVICE_CALLABLE bool operator==( const ConstIterator& rhs ) const {
317          return left_ == rhs.left_;
318       }
319       //*******************************************************************************************
320 
321       //**Inequality operator**********************************************************************
322       /*!\brief Inequality comparison between two ConstIterator objects.
323       //
324       // \param rhs The right-hand side iterator.
325       // \return \a true if the iterators don't refer to the same element, \a false if they do.
326       */
327       inline BLAZE_DEVICE_CALLABLE bool operator!=( const ConstIterator& rhs ) const {
328          return left_ != rhs.left_;
329       }
330       //*******************************************************************************************
331 
332       //**Less-than operator***********************************************************************
333       /*!\brief Less-than comparison between two ConstIterator objects.
334       //
335       // \param rhs The right-hand side iterator.
336       // \return \a true if the left-hand side iterator is smaller, \a false if not.
337       */
338       inline BLAZE_DEVICE_CALLABLE bool operator<( const ConstIterator& rhs ) const {
339          return left_ < rhs.left_;
340       }
341       //*******************************************************************************************
342 
343       //**Greater-than operator********************************************************************
344       /*!\brief Greater-than comparison between two ConstIterator objects.
345       //
346       // \param rhs The right-hand side iterator.
347       // \return \a true if the left-hand side iterator is greater, \a false if not.
348       */
349       inline BLAZE_DEVICE_CALLABLE bool operator>( const ConstIterator& rhs ) const {
350          return left_ > rhs.left_;
351       }
352       //*******************************************************************************************
353 
354       //**Less-or-equal-than operator**************************************************************
355       /*!\brief Less-than comparison between two ConstIterator objects.
356       //
357       // \param rhs The right-hand side iterator.
358       // \return \a true if the left-hand side iterator is smaller or equal, \a false if not.
359       */
360       inline BLAZE_DEVICE_CALLABLE bool operator<=( const ConstIterator& rhs ) const {
361          return left_ <= rhs.left_;
362       }
363       //*******************************************************************************************
364 
365       //**Greater-or-equal-than operator***********************************************************
366       /*!\brief Greater-than comparison between two ConstIterator objects.
367       //
368       // \param rhs The right-hand side iterator.
369       // \return \a true if the left-hand side iterator is greater or equal, \a false if not.
370       */
371       inline BLAZE_DEVICE_CALLABLE bool operator>=( const ConstIterator& rhs ) const {
372          return left_ >= rhs.left_;
373       }
374       //*******************************************************************************************
375 
376       //**Subtraction operator*********************************************************************
377       /*!\brief Calculating the number of elements between two iterators.
378       //
379       // \param rhs The right-hand side iterator.
380       // \return The number of elements between the two iterators.
381       */
382       inline BLAZE_DEVICE_CALLABLE DifferenceType operator-( const ConstIterator& rhs ) const {
383          return left_ - rhs.left_;
384       }
385       //*******************************************************************************************
386 
387       //**Addition operator************************************************************************
388       /*!\brief Addition between a ConstIterator and an integral value.
389       //
390       // \param it The iterator to be incremented.
391       // \param inc The number of elements the iterator is incremented.
392       // \return The incremented iterator.
393       */
394       friend inline const ConstIterator operator+( const ConstIterator& it, size_t inc ) {
395          return ConstIterator( it.left_ + inc, it.right_ + inc );
396       }
397       //*******************************************************************************************
398 
399       //**Addition operator************************************************************************
400       /*!\brief Addition between an integral value and a ConstIterator.
401       //
402       // \param inc The number of elements the iterator is incremented.
403       // \param it The iterator to be incremented.
404       // \return The incremented iterator.
405       */
406       friend inline const ConstIterator operator+( size_t inc, const ConstIterator& it ) {
407          return ConstIterator( it.left_ + inc, it.right_ + inc );
408       }
409       //*******************************************************************************************
410 
411       //**Subtraction operator*********************************************************************
412       /*!\brief Subtraction between a ConstIterator and an integral value.
413       //
414       // \param it The iterator to be decremented.
415       // \param dec The number of elements the iterator is decremented.
416       // \return The decremented iterator.
417       */
418       friend inline BLAZE_DEVICE_CALLABLE const ConstIterator operator-( const ConstIterator& it, size_t dec ) {
419          return ConstIterator( it.left_ - dec, it.right_ - dec );
420       }
421       //*******************************************************************************************
422 
423     private:
424       //**Member variables*************************************************************************
425       LeftIteratorType  left_;   //!< Iterator to the current left-hand side element.
426       RightIteratorType right_;  //!< Iterator to the current right-hand side element.
427       //*******************************************************************************************
428    };
429    //**********************************************************************************************
430 
431    //**Compilation flags***************************************************************************
432    //! Compilation switch for the expression template evaluation strategy.
433    static constexpr bool simdEnabled =
434       ( MT1::simdEnabled && MT2::simdEnabled && HasSIMDSub_v<ET1,ET2> );
435 
436    //! Compilation switch for the expression template assignment strategy.
437    static constexpr bool smpAssignable = ( MT1::smpAssignable && MT2::smpAssignable );
438    //**********************************************************************************************
439 
440    //**SIMD properties*****************************************************************************
441    //! The number of elements packed within a single SIMD element.
442    static constexpr size_t SIMDSIZE = SIMDTrait<ElementType>::size;
443    //**********************************************************************************************
444 
445    //**Constructor*********************************************************************************
446    /*!\brief Constructor for the DMatDMatSubExpr class.
447    //
448    // \param lhs The left-hand side operand of the subtraction expression.
449    // \param rhs The right-hand side operand of the subtraction expression.
450    */
DMatDMatSubExpr(const MT1 & lhs,const MT2 & rhs)451    inline DMatDMatSubExpr( const MT1& lhs, const MT2& rhs ) noexcept
452       : lhs_( lhs )  // Left-hand side dense matrix of the subtraction expression
453       , rhs_( rhs )  // Right-hand side dense matrix of the subtraction expression
454    {
455       BLAZE_INTERNAL_ASSERT( lhs.rows()    == rhs.rows()   , "Invalid number of rows"    );
456       BLAZE_INTERNAL_ASSERT( lhs.columns() == rhs.columns(), "Invalid number of columns" );
457    }
458    //**********************************************************************************************
459 
460    //**Access operator*****************************************************************************
461    /*!\brief 2D-access to the matrix elements.
462    //
463    // \param i Access index for the row. The index has to be in the range \f$[0..M-1]\f$.
464    // \param j Access index for the column. The index has to be in the range \f$[0..N-1]\f$.
465    // \return The resulting value.
466    */
operator()467    inline ReturnType operator()( size_t i, size_t j ) const {
468       BLAZE_INTERNAL_ASSERT( i < lhs_.rows()   , "Invalid row access index"    );
469       BLAZE_INTERNAL_ASSERT( j < lhs_.columns(), "Invalid column access index" );
470       return lhs_(i,j) - rhs_(i,j);
471    }
472    //**********************************************************************************************
473 
474    //**At function*********************************************************************************
475    /*!\brief Checked access to the matrix elements.
476    //
477    // \param i Access index for the row. The index has to be in the range \f$[0..M-1]\f$.
478    // \param j Access index for the column. The index has to be in the range \f$[0..N-1]\f$.
479    // \return The resulting value.
480    // \exception std::out_of_range Invalid matrix access index.
481    */
at(size_t i,size_t j)482    inline ReturnType at( size_t i, size_t j ) const {
483       if( i >= lhs_.rows() ) {
484          BLAZE_THROW_OUT_OF_RANGE( "Invalid row access index" );
485       }
486       if( j >= lhs_.columns() ) {
487          BLAZE_THROW_OUT_OF_RANGE( "Invalid column access index" );
488       }
489       return (*this)(i,j);
490    }
491    //**********************************************************************************************
492 
493    //**Load function*******************************************************************************
494    /*!\brief Access to the SIMD elements of the matrix.
495    //
496    // \param i Access index for the row. The index has to be in the range \f$[0..M-1]\f$.
497    // \param j Access index for the column. The index has to be in the range \f$[0..N-1]\f$.
498    // \return Reference to the accessed values.
499    */
load(size_t i,size_t j)500    BLAZE_ALWAYS_INLINE auto load( size_t i, size_t j ) const noexcept {
501       BLAZE_INTERNAL_ASSERT( i < lhs_.rows()   , "Invalid row access index"    );
502       BLAZE_INTERNAL_ASSERT( j < lhs_.columns(), "Invalid column access index" );
503       BLAZE_INTERNAL_ASSERT( !SO || ( i % SIMDSIZE == 0UL ), "Invalid row access index"    );
504       BLAZE_INTERNAL_ASSERT( SO  || ( j % SIMDSIZE == 0UL ), "Invalid column access index" );
505       return lhs_.load(i,j) - rhs_.load(i,j);
506    }
507    //**********************************************************************************************
508 
509    //**Begin function******************************************************************************
510    /*!\brief Returns an iterator to the first non-zero element of row/column \a i.
511    //
512    // \param i The row/column index.
513    // \return Iterator to the first non-zero element of row/column \a i.
514    */
begin(size_t i)515    inline ConstIterator begin( size_t i ) const {
516       return ConstIterator( lhs_.begin(i), rhs_.begin(i) );
517    }
518    //**********************************************************************************************
519 
520    //**End function********************************************************************************
521    /*!\brief Returns an iterator just past the last non-zero element of row/column \a i.
522    //
523    // \param i The row/column index.
524    // \return Iterator just past the last non-zero element of row/column \a i.
525    */
end(size_t i)526    inline ConstIterator end( size_t i ) const {
527       return ConstIterator( lhs_.end(i), rhs_.end(i) );
528    }
529    //**********************************************************************************************
530 
531    //**Rows function*******************************************************************************
532    /*!\brief Returns the current number of rows of the matrix.
533    //
534    // \return The number of rows of the matrix.
535    */
rows()536    inline size_t rows() const noexcept {
537       return lhs_.rows();
538    }
539    //**********************************************************************************************
540 
541    //**Columns function****************************************************************************
542    /*!\brief Returns the current number of columns of the matrix.
543    //
544    // \return The number of columns of the matrix.
545    */
columns()546    inline size_t columns() const noexcept {
547       return lhs_.columns();
548    }
549    //**********************************************************************************************
550 
551    //**Left operand access*************************************************************************
552    /*!\brief Returns the left-hand side dense matrix operand.
553    //
554    // \return The left-hand side dense matrix operand.
555    */
leftOperand()556    inline LeftOperand leftOperand() const noexcept {
557       return lhs_;
558    }
559    //**********************************************************************************************
560 
561    //**Right operand access************************************************************************
562    /*!\brief Returns the right-hand side dense matrix operand.
563    //
564    // \return The right-hand side dense matrix operand.
565    */
rightOperand()566    inline RightOperand rightOperand() const noexcept {
567       return rhs_;
568    }
569    //**********************************************************************************************
570 
571    //**********************************************************************************************
572    /*!\brief Returns whether the expression can alias with the given address \a alias.
573    //
574    // \param alias The alias to be checked.
575    // \return \a true in case the expression can alias, \a false otherwise.
576    */
577    template< typename T >
canAlias(const T * alias)578    inline bool canAlias( const T* alias ) const noexcept {
579       return ( IsExpression_v<MT1> && ( RequiresEvaluation_v<MT1> ? lhs_.isAliased( alias ) : lhs_.canAlias( alias ) ) ) ||
580              ( IsExpression_v<MT2> && ( RequiresEvaluation_v<MT2> ? rhs_.isAliased( alias ) : rhs_.canAlias( alias ) ) );
581    }
582    //**********************************************************************************************
583 
584    //**********************************************************************************************
585    /*!\brief Returns whether the expression is aliased with the given address \a alias.
586    //
587    // \param alias The alias to be checked.
588    // \return \a true in case an alias effect is detected, \a false otherwise.
589    */
590    template< typename T >
isAliased(const T * alias)591    inline bool isAliased( const T* alias ) const noexcept {
592       return ( lhs_.isAliased( alias ) || rhs_.isAliased( alias ) );
593    }
594    //**********************************************************************************************
595 
596    //**********************************************************************************************
597    /*!\brief Returns whether the operands of the expression are properly aligned in memory.
598    //
599    // \return \a true in case the operands are aligned, \a false if not.
600    */
isAligned()601    inline bool isAligned() const noexcept {
602       return lhs_.isAligned() && rhs_.isAligned();
603    }
604    //**********************************************************************************************
605 
606    //**********************************************************************************************
607    /*!\brief Returns whether the expression can be used in SMP assignments.
608    //
609    // \return \a true in case the expression can be used in SMP assignments, \a false if not.
610    */
canSMPAssign()611    inline bool canSMPAssign() const noexcept {
612       return lhs_.canSMPAssign() || rhs_.canSMPAssign() ||
613              ( rows() * columns() >= SMP_DMATDMATSUB_THRESHOLD );
614    }
615    //**********************************************************************************************
616 
617  private:
618    //**Member variables****************************************************************************
619    LeftOperand  lhs_;  //!< Left-hand side dense matrix of the subtraction expression.
620    RightOperand rhs_;  //!< Right-hand side dense matrix of the subtraction expression.
621    //**********************************************************************************************
622 
623    //**Assignment to dense matrices****************************************************************
624    /*! \cond BLAZE_INTERNAL */
625    /*!\brief Assignment of a dense matrix-dense matrix subtraction to a dense matrix.
626    // \ingroup dense_matrix
627    //
628    // \param lhs The target left-hand side dense matrix.
629    // \param rhs The right-hand side subtraction expression to be assigned.
630    // \return void
631    //
632    // This function implements the performance optimized assignment of a dense matrix-dense
633    // matrix subtraction expression to a dense matrix. Due to the explicit application of the
634    // SFINAE principle, this function can only be selected by the compiler in case either
635    // of the two operands requires an intermediate evaluation.
636    */
637    template< typename MT  // Type of the target dense matrix
638            , bool SO2 >   // Storage order of the target dense matrix
639    friend inline auto assign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
640       -> EnableIf_t< UseAssign_v<MT> >
641    {
642       BLAZE_FUNCTION_TRACE;
643 
644       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
645       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
646 
647       if( !IsOperation_v<MT1> && isSame( *lhs, rhs.lhs_ ) ) {
648          subAssign( *lhs, rhs.rhs_ );
649       }
650       else if( ( !IsOperation_v<MT2> && isSame( *lhs, rhs.rhs_ ) ) ||
651                ( !RequiresEvaluation_v<MT2> && rhs.rhs_.isAliased( &(*lhs) ) ) ) {
652          assign   ( *lhs, -rhs.rhs_ );
653          addAssign( *lhs,  rhs.lhs_ );
654       }
655       else {
656          assign   ( *lhs, rhs.lhs_ );
657          subAssign( *lhs, rhs.rhs_ );
658       }
659    }
660    /*! \endcond */
661    //**********************************************************************************************
662 
663    //**Assignment to sparse matrices***************************************************************
664    /*! \cond BLAZE_INTERNAL */
665    /*!\brief Assignment of a dense matrix-dense matrix subtraction to a sparse matrix.
666    // \ingroup dense_matrix
667    //
668    // \param lhs The target left-hand side sparse matrix.
669    // \param rhs The right-hand side subtraction expression to be assigned.
670    // \return void
671    //
672    // This function implements the performance optimized assignment of a dense matrix-dense
673    // matrix subtraction expression to a sparse matrix. Due to the explicit application of the
674    // SFINAE principle, this function can only be selected by the compiler in case either
675    // of the two operands requires an intermediate evaluation.
676    */
677    template< typename MT  // Type of the target sparse matrix
678            , bool SO2 >   // Storage order of the target sparse matrix
679    friend inline auto assign( SparseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
680       -> EnableIf_t< UseAssign_v<MT> >
681    {
682       BLAZE_FUNCTION_TRACE;
683 
684       using TmpType = If_t< SO == SO2, ResultType, OppositeType >;
685 
686       BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( ResultType );
687       BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( OppositeType );
688       BLAZE_CONSTRAINT_MUST_BE_MATRIX_WITH_STORAGE_ORDER( ResultType, SO );
689       BLAZE_CONSTRAINT_MUST_BE_MATRIX_WITH_STORAGE_ORDER( OppositeType, !SO );
690       BLAZE_CONSTRAINT_MATRICES_MUST_HAVE_SAME_STORAGE_ORDER( MT, TmpType );
691       BLAZE_CONSTRAINT_MUST_NOT_REQUIRE_EVALUATION( TmpType );
692 
693       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
694       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
695 
696       const TmpType tmp( serial( rhs ) );
697       assign( *lhs, tmp );
698    }
699    /*! \endcond */
700    //**********************************************************************************************
701 
702    //**Addition assignment to dense matrices*******************************************************
703    /*! \cond BLAZE_INTERNAL */
704    /*!\brief Addition assignment of a dense matrix-dense matrix subtraction to a dense matrix.
705    // \ingroup dense_matrix
706    //
707    // \param lhs The target left-hand side dense matrix.
708    // \param rhs The right-hand side subtraction expression to be added.
709    // \return void
710    //
711    // This function implements the performance optimized addition assignment of a dense matrix-
712    // dense matrix subtraction expression to a dense matrix. Due to the explicit application of
713    // the SFINAE principle, this function can only be selected by the compiler in case either
714    // of the operands requires an intermediate evaluation.
715    */
716    template< typename MT  // Type of the target dense matrix
717            , bool SO2 >   // Storage order of the target dense matrix
718    friend inline auto addAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
719       -> EnableIf_t< UseAssign_v<MT> >
720    {
721       BLAZE_FUNCTION_TRACE;
722 
723       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
724       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
725 
726       if( !RequiresEvaluation_v<MT2> ) {
727          subAssign( *lhs, rhs.rhs_ );
728          addAssign( *lhs, rhs.lhs_ );
729       }
730       else {
731          addAssign( *lhs, rhs.lhs_ );
732          subAssign( *lhs, rhs.rhs_ );
733       }
734    }
735    /*! \endcond */
736    //**********************************************************************************************
737 
738    //**Addition assignment to sparse matrices******************************************************
739    // No special implementation for the addition assignment to sparse matrices.
740    //**********************************************************************************************
741 
742    //**Subtraction assignment to dense matrices****************************************************
743    /*! \cond BLAZE_INTERNAL */
744    /*!\brief Subtraction assignment of a dense matrix-dense matrix subtraction to a dense matrix.
745    // \ingroup dense_matrix
746    //
747    // \param lhs The target left-hand side dense matrix.
748    // \param rhs The right-hand side subtraction expression to be subtracted.
749    // \return void
750    //
751    // This function implements the performance optimized subtraction assignment of a dense matrix-
752    // dense matrix subtraction expression to a dense matrix. Due to the explicit application of
753    // the SFINAE principle, this function can only be selected by the compiler in case either
754    // of the operands requires an intermediate evaluation.
755    */
756    template< typename MT  // Type of the target dense matrix
757            , bool SO2 >   // Storage order of the target dense matrix
758    friend inline auto subAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
759       -> EnableIf_t< UseAssign_v<MT> >
760    {
761       BLAZE_FUNCTION_TRACE;
762 
763       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
764       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
765 
766       if( !RequiresEvaluation_v<MT2> ) {
767          addAssign( *lhs, rhs.rhs_ );
768          subAssign( *lhs, rhs.lhs_ );
769       }
770       else {
771          subAssign( *lhs, rhs.lhs_ );
772          addAssign( *lhs, rhs.rhs_ );
773       }
774    }
775    /*! \endcond */
776    //**********************************************************************************************
777 
778    //**Subtraction assignment to sparse matrices***************************************************
779    // No special implementation for the subtraction assignment to sparse matrices.
780    //**********************************************************************************************
781 
782    //**Schur product assignment to dense matrices**************************************************
783    /*! \cond BLAZE_INTERNAL */
784    /*!\brief Schur product assignment of a dense matrix-dense matrix subtraction to a dense matrix.
785    // \ingroup dense_matrix
786    //
787    // \param lhs The target left-hand side dense matrix.
788    // \param rhs The right-hand side subtraction expression for the Schur product.
789    // \return void
790    //
791    // This function implements the performance optimized Schur product assignment of a dense
792    // matrix-dense matrix subtraction expression to a dense matrix. Due to the explicit application
793    // of the SFINAE principle, this function can only be selected by the compiler in case either
794    // of the operands requires an intermediate evaluation.
795    */
796    template< typename MT  // Type of the target dense matrix
797            , bool SO2 >   // Storage order of the target dense matrix
798    friend inline auto schurAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
799       -> EnableIf_t< UseAssign_v<MT> >
800    {
801       BLAZE_FUNCTION_TRACE;
802 
803       BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( ResultType );
804       BLAZE_CONSTRAINT_MUST_BE_MATRIX_WITH_STORAGE_ORDER( ResultType, SO );
805       BLAZE_CONSTRAINT_MUST_NOT_REQUIRE_EVALUATION( ResultType );
806 
807       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
808       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
809 
810       const ResultType tmp( serial( rhs ) );
811       schurAssign( *lhs, tmp );
812    }
813    /*! \endcond */
814    //**********************************************************************************************
815 
816    //**Schur product assignment to sparse matrices*************************************************
817    // No special implementation for the Schur product assignment to sparse matrices.
818    //**********************************************************************************************
819 
820    //**Multiplication assignment to dense matrices*************************************************
821    // No special implementation for the multiplication assignment to dense matrices.
822    //**********************************************************************************************
823 
824    //**Multiplication assignment to sparse matrices************************************************
825    // No special implementation for the multiplication assignment to sparse matrices.
826    //**********************************************************************************************
827 
828    //**SMP assignment to dense matrices************************************************************
829    /*! \cond BLAZE_INTERNAL */
830    /*!\brief SMP assignment of a dense matrix-dense matrix subtraction to a dense matrix.
831    // \ingroup dense_matrix
832    //
833    // \param lhs The target left-hand side dense matrix.
834    // \param rhs The right-hand side subtraction expression to be assigned.
835    // \return void
836    //
837    // This function implements the performance optimized SMP assignment of a dense matrix-dense
838    // matrix subtraction expression to a dense matrix. Due to the explicit application of the
839    // SFINAE principle, this function can only be selected by the compiler in case the expression
840    // specific parallel evaluation strategy is selected.
841    */
842    template< typename MT  // Type of the target dense matrix
843            , bool SO2 >   // Storage order of the target dense matrix
844    friend inline auto smpAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
845       -> EnableIf_t< UseSMPAssign_v<MT> >
846    {
847       BLAZE_FUNCTION_TRACE;
848 
849       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
850       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
851 
852       if( !IsOperation_v<MT1> && isSame( *lhs, rhs.lhs_ ) ) {
853          smpSubAssign( *lhs, rhs.rhs_ );
854       }
855       else if( ( !IsOperation_v<MT2> && isSame( *lhs, rhs.rhs_ ) ) ||
856                ( !RequiresEvaluation_v<MT2> && rhs.rhs_.isAliased( &(*lhs) ) ) ) {
857          smpAssign   ( *lhs, -rhs.rhs_ );
858          smpAddAssign( *lhs,  rhs.lhs_ );
859       }
860       else {
861          smpAssign   ( *lhs, rhs.lhs_ );
862          smpSubAssign( *lhs, rhs.rhs_ );
863       }
864    }
865    /*! \endcond */
866    //**********************************************************************************************
867 
868    //**SMP assignment to sparse matrices***********************************************************
869    /*! \cond BLAZE_INTERNAL */
870    /*!\brief SMP assignment of a dense matrix-dense matrix subtraction to a sparse matrix.
871    // \ingroup dense_matrix
872    //
873    // \param lhs The target left-hand side sparse matrix.
874    // \param rhs The right-hand side subtraction expression to be assigned.
875    // \return void
876    //
877    // This function implements the performance optimized SMP assignment of a dense matrix-dense
878    // matrix subtraction expression to a sparse matrix. Due to the explicit application of the
879    // SFINAE principle, this function can only be selected by the compiler in case the expression
880    // specific parallel evaluation strategy is selected.
881    */
882    template< typename MT  // Type of the target sparse matrix
883            , bool SO2 >   // Storage order of the target sparse matrix
884    friend inline auto smpAssign( SparseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
885       -> EnableIf_t< UseSMPAssign_v<MT> >
886    {
887       BLAZE_FUNCTION_TRACE;
888 
889       using TmpType = If_t< SO == SO2, ResultType, OppositeType >;
890 
891       BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( ResultType );
892       BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( OppositeType );
893       BLAZE_CONSTRAINT_MUST_BE_MATRIX_WITH_STORAGE_ORDER( ResultType, SO );
894       BLAZE_CONSTRAINT_MUST_BE_MATRIX_WITH_STORAGE_ORDER( OppositeType, !SO );
895       BLAZE_CONSTRAINT_MATRICES_MUST_HAVE_SAME_STORAGE_ORDER( MT, TmpType );
896       BLAZE_CONSTRAINT_MUST_NOT_REQUIRE_EVALUATION( TmpType );
897 
898       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
899       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
900 
901       const TmpType tmp( rhs );
902       smpAssign( *lhs, tmp );
903    }
904    /*! \endcond */
905    //**********************************************************************************************
906 
907    //**SMP addition assignment to dense matrices***************************************************
908    /*! \cond BLAZE_INTERNAL */
909    /*!\brief SMP addition assignment of a dense matrix-dense matrix subtraction to a dense matrix.
910    // \ingroup dense_matrix
911    //
912    // \param lhs The target left-hand side dense matrix.
913    // \param rhs The right-hand side subtraction expression to be added.
914    // \return void
915    //
916    // This function implements the performance optimized SMP addition assignment of a dense
917    // matrix-dense matrix subtraction expression to a dense matrix. Due to the explicit
918    // application of the SFINAE principle, this function can only be selected by the compiler
919    // in case the expression specific parallel evaluation strategy is selected.
920    */
921    template< typename MT  // Type of the target dense matrix
922            , bool SO2 >   // Storage order of the target dense matrix
923    friend inline auto smpAddAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
924       -> EnableIf_t< UseAssign_v<MT> >
925    {
926       BLAZE_FUNCTION_TRACE;
927 
928       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
929       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
930 
931       if( !RequiresEvaluation_v<MT2> ) {
932          smpSubAssign( *lhs, rhs.rhs_ );
933          smpAddAssign( *lhs, rhs.lhs_ );
934       }
935       else {
936          smpAddAssign( *lhs, rhs.lhs_ );
937          smpSubAssign( *lhs, rhs.rhs_ );
938       }
939    }
940    /*! \endcond */
941    //**********************************************************************************************
942 
943    //**SMP addition assignment to sparse matrices**************************************************
944    // No special implementation for the SMP addition assignment to sparse matrices.
945    //**********************************************************************************************
946 
947    //**SMP subtraction assignment to dense matrices************************************************
948    /*! \cond BLAZE_INTERNAL */
949    /*!\brief SMP subtraction assignment of a dense matrix-dense matrix subtraction to a dense matrix.
950    // \ingroup dense_matrix
951    //
952    // \param lhs The target left-hand side dense matrix.
953    // \param rhs The right-hand side subtraction expression to be subtracted.
954    // \return void
955    //
956    // This function implements the performance optimized SMP subtraction assignment of a dense
957    // matrix-dense matrix subtraction expression to a dense matrix. Due to the explicit application
958    // of the SFINAE principle, this function can only be selected by the compiler in case the
959    // expression specific parallel evaluation strategy is selected.
960    */
961    template< typename MT  // Type of the target dense matrix
962            , bool SO2 >   // Storage order of the target dense matrix
963    friend inline auto smpSubAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
964       -> EnableIf_t< UseSMPAssign_v<MT> >
965    {
966       BLAZE_FUNCTION_TRACE;
967 
968       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
969       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
970 
971       if( !RequiresEvaluation_v<MT2> ) {
972          smpAddAssign( *lhs, rhs.rhs_ );
973          smpSubAssign( *lhs, rhs.lhs_ );
974       }
975       else {
976          smpSubAssign( *lhs, rhs.lhs_ );
977          smpAddAssign( *lhs, rhs.rhs_ );
978       }
979    }
980    /*! \endcond */
981    //**********************************************************************************************
982 
983    //**SMP subtraction assignment to sparse matrices***********************************************
984    // No special implementation for the SMP subtraction assignment to sparse matrices.
985    //**********************************************************************************************
986 
987    //**SMP Schur product assignment to dense matrices**********************************************
988    /*! \cond BLAZE_INTERNAL */
989    /*!\brief SMP Schur product assignment of a dense matrix-dense matrix subtraction to a dense
990    //        matrix.
991    // \ingroup dense_matrix
992    //
993    // \param lhs The target left-hand side dense matrix.
994    // \param rhs The right-hand side subtraction expression for the Schur product.
995    // \return void
996    //
997    // This function implements the performance optimized SMP Schur product assignment of a dense
998    // matrix-dense matrix subtraction expression to a dense matrix. Due to the explicit application
999    // of the SFINAE principle, this function can only be selected by the compiler in case the
1000    // expression specific parallel evaluation strategy is selected.
1001    */
1002    template< typename MT  // Type of the target dense matrix
1003            , bool SO2 >   // Storage order of the target dense matrix
1004    friend inline auto smpSchurAssign( DenseMatrix<MT,SO2>& lhs, const DMatDMatSubExpr& rhs )
1005       -> EnableIf_t< UseSMPAssign_v<MT> >
1006    {
1007       BLAZE_FUNCTION_TRACE;
1008 
1009       BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( ResultType );
1010       BLAZE_CONSTRAINT_MUST_BE_MATRIX_WITH_STORAGE_ORDER( ResultType, SO );
1011       BLAZE_CONSTRAINT_MUST_NOT_REQUIRE_EVALUATION( ResultType );
1012 
1013       BLAZE_INTERNAL_ASSERT( (*lhs).rows()    == rhs.rows()   , "Invalid number of rows"    );
1014       BLAZE_INTERNAL_ASSERT( (*lhs).columns() == rhs.columns(), "Invalid number of columns" );
1015 
1016       const ResultType tmp( rhs );
1017       smpSchurAssign( *lhs, tmp );
1018    }
1019    /*! \endcond */
1020    //**********************************************************************************************
1021 
1022    //**SMP Schur product assignment to sparse matrices*********************************************
1023    // No special implementation for the SMP Schur product assignment to sparse matrices.
1024    //**********************************************************************************************
1025 
1026    //**SMP multiplication assignment to dense matrices*********************************************
1027    // No special implementation for the SMP multiplication assignment to dense matrices.
1028    //**********************************************************************************************
1029 
1030    //**SMP multiplication assignment to sparse matrices********************************************
1031    // No special implementation for the SMP multiplication assignment to sparse matrices.
1032    //**********************************************************************************************
1033 
1034    //**Compile time checks*************************************************************************
1035    /*! \cond BLAZE_INTERNAL */
1036    BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( MT1 );
1037    BLAZE_CONSTRAINT_MUST_BE_DENSE_MATRIX_TYPE( MT2 );
1038    BLAZE_CONSTRAINT_MUST_FORM_VALID_MATMATSUBEXPR( MT1, MT2 );
1039    /*! \endcond */
1040    //**********************************************************************************************
1041 };
1042 //*************************************************************************************************
1043 
1044 
1045 
1046 
1047 //=================================================================================================
1048 //
1049 //  GLOBAL BINARY ARITHMETIC OPERATORS
1050 //
1051 //=================================================================================================
1052 
1053 //*************************************************************************************************
1054 /*!\brief Subtraction operator for the subtraction of two dense matrices with identical storage
1055 //        order (\f$ A=B-C \f$).
1056 // \ingroup dense_matrix
1057 //
1058 // \param lhs The left-hand side dense matrix for the matrix subtraction.
1059 // \param rhs The right-hand side dense matrix to be subtracted from the left-hand side matrix.
1060 // \return The difference of the two matrices.
1061 // \exception std::invalid_argument Matrix sizes do not match.
1062 //
1063 // This operator represents the subtraction of two dense matrices with identical storage order:
1064 
1065    \code
1066    blaze::DynamicMatrix<double> A, B, C;
1067    // ... Resizing and initialization
1068    C = A - B;
1069    \endcode
1070 
1071 // The operator returns an expression representing a dense matrix of the higher-order element
1072 // type of the two involved matrix element types \a MT1::ElementType and \a MT2::ElementType.
1073 // Both matrix types \a MT1 and \a MT2 as well as the two element types \a MT1::ElementType
1074 // and \a MT2::ElementType have to be supported by the SubTrait class template.\n
1075 // In case the current number of rows and columns of the two given  matrices don't match, a
1076 // \a std::invalid_argument is thrown.
1077 */
1078 template< typename MT1  // Type of the left-hand side dense matrix
1079         , typename MT2  // Type of the right-hand side dense matrix
1080         , bool SO >     // Storage order
1081 inline decltype(auto)
1082    operator-( const DenseMatrix<MT1,SO>& lhs, const DenseMatrix<MT2,SO>& rhs )
1083 {
1084    BLAZE_FUNCTION_TRACE;
1085 
1086    if( (*lhs).rows() != (*rhs).rows() || (*lhs).columns() != (*rhs).columns() ) {
1087       BLAZE_THROW_INVALID_ARGUMENT( "Matrix sizes do not match" );
1088    }
1089 
1090    using ReturnType = const DMatDMatSubExpr<MT1,MT2,SO>;
1091    return ReturnType( *lhs, *rhs );
1092 }
1093 //*************************************************************************************************
1094 
1095 
1096 
1097 
1098 //=================================================================================================
1099 //
1100 //  ISALIGNED SPECIALIZATIONS
1101 //
1102 //=================================================================================================
1103 
1104 //*************************************************************************************************
1105 /*! \cond BLAZE_INTERNAL */
1106 template< typename MT1, typename MT2, bool SO >
1107 struct IsAligned< DMatDMatSubExpr<MT1,MT2,SO> >
1108    : public BoolConstant< IsAligned_v<MT1> && IsAligned_v<MT2> >
1109 {};
1110 /*! \endcond */
1111 //*************************************************************************************************
1112 
1113 
1114 
1115 
1116 //=================================================================================================
1117 //
1118 //  ISPADDED SPECIALIZATIONS
1119 //
1120 //=================================================================================================
1121 
1122 //*************************************************************************************************
1123 /*! \cond BLAZE_INTERNAL */
1124 template< typename MT1, typename MT2, bool SO >
1125 struct IsPadded< DMatDMatSubExpr<MT1,MT2,SO> >
1126    : public BoolConstant< IsPadded_v<MT1> && IsPadded_v<MT2> >
1127 {};
1128 /*! \endcond */
1129 //*************************************************************************************************
1130 
1131 } // namespace blaze
1132 
1133 #endif
1134