/*
 *  $Id: raw-data.c 28166 2025-06-25 18:11:15Z yeti-dn $
 *  Copyright (C) 2009-2025 David Nečas (Yeti).
 *  E-mail: yeti@gwyddion.net.
 *
 *  This program 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 2 of the License, or (at your option) any
 *  later version.
 *
 *  This program 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 this program; if not, write to the
 *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "tests/testlibgwy.h"

/* XXX: This file is a minefield. Compilers like to miscompile it in mysterious ways, making the tests inexplicably
 * fail – until one starts adding debug prints, which usually makes the problem disappear. The actual conversion code
 * is compiled separately and cannot depend on how we run the test. So even though the conversion is the part doing
 * some silly bit shuffling, it is probably fine. It seems the part making compilers lose their shit is the heavy use
 * of g_assert_cmpfloat(). No idea why. */

/* NAN which does not depend on fast-math. We actually do not like returning NANs, but it is unclear what should
 * happen when the raw data literally contain NANs. It is fair to pass them to the caller unmolested. */
static const GDoubleIEEE754 simple_nan = {
    .mpn.mantissa_low = 0u,
    .mpn.mantissa_high = 0x8000u,
    .mpn.biased_exponent = 0x7ffu,
    .mpn.sign = 0u,
};

void
test_raw_data_size_consistency(void)
{
    static GwyRawDataType types[] = {
        GWY_RAW_DATA_SINT8,
        GWY_RAW_DATA_UINT8,
        GWY_RAW_DATA_SINT16,
        GWY_RAW_DATA_UINT16,
        GWY_RAW_DATA_SINT32,
        GWY_RAW_DATA_UINT32,
        GWY_RAW_DATA_SINT64,
        GWY_RAW_DATA_UINT64,
        GWY_RAW_DATA_HALF,
        GWY_RAW_DATA_FLOAT,
        GWY_RAW_DATA_REAL,
        GWY_RAW_DATA_DOUBLE,
        GWY_RAW_DATA_BIT,
        GWY_RAW_DATA_SINT4,
        GWY_RAW_DATA_UINT4,
        GWY_RAW_DATA_SINT12,
        GWY_RAW_DATA_UINT12,
        GWY_RAW_DATA_SINT24,
        GWY_RAW_DATA_UINT24,
        GWY_RAW_DATA_EXTENDED,
    };
    for (guint i = 0; i < G_N_ELEMENTS(types); i++) {
        gsize byte_size = gwy_raw_data_size(types[i]);
        gsize bit_size = gwy_raw_data_size_bits(types[i]);
        g_assert_true(bit_size || !byte_size);
        if (byte_size)
            g_assert_cmpuint(8*byte_size, ==, bit_size);
        else if (bit_size)
            g_assert_cmpuint(bit_size % 8, !=, 0);
    }
}

static void
check_conversion(gconstpointer raw_data, gint stride,
                 GwyRawDataType raw_type, GwyByteOrder byte_order,
                 gdouble scale, gdouble offset,
                 const gdouble *expected, guint n, gdouble eps)
{
    gdouble converted[n];
    for (guint i = 0; i < n; i++)
        converted[n] = simple_nan.v_double;
    gwy_convert_raw_data(raw_data, n, stride, raw_type, byte_order, converted, scale, offset);

#if 0
    for (guint i = 0; i < n; i++)
        fprintf(stderr, "[%u] %g(0x%lx) vs. %g\n", i, converted[i], (gulong)converted[i], expected[i]);
#endif

    /* with-epsilon means smaller than epsilon which fails even for exact equality */
    if (eps == 0.0) {
        /* For some reason, the following line seems to prevent CLANG 1.18.1 in Fedora 40 from miscompiling the cycle
         * to some completely broken nonsense (which seems to want to jump to ’address’ in one of the floats). */
        g_assert_cmpfloat(converted[0], ==, expected[0]);
        for (guint i = 0; i < n; i++)
            g_assert_cmpfloat(converted[i], ==, expected[i]);
    }
    else {
        for (guint i = 0; i < n; i++)
            g_assert_cmpfloat_with_epsilon(converted[i], expected[i], eps);
    }
}

void
test_raw_data_uint8_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x00, 0x01, 0xff, 0x7f, 0x80 };
    static const gdouble expected[] = { 0, 1, 255, 127, 128 };
    static const gdouble expectedq[] = { 0, 2, 510, 254, 256 };
    static const gdouble expectedo[] = { -1, 0, 254, 126, 127 };
    static const gdouble expectedqo[] = { -1, 1, 509, 253, 255 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, 0,
                     expectedq, G_N_ELEMENTS(expectedq), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, -1,
                     expectedo, G_N_ELEMENTS(expectedo), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, -1,
                     expectedqo, G_N_ELEMENTS(expectedqo), 0.0);
}

void
test_raw_data_uint8_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x00, 0x01, 0xff, 0x7f, 0x80 };
    static const gdouble expected[] = { 0, 1, 255, 127, 128 };
    static const gdouble expectedq[] = { 0, 2, 510, 254, 256 };
    static const gdouble expectedo[] = { -1, 0, 254, 126, 127 };
    static const gdouble expectedqo[] = { -1, 1, 509, 253, 255 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 2, 0,
                     expectedq, G_N_ELEMENTS(expectedq), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 1, -1,
                     expectedo, G_N_ELEMENTS(expectedo), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 2, -1,
                     expectedqo, G_N_ELEMENTS(expectedqo), 0.0);
}

void
test_raw_data_uint8_stride0(void)
{
    static const guint8 raw_data[] = { 0x12 };
    static const gdouble expected[] = { 18, 18, 18, 18, 18 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint8_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x00, 0x01, 0xff, 0x7f, 0x80 };
    static const gdouble expected[] = { 0, 1, -1, 127, -128 };
    static const gdouble expectedq[] = { 0, 2, -2, 254, -256 };
    static const gdouble expectedo[] = { -1, 0, -2, 126, -129 };
    static const gdouble expectedqo[] = { -1, 1, -3, 253, -257 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, 0,
                     expectedq, G_N_ELEMENTS(expectedq), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, -1,
                     expectedo, G_N_ELEMENTS(expectedo), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, -1,
                     expectedqo, G_N_ELEMENTS(expectedqo), 0.0);
}

void
test_raw_data_sint8_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x00, 0x01, 0xff, 0x7f, 0x80 };
    static const gdouble expected[] = { 0, 1, -1, 127, -128 };
    static const gdouble expectedq[] = { 0, 2, -2, 254, -256 };
    static const gdouble expectedo[] = { -1, 0, -2, 126, -129 };
    static const gdouble expectedqo[] = { -1, 1, -3, 253, -257 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 2, 0,
                     expectedq, G_N_ELEMENTS(expectedq), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 1, -1,
                     expectedo, G_N_ELEMENTS(expectedo), 0.0);
    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT8, GWY_BYTE_ORDER_BIG_ENDIAN, 2, -1,
                     expectedqo, G_N_ELEMENTS(expectedqo), 0.0);
}

