pax_global_header 0000666 0000000 0000000 00000000064 12456066661 0014526 g ustar 00root root 0000000 0000000 52 comment=5d7ac5c5010faaee4604b64015c31c716db388a0
pgmp-rel-1.0.2/ 0000775 0000000 0000000 00000000000 12456066661 0013251 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/.gitignore 0000664 0000000 0000000 00000000312 12456066661 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/*
pgmp-rel-1.0.2/AUTHORS 0000664 0000000 0000000 00000000214 12456066661 0014316 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.2/COPYING 0000664 0000000 0000000 00000016743 12456066661 0014317 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.2/META.json 0000664 0000000 0000000 00000002254 12456066661 0014675 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.2",
"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.2",
"abstract": "PostgreSQL Multiple Precision Arithmetic extension"
}
},
"prereqs": {
"runtime": {
"requires": {
"PostgreSQL": "8.4.0"
}
}
},
"resources": {
"homepage": "http://pgmp.projects.postgresql.org/",
"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": "http://pgxn.org/meta/spec.txt"
}
}
pgmp-rel-1.0.2/Makefile 0000664 0000000 0000000 00000005634 12456066661 0014721 0 ustar 00root root 0000000 0000000 # pgmp -- pgxs-based makefile
#
# 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/.
.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 = $(wildcard src/*.c)
SRC_H = $(wildcard src/*.h)
SRCFILES = $(SRC_C) $(SRC_H)
OBJS = $(patsubst %.c,%.o,$(SRC_C))
TESTFILES = $(wildcard test/sql/*.sql) $(wildcard test/expected/*.out)
DOCS = $(wildcard docs/*.rst) docs/conf.py docs/Makefile docs/_static/pgmp.css
PKGFILES = AUTHORS COPYING README.rst Makefile \
META.json pgmp.control \
sql/pgmp.pysql sql/uninstall_pgmp.sql \
$(SRCFILES) $(DOCS) $(TESTFILES) \
$(wildcard tools/*.py)
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.2/README.rst 0000664 0000000 0000000 00000002506 12456066661 0014743 0 ustar 00root root 0000000 0000000 PostgreSQL Multiple Precision Arithmetic extension
==================================================
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: http://pgmp.projects.postgresql.org/
- Source repository: https://github.com/dvarrazzo/pgmp/
pgmp-rel-1.0.2/docs/ 0000775 0000000 0000000 00000000000 12456066661 0014201 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/docs/Makefile 0000664 0000000 0000000 00000002370 12456066661 0015643 0 ustar 00root root 0000000 0000000 # pgmp -- documentation makefile
#
# Use 'make env', then 'make html' to build the HTML documentation
#
# Copyright (C) 2011 Daniele Varrazzo
PYTHON := python$(PYTHON_VERSION)
PYTHON_VERSION ?= $(shell $(PYTHON) -c 'import sys; print ("%d.%d" % sys.version_info[:2])')
BUILD_DIR = $(shell pwd)/build/lib.$(PYTHON_VERSION)
ENV_DIR = $(shell pwd)/env/py-$(PYTHON_VERSION)
ENV_BIN = $(ENV_DIR)/bin
ENV_LIB = $(ENV_DIR)/lib
EASY_INSTALL = PYTHONPATH=$(ENV_LIB) $(ENV_BIN)/easy_install-$(PYTHON_VERSION) -d $(ENV_LIB) -s $(ENV_BIN)
EZ_SETUP = $(ENV_BIN)/ez_setup.py
SPHINXOPTS =
SPHINXBUILD = $(ENV_BIN)/sphinx-build
PAPER =
BUILDDIR = .
.PHONY: env clean html
default: html
html:
PYTHONPATH=$(ENV_LIB) $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) \
. $(BUILDDIR)/html
# The environment is currently required to build the documentation.
# It is not clean by 'make clean'
env: easy_install
mkdir -p $(ENV_BIN)
mkdir -p $(ENV_LIB)
$(EASY_INSTALL) -U "Pygments>=1.5"
$(EASY_INSTALL) -U "sphinx>=1.1"
easy_install: ez_setup
PYTHONPATH=$(ENV_LIB) $(PYTHON) $(EZ_SETUP) -d $(ENV_LIB) -s $(ENV_BIN) setuptools
ez_setup:
mkdir -p $(ENV_BIN)
mkdir -p $(ENV_LIB)
wget -O $(EZ_SETUP) http://peak.telecommunity.com/dist/ez_setup.py
clean:
$(RM) -r html
pgmp-rel-1.0.2/docs/_static/ 0000775 0000000 0000000 00000000000 12456066661 0015627 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/docs/_static/pgmp.css 0000664 0000000 0000000 00000001264 12456066661 0017307 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%;
}
pgmp-rel-1.0.2/docs/conf.py 0000664 0000000 0000000 00000020245 12456066661 0015503 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('.'))
# -- 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']
# 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, 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()
# 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 = 'http://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 = {'http://docs.python.org/': None}
pgmp-rel-1.0.2/docs/index.rst 0000664 0000000 0000000 00000002447 12456066661 0016051 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.
.. _PostgreSQL: http://www.postgresql.org
.. _GMP: http://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.
References
----------
- Homepage: http://pgmp.projects.postgresql.org/
- Discussion group: http://groups.google.com/group/postgresql-gmp
- Source repository: https://github.com/dvarrazzo/pgmp/
- Bug tracking: https://github.com/dvarrazzo/pgmp/issues
- PgFoundry project: http://pgfoundry.org/projects/pgmp/
Documentation
-------------
.. toctree::
:maxdepth: 2
install
mpz
mpq
misc
Indices and tables
------------------
* :ref:`genindex`
* :ref:`search`
..
* :ref:`modindex`
..
To Do List
----------
.. todolist::
pgmp-rel-1.0.2/docs/install.rst 0000664 0000000 0000000 00000005453 12456066661 0016410 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 and 5.0.1)
.. 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.
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.
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.
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 function 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.
.. __: http://developer.postgresql.org/pgdocs/postgres/extend-extensions.html
pgmp-rel-1.0.2/docs/misc.rst 0000664 0000000 0000000 00000001001 12456066661 0015656 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.2/docs/mpq.rst 0000664 0000000 0000000 00000015044 12456066661 0015534 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`__.
.. __: http://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.2/docs/mpz.rst 0000664 0000000 0000000 00000057124 12456066661 0015552 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.
.. __: http://www.postgresql.org/docs/9.0/static/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”.
.. __: http://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}`.
.. __: http://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}`.
.. __: http://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}`.
.. __: http://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.
.. __: http://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.
.. __: http://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.
.. __: http://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. http://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()`.
.. __: http://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.
.. __: http://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`.
.. __: http://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.2/pgmp.control 0000664 0000000 0000000 00000001647 12456066661 0015626 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.0'
comment = 'Multiple Precision Arithmetic extension'
directory = 'pgmp'
relocatable = true
pgmp-rel-1.0.2/sandbox/ 0000775 0000000 0000000 00000000000 12456066661 0014707 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/sandbox/bench/ 0000775 0000000 0000000 00000000000 12456066661 0015766 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/sandbox/bench/Arith-1e6.txt 0000664 0000000 0000000 00000001762 12456066661 0020175 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.2/sandbox/bench/Factorial.txt 0000664 0000000 0000000 00000001251 12456066661 0020432 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.2/sandbox/bench/SumInteger-1e6.txt 0000664 0000000 0000000 00000003321 12456066661 0021201 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.2/sandbox/bench/SumRational-p2-1e6.txt 0000664 0000000 0000000 00000001660 12456066661 0021700 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.2/sandbox/bench/SumRational-p4-1e6.txt 0000664 0000000 0000000 00000001661 12456066661 0021703 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.2/sandbox/bench/SumRational-p8-1e6.txt 0000664 0000000 0000000 00000001547 12456066661 0021712 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.2/sandbox/bench/SumSequence-1e6.txt 0000664 0000000 0000000 00000001540 12456066661 0021355 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.2/sandbox/bench/TableSize-1e6-small.txt 0000664 0000000 0000000 00000002024 12456066661 0022106 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.2/sandbox/bench/TableSize-1e6.txt 0000664 0000000 0000000 00000002250 12456066661 0021001 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.2/sandbox/bench/benchmark.py 0000775 0000000 0000000 00000032606 12456066661 0020304 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""Script to perform comparisons between pmpz and other data types.
Copyright (C) 2011 - 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.2/sandbox/bench/plot_result.py 0000775 0000000 0000000 00000002607 12456066661 0020724 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""Plot a result from benchmark.py
Copyright (C) 2011 - 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 = 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.2/sandbox/hello/ 0000775 0000000 0000000 00000000000 12456066661 0016012 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/sandbox/hello/Makefile 0000664 0000000 0000000 00000000101 12456066661 0017442 0 ustar 00root root 0000000 0000000 CFLAGS=-g
LDFLAGS=-lgmp
all: hello
clean:
$(RM) hello.o hello
pgmp-rel-1.0.2/sandbox/hello/hello.c 0000664 0000000 0000000 00000000535 12456066661 0017264 0 ustar 00root root 0000000 0000000 /* A test program to study the mpz_t structure.
*
* Copyright (C) 2011 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.2/sandbox/pi/ 0000775 0000000 0000000 00000000000 12456066661 0015317 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/sandbox/pi/calc_pi.py 0000775 0000000 0000000 00000002402 12456066661 0017264 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
A script to calculate pi using parallel PostgreSQL processes.
Copyright (C) 2011 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.2/sandbox/pi/calc_pi.sh 0000775 0000000 0000000 00000002054 12456066661 0017251 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 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.2/sandbox/pi/pi.sql 0000664 0000000 0000000 00000003364 12456066661 0016456 0 ustar 00root root 0000000 0000000 -- Some functions to calculate pi digits using mpz integers.
--
-- Reference:
-- http://en.literateprograms.org/Pi_with_Machin%27s_formula_%28Python%29
--
-- Copyright (C) 2011 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.2/sql/ 0000775 0000000 0000000 00000000000 12456066661 0014050 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/sql/pgmp.pysql 0000664 0000000 0000000 00000035173 12456066661 0016116 0 ustar 00root root 0000000 0000000 /* pgmp -- Module installation SQL script
*
* 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/.
*/
!# 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",
print volatile and "VOLATILE" or "IMMUTABLE",
print strict and "STRICT" or "",
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.2/sql/uninstall_pgmp.sql 0000664 0000000 0000000 00000002666 12456066661 0017637 0 ustar 00root root 0000000 0000000 /* pgmp -- Module uninstall SQL script
*
* 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/.
*/
--
-- 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.2/src/ 0000775 0000000 0000000 00000000000 12456066661 0014040 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/src/pgmp-impl.h 0000664 0000000 0000000 00000010775 12456066661 0016125 0 ustar 00root root 0000000 0000000 /* pgmp-impl -- Implementation details
*
* 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/.
*/
#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.2/src/pgmp.c 0000664 0000000 0000000 00000006322 12456066661 0015152 0 ustar 00root root 0000000 0000000 /* pgmp -- PostgreSQL GMP module
*
* 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/.
*/
#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.2/src/pgmp_utils.c 0000664 0000000 0000000 00000003662 12456066661 0016376 0 ustar 00root root 0000000 0000000 /* pgmp_utils -- misc utility module
*
* 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/.
*/
#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.2/src/pgmp_utils.h 0000664 0000000 0000000 00000002254 12456066661 0016377 0 ustar 00root root 0000000 0000000 /* pgmp_utils -- misc utility module
*
* 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/.
*/
#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.2/src/pmpq.c 0000664 0000000 0000000 00000011135 12456066661 0015162 0 ustar 00root root 0000000 0000000 /* pmpq -- PostgreSQL data type for GMP mpq
*
* 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/.
*/
#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);
mpz_ptr den = mpq_denref(q);
int nsize = SIZ(num);
if (LIKELY(0 != nsize))
{
/* Make enough room after the numer to store the denom limbs */
int nalloc = ABS(nsize);
int dsize = SIZ(mpq_denref(q));
if (nalloc >= dsize)
{
LIMBS(num) = _mpz_realloc(num, nalloc + dsize);
res = (pmpq *)((char *)LIMBS(num) - PMPQ_HDRSIZE);
SET_VARSIZE(res,
PMPQ_HDRSIZE + (nalloc + dsize) * sizeof(mp_limb_t));
/* 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);
SET_VARSIZE(res,
PMPQ_HDRSIZE + (nalloc + dsize) * sizeof(mp_limb_t));
/* 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 the sign */
if (nsize < 0) { res->mdata = PMPQ_SET_NEGATIVE(res->mdata); }
}
else
{
res = (pmpq *)((char *)LIMBS(num) - 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.2/src/pmpq.h 0000664 0000000 0000000 00000007573 12456066661 0015202 0 ustar 00root root 0000000 0000000 /* pmpq -- PostgreSQL data type for GMP mpq
*
* 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/.
*/
#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.2/src/pmpq_agg.c 0000664 0000000 0000000 00000005315 12456066661 0016003 0 ustar 00root root 0000000 0000000 /* pmpq_agg -- mpq aggregation functions
*
* 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/.
*/
#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.2/src/pmpq_arith.c 0000664 0000000 0000000 00000014706 12456066661 0016360 0 ustar 00root root 0000000 0000000 /* pmpq_arith -- mpq arithmetic functions
*
* 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/.
*/
#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
* http://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.2/src/pmpq_io.c 0000664 0000000 0000000 00000027625 12456066661 0015664 0 ustar 00root root 0000000 0000000 /* pmpq_io -- mpq Input/Output functions
*
* 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/.
*/
#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;
unsigned 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.2/src/pmpz.c 0000664 0000000 0000000 00000006222 12456066661 0015174 0 ustar 00root root 0000000 0000000 /* pmpz -- PostgreSQL data type for GMP mpz
*
* 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/.
*/
#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;
int size = SIZ(z);
res = (pmpz *)((char *)LIMBS(z) - PMPZ_HDRSIZE);
if (LIKELY(0 != size))
{
size_t slimbs;
int sign;
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
{
/* In the zero representation there are no limbs */
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.2/src/pmpz.h 0000664 0000000 0000000 00000011307 12456066661 0015201 0 ustar 00root root 0000000 0000000 /* pmpz -- PostgreSQL data type for GMP mpz
*
* 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/.
*/
#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.2/src/pmpz_agg.c 0000664 0000000 0000000 00000005424 12456066661 0016015 0 ustar 00root root 0000000 0000000 /* pmpz_agg -- mpz aggregation functions
*
* 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/.
*/
#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.2/src/pmpz_arith.c 0000664 0000000 0000000 00000016654 12456066661 0016375 0 ustar 00root root 0000000 0000000 /* pmpz_arith -- mpz arithmetic functions
*
* 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/.
*/
#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
/*
* 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.2/src/pmpz_bits.c 0000664 0000000 0000000 00000006324 12456066661 0016220 0 ustar 00root root 0000000 0000000 /* pmpz_bits -- bit manipulation functions
*
* 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/.
*/
#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.2/src/pmpz_io.c 0000664 0000000 0000000 00000021114 12456066661 0015660 0 ustar 00root root 0000000 0000000 /* pmpz_io -- mpz Input/Output functions
*
* 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/.
*/
#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 mpmz_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.2/src/pmpz_rand.c 0000664 0000000 0000000 00000010534 12456066661 0016201 0 ustar 00root root 0000000 0000000 /* pmpz_rand -- mpz random numbers
*
* 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/.
*/
#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.2/src/pmpz_roots.c 0000664 0000000 0000000 00000003355 12456066661 0016426 0 ustar 00root root 0000000 0000000 /* pmpz_roots -- root extraction functions
*
* 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/.
*/
#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.2/src/pmpz_theor.c 0000664 0000000 0000000 00000006307 12456066661 0016401 0 ustar 00root root 0000000 0000000 /* pmpz_theor -- number theoretic functions
*
* 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/.
*/
#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.2/test/ 0000775 0000000 0000000 00000000000 12456066661 0014230 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/test/expected/ 0000775 0000000 0000000 00000000000 12456066661 0016031 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/test/expected/mpq.out 0000664 0000000 0000000 00000036276 12456066661 0017375 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);
create index test_mpq_hash_idx on test_mpq_idx using hash (q);
-- 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.2/test/expected/mpz.out 0000664 0000000 0000000 00000064303 12456066661 0017376 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);
create index test_mpz_hash_idx on test_mpz_idx using hash (z);
-- 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.2/test/expected/setup-91.out 0000664 0000000 0000000 00000000115 12456066661 0020146 0 ustar 00root root 0000000 0000000 SET client_min_messages = warning;
\set ECHO none
RESET client_min_messages;
pgmp-rel-1.0.2/test/expected/setup-pre91.out 0000664 0000000 0000000 00000000115 12456066661 0020655 0 ustar 00root root 0000000 0000000 SET client_min_messages = warning;
\set ECHO none
RESET client_min_messages;
pgmp-rel-1.0.2/test/sql/ 0000775 0000000 0000000 00000000000 12456066661 0015027 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/test/sql/mpq.sql 0000664 0000000 0000000 00000023674 12456066661 0016361 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);
create index test_mpq_hash_idx on test_mpq_idx using hash (q);
-- 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.2/test/sql/mpz.sql 0000664 0000000 0000000 00000044271 12456066661 0016366 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);
create index test_mpz_hash_idx on test_mpz_idx using hash (z);
-- 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.2/test/sql/setup-91.sql 0000664 0000000 0000000 00000000406 12456066661 0017137 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.2/test/sql/setup-pre91.sql 0000664 0000000 0000000 00000000405 12456066661 0017645 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.2/tools/ 0000775 0000000 0000000 00000000000 12456066661 0014411 5 ustar 00root root 0000000 0000000 pgmp-rel-1.0.2/tools/sql2extension.py 0000775 0000000 0000000 00000013765 12456066661 0017620 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, 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 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.2/tools/unmix.py 0000775 0000000 0000000 00000005520 12456066661 0016130 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, 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
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':
exec ''.join(script) in 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)