pax_global_header 0000666 0000000 0000000 00000000064 13617050736 0014522 g ustar 00root root 0000000 0000000 52 comment=1542950d1e924ec14b136a3a8d08c82d7439c551
pgmp-rel-1.0.4/ 0000775 0000000 0000000 00000000000 13617050736 0013247 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/.gitignore 0000664 0000000 0000000 00000000323 13617050736 0015235 0 ustar 00root root 0000000 0000000 data
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.yml 0000664 0000000 0000000 00000002154 13617050736 0015362 0 ustar 00root root 0000000 0000000 # 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/AUTHORS 0000664 0000000 0000000 00000000214 13617050736 0014314 0 ustar 00root root 0000000 0000000 Authors of the PostgreSQL GMP Module
Daniele Varrazzo Main author
Torello Querci Several contributions, lots of patience
pgmp-rel-1.0.4/COPYING 0000664 0000000 0000000 00000016744 13617050736 0014316 0 ustar 00root root 0000000 0000000 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.json 0000664 0000000 0000000 00000002252 13617050736 0014671 0 ustar 00root root 0000000 0000000 {
"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/Makefile 0000664 0000000 0000000 00000006151 13617050736 0014712 0 ustar 00root root 0000000 0000000 # 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.rst 0000664 0000000 0000000 00000001362 13617050736 0014557 0 ustar 00root root 0000000 0000000 Current 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.rst 0000664 0000000 0000000 00000003224 13617050736 0014737 0 ustar 00root root 0000000 0000000 PostgreSQL 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/ 0000775 0000000 0000000 00000000000 13617050736 0014326 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/bench/Arith-1e6.txt 0000664 0000000 0000000 00000001762 13617050736 0016535 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000001251 13617050736 0016772 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000003321 13617050736 0017541 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000001660 13617050736 0020240 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000001661 13617050736 0020243 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000001547 13617050736 0020252 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000001540 13617050736 0017715 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000002024 13617050736 0020446 0 ustar 00root root 0000000 0000000 title: 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.txt 0000664 0000000 0000000 00000002250 13617050736 0017341 0 ustar 00root root 0000000 0000000 title: 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.py 0000775 0000000 0000000 00000032613 13617050736 0016642 0 ustar 00root root 0000000 0000000 #!/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.py 0000775 0000000 0000000 00000002622 13617050736 0017261 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 13617050736 0014177 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/docs/Makefile 0000664 0000000 0000000 00000002706 13617050736 0015644 0 ustar 00root root 0000000 0000000 # 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/ 0000775 0000000 0000000 00000000000 13617050736 0015625 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/docs/_static/pgmp.css 0000664 0000000 0000000 00000001366 13617050736 0017310 0 ustar 00root root 0000000 0000000 @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.py 0000664 0000000 0000000 00000020450 13617050736 0015477 0 ustar 00root root 0000000 0000000 # -*- 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.rst 0000664 0000000 0000000 00000003071 13617050736 0016037 0 ustar 00root root 0000000 0000000 .. _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-gitignore 0000664 0000000 0000000 00000000102 13617050736 0017044 0 ustar 00root root 0000000 0000000 .buildinfo
.doctrees/
_static/jquery-*.js
_static/underscore-*.js
pgmp-rel-1.0.4/docs/index.rst 0000664 0000000 0000000 00000003244 13617050736 0016043 0 ustar 00root root 0000000 0000000 PostgreSQL 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.rst 0000664 0000000 0000000 00000006222 13617050736 0016401 0 ustar 00root root 0000000 0000000 `!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/ 0000775 0000000 0000000 00000000000 13617050736 0014745 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/docs/lib/issue_role.py 0000664 0000000 0000000 00000002401 13617050736 0017465 0 ustar 00root root 0000000 0000000 # -*- 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.rst 0000664 0000000 0000000 00000001001 13617050736 0015654 0 ustar 00root root 0000000 0000000 Miscellaneous 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.rst 0000664 0000000 0000000 00000015045 13617050736 0015533 0 ustar 00root root 0000000 0000000 `!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.rst 0000664 0000000 0000000 00000057132 13617050736 0015547 0 ustar 00root root 0000000 0000000 `!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.rst 0000664 0000000 0000000 00000000066 13617050736 0015707 0 ustar 00root root 0000000 0000000 Release notes
=============
.. include:: ../NEWS.rst
pgmp-rel-1.0.4/docs/performance.rst 0000664 0000000 0000000 00000007011 13617050736 0017231 0 ustar 00root root 0000000 0000000 .. _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.rst 0000664 0000000 0000000 00000011672 13617050736 0015350 0 ustar 00root root 0000000 0000000 [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.txt 0000664 0000000 0000000 00000000065 13617050736 0017464 0 ustar 00root root 0000000 0000000 sphinx>=1.8,<1.9
docutils==0.16
matplotlib>=2.2,<2.3
pgmp-rel-1.0.4/pgmp.control 0000664 0000000 0000000 00000001647 13617050736 0015624 0 ustar 00root root 0000000 0000000 # 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/ 0000775 0000000 0000000 00000000000 13617050736 0014705 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/sandbox/hello/ 0000775 0000000 0000000 00000000000 13617050736 0016010 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/sandbox/hello/Makefile 0000664 0000000 0000000 00000000101 13617050736 0017440 0 ustar 00root root 0000000 0000000 CFLAGS=-g
LDFLAGS=-lgmp
all: hello
clean:
$(RM) hello.o hello
pgmp-rel-1.0.4/sandbox/hello/hello.c 0000664 0000000 0000000 00000000542 13617050736 0017260 0 ustar 00root root 0000000 0000000 /* 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/ 0000775 0000000 0000000 00000000000 13617050736 0015315 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/sandbox/pi/calc_pi.py 0000775 0000000 0000000 00000002407 13617050736 0017267 0 ustar 00root root 0000000 0000000 #!/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.sh 0000775 0000000 0000000 00000002061 13617050736 0017245 0 ustar 00root root 0000000 0000000 #!/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.sql 0000664 0000000 0000000 00000003436 13617050736 0016454 0 ustar 00root root 0000000 0000000 -- 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/ 0000775 0000000 0000000 00000000000 13617050736 0014046 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/sql/pgmp.pysql 0000664 0000000 0000000 00000035326 13617050736 0016114 0 ustar 00root root 0000000 0000000 /* 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.sql 0000664 0000000 0000000 00000002674 13617050736 0017634 0 ustar 00root root 0000000 0000000 /* 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/ 0000775 0000000 0000000 00000000000 13617050736 0014036 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/src/pgmp-impl.h 0000664 0000000 0000000 00000011003 13617050736 0016104 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000006330 13617050736 0015147 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000003670 13617050736 0016373 0 ustar 00root root 0000000 0000000 /* 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.h 0000664 0000000 0000000 00000002262 13617050736 0016374 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000011044 13617050736 0015157 0 ustar 00root root 0000000 0000000 /* 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.h 0000664 0000000 0000000 00000007601 13617050736 0015170 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000005323 13617050736 0016000 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000014715 13617050736 0016356 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000027633 13617050736 0015661 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000006316 13617050736 0015176 0 ustar 00root root 0000000 0000000 /* 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.h 0000664 0000000 0000000 00000011315 13617050736 0015176 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000005432 13617050736 0016012 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000017015 13617050736 0016363 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000006332 13617050736 0016215 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000021122 13617050736 0015655 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000010542 13617050736 0016176 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000003363 13617050736 0016423 0 ustar 00root root 0000000 0000000 /* 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.c 0000664 0000000 0000000 00000006315 13617050736 0016376 0 ustar 00root root 0000000 0000000 /* 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/ 0000775 0000000 0000000 00000000000 13617050736 0014226 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/test/expected/ 0000775 0000000 0000000 00000000000 13617050736 0016027 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/test/expected/mpq.out 0000664 0000000 0000000 00000036372 13617050736 0017370 0 ustar 00root root 0000000 0000000 --
-- 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.out 0000664 0000000 0000000 00000064377 13617050736 0017407 0 ustar 00root root 0000000 0000000 --
-- 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.out 0000664 0000000 0000000 00000064423 13617050736 0017617 0 ustar 00root root 0000000 0000000 --
-- 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.out 0000664 0000000 0000000 00000000115 13617050736 0020144 0 ustar 00root root 0000000 0000000 SET client_min_messages = warning;
\set ECHO none
RESET client_min_messages;
pgmp-rel-1.0.4/test/expected/setup-pre91.out 0000664 0000000 0000000 00000000115 13617050736 0020653 0 ustar 00root root 0000000 0000000 SET client_min_messages = warning;
\set ECHO none
RESET client_min_messages;
pgmp-rel-1.0.4/test/sql/ 0000775 0000000 0000000 00000000000 13617050736 0015025 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/test/sql/mpq.sql 0000664 0000000 0000000 00000023770 13617050736 0016354 0 ustar 00root root 0000000 0000000 --
-- 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.sql 0000664 0000000 0000000 00000044365 13617050736 0016370 0 ustar 00root root 0000000 0000000 --
-- 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.sql 0000664 0000000 0000000 00000000406 13617050736 0017135 0 ustar 00root root 0000000 0000000 SET 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.sql 0000664 0000000 0000000 00000000405 13617050736 0017643 0 ustar 00root root 0000000 0000000 SET 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/ 0000775 0000000 0000000 00000000000 13617050736 0014407 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.4/tools/sql2extension.py 0000775 0000000 0000000 00000014021 13617050736 0017600 0 ustar 00root root 0000000 0000000 #!/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.py 0000775 0000000 0000000 00000005654 13617050736 0016136 0 ustar 00root root 0000000 0000000 #!/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)