function U = orthogonal(V, W);
% ORTHOGONAL(V) constructs a unit pure quaternion perpendicular to V, and
% perpendicular to W,  if given. V (and W if given) must be pure quaternion
% scalar(s). W need not be perpendicular to V, but it must not be parallel
% (that is the scalar product of W and V must not have unit modulus).

% Copyright © 2005 Stephen J. Sangwine and Nicolas Le Bihan.
% See the file : Copyright.m for further details.

% Modified 27 September 2005 by SJS to include comments on complexified
% case.

error(nargchk(1, 2, nargin)), error(nargoutchk(0, 1, nargout))

if size(V) ~= [1, 1]
    error('First argument must not be a vector or matrix.');
end

if ~isa(V, 'quaternion') | ~ispure(V)
    error('First argument must be a pure quaternion.')
end

V = unit(V); % Ensure that V is a unit pure quaternion.

if nargin == 2
    if size(W) ~= [1, 1]
        error('Second argument must not be a vector or matrix.');
    end
    
    if ~isa(W, 'quaternion') | ~ispure(W)
        error('Second argument must be a pure quaternion.')
    end
    
    % W must be distinct in direction from V, that is it must not be
    % parallel to V. We verify this by computing the dot (scalar) product
    % between V and unit(W), which will be +1 or -1 in the case where they
    % are parallel.
    %
    % Notice that, if the dot product is complex, the abs function ensures
    % a real result before 1 is subtracted. Thus 'parallel' in the case of
    % complex quaternions means having a dot product with a modulus of 1.
    % It is possible this restriction could be relaxed when it is better
    % understood, but since W is somewhat arbitrary, it is not difficult to
    % make a choice that satisfies the check below.
    
    if abs(abs(dot(V, unit(W))) - 1) <= eps
        error('Arguments must not be parallel to each other.');    
    end
end

% Now compute the required vector. The method used was published in:
% 
% Ell, T. A. and Sangwine, S. J., "Decomposition of 2D Hypercomplex Fourier
% Transforms into Pairs of Complex Fourier Transforms", in: Gabbouj, M. and
% Kuosmanen (eds), "Signal Processing X, Theories and Applications",
% Proceedings of EUSIPCO 2000, Tenth European Signal Processing Conference,
% Tampere, Finland, 5-8 September 2000, Vol. II, 1061-1064, section 6.
% 
% In the paper, a specific case was shown.  Here we have a general method
% for choosing one of the three basis vectors qi, qj, qk, making the choice
% in such a way that if V is one of these vectors we will choose a
% different one.

% Note: This has not been thoroughly generalized to the complex case. If V
% is a pure multivector, the result will be a pure multivector (that is a
% complex pure quaternion), but if V is purely imaginary, the result will
% be purely real. Since this code is used only if no value is given for W,
% this is not a serious limitation, since the user is free to specify W and
% thus obtain a complex result if needed. However, this issue merits
% further study. SJS 27 September 2005.

% Note added 3 October 2005. Perhaps we should test here not for equality,
% but for parallelism, using the dot product. The code would read something
% like: if dot(V, qi) == 1 | dot(V, qi) == i ? The problem is that if V is
% complex, the dot product may be imaginary. What do we mean by parallel?
% Whatever we mean, we want to avoid it and make a different choice.

if nargin == 1
    
    % W was not given, so we have to choose an arbitrary vector not
    % parallel to V. We make this choice in such a way that if V is
    % in the set {qi, qj, qk}, the result of the function is the
    % next element of the set (cyclically). This is not essential,
    % but since the result is arbitary, it is neater to make a
    % sensible choice, rather than an arbitrary one.
    
    if V == qi
        W = -qk;
    elseif V == qj
        W = -qi;
    else
        W = -qj;
    end;
end

% Now compute the result. We have to normalise this, because W may not be
% perpendicular to V, and therefore the result may not have unit modulus.

U = unit(cross(V, W));
