% CircularMat test cases
%
% Author: Jonathan Karr
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 9/5/2010
classdef CircularMat_Test < TestCase
    %constructor
    methods
        function this = CircularMat_Test(name)
            this = this@TestCase(name);
        end
    end

    %tests
    methods
        function testConstructor(this)
            import edu.stanford.covert.util.CircularMat;

            %0 arguments
            cmat = CircularMat();

            %1 argument
            mat = zeros(4,3,2);
            mat(4, 3, 1)=1;
            mat(3, 2, 2)=2;
            mat(1, 1, 2)=3;
            mat(3, 1, 1)=4;

            cmat = CircularMat(mat);

            %2 arguments
            mat = zeros(4,3,2);
            mat(4, 3, 1)=1;
            mat(3, 2, 2)=2;
            mat(1, 1, 2)=3;
            mat(3, 1, 1)=4;

            cmat = CircularMat(mat,1);
        end

        function testSubscriptReferenceAssignment(this)
            import edu.stanford.covert.util.CircularMat;

            %reference
            mat = zeros(100,2);
            mat(2,2)=1;
            cmat = CircularMat(mat,1);
            cmat2 = CircularMat(mat,2);
            assertEqual(CircularMat([0; 0; 0; 1]), cmat(99:102,2));
            assertEqual(CircularMat([0;0;0;1]), cmat(99:102,2));

            assertEqual(CircularMat(zeros(1,2)), cmat(1,:));
            assertEqual(CircularMat(zeros(1,2),2), cmat2(1,:));
            assertEqual(CircularMat(zeros(100,1),1), cmat(:,1));
            assertEqual(CircularMat([0;1;zeros(98,1)],1), cmat(:,2));
            assertEqual(CircularMat(zeros(100,1)), cmat(1:100,1));
            assertEqual(CircularMat([0;1;zeros(98,1)]), cmat(1:100,2));

            %assignment
            cmat = CircularMat(zeros(100,2),1);
            cmat(102,2)=1;
            assertEqual(CircularMat([0 0; 0 1; zeros(98,2)],1), cmat);

            cmat = CircularMat(zeros(100,2),1);
            cmat(100,3)=1;
            mat = zeros(100,2);
            mat(100,3)=1;
            assertEqual(CircularMat(mat,1), cmat);
        end

        function testEnd(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            assertEqual(CircularMat(mat(2:end,:,:,:)), cmat(2:end,:,:,:));
            assertEqual(CircularMat(mat(:,:,end,:)), cmat(:,:,end,:));
            assertEqual(CircularMat(mat(:,:,:,end-1)), cmat(:,:,:,end-1));
            assertEqual(CircularMat(mat(1:end)), cmat(1:end));
        end

        function testSize(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            %isempty
            assertEqual(false, isempty(cmat));
            assertEqual(true, isempty(CircularMat));

            %length
            assertEqual(4, length(cmat));

            %size
            assertEqual([4 1 3 2], size(cmat));
            assertEqual(4, size(cmat,1));
            assertEqual(1, size(cmat,5));

            %numel
            assertEqual(4*1*3*2, numel(cmat));

            %ndims
            assertEqual(4, ndims(cmat));

            %permute
            assertEqual(CircularMat(permute(mat,[2 4 1 3])), permute(cmat, [2 4 1 3]));
            assertEqual(CircularMat(permute(mat,[2 5 4 1 3])), permute(cmat, [2 5 4 1 3]));

            %transpose
            mat2 = permute(sum(mat,4),[1 3 2]);
            assertEqual(CircularMat(mat2'), transpose(CircularMat(mat2)));
            assertEqual(CircularMat(mat2'), CircularMat(mat2)');

            %ctranspose
            mat2 = permute(sum(mat,4),[1 3 2])+sqrt(-1);
            assertEqual(CircularMat(ctranspose(mat2)), ctranspose(CircularMat(mat2)));

            %reshape
            assertEqual(CircularMat(reshape(mat,[3 2 4])), reshape(CircularMat(mat),[3 2 4]));
            assertEqual(CircularMat(reshape(zeros(0,2),[0 3])), reshape(CircularMat(zeros(0,2)),[0 3]));

            %squeeze
            assertEqual(CircularMat(squeeze(mat)), squeeze(cmat));
        end

        function testFind(this)
            import edu.stanford.covert.util.CircularMat;

            %example 1
            mat = zeros(4,3);
            mat(4, 3)=1;
            mat(3, 2)=2;
            mat(1, 1)=3;
            mat(3, 1)=4;
            cmat = CircularMat(mat);

            [i1,j1,k1]=find(mat);
            [i2,j2,k2]=find(cmat);

            assertEqual(i1, i2);
            assertEqual(j1, j2);
            assertEqual(k1, k2);

            %example 2
            mat = zeros(4,3,'int32');
            mat(4, 3)=1;
            mat(3, 2)=2;
            mat(1, 1)=3;
            mat(3, 1)=4;
            cmat = CircularMat(mat);

            [i1,j1,k1]=find(mat);
            [i2,j2,k2]=find(cmat);

            assertEqual(i1, i2);
            assertEqual(j1, j2);
            assertEqual(k1, k2);
        end

        %concatenation
        function testConcatenation(this)
            import edu.stanford.covert.util.CircularMat;

            %Vertcat_nullIdentity
            assertEqual(...
                CircularMat,...
                vertcat(CircularMat));

            %Vertcat_identity
            t = CircularMat([1 2; 3 3], 1);
            assertEqual(t, vertcat(t));

            %Vertcat_threeTensors
            u = CircularMat([1 2; 3 3], 1);
            v = CircularMat([6 3; 4 2; 1 4], 1);

            assertEqual(CircularMat([...
                1 2;
                3 3;
                6 3;
                4 2;
                1 4;
                1 2;
                3 3]),...
                [u; v; u]);

            %Vertcat_nullFirst
            t = CircularMat([1 2; 3 3], 1);
            assertEqual(...
                CircularMat([1 2; 3 3]),...
                vertcat(CircularMat(), t));

            %Vertcat_nullLast
            t = CircularMat([1 2; 3 3], 1);
            assertEqual(...
                CircularMat([1 2; 3 3]),...
                vertcat(t, CircularMat()));

            %Vertcat_zeros
            assertEqual(...
                CircularMat(zeros(7,2)),...
                vertcat(...
                CircularMat(zeros(3,2)), CircularMat(zeros(4,2))));

            %Horzcat_nullIdentity
            assertEqual(...
                CircularMat,...
                horzcat(CircularMat));

            %Horzcat_identity
            t = CircularMat([1 2; 3 3], 1);
            assertEqual(t, horzcat(t));

            %Horzcat_nullFirst
            t = CircularMat([1 2; 3 3], 1);
            assertEqual(...
                CircularMat([zeros(2,3) [1 2; 3 3]]),...
                horzcat(CircularMat(zeros(2,3)), t));

            %Horzcat_nullLast
            t = CircularMat([1 2; 3 3], 1);
            assertEqual(...
                CircularMat([[1 2; 3 3] zeros(2,3)],1),...
                horzcat(t, CircularMat(zeros(2,3))));

            %Horzcat_zeros
            assertEqual(...
                CircularMat(zeros([2 7])),...
                horzcat(...
                CircularMat(zeros([2 3])), CircularMat(zeros([2 4]))));

            %padarray
            x = reshape(1:15,5,3);
            cmat = CircularMat(x);

            assertEqual(CircularMat(padarray(x,3)),padarray(cmat,3));
            assertEqual(CircularMat(padarray(x,[0 2])),padarray(cmat,[0 2]));
            assertEqual(CircularMat(padarray(x,[1 0 2])),padarray(cmat,[1 0 2]));
            assertEqual(CircularMat(padarray(x,[1 0 0])),padarray(cmat,[1 0 0]));
            assertEqual(CircularMat(padarray(x,[0 1 1],1)),padarray(cmat,[0 1 1],1));
            assertEqual(CircularMat(padarray(x,[0 1 1],2,'pre')),padarray(cmat,[0 1 1],2,'pre'));
            assertEqual(CircularMat(padarray(x,[0 1 1],2,'post')),padarray(cmat,[0 1 1],2,'post'));
            assertEqual(CircularMat(padarray(x,[0 1 1],2,'both')),padarray(cmat,[0 1 1],2,'both'));

            assertEqual(CircularMat(padarray(x,[2 2],'circular','pre')),padarray(cmat,[2 2],'circular','pre'));
            assertEqual(CircularMat(padarray(x,[2 2],'replicate','pre')),padarray(cmat,[2 2],'replicate','pre'));
            assertEqual(CircularMat(padarray(x,[2 2],'symmetric','pre')),padarray(cmat,[2 2],'symmetric','pre'));
            assertEqual(CircularMat(padarray(x,[2 2],'circular','post')),padarray(cmat,[2 2],'circular','post'));
            assertEqual(CircularMat(padarray(x,[2 2],'replicate','post')),padarray(cmat,[2 2],'replicate','post'));
            assertEqual(CircularMat(padarray(x,[2 2],'symmetric','post')),padarray(cmat,[2 2],'symmetric','post'));
            assertEqual(CircularMat(padarray(x,[2 2],'circular','both')),padarray(cmat,[2 2],'circular','both'));
            assertEqual(CircularMat(padarray(x,[2 2],'replicate','both')),padarray(cmat,[2 2],'replicate','both'));
            assertEqual(CircularMat(padarray(x,[2 2],'symmetric','both')),padarray(cmat,[2 2],'symmetric','both'));
        end

        function testAndOrXorNot(test)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            %mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            %not
            assertEqual(CircularMat(~mat), ~cmat);
            assertEqual(CircularMat(~mat2), ~cmat2);

            %or
            assertEqual(CircularMat(mat | 0), cmat | 0);
            assertEqual(CircularMat(mat | 2), cmat | 2);
            assertEqual(CircularMat(mat | Inf), cmat | Inf);
            assertEqual(CircularMat(mat | -Inf), cmat | -Inf);
            assertEqual(CircularMat(mat | mat2), cmat | cmat2);
            assertEqual(CircularMat(mat | mat2), cmat | mat2);

            assertEqual(CircularMat(0 | mat), 0 | cmat);
            assertEqual(CircularMat(2 | mat), 2 | cmat);
            assertEqual(CircularMat(Inf | mat), Inf | cmat);
            assertEqual(CircularMat(-Inf | mat), -Inf | cmat);
            assertEqual(CircularMat(mat2 | mat), cmat2 | cmat);
            assertEqual(CircularMat(mat2 | mat), mat2 | cmat);

            %and
            assertEqual(CircularMat(mat & 0), cmat & 0);
            assertEqual(CircularMat(mat & 2), cmat & 2);
            assertEqual(CircularMat(mat & Inf), cmat & Inf);
            assertEqual(CircularMat(mat & -Inf), cmat & -Inf);
            assertEqual(CircularMat(mat & mat2), cmat & cmat2);
            assertEqual(CircularMat(mat & mat2), cmat & mat2);

            assertEqual(CircularMat(0 & mat), 0 & cmat);
            assertEqual(CircularMat(2 & mat), 2 & cmat);
            assertEqual(CircularMat(Inf & mat), Inf & cmat);
            assertEqual(CircularMat(-Inf & mat), -Inf & cmat);
            assertEqual(CircularMat(mat2 & mat), cmat2 & cmat);
            assertEqual(CircularMat(mat2 & mat), mat2 & cmat);

            %xor
            assertEqual(CircularMat(xor(mat,0)), xor(cmat,0));
            assertEqual(CircularMat(xor(mat,2)), xor(cmat,2));
            assertEqual(CircularMat(xor(mat,Inf)), xor(cmat,Inf));
            assertEqual(CircularMat(xor(mat,-Inf)), xor(cmat,-Inf));
            assertEqual(CircularMat(xor(mat,mat2)), xor(cmat,cmat2));
            assertEqual(CircularMat(xor(mat,mat2)), xor(cmat,mat2));

            assertEqual(CircularMat(xor(0,mat)), xor(0,cmat));
            assertEqual(CircularMat(xor(2,mat)), xor(2,cmat));
            assertEqual(CircularMat(xor(Inf,mat)), xor(Inf,cmat));
            assertEqual(CircularMat(xor(-Inf,mat)), xor(-Inf,cmat));
            assertEqual(CircularMat(xor(mat2,mat)), xor(cmat2,cmat));
            assertEqual(CircularMat(xor(mat2,mat)), xor(mat2,cmat));
        end

        function testPlusMinus(test)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            %unary
            assertEqual(CircularMat(+mat), +cmat);
            assertEqual(CircularMat(-mat), -cmat);

            %addition
            assertEqual(CircularMat(mat+0), cmat+0);
            assertEqual(CircularMat(mat+2), cmat+2);
            assertEqual(CircularMat(mat+NaN), cmat+NaN);
            assertEqual(CircularMat(mat+Inf), cmat+Inf);
            assertEqual(CircularMat(mat+-Inf), cmat+-Inf);
            assertEqual(CircularMat(mat+mat2), cmat+cmat2);
            assertEqual(CircularMat(mat+mat2), cmat+mat2);

            assertEqual(CircularMat(0+mat), 0+cmat);
            assertEqual(CircularMat(2+mat), 2+cmat);
            assertEqual(CircularMat(NaN+mat), NaN+cmat);
            assertEqual(CircularMat(Inf+mat), Inf+cmat);
            assertEqual(CircularMat(-Inf+mat), -Inf+cmat);
            assertEqual(CircularMat(mat2+mat), cmat2+cmat);
            assertEqual(CircularMat(mat2+mat), mat2+cmat);

            %subtraction
            assertEqual(CircularMat(mat-0), cmat-0);
            assertEqual(CircularMat(mat-2), cmat-2);
            assertEqual(CircularMat(mat-NaN), cmat-NaN);
            assertEqual(CircularMat(mat-Inf), cmat-Inf);
            assertEqual(CircularMat(mat--Inf), cmat--Inf);
            assertEqual(CircularMat(mat-mat2), cmat-cmat2);
            assertEqual(CircularMat(mat-mat2), cmat-mat2);

            assertEqual(CircularMat(0-mat), 0-cmat);
            assertEqual(CircularMat(2-mat), 2-cmat);
            assertEqual(CircularMat(NaN-mat), NaN-cmat);
            assertEqual(CircularMat(Inf-mat), Inf-cmat);
            assertEqual(CircularMat(-Inf-mat), -Inf-cmat);
            assertEqual(CircularMat(mat2-mat), cmat2-cmat);
            assertEqual(CircularMat(mat2-mat), mat2-cmat);
        end

        function testMultiplyDivideExponentiate(test)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            mat3 = [
                2 Inf 0;
                0 2 -3;
                0 0 NaN];
            mat4 = [
                4 0 -7;
                -Inf 3 0
                NaN 0 0];
            cmat3 = CircularMat(mat3);
            cmat4 = CircularMat(mat4);

            %element-wise multiplication
            assertEqual(CircularMat(mat.*0), cmat.*0);
            assertEqual(CircularMat(mat.*2), cmat.*2);
            assertEqual(CircularMat(mat.*NaN), cmat.*NaN);
            assertEqual(CircularMat(mat.*Inf), cmat.*Inf);
            assertEqual(CircularMat(mat.*-Inf), cmat.*-Inf);
            assertEqual(CircularMat(mat.*mat2), cmat.*cmat2);
            assertEqual(CircularMat(mat.*mat2), cmat.*mat2);

            assertEqual(CircularMat(0.*mat), 0.*cmat);
            assertEqual(CircularMat(2.*mat), 2.*cmat);
            assertEqual(CircularMat(NaN.*mat), NaN.*cmat);
            assertEqual(CircularMat(Inf.*mat), Inf.*cmat);
            assertEqual(CircularMat(-Inf.*mat), -Inf.*cmat);
            assertEqual(CircularMat(mat2.*mat), cmat2.*cmat);
            assertEqual(CircularMat(mat2.*mat), mat2.*cmat);

            %matrix multiplication
            assertEqual(CircularMat(mat*0), cmat*0);
            assertEqual(CircularMat(mat*2), cmat*2);
            assertEqual(CircularMat(mat*NaN), cmat*NaN);
            assertEqual(CircularMat(mat*Inf), cmat*Inf);
            assertEqual(CircularMat(mat*-Inf), cmat*-Inf);
            assertEqual(CircularMat(mat3*mat4), cmat3*cmat4);
            assertEqual(CircularMat(mat3*mat4), cmat3*mat4);

            assertEqual(CircularMat(0*mat), 0*cmat);
            assertEqual(CircularMat(2*mat), 2*cmat);
            assertEqual(CircularMat(NaN*mat), NaN*cmat);
            assertEqual(CircularMat(Inf*mat), Inf*cmat);
            assertEqual(CircularMat(-Inf*mat), -Inf*cmat);
            assertEqual(CircularMat(mat4*mat3), cmat4*cmat3);
            assertEqual(CircularMat(mat4*mat3), mat4*cmat3);

            %element-wise right division
            assertEqual(CircularMat(mat./0), cmat./0);
            assertEqual(CircularMat(mat./2), cmat./2);
            assertEqual(CircularMat(mat./NaN), cmat./NaN);
            assertEqual(CircularMat(mat./Inf), cmat./Inf);
            assertEqual(CircularMat(mat./-Inf), cmat./-Inf);
            assertEqual(CircularMat(mat./mat2), cmat./cmat2);
            assertEqual(CircularMat(mat./mat2), cmat./mat2);

            assertEqual(CircularMat(0./mat), 0./cmat);
            assertEqual(CircularMat(2./mat), 2./cmat);
            assertEqual(CircularMat(NaN./mat), NaN./cmat);
            assertEqual(CircularMat(Inf./mat), Inf./cmat);
            assertEqual(CircularMat(-Inf./mat), -Inf./cmat);
            assertEqual(CircularMat(mat2./mat), cmat2./cmat);
            assertEqual(CircularMat(mat2./mat), mat2./cmat);

            %matrix right division
            % assertEqual(CircularMat(mat/0), cmat/0);
            % assertEqual(CircularMat(mat/2), cmat/2);
            % assertEqual(CircularMat(mat/NaN), cmat/NaN);
            % assertEqual(CircularMat(mat/Inf), cmat/Inf);
            % assertEqual(CircularMat(mat/-Inf), cmat/-Inf);
            % assertEqual(CircularMat(mat/mat2), cmat/cmat2);
            % assertEqual(CircularMat(mat/mat2), cmat/mat2);

            % assertEqual(CircularMat(0/mat), 0/cmat);
            % assertEqual(CircularMat(2/mat), 2/cmat);
            % assertEqual(CircularMat(NaN/mat), NaN/cmat);
            % assertEqual(CircularMat(Inf/mat), Inf/cmat);
            % assertEqual(CircularMat(-Inf/mat), -Inf/cmat);
            % assertEqual(CircularMat(mat2/mat), cmat2/cmat);
            % assertEqual(CircularMat(mat2/mat), mat2/cmat);

            %element-wise left division
            assertEqual(CircularMat(mat.\0), cmat.\0);
            assertEqual(CircularMat(mat.\2), cmat.\2);
            assertEqual(CircularMat(mat.\NaN), cmat.\NaN);
            assertEqual(CircularMat(mat.\Inf), cmat.\Inf);
            assertEqual(CircularMat(mat.\-Inf), cmat.\-Inf);
            assertEqual(CircularMat(mat.\mat2), cmat.\cmat2);
            assertEqual(CircularMat(mat.\mat2), cmat.\mat2);

            assertEqual(CircularMat(0.\mat), 0.\cmat);
            assertEqual(CircularMat(2.\mat), 2.\cmat);
            assertEqual(CircularMat(NaN.\mat), NaN.\cmat);
            assertEqual(CircularMat(Inf.\mat), Inf.\cmat);
            assertEqual(CircularMat(-Inf.\mat), -Inf.\cmat);
            assertEqual(CircularMat(mat2.\mat), cmat2.\cmat);
            assertEqual(CircularMat(mat2.\mat), mat2.\cmat);

            %matrix left division
            % assertEqual(CircularMat(mat\0), cmat\0);
            % assertEqual(CircularMat(mat\2), cmat\2);
            % assertEqual(CircularMat(mat\NaN), cmat\NaN);
            % assertEqual(CircularMat(mat\Inf), cmat\Inf);
            % assertEqual(CircularMat(mat\-Inf), cmat\-Inf);
            % assertEqual(CircularMat(mat\mat2), cmat\cmat2);
            % assertEqual(CircularMat(mat\mat2), cmat\mat2);

            % assertEqual(CircularMat(0\mat), 0\cmat);
            % assertEqual(CircularMat(2\mat), 2\cmat);
            % assertEqual(CircularMat(NaN\mat), NaN\cmat);
            % assertEqual(CircularMat(Inf\mat), Inf\cmat);
            % assertEqual(CircularMat(-Inf\mat), -Inf\cmat);
            % assertEqual(CircularMat(mat2\mat), cmat2\cmat);
            % assertEqual(CircularMat(mat2\mat), mat2\cmat);

            %element-wise exponentiation
            assertEqual(CircularMat(mat.^0), cmat.^0);
            assertEqual(CircularMat(mat.^2), cmat.^2);
            assertEqual(CircularMat(mat.^NaN), cmat.^NaN);
            assertEqual(CircularMat(mat.^Inf), cmat.^Inf);
            assertEqual(CircularMat(mat.^-Inf), cmat.^-Inf);
            assertEqual(CircularMat(mat.^mat2), cmat.^cmat2);
            assertEqual(CircularMat(mat.^mat2), cmat.^mat2);

            assertEqual(CircularMat(0.^mat), 0.^cmat);
            assertEqual(CircularMat(2.^mat), 2.^cmat);
            assertEqual(CircularMat(NaN.^mat), NaN.^cmat);
            assertEqual(CircularMat(Inf.^mat), Inf.^cmat);
            assertEqual(CircularMat(-Inf.^mat), -Inf.^cmat);
            assertEqual(CircularMat(mat2.^mat), cmat2.^cmat);
            assertEqual(CircularMat(mat2.^mat), mat2.^cmat);

            %matrix exponentiation
            mat = reshape(1:9,3,3);
            mat2 = reshape(10:18,3,3);
            cmat = CircularMat(mat);
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat^0), cmat^0);
            assertEqual(CircularMat(mat^2), cmat^2);
            assertEqual(CircularMat(mat^NaN), cmat^NaN);
            assertEqual(CircularMat(mat^Inf), cmat^Inf);
            assertEqual(CircularMat(mat^-Inf), cmat^-Inf);
            % assertEqual(CircularMat(mat^mat2), cmat^cmat2);
            % assertEqual(CircularMat(mat^mat2), cmat^mat2);

            assertEqual(CircularMat(0^mat), 0^cmat);
            assertEqual(CircularMat(2^mat), 2^cmat);
            assertEqual(CircularMat(NaN^mat), NaN^cmat);
            assertEqual(CircularMat(Inf^mat), Inf^cmat);
            assertEqual(CircularMat(-Inf^mat), -Inf^cmat);
            % assertEqual(CircularMat(mat2^mat), cmat2^cmat);
            % assertEqual(CircularMat(mat2^mat), mat2^cmat);
        end

        function testEquality(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat==0), cmat==0);
            assertEqual(CircularMat(mat==2), cmat==2);
            assertEqual(CircularMat(mat==NaN), cmat==NaN);
            assertEqual(CircularMat(mat==Inf), cmat==Inf);
            assertEqual(CircularMat(mat==-Inf), cmat==-Inf);
            assertEqual(CircularMat(mat==mat2), cmat==cmat2);
            assertEqual(CircularMat(mat==mat2), cmat==mat2);

            assertEqual(CircularMat(0==mat), 0==cmat);
            assertEqual(CircularMat(2==mat), 2==cmat);
            assertEqual(CircularMat(NaN==mat), NaN==cmat);
            assertEqual(CircularMat(Inf==mat), Inf==cmat);
            assertEqual(CircularMat(-Inf==mat), -Inf==cmat);
            assertEqual(CircularMat(mat2==mat), cmat2==cmat);
            assertEqual(CircularMat(mat2==mat), mat2==cmat);
        end

        function testInequality(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat~=0), cmat~=0);
            assertEqual(CircularMat(mat~=2), cmat~=2);
            assertEqual(CircularMat(mat~=NaN), cmat~=NaN);
            assertEqual(CircularMat(mat~=Inf), cmat~=Inf);
            assertEqual(CircularMat(mat~=-Inf), cmat~=-Inf);
            assertEqual(CircularMat(mat~=mat2), cmat~=cmat2);
            assertEqual(CircularMat(mat~=mat2), cmat~=mat2);

            assertEqual(CircularMat(0~=mat), 0~=cmat);
            assertEqual(CircularMat(2~=mat), 2~=cmat);
            assertEqual(CircularMat(NaN~=mat), NaN~=cmat);
            assertEqual(CircularMat(Inf~=mat), Inf~=cmat);
            assertEqual(CircularMat(-Inf~=mat), -Inf~=cmat);
            assertEqual(CircularMat(mat2~=mat), cmat2~=cmat);
            assertEqual(CircularMat(mat2~=mat), mat2~=cmat);
        end

        function testGreaterThan(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat>0), cmat>0);
            assertEqual(CircularMat(mat>2), cmat>2);
            assertEqual(CircularMat(mat>NaN), cmat>NaN);
            assertEqual(CircularMat(mat>Inf), cmat>Inf);
            assertEqual(CircularMat(mat>-Inf), cmat>-Inf);
            assertEqual(CircularMat(mat>mat2), cmat>cmat2);
            assertEqual(CircularMat(mat>mat2), cmat>mat2);

            assertEqual(CircularMat(0>mat), 0>cmat);
            assertEqual(CircularMat(2>mat), 2>cmat);
            assertEqual(CircularMat(NaN>mat), NaN>cmat);
            assertEqual(CircularMat(Inf>mat), Inf>cmat);
            assertEqual(CircularMat(-Inf>mat), -Inf>cmat);
            assertEqual(CircularMat(mat2>mat), cmat2>cmat);
            assertEqual(CircularMat(mat2>mat), mat2>cmat);
        end

        function testGreaterThanOrEqualTo(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat>=0), cmat>=0);
            assertEqual(CircularMat(mat>=2), cmat>=2);
            assertEqual(CircularMat(mat>=NaN), cmat>=NaN);
            assertEqual(CircularMat(mat>=Inf), cmat>=Inf);
            assertEqual(CircularMat(mat>=-Inf), cmat>=-Inf);
            assertEqual(CircularMat(mat>=mat2), cmat>=cmat2);
            assertEqual(CircularMat(mat>=mat2), cmat>=mat2);

            assertEqual(CircularMat(0>=mat), 0>=cmat);
            assertEqual(CircularMat(2>=mat), 2>=cmat);
            assertEqual(CircularMat(NaN>=mat), NaN>=cmat);
            assertEqual(CircularMat(Inf>=mat), Inf>=cmat);
            assertEqual(CircularMat(-Inf>=mat), -Inf>=cmat);
            assertEqual(CircularMat(mat2>=mat), cmat2>=cmat);
            assertEqual(CircularMat(mat2>=mat), mat2>=cmat);
        end

        function testLessThan(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat<0), cmat<0);
            assertEqual(CircularMat(mat<2), cmat<2);
            assertEqual(CircularMat(mat<NaN), cmat<NaN);
            assertEqual(CircularMat(mat<Inf), cmat<Inf);
            assertEqual(CircularMat(mat<-Inf), cmat<-Inf);
            assertEqual(CircularMat(mat<mat2), cmat<cmat2);
            assertEqual(CircularMat(mat<mat2), cmat<mat2);

            assertEqual(CircularMat(0<mat), 0<cmat);
            assertEqual(CircularMat(2<mat), 2<cmat);
            assertEqual(CircularMat(NaN<mat), NaN<cmat);
            assertEqual(CircularMat(Inf<mat), Inf<cmat);
            assertEqual(CircularMat(-Inf<mat), -Inf<cmat);
            assertEqual(CircularMat(mat2<mat), cmat2<cmat);
            assertEqual(CircularMat(mat2<mat), mat2<cmat);
        end

        function testLessThanOrEqualTo(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=5;
            mat2(3, 1, 2, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(3, 1, 3, 1)=4;
            cmat2 = CircularMat(mat2);

            assertEqual(CircularMat(mat<=0), cmat<=0);
            assertEqual(CircularMat(mat<=2), cmat<=2);
            assertEqual(CircularMat(mat<=NaN), cmat<=NaN);
            assertEqual(CircularMat(mat<=Inf), cmat<=Inf);
            assertEqual(CircularMat(mat<=-Inf), cmat<=-Inf);
            assertEqual(CircularMat(mat<=mat2), cmat<=cmat2);
            assertEqual(CircularMat(mat<=mat2), cmat<=mat2);

            assertEqual(CircularMat(0<=mat), 0<=cmat);
            assertEqual(CircularMat(2<=mat), 2<=cmat);
            assertEqual(CircularMat(NaN<=mat), NaN<=cmat);
            assertEqual(CircularMat(Inf<=mat), Inf<=cmat);
            assertEqual(CircularMat(-Inf<=mat), -Inf<=cmat);
            assertEqual(CircularMat(mat2<=mat), cmat2<=cmat);
            assertEqual(CircularMat(mat2<=mat), mat2<=cmat);
        end

        function testScalarFunctions(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            %abs
            assertEqual(CircularMat(abs(mat)), abs(cmat));

            %sign
            assertEqual(CircularMat(sign(mat)), sign(cmat));

            %sqrt
            assertEqual(CircularMat(sqrt(mat)), sqrt(cmat));

            %realsqrt
            assertEqual(CircularMat(realsqrt(abs(mat))), realsqrt(abs(cmat)));

            %exp
            assertEqual(CircularMat(exp(mat)), exp(cmat));

            %expm1
            assertEqual(CircularMat(expm1(mat)), expm1(cmat));

            %log
            assertEqual(CircularMat(log(mat)), log(cmat));

            %log1p
            assertEqual(CircularMat(log1p(mat)), log1p(cmat));

            %log2
            assertEqual(CircularMat(log2(mat)), log2(cmat));

            %log10
            assertEqual(CircularMat(log10(mat)), log10(cmat));

            %reallog
            assertEqual(CircularMat(reallog(abs(mat))), reallog(abs(cmat)));
        end

        function testComplexNumbers(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3+sqrt(-1);
            mat(3, 1, 1, 1)=4-sqrt(-1);
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            %abs
            assertEqual(CircularMat(abs(mat)), abs(cmat));

            %sign
            assertEqual(CircularMat(sign(mat)), sign(cmat));

            %real
            assertEqual(CircularMat(real(mat)), real(cmat));

            %imag
            assertEqual(CircularMat(imag(mat)), imag(cmat));

            %angle
            assertEqual(CircularMat(angle(mat)), angle(cmat));

            %conj
            assertEqual(CircularMat(conj(mat)), conj(cmat));

            %isreal
            assertEqual(isreal(mat), isreal(cmat));
        end

        function testSetOperators(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2=ones(4,1);
            cmat2=CircularMat(mat2);

            %unique
            assertEqual(CircularMat(unique(mat)), unique(cmat));
            assertEqual(CircularMat(unique(mat2)), unique(cmat2));
            assertEqual(CircularMat(unique(zeros(0,1))), unique(CircularMat(zeros(0,1))));
            assertEqual(CircularMat(unique(zeros(1,0))), unique(CircularMat(zeros(1,0))));
        end

        function testAnyAll(this)
            import edu.stanford.covert.util.CircularMat;

            fl_x=zeros(3,2);
            fl_x(1,3)=2;
            fl_x(2,:)=-1;

            st_x=CircularMat(fl_x);

            %all
            assertEqual(CircularMat(all(fl_x)), all(st_x));
            assertEqual(CircularMat(all(fl_x,1)), all(st_x,1));
            assertEqual(CircularMat(all(fl_x,2)), all(st_x,2));
            assertEqual(CircularMat(all(fl_x,3)), all(st_x,3));

            %any
            assertEqual(CircularMat(any(fl_x)), any(st_x));
            assertEqual(CircularMat(any(fl_x,1)), any(st_x,1));
            assertEqual(CircularMat(any(fl_x,2)), any(st_x,2));
            assertEqual(CircularMat(any(fl_x,3)), any(st_x,3));
        end

        function testReduction(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = zeros(4,1,3,2);
            mat2(4, 1, 3, 1)=-1;
            mat2(2, 1, 3, 2)=2;
            mat2(1, 1, 1, 2)=3;
            mat2(1, 1, 2, 1)=4;
            mat2(3, 1, 2, 2)=Inf;
            mat2(4, 1, 2, 1)=NaN;
            mat2(3, 1, 2, 1)=-Inf;
            cmat2=CircularMat(mat2);

            %min
            assertEqual(CircularMat(min(mat,0)), min(cmat,0));
            assertEqual(CircularMat(min(mat,2)), min(cmat,2));
            assertEqual(CircularMat(min(mat,-2)), min(cmat,-2));
            assertEqual(CircularMat(min(mat,NaN)), min(cmat,NaN));
            assertEqual(CircularMat(min(mat,Inf)), min(cmat,Inf));
            assertEqual(CircularMat(min(mat,-Inf)), min(cmat,-Inf));

            assertEqual(CircularMat(min(0,mat)), min(0,cmat));
            assertEqual(CircularMat(min(2,mat)), min(2,cmat));
            assertEqual(CircularMat(min(-2,mat)), min(-2,cmat));
            assertEqual(CircularMat(min(NaN,mat)), min(NaN,cmat));
            assertEqual(CircularMat(min(Inf,mat)), min(Inf,cmat));
            assertEqual(CircularMat(min(-Inf,mat)), min(-Inf, cmat));

            assertEqual(CircularMat(min(mat,mat2)), min(cmat,cmat2));
            assertEqual(CircularMat(min(mat,mat2)), min(cmat,mat2));
            assertEqual(CircularMat(min(mat2,mat)), min(cmat2,cmat));
            assertEqual(CircularMat(min(mat,mat2)), min(mat2,cmat));

            assertEqual(CircularMat(min(mat)), min(cmat));
            assertEqual(CircularMat(min(mat,[],1)), min(cmat,[],1));
            assertEqual(CircularMat(min(mat,[],2)), min(cmat,[],2));
            assertEqual(CircularMat(min(mat,[],3)), min(cmat,[],3));
            assertEqual(CircularMat(min(mat,[],4)), min(cmat,[],4));
            assertEqual(CircularMat(min(mat,[],5)), min(cmat,[],5));

            %max
            assertEqual(CircularMat(max(mat,0)), max(cmat,0));
            assertEqual(CircularMat(max(mat,2)), max(cmat,2));
            assertEqual(CircularMat(max(mat,-2)), max(cmat,-2));
            assertEqual(CircularMat(max(mat,NaN)), max(cmat,NaN));
            assertEqual(CircularMat(max(mat,Inf)), max(cmat,Inf));
            assertEqual(CircularMat(max(mat,-Inf)), max(cmat,-Inf));

            assertEqual(CircularMat(max(0,mat)), max(0,cmat));
            assertEqual(CircularMat(max(2,mat)), max(2,cmat));
            assertEqual(CircularMat(max(-2,mat)), max(-2,cmat));
            assertEqual(CircularMat(max(NaN,mat)), max(NaN,cmat));
            assertEqual(CircularMat(max(Inf,mat)), max(Inf,cmat));
            assertEqual(CircularMat(max(-Inf,mat)), max(-Inf, cmat));

            assertEqual(CircularMat(max(mat,mat2)), max(cmat,cmat2));
            assertEqual(CircularMat(max(mat,mat2)), max(cmat,mat2));
            assertEqual(CircularMat(max(mat2,mat)), max(cmat2,cmat));
            assertEqual(CircularMat(max(mat,mat2)), max(mat2,cmat));

            assertEqual(CircularMat(max(mat)), max(cmat));
            assertEqual(CircularMat(max(mat,[],1)), max(cmat,[],1));
            assertEqual(CircularMat(max(mat,[],2)), max(cmat,[],2));
            assertEqual(CircularMat(max(mat,[],3)), max(cmat,[],3));
            assertEqual(CircularMat(max(mat,[],4)), max(cmat,[],4));
            assertEqual(CircularMat(max(mat,[],5)), max(cmat,[],5));

            %range
            assertEqual(CircularMat(range(mat)), range(cmat));
            assertEqual(CircularMat(range(mat,1)), range(cmat,1));
            assertEqual(CircularMat(range(mat,2)), range(cmat,2));
            assertEqual(CircularMat(range(mat,3)), range(cmat,3));
            assertEqual(CircularMat(range(mat,4)), range(cmat,4));
            assertEqual(CircularMat(range(mat,5)), range(cmat,5));

            %sum
            assertEqual(CircularMat(sum(mat)), sum(cmat));
            assertEqual(CircularMat(sum(mat,1)), sum(cmat,1));
            assertEqual(CircularMat(sum(mat,2)), sum(cmat,2));
            assertEqual(CircularMat(sum(mat,3)), sum(cmat,3));
            assertEqual(CircularMat(sum(mat,4)), sum(cmat,4));
            assertEqual(CircularMat(sum(mat,5)), sum(cmat,5));

            %mean
            assertEqual(CircularMat(mean(mat)), mean(cmat));
            assertEqual(CircularMat(mean(mat,1)), mean(cmat,1));
            assertEqual(CircularMat(mean(mat,2)), mean(cmat,2));
            assertEqual(CircularMat(mean(mat,3)), mean(cmat,3));
            assertEqual(CircularMat(mean(mat,4)), mean(cmat,4));
            assertEqual(CircularMat(mean(mat,5)), mean(cmat,5));

            %var
            assertElementsAlmostEqual(var(mat), double(var(cmat)));
            assertElementsAlmostEqual(var(mat,1), double(var(cmat,1)));

            % assertElementsAlmostEqual(var(mat,0,1), double(var(cmat,0,1)));
            % assertElementsAlmostEqual(var(mat,0,2), double(var(cmat,0,2)));
            % assertElementsAlmostEqual(var(mat,0,3), double(var(cmat,0,3)));
            % assertElementsAlmostEqual(var(mat,0,4), double(var(cmat,0,4)));
            % assertElementsAlmostEqual(var(mat,0,5), double(var(cmat,0,5)));

            assertElementsAlmostEqual(var(mat,1,1), double(var(cmat,1,1)));
            assertElementsAlmostEqual(var(mat,1,2), double(var(cmat,1,2)));
            assertElementsAlmostEqual(var(mat,1,3), double(var(cmat,1,3)));
            assertElementsAlmostEqual(var(mat,1,4), double(var(cmat,1,4)));
            assertElementsAlmostEqual(var(mat,1,5), double(var(cmat,1,5)));

            %std
            assertEqual(CircularMat(std(mat)), std(cmat));
            assertEqual(CircularMat(std(mat,1)), std(cmat,1));

            % assertEqual(CircularMat(std(mat,0,1)), std(cmat,0,1));
            % assertEqual(CircularMat(std(mat,0,2)), std(cmat,0,2));
            % assertEqual(CircularMat(std(mat,0,3)), std(cmat,0,3));
            % assertEqual(CircularMat(std(mat,0,4)), std(cmat,0,4));
            % assertEqual(CircularMat(std(mat,0,5)), std(cmat,0,5));

            assertEqual(CircularMat(std(mat,1,1)), std(cmat,1,1));
            assertEqual(CircularMat(std(mat,1,2)), std(cmat,1,2));
            assertEqual(CircularMat(std(mat,1,3)), std(cmat,1,3));
            assertEqual(CircularMat(std(mat,1,4)), std(cmat,1,4));
            assertEqual(CircularMat(std(mat,1,5)), std(cmat,1,5));

            %norm
            mat = reshape(1:20,4,5);
            cmat = CircularMat(mat);
            assertEqual(norm(mat,1), norm(cmat,1));
            assertEqual(norm(mat,2), norm(cmat,2));
            assertEqual(norm(mat,Inf), norm(cmat,Inf));
            assertEqual(norm(reshape(mat,[],1),-Inf), norm(reshape(cmat,[],1),-Inf));
            assertEqual(norm(mat,'fro'), norm(cmat,'fro'));
        end

        function testIss(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            assertEqual(CircularMat(isnan(mat)), isnan(cmat));
            assertEqual(CircularMat(isinf(mat)), isinf(cmat));
            assertEqual(CircularMat(isfinite(mat)), isfinite(cmat));
        end

        %type casting
        function testTypeCasting(this)
            import edu.stanford.covert.util.CircularMat;

            mat = zeros(4,1,3,2);
            mat(4, 1, 3, 1)=-1;
            mat(3, 1, 2, 2)=2;
            mat(1, 1, 1, 2)=3;
            mat(3, 1, 1, 1)=4;
            mat(3, 1, 2, 1)=Inf;
            mat(1, 1, 2, 1)=NaN;
            mat(2, 1, 2, 1)=-Inf;
            cmat = CircularMat(mat);

            mat2 = sparse(permute(sum(mat,4),[1 3 2]));
            cmat2 = CircularMat(mat2);

            %sparse
            assertEqual(mat2, sparse(cmat2));

            %cast
            assertEqual(cast(mat,'double'), cast(cmat,'double'));
            assertEqual(cast(mat,'int32'), cast(cmat,'int32'));

            warning('off','MATLAB:nonIntegerTruncatedInConversionToChar')
            assertEqual(char(mat), char(cmat));
            warning('on','MATLAB:nonIntegerTruncatedInConversionToChar')

            exception=[];
            try
                assertEqual(logical(mat), logical(cmat));
            catch exception
            end
            if isempty(exception)
                throw(MException('CircularMat:typeConversion','NaN''s cannot be converted to logicals'));
            end
            assertEqual(int8(mat), int8(cmat));
            assertEqual(int16(mat), int16(cmat));
            assertEqual(int32(mat), int32(cmat));
            assertEqual(int64(mat), int64(cmat));
            assertEqual(uint8(mat), uint8(cmat));
            assertEqual(uint16(mat), uint16(cmat));
            assertEqual(uint32(mat), uint32(cmat));
            assertEqual(uint64(mat), uint64(cmat));
            assertEqual(single(mat), single(cmat));
            assertEqual(double(mat), double(cmat));
        end
    end
end


% FT is the enumeration class for the typel of fields.
classdef FT < Enumerated
	enumeration
		e('E')
		h('H')
	end

	methods (Static)
		function elems = elems(ind)
			elems = [FT.e, FT.h];
			if nargin > 0  % ind
				elems = elems(ind);
			end
		end

		function count = count()
			count = length(FT.elems);
		end
	end

	methods
		function f = alter(this)
			% This function simply returned FT.h if this == FT.e and FT.e if
			% this == FT.h originally, but it is modified to handle arguments
			% given as arrays.
			chkarg(istypeof(this, 'FT'), '"this" should have FT as elements.');
			if isempty(this)  % alter([]) makes no sense, but alter(FT.empty()) does
				f = [];
			else
				f = this.elems(FT.count + 1 - int(this));
			end
		end
	end
end


function DrawRoute(X, Y)
for i = 2: length(X)
    plot([X(i - 1), X(i)], [Y(i - 1), Y(i)], 'b-')
    hold on
    pause(0.1)
    frame = getframe(gcf);
    imind = frame2im(frame);
    [imind, cm] = rgb2ind(imind, 256);
    if i == 2
        imwrite(imind, cm, 'test.gif', 'gif', 'Loopcount', inf, 'DelayTime', 1e-4);
    else
        imwrite(imind, cm, 'test.gif', 'gif', 'WriteMode', 'append', 'DelayTime', 1e-4);
    end

end


% Script demo_dim_info
% Exemplifies creation and usage of MrDimInfo class for retrieving and
% manipulating indices of multi-dimensional array
%
%  demo_dim_info
%
%
%   See also MrDimInfo

% Author:   Saskia Bollmann & Lars Kasper
% Created:  2016-01-23
% Copyright (C) 2016 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public License (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

clear;
close all;
clc;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1. Construct common dimInfo objects:
%   a) 4D EPI-fMRI array, with fixed TR
%   b) 5D multi-coil time series
%   c) 5D multi-echo time series
%   d) Create 5D multi-coil time series via nSamples and ranges
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  a) creates standard 4D dim info array from arraySize
% presets: units, dimLabels, resolutions
arraySize   = [64 50 33 100];
dimInfo     = MrDimInfo('nSamples', arraySize);
disp(dimInfo);

%% b) creates standard 5D dim info array from arraySize and resolutions
% presets of dimLabels, startingPoint = [1 1 1 1 1];
arraySize   = [64 50 33 100 8];
resolutions = [3 3 3 2.5 1];
units       = {'mm', 'mm', 'mm', 's', 'nil'};
dimInfo2    = MrDimInfo('nSamples', arraySize, 'resolutions', resolutions, ...
    'units', units);
disp(dimInfo2);
% no empty units allowed, will be overwritten by default!

%% c) creates standard 5D dim info array from arraySize, resolutions,
% startingPoints
% no presets
arraySize   = [64 50 33 8 3];
resolutions = [3 3 3 1 25];
units       = {'mm', 'mm', 'mm', 'nil', 'ms'};
dimLabels   = {'x', 'y', 'z', 'coil', 'echo_time'};
firstSamplingPoint = [-110, -110, -80, 0, 15];
dimInfo3    = MrDimInfo('nSamples', arraySize, 'resolutions', resolutions, ...
    'units', units, 'dimLabels', dimLabels, ...
    'firstSamplingPoint', firstSamplingPoint);
disp(dimInfo3.ranges);

%% d) Create 5D multi-coil time series via nSamples and ranges
% no presets, resolutions computed automatically
dimInfo4 = MrDimInfo(...
    'nSamples', [128 96 35 8 1000], ...
    'dimLabels', {'x', 'y', 'z', 'coil', 't'}, ...
    'units', {'mm', 'mm', 'mm', '', 's'}, ...
    'ranges', {[2 256], [2 192], [3 105], [1 8], [0 2497.5]});
