function B = spqr_null_mult (N,A,method)
%SPQR_NULL_MULT multiplies a matrix by numerical null space from spqr_rank methods
%
%  Multiplies the matrix A by N, the orthonormal basis for
%  a numerical null space produced by spqr_basic, spqr_null, spqr_pinv,
%  or spqr_cod.  N can be stored either implicitly (see the above
%  routines for details) or as an explicit matrix.
%
% Example of use:  B = spqr_null_mult(N,A,method) ;
%    method = 0: N'*A  (the default)
%    method = 1: N*A
%    method = 2: A*N'
%    method = 3: A*N
% Also if N is stored implicitly then to create an explicit representation
% of the orthonormal null space basis
%    Nexplicit = spqr_null_mult(N,eye(size(N.X,2)),1) ;
% creates a full matrix and
%    Nexplicit = spqr_null_mult(N,speye(size(N.X,2)),1) ;
% creates a sparse matrix.
%
% If N is stored implicitly then N can have fields:
%    N.P contains a permutation vector (N.P is not always present)
%    N.Q contains sparse Householder transforms from spqr
%    N.X is a sparse matrix with orthonormal columns
%
% N.kind determines how the implicit orthonormal basis is represented.
%   N.kind = 'P*Q*X':  the basis is N.P*N.Q*N.X
%   N.kind = 'Q*P*X':  the basis is N.Q*N.P*N.X
%   N.kind = 'Q*X':    the basis is N.Q*N.X, and N.P does not appear in N
%
% Examples
%
%   N = spqr_null (A) ;              % find nullspace of A
%   AN = spqr_null_mult (N,A,3) ;    % compute A*N, which will have a low norm.
%
% See also spqr, spqr_basic, spqr_cod, spqr_null, spqr_pinv, spqr_ssi, spqr_ssp.

% spqr_rank, Copyright (c) 2012, Leslie Foster and Timothy A Davis.
% All Rights Reserved.
% SPDX-License-Identifier: BSD-3-clause

% If N is stored implicitly, to potentially improve efficiency, code is
% selected based on the number of rows and columns in A and N.

if nargin < 3
    method = 0 ;
end
[m n] = size (A) ;

if isstruct (N)

    %---------------------------------------------------------------------------
    % N held implicitly
    %---------------------------------------------------------------------------

    p = size (N.X, 2) ;

    switch method

        case 0

            %-------------------------------------------------------------------
            % B = N'*A
            %-------------------------------------------------------------------

            switch N.kind

                case 'Q*X'

                    if n <= p
                        % B = X' * ( Q' * A )
                        B = spqr_qmult (N.Q, A, 0) ;
                        B = N.X'*B ;
                    else
                        % B = ( X' * Q' ) * A
                        B = spqr_qmult (N.Q, (N.X)', 2) ;
                        B = B*A ;
                    end

                case 'Q*P*X'

                    if n <= p
                        % B = X' * P' * ( Q' * A ) ;
                        B = spqr_qmult (N.Q,A,0) ;
                        p (N.P) = 1:length (N.P) ;
                        B = B(p,:) ;
                        B = N.X'*B ;
                    else
                        % B = ( ( P * X )' * Q' ) * A ;
                        B = N.X(N.P,:)';
                        B = spqr_qmult (N.Q,B,2) ;
                        B = B * A ;
                    end

                case 'P*Q*X'

                    if n <= p
                        % B = X' * ( Q' * ( P' * A ) ) ;
                        p( N.P) = 1:length(N.P) ;
                        B = A(p,:) ;
                        B = spqr_qmult (N.Q,B,0) ;
                        B = N.X'*B ;
                    else
                        % B = ( ( X' * Q' ) * P' ) * A ;
                        B = spqr_qmult (N.Q, (N.X)', 2) ;
                        B = B(:,N.P) ;
                        B = B * A ;
                    end

                otherwise
                    error ('unrecognized N struct') ;
            end

        case 1

            %-------------------------------------------------------------------
            % B = N*A
            %-------------------------------------------------------------------

            switch N.kind

                case 'Q*X'

                    % B = Q * ( X * A ) ;
                    B = N.X * A ;
                    B = spqr_qmult (N.Q,B,1) ;

                case 'Q*P*X'

                    % B = Q * ( P * ( X * A ) ) ;
                    B = N.X * A ;
                    B = B(N.P,:) ;
                    B = spqr_qmult (N.Q,B,1) ;

                case 'P*Q*X'

                    % B = P * ( Q * ( X * A ) ) ;
                    B = N.X * A ;
                    B = spqr_qmult (N.Q,B,1) ;
                    B = B(N.P,:) ;

                otherwise
                    error ('unrecognized N struct') ;
            end

        case 2

            %-------------------------------------------------------------------
            % B = A*N'
            %-------------------------------------------------------------------

            switch N.kind

                case 'Q*X'

                    % B = (A * X') * Q'
                    B = A * (N.X)' ;
                    B = spqr_qmult (N.Q,B,2) ;

                case 'Q*P*X'

                    % B = ( ( A * X' ) * P' ) * Q'
                    B = A * (N.X)' ;
                    B = B(:,N.P) ;
                    B = spqr_qmult (N.Q,B,2) ;

                case 'P*Q*X'

                    % B = ( ( A * X' ) * Q' ) * P'
                    B = A * (N.X)' ;
                    B = spqr_qmult (N.Q,B,2) ;
                    B = B(:,N.P) ;

                otherwise
                    error ('unrecognized N struct') ;
            end

        case 3

            %-------------------------------------------------------------------
            % B = A*N
            %-------------------------------------------------------------------

            switch N.kind

                case 'Q*X'

                    if m <= p
                        % B = ( A * Q ) * X
                        B = spqr_qmult (N.Q,A,3) ;
                        B = B*N.X ;
                    else
                        % B = A * ( Q * X )
                        B = spqr_qmult (N.Q, N.X, 1 ) ;
                        B = A * B ;
                    end

                case 'Q*P*X'

                    if m <= p
                        % B = ( ( A * Q ) * P ) * X
                        B = spqr_qmult (N.Q,A,3) ;
                        p(N.P) = 1:length(N.P) ;
                        B = B(:,p) ;
                        B = B*N.X ;
                    else
                        % B = A * ( Q * ( P * X ) )
                        B = N.X(N.P,:) ;
                        B = spqr_qmult (N.Q, B, 1 ) ;
                        B = A * B ;
                    end

                case 'P*Q*X'

                    if m <= p
                        % B = ( ( A * P ) * Q ) * X
                        p(N.P) = 1:length(N.P) ;
                        B = A(:,p) ;
                        B = spqr_qmult (N.Q,B,3) ;
                        B = B*N.X ;
                    else
                        % B = A * ( P * ( Q * X ) )
                        B = spqr_qmult (N.Q, N.X, 1) ;
                        B = B(N.P,:) ;
                        B = A * B ;
                    end

                otherwise
                    error ('unrecognized N struct') ;
            end

        otherwise
            error ('unrecognized method') ;
    end

else

    %---------------------------------------------------------------------------
    % N held as an explicit matrix
    %---------------------------------------------------------------------------

    switch method
        case 0
            B = N'*A ;
        case 1
            B = N*A ;
        case 2
            B = A*N' ;
        case 3
            B = A*N ;
        otherwise
            error ('unrecognized method') ;
    end

end
