test surficial io gifti

function test_suite = test_surficial_io_gifti
    % tests for GIFTI input/output
    %
    % #   For CoSMoMVPA's copyright information and license terms,   #
    % #   see the COPYING file distributed with CoSMoMVPA.           #
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions = localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function test_gifti_dataset_io()
    if cosmo_skip_test_if_no_external('gifti')
        return
    end

    ds = cosmo_synthetic_dataset('ntargets', 2, 'nchunks', 1, ...
                                 'type', 'surface');
    ds.a.fdim.values{1} = 1 + [1 4 7 8 5 3];

    encodings = get_gifti_encodings();
    orders = get_indexing_orders();

    fn = cosmo_make_temp_filename('_tmp', '.gii');
    cleaner = onCleanup(@()delete(fn));

    % only test subset
    ntest = 2;

    n_encodings = numel(encodings);
    n_orders = numel(orders);

    rp = randperm(n_encodings * n_orders);
    test_idxs = rp(1:ntest);

    counter = 0;
    for k = 1:numel(encodings)
        encoding = encodings{k};
        for j = 1:numel(orders)
            counter = counter + 1;
            if ~any(test_idxs == counter)
                continue
            end

            order = orders{j};

            cosmo_map2surface(ds, fn, 'encoding', encoding);
            ds2 = cosmo_surface_dataset(fn);
            assert_dataset_equal(ds, ds2);

            g_str = get_gifti_string(encoding, order);
            fid = fopen(fn, 'w');
            fprintf(fid, '%s', g_str);
            fclose(fid);

            ds3 = cosmo_surface_dataset(fn);
            assert_dataset_equal(ds, ds3);

            g = gifti(fn);
            assertElementsAlmostEqual(double(g.cdata)', ds.samples, ...
                                      'absolute', 1e-5);
            node_indices = ds.a.fdim.values{1}(ds.fa.node_indices);
            assertEqual(double(g.indices)', node_indices);

            g2 = cosmo_map2surface(ds, '-gii');
            assertElementsAlmostEqual(g.cdata, g2.cdata, ...
                                      'absolute', 1e-5);
            assertEqual(g.indices, g2.indices);
        end
    end

function assert_dataset_equal(x, y)
    assertElementsAlmostEqual(x.samples, y.samples, 'absolute', 1e-5);
    assertEqual(sort(fieldnames(x)), sort(fieldnames(y)));
    assertEqual(x.fa, y.fa);
    assertEqual(x.a, y.a);

function string = get_gifti_string(encoding, indexing_order)
    header = get_header();
    body = get_data_arrays(encoding, indexing_order);
    footer = get_footer();

    string = [header body footer];

function string = get_header()
    string = sprintf(['<?xml version="1.0" encoding="UTF-8"?>\n'...
                      '<!DOCTYPE GIFTI SYSTEM "http://gifti.projects.nitr'...
                      'c.org/gifti.dtd">\n'...
                      '<GIFTI Version="1.0"  NumberOfDataArrays="3">\n'...
                      '   <MetaData>\n'...
                      '      <MD>\n'...
                      '         <Name><![CDATA[UniqueID]]></Name>\n'...
                      '         <Value><![CDATA[XYZ_Uj6iYZUAV3HKbGirxcn-c'...
                      'A]]></Value>\n'...
                      '      </MD>\n'...
                      '   </MetaData>\n'...
                      '   <LabelTable/>\n' ...
                     ]);

function string = get_footer()
    string = '</GIFTI>';

function encodings = get_gifti_encodings()
    encodings = {'ASCII', 'Base64Binary', 'GZipBase64Binary'};

function orders = get_indexing_orders()
    orders = {'ColumnMajorOrder', 'RowMajorOrder'};

function string = get_data_array_helper(data_str, encoding, data_type, ...
                                        indexing_order, intent)
    check_one('encoding', encoding, ...
              get_gifti_encodings());
    check_one('data_type', data_type, ...
              {'NIFTI_TYPE_INT32', 'NIFTI_TYPE_FLOAT32'});
    check_one('indexing_order', indexing_order, ...
              get_indexing_orders());

    string = sprintf([ ...
                      '   <DataArray Intent="%s"\n'...
                      '              DataType="%s"\n'...
                      '              ArrayIndexingOrder="%s"\n'...
                      '              Dimensionality="1"\n'...
                      '              Dim0="6"\n'...
                      '              Encoding="%s"\n'...
                      '              Endian="LittleEndian"\n'...
                      '              ExternalFileName=""\n'...
                      '              ExternalFileOffset="">\n'...
                      '      <MetaData>\n'...
                      '         <MD>\n'...
                      '            <Name><![CDATA[Name]]></Name>\n'...
                      '            <Value><![CDATA[numeric]]></Value>\n'...
                      '         </MD>\n'...
                      '      </MetaData>\n'...
                      '      <Data>\n%s'...
                      '      </Data>\n'...
                      '   </DataArray>\n' ...
                     ], intent, data_type, indexing_order, encoding, data_str);

function string = get_data_arrays(encoding, indexing_order)
    switch encoding
        case 'ASCII'
            data = {sprintf(['        2.031686\n'...
                             '        -3.684948\n'...
                             '        -1.050449\n'...
                             '        1.349442\n'...
                             '        -0.261723\n'...
                             '        -0.203955\n' ...
                            ]), ...
                    sprintf(['        0.583806\n'...
                             '        1.723501\n'...
                             '        -1.326491\n'...
                             '        -0.397334\n'...
                             '        2.338657\n'...
                             '        0.482345\n' ...
                            ]), ...
                    sprintf(['         1 \n'...
                             '         4  \n'...
                             '         7 \n'...
                             '         8 \n'...
                             '         5 \n'...
                             '         3 \n' ...
                            ])};
        case 'Base64Binary'
            data = {'JQcCQDDWa8AddYa/hLqsP48Ahr6U2VC+', ...
                    'T3QVP66b3D91yqm/XW/Lvo6sFUDt9fY+', ...
                    'AQAAAAQAAAAHAAAACAAAAAUAAAADAAAA' ...
                   };
        case 'GZipBase64Binary'
            data = {  'eAEBGADn/yUHAkAw1mvAHXWGv4S6rD+PAIa+lNlQvnPfCu4=', ...
                    'eAEBGADn/090FT+um9w/dcqpv11vy76OrBVA7fX2PpcYDR0=', ...
                    'eAEBGADn/wEAAAAEAAAABwAAAAgAAAAFAAAAAwAAAAGEAB0=' ...
                   };
    end

    narrays = numel(data);
    arrays_cell = cell(1, narrays);

    data_types = {'NIFTI_TYPE_FLOAT32', ...
                  'NIFTI_TYPE_FLOAT32', ...
                  'NIFTI_TYPE_INT32' ...
                 };
    intents = {'NIFTI_INTENT_NONE', ...
               'NIFTI_INTENT_NONE', ...
               'NIFTI_INTENT_NODE_INDEX'};

    assert(numel(data_types) == narrays);
    assert(numel(intents) == narrays);

    for k = 1:narrays
        arrays_cell{k} = get_data_array_helper(data{k}, encoding, ...
                                               data_types{k}, ...
                                               indexing_order, intents{k});
    end

    string = cat(2, arrays_cell{:});

function check_one(label, value, allowed)
    if isempty(strmatch(value, allowed))
        error('illegal value for %s: %s', label, value);
    end