/* Copyright 2016 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <limits.h>
/*#include <stdio.h>*/

static int scl_gnuc_builtin_overflow = 0;

#if __GNUC__ >= 5
#	define SCL_GNUC_BUILTIN_OVERFLOW scl_gnuc_builtin_overflow
#endif

#include "scl/arithmetic.h"

#if (__STDC_VERSION__ >= 199901) || defined(__cplusplus)
#	define __STDC_LIMIT_MACROS
#	include <stdint.h>
#else
#	if !defined(SIZE_MAX)
#		define SIZE_MAX (sizeof(size_t) == sizeof(unsigned) ? UINT_MAX : ULONG_MAX)
#	endif
#endif

#if SCL_USE_LONG_LONG
#	define LL1 1ll
#else
#	define LL1 1l
#endif

static void check_uadd(unsigned a, unsigned b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	unsigned res = 0;

	ov = !scl_add_u(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || (res == (a + b))));

	ov = !scl_add_u(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || (res == (b + a))));
}
static void uadd(void) {
	check_uadd(UINT_MAX, 0, 0/*false*/);
	check_uadd(UINT_MAX, 1, 1/*true*/);
	check_uadd(UINT_MAX / 2 + 1, UINT_MAX / 2, 0/*false*/);
	check_uadd(UINT_MAX / 2 + 1, UINT_MAX / 2 + 1, 1/*true*/);
	check_uadd(3498, 12983, 0/*false*/);
}

static void check_usub(unsigned a, unsigned b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	unsigned res = 0;

	ov = !scl_sub_u(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || (res == (a - b))));
	if (a == b) {
		SCL_ASSERT((ov == overflow) && !overflow);
	} else {
		ov = !scl_sub_u(&res, b, a);
		SCL_ASSERT((ov == !overflow) && (ov || (res == (b - a))));
	}
}
static void usub(void) {
	check_usub(UINT_MAX, 0, 0/*false*/);
	check_usub(0, 0, 0/*false*/);
	check_usub(UINT_MAX, UINT_MAX, 0/*false*/);
	check_usub(UINT_MAX, UINT_MAX - 1, 0/*false*/);
	check_usub(UINT_MAX / 2 - 1, UINT_MAX / 2 + 1, 1/*true*/);
	check_usub(3498, 12983, 1/*true*/);
	check_usub(4328, 23376, 1/*true*/);
}

static void check_ssub(int a, int b, int/*bool*/ overflow) {
	int/*bool*/ ov1, ov2;
	int res1, res2;

	res1 = 0; res2 = 0;
	ov1 = !scl_sub(&res1, a, b);
	ov2 = !scl_sub(&res2, b, a);

	if (ov1 != ov2) {
		SCL_ASSERT(overflow);
		SCL_ASSERT(ov1 || (res1 == INT_MIN));
		SCL_ASSERT(ov2 || (res2 == INT_MIN));
	} else {
		SCL_ASSERT((ov1 == overflow) && (overflow || (res1 == (a - b))));
		SCL_ASSERT((ov2 == overflow) && (overflow || (res2 == (b - a))));
	}
}
static void ssub(void) {
	check_ssub(INT_MAX, 0, 0/*false*/);
	check_ssub(INT_MIN, 0, 1/*true*/);
	check_ssub(INT_MAX, INT_MAX, 0/*false*/);
	check_ssub(INT_MIN, INT_MIN, 0/*false*/);
	check_ssub(INT_MIN, INT_MAX, 1/*true*/);
	check_ssub(INT_MAX, INT_MAX - 1, 0/*false*/);
	check_ssub(INT_MIN, INT_MIN + 1, 0/*false*/);
	check_ssub(INT_MAX / 2 - 1, -(INT_MAX / 2 + 1), 0/*false*/);
	check_ssub(INT_MAX / 2, -(INT_MAX / 2 + 1), 0/*false*/);
	check_ssub(INT_MAX / 2 + 1, -(INT_MAX / 2 + 1), 1/*true*/);
	check_ssub(3498, 12983, 0/*false*/);
	check_ssub(4328, 23376, 0/*false*/);
}

static void check_ulsub(unsigned long a, unsigned long b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	unsigned long res = 0;

	ov = !scl_sub_ul(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || (res == (a - b))));
	if (a == b) {
		SCL_ASSERT((ov == overflow) && !overflow);
	} else {
		ov = !scl_sub_ul(&res, b, a);
		SCL_ASSERT((ov == !overflow) && (ov || (res == (b - a))));
	}
}
static void ulsub(void) {
	check_ulsub(ULONG_MAX, 0, 0/*false*/);
	check_ulsub(0, 0, 0/*false*/);
	check_ulsub(ULONG_MAX, ULONG_MAX, 0/*false*/);
	check_ulsub(ULONG_MAX, ULONG_MAX - 1, 0/*false*/);
	check_ulsub(ULONG_MAX / 2 - 1, ULONG_MAX / 2 + 1, 1/*true*/);
	check_ulsub(3498, 12983, 1/*true*/);
	check_ulsub(4328, 23376, 1/*true*/);
}

static void check_slsub(long a, long b, int/*bool*/ overflow) {
	int/*bool*/ ov1, ov2;
	long res1, res2;

	res1 = 0; res2 = 0;
	ov1 = !scl_sub_l(&res1, a, b);
	ov2 = !scl_sub_l(&res2, b, a);

	if (ov1 != ov2) {
		SCL_ASSERT(overflow);
		SCL_ASSERT(ov1 || (res1 == LONG_MIN));
		SCL_ASSERT(ov2 || (res2 == LONG_MIN));
	} else {
		SCL_ASSERT((ov1 == overflow) && (overflow || (res1 == (a - b))));
		SCL_ASSERT((ov2 == overflow) && (overflow || (res2 == (b - a))));
	}
}
static void slsub(void) {
	check_slsub(LONG_MAX, 0, 0/*false*/);
	check_slsub(LONG_MIN, 0, 1/*true*/);
	check_slsub(LONG_MAX, LONG_MAX, 0/*false*/);
	check_slsub(LONG_MIN, LONG_MIN, 0/*false*/);
	check_slsub(LONG_MIN, LONG_MAX, 1/*true*/);
	check_slsub(LONG_MAX, LONG_MAX - 1, 0/*false*/);
	check_slsub(LONG_MIN, LONG_MIN + 1, 0/*false*/);
	check_slsub(LONG_MAX / 2 - 1, -(LONG_MAX / 2 + 1), 0/*false*/);
	check_slsub(LONG_MAX / 2, -(LONG_MAX / 2 + 1), 0/*false*/);
	check_slsub(LONG_MAX / 2 + 1, -(LONG_MAX / 2 + 1), 1/*true*/);
	check_slsub(33498, 712983, 0/*false*/);
	check_slsub(84328, 523376, 0/*false*/);
}

static void check_size_sub(size_t a, size_t b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	size_t res = 0;

	ov = !scl_sub_sz(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || (res == (a - b))));
	if (a == b) {
		SCL_ASSERT((ov == overflow) && !overflow);
	} else {
		ov = !scl_sub_sz(&res, b, a);
		SCL_ASSERT((ov == !overflow) && (ov || (res == (b - a))));
	}
}
static void size_sub(void) {
	check_size_sub(SIZE_MAX, 0, 0/*false*/);
	check_size_sub(0, 0, 0/*false*/);
	check_size_sub(SIZE_MAX, SIZE_MAX, 0/*false*/);
	check_size_sub(SIZE_MAX, UINT_MAX - 1, 0/*false*/);
	check_size_sub(SIZE_MAX / 2 - 1, SIZE_MAX / 2 + 1, 1/*true*/);
	check_size_sub(3498, 12983, 1/*true*/);
	check_size_sub(4328, 23376, 1/*true*/);
}

static void check_uladd(unsigned long a, unsigned long b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	unsigned long res = 0;

	ov = !scl_add_ul(&res, a, b);
	SCL_ASSERT((ov == overflow) && (overflow || (res == (a + b))));

	ov = !scl_add_ul(&res, b, a);
	SCL_ASSERT((ov == overflow) && (overflow || (res == (b + a))));
}
static void uladd(void) {
	check_uladd(ULONG_MAX, 0, 0/*false*/);
	check_uladd(ULONG_MAX, 1, 1/*true*/);
	check_uladd(ULONG_MAX / 2 + 1, ULONG_MAX / 2, 0/*false*/);
	check_uladd(ULONG_MAX / 2 + 1, ULONG_MAX / 2 + 1, 1/*true*/);
	check_uladd(34986ul, 129834ul, 0/*false*/);
}


static void check_sadd(int a, int b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	int res;
	/*printf("%d\n", overflow);*/
	res = 0;
	ov = !scl_add(&res, a, b);
	/*printf("ov = %d overflow = %d res = %d a+b = %d\n",
			ov, overflow, res, (int)(a + b));*/
	SCL_ASSERT((ov == overflow) && (ov || ((scl_imax_t)res == (a + b))));

	ov = !scl_add(&res, b, a);
	/*printf("ov = %d overflow = %d res = %d b+a = %d\n",
			ov, overflow, res, (int)(b + a));*/
	SCL_ASSERT((ov == overflow) && (ov || ((scl_imax_t)res == (b + a))));
}
static void sadd(void) {
	/*printf("SCL_IMAX_MIN = %lld SCL_IMAX_MAX = %lld, INT_MIN = %d INT_MAX = %d\n",
			(long long)SCL_IMAX_MIN, (long long)SCL_IMAX_MAX, INT_MIN, INT_MAX);*/
	check_sadd(INT_MIN, 0, 0/*false*/);
	check_sadd(INT_MIN + 876, -876, 0/*false*/);
	check_sadd(INT_MIN + 9234, -876, 0/*false*/);
	check_sadd(INT_MIN + 876, -877, 1/*true*/);
	check_sadd(INT_MIN / 2, INT_MIN / 2 - 1, 1/*true*/);

	check_sadd(INT_MAX, 0, 0/*false*/);
	check_sadd(INT_MAX - 1876, 1876, 0/*false*/);
	check_sadd(INT_MAX - 19234, 1876, 0/*false*/);
	check_sadd(INT_MAX - 2876, 2877, 1/*true*/);
	check_sadd(INT_MAX / 2, INT_MAX / 2 + 2, 1/*true*/);
	check_sadd(INT_MAX, INT_MIN, 0/*false*/);

	/*check_sadd(SCL_IMAX_MAX, (SCL_IMAX_MIN + 1) + INT_MAX, 0);
	check_sadd(SCL_IMAX_MAX, (SCL_IMAX_MIN + 2) + INT_MAX, 1);*/
	check_sadd(INT_MAX, INT_MIN, 0);
	check_sadd(INT_MAX, INT_MAX, 1);
	check_sadd(INT_MAX, 1, 1);
	check_sadd(INT_MAX, -1, 0);
	/*check_sadd(SCL_IMAX_MIN + 1, SCL_IMAX_MAX + INT_MIN, 0);
	check_sadd(SCL_IMAX_MIN, SCL_IMAX_MAX + INT_MIN, 1);*/
	check_sadd(INT_MIN, INT_MIN, 1);
	check_sadd(INT_MIN, -1, 1);
	check_sadd(INT_MIN, 1, 0);
}


static void check_sladd(long a, long b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	long res = 0;

	ov = !scl_add_l(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || (res == (a + b))));

	ov = !scl_add_l(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || (res == (b + a))));
}
static void sladd(void) {
	check_sladd(LONG_MIN, 0, 0/*false*/);
	check_sladd(LONG_MIN + 876, -876, 0/*false*/);
	check_sladd(LONG_MIN + 9234, -876, 0/*false*/);
	check_sladd(LONG_MIN + 876, -877, 1/*true*/);
	check_sladd(LONG_MIN / 2, LONG_MIN / 2 - 1, 1/*true*/);

	check_sladd(LONG_MAX, 0, 0/*false*/);
	check_sladd(LONG_MAX - 1876, 1876, 0/*false*/);
	check_sladd(LONG_MAX - 19234, 1876, 0/*false*/);
	check_sladd(LONG_MAX - 2876, 2877, 1/*true*/);
	check_sladd(LONG_MAX / 2, LONG_MAX / 2 + 2, 1/*true*/);
}

#define CHECK_MULT(a, b, res, overflow) do {\
	int/*bool*/ cm_ov; \
	*(res) = 0; \
	cm_ov = !SCL_MULT(res, a, b); \
	/*printf("cm_ov = %d res = %lld\n", cm_ov, (long long)*(res));*/ \
	SCL_ASSERT((cm_ov == (overflow)) && (cm_ov || ((scl_umax_t)((a) * (b)) == (scl_umax_t)*(res)))); \
	\
	cm_ov = !SCL_MULT(res, b, a); \
	SCL_ASSERT((cm_ov == (overflow)) && (cm_ov || ((scl_umax_t)((b) * (a)) == (scl_umax_t)*(res)))); \
} while (0)