disp(dimInfo4);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Get parameters of dimInfo via get_dims and dimInfo.'dimLabel'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% check out how neatly you can retrieve information about certain
% dimensions:
% dimInfo2.get_dims('x').resolutions
dimInfo2.x.resolutions
dimInfo2.get_dims('x')
dimInfo2.get_dims({'x' 'y' 'z'})

%% even cooler: use dimLabels directly for referencing!
dimInfo2.z.samplingPoints
dimInfo2.nSamples('z')
dimInfo2.nSamples({'z','y'})

dimInfo2('z')
dimInfo2({'z', 'y'})
dimInfo2([3 2])

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2. Modify dimInfo-dimensions via set_dims/add_dims-command
% a) Specify non-consecutive sampling-points (e.g. coil channels)
% b) Shift start sample of dimensions (e.g. centre FOV in x/y)
% c) Add a 6th dimension (e.g. additinal echoes)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  a) Specify non-consecutive sampling-points (e.g. coil channels)
disp(dimInfo3);
dimInfo3.set_dims('coil', 'samplingPoints', [2 3 4 7 8 10 11 12])
% Note that there is no concept of resolution here anymore, since there is
% equidistant spacing!
dimInfo3.resolutions
% However, samplingWidths is retained, since the data is still coming from
% one coil (a better way to think of it would be to select a number of
% slices; the resolution would be nan but each slice would still have a
% thickness = samplingWidth [which is the true slice thickness, resolution
% would be composed of samplingWidth + sliceGap])
dimInfo3.samplingWidths

