/*
 * Copyright © 2005 Eric Anholt
 * Copyright © 2009 Chris Wilson
 * Copyright © 2010 Soeren Sandmann
 * Copyright © 2010 Red Hat, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Eric Anholt not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Eric Anholt makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
#include <stdio.h>
#include <stdlib.h> /* abort() */
#include <math.h>
#include <time.h>
#include "utils.h"

typedef struct format_t format_t;
typedef struct image_t image_t;
typedef struct operator_t operator_t;

struct format_t
{
    pixman_format_code_t format;
    const char *name;
};

static const color_t colors[] =
{
    { 1.0, 1.0, 1.0, 1.0 },
    { 1.0, 1.0, 1.0, 0.0 },
    { 0.0, 0.0, 0.0, 1.0 },
    { 0.0, 0.0, 0.0, 0.0 },
    { 1.0, 0.0, 0.0, 1.0 },
    { 0.0, 1.0, 0.0, 1.0 },
    { 0.0, 0.0, 1.0, 1.0 },
    { 0.5, 0.0, 0.0, 0.5 },
};

static uint16_t
_color_double_to_short (double d)
{
    uint32_t i;

    i = (uint32_t) (d * 65536);
    i -= (i >> 16);

    return i;
}

static void
compute_pixman_color (const color_t *color,
		      pixman_color_t *out)
{
    out->red   = _color_double_to_short (color->r);
    out->green = _color_double_to_short (color->g);
    out->blue  = _color_double_to_short (color->b);
    out->alpha = _color_double_to_short (color->a);
}

#define REPEAT 0x01000000
#define FLAGS  0xff000000

static const int sizes[] =
{
    0,
    1,
    1 | REPEAT,
    10
};

static const format_t formats[] =
{
#define P(x) { PIXMAN_##x, #x }

    /* 32 bpp formats */
    P(a8r8g8b8),
    P(x8r8g8b8),
    P(a8b8g8r8),
    P(x8b8g8r8),
    P(b8g8r8a8),
    P(b8g8r8x8),
    P(r8g8b8a8),
    P(r8g8b8x8),
    P(x2r10g10b10),
    P(x2b10g10r10),
    P(a2r10g10b10),
    P(a2b10g10r10),
    
    /* sRGB formats */
    P(a8r8g8b8_sRGB),

    /* 24 bpp formats */
    P(r8g8b8),
    P(b8g8r8),
    P(r5g6b5),
    P(b5g6r5),

    /* 16 bpp formats */
    P(x1r5g5b5),
    P(x1b5g5r5),
    P(a1r5g5b5),
    P(a1b5g5r5),
    P(a4b4g4r4),
    P(x4b4g4r4),
    P(a4r4g4b4),
    P(x4r4g4b4),

    /* 8 bpp formats */
    P(a8),
    P(r3g3b2),
    P(b2g3r3),
    P(a2r2g2b2),
    P(a2b2g2r2),
    P(x4a4),

    /* 4 bpp formats */
    P(a4),
    P(r1g2b1),
    P(b1g2r1),
    P(a1r1g1b1),
    P(a1b1g1r1),

    /* 1 bpp formats */
    P(a1)
#undef P
};

struct image_t
{
    pixman_image_t *image;
    const format_t *format;
    const color_t *color;
    pixman_repeat_t repeat;
    int size;
};

struct operator_t
{
    pixman_op_t op;
    const char *name;
};

static const operator_t operators[] =
{
#define P(x) { PIXMAN_OP_##x, #x }
    P(CLEAR),
    P(SRC),
    P(DST),
    P(OVER),
    P(OVER_REVERSE),
    P(IN),
    P(IN_REVERSE),
    P(OUT),
    P(OUT_REVERSE),
    P(ATOP),
    P(ATOP_REVERSE),
    P(XOR),
    P(ADD),
    P(SATURATE),

    P(DISJOINT_CLEAR),
    P(DISJOINT_SRC),
    P(DISJOINT_DST),
    P(DISJOINT_OVER),
    P(DISJOINT_OVER_REVERSE),
    P(DISJOINT_IN),
    P(DISJOINT_IN_REVERSE),
    P(DISJOINT_OUT),
    P(DISJOINT_OUT_REVERSE),
    P(DISJOINT_ATOP),
    P(DISJOINT_ATOP_REVERSE),
    P(DISJOINT_XOR),

    P(CONJOINT_CLEAR),
    P(CONJOINT_SRC),
    P(CONJOINT_DST),
    P(CONJOINT_OVER),
    P(CONJOINT_OVER_REVERSE),
    P(CONJOINT_IN),
    P(CONJOINT_IN_REVERSE),
    P(CONJOINT_OUT),
    P(CONJOINT_OUT_REVERSE),
    P(CONJOINT_ATOP),
    P(CONJOINT_ATOP_REVERSE),
    P(CONJOINT_XOR),
#undef P
};