static void check_umult(unsigned a, unsigned b, int/*bool*/ overflow) {
	unsigned res = 0;
	int/*bool*/ ov;

	ov = !scl_mult_u(&res, a, b);
	/*printf("ov = %u, res = %u\n", ov, res);*/
	SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

	ov = !scl_mult_u(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));

	CHECK_MULT(a, b, &res, overflow);
}
static void umult(void) {
	unsigned u;

	check_umult(UINT_MAX, 1, 0/*false*/);
	check_umult(UINT_MAX / 5, 5, 0/*false*/);
	check_umult(UINT_MAX, 0, 0/*false*/);
	check_umult(UINT_MAX / 2, 0, 0/*false*/);
	check_umult(327, 78, 0/*false*/);

	check_umult(UINT_MAX / 2, UINT_MAX / 3, 1/*true*/);
	check_umult(1u << ((int)sizeof(unsigned) * 4), 1u << ((int)sizeof(unsigned) * 4), 1/*true*/);
	check_umult((1u << ((int)sizeof(unsigned) * 4)) - 1, 1u << ((int)sizeof(unsigned) * 4), 0/*false*/);

	CHECK_MULT(SCL_IMAX_MAX, 0, &u, 0);
	CHECK_MULT(SCL_UMAX_MAX, 0, &u, 0);
	CHECK_MULT(-1, 1, &u, 1);
	CHECK_MULT(-1, -1, &u, 0);
	CHECK_MULT(SCL_IMAX_MAX, 1, &u, (scl_imax_t)INT_MAX < SCL_IMAX_MAX);
	CHECK_MULT(SCL_IMAX_MIN, 1, &u, 1);

	CHECK_MULT(SCL_IMAX_MAX, (scl_umax_t)1, &u, SCL_IMAX_MAX > UINT_MAX);
}

static void check_size_mult(size_t a, size_t b, int/*bool*/ overflow) {
	size_t res = 0;
	int/*bool*/ ov;

	ov = !scl_mult_sz(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

	ov = !scl_mult_sz(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));
}
static void size_mult(void) {
	check_size_mult(SIZE_MAX, 1, 0/*false*/);
	check_size_mult(SIZE_MAX / 5, 5, 0/*false*/);
	check_size_mult(SIZE_MAX, 0, 0/*false*/);
	check_size_mult(SIZE_MAX / 2, 0, 0/*false*/);
	check_size_mult(324327, 728, 0/*false*/);

	check_size_mult(SIZE_MAX / 2, SIZE_MAX / 3, 1/*true*/);
	check_size_mult(1ul << ((int)sizeof(size_t) * 4), 1ul << ((int)sizeof(size_t) * 4), 1/*true*/);
	check_size_mult((1ul << ((int)sizeof(size_t) * 4)) - 1, 1ul << ((int)sizeof(size_t) * 4), 0/*false*/);
}

static void check_ulmult(unsigned long a, unsigned long b, int/*bool*/ overflow) {
	unsigned long res = 0;
	int/*bool*/ ov;

	ov = !scl_mult_ul(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

	ov = !scl_mult_ul(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));

	CHECK_MULT(a, b, &res, overflow);
}
static void ulmult(void) {
	unsigned long ul;

	check_ulmult(ULONG_MAX, 1, 0/*false*/);
	check_ulmult(ULONG_MAX / 5, 5, 0/*false*/);
	check_ulmult(ULONG_MAX, 0, 0/*false*/);
	check_ulmult(ULONG_MAX / 2, 0, 0/*false*/);
	check_ulmult(327, 78, 0/*false*/);

	check_ulmult(ULONG_MAX / 2, ULONG_MAX / 3, 1/*true*/);
	check_ulmult(1ul << ((int)sizeof(unsigned long) * 4), 1ul << ((int)sizeof(unsigned long) * 4), 1/*true*/);
	check_ulmult((1ul << ((int)sizeof(unsigned long) * 4)) - 1, 1ul << ((int)sizeof(unsigned long) * 4), 0/*false*/);

	CHECK_MULT(SCL_IMAX_MAX, 0, &ul, 0);
	CHECK_MULT(SCL_UMAX_MAX, 0, &ul, 0);
	CHECK_MULT(-1, 1, &ul, 1);
	CHECK_MULT(-1, -1, &ul, 0);
	CHECK_MULT(SCL_IMAX_MAX, 1, &ul, LONG_MAX < SCL_IMAX_MAX);
	CHECK_MULT(SCL_IMAX_MIN, 1, &ul, 1);
}

static void check_smult(int a, int b, int/*bool*/ overflow) {
	int res;
	int/*bool*/ ov;
	res = 0;
	ov = !scl_mult(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

	ov = !scl_mult(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));

	CHECK_MULT(a, b, &res, overflow);
}
static void smult(void) {
	int i;

	check_smult(INT_MAX, 1, 0/*false*/);
	check_smult(INT_MIN + 1, -1, 0/*false*/);
	check_smult(INT_MAX, -1, 0/*false*/);
	check_smult(INT_MAX / 5, 5, 0/*false*/);
	check_smult(INT_MAX / 5, -5, 0/*false*/);
	check_smult(INT_MIN / 5, -5, 0/*false*/);
	check_smult(INT_MIN / 5, 5, 0/*false*/);
	check_smult(INT_MAX, 0, 0/*false*/);
	check_smult(INT_MIN, 0, 0/*false*/);
	check_smult(327, 78, 0/*false*/);
	check_smult(401, -64, 0/*false*/);
	check_smult(-592, -58, 0/*false*/);
	check_smult(-267, 89, 0/*false*/);

	check_smult(INT_MAX / 2, INT_MAX / 3, 1/*true*/);
	check_smult(INT_MIN / 2, INT_MIN / 3, 1/*true*/);
	check_smult(INT_MAX / 3, INT_MIN / 3, 1/*true*/);
	check_smult(INT_MIN, -1, 1/*true*/);
	/* TODO CERT */
	check_smult(1u << ((int)sizeof(int) * 4 - 1), 1u << ((int)sizeof(int) * 4), 1/*true*/);
	check_smult(-(int)(1u << ((int)sizeof(int) * 4 - 1)), 1u << ((int)sizeof(int) * 4), 0/*false*/);
	check_smult(-(int)(1u << ((int)sizeof(int) * 4 - 1)), -(int)(1u << ((int)sizeof(int) * 4)), 1/*true*/);
	check_smult((1u << ((int)sizeof(int) * 4 - 1)) - 1, 1u << ((int)sizeof(int) * 4), 0/*false*/);
	check_smult(-(int)((1u << ((int)sizeof(int) * 4 - 1)) - 1), 1u << ((int)sizeof(int) * 4), 0/*false*/);
	check_smult((1u << ((int)sizeof(int) * 4 - 1)) - 1, -(int)(1u << ((int)sizeof(int) * 4)), 0/*false*/);
	check_smult(-(int)((1u << ((int)sizeof(int) * 4 - 1)) - 1), -(int)(1u << ((int)sizeof(int) * 4)), 0/*false*/);

	CHECK_MULT(SCL_IMAX_MAX, 0, &i, 0);
	CHECK_MULT(SCL_IMAX_MIN, 0, &i, 0);
	CHECK_MULT(INT_MAX, -1, &i, 0);
	CHECK_MULT(SCL_IMAX_MAX, 1, &i, INT_MAX < SCL_IMAX_MAX);
	CHECK_MULT(SCL_IMAX_MIN, 1, &i, INT_MAX < SCL_IMAX_MAX);

	CHECK_MULT(SCL_IMAX_MAX, (scl_umax_t)1, &i, SCL_IMAX_MAX > INT_MAX);

}

static void check_slmult(long a, long b, int/*bool*/ overflow) {
	long res;
	int/*bool*/ ov;

	res = 0;
	ov = !scl_mult_l(&res, a, b);
	SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

	ov = !scl_mult_l(&res, b, a);
	SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));

	CHECK_MULT(a, b, &res, overflow);
}
static void slmult(void) {
	long l;

	check_slmult(LONG_MAX, 1, 0/*false*/);
	check_slmult(LONG_MIN + 1, -1, 0/*false*/);
	check_slmult(LONG_MAX, -1, 0/*false*/);
	check_slmult(LONG_MAX / 5, 5, 0/*false*/);
	check_slmult(LONG_MAX / 5, -5, 0/*false*/);
	check_slmult(LONG_MIN / 5, -5, 0/*false*/);
	check_slmult(LONG_MIN / 5, 5, 0/*false*/);
	check_slmult(LONG_MAX, 0, 0/*false*/);
	check_slmult(LONG_MIN, 0, 0/*false*/);
	check_slmult(3427, 738, 0/*false*/);
	check_slmult(3401, -6455, 0/*false*/);
	check_slmult(-5492, -583, 0/*false*/);
	check_slmult(-21267, 819, 0/*false*/);

	check_slmult(LONG_MAX / 2, LONG_MAX / 3, 1/*true*/);
	check_slmult(LONG_MIN / 2, LONG_MIN / 3, 1/*true*/);
	check_slmult(LONG_MAX / 3, LONG_MIN / 3, 1/*true*/);
	check_slmult(LONG_MIN, -1, 1/*true*/);
	/* TODO CERT */
	check_slmult(1ul << ((int)sizeof(long) * 4 - 1), 1ul << ((int)sizeof(long) * 4), 1/*true*/);
	check_slmult(-(long)(1ul << ((int)sizeof(long) * 4 - 1)), 1ul << ((int)sizeof(long) * 4), 0/*false*/);
	check_slmult(-(long)(1ul << ((int)sizeof(long) * 4 - 1)), -(long)(1ul << ((int)sizeof(long) * 4)), 1/*true*/);
	check_slmult((1ul << ((int)sizeof(long) * 4 - 1)) - 1, 1ul << ((int)sizeof(long) * 4), 0/*false*/);
	check_slmult(-(long)((1ul << ((int)sizeof(long) * 4 - 1)) - 1), 1ul << ((int)sizeof(long) * 4), 0/*false*/);
	check_slmult((1ul << ((int)sizeof(long) * 4 - 1)) - 1, -(long)(1ul << ((int)sizeof(long) * 4)), 0/*false*/);
	check_slmult(-(long)((1ul << ((int)sizeof(long) * 4 - 1)) - 1), -(long)(1ul << ((int)sizeof(long) * 4)), 0/*false*/);

	CHECK_MULT(SCL_IMAX_MAX, 0, &l, 0);
	CHECK_MULT(SCL_IMAX_MIN, 0, &l, 0);
	CHECK_MULT(LONG_MAX, -1, &l, 0);
	CHECK_MULT(SCL_IMAX_MAX, 1, &l, LONG_MAX < SCL_IMAX_MAX);
	CHECK_MULT(SCL_IMAX_MIN, 1, &l, LONG_MAX < SCL_IMAX_MAX);
}

