classdef testDecompositionCore < TestCase
    methods
        %% Setup functions
        function self = testDecompositionCore(name)
            self = self@TestCase(name);
        end
        
        %% Info
        % For the following function it would make sense to test them
        %   separately (in the same way it would make sense to have them as
        %   user functions: decomposeMinkowskiHrep,
        %   computeSetDifferenceHrep, computeSetIntersectionHrep.
        
        %% fixPosition (just A)
        % example: octahedron
        % test: position-fix-condition (from thesis)
        
        %% fixPosition (with given constraint, zerotol, 2nd output)
        % example: octahedron; constraint fixes one direction (two opposing
        %   facets with equal distances
        % test: position-fix-condition (from thesis); use a [1 1 .. 1]
        %   vector and try mapping to fulfill constraint
        
        %% obtainDec (simple example)
        function testDecCube(self)
        % decompose a simple cube, the result should be the three line 
        % segments
        cdef = obtainCrystal('cuboid');
        dec = obtainDec(cdef.A, 'mapping', cdef.groupMappingMatrix, ...
            'verbose', 0);
        seExpected = eye(3);
        
        % check numbers
        assertEqual(dec.nC, 3, 'failed: dimension of hC-vectors');
        assertEqual(dec.nS, 3, 'failed: number of structuring elements');
        assertEqual(dec.nPU, 1, 'failed: number of unified partitions');
        assertEqual(dec.nPS, 1, 'failed: number of simplex partitions');
        
        % are the structuring elements the three line segments?
        assertEqual(eliminateRows(dec.se, seExpected, eps), [], ...
            'failed: structuring elements');
        end
        
        %% obtainDec (complex example)
        function testDecPotash(self)
        % decompose the standard potash example with (6+8+12) faces, three
        %   face groups and two unified/simplex partitions
        cdef = obtainCrystal('generic_potash_alum', 3);
        dec = obtainDec(cdef.A, 'mapping', cdef.groupMappingMatrix, ...
            'verbose', 0);
        load dataCasePotash
        
        % check numbers
        assertEqual(dec.nC, decReference.nC, 'failed: dimension of hC-vectors');
        assertEqual(dec.nS, decReference.nS, 'failed: number of structuring elements');
        assertEqual(dec.nPU, decReference.nPU, 'failed: number of unified partitions');
        assertEqual(dec.nPS, decReference.nPS, 'failed: number of simplex partitions');
        
        % check decomposition
        [decTmp, acceptDec] = validateDec(dec, 'verbose', 0);
        assertTrue(acceptDec==1, ...
            'verification of decomposition failed');
        
        % are the structuring elements right
        assertElementsAlmostEqual(sortrows(dec.se), ...
            sortrows(decReference.se), 'relative', eps, 30*eps, ...
            'failed: structuring elements');
        
        % are the partitions identical
        alterPart = [2 1];
        for iPart = 1:dec.nPS
            assertElementsAlmostEqual(...
                sortrows(dec.simplexPartition{alterPart(iPart)}.A), ...
                sortrows(decReference.simplexPartition{iPart}.A), ...
                'relative', eps, 200*eps, ['failed: simplex partition ' ...
                num2str(iPart)]);
        end
        
        % continue with measures
        dirMatrix = [1         0         0     ; ...
            1         1         0     ; ...
            1         1         1     ; ...
            0.3023    0.1863    0.5388; ...
            0.1468    0.3456    0.4192; ...
            0.0923    0.3968    0.6852];
        dec = addMeasureDataDec(dec, 'volume');
        dec = addMeasureDataDec(dec, 'surface');
        dec = addMeasureDataDec(dec, 'projection', dirMatrix);
        dec = addMeasureDataDec(dec, 'facet', [1 0 0; 1 1 0; 1 1 1]);
        dec = addMeasureDataDec(dec, 'feret', dirMatrix);
        dec = addMeasureDataDec(dec, 'support', dirMatrix);
        
        assertTrue(prod(prod(validateMeasureDataDec(dec))) == 1, ...
            'verification of measure data failed');
        
        % compare measures for each simplex partition, measure and
        % parameter
        measureNameList = fieldnames(dec.measureData);
        for iMeas = 1:length(measureNameList)
            thisMeas = dec.measureData.(measureNameList{iMeas});
            for iPart = 1:dec.nPS
                if thisMeas.isPar
                    for iPar = 1:length(thisMeas.tensorSe)
                    assertElementsAlmostEqual(thisMeas.tensorHc{iPar}{alterPart(iPart)}, ...
                      decReference.measureData.(measureNameList{iMeas}).tensorHc{iPar}{iPart}, ...
                      'relative', eps, 6e3*eps, ...
                      ['failed: measure tensorhC: ' measureNameList{iMeas}]);
                    end
                else
                    assertElementsAlmostEqual(thisMeas.tensorHc{alterPart(iPart)}, ...
                        decReference.measureData.(measureNameList{iMeas}).tensorHc{iPart}, ...
                        'relative', eps, 9e4*eps, ...
                        ['failed: measure tensorhC: ' measureNameList{iMeas}]);
                end
            end 
        end
        
        end
        
        %% obtainDec (h>=0 not required example)
        % missing.
        
        %% obtainDec (position not fixed)
        % missing.
        
        %% obtainDec (constraint given)
        % missing.
        
        %% obtainDec (constraint and mapping given)
        % missing (both valid and non-matching)
        
        %% validateDec
        % Test the validation of the decomposition
        function testValidateDec(self)
            load dataCasePotash
            
            % Q1 shoud fail, if one unified partition is deleted
            % It fails also: Q4 (total volume).
            dec = decReference;
            dec.unifiedPartition{end} = [];
            dec.nPU = dec.nPU - 1;
            textOutput = evalc('disp('' '');validateDec(dec, ''verbose'', 2);');
            
            assertTrue(~isempty(strfind(textOutput, '[-] Q1')), ...
                ['Q1 should fail. Current output:' textOutput]);
            assertTrue(~isempty(strfind(textOutput, '[-] Q4')), ...
                ['Q4 should fail. Current output:' textOutput]);
            
            % Q2 should fail, if we alter an outer se sligthly
            % It fails also: L1 (this se is decomposable); L3 (se is not more the same)
            dec = decReference;
            dec.seScaledToClose(1, 1) = dec.seScaledToClose(1, 1) * (1 - elkZerotol*10);
            textOutput = evalc('disp('' '');validateDec(dec, ''verbose'', 2);');
            
            assertTrue(~isempty(strfind(textOutput, '[-] Q2')), ...
                ['Q2 should fail. Current output:' textOutput]);
            assertTrue(~isempty(strfind(textOutput, '[-] L1')), ...
                ['L1 should fail. Current output:' textOutput]);
            assertTrue(~isempty(strfind(textOutput, '[-] L3')), ...
                ['L3 should fail. Current output:' textOutput]);
            
            % Q3 should fail if we set a volume to zero
            % It fails also: Q4 (total volume)
            dec = decReference;
            dec.unifiedPartition{end}.volumeScaled = 0;
            textOutput = evalc('disp('' '');validateDec(dec, ''verbose'', 2);');
            
            assertTrue(~isempty(strfind(textOutput, '[-] Q3')), ...
                ['Q3 should fail. Current output:' textOutput]);
            assertTrue(~isempty(strfind(textOutput, '[-] Q4')), ...
                ['Q4 should fail. Current output:' textOutput]);
            
            % Q4 should fail if we slightly alter the volume of one partition.
            % Nothing else fails.
            dec = decReference;
            dec.unifiedPartition{end}.volumeScaled = ...
                dec.unifiedPartition{end}.volumeScaled * (1 + elkZerotol*10);
            textOutput = evalc('disp('' '');validateDec(dec, ''verbose'', 2);');
            
            assertTrue(~isempty(strfind(textOutput, '[-] Q4')), ...
                ['Q4 should fail. Current output:' textOutput]);
            
            % L1 should fail, if we alter an se sligthly. We use an inner one so that
            %   Q2 is not triggered.
            % It fails also: L3 (the se is not any more the same)
            dec = decReference;
            dec.seScaledToClose(end, 1) = dec.seScaledToClose(3, 1) * (1 - elkZerotol*10);
            textOutput = evalc('disp('' '');validateDec(dec, ''verbose'', 2);');
            
            assertTrue(~isempty(strfind(textOutput, '[-] L1')), ...
                ['L1 should fail. Current output:' textOutput]);
            assertTrue(~isempty(strfind(textOutput, '[-] L3')), ...
                ['L3 should fail. Current output:' textOutput]);
            assertTrue(~isempty(strfind(textOutput, '[+] Q2')), ...
                ['Q2 should not fail. Current output:' textOutput]);
            
            
            % L2 is hard to make it fail with simple examples
            % L3 is already tested with the cases above        
        end
        
        %% Improper example
        function testImproperThree(self)
            A = [1 -1 0 0 2 -2 2 -2; 0 0 1 -1 2 -2 -2 2]';
            A(A==-2) = -1/sqrt(2);
            A(A==2) = 1/sqrt(2);
            M = [1 1 1 1 0 0 0 0;0 0 0 0 2 2 1 1]';
            
            assertExceptionThrown(@()(obtainDec(A, 'mapping', M)), ...
                'elk:decomposition:improper');
             
            % just try if it works otherwise (no testing)
            dec = obtainDec(A, 'mapping', M, 'allowImproper', -1, 'verbose', 0);
        end
                
        
        %% mapToValidConeDec
        function testWrongMapping01(self)
            load('debugPositionMatrix_wrongMapping_01.mat')
            dec = obtainCrystalDec('generic_potash_alum', 3, 0, 0);
            positionMatrix = mapToValidityCone(positionMatrix, dec, 0);
            assertTrue(all(all(dec.validityCone.A * positionMatrix <= eps)), ...
                'Position matrix not mapped to validity cone properly');
        end
        
        %% bugfix case: zerotol=0 (error found in REV470)
        % (this case might be void when the decomposition structure
        %  changes)
        function bugfixMappingZerotolZero(self)
            load('bugfixMappingZerotolZero.mat');
            mapToValidityCone(hcMatrix, dec, 0);
        end
        
%         %% Test cases - MINKOWSKI DECOMPOSITION
%         %
%         % Input: without constraint; mapping constraint; matrix constraint
%         %  >> probably remove mapping possibility!
%         %
%         % Output: summands cell array, summands matrix, domain in hC-space
%         %
%         % Examples: mix-cube-octahedron (constrained), cube (unconstrained),
%         % extremely small example, extremely large example
%         function testMinkowskiCube(self)
%             hrep = obtainHrep('cube');
%             hrepList = decomposeMinkowskiHrep(hrep);
%             
%             assertEqual(length(hrepList), 3, ...
%                 'Decomposition of cube should yield three summands');
%             
%             vrep = convertHrepToVrep(hrepList{1});
%             for iSummand = 2:length(hrepList)
%                 vrep = addMinkowskiVrep(vrep, ...
%                     convertHrepToVrep(hrepList{iSummand}));
%             end
%             vrepOriginal = convertHrepToVrep(hrep);
%             
%             assertEqual(eliminateRows(vrep.V, vrepOriginal.V, 3*eps), [], ...
%                 'Minkowski decomposition does not sum up to original');
%                 
%         end
%         
%         function testMinkowskiSphere(self)
%             hrep = obtainHrep('sphere', 5);
%             hrepList = decomposeMinkowskiHrep(hrep);
%             
%             vrep = convertHrepToVrep(hrepList{1});
%             for iSummand = 2:length(hrepList)
%                 vrep = addMinkowskiVrep(vrep, ...
%                     convertHrepToVrep(hrepList{iSummand}));
%             end
%             vrepOriginal = convertHrepToVrep(hrep);
%             
%             assertEqual(eliminateRows(vrep.V, vrepOriginal.V, 5000*eps), [], ...
%                 'Minkowski decomposition does not sum up to original');
%                 
%         end
%         
%         function testMinkowskiMany(self)
%             % OK, this produces errors. Even the sphere thing does not run
%             %   well with larger parameters. However, I have to focus on a
%             %   somehow working result to start with shape approximation!!
%             vrep = obtainVrep('cube');
% %            vrep = addMinkowskiVrep(vrep, obtainVrep('bipyramid', 3));
% %            vrep = addMinkowskiVrep(vrep, obtainVrep('bipyramid', 5));
%             %vrep = obtainVrep('pyramid', 7);
%             vrep = addMinkowskiVrep(vrep, obtainVrep('pyramid', 7));
%             vrep = addMinkowskiVrep(vrep, obtainVrep('antiprism', 4));
%             hrep = convertVrepToHrep(vrep);
%             vrepOriginal = vrep;
%             viewPolytope(vrep);
%             
%             hrepList = decomposeMinkowskiHrep(hrep);
%             
%             vrep = convertHrepToVrep(hrepList{1});
%             for iSummand = 2:length(hrepList)
%                 vrep = addMinkowskiVrep(vrep, ...
%                     convertHrepToVrep(hrepList{iSummand}));
%             end
%             
%             assertEqual(eliminateRows(vrep.V, vrepOriginal.V, 5000*eps), [], ...
%                 'Minkowski decomposition does not sum up to original');
%                 
%         end
        
    end
end