% b) Shift start sample of dimensions (e.g. centre FOV in x/y)
disp(dimInfo4);
dimInfo4.set_dims([1 2], 'arrayIndex', [65 49], 'samplingPoint', [0 0]);
dimInfo4.ranges(:,1:2)

disp(dimInfo3);
dimInfo3.samplingPoints('coil') = {13:15};
dimInfo3.z.samplingPoints = {1:20};
dimInfo3.echo_time.nSamples = 5;

% c) Add a 6th dimension (e.g. additinal echoes)
disp(dimInfo2);
dimInfo2.add_dims(6, 'samplingPoints', [17 32], 'dimLabels', 'echo', 'units', 'ms');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3. Display sampling points (=absolute indices with units) of
% selected first/center/last voxel
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

arrayIndexFirst     = [1,1,1,1,1];
arrayIndexLast      = [128 96 35 8 1000];
arrayIndexCenter    = [64, 48, 18, 4, 500];

arrayIndices = [
    arrayIndexFirst
    arrayIndexCenter
    arrayIndexLast
    ];

nVoxels = size(arrayIndices, 1);

samplingPointArray = dimInfo4.index2sample(...
    arrayIndices);

fprintf('===\ndimInfo.sample2index(arrayIndices): \n');
for iVoxel = 1:nVoxels
    fprintf('array Index, voxel %d:', iVoxel);
    fprintf('%5.1f  ', samplingPointArray(iVoxel,:));
    fprintf('\n');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2. Display strings with units: sampling point of selected