#if SCL_USE_LONG_LONG

	static void check_ullsub(unsigned long long a, unsigned long long b,
		int/*bool*/ overflow)
	{
		int/*bool*/ ov;
		unsigned long long res = 0;

		ov = !scl_sub_ull(&res, a, b);
		SCL_ASSERT((ov == overflow) && (ov || (res == (a - b))));
		if (a == b) {
			SCL_ASSERT((ov == overflow) && !overflow);
		} else {
			ov = !scl_sub_ull(&res, b, a);
			SCL_ASSERT((ov == !overflow) && (ov || (res == (b - a))));
		}
	}
	static void ullsub(void) {
		check_ullsub(ULLONG_MAX, 0, 0/*false*/);
		check_ullsub(0, 0, 0/*false*/);
		check_ullsub(ULLONG_MAX, ULLONG_MAX, 0/*false*/);
		check_ullsub(ULLONG_MAX, ULLONG_MAX - 1, 0/*false*/);
		check_ullsub(ULLONG_MAX / 2 - 1, ULLONG_MAX / 2 + 1, 1/*true*/);
		check_ullsub(3498, 12983, 1/*true*/);
		check_ullsub(4328, 23376, 1/*true*/);
	}

	static void check_sllsub(long long a, long long b, int/*bool*/ overflow) {
		int/*bool*/ ov1, ov2;
		long long res1, res2;

		res1 = 0; res2 = 0;
		ov1 = !scl_sub_ll(&res1, a, b);
		ov2 = !scl_sub_ll(&res2, b, a);

		if (ov1 != ov2) {
			SCL_ASSERT(overflow);
			SCL_ASSERT(ov1 || (res1 == LLONG_MIN));
			SCL_ASSERT(ov2 || (res2 == LLONG_MIN));
		} else {
			SCL_ASSERT((ov1 == overflow) && (overflow || (res1 == (a - b))));
			SCL_ASSERT((ov2 == overflow) && (overflow || (res2 == (b - a))));
		}
	}
	static void sllsub(void) {
		check_sllsub(LLONG_MAX, 0, 0/*false*/);
		check_sllsub(LLONG_MIN, 0, 1/*true*/);
		check_sllsub(LLONG_MAX, LLONG_MAX, 0/*false*/);
		check_sllsub(LLONG_MIN, LLONG_MIN, 0/*false*/);
		check_sllsub(LLONG_MIN, LLONG_MAX, 1/*true*/);
		check_sllsub(LLONG_MAX, LLONG_MAX - 1, 0/*false*/);
		check_sllsub(LLONG_MIN, LLONG_MIN + 1, 0/*false*/);
		check_sllsub(LLONG_MAX / 2 - 1, -(LLONG_MAX / 2 + 1), 0/*false*/);
		check_sllsub(LLONG_MAX / 2, -(LLONG_MAX / 2 + 1), 0/*false*/);
		check_sllsub(LLONG_MAX / 2 + 1, -(LLONG_MAX / 2 + 1), 1/*true*/);
		check_sllsub(353498, 7132983, 0/*false*/);
		check_sllsub(484328, 5234376, 0/*false*/);
	}

	static void check_sllmult(long long a, long long b, int/*bool*/ overflow) {
		long long res;
		int/*bool*/ ov;
		res = 0;
		ov = !scl_mult_ll(&res, a, b);
		SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

		ov = !scl_mult_ll(&res, b, a);
		SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));

		CHECK_MULT(a, b, &res, overflow);
	}
	static void sllmult(void) {
		long long ll;
		check_sllmult(LLONG_MAX, 1, 0/*false*/);
		check_sllmult(LLONG_MIN + 1, -1, 0/*false*/);
		check_sllmult(LLONG_MAX, -1, 0/*false*/);
		check_sllmult(LLONG_MAX / 5, 5, 0/*false*/);
		check_sllmult(LLONG_MAX / 5, -5, 0/*false*/);
		check_sllmult(LLONG_MIN / 5, -5, 0/*false*/);
		check_sllmult(LLONG_MIN / 5, 5, 0/*false*/);
		check_sllmult(LLONG_MAX, 0, 0/*false*/);
		check_sllmult(LLONG_MIN, 0, 0/*false*/);
		check_sllmult(34327, 7138, 0/*false*/);
		check_sllmult(43401, -64155, 0/*false*/);
		check_sllmult(-53492, -5283, 0/*false*/);
		check_sllmult(-121267, 8129, 0/*false*/);

		check_sllmult(LLONG_MAX / 2, LLONG_MAX / 3, 1/*true*/);
		check_sllmult(LLONG_MIN / 2, LLONG_MIN / 3, 1/*true*/);
		check_sllmult(LLONG_MAX / 3, LLONG_MIN / 3, 1/*true*/);
		check_sllmult(LLONG_MIN, -1, 1/*true*/);
		/* TODO CERT */
		check_sllmult(1ull << ((int)sizeof(long long) * 4 - 1), 1ull << ((int)sizeof(long long) * 4), 1/*true*/);
		check_sllmult(-(long long)(1ull << ((int)sizeof(long long) * 4 - 1)), 1ull << ((int)sizeof(long long) * 4), 0/*false*/);
		check_sllmult(-(long long)(1ull << ((int)sizeof(long long) * 4 - 1)), -(long long)(1ull << ((int)sizeof(long long) * 4)), 1/*true*/);
		check_sllmult((1ull << ((int)sizeof(long long) * 4 - 1)) - 1, 1ull << ((int)sizeof(long long) * 4), 0/*false*/);
		check_sllmult(-(long long)((1ull << ((int)sizeof(long long) * 4 - 1)) - 1), 1ull << ((int)sizeof(long long) * 4), 0/*false*/);
		check_sllmult((1ull << ((int)sizeof(long long) * 4 - 1)) - 1, -(long long)(1ull << ((int)sizeof(long long) * 4)), 0/*false*/);
		check_sllmult(-(long long)((1ull << ((int)sizeof(long long) * 4 - 1)) - 1), -(long long)(1ull << ((int)sizeof(long long) * 4)), 0/*false*/);

		CHECK_MULT(SCL_IMAX_MAX, 0, &ll, 0);
		CHECK_MULT(SCL_IMAX_MIN, 0, &ll, 0);
		CHECK_MULT(LLONG_MAX, -1, &ll, 0);
		CHECK_MULT(SCL_IMAX_MAX, 1, &ll, LLONG_MAX < SCL_IMAX_MAX);
		CHECK_MULT(SCL_IMAX_MIN, 1, &ll, LLONG_MAX < SCL_IMAX_MAX);
	}
#endif

#define CHECK_DIV(a, b, res, rest, div, mod, ov) { \
	int/*bool*/ rd, rm, rdm; \
	int volatile zero = 0; /* to suppress warnings */\
	res = 0; \
	rest = 0; \
	div = 0; \
	mod = 0; \
	\
	rd = !SCL_DIV(&(res), a, b); \
	rm = !SCL_MOD(&(rest), a, b); \
	rdm = !SCL_DIV_MOD(&(div), &(mod), a, b); \
	/*printf("rd = %d rm = %d rdm = %d ov = %d res = %lld rest = %lld div = %lld mod = %lld\n", rd, rm, rdm, ov, (long long)(res), (long long)(rest), (long long)(div), (long long)(mod));*/\
	\
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov)); \
	SCL_ASSERT((rd || rm || rdm) || (((res) == (div)) && ((rest) == (mod)))); \
	if (!ov) { \
		if ((a) / ((b) + zero) >= 0) { \
			SCL_ASSERT((scl_umax_t)res == (scl_umax_t)((a) / ((b) + zero))); \
			SCL_ASSERT((scl_umax_t)rest == (scl_umax_t)((a) % ((b) + zero))); \
			SCL_ASSERT((scl_umax_t)((div) * (b) + (mod)) == (scl_umax_t)(a)); \
		} else { \
			SCL_ASSERT(res < 0); \
			SCL_ASSERT((scl_imax_t)res == (scl_imax_t)((a) / ((b) + zero))); \
			SCL_ASSERT((scl_imax_t)rest == (scl_imax_t)((a) % ((b) + zero))); \
			SCL_ASSERT((scl_imax_t)((div) * (b) + (mod)) == (scl_imax_t)(a)); \
		} \
	} \
}

#define CHECK_UDIV(a, b, res, rest, div, mod, ov) { \
	int/*bool*/ rd, rm, rdm; \
	unsigned volatile zero = 0; /* to suppress warnings */\
	res = 0; \
	rest = 0; \
	div = 0; \
	mod = 0; \
	\
	rd = !SCL_DIV(&(res), a, b); \
	rm = !SCL_MOD(&(rest), a, b); \
	rdm = !SCL_DIV_MOD(&(div), &(mod), a, b); \
	/*printf("rd = %d rm = %d rdm = %d ov = %d res = %lld rest = %lld div = %lld mod = %lld\n", rd, rm, rdm, ov, (long long)(res), (long long)(rest), (long long)(div), (long long)(mod));*/\
	\
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov)); \
	SCL_ASSERT((rd || rm || rdm) || (((res) == (div)) && ((rest) == (mod)))); \
	if (!ov) { \
		if ((a) / ((b) + zero) >= 0) { \
			SCL_ASSERT((scl_umax_t)res == (scl_umax_t)((a) / ((b) + zero))); \
			SCL_ASSERT((scl_umax_t)rest == (scl_umax_t)((a) % ((b) + zero))); \
			SCL_ASSERT((scl_umax_t)((div) * (b) + (mod)) == (scl_umax_t)(a)); \
		} else { \
			SCL_ASSERT(res < 0); \
			SCL_ASSERT((scl_imax_t)res == (scl_imax_t)((a) / ((b) + zero))); \
			SCL_ASSERT((scl_imax_t)rest == (scl_imax_t)((a) % ((b) + zero))); \
			SCL_ASSERT((scl_imax_t)((div) * (b) + (mod)) == (scl_imax_t)(a)); \
		} \
	} \
}