static double
calc_op (pixman_op_t op, double src, double dst, double srca, double dsta)
{
#define mult_chan(src, dst, Fa, Fb) MIN ((src) * (Fa) + (dst) * (Fb), 1.0)

    double Fa, Fb;

    switch (op)
    {
    case PIXMAN_OP_CLEAR:
    case PIXMAN_OP_DISJOINT_CLEAR:
    case PIXMAN_OP_CONJOINT_CLEAR:
	return mult_chan (src, dst, 0.0, 0.0);

    case PIXMAN_OP_SRC:
    case PIXMAN_OP_DISJOINT_SRC:
    case PIXMAN_OP_CONJOINT_SRC:
	return mult_chan (src, dst, 1.0, 0.0);

    case PIXMAN_OP_DST:
    case PIXMAN_OP_DISJOINT_DST:
    case PIXMAN_OP_CONJOINT_DST:
	return mult_chan (src, dst, 0.0, 1.0);

    case PIXMAN_OP_OVER:
	return mult_chan (src, dst, 1.0, 1.0 - srca);

    case PIXMAN_OP_OVER_REVERSE:
	return mult_chan (src, dst, 1.0 - dsta, 1.0);

    case PIXMAN_OP_IN:
	return mult_chan (src, dst, dsta, 0.0);

    case PIXMAN_OP_IN_REVERSE:
	return mult_chan (src, dst, 0.0, srca);

    case PIXMAN_OP_OUT:
	return mult_chan (src, dst, 1.0 - dsta, 0.0);

    case PIXMAN_OP_OUT_REVERSE:
	return mult_chan (src, dst, 0.0, 1.0 - srca);

    case PIXMAN_OP_ATOP:
	return mult_chan (src, dst, dsta, 1.0 - srca);

    case PIXMAN_OP_ATOP_REVERSE:
	return mult_chan (src, dst, 1.0 - dsta,  srca);

    case PIXMAN_OP_XOR:
	return mult_chan (src, dst, 1.0 - dsta, 1.0 - srca);

    case PIXMAN_OP_ADD:
	return mult_chan (src, dst, 1.0, 1.0);

    case PIXMAN_OP_SATURATE:
    case PIXMAN_OP_DISJOINT_OVER_REVERSE:
	if (srca == 0.0)
	    Fa = 1.0;
	else
	    Fa = MIN (1.0, (1.0 - dsta) / srca);
	return mult_chan (src, dst, Fa, 1.0);

    case PIXMAN_OP_DISJOINT_OVER:
	if (dsta == 0.0)
	    Fb = 1.0;
	else
	    Fb = MIN (1.0, (1.0 - srca) / dsta);
	return mult_chan (src, dst, 1.0, Fb);

    case PIXMAN_OP_DISJOINT_IN:
	if (srca == 0.0)
	    Fa = 0.0;
	else
	    Fa = MAX (0.0, 1.0 - (1.0 - dsta) / srca);
	return mult_chan (src, dst, Fa, 0.0);

    case PIXMAN_OP_DISJOINT_IN_REVERSE:
	if (dsta == 0.0)
	    Fb = 0.0;
	else
	    Fb = MAX (0.0, 1.0 - (1.0 - srca) / dsta);
	return mult_chan (src, dst, 0.0, Fb);

    case PIXMAN_OP_DISJOINT_OUT:
	if (srca == 0.0)
	    Fa = 1.0;
	else
	    Fa = MIN (1.0, (1.0 - dsta) / srca);
	return mult_chan (src, dst, Fa, 0.0);

    case PIXMAN_OP_DISJOINT_OUT_REVERSE:
	if (dsta == 0.0)
	    Fb = 1.0;
	else
	    Fb = MIN (1.0, (1.0 - srca) / dsta);
	return mult_chan (src, dst, 0.0, Fb);

    case PIXMAN_OP_DISJOINT_ATOP:
	if (srca == 0.0)
	    Fa = 0.0;
	else
	    Fa = MAX (0.0, 1.0 - (1.0 - dsta) / srca);
	if (dsta == 0.0)
	    Fb = 1.0;
	else
	    Fb = MIN (1.0, (1.0 - srca) / dsta);
	return mult_chan (src, dst, Fa, Fb);

    case PIXMAN_OP_DISJOINT_ATOP_REVERSE:
	if (srca == 0.0)
	    Fa = 1.0;
	else
	    Fa = MIN (1.0, (1.0 - dsta) / srca);
	if (dsta == 0.0)
	    Fb = 0.0;
	else
	    Fb = MAX (0.0, 1.0 - (1.0 - srca) / dsta);
	return mult_chan (src, dst, Fa, Fb);

    case PIXMAN_OP_DISJOINT_XOR:
	if (srca == 0.0)
	    Fa = 1.0;
	else
	    Fa = MIN (1.0, (1.0 - dsta) / srca);
	if (dsta == 0.0)
	    Fb = 1.0;
	else
	    Fb = MIN (1.0, (1.0 - srca) / dsta);
	return mult_chan (src, dst, Fa, Fb);

    case PIXMAN_OP_CONJOINT_OVER:
	if (dsta == 0.0)
	    Fb = 0.0;
	else
	    Fb = MAX (0.0, 1.0 - srca / dsta);
	return mult_chan (src, dst, 1.0, Fb);

    case PIXMAN_OP_CONJOINT_OVER_REVERSE:
	if (srca == 0.0)
	    Fa = 0.0;
	else
	    Fa = MAX (0.0, 1.0 - dsta / srca);
	return mult_chan (src, dst, Fa, 1.0);

    case PIXMAN_OP_CONJOINT_IN:
	if (srca == 0.0)
	    Fa = 1.0;
	else
	    Fa = MIN (1.0, dsta / srca);
	return mult_chan (src, dst, Fa, 0.0);

    case PIXMAN_OP_CONJOINT_IN_REVERSE:
	if (dsta == 0.0)
	    Fb = 1.0;
	else
	    Fb = MIN (1.0, srca / dsta);
	return mult_chan (src, dst, 0.0, Fb);

    case PIXMAN_OP_CONJOINT_OUT:
	if (srca == 0.0)
	    Fa = 0.0;
	else
	    Fa = MAX (0.0, 1.0 - dsta / srca);
	return mult_chan (src, dst, Fa, 0.0);

    case PIXMAN_OP_CONJOINT_OUT_REVERSE:
	if (dsta == 0.0)
	    Fb = 0.0;
	else
	    Fb = MAX (0.0, 1.0 - srca / dsta);
	return mult_chan (src, dst, 0.0, Fb);

    case PIXMAN_OP_CONJOINT_ATOP:
	if (srca == 0.0)
	    Fa = 1.0;
	else
	    Fa = MIN (1.0, dsta / srca);
	if (dsta == 0.0)
	    Fb = 0.0;
	else
	    Fb = MAX (0.0, 1.0 - srca / dsta);
	return mult_chan (src, dst, Fa, Fb);

    case PIXMAN_OP_CONJOINT_ATOP_REVERSE:
	if (srca == 0.0)
	    Fa = 0.0;
	else
	    Fa = MAX (0.0, 1.0 - dsta / srca);
	if (dsta == 0.0)
	    Fb = 1.0;
	else
	    Fb = MIN (1.0, srca / dsta);
	return mult_chan (src, dst, Fa, Fb);

    case PIXMAN_OP_CONJOINT_XOR:
	if (srca == 0.0)
	    Fa = 0.0;
	else
	    Fa = MAX (0.0, 1.0 - dsta / srca);
	if (dsta == 0.0)
	    Fb = 0.0;
	else
	    Fb = MAX (0.0, 1.0 - srca / dsta);
	return mult_chan (src, dst, Fa, Fb);

    case PIXMAN_OP_MULTIPLY:
    case PIXMAN_OP_SCREEN:
    case PIXMAN_OP_OVERLAY:
    case PIXMAN_OP_DARKEN:
    case PIXMAN_OP_LIGHTEN:
    case PIXMAN_OP_COLOR_DODGE:
    case PIXMAN_OP_COLOR_BURN:
    case PIXMAN_OP_HARD_LIGHT:
    case PIXMAN_OP_SOFT_LIGHT:
    case PIXMAN_OP_DIFFERENCE:
    case PIXMAN_OP_EXCLUSION:
    case PIXMAN_OP_HSL_HUE:
    case PIXMAN_OP_HSL_SATURATION:
    case PIXMAN_OP_HSL_COLOR:
    case PIXMAN_OP_HSL_LUMINOSITY:
    default:
	abort();
	return 0; /* silence MSVC */
    }
#undef mult_chan
}