%   first/center/last voxel
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

indexLabelArray = dimInfo4.index2label(...
    arrayIndices);

fprintf('===\ndimInfo.index2label(arrayIndices): \n');
for iVoxel = 1:nVoxels
    fprintf('Voxel %d: ', iVoxel);
    fprintf('%s ',indexLabelArray{iVoxel}{:});
    fprintf('\n\n');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3. Back-transform: Retrieve voxel index from absolute index
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

retrievedVoxelIndexArray = dimInfo4.sample2index(samplingPointArray);

fprintf('===\ndimInfo.sample2index(samplingPointArray): \n');
for iVoxel = 1:nVoxels
    fprintf('retrieved array index, voxel %d:', iVoxel);
    disp(retrievedVoxelIndexArray(iVoxel,:));
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 4. dimInfo.select() - extract subset of dimension info from
%                        PropName/Value-pairs
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  a) Select only data from a specific subset of one dimension (e.g. all
% data from some coils)
[selectionDimInfo, selectionIndexArray] = dimInfo4.select('type', 'index', ...
    'coil', [2 3 5 6]);

%% b) Select all data excluding subsets of one dimension (e.g. first volumes
% of time series data)
[selectionDimInfo2, selectionIndexArray2] = dimInfo4.select('type', 'index', ...
    'invert', true, 't', [1:5]);

