pax_global_header00006660000000000000000000000064136170507360014522gustar00rootroot0000000000000052 comment=1542950d1e924ec14b136a3a8d08c82d7439c551 pgmp-rel-1.0.4/000077500000000000000000000000001361705073600132475ustar00rootroot00000000000000pgmp-rel-1.0.4/.gitignore000066400000000000000000000003231361705073600152350ustar00rootroot00000000000000data tags pgmp.so pgmp.sql pgmp.control pgmp--*.sql results/*.out regression.diffs regression.out docs/env docs/html docs/_static/jsMath/ sandbox/hello/hello sandbox/bench/*.png sandbox/pi/*.txt dist/* src/*.bc pgmp-rel-1.0.4/.travis.yml000066400000000000000000000021541361705073600153620ustar00rootroot00000000000000# pgmp -- Travis CI configuration # # Copyright (C) 2020 Daniele Varrazzo # # This file is part of the PostgreSQL GMP Module language: python matrix: include: - addons: postgresql: '9.4' apt: packages: - postgresql-server-dev-9.4 - addons: postgresql: '9.5' apt: packages: - postgresql-server-dev-9.5 - addons: postgresql: '9.6' apt: packages: - postgresql-server-dev-9.6 - addons: postgresql: '10' apt: packages: - postgresql-server-dev-10 - addons: postgresql: '11' apt: packages: - postgresql-11 - postgresql-client-11 - postgresql-server-dev-11 env: PGPORT: "5433" - addons: postgresql: '12' apt: packages: - postgresql-12 - postgresql-client-12 - postgresql-server-dev-12 env: PGPORT: "5433" install: - make - sudo make install script: - make installcheck notifications: email: false pgmp-rel-1.0.4/AUTHORS000066400000000000000000000002141361705073600143140ustar00rootroot00000000000000Authors of the PostgreSQL GMP Module Daniele Varrazzo Main author Torello Querci Several contributions, lots of patience pgmp-rel-1.0.4/COPYING000066400000000000000000000167441361705073600143160ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. pgmp-rel-1.0.4/META.json000066400000000000000000000022521361705073600146710ustar00rootroot00000000000000{ "name": "pgmp", "abstract": "PostgreSQL Multiple Precision Arithmetic extension", "description": "The pgmp extension adds PostgreSQL data types wrapping the high performance integer and rational data types offered by the GMP library.", "version": "1.0.4", "maintainer": "Daniele Varrazzo ", "tags": [ "arithmetic", "gmp", "rational", "integer", "data types" ], "release_status": "stable", "license": "lgpl_3_0", "provides": { "pgmp": { "file": "sql/pgmp.sql", "version": "1.0.4", "abstract": "PostgreSQL Multiple Precision Arithmetic extension" } }, "prereqs": { "runtime": { "requires": { "PostgreSQL": "8.4.0" } } }, "resources": { "homepage": "https://dvarrazzo.github.io/pgmp/", "bugtracker": { "web": "https://github.com/dvarrazzo/pgmp/issues" }, "repository": { "url": "git://github.com/dvarrazzo/pgmp.git", "web": "https://github.com/dvarrazzo/pgmp/", "type": "git" } }, "meta-spec": { "version": "1.0.0", "url": "https://pgxn.org/meta/spec.txt" } } pgmp-rel-1.0.4/Makefile000066400000000000000000000061511361705073600147120ustar00rootroot00000000000000# pgmp -- pgxs-based makefile # # Copyright (C) 2011-2020 Daniele Varrazzo # # This file is part of the PostgreSQL GMP Module # # The PostgreSQL GMP Module is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 3 of the License, # or (at your option) any later version. # # The PostgreSQL GMP Module is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with the PostgreSQL GMP Module. If not, see # https://www.gnu.org/licenses/. .PHONY: docs # You may have to customize this to run the test suite # REGRESS_OPTS=--user postgres PG_CONFIG=pg_config SHLIB_LINK=-lgmp -lm EXTENSION = pgmp MODULEDIR = $(EXTENSION) MODULE_big = $(EXTENSION) EXT_LONGVER = $(shell grep '"version":' META.json | head -1 | sed -e 's/\s*"version":\s*"\(.*\)",/\1/') EXT_SHORTVER = $(shell grep 'default_version' $(EXTENSION).control | head -1 | sed -e "s/default_version\s*=\s'\(.*\)'/\1/") PG91 = $(shell $(PG_CONFIG) --version | grep -qE " 8\.| 9\.0" && echo pre91 || echo 91) SRC_C = $(sort $(wildcard src/*.c)) SRC_H = $(sort $(wildcard src/*.h)) SRCFILES = $(SRC_C) $(SRC_H) OBJS = $(patsubst %.c,%.o,$(SRC_C)) TESTFILES = $(sort $(wildcard test/sql/*.sql) $(wildcard test/expected/*.out)) BENCHFILES = $(sort $(wildcard bench/*.py) $(wildcard bench/*.txt)) DOCFILES = $(sort $(wildcard docs/*.rst)) TOOLSFILES = $(sort $(wildcard tools/*.py)) PKGFILES = $(SRCFILES) $(DOCFILES) $(TESTFILES) $(BENCHFILES) $(TOOLSFILES) \ AUTHORS COPYING README.rst Makefile META.json pgmp.control \ sql/pgmp.pysql sql/uninstall_pgmp.sql \ docs/conf.py docs/Makefile docs/_static/pgmp.css \ docs/html-gitignore docs/requirements.txt ifeq ($(PG91),91) INSTALLSCRIPT=sql/pgmp--$(EXT_SHORTVER).sql UPGRADESCRIPT=sql/pgmp--unpackaged--$(EXT_SHORTVER).sql DATA = $(INSTALLSCRIPT) $(UPGRADESCRIPT) else INSTALLSCRIPT=sql/pgmp.sql DATA = $(INSTALLSCRIPT) sql/uninstall_pgmp.sql endif # the += doesn't work if the user specified his own REGRESS_OPTS REGRESS = --inputdir=test setup-$(PG91) mpz mpq EXTRA_CLEAN = $(INSTALLSCRIPT) $(UPGRADESCRIPT) PKGNAME = pgmp-$(EXT_LONGVER) SRCPKG = $(SRCPKG_TGZ) $(SRCPKG_ZIP) SRCPKG_TGZ = dist/$(PKGNAME).tar.gz SRCPKG_ZIP = dist/$(PKGNAME).zip USE_PGXS=1 PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) # added to the targets defined in pgxs all: $(INSTALLSCRIPT) $(UPGRADESCRIPT) $(INSTALLSCRIPT): sql/pgmp.pysql tools/unmix.py < $< > $@ $(UPGRADESCRIPT): $(INSTALLSCRIPT) tools/sql2extension.py --extname pgmp $< > $@ docs: $(MAKE) -C docs sdist: $(SRCPKG) $(SRCPKG): $(PKGFILES) ln -sf . $(PKGNAME) mkdir -p dist rm -f $(SRCPKG_TGZ) tar czvf $(SRCPKG_TGZ) $(addprefix $(PKGNAME)/,$^) rm -f $(SRCPKG_ZIP) zip -r $(SRCPKG_ZIP) $(addprefix $(PKGNAME)/,$^) rm $(PKGNAME) # Required for testing in pgxn client on PG < 9.1 installcheck: $(INSTALLSCRIPT) pgmp-rel-1.0.4/NEWS.rst000066400000000000000000000013621361705073600145570ustar00rootroot00000000000000Current release --------------- What's new in pgmp 1.0.4 ^^^^^^^^^^^^^^^^^^^^^^^^ Released on 2020-02-06. - Fixed compatibility with GMP 6.2 (:issue:`#18`) What's new in pgmp 1.0.3 ^^^^^^^^^^^^^^^^^^^^^^^^ Released on 2020-01-30. - Fixed build with PostgreSQL up to 12 (:issues:`#8, #11, #14`) - Fixed build with Python 3 (:issue:`#16`) - Fixed access violation on Windows (:issue:`#9`) What's new in pgmp 1.0.2 ^^^^^^^^^^^^^^^^^^^^^^^^ Released on 2015-01-16. - Fixed build with PostgreSQL 9.3, 9.4 (:issues:`#3`) What's new in pgmp 1.0.1 ^^^^^^^^^^^^^^^^^^^^^^^^ Released on 2012-09-29. - Only documetation improvements and corrections What's new in pgmp 1.0.0 ------------------------ Released on 2011-05-23. - First public release pgmp-rel-1.0.4/README.rst000066400000000000000000000032241361705073600147370ustar00rootroot00000000000000PostgreSQL Multiple Precision Arithmetic extension ================================================== |travis| .. |travis| image:: https://travis-ci.org/dvarrazzo/pgmp.svg?branch=master :target: https://travis-ci.org/dvarrazzo/pgmp :alt: Build status pgmp is a PostgreSQL extension module to add support for the arbitrary precision data types offered by the GMP library into the database. The extension adds the types mpz_ (arbitrary size integers) and mpq_ (arbitrary precision rationals) to PostgreSQL and exposes to the database all the functions available in the GMP library for these types, providing: - higher performance arithmetic on integers respect to the ``decimal`` data type, using numbers only limited by the 1GB varlena maximum size; - a rational data type for absolute precision storage and arithmetic; - the use of specialized functions to deal with prime numbers, random numbers, factorization directly into the database. The GMP data types can be stored into the database, used in mixed arithmetic with other PostgreSQL numeric types and indexed using the btree or hash methods. The extension is compatible with PostgreSQL versions from 8.4 and packaged as a SQL extension in 9.1. The package includes comprehensive documentation and regression tests. Please refer to the documentation for installation and usage, either online__ or in the ``docs/`` directory. - Homepage: https://dvarrazzo.github.io/pgmp/ - Project page: https://github.com/dvarrazzo/pgmp/ - Download: https://pgxn.org/dist/pgmp/ .. _mpz: https://dvarrazzo.github.io/pgmp/mpz.html .. _mpq: https://dvarrazzo.github.io/pgmp/mpq.html .. __: https://dvarrazzo.github.io/pgmp/ pgmp-rel-1.0.4/bench/000077500000000000000000000000001361705073600143265ustar00rootroot00000000000000pgmp-rel-1.0.4/bench/Arith-1e6.txt000066400000000000000000000017621361705073600165350ustar00rootroot00000000000000title: Performance on arithmetic operations xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) Arith int4 1000000 4 898.941 Arith int8 1000000 4 1500.954 Arith mpz 1000000 4 1501.565 Arith numeric 1000000 4 2562.059 Arith int8 1000000 9 1530.071 Arith mpz 1000000 9 1533.018 Arith numeric 1000000 9 2937.77 Arith mpz 1000000 20 1855.234 Arith numeric 1000000 20 3142.704 Arith mpz 1000000 30 1866.971 Arith numeric 1000000 30 3901.096 Arith mpz 1000000 40 1920.063 Arith numeric 1000000 40 4539.643 Arith mpz 1000000 50 2003.029 Arith numeric 1000000 50 5619.141 Arith mpz 1000000 60 2060.608 Arith numeric 1000000 60 6527.557 Arith mpz 1000000 70 2109.8 Arith numeric 1000000 70 7832.202 Arith mpz 1000000 80 2181.845 Arith numeric 1000000 80 8878.389 Arith mpz 1000000 90 2274.953 Arith numeric 1000000 90 10746.137 Arith mpz 1000000 100 2515.654 Arith numeric 1000000 100 12040.895 title: Performance on arithmetic operations xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) pgmp-rel-1.0.4/bench/Factorial.txt000066400000000000000000000012511361705073600167720ustar00rootroot00000000000000title: Time to calculate n! xlabel: Input number ylabel: Time (in millis) Factorial mpz 0 1000 3.029 Factorial numeric 0 1000 6.427 Factorial mpz 0 2000 5.894 Factorial numeric 0 2000 40.662 Factorial mpz 0 3000 5.281 Factorial numeric 0 3000 133.611 Factorial mpz 0 4000 8.487 Factorial numeric 0 4000 321.281 Factorial mpz 0 5000 19.249 Factorial numeric 0 5000 643.611 Factorial mpz 0 6000 16.97 Factorial numeric 0 6000 1140.778 Factorial mpz 0 7000 21.159 Factorial numeric 0 7000 1854.77 Factorial mpz 0 8000 26.373 Factorial numeric 0 8000 2822.352 Factorial mpz 0 9000 32.44 Factorial numeric 0 9000 4106.126 Factorial mpz 0 10000 39.152 Factorial numeric 0 10000 5772.065 pgmp-rel-1.0.4/bench/SumInteger-1e6.txt000066400000000000000000000033211361705073600175410ustar00rootroot00000000000000title: Time to calculate sum() on a table xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) SumInteger int4 1000000 9 668.755 SumInteger int8 1000000 9 1192.607 SumInteger mpq 1000000 9 918.483 SumInteger mpz 1000000 9 782.914 SumInteger numeric 1000000 9 1091.92 SumInteger int8 1000000 18 1249.43 SumInteger mpq 1000000 18 922.206 SumInteger mpz 1000000 18 782.608 SumInteger numeric 1000000 18 1134.221 SumInteger mpq 1000000 25 938.396 SumInteger mpz 1000000 25 791.592 SumInteger numeric 1000000 25 1172.895 SumInteger mpq 1000000 50 944.448 SumInteger mpz 1000000 50 793.938 SumInteger numeric 1000000 50 1250.873 SumInteger mpq 1000000 100 978.069 SumInteger mpz 1000000 100 820.975 SumInteger numeric 1000000 100 1413.902 SumInteger mpq 1000000 200 1027.79 SumInteger mpz 1000000 200 851.698 SumInteger numeric 1000000 200 1690.229 SumInteger mpq 1000000 300 997.334 SumInteger mpz 1000000 300 810.103 SumInteger numeric 1000000 300 1944.131 SumInteger mpq 1000000 400 1048.459 SumInteger mpz 1000000 400 834.52 SumInteger numeric 1000000 400 2209.226 SumInteger mpq 1000000 500 1094.662 SumInteger mpz 1000000 500 868.257 SumInteger numeric 1000000 500 2493.098 SumInteger mpq 1000000 600 1144.607 SumInteger mpz 1000000 600 889.218 SumInteger numeric 1000000 600 2766.194 SumInteger mpq 1000000 700 1194.763 SumInteger mpz 1000000 700 919.533 SumInteger numeric 1000000 700 3049.253 SumInteger mpq 1000000 800 1265.843 SumInteger mpz 1000000 800 951.993 SumInteger numeric 1000000 800 3302.619 SumInteger mpq 1000000 900 1287.379 SumInteger mpz 1000000 900 981.598 SumInteger numeric 1000000 900 3583.304 SumInteger mpq 1000000 1000 1344.086 SumInteger mpz 1000000 1000 999.312 SumInteger numeric 1000000 1000 3835.676 pgmp-rel-1.0.4/bench/SumRational-p2-1e6.txt000066400000000000000000000016601361705073600202400ustar00rootroot00000000000000title: Time for sum() for values with scale 2 xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) SumRational mpq 1000000 5 1164.159 SumRational numeric 1000000 5 1080.567 SumRational mpq 1000000 10 1172.377 SumRational numeric 1000000 10 1093.265 SumRational mpq 1000000 20 1187.437 SumRational numeric 1000000 20 1140.502 SumRational mpq 1000000 30 1188.257 SumRational numeric 1000000 30 1193.76 SumRational mpq 1000000 40 1194.309 SumRational numeric 1000000 40 1226.504 SumRational mpq 1000000 50 1203.34 SumRational numeric 1000000 50 1266.917 SumRational mpq 1000000 60 1205.517 SumRational numeric 1000000 60 1299.281 SumRational mpq 1000000 70 1219.193 SumRational numeric 1000000 70 1323.616 SumRational mpq 1000000 80 1225.19 SumRational numeric 1000000 80 1354.83 SumRational mpq 1000000 90 1246.252 SumRational numeric 1000000 90 1391.645 SumRational mpq 1000000 100 1238.198 SumRational numeric 1000000 100 1418.017 pgmp-rel-1.0.4/bench/SumRational-p4-1e6.txt000066400000000000000000000016611361705073600202430ustar00rootroot00000000000000title: Time for sum() for values with scale 4 xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) SumRational mpq 1000000 5 1119.65 SumRational numeric 1000000 5 1080.88 SumRational mpq 1000000 10 1141.113 SumRational numeric 1000000 10 1090.011 SumRational mpq 1000000 20 1150.815 SumRational numeric 1000000 20 1127.691 SumRational mpq 1000000 30 1147.41 SumRational numeric 1000000 30 1160.891 SumRational mpq 1000000 40 1159.387 SumRational numeric 1000000 40 1207.802 SumRational mpq 1000000 50 1153.466 SumRational numeric 1000000 50 1261.346 SumRational mpq 1000000 60 1157.451 SumRational numeric 1000000 60 1277.414 SumRational mpq 1000000 70 1167.845 SumRational numeric 1000000 70 1302.096 SumRational mpq 1000000 80 1173.909 SumRational numeric 1000000 80 1347.751 SumRational mpq 1000000 90 1196.104 SumRational numeric 1000000 90 1370.181 SumRational mpq 1000000 100 1196.038 SumRational numeric 1000000 100 1410.034 pgmp-rel-1.0.4/bench/SumRational-p8-1e6.txt000066400000000000000000000015471361705073600202520ustar00rootroot00000000000000title: Time for sum() for values with scale 8 xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) SumRational mpq 1000000 10 1237.856 SumRational numeric 1000000 10 1090.758 SumRational mpq 1000000 20 1229.576 SumRational numeric 1000000 20 1131.46 SumRational mpq 1000000 30 1236.447 SumRational numeric 1000000 30 1164.837 SumRational mpq 1000000 40 1231.617 SumRational numeric 1000000 40 1193.801 SumRational mpq 1000000 50 1244.692 SumRational numeric 1000000 50 1261.286 SumRational mpq 1000000 60 1242.672 SumRational numeric 1000000 60 1273.981 SumRational mpq 1000000 70 1254.33 SumRational numeric 1000000 70 1303.641 SumRational mpq 1000000 80 1266.38 SumRational numeric 1000000 80 1352.494 SumRational mpq 1000000 90 1274.606 SumRational numeric 1000000 90 1371.406 SumRational mpq 1000000 100 1276.491 SumRational numeric 1000000 100 1411.129 pgmp-rel-1.0.4/bench/SumSequence-1e6.txt000066400000000000000000000015401361705073600177150ustar00rootroot00000000000000title: Time spent for sum() on a SRF xlabel: Numbers size (in decimal digits) ylabel: Time (in millis) SumSequence mpz 1000000 1 1182.24 SumSequence numeric 1000000 1 1484.823 SumSequence mpz 1000000 2 1177.217 SumSequence numeric 1000000 2 1481.465 SumSequence mpz 1000000 5 1176.305 SumSequence numeric 1000000 5 1513.082 SumSequence mpz 1000000 10 1184.41 SumSequence numeric 1000000 10 1508.305 SumSequence mpz 1000000 20 1189.85 SumSequence numeric 1000000 20 1538.56 SumSequence mpz 1000000 50 1297.238 SumSequence numeric 1000000 50 1776.067 SumSequence mpz 1000000 100 1398.437 SumSequence numeric 1000000 100 1943.531 SumSequence mpz 1000000 200 1739.455 SumSequence numeric 1000000 200 2511.307 SumSequence mpz 1000000 500 3229.993 SumSequence numeric 1000000 500 4672.664 SumSequence mpz 1000000 1000 9642.867 SumSequence numeric 1000000 1000 13688.062 pgmp-rel-1.0.4/bench/TableSize-1e6-small.txt000066400000000000000000000020241361705073600204460ustar00rootroot00000000000000title: Size of a table on disk xlabel: Numbers size (in decimal digits) ylabel: Size (in pages) TableSize int4 1000000 1 3922 TableSize int8 1000000 1 4425 TableSize mpq 1000000 1 5301 TableSize mpz 1000000 1 4858 TableSize numeric 1000000 1 4425 TableSize int4 1000000 2 3922 TableSize int8 1000000 2 4425 TableSize mpq 1000000 2 5390 TableSize mpz 1000000 2 4902 TableSize numeric 1000000 2 4425 TableSize int4 1000000 5 3922 TableSize int8 1000000 5 4425 TableSize mpq 1000000 5 5406 TableSize mpz 1000000 5 4902 TableSize numeric 1000000 5 4857 TableSize int4 1000000 9 3922 TableSize int8 1000000 9 4425 TableSize mpq 1000000 9 5406 TableSize mpz 1000000 9 4902 TableSize numeric 1000000 9 4902 TableSize int8 1000000 15 4425 TableSize mpq 1000000 15 5883 TableSize mpz 1000000 15 5406 TableSize numeric 1000000 15 5406 TableSize int8 1000000 18 4425 TableSize mpq 1000000 18 5883 TableSize mpz 1000000 18 5406 TableSize numeric 1000000 18 5406 TableSize mpq 1000000 25 6370 TableSize mpz 1000000 25 5883 TableSize numeric 1000000 25 5883 pgmp-rel-1.0.4/bench/TableSize-1e6.txt000066400000000000000000000022501361705073600173410ustar00rootroot00000000000000title: Size of a table on disk xlabel: Numbers size (in decimal digits) ylabel: Size (in pages) TableSize mpq 1000000 20 6294 TableSize mpz 1000000 20 5802 TableSize numeric 1000000 20 5406 TableSize mpq 1000000 100 10310 TableSize mpz 1000000 100 9804 TableSize numeric 1000000 100 10310 TableSize mpq 1000000 200 15385 TableSize mpz 1000000 200 14706 TableSize numeric 1000000 200 16667 TableSize mpq 1000000 300 20834 TableSize mpz 1000000 300 20408 TableSize numeric 1000000 300 23256 TableSize mpq 1000000 400 25642 TableSize mpz 1000000 400 25000 TableSize numeric 1000000 400 29412 TableSize mpq 1000000 500 31250 TableSize mpz 1000000 500 30304 TableSize numeric 1000000 500 35715 TableSize mpq 1000000 600 37036 TableSize mpz 1000000 600 35715 TableSize numeric 1000000 600 41667 TableSize mpq 1000000 700 41667 TableSize mpz 1000000 700 41667 TableSize numeric 1000000 700 47620 TableSize mpq 1000000 800 47620 TableSize mpz 1000000 800 45515 TableSize numeric 1000000 800 55556 TableSize mpq 1000000 900 52632 TableSize mpz 1000000 900 52632 TableSize numeric 1000000 900 62500 TableSize mpq 1000000 1000 58824 TableSize mpz 1000000 1000 55556 TableSize numeric 1000000 1000 66667 pgmp-rel-1.0.4/bench/benchmark.py000077500000000000000000000326131361705073600166420ustar00rootroot00000000000000#!/usr/bin/env python """Script to perform comparisons between pmpz and other data types. Copyright (C) 2011-2020 - Daniele Varrazzo """ import sys import psycopg2 import logging logger = logging.getLogger() logging.basicConfig( level=logging.INFO, stream=sys.stderr, format="%(asctime)s %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") class SkipTest(Exception): """The test can't be performed for some reason.""" class Benchmark(object): """Base class for a test. Tests should subclass and create methods called test_whatever. There can also be methods setup_whatever that will be invoked just once (the test can be repeated if -r is not 1). """ # Subclass may change these details that will appear in the plots. title = "Benchmark" xlabel = "x axis" ylabel = "y axis" def __init__(self, opt): self.opt = opt def run(self): opt = self.opt self.conn = psycopg2.connect(opt.dsn) self.conn.set_isolation_level(0) tests = [] for k in dir(self): if k.startswith('test_'): tests.append((k[5:], getattr(self, k))) tests.sort() print "title:", self.title print "xlabel:", self.xlabel print "ylabel:", self.ylabel for n in opt.nsamples: for s in opt.size: for name, f in tests: # test initialization setup = getattr(self, 'setup_' + name, None) if setup: logger.info("setup %s: n=%d s=%d", name, n, s) try: setup(n, s) except SkipTest, e: logger.info("skipping %s (n=%d s=%d): %s", name, n, s, e) continue # test run results = [] for i in xrange(opt.repeats): logger.info("test %s (n=%d s=%s) run %d of %d", name, n, s, i+1, opt.repeats) results.append(f(n, s)) logger.info("result: %s", results[-1]) result = self.best_value(results) print self.__class__.__name__, name, n, s, result def best_value(self, results): """Take the best values from a list of results.""" return min(results) class SumRational(Benchmark): """Test the time used to perform sum(x) for mpq and decimal data types. The type represent the same values. """ _title = "Time for sum() for values with scale %s" xlabel = "Numbers size (in decimal digits)" ylabel = "Time (in millis)" @property def title(self): return self._title % self.opt.scale def setup_numeric(self, n, s): self._setup(n, s, "test_sum_rat_numeric", "create table test_sum_rat_numeric (n numeric(%s,%s));" % (s, self.opt.scale)) def test_numeric(self, n, s): return self._test("test_sum_rat_numeric") def setup_mpq(self, n, s): self._setup(n, s, "test_sum_rat_mpq", "create table test_sum_rat_mpq (n mpq);") def test_mpq(self, n, s): return self._test("test_sum_rat_mpq") def _setup(self, n, s, table, query): cur = self.conn.cursor() cur.execute("drop table if exists %s;" % table) cur.execute(query) cur.execute(""" select randinit(); select randseed(31415926); insert into %s select urandomm(%%(max)s::mpz)::mpq / %%(scale)s from generate_series(1, %%(n)s); """ % table, { 'max': 10 ** s, 'scale': 10 ** self.opt.scale, 'n': n}) cur.execute("vacuum analyze %s;" % table) def _test(self, table): cur = self.conn.cursor() cur.execute("explain analyze select sum(n) from %s;" % table) recs = cur.fetchall() return float(recs[-1][0].split()[-2]) class SumInteger(Benchmark): """Test the time used to perform sum(n) for different data types. n is read from a table. """ title = "Time to calculate sum() on a table" xlabel = "Numbers size (in decimal digits)" ylabel = "Time (in millis)" def setup_mpq(self, n, s): self._setup(n, s, "test_sum_mpq", "create table test_sum_mpq (n mpq);") def setup_mpz(self, n, s): self._setup(n, s, "test_sum_mpz", "create table test_sum_mpz (n mpz);") def setup_numeric(self, n, s): self._setup(n, s, "test_sum_numeric", "create table test_sum_numeric (n numeric);") def setup_int8(self, n, s): if s > 18: raise SkipTest("skipping test with %d digits" % s) self._setup(n, s, "test_sum_int8", "create table test_sum_int8 (n int8);") def setup_int4(self, n, s): if s > 9: raise SkipTest("skipping test with %d digits" % s) self._setup(n, s, "test_sum_int4", "create table test_sum_int4 (n int4);") def _setup(self, n, s, table, query): cur = self.conn.cursor() cur.execute("drop table if exists %s;" % table) cur.execute(query) cur.execute(""" select randinit(); select randseed(31415926); insert into %s select urandomm(%%(max)s::mpz)s from generate_series(1, %%(n)s); """ % table, { 'max': 10 ** s, 'n': n}) cur.execute("vacuum analyze %s;" % table) def test_mpq(self, n, s): return self._test('test_sum_mpq') def test_mpz(self, n, s): return self._test('test_sum_mpz') def test_numeric(self, n, s): return self._test('test_sum_numeric') def test_int8(self, n, s): return self._test('test_sum_int8') def test_int4(self, n, s): return self._test('test_sum_int4') def _test(self, table): cur = self.conn.cursor() cur.execute("explain analyze select sum(n) from %s;" % table) recs = cur.fetchall() return float(recs[-1][0].split()[-2]) class Arith(Benchmark): """Perform an operation sum(a + b * c / d) on a table. """ title = "Performance on arithmetic operations" xlabel = "Numbers size (in decimal digits)" ylabel = "Time (in millis)" def setup_mpq(self, n, s): self._setup(n, s, "test_arith_mpq", """ create table test_arith_mpq (a mpq, b mpq, c mpq, d mpq);""") def setup_mpz(self, n, s): self._setup(n, s, "test_arith_mpz", """ create table test_arith_mpz (a mpz, b mpz, c mpz, d mpz);""") def setup_numeric(self, n, s): self._setup(n, s, "test_arith_numeric", """ create table test_arith_numeric ( a numeric(%s), b numeric(%s), c numeric(%s), d numeric(%s)); """ % ((s,s,s,s+1))) def setup_int8(self, n, s): if s > 9: raise SkipTest("skipping test with %d digits" % s) self._setup(n, s, "test_arith_int8", """ create table test_arith_int8 (a int8, b int8, c int8, d int8);""") def setup_int4(self, n, s): if s > 4: raise SkipTest("skipping test with %d digits" % s) self._setup(n, s, "test_arith_int4", """ create table test_arith_int4 (a int4, b int4, c int4, d int4);""") def test_mpq(self, n, s): return self._test("test_arith_mpq") def test_mpz(self, n, s): return self._test("test_arith_mpz") def test_numeric(self, n, s): return self._test("test_arith_numeric") def test_int8(self, n, s): return self._test("test_arith_int8") def test_int4(self, n, s): return self._test("test_arith_int4") def _setup(self, n, s, table, query): cur = self.conn.cursor() cur.execute("drop table if exists %s;" % table) cur.execute(query) cur.execute(""" select randinit(); select randseed(31415926); insert into %s select urandomm(%%(max)s::mpz), urandomm(%%(max)s::mpz), urandomm(%%(max)s::mpz), urandomm(%%(max)s::mpz) + 1 from generate_series(1, %%(n)s); """ % table, { 'max': 10 ** s, 'n': n}) cur.execute("vacuum analyze %s;" % table) def _test(self, table): cur = self.conn.cursor() cur.execute("""explain analyze select sum(a + b * c / d) from %s;""" % table) recs = cur.fetchall() return float(recs[-1][0].split()[-2]) class Factorial(Benchmark): """Measure the speed to calculate the factorial of n""" title = "Time to calculate n!" xlabel = "Input number" ylabel = "Time (in millis)" def setup_mpz(self, n, s): self._setup('mpz', 'mpz_mul') def setup_numeric(self, n, s): self._setup('numeric', 'numeric_mul') def _setup(self, type, f): cur = self.conn.cursor() cur.execute("drop aggregate if exists m(%s);" % type) cur.execute("create aggregate m(%s) (sfunc=%s, stype=%s);" % (type, f, type)) def test_mpz(self, n, s): return self._test('mpz', s) def test_numeric(self, n, s): return self._test('numeric', s) def _test(self, type, s): cur = self.conn.cursor() cur.execute(""" explain analyze select m(n::%s) from generate_series(1,%s) n; """ % (type, s)) recs = cur.fetchall() return float(recs[-1][0].split()[-2]) class TableSize(Benchmark): """Measure the size of a table on disk with mpz and decimal columns. """ title = "Size of a table on disk" xlabel = "Numbers size (in decimal digits)" ylabel = "Size (in pages)" def setup_int8(self, n, s): if s > 18: raise SkipTest("skipping test with %d digits" % s) def setup_int4(self, n, s): if s > 9: raise SkipTest("skipping test with %d digits" % s) def test_mpq(self, n, s): return self._test(n, s, "test_size_mpq", """ create table test_size_mpq (n mpq); insert into test_size_mpq select urandomm(%(max)s::mpz)s from generate_series(1, %(n)s); """) def test_mpz(self, n, s): return self._test(n, s, "test_size_mpz", """ create table test_size_mpz (n mpz); insert into test_size_mpz select urandomm(%(max)s::mpz)s from generate_series(1, %(n)s); """) def test_numeric(self, n, s): return self._test(n, s, "test_size_numeric", """ create table test_size_numeric (n numeric); insert into test_size_numeric select urandomm(%(max)s::mpz)s from generate_series(1, %(n)s); """) def test_int8(self, n, s): return self._test(n, s, "test_size_int8", """ create table test_size_int8 (n int8); insert into test_size_int8 select urandomm(%(max)s::mpz)s from generate_series(1, %(n)s); """) def test_int4(self, n, s): return self._test(n, s, "test_size_int4", """ create table test_size_int4 (n int4); insert into test_size_int4 select urandomm(%(max)s::mpz)s from generate_series(1, %(n)s); """) def _test(self, n, s, table, query): cur = self.conn.cursor() cur.execute(""" drop table if exists %s; select randinit(); select randseed(31415926); """ % table) cur.execute(query, {'n': n, 'max': 10 ** s}) cur.execute("vacuum analyze %s;" % table) cur.execute( "select relpages from pg_class where relname = %s;" , (table, )) return cur.fetchone()[0] def main(): opt = parse_args() cls = globals()[opt.test_name] test = cls(opt) test.run() def parse_args(): # Find the tests available test_names = sorted(o.__name__ for o in globals().values() if isinstance(o, type) and issubclass(o, Benchmark) and o is not Benchmark) from optparse import OptionParser parser = OptionParser(usage="%prog [OPTIONS] TEST-NAME", description="choose a test from: %s" % ', '.join(test_names)) parser.add_option('-n', '--nsamples', type=int, action='append', help="number of numbers to sum. specify once or more") parser.add_option('-s', '--size', type=int, action='append', help="size of numbers to sum. specify once or more") parser.add_option('-p', '--scale', type=int, help="scale of the tested numbers, if applicable") parser.add_option('-r', '--repeats', type=int, default=3, help="test repetitions (take the best value) [default: %default]") parser.add_option('--dsn', help="database to connect", default="") opt, args = parser.parse_args() if len(args) != 1: parser.error("please specify one test") opt.test_name = args[0] if opt.test_name not in test_names: parser.error("bad test name: '%s'" % opt.test_name) if not opt.nsamples or not opt.size: parser.error("please specify -n and -s at least once") return opt if __name__ == '__main__': sys.exit(main()) pgmp-rel-1.0.4/bench/plot_result.py000077500000000000000000000026221361705073600172610ustar00rootroot00000000000000#!/usr/bin/env python """Plot a result from benchmark.py Copyright (C) 2011-2020 - Daniele Varrazzo """ import sys from collections import defaultdict import matplotlib.pyplot as plt tests = defaultdict(list) nsamples = cls_name = None # Read from a file or stdin if no file provided f = len(sys.argv) > 1 and open(sys.argv[1]) or sys.stdin labels = {} for line in f: if line.isspace(): continue if ':' in line: tokens = line.split(':', 1) labels[tokens[0].strip()] = tokens[1].strip() continue tokens = line.split() # Parse the class of the test if cls_name is None: cls_name = tokens[0] assert cls_name == tokens[0], (cls_name, tokens) # Parse the number of samples if nsamples is None: nsamples = tokens[2] assert nsamples == tokens[2], (nsamples, tokens) tests[tokens[1]].append( (int(tokens[3]), float(tokens[4]))) fig = plt.figure() ax = fig.add_subplot(111) for label, data in sorted(tests.items()): data = list(zip(*data)) # transpose p = ax.plot(data[0], data[1], 'o-', label=label) title = labels.get('title', '') if int(nsamples): title += " (n=%s)" % nsamples ax.set_title(title) if 'xlabel' in labels: ax.set_xlabel(labels['xlabel']) if 'ylabel' in labels: ax.set_ylabel(labels['ylabel']) ax.legend(loc=2) if '-o' in sys.argv: plt.savefig(sys.argv[sys.argv.index('-o') + 1], dpi=72) else: plt.show() pgmp-rel-1.0.4/docs/000077500000000000000000000000001361705073600141775ustar00rootroot00000000000000pgmp-rel-1.0.4/docs/Makefile000066400000000000000000000027061361705073600156440ustar00rootroot00000000000000# pgmp -- documentation makefile # # Use 'make env' once, then 'make html' to build the HTML documentation. # You can use PYTHON=python3.6 to use a different Python version to build. # # Copyright (C) 2011-2020 Daniele Varrazzo ENV_DIR = $(shell pwd)/env ENV_BIN = $(ENV_DIR)/bin ENV_LIB = $(ENV_DIR)/lib SPHINXOPTS = SPHINXBUILD = $(ENV_BIN)/sphinx-build PAPER = BUILDDIR = . PYTHON ?= python PLOTS = $(patsubst ../bench/%.txt,html/img/%.png,$(sort $(wildcard ../bench/*.txt))) .PHONY: env html upload clean default: html html: $(PLOTS) html/.gitignore html/.nojekill $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) \ . $(BUILDDIR)/html html/img/%.png: ../bench/%.txt mkdir -p html/img $(ENV_BIN)/python ../bench/plot_result.py $< -o $@ # The environment is currently required to build the documentation. # It is not clean by 'make clean' env: virtualenv -p $(PYTHON) $(ENV_DIR) $(ENV_BIN)/pip install -r requirements.txt # Clone the github pages repository to update # Fail silently if this is not a git repos (upload won't work of course) git status 2>/dev/null \ && git clone -b gh-pages \ $$(git remote show -n origin | awk '/URL/ {print $$NF; exit}') \ html \ || true html/.gitignore: html-gitignore cp $< $@ html/.nojekill: touch $@ upload: cd html && test -d .git \ && git add --all \ && git commit -m "website update on $$(date)" \ && git push clean: # Do not delete .gitignore and .git directories $(RM) -r html/* pgmp-rel-1.0.4/docs/_static/000077500000000000000000000000001361705073600156255ustar00rootroot00000000000000pgmp-rel-1.0.4/docs/_static/pgmp.css000066400000000000000000000013661361705073600173100ustar00rootroot00000000000000@import url("agogo.css"); /* Tables */ table.docutils { border: 0; border-collapse: collapse; margin-top: 1em; margin-bottom: 1em; } table.docutils td, table.docutils th { padding: 1px 8px 1px 5px; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.docutils td { text-align: left; } table.docutils th { background-color; #eeeeec; } /* Less vertical margin around lists */ html div.document ul { margin: 0.5em 1.5em; } /* Less vertical spacing in the examples */ div.highlight { line-height: 1em; } /* The title justified is awful */ div.body h1 { text-align: left; line-height: 100%; } /* definitions in bold */ dl.goals dt { font-weight: bold; } pgmp-rel-1.0.4/docs/conf.py000066400000000000000000000204501361705073600154770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # PostgreSQL Multiple Precision Arithmetic documentation build configuration file, created by # sphinx-quickstart on Thu Mar 10 01:09:43 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os, re, shutil # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('lib')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'issue_role'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'PostgreSQL Multiple Precision Arithmetic' copyright = u'2011-2020, Daniele Varrazzo' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # The full version, including alpha/beta/rc tags. try: release = re.search(r'"version"\s*:\s*"([^"]+)"', open('../META.json').read()).group(1) except: release = '1.0' # fallback # The short X.Y version. version = re.match(r'\d+\.\d+(?:\.\d+)?', release).group() # Pattern to generate links to the bug tracker issue_url = 'https://github.com/dvarrazzo/pgmp/issues/%s' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['env', 'html'] # The reST default role (used for this markup: `text`) to use for all documents. default_role = 'obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # Plugins configuration todo_include_todos = True mathjax_path = 'https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=Accessible' jsmath_path = 'jsMath/easy/load.js' # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'agogo' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = dict( pagewidth='60em', documentwidth='40em', sidebarwidth='15em', ) # The stylesheet to use with HTML output: this will include the original one # adding a few classes. html_style = 'pgmp.css' # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # the pgxs Makefile flattens all the files into the doc dir. # Put them back in their place. if not os.path.isdir(html_static_path[0]): os.makedirs(html_static_path[0]) for fn in [html_style]: if not os.path.exists(os.path.join(html_static_path[0], fn)): shutil.copy(fn, html_static_path[0]) # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'PostgreSQLMultiplePrecisionArithmeticdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'PostgreSQLMultiplePrecisionArithmetic.tex', u'PostgreSQL Multiple Precision Arithmetic Documentation', u'Daniele Varrazzo', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'postgresqlmultipleprecisionarithmetic', u'PostgreSQL Multiple Precision Arithmetic Documentation', [u'Daniele Varrazzo'], 1) ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/': None} pgmp-rel-1.0.4/docs/goals.rst000066400000000000000000000030711361705073600160370ustar00rootroot00000000000000.. _goals: Goals of the project ==================== If you wonder what is this project for, here is a loose set of goals and achievements. .. cssclass:: goals Learning more about PostgreSQL internals The project has been an occasion to learn something more about PostgreSQL internals and more advanced topics: memory allocation in the server, definition of complete arithmetic data types and their operators. This goal has been completely fulfilled. Providing and edge on other databases As far as I know no other database provides rational numbers nor the wide range of mathematical functions exposed by the GMP. Having them directly available into the database makes PostgreSQL a good choice in environments where these functions are of use, such as in mathematical and cryptography research. Here instead is a list of *non-goals*: .. cssclass:: goals An extension to be used by everybody For almost every user the performance offered by the `!numeric` data type are perfectly acceptable and the extra performance provided by `!mpz` may not justify the use of an external library. Replacing PostgreSQL builtin numerical types GMP data type are not constrained by the semantic specified by SQL, so they will hardly replace any standard data type. Furthermore the GMP (as well as the pgmp extension) is distributed with LGPL license, which is perfectly compatible with the PostgreSQL terms of distribution but likely to not be well accepted by the PostgreSQL developers who usually prefer BSD-style licenses. pgmp-rel-1.0.4/docs/html-gitignore000066400000000000000000000001021361705073600170440ustar00rootroot00000000000000.buildinfo .doctrees/ _static/jquery-*.js _static/underscore-*.js pgmp-rel-1.0.4/docs/index.rst000066400000000000000000000032441361705073600160430ustar00rootroot00000000000000PostgreSQL Multiple Precision Arithmetic ======================================== pgmp_ is a PostgreSQL_ extension to add the GMP_ data types and functions directly into the database. .. _pgmp: https://dvarrazzo.github.io/pgmp/ .. _PostgreSQL: https://www.postgresql.org .. _GMP: https://www.gmplib.org The extension adds the types `mpz` (arbitrary size integers) and `mpq` (arbitrary precision rationals) to PostgreSQL and exposes to the database all the functions available in the GMP library for these data types, allowing: - higher performance arithmetic on integers respect to the `!numeric` data type - a rational data type for absolute precision storage and arithmetic - using specialized functions to deal with prime numbers, random numbers, factorization directly into the database. Here you can find a more detailed `list of goals `__ for this extension. See the `performance `__ page for comparison tests between GMP data types and PostgreSQL builtins. `[Ab]using PostgreSQL to calculate pi `__, a pgmp demo showing how to generate millions of digits of :math:`\pi` using PL/pgSQL and Python to run concurrent backends. The library is released under `LGPL License `__. References ---------- - Homepage: https://dvarrazzo.github.io/pgmp/ - Project page: https://github.com/dvarrazzo/pgmp/ - Download: https://pgxn.org/dist/pgmp/ Documentation ------------- .. toctree:: :maxdepth: 2 install mpz mpq misc news Indices and tables ------------------ * :ref:`genindex` * :ref:`search` .. * :ref:`modindex` .. To Do List ---------- .. todolist:: pgmp-rel-1.0.4/docs/install.rst000066400000000000000000000062221361705073600164010ustar00rootroot00000000000000`!pgmp` extension installation ============================== Prerequisites ------------- `!pgmp` is currently compatible with: - PostgreSQL from version 8.4 - GMP from version 4.1 (tested with versions 4.1.4, 4.2.4, 4.3.2, 5.0.1, 6.1.2, 6.2.0). .. note:: GMP 4.1 doesn't implement a few functions (`rootrem()`, `combit()`, `randinit_mt()`) and the maximum base accepted by the I/O functions is 36, not 62. In order to build the library your system must have the server development files (on Debian systems usually packaged as ``postgresql-server-dev``) and regular UNIX development tools, such as :program:`make`. The :program:`pg_config` program should be available in the :envvar:`PATH`. If more than one PostgreSQL version is available on the system, the library will be built against the version of the first :program:`pg_config` found in the path. You may also override the selection specifying the :envvar:`PG_CONFIG` variable. Using the PGXN client --------------------- If the prerequsites are met you can use the `PGXN Client`__ to download, build, and install `!pgmp`, e.g.:: $ pgxn install pgmp $ pgxn load -d somedb pgmp .. __: https://pgxn.github.io/pgxnclient/ The further instructions are to build, test, and install the library without using the PGXN Client. Building the library -------------------- The library must be built and installed to be used with a database cluster: once it is built, SQL installation scripts can be used to install the data types and functions in one or more databases. To build and install the library: .. code-block:: console $ make $ sudo make install You can test the installation with: .. code-block:: console $ make installcheck (adjust the :envvar:`REGRESS_OPTS` variable to select a test database). .. note:: Because of the missing functions in GMP 4.1 (see Prerequisites_), a few tests are expected to fail with this library version. After running the test suite you may check the ``regression.diff`` file to verify that the only tests failed are the ones related to the missing functionalities and the different default random numbers gerenator algorithm. Installing the extension ------------------------ With PostgreSQL versions before 9.1, an install script called ``pgmp.sql`` is installed in the directory :samp:`{$sharedir}/pgmp`: just run the script into a database to install the provided types and functions. An uninstall script is also provided in the same directory to remove the installed objects. With PostgreSQL 9.1 the library is packaged as an extension: once built and installed in the cluster, use the command: .. code-block:: psql =# CREATE EXTENSION pgmp; to install it in a database. If your database was migrated from a previous PostgreSQL version, you can convert the `!pgmp` objects into a packaged extension using the command: .. code-block:: psql =# CREATE EXTENSION pgmp FROM unpackaged; In order to uninstall the extension you can use the ``DROP EXTENSION`` command. Please refer to `the documentation`__ for further informations about PostgreSQL extensions management. .. __: https://www.postgresql.org/docs/current/extend-extensions.html pgmp-rel-1.0.4/docs/lib/000077500000000000000000000000001361705073600147455ustar00rootroot00000000000000pgmp-rel-1.0.4/docs/lib/issue_role.py000066400000000000000000000024011361705073600174650ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ issue role ~~~~~~~~~~ An interpreted text role to link docs to issues. :copyright: Copyright 2013-2020 by Daniele Varrazzo. """ import re from docutils import nodes, utils from docutils.parsers.rst import roles def issue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): cfg = inliner.document.settings.env.app.config if cfg.issue_url is None: msg = inliner.reporter.warning( "issue not configured: please configure issue_url in conf.py" ) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] rv = [nodes.Text(name + ' ')] tokens = re.findall(r'(#?\d+)|([^\d#]+)', text) for issue, noise in tokens: if issue: num = int(issue.replace('#', '')) url = cfg.issue_url % num roles.set_classes(options) node = nodes.reference( issue, utils.unescape(issue), refuri=url, **options ) rv.append(node) else: assert noise rv.append(nodes.Text(noise)) return rv, [] def setup(app): app.add_config_value('issue_url', None, 'env') app.add_role('issue', issue_role) app.add_role('issues', issue_role) pgmp-rel-1.0.4/docs/misc.rst000066400000000000000000000010011361705073600156540ustar00rootroot00000000000000Miscellaneous functions ======================= .. function:: gmp_version() Return the version of the GMP library currently used as an integer. For example GMP 4.3.2 is reported as 40302. .. function:: gmp_max_bitcnt() Return the maximum value possible for the bit count as accepted or returned by functions dealing with bits. The value is the maximum `!unsigned long` defined on the server, so it can be :math:`2^{32}-1` or :math:`2^{64}-1` according to the server platform. pgmp-rel-1.0.4/docs/mpq.rst000066400000000000000000000150451361705073600155330ustar00rootroot00000000000000`!mpq` data type ================ The `!mpq` data type can store rational numbers whose denominator and numerator have arbitrary size. Rational numbers are converted in canonical form on input (meaning that the denominator and the numerator have no common factors) and all the operators will return a number in canonical form. PostgreSQL integer types (`!int16`, `!int32`, `!int64`), `!numeric` and `mpz` can be converted to `!mpq` without loss of precision and without surprise. Floating point types (`!float4`, `!float8`) are converted without loss as well... but with some surprise, as many fractions with finite decimal expansion have no finite expansion in binary. .. code-block:: psql =# select 10.1::numeric::mpq as "numeric", -# 10.1::float4::mpq as "single", -# 10.1::float8::mpq as "double"; numeric | single | double ---------+----------------+---------------------------------- 101/10 | 5295309/524288 | 5685794529555251/562949953421312 `!mpq` values can be converted to integer types (both PostgreSQL's and `!mpz`): the result will be truncated. Conversion to `!float4` and `!float8` will round the values to the precision allowed by the types (in case of overflow the value will be *Infinity*). Conversion to `!numeric` will perform a rounding to the precision set for the target type. .. code-block:: psql =# select mpq('4/3')::integer as "integer", -# mpq('4/3')::float4 as "single", -# mpq('4/3')::decimal(10,3) as "decimal"; integer | single | decimal ---------+---------+--------- 1 | 1.33333 | 1.333 `!mpq` values can be compared using the regular PostgreSQL comparison operators. Indexes on `!mpq` columns can be created using the *btree* or the *hash* method. `!mpq` textual input/output --------------------------- .. function:: mpq(text) mpq(text, base) Convert a textual representation into an `!mpq` number. The form :samp:`{text}::mpq` is equivalent to :samp:`mpq({text})`. The string can be an integer like ``41`` or a fraction like ``41/152``. The fraction will be converted in canonical form, so common factors between denominator and numerator will be removed. The numerator and optional denominator are parsed the same as in `mpz`. White space is allowed in the string, and is simply ignored. The base can vary from 2 to 62, or if base is 0 then the leading characters are used: ``0x`` or ``0X`` for hex, ``0b`` or ``0B`` for binary, ``0`` for octal, or decimal otherwise. Note that this is done separately for the numerator and denominator, so for instance ``0xEF/100`` is 239/100, whereas ``0xEF/0x100`` is 239/256. .. note:: The maximum base accepted by GMP 4.1 is 36, not 62. .. function:: text(q) text(q, base) Convert the `!mpq` *q* into a string. The form :samp:`{q}::text` is equivalent to :samp:`text({q})`. The string will be of the form :samp:`{num}/{den}`, or if the denominator is 1 then just :samp:`{num}`. *base* may vary from 2 to 62 or from -2 to -36. For base in the range 2..36, digits and lower-case letters are used; for -2..-36, digits and upper-case letters are used; for 37..62, digits, upper-case letters, and lower-case letters (in that significance order) are used. If *base* is not specified, 10 is assumed. .. note:: The maximum base accepted by GMP 4.1 is 36, not 62. `!mpq` conversions ------------------ .. function:: mpq(num, den) Return an `!mpq` from its numerator and denominator. .. note:: The function signature accepts `!mpz` values. PostgreSQL integers are implicitly converted to `!mpz` so invoking the function as ``mpq(30,17)`` will work as expected. However if the numbers become too big for an `int8` they will be interpreted by PostgreSQL as `!numeric` and, because the cast from `!numeric` to `!mpz` is not implicit, the call will fail. Forcing a cast to `!mpz` (*e.g.* ``mpq(30::mpz,17::mpz)``) will work for numbers of every size. .. function:: num(q) den(q) Return the numerator or the denominator of *q* as `!mpz`. Arithmetic Operators and Functions ---------------------------------- All the arithmetic operators and functions return their their output in canonical form. .. table:: Arithmetic operators =========== =============================== =========================== =========== Operator Description Example Return =========== =============================== =========================== =========== `!-` Unary minus `!- '4/3'::mpq` -4/3 `!+` Unary plus `!+ '4/3'::mpq` 4/3 `!+` Addition `!'2/3'::mpq + '5/6'::mpq` 3/2 `!-` Subtraction `!'1/3'::mpq - '5/6'::mpq` -1/2 `!*` Multiplication `!'2/3'::mpq * '5/6'::mpq` 5/9 `!/` Division `!'2/3'::mpq / '5/6'::mpq` 4/5 `!<<` Multiplication by :math:`2^n` `!'2/3'::mpq << 3` 16/3 `!>>` Division by :math:`2^n` `!'2/3'::mpq >> 3` 1/12 =========== =============================== =========================== =========== .. function:: abs(q) Return the absolute value of *q*. .. function:: inv(q) Return 1/*q*. .. function:: limit_den(q, max_den=1000000) Return the closest rational to *q* with denominator at most *max_den*. The function is useful for finding rational approximations to a given floating-point number: .. code-block:: psql =# select limit_den(pi(), 10); 22/7 or for recovering a rational number that's represented as a float: .. code-block:: psql =# select mpq(cos(pi()/3)); 4503599627370497/9007199254740992 =# select limit_den(cos(pi()/3)); 1/2 =# select limit_den(10.1::float4); 101/10 This function is not part of the GMP library: it is ported instead `from the Python library`__. .. __: https://docs.python.org/library/fractions.html#fractions.Fraction.limit_denominator Aggregation functions --------------------- .. function:: sum(q) Return the sum of *q* across all input values. .. function:: prod(q) Return the product of *q* across all input values. .. function:: max(q) Return the maximum value of *q* across all input values. .. function:: min(q) Return the minimum value of *q* across all input values. pgmp-rel-1.0.4/docs/mpz.rst000066400000000000000000000571321361705073600155470ustar00rootroot00000000000000`!mpz` data type ================ The `!mpz` data type can store integer numbers of arbitrary size, up to the maximum memory allowed by PostgreSQL data types (about 1GB). Every PostgreSQL integer type (`!int16`, `!int32`, `!int64`) can be converted to `!mpz`. Not integer types (`!float4`, `!float8`, `!numeric`) are truncated. Cast from integer types are automatic, whereas non integer require explicit cast (but are implicitly converted in assignment). .. code-block:: postgres SELECT mpz(10000); -- Cast using mpz as a function SELECT 10000::mpz; -- PostgreSQL-style cast Casting `!mpz` to PostgreSQL integer types and `!numeric` works as expected and will overflow if the value is too big for their range. Casting to `!float4` and `!float8` works as well: in case of overflow the value will be *Infinity*. `!mpz` values can be compared using the regular PostgreSQL comparison operators. Indexes on `!mpz` columns can be created using the *btree* or the *hash* method. `!mpz` textual input/output --------------------------- .. function:: mpz(text) mpz(text, base) Convert a textual representation into an `!mpz` number. The form :samp:`{text}::mpz` is equivalent to :samp:`mpz({text})`. White space is allowed in the string, and is simply ignored. The *base* may vary from 2 to 62, or if *base* is 0 or omitted, then the leading characters are used: ``0x`` and ``0X`` for hexadecimal, ``0b`` and ``0B`` for binary, ``0`` for octal, or decimal otherwise. For bases up to 36, case is ignored; upper-case and lower-case letters have the same value. For bases 37 to 62, upper-case letter represent the usual 10..35 while lower-case letter represent 36..61. .. code-block:: psql =# SELECT '0x10'::mpz AS "hex", '10'::mpz AS "dec", -# '010'::mpz AS "oct", '0b10'::mpz AS "bin"; hex | dec | oct | bin -----+-----+-----+----- 16 | 10 | 8 | 2 .. note:: The maximum base accepted by GMP 4.1 is 36, not 62. .. function:: text(z) text(z, base) Convert the `!mpz` *z* into a string. The form :samp:`{z}::text` is equivalent to :samp:`text({z})`. *base* may vary from 2 to 62 or from -2 to -36. For base in the range 2..36, digits and lower-case letters are used; for -2..-36, digits and upper-case letters are used; for 37..62, digits, upper-case letters, and lower-case letters (in that significance order) are used. If *base* is not specified, 10 is assumed. .. note:: The maximum base accepted by GMP 4.1 is 36, not 62. Arithmetic Operators and Functions ---------------------------------- These operators can either work on `!mpz` arguments or take an integer argument that will be implicitly converted. Operators taking a :math:`2^n` argument always use an integer as right argument. .. note:: GMP defines many structures in terms of `!long` or `!unsigned long`, whose definitions may vary across platforms. PostgreSQL instead offers data types with a defined number of bytes (e.g. `!int4`, `!int8`). For this reason, functions taking an integer as argument are defined as `!int8`, but they may actually fail if the server is 32 bit and the argument doesn't fit into an `!int4`. .. table:: Arithmetic operators =========== =============================== =================== =========== Operator Description Example Return =========== =============================== =================== =========== `!-` Unary minus `!- 5::mpz` -5 `!+` Unary plus `!+ 5::mpz` 5 `!+` Addition `!2::mpz + 3::mpz` 5 `!-` Subtraction `!2::mpz - 3::mpz` -1 `!*` Multiplication `!7::mpz * 3::mpz` 21 `!<<` Multiplication by :math:`2^n` `!3::mpz << 2` 12 `!^` Power (1) `!3::mpz ^ 2` 9 =========== =============================== =================== =========== Notes: (1) See also the `exponentiation functions`_. .. function:: abs(z) Return the absolute value of *z*. .. function:: sgn(z) Return +1 if *z* > 0, 0 if *z* = 0, and -1 if *z* < 0. .. function:: odd(z) even(z) Return `!true` if *z* is odd or even, respectively, else `!false`. Division Operators and Functions -------------------------------- For all the division-related operators :math:`n \oslash d`, :math:`q` and :math:`r` will satisfy :math:`n = q \cdot d + r`, and :math:`r` will satisfy :math:`0 \le |r| \lt |d|`. .. note:: Only the truncating division and reminder (`!/` and `!%`) have the correct precedence respect to addition, subtraction and multiplication. See `the PostgreSQL precedence table`__ for further details. .. __: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-PRECEDENCE-TABLE .. note: this table contains non-breaking spaces to align the - signs. .. table:: Division operators =========== =============================== ==================== ======= Operator Description Example Return =========== =============================== ==================== ======= `!/` Division quotient `! 7::mpz / 3::mpz` 2 Rounding towards zero `!-7::mpz / 3::mpz` -2 `!%` Division reminder `! 7::mpz % 3::mpz` 1 Rounding towards zero `!-7::mpz % 3::mpz` -1 `+/` Division quotient `! 7::mpz +/ 3::mpz` 3 Rounding towards +infinity `!-7::mpz +/ 3::mpz` -2 `+%` Division reminder `! 7::mpz +% 3::mpz` -2 Rounding towards +infinity `!-7::mpz +% 3::mpz` -1 `!-/` Division quotient `! 7::mpz -/ 3::mpz` 2 Rounding towards -infinity `!-7::mpz -/ 3::mpz` -3 `!-%` Division reminder `! 7::mpz -% 3::mpz` 1 Rounding towards -infinity `!-7::mpz -% 3::mpz` 2 `/?` Divisible (1) `!21::mpz /? 7::mpz` `!true` `/!` Exact division (2) `!21::mpz /! 7::mpz` 3 =========== =============================== ==================== ======= Notes: (1) See also the function `divisible()`. (2) The exact division operator (`!/!`) produces correct results only when it is known in advance that :math:`d` divides :math:`n`. The operator is much faster than the other division operators, and is the best choice when exact division is known to occur, for example reducing a rational to lowest terms. .. note: this table contains non-breaking spaces to align the - signs. .. table:: Division operators for powers of 2 ======== ==================================== =================== ======= Operator Description Example Return ======== ==================================== =================== ======= `!>>` Quotient of division by :math:`2^n` `! 1027::mpz >> 3` 128 Rounding towards zero `!-1027::mpz >> 3` -128 `!%>` Remainder of division by :math:`2^n` `! 1027::mpz %> 3` 3 Rounding towards zero `!-1027::mpz %> 3` -3 `!+>>` Quotient of division by :math:`2^n` `! 1027::mpz +>> 3` 129 Rounding towards +infinity `!-1027::mpz +>> 3` -128 `!+%>` Remainder of division by :math:`2^n` `! 1027::mpz +%> 3` -5 Rounding towards +infinity `!-1027::mpz +%> 3` -3 `!->>` Quotient of division by :math:`2^n` `! 1027::mpz ->> 3` 128 Rounding towards -infinity `!-1027::mpz ->> 3` -129 `!-%>` Remainder of division by :math:`2^n` `! 1027::mpz -%> 3` 3 Rounding towards -infinity `!-1027::mpz -%> 3` 5 `>>?` Divisible by :math:`2^n` (1) `!64::mpz >>? 3` `!true` ======== ==================================== =================== ======= (1) See also the function `divisible_2exp()`. .. function:: tdiv_qr(n, d) Return a tuple containing quotient *q* and remainder *r* of the division, rounding towards 0. .. function:: cdiv_qr(n, d) Return a tuple containing quotient *q* and remainder *r* of the division, rounding towards +infinity (ceil). .. function:: fdiv_qr(n, d) Return a tuple containing quotient *q* and remainder *r* of the division, rounding towards -infinity (floor). .. function:: divisible(n, d) divisible_2exp(n, b) Return `!true` if *n* is exactly divisible by *d*, or in the case of `!divisible_2exp()` by :math:`2^b`. :math:`n` is divisible by :math:`d` if there exists an integer :math:`q` satisfying :math:`n = q \cdot d`. Unlike the other division operators, *d*\=0 is accepted and following the rule it can be seen that only 0 is considered divisible by 0. The operators `!/?` and `!>>?` are aliases for `!divisible()` and `!divisible_2exp()`. .. function:: congruent(n, c, d) congruent_2exp(n, c, b) Return `!true` if *n* is congruent to *c* modulo *d*, or in the case of `!congruent_2exp()` modulo :math:`2^b`. :math:`n` is congruent to :math:`c \mod d` if there exists an integer :math:`q` satisfying :math:`n = c + q \cdot d`. Unlike the other division operators, *d*\=0 is accepted and following the rule it can be seen that n and c are considered congruent mod 0 only when exactly equal. Exponentiation Functions ------------------------ .. function:: pow(base, exp) Return *base* raised to *exp*. *exp* is defined as `!int8` but must fit into a `!long` as defined on the server. The function is an alias for the `!^` operator. .. function:: powm(base, exp, mod) Return (*base* raised to *exp*) modulo *mod*. Negative *exp* is supported if an inverse *base^-1* mod *mod* exists (see `invert()` function). If an inverse doesn't exist then a divide by zero is raised. Root Extraction Functions ------------------------- .. function:: root(op, n) Return the truncated integer part of the *n*\th root of *op*. *n* is defined as `!int8` but must fit into a `!long` as defined on the server. .. function:: rootrem(op, n) Return a tuple of 2 elements with the truncated integer part of the *n*\th root of *op* and the remainder (*i.e.* *op* - *root* ^ *n*). .. code-block:: psql =# select * from rootrem(28, 3); root | rem ------+----- 3 | 1 .. note:: The function is not available on GMP version < 4.2. .. function:: sqrt(op) Return the truncated integer part of the square root of *op*. .. function:: sqrtrem(op) Return a tuple of 2 elements with the truncated integer part of the square root of *op* and the remainder (*i.e.* *op* - *root* \* *root*). .. code-block:: psql =# select * from sqrtrem(83); root | rem ------+----- 9 | 2 .. function:: perfect_power(op) Return `!true` if *op* is a perfect power, *i.e.*, if there exist integers :math:`a` and :math:`b`, with :math:`b>1`, such that *op* equals :math:`a^b`. Under this definition both 0 and 1 are considered to be perfect powers. Negative values of op are accepted, but of course can only be odd perfect powers. .. function:: perfect_square(op) Return `!true` if *op* is a perfect square, *i.e.*, if the square root of *op* is an integer. Under this definition both 0 and 1 are considered to be perfect squares. Number Theoretic Functions -------------------------- .. function:: probab_prime(n, reps) Determine whether *n* is prime. Return 2 if *n* is definitely prime, return 1 if *n* is probably prime (without being certain), or return 0 if *n* is definitely composite. This function does some trial divisions, then some `Miller-Rabin probabilistic primality tests`__. *reps* controls how many such tests are done, 5 to 10 is a reasonable number, more will reduce the chances of a composite being returned as "probably prime". .. __: https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin and similar tests can be more properly called compositeness tests. Numbers which fail are known to be composite but those which pass might be prime or might be composite. Only a few composites pass, hence those which pass are considered probably prime. .. seealso:: `Primality test `__ .. function:: nextprime(op) Return the next prime greater than *op*. This function uses a probabilistic algorithm to identify primes. For practical purposes it's adequate, the chance of a composite passing will be extremely small. .. function:: gcd(a, b) Return the greatest common divisor of *a* and *b*. The result is always positive even if one or both input operands are negative. .. function:: gcdext(a, b) Return the greatest common divisor *g* of *a* and *b*, and in addition coefficients *s* and *t* satisfying :math:`a \cdot s + b \cdot t = g`. The value *g* is always positive, even if one or both of *a* and *b* are negative. The values *s* and *t* are chosen such that :math:`|s| \le |b| \hspace{0em}` and :math:`|t| \le |a| \hspace{0em}`. .. The \hspace{} are there to avoid the vim rest syntax highlighter to get crazy. .. code-block:: psql =# select * from gcdext(6, 15); g | s | t ---+----+--- 3 | -2 | 1 .. function:: lcm(a, b) Return the least common multiple of *a* and *b*. The value returned is always positive, irrespective of the signs of *a* and *b*. The return will be zero if either *a* or *b* is zero. .. function:: fac(op) Return *op*\!, the factorial of *op*. .. function:: bin(n, k) Return the `binomial coefficient`__ :math:`{n \choose k}`. Negative values of *n* are supported, using the identity :math:`{-n \choose k} = (-1)^k {n+k-1 \choose k}`. .. __: https://en.wikipedia.org/wiki/Binomial_coefficient .. function:: fib(n) fib2(n) `!fib()` returns :math:`F_n`, the *n*\th `Fibonacci number`__. `!fib2()` returns :math:`F_n` and :math:`F_{n-1}`. .. __: https://en.wikipedia.org/wiki/Fibonacci_number These functions are designed for calculating isolated Fibonacci numbers. When a sequence of values is wanted it's best to start with `!fib2()` and iterate the defining :math:`F_{n+1}=F_n+F_{n-1}` or similar. .. function:: lucnum(n) lucnum2(n) `!lucnum()` returns :math:`L_n`, the *n*\th `Lucas number`__. `!lucnum2()` returns :math:`L_n` and :math:`L_{n-1}`. .. __: https://en.wikipedia.org/wiki/Lucas_number These functions are designed for calculating isolated Lucas numbers. When a sequence of values is wanted it's best to start with `!lucnum2()` and iterate the defining :math:`L_{n+1}=L_n+L_{n-1}` or similar. The Fibonacci numbers and Lucas numbers are related sequences, so it's never necessary to call both `!fib2()` and `!lucnum2()`. The formulas for going from Fibonacci to Lucas can be found in `Lucas Numbers Algorithm`__, the reverse is straightforward too. .. __: https://gmplib.org/manual/Lucas-Numbers-Algorithm.html .. function:: invert(a, b) Return the inverse of *a* modulo *b* if exists. The return value *r* will satisfy :math:`0 \le r \lt b`. If an inverse doesn't exist return `!NULL`. .. function:: jacobi(a, b) Calculate the `Jacobi symbol`__ :math:`(\frac{a}{b})`. This is defined only for *b* odd. .. __: https://en.wikipedia.org/wiki/Jacobi_symbol .. function:: legendre(a, p) Calculate the `Legendre symbol`__ :math:`(\frac{a}{p})`. This is defined only for *p* an odd positive prime, and for such *p* it's identical to the Jacobi symbol. .. __: https://en.wikipedia.org/wiki/Legendre_symbol .. function:: kronecker(a, b) Calculate the Jacobi symbol :math:`(\frac{a}{b})` with the Kronecker extension :math:`(\frac{a}{2})=(\frac{2}{a})` when *a* odd, or :math:`(\frac{a}{2})=0` when *a* even. .. seealso:: Section 1.4.2, Henri Cohen, "A Course in Computational Algebraic Number Theory", Graduate Texts in Mathematics number 138, Springer-Verlag, 1993. https://www.math.u-bordeaux.fr/~cohen/ Logical and Bit Manipulation Functions -------------------------------------- These functions behave as if twos complement arithmetic were used (although sign-magnitude is the actual implementation). The least significant bit is number 0. .. table:: Logical Operators ======== ======================== =================================== =================== Operator Description Example Return ======== ======================== =================================== =================== `!&` Bitwise and `!'0b10001'::mpz & '0b01001'::mpz` `!'0b1'::mpz` `!|` Bitwise inclusive-or `!'0b10001'::mpz | '0b01001'::mpz` `!'0b11001'::mpz` `!#` Bitwise exclusive-or `!'0b10001'::mpz # '0b01001'::mpz` `!'0b11000'::mpz` ======== ======================== =================================== =================== .. function:: com(op) Return the ones' complement of *op*. .. function:: popcount(op) If op>=0, return the population count of *op*, which is the number of 1 bits in the binary representation. If op<0, the number of 1s is infinite, and the return value is the largest possible, represented by `gmp_max_bitcnt()`. .. function:: hamdist(op1, op2) If *op1* and *op2* are both >=0 or both <0, return the `Hamming distance`__ between the two operands, which is the number of bit positions where *op1* and *op2* have different bit values. If one operand is >=0 and the other <0 then the number of bits different is infinite, and the return value is the largest possible, represented by `gmp_max_bitcnt()`. .. __: https://en.wikipedia.org/wiki/Hamming_distance .. function:: scan0(op, starting_bit) scan1(op, starting_bit) Scan *op*, starting from bit *starting_bit*, towards more significant bits, until the first 0 or 1 bit (respectively) is found. Return the index of the found bit. If the bit at *starting_bit* is already what's sought, then *starting_bit* is returned. If there's no bit found, then the largest possible bit count is returned (represented by `gmp_max_bitcnt()`). This will happen in `!scan0()` past the end of a negative number, or `!scan1()` past the end of a nonnegative number. .. function:: setbit(op, bit_index) Return *op* with bit *bit_index* set. .. function:: clrbit(op, bit_index) Return *op* with bit *bit_index* cleared. .. function:: combit(op, bit_index) Return *op* with bit *bit_index* complemented. .. note:: The function is not available on GMP version < 4.2. .. function:: tstbit(op, bit_index) Test bit *bit_index* in *op* and return 0 or 1 accordingly. Random number functions ----------------------- Sequences of pseudo-random numbers are generated using an internal per-session variable, which holds an algorithm selection and a current state. Such a variable must be initialized by a call to one of the `!randinit*()` functions, and can be seeded with the `randseed()` function. .. function:: randinit() Initialize the session random state with a default algorithm. This will be a compromise between speed and randomness, and is recommended for applications with no special requirements. Currently this is `randinit_mt()`. .. function:: randinit_mt() Initialize the session random state for a `Mersenne Twister`__ algorithm. This algorithm is fast and has good randomness properties. .. __: https://en.wikipedia.org/wiki/Mersenne_twister .. note:: The function is not available on GMP version < 4.2. .. function:: randinit_lc_2exp(a, c, e) Initialize the session random state with a `linear congruential`__ algorithm :math:`X = (a \cdot X + c) \mod 2^e`. .. __: https://en.wikipedia.org/wiki/Linear_congruential_generator The low bits of *X* in this algorithm are not very random. The least significant bit will have a period no more than 2, and the second bit no more than 4, etc. For this reason only the high half of each *X* is actually used. When a random number of more than :math:`e/2` bits is to be generated, multiple iterations of the recurrence are used and the results concatenated. .. function:: randinit_lc_2exp_size(s) Initialize the session random state for a linear congruential algorithm as per `randinit_lc_2exp()`. *a*, *c* and *e* are selected from a table, chosen so that size bits (or more) of each *X* will be used, ie. :math:`e/2 \ge s`. The function fails if *s* is bigger than the table data provides. The maximum size currently supported is 128. .. function:: randseed(seed) Set an initial seed value into session random state. The size of a seed determines how many different sequences of random numbers is possible to generate. The "quality" of the seed is the randomness of a given seed compared to the previous seed used, and this affects the randomness of separate number sequences. The method for choosing a seed is critical if the generated numbers are to be used for important applications, such as generating cryptographic keys. Traditionally the system time has been used to seed, but care needs to be taken with this. If an application seeds often and the resolution of the system clock is low, then the same sequence of numbers might be repeated. Also, the system time is quite easy to guess, so if unpredictability is required then it should definitely not be the only source for the seed value. On some systems there's a special device ``/dev/random`` which provides random data better suited for use as a seed. .. function:: urandomb(n) Generate a uniformly distributed random integer in the range :math:`0` to :math:`2^n-1`, inclusive. The session state must be initialized by calling one of the `!randinit()` functions before invoking this function. .. function:: urandomm(n) Generate a uniformly distributed random integer in the range 0 to *n*\-1, inclusive. The session state must be initialized by calling one of the `!randinit()` functions before invoking this function. .. function:: rrandomb(n) Generate a random integer with long strings of zeros and ones in the binary representation. Useful for testing functions and algorithms, since this kind of random numbers have proven to be more likely to trigger corner-case bugs. The random number will be in the range :math:`0` to :math:`2^n-1`, inclusive. The session state must be initialized by calling one of the `!randinit()` functions before invoking this function. Aggregation functions --------------------- .. function:: sum(z) Return the sum of *z* across all input values. .. function:: prod(z) Return the product of *z* across all input values. .. function:: max(z) Return the maximum value of *z* across all input values. .. function:: min(z) Return the minimum value of *z* across all input values. .. function:: bit_and(z) Return the bitwise and of *z* across all input values. .. function:: bit_or(z) Return the bitwise inclusive-or of *z* across all input values. .. function:: bit_xor(z) Return the bitwise exclusive-or of *z* across all input values. pgmp-rel-1.0.4/docs/news.rst000066400000000000000000000000661361705073600157070ustar00rootroot00000000000000Release notes ============= .. include:: ../NEWS.rst pgmp-rel-1.0.4/docs/performance.rst000066400000000000000000000070111361705073600172310ustar00rootroot00000000000000.. _performance: Performance =========== Here are a few comparisons between the data types provided by pgmp and the builtin PostgreSQL data types. A few observations: - Of course `mpz` is not a substitute for `!decimal` as it doesn't store the non-integer part. So yes, we are comparing apples with pineapples, but `!decimal` is the only currently available way to have arbitrary size numbers in PostgreSQL. - We don't claim the extra speed summing numbers with 1000 digits is something everybody needs, nor that applications doing a mix of math and other operations or under an I/O load will benefit of the same speedup. - Those are "laptop comparisons", not obtained with a tuned PostgreSQL installation nor on production-grade hardware. However they are probably fine enough to compare the difference in behaviour between the data types, and I expect the same performance ratio on different hardware with the same platform. - All the results are obtained using the scripts available in the `bench`__ directory of the pmpz source code. .. __: https://github.com/dvarrazzo/pgmp/tree/master/bench .. _performance-sum: Just taking the sum of a table with 1M records, `!mpz` is about 25% faster than `!numeric` for small numbers; the difference increases with the size of the number up to about 75% for numbers with 1000 digits. `!int8` is probably slower than `!numeric` because the numbers are cast to `!numeric` before calculation. `!int4` is casted to `!int8` instead, so it still benefits of the speed of a native datatype. `!mpq` behaves good as no canonicalization has to be performed. .. image:: img/SumInteger-1e6.png .. _performance-arith: Performing a mix of operations the differences becomes more noticeable. This plot shows the time taken to calculate sum(a + b * c / d) on a 1M records table. `!mpz` is about 45% faster for small numbers, up to 80% faster for numbers with 100 digits. `!int8` is not visible as perfectly overlapping `!mpz`. `!mpq` is not shown as out of scale (a test with smaller table reveals a quadratic behavior probably due to the canonicalization). .. image:: img/Arith-1e6.png .. _performance-fact: The difference in performance of multiplications is particularly evident: Here is a test calculating *n*! in a trivial way (performing the product of a sequence of numbers via a *product* aggregate function `defined in SQL`__). The time taken to calculate 10000! via repeated `!mpz` multiplications is about 40 ms. .. image:: img/Factorial.png .. __: https://www.postgresql.org/docs/current/sql-createaggregate.html .. _preformance-dec: These comparisons show the perfomance with a sum of the same values stored in `!mpq` and `!decimal`. Because these rationals are representation of numbers with finite decimal expansion, the denominator doesn't grow unbounded (as in sum(1/n) on a sequence of random numbers) but is capped by 10^scale. `!decimal` is pretty stable in its performance for any scale but the time increases markedly with the precision (total number of digits). `!mpq` grows way more slowly with the precision, but has a noticeable overhead increasing with the scale. .. image:: img/SumRational-p2-1e6.png .. image:: img/SumRational-p4-1e6.png .. image:: img/SumRational-p8-1e6.png .. _performance-size: Here is a comparison of the size on disk of tables containing 1M records of different data types. The numbers are integers, so there is about a constant offset between `!mpz` and `!mpq`. The platform is 32 bit. .. image:: img/TableSize-1e6-small.png .. image:: img/TableSize-1e6.png pgmp-rel-1.0.4/docs/pi.rst000066400000000000000000000116721361705073600153500ustar00rootroot00000000000000[Ab]using PostgreSQL to calculate :math:`\pi` ============================================= Once the crime of binding the GMP_ multiple precision arithmetic library into PostgreSQL_ has been perpetrated, using it to calculate one million digits of :math:`\pi` is the least evil. .. _GMP: https://www.gmplib.org/ .. _PostgreSQL: https://www.postgresql.org/ Calculation is performed using a `Machin-like formula`__, a generalization of the Machin's formula: .. math:: \frac{\pi}{4} = 4 \; \tan^{-1}\frac{1}{5} - \tan^{-1}\frac{1}{239} known as one of the most efficient ways to compute a large number of digits of :math:`\pi`. Specifically we will use the Chien-Lih Hwang 1997 formula (further details and references in the above link). Calculation is performed using the `mpz` data type, thus using integers: to calculate an approximation of :math:`\tan^{-1}\frac{1}{x}` to :math:`n` digits, we use a Taylor series scaled to :math:`10^n`. Full details are provided in this nice `LiteratePrograms article`__. .. __: https://en.wikipedia.org/wiki/Machin-like_formula .. __: https://web.archive.org/web/20111211140154/http://en.literateprograms.org/Pi_with_Machin's_formula_(Python) It is possible to implement the `!arccot()` function using a `common table expression`__ from PostgreSQL 8.4: .. __: https://www.postgresql.org/docs/current/queries-with.html .. _PL/pgSQL: https://www.postgresql.org/docs/current/plpgsql.html .. code-block:: postgresql CREATE OR REPLACE FUNCTION arccot(x mpz, unity mpz) RETURNS mpz AS $$ WITH RECURSIVE t(n, xp) AS ( VALUES (3, $2 / -($1 ^ 3)) UNION ALL SELECT n+2, xp / -($1 ^ 2) FROM t WHERE xp <> 0 ) SELECT sum(xp / n) + ($2 / $1) FROM t; $$ LANGUAGE sql IMMUTABLE STRICT; However using this method all the temporary terms in the calculation are kept in memory, and the complex bookkeeping results in a non optimal use of the CPU. The performance can be improved using `PL/pgSQL`_, which may be not the fastest language around, but it's perfectly suitable for this task, as the time spent in the function body is negligible respect to the time spent in the arithmetic functions with huge operators. .. code-block:: postgresql CREATE OR REPLACE FUNCTION arccot(x mpz, unity mpz) RETURNS mpz AS $$ DECLARE xp mpz := unity / x; xp2 mpz := -(x ^ 2); acc mpz := xp; n mpz := 3; term mpz; BEGIN LOOP xp := xp / xp2; term := xp / n; EXIT WHEN term = 0; acc := acc + term; n := n + 2; END LOOP; RETURN acc; END $$ LANGUAGE plpgsql IMMUTABLE STRICT; And the Machin-like formula using it: .. code-block:: postgresql CREATE OR REPLACE FUNCTION pi_hwang_97(ndigits int) RETURNS mpz AS $$ DECLARE unity mpz = 10::mpz ^ (ndigits + 10); BEGIN RETURN 4 * ( 183 * arccot(239, unity) + 32 * arccot(1023, unity) - 68 * arccot(5832, unity) + 12 * arccot(110443, unity) - 12 * arccot(4841182, unity) - 100 * arccot(6826318, unity) ) / (10::mpz ^ 10); END $$ LANGUAGE plpgsql IMMUTABLE STRICT; This function produces 200,000 digits of :math:`\pi` in 30 sec on my laptop (Intel i5 2.53GHz). As a comparison, the equivalent Python implementation (using the Python built-in long integer implementation) takes almost 4 minutes. The function above has the shortcoming of using only one of the 4 cores available on my machine. But the formula itself is easy to parallelize as it contains 6 independent terms: we can produce each term in a separate backend process, storing the computation into a table (it's a database, after all!) and composing the result as a last step. The following Python script implements this idea: .. code-block:: python import eventlet eventlet.patcher.monkey_patch() import sys import psycopg2 dsn = 'dbname=regression' nprocs = 4 ndigits = int(sys.argv[1]) cnn = psycopg2.connect(dsn) cnn.set_isolation_level(0) cur = cnn.cursor() cur.execute(""" drop table if exists pi; create table pi (mult int4, arccot mpz); """) def arccot((mult, arg)): cnn = psycopg2.connect(dsn) cnn.set_isolation_level(0) cur = cnn.cursor() cur.execute(""" insert into pi values (%s, arccot(%s, 10::mpz ^ (%s + 10))); """, (mult, arg, ndigits)) cnn.close() pool = eventlet.GreenPool(nprocs) list(pool.imap(arccot, [ (183, 239), (32, 1023), (-68, 5832), (12, 110443), (-12, 4841182), (-100, 6826318)])) cur.execute("select 4 * sum(mult * arccot) / (10::mpz ^ 10) from pi;") print cur.fetchone()[0] The script uses 4 cores concurrently to calculate the intermediate terms: it produces 200,000 digits in about 13 seconds and 1 million digits in 8 minutes and 30 seconds. pgmp-rel-1.0.4/docs/requirements.txt000066400000000000000000000000651361705073600174640ustar00rootroot00000000000000sphinx>=1.8,<1.9 docutils==0.16 matplotlib>=2.2,<2.3 pgmp-rel-1.0.4/pgmp.control000066400000000000000000000016471361705073600156240ustar00rootroot00000000000000# pgmp -- Extension control file # # Copyright (C) 2011 Daniele Varrazzo # # This file is part of the PostgreSQL GMP Module # # The PostgreSQL GMP Module is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 3 of the License, # or (at your option) any later version. # # The PostgreSQL GMP Module is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with the PostgreSQL GMP Module. If not, see # http://www.gnu.org/licenses/. default_version = '1.1' comment = 'Multiple Precision Arithmetic extension' directory = 'pgmp' relocatable = true pgmp-rel-1.0.4/sandbox/000077500000000000000000000000001361705073600147055ustar00rootroot00000000000000pgmp-rel-1.0.4/sandbox/hello/000077500000000000000000000000001361705073600160105ustar00rootroot00000000000000pgmp-rel-1.0.4/sandbox/hello/Makefile000066400000000000000000000001011361705073600174400ustar00rootroot00000000000000CFLAGS=-g LDFLAGS=-lgmp all: hello clean: $(RM) hello.o hello pgmp-rel-1.0.4/sandbox/hello/hello.c000066400000000000000000000005421361705073600172600ustar00rootroot00000000000000/* A test program to study the mpz_t structure. * * Copyright (C) 2011-2020 Daniele Varrazzo */ #include #include int main(int argc, char **argv) { mpz_t z1, z2; mpz_init_set_ui(z1, ~((unsigned long int)0)); mpz_init(z2); mpz_add_ui(z2, z1, 1); mpz_out_str(stdout, 10, z2); printf("\n"); return 0; } pgmp-rel-1.0.4/sandbox/pi/000077500000000000000000000000001361705073600153155ustar00rootroot00000000000000pgmp-rel-1.0.4/sandbox/pi/calc_pi.py000077500000000000000000000024071361705073600172670ustar00rootroot00000000000000#!/usr/bin/env python """ A script to calculate pi using parallel PostgreSQL processes. Copyright (C) 2011-2020 Daniele Varrazzo """ import eventlet eventlet.patcher.monkey_patch() import sys import psycopg2 def main(): dsn = 'dbname=regression' nprocs = 4 ndigits = int(sys.argv[1]) def arccot((mult, arg)): print >>sys.stderr, 'start', arg cnn = psycopg2.connect(dsn) cnn.set_isolation_level(0) cur = cnn.cursor() cur.execute(""" insert into pi values (%s, arccot(%s, 10::mpz ^ (%s + 10))) returning %s; """, (mult, arg, ndigits, arg)) rv = cur.fetchone()[0] cnn.close() print >>sys.stderr, 'end', arg cnn = psycopg2.connect(dsn) cnn.set_isolation_level(0) cur = cnn.cursor() cur.execute(""" drop table if exists pi; create table pi (mult int4, arccot mpz); """) pool = eventlet.GreenPool(nprocs) list(pool.imap(arccot, [ (183, 239), (32, 1023), (-68, 5832), (12, 110443), (-12, 4841182), (-100, 6826318), ])) cur.execute("select 4 * sum(mult * arccot) / (10::mpz ^ 10) from pi;") print cur.fetchone()[0] if __name__ == '__main__': main() pgmp-rel-1.0.4/sandbox/pi/calc_pi.sh000077500000000000000000000020611361705073600172450ustar00rootroot00000000000000#!/bin/bash # A script to calculate pi using parallel backends # See calc_pi.py for a more advanced version. # # Copyright (C) 2011-2020 Daniele Varrazzo export DBARGS=regression export NDIGITS=$1 psql -q -c "drop table if exists pi;" $DBARGS psql -q -c "create table pi (mult int4, arccot mpz);" $DBARGS psql -Atq -c "insert into pi values (183, arccot(239, 10::mpz ^ ($NDIGITS + 10))) returning 1;" $DBARGS >&2 & psql -Atq -c "insert into pi values (32, arccot(1023, 10::mpz ^ ($NDIGITS + 10))) returning 2;" $DBARGS >&2 & psql -Atq -c "insert into pi values (-68, arccot(5832, 10::mpz ^ ($NDIGITS + 10))) returning 3;" $DBARGS >&2 & wait; psql -Atq -c "insert into pi values (12, arccot(110443, 10::mpz ^ ($NDIGITS + 10))) returning 4;" $DBARGS >&2 & psql -Atq -c "insert into pi values (-12, arccot(4841182, 10::mpz ^ ($NDIGITS + 10))) returning 5;" $DBARGS >&2 & psql -Atq -c "insert into pi values (-100, arccot(6826318, 10::mpz ^ ($NDIGITS + 10))) returning 6;" $DBARGS >&2 & wait; psql -At -c "select 4 * sum(mult * arccot) / (10::mpz ^ 10) from pi;" $DBARGS pgmp-rel-1.0.4/sandbox/pi/pi.sql000066400000000000000000000034361361705073600164540ustar00rootroot00000000000000-- Some functions to calculate pi digits using mpz integers. -- -- Reference: -- https://web.archive.org/web/20111211140154/http://en.literateprograms.org/Pi_with_Machin's_formula_(Python) -- -- Copyright (C) 2011-2020 Daniele Varrazzo CREATE FUNCTION arccot(x mpz, unity mpz) RETURNS mpz LANGUAGE plpgsql IMMUTABLE STRICT AS $$ DECLARE xp mpz := unity / x; xp2 mpz := -(x ^ 2); acc mpz := xp; term mpz; n mpz := 3; BEGIN LOOP xp := xp / xp2; term := xp / n; EXIT WHEN term = 0; acc := acc + term; n := n + 2; END LOOP; RETURN acc; END $$; CREATE FUNCTION pi_machin(ndigits integer) RETURNS mpz LANGUAGE plpgsql IMMUTABLE STRICT AS $$ DECLARE unity mpz = 10::mpz ^ (ndigits + 10); BEGIN RETURN 4 * ( 4 * arccot(5, unity) - arccot(239, unity) ) / (10::mpz ^ 10); END $$; CREATE FUNCTION pi_hwang_97(ndigits integer) RETURNS mpz LANGUAGE plpgsql IMMUTABLE STRICT AS $$ DECLARE unity mpz = 10::mpz ^ (ndigits + 10); BEGIN RETURN 4 * ( 183 * arccot(239, unity) + 32 * arccot(1023, unity) - 68 * arccot(5832, unity) + 12 * arccot(110443, unity) - 12 * arccot(4841182, unity) - 100 * arccot(6826318, unity) ) / (10::mpz ^ 10); END $$; CREATE FUNCTION pi_hwang_03(ndigits integer) RETURNS mpz LANGUAGE plpgsql IMMUTABLE STRICT AS $$ DECLARE unity mpz = 10::mpz ^ (ndigits + 10); BEGIN RETURN 4 * ( 183 * arccot(239, unity) + 32 * arccot(1023, unity) - 68 * arccot(5832, unity) + 12 * arccot(113021, unity) - 100 * arccot(6826318, unity) - 12 * arccot(33366019650, unity) + 12 * arccot(43599522992503626068::mpz, unity) ) / (10::mpz ^ 10); END $$; pgmp-rel-1.0.4/sql/000077500000000000000000000000001361705073600140465ustar00rootroot00000000000000pgmp-rel-1.0.4/sql/pgmp.pysql000066400000000000000000000353261361705073600161140ustar00rootroot00000000000000/* pgmp -- Module installation SQL script * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ !# This file is made of a mix of Python code and SQL statements. !# Use the script ``tools/unmix.py`` to convert it into plain SQL. !! PYON base_type = 'mpz' def func(sqlname, argin, argout=None, cname=None, volatile=False, strict=True): """Create a SQL function from a C function""" if not argout: argout = base_type print("CREATE OR REPLACE FUNCTION %s(%s)" \ % (sqlname, ", ".join(argin.split()))) print("RETURNS", argout) if not cname: pre = base_type + '_' cname = (sqlname.startswith(pre) and "p" + sqlname or 'p' + pre + sqlname) # not using MODULE_PATHNAME to make the resulting sql file # compatible with both PG pre-9.1 and 9.1 print("AS '$libdir/pgmp', '%s'" % cname) print("LANGUAGE C", end=' ') print(volatile and "VOLATILE" or "IMMUTABLE", end=' ') print( strict and "STRICT" or "", end=' ') print(";") print() def func_tuple(sqlname, args, argout=None, cname=None): """Create a SQL function returning many arguments from a C function""" if not argout: argout = base_type print("CREATE OR REPLACE FUNCTION %s(%s)" \ % (sqlname, args)) print("RETURNS RECORD") if not cname: pre = base_type + '_' cname = (sqlname.startswith(pre) and "p" + sqlname or 'p' + pre + sqlname) print("AS '$libdir/pgmp', '%s'" % cname) print("LANGUAGE C IMMUTABLE STRICT;") print() func('gmp_version', '', 'int4', cname='pgmp_gmp_version') !! PYOFF -- -- mpz user-defined type -- !! PYON func('mpz_in', 'cstring', 'mpz') func('mpz_out', 'mpz', 'cstring') !! PYOFF CREATE TYPE mpz ( INPUT = mpz_in , OUTPUT = mpz_out , INTERNALLENGTH = VARIABLE , STORAGE = EXTENDED , CATEGORY = 'N' ); -- Other I/O functions !! PYON func('mpz', 'text int4', 'mpz', cname='pmpz_in_base') func('text', 'mpz int4', 'cstring', cname='pmpz_out_base') !! PYOFF -- -- mpz cast -- !! PYON import re def castfrom(typname, implicit=False): """Create a cast from a different type to `base_type`""" func(base_type, typname, cname="p%s_from_%s" % (base_type, typname)) print("CREATE CAST (%s AS %s)" % (typname, base_type)) print("WITH FUNCTION %s(%s)" % (base_type, typname)) print({'I': "AS IMPLICIT;", 'A': "AS ASSIGNMENT;", False: ';'}[implicit]) print() print() castfrom('int2', implicit='I') castfrom('int4', implicit='I') castfrom('int8', implicit='I') castfrom('float4', implicit='A') castfrom('float8', implicit='A') castfrom('numeric', implicit='A') def castto(typname, implicit=False): """Create a cast from `base_type` to a different type""" func(typname, base_type, typname, cname="p%s_to_%s" % (base_type, typname)) print("CREATE CAST (%s AS %s)" % (base_type, typname)) print("WITH FUNCTION %s(%s)" % (typname, base_type)) print({'I': "AS IMPLICIT;", 'A': "AS ASSIGNMENT;", False: ';'}[implicit]) print() print() castto('int8', implicit='A') castto('int4', implicit='A') castto('int2', implicit='A') castto('float4', implicit='A') castto('float8', implicit='A') !! PYOFF CREATE CAST (mpz AS numeric) WITH INOUT AS ASSIGNMENT; -- -- mpz operators -- !! PYON func('mpz_uplus', 'mpz') func('mpz_neg', 'mpz') func('abs', 'mpz') func('sgn', 'mpz', 'int4') func('even', 'mpz', 'bool') func('odd', 'mpz', 'bool') !! PYOFF CREATE OPERATOR - ( RIGHTARG = mpz, PROCEDURE = mpz_neg ); CREATE OPERATOR + ( RIGHTARG = mpz, PROCEDURE = mpz_uplus ); !! PYON def op(sym, fname, rarg=None, comm=None): """Create an operator on `base_type`""" if rarg == None: rarg = base_type func('%s_%s' % (base_type, fname), base_type + " " + rarg) print("CREATE OPERATOR %s (" % sym) print(" LEFTARG = %s," % base_type) print(" RIGHTARG = %s," % rarg) if comm: print(" COMMUTATOR = %s," % comm) print(" PROCEDURE = %s_%s" % (base_type, fname)) print(");") print() print() op('+', 'add', comm='+') op('-', 'sub') op('*', 'mul', comm='*') op('/', 'tdiv_q') op('%', 'tdiv_r') op('+/', 'cdiv_q') op('+%', 'cdiv_r') op('-/', 'fdiv_q') op('-%', 'fdiv_r') op('/!', 'divexact') op('<<', 'mul_2exp', rarg='int8') op('>>', 'tdiv_q_2exp', rarg='int8') op('%>', 'tdiv_r_2exp', rarg='int8') op('+>>', 'cdiv_q_2exp', rarg='int8') op('+%>', 'cdiv_r_2exp', rarg='int8') op('->>', 'fdiv_q_2exp', rarg='int8') op('-%>', 'fdiv_r_2exp', rarg='int8') func_tuple('tdiv_qr', 'mpz, mpz, out q mpz, out r mpz') func_tuple('cdiv_qr', 'mpz, mpz, out q mpz, out r mpz') func_tuple('fdiv_qr', 'mpz, mpz, out q mpz, out r mpz') func('divisible', 'mpz mpz', 'bool') func('divisible_2exp', 'mpz int8', 'bool') func('congruent', 'mpz mpz mpz', 'bool') func('congruent_2exp', 'mpz mpz int8', 'bool') func('pow', 'mpz int8', cname='pmpz_pow_ui') func('powm', 'mpz mpz mpz') op('&', 'and') op('|', 'ior') op('#', 'xor') func('com', 'mpz', 'mpz') func('popcount', 'mpz', 'mpz') func('hamdist', 'mpz mpz', 'mpz') func('scan0', 'mpz mpz', 'mpz') func('scan1', 'mpz mpz', 'mpz') func('setbit', 'mpz mpz', 'mpz') func('clrbit', 'mpz mpz', 'mpz') func('combit', 'mpz mpz', 'mpz') func('tstbit', 'mpz mpz', 'int4') func('gmp_max_bitcnt', '', 'mpz', cname='pgmp_max_bitcnt') !! PYOFF CREATE OPERATOR /? ( LEFTARG = mpz, RIGHTARG = mpz, PROCEDURE = divisible ); CREATE OPERATOR >>? ( LEFTARG = mpz, RIGHTARG = int8, PROCEDURE = divisible_2exp ); CREATE OPERATOR ^ ( LEFTARG = mpz, RIGHTARG = int8, PROCEDURE = pow ); -- -- mpz comparisons -- CREATE OR REPLACE FUNCTION mpz_eq(mpz, mpz) RETURNS boolean AS '$libdir/pgmp', 'pmpz_eq' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR = ( LEFTARG = mpz , RIGHTARG = mpz , PROCEDURE = mpz_eq , COMMUTATOR = = , NEGATOR = <> , RESTRICT = eqsel , JOIN = eqjoinsel , HASHES , MERGES ); CREATE OR REPLACE FUNCTION mpz_ne(mpz, mpz) RETURNS boolean AS '$libdir/pgmp', 'pmpz_ne' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR <> ( LEFTARG = mpz , RIGHTARG = mpz , PROCEDURE = mpz_ne , COMMUTATOR = <> , NEGATOR = = , RESTRICT = neqsel , JOIN = neqjoinsel ); !! PYON def bop(sym, fname, comm, neg): """Create an operator on `base_type` returning a bool""" func('%s_%s' % (base_type, fname), base_type + " " + base_type, argout='boolean') fname1 = fname[0] + 't' print("CREATE OPERATOR %s (" % sym) print(" LEFTARG =", base_type) print(" , RIGHTARG =", base_type) print(" , PROCEDURE = %s_%s" % (base_type, fname)) print(" , COMMUTATOR = %s" % comm) print(" , NEGATOR = %s" % neg) print(" , RESTRICT = scalar%ssel" % fname1) print(" , JOIN = scalar%sjoinsel" % fname1) print(");") print() print() bop('>', 'gt', comm='<', neg='<=') bop('>=', 'ge', comm='<=', neg='<') bop('<', 'lt', comm='>', neg='>=') bop('<=', 'le', comm='>=', neg='>') !! PYOFF -- -- mpz indexes -- CREATE OR REPLACE FUNCTION mpz_cmp(mpz, mpz) RETURNS integer AS '$libdir/pgmp', 'pmpz_cmp' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpz_ops DEFAULT FOR TYPE mpz USING btree AS OPERATOR 1 < , OPERATOR 2 <= , OPERATOR 3 = , OPERATOR 4 >= , OPERATOR 5 > , FUNCTION 1 mpz_cmp(mpz, mpz) ; CREATE OR REPLACE FUNCTION mpz_hash(mpz) RETURNS integer AS '$libdir/pgmp', 'pmpz_hash' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpz_ops DEFAULT FOR TYPE mpz USING hash AS OPERATOR 1 = , FUNCTION 1 mpz_hash(mpz) ; -- TODO: OPERATOR FAMILY? -- mpz functions !! PYON func('sqrt', 'mpz', 'mpz') func('root', 'mpz int8', 'mpz') func('perfect_power', 'mpz', 'bool', cname='pmpz_perfect_power') func('perfect_square', 'mpz', 'bool', cname='pmpz_perfect_square') func_tuple('rootrem', 'mpz, int8, out root mpz, out rem mpz') func_tuple('sqrtrem', 'mpz, out root mpz, out rem mpz') !! PYOFF -- -- Number Theoretic Functions -- !! PYON func('probab_prime', 'mpz int4', 'int4', cname='pmpz_probab_prime_p') func('nextprime', 'mpz') func('gcd', 'mpz mpz', 'mpz') func('lcm', 'mpz mpz', 'mpz') func('invert', 'mpz mpz', 'mpz') func('jacobi', 'mpz mpz', 'int4') func('legendre', 'mpz mpz', 'int4') func('kronecker', 'mpz mpz', 'int4') func('remove', 'mpz mpz', 'mpz') func('fac', 'int8', 'mpz', cname='pmpz_fac_ui') func('bin', 'mpz int8', 'mpz', cname='pmpz_bin_ui') func('fib', 'int8', 'mpz', cname='pmpz_fib_ui') func('lucnum', 'int8', 'mpz', cname='pmpz_lucnum_ui') func_tuple('gcdext', 'mpz, mpz, out g mpz, out s mpz, out t mpz') func_tuple('fib2', 'int8, out fn mpz, out fnsub1 mpz', cname='pmpz_fib2_ui') func_tuple('lucnum2', 'int8, out ln mpz, out lnsub1 mpz', cname='pmpz_lucnum2_ui') !! PYOFF -- -- Random numbers -- !! PYON func('randinit', '', 'void', cname='pgmp_randinit_default', volatile=True) func('randinit_mt', '', 'void', cname='pgmp_randinit_mt', volatile=True) func('randinit_lc_2exp', 'mpz int8 int8', 'void', cname='pgmp_randinit_lc_2exp', volatile=True) func('randinit_lc_2exp_size', 'int8', 'void', cname='pgmp_randinit_lc_2exp_size', volatile=True) func('randseed', 'mpz', 'void', cname='pgmp_randseed', volatile=True) func('urandomb', 'int8', 'mpz', volatile=True) func('urandomm', 'mpz', 'mpz', volatile=True) func('rrandomb', 'int8', 'mpz', volatile=True) !! PYOFF -- -- Aggregation functions -- !! PYON def agg(sqlname, argin, sfunc, argout=None, ffunc=None, sortop=None): assert sfunc.startswith('_' + base_type) cname = '_p' + sfunc[1:] func(sfunc, 'internal ' + argin, 'internal', cname=cname, strict=False) if not argout: argout = base_type print("CREATE AGGREGATE %s(%s)\n(" \ % (sqlname, ", ".join(argin.split()))) print(" SFUNC =", sfunc) print(" , STYPE = internal") print(" , FINALFUNC =", ffunc or "_%s_from_agg" % base_type) if sortop: print(" , SORTOP =", sortop) print(");") print() func('_mpz_from_agg', 'internal', 'mpz', cname='_pmpz_from_agg') agg('sum', 'mpz', '_mpz_agg_add') agg('prod', 'mpz', '_mpz_agg_mul') agg('max', 'mpz', '_mpz_agg_max', sortop='>') agg('min', 'mpz', '_mpz_agg_min', sortop='<') agg('bit_and', 'mpz', '_mpz_agg_and') agg('bit_or', 'mpz', '_mpz_agg_ior') agg('bit_xor', 'mpz', '_mpz_agg_xor') !! PYOFF -- -- mpq user-defined type -- !! PYON base_type = 'mpq' func('mpq_in', 'cstring', 'mpq') func('mpq_out', 'mpq', 'cstring') !! PYOFF CREATE TYPE mpq ( INPUT = mpq_in , OUTPUT = mpq_out , INTERNALLENGTH = VARIABLE , STORAGE = EXTENDED , CATEGORY = 'N' ); !! PYON func('mpq', 'text int4', 'mpq', cname='pmpq_in_base') func('text', 'mpq int4', 'cstring', cname='pmpq_out_base') func('mpq', 'mpz mpz', 'mpq', cname='pmpq_mpz_mpz') # Currently not exposing these versions as they have surprising limits # E.g. calling mpq(n, d) with two numeric literal of which one fits # into an int and one doesn't fails to find a best candidate. # So we currently only accept mpq(n, d) if both fit into a type smaller # than numeric (because of the implicit casts int? -> mpz) and require # explicit mpz for bigger numbers. # func('mpq', 'int4 int4', 'mpq', cname='pmpq_int4_int4') # func('mpq', 'numeric numeric', 'mpq', cname='pmpq_numeric_numeric') func('num', 'mpq', 'mpz') func('den', 'mpq', 'mpz') castfrom('int2', implicit='I') castfrom('int4', implicit='I') castfrom('int8', implicit='I') castfrom('float4', implicit='I') castfrom('float8', implicit='I') castfrom('numeric', implicit='I') castfrom('mpz', implicit='I') castto('int2', implicit='A') castto('int4', implicit='A') castto('int8', implicit='A') castto('float4', implicit='A') castto('float8', implicit='A') castto('mpz', implicit='A') !! PYOFF CREATE OR REPLACE FUNCTION mpq_to_numeric(mpq, int4) RETURNS numeric AS '$libdir/pgmp', 'pmpq_to_numeric' LANGUAGE C IMMUTABLE STRICT ; CREATE CAST (mpq AS numeric) WITH FUNCTION mpq_to_numeric(mpq, int4) AS ASSIGNMENT; -- -- mpq operators -- !! PYON func('mpq_uplus', 'mpq') func('mpq_neg', 'mpq') func('abs', 'mpq') func('inv', 'mpq') !! PYOFF CREATE OPERATOR - ( RIGHTARG = mpq, PROCEDURE = mpq_neg ); CREATE OPERATOR + ( RIGHTARG = mpq, PROCEDURE = mpq_uplus ); !! PYON op('+', 'add', comm='+') op('-', 'sub') op('*', 'mul', comm='*') op('/', 'div') op('<<', 'mul_2exp', rarg='int8') op('>>', 'div_2exp', rarg='int8') func('limit_den', 'mpq') func('limit_den', 'mpq mpz') !! PYOFF -- -- mpq comparisons -- CREATE OR REPLACE FUNCTION mpq_eq(mpq, mpq) RETURNS boolean AS '$libdir/pgmp', 'pmpq_eq' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR = ( LEFTARG = mpq , RIGHTARG = mpq , PROCEDURE = mpq_eq , COMMUTATOR = = , NEGATOR = <> , RESTRICT = eqsel , JOIN = eqjoinsel , HASHES , MERGES ); CREATE OR REPLACE FUNCTION mpq_ne(mpq, mpq) RETURNS boolean AS '$libdir/pgmp', 'pmpq_ne' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR <> ( LEFTARG = mpq , RIGHTARG = mpq , PROCEDURE = mpq_ne , COMMUTATOR = <> , NEGATOR = = , RESTRICT = neqsel , JOIN = neqjoinsel ); !! PYON bop('>', 'gt', comm='<', neg='<=') bop('>=', 'ge', comm='<=', neg='<') bop('<', 'lt', comm='>', neg='>=') bop('<=', 'le', comm='>=', neg='>') !! PYOFF -- -- mpq indexes -- CREATE OR REPLACE FUNCTION mpq_cmp(mpq, mpq) RETURNS integer AS '$libdir/pgmp', 'pmpq_cmp' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpq_ops DEFAULT FOR TYPE mpq USING btree AS OPERATOR 1 < , OPERATOR 2 <= , OPERATOR 3 = , OPERATOR 4 >= , OPERATOR 5 > , FUNCTION 1 mpq_cmp(mpq, mpq) ; CREATE OR REPLACE FUNCTION mpq_hash(mpq) RETURNS integer AS '$libdir/pgmp', 'pmpq_hash' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpq_ops DEFAULT FOR TYPE mpq USING hash AS OPERATOR 1 = , FUNCTION 1 mpq_hash(mpq) ; -- TODO: OPERATOR FAMILY? -- -- Aggregation functions -- !! PYON func('_mpq_from_agg', 'internal', 'mpq', cname='_pmpq_from_agg') agg('sum', 'mpq', '_mpq_agg_add') agg('prod', 'mpq', '_mpq_agg_mul') agg('max', 'mpq', '_mpq_agg_max', sortop='>') agg('min', 'mpq', '_mpq_agg_min', sortop='<') !! PYOFF pgmp-rel-1.0.4/sql/uninstall_pgmp.sql000066400000000000000000000026741361705073600176340ustar00rootroot00000000000000/* pgmp -- Module uninstall SQL script * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ -- -- NOTE: use this script only with PostgreSQL before 9.1 -- Starting from PostgreSQL 9.1 use DROP EXTENSION pgmp; instead. -- -- Drop the data types: this will rip off all the functions defined on them. DROP TYPE mpz CASCADE; DROP TYPE mpq CASCADE; -- Drop the remaining objects. DROP FUNCTION gmp_version(); DROP FUNCTION randinit(); DROP FUNCTION randinit_mt(); DROP FUNCTION randinit_lc_2exp_size(int8); DROP OPERATOR FAMILY mpz_ops USING btree CASCADE; DROP OPERATOR FAMILY mpz_ops USING hash CASCADE; DROP OPERATOR FAMILY mpq_ops USING btree CASCADE; DROP OPERATOR FAMILY mpq_ops USING hash CASCADE; pgmp-rel-1.0.4/src/000077500000000000000000000000001361705073600140365ustar00rootroot00000000000000pgmp-rel-1.0.4/src/pgmp-impl.h000066400000000000000000000110031361705073600161040ustar00rootroot00000000000000/* pgmp-impl -- Implementation details * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #ifndef __PGMP_IMPL_H__ #define __PGMP_IMPL_H__ #include /* for LONG_MAX etc. */ /* Ensure we know what platform are we working on. * * GMP defines many structures and functions in term of long/ulong, while * Postgres always uses data types with an explicit number of bytes (int32, * int64 etc). Ensure we know what to do when passing arguments around. */ #if LONG_MAX == INT32_MAX #define PGMP_LONG_32 1 #define PGMP_LONG_64 0 #endif #if LONG_MAX == INT64_MAX #define PGMP_LONG_32 0 #define PGMP_LONG_64 1 #endif #if !(PGMP_LONG_32 || PGMP_LONG_64) #error Expected platform where long is either 32 or 64 bits #endif /* Space to leave before the pointers returned by the GMP custom allocator. * * The space is used to store the varlena and structure header before the gmp * limbs, so it must be at least as big as the bigger of such headers. */ #define PGMP_MAX_HDRSIZE 8 /* * Macros equivalent to the ones defimed in gmp-impl.h */ #define ABS(x) ((x) >= 0 ? (x) : -(x)) #define SIZ(z) ((z)->_mp_size) #define NLIMBS(z) ABS((z)->_mp_size) #define LIMBS(z) ((z)->_mp_d) #define ALLOC(v) ((v)->_mp_alloc) /* Allow direct user access to numerator and denominator of a mpq_t object. */ #define mpq_numref(Q) (&((Q)->_mp_num)) #define mpq_denref(Q) (&((Q)->_mp_den)) /* __builtin_expect is in gcc 3.0, and not in 2.95. */ #if __GNUC__ >= 3 #define LIKELY(cond) __builtin_expect ((cond) != 0, 1) #define UNLIKELY(cond) __builtin_expect ((cond) != 0, 0) #else #define LIKELY(cond) (cond) #define UNLIKELY(cond) (cond) #endif /* Not available before GMP 5 */ #if (__GNU_MP_VERSION < 5) typedef unsigned long int mp_bitcnt_t; #endif /* Less noise in the creation of postgresql functions */ #define PGMP_PG_FUNCTION(name) \ \ PG_FUNCTION_INFO_V1(name); \ \ Datum name(PG_FUNCTION_ARGS); \ \ Datum name(PG_FUNCTION_ARGS) /* Macros to get a long/ulong argument in wrappers. * * The argument SQL type should be int8. The macros may raise exception if the * input doesn't fit in a long/ulong as defined on the server. */ #if PGMP_LONG_32 #define PGMP_GETARG_LONG(tgt,narg) \ do { \ int64 _tmp = PG_GETARG_INT64(narg); \ if (!(LONG_MIN <= _tmp && _tmp <= LONG_MAX)) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument too large for a long: %lld", _tmp) )); \ } \ tgt = (long)_tmp; \ } while (0) #define PGMP_GETARG_ULONG(tgt,narg) \ do { \ int64 _tmp = PG_GETARG_INT64(narg); \ if (_tmp > ULONG_MAX) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument too large for an unsigned long: %lld", _tmp) )); \ } \ if (_tmp < 0) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument can't be negative") )); \ } \ tgt = (unsigned long)_tmp; \ } while (0) #elif PGMP_LONG_64 #define PGMP_GETARG_LONG(tgt,narg) \ tgt = (long)PG_GETARG_INT64(narg); #define PGMP_GETARG_ULONG(tgt,narg) \ do { \ int64 _tmp = PG_GETARG_INT64(narg); \ if (_tmp < 0) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument can't be negative") )); \ } \ tgt = (unsigned long)_tmp; \ } while (0) #endif /* Not available e.g. on GMP 4.1 */ #ifndef __GMP_MP_RELEASE #define __GMP_MP_RELEASE ( \ __GNU_MP_VERSION * 10000 + \ __GNU_MP_VERSION_MINOR * 100 + \ __GNU_MP_VERSION_PATCHLEVEL) #endif /* The text parsing functions have different range across versions */ #if __GMP_MP_RELEASE >= 40200 #define PGMP_MAXBASE_IO 62 #else #define PGMP_MAXBASE_IO 36 #endif #endif /* __PGMP_IMPL_H__ */ pgmp-rel-1.0.4/src/pgmp.c000066400000000000000000000063301361705073600151470ustar00rootroot00000000000000/* pgmp -- PostgreSQL GMP module * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include #include "postgres.h" #include "fmgr.h" #include "pgmp-impl.h" PG_MODULE_MAGIC; void _PG_init(void); void _PG_fini(void); static void *_pgmp_alloc(size_t alloc_size); static void *_pgmp_realloc(void *ptr, size_t old_size, size_t new_size); static void _pgmp_free(void *ptr, size_t size); /* A couple of constant limbs used to create constant mp? data * from the content of varlena data */ const mp_limb_t _pgmp_limb_0 = 0; const mp_limb_t _pgmp_limb_1 = 1; /* * Module initialization and cleanup */ void _PG_init(void) { /* A vow to the gods of the memory allocation */ mp_set_memory_functions( _pgmp_alloc, _pgmp_realloc, _pgmp_free); } void _PG_fini(void) { } /* * GMP custom allocation functions using PostgreSQL memory management. * * In order to store data into the database, the structure must be contiguous * in memory. GMP instead allocated the limbs dynamically. This means that to * convert from mpz_p to the varlena a memcpy would be required. * * But we don't like memcpy... So we allocate enough space to add the varlena * header and we return an offsetted pointer to GMP, so that we can always * scrubble a varlena header in front of the limbs and just ask the database * to store the result. */ static void * _pgmp_alloc(size_t size) { return PGMP_MAX_HDRSIZE + (char *)palloc(size + PGMP_MAX_HDRSIZE); } static void * _pgmp_realloc(void *ptr, size_t old_size, size_t new_size) { return PGMP_MAX_HDRSIZE + (char *)repalloc( (char *)ptr - PGMP_MAX_HDRSIZE, new_size + PGMP_MAX_HDRSIZE); } static void _pgmp_free(void *ptr, size_t size) { pfree((char *)ptr - PGMP_MAX_HDRSIZE); } /* Return the version of the library as an integer * * Parse the format from the variable gmp_version instead of using the macro * __GNU_PG_VERSION* in order to detect the runtime version instead of the * version pgmp was compiled against (although if I'm not entirely sure it is * working as expected). */ PGMP_PG_FUNCTION(pgmp_gmp_version) { int maj = 0, min = 0, patch = 0; const char *p; /* Parse both A.B.C and A.B formats. */ maj = atoi(gmp_version); if (NULL == (p = strchr(gmp_version, '.'))) { goto end; } min = atoi(p + 1); if (NULL == (p = strchr(p + 1, '.'))) { goto end; } patch = atoi(p + 1); end: PG_RETURN_INT32(maj * 10000 + min * 100 + patch); } pgmp-rel-1.0.4/src/pgmp_utils.c000066400000000000000000000036701361705073600163730ustar00rootroot00000000000000/* pgmp_utils -- misc utility module * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pgmp_utils.h" #if PG_VERSION_NUM < 90000 #include "nodes/nodes.h" /* for IsA */ #include "nodes/execnodes.h" /* for AggState */ /* * AggCheckCallContext - test if a SQL function is being called as an aggregate * * The function is available from PG 9.0. This allows compatibility with * previous versions. */ int AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext) { if (fcinfo->context && IsA(fcinfo->context, AggState)) { if (aggcontext) { *aggcontext = ((AggState *) fcinfo->context)->aggcontext; } return AGG_CONTEXT_AGGREGATE; } #if PG_VERSION_NUM >= 80400 if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) { if (aggcontext) { /* different from PG 9.0: in PG 8.4 there is no aggcontext */ *aggcontext = ((WindowAggState *) fcinfo->context)->wincontext; } return AGG_CONTEXT_WINDOW; } #endif /* this is just to prevent "uninitialized variable" warnings */ if (aggcontext) { *aggcontext = NULL; } return 0; } #endif pgmp-rel-1.0.4/src/pgmp_utils.h000066400000000000000000000022621361705073600163740ustar00rootroot00000000000000/* pgmp_utils -- misc utility module * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #if PG_VERSION_NUM < 90000 #include "postgres.h" /* for MemoryContext */ #include "fmgr.h" /* for FunctionCallInfo */ #define AGG_CONTEXT_AGGREGATE 1 /* regular aggregate */ #define AGG_CONTEXT_WINDOW 2 /* window function */ int AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext); #endif pgmp-rel-1.0.4/src/pmpq.c000066400000000000000000000110441361705073600151570ustar00rootroot00000000000000/* pmpq -- PostgreSQL data type for GMP mpq * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpq.h" #include "pgmp-impl.h" #include "fmgr.h" /* To be referred to to represent the zero */ extern const mp_limb_t _pgmp_limb_0; extern const mp_limb_t _pgmp_limb_1; /* * Create a pmpq structure from the content of a mpq * * The function is not const as the numerator will be realloc'd to make room * to the denom limbs after it. For this reason this function must never * receive directly data read from the database. */ pmpq * pmpq_from_mpq(mpq_ptr q) { pmpq *res; mpz_ptr num = mpq_numref(q); if (LIKELY(ALLOC(num))) { /* Make enough room after the numer to store the denom limbs */ int nsize = SIZ(num); int nalloc = ABS(nsize); int dsize = SIZ(mpq_denref(q)); mpz_ptr den = mpq_denref(q); if (nalloc >= dsize) { LIMBS(num) = _mpz_realloc(num, nalloc + dsize); res = (pmpq *)((char *)LIMBS(num) - PMPQ_HDRSIZE); /* copy the denom after the numer */ memcpy(res->data + nalloc, LIMBS(den), dsize * sizeof(mp_limb_t)); /* Set the number of limbs and order and implicitly version 0 */ res->mdata = PMPQ_SET_SIZE_FIRST(PMPQ_SET_NUMER_FIRST(0), nalloc); } else { LIMBS(den) = _mpz_realloc(den, nalloc + dsize); res = (pmpq *)((char *)LIMBS(den) - PMPQ_HDRSIZE); /* copy the numer after the denom */ memcpy(res->data + dsize, LIMBS(num), nalloc * sizeof(mp_limb_t)); /* Set the number of limbs and order and implicitly version 0 */ res->mdata = PMPQ_SET_SIZE_FIRST(PMPQ_SET_DENOM_FIRST(0), dsize); } SET_VARSIZE(res, PMPQ_HDRSIZE + (nalloc + dsize) * sizeof(mp_limb_t)); /* Set the sign */ if (nsize < 0) { res->mdata = PMPQ_SET_NEGATIVE(res->mdata); } } else { /* No allocation for the limbs: allocate something of ours */ res = palloc(PMPQ_HDRSIZE); SET_VARSIZE(res, PMPQ_HDRSIZE); res->mdata = 0; } return res; } /* * Initialize a mpq from the content of a datum * * NOTE: the function takes a pointer to a const and changes the structure. * This allows to define the structure as const in the calling function and * avoid the risk to change it inplace, which may corrupt the database data. * * The structure populated doesn't own the pointed data, so it must not be * changed in any way and must not be cleared. */ void mpq_from_pmpq(mpq_srcptr q, const pmpq *pq) { /* discard the const qualifier */ mpq_ptr wq = (mpq_ptr)q; mpz_ptr num = mpq_numref(wq); mpz_ptr den = mpq_denref(wq); if (UNLIKELY(0 != (PMPQ_VERSION(pq)))) { ereport(ERROR, ( errcode(ERRCODE_DATA_EXCEPTION), errmsg("unsupported mpq version: %d", PMPQ_VERSION(pq)))); } if (0 != PMPQ_NLIMBS(pq)) { mpz_ptr fst, snd; if (PMPQ_NUMER_FIRST(pq)) { fst = num; snd = den; } else { fst = den; snd = num; } /* We have data from numer and denom into the datum */ ALLOC(fst) = SIZ(fst) = PMPQ_SIZE_FIRST(pq); LIMBS(fst) = (mp_limb_t *)pq->data; ALLOC(snd) = SIZ(snd) = PMPQ_SIZE_SECOND(pq); LIMBS(snd) = (mp_limb_t *)pq->data + ALLOC(fst); if (PMPQ_NEGATIVE(pq)) { SIZ(num) = -SIZ(num); } } else { /* in the datum there is not 1/0, * so let's just refer to some static const */ ALLOC(num) = 1; SIZ(num) = 0; LIMBS(num) = (mp_limb_t *)(&_pgmp_limb_0); ALLOC(den) = 1; SIZ(den) = 1; LIMBS(den) = (mp_limb_t *)(&_pgmp_limb_1); } } pgmp-rel-1.0.4/src/pmpq.h000066400000000000000000000076011361705073600151700ustar00rootroot00000000000000/* pmpq -- PostgreSQL data type for GMP mpq * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #ifndef __PMPQ_H__ #define __PMPQ_H__ #include #include "postgres.h" typedef struct { char vl_len_[4]; /* varlena header */ unsigned mdata; /* version, sign, limbs in numer */ mp_limb_t data[1]; /* limbs: numer, then denom */ } pmpq; /* Postgres only allows 2^30 bytes in varlena. Because each limb is at least 4 * bytes we need at most 28 bits to store the size. So we can use the * higher 4 bits for other stuff: we use 2 bits for the version, 1 for the * sign and 1 for the order in which denom and numer limbs are stored. */ #define PMPQ_VERSION_MASK 0x30000000U #define PMPQ_DENOM_FIRST_MASK 0x40000000U #define PMPQ_SIGN_MASK 0x80000000U #define PMPQ_SIZE_FIRST_MASK 0x0FFFFFFFU /* Must be not larger than PGMP_MAX_HDRSIZE */ #define PMPQ_HDRSIZE MAXALIGN(offsetof(pmpq,data)) #define PMPQ_VERSION(mq) ((((mq)->mdata) & PMPQ_VERSION_MASK) >> 28) #define PMPQ_SET_VERSION(mdata,v) \ (((mdata) & ~PMPQ_VERSION_MASK) | (((v) << 28) & PMPQ_VERSION_MASK)) #define PMPQ_SET_NEGATIVE(mdata) ((mdata) | PMPQ_SIGN_MASK) #define PMPQ_SET_POSITIVE(mdata) ((mdata) & ~PMPQ_SIGN_MASK) #define PMPQ_NEGATIVE(mq) (((mq)->mdata) & PMPQ_SIGN_MASK) #define PMPQ_SET_NUMER_FIRST(mdata) ((mdata) & ~PMPQ_DENOM_FIRST_MASK) #define PMPQ_SET_DENOM_FIRST(mdata) ((mdata) | PMPQ_DENOM_FIRST_MASK) #define PMPQ_NUMER_FIRST(mq) (!(((mq)->mdata) & PMPQ_DENOM_FIRST_MASK)) #define PMPQ_DENOM_FIRST(mq) (((mq)->mdata) & PMPQ_DENOM_FIRST_MASK) #define PMPQ_NLIMBS(mq) ((VARSIZE(mq) - PMPQ_HDRSIZE) / sizeof(mp_limb_t)) #define PMPQ_SIZE_FIRST(mq) (((mq)->mdata) & PMPQ_SIZE_FIRST_MASK) #define PMPQ_SIZE_SECOND(mq) (PMPQ_NLIMBS(mq) - PMPQ_SIZE_FIRST(mq)) #define PMPQ_SET_SIZE_FIRST(mdata,s) \ (((mdata) & ~PMPQ_SIZE_FIRST_MASK) | ((s) & PMPQ_SIZE_FIRST_MASK)) /* Macros to convert mpz arguments and return values */ #define PGMP_GETARG_PMPQ(x) \ ((pmpq*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x)))) #define PGMP_GETARG_MPQ(q,n) \ mpq_from_pmpq(q, PGMP_GETARG_PMPQ(n)); #define PGMP_RETURN_MPQ(q) \ PG_RETURN_POINTER(pmpq_from_mpq(q)) pmpq * pmpq_from_mpq(mpq_ptr q); void mpq_from_pmpq(mpq_srcptr q, const pmpq *pq); /* Macros to be used in functions wrappers to limit the arguments domain */ /* TODO: this macro should be somewhere I can import from, * I'd prefer avoiding to import pmpz.h here */ #ifndef MPZ_IS_ZERO #define MPZ_IS_ZERO(z) (SIZ(z) == 0) #endif #define PMPQ_NO_CHECK(arg) #define PMPQ_CHECK_DIV0(arg) \ do { \ if (UNLIKELY(MPZ_IS_ZERO(mpq_numref(arg)))) \ { \ ereport(ERROR, ( \ errcode(ERRCODE_DIVISION_BY_ZERO), \ errmsg("division by zero"))); \ } \ } while (0) /* used in mpq creation. Note that the argument is a z */ #define ERROR_IF_DENOM_ZERO(arg) \ do { \ if (UNLIKELY(MPZ_IS_ZERO(arg))) \ { \ ereport(ERROR, ( \ errcode(ERRCODE_DIVISION_BY_ZERO), \ errmsg("denominator can't be zero"))); \ } \ } while (0) #endif /* __PMPQ_H__ */ pgmp-rel-1.0.4/src/pmpq_agg.c000066400000000000000000000053231361705073600160000ustar00rootroot00000000000000/* pmpq_agg -- mpq aggregation functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpq.h" #include "pgmp_utils.h" /* for AggCheckCallContext on PG < 9.0 */ #include "pgmp-impl.h" #include "fmgr.h" /* Convert an inplace accumulator into a pmpq structure. * * This function is strict, so don't care about NULLs */ PGMP_PG_FUNCTION(_pmpq_from_agg) { mpq_t *a; a = (mpq_t *)PG_GETARG_POINTER(0); PGMP_RETURN_MPQ(*a); } /* Macro to create an accumulation function from a gmp operator. * * This function can't be strict because the internal state is not compatible * with the base type. */ #define PMPQ_AGG(op, BLOCK, rel) \ \ PGMP_PG_FUNCTION(_pmpq_agg_ ## op) \ { \ mpq_t *a; \ const mpq_t q; \ MemoryContext oldctx; \ MemoryContext aggctx; \ \ if (UNLIKELY(!AggCheckCallContext(fcinfo, &aggctx))) \ { \ ereport(ERROR, \ (errcode(ERRCODE_DATA_EXCEPTION), \ errmsg("_mpq_agg_" #op " can only be called in accumulation"))); \ } \ \ if (PG_ARGISNULL(1)) { \ if (PG_ARGISNULL(0)) { \ PG_RETURN_NULL(); \ } \ else { \ PG_RETURN_POINTER(PG_GETARG_POINTER(0)); \ } \ } \ \ PGMP_GETARG_MPQ(q, 1); \ \ oldctx = MemoryContextSwitchTo(aggctx); \ \ if (LIKELY(!PG_ARGISNULL(0))) { \ a = (mpq_t *)PG_GETARG_POINTER(0); \ BLOCK(op, rel); \ } \ else { /* uninitialized */ \ a = (mpq_t *)palloc(sizeof(mpq_t)); \ mpq_init(*a); \ mpq_set(*a, q); \ } \ \ MemoryContextSwitchTo(oldctx); \ \ PG_RETURN_POINTER(a); \ } #define PMPQ_AGG_OP(op, rel) \ mpq_ ## op (*a, *a, q) PMPQ_AGG(add, PMPQ_AGG_OP, 0) PMPQ_AGG(mul, PMPQ_AGG_OP, 0) #define PMPQ_AGG_REL(op, rel) \ do { \ if (mpq_cmp(*a, q) rel 0) { \ mpq_set(*a, q); \ } \ } while (0) PMPQ_AGG(min, PMPQ_AGG_REL, >) PMPQ_AGG(max, PMPQ_AGG_REL, <) pgmp-rel-1.0.4/src/pmpq_arith.c000066400000000000000000000147151361705073600163560ustar00rootroot00000000000000/* pmpq_arith -- mpq arithmetic functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpq.h" #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "access/hash.h" /* for hash_any */ /* * Unary operators */ PGMP_PG_FUNCTION(pmpq_uplus) { const pmpq *pq1; pmpq *res; pq1 = PGMP_GETARG_PMPQ(0); res = (pmpq *)palloc(VARSIZE(pq1)); memcpy(res, pq1, VARSIZE(pq1)); PG_RETURN_POINTER(res); } #define PMPQ_UN(op, CHECK) \ \ PGMP_PG_FUNCTION(pmpq_ ## op) \ { \ const mpq_t q; \ mpq_t qf; \ \ PGMP_GETARG_MPQ(q, 0); \ CHECK(q); \ \ mpq_init(qf); \ mpq_ ## op (qf, q); \ \ PGMP_RETURN_MPQ(qf); \ } PMPQ_UN(neg, PMPQ_NO_CHECK) PMPQ_UN(abs, PMPQ_NO_CHECK) PMPQ_UN(inv, PMPQ_CHECK_DIV0) /* * Binary operators */ /* Template to generate binary operators */ #define PMPQ_OP(op, CHECK2) \ \ PGMP_PG_FUNCTION(pmpq_ ## op) \ { \ const mpq_t q1; \ const mpq_t q2; \ mpq_t qf; \ \ PGMP_GETARG_MPQ(q1, 0); \ PGMP_GETARG_MPQ(q2, 1); \ CHECK2(q2); \ \ mpq_init(qf); \ mpq_ ## op (qf, q1, q2); \ \ PGMP_RETURN_MPQ(qf); \ } PMPQ_OP(add, PMPQ_NO_CHECK) PMPQ_OP(sub, PMPQ_NO_CHECK) PMPQ_OP(mul, PMPQ_NO_CHECK) PMPQ_OP(div, PMPQ_CHECK_DIV0) /* Functions defined on bit count */ #define PMPQ_BIT(op) \ \ PGMP_PG_FUNCTION(pmpq_ ## op) \ { \ const mpq_t q; \ unsigned long b; \ mpq_t qf; \ \ PGMP_GETARG_MPQ(q, 0); \ PGMP_GETARG_ULONG(b, 1); \ \ mpq_init(qf); \ mpq_ ## op (qf, q, b); \ \ PGMP_RETURN_MPQ(qf); \ } PMPQ_BIT(mul_2exp) PMPQ_BIT(div_2exp) /* * Comparison operators */ PGMP_PG_FUNCTION(pmpq_cmp) { const mpq_t q1; const mpq_t q2; PGMP_GETARG_MPQ(q1, 0); PGMP_GETARG_MPQ(q2, 1); PG_RETURN_INT32(mpq_cmp(q1, q2)); } #define PMPQ_CMP_EQ(op, rel) \ \ PGMP_PG_FUNCTION(pmpq_ ## op) \ { \ const mpq_t q1; \ const mpq_t q2; \ \ PGMP_GETARG_MPQ(q1, 0); \ PGMP_GETARG_MPQ(q2, 1); \ \ PG_RETURN_BOOL(mpq_equal(q1, q2) rel 0); \ } PMPQ_CMP_EQ(eq, !=) /* note that the operators are reversed */ PMPQ_CMP_EQ(ne, ==) #define PMPQ_CMP(op, rel) \ \ PGMP_PG_FUNCTION(pmpq_ ## op) \ { \ const mpq_t q1; \ const mpq_t q2; \ \ PGMP_GETARG_MPQ(q1, 0); \ PGMP_GETARG_MPQ(q2, 1); \ \ PG_RETURN_BOOL(mpq_cmp(q1, q2) rel 0); \ } PMPQ_CMP(gt, >) PMPQ_CMP(ge, >=) PMPQ_CMP(lt, <) PMPQ_CMP(le, <=) /* The hash of an integer mpq is the same of the same number as mpz. * This allows cross-type hash joins with mpz and builtins. */ PGMP_PG_FUNCTION(pmpq_hash) { const mpq_t q; Datum nhash; PGMP_GETARG_MPQ(q, 0); nhash = pmpz_get_hash(mpq_numref(q)); if (mpz_cmp_si(mpq_denref(q), 1L) == 0) { return nhash; } PG_RETURN_INT32( DatumGetInt32(nhash) ^ hash_any( (unsigned char *)LIMBS(mpq_denref(q)), NLIMBS(mpq_denref(q)) * sizeof(mp_limb_t))); } /* limit_den */ static void limit_den(mpq_ptr q_out, mpq_srcptr q_in, mpz_srcptr max_den); PGMP_PG_FUNCTION(pmpq_limit_den) { const mpq_t q_in; const mpz_t max_den; mpq_t q_out; PGMP_GETARG_MPQ(q_in, 0); if (PG_NARGS() >= 2) { PGMP_GETARG_MPZ(max_den, 1); } else { mpz_init_set_si((mpz_ptr)max_den, 1000000); } if (mpz_cmp_si(max_den, 1) < 0) { ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("max_den should be at least 1"))); \ } mpq_init(q_out); limit_den(q_out, q_in, max_den); PGMP_RETURN_MPQ(q_out); } /* * Set q_out to the closest fraction to q_in with denominator at most max_den * * Ported from Python library: see * https://hg.python.org/cpython/file/v2.7/Lib/fractions.py#l206 * for implementation notes. */ static void limit_den(mpq_ptr q_out, mpq_srcptr q_in, mpz_srcptr max_den) { mpz_t p0, q0, p1, q1; mpz_t n, d; mpz_t a, q2; mpz_t k; mpq_t b1, b2; mpq_t ab1, ab2; if (mpz_cmp(mpq_denref(q_in), max_den) <= 0) { mpq_set(q_out, q_in); return; } /* p0, q0, p1, q1 = 0, 1, 1, 0 */ mpz_init_set_si(p0, 0); mpz_init_set_si(q0, 1); mpz_init_set_si(p1, 1); mpz_init_set_si(q1, 0); /* n, d = self._numerator, self._denominator */ mpz_init_set(n, mpq_numref(q_in)); mpz_init_set(d, mpq_denref(q_in)); mpz_init(a); mpz_init(q2); for (;;) { /* a = n // d */ mpz_tdiv_q(a, n, d); /* q2 = q0+a*q1 */ mpz_set(q2, q0); mpz_addmul(q2, a, q1); if (mpz_cmp(q2, max_den) > 0) { break; } /* p0, q0, p1, q1 = p1, q1, p0+a*p1, q2 */ mpz_swap(p0, p1); mpz_addmul(p1, a, p0); mpz_swap(q0, q1); mpz_swap(q1, q2); /* n, d = d, n-a*d */ mpz_swap(n, d); mpz_submul(d, a, n); } /* k = (max_denominator - q0) // q1 */ mpz_init(k); mpz_sub(k, max_den, q0); mpz_tdiv_q(k, k, q1); /* bound1 = Fraction(p0+k*p1, q0+k*q1) */ mpq_init(b1); mpz_addmul(p0, k, p1); mpz_set(mpq_numref(b1), p0); mpz_addmul(q0, k, q1); mpz_set(mpq_denref(b1), q0); mpq_canonicalize(b1); /* bound2 = Fraction(p1, q1) */ mpq_init(b2); mpz_set(mpq_numref(b2), p1); mpz_set(mpq_denref(b2), q1); mpq_canonicalize(b2); /* if abs(bound2 - self) <= abs(bound1 - self): */ mpq_init(ab1); mpq_sub(ab1, b1, q_in); mpq_abs(ab1, ab1); mpq_init(ab2); mpq_sub(ab2, b2, q_in); mpq_abs(ab2, ab2); if (mpq_cmp(ab2, ab1) <= 0) { /* return bound2 */ mpq_set(q_out, b2); } else { /* return bound1 */ mpq_set(q_out, b1); } } pgmp-rel-1.0.4/src/pmpq_io.c000066400000000000000000000276331361705073600156610ustar00rootroot00000000000000/* pmpq_io -- mpq Input/Output functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpq.h" #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "utils/builtins.h" /* for numeric_out */ #include /* * Input/Output functions */ PGMP_PG_FUNCTION(pmpq_in) { char *str; mpq_t q; str = PG_GETARG_CSTRING(0); mpq_init(q); if (0 != mpq_set_str(q, str, 0)) { ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for mpq: \"%s\"", str))); } ERROR_IF_DENOM_ZERO(mpq_denref(q)); mpq_canonicalize(q); PGMP_RETURN_MPQ(q); } PGMP_PG_FUNCTION(pmpq_in_base) { int base; char *str; mpq_t q; base = PG_GETARG_INT32(1); if (!(base == 0 || (2 <= base && base <= PGMP_MAXBASE_IO))) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid base for mpq input: %d", base), errhint("base should be between 2 and %d", PGMP_MAXBASE_IO))); } str = TextDatumGetCString(PG_GETARG_POINTER(0)); mpq_init(q); if (0 != mpq_set_str(q, str, base)) { const char *ell; const int maxchars = 50; ell = (strlen(str) > maxchars) ? "..." : ""; ereport(ERROR, ( errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input for mpq base %d: \"%.*s%s\"", base, 50, str, ell))); } ERROR_IF_DENOM_ZERO(mpq_denref(q)); mpq_canonicalize(q); PGMP_RETURN_MPQ(q); } PGMP_PG_FUNCTION(pmpq_out) { const mpq_t q; char *buf; PGMP_GETARG_MPQ(q, 0); /* Allocate the output buffer manually - see mpmz_out to know why */ buf = palloc(3 /* add sign, slash and null */ + mpz_sizeinbase(mpq_numref(q), 10) + mpz_sizeinbase(mpq_denref(q), 10)); PG_RETURN_CSTRING(mpq_get_str(buf, 10, q)); } PGMP_PG_FUNCTION(pmpq_out_base) { const mpq_t q; int base; char *buf; PGMP_GETARG_MPQ(q, 0); base = PG_GETARG_INT32(1); if (!((-36 <= base && base <= -2) || (2 <= base && base <= PGMP_MAXBASE_IO))) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid base for mpq output: %d", base), errhint("base should be between -36 and -2 or between 2 and %d", PGMP_MAXBASE_IO))); } /* Allocate the output buffer manually - see mpmz_out to know why */ buf = palloc(3 /* add sign, slash and null */ + mpz_sizeinbase(mpq_numref(q), ABS(base)) + mpz_sizeinbase(mpq_denref(q), ABS(base))); PG_RETURN_CSTRING(mpq_get_str(buf, base, q)); } /* * Cast functions */ static Datum _pmpq_from_long(long in); PGMP_PG_FUNCTION(pmpq_from_int2) { int16 in = PG_GETARG_INT16(0); return _pmpq_from_long(in); } PGMP_PG_FUNCTION(pmpq_from_int4) { int32 in = PG_GETARG_INT32(0); return _pmpq_from_long(in); } static Datum _pmpq_from_long(long in) { mpq_t q; mpz_init_set_si(mpq_numref(q), in); mpz_init_set_si(mpq_denref(q), 1L); PGMP_RETURN_MPQ(q); } static Datum _pmpq_from_double(double in); PGMP_PG_FUNCTION(pmpq_from_float4) { double in = (double)PG_GETARG_FLOAT4(0); return _pmpq_from_double(in); } PGMP_PG_FUNCTION(pmpq_from_float8) { double in = PG_GETARG_FLOAT8(0); return _pmpq_from_double(in); } static Datum _pmpq_from_double(double in) { mpq_t q; mpq_init(q); mpq_set_d(q, in); PGMP_RETURN_MPQ(q); } /* to convert from int8 we piggyback all the mess we've made for mpz */ Datum pmpz_from_int8(PG_FUNCTION_ARGS); PGMP_PG_FUNCTION(pmpq_from_int8) { mpq_t q; mpz_t tmp; mpz_from_pmpz(tmp, (pmpz *)DirectFunctionCall1(pmpz_from_int8, PG_GETARG_DATUM(0))); /* Make a copy of the num as MPQ will try to realloc on it */ mpz_init_set(mpq_numref(q), tmp); mpz_init_set_si(mpq_denref(q), 1L); PGMP_RETURN_MPQ(q); } /* To convert from numeric we convert the numeric in str, then work on that */ PGMP_PG_FUNCTION(pmpq_from_numeric) { mpq_t q; char *sn, *pn; sn = DatumGetCString(DirectFunctionCall1(numeric_out, PG_GETARG_DATUM(0))); if ((pn = strchr(sn, '.'))) { char *sd, *pd; /* Convert "123.45" into "12345" and produce "100" in the process. */ pd = sd = (char *)palloc(strlen(sn)); *pd++ = '1'; while (pn[1]) { pn[0] = pn[1]; ++pn; *pd++ = '0'; } *pd = *pn = '\0'; if (0 != mpz_init_set_str(mpq_numref(q), sn, 10)) { goto error; } mpz_init_set_str(mpq_denref(q), sd, 10); mpq_canonicalize(q); } else { /* just an integer */ if (0 != mpz_init_set_str(mpq_numref(q), sn, 10)) { goto error; } mpz_init_set_si(mpq_denref(q), 1L); } PGMP_RETURN_MPQ(q); error: ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("can't convert numeric value to mpq: \"%s\"", sn))); PG_RETURN_NULL(); } PGMP_PG_FUNCTION(pmpq_from_mpz) { mpq_t q; mpz_t tmp; /* Make a copy of the num as MPQ will try to realloc on it */ PGMP_GETARG_MPZ(tmp, 0); mpz_init_set(mpq_numref(q), tmp); mpz_init_set_si(mpq_denref(q), 1L); PGMP_RETURN_MPQ(q); } PGMP_PG_FUNCTION(pmpq_to_mpz) { const mpq_t q; mpz_t z; PGMP_GETARG_MPQ(q, 0); mpz_init(z); mpz_set_q(z, q); PGMP_RETURN_MPZ(z); } #define PMPQ_TO_INT(type) \ \ Datum pmpz_to_ ## type (PG_FUNCTION_ARGS); \ \ PGMP_PG_FUNCTION(pmpq_to_ ## type) \ { \ const mpq_t q; \ mpz_t z; \ \ PGMP_GETARG_MPQ(q, 0); \ \ mpz_init(z); \ mpz_set_q(z, q); \ \ return DirectFunctionCall1(pmpz_to_ ## type, (Datum)pmpz_from_mpz(z)); \ } PMPQ_TO_INT(int2) PMPQ_TO_INT(int4) PMPQ_TO_INT(int8) PGMP_PG_FUNCTION(pmpq_to_float4) { const mpq_t q; PGMP_GETARG_MPQ(q, 0); PG_RETURN_FLOAT4((float4)mpq_get_d(q)); } PGMP_PG_FUNCTION(pmpq_to_float8) { const mpq_t q; PGMP_GETARG_MPQ(q, 0); PG_RETURN_FLOAT8((float8)mpq_get_d(q)); } PGMP_PG_FUNCTION(pmpq_to_numeric) { const mpq_t q; int32 typmod; long scale; mpz_t z; char *buf; int sbuf, snum; PGMP_GETARG_MPQ(q, 0); typmod = PG_GETARG_INT32(1); /* Parse precision and scale from the type modifier */ if (typmod >= VARHDRSZ) { scale = (typmod - VARHDRSZ) & 0xffff; } else { scale = 15; } if (scale) { /* Convert q into a scaled z */ char *cmult; mpz_t mult; /* create 10000... with as many 0s as the scale */ cmult = (char *)palloc(scale + 2); memset(cmult + 1, '0', scale); cmult[0] = '1'; cmult[scale + 1] = '\0'; mpz_init_set_str(mult, cmult, 10); pfree(cmult); mpz_init(z); mpz_mul(z, mpq_numref(q), mult); sbuf = mpz_sizeinbase(z, 10); /* size of the output buffer */ mpz_tdiv_q(z, z, mpq_denref(q)); snum = mpz_sizeinbase(z, 10); /* size of the number */ } else { /* Just truncate q into an integer */ mpz_init(z); mpz_set_q(z, q); sbuf = snum = mpz_sizeinbase(z, 10); } /* If the numer is 0, everything is a special case: bail out */ if (mpz_cmp_si(z, 0) == 0) { return DirectFunctionCall3(numeric_in, CStringGetDatum("0"), ObjectIdGetDatum(0), /* unused 2nd value */ Int32GetDatum(typmod)); } /* convert z into a string */ buf = palloc(sbuf + 3); /* add sign, point and null */ mpz_get_str(buf, 10, z); if (scale) { char *end, *p; /* Left pad with 0s the number if smaller than the buffer */ if (snum < sbuf) { char *num0 = buf + (buf[0] == '-'); /* start of the num w/o sign */ memmove(num0 + (sbuf - snum), num0, snum + 1); memset(num0, '0', sbuf - snum); } end = buf + strlen(buf); /* Add the decimal point in the right place */ memmove(end - scale + 1, end - scale, scale + 1); end[-scale] = '.'; /* delete trailing 0s or they will be used to add extra precision */ if (typmod < VARHDRSZ) { /* scale was not specified */ for (p = end; p > (end - scale) && *p == '0'; --p) { *p = '\0'; } /* Don't leave a traliling point */ if (*p == '.') { *p = '\0'; } } } /* use numeric_in to build the value from the string and to apply the * typemod (which may result in overflow) */ return DirectFunctionCall3(numeric_in, CStringGetDatum(buf), ObjectIdGetDatum(0), /* unused 2nd value */ Int32GetDatum(typmod)); } /* * Constructor and accessors to num and den */ PGMP_PG_FUNCTION(pmpq_mpz_mpz) { const mpz_t num; const mpz_t den; mpq_t q; /* We must take a copy of num and den because they may be modified by * canonicalize */ PGMP_GETARG_MPZ(num, 0); PGMP_GETARG_MPZ(den, 1); ERROR_IF_DENOM_ZERO(den); /* Put together the input and canonicalize */ mpz_init_set(mpq_numref(q), num); mpz_init_set(mpq_denref(q), den); mpq_canonicalize(q); PGMP_RETURN_MPQ(q); } PGMP_PG_FUNCTION(pmpq_int4_int4) { int32 num = PG_GETARG_INT32(0); int32 den = PG_GETARG_INT32(1); mpq_t q; /* Put together the input and canonicalize */ mpz_init_set_si(mpq_numref(q), (long)num); mpz_init_set_si(mpq_denref(q), (long)den); ERROR_IF_DENOM_ZERO(mpq_denref(q)); mpq_canonicalize(q); PGMP_RETURN_MPQ(q); } PGMP_PG_FUNCTION(pmpq_numeric_numeric) { char *sn; char *sd; mpq_t q; sn = DatumGetCString(DirectFunctionCall1(numeric_out, PG_GETARG_DATUM(0))); if (0 != mpz_init_set_str(mpq_numref(q), sn, 10)) { ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("can't handle numeric value at numerator: %s", sn), errhint("the mpq components should be integers"))); } sd = DatumGetCString(DirectFunctionCall1(numeric_out, PG_GETARG_DATUM(1))); if (0 != mpz_init_set_str(mpq_denref(q), sd, 10)) { ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("can't handle numeric value at denominator: %s", sd), errhint("the mpq components should be integers"))); } ERROR_IF_DENOM_ZERO(mpq_denref(q)); mpq_canonicalize(q); PGMP_RETURN_MPQ(q); } PGMP_PG_FUNCTION(pmpq_num) { const mpq_t q; mpz_t z; PGMP_GETARG_MPQ(q, 0); mpz_init_set(z, mpq_numref(q)); PGMP_RETURN_MPZ(z); } PGMP_PG_FUNCTION(pmpq_den) { const mpq_t q; mpz_t z; PGMP_GETARG_MPQ(q, 0); mpz_init_set(z, mpq_denref(q)); PGMP_RETURN_MPZ(z); } pgmp-rel-1.0.4/src/pmpz.c000066400000000000000000000063161361705073600151760ustar00rootroot00000000000000/* pmpz -- PostgreSQL data type for GMP mpz * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" /* To be referred to to represent the zero */ extern const mp_limb_t _pgmp_limb_0; /* * Create a pmpz structure from the content of a mpz. * * The function relies on the limbs being allocated using the GMP custom * allocator: such allocator leaves PGMP_MAX_HDRSIZE bytes *before* the * returned pointer. We scrubble that area prepending the pmpz header. */ pmpz * pmpz_from_mpz(mpz_srcptr z) { pmpz *res; if (LIKELY(ALLOC(z))) { size_t slimbs; int sign; int size = SIZ(z); res = (pmpz *)((char *)LIMBS(z) - PMPZ_HDRSIZE); if (size >= 0) { slimbs = size * sizeof(mp_limb_t); sign = 0; } else { slimbs = -size * sizeof(mp_limb_t); sign = PMPZ_SIGN_MASK; } SET_VARSIZE(res, PMPZ_HDRSIZE + slimbs); res->mdata = sign; /* implicit version: 0 */ } else { /* No allocation for the limbs: allocate something of ours */ res = palloc(PMPZ_HDRSIZE); SET_VARSIZE(res, PMPZ_HDRSIZE); res->mdata = 0; /* version: 0 */ } return res; } /* * Initialize a mpz from the content of a datum * * NOTE: the function takes a pointer to a const and changes the structure. * This allows to define the structure as const in the calling function and * avoid the risk to change it inplace, which may corrupt the database data. * * The structure populated doesn't own the pointed data, so it must not be * changed in any way and must not be cleared. */ void mpz_from_pmpz(mpz_srcptr z, const pmpz *pz) { int nlimbs; mpz_ptr wz; if (UNLIKELY(0 != (PMPZ_VERSION(pz)))) { ereport(ERROR, ( errcode(ERRCODE_DATA_EXCEPTION), errmsg("unsupported mpz version: %d", PMPZ_VERSION(pz)))); } /* discard the const qualifier */ wz = (mpz_ptr)z; nlimbs = (VARSIZE(pz) - PMPZ_HDRSIZE) / sizeof(mp_limb_t); if (LIKELY(nlimbs != 0)) { ALLOC(wz) = nlimbs; SIZ(wz) = PMPZ_NEGATIVE(pz) ? -nlimbs : nlimbs; LIMBS(wz) = (mp_limb_t *)pz->data; } else { /* in the datum there is just the varlena header * so let's just refer to some static const */ ALLOC(wz) = 1; SIZ(wz) = 0; LIMBS(wz) = (mp_limb_t *)&_pgmp_limb_0; } } pgmp-rel-1.0.4/src/pmpz.h000066400000000000000000000113151361705073600151760ustar00rootroot00000000000000/* pmpz -- PostgreSQL data type for GMP mpz * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #ifndef __PMPZ_H__ #define __PMPZ_H__ #include #include "postgres.h" typedef struct { char vl_len_[4]; /* varlena header */ unsigned mdata; /* version number, sign */ mp_limb_t data[1]; /* limbs */ } pmpz; /* Must be not larger than PGMP_MAX_HDRSIZE */ #define PMPZ_HDRSIZE MAXALIGN(offsetof(pmpz,data)) /* Macros to convert mpz arguments and return values */ #define PGMP_GETARG_PMPZ(n) \ ((pmpz*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(n)))) #define PGMP_GETARG_MPZ(z,n) \ mpz_from_pmpz(z, PGMP_GETARG_PMPZ(n)); #define PGMP_RETURN_MPZ(z) \ PG_RETURN_POINTER(pmpz_from_mpz(z)) #define PGMP_RETURN_MPZ_MPZ(z1,z2) \ do { \ TupleDesc _tupdesc; \ Datum _result[2]; \ bool _isnull[2] = {0,0}; \ \ if (get_call_result_type(fcinfo, NULL, &_tupdesc) != TYPEFUNC_COMPOSITE) \ ereport(ERROR, \ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ errmsg("function returning composite called in context " \ "that cannot accept type composite"))); \ \ _tupdesc = BlessTupleDesc(_tupdesc); \ \ _result[0] = (Datum)pmpz_from_mpz(z1); \ _result[1] = (Datum)pmpz_from_mpz(z2); \ \ return HeapTupleGetDatum(heap_form_tuple(_tupdesc, _result, _isnull)); \ } while (0) #define PGMP_RETURN_MPZ_MPZ_MPZ(z1,z2,z3) \ do { \ TupleDesc _tupdesc; \ Datum _result[3]; \ bool _isnull[3] = {0,0,0}; \ \ if (get_call_result_type(fcinfo, NULL, &_tupdesc) != TYPEFUNC_COMPOSITE) \ ereport(ERROR, \ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ errmsg("function returning composite called in context " \ "that cannot accept type composite"))); \ \ _tupdesc = BlessTupleDesc(_tupdesc); \ \ _result[0] = (Datum)pmpz_from_mpz(z1); \ _result[1] = (Datum)pmpz_from_mpz(z2); \ _result[2] = (Datum)pmpz_from_mpz(z3); \ \ return HeapTupleGetDatum(heap_form_tuple(_tupdesc, _result, _isnull)); \ } while (0) /* Allow versioning of the data in the database. * Versions 0-7 allowed... hope to not change my mind more than 8 times */ #define PMPZ_VERSION_MASK 0x07 #define PMPZ_SIGN_MASK 0x80 #define PMPZ_VERSION(mz) (((mz)->mdata) & PMPZ_VERSION_MASK) #define PMPZ_SET_VERSION(mdata,v) \ (((mdata) & ~PMPZ_VERSION_MASK) | ((v) & PMPZ_VERSION_MASK)) #define PMPZ_SET_NEGATIVE(mdata) ((mdata) | PMPZ_SIGN_MASK) #define PMPZ_SET_POSITIVE(mdata) ((mdata) & ~PMPZ_SIGN_MASK) #define PMPZ_NEGATIVE(mz) (((mz)->mdata) & PMPZ_SIGN_MASK) /* Definitions useful for internal use in mpz-related modules */ pmpz * pmpz_from_mpz(mpz_srcptr z); void mpz_from_pmpz(mpz_srcptr z, const pmpz *pz); int pmpz_get_int64(mpz_srcptr z, int64 *out); Datum pmpz_get_hash(mpz_srcptr z); #define MPZ_IS_ZERO(z) (SIZ(z) == 0) /* Macros to be used in functions wrappers to limit the arguments domain */ #define PMPZ_NO_CHECK(arg) #define PMPZ_CHECK_DIV0(arg) \ do { \ if (UNLIKELY(MPZ_IS_ZERO(arg))) \ { \ ereport(ERROR, ( \ errcode(ERRCODE_DIVISION_BY_ZERO), \ errmsg("division by zero"))); \ } \ } while (0) #define PMPZ_CHECK_NONEG(arg) \ do { \ if (UNLIKELY(SIZ(arg) < 0)) \ { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument can't be negative"))); \ } \ } while (0) #define PMPZ_CHECK_LONG_POS(arg) \ do { \ if (UNLIKELY((arg) <= 0)) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument must be positive") )); \ } \ } while (0) #define PMPZ_CHECK_LONG_NONEG(arg) \ do { \ if (UNLIKELY((arg) < 0)) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument can't be negative") )); \ } \ } while (0) #endif /* __PMPZ_H__ */ pgmp-rel-1.0.4/src/pmpz_agg.c000066400000000000000000000054321361705073600160120ustar00rootroot00000000000000/* pmpz_agg -- mpz aggregation functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp_utils.h" /* for AggCheckCallContext on PG < 9.0 */ #include "pgmp-impl.h" #include "fmgr.h" /* Convert an inplace accumulator into a pmpz structure. * * This function is strict, so don't care about NULLs */ PGMP_PG_FUNCTION(_pmpz_from_agg) { mpz_t *a; a = (mpz_t *)PG_GETARG_POINTER(0); PGMP_RETURN_MPZ(*a); } /* Macro to create an accumulation function from a gmp operator. * * This function can't be strict because the internal state is not compatible * with the base type. */ #define PMPZ_AGG(op, BLOCK, rel) \ \ PGMP_PG_FUNCTION(_pmpz_agg_ ## op) \ { \ mpz_t *a; \ const mpz_t z; \ MemoryContext oldctx; \ MemoryContext aggctx; \ \ if (UNLIKELY(!AggCheckCallContext(fcinfo, &aggctx))) \ { \ ereport(ERROR, \ (errcode(ERRCODE_DATA_EXCEPTION), \ errmsg("_mpz_agg_" #op " can only be called in accumulation"))); \ } \ \ if (PG_ARGISNULL(1)) { \ if (PG_ARGISNULL(0)) { \ PG_RETURN_NULL(); \ } \ else { \ PG_RETURN_POINTER(PG_GETARG_POINTER(0)); \ } \ } \ \ PGMP_GETARG_MPZ(z, 1); \ \ oldctx = MemoryContextSwitchTo(aggctx); \ \ if (LIKELY(!PG_ARGISNULL(0))) { \ a = (mpz_t *)PG_GETARG_POINTER(0); \ BLOCK(op, rel); \ } \ else { /* uninitialized */ \ a = (mpz_t *)palloc(sizeof(mpz_t)); \ mpz_init_set(*a, z); \ } \ \ MemoryContextSwitchTo(oldctx); \ \ PG_RETURN_POINTER(a); \ } #define PMPZ_AGG_OP(op, rel) \ mpz_ ## op (*a, *a, z) PMPZ_AGG(add, PMPZ_AGG_OP, 0) PMPZ_AGG(mul, PMPZ_AGG_OP, 0) PMPZ_AGG(and, PMPZ_AGG_OP, 0) PMPZ_AGG(ior, PMPZ_AGG_OP, 0) PMPZ_AGG(xor, PMPZ_AGG_OP, 0) #define PMPZ_AGG_REL(op, rel) \ do { \ if (mpz_cmp(*a, z) rel 0) { \ mpz_set(*a, z); \ } \ } while (0) PMPZ_AGG(min, PMPZ_AGG_REL, >) PMPZ_AGG(max, PMPZ_AGG_REL, <) pgmp-rel-1.0.4/src/pmpz_arith.c000066400000000000000000000170151361705073600163630ustar00rootroot00000000000000/* pmpz_arith -- mpz arithmetic functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "funcapi.h" #include "access/hash.h" /* for hash_any */ #if PG_VERSION_NUM >= 90300 #include /* for heap_form_tuple */ #endif #if PG_VERSION_NUM >= 100000 #include /* for hashint8 */ #endif /* * Unary operators */ PGMP_PG_FUNCTION(pmpz_uplus) { const pmpz *pz1; pmpz *res; pz1 = PGMP_GETARG_PMPZ(0); res = (pmpz *)palloc(VARSIZE(pz1)); memcpy(res, pz1, VARSIZE(pz1)); PG_RETURN_POINTER(res); } /* Template to generate unary functions */ #define PMPZ_UN(op, CHECK) \ \ PGMP_PG_FUNCTION(pmpz_ ## op) \ { \ const mpz_t z1; \ mpz_t zf; \ \ PGMP_GETARG_MPZ(z1, 0); \ CHECK(z1); \ \ mpz_init(zf); \ mpz_ ## op (zf, z1); \ \ PGMP_RETURN_MPZ(zf); \ } PMPZ_UN(neg, PMPZ_NO_CHECK) PMPZ_UN(abs, PMPZ_NO_CHECK) PMPZ_UN(sqrt, PMPZ_CHECK_NONEG) PMPZ_UN(com, PMPZ_NO_CHECK) /* * Binary operators */ /* Operators defined (mpz, mpz) -> mpz. * * CHECK2 is a check performed on the 2nd argument. */ #define PMPZ_OP(op, CHECK2) \ \ PGMP_PG_FUNCTION(pmpz_ ## op) \ { \ const mpz_t z1; \ const mpz_t z2; \ mpz_t zf; \ \ PGMP_GETARG_MPZ(z1, 0); \ PGMP_GETARG_MPZ(z2, 1); \ CHECK2(z2); \ \ mpz_init(zf); \ mpz_ ## op (zf, z1, z2); \ \ PGMP_RETURN_MPZ(zf); \ } PMPZ_OP(add, PMPZ_NO_CHECK) PMPZ_OP(sub, PMPZ_NO_CHECK) PMPZ_OP(mul, PMPZ_NO_CHECK) PMPZ_OP(tdiv_q, PMPZ_CHECK_DIV0) PMPZ_OP(tdiv_r, PMPZ_CHECK_DIV0) PMPZ_OP(cdiv_q, PMPZ_CHECK_DIV0) PMPZ_OP(cdiv_r, PMPZ_CHECK_DIV0) PMPZ_OP(fdiv_q, PMPZ_CHECK_DIV0) PMPZ_OP(fdiv_r, PMPZ_CHECK_DIV0) PMPZ_OP(divexact, PMPZ_CHECK_DIV0) PMPZ_OP(and, PMPZ_NO_CHECK) PMPZ_OP(ior, PMPZ_NO_CHECK) PMPZ_OP(xor, PMPZ_NO_CHECK) PMPZ_OP(gcd, PMPZ_NO_CHECK) PMPZ_OP(lcm, PMPZ_NO_CHECK) PMPZ_OP(remove, PMPZ_NO_CHECK) /* TODO: return value not returned */ /* Operators defined (mpz, mpz) -> (mpz, mpz). */ #define PMPZ_OP2(op, CHECK2) \ \ PGMP_PG_FUNCTION(pmpz_ ## op) \ { \ const mpz_t z1; \ const mpz_t z2; \ mpz_t zf1; \ mpz_t zf2; \ \ PGMP_GETARG_MPZ(z1, 0); \ PGMP_GETARG_MPZ(z2, 1); \ CHECK2(z2); \ \ mpz_init(zf1); \ mpz_init(zf2); \ mpz_ ## op (zf1, zf2, z1, z2); \ \ PGMP_RETURN_MPZ_MPZ(zf1, zf2); \ } PMPZ_OP2(tdiv_qr, PMPZ_CHECK_DIV0) PMPZ_OP2(cdiv_qr, PMPZ_CHECK_DIV0) PMPZ_OP2(fdiv_qr, PMPZ_CHECK_DIV0) /* Functions defined on unsigned long */ #define PMPZ_OP_UL(op, CHECK1, CHECK2) \ \ PGMP_PG_FUNCTION(pmpz_ ## op) \ { \ const mpz_t z; \ unsigned long b; \ mpz_t zf; \ \ PGMP_GETARG_MPZ(z, 0); \ CHECK1(z); \ \ PGMP_GETARG_ULONG(b, 1); \ CHECK2(b); \ \ mpz_init(zf); \ mpz_ ## op (zf, z, b); \ \ PGMP_RETURN_MPZ(zf); \ } PMPZ_OP_UL(pow_ui, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_UL(root, PMPZ_CHECK_NONEG, PMPZ_CHECK_LONG_POS) PMPZ_OP_UL(bin_ui, PMPZ_NO_CHECK, PMPZ_CHECK_LONG_NONEG) /* Functions defined on bit count * * mp_bitcnt_t is defined as unsigned long. */ #define PMPZ_OP_BITCNT PMPZ_OP_UL PMPZ_OP_BITCNT(mul_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_BITCNT(tdiv_q_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_BITCNT(tdiv_r_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_BITCNT(cdiv_q_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_BITCNT(cdiv_r_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_BITCNT(fdiv_q_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) PMPZ_OP_BITCNT(fdiv_r_2exp, PMPZ_NO_CHECK, PMPZ_NO_CHECK) /* Unary predicates */ #define PMPZ_PRED(pred) \ \ PGMP_PG_FUNCTION(pmpz_ ## pred) \ { \ const mpz_t op; \ \ PGMP_GETARG_MPZ(op, 0); \ \ PG_RETURN_BOOL(mpz_ ## pred ## _p(op)); \ } PMPZ_PRED(even) PMPZ_PRED(odd) PMPZ_PRED(perfect_power) PMPZ_PRED(perfect_square) /* * Comparison operators */ PGMP_PG_FUNCTION(pmpz_cmp) { const mpz_t z1; const mpz_t z2; PGMP_GETARG_MPZ(z1, 0); PGMP_GETARG_MPZ(z2, 1); PG_RETURN_INT32(mpz_cmp(z1, z2)); } #define PMPZ_CMP(op, rel) \ \ PGMP_PG_FUNCTION(pmpz_ ## op) \ { \ const mpz_t z1; \ const mpz_t z2; \ \ PGMP_GETARG_MPZ(z1, 0); \ PGMP_GETARG_MPZ(z2, 1); \ \ PG_RETURN_BOOL(mpz_cmp(z1, z2) rel 0); \ } PMPZ_CMP(eq, ==) PMPZ_CMP(ne, !=) PMPZ_CMP(gt, >) PMPZ_CMP(ge, >=) PMPZ_CMP(lt, <) PMPZ_CMP(le, <=) /* The hash of an mpz fitting into a int64 is the same of the PG builtin. * This allows cross-type hash joins int2/int4/int8. */ PGMP_PG_FUNCTION(pmpz_hash) { const mpz_t z; PGMP_GETARG_MPZ(z, 0); return pmpz_get_hash(z); } Datum pmpz_get_hash(mpz_srcptr z) { int64 z64; if (0 == pmpz_get_int64(z, &z64)) { return DirectFunctionCall1(hashint8, Int64GetDatumFast(z64)); } PG_RETURN_INT32(hash_any( (unsigned char *)LIMBS(z), NLIMBS(z) * sizeof(mp_limb_t))); } /* * Misc functions... each one has its own signature, sigh. */ PGMP_PG_FUNCTION(pmpz_sgn) { const mpz_t n; PGMP_GETARG_MPZ(n, 0); PG_RETURN_INT32(mpz_sgn(n)); } PGMP_PG_FUNCTION(pmpz_divisible) { const mpz_t n; const mpz_t d; PGMP_GETARG_MPZ(n, 0); PGMP_GETARG_MPZ(d, 1); /* GMP 4.1 doesn't guard for zero */ #if __GMP_MP_RELEASE < 40200 if (UNLIKELY(MPZ_IS_ZERO(d))) { PG_RETURN_BOOL(MPZ_IS_ZERO(n)); } #endif PG_RETURN_BOOL(mpz_divisible_p(n, d)); } PGMP_PG_FUNCTION(pmpz_divisible_2exp) { const mpz_t n; mp_bitcnt_t b; PGMP_GETARG_MPZ(n, 0); PGMP_GETARG_ULONG(b, 1); PG_RETURN_BOOL(mpz_divisible_2exp_p(n, b)); } PGMP_PG_FUNCTION(pmpz_congruent) { const mpz_t n; const mpz_t c; const mpz_t d; PGMP_GETARG_MPZ(n, 0); PGMP_GETARG_MPZ(c, 1); PGMP_GETARG_MPZ(d, 2); /* GMP 4.1 doesn't guard for zero */ #if __GMP_MP_RELEASE < 40200 if (UNLIKELY(MPZ_IS_ZERO(d))) { PG_RETURN_BOOL(0 == mpz_cmp(n, c)); } #endif PG_RETURN_BOOL(mpz_congruent_p(n, c, d)); } PGMP_PG_FUNCTION(pmpz_congruent_2exp) { const mpz_t n; const mpz_t c; mp_bitcnt_t b; PGMP_GETARG_MPZ(n, 0); PGMP_GETARG_MPZ(c, 1); PGMP_GETARG_ULONG(b, 2); PG_RETURN_BOOL(mpz_congruent_2exp_p(n, c, b)); } PGMP_PG_FUNCTION(pmpz_powm) { const mpz_t base; const mpz_t exp; const mpz_t mod; mpz_t zf; PGMP_GETARG_MPZ(base, 0); PGMP_GETARG_MPZ(exp, 1); PMPZ_CHECK_NONEG(exp); PGMP_GETARG_MPZ(mod, 2); PMPZ_CHECK_DIV0(mod); mpz_init(zf); mpz_powm(zf, base, exp, mod); PGMP_RETURN_MPZ(zf); } pgmp-rel-1.0.4/src/pmpz_bits.c000066400000000000000000000063321361705073600162150ustar00rootroot00000000000000/* pmpz_bits -- bit manipulation functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "funcapi.h" /* Function with a more generic signature are defined in pmpz.arith.c */ /* Macro to get and return mp_bitcnt_t * * the value is defined as unsigned long, so it doesn't fit into an int8 on 64 * bit platform. We'll convert them to/from mpz in SQL. */ #define PGMP_GETARG_BITCNT(tgt,n) \ do { \ mpz_t _tmp; \ PGMP_GETARG_MPZ(_tmp, n); \ \ if (!(mpz_fits_ulong_p(_tmp))) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("argument doesn't fit into a bitcount type") )); \ } \ \ tgt = mpz_get_ui(_tmp); \ } while (0) #define PGMP_RETURN_BITCNT(n) \ do { \ mpz_t _rv; \ mpz_init_set_ui(_rv, n); \ PGMP_RETURN_MPZ(_rv); \ } while (0) /* Return the largest possible mp_bitcnt_t. Useful for testing the return * value of a few other bit manipulation functions as the value depends on the * server platform. */ PGMP_PG_FUNCTION(pgmp_max_bitcnt) { mp_bitcnt_t ret; ret = ~((mp_bitcnt_t)0); PGMP_RETURN_BITCNT(ret); } PGMP_PG_FUNCTION(pmpz_popcount) { const mpz_t z; mp_bitcnt_t ret; PGMP_GETARG_MPZ(z, 0); ret = mpz_popcount(z); PGMP_RETURN_BITCNT(ret); } PGMP_PG_FUNCTION(pmpz_hamdist) { const mpz_t z1; const mpz_t z2; mp_bitcnt_t ret; PGMP_GETARG_MPZ(z1, 0); PGMP_GETARG_MPZ(z2, 1); ret = mpz_hamdist(z1, z2); PGMP_RETURN_BITCNT(ret); } #define PMPZ_SCAN(f) \ \ PGMP_PG_FUNCTION(pmpz_ ## f) \ { \ const mpz_t z; \ mp_bitcnt_t start; \ \ PGMP_GETARG_MPZ(z, 0); \ PGMP_GETARG_BITCNT(start, 1); \ \ PGMP_RETURN_BITCNT(mpz_ ## f(z, start)); \ } PMPZ_SCAN(scan0) PMPZ_SCAN(scan1) /* inplace bit fiddling operations */ #define PMPZ_BIT(f) \ \ PGMP_PG_FUNCTION(pmpz_ ## f) \ { \ const mpz_t z; \ mp_bitcnt_t idx; \ mpz_t ret; \ \ PGMP_GETARG_MPZ(z, 0); \ PGMP_GETARG_BITCNT(idx, 1); \ \ mpz_init_set(ret, z); \ mpz_ ## f(ret, idx); \ PGMP_RETURN_MPZ(ret); \ } PMPZ_BIT(setbit) PMPZ_BIT(clrbit) #if __GMP_MP_RELEASE >= 40200 PMPZ_BIT(combit) #endif PGMP_PG_FUNCTION(pmpz_tstbit) { const mpz_t z; mp_bitcnt_t idx; int32 ret; PGMP_GETARG_MPZ(z, 0); PGMP_GETARG_BITCNT(idx, 1); ret = mpz_tstbit(z, idx); PG_RETURN_INT32(ret); } pgmp-rel-1.0.4/src/pmpz_io.c000066400000000000000000000211221361705073600156550ustar00rootroot00000000000000/* pmpz_io -- mpz Input/Output functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "utils/builtins.h" /* for numeric_out */ #include /* for isinf, isnan */ /* * Input/Output functions */ PGMP_PG_FUNCTION(pmpz_in) { char *str; mpz_t z; str = PG_GETARG_CSTRING(0); if (0 != mpz_init_set_str(z, str, 0)) { const char *ell; const int maxchars = 50; ell = (strlen(str) > maxchars) ? "..." : ""; ereport(ERROR, ( errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input for mpz: \"%.*s%s\"", maxchars, str, ell))); } PGMP_RETURN_MPZ(z); } PGMP_PG_FUNCTION(pmpz_in_base) { int base; char *str; mpz_t z; base = PG_GETARG_INT32(1); if (!(base == 0 || (2 <= base && base <= PGMP_MAXBASE_IO))) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid base for mpz input: %d", base), errhint("base should be between 2 and %d", PGMP_MAXBASE_IO))); } str = TextDatumGetCString(PG_GETARG_POINTER(0)); if (0 != mpz_init_set_str(z, str, base)) { const char *ell; const int maxchars = 50; ell = (strlen(str) > maxchars) ? "..." : ""; ereport(ERROR, ( errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input for mpz base %d: \"%.*s%s\"", base, 50, str, ell))); } PGMP_RETURN_MPZ(z); } PGMP_PG_FUNCTION(pmpz_out) { const mpz_t z; char *buf; PGMP_GETARG_MPZ(z, 0); /* We must allocate the output buffer ourselves because the buffer * returned by mpz_get_str actually starts a few bytes before (because of * the custom GMP allocator); Postgres will try to free the pointer we * return in printtup() so with the offsetted pointer a segfault is * granted. */ buf = palloc(mpz_sizeinbase(z, 10) + 2); /* add sign and null */ PG_RETURN_CSTRING(mpz_get_str(buf, 10, z)); } PGMP_PG_FUNCTION(pmpz_out_base) { const mpz_t z; int base; char *buf; PGMP_GETARG_MPZ(z, 0); base = PG_GETARG_INT32(1); if (!((-36 <= base && base <= -2) || (2 <= base && base <= PGMP_MAXBASE_IO))) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid base for mpz output: %d", base), errhint("base should be between -36 and -2 or between 2 and %d", PGMP_MAXBASE_IO))); } /* Allocate the output buffer manually - see pmpz_out to know why */ buf = palloc(mpz_sizeinbase(z, ABS(base)) + 2); /* add sign and null */ PG_RETURN_CSTRING(mpz_get_str(buf, base, z)); } /* * Cast functions */ static Datum _pmpz_from_long(long in); static Datum _pmpz_from_double(double in); PGMP_PG_FUNCTION(pmpz_from_int2) { int16 in = PG_GETARG_INT16(0); return _pmpz_from_long(in); } PGMP_PG_FUNCTION(pmpz_from_int4) { int32 in = PG_GETARG_INT32(0); return _pmpz_from_long(in); } PGMP_PG_FUNCTION(pmpz_from_int8) { int64 in = PG_GETARG_INT64(0); #if PGMP_LONG_64 return _pmpz_from_long(in); #elif PGMP_LONG_32 int neg = 0; uint32 lo; uint32 hi; mpz_t z; if (LIKELY(in != INT64_MIN)) { if (in < 0) { neg = 1; in = -in; } lo = in & 0xFFFFFFFFUL; hi = in >> 32; if (hi) { mpz_init_set_ui(z, hi); mpz_mul_2exp(z, z, 32); mpz_add_ui(z, z, lo); } else { mpz_init_set_ui(z, lo); } if (neg) { mpz_neg(z, z); } } else { /* this would overflow the long */ mpz_init_set_si(z, 1L); mpz_mul_2exp(z, z, 63); mpz_neg(z, z); } PGMP_RETURN_MPZ(z); #endif } static Datum _pmpz_from_long(long in) { mpz_t z; mpz_init_set_si(z, in); PGMP_RETURN_MPZ(z); } PGMP_PG_FUNCTION(pmpz_from_float4) { float4 in = PG_GETARG_FLOAT4(0); return _pmpz_from_double(in); } PGMP_PG_FUNCTION(pmpz_from_float8) { float8 in = PG_GETARG_FLOAT8(0); return _pmpz_from_double(in); } static Datum _pmpz_from_double(double in) { mpz_t z; if (isinf(in) || isnan(in)) { ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("can't convert float value to mpz: \"%f\"", in))); } mpz_init_set_d(z, in); PGMP_RETURN_MPZ(z); } PGMP_PG_FUNCTION(pmpz_from_numeric) { char *str; char *p; mpz_t z; /* convert the numeric into string. */ str = DatumGetCString(DirectFunctionCall1(numeric_out, PG_GETARG_DATUM(0))); /* truncate the string if it contains a decimal dot */ if ((p = strchr(str, '.'))) { *p = '\0'; } if (0 != mpz_init_set_str(z, str, 10)) { /* here str may have been cropped, but I expect this error * only triggered by NaN, so not in case of regular number */ ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("can't convert numeric value to mpz: \"%s\"", str))); } PGMP_RETURN_MPZ(z); } PGMP_PG_FUNCTION(pmpz_to_int2) { const mpz_t z; int16 out; PGMP_GETARG_MPZ(z, 0); if (!mpz_fits_sshort_p(z)) { ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("numeric value too big to be converted to int2 data type"))); } out = mpz_get_si(z); PG_RETURN_INT16(out); } PGMP_PG_FUNCTION(pmpz_to_int4) { const mpz_t z; int32 out; PGMP_GETARG_MPZ(z, 0); if (!mpz_fits_sint_p(z)) { ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("numeric value too big to be converted to int4 data type"))); } out = mpz_get_si(z); PG_RETURN_INT32(out); } PGMP_PG_FUNCTION(pmpz_to_int8) { const mpz_t z; int64 ret = 0; PGMP_GETARG_MPZ(z, 0); if (0 != pmpz_get_int64(z, &ret)) { ereport(ERROR, ( errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("numeric value too big to be converted to int8 data type"))); } PG_RETURN_INT64(ret); } /* Convert an mpz into and int64. * * return 0 in case of success, else a nonzero value */ int pmpz_get_int64(mpz_srcptr z, int64 *out) { #if PGMP_LONG_64 if (mpz_fits_slong_p(z)) { *out = mpz_get_si(z); return 0; } #elif PGMP_LONG_32 switch (SIZ(z)) { case 0: *out = 0LL; return 0; break; case 1: *out = (int64)(LIMBS(z)[0]); return 0; break; case -1: *out = -(int64)(LIMBS(z)[0]); return 0; break; case 2: if (LIMBS(z)[1] < 0x80000000L) { *out = (int64)(LIMBS(z)[1]) << 32 | (int64)(LIMBS(z)[0]); return 0; } break; case -2: if (LIMBS(z)[1] < 0x80000000L) { *out = -((int64)(LIMBS(z)[1]) << 32 | (int64)(LIMBS(z)[0])); return 0; } else if (LIMBS(z)[0] == 0 && LIMBS(z)[1] == 0x80000000L) { *out = -0x8000000000000000LL; return 0; } break; } #endif return -1; } PGMP_PG_FUNCTION(pmpz_to_float4) { const mpz_t z; double out; PGMP_GETARG_MPZ(z, 0); out = mpz_get_d(z); PG_RETURN_FLOAT4((float4)out); } PGMP_PG_FUNCTION(pmpz_to_float8) { const mpz_t z; double out; PGMP_GETARG_MPZ(z, 0); out = mpz_get_d(z); PG_RETURN_FLOAT8((float8)out); } pgmp-rel-1.0.4/src/pmpz_rand.c000066400000000000000000000105421361705073600161760ustar00rootroot00000000000000/* pmpz_rand -- mpz random numbers * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "utils/memutils.h" /* for TopMemoryContext */ /* The state of the random number generator. * * Currently this variable is reset when the library is loaded: this means at * every session but would break if the library starts being preloaded. So, * TODO: check if there is a way to explicitly allocate this structure per * session. */ gmp_randstate_t *pgmp_randstate; /* Clear the random state if set * * This macro should be invoked with the TopMemoryContext set as current * memory context */ #define PGMP_CLEAR_RANDSTATE \ do { \ if (pgmp_randstate) { \ gmp_randclear(*pgmp_randstate); \ pfree(pgmp_randstate); \ pgmp_randstate = NULL; \ } \ } while (0) /* Exit with an error if the random state is not set */ #define PGMP_CHECK_RANDSTATE \ do { \ if (!pgmp_randstate) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("random state not initialized") )); \ } \ } while (0) /* * Random state initialization */ #define PGMP_RANDINIT(f, INIT) \ \ PGMP_PG_FUNCTION(pgmp_ ## f) \ { \ gmp_randstate_t *state; \ MemoryContext oldctx; \ \ /* palloc and init of the global variable should happen */ \ /* in the global memory context. */ \ oldctx = MemoryContextSwitchTo(TopMemoryContext); \ \ state = palloc(sizeof(gmp_randstate_t)); \ INIT(f); \ \ /* set the global variable to the initialized state */ \ PGMP_CLEAR_RANDSTATE; \ pgmp_randstate = state; \ \ MemoryContextSwitchTo(oldctx); \ \ PG_RETURN_NULL(); \ } #define PGMP_RANDINIT_NOARG(f) gmp_ ## f (*state) PGMP_RANDINIT(randinit_default, PGMP_RANDINIT_NOARG) #if __GMP_MP_RELEASE >= 40200 PGMP_RANDINIT(randinit_mt, PGMP_RANDINIT_NOARG) #endif #define PGMP_RANDINIT_ACE(f) \ do { \ const mpz_t a; \ unsigned long c; \ mp_bitcnt_t e; \ \ PGMP_GETARG_MPZ(a, 0); \ PGMP_GETARG_ULONG(c, 1); \ PGMP_GETARG_ULONG(e, 2); \ \ gmp_ ## f (*state, a, c, e); \ } while (0) PGMP_RANDINIT(randinit_lc_2exp, PGMP_RANDINIT_ACE) #define PGMP_RANDINIT_SIZE(f) \ do { \ mp_bitcnt_t size; \ \ PGMP_GETARG_ULONG(size, 0); \ \ if (!gmp_ ## f (*state, size)) { \ ereport(ERROR, ( \ errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("failed to initialized random state with size %lu", \ size) )); \ } \ } while (0) PGMP_RANDINIT(randinit_lc_2exp_size, PGMP_RANDINIT_SIZE) PGMP_PG_FUNCTION(pgmp_randseed) { const mpz_t seed; MemoryContext oldctx; PGMP_CHECK_RANDSTATE; PGMP_GETARG_MPZ(seed, 0); /* Switch to the global memory cx in case gmp_randseed allocates */ oldctx = MemoryContextSwitchTo(TopMemoryContext); gmp_randseed(*pgmp_randstate, seed); MemoryContextSwitchTo(oldctx); PG_RETURN_NULL(); } /* * Random numbers functions */ #define PMPZ_RAND_BITCNT(f) \ \ PGMP_PG_FUNCTION(pmpz_ ## f) \ { \ unsigned long n; \ mpz_t ret; \ \ PGMP_CHECK_RANDSTATE; \ \ PGMP_GETARG_ULONG(n, 0); \ \ mpz_init(ret); \ mpz_ ## f (ret, *pgmp_randstate, n); \ \ PGMP_RETURN_MPZ(ret); \ } PMPZ_RAND_BITCNT(urandomb) PMPZ_RAND_BITCNT(rrandomb) PGMP_PG_FUNCTION(pmpz_urandomm) { const mpz_t n; mpz_t ret; PGMP_CHECK_RANDSTATE; PGMP_GETARG_MPZ(n, 0); mpz_init(ret); mpz_urandomm(ret, *pgmp_randstate, n); PGMP_RETURN_MPZ(ret); } pgmp-rel-1.0.4/src/pmpz_roots.c000066400000000000000000000033631361705073600164230ustar00rootroot00000000000000/* pmpz_roots -- root extraction functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "funcapi.h" #if PG_VERSION_NUM >= 90300 #include /* for heap_form_tuple */ #endif /* Functions with a more generic signature are defined in pmpz.arith.c */ #if __GMP_MP_RELEASE >= 40200 PGMP_PG_FUNCTION(pmpz_rootrem) { const mpz_t z1; mpz_t zroot; mpz_t zrem; unsigned long n; PGMP_GETARG_MPZ(z1, 0); PMPZ_CHECK_NONEG(z1); PGMP_GETARG_ULONG(n, 1); PMPZ_CHECK_LONG_POS(n); mpz_init(zroot); mpz_init(zrem); mpz_rootrem (zroot, zrem, z1, n); PGMP_RETURN_MPZ_MPZ(zroot, zrem); } #endif PGMP_PG_FUNCTION(pmpz_sqrtrem) { const mpz_t z1; mpz_t zroot; mpz_t zrem; PGMP_GETARG_MPZ(z1, 0); mpz_init(zroot); mpz_init(zrem); mpz_sqrtrem(zroot, zrem, z1); PGMP_RETURN_MPZ_MPZ(zroot, zrem); } pgmp-rel-1.0.4/src/pmpz_theor.c000066400000000000000000000063151361705073600163760ustar00rootroot00000000000000/* pmpz_theor -- number theoretic functions * * Copyright (C) 2011-2020 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * https://www.gnu.org/licenses/. */ #include "pmpz.h" #include "pgmp-impl.h" #include "fmgr.h" #include "funcapi.h" #if PG_VERSION_NUM >= 90300 #include /* for heap_form_tuple */ #endif /* Function with a more generic signature are defined in pmpz.arith.c */ PGMP_PG_FUNCTION(pmpz_probab_prime_p) { const mpz_t z1; int reps; PGMP_GETARG_MPZ(z1, 0); reps = PG_GETARG_INT32(1); PG_RETURN_INT32(mpz_probab_prime_p(z1, reps)); } PGMP_PG_FUNCTION(pmpz_nextprime) { const mpz_t z1; mpz_t zf; PGMP_GETARG_MPZ(z1, 0); mpz_init(zf); #if __GMP_MP_RELEASE < 40300 if (UNLIKELY(mpz_sgn(z1) < 0)) { mpz_set_ui(zf, 2); } else #endif { mpz_nextprime(zf, z1); } PGMP_RETURN_MPZ(zf); } PGMP_PG_FUNCTION(pmpz_gcdext) { const mpz_t z1; const mpz_t z2; mpz_t zf; mpz_t zs; mpz_t zt; PGMP_GETARG_MPZ(z1, 0); PGMP_GETARG_MPZ(z2, 1); mpz_init(zf); mpz_init(zs); mpz_init(zt); mpz_gcdext(zf, zs, zt, z1, z2); PGMP_RETURN_MPZ_MPZ_MPZ(zf, zs, zt); } PGMP_PG_FUNCTION(pmpz_invert) { const mpz_t z1; const mpz_t z2; mpz_t zf; int ret; PGMP_GETARG_MPZ(z1, 0); PGMP_GETARG_MPZ(z2, 1); mpz_init(zf); ret = mpz_invert(zf, z1, z2); if (ret != 0) { PGMP_RETURN_MPZ(zf); } else { PG_RETURN_NULL(); } } #define PMPZ_INT32(f) \ \ PGMP_PG_FUNCTION(pmpz_ ## f) \ { \ const mpz_t z1; \ const mpz_t z2; \ \ PGMP_GETARG_MPZ(z1, 0); \ PGMP_GETARG_MPZ(z2, 1); \ \ PG_RETURN_INT32(mpz_ ## f (z1, z2)); \ } PMPZ_INT32(jacobi) PMPZ_INT32(legendre) PMPZ_INT32(kronecker) #define PMPZ_ULONG(f) \ \ PGMP_PG_FUNCTION(pmpz_ ## f) \ { \ unsigned long op; \ mpz_t ret; \ \ PGMP_GETARG_ULONG(op, 0); \ \ mpz_init(ret); \ mpz_ ## f (ret, op); \ \ PGMP_RETURN_MPZ(ret); \ } PMPZ_ULONG(fac_ui) PMPZ_ULONG(fib_ui) PMPZ_ULONG(lucnum_ui) #define PMPZ_ULONG_MPZ2(f) \ \ PGMP_PG_FUNCTION(pmpz_ ## f) \ { \ unsigned long op; \ mpz_t ret1; \ mpz_t ret2; \ \ PGMP_GETARG_ULONG(op, 0); \ \ mpz_init(ret1); \ mpz_init(ret2); \ mpz_ ## f (ret1, ret2, op); \ \ PGMP_RETURN_MPZ_MPZ(ret1, ret2); \ } PMPZ_ULONG_MPZ2(fib2_ui) PMPZ_ULONG_MPZ2(lucnum2_ui) pgmp-rel-1.0.4/test/000077500000000000000000000000001361705073600142265ustar00rootroot00000000000000pgmp-rel-1.0.4/test/expected/000077500000000000000000000000001361705073600160275ustar00rootroot00000000000000pgmp-rel-1.0.4/test/expected/mpq.out000066400000000000000000000363721361705073600173700ustar00rootroot00000000000000-- -- Test mpq datatype -- -- Compact output \t \a -- -- mpq input and output functions -- SELECT '0'::mpq; 0 SELECT '1'::mpq; 1 SELECT '-1'::mpq; -1 SELECT '10'::mpq; 10 SELECT '-10'::mpq; -10 SELECT '4294967295'::mpq; -- limbs boundaries 4294967295 SELECT '4294967296'::mpq; 4294967296 SELECT '-4294967296'::mpq; -4294967296 SELECT '-4294967297'::mpq; -4294967297 SELECT '18446744073709551614'::mpq; 18446744073709551614 SELECT '18446744073709551615'::mpq; 18446744073709551615 SELECT '18446744073709551616'::mpq; 18446744073709551616 SELECT '18446744073709551617'::mpq; 18446744073709551617 SELECT '-18446744073709551615'::mpq; -18446744073709551615 SELECT '-18446744073709551616'::mpq; -18446744073709551616 SELECT '-18446744073709551617'::mpq; -18446744073709551617 SELECT '-18446744073709551618'::mpq; -18446744073709551618 SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; 12345678901234567890123456789012345678901234567890123456789012345678901234567890 SELECT '-12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; -12345678901234567890123456789012345678901234567890123456789012345678901234567890 SELECT '1/4294967295'::mpq; -- limbs boundaries on denom 1/4294967295 SELECT '1/4294967296'::mpq; 1/4294967296 SELECT '-1/4294967296'::mpq; -1/4294967296 SELECT '-1/4294967297'::mpq; -1/4294967297 SELECT '1/18446744073709551614'::mpq; 1/18446744073709551614 SELECT '1/18446744073709551615'::mpq; 1/18446744073709551615 SELECT '1/18446744073709551616'::mpq; 1/18446744073709551616 SELECT '1/18446744073709551617'::mpq; 1/18446744073709551617 SELECT '-1/18446744073709551615'::mpq; -1/18446744073709551615 SELECT '-1/18446744073709551616'::mpq; -1/18446744073709551616 SELECT '-1/18446744073709551617'::mpq; -1/18446744073709551617 SELECT '-1/18446744073709551618'::mpq; -1/18446744073709551618 SELECT '1/12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; 1/12345678901234567890123456789012345678901234567890123456789012345678901234567890 SELECT '-1/12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; -1/12345678901234567890123456789012345678901234567890123456789012345678901234567890 SELECT '1/1'::mpq; 1 SELECT '2/3'::mpq; 2/3 SELECT '640/30'::mpq; 64/3 SELECT '-640/30'::mpq; -64/3 SELECT '18446744073709551616/18446744073709551616'::mpq; 1 SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890/' '88888888888888888888888888888888888888888888888888888888888888888888888888888888'::mpq; 617283945/4444444444 SELECT '1/0'::mpq; ERROR: denominator can't be zero LINE 1: SELECT '1/0'::mpq; ^ SELECT mpq('1/1'); 1 SELECT mpq('2/3'); 2/3 SELECT mpq('640/30'); 64/3 SELECT mpq('-640/30'); -64/3 SELECT mpq('0xEF/100'); 239/100 SELECT mpq('0xEF/0x100'); 239/256 SELECT mpq('10/30', 10); 1/3 SELECT mpq('EF/100', 16); 239/256 SELECT mpq('0xEF/100', 0); 239/100 SELECT mpq('z', 36), mpq('Z', 36); 35|35 SELECT mpq('z', 62), mpq('Z', 62); 61|35 SELECT mpq('1', 1); ERROR: invalid base for mpq input: 1 HINT: base should be between 2 and 62 SELECT mpq('1', -10); ERROR: invalid base for mpq input: -10 HINT: base should be between 2 and 62 SELECT mpq('1', 63); ERROR: invalid base for mpq input: 63 HINT: base should be between 2 and 62 SELECT text('239'::mpq); 239 SELECT text('-239'::mpq); -239 SELECT text('239/256'::mpq); 239/256 SELECT text('239'::mpq, 16); ef SELECT text('239/256'::mpq, 10); 239/256 SELECT text('239/256'::mpq, 16); ef/100 SELECT text('239/256'::mpq, 0); ERROR: invalid base for mpq output: 0 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text('239/256'::mpq, 1); ERROR: invalid base for mpq output: 1 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text('239/256'::mpq, 2); 11101111/100000000 SELECT text('239/256'::mpq, 36); 6n/74 SELECT text('239/256'::mpq, 62); 3r/48 SELECT text('239/256'::mpq, 63); ERROR: invalid base for mpq output: 63 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text('239/256'::mpq, -1); ERROR: invalid base for mpq output: -1 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text('239/256'::mpq, -2); 11101111/100000000 SELECT text('239/256'::mpq, -36); 6N/74 SELECT text('239/256'::mpq, -37); ERROR: invalid base for mpq output: -37 HINT: base should be between -36 and -2 or between 2 and 62 -- -- mpq cast -- SELECT 0::smallint::mpq, (-32768)::smallint::mpq, 32767::smallint::mpq; 0|-32768|32767 SELECT 0::integer::mpq, (-2147483648)::integer::mpq, 2147483647::integer::mpq; 0|-2147483648|2147483647 SELECT 0::bigint::mpq, (-9223372036854775808)::bigint::mpq, 9223372036854775807::bigint::mpq; 0|-9223372036854775808|9223372036854775807 SELECT 0::numeric::mpq, (-12345678901234567890)::numeric::mpq, 12345678901234567890::numeric::mpq; 0|-12345678901234567890|12345678901234567890 SELECT 0::mpz::mpq, (-12345678901234567890)::mpz::mpq, 12345678901234567890::mpz::mpq; 0|-12345678901234567890|12345678901234567890 SELECT 0.0::float4::mpq, (-12345.25)::float4::mpq, 12345.25::float4::mpq; 0|-49381/4|49381/4 SELECT 0.0::float8::mpq, (-123456789012.25)::float8::mpq, 123456789012.25::float8::mpq; 0|-493827156049/4|493827156049/4 SELECT 0.1::float4::mpq; -- don't know if it's portable 13421773/134217728 SELECT 0.1::float8::mpq; 3602879701896397/36028797018963968 SELECT 0.0::numeric::mpq, (-1234567890.12345)::numeric::mpq, 1234567890.12345::numeric::mpq; 0|-24691357802469/20000|24691357802469/20000 SELECT 0::mpq, 1::mpq, (-1)::mpq; -- automatic casts 0|1|-1 SELECT 1000000::mpq, (-1000000)::mpq; 1000000|-1000000 SELECT 1000000000::mpq, (-1000000000)::mpq; 1000000000|-1000000000 SELECT 1000000000000000::mpq, (-1000000000000000)::mpq; 1000000000000000|-1000000000000000 SELECT 1000000000000000000000000000000::mpq, (-1000000000000000000000000000000)::mpq; 1000000000000000000000000000000|-1000000000000000000000000000000 SELECT 0.0::mpq, (-1234567890.12345)::mpq, 1234567890.12345::mpq; 0|-24691357802469/20000|24691357802469/20000 SELECT 'NaN'::decimal::mpq; ERROR: can't convert numeric value to mpq: "NaN" SELECT -1::mpq; -- these take the unary minus to work -1 SELECT -1000000::mpq; -1000000 SELECT -1000000000::mpq; -1000000000 SELECT -1000000000000000::mpq; -1000000000000000 SELECT -1000000000000000000000000000000::mpq; -1000000000000000000000000000000 SELECT 123.10::mpq::mpz, (-123.10)::mpq::mpz; 123|-123 SELECT 123.90::mpq::mpz, (-123.90)::mpq::mpz; 123|-123 SELECT 123.10::mpq::int2, (-123.10)::mpq::int2; 123|-123 SELECT 123.10::mpq::int4, (-123.10)::mpq::int4; 123|-123 SELECT 123.10::mpq::int8, (-123.10)::mpq::int8; 123|-123 SELECT 32767::mpq::int2; 32767 SELECT 32768::mpq::int2; ERROR: numeric value too big to be converted to int2 data type SELECT (-32768)::mpq::int2; -32768 SELECT (-32769)::mpq::int2; ERROR: numeric value too big to be converted to int2 data type SELECT 2147483647::mpq::int4; 2147483647 SELECT 2147483648::mpq::int4; ERROR: numeric value too big to be converted to int4 data type SELECT (-2147483648)::mpq::int4; -2147483648 SELECT (-2147483649)::mpq::int4; ERROR: numeric value too big to be converted to int4 data type SELECT 9223372036854775807::mpq::int8; 9223372036854775807 SELECT 9223372036854775808::mpq::int8; ERROR: numeric value too big to be converted to int8 data type SELECT (-9223372036854775808)::mpq::int8; -9223372036854775808 SELECT (-9223372036854775809)::mpq::int8; ERROR: numeric value too big to be converted to int8 data type SELECT 123.10::mpq::float4, (-123.10)::mpq::float4; 123.1|-123.1 SELECT 123.10::mpq::float8, (-123.10)::mpq::float8; 123.1|-123.1 SELECT pow(10::mpz,400)::mpq::float4; -- +inf Infinity SELECT (-pow(10::mpz,400))::mpq::float4; -- -inf -Infinity SELECT mpq(1,pow(10::mpz,400))::float4; -- underflow 0 SELECT pow(10::mpz,400)::mpq::float8; Infinity SELECT (-pow(10::mpz,400))::mpq::float8; -Infinity SELECT mpq(1,pow(10::mpz,400))::float8; 0 SELECT 1::mpq::numeric; 1 SELECT 123.456::mpq::numeric; 123.456 SELECT 123.456::mpq::numeric(10); 123 SELECT 123.456::mpq::numeric(10,2); 123.45 SELECT mpq(4,3)::numeric; 1.333333333333333 SELECT mpq(4,3)::numeric(10); 1 SELECT mpq(4,3)::numeric(10,5); 1.33333 SELECT mpq(40000,3)::numeric(10,5); 13333.33333 SELECT mpq(-40000,3)::numeric(10,5); -13333.33333 SELECT mpq(400000,3)::numeric(10,5); ERROR: numeric field overflow DETAIL: A field with precision 10, scale 5 must round to an absolute value less than 10^5. -- function-style casts SELECT mpq('0'::varchar); 0 SELECT mpq('0'::int2); 0 SELECT mpq('0'::int4); 0 SELECT mpq('0'::int8); 0 SELECT mpq('0'::float4); 0 SELECT mpq('0'::float8); 0 SELECT mpq('0'::numeric); 0 SELECT mpq('0'::mpz); 0 SELECT text(0::mpq); 0 SELECT int2(0::mpq); 0 SELECT int4(0::mpq); 0 SELECT int8(0::mpq); 0 SELECT float4(0::mpq); 0 SELECT float8(0::mpq); 0 SELECT mpz('0'::mpq); 0 -- tricky cases of cast to numeric select (x::mpz::mpq / 100)::decimal from generate_series(-2, 2) x; -0.02 -0.01 0 0.01 0.02 select (x::mpz::mpq / 100)::decimal(6,0) from generate_series(-2, 2) x; 0 0 0 0 0 select (x::mpz::mpq / 100)::decimal(6,1) from generate_series(-2, 2) x; 0.0 0.0 0.0 0.0 0.0 select (x::mpz::mpq / 100)::decimal(6,2) from generate_series(-2, 2) x; -0.02 -0.01 0.00 0.01 0.02 SELECT mpq(10, 4), mpq(10, -4); 5/2|-5/2 SELECT mpq(10, 0); ERROR: denominator can't be zero -- fails if mpq(int, int) or similar are availiable SELECT mpq(4000000000000000000,3); 4000000000000000000/3 -- TODO: this shoud work. -- currently not accepting it for ambiguous type promotion problems, -- but this could change in the future if we find how to fix the above problem SELECT mpq(47563485764385764395874365986384, 874539847539845639485769837553465); ERROR: function mpq(numeric, numeric) does not exist LINE 1: SELECT mpq(47563485764385764395874365986384, 874539847539845... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- Enable these checks if the above is solved. -- SELECT mpq(1230::numeric, 123::numeric); -- SELECT mpq(123.45::numeric, 1::numeric); -- SELECT mpq(1::numeric, 123.45::numeric); -- SELECT mpq(123::numeric, 0::numeric); SELECT mpq(47563485764385764395874365986384::mpz, 874539847539845639485769837553465::mpz); 15854495254795254798624788662128/291513282513281879828589945851155 SELECT mpq('10'::mpz, '0'::mpz); ERROR: denominator can't be zero SELECT num('4/5'::mpq); 4 SELECT den('4/5'::mpq); 5 -- -- mpq arithmetic -- SELECT -('0'::mpq), +('0'::mpq), -('1'::mpq), +('1'::mpq), -('-1'::mpq), +('-1'::mpq); 0|0|-1|1|1|-1 SELECT -('1234567890123456/7890'::mpq), +('1234567890123456/7890'::mpq); -205761315020576/1315|205761315020576/1315 SELECT '4/5'::mpq + '6/8'::mpq; 31/20 SELECT '4/5'::mpq - '6/8'::mpq; 1/20 SELECT '4/5'::mpq * '6/8'::mpq; 3/5 SELECT '4/5'::mpq / '6/8'::mpq; 16/15 SELECT '4/5'::mpq / '0'::mpq; ERROR: division by zero SELECT '4/5'::mpq << 4; 64/5 SELECT '4/5'::mpq << -1; ERROR: argument can't be negative SELECT '4/5'::mpq >> 4; 1/20 SELECT '4/5'::mpq >> -1; ERROR: argument can't be negative -- -- mpq unary function -- SELECT abs(mpq(1,3)); 1/3 SELECT abs(mpq(-1,3)); 1/3 SELECT abs(mpq(1,-3)); 1/3 SELECT abs(mpq(-1,-3)); 1/3 SELECT inv(mpq(1,3)); 3 SELECT inv(mpq(-1,3)); -3 SELECT inv(mpq(3,1)); 1/3 SELECT inv(mpq(-3,1)); -1/3 SELECT inv(0::mpq); ERROR: division by zero SELECT limit_den(3.141592653589793, 10); 22/7 SELECT limit_den(3.141592653589793, 100); 311/99 SELECT limit_den(3.141592653589793, 1000000); 3126535/995207 SELECT limit_den(3.141592653589793); 3126535/995207 SELECT limit_den('4321/8765', 10000); 4321/8765 SELECT limit_den(3.141592653589793, 10000); 355/113 SELECT limit_den(-3.141592653589793, 10000); -355/113 SELECT limit_den(3.141592653589793, 113); 355/113 SELECT limit_den(3.141592653589793, 112); 333/106 SELECT limit_den('201/200', 100); 1 SELECT limit_den('201/200', 101); 102/101 SELECT limit_den(0, 10000); 0 -- -- mpq ordering operators -- select 1000::mpq = 999::mpq; f select 1000::mpq = 1000::mpq; t select 1000::mpq = 1001::mpq; f select 1000::mpq <> 999::mpq; t select 1000::mpq <> 1000::mpq; f select 1000::mpq <> 1001::mpq; t select 1000::mpq != 999::mpq; t select 1000::mpq != 1000::mpq; f select 1000::mpq != 1001::mpq; t select 1000::mpq < 999::mpq; f select 1000::mpq < 1000::mpq; f select 1000::mpq < 1001::mpq; t select 1000::mpq <= 999::mpq; f select 1000::mpq <= 1000::mpq; t select 1000::mpq <= 1001::mpq; t select 1000::mpq > 999::mpq; t select 1000::mpq > 1000::mpq; f select 1000::mpq > 1001::mpq; f select 1000::mpq >= 999::mpq; t select 1000::mpq >= 1000::mpq; t select 1000::mpq >= 1001::mpq; f select mpq_cmp(1000::mpq, 999::mpq); 1 select mpq_cmp(1000::mpq, 1000::mpq); 0 select mpq_cmp(1000::mpq, 1001::mpq); -1 -- Can create btree and hash indexes create table test_mpq_idx (q mpq); insert into test_mpq_idx select generate_series(1, 10000); create index test_mpq_btree_idx on test_mpq_idx using btree (q); set client_min_messages = error; create index test_mpq_hash_idx on test_mpq_idx using hash (q); reset client_min_messages; -- Hash is compatible with mpz select mpq_hash(0) = mpz_hash(0); t select mpq_hash(1000) = mpz_hash(1000); t select mpq_hash(-1000) = mpz_hash(-1000); t select mpq_hash('123456789012345678901234567890123456789012345678901234567890') = mpz_hash('123456789012345678901234567890123456789012345678901234567890'); t -- den is used in hash select mpq_hash(2) <> mpq_hash('2/3'); t select mpq_hash('2/3') <> mpq_hash('2/5'); t -- -- mpq aggregation -- CREATE TABLE mpqagg(q mpq); SELECT sum(q) FROM mpqagg; -- NULL sum INSERT INTO mpqagg SELECT mpq(x+1, x) from generate_series(1, 100) x; INSERT INTO mpqagg VALUES (NULL); SELECT sum(q) FROM mpqagg; 293348137198370259818356753784353345674911/2788815009188499086581352357412492142272 SELECT prod(q) FROM mpqagg; 101 SELECT min(q) FROM mpqagg; 101/100 SELECT max(q) FROM mpqagg; 2 -- check correct values when the sortop kicks in CREATE INDEX mpqagg_idx ON mpqagg(q); SELECT min(q) FROM mpqagg; 101/100 SELECT max(q) FROM mpqagg; 2 -- check aggregates work in windows functions too CREATE TABLE test_mpq_win(q mpq); INSERT INTO test_mpq_win SELECT mpq(1::mpz, i::mpz) from generate_series(1,500) i; SELECT DISTINCT den(q) % 5, prod(q) OVER (PARTITION BY den(q) % 5) FROM test_mpq_win ORDER BY 1; 0|1/736214027959609564214534807933509860360590478604140717816562255320550732004257967720125762877551166630104095572009682655334472656250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1|1/4024980661396945255594437948691099096750750303522148939207253140343256389690134608552507750666273153464851186606092569064940061274937877687035835465562596381725444297753023206467590336262178237450463859369896155905624559845376 2|1/20916546636333781039819147945471434631041853197955027503272329909375076603172061212536955170894771081399258164490448681039306210750168807043358712676705447209588907620793116406518489750133917827418353041315415425444640641253376 3|1/78258648528733027168387470491499343638520216905903130597214530733536396165915793335397652314194905058465162496835320770455185720537199021723403297752439421984679965716575544045339560632302893082461947076538277070518358298853376 4|1/251546524835575501784021324366402680054179057461650771690723123931592596438400569072874480196699629786768776600250612669749873513568533129791068662565559263147495268772282292685896436853700130137800525196100073761792376099045376 pgmp-rel-1.0.4/test/expected/mpz.out000066400000000000000000000643771361705073600174070ustar00rootroot00000000000000-- -- Test mpz datatype -- -- Compact output \t \a SELECT gmp_version() > 10000; t -- -- mpz input and output functions -- SELECT '0'::mpz; 0 SELECT '1'::mpz; 1 SELECT '-1'::mpz; -1 SELECT '10'::mpz; 10 SELECT '-10'::mpz; -10 SELECT '000001'::mpz; -- padding zeros 1 SELECT '-000001'::mpz; -1 SELECT '4294967295'::mpz; -- limbs boundaries 4294967295 SELECT '4294967296'::mpz; 4294967296 SELECT '-4294967296'::mpz; -4294967296 SELECT '-4294967297'::mpz; -4294967297 SELECT '18446744073709551614'::mpz; 18446744073709551614 SELECT '18446744073709551615'::mpz; 18446744073709551615 SELECT '18446744073709551616'::mpz; 18446744073709551616 SELECT '18446744073709551617'::mpz; 18446744073709551617 SELECT '-18446744073709551615'::mpz; -18446744073709551615 SELECT '-18446744073709551616'::mpz; -18446744073709551616 SELECT '-18446744073709551617'::mpz; -18446744073709551617 SELECT '-18446744073709551618'::mpz; -18446744073709551618 SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpz; 12345678901234567890123456789012345678901234567890123456789012345678901234567890 -- other bases SELECT '0x10'::mpz, '010'::mpz, '0b10'::mpz; 16|8|2 SELECT mpz('10'), mpz('10', 16), mpz('10', 2); 10|16|2 SELECT mpz('10', 62); 62 SELECT mpz('10', 1); ERROR: invalid base for mpz input: 1 HINT: base should be between 2 and 62 SELECT mpz('10', 63); ERROR: invalid base for mpz input: 63 HINT: base should be between 2 and 62 SELECT mpz('10', 0), mpz('0x10', 0), mpz('010', 0), mpz('0b10', 0); 10|16|8|2 SELECT text(10::mpz); 10 SELECT text(10::mpz, 2); 1010 SELECT text(10::mpz, -2); 1010 SELECT text(255::mpz, 16); ff SELECT text((36 * 36 - 1)::mpz, 36); zz SELECT text((62 * 62 - 1)::mpz, 62); zz SELECT text((36 * 36 - 1)::mpz, -36); ZZ SELECT text(10::mpz, -37); ERROR: invalid base for mpz output: -37 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, -1); ERROR: invalid base for mpz output: -1 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, 0); ERROR: invalid base for mpz output: 0 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, 1); ERROR: invalid base for mpz output: 1 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, 63); ERROR: invalid base for mpz output: 63 HINT: base should be between -36 and -2 or between 2 and 62 -- limited error SELECT ('xx' || repeat('1234567890', 10))::mpz; ERROR: invalid input for mpz: "xx123456789012345678901234567890123456789012345678..." SELECT mpz('xx' || repeat('1234567890', 10), 42); ERROR: invalid input for mpz base 42: "xx123456789012345678901234567890123456789012345678..." -- -- mpz cast -- SELECT 0::smallint::mpz, (-32768)::smallint::mpz, 32767::smallint::mpz; 0|-32768|32767 SELECT 0::integer::mpz, (-2147483648)::integer::mpz, 2147483647::integer::mpz; 0|-2147483648|2147483647 SELECT 0::bigint::mpz, (-9223372036854775808)::bigint::mpz, 9223372036854775807::bigint::mpz; 0|-9223372036854775808|9223372036854775807 SELECT 0::numeric::mpz, (-12345678901234567890)::numeric::mpz, 12345678901234567890::numeric::mpz; 0|-12345678901234567890|12345678901234567890 -- decimal are truncated SELECT 123.10::numeric::mpz, 123.90::numeric::mpz; 123|123 SELECT (-123.10::numeric)::mpz, (-123.90::numeric)::mpz; -123|-123 SELECT 'NaN'::numeric::mpz; ERROR: can't convert numeric value to mpz: "NaN" SELECT 0.0::float4::mpz, 123.15::float4::mpz, 123.95::float4::mpz; 0|123|123 SELECT (1e36::float4)::mpz BETWEEN pow(10::mpz,36) - pow(10::mpz,30) AND pow(10::mpz,36) + pow(10::mpz,30); t SELECT (-1e36::float4)::mpz BETWEEN -pow(10::mpz,36) - pow(10::mpz,30) AND -pow(10::mpz,36) + pow(10::mpz,30); t SELECT 'NaN'::float4::mpz; ERROR: can't convert float value to mpz: "nan" SELECT 'Infinity'::float4::mpz; ERROR: can't convert float value to mpz: "inf" SELECT '-Infinity'::float4::mpz; ERROR: can't convert float value to mpz: "-inf" SELECT 0.0::float8::mpz, 123.15::float8::mpz, 123.95::float8::mpz; 0|123|123 SELECT (1e307::float8)::mpz BETWEEN pow(10::mpz,307) - pow(10::mpz,307-15) AND pow(10::mpz,307) + pow(10::mpz,307-15); t SELECT (-1e307::float8)::mpz BETWEEN -pow(10::mpz,307) - pow(10::mpz,307-15) AND -pow(10::mpz,307) + pow(10::mpz,307-15); t SELECT 'NaN'::float8::mpz; ERROR: can't convert float value to mpz: "nan" SELECT 'Infinity'::float8::mpz; ERROR: can't convert float value to mpz: "inf" SELECT '-Infinity'::float8::mpz; ERROR: can't convert float value to mpz: "-inf" SELECT 0::mpz, 1::mpz, (-1)::mpz; -- automatic casts 0|1|-1 SELECT 1000000::mpz, (-1000000)::mpz; 1000000|-1000000 SELECT 1000000000::mpz, (-1000000000)::mpz; 1000000000|-1000000000 SELECT 1000000000000000::mpz, (-1000000000000000)::mpz; 1000000000000000|-1000000000000000 SELECT 1000000000000000000000000000000::mpz, (-1000000000000000000000000000000)::mpz; 1000000000000000000000000000000|-1000000000000000000000000000000 SELECT -1::mpz; -- these take the unary minus to work -1 SELECT -1000000::mpz; -1000000 SELECT -1000000000::mpz; -1000000000 SELECT -1000000000000000::mpz; -1000000000000000 SELECT -1000000000000000000000000000000::mpz; -1000000000000000000000000000000 SELECT 32767::mpz::int2; 32767 SELECT 32768::mpz::int2; ERROR: numeric value too big to be converted to int2 data type SELECT (-32768)::mpz::int2; -32768 SELECT (-32769)::mpz::int2; ERROR: numeric value too big to be converted to int2 data type SELECT 2147483647::mpz::int4; 2147483647 SELECT 2147483648::mpz::int4; ERROR: numeric value too big to be converted to int4 data type SELECT (-2147483648)::mpz::int4; -2147483648 SELECT (-2147483649)::mpz::int4; ERROR: numeric value too big to be converted to int4 data type SELECT 9223372036854775807::mpz::int8; 9223372036854775807 SELECT 9223372036854775808::mpz::int8; ERROR: numeric value too big to be converted to int8 data type SELECT (-9223372036854775808)::mpz::int8; -9223372036854775808 SELECT (-9223372036854775809)::mpz::int8; ERROR: numeric value too big to be converted to int8 data type SELECT (2147483648)::mpz::int8; 2147483648 SELECT (-2147483648)::mpz::int8; -2147483648 SELECT (65536::mpz)::bigint; 65536 SELECT (65536::mpz*65536::mpz)::bigint; 4294967296 SELECT (65536::mpz*65536::mpz*65536::mpz)::bigint; 281474976710656 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz-1::mpz)::bigint; 9223372036854775807 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz)::bigint; ERROR: numeric value too big to be converted to int8 data type SELECT (-65536::mpz)::bigint; -65536 SELECT (-65536::mpz*65536::mpz)::bigint; -4294967296 SELECT (-65536::mpz*65536::mpz*65536::mpz)::bigint; -281474976710656 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz+1::mpz)::bigint; -9223372036854775807 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz)::bigint; -9223372036854775808 SELECT (65536::mpz)::numeric; 65536 SELECT (65536::mpz*65536::mpz)::numeric; 4294967296 SELECT (65536::mpz*65536::mpz*65536::mpz)::numeric; 281474976710656 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz)::numeric; 18446744073709551616 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz-1::mpz)::numeric; 18446744073709551615 SELECT (-65536::mpz)::numeric; -65536 SELECT (-65536::mpz*65536::mpz)::numeric; -4294967296 SELECT (-65536::mpz*65536::mpz*65536::mpz)::numeric; -281474976710656 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz)::numeric; -18446744073709551616 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz+1::mpz)::numeric; -18446744073709551615 SELECT 0::mpz::float4, 123::mpz::float4, (-123::mpz)::float4; 0|123|-123 SELECT pow(10::mpz, 30)::float4, (-pow(10::mpz, 30))::float4; 1e+30|-1e+30 SELECT pow(10::mpz, 300)::float4, (-pow(10::mpz, 300))::float4; Infinity|-Infinity SELECT 0::mpz::float8, 123::mpz::float8, (-123::mpz)::float8; 0|123|-123 SELECT pow(10::mpz, 307)::float8, (-pow(10::mpz, 307))::float8; 1e+307|-1e+307 SELECT pow(10::mpz, 407)::float8, (-pow(10::mpz, 407))::float8; Infinity|-Infinity -- function-style casts SELECT mpz('0'::varchar); 0 SELECT mpz('0'::int2); 0 SELECT mpz('0'::int4); 0 SELECT mpz('0'::int8); 0 SELECT mpz('0'::float4); 0 SELECT mpz('0'::float8); 0 SELECT mpz('0'::numeric); 0 SELECT text(0::mpz); 0 SELECT int2(0::mpz); 0 SELECT int4(0::mpz); 0 SELECT int8(0::mpz); 0 SELECT float4(0::mpz); 0 SELECT float8(0::mpz); 0 -- -- mpz arithmetic -- SELECT -('0'::mpz), +('0'::mpz), -('1'::mpz), +('1'::mpz); 0|0|-1|1 SELECT -('12345678901234567890'::mpz), +('12345678901234567890'::mpz); -12345678901234567890|12345678901234567890 SELECT abs('-1234567890'::mpz), abs('1234567890'::mpz); 1234567890|1234567890 SELECT sgn(0::mpz), sgn('-1234567890'::mpz), sgn('1234567890'::mpz); 0|-1|1 SELECT even(10::mpz), even(11::mpz); t|f SELECT odd(10::mpz), odd(11::mpz); f|t SELECT '1'::mpz + '2'::mpz; 3 SELECT '2'::mpz + '-4'::mpz; -2 SELECT regexp_matches(( ('1' || repeat('0', 1000))::mpz + ('2' || repeat('0', 1000))::mpz)::text, '^3(0000000000){100}$') IS NOT NULL; t SELECT '3'::mpz - '2'::mpz; 1 SELECT '3'::mpz - '5'::mpz; -2 SELECT regexp_matches(( ('5' || repeat('0', 1000))::mpz - ('2' || repeat('0', 1000))::mpz)::text, '^3(0000000000){100}$') IS NOT NULL; t SELECT '3'::mpz * '2'::mpz; 6 SELECT '3'::mpz * '-5'::mpz; -15 SELECT regexp_matches(( ('2' || repeat('0', 1000))::mpz * ('3' || repeat('0', 1000))::mpz)::text, '^6(00000000000000000000){100}$') IS NOT NULL; t -- PostgreSQL should apply the conventional precedence to operators -- with the same name of the builtin operators. SELECT '2'::mpz + '6'::mpz * '7'::mpz; -- cit. 44 SELECT '7'::mpz / '3'::mpz; 2 SELECT '-7'::mpz / '3'::mpz; -2 SELECT '7'::mpz / '-3'::mpz; -2 SELECT '-7'::mpz / '-3'::mpz; 2 SELECT '7'::mpz % '3'::mpz; 1 SELECT '-7'::mpz % '3'::mpz; -1 SELECT '7'::mpz % '-3'::mpz; 1 SELECT '-7'::mpz % '-3'::mpz; -1 SELECT '7'::mpz +/ '3'::mpz; 3 SELECT '-7'::mpz +/ '3'::mpz; -2 SELECT '7'::mpz +/ '-3'::mpz; -2 SELECT '-7'::mpz +/ '-3'::mpz; 3 SELECT '7'::mpz +% '3'::mpz; -2 SELECT '-7'::mpz +% '3'::mpz; -1 SELECT '7'::mpz +% '-3'::mpz; 1 SELECT '-7'::mpz +% '-3'::mpz; 2 SELECT '7'::mpz -/ '3'::mpz; 2 SELECT '-7'::mpz -/ '3'::mpz; -3 SELECT '7'::mpz -/ '-3'::mpz; -3 SELECT '-7'::mpz -/ '-3'::mpz; 2 SELECT '7'::mpz -% '3'::mpz; 1 SELECT '-7'::mpz -% '3'::mpz; 2 SELECT '7'::mpz -% '-3'::mpz; -2 SELECT '-7'::mpz -% '-3'::mpz; -1 SELECT '7'::mpz / '0'::mpz; ERROR: division by zero SELECT '7'::mpz % '0'::mpz; ERROR: division by zero SELECT '7'::mpz +/ '0'::mpz; ERROR: division by zero SELECT '7'::mpz +% '0'::mpz; ERROR: division by zero SELECT '7'::mpz -/ '0'::mpz; ERROR: division by zero SELECT '7'::mpz -% '0'::mpz; ERROR: division by zero SELECT '21'::mpz /! '7'::mpz; 3 SELECT '10000000000'::mpz << 10; 10240000000000 SELECT '10000000000'::mpz << 0; 10000000000 SELECT '10000000000'::mpz << -1; ERROR: argument can't be negative SELECT '1027'::mpz >> 3; 128 SELECT '-1027'::mpz >> 3; -128 SELECT '1027'::mpz >> -3; ERROR: argument can't be negative SELECT '1027'::mpz %> 3; 3 SELECT '-1027'::mpz %> 3; -3 SELECT '1027'::mpz %> -3; ERROR: argument can't be negative SELECT '1027'::mpz +>> 3; 129 SELECT '-1027'::mpz +>> 3; -128 SELECT '1027'::mpz +>> -3; ERROR: argument can't be negative SELECT '1027'::mpz +%> 3; -5 SELECT '-1027'::mpz +%> 3; -3 SELECT '1027'::mpz +%> -3; ERROR: argument can't be negative SELECT '1027'::mpz ->> 3; 128 SELECT '-1027'::mpz ->> 3; -129 SELECT '1027'::mpz ->> -3; ERROR: argument can't be negative SELECT '1027'::mpz -%> 3; 3 SELECT '-1027'::mpz -%> 3; 5 SELECT '1027'::mpz -%> -3; ERROR: argument can't be negative SELECT q, r from tdiv_qr( 7::mpz, 3::mpz); 2|1 SELECT q, r from tdiv_qr(-7::mpz, 3::mpz); -2|-1 SELECT q, r from tdiv_qr( 7::mpz, -3::mpz); -2|1 SELECT q, r from tdiv_qr(-7::mpz, -3::mpz); 2|-1 SELECT q, r from tdiv_qr( 7::mpz, 0::mpz); ERROR: division by zero SELECT q, r from cdiv_qr( 7::mpz, 3::mpz); 3|-2 SELECT q, r from cdiv_qr(-7::mpz, 3::mpz); -2|-1 SELECT q, r from cdiv_qr( 7::mpz, -3::mpz); -2|1 SELECT q, r from cdiv_qr(-7::mpz, -3::mpz); 3|2 SELECT q, r from cdiv_qr( 7::mpz, 0::mpz); ERROR: division by zero SELECT q, r from fdiv_qr( 7::mpz, 3::mpz); 2|1 SELECT q, r from fdiv_qr(-7::mpz, 3::mpz); -3|2 SELECT q, r from fdiv_qr( 7::mpz, -3::mpz); -3|-2 SELECT q, r from fdiv_qr(-7::mpz, -3::mpz); 2|-1 SELECT q, r from fdiv_qr( 7::mpz, 0::mpz); ERROR: division by zero SELECT divisible(10::mpz, 3::mpz); f SELECT divisible(12::mpz, 3::mpz); t SELECT divisible(10::mpz, 0::mpz); f SELECT divisible(0::mpz, 0::mpz); t SELECT divisible_2exp(63::mpz, 3); f SELECT divisible_2exp(64::mpz, 3); t SELECT 10::mpz /? 3::mpz; f SELECT 12::mpz /? 3::mpz; t SELECT 10::mpz /? 0::mpz; f SELECT 0::mpz /? 0::mpz; t SELECT 63::mpz >>? 3; f SELECT 64::mpz >>? 3; t SELECT congruent(12::mpz, 16::mpz, 5::mpz); f SELECT congruent(12::mpz, 17::mpz, 5::mpz); t SELECT congruent(12::mpz, 11::mpz, 0::mpz); f SELECT congruent(12::mpz, 12::mpz, 0::mpz); t SELECT congruent_2exp(18::mpz, 41::mpz, 3); f SELECT congruent_2exp(18::mpz, 42::mpz, 3); t -- power operator/functions SELECT 2::mpz ^ 10; 1024 SELECT 2::mpz ^ 0; 1 SELECT 2::mpz ^ -1; ERROR: argument can't be negative SELECT pow(2::mpz, 10); 1024 SELECT pow(2::mpz, 0); 1 SELECT pow(2::mpz, -1); ERROR: argument can't be negative SELECT powm(3::mpz, 2::mpz, 9::mpz); 0 SELECT powm(3::mpz, 2::mpz, 8::mpz); 1 SELECT powm(3::mpz, -1::mpz, 8::mpz); ERROR: argument can't be negative SELECT powm(3::mpz, 2::mpz, 0::mpz); ERROR: division by zero -- -- mpz ordering operators -- select 1000::mpz = 999::mpz; f select 1000::mpz = 1000::mpz; t select 1000::mpz = 1001::mpz; f select 1000::mpz <> 999::mpz; t select 1000::mpz <> 1000::mpz; f select 1000::mpz <> 1001::mpz; t select 1000::mpz != 999::mpz; t select 1000::mpz != 1000::mpz; f select 1000::mpz != 1001::mpz; t select 1000::mpz < 999::mpz; f select 1000::mpz < 1000::mpz; f select 1000::mpz < 1001::mpz; t select 1000::mpz <= 999::mpz; f select 1000::mpz <= 1000::mpz; t select 1000::mpz <= 1001::mpz; t select 1000::mpz > 999::mpz; t select 1000::mpz > 1000::mpz; f select 1000::mpz > 1001::mpz; f select 1000::mpz >= 999::mpz; t select 1000::mpz >= 1000::mpz; t select 1000::mpz >= 1001::mpz; f select mpz_cmp(1000::mpz, 999::mpz); 1 select mpz_cmp(1000::mpz, 1000::mpz); 0 select mpz_cmp(1000::mpz, 1001::mpz); -1 -- Can create btree and hash indexes create table test_mpz_idx (z mpz); insert into test_mpz_idx select generate_series(1, 10000); create index test_mpz_btree_idx on test_mpz_idx using btree (z); set client_min_messages = error; create index test_mpz_hash_idx on test_mpz_idx using hash (z); reset client_min_messages; -- Hash is compatible with builtins select mpz_hash(0) = hashint4(0); t select mpz_hash(32767::int2) = hashint2(32767::int2); t select mpz_hash((-32768)::int2) = hashint2((-32768)::int2); t select mpz_hash(2147483647) = hashint4(2147483647); t select mpz_hash(-2147483648) = hashint4(-2147483648); t select mpz_hash(9223372036854775807) = hashint8(9223372036854775807); t select mpz_hash(-9223372036854775808) = hashint8(-9223372036854775808); t -- -- mpz aggregation -- CREATE TABLE mpzagg(z mpz); SELECT sum(z) FROM mpzagg; -- NULL sum INSERT INTO mpzagg SELECT generate_series(1, 100); INSERT INTO mpzagg VALUES (NULL); SELECT sum(z) FROM mpzagg; 5050 SELECT prod(z) FROM mpzagg; 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 SELECT min(z) FROM mpzagg; 1 SELECT max(z) FROM mpzagg; 100 -- check correct values when the sortop kicks in CREATE INDEX mpzagg_idx ON mpzagg(z); SELECT min(z) FROM mpzagg; 1 SELECT max(z) FROM mpzagg; 100 SELECT bit_and(z) FROM mpzagg; 0 SELECT bit_and(z) FROM mpzagg WHERE z % 2 = 1; 1 SELECT bit_or(z) FROM mpzagg; 127 SELECT bit_or(z) FROM mpzagg WHERE z % 2 = 0; 126 SELECT bit_or(z) FROM mpzagg WHERE z = 1 or z = 2; 3 SELECT bit_xor(z) FROM mpzagg; 100 SELECT bit_xor(z) FROM mpzagg WHERE z = 1 or z = 2; 3 SELECT bit_xor(z) FROM mpzagg WHERE z = 1 or z = 2 or z = 3; 0 -- check aggregates work in windows functions too CREATE TABLE test_mpz_win(z mpz); INSERT INTO test_mpz_win SELECT generate_series(1,500); SELECT DISTINCT z % 5, prod(z) OVER (PARTITION BY z % 5) FROM test_mpz_win ORDER BY 1; 0|736214027959609564214534807933509860360590478604140717816562255320550732004257967720125762877551166630104095572009682655334472656250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1|4024980661396945255594437948691099096750750303522148939207253140343256389690134608552507750666273153464851186606092569064940061274937877687035835465562596381725444297753023206467590336262178237450463859369896155905624559845376 2|20916546636333781039819147945471434631041853197955027503272329909375076603172061212536955170894771081399258164490448681039306210750168807043358712676705447209588907620793116406518489750133917827418353041315415425444640641253376 3|78258648528733027168387470491499343638520216905903130597214530733536396165915793335397652314194905058465162496835320770455185720537199021723403297752439421984679965716575544045339560632302893082461947076538277070518358298853376 4|251546524835575501784021324366402680054179057461650771690723123931592596438400569072874480196699629786768776600250612669749873513568533129791068662565559263147495268772282292685896436853700130137800525196100073761792376099045376 -- -- mpz functions tests -- SELECT sqrt(25::mpz); 5 SELECT sqrt(('1' || repeat('0',100))::mpz); 100000000000000000000000000000000000000000000000000 SELECT sqrt(0::mpz); 0 SELECT sqrt(-1::mpz); ERROR: argument can't be negative SELECT root(27::mpz, 3); 3 SELECT root(('1' || repeat('0',100))::mpz, 3); 2154434690031883721759293566519350 SELECT root(0::mpz, 3); 0 SELECT root(27::mpz, 1); 27 SELECT root(27::mpz, 0); ERROR: argument must be positive SELECT root(-27::mpz, 3); ERROR: argument can't be negative SELECT root(27::mpz, -1); ERROR: argument can't be negative select * from rootrem(1000::mpz,2) as rootrem; 31|39 select * from rootrem(1000::mpz,9) as rootrem; 2|488 select * from rootrem(('1' || repeat('0',100))::mpz,2); 100000000000000000000000000000000000000000000000000|0 select * from rootrem(('1' || repeat('0',100))::mpz,5); 100000000000000000000|0 select root from rootrem(1000::mpz, 2); 31 select rem from rootrem(1000::mpz, 2); 39 select * from sqrtrem(1000::mpz) as rootrem; 31|39 select * from sqrtrem(('1' || repeat('0',100))::mpz); 100000000000000000000000000000000000000000000000000|0 select root from sqrtrem(1000::mpz); 31 select rem from sqrtrem(1000::mpz); 39 select perfect_power(26::mpz); f select perfect_power(27::mpz); t select perfect_power(65535::mpz); f select perfect_power(65536::mpz); t select perfect_power(-65536::mpz); f select perfect_power(-65535::mpz); f select perfect_power(('1' || repeat('0',100))::mpz); t select perfect_power(('1' || repeat('0',10000))::mpz); t select perfect_power(('1' || repeat('0',10001))::mpz); t select perfect_power(('1' || repeat('0',10000))::mpz+1::mpz); f select perfect_square(0::mpz); t select perfect_square(1::mpz); t select perfect_square(-1::mpz); f select perfect_square(26::mpz); f select perfect_square(27::mpz); f select perfect_square(16777215::mpz); f select perfect_square(16777216::mpz); t select perfect_square(('1' || repeat('0',10000))::mpz); t select perfect_square(('1' || repeat('0',10000))::mpz+1::mpz); f -- -- Number Theoretic Functions -- SELECT probab_prime(5::mpz, 2); 2 SELECT probab_prime(10::mpz, 2); 0 SELECT probab_prime(17::mpz, 2); 2 SELECT nextprime(5::mpz); 7 SELECT nextprime(10::mpz); 11 SELECT nextprime(100::mpz); 101 SELECT nextprime(1000::mpz); 1009 SELECT nextprime(0::mpz); 2 SELECT nextprime(-8::mpz); 2 SELECT gcd(3::mpz, 15::mpz); 3 SELECT gcd(17::mpz, 15::mpz); 1 SELECT gcd(12345::mpz, 54321::mpz); 3 SELECT gcd(10000000::mpz, 10000::mpz); 10000 SELECT g, s, t FROM gcdext(6::mpz, 15::mpz); 3|-2|1 SELECT lcm(3::mpz, 15::mpz); 15 SELECT lcm(17::mpz, 15::mpz); 255 SELECT lcm(12345::mpz, 54321::mpz); 223530915 SELECT invert(1::mpz,2::mpz); 1 SELECT invert(1::mpz,3::mpz); 1 SELECT invert(2::mpz,3::mpz); 2 SELECT invert(20::mpz,3::mpz); 2 SELECT invert(30::mpz,3::mpz); select jacobi(2::mpz, 3::mpz); -1 select jacobi(5::mpz, 3::mpz); -1 select jacobi(5::mpz, 10::mpz); 0 select jacobi(5::mpz, 20::mpz); 0 select jacobi(5::mpz, 200::mpz); 0 select legendre(2::mpz, 3::mpz); -1 select legendre(5::mpz, 3::mpz); -1 select legendre(5::mpz, 10::mpz); 0 select legendre(5::mpz, 20::mpz); 0 select legendre(5::mpz, 200::mpz); 0 select kronecker(2::mpz, 3::mpz); -1 select kronecker(5::mpz, 3::mpz); -1 select kronecker(5::mpz, 10::mpz); 0 select kronecker(5::mpz, 20::mpz); 0 select kronecker(5::mpz, 200::mpz); 0 select remove(40::mpz, 5::mpz); 8 select remove(43::mpz, 5::mpz); 43 select remove(48::mpz, 6::mpz); 8 select remove(48::mpz, 3::mpz); 16 select fac(0); 1 select fac(1); 1 select fac(10); 3628800 select fac(100); 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 select fac(-1); ERROR: argument can't be negative select bin(0::mpz, 0); 1 select bin(7::mpz, 2); 21 select bin(-2::mpz, 1); -2 select bin(2::mpz, -1); ERROR: argument can't be negative select fib(0); 0 select fib(1); 1 select fib(10); 55 select fib(-1); ERROR: argument can't be negative select fn, fnsub1 from fib2(0); 0|1 select fn, fnsub1 from fib2(1); 1|0 select fn, fnsub1 from fib2(2); 1|1 select fn, fnsub1 from fib2(10); 55|34 select fn, fnsub1 from fib2(-1); ERROR: argument can't be negative select lucnum(0); 2 select lucnum(1); 1 select lucnum(10); 123 select lucnum(-1); ERROR: argument can't be negative select ln, lnsub1 from lucnum2(0); 2|-1 select ln, lnsub1 from lucnum2(1); 1|2 select ln, lnsub1 from lucnum2(2); 3|1 select ln, lnsub1 from lucnum2(10); 123|76 select ln, lnsub1 from lucnum2(-1); ERROR: argument can't be negative -- -- Logic and bit fiddling functions and operators -- SELECT text('0b10001'::mpz & '0b01001'::mpz, 2); 1 SELECT text('0b10001'::mpz | '0b01001'::mpz, 2); 11001 SELECT text('0b10001'::mpz # '0b01001'::mpz, 2); 11000 SELECT com(10::mpz); -11 SELECT popcount('0b101010'::mpz); 3 SELECT popcount(0::mpz); 0 SELECT popcount(-1::mpz) = gmp_max_bitcnt(); t SELECT hamdist('0b101010'::mpz, '0b101100'::mpz); 2 SELECT hamdist(0::mpz, -1::mpz) = gmp_max_bitcnt(); t SELECT scan0('0b110110'::mpz, 1); 3 SELECT scan0('0b110110'::mpz, 3); 3 SELECT scan0(-1::mpz, 2) = gmp_max_bitcnt(); t SELECT scan0(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT scan0(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT scan1('0b110110'::mpz, 1); 1 SELECT scan1('0b110110'::mpz, 3); 4 SELECT scan1(1::mpz, 2) = gmp_max_bitcnt(); t SELECT scan1(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT scan1(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT text(setbit('0b1010'::mpz, 0), 2); 1011 SELECT text(setbit('0b1010'::mpz, 1), 2); 1010 SELECT setbit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT setbit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT text(clrbit('0b1010'::mpz, 0), 2); 1010 SELECT text(clrbit('0b1010'::mpz, 1), 2); 1000 SELECT clrbit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT clrbit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT text(combit('0b1010'::mpz, 0), 2); 1011 SELECT text(combit('0b1010'::mpz, 1), 2); 1000 SELECT combit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT combit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT tstbit('0b1010'::mpz, 0); 0 SELECT tstbit('0b1010'::mpz, 1); 1 SELECT tstbit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT tstbit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type -- -- Random numbers -- -- Errors SELECT rrandomb(128); ERROR: random state not initialized SELECT urandomb(128); ERROR: random state not initialized SELECT randseed(123456::mpz); ERROR: random state not initialized -- Correct sequence SELECT randinit(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT urandomb(128); 134920890566235299823285762767211707848 -- Re-initialization SELECT randinit(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT urandomb(128); 134920890566235299823285762767211707848 SELECT randinit_mt(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT urandomb(128); 134920890566235299823285762767211707848 SELECT randinit_lc_2exp(1103515245, 12345, 32); SELECT urandomb(128); 208667253088805722654994525601019085254 SELECT urandomb(128); 261232200431476629202778117829285035860 SELECT randinit_lc_2exp_size(64); SELECT urandomb(128); 249447876620907321381460515833516635592 SELECT urandomb(128); 22936434003400892378378850487313686779 -- A failed initialization leaves the state as it was before SELECT randinit(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT randinit_lc_2exp_size(8192); ERROR: failed to initialized random state with size 8192 SELECT urandomb(128); 134920890566235299823285762767211707848 -- Seeding SELECT randinit(); SELECT randseed(123456::mpz); SELECT urandomb(128); 138901051744608890087912541094105168008 SELECT urandomb(128); 95807093925713229772160210272764553732 SELECT randseed(123456::mpz); SELECT urandomb(128); 138901051744608890087912541094105168008 SELECT urandomb(128); 95807093925713229772160210272764553732 SELECT randinit(); SELECT randseed(123456::mpz); SELECT text(rrandomb(128), 2); 11111111111111111111111111111100000011111111111111111111111000001111111111111111111111111111111110000000000000000000000000000000 SELECT text(rrandomb(128), 2); 11111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000011110000000000000000000000000 SELECT randseed(123456::mpz); SELECT text(rrandomb(128), 2); 11111111111111111111111111111100000011111111111111111111111000001111111111111111111111111111111110000000000000000000000000000000 SELECT text(rrandomb(128), 2); 11111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000011110000000000000000000000000 SELECT randinit(); SELECT randseed(123456::mpz); SELECT urandomm(1000000::mpz); 112776 SELECT urandomm(1000000::mpz); 928797 SELECT randseed(123456::mpz); SELECT urandomm(1000000::mpz); 112776 SELECT urandomm(1000000::mpz); 928797 pgmp-rel-1.0.4/test/expected/mpz_1.out000066400000000000000000000644231361705073600176170ustar00rootroot00000000000000-- -- Test mpz datatype -- -- Compact output \t \a SELECT gmp_version() > 10000; t -- -- mpz input and output functions -- SELECT '0'::mpz; 0 SELECT '1'::mpz; 1 SELECT '-1'::mpz; -1 SELECT '10'::mpz; 10 SELECT '-10'::mpz; -10 SELECT '000001'::mpz; -- padding zeros 1 SELECT '-000001'::mpz; -1 SELECT '4294967295'::mpz; -- limbs boundaries 4294967295 SELECT '4294967296'::mpz; 4294967296 SELECT '-4294967296'::mpz; -4294967296 SELECT '-4294967297'::mpz; -4294967297 SELECT '18446744073709551614'::mpz; 18446744073709551614 SELECT '18446744073709551615'::mpz; 18446744073709551615 SELECT '18446744073709551616'::mpz; 18446744073709551616 SELECT '18446744073709551617'::mpz; 18446744073709551617 SELECT '-18446744073709551615'::mpz; -18446744073709551615 SELECT '-18446744073709551616'::mpz; -18446744073709551616 SELECT '-18446744073709551617'::mpz; -18446744073709551617 SELECT '-18446744073709551618'::mpz; -18446744073709551618 SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpz; 12345678901234567890123456789012345678901234567890123456789012345678901234567890 -- other bases SELECT '0x10'::mpz, '010'::mpz, '0b10'::mpz; 16|8|2 SELECT mpz('10'), mpz('10', 16), mpz('10', 2); 10|16|2 SELECT mpz('10', 62); 62 SELECT mpz('10', 1); ERROR: invalid base for mpz input: 1 HINT: base should be between 2 and 62 SELECT mpz('10', 63); ERROR: invalid base for mpz input: 63 HINT: base should be between 2 and 62 SELECT mpz('10', 0), mpz('0x10', 0), mpz('010', 0), mpz('0b10', 0); 10|16|8|2 SELECT text(10::mpz); 10 SELECT text(10::mpz, 2); 1010 SELECT text(10::mpz, -2); 1010 SELECT text(255::mpz, 16); ff SELECT text((36 * 36 - 1)::mpz, 36); zz SELECT text((62 * 62 - 1)::mpz, 62); zz SELECT text((36 * 36 - 1)::mpz, -36); ZZ SELECT text(10::mpz, -37); ERROR: invalid base for mpz output: -37 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, -1); ERROR: invalid base for mpz output: -1 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, 0); ERROR: invalid base for mpz output: 0 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, 1); ERROR: invalid base for mpz output: 1 HINT: base should be between -36 and -2 or between 2 and 62 SELECT text(10::mpz, 63); ERROR: invalid base for mpz output: 63 HINT: base should be between -36 and -2 or between 2 and 62 -- limited error SELECT ('xx' || repeat('1234567890', 10))::mpz; ERROR: invalid input for mpz: "xx123456789012345678901234567890123456789012345678..." SELECT mpz('xx' || repeat('1234567890', 10), 42); ERROR: invalid input for mpz base 42: "xx123456789012345678901234567890123456789012345678..." -- -- mpz cast -- SELECT 0::smallint::mpz, (-32768)::smallint::mpz, 32767::smallint::mpz; 0|-32768|32767 SELECT 0::integer::mpz, (-2147483648)::integer::mpz, 2147483647::integer::mpz; 0|-2147483648|2147483647 SELECT 0::bigint::mpz, (-9223372036854775808)::bigint::mpz, 9223372036854775807::bigint::mpz; 0|-9223372036854775808|9223372036854775807 SELECT 0::numeric::mpz, (-12345678901234567890)::numeric::mpz, 12345678901234567890::numeric::mpz; 0|-12345678901234567890|12345678901234567890 -- decimal are truncated SELECT 123.10::numeric::mpz, 123.90::numeric::mpz; 123|123 SELECT (-123.10::numeric)::mpz, (-123.90::numeric)::mpz; -123|-123 SELECT 'NaN'::numeric::mpz; ERROR: can't convert numeric value to mpz: "NaN" SELECT 0.0::float4::mpz, 123.15::float4::mpz, 123.95::float4::mpz; 0|123|123 SELECT (1e36::float4)::mpz BETWEEN pow(10::mpz,36) - pow(10::mpz,30) AND pow(10::mpz,36) + pow(10::mpz,30); t SELECT (-1e36::float4)::mpz BETWEEN -pow(10::mpz,36) - pow(10::mpz,30) AND -pow(10::mpz,36) + pow(10::mpz,30); t SELECT 'NaN'::float4::mpz; ERROR: can't convert float value to mpz: "NaN" SELECT 'Infinity'::float4::mpz; ERROR: can't convert float value to mpz: "Infinity" SELECT '-Infinity'::float4::mpz; ERROR: can't convert float value to mpz: "-Infinity" SELECT 0.0::float8::mpz, 123.15::float8::mpz, 123.95::float8::mpz; 0|123|123 SELECT (1e307::float8)::mpz BETWEEN pow(10::mpz,307) - pow(10::mpz,307-15) AND pow(10::mpz,307) + pow(10::mpz,307-15); t SELECT (-1e307::float8)::mpz BETWEEN -pow(10::mpz,307) - pow(10::mpz,307-15) AND -pow(10::mpz,307) + pow(10::mpz,307-15); t SELECT 'NaN'::float8::mpz; ERROR: can't convert float value to mpz: "NaN" SELECT 'Infinity'::float8::mpz; ERROR: can't convert float value to mpz: "Infinity" SELECT '-Infinity'::float8::mpz; ERROR: can't convert float value to mpz: "-Infinity" SELECT 0::mpz, 1::mpz, (-1)::mpz; -- automatic casts 0|1|-1 SELECT 1000000::mpz, (-1000000)::mpz; 1000000|-1000000 SELECT 1000000000::mpz, (-1000000000)::mpz; 1000000000|-1000000000 SELECT 1000000000000000::mpz, (-1000000000000000)::mpz; 1000000000000000|-1000000000000000 SELECT 1000000000000000000000000000000::mpz, (-1000000000000000000000000000000)::mpz; 1000000000000000000000000000000|-1000000000000000000000000000000 SELECT -1::mpz; -- these take the unary minus to work -1 SELECT -1000000::mpz; -1000000 SELECT -1000000000::mpz; -1000000000 SELECT -1000000000000000::mpz; -1000000000000000 SELECT -1000000000000000000000000000000::mpz; -1000000000000000000000000000000 SELECT 32767::mpz::int2; 32767 SELECT 32768::mpz::int2; ERROR: numeric value too big to be converted to int2 data type SELECT (-32768)::mpz::int2; -32768 SELECT (-32769)::mpz::int2; ERROR: numeric value too big to be converted to int2 data type SELECT 2147483647::mpz::int4; 2147483647 SELECT 2147483648::mpz::int4; ERROR: numeric value too big to be converted to int4 data type SELECT (-2147483648)::mpz::int4; -2147483648 SELECT (-2147483649)::mpz::int4; ERROR: numeric value too big to be converted to int4 data type SELECT 9223372036854775807::mpz::int8; 9223372036854775807 SELECT 9223372036854775808::mpz::int8; ERROR: numeric value too big to be converted to int8 data type SELECT (-9223372036854775808)::mpz::int8; -9223372036854775808 SELECT (-9223372036854775809)::mpz::int8; ERROR: numeric value too big to be converted to int8 data type SELECT (2147483648)::mpz::int8; 2147483648 SELECT (-2147483648)::mpz::int8; -2147483648 SELECT (65536::mpz)::bigint; 65536 SELECT (65536::mpz*65536::mpz)::bigint; 4294967296 SELECT (65536::mpz*65536::mpz*65536::mpz)::bigint; 281474976710656 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz-1::mpz)::bigint; 9223372036854775807 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz)::bigint; ERROR: numeric value too big to be converted to int8 data type SELECT (-65536::mpz)::bigint; -65536 SELECT (-65536::mpz*65536::mpz)::bigint; -4294967296 SELECT (-65536::mpz*65536::mpz*65536::mpz)::bigint; -281474976710656 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz+1::mpz)::bigint; -9223372036854775807 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz)::bigint; -9223372036854775808 SELECT (65536::mpz)::numeric; 65536 SELECT (65536::mpz*65536::mpz)::numeric; 4294967296 SELECT (65536::mpz*65536::mpz*65536::mpz)::numeric; 281474976710656 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz)::numeric; 18446744073709551616 SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz-1::mpz)::numeric; 18446744073709551615 SELECT (-65536::mpz)::numeric; -65536 SELECT (-65536::mpz*65536::mpz)::numeric; -4294967296 SELECT (-65536::mpz*65536::mpz*65536::mpz)::numeric; -281474976710656 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz)::numeric; -18446744073709551616 SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz+1::mpz)::numeric; -18446744073709551615 SELECT 0::mpz::float4, 123::mpz::float4, (-123::mpz)::float4; 0|123|-123 SELECT pow(10::mpz, 30)::float4, (-pow(10::mpz, 30))::float4; 1e+30|-1e+30 SELECT pow(10::mpz, 300)::float4, (-pow(10::mpz, 300))::float4; Infinity|-Infinity SELECT 0::mpz::float8, 123::mpz::float8, (-123::mpz)::float8; 0|123|-123 SELECT pow(10::mpz, 307)::float8, (-pow(10::mpz, 307))::float8; 1e+307|-1e+307 SELECT pow(10::mpz, 407)::float8, (-pow(10::mpz, 407))::float8; Infinity|-Infinity -- function-style casts SELECT mpz('0'::varchar); 0 SELECT mpz('0'::int2); 0 SELECT mpz('0'::int4); 0 SELECT mpz('0'::int8); 0 SELECT mpz('0'::float4); 0 SELECT mpz('0'::float8); 0 SELECT mpz('0'::numeric); 0 SELECT text(0::mpz); 0 SELECT int2(0::mpz); 0 SELECT int4(0::mpz); 0 SELECT int8(0::mpz); 0 SELECT float4(0::mpz); 0 SELECT float8(0::mpz); 0 -- -- mpz arithmetic -- SELECT -('0'::mpz), +('0'::mpz), -('1'::mpz), +('1'::mpz); 0|0|-1|1 SELECT -('12345678901234567890'::mpz), +('12345678901234567890'::mpz); -12345678901234567890|12345678901234567890 SELECT abs('-1234567890'::mpz), abs('1234567890'::mpz); 1234567890|1234567890 SELECT sgn(0::mpz), sgn('-1234567890'::mpz), sgn('1234567890'::mpz); 0|-1|1 SELECT even(10::mpz), even(11::mpz); t|f SELECT odd(10::mpz), odd(11::mpz); f|t SELECT '1'::mpz + '2'::mpz; 3 SELECT '2'::mpz + '-4'::mpz; -2 SELECT regexp_matches(( ('1' || repeat('0', 1000))::mpz + ('2' || repeat('0', 1000))::mpz)::text, '^3(0000000000){100}$') IS NOT NULL; t SELECT '3'::mpz - '2'::mpz; 1 SELECT '3'::mpz - '5'::mpz; -2 SELECT regexp_matches(( ('5' || repeat('0', 1000))::mpz - ('2' || repeat('0', 1000))::mpz)::text, '^3(0000000000){100}$') IS NOT NULL; t SELECT '3'::mpz * '2'::mpz; 6 SELECT '3'::mpz * '-5'::mpz; -15 SELECT regexp_matches(( ('2' || repeat('0', 1000))::mpz * ('3' || repeat('0', 1000))::mpz)::text, '^6(00000000000000000000){100}$') IS NOT NULL; t -- PostgreSQL should apply the conventional precedence to operators -- with the same name of the builtin operators. SELECT '2'::mpz + '6'::mpz * '7'::mpz; -- cit. 44 SELECT '7'::mpz / '3'::mpz; 2 SELECT '-7'::mpz / '3'::mpz; -2 SELECT '7'::mpz / '-3'::mpz; -2 SELECT '-7'::mpz / '-3'::mpz; 2 SELECT '7'::mpz % '3'::mpz; 1 SELECT '-7'::mpz % '3'::mpz; -1 SELECT '7'::mpz % '-3'::mpz; 1 SELECT '-7'::mpz % '-3'::mpz; -1 SELECT '7'::mpz +/ '3'::mpz; 3 SELECT '-7'::mpz +/ '3'::mpz; -2 SELECT '7'::mpz +/ '-3'::mpz; -2 SELECT '-7'::mpz +/ '-3'::mpz; 3 SELECT '7'::mpz +% '3'::mpz; -2 SELECT '-7'::mpz +% '3'::mpz; -1 SELECT '7'::mpz +% '-3'::mpz; 1 SELECT '-7'::mpz +% '-3'::mpz; 2 SELECT '7'::mpz -/ '3'::mpz; 2 SELECT '-7'::mpz -/ '3'::mpz; -3 SELECT '7'::mpz -/ '-3'::mpz; -3 SELECT '-7'::mpz -/ '-3'::mpz; 2 SELECT '7'::mpz -% '3'::mpz; 1 SELECT '-7'::mpz -% '3'::mpz; 2 SELECT '7'::mpz -% '-3'::mpz; -2 SELECT '-7'::mpz -% '-3'::mpz; -1 SELECT '7'::mpz / '0'::mpz; ERROR: division by zero SELECT '7'::mpz % '0'::mpz; ERROR: division by zero SELECT '7'::mpz +/ '0'::mpz; ERROR: division by zero SELECT '7'::mpz +% '0'::mpz; ERROR: division by zero SELECT '7'::mpz -/ '0'::mpz; ERROR: division by zero SELECT '7'::mpz -% '0'::mpz; ERROR: division by zero SELECT '21'::mpz /! '7'::mpz; 3 SELECT '10000000000'::mpz << 10; 10240000000000 SELECT '10000000000'::mpz << 0; 10000000000 SELECT '10000000000'::mpz << -1; ERROR: argument can't be negative SELECT '1027'::mpz >> 3; 128 SELECT '-1027'::mpz >> 3; -128 SELECT '1027'::mpz >> -3; ERROR: argument can't be negative SELECT '1027'::mpz %> 3; 3 SELECT '-1027'::mpz %> 3; -3 SELECT '1027'::mpz %> -3; ERROR: argument can't be negative SELECT '1027'::mpz +>> 3; 129 SELECT '-1027'::mpz +>> 3; -128 SELECT '1027'::mpz +>> -3; ERROR: argument can't be negative SELECT '1027'::mpz +%> 3; -5 SELECT '-1027'::mpz +%> 3; -3 SELECT '1027'::mpz +%> -3; ERROR: argument can't be negative SELECT '1027'::mpz ->> 3; 128 SELECT '-1027'::mpz ->> 3; -129 SELECT '1027'::mpz ->> -3; ERROR: argument can't be negative SELECT '1027'::mpz -%> 3; 3 SELECT '-1027'::mpz -%> 3; 5 SELECT '1027'::mpz -%> -3; ERROR: argument can't be negative SELECT q, r from tdiv_qr( 7::mpz, 3::mpz); 2|1 SELECT q, r from tdiv_qr(-7::mpz, 3::mpz); -2|-1 SELECT q, r from tdiv_qr( 7::mpz, -3::mpz); -2|1 SELECT q, r from tdiv_qr(-7::mpz, -3::mpz); 2|-1 SELECT q, r from tdiv_qr( 7::mpz, 0::mpz); ERROR: division by zero SELECT q, r from cdiv_qr( 7::mpz, 3::mpz); 3|-2 SELECT q, r from cdiv_qr(-7::mpz, 3::mpz); -2|-1 SELECT q, r from cdiv_qr( 7::mpz, -3::mpz); -2|1 SELECT q, r from cdiv_qr(-7::mpz, -3::mpz); 3|2 SELECT q, r from cdiv_qr( 7::mpz, 0::mpz); ERROR: division by zero SELECT q, r from fdiv_qr( 7::mpz, 3::mpz); 2|1 SELECT q, r from fdiv_qr(-7::mpz, 3::mpz); -3|2 SELECT q, r from fdiv_qr( 7::mpz, -3::mpz); -3|-2 SELECT q, r from fdiv_qr(-7::mpz, -3::mpz); 2|-1 SELECT q, r from fdiv_qr( 7::mpz, 0::mpz); ERROR: division by zero SELECT divisible(10::mpz, 3::mpz); f SELECT divisible(12::mpz, 3::mpz); t SELECT divisible(10::mpz, 0::mpz); f SELECT divisible(0::mpz, 0::mpz); t SELECT divisible_2exp(63::mpz, 3); f SELECT divisible_2exp(64::mpz, 3); t SELECT 10::mpz /? 3::mpz; f SELECT 12::mpz /? 3::mpz; t SELECT 10::mpz /? 0::mpz; f SELECT 0::mpz /? 0::mpz; t SELECT 63::mpz >>? 3; f SELECT 64::mpz >>? 3; t SELECT congruent(12::mpz, 16::mpz, 5::mpz); f SELECT congruent(12::mpz, 17::mpz, 5::mpz); t SELECT congruent(12::mpz, 11::mpz, 0::mpz); f SELECT congruent(12::mpz, 12::mpz, 0::mpz); t SELECT congruent_2exp(18::mpz, 41::mpz, 3); f SELECT congruent_2exp(18::mpz, 42::mpz, 3); t -- power operator/functions SELECT 2::mpz ^ 10; 1024 SELECT 2::mpz ^ 0; 1 SELECT 2::mpz ^ -1; ERROR: argument can't be negative SELECT pow(2::mpz, 10); 1024 SELECT pow(2::mpz, 0); 1 SELECT pow(2::mpz, -1); ERROR: argument can't be negative SELECT powm(3::mpz, 2::mpz, 9::mpz); 0 SELECT powm(3::mpz, 2::mpz, 8::mpz); 1 SELECT powm(3::mpz, -1::mpz, 8::mpz); ERROR: argument can't be negative SELECT powm(3::mpz, 2::mpz, 0::mpz); ERROR: division by zero -- -- mpz ordering operators -- select 1000::mpz = 999::mpz; f select 1000::mpz = 1000::mpz; t select 1000::mpz = 1001::mpz; f select 1000::mpz <> 999::mpz; t select 1000::mpz <> 1000::mpz; f select 1000::mpz <> 1001::mpz; t select 1000::mpz != 999::mpz; t select 1000::mpz != 1000::mpz; f select 1000::mpz != 1001::mpz; t select 1000::mpz < 999::mpz; f select 1000::mpz < 1000::mpz; f select 1000::mpz < 1001::mpz; t select 1000::mpz <= 999::mpz; f select 1000::mpz <= 1000::mpz; t select 1000::mpz <= 1001::mpz; t select 1000::mpz > 999::mpz; t select 1000::mpz > 1000::mpz; f select 1000::mpz > 1001::mpz; f select 1000::mpz >= 999::mpz; t select 1000::mpz >= 1000::mpz; t select 1000::mpz >= 1001::mpz; f select mpz_cmp(1000::mpz, 999::mpz); 1 select mpz_cmp(1000::mpz, 1000::mpz); 0 select mpz_cmp(1000::mpz, 1001::mpz); -1 -- Can create btree and hash indexes create table test_mpz_idx (z mpz); insert into test_mpz_idx select generate_series(1, 10000); create index test_mpz_btree_idx on test_mpz_idx using btree (z); set client_min_messages = error; create index test_mpz_hash_idx on test_mpz_idx using hash (z); reset client_min_messages; -- Hash is compatible with builtins select mpz_hash(0) = hashint4(0); t select mpz_hash(32767::int2) = hashint2(32767::int2); t select mpz_hash((-32768)::int2) = hashint2((-32768)::int2); t select mpz_hash(2147483647) = hashint4(2147483647); t select mpz_hash(-2147483648) = hashint4(-2147483648); t select mpz_hash(9223372036854775807) = hashint8(9223372036854775807); t select mpz_hash(-9223372036854775808) = hashint8(-9223372036854775808); t -- -- mpz aggregation -- CREATE TABLE mpzagg(z mpz); SELECT sum(z) FROM mpzagg; -- NULL sum INSERT INTO mpzagg SELECT generate_series(1, 100); INSERT INTO mpzagg VALUES (NULL); SELECT sum(z) FROM mpzagg; 5050 SELECT prod(z) FROM mpzagg; 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 SELECT min(z) FROM mpzagg; 1 SELECT max(z) FROM mpzagg; 100 -- check correct values when the sortop kicks in CREATE INDEX mpzagg_idx ON mpzagg(z); SELECT min(z) FROM mpzagg; 1 SELECT max(z) FROM mpzagg; 100 SELECT bit_and(z) FROM mpzagg; 0 SELECT bit_and(z) FROM mpzagg WHERE z % 2 = 1; 1 SELECT bit_or(z) FROM mpzagg; 127 SELECT bit_or(z) FROM mpzagg WHERE z % 2 = 0; 126 SELECT bit_or(z) FROM mpzagg WHERE z = 1 or z = 2; 3 SELECT bit_xor(z) FROM mpzagg; 100 SELECT bit_xor(z) FROM mpzagg WHERE z = 1 or z = 2; 3 SELECT bit_xor(z) FROM mpzagg WHERE z = 1 or z = 2 or z = 3; 0 -- check aggregates work in windows functions too CREATE TABLE test_mpz_win(z mpz); INSERT INTO test_mpz_win SELECT generate_series(1,500); SELECT DISTINCT z % 5, prod(z) OVER (PARTITION BY z % 5) FROM test_mpz_win ORDER BY 1; 0|736214027959609564214534807933509860360590478604140717816562255320550732004257967720125762877551166630104095572009682655334472656250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1|4024980661396945255594437948691099096750750303522148939207253140343256389690134608552507750666273153464851186606092569064940061274937877687035835465562596381725444297753023206467590336262178237450463859369896155905624559845376 2|20916546636333781039819147945471434631041853197955027503272329909375076603172061212536955170894771081399258164490448681039306210750168807043358712676705447209588907620793116406518489750133917827418353041315415425444640641253376 3|78258648528733027168387470491499343638520216905903130597214530733536396165915793335397652314194905058465162496835320770455185720537199021723403297752439421984679965716575544045339560632302893082461947076538277070518358298853376 4|251546524835575501784021324366402680054179057461650771690723123931592596438400569072874480196699629786768776600250612669749873513568533129791068662565559263147495268772282292685896436853700130137800525196100073761792376099045376 -- -- mpz functions tests -- SELECT sqrt(25::mpz); 5 SELECT sqrt(('1' || repeat('0',100))::mpz); 100000000000000000000000000000000000000000000000000 SELECT sqrt(0::mpz); 0 SELECT sqrt(-1::mpz); ERROR: argument can't be negative SELECT root(27::mpz, 3); 3 SELECT root(('1' || repeat('0',100))::mpz, 3); 2154434690031883721759293566519350 SELECT root(0::mpz, 3); 0 SELECT root(27::mpz, 1); 27 SELECT root(27::mpz, 0); ERROR: argument must be positive SELECT root(-27::mpz, 3); ERROR: argument can't be negative SELECT root(27::mpz, -1); ERROR: argument can't be negative select * from rootrem(1000::mpz,2) as rootrem; 31|39 select * from rootrem(1000::mpz,9) as rootrem; 2|488 select * from rootrem(('1' || repeat('0',100))::mpz,2); 100000000000000000000000000000000000000000000000000|0 select * from rootrem(('1' || repeat('0',100))::mpz,5); 100000000000000000000|0 select root from rootrem(1000::mpz, 2); 31 select rem from rootrem(1000::mpz, 2); 39 select * from sqrtrem(1000::mpz) as rootrem; 31|39 select * from sqrtrem(('1' || repeat('0',100))::mpz); 100000000000000000000000000000000000000000000000000|0 select root from sqrtrem(1000::mpz); 31 select rem from sqrtrem(1000::mpz); 39 select perfect_power(26::mpz); f select perfect_power(27::mpz); t select perfect_power(65535::mpz); f select perfect_power(65536::mpz); t select perfect_power(-65536::mpz); f select perfect_power(-65535::mpz); f select perfect_power(('1' || repeat('0',100))::mpz); t select perfect_power(('1' || repeat('0',10000))::mpz); t select perfect_power(('1' || repeat('0',10001))::mpz); t select perfect_power(('1' || repeat('0',10000))::mpz+1::mpz); f select perfect_square(0::mpz); t select perfect_square(1::mpz); t select perfect_square(-1::mpz); f select perfect_square(26::mpz); f select perfect_square(27::mpz); f select perfect_square(16777215::mpz); f select perfect_square(16777216::mpz); t select perfect_square(('1' || repeat('0',10000))::mpz); t select perfect_square(('1' || repeat('0',10000))::mpz+1::mpz); f -- -- Number Theoretic Functions -- SELECT probab_prime(5::mpz, 2); 2 SELECT probab_prime(10::mpz, 2); 0 SELECT probab_prime(17::mpz, 2); 2 SELECT nextprime(5::mpz); 7 SELECT nextprime(10::mpz); 11 SELECT nextprime(100::mpz); 101 SELECT nextprime(1000::mpz); 1009 SELECT nextprime(0::mpz); 2 SELECT nextprime(-8::mpz); 2 SELECT gcd(3::mpz, 15::mpz); 3 SELECT gcd(17::mpz, 15::mpz); 1 SELECT gcd(12345::mpz, 54321::mpz); 3 SELECT gcd(10000000::mpz, 10000::mpz); 10000 SELECT g, s, t FROM gcdext(6::mpz, 15::mpz); 3|-2|1 SELECT lcm(3::mpz, 15::mpz); 15 SELECT lcm(17::mpz, 15::mpz); 255 SELECT lcm(12345::mpz, 54321::mpz); 223530915 SELECT invert(1::mpz,2::mpz); 1 SELECT invert(1::mpz,3::mpz); 1 SELECT invert(2::mpz,3::mpz); 2 SELECT invert(20::mpz,3::mpz); 2 SELECT invert(30::mpz,3::mpz); select jacobi(2::mpz, 3::mpz); -1 select jacobi(5::mpz, 3::mpz); -1 select jacobi(5::mpz, 10::mpz); 0 select jacobi(5::mpz, 20::mpz); 0 select jacobi(5::mpz, 200::mpz); 0 select legendre(2::mpz, 3::mpz); -1 select legendre(5::mpz, 3::mpz); -1 select legendre(5::mpz, 10::mpz); 0 select legendre(5::mpz, 20::mpz); 0 select legendre(5::mpz, 200::mpz); 0 select kronecker(2::mpz, 3::mpz); -1 select kronecker(5::mpz, 3::mpz); -1 select kronecker(5::mpz, 10::mpz); 0 select kronecker(5::mpz, 20::mpz); 0 select kronecker(5::mpz, 200::mpz); 0 select remove(40::mpz, 5::mpz); 8 select remove(43::mpz, 5::mpz); 43 select remove(48::mpz, 6::mpz); 8 select remove(48::mpz, 3::mpz); 16 select fac(0); 1 select fac(1); 1 select fac(10); 3628800 select fac(100); 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 select fac(-1); ERROR: argument can't be negative select bin(0::mpz, 0); 1 select bin(7::mpz, 2); 21 select bin(-2::mpz, 1); -2 select bin(2::mpz, -1); ERROR: argument can't be negative select fib(0); 0 select fib(1); 1 select fib(10); 55 select fib(-1); ERROR: argument can't be negative select fn, fnsub1 from fib2(0); 0|1 select fn, fnsub1 from fib2(1); 1|0 select fn, fnsub1 from fib2(2); 1|1 select fn, fnsub1 from fib2(10); 55|34 select fn, fnsub1 from fib2(-1); ERROR: argument can't be negative select lucnum(0); 2 select lucnum(1); 1 select lucnum(10); 123 select lucnum(-1); ERROR: argument can't be negative select ln, lnsub1 from lucnum2(0); 2|-1 select ln, lnsub1 from lucnum2(1); 1|2 select ln, lnsub1 from lucnum2(2); 3|1 select ln, lnsub1 from lucnum2(10); 123|76 select ln, lnsub1 from lucnum2(-1); ERROR: argument can't be negative -- -- Logic and bit fiddling functions and operators -- SELECT text('0b10001'::mpz & '0b01001'::mpz, 2); 1 SELECT text('0b10001'::mpz | '0b01001'::mpz, 2); 11001 SELECT text('0b10001'::mpz # '0b01001'::mpz, 2); 11000 SELECT com(10::mpz); -11 SELECT popcount('0b101010'::mpz); 3 SELECT popcount(0::mpz); 0 SELECT popcount(-1::mpz) = gmp_max_bitcnt(); t SELECT hamdist('0b101010'::mpz, '0b101100'::mpz); 2 SELECT hamdist(0::mpz, -1::mpz) = gmp_max_bitcnt(); t SELECT scan0('0b110110'::mpz, 1); 3 SELECT scan0('0b110110'::mpz, 3); 3 SELECT scan0(-1::mpz, 2) = gmp_max_bitcnt(); t SELECT scan0(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT scan0(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT scan1('0b110110'::mpz, 1); 1 SELECT scan1('0b110110'::mpz, 3); 4 SELECT scan1(1::mpz, 2) = gmp_max_bitcnt(); t SELECT scan1(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT scan1(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT text(setbit('0b1010'::mpz, 0), 2); 1011 SELECT text(setbit('0b1010'::mpz, 1), 2); 1010 SELECT setbit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT setbit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT text(clrbit('0b1010'::mpz, 0), 2); 1010 SELECT text(clrbit('0b1010'::mpz, 1), 2); 1000 SELECT clrbit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT clrbit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT text(combit('0b1010'::mpz, 0), 2); 1011 SELECT text(combit('0b1010'::mpz, 1), 2); 1000 SELECT combit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT combit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type SELECT tstbit('0b1010'::mpz, 0); 0 SELECT tstbit('0b1010'::mpz, 1); 1 SELECT tstbit(0::mpz, -1); ERROR: argument doesn't fit into a bitcount type SELECT tstbit(0::mpz, (2^64)::numeric::mpz); ERROR: argument doesn't fit into a bitcount type -- -- Random numbers -- -- Errors SELECT rrandomb(128); ERROR: random state not initialized SELECT urandomb(128); ERROR: random state not initialized SELECT randseed(123456::mpz); ERROR: random state not initialized -- Correct sequence SELECT randinit(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT urandomb(128); 134920890566235299823285762767211707848 -- Re-initialization SELECT randinit(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT urandomb(128); 134920890566235299823285762767211707848 SELECT randinit_mt(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT urandomb(128); 134920890566235299823285762767211707848 SELECT randinit_lc_2exp(1103515245, 12345, 32); SELECT urandomb(128); 208667253088805722654994525601019085254 SELECT urandomb(128); 261232200431476629202778117829285035860 SELECT randinit_lc_2exp_size(64); SELECT urandomb(128); 249447876620907321381460515833516635592 SELECT urandomb(128); 22936434003400892378378850487313686779 -- A failed initialization leaves the state as it was before SELECT randinit(); SELECT urandomb(128); 157222500695008773376422121395749431412 SELECT randinit_lc_2exp_size(8192); ERROR: failed to initialized random state with size 8192 SELECT urandomb(128); 134920890566235299823285762767211707848 -- Seeding SELECT randinit(); SELECT randseed(123456::mpz); SELECT urandomb(128); 138901051744608890087912541094105168008 SELECT urandomb(128); 95807093925713229772160210272764553732 SELECT randseed(123456::mpz); SELECT urandomb(128); 138901051744608890087912541094105168008 SELECT urandomb(128); 95807093925713229772160210272764553732 SELECT randinit(); SELECT randseed(123456::mpz); SELECT text(rrandomb(128), 2); 11111111111111111111111111111100000011111111111111111111111000001111111111111111111111111111111110000000000000000000000000000000 SELECT text(rrandomb(128), 2); 11111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000011110000000000000000000000000 SELECT randseed(123456::mpz); SELECT text(rrandomb(128), 2); 11111111111111111111111111111100000011111111111111111111111000001111111111111111111111111111111110000000000000000000000000000000 SELECT text(rrandomb(128), 2); 11111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000011110000000000000000000000000 SELECT randinit(); SELECT randseed(123456::mpz); SELECT urandomm(1000000::mpz); 112776 SELECT urandomm(1000000::mpz); 928797 SELECT randseed(123456::mpz); SELECT urandomm(1000000::mpz); 112776 SELECT urandomm(1000000::mpz); 928797 pgmp-rel-1.0.4/test/expected/setup-91.out000066400000000000000000000001151361705073600201440ustar00rootroot00000000000000SET client_min_messages = warning; \set ECHO none RESET client_min_messages; pgmp-rel-1.0.4/test/expected/setup-pre91.out000066400000000000000000000001151361705073600206530ustar00rootroot00000000000000SET client_min_messages = warning; \set ECHO none RESET client_min_messages; pgmp-rel-1.0.4/test/sql/000077500000000000000000000000001361705073600150255ustar00rootroot00000000000000pgmp-rel-1.0.4/test/sql/mpq.sql000066400000000000000000000237701361705073600163540ustar00rootroot00000000000000-- -- Test mpq datatype -- -- Compact output \t \a -- -- mpq input and output functions -- SELECT '0'::mpq; SELECT '1'::mpq; SELECT '-1'::mpq; SELECT '10'::mpq; SELECT '-10'::mpq; SELECT '4294967295'::mpq; -- limbs boundaries SELECT '4294967296'::mpq; SELECT '-4294967296'::mpq; SELECT '-4294967297'::mpq; SELECT '18446744073709551614'::mpq; SELECT '18446744073709551615'::mpq; SELECT '18446744073709551616'::mpq; SELECT '18446744073709551617'::mpq; SELECT '-18446744073709551615'::mpq; SELECT '-18446744073709551616'::mpq; SELECT '-18446744073709551617'::mpq; SELECT '-18446744073709551618'::mpq; SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; SELECT '-12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; SELECT '1/4294967295'::mpq; -- limbs boundaries on denom SELECT '1/4294967296'::mpq; SELECT '-1/4294967296'::mpq; SELECT '-1/4294967297'::mpq; SELECT '1/18446744073709551614'::mpq; SELECT '1/18446744073709551615'::mpq; SELECT '1/18446744073709551616'::mpq; SELECT '1/18446744073709551617'::mpq; SELECT '-1/18446744073709551615'::mpq; SELECT '-1/18446744073709551616'::mpq; SELECT '-1/18446744073709551617'::mpq; SELECT '-1/18446744073709551618'::mpq; SELECT '1/12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; SELECT '-1/12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq; SELECT '1/1'::mpq; SELECT '2/3'::mpq; SELECT '640/30'::mpq; SELECT '-640/30'::mpq; SELECT '18446744073709551616/18446744073709551616'::mpq; SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890/' '88888888888888888888888888888888888888888888888888888888888888888888888888888888'::mpq; SELECT '1/0'::mpq; SELECT mpq('1/1'); SELECT mpq('2/3'); SELECT mpq('640/30'); SELECT mpq('-640/30'); SELECT mpq('0xEF/100'); SELECT mpq('0xEF/0x100'); SELECT mpq('10/30', 10); SELECT mpq('EF/100', 16); SELECT mpq('0xEF/100', 0); SELECT mpq('z', 36), mpq('Z', 36); SELECT mpq('z', 62), mpq('Z', 62); SELECT mpq('1', 1); SELECT mpq('1', -10); SELECT mpq('1', 63); SELECT text('239'::mpq); SELECT text('-239'::mpq); SELECT text('239/256'::mpq); SELECT text('239'::mpq, 16); SELECT text('239/256'::mpq, 10); SELECT text('239/256'::mpq, 16); SELECT text('239/256'::mpq, 0); SELECT text('239/256'::mpq, 1); SELECT text('239/256'::mpq, 2); SELECT text('239/256'::mpq, 36); SELECT text('239/256'::mpq, 62); SELECT text('239/256'::mpq, 63); SELECT text('239/256'::mpq, -1); SELECT text('239/256'::mpq, -2); SELECT text('239/256'::mpq, -36); SELECT text('239/256'::mpq, -37); -- -- mpq cast -- SELECT 0::smallint::mpq, (-32768)::smallint::mpq, 32767::smallint::mpq; SELECT 0::integer::mpq, (-2147483648)::integer::mpq, 2147483647::integer::mpq; SELECT 0::bigint::mpq, (-9223372036854775808)::bigint::mpq, 9223372036854775807::bigint::mpq; SELECT 0::numeric::mpq, (-12345678901234567890)::numeric::mpq, 12345678901234567890::numeric::mpq; SELECT 0::mpz::mpq, (-12345678901234567890)::mpz::mpq, 12345678901234567890::mpz::mpq; SELECT 0.0::float4::mpq, (-12345.25)::float4::mpq, 12345.25::float4::mpq; SELECT 0.0::float8::mpq, (-123456789012.25)::float8::mpq, 123456789012.25::float8::mpq; SELECT 0.1::float4::mpq; -- don't know if it's portable SELECT 0.1::float8::mpq; SELECT 0.0::numeric::mpq, (-1234567890.12345)::numeric::mpq, 1234567890.12345::numeric::mpq; SELECT 0::mpq, 1::mpq, (-1)::mpq; -- automatic casts SELECT 1000000::mpq, (-1000000)::mpq; SELECT 1000000000::mpq, (-1000000000)::mpq; SELECT 1000000000000000::mpq, (-1000000000000000)::mpq; SELECT 1000000000000000000000000000000::mpq, (-1000000000000000000000000000000)::mpq; SELECT 0.0::mpq, (-1234567890.12345)::mpq, 1234567890.12345::mpq; SELECT 'NaN'::decimal::mpq; SELECT -1::mpq; -- these take the unary minus to work SELECT -1000000::mpq; SELECT -1000000000::mpq; SELECT -1000000000000000::mpq; SELECT -1000000000000000000000000000000::mpq; SELECT 123.10::mpq::mpz, (-123.10)::mpq::mpz; SELECT 123.90::mpq::mpz, (-123.90)::mpq::mpz; SELECT 123.10::mpq::int2, (-123.10)::mpq::int2; SELECT 123.10::mpq::int4, (-123.10)::mpq::int4; SELECT 123.10::mpq::int8, (-123.10)::mpq::int8; SELECT 32767::mpq::int2; SELECT 32768::mpq::int2; SELECT (-32768)::mpq::int2; SELECT (-32769)::mpq::int2; SELECT 2147483647::mpq::int4; SELECT 2147483648::mpq::int4; SELECT (-2147483648)::mpq::int4; SELECT (-2147483649)::mpq::int4; SELECT 9223372036854775807::mpq::int8; SELECT 9223372036854775808::mpq::int8; SELECT (-9223372036854775808)::mpq::int8; SELECT (-9223372036854775809)::mpq::int8; SELECT 123.10::mpq::float4, (-123.10)::mpq::float4; SELECT 123.10::mpq::float8, (-123.10)::mpq::float8; SELECT pow(10::mpz,400)::mpq::float4; -- +inf SELECT (-pow(10::mpz,400))::mpq::float4; -- -inf SELECT mpq(1,pow(10::mpz,400))::float4; -- underflow SELECT pow(10::mpz,400)::mpq::float8; SELECT (-pow(10::mpz,400))::mpq::float8; SELECT mpq(1,pow(10::mpz,400))::float8; SELECT 1::mpq::numeric; SELECT 123.456::mpq::numeric; SELECT 123.456::mpq::numeric(10); SELECT 123.456::mpq::numeric(10,2); SELECT mpq(4,3)::numeric; SELECT mpq(4,3)::numeric(10); SELECT mpq(4,3)::numeric(10,5); SELECT mpq(40000,3)::numeric(10,5); SELECT mpq(-40000,3)::numeric(10,5); SELECT mpq(400000,3)::numeric(10,5); -- function-style casts SELECT mpq('0'::varchar); SELECT mpq('0'::int2); SELECT mpq('0'::int4); SELECT mpq('0'::int8); SELECT mpq('0'::float4); SELECT mpq('0'::float8); SELECT mpq('0'::numeric); SELECT mpq('0'::mpz); SELECT text(0::mpq); SELECT int2(0::mpq); SELECT int4(0::mpq); SELECT int8(0::mpq); SELECT float4(0::mpq); SELECT float8(0::mpq); SELECT mpz('0'::mpq); -- tricky cases of cast to numeric select (x::mpz::mpq / 100)::decimal from generate_series(-2, 2) x; select (x::mpz::mpq / 100)::decimal(6,0) from generate_series(-2, 2) x; select (x::mpz::mpq / 100)::decimal(6,1) from generate_series(-2, 2) x; select (x::mpz::mpq / 100)::decimal(6,2) from generate_series(-2, 2) x; SELECT mpq(10, 4), mpq(10, -4); SELECT mpq(10, 0); -- fails if mpq(int, int) or similar are availiable SELECT mpq(4000000000000000000,3); -- TODO: this shoud work. -- currently not accepting it for ambiguous type promotion problems, -- but this could change in the future if we find how to fix the above problem SELECT mpq(47563485764385764395874365986384, 874539847539845639485769837553465); -- Enable these checks if the above is solved. -- SELECT mpq(1230::numeric, 123::numeric); -- SELECT mpq(123.45::numeric, 1::numeric); -- SELECT mpq(1::numeric, 123.45::numeric); -- SELECT mpq(123::numeric, 0::numeric); SELECT mpq(47563485764385764395874365986384::mpz, 874539847539845639485769837553465::mpz); SELECT mpq('10'::mpz, '0'::mpz); SELECT num('4/5'::mpq); SELECT den('4/5'::mpq); -- -- mpq arithmetic -- SELECT -('0'::mpq), +('0'::mpq), -('1'::mpq), +('1'::mpq), -('-1'::mpq), +('-1'::mpq); SELECT -('1234567890123456/7890'::mpq), +('1234567890123456/7890'::mpq); SELECT '4/5'::mpq + '6/8'::mpq; SELECT '4/5'::mpq - '6/8'::mpq; SELECT '4/5'::mpq * '6/8'::mpq; SELECT '4/5'::mpq / '6/8'::mpq; SELECT '4/5'::mpq / '0'::mpq; SELECT '4/5'::mpq << 4; SELECT '4/5'::mpq << -1; SELECT '4/5'::mpq >> 4; SELECT '4/5'::mpq >> -1; -- -- mpq unary function -- SELECT abs(mpq(1,3)); SELECT abs(mpq(-1,3)); SELECT abs(mpq(1,-3)); SELECT abs(mpq(-1,-3)); SELECT inv(mpq(1,3)); SELECT inv(mpq(-1,3)); SELECT inv(mpq(3,1)); SELECT inv(mpq(-3,1)); SELECT inv(0::mpq); SELECT limit_den(3.141592653589793, 10); SELECT limit_den(3.141592653589793, 100); SELECT limit_den(3.141592653589793, 1000000); SELECT limit_den(3.141592653589793); SELECT limit_den('4321/8765', 10000); SELECT limit_den(3.141592653589793, 10000); SELECT limit_den(-3.141592653589793, 10000); SELECT limit_den(3.141592653589793, 113); SELECT limit_den(3.141592653589793, 112); SELECT limit_den('201/200', 100); SELECT limit_den('201/200', 101); SELECT limit_den(0, 10000); -- -- mpq ordering operators -- select 1000::mpq = 999::mpq; select 1000::mpq = 1000::mpq; select 1000::mpq = 1001::mpq; select 1000::mpq <> 999::mpq; select 1000::mpq <> 1000::mpq; select 1000::mpq <> 1001::mpq; select 1000::mpq != 999::mpq; select 1000::mpq != 1000::mpq; select 1000::mpq != 1001::mpq; select 1000::mpq < 999::mpq; select 1000::mpq < 1000::mpq; select 1000::mpq < 1001::mpq; select 1000::mpq <= 999::mpq; select 1000::mpq <= 1000::mpq; select 1000::mpq <= 1001::mpq; select 1000::mpq > 999::mpq; select 1000::mpq > 1000::mpq; select 1000::mpq > 1001::mpq; select 1000::mpq >= 999::mpq; select 1000::mpq >= 1000::mpq; select 1000::mpq >= 1001::mpq; select mpq_cmp(1000::mpq, 999::mpq); select mpq_cmp(1000::mpq, 1000::mpq); select mpq_cmp(1000::mpq, 1001::mpq); -- Can create btree and hash indexes create table test_mpq_idx (q mpq); insert into test_mpq_idx select generate_series(1, 10000); create index test_mpq_btree_idx on test_mpq_idx using btree (q); set client_min_messages = error; create index test_mpq_hash_idx on test_mpq_idx using hash (q); reset client_min_messages; -- Hash is compatible with mpz select mpq_hash(0) = mpz_hash(0); select mpq_hash(1000) = mpz_hash(1000); select mpq_hash(-1000) = mpz_hash(-1000); select mpq_hash('123456789012345678901234567890123456789012345678901234567890') = mpz_hash('123456789012345678901234567890123456789012345678901234567890'); -- den is used in hash select mpq_hash(2) <> mpq_hash('2/3'); select mpq_hash('2/3') <> mpq_hash('2/5'); -- -- mpq aggregation -- CREATE TABLE mpqagg(q mpq); SELECT sum(q) FROM mpqagg; -- NULL sum INSERT INTO mpqagg SELECT mpq(x+1, x) from generate_series(1, 100) x; INSERT INTO mpqagg VALUES (NULL); SELECT sum(q) FROM mpqagg; SELECT prod(q) FROM mpqagg; SELECT min(q) FROM mpqagg; SELECT max(q) FROM mpqagg; -- check correct values when the sortop kicks in CREATE INDEX mpqagg_idx ON mpqagg(q); SELECT min(q) FROM mpqagg; SELECT max(q) FROM mpqagg; -- check aggregates work in windows functions too CREATE TABLE test_mpq_win(q mpq); INSERT INTO test_mpq_win SELECT mpq(1::mpz, i::mpz) from generate_series(1,500) i; SELECT DISTINCT den(q) % 5, prod(q) OVER (PARTITION BY den(q) % 5) FROM test_mpq_win ORDER BY 1; pgmp-rel-1.0.4/test/sql/mpz.sql000066400000000000000000000443651361705073600163700ustar00rootroot00000000000000-- -- Test mpz datatype -- -- Compact output \t \a SELECT gmp_version() > 10000; -- -- mpz input and output functions -- SELECT '0'::mpz; SELECT '1'::mpz; SELECT '-1'::mpz; SELECT '10'::mpz; SELECT '-10'::mpz; SELECT '000001'::mpz; -- padding zeros SELECT '-000001'::mpz; SELECT '4294967295'::mpz; -- limbs boundaries SELECT '4294967296'::mpz; SELECT '-4294967296'::mpz; SELECT '-4294967297'::mpz; SELECT '18446744073709551614'::mpz; SELECT '18446744073709551615'::mpz; SELECT '18446744073709551616'::mpz; SELECT '18446744073709551617'::mpz; SELECT '-18446744073709551615'::mpz; SELECT '-18446744073709551616'::mpz; SELECT '-18446744073709551617'::mpz; SELECT '-18446744073709551618'::mpz; SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpz; -- other bases SELECT '0x10'::mpz, '010'::mpz, '0b10'::mpz; SELECT mpz('10'), mpz('10', 16), mpz('10', 2); SELECT mpz('10', 62); SELECT mpz('10', 1); SELECT mpz('10', 63); SELECT mpz('10', 0), mpz('0x10', 0), mpz('010', 0), mpz('0b10', 0); SELECT text(10::mpz); SELECT text(10::mpz, 2); SELECT text(10::mpz, -2); SELECT text(255::mpz, 16); SELECT text((36 * 36 - 1)::mpz, 36); SELECT text((62 * 62 - 1)::mpz, 62); SELECT text((36 * 36 - 1)::mpz, -36); SELECT text(10::mpz, -37); SELECT text(10::mpz, -1); SELECT text(10::mpz, 0); SELECT text(10::mpz, 1); SELECT text(10::mpz, 63); -- limited error SELECT ('xx' || repeat('1234567890', 10))::mpz; SELECT mpz('xx' || repeat('1234567890', 10), 42); -- -- mpz cast -- SELECT 0::smallint::mpz, (-32768)::smallint::mpz, 32767::smallint::mpz; SELECT 0::integer::mpz, (-2147483648)::integer::mpz, 2147483647::integer::mpz; SELECT 0::bigint::mpz, (-9223372036854775808)::bigint::mpz, 9223372036854775807::bigint::mpz; SELECT 0::numeric::mpz, (-12345678901234567890)::numeric::mpz, 12345678901234567890::numeric::mpz; -- decimal are truncated SELECT 123.10::numeric::mpz, 123.90::numeric::mpz; SELECT (-123.10::numeric)::mpz, (-123.90::numeric)::mpz; SELECT 'NaN'::numeric::mpz; SELECT 0.0::float4::mpz, 123.15::float4::mpz, 123.95::float4::mpz; SELECT (1e36::float4)::mpz BETWEEN pow(10::mpz,36) - pow(10::mpz,30) AND pow(10::mpz,36) + pow(10::mpz,30); SELECT (-1e36::float4)::mpz BETWEEN -pow(10::mpz,36) - pow(10::mpz,30) AND -pow(10::mpz,36) + pow(10::mpz,30); SELECT 'NaN'::float4::mpz; SELECT 'Infinity'::float4::mpz; SELECT '-Infinity'::float4::mpz; SELECT 0.0::float8::mpz, 123.15::float8::mpz, 123.95::float8::mpz; SELECT (1e307::float8)::mpz BETWEEN pow(10::mpz,307) - pow(10::mpz,307-15) AND pow(10::mpz,307) + pow(10::mpz,307-15); SELECT (-1e307::float8)::mpz BETWEEN -pow(10::mpz,307) - pow(10::mpz,307-15) AND -pow(10::mpz,307) + pow(10::mpz,307-15); SELECT 'NaN'::float8::mpz; SELECT 'Infinity'::float8::mpz; SELECT '-Infinity'::float8::mpz; SELECT 0::mpz, 1::mpz, (-1)::mpz; -- automatic casts SELECT 1000000::mpz, (-1000000)::mpz; SELECT 1000000000::mpz, (-1000000000)::mpz; SELECT 1000000000000000::mpz, (-1000000000000000)::mpz; SELECT 1000000000000000000000000000000::mpz, (-1000000000000000000000000000000)::mpz; SELECT -1::mpz; -- these take the unary minus to work SELECT -1000000::mpz; SELECT -1000000000::mpz; SELECT -1000000000000000::mpz; SELECT -1000000000000000000000000000000::mpz; SELECT 32767::mpz::int2; SELECT 32768::mpz::int2; SELECT (-32768)::mpz::int2; SELECT (-32769)::mpz::int2; SELECT 2147483647::mpz::int4; SELECT 2147483648::mpz::int4; SELECT (-2147483648)::mpz::int4; SELECT (-2147483649)::mpz::int4; SELECT 9223372036854775807::mpz::int8; SELECT 9223372036854775808::mpz::int8; SELECT (-9223372036854775808)::mpz::int8; SELECT (-9223372036854775809)::mpz::int8; SELECT (2147483648)::mpz::int8; SELECT (-2147483648)::mpz::int8; SELECT (65536::mpz)::bigint; SELECT (65536::mpz*65536::mpz)::bigint; SELECT (65536::mpz*65536::mpz*65536::mpz)::bigint; SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz-1::mpz)::bigint; SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz)::bigint; SELECT (-65536::mpz)::bigint; SELECT (-65536::mpz*65536::mpz)::bigint; SELECT (-65536::mpz*65536::mpz*65536::mpz)::bigint; SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz+1::mpz)::bigint; SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz/2::mpz)::bigint; SELECT (65536::mpz)::numeric; SELECT (65536::mpz*65536::mpz)::numeric; SELECT (65536::mpz*65536::mpz*65536::mpz)::numeric; SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz)::numeric; SELECT (65536::mpz*65536::mpz*65536::mpz*65536::mpz-1::mpz)::numeric; SELECT (-65536::mpz)::numeric; SELECT (-65536::mpz*65536::mpz)::numeric; SELECT (-65536::mpz*65536::mpz*65536::mpz)::numeric; SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz)::numeric; SELECT (-65536::mpz*65536::mpz*65536::mpz*65536::mpz+1::mpz)::numeric; SELECT 0::mpz::float4, 123::mpz::float4, (-123::mpz)::float4; SELECT pow(10::mpz, 30)::float4, (-pow(10::mpz, 30))::float4; SELECT pow(10::mpz, 300)::float4, (-pow(10::mpz, 300))::float4; SELECT 0::mpz::float8, 123::mpz::float8, (-123::mpz)::float8; SELECT pow(10::mpz, 307)::float8, (-pow(10::mpz, 307))::float8; SELECT pow(10::mpz, 407)::float8, (-pow(10::mpz, 407))::float8; -- function-style casts SELECT mpz('0'::varchar); SELECT mpz('0'::int2); SELECT mpz('0'::int4); SELECT mpz('0'::int8); SELECT mpz('0'::float4); SELECT mpz('0'::float8); SELECT mpz('0'::numeric); SELECT text(0::mpz); SELECT int2(0::mpz); SELECT int4(0::mpz); SELECT int8(0::mpz); SELECT float4(0::mpz); SELECT float8(0::mpz); -- -- mpz arithmetic -- SELECT -('0'::mpz), +('0'::mpz), -('1'::mpz), +('1'::mpz); SELECT -('12345678901234567890'::mpz), +('12345678901234567890'::mpz); SELECT abs('-1234567890'::mpz), abs('1234567890'::mpz); SELECT sgn(0::mpz), sgn('-1234567890'::mpz), sgn('1234567890'::mpz); SELECT even(10::mpz), even(11::mpz); SELECT odd(10::mpz), odd(11::mpz); SELECT '1'::mpz + '2'::mpz; SELECT '2'::mpz + '-4'::mpz; SELECT regexp_matches(( ('1' || repeat('0', 1000))::mpz + ('2' || repeat('0', 1000))::mpz)::text, '^3(0000000000){100}$') IS NOT NULL; SELECT '3'::mpz - '2'::mpz; SELECT '3'::mpz - '5'::mpz; SELECT regexp_matches(( ('5' || repeat('0', 1000))::mpz - ('2' || repeat('0', 1000))::mpz)::text, '^3(0000000000){100}$') IS NOT NULL; SELECT '3'::mpz * '2'::mpz; SELECT '3'::mpz * '-5'::mpz; SELECT regexp_matches(( ('2' || repeat('0', 1000))::mpz * ('3' || repeat('0', 1000))::mpz)::text, '^6(00000000000000000000){100}$') IS NOT NULL; -- PostgreSQL should apply the conventional precedence to operators -- with the same name of the builtin operators. SELECT '2'::mpz + '6'::mpz * '7'::mpz; -- cit. SELECT '7'::mpz / '3'::mpz; SELECT '-7'::mpz / '3'::mpz; SELECT '7'::mpz / '-3'::mpz; SELECT '-7'::mpz / '-3'::mpz; SELECT '7'::mpz % '3'::mpz; SELECT '-7'::mpz % '3'::mpz; SELECT '7'::mpz % '-3'::mpz; SELECT '-7'::mpz % '-3'::mpz; SELECT '7'::mpz +/ '3'::mpz; SELECT '-7'::mpz +/ '3'::mpz; SELECT '7'::mpz +/ '-3'::mpz; SELECT '-7'::mpz +/ '-3'::mpz; SELECT '7'::mpz +% '3'::mpz; SELECT '-7'::mpz +% '3'::mpz; SELECT '7'::mpz +% '-3'::mpz; SELECT '-7'::mpz +% '-3'::mpz; SELECT '7'::mpz -/ '3'::mpz; SELECT '-7'::mpz -/ '3'::mpz; SELECT '7'::mpz -/ '-3'::mpz; SELECT '-7'::mpz -/ '-3'::mpz; SELECT '7'::mpz -% '3'::mpz; SELECT '-7'::mpz -% '3'::mpz; SELECT '7'::mpz -% '-3'::mpz; SELECT '-7'::mpz -% '-3'::mpz; SELECT '7'::mpz / '0'::mpz; SELECT '7'::mpz % '0'::mpz; SELECT '7'::mpz +/ '0'::mpz; SELECT '7'::mpz +% '0'::mpz; SELECT '7'::mpz -/ '0'::mpz; SELECT '7'::mpz -% '0'::mpz; SELECT '21'::mpz /! '7'::mpz; SELECT '10000000000'::mpz << 10; SELECT '10000000000'::mpz << 0; SELECT '10000000000'::mpz << -1; SELECT '1027'::mpz >> 3; SELECT '-1027'::mpz >> 3; SELECT '1027'::mpz >> -3; SELECT '1027'::mpz %> 3; SELECT '-1027'::mpz %> 3; SELECT '1027'::mpz %> -3; SELECT '1027'::mpz +>> 3; SELECT '-1027'::mpz +>> 3; SELECT '1027'::mpz +>> -3; SELECT '1027'::mpz +%> 3; SELECT '-1027'::mpz +%> 3; SELECT '1027'::mpz +%> -3; SELECT '1027'::mpz ->> 3; SELECT '-1027'::mpz ->> 3; SELECT '1027'::mpz ->> -3; SELECT '1027'::mpz -%> 3; SELECT '-1027'::mpz -%> 3; SELECT '1027'::mpz -%> -3; SELECT q, r from tdiv_qr( 7::mpz, 3::mpz); SELECT q, r from tdiv_qr(-7::mpz, 3::mpz); SELECT q, r from tdiv_qr( 7::mpz, -3::mpz); SELECT q, r from tdiv_qr(-7::mpz, -3::mpz); SELECT q, r from tdiv_qr( 7::mpz, 0::mpz); SELECT q, r from cdiv_qr( 7::mpz, 3::mpz); SELECT q, r from cdiv_qr(-7::mpz, 3::mpz); SELECT q, r from cdiv_qr( 7::mpz, -3::mpz); SELECT q, r from cdiv_qr(-7::mpz, -3::mpz); SELECT q, r from cdiv_qr( 7::mpz, 0::mpz); SELECT q, r from fdiv_qr( 7::mpz, 3::mpz); SELECT q, r from fdiv_qr(-7::mpz, 3::mpz); SELECT q, r from fdiv_qr( 7::mpz, -3::mpz); SELECT q, r from fdiv_qr(-7::mpz, -3::mpz); SELECT q, r from fdiv_qr( 7::mpz, 0::mpz); SELECT divisible(10::mpz, 3::mpz); SELECT divisible(12::mpz, 3::mpz); SELECT divisible(10::mpz, 0::mpz); SELECT divisible(0::mpz, 0::mpz); SELECT divisible_2exp(63::mpz, 3); SELECT divisible_2exp(64::mpz, 3); SELECT 10::mpz /? 3::mpz; SELECT 12::mpz /? 3::mpz; SELECT 10::mpz /? 0::mpz; SELECT 0::mpz /? 0::mpz; SELECT 63::mpz >>? 3; SELECT 64::mpz >>? 3; SELECT congruent(12::mpz, 16::mpz, 5::mpz); SELECT congruent(12::mpz, 17::mpz, 5::mpz); SELECT congruent(12::mpz, 11::mpz, 0::mpz); SELECT congruent(12::mpz, 12::mpz, 0::mpz); SELECT congruent_2exp(18::mpz, 41::mpz, 3); SELECT congruent_2exp(18::mpz, 42::mpz, 3); -- power operator/functions SELECT 2::mpz ^ 10; SELECT 2::mpz ^ 0; SELECT 2::mpz ^ -1; SELECT pow(2::mpz, 10); SELECT pow(2::mpz, 0); SELECT pow(2::mpz, -1); SELECT powm(3::mpz, 2::mpz, 9::mpz); SELECT powm(3::mpz, 2::mpz, 8::mpz); SELECT powm(3::mpz, -1::mpz, 8::mpz); SELECT powm(3::mpz, 2::mpz, 0::mpz); -- -- mpz ordering operators -- select 1000::mpz = 999::mpz; select 1000::mpz = 1000::mpz; select 1000::mpz = 1001::mpz; select 1000::mpz <> 999::mpz; select 1000::mpz <> 1000::mpz; select 1000::mpz <> 1001::mpz; select 1000::mpz != 999::mpz; select 1000::mpz != 1000::mpz; select 1000::mpz != 1001::mpz; select 1000::mpz < 999::mpz; select 1000::mpz < 1000::mpz; select 1000::mpz < 1001::mpz; select 1000::mpz <= 999::mpz; select 1000::mpz <= 1000::mpz; select 1000::mpz <= 1001::mpz; select 1000::mpz > 999::mpz; select 1000::mpz > 1000::mpz; select 1000::mpz > 1001::mpz; select 1000::mpz >= 999::mpz; select 1000::mpz >= 1000::mpz; select 1000::mpz >= 1001::mpz; select mpz_cmp(1000::mpz, 999::mpz); select mpz_cmp(1000::mpz, 1000::mpz); select mpz_cmp(1000::mpz, 1001::mpz); -- Can create btree and hash indexes create table test_mpz_idx (z mpz); insert into test_mpz_idx select generate_series(1, 10000); create index test_mpz_btree_idx on test_mpz_idx using btree (z); set client_min_messages = error; create index test_mpz_hash_idx on test_mpz_idx using hash (z); reset client_min_messages; -- Hash is compatible with builtins select mpz_hash(0) = hashint4(0); select mpz_hash(32767::int2) = hashint2(32767::int2); select mpz_hash((-32768)::int2) = hashint2((-32768)::int2); select mpz_hash(2147483647) = hashint4(2147483647); select mpz_hash(-2147483648) = hashint4(-2147483648); select mpz_hash(9223372036854775807) = hashint8(9223372036854775807); select mpz_hash(-9223372036854775808) = hashint8(-9223372036854775808); -- -- mpz aggregation -- CREATE TABLE mpzagg(z mpz); SELECT sum(z) FROM mpzagg; -- NULL sum INSERT INTO mpzagg SELECT generate_series(1, 100); INSERT INTO mpzagg VALUES (NULL); SELECT sum(z) FROM mpzagg; SELECT prod(z) FROM mpzagg; SELECT min(z) FROM mpzagg; SELECT max(z) FROM mpzagg; -- check correct values when the sortop kicks in CREATE INDEX mpzagg_idx ON mpzagg(z); SELECT min(z) FROM mpzagg; SELECT max(z) FROM mpzagg; SELECT bit_and(z) FROM mpzagg; SELECT bit_and(z) FROM mpzagg WHERE z % 2 = 1; SELECT bit_or(z) FROM mpzagg; SELECT bit_or(z) FROM mpzagg WHERE z % 2 = 0; SELECT bit_or(z) FROM mpzagg WHERE z = 1 or z = 2; SELECT bit_xor(z) FROM mpzagg; SELECT bit_xor(z) FROM mpzagg WHERE z = 1 or z = 2; SELECT bit_xor(z) FROM mpzagg WHERE z = 1 or z = 2 or z = 3; -- check aggregates work in windows functions too CREATE TABLE test_mpz_win(z mpz); INSERT INTO test_mpz_win SELECT generate_series(1,500); SELECT DISTINCT z % 5, prod(z) OVER (PARTITION BY z % 5) FROM test_mpz_win ORDER BY 1; -- -- mpz functions tests -- SELECT sqrt(25::mpz); SELECT sqrt(('1' || repeat('0',100))::mpz); SELECT sqrt(0::mpz); SELECT sqrt(-1::mpz); SELECT root(27::mpz, 3); SELECT root(('1' || repeat('0',100))::mpz, 3); SELECT root(0::mpz, 3); SELECT root(27::mpz, 1); SELECT root(27::mpz, 0); SELECT root(-27::mpz, 3); SELECT root(27::mpz, -1); select * from rootrem(1000::mpz,2) as rootrem; select * from rootrem(1000::mpz,9) as rootrem; select * from rootrem(('1' || repeat('0',100))::mpz,2); select * from rootrem(('1' || repeat('0',100))::mpz,5); select root from rootrem(1000::mpz, 2); select rem from rootrem(1000::mpz, 2); select * from sqrtrem(1000::mpz) as rootrem; select * from sqrtrem(('1' || repeat('0',100))::mpz); select root from sqrtrem(1000::mpz); select rem from sqrtrem(1000::mpz); select perfect_power(26::mpz); select perfect_power(27::mpz); select perfect_power(65535::mpz); select perfect_power(65536::mpz); select perfect_power(-65536::mpz); select perfect_power(-65535::mpz); select perfect_power(('1' || repeat('0',100))::mpz); select perfect_power(('1' || repeat('0',10000))::mpz); select perfect_power(('1' || repeat('0',10001))::mpz); select perfect_power(('1' || repeat('0',10000))::mpz+1::mpz); select perfect_square(0::mpz); select perfect_square(1::mpz); select perfect_square(-1::mpz); select perfect_square(26::mpz); select perfect_square(27::mpz); select perfect_square(16777215::mpz); select perfect_square(16777216::mpz); select perfect_square(('1' || repeat('0',10000))::mpz); select perfect_square(('1' || repeat('0',10000))::mpz+1::mpz); -- -- Number Theoretic Functions -- SELECT probab_prime(5::mpz, 2); SELECT probab_prime(10::mpz, 2); SELECT probab_prime(17::mpz, 2); SELECT nextprime(5::mpz); SELECT nextprime(10::mpz); SELECT nextprime(100::mpz); SELECT nextprime(1000::mpz); SELECT nextprime(0::mpz); SELECT nextprime(-8::mpz); SELECT gcd(3::mpz, 15::mpz); SELECT gcd(17::mpz, 15::mpz); SELECT gcd(12345::mpz, 54321::mpz); SELECT gcd(10000000::mpz, 10000::mpz); SELECT g, s, t FROM gcdext(6::mpz, 15::mpz); SELECT lcm(3::mpz, 15::mpz); SELECT lcm(17::mpz, 15::mpz); SELECT lcm(12345::mpz, 54321::mpz); SELECT invert(1::mpz,2::mpz); SELECT invert(1::mpz,3::mpz); SELECT invert(2::mpz,3::mpz); SELECT invert(20::mpz,3::mpz); SELECT invert(30::mpz,3::mpz); select jacobi(2::mpz, 3::mpz); select jacobi(5::mpz, 3::mpz); select jacobi(5::mpz, 10::mpz); select jacobi(5::mpz, 20::mpz); select jacobi(5::mpz, 200::mpz); select legendre(2::mpz, 3::mpz); select legendre(5::mpz, 3::mpz); select legendre(5::mpz, 10::mpz); select legendre(5::mpz, 20::mpz); select legendre(5::mpz, 200::mpz); select kronecker(2::mpz, 3::mpz); select kronecker(5::mpz, 3::mpz); select kronecker(5::mpz, 10::mpz); select kronecker(5::mpz, 20::mpz); select kronecker(5::mpz, 200::mpz); select remove(40::mpz, 5::mpz); select remove(43::mpz, 5::mpz); select remove(48::mpz, 6::mpz); select remove(48::mpz, 3::mpz); select fac(0); select fac(1); select fac(10); select fac(100); select fac(-1); select bin(0::mpz, 0); select bin(7::mpz, 2); select bin(-2::mpz, 1); select bin(2::mpz, -1); select fib(0); select fib(1); select fib(10); select fib(-1); select fn, fnsub1 from fib2(0); select fn, fnsub1 from fib2(1); select fn, fnsub1 from fib2(2); select fn, fnsub1 from fib2(10); select fn, fnsub1 from fib2(-1); select lucnum(0); select lucnum(1); select lucnum(10); select lucnum(-1); select ln, lnsub1 from lucnum2(0); select ln, lnsub1 from lucnum2(1); select ln, lnsub1 from lucnum2(2); select ln, lnsub1 from lucnum2(10); select ln, lnsub1 from lucnum2(-1); -- -- Logic and bit fiddling functions and operators -- SELECT text('0b10001'::mpz & '0b01001'::mpz, 2); SELECT text('0b10001'::mpz | '0b01001'::mpz, 2); SELECT text('0b10001'::mpz # '0b01001'::mpz, 2); SELECT com(10::mpz); SELECT popcount('0b101010'::mpz); SELECT popcount(0::mpz); SELECT popcount(-1::mpz) = gmp_max_bitcnt(); SELECT hamdist('0b101010'::mpz, '0b101100'::mpz); SELECT hamdist(0::mpz, -1::mpz) = gmp_max_bitcnt(); SELECT scan0('0b110110'::mpz, 1); SELECT scan0('0b110110'::mpz, 3); SELECT scan0(-1::mpz, 2) = gmp_max_bitcnt(); SELECT scan0(0::mpz, -1); SELECT scan0(0::mpz, (2^64)::numeric::mpz); SELECT scan1('0b110110'::mpz, 1); SELECT scan1('0b110110'::mpz, 3); SELECT scan1(1::mpz, 2) = gmp_max_bitcnt(); SELECT scan1(0::mpz, -1); SELECT scan1(0::mpz, (2^64)::numeric::mpz); SELECT text(setbit('0b1010'::mpz, 0), 2); SELECT text(setbit('0b1010'::mpz, 1), 2); SELECT setbit(0::mpz, -1); SELECT setbit(0::mpz, (2^64)::numeric::mpz); SELECT text(clrbit('0b1010'::mpz, 0), 2); SELECT text(clrbit('0b1010'::mpz, 1), 2); SELECT clrbit(0::mpz, -1); SELECT clrbit(0::mpz, (2^64)::numeric::mpz); SELECT text(combit('0b1010'::mpz, 0), 2); SELECT text(combit('0b1010'::mpz, 1), 2); SELECT combit(0::mpz, -1); SELECT combit(0::mpz, (2^64)::numeric::mpz); SELECT tstbit('0b1010'::mpz, 0); SELECT tstbit('0b1010'::mpz, 1); SELECT tstbit(0::mpz, -1); SELECT tstbit(0::mpz, (2^64)::numeric::mpz); -- -- Random numbers -- -- Errors SELECT rrandomb(128); SELECT urandomb(128); SELECT randseed(123456::mpz); -- Correct sequence SELECT randinit(); SELECT urandomb(128); SELECT urandomb(128); -- Re-initialization SELECT randinit(); SELECT urandomb(128); SELECT urandomb(128); SELECT randinit_mt(); SELECT urandomb(128); SELECT urandomb(128); SELECT randinit_lc_2exp(1103515245, 12345, 32); SELECT urandomb(128); SELECT urandomb(128); SELECT randinit_lc_2exp_size(64); SELECT urandomb(128); SELECT urandomb(128); -- A failed initialization leaves the state as it was before SELECT randinit(); SELECT urandomb(128); SELECT randinit_lc_2exp_size(8192); SELECT urandomb(128); -- Seeding SELECT randinit(); SELECT randseed(123456::mpz); SELECT urandomb(128); SELECT urandomb(128); SELECT randseed(123456::mpz); SELECT urandomb(128); SELECT urandomb(128); SELECT randinit(); SELECT randseed(123456::mpz); SELECT text(rrandomb(128), 2); SELECT text(rrandomb(128), 2); SELECT randseed(123456::mpz); SELECT text(rrandomb(128), 2); SELECT text(rrandomb(128), 2); SELECT randinit(); SELECT randseed(123456::mpz); SELECT urandomm(1000000::mpz); SELECT urandomm(1000000::mpz); SELECT randseed(123456::mpz); SELECT urandomm(1000000::mpz); SELECT urandomm(1000000::mpz); pgmp-rel-1.0.4/test/sql/setup-91.sql000066400000000000000000000004061361705073600171350ustar00rootroot00000000000000SET client_min_messages = warning; \set ECHO none -- The above turn off echoing so that expected file -- does not depend on contents of the setup file. -- Setup the extension on PostgreSQL 9.1 CREATE EXTENSION pgmp; \set ECHO all RESET client_min_messages; pgmp-rel-1.0.4/test/sql/setup-pre91.sql000066400000000000000000000004051361705073600176430ustar00rootroot00000000000000SET client_min_messages = warning; \set ECHO none -- The above turn off echoing so that expected file -- does not depend on contents of the setup file. -- Setup the extension on PostgreSQL before 9.1 \i sql/pgmp.sql \set ECHO all RESET client_min_messages; pgmp-rel-1.0.4/tools/000077500000000000000000000000001361705073600144075ustar00rootroot00000000000000pgmp-rel-1.0.4/tools/sql2extension.py000077500000000000000000000140211361705073600176000ustar00rootroot00000000000000#!/usr/bin/env python """Generate "ALTER EXTENSION" statements to package a list of SQL definitions. The script doesn't try to be a robust parser: it relies on the input file being regular enough. The script is also incomplete, but it complains loudly if it meets elements it doesn't know how to deal with. """ # Copyright (c) 2011-2020, Daniele Varrazzo # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * The name of Daniele Varrazzo may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function import re import sys re_stmt = re.compile( r'CREATE\s+(?:OR\s+REPLACE\s+)?(\w+)\b([^;]+);', re.MULTILINE | re.IGNORECASE) def process_file(f, opt): data = f.read() # Clean up parts we don't care about and that make parsing more complex data = strip_comments(data) data = strip_strings(data) for m in re_stmt.finditer(data): try: f = globals()['process_' + m.group(1).lower()] except: # TODO: all the missing statements raise KeyError("can't process statement 'CREATE %s'" % (m.group(1).upper(),)) f(m.group(2), opt.extname) def process_aggregate(body, extname): # TODO: parse the "old syntax" name = _find_name(body) args = _find_args(body) print("ALTER EXTENSION %s ADD AGGREGATE %s %s;" % (extname, name, args)) def process_cast(body, extname): args = _find_args(body) print("ALTER EXTENSION %s ADD CAST %s;" % (extname, args)) def process_function(body, extname): name = _find_name(body) args = _find_args(body) print("ALTER EXTENSION %s ADD FUNCTION %s %s;" % (extname, name, args)) def process_operator(body, extname): if body.lstrip().lower().startswith('class'): return process_operator_class(body, extname) m = re.match(r'^\s*([^\s\(]+)\s*\(', body) if m is None: raise ValueError("can't find operator:\n%s" % body) op = m.group(1) m = re.search(r'LEFTARG\s*=\s*([^,\)]+)', body, re.IGNORECASE) larg = m and m.group(1).strip() m = re.search(r'RIGHTARG\s*=\s*([^,\)]+)', body, re.IGNORECASE) rarg = m and m.group(1).strip() if not (larg or rarg): raise ValueError("can't find operator arguments:\n%s" % body) print("ALTER EXTENSION %s ADD OPERATOR %s (%s, %s);" % ( extname, op, larg or 'NONE', rarg or 'NONE')) def process_operator_class(body, extname): m = re.match(r'^\s*CLASS\s*(\w+)\b.*?USING\s+(\w+)\b', body, re.IGNORECASE | re.DOTALL) if m is None: raise ValueError("can't parse operator class:\n%s" % body) print("ALTER EXTENSION %s ADD OPERATOR CLASS %s USING %s;" % ( extname, m.group(1), m.group(2))) def process_type(body, extname): name = _find_name(body) print("ALTER EXTENSION %s ADD TYPE %s;" % ( extname, name)) re_name = re.compile(r'^\s*(\w+)\b') def _find_name(body): m = re_name.match(body) if m is None: raise ValueError("can't find name:\n%s" % body) return m.group(1) def _find_args(body): # find the closing brace of the arguments list # count the braces to avoid getting fooled by type modifiers # e.g. varchar(10) count = 0 for i, c in enumerate(body): if c == '(': count += 1 elif c == ')': count -= 1 if count == 0: break else: raise ValueError("failed to parse arguments list:\n%s") astart = body.index('(') aend = i + 1 return ' '.join(body[astart:aend].split()) re_comment_single = re.compile(r'--.*?$', re.MULTILINE) re_comment_multi = re.compile(r'/\*.*?\*/', re.DOTALL) def strip_comments(s): """Remove SQL comments from a string. TODO: doesn't handle nested comments. """ s = re_comment_single.sub("''", s) s = re_comment_multi.sub("''", s) return s re_string_quote = re.compile(r"'(''|[^'])*'") re_string_dollar = re.compile(r'\$([^$]*)\$.*?\$\1\$') def strip_strings(s): """Replace all the SQL literal strings with the empty string.""" s = re_string_quote.sub('', s) s = re_string_dollar.sub('', s) return s def main(): opt = parse_options() print("-- This file was automatically generated") print("-- by the script '%s'" % __file__) print("-- from input files:", ", ".join(opt.filenames)) print() for fn in opt.filenames: f = fn == '-' and sys.stdin or open(fn) process_file(f, opt) def parse_options(): from optparse import OptionParser parser = OptionParser() parser.add_option('--extname') opt, args = parser.parse_args() if not opt.extname: parser.error("extension name must be specified") opt.filenames = args or ['-'] return opt if __name__ == '__main__': main() pgmp-rel-1.0.4/tools/unmix.py000077500000000000000000000056541361705073600161360ustar00rootroot00000000000000#!/usr/bin/env python """Convert a file containing a mix of python code and content into content. Write the input file to stdout. Python code included between blocks ``!! PYON`` and ``!! PYOFF`` is executed and the output emitted. """ # Copyright (c) 2011-2020, Daniele Varrazzo # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * The name of Daniele Varrazzo may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import sys import six def convert(f): mode = 'out' # can be 'out' or 'py' script = [] env = {} while 1: line = f.readline() if not line: break if line.startswith("!#"): # a comment continue if not line.startswith("!!"): # a regular line if mode == 'out': sys.stdout.write(line) elif mode == 'py': script.append(line) else: raise ValueError("unexpected mode: %s" % mode) continue # state change if 'PYON' in line and mode == 'out': del script[:] mode = 'py' elif 'PYOFF' in line and mode == 'py': script.insert(0, 'from __future__ import print_function\n') six.exec_(''.join(script), env) mode = 'out' else: raise ValueError("bad line in mode %s: %s" % (mode, line.rstrip())) if __name__ == '__main__': if len(sys.argv) > 2: print >>sys.stderr, "usage: %s [FILE]" % sys.argv[1] sys.exit(2) f = len(sys.argv) == 2 and open(sys.argv[1]) or sys.stdin convert(f) sys.exit(0)