% SparseMat test cases
%
% Author: Jonathan Karr
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 9/5/2010
classdef SparseMat_Test < TestCase
    properties
        inefficientWarning
    end

    %constructor
    methods
        function this = SparseMat_Test(name)
            this = this@TestCase(name);
        end
    end

    methods
        function setUp(this)
            this.inefficientWarning = warning('query', 'SparseMat:inefficient');
            warning('off', 'SparseMat:inefficient');
        end

        function tearDown(this)
            warning(this.inefficientWarning.state, 'SparseMat:inefficient');
        end
    end

    %tests
    methods
        function testConstructor(~)
            import edu.stanford.covert.util.SparseMat;

            %1 argument (matrix)
            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;

            spmat = SparseMat(mat);
            [subs, vals]=find(spmat);
            assertEqual([4 3 2], size(spmat));
            assertEqual([3 1 1; 4 3 1; 1 1 2; 3 2 2], subs);
            assertEqual([4;1;3;2], vals);

            spmat = SparseMat(double(mat));
            assertEqual('double', valueClass(spmat));

            spmat = SparseMat(int32(mat));
            assertEqual('int32', valueClass(spmat));

            spmat = SparseMat(sparse(double(mat(:,:,1))));
            assertEqual('double', valueClass(spmat));

            spmat = SparseMat([],false(0,1), [1 1]);
            assertEqual('logical', valueClass(spmat));

            spmat = SparseMat([],false(0,0), [1 1]);
            assertEqual('logical', valueClass(spmat));

            spmat = SparseMat(false(0,1));
            assertEqual('logical', valueClass(spmat));

            %1 argument (struct)
            assertEqual(spmat, SparseMat(struct(...
                'subs', spmat.subs, 'vals', spmat.vals, 'siz', spmat.siz)));

            %3 arguments
            spmat = SparseMat([3 1 2; 1 2 1; 2 2 3],[1;2;3],[4 2 3]);
            [subs, vals]=find(spmat);
            assertEqual([1 2 1; 3 1 2; 2 2 3], subs);
            assertEqual([2;1;3], vals);
            assertEqual([4 2 3], size(spmat));
        end

        function testSubscriptReference(~)
            import edu.stanford.covert.util.SparseMat;

            mat = zeros(10,10);
            mat(10, 1) = 1;
            spmat = SparseMat(mat);

            assertEqual(zeros(0,1), spmat(zeros(0,2)))
            assertEqual(mat(sub2ind(size(mat), 10, 1)), spmat([10 1]))
            assertEqual(mat(sub2ind(size(mat), [10 10 10]', [1 2 3]')), spmat([10 1; 10 2; 10 3]))
            assertEqual(mat(sub2ind(size(mat), [10 10 10 2]', [2 1 1 1]')), spmat([10 2; 10 1; 10 1; 2 1]))

            assertEqual(SparseMat(mat([],:)), spmat([],:))
            assertEqual(SparseMat(mat([],[])), spmat([],[]))
            assertEqual(SparseMat(mat(1:2,:)), spmat(1:2,:))
            assertEqual(SparseMat(mat(1:10,:)), spmat(1:10,:))
            assertEqual(SparseMat(mat([10 10 9 8],:)), spmat([10 10 9 8], :))
            assertEqual(SparseMat(mat([10 10 9 8], [1 2 1])), spmat([10 10 9 8], [1 2 1]))
            assertEqual(SparseMat(mat(10, :)), spmat(10,:))
            assertEqual(SparseMat(mat(:, 1)), spmat(:,1))
            assertEqual(SparseMat(mat(:, 2)), spmat(:,2))
            assertEqual(SparseMat(mat(1:2,:,1)), spmat(1:2,:,1))

            mat = false(10, 10);
            mat(10, 1) = 1;
            spmat = SparseMat(mat);
            assertEqual(SparseMat(mat(1:2, :, 1)), spmat(1:2, :, 1))

            mat = zeros(10, 10);
            mat(1:2, 1:2) = [1 2; 3 4];
            spmat = SparseMat(mat);
            assertEqual(SparseMat(mat(1:2, 1:2)), spmat(1:2, 1:2))
        end

        function testSubscriptAssignment(~)
            import edu.stanford.covert.util.SparseMat;

            warning('off','SparseMat:invalidAssignment');

            spmat=SparseMat([],[],[10 10]);
            spmat([])=1;
            assertEqual(SparseMat([],[],[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(zeros(0,2))=1;
            assertEqual(SparseMat([],[],[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat([1 1])=1;
            assertEqual(SparseMat([1 1],1,[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat([1 1; 1 1])=1;
            assertEqual(SparseMat([1 1],1,[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat([1 1; 1 1])=[1;2];
            assertEqual(SparseMat([1 1],2,[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(1,1)=1;
            assertEqual(SparseMat([1 1],1,[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(1:10,1:2)=[(1:10)' zeros(10,1)];
            assertEqual(SparseMat([(1:10)' ones(10,1)],(1:10)',[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(2:8,1:2)=SparseMat([2 2],3,[7 2]);
            assertEqual(SparseMat([3 2],3,[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat([2 2],[1 1])=[1 2; 3 4];
            assertEqual(SparseMat([2 1],4,[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat([],1)=1;
            assertEqual(SparseMat([],[],[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat([],[])=1;
            assertEqual(SparseMat([],[],[10 10]), spmat)

            spmat=SparseMat([],[],[10 2]);
            exception = [];
            try
                spmat(1:2,:)=ones(2,3);
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Syntax should not be allowed'));
            end
            assertEqual(SparseMat([],[],[10 2]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(3:5,3) = [6;7;8];
            assertEqual(SparseMat([3 3; 4 3; 5 3],[6;7;8],[10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(1:2,1:2) = 1;
            assertEqual(SparseMat([1 1; 2 1; 1 2; 2 2],[1;1;1;1], [10 10]), spmat)

            spmat=SparseMat([],[],[10 10]);
            spmat(1:2,1:2) = [1 2;3 4];
            assertEqual(SparseMat([1 1; 2 1; 1 2; 2 2],[1;3;2;4], [10 10]), spmat)
        end

        function testSubscriptReferenceAssignment(~)
            import edu.stanford.covert.util.SparseMat;

            %example 1
            st_x=SparseMat([],[],[10 10 1]);

            st_x(10,5,1)=1;
            st_x(5,7,1)=3;
            st_x(8,6,1)=4;

            x=st_x(:,:,:);

            assertEqual(ones(10,10),double(eq(st_x, st_x(:,:,:))))
            assertEqual(ones(10,10),double(eq(st_x, st_x(:,:,1))))

            %example 2
            fl_x=zeros([10 10 1]);
            st_x=SparseMat([],[],[10 10 1]);
            assertEqual(size(fl_x(1,:,:)), size(st_x(1,:,:)))
            assertEqual(size(fl_x(:,1,:)), size(st_x(:,1,:)))

            %example 3
            fl_x=zeros([10 10 1]);
            st_x=SparseMat([],[],[10 10 1]);

            fl_x(3,2)=1;
            st_x(3,2)=1;

            assertEqual(size(fl_x(3,:,:)), size(st_x(3,:,:)))
            assertEqual(size(fl_x(:,2,:)), size(st_x(:,2,:)))

            %example 4
            st_x=SparseMat([],[],[500 1 1]);
            exception = [];
            try
                st_x(1:50);
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Linear indexing should not be valid'));
            end

            exception = [];
            try
                st_x((1:50)');
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Linear indexing should not be valid'));
            end

            %example 5
            st_x=SparseMat([],[],[500 1 1]);
            exception = [];
            try
                st_x(4,5,6);
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Should not be able to reference unitialized dimensions'));
            end

            exception = [];
            try
                st_x([4 5 6]);
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Should not be able to reference unitialized dimensions'));
            end

            %example 6
            st_x=SparseMat([],[],[500 2 1]);
            assertEqual([1 1],size(st_x(4,2,1,1)));
            assertEqual([1 1],size(st_x(4,2,1,:,1)));
            assertEqual([1 2],size(st_x(4,:,1,:,1)));
            assertEqual([500 2],size(st_x(:,:,1,:,1)));

            %example 7
            st_x=SparseMat([],[],[500 2 1]);
            fl_x=st_x([(1:50)' ones(50,1)]);
            assertEqual('edu.stanford.covert.util.SparseMat',class(st_x(1:50,1)));
            assertEqual('double',class(fl_x));
            assertEqual([50 1], size(fl_x));

            %example 8
            st_x=SparseMat([],[],[500 2 1]);
            fl_x=zeros(500,2);
            assertEqual(size(fl_x([],:)), size(st_x([],:)));
            assertEqual(size(fl_x([],2)), size(st_x([],2)));
            assertEqual([0 1], size(st_x(zeros(0,2))));

            %example 9
            st_x=SparseMat([],[],[3 2]);
            fl_x=zeros(3,2);

            st_x(1,1)=1;
            fl_x(1,1)=1;

            assertEqual(fl_x([1;1],1), double(st_x([1;1],1)));
            assertEqual(fl_x([1;1],:), double(st_x([1;1],:)));
            assertEqual(fl_x([1;1;2],:), double(st_x([1;1;2],:)));
            assertEqual(fl_x([1;2;1],:), double(st_x([1;2;1],:)));
            assertEqual(fl_x([1;2;1],2), double(st_x([1;2;1],2)));
            assertEqual(fl_x([1;2;2;1;3],2), double(st_x([1;2;3;1;3],2)));
            assertEqual(fl_x([1;1;2;2],:), double(st_x([1;1;2;2],:)));
            assertEqual(fl_x(sub2ind([3 2],[1;1],[1;1])), double(st_x([1 1; 1 1])));
            assertEqual(fl_x(sub2ind([3 2],[1;1;2],[1;1;1])), double(st_x([1 1; 1 1; 2 1])));

            st_x(2,2)=3;
            fl_x(2,2)=3;

            assertEqual(fl_x([1;1],1), double(st_x([1;1],1)));
            assertEqual(fl_x([1;1],:), double(st_x([1;1],:)));
            assertEqual(fl_x([1;1;2],:), double(st_x([1;1;2],:)));
            assertEqual(fl_x([1;2;1],:), double(st_x([1;2;1],:)));
            assertEqual(fl_x([1;2;1],2), double(st_x([1;2;1],2)));
            assertEqual(fl_x([1;2;2;1;3],2), double(st_x([1;2;2;1;3],2)));
            assertEqual(fl_x([1;1;2;2],:), double(st_x([1;1;2;2],:)));
            assertEqual(fl_x([1;2;2;1;3],:), double(st_x([1;2;2;1;3],:)));
            assertEqual(fl_x(sub2ind([3 2],[1;1],[1;1])), double(st_x([1 1; 1 1])));
            assertEqual(fl_x(sub2ind([3 2],[1;1;2],[1;1;1])), double(st_x([1 1; 1 1; 2 1])));
        end

        function testSubscriptAssignmentReference(~)
            import edu.stanford.covert.util.SparseMat;

            warning('off','SparseMat:invalidAssignment');

            %example 1
            st_x=SparseMat([],[],[10 10 1]);
            st_x(8,6,:)=4;
            assertEqual([10 10],size(st_x))

            %example 2
            st_x=SparseMat([],[],[500 1 1]);
            exception = [];
            try
                st_x(1:50)=1;
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Invalid syntax'));
            end

            %example 3
            st_x=SparseMat([],[],[500 1 1]);
            exception = [];
            try
                st_x((1:50)')=1;
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Linear indexing should not be valid'));
            end

            %example 4
            st_x=SparseMat([],[],[100 1 1]);
            st_y=SparseMat(ones(10,1));
            exception = [];
            try
                st_x(1:10)=st_y;
            catch exception
            end
            if isempty(exception)
                throw(mexception('SparseMat_test:error', 'linear indexing should not be valid'));
            end

            st_z=SparseMat([],[],[100 1 1]);
            exception = [];
            try
                st_z(1:10)=1;
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_Test:error', 'Invalid syntax'));
            end

            %example 5
            st_x=SparseMat([],[],[100 1 1]);
            st_y=SparseMat(ones(10,1));
            exception = [];
            try
                st_x((1:10)')=st_y;
            catch exception
            end
            if isempty(exception)
                throw(mexception('SparseMat_test:error', 'linear indexing should not be valid'));
            end

            %example 6
            st_x=SparseMat([],[],[100 1 1]);

            st_x([(1:10)' ones(10,1)])=1;
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x([(1:10)' ones(10,1)])=SparseMat(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x([(1:10)' ones(10,1)])=ones(10,1);
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x([(1:10)' ones(10,1)])=1;
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x([(1:10)' ones(10,1)])=sparse(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:10,1)=SparseMat(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:10,1)=ones(10,1);
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:10,1)=1;
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:10,1)=sparse(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            %example 7
            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:10)' ones(10,1)])=SparseMat(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:10)' ones(10,1)])=ones(10,1);
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:10)' ones(10,1)])=1;
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:10)' ones(10,1)])=sparse(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:10,1)=SparseMat(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:10,1)=ones(10,1);
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:10,1)=1;
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:10,1)=sparse(ones(10,1));
            assertEqual(ones(10,1), double(st_x(1:10,1)));
            assertEqual([100 1],size(st_x));

            %example 8
            st_x=SparseMat([],[],[100 1 1]);

            st_x([(1:4)' ones(4,1)])=SparseMat([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x([(1:4)' ones(4,1)])=[1 0 1 0]';
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x([(1:4)' ones(4,1)])=sparse([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:4,1)=SparseMat([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:4,1)=[1 0 1 0]';
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x(1:4,1)=sparse([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            %example 9
            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:4)' ones(4,1)])=SparseMat([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:4)' ones(4,1)])=[1 0 1 0]';
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x([(1:4)' ones(4,1)])=sparse([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:4,1)=SparseMat([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:4,1)=[1 0 1 0]';
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            st_x=SparseMat([],[],[100 1 1]);
            st_x(1:4,1)=sparse([1 0 1 0]');
            assertEqual([1 0 1 0]', double(st_x(1:4,1)));
            assertEqual([100 1],size(st_x));

            %example 10
            st_x=SparseMat([],[],[100 2]);
            st_x(:,:)=1;
            assertEqual(ones(100,2), double(st_x));

            st_x=SparseMat([],[],[4 2]);
            st_x(:,:)=1;
            st_x(1:2,1)=SparseMat([],[],[2 1]);
            assertEqual([zeros(2,1) ones(2,1);ones(2,2)], double(st_x));

            st_x=SparseMat([],[],[4 2]);
            st_x(:,:)=1;
            st_x(1:2,:)=0;
            assertEqual([zeros(2,2);ones(2,2)], double(st_x));

            st_x=SparseMat([],[],[4 2]);
            st_x(:,:)=1;
            st_x(1:2,1)=0;
            assertEqual([zeros(2,1) ones(2,1);ones(2,2)], double(st_x));

            st_x=SparseMat([],[],[100 2]);
            st_x(:,:)=1;
            st_x(:,:)=0;
            assertEqual(SparseMat([],[],[100 2]), st_x);

            %example 11
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;
            st_x([],1)=2;
            assertEqual(st_y, st_x);

            %example 12
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;
            st_x([],:)=2;
            assertEqual(st_y, st_x);

            %example 13
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;
            st_x([],:)=zeros(0,2);
            assertEqual(st_y, st_x);

            %example 14
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;
            st_x([],:)=SparseMat;
            assertEqual(st_y, st_x);

            %example 15
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;

            exception = [];
            try
                st_x(1,:)=SparseMat;
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat_test:error', 'RHS cannot be null with non-null subtensor'));
            end
            assertEqual(st_y, st_x);

            %example 16
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;
            st_x(zeros(0,2))=2;
            assertEqual(st_y, st_x);

            %example 17
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;
            st_x(zeros(0,2))=zeros(0,1);
            assertEqual(st_y, st_x);

            %example 18
            st_x=SparseMat([],[],[100 2]);
            st_y=SparseMat([],[],[100 2]);
            st_x(1:10,:)=1;
            st_y(1:10,:)=1;

            exception=[];
            try
                st_x(ones(1,2))=[];
            catch exception
            end
            if isempty(exception)
                throw(mexception('SparseMat_test:error', 'RHS cannot be null with non-null subscripts'));
            end
            assertEqual(st_y, st_x);
        end

        function testConsistentBehavior(~)
            import edu.stanford.covert.util.SparseMat;

            %example 1
            fl_x=zeros([2 2 1]);
            st_x=SparseMat([],[],[2 2 1]);

            assertEqual(size(fl_x), size(st_x));
            assertEqual(size(fl_x,5),size(st_x,5));

            %example 2
            sp_x=sparse(zeros([10 10]));
            st_x=SparseMat([],[],[10 10]);

            sp_x(10,5)=1;
            sp_x(5,7)=3;
            sp_x(12,6)=4;

            st_x(10,5)=1;
            st_x(5,7)=3;
            st_x(12,6)=4;

            [i,j]=find(sp_x);
            assertEqual([i j], find(st_x));

            %example 3
            fl_x=zeros([10 10 10]);
            st_x=SparseMat([],[],[10 10 10]);

            fl_x(10,5,3)=1;
            fl_x(5,7,2)=3;
            fl_x(12,6,1)=4;
            fl_x(10,4,3)=1;
            fl_x(5,8,2)=3;
            fl_x(12,5,1)=4;

            st_x(10,5,3)=1;
            st_x(5,7,2)=3;
            st_x(12,6,1)=4;
            st_x(10,4,3)=1;
            st_x(5,8,2)=3;
            st_x(12,5,1)=4;

            [i,j,k]=ind2sub(size(fl_x),find(fl_x));
            assertEqual([i j k], find(st_x));

            [~,vals]=find(st_x);
            assertEqual(fl_x(find(fl_x)),vals);
        end

        function testInternalConsistency(~)
            import edu.stanford.covert.util.SparseMat;

            %example 1
            st_x=SparseMat([],[],[100 1 1]);
            st_y=SparseMat(ones(10,1));
            st_x(1:10,1,1)=st_y;

            subs=find(st_x);
            assertEqual(length(size(st_x)), size(subs,2));

            %example 2
            st_x=SparseMat([],[],[100 1 1]);
            st_y=SparseMat(ones(10,1));
            st_x(1:10,1,1,1)=st_y;

            subs=find(st_x);
            assertEqual(length(size(st_x)), size(subs,2));

            %example 3
            st_x=SparseMat([],[],[500 1 1]);
            st_x(1:3,1,1)=SparseMat(ones(3,1));

            st_y=SparseMat([],[],[3 1 1]);
            st_y(1,1,1)=3;
            st_y(2,1,1)=4;
            st_y(3,1,1)=5;
            st_x(4:6,1,1)=st_y;

            subs=find(st_x);
            assertEqual(length(size(st_x)), size(subs,2));
        end

        function testEnd(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

            assertEqual(SparseMat(mat(2:end,:,:,:)), spmat(2:end,:,:,:));
            assertEqual(SparseMat(mat(:,:,end,:)), spmat(:,:,end,:));
            assertEqual(SparseMat(mat(:,:,:,end-1)), spmat(:,:,:,end-1));

            exception = [];
            try
                assertEqual(SparseMat(mat(1:end)), spmat(1:end));
            catch exception
            end
            if isempty(exception)
                throw(MException('SparseMat:unsupportedSubsref','SparseMat doesn''t support linear indexing'));
            end
        end

        function testSub2ind(~)
            import edu.stanford.covert.util.SparseMat;

            inds=sub2ind([100 2 3], ...
                reshape(repmat((1:100)',[1 2 3]),[],1),...
                reshape(repmat(1:2,[100 1 3]),[],1),...
                reshape(repmat(permute(1:3,[1 3 2]),[100 2 1]),[],1));

            spmat = SparseMat([],[],[100 2 3]);
            subs = [...
                reshape(repmat((1:100)',[1 2 3]),[],1) ...
                reshape(repmat(1:2,[100 1 3]),[],1) ...
                reshape(repmat(permute(1:3,[1 3 2]),[100 2 1]),[],1)];
            assertEqual(inds, sub2ind(spmat,subs));
        end

        function testInd2sub(~)
            import edu.stanford.covert.util.SparseMat;

            [i,j,k]=ind2sub([100 2 3], (1:100*2*3)');

            spmat = SparseMat([],[],[100 2 3]);
            assertEqual([i j k], ind2sub(spmat,(1:100*2*3)'));
        end

        function testSize(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

            %isempty
            assertEqual(false, isempty(spmat));
            assertEqual(true, isempty(SparseMat));

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

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

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

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

            %nnz
            assertEqual(7, nnz(spmat));

            %permute
            assertEqual(SparseMat(permute(mat,[2 4 1 3])), permute(spmat, [2 4 1 3]));
            assertEqual(SparseMat(permute(mat,[2 5 4 1 3])), permute(spmat, [2 5 4 1 3]));

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

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

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

            %repmat
            assertEqual(spmat, repmat(spmat, 1, 1));
            assertEqual(spmat, repmat(spmat, [1 1 1]));
            assertEqual(spmat, repmat(spmat, [1 1 1 1]));
            assertEqual([spmat;spmat], repmat(spmat, 2, 1));
            assertEqual(SparseMat(repmat(mat,[2 3 4 1 4 3])), repmat(spmat, [2 3 4 1 4 3]));

            %squeeze
            assertEqual(SparseMat(squeeze(mat)), squeeze(spmat));
        end

        function testRepeatMatrix(~)
            A = edu.stanford.covert.util.SparseMat(1);
            assertEqual([1 3], size(repmat(A, 1, 3)));
            assertEqual([1 3], size(repmat(A, [1 3])));
            assertEqual([0 2], size(repmat(A, [0 2])));
            assertEqual([1 1], size(repmat(A, [1 1 1])));
            assertEqual([0 1], size(repmat(A, [0 1 1])));
            assertEqual([1 0], size(repmat(A, [1 0 1])));
            assertEqual([1 1 0], size(repmat(A, [1 1 0])));
        end

        function testFind(~)
            import edu.stanford.covert.util.SparseMat;

            %example 1
            mat = zeros(4,3);
            mat(4, 3)=1;
            mat(3, 2)=2;
            mat(1, 1)=3;
            mat(3, 1)=4;
            spmat = SparseMat(mat);

            [i,j,k]=find(mat);
            [subs,vals]=find(spmat);

            assertEqual([i j], subs);
            assertEqual(k, vals);

            %example 2
            mat = zeros(4,3,'int32');
            mat(4, 3)=1;
            mat(3, 2)=2;
            mat(1, 1)=3;
            mat(3, 1)=4;
            spmat = SparseMat(mat);

            [i,j,k]=find(mat);
            [subs,vals]=find(spmat);

            assertEqual([i j], subs);
            assertEqual(k, vals);
        end

        function testRandomlySelectZeros(~)
            import edu.stanford.covert.util.SparseMat;

            mat = zeros(4,3);
            mat(4, 3)=1;
            mat(3, 2)=2;
            mat(1, 1)=3;
            mat(3, 1)=4;
            spmat = SparseMat(mat);

            %example 1
            randomlySelectZeros(spmat,0.2);
            assertEqual([2 2], size(randomlySelectZeros(spmat,2)));

            %example 2
            randStream = edu.stanford.covert.util.RandStream('mcg16807');
            randStream.reset(1);
            randomlySelectZeros(spmat, 0.2, randStream);
            assertEqual([2 2], size(randomlySelectZeros(spmat, 2, randStream)));
        end

        %concatenation
        function testConcatenation(~)
            import edu.stanford.covert.util.SparseMat;

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

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

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

            assertEqual([...
                0 2 0 0;
                0 0 0 0;
                0 0 1 0;
                0 0 0 -1;
                0 0 0 0;
                0 0 0 0;
                0 4 0 0;
                0 0 0 0;
                0 0 3 0;
                0 0 0 0;
                0 2 0 0;
                0 0 0 0;
                0 0 1 0],...
                double(vertcat(u, v, u)));

            assertEqual([...
                0 2 0 0;
                0 0 0 0;
                0 0 1 0;
                0 0 0 -1;
                0 0 0 0;
                0 0 0 0;
                0 4 0 0;
                0 0 0 0;
                0 0 3 0;
                0 0 0 0;
                0 2 0 0;
                0 0 0 0;
                0 0 1 0],...
                double([u; v; u]));

            %Vertcat_nullFirst
            t = SparseMat([1 2; 3 3], [2;1], [3 4]);
            assertEqual(...
                SparseMat([5 2; 7 3], [2;1], [7 4]),...
                vertcat(SparseMat([],[],[4 4]), t));

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

            %Vertcat_zeros
            assertEqual(...
                SparseMat([],[],[7 2]),...
                vertcat(...
                SparseMat([],[],[3 2]), SparseMat([],[],[4 2])));

            %Vertcat_inconsistentDimensions
            assertExceptionThrown(...
                @()vertcat(...
                SparseMat([1 2; 3 3], [2;1], [3 3]),...
                SparseMat([1 2], 3, [3 2])),...
                'SparseMat:cat');

            %Vertcat_threeDimensions
            assertEqual(...
                SparseMat([1 2 1; 3 3 1; 4 1 2], [2;1;3], [4 3 2]),...
                vertcat(...
                SparseMat([1 2 1; 3 3 1], [2;1], [3 3 2]),...
                SparseMat([1 1 2], 3, [1 3 2])));

            %Vertcat_threeInconsistentDimensions
            assertExceptionThrown(...
                @()vertcat(...
                SparseMat([1 2 1; 3 3 1], [2;1], [3 3 1]),...
                SparseMat([1 1 2], 3, [1 3 2])),...
                'SparseMat:cat');

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

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

            %Horzcat_threeTensors
            u = SparseMat([2 1; 3 3], [2;1], [4 3]);
            v = SparseMat([3 6; 2 4; 4 1], [3;4;-1], [4 7]);

            assertEqual([
                0  0  0  0  0  0  0  0  0  0  0  0  0;
                2  0  0  0  0  0  4  0  0  0  2  0  0;
                0  0  1  0  0  0  0  0  3  0  0  0  1;
                0  0  0 -1  0  0  0  0  0  0  0  0  0],...
                double(horzcat(u, v, u)));

            assertEqual([
                0  0  0  0  0  0  0  0  0  0  0  0  0;
                2  0  0  0  0  0  4  0  0  0  2  0  0;
                0  0  1  0  0  0  0  0  3  0  0  0  1;
                0  0  0 -1  0  0  0  0  0  0  0  0  0],...
                double([u, v, u]));

            %Horzcat_nullFirst
            t = SparseMat([1 2; 3 3], [2;1], [3 4]);
            assertEqual(...
                SparseMat([1 7; 3 8], [2;1], [3 9]),...
                horzcat(SparseMat([],[],[3 5]), t));

            %Horzcat_nullLast
            t = SparseMat([1 2; 3 3], [2;1], [3 4]);
            assertEqual(...
                SparseMat([1 2; 3 3], [2;1], [3 9]),...
                horzcat(t, SparseMat([],[],[3 5])));

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

            %Horzcat_inconsistentDimensions
            assertExceptionThrown(...
                @()horzcat(...
                SparseMat([1 2; 3 3], [2;1], [3 3]),...
                SparseMat([1 2], 3, [2 3])),...
                'SparseMat:cat');

            %Horzcat_threeDimensions
            assertEqual(...
                full(SparseMat([1 2 1; 3 3 1; 2 4 1], [2;1;3], [3 4 2])),...
                full(horzcat(...
                SparseMat([1 2 1; 3 3 1], [2;1], [3 3 2]),...
                SparseMat([2 1 1], 3, [3 1 2]))));

            %Horzcat_threeInconsistentDimensions
            assertExceptionThrown(...
                @()horzcat(...
                SparseMat([1 2 1; 3 3 1], [2;1], [3 3 2]),...
                SparseMat([2 1 1], 3, [3 1 1])),...
                'SparseMat:cat');

            %padarray
            x = reshape(1:15,5,3);
            spmat = SparseMat(x);

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

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

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

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            %not
            assertEqual(SparseMat(~mat), ~spmat);
            assertEqual(SparseMat(~mat2), ~spmat2);

            %or
            assertEqual(SparseMat(mat | 0), spmat | 0);
            assertEqual(SparseMat(mat | 2), spmat | 2);
            assertEqual(SparseMat(mat | Inf), spmat | Inf);
            assertEqual(SparseMat(mat | -Inf), spmat | -Inf);
            assertEqual(SparseMat(mat | mat2), spmat | spmat2);
            assertEqual(SparseMat(mat | mat2), spmat | mat2);

            assertEqual(SparseMat(0 | mat), 0 | spmat);
            assertEqual(SparseMat(2 | mat), 2 | spmat);
            assertEqual(SparseMat(Inf | mat), Inf | spmat);
            assertEqual(SparseMat(-Inf | mat), -Inf | spmat);
            assertEqual(SparseMat(mat2 | mat), spmat2 | spmat);
            assertEqual(SparseMat(mat2 | mat), mat2 | spmat);

            %and
            assertEqual(SparseMat(mat & 0), spmat & 0);
            assertEqual(SparseMat(mat & 2), spmat & 2);
            assertEqual(SparseMat(mat & Inf), spmat & Inf);
            assertEqual(SparseMat(mat & -Inf), spmat & -Inf);
            assertEqual(SparseMat(mat & mat2), spmat & spmat2);
            assertEqual(SparseMat(mat & mat2), spmat & mat2);

            assertEqual(SparseMat(0 & mat), 0 & spmat);
            assertEqual(SparseMat(2 & mat), 2 & spmat);
            assertEqual(SparseMat(Inf & mat), Inf & spmat);
            assertEqual(SparseMat(-Inf & mat), -Inf & spmat);
            assertEqual(SparseMat(mat2 & mat), spmat2 & spmat);
            assertEqual(SparseMat(mat2 & mat), mat2 & spmat);

            %xor
            assertEqual(SparseMat(xor(mat,0)), xor(spmat,0));
            assertEqual(SparseMat(xor(mat,2)), xor(spmat,2));
            assertEqual(SparseMat(xor(mat,Inf)), xor(spmat,Inf));
            assertEqual(SparseMat(xor(mat,-Inf)), xor(spmat,-Inf));
            assertEqual(SparseMat(xor(mat,mat2)), xor(spmat,spmat2));
            assertEqual(SparseMat(xor(mat,mat2)), xor(spmat,mat2));

            assertEqual(SparseMat(xor(0,mat)), xor(0,spmat));
            assertEqual(SparseMat(xor(2,mat)), xor(2,spmat));
            assertEqual(SparseMat(xor(Inf,mat)), xor(Inf,spmat));
            assertEqual(SparseMat(xor(-Inf,mat)), xor(-Inf,spmat));
            assertEqual(SparseMat(xor(mat2,mat)), xor(spmat2,spmat));
            assertEqual(SparseMat(xor(mat2,mat)), xor(mat2,spmat));

            %nor
            assertEqual(SparseMat(~(mat | 0)), nor(spmat,0));
            assertEqual(SparseMat(~(mat | 2)), nor(spmat,2));
            assertEqual(SparseMat(~(mat | Inf)), nor(spmat,Inf));
            assertEqual(SparseMat(~(mat | -Inf)), nor(spmat,-Inf));
            assertEqual(SparseMat(~(mat | mat2)), nor(spmat,spmat2));
            assertEqual(SparseMat(~(mat | mat2)), nor(spmat,mat2));

            assertEqual(SparseMat(~(0 | mat)), nor(0,spmat));
            assertEqual(SparseMat(~(2 | mat)), nor(2,spmat));
            assertEqual(SparseMat(~(Inf | mat)), nor(Inf,spmat));
            assertEqual(SparseMat(~(-Inf | mat)), nor(-Inf,spmat));
            assertEqual(SparseMat(~(mat2 | mat)), nor(spmat2,spmat));
            assertEqual(SparseMat(~(mat2 | mat)), nor(mat2,spmat));

            %nand
            assertEqual(SparseMat(~(mat & 0)), nand(spmat,0));
            assertEqual(SparseMat(~(mat & 2)), nand(spmat,2));
            assertEqual(SparseMat(~(mat & Inf)), nand(spmat,Inf));
            assertEqual(SparseMat(~(mat & -Inf)), nand(spmat,-Inf));
            assertEqual(SparseMat(~(mat & mat2)), nand(spmat,spmat2));
            assertEqual(SparseMat(~(mat & mat2)), nand(spmat,mat2));

            assertEqual(SparseMat(~(0 & mat)), nand(0,spmat));
            assertEqual(SparseMat(~(2 & mat)), nand(2,spmat));
            assertEqual(SparseMat(~(Inf & mat)), nand(Inf,spmat));
            assertEqual(SparseMat(~(-Inf & mat)), nand(-Inf,spmat));
            assertEqual(SparseMat(~(mat2 & mat)), nand(spmat2,spmat));
            assertEqual(SparseMat(~(mat2 & mat)), nand(mat2,spmat));
        end

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

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            %unary
            assertEqual(SparseMat(+mat), +spmat);
            assertEqual(SparseMat(-mat), -spmat);

            %addition
            assertEqual(SparseMat(mat+0), spmat+0);
            assertEqual(SparseMat(mat+2), spmat+2);
            assertEqual(SparseMat(mat+NaN), spmat+NaN);
            assertEqual(SparseMat(mat+Inf), spmat+Inf);
            assertEqual(SparseMat(mat+-Inf), spmat+-Inf);
            assertEqual(SparseMat(mat+mat2), spmat+spmat2);
            assertEqual(SparseMat(mat+mat2), spmat+mat2);

            assertEqual(SparseMat(0+mat), 0+spmat);
            assertEqual(SparseMat(2+mat), 2+spmat);
            assertEqual(SparseMat(NaN+mat), NaN+spmat);
            assertEqual(SparseMat(Inf+mat), Inf+spmat);
            assertEqual(SparseMat(-Inf+mat), -Inf+spmat);
            assertEqual(SparseMat(mat2+mat), spmat2+spmat);
            assertEqual(SparseMat(mat2+mat), mat2+spmat);

            %subtraction
            assertEqual(SparseMat(mat-0), spmat-0);
            assertEqual(SparseMat(mat-2), spmat-2);
            assertEqual(SparseMat(mat-NaN), spmat-NaN);
            assertEqual(SparseMat(mat-Inf), spmat-Inf);
            assertEqual(SparseMat(mat--Inf), spmat--Inf);
            assertEqual(SparseMat(mat-mat2), spmat-spmat2);
            assertEqual(SparseMat(mat-mat2), spmat-mat2);

            assertEqual(SparseMat(0-mat), 0-spmat);
            assertEqual(SparseMat(2-mat), 2-spmat);
            assertEqual(SparseMat(NaN-mat), NaN-spmat);
            assertEqual(SparseMat(Inf-mat), Inf-spmat);
            assertEqual(SparseMat(-Inf-mat), -Inf-spmat);
            assertEqual(SparseMat(mat2-mat), spmat2-spmat);
            assertEqual(SparseMat(mat2-mat), mat2-spmat);
        end

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

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            mat3 = [
                2 Inf 0;
                0 2 -3;
                0 0 NaN];
            mat4 = [
                4 0 -7;
                -Inf 3 0
                NaN 0 0];
            spmat3 = SparseMat(mat3);
            spmat4 = SparseMat(mat4);

            %element-wise multiplication
            assertEqual(SparseMat(mat.*0), spmat.*0);
            assertEqual(SparseMat(mat.*2), spmat.*2);
            assertEqual(SparseMat(mat.*NaN), spmat.*NaN);
            assertEqual(SparseMat(mat.*Inf), spmat.*Inf);
            assertEqual(SparseMat(mat.*-Inf), spmat.*-Inf);
            assertEqual(SparseMat(mat.*mat2), spmat.*spmat2);
            assertEqual(SparseMat(mat.*mat2), spmat.*mat2);

            assertEqual(SparseMat(0.*mat), 0.*spmat);
            assertEqual(SparseMat(2.*mat), 2.*spmat);
            assertEqual(SparseMat(NaN.*mat), NaN.*spmat);
            assertEqual(SparseMat(Inf.*mat), Inf.*spmat);
            assertEqual(SparseMat(-Inf.*mat), -Inf.*spmat);
            assertEqual(SparseMat(mat2.*mat), spmat2.*spmat);
            assertEqual(SparseMat(mat2.*mat), mat2.*spmat);

            %matrix multiplication
            assertEqual(SparseMat(mat*0), spmat*0);
            assertEqual(SparseMat(mat*2), spmat*2);
            assertEqual(SparseMat(mat*NaN), spmat*NaN);
            assertEqual(SparseMat(mat*Inf), spmat*Inf);
            assertEqual(SparseMat(mat*-Inf), spmat*-Inf);
            assertEqual(SparseMat(mat3*mat4), spmat3*spmat4);
            assertEqual(SparseMat(mat3*mat4), spmat3*mat4);

            assertEqual(SparseMat(0*mat), 0*spmat);
            assertEqual(SparseMat(2*mat), 2*spmat);
            assertEqual(SparseMat(NaN*mat), NaN*spmat);
            assertEqual(SparseMat(Inf*mat), Inf*spmat);
            assertEqual(SparseMat(-Inf*mat), -Inf*spmat);
            assertEqual(SparseMat(mat4*mat3), spmat4*spmat3);
            assertEqual(SparseMat(mat4*mat3), mat4*spmat3);

            %element-wise right division
            assertEqual(SparseMat(mat./0), spmat./0);
            assertEqual(SparseMat(mat./2), spmat./2);
            assertEqual(SparseMat(mat./NaN), spmat./NaN);
            assertEqual(SparseMat(mat./Inf), spmat./Inf);
            assertEqual(SparseMat(mat./-Inf), spmat./-Inf);
            assertEqual(SparseMat(mat./mat2), spmat./spmat2);
            assertEqual(SparseMat(mat./mat2), spmat./mat2);

            assertEqual(SparseMat(0./mat), 0./spmat);
            assertEqual(SparseMat(2./mat), 2./spmat);
            assertEqual(SparseMat(NaN./mat), NaN./spmat);
            assertEqual(SparseMat(Inf./mat), Inf./spmat);
            assertEqual(SparseMat(-Inf./mat), -Inf./spmat);
            assertEqual(SparseMat(mat2./mat), spmat2./spmat);
            assertEqual(SparseMat(mat2./mat), mat2./spmat);

            %matrix right division
            assertEqual(SparseMat(mat/0), spmat/0);
            assertEqual(SparseMat(mat/2), spmat/2);
            assertEqual(SparseMat(mat/NaN), spmat/NaN);
            assertEqual(SparseMat(mat/Inf), spmat/Inf);
            assertEqual(SparseMat(mat/-Inf), spmat/-Inf);
            % assertEqual(SparseMat(mat/mat2), spmat/spmat2);
            % assertEqual(SparseMat(mat/mat2), spmat/mat2);

            assertEqual(SparseMat(0/mat), 0/spmat);
            assertEqual(SparseMat(2/mat), 2/spmat);
            assertEqual(SparseMat(NaN/mat), NaN/spmat);
            assertEqual(SparseMat(Inf/mat), Inf/spmat);
            assertEqual(SparseMat(-Inf/mat), -Inf/spmat);
            % assertEqual(SparseMat(mat2/mat), spmat2/spmat);
            % assertEqual(SparseMat(mat2/mat), mat2/spmat);

            %element-wise left division
            assertEqual(SparseMat(mat.\0), spmat.\0);
            assertEqual(SparseMat(mat.\2), spmat.\2);
            assertEqual(SparseMat(mat.\NaN), spmat.\NaN);
            assertEqual(SparseMat(mat.\Inf), spmat.\Inf);
            assertEqual(SparseMat(mat.\-Inf), spmat.\-Inf);
            assertEqual(SparseMat(mat.\mat2), spmat.\spmat2);
            assertEqual(SparseMat(mat.\mat2), spmat.\mat2);

            assertEqual(SparseMat(0.\mat), 0.\spmat);
            assertEqual(SparseMat(2.\mat), 2.\spmat);
            assertEqual(SparseMat(NaN.\mat), NaN.\spmat);
            assertEqual(SparseMat(Inf.\mat), Inf.\spmat);
            assertEqual(SparseMat(-Inf.\mat), -Inf.\spmat);
            assertEqual(SparseMat(mat2.\mat), spmat2.\spmat);
            assertEqual(SparseMat(mat2.\mat), mat2.\spmat);

            %matrix left division
            assertEqual(SparseMat(mat\0), spmat\0);
            assertEqual(SparseMat(mat\2), spmat\2);
            assertEqual(SparseMat(mat\NaN), spmat\NaN);
            assertEqual(SparseMat(mat\Inf), spmat\Inf);
            assertEqual(SparseMat(mat\-Inf), spmat\-Inf);
            % assertEqual(SparseMat(mat\mat2), spmat\spmat2);
            % assertEqual(SparseMat(mat\mat2), spmat\mat2);

            assertEqual(SparseMat(0\mat), 0\spmat);
            assertEqual(SparseMat(2\mat), 2\spmat);
            assertEqual(SparseMat(NaN\mat), NaN\spmat);
            assertEqual(SparseMat(Inf\mat), Inf\spmat);
            assertEqual(SparseMat(-Inf\mat), -Inf\spmat);
            % assertEqual(SparseMat(mat2\mat), spmat2\spmat);
            % assertEqual(SparseMat(mat2\mat), mat2\spmat);

            %element-wise exponentiation
            assertEqual(SparseMat(mat.^0), spmat.^0);
            assertEqual(SparseMat(mat.^2), spmat.^2);
            assertEqual(SparseMat(mat.^NaN), spmat.^NaN);
            assertEqual(SparseMat(mat.^Inf), spmat.^Inf);
            assertEqual(SparseMat(mat.^-Inf), spmat.^-Inf);
            assertEqual(SparseMat(mat.^mat2), spmat.^spmat2);
            assertEqual(SparseMat(mat.^mat2), spmat.^mat2);

            assertEqual(SparseMat(0.^mat), 0.^spmat);
            assertEqual(SparseMat(2.^mat), 2.^spmat);
            assertEqual(SparseMat(NaN.^mat), NaN.^spmat);
            assertEqual(SparseMat(Inf.^mat), Inf.^spmat);
            assertEqual(SparseMat(-Inf.^mat), -Inf.^spmat);
            assertEqual(SparseMat(mat2.^mat), spmat2.^spmat);
            assertEqual(SparseMat(mat2.^mat), mat2.^spmat);

            %matrix exponentiation
            % assertEqual(SparseMat(mat^0), spmat^0);
            % assertEqual(SparseMat(mat^2), spmat^2);
            % assertEqual(SparseMat(mat^NaN), spmat^NaN);
            % assertEqual(SparseMat(mat^Inf), spmat^Inf);
            % assertEqual(SparseMat(mat^-Inf), spmat^-Inf);
            % assertEqual(SparseMat(mat^mat2), spmat^spmat2);
            % assertEqual(SparseMat(mat^mat2), spmat^mat2);

            % assertEqual(SparseMat(0^mat), 0^spmat);
            % assertEqual(SparseMat(2^mat), 2^spmat);
            % assertEqual(SparseMat(NaN^mat), NaN^spmat);
            % assertEqual(SparseMat(Inf^mat), Inf^spmat);
            % assertEqual(SparseMat(-Inf^mat), -Inf^spmat);
            % assertEqual(SparseMat(mat2^mat), spmat2^spmat);
            % assertEqual(SparseMat(mat2^mat), mat2^spmat);
        end

        function testEquality(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            assertEqual(SparseMat(mat==0), spmat==0);
            assertEqual(SparseMat(mat==2), spmat==2);
            assertEqual(SparseMat(mat==NaN), spmat==NaN);
            assertEqual(SparseMat(mat==Inf), spmat==Inf);
            assertEqual(SparseMat(mat==-Inf), spmat==-Inf);
            assertEqual(SparseMat(mat==mat2), spmat==spmat2);
            assertEqual(SparseMat(mat==mat2), spmat==mat2);

            assertEqual(SparseMat(0==mat), 0==spmat);
            assertEqual(SparseMat(2==mat), 2==spmat);
            assertEqual(SparseMat(NaN==mat), NaN==spmat);
            assertEqual(SparseMat(Inf==mat), Inf==spmat);
            assertEqual(SparseMat(-Inf==mat), -Inf==spmat);
            assertEqual(SparseMat(mat2==mat), spmat2==spmat);
            assertEqual(SparseMat(mat2==mat), mat2==spmat);
        end

        function testInequality(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            assertEqual(SparseMat(mat~=0), spmat~=0);
            assertEqual(SparseMat(mat~=2), spmat~=2);
            assertEqual(SparseMat(mat~=NaN), spmat~=NaN);
            assertEqual(SparseMat(mat~=Inf), spmat~=Inf);
            assertEqual(SparseMat(mat~=-Inf), spmat~=-Inf);
            assertEqual(SparseMat(mat~=mat2), spmat~=spmat2);
            assertEqual(SparseMat(mat~=mat2), spmat~=mat2);

            assertEqual(SparseMat(0~=mat), 0~=spmat);
            assertEqual(SparseMat(2~=mat), 2~=spmat);
            assertEqual(SparseMat(NaN~=mat), NaN~=spmat);
            assertEqual(SparseMat(Inf~=mat), Inf~=spmat);
            assertEqual(SparseMat(-Inf~=mat), -Inf~=spmat);
            assertEqual(SparseMat(mat2~=mat), spmat2~=spmat);
            assertEqual(SparseMat(mat2~=mat), mat2~=spmat);
        end

        function testGreaterThan(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            assertEqual(SparseMat(mat>0), spmat>0);
            assertEqual(SparseMat(mat>2), spmat>2);
            assertEqual(SparseMat(mat>NaN), spmat>NaN);
            assertEqual(SparseMat(mat>Inf), spmat>Inf);
            assertEqual(SparseMat(mat>-Inf), spmat>-Inf);
            assertEqual(SparseMat(mat>mat2), spmat>spmat2);
            assertEqual(SparseMat(mat>mat2), spmat>mat2);

            assertEqual(SparseMat(0>mat), 0>spmat);
            assertEqual(SparseMat(2>mat), 2>spmat);
            assertEqual(SparseMat(NaN>mat), NaN>spmat);
            assertEqual(SparseMat(Inf>mat), Inf>spmat);
            assertEqual(SparseMat(-Inf>mat), -Inf>spmat);
            assertEqual(SparseMat(mat2>mat), spmat2>spmat);
            assertEqual(SparseMat(mat2>mat), mat2>spmat);
        end

        function testGreaterThanOrEqualTo(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            assertEqual(SparseMat(mat>=0), spmat>=0);
            assertEqual(SparseMat(mat>=2), spmat>=2);
            assertEqual(SparseMat(mat>=NaN), spmat>=NaN);
            assertEqual(SparseMat(mat>=Inf), spmat>=Inf);
            assertEqual(SparseMat(mat>=-Inf), spmat>=-Inf);
            assertEqual(SparseMat(mat>=mat2), spmat>=spmat2);
            assertEqual(SparseMat(mat>=mat2), spmat>=mat2);

            assertEqual(SparseMat(0>=mat), 0>=spmat);
            assertEqual(SparseMat(2>=mat), 2>=spmat);
            assertEqual(SparseMat(NaN>=mat), NaN>=spmat);
            assertEqual(SparseMat(Inf>=mat), Inf>=spmat);
            assertEqual(SparseMat(-Inf>=mat), -Inf>=spmat);
            assertEqual(SparseMat(mat2>=mat), spmat2>=spmat);
            assertEqual(SparseMat(mat2>=mat), mat2>=spmat);
        end

        function testLessThan(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            assertEqual(SparseMat(mat<0), spmat<0);
            assertEqual(SparseMat(mat<2), spmat<2);
            assertEqual(SparseMat(mat<NaN), spmat<NaN);
            assertEqual(SparseMat(mat<Inf), spmat<Inf);
            assertEqual(SparseMat(mat<-Inf), spmat<-Inf);
            assertEqual(SparseMat(mat<mat2), spmat<spmat2);
            assertEqual(SparseMat(mat<mat2), spmat<mat2);

            assertEqual(SparseMat(0<mat), 0<spmat);
            assertEqual(SparseMat(2<mat), 2<spmat);
            assertEqual(SparseMat(NaN<mat), NaN<spmat);
            assertEqual(SparseMat(Inf<mat), Inf<spmat);
            assertEqual(SparseMat(-Inf<mat), -Inf<spmat);
            assertEqual(SparseMat(mat2<mat), spmat2<spmat);
            assertEqual(SparseMat(mat2<mat), mat2<spmat);
        end

        function testLessThanOrEqualTo(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2 = SparseMat(mat2);

            assertEqual(SparseMat(mat<=0), spmat<=0);
            assertEqual(SparseMat(mat<=2), spmat<=2);
            assertEqual(SparseMat(mat<=NaN), spmat<=NaN);
            assertEqual(SparseMat(mat<=Inf), spmat<=Inf);
            assertEqual(SparseMat(mat<=-Inf), spmat<=-Inf);
            assertEqual(SparseMat(mat<=mat2), spmat<=spmat2);
            assertEqual(SparseMat(mat<=mat2), spmat<=mat2);

            assertEqual(SparseMat(0<=mat), 0<=spmat);
            assertEqual(SparseMat(2<=mat), 2<=spmat);
            assertEqual(SparseMat(NaN<=mat), NaN<=spmat);
            assertEqual(SparseMat(Inf<=mat), Inf<=spmat);
            assertEqual(SparseMat(-Inf<=mat), -Inf<=spmat);
            assertEqual(SparseMat(mat2<=mat), spmat2<=spmat);
            assertEqual(SparseMat(mat2<=mat), mat2<=spmat);
        end

        function testScalarFunctions(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

            %abs
            assertEqual(SparseMat(abs(mat)), abs(spmat));

            %sign
            assertEqual(SparseMat(sign(mat)), sign(spmat));

            %sqrt
            assertEqual(SparseMat(sqrt(mat)), sqrt(spmat));

            %realsqrt
            assertEqual(SparseMat(realsqrt(abs(mat))), realsqrt(abs(spmat)));

            %exp
            assertEqual(SparseMat(exp(mat)), exp(spmat));

            %expm1
            assertEqual(SparseMat(expm1(mat)), expm1(spmat));

            %log
            assertEqual(SparseMat(log(mat)), log(spmat));

            %log1p
            assertEqual(SparseMat(log1p(mat)), log1p(spmat));

            %log2
            assertEqual(SparseMat(log2(mat)), log2(spmat));

            %log10
            assertEqual(SparseMat(log10(mat)), log10(spmat));

            %reallog
            assertEqual(SparseMat(reallog(abs(mat))), reallog(abs(spmat)));
        end

        function testComplexNumbers(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

            %abs
            assertEqual(SparseMat(abs(mat)), abs(spmat));

            %sign
            assertEqual(SparseMat(sign(mat)), sign(spmat));

            %real
            assertEqual(SparseMat(real(mat)), real(spmat));

            %imag
            assertEqual(SparseMat(imag(mat)), imag(spmat));

            %angle
            assertEqual(SparseMat(angle(mat)), angle(spmat));

            %conj
            assertEqual(SparseMat(conj(mat)), conj(spmat));

            %isreal
            assertEqual(SparseMat(isreal(mat)), isreal(spmat));
        end

        function testSetOperators(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

            mat2=ones(4,1);
            spmat2=SparseMat(mat2);

            %unique
            assertEqual(SparseMat(unique(mat)), unique(spmat));
            assertEqual(SparseMat(unique(mat2)), unique(spmat2));
            assertEqual(SparseMat(unique(zeros(0,1))), unique(SparseMat));
            assertEqual(SparseMat(unique(zeros(1,0))), unique(SparseMat));
        end

        function testAnyAll(~)
            import edu.stanford.covert.util.SparseMat;

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

            st_x=SparseMat(fl_x);

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

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

        function testReduction(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(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;
            spmat2=SparseMat(mat2);

            %min
            assertEqual(SparseMat(min(mat,0)), min(spmat,0));
            assertEqual(SparseMat(min(mat,2)), min(spmat,2));
            assertEqual(SparseMat(min(mat,-2)), min(spmat,-2));
            assertEqual(SparseMat(min(mat,NaN)), min(spmat,NaN));
            assertEqual(SparseMat(min(mat,Inf)), min(spmat,Inf));
            assertEqual(SparseMat(min(mat,-Inf)), min(spmat,-Inf));

            assertEqual(SparseMat(min(0,mat)), min(0,spmat));
            assertEqual(SparseMat(min(2,mat)), min(2,spmat));
            assertEqual(SparseMat(min(-2,mat)), min(-2,spmat));
            assertEqual(SparseMat(min(NaN,mat)), min(NaN,spmat));
            assertEqual(SparseMat(min(Inf,mat)), min(Inf,spmat));
            assertEqual(SparseMat(min(-Inf,mat)), min(-Inf, spmat));

            assertEqual(SparseMat(min(mat,mat2)), min(spmat,spmat2));
            assertEqual(SparseMat(min(mat,mat2)), min(spmat,mat2));
            assertEqual(SparseMat(min(mat2,mat)), min(spmat2,spmat));
            assertEqual(SparseMat(min(mat,mat2)), min(mat2,spmat));

            assertEqual(SparseMat(min(mat)), min(spmat));
            assertEqual(SparseMat(min(mat,[],1)), min(spmat,[],1));
            assertEqual(SparseMat(min(mat,[],2)), min(spmat,[],2));
            assertEqual(SparseMat(min(mat,[],3)), min(spmat,[],3));
            assertEqual(SparseMat(min(mat,[],4)), min(spmat,[],4));
            assertEqual(SparseMat(min(mat,[],5)), min(spmat,[],5));

            %max
            assertEqual(SparseMat(max(mat,0)), max(spmat,0));
            assertEqual(SparseMat(max(mat,2)), max(spmat,2));
            assertEqual(SparseMat(max(mat,-2)), max(spmat,-2));
            assertEqual(SparseMat(max(mat,NaN)), max(spmat,NaN));
            assertEqual(SparseMat(max(mat,Inf)), max(spmat,Inf));
            assertEqual(SparseMat(max(mat,-Inf)), max(spmat,-Inf));

            assertEqual(SparseMat(max(0,mat)), max(0,spmat));
            assertEqual(SparseMat(max(2,mat)), max(2,spmat));
            assertEqual(SparseMat(max(-2,mat)), max(-2,spmat));
            assertEqual(SparseMat(max(NaN,mat)), max(NaN,spmat));
            assertEqual(SparseMat(max(Inf,mat)), max(Inf,spmat));
            assertEqual(SparseMat(max(-Inf,mat)), max(-Inf, spmat));

            assertEqual(SparseMat(max(mat,mat2)), max(spmat,spmat2));
            assertEqual(SparseMat(max(mat,mat2)), max(spmat,mat2));
            assertEqual(SparseMat(max(mat2,mat)), max(spmat2,spmat));
            assertEqual(SparseMat(max(mat,mat2)), max(mat2,spmat));

            assertEqual(SparseMat(max(mat)), max(spmat));
            assertEqual(SparseMat(max(mat,[],1)), max(spmat,[],1));
            assertEqual(SparseMat(max(mat,[],2)), max(spmat,[],2));
            assertEqual(SparseMat(max(mat,[],3)), max(spmat,[],3));
            assertEqual(SparseMat(max(mat,[],4)), max(spmat,[],4));
            assertEqual(SparseMat(max(mat,[],5)), max(spmat,[],5));

            %range
            assertEqual(SparseMat(range(mat)), range(spmat));
            assertEqual(SparseMat(range(mat,1)), range(spmat,1));
            assertEqual(SparseMat(range(mat,2)), range(spmat,2));
            assertEqual(SparseMat(range(mat,3)), range(spmat,3));
            assertEqual(SparseMat(range(mat,4)), range(spmat,4));
            assertEqual(SparseMat(range(mat,5)), range(spmat,5));

            %sum
            assertEqual(SparseMat(sum(mat)), sum(spmat));
            assertEqual(SparseMat(sum(mat,1)), sum(spmat,1));
            assertEqual(SparseMat(sum(mat,2)), sum(spmat,2));
            assertEqual(SparseMat(sum(mat,3)), sum(spmat,3));
            assertEqual(SparseMat(sum(mat,4)), sum(spmat,4));
            assertEqual(SparseMat(sum(mat,5)), sum(spmat,5));
            assertEqual(SparseMat(sum(true(5, 1))), sum(SparseMat(true(5, 1))));

            %collapse
            assertEqual(sum(sum(sum(sum(mat)))), collapse(spmat));
            assertEqual(squeeze(sum(mat,2)), collapse(spmat,2));
            assertEqual(squeeze(sum(sum(mat,1),3)), collapse(spmat,[1 3]));
            assertEqual(squeeze(sum(sum(sum(mat,3),4),2)), collapse(spmat,[2 3 4]));
            assertEqual(squeeze(sum(sum(sum(mat,2),3),4)), collapse(spmat,[2 3 4]));
            assertEqual(squeeze(sum(sum(sum(mat,1),3),4)), collapse(spmat,[1 3 4]));

            %mean
            assertEqual(SparseMat(mean(mat)), mean(spmat));
            assertEqual(SparseMat(mean(mat,1)), mean(spmat,1));
            assertEqual(SparseMat(mean(mat,2)), mean(spmat,2));
            assertEqual(SparseMat(mean(mat,3)), mean(spmat,3));
            assertEqual(SparseMat(mean(mat,4)), mean(spmat,4));
            assertEqual(SparseMat(mean(mat,5)), mean(spmat,5));

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

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

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

            %std
            assertEqual(SparseMat(std(mat)), std(spmat));
            assertEqual(SparseMat(std(mat,1)), std(spmat,1));

            % assertEqual(SparseMat(std(mat,0,1)), std(spmat,0,1));
            % assertEqual(SparseMat(std(mat,0,2)), std(spmat,0,2));
            % assertEqual(SparseMat(std(mat,0,3)), std(spmat,0,3));
            % assertEqual(SparseMat(std(mat,0,4)), std(spmat,0,4));
            % assertEqual(SparseMat(std(mat,0,5)), std(spmat,0,5));

            assertEqual(SparseMat(std(mat,1,1)), std(spmat,1,1));
            assertEqual(SparseMat(std(mat,1,2)), std(spmat,1,2));
            assertEqual(SparseMat(std(mat,1,3)), std(spmat,1,3));
            assertEqual(SparseMat(std(mat,1,4)), std(spmat,1,4));
            assertEqual(SparseMat(std(mat,1,5)), std(spmat,1,5));

            %norm
            assertEqual(SparseMat(norm(reshape(mat,[],1),1)), norm(spmat,1));
            assertEqual(SparseMat(norm(reshape(mat,[],1),2)), norm(spmat,2));
            assertEqual(SparseMat(norm(reshape(mat,[],1),Inf)), norm(spmat,Inf));
            assertEqual(SparseMat(norm(reshape(mat,[],1),-Inf)), norm(spmat,-Inf));
            assertEqual(SparseMat(norm(reshape(mat,[],1),'fro')), norm(spmat,'fro'));
        end

        function testSum(~)
            import edu.stanford.covert.util.SparseMat;

            x = SparseMat([],[],[3 1]);
            assertEqual(x, sum(x,3));
            assertEqual(x, sum(x,2));
            assertEqual(SparseMat([],[],[1 1]), sum(x,1));

            x = SparseMat([],[],[1 3]);
            assertEqual(x, sum(x,3));
            assertEqual(x, sum(x,1));
            assertEqual(SparseMat([],[],[1 1]), sum(x,2));
        end

        function testIss(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

            assertEqual(SparseMat(isnan(mat)), isnan(spmat));
            assertEqual(SparseMat(isinf(mat)), isinf(spmat));
            assertEqual(SparseMat(isfinite(mat)), isfinite(spmat));
        end

        %type casting
        function testTypeCasting(~)
            import edu.stanford.covert.util.SparseMat;

            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;
            spmat = SparseMat(mat);

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

            %full
            assertEqual(mat, full(spmat));

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

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

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

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

        function testSort_subs(~)
            import edu.stanford.covert.util.SparseMat;

            for i = 1:1000
                A = randi(100, [6 3]);

                assertEqual(sortrows(A, size(A, 2):-1:1), SparseMat.sort_subs(A, max(A, [], 1), size(A, 2):-1:1));
                assertEqual(sortrows(A, 1:size(A, 2)), SparseMat.sort_subs(A, max(A, [], 1), 1:size(A, 2)));
                assertEqual(sortrows(A, size(A, 2):-1:1), SparseMat.sort_subs(A, max(A, [], 1)));
                assertEqual(sortrows(A, [3 1 2]), SparseMat.sort_subs(A, max(A, [], 1), [3 1 2]));
                assertEqual(sortrows(A, [2 1 3]), SparseMat.sort_subs(A, max(A, [], 1), [2 1 3]));
                assertEqual(sortrows(A, [2 3 1]), SparseMat.sort_subs(A, max(A, [], 1), [2 3 1]));
                assertEqual(sortrows(A, [1 3 2]), SparseMat.sort_subs(A, max(A, [], 1), [1 3 2]));

                assertEqual(sortrows(A, [1 2]), SparseMat.sort_subs(A, max(A, [], 1), [1 2]));
                assertEqual(sortrows(A, [1 3]), SparseMat.sort_subs(A, max(A, [], 1), [1 3]));
                assertEqual(sortrows(A, [2 1]), SparseMat.sort_subs(A, max(A, [], 1), [2 1]));
                assertEqual(sortrows(A, [2 3]), SparseMat.sort_subs(A, max(A, [], 1), [2 3]));
                assertEqual(sortrows(A, [3 1]), SparseMat.sort_subs(A, max(A, [], 1), [3 1]));
                assertEqual(sortrows(A, [3 2]), SparseMat.sort_subs(A, max(A, [], 1), [3 2]));

                assertEqual(sortrows(A, 1), SparseMat.sort_subs(A, max(A, [], 1), 1));
                assertEqual(sortrows(A, 2), SparseMat.sort_subs(A, max(A, [], 1), 2));
                assertEqual(sortrows(A, 3), SparseMat.sort_subs(A, max(A, [], 1), 3));
            end
        end

        function testDiskIO(~)
            import edu.stanford.covert.util.SparseMat;

            for i = 1:10
                A = randi(100, [20 20 20]);

                S = SparseMat(A);

                SparseMat.toDisk(S,'spmat.bin');
                assertEqual(S(4:8,12:14,18:20),SparseMat.fromDisk('spmat.bin', [4 8], [12 14], [18 20]));
                assertEqual(S(5:10,5:10,5:10),SparseMat.fromDisk('spmat.bin', [5 10], [5 10], [5 10]));
                assertEqual(S(8:8,14:14,20:20),SparseMat.fromDisk('spmat.bin', [8 8], [14 14], [20 20]));
                delete('spmat.bin');
            end
        end
    end
end


function [obj] = update(obj)
    % Updates the NLP problems before load it to NLP solver
    %
    % @note Overload the superclass (NonlinearProgram) for additional
    % updates

    % initialize the type of the variables
    obj.VariableArray = NlpVariable.empty();
    obj.CostArray  = NlpFunction.empty();
    obj.ConstrArray = NlpFunction.empty();
    phase = obj.Phase;
    n_phase = length(phase);

    index_s = 1;
    % register variables
    for i=1:n_phase

        % register variables
        obj = regVariable(obj, phase(i).OptVarTable);

        index_f = sum([obj.VariableArray.Dimension]);
        obj.PhaseVarIndices(i,:)= [index_s, index_f];
        index_s = index_f+1;

        % register constraints
        obj = regConstraint(obj, phase(i).ConstrTable);

        % register cost functions
        obj = regObjective(obj, phase(i).CostTable);

    end

    obj = update@NonlinearProgram(obj);

end


function [feature_matrix, labels] = ...
    get_speaker_features_and_labels(db, field_name)
    % Extracts features and labels from all the samples corresponding
    % to the specified speakers.
    % db - Database of samples generated by gendb()
    % speakers - List of speakers for which to extract features
    % feature_matrix - Feature matrix for input to Diffusion Maps
    % labels - Label for each column of feature_matrix

    unique_field_values = get_field(db, field_name);
    if (strcmp(field_name, 'digit'))
       unique_field_values = cell2mat(unique_field_values);
       unique_field_values(:, 2) = '*';
       unique_field_values = mat2cell(unique_field_values, ...
           ones(size(unique_field_values,1), 1));
       unique_field_values = unique(unique_field_values);
    end

    feature_matrix = [];
    labels = [];
    total_entries = 0; % counter of handled entries

    progressbar;
    NUM_OF_UNIQUE_VALUES = length(unique_field_values);
    for k = 1:NUM_OF_UNIQUE_VALUES
        speaker_db = filterdb(db, field_name, unique_field_values{k});

        % extract features for all speaker samples
        func = @calc_mfcc;
%         func = @calc_stft;
        [features, num_of_success] = extract_features(speaker_db, func);

        if num_of_success > 0
            labels = [labels; ones(num_of_success, 1) * k];
            feature_matrix = [feature_matrix features];
            total_entries = total_entries + num_of_success;
        end

        progressbar(k/NUM_OF_UNIQUE_VALUES);
    end

    fprintf('Handled %d entries\n', total_entries);
end


%Install_lightspeed
% Compiles mex files for the lightspeed library.

% Written by Tom Minka
% (c) Microsoft Corporation. All rights reserved.

% thanks to Kevin Murphy for suggesting this routine.
% thanks to Ruben Martinez-Cantin for UNDERSCORE_LAPACK_CALL

fprintf('Compiling lightspeed 2.6 mex files...\n');
fprintf('Change directory to lightspeed for this to work.\n');

% Matlab version
v = sscanf(version,'%d.%d.%*s (R%d) %*s');
% v(3) is the R number
% could also use v(3)>=13
atleast65 = (v(1)>6 || (v(1)==6 && v(2)>=5));
atleast73 = (v(1)>7 || (v(1)==7 && v(2)>=3));
atleast75 = (v(1)>7 || (v(1)==7 && v(2)>=5));
atleast76 = (v(1)>7 || (v(1)==7 && v(2)>=6));
atleast78 = (v(1)>7 || (v(1)==7 && v(2)>=8)); % R2009a
if atleast73
	% largeArrayDims and mwSize were added in version 7.3 (R2006b)
	% http://www.mathworks.com/help/techdoc/rn/bqt6wtq.html
	flags = ' -largeArrayDims ';
else
	flags = ' -DmwSize=int -DmwIndex=int ';
end

% copy matlab's original repmat.m as xrepmat.m
if exist('xrepmat.m') ~= 2
  w = fullfile(matlabroot,'toolbox','matlab','elmat','repmat.m');
  cmd = ['"' w '" xrepmat.m'];
  if ispc
    system(['copy ' cmd]);
  else
    system(['cp -rp ' cmd]);
  end
end

% these are done first to initialize mex
eval(['mex' flags '-c flops.c']);
eval(['mex' flags 'sameobject.c']);
eval(['mex' flags 'int_hist.c']);
eval(['mex' flags '-c mexutil.c']);
eval(['mex' flags '-c util.c']);

libdir = '';
if ispc
	[compiler,options] = mexcompiler;
	libdir = options.LIBLOC;
	engmatopts = [compiler 'engmatopts.bat'];
elseif ismac
	options = struct;
	% this installer is set up for 64-bit MacOSX 10.6 with gcc-4.0
	% if you are using something else, run 'mex -v -c flops.c'
	% and use the output to change these strings
	options.COMPILER = 'gcc-4.0';
	options.COMPFLAGS = '-fno-common -no-cpp-precomp -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.6.sdk -mmacosx-version-min=10.6  -fexceptions';
	options.OPTIMFLAGS = '-O -DNDEBUG';
else
	options = struct;
	options.COMPILER = 'cc';
	options.COMPFLAGS = '-fPIC';
	options.OPTIMFLAGS = '-O';
end

% Routines that use LAPACK
lapacklib = '';
blaslib = '';
lapackflags = flags;
if atleast78
	lapackflags = [lapackflags ' -DBLAS64'];
end
if ispc
  if strncmp(compiler,'MSVC',4)
    if atleast65
      % version >= 6.5
      lapacklib = fullfile(libdir,'libmwlapack.lib');
    end
  else
    lapacklib = fullfile(libdir,'libmwlapack.lib');
  end
  if atleast75
    blaslib = fullfile(libdir,'libmwblas.lib');
  end
  %%% Paste the location of libmwlapack.lib %%%
  %lapacklib = '';
  if ~exist(lapacklib,'file')
    lapacklib = 'dtrsm.c';
    fprintf('libmwlapack.lib was not found.  To get additional optimizations, paste its location into install_lightspeed.m\n');
  else
    fprintf('Using the lapack library at %s\n',lapacklib);
  end
else
  % in version 7.5, non-PC systems do not need to specify lapacklib,
  % but they must use an underscore when calling lapack routines
	% http://www.mathworks.com/help/techdoc/matlab_external/br_2m24-1.html
	lapackflags = [lapackflags ' -DUNDERSCORE_LAPACK_CALL'];
  if atleast76
    lapacklib = '-lmwlapack';
    blaslib = '-lmwblas';
  end
end
disp(['mex' lapackflags ' solve_triu.c "' lapacklib '" "' blaslib '"'])
eval(['mex' lapackflags ' solve_triu.c "' lapacklib '" "' blaslib '"']);
eval(['mex' lapackflags ' solve_tril.c "' lapacklib '" "' blaslib '"']);

if ispc
  % Windows
  %if exist('util.obj','file')
  eval(['mex' flags 'addflops.c flops.obj'])
	if atleast78
		eval(['mex' flags 'gammaln.c util.obj -outdir @double'])
	else
		eval(['mex' flags 'gammaln.c util.obj'])
	end
  eval(['mex' flags 'digamma.c util.obj'])
  eval(['mex' flags 'trigamma.c util.obj'])
  eval(['mex' flags 'tetragamma.c util.obj'])
	eval(['mex' flags 'setnonzeros.c'])
  if strncmp(compiler,'MSVC',4)
		clear random.dll randomseed randbinom randgamma sample_hist
    disp(['install_random.bat "' options.VSINSTALLDIR '" ' options.vcvarsopts]);
    system(['install_random.bat "' options.VSINSTALLDIR '" ' options.vcvarsopts]);
    eval(['mex' flags 'randomseed.c util.obj random.lib'])
    eval(['mex' flags 'randbinom.c mexutil.obj util.obj random.lib'])
    eval(['mex' flags 'randgamma.c mexutil.obj util.obj random.lib'])
    eval(['mex' flags 'sample_hist.c util.obj random.lib'])
  else
    fprintf('mexcompiler is not MSVC. The randomseed() function will have no effect.');
    eval(['mex' flags 'randomseed.c util.obj random.c'])
    eval(['mex' flags 'randbinom.c mexutil.obj util.obj random.c'])
    eval(['mex' flags 'randgamma.c mexutil.obj util.obj random.c'])
    eval(['mex' flags 'sample_hist.c util.obj random.c'])
  end
  eval(['mex' flags 'repmat.c mexutil.obj'])
  try
    % standalone programs
    % compilation instructions are described at:
    % http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_external/ch1_im15.html#27765
    if atleast78
			disp('lightspeed''s matfile utility is not supported for this version of Matlab');
		elseif atleast65
      % -V5 is required for Matlab >=6.5
      mex('-f',engmatopts,'matfile.c','-V5');
    else
      mex('-f',engmatopts,'matfile.c');
    end
    % uncomment the line below if you want to build test_flops.exe
    % This program lets you check the flop counts on your processor.
    % mex('-f',engmatopts,'tests/test_flops.c');
  catch
    disp('Could not install the standalone programs.');
    disp(lasterr)
  end
else
  % UNIX
  eval(['mex' flags 'addflops.c flops.o'])
	if atleast78
		eval(['mex' flags 'gammaln.c util.o -lm -outdir @double'])
	else
		eval(['mex' flags 'gammaln.c util.o -lm'])
	end
  eval(['mex' flags 'digamma.c util.o -lm'])
  eval(['mex' flags 'trigamma.c util.o -lm'])
  eval(['mex' flags 'tetragamma.c util.o -lm'])
	eval(['mex' flags 'setnonzeros.c'])
  if ismac
    % thanks to Nicholas Butko for these mac-specific lines
		clear librandom.dylib randomseed randbinom randgamma sample_hist
		cmd = [options.COMPILER ' ' options.COMPFLAGS ' ' options.OPTIMFLAGS ' -c random.c; ' options.COMPILER ' ' options.COMPFLAGS ' -dynamiclib -Wl,-install_name,`pwd`/librandom.dylib -o librandom.dylib random.o'];
		disp(cmd);
		system(cmd)
    eval(['mex' flags 'randomseed.c util.o librandom.dylib -lm'])
    eval(['mex' flags 'randbinom.c mexutil.o util.o librandom.dylib -lm'])
    eval(['mex' flags 'randgamma.c mexutil.o util.o librandom.dylib -lm'])
    eval(['mex' flags 'sample_hist.c util.o librandom.dylib -lm'])
  else
    % this command only works on linux
		clear librandom.so randomseed randbinom randgamma sample_hist
    cmd = [options.COMPILER ' ' options.COMPFLAGS ' ' options.OPTIMFLAGS ' -c random.c; ' options.COMPILER ' ' options.COMPFLAGS ' -shared -Wl,-E -Wl,-soname,`pwd`/librandom.so -o librandom.so random.o'];
		disp(cmd);
		system(cmd)
    eval(['mex' flags 'randomseed.c util.o librandom.so -lm'])
    eval(['mex' flags 'randbinom.c mexutil.o util.o librandom.so -lm'])
    eval(['mex' flags 'randgamma.c mexutil.o util.o librandom.so -lm'])
    eval(['mex' flags 'sample_hist.c util.o librandom.so -lm'])
  end
  eval(['mex' flags 'repmat.c mexutil.o'])
  try
    % standalone programs
    if atleast78
			disp('lightspeed''s matfile utility is not supported for this version of Matlab');
		elseif atleast65
      % -V5 is required only for Matlab >=6.5
      mex -f matopts.sh matfile.c -V5
    else
      mex -f matopts.sh matfile.c
    end
    % uncomment the line below if you want to build test_flops.exe
    % This program lets you check the flop counts on your processor.
    % mex -f matopts.sh tests/test_flops.c
  catch
    disp('Could not install the standalone programs.');
    disp(lasterr);
    fprintf('Note: if matlab cannot find matopts.sh, your installation of matlab is faulty.\nIf you get this error, don''t worry, lightspeed should still work.');
  end
end

addpath(genpath(pwd))
fprintf('Done.\n');
fprintf('Type "test_lightspeed" to verify the installation.\n');


function displayTrackingResult(sceneInfo, res, seqName)
% Display Tracking Result
%
% Take scene information sceneInfo and
% the tracking result from stateInfo
%
%
% (C) Anton Andriyenko, 2012
%
% The code may be used free of charge for non-commercial and
% educational purposes, the only requirement is that this text is
% preserved within the derivative work. For any other purpose you
% must contact the authors for permission. This code may not be
% redistributed without written permission from the authors.

%% convert res to stateInfo
stateInfo = [];
stateInfo.F = max(sceneInfo.frameNums);
stateInfo.frameNums = sceneInfo.frameNums;
index = 0;
cur_id = -1;

res = sortrows(res,2);

for i = 1:size(res,1)
    if (cur_id ~= res(i,2))
        cur_id = res(i,2);
        index = index + 1;
        stateInfo.X(:,index) = zeros(stateInfo.F,1);
        stateInfo.Y(:,index) = zeros(stateInfo.F,1);
        stateInfo.Xi(:,index) = zeros(stateInfo.F,1);
        stateInfo.Yi(:,index) = zeros(stateInfo.F,1);
        stateInfo.W(:,index) = zeros(stateInfo.F,1);
        stateInfo.H(:,index) = zeros(stateInfo.F,1);
    end
    bbox = res(i,:);
    n = bbox(1);
    stateInfo.X(n,index) = bbox(3)+0.5*bbox(5);
    stateInfo.Y(n,index) = bbox(4)+bbox(6);
    stateInfo.Xi(n,index) = stateInfo.X(n,index);
    stateInfo.Yi(n,index) = stateInfo.Y(n,index);
    stateInfo.W(n,index) = bbox(5);
    stateInfo.H(n,index) = bbox(6);
end

reopenFig(['Tracking Results of Sequence ' seqName]);
displayBBoxes(sceneInfo, stateInfo);


% h = POINTLAB(P) point cloud visualizer.
%
% P: a struct where P.points contains 3D point coordinates
%
function h_out = pointlab( P )

% setup
h = gca;
hold on;

% modify the position of the axis in the current figure
hg = hgtransform;
setappdata( h, 'hg', hg );
set( h,'Position',[0 0.05 1 1-0.05 ] );
set( gcf, 'color', 'white');

% do not give output if not requested
if nargout == 0
    h_out = [];
else
    h_out = h;
end

%%%%%%%%%%%%%%%%%%%%%%%% menu setup %%%%%%%%%%%%%%%%%%%%%%%%%%%%
menu_view = uimenu('Label','View');
    uimenu( menu_view,'Label','View Samples','Callback',@view_samples_callback);
    uimenu( menu_view,'Label','View Normals','Callback',@view_normals_callback);

menu_tools = uimenu('Label','Tools');
    uimenu( menu_tools,'Label','Pick sample', 'Callback', @point_pick_callback);
    uimenu( menu_tools,'Label','Snapshot', 'Callback', @snapshot_callback);
    uimenu( menu_tools,'Label','Record Fly Around', 'Callback', @record_callback);
% save status variables
h_samples = draw_samples();  setappdata(h,'h_samples',h_samples);

% set drawing limits and vis setup
xlim([-1,1]);
ylim([-1,1]);
axis equal
axis off

function view_samples_callback( IGNORE1, IGNORE2 ) %#ok<INUSD>
    if strcmp( get( h_samples, 'Visible' ), 'on')
        set( h_samples, 'Visible', 'off' )
    else
        set( h_samples, 'Visible', 'on' )
    end
end
% this is slighgly different...
% normals are not drawn by default (they would take too long)
% I just draw them if specifically requested.
function view_normals_callback( IGNORE1, IGNORE2 )  %#ok<INUSD>
    for i=1:size(P.points,1)
        myline( P.points(i,:), P.normals(i,:), .1, 'parent', hg );
    end
end

function point_pick_callback( IGNORE1, IGNORE2 ) %#ok<INUSD>
    % convert selection point in homogeneous and apply transform
	disp( find_closest( h, P.points ) );
end

function snapshot_callback( IGNORE1, IGNORE2 ) %#ok<INUSD>
    outfilename = sprintf('results/snapshot_%s_%s', datestr(now,'ddmmyy'), datestr(now,'HHMMss') );
    print('-dpng', outfilename);
end
function record_callback( IGNORE1, IGNORE2 ) %#ok<INUSD>
    disp('- modify the basic view direction');
    filename = input('- insert the name of the output: ', 's');

    prev_matrix = get( hg, 'matrix' );
    for alpha = linspace(0,2*pi,100);
        rot_matrix = makehgtform('yrotate',alpha);
        set(hg,'Matrix',rot_matrix*prev_matrix);
        drawnow;
        gif_add_frame(gca,filename,2);
    end
end

function h_samples = draw_samples()
    h_samples = plot3( P.points(:,1), P.points(:,2), P.points(:,3), '.b', 'parent', hg, 'MarkerSize', .25 );
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ARCBALL STUFF %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% register callbacks ad
set( gcf, 'WindowButtonMotionFcn', @motion_callback );
set( gcf, 'WindowButtonDownFcn', @buttondown_callback );
set( gcf, 'WindowButtonUpFcn', @buttonup_callback );
mousestatus = 'buttonup';
START = [0,0,0];
M_previous = get( hg, 'Matrix' );

%%%%%%%%%%%% CALLBACKS %%%%%%%%%%%%%%
% motion callback, event "every" mouse movement
function motion_callback(src, event)
    % retrieve the current point
    currp = get(gcf,'CurrentPoint');
    % retrieve window geometry
    HGEOM = get( src, 'Position');
    % transform in sphere coordinates (3,4) = (WIDTH, HEIGHT)
    currp = point_on_sphere( currp, HGEOM(3), HGEOM(4) );

    % workaround condition (see point_on_sphere)
    if isnan(currp)
       return;
    end

    %%%%% ARCBALL COMPUTATION %%%%%
    if strcmp(mousestatus, 'buttondown')
        % compute angle and rotation axis
        rot_dir = cross( START, currp ); rot_dir = rot_dir / norm( rot_dir );
        rot_ang = acos( dot( currp, START ) );

        % convert direction in model coordinate system
        M_tr = inv( M_previous );
        rot_dir = M_tr*[rot_dir,0]';
        rot_dir = rot_dir(1:3);
        rot_dir = rot_dir / norm( rot_dir ); % renormalize
        % construct matrix
        R_matrix = makehgtform('axisrotate',rot_dir,rot_ang);
        % set hgt matrix
        set(hg,'Matrix',M_previous*R_matrix);
        % refresh drawing
        drawnow;
    end
end

% only 1 event on click
function buttondown_callback( src, evnt )
    % change status
    mousestatus = 'buttondown';
    % retrieve the current point
    currp = get(gcf,'CurrentPoint');
    % retrieve window geometry
    HGEOM = get( src, 'Position');
    % SET START POSITION
    START = point_on_sphere( currp, HGEOM(3), HGEOM(4) );
    % SET START MATRIX
    M_previous = get( hg, 'Matrix' );
end
function buttonup_callback( src, evnt )
    % change status
    mousestatus = 'buttonup';
    % reset the start position
    START = [0,0,0];
end

%%%%%%%%%%%% UTILITY FUNCTION %%%%%%%%%%%%%
function currp = point_on_sphere( currp, width, height )
    currp(3) = 0;

    % determine radius of the sphere
    R = min(width, height)/2;

    % TRANSFORM the point in window coordinate into
    % the coordinate of a sphere centered in middle window
    ORIGIN = [width/2, height/2, 0];
    currp = currp - ORIGIN;

    % normalize position to [-1:1] WRT unit sphere
    % centered at the origin of the window
    currp = currp / R;

    % if position is out of sphere, normalize it to
    % unit length
    L = sqrt( currp*currp' );
    if L > 1
       % currp = nan; % workaround to stop evaluation
       % disp('out of sphere');

       currp = currp / L;
       currp(3) = 0;
    else
       % add the Z coordinate to the scheme
       currp(3) = sqrt( 1 - currp(1)^2 - currp(2)^2 );
    end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% END ARCBALL STUFF %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

end % END OF CURVELAB


 function g = disimKernDiagGradient(kern, x, covDiag)

% DISIMKERNDIAGGRADIENT Compute the gradient of the DISIM kernel's diagonal wrt parameters.
% FORMAT
% DESC computes the gradient of functions of the diagonal of the
% single input motif kernel matrix with respect to the parameters of the kernel. The
% parameters' gradients are returned in the order given by the
% disimKernExtractParam command.
% ARG kern : the kernel structure for which the gradients are
% computed.
% ARG x : the input data for which the gradient is being computed.
% ARG factors : partial derivatives of the function of interest with
% respect to the diagonal elements of the kernel.
% RETURN g : gradients of the relevant function with respect to each
% of the parameters. Ordering should match the ordering given in
% disimKernExtractParam.
%
% SEEALSO : disimKernParamInit, kernDiagGradient, disimKernExtractParam, disimKernGradient
%
% COPYRIGHT : Neil D. Lawrence, 2006
%
% COPYRIGHT : Antti Honkela, 2007

% KERN

error('disimKernDiagGradient not yet implemented.')


clear;
clc;

s = iio_sys_obj_matlab; % Constructor
s.ip_address = '10.66.99.200';
s.dev_name = 'adrv9009';
s.in_ch_no = 2;
s.out_ch_no = 2;
s.in_ch_size = 8192;
s.out_ch_size = 8192;

s = s.setupImpl();

input = cell(1, s.in_ch_no + length(s.iio_dev_cfg.cfg_ch));
Fs = 245.76e6;
Fc = 1e6;
Bw = 200e6;
t = 1/Fs:1/Fs:s.in_ch_size/Fs;
for i=1:s.in_ch_no
    input{i} = sin(2*pi*Fc*t+(i-1)*pi/2)*2^15;
end

input{s.getInChannel('TRX_LO_FREQ')} = 2.45e9;
input{s.getInChannel('RX1_RF_BANDWIDTH')} = Bw;
input{s.getInChannel('RX1_GAIN_MODE')} = 'slow_attack';
input{s.getInChannel('RX1_GAIN')} = 0;
input{s.getInChannel('RX2_GAIN_MODE')} = 'slow_attack';
input{s.getInChannel('RX2_RF_BANDWIDTH')} = Bw;
input{s.getInChannel('RX2_GAIN')} = 0;
input{s.getInChannel('TX_RF_BANDWIDTH')} = Bw;
input{s.getInChannel('TX_GAIN')} = -20;

for i = 1:20
output = stepImpl(s, input);
end

s.releaseImpl();

figure % new figure
ax1 = subplot(2,1,1); % top subplot
ax2 = subplot(2,1,2); % bottom subplot

plot(ax1,output{1});
title(ax1,'I');
xlabel('Sample');
ylabel('Amplitude');

plot(ax2,output{2});
title(ax2,'Q');
xlabel('Sample');
ylabel('Amplitude');


% Copyright (C) 2017-2018 Titus Cieslewski, RPG, University of Zurich,
%   Switzerland
%   You can contact the author at <titus at ifi dot uzh dot ch>
% Copyright (C) 2017-2018 Siddharth Choudhary, College of Computing,
%   Georgia Institute of Technology, Atlanta, GA, USA
% Copyright (C) 2017-2018 Davide Scaramuzza, RPG, University of Zurich,
%   Switzerland
%
% This file is part of dslam_open.
%
% dslam_open is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% dslam_open is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with dslam_open. If not, see <http://www.gnu.org/licenses/>.

function [robot_i, frame_i] = gtsamFrameIdToIndices(id)
% robot_i and frame_i are 1-indexed!
assert(isa(id, 'int64'));
frame_i = mod(id, 2^56);
robot_i = (id - frame_i + 1) / 2^56 - 'a' + 1;
end


% Copyright (C) 1994, 1995, 1996, 1997, 1998, 2000, 2002, 2004, 2005,
%               2006, 2007, 2008, 2009 John W. Eaton
%
% This file is part of Octave.
%
% Octave is free software; you can redistribute it and/or modify it
% under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or (at
% your option) any later version.
%
% Octave is distributed in the hope that it will be useful, but
% WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
% General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with Octave; see the file COPYING.  If not, see
% <http://www.gnu.org/licenses/>.

% -*- texinfo -*-
% @deftypefn {Function File} {} postpad (@var{x}, @var{l}, @var{c})
% @deftypefnx {Function File} {} postpad (@var{x}, @var{l}, @var{c}, @var{dim})
% @seealso{prepad, resize}
% @end deftypefn

% Author: Tony Richardson <arichard@stark.cc.oh.us>
% Created: June 1994

function y = postpad (x, l, c, dim)

if nargin < 2 || nargin > 4
  %print_usage ();
  error('wrong number of input arguments, should be between 2 and 4');
end

if nargin < 3 || isempty(c)
  c = 0;
else
  if ~isscalar(c)
    error ('postpad: third argument must be empty or a scalar');
  end
end

nd = ndims(x);
sz = size(x);
if nargin < 4
  % Find the first non-singleton dimension
  dim  = 1;
  while dim < nd+1 && sz(dim)==1
    dim = dim + 1;
  end
  if dim > nd
    dim = 1;
  elseif ~(isscalar(dim) && dim == round(dim)) && dim > 0 && dim< nd+1
    error('postpad: dim must be an integer and valid dimension');
  end
end

if ~isscalar(l) || l<0
  error ('second argument must be a positive scalar');
end

if dim > nd
  sz(nd+1:dim) = 1;
end

d = sz(dim);

if d >= l
  idx = cell(1,nd);
  for i = 1:nd
    idx{i} = 1:sz(i);
  end
  idx{dim} = 1:l;
  y = x(idx{:});
else
  sz(dim) = l-d;
  y = cat(dim, x, c * ones(sz));
end