%% c) Select subset of data array from multiple dimensions (e.g. cubic volume ROI,
% but see MrRoi for more sophisticated region definitions)
[selectionDimInfo3, selectionIndexArray3] = dimInfo4.select('type', 'index', ...
    'x', [20:40 80:100],  'y', [1:16 81:96], 'z', [10:15]);

%% d) Select subset of data array by specifying sampling-points
[selectionDimInfo4, selectionIndexArray4] = dimInfo4.select('type', 'sample', ...
    'x', [-128:2:0],  'y', [0:2:80]);

%% e) Combine selection into a nice structure, instead of
% ParameterName/Value-pairs (not all have to be given!)
selection.x = 1:10;
selection.t = 200:300;
[selectionDimInfo5, selectionIndexArray5] = dimInfo4.select(selection);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 5. dimInfo = MrDimInfo(fileName) - extract dimInfo directly from file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  3D Nifti
dataPath = tapas_uniqc_get_path('data');
niftiFile3D = fullfile(dataPath, 'nifti', 'rest', 'meanfmri.nii');
dimInfo3DFile = MrDimInfo(niftiFile3D);

% 4D Nifti
niftiFile4D = fullfile(dataPath, 'nifti', 'rest', 'fmri_short.nii');
dimInfo4DFile = MrDimInfo(niftiFile4D);

% 5D nifti
niftiFile5D = fullfile(dataPath, 'nifti', '5D', 'y_5d_deformation_field.nii');
dimInfo5DFile = MrDimInfo(niftiFile5D);

% several files in folder
niftiFolder = fullfile(dataPath, 'nifti', 'split', 'full');
dimInfoFolder = MrDimInfo(niftiFolder);

% par/rec
parRecFile = fullfile(dataPath, 'parrec', 'rest_feedback_7T', 'fmri1.par');
dimInfoParRec = MrDimInfo(parRecFile);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 6. Create MrDimInfo object from dimInfo struct (MrDimInfo(dimInfoStruct))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% creat MrDimInfo object
dimInfoObject = MrDimInfo('nSamples', [64 50 33 100], ...
    'units', {'u1', 'u2', 'u3', 'u4'}, ...
    'dimLabels', {'l1', 'l2', 'l3', 'l4'}, ...
    'resolutions', [0.3 2 0.85 5]);
disp(dimInfoObject);
% convert to struct (gives a warning)
dimInfoStruct = struct(dimInfoObject);
disp(dimInfoStruct);
% now create a new dimInfo object using the struct as input
newDimInfoObject = MrDimInfo(dimInfoStruct);


function [feat, x, y, wid, hgt] = extract_feature(feature, img, c)
%
% Copyright Aditya Khosla http://mit.edu/khosla
%
% Please cite this paper if you use this code in your publication:
%   A. Khosla, J. Xiao, A. Torralba, A. Oliva
%   Memorability of Image Regions
%   Advances in Neural Information Processing Systems (NIPS) 2012
%

if(~exist('c', 'var'))
  c = conf();
end

[feat, x, y, wid, hgt] = feval(['extract_' feature], img, c);


function [w,scnl]=obspy.stream2waveform(path_to_python, path_to_converter, path_to_data)
%OBSPY.STREAM2WAVEFORM Use ObsPy to read a stream object and then convert
% into a waveform object. Also return a scnlobject.
% Example:
%  [w,scnl]=obspy.stream2waveform('/Users/glennthompson/anaconda/bin/python', ...
%       '/Users/glennthompson/obspy_stream2matfile.py', ...
%       'https://examples.obspy.org/BW.BGLD..EH.D.2010.037')
%  plot(w)
    commandstr=sprintf('%s %s "%s"',path_to_python, path_to_converter, path_to_data);
    system(commandstr);
    clear commandstr fname
    d=dir('obspy.stream.*.mat');
    disp(sprintf('%d trace matfiles found\n',length(d)))
    for c=1:length(d)
        tr=load(d(c).name);
        disp(sprintf('Loaded trace %d',c))
        scnl(c)=scnlobject(tr.station, tr.channel, tr.network, tr.location);
        snum=(tr.starttime.timestamp/86400)+datenum(1970,1,1);
        w(c)=waveform(scnl, tr.sampling_rate, snum, tr.data);
        delete(d(c).name);
    end
end


function [sFile, nEpochs] = in_epochs_nwb(sFile, nwb2)
% IN_TRIALS_NWB Read trials from a Neurodata Without Borders .nwb file.

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Konstantinos Nasiotis, 2019,2020

% Check if there are trials present
if isempty(nwb2.intervals_epochs)
    nEpochs = 1;
    sFile.prop.nAvg = 1;
    sFile.format    = 'NWB-CONTINUOUS';
    return

else
    % Get epochs
    timeBoundsTrials = double([nwb2.intervals_epochs.start_time.data.load nwb2.intervals_epochs.stop_time.data.load]);

    % Get number of epochs
    nEpochs = length(nwb2.intervals_epochs.start_time.data.load);

    % === EPOCHS FILE ===
    % Build epochs structure
    for iEpoch = 1:nEpochs
        try
            epochLabel = nwb2.intervals_epochs.tags.data.load(iEpoch);
            sFile.epochs(iEpoch).label       = ['Epoch #(' num2str(iEpoch) ') - ' epochLabel{1}];
        catch
            sFile.epochs(iEpoch).label       = ['Epoch #(' num2str(iEpoch) ')'];
        end
        sFile.epochs(iEpoch).times       = timeBoundsTrials(iEpoch,:);
%         sFile.epochs(iEpoch).samples     = round(sFile.epochs(iEpoch).times * sFile.prop.sfreq);
        sFile.epochs(iEpoch).nAvg        = 1;
        sFile.epochs(iEpoch).select      = 1;
        sFile.epochs(iEpoch).bad         = 0;
%         sFile.epochs(iEpoch).bad         = badTrials(iEpoch);
        sFile.epochs(iEpoch).channelflag = [];
    end

    sFile.format    = 'NWB';
end

end


function [DataMat, ChannelMat] = in_data_rhd( DataFile )
% IN_DATA_RHD: Read a RHD file.
%
% INPUT:
%    - DataFile : Full path to a recordings file (called 'data' files in Brainstorm)
% OUTPUT:
%    - DataMat : Brainstorm standard recordings ('data') structure

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Konstantinos Nasiotis 2022

%% ===== GET OPTIONS =====
% Get file info
[sFile, ChannelMat] = in_fopen_intan(DataFile); % This call gets all of the file info (channels, events etc.)

%% Now Read entire recording and assign to DataMat
% Initialize returned structures
DataMat = db_template('DataMat');
DataMat.Comment  = 'EEG/Intan';
DataMat.DataType = 'recordings';
DataMat.Device   = 'Intan';

isProgress = bst_progress('isVisible');
if ~isProgress
    bst_progress('start', 'Intan importer', 'Reading entire recording');
end
% Read entire file
DataMat.F = in_fread_intan(sFile, [], [], []);

% Add the events
DataMat.Events = sFile.events;

% Build time vector
DataMat.Time = sFile.prop.times(1):1/sFile.prop.sfreq:sFile.prop.times(2);

% ChannelFlag
DataMat.ChannelFlag = ones(size(DataMat.F,1), 1);

% Replace NaN with 0
DataMat.F(isnan(DataMat.F)) = 0;

% Save number of trials averaged
DataMat.nAvg = 1;

% Build comment
[fPath, fBase, fExt] = bst_fileparts(DataFile);
DataMat.Comment = fBase;

isProgress = bst_progress('isVisible');
if isProgress
    bst_progress('stop');
end
end


function o = npRegGet(options,name,default,flag)
%NPREGGET Get NPREG OPTIONS parameters.
%   VAL = NPREGGET(OPTIONS,'NAME') extracts the value of the named
%   parameter from npReg options structure OPTIONS, returning an empty
%   matrix if the parameter value is not specified in OPTIONS.  It is
%   sufficient to type only the leading characters that uniquely identify
%   the parameter.  Case is ignored for parameter names.  [] is a valid
%   OPTIONS argument.
%
%   VAL = NPREGGET(OPTIONS,'NAME',DEFAULT) extracts the named parameter
%   as above, but returns DEFAULT if the named parameter is not specified
%   (is []) in OPTIONS.  For example
%
%     val = npRegGet(opts,'UDiffTol',1e-4);
%
%   returns val = 1e-4 if the UDiffTol property is not specified in opts.
%
%   See also NPREGSET.
%
% author: Nathan D. Cahill
% email: nathan.cahill@rit.edu
% affiliation: Rochester Institute of Technology
% date: January 2014
% licence: GNU GPL v3 licence.
%
% Copyright Nathan D. Cahill
% Code available from https://github.com/tomdoel/npReg

% undocumented usage for fast access with no error checking
if (nargin == 4) && isequal(flag,'fast')
    o = npRegGetFast(options,name,default);
    return
end

if nargin < 2
    error('MATLAB:npRegGet:NotEnoughInputs', 'Not enough input arguments.');