void
test_raw_data_uint8_stride2(void)
{
    static const guint8 raw_data[] = { 0x00, 0x33, 0x01, 0x33, 0xff, 0x33, 0x7f, 0x33, 0x80 };
    static const gdouble expected[] = { 0, 1, 255, 127, 128 };
    static const gdouble expectedq[] = { 0, 2, 510, 254, 256 };
    static const gdouble expectedo[] = { -1, 0, 254, 126, 127 };
    static const gdouble expectedqo[] = { -1, 1, 509, 253, 255 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, 0,
                     expectedq, G_N_ELEMENTS(expectedq), 0.0);
    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, -1,
                     expectedo, G_N_ELEMENTS(expectedo), 0.0);
    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, -1,
                     expectedqo, G_N_ELEMENTS(expectedqo), 0.0);
}

void
test_raw_data_uint8_stridem1(void)
{
    static const guint8 raw_data[] = { 0x80, 0x7f, 0xff, 0x01, 0x00 };
    static const gdouble expected[] = { 0, 1, 255, 127, 128 };
    static const gdouble expectedq[] = { 0, 2, 510, 254, 256 };
    static const gdouble expectedo[] = { -1, 0, 254, 126, 127 };
    static const gdouble expectedqo[] = { -1, 1, 509, 253, 255 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT8);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end, -1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, 0,
                     expectedq, G_N_ELEMENTS(expectedq), 0.0);
    check_conversion(data_end, -1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, -1,
                     expectedo, G_N_ELEMENTS(expectedo), 0.0);
    check_conversion(data_end, -1, GWY_RAW_DATA_UINT8, GWY_BYTE_ORDER_LITTLE_ENDIAN, 2, -1,
                     expectedqo, G_N_ELEMENTS(expectedqo), 0.0);
}

void
test_raw_data_uint4_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x01, 0xf7, 0x84, 0x3f };
    static const gdouble expected0[] = { 0, 1, 15, 7, 8, 4, 3 };
    static const gdouble expected1[] = { 1, 15, 7, 8, 4, 3, 15 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 2*1 + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 2*1 + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
}

void
test_raw_data_uint4_be_stride2(void)
{
    static const guint8 raw_data0[] = { 0x0c, 0x1c, 0xfc, 0x7c, 0x8c };
    static const guint8 raw_data1[] = { 0xc0, 0xc1, 0xcf, 0xc7, 0xc8 };
    static const gdouble expected[] = { 0, 1, 15, 7, 8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*2 + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*2 + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint4_be_stride3(void)
{
    static const guint8 raw_data0[] = { 0x0c, 0xc1, 0xcc, 0xfc, 0xc7, 0xcc, 0x8c };
    static const guint8 raw_data1[] = { 0xc0, 0xcc, 0x1c, 0xcf, 0xcc, 0x7c, 0xc8 };
    static const gdouble expected[] = { 0, 1, 15, 7, 8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*3 + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*3 + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

/* XXX: This test (alongside with three more analogous ones) seems miscompiled by Ubunutu 24.04.2 LTS's
 * gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
 * We get some bogus value in data_end after a couple of check_conversion()s.
 *
 * Valgrind does not find anything and as far as I can tell gwy_convert_raw_data() is not overwriting the variable on
 * stack. If we ever try to look at the value of data_end here, the segfault disapears. So it depends on data_end
 * being a register variable? Any random code changes, like not adding memcpy(converted, expected, ...) to
 * check_conversion() can make the segfault disappear. Changing gwy_convert_raw_data() itself does not seem to have
 * any effect.
 *
 * If we look at the pointer inside gwy_convert_raw_data() we see it is getting the bogus value.
 */
void
test_raw_data_uint4_be_stridem1(void)
{
    static const guint8 raw_data[] = { 0xf3, 0x48, 0x7f, 0x10 };
    static const gdouble expected0odd[] = { 0, 1, 15, 7, 8, 4, 3 };
    static const gdouble expected1odd[] = { 1, 15, 7, 8, 4, 3, 15 };
    static const gdouble expected0even[] = { 0, 1, 15, 7, 8, 4 };
    static const gdouble expected1even[] = { 1, 15, 7, 8, 4, 3 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT4) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    /* This testcase can be confusing. Remainder of 0 means we start at the first nibble in the byte (according to
     * endianness) by definition. But we go backwards, so we do not continue to the second nibble, but to the last
     * nibble of the previous byte. */
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0odd, G_N_ELEMENTS(expected0odd), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1odd, G_N_ELEMENTS(expected1odd), 0.0);
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0even, G_N_ELEMENTS(expected0even), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1even, G_N_ELEMENTS(expected1even), 0.0);
}

void
test_raw_data_uint4_be_stridem2(void)
{
    static const guint8 raw_data0[] = { 0x3c, 0x4c, 0x8c, 0x7c, 0xfc, 0x1c, 0x0c };
    static const guint8 raw_data1[] = { 0xc3, 0xc4, 0xc8, 0xc7, 0xcf, 0xc1, 0xc0 };
    static const gdouble expected[] = { 0, 1, 15, 7, 8, 4, 3 };
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0)-1;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1)-1;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-2) + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-2) + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint4_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x3a };
    static const gdouble expected[] = { 3, 3, 3, 3, 3 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint4_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x01, 0xf7, 0x84, 0x3f };
    static const gdouble expected0[] = { 0, 1, -1, 7, -8, 4, 3 };
    static const gdouble expected1[] = { 1, -1, 7, -8, 4, 3, -1 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 2*1 + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 2*1 + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
}

void
test_raw_data_sint4_be_stride2(void)
{
    static const guint8 raw_data0[] = { 0x0c, 0x1c, 0xfc, 0x7c, 0x8c };
    static const guint8 raw_data1[] = { 0xc0, 0xc1, 0xcf, 0xc7, 0xc8 };
    static const gdouble expected[] = { 0, 1, -1, 7, -8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*2 + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*2 + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint4_be_stride3(void)
{
    static const guint8 raw_data0[] = { 0x0c, 0xc1, 0xcc, 0xfc, 0xc7, 0xcc, 0x8c };
    static const guint8 raw_data1[] = { 0xc0, 0xcc, 0x1c, 0xcf, 0xcc, 0x7c, 0xc8 };
    static const gdouble expected[] = { 0, 1, -1, 7, -8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*3 + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*3 + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint4_be_stridem1(void)
{
    static const guint8 raw_data[] = { 0xf3, 0x48, 0x7f, 0x10 };
    static const gdouble expected0odd[] = { 0, 1, -1, 7, -8, 4, 3 };
    static const gdouble expected1odd[] = { 1, -1, 7, -8, 4, 3, -1 };
    static const gdouble expected0even[] = { 0, 1, -1, 7, -8, 4 };
    static const gdouble expected1even[] = { 1, -1, 7, -8, 4, 3 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - (gwy_raw_data_size_bits(GWY_RAW_DATA_SINT4) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    /* This testcase can be confusing. Remainder of 0 means we start at the first nibble in the byte (according to
     * endianness) by definition. But we go backwards, so we do not continue to the second nibble, but to the last
     * nibble of the previous byte. */
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0odd, G_N_ELEMENTS(expected0odd), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1odd, G_N_ELEMENTS(expected1odd), 0.0);
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0even, G_N_ELEMENTS(expected0even), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1even, G_N_ELEMENTS(expected1even), 0.0);
}

void
test_raw_data_sint4_be_stridem2(void)
{
    static const guint8 raw_data0[] = { 0x3c, 0x4c, 0x8c, 0x7c, 0xfc, 0x1c, 0x0c };
    static const guint8 raw_data1[] = { 0xc3, 0xc4, 0xc8, 0xc7, 0xcf, 0xc1, 0xc0 };
    static const gdouble expected[] = { 0, 1, -1, 7, -8, 4, 3 };
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0)-1;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1)-1;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-2) + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-2) + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint4_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x10, 0x7f, 0x48, 0xf3 };
    static const gdouble expected0[] = { 0, 1, 15, 7, 8, 4, 3 };
    static const gdouble expected1[] = { 1, 15, 7, 8, 4, 3, 15 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 2*1 + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 2*1 + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
}