static void check_sdiv(int a, int b, int/*bool*/ ov) {
	int/*bool*/ rd, rm, rdm;
	int res = 0, rest = 0, div = 0, mod = 0;

	rd = !scl_div(&res, a, b);
	rm = !scl_mod(&rest, a, b);
	rdm = !scl_div_mod(&div, &mod, a, b);
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
	SCL_ASSERT((rd || rm || rdm) || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
	SCL_ASSERT(ov || (div * b + mod == a));

	CHECK_DIV(a, b, res, rest, div, mod, ov);
}
static void sdiv(void) {
	int res, rest;
	int/*bool*/ ret;
	check_sdiv(1, 0, 1/*true*/);
	check_sdiv(INT_MAX, 0, 1/*true*/);
	check_sdiv(INT_MIN, 0, 1/*true*/);
	check_sdiv(0, 0, 1/*true*/);

	ret = !scl_div(&res, INT_MIN, -1);
	SCL_ASSERT(ret || (INT_MIN + res == 0));

	ret = !scl_mod(&rest, INT_MIN, -1);
	SCL_ASSERT(ret || (rest == 0));

	ret = !scl_div_mod(&res, &rest, INT_MIN, -1);
	SCL_ASSERT(ret || ((res + INT_MIN == 0) && (rest == 0)));

	check_sdiv(INT_MIN, -2, 0/*false*/);
	check_sdiv(INT_MIN, 1, 0/*false*/);
	check_sdiv(INT_MAX, 1, 0/*false*/);
	check_sdiv(INT_MAX, -1, 0/*false*/);
	check_sdiv(32000, 100, 0/*false*/);
	check_sdiv(-540, 27, 0/*false*/);
	check_sdiv(3250, 100, 0/*false*/);
	check_sdiv(-40 * 54 - 17, 54, 0/*false*/);
}


static void check_udiv(unsigned a, unsigned b, int/*bool*/ ov) {
	int/*bool*/ rd, rm, rdm;
	unsigned res = 0, rest = 0, div = 0, mod = 0;

	rd = !scl_div_u(&res, a, b);
	rm = !scl_mod_u(&rest, a, b);
	rdm = !scl_div_mod_u(&div, &mod, a, b);
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
	SCL_ASSERT(ov || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
	SCL_ASSERT(ov || (div * b + mod == a));

	CHECK_UDIV(a, b, res, rest, div, mod, ov);
}
static void udiv(void) {
	check_udiv(1, 0, 1/*true*/);
	check_udiv(0, 0, 1/*true*/);
	check_udiv(UINT_MAX, 0, 1/*true*/);

	check_udiv(0, 2, 0/*false*/);
	check_udiv(UINT_MAX, 1, 0/*false*/);
	check_udiv(32000, 100, 0/*false*/);
	check_udiv(540, 27, 0/*false*/);
	check_udiv(3250, 100, 0/*false*/);
	check_udiv(40 * 54 - 17, 54, 0/*false*/);
}

static void check_sldiv(long a, long b, int/*bool*/ ov) {
	int/*bool*/ rd, rm, rdm;
	long res = 0, rest = 0, div = 0, mod = 0;

	rd = !scl_div_l(&res, a, b);
	rm = !scl_mod_l(&rest, a, b);
	rdm = !scl_div_mod_l(&div, &mod, a, b);
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
	SCL_ASSERT(rd || rm || rdm || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
	SCL_ASSERT(rd || rm || rdm || (div * b + mod == a));

	CHECK_DIV(a, b, res, rest, div, mod, ov);
}
static void sldiv(void) {
	long res, rest;
	int/*bool*/ ret;
	check_sldiv(1, 0, 1/*true*/);
	check_sldiv(LONG_MAX, 0, 1/*true*/);
	check_sldiv(LONG_MIN, 0, 1/*true*/);
	check_sldiv(0, 0, 1/*true*/);

	ret = !scl_div_l(&res, LONG_MIN, -1);
	SCL_ASSERT(ret || (LONG_MIN + res == 0));

	ret = !scl_mod_l(&rest, LONG_MIN, -1);
	SCL_ASSERT(ret || (rest == 0));

	ret = !scl_div_mod_l(&res, &rest, LONG_MIN, -1);
	SCL_ASSERT(ret || ((res + LONG_MIN == 0) && (rest == 0)));

	check_sldiv(LONG_MIN, -2, 0/*false*/);
	check_sldiv(LONG_MIN, 1, 0/*false*/);
	check_sldiv(LONG_MAX, 1, 0/*false*/);
	check_sldiv(LONG_MAX, -1, 0/*false*/);
	check_sldiv(732200, 100, 0/*false*/);
	check_sldiv(-5400, 27, 0/*false*/);
	check_sldiv(3250, 100, 0/*false*/);
	check_sldiv(-401 * 54 - 179, 54, 0/*false*/);
}


static void check_uldiv(unsigned long a, unsigned long b, int/*bool*/ ov) {
	int/*bool*/ rd, rm, rdm;
	unsigned long res = 0, rest = 0, div = 0, mod = 0;

	rd = !scl_div_ul(&res, a, b);
	rm = !scl_mod_ul(&rest, a, b);
	rdm = !scl_div_mod_ul(&div, &mod, a, b);
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
	SCL_ASSERT(ov || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
	SCL_ASSERT(ov || (div * b + mod == a));

	CHECK_UDIV(a, b, res, rest, div, mod, ov);
}
static void uldiv(void) {
	check_uldiv(1, 0, 1/*true*/);
	check_uldiv(0, 0, 1/*true*/);
	check_uldiv(ULONG_MAX, 0, 1/*true*/);

	check_uldiv(0, 2, 0/*false*/);
	check_uldiv(ULONG_MAX, 1, 0/*false*/);
	check_uldiv(32000, 100, 0/*false*/);
	check_uldiv(540, 27, 0/*false*/);
	check_uldiv(3250, 100, 0/*false*/);
	check_uldiv(40 * 54 - 17, 54, 0/*false*/);
}

static void check_size_div(size_t a, size_t b, int/*bool*/ ov) {
	int/*bool*/ rd, rm, rdm;
	size_t res = 0, rest = 0, div = 0, mod = 0;

	rd = !scl_div_sz(&res, a, b);
	rm = !scl_mod_sz(&rest, a, b);
	rdm = !scl_div_mod_sz(&div, &mod, a, b);
	SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
	SCL_ASSERT(ov || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
	SCL_ASSERT(ov || (div * b + mod == a));
}
static void size_div(void) {
	check_size_div(1, 0, 1/*true*/);
	check_size_div(0, 0, 1/*true*/);
	check_size_div(SIZE_MAX, 0, 1/*true*/);

	check_size_div(0, 2, 0/*false*/);
	check_size_div(SIZE_MAX, 1, 0/*false*/);
	check_size_div(32000, 100, 0/*false*/);
	check_size_div(540, 27, 0/*false*/);
	check_size_div(3250, 100, 0/*false*/);
	check_size_div(40 * 54 - 17, 54, 0/*false*/);
}

#if SCL_USE_LONG_LONG
	static void check_slldiv(long long a, long long b, int/*bool*/ ov) {
		int/*bool*/ rd, rm, rdm;
		long long res = 0, rest = 0, div = 0, mod = 0;

		rd = !scl_div_ll(&res, a, b);
		rm = !scl_mod_ll(&rest, a, b);
		rdm = !scl_div_mod_ll(&div, &mod, a, b);
		SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
		SCL_ASSERT(rd || rm || rdm || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
		SCL_ASSERT(rd || rm || rdm || (div * b + mod == a));

		CHECK_DIV(a, b, res, rest, div, mod, ov);
	}
	static void slldiv(void) {
		long long res, rest;
		int/*bool*/ ret;
		check_slldiv(1, 0, 1/*true*/);
		check_slldiv(LLONG_MAX, 0, 1/*true*/);
		check_slldiv(LLONG_MIN, 0, 1/*true*/);
		check_slldiv(0, 0, 1/*true*/);

		ret = !scl_div_ll(&res, LLONG_MIN, -1);
		SCL_ASSERT(ret || (LLONG_MIN + res == 0));

		ret = !scl_mod_ll(&rest, LLONG_MIN, -1);
		SCL_ASSERT(ret || (rest == 0));

		ret = !scl_div_mod_ll(&res, &rest, LLONG_MIN, -1);
		SCL_ASSERT(ret || ((res + LLONG_MIN == 0) && (rest == 0)));

		check_slldiv(LLONG_MIN, -2, 0/*false*/);
		check_slldiv(LLONG_MIN, 1, 0/*false*/);
		check_slldiv(LLONG_MAX, 1, 0/*false*/);
		check_slldiv(LLONG_MAX, -1, 0/*false*/);
		check_slldiv(732200, 100, 0/*false*/);
		check_slldiv(-5400, 27, 0/*false*/);
		check_slldiv(3250, 100, 0/*false*/);
		check_slldiv(-401 * 54 - 179, 54, 0/*false*/);
	}

	static void check_ulldiv(unsigned long long a, unsigned long long b, int/*bool*/ ov) {
		int/*bool*/ rd, rm, rdm;
		unsigned long long res = 0, rest = 0, div = 0, mod = 0;

		rd = !scl_div_ull(&res, a, b);
		rm = !scl_mod_ull(&rest, a, b);
		rdm = !scl_div_mod_ull(&div, &mod, a, b);
		SCL_ASSERT((rd == rm) && (rm == rdm) && (rd == ov));
		SCL_ASSERT(ov || ((res == div) && (rest == mod) && (res == a / b) && (rest == a % b)));
		SCL_ASSERT(ov || (div * b + mod == a));

		CHECK_UDIV(a, b, res, rest, div, mod, ov);
	}
	static void ulldiv(void) {
		check_ulldiv(1, 0, 1/*true*/);
		check_ulldiv(0, 0, 1/*true*/);
		check_ulldiv(ULLONG_MAX, 0, 1/*true*/);

		check_ulldiv(0, 2, 0/*false*/);
		check_ulldiv(ULLONG_MAX, 1, 0/*false*/);
		check_ulldiv(32000, 100, 0/*false*/);
		check_ulldiv(540, 27, 0/*false*/);
		check_ulldiv(3250, 100, 0/*false*/);
		check_ulldiv(40 * 54 - 17, 54, 0/*false*/);
	}

	static void combll_div(void) {
		unsigned long long ures = 0, urest = 0, udiv = 0, umod = 0;
		long long res = 0, rest = 0, div = 0, mod = 0;
		CHECK_UDIV(2 * (scl_umax_t)LLONG_MAX, 2, ures, urest, udiv, umod, 0/*false*/);
		CHECK_UDIV((scl_umax_t)LLONG_MAX, 2, res, rest, div, mod, 0/*false*/);
#	if __GNUC__ >= 5
		CHECK_DIV(LLONG_MIN, -1, ures, rest, udiv, mod, (INT_MAX + INT_MIN < 0));
		/*printf("div = %lld mod = %lld\n", (-LLONG_MAX) / (-1), (-LLONG_MAX) % (-1));*/
		CHECK_DIV(-LLONG_MAX, -1, ures, rest, udiv, mod, 0/*false*/);
		CHECK_DIV(2 * (scl_umax_t)LLONG_MAX, 2, res, rest, div, mod, 1/*true*/);

		CHECK_DIV(LLONG_MIN, ULLONG_MAX, ures, rest, udiv, mod, 1);
		CHECK_DIV(LLONG_MAX, ULLONG_MAX / 2, res, rest, div, mod, 0/*true*/);
		CHECK_DIV(LLONG_MAX, ULLONG_MAX, ures, urest, udiv, umod, 0/*false*/);
#	endif
	}
#endif

static void check_size_add(size_t a, size_t b, int/*bool*/ overflow) {
	int/*bool*/ ov;
	size_t res = 0;

	ov = !scl_add_sz(&res, a, b);
	SCL_ASSERT((ov == overflow) && (overflow || (res == (a + b))));

	ov = !scl_add_sz(&res, b, a);
	SCL_ASSERT((ov == overflow) && (overflow || (res == (b + a))));
}
static void size_add(void) {
	check_size_add(SIZE_MAX, 0, 0/*false*/);
	check_size_add(SIZE_MAX, 1, 1/*true*/);
	check_size_add(SIZE_MAX / 2 + 1, SIZE_MAX / 2, 0/*false*/);
	check_size_add(SIZE_MAX / 2 + 1, SIZE_MAX / 2 + 1, 1/*true*/);
	check_size_add(34986ul, 129834ul, 0/*false*/);
}

static void check_abs(int v, int overflow) {
	int ov;
	int res, inv;
	res = 0; inv = 0;

	ov = scl_abs(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == (v >= 0 ? v : -v))));

	if (ov) {
		ov = scl_abs(&inv, res);
		SCL_ASSERT(ov && (res == inv));
	}

	ov = SCL_ABS(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == (v >= 0 ? v : -v))));

	if (ov) {
		ov = SCL_ABS(&inv, res);
		SCL_ASSERT(ov && (res == inv));
	}
}

static void s_abs(void) {
	check_abs(-1, 1);
	check_abs(0, 1);
	check_abs(INT_MIN, 0);
	check_abs(INT_MIN + 1, 1);
	check_abs(INT_MAX, 1);
	check_abs(-5748, 1);
	check_abs(17642, 1);
}

static void check_labs(long v, int overflow) {
	int ov;
	long res, inv;
	res = 0; inv = 0;

	ov = scl_abs_l(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == (v >= 0 ? v : -v))));

	if (ov) {
		ov = scl_abs_l(&inv, res);
		SCL_ASSERT(ov && (res == inv));
	}

	ov = SCL_ABS(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == (v >= 0 ? v : -v))));

	if (ov) {
		ov = SCL_ABS(&inv, res);
		SCL_ASSERT(ov && (res == inv));
	}
}

static void s_labs(void) {
	check_labs(-1, 1);
	check_labs(0, 1);
	check_labs(LONG_MIN, 0);
	check_labs(LONG_MIN + 1, 1);
	check_labs(LONG_MAX, 1);
	check_labs(-655748, 1);
	check_labs(1764263, 1);
}

static scl_imax_t abs_control(scl_imax_t v) {
	if (v < 0) {
		v = -v;
	}
	return v;
}

static void check_imax_abs(scl_imax_t v, int overflow) {
	int ov;
	scl_imax_t res, inv;
	res = 0; inv = 0;

	ov = scl_abs_imx(&res, v);
	/*printf("ov = %d overflow = %d v = %lld res = %lld sizeof(v) = %d %lld %lld\n",
		ov, overflow, (long long)v, (long long)res, (int)sizeof(v),
		(long long)SCL_IMAX_MAX, (long long)SCL_IMAX_MIN);*/
	SCL_ASSERT((ov == overflow) && (!ov || (res == abs_control(v))));

	if (ov) {
		ov = scl_abs_imx(&inv, res);
		SCL_ASSERT(ov && (res == inv));
	}
}

static void imax_abs(void) {
	check_imax_abs(-1, 1);
	check_imax_abs(0, 1);
	check_imax_abs(SCL_IMAX_MIN, -SCL_IMAX_MAX == SCL_IMAX_MIN);
	check_imax_abs(SCL_IMAX_MIN + 1, 1);
	check_imax_abs(SCL_IMAX_MAX, 1);
	check_imax_abs(-53748, 1);
	check_imax_abs(176442, 1);
}