end
if nargin < 3
    default = [];
end

if ~isempty(options) && ~isa(options,'struct')
    error('MATLAB:npRegGet:Arg1NotStruct',...
        'First argument must be an options structure created with NPREGSET.');
end

if isempty(options)
    o = default;
    return;
end

allfields = {'Display'};
try
    npRegFields = npRegOptionGetFields;
    allfields = [allfields; npRegFields];
catch
    lasterrstruct = lasterror;
    if strcmp(lasterrstruct.identifier, 'MATLAB:UndefinedFunction')
        % Function NPREGOPTIONGETFIELDS not found, so we assume npReg Toolbox not on path
        %   and use the "MATLAB only" struct.
        % clean up last error
        lasterr('');
    else
        rethrow(lasterror);
    end
end

Names = allfields;

name = deblank(name(:)'); % force this to be a row vector
j = find(strncmpi(name,Names,length(name)));
if isempty(j)               % if no matches
    error('MATLAB:npRegGet:InvalidPropName',...
        ['Unrecognized property name ''%s''.  ' ...
        'See NPREGSET for possibilities.'], name);
elseif length(j) > 1            % if more than one match
    % Check for any exact matches (in case any names are subsets of others)
    k = find(strcmpi(name,Names));
    if length(k) == 1
        j = k;
    else
        msg = sprintf('Ambiguous property name ''%s'' ', name);
        msg = [msg '(' Names{j(1),:}];
        for k = j(2:length(j))'
            msg = [msg ', ' Names{k,:}];
        end
        msg = sprintf('%s).', msg);
        error('MATLAB:npRegGet:AmbiguousPropName', msg);
    end
end

if any(strcmp(Names,Names{j,:}))
    o = options.(Names{j,:});
    if isempty(o)
        o = default;
    end
else
    o = default;
end

%------------------------------------------------------------------
function value = npRegGetFast(options,name,defaultopt)
%NPREGGETFAST Get NPREG OPTIONS parameter with no error checking.
%   VAL = NPREGGETFAST(OPTIONS,FIELDNAME,DEFAULTOPTIONS) will get the
%   value of the FIELDNAME from OPTIONS with no error checking or
%   fieldname completion. If the value is [], it gets the value of the
%   FIELDNAME from DEFAULTOPTIONS, another OPTIONS structure which is
%   probably a subset of the options in OPTIONS.
%

if isempty(options)
     value = defaultopt.(name);
     return;
end
% We need to know if name is a valid field of options, but it is faster to use
% a try-catch than to test if the field exists and if the field name is
% correct. If the options structure is from an older version of the
% toolbox, it could be missing a newer field.
try
    value = options.(name);
catch
    value = [];
    lasterr('');  % clean up last error
end

if isempty(value)
    value = defaultopt.(name);
end


function imData=bigread2(path_to_file,sframe,num2read)
%reads tiff files in Matlab bigger than 4GB, allows reading from sframe to sframe+num2read-1 frames of the tiff - in other words, you can read page 200-300 without rading in from page 1.
%based on a partial solution posted on Matlab Central (http://www.mathworks.com/matlabcentral/answers/108021-matlab-only-opens-first-frame-of-multi-page-tiff-stack)
%Darcy Peterka 2014, v1.0
%Darcy Peterka 2014, v1.1
%Darcy Peterka 2016, v1.2(bugs to dp2403@columbia.edu)
%Eftychios Pnevmatikakis 2016, v1.3 (added hdf5 support)
%Program checks for bit depth, whether int or float, and byte order.  Assumes uncompressed, non-negative (i.e. unsigned) data.
%
% Usage:  my_data=bigread('path_to_data_file, start frame, num to read);
% "my_data" will be your [M,N,frames] array.
%Will do mild error checking on the inputs - last two inputs are optional -
%if nargin == 2, then assumes second is number of frames to read, and
%starts at frame 1

[~,~,ext] = fileparts(path_to_file);

if strcmpi(ext,'.tiff') || strcmpi(ext,'.tif');

    %get image info
    info = imfinfo(path_to_file);

    % if ~isfield(info,'ImageDescription')
    %    blah=size(info);
    %    numFrames= blah(1);
    % else
    %     he=info.ImageDescription;
    %     numFramesStr = regexp(he, 'images=(\d*)', 'tokens');
    %     numFrames = str2double(numFramesStr{1}{1});
    % end
    blah=size(info);
    numFrames= blah(1);

    num_tot_frames=numFrames;

    %should add more error checking for args... very ugly code below.  works
    %for me after midnight though...
    if nargin<2
        sframe = 1;
    end
    if nargin<3
        num2read=numFrames-sframe+1;
    end
    if sframe<=0
        sframe=1;
    end
    if num2read<1
        num2read=1;
    end
    if sframe>num_tot_frames
        sframe=num_tot_frames;
        num2read=1;
        display('starting frame has to be less than number of total frames...');
    end
    if (num2read+sframe<= num_tot_frames+1)
        lastframe=num2read+sframe-1;
    else
        num2read=numFrames-sframe+1;
        lastframe=num_tot_frames;
        display('Hmmm...just reading from starting frame until the end');
    end

    bd=info.BitDepth;
    he=info.ByteOrder;
    bo=strcmp(he,'big-endian');
    if (bd==64)
        form='double';
    elseif(bd==32)
        form='single'
    elseif (bd==16)
        form='uint16';
    elseif (bd==8)
        form='uint8';
    end

    % Use low-level File I/O to read the file
    fp = fopen(path_to_file , 'rb');
    % The StripOffsets field provides the offset to the first strip. Based on
    % the INFO for this file, each image consists of 1 strip.

    he=info.StripOffsets;
    %finds the offset of each strip in the movie.  Image does not have to have
    %uniform strips, but needs uniform bytes per strip/row.
    idss=max(size(info(1).StripOffsets));
    ofds=zeros(numFrames);
    for i=1:numFrames
        ofds(i)=info(i).StripOffsets(1);
        %ofds(i)
    end
    sframemsg = ['Reading from frame ',num2str(sframe),' to frame ',num2str(num2read+sframe-1),' of ',num2str(num_tot_frames), ' total frames'];
    disp(sframemsg)
    pause(.2)
    %go to start of first strip
    fseek(fp, ofds(1), 'bof');
    %framenum=numFrames;
    framenum=num2read;
    imData=cell(1,framenum);

    he_w=info.Width;
    he_h=info.Height;
    % mul is set to > 1 for debugging only
    mul=1;
    if strcmpi(form,'uint16') || strcmpi(form,'uint8')
        if(bo)
            for cnt = sframe:lastframe
                %cnt;
                fseek(fp,ofds(cnt),'bof');
                tmp1 = fread(fp, [he_w he_h*mul], form, 0, 'ieee-be')';
                imData{cnt-sframe+1}=cast(tmp1,form);
            end
        else
            for cnt=sframe:lastframe
                % cnt;
                fseek(fp,ofds(cnt),'bof');
                tmp1 = fread(fp, [he_w he_h*mul], form, 0, 'ieee-le')';
                imData{cnt-sframe+1}=cast(tmp1,form);
            end
        end
    elseif strcmpi(form,'single')
        if(bo)
            for cnt = sframe:lastframe
                %cnt;
                fseek(fp,ofds(cnt),'bof');
                tmp1 = fread(fp, [he_w he_h*mul], form, 0, 'ieee-be')';
                imData{cnt-sframe+1}=cast(tmp1,'single');
            end
        else
            for cnt = sframe:lastframe
                %cnt;
                fseek(fp,ofds(cnt),'bof');
                tmp1 = fread(fp, [he_w he_h*mul], form, 0, 'ieee-le')';
                imData{cnt-sframe+1}=cast(tmp1,'single');
            end
        end
        elseif strcmpi(form,'double')
            if(bo)
                for cnt = sframe:lastframe
                    %cnt;
                    fseek(fp,ofds(cnt),'bof');
                    tmp1 = fread(fp, [he_w he_h*mul], form, 0, 'ieee-be.l64')';
                    imData{cnt-sframe+1}=cast(tmp1,'single');
                end
            else
                for cnt = sframe:lastframe
                    %cnt;
                    fseek(fp,ofds(cnt),'bof');
                    tmp1 = fread(fp, [he_w he_h*mul], form, 0, 'ieee-le.l64')';
                    imData{cnt-sframe+1}=cast(tmp1,'single');
                end
            end
    end
            %ieee-le.l64

        imData=cell2mat(imData);
        imData=reshape(imData,[he_h*mul,he_w,framenum]);
        fclose(fp);
        display('Finished reading images')
elseif strcmpi(ext,'.hdf5') || strcmpi(ext,'.h5');
    info = hdf5info(path_to_file);
    dims = info.GroupHierarchy.Datasets.Dims;
    if nargin < 2
        sframe = 1;
    end
    if nargin < 3
        num2read = dims(end)-sframe+1;
    end
    num2read = min(num2read,dims(end)-sframe+1);
    imData = h5read(path_to_file,'/mov',[ones(1,length(dims)-1),sframe],[dims(1:end-1),num2read]);
else
    error('Unknown file extension. Only .tiff and .hdf5 files are currently supported');
end


% load data

load initData

% set irradiance

irradiance = StationData(1).Irradiance;


function t = isFactored(disc)
%ISFACTORED   Check if a useful factorization of a VALSDISCRETIZATION is stored.
%   ISFACTORED(DISC) returns true if the discretization has stored LU
%   factors whose dimensions are compatible with the current discretization
%   size.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Assume false:
t = false;

% Check whether any factorisation data is stored:
if ( ~isempty(disc.mldivideData) )
    % Total discretization size is the sum over the pieces, times the
    % number of function variables, plus the number of scalar variables.
    isFun = isFunVariable(disc.source);
    len = sum(isFun)*sum(disc.dimension) + sum(~isFun);

    % Compare length to that of stored data.
    L = disc.mldivideData{1};
    if ( len == size(L, 1) )
        t = true;
    end
end

end


function[blobSubY, blobSubX] = blobToImageSubs(blob)
% [blobSubY, blobSubX] = blobToImageSubs(blob)
%
% Convert a blob and an image to the indices of that blob in the image.
% These indices are relative to the size of the whole image. We don't need
% to know the image size for that!
% See "help blob" for more information.
%
% Copyright by Holger Caesar, 2014

% % For Matlab coder (not used)
% assert(isa(blob, 'struct'));
% assert(isa(blob.rect, 'double'));
% assert(all(size(blob.rect) == [1, 4]));
% assert(isa(blob.mask, 'logical'));
% assert(all(size(blob.mask) >= [1, 1]));
% assert(isa(blob.size, 'double'));
% assert(all(size(blob.size) == [1, 1]));

% Get all pix. coords for the mask
[blobSubY, blobSubX] = find(blob.mask);

% Make sure blobSub* are vectors (necessary when mask is a row vector)
blobSubY = blobSubY(:);
blobSubX = blobSubX(:);

% Translate from blob coords to image coords
blobSubY = blobSubY + blob.rect(1) - 1;
blobSubX = blobSubX + blob.rect(2) - 1;


function [V,F3,F4]=loadawobj(modelname,opts)
% loadawobj
% Load an Wavefront/Alias obj style model. Will only consider polygons with
% 3 or 4 vertices.
% Programme will also ignore normal and texture data. It will also ignore any
% part of the obj specification that is not a polygon mesh, ie nurbs, these
% deficiencies can probably be remedied relatively easily
%
% [v,f3,f4]=loadawobj(modelname)
%
% To come, s=loadawobj(modelname) where s is a structure
% s should contain containing texture coords, vertex norms, vn, sub
% ranges g3 and g4, and possibly even faces > 4
%
% See also Anders Sandberg's vertface2obj.m and saveobjmesh.m
%
% there is still a good chance that obj files will not load. I
% would be grateful for any reports and examples of those that fail.
%
% W.S. Harwin, University Reading, 2006,2010.
% Matlab BSD license
% thanks also to Doug Hackett

version=0.2;
if nargin <1
  disp('specify model name')
end

fid = fopen(modelname,'r');
if (fid<0)
  error(['can not open file: ' modelname]);
  return ;
end

vnum=1;
f3num=1;
f4num=1;
vtnum=1;
vnnum=1;
g3num=1;
g4num=1;
Vtmp=[];

% Line by line passing of the obj file

while ~feof(fid)
  Ln=fgets(fid);
  Ln=removespace(Ln);
  objtype=sscanf(Ln,'%s',1);
  l=length(Ln);
  if l==0  % isempty(s) ;
%    disp(['empty' Ln]);
    continue
  end
%  disp(Ln)
  Lyn=Ln; %temp hack
  switch objtype
    case '#' % comment
      disp(Ln);
    case 'v' % vertex
      v=sscanf(Ln(2:end),'%f');
      Vtmp(:,vnum)=v;
      vnum=vnum+1;
    case 'vt'	% textures
      if vtnum==1
        disp(Ln);
        vtnum=vtnum+1;
      end
    case 'g' % sub mesh
      disp(Ln);
%      if f3num > g3num(end);
      g3num=[g3num f3num];
%      if f4num> g4num(end)
      g4num=[g4num f4num];
    case 'mtllib' % material library
        disp(Ln);
    case 'usemtl' % use this material name
        disp(Ln);
    case 'l' % Line
        disp(Ln);
    case 's' %smooth shading across polygons
        disp(Ln);
    case 'vn' % normals
      if vnnum==1
        disp(Ln);
        vnnum=vnnum+1;
      end
    case 'f' % faces
      nvrts=length(findstr(Ln,' ')); % spaces as a predictor of n vertices
      slashpat=findstr(Ln,'/');
      nslash=length(slashpat);
      if nslash >1 % dblslash can be 0, 1 or >1
        dblslash=slashpat(2)-slashpat(1); else dblslash=0;
      end
      Ln=Ln(3:end); % get rid of the f
      if nslash == 0 % Face = vertex
%        disp('-v');
        f1=sscanf(Ln,'%f');
      elseif nslash == nvrts && dblslash>1 % Face = v/tc
%        disp('-v/tc');
        data1=sscanf(Ln,'%f/%f');
        if nvrts == 3
          f1=data1([1 3 5]);
          tc1=data1([2 4 6]);
        end
        if nvrts == 4;
          f1=data1([1 3 5 7]);
          tc1=data1([2 4 6 8]);
        end
      elseif nslash == 2*nvrts && dblslash==1 % v//n
%        disp('-v//n');
        data1=sscanf(Ln,'%f//%f');
        if nvrts == 3
          f1=data1([1 3 5]);
          vn1=data1([2 4 6]);
          Vn3(:,f3num)=f1;
        end
        if nvrts == 4;
          f1=data1([1 3 5 7]);
          vn11=data1([2 4 6 8]);
          Vn4(:,f4num)=f1;
        end
      elseif nslash == 2*nvrts && dblslash>1 % v/tc/n
%        disp('-v/tc/n');
        data1=sscanf(Ln,'%f/%f/%f');
        if nvrts == 3
          f1=data1([1 4 7]);
          tc1=data1([2 5 8]);
          vn1=data1([3 6 9]);
          Vn3(:,f3num)=f1;
        end
        if nvrts == 4;
          f1=data1([1 4 7 10]);
          tc1=data1([2 5 8 11]);
          vn1=data1([3 6 9 12]);
          Vn4(:,f4num)=f1;
        end
      end
% Now put the data into the array(s)
      if nvrts == 3
          F3(:,f3num)=f1;
          f3num=f3num+1;
      elseif nvrts ==4
          F4(:,f4num)=f1;
          f4num=f4num+1;
      else
          warning(sprintf('v nvrts=%d %s',nvrts, Ln));
      end
    otherwise
%      if ~strcmp(Lyn,char([13 10])) % carriage return
%        if ~ all(Lyn == ' ') % ignore lines with only space
          disp(['unprocessed-' Ln '-']); % see what has not been processed
%          double(Ln)
          %        end
%      end
    end

end

fclose(fid);

% plot if no output arguments are given
if nargout ==0
  if exist('F3','var')
    patch('Vertices',Vtmp','Faces',F3','FaceColor','g');
  end
  if exist('F4','var')
    patch('Vertices',Vtmp','Faces',F4','FaceColor','b');
  end
  axis('equal')
  clear Vtmp F3 F4
end

if nargout >=2
  V=Vtmp;
  if ~ exist('F3','var')
    warning('No 3 element faces')
    F3=[];
  end
  if nargout ==3
    if ~ exist('F4','var')
      warning('No 4 element faces')
      F4=[];
    end
  end
end

if nargout ==1
  V.v=Vtmp;
  V.f3=F3;
  if exist('F4','var')
    V.f4=F4;end
  V.g3=g3num;
  V.g4=g4num;
  if exist('Vn3','var')
    V.Vn3=Vn3;  end
  if exist('Vn4','var')
    V.Vn4=Vn4;  end
end

function Lyn=removespace(Lyn)
% A not an elegant way to remove
% surplus space
Lyn=strtrim(Lyn);
Lyn=strrep(Lyn,'       ',' '); % 8-2 .. 12-6
Lyn=strrep(Lyn,'    ',' '); % 5-2 6-3 4-1
Lyn=strrep(Lyn,'  ',' '); % 3-2 2-1
Lyn=strrep(Lyn,'  ',' ');
Lyn=strrep(Lyn,char([13 10]),''); % remove cr/lf
Lyn=strrep(Lyn,char([10]),''); % remove lf


function [img,hdr] = makeAnalyzeFromIfiles(ifileDir, outFileName, options,startIndex)
% [img,hdr] = makeAnalyzeFromIfiles(ifileDir, [outFileName], [options],[startIndex])
%
% ifileDir shall include the filename prefix- eg. 'spgr1/I' for I-files
% that look like 'I.001', 'I.002.dcm', etc...
%
% outFileName should not include '.hdr' or '.img'- just the base name.
% It defaults to a reasonable file name in the current working directory.
%
% options is a cell array specifying various little options. Currently
% supported options:
%   'verbose' : displays various bits of information and progress (default).
%   'silent'  : opposite of verbose.
%
% We will grab all suitable I-Files in ifileDir, the startIndex input is
% removed. If you did acquire many anatomies using the same prescription,
% you shall put them in separate folders respectively. -- Junjie
%
% The data in the resulting Analyze file will be oriented according to
% the default Analyze convention- 'transverse unflipped' (orient code '0' in
% the .hdr file). That is, the image will be reoriented so that left-right is
% along the x-axis, anterior-posterior is along the y-axis, superior-inferior
% is along the z-axis, and the leftmost, anterior-most, superior-most point is
% at 0,0,0 (which, for Analyze, is the lower left-hand corner of the last slice).
%
% To help you find the 0,0,0 point, a 4-pixel rectange is drawn there with a pixel
% value equal to the maximum image intensity.
%
% The reorientation involves only cannonical rotations and mirror flips- no
% interpolation is performed. Also, note that the reorientation depends on info
% in the GE I-file header- if this info is wrong, the reorientation will be wrong.
%
% REQUIRES:
%   * Stanford Anatomy and filter functions
%
% SEE ALSO: loadAnalyze, saveAnalyze, analyze2mrGray, mrGray2Analyze,
% makeAnalyzeFromRaw (that does all the hard work).
%
% HISTORY:
%   2002.05.31 RFD (bob@white.stanford.edu): wrote it.
%   2002.06.05 RFD: got it to actually work reliably.
%   2002.06.14 RFD: cleaned up the code and added more comments.
%   2003.06.19 RFD: Moved logic for computing the transform to the
%   cannonical pixel order from makeAnalyzeFromRaw to a separate function
%   (computeCannonicalXformFromIfile). What does that have to do with this
%   function? Well, in doing so, things got simple enough that we can just
%   do everything in here.
%   2004.01.24 Junjie: disable the startIndex input, with explanation.
%   2004.06.17 RFD: optionally reurns img and hdr.

if(~exist('ifileDir','var') | isempty(ifileDir))
    [f,p] = uigetfile({'*.dcm','DICOM';'*.*','All files'}, 'Select the first raw image file...');
    if(isnumeric(f))
        disp('User canceled.'); return;
    end
    ifileDir = fullfile(p,f);
    if(~exist('outFileName','var') | isempty(outFileName))
        [p,f] = fileparts(ifileDir);
        outFileName = fullfile(pwd, f);
    end
end
if(~ischar(ifileDir))
    help(mfilename);
    return;
end

if(exist('outFileName','file'))
    error([outFileName 'exists!']);
end
if(~exist('options','var'))
    options = {};
end
% if(~exist('startIndex','var'))
%     startIndex=1;
% end
% indSuffix = sprintf('%03d',startIndex);
if exist('startIndex','var');
    warning('startIndex no longer a valid input. Each folder shall contain only one series of anatomies, so that the code auto detect first and last slices');
end

[img2std, ifileBaseName, mmPerVox, imDim, notes] = computeCannonicalXformFromIfile(ifileDir);

% Load the image data
disp(['Loading data from ', ifileDir, '...']);
img = int16(makeCubeIfiles(ifileDir, imDim([1,2]), 1:imDim(3))); %[startIndex:startIndex+imDim(3)-1]));

% The following does all the hard work.
%makeAnalyzeFromRaw(img, [ifileDir,'.001'], sprintf('%s.%03d',ifileDir,nSlices), nSlices, outFileName, options);
[img, mmPerVoxNew] = applyCannonicalXform(img, img2std, mmPerVox);

disp(['Saving ',outFileName,'.hdr and ',outFileName,'.img ...']);
hdr = saveAnalyze(img, outFileName, mmPerVoxNew, notes);

if(nargout<1)
    clear img hdr;
end
return;


function varargout = DEMO(varargin)
% Last Modified by GUIDE v2.5 05-Mar-2009 14:13:52

gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @DEMO_OpeningFcn, ...
                   'gui_OutputFcn',  @DEMO_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);