static void
do_composite (pixman_op_t op,
	      const color_t *src,
	      const color_t *mask,
	      const color_t *dst,
	      color_t *result,
	      pixman_bool_t component_alpha)
{
    color_t srcval, srcalpha;

    if (mask == NULL)
    {
	srcval = *src;

	srcalpha.r = src->a;
	srcalpha.g = src->a;
	srcalpha.b = src->a;
	srcalpha.a = src->a;
    }
    else if (component_alpha)
    {
	srcval.r = src->r * mask->r;
	srcval.g = src->g * mask->g;
	srcval.b = src->b * mask->b;
	srcval.a = src->a * mask->a;

	srcalpha.r = src->a * mask->r;
	srcalpha.g = src->a * mask->g;
	srcalpha.b = src->a * mask->b;
	srcalpha.a = src->a * mask->a;
    }
    else
    {
	srcval.r = src->r * mask->a;
	srcval.g = src->g * mask->a;
	srcval.b = src->b * mask->a;
	srcval.a = src->a * mask->a;

	srcalpha.r = src->a * mask->a;
	srcalpha.g = src->a * mask->a;
	srcalpha.b = src->a * mask->a;
	srcalpha.a = src->a * mask->a;
    }

    result->r = calc_op (op, srcval.r, dst->r, srcalpha.r, dst->a);
    result->g = calc_op (op, srcval.g, dst->g, srcalpha.g, dst->a);
    result->b = calc_op (op, srcval.b, dst->b, srcalpha.b, dst->a);
    result->a = calc_op (op, srcval.a, dst->a, srcalpha.a, dst->a);
}