static void check_neg(int v, int overflow) {
	int ov;
	int res, inv;
	res = 0; inv = 0;

	ov = scl_neg(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

	if (ov) {
		ov = scl_neg(&inv, res);
		SCL_ASSERT(ov && (v == inv));
	}

	ov = SCL_NEG(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

	if (ov) {
		ov = SCL_NEG(&inv, res);
		SCL_ASSERT(ov && (v == inv));
	}
}

static void neg(void) {
	check_neg(-1, 1);
	check_neg(0, 1);
	check_neg(INT_MIN, 0);
	check_neg(INT_MIN + 1, 1);
	check_neg(INT_MAX, 1);
	check_neg(-5748, 1);
	check_neg(17642, 1);
}

static void check_lneg(long v, int overflow) {
	int ov;
	long res, inv;
	res = 0; inv = 0;

	ov = scl_neg_l(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

	if (ov) {
		ov = scl_neg_l(&inv, res);
		SCL_ASSERT(ov && (v == inv));
	}

	ov = SCL_NEG(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

	if (ov) {
		ov = SCL_NEG(&inv, res);
		SCL_ASSERT(ov && (v == inv));
	}
}

static void lneg(void) {
	check_lneg(-1, 1);
	check_lneg(0, 1);
	check_lneg(LONG_MIN, 0);
	check_lneg(LONG_MIN + 1, 1);
	check_lneg(LONG_MAX, 1);
	check_lneg(-55748, 1);
	check_lneg(3617642, 1);
}

static void check_imax_neg(scl_imax_t v, int overflow) {
	int ov;
	scl_imax_t res, inv;
	res = 0; inv = 0;

	ov = scl_neg_imx(&res, v);
	SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

	if (ov) {
		ov = scl_neg_imx(&inv, res);
		SCL_ASSERT(ov && (v == inv));
	}
}

static void imax_neg(void) {
	check_imax_neg(-1, 1);
	check_imax_neg(0, 1);
	check_imax_neg(SCL_IMAX_MIN, 0);
	check_imax_neg(SCL_IMAX_MIN + 1, 1);
	check_imax_neg(SCL_IMAX_MAX, 1);
	check_imax_neg(-557748, 1);
	check_imax_neg(43617642, 1);
}

#define CHECK_ADD(a, b, res, overflow) do {\
	int ov;\
	scl_imax_t a1 = (a), b1 = (b); \
	\
	ov = !SCL_ADD(res, (a), (b));\
	/*printf("ov = %d overflow = %d a1 = %lld b1 = %lld res = %lld\n", ov, overflow, (long long)a1, (long long)b1, (long long)*(res));*/\
	SCL_ASSERT((ov == (overflow)) && (ov || (*(res) == (a1 + b1))));\
	\
	ov = !SCL_ADD(res, (b), (a));\
	SCL_ASSERT((ov == (overflow)) && (ov || (*(res) == (b1 + a1))));\
} while (0)

#define CHECK_UADD(a, b, res, overflow) do {\
	int ov;\
	scl_umax_t a1 = (a), b1 = (b); \
	\
	ov = !SCL_ADD(res, (a), (b));\
	SCL_ASSERT((ov == (overflow)) && (ov || (*(res) == ((a1) + (b1)))));\
	\
	ov = !SCL_ADD(res, (b), (a));\
	SCL_ASSERT((ov == (overflow)) && (ov || (*(res) == ((b1) + (a1)))));\
} while (0)

#define CHECK_USADD(a, b, res, overflow, answer) do {\
	int ov;\
	\
	ov = !SCL_ADD(res, (a), (b));\
	SCL_ASSERT((ov == (overflow)) && (ov || (*(res) == (answer))));\
	\
	ov = !SCL_ADD(res, (b), (a));\
	SCL_ASSERT((ov == (overflow)) && (ov || (*(res) == (answer))));\
} while (0)

static void ADD(void) {
	char signed c;
	char unsigned uc;
	short s;
	unsigned short us;

	long resl;
	int resi;
	scl_imax_t resm;

	unsigned resu;
	unsigned long resul;

	CHECK_ADD(SCHAR_MIN, 0, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MIN + 876, -876, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MIN + 924, -876, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MIN + 876, -877, &c, 1/*true*/);
	CHECK_ADD(SCHAR_MIN / 2, SCHAR_MIN / 2 - 1, &c, 1/*true*/);

	CHECK_ADD(SCHAR_MIN, UCHAR_MAX, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MIN + 2, UCHAR_MAX, &c, 1/*true*/);

	CHECK_ADD(SCHAR_MAX, 0, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MAX - 1876, 1876, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MAX - 1934, 1876, &c, 0/*false*/);
	CHECK_ADD(SCHAR_MAX - 2876, 2877, &c, 1/*true*/);
	CHECK_ADD(SCHAR_MAX / 2, SCHAR_MAX / 2 + 2, &c, 1/*true*/);
	CHECK_ADD(SCHAR_MAX, SCHAR_MIN, &c, 0/*false*/);

	CHECK_ADD(SCL_IMAX_MAX, (SCL_IMAX_MIN + 1) + SCHAR_MAX, &c, 0);
	CHECK_ADD(SCL_IMAX_MAX, (SCL_IMAX_MIN + 2) + SCHAR_MAX, &c, 1);
	CHECK_ADD(SCHAR_MAX, SCHAR_MIN, &c, 0);
	CHECK_ADD(SCHAR_MAX, SCHAR_MAX, &c, 1);
	CHECK_ADD(SCHAR_MAX, 1, &c, 1);
	CHECK_ADD(SCHAR_MAX, -1, &c, 0);
	CHECK_ADD(SCL_IMAX_MIN + 1, SCL_IMAX_MAX + SCHAR_MIN, &c, 0);
	CHECK_ADD(SCL_IMAX_MIN, SCL_IMAX_MAX + SCHAR_MIN, &c, 1);
	CHECK_ADD(SCHAR_MIN, SCHAR_MIN, &c, 1);
	CHECK_ADD(SCHAR_MIN, -1, &c, 1);
	CHECK_ADD(SCHAR_MIN, 1, &c, 0);

	CHECK_USADD(SCL_IMAX_MIN, (scl_umax_t)SCL_IMAX_MAX, &c, 0, SCL_IMAX_MAX + SCL_IMAX_MIN);
	CHECK_USADD(SCL_IMAX_MIN, SCL_UMAX_MAX, &c, 1, 0);

	CHECK_UADD(UCHAR_MAX, 0, &uc, 0/*false*/);
	CHECK_UADD(UCHAR_MAX, 1, &uc, 1/*true*/);
	CHECK_UADD(UCHAR_MAX / 2 + 1, UCHAR_MAX / 2, &uc, 0/*false*/);
	CHECK_UADD(UCHAR_MAX / 2 + 1, UCHAR_MAX / 2 + 1, &uc, 1/*true*/);
	CHECK_UADD(34, 83, &uc, 0/*false*/);


	CHECK_ADD(SHRT_MIN, 0, &s, 0/*false*/);
	CHECK_ADD(SHRT_MIN + 9876, -9876, &s, 0/*false*/);
	CHECK_ADD(SHRT_MIN + 5924, -5876, &s, 0/*false*/);
	CHECK_ADD(SHRT_MIN + 1876, -1877, &s, 1/*true*/);
	CHECK_ADD(SHRT_MIN / 2, SHRT_MIN / 2 - 1, &s, 1/*true*/);

	CHECK_ADD(SHRT_MAX, 0, &s, 0/*false*/);
	CHECK_ADD(SHRT_MAX - 21876, 21876, &s, 0/*false*/);
	CHECK_ADD(SHRT_MAX - 61934, 60076, &s, 0/*false*/);
	CHECK_ADD(SHRT_MAX - 28976, 28977, &s, 1/*true*/);
	CHECK_ADD(SHRT_MAX / 2, SHRT_MAX / 2 + 2, &s, 1/*true*/);
	CHECK_ADD(SHRT_MAX, SHRT_MIN, &s, 0/*false*/);

	CHECK_ADD(SCL_IMAX_MAX, (SCL_IMAX_MIN + 1) + SHRT_MAX, &s, 0);
	CHECK_ADD(SCL_IMAX_MAX, (SCL_IMAX_MIN + 2) + SHRT_MAX, &s, 1);
	CHECK_ADD(SHRT_MAX, SHRT_MIN, &s, 0);
	CHECK_ADD(SHRT_MAX, SHRT_MAX, &s, 1);
	CHECK_ADD(SHRT_MAX, 1, &s, 1);
	CHECK_ADD(SHRT_MAX, -1, &s, 0);
	CHECK_ADD(SCL_IMAX_MIN + 1, SCL_IMAX_MAX + SHRT_MIN, &s, 0);
	CHECK_ADD(SCL_IMAX_MIN, SCL_IMAX_MAX + SHRT_MIN, &s, 1);
	CHECK_ADD(SHRT_MIN, SHRT_MIN, &s, 1);
	CHECK_ADD(SHRT_MIN, -1, &s, 1);
	CHECK_ADD(SHRT_MIN, 1, &s, 0);

	CHECK_UADD(USHRT_MAX, 0, &us, 0/*false*/);
	CHECK_UADD(USHRT_MAX, 1, &us, 1/*true*/);
	CHECK_UADD(USHRT_MAX / 2 + 1, USHRT_MAX / 2, &us, 0/*false*/);
	CHECK_UADD(USHRT_MAX / 2 + 1, USHRT_MAX / 2 + 1, &us, 1/*true*/);
	CHECK_UADD(334, 8335, &us, 0/*false*/);


	CHECK_ADD(INT_MIN, 0, &resi, 0/*false*/);
	CHECK_ADD(INT_MIN + 876, -876, &resi, 0/*false*/);
	CHECK_ADD(INT_MIN + 9234, -876, &resi, 0/*false*/);
	CHECK_ADD(INT_MIN + 876, -877, &resi, 1/*true*/);
	CHECK_ADD(INT_MIN / 2, INT_MIN / 2 - 1, &resi, 1/*true*/);

	CHECK_ADD(INT_MAX, 0, &resi, 0/*false*/);
	CHECK_ADD(INT_MAX - 1876, 1876, &resi, 0/*false*/);
	CHECK_ADD(INT_MAX - 19234, 1876, &resi, 0/*false*/);
	CHECK_ADD(INT_MAX - 2876, 2877, &resi, 1/*true*/);
	CHECK_ADD(INT_MAX / 2, INT_MAX / 2 + 2, &resi, 1/*true*/);

	CHECK_ADD(INT_MIN, INT_MAX, &resi, 0/*false*/);


	CHECK_ADD(LONG_MIN, 0, &resl, 0/*false*/);
	CHECK_ADD(LONG_MIN + 876, -876, &resl, 0/*false*/);
	CHECK_ADD(LONG_MIN + 9234, -876, &resl, 0/*false*/);
	CHECK_ADD(LONG_MIN + 876, -877, &resl, 1/*true*/);
	CHECK_ADD(LONG_MIN / 2, LONG_MIN / 2 - 1, &resl, 1/*true*/);

	CHECK_ADD(LONG_MAX, 0, &resl, 0/*false*/);
	CHECK_ADD(LONG_MAX - 1876, 1876, &resl, 0/*false*/);
	CHECK_ADD(LONG_MAX - 19234, 1876, &resl, 0/*false*/);
	CHECK_ADD(LONG_MAX - 2876, 2877, &resl, 1/*true*/);
	CHECK_ADD(LONG_MAX / 2, LONG_MAX / 2 + 2, &resl, 1/*true*/);

	CHECK_ADD(LONG_MIN, LONG_MAX, &resl, 0/*false*/);


	CHECK_ADD(SCL_IMAX_MIN, 0, &resm, 0);
	CHECK_ADD(SCL_IMAX_MIN + 876, -876, &resm, 0);
	CHECK_ADD(SCL_IMAX_MIN + 9234, -876, &resm, 0);
	CHECK_ADD(SCL_IMAX_MIN + 876, -877, &resm, 1);
	CHECK_ADD(SCL_IMAX_MIN / 2, SCL_IMAX_MIN / 2 - 1, &resm, 1);

	CHECK_ADD(SCL_IMAX_MAX, 0, &resm, 0);
	CHECK_ADD(SCL_IMAX_MAX - 1876, 1876, &resm, 0);
	CHECK_ADD(SCL_IMAX_MAX - 19234, 1876, &resm, 0);
	CHECK_ADD(SCL_IMAX_MAX - 2876, 2877, &resm, 1);
	CHECK_ADD(SCL_IMAX_MAX / 2, SCL_IMAX_MAX / 2 + 2, &resm, 1);

	CHECK_ADD(SCL_IMAX_MIN, SCL_IMAX_MAX, &resm, 0);


	CHECK_UADD(UINT_MAX, 0, &resu, 0/*false*/);
	CHECK_UADD(UINT_MAX, 1, &resu, 1/*true*/);
	CHECK_UADD(UINT_MAX / 2 + 1, UINT_MAX / 2, &resu, 0/*false*/);
	CHECK_UADD(UINT_MAX / 2 + 1, UINT_MAX / 2 + 1, &resu, 1/*true*/);
	CHECK_UADD(3498, 12983, &resu, 0/*false*/);

	CHECK_UADD(ULONG_MAX, 0, &resul, 0/*false*/);
	CHECK_UADD(ULONG_MAX, 1, &resul, 1/*true*/);
	CHECK_UADD(ULONG_MAX / 2 + 1, ULONG_MAX / 2, &resul, 0/*false*/);
	CHECK_UADD(ULONG_MAX / 2 + 1, ULONG_MAX / 2 + 1, &resul, 1/*true*/);
	CHECK_UADD(34986ul, 129834ul, &resul, 0/*false*/);
}

#define CHECK_SUB(a, b, res, overflow1, overflow2) { \
	int/*bool*/ ov[2]; \
	scl_imax_t a1 = (a), b1 = (b); \
	\
	res[0] = 0; res[1] = 0; \
	ov[0] = !SCL_SUB(res + 0, (a), (b)); \
	ov[1] = !SCL_SUB(res + 1, (b), (a)); \
	SCL_ASSERT((ov[0] == overflow1) && (ov[0] || (res[0] == (a1 - b1)))); \
	SCL_ASSERT((ov[1] == overflow2) && (ov[1] || (res[1] == (b1 - a1)))); \
	\
	if (ov[0] != ov[1]) { \
		/*printf("ov2=%d overflow=%d %lld %lld\n", ov2, overflow, (long long int)(*(res2)), ((-2ll) * (LL1 << ((int)sizeof(*(res2)) * 8 - 2))));*/ \
		SCL_ASSERT((ov[0] && ((res[0] == 0) || (sizeof(res[0]) < sizeof(int)))) || (!ov[0] && (res[0] == ((-2) * (LL1 << (sizeof(res[0]) * CHAR_BIT - 2)))))); \
		SCL_ASSERT((ov[1] && ((res[1] == 0) || (sizeof(res[1]) < sizeof(int)))) || (!ov[1] && (res[1] == ((-2) * (LL1 << (sizeof(res[1]) * CHAR_BIT - 2)))))); \
	} \
}