void
test_raw_data_uint4_le_stride0(void)
{
    static const guint8 raw_data[] = { 0xa3 };
    static const gdouble expected[] = { 3, 3, 3, 3, 3 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint4_le_stride2(void)
{
    static const guint8 raw_data0[] = { 0xc0, 0xc1, 0xcf, 0xc7, 0xc8 };
    static const guint8 raw_data1[] = { 0x0c, 0x1c, 0xfc, 0x7c, 0x8c };
    static const gdouble expected[] = { 0, 1, 15, 7, 8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*2 + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*2 + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint4_le_stride3(void)
{
    static const guint8 raw_data0[] = { 0xc0, 0x1c, 0xcc, 0xcf, 0x7c, 0xcc, 0xc8 };
    static const guint8 raw_data1[] = { 0x0c, 0xcc, 0xc1, 0xfc, 0xcc, 0xc7, 0x8c };
    static const gdouble expected[] = { 0, 1, 15, 7, 8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*3 + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*3 + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint4_le_stridem1(void)
{
    static const guint8 raw_data[] = { 0x3f, 0x84, 0xf7, 0x01 };
    static const gdouble expected0odd[] = { 0, 1, 15, 7, 8, 4, 3 };
    static const gdouble expected1odd[] = { 1, 15, 7, 8, 4, 3, 15 };
    static const gdouble expected0even[] = { 0, 1, 15, 7, 8, 4 };
    static const gdouble expected1even[] = { 1, 15, 7, 8, 4, 3 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT4) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    /* This testcase can be confusing. Remainder of 0 means we start at the first nibble in the byte (according to
     * endianness) by definition. But we go backwards, so we do not continue to the second nibble, but to the last
     * nibble of the previous byte. */
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0odd, G_N_ELEMENTS(expected0odd), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1odd, G_N_ELEMENTS(expected1odd), 0.0);
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0even, G_N_ELEMENTS(expected0even), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1even, G_N_ELEMENTS(expected1even), 0.0);
}

void
test_raw_data_uint4_le_stridem2(void)
{
    static const guint8 raw_data0[] = { 0xc3, 0xc4, 0xc8, 0xc7, 0xcf, 0xc1, 0xc0 };
    static const guint8 raw_data1[] = { 0x3c, 0x4c, 0x8c, 0x7c, 0xfc, 0x1c, 0x0c };
    static const gdouble expected[] = { 0, 1, 15, 7, 8, 4, 3 };
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0)-1;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1)-1;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-2) + 0, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-2) + 1, GWY_RAW_DATA_UINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint4_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x10, 0x7f, 0x48, 0xf3 };
    static const gdouble expected0[] = { 0, 1, -1, 7, -8, 4, 3 };
    static const gdouble expected1[] = { 1, -1, 7, -8, 4, 3, -1 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 2*1 + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 2*1 + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
}