static uint32_t
get_value (pixman_image_t *image)
{
    uint32_t value = *(uint32_t *)pixman_image_get_data (image);

#ifdef WORDS_BIGENDIAN
    {
	pixman_format_code_t format = pixman_image_get_format (image);
	value >>= 8 * sizeof(value) - PIXMAN_FORMAT_BPP (format);
    }
#endif

    return value;
}

static char *
describe_image (image_t *info, char *buf)
{
    if (info->size)
    {
	sprintf (buf, "%s, %dx%d%s",
		 info->format->name,
		 info->size, info->size,
		 info->repeat ? " R" :"");
    }
    else
    {
	sprintf (buf, "solid");
    }

    return buf;
}

static char *
describe_color (const color_t *color, char *buf)
{
    sprintf (buf, "%.3f %.3f %.3f %.3f",
	     color->r, color->g, color->b, color->a);

    return buf;
}

static pixman_bool_t
composite_test (image_t *dst,
		const operator_t *op,
		image_t *src,
		image_t *mask,
		pixman_bool_t component_alpha,
		int testno)
{
    color_t expected, tdst, tsrc, tmsk;
    pixel_checker_t checker;

    if (mask)
    {
	pixman_image_set_component_alpha (mask->image, component_alpha);

	pixman_image_composite (op->op, src->image, mask->image, dst->image,
				0, 0, 0, 0, 0, 0, dst->size, dst->size);
    }
    else
    {
	pixman_image_composite (op->op, src->image, NULL, dst->image,
				0, 0,
				0, 0,
				0, 0,
				dst->size, dst->size);
    }

    tdst = *dst->color;
    tsrc = *src->color;

    if (mask)
    {
	tmsk = *mask->color;
    }

    /* It turns out that by construction all source, mask etc. colors are
     * linear because they are made from fills, and fills are always in linear
     * color space.  However, if they have been converted to bitmaps, we need
     * to simulate the sRGB approximation to pass the test cases.
     */
    if (src->size)
    {
	if (PIXMAN_FORMAT_TYPE (src->format->format) == PIXMAN_TYPE_ARGB_SRGB)
        {
	    tsrc.r = convert_linear_to_srgb (tsrc.r);
	    tsrc.g = convert_linear_to_srgb (tsrc.g);
	    tsrc.b = convert_linear_to_srgb (tsrc.b);
	    round_color (src->format->format, &tsrc);
	    tsrc.r = convert_srgb_to_linear (tsrc.r);
	    tsrc.g = convert_srgb_to_linear (tsrc.g);
	    tsrc.b = convert_srgb_to_linear (tsrc.b);
	}
        else
        {
	    round_color (src->format->format, &tsrc);
	}
    }

    if (mask && mask->size)
    {
	if (PIXMAN_FORMAT_TYPE (mask->format->format) == PIXMAN_TYPE_ARGB_SRGB)
	{
	    tmsk.r = convert_linear_to_srgb (tmsk.r);
	    tmsk.g = convert_linear_to_srgb (tmsk.g);
	    tmsk.b = convert_linear_to_srgb (tmsk.b);
	    round_color (mask->format->format, &tmsk);
	    tmsk.r = convert_srgb_to_linear (tmsk.r);
	    tmsk.g = convert_srgb_to_linear (tmsk.g);
	    tmsk.b = convert_srgb_to_linear (tmsk.b);
	}
	else
	{
	    round_color (mask->format->format, &tmsk);
	}
    }

    if (mask)
    {
	if (component_alpha && PIXMAN_FORMAT_R (mask->format->format) == 0)
	{
	    /* Ax component-alpha masks expand alpha into
	     * all color channels.
	     */
	    tmsk.r = tmsk.g = tmsk.b = tmsk.a;
	}
    }

    if (PIXMAN_FORMAT_TYPE (dst->format->format) == PIXMAN_TYPE_ARGB_SRGB)
    {
	tdst.r = convert_linear_to_srgb (tdst.r);
	tdst.g = convert_linear_to_srgb (tdst.g);
	tdst.b = convert_linear_to_srgb (tdst.b);
    	round_color (dst->format->format, &tdst);
	tdst.r = convert_srgb_to_linear (tdst.r);
	tdst.g = convert_srgb_to_linear (tdst.g);
	tdst.b = convert_srgb_to_linear (tdst.b);
    }
    else
    {
    	round_color (dst->format->format, &tdst);
    }

    do_composite (op->op,
		  &tsrc,
		  mask? &tmsk : NULL,
		  &tdst,
		  &expected,
		  component_alpha);

    pixel_checker_init (&checker, dst->format->format);

    if (!pixel_checker_check (&checker, get_value (dst->image), &expected))
    {
	char buf[40], buf2[40];
	int a, r, g, b;
	uint32_t pixel;

	printf ("---- Test %d failed ----\n", testno);
	printf ("Operator:      %s %s\n",
		 op->name, component_alpha ? "CA" : "");

	printf ("Source:        %s\n", describe_image (src, buf));
	if (mask != NULL)
	    printf ("Mask:          %s\n", describe_image (mask, buf));

	printf ("Destination:   %s\n\n", describe_image (dst, buf));
	printf ("               R     G     B     A         Rounded\n");
	printf ("Source color:  %s     %s\n",
		describe_color (src->color, buf),
		describe_color (&tsrc, buf2));
	if (mask)
	{
	    printf ("Mask color:    %s     %s\n",
		    describe_color (mask->color, buf),
		    describe_color (&tmsk, buf2));
	}
	printf ("Dest. color:   %s     %s\n",
		describe_color (dst->color, buf),
		describe_color (&tdst, buf2));

	pixel = get_value (dst->image);

	printf ("Expected:      %s\n", describe_color (&expected, buf));

	pixel_checker_split_pixel (&checker, pixel, &a, &r, &g, &b);

	printf ("Got:           %5d %5d %5d %5d  [pixel: 0x%08x]\n", r, g, b, a, pixel);
	pixel_checker_get_min (&checker, &expected, &a, &r, &g, &b);
	printf ("Min accepted:  %5d %5d %5d %5d\n", r, g, b, a);
	pixel_checker_get_max (&checker, &expected, &a, &r, &g, &b);
	printf ("Max accepted:  %5d %5d %5d %5d\n", r, g, b, a);

	return FALSE;
    }
    return TRUE;
}