#define CHECK_USUB(a, b, res, overflow1, overflow2) { \
	int/*bool*/ ov[2]; \
	scl_umax_t a1 = (a), b1 = (b); \
	\
	res[0] = 0; res[1] = 0; \
	ov[0] = !SCL_SUB(res + 0, (a), (b)); \
	ov[1] = !SCL_SUB(res + 1, (b), (a)); \
	\
	/*printf("ov[0]=%d overflow1 = %d %llu %llu\n", ov[0], overflow1, (long long unsigned)(res[1]), ((-2ll) * (1ll << ((int)sizeof(res[1]) * 8 - 2))));*/ \
	SCL_ASSERT((ov[0] == (overflow1)) && (ov[0] || (res[0] == (a1 - b1)))); \
	SCL_ASSERT((ov[1] == (overflow2)) && (ov[1] || (res[1] == (b1 - a1)))); \
}

#define CHECK_USSUB(a, b, res, overflow1, overflow2, answer) { \
	int/*bool*/ ov[2]; \
	\
	res[0] = 0; res[1] = 0; \
	ov[0] = !SCL_SUB(res + 0, (a), (b)); \
	ov[1] = !SCL_SUB(res + 1, (b), (a)); \
	SCL_ASSERT((ov[0] == overflow1) && (ov[0] || (res[0] == (answer)))); \
	SCL_ASSERT((ov[1] == overflow2) && (ov[1] || (res[1] == -(answer)))); \
	\
	if (ov[0] != ov[1]) { \
		/*printf("ov2=%d overflow=%d %lld %lld\n", ov2, overflow, (long long int)(*(res2)), ((-2ll) * (LL1 << ((int)sizeof(*(res2)) * 8 - 2))));*/ \
		SCL_ASSERT((ov[0] && ((res[0] == 0) || (sizeof(res[0]) < sizeof(int)))) || (!ov[0] && (res[0] == ((-2) * (LL1 << (sizeof(res[0]) * CHAR_BIT - 2)))))); \
		SCL_ASSERT((ov[1] && ((res[1] == 0) || (sizeof(res[1]) < sizeof(int)))) || (!ov[1] && (res[1] == ((-2) * (LL1 << (sizeof(res[1]) * CHAR_BIT - 2)))))); \
	} \
}

static void SUB(void) {
	char signed c[2];
	char unsigned uc[2];
	short s[2];
	unsigned short us[2];
	int i[2];
	unsigned u[2];
	long l[2];
	unsigned long ul[2];

	CHECK_SUB(SCHAR_MAX, 0, c, 0, 0);
	CHECK_SUB(SCHAR_MIN, 0, c, 0, 1);
	CHECK_SUB(SCHAR_MAX, SCHAR_MAX, c, 0, 0);
	CHECK_SUB(SCHAR_MIN, SCHAR_MIN, c, 0, 0);
	CHECK_SUB(SCHAR_MIN, SCHAR_MAX, c, 1, 1);
	CHECK_SUB(SCHAR_MAX, SCHAR_MAX - 1, c, 0, 0);
	CHECK_SUB(SCHAR_MIN, SCHAR_MIN + 1, c, 0, 0);
	CHECK_SUB(SCHAR_MAX / 2 - 1, -(SCHAR_MAX / 2 + 1), c, 0, 0);
	CHECK_SUB(SCHAR_MAX / 2, -(SCHAR_MAX / 2 + 1), c, 0, 0);
	CHECK_SUB(SCHAR_MAX / 2 + 1, -(SCHAR_MAX / 2 + 1), c, 1, 0);
	CHECK_SUB(3498, 3566, c, 0, 0);
	CHECK_SUB(4328, 4444, c, 0, 0);

	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - 1, c, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + 1, c, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - SCHAR_MAX, c, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + SCHAR_MAX, c, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - SCHAR_MAX - 1, c, 1, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + SCHAR_MAX + 1, c, 0, 1);

	CHECK_USSUB(SCHAR_MAX, (scl_umax_t)SCHAR_MAX * 2, c, 0, 0, -SCHAR_MAX);
	CHECK_USSUB(SCHAR_MIN, (scl_umax_t)SCHAR_MAX + 1, c, 1, 1, 0);


	CHECK_USUB(UCHAR_MAX, 0, uc, 0, 1);
	CHECK_USUB(0, 0, uc, 0, 0);
	CHECK_USUB(UCHAR_MAX, UCHAR_MAX, uc, 0, 0);
	CHECK_USUB(UCHAR_MAX, UCHAR_MAX - 1, uc, 0, 1);
	CHECK_USUB(UCHAR_MAX / 2 - 1, UCHAR_MAX / 2 + 1, uc, 1, 0);
	CHECK_USUB(3498, 3555, uc, 1, 0);
	CHECK_USUB(4328, 4379, uc, 1, 0);

	CHECK_USUB(SCL_UMAX_MAX, 0, uc, 1, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - 1, uc, 0, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - UCHAR_MAX, uc, 0, 1);

	CHECK_SUB(SHRT_MAX, 0, s, 0, 0);
	CHECK_SUB(SHRT_MIN, 0, s, 0, 1);
	CHECK_SUB(SHRT_MAX, SHRT_MAX, s, 0, 0);
	CHECK_SUB(SHRT_MIN, SHRT_MIN, s, 0, 0);
	CHECK_SUB(SHRT_MIN, SHRT_MAX, s, 1, 1);
	CHECK_SUB(SHRT_MAX, SHRT_MAX - 1, s, 0, 0);
	CHECK_SUB(SHRT_MIN, SHRT_MIN + 1, s, 0, 0);
	CHECK_SUB(SHRT_MAX / 2 - 1, -(SHRT_MAX / 2 + 1), s, 0, 0);
	CHECK_SUB(SHRT_MAX / 2, -(SHRT_MAX / 2 + 1), s, 0, 0);
	CHECK_SUB(SHRT_MAX / 2 + 1, -(SHRT_MAX / 2 + 1), s, 1, 0);
	CHECK_SUB(3498, 12983, s, 0, 0);
	CHECK_SUB(4328, 23376, s, 0, 0);

	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - 1, s, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + 1, s, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - SHRT_MAX, s, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + SHRT_MAX, s, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - SHRT_MAX - 1, s, 1, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + SHRT_MAX + 1, s, 0, 1);


	CHECK_USUB(USHRT_MAX, 0, us, 0, 1);
	CHECK_USUB(0, 0, us, 0, 0);
	CHECK_USUB(USHRT_MAX, USHRT_MAX, us, 0, 0);
	CHECK_USUB(USHRT_MAX, USHRT_MAX - 1, us, 0, 1);
	CHECK_USUB(USHRT_MAX / 2 - 1, USHRT_MAX / 2 + 1, us, 1, 0);
	CHECK_USUB(3498, 12983, us, 1, 0);
	CHECK_USUB(4328, 23376, us, 1, 0);

	CHECK_USUB(SCL_UMAX_MAX, 0, us, 1, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - 1, us, 0, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - USHRT_MAX, us, 0, 1);


	CHECK_SUB(INT_MAX, 0, i, 0, 0);
	CHECK_SUB(INT_MIN, 0, i, 0, 1);
	CHECK_SUB(INT_MAX, INT_MAX, i, 0, 0);
	CHECK_SUB(INT_MIN, INT_MIN, i, 0, 0);
	CHECK_SUB(INT_MIN, INT_MAX, i, 1, 1);
	CHECK_SUB(INT_MAX, INT_MAX - 1, i, 0, 0);
	CHECK_SUB(INT_MIN, INT_MIN + 1, i, 0, 0);
	CHECK_SUB(INT_MAX / 2 - 1, -(INT_MAX / 2 + 1), i, 0, 0);
	CHECK_SUB(INT_MAX / 2, -(INT_MAX / 2 + 1), i, 0, 0);
	CHECK_SUB(INT_MAX / 2 + 1, -(INT_MAX / 2 + 1), i, 1, 0);
	CHECK_SUB(3498, 12983, i, 0, 0);
	CHECK_SUB(4328, 23376, i, 0, 0);

	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - 1, i, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + 1, i, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - INT_MAX, i, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + INT_MAX, i, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - INT_MAX - 1, i, 1, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + INT_MAX + 1, i, 0, 1);


	CHECK_USUB(UINT_MAX, 0, u, 0, 1);
	CHECK_USUB(0, 0, u, 0, 0);
	CHECK_USUB(UINT_MAX, UINT_MAX, u, 0, 0);
	CHECK_USUB(UINT_MAX, UINT_MAX - 1, u, 0, 1);
	CHECK_USUB(UINT_MAX / 2 - 1, UINT_MAX / 2 + 1, u, 1, 0);
	CHECK_USUB(3498, 12983, u, 1, 0);
	CHECK_USUB(4328, 23376, u, 1, 0);

	CHECK_USUB(SCL_UMAX_MAX, 0, u, (scl_umax_t)UINT_MAX < SCL_UMAX_MAX, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - 1, u, 0, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - UINT_MAX, u, 0, 1);


	CHECK_SUB(LONG_MAX, 0, l, 0, 0);
	CHECK_SUB(LONG_MIN, 0, l, 0, 1);
	CHECK_SUB(LONG_MAX, LONG_MAX, l, 0, 0);
	CHECK_SUB(LONG_MIN, LONG_MIN, l, 0, 0);
	CHECK_SUB(LONG_MIN, LONG_MAX, l, 1, 1);
	CHECK_SUB(LONG_MAX, LONG_MAX - 1, l, 0, 0);
	CHECK_SUB(LONG_MIN, LONG_MIN + 1, l, 0, 0);
	CHECK_SUB(LONG_MAX / 2 - 1, -(LONG_MAX / 2 + 1), l, 0, 0);
	CHECK_SUB(LONG_MAX / 2, -(LONG_MAX / 2 + 1), l, 0, 0);
	CHECK_SUB(LONG_MAX / 2 + 1, -(LONG_MAX / 2 + 1), l, 1, 0);
	CHECK_SUB(33498, 712983, l, 0, 0);
	CHECK_SUB(84328, 523376, l, 0, 0);

	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - 1, l, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + 1, l, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - LONG_MAX, l, 0, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + LONG_MAX, l, 0, 0);
	CHECK_SUB(SCL_IMAX_MAX, SCL_IMAX_MAX - LONG_MAX - 1, l, 1, 0);
	CHECK_SUB(SCL_IMAX_MIN, SCL_IMAX_MIN + LONG_MAX + 1, l, 0, 1);


	CHECK_USUB(ULONG_MAX, 0, ul, 0, 1);
	CHECK_USUB(0, 0, ul, 0, 0);
	CHECK_USUB(ULONG_MAX, ULONG_MAX, ul, 0, 0);
	CHECK_USUB(ULONG_MAX, ULONG_MAX - 1, ul, 0, 1);
	CHECK_USUB(ULONG_MAX / 2 - 1, ULONG_MAX / 2 + 1, ul, 1, 0);
	CHECK_USUB(3498, 12983, ul, 1, 0);
	CHECK_USUB(4328, 23376, ul, 1, 0);

	CHECK_USUB(SCL_UMAX_MAX, 0, ul, (int)(ULONG_MAX < SCL_UMAX_MAX), 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - 1, ul, 0, 1);
	CHECK_USUB(SCL_UMAX_MAX, SCL_UMAX_MAX - ULONG_MAX, ul, 0, 1);
}

static void mov_c(int corr, scl_imax_t i) {
	int ret;
	char signed r;
	scl_umax_t u;

	r = -2;

	ret = scl_c_mov(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	if (i >= 0) {
		u = (scl_umax_t)i;

		ret = scl_c_mov_u(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));

		ret = SCL_MOV(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));
	}
}

static void mov_uc_u(int corr, scl_umax_t i) {
	int ret;
	char unsigned r;
	scl_imax_t s;

	r = 23;

	ret = scl_uc_mov_u(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	s = -(scl_imax_t)(i / 2);

	ret = scl_uc_mov(&r, s);
	SCL_ASSERT(ret == (s == 0));

	ret = SCL_MOV(&r, s);
	SCL_ASSERT(ret == (s == 0));
}

static void mov_s(int corr, scl_imax_t i) {
	int ret;
	short signed r;
	scl_umax_t u;
	r = -2;

	ret = scl_s_mov(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	if (i >= 0) {
		u = (scl_umax_t)i;

		ret = scl_s_mov_u(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));

		ret = SCL_MOV(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));
	}
}

static void mov_us_u(int corr, scl_umax_t i) {
	int ret;
	unsigned short r;
	scl_imax_t s;

	r = 23;

	ret = scl_us_mov_u(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	s = -(scl_imax_t)(i / 2);

	ret = scl_us_mov(&r, s);
	SCL_ASSERT(ret == (s == 0));

	ret = SCL_MOV(&r, s);
	SCL_ASSERT(ret == (s == 0));
}

static void mov(int corr, scl_imax_t i) {
	int ret;
	int r;
	scl_umax_t u;

	r = -2;

	ret = scl_mov(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	if (i >= 0) {
		u = (scl_umax_t)i;

		ret = scl_mov_u(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));

		ret = SCL_MOV(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));
	}
}

