Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1ilFQi-0000VA-0y for pgsql-hackers@arkaria.postgresql.org; Sat, 28 Dec 2019 16:59:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1ilFQg-0006sJ-9z for pgsql-hackers@arkaria.postgresql.org; Sat, 28 Dec 2019 16:59:06 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1ilFQg-0006sC-0q for pgsql-hackers@lists.postgresql.org; Sat, 28 Dec 2019 16:59:06 +0000 Received: from mail-wr1-x435.google.com ([2a00:1450:4864:20::435]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1ilFQY-0003tM-1z for pgsql-hackers@lists.postgresql.org; Sat, 28 Dec 2019 16:59:05 +0000 Received: by mail-wr1-x435.google.com with SMTP id d16so28890720wre.10 for ; Sat, 28 Dec 2019 08:58:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=from:subject:to:message-id:date:user-agent:mime-version :content-language; bh=zN/bqJk9gwNtVEAU/02+M/JtxvowXYOh6nDyv4a++ug=; b=s/HHFCg6Xwf7nIvWkPh1kkoOx8q/BCIi49E4uBc+SR83FoiiQ4Bpci3a4hDq+a9Pwt 54HAJOHZPLoQZrJzRbQc4KtSZH4xpKm2mgrG57cdJkYBH8qeahs1p/Vvv+O+NC7pZNEB 8Endm3Shu/i60EjqEOyRTvTa8RBMMU77f/OcUc4G2UyHJzrlzFHJLlSWUKJT1Ltier1t AwjFm/USYKiTqn/BV0MYn7gHrKiFSoR2EbFRFfBUDQpXE00tke9PkxrBenjdlo93TfRF LW1v+fYzrAGApLJUOGJRkYi1TsmWCgk2vWaZXGGy1h+s0ZaOydsjLU6Z1T9mvrAjraut lzsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:subject:to:message-id:date:user-agent :mime-version:content-language; bh=zN/bqJk9gwNtVEAU/02+M/JtxvowXYOh6nDyv4a++ug=; b=B661mMJzE1D3iRg9ccFnqteBPGmeE2Xw4paIvPYIqptRbKPqVr//yG0R10QlbPELWR Fgx+LqBpxk7x/qifs4f9SK01YS1nQ9mE80w7yGjexVxrDp6afu/wcE5YlvB9HfxHBotA gxcCijn6NgzpZs9O7J1r1rsvyXoPtF3cReQ6DA7uMVKa71ptY5UTRyZ+uEJavvLffkzC wXO/TMJMW4crWUbtWKEIV37VEC8V35plj9xP+dCim4rpEXT3r6ivH9Rb8syxuKXTfGC7 ul934/gHpiHYwyZvGSBgpzK7Yx0k0eFYRyM+0FbV+W3ZK7Fo6jImuhLcxNLa9beSIWBU 4+0A== X-Gm-Message-State: APjAAAUwUIl/f8JZrBcsfrebYXix6VG11O/R8J6TbkllIGsOX670sU5x HdUBp9PkpbQEUyEF8CODCXl+LiMcaiY4TkntUABRzXK/Dz9v9OOKfxit423NLbT9SrFrPhjwdRQ gK15ZNGMPrad7azuTwB30mriKl4fSGasaeKSjqArJ8P83xAtN1liI2NRgKwnRJE1s58Z0fHb2kX 8NDnrmymF+KTW3HW9VftIq X-Google-Smtp-Source: APXvYqxKRdGfuop9+RTKr7F/QlB79BQYDdWLI5ZJ88ZvF8d1zk1U45HHg+YlwfI56ZvIWL1/cdn6Fg== X-Received: by 2002:a05:6000:1142:: with SMTP id d2mr53464584wrx.253.1577552335801; Sat, 28 Dec 2019 08:58:55 -0800 (PST) Received: from [10.8.8.7] ([185.94.189.189]) by smtp.gmail.com with ESMTPSA id l3sm38131940wrt.29.2019.12.28.08.58.54 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 28 Dec 2019 08:58:55 -0800 (PST) From: Vik Fearing Subject: Greatest Common Divisor To: PostgreSQL Hackers Message-ID: Date: Sat, 28 Dec 2019 17:58:52 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.2.2 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------A4229E325B4C69FF36A1DB1C" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------A4229E325B4C69FF36A1DB1C Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit I recently came across the need for a gcd function (actually I needed lcm) and was surprised that we didn't have one. So here one is, using the basic Euclidean algorithm.  I made one for smallint, integer, and bigint. -- Vik Fearing +33 6 46 75 15 36 http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support --------------A4229E325B4C69FF36A1DB1C Content-Type: text/x-patch; charset=UTF-8; name="gcd.0001.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gcd.0001.patch" diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 57a1539506..b2c663cd92 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -870,6 +870,19 @@ -43 + + + + gcd + + gcd(a, b) + + (same as argument types) + greatest common divisor + gcd(1071, 462) + 21 + + diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 04825fc77d..bc74d367bb 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -1196,6 +1196,47 @@ int2abs(PG_FUNCTION_ARGS) PG_RETURN_INT16(result); } +/* int[24]gcd() + * Greatest Common Divisor + */ +Datum +int4gcd(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + while (arg2 != 0) + { + int32 tmp = arg2; + arg2 = arg1 % arg2; + arg1 = tmp; + } + + if (arg1 < 0) + arg1 = -arg1; + + PG_RETURN_INT32(arg1); +} + +Datum +int2gcd(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + int16 arg2 = PG_GETARG_INT16(1); + + while (arg2 != 0) + { + int16 tmp = arg2; + arg2 = arg1 % arg2; + arg1 = tmp; + } + + if (arg1 < 0) + arg1 = -arg1; + + PG_RETURN_INT16(arg1); +} + Datum int2larger(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index a40ae40dff..97cbd1443b 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -667,6 +667,28 @@ int8mod(PG_FUNCTION_ARGS) PG_RETURN_INT64(arg1 % arg2); } +/* int8gcd() + * Greatest Common Divisor + */ +Datum +int8gcd(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + + while (arg2 != 0) + { + int64 tmp = arg2; + arg2 = arg1 % arg2; + arg1 = tmp; + } + + if (arg1 < 0) + arg1 = -arg1; + + PG_RETURN_INT64(arg1); +} + Datum int8inc(PG_FUNCTION_ARGS) diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index ac8f64b219..b3057f2cdd 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -10729,4 +10729,15 @@ proname => 'pg_partition_root', prorettype => 'regclass', proargtypes => 'regclass', prosrc => 'pg_partition_root' }, +# greatest common divisor +{ oid => '8463', descr => 'greatest common divisor', + proname => 'gcd', prorettype => 'int2', proargtypes => 'int2 int2', + prosrc => 'int2gcd' }, +{ oid => '8464', descr => 'greatest common divisor', + proname => 'gcd', prorettype => 'int4', proargtypes => 'int4 int4', + prosrc => 'int4gcd' }, +{ oid => '8465', descr => 'greatest common divisor', + proname => 'gcd', prorettype => 'int8', proargtypes => 'int8 int8', + prosrc => 'int8gcd' }, + ] diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out index 8c255b9e4d..66906a7b03 100644 --- a/src/test/regress/expected/int2.out +++ b/src/test/regress/expected/int2.out @@ -306,3 +306,11 @@ FROM (VALUES (-2.5::numeric), 2.5 | 3 (7 rows) +-- gcd +SELECT gcd(a, b), gcd(a, -b), gcd(-a, b), gcd(-a, -b) +FROM (VALUES (24948, 4914)) AS v(a, b); + gcd | gcd | gcd | gcd +-----+-----+-----+----- + 378 | 378 | 378 | 378 +(1 row) + diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out index bda7a8daef..a261964454 100644 --- a/src/test/regress/expected/int4.out +++ b/src/test/regress/expected/int4.out @@ -403,3 +403,11 @@ FROM (VALUES (-2.5::numeric), 2.5 | 3 (7 rows) +-- gcd +SELECT gcd(a, b), gcd(a, -b), gcd(-a, b), gcd(-a, -b) +FROM (VALUES (61866666, 6410818)) AS v(a, b); + gcd | gcd | gcd | gcd +------+------+------+------ + 1466 | 1466 | 1466 | 1466 +(1 row) + diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out index 8447a28c3d..1a0836998a 100644 --- a/src/test/regress/expected/int8.out +++ b/src/test/regress/expected/int8.out @@ -388,6 +388,16 @@ SELECT '' AS five, q1 * 2 AS "twice int4" FROM INT8_TBL; | 9135780246913578 (5 rows) +SELECT q1, q2, gcd(q1, q2) FROM INT8_TBL; + q1 | q2 | gcd +------------------+-------------------+------------------ + 123 | 456 | 3 + 123 | 4567890123456789 | 3 + 4567890123456789 | 123 | 3 + 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 +(5 rows) + -- int8 op int4 SELECT q1 + 42::int4 AS "8plus4", q1 - 42::int4 AS "8minus4", q1 * 42::int4 AS "8mul4", q1 / 42::int4 AS "8div4" FROM INT8_TBL; 8plus4 | 8minus4 | 8mul4 | 8div4 diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql index 7dbafb6dac..e0bf7276fc 100644 --- a/src/test/regress/sql/int2.sql +++ b/src/test/regress/sql/int2.sql @@ -112,3 +112,7 @@ FROM (VALUES (-2.5::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t(x); + +-- gcd +SELECT gcd(a, b), gcd(a, -b), gcd(-a, b), gcd(-a, -b) +FROM (VALUES (24948, 4914)) AS v(a, b); diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql index f014cb2d32..0acc6c9060 100644 --- a/src/test/regress/sql/int4.sql +++ b/src/test/regress/sql/int4.sql @@ -155,3 +155,7 @@ FROM (VALUES (-2.5::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t(x); + +-- gcd +SELECT gcd(a, b), gcd(a, -b), gcd(-a, b), gcd(-a, -b) +FROM (VALUES (61866666, 6410818)) AS v(a, b); diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql index e890452236..76959270e0 100644 --- a/src/test/regress/sql/int8.sql +++ b/src/test/regress/sql/int8.sql @@ -79,6 +79,8 @@ SELECT 37 - q1 AS minus4 FROM INT8_TBL; SELECT '' AS five, 2 * q1 AS "twice int4" FROM INT8_TBL; SELECT '' AS five, q1 * 2 AS "twice int4" FROM INT8_TBL; +SELECT q1, q2, gcd(q1, q2) FROM INT8_TBL; + -- int8 op int4 SELECT q1 + 42::int4 AS "8plus4", q1 - 42::int4 AS "8minus4", q1 * 42::int4 AS "8mul4", q1 / 42::int4 AS "8div4" FROM INT8_TBL; -- int4 op int8 --------------A4229E325B4C69FF36A1DB1C--