void
test_raw_data_sint4_le_stride2(void)
{
    static const guint8 raw_data0[] = { 0xc0, 0xc1, 0xcf, 0xc7, 0xc8 };
    static const guint8 raw_data1[] = { 0x0c, 0x1c, 0xfc, 0x7c, 0x8c };
    static const gdouble expected[] = { 0, 1, -1, 7, -8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*2 + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*2 + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint4_le_stride3(void)
{
    static const guint8 raw_data0[] = { 0xc0, 0x1c, 0xcc, 0xcf, 0x7c, 0xcc, 0xc8 };
    static const guint8 raw_data1[] = { 0x0c, 0xcc, 0xc1, 0xfc, 0xcc, 0xc7, 0x8c };
    static const gdouble expected[] = { 0, 1, -1, 7, -8 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*3 + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*3 + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint4_le_stridem1(void)
{
    static const guint8 raw_data[] = { 0x3f, 0x84, 0xf7, 0x01 };
    static const gdouble expected0odd[] = { 0, 1, -1, 7, -8, 4, 3 };
    static const gdouble expected1odd[] = { 1, -1, 7, -8, 4, 3, -1 };
    static const gdouble expected0even[] = { 0, 1, -1, 7, -8, 4 };
    static const gdouble expected1even[] = { 1, -1, 7, -8, 4, 3 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - (gwy_raw_data_size_bits(GWY_RAW_DATA_SINT4) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    /* This testcase can be confusing. Remainder of 0 means we start at the first nibble in the byte (according to
     * endianness) by definition. But we go backwards, so we do not continue to the second nibble, but to the last
     * nibble of the previous byte. */
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0odd, G_N_ELEMENTS(expected0odd), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1odd, G_N_ELEMENTS(expected1odd), 0.0);
    check_conversion(data_end, 2*(-1) + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0even, G_N_ELEMENTS(expected0even), 0.0);
    check_conversion(data_end, 2*(-1) + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1even, G_N_ELEMENTS(expected1even), 0.0);
}

void
test_raw_data_sint4_le_stridem2(void)
{
    static const guint8 raw_data0[] = { 0xc3, 0xc4, 0xc8, 0xc7, 0xcf, 0xc1, 0xc0 };
    static const guint8 raw_data1[] = { 0x3c, 0x4c, 0x8c, 0x7c, 0xfc, 0x1c, 0x0c };
    static const gdouble expected[] = { 0, 1, -1, 7, -8, 4, 3 };
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0)-1;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1)-1;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-2) + 0, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-2) + 1, GWY_RAW_DATA_SINT4, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_bit_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x5a, 0x96 };
    static const gdouble expected0[] = { 0, 1, 0, 1, 1, 0, 1, 0, 1 };
    static const gdouble expected1[] = { 1, 0, 1, 1, 0, 1, 0, 1, 0 };
    static const gdouble expected2[] = { 0, 1, 1, 0, 1, 0, 1, 0, 0 };
    static const gdouble expected3[] = { 1, 1, 0, 1, 0, 1, 0, 0, 1 };
    static const gdouble expected4[] = { 1, 0, 1, 0, 1, 0, 0, 1, 0 };
    static const gdouble expected5[] = { 0, 1, 0, 1, 0, 0, 1, 0, 1 };
    static const gdouble expected6[] = { 1, 0, 1, 0, 0, 1, 0, 1, 1 };
    static const gdouble expected7[] = { 0, 1, 0, 0, 1, 0, 1, 1, 0 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 8*1 + 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 8*1 + 1, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
    check_conversion(raw_data, 8*1 + 2, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected2, G_N_ELEMENTS(expected2), 0.0);
    check_conversion(raw_data, 8*1 + 3, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected3, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 4, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected4, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 5, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected5, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 6, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected6, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 7, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected7, G_N_ELEMENTS(expected3), 0.0);
}

void
test_raw_data_bit_be_stride2(void)
{
    static const guint8 raw_data[] = { 0x5a, 0x96, 0x47 };
    static const gdouble expected0[] = { 0, 0, 1, 1, 1, 0, 0, 1 };
    static const gdouble expected1[] = { 1, 1, 0, 0, 0, 1, 1, 0 };
    static const gdouble expected2[] = { 0, 1, 1, 1, 0, 0, 1, 0 };
    static const gdouble expected3[] = { 1, 0, 0, 0, 1, 1, 0, 1 };
    static const gdouble expected4[] = { 1, 1, 1, 0, 0, 1, 0, 0 };
    static const gdouble expected5[] = { 0, 0, 0, 1, 1, 0, 1, 0 };
    static const gdouble expected6[] = { 1, 1, 0, 0, 1, 0, 0, 0 };
    static const gdouble expected7[] = { 0, 0, 1, 1, 0, 1, 0, 1 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 8*2 + 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 8*2 + 1, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
    check_conversion(raw_data, 8*2 + 2, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected2, G_N_ELEMENTS(expected2), 0.0);
    check_conversion(raw_data, 8*2 + 3, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected3, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 4, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected4, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 5, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected5, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 6, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected6, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 7, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected7, G_N_ELEMENTS(expected3), 0.0);
}

void
test_raw_data_bit_be_stridem1(void)
{
    static const guint8 raw_data[] = { 0x5a, 0x96, 0x47 };
    static const gdouble expected0[] = { 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1 };
    static const gdouble expected1[] = { 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0 };
    static const gdouble expected2[] = { 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 };
    static const gdouble expected3[] = { 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0 };
    static const gdouble expected4[] = { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0 };
    static const gdouble expected5[] = { 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1 };
    static const gdouble expected6[] = { 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0 };
    static const gdouble expected7[] = { 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - (gwy_raw_data_size_bits(GWY_RAW_DATA_BIT) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end, 8*(-1) + 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(data_end, 8*(-1) + 1, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
    check_conversion(data_end, 8*(-1) + 2, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected2, G_N_ELEMENTS(expected2), 0.0);
    check_conversion(data_end, 8*(-1) + 3, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected3, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 4, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected4, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 5, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected5, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 6, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected6, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 7, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected7, G_N_ELEMENTS(expected3), 0.0);
}

void
test_raw_data_bit_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x5a, 0x69 };
    static const gdouble expected0[] = { 0, 1, 0, 1, 1, 0, 1, 0, 1 };
    static const gdouble expected1[] = { 1, 0, 1, 1, 0, 1, 0, 1, 0 };
    static const gdouble expected2[] = { 0, 1, 1, 0, 1, 0, 1, 0, 0 };
    static const gdouble expected3[] = { 1, 1, 0, 1, 0, 1, 0, 0, 1 };
    static const gdouble expected4[] = { 1, 0, 1, 0, 1, 0, 0, 1, 0 };
    static const gdouble expected5[] = { 0, 1, 0, 1, 0, 0, 1, 0, 1 };
    static const gdouble expected6[] = { 1, 0, 1, 0, 0, 1, 0, 1, 1 };
    static const gdouble expected7[] = { 0, 1, 0, 0, 1, 0, 1, 1, 0 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 8*1 + 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 8*1 + 1, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
    check_conversion(raw_data, 8*1 + 2, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected2, G_N_ELEMENTS(expected2), 0.0);
    check_conversion(raw_data, 8*1 + 3, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected3, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 4, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected4, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 5, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected5, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 6, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected6, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*1 + 7, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected7, G_N_ELEMENTS(expected3), 0.0);
}

void
test_raw_data_bit_le_stride2(void)
{
    static const guint8 raw_data[] = { 0x5a, 0x69, 0xe2 };
    static const gdouble expected0[] = { 0, 0, 1, 1, 1, 0, 0, 1 };
    static const gdouble expected1[] = { 1, 1, 0, 0, 0, 1, 1, 0 };
    static const gdouble expected2[] = { 0, 1, 1, 1, 0, 0, 1, 0 };
    static const gdouble expected3[] = { 1, 0, 0, 0, 1, 1, 0, 1 };
    static const gdouble expected4[] = { 1, 1, 1, 0, 0, 1, 0, 0 };
    static const gdouble expected5[] = { 0, 0, 0, 1, 1, 0, 1, 0 };
    static const gdouble expected6[] = { 1, 1, 0, 0, 1, 0, 0, 0 };
    static const gdouble expected7[] = { 0, 0, 1, 1, 0, 1, 0, 1 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data, 8*2 + 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 8*2 + 1, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
    check_conversion(raw_data, 8*2 + 2, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected2, G_N_ELEMENTS(expected2), 0.0);
    check_conversion(raw_data, 8*2 + 3, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected3, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 4, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected4, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 5, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected5, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 6, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected6, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(raw_data, 8*2 + 7, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected7, G_N_ELEMENTS(expected3), 0.0);
}

void
test_raw_data_bit_le_stridem1(void)
{
    static const guint8 raw_data[] = { 0x5a, 0x69, 0xe2 };
    static const gdouble expected0[] = { 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1 };
    static const gdouble expected1[] = { 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0 };
    static const gdouble expected2[] = { 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 };
    static const gdouble expected3[] = { 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0 };
    static const gdouble expected4[] = { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0 };
    static const gdouble expected5[] = { 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1 };
    static const gdouble expected6[] = { 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0 };
    static const gdouble expected7[] = { 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1 };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - (gwy_raw_data_size_bits(GWY_RAW_DATA_BIT) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end, 8*(-1) + 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(data_end, 8*(-1) + 1, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
    check_conversion(data_end, 8*(-1) + 2, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected2, G_N_ELEMENTS(expected2), 0.0);
    check_conversion(data_end, 8*(-1) + 3, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected3, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 4, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected4, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 5, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected5, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 6, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected6, G_N_ELEMENTS(expected3), 0.0);
    check_conversion(data_end, 8*(-1) + 7, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected7, G_N_ELEMENTS(expected3), 0.0);
}

void
test_raw_data_bit_stride0(void)
{
    static const guint8 raw_data[] = { 0x71 };
    static const gdouble expected0[] = { 0, 0, 0, 0, 0 };
    static const gdouble expected1[] = { 1, 1, 1, 1, 1 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected0, G_N_ELEMENTS(expected0), 0.0);
    check_conversion(raw_data, 0, GWY_RAW_DATA_BIT, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected1, G_N_ELEMENTS(expected1), 0.0);
}

void
test_raw_data_uint12_be_stride1(void)
{
    static const guint8 raw_data0[] = { 0x00, 0x1f, 0xff, 0x80, 0x07, 0xff, 0x12, 0x3f };
    static const guint8 raw_data1[] = { 0xf0, 0x01, 0xff, 0xf8, 0x00, 0x7f, 0xf1, 0x23 };
    static const gdouble expected[] = { 1, 4095, 2048, 2047, 291 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*1 + 0, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*1 + 1, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint12_be_stride2(void)
{
    static const guint8 raw_data0[] = {
        0x00, 0x1c, 0xcc, 0xff, 0xfc, 0xcc, 0x80, 0x0c, 0xcc, 0x7f, 0xfc, 0xcc, 0x12, 0x3f
    };
    static const guint8 raw_data1[] = {
        0xf0, 0x01, 0xcc, 0xcf, 0xff, 0xcc, 0xc8, 0x00, 0xcc, 0xc7, 0xff, 0xcc, 0xc1, 0x23
    };
    static const gdouble expected[] = { 1, 4095, 2048, 2047, 291 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*2 + 0, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*2 + 1, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint12_be_stride3(void)
{
    static const guint8 raw_data0[] = {
        0x00, 0x1c, 0xcc, 0xcc, 0xcf, 0xff, 0xcc, 0xcc, 0xcc, 0x80, 0x0c, 0xcc, 0xcc, 0xc7, 0xff, 0xcc, 0xcc, 0xcc,
        0x12, 0x3f
    };
    static const guint8 raw_data1[] = {
        0xf0, 0x01, 0xcc, 0xcc, 0xcc, 0xff, 0xfc, 0xcc, 0xcc, 0xc8, 0x00, 0xcc, 0xcc, 0xcc, 0x7f, 0xfc, 0xcc, 0xcc,
        0xc1, 0x23 };
    static const gdouble expected[] = { 1, 4095, 2048, 2047, 291 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*3 + 0, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*3 + 1, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint12_be_stridem1(void)
{
    static const guint8 raw_data1[] = { 0xf1, 0x23, 0x7f, 0xf8, 0x00, 0xff, 0xf0, 0x01 };
    static const guint8 raw_data0[] = { 0x12, 0x37, 0xff, 0x80, 0x0f, 0xff, 0x00, 0x1f };
    static const gdouble expected[] = { 1, 4095, 2048, 2047, 291 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-1) + 0, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-1) + 1, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint12_be_stridem2(void)
{
    static const guint8 raw_data0[] = {
        0x12, 0x3c, 0xcc, 0x7f, 0xfc, 0xcc, 0x80, 0x0c, 0xcc, 0xff, 0xfc, 0xcc, 0x00, 0x1f
    };
    static const guint8 raw_data1[] = {
        0xf1, 0x23, 0xcc, 0xc7, 0xff, 0xcc, 0xc8, 0x00, 0xcc, 0xcf, 0xff, 0xcc, 0xc0, 0x01
    };
    static const gdouble expected[] = { 1, 4095, 2048, 2047, 291 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-2) + 0, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-2) + 1, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint12_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x12, 0x3c };
    static const gdouble expected[] = { 291, 291, 291, 291, 291 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint12_be_stride1(void)
{
    static const guint8 raw_data0[] = { 0x00, 0x1f, 0xff, 0x80, 0x07, 0xff, 0x12, 0x3f };
    static const guint8 raw_data1[] = { 0xf0, 0x01, 0xff, 0xf8, 0x00, 0x7f, 0xf1, 0x23 };
    static const gdouble expected[] = { 1, -1, -2048, 2047, 291 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*1 + 0, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*1 + 1, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint12_be_stride2(void)
{
    static const guint8 raw_data0[] = {
        0x00, 0x1c, 0xcc, 0xff, 0xfc, 0xcc, 0x80, 0x0c, 0xcc, 0x7f, 0xfc, 0xcc, 0x12, 0x3f
    };
    static const guint8 raw_data1[] = {
        0xf0, 0x01, 0xcc, 0xcf, 0xff, 0xcc, 0xc8, 0x00, 0xcc, 0xc7, 0xff, 0xcc, 0xc1, 0x23
    };
    static const gdouble expected[] = { 1, -1, -2048, 2047, 291 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*2 + 0, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*2 + 1, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint12_be_stride3(void)
{
    static const guint8 raw_data0[] = {
        0x00, 0x1c, 0xcc, 0xcc, 0xcf, 0xff, 0xcc, 0xcc, 0xcc, 0x80, 0x0c, 0xcc, 0xcc, 0xc7, 0xff, 0xcc, 0xcc, 0xcc,
        0x12, 0x3f
    };
    static const guint8 raw_data1[] = {
        0xf0, 0x01, 0xcc, 0xcc, 0xcc, 0xff, 0xfc, 0xcc, 0xcc, 0xc8, 0x00, 0xcc, 0xcc, 0xcc, 0x7f, 0xfc, 0xcc, 0xcc,
        0xc1, 0x23 };
    static const gdouble expected[] = { 1, -1, -2048, 2047, 291 };

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(raw_data0, 2*3 + 0, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(raw_data1, 2*3 + 1, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint12_be_stridem1(void)
{
    static const guint8 raw_data1[] = { 0xf1, 0x23, 0x7f, 0xf8, 0x00, 0xff, 0xf0, 0x01 };
    static const guint8 raw_data0[] = { 0x12, 0x37, 0xff, 0x80, 0x0f, 0xff, 0x00, 0x1f };
    static const gdouble expected[] = { 1, -1, -2048, 2047, 291 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-1) + 0, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-1) + 1, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint12_be_stridem2(void)
{
    static const guint8 raw_data0[] = {
        0x12, 0x3c, 0xcc, 0x7f, 0xfc, 0xcc, 0x80, 0x0c, 0xcc, 0xff, 0xfc, 0xcc, 0x00, 0x1f
    };
    static const guint8 raw_data1[] = {
        0xf1, 0x23, 0xcc, 0xc7, 0xff, 0xcc, 0xc8, 0x00, 0xcc, 0xcf, 0xff, 0xcc, 0xc0, 0x01
    };
    static const gdouble expected[] = { 1, -1, -2048, 2047, 291 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end0 = raw_data0 + G_N_ELEMENTS(raw_data0) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;
    const guint8 *data_end1 = raw_data1 + G_N_ELEMENTS(raw_data1) - (gwy_raw_data_size_bits(GWY_RAW_DATA_UINT12) + 7)/8;

    /* The stride is given as n*s + r, where n is the ‘denominator’, s is the ‘true stride’, and r offset within the
     * starting byte (which gets convoluted when moving backwards). */
    check_conversion(data_end0, 2*(-2) + 0, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
    check_conversion(data_end1, 2*(-2) + 1, GWY_RAW_DATA_SINT12, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0x12, 0x34 };
    static const gdouble expected[] = { 1, 65535, 32768, 32767, 4660 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x01, 0xcc, 0xcc,
        0xff, 0xff, 0xcc, 0xcc,
        0x80, 0x00, 0xcc, 0xcc,
        0x7f, 0xff, 0xcc, 0xcc,
        0x12, 0x34
    };
    static const gdouble expected[] = { 1, 65535, 32768, 32767, 4660 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_be_stridem1(void)
{
    static const guint8 raw_data[] = { 0x12, 0x34, 0x7f, 0xff, 0x80, 0x00, 0xff, 0xff, 0x00, 0x01 };
    static const gdouble expected[] = { 1, 65535, 32768, 32767, 4660 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT16);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x12, 0x34 };
    static const gdouble expected[] = { 4660, 4660, 4660, 4660, 4660 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint16_be_stride1(void)
{
    static const guint8 raw_data[] = { 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0x12, 0x34 };
    static const gdouble expected[] = { 1, -1, -32768, 32767, 4660 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint16_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x01, 0xcc, 0xcc,
        0xff, 0xff, 0xcc, 0xcc,
        0x80, 0x00, 0xcc, 0xcc,
        0x7f, 0xff, 0xcc, 0xcc,
        0x12, 0x34
    };
    static const gdouble expected[] = { 1, -1, -32768, 32767, 4660 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint16_be_stridem1(void)
{
    static const guint8 raw_data[] = { 0x12, 0x34, 0x7f, 0xff, 0x80, 0x00, 0xff, 0xff, 0x00, 0x01 };
    static const gdouble expected[] = { 1, -1, -32768, 32767, 4660 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT16);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x01, 0x00, 0xff, 0xff, 0x00, 0x80, 0xff, 0x7f, 0x34, 0x12 };
    static const gdouble expected[] = { 1, 65535, 32768, 32767, 4660 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0xcc, 0xcc,
        0xff, 0xff, 0xcc, 0xcc,
        0x00, 0x80, 0xcc, 0xcc,
        0xff, 0x7f, 0xcc, 0xcc,
        0x34, 0x12
    };
    static const gdouble expected[] = { 1, 65535, 32768, 32767, 4660 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_le_stridem1(void)
{
    static const guint8 raw_data[] = { 0x34, 0x12, 0xff, 0x7f, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00 };
    static const gdouble expected[] = { 1, 65535, 32768, 32767, 4660 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT16);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint16_le_stride0(void)
{
    static const guint8 raw_data[] = { 0x34, 0x12 };
    static const gdouble expected[] = { 4660, 4660, 4660, 4660, 4660 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint16_le_stride1(void)
{
    static const guint8 raw_data[] = { 0x01, 0x00, 0xff, 0xff, 0x00, 0x80, 0xff, 0x7f, 0x34, 0x12 };
    static const gdouble expected[] = { 1, -1, -32768, 32767, 4660 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint16_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0xcc, 0xcc,
        0xff, 0xff, 0xcc, 0xcc,
        0x00, 0x80, 0xcc, 0xcc,
        0xff, 0x7f, 0xcc, 0xcc,
        0x34, 0x12
    };
    static const gdouble expected[] = { 1, -1, -32768, 32767, 4660 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint16_le_stridem1(void)
{
    static const guint8 raw_data[] = { 0x34, 0x12, 0xff, 0x7f, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00 };
    static const gdouble expected[] = { 1, -1, -32768, 32767, 4660 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT16);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x01,
        0xff, 0xff, 0xff,
        0x80, 0x00, 0x00,
        0x7f, 0xff, 0xff,
        0x12, 0x34, 0x56
    };
    static const gdouble expected[] = { 1, 16777215, 8388608, 8388607, 1193046 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xcc, 0xcc, 0xcc,
        0x12, 0x34, 0x56
    };
    static const gdouble expected[] = { 1, 16777215, 8388608, 8388607, 1193046 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x12, 0x34, 0x56,
        0x7f, 0xff, 0xff,
        0x80, 0x00, 0x00,
        0xff, 0xff, 0xff,
        0x00, 0x00, 0x01
    };
    static const gdouble expected[] = { 1, 16777215, 8388608, 8388607, 1193046 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT24);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x12, 0x34, 0x56 };
    static const gdouble expected[] = { 1193046, 1193046, 1193046, 1193046, 1193046 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00,
        0xff, 0xff, 0xff,
        0x00, 0x00, 0x80,
        0xff, 0xff, 0x7f,
        0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, 16777215, 8388608, 8388607, 1193046 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc,
        0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, 16777215, 8388608, 8388607, 1193046 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x56, 0x34, 0x12,
        0xff, 0xff, 0x7f,
        0x00, 0x00, 0x80,
        0xff, 0xff, 0xff,
        0x01, 0x00, 0x00
    };
    static const gdouble expected[] = { 1, 16777215, 8388608, 8388607, 1193046 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT24);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint24_le_stride0(void)
{
    static const guint8 raw_data[] = { 0x56, 0x34, 0x12 };
    static const gdouble expected[] = { 1193046, 1193046, 1193046, 1193046, 1193046 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint24_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x01,
        0xff, 0xff, 0xff,
        0x80, 0x00, 0x00,
        0x7f, 0xff, 0xff,
        0x12, 0x34, 0x56
    };
    static const gdouble expected[] = { 1, -1, -8388608, 8388607, 1193046 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint24_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xcc, 0xcc, 0xcc,
        0x12, 0x34, 0x56
    };
    static const gdouble expected[] = { 1, -1, -8388608, 8388607, 1193046 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint24_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x12, 0x34, 0x56,
        0x7f, 0xff, 0xff,
        0x80, 0x00, 0x00,
        0xff, 0xff, 0xff,
        0x00, 0x00, 0x01
    };
    static const gdouble expected[] = { 1, -1, -8388608, 8388607, 1193046 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT24);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT24, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint24_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00,
        0xff, 0xff, 0xff,
        0x00, 0x00, 0x80,
        0xff, 0xff, 0x7f,
        0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, -1, -8388608, 8388607, 1193046 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint24_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc,
        0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, -1, -8388608, 8388607, 1193046 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint24_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x56, 0x34, 0x12,
        0xff, 0xff, 0x7f,
        0x00, 0x00, 0x80,
        0xff, 0xff, 0xff,
        0x01, 0x00, 0x00
    };
    static const gdouble expected[] = { 1, -1, -8388608, 8388607, 1193046 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT24);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT24, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x01,
        0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00,
        0x7f, 0xff, 0xff, 0xff,
        0x12, 0x34, 0x56, 0x78
    };
    static const gdouble expected[] = { 1, 4294967295, 2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc,
        0x12, 0x34, 0x56, 0x78
    };
    static const gdouble expected[] = { 1, 4294967295, 2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x12, 0x34, 0x56, 0x78,
        0x7f, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x01
    };
    static const gdouble expected[] = { 1, 4294967295, 2147483648, 2147483647, 305419896 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT32);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x12, 0x34, 0x56, 0x78 };
    static const gdouble expected[] = { 305419896, 305419896, 305419896, 305419896, 305419896 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0x7f,
        0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, 4294967295, 2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc, 0xcc,
        0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, 4294967295, 2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x78, 0x56, 0x34, 0x12,
        0xff, 0xff, 0xff, 0x7f,
        0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff,
        0x01, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = { 1, 4294967295, 2147483648, 2147483647, 305419896 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT32);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint32_le_stride0(void)
{
    static const guint8 raw_data[] = { 0x78, 0x56, 0x34, 0x12 };
    static const gdouble expected[] = { 305419896, 305419896, 305419896, 305419896, 305419896 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint32_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x01,
        0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00,
        0x7f, 0xff, 0xff, 0xff,
        0x12, 0x34, 0x56, 0x78
    };
    static const gdouble expected[] = { 1, -1, -2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint32_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc,
        0x12, 0x34, 0x56, 0x78
    };
    static const gdouble expected[] = { 1, -1, -2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint32_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x12, 0x34, 0x56, 0x78,
        0x7f, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x01
    };
    static const gdouble expected[] = { 1, -1, -2147483648, 2147483647, 305419896 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT32);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT32, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint32_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0x7f,
        0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, -1, -2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint32_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc, 0xcc,
        0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = { 1, -1, -2147483648, 2147483647, 305419896 };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint32_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x78, 0x56, 0x34, 0x12,
        0xff, 0xff, 0xff, 0x7f,
        0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff,
        0x01, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = { 1, -1, -2147483648, 2147483647, 305419896 };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT32);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT32, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
    };
    static const gdouble expected[] = {
        1,
        18446744073709551615.0,
        /* These two are actually indistinguishable after rounding. */
        9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
    };
    static const gdouble expected[] = {
        1,
        18446744073709551615.0,
        /* These two are actually indistinguishable after rounding. */
        9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
    };
    static const gdouble expected[] = {
        1,
        18446744073709551615.0,
        /* These two are actually indistinguishable after rounding. */
        9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT64);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 };
    static const gdouble expected[] = {
        1311768467463790320.0,
        1311768467463790320.0,
        1311768467463790320.0,
        1311768467463790320.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
        0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = {
        1,
        18446744073709551615.0,
        /* These two are actually indistinguishable after rounding. */
        9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = {
        1,
        18446744073709551615.0,
        /* These two are actually indistinguishable after rounding. */
        9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = {
        1,
        18446744073709551615.0,
        /* These two are actually indistinguishable after rounding. */
        9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_UINT64);

    check_conversion(data_end, -1, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_uint64_le_stride0(void)
{
    static const guint8 raw_data[] = { 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12 };
    static const gdouble expected[] = {
        1311768467463790320.0,
        1311768467463790320.0,
        1311768467463790320.0,
        1311768467463790320.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 0, GWY_RAW_DATA_UINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint64_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
    };
    static const gdouble expected[] = {
        1,
        -1,
        -9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint64_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
    };
    static const gdouble expected[] = {
        1,
        -1,
        -9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint64_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
    };
    static const gdouble expected[] = {
        1,
        -1,
        -9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT64);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT64, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint64_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
        0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = {
        1,
        -1,
        -9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_SINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint64_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12
    };
    static const gdouble expected[] = {
        1,
        -1,
        -9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_SINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_sint64_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = {
        1,
        -1,
        -9223372036854775808.0,
        9223372036854775807.0,
        1311768467463790320.0
    };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_SINT64);

    check_conversion(data_end, -1, GWY_RAW_DATA_SINT64, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

/* The half byte representations were verified using GCC's _Float16 and Imath from OpenEXR.
 * FIXME: We do not care that deeply about denormalised values, but probably we should include them. */
void
test_raw_data_half_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00,
        0x3c, 0x00,
        0x42, 0x48,
        0xc2, 0x48,
        0x04, 0x00,
        0x84, 0x00,
        0x7b, 0xff,
        0xfb, 0xff
    };
    static const gdouble expected[] = {
        0.0,
        1.0,
        3.140625,
        -3.140625,
        6.103515625e-05,
        -6.103515625e-05,
        65504.0,
        -65504.0
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0xcc, 0xcc,
        0x3c, 0x00, 0xcc, 0xcc,
        0x42, 0x48, 0xcc, 0xcc,
        0xc2, 0x48, 0xcc, 0xcc,
        0x04, 0x00, 0xcc, 0xcc,
        0x84, 0x00, 0xcc, 0xcc,
        0x7b, 0xff, 0xcc, 0xcc,
        0xfb, 0xff
    };
    static const gdouble expected[] = {
        0.0,
        1.0,
        3.140625,
        -3.140625,
        6.103515625e-05,
        -6.103515625e-05,
        65504.0,
        -65504.0
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0xfb, 0xff,
        0x7b, 0xff,
        0x84, 0x00,
        0x04, 0x00,
        0xc2, 0x48,
        0x42, 0x48,
        0x3c, 0x00,
        0x00, 0x00
    };
    static const gdouble expected[] = {
        0.0,
        1.0,
        3.140625,
        -3.140625,
        6.103515625e-05,
        -6.103515625e-05,
        65504.0,
        -65504.0
    };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_HALF);

    check_conversion(data_end, -1, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x42, 0x48 };
    static const gdouble expected[] = { 3.140625, 3.140625, 3.140625, 3.140625, 3.140625 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00,
        0x00, 0x3c,
        0x48, 0x42,
        0x48, 0xc2,
        0x00, 0x04,
        0x00, 0x84,
        0xff, 0x7b,
        0xff, 0xfb
    };
    static const gdouble expected[] = {
        0.0,
        1.0,
        3.140625,
        -3.140625,
        6.103515625e-05,
        -6.103515625e-05,
        65504.0,
        -65504.0
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0xcc, 0xcc,
        0x00, 0x3c, 0xcc, 0xcc,
        0x48, 0x42, 0xcc, 0xcc,
        0x48, 0xc2, 0xcc, 0xcc,
        0x00, 0x04, 0xcc, 0xcc,
        0x00, 0x84, 0xcc, 0xcc,
        0xff, 0x7b, 0xcc, 0xcc,
        0xff, 0xfb
    };
    static const gdouble expected[] = {
        0.0,
        1.0,
        3.140625,
        -3.140625,
        6.103515625e-05,
        -6.103515625e-05,
        65504.0,
        -65504.0
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0xff, 0xfb,
        0xff, 0x7b,
        0x00, 0x84,
        0x00, 0x04,
        0x48, 0xc2,
        0x48, 0x42,
        0x00, 0x3c,
        0x00, 0x00
    };
    static const gdouble expected[] = {
        0.0,
        1.0,
        3.140625,
        -3.140625,
        6.103515625e-05,
        -6.103515625e-05,
        65504.0,
        -65504.0
    };
    /* NB: We must point to the first byte of the last item, not to the last byte of the buffer. */
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_HALF);

    check_conversion(data_end, -1, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_half_le_stride0(void)
{
    static const guint8 raw_data[] = { 0x48, 0x42 };
    static const gdouble expected[] = { 3.140625, 3.140625, 3.140625, 3.140625, 3.140625 };

    check_conversion(raw_data, 0, GWY_RAW_DATA_HALF, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

/* The Real48 representations were verified using FreePascal Real2Double(). */
void
test_raw_data_pascal_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x81,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x81,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x49, 0x0f, 0xda, 0xa2, 0x21, 0x82
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7014118346031449e+038,
        -1.7014118346031449e+038,
        2.9387358770557188e-039,
        -2.9387358770557188e-039,
        3.1415926535883045e+000
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x81, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x49, 0x0f, 0xda, 0xa2, 0x21, 0x82, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7014118346031449e+038,
        -1.7014118346031449e+038,
        2.9387358770557188e-039,
        -2.9387358770557188e-039,
        3.1415926535883045e+000
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x49, 0x0f, 0xda, 0xa2, 0x21, 0x82,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x80, 0x00, 0x00, 0x00, 0x00, 0x81,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x81,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7014118346031449e+038,
        -1.7014118346031449e+038,
        2.9387358770557188e-039,
        -2.9387358770557188e-039,
        3.1415926535883045
    };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_REAL);

    check_conversion(data_end, -1, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x49, 0x0f, 0xda, 0xa2, 0x21, 0x82 };
    static const gdouble expected[] = {
        3.1415926535883045, 3.1415926535883045, 3.1415926535883045, 3.1415926535883045, 3.1415926535883045,
    };

    check_conversion(raw_data, 0, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x81, 0x00, 0x00, 0x00, 0x00, 0x80,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
        0x82, 0x21, 0xa2, 0xda, 0x0f, 0x49
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7014118346031449e+038,
        -1.7014118346031449e+038,
        2.9387358770557188e-039,
        -2.9387358770557188e-039,
        3.1415926535883045e+000
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x81, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x82, 0x21, 0xa2, 0xda, 0x0f, 0x49, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7014118346031449e+038,
        -1.7014118346031449e+038,
        2.9387358770557188e-039,
        -2.9387358770557188e-039,
        3.1415926535883045e+000
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x82, 0x21, 0xa2, 0xda, 0x0f, 0x49,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
        0x81, 0x00, 0x00, 0x00, 0x00, 0x80,
        0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7014118346031449e+038,
        -1.7014118346031449e+038,
        2.9387358770557188e-039,
        -2.9387358770557188e-039,
        3.1415926535883045
    };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_REAL);

    check_conversion(data_end, -1, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_pascal_le_stride0(void)
{
    static const guint8 raw_data[] = { 0x82, 0x21, 0xa2, 0xda, 0x0f, 0x49 };
    static const gdouble expected[] = {
        3.1415926535883045, 3.1415926535883045, 3.1415926535883045, 3.1415926535883045, 3.1415926535883045,
    };

    check_conversion(raw_data, 0, GWY_RAW_DATA_REAL, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

/* The Float80 representations were verified using 32bit 387 coprocessor code.
 * FIXME: The range and precision is larger than for doubles. What are our exact assertions about the
 * non-representable numbers? */
void
test_raw_data_extended_be_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x3f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xbf, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        /* Putting any more 1 bits after the f8 will (correctly) result in infinities. */
        0x43, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
        0xc3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
        0x3c, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xbc, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x35
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7976931348623157e308,
        -1.7976931348623157e308,
        2.22507385850720141e-308,
        -2.22507385850720141e-308,
        3.14159265358979323851
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_extended_be_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x3f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xbf, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x43, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xc3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x3c, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xbc, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x35
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7976931348623157e308,
        -1.7976931348623157e308,
        2.22507385850720141e-308,
        -2.22507385850720141e-308,
        3.14159265358979323851
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_extended_be_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x35,
        0xbc, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x3c, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xc3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
        0x43, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
        0xbf, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x3f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7976931348623157e308,
        -1.7976931348623157e308,
        2.22507385850720141e-308,
        -2.22507385850720141e-308,
        3.14159265358979323851
    };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_EXTENDED);

    check_conversion(data_end, -1, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_extended_be_stride0(void)
{
    static const guint8 raw_data[] = { 0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x35 };
    static const gdouble expected[] = {
        3.14159265358979323851, 3.14159265358979323851, 3.14159265358979323851, 3.14159265358979323851,
        3.14159265358979323851,
    };

    check_conversion(raw_data, 0, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_BIG_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_extended_le_stride1(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x3f,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xbf,
        /* Putting any more 1 bits after the f8 will (correctly) result in infinities. */
        0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x43,
        0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xc3,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x3c,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xbc,
        0x35, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0x00, 0x40
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7976931348623157e308,
        -1.7976931348623157e308,
        2.22507385850720141e-308,
        -2.22507385850720141e-308,
        3.14159265358979323851
    };

    check_conversion(raw_data, 1, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_extended_le_stride2(void)
{
    static const guint8 raw_data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x3f,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xbf,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x43,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xc3,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x3c,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xbc,
        0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
        0x35, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0x00, 0x40
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7976931348623157e308,
        -1.7976931348623157e308,
        2.22507385850720141e-308,
        -2.22507385850720141e-308,
        3.14159265358979323851
    };

    check_conversion(raw_data, 2, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

void
test_raw_data_extended_le_stridem1(void)
{
    static const guint8 raw_data[] = {
        0x35, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0x00, 0x40,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xbc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x3c,
        0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xc3,
        0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x43,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xbf,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x3f,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    static const gdouble expected[] = {
        0.0,
        0.0,
        1.0,
        -1.0,
        1.7976931348623157e308,
        -1.7976931348623157e308,
        2.22507385850720141e-308,
        -2.22507385850720141e-308,
        3.14159265358979323851
    };
    const guint8 *data_end = raw_data + G_N_ELEMENTS(raw_data) - gwy_raw_data_size(GWY_RAW_DATA_EXTENDED);

    check_conversion(data_end, -1, GWY_RAW_DATA_EXTENDED, GWY_BYTE_ORDER_LITTLE_ENDIAN, 1, 0,
                     expected, G_N_ELEMENTS(expected), 0.0);
}

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