static void mov_u_u(int corr, scl_umax_t i) {
	int ret;
	unsigned r;
	scl_imax_t s;

	r = 23;

	ret = scl_u_mov_u(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	s = -(scl_imax_t)(i / 2);

	ret = scl_u_mov(&r, s);
	SCL_ASSERT(ret == (s == 0));

	ret = SCL_MOV(&r, s);
	SCL_ASSERT(ret == (s == 0));
}

static void mov_l(int corr, scl_imax_t i) {
	int ret;
	long r;
	scl_umax_t u;

	r = -2;

	ret = scl_l_mov(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	if (i >= 0) {
		u = (scl_umax_t)i;

		ret = scl_l_mov_u(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));

		ret = SCL_MOV(&r, u);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));
	}
}

static void mov_ul_u(int corr, scl_umax_t i) {
	int ret;
	unsigned long r;
	scl_imax_t s;

	r = 23;

	ret = scl_ul_mov_u(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == i));

	s = -(scl_imax_t)(i / 2);

	ret = scl_ul_mov(&r, s);
	SCL_ASSERT(ret == (s == 0));

	ret = SCL_MOV(&r, s);
	SCL_ASSERT(ret == (s == 0));
}

static void mov_imax_u(int corr, scl_umax_t i) {
	int ret;
	scl_imax_t r;

	r = -1;

	ret = scl_imax_mov_u(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == (scl_imax_t)i));


	ret = SCL_MOV(&r, i);
	SCL_ASSERT(ret == corr);
	SCL_ASSERT(!ret || (r == (scl_imax_t)i));
}

static void mov_ll(int corr, scl_imax_t i) {
#	if SCL_USE_LONG_LONG
		int ret;
		long long r;
		scl_umax_t u;

		r = -2;

		ret = SCL_MOV(&r, i);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));

		if (i >= 0) {
			u = (scl_umax_t)i;

			ret = SCL_MOV(&r, u);
			SCL_ASSERT(ret == corr);
			SCL_ASSERT(!ret || (r == i));
		}
#	else
		(void)corr; (void)i;
#	endif
}

static void mov_ull_u(int corr, scl_umax_t i) {
#	if SCL_USE_LONG_LONG
		int ret;
		unsigned long long r;
		scl_imax_t s;

		r = 23;

		ret = SCL_MOV(&r, i);
		SCL_ASSERT(ret == corr);
		SCL_ASSERT(!ret || (r == i));

		s = -(scl_imax_t)(i / 2);

		ret = scl_ull_mov(&r, s);
		SCL_ASSERT(ret == (s == 0));

		ret = SCL_MOV(&r, s);
		SCL_ASSERT(ret == (s == 0));
#	else
		(void)corr; (void)i;
#	endif
}

static void movs(void) {
	mov_c(1, SCHAR_MAX);
	mov_c(0, SCHAR_MAX + 1);
	mov_c(1, SCHAR_MIN);
	mov_c(0, SCHAR_MIN - 1);
	mov_c(1, 0);

	mov_uc_u(1, 0);
	mov_uc_u(1, UCHAR_MAX);
	mov_uc_u(0, UCHAR_MAX + 1);

	mov_s(1, SHRT_MAX);
	mov_s(0, SHRT_MAX + 1);
	mov_s(1, SHRT_MIN);
	mov_s(0, SHRT_MIN - 1);
	mov_s(1, 0);

	mov_us_u(1, 0);
	mov_us_u(1, USHRT_MAX);
	mov_us_u(0, USHRT_MAX + 1);

	mov(1, INT_MAX);
	mov(1, INT_MIN);
	mov(1, 0);

	mov_u_u(1, 0);
	mov_u_u(1, UINT_MAX);
	if (sizeof(int) < sizeof(scl_imax_t)) {
		mov(0, (scl_imax_t)INT_MAX + (sizeof(int) < sizeof(scl_imax_t)));
		mov(0, (scl_imax_t)INT_MIN - (sizeof(int) < sizeof(scl_imax_t)));

		mov_u_u(0, (scl_imax_t)UINT_MAX + 1);
	}

	mov_l(1, LONG_MAX);
	mov_l(1, LONG_MIN);
	mov_l(1, 0);

	mov_ul_u(1, 0);
	mov_ul_u(1, ULONG_MAX);
	if (sizeof(long) < sizeof(scl_imax_t)) {
		mov_l(0, (scl_imax_t)LONG_MAX + (sizeof(long) < sizeof(scl_imax_t)));
		mov_l(0, (scl_imax_t)LONG_MIN - (sizeof(long) < sizeof(scl_imax_t)));

		mov_ul_u(0, (scl_imax_t)ULONG_MAX + 1);
	}

	mov_ll(1, SCL_IMAX_MAX);
	mov_ll(1, SCL_IMAX_MIN);
	mov_ll(1, 0);

	mov_ull_u(1, 0);
	mov_ull_u(1, ULONG_MAX);

	mov_imax_u(1, (scl_umax_t)SCL_IMAX_MAX);
	mov_imax_u(0, (scl_umax_t)SCL_IMAX_MAX + 1);
	mov_imax_u(0, SCL_UMAX_MAX);
	mov_imax_u(1, 0);
}

#if SCL_USE_LONG_LONG
	static void check_ulladd(unsigned long long a, unsigned long long b, int/*bool*/ overflow) {
		int/*bool*/ ov;
		unsigned long long res = 0;

		ov = !scl_add_ull(&res, a, b);
		SCL_ASSERT((ov == overflow) && (overflow || (res == (a + b))));

		ov = !scl_add_ull(&res, b, a);
		SCL_ASSERT((ov == overflow) && (overflow || (res == (b + a))));
	}
	static void ulladd(void) {
		check_ulladd(ULLONG_MAX, 0, 0/*false*/);
		check_ulladd(ULLONG_MAX, 1, 1/*true*/);
		check_ulladd(ULLONG_MAX / 2 + 1, ULLONG_MAX / 2, 0/*false*/);
		check_ulladd(ULLONG_MAX / 2 + 1, ULLONG_MAX / 2 + 1, 1/*true*/);
		check_ulladd(34986ul, 129834ul, 0/*false*/);
	}
	static void check_slladd(long long a, long long b, int/*bool*/ overflow) {
		int/*bool*/ ov;
		long long res = 0;

		ov = !scl_add_ll(&res, a, b);
		SCL_ASSERT((ov == overflow) && (ov || (res == (a + b))));

		ov = !scl_add_ll(&res, b, a);
		SCL_ASSERT((ov == overflow) && (ov || (res == (b + a))));
	}
	static void slladd(void) {
		check_slladd(LLONG_MIN, 0, 0/*false*/);
		check_slladd(LLONG_MIN + 876, -876, 0/*false*/);
		check_slladd(LLONG_MIN + 9234, -876, 0/*false*/);
		check_slladd(LLONG_MIN + 876, -877, 1/*true*/);
		check_slladd(LLONG_MIN / 2, LLONG_MIN / 2 - 1, 1/*true*/);

		check_slladd(LLONG_MAX, 0, 0/*false*/);
		check_slladd(LLONG_MAX - 1876, 1876, 0/*false*/);
		check_slladd(LLONG_MAX - 19234, 1876, 0/*false*/);
		check_slladd(LLONG_MAX - 2876, 2877, 1/*true*/);
		check_slladd(LLONG_MAX / 2, LLONG_MAX / 2 + 2, 1/*true*/);
	}

	static void check_llabs(long long v, int overflow) {
		int ov;
		long long res, inv;
		res = 0; inv = 0;

		ov = scl_abs_ll(&res, v);
		SCL_ASSERT((ov == overflow) && (!ov || (res == ((v < 0) ? -v : v))));

		if (ov) {
			ov = scl_abs_ll(&inv, res);
			SCL_ASSERT(ov && (res == inv));
		}

		ov = SCL_ABS(&res, v);
		SCL_ASSERT((ov == overflow) && (!ov || (res == ((v < 0) ? -v : v))));

		if (ov) {
			ov = SCL_ABS(&inv, res);
			SCL_ASSERT(ov && (res == inv));
		}
	}

	static void s_llabs(void) {
		check_llabs(-1, 1);
		check_llabs(0, 1);
		check_llabs(LLONG_MIN, 0);
		check_llabs(LLONG_MIN + 1, 1);
		check_llabs(LLONG_MAX, 1);
		check_llabs(-65574834, 1);
		check_llabs(817644263, 1);
	}

	static void check_llneg(long long v, int overflow) {
		int ov;
		long long res, inv;
		res = 0; inv = 0;

		ov = scl_neg_ll(&res, v);
		SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

		if (ov) {
			ov = scl_neg_ll(&inv, res);
			SCL_ASSERT(ov && (v == inv));
		}

		ov = SCL_NEG(&res, v);
		SCL_ASSERT((ov == overflow) && (!ov || (res == -v)));

		if (ov) {
			ov = SCL_NEG(&inv, res);
			SCL_ASSERT(ov && (v == inv));
		}
	}

	static void llneg(void) {
		check_llneg(-1, 1);
		check_llneg(0, 1);
		check_llneg(LLONG_MIN, 0);
		check_llneg(LLONG_MIN + 1, 1);
		check_llneg(LLONG_MAX, 1);
		check_llneg(-79025748, 1);
		check_llneg(3176342, 1);
	}

	static void ADD_LL(void) {
		long long res;
		unsigned long long resull;

		CHECK_ADD(LLONG_MIN, 0, &res, 0/*false*/);
		CHECK_ADD(LLONG_MIN + 876, -876, &res, 0/*false*/);
		CHECK_ADD(LLONG_MIN + 9234, -876, &res, 0/*false*/);
		CHECK_ADD(LLONG_MIN + 876, -877, &res, 1/*true*/);
		CHECK_ADD(LLONG_MIN / 2, LLONG_MIN / 2 - 1, &res, 1/*true*/);

		CHECK_ADD(LLONG_MAX, 0, &res, 0/*false*/);
		CHECK_ADD(LLONG_MAX - 1876, 1876, &res, 0/*false*/);
		CHECK_ADD(LLONG_MAX - 19234, 1876, &res, 0/*false*/);
		CHECK_ADD(LLONG_MAX - 2876, 2877, &res, 1/*true*/);
		CHECK_ADD(LLONG_MAX / 2, LLONG_MAX / 2 + 2, &res, 1/*true*/);

		CHECK_ADD(LLONG_MIN, LLONG_MAX, &res, 0/*false*/);

		CHECK_UADD(ULLONG_MAX, 0, &resull, 0/*false*/);
		CHECK_UADD(ULLONG_MAX, 1, &resull, 1/*true*/);
		CHECK_UADD(ULLONG_MAX / 2 + 1, ULLONG_MAX / 2, &resull, 0/*false*/);
		CHECK_UADD(ULLONG_MAX / 2 + 1, ULLONG_MAX / 2 + 1, &resull, 1/*true*/);
		CHECK_UADD(34986ull, 129834ull, &resull, 0/*false*/);
	}


	static void SUB_LL(void) {
		long long ll[2];
		unsigned long long ull[2];

		CHECK_SUB(LLONG_MAX, 0, ll, 0, 0);
		CHECK_SUB(LLONG_MIN, 0, ll, 0, 1);
		CHECK_SUB(LLONG_MAX, LLONG_MAX, ll, 0, 0);
		CHECK_SUB(LLONG_MIN, LLONG_MIN, ll, 0, 0);
		CHECK_SUB(LLONG_MIN, LLONG_MAX, ll, 1, 1);
		CHECK_SUB(LLONG_MAX, LLONG_MAX - 1, ll, 0, 0);
		CHECK_SUB(LLONG_MIN, LLONG_MIN + 1, ll, 0, 0);
		CHECK_SUB(LLONG_MAX / 2 - 1, -(LLONG_MAX / 2 + 1), ll, 0, 0);
		CHECK_SUB(LLONG_MAX / 2, -(LLONG_MAX / 2 + 1), ll, 0, 0);
		CHECK_SUB(LLONG_MAX / 2 + 1, -(LLONG_MAX / 2 + 1), ll, 1, 0);
		CHECK_SUB(353498, 7132983, ll, 0, 0);
		CHECK_SUB(484328, 5234376, ll, 0, 0);

		CHECK_USUB(ULLONG_MAX, 0, ull, 0, 1);
		CHECK_USUB(0, 0, ull, 0, 0);
		CHECK_USUB(ULLONG_MAX, ULLONG_MAX, ull, 0, 0);
		CHECK_USUB(ULLONG_MAX, ULLONG_MAX - 1, ull, 0, 1);
		CHECK_USUB(ULLONG_MAX / 2 - 1, ULLONG_MAX / 2 + 1, ull, 1, 0);
		CHECK_USUB(3498, 12983, ull, 1, 0);
		CHECK_USUB(4328, 23376, ull, 1, 0);
	}

	static void check_ullmult(unsigned long long a, unsigned long long b, int/*bool*/ overflow) {
		unsigned long long res = 0;
		int/*bool*/ ov;

		ov = !scl_mult_ull(&res, a, b);
		SCL_ASSERT((ov == overflow) && (ov || ((a * b) == res)));

		ov = !scl_mult_ull(&res, b, a);
		SCL_ASSERT((ov == overflow) && (ov || ((b * a) == res)));

		CHECK_MULT(a, b, &res, overflow);
	}
	static void ullmult(void) {
		unsigned long long ull;

		check_ullmult(ULLONG_MAX, 1, 0/*false*/);
		check_ullmult(ULLONG_MAX / 5, 5, 0/*false*/);
		check_ullmult(ULLONG_MAX, 0, 0/*false*/);
		check_ullmult(ULLONG_MAX / 2, 0, 0/*false*/);
		check_ullmult(327, 78, 0/*false*/);

		check_ullmult(ULLONG_MAX / 2, ULLONG_MAX / 3, 1/*true*/);
		check_ullmult(1ull << ((int)sizeof(unsigned long long) * 4), 1ull << ((int)sizeof(unsigned long long) * 4), 1/*true*/);
		check_ullmult((1ull << ((int)sizeof(unsigned long long) * 4)) - 1, 1ull << ((int)sizeof(unsigned long long) * 4), 0/*false*/);

		CHECK_MULT(SCL_IMAX_MAX, 0, &ull, 0);
		CHECK_MULT(SCL_UMAX_MAX, 0u, &ull, 0);
		CHECK_MULT(-1, 1, &ull, 1);
		CHECK_MULT(-1, -1, &ull, 0);
		CHECK_MULT((scl_umax_t)SCL_IMAX_MAX, 1, &ull, LLONG_MAX < SCL_IMAX_MAX);
		CHECK_MULT(SCL_IMAX_MIN, 1, &ull, 1);
	}