if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end

return

% --- Executes just before DEMO is made visible.
function DEMO_OpeningFcn(hObject, eventdata, handles, varargin)
global FSSettings StopByUser

FSSettings.ErrorEstMethod      = 'ProposedAB';
FSSettings.FSMethod            = 'SFS';
%------- Classifier Settings -------
FSSettings.GammaParam          = 0.015;
FSSettings.ConfMatSwitch       = 0;
FSSettings.PercTest            = 10;
%-- Sequential Selection Settings -----
FSSettings.MahalInfoLossMethod = 'on';
FSSettings.TotalNStepsThres    = 250;
FSSettings.LogViewOfIntStep    = 1;
%------------- ReliefF --------------
FSSettings.NCorePatterns  = 250;
FSSettings.NHits          = 10;
%------------------------------------

handles.output = hObject;
StopByUser = 0;
axes(handles.YelLinesAxes); set(gca, 'Visible', 'off');
handles.SliderValue = 10;
warning off all
guidata(hObject, handles);

% --- Outputs from this function are returned to the command line.
function varargout = DEMO_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
varargout{1} = handles.output;

% -----------------Data Load and View -----------------------------
function OpenDataFile_ClickedCallback(hObject, eventdata, handles)
handles.file = uigetfile('*.*');

global StopByUser