static void
image_init (image_t *info,
	    int color,
	    int format,
	    int size)
{
    pixman_color_t fill;

    info->color = &colors[color];
    compute_pixman_color (info->color, &fill);

    info->format = &formats[format];
    info->size = sizes[size] & ~FLAGS;
    info->repeat = PIXMAN_REPEAT_NONE;

    if (info->size)
    {
	pixman_image_t *solid;

	info->image = pixman_image_create_bits (info->format->format,
						info->size, info->size,
						NULL, 0);

	solid = pixman_image_create_solid_fill (&fill);
	pixman_image_composite32 (PIXMAN_OP_SRC, solid, NULL, info->image,
				  0, 0, 0, 0, 0, 0, info->size, info->size);
	pixman_image_unref (solid);

	if (sizes[size] & REPEAT)
	{
	    pixman_image_set_repeat (info->image, PIXMAN_REPEAT_NORMAL);
	    info->repeat = PIXMAN_REPEAT_NORMAL;
	}
    }
    else
    {
	info->image = pixman_image_create_solid_fill (&fill);
    }
}

static void
image_fini (image_t *info)
{
    pixman_image_unref (info->image);
}

static int
random_size (void)
{
    return lcg_rand_n (ARRAY_LENGTH (sizes));
}

static int
random_color (void)
{
    return lcg_rand_n (ARRAY_LENGTH (colors));
}

