function varargout = compareStructs( s1, s2, reportCompatible, path )
%ok = compareStructs( s1, s2, reportCompatible, path )
%   Report on any differences of structure between s1 and s2.
%   A difference of structure is:
%       s1 and s2 are of different classes.
%       s1 and s2 are both arrays, struct arrays, or cell arrays, but have
%           different shapes. 
%       s1 and s2 are both structs, but have different sets of fields.
%       s1 and s2 are both structs, but for some field f belonging to both,
%           s1.f and s2.f have an incompatibility.
%   The result is true iff there are no incompatibilities.
%
%   If reportCompatible is true, compatible components of s1 and s2 will be
%   reported, otherwise only incompatibilities will be reported.
%
%   path is for internal use only

    if (nargin < 3) || isempty( reportCompatible )
        reportCompatible = false;
    end
    
    if (nargin < 4) || isempty( path )
        path = 'ROOT';
    end
    
    compatible = true;
    
    % Check same class.
    c1 = class(s1);
    c2 = class(s2);
    if ~strcmp(c1,c2)
        fprintf( 1, 'At %s, classes are %s and %s.\n', ...
            path, c1, c2 );
        compatible = false;
    end
    
    % Check same size.
    sz1 = size(s1);
    sz2 = size(s2);
    if any(sz1 ~= sz2)
        fprintf( 1, 'At %s, sizes are [', path );
        fprintf( 1, ' %d', sz1 );
        fprintf( 1, ' ] and [' );
        fprintf( 1, ' %d', sz2 );
        fprintf( 1, ' ].\n' );
        compatible = false;
%     elseif ~strcmp( class(s1), class(s2) )
%         fprintf( 1, 'At %s, classes are %s and %s.\n', path, class(s1), class(s2) );
    elseif (isnumeric(s1) || islogical(s1)) && any( s1(:) ~= s2(:) )
        fprintf( 1, 'At %s, elements differ.\n', path );
    end
    
    if isstruct(s1) && isstruct(s2)
        % Check same field names.
        f1 = fieldnames(s1);
        f2 = fieldnames(s2);
        f1minus2 = setdiff(f1,f2);
        f2minus1 = setdiff(f2,f1);
        f12 = intersect(f1,f2);
        if ~isempty(f1minus2)
            fprintf( 1, 'At %s, these fields occur only in the first struct:\n   ', path );
            fprintf( 1, ' %s', f1minus2{:} );
            fprintf( 1, '\n' );
            compatible = false;
        end
        if ~isempty(f2minus1)
            fprintf( 1, 'At %s, these fields occur only in the second struct:\n   ', path );
            fprintf( 1, ' %s', f2minus1{:} );
            fprintf( 1, '\n' );
            compatible = false;
        end
        % Compare corresponding field values.
        for i=1:length(f12)
            ok = compareStructs( [s1.(f12{i})], [s2.(f12{i})], ...
                reportCompatible, [ path, '.', f12{i} ] );
            compatible = compatible && ok;
        end
    end
    if reportCompatible && compatible
        fprintf( 1, 'Compatible at %s.\n', path );
    end
    
    if nargout > 0
        varargout{1} = compatible;
    end
end