#endif

static void inner_use_reverse_mov(void) {
	char signed c;
	char unsigned cu;
	short s;
	short unsigned su;
	scl_imax_t im;
	int ret;

	c = SCHAR_MAX;
	ret = scl_inner_use_reverse_mov(&c, SCL_GET_ITID_SIDE_EFFECT(c), &im);
	SCL_ASSERT(ret && (im == c));

	c = SCHAR_MIN;
	ret = scl_inner_use_reverse_mov(&c, SCL_GET_ITID_SIDE_EFFECT(c), &im);
	SCL_ASSERT(ret && (im == c));

	c = 0;
	ret = scl_inner_use_reverse_mov(&c, SCL_GET_ITID_SIDE_EFFECT(c), &im);
	SCL_ASSERT(ret && (im == c));

	cu = UCHAR_MAX;
	ret = scl_inner_use_reverse_mov(&cu, SCL_GET_ITID_SIDE_EFFECT(cu), &im);
	SCL_ASSERT(ret && (im == cu));

	cu = (char unsigned)(SCHAR_MAX + 1);
	ret = scl_inner_use_reverse_mov(&cu, SCL_GET_ITID_SIDE_EFFECT(cu), &im);
	SCL_ASSERT(ret && (im == cu));

	cu = 0;
	ret = scl_inner_use_reverse_mov(&cu, SCL_GET_ITID_SIDE_EFFECT(cu), &im);
	SCL_ASSERT(ret && (im == cu));


	s = SHRT_MAX;
	ret = scl_inner_use_reverse_mov(&s, SCL_GET_ITID_SIDE_EFFECT(s), &im);
	SCL_ASSERT(ret && (im == s));

	s = SHRT_MIN;
	ret = scl_inner_use_reverse_mov(&s, SCL_GET_ITID_SIDE_EFFECT(s), &im);
	SCL_ASSERT(ret && (im == s));

	s = 0;
	ret = scl_inner_use_reverse_mov(&s, SCL_GET_ITID_SIDE_EFFECT(s), &im);
	SCL_ASSERT(ret && (im == s));

	su = USHRT_MAX;
	ret = scl_inner_use_reverse_mov(&su, SCL_GET_ITID_SIDE_EFFECT(su), &im);
	SCL_ASSERT(ret && (im == su));

	su = (char unsigned)(SHRT_MAX + 1);
	ret = scl_inner_use_reverse_mov(&su, SCL_GET_ITID_SIDE_EFFECT(su), &im);
	SCL_ASSERT(ret && (im == su));

	su = 0;
	ret = scl_inner_use_reverse_mov(&su, SCL_GET_ITID_SIDE_EFFECT(su), &im);
	SCL_ASSERT(ret && (im == su));
}

static void inner_use_reverse_mov_u(void) {
	char signed c;
	char unsigned cu;
	short s;
	short unsigned su;
	int i;
	unsigned u;
	long l;
	long unsigned lu;
	scl_umax_t im;
	int ret;
	int id;

	id = (int)SCL_GET_ITID_SIDE_EFFECT(c);
	c = SCHAR_MAX;
	/*printf("1 %ld & %d sizeof(im) = %lu\n", im, c, sizeof(im));*/
	ret = scl_inner_use_reverse_mov_u(&c, id, &im);
	/*printf("2 %ld & %d id = %d\n", im, (int)c, ret);*/
	SCL_ASSERT(ret && (im == (scl_umax_t)c));

	c = SCHAR_MIN;
	ret = scl_inner_use_reverse_mov_u(&c, id, &im);
	SCL_ASSERT(!ret);

	c = 0;
	ret = scl_inner_use_reverse_mov_u(&c, id, &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)c));

	id = (int)SCL_GET_ITID_SIDE_EFFECT(cu);
	cu = UCHAR_MAX;
	ret = scl_inner_use_reverse_mov_u(&cu, id, &im);
	SCL_ASSERT(ret && (im == cu));

	cu = (char unsigned)(SCHAR_MAX + 1);
	ret = scl_inner_use_reverse_mov_u(&cu, id, &im);
	SCL_ASSERT(ret && (im == cu));

	cu = 0;
	ret = scl_inner_use_reverse_mov_u(&cu, id, &im);
	SCL_ASSERT(ret && (im == cu));

	id = (int)SCL_GET_ITID_SIDE_EFFECT(s);
	s = SHRT_MAX;
	ret = scl_inner_use_reverse_mov_u(&s, id, &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)s));

	s = SHRT_MIN;
	ret = scl_inner_use_reverse_mov_u(&s, id, &im);
	SCL_ASSERT(!ret);

	s = 0;
	ret = scl_inner_use_reverse_mov_u(&s, id, &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)s));

	id = (int)SCL_GET_ITID_SIDE_EFFECT(su);
	su = USHRT_MAX;
	ret = scl_inner_use_reverse_mov_u(&su, id, &im);
	SCL_ASSERT(ret && (im == su));

	su = (short unsigned)(SHRT_MAX + 1);
	ret = scl_inner_use_reverse_mov_u(&su, id, &im);
	SCL_ASSERT(ret && (im == su));

	su = 0;
	ret = scl_inner_use_reverse_mov_u(&su, id, &im);
	SCL_ASSERT(ret && (im == su));

	i = INT_MAX;
	ret = scl_inner_use_reverse_mov_u(&i, SCL_GET_ITID_SIDE_EFFECT(i), &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)i));

	i = INT_MIN;
	ret = scl_inner_use_reverse_mov_u(&i, SCL_GET_ITID_SIDE_EFFECT(i), &im);
	SCL_ASSERT(!ret);

	i = 0;
	ret = scl_inner_use_reverse_mov_u(&i, SCL_GET_ITID_SIDE_EFFECT(i), &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)i));

	u = UINT_MAX;
	ret = scl_inner_use_reverse_mov_u(&u, SCL_GET_ITID_SIDE_EFFECT(u), &im);
	SCL_ASSERT(ret && (im == u));

	u = (unsigned)(INT_MAX) + 1;
	ret = scl_inner_use_reverse_mov_u(&u, SCL_GET_ITID_SIDE_EFFECT(u), &im);
	SCL_ASSERT(ret && (im == u));

	u = 0;
	ret = scl_inner_use_reverse_mov_u(&u, SCL_GET_ITID_SIDE_EFFECT(u), &im);
	SCL_ASSERT(ret && (im == u));

	l = LONG_MAX;
	ret = scl_inner_use_reverse_mov_u(&l, SCL_GET_ITID_SIDE_EFFECT(l), &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)l));

	l = LONG_MIN;
	ret = scl_inner_use_reverse_mov_u(&l, SCL_GET_ITID_SIDE_EFFECT(l), &im);
	SCL_ASSERT(!ret);

	l = 0;
	ret = scl_inner_use_reverse_mov_u(&l, SCL_GET_ITID_SIDE_EFFECT(l), &im);
	SCL_ASSERT(ret && (im == (scl_umax_t)l));

	lu = UINT_MAX;
	ret = scl_inner_use_reverse_mov_u(&lu, SCL_GET_ITID_SIDE_EFFECT(lu), &im);
	SCL_ASSERT(ret && (im == lu));

	lu = (long unsigned)(LONG_MAX) + 1;
	ret = scl_inner_use_reverse_mov_u(&lu, SCL_GET_ITID_SIDE_EFFECT(lu), &im);
	SCL_ASSERT(ret && (im == lu));

	lu = 0;
	ret = scl_inner_use_reverse_mov_u(&lu, SCL_GET_ITID_SIDE_EFFECT(lu), &im);
	SCL_ASSERT(ret && (im == lu));

#	if SCL_USE_LONG_LONG
	{
		long long ll;
		ll = LLONG_MAX;
		ret = scl_inner_use_reverse_mov_u(&ll, SCL_GET_ITID_SIDE_EFFECT(ll), &im);
		SCL_ASSERT(ret && (im == (long long unsigned)ll));

		ll = LONG_MIN;
		ret = scl_inner_use_reverse_mov_u(&ll, SCL_GET_ITID_SIDE_EFFECT(ll), &im);
		SCL_ASSERT(!ret);

		ll = 0;
		ret = scl_inner_use_reverse_mov_u(&ll, SCL_GET_ITID_SIDE_EFFECT(ll), &im);
		SCL_ASSERT(ret && (im == (long long unsigned)ll));
	}
#	endif
}

static void inner_use_oper(void) {
	scl_imax_t im;
	int ret;
	scl_oper_t op;
	scl_mov_t mov;

	ret = scl_inner_use_oper(&im, 0, SCL_OPER_MOD, 0);
	SCL_ASSERT(!ret);
	ret = scl_inner_use_oper(&im, 1, SCL_OPER_MOD, 0);
	SCL_ASSERT(!ret);
	ret = scl_inner_use_oper(&im, 1, SCL_OPER_MOD, 1);
	SCL_ASSERT(ret && (im == 0));

	op = scl_inner_use_get_oper_id("&");
	SCL_ASSERT(SCL_OPER_WRONG == op);
	op = scl_inner_use_get_oper_id("&&");
	SCL_ASSERT(SCL_OPER_WRONG == op);

	mov = scl_inner_use_get_move_id("&");
	SCL_ASSERT(SCL_MOV_WRONG == mov);
	mov = scl_inner_use_get_move_id("&&");
	SCL_ASSERT(SCL_MOV_WRONG == mov);
	mov = scl_inner_use_get_move_id("&=");
	SCL_ASSERT(SCL_MOV_WRONG == mov);

	mov = scl_inner_use_get_move_id("%=");
	SCL_ASSERT(SCL_MOV_MOD == mov);
}

static void inner_use_check(void) {
	inner_use_reverse_mov();
	inner_use_reverse_mov_u();
	inner_use_oper();
}

static void do_tests(void) {
	uadd();
	uladd();
	sadd();
	sladd();
	size_add();
	ADD();

	usub();
	ulsub();
	ssub();
	slsub();
	size_sub();
	SUB();

	umult();
	ulmult();
	smult();
	slmult();
	size_mult();

	sdiv();
	udiv();
	sldiv();
	uldiv();
	size_div();

	s_abs();
	s_labs();
	imax_abs();

	neg();
	lneg();
	imax_neg();

	movs();

	inner_use_check();

#	if SCL_USE_LONG_LONG
		ulladd();
		slladd();
		ADD_LL();

		ullsub();
		sllsub();
		SUB_LL();

		ullmult();
		sllmult();

		slldiv();
		ulldiv();

		combll_div();

		s_llabs();

		llneg();
#	endif
}

#if defined(START_FUNC) && (START_FUNC == 1)
#	undef START_FUNC
#	define START_FUNC test_overflow
#endif

#if defined(START_FUNC)
	extern void START_FUNC(void) {
		do_tests();
		scl_gnuc_builtin_overflow = 1 - 0;
		do_tests();
	}
#else

#	include <stdio.h>

	extern int main(int argc, char const **argv) {
		do_tests();
		scl_gnuc_builtin_overflow = 1 - 0;
		do_tests();
		puts("overflow finish");
		return 0;
	}
#endif