static int
random_format (void)
{
    return lcg_rand_n (ARRAY_LENGTH (formats));
}

static pixman_bool_t
run_test (uint32_t seed)
{
    image_t src, mask, dst;
    const operator_t *op;
    int ca;
    int ok;

    lcg_srand (seed);

    image_init (&dst, random_color(), random_format(), 1);
    image_init (&src, random_color(), random_format(), random_size());
    image_init (&mask, random_color(), random_format(), random_size());

    op = &(operators [lcg_rand_n (ARRAY_LENGTH (operators))]);

    ca = lcg_rand_n (3);

    switch (ca)
    {
    case 0:
	ok = composite_test (&dst, op, &src, NULL, FALSE, seed);
	break;
    case 1:
	ok = composite_test (&dst, op, &src, &mask, FALSE, seed);
	break;
    case 2:
	ok = composite_test (&dst, op, &src, &mask,
			     mask.size? TRUE : FALSE, seed);
	break;
    default:
	ok = FALSE;
	break;
    }

    image_fini (&src);
    image_fini (&mask);
    image_fini (&dst);

    return ok;
}

int
main (int argc, char **argv)
{
#define N_TESTS (8 * 1024 * 1024)
    int result = 0;
    uint32_t seed;
    int32_t i;

    if (argc > 1)
    {
	char *end;

	i = strtol (argv[1], &end, 0);

	if (end != argv[1])
	{
	    if (!run_test (i))
		return 1;
	    else
		return 0;
	}
	else
	{
	    printf ("Usage:\n\n   %s <number>\n\n", argv[0]);
	    return -1;
	}
    }

    if (getenv ("PIXMAN_RANDOMIZE_TESTS"))
	seed = get_random_seed();
    else
	seed = 1;

#ifdef USE_OPENMP
#   pragma omp parallel for default(none) shared(result, argv, seed)
#endif
    for (i = 0; i <= N_TESTS; ++i)
    {
	if (!result && !run_test (i + seed))
	{
	    printf ("Test 0x%08X failed.\n", seed + i);

	    result = seed + i;
	}
    }

    return result;
}
