Add functions gcd() and lcm() for integer and numeric types.
These compute the greatest common divisor and least common multiple of a pair of numbers using the Euclidean algorithm. Vik Fearing, reviewed by Fabien Coelho. Discussion: https://postgr.es/m/adbd3e0b-e3f1-5bbc-21db-03caf1cef0f7@2ndquadrant.com
This commit is contained in:
parent
530609aa42
commit
13661ddd7e
@ -870,6 +870,40 @@
|
||||
<entry><literal>-43</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>gcd</primary>
|
||||
</indexterm>
|
||||
<literal><function>gcd(<replaceable>a</replaceable>, <replaceable>b</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>(same as argument types)</entry>
|
||||
<entry>
|
||||
greatest common divisor (the largest positive number that divides both
|
||||
inputs with no remainder); returns <literal>0</literal> if both inputs
|
||||
are zero
|
||||
</entry>
|
||||
<entry><literal>gcd(1071, 462)</literal></entry>
|
||||
<entry><literal>21</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>lcm</primary>
|
||||
</indexterm>
|
||||
<literal><function>lcm(<replaceable>a</replaceable>, <replaceable>b</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>(same as argument types)</entry>
|
||||
<entry>
|
||||
least common multiple (the smallest strictly positive number that is
|
||||
an integral multiple of both inputs); returns <literal>0</literal> if
|
||||
either input is zero
|
||||
</entry>
|
||||
<entry><literal>lcm(1071, 462)</literal></entry>
|
||||
<entry><literal>23562</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
|
@ -1196,6 +1196,132 @@ int2abs(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INT16(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Greatest Common Divisor
|
||||
*
|
||||
* Returns the largest positive integer that exactly divides both inputs.
|
||||
* Special cases:
|
||||
* - gcd(x, 0) = gcd(0, x) = abs(x)
|
||||
* because 0 is divisible by anything
|
||||
* - gcd(0, 0) = 0
|
||||
* complies with the previous definition and is a common convention
|
||||
*
|
||||
* Special care must be taken if either input is INT_MIN --- gcd(0, INT_MIN),
|
||||
* gcd(INT_MIN, 0) and gcd(INT_MIN, INT_MIN) are all equal to abs(INT_MIN),
|
||||
* which cannot be represented as a 32-bit signed integer.
|
||||
*/
|
||||
static int32
|
||||
int4gcd_internal(int32 arg1, int32 arg2)
|
||||
{
|
||||
int32 swap;
|
||||
int32 a1, a2;
|
||||
|
||||
/*
|
||||
* Put the greater absolute value in arg1.
|
||||
*
|
||||
* This would happen automatically in the loop below, but avoids an
|
||||
* expensive modulo operation, and simplifies the special-case handling
|
||||
* for INT_MIN below.
|
||||
*
|
||||
* We do this in negative space in order to handle INT_MIN.
|
||||
*/
|
||||
a1 = (arg1 < 0) ? arg1 : -arg1;
|
||||
a2 = (arg2 < 0) ? arg2 : -arg2;
|
||||
if (a1 > a2)
|
||||
{
|
||||
swap = arg1;
|
||||
arg1 = arg2;
|
||||
arg2 = swap;
|
||||
}
|
||||
|
||||
/* Special care needs to be taken with INT_MIN. See comments above. */
|
||||
if (arg1 == PG_INT32_MIN)
|
||||
{
|
||||
if (arg2 == 0 || arg2 == PG_INT32_MIN)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("integer out of range")));
|
||||
|
||||
/*
|
||||
* Some machines throw a floating-point exception for INT_MIN % -1,
|
||||
* which is a bit silly since the correct answer is perfectly
|
||||
* well-defined, namely zero. Guard against this and just return the
|
||||
* result, gcd(INT_MIN, -1) = 1.
|
||||
*/
|
||||
if (arg2 == -1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Use the Euclidean algorithm to find the GCD */
|
||||
while (arg2 != 0)
|
||||
{
|
||||
swap = arg2;
|
||||
arg2 = arg1 % arg2;
|
||||
arg1 = swap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the result is positive. (We know we don't have INT_MIN
|
||||
* anymore).
|
||||
*/
|
||||
if (arg1 < 0)
|
||||
arg1 = -arg1;
|
||||
|
||||
return arg1;
|
||||
}
|
||||
|
||||
Datum
|
||||
int4gcd(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 arg1 = PG_GETARG_INT32(0);
|
||||
int32 arg2 = PG_GETARG_INT32(1);
|
||||
int32 result;
|
||||
|
||||
result = int4gcd_internal(arg1, arg2);
|
||||
|
||||
PG_RETURN_INT32(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Least Common Multiple
|
||||
*/
|
||||
Datum
|
||||
int4lcm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 arg1 = PG_GETARG_INT32(0);
|
||||
int32 arg2 = PG_GETARG_INT32(1);
|
||||
int32 gcd;
|
||||
int32 result;
|
||||
|
||||
/*
|
||||
* Handle lcm(x, 0) = lcm(0, x) = 0 as a special case. This prevents a
|
||||
* division-by-zero error below when x is zero, and an overflow error from
|
||||
* the GCD computation when x = INT_MIN.
|
||||
*/
|
||||
if (arg1 == 0 || arg2 == 0)
|
||||
PG_RETURN_INT32(0);
|
||||
|
||||
/* lcm(x, y) = abs(x / gcd(x, y) * y) */
|
||||
gcd = int4gcd_internal(arg1, arg2);
|
||||
arg1 = arg1 / gcd;
|
||||
|
||||
if (unlikely(pg_mul_s32_overflow(arg1, arg2, &result)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("integer out of range")));
|
||||
|
||||
/* If the result is INT_MIN, it cannot be represented. */
|
||||
if (unlikely(result == PG_INT32_MIN))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("integer out of range")));
|
||||
|
||||
if (result < 0)
|
||||
result = -result;
|
||||
|
||||
PG_RETURN_INT32(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
int2larger(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
@ -667,6 +667,132 @@ int8mod(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INT64(arg1 % arg2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Greatest Common Divisor
|
||||
*
|
||||
* Returns the largest positive integer that exactly divides both inputs.
|
||||
* Special cases:
|
||||
* - gcd(x, 0) = gcd(0, x) = abs(x)
|
||||
* because 0 is divisible by anything
|
||||
* - gcd(0, 0) = 0
|
||||
* complies with the previous definition and is a common convention
|
||||
*
|
||||
* Special care must be taken if either input is INT64_MIN ---
|
||||
* gcd(0, INT64_MIN), gcd(INT64_MIN, 0) and gcd(INT64_MIN, INT64_MIN) are
|
||||
* all equal to abs(INT64_MIN), which cannot be represented as a 64-bit signed
|
||||
* integer.
|
||||
*/
|
||||
static int64
|
||||
int8gcd_internal(int64 arg1, int64 arg2)
|
||||
{
|
||||
int64 swap;
|
||||
int64 a1, a2;
|
||||
|
||||
/*
|
||||
* Put the greater absolute value in arg1.
|
||||
*
|
||||
* This would happen automatically in the loop below, but avoids an
|
||||
* expensive modulo operation, and simplifies the special-case handling
|
||||
* for INT64_MIN below.
|
||||
*
|
||||
* We do this in negative space in order to handle INT64_MIN.
|
||||
*/
|
||||
a1 = (arg1 < 0) ? arg1 : -arg1;
|
||||
a2 = (arg2 < 0) ? arg2 : -arg2;
|
||||
if (a1 > a2)
|
||||
{
|
||||
swap = arg1;
|
||||
arg1 = arg2;
|
||||
arg2 = swap;
|
||||
}
|
||||
|
||||
/* Special care needs to be taken with INT64_MIN. See comments above. */
|
||||
if (arg1 == PG_INT64_MIN)
|
||||
{
|
||||
if (arg2 == 0 || arg2 == PG_INT64_MIN)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
|
||||
/*
|
||||
* Some machines throw a floating-point exception for INT64_MIN % -1,
|
||||
* which is a bit silly since the correct answer is perfectly
|
||||
* well-defined, namely zero. Guard against this and just return the
|
||||
* result, gcd(INT64_MIN, -1) = 1.
|
||||
*/
|
||||
if (arg2 == -1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Use the Euclidean algorithm to find the GCD */
|
||||
while (arg2 != 0)
|
||||
{
|
||||
swap = arg2;
|
||||
arg2 = arg1 % arg2;
|
||||
arg1 = swap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the result is positive. (We know we don't have INT64_MIN
|
||||
* anymore).
|
||||
*/
|
||||
if (arg1 < 0)
|
||||
arg1 = -arg1;
|
||||
|
||||
return arg1;
|
||||
}
|
||||
|
||||
Datum
|
||||
int8gcd(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 arg1 = PG_GETARG_INT64(0);
|
||||
int64 arg2 = PG_GETARG_INT64(1);
|
||||
int64 result;
|
||||
|
||||
result = int8gcd_internal(arg1, arg2);
|
||||
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Least Common Multiple
|
||||
*/
|
||||
Datum
|
||||
int8lcm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 arg1 = PG_GETARG_INT64(0);
|
||||
int64 arg2 = PG_GETARG_INT64(1);
|
||||
int64 gcd;
|
||||
int64 result;
|
||||
|
||||
/*
|
||||
* Handle lcm(x, 0) = lcm(0, x) = 0 as a special case. This prevents a
|
||||
* division-by-zero error below when x is zero, and an overflow error from
|
||||
* the GCD computation when x = INT64_MIN.
|
||||
*/
|
||||
if (arg1 == 0 || arg2 == 0)
|
||||
PG_RETURN_INT64(0);
|
||||
|
||||
/* lcm(x, y) = abs(x / gcd(x, y) * y) */
|
||||
gcd = int8gcd_internal(arg1, arg2);
|
||||
arg1 = arg1 / gcd;
|
||||
|
||||
if (unlikely(pg_mul_s64_overflow(arg1, arg2, &result)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
|
||||
/* If the result is INT64_MIN, it cannot be represented. */
|
||||
if (unlikely(result == PG_INT64_MIN))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("bigint out of range")));
|
||||
|
||||
if (result < 0)
|
||||
result = -result;
|
||||
|
||||
PG_RETURN_INT64(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
int8inc(PG_FUNCTION_ARGS)
|
||||
|
@ -521,6 +521,8 @@ static void mod_var(const NumericVar *var1, const NumericVar *var2,
|
||||
static void ceil_var(const NumericVar *var, NumericVar *result);
|
||||
static void floor_var(const NumericVar *var, NumericVar *result);
|
||||
|
||||
static void gcd_var(const NumericVar *var1, const NumericVar *var2,
|
||||
NumericVar *result);
|
||||
static void sqrt_var(const NumericVar *arg, NumericVar *result, int rscale);
|
||||
static void exp_var(const NumericVar *arg, NumericVar *result, int rscale);
|
||||
static int estimate_ln_dweight(const NumericVar *var);
|
||||
@ -2838,6 +2840,107 @@ numeric_larger(PG_FUNCTION_ARGS)
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* numeric_gcd() -
|
||||
*
|
||||
* Calculate the greatest common divisor of two numerics
|
||||
*/
|
||||
Datum
|
||||
numeric_gcd(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
Numeric res;
|
||||
|
||||
/*
|
||||
* Handle NaN
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
|
||||
/*
|
||||
* Unpack the arguments
|
||||
*/
|
||||
init_var_from_num(num1, &arg1);
|
||||
init_var_from_num(num2, &arg2);
|
||||
|
||||
init_var(&result);
|
||||
|
||||
/*
|
||||
* Find the GCD and return the result
|
||||
*/
|
||||
gcd_var(&arg1, &arg2, &result);
|
||||
|
||||
res = make_result(&result);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* numeric_lcm() -
|
||||
*
|
||||
* Calculate the least common multiple of two numerics
|
||||
*/
|
||||
Datum
|
||||
numeric_lcm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
Numeric res;
|
||||
|
||||
/*
|
||||
* Handle NaN
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
|
||||
/*
|
||||
* Unpack the arguments
|
||||
*/
|
||||
init_var_from_num(num1, &arg1);
|
||||
init_var_from_num(num2, &arg2);
|
||||
|
||||
init_var(&result);
|
||||
|
||||
/*
|
||||
* Compute the result using lcm(x, y) = abs(x / gcd(x, y) * y), returning
|
||||
* zero if either input is zero.
|
||||
*
|
||||
* Note that the division is guaranteed to be exact, returning an integer
|
||||
* result, so the LCM is an integral multiple of both x and y. A display
|
||||
* scale of Min(x.dscale, y.dscale) would be sufficient to represent it,
|
||||
* but as with other numeric functions, we choose to return a result whose
|
||||
* display scale is no smaller than either input.
|
||||
*/
|
||||
if (arg1.ndigits == 0 || arg2.ndigits == 0)
|
||||
set_var_from_var(&const_zero, &result);
|
||||
else
|
||||
{
|
||||
gcd_var(&arg1, &arg2, &result);
|
||||
div_var(&arg1, &result, &result, 0, false);
|
||||
mul_var(&arg2, &result, &result, arg2.dscale);
|
||||
result.sign = NUMERIC_POS;
|
||||
}
|
||||
|
||||
result.dscale = Max(arg1.dscale, arg2.dscale);
|
||||
|
||||
res = make_result(&result);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* numeric_fac()
|
||||
*
|
||||
@ -8039,6 +8142,74 @@ floor_var(const NumericVar *var, NumericVar *result)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gcd_var() -
|
||||
*
|
||||
* Calculate the greatest common divisor of two numerics at variable level
|
||||
*/
|
||||
static void
|
||||
gcd_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result)
|
||||
{
|
||||
int res_dscale;
|
||||
int cmp;
|
||||
NumericVar tmp_arg;
|
||||
NumericVar mod;
|
||||
|
||||
res_dscale = Max(var1->dscale, var2->dscale);
|
||||
|
||||
/*
|
||||
* Arrange for var1 to be the number with the greater absolute value.
|
||||
*
|
||||
* This would happen automatically in the loop below, but avoids an
|
||||
* expensive modulo operation.
|
||||
*/
|
||||
cmp = cmp_abs(var1, var2);
|
||||
if (cmp < 0)
|
||||
{
|
||||
const NumericVar *tmp = var1;
|
||||
|
||||
var1 = var2;
|
||||
var2 = tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Also avoid the taking the modulo if the inputs have the same absolute
|
||||
* value, or if the smaller input is zero.
|
||||
*/
|
||||
if (cmp == 0 || var2->ndigits == 0)
|
||||
{
|
||||
set_var_from_var(var1, result);
|
||||
result->sign = NUMERIC_POS;
|
||||
result->dscale = res_dscale;
|
||||
return;
|
||||
}
|
||||
|
||||
init_var(&tmp_arg);
|
||||
init_var(&mod);
|
||||
|
||||
/* Use the Euclidean algorithm to find the GCD */
|
||||
set_var_from_var(var1, &tmp_arg);
|
||||
set_var_from_var(var2, result);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* this loop can take a while, so allow it to be interrupted */
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
mod_var(&tmp_arg, result, &mod);
|
||||
if (mod.ndigits == 0)
|
||||
break;
|
||||
set_var_from_var(result, &tmp_arg);
|
||||
set_var_from_var(&mod, result);
|
||||
}
|
||||
result->sign = NUMERIC_POS;
|
||||
result->dscale = res_dscale;
|
||||
|
||||
free_var(&tmp_arg);
|
||||
free_var(&mod);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* sqrt_var() -
|
||||
*
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 202001171
|
||||
#define CATALOG_VERSION_NO 202001251
|
||||
|
||||
#endif
|
||||
|
@ -1818,6 +1818,20 @@
|
||||
proname => 'mod', prorettype => 'int8', proargtypes => 'int8 int8',
|
||||
prosrc => 'int8mod' },
|
||||
|
||||
{ oid => '8463', descr => 'greatest common divisor',
|
||||
proname => 'gcd', prorettype => 'int4', proargtypes => 'int4 int4',
|
||||
prosrc => 'int4gcd' },
|
||||
{ oid => '8464', descr => 'greatest common divisor',
|
||||
proname => 'gcd', prorettype => 'int8', proargtypes => 'int8 int8',
|
||||
prosrc => 'int8gcd' },
|
||||
|
||||
{ oid => '8465', descr => 'least common multiple',
|
||||
proname => 'lcm', prorettype => 'int4', proargtypes => 'int4 int4',
|
||||
prosrc => 'int4lcm' },
|
||||
{ oid => '8466', descr => 'least common multiple',
|
||||
proname => 'lcm', prorettype => 'int8', proargtypes => 'int8 int8',
|
||||
prosrc => 'int8lcm' },
|
||||
|
||||
{ oid => '944', descr => 'convert text to char',
|
||||
proname => 'char', prorettype => 'char', proargtypes => 'text',
|
||||
prosrc => 'text_char' },
|
||||
@ -4218,6 +4232,12 @@
|
||||
{ oid => '1729',
|
||||
proname => 'numeric_mod', prorettype => 'numeric',
|
||||
proargtypes => 'numeric numeric', prosrc => 'numeric_mod' },
|
||||
{ oid => '8467', descr => 'greatest common divisor',
|
||||
proname => 'gcd', prorettype => 'numeric', proargtypes => 'numeric numeric',
|
||||
prosrc => 'numeric_gcd' },
|
||||
{ oid => '8468', descr => 'least common multiple',
|
||||
proname => 'lcm', prorettype => 'numeric', proargtypes => 'numeric numeric',
|
||||
prosrc => 'numeric_lcm' },
|
||||
{ oid => '1730', descr => 'square root',
|
||||
proname => 'sqrt', prorettype => 'numeric', proargtypes => 'numeric',
|
||||
prosrc => 'numeric_sqrt' },
|
||||
|
@ -403,3 +403,49 @@ FROM (VALUES (-2.5::numeric),
|
||||
2.5 | 3
|
||||
(7 rows)
|
||||
|
||||
-- test gcd()
|
||||
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a)
|
||||
FROM (VALUES (0::int4, 0::int4),
|
||||
(0::int4, 6410818::int4),
|
||||
(61866666::int4, 6410818::int4),
|
||||
(-61866666::int4, 6410818::int4),
|
||||
((-2147483648)::int4, 1::int4),
|
||||
((-2147483648)::int4, 2147483647::int4),
|
||||
((-2147483648)::int4, 1073741824::int4)) AS v(a, b);
|
||||
a | b | gcd | gcd | gcd | gcd
|
||||
-------------+------------+------------+------------+------------+------------
|
||||
0 | 0 | 0 | 0 | 0 | 0
|
||||
0 | 6410818 | 6410818 | 6410818 | 6410818 | 6410818
|
||||
61866666 | 6410818 | 1466 | 1466 | 1466 | 1466
|
||||
-61866666 | 6410818 | 1466 | 1466 | 1466 | 1466
|
||||
-2147483648 | 1 | 1 | 1 | 1 | 1
|
||||
-2147483648 | 2147483647 | 1 | 1 | 1 | 1
|
||||
-2147483648 | 1073741824 | 1073741824 | 1073741824 | 1073741824 | 1073741824
|
||||
(7 rows)
|
||||
|
||||
SELECT gcd((-2147483648)::int4, 0::int4); -- overflow
|
||||
ERROR: integer out of range
|
||||
SELECT gcd((-2147483648)::int4, (-2147483648)::int4); -- overflow
|
||||
ERROR: integer out of range
|
||||
-- test lcm()
|
||||
SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a)
|
||||
FROM (VALUES (0::int4, 0::int4),
|
||||
(0::int4, 42::int4),
|
||||
(42::int4, 42::int4),
|
||||
(330::int4, 462::int4),
|
||||
(-330::int4, 462::int4),
|
||||
((-2147483648)::int4, 0::int4)) AS v(a, b);
|
||||
a | b | lcm | lcm | lcm | lcm
|
||||
-------------+-----+------+------+------+------
|
||||
0 | 0 | 0 | 0 | 0 | 0
|
||||
0 | 42 | 0 | 0 | 0 | 0
|
||||
42 | 42 | 42 | 42 | 42 | 42
|
||||
330 | 462 | 2310 | 2310 | 2310 | 2310
|
||||
-330 | 462 | 2310 | 2310 | 2310 | 2310
|
||||
-2147483648 | 0 | 0 | 0 | 0 | 0
|
||||
(6 rows)
|
||||
|
||||
SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
|
||||
ERROR: integer out of range
|
||||
SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
|
||||
ERROR: integer out of range
|
||||
|
@ -886,3 +886,49 @@ FROM (VALUES (-2.5::numeric),
|
||||
2.5 | 3
|
||||
(7 rows)
|
||||
|
||||
-- test gcd()
|
||||
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a)
|
||||
FROM (VALUES (0::int8, 0::int8),
|
||||
(0::int8, 29893644334::int8),
|
||||
(288484263558::int8, 29893644334::int8),
|
||||
(-288484263558::int8, 29893644334::int8),
|
||||
((-9223372036854775808)::int8, 1::int8),
|
||||
((-9223372036854775808)::int8, 9223372036854775807::int8),
|
||||
((-9223372036854775808)::int8, 4611686018427387904::int8)) AS v(a, b);
|
||||
a | b | gcd | gcd | gcd | gcd
|
||||
----------------------+---------------------+---------------------+---------------------+---------------------+---------------------
|
||||
0 | 0 | 0 | 0 | 0 | 0
|
||||
0 | 29893644334 | 29893644334 | 29893644334 | 29893644334 | 29893644334
|
||||
288484263558 | 29893644334 | 6835958 | 6835958 | 6835958 | 6835958
|
||||
-288484263558 | 29893644334 | 6835958 | 6835958 | 6835958 | 6835958
|
||||
-9223372036854775808 | 1 | 1 | 1 | 1 | 1
|
||||
-9223372036854775808 | 9223372036854775807 | 1 | 1 | 1 | 1
|
||||
-9223372036854775808 | 4611686018427387904 | 4611686018427387904 | 4611686018427387904 | 4611686018427387904 | 4611686018427387904
|
||||
(7 rows)
|
||||
|
||||
SELECT gcd((-9223372036854775808)::int8, 0::int8); -- overflow
|
||||
ERROR: bigint out of range
|
||||
SELECT gcd((-9223372036854775808)::int8, (-9223372036854775808)::int8); -- overflow
|
||||
ERROR: bigint out of range
|
||||
-- test lcm()
|
||||
SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a)
|
||||
FROM (VALUES (0::int8, 0::int8),
|
||||
(0::int8, 29893644334::int8),
|
||||
(29893644334::int8, 29893644334::int8),
|
||||
(288484263558::int8, 29893644334::int8),
|
||||
(-288484263558::int8, 29893644334::int8),
|
||||
((-9223372036854775808)::int8, 0::int8)) AS v(a, b);
|
||||
a | b | lcm | lcm | lcm | lcm
|
||||
----------------------+-------------+------------------+------------------+------------------+------------------
|
||||
0 | 0 | 0 | 0 | 0 | 0
|
||||
0 | 29893644334 | 0 | 0 | 0 | 0
|
||||
29893644334 | 29893644334 | 29893644334 | 29893644334 | 29893644334 | 29893644334
|
||||
288484263558 | 29893644334 | 1261541684539134 | 1261541684539134 | 1261541684539134 | 1261541684539134
|
||||
-288484263558 | 29893644334 | 1261541684539134 | 1261541684539134 | 1261541684539134 | 1261541684539134
|
||||
-9223372036854775808 | 0 | 0 | 0 | 0 | 0
|
||||
(6 rows)
|
||||
|
||||
SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow
|
||||
ERROR: bigint out of range
|
||||
SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow
|
||||
ERROR: bigint out of range
|
||||
|
@ -2220,3 +2220,47 @@ SELECT SUM((-9999)::numeric) FROM generate_series(1, 100000);
|
||||
-999900000
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Tests for GCD()
|
||||
--
|
||||
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(-b, a), gcd(-b, -a)
|
||||
FROM (VALUES (0::numeric, 0::numeric),
|
||||
(0::numeric, numeric 'NaN'),
|
||||
(0::numeric, 46375::numeric),
|
||||
(433125::numeric, 46375::numeric),
|
||||
(43312.5::numeric, 4637.5::numeric),
|
||||
(4331.250::numeric, 463.75000::numeric)) AS v(a, b);
|
||||
a | b | gcd | gcd | gcd | gcd
|
||||
----------+-----------+---------+---------+---------+---------
|
||||
0 | 0 | 0 | 0 | 0 | 0
|
||||
0 | NaN | NaN | NaN | NaN | NaN
|
||||
0 | 46375 | 46375 | 46375 | 46375 | 46375
|
||||
433125 | 46375 | 875 | 875 | 875 | 875
|
||||
43312.5 | 4637.5 | 87.5 | 87.5 | 87.5 | 87.5
|
||||
4331.250 | 463.75000 | 8.75000 | 8.75000 | 8.75000 | 8.75000
|
||||
(6 rows)
|
||||
|
||||
--
|
||||
-- Tests for LCM()
|
||||
--
|
||||
SELECT a,b, lcm(a, b), lcm(a, -b), lcm(-b, a), lcm(-b, -a)
|
||||
FROM (VALUES (0::numeric, 0::numeric),
|
||||
(0::numeric, numeric 'NaN'),
|
||||
(0::numeric, 13272::numeric),
|
||||
(13272::numeric, 13272::numeric),
|
||||
(423282::numeric, 13272::numeric),
|
||||
(42328.2::numeric, 1327.2::numeric),
|
||||
(4232.820::numeric, 132.72000::numeric)) AS v(a, b);
|
||||
a | b | lcm | lcm | lcm | lcm
|
||||
----------+-----------+--------------+--------------+--------------+--------------
|
||||
0 | 0 | 0 | 0 | 0 | 0
|
||||
0 | NaN | NaN | NaN | NaN | NaN
|
||||
0 | 13272 | 0 | 0 | 0 | 0
|
||||
13272 | 13272 | 13272 | 13272 | 13272 | 13272
|
||||
423282 | 13272 | 11851896 | 11851896 | 11851896 | 11851896
|
||||
42328.2 | 1327.2 | 1185189.6 | 1185189.6 | 1185189.6 | 1185189.6
|
||||
4232.820 | 132.72000 | 118518.96000 | 118518.96000 | 118518.96000 | 118518.96000
|
||||
(7 rows)
|
||||
|
||||
SELECT lcm(9999 * (10::numeric)^131068 + (10::numeric^131068 - 1), 2); -- overflow
|
||||
ERROR: value overflows numeric format
|
||||
|
@ -155,3 +155,28 @@ FROM (VALUES (-2.5::numeric),
|
||||
(0.5::numeric),
|
||||
(1.5::numeric),
|
||||
(2.5::numeric)) t(x);
|
||||
|
||||
-- test gcd()
|
||||
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a)
|
||||
FROM (VALUES (0::int4, 0::int4),
|
||||
(0::int4, 6410818::int4),
|
||||
(61866666::int4, 6410818::int4),
|
||||
(-61866666::int4, 6410818::int4),
|
||||
((-2147483648)::int4, 1::int4),
|
||||
((-2147483648)::int4, 2147483647::int4),
|
||||
((-2147483648)::int4, 1073741824::int4)) AS v(a, b);
|
||||
|
||||
SELECT gcd((-2147483648)::int4, 0::int4); -- overflow
|
||||
SELECT gcd((-2147483648)::int4, (-2147483648)::int4); -- overflow
|
||||
|
||||
-- test lcm()
|
||||
SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a)
|
||||
FROM (VALUES (0::int4, 0::int4),
|
||||
(0::int4, 42::int4),
|
||||
(42::int4, 42::int4),
|
||||
(330::int4, 462::int4),
|
||||
(-330::int4, 462::int4),
|
||||
((-2147483648)::int4, 0::int4)) AS v(a, b);
|
||||
|
||||
SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
|
||||
SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
|
||||
|
@ -225,3 +225,28 @@ FROM (VALUES (-2.5::numeric),
|
||||
(0.5::numeric),
|
||||
(1.5::numeric),
|
||||
(2.5::numeric)) t(x);
|
||||
|
||||
-- test gcd()
|
||||
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a)
|
||||
FROM (VALUES (0::int8, 0::int8),
|
||||
(0::int8, 29893644334::int8),
|
||||
(288484263558::int8, 29893644334::int8),
|
||||
(-288484263558::int8, 29893644334::int8),
|
||||
((-9223372036854775808)::int8, 1::int8),
|
||||
((-9223372036854775808)::int8, 9223372036854775807::int8),
|
||||
((-9223372036854775808)::int8, 4611686018427387904::int8)) AS v(a, b);
|
||||
|
||||
SELECT gcd((-9223372036854775808)::int8, 0::int8); -- overflow
|
||||
SELECT gcd((-9223372036854775808)::int8, (-9223372036854775808)::int8); -- overflow
|
||||
|
||||
-- test lcm()
|
||||
SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a)
|
||||
FROM (VALUES (0::int8, 0::int8),
|
||||
(0::int8, 29893644334::int8),
|
||||
(29893644334::int8, 29893644334::int8),
|
||||
(288484263558::int8, 29893644334::int8),
|
||||
(-288484263558::int8, 29893644334::int8),
|
||||
((-9223372036854775808)::int8, 0::int8)) AS v(a, b);
|
||||
|
||||
SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow
|
||||
SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow
|
||||
|
@ -1073,3 +1073,28 @@ select trim_scale(1e100);
|
||||
-- cases that need carry propagation
|
||||
SELECT SUM(9999::numeric) FROM generate_series(1, 100000);
|
||||
SELECT SUM((-9999)::numeric) FROM generate_series(1, 100000);
|
||||
|
||||
--
|
||||
-- Tests for GCD()
|
||||
--
|
||||
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(-b, a), gcd(-b, -a)
|
||||
FROM (VALUES (0::numeric, 0::numeric),
|
||||
(0::numeric, numeric 'NaN'),
|
||||
(0::numeric, 46375::numeric),
|
||||
(433125::numeric, 46375::numeric),
|
||||
(43312.5::numeric, 4637.5::numeric),
|
||||
(4331.250::numeric, 463.75000::numeric)) AS v(a, b);
|
||||
|
||||
--
|
||||
-- Tests for LCM()
|
||||
--
|
||||
SELECT a,b, lcm(a, b), lcm(a, -b), lcm(-b, a), lcm(-b, -a)
|
||||
FROM (VALUES (0::numeric, 0::numeric),
|
||||
(0::numeric, numeric 'NaN'),
|
||||
(0::numeric, 13272::numeric),
|
||||
(13272::numeric, 13272::numeric),
|
||||
(423282::numeric, 13272::numeric),
|
||||
(42328.2::numeric, 1327.2::numeric),
|
||||
(4232.820::numeric, 132.72000::numeric)) AS v(a, b);
|
||||
|
||||
SELECT lcm(9999 * (10::numeric)^131068 + (10::numeric^131068 - 1), 2); -- overflow
|
||||
|
Loading…
x
Reference in New Issue
Block a user