if handles.file~=0
    % Only for viewing purpose
    [Patterns, Targets] = DataLoadAndPreprocess(handles.file);
    handles.PatternsToRunFS = Patterns;
    handles.TargetsToRunFS  = Targets;
    StopByUser = 0;

    [NPatterns, KFeatures] = size(Patterns);
    axes(handles.FeatSelCurve);cla reset;
    axes(handles.ClassResAxes); cla reset;
    axes(handles.ClassesLegendAxes); cla reset;
    set(gca,'Visible','off');
    axes(handles.YelLinesAxes); set(gca, 'Visible', 'off');
    axis manual
    axes(handles.PatternsFeaturesAxes);cla reset;

    if NPatterns>KFeatures
        imagesc(1.00001*Patterns');
        xlabel('Patterns')
        ylabel('Features','Rotation',0,'Position',[0 0]);
    else
        imagesc(1.00001*Patterns);
        xlabel('Features')
        ylabel('Patterns','Rotation',0,'Position',[0 0]);
    end
    title(handles.file);
    colorbar;

     set(findobj(gcf,'Tag','ListSelFeats'), 'String', ...
         'Press Start to select features');
    guidata(hObject, handles);
end
return

% -----------------------------------------------------------------
function RunFeatSelection_ClickedCallback(hObject, eventdata, ...
                                                           handles)
global StopByUser  FSSettings

set(findobj(gcf,'Tag','ListSelFeats'), 'String', []);
axes(handles.FeatSelCurve); cla reset;
axes(handles.YelLinesAxes); cla reset; set(gca,'Visible','off',...
                                                'YDir','reverse');
StopByUser = 0;
guidata(hObject, handles);

FSSettings.FSMethod

if strcmp(FSSettings.FSMethod,'SFS')|| strcmp(FSSettings.FSMethod,'SFFS')
[ResultMat, ConfMatOpt, Tlapse, handles.OptimumFeatureSet,...
      OptimumCCR]= ForwSel_main(handles.file, FSSettings, handles);
elseif strcmp(FSSettings.FSMethod,'ReliefF')
[FeatureWeightsOrdered, FeaturesIndexOrdered, ...
    handles.OptimumFeatureSet] = ReliefF(handles.file,...
                                               FSSettings,handles);
elseif strcmp(FSSettings.FSMethod,'SBS') || ...
                                 strcmp(FSSettings.FSMethod,'SFBS')
[ResultMat, ConfMatOpt, Tlapse, handles.OptimumFeatureSet,...
     OptimumCCR]= BackSel_main(handles.file, FSSettings, handles);
end
handles.OptimumFeatureSet = sort(handles.OptimumFeatureSet);
guidata(hObject, handles);

% -----------------------------------------------------------------
function StopFeatSelButton_ClickedCallback(hObject, eventdata,...
                                                           handles)
global StopByUser
StopByUser = 1;
guidata(hObject, handles);
return

%------------------------------------------------------------------
function ListSelFeats_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), ...
                         get(0,'defaultUicontrolBackgroundColor'));
    set(hObject,'BackgroundColor','white');
end
set(hObject,'String','None Selected yet');

%------------------------------------------------------------------
function LoadAndClassifyButton_ClickedCallback(hObject,...
                                                eventdata, handles)

handles.fileToClassify = uigetfile('*.mat');
PatternsToClassify = DataLoadAndPreprocess(handles.fileToClassify);

handles.PatternsToClassify = PatternsToClassify(:,...
                                        handles.OptimumFeatureSet);

NPatternsToClassify = size(PatternsToClassify,1);

if NPatternsToClassify >= 10
 set(findobj(gcf,'Tag','ClassResSlider'), ...
                 'Max', NPatternsToClassify,...
                 'Min', 10, ...
                 'Value', 10, ...
       'SliderStep',[1 1]/(NPatternsToClassify-10),'Enable', 'on');
else
  set(findobj(gcf,'Tag','ClassResSlider'), 'Enable', 'off');
end
  handles.SliderValue = 10;

guidata(hObject, handles);
DEMO('ClassifyAndPlot',guidata(gcbo));
guidata(hObject, handles);

%------------------------------------------------------------------
function ClassResSlider_Callback(hObject, eventdata, handles)
handles.SliderValue = get(hObject,'Value');
guidata(hObject, handles);
DEMO('ClassifyAndPlot',guidata(gcbo));
guidata(hObject, handles);

%------------------------------------------------------------------
function ClassResSlider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), ...
                          get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

%------------------------------------------------------------------
function ClassifyAndPlot(handles)

PatternsToRunFS = handles.PatternsToRunFS(:,...
                                        handles.OptimumFeatureSet);

ProbsClass = BayesClassValidationSet(PatternsToRunFS,...
               handles.TargetsToRunFS, handles.PatternsToClassify);

NPatternsToClassify = size(ProbsClass,1);
SumProbsClass       = sum(ProbsClass,2);

for IndexPatterns = 1:NPatternsToClassify
    ProbsClass(IndexPatterns,:) = ProbsClass(IndexPatterns, :) ...
                                     /SumProbsClass(IndexPatterns);
end

[Dummy, PredictionClass] = max(ProbsClass, [], 2);
[NPatterns, CClasses]    = size(ProbsClass);
axes(handles.ClassResAxes); cla reset;
set(gca,'YDir','reverse','Color',[.925 .914 .847]);
axis([-0.5 1.5 0.5 10.5]);

CumProbsClass = [zeros(NPatterns,1) cumsum(ProbsClass,2)];
ColorsToUse   = 'rgbycmk';
YLocat        = 0;

handles.SliderValue = 10 + NPatterns - ceil(handles.SliderValue);

for IndexPattern = (handles.SliderValue-9):handles.SliderValue
    YLocat = YLocat + 1;
    if IndexPattern <= NPatterns
        for IndexClass = 1:CClasses
            hold on
            text(-0.15, YLocat, num2str(IndexPattern),...
                                   'HorizontalAlignment', 'right');
            plot(CumProbsClass(IndexPattern, ...
                IndexClass:(IndexClass+1)), ...
                YLocat*ones(1,2), ...
                ColorsToUse(IndexClass), 'LineWidth', 5);
        end
        text(1.2, YLocat, num2str(PredictionClass(IndexPattern)));
    end
end

text(-0.9  ,0.5,'Pattern #');
text(0.2   ,0.5,'P(x|\Omega_i)');
text(1     ,0.5,'Predict');
axis([-0.5 1.5 0.5 10.5]);
set(gca,'Visible','off');
drawnow

axes(handles.ClassesLegendAxes);
for IndexClass = 1:CClasses
    text(0.1, IndexClass*0.5, ['Class ' num2str(IndexClass)],...
                          'Color', ColorsToUse(IndexClass));
end
axis([0 1.5 0.5 5])

% ------------- Menu Functions ------------------------------------
function OpenDataMenu_Callback(hObject, eventdata, handles)
DEMO('OpenDataFile_ClickedCallback',gcbo,[],guidata(gcbo));

function RunFsMenu_Callback(hObject, eventdata, handles)
DEMO('RunFeatSelection_ClickedCallback',gcbo,[],guidata(gcbo));

function StopFSMenu_Callback(hObject, eventdata, handles)
DEMO('StopFeatSelButton_ClickedCallback',gcbo,[],guidata(gcbo));

function StandardCrossMenu_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.ErrorEstMethod = 'Standard';
set(hObject,'Checked','on');
set(findobj(gcf,'Tag','ResubMenu'),'Checked','off');
set(findobj(gcf,'Tag','ProposedACrossMenu'),'Checked','off');
set(findobj(gcf,'Tag','ProposedABCrossMenu'),'Checked','off');
guidata(hObject, handles);

function ProposedACrossMenu_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.ErrorEstMethod = 'ProposedA';
set(hObject,'Checked','on');
set(findobj(gcf,'Tag','ResubMenu'),'Checked','off');
set(findobj(gcf,'Tag','ProposedABCrossMenu'),'Checked','off');
set(findobj(gcf,'Tag','StandardCrossMenu'),'Checked','off');
guidata(hObject, handles);

function ProposedABCrossMenu_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.ErrorEstMethod = 'ProposedAB';
set(hObject,'Checked','on');
set(findobj(gcf,'Tag','ResubMenu'),'Checked','off');
set(findobj(gcf,'Tag','ProposedACrossMenu'),'Checked','off');
set(findobj(gcf,'Tag','StandardCrossMenu'),'Checked','off');
guidata(hObject, handles);

function ResubMenu_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.ErrorEstMethod = 'Resubstitution';
set(hObject,'Checked','on');
set(findobj(gcf,'Tag','ProposedABCrossMenu'),'Checked','off');
set(findobj(gcf,'Tag','ProposedACrossMenu'),'Checked','off');
set(findobj(gcf,'Tag','StandardCrossMenu'),'Checked','off');
guidata(hObject, handles);

%---------------- FS Menu Functions -------------------------------
function SFS_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.FSMethod  =  'SFS';
FS_Method_ClearAllChecks(hObject, handles)
set(hObject,'Checked','on');
guidata(hObject, handles);

function SFFS_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.FSMethod  = 'SFFS';
FS_Method_ClearAllChecks(hObject, handles)
set(hObject,'Checked','on');
guidata(hObject, handles);

function ReliefF_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.FSMethod  = 'ReliefF';
FS_Method_ClearAllChecks(hObject, handles)
set(hObject,'Checked','on');
guidata(hObject, handles);

function SBS_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.FSMethod  =  'SBS';
FS_Method_ClearAllChecks(hObject, handles)
set(hObject,'Checked','on');
guidata(hObject, handles);

function SFBS_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings.FSMethod  = 'SFBS';
FS_Method_ClearAllChecks(hObject, handles)
set(hObject,'Checked','on');
guidata(hObject, handles);

function FS_Method_ClearAllChecks(hObject, handles)
set(findobj(gcf,'Tag','ReliefF'),'Checked','off');
set(findobj(gcf,'Tag','SFFS'),'Checked','off');
set(findobj(gcf,'Tag','SFS'),'Checked','off');
set(findobj(gcf,'Tag','SFBS'),'Checked','off');
set(findobj(gcf,'Tag','SBS'),'Checked','off');
guidata(hObject, handles);

%------------ Help Menu -------------------------------------------
function InstructionsMenu_Callback(hObject, eventdata, handles)
web([pwd '\Help\Readme.htm'])

function PubMenu_1_Callback(hObject, eventdata, handles)
open('Help\Verver_ElsSigPro_08.pdf')

function AuthorsVerverMenu_Callback(hObject, eventdata, handles)
web([pwd '\Help\AuthorsVerveridis.htm'])

function AuthorsKotroMenu_Callback(hObject, eventdata, handles)
web(['http://poseidon.csd.auth.gr/LAB_PEOPLE/CKotropoulos.htm'])
%------------------------------------------------------------------
function SettingsMenu_Callback(hObject, eventdata, handles)
global FSSettings
FSSettings = SettingsMenuFig;


function sparse_display_test ( )

%*****************************************************************************80
%
%% SPARSE_DISPLAY_TEST tests the SPARSE_DISPLAY library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 September 2014
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SPARSE_DISPLAY_TEST\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the SPARSE_DISPLAY library.\n' );

  sparse_display_test01 ( );
  sparse_display_test02 ( );
  sparse_display_test03 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SPARSE_DISPLAY_TEST\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end


function out = isequal(f, g)
%ISEQUAL   Test if two CLASSICFUN objects are equal.
%   ISEQUAL(F, G) returns logical 1 (TRUE) if the CLASSICFUN objects F and G have the
%   same domain and .onefun fields which are equal, as determined by their
%   respective class methods.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Check whether .domains and .onefun fields agree:
out = (all(f.domain == g.domain) && isequal(f.onefun, g.onefun));

end


function neqn = p05_neqn ( )

%*****************************************************************************80
%
%% P05_NEQN returns the number of equations for problem P05.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer NEQN, the number of equations.
%
  neqn = 1;

  return
end


function yout = oneinsert(removed_voxels, data)
% Insert ones (for p values) back into data series (i.e., obj.p)
% from which they were removed.
%
% :Usage:
% ::
%
%     yout = oneinsert(removed_voxels, data)
%
% removed_voxels is indicator for the locations of removed voxels
%
% This function is a modified version of naninsert.m, but different.
%
% ..
%    Wani Woo, Aug 2012
% ..
%
% :See also:
% naninsert.m, nanremove.m, zeroinsert.m

yout = zeros(size(removed_voxels,1), size(data,2));
yout(removed_voxels,:) = 1;
yout(~removed_voxels,:) = data;

return