[mathicgb] 01/393: Initial commit of mathicgb code.
Doug Torrance
dtorrance-guest at moszumanska.debian.org
Fri Apr 3 15:58:20 UTC 2015
This is an automated email from the git hooks/post-receive script.
dtorrance-guest pushed a commit to branch upstream
in repository mathicgb.
commit 31d363bb6835670834825d6ff74aea8ee165c8f0
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date: Mon Jul 9 00:24:25 2012 -0400
Initial commit of mathicgb code.
---
Makefile.am | 86 ++++
README.md | 6 +
autogen.sh | 26 ++
configure.ac | 67 +++
fixspace | 51 +++
libs/.gitignore | 1 +
replace | 11 +
src/cli/GBMain.cpp | 335 +++++++++++++++
src/mathicgb/BitTriangle.cpp | 13 +
src/mathicgb/BitTriangle.hpp | 78 ++++
src/mathicgb/BjarkeGeobucket.cpp | 136 +++++++
src/mathicgb/BjarkeGeobucket.hpp | 100 +++++
src/mathicgb/BuchbergerAlg.cpp | 426 +++++++++++++++++++
src/mathicgb/BuchbergerAlg.hpp | 78 ++++
src/mathicgb/ChainedHashTable.cpp | 11 +
src/mathicgb/ChainedHashTable.hpp | 290 +++++++++++++
src/mathicgb/DivLookup.cpp | 7 +
src/mathicgb/DivLookup.hpp | 630 +++++++++++++++++++++++++++++
src/mathicgb/DivisorLookup.cpp | 130 ++++++
src/mathicgb/DivisorLookup.hpp | 92 +++++
src/mathicgb/FreeModuleOrder.cpp | 592 +++++++++++++++++++++++++++
src/mathicgb/FreeModuleOrder.hpp | 54 +++
src/mathicgb/GroebnerBasis.cpp | 482 ++++++++++++++++++++++
src/mathicgb/GroebnerBasis.hpp | 248 ++++++++++++
src/mathicgb/HashTourReducer.cpp | 217 ++++++++++
src/mathicgb/HashTourReducer.hpp | 88 ++++
src/mathicgb/Ideal.cpp | 73 ++++
src/mathicgb/Ideal.hpp | 44 ++
src/mathicgb/KoszulQueue.cpp | 12 +
src/mathicgb/KoszulQueue.hpp | 89 ++++
src/mathicgb/MTArray.cpp | 254 ++++++++++++
src/mathicgb/MTArray.hpp | 104 +++++
src/mathicgb/MonTableDivList.cpp | 7 +
src/mathicgb/MonTableDivList.hpp | 287 +++++++++++++
src/mathicgb/MonTableKDTree.cpp | 7 +
src/mathicgb/MonTableKDTree.hpp | 292 ++++++++++++++
src/mathicgb/MonTableNaive.cpp | 169 ++++++++
src/mathicgb/MonTableNaive.hpp | 84 ++++
src/mathicgb/MonomialHashTable.cpp | 9 +
src/mathicgb/MonomialHashTable.hpp | 90 +++++
src/mathicgb/PairTriangle.cpp | 226 +++++++++++
src/mathicgb/PairTriangle.hpp | 135 +++++++
src/mathicgb/Poly.cpp | 284 +++++++++++++
src/mathicgb/Poly.hpp | 132 ++++++
src/mathicgb/PolyBasis.cpp | 343 ++++++++++++++++
src/mathicgb/PolyBasis.hpp | 267 ++++++++++++
src/mathicgb/PolyGeoBucket.cpp | 264 ++++++++++++
src/mathicgb/PolyGeoBucket.hpp | 59 +++
src/mathicgb/PolyHashReducer.cpp | 180 +++++++++
src/mathicgb/PolyHashReducer.hpp | 56 +++
src/mathicgb/PolyHashTable.cpp | 466 +++++++++++++++++++++
src/mathicgb/PolyHashTable.hpp | 147 +++++++
src/mathicgb/PolyHeap.cpp | 185 +++++++++
src/mathicgb/PolyHeap.hpp | 74 ++++
src/mathicgb/PolyReducer.cpp | 113 ++++++
src/mathicgb/PolyReducer.hpp | 44 ++
src/mathicgb/PolyRing.cpp | 748 ++++++++++++++++++++++++++++++++++
src/mathicgb/PolyRing.hpp | 752 ++++++++++++++++++++++++++++++++++
src/mathicgb/Reducer.cpp | 393 ++++++++++++++++++
src/mathicgb/Reducer.hpp | 153 +++++++
src/mathicgb/ReducerDedup.hpp | 192 +++++++++
src/mathicgb/ReducerHash.hpp | 188 +++++++++
src/mathicgb/ReducerHashPack.hpp | 285 +++++++++++++
src/mathicgb/ReducerHelper.hpp | 85 ++++
src/mathicgb/ReducerNoDedup.hpp | 186 +++++++++
src/mathicgb/ReducerPack.hpp | 250 ++++++++++++
src/mathicgb/ReducerPackDedup.hpp | 308 ++++++++++++++
src/mathicgb/SPairHandler.cpp | 624 ++++++++++++++++++++++++++++
src/mathicgb/SPairHandler.hpp | 200 +++++++++
src/mathicgb/SPairQueue.cpp | 4 +
src/mathicgb/SPairQueue.hpp | 24 ++
src/mathicgb/SPairs.cpp | 546 +++++++++++++++++++++++++
src/mathicgb/SPairs.hpp | 156 +++++++
src/mathicgb/SignatureGB.cpp | 764 +++++++++++++++++++++++++++++++++++
src/mathicgb/SignatureGB.hpp | 113 ++++++
src/mathicgb/TournamentReducer.cpp | 183 +++++++++
src/mathicgb/TournamentReducer.hpp | 82 ++++
src/mathicgb/io-util.cpp | 175 ++++++++
src/mathicgb/io-util.hpp | 42 ++
src/mathicgb/stdinc.h | 57 +++
src/test/FreeModuleOrderTest.cpp | 94 +++++
src/test/gb-test.cpp | 165 ++++++++
src/test/gtestInclude.cpp | 10 +
src/test/ideals.cpp | 266 ++++++++++++
src/test/ideals.hpp | 55 +++
src/test/poly-test.cpp | 808 +++++++++++++++++++++++++++++++++++++
src/test/testMain.cpp | 6 +
87 files changed, 16665 insertions(+)
diff --git a/Makefile.am b/Makefile.am
new file mode 100755
index 0000000..0bac575
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,86 @@
+# options passed to aclocal, which is a tool for making macroes visible to
+# autoconf. We use -I to tell aclocal where we put the local macros.
+ACLOCAL_AMFLAGS = -I build/autotools/m4
+
+# Options passed to the C PreProcessor (CPP), NOT the C Plus Plus compiler.
+AM_CPPFLAGS = -I${top_srcdir}/
+libmathicgb_la_CPPFLAGS = $(DEPS_CFLAGS)
+
+# tell Libtool what the name of the library is.
+noinst_LTLIBRARIES = libmathicgb.la
+
+# set the C++ compiler to include src/
+AM_CXXFLAGS=-I$(top_srcdir)/src/
+
+# libraries that are needed by this library
+libmathicgb_la_LIBADD= $(DEPS_LIBS)
+
+# the sources that are built to make libmathicgb. Listing the headers in
+# sources ensure that those files are included in distributions.
+libmathicgb_la_SOURCES = src/mathicgb/BitTriangle.cpp \
+ src/mathicgb/BitTriangle.hpp src/mathicgb/BjarkeGeobucket.cpp \
+ src/mathicgb/BjarkeGeobucket.hpp src/mathicgb/BuchbergerAlg.cpp \
+ src/mathicgb/BuchbergerAlg.hpp src/mathicgb/ChainedHashTable.cpp \
+ src/mathicgb/ChainedHashTable.hpp src/mathicgb/DivisorLookup.cpp \
+ src/mathicgb/DivisorLookup.hpp src/mathicgb/DivLookup.cpp \
+ src/mathicgb/DivLookup.hpp src/mathicgb/FreeModuleOrder.cpp \
+ src/mathicgb/FreeModuleOrder.hpp src/mathicgb/GroebnerBasis.cpp \
+ src/mathicgb/GroebnerBasis.hpp src/mathicgb/HashTourReducer.cpp \
+ src/mathicgb/HashTourReducer.hpp src/mathicgb/Ideal.cpp \
+ src/mathicgb/Ideal.hpp src/mathicgb/io-util.cpp \
+ src/mathicgb/io-util.hpp src/mathicgb/KoszulQueue.cpp \
+ src/mathicgb/KoszulQueue.hpp src/mathicgb/MonomialHashTable.cpp \
+ src/mathicgb/MonomialHashTable.hpp src/mathicgb/MonTableDivList.cpp \
+ src/mathicgb/MonTableDivList.hpp src/mathicgb/MonTableKDTree.cpp \
+ src/mathicgb/MonTableKDTree.hpp src/mathicgb/MonTableNaive.cpp \
+ src/mathicgb/MonTableNaive.hpp src/mathicgb/MTArray.cpp \
+ src/mathicgb/MTArray.hpp src/mathicgb/PairTriangle.cpp \
+ src/mathicgb/PairTriangle.hpp src/mathicgb/Poly.cpp \
+ src/mathicgb/Poly.hpp src/mathicgb/PolyBasis.cpp \
+ src/mathicgb/PolyBasis.hpp src/mathicgb/PolyGeoBucket.cpp \
+ src/mathicgb/PolyGeoBucket.hpp src/mathicgb/PolyHashReducer.cpp \
+ src/mathicgb/PolyHashReducer.hpp src/mathicgb/PolyHashTable.cpp \
+ src/mathicgb/PolyHashTable.hpp src/mathicgb/PolyHeap.cpp \
+ src/mathicgb/PolyHeap.hpp src/mathicgb/PolyReducer.cpp \
+ src/mathicgb/PolyReducer.hpp src/mathicgb/PolyRing.cpp \
+ src/mathicgb/PolyRing.hpp src/mathicgb/Reducer.cpp \
+ src/mathicgb/Reducer.hpp src/mathicgb/ReducerDedup.hpp \
+ src/mathicgb/ReducerHash.hpp src/mathicgb/ReducerHashPack.hpp \
+ src/mathicgb/ReducerHelper.hpp src/mathicgb/ReducerNoDedup.hpp \
+ src/mathicgb/ReducerPack.hpp src/mathicgb/ReducerPackDedup.hpp \
+ src/mathicgb/SignatureGB.cpp src/mathicgb/SignatureGB.hpp \
+ src/mathicgb/SPairHandler.cpp src/mathicgb/SPairHandler.hpp \
+ src/mathicgb/SPairQueue.cpp src/mathicgb/SPairQueue.hpp \
+ src/mathicgb/SPairs.cpp src/mathicgb/SPairs.hpp \
+ src/mathicgb/stdinc.h src/mathicgb/TournamentReducer.cpp \
+ src/mathicgb/TournamentReducer.hpp
+
+# When making a distribution file, Automake knows to include all files
+# that are necessary to build the project. EXTRA_DIST specifies files
+# to include beyond those used in the build process.
+EXTRA_DIST = autogen.sh libs/gtest
+
+bin_PROGRAMS = mgb
+
+# set up the divisor query simulation. Listing the headers in sources
+# ensure that those files are included in distributions.
+mgb_CPPFLAGS = $(DEPS_CFLAGS)
+mgb_SOURCES = src/cli/GBMain.cpp
+mgb_LDADD = $(top_builddir)/libmathicgb.la
+
+# set up tests to run on "make check"
+TESTS=unittest
+check_PROGRAMS=$(TESTS)
+
+unittest_CPPFLAGS = $(DEPS_CFLAGS)
+unittest_CXXFLAGS=\
+ -I$(top_srcdir)/libs/gtest/include\
+ -I$(top_srcdir)/libs/gtest/\
+ -I$(top_srcdir)/src/
+unittest_LDADD = $(DEPS_LIBS)
+unittest_LDFLAGS= $(top_builddir)/libmathicgb.la
+
+test_LIBS=
+unittest_SOURCES=src/test/FreeModuleOrderTest.cpp \
+ src/test/gtestInclude.cpp src/test/testMain.cpp src/test/gb-test.cpp \
+ src/test/ideals.cpp src/test/poly-test.cpp src/test/ideals.hpp
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..da956ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+mathicgb
+=========
+
+Program for computing Groebner basis using the fast datastructures
+from mathic. Can also compute signature Groebner basis using the SB
+algorithm (see http://arxiv.org/abs/1206.6940).
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..aac2d0b
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+srcdir="`dirname '$0'`"
+
+autoreconf --verbose --install --force $srcdir
+
+# Download gtest into libs/gtest if it does not already exist. If you
+# changed gtest and need to get the original version back, just delete
+# the directory libs/gtest and run this script again.
+GTEST_DIR=$srcdir/libs/
+GTEST_TMP_DIR=$srcdir/libs/
+GTEST_VERSION=1.6.0
+GTEST_DOWNLOAD_FILE=gtest-$GTEST_VERSION.zip
+if [ ! -d libs/gtest ]; then
+ mkdir -p $GTEST_TMP_DIR;
+ rm -rf $GTEST_TMP_DIR/$GTEST_DOWNLOAD_FILE;
+ rm -rf $GTEST_TMP_DIR/gtest-$GTEST_VERSION;
+ rm -rf $GTEST_TMP_DIR/gtest;
+ ( \
+ cd $GTEST_TMP_DIR; \
+ wget http://googletest.googlecode.com/files/$GTEST_DOWNLOAD_FILE; \
+ unzip $GTEST_DOWNLOAD_FILE; \
+ rm $GTEST_DOWNLOAD_FILE \
+ )
+ rm -rf $GTEST_DIR/gtest;
+ mv $GTEST_TMP_DIR/gtest-$GTEST_VERSION $GTEST_DIR/gtest;
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100755
index 0000000..0f97a43
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,67 @@
+dnl AC_INIT sets up autoconf and must be first macro.
+AC_INIT([mathicgb], [1.0]) # package, version, bug-report-email
+
+# Check that mathicgb is installed and locate it
+PKG_CHECK_MODULES([DEPS], [memtailor-1.0 mathic-1.0])
+
+# set up information about directories
+AC_CONFIG_MACRO_DIR([build/autotools/m4]) # directory of extra autoconf macroes
+AC_CONFIG_AUX_DIR([build/autotools]) # directory for auxiliary build tools (install-sh etc)
+
+# check that source directory is correct
+dnl if autoconf is told the source code is in a directory that does not
+dnl contain this file then it knows that the directory is wrong.
+AC_CONFIG_SRCDIR([src/cli/GBMain.cpp])
+
+# Enable optional maintainer mode (off by default)
+dnl AM_MAINTAINER_MODE turns off automatic reconstruction of the build
+dnl files if the source build files have changed. A developer will want
+dnl those automatic reconstructions to happen so that changes to the
+dnl build system are actually carried out. However, a user might not
+dnl have the tools required to reconfigure and the need for
+dnl reconstruction might be spurious if the last-modified date is set
+dnl incorrectly on the build files.
+dnl
+dnl Passing the option [enable] to AM_MAINTAINER_MODE makes the
+dnl non-reconstruction feature available, but only when turned on by
+dnl passing the option –disable-maintainer-mode. This option is
+dnl apparently useful to some package distributors.
+AM_MAINTAINER_MODE([enable])
+
+# Set up Automake
+dnl foreign: do not create the GNU-specific file COPYING and do not complain
+dnl that GNU-specific files like NEWS, README, AUTHORS and ChangeLog are
+dnl missing.
+dnl -Wall: set Automake to emit all warnings it can. Is NOT RELATED to setting
+dnl warnings for other tools. For example, it wil not make the compiler
+dnl get a -Wall option.
+dnl subdir-objects: Put object files in a directory structure based on
+dnl the directory structure of the source files. This way, two source
+dnl files with the same name in different directories do not conflict.
+AM_INIT_AUTOMAKE([foreign subdir-objects -Wall])
+
+# Set up the $(LN_S) macro, which creates symbolic links
+AC_PROG_LN_S
+
+# set output variable INSTALL to the name of a BSD-compatible install program.
+# Requires install-sh to be present as a fallback, even on systems where
+# the fallback is not used.
+AC_PROG_INSTALL
+
+# Locate the C++ compiler.
+AC_PROG_CXX
+
+# Locate the archiver. This is only necessary in case of using an
+# unusual archiver such as Microsoft lib.
+AM_PROG_AR
+
+# Set up LibTool
+LT_INIT
+
+dnl Set up AC_OUTPUT to create each file by copying an input file
+dnl while substituting the output variable values.
+AC_CONFIG_FILES([Makefile])
+
+dnl Macro that is required to be at the end of any Autoconf script.
+dnl Creates config.status and launches it.
+AC_OUTPUT
diff --git a/fixspace b/fixspace
new file mode 100644
index 0000000..0228dcd
--- /dev/null
+++ b/fixspace
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Does the following operations on the files passed as arguments:
+# - Remove trailing space from lines
+# - Remove trailing blank lines
+# - Remove/convert DOS-style line breaks
+# - Expand tabs to spaces with a tabspace of 8
+
+# I once had an error where the conversion had an error (the computer
+# didn't have dos2unix), resulting in the converted files being empty.
+# The result was that every file got replaced by an empty file! That
+# was not so nice, so this script stops operation as soon as any error
+# occurs, and it also checks that only space has been changed before
+# it overwrites the original file with a space-fixed version.
+
+for f in $*; do echo $f;
+
+ tr -d '\r' < $f > __spaceTmp0;
+ if [ $? != 0 ]; then echo "There was an error removing DOS-style line breaks."; exit 1; fi;
+
+ expand -4 < __spaceTmp0 > __spaceTmp1;
+ if [ $? != 0 ]; then echo "There was an error expanding tabs."; exit 1; fi;
+
+ sed 's/[[:blank:]]*$//g' < __spaceTmp1 > __spaceTmp2;
+ if [ $? != 0 ]; then echo "There was an error eliminating trailing space from lines."; exit 1; fi;
+
+ # Make completely sure that we only changed the spaces
+ diff -EbwB -q $f __spaceTmp2;
+ if [ $? != 0 ]; then echo "There was an error. Conversion not confirmed correct."; exit 1; fi;
+
+ sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' < __spaceTmp2 > __spaceTmp3;
+ if [ $? != 0 ]; then echo "There was an error eliminating trailing blank lines."; exit 1; fi;
+
+ # We have to do diff twice, because diff will not ignore trailing
+ # lines that consist only of spaces. It will ignore changes to space and removal of
+ # completely empty lines, so if we do it twice we get the right thing.
+
+ # Make completely sure that we only changed the spaces
+ diff -EbwB -q __spaceTmp2 __spaceTmp3;
+ if [ $? != 0 ]; then echo "There was an error. Conversion not confirmed correct."; exit 1; fi;
+
+ diff -q $f __spaceTmp3 1>/dev/null;
+ if [ $? != 0 ]; then
+ mv -f __spaceTmp3 $f;
+ if [ $? != 0 ]; then echo "There was an error moving fixed file into place."; exit 1; fi;
+ echo "Fixed space issue for $f."
+ fi
+
+ rm -f __spaceTmp0 __spaceTmp1 __spaceTmp2 __spaceTmp3;
+ if [ $? != 0 ]; then echo "There was an error removing temporary files."; exit 1; fi;
+done;
diff --git a/libs/.gitignore b/libs/.gitignore
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/libs/.gitignore
@@ -0,0 +1 @@
+
diff --git a/replace b/replace
new file mode 100755
index 0000000..fb8bd7e
--- /dev/null
+++ b/replace
@@ -0,0 +1,11 @@
+#!/bin/bash
+echo "Running script \"sed s/$1/$2/\""
+for i in 1 2 3; do
+ /usr/bin/sleep 1; echo -n .;
+done
+
+for f in `ls src/*.cpp src/*.h`; do
+ echo "Processing $f";
+ sed "s/$1/$2/" < $f > $f.tmp
+ mv $f.tmp $f
+done
diff --git a/src/cli/GBMain.cpp b/src/cli/GBMain.cpp
new file mode 100755
index 0000000..bb4be44
--- /dev/null
+++ b/src/cli/GBMain.cpp
@@ -0,0 +1,335 @@
+#include "mathicgb/stdinc.h"
+
+#include "mathicgb/Ideal.hpp"
+#include "mathicgb/SignatureGB.hpp"
+#include "mathicgb/BuchbergerAlg.hpp"
+#include "mathicgb/io-util.hpp"
+#include "mathicgb/MTArray.hpp"
+
+#include <mathic.h>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <istream>
+#include <cctype>
+
+extern int tracingLevel;
+
+class CliActionSignature : public mic::Action {
+public:
+ CliActionSignature():
+ mUseSingularCriterionEarly("earlySingularCriterion",
+ "Apply the singular S-pair elimination criterion before queueing "
+ "that S-pair. Otherwise, the criterion is only checked just before "
+ "the S-pair would otherwise cause a polynomial reduction to occur. "
+ "This criterion is only relevant to the signature Buchberger "
+ "algorithm.",
+ false),
+
+ mPreferSparseReducers("preferSparseReducers",
+ "If true, always use the sparsest reducer in polynomial reduction. "
+ "This option impacts both classic and signature constrained "
+ "polynomial reduction. Ties are broken by taking the oldest reducer. "
+ "If this option is false, the oldest reducer is always used.",
+ true),
+
+ mAutoTailReduce("autoTailReduce",
+ "Reduce the non-leading terms of all polynomials whenever an element "
+ "is inserted into the basis. Only relevant to the "
+ "classic Buchberger algorithm.",
+ false),
+
+ mAutoTopReduce("autoTopReduce",
+ "Reduce any basis element whose lead term becomes reducible "
+ "by a different basis element. Only relevant to the "
+ "classic Buchberger algorithm.",
+ true),
+
+ mClassicBuchbergerAlgorithm("classicBuchbergerAlgorithm",
+ "Use the classic buchberger algorithm. If true, ignore "
+ "any options having to do with signatures or the signature "
+ "algorithm.",
+ false),
+
+ mPostponeKoszul("postponeKoszul",
+ "Postpone the construction of Koszul syzygy signatures.",
+ true),
+
+ mComputeSignatureBasis("computeSignatureBasis",
+ "Compute a signature basis. If false and using the signature "
+ "algorithm, stop the computation early when detecting "
+ "a grobner basis.",
+ true),
+
+ mUseBaseDivisors("useBaseDivisors",
+ "Use high ratio and low ratio base divisors to eliminate "
+ "S-spairs quickly based on signature.",
+ true),
+
+ mSPairQueue("spairQueue",
+ "The priority queue used to order S-pairs.\n"
+ " 0 tournament tree in front of triangle\n"
+ " 1 heap in front of triangle\n"
+ " 2 tournament tree\n"
+ " 3 heap\n",
+ 0),
+
+ mTracingLevel("tracingLevel",
+ "How much information to print out about actions taken. Nothing "
+ "extra is printed if the value is zero. Higher values than zero "
+ "result in more information.",
+ 0),
+
+ mBreakAfter("breakAfter",
+ "Stop the computation after this elements have been added to the basis. "
+ "The computation runs to the end of the value is zero.",
+ 0),
+
+ mPrintInterval("printInterval",
+ "Print information about the computation every time this many S-pair "
+ "reductions have been performed. Do not print information like this "
+ "during the computation if the value is zero.",
+ 0),
+
+ mMonomialTable("monomialTable",
+ "The kind of monomial table data structure to use.\n",
+ 2),
+
+ mDivisorLookup("divisorLookup",
+ "The divisor lookup data structure to use.\n",
+ 2),
+
+ mReducer("reducer",
+ "The data structure to use for polynomial reduction.\n",
+ 4),
+
+ mModuleOrder("moduleOrder",
+ "The free module term order.\n",
+ 4),
+
+ mProjectName("projectName",
+ "For a value X, read the input ideal from X.ideal, "
+ "write statistics to X.stats and write the output to X.gb",
+ ""),
+
+ mStrategy("strategy",
+ "The integer representing the strategy to use for the computation:\n"
+ " 0 signature+gbstop\n"
+ " 1 buchberger\n"
+ " 2 late koszul\n"
+ " 4 not used (used to be MES reduction)\n"
+ " 8 signature\n",
+ 2)
+ {
+ {
+ std::ostringstream orderOut;
+ FreeModuleOrder::displayOrderTypes(orderOut);
+ mModuleOrder.appendToDescription(orderOut.str());
+ }
+ {
+ std::ostringstream reducerOut;
+ Reducer::displayReducerTypes(reducerOut);
+ mReducer.appendToDescription(reducerOut.str());
+ }
+ {
+ std::ostringstream divisorLookupOut;
+ DivisorLookup::displayDivisorLookupTypes(divisorLookupOut);
+ mDivisorLookup.appendToDescription(divisorLookupOut.str());
+ }
+ {
+ std::ostringstream monomialTableOut;
+ MonomialTableArray::displayMTTypes(monomialTableOut);
+ mMonomialTable.appendToDescription(monomialTableOut.str());
+ }
+ }
+
+ virtual ~CliActionSignature() {}
+
+ virtual void directOptions(
+ std::vector<std::string> tokens,
+ mic::CliParser& parser
+ ) {
+ switch (tokens.size()) {
+ case 8: mStrategy.processArgument(tokens[6]);
+ mClassicBuchbergerAlgorithm.setValue((mStrategy.value() & 1) == 1);
+ mPostponeKoszul.setValue((mStrategy.value() & 2) != 0);
+ mComputeSignatureBasis.setValue((mStrategy.value() & 8) != 0);
+ mUseBaseDivisors.setValue((mStrategy.value() & 16) != 0);
+ case 7: mTracingLevel.processArgument(tokens[5]);
+ case 6: // ignore this value
+ case 5: mMonomialTable.processArgument(tokens[3]);
+ case 4: mDivisorLookup.processArgument(tokens[2]);
+ case 3: mReducer.processArgument(tokens[1]);
+ case 2: mModuleOrder.processArgument(tokens[0]);
+ case 1: mProjectName.processArgument(tokens.back());
+ case 0: break;
+ default: mic::reportError
+ (std::string("Too many direct options for action ") + staticName());
+ }
+ }
+
+ virtual void performAction() {
+ tracingLevel = mTracingLevel.value();
+
+ // read input file
+ std::auto_ptr<Ideal> ideal;
+ {
+ std::string const inputIdealFile = mProjectName.value() + ".ideal";
+ std::ifstream inputFile(inputIdealFile.c_str());
+ if (inputFile.fail())
+ mic::reportError("Could not read input file " + inputIdealFile);
+ ideal.reset(Ideal::parse(inputFile));
+ }
+ std::auto_ptr<PolyRing const> ring(&(ideal->ring()));
+
+ if (mClassicBuchbergerAlgorithm.value()) {
+ BuchbergerAlg alg(
+ *ideal,
+ mModuleOrder.value(),
+ Reducer::ReducerType(mReducer.value()),
+ mDivisorLookup.value(),
+ mPreferSparseReducers.value(),
+ mSPairQueue.value());
+ alg.setBreakAfter(mBreakAfter.value());
+ alg.setPrintInterval(mPrintInterval.value());
+ alg.setUseAutoTopReduction(mAutoTopReduce.value());
+ alg.setUseAutoTailReduction(mAutoTailReduce.value());
+
+ alg.computeGrobnerBasis();
+ alg.printStats(std::cerr);
+
+ std::ofstream statsOut((mProjectName.value() + ".stats").c_str());
+ alg.printStats(statsOut);
+ std::ofstream gbOut((mProjectName.value() + ".gb").c_str());
+ output(gbOut, alg.basis());
+ } else {
+ SignatureGB alg(
+ *ideal,
+ mModuleOrder.value(),
+ Reducer::reducerType(mReducer.value()),
+ mDivisorLookup.value(),
+ mMonomialTable.value(),
+ mPostponeKoszul.value(),
+ mUseBaseDivisors.value(),
+ mPreferSparseReducers.value(),
+ mUseSingularCriterionEarly.value(),
+ mSPairQueue.value());
+ alg.setBreakAfter(mBreakAfter.value());
+ alg.setPrintInterval(mPrintInterval.value());
+ alg.setComputeSignatureBasis(mComputeSignatureBasis.value());
+ alg.computeGrobnerBasis();
+
+ // print statistics
+ alg.displayStats(std::cout);
+ alg.displayPaperStats(std::cout);
+
+ {
+ std::ofstream statsOut((mProjectName.value() + ".stats").c_str());
+ alg.displayStats(statsOut);
+ alg.displayPaperStats(statsOut);
+ }
+
+ // print basis
+ {
+ std::ofstream ogb((mProjectName.value() + ".gb").c_str());
+ ogb << "-- gb: ----\n";
+ alg.getGB()->display(ogb);
+ }
+
+ // print syzygy basis
+ {
+ std::ofstream syzygyOut((mProjectName.value() + ".syz").c_str());
+ syzygyOut << "-- syz: ----\n";
+ alg.getSyzTable()->display(syzygyOut, 1);
+ syzygyOut << std::endl;
+ }
+ }
+ }
+
+ static const char* staticName() {return "gb";}
+
+ virtual const char* name() const {return staticName();}
+ virtual const char* description() const {
+ return
+ "Compute a Grobner basis. Takes optional direct parameters in this order:\n"
+ " <module order> <reducer> <divisor lookup> <monomial table>\n"
+ " <ignore this value> <trace level> <strategy> <project name>\n"
+ "If fewer than 8 direct parameters, only so many options are read. They "
+ "are read in the order given, except that the the last direct "
+ "parameter, if any, is always assumed to be the project name. "
+ "Parameters given with a dash take priority over direct parameters.";
+ }
+ virtual const char* shortDescription() const {return "Compute a Grobner basis";}
+
+ virtual void pushBackParameters(std::vector<mic::CliParameter*>& parameters) {
+ parameters.push_back(&mUseSingularCriterionEarly);
+ parameters.push_back(&mPreferSparseReducers);
+ parameters.push_back(&mAutoTailReduce);
+ parameters.push_back(&mAutoTopReduce);
+ parameters.push_back(&mClassicBuchbergerAlgorithm);
+ parameters.push_back(&mPostponeKoszul);
+ parameters.push_back(&mComputeSignatureBasis);
+ parameters.push_back(&mUseBaseDivisors);
+ parameters.push_back(&mSPairQueue);
+ parameters.push_back(&mTracingLevel);
+ parameters.push_back(&mBreakAfter);
+ parameters.push_back(&mPrintInterval);
+ parameters.push_back(&mMonomialTable);
+ parameters.push_back(&mDivisorLookup);
+ parameters.push_back(&mReducer);
+ parameters.push_back(&mModuleOrder);
+ parameters.push_back(&mProjectName);
+
+ // do not expose the strategy parameter - it is only here to support
+ // the old format of using direct numeric parameters to the action.
+ //parameters.push_back(&mStrategy);
+ }
+
+private:
+ mic::BoolParameter mUseSingularCriterionEarly;
+ mic::BoolParameter mPreferSparseReducers;
+ mic::BoolParameter mAutoTailReduce;
+ mic::BoolParameter mAutoTopReduce;
+ mic::BoolParameter mClassicBuchbergerAlgorithm;
+ mic::BoolParameter mPostponeKoszul;
+ mic::BoolParameter mComputeSignatureBasis;
+ mic::BoolParameter mUseBaseDivisors;
+ mic::IntegerParameter mSPairQueue;
+ mic::IntegerParameter mTracingLevel;
+ mic::IntegerParameter mBreakAfter;
+ mic::IntegerParameter mPrintInterval;
+ mic::IntegerParameter mMonomialTable;
+ mic::IntegerParameter mDivisorLookup;
+ mic::IntegerParameter mReducer;
+ mic::IntegerParameter mModuleOrder;
+ mic::StringParameter mProjectName;
+ mic::IntegerParameter mStrategy;
+};
+
+int oldmain(int argc, char **argv);
+int main(int argc, char **argv) {
+ //oldmain(argc, argv);
+ try {
+ mic::CliParser parser;
+ parser.registerAction<CliActionSignature>();
+ parser.registerAction<mic::HelpAction>();
+ std::vector<std::string> commandLine(argv, argv + argc);
+ if (commandLine.size() >= 2 &&
+ !commandLine[1].empty() &&
+ std::isdigit(commandLine[1][0])) {
+ commandLine[0] = "gb";
+ } else
+ commandLine.erase(commandLine.begin());
+ std::auto_ptr<mic::Action> action = parser.parse(commandLine);
+ action->performAction();
+ } catch (const mic::MathicException& e) {
+ mic::display(e.what());
+ return -1;
+ }
+ return 0;
+};
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/BitTriangle.cpp b/src/mathicgb/BitTriangle.cpp
new file mode 100644
index 0000000..b141cca
--- /dev/null
+++ b/src/mathicgb/BitTriangle.cpp
@@ -0,0 +1,13 @@
+#include "stdinc.h"
+#include "BitTriangle.hpp"
+
+size_t BitTriangle::getMemoryUse() const
+{
+ size_t sum = mColumns.capacity() * sizeof(mColumns.front());
+ const size_t stop = mColumns.size();
+ for (size_t i = 0; i != stop; ++i) {
+ size_t const capacity = mColumns[i].capacity();
+ sum += (capacity / 8) + (capacity % 8 != 0); // 8 bits per byte rounded up
+ }
+ return sum;
+}
diff --git a/src/mathicgb/BitTriangle.hpp b/src/mathicgb/BitTriangle.hpp
new file mode 100644
index 0000000..309e766
--- /dev/null
+++ b/src/mathicgb/BitTriangle.hpp
@@ -0,0 +1,78 @@
+#ifndef _bit_triangle_h_
+#define _bit_triangle_h_
+
+#include <vector>
+
+// Object that stores a triangular 2-dimensional array of bits. For example:
+//
+// row
+// 3|
+// 2| 1
+// 1| 0 1
+// 0| 0 0 0
+// -------
+// 0 1 2 3 column
+//
+// A bit is addressed by a pair (column, row) where the column goes first.
+// All valid address pairs have 0 <= row < column < columnCount().
+class BitTriangle {
+public:
+ // Returns how many columns the triangle has
+ size_t columnCount() const {return mColumns.size();}
+
+ // Returns true if there are no columns in the triangle
+ bool empty() const {return mColumns.empty();}
+
+ // Adds a new column of the triangle. This increases columnCount() by
+ // one, and the index of the new column is the previous value of
+ // columnCount(). The new bits are all set to false initially.
+ void addColumn() {
+ size_t oldSize = mColumns.size();
+ mColumns.resize(oldSize + 1);
+ mColumns[oldSize].resize(oldSize);
+ }
+
+ // Returns the bit in the given column and row. As this is a triangle it
+ // must be true that column >= row.
+ bool bit(size_t column, size_t row) const {
+ ASSERT(column < columnCount());
+ ASSERT(row < column);
+ return mColumns[column][row];
+ }
+
+ // As bit, but uses max(x,y) as the column and min(x,y) as the row.
+ bool bitUnordered(size_t x, size_t y) const {
+ ASSERT(x < columnCount());
+ ASSERT(y < columnCount());
+ ASSERT(x != y);
+ if (x < y)
+ std::swap(x, y);
+ return bit(x, y);
+ }
+
+ // Returns the bit in the given column and row. As this is a triangle it
+ // must be true that column >= row.
+ void setBit(size_t column, size_t row, bool value) {
+ ASSERT(column < columnCount());
+ ASSERT(row < column);
+ mColumns[column][row] = value;
+ }
+
+ // As setBit, but uses max(x,y) as the column and min(x,y) as the row.
+ void setBitUnordered(size_t x, size_t y, bool value) {
+ ASSERT(x < columnCount());
+ ASSERT(y < columnCount());
+ ASSERT(x != y);
+ if (x < y)
+ std::swap(x, y);
+ setBit(x, y, value);
+ }
+
+ size_t getMemoryUse() const;
+
+private:
+ std::vector<std::vector<bool> > mColumns;
+};
+
+#endif
+
diff --git a/src/mathicgb/BjarkeGeobucket.cpp b/src/mathicgb/BjarkeGeobucket.cpp
new file mode 100644
index 0000000..7d89d79
--- /dev/null
+++ b/src/mathicgb/BjarkeGeobucket.cpp
@@ -0,0 +1,136 @@
+// Copyright 2011 Michael E. Stillman
+
+#include <iostream>
+#include "stdinc.h"
+#include "BjarkeGeobucket.hpp"
+
+extern int tracingLevel;
+
+BjarkeGeobucket::BjarkeGeobucket(const PolyRing *R0)
+ : Reducer(),
+ R_(R0),
+ H_(R0,10),
+ mNodeCount(0),
+ G_(Configuration(R0,4,1))
+{
+}
+
+BjarkeGeobucket::~BjarkeGeobucket()
+{
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+void BjarkeGeobucket::insertTail(const_term multiplier, const Poly *g1)
+{
+ if (g1->nTerms() <= 1) return;
+
+ ASSERT(mNodeCount == H_.getNodeCount());
+ HashPoly M;
+ H_.insert(multiplier, ++(g1->begin()), g1->end(), M);
+
+ if (!M.empty())
+ {
+ G_.push(M.begin(),M.end());
+ mNodeCount += M.size();
+ }
+
+ stats_n_inserts++;
+ stats_n_compares += G_.getConfiguration().getComparisons();
+ G_.getConfiguration().resetComparisons();
+
+ ASSERT(mNodeCount == H_.getNodeCount());
+}
+
+void BjarkeGeobucket::insert(monomial multiplier, const Poly *g1)
+{
+ HashPoly M;
+
+ ASSERT(mNodeCount == H_.getNodeCount());
+
+ H_.insert(multiplier, g1->begin(), g1->end(), M);
+
+ if (!M.empty())
+ {
+ G_.push(M.begin(),M.end());
+ mNodeCount += M.size();
+ }
+
+ stats_n_inserts++;
+ stats_n_compares += G_.getConfiguration().getComparisons();
+ G_.getConfiguration().resetComparisons();
+
+ ASSERT(mNodeCount == H_.getNodeCount());
+}
+
+bool BjarkeGeobucket::findLeadTerm(const_term &result)
+{
+ ASSERT(mNodeCount == H_.getNodeCount());
+ while (!G_.empty())
+ {
+ if (H_.popTerm(G_.top(), result.coeff, result.monom))
+ // returns true if G_.top() is not the zero element
+ return true;
+ G_.pop();
+ mNodeCount--;
+ }
+ return false;
+}
+
+void BjarkeGeobucket::removeLeadTerm()
+// returns true if there is a term to extract
+{
+ G_.pop();
+ mNodeCount--;
+
+ ASSERT(mNodeCount == H_.getNodeCount());
+}
+
+void BjarkeGeobucket::value(Poly &result)
+// keep extracting lead term until done
+{
+ const_term t;
+ while (findLeadTerm(t))
+ {
+ result.appendTerm(t.coeff, t.monom);
+ G_.pop();
+ mNodeCount--;
+ }
+
+ ASSERT(mNodeCount == H_.getNodeCount());
+ resetReducer();
+}
+
+void BjarkeGeobucket::resetReducer()
+{
+ ASSERT(mNodeCount == H_.getNodeCount());
+ const_term t;
+ while (findLeadTerm(t))
+ {
+ G_.pop();
+ mNodeCount--;
+ }
+ ASSERT(mNodeCount == H_.getNodeCount());
+ H_.reset();
+ ASSERT(mNodeCount == H_.getNodeCount());
+ // how to reset G_ ?
+}
+
+size_t BjarkeGeobucket::getMemoryUse() const
+{
+ size_t result = H_.getMemoryUse();
+ result += G_.getMemoryUse();
+ // std::cerr << "[reducer: " << H_.getMemoryUse() << " " << G_.getMemoryUse() << "]" << std::endl;
+ return result;
+}
+
+void BjarkeGeobucket::dump() const
+{
+ H_.dump(0);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/BjarkeGeobucket.hpp b/src/mathicgb/BjarkeGeobucket.hpp
new file mode 100755
index 0000000..a7c0104
--- /dev/null
+++ b/src/mathicgb/BjarkeGeobucket.hpp
@@ -0,0 +1,100 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _bjarkeGeoBucket_h_
+#define _bjarkeGeoBucket_h_
+
+#include <mathic.h>
+#include "Reducer.hpp"
+#include "PolyHashTable.hpp"
+
+template<
+ bool TrackFront,
+ bool MinBucketBinarySearch,
+ bool Deduplicate,
+ bool Premerge,
+ bool CollectMax,
+ int BucketStorage,
+ size_t InsertFactor = 1>
+class GeoConfiguration {
+public:
+ GeoConfiguration(
+ const PolyRing *R0,
+ size_t geoBase,
+ size_t minBucketSize):
+ R(R0), geoBase(geoBase), minBucketSize(minBucketSize), _comparisons(0) {}
+
+ const PolyRing *R;
+ size_t geoBase;
+ size_t minBucketSize;
+
+ typedef PolyHashTable::node * Entry;
+
+ typedef bool CompareResult;
+
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ ++_comparisons;
+ return R->monomialLT(a->monom, b->monom);
+ }
+ bool cmpLessThan(CompareResult r) const {return r;}
+
+ static const bool supportDeduplication = Deduplicate;
+ bool cmpEqual(CompareResult r) const {return r;} // NOT USED IN OUR CASE HERRE!
+ Entry deduplicate(const Entry& a, const Entry& /* b */) const {ASSERT(false); return a;}
+
+ size_t getComparisons() const {return _comparisons;}
+ void resetComparisons() const {_comparisons = 0;}
+
+ static const bool minBucketBinarySearch = MinBucketBinarySearch;
+ static const bool trackFront = TrackFront;
+ static const bool premerge = Premerge;
+ static const bool collectMax = CollectMax;
+ static const mic::GeobucketBucketStorage bucketStorage =
+ (mic::GeobucketBucketStorage)BucketStorage;
+ static const size_t insertFactor = InsertFactor;
+
+private:
+ mutable size_t _comparisons;
+};
+
+class BjarkeGeobucket : public Reducer {
+public:
+ BjarkeGeobucket(const PolyRing *R);
+ ~BjarkeGeobucket();
+
+ virtual std::string description() const { return "bjarke geo buckets"; }
+
+ void insertTail(const_term multiplier, const Poly *f);
+ void insert(monomial multiplier, const Poly *f);
+
+ bool findLeadTerm(const_term &result);
+ void removeLeadTerm();
+
+ void value(Poly &result); // keep extracting lead term until done
+
+ size_t getMemoryUse() const;
+
+ void dump() const; // Used for debugging
+
+protected:
+ void resetReducer();
+
+private:
+ void insert(const_term multiplier, Poly::iterator first, Poly::iterator last);
+
+ typedef PolyHashTable::MonomialArray HashPoly;
+
+ const PolyRing *R_;
+ PolyHashTable H_;
+
+ size_t mNodeCount; // number of (distinct) monomials in G_
+
+ typedef GeoConfiguration<true,true,false,false,false,1,1> Configuration;
+ mic::Geobucket< Configuration > G_;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/BuchbergerAlg.cpp b/src/mathicgb/BuchbergerAlg.cpp
new file mode 100644
index 0000000..c128161
--- /dev/null
+++ b/src/mathicgb/BuchbergerAlg.cpp
@@ -0,0 +1,426 @@
+#include "stdinc.h"
+#include "BuchbergerAlg.hpp"
+#include "Ideal.hpp"
+
+#include <iostream>
+
+extern int tracingLevel;
+
+BuchbergerAlg::BuchbergerAlg(
+ const Ideal& ideal,
+ FreeModuleOrderType orderType,
+ Reducer::ReducerType reducerType,
+ int divisorLookupType,
+ bool preferSparseReducers,
+ size_t queueType
+):
+ mBreakAfter(0),
+ mPrintInterval(0),
+ mUseAutoTopReduction(true),
+ mUseAutoTailReduction(false),
+ mRing(*ideal.getPolyRing()),
+ mOrder(FreeModuleOrder::makeOrder(orderType, &ideal)),
+ mReducer(Reducer::makeReducer(reducerType, *ideal.getPolyRing())),
+ mBasis(mRing, *mOrder, DivisorLookup::makeFactory(
+ *ideal.getPolyRing(),
+ divisorLookupType)->create(preferSparseReducers, true)
+ ),
+ mSPairs(mBasis, queueType),
+ mSPolyReductionCount(0)
+{
+ // Reduce and insert the generators of the ideal into the starting basis
+ size_t const idealSize = ideal.size();
+ for (size_t gen = 0; gen != idealSize; ++gen)
+ insertReducedPoly(mReducer->classicReduce(*ideal.getPoly(gen), mBasis));
+}
+
+void BuchbergerAlg::insertReducedPoly(
+ std::auto_ptr<Poly> polyToInsert
+) {
+ ASSERT(polyToInsert.get() != 0);
+ if (polyToInsert->isZero())
+ return;
+ ASSERT(mBasis.divisor(polyToInsert->getLeadMonomial()) ==
+ static_cast<size_t>(-1));
+
+ if (tracingLevel > 20) {
+ std::cerr << "inserting basis element " << mBasis.size() << ": ";
+ if (tracingLevel > 100) {
+ polyToInsert->display(std::cerr);
+ std::cerr << std::endl;
+ } else {
+ mRing.printMonomialFrobbyM2Format
+ (std::cerr, polyToInsert->getLeadMonomial());
+ if (polyToInsert->nTerms() > 1)
+ std::cerr << " + [...]";
+ std::cerr << std::endl;
+ }
+ }
+
+ if (!mUseAutoTopReduction) {
+ size_t const newGen = mBasis.size();
+ mBasis.insert(polyToInsert);
+ mSPairs.addPairs(newGen);
+ return;
+ }
+
+ std::vector<size_t> toRetireAndReduce;
+ std::vector<Poly*> toReduce;
+
+ try {
+ do {
+ // reduce polynomial and insert into basis
+ {
+ std::auto_ptr<Poly> reduced;
+ if (toReduce.empty()) // if first iteration
+ reduced = polyToInsert;
+ else {
+ reduced = mReducer->classicReduce(*toReduce.back(), mBasis);
+ if (tracingLevel > 20) {
+ if (reduced->isZero()) {
+ std::cerr << "auto-top-reduce cascade: "
+ "basis element reduced to zero."
+ << std::endl;
+ } else {
+ std::cerr << "auto-top-reduce cascade: "
+ "inserting reduced poly with lead term "
+ << std::endl;
+ mRing.printMonomialFrobbyM2Format
+ (std::cerr, reduced->getLeadMonomial());
+ std::cerr << '\n';
+ }
+ }
+ delete toReduce.back();
+ toReduce.pop_back();
+ }
+ if (reduced->isZero())
+ continue;
+ reduced->makeMonic();
+ mBasis.insert(reduced);
+ }
+
+ // form S-pairs and retire basis elements that become top reducible.
+ const size_t newGen = mBasis.size() - 1;
+ mSPairs.addPairsAssumeAutoReduce(newGen, toRetireAndReduce);
+ for (std::vector<size_t>::const_iterator it = toRetireAndReduce.begin();
+ it != toRetireAndReduce.end(); ++it) {
+ toReduce.push_back(0); // allocate space in vector before .release()
+ toReduce.back() = mBasis.retire(*it).release();
+ }
+ toRetireAndReduce.clear();
+ } while (!toReduce.empty());
+ } catch (...) {
+ for (std::vector<Poly*>::iterator it = toReduce.begin();
+ it != toReduce.end(); ++it)
+ delete *it;
+ throw;
+ }
+ ASSERT(toReduce.empty());
+ ASSERT(toRetireAndReduce.empty());
+}
+
+void BuchbergerAlg::computeGrobnerBasis() {
+ size_t counter = 0;
+ mTimer.reset();
+ mRing.resetCoefficientStats();
+
+ if (mUseAutoTailReduction)
+ autoTailReduce();
+
+ while (!mSPairs.empty()) {
+ step();
+ if (mBreakAfter != 0 && mBasis.size() > mBreakAfter) {
+ std::cerr
+ << "Stopping Grobner basis computation due to reaching limit of "
+ << mBreakAfter << " basis elements." << std::endl;
+ break;
+ }
+ if (mPrintInterval != 0 && (++counter % mPrintInterval) == 0)
+ printStats(std::cerr);
+ }
+ //printStats(std::cerr);
+ //mReducer->dump();
+}
+
+void BuchbergerAlg::step() {
+ ASSERT(!mSPairs.empty());
+ if (tracingLevel > 30)
+ std::cerr << "Determining next S-pair" << std::endl;
+ std::pair<size_t, size_t> p = mSPairs.pop();
+ if (p.first == static_cast<size_t>(-1)) {
+ ASSERT(p.second == static_cast<size_t>(-1));
+ return; // no more S-pairs
+ }
+ ASSERT(p.first != static_cast<size_t>(-1));
+ ASSERT(p.second != static_cast<size_t>(-1));
+ ASSERT(!mBasis.retired(p.first));
+ ASSERT(!mBasis.retired(p.second));
+
+ if (tracingLevel > 20) {
+ std::cerr << "Reducing S-pair ("
+ << p.first << ", "
+ << p.second << ")" << std::endl;
+ }
+ std::auto_ptr<Poly> reduced(mReducer->classicReduceSPoly
+ (mBasis.poly(p.first), mBasis.poly(p.second), mBasis));
+ if (!reduced->isZero()) {
+ insertReducedPoly(reduced);
+ if (mUseAutoTailReduction)
+ autoTailReduce();
+ }
+}
+
+void BuchbergerAlg::autoTailReduce() {
+ ASSERT(mUseAutoTailReduction);
+
+ for (size_t i = 0; i < mBasis.size(); ++i) {
+ if (mBasis.retired(i))
+ continue;
+ if (mBasis.usedAsReducerCount(i) < 1000)
+ continue;
+ mBasis.replaceSameLeadTerm
+ (i, mReducer->classicTailReduce(mBasis.poly(i), mBasis));
+ }
+}
+
+size_t BuchbergerAlg::getMemoryUse() const {
+ return
+ mBasis.getMemoryUse() +
+ mRing.getMonomialPool().getMemoryUse() +
+ mReducer->getMemoryUse() +
+ mSPairs.getMemoryUse();
+}
+
+void BuchbergerAlg::printStats(std::ostream& out) const {
+ out << " term order: " << mBasis.order().description() << '\n';
+ out << " reduction type: " << mReducer->description() << '\n';
+ out << " divisor tab type: " << mBasis.divisorLookup().getName() << '\n';
+ out << " S-pair queue type: " << mSPairs.name() << '\n';
+ out << " total compute time: " << mTimer.getMilliseconds()/1000.0 << " seconds " << '\n';
+
+ const PolyRing::coefficientStats& cstats = mRing.getCoefficientStats();
+ out << "n-coeff-add: " << cstats.n_add << '\n';
+ out << "n-coeff-addmult: " << cstats.n_addmult << '\n';
+ out << "n-coeff-mult: " << cstats.n_mult << '\n';
+ out << "n-coeff-recip: " << cstats.n_recip << '\n';
+ out << "n-coeff-divide: " << cstats.n_divide << '\n';
+
+ mic::ColumnPrinter pr;
+ pr.addColumn(true, " ");
+ pr.addColumn(false, " ");
+ pr.addColumn(true, " ");
+
+ std::ostream& name = pr[0];
+ std::ostream& value = pr[1];
+ std::ostream& extra = pr[2];
+
+ const size_t basisSize = mBasis.size();
+ const double mseconds = mTimer.getMilliseconds();
+ const size_t pending = mSPairs.size();
+
+ name << "Time spent:\n";
+ value << mTimer << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(mseconds / basisSize)
+ << " ms per basis element\n";
+
+ const double pendingRatio = static_cast<double>(pending) / basisSize;
+ name << "Basis elements:\n";
+ value << mic::ColumnPrinter::commafy(basisSize) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(pendingRatio)
+ << " Sp pend per basis ele\n";
+
+ const size_t basisTermCount = mBasis.monomialCount();
+ name << "Terms for basis:\n";
+ value << mic::ColumnPrinter::commafy(basisTermCount) << '\n';
+ extra << mic::ColumnPrinter::ratio(basisTermCount, basisSize)
+ << " terms per basis ele\n";
+
+ const size_t minLeadCount = mBasis.minimalLeadCount();
+ name << "Minimum lead terms:\n";
+ value << mic::ColumnPrinter::commafy(minLeadCount) << '\n';
+ extra << mic::ColumnPrinter::percent(minLeadCount, basisSize)
+ << " basis ele have min lead\n";
+
+ const size_t lastMinLead = mBasis.maxIndexMinimalLead() + 1;
+ const size_t timeSinceLastMinLead = basisSize - lastMinLead;
+ name << "Index of last min lead:\n";
+ value << mic::ColumnPrinter::commafy(lastMinLead) << '\n';
+ extra << mic::ColumnPrinter::percent(timeSinceLastMinLead, basisSize)
+ << " of basis added since then\n";
+
+ const unsigned long long considered =
+ mBasis.size() * (mBasis.size() - 1) / 2;
+ name << "S-pairs considered:\n";
+ value << mic::ColumnPrinter::commafy(considered) << '\n';
+ extra << '\n';
+
+ name << "S-pairs pending:\n";
+ value << mic::ColumnPrinter::commafy(pending) << '\n';
+ extra << mic::ColumnPrinter::percent(pending, considered)
+ << " of considered\n";
+
+ unsigned long long const reductions = sPolyReductionCount();
+ name << "S-pairs reduced:\n";
+ value << mic::ColumnPrinter::commafy(reductions) << '\n';
+ extra << '\n';
+
+ Reducer::Stats reducerStats = mReducer->sigStats();
+ SPairs::Stats sPairStats = mSPairs.stats();
+
+ unsigned long long const primeElim = sPairStats.relativelyPrimeHits;
+ name << "Rel.prime sp eliminated:\n";
+ value << mic::ColumnPrinter::commafy(primeElim) << '\n';
+ extra << mic::ColumnPrinter::percent(primeElim, reductions)
+ << " of late eliminations\n";
+
+ const unsigned long long singularReductions =
+ reducerStats.singularReductions;
+ name << "Singular reductions:\n";
+ value << mic::ColumnPrinter::commafy(singularReductions) << '\n';
+ extra << mic::ColumnPrinter::percent(singularReductions, reductions)
+ << " of reductions\n";
+
+ const unsigned long long zeroReductions = reducerStats.zeroReductions;
+ name << "Reductions to zero:\n";
+ value << mic::ColumnPrinter::commafy(zeroReductions) << '\n';
+ extra << mic::ColumnPrinter::percent(zeroReductions, reductions)
+ << " of reductions\n";
+
+ const unsigned long long newReductions =
+ reductions - singularReductions - zeroReductions;
+ name << "Reductions to new ele:\n";
+ value << mic::ColumnPrinter::commafy(newReductions) << '\n';
+ extra << mic::ColumnPrinter::percent(newReductions, reductions)
+ << " of reductions\n";
+
+ const unsigned long long redSteps = reducerStats.steps;
+ const double stepsRatio =
+ static_cast<double>(redSteps) / (reductions - singularReductions);
+ name << "Sig reduction steps:\n";
+ value << mic::ColumnPrinter::commafy(redSteps) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(stepsRatio)
+ << " steps per non-sing reduction\n";
+
+ const unsigned long long longestReduction = reducerStats.maxSteps;
+ name << "Longest sig reduction:\n";
+ value << mic::ColumnPrinter::commafy(longestReduction) << '\n';
+ extra << '\n';
+
+ Reducer::Stats classicRedStats = mReducer->classicStats();
+ const unsigned long long clReductions = classicRedStats.reductions;
+ name << "Classic reductions:\n";
+ value << mic::ColumnPrinter::commafy(clReductions) << '\n';
+ extra << '\n';
+
+ const unsigned long long clRedSteps = classicRedStats.steps;
+ const double clStepsRatio = static_cast<double>(clRedSteps) / clReductions;
+ name << "Classic reduction steps:\n";
+ value << mic::ColumnPrinter::commafy(clRedSteps) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(clStepsRatio)
+ << " steps per reduction\n";
+
+ const unsigned long long clLongestReduction = classicRedStats.maxSteps;
+ name << "Longest classic red:\n";
+ value << mic::ColumnPrinter::commafy(clLongestReduction) << '\n';
+ extra << '\n';
+
+ //SPairs::Stats sPairStats = mSPairs.stats();
+ unsigned long long marginal = sPairStats.sPairsConsidered;
+
+ unsigned long long const primeHits = sPairStats.relativelyPrimeHits;
+ name << "Buchb relatively prime:\n";
+ value << mic::ColumnPrinter::commafy(primeHits) << '\n';
+ extra << mic::ColumnPrinter::percent(primeHits, marginal) <<
+ " of S-pairs\n";
+ marginal -= primeHits;
+
+ unsigned long long const simpleHits = sPairStats.buchbergerLcmSimpleHits;
+ name << "Buchb lcm simple hits:\n";
+ value << mic::ColumnPrinter::commafy(simpleHits) << '\n';
+ extra << mic::ColumnPrinter::percent(simpleHits, marginal) <<
+ " of remaining S-pairs\n";
+ marginal -= simpleHits;
+
+ unsigned long long const simpleHitsLate = sPairStats.buchbergerLcmSimpleHitsLate;
+ name << "Buchb late lcm simple hits:\n";
+ value << mic::ColumnPrinter::commafy(simpleHitsLate) << '\n';
+ extra << mic::ColumnPrinter::percent(simpleHitsLate, marginal) <<
+ " of remaining S-pairs\n";
+ marginal -= simpleHitsLate;
+
+ unsigned long long const buchAdvHits =
+ sPairStats.buchbergerLcmAdvancedHits;
+ name << "Buchb lcm adv hits:\n";
+ value << mic::ColumnPrinter::commafy(buchAdvHits) << '\n';
+ extra << mic::ColumnPrinter::percent(buchAdvHits, marginal) <<
+ " of remaining S-pairs\n";
+
+ const unsigned long long buchCache = sPairStats.buchbergerLcmCacheHits;
+ name << "Buchb lcm cache hits:\n";
+ value << mic::ColumnPrinter::commafy(buchCache) << '\n';
+ extra << mic::ColumnPrinter::percent(buchCache, simpleHits) <<
+ " of simple hits\n";
+
+ const unsigned long long buchCacheLate = sPairStats.buchbergerLcmCacheHitsLate;
+ name << "Buchb late lcm cache hits:\n";
+ value << mic::ColumnPrinter::commafy(buchCacheLate) << '\n';
+ extra << mic::ColumnPrinter::percent(buchCacheLate, simpleHits) <<
+ " of simple hits\n";
+
+ out << "***** Classic Buchberger algorithm statistics *****\n"
+ << pr << std::flush;
+}
+
+void BuchbergerAlg::printMemoryUse(std::ostream& out) const
+{
+ // Set up printer
+ mic::ColumnPrinter pr;
+ pr.addColumn();
+ pr.addColumn(false);
+ pr.addColumn(false);
+
+ std::ostream& name = pr[0];
+ std::ostream& value = pr[1];
+ std::ostream& extra = pr[2];
+
+ const size_t total = getMemoryUse();
+ { // Grobner basis
+ const size_t basisMem = mBasis.getMemoryUse();
+ name << "Grobner basis:\n";
+ value << mic::ColumnPrinter::bytesInUnit(basisMem) << '\n';
+ extra << mic::ColumnPrinter::percent(basisMem, total) << '\n';
+ }
+ { // Spairs
+ const size_t sPairMem = mSPairs.getMemoryUse();
+ name << "S-pairs:\n";
+ value << mic::ColumnPrinter::bytesInUnit(sPairMem) << '\n';
+ extra << mic::ColumnPrinter::percent(sPairMem, total) << '\n';
+ }
+ { // Reducer
+ const size_t reducerMem = mReducer->getMemoryUse();
+ name << "Reducer:\n";
+ value << mic::ColumnPrinter::bytesInUnit(reducerMem) << '\n';
+ extra << mic::ColumnPrinter::percent(reducerMem, total) << '\n';
+ }
+ { // Signatures
+ const size_t sigMem = mRing.getMonomialPool().getMemoryUse();
+ name << "Monomials:\n";
+ value << mic::ColumnPrinter::bytesInUnit(sigMem) << '\n';
+ extra << mic::ColumnPrinter::percent(sigMem, total) << '\n';
+ }
+ // total
+ name << "-------------\n";
+ value << '\n';
+ extra << '\n';
+
+ name << "Memory used in total:\n";
+ value << mic::ColumnPrinter::bytesInUnit(total) << "\n";
+ extra << "\n";
+
+ out << "*** Memory use by component ***\n" << pr << std::flush;
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/BuchbergerAlg.hpp b/src/mathicgb/BuchbergerAlg.hpp
new file mode 100644
index 0000000..5b02ab6
--- /dev/null
+++ b/src/mathicgb/BuchbergerAlg.hpp
@@ -0,0 +1,78 @@
+#ifndef _buchberger_alg_
+#define _bucbberger_alg_
+
+#include "Reducer.hpp"
+#include "FreeModuleOrder.hpp"
+#include "SPairs.hpp"
+#include "PolyBasis.hpp"
+#include <mathic.h>
+#include <memory>
+#include <ostream>
+
+// Calculates a non-signature Grobner basis using Buchberger's algorithm.
+class BuchbergerAlg {
+public:
+ BuchbergerAlg(
+ const Ideal& ideal,
+ FreeModuleOrderType orderType,
+ Reducer::ReducerType reducerType,
+ int divisorLookupType,
+ bool preferSparseReducers,
+ size_t queueType);
+
+ // Replaces the current basis with a Grobner basis of the same ideal.
+ void computeGrobnerBasis();
+
+ // How many S-pairs were not eliminated before reduction of the
+ // corresponding S-polynomial.
+ unsigned long long sPolyReductionCount() const {return mSPolyReductionCount;}
+
+ // Returns the current basis.
+ PolyBasis& basis() {return mBasis;}
+
+ // Shows statistics on what the algorithm has done.
+ void printStats(std::ostream& out) const;
+
+ void printMemoryUse(std::ostream& out) const;
+
+ size_t getMemoryUse() const;
+
+ void setBreakAfter(unsigned int elements) {
+ mBreakAfter = elements;
+ }
+
+ void setPrintInterval(unsigned int reductions) {
+ mPrintInterval = reductions;
+ }
+
+ void setUseAutoTopReduction(bool value) {
+ mUseAutoTopReduction = value;
+ }
+
+ void setUseAutoTailReduction(bool value) {
+ mUseAutoTailReduction = value;
+ }
+
+private:
+ unsigned int mBreakAfter;
+ unsigned int mPrintInterval;
+ bool mUseAutoTopReduction;
+ bool mUseAutoTailReduction;
+
+ // Perform a step of the algorithm.
+ void step();
+
+ void autoTailReduce();
+
+ void insertReducedPoly(std::auto_ptr<Poly> poly);
+
+ const PolyRing& mRing;
+ std::auto_ptr<FreeModuleOrder> mOrder;
+ std::auto_ptr<Reducer> mReducer;
+ PolyBasis mBasis;
+ SPairs mSPairs;
+ mic::Timer mTimer;
+ unsigned long long mSPolyReductionCount;
+};
+
+#endif
diff --git a/src/mathicgb/ChainedHashTable.cpp b/src/mathicgb/ChainedHashTable.cpp
new file mode 100644
index 0000000..a68487a
--- /dev/null
+++ b/src/mathicgb/ChainedHashTable.cpp
@@ -0,0 +1,11 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "ChainedHashTable.hpp"
+
+template class ChainedHashTable<HashControlExample>;
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ChainedHashTable.hpp b/src/mathicgb/ChainedHashTable.hpp
new file mode 100644
index 0000000..7e87c15
--- /dev/null
+++ b/src/mathicgb/ChainedHashTable.hpp
@@ -0,0 +1,290 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _chainedHashTable_h_
+#define _chainedHashTable_h_
+
+#include <vector>
+#include <iostream>
+
+#include <memtailor.h>
+
+class ostream;
+
+// One template parameter, with the following:
+// types:
+// ValueType, KeyType
+// functions:
+// size_t HashControl::hash_value(key)
+// bool ::is_equal(key1, key2)
+// void ValueType::combine(value &already_there, value new_one);
+// void ValueType::show(ostream &o, KeyType k, ValueType v)
+//
+// keys and values: are not copied or stored, except via standard operator=.
+
+class HashControlExample
+{
+public:
+ typedef int * KeyType;
+ typedef int ValueType;
+
+ size_t hash_value(KeyType k) const { return static_cast<size_t>(k - static_cast<int *>(0)); }
+ bool is_equal(KeyType k1, KeyType k2) const { return k1 == k2; }
+ void combine(ValueType &v, ValueType w) const { v += w; }
+ void show(std::ostream &o, KeyType k, ValueType v) const { o << "[" << k << " " << v << "]"; }
+};
+
+template<typename HashControl>
+class ChainedHashTable {
+ typedef typename HashControl::KeyType Key;
+ typedef typename HashControl::ValueType Value;
+
+ struct node {
+ node *next;
+ Key key;
+ Value value;
+ void *unused;
+ };
+
+ typedef std::vector<node *> NodeArray;
+public:
+ struct Stats {
+ size_t max_chain_len;
+ size_t n_nonempty_bins;
+ size_t n_inserts; // # calls to insert a monomial
+ size_t n_nodes; // # of unique monomials represented here
+ size_t n_eq_true; // total number of true is_equal calls during lookup_and_insert
+ size_t n_eq_false; // same, but number for false.
+ };
+
+ ChainedHashTable(HashControl M, int nbits);
+
+ ~ChainedHashTable() {}
+
+ std::string description() const { return "chained hash table"; }
+
+ void reset(); // Clear the table, and memory areas, but don't release space grabbed so far
+
+ void resize(int new_nbits); // Don't change the nodes, table, but do recreate hashtable_
+
+ bool member(Key k, Value &result_v) const;
+ // Return true if k is in the hash table.
+ // In this case, set 'result_v' with its value
+
+ void insertUnique(Key m, Value v);
+ // The caller must insure that 'm' is not currently in the table.
+
+ bool lookupAndInsert(Key k, Value v);
+ // returns true if 'k' is in the table. In this case, combine values.
+ // If false, 'k' is inserted with the given value.
+
+ void getStats(Stats &stats) const; // set the stats table with current values
+
+ void resetStats() const; // reset all stat values to 0
+
+ void dump(int level = 0) const; // For debugging: display the current state of the table
+
+ size_t getMemoryUse() const;
+private:
+ node * lookup(Key k) const;
+
+ HashControl M_;
+ size_t table_size_;
+ mutable Stats stats_;
+
+ std::vector<node *> hashtable_;
+ memt::Arena mNodes; // where we keep 'node's
+};
+
+template <typename HashControl>
+ChainedHashTable<HashControl>::ChainedHashTable(HashControl M, int nbits)
+ : M_(M),
+ table_size_(1 << nbits)
+{
+ hashtable_.resize(table_size_);
+
+ // set each entry of hashtable_ to null
+ reset();
+}
+
+template <typename HashControl>
+size_t ChainedHashTable<HashControl>::getMemoryUse() const
+{
+ size_t total = mNodes.getMemoryUse();
+ total += sizeof(node *) * hashtable_.size();
+ return total;
+}
+
+template <typename HashControl>
+void ChainedHashTable<HashControl>::reset()
+{
+ // Clear the table, and memory areas.
+ for (typename NodeArray::iterator i = hashtable_.begin(); i != hashtable_.end(); ++i)
+ *i = 0;
+
+ mNodes.freeAllAllocs();
+ resetStats();
+}
+
+template <typename HashControl>
+void ChainedHashTable<HashControl>::resize(int new_nbits)
+// Don't change the nodes, table, but do recreate hashtable_
+{
+ // Make a new vector of node *'s.
+ // swap the two.
+ // Loop through each one, reinserting the node into the proper bin.
+
+ size_t old_table_size = table_size_;
+ table_size_ = static_cast<size_t>(1) << new_nbits;
+ NodeArray old_table(table_size_);
+ swap(old_table, hashtable_);
+ for (size_t i = 0; i<old_table_size; i++)
+ {
+ node *p = old_table[i];
+ while (p != 0)
+ {
+ node *q = p;
+ p = p->next;
+ // Reinsert node. We know that it is unique
+ size_t hashval = M_.hash_value(q->key) % table_size_;
+ node *r = hashtable_[hashval];
+ q->next = r;
+ hashtable_[hashval] = q;
+ }
+ }
+}
+
+template <typename HashControl>
+bool ChainedHashTable<HashControl>::member(Key m, Value &val) const
+{
+ node *p = lookup(m);
+ if (p == 0) return false;
+ val = p->value; // ASSIGN
+ return true;
+}
+
+template <typename HashControl>
+bool ChainedHashTable<HashControl>::lookupAndInsert(Key m, Value val)
+// Returns true if m is in the table
+{
+ node *p = lookup(m);
+ if (p == 0)
+ {
+ insertUnique(m, val);
+ return false;
+ }
+ else
+ {
+ M_.combine(p->value, val);
+ return true;
+ }
+}
+
+template <typename HashControl>
+void ChainedHashTable<HashControl>::insertUnique(Key k, Value v)
+// Returns null if not in the table
+{
+ size_t hashval = M_.hash_value(k) % table_size_; // table_size is a power of 2
+
+ node *q = static_cast<node *>(mNodes.alloc(sizeof(node)));
+ q->next = hashtable_[hashval];
+ q->key = k; // ASSIGN
+ q->value = v; // ASSIGN
+ hashtable_[hashval] = q;
+
+ stats_.n_inserts++;
+}
+
+template <typename HashControl>
+typename ChainedHashTable<HashControl>::node * ChainedHashTable<HashControl>::lookup(Key k) const
+// Returns null if not in the table
+{
+ size_t hashval = M_.hash_value(k) % table_size_; // table_size is a power of 2
+ for (node *p=hashtable_[hashval]; p != 0; p = p->next)
+ if (M_.is_equal(k, p->key))
+ {
+ stats_.n_eq_true++;
+ return p;
+ }
+ else
+ {
+ stats_.n_eq_false++;
+ }
+ return 0;
+}
+
+template <typename HashControl>
+void ChainedHashTable<HashControl>::resetStats() const
+{
+ stats_.max_chain_len = 0;
+ stats_.n_nonempty_bins = 0;
+ stats_.n_inserts = 0;
+ stats_.n_nodes = 0;
+ stats_.n_eq_true = 0;
+ stats_.n_eq_false = 0;
+}
+
+template <typename HashControl>
+void ChainedHashTable<HashControl>::getStats(Stats &stats) const
+{
+ // First we set the values in stats_
+
+ stats_.n_nonempty_bins = 0;
+ stats_.max_chain_len = 0;
+ stats_.n_nodes = 0;
+ for (size_t i = 0; i<table_size_; i++)
+ {
+ if (hashtable_[i] == 0) continue;
+ stats_.n_nonempty_bins++;
+ size_t chain_len = 0;
+ for (node *p = hashtable_[i]; p != 0; p = p->next)
+ chain_len++;
+ stats_.n_nodes += chain_len;
+ if (chain_len > stats_.max_chain_len)
+ stats_.max_chain_len = chain_len;
+ }
+
+ if (&stats != &stats_)
+ stats = stats_;
+}
+
+template <typename HashControl>
+void ChainedHashTable<HashControl>::dump(int level) const
+{
+ // Compute:
+ // # bins in use
+ // max chain length
+ // # keys in use
+ // average number in non-zero bins
+ // Report on:
+ // number of is_equal true/false
+ // # of lookup and insert calls
+ //
+ // For debugging: display the current state of the table
+ getStats(stats_);
+ std::cout << "ChainedHashTable stats:" << std::endl;
+ std::cout << " # nonempty bins: " << stats_.n_nonempty_bins << std::endl;
+ std::cout << " # nodes: " << stats_.n_nodes << std::endl;
+ std::cout << " max chain length: " << stats_.max_chain_len << std::endl;
+ std::cout << " # insert calls: " << stats_.n_inserts << std::endl;
+ std::cout << " # isequal true calls: " << stats_.n_eq_true << std::endl;
+ std::cout << " # isequal false calls: " << stats_.n_eq_false << std::endl;
+
+ if (level == 0) return;
+
+ for (size_t i = 0; i<table_size_; i++)
+ {
+ if (hashtable_[i] == 0) continue;
+ std::cout << "bin " << i << ": ";
+ for (node *p = hashtable_[i]; p != 0; p = p->next)
+ M_.show(std::cout, p->key, p->value);
+ std::cout << std::endl;
+ }
+}
+
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/DivLookup.cpp b/src/mathicgb/DivLookup.cpp
new file mode 100644
index 0000000..3b775de
--- /dev/null
+++ b/src/mathicgb/DivLookup.cpp
@@ -0,0 +1,7 @@
+#include "stdinc.h"
+#include "DivLookup.hpp"
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/DivLookup.hpp b/src/mathicgb/DivLookup.hpp
new file mode 100644
index 0000000..30de634
--- /dev/null
+++ b/src/mathicgb/DivLookup.hpp
@@ -0,0 +1,630 @@
+#ifndef __div_lookup_guard_
+#define __div_lookup_guard_
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "GroebnerBasis.hpp"
+#include "DivisorLookup.hpp"
+#include "PolyRing.hpp"
+#include "FreeModuleOrder.hpp"
+
+/** Configuration class for interface to KDTree, DivList */
+/* As such, it has entries that both will expect */
+/* It also contains enough for the Naive monomial table use */
+
+extern int tracingLevel;
+
+template<bool AllowRemovals, bool UseDivMask>
+class DivLookupConfiguration;
+
+template<bool AR, bool DM>
+class DivLookupConfiguration {
+
+public:
+ DivLookupConfiguration(
+ const PolyRing& ring,
+ bool minimizeOnInsert,
+ bool sortOnInsert,
+ bool useDivisorCache,
+ double rebuildRatio,
+ size_t minRebuild,
+ int type,
+ bool preferSparseReducers
+ ):
+ mBasis(0),
+ mSigBasis(0),
+ mRing(&ring),
+ _varCount(ring.getNumVars()),
+ _minimize_on_insert(minimizeOnInsert),
+ _sortOnInsert(sortOnInsert),
+ _useDivisorCache(useDivisorCache),
+ _useAutomaticRebuild((rebuildRatio > 0.0 || minRebuild > 0) && UseDivMask),
+ _rebuildRatio(rebuildRatio),
+ _minRebuild(minRebuild),
+ _expQueryCount(0),
+ _type(type),
+ _preferSparseReducers(preferSparseReducers)
+ {
+ ASSERT(rebuildRatio >= 0);
+ }
+
+ void setBasis(const PolyBasis& basis) {
+ if (mBasis == &basis)
+ return;
+ ASSERT(mBasis == 0);
+ ASSERT(mRing == &basis.ring());
+ mBasis = &basis;
+ }
+
+ void setSigBasis(const GroebnerBasis& sigBasis) {
+ if (mSigBasis == &sigBasis)
+ return;
+ ASSERT(mSigBasis == 0);
+ ASSERT(mBasis == 0 || mBasis == &sigBasis.basis());
+ ASSERT(mRing == &sigBasis.basis().ring());
+ mSigBasis = &sigBasis;
+ setBasis(sigBasis.basis());
+ }
+
+ //////////////////////////
+ // Functions and types required by KDTree, or by DivList, or by ...
+ //////////////////////////
+
+ typedef int Exponent;
+ typedef const_monomial Monomial;
+ struct Entry {
+ const_monomial monom;
+ size_t index;
+
+ Entry(): monom(0), index(static_cast<size_t>(-1)) {}
+ Entry(const_monomial monom0, size_t index0):
+ monom(monom0), index(index0) {}
+ };
+
+ Exponent getExponent(const Monomial& m, size_t var) const
+ {
+ ++_expQueryCount;
+ return mRing->monomialExponent(m, var);
+ }
+ Exponent getExponent(const Entry& e, size_t var) const
+ {
+ ++_expQueryCount;
+ return mRing->monomialExponent(e.monom, var);
+ }
+
+ bool divides(const Monomial& a, const Monomial& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var)
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ return true;
+ }
+
+ bool divides(const Entry& a, const Monomial& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var)
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ return true;
+ }
+
+ bool divides(const Monomial& a, const Entry& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var)
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ return true;
+ }
+
+ bool divides(const Entry& a, const Entry& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var)
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ return true;
+ }
+
+ bool isLessThan(const Monomial& a, const Monomial& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var) {
+ if (getExponent(a, var) < getExponent(b, var))
+ return true;
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ }
+ return false;
+ }
+
+ bool isLessThan(const Entry& a, const Monomial& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var) {
+ if (getExponent(a, var) < getExponent(b, var))
+ return true;
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ }
+ return false;
+ }
+
+ bool isLessThan(const Monomial& a, const Entry& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var) {
+ if (getExponent(a, var) < getExponent(b, var))
+ return true;
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ }
+ return false;
+ }
+
+ bool isLessThan(const Entry& a, const Entry& b) const
+ {
+ for (size_t var = 0; var < getVarCount(); ++var) {
+ if (getExponent(a, var) < getExponent(b, var))
+ return true;
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ }
+ return false;
+ }
+
+ size_t getVarCount() const {return _varCount;}
+
+ static const bool UseTreeDivMask = DM;
+ static const bool UseLinkedList = false;
+ static const bool UseDivMask = DM;
+ static const size_t LeafSize = 1;
+ static const bool PackedTree = true;
+ static const bool AllowRemovals = AR;
+
+ bool getSortOnInsert() const {return _sortOnInsert;}
+ bool getUseDivisorCache() const {return _useDivisorCache;}
+ bool getMinimizeOnInsert() const {return _minimize_on_insert;}
+
+ bool getDoAutomaticRebuilds() const {return _useAutomaticRebuild;}
+ double getRebuildRatio() const {return _rebuildRatio;}
+ size_t getRebuildMin() const {return _minRebuild;}
+
+///////////////////////////
+// Stats gathering ////////
+///////////////////////////
+public:
+ struct Stats {
+ size_t n_member;
+ size_t n_inserts; // includes koszuls
+ size_t n_insert_already_there;
+ size_t n_compares;
+ unsigned long long n_expQuery;
+
+ Stats():
+ n_member(0),
+ n_inserts(0),
+ n_insert_already_there(0),
+ n_compares(0),
+ n_expQuery(0)
+ {}
+
+ };
+
+ void displayStats(std::ostream &o) const;
+
+///////////////////////////
+ const GroebnerBasis* basis() const {return mSigBasis;}
+ const PolyRing* getPolyRing() const {return mRing;}
+ unsigned long long getExpQueryCount() const {return _expQueryCount;}
+ int type() const {return _type;}
+ bool preferSparseReducers() const {return _preferSparseReducers;}
+
+private:
+ PolyBasis const* mBasis;
+ GroebnerBasis const* mSigBasis;
+ const PolyRing* mRing;
+ const size_t _varCount;
+ const bool _minimize_on_insert;
+ const bool _sortOnInsert;
+ const bool _useDivisorCache;
+ const bool _useAutomaticRebuild;
+ const double _rebuildRatio;
+ const size_t _minRebuild;
+ mutable unsigned long long _expQueryCount;
+ mutable Stats _stats;
+ const int _type;
+ bool const _preferSparseReducers;
+};
+
+template<class Finder>
+class DivLookup : public DivisorLookup {
+ public:
+ typedef typename Finder::Monomial Monomial;
+ typedef typename Finder::Entry Entry;
+ typedef typename Finder::Configuration Configuration;
+ typedef Configuration C;
+
+ DivLookup(const Configuration &C) :
+ _finder(C)
+ {
+ ASSERT(!C.UseTreeDivMask || C.UseDivMask);
+ }
+
+ ~DivLookup() {}
+
+ virtual void setBasis(const PolyBasis& basis) {
+ _finder.getConfiguration().setBasis(basis);
+ }
+
+ virtual void setSigBasis(const GroebnerBasis& sigBasis) {
+ _finder.getConfiguration().setSigBasis(sigBasis);
+ }
+
+
+ virtual int type() const {return _finder.getConfiguration().type();}
+
+ virtual void lowBaseDivisors(
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator
+ ) const {
+ const GroebnerBasis* GB = _finder.getConfiguration().basis();
+
+ const_monomial sigNew = GB->getSignature(newGenerator);
+
+ ASSERT(newGenerator < GB->size());
+ LowBaseDivisor searchObject(*GB, divisors, maxDivisors, newGenerator);
+ _finder.findAllDivisors(sigNew, searchObject);
+ }
+
+ virtual size_t highBaseDivisor(size_t newGenerator) const {
+ const GroebnerBasis* basis = _finder.getConfiguration().basis();
+ ASSERT(newGenerator < basis->size());
+
+ HighBaseDivisor searchObject(*basis, newGenerator);
+ _finder.findAllDivisors
+ (basis->getLeadMonomial(newGenerator), searchObject);
+ return searchObject.highDivisor();
+ }
+
+ virtual size_t minimalLeadInSig(const_monomial sig) const {
+ MinimalLeadInSig searchObject(*_finder.getConfiguration().basis());
+ _finder.findAllDivisors(sig, searchObject);
+ return searchObject.minLeadGen();
+ }
+
+ virtual size_t divisor(const_monomial mon) const {
+ const Entry* entry = _finder.findDivisor(mon);
+ return entry == 0 ? static_cast<size_t>(-1) : entry->index;
+ }
+
+ virtual void divisors(const_monomial mon, EntryOutput& consumer) const {
+ PassOn out(consumer);
+ _finder.findAllDivisors(mon, out);
+ }
+
+ virtual void multiples(const_monomial mon, EntryOutput& consumer) const {
+ PassOn out(consumer);
+ _finder.findAllMultiples(mon, out);
+ }
+
+ virtual void removeMultiples(const_monomial mon) {
+ _finder.removeMultiples(mon);
+ }
+
+ virtual void remove(const_monomial mon) {
+ _finder.removeElement(mon);
+ }
+
+ virtual size_t size() const {
+ return _finder.size();
+ }
+
+ const C& getConfiguration() const {return _finder.getConfiguration();}
+ C& getConfiguration() {return _finder.getConfiguration();}
+
+ std::string getName() const;
+ const PolyRing * getPolyRing() const { return getConfiguration().getPolyRing(); }
+
+ size_t getMemoryUse() const;
+
+private:
+ // Class used in multiples() and divisors()
+ struct PassOn {
+ public:
+ PassOn(EntryOutput& out): mOut(out) {}
+ bool proceed(const Entry& entry) {
+ return mOut.proceed(entry.index);
+ }
+ private:
+ EntryOutput& mOut;
+ };
+
+ // Class used in lowBaseDivisor()
+ class LowBaseDivisor {
+ public:
+ LowBaseDivisor(
+ const GroebnerBasis& basis,
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator
+ ):
+ mSigBasis(basis),
+ mDivisors(divisors),
+ mMaxDivisors(maxDivisors),
+ mNewGenerator(newGenerator)
+ {
+ mDivisors.clear();
+ mDivisors.reserve(maxDivisors + 1);
+ }
+
+ bool proceed(const Entry& entry) {
+ if (entry.index >= mNewGenerator)
+ return true;
+ for (size_t j = 0; j <= mDivisors.size(); ++j) {
+ if (j == mDivisors.size()) {
+ mDivisors.push_back(entry.index);
+ break;
+ }
+ int cmp = mSigBasis.ratioCompare(entry.index, mDivisors[j]);
+ if (cmp == EQ && (entry.index < mDivisors[j]))
+ cmp = GT; // prefer minimum index to ensure deterministic behavior
+ if (cmp == GT) {
+ mDivisors.insert(mDivisors.begin() + j, entry.index);
+ break;
+ }
+ }
+ if (mDivisors.size() > mMaxDivisors)
+ mDivisors.pop_back();
+ ASSERT(mDivisors.size() <= mMaxDivisors);
+ return true;
+ }
+ private:
+ const GroebnerBasis& mSigBasis;
+ std::vector<size_t>& mDivisors;
+ const size_t mMaxDivisors;
+ const size_t mNewGenerator;
+ };
+
+ // Class used in highBaseDivisor()
+ class HighBaseDivisor {
+ public:
+ HighBaseDivisor(const GroebnerBasis& basis, size_t newGenerator):
+ mSigBasis(basis),
+ mNewGenerator(newGenerator),
+ mHighDivisor(static_cast<size_t>(-1)) {}
+ bool proceed(const Entry& entry) {
+ if (entry.index >= mNewGenerator)
+ return true;
+ if (mHighDivisor != static_cast<size_t>(-1)) {
+ int cmp = mSigBasis.ratioCompare(mHighDivisor, entry.index);
+ if (cmp == LT)
+ return true;
+ if (cmp == EQ && (entry.index > mHighDivisor))
+ return true; // prefer minimum index to ensure deterministic behavior
+ }
+ mHighDivisor = entry.index;
+ return true;
+ }
+ size_t highDivisor() const {return mHighDivisor;}
+ private:
+ const GroebnerBasis& mSigBasis;
+ const size_t mNewGenerator;
+ size_t mHighDivisor;
+ };
+
+ // Class used in minimalLeadInSig()
+ class MinimalLeadInSig {
+ public:
+ MinimalLeadInSig(const GroebnerBasis& basis):
+ mSigBasis(basis),
+ mMinLeadGen(static_cast<size_t>(-1)) {}
+
+ bool proceed(const Entry& entry) {
+ // Given signature sig, we want to minimize (S/G)g where
+ // g and G are the lead term and signature taken over basis elements
+ // whose signature G divide S. The code here instead maximizes G/g,
+ // which is equivalent and also faster since the basis has a data
+ // structure to accelerate comparisons between the ratio of
+ // signature to lead term.
+ //
+ // In case of ties, we select the sparser elements. If there is
+ // still a tie, we select the basis element with the largest
+ // signature. There can be no further ties since all basis
+ // elements have distinct signatures.
+
+ if (mMinLeadGen != static_cast<size_t>(-1)) {
+ const int ratioCmp = mSigBasis.ratioCompare(entry.index, mMinLeadGen);
+ if (ratioCmp == LT)
+ return true;
+ if (ratioCmp == EQ) {
+ // If same lead monomial in signature, pick the one with fewer terms
+ // as that one might be less effort to reduce.
+ const size_t minTerms = mSigBasis.poly(mMinLeadGen).nTerms();
+ const size_t terms = mSigBasis.poly(entry.index).nTerms();
+ if (minTerms > terms)
+ return true;
+ if (minTerms == terms) {
+ // If same number of terms, pick the one with larger signature
+ // before being multiplied into the same signature. That one
+ // might be more reduced as the constraint on regular reduction
+ // is less. Also, as no two generators have same signature, this
+ // ensures deterministic behavior.
+ const const_monomial minSig = mSigBasis.getSignature(mMinLeadGen);
+ const const_monomial genSig = mSigBasis.getSignature(entry.index);
+ int sigCmp = mSigBasis.order().signatureCompare(minSig, genSig);
+ ASSERT(sigCmp != EQ); // no two generators have same signature
+ if (sigCmp == GT)
+ return true;
+ }
+ }
+ }
+ mMinLeadGen = entry.index;
+ return true;
+ }
+
+ size_t minLeadGen() const {return mMinLeadGen;}
+ private:
+ const GroebnerBasis& mSigBasis;
+ size_t mMinLeadGen;
+ };
+ /*
+ // Class used in findDivisor()
+ class DO {
+ public:
+ DO(const FreeModuleOrder *F0, const PolyRing *R0, const_monomial sig, const_monomial monom, size_t &val, monomial &result_multiplier)
+ : F(F0),
+ R(R0),
+ _sig(sig),
+ _monom(monom),
+ _val(val),
+ _multiplier(result_multiplier),
+ _found(false)
+ {}
+
+ bool proceed(const Entry &e)
+ {
+ bool result = true;
+ // ASSERT(R->monomialDivide(_monom, e.monom, _multiplier));
+ // stats_n_reducer_divides++;
+ R->monomialDivide(_monom, e.monom, _multiplier);
+ // stats_n_reducer_sig_compares++;
+ if (GT == F->signatureCompare(_sig, _multiplier, e.sig))
+ {
+ //if (divisors) divisors[hashval] = i;
+ _val = e.index;
+ _found = true;
+ result = false;
+ }
+ if (tracingLevel==11)
+ {
+ std::cerr << " PR: " << result << " _monom= ";
+ R->monomialDisplay(std::cerr, _monom);
+ std::cerr << "e.monom= ";
+ R->monomialDisplay(std::cerr, e.monom);
+ std::cerr << " _multiplier= ";
+ R->monomialDisplay(std::cerr, _multiplier);
+ std::cerr << std::endl;
+ }
+ return result;
+ }
+
+ bool found() const { return _found; }
+ private:
+ const FreeModuleOrder *F;
+ const PolyRing *R;
+ const_monomial _sig;
+ const_monomial _monom;
+ // output values (the first two are references elsewhere, so no interface to them is needed).
+ size_t &_val;
+ monomial &_multiplier;
+ bool _found;
+ };
+ */
+
+ class DOCheckAll {
+ public:
+ DOCheckAll(
+ const GroebnerBasis& basis,
+ const_monomial sig,
+ const_monomial monom,
+ bool preferSparseReducers
+ ):
+ mRatioCmp(sig, monom, basis),
+ mSigBasis(basis),
+ mReducer(static_cast<size_t>(-1)),
+ mPreferSparseReducers(preferSparseReducers) {}
+
+ bool proceed(const Entry& e)
+ {
+ if (mRatioCmp.compare(e.index) != GT) {
+ mSigBasis.basis().wasNonSignatureReducer(e.index);
+ return true;
+ }
+
+ mSigBasis.basis().wasPossibleReducer(e.index);
+ if (mReducer != static_cast<size_t>(-1)) {
+ if (mPreferSparseReducers) {
+ // pick sparsest
+ size_t const newTermCount = mSigBasis.poly(e.index).nTerms();
+ size_t const oldTermCount = mSigBasis.poly(mReducer).nTerms();
+ if (newTermCount > oldTermCount)
+ return true; // what we already have is sparser
+ // resolve ties by picking oldest
+ if (newTermCount == oldTermCount && e.index > mReducer)
+ return true; // same sparsity and what we already have is older
+ } else {
+ // pick oldest
+ if (e.index > mReducer)
+ return true; // what we already have is older
+ }
+ }
+ mReducer = e.index;
+ return true;
+ }
+
+ size_t reducer() {return mReducer;}
+
+ private:
+ GroebnerBasis::StoredRatioCmp const mRatioCmp;
+ GroebnerBasis const& mSigBasis;
+ size_t mReducer;
+ bool const mPreferSparseReducers;
+ };
+
+public:
+ virtual void insert(const_monomial mon, size_t val) {
+ _finder.insert(Entry(mon, val));
+ }
+
+#if 0
+ virtual bool findDivisor(const_monomial sig, const_monomial mon, size_t &val, monomial &result_multiplier)
+ {
+ DO out(getConfiguration().order(), getConfiguration().getPolyRing(), sig, mon, val, result_multiplier);
+ _finder.findAllDivisors(mon, out);
+ return out.found();
+ }
+#endif
+ // ifdef the above code to 0 and this to 1, if you wish to use: choose divisor to be a one wih minimal number
+ // of monomial that satisfies sig and divisibility criteria.
+#if 1
+ virtual size_t regularReducer(const_monomial sig, const_monomial mon) const
+ {
+ DOCheckAll out(
+ *getConfiguration().basis(),
+ sig,
+ mon,
+ getConfiguration().preferSparseReducers()
+ );
+ _finder.findAllDivisors(mon, out);
+ return out.reducer();
+ }
+#endif
+ unsigned long long getExpQueryCount() const {
+ return _finder.getConfiguration().getExpQueryCount();
+ }
+
+ size_t n_elems() const { return _finder.size(); }
+
+ void display(std::ostream &o, int level) const; /**TODO: WRITE ME */
+ void dump(int level) const; /**TODO: WRITE ME */
+ private:
+ Finder _finder;
+};
+
+template<typename C>
+inline std::string DivLookup<C>::getName() const {
+ return "DL " + _finder.getName();
+}
+
+template<typename C>
+size_t DivLookup<C>::getMemoryUse() const
+{
+//#warning "implement getMemoryUse for DivLookup"
+ return 4 * sizeof(void *) * _finder.size(); // NOT CORRECT!!
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/DivisorLookup.cpp b/src/mathicgb/DivisorLookup.cpp
new file mode 100644
index 0000000..b7d8576
--- /dev/null
+++ b/src/mathicgb/DivisorLookup.cpp
@@ -0,0 +1,130 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "DivisorLookup.hpp"
+
+#include <mathic.h>
+#include "GroebnerBasis.hpp"
+#include "FreeModuleOrder.hpp"
+#include "DivLookup.hpp"
+
+namespace {
+ struct DefaultParams {
+ static bool const minimizeOnInsert = false;
+ static bool const sortOnInsert = false;
+ static bool const useDivisorCache = true;
+ static double const rebuildRatio;
+ static size_t const minRebuildRatio = 50;
+ };
+ double const DefaultParams::rebuildRatio = 0.5;
+
+ class DivListFactory : public DivisorLookup::Factory {
+ public:
+ DivListFactory(const PolyRing& ring, bool useDivMask): mRing(ring), mUseDivMask(useDivMask) {}
+ virtual std::auto_ptr<DivisorLookup> create(
+ bool preferSparseReducers,
+ bool allowRemovals
+ ) const {
+ if (mUseDivMask)
+ return createIt<true>(preferSparseReducers);
+ else
+ return createIt<false>(preferSparseReducers);
+ }
+
+ private:
+ template<bool UseDivMask>
+ std::auto_ptr<DivisorLookup> createIt(bool preferSparseReducers) const {
+ typedef DivLookupConfiguration<true, UseDivMask> Configuration;
+ bool useDM = UseDivMask;
+ Configuration configuration(
+ mRing,
+ DefaultParams::minimizeOnInsert,
+ DefaultParams::sortOnInsert,
+ DefaultParams::useDivisorCache,
+ DefaultParams::rebuildRatio,
+ DefaultParams::minRebuildRatio,
+ (useDM ? 1 : 3),
+ preferSparseReducers);
+ return std::auto_ptr<DivisorLookup>
+ (new DivLookup<mathic::DivList<Configuration> >(configuration));
+ }
+
+ const PolyRing& mRing;
+ bool mUseDivMask;
+ };
+
+ class KDTreeFactory : public DivisorLookup::Factory {
+ public:
+ KDTreeFactory(const PolyRing& ring, bool useDivMask): mRing(ring), mUseDivMask(useDivMask) {}
+ virtual std::auto_ptr<DivisorLookup> create(
+ bool preferSparseReducers,
+ bool allowRemovals
+ ) const {
+ if (allowRemovals) {
+ if (mUseDivMask)
+ return createAllowRemovals<true,true>(preferSparseReducers);
+ else
+ return createAllowRemovals<true,false>(preferSparseReducers);
+ } else {
+ if (mUseDivMask)
+ return createAllowRemovals<false,true>(preferSparseReducers);
+ else
+ return createAllowRemovals<false,false>(preferSparseReducers);
+ }
+ }
+
+ private:
+ template<bool AllowRemovals, bool UseDivMask>
+ std::auto_ptr<DivisorLookup> createAllowRemovals(
+ bool preferSparseReducers
+ ) const {
+ typedef DivLookupConfiguration<AllowRemovals, UseDivMask> Configuration;
+ bool useDM = UseDivMask;
+ Configuration configuration(
+ mRing,
+ DefaultParams::minimizeOnInsert,
+ DefaultParams::sortOnInsert,
+ DefaultParams::useDivisorCache,
+ DefaultParams::rebuildRatio,
+ DefaultParams::minRebuildRatio,
+ (useDM ? 2 : 4),
+ preferSparseReducers);
+ return std::auto_ptr<DivisorLookup>
+ (new DivLookup<mathic::KDTree<Configuration> >(configuration));
+ }
+ const PolyRing& mRing;
+ bool mUseDivMask;
+ };
+}
+
+std::auto_ptr<DivisorLookup::Factory> DivisorLookup::makeFactory(
+ const PolyRing& ring,
+ int type
+) {
+ if (type == 1)
+ return std::auto_ptr<Factory>(new DivListFactory(ring, true));
+ else if (type == 2)
+ return std::auto_ptr<Factory>(new KDTreeFactory(ring, true));
+ if (type == 3)
+ return std::auto_ptr<Factory>(new DivListFactory(ring, false));
+ else if (type == 4)
+ return std::auto_ptr<Factory>(new KDTreeFactory(ring, false));
+ else if (type == 0)
+ throw std::runtime_error("Divisor lookup 0 (DivisorLookupGB) disabled.");
+ else
+ throw std::runtime_error("Unknown divisor lookup code.");
+}
+
+void DivisorLookup::displayDivisorLookupTypes(std::ostream &o)
+{
+ o << "Divisor Lookup Types:" << std::endl;
+ o << " 1 divlist+divmask" << std::endl;
+ o << " 2 kdtree+divmask" << std::endl;
+ o << " 3 divlist" << std::endl;
+ o << " 4 kdtree" << std::endl;
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/DivisorLookup.hpp b/src/mathicgb/DivisorLookup.hpp
new file mode 100644
index 0000000..ae9889a
--- /dev/null
+++ b/src/mathicgb/DivisorLookup.hpp
@@ -0,0 +1,92 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _divisor_lookup_h_
+#define _divisor_lookup_h_
+
+#include "PolyRing.hpp"
+#include <vector>
+
+class PolyBasis;
+class GroebnerBasis;
+class FreeModuleOrder;
+
+// Supports queries on the lead terms of the monomials in a PolyBasis.
+// todo: rename to MonomialLookup.
+class DivisorLookup
+{
+public:
+ // Call after construction. Can be called multiple times, but only if the
+ // parameter object is the same each time.
+ virtual void setBasis(const PolyBasis& basis) = 0;
+
+ // Call after construction. Can be called multiple times, but only if the
+ // parameter object is the same each time.
+ virtual void setSigBasis(const GroebnerBasis& sigBasis) = 0;
+
+ virtual ~DivisorLookup() {}
+
+ virtual void insert(const_monomial mon, size_t index) = 0;
+
+ // Returns the index of a basis element that regular reduces mon in
+ // signature sig. Returns -1 if no such element exists. A basis element
+ // u is a regular reducer if leadTerm(u) divides mon
+ // and (mon / leadTerm(u)) * signature(u) < sig.
+ virtual size_t regularReducer
+ (const_monomial sig, const_monomial mon) const = 0;
+
+ virtual std::string getName() const = 0;
+
+ virtual size_t getMemoryUse() const = 0;
+
+ virtual size_t highBaseDivisor(size_t newGenerator) const = 0;
+ virtual void lowBaseDivisors(
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator) const = 0;
+ virtual size_t minimalLeadInSig(const_monomial sig) const = 0;
+
+ virtual int type() const = 0;
+
+ static void displayDivisorLookupTypes(std::ostream &o);
+
+ class Factory {
+ public:
+ virtual std::auto_ptr<DivisorLookup> create
+ (bool preferSparseReducers, bool allowRemovals) const = 0;
+ };
+ static std::auto_ptr<Factory> makeFactory(const PolyRing& ring, int type);
+ // choices for type: 1: divlist, 2:kdtree.
+
+ class EntryOutput {
+ public:
+ // Stop whatever is happening if proceed returns false.
+ virtual bool proceed(size_t index) = 0;
+ };
+
+ // Calls consumer.proceed(index) for each element whose lead term
+ // divides mon. Stops the search if proceed returns false.
+ virtual void multiples(const_monomial mon, EntryOutput& consumer) const = 0;
+
+ // Returns the index of a basis element whose lead term divides mon.
+ virtual size_t divisor(const_monomial mon) const = 0;
+
+ // Calls consumer.proceed(index) for each element whose term
+ // mon divides. Stops the search if proceed returns false.
+ virtual void divisors(const_monomial mon, EntryOutput& consumer) const = 0;
+
+ // Removes multiples of mon. An element equal to mon counts as a multiple.
+ virtual void removeMultiples(const_monomial mon) = 0;
+
+ // Removes entries whose monomial are equal to mon.
+ virtual void remove(const_monomial mon) = 0;
+
+ // Returns how many elements are in the data structure.
+ virtual size_t size() const = 0;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/FreeModuleOrder.cpp b/src/mathicgb/FreeModuleOrder.cpp
new file mode 100644
index 0000000..f532b72
--- /dev/null
+++ b/src/mathicgb/FreeModuleOrder.cpp
@@ -0,0 +1,592 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include <iostream>
+#include <algorithm>
+#include "FreeModuleOrder.hpp"
+#include "SPairQueue.hpp"
+#include "Poly.hpp"
+#include "Ideal.hpp"
+#include <mathic.h>
+
+namespace {
+ struct SizeSummer {
+ public:
+ SizeSummer(): mSum(0) {}
+ bool proceed(const SPairGroup* group) {
+ mSum += group->size();
+ return true;
+ }
+ size_t sum() const {return mSum;}
+ private:
+ size_t mSum;
+ };
+}
+
+template<class Cmp, template<class> class Queue = mathic::TourTree>
+class ConcreteQueue : public SPairQueue {
+public:
+ ConcreteQueue(const Cmp& cmp): mQueue(Configuration(cmp)) {}
+
+ virtual bool empty() const {return mQueue.empty();}
+ virtual void push(const Entry& entry) {mQueue.push(entry);}
+ virtual Entry pop() {return mQueue.pop();}
+ virtual Entry top() const {return mQueue.top();}
+ virtual void decreaseTop(const Entry& newValue) {
+ mQueue.decreaseTop(newValue);
+ }
+
+ virtual void print(std::ostream& out) const {mQueue.print(out);}
+ virtual std::string getName() const {return mQueue.getName();}
+ virtual size_t getMemoryUse() const {return mQueue.getMemoryUse();}
+ virtual size_t sumOfSizes() const {
+ SizeSummer summer;
+ mQueue.forAll(summer);
+ return summer.sum();
+ }
+
+private:
+ class Configuration {
+ public:
+ typedef SPairQueue::Entry Entry;
+
+ Configuration(const Cmp& cmp): mCmp(cmp) {}
+
+ typedef int CompareResult;
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return mCmp.signatureCompare(a->signature(), b->signature());
+ }
+ bool cmpLessThan(CompareResult r) const {return r == GT;}
+ bool cmpEqual(CompareResult r) const {
+ ASSERT(false); // not supposed to be used
+ return r == EQ;
+ }
+ Entry deduplicate(const Entry& a, const Entry& b) const {
+ ASSERT(false); // not supposed to be used
+ return a;
+ }
+
+ static const bool supportDeduplication = false;
+ static const bool fastIndex = true;
+
+ private:
+ const Cmp& mCmp;
+ };
+
+ Queue<Configuration> mQueue;
+};
+
+template<class Cmp>
+class ConcreteOrder : public FreeModuleOrder {
+public:
+ ConcreteOrder(Cmp cmp): mCmp(cmp), mComparisons(0), mPreComparisons(0) {}
+ virtual ~ConcreteOrder() {}
+
+ virtual int signatureCompare(const_monomial sigA, const_monomial sigB) const {
+ ++mComparisons;
+ return mCmp.signatureCompare(sigA, sigB);
+ }
+
+ virtual int signatureCompare(
+ const_monomial sigA,
+ const_monomial monoB,
+ const_monomial sigB
+ ) const {
+ ++mComparisons;
+ return mCmp.signatureCompare(sigA, monoB, sigB);
+ }
+
+ virtual void destructiveSort(std::vector<PreSPair>& pairs) const {
+ DestructiveSortComparer cmp(mCmp);
+ mCmp.prepareDestructiveSort(pairs);
+ std::sort(pairs.begin(), pairs.end(), cmp);
+ mPreComparisons += cmp.comparisons();
+ }
+
+ virtual void appendBasisElement(const_monomial m) {
+ mCmp.appendBasisElement(m);
+ }
+
+ virtual std::auto_ptr<SPairQueue> makeQueue(size_t queueType) const {
+ switch (queueType) {
+ case 0:
+ return std::auto_ptr<SPairQueue>
+ (new ConcreteQueue<Cmp, mic::TourTree>(mCmp));
+ case 1:
+ return std::auto_ptr<SPairQueue>
+ (new ConcreteQueue<Cmp, mic::Heap>(mCmp));
+ default:
+ mic::reportError("Unknown queue type.");
+ return std::auto_ptr<SPairQueue>(); // won't reach this far
+ }
+ }
+
+ virtual std::string description() const {
+ return mCmp.description();
+ }
+
+ virtual void getStats(size_t& comparisons, size_t& preComparisons) const {
+ comparisons = mComparisons;
+ preComparisons = mPreComparisons;
+ }
+
+private:
+ class DestructiveSortComparer {
+ public:
+ DestructiveSortComparer(const Cmp& cmp): mCmp(cmp), mComparisons(0) {}
+ bool operator()(const PreSPair& a, const PreSPair& b) const {
+ ++mComparisons;
+ return mCmp.lessThanForDestructiveSort(a.signature, b.signature);
+ }
+ size_t comparisons() const {return mComparisons;}
+ private:
+ const Cmp& mCmp;
+ mutable size_t mComparisons;
+ };
+
+ Cmp mCmp;
+ mutable size_t mComparisons;
+ mutable size_t mPreComparisons;
+};
+
+// ** Graded reverse lex.
+// Degrees and exponents considered from high index to low index.
+//
+// rule: a[i] < b[i] then a > b
+//
+// also applies to component, which is considered last.
+extern int tracingLevel;
+class OrderA {
+public:
+ OrderA(const PolyRing* ring): mRing(ring) {}
+
+ int signatureCompare(const_monomial sigA, const_monomial sigB) const {
+ return mRing->monomialCompare(sigA, sigB);
+ }
+
+ void prepareDestructiveSort(std::vector<PreSPair>& pairs) const {}
+
+ bool lessThanForDestructiveSort(const_monomial sigA, const_monomial sigB) const {
+ return mRing->monomialLT(sigA, sigB);
+ }
+
+ int signatureCompare(const_monomial sigA,
+ const_monomial monoB, const_monomial sigB) const {
+ return mRing->monomialCompare(sigA, monoB, sigB);
+ }
+
+ void appendBasisElement(const_monomial) {}
+
+ char const* description() const {return "GrevLex IndexDown";}
+
+private:
+ PolyRing const* const mRing;
+};
+
+// let d(x) be the first degree of x, so if there are more than one
+// vector of weights, only the first one matters for d. By "first"
+// degree, read "one with highest index".
+//
+// rule 1: if d(ag_i) > (bg_j) then ae_i > be_j
+// rule 2: if i > j then ae_i > be_j
+// rule 2: graded reverse lex as for OrderA, but ignoring the first degree
+//
+// It was probably intended that all the degree should be considered,
+// it just wasn't implemented for more than 1 degree.
+class OrderB {
+public:
+ OrderB(const Ideal *I):
+ mRing(I->getPolyRing()),
+ topindex(I->getPolyRing()->maxMonomialSize() - 2) {
+ for (size_t i = 0; i < I->size(); ++i)
+ appendBasisElement(I->getPoly(i)->getLeadMonomial());
+ }
+
+ void prepareDestructiveSort(std::vector<PreSPair>& pairs) const {}
+
+ bool lessThanForDestructiveSort(const_monomial a, const_monomial b) const {
+ return signatureCompare(a, b) == LT;
+ }
+
+ int signatureCompare(const_monomial sig, const_monomial sig2) const {
+ int da = - sig[topindex] + deg[*sig];
+ int db = -sig2[topindex] + deg[*sig2];
+ if (da > db) return GT;
+ if (db > da) return LT;
+ int cmp = *sig - *sig2;
+ if (cmp > 0) return GT;
+ if (cmp < 0) return LT;
+ for (size_t i = topindex-1; i >= 1; --i)
+ {
+ int cmp = sig[i] - sig2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+ }
+
+ int signatureCompare(
+ const_monomial sig,
+ const_monomial m2,
+ const_monomial sig2
+ ) const {
+ int da = - sig[topindex] + deg[*sig];
+ int db = - m2[topindex];
+ db += -sig2[topindex] + deg[*sig2];
+ if (da > db) return GT;
+ if (db > da) return LT;
+ int cmp = *sig - *sig2;
+ if (cmp > 0) return GT;
+ if (cmp < 0) return LT;
+ for (size_t i = topindex-1; i >= 1; --i)
+ {
+ int cmp = sig[i] - m2[i] - sig2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+ }
+
+ char const* description() const {return "DegreeUp IndexUp GrevLex";}
+ void appendBasisElement(const_monomial m) {deg.push_back(-m[topindex]);}
+
+private:
+ PolyRing const* const mRing;
+ size_t const topindex;
+
+ // array of degrees for each component 0..numgens I - 1
+ std::vector<int> deg;
+};
+
+// as OrderB, except rule 2 is opposite, so
+// rule 2: if i > j then ae_i < be_j
+class OrderC
+{
+public:
+ OrderC(const Ideal* I):
+ mRing(I->getPolyRing()),
+ topindex(I->getPolyRing()->maxMonomialSize() - 2)
+ {
+ for (size_t i = 0; i < I->size(); ++i)
+ appendBasisElement(I->getPoly(i)->getLeadMonomial());
+ }
+
+ void appendBasisElement(const_monomial m) {deg.push_back(-m[topindex]);}
+
+ void prepareDestructiveSort(std::vector<PreSPair>& pairs) const {}
+
+ bool lessThanForDestructiveSort(const_monomial a, const_monomial b) const {
+ return signatureCompare(a, b) == LT;
+ }
+
+ int signatureCompare(const_monomial sig, const_monomial sig2) const {
+ int da = - sig[topindex] + deg[*sig];
+ int db = -sig2[topindex] + deg[*sig2];
+ if (da > db) return GT;
+ if (db > da) return LT;
+ int cmp = *sig - *sig2;
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ for (size_t i = topindex-1; i >= 1; --i)
+ {
+ int cmp = sig[i] - sig2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+ }
+
+ int signatureCompare(
+ const_monomial sig,
+ const_monomial m2,
+ const_monomial sig2
+ ) const {
+ int da = - sig[topindex] + deg[*sig];
+ int db = - m2[topindex];
+ db += -sig2[topindex] + deg[*sig2];
+ if (da > db) return GT;
+ if (db > da) return LT;
+ int cmp = *sig - *sig2;
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ for (size_t i = topindex-1; i >= 1; --i)
+ {
+ int cmp = sig[i] - m2[i] - sig2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+ }
+
+ char const* description() const {return "DegreeUp IndexDown GrevLex";}
+
+private:
+ PolyRing const* const mRing;
+ const size_t topindex;
+
+ // array of degrees for each component 0..numgens I - 1
+ std::vector<int> deg;
+};
+
+// Let l(ae_i) be the leading monomial of ag_i.
+// Indexes are considered from high to low
+// rule 1: higher degree of l(ae_I) wins
+// rule 2: reverse lex on l(ae_i)
+// rule 3: higher component wins (if mUp is true)
+// rule 3': lower components wins (if mUp is false)
+class OrderD
+{
+public:
+ OrderD(Ideal const* I, bool up0):
+ mRing(I->getPolyRing()),
+ mUp(up0),
+ topindex(mRing->maxMonomialSize() - 2) {
+ for (size_t i = 0; i < I->size(); ++i)
+ appendBasisElement(I->getPoly(i)->getLeadMonomial());
+ }
+
+ void appendBasisElement(const_monomial m) {monoms.push_back(m);}
+
+ int signatureCompare(const_monomial a, const_monomial b) const {
+ const_monomial ma = monoms[*a];
+ const_monomial mb = monoms[*b];
+ for (size_t i = topindex; i >= 1; i--) {
+ int cmp = a[i] - b[i] + ma[i] - mb[i];
+ if (cmp != 0)
+ return cmp < 0 ? GT : LT;
+ }
+ if (*a == *b)
+ return EQ;
+ if (mUp)
+ return *a < *b ? LT : GT;
+ else
+ return *a < *b ? GT : LT;
+ }
+
+ void prepareDestructiveSort(std::vector<PreSPair>& pairs) const {
+ typedef std::vector<PreSPair>::iterator Iter;
+ Iter end = pairs.end();
+ for (Iter it = pairs.begin(); it != end; ++it) {
+ monomial sig = it->signature;
+ const_monomial adjust = monoms[*sig];
+ for (size_t i = topindex; i >= 1; --i)
+ sig[i] += adjust[i];
+ if (mUp)
+ *sig = -*sig;
+ }
+ }
+
+ inline bool lessThanForDestructiveSort(
+ const_monomial a,
+ const_monomial b
+ ) const {
+ /* non-unrolled version:
+ for (size_t i = topindex; i != static_cast<size_t>(-1); i--) {
+ // for i == 0, this will compare the components. We have
+ // already adjusted those to take account of mUp.
+ int cmp = a[i] - b[i]; // already done: + ma[i] - mb[i];
+ if (cmp != 0)
+ return cmp > 0;
+ }
+ return false; // equality */
+
+ size_t i = topindex;
+ if ((topindex & 1) == 0) { // if odd number of entries to check
+ int cmp = a[topindex] - b[topindex];
+ if (cmp != 0)
+ return cmp > 0;
+ --i;
+ }
+ for (; i != static_cast<size_t>(-1); i -= 2) {
+ int cmp = a[i] - b[i]; // already done: + ma[i] - mb[i];
+ if (cmp != 0)
+ return cmp > 0;
+ int cmp2 = a[i - 1] - b[i - 1];
+ if (cmp2 != 0)
+ return cmp2 > 0;
+ }
+ return false; // equality
+ }
+
+ int signatureCompare(const_monomial a, const_monomial m2, const_monomial b) const {
+ const_monomial ma = monoms[*a];
+ const_monomial mb = monoms[*b];
+ for (size_t i = topindex; i >= 1; i--)
+ {
+ int cmp = a[i] - b[i] + ma[i] - mb[i] - m2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ int cmp = *a - *b;
+ if (mUp)
+ {
+ if (cmp < 0) return LT;
+ if (cmp > 0) return GT;
+ }
+ else
+ {
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+ }
+
+ virtual char const* description() const {
+ if (mUp)
+ return "SchreyerGrevLexUp";
+ else
+ return "SchreyerGrevLexDown";
+ }
+
+private:
+ PolyRing const* const mRing;
+ bool const mUp; // how to break ties once the monomials are the same
+ size_t const topindex; // taken from R
+
+ // array 0..numgens I-1 pointing to lead terms of elements of I
+ std::vector<const_monomial> monoms;
+};
+
+
+// Let l(ae_i) be the leading monomial of ag_i.
+// Indexes are considered from high to low
+// rule 1: higher component wins (if mUp is true)
+// rule 1': lower components wins (if mUp is false)
+// rule 2: higher degree of l(ae_I) wins
+// rule 3: reverse lex on l(ae_i)
+class OrderE
+{
+public:
+ OrderE(Ideal const* I, bool up0):
+ mRing(I->getPolyRing()),
+ mUp(up0),
+ topindex(mRing->maxMonomialSize() - 2) {
+ for (size_t i = 0; i < I->size(); ++i)
+ appendBasisElement(I->getPoly(i)->getLeadMonomial());
+ }
+
+ void appendBasisElement(const_monomial m) {monoms.push_back(m);}
+
+ int signatureCompare(const_monomial a, const_monomial b) const {
+ const_monomial ma = monoms[*a];
+ const_monomial mb = monoms[*b];
+
+ if (*a != *b)
+ {
+ if (mUp)
+ return *a < *b ? LT : GT;
+ else
+ return *a < *b ? GT : LT;
+ }
+
+ for (size_t i = topindex; i >= 1; i--) {
+ int cmp = a[i] - b[i] + ma[i] - mb[i];
+ if (cmp != 0)
+ return cmp < 0 ? GT : LT;
+ }
+ return EQ;
+ }
+
+ void prepareDestructiveSort(std::vector<PreSPair>& pairs) const {
+ }
+
+ inline bool lessThanForDestructiveSort(
+ const_monomial a,
+ const_monomial b
+ ) const {
+ return signatureCompare(a,b) == LT;
+ }
+
+ int signatureCompare(const_monomial a, const_monomial m2, const_monomial b) const {
+ const_monomial ma = monoms[*a];
+ const_monomial mb = monoms[*b];
+ int cmp = *a - *b;
+ if (cmp != 0)
+ {
+ if (mUp)
+ {
+ if (cmp < 0) return LT;
+ if (cmp > 0) return GT;
+ }
+ else
+ {
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ }
+
+ for (size_t i = topindex; i >= 1; i--)
+ {
+ int cmp = a[i] - b[i] + ma[i] - mb[i] - m2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+ }
+
+ virtual char const* description() const {
+ if (mUp)
+ return "IndexUp SchreyerGrevLex";
+ else
+ return "IndexDown SchreyerGrevLex";
+ }
+
+private:
+ PolyRing const* const mRing;
+ bool const mUp;
+ size_t const topindex; // taken from R
+
+ // array 0..numgens I-1 pointing to lead terms of elements of I
+ std::vector<const_monomial> monoms;
+};
+
+void FreeModuleOrder::displayOrderTypes(std::ostream &o)
+{
+ o << "FreeModule orders:" << std::endl;
+ o << " 1 GrevLex IndexDown" << std::endl;
+ o << " 2 DegreeUp IndexUp GrevLex" << std::endl;
+ o << " 3 DegreeUp IndexDown GrevLex" << std::endl;
+ o << " 4 SchreyerGrevLexUp" << std::endl;
+ o << " 5 SchreyerGrevLexDown" << std::endl;
+ o << " 6 IndexUp SchreyerGrevLex" << std::endl;
+ o << " 7 IndexDown SchreyerGrevLex" << std::endl;
+}
+
+FreeModuleOrder *FreeModuleOrder::makeOrder(FreeModuleOrderType type, const Ideal *I)
+{
+ int i;
+ if (type == 0)
+ type = 1; // Set the default
+
+ switch (type) {
+ case 1:
+ return new ConcreteOrder<OrderA>(OrderA(I->getPolyRing()));
+ case 2:
+ return new ConcreteOrder<OrderB>(OrderB(I));
+ case 3:
+ return new ConcreteOrder<OrderC>(OrderC(I));
+ case 4:
+ return new ConcreteOrder<OrderD>(OrderD(I, true));
+ case 5:
+ return new ConcreteOrder<OrderD>(OrderD(I, false));
+ case 6:
+ return new ConcreteOrder<OrderE>(OrderE(I, true));
+ case 7:
+ return new ConcreteOrder<OrderE>(OrderE(I, false));
+ default: break;
+ }
+
+ std::cerr << "unknown free module order type" << std::endl;
+ std::cerr << "possible orders are: " << std::endl;
+ for (i=1; i<=5; i++)
+ {
+ FreeModuleOrder* F = makeOrder(i,I);
+ std::cerr << " " << i << ": " << F->description() << std::endl;
+ }
+ exit(1);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/FreeModuleOrder.hpp b/src/mathicgb/FreeModuleOrder.hpp
new file mode 100644
index 0000000..a39bd7f
--- /dev/null
+++ b/src/mathicgb/FreeModuleOrder.hpp
@@ -0,0 +1,54 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _free_module_order_h_
+#define _free_module_order_h_
+
+#include "PolyRing.hpp"
+#include "PairTriangle.hpp"
+
+class PolyRing;
+class SPairQueue;
+class Ideal;
+typedef int FreeModuleOrderType;
+
+class FreeModuleOrder
+{
+public:
+ FreeModuleOrder() {}
+ virtual ~FreeModuleOrder() {}
+
+ // returns LT, EQ, or GT, depending on sig ? sig2.
+ virtual int signatureCompare(const_monomial sig, const_monomial sig2) const = 0;
+
+ // compares sig vs (m2*sig)
+ virtual int signatureCompare(
+ const_monomial sig,
+ const_monomial m2,
+ const_monomial sig2
+ ) const = 0;
+
+ // Sorts in ascending order of signature. May alter the signatures,
+ // so do not use them after calling this method other than to free them.
+ virtual void destructiveSort(std::vector<PreSPair>& pairs) const = 0;
+
+ // You must use this method to inform the order when a
+ // new basis element has been added.
+ virtual void appendBasisElement(const_monomial m) = 0;
+
+ virtual void getStats(size_t& comparisons, size_t& preComparisons) const = 0;
+
+ virtual std::string description() const = 0;
+
+ virtual std::auto_ptr<SPairQueue> makeQueue(size_t type) const = 0;
+
+ static FreeModuleOrder* makeOrder(FreeModuleOrderType type, const Ideal* I);
+
+ static void displayOrderTypes(std::ostream &o);
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/GroebnerBasis.cpp b/src/mathicgb/GroebnerBasis.cpp
new file mode 100644
index 0000000..aa7e012
--- /dev/null
+++ b/src/mathicgb/GroebnerBasis.cpp
@@ -0,0 +1,482 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include <iostream>
+#include <iomanip>
+#include "Poly.hpp"
+#include "GroebnerBasis.hpp"
+#include <limits>
+
+GroebnerBasis::GroebnerBasis(
+ const PolyRing* R0,
+ FreeModuleOrder* order,
+ int divisorLookupType,
+ int monTableType,
+ bool preferSparseReducers):
+ mDivisorLookupFactory
+ (DivisorLookup::makeFactory(*R0, divisorLookupType)),
+ mRatioSorted(RatioOrder(sigLeadRatio, *order)),
+ mMinimalDivisorLookup(mDivisorLookupFactory->create(preferSparseReducers, true)),
+ mBasis(*R0, *order, mDivisorLookupFactory->create(preferSparseReducers, true)),
+ mPreferSparseReducers(preferSparseReducers)
+{
+ mTmp = mBasis.ring().allocMonomial();
+ const_cast<DivisorLookup&>(mBasis.divisorLookup()).setSigBasis(*this);
+ mMinimalDivisorLookup->setSigBasis(*this);
+}
+
+GroebnerBasis::~GroebnerBasis()
+{
+ ASSERT(mBasis.size() == mSignatures.size());
+ ASSERT(mBasis.size() == sigLeadRatio.size());
+
+ for (size_t i = 0; i < mBasis.size(); i++) {
+ if (! mSignatures[i].isNull())
+ ring().freeMonomial(mSignatures[i]);
+ if (! sigLeadRatio[i].isNull())
+ ring().freeMonomial(sigLeadRatio[i]);
+ }
+ for (size_t i = 0; i < mSignatureLookup.size(); ++i)
+ delete mSignatureLookup[i];
+ mBasis.ring().freeMonomial(mTmp);
+}
+
+void GroebnerBasis::addComponent() {
+ std::auto_ptr<DivisorLookup> lookup =
+ mDivisorLookupFactory->create(mPreferSparseReducers, true);
+ lookup->setSigBasis(*this);
+ mSignatureLookup.push_back(0);
+ mSignatureLookup.back() = lookup.release(); // only release after alloc
+}
+
+void GroebnerBasis::insert(monomial sig, std::auto_ptr<Poly> f)
+{
+ ASSERT(f.get() != 0);
+ ASSERT(f->getLeadCoefficient() != 0);
+ ASSERT(sig.isNull() || ring().getMonomialPool().fromPool(sig.unsafeGetRepresentation()));
+ const size_t index = mSignatures.size();
+
+ mSignatures.push_back(sig);
+
+ monomial ratio = 0;
+ if (!sig.isNull()) {
+ const size_t component = ring().monomialGetComponent(sig);
+ ASSERT(component < mSignatureLookup.size());
+ mSignatureLookup[component]->insert(sig, index);
+
+ ratio = ring().allocMonomial(ring().getMonomialPool());
+ ring().monomialDivideToNegative(sig, f->getLeadMonomial(), ratio);
+ }
+ sigLeadRatio.push_back(ratio);
+
+ const_monomial const lead = f->getLeadMonomial();
+ mBasis.insert(f);
+ if (mBasis.leadMinimal(mBasis.size() - 1)) {
+ mMinimalDivisorLookup->removeMultiples(lead);
+ mMinimalDivisorLookup->insert(lead, index);
+ }
+
+ ASSERT(mMinimalDivisorLookup->type() == 0 ||
+ mBasis.minimalLeadCount() == mMinimalDivisorLookup->size());
+ ASSERT(mSignatures.size() == index + 1);
+ ASSERT(mBasis.size() == index + 1);
+ if (!mUseRatioRank || sig.isNull())
+ return;
+
+ // compute rank of the ratio
+ RatioSortedType::iterator pos = mRatioSorted.insert(index);
+again:
+ Rank prevRank;
+ if (pos == mRatioSorted.begin())
+ prevRank = 0;
+ else {
+ RatioSortedType::iterator prev = pos;
+ --prev;
+ prevRank = mRatioRanks[*prev];
+ if (ring().monomialEQ(ratio, sigLeadRatio[*prev])) {
+ mRatioRanks.push_back(prevRank);
+ return;
+ }
+ }
+
+ Rank nextRank;
+ RatioSortedType::iterator next = pos;
+ ++next;
+ if (next == mRatioSorted.end())
+ nextRank = std::numeric_limits<Rank>::max();
+ else {
+ nextRank = mRatioRanks[*next];
+ if (ring().monomialEQ(ratio, sigLeadRatio[*next])) {
+ mRatioRanks.push_back(nextRank);
+ return;
+ }
+ }
+ ASSERT(prevRank < nextRank);
+
+ // this formula avoids the overflow inherent in prevRank + nextRank;
+ Rank rank = prevRank + (nextRank - prevRank) / 2;
+
+ // must have at least 1 space between ranks to support
+ // queries for non-basis element rank
+ if (rank == 0 || // must leave space for smaller ratio
+ rank == std::numeric_limits<Rank>::max() || // shouldn't happen
+ nextRank - prevRank < 4) { // 4 as require: prev, gap, new, gap, next
+ // size plus 1 to account for the gaps at the beginning and end.
+ size_t increment = std::numeric_limits<Rank>::max() / (mSignatures.size() + 1);
+ if (increment == 0)
+ increment = 2;
+ ASSERT(!mRatioSorted.empty());
+ size_t rankSum = increment; // leave a gap at beginning
+ Rank prevRank = *mRatioRanks.begin();
+ for (RatioSortedType::iterator it = mRatioSorted.begin();
+ it != mRatioSorted.end(); ++it) {
+ if (it == pos)
+ continue;
+ if (mRatioRanks[*it] != prevRank)
+ rankSum += increment;
+ prevRank = mRatioRanks[*it];
+ mRatioRanks[*it] = rankSum;
+ }
+ goto again;
+ }
+ ASSERT(rank > 0);
+ ASSERT(rank < std::numeric_limits<Rank>::max());
+ ASSERT(prevRank + 1 < rank && rank < nextRank - 1);
+ mRatioRanks.push_back(rank);
+ ASSERT(mRatioRanks.size() == index + 1);
+
+#ifdef DEBUG
+ // Check that at least one space has been left between every rank
+ ASSERT(mRatioRanks[*mRatioSorted.begin()] > 0);
+ ASSERT(mRatioRanks[*mRatioSorted.rbegin()] <
+ std::numeric_limits<Rank>::max());
+ RatioSortedType::iterator it2 = mRatioSorted.begin();
+ for (++it2; it2 != mRatioSorted.end(); ++it2) {
+ RatioSortedType::iterator prev = it2;
+ --prev;
+ ASSERT(mRatioRanks[*it2] == mRatioRanks[*prev] ||
+ mRatioRanks[*it2] - 1 > mRatioRanks[*prev]);
+ }
+#endif
+}
+
+size_t GroebnerBasis::regularReducer(
+ const_monomial sig,
+ const_monomial term
+) const {
+ size_t reducer = divisorLookup().regularReducer(sig, term);
+#ifdef DEBUG
+ const size_t debugValue = regularReducerSlow(sig, term);
+ if (reducer == static_cast<size_t>(-1)) {
+ ASSERT(debugValue == static_cast<size_t>(-1));
+ } else {
+ ASSERT(debugValue != static_cast<size_t>(-1));
+ monomial m = ring().allocMonomial();
+ ASSERT(ring().monomialDivide(term, getLeadMonomial(reducer), m));
+ ring().monomialMultTo(m, getSignature(reducer));
+ ASSERT(order().signatureCompare(sig, m) == GT);
+ ring().freeMonomial(m);
+ }
+#endif
+ return reducer;
+}
+
+size_t GroebnerBasis::regularReducerSlow(
+ const_monomial sig,
+ const_monomial term
+) const {
+ monomial m = ring().allocMonomial();
+ const size_t stop = size();
+ for (size_t be = 0; be < stop; ++be) {
+ if (!ring().monomialDivide(term, getLeadMonomial(be), m))
+ continue;
+ ring().monomialMultTo(m, getSignature(be));
+ if (order().signatureCompare(sig, m) == GT) {
+ ring().freeMonomial(m);
+ return be;
+ }
+ }
+ ring().freeMonomial(m);
+ return static_cast<size_t>(-1);
+}
+
+void GroebnerBasis::lowBaseDivisors(
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator) const
+{
+ ASSERT(newGenerator < size());
+ const_monomial sigNew = getSignature(newGenerator);
+ const size_t component = ring().monomialGetComponent(sigNew);
+ mSignatureLookup[component]->
+ lowBaseDivisors(divisors, maxDivisors, newGenerator);
+#ifdef DEBUG
+ std::vector<size_t> debugValue;
+ lowBaseDivisorsSlow(debugValue, maxDivisors, newGenerator);
+ ASSERT(divisors.size() <= maxDivisors);
+ ASSERT(debugValue.size() == divisors.size());
+ for (size_t i = 0; i < divisors.size(); ++i) {
+ ASSERT(ratioCompare(debugValue[i], divisors[i]) == EQ);
+ }
+#endif
+}
+
+void GroebnerBasis::lowBaseDivisorsSlow(
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator) const
+{
+ ASSERT(newGenerator < size());
+
+ divisors.clear();
+ divisors.reserve(maxDivisors + 1);
+
+ const_monomial sigNew = getSignature(newGenerator);
+ for (size_t i = 0; i < newGenerator; ++i) {
+ const_monomial sigi = getSignature(i);
+
+ if (ring().monomialGetComponent(sigi) !=
+ ring().monomialGetComponent(sigNew))
+ continue;
+ if (!ring().monomialIsDivisibleBy(sigNew, sigi))
+ continue;
+ for (size_t j = 0; j <= divisors.size(); ++j) {
+ if (j == divisors.size()) {
+ divisors.push_back(i);
+ break;
+ }
+ if (ratioCompare(i, divisors[j]) == GT) {
+ divisors.insert(divisors.begin() + j, i);
+ break;
+ }
+ }
+ if (divisors.size() > maxDivisors)
+ divisors.pop_back();
+ ASSERT(divisors.size() <= maxDivisors);
+ }
+ ASSERT(divisors.size() <= maxDivisors);
+}
+
+size_t GroebnerBasis::highBaseDivisor(size_t newGenerator) const {
+ ASSERT(newGenerator < size());
+ size_t highDivisor = divisorLookup().highBaseDivisor(newGenerator);
+#ifdef DEBUG
+ size_t debugValue = highBaseDivisorSlow(newGenerator);
+ ASSERT((highDivisor == static_cast<size_t>(-1)) ==
+ (debugValue == static_cast<size_t>(-1)));
+ ASSERT(highDivisor == static_cast<size_t>(-1) ||
+ ratioCompare(debugValue, highDivisor) == EQ);
+#endif
+ return highDivisor;
+}
+
+size_t GroebnerBasis::highBaseDivisorSlow(size_t newGenerator) const {
+ ASSERT(newGenerator < size());
+
+ size_t highDivisor = static_cast<size_t>(-1);
+ const_monomial leadNew = getLeadMonomial(newGenerator);
+ for (size_t i = 0; i < newGenerator; ++i) {
+ // continue if this generator would not be an improvement
+ // even if it does divide. This is a faster check than
+ // checking divisiblity, so do it first.
+ if (highDivisor != static_cast<size_t>(-1) &&
+ ratioCompare(highDivisor, i) == LT)
+ continue;
+ const_monomial leadi = getLeadMonomial(i);
+ if (ring().monomialIsDivisibleBy(leadNew, leadi))
+ highDivisor = i;
+ }
+ return highDivisor;
+}
+
+size_t GroebnerBasis::minimalLeadInSig(const_monomial sig) const {
+ ASSERT(! sig.isNull() );
+ const size_t component = ring().monomialGetComponent(sig);
+ const size_t minLeadGen = mSignatureLookup[component]->minimalLeadInSig(sig);
+ ASSERT(minLeadGen == minimalLeadInSigSlow(sig));
+ return minLeadGen;
+}
+
+size_t GroebnerBasis::minimalLeadInSigSlow(const_monomial sig) const {
+ monomial multiplier = ring().allocMonomial();
+ monomial minLead = ring().allocMonomial();
+
+ size_t minLeadGen = static_cast<size_t>(-1);
+ const int sigComponent = ring().monomialGetComponent(sig);
+ const size_t genCount = size();
+ for (size_t gen = 0; gen < genCount; ++gen) {
+ if (ring().monomialGetComponent(getSignature(gen)) != sigComponent)
+ continue;
+ if (!ring().monomialDivide(sig, getSignature(gen), multiplier))
+ continue;
+ if (minLeadGen != static_cast<size_t>(-1)) {
+ const_monomial genLead = getLeadMonomial(gen);
+ int leadCmp = order().signatureCompare(minLead, multiplier, genLead);
+ if (leadCmp == LT)
+ continue;
+ if (leadCmp == EQ) {
+ // If same lead monomial in signature, pick the one with fewer terms
+ // as that one might be less effort to reduce.
+ const size_t minTerms = poly(minLeadGen).nTerms();
+ const size_t terms = poly(gen).nTerms();
+ if (minTerms > terms)
+ continue;
+ if (minTerms == terms) {
+ // If same number of terms, pick the one with larger signature
+ // before being multiplied into the same signature. That one
+ // might be more reduced as the constraint on regular reduction
+ // is less.
+ const const_monomial minSig = getSignature(minLeadGen);
+ const const_monomial genSig = getSignature(gen);
+ int sigCmp = order().signatureCompare(minSig, genSig);
+ ASSERT(sigCmp != EQ); // no two generators have same signature
+ if (sigCmp == GT)
+ continue;
+ }
+ }
+ }
+
+ minLeadGen = gen;
+ ring().monomialMult(multiplier, getLeadMonomial(gen), minLead);
+ }
+ ring().freeMonomial(multiplier);
+ ring().freeMonomial(minLead);
+ return minLeadGen;
+}
+
+bool GroebnerBasis::isSingularTopReducible
+ (const Poly& poly, const_monomial sig) const {
+ ASSERT( ! sig.isNull() );
+ if (poly.isZero())
+ return false;
+
+ monomial multiplier = ring().allocMonomial();
+ const size_t genCount = size();
+ const_monomial polyLead = poly.getLeadMonomial();
+ for (size_t i = 0; i < genCount; ++i) {
+ if (!ring().monomialDivide(polyLead, getLeadMonomial(i), multiplier))
+ continue;
+ if (order().signatureCompare(sig, multiplier, getSignature(i)) == EQ)
+ return true;
+ }
+ ring().freeMonomial(multiplier);
+ return false;
+}
+
+void GroebnerBasis::display(std::ostream &o) const
+{
+ for (size_t i = 0; i<mBasis.size(); i++)
+ {
+ o << i << " ";
+ if (! mSignatures[i].isNull())
+ ring().monomialDisplay(o, mSignatures[i]);
+ if (!mBasis.retired(i)) {
+ o << " ";
+ mBasis.poly(i).display(o, false); // don't display component
+ }
+ o << std::endl;
+ }
+}
+
+void GroebnerBasis::displayBrief(std::ostream &o) const
+{
+ for (size_t i = 0; i<mBasis.size(); i++)
+ {
+ o << std::setw(4) << i << " ";
+ o << std::setw(4) << mBasis.usedAsStartCount(i) << " ";
+ o << std::setw(6) << mBasis.usedAsReducerCount(i) << " ";
+ o << std::setw(6) << mBasis.wasPossibleReducerCount(i) << " ";
+ o << std::setw(6) << mBasis.wasNonSignatureReducerCount(i) << " ";
+ o << std::setw(6) << mBasis.poly(i).nTerms() << " ";
+ ring().monomialDisplay(o, mSignatures[i]);
+ o << " ";
+ ring().monomialDisplay(o, mBasis.leadMonomial(i));
+ o << std::endl;
+ }
+}
+
+void GroebnerBasis::dump() const
+{
+ display(std::cerr);
+}
+
+size_t GroebnerBasis::getMemoryUse() const
+{
+ // Note: we do not count the signatures as they are counted elsewhere.
+ size_t total = 0;
+ total += mBasis.getMemoryUse();
+ total += mSignatures.capacity() * sizeof(mSignatures.front());
+ total += sigLeadRatio.capacity() * sizeof(sigLeadRatio.front());
+ total += mRatioRanks.capacity() * sizeof(mRatioRanks.front());
+ total += divisorLookup().getMemoryUse();
+ total += mMinimalDivisorLookup->getMemoryUse();
+
+ // This is an estimate of how much memory mRatioSorted uses per item.
+ // It is based on assuming a tree representation with a left pointer,
+ // a right pointer and a data member for each node. This is probably
+ // an underestimate.
+ const size_t perItemOverhead =
+ 2 * sizeof(void*) + sizeof(*mRatioSorted.begin());
+ total += mRatioSorted.size() * perItemOverhead;
+
+ return total;
+}
+
+size_t GroebnerBasis::ratioRank(const_monomial ratio) const {
+ ASSERT(mUseRatioRank);
+ const size_t index = size();
+ if (index == 0)
+ return 0; // any value will do as there is nothing to compare to
+ std::vector<monomial>& sigLeadRatioNonConst =
+ const_cast<std::vector<monomial>&>(sigLeadRatio);
+
+ sigLeadRatioNonConst.push_back(ratio.castAwayConst());
+ RatioSortedType::iterator pos = mRatioSorted.lower_bound(index);
+ sigLeadRatioNonConst.pop_back();
+
+ if (pos == mRatioSorted.end()) {
+ ASSERT(ratioRank(*mRatioSorted.rbegin()) <
+ std::numeric_limits<Rank>::max());
+ return std::numeric_limits<Rank>::max();
+ } else {
+ if (order().signatureCompare(ratio, getSigLeadRatio(*pos)) == EQ)
+ return ratioRank(*pos);
+ ASSERT(ratioRank(*pos) > 0);
+#ifdef DEBUG
+ if (pos != mRatioSorted.begin()) {
+ RatioSortedType::iterator prev = pos;
+ --prev;
+ ASSERT(ratioRank(*pos) - 1 > ratioRank(*prev));
+ }
+#endif
+ return ratioRank(*pos) - 1;
+ }
+}
+
+GroebnerBasis::StoredRatioCmp::StoredRatioCmp(
+ const_monomial numerator,
+ const_monomial denominator,
+ const GroebnerBasis& basis):
+ mBasis(basis)
+{
+ const PolyRing& ring = basis.ring();
+ mRatio = ring.allocMonomial();
+ ring.monomialDivideToNegative(numerator, denominator, mRatio);
+
+ if (GroebnerBasis::mUseRatioRank) {
+ mRatioRank = basis.ratioRank(mRatio);
+ mTmp = 0;
+ } else
+ mTmp = mBasis.ring().allocMonomial();
+}
+
+GroebnerBasis::StoredRatioCmp::~StoredRatioCmp() {
+ mBasis.ring().freeMonomial(mRatio);
+ if (!GroebnerBasis::mUseRatioRank)
+ mBasis.ring().freeMonomial(mTmp);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/GroebnerBasis.hpp b/src/mathicgb/GroebnerBasis.hpp
new file mode 100644
index 0000000..0908696
--- /dev/null
+++ b/src/mathicgb/GroebnerBasis.hpp
@@ -0,0 +1,248 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _groebner_basis_h_
+#define _groebner_basis_h_
+
+#include <vector>
+#include <set>
+#include "PolyRing.hpp"
+#include "Poly.hpp"
+#include "FreeModuleOrder.hpp"
+#include "DivisorLookup.hpp"
+#include "PolyBasis.hpp"
+
+#ifndef USE_RATIO_RANK
+#define USE_RATIO_RANK true
+#endif
+
+class GroebnerBasis {
+public:
+ GroebnerBasis(
+ const PolyRing* R,
+ FreeModuleOrder* order,
+ int divisorLookupType,
+ int monTableType,
+ bool preferSparseReducers
+ );
+ ~GroebnerBasis();
+
+ const PolyRing& ring() const {return mBasis.ring();}
+ const Poly& poly(size_t index) const {return mBasis.poly(index);}
+ size_t size() const {return mBasis.size();}
+ const FreeModuleOrder& order() const {return mBasis.order();}
+
+ // todo: stop using non-const version of basis() and then remove it.
+ PolyBasis& basis() {return mBasis;}
+ const PolyBasis& basis() const {return mBasis;}
+
+ const_monomial getLeadMonomial(size_t i) const {
+ return mBasis.leadMonomial(i);
+ }
+
+ coefficient getLeadCoefficient(size_t i) const {
+ return mBasis.leadCoefficient(i);
+ }
+
+ const_monomial getSigLeadRatio(size_t i) const {
+ ASSERT(i < size());
+ return sigLeadRatio[i];
+ }
+
+ // Signifies that the module has taken on another e_i.
+ // Must call this before adding a polynomial to the basis with
+ // a signature in the new component.
+ void addComponent();
+
+ // Takes over ownership of sig and f. sig must come from the pool
+ // of ring() and f must have been allocated with new.
+ void insert(monomial sig, std::auto_ptr<Poly> f);
+
+ const_monomial getSignature(size_t i) const {
+ ASSERT(i < size());
+ return mSignatures[i];
+ }
+
+ // Returns the index of a basis element that regular reduces term in
+ // signature sig. Returns -1 if no such element exists. A basis element
+ // u is a regular reducer if leadTerm(u) divides term
+ // and (term / leadTerm(u)) * signature(u) < sig.
+ size_t regularReducer(const_monomial sig, const_monomial term) const;
+
+ // Uses the functionality in the divisor finder for
+ // computing up to maxDivisors low ratio base divisors.
+ // The divisors are placed into divisors.
+ void lowBaseDivisors(
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator) const;
+
+ // Uses the functionality in the divisor finder for
+ // computing a high base divisor. Returns the index
+ // of the divisor or -1 if none are found.
+ size_t highBaseDivisor(size_t newGenerator) const;
+
+ // Find the basis element g_i whose signature S divides sig
+ // such that (S/sig)g_i has minimal leading term. Returns i.
+ size_t minimalLeadInSig(const_monomial sig) const;
+
+ // Returns true if poly can be singular reduced in signature sig.
+ // In other words, returns true if there is a basis element with lead
+ // term M and signature S such that M divides the lead term N of
+ // poly and such that N/M*S == sig.
+ bool isSingularTopReducible(const Poly& poly, const_monomial sig) const;
+
+ void display(std::ostream &o) const;
+ void displayBrief(std::ostream &o) const;
+ void dump() const;
+ size_t getMemoryUse() const;
+
+ // Compares the signature/lead ratio of basis element a to basis element b
+ // and returns LT, EQ or GT.
+ inline int ratioCompare(size_t a, size_t b) const;
+
+ class StoredRatioCmp {
+ public:
+ // Stores the ratio numerator/denominator and prepares it for comparing
+ // to the sig/lead ratios in basis.
+ StoredRatioCmp(
+ const_monomial numerator,
+ const_monomial denominator,
+ const GroebnerBasis& basis);
+ ~StoredRatioCmp();
+
+ // compares the stored ratio to the basis element with index be.
+ inline int compare(size_t be) const;
+
+ private:
+ StoredRatioCmp(const StoredRatioCmp&); // not available
+ void operator=(const StoredRatioCmp&); // not available
+
+ const GroebnerBasis& mBasis;
+ size_t mRatioRank;
+ monomial mRatio;
+ mutable monomial mTmp;
+ };
+
+private:
+ // Slow versions use simpler code. Used to check results in debug mode.
+ size_t regularReducerSlow(const_monomial sig, const_monomial term) const;
+ size_t minimalLeadInSigSlow(const_monomial sig) const;
+ size_t highBaseDivisorSlow(size_t newGenerator) const;
+ void lowBaseDivisorsSlow(
+ std::vector<size_t>& divisors,
+ size_t maxDivisors,
+ size_t newGenerator) const;
+
+ friend class StoredRatioCmp;
+
+ const DivisorLookup& divisorLookup() const {
+ return mBasis.divisorLookup();
+ }
+
+ std::auto_ptr<DivisorLookup::Factory const> const mDivisorLookupFactory;
+
+ // may change at next insert!
+ size_t ratioRank(size_t index) const {
+ ASSERT(index < size());
+ return mRatioRanks[index];
+ }
+
+ // Only useful for comparing to basis elements. Two ratios might get the same
+ // rank without being equal. The proper rank may change when a new generator
+ // is added.
+ size_t ratioRank(const_monomial ratio) const;
+
+ std::vector<monomial> mSignatures;
+
+ // the ratio signature/initial term including negative entries and module component
+ std::vector<monomial> sigLeadRatio;
+
+ // true if giving each generator an integer id based on its
+ // position in a sorted order of sig/lead ratios.
+ static const bool mUseRatioRank = USE_RATIO_RANK;
+ static const bool mUseStoredRatioRank = USE_RATIO_RANK;
+ class RatioOrder {
+ public:
+ RatioOrder(std::vector<monomial>& ratio, const FreeModuleOrder& order):
+ mRatio(ratio), mOrder(order) {}
+ bool operator()(size_t a, size_t b) const {
+ return mOrder.signatureCompare(mRatio[a], mRatio[b]) == LT;
+ }
+ private:
+ std::vector<monomial>& mRatio;
+ const FreeModuleOrder& mOrder;
+ };
+ typedef std::multiset<size_t, RatioOrder> RatioSortedType;
+ typedef size_t Rank;
+ RatioSortedType mRatioSorted;
+ std::vector<Rank> mRatioRanks;
+
+ std::vector<DivisorLookup*> mSignatureLookup;
+
+ // contains those lead terms that are minimal
+ std::auto_ptr<DivisorLookup> const mMinimalDivisorLookup;
+
+ PolyBasis mBasis;
+ bool const mPreferSparseReducers;
+ mutable monomial mTmp;
+};
+
+inline int GroebnerBasis::ratioCompare(size_t a, size_t b) const {
+ if (mUseRatioRank) {
+#ifdef DEBUG
+ int const value =
+ order().signatureCompare(getSigLeadRatio(a), getSigLeadRatio(b));
+#endif
+ if (mRatioRanks[a] < mRatioRanks[b]) {
+ ASSERT(value == LT);
+ return LT;
+ } else if (mRatioRanks[a] > mRatioRanks[b]) {
+ ASSERT(value == GT);
+ return GT;
+ } else {
+ ASSERT(value == EQ);
+ return EQ;
+ }
+ } else {
+ // A/a < B/b <=> A < (B/b)a
+ ring().monomialDivideToNegative(getSignature(b), getLeadMonomial(b), mTmp);
+ ring().monomialMultTo(mTmp, getLeadMonomial(a));
+ int value = order().signatureCompare(getSignature(a), mTmp);
+ ASSERT(value ==
+ order().signatureCompare(getSigLeadRatio(a), getSigLeadRatio(b)));
+ return value;
+ }
+}
+
+inline int GroebnerBasis::StoredRatioCmp::compare(size_t be) const {
+ if (GroebnerBasis::mUseStoredRatioRank) {
+#ifdef DEBUG
+ int value = mBasis.order().
+ signatureCompare(mRatio, mBasis.getSigLeadRatio(be));
+#endif
+ GroebnerBasis::Rank otherRank = mBasis.ratioRank(be);
+ if (mRatioRank < otherRank) {
+ ASSERT(value == LT);
+ return LT;
+ } else if (mRatioRank > otherRank) {
+ ASSERT(value == GT);
+ return GT;
+ } else {
+ ASSERT(value == EQ);
+ return EQ;
+ }
+ } else {
+ mBasis.ring().monomialMult(mRatio, mBasis.getLeadMonomial(be), mTmp);
+ int value = mBasis.order().signatureCompare(mTmp, mBasis.getSignature(be));
+ ASSERT(value ==
+ mBasis.order().signatureCompare(mRatio, mBasis.getSigLeadRatio(be)));
+ return value;
+ }
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/HashTourReducer.cpp b/src/mathicgb/HashTourReducer.cpp
new file mode 100644
index 0000000..c06c814
--- /dev/null
+++ b/src/mathicgb/HashTourReducer.cpp
@@ -0,0 +1,217 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#include "stdinc.h"
+#include "HashTourReducer.hpp"
+
+#include <utility>
+
+extern int tracingLevel;
+
+HashTourReducer::HashTourReducer(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring)),
+ mHashTable(&ring, 10),
+ mPool(sizeof(MultipleWithPos))
+{
+}
+
+class HashTourReducer::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(MultipleWithPos* entry) {
+ entry->destroy(mRing);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+HashTourReducer::~HashTourReducer()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+void HashTourReducer::insertTail(const_term multiple, const Poly* poly)
+{
+ ASSERT(poly != 0);
+ ASSERT(&poly->ring() == &mRing);
+ if (poly->nTerms() < 2)
+ return;
+ MultipleWithPos* entry =
+ new (mPool.alloc()) MultipleWithPos(*poly, multiple);
+ ++entry->pos;
+ insertEntry(entry);
+}
+
+void HashTourReducer::insert(monomial multiple, const Poly* poly)
+{
+ ASSERT(poly != 0);
+ ASSERT(&poly->ring() == &mRing);
+ if (poly->isZero())
+ return;
+ term termMultiple(1, multiple);
+ insertEntry(new (mPool.alloc()) MultipleWithPos(*poly, termMultiple));
+}
+
+namespace {
+ const_term allocTerm(const PolyRing& ring, const_term term) {
+ monomial mono = ring.allocMonomial();
+ ring.monomialCopy(term.monom, mono);
+ return const_term(term.coeff, mono);
+ }
+}
+
+HashTourReducer::MultipleWithPos::MultipleWithPos
+(const Poly& poly, const_term multiple):
+ pos(poly.begin()),
+ end(poly.end()),
+ multiple(allocTerm(poly.ring(), multiple)),
+ current(poly.ring().allocMonomial()),
+ node(0) {}
+
+void HashTourReducer::MultipleWithPos::
+computeCurrent(const PolyRing& ring, monomial current) {
+ ring.monomialMult(multiple.monom, pos.getMonomial(), current);
+}
+
+void HashTourReducer::MultipleWithPos::currentCoefficient
+(const PolyRing& ring, coefficient& coeff) {
+ ring.coefficientMult(multiple.coeff, pos.getCoefficient(), coeff);
+}
+
+void HashTourReducer::MultipleWithPos::destroy(const PolyRing& ring) {
+ ring.freeMonomial(current);
+ ring.freeMonomial(const_cast<ConstMonomial&>(multiple.monom).castAwayConst());
+
+ // Call the destructor to destruct the iterators into std::vector.
+ // In debug mode MSVC puts those in a linked list and the destructor
+ // has to be called since it takes an iterator off the list. We had
+ // memory corruption problems before doing this.
+ this->~MultipleWithPos();
+}
+
+bool HashTourReducer::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ MultipleWithPos* entry = mQueue.top();
+ ASSERT(entry != 0);
+
+ // remove node from hash table first since we are going to be changing
+ // the monomial after this, and if we do that before the hash value will
+ // change.
+ mHashTable.remove(entry->node);
+
+ // extract information into mLeadTerm
+ mLeadTerm.monom.swap(entry->current);
+ entry->node->monom = entry->current;
+ mLeadTerm.coeff = entry->node->coeff;
+
+ // remove old monomial from hash table and insert next
+ ASSERT(entry->pos != entry->end);
+ while (true) {
+ ++entry->pos;
+ if (entry->pos == entry->end) {
+ mQueue.pop();
+ entry->destroy(mRing);
+ mPool.free(entry);
+ break;
+ }
+ term t;
+ t.monom = entry->current;
+ entry->computeCurrent(mRing, t.monom);
+ entry->currentCoefficient(mRing, t.coeff);
+
+ std::pair<bool, PolyHashTable::node*> p = mHashTable.insert(t);
+ if (p.first) {
+ entry->node = p.second;
+ mQueue.decreaseTop(entry);
+ break;
+ }
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+void HashTourReducer::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+void HashTourReducer::insertEntry(MultipleWithPos* entry) {
+ ASSERT(entry != 0);
+ for (; entry->pos != entry->end; ++entry->pos) {
+ term t;
+ t.monom = entry->current;
+ entry->computeCurrent(mRing, t.monom);
+ entry->currentCoefficient(mRing, t.coeff);
+
+ std::pair<bool, PolyHashTable::node*> p = mHashTable.insert(t);
+ if (p.first) {
+ mLeadTermKnown = false;
+ entry->node = p.second;
+ mQueue.push(entry);
+ return;
+ }
+ }
+ entry->destroy(mRing);
+ mPool.free(entry);
+}
+
+void HashTourReducer::value(Poly &result)
+{
+ const_term t;
+ while (findLeadTerm(t)) {
+ result.appendTerm(t.coeff, t.monom);
+ removeLeadTerm();
+ }
+ resetReducer();
+}
+
+void HashTourReducer::resetReducer()
+{
+ mLeadTermKnown = false;
+ MonomialFree freeer(mRing);
+ mQueue.forAll(freeer);
+ mQueue.clear();
+ mHashTable.reset();
+}
+
+size_t HashTourReducer::getMemoryUse() const
+{
+ return mQueue.getMemoryUse() +
+ mPool.getMemoryUse() +
+ mHashTable.getMemoryUse();
+}
+
+void HashTourReducer::dump() const
+{
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
+
diff --git a/src/mathicgb/HashTourReducer.hpp b/src/mathicgb/HashTourReducer.hpp
new file mode 100644
index 0000000..aea3fbb
--- /dev/null
+++ b/src/mathicgb/HashTourReducer.hpp
@@ -0,0 +1,88 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _hash_tour_reducer_h_
+#define _hash_tour_reducer_h_
+
+#include "Reducer.hpp"
+#include "PolyHashTable.hpp"
+#include <mathic.h>
+#include <memtailor.h>
+
+class HashTourReducer : public Reducer {
+public:
+ HashTourReducer(const PolyRing& R);
+ virtual ~HashTourReducer();
+
+ virtual std::string description() const {
+ return "hashed tournament tree reducer";
+ }
+
+ virtual void insertTail(const_term multiplier, const Poly *f);
+ virtual void insert(monomial multiplier, const Poly *f);
+
+ virtual bool findLeadTerm(const_term &result);
+ virtual void removeLeadTerm();
+
+ virtual void value(Poly &result); // keep extracting lead term until done
+
+ virtual size_t getMemoryUse() const;
+
+ virtual void dump() const; // Used for debugging
+
+protected:
+ virtual void resetReducer();
+
+private:
+ // Represents a term multiple of a polynomial,
+ // together with a current term of the multiple.
+ struct MultipleWithPos {
+ MultipleWithPos(const Poly& poly, const_term multiple);
+
+ Poly::const_iterator pos;
+ Poly::const_iterator const end;
+ const_term const multiple;
+ monomial current; // multiple.monom * pos.getMonomial()
+ PolyHashTable::node* node;
+
+ void computeCurrent(const PolyRing& ring, monomial current);
+ void currentCoefficient(const PolyRing& ring, coefficient& coeff);
+ void destroy(const PolyRing& ring);
+ };
+
+ class Configuration {
+ public:
+ typedef MultipleWithPos* Entry;
+
+ Configuration(const PolyRing& ring) : mRing(ring) {}
+
+ typedef bool CompareResult;
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return mRing.monomialLT(a->current, b->current);
+ }
+ bool cmpLessThan(CompareResult r) const {return r;}
+
+ static const bool fastIndex = true;
+
+ private:
+ const PolyRing& mRing;
+ };
+
+ class MonomialFree;
+
+ void insertEntry(MultipleWithPos* entry);
+ void insert(const_term multiplier, Poly::iterator first, Poly::iterator last);
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ mic::TourTree<Configuration> mQueue;
+ PolyHashTable mHashTable;
+ memt::BufferPool mPool;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/Ideal.cpp b/src/mathicgb/Ideal.cpp
new file mode 100644
index 0000000..e5cc08c
--- /dev/null
+++ b/src/mathicgb/Ideal.cpp
@@ -0,0 +1,73 @@
+// Copyright 2011 Michael E. Stillman
+#include "stdinc.h"
+#include "PolyRing.hpp"
+
+#include "Poly.hpp"
+#include "FreeModuleOrder.hpp"
+#include "Ideal.hpp"
+#include <ostream>
+#include <istream>
+#include <iostream>
+#include <cctype>
+
+Ideal::~Ideal()
+{
+ for (size_t i = 0; i<mGenerators.size(); i++)
+ delete mGenerators[i];
+}
+
+void Ideal::insert(std::auto_ptr<Poly> p) {
+ mGenerators.reserve(mGenerators.size() + 1);
+ mGenerators.push_back(p.release());
+}
+
+namespace {
+ class IdealSort {
+ public:
+ IdealSort(const FreeModuleOrder& order): mOrder(order) {}
+ bool operator()(const Poly* a, const Poly* b) {
+ return mOrder.signatureCompare
+ (a->getLeadMonomial(), b->getLeadMonomial()) == LT;
+ }
+
+ private:
+ const FreeModuleOrder& mOrder;
+ };
+}
+
+void Ideal::sort(FreeModuleOrder& order) {
+ IdealSort cmp(order);
+ std::sort(mGenerators.begin(), mGenerators.end(), cmp);
+}
+
+Ideal *Ideal::parse(std::istream &i)
+{
+ PolyRing *R = PolyRing::read(i);
+ size_t npolys;
+ i >> npolys;
+ Ideal *result = new Ideal(*R);
+ for (size_t j= 0; j<npolys; j++)
+ {
+ Poly *g = new Poly(R);
+ while (std::isspace(i.peek())) i.get();
+ g->parse(i);
+ result->mGenerators.push_back(g);
+ }
+ return result;
+}
+void Ideal::display(std::ostream &o, bool print_comp) const
+{
+ mRing.write(o);
+ o << std::endl;
+ o << mGenerators.size() << std::endl;
+ for (size_t i = 0; i<mGenerators.size(); i++)
+ {
+ mGenerators[i]->display(o, print_comp);
+ o << std::endl;
+ }
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/Ideal.hpp b/src/mathicgb/Ideal.hpp
new file mode 100644
index 0000000..24e86f0
--- /dev/null
+++ b/src/mathicgb/Ideal.hpp
@@ -0,0 +1,44 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _ideal_h_
+#define _ideal_h_
+
+#include <memory>
+#include <algorithm>
+#include "PolyRing.hpp"
+
+class Poly;
+class FreeModuleOrder;
+
+class Ideal {
+ // Really: a list of polynomials
+ // BUT ALSO maybe: includes memory areas for the polynomials?
+public:
+ Ideal(const PolyRing &R) : mRing(R) {}
+ ~Ideal();
+
+ void insert(std::auto_ptr<Poly> p);
+
+ static Ideal *parse(std::istream &i); // reads ring, #gens, each generator in turn
+ void display(std::ostream &o, bool print_comp) const; // inverse operation
+
+ const PolyRing & ring() const { return mRing; }
+
+ const PolyRing *getPolyRing() const { return &mRing; }
+ const std::vector<Poly *> & viewGenerators() { return mGenerators; }
+ const Poly *getPoly(size_t i) const { return mGenerators[i]; }
+ size_t size() const { return mGenerators.size(); }
+
+ void sort(FreeModuleOrder& order);
+
+private:
+ const PolyRing& mRing;
+ std::vector<Poly*> mGenerators;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/KoszulQueue.cpp b/src/mathicgb/KoszulQueue.cpp
new file mode 100644
index 0000000..6807fe1
--- /dev/null
+++ b/src/mathicgb/KoszulQueue.cpp
@@ -0,0 +1,12 @@
+#include "stdinc.h"
+#include "KoszulQueue.hpp"
+#include "FreeModuleOrder.hpp"
+
+KoszulQueue::Configuration::CompareResult KoszulQueue::Configuration::compare(const Entry& a, const Entry& b) const {
+ return mOrder->signatureCompare(a,b);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/KoszulQueue.hpp b/src/mathicgb/KoszulQueue.hpp
new file mode 100644
index 0000000..2871e3f
--- /dev/null
+++ b/src/mathicgb/KoszulQueue.hpp
@@ -0,0 +1,89 @@
+#ifndef koszul_queue_guard
+#define koszul_queue_guard
+
+#include <memtailor.h>
+#include <mathic.h>
+#include "PolyRing.hpp"
+
+class FreeModuleOrder;
+
+class KoszulQueue
+{
+public:
+ KoszulQueue(const FreeModuleOrder *order,
+ memt::BufferPool &pool) :
+ mQueue(Configuration(order, pool))
+ {
+ }
+
+ const_monomial top() const {
+ ASSERT(!empty());
+ return mQueue.top();
+ }
+
+ monomial popRelease() {
+ ASSERT(!empty());
+ return mQueue.pop();
+ }
+
+ void pop() {
+ ASSERT(!empty());
+ mQueue.getConfiguration().getPool().free(popRelease().unsafeGetRepresentation());
+ }
+
+ void push(monomial sig) {
+ //TODO: ASSERT(mPool.member(sig));
+ mQueue.push(sig);
+ }
+
+ bool empty() const { return mQueue.empty(); }
+
+ size_t size() const { return mQueue.size(); }
+
+ size_t getMemoryUse() const {return mQueue.getMemoryUse();}
+
+private:
+ class Configuration
+ {
+ public:
+ typedef monomial Entry;
+
+ Configuration(const FreeModuleOrder *order,
+ memt::BufferPool &pool) :
+ mOrder(order),
+ mPool(pool)
+ {
+ ASSERT(mOrder != 0);
+ }
+
+ memt::BufferPool &getPool() { return mPool; }
+
+ typedef int CompareResult; /* LT, EQ, GT */
+
+ CompareResult compare(const Entry& a, const Entry& b) const;
+
+ bool cmpLessThan(CompareResult r) const {return r == GT;}
+
+ static const bool fastIndex = false;
+ static const bool supportDeduplication = true;
+ bool cmpEqual(CompareResult r) const {return r == EQ;}
+
+ Entry deduplicate(Entry a, Entry b)
+ {
+ mPool.free(b.unsafeGetRepresentation());
+ return a;
+ }
+ private:
+ const FreeModuleOrder *mOrder;
+ memt::BufferPool &mPool; // for signature monomials
+ };
+
+ mic::Heap<Configuration> mQueue;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MTArray.cpp b/src/mathicgb/MTArray.cpp
new file mode 100644
index 0000000..92377d7
--- /dev/null
+++ b/src/mathicgb/MTArray.cpp
@@ -0,0 +1,254 @@
+#include "stdinc.h"
+#include "MTArray.hpp"
+
+#include "MonTableNaive.hpp"
+#include "MonTableKDTree.hpp"
+#include "MonTableDivList.hpp"
+
+template <typename MT>
+class MTArrayT : public MonomialTableArray
+{
+ typedef MT T;
+ typedef typename MT::Configuration Conf;
+ typedef MonomialTableArray::V V; // value type
+public:
+ MTArrayT(size_t components, const Conf &conf):
+ MonomialTableArray(conf.getPolyRing()), conf_(conf) {
+ for (size_t i = 0; i < components; ++i)
+ addComponent();
+ }
+
+ ~MTArrayT()
+ {
+ for (size_t i = 0; i<tables.size(); i++)
+ delete tables[i];
+ }
+
+ Conf& getConfiguration() { return conf_; }
+ const Conf &getConfiguration() const { return conf_; }
+
+ bool insert(const_monomial m, V val);
+ // Only inserts if minimal, in this case "true" is returned.
+ // and the object takes ownership of 'm'.
+
+ void addComponent() {tables.push_back(new T(conf_));}
+
+ bool member(const_monomial m, V &result);
+
+ void getStats(MonomialTableArray::Stats &stats) const;
+
+ std::string description() const;
+
+ void displayStats(std::ostream &o) const;
+
+ void display(std::ostream &o, int level) const;
+
+ virtual void printFrobbyM2Format
+ (std::ostream& out, size_t component) const;
+
+ void dump(int level=0) const;
+
+ size_t n_elems() const;
+
+ size_t getMemoryUse() const;
+private:
+ Conf conf_; // Used to create new instances of T.
+ std::vector<T *> tables;
+
+ mutable MonomialTableArray::Stats stats_;
+};
+
+template <typename MT>
+bool MTArrayT<MT>::insert(const_monomial m, V val)
+{
+ stats_.n_calls_insert++;
+ size_t x = R->monomialGetComponent(m);
+ ASSERT(x < tables.size());
+ ASSERT(tables[x] != 0);
+ bool result = tables[x]->insert(m, val);
+ if (result) stats_.n_actual_inserts++;
+ return result;
+}
+
+template <typename MT>
+bool MTArrayT<MT>::member(const_monomial m, V &result)
+{
+ stats_.n_calls_member++;
+ size_t x = R->monomialGetComponent(m);
+ ASSERT(x < tables.size());
+ ASSERT(tables[x] != 0);
+ return tables[x]->member(m, result);
+}
+
+template <typename MT>
+void MTArrayT<MT>::getStats(Stats &stats) const
+{
+ stats = stats_;
+
+ // compute denseness
+ std::vector<const_monomial> monomials;
+ unsigned long long support = 0;
+ unsigned long long exponentCount = 0;
+ for (size_t i=0; i<tables.size(); i++) {
+ T *p = tables[i];
+ ASSERT(p != 0);
+ monomials.clear();
+ p->getMonomials(monomials);
+ typedef std::vector<const_monomial>::const_iterator iter;
+ for (iter it = monomials.begin(); it != monomials.end(); ++it) {
+ support += R->monomialSizeOfSupport(*it);
+ exponentCount += R->getNumVars();
+ }
+ }
+ stats.denseness = static_cast<double>(support) / exponentCount;
+}
+
+template <typename MT>
+void MTArrayT<MT>::displayStats(std::ostream & /* o */) const
+{
+}
+
+template <typename MT>
+void MTArrayT<MT>::display(std::ostream &o, int level) const
+{
+ std::vector<const_monomial> monomials;
+ for (size_t i=0; i<tables.size(); i++)
+ {
+ T *p = tables[i];
+ ASSERT(p != 0);
+ if (p->n_elems() == 0)
+ continue;
+
+ o << " " << i << ": ";
+ monomials.clear();
+ p->getMonomials(monomials);
+ std::sort(monomials.begin(), monomials.end(),
+ MonomialTableArray::MonomialCompare(*R));
+ typedef std::vector<const_monomial>::const_iterator iter;
+ for (iter it = monomials.begin(); it != monomials.end(); ++it)
+ {
+ R->monomialDisplay(o, *it, false);
+ o << " ";
+ }
+ o << '\n';
+ }
+}
+
+template <typename MT>
+void MTArrayT<MT>::printFrobbyM2Format
+ (std::ostream& out, size_t component) const
+{
+ R->printRingFrobbyM2Format(out);
+
+ ASSERT(component < tables.size());
+ T* p = tables[component];
+ ASSERT(p != 0);
+ std::vector<const_monomial> monomials;
+ p->getMonomials(monomials);
+ std::sort(monomials.begin(), monomials.end(),
+ MonomialTableArray::MonomialCompare(*R));
+
+ out << "I = monomialIdeal(\n";
+ typedef std::vector<const_monomial>::const_iterator iter;
+ for (iter it = monomials.begin(); it != monomials.end(); ++it) {
+ if (it != monomials.begin())
+ out << ",\n";
+ R->printMonomialFrobbyM2Format(out, *it);
+ }
+ out << "\n);\n";
+}
+
+template <typename MT>
+void MTArrayT<MT>::dump(int level) const
+{
+ // display on stderr the table.
+ displayStats(std::cerr);
+ if (level > 0) display(std::cerr, level-1);
+}
+
+template <typename MT>
+size_t MTArrayT<MT>::n_elems() const
+{
+ size_t result = 0;
+ for (size_t i=0; i<tables.size(); i++)
+ if (tables[i] != 0)
+ result += tables[i]->n_elems();
+ return result;;
+}
+
+template <typename MT>
+size_t MTArrayT<MT>::getMemoryUse() const
+{
+ size_t sum = tables.capacity() * sizeof(tables.front());
+ for (size_t i = 0; i < tables.size(); ++i) {
+ ASSERT(tables[i] != 0);
+ sum += sizeof(tables[i]) + tables[i]->getMemoryUse();
+ }
+ return sum;
+}
+
+template <typename MT>
+std::string MTArrayT<MT>::description() const
+{
+ T obj(getConfiguration());
+ return obj.getName();
+}
+
+int MonomialTableArray::displayMTTypes(std::ostream &o)
+ // returns n s.t. 0..n-1 are valid types
+{
+ o << "Monomial table types:" << std::endl;
+ o << " 0 naive" << std::endl;
+ o << " 1 divlist+divmask" << std::endl;
+ o << " 2 kdtree+divmask" << std::endl;
+ o << " 3 divlist" << std::endl;
+ o << " 4 kdtree" << std::endl;
+ return 5;
+}
+
+MonomialTableArray * MonomialTableArray::make(const PolyRing *R, int typ, size_t components, bool allowRemovals)
+{
+ switch (typ) {
+ case 0:
+ return new MTArrayT<MonTableNaive>(components, R);
+
+ case 1: {
+ typedef MonTableDivList<false,true> MT;
+ return new MTArrayT<MT>(components, MT::Configuration
+ (R, true, false, true, .5, 500));
+ }
+ case 2: {
+ if (allowRemovals) {
+ typedef MonTableKDTree<true,true,1,true> MT;
+ return new MTArrayT<MT>(components, MT::Configuration
+ (R, true, false, true, .5, 50));
+ } else {
+ typedef MonTableKDTree<true,true,1,false> MT;
+ return new MTArrayT<MT>(components, MT::Configuration
+ (R, true, false, true, .5, 50));
+ }
+ }
+ case 3: {
+ typedef MonTableDivList<false,false> MT;
+ return new MTArrayT<MT>(components, MT::Configuration
+ (R, true, false, true, .5, 500));
+ }
+ case 4: // fall through
+ default: {
+ if (allowRemovals) {
+ typedef MonTableKDTree<false,false,1,true> MT;
+ return new MTArrayT<MT>(components, MT::Configuration
+ (R, true, false, true, .5, 50));
+ } else {
+ typedef MonTableKDTree<false,false,1,false> MT;
+ return new MTArrayT<MT>(components, MT::Configuration
+ (R, true, false, true, .5, 50));
+ }
+ }
+ }
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MTArray.hpp b/src/mathicgb/MTArray.hpp
new file mode 100644
index 0000000..2ba2d98
--- /dev/null
+++ b/src/mathicgb/MTArray.hpp
@@ -0,0 +1,104 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _monomial_module_h_
+#define _monomial_module_h_
+
+#include "PolyRing.hpp"
+#include <vector>
+#include <iostream>
+#include <algorithm>
+
+class MonomialTableArray
+{
+public:
+ typedef size_t V; // value type
+
+ static MonomialTableArray *makeMonomialTableArray(int type, // 0=naive, 1=DivList, 2=KDTree
+ const PolyRing *R,
+ // for each type, only some of the following are relevant
+ bool use_cache,
+ bool remin);
+
+ MonomialTableArray(const PolyRing *R0) : R(R0) {}
+ virtual ~MonomialTableArray() {};
+
+ virtual bool insert(const_monomial m, V val) = 0;
+ // returns true if the monomial actually needs to be inserted.
+ // If the monomial is inserted, the caller agrees to keep that monomial
+ // active until it is removed from the table.
+ //TODO: At some point: deal with removals too
+
+ // Adds a new component, increasing the number of monomial tables
+ // in the array by one. Its index is one more than the previous
+ // maximum index.
+ virtual void addComponent() = 0;
+
+ bool member(const_monomial m) {
+ size_t dummy;
+ return member(m, dummy);
+ }
+ virtual bool member(const_monomial m, V &result) = 0;
+
+ struct Stats {
+ size_t n_actual_inserts;
+ size_t n_calls_member;
+ size_t n_calls_insert;
+ size_t n_compares;
+
+ // ratio of non-zero exponents. 0.75 means a
+ // quarter of all exponents are zero.
+ double denseness;
+
+ Stats() : n_actual_inserts(0),
+ n_calls_member(0),
+ n_calls_insert(0),
+ n_compares(0),
+ denseness(0.0) {}
+ };
+
+ virtual void getStats(Stats &stats) const = 0;
+
+ virtual void display(std::ostream &o, int level) const = 0;
+
+ virtual void printFrobbyM2Format
+ (std::ostream& out, size_t component) const = 0;
+
+ virtual void displayStats(std::ostream &o) const = 0;
+
+ virtual size_t n_elems() const = 0;
+
+ void dump(int level=0) const
+ {
+ // display on stderr the table.
+ displayStats(std::cerr);
+ if (level > 0) display(std::cerr, level-1);
+ }
+
+ virtual std::string description() const = 0;
+
+ virtual size_t getMemoryUse() const = 0;
+
+ // Choosing one
+ static int displayMTTypes(std::ostream &o); // returns n s.t. 0..n-1 are valid types
+ static MonomialTableArray* make(const PolyRing *R, int typ, size_t components, bool allowRemovals);
+
+protected:
+ class MonomialCompare {
+ public:
+ MonomialCompare(const PolyRing& ring): mRing(ring) {}
+ bool operator()(const_monomial a, const_monomial b) {
+ return mRing.monomialLT(a, b);
+ }
+ private:
+ const PolyRing& mRing;
+ };
+
+ const PolyRing *R;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonTableDivList.cpp b/src/mathicgb/MonTableDivList.cpp
new file mode 100644
index 0000000..4fb028d
--- /dev/null
+++ b/src/mathicgb/MonTableDivList.cpp
@@ -0,0 +1,7 @@
+#include "stdinc.h"
+#include "MonTableDivList.hpp"
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonTableDivList.hpp b/src/mathicgb/MonTableDivList.hpp
new file mode 100644
index 0000000..17ad7be
--- /dev/null
+++ b/src/mathicgb/MonTableDivList.hpp
@@ -0,0 +1,287 @@
+#ifndef DIV_ARRAY_MODEL_GUARD
+#define DIV_ARRAY_MODEL_GUARD
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include <mathic.h>
+#include "PolyRing.hpp"
+
+/** Helper class for MonTableDivList. */
+template<bool UseLinkedList, bool UseDivMask>
+class MonTableDivListConfiguration;
+
+template<bool ULL, bool UDM>
+class MonTableDivListConfiguration {
+public:
+ typedef int Exponent;
+ typedef const_monomial Monomial;
+ typedef Monomial Entry;
+
+ MonTableDivListConfiguration
+ (const PolyRing *R0,
+ bool minimizeOnInsert,
+ bool moveDivisorToFront,
+ bool sortOnInsert,
+ double rebuildRatio,
+ size_t minRebuild):
+ R(R0),
+ _varCount(R->getNumVars()),
+ _minimizeOnInsert(minimizeOnInsert),
+ _moveDivisorToFront(moveDivisorToFront),
+ _sortOnInsert(sortOnInsert),
+ _useAutomaticRebuild((rebuildRatio > 0.0 || minRebuild > 0) && UDM),
+ _rebuildRatio(rebuildRatio),
+ _minRebuild(minRebuild),
+ _expQueryCount(0) {}
+
+ static const bool UseLinkedList = ULL;
+ static const bool UseDivMask = UDM;
+
+ const PolyRing *getPolyRing() const {return R;}
+ bool getDoAutomaticRebuilds() const {return _useAutomaticRebuild;}
+ double getRebuildRatio() const {return _rebuildRatio;}
+ size_t getRebuildMin() const {return _minRebuild;}
+ bool getSortOnInsert() const {return _sortOnInsert;}
+ bool getMinimizeOnInsert() const {return _minimizeOnInsert;}
+ bool getMoveDivisorToFront() const {return _moveDivisorToFront;}
+
+ size_t getVarCount() const {return _varCount;}
+
+ NO_PINLINE Exponent getExponent(const Monomial& m, size_t var) const {
+ ++_expQueryCount;
+ return R->monomialExponent(m, var);
+ }
+
+ bool divides(const Monomial& a, const Monomial& b) const {
+ for (size_t var = 0; var < getVarCount(); ++var)
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ return true;
+ }
+
+ bool isLessThan(const Monomial& a, const Monomial& b) const {
+ for (size_t var = 0; var < getVarCount(); ++var) {
+ if (getExponent(a, var) < getExponent(b, var))
+ return true;
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ }
+ return false;
+ }
+
+ unsigned long long getExpQueryCount() const {return _expQueryCount;}
+
+ private:
+ const PolyRing *R;
+ const size_t _varCount;
+ const bool _minimizeOnInsert;
+ const bool _moveDivisorToFront;
+ const bool _sortOnInsert;
+ const bool _useAutomaticRebuild;
+ const double _rebuildRatio;
+ const size_t _minRebuild;
+ mutable unsigned long long _expQueryCount;
+};
+
+template<bool UseLinkedList, bool UseDivMask>
+class MonTableDivList;
+
+/** An instantiation of the capabilities of DivList. */
+template<bool ULL, bool UDM>
+class MonTableDivList {
+ private:
+ typedef MonTableDivListConfiguration<ULL, UDM> C;
+ typedef mic::DivList<C> Finder;
+ public:
+ typedef typename Finder::iterator iterator;
+ typedef typename Finder::const_iterator const_iterator;
+ typedef typename Finder::Monomial Monomial;
+ typedef typename Finder::Entry Entry;
+ typedef C Configuration;
+
+ MonTableDivList(const PolyRing *R,
+ bool minimizeOnInsert,
+ bool moveDivisorToFront,
+ bool sortOnInsert,
+ double rebuildRatio,
+ size_t minRebuild):
+ _finder(C(R, minimizeOnInsert, moveDivisorToFront, sortOnInsert, rebuildRatio, minRebuild))
+ {
+ ASSERT(!sortOnInsert || !moveDivisorToFront);
+ }
+
+ MonTableDivList(const Configuration &C) :
+ _finder(C)
+ {
+ ASSERT(!C.getSortOnInsert() || !C.getMoveDivisorToFront());
+ }
+
+ const C& getConfiguration() const {return _finder.getConfiguration();}
+ C& getConfiguration() {return _finder.getConfiguration();}
+
+ bool getMinimizeOnInsert() const {return getConfiguration().getMinimizeOnInsert();}
+ bool getMoveDivisorToFront() const {return getConfiguration().getMoveDivisorToFront();}
+
+ bool insert(const Entry& entry);
+ template<class MultipleOutput>
+ bool insert(const Entry& entry, MultipleOutput& removed);
+
+ Entry* findDivisor(const Monomial& monomial) {
+ return _finder.findDivisor(monomial);
+ }
+
+ const Entry* findDivisor(const Monomial& monomial) const {
+ return const_cast<MonTableDivList<ULL, UDM>&>(*this).findDivisor(monomial);
+ }
+
+ template<class DO>
+ void findAllDivisors(const Monomial& monomial, DO& out) {
+ _finder.findAllDivisors(monomial, out);
+ }
+ template<class DO>
+ void findAllDivisors(const Monomial& monomial, DO& out) const {
+ _finder.findAllDivisors(monomial, out);
+ }
+
+ std::string getName() const;
+
+
+ iterator begin() {return _finder.begin();}
+ const_iterator begin() const {return _finder.begin();}
+ iterator end() {return _finder.end();}
+ const_iterator end() const {return _finder.end();}
+ size_t size() const {return _finder.size();}
+
+ unsigned long long getExpQueryCount() const {
+ return _finder.getConfiguration().getExpQueryCount();
+ }
+
+ void getMonomials(std::vector<const_monomial>& monomials);
+
+public:
+ typedef size_t ValueType;
+
+ const PolyRing * getPolyRing() const { return getConfiguration().getPolyRing(); }
+
+ bool member(const_monomial t, ValueType & val) const {
+ const Entry* i = findDivisor(t);
+ if (i == 0) return false;
+ val = 0;
+ return true;
+ }
+ bool insert(const_monomial t, ValueType /* val */) { return insert(t); } // Only insert if not there
+
+ void display(std::ostream &o, int level) const;
+
+ void dump(int level) const;
+
+ size_t n_elems() const { return size(); }
+
+ size_t getMemoryUse() const;
+
+ void displayStats(std::ostream &o) const;
+
+ struct Stats {
+ size_t n_member;
+ size_t n_inserts; // includes koszuls
+ size_t n_insert_already_there;
+ size_t n_compares;
+ };
+
+ void getStats(Stats &stats) const { stats = stats_; }
+
+ private:
+ Finder _finder;
+ Stats stats_;
+};
+
+template<bool ULL, bool UDM>
+inline bool MonTableDivList<ULL, UDM>::insert(const Entry& entry) {
+ if (!getMinimizeOnInsert()) {
+ _finder.insert(entry);
+ return true;
+ }
+ if (findDivisor(entry) != 0)
+ return false;
+ bool hasMultiples = _finder.removeMultiples(entry);
+ _finder.insert(entry);
+ if (getMoveDivisorToFront() && hasMultiples) {
+ iterator it = _finder.end();
+ _finder.moveToFront(--it);
+ }
+ return true;
+}
+
+template<bool ULL, bool UDM>
+template<class MO>
+inline bool MonTableDivList<ULL, UDM>::insert(const Entry& entry, MO& out) {
+ if (!getMinimizeOnInsert()) {
+ _finder.insert(entry);
+ return true;
+ }
+ if (findDivisor(entry) != _finder.end())
+ return false;
+ bool hasMultiples = _finder.removeMultiples(entry, out);
+ _finder.insert(entry);
+ if (getMoveDivisorToFront() && hasMultiples) {
+ iterator it = _finder.end();
+ _finder.moveToFront(--it);
+ }
+ return true;
+}
+
+template<bool ULL, bool UDM>
+inline std::string MonTableDivList<ULL, UDM>::getName() const {
+ return _finder.getName() +
+ (getMinimizeOnInsert() ? " remin" : " nomin") +
+ (getMoveDivisorToFront() ? " toFront" : "");
+}
+
+template <bool UDL, bool UDM>
+void MonTableDivList<UDL,UDM>::display(std::ostream &o, int /* level */) const
+{
+ const_iterator e = _finder.end();
+
+ for (const_iterator i=_finder.begin(); i != e; ++i)
+ {
+ getPolyRing()->monomialDisplay(o, *i, false);
+ o << " ";
+ }
+ o << std::endl;
+}
+
+template <bool UDL, bool UDM>
+void MonTableDivList<UDL,UDM>::dump(int level) const
+{
+ displayStats(std::cerr);
+ if (level > 0) display(std::cerr, level-1);
+}
+
+template <bool UDL, bool UDM>
+size_t MonTableDivList<UDL,UDM>::getMemoryUse() const
+{
+ return _finder.getMemoryUse();
+}
+
+template <bool UDL, bool UDM>
+void MonTableDivList<UDL,UDM>::displayStats(std::ostream &o) const
+{
+ o << " #elements: " << n_elems() << std::endl;
+ o << " #calls member: " << stats_.n_member << std::endl;
+ o << " #calls insert, but already there: " << stats_.n_insert_already_there << std::endl;
+ o << " #compares: " << stats_.n_compares << std::endl;
+}
+
+template<bool UDL, bool UDM>
+void MonTableDivList<UDL, UDM>::getMonomials(std::vector<const_monomial>& monomials) {
+ monomials.insert(monomials.begin(), begin(), end());
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonTableKDTree.cpp b/src/mathicgb/MonTableKDTree.cpp
new file mode 100644
index 0000000..c3cadb3
--- /dev/null
+++ b/src/mathicgb/MonTableKDTree.cpp
@@ -0,0 +1,7 @@
+#include "stdinc.h"
+#include "MonTableKDTree.hpp"
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonTableKDTree.hpp b/src/mathicgb/MonTableKDTree.hpp
new file mode 100644
index 0000000..81867a1
--- /dev/null
+++ b/src/mathicgb/MonTableKDTree.hpp
@@ -0,0 +1,292 @@
+#ifndef K_D_TREE_MODEL_GUARD
+#define K_D_TREE_MODEL_GUARD
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include <memtailor.h>
+#include <mathic.h>
+
+#include "PolyRing.hpp"
+
+/** Helper class for KDTreeModel. */
+template<bool UseDivMask, bool UseTreeDivMask, size_t LeafSize, bool AllowRemovals>
+class KDTreeModelConfiguration;
+
+template<bool UDM, bool UTDM, size_t LS, bool AR>
+class MonTableKDTreeConfiguration {
+ public:
+ typedef int Exponent;
+ typedef const_monomial Monomial;
+ typedef Monomial Entry;
+
+ MonTableKDTreeConfiguration
+ (const PolyRing *R0,
+ bool minimizeOnInsert,
+ bool sortOnInsert,
+ bool useDivisorCache,
+ double rebuildRatio,
+ size_t minRebuild):
+ R(R0),
+ _varCount(R->getNumVars()),
+ _minimize_on_insert(minimizeOnInsert),
+ _sortOnInsert(sortOnInsert),
+ _useDivisorCache(useDivisorCache),
+ _useAutomaticRebuild((rebuildRatio > 0.0 || minRebuild > 0) && UDM),
+ _rebuildRatio(rebuildRatio),
+ _minRebuild(minRebuild),
+ _expQueryCount(0) {
+ ASSERT(rebuildRatio >= 0);
+ }
+
+ size_t getVarCount() const {return _varCount;}
+ bool getSortOnInsert() const {return _sortOnInsert;}
+
+ Exponent getExponent(const Monomial& m, size_t var) const {
+ ++_expQueryCount;
+ return R->monomialExponent(m, var);
+ }
+
+ bool divides(const Monomial& a, const Monomial& b) const {
+ for (size_t var = 0; var < getVarCount(); ++var)
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ return true;
+ }
+
+ bool isLessThan(const Monomial& a, const Monomial& b) const {
+ for (size_t var = 0; var < getVarCount(); ++var) {
+ if (getExponent(a, var) < getExponent(b, var))
+ return true;
+ if (getExponent(b, var) < getExponent(a, var))
+ return false;
+ }
+ return false;
+ }
+
+ const PolyRing *getPolyRing() const {return R;}
+ bool getUseDivisorCache() const {return _useDivisorCache;}
+ bool getDoAutomaticRebuilds() const {return _useAutomaticRebuild;}
+ double getRebuildRatio() const {return _rebuildRatio;}
+ size_t getRebuildMin() const {return _minRebuild;}
+ bool getMinimizeOnInsert() const {return _minimize_on_insert;}
+ static const bool UseDivMask = UDM;
+ static const bool UseTreeDivMask = UTDM;
+ static const size_t LeafSize = LS;
+ static const bool PackedTree = true;
+ static const bool AllowRemovals = AR;
+
+ unsigned long long getExpQueryCount() const {return _expQueryCount;}
+
+ private:
+ const PolyRing *R;
+ const size_t _varCount;
+ const bool _minimize_on_insert;
+ const bool _sortOnInsert;
+ const bool _useDivisorCache;
+ const bool _useAutomaticRebuild;
+ const double _rebuildRatio;
+ const size_t _minRebuild;
+ mutable unsigned long long _expQueryCount;
+};
+
+/** An instantiation of the capabilities of KDTree. */
+template<bool UseDivMask, bool UseTreeDivMask, size_t LeafSize, bool AllowRemovals>
+class MonTableKDTree {
+ private:
+ typedef MonTableKDTreeConfiguration<UseDivMask, UseTreeDivMask, LeafSize, AllowRemovals> C;
+ typedef mic::KDTree<C> Finder;
+ public:
+ typedef typename Finder::Monomial Monomial;
+ typedef typename Finder::Entry Entry;
+ typedef C Configuration;
+
+ MonTableKDTree(const PolyRing *R,
+ bool minimizeOnInsert,
+ bool sortOnInsert,
+ bool useDivisorCache,
+ double rebuildRatio,
+ size_t minRebuild):
+ _finder(C(R, minimizeOnInsert, sortOnInsert, useDivisorCache, rebuildRatio, minRebuild))
+ {
+ ASSERT(!UseTreeDivMask || UseDivMask);
+ }
+
+ MonTableKDTree(const Configuration &C) :
+ _finder(C)
+ {
+ ASSERT(!UseTreeDivMask || UseDivMask);
+ }
+
+ const C& getConfiguration() const {return _finder.getConfiguration();}
+ C& getConfiguration() {return _finder.getConfiguration();}
+
+ bool insert(const Entry& entry);
+ template<class MultipleOutput>
+ bool insert(const Entry& entry, MultipleOutput& removed);
+
+ inline Entry* findDivisor(const Monomial& monomial) {
+ return _finder.findDivisor(monomial);
+ }
+ inline const Entry* findDivisor(const Monomial& monomial) const {
+ return _finder.findDivisor(monomial);
+ }
+ std::string getName() const;
+
+ template<class DO>
+ inline void findAllDivisors(const Monomial& monomial, DO& out) {
+ _finder.findAllDivisors(monomial, out);
+ }
+ template<class DO>
+ inline void findAllDivisors(const Monomial& monomial, DO& out) const {
+ _finder.findAllDivisors(monomial, out);
+ }
+
+ unsigned long long getExpQueryCount() const {
+ return _finder.getConfiguration().getExpQueryCount();
+ }
+
+ void getMonomials(std::vector<const_monomial>& monomials);
+
+public:
+ typedef size_t ValueType;
+
+ const PolyRing * getPolyRing() const { return getConfiguration().getPolyRing(); }
+
+ inline bool member(const_monomial t, ValueType & val) const {
+ const Entry* entry = findDivisor(t);
+ if (entry == 0)
+ return false;
+ val = 0;
+ return true;
+ }
+ bool insert(const_monomial t, ValueType /* val */) { return insert(t); } // Only insert if not there
+
+ size_t n_elems() const { return _finder.size(); }
+
+ size_t getMemoryUse() const;
+
+ void displayStats(std::ostream &o) const;
+
+ struct Stats {
+ size_t n_member;
+ size_t n_inserts; // includes koszuls
+ size_t n_insert_already_there;
+ size_t n_compares;
+ };
+
+ void getStats(Stats &stats) const { stats = stats_; }
+
+ private:
+ Finder _finder;
+ Stats stats_;
+};
+
+class MonomialDeleter {
+public:
+ MonomialDeleter(memt::BufferPool& pool): _pool(pool) {}
+ void push_back(const void* p) {
+ _pool.free(const_cast<void*>(p));
+ }
+ void push_back(ConstMonomial p) {
+ _pool.free(static_cast<void*>(const_cast<exponent *>(p.unsafeGetRepresentation())));
+ }
+private:
+ memt::BufferPool& _pool;
+};
+
+template<bool UDM, bool UTDM, size_t LS, bool AR>
+inline bool MonTableKDTree<UDM, UTDM, LS, AR>::insert(const Entry& entry) {
+ if (!getConfiguration().getMinimizeOnInsert()) {
+ _finder.insert(entry);
+ return true;
+ }
+ if (C::AllowRemovals) {
+ if (findDivisor(entry) != 0)
+ return false;
+ MonomialDeleter mondelete(getPolyRing()->getMonomialPool());
+ _finder.removeMultiples(entry, mondelete);
+ } else {
+ ASSERT(findDivisor(entry) == 0);
+ MonomialDeleter mondelete(getPolyRing()->getMonomialPool());
+ // todo: can't do the below ASSERT as KD tree won't allow a
+ // removal action when removals are not allowed. So there
+ // should be a getMultiples() method that could be
+ // used instead.
+ //ASSERT(!_finder.removeMultiples(entry, mondelete));
+ }
+ _finder.insert(entry);
+ return true;
+}
+
+template<bool UDM, bool UTDM, size_t LS, bool AR>
+template<class MultipleOutput>
+inline bool MonTableKDTree<UDM, UTDM, LS, AR>::
+insert(const Entry& entry, MultipleOutput& removed) {
+ if (!getConfiguration().getMinimizeOnInsert()) {
+ _finder.insert(entry);
+ return true;
+ }
+ if (C::AllowRemovals) {
+ if (findDivisor(entry) != _finder.end())
+ return false;
+ _finder.removeMultiples(entry, removed);
+ } else {
+ ASSERT(findDivisor(entry) == _finder.end());
+ // todo: can't do the below ASSERT as KD tree won't allow a
+ // removal action when removals are not allowed. So there
+ // should be a getMultiples() method that could be
+ // used instead.
+ //ASSERT(!_finder.removeMultiples(entry, removed));
+ }
+ _finder.insert(entry);
+ return true;
+}
+
+template<bool UDM, bool UTDM, size_t LS, bool AR>
+inline std::string MonTableKDTree<UDM, UTDM, LS, AR>::getName() const {
+ return _finder.getName() +
+ (getConfiguration().getMinimizeOnInsert() ? " remin" : " nomin");
+}
+
+template<bool UDM, bool UTDM, size_t LS, bool AR>
+size_t MonTableKDTree<UDM, UTDM, LS, AR>::getMemoryUse() const
+{
+ return _finder.getMemoryUse();
+}
+
+template <bool UDM, bool UTM, size_t LS, bool AR>
+void MonTableKDTree<UDM,UTM,LS, AR>::displayStats(std::ostream &o) const
+{
+ o << " #elements: " << n_elems() << std::endl;
+ o << " #calls member: " << stats_.n_member << std::endl;
+ o << " #calls insert, but already there: " << stats_.n_insert_already_there << std::endl;
+ o << " #compares: " << stats_.n_compares << std::endl;
+}
+
+namespace {
+ class ForAllCopy {
+ public:
+ ForAllCopy(std::vector<const_monomial>& monomials):
+ mMonomials(monomials) {}
+ bool proceed(const_monomial m) {
+ mMonomials.push_back(m);
+ return true;
+ }
+ private:
+ std::vector<const_monomial>& mMonomials;
+ };
+}
+
+template<bool UDM, bool UTM, size_t LS, bool AR>
+void MonTableKDTree<UDM, UTM, LS, AR>::getMonomials(std::vector<const_monomial>& monomials) {
+ ForAllCopy copier(monomials);
+ _finder.forAll(copier);
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonTableNaive.cpp b/src/mathicgb/MonTableNaive.cpp
new file mode 100644
index 0000000..3543ec1
--- /dev/null
+++ b/src/mathicgb/MonTableNaive.cpp
@@ -0,0 +1,169 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include <ostream>
+#include "MonTableNaive.hpp"
+#include <iostream>
+
+MonTableNaive::MonTableNaive(const PolyRing *R) :
+ conf_(R),
+ mPool(sizeof(mon_node)),
+ table(0)
+{
+ stats_.n_member = 0;
+ stats_.n_inserts = 0;
+ stats_.n_insert_already_there = 0;
+ stats_.n_compares = 0;
+}
+
+MonTableNaive::MonTableNaive(const Configuration& C) :
+ conf_(C),
+ mPool(sizeof(mon_node)),
+ table(0)
+{
+ stats_.n_member = 0;
+ stats_.n_inserts = 0;
+ stats_.n_insert_already_there = 0;
+ stats_.n_compares = 0;
+}
+
+MonTableNaive::~MonTableNaive()
+{
+ // Nothing to do?
+}
+
+bool MonTableNaive::member(const_monomial t, ValueType &result_val) const
+{
+ result_val = 0;
+ stats_.n_member++;
+ for (mon_node *p = table; p != 0; p = p->next)
+ {
+ stats_.n_compares++;
+ switch (getPolyRing()->monomialCompare(p->monom, t))
+ {
+ case GT:
+ return false;
+ case EQ:
+ // Already exists, leave
+ return true;
+ case LT:
+ // Check divisibility
+ if (getPolyRing()->monomialIsDivisibleBy(t, p->monom))
+ return true;
+ }
+ }
+ return false;
+}
+
+void MonTableNaive::insert_node(mon_node *p, const_monomial t)
+{
+ mon_node *q = static_cast<mon_node *>(mPool.alloc());
+ q->next = p->next;
+ p->next = q;
+ q->monom = t;
+
+ // Now remove the elements of the list p->next which are
+ // divisible by q->monom
+
+ mon_node *r = q;
+ while (r->next != 0)
+ {
+ if (getPolyRing()->monomialIsDivisibleBy(r->next->monom, q->monom))
+ {
+ // remove this term:
+ mon_node *a = r->next;
+ r->next = a->next;
+ mPool.free(a);
+ }
+ else
+ r = r->next;
+ }
+}
+
+bool MonTableNaive::insert(const_monomial t, ValueType /* val_ignored */)
+{
+ stats_.n_inserts++;
+ // Only inserts if not in the table...
+ mon_node head;
+ head.next = table;
+ mon_node *f = &head;
+ while (f->next != 0)
+ {
+ stats_.n_compares++;
+ switch (getPolyRing()->monomialCompare(f->next->monom, t))
+ {
+ case GT:
+ // Insert t before f->next
+ insert_node(f, t);
+ table = head.next;
+ return true;
+ case EQ:
+ // Already exists, leave
+ stats_.n_insert_already_there++;
+ return false;
+ case LT:
+ // Check divisibility
+ if (getPolyRing()->monomialIsDivisibleBy(t, f->next->monom))
+ {
+ stats_.n_insert_already_there++;
+ return false;
+ }
+ f = f->next;
+ }
+ }
+ insert_node(f, t);
+ table = head.next;
+ return true;
+}
+
+size_t MonTableNaive::n_elems() const
+{
+ size_t len = 0;
+ for (mon_node *q = table; q != 0; q = q->next) len++;
+ return len;
+}
+
+void MonTableNaive::getMonomials(std::vector<const_monomial>& monomials) {
+ for (mon_node *p = table; p != 0; p=p->next)
+ monomials.push_back(p->monom);
+}
+
+void MonTableNaive::display(std::ostream &o, int /* level */) const
+{
+ o << n_elems() << ": ";
+ for (mon_node *p = table; p != 0; p=p->next)
+ {
+ getPolyRing()->monomialDisplay(o, p->monom, false);
+ o << " ";
+ }
+ o << std::endl;
+}
+
+void MonTableNaive::displayStats(std::ostream &o) const
+{
+ o << " #elements: " << n_elems() << std::endl;
+ o << " #calls member: " << stats_.n_member << std::endl;
+ o << " #calls insert, but already there: " << stats_.n_insert_already_there << std::endl;
+ o << " #compares: " << stats_.n_compares << std::endl;
+}
+
+void MonTableNaive::dump(int level) const
+{
+ displayStats(std::cerr);
+ if (level > 0) display(std::cerr, level-1);
+}
+
+std::string MonTableNaive::getName() const
+{
+ return "naive";
+}
+
+size_t MonTableNaive::getMemoryUse() const
+{
+ return mPool.getMemoryUse();
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonTableNaive.hpp b/src/mathicgb/MonTableNaive.hpp
new file mode 100644
index 0000000..4d135a5
--- /dev/null
+++ b/src/mathicgb/MonTableNaive.hpp
@@ -0,0 +1,84 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _monomial_table_h_
+#define _monomial_table_h_
+
+#include <memtailor.h>
+#include "PolyRing.hpp"
+
+struct mon_node { // each node is in 'nodes' arena
+ mon_node *next;
+ const_monomial monom; // points into 'monoms'
+};
+
+class MonTableNaiveConfiguration
+{
+public:
+ MonTableNaiveConfiguration(const PolyRing *R) : R_(R) {}
+
+ const PolyRing * getPolyRing() const { return R_; }
+
+private:
+ const PolyRing *R_;
+};
+
+class MonTableNaive {
+public:
+ typedef size_t ValueType;
+ typedef MonTableNaiveConfiguration Configuration;
+
+ MonTableNaive(const PolyRing *R);
+ MonTableNaive(const Configuration& C);
+ ~MonTableNaive();
+
+ Configuration& getConfiguration() { return conf_; }
+ const Configuration& getConfiguration() const { return conf_; }
+
+ const PolyRing * getPolyRing() const { return conf_.getPolyRing(); }
+
+ bool member(const_monomial t, ValueType & val) const;
+ bool insert(const_monomial t, ValueType val = 0); // Only insert if not there
+
+ std::string getName() const;
+ void display(std::ostream &o, int level) const;
+ void dump(int level) const;
+
+ size_t n_elems() const;
+
+ void displayStats(std::ostream &o) const;
+
+ struct Stats {
+ size_t n_member;
+ size_t n_inserts; // includes koszuls
+ size_t n_insert_already_there;
+ size_t n_compares;
+ };
+
+ void getStats(Stats &stats) const { stats = stats_; }
+
+ size_t getMemoryUse() const;
+
+ void getMonomials(std::vector<const_monomial>& monomials);
+
+private:
+ void insert_node(mon_node *p, const_monomial t);
+
+ // For now, keep a sorted linked list of monomials (in increasing order), one such list for each component found
+ // member: just goes through each one, checking for divisibility
+ // insert: find insertion spot, insert it, then try to remove ones after that that are not needed
+ Configuration conf_;
+
+ memt::BufferPool mPool; // used for mon_node's
+ mon_node *table;
+
+ // stats:
+ mutable Stats stats_;
+};
+
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonomialHashTable.cpp b/src/mathicgb/MonomialHashTable.cpp
new file mode 100644
index 0000000..c0ce9e1
--- /dev/null
+++ b/src/mathicgb/MonomialHashTable.cpp
@@ -0,0 +1,9 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "MonomialHashTable.hpp"
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/MonomialHashTable.hpp b/src/mathicgb/MonomialHashTable.hpp
new file mode 100644
index 0000000..ebe7854
--- /dev/null
+++ b/src/mathicgb/MonomialHashTable.hpp
@@ -0,0 +1,90 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _MonomialHashTable_h_
+#define _MonomialHashTable_h_
+
+#include "ChainedHashTable.hpp"
+#include "PolyRing.hpp"
+
+class MonomialHashControl
+{
+public:
+ typedef const_monomial KeyType;
+ typedef int ValueType;
+
+ MonomialHashControl(const PolyRing *R) : R_(R), hash_index_(R->monomialHashIndex()) {}
+ size_t hash_value(KeyType k) const { return R_->monomialHashValue(k); }
+ bool is_equal(KeyType k1, KeyType k2) const { return R_->monomialEQ(k1, k2); }
+ void combine(ValueType &, ValueType) const { }
+ void show(std::ostream &o, KeyType k, ValueType v) const {
+ o << "[";
+ R_->monomialDisplay(o, k);
+ o << " " << v << "]";
+ }
+private:
+ const PolyRing *R_;
+ size_t hash_index_;
+};
+
+typedef ChainedHashTable<MonomialHashControl> MonomialHashTableBasic;
+
+class MonomialHashTable
+{
+public:
+ typedef MonomialHashTableBasic::Stats Stats;
+
+ MonomialHashTable(const PolyRing *R, int nbits)
+ : R_(R), H_(R, nbits) {}
+
+ ~MonomialHashTable() {}
+
+ std::string description() const { return "monomial hash table"; }
+
+ void reset() {
+ // Clear the table, and memory areas, but don't release space grabbed so far
+ mMonomialPool.freeAllAllocs();
+ H_.reset();
+ }
+
+ void resize(int new_nbits) { H_.resize(new_nbits); }
+ // Don't change the nodes, table, but do recreate hashtable_
+
+ bool member(const_monomial m, int &result_val) const { return H_.member(m, result_val); }
+ // Return true if m is in the hash table.
+ // In this case, set 'result_val' with its value
+
+ void insertUnique(const_monomial m, int v) {
+ // The caller must insure that 'm' is not currently in the table.
+ // First copy m.
+ monomial m1 = R_->allocMonomial(mMonomialPool);
+ R_->monomialCopy(m, m1);
+ H_.insertUnique(m1,v);
+ }
+
+ bool lookupAndInsert(const_monomial m, int &v) {
+ // returns true if 'm' is in the table. In this case, v is set to value that is in the table,
+ // If false, 'm' is inserted, and its value is set to v.
+ if (member(m, v)) return true;
+ insertUnique(m, v);
+ return false;
+ }
+
+ void getStats(Stats &stats) const { H_.getStats(stats) ; }
+
+ void resetStats() const { H_.resetStats(); }
+ // reset all values to 0
+
+ void dump(int level = 0) const { H_.dump(level); } // For debugging: display the current state of the table
+
+ size_t getMemoryUse() const { return mMonomialPool.getMemoryUse() + H_.getMemoryUse(); }
+private:
+ const PolyRing *R_;
+ memt::Arena mMonomialPool;
+ MonomialHashTableBasic H_;
+};
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PairTriangle.cpp b/src/mathicgb/PairTriangle.cpp
new file mode 100644
index 0000000..af2e7be
--- /dev/null
+++ b/src/mathicgb/PairTriangle.cpp
@@ -0,0 +1,226 @@
+#include "stdinc.h"
+#include "PairTriangle.hpp"
+
+#include <limits>
+#include <stdexcept>
+#include "FreeModuleOrder.hpp"
+
+SPairGroup::SPairGroup(
+ size_t fixedIndex,
+ monomial signature,
+ std::vector<PreSPair>& prePairs,
+ memt::Arena& arena
+):
+ mSignature(signature),
+ mFixedIndex(fixedIndex)
+{
+ const size_t prePairCount = prePairs.size();
+ if (big()) {
+ std::pair<BigIndex*, BigIndex*> range =
+ arena.allocArrayNoCon<BigIndex>(prePairCount);
+ for (size_t i = 0; i < prePairCount; ++i) {
+ ASSERT(prePairs[i].i < mFixedIndex);
+ ASSERT(prePairs[i].i <= std::numeric_limits<BigIndex>::max());
+ range.first[i] = prePairs[i].i;
+ }
+ bigBegin = range.first;
+ bigEnd = range.second;
+ } else {
+ std::pair<SmallIndex*, SmallIndex*> range =
+ arena.allocArrayNoCon<SmallIndex>(prePairCount);
+ for (size_t i = 0; i < prePairCount; ++i) {
+ ASSERT(prePairs[i].i < mFixedIndex);
+ ASSERT(prePairs[i].i <= std::numeric_limits<SmallIndex>::max());
+ range.first[i] = prePairs[i].i;
+ }
+ smallBegin = range.first;
+ smallEnd = range.second;
+ }
+ ASSERT(size() == prePairCount);
+ ASSERT(empty() == (prePairCount == 0));
+}
+
+void SPairGroup::write(const PolyRing* R, std::ostream& out) {
+ if (big()) {
+ for (BigIndex* f = bigBegin; f != bigEnd; f++) {
+ out << " [" << fixedIndex() << " " << *f;
+ if (f == bigBegin)
+ R->monomialDisplay(out, signature());
+ out << "]" << std::endl;
+ }
+ } else {
+ for (SmallIndex* f = smallBegin; f != smallEnd; f++) {
+ out << " [" << fixedIndex() << " " << *f;
+ if (f == smallBegin)
+ R->monomialDisplay(out, signature());
+ out << "]" << std::endl;
+ }
+ }
+}
+
+size_t SPairGroup::otherIndex() const {
+ ASSERT(!empty());
+ if (big())
+ return *bigBegin;
+ else
+ return *smallBegin;
+}
+
+void SPairGroup::increment() {
+ ASSERT(!empty());
+ do {
+ if (big())
+ ++bigBegin;
+ else
+ ++smallBegin;
+ } while (!empty() && otherIndex() == fixedIndex());
+}
+
+bool SPairGroup::empty() const {
+ if (big())
+ return bigBegin == bigEnd;
+ else
+ return smallBegin == smallEnd;
+}
+
+bool SPairGroup::big() const {
+ return mFixedIndex >
+ static_cast<size_t>(std::numeric_limits<SmallIndex>::max());
+}
+
+size_t SPairGroup::size() const {
+ if (big())
+ return bigEnd - bigBegin;
+ else
+ return smallEnd - smallBegin;
+}
+
+PairTriangle::PairTriangle(const FreeModuleOrder& order, const PolyRing& ring, size_t queueType):
+ mUseSingletonGroups((queueType & 2) != 0),
+ mColumnCount(0),
+ mQueue(order.makeQueue((queueType & 1))),
+ mOrder(order),
+ mRing(ring) {
+}
+
+PairTriangle::~PairTriangle() {
+}
+
+size_t PairTriangle::size() const {
+ size_t sum = 0;
+ for (size_t i = 0; i < mGroups.size(); ++i)
+ if (mGroups[i] != 0)
+ sum += mGroups[i]->size();
+ return sum;
+}
+
+void PairTriangle::beginColumn() {
+ ASSERT(mPrePairs.empty());
+ size_t const maxBigIndex = std::numeric_limits<BigIndex>::max();
+ if (mColumnCount >= maxBigIndex)
+ throw std::overflow_error
+ ("Too large basis element index in constructing S-pairs.");
+}
+
+void PairTriangle::addPair(size_t index, monomial orderBy) {
+ ASSERT(index < mColumnCount);
+#ifdef DEBUG
+ monomial tmp = mRing.allocMonomial();
+ calculateOrderBy(mColumnCount, index, tmp);
+ ASSERT(mRing.monomialEQ(tmp, orderBy));
+ mRing.freeMonomial(tmp);
+#endif
+
+ PreSPair p;
+ p.i = static_cast<BigIndex>(index);
+ p.signature = orderBy;
+ mPrePairs.push_back(p);
+}
+
+void PairTriangle::endColumn() {
+ ++mColumnCount;
+ if (mPrePairs.empty())
+ return;
+
+ size_t const newGen = mColumnCount - 1;
+ if (mUseSingletonGroups) {
+ size_t const size = mPrePairs.size();
+ for (size_t i = 0; i < size; ++i) {
+ mGroups.push_back(mArena.allocObjectNoCon<SPairGroup>());
+ SPairGroup* p = mGroups.back();
+
+ ASSERT(mPrePairTmp.empty());
+ mPrePairTmp.push_back(mPrePairs[i]);
+ new (p) SPairGroup
+ (newGen, mPrePairTmp.front().signature, mPrePairTmp, mArena);
+ mPrePairTmp.clear();
+
+ calculateOrderBy(p->fixedIndex(), p->otherIndex(), p->signature());
+ mQueue->push(p);
+ }
+ } else {
+ mGroups.push_back(mArena.allocObjectNoCon<SPairGroup>());
+ SPairGroup* p = mGroups.back();
+
+ mOrder.destructiveSort(mPrePairs);
+
+ // Take the very first one and insert it into the queue
+ new (p) SPairGroup(newGen, mPrePairs.front().signature, mPrePairs, mArena);
+ const size_t size = mPrePairs.size();
+ for (size_t i = 1; i < size; ++i)
+ mRing.freeMonomial(mPrePairs[i].signature);
+
+ calculateOrderBy(p->fixedIndex(), p->otherIndex(), p->signature());
+ mQueue->push(p);
+ }
+ mPrePairs.clear();
+}
+
+void PairTriangle::pop() {
+ ASSERT(!empty());
+
+ SPairGroup* topGroup = mQueue->top();
+ ASSERT(topGroup != 0);
+
+ while (true) {
+ topGroup->increment();
+ if (topGroup->empty()) {
+ mQueue->pop();
+ mRing.freeMonomial(topGroup->signature());
+ topGroup->setSignature(0);
+ } else {
+ // Compute the signature of the next S-pair in topGroup.
+ if (!calculateOrderBy
+ (topGroup->fixedIndex(), topGroup->otherIndex(), topGroup->signature()))
+ continue;
+ mQueue->decreaseTop(topGroup);
+ }
+ break;
+ }
+}
+
+size_t PairTriangle::getMemoryUse() const {
+ return mArena.getMemoryUse() + mQueue->getMemoryUse();
+}
+
+std::string PairTriangle::name() const {
+ return mQueue->getName() + (mUseSingletonGroups ? "" : "-triangle");
+}
+
+std::pair<size_t, size_t> PairTriangle::topPair() const {
+ ASSERT(!mQueue->empty());
+ SPairGroup* group = mQueue->top();
+ ASSERT(group != 0);
+ ASSERT(!group->empty());
+ return std::make_pair(group->fixedIndex(), group->otherIndex());
+}
+
+// Returns the minimal orderBy monomial along all pairs. This is the orderBy
+// monomial of topPair().
+const_monomial PairTriangle::topOrderBy() const {
+ ASSERT(!mQueue->empty());
+ SPairGroup* group = mQueue->top();
+ ASSERT(group != 0);
+ ASSERT(!group->empty());
+ return group->signature();
+}
diff --git a/src/mathicgb/PairTriangle.hpp b/src/mathicgb/PairTriangle.hpp
new file mode 100644
index 0000000..b6b6c54
--- /dev/null
+++ b/src/mathicgb/PairTriangle.hpp
@@ -0,0 +1,135 @@
+#ifndef _pair_triangle_h
+#define _pair_triangle_h
+
+#include "SPairQueue.hpp"
+#include "PolyRing.hpp"
+class FreeModuleOrder;
+
+typedef unsigned short SmallIndex;
+typedef unsigned int BigIndex;
+
+// The following type is only used in the creation of SPairGroups
+struct PreSPair {
+ BigIndex i;
+ monomial signature;
+};
+
+class SPairGroup {
+public:
+ SPairGroup(size_t fixedIndex, monomial signature, std::vector<PreSPair>& prePairs, memt::Arena& arena);
+
+ monomial signature() {return mSignature;}
+ void setSignature(monomial signature) {mSignature = signature;}
+ const_monomial signature() const {return mSignature;}
+ size_t fixedIndex() const {return mFixedIndex;}
+ size_t otherIndex() const;
+
+ void increment();
+ bool empty() const;
+ size_t size() const; // number of S-pairs remaining in this group
+
+ void write(const PolyRing* R, std::ostream& out);
+
+private:
+ bool big() const;
+
+ monomial mSignature; // signature of the current pair
+ const size_t mFixedIndex; // all pairs here have this index
+
+ union {
+ BigIndex* bigBegin; // the current other index is *begin
+ SmallIndex* smallBegin;
+ };
+ union {
+ BigIndex* bigEnd; // the other indexes lie in [begin, end)
+ SmallIndex* smallEnd;
+ };
+};
+
+
+// Object that stores integer pairs.
+// The pairs are stored in the shape of a triangle:
+//
+// 3
+// 2 2
+// 1 1 1
+// 0 0 0 0
+// ---------
+// 0 1 2 3 4
+//
+// So column a stores the numbers b such that (a,b) is a pair
+// with a > b. Not all pairs need be present, and there can be
+// any ordering of the entries in each column.
+//
+// PairTriangles are useful for storing S-pair indices, and the
+// class has code that helps with that.
+class PairTriangle {
+public:
+ PairTriangle(
+ const FreeModuleOrder& order,
+ const PolyRing& ring,
+ size_t queueType);
+ ~PairTriangle();
+
+ // Returns how many columns the triangle has
+ size_t columnCount() const {return mColumnCount;}
+
+ // Returns how many pairs are in the triangle
+ size_t size() const;
+
+ // Returns true if there are no pairs in the triangle
+ bool empty() const {return mQueue->empty();}
+
+ // Adds a new column of the triangle and opens it for addition of pairs.
+ // This increases columnCount() by one, and the index of the new column
+ // is the previous value of columnCount(). Must call endColumn
+ // before calling beginColumn again or using the new column.
+ void beginColumn();
+
+ // Adds a pair to the most recent column that must still be open for
+ // addition of pairs. If a is the index of the new column, then
+ // the added pair is (a,index). index must be less than a.
+ // orderBy must have been allocated on the ring's pool of monomials,
+ // and ownership of the memory is passed to the this triangle object.
+ void addPair(size_t index, monomial orderBy);
+
+ // Closes the new column for addition of pairs. Must be preceded by a call
+ // to beginColumn(). This sorts the added pairs according to their orderBy
+ // monomials.
+ void endColumn();
+
+ // Returns a pair with minimal orderBy monomial.
+ std::pair<size_t, size_t> topPair() const;
+
+ // Returns the minimal orderBy monomial along all pairs. This is the orderBy
+ // monomial of topPair().
+ const_monomial topOrderBy() const;
+
+ // Removes topPair() from the triangle.
+ void pop();
+
+ size_t getMemoryUse() const;
+
+ std::string name() const;
+
+protected:
+ // Sub classes implement this to say what monomial each pair is ordered
+ // according to. That monomial should be placed into orderBy.
+ //
+ // If false is returned, the requested S-pair is not valid and should be
+ // skipped.
+ virtual bool calculateOrderBy(size_t a, size_t b, monomial orderBy) const = 0;
+
+private:
+ bool const mUseSingletonGroups;
+ size_t mColumnCount;
+ std::vector<SPairGroup*> mGroups;
+ std::auto_ptr<SPairQueue> mQueue;
+ memt::Arena mArena;
+ FreeModuleOrder const& mOrder;
+ std::vector<PreSPair> mPrePairs;
+ std::vector<PreSPair> mPrePairTmp;
+ PolyRing const& mRing;
+};
+
+#endif
diff --git a/src/mathicgb/Poly.cpp b/src/mathicgb/Poly.cpp
new file mode 100644
index 0000000..7a6c42e
--- /dev/null
+++ b/src/mathicgb/Poly.cpp
@@ -0,0 +1,284 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "Poly.hpp"
+#include <ostream>
+#include <istream>
+#include <iostream>
+
+// Format for input/output:
+// #terms term1 term2 ...
+// each term: coeff monom
+// each coeff: int
+// each monom: len v1 e1 v2 e2 ... vr er
+// where len = r
+// MAJOR ASSUMPTION: the monomials are ordered in descending order!!
+
+void Poly::copy(Poly &result) const
+{
+ result.R = R;
+ result.coeffs.resize(coeffs.size());
+ result.monoms.resize(monoms.size());
+ std::copy(coeffs.begin(), coeffs.end(), result.coeffs.begin());
+ std::copy(monoms.begin(), monoms.end(), result.monoms.begin());
+}
+void Poly::appendTerm(coefficient a, const_monomial m)
+{
+ // the monomial will be copied on.
+ coeffs.push_back(a);
+ size_t len = R->monomialSize(m);
+ exponent const * e = m.unsafeGetRepresentation();
+ monoms.insert(monoms.end(), e, e + len);
+}
+void Poly::append(iterator &first, iterator &last)
+{
+ for ( ; first != last; ++first)
+ appendTerm(first.getCoefficient(), first.getMonomial());
+}
+Poly *Poly::copy() const
+{
+ Poly *const_this = const_cast<Poly *>(this);
+ Poly *result = new Poly(R);
+ iterator a = const_this->begin();
+ iterator b = const_this->end();
+ result->append(a,b);
+ return result;
+}
+
+void Poly::multByCoefficient(coefficient c)
+{
+ for (std::vector<coefficient>::iterator i = coeffs.begin(); i != coeffs.end(); i++)
+ R->coefficientMultTo(*i, c);
+}
+
+void Poly::makeMonic()
+{
+ if (isZero()) return;
+ coefficient c = getLeadCoefficient();
+ R->coefficientReciprocalTo(c);
+ for (std::vector<coefficient>::iterator i = coeffs.begin(); i != coeffs.end(); i++)
+ R->coefficientMultTo(*i, c);
+}
+
+bool operator==(const Poly &a, const Poly &b)
+{
+ const PolyRing* R = a.getRing();
+ if (R != b.getRing())
+ return false;
+ if (a.nTerms() != b.nTerms())
+ return false;
+ Poly::const_iterator a1 = a.begin();
+ Poly::const_iterator b1 = b.begin();
+ for ( ; a1 != a.end(); ++a1, ++b1)
+ {
+ if (a1.getCoefficient() != b1.getCoefficient())
+ return false;
+ if (!R->monomialEQ(a1.getMonomial(), b1.getMonomial()))
+ return false;
+ }
+ return true;
+}
+
+Poly * Poly::add(const PolyRing *R,
+ iterator i,
+ iterator iend,
+ iterator j,
+ iterator jend,
+ size_t &n_compares)
+{
+ coefficient c;
+ n_compares = 0;
+ Poly *result = new Poly(R);
+
+ if (i == iend)
+ result->append(j, jend);
+ else if (j == jend)
+ result->append(i, iend);
+ else {
+ bool done = false;
+ while (!done)
+ {
+ int cmp = R->monomialCompare(i.getMonomial(), j.getMonomial());
+ n_compares++;
+ switch (cmp) {
+ case LT:
+ result->appendTerm(j.getCoefficient(), j.getMonomial());
+ ++j;
+ if (j == jend)
+ {
+ result->append(i, iend);
+ done = true;
+ }
+ break;
+ case GT:
+ result->appendTerm(i.getCoefficient(), i.getMonomial());
+ ++i;
+ if (i == iend)
+ {
+ result->append(j, jend);
+ done = true;
+ }
+ break;
+ case EQ:
+ R->coefficientSet(c, i.getCoefficient());
+ R->coefficientAddTo(c, j.getCoefficient());
+ if (!R->coefficientIsZero(c))
+ result->appendTerm(c, i.getMonomial());
+ ++j;
+ ++i;
+ if (j == jend)
+ {
+ result->append(i, iend);
+ done = true;
+ }
+ else
+ {
+ if (i == iend)
+ {
+ result->append(j, jend);
+ done = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+const_monomial Poly::backMonomial() const {
+ ASSERT(begin() != end());
+ return &(monoms.front()) + R->maxMonomialSize() * (nTerms() - 1);
+}
+
+void Poly::multByTerm(coefficient a, const_monomial m)
+{
+ size_t p = 0;
+ exponent * n = &monoms[0];
+ iterator j = end();
+
+ for (iterator i = begin(); i != j; ++i, ++p, n += R->maxMonomialSize())
+ {
+ monomial nmon = n;
+ R->coefficientMultTo(coeffs[p], a);
+ R->monomialMultTo(nmon, m); // changes the monomial pointed to by n.
+ }
+}
+void Poly::multByMonomial(const_monomial m)
+{
+ size_t p = 0;
+ exponent * n = &monoms[0];
+ iterator j = end();
+
+ for (iterator i = begin(); i != j; ++i, ++p, n += R->maxMonomialSize())
+ {
+ monomial nmon = n;
+ R->monomialMultTo(nmon, m); // changes the monomial pointed to by n.
+ }
+}
+
+void Poly::dump() const
+{
+ std::cout << "coeffs: ";
+ for (unsigned i=0; i<coeffs.size(); i++)
+ std::cout << " " << coeffs[i];
+ std::cout << std::endl;
+ std::cout << "monoms: ";
+ for (unsigned int i=0; i<monoms.size(); i++)
+ std::cout << " " << monoms[i];
+ std::cout << std::endl;
+}
+
+void Poly::parse(std::istream &i)
+{
+ char next = i.peek();
+ if (next == '0')
+ {
+ i.get();
+ return;
+ }
+ for (;;)
+ {
+ bool is_neg = false;
+ next = i.peek();
+ if (next == '+')
+ {
+ i.get();
+ next = i.peek();
+ }
+ else if (next == '-')
+ {
+ is_neg = true;
+ i.get();
+ next = i.peek();
+ }
+ if (isdigit(next) || isalpha(next) || next == '<')
+ {
+ int a = 1;
+ coefficient b;
+ if (isdigit(next))
+ {
+ i >> a;
+ next = i.peek();
+ }
+ if (is_neg) a = -a;
+ size_t firstloc = monoms.size();
+ monoms.resize(firstloc + R->maxMonomialSize());
+ monomial m = &monoms[firstloc];
+ R->coefficientFromInt(b,a);
+ coeffs.push_back(b);
+ if (isalpha(next) || next == '<')
+ {
+ R->monomialParse(i, m);
+ }
+ next = i.peek();
+ if (next == '>') i.get();
+ }
+ else return;
+ }
+}
+
+void Poly::display(std::ostream &o, bool print_comp) const
+{
+ long p = R->charac();
+ int maxpos = (p+1)/2;
+ if (nTerms() == 0)
+ {
+ o << "0";
+ return;
+ }
+ int nterms = 0;
+ for (const_iterator i = begin(); i != end(); ++i)
+ {
+ nterms++;
+ coefficient a = i.getCoefficient();
+ bool is_neg = (a > maxpos);
+ if (is_neg) a = p-a;
+ if (is_neg)
+ o << "-";
+ else if (nterms > 1)
+ o << "+";
+ bool print_one = (a == 1);
+ if (a != 1) o << a;
+ R->monomialDisplay(o, i.getMonomial(), print_comp, print_one);
+ }
+}
+
+size_t Poly::getMemoryUse() const
+{
+ size_t total = sizeof(const PolyRing *);
+ total += sizeof(coefficient) * coeffs.capacity();
+ total += sizeof(int) * monoms.capacity();
+ return total;
+}
+
+void Poly::see(bool print_comp) const
+{
+ display(std::cout, print_comp);
+ std::cout << std::endl;
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/Poly.hpp b/src/mathicgb/Poly.hpp
new file mode 100644
index 0000000..f8f4292
--- /dev/null
+++ b/src/mathicgb/Poly.hpp
@@ -0,0 +1,132 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _poly_h_
+#define _poly_h_
+
+#include <vector>
+#include "PolyRing.hpp"
+
+class Poly {
+ const PolyRing *R;
+ std::vector<coefficient> coeffs;
+ std::vector<int> monoms;
+public:
+ Poly(const PolyRing *R0) : R(R0) {};
+ ~Poly() {} // nothing needs to be done
+
+ void parse(std::istream &i); // reads into this.
+ void display(std::ostream &o, bool print_comp=true) const;
+ void see(bool print_comp) const;
+
+ class iterator {
+ // only for const objects...
+ size_t monsize;
+ std::vector<coefficient>::iterator ic;
+ std::vector<int>::iterator im;
+ friend class Poly;
+
+ iterator(Poly& f) : monsize(f.getRing()->maxMonomialSize()), ic(f.coeffs.begin()), im(f.monoms.begin()) {}
+ iterator(Poly& f,int) : ic(f.coeffs.end()), im() {}
+ public:
+ iterator() {}
+ iterator operator++() { ++ic; im += monsize; return *this; }
+ coefficient &getCoefficient() { return *ic; }
+ monomial getMonomial() { return &*im; }
+ size_t operator-(const iterator &b) const { return ic - b.ic; }
+ friend bool operator==(const iterator &a, const iterator &b);
+ friend bool operator!=(const iterator &a, const iterator &b);
+ };
+
+ class const_iterator {
+ // only for const objects...
+ size_t monsize;
+ std::vector<coefficient>::const_iterator ic;
+ std::vector<int>::const_iterator im;
+ friend class Poly;
+
+ const_iterator(const Poly& f) : monsize(f.getRing()->maxMonomialSize()), ic(f.coeffs.begin()), im(f.monoms.begin()) {}
+ const_iterator(const Poly& f,int) : ic(f.coeffs.end()), im() {}
+ public:
+ const_iterator() {}
+ const_iterator operator++() { ++ic; im += monsize; return *this; }
+ coefficient getCoefficient() { return *ic; }
+ const_monomial getMonomial() { return &*im; }
+ size_t operator-(const const_iterator &b) const { return ic - b.ic; }
+ friend bool operator==(const const_iterator &a, const const_iterator &b);
+ friend bool operator!=(const const_iterator &a, const const_iterator &b);
+ };
+
+ void append(iterator &first, iterator &last);
+ // Insert [first, last) onto the end of this.
+ // This invalidates iterators on this.
+
+ Poly *copy() const;
+
+ void appendTerm(coefficient a, const_monomial m);
+ // all iterators are invalid after this
+
+ const_iterator begin() const { return const_iterator(*this); }
+ const_iterator end() const { return const_iterator(*this,1); }
+ const_monomial backMonomial() const;
+
+ iterator begin() { return iterator(*this); }
+ iterator end() { return iterator(*this,1); }
+
+
+ static Poly * add(const PolyRing *R,
+ iterator first1,
+ iterator last1,
+ iterator first2,
+ iterator last2,
+ size_t &n_compares);
+
+ void multByTerm(coefficient a, const_monomial m);
+ void multByMonomial(const_monomial m);
+ void multByCoefficient(coefficient a);
+
+ void makeMonic();
+
+ const_monomial getLeadMonomial() const { return &(monoms[0]); }
+ const_coefficient getLeadCoefficient() const { return coeffs[0]; }
+ long getLeadComponent() const { return R->monomialGetComponent(&(monoms[0])); }
+ bool isZero() const { return coeffs.empty(); }
+ size_t nTerms() const { return coeffs.size(); }
+
+ size_t getMemoryUse() const;
+
+ void copy(Poly &result) const;
+ friend bool operator==(const Poly &a, const Poly &b);
+
+ // deprecated
+ const PolyRing *getRing() const { return R; }
+
+ // use this instead of getRing()
+ const PolyRing& ring() const {return *R;}
+
+ void dump() const; // used for debugging
+};
+
+inline bool operator==(const Poly::iterator &a, const Poly::iterator &b)
+{
+ return a.ic == b.ic;
+}
+inline bool operator!=(const Poly::iterator &a, const Poly::iterator &b)
+{
+ return a.ic != b.ic;
+}
+
+inline bool operator==(const Poly::const_iterator &a, const Poly::const_iterator &b)
+{
+ return a.ic == b.ic;
+}
+inline bool operator!=(const Poly::const_iterator &a, const Poly::const_iterator &b)
+{
+ return a.ic != b.ic;
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyBasis.cpp b/src/mathicgb/PolyBasis.cpp
new file mode 100644
index 0000000..586ee6f
--- /dev/null
+++ b/src/mathicgb/PolyBasis.cpp
@@ -0,0 +1,343 @@
+#include "stdinc.h"
+#include "Ideal.hpp"
+#include "PolyBasis.hpp"
+
+#include "DivisorLookup.hpp"
+
+PolyBasis::PolyBasis(
+ const PolyRing& ring,
+ FreeModuleOrder& order,
+ std::auto_ptr<DivisorLookup> divisorLookup
+):
+ mRing(ring),
+ mOrder(order),
+ mDivisorLookup(divisorLookup)
+{
+ ASSERT(mDivisorLookup.get() != 0);
+ mDivisorLookup->setBasis(*this);
+}
+
+PolyBasis::~PolyBasis() {
+ EntryIter const stop = mEntries.end();
+ for (EntryIter it = mEntries.begin(); it != stop; ++it) {
+ ASSERT(it->poly != 0);
+ delete it->poly;
+ }
+}
+
+std::auto_ptr<Ideal> PolyBasis::initialIdeal() const {
+ std::auto_ptr<Ideal> ideal(new Ideal(mRing));
+ size_t const idealSize = size();
+ for (size_t gen = 0; gen != idealSize; ++gen) {
+ if (!retired(gen) && leadMinimal(gen)) {
+ std::auto_ptr<Poly> p(new Poly(&mRing));
+ p->appendTerm(1, leadMonomial(gen));
+ ideal->insert(p);
+ }
+ }
+ ideal->sort(mOrder);
+ return ideal;
+}
+
+void PolyBasis::insert(std::auto_ptr<Poly> poly) {
+ ASSERT(poly.get() != 0);
+ const size_t index = size();
+ EntryIter const stop = mEntries.end();
+ const_monomial const lead = poly->getLeadMonomial();
+#ifdef DEBUG
+ // lead monomials must be unique among basis elements
+ for (EntryIter it = mEntries.begin(); it != stop; ++it) {
+ if (it->retired)
+ continue;
+ ASSERT(!ring().monomialEQ(lead, it->poly->getLeadMonomial()));
+ }
+#endif
+
+ // Update information about minimal lead terms.
+ const bool leadMinimal = (divisor(lead) == static_cast<size_t>(-1));
+ if (leadMinimal) {
+ class MultipleOutput : public DivisorLookup::EntryOutput {
+ public:
+ MultipleOutput(EntryCont& entries): mEntries(entries) {}
+ virtual bool proceed(size_t index) {
+ ASSERT(index < mEntries.size());
+ mEntries[index].leadMinimal = false;
+ return true;
+ }
+ private:
+ EntryCont& mEntries;
+ };
+ MultipleOutput out(mEntries);
+ divisorLookup().multiples(lead, out);
+ }
+
+ mDivisorLookup->insert(lead, index);
+
+ mEntries.push_back(Entry());
+ Entry& entry = mEntries.back();
+ entry.poly = poly.release();
+ entry.leadMinimal = leadMinimal;
+
+ if (mUseBuchbergerLcmHitCache)
+ mBuchbergerLcmHitCache.push_back(0);
+}
+
+size_t PolyBasis::divisor(const_monomial mon) const {
+ size_t index = divisorLookup().divisor(mon);
+ ASSERT((index == static_cast<size_t>(-1)) ==
+ (divisorSlow(mon) == static_cast<size_t>(-1)));
+ ASSERT(index == static_cast<size_t>(-1) ||
+ ring().monomialIsDivisibleBy(mon, leadMonomial(index)));
+ return index;
+}
+
+size_t PolyBasis::divisorSlow(const_monomial mon) const {
+ const size_t stop = size();
+ for (size_t i = 0; i != stop; ++i)
+ if (!retired(i) && ring().monomialIsDivisibleBy(mon, leadMonomial(i)))
+ return i;
+ return static_cast<size_t>(-1);
+}
+
+bool PolyBasis::leadMinimalSlow(size_t index) const {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ const_monomial const lead = leadMonomial(index);
+ EntryCIter const skip = mEntries.begin() + index;
+ EntryCIter const stop = mEntries.end();
+ for (EntryCIter it = mEntries.begin(); it != stop; ++it) {
+ if (it->retired)
+ continue;
+ const_monomial const itLead = it->poly->getLeadMonomial();
+ if (ring().monomialIsDivisibleBy(lead, itLead) && it != skip)
+ return false;
+ }
+ return true;
+}
+
+size_t PolyBasis::minimalLeadCount() const {
+ // todo: use iterators
+ size_t minCount = 0;
+ const size_t stop = size();
+ for (size_t i = 0; i != stop; ++i)
+ if (!retired(i) && leadMinimal(i))
+ ++minCount;
+ return minCount;
+}
+
+size_t PolyBasis::maxIndexMinimalLead() const {
+ // todo: use iterators
+ size_t i = size() - 1;
+ for (; i != static_cast<size_t>(-1); --i)
+ if (!retired(i) && leadMinimal(i))
+ break;
+ return i;
+}
+
+size_t PolyBasis::monomialCount() const {
+ size_t sum = 0;
+ EntryCIter const stop = mEntries.end();
+ for (EntryCIter it = mEntries.begin(); it != stop; ++it)
+ if (!it->retired)
+ sum += it->poly->nTerms();
+ return sum;
+}
+
+size_t PolyBasis::getMemoryUse() const {
+ size_t sum = mEntries.capacity() * sizeof(mEntries.front());
+ EntryCIter const stop = mEntries.end();
+ for (EntryCIter it = mEntries.begin(); it != stop; ++it)
+ if (!it->retired)
+ sum += it->poly->getMemoryUse();
+ return sum;
+}
+
+bool PolyBasis::buchbergerLcmCriterion(size_t a, size_t b) const {
+ ASSERT(a != b);
+ ASSERT(!retired(a));
+ ASSERT(!retired(b));
+ // We don't need to set the weights on the lcm since we will only use it
+ // for testing divisibility, not for determining order.
+ monomial lcmAB = mRing.allocMonomial();
+ mRing.monomialLeastCommonMultipleNoWeights
+ (leadMonomial(a), leadMonomial(b), lcmAB);
+ bool value = buchbergerLcmCriterion(a, b, lcmAB);
+ mRing.freeMonomial(lcmAB);
+ return value;
+}
+
+bool PolyBasis::buchbergerLcmCriterion
+ (size_t a, size_t b, const_monomial lcmAB) const
+{
+ ASSERT(a != b);
+ ASSERT(!retired(a));
+ ASSERT(!retired(b));
+ ASSERT(mRing.monomialIsLeastCommonMultipleNoWeights
+ (leadMonomial(a), leadMonomial(b), lcmAB));
+
+ class Criterion : public DivisorLookup::EntryOutput {
+ public:
+ Criterion(size_t a, size_t b, const_monomial lcmAB, const PolyBasis& basis):
+ mA(a), mB(b),
+ mLcmAB(lcmAB),
+ mRing(basis.ring()),
+ mBasis(basis),
+ mHit(static_cast<size_t>(-1)),
+ mAlmostApplies(false) {}
+
+ virtual bool proceed(size_t index) {
+ ASSERT(index < mBasis.size());
+ ASSERT(!applies()); // should have stopped search in this case
+ ASSERT(mRing.monomialIsDivisibleBy(mLcmAB, mBasis.leadMonomial(index)));
+ if (!mBasis.leadMinimal(index))
+ return true;
+ if (index == mA || index == mB)
+ return true;
+ mAlmostApplies = true;
+
+ if (index < mA || index < mB) {
+ // check lcm(a,index) != lcm(a,b) <=>
+ // exists i such that max(a[i], c[i]) != max(a[i],b[i]) <=>
+ // exists i such that b[i] > a[i] && b[i] > c[i]
+ const_monomial leadA = mBasis.leadMonomial(mA);
+ const_monomial leadB = mBasis.leadMonomial(mB);
+ const_monomial leadC = mBasis.leadMonomial(index);
+ if (//index > mA &&
+ !mRing.monomialHasStrictlyLargerExponent(leadB, leadC, leadA))
+ return true;
+
+ // check lcm(b,index) != lcm(a,b)
+ if (//index > mB &&
+ !mRing.monomialHasStrictlyLargerExponent(leadA, leadC, leadB))
+ return true;
+ }
+
+ mHit = index;
+ return false; // stop search
+ }
+
+ const_monomial lcmAB() const {return mLcmAB;}
+ bool almostApplies() const {return mAlmostApplies;}
+ bool applies() const {return mHit != static_cast<size_t>(-1);}
+ size_t hit() const {return mHit;}
+
+ private:
+ size_t mA;
+ size_t mB;
+ const_monomial mLcmAB;
+ const PolyRing& mRing;
+ const PolyBasis& mBasis;
+ size_t mHit; // the divisor that made the criterion apply
+ bool mAlmostApplies; // applies ignoring lcm(a,b)=lcm(a,c) complication
+ };
+
+ ++mStats.buchbergerLcmQueries;
+ bool applies = false;
+ bool almostApplies = false;
+ {
+ Criterion criterion(a, b, lcmAB, *this);
+ if (mUseBuchbergerLcmHitCache) {
+ // Check cacheB first since when I tried this there was a higher hit rate
+ // for cacheB than cacheA. Might not be a persistent phenomenon, but
+ // there's no downside to trying out cacheB first so I'm going for that.
+ //
+ // I update the cache if the second check is a hit but not if the first
+ // check is a hit. In the one test I did, the worst hit rate was from
+ // updating the cache every time, the second best hit rate was from
+ // not updating the cache (from cache hits) and the best hit rate was
+ // from doing this.
+ //
+ // The idea is that when the first cache check is a hit,
+ // the second cache member might have been a hit too, and updating it
+ // might replace a high hit rate element with a low hit rate element,
+ // which would be bad. When the second cache check is a hit, we know
+ // that the first one wasn't (or we would have taken an early exit),
+ // so we have reason to suspect that the first cache element is not
+ // a high hit rate element. So it should be better to replace it.
+ // That idea seems to be right since it worked better in the one
+ // test I did.
+ size_t cacheB = mBuchbergerLcmHitCache[b];
+ if (!applies && !retired(cacheB) &&
+ ring().monomialIsDivisibleBy
+ (criterion.lcmAB(), leadMonomial(cacheB)))
+ applies = !criterion.Criterion::proceed(cacheB);
+
+ size_t cacheA = mBuchbergerLcmHitCache[a];
+ if (!applies && !retired(cacheA) &&
+ ring().monomialIsDivisibleBy
+ (criterion.lcmAB(), leadMonomial(cacheA))) {
+ applies = !criterion.Criterion::proceed(cacheA);
+ if (applies)
+ mBuchbergerLcmHitCache[b] = cacheA;
+ }
+ }
+ if (applies)
+ ++mStats.buchbergerLcmCacheHits;
+ else {
+ ASSERT(!criterion.applies());
+ mDivisorLookup->divisors(criterion.lcmAB(), criterion);
+ applies = criterion.applies();
+
+ if (mUseBuchbergerLcmHitCache && applies) {
+ ASSERT(criterion.hit() < size());
+ mBuchbergerLcmHitCache[a] = criterion.hit();
+ mBuchbergerLcmHitCache[b] = criterion.hit();
+ }
+ }
+ if (!applies)
+ almostApplies = criterion.almostApplies();
+ }
+
+ if (applies)
+ ++mStats.buchbergerLcmHits;
+ else if (almostApplies)
+ ++mStats.buchbergerLcmNearHits;
+
+ ASSERT(applies == buchbergerLcmCriterionSlow(a, b));
+ return applies;
+}
+
+bool PolyBasis::buchbergerLcmCriterionSlow(size_t a, size_t b) const {
+ ASSERT(a != b);
+ ASSERT(!retired(a));
+ ASSERT(!retired(b));
+
+ monomial lcmAB = ring().allocMonomial();
+ monomial lcm = ring().allocMonomial();
+ ring().monomialLeastCommonMultiple
+ (leadMonomial(a), leadMonomial(b), lcmAB);
+ size_t stop = size();
+ size_t i = 0;
+ for (; i < stop; ++i) {
+ if (retired(i) || !leadMinimal(i))
+ continue;
+ if (!ring().monomialIsDivisibleBy(lcmAB, leadMonomial(i)))
+ continue;
+ if (i == a || i == b)
+ continue;
+ if (i < a || i < b) {
+ ring().monomialLeastCommonMultiple
+ (leadMonomial(a), leadMonomial(i), lcm);
+ if (ring().monomialEQ(lcmAB, lcm))
+ continue;
+
+ ring().monomialLeastCommonMultiple
+ (leadMonomial(b), leadMonomial(i), lcm);
+ if (ring().monomialEQ(lcmAB, lcm))
+ continue;
+ }
+ break;
+ }
+ ring().freeMonomial(lcmAB);
+ ring().freeMonomial(lcm);
+ return i != stop;
+}
+
+PolyBasis::Entry::Entry():
+ poly(0),
+ leadMinimal(0),
+ retired(false),
+ usedAsStartCount(0),
+ usedAsReducerCount(0),
+ possibleReducerCount(0),
+ nonSignatureReducerCount(0) {}
diff --git a/src/mathicgb/PolyBasis.hpp b/src/mathicgb/PolyBasis.hpp
new file mode 100644
index 0000000..fc02c9b
--- /dev/null
+++ b/src/mathicgb/PolyBasis.hpp
@@ -0,0 +1,267 @@
+#ifndef _poly_basis_h_
+#define _poly_basis_h_
+
+#include "Poly.hpp"
+#include "DivisorLookup.hpp"
+#include <vector>
+#include <memory>
+
+class PolyRing;
+class Ideal;
+class FreeModuleOrder;
+
+class PolyBasis {
+public:
+ // Ring and order must live for as long as this object. divisorLookupFactory
+ // only needs to live for the duration of the constructor.
+ PolyBasis(
+ const PolyRing& ring,
+ FreeModuleOrder& order,
+ std::auto_ptr<DivisorLookup> divisorLookup);
+
+ // Deletes the Poly's stored in the basis.
+ ~PolyBasis();
+
+ // Returns the initial monomial ideal of the basis (not the ideal).
+ std::auto_ptr<Ideal> initialIdeal() const;
+
+ // Inserts a polynomial into the basis at index size().
+ // Lead monomials must be unique among basis elements.
+ // So the index is size() - 1 afterwards since size() will increase by 1.
+ void insert(std::auto_ptr<Poly> poly);
+
+ // Returns the index of a basis element whose lead term divides mon.
+ // Returns -1 if there is no such basis element.
+ size_t divisor(const_monomial mon) const;
+
+ // As the non-slow version, but uses simpler and slower code.
+ size_t divisorSlow(const_monomial mon) const;
+
+ // Replaces basis element at index with the given new value. The lead
+ // term of the new polynomial must be the same as the previous one.
+ // This is useful for auto-tail-reduction.
+ void replaceSameLeadTerm(size_t index, std::auto_ptr<Poly> newValue) {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ ASSERT(newValue.get() != 0);
+ ASSERT(!newValue->isZero());
+ ASSERT(mRing.monomialEQ(leadMonomial(index), newValue->getLeadMonomial()));
+ mDivisorLookup->remove(leadMonomial(index));
+ delete mEntries[index].poly;
+ mEntries[index].poly = newValue.release();
+ mDivisorLookup->insert(leadMonomial(index), index);
+ }
+
+ struct Stats {
+ Stats():
+ buchbergerLcmQueries(0),
+ buchbergerLcmHits(0),
+ buchbergerLcmNearHits(0),
+ buchbergerLcmCacheHits(0) {}
+
+ unsigned long long buchbergerLcmQueries;
+ unsigned long long buchbergerLcmHits;
+ unsigned long long buchbergerLcmNearHits;
+ unsigned long long buchbergerLcmCacheHits;
+ };
+ Stats stats() const {return mStats;}
+
+ // Returns true if Buchberger's second criterion for eliminating useless
+ // S-pairs applies to the pair (a,b). Let
+ // l(a,b) = lcm(lead(a), lead(b)).
+ // The criterion says that if there is some other basis element c such that
+ // lead(c)|l(a,b)
+ // and
+ // l(a,c) reduces to zero, and
+ // l(b,c) reduces to zero
+ // then (a,b) will reduce to zero (using classic non-signature reduction).
+ //
+ // This criterion is less straight forward to apply in case for example
+ // l(a,b) = l(a,c) = l(b,c)
+ // since then there is the potential to erroneously eliminate all the three
+ // pairs among a,b,c on the assumption that the other two pairs will reduce
+ // to zero. In such cases, we eliminate the pair with the lowest indexes.
+ // This allows removing generators that get non-minimal lead term without
+ // problems.
+ bool buchbergerLcmCriterion(size_t a, size_t b) const;
+
+ // As the overload with an lcmAb parameter, except lcmAB must be the lcm of
+ // the lead monomials of a and b. Then this quantity does not have to be
+ // computed for the criterion.
+ bool buchbergerLcmCriterion(size_t a, size_t b, const_monomial lcmAB) const;
+
+ // As the non-slow version, but uses simpler and slower code.
+ bool buchbergerLcmCriterionSlow(size_t a, size_t b) const;
+
+ // Returns the number of basis elements, including retired elements.
+ size_t size() const {return mEntries.size();}
+
+ // Returns the ambient polynomial ring of the polynomials in the basis.
+ const PolyRing& ring() const {return mRing;}
+
+ // Returns the term order on the basis.
+ const FreeModuleOrder& order() const {return mOrder;}
+
+ // Returns a data structure containing the lead monomial of each lead
+ // monomial.
+ const DivisorLookup& divisorLookup() const {return *mDivisorLookup;}
+
+ // Retires the basis element at index, which frees the memory associated
+ // to it, including the basis element polynomial, and marks it as retired.
+ // todo: implement
+ std::auto_ptr<Poly> retire(size_t index) {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ mDivisorLookup->remove(leadMonomial(index));
+ std::auto_ptr<Poly> poly(mEntries[index].poly);
+ mEntries[index].poly = 0;
+ mEntries[index].retired = true;
+ return poly;
+ }
+
+ // Returns true of the basis element at index has been retired.
+ bool retired(size_t index) const {
+ ASSERT(index < size());
+ return mEntries[index].retired;
+ }
+
+ // Returns the basis element polynomial at index.
+ Poly& poly(size_t index) {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ return *mEntries[index].poly;
+ }
+
+ // Returns the basis element polynomial at index.
+ const Poly& poly(size_t index) const {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ return *mEntries[index].poly;
+ }
+
+ const_monomial leadMonomial(size_t index) const {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ return poly(index).getLeadMonomial();
+ }
+
+ coefficient leadCoefficient(size_t index) const {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ return poly(index).getLeadCoefficient();
+ }
+
+ // Returns true if the leading monomial of the basis element at index is not
+ // divisible by the lead monomial of any other basis element. Lead
+ // monomials are required to be unique among basis elements, so the case
+ // of several equal lead monomials does not occur.
+ bool leadMinimal(size_t index) const {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ ASSERT(mEntries[index].leadMinimal == leadMinimalSlow(index));
+ return mEntries[index].leadMinimal;
+ }
+
+ // Returns true m is not divisible by the lead monomial of any
+ // basis element. Equality counts as divisibility.
+ bool leadMinimal(const Poly& poly) const {
+ ASSERT(&poly != 0);
+ return mDivisorLookup->divisor(poly.getLeadMonomial()) !=
+ static_cast<size_t>(-1);
+ }
+
+ // Returns the number of basis elements with minimal lead monomial.
+ size_t minimalLeadCount() const;
+
+ // Returns the index of the basis element of maximal index
+ // whose lead monomial is minimal.
+ size_t maxIndexMinimalLead() const;
+
+ // Returns the basis element polynomial at index.
+ const Poly& basisElement(size_t index) const {
+ ASSERT(index < size());
+ ASSERT(!retired(index));
+ return *mEntries[index].poly;
+ }
+
+ // Returns the number of monomials across all the basis elements.
+ // Monomials that appear in more than one basis element are counted more
+ // than once.
+ size_t monomialCount() const;
+
+ // Returns how many bytes has been allocated by this object.
+ size_t getMemoryUse() const;
+
+ void usedAsStart(size_t index) const {
+ ASSERT(index < size());
+ ++mEntries[index].usedAsStartCount;
+ }
+
+ unsigned long long usedAsStartCount(size_t index) const {
+ ASSERT(index < size());
+ return mEntries[index].usedAsStartCount;
+ }
+
+ void usedAsReducer(size_t index) const {
+ ASSERT(index < size());
+ ++mEntries[index].usedAsReducerCount;
+ }
+
+ unsigned long long usedAsReducerCount(size_t index) const {
+ ASSERT(index < size());
+ return mEntries[index].usedAsReducerCount;
+ }
+
+ void wasPossibleReducer(size_t index) const {
+ ASSERT(index < size());
+ ++mEntries[index].possibleReducerCount;
+ }
+
+ unsigned long long wasPossibleReducerCount(size_t index) const {
+ ASSERT(index < size());
+ return mEntries[index].possibleReducerCount;
+ }
+
+ void wasNonSignatureReducer(size_t index) const {
+ ASSERT(index < size());
+ ++mEntries[index].nonSignatureReducerCount;
+ }
+
+ unsigned long long wasNonSignatureReducerCount(size_t index) const {
+ ASSERT(index < size());
+ return mEntries[index].nonSignatureReducerCount;
+ }
+
+private:
+ // Slow versions use simpler code. Used to check results in debug mode.
+ bool leadMinimalSlow(size_t index) const;
+
+ class Entry {
+ public:
+ Entry();
+
+ Poly* poly;
+ bool leadMinimal;
+ bool retired;
+
+ // Statistics on reducer choice in reduction
+ mutable unsigned long long usedAsStartCount;
+ mutable unsigned long long usedAsReducerCount;
+ mutable unsigned long long possibleReducerCount;
+ mutable unsigned long long nonSignatureReducerCount;
+ };
+ typedef std::vector<Entry> EntryCont;
+ typedef EntryCont::iterator EntryIter;
+ typedef EntryCont::const_iterator EntryCIter;
+
+ const PolyRing& mRing;
+ FreeModuleOrder& mOrder;
+ std::auto_ptr<DivisorLookup> mDivisorLookup;
+ std::vector<Entry> mEntries;
+ mutable Stats mStats;
+
+ static const bool mUseBuchbergerLcmHitCache = true;
+ mutable std::vector<size_t> mBuchbergerLcmHitCache;
+};
+
+#endif
diff --git a/src/mathicgb/PolyGeoBucket.cpp b/src/mathicgb/PolyGeoBucket.cpp
new file mode 100644
index 0000000..2bd0e00
--- /dev/null
+++ b/src/mathicgb/PolyGeoBucket.cpp
@@ -0,0 +1,264 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "PolyGeoBucket.hpp"
+
+const size_t heap_size[GEOHEAP_SIZE] = {4, 16, 64, 256, 1024, 4096,
+ 16384, 65536, 262144, 1048576, 4194304,
+ 16777216, 67108864, 268435456,
+ 1073741824};
+
+PolyGeoBucket::PolyGeoBucket(const PolyRing *R0)
+ : Reducer(),
+ R(R0),
+ top_of_heap(-1),
+ lead(-1)
+{
+ int i;
+ for (i=0; i<GEOHEAP_SIZE; i++)
+ {
+ heap[i].poly = 0;
+ // the iterators are NOT VALID if this poly is the null value
+ }
+}
+
+PolyGeoBucket::~PolyGeoBucket()
+{
+ resetReducer();
+}
+
+///////////////////////////////////////
+// Helper routines ////////////////////
+///////////////////////////////////////
+
+void PolyGeoBucket::add_to(heap_record &a, heap_record &b)
+// handles the case when a.poly == 0 too
+// If the result is 0, then a.poly is set to 0.
+{
+ assert(b.poly != 0);
+ assert(b.first != b.last);
+ if (a.poly == 0)
+ {
+ a = b;
+ b.poly = 0;
+ }
+ else
+ {
+ size_t ncmps;
+ Poly *g = Poly::add(R, a.first, a.last, b.first, b.last, ncmps);
+ stats_n_inserts++;
+ delete a.poly;
+ a.poly = 0;
+ stats_n_compares += ncmps;
+ if (g->isZero())
+ {
+ delete g;
+ }
+ else
+ {
+ a.poly = g;
+ a.first = g->begin();
+ a.last = g->end();
+ }
+ delete b.poly;
+ b.poly = 0;
+ }
+}
+
+void PolyGeoBucket::update_size_stats()
+{
+ size_t size_polys = 0;
+ size_t size_live = 0;
+ for (int i = 0; i<= top_of_heap; i++)
+ {
+ if (heap[i].poly == 0) continue;
+ size_polys += heap[i].poly->nTerms();
+ size_live += heap[i].last - heap[i].first;
+ }
+ if (size_polys > stats_maxsize)
+ stats_maxsize = size_polys;
+ if(size_live >= stats_maxsize_live)
+ stats_maxsize_live = size_live;
+}
+void PolyGeoBucket::insert(heap_record &a)
+{
+ // Takes the heap_record, and adds it into one of the heap elems
+ // If the size changes, then this continues until the heap
+ // sizes are valid
+
+ lead = -1;
+ size_t len = a.last - a.first;
+ int i= 0;
+ while (len >= heap_size[i]) i++;
+
+ add_to(heap[i], a);
+ if (heap[i].poly != 0)
+ {
+ len = heap[i].last - heap[i].first;
+ while (len >= heap_size[i])
+ {
+ i++;
+
+ add_to(heap[i], heap[i-1]);
+ if (heap[i].poly == 0) break;
+ len = heap[i].last - heap[i].first;
+ }
+ }
+ if (i > top_of_heap)
+ top_of_heap = i;
+
+ stats_n_inserts++;
+ update_size_stats();
+}
+
+bool PolyGeoBucket::computeLeadTerm()
+// returns true if the heap does not add to 0
+{
+ int lead_so_far = -1;
+ for (int i=0; i <= top_of_heap; i++)
+ {
+ if (heap[i].poly == 0) continue;
+ if (lead_so_far < 0)
+ {
+ lead_so_far = i;
+ continue;
+ }
+ int cmp = R->monomialCompare(heap[lead_so_far].first.getMonomial(), heap[i].first.getMonomial());
+ stats_n_compares++;
+ if (cmp == GT) continue;
+ if (cmp == LT)
+ {
+ lead_so_far = i;
+ continue;
+ }
+ // At this point we have equality
+ R->coefficientAddTo(heap[lead_so_far].first.getCoefficient(), heap[i].first.getCoefficient());
+ // now increment one of these
+ ++heap[i].first;
+ if (heap[i].first == heap[i].last)
+ {
+ delete heap[i].poly;
+ heap[i].poly = 0;
+ }
+ if (R->coefficientIsZero(heap[lead_so_far].first.getCoefficient()))
+ {
+ ++heap[lead_so_far].first;
+ if (heap[lead_so_far].first == heap[lead_so_far].last)
+ {
+ delete heap[lead_so_far].poly;
+ heap[lead_so_far].poly = 0;
+ }
+ lead_so_far = -1;
+ i = -1;
+ }
+ }
+ lead = lead_so_far;
+ return (lead_so_far >= 0);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+
+void PolyGeoBucket::insertTail(const_term multiplier, const Poly *f)
+{
+ if (f->nTerms() <= 1) return;
+ Poly *g = f->copy();
+ g->multByTerm(multiplier.coeff, multiplier.monom);
+ heap_record a;
+ a.poly = g;
+ a.first = g->begin();
+ ++a.first;
+ a.last = g->end();
+ insert(a);
+}
+
+void PolyGeoBucket::insert(monomial multiplier, const Poly *f)
+{
+ Poly *g = f->copy();
+ g->multByMonomial(multiplier);
+ heap_record a;
+ a.poly = g;
+ a.first = g->begin();
+ a.last = g->end();
+ insert(a);
+}
+
+bool PolyGeoBucket::findLeadTerm(const_term &result)
+{
+ if (lead == -1 && !computeLeadTerm()) return false;
+ result.coeff = heap[lead].first.getCoefficient();
+ result.monom = heap[lead].first.getMonomial();
+ return true;
+}
+
+void PolyGeoBucket::removeLeadTerm()
+// returns true if there is a term to extract
+{
+ if (lead == -1 && !computeLeadTerm()) return;
+ ++heap[lead].first;
+ if (heap[lead].first == heap[lead].last)
+ {
+ // We are at the end here, so free poly, and set poly to 0.
+ delete heap[lead].poly;
+ heap[lead].poly = 0;
+ lead = -1;
+ return;
+ }
+ lead = -1;
+}
+
+void PolyGeoBucket::value(Poly &result)
+// keep extracting lead term until done
+{
+ heap_record a;
+ a.poly = 0;
+ for (int i=0; i<=top_of_heap; i++)
+ {
+ if (heap[i].poly == 0) continue;
+ add_to(a, heap[i]);
+ }
+ top_of_heap = -1;
+ if (a.poly != 0)
+ {
+ result.append(a.first, a.last);
+ delete a.poly;
+ a.poly = 0;
+ }
+}
+
+void PolyGeoBucket::resetReducer()
+{
+ for (int i=0; i<=top_of_heap; i++)
+ {
+ if (heap[i].poly != 0)
+ {
+ delete heap[i].poly;
+ heap[i].poly = 0;
+ }
+ }
+ top_of_heap = -1;
+ lead = -1;
+}
+
+size_t PolyGeoBucket::getMemoryUse() const
+{
+ // size includes: each poly in the heap, as well as the
+ // size of the heap itself
+ size_t result =
+ Reducer::getMemoryUse() +
+ sizeof(heap_record) * GEOHEAP_SIZE;
+ for (int i=0; i<GEOHEAP_SIZE; ++i)
+ if (heap[i].poly != 0)
+ result += heap[i].poly->getMemoryUse();
+ return result;
+}
+
+void PolyGeoBucket::dump() const
+{
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyGeoBucket.hpp b/src/mathicgb/PolyGeoBucket.hpp
new file mode 100644
index 0000000..11b3037
--- /dev/null
+++ b/src/mathicgb/PolyGeoBucket.hpp
@@ -0,0 +1,59 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _polyGeoBucket_h_
+#define _polyGeoBucket_h_
+
+#include "Reducer.hpp"
+
+#define GEOHEAP_SIZE 15
+
+class PolyGeoBucket : public Reducer {
+public:
+ PolyGeoBucket(const PolyRing *R);
+ ~PolyGeoBucket();
+
+ virtual std::string description() const { return "geo buckets"; }
+
+ void insertTail(const_term multiplier, const Poly *f);
+ void insert(monomial multiplier, const Poly *f);
+
+ bool findLeadTerm(const_term &result);
+ void removeLeadTerm();
+
+ void value(Poly &result); // keep extracting lead term until done
+ void resetReducer();
+
+ void dump() const; // Used for debugging
+
+ size_t getMemoryUse() const;
+
+private:
+ void insert(const_term multiplier, Poly::iterator first, Poly::iterator last);
+
+ struct heap_record {
+ Poly *poly;
+ Poly::iterator first;
+ Poly::iterator last;
+ };
+
+ void add_to(heap_record &a, heap_record &b);
+ // sets a to a+b, sets b to 0.
+
+private:
+ const PolyRing *R;
+ heap_record heap[GEOHEAP_SIZE];
+ int top_of_heap;
+ int lead; // where the lead monomial is located (i.e. heap[lead]). = -1 means not known.
+
+ void insert(heap_record &a);
+ bool computeLeadTerm();
+ void update_size_stats();
+
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyHashReducer.cpp b/src/mathicgb/PolyHashReducer.cpp
new file mode 100644
index 0000000..8af435b
--- /dev/null
+++ b/src/mathicgb/PolyHashReducer.cpp
@@ -0,0 +1,180 @@
+// Copyright 2011 Michael E. Stillman
+
+#include <iostream>
+
+#include "stdinc.h"
+#include "PolyHashReducer.hpp"
+
+PolyHashReducer::PolyHashReducer(const PolyRing *R0)
+ : Reducer(),
+ R_(R0),
+ H_(R0,15)
+{
+ f_ = new HashPoly;
+ f_iter_ = f_->begin();
+}
+
+PolyHashReducer::~PolyHashReducer()
+{
+ delete f_;
+ f_ = 0;
+}
+
+void PolyHashReducer::merge(const HashPoly::const_iterator &fbegin, const HashPoly::const_iterator &fend,
+ const HashPoly::const_iterator &gbegin, const HashPoly::const_iterator &gend,
+ HashPoly &result,
+ size_t &n_compares)
+{
+ HashPoly::const_iterator f = fbegin;
+ HashPoly::const_iterator g = gbegin;
+
+ n_compares = 0;
+
+ if (f == fend)
+ result.insert(result.end(), g, gend);
+ else if (g == gend)
+ result.insert(result.end(), f, fend);
+ else {
+ bool done = false;
+ while (!done)
+ {
+ int cmp = R_->monomialCompare((*f)->monom, (*g)->monom);
+ n_compares++;
+ switch (cmp) {
+ case LT:
+ result.push_back(*g);
+ ++g;
+ if (g == gend)
+ {
+ result.insert(result.end(), f, fend);
+ done = true;
+ }
+ break;
+ case GT:
+ result.push_back(*f);
+ ++f;
+ if (f == fend)
+ {
+ result.insert(result.end(), g, gend);
+ done = true;
+ }
+ break;
+ case EQ:
+ std::cout << "Error: found equal monomials in PolyHashReducer::merge!" << std::endl;
+ break;
+ }
+ }
+ }
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+
+void PolyHashReducer::insertTail(const_term multiplier, const Poly *g1)
+{
+ size_t ncmps = 0;
+
+ if (g1->nTerms() <= 1) return;
+
+ HashPoly M;
+ // Poly *g = g1->copy(); //MES: ONLY COPY THE TAIL!!
+ // g->multByTerm(multiplier.coeff, multiplier.monom);
+ // H_.fromPoly(*g, M);
+
+ H_.insert(multiplier, ++(g1->begin()), g1->end(), M);
+
+ if (!M.empty())
+ {
+ HashPoly h;
+ merge(f_iter_, f_->end(), M.begin(), M.end(), h, ncmps);
+ swap(*f_, h);
+ f_iter_ = f_->begin();
+ }
+
+ stats_n_compares += ncmps;
+ stats_n_inserts++;
+ // delete g;
+}
+
+void PolyHashReducer::insert(monomial multiplier, const Poly *g1)
+{
+ size_t ncmps = 0;
+
+ // Poly *g = g1->copy();
+ // g->multByMonomial(multiplier);
+
+ HashPoly M;
+ // H_.fromPoly(*g, M);
+
+ H_.insert(multiplier, g1->begin(), g1->end(), M);
+
+ HashPoly h;
+ merge(f_iter_, f_->end(), M.begin(), M.end(), h, ncmps);
+ stats_n_compares += ncmps;
+ stats_n_inserts++;
+
+ // delete g;
+ swap(*f_, h);
+ f_iter_ = f_->begin();
+}
+
+bool PolyHashReducer::findLeadTerm(const_term &result)
+{
+ while (f_iter_ != f_->end())
+ {
+ if (H_.popTerm(*f_iter_, result.coeff, result.monom))
+ // returns true *f_iter is not the zero element
+ return true;
+ ++f_iter_;
+ }
+ return false;
+}
+
+void PolyHashReducer::removeLeadTerm()
+// returns true if there is a term to extract
+{
+ if (f_iter_ == f_->end()) return;
+ ++f_iter_;
+}
+
+void PolyHashReducer::value(Poly &result)
+// keep extracting lead term until done
+{
+ const_term t;
+ for ( ; f_iter_ != f_->end(); ++f_iter_)
+ if (findLeadTerm(t))
+ result.appendTerm(t.coeff, t.monom);
+ resetReducer();
+}
+
+size_t PolyHashReducer::getMemoryUse() const
+{
+ return
+ Reducer::getMemoryUse() +
+ H_.getMemoryUse() +
+ f_->capacity() * sizeof(PolyHashTable::node *);
+}
+
+void PolyHashReducer::resetReducer()
+{
+ const_term t;
+ for ( ; f_iter_ != f_->end(); ++f_iter_)
+ findLeadTerm(t);
+
+ delete f_;
+ f_ = new HashPoly;
+ f_iter_ = f_->begin();
+
+ H_.reset();
+}
+
+void PolyHashReducer::dump() const
+{
+ H_.dump(0);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyHashReducer.hpp b/src/mathicgb/PolyHashReducer.hpp
new file mode 100644
index 0000000..5213371
--- /dev/null
+++ b/src/mathicgb/PolyHashReducer.hpp
@@ -0,0 +1,56 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _polyHashReducer_h_
+#define _polyHashReducer_h_
+
+#include "Reducer.hpp"
+#include "PolyHashTable.hpp"
+
+class PolyHashReducer : public Reducer {
+public:
+ PolyHashReducer(const PolyRing *R);
+
+ virtual ~PolyHashReducer();
+
+ virtual std::string description() const { return "poly hash reducer"; }
+
+ void insertTail(const_term multiplier, const Poly *f);
+ void insert(monomial multiplier, const Poly *f);
+
+ bool findLeadTerm(const_term &result);
+ void removeLeadTerm();
+
+ void value(Poly &result); // keep extracting lead term until done
+ void dump() const;
+
+ size_t getMemoryUse() const;
+
+protected:
+ virtual void resetReducer();
+
+private:
+ void insert(const_term multiplier, Poly::const_iterator first, Poly::const_iterator last);
+
+ typedef PolyHashTable::MonomialArray HashPoly;
+
+ void merge(
+ const HashPoly::const_iterator &fbegin,
+ const HashPoly::const_iterator &fend,
+ const HashPoly::const_iterator &gbegin,
+ const HashPoly::const_iterator &gend,
+ HashPoly &result,
+ size_t &n_compares);
+
+ const PolyRing* R_;
+ HashPoly* f_;
+ HashPoly::iterator f_iter_;
+
+ PolyHashTable H_;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyHashTable.cpp b/src/mathicgb/PolyHashTable.cpp
new file mode 100644
index 0000000..cf0cc28
--- /dev/null
+++ b/src/mathicgb/PolyHashTable.cpp
@@ -0,0 +1,466 @@
+// Copyright 2011 Michael E. Stillman
+
+#include <iostream>
+#include <cmath>
+
+#include "stdinc.h"
+#include <mathic.h>
+
+#include "PolyHashTable.hpp"
+
+const double PolyHashTable::threshold = 0.1;
+const bool AlwaysInsertAtEnd = true;
+
+PolyHashTable::PolyHashTable(const PolyRing *R, int nbits)
+ : mRing(*R),
+ mHashMask((1 << nbits)-1),
+ mTableSize(1 << nbits),
+ mLogTableSize(nbits),
+ mNodeCount(0),
+ mBinCount(0),
+ mMaxCountBeforeRebuild(0)
+{
+ mHashTable.resize(mTableSize);
+
+ mMonomialSize = R->maxMonomialSize() * sizeof(exponent);
+ // set each entry of mHashTable to null
+
+ mStats.max_table_size = 0;
+ mStats.max_chain_len_ever = 0;
+ mStats.n_resets = 0;
+ mStats.max_n_nonempty_bins = 0;
+ mStats.n_inserts = 0;
+ mStats.n_moneq_true = 0;
+ mStats.n_moneq_false = 0;
+ mStats.n_easy_inserts = 0;
+
+ mStats.n_nodes = 0;
+ mStats.n_nonempty_bins = 0;
+ reset();
+}
+
+void PolyHashTable::reset()
+{
+ // The following no longer needs to be done
+
+ // Clear the table, and memory areas.
+
+ mStats.n_resets++;
+
+#if 0
+ ASSERT(mNodeCount != 0);
+ for (size_t count = 0; count < mTableSize; ++count)
+ {
+ if ( mHashTable[count] != 0)
+ std::cerr << "error: hash table is not zero on reset" << std::endl;
+ }
+#endif
+
+ mArena.freeAllAllocs();
+
+ mBinCount = 0;
+ mNodeCount = 0;
+
+ resetStats();
+ ASSERT(computeNodeCount() == 0);
+}
+
+size_t PolyHashTable::computeNodeCount() const
+{
+ size_t result = 0;
+ for (size_t i=0; i<mTableSize; i++)
+ {
+ for (node *p=mHashTable[i]; p != 0; p=p->next) result++;
+ }
+ return result;
+}
+void PolyHashTable::resize(size_t new_nbits)
+// Don't change the nodes, table, but do recreate mHashTable
+{
+ // Make a new vector of node *'s.
+ // swap the two.
+ // Loop through each one, reinserting the node into the proper bin.
+
+ // std::cout << "resizing PolyHashTable to " << new_nbits << " bits" << " count=" << mNodeCount << std::endl;
+ ASSERT(computeNodeCount() == mNodeCount);
+ size_t const old_table_size = mTableSize;
+ mTableSize = static_cast<size_t>(1) << new_nbits;
+ mLogTableSize = new_nbits;
+ mHashMask = mTableSize-1;
+ MonomialArray old_table(mTableSize);
+
+ std::swap(old_table, mHashTable);
+ mBinCount = 0;
+
+ for (size_t i = 0; i < old_table_size; ++i)
+ {
+ node *p = old_table[i];
+ while (p != 0)
+ {
+ node *q = p;
+ p = p->next;
+ q->next = 0;
+ // Reinsert node. We know that it is unique
+ const_monomial m = q->monom;
+ size_t hashval = mRing.monomialHashValue(m) & mHashMask;
+ node *r = mHashTable[hashval];
+ if (r == 0 || !AlwaysInsertAtEnd)
+ {
+ mBinCount++;
+ q->next = r;
+ mHashTable[hashval] = q;
+ }
+ else
+ {
+ // put it at the end
+ for ( ; r->next != 0; r = r->next) { }
+ r->next = q;
+ }
+ }
+ }
+
+ mStats.max_table_size = mTableSize;
+
+ // todo: consider if this can overflow or something else nasty might happen
+ mMaxCountBeforeRebuild =
+ static_cast<size_t>(std::floor(mTableSize * threshold));
+
+ ASSERT(computeNodeCount() == mNodeCount);
+}
+
+PolyHashTable::node * PolyHashTable::makeNode(coefficient coeff, const_monomial monom)
+{
+ mNodeCount++;
+ if (mNodeCount > mStats.n_nodes)
+ mStats.n_nodes = mNodeCount;
+ if (mBinCount > mStats.n_nonempty_bins)
+ mStats.n_nonempty_bins = mBinCount;
+ node *q = static_cast<node *>(mArena.allocObjectNoCon<node>());
+ q->next = 0;
+ q->monom = monom;
+ mRing.coefficientSet(q->coeff, coeff);
+ return q;
+}
+
+bool PolyHashTable::lookup_and_insert(const_monomial m, coefficient val, node *& result)
+// Returns true if m is in the table, else inserts m into the hash table (as is, without copying it)
+{
+ mStats.n_inserts++;
+
+ size_t fullHashVal = mRing.monomialHashValue(m);
+ size_t hashval = fullHashVal & mHashMask;
+
+ ASSERT(hashval < mHashTable.size());
+ node *tmpNode = mHashTable[hashval];
+ if (tmpNode == 0)
+ {
+ mStats.n_easy_inserts++;
+ result = makeNode(val, m);
+ mHashTable[hashval] = result;
+ }
+ else
+ {
+ // loop through to see if we have it
+ size_t chainLength = 0;
+ while (true)
+ {
+ if (mRing.monomialHashValue(tmpNode->monom) == fullHashVal && mRing.monomialEQ(m, tmpNode->monom))
+ {
+ mStats.n_moneq_true++;
+ mRing.coefficientAddTo(tmpNode->coeff, val);
+ result = tmpNode;
+ return true;
+ }
+ mStats.n_moneq_false++;
+ if (tmpNode->next == 0)
+ {
+ // time to insert the monomial
+ result = makeNode(val, m);
+ chainLength++;
+ if (AlwaysInsertAtEnd)
+ {
+ tmpNode->next = result;
+ }
+ else
+ {
+ result->next = mHashTable[hashval];
+ mHashTable[hashval] = result;
+ }
+ break;
+ }
+ tmpNode = tmpNode->next;
+ chainLength++;
+ }
+ if (chainLength > mStats.max_chain_len_ever)
+ mStats.max_chain_len_ever = chainLength;
+ }
+
+
+ if (mNodeCount > mMaxCountBeforeRebuild)
+ resize(mLogTableSize + 2); // increase by a factor of 4??
+
+ ASSERT(computeNodeCount() == mNodeCount);
+
+ return false;
+}
+
+void PolyHashTable::insert(Poly::const_iterator first,
+ Poly::const_iterator last,
+ MonomialArray &result)
+{
+ for (Poly::const_iterator i = first; i != last; ++i)
+ {
+ monomial monomspace = mRing.allocMonomial(mArena);
+ node *p;
+ mRing.monomialCopy(i.getMonomial(), monomspace);
+ bool found = lookup_and_insert(monomspace, i.getCoefficient(), p);
+ if (found)
+ {
+ // remove the monomial. It should be at the top of the mArena arena.
+ mRing.freeTopMonomial(mArena,monomspace);
+ }
+ else
+ {
+ result.push_back(p);
+ }
+ }
+}
+
+void PolyHashTable::insert(const_term multiplier,
+ Poly::const_iterator first,
+ Poly::const_iterator last,
+ MonomialArray &result)
+{
+ for (Poly::const_iterator i = first; i != last; ++i)
+ {
+ monomial monomspace = mRing.allocMonomial(mArena);
+ coefficient c;
+ mRing.coefficientSet(c, multiplier.coeff);
+ node *p;
+ mRing.monomialMult(multiplier.monom, i.getMonomial(), monomspace);
+ mRing.coefficientMultTo(c, i.getCoefficient());
+ bool found = lookup_and_insert(monomspace, c, p);
+ if (found)
+ {
+ // remove the monomial. It should be at the top of the mArena arena.
+ mRing.freeTopMonomial(mArena,monomspace);
+ }
+ else
+ {
+ result.push_back(p);
+ }
+ }
+}
+
+void PolyHashTable::insert(const_monomial multiplier,
+ Poly::const_iterator first,
+ Poly::const_iterator last,
+ MonomialArray &result)
+{
+ for (Poly::const_iterator i = first; i != last; ++i)
+ {
+ monomial monomspace = mRing.allocMonomial(mArena);
+ node *p;
+ mRing.monomialMult(multiplier, i.getMonomial(), monomspace);
+ bool found = lookup_and_insert(monomspace, i.getCoefficient(), p);
+ if (found)
+ {
+ // remove the monomial. It should be at the top of the mArena arena.
+ mRing.freeTopMonomial(mArena,monomspace);
+ }
+ else
+ {
+ result.push_back(p);
+ }
+ }
+}
+
+std::pair<bool, PolyHashTable::node*>
+PolyHashTable::insert(const_term termToInsert) {
+ node* n;
+ bool alreadyInThere = lookup_and_insert
+ (termToInsert.monom, termToInsert.coeff, n);
+ return std::make_pair(!alreadyInThere, n);
+}
+
+void PolyHashTable::unlink(node* p)
+{
+ mNodeCount--;
+ size_t const hashval = mRing.monomialHashValue(p->monom) & mHashMask;
+
+ node head;
+ node* tmpNode = mHashTable[hashval];
+ head.next = tmpNode;
+ for (node* q = &head; q->next != 0; q = q->next) {
+ if (q->next == p) {
+ q->next = p->next;
+ mHashTable[hashval] = head.next;
+ return;
+ }
+ }
+ // If we get here, then the node is not at its supposed hash value.
+ // That probably means either that the node has been deleted twice
+ // or that the value in the node changed so that its hash value
+ // changed. That is not allowed.
+ ASSERT(false);
+}
+
+void PolyHashTable::remove(node* n) {
+ unlink(n);
+}
+
+bool PolyHashTable::popTerm(node *p, coefficient &result_coeff, const_monomial &result_monom)
+{
+ unlink(p);
+ if (!mRing.coefficientIsZero(p->coeff))
+ {
+ result_coeff = p->coeff;
+ result_monom = p->monom;
+ return true;
+ }
+ return false;
+}
+
+void PolyHashTable::toPoly(const MonomialArray::const_iterator &fbegin,
+ const MonomialArray::const_iterator &fend,
+ Poly &result)
+{
+ coefficient coeff;
+ const_monomial monom;
+ for (MonomialArray::const_iterator i = fbegin; i != fend; ++i)
+ if (popTerm(*i, coeff, monom))
+ result.appendTerm(coeff, monom);
+}
+
+void PolyHashTable::toPoly(const MonomialArray &f, Poly &result)
+{
+ // Here we take the monomials in f. Find corresponding coeff, and append to result.
+ // ASSUMPTION: The monomials of f are in order, AND each appears in the hash table
+ toPoly(f.begin(), f.end(), result);
+}
+
+void PolyHashTable::fromPoly(const Poly &f, MonomialArray &result)
+{
+ insert(f.begin(), f.end(), result);
+}
+
+size_t PolyHashTable::getMemoryUse() const
+{
+ size_t result = mHashTable.capacity() * sizeof(node *);
+ result += mArena.getMemoryUse();
+ return result;
+}
+
+void PolyHashTable::resetStats() const
+{
+ // mStats.max_chain_len = 0;
+ mStats.n_nonempty_bins = 0;
+}
+
+void PolyHashTable::getStats(Stats &stats) const
+{
+ // First we set the values in mStats
+
+ // mStats.max_chain_len = 0;
+
+#if 0
+ mStats.n_nonempty_bins = 0;
+ mStats.n_nodes = 0;
+ for (size_t i = 0; i<mTableSize; i++)
+ {
+ if (mHashTable[i] == 0) continue;
+ mStats.n_nonempty_bins++;
+ size_t chain_len = 0;
+ for (node *p = mHashTable[i]; p != 0; p = p->next)
+ chain_len++;
+ mStats.n_nodes += chain_len;
+ if (chain_len > mStats.max_chain_len)
+ mStats.max_chain_len = chain_len;
+ }
+
+ if (mStats.max_chain_len > mStats.max_chain_len_ever)
+ mStats.max_chain_len_ever = mStats.max_chain_len;
+#endif
+
+ if (&stats != &mStats)
+ stats = mStats;
+}
+
+void PolyHashTable::dump(int level) const
+{
+ mic::ColumnPrinter pr;
+ pr.addColumn();
+ pr.addColumn(false);
+ pr.addColumn(false);
+
+ std::ostream& name = pr[0];
+ std::ostream& value = pr[1];
+ std::ostream& extra = pr[2];
+
+ name << "PolyHashTable stats:" << '\n';
+ value << "\n";
+ extra << "\n";
+
+ mRing.displayHashValues();
+
+ name << "# resets:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_resets) << '\n';
+ extra << '\n';
+
+ name << "# bins:\n";
+ value << mic::ColumnPrinter::commafy(mTableSize) << '\n';
+ extra << '\n';
+
+ name << "max # monomials in table:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_nodes) << '\n';
+ extra << '\n';
+
+ name << "max # nonempty bins:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_nonempty_bins) << '\n';
+ extra << mic::ColumnPrinter::percent(mStats.n_nonempty_bins, mTableSize) << " used\n";
+
+ name << "max chain length ever:\n";
+ value << mic::ColumnPrinter::commafy(mStats.max_chain_len_ever) << '\n';
+ extra << '\n';
+
+ // name << "max chain length this computation:\n";
+ // value << mic::ColumnPrinter::commafy(mStats.max_chain_len) << '\n';
+ // extra << '\n';
+
+ name << "# calls to lookup_and_insert:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_inserts) << '\n';
+ extra << '\n';
+
+ name << "# easy inserts:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_easy_inserts) << '\n';
+ extra << '\n';
+
+ name << "# monomialEQ true calls:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_moneq_true) << '\n';
+ extra << '\n';
+
+ name << "# monomialEQ false calls:\n";
+ value << mic::ColumnPrinter::commafy(mStats.n_moneq_false) << '\n';
+ extra << "(Also number of monomials inserted in populated bins)\n";
+
+ std::cout << pr << std::flush;
+
+ if (level == 0) return;
+
+ for (size_t i = 0; i<mTableSize; i++)
+ {
+ if (mHashTable[i] == 0) continue;
+ std::cout << "bin " << i << ": ";
+ Poly f(&mRing);
+ for (node *p = mHashTable[i]; p != 0; p = p->next)
+ f.appendTerm(p->coeff, p->monom);
+ f.display(std::cout);
+ std::cout << std::endl;
+ }
+
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyHashTable.hpp b/src/mathicgb/PolyHashTable.hpp
new file mode 100644
index 0000000..3119be8
--- /dev/null
+++ b/src/mathicgb/PolyHashTable.hpp
@@ -0,0 +1,147 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _PolyHashTable_h_
+#define _PolyHashTable_h_
+
+#include <vector>
+
+#include <memtailor.h>
+#include "PolyRing.hpp"
+#include "Poly.hpp"
+#include <utility>
+
+// The hash table is a map: monomial => coeff
+// Operations required on monomials:
+// hash (this will currently pick out one entry of a monomial)
+//
+// Operations required on coefficients:
+// add, maybe multiply too?
+// isZero?
+//
+// Does not take ownership of any of the monomials.
+class PolyHashTable {
+
+ static const double threshold; // if the number of elements is > threshold * (#monomials contained),
+ // then rebuild the table
+
+ // If true, then remove zero entries from hash table.
+ static const bool removeZerosFromTable = true;
+
+public:
+ struct node {
+ node *next;
+ coefficient coeff;
+ const_monomial monom;
+ void *unused;
+ };
+
+ struct Stats {
+ // These first ones are not reset
+ size_t max_table_size;
+ size_t max_chain_len_ever; // not reset after each reduction
+ size_t n_resets; // actually, number of times 'reset' is called
+ size_t max_n_nonempty_bins;
+ size_t n_inserts; // # calls to insert a monomial
+ size_t n_moneq_true; // total number of true monomialEQ calls during lookup_and_insert
+ size_t n_moneq_false; // same, but number for false.
+ size_t n_easy_inserts;
+
+ // These are set after each reduction
+ // size_t max_chain_len;
+ size_t n_nodes; // # of unique monomials represented here, maximum since last reset
+ size_t n_nonempty_bins;
+ };
+
+ typedef std::vector<node *> MonomialArray;
+
+ PolyHashTable(const PolyRing *R, int nbits);
+
+ ~PolyHashTable() {}
+
+ std::string description() const { return "polynomial hash table"; }
+
+ void reset(); // Clear the table, and memory areas.
+
+ void resize(size_t new_nbits); // Don't change the nodes, table, but do recreate hashtable_
+
+ void fromPoly(const Poly &f, MonomialArray &result);
+
+ void toPoly(const MonomialArray &f, Poly &result);
+
+ void toPoly(const MonomialArray::const_iterator &fbegin,
+ const MonomialArray::const_iterator &fend,
+ Poly &result);
+
+ void getStats(Stats &stats) const; // set the stats table with current values
+
+ size_t getMemoryUse() const;
+
+ void resetStats() const; // reset all values to 0
+
+ void dump(int level = 0) const; // For debugging: display the current state of the table
+
+
+ //@ insert multiplier * g: any monomials already in the hash table are removed,
+ // but their field coefficients in the table are modified accordingly.
+ // Resulting pointers to 'node's are placed, in order, into result.
+ void insert(Poly::const_iterator first,
+ Poly::const_iterator last,
+ MonomialArray &result);
+ void insert(const_term multiplier,
+ Poly::const_iterator first,
+ Poly::const_iterator last,
+ MonomialArray &result);
+ void insert(const_monomial multiplier,
+ Poly::const_iterator first,
+ Poly::const_iterator last,
+ MonomialArray &result);
+
+ // Inserts t into the hashtable. Returns true if there is not already
+ // a node with the monomial t.monom. If there is already such a node,
+ // then t.coeff is added to the coefficient of that node. In either case,
+ // node will point to the node for t.monom. The original value of
+ // nodeOut is not used.
+ std::pair<bool, node*> insert(const_term termToInsert);
+
+ // Removes the node from the hash table.
+ void remove(node* n);
+
+ // deprecated: use remove instead
+ // popTerm removes 'p' from the hash table, setting result_coeff and result_monom if the coefficient is not zero.
+ // result_monom is set with a pointer into monomial space in this class, so will only
+ // be valid until a 'reset' is called.
+ bool popTerm(node *p, coefficient &result_coeff, const_monomial &result_monom);
+
+ size_t getNodeCount() const { return mNodeCount; }
+protected:
+ size_t computeNodeCount() const;
+ node * makeNode(coefficient coeff, const_monomial monom);
+ void unlink(node *p);
+ bool lookup_and_insert(const_monomial m, coefficient val, node *&result);
+
+ const PolyRing &mRing;
+ std::vector<node *> mHashTable;
+ size_t mHashMask; // this is the number, in binary: 00001111...1, where
+ // the number of 1's is mLogTableSize
+
+ memt::Arena mArena; // space for monomials represented in this class. Also nodes??
+ mutable Stats mStats;
+
+ size_t mTableSize;
+ size_t mLogTableSize; // number of bits in the table: mTableSize should be 2^mLogTableSize
+
+ size_t mNodeCount; // current number of nodes in the hash table
+ size_t mBinCount;
+ size_t mMaxCountBeforeRebuild;
+
+ size_t mMonomialSize;
+};
+
+
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyHeap.cpp b/src/mathicgb/PolyHeap.cpp
new file mode 100644
index 0000000..7ff2338
--- /dev/null
+++ b/src/mathicgb/PolyHeap.cpp
@@ -0,0 +1,185 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "PolyHeap.hpp"
+
+#include <iostream>
+#include <algorithm>
+
+extern int tracingLevel;
+
+size_t PolyHeap::stats_static_n_compares = 0;
+
+PolyHeap::PolyHeap(const PolyRing *R_)
+ : Reducer(),
+ R(R_),
+ heapCompare(R_),
+ lead_is_computed(false)
+{
+ stats_static_n_compares = 0;
+}
+
+void PolyHeap::insert(const_term multiplier, Poly::const_iterator first, Poly::const_iterator last)
+{
+ if (first == last) return;
+ heap_term t;
+ t.multiplier = multiplier;
+ t.first = first;
+ t.last = last;
+ t.actual = R->allocMonomial(mArena);
+ R->monomialMult(multiplier.monom, t.first.getMonomial(), t.actual);
+ terms_.push_back(t);
+ std::push_heap(terms_.begin(), terms_.end(), heapCompare);
+ stats_n_compares += stats_static_n_compares;
+ stats_static_n_compares = 0;
+ if (terms_.size() > stats_maxsize)
+ stats_maxsize = terms_.size();
+ stats_n_inserts++;
+}
+
+void PolyHeap::insertTail(const_term multiplier, const Poly *f)
+{
+ if (tracingLevel > 100) {
+ std::cerr << "PolyHeap inserting tail of ";
+ f->display(std::cerr);
+ std::cerr << std::endl;
+ std::cerr << "multiplied by: " << multiplier.coeff << " * ";
+ R->monomialDisplay(std::cerr, multiplier.monom);
+ std::cerr << std::endl;
+ }
+
+ Poly::const_iterator a = f->begin();
+ insert(multiplier, ++a, f->end());
+}
+void PolyHeap::insert(monomial multiplier, const Poly *f)
+{
+ const_term t;
+ t.monom = multiplier;
+ R->coefficientSetOne(t.coeff);
+ Poly::const_iterator a = f->begin();
+ insert(t, a, f->end());
+}
+
+void PolyHeap::extract1(const_term &t)
+{
+ // Pop the largest term into t, incrememnt that poly iterator
+ std::pop_heap(terms_.begin(), terms_.end(), heapCompare);
+ stats_n_compares += stats_static_n_compares;
+ stats_static_n_compares = 0;
+ heap_term h = *(terms_.rbegin());
+ t.monom = h.actual;
+ t.coeff = h.multiplier.coeff;
+ R->coefficientMultTo(t.coeff, h.first.getCoefficient());
+ terms_.pop_back(); // removes h from heap
+ ++h.first;
+ if (h.first != h.last)
+ insert(h.multiplier, h.first, h.last);
+ else {
+ // REMOVE h.multiplier
+ }
+}
+
+bool PolyHeap::extractLeadTerm(const_term &result)
+{
+ bool result_set = false;
+ for (;;) {
+ if (terms_.empty()) break;
+ if (!result_set)
+ {
+ extract1(result);
+ result_set = true;
+ continue;
+ }
+ heap_term &h = terms_[0]; // Now look at the next term
+ if (!R->monomialEQ(h.actual, result.monom)) break;
+ // at this point, we need to grab the monomial
+ const_term t;
+ extract1(t);
+ R->coefficientAddTo(result.coeff, t.coeff);
+ result_set = !(R->coefficientIsZero(result.coeff));
+ }
+ return result_set;
+}
+
+bool PolyHeap::computeLeadTerm()
+{
+ if (!lead_is_computed)
+ {
+ for (;;) {
+ if (terms_.empty()) break;
+ if (!lead_is_computed)
+ {
+ extract1(lead_term);
+ lead_is_computed = true;
+ continue;
+ }
+ heap_term &h = terms_[0]; // Now look at the next term
+ if (!R->monomialEQ(h.actual, lead_term.monom)) break;
+ // at this point, we need to grab the monomial
+ const_term t;
+ extract1(t);
+ R->coefficientAddTo(lead_term.coeff, t.coeff);
+ lead_is_computed = !(R->coefficientIsZero(lead_term.coeff));
+ }
+ }
+ return lead_is_computed; // will be false if could not compute
+}
+
+bool PolyHeap::findLeadTerm(const_term &result)
+{
+ if (!lead_is_computed && !computeLeadTerm()) return false;
+ result = lead_term;
+ return true;
+}
+
+void PolyHeap::removeLeadTerm()
+{
+ if (!lead_is_computed) computeLeadTerm();
+ lead_is_computed = false;
+}
+
+void PolyHeap::value(Poly &result)
+{
+ const_term t;
+ while (findLeadTerm(t))
+ {
+ result.appendTerm(t.coeff, t.monom);
+ lead_is_computed = false;
+ }
+ resetReducer();
+}
+
+size_t PolyHeap::getMemoryUse() const
+{
+ return
+ Reducer::getMemoryUse() +
+ terms_.capacity() * sizeof(heap_term);
+}
+
+void PolyHeap::resetReducer()
+{
+ terms_.resize(0);
+ lead_is_computed = false;
+}
+
+void PolyHeap::dump() const
+// display debugging info about the heap
+{
+ std::cout << "-- polyheap --" << std::endl;
+ for (size_t i = 0; i < terms_.size(); i++)
+ {
+ std::cout << "actual: ";
+ R->monomialDisplay(std::cout, terms_[i].actual);
+
+ std::cout << " monom: ";
+ R->monomialDisplay(std::cout, terms_[i].multiplier.monom);
+
+ std::cout << " coeff: " << terms_[i].multiplier.coeff ;
+ std::cout << std::endl;
+ }
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyHeap.hpp b/src/mathicgb/PolyHeap.hpp
new file mode 100644
index 0000000..edfec7f
--- /dev/null
+++ b/src/mathicgb/PolyHeap.hpp
@@ -0,0 +1,74 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _polyHeap_h_
+#define _polyHeap_h_
+
+#include "Reducer.hpp"
+
+struct heap_term {
+ monomial actual; // multiplier * *first
+ const_term multiplier;
+ Poly::const_iterator first;
+ Poly::const_iterator last;
+};
+
+class heapCompareFcn {
+ const PolyRing *R;
+public:
+ heapCompareFcn(const PolyRing *R0) : R(R0) {}
+ bool operator()(heap_term &a, heap_term &b);
+};
+
+class PolyHeap : public Reducer {
+public:
+ PolyHeap(const PolyRing *R);
+ ~PolyHeap() {}
+
+ virtual std::string description() const { return "heap"; }
+
+ void insertTail(const_term multiplier, const Poly *f);
+ void insert(monomial multiplier, const Poly *f);
+
+ bool findLeadTerm(const_term &result);
+ void removeLeadTerm();
+
+ void value(Poly &result); // keep extracting lead term until done
+ void dump() const;
+
+ static size_t stats_static_n_compares; // static so that heap compares can be easily counted
+
+ void insert(const_term multiplier, Poly::const_iterator first, Poly::const_iterator last);
+
+ size_t getMemoryUse() const;
+
+protected:
+ void resetReducer();
+
+private:
+ bool extractLeadTerm(const_term &result);
+ // returns true if there is a term to extract
+
+ const PolyRing *R;
+ heapCompareFcn heapCompare;
+ std::vector<heap_term> terms_; // the actual heap
+
+ bool lead_is_computed;
+ const_term lead_term; // if it has been computed, this will be set.
+ // monomial points somewhere into 'monoms'.
+
+ void extract1(const_term &t); // assumes: heap is not empty
+ bool computeLeadTerm();
+};
+
+inline bool heapCompareFcn::operator()(heap_term &a, heap_term &b)
+{
+ PolyHeap::stats_static_n_compares++;
+ return R->monomialLT(a.actual, b.actual);
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyReducer.cpp b/src/mathicgb/PolyReducer.cpp
new file mode 100644
index 0000000..4d3c239
--- /dev/null
+++ b/src/mathicgb/PolyReducer.cpp
@@ -0,0 +1,113 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include "PolyReducer.hpp"
+
+PolyReducer::PolyReducer(const PolyRing *R0)
+ : Reducer(),
+ R(R0),
+ mMemUsage(0)
+{
+ f = new Poly(R);
+ f_iter = f->begin();
+}
+
+PolyReducer::~PolyReducer()
+{
+ delete f;
+ f = 0;
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+
+void PolyReducer::insertTail(const_term multiplier, const Poly *g1)
+{
+ size_t ncmps = 0;
+
+ if (g1->nTerms() <= 1) return;
+ Poly *g = g1->copy();
+ g->multByTerm(multiplier.coeff, multiplier.monom);
+
+ Poly::iterator ig = g->begin();
+ ++ig;
+ Poly *h = Poly::add(R, f_iter, f->end(), ig, g->end(), ncmps);
+ stats_n_compares += ncmps;
+ stats_n_inserts++;
+
+ delete f;
+ delete g;
+ f = h;
+ f_iter = f->begin();
+
+ size_t fmem = f->getMemoryUse();
+ if (fmem > mMemUsage)
+ mMemUsage = fmem;
+}
+
+void PolyReducer::insert(monomial multiplier, const Poly *g1)
+{
+ size_t ncmps = 0;
+
+ Poly *g = g1->copy();
+ g->multByMonomial(multiplier);
+
+ Poly::iterator ig = g->begin();
+ Poly *h = Poly::add(R, f_iter, f->end(), ig, g->end(), ncmps);
+ stats_n_compares += ncmps;
+ stats_n_inserts++;
+
+ delete f;
+ delete g;
+ f = h;
+ f_iter = f->begin();
+
+ size_t fmem = f->getMemoryUse();
+ if (fmem > mMemUsage)
+ mMemUsage = fmem;
+}
+
+bool PolyReducer::findLeadTerm(const_term &result)
+{
+ if (f_iter != f->end())
+ {
+ result.coeff = f_iter.getCoefficient();
+ result.monom = f_iter.getMonomial();
+ return true;
+ }
+ return false;
+}
+
+void PolyReducer::removeLeadTerm()
+// returns true if there is a term to extract
+{
+ if (f_iter == f->end()) return;
+ ++f_iter;
+}
+
+void PolyReducer::value(Poly &result)
+// keep extracting lead term until done
+{
+ Poly::iterator fend = f->end();
+ result.append(f_iter, fend);
+ delete f;
+ f = new Poly(R);
+ f_iter = f->begin();
+}
+
+void PolyReducer::resetReducer()
+{
+ delete f;
+ f = new Poly(R);
+ f_iter = f->begin();
+}
+
+void PolyReducer::dump() const
+{
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyReducer.hpp b/src/mathicgb/PolyReducer.hpp
new file mode 100644
index 0000000..23989f2
--- /dev/null
+++ b/src/mathicgb/PolyReducer.hpp
@@ -0,0 +1,44 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _polyReducer_h_
+#define _polyReducer_h_
+
+#include "Reducer.hpp"
+
+class PolyReducer : public Reducer {
+public:
+ PolyReducer(const PolyRing *R);
+
+ virtual ~PolyReducer();
+
+ virtual std::string description() const { return "poly reducer"; }
+
+ void insertTail(const_term multiplier, const Poly *f);
+ void insert(monomial multiplier, const Poly *f);
+
+ bool findLeadTerm(const_term &result);
+ void removeLeadTerm();
+
+ void value(Poly &result); // keep extracting lead term until done
+ void dump() const;
+
+ size_t getMemoryUse() const { return mMemUsage; } // this one reports high water usage
+protected:
+ void resetReducer();
+
+private:
+ void insert(const_term multiplier, Poly::const_iterator first, Poly::const_iterator last);
+
+ const PolyRing *R;
+ Poly *f;
+ Poly::iterator f_iter;
+
+ size_t mMemUsage;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyRing.cpp b/src/mathicgb/PolyRing.cpp
new file mode 100644
index 0000000..7da7ff4
--- /dev/null
+++ b/src/mathicgb/PolyRing.cpp
@@ -0,0 +1,748 @@
+// Copyright 2011 Michael E. Stillman
+#include "stdinc.h"
+#include "PolyRing.hpp"
+
+#include <algorithm>
+#include <ostream>
+#include <istream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+#include <cstdlib>
+#include <limits>
+
+PolyRing::PolyRing(long p0,
+ int nvars,
+ int nweights)
+ : mCharac(p0),
+ mNumVars(nvars),
+ mNumWeights(nweights),
+ mTopIndex(nvars + nweights),
+ mHashIndex(nvars + nweights + 1),
+ mMaxMonomialSize(nvars + nweights + 2),
+ mMaxMonomialByteSize(mMaxMonomialSize * sizeof(exponent)),
+ mMonomialPool(mMaxMonomialByteSize),
+ mTotalDegreeGradedOnly(false)
+{
+ resetCoefficientStats();
+ // rand();
+ for (size_t i=0; i<mNumVars; i++)
+ mHashVals.push_back(rand());
+}
+
+void PolyRing::displayHashValues() const
+{
+ std::cerr << "hash values: " << std::endl;
+ for (size_t i=0; i<mNumVars; i++)
+ std::cerr << " " << mHashVals[i] << std::endl;
+}
+
+void PolyRing::resetCoefficientStats() const
+{
+ mStats.n_addmult = 0;
+ mStats.n_add = 0;
+ mStats.n_mult = 0;
+ mStats.n_recip = 0;
+ mStats.n_divide = 0;
+}
+
+///////////////////////////////////////
+// (New) Monomial Routines ////////////
+///////////////////////////////////////
+
+void PolyRing::setWeightsAndHash(Monomial& a1) const
+{
+ setWeightsOnly(a1);
+ setHashOnly(a1);
+}
+
+inline bool PolyRing::weightsCorrect(ConstMonomial a1) const
+{
+ exponent const *a = a1.mValue;
+ ++a;
+ const int *wts = &mWeights[0];
+ for (int i=0; i < mNumWeights; ++i) {
+ int result = 0;
+ for (size_t j=0; j < mNumVars; ++j)
+ result += *wts++ * a[j];
+ if (a[mNumVars + i] != result)
+ return false;
+ }
+ return true;
+}
+
+int PolyRing::monomialCompare(ConstMonomial sig, ConstMonomial m2, ConstMonomial sig2) const
+ // returns LT, EQ, or GT, depending on sig ? (m2 * sig2).
+{
+ for (size_t i = mTopIndex; i != static_cast<size_t>(-1); --i)
+ {
+ int cmp = sig[i] - m2[i] - sig2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+}
+
+void PolyRing::monomialSetIdentity(Monomial& result) const
+{
+ for (size_t i = 0; i <= mTopIndex; ++i)
+ result[i] = 0;
+ result[mHashIndex] = 0;
+}
+
+void PolyRing::monomialEi(size_t i, Monomial &result) const
+{
+ for (size_t j=mTopIndex; j != static_cast<size_t>(-1); --j) result[j] = 0;
+ *result = static_cast<int>(i); // todo: handle overflow or change representation
+ result[mHashIndex] = static_cast<int>(i); // todo: handle overflow or change representation
+}
+
+void PolyRing::monomialMultTo(Monomial &a, ConstMonomial b) const
+{
+ // a *= b
+ for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ a[i] += b[i];
+}
+
+
+void PolyRing::monomialCopy(ConstMonomial a,
+ Monomial& result) const
+{
+ for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ result[i] = a[i];
+}
+
+void PolyRing::monomialQuotientAndMult(ConstMonomial a,
+ ConstMonomial b,
+ ConstMonomial c,
+ Monomial& result) const
+{
+ // result is set to (a:b) * c. It is not assumed that b divides a.
+ for (size_t i = 0; i <= mNumVars; ++i)
+ {
+ result[i] = c[i];
+ int cmp = a[i] - b[i];
+ if (cmp > 0) result[i] += cmp;
+ }
+ setWeightsAndHash(result);
+}
+
+void PolyRing::monomialFindSignature(ConstMonomial v1,
+ ConstMonomial v2,
+ ConstMonomial u1,
+ Monomial& t1) const
+{
+ // t1 := (v2:v1) u1
+ *t1 = *u1.mValue;
+ if (mTotalDegreeGradedOnly) {
+ ASSERT(mNumWeights == 1);
+ exponent weight = 0;
+ for (size_t i = 1; i <= mNumVars; ++i) {
+ ASSERT(mWeights[i - 1] == -1);
+ if (v1[i] < v2[i])
+ weight -= t1[i] = u1[i] + v2[i] - v1[i];
+ else
+ weight -= t1[i] = u1[i];
+ }
+#ifdef DEBUG
+ setWeightsOnly(t1);
+ ASSERT(t1[mNumVars + 1] == weight);
+#endif
+ t1[mNumVars + 1] = weight;
+ } else {
+ for (size_t i = 1; i <= mNumVars; ++i) {
+ if (v1[i] < v2[i])
+ t1[i] = u1[i] + v2[i] - v1[i];
+ else
+ t1[i] = u1[i];
+ }
+ setWeightsOnly(t1);
+ }
+}
+
+size_t PolyRing::monomialSizeOfSupport(ConstMonomial m) const
+{
+ size_t support = 0;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (m[i] != 0)
+ ++support;
+ return support;
+}
+
+void PolyRing::monomialGreatestCommonDivisor(ConstMonomial a,
+ ConstMonomial b,
+ Monomial& g) const
+{
+ *g = 0;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ g[i] = std::min(a[i], b[i]);
+ setWeightsOnly(g);
+}
+
+bool PolyRing::monomialIsLeastCommonMultiple(
+ ConstMonomial a,
+ ConstMonomial b,
+ ConstMonomial l) const
+{
+ return monomialIsLeastCommonMultipleNoWeights(a, b, l) && weightsCorrect(l);
+}
+
+bool PolyRing::monomialIsLeastCommonMultipleNoWeights(
+ ConstMonomial a,
+ ConstMonomial b,
+ ConstMonomial l) const
+{
+ if (*l != 0)
+ return false;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (l[i] != std::max(a[i], b[i]))
+ return false;
+ return true;
+}
+
+
+void PolyRing::mysteriousSPairMonomialRoutine(ConstMonomial newSig,
+ ConstMonomial newLead,
+ ConstMonomial baseDivSig,
+ ConstMonomial baseDivLead,
+ Monomial result) const
+{
+ result[0] = 0; // set component
+
+ for (size_t i = 1; i <= mNumVars; ++i) {
+ exponent x = newSig[i] - baseDivSig[i] + baseDivLead[i];
+ if (newLead[i] <= x)
+ x = std::numeric_limits<exponent>::max();
+ result[i] = std::max(baseDivLead[i], x);
+ }
+}
+
+// The following two read/write monomials in the form:
+// letter number letter number ... letter number < number >
+// allowed letters: a-zA-Z
+// also allowed instead of letter: [ number ], [0] refers to first var, etc.
+// What about errors??
+void PolyRing::monomialParse(std::istream &i,
+ Monomial& result) const
+{
+ // first initialize result:
+ for (size_t j=0; j<mMaxMonomialSize; j++) result[j] = 0;
+
+ int v, e, x;
+ // now look at the next char
+ for (;;)
+ {
+ char next = i.peek();
+ if (isalpha(next))
+ {
+ // determine variable
+ v = next - 'a';
+ if (v > 25 || v < 0)
+ v = next - 'A' + 26;
+ if (v < 0 || v > 51)
+ std::cerr << "variable out of range" << std::endl;
+ i.get(); // move past the letter
+ next = i.peek();
+ e = 1;
+ if (isdigit(next))
+ {
+ i >> e;
+ }
+ result[v+1] = e;
+ }
+ else if (next == '<')
+ {
+ // get component
+ i.get();
+ i >> x;
+ *result = x; // place component
+ if (i.peek() == '>') i.get();
+ }
+ else
+ {
+ break; // We assume that we are at the end of the monomial
+ }
+ }
+ setWeightsAndHash(result);
+}
+
+void PolyRing::monomialDisplay(std::ostream &o,
+ ConstMonomial a,
+ bool print_comp,
+ bool print_one) const
+{
+ // We display a monomial in the form that can be used with monomialParse
+ // print_one: only is consulted in print_comp is false.
+
+ bool printed_any = false;
+ for (size_t i=0; i<mNumVars; i++)
+ if (a[i+1] != 0)
+ {
+ printed_any = true;
+ if (i <= 25)
+ o << static_cast<unsigned char>('a' + i);
+ else
+ o << static_cast<unsigned char>('A' + (i - 26));
+ if (a[i+1] != 1)
+ o << a[i+1];
+ }
+ if (print_comp)
+ o << '<' << *a.mValue << '>';
+ else if (!printed_any && print_one)
+ o << "1";
+}
+
+void PolyRing::printMonomialFrobbyM2Format(std::ostream& out, ConstMonomial m) const {
+ out << " ";
+ bool isOne = true;
+ for (size_t i = 0; i < mNumVars; ++i) {
+ int e = m[i + 1];
+ if (e == 0)
+ continue;
+ if (!isOne)
+ out << '*';
+ else
+ isOne = false;
+ out << 'x' << (i + 1) << '^' << e;
+ }
+ if (isOne)
+ out << '1';
+}
+
+void PolyRing::printRingFrobbyM2Format(std::ostream& out) const {
+ out << "R = QQ[";
+ for (size_t i = 0; i < mNumVars; ++i)
+ out << (i == 0 ? "" : ", ") << 'x' << (i + 1);
+ out << "];\n";
+}
+
+
+
+///////////////////////////////////////
+// (Old) Monomial Routines ////////////
+///////////////////////////////////////
+
+#ifndef NEWMONOMIALS
+bool PolyRing::monomialIsLeastCommonMultiple(
+ const_monomial a,
+ const_monomial b,
+ const_monomial l) const
+{
+ return monomialIsLeastCommonMultipleNoWeights(a, b, l) && weightsCorrect(l);
+}
+
+inline bool PolyRing::weightsCorrect(const_monomial a) const
+{
+ ++a;
+ const int *wts = &mWeights[0];
+ for (int i=0; i < mNumWeights; ++i) {
+ int result = 0;
+ for (size_t j=0; j < mNumVars; ++j)
+ result += *wts++ * a[j];
+ if (a[mNumVars + i] != result)
+ return false;
+ }
+ return true;
+}
+
+bool PolyRing::monomialIsLeastCommonMultipleNoWeights(
+ const_monomial a,
+ const_monomial b,
+ const_monomial l) const
+{
+ if (*l != 0)
+ return false;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (l[i] != std::max(a[i], b[i]))
+ return false;
+ return true;
+}
+
+int PolyRing::monomialCompare(const_monomial sig, const_monomial m2, const_monomial sig2) const
+ // returns LT, EQ, or GT, depending on sig ? (m2 * sig2).
+{
+ for (size_t i = mTopIndex; i != static_cast<size_t>(-1); --i)
+ {
+ int cmp = sig[i] - m2[i] - sig2[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+}
+
+bool PolyRing::monomialEQ(const_monomial a, const_monomial b) const
+{
+ for (size_t i = 0; i <= mNumVars; ++i)
+ if (a[i] != b[i]) return false;
+ return true;
+}
+
+void PolyRing::monomialSetIdentity(monomial& result) const {
+ for (size_t i = 0; i <= mTopIndex; ++i)
+ result[i] = 0;
+ result[mHashIndex] = 0;
+}
+
+void PolyRing::monomialEi(size_t i, monomial &result) const
+{
+ for (size_t j=mTopIndex; j != static_cast<size_t>(-1); --j) result[j] = 0;
+ *result = static_cast<int>(i); // todo: handle overflow or change representation
+ result[mHashIndex] = static_cast<int>(i); // todo: handle overflow or change representation
+}
+
+void PolyRing::monomialMult(const_monomial a, const_monomial b, monomial &result) const
+{
+ for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ result[i] = a[i] + b[i];
+}
+void PolyRing::monomialMultTo(monomial a, const_monomial b) const
+{
+ // a *= b
+ for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ a[i] += b[i];
+}
+
+
+void PolyRing::monomialCopy(const_monomial a, monomial &result) const
+{
+ for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ result[i] = a[i];
+}
+void PolyRing::monomialQuotientAndMult(const_monomial a,
+ const_monomial b,
+ const_monomial c,
+ monomial result) const
+{
+ // result is set to (a:b) * c. It is not assumed that b divides a.
+ for (size_t i = 0; i <= mNumVars; ++i)
+ {
+ result[i] = c[i];
+ int cmp = a[i] - b[i];
+ if (cmp > 0) result[i] += cmp;
+ }
+ setWeightsAndHash(result);
+}
+
+void PolyRing::setWeightsAndHash(monomial a) const
+{
+ setWeightsOnly(a);
+ int hash = *a;
+ a++;
+ for (size_t i = 0; i < mNumVars; ++i)
+ hash += a[i] * mHashVals[i];
+ a[mHashIndex - 1] = hash;
+}
+
+void PolyRing::monomialFindSignatures(const_monomial v1,
+ const_monomial v2,
+ const_monomial u1,
+ const_monomial u2,
+ monomial t1,
+ monomial t2) const
+{
+ // t1 := (v2:v1) u1
+ // t2 := (v1:v2) u2
+ // set components:
+ *t1 = *u1;
+ *t2 = *u2;
+
+ for (size_t i = 1; i <= mNumVars; ++i)
+ {
+ exponent a = v1[i];
+ exponent b = v2[i];
+ exponent min = std::min(a, b);
+ t1[i] = b - min + u1[i];
+ t2[i] = a - min + u2[i];
+ }
+ setWeightsOnly(t1);
+ setWeightsOnly(t2);
+}
+
+void PolyRing::monomialFindSignature(const_monomial v1,
+ const_monomial v2,
+ const_monomial u1,
+ monomial t1) const
+{
+ // t1 := (v2:v1) u1
+ *t1 = *u1;
+ if (mTotalDegreeGradedOnly) {
+ ASSERT(mNumWeights == 1);
+ exponent weight = 0;
+ for (size_t i = 1; i <= mNumVars; ++i) {
+ ASSERT(mWeights[i - 1] == -1);
+ if (v1[i] < v2[i])
+ weight -= t1[i] = u1[i] + v2[i] - v1[i];
+ else
+ weight -= t1[i] = u1[i];
+ }
+#ifdef DEBUG
+ setWeightsOnly(t1);
+ ASSERT(t1[mNumVars + 1] == weight);
+#endif
+ t1[mNumVars + 1] = weight;
+ } else {
+ for (size_t i = 1; i <= mNumVars; ++i) {
+ if (v1[i] < v2[i])
+ t1[i] = u1[i] + v2[i] - v1[i];
+ else
+ t1[i] = u1[i];
+ }
+ setWeightsOnly(t1);
+ }
+}
+
+size_t PolyRing::monomialSizeOfSupport(const_monomial m) const {
+ size_t support = 0;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (m[i] != 0)
+ ++support;
+ return support;
+}
+
+void PolyRing::monomialGreatestCommonDivisor(const_monomial a, const_monomial b, monomial g) const {
+ *g = 0;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ g[i] = std::min(a[i], b[i]);
+ setWeightsOnly(g);
+}
+
+void PolyRing::monomialRead(std::istream &I, monomial &result) const
+{
+ // format is as stated in PolyRing.h
+ int len;
+ I >> *result; // component
+ I >> len; // nterms of monomial
+ for (size_t i=1; i<=mNumVars; i++)
+ result[i] = 0;
+ for (int i=len; i>0; --i)
+ {
+ int v,e;
+ I >> v;
+ I >> e;
+ result[v+1] = e;
+ }
+ setWeightsAndHash(result);
+}
+void PolyRing::monomialWrite(std::ostream &o, const_monomial monom) const
+{
+ int len = 0;
+ for (size_t i=1; i<=mNumVars; i++)
+ if (monom[i] != 0) len++;
+ o << *monom << " " << len; // the component
+ monom++;
+ for (size_t i = mNumVars - 1; i != static_cast<size_t>(-1); --i)
+ if (monom[i] != 0)
+ o << " " << i << " " << monom[i];
+}
+
+// The following two read/write monomials in the form:
+// letter number letter number ... letter number < number >
+// allowed letters: a-zA-Z
+// also allowed instead of letter: [ number ], [0] refers to first var, etc.
+// What about errors??
+void PolyRing::monomialParse(std::istream &i, monomial &result) const
+{
+ // first initialize result:
+ for (size_t j=0; j<mMaxMonomialSize; j++) result[j] = 0;
+
+ int v, e, x;
+ // now look at the next char
+ for (;;)
+ {
+ char next = i.peek();
+ if (isalpha(next))
+ {
+ // determine variable
+ v = next - 'a';
+ if (v > 25 || v < 0)
+ v = next - 'A' + 26;
+ if (v < 0 || v > 51)
+ std::cerr << "variable out of range" << std::endl;
+ i.get(); // move past the letter
+ next = i.peek();
+ e = 1;
+ if (isdigit(next))
+ {
+ i >> e;
+ }
+ result[v+1] = e;
+ }
+ else if (next == '<')
+ {
+ // get component
+ i.get();
+ i >> x;
+ *result = x; // place component
+ if (i.peek() == '>') i.get();
+ }
+ else
+ {
+ break; // We assume that we are at the end of the monomial
+ }
+ }
+ setWeightsAndHash(result);
+}
+
+void PolyRing::monomialDisplay(std::ostream &o, const_monomial a, bool print_comp, bool print_one) const
+{
+ // We display a monomial in the form that can be used with monomialParse
+ // print_one: only is consulted in print_comp is false.
+
+ bool printed_any = false;
+ for (size_t i=0; i<mNumVars; i++)
+ if (a[i+1] > 0)
+ {
+ printed_any = true;
+ if (i <= 25)
+ o << static_cast<unsigned char>('a' + i);
+ else
+ o << static_cast<unsigned char>('A' + (i - 26));
+ if (a[i+1] >= 2)
+ o << a[i+1];
+ }
+ if (print_comp)
+ o << '<' << *a << '>';
+ else if (!printed_any && print_one)
+ o << "1";
+}
+
+void PolyRing::printMonomialFrobbyM2Format(std::ostream& out, const_monomial m) const {
+ out << " ";
+ bool isOne = true;
+ for (size_t i = 0; i < mNumVars; ++i) {
+ int e = m[i + 1];
+ if (e == 0)
+ continue;
+ if (!isOne)
+ out << '*';
+ else
+ isOne = false;
+ out << 'x' << (i + 1) << '^' << e;
+ }
+ if (isOne)
+ out << '1';
+}
+#endif
+
+PolyRing *PolyRing::read(std::istream &i)
+{
+ long charac;
+ int mNumVars, mNumWeights;
+
+ i >> charac;
+ i >> mNumVars;
+ i >> mNumWeights;
+ PolyRing *R = new PolyRing(charac, mNumVars, mNumWeights);
+ int wtlen = mNumVars * mNumWeights;
+ R->mWeights.resize(wtlen);
+ R->mTotalDegreeGradedOnly = (mNumWeights == 1);
+ for (int j=0; j <mNumVars * mNumWeights; j++)
+ {
+ int a;
+ i >> a;
+ R->mWeights[j] = -a;
+ if (R->mWeights[j] != -1)
+ R->mTotalDegreeGradedOnly = false;
+ }
+ return R;
+}
+
+void PolyRing::write(std::ostream &o) const
+{
+ o << mCharac << " " << mNumVars << " " << mNumWeights << std::endl;
+ const int *wts = &mWeights[0];
+ for (int i=0; i<mNumWeights; i++)
+ {
+ for (size_t j=0; j<mNumVars; j++)
+ o << " " << - *wts++;
+ o << std::endl;
+ }
+}
+
+void PolyRing::coefficientFromInt(coefficient &result, int a) const
+{
+ result = a % mCharac;
+ if (result < 0) result += mCharac;
+}
+
+void PolyRing::coefficientAddOneTo(coefficient &result) const
+{
+ result++;
+ if (result == mCharac) result = 0;
+}
+void PolyRing::coefficientNegateTo(coefficient &result) const
+ // result = -result
+{
+ result = mCharac - result;
+}
+void PolyRing::coefficientAddTo(coefficient &result, coefficient a, coefficient b) const
+// result += a*b
+{
+ mStats.n_addmult++;
+ long c = a * b + result;
+ result = c % mCharac;
+}
+void PolyRing::coefficientAddTo(coefficient &result, coefficient a) const
+ // result += a
+{
+ mStats.n_add++;
+ result += a;
+ if (result >= mCharac) result -= mCharac;
+}
+void PolyRing::coefficientMultTo(coefficient &result, coefficient a) const
+ // result *= a
+{
+ mStats.n_mult++;
+ long b = result * a;
+ result = b % mCharac;
+}
+void PolyRing::coefficientMult(coefficient a, coefficient b, coefficient &result) const
+{
+ mStats.n_mult++;
+ long c = b * a;
+ result = c % mCharac;
+}
+
+void gcd_extended(long a, long b, long &u, long &v, long &g)
+{
+ long q ;
+ long u1, v1, g1;
+ long utemp, vtemp, gtemp;
+
+ g1 = b; u1 = 0; v1 = 1;
+ g = a; u = 1; v = 0;
+ while (g1 != 0)
+ {
+ q = g / g1 ;
+ gtemp= g - q * g1;
+ utemp= u - q * u1;
+ vtemp= v - q * v1;
+ g = g1; u = u1; v = v1 ;
+ g1 = gtemp; u1 = utemp; v1 = vtemp;
+ }
+}
+
+void PolyRing::coefficientReciprocalTo(coefficient &result) const
+{
+ mStats.n_recip++;
+ long u,v,g;
+ gcd_extended(result, mCharac, u, v, g);
+ if (u < 0) u += mCharac;
+ result = u;
+}
+
+void PolyRing::coefficientDivide(coefficient a, coefficient b, coefficient &result) const
+ // result /= a
+{
+ mStats.n_divide++;
+ long u,v,g;
+ gcd_extended(b, mCharac, u, v, g);
+ result = a * u;
+ result %= mCharac;
+ if (result < 0) result += mCharac;
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/PolyRing.hpp b/src/mathicgb/PolyRing.hpp
new file mode 100755
index 0000000..9f84c21
--- /dev/null
+++ b/src/mathicgb/PolyRing.hpp
@@ -0,0 +1,752 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _polyRing_h_
+#define _polyRing_h_
+
+#define NEWMONOMIALS 1
+
+#include <assert.h>
+#include <string>
+#include <vector>
+#include <memtailor.h>
+
+#define LT (-1)
+#define EQ 0
+#define GT 1
+
+typedef int exponent ;
+typedef long coefficient;
+typedef exponent *vecmonomial; // includes a component
+typedef coefficient const_coefficient;
+
+class Monomial;
+
+class ConstMonomial
+{
+ friend class PolyRing;
+ friend class OrderA;
+ friend class OrderB;
+ friend class OrderC;
+ friend class OrderD;
+ friend class OrderE;
+public:
+ //* Wrap a raw pointer to create a monomial
+ // Assumptions:
+ // 1. This is done in the presence of an PolyRing
+ // 2. Space for the monomial has been created
+ ConstMonomial() : mValue(0) {}
+ ConstMonomial(const exponent *val) : mValue(val) {}
+
+ inline Monomial& castAwayConst();
+
+ bool isNull() const { return mValue == 0; }
+
+ exponent const * unsafeGetRepresentation() const { return mValue; }
+
+
+ exponent component() const { return *mValue; }
+
+private:
+ exponent operator[](size_t index) const { return mValue[index]; }
+
+ exponent operator*() const { return *mValue; }
+protected:
+ exponent const* mValue;
+};
+
+class Monomial : public ConstMonomial
+{
+ friend class PolyRing;
+ friend class OrderA;
+ friend class OrderB;
+ friend class OrderC;
+ friend class OrderD;
+ friend class OrderE;
+public:
+ //* Wrap a raw pointer to create a monomial
+ // Assumptions:
+ // 1. This is done in the presence of an PolyRing
+ // 2. Space for the monomial has been created
+ Monomial() : ConstMonomial() {}
+ Monomial(exponent *val) : ConstMonomial(val) {}
+
+ void swap(Monomial& monomial) {
+ std::swap(mValue, monomial.mValue);
+ }
+
+ exponent * unsafeGetRepresentation() { return const_cast<exponent *>(mValue); }
+ exponent const * unsafeGetRepresentation() const { return mValue; }
+
+private:
+ exponent operator[](size_t index) const { return mValue[index]; }
+ exponent& operator[](size_t index) { return unsafeGetRepresentation()[index]; }
+
+ exponent operator*() const { return *mValue; }
+ exponent& operator*() { return * const_cast<exponent *>(mValue); }
+};
+
+inline Monomial& ConstMonomial::castAwayConst()
+{
+ return reinterpret_cast<Monomial&>(*this);
+}
+
+#ifdef NEWMONOMIALS
+ typedef Monomial monomial;
+ typedef ConstMonomial const_monomial;
+#else
+ typedef exponent *monomial; // a power product in the ring
+ typedef const exponent * const_monomial;
+#endif
+
+struct const_term {
+ const_coefficient coeff;
+ const_monomial monom; // includes component
+
+ const_term() {}
+ const_term(const_coefficient c, const_monomial m) : coeff(c), monom(m) {}
+};
+
+struct term {
+ coefficient coeff;
+ monomial monom; // includes component
+ operator const_term() const {return const_term(coeff, monom);}
+
+ term() {}
+ term(coefficient c, monomial m) : coeff(c), monom(m) {}
+};
+
+class PolyRing {
+public:
+ PolyRing(long charac,
+ int nvars,
+ int nweights
+ );
+ ~PolyRing() {}
+
+ memt::BufferPool &getMonomialPool() const { return mMonomialPool; }
+
+ long charac() const { return mCharac; }
+ size_t getNumVars() const { return mNumVars; }
+ // const std::vector<int> °s,
+ // const std::string &monorder);
+
+ static PolyRing *read(std::istream &i);
+ void write(std::ostream &o) const;
+ // Format for ring
+ // <char> <mNumVars> <deg1> ... <deg_n> <monorder>
+
+ void printRingFrobbyM2Format(std::ostream& out) const;
+
+ // Allocate a monomial from an arena A
+ // This monomial may only be freed if no other elements have
+ // been allocated on A. In this case, use freeMonomial(A,m) to free 'm'.
+ Monomial allocMonomial(memt::Arena &A) const {
+ return static_cast<exponent *>(A.alloc(mMaxMonomialByteSize));
+ }
+
+ // Free monomial 'm' obtained by allocMonomial(A)
+ // by calling freeMonomial(A,m)
+ // Recall this only works if this was the last thing
+ // allocated in A.
+ void freeTopMonomial(memt::Arena &A, Monomial m) const {
+ A.freeTop(m.unsafeGetRepresentation());
+ }
+
+ // Allocate a monomial from a pool that has had its size set to
+ // maxMonomialByteSize()
+ // Free monomials here using the SAME pool
+ Monomial allocMonomial(memt::BufferPool &P) const {
+ return static_cast<exponent *>(P.alloc());
+ }
+
+ // Free monomial 'm' obtained by allocMonomial(P)
+ // by calling freeMonomial(P,m)
+ void freeMonomial(memt::BufferPool &P, Monomial m) const {
+ P.free(m.unsafeGetRepresentation());
+ }
+
+
+
+ // Free monomials allocated here by calling freeMonomial().
+ Monomial allocMonomial1() const {
+ return static_cast<exponent *>(mMonomialPool.alloc());
+ }
+
+ // Only call this method for monomials returned by allocMonomial().
+ void freeMonomial(Monomial m) const {mMonomialPool.free(m.unsafeGetRepresentation());}
+
+#ifdef NEWMONOMIALS
+ // Free monomials allocated here by calling freeMonomial().
+ monomial allocMonomial() const { return allocMonomial(mMonomialPool); }
+#else
+ // Free monomials allocated here by calling freeMonomial().
+ monomial allocMonomial() const {
+ return static_cast<monomial>(mMonomialPool.alloc());
+ }
+
+ // Only call this method for monomials returned by allocMonomial().
+ void freeMonomial(monomial m) const {mMonomialPool.free(m);}
+#endif
+
+
+
+ void coefficientFromInt(coefficient &result, int a) const;
+ void coefficientSetOne(coefficient &result) const { result = 1; }
+ void coefficientAddOneTo(coefficient &result) const;
+ void coefficientReciprocalTo(coefficient &result) const;
+ void coefficientNegateTo(coefficient &result) const; // result = -result
+ void coefficientAddTo(coefficient &result, coefficient a, coefficient b) const; // result += a*b
+ void coefficientAddTo(coefficient &result, coefficient a) const; // result += a
+ void coefficientMultTo(coefficient &result, coefficient a) const; // result *= a
+ void coefficientMult(coefficient a, coefficient b, coefficient &result) const; // result = a*b
+ void coefficientDivide(coefficient a, coefficient b, coefficient &result) const; // result /= a
+ void coefficientSet(coefficient &result, coefficient a) const { result = a; }
+ bool coefficientIsZero(coefficient a) const { return a == 0; }
+ bool coefficientIsOne(coefficient a) const { return a == 1; }
+ bool coefficientEQ(coefficient a, coefficient b) const { return a == b; }
+
+ // Format for monomials might be changeable, but for now, let us just do
+ // array of ints:
+ // -comp exp0 exp1 ... exp(mNumVars-1) -wtr ... -wt1
+ // However when written and read from a file the format will be:
+ // nterms comp v1 e1 ... v_nterms e_nterms
+ // with each e_i nonzero, and v_1 > v_2 > ... > v_nterms
+
+ size_t maxMonomialByteSize() const { return mMaxMonomialByteSize; }
+
+ size_t maxMonomialSize() const { return mMaxMonomialSize; }
+
+ void displayHashValues() const;
+
+ size_t monomialHashIndex() const { return mHashIndex; }
+
+ ///////////////////////////////////////////
+ // Monomial Routines //////////////////////
+ ///////////////////////////////////////////
+
+ size_t monomialHashValue(ConstMonomial m) const { return static_cast<size_t>(m[mHashIndex]); }
+
+ exponent monomialExponent(ConstMonomial m, size_t var) const {
+ return m[var+1];
+ }
+
+ // This function only sets component and the monomial itself. NOT weights, degree, or hash value
+ //TODO: get Bjarke to name this function!!
+ void mysteriousSPairMonomialRoutine(ConstMonomial newSig,
+ ConstMonomial newLead,
+ ConstMonomial baseDivSig,
+ ConstMonomial baseDivLead,
+ Monomial result) const;
+
+ void setWeightsAndHash(Monomial& a) const;
+
+ inline void setWeightsOnly(Monomial& a) const;
+
+ inline void setHashOnly(Monomial& a) const;
+
+ bool weightsCorrect(ConstMonomial a) const;
+
+ int monomialCompare(ConstMonomial a,
+ ConstMonomial b) const;
+ // returns LT, EQ or GT
+
+ int monomialCompare(ConstMonomial sig,
+ ConstMonomial m2,
+ ConstMonomial sig2) const;
+ // returns LT, EQ, or GT, depending on sig ? (m2 * sig2).
+
+ bool monomialLT(ConstMonomial a, ConstMonomial b) const {
+ for (size_t i = mTopIndex; i != static_cast<size_t>(-1); --i)
+ {
+ int cmp = a[i] - b[i];
+ if (cmp == 0) continue;
+ if (cmp < 0) return false;
+ return true;
+ }
+ return false;
+ }
+
+ bool monomialEQ(ConstMonomial a, ConstMonomial b) const;
+
+ size_t monomialSize(ConstMonomial) const { return mMaxMonomialSize; }
+
+ int monomialGetComponent(ConstMonomial a) const { return *a.mValue; }
+
+ void monomialChangeComponent(Monomial a, int x) const {
+ a[mHashIndex] -= *a.mValue;
+ a[mHashIndex] += x;
+ *a = x;
+ }
+
+ void monomialSetIdentity(Monomial& result) const;
+
+ void monomialEi(size_t i, Monomial &result) const;
+
+ void monomialMult(ConstMonomial a, ConstMonomial b, Monomial &result) const;
+
+ void monomialMultTo(Monomial &a, ConstMonomial b) const; // a *= b
+
+ bool monomialDivide(ConstMonomial a, ConstMonomial b, Monomial &result) const;
+ // returns true if b divides a, in this case, result is set to b//a.
+
+ void monomialDivideToNegative(ConstMonomial a, ConstMonomial b, Monomial &result) const;
+ // sets result to a/b, even if that produces negative exponents.
+
+ bool monomialIsDivisibleBy(ConstMonomial a, ConstMonomial b) const;
+ // returns true if b divides a. Components are ignored.
+
+
+ void monomialCopy(ConstMonomial a, Monomial &result) const;
+
+ void monomialQuotientAndMult(ConstMonomial a,
+ ConstMonomial b,
+ ConstMonomial c,
+ Monomial& result) const;
+ // result is set to (a//b) * c
+
+ inline bool monomialRelativelyPrime(ConstMonomial a,
+ ConstMonomial b) const;
+
+ void monomialFindSignature(ConstMonomial v1,
+ ConstMonomial v2,
+ ConstMonomial u1,
+ Monomial& t1) const;
+ // answer into the already allocated t1
+
+ size_t monomialSizeOfSupport(ConstMonomial m) const;
+
+ void monomialGreatestCommonDivisor(ConstMonomial a,
+ ConstMonomial b,
+ Monomial& g) const;
+
+ inline void monomialLeastCommonMultiple(ConstMonomial a,
+ ConstMonomial b,
+ Monomial& l) const;
+
+ inline void monomialLeastCommonMultipleNoWeights(ConstMonomial a,
+ ConstMonomial b,
+ Monomial& l) const;
+
+ bool monomialIsLeastCommonMultiple(ConstMonomial a,
+ ConstMonomial b,
+ ConstMonomial l) const;
+
+ bool monomialIsLeastCommonMultipleNoWeights(ConstMonomial a,
+ ConstMonomial b,
+ ConstMonomial l) const;
+
+ // Returns true if there is a variable var such that hasLarger raises var to
+ // a strictly greater exponent than both smaller1 and smaller2 does.
+ inline bool monomialHasStrictlyLargerExponent(
+ ConstMonomial hasLarger,
+ ConstMonomial smaller1,
+ ConstMonomial smaller2) const;
+
+ void monomialParse(std::istream& i,
+ Monomial& result) const;
+
+ void monomialDisplay(std::ostream& o,
+ ConstMonomial a,
+ bool print_comp=true,
+ bool print_one=true) const;
+
+ void printMonomialFrobbyM2Format(std::ostream& out, ConstMonomial m) const;
+
+ ///////////////////////////////////////////
+ ///////////////////////////////////////////
+
+#ifndef NEWMONOMIALS
+ int monomialCompare(const_monomial a, const_monomial b) const; // returns LT, EQ or GT
+ int monomialCompare(const_monomial sig, const_monomial m2, const_monomial sig2) const;
+ // returns LT, EQ, or GT, depending on sig ? (m2 * sig2).
+ bool monomialLT(const_monomial a, const_monomial b) const {
+ for (size_t i = mTopIndex; i != static_cast<size_t>(-1); --i)
+ {
+ int cmp = a[i] - b[i];
+ if (cmp == 0) continue;
+ if (cmp < 0) return false;
+ return true;
+ }
+ return false;
+ }
+ bool monomialEQ(const_monomial a, const_monomial b) const;
+
+ size_t monomialSize(const_monomial) const { return mMaxMonomialSize; }
+ int monomialGetComponent(const_monomial a) const { return *a; }
+ void monomialChangeComponent(monomial a, int x) const {
+ a[mHashIndex] -= *a;
+ a[mHashIndex] += x;
+ *a = x;
+ }
+
+ void monomialSetIdentity(monomial& result) const;
+ void monomialEi(size_t i, monomial &result) const;
+ void monomialMult(const_monomial a, const_monomial b, monomial &result) const;
+ bool monomialDivide(const_monomial a, const_monomial b, monomial &result) const;
+ // returns truue if b divides a, in this case, result is set to b//a.
+ void monomialDivideToNegative(const_monomial a, const_monomial b, monomial result) const;
+ // sets result to a/b, even if that produces negative exponents.
+
+ bool monomialIsDivisibleBy(const_monomial a, const_monomial b) const;
+ // returns true if b divides a. Components are ignored.
+
+ void monomialMultTo(monomial a, const_monomial b) const; // a *= b
+ void monomialCopy(const_monomial a, monomial &result) const;
+ void monomialQuotientAndMult(const_monomial a, const_monomial b, const_monomial c, monomial result) const;
+ // result is set to (a//b) * c
+
+ inline bool monomialRelativelyPrime(const_monomial a, const_monomial b) const;
+ void monomialFindSignatures(const_monomial v1,
+ const_monomial v2,
+ const_monomial u1,
+ const_monomial u2,
+ monomial t1,
+ monomial t2) const; // answer into the already allocated t1,t2
+ // t1 := (v2:v1) u1
+ // t2 := (v1:v2) u2
+ void monomialFindSignature(const_monomial v1,
+ const_monomial v2,
+ const_monomial u1,
+ monomial t1) const; // answer into the already allocated t1
+
+ size_t monomialSizeOfSupport(const_monomial m) const;
+
+ void monomialGreatestCommonDivisor(const_monomial a, const_monomial b, monomial g) const;
+ inline void monomialLeastCommonMultiple(const_monomial a, const_monomial b, monomial l) const;
+ inline void monomialLeastCommonMultipleNoWeights(const_monomial a, const_monomial b, monomial l) const;
+ bool monomialIsLeastCommonMultiple(const_monomial a, const_monomial b, const_monomial l) const;
+ bool monomialIsLeastCommonMultipleNoWeights(const_monomial a, const_monomial b, const_monomial l) const;
+
+ // Returns true if there is a variable var such that hasLarger raises var to
+ // a strictly greater exponent than both smaller1 and smaller2 does.
+ inline bool monomialHasStrictlyLargerExponent(
+ const_monomial hasLarger,
+ const_monomial smaller1,
+ const_monomial smaller2) const;
+
+ void monomialRead(std::istream &i, monomial &result) const;
+ void monomialWrite(std::ostream &o, const_monomial a) const;
+
+ void monomialParse(std::istream &i, monomial &result) const;
+ void monomialDisplay(std::ostream&o, const_monomial a, bool print_comp=true, bool print_one=true) const;
+
+ void printMonomialFrobbyM2Format(std::ostream& out, const_monomial m) const;
+
+ void setWeightsAndHash(monomial a) const;
+ inline void setWeightsOnly(monomial a) const;
+ bool weightsCorrect(const_monomial a) const;
+#endif
+
+ struct coefficientStats {
+ size_t n_addmult;
+ size_t n_add;
+ size_t n_mult;
+ size_t n_recip;
+ size_t n_divide;
+ };
+ const coefficientStats & getCoefficientStats() const { return mStats; }
+ void resetCoefficientStats() const;
+
+private:
+ long mCharac; // p=mCharac: ring is ZZ/p
+ size_t mNumVars;
+ int mNumWeights; // stored as negative of weight vectors
+ size_t mTopIndex;
+ size_t mHashIndex; // 1 more than mTopIndex. Where the has value is stored.
+ size_t mMaxMonomialSize;
+ size_t mMaxMonomialByteSize;
+ std::vector<int> mWeights; // 0..mNumWeights * mNumVars - 1.
+
+ std::vector<int> mHashVals; // one for each variable 0..mNumVars-1
+ // stored as weightvec1 weightvec2 ...
+
+ mutable memt::BufferPool mMonomialPool;
+ mutable coefficientStats mStats;
+
+ bool mTotalDegreeGradedOnly;
+};
+
+////////////////////////////////////////////////
+// New Monomial Routines ///////////////////////
+////////////////////////////////////////////////
+
+inline bool PolyRing::monomialEQ(ConstMonomial a, ConstMonomial b) const
+{
+ for (size_t i = 0; i <= mNumVars; ++i)
+ if (a[i] != b[i]) return false;
+ return true;
+}
+
+inline void PolyRing::monomialMult(ConstMonomial a,
+ ConstMonomial b,
+ Monomial &result) const
+{
+ for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ result[i] = a[i] + b[i];
+
+#if 0
+ // testing different things to see if we can speed it up further.
+ // changing to ascending loop slowed it down.
+ // ascending, with pointers: slightly faster than prev, but still slower than above simple code
+ exponent *presult = result.unsafeGetRepresentation();
+ exponent const * pa = a.unsafeGetRepresentation();
+ exponent const * pb = b.unsafeGetRepresentation();
+ for (size_t i=0; i<= mHashIndex; ++i)
+ // for (size_t i = mHashIndex; i != static_cast<size_t>(-1); --i)
+ *presult++ = *pa++ + *pb++;
+ // result[i] = a[i] + b[i];
+#endif
+}
+
+inline void PolyRing::setWeightsOnly(Monomial& a1) const
+{
+ exponent *a = a1.unsafeGetRepresentation();
+ a++;
+ const int *wts = &mWeights[0];
+ for (int i=0; i<mNumWeights; i++)
+ {
+ int result = 0;
+ for (size_t j=0; j<mNumVars; j++)
+ result += *wts++ * a[j];
+ a[mNumVars+i] = result;
+ }
+}
+
+inline void PolyRing::setHashOnly(Monomial& a1) const
+{
+ exponent *a = a1.unsafeGetRepresentation();
+ int hash = *a;
+ a++;
+ for (size_t i = 0; i < mNumVars; ++i)
+ hash += a[i] * mHashVals[i];
+ a[mHashIndex - 1] = hash;
+}
+
+inline int PolyRing::monomialCompare(ConstMonomial a, ConstMonomial b) const
+// returns LT, EQ or GT
+{
+ for (size_t i = mTopIndex; i != static_cast<size_t>(-1); --i)
+ {
+ int cmp = a[i] - b[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+}
+
+inline bool PolyRing::monomialIsDivisibleBy(ConstMonomial a,
+ ConstMonomial b) const
+{
+ // returns truue if b divides a, in this case, result is set to b//a.
+ // for (int i = mNumVars; i >= 1; --i)
+ // {
+ // int c = a[i] - b[i];
+ // if (c < 0) return false;
+ // }
+ for (size_t i = 1; i<= mNumVars; i++)
+ if (a[i] < b[i])
+ return false;
+
+ return true;
+}
+
+inline bool PolyRing::monomialDivide(ConstMonomial a,
+ ConstMonomial b,
+ Monomial& result) const
+{
+ //// returns true if b divides a, in this case, result is set to b//a.
+ size_t i;
+ for (i = 1; i <= mNumVars; i++)
+ {
+ int c = a[i] - b[i];
+ if (c >= 0)
+ result[i] = c;
+ else
+ return false;
+ }
+ // at this point we have divisibility, so need to fill in the rest of the monomial
+ *result = *a.mValue - *b.mValue; // component
+ for ( ; i<=mHashIndex; i++)
+ result[i] = a[i] - b[i];
+ return true;
+}
+
+inline void PolyRing::monomialDivideToNegative(ConstMonomial a,
+ ConstMonomial b,
+ Monomial& result) const
+{
+ for (size_t i = 0; i <= this->mTopIndex; ++i)
+ result[i] = a[i] - b[i];
+}
+
+inline bool PolyRing::monomialRelativelyPrime(ConstMonomial a,
+ ConstMonomial b) const
+{
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (a[i] > 0 && b[i] > 0)
+ return false;
+ return true;
+}
+
+inline void PolyRing::monomialLeastCommonMultiple(
+ ConstMonomial a,
+ ConstMonomial b,
+ Monomial& l) const
+{
+ monomialLeastCommonMultipleNoWeights(a, b, l);
+ setWeightsAndHash(l);
+}
+
+inline void PolyRing::monomialLeastCommonMultipleNoWeights(
+ ConstMonomial a,
+ ConstMonomial b,
+ Monomial& l) const
+{
+ *l = 0;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ l[i] = std::max(a[i], b[i]);
+}
+
+inline bool PolyRing::monomialHasStrictlyLargerExponent(
+ ConstMonomial hasLarger,
+ ConstMonomial smaller1,
+ ConstMonomial smaller2) const
+{
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (hasLarger[i] > smaller1[i] && hasLarger[i] > smaller2[i])
+ return true;
+ return false;
+}
+
+
+////////////////////////////////////////////////
+// Old Monomial Routines ///////////////////////
+////////////////////////////////////////////////
+
+#ifndef NEWMONOMIALS
+inline bool PolyRing::monomialIsDivisibleBy(const_monomial a, const_monomial b) const
+{
+ // returns truue if b divides a, in this case, result is set to b//a.
+ // for (int i = mNumVars; i >= 1; --i)
+ // {
+ // int c = a[i] - b[i];
+ // if (c < 0) return false;
+ // }
+ for (size_t i = 1; i<= mNumVars; i++)
+ if (a[i] < b[i])
+ return false;
+
+ return true;
+}
+
+inline void PolyRing::monomialDivideToNegative(const_monomial a, const_monomial b, monomial result) const {
+ for (size_t i = 0; i <= this->mTopIndex; ++i)
+ result[i] = a[i] - b[i];
+}
+
+inline bool PolyRing::monomialDivide(const_monomial a, const_monomial b, monomial &result) const
+{
+ //// returns true if b divides a, in this case, result is set to b//a.
+ // for (int i = mNumVars; i >= 1; --i)
+ // {
+ // int c = a[i] - b[i];
+ // if (c >= 0)
+ // result[i] = c;
+ // else
+ // return false;
+ // }
+ //// at this point we have divisibility, so need to fill in the rest of the monomial
+ // *result = *a - *b; // component
+ // for (int i=mHashIndex; i>mNumVars; --i)
+ // result[i] = a[i] - b[i];
+ // return true;
+
+ size_t i;
+ for (i = 1; i <= mNumVars; i++)
+ {
+ int c = a[i] - b[i];
+ if (c >= 0)
+ result[i] = c;
+ else
+ return false;
+ }
+ // at this point we have divisibility, so need to fill in the rest of the monomial
+ *result = *a - *b; // component
+ for ( ; i<=mHashIndex; i++)
+ result[i] = a[i] - b[i];
+ return true;
+}
+
+inline int PolyRing::monomialCompare(const_monomial a, const_monomial b) const
+// returns LT, EQ or GT
+{
+ for (size_t i = mTopIndex; i != static_cast<size_t>(-1); --i)
+ {
+ // if (a[i] == b[i]) continue;
+ // if (a[i] < b[i]) return GT;
+ // return LT;
+ // if (a[i] > b[i]) return LT;
+ int cmp = a[i] - b[i];
+ if (cmp < 0) return GT;
+ if (cmp > 0) return LT;
+ }
+ return EQ;
+}
+
+inline void PolyRing::monomialLeastCommonMultiple(
+ const_monomial a,
+ const_monomial b,
+ monomial l) const
+{
+ monomialLeastCommonMultipleNoWeights(a, b, l);
+ setWeightsAndHash(l);
+}
+
+inline void PolyRing::monomialLeastCommonMultipleNoWeights(
+ const_monomial a,
+ const_monomial b,
+ monomial l) const
+{
+ *l = 0;
+ for (size_t i = 1; i <= mNumVars; ++i)
+ l[i] = std::max(a[i], b[i]);
+}
+
+inline bool PolyRing::monomialHasStrictlyLargerExponent(
+ const_monomial hasLarger,
+ const_monomial smaller1,
+ const_monomial smaller2) const {
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (hasLarger[i] > smaller1[i] && hasLarger[i] > smaller2[i])
+ return true;
+ return false;
+}
+
+inline void PolyRing::setWeightsOnly(monomial a) const
+{
+ a++;
+ const int *wts = &mWeights[0];
+ for (int i=0; i<mNumWeights; i++)
+ {
+ int result = 0;
+ for (size_t j=0; j<mNumVars; j++)
+ result += *wts++ * a[j];
+ a[mNumVars+i] = result;
+ }
+}
+
+inline bool PolyRing::monomialRelativelyPrime(const_monomial a, const_monomial b) const
+{
+ for (size_t i = 1; i <= mNumVars; ++i)
+ if (a[i] > 0 && b[i] > 0)
+ return false;
+ return true;
+}
+#endif
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/Reducer.cpp b/src/mathicgb/Reducer.cpp
new file mode 100644
index 0000000..062e843
--- /dev/null
+++ b/src/mathicgb/Reducer.cpp
@@ -0,0 +1,393 @@
+// Copyright 2011 Michael E. Stillman
+
+#include <iostream>
+#include "stdinc.h"
+#include "Reducer.hpp"
+#include "PolyHeap.hpp"
+#include "PolyGeoBucket.hpp"
+#include "PolyReducer.hpp"
+#include "PolyHashReducer.hpp"
+#include "BjarkeGeobucket.hpp"
+#include "GroebnerBasis.hpp"
+#include "TournamentReducer.hpp"
+#include "HashTourReducer.hpp"
+#include "ReducerPack.hpp"
+#include "ReducerPackDedup.hpp"
+#include "ReducerNoDedup.hpp"
+#include "ReducerDedup.hpp"
+#include "ReducerHash.hpp"
+#include "ReducerHashPack.hpp"
+#include <algorithm>
+
+extern int tracingLevel;
+
+std::auto_ptr<Reducer> Reducer::makeReducer(
+ ReducerType type,
+ PolyRing const& ring
+) {
+ std::auto_ptr<Reducer> reducer = makeReducerNullOnUnknown(type, ring);
+ if (reducer.get() == 0) {
+ std::ostringstream error;
+ error << "Unknown or unimplemented reducer type " << type << ".\n";
+ throw std::runtime_error(error.str());
+ }
+ return reducer;
+}
+
+std::auto_ptr<Reducer> Reducer::makeReducerNullOnUnknown(
+ ReducerType type,
+ PolyRing const& ring
+) {
+ switch (type) {
+ case Reducer_PolyHeap:
+ return std::auto_ptr<Reducer>(new PolyHeap(&ring));
+ case Reducer_PolyGeoBucket:
+ return std::auto_ptr<Reducer>(new PolyGeoBucket(&ring));
+ case Reducer_Poly:
+ return std::auto_ptr<Reducer>(new PolyReducer(&ring));
+ case Reducer_PolyHash:
+ return std::auto_ptr<Reducer>(new PolyHashReducer(&ring));
+ case Reducer_BjarkeGeo:
+ return std::auto_ptr<Reducer>(new BjarkeGeobucket(&ring));
+ case Reducer_TournamentTree:
+ return std::auto_ptr<Reducer>(new TournamentReducer(ring));
+ //return std::auto_ptr<Reducer>
+ //(new ReducerPack<mic::TourTree>(ring));
+ case Reducer_HashTourTree:
+ return std::auto_ptr<Reducer>(new HashTourReducer(ring));
+
+
+ case Reducer_TourTree_NoDedup:
+ return std::auto_ptr<Reducer>(new ReducerNoDedup<mic::TourTree>(ring));
+ case Reducer_TourTree_Dedup:
+ return std::auto_ptr<Reducer>(new ReducerDedup<mic::TourTree>(ring));
+ case Reducer_TourTree_Hashed:
+ return std::auto_ptr<Reducer>(new ReducerHash<mic::TourTree>(ring));
+ //break;
+ case Reducer_TourTree_NoDedup_Packed:
+ return std::auto_ptr<Reducer>(new ReducerPack<mic::TourTree>(ring));
+ case Reducer_TourTree_Dedup_Packed:
+ return std::auto_ptr<Reducer>(new ReducerPackDedup<mic::TourTree>(ring));
+ case Reducer_TourTree_Hashed_Packed:
+ return std::auto_ptr<Reducer>(new ReducerHashPack<mic::TourTree>(ring));
+
+ case Reducer_Heap_NoDedup:
+ return std::auto_ptr<Reducer>(new ReducerNoDedup<mic::Heap>(ring));
+ case Reducer_Heap_Dedup:
+ return std::auto_ptr<Reducer>(new ReducerDedup<mic::Heap>(ring));
+ case Reducer_Heap_Hashed:
+ return std::auto_ptr<Reducer>(new ReducerHash<mic::Heap>(ring));
+ //break;
+ case Reducer_Heap_NoDedup_Packed:
+ return std::auto_ptr<Reducer>(new ReducerPack<mic::Heap>(ring));
+ case Reducer_Heap_Dedup_Packed:
+ return std::auto_ptr<Reducer>(new ReducerPackDedup<mic::Heap>(ring));
+ case Reducer_Heap_Hashed_Packed:
+ return std::auto_ptr<Reducer>(new ReducerHashPack<mic::Heap>(ring));
+
+ case Reducer_Geobucket_NoDedup:
+ return std::auto_ptr<Reducer>(new ReducerNoDedup<mic::Geobucket>(ring));
+ case Reducer_Geobucket_Dedup:
+ return std::auto_ptr<Reducer>(new ReducerDedup<mic::Geobucket>(ring));
+ case Reducer_Geobucket_Hashed:
+ return std::auto_ptr<Reducer>(new ReducerHash<mic::Geobucket>(ring));
+ case Reducer_Geobucket_NoDedup_Packed:
+ return std::auto_ptr<Reducer>(new ReducerPack<mic::Geobucket>(ring));
+ case Reducer_Geobucket_Dedup_Packed:
+ return std::auto_ptr<Reducer>(new ReducerPackDedup<mic::Geobucket>(ring));
+ case Reducer_Geobucket_Hashed_Packed:
+ return std::auto_ptr<Reducer>(new ReducerHashPack<mic::Geobucket>(ring));
+
+ default:
+ break;
+ };
+ return std::auto_ptr<Reducer>();
+}
+
+Reducer::ReducerType Reducer::reducerType(int typ)
+{
+ switch (typ) {
+ case 0: return Reducer_PolyHeap;
+ case 1: return Reducer_PolyGeoBucket;
+ case 2: return Reducer_Poly;
+ case 3: return Reducer_PolyHash;
+ case 4: return Reducer_BjarkeGeo;
+ case 5: return Reducer_TournamentTree;
+ case 6: return Reducer_HashTourTree;
+
+ case 7: return Reducer_TourTree_NoDedup;
+ case 8: return Reducer_TourTree_Dedup;
+ case 9: return Reducer_TourTree_Hashed;
+ case 10: return Reducer_TourTree_NoDedup_Packed;
+ case 11: return Reducer_TourTree_Dedup_Packed;
+ case 12: return Reducer_TourTree_Hashed_Packed;
+
+ case 13: return Reducer_Heap_NoDedup;
+ case 14: return Reducer_Heap_Dedup;
+ case 15: return Reducer_Heap_Hashed;
+ case 16: return Reducer_Heap_NoDedup_Packed;
+ case 17: return Reducer_Heap_Dedup_Packed;
+ case 18: return Reducer_Heap_Hashed_Packed;
+
+ case 19: return Reducer_Geobucket_NoDedup;
+ case 20: return Reducer_Geobucket_Dedup;
+ case 21: return Reducer_Geobucket_Hashed;
+ case 22: return Reducer_Geobucket_NoDedup_Packed;
+ case 23: return Reducer_Geobucket_Dedup_Packed;
+ case 24: return Reducer_Geobucket_Hashed_Packed;
+
+ default: return Reducer_PolyHeap;
+ }
+}
+
+void Reducer::displayReducerTypes(std::ostream &o)
+{
+ o << "Reducer types:" << std::endl;
+ o << " 0 PolyHeap" << std::endl;
+ o << " 1 PolyGeoBucket" << std::endl;
+ o << " 2 Poly" << std::endl;
+ o << " 3 PolyHash" << std::endl;
+ o << " 4 BjarkeGeo" << std::endl;
+ o << " 5 Tournament tree" << std::endl;
+ o << " 6 Hashed Tournament tree" << std::endl;
+
+ o << " 7 TournamentTree.NoDedup" << std::endl;
+ o << " 8 TournamentTree.Dedup" << std::endl;
+ o << " 9 TournamentTree.Hashed" << std::endl;
+ o << " 10 TournamentTree.NoDedup.Packed" << std::endl;
+ o << " 11 TournamentTree.Dedup.Packed" << std::endl;
+ o << " 12 TournamentTree.Hashed.Packed" << std::endl;
+
+ o << " 13 Heap.NoDedup" << std::endl;
+ o << " 14 Heap.Dedup" << std::endl;
+ o << " 15 Heap.Hashed" << std::endl;
+ o << " 16 Heap.NoDedup.Packed" << std::endl;
+ o << " 17 Heap.Dedup.Packed" << std::endl;
+ o << " 18 Heap.Hashed.Packed" << std::endl;
+
+ o << " 19 Geobucket.NoDedup" << std::endl;
+ o << " 20 Geobucket.Dedup" << std::endl;
+ o << " 21 Geobucket.Hashed" << std::endl;
+ o << " 22 Geobucket.NoDedup.Packed" << std::endl;
+ o << " 23 Geobucket.Dedup.Packed" << std::endl;
+ o << " 24 Geobucket.Hashed.Packed" << std::endl;
+}
+
+///////////////////////////
+// ReducerPack //////////
+///////////////////////////
+
+///////////////////////////
+// Reducer NoDedup/Dedup //
+///////////////////////////
+
+
+///////////////////////
+// Reducer ////////////
+///////////////////////
+
+Reducer::Stats::Stats():
+ reductions(0),
+ singularReductions(0),
+ zeroReductions(0),
+ steps(0),
+ maxSteps(0) {}
+
+Reducer::Reducer():
+ stats_maxsize(0),
+ stats_maxsize_live(0),
+ stats_n_inserts(0),
+ stats_n_compares(0) {}
+
+void Reducer::reset()
+{
+ mArena.freeAllAllocs();
+ resetReducer();
+}
+
+size_t Reducer::getMemoryUse() const {
+ return mArena.getMemoryUse();
+}
+
+Poly* Reducer::regularReduce(
+ const_monomial sig,
+ const_monomial multiple,
+ size_t basisElement,
+ const GroebnerBasis& basis)
+{
+ const PolyRing& ring = basis.ring();
+ ++mSigStats.reductions;
+
+ monomial tproduct = ring.allocMonomial(mArena);
+ monomial u = ring.allocMonomial(mArena);
+ ring.monomialMult(multiple, basis.getLeadMonomial(basisElement), tproduct);
+
+ size_t reducer = basis.regularReducer(sig, tproduct);
+ if (reducer == static_cast<size_t>(-1)) {
+ ++mSigStats.singularReductions;
+ mArena.freeAllAllocs();
+ return 0; // singular reduction: no regular top reduction possible
+ }
+
+ ring.monomialDivide(tproduct, basis.getLeadMonomial(reducer), u);
+
+ coefficient coef;
+ ring.coefficientSet(coef, 1);
+ insertTail(const_term(coef, multiple), &basis.poly(basisElement));
+
+ ASSERT(ring.coefficientIsOne(basis.getLeadCoefficient(reducer)));
+ ring.coefficientFromInt(coef, -1);
+ insertTail(const_term(coef, u), &basis.poly(reducer));
+ basis.basis().usedAsReducer(reducer);
+
+ Poly* result = new Poly(&ring);
+
+ unsigned long long steps = 2; // number of steps in this reduction
+ for (const_term v; findLeadTerm(v); ++steps) {
+ ASSERT(v.coeff != 0);
+ reducer = basis.regularReducer(sig, v.monom);
+ if (reducer == static_cast<size_t>(-1)) { // no reducer found
+ result->appendTerm(v.coeff, v.monom);
+ removeLeadTerm();
+ } else { // reduce by reducer
+ basis.basis().usedAsReducer(reducer);
+ monomial mon = ring.allocMonomial(mArena);
+ ring.monomialDivide(v.monom, basis.getLeadMonomial(reducer), mon);
+ ring.coefficientDivide(v.coeff, basis.getLeadCoefficient(reducer), coef);
+ ring.coefficientNegateTo(coef);
+ removeLeadTerm();
+ insertTail(const_term(coef, mon), &basis.poly(reducer));
+ }
+ }
+ result->makeMonic();
+
+ mSigStats.steps += steps;
+ mSigStats.maxSteps = std::max(mSigStats.maxSteps, steps);
+ if (result->isZero())
+ ++mSigStats.zeroReductions;
+
+ reset();
+ return result;
+}
+
+std::auto_ptr<Poly> Reducer::classicReduce(const Poly& poly, const PolyBasis& basis) {
+ monomial identity = basis.ring().allocMonomial(mArena);
+ basis.ring().monomialSetIdentity(identity);
+ insert(identity, &poly);
+
+ return classicReduce(basis);
+}
+
+std::auto_ptr<Poly> Reducer::classicTailReduce(const Poly& poly, const PolyBasis& basis) {
+ ASSERT(&poly.ring() == &basis.ring());
+ ASSERT(!poly.isZero());
+ term identity;
+ identity.monom = basis.ring().allocMonomial(mArena);
+ basis.ring().monomialSetIdentity(identity.monom);
+ basis.ring().coefficientSetOne(identity.coeff);
+ insertTail(identity, &poly);
+
+ std::auto_ptr<Poly> result(new Poly(&basis.ring()));
+ result->appendTerm(poly.getLeadCoefficient(), poly.getLeadMonomial());
+
+ return classicReduce(result, basis);
+}
+
+std::auto_ptr<Poly> Reducer::classicReduceSPoly(
+ const Poly& a,
+ const Poly& b,
+ const PolyBasis& basis
+) {
+ const PolyRing& ring = basis.ring();
+
+ monomial lcm = ring.allocMonomial();
+ ring.monomialLeastCommonMultiple
+ (a.getLeadMonomial(), b.getLeadMonomial(), lcm);
+
+ // insert tail of multiple of a
+ monomial multiple1 = ring.allocMonomial();
+ ring.monomialDivide(lcm, a.getLeadMonomial(), multiple1);
+ coefficient plusOne;
+ ring.coefficientSet(plusOne, 1);
+ insertTail(const_term(plusOne, multiple1), &a);
+
+ // insert tail of multiple of b
+ monomial multiple2 = ring.allocMonomial();
+ ring.monomialDivide(lcm, b.getLeadMonomial(), multiple2);
+ coefficient minusOne = plusOne;
+ ring.coefficientNegateTo(minusOne);
+ insertTail(const_term(minusOne, multiple2), &b);
+
+ std::auto_ptr<Poly> reduced = classicReduce(basis);
+ ring.freeMonomial(lcm);
+ ring.freeMonomial(multiple1);
+ ring.freeMonomial(multiple2);
+ return reduced;
+}
+
+std::auto_ptr<Poly> Reducer::classicReduce
+ (std::auto_ptr<Poly> result, const PolyBasis& basis) {
+ const PolyRing& ring = basis.ring();
+ ASSERT(&result->ring() == &ring);
+ ++mClassicStats.reductions;
+
+ if (tracingLevel > 100)
+ std::cerr << "Classic reduction begun." << std::endl;
+
+ coefficient coef;
+ unsigned long long steps = 1; // number of steps in this reduction
+ for (const_term v; findLeadTerm(v); ++steps) {
+ if (tracingLevel > 100) {
+ std::cerr << "from reducer queue: ";
+ basis.ring().monomialDisplay(std::cerr, v.monom);
+ std::cerr << std::endl;
+ }
+
+ size_t reducer = basis.divisor(v.monom);
+ if (reducer == static_cast<size_t>(-1)) { // no reducer found
+ ASSERT(result->isZero() ||
+ basis.order().signatureCompare(v.monom, result->backMonomial()) == LT);
+ result->appendTerm(v.coeff, v.monom);
+ removeLeadTerm();
+ } else { // reduce by reducer
+ basis.usedAsReducer(reducer);
+ monomial mon = ring.allocMonomial(mArena);
+ ring.monomialDivide(v.monom, basis.leadMonomial(reducer), mon);
+ ring.coefficientDivide(v.coeff, basis.leadCoefficient(reducer), coef);
+ ring.coefficientNegateTo(coef);
+ removeLeadTerm();
+ insertTail(const_term(coef, mon), &basis.poly(reducer));
+
+ if (tracingLevel > 100) {
+ std::cerr << "Reducing by basis element " << reducer << ": ";
+ basis.poly(reducer).display(std::cerr);
+ std::cerr << std::endl;
+ std::cerr << "multiplied by: " << coef << " * ";
+ basis.ring().monomialDisplay(std::cerr, mon);
+ std::cerr << std::endl;
+ }
+ }
+ }
+ result->makeMonic();
+
+ mClassicStats.steps += steps;
+ mClassicStats.maxSteps = std::max(mClassicStats.maxSteps, steps);
+ if (result->isZero())
+ ++mClassicStats.zeroReductions;
+
+ if (tracingLevel > 100)
+ std::cerr << "Classic reduction done." << std::endl;
+
+ reset();
+ return result;
+}
+
+std::auto_ptr<Poly> Reducer::classicReduce(const PolyBasis& basis) {
+ std::auto_ptr<Poly> result(new Poly(&basis.ring()));
+ return classicReduce(result, basis);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/Reducer.hpp b/src/mathicgb/Reducer.hpp
new file mode 100644
index 0000000..1a99981
--- /dev/null
+++ b/src/mathicgb/Reducer.hpp
@@ -0,0 +1,153 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _Reducer_h_
+#define _Reducer_h_
+
+#include "PolyRing.hpp"
+#include "Poly.hpp"
+#include <memtailor.h>
+#include <memory>
+
+class GroebnerBasis;
+class PolyBasis;
+
+/** Abstract base class for classes that allow reduction of polynomials.
+
+todo: consider changing name of findLeadTerm to leadTerm.
+*/
+class Reducer {
+public:
+ enum ReducerType {
+ Reducer_PolyHeap,
+ Reducer_PolyGeoBucket,
+ Reducer_Poly,
+ Reducer_PolyHash,
+ Reducer_BjarkeGeo, // uses hash table on front to remove duplicates
+ Reducer_TournamentTree,
+ Reducer_HashTourTree,
+
+ Reducer_TourTree_NoDedup,
+ Reducer_TourTree_Dedup,
+ Reducer_TourTree_Hashed,
+ Reducer_TourTree_NoDedup_Packed,
+ Reducer_TourTree_Dedup_Packed,
+ Reducer_TourTree_Hashed_Packed,
+
+ Reducer_Heap_NoDedup,
+ Reducer_Heap_Dedup,
+ Reducer_Heap_Hashed,
+ Reducer_Heap_NoDedup_Packed,
+ Reducer_Heap_Dedup_Packed,
+ Reducer_Heap_Hashed_Packed,
+
+ Reducer_Geobucket_NoDedup,
+ Reducer_Geobucket_Dedup,
+ Reducer_Geobucket_Hashed,
+ Reducer_Geobucket_NoDedup_Packed,
+ Reducer_Geobucket_Dedup_Packed,
+ Reducer_Geobucket_Hashed_Packed
+ };
+
+ struct Stats {
+ Stats();
+
+ // Number of signature reductions performed including singular reductions.
+ unsigned long long reductions;
+
+ // Number of regular reductions where the polynomial was not top
+ // regular reducible.
+ unsigned long long singularReductions;
+
+ // Number of reductions to zero.
+ unsigned long long zeroReductions;
+
+ // Total number of steps across all reductions. Does not count detecting
+ // singular reduction as a reduction step.
+ unsigned long long steps;
+
+ // Number of reduction steps in the reduction that had the most steps.
+ unsigned long long maxSteps;
+ };
+
+ Reducer();
+ virtual ~Reducer() {}
+
+ static ReducerType reducerType(int typ);
+ static void displayReducerTypes(std::ostream &o);
+
+ Stats sigStats() const {return mSigStats;}
+ Stats classicStats() const {return mClassicStats;}
+
+ void reset();
+
+ virtual std::string description() const = 0;
+ virtual void insertTail(const_term multiplier, const Poly *f) = 0;
+ virtual void insert(monomial multiplier, const Poly *f) = 0;
+
+ virtual bool findLeadTerm(const_term &result) = 0;
+ virtual void removeLeadTerm() = 0;
+
+ virtual void dump() const {}
+
+ virtual size_t getMemoryUse() const = 0;
+
+ // Regular reduce multiple*basisElement in signature sig by the
+ // basis elements in basis.
+ //
+ // Returns null (0) if multiple*basisElement is not regular top
+ // reducible. This indicates a singular reduction.
+ Poly* regularReduce(
+ const_monomial sig,
+ const_monomial multiple,
+ size_t basisElement,
+ const GroebnerBasis& basis);
+
+ // Clasically reduces poly by the basis elements of basis. The reduction
+ // is classic in that no signatures are taken into account.
+ std::auto_ptr<Poly> classicReduce(const Poly& poly, const PolyBasis& basis);
+
+ // Clasically reduces poly by the basis elements of basis, except that the
+ // lead term is not reduced. The reduction is classic in that no signatures
+ // are taken into account.
+ std::auto_ptr<Poly> classicTailReduce(const Poly& poly, const PolyBasis& basis);
+
+ // Clasically reduces the S-polynomial between a and b.
+ std::auto_ptr<Poly> classicReduceSPoly
+ (const Poly& a, const Poly& b, const PolyBasis& basis);
+
+ static std::auto_ptr<Reducer> makeReducer
+ (ReducerType t, PolyRing const& ring);
+ static std::auto_ptr<Reducer> makeReducerNullOnUnknown
+ (ReducerType t, PolyRing const& ring);
+
+protected:
+ std::auto_ptr<Poly> classicReduce(const PolyBasis& basis);
+ std::auto_ptr<Poly> classicReduce
+ (std::auto_ptr<Poly> partialResult, const PolyBasis& basis);
+
+ virtual void resetReducer() = 0;
+
+ size_t stats_maxsize;
+ size_t stats_maxsize_live;
+ unsigned long long stats_n_inserts;
+ unsigned long long stats_n_compares;
+
+ Stats mSigStats;
+ Stats mClassicStats;
+ memt::Arena mArena;
+};
+
+#if 0
+template<typename Queue, bool Deduplicate> class ReducerPacked; // *,(0,1),1, although Dup and DeDup have
+ // different Entry types...
+template<typename Queue, bool Deduplicate> class ReducerNotPacked; // *,(0,1),0
+template<typename Queue> class ReducerHashedNotPacked; // *,2,0
+template<typename Queue> class ReducerHashedPacked; // *,2,1
+#endif
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerDedup.hpp b/src/mathicgb/ReducerDedup.hpp
new file mode 100644
index 0000000..5a91f60
--- /dev/null
+++ b/src/mathicgb/ReducerDedup.hpp
@@ -0,0 +1,192 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _reducer_dedup_h_
+#define _reducer_dedup_h_
+
+#include <memtailor.h>
+#include <mathic.h>
+
+#include "Reducer.hpp"
+#include "ReducerHelper.hpp"
+
+template<template<typename ConfigType> class Queue> class ReducerDedup;
+
+template<template<typename> class Queue>
+class ReducerDedup : public Reducer {
+public:
+ ReducerDedup(const PolyRing& R);
+ virtual ~ReducerDedup();
+
+ virtual std::string description() const {
+ return mQueue.getName() + "-dedup";
+ }
+
+ virtual void insertTail(const_term multiplier, const Poly *f);
+ virtual void insert(monomial multiplier, const Poly *f);
+
+ virtual bool findLeadTerm(const_term &result);
+ virtual void removeLeadTerm();
+
+ virtual size_t getMemoryUse() const;
+
+protected:
+ virtual void resetReducer();
+
+public:
+ // This Configuration is designed to work with
+ // mathic::TourTree, mathic::Heap, and mathic::Geobucket
+
+ class Configuration : public ReducerHelper::DedupConfiguration {
+ public:
+ typedef term Entry;
+ Configuration(const PolyRing& ring): DedupConfiguration(ring) {}
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return ring().monomialCompare(a.monom, b.monom);
+ }
+ Entry deduplicate(Entry a, Entry b) const {
+ // change a.coeff, and free b.monom
+ ring().coefficientAddTo(a.coeff, b.coeff);
+ ring().freeMonomial(b.monom);
+ return a;
+ }
+ };
+
+private:
+ class MonomialFree;
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ Queue<Configuration> mQueue;
+};
+
+template<template<typename> class Q>
+ReducerDedup<Q>::ReducerDedup(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring))
+{
+}
+
+template<template<typename> class Q>
+class ReducerDedup<Q>::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(term entry)
+ {
+ mRing.freeMonomial(entry.monom);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+template<template<typename> class Q>
+ReducerDedup<Q>::~ReducerDedup()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+template<template<typename> class Q>
+void ReducerDedup<Q>::insertTail(const_term multiple, const Poly* poly)
+{
+ if (poly->nTerms() <= 1)
+ return;
+ mLeadTermKnown = false;
+
+ Poly::const_iterator i = poly->begin();
+ for (++i; i != poly->end(); ++i)
+ {
+ term t;
+ t.monom = mRing.allocMonomial();
+ mRing.monomialMult(multiple.monom, i.getMonomial(), t.monom);
+ mRing.coefficientMult(multiple.coeff, i.getCoefficient(), t.coeff);
+ mQueue.push(t);
+ }
+}
+
+template<template<typename> class Q>
+void ReducerDedup<Q>::insert(monomial multiple, const Poly* poly)
+{
+ if (poly->isZero())
+ return;
+ mLeadTermKnown = false;
+
+ for (Poly::const_iterator i = poly->begin(); i != poly->end(); ++i) {
+ term t(i.getCoefficient(), mRing.allocMonomial());
+ mRing.monomialMult(multiple, i.getMonomial(), t.monom);
+ mQueue.push(t);
+ }
+}
+
+template<template<typename> class Q>
+bool ReducerDedup<Q>::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ mLeadTerm = mQueue.top();
+ mQueue.pop();
+
+ while (true) {
+ if (mQueue.empty())
+ break;
+
+ term entry = mQueue.top();
+ if (!mRing.monomialEQ(entry.monom, mLeadTerm.monom))
+ break;
+ mRing.coefficientAddTo(mLeadTerm.coeff, entry.coeff);
+ mRing.freeMonomial(entry.monom);
+ mQueue.pop();
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+template<template<typename> class Q>
+void ReducerDedup<Q>::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+template<template<typename> class Q>
+void ReducerDedup<Q>::resetReducer()
+{
+ MonomialFree freeer(mRing);
+ //mQueue.forAll(freeer);
+ // mQueue.clear();
+}
+
+template<template<typename> class Q>
+size_t ReducerDedup<Q>::getMemoryUse() const
+{
+ return mQueue.getMemoryUse();
+}
+
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerHash.hpp b/src/mathicgb/ReducerHash.hpp
new file mode 100644
index 0000000..a08e4ca
--- /dev/null
+++ b/src/mathicgb/ReducerHash.hpp
@@ -0,0 +1,188 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _reducer_hash_h_
+#define _reducer_hash_h_
+
+#include <memtailor.h>
+#include <mathic.h>
+
+#include "Reducer.hpp"
+#include "ReducerHelper.hpp"
+#include "PolyHashTable.hpp"
+
+template<template<typename ConfigType> class Queue> class ReducerHash;
+
+template<template<typename> class Queue>
+class ReducerHash : public Reducer {
+public:
+ ReducerHash(const PolyRing &ring);
+ ~ReducerHash();
+
+ virtual std::string description() const {
+ return mQueue.getName() + "-hashed";
+ }
+
+ void insertTail(const_term multiplier, const Poly *f);
+ void insert(monomial multiplier, const Poly *f);
+
+ bool findLeadTerm(const_term &result);
+ void removeLeadTerm();
+
+ size_t getMemoryUse() const;
+
+protected:
+ void resetReducer();
+
+public:
+ class Configuration : public ReducerHelper::PlainConfiguration {
+ public:
+ typedef PolyHashTable::node * Entry;
+
+ Configuration(const PolyRing& ring):
+ PlainConfiguration(ring),
+ mComparisonCount(0) {}
+
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ ++mComparisonCount;
+ return ring().monomialLT(a->monom, b->monom);
+ }
+
+ unsigned long long getComparisonCount() const {return mComparisonCount;}
+
+ void resetComparisonCount() const {mComparisonCount = 0;}
+
+ private:
+ mutable unsigned long long mComparisonCount;
+ };
+
+private:
+ const PolyRing &mRing;
+ PolyHashTable mHashTable;
+ Queue<Configuration> mQueue;
+
+ // Number of (distinct) monomials in mQueue.
+ // Statistics and debugging use only
+ size_t mNodeCount;
+};
+
+template<template<typename> class Q>
+ReducerHash<Q>::ReducerHash(const PolyRing &ring)
+ : Reducer(),
+ mRing(ring),
+ mHashTable(&ring,10),
+ mQueue(Configuration(ring)),
+ mNodeCount(0)
+{
+}
+
+template<template<typename> class Q>
+ReducerHash<Q>::~ReducerHash()
+{
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+
+template<template<typename> class Q>
+void ReducerHash<Q>::insertTail(const_term multiplier, const Poly *g1)
+{
+ if (g1->nTerms() <= 1) return;
+
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+ PolyHashTable::MonomialArray M;
+ mHashTable.insert(multiplier, ++(g1->begin()), g1->end(), M);
+
+ if (!M.empty()) {
+ mQueue.push(M.begin(),M.end());
+ mNodeCount += M.size();
+ }
+
+ stats_n_inserts++;
+ stats_n_compares += mQueue.getConfiguration().getComparisonCount();
+ mQueue.getConfiguration().resetComparisonCount();
+
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+}
+
+template<template<typename> class Q>
+void ReducerHash<Q>::insert(monomial multiplier, const Poly *g1)
+{
+ PolyHashTable::MonomialArray M;
+
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+
+ mHashTable.insert(multiplier, g1->begin(), g1->end(), M);
+
+ if (!M.empty())
+ {
+ mQueue.push(M.begin(),M.end());
+#if 0
+ for (PolyHashTable::MonomialArray::const_iterator a = M.begin(); a != M.end(); ++a)
+ mQueue.push(*a);
+#endif
+ mNodeCount += M.size();
+ }
+
+ stats_n_inserts++;
+ stats_n_compares += mQueue.getConfiguration().getComparisonCount();
+ mQueue.getConfiguration().resetComparisonCount();
+
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+}
+
+template<template<typename> class Q>
+bool ReducerHash<Q>::findLeadTerm(const_term &result)
+{
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+ while (!mQueue.empty())
+ {
+ if (mHashTable.popTerm(mQueue.top(), result.coeff, result.monom))
+ // returns true if mQueue.top() is not the zero element
+ return true;
+ mQueue.pop();
+ mNodeCount--;
+ }
+ return false;
+}
+
+template<template<typename> class Q>
+void ReducerHash<Q>::removeLeadTerm()
+// returns true if there is a term to extract
+{
+ mQueue.pop();
+ mNodeCount--;
+
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+}
+
+template<template<typename> class Q>
+void ReducerHash<Q>::resetReducer()
+{
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+ const_term t;
+ while (findLeadTerm(t))
+ {
+ mQueue.pop();
+ mNodeCount--;
+ }
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+ mHashTable.reset();
+ ASSERT(mNodeCount == mHashTable.getNodeCount());
+ // how to reset mQueue ?
+}
+
+template<template<typename> class Q>
+size_t ReducerHash<Q>::getMemoryUse() const
+{
+ size_t result = mHashTable.getMemoryUse();
+ result += mQueue.getMemoryUse();
+ return result;
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerHashPack.hpp b/src/mathicgb/ReducerHashPack.hpp
new file mode 100644
index 0000000..f7399c6
--- /dev/null
+++ b/src/mathicgb/ReducerHashPack.hpp
@@ -0,0 +1,285 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _reducer_hash_pack_h
+#define _reducer_hash_pack_h
+
+#include <mathic.h>
+#include <memtailor.h>
+
+#include "Reducer.hpp"
+#include "ReducerHelper.hpp"
+#include "PolyHashTable.hpp"
+
+template<template<typename ConfigType> class Queue> class ReducerHashPack;
+
+template<template<typename> class Queue>
+class ReducerHashPack : public Reducer {
+public:
+ ReducerHashPack(const PolyRing& R);
+ virtual ~ReducerHashPack();
+
+ virtual std::string description() const {
+ return mQueue.getName() + "-hashed-packed";
+ }
+
+ virtual void insertTail(const_term multiplier, const Poly *f);
+ virtual void insert(monomial multiplier, const Poly *f);
+
+ virtual bool findLeadTerm(const_term &result);
+ virtual void removeLeadTerm();
+
+ virtual size_t getMemoryUse() const;
+
+protected:
+ virtual void resetReducer();
+
+private:
+ // Represents a term multiple of a polynomial,
+ // together with a current term of the multiple.
+ struct MultipleWithPos {
+ MultipleWithPos(const Poly& poly, const_term multiple);
+
+ Poly::const_iterator pos;
+ Poly::const_iterator const end;
+ const_term const multiple;
+ monomial current; // multiple.monom * pos.getMonomial()
+ PolyHashTable::node* node;
+
+ void computeCurrent(const PolyRing& ring, monomial current);
+ void currentCoefficient(const PolyRing& ring, coefficient& coeff);
+ void destroy(const PolyRing& ring);
+ };
+
+ class Configuration : public ReducerHelper::PlainConfiguration {
+ public:
+ typedef MultipleWithPos* Entry;
+ Configuration(const PolyRing& ring) : PlainConfiguration(ring) {}
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return ring().monomialLT(a->current, b->current);
+ }
+ };
+
+ class MonomialFree;
+
+ void insertEntry(MultipleWithPos* entry);
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ Queue<Configuration> mQueue;
+ PolyHashTable mHashTable;
+ memt::BufferPool mPool;
+};
+
+template<template<typename> class Q>
+ReducerHashPack<Q>::ReducerHashPack(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring)),
+ mHashTable(&ring, 10),
+ mPool(sizeof(MultipleWithPos))
+{
+}
+
+template<template<typename> class Q>
+class ReducerHashPack<Q>::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(MultipleWithPos* entry) {
+ entry->destroy(mRing);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+template<template<typename> class Q>
+ReducerHashPack<Q>::~ReducerHashPack()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+template<template<typename> class Q>
+void ReducerHashPack<Q>::insertTail(const_term multiple, const Poly* poly)
+{
+ ASSERT(poly != 0);
+ ASSERT(&poly->ring() == &mRing);
+ if (poly->nTerms() < 2)
+ return;
+ MultipleWithPos* entry =
+ new (mPool.alloc()) MultipleWithPos(*poly, multiple);
+ ++entry->pos;
+ insertEntry(entry);
+}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::insert(monomial multiple, const Poly* poly)
+{
+ ASSERT(poly != 0);
+ ASSERT(&poly->ring() == &mRing);
+ if (poly->isZero())
+ return;
+ term termMultiple(1, multiple);
+ insertEntry(new (mPool.alloc()) MultipleWithPos(*poly, termMultiple));
+}
+
+namespace {
+ const_term allocTerm(const PolyRing& ring, const_term term) {
+ monomial mono = ring.allocMonomial();
+ ring.monomialCopy(term.monom, mono);
+ return const_term(term.coeff, mono);
+ }
+}
+
+template<template<typename> class Q>
+ReducerHashPack<Q>::MultipleWithPos::MultipleWithPos
+(const Poly& poly, const_term multiple):
+ pos(poly.begin()),
+ end(poly.end()),
+ multiple(allocTerm(poly.ring(), multiple)),
+ current(poly.ring().allocMonomial()),
+ node(0) {}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::MultipleWithPos::
+computeCurrent(const PolyRing& ring, monomial current) {
+ ring.monomialMult(multiple.monom, pos.getMonomial(), current);
+}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::MultipleWithPos::currentCoefficient
+(const PolyRing& ring, coefficient& coeff) {
+ ring.coefficientMult(multiple.coeff, pos.getCoefficient(), coeff);
+}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::MultipleWithPos::destroy(const PolyRing& ring) {
+ ring.freeMonomial(current);
+ ring.freeMonomial(const_cast<ConstMonomial&>(multiple.monom).castAwayConst());
+
+ // Call the destructor to destruct the iterators into std::vector.
+ // In debug mode MSVC puts those in a linked list and the destructor
+ // has to be called since it takes an iterator off the list. We had
+ // memory corruption problems before doing this.
+ this->~MultipleWithPos();
+}
+
+template<template<typename> class Q>
+bool ReducerHashPack<Q>::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ MultipleWithPos* entry = mQueue.top();
+ ASSERT(entry != 0);
+
+ // remove node from hash table first since we are going to be changing
+ // the monomial after this, and if we do that before the hash value will
+ // change.
+ mHashTable.remove(entry->node);
+
+ // extract information into mLeadTerm
+ mLeadTerm.monom.swap(entry->current);
+ entry->node->monom = entry->current;
+ mLeadTerm.coeff = entry->node->coeff;
+
+ // remove old monomial from hash table and insert next
+ ASSERT(entry->pos != entry->end);
+ while (true) {
+ ++entry->pos;
+ if (entry->pos == entry->end) {
+ mQueue.pop();
+ entry->destroy(mRing);
+ mPool.free(entry);
+ break;
+ }
+ term t;
+ t.monom = entry->current;
+ entry->computeCurrent(mRing, t.monom);
+ entry->currentCoefficient(mRing, t.coeff);
+
+ std::pair<bool, PolyHashTable::node*> p = mHashTable.insert(t);
+ if (p.first) {
+ entry->node = p.second;
+ mQueue.decreaseTop(entry);
+ break;
+ }
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::insertEntry(MultipleWithPos* entry) {
+ ASSERT(entry != 0);
+ for (; entry->pos != entry->end; ++entry->pos) {
+ term t;
+ t.monom = entry->current;
+ entry->computeCurrent(mRing, t.monom);
+ entry->currentCoefficient(mRing, t.coeff);
+
+ std::pair<bool, PolyHashTable::node*> p = mHashTable.insert(t);
+ if (p.first) {
+ mLeadTermKnown = false;
+ entry->node = p.second;
+ mQueue.push(entry);
+ return;
+ }
+ }
+ entry->destroy(mRing);
+ mPool.free(entry);
+}
+
+template<template<typename> class Q>
+void ReducerHashPack<Q>::resetReducer()
+{
+ mLeadTermKnown = false;
+ MonomialFree freeer(mRing);
+#if 0
+ //TODO: reinstate these lines, once Geobucket, TourTree and Heap can all handle them
+ mQueue.forAll(freeer);
+ mQueue.clear();
+#endif
+ mHashTable.reset();
+}
+
+template<template<typename> class Q>
+size_t ReducerHashPack<Q>::getMemoryUse() const
+{
+ return mQueue.getMemoryUse() +
+ mPool.getMemoryUse() +
+ mHashTable.getMemoryUse();
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerHelper.hpp b/src/mathicgb/ReducerHelper.hpp
new file mode 100644
index 0000000..d943061
--- /dev/null
+++ b/src/mathicgb/ReducerHelper.hpp
@@ -0,0 +1,85 @@
+#ifndef _reducer_helper_h_
+#define _reducer_helper_h_
+
+// This namespace contains functions and classees that are useful for
+// writing subclasses of Reducer.
+
+#include "PolyRing.hpp"
+
+namespace ReducerHelper {
+ // ************** Configurations **********************
+
+ // Common base class for the configurations offered here.
+ // The configurations are designed to work with
+ // mathic::TourTree, mathic::Heap and mathic::Geobucket.
+ class ConfigurationBasics {
+ public:
+ ConfigurationBasics(const PolyRing& ring): mRing(ring) {}
+ const PolyRing& ring() const {return mRing;}
+
+ // Special fields for TourTree and Heap
+ static const bool fastIndex = true;
+
+ // Special fields for Geobuckets:
+ static const size_t geoBase = 4;
+ static const size_t minBucketSize = 8;
+
+ static const bool minBucketBinarySearch = false;
+ static const bool trackFront = true;
+ static const bool premerge = false;
+ static const bool collectMax = false;
+ static const int bucketStorage = 1;
+ static const size_t insertFactor = 1;
+
+ private:
+ const PolyRing& mRing;
+ };
+
+ // Base class for a configuration with deduplication turned off.
+ // This cannot be a template since then all the names would be hidden
+ // in any subclasses.
+ class PlainConfiguration : public ConfigurationBasics {
+ public:
+ PlainConfiguration(const PolyRing& ring): ConfigurationBasics(ring) {}
+
+ static const bool supportDeduplication = false;
+ typedef bool CompareResult;
+ bool cmpLessThan(bool r) const {return r;}
+
+ // These last two members are not supposed to be called.
+ // The dummy deduplicate function has to be a template since we do not
+ // know what type Entry is.
+ template<class Entry>
+ Entry deduplicate(Entry a, Entry b) const {ASSERT(false); return a;}
+ bool cmpEqual(bool) const {ASSERT(false); return false;}
+ };
+
+ // Base class for a configuration with deduplication turned on.
+ // This cannot be a template since then all the names would be hidden
+ // in any subclasses.
+ class DedupConfiguration : public ConfigurationBasics {
+ public:
+ DedupConfiguration(const PolyRing& ring): ConfigurationBasics(ring) {}
+
+ static const bool supportDeduplication = true;
+ typedef int CompareResult;
+ bool cmpLessThan(int r) const {return r == LT;}
+ bool cmpEqual(int r) const {return r == EQ;}
+ };
+
+ // ************** Utility functions **********************
+
+ const_term allocTermCopy(const PolyRing& ring, const_term term) {
+ monomial mono = ring.allocMonomial();
+ ring.monomialCopy(term.monom, mono);
+ return const_term(term.coeff, mono);
+ }
+
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerNoDedup.hpp b/src/mathicgb/ReducerNoDedup.hpp
new file mode 100644
index 0000000..f4fdd3a
--- /dev/null
+++ b/src/mathicgb/ReducerNoDedup.hpp
@@ -0,0 +1,186 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _reducer_nodedup_h_
+#define _reducer_nodedup_h_
+
+#include <memtailor.h>
+#include <mathic.h>
+
+#include "Reducer.hpp"
+#include "ReducerHelper.hpp"
+
+template<template<typename ConfigType> class Queue> class ReducerNoDedup;
+
+template<template<typename> class Queue>
+class ReducerNoDedup : public Reducer {
+public:
+ ReducerNoDedup(const PolyRing& R);
+ virtual ~ReducerNoDedup();
+
+ virtual std::string description() const {
+ return mQueue.getName() + "-nodedup";
+ }
+
+ virtual void insertTail(const_term multiplier, const Poly *f);
+ virtual void insert(monomial multiplier, const Poly *f);
+
+ virtual bool findLeadTerm(const_term &result);
+ virtual void removeLeadTerm();
+
+ virtual size_t getMemoryUse() const;
+
+protected:
+ virtual void resetReducer();
+
+public:
+ // This Configuration is designed to work with
+ // mathic::TourTree, mathic::Heap, and mathic::Geobucket
+
+ class Configuration : public ReducerHelper::PlainConfiguration {
+ public:
+ typedef term Entry;
+ Configuration(const PolyRing& ring): PlainConfiguration(ring) {}
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return ring().monomialLT(a.monom, b.monom);
+ }
+ };
+
+private:
+ class MonomialFree;
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ Queue<Configuration> mQueue;
+};
+
+template<template<typename> class Q>
+ReducerNoDedup<Q>::ReducerNoDedup(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring))
+{
+}
+
+template<template<typename> class Q>
+class ReducerNoDedup<Q>::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(term entry)
+ {
+ mRing.freeMonomial(entry.monom);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+template<template<typename> class Q>
+ReducerNoDedup<Q>::~ReducerNoDedup()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+template<template<typename> class Q>
+void ReducerNoDedup<Q>::insertTail(const_term multiple, const Poly* poly)
+{
+ if (poly->nTerms() <= 1)
+ return;
+ mLeadTermKnown = false;
+
+ Poly::const_iterator i = poly->begin();
+ for (++i; i != poly->end(); ++i)
+ {
+ term t;
+ t.monom = mRing.allocMonomial();
+ mRing.monomialMult(multiple.monom, i.getMonomial(), t.monom);
+ mRing.coefficientMult(multiple.coeff, i.getCoefficient(), t.coeff);
+ mQueue.push(t);
+ }
+}
+
+template<template<typename> class Q>
+void ReducerNoDedup<Q>::insert(monomial multiple, const Poly* poly)
+{
+ if (poly->isZero())
+ return;
+ mLeadTermKnown = false;
+
+ for (Poly::const_iterator i = poly->begin(); i != poly->end(); ++i) {
+ term t(i.getCoefficient(), mRing.allocMonomial());
+ mRing.monomialMult(multiple, i.getMonomial(), t.monom);
+ mQueue.push(t);
+ }
+}
+
+template<template<typename> class Q>
+bool ReducerNoDedup<Q>::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ mLeadTerm = mQueue.top();
+ mQueue.pop();
+
+ while (true) {
+ if (mQueue.empty())
+ break;
+
+ term entry = mQueue.top();
+ if (!mRing.monomialEQ(entry.monom, mLeadTerm.monom))
+ break;
+ mRing.coefficientAddTo(mLeadTerm.coeff, entry.coeff);
+ mRing.freeMonomial(entry.monom);
+ mQueue.pop();
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+template<template<typename> class Q>
+void ReducerNoDedup<Q>::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+template<template<typename> class Q>
+void ReducerNoDedup<Q>::resetReducer()
+{
+ MonomialFree freeer(mRing);
+ //mQueue.forAll(freeer);
+ // mQueue.clear();
+}
+
+template<template<typename> class Q>
+size_t ReducerNoDedup<Q>::getMemoryUse() const
+{
+ return mQueue.getMemoryUse();
+}
+
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerPack.hpp b/src/mathicgb/ReducerPack.hpp
new file mode 100644
index 0000000..747a1d5
--- /dev/null
+++ b/src/mathicgb/ReducerPack.hpp
@@ -0,0 +1,250 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _reducer_pack_h_
+#define _reducer_pack_h_
+
+#include <memtailor.h>
+#include <mathic.h>
+
+#include "Reducer.hpp"
+#include "ReducerHelper.hpp"
+
+/**
+
+todo: consider changing name of findLeadTerm to leadTerm.
+*/
+template<template<typename> class Queue>
+class ReducerPack : public Reducer {
+public:
+ ReducerPack(const PolyRing& ring);
+ virtual ~ReducerPack();
+
+ virtual std::string description() const {
+ return mQueue.getName() + "-packed";
+ }
+
+ virtual void insertTail(const_term multiplier, const Poly* f);
+ virtual void insert(monomial multiplier, const Poly* f);
+
+ virtual bool findLeadTerm(const_term& result);
+ virtual void removeLeadTerm();
+
+ virtual size_t getMemoryUse() const;
+
+protected:
+ virtual void resetReducer();
+
+private:
+ // Represents a term multiple of a polynomial,
+ // together with a current term of the multiple.
+public:
+ struct MultipleWithPos {
+ MultipleWithPos(const Poly& poly, const_term multiple);
+
+ Poly::const_iterator pos;
+ Poly::const_iterator const end;
+ const_term const multiple;
+
+ // invariant: current is the monomial product of multiple.monom
+ // and pos.getMonomial().
+ monomial current;
+
+ // Ensures the invariant, so sets current to the product of
+ // multiple.monom and pos.getMonomial().
+ void computeCurrent(const PolyRing& ring);
+ void currentCoefficient(const PolyRing& ring, coefficient& coeff);
+ void destroy(const PolyRing& ring);
+ };
+
+ class Configuration : public ReducerHelper::PlainConfiguration {
+ public:
+ typedef MultipleWithPos* Entry;
+ Configuration(const PolyRing& ring) : PlainConfiguration(ring) {}
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return ring().monomialLT(a->current, b->current);
+ }
+ };
+private:
+
+ class MonomialFree;
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ Queue<Configuration> mQueue;
+ memt::BufferPool mPool;
+};
+
+extern int tracingLevel;
+
+template<template<typename> class Q>
+ReducerPack<Q>::ReducerPack(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring)),
+ mPool(sizeof(MultipleWithPos))
+{
+}
+
+template<template<typename> class Q>
+class ReducerPack<Q>::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(MultipleWithPos* entry)
+ {
+ entry->destroy(mRing);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+template<template<typename> class Q>
+ReducerPack<Q>::~ReducerPack()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+template<template<typename> class Q>
+void ReducerPack<Q>::insertTail(const_term multiple, const Poly* poly)
+{
+ if (poly->nTerms() <= 1)
+ return;
+ mLeadTermKnown = false;
+
+ MultipleWithPos* entry =
+ new (mPool.alloc()) MultipleWithPos(*poly, multiple);
+ ++entry->pos;
+ entry->computeCurrent(poly->ring());
+ mQueue.push(entry);
+}
+
+template<template<typename> class Q>
+void ReducerPack<Q>::insert(monomial multiple, const Poly* poly)
+{
+ if (poly->isZero())
+ return;
+ mLeadTermKnown = false;
+
+ // todo: avoid multiplication by 1
+ term termMultiple(1, multiple);
+ MultipleWithPos* entry =
+ new (mPool.alloc()) MultipleWithPos(*poly, termMultiple);
+ entry->computeCurrent(poly->ring());
+ mQueue.push(entry);
+}
+
+template<template<typename> class Q>
+ReducerPack<Q>::MultipleWithPos::MultipleWithPos
+(const Poly& poly, const_term multipleParam):
+ pos(poly.begin()),
+ end(poly.end()),
+ multiple(ReducerHelper::allocTermCopy(poly.ring(), multipleParam)),
+ current(poly.ring().allocMonomial()) {}
+
+template<template<typename> class Q>
+void ReducerPack<Q>::MultipleWithPos::computeCurrent(const PolyRing& ring) {
+ ring.monomialMult(multiple.monom, pos.getMonomial(), current);
+}
+
+template<template<typename> class Q>
+void ReducerPack<Q>::MultipleWithPos::currentCoefficient
+(const PolyRing& ring, coefficient& coeff) {
+ ring.coefficientMult(multiple.coeff, pos.getCoefficient(), coeff);
+}
+
+template<template<typename> class Q>
+void ReducerPack<Q>::MultipleWithPos::destroy(const PolyRing& ring) {
+ ring.freeMonomial(current);
+ ConstMonomial& monom = const_cast<ConstMonomial&>(multiple.monom);
+ ring.freeMonomial(monom.castAwayConst());
+
+ // Call the destructor to destruct the iterators into std::vector.
+ // In debug mode MSVC puts those in a linked list and the destructor
+ // has to be called since it takes an iterator off the list. We had
+ // memory corruption problems before doing this.
+ this->~MultipleWithPos();
+}
+
+template<template<typename> class Q>
+bool ReducerPack<Q>::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ MultipleWithPos* entry = mQueue.top();
+ mLeadTerm.monom.swap(entry->current);
+ entry->currentCoefficient(mRing, mLeadTerm.coeff);
+
+ while (true) {
+ ++entry->pos;
+ if (entry->pos == entry->end) {
+ mQueue.pop();
+ entry->destroy(mRing);
+ mPool.free(entry);
+ } else {
+ entry->computeCurrent(mRing);
+ mQueue.decreaseTop(entry);
+ }
+
+ if (mQueue.empty())
+ break;
+
+ entry = mQueue.top();
+ if (!mRing.monomialEQ(entry->current, mLeadTerm.monom))
+ break;
+ coefficient coeff;
+ entry->currentCoefficient(mRing, coeff);
+ mRing.coefficientAddTo
+ (mLeadTerm.coeff, const_cast<const coefficient&>(coeff));
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+template<template<typename> class Q>
+void ReducerPack<Q>::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+template<template<typename> class Q>
+void ReducerPack<Q>::resetReducer()
+{
+ MonomialFree freeer(mRing);
+ mQueue.forAll(freeer);
+ mQueue.clear();
+}
+
+template<template<typename> class Q>
+size_t ReducerPack<Q>::getMemoryUse() const
+{
+ return mQueue.getMemoryUse() + mPool.getMemoryUse();
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/ReducerPackDedup.hpp b/src/mathicgb/ReducerPackDedup.hpp
new file mode 100644
index 0000000..f011c5e
--- /dev/null
+++ b/src/mathicgb/ReducerPackDedup.hpp
@@ -0,0 +1,308 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _reducer_pack_dedup_h_
+#define _reducer_pack_dedup_h_
+
+#include <memtailor.h>
+#include <mathic.h>
+
+#include "Reducer.hpp"
+#include "ReducerHelper.hpp"
+
+template<template<typename> class Queue>
+class ReducerPackDedup : public Reducer {
+public:
+ ReducerPackDedup(const PolyRing& ring);
+ virtual ~ReducerPackDedup();
+
+ virtual std::string description() const {
+ return mQueue.getName() + "-packed";
+ }
+
+ virtual void insertTail(const_term multiplier, const Poly* f);
+ virtual void insert(monomial multiplier, const Poly* f);
+
+ virtual bool findLeadTerm(const_term& result);
+ virtual void removeLeadTerm();
+
+ virtual size_t getMemoryUse() const;
+
+protected:
+ virtual void resetReducer();
+
+private:
+ // Represents a term multiple of a polynomial,
+ // together with a current term of the multiple.
+public:
+ struct MultipleWithPos {
+ MultipleWithPos(const Poly& poly, const_term multiple);
+
+ Poly::const_iterator pos;
+ Poly::const_iterator const end;
+ const_term const multiple;
+
+ // invariant: current is the monomial product of multiple.monom
+ // and pos.getMonomial().
+ monomial current;
+
+ // Ensures the invariant, so sets current to the product of
+ // multiple.monom and pos.getMonomial().
+ void computeCurrent(const PolyRing& ring);
+ void currentCoefficient(const PolyRing& ring, coefficient& coeff);
+ void addCurrentCoefficient(const PolyRing& ring, coefficient& coeff);
+ void destroy(const PolyRing& ring);
+
+ // Points to a circular list of entries that have the same current
+ // monomial. If no other such entries have been discovered, then
+ // chain points to this object itself. We use a circular linked list
+ // as those allow merging in O(1) time.
+ MultipleWithPos* chain;
+ void mergeChains(MultipleWithPos& entry) {
+ // This only works if *this and entry are not already in the
+ // same chain!
+ std::swap(chain, entry.chain);
+ }
+ };
+
+ class Configuration : public ReducerHelper::DedupConfiguration {
+ public:
+ typedef MultipleWithPos* Entry;
+ Configuration(const PolyRing& ring): DedupConfiguration(ring) {}
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return ring().monomialCompare(a->current, b->current);
+ }
+ Entry deduplicate(Entry a, Entry b) const {
+ a->mergeChains(*b);
+ return a;
+ }
+ };
+private:
+ class MonomialFree;
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ Queue<Configuration> mQueue;
+ memt::BufferPool mPool;
+};
+
+extern int tracingLevel;
+
+template<template<typename> class Q>
+ReducerPackDedup<Q>::ReducerPackDedup(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring)),
+ mPool(sizeof(MultipleWithPos))
+{
+}
+
+template<template<typename> class Q>
+class ReducerPackDedup<Q>::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(MultipleWithPos* entry)
+ {
+ entry->destroy(mRing);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+template<template<typename> class Q>
+ReducerPackDedup<Q>::~ReducerPackDedup()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::insertTail(const_term multiple, const Poly* poly)
+{
+ if (poly->nTerms() <= 1)
+ return;
+ mLeadTermKnown = false;
+
+ MultipleWithPos* entry =
+ new (mPool.alloc()) MultipleWithPos(*poly, multiple);
+ ++entry->pos;
+ entry->computeCurrent(poly->ring());
+ mQueue.push(entry);
+}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::insert(monomial multiple, const Poly* poly)
+{
+ if (poly->isZero())
+ return;
+ mLeadTermKnown = false;
+
+ // todo: avoid multiplication by 1
+ term termMultiple(1, multiple);
+ MultipleWithPos* entry =
+ new (mPool.alloc()) MultipleWithPos(*poly, termMultiple);
+ entry->computeCurrent(poly->ring());
+ mQueue.push(entry);
+}
+
+template<template<typename> class Q>
+ReducerPackDedup<Q>::MultipleWithPos::MultipleWithPos
+(const Poly& poly, const_term multipleParam):
+ pos(poly.begin()),
+ end(poly.end()),
+ multiple(ReducerHelper::allocTermCopy(poly.ring(), multipleParam)),
+ current(poly.ring().allocMonomial()),
+ chain(this) {}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::MultipleWithPos::computeCurrent(const PolyRing& ring) {
+ ring.monomialMult(multiple.monom, pos.getMonomial(), current);
+}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::MultipleWithPos::currentCoefficient
+(const PolyRing& ring, coefficient& coeff) {
+ ring.coefficientMult(multiple.coeff, pos.getCoefficient(), coeff);
+}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::MultipleWithPos::addCurrentCoefficient
+(const PolyRing& ring, coefficient& coeff) {
+ coefficient tmp;
+ ring.coefficientMult(multiple.coeff, pos.getCoefficient(), tmp);
+ ring.coefficientAddTo(coeff, tmp);
+}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::MultipleWithPos::destroy(const PolyRing& ring) {
+ MultipleWithPos* entry = this;
+ do {
+ ring.freeMonomial(entry->current);
+ ConstMonomial& monom = const_cast<ConstMonomial&>(entry->multiple.monom);
+ ring.freeMonomial(monom.castAwayConst());
+ MultipleWithPos* next = entry->chain;
+ ASSERT(next != 0);
+
+ // Call the destructor to destruct the iterators into std::vector.
+ // In debug mode MSVC puts those in a linked list and the destructor
+ // has to be called since it takes an iterator off the list. We had
+ // memory corruption problems before doing this.
+ entry->~MultipleWithPos();
+
+ entry = next;
+ } while (entry != this);
+}
+
+template<template<typename> class Q>
+bool ReducerPackDedup<Q>::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ MultipleWithPos* entry = mQueue.top();
+ entry->currentCoefficient(mRing, mLeadTerm.coeff);
+ while (true) {
+ // store the chained elements
+ MultipleWithPos* const chainBegin = entry->chain;
+ MultipleWithPos* const chainEnd = entry; // the list is circular
+ entry->chain = entry; // detach any chained elements
+
+ // handle the entry itself
+ mLeadTerm.monom.swap(entry->current);
+ ++entry->pos;
+ if (entry->pos == entry->end) {
+ mQueue.pop();
+ entry->destroy(mRing);
+ mPool.free(entry);
+ } else {
+ entry->computeCurrent(mRing);
+ // Inserted spans must be in descending order
+ ASSERT(mQueue.getConfiguration().ring().
+ monomialLT(entry->current, mLeadTerm.monom));
+ mQueue.decreaseTop(entry);
+ }
+
+ // handle any chained elements
+ MultipleWithPos* chain = chainBegin;
+ while (chain != chainEnd) {
+ ASSERT(chain != 0);
+ ASSERT(mRing.monomialEQ(chain->current, mLeadTerm.monom));
+
+ MultipleWithPos* const next = chain->chain;
+ chain->chain = chain; // detach from remaining chained elements
+
+ chain->addCurrentCoefficient(mRing, mLeadTerm.coeff);
+ ++chain->pos;
+ if (chain->pos == chain->end) {
+ chain->destroy(mRing);
+ mPool.free(chain);
+ } else {
+ chain->computeCurrent(mRing);
+ // Inserted spans must be in descending order
+ ASSERT(mQueue.getConfiguration().ring().
+ monomialLT(chain->current, mLeadTerm.monom));
+ mQueue.push(chain);
+ }
+ chain = next;
+ }
+
+ if (mQueue.empty())
+ break;
+
+ entry = mQueue.top();
+ if (!mRing.monomialEQ(entry->current, mLeadTerm.monom))
+ break;
+ entry->addCurrentCoefficient(mRing, mLeadTerm.coeff);
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+template<template<typename> class Q>
+void ReducerPackDedup<Q>::resetReducer()
+{
+ MonomialFree freeer(mRing);
+ // todo: uncomment when all queues have forAll and clear.
+ //mQueue.forAll(freeer);
+ // mQueue.clear();
+}
+
+template<template<typename> class Q>
+size_t ReducerPackDedup<Q>::getMemoryUse() const
+{
+ return mQueue.getMemoryUse() + mPool.getMemoryUse();
+}
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
+
diff --git a/src/mathicgb/SPairHandler.cpp b/src/mathicgb/SPairHandler.cpp
new file mode 100644
index 0000000..a5e7619
--- /dev/null
+++ b/src/mathicgb/SPairHandler.cpp
@@ -0,0 +1,624 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include <iostream>
+#include "SPairHandler.hpp"
+#include "GroebnerBasis.hpp"
+#include "MTArray.hpp"
+#include "FreeModuleOrder.hpp"
+#include "Reducer.hpp"
+#include <limits>
+#include <stdexcept>
+
+extern int tracingLevel;
+
+SPairHandler::SigPairTriangle::SigPairTriangle(const GroebnerBasis& basis, size_t queueType):
+ PairTriangle(basis.order(), basis.ring(), queueType),
+ mBasis(basis) {}
+
+bool SPairHandler::SigPairTriangle::calculateOrderBy(
+ size_t a,
+ size_t b,
+ monomial orderBy
+) const {
+ ASSERT(mBasis.ratioCompare(a, b) != EQ);
+ // ensure that ratio(a) > ratio(b)
+ if (mBasis.ratioCompare(a, b) == LT)
+ std::swap(a, b);
+ mBasis.ring().monomialFindSignature(
+ mBasis.getLeadMonomial(a),
+ mBasis.getLeadMonomial(b),
+ mBasis.getSignature(a),
+ orderBy);
+ return true;
+}
+
+SPairHandler::SPairHandler(
+ const PolyRing *R0,
+ FreeModuleOrder *F0,
+ const GroebnerBasis *GB0,
+ MonomialTableArray *Hsyz0,
+ Reducer* reducer,
+ bool postponeKoszuls,
+ bool useBaseDivisors,
+ bool useSingularCriterionEarly,
+ size_t queueType
+):
+ mTrackEssentialPair(false),
+ mEssentialFirst(static_cast<size_t>(-1)),
+ mEssentialSecond(static_cast<size_t>(-1)),
+ mEssentialSig(R0->allocMonomial()),
+ R(R0),
+ F(F0),
+ mUseSingularCriterionEarly(useSingularCriterionEarly),
+ mUseBaseDivisors(useBaseDivisors),
+ mUseHighBaseDivisors(useBaseDivisors),
+ Hsyz(Hsyz0),
+ GB(GB0),
+ mReducer(reducer),
+ mPostponeKoszuls(postponeKoszuls),
+ mTri(*GB0, queueType) {
+}
+
+SPairHandler::~SPairHandler()
+{
+ ASSERT(mUseBaseDivisors || mUseHighBaseDivisors || mKnownSyzygyTri.empty());
+ R->freeMonomial(mEssentialSig);
+}
+
+bool SPairHandler::hasEssentialPair() const {
+ ASSERT(mTrackEssentialPair);
+ bool value = (mEssentialFirst < GB->size());
+ ASSERT(!value || isEssential(mEssentialFirst, mEssentialSecond));
+ return value;
+}
+
+void SPairHandler::newSyzygy(const_monomial sig) {
+ ASSERT(Hsyz->member(sig));
+}
+
+bool SPairHandler::isEssential(size_t a, size_t b) const {
+ ASSERT(mTrackEssentialPair);
+ bool minA = GB->basis().leadMinimal(a);
+ bool minB = GB->basis().leadMinimal(b);
+ if (minA && minB)
+ return true;
+ if (!minA && !minB)
+ return false;
+ if (!minA)
+ std::swap(a, b);
+ ASSERT(GB->basis().leadMinimal(a));
+ ASSERT(!GB->basis().leadMinimal(b));
+ if (mDidReducingSPair[b])
+ return false;
+ return R->monomialIsDivisibleBy
+ (GB->getLeadMonomial(b), GB->getLeadMonomial(a));
+}
+
+void SPairHandler::setTrackEssentialPair(bool value) {
+ if (value == mTrackEssentialPair)
+ return;
+ mTrackEssentialPair = value;
+ if (mTrackEssentialPair) {
+ ASSERT(mEssentialPoly.get() == 0);
+ while (mDidReducingSPair.size() < GB->size())
+ mDidReducingSPair.push_back(false);
+ mEssentialFirst = 0;
+ mEssentialSecond = static_cast<size_t>(-1);
+ if (GB->size() > 0)
+ nextEssentialPair();
+ } else {
+ mEssentialPoly.reset(0);
+ mEssentialFirst = static_cast<size_t>(-1);
+ mEssentialSecond = static_cast<size_t>(-1);
+ }
+}
+
+void SPairHandler::nextEssentialPair() {
+ ASSERT(mTrackEssentialPair);
+ ASSERT(mEssentialFirst < GB->size());
+ ASSERT(mEssentialSecond == static_cast<size_t>(-1) ||
+ mEssentialSecond < mEssentialFirst);
+
+ if (mEssentialPoly.get() != 0)
+ mEssentialPoly.reset(0);
+
+ // If the queue is empty, we don't know what the next signature will be,
+ // so we don't know what has been shown to reduce to zero, which means
+ // we can't proceed. The queue can be empty without the computation
+ // being done if the last S-pair does not reduce to zero, and this
+ // method gets called between the last S-pair being popped and the
+ // new basis element being inserted.
+ ASSERT(!mTri.empty());
+ const_monomial currentSig = mTri.topOrderBy();
+ const size_t basisElementCount = GB->size();
+
+ size_t& first = mEssentialFirst;
+ size_t& second = mEssentialSecond;
+ bool firstIteration = true;
+ while (true) {
+ if (!mDidReducingSPair[first] &&
+ !firstIteration &&
+ !GB->basis().leadMinimal(first)) {
+ ASSERT(R->monomialIsDivisibleBy
+ (GB->getLeadMonomial(first), GB->getLeadMonomial(second)));
+ mDidReducingSPair[first] = true;
+ second = first; // move on to next first
+ } else {
+nextIteration:
+ firstIteration = false;
+ ++second;
+ }
+ // ** move on to next S-pair
+ // Setting mEssentialSecond to -1 instructs this method to search the
+ // S-pairs with the given mEssentialFirst starting at the first one.
+ if (first == second) {
+ ++first;
+ if (first == basisElementCount) {
+ second = static_cast<size_t>(-1); // start at zero next time
+ break; // no more S-pairs to check for now
+ }
+ second = 0;
+ }
+
+ // Let
+ // A = the set of basis elements with minimal lead term
+ // B = the set of basis elements with non-minimal lead term
+ // G = the union of A and B
+ // P' = the set of pairs (a,b) in AxB such that lead(a)|lead(b)
+ // P = The union of AxA and P'
+ // We say that an S-pair is essential if it is a member of P.
+
+ // Skip pair if not a member of P since we only need the S-polynomials of
+ // members of P to reduce to zero. We can skip all the other S-pairs in GxG
+ // due to Buchberger's second criterion (lcm criterion).
+
+ if (!GB->basis().leadMinimal(first)) {
+ if (mDidReducingSPair[first]) {
+ second = first - 1;
+ continue;
+ }
+ for (; second < first; ++second) {
+ if (GB->basis().leadMinimal(second) &&
+ R->monomialIsDivisibleBy
+ (GB->getLeadMonomial(first), GB->getLeadMonomial(second))) {
+ ASSERT(isEssential(first, second));
+ break;
+ }
+ ASSERT(!isEssential(first, second));
+ }
+ } else {
+ for (; second < first; ++second) {
+ if (GB->basis().leadMinimal(second))
+ break;
+ if (!mDidReducingSPair[second] &&
+ R->monomialIsDivisibleBy
+ (GB->getLeadMonomial(second), GB->getLeadMonomial(first))) {
+ ASSERT(isEssential(first, second));
+ break;
+ }
+ ASSERT(!isEssential(first, second));
+ }
+ }
+
+ if (second == first) {
+ --second; // as it is incremented at top of loop
+ goto nextIteration; // todo: we really need to improve the code structure here
+ }
+ ASSERT(isEssential(first, second));
+
+ ASSERT(mTrackEssentialPair);
+ bool minFirst = GB->basis().leadMinimal(first);
+ bool minSecond = GB->basis().leadMinimal(second);
+ if (minFirst) {
+ if (!minSecond) {
+ if (!R->monomialIsDivisibleBy
+ (GB->getLeadMonomial(second), GB->getLeadMonomial(first))) {
+ ASSERT(!isEssential(first, second));
+ continue;
+ }
+ }
+ } else {
+ if (!minSecond) {
+ ASSERT(!isEssential(first, second));
+ continue;
+ }
+ if (!R->monomialIsDivisibleBy
+ (GB->getLeadMonomial(first), GB->getLeadMonomial(second))) {
+ ASSERT(!isEssential(first, second));
+ continue;
+ }
+ }
+ ASSERT(isEssential(first, second));
+ //if (!isEssential(first, second))
+ // continue;
+
+ // Use buchberger's first criterion: skip S-pairs whose leading terms
+ // are relatively prime.
+ if (R->monomialRelativelyPrime(
+ GB->getLeadMonomial(first),
+ GB->getLeadMonomial(second)))
+ continue;
+
+ // Use Buchberger's second criterion (lcm criterion).
+ if (GB->basis().buchbergerLcmCriterion(first, second))
+ continue;
+
+ // Skip pair if signature less than or equal to current signature, as
+ // then the S-polynomial has been reduced to zero. Note that it is not
+ // relevant if the signature of the S-pair is an element of the initial
+ // syzygy submodule, as that only implies that the S-pair will reduce to
+ // once we get to its signature. It does not imply that the S-pair reduces
+ // to zero right now.
+ //
+ // Usually in the Signature Buchberger Algorithm, it is not OK for
+ // the signature of the two halves of the S-pair to be equal.
+ // So those S-pairs are usually discarded. We can't do that here.
+ size_t greater = first;
+ size_t smaller = second;
+ if (GB->ratioCompare(greater, smaller) == LT)
+ std::swap(greater, smaller);
+ R->monomialFindSignature(
+ GB->getLeadMonomial(greater),
+ GB->getLeadMonomial(smaller),
+ GB->getSignature(greater),
+ mEssentialSig); // Set mEssentialSig to signature of S-pair
+
+ if (F->signatureCompare(mEssentialSig, currentSig) == LT)
+ continue;
+
+ mEssentialPoly = mReducer->classicReduceSPoly
+ (GB->poly(first), GB->poly(second), GB->basis());
+ ASSERT(mEssentialPoly.get() != 0);
+ if (mEssentialPoly->isZero()) {
+ mEssentialPoly.reset(0);
+ continue;
+ }
+
+ // (first, second) is an essential pair that we cannot prove reduces
+ // to zero.
+ break;
+ }
+}
+
+SPairHandler::Stats SPairHandler::getStats() const
+{
+ F->getStats(mStats.comparisons, mStats.precomparisons);
+ return mStats;
+}
+
+monomial SPairHandler::popSignature(PairContainer& pairs) {
+ ASSERT(!empty());
+
+ monomial sig = R->allocMonomial();
+ R->monomialCopy(mTri.topOrderBy(), sig);
+ R->setHashOnly(sig); // mTri returns monomials without the hash value set, so we set it here
+
+ pairs.clear();
+ do { // pop top as long as the top S-pair has signature sig
+ // loop invariant: topGroup is the top of the queue and has signature sig
+
+ { // push the current top S-pair onto pairs
+ std::pair<size_t, size_t> p = mTri.topPair();
+ size_t greater = p.first;
+ size_t smaller = p.second;
+ ASSERT(GB->ratioCompare(greater, smaller) != EQ);
+ if (GB->ratioCompare(greater, smaller) == LT)
+ std::swap(greater, smaller);
+ // now, greater in sense of signature in S-pair or, equivalently,
+ // in sense of sig/lead ratio.
+ ++mStats.spairsFinal;
+ pairs.push_back(std::make_pair(greater, smaller));
+ }
+ mTri.pop();
+ ++mStats.duplicateSignatures;
+ } while (!mTri.empty() && R->monomialEQ(mTri.topOrderBy(), sig));
+
+ --mStats.duplicateSignatures; // We added one too many in this loop
+ return sig;
+}
+
+void SPairHandler::newPairs(size_t newGen)
+{
+ mTri.beginColumn();
+ makePreSPairs(newGen);
+ mTri.endColumn();
+
+ if (mTrackEssentialPair) {
+ ASSERT(mDidReducingSPair.size() == newGen);
+ mDidReducingSPair.push_back(false);
+ if (!mTri.empty()) {
+ ASSERT(mEssentialFirst < newGen ||
+ mEssentialSecond == static_cast<size_t>(-1));
+ if (mEssentialPoly.get() == 0 ||
+ R->monomialIsDivisibleBy
+ (mEssentialPoly->getLeadMonomial(), GB->getLeadMonomial(newGen))) {
+ if (mEssentialFirst < newGen)
+ --mEssentialSecond;
+ nextEssentialPair();
+ }
+#ifdef DEBUG
+ else {
+ ASSERT(GB->basis().divisor(mEssentialPoly->getLeadMonomial()) ==
+ static_cast<size_t>(-1));
+ }
+#endif
+ }
+ }
+
+ ASSERT((!mUseBaseDivisors && !mUseHighBaseDivisors) ||
+ mKnownSyzygyTri.columnCount() == newGen + 1);
+}
+
+void SPairHandler::setupBaseDivisors(
+ BaseDivisor& divisor1,
+ BaseDivisor& divisor2,
+ size_t& highDivisorCmp,
+ size_t newGenerator
+) {
+ BaseDivContainer divisors;
+ const size_t MaxBaseDivisors = 2;
+ if (mUseBaseDivisors || mUseHighBaseDivisors)
+ divisors.reserve(MaxBaseDivisors + 1);
+
+ ASSERT(mUseBaseDivisors || mUseHighBaseDivisors);
+ ASSERT(mKnownSyzygyTri.columnCount() == newGenerator);
+ mKnownSyzygyTri.addColumn();
+
+ if (mUseHighBaseDivisors) {
+ size_t highDivisor = GB->highBaseDivisor(newGenerator);
+ if (highDivisor != static_cast<size_t>(-1)) {
+ // To use a high divisor, the ratio of the other generator has to be
+ // greater than both the ratio of newGenerator and of the high ratio
+ // divisor. We can check both at once by letting highDivisorCmp
+ // be the one out of newGenerator and highDivisor that has the
+ // highest ratio.
+ if (GB->ratioCompare(newGenerator, highDivisor) == GT)
+ highDivisorCmp = newGenerator;
+ else
+ highDivisorCmp = highDivisor;
+ }
+ } else
+ highDivisorCmp = static_cast<size_t>(-1);
+ if (!mUseBaseDivisors)
+ return;
+
+ std::vector<size_t> divs;
+ GB->lowBaseDivisors(divs, MaxBaseDivisors, newGenerator);
+ ASSERT(divs.size() <= MaxBaseDivisors);
+
+ divisors.resize(divs.size());
+ for (size_t i = 0; i < divisors.size(); ++i) {
+ BaseDivisor& bd = divisors[i];
+ bd.baseDivisor = divs[i];
+
+ // Only use the base divisor technique for generators with ratio
+ // less than both N and baseDivisor. baseDivisorCmp is the
+ // smallest one of these, so it can be used for this comparison.
+ if (GB->ratioCompare(newGenerator, bd.baseDivisor) == LT)
+ bd.ratioLessThan = newGenerator;
+ else
+ bd.ratioLessThan = bd.baseDivisor;
+
+ // Construct a monomial in makeSPair_t2 that can be used
+ // to eliminate s-pairs quickly based on the s-pairs already
+ // eliminated for baseDivisor.
+ const_monomial newSig = GB->getSignature(newGenerator);
+ const_monomial newLead = GB->getLeadMonomial(newGenerator);
+ const_monomial baseDivSig = GB->getSignature(bd.baseDivisor);
+ const_monomial baseDivLead = GB->getLeadMonomial(bd.baseDivisor);
+ bd.baseMonomial = R->allocMonomial();
+ R->mysteriousSPairMonomialRoutine(newSig, newLead, baseDivSig, baseDivLead, bd.baseMonomial);
+ }
+
+ divisor1.baseDivisor = static_cast<size_t>(-1);
+ divisor2.baseDivisor = static_cast<size_t>(-1);
+ if (divisors.size() >= 1)
+ divisor1 = divisors.front();
+ if (divisors.size() == 2) {
+ divisor2 = divisors.back();
+ ASSERT(GB->ratioCompare
+ (divisor1.ratioLessThan, divisor2.ratioLessThan) != LT);
+ }
+}
+
+void SPairHandler::makePreSPairs(size_t newGen)
+{
+ ASSERT(newGen < GB->size());
+ mStats.spairsConstructed += newGen;
+
+ monomial baseDivisorMonomial = 0;
+
+ BaseDivisor divisor1;
+ BaseDivisor divisor2;
+ divisor1.baseDivisor = static_cast<size_t>(-1);
+ divisor2.baseDivisor = static_cast<size_t>(-1);
+ size_t highDivisorCmp = static_cast<size_t>(-1);
+ if (mUseBaseDivisors || mUseHighBaseDivisors)
+ setupBaseDivisors(divisor1, divisor2, highDivisorCmp, newGen);
+
+ monomial hsyz = 0;
+ if (!mPostponeKoszuls)
+ hsyz = R->allocMonomial();
+
+ const_monomial newSig = GB->getSignature(newGen);
+ const_monomial newLead = GB->getLeadMonomial(newGen);
+ monomial pairSig = R->allocMonomial();
+
+ if (mUseHighBaseDivisors && divisor1.baseDivisor != static_cast<size_t>(-1))
+ ++mStats.hasLowBaseDivisor;
+ if (mUseHighBaseDivisors && highDivisorCmp != static_cast<size_t>(-1))
+ ++mStats.hasHighBaseDivisor;
+
+ PreSPair result;
+ for (size_t oldGen = 0; oldGen < newGen; oldGen++) {
+ const_monomial oldSig = GB->getSignature(oldGen);
+ const_monomial oldLead = GB->getLeadMonomial(oldGen);
+
+ // Check whether this is a non-regular spair.
+ // 'cmp' is used below too.
+ const int cmp = GB->ratioCompare(newGen, oldGen);
+ if (cmp == EQ) {
+ ++mStats.nonregularSPairs;
+ continue;
+ }
+
+ // check high ratio divisor
+ if (mUseHighBaseDivisors &&
+ highDivisorCmp != static_cast<size_t>(-1) &&
+ GB->ratioCompare(oldGen, highDivisorCmp) == GT &&
+ mKnownSyzygyTri.bitUnordered(oldGen, highDivisorCmp)) {
+ ASSERT(oldGen != highDivisorCmp); // otherwise ratios should be equal
+ mKnownSyzygyTri.setBit(newGen, oldGen, true);
+ ++mStats.highBaseDivisorHits;
+ // if DEBUG defined, get to the ASSERT below stating
+ // that this is really a syzygy
+#ifndef DEBUG
+ continue;
+#endif
+ }
+
+ // check low ratio divisors
+ if (mUseBaseDivisors &&
+ divisor1.baseDivisor != static_cast<size_t>(-1) &&
+ GB->ratioCompare(oldGen, divisor1.ratioLessThan) == LT) {
+ // if no divisor1, also no divisor 2 and also
+ // divisor1 has larger ratio, so skip both checks if divisor1 fails due
+ // to the ratio being too small or because there is no divisor1.
+
+ if (
+ (divisor1.baseDivisor != oldGen && // if divisor1 is a hit
+ mKnownSyzygyTri.bitUnordered(divisor1.baseDivisor, oldGen) &&
+ R->monomialIsDivisibleBy(divisor1.baseMonomial, oldLead))
+ || // or if divisor2 is a hit
+ (divisor2.baseDivisor != static_cast<size_t>(-1) &&
+ GB->ratioCompare(oldGen, divisor2.ratioLessThan) == LT &&
+ divisor2.baseDivisor != oldGen &&
+ mKnownSyzygyTri.bitUnordered(divisor2.baseDivisor, oldGen) &&
+ R->monomialIsDivisibleBy(divisor2.baseMonomial, oldLead))
+ ) {
+ mKnownSyzygyTri.setBit(newGen, oldGen, true);
+ ++mStats.lowBaseDivisorHits;
+ // if DEBUG defined, get to the ASSERT below stating
+ // that this really is a syzygy.
+#ifndef DEBUG
+ continue;
+#endif
+ }
+ }
+
+ if (cmp == GT)
+ R->monomialFindSignature(newLead, oldLead, newSig, pairSig);
+ else {
+ ASSERT(cmp == LT);
+ R->monomialFindSignature(oldLead, newLead, oldSig, pairSig);
+ }
+
+ size_t result_ignored;
+ if (Hsyz->member(pairSig, result_ignored)) {
+ ++mStats.syzygyModuleHits;
+#ifdef DEBUG
+ // Check if actually already elim. by low/high base divisor.
+ // Only check in DEBUG mode as otherwise we would have taken an early
+ // exit before getting here.
+ if ((mUseBaseDivisors || mUseHighBaseDivisors) &&
+ mKnownSyzygyTri.bit(newGen, oldGen))
+ --mStats.syzygyModuleHits;
+#endif
+ if (mUseBaseDivisors || mUseHighBaseDivisors)
+ mKnownSyzygyTri.setBit(newGen, oldGen, true);
+ continue;
+ }
+ ASSERT((!mUseBaseDivisors && !mUseHighBaseDivisors)
+ || !mKnownSyzygyTri.bit(newGen, oldGen));
+
+ if (!mPostponeKoszuls) {
+ // add koszul syzygy to Hsyz.
+ ASSERT(cmp == GT || cmp == LT);
+ if (cmp == GT)
+ R->monomialMult(newSig, oldLead, hsyz);
+ else
+ R->monomialMult(oldSig, newLead, hsyz);
+ if (Hsyz->insert(hsyz, 0))
+ hsyz = R->allocMonomial();
+ if (R->monomialRelativelyPrime(newLead, oldLead))
+ {
+ ++mStats.earlyRelativelyPrimePairs;
+ continue;
+ }
+ }
+
+ if (mUseSingularCriterionEarly) {
+ ASSERT(cmp == GT || cmp == LT);
+ size_t const givesSig = (cmp == GT ? newGen : oldGen);
+ if (GB->ratioCompare(GB->minimalLeadInSig(pairSig), givesSig) == GT &&
+ !R->monomialRelativelyPrime(newLead, oldLead)) {
+
+ ++mStats.earlySingularCriterionPairs;
+ continue;
+ }
+ }
+
+ // construct the PreSPair
+ result.signature = pairSig;
+ pairSig = R->allocMonomial();
+ result.i = static_cast<BigIndex>(oldGen);
+ mTri.addPair(result.i, result.signature);
+ ++mStats.queuedPairs;
+ //pairs.push_back(result);
+ }
+ R->freeMonomial(pairSig);
+ if (mUseBaseDivisors && ! baseDivisorMonomial.isNull())
+ R->freeMonomial(baseDivisorMonomial);
+ if (!mPostponeKoszuls)
+ R->freeMonomial(hsyz);
+}
+
+void SPairHandler::setKnownSyzygies(std::vector<std::pair<size_t, size_t> >& pairs) {
+ if (!mUseBaseDivisors && !mUseHighBaseDivisors)
+ return;
+ for (size_t i = 0; i < pairs.size(); ++i)
+ setKnownSyzygy(pairs[i].first, pairs[i].second);
+}
+
+void SPairHandler::setKnownSyzygy(size_t gen1, size_t gen2) {
+ ASSERT(gen1 < GB->size());
+ ASSERT(gen2 < GB->size());
+ ASSERT(gen1 != gen2);
+ if (mUseBaseDivisors || mUseHighBaseDivisors)
+ mKnownSyzygyTri.setBitUnordered(gen1, gen2, true);
+}
+
+std::string SPairHandler::name() {
+ return mTri.name();
+}
+
+void SPairHandler::write(std::ostream &out) const
+{
+ out << "-- spairs --" << std::endl;
+ // We write out all the pairs, one per line, in the form: [i j signature]
+ // We do each element of the heap:
+ for (size_t i = 0; i<heap.size(); i++)
+ heap[i]->write(R, out);
+}
+void SPairHandler::dump() const
+{
+ write(std::cerr);
+}
+
+size_t SPairHandler::getMemoryUse() const
+{
+ return
+ mTri.getMemoryUse() +
+ heap.capacity() * sizeof(heap.front()) +
+ getKnownSyzygyBitsMemoryUse();
+}
+
+size_t SPairHandler::getKnownSyzygyBitsMemoryUse() const {
+ return mKnownSyzygyTri.getMemoryUse();
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/SPairHandler.hpp b/src/mathicgb/SPairHandler.hpp
new file mode 100644
index 0000000..c29bbbe
--- /dev/null
+++ b/src/mathicgb/SPairHandler.hpp
@@ -0,0 +1,200 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _spair_handler_h_
+#define _spair_handler_h_
+
+// This class is designed for the Gao GB algorithm, or other signature based methods.
+// The idea is to keep the size of the spair structures as small as possible
+
+// Externally, an spair is (signature, integer).
+
+#include "PairTriangle.hpp"
+#include <vector>
+#include "PolyRing.hpp"
+#include "KoszulQueue.hpp"
+#include "SPairQueue.hpp"
+#include "BitTriangle.hpp"
+#include <memtailor.h>
+
+class Poly;
+class MonomialTableArray;
+class GroebnerBasis;
+class FreeModuleOrder;
+class Reducer;
+
+
+class SPairHandler
+{
+public:
+ SPairHandler(
+ const PolyRing *R0,
+ FreeModuleOrder *F0,
+ const GroebnerBasis *GB0,
+ MonomialTableArray *Hsyz0,
+ Reducer* reducer,
+ bool postponeKoszuls,
+ bool useBaseDivisors,
+ bool useSingularCriterionEarly,
+ size_t queueType);
+ ~SPairHandler();
+
+ bool empty() const {return mTri.empty();}
+ typedef std::vector<std::pair<size_t, size_t> > PairContainer;
+ monomial popSignature(PairContainer& pairs);
+
+ void newPairs(size_t i);
+ // fills in the next SPairGroup
+ // loops through 0..i-1 making each pair
+ // do the following for each new pair:
+ // a. create it (meaning, find gcd, and two multipliers, and two signatures
+ // b. determine which one is larger, discard the other
+ // c. check if that signature is in the Hsyz module.
+ // if so: throw it out
+ // if not: insert it into the current vector
+ // compute lead term of the corresponding Koszul syz
+ // if that is not in Hsyz, then insert it.
+ // d. sort the vector of spairs
+ // e. insert the first one onto the spair heap
+
+ // debug display
+ void dump() const;
+ void write(std::ostream &out) const;
+
+ // Set to true to enable hasEssentialPair().
+ void setTrackEssentialPair(bool value);
+
+ // Returns true if any essential pairs are yet to be popped. Requires
+ // tracking of essential pairs to be turned on.
+ bool hasEssentialPair() const;
+
+ // Inform the S-pair handler that there is a new syzygy signature in play.
+ void newSyzygy(const_monomial sig);
+
+ struct Stats {
+ size_t comparisons; // comparisons not in construction (?)
+ size_t precomparisons; // comparisons in spair construction (?)
+ unsigned long long spairsConstructed; // all spairs
+ unsigned long long spairsFinal; // spairs given to client
+ unsigned long long nonregularSPairs; // spairs eliminated by being non-regular
+ unsigned long long highBaseDivisorHits; // spairs eliminated by high base divisor
+ unsigned long long lowBaseDivisorHits; // spairs eliminated by low base divisor
+ unsigned long long hasHighBaseDivisor; // generators that have a high base divisor
+ unsigned long long hasLowBaseDivisor; // generators that have a low base divisor
+ unsigned long long syzygyModuleHits; // spairs eliminated by syzygy module
+ unsigned long long earlyRelativelyPrimePairs;
+ unsigned long long earlySingularCriterionPairs;
+ unsigned long long queuedPairs; // number actually placed on spair triangle
+ unsigned long long duplicateSignatures; // number of spairs removed due to duplicate signature
+
+ Stats():
+ comparisons(0),
+ precomparisons(0),
+ spairsConstructed(0),
+ spairsFinal(0),
+ nonregularSPairs(0),
+ highBaseDivisorHits(0),
+ lowBaseDivisorHits(0),
+ hasHighBaseDivisor(0),
+ hasLowBaseDivisor(0),
+ syzygyModuleHits(0),
+ earlyRelativelyPrimePairs(0),
+ earlySingularCriterionPairs(0),
+ queuedPairs(0),
+ duplicateSignatures(0) {}
+ };
+ Stats getStats() const;
+
+ size_t size() const {return mTri.size();}
+
+ size_t getMemoryUse() const;
+ size_t getKnownSyzygyBitsMemoryUse() const;
+
+ // Informs the s-pair handler that the syzygy between gen1 and gen2
+ // is a known syzygy.
+ void setKnownSyzygy(size_t gen1, size_t gen2);
+ void setKnownSyzygies(std::vector<std::pair<size_t, size_t> >& pairs);
+
+ std::string name();
+
+private:
+ void nextEssentialPair();
+ bool isEssential(size_t a, size_t b) const;
+
+ // If there is no essential pair, then essentialFirst == GB->size().
+ // Otherwise (a,b)=(essentialFirst, essentialSecond) is an essential
+ // S-pair such that a > b. Among all essential S-pairs, a is minimal
+ // and b is maximal.
+ bool mTrackEssentialPair; // whether to keep track of an essential pair
+ size_t mEssentialFirst; // first generator of the essential pair
+ size_t mEssentialSecond; // second generator of the essential pair
+ monomial mEssentialSig; // signature of the essential pair
+ // classic reduced S-pair of the essential pair
+ std::auto_ptr<Poly> mEssentialPoly;
+
+ // Value at be is true if we have reduced an S-pair (be,div) where
+ // div is the index of a basis element whose lead term divides the
+ // lead term of div. Only used if mTrackEssentialPair is true.
+ std::vector<char> mDidReducingSPair;
+
+ bool increment(SPairGroup* p);
+
+ void makePreSPairs(size_t newGen);
+
+ struct BaseDivisor { // a low ratio base divisor
+ size_t baseDivisor; // the index of the generator that is the base divisor
+ size_t ratioLessThan; // consider generators with ratio less than this
+ monomial baseMonomial; // the monomial that has to divide to get a hit
+ };
+ typedef std::vector<BaseDivisor> BaseDivContainer;
+ void setupBaseDivisors(
+ BaseDivisor& divisor1,
+ BaseDivisor& divisor2,
+ size_t& highDivisorCmp,
+ size_t newGenerator);
+
+ const PolyRing *R;
+
+ FreeModuleOrder *F;
+
+ std::vector<SPairGroup *> heap; // the actual heap
+
+ // if true, apply the early singular criterion
+ bool const mUseSingularCriterionEarly;
+
+ // true if low ratio base divisors are used to speed up S-pair elimination.
+ const bool mUseBaseDivisors;
+
+ // True if high ratio base divisors are used to speed up S-pair elimination.
+ // The syzygy should have already been inserted into the syzygy module.
+ const bool mUseHighBaseDivisors;
+
+ // one entry for every s-pair, which is set to true if the
+ // s-pair is known to be a syzygy. Only used if
+ // mUseBaseDivisors is true.
+ BitTriangle mKnownSyzygyTri;
+
+ // From elsewhere
+ MonomialTableArray *Hsyz; // we often modify this
+ const GroebnerBasis *GB;
+ Reducer* mReducer;
+ const bool mPostponeKoszuls;
+
+ class SigPairTriangle : public PairTriangle {
+ public:
+ SigPairTriangle(const GroebnerBasis& basis, size_t queueType);
+ protected:
+ virtual bool calculateOrderBy(size_t a, size_t b, monomial orderBy) const;
+ private:
+ const GroebnerBasis& mBasis;
+ };
+ SigPairTriangle mTri;
+
+ mutable Stats mStats;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/SPairQueue.cpp b/src/mathicgb/SPairQueue.cpp
new file mode 100644
index 0000000..13b13c0
--- /dev/null
+++ b/src/mathicgb/SPairQueue.cpp
@@ -0,0 +1,4 @@
+#include "stdinc.h"
+#include "SPairQueue.hpp"
+
+#include <mathic.h>
diff --git a/src/mathicgb/SPairQueue.hpp b/src/mathicgb/SPairQueue.hpp
new file mode 100644
index 0000000..91abb47
--- /dev/null
+++ b/src/mathicgb/SPairQueue.hpp
@@ -0,0 +1,24 @@
+#ifndef _sPairQueue_h_
+#define _sPairQueue_h_
+
+#include <ostream>
+class SPairGroup;
+
+// Get concrete instances through a module order.
+class SPairQueue {
+public:
+ typedef SPairGroup* Entry;
+
+ virtual bool empty() const = 0;
+ virtual void push(const Entry& entry) = 0;
+ virtual Entry pop() = 0;
+ virtual Entry top() const = 0;
+ virtual void decreaseTop(const Entry& newValue) = 0;
+
+ virtual void print(std::ostream& out) const = 0;
+ virtual std::string getName() const = 0;
+ virtual size_t getMemoryUse() const = 0;
+ virtual size_t sumOfSizes() const = 0;
+};
+
+#endif
diff --git a/src/mathicgb/SPairs.cpp b/src/mathicgb/SPairs.cpp
new file mode 100644
index 0000000..ece1865
--- /dev/null
+++ b/src/mathicgb/SPairs.cpp
@@ -0,0 +1,546 @@
+#include "stdinc.h"
+#include "SPairs.hpp"
+
+#include "GroebnerBasis.hpp"
+
+#include <iostream>
+
+SPairs::SPairs(const PolyBasis& basis, size_t queueType):
+ mTri(basis, queueType),
+ mBasis(basis),
+ mRing(basis.ring()) {}
+
+std::pair<size_t, size_t> SPairs::pop() {
+ // Must call addPairs for new elements before popping.
+ ASSERT(mEliminated.columnCount() == mBasis.size());
+
+ while (!mTri.empty()) {
+ std::pair<size_t, size_t> p;
+ p = mTri.topPair();
+ if (mBasis.retired(p.first) || mBasis.retired(p.second)) {
+ mTri.pop();
+ continue;
+ }
+ const_monomial lcm = mTri.topOrderBy();
+ ASSERT(mRing.monomialIsLeastCommonMultiple
+ (mBasis.leadMonomial(p.first),
+ mBasis.leadMonomial(p.second), lcm));
+ // Can't pop before done with lcm as popping overwrites lcm.
+ if (!advancedBuchbergerLcmCriterion(p.first, p.second, lcm)) {
+ mTri.pop();
+ mEliminated.setBit(p.first, p.second, true);
+ return p;
+ }
+ mTri.pop();
+ }
+ return std::make_pair(static_cast<size_t>(-1), static_cast<size_t>(-1));
+}
+
+size_t SPairs::size() const {
+ return mTri.size();
+}
+
+namespace {
+ // Records multiples of a basis element.
+ // Used in addPairs().
+ class RecordIndexes : public DivisorLookup::EntryOutput {
+ public:
+ RecordIndexes(
+ size_t newGen,
+ BitTriangle& eliminated,
+ std::vector<size_t>& indexes
+ ):
+ mNewGen(newGen),
+ mEliminated(eliminated),
+ mIndexes(indexes) {
+ mIndexes.clear();
+ }
+ virtual bool proceed(size_t index) {
+ if (index == mNewGen)
+ return true;
+ mIndexes.push_back(index);
+
+ // The S-pair (newGen, *it) corresponds to reducing the non-minimal
+ // basis element *it. The S-polynomial corresponds to the first
+ // step of that reduction. We tell the caller to reduce *it, so we
+ // get to assume that this S-pair can be eliminated. This is important
+ // because it sometimes allows us to eliminate an S-pair (newGen, x)
+ // when (*it, x) has already been eliminated. We want to make use of
+ // this opportunity before removing all the information about *it.
+ mEliminated.setBit(mNewGen, index, true);
+ return true;
+ }
+ private:
+ size_t const mNewGen;
+ BitTriangle& mEliminated;
+ std::vector<size_t>& mIndexes;
+ };
+}
+
+void SPairs::addPairsAssumeAutoReduce(
+ size_t newGen,
+ std::vector<size_t>& toRetireAndReduce
+) {
+ ASSERT(mTri.columnCount() == newGen);
+
+ ASSERT(newGen < mBasis.size());
+ ASSERT(!mBasis.retired(newGen));
+
+ while (mEliminated.columnCount() < mBasis.size()) {
+ if (mUseBuchbergerLcmHitCache) {
+ ASSERT(mEliminated.columnCount() == mBuchbergerLcmHitCache.size());
+ mBuchbergerLcmHitCache.push_back(0);
+ }
+ mEliminated.addColumn();
+ }
+
+ RecordIndexes indexes(newGen, mEliminated, toRetireAndReduce);
+ mBasis.divisorLookup().multiples(mBasis.leadMonomial(newGen), indexes);
+ addPairs(newGen);
+}
+
+void SPairs::addPairs(size_t newGen) {
+ // Must call addPairs with newGen parameter in the sequence 0, 1, ...
+ // newGen could be implicitly picked up from mTri.columnCount(), but
+ // doing it this way ensures that what happens is what the client thinks
+ // is happening and offers an ASSERT to inform mistaken client code.
+ ASSERT(mTri.columnCount() == newGen);
+
+ ASSERT(newGen < mBasis.size());
+ ASSERT(!mBasis.retired(newGen));
+
+ while (mEliminated.columnCount() < mBasis.size()) {
+ if (mUseBuchbergerLcmHitCache) {
+ ASSERT(mEliminated.columnCount() == mBuchbergerLcmHitCache.size());
+ mBuchbergerLcmHitCache.push_back(0);
+ }
+ mEliminated.addColumn();
+ }
+
+ mTri.beginColumn();
+ const_monomial const newLead = mBasis.leadMonomial(newGen);
+ monomial lcm = mBasis.ring().allocMonomial();
+ for (size_t oldGen = 0; oldGen < newGen; ++oldGen) {
+ if (mBasis.retired(oldGen))
+ continue;
+ const_monomial const oldLead = mBasis.leadMonomial(oldGen);
+ if (mRing.monomialRelativelyPrime(newLead, oldLead)) {
+ ++mStats.relativelyPrimeHits;
+ mEliminated.setBit(newGen, oldGen, true);
+ continue;
+ }
+ mRing.monomialLeastCommonMultipleNoWeights(newLead, oldLead, lcm);
+ if (simpleBuchbergerLcmCriterion(newGen, oldGen, lcm)) {
+ mEliminated.setBit(newGen, oldGen, true);
+ continue;
+ }
+ mRing.setWeightsOnly(lcm);
+ mTri.addPair(oldGen, lcm);
+ lcm = mBasis.ring().allocMonomial();
+ }
+ mBasis.ring().freeMonomial(lcm);
+ mTri.endColumn();
+}
+
+size_t SPairs::getMemoryUse() const {
+ return mTri.getMemoryUse();
+}
+
+SPairs::ClassicPairTriangle::ClassicPairTriangle(const PolyBasis& basis, size_t queueType):
+ PairTriangle(basis.order(), basis.ring(), queueType),
+ mBasis(basis) {}
+
+bool SPairs::simpleBuchbergerLcmCriterion
+ (size_t a, size_t b, const_monomial lcmAB) const
+{
+ ASSERT(a < mBasis.size());
+ ASSERT(b < mBasis.size());
+ ASSERT(a != b);
+ ASSERT(!mBasis.retired(a));
+ ASSERT(!mBasis.retired(b));
+ ASSERT(mRing.monomialIsLeastCommonMultipleNoWeights
+ (mBasis.leadMonomial(a), mBasis.leadMonomial(b), lcmAB));
+ ASSERT(mEliminated.columnCount() == mBasis.size());
+
+ class Criterion : public DivisorLookup::EntryOutput {
+ public:
+ Criterion(size_t a, size_t b, const_monomial lcmAB, const SPairs& sPairs):
+ mA(a), mB(b),
+ mLcmAB(lcmAB),
+ mSPairs(sPairs),
+ mRing(sPairs.ring()),
+ mBasis(sPairs.basis()),
+ mHit(static_cast<size_t>(-1)),
+ mAlmostApplies(false) {}
+
+ virtual bool proceed(size_t index) {
+ ASSERT(index < mBasis.size());
+ ASSERT(!applies()); // should have stopped search in this case
+ ASSERT(mRing.monomialIsDivisibleBy(mLcmAB, mBasis.leadMonomial(index)));
+ if (!mBasis.leadMinimal(index))
+ return true;
+ if (index == mA || index == mB)
+ return true;
+ mAlmostApplies = true;
+
+ // check lcm(a,index) != lcm(a,b) <=>
+ // exists i such that max(a[i], c[i]) != max(a[i],b[i]) <=>
+ // exists i such that b[i] > a[i] && b[i] > c[i]
+ const_monomial leadA = mBasis.leadMonomial(mA);
+ const_monomial leadB = mBasis.leadMonomial(mB);
+ const_monomial leadC = mBasis.leadMonomial(index);
+ if (!mSPairs.eliminated(index, mA) &&
+ !mRing.monomialHasStrictlyLargerExponent(leadB, leadC, leadA))
+ return true;
+
+ // check lcm(b,index) != lcm(a,b)
+ if (!mSPairs.eliminated(index, mB) &&
+ !mRing.monomialHasStrictlyLargerExponent(leadA, leadC, leadB))
+ return true;
+
+ mHit = index;
+ return false; // stop search
+ }
+
+ const_monomial lcmAB() const {return mLcmAB;}
+ bool almostApplies() const {return mAlmostApplies;}
+ bool applies() const {return mHit != static_cast<size_t>(-1);}
+ size_t hit() const {return mHit;}
+
+ private:
+ size_t mA;
+ size_t mB;
+ const_monomial mLcmAB;
+ const SPairs& mSPairs;
+ const PolyRing& mRing;
+ const PolyBasis& mBasis;
+ size_t mHit; // the divisor that made the criterion apply
+ bool mAlmostApplies; // applies ignoring lcm(a,b)=lcm(a,c) complication
+ };
+
+ bool applies = false;
+ bool almostApplies = false;
+ {
+ Criterion criterion(a, b, lcmAB, *this);
+ if (mUseBuchbergerLcmHitCache) {
+ // Check cacheB first since when I tried this there was a higher hit rate
+ // for cacheB than cacheA. Might not be a persistent phenomenon, but
+ // there's no downside to trying out cacheB first so I'm going for that.
+ //
+ // I update the cache if the second check is a hit but not if the first
+ // check is a hit. In the one test I did, the worst hit rate was from
+ // updating the cache every time, the second best hit rate was from
+ // not updating the cache (from cache hits) and the best hit rate was
+ // from doing this.
+ //
+ // The idea is that when the first cache check is a hit,
+ // the second cache member might have been a hit too, and updating it
+ // might replace a high hit rate element with a low hit rate element,
+ // which would be bad. When the second cache check is a hit, we know
+ // that the first one wasn't (or we would have taken an early exit),
+ // so we have reason to suspect that the first cache element is not
+ // a high hit rate element. So it should be better to replace it.
+ // That idea seems to be right since it worked better in the one
+ // test I did.
+ size_t cacheB = mBuchbergerLcmHitCache[b];
+ if (!applies && !mBasis.retired(cacheB) &&
+ mRing.monomialIsDivisibleBy
+ (criterion.lcmAB(), mBasis.leadMonomial(cacheB)))
+ applies = !criterion.Criterion::proceed(cacheB);
+
+ size_t cacheA = mBuchbergerLcmHitCache[a];
+ if (!applies && !mBasis.retired(cacheA) &&
+ mRing.monomialIsDivisibleBy
+ (criterion.lcmAB(), mBasis.leadMonomial(cacheA))) {
+ applies = !criterion.Criterion::proceed(cacheA);
+ if (applies)
+ mBuchbergerLcmHitCache[b] = cacheA;
+ }
+ }
+ if (applies)
+ {
+ if (mStats.late)
+ ++mStats.buchbergerLcmCacheHitsLate;
+ else
+ ++mStats.buchbergerLcmCacheHits;
+ }
+ else {
+ ASSERT(!criterion.applies());
+ mBasis.divisorLookup().divisors(criterion.lcmAB(), criterion);
+ applies = criterion.applies();
+
+ if (mUseBuchbergerLcmHitCache && applies) {
+ ASSERT(criterion.hit() < mBasis.size());
+ mBuchbergerLcmHitCache[a] = criterion.hit();
+ mBuchbergerLcmHitCache[b] = criterion.hit();
+ }
+ }
+ if (!applies)
+ almostApplies = criterion.almostApplies();
+ }
+
+ if (applies)
+ {
+ if (mStats.late)
+ ++mStats.buchbergerLcmSimpleHitsLate;
+ else
+ ++mStats.buchbergerLcmSimpleHits;
+ }
+
+ ASSERT(applies == simpleBuchbergerLcmCriterionSlow(a, b));
+ return applies;
+}
+
+bool SPairs::simpleBuchbergerLcmCriterionSlow(size_t a, size_t b) const {
+ ASSERT(a < mBasis.size());
+ ASSERT(b < mBasis.size());
+ ASSERT(a != b);
+ ASSERT(!mBasis.retired(a));
+ ASSERT(!mBasis.retired(b));
+ ASSERT(mEliminated.columnCount() == mBasis.size());
+
+ // todo: use iterators
+ monomial lcmAB = mRing.allocMonomial();
+ monomial lcm = mRing.allocMonomial();
+ mRing.monomialLeastCommonMultiple
+ (mBasis.leadMonomial(a), mBasis.leadMonomial(b), lcmAB);
+ size_t stop = mBasis.size();
+ size_t i = 0;
+ for (; i < stop; ++i) {
+ if (mBasis.retired(i) || !mBasis.leadMinimal(i))
+ continue;
+ if (!mRing.monomialIsDivisibleBy(lcmAB, mBasis.leadMonomial(i)))
+ continue;
+ if (i == a || i == b)
+ continue;
+ if (!eliminated(i, a)) {
+ mRing.monomialLeastCommonMultiple
+ (mBasis.leadMonomial(a), mBasis.leadMonomial(i), lcm);
+ if (mRing.monomialEQ(lcmAB, lcm))
+ continue;
+ }
+
+ if (!eliminated(i, b)) {
+ mRing.monomialLeastCommonMultiple
+ (mBasis.leadMonomial(b), mBasis.leadMonomial(i), lcm);
+ if (mRing.monomialEQ(lcmAB, lcm))
+ continue;
+ }
+ break;
+ }
+ mRing.freeMonomial(lcmAB);
+ mRing.freeMonomial(lcm);
+ return i != stop;
+}
+
+bool SPairs::advancedBuchbergerLcmCriterion
+ (size_t a, size_t b, const_monomial lcmAB) const
+{
+ ASSERT(a != b);
+ ASSERT(mEliminated.columnCount() == mBasis.size());
+ ASSERT(mRing.monomialIsLeastCommonMultipleNoWeights
+ (mBasis.leadMonomial(a), mBasis.leadMonomial(b), lcmAB));
+
+ mStats.late = true;
+ if (simpleBuchbergerLcmCriterion(a, b, lcmAB)) {
+ mStats.late = false;
+ ASSERT(advancedBuchbergerLcmCriterionSlow(a, b));
+ return true;
+ }
+ mStats.late = false;
+
+ // *** Build the graph vertices
+ // graph contains pairs (index, state). index is the index of a basis
+ // that is a node in G. state indicates which of a and b that the node
+ // in question is so far known to be connected to.
+
+ typedef std::vector<std::pair<size_t, Connection> > Graph;
+ class GraphBuilder : public DivisorLookup::EntryOutput {
+ public:
+ GraphBuilder(Graph& graph): mGraph(graph) {graph.clear();}
+ virtual bool proceed(size_t index) {
+ mGraph.push_back(std::make_pair(index, NotConnected));
+ return true;
+ }
+ private:
+ Graph& mGraph;
+ };
+ Graph& graph = mAdvancedBuchbergerLcmCriterionGraph;
+ graph.clear();
+ GraphBuilder builder(graph);
+ mBasis.divisorLookup().divisors(lcmAB, builder);
+
+ if (graph.size() <= 3) {
+ // For the graph approach to be better than the simpler approach of
+ // considering triples, there has to be more than 3 nodes in the graph.
+ ASSERT(!advancedBuchbergerLcmCriterionSlow(a, b));
+ return false;
+ }
+
+ // *** Set up todo with a and b
+ // todo points to elements (nodes) of graph to process.
+ std::vector<Graph::iterator> todo;
+ Graph::iterator const graphEnd = graph.end();
+ for (Graph::iterator it = graph.begin(); it != graphEnd; ++it) {
+ if (it->first == a)
+ it->second = ConnectedA;
+ else if (it->first == b)
+ it->second = ConnectedB;
+ else
+ continue;
+ todo.push_back(it);
+ }
+
+ // *** Follow edges in the graph
+ // We stop as soon as we find a node that is connected to both a and b,
+ // since then a and b are connected so that the criterion applies.
+ bool applies = false;
+ while (!applies && !todo.empty()) {
+ size_t const currentIndex = todo.back()->first;
+ Connection const currentConnect = todo.back()->second;
+ ASSERT(currentConnect != NotConnected);
+ todo.pop_back();
+
+ // loop through all potential edges (currentIndex, otherIndex)
+ const_monomial const currentLead = mBasis.leadMonomial(currentIndex);
+ for (Graph::iterator other = graph.begin(); other != graphEnd; ++other) {
+ Connection const otherConnect = other->second;
+ if (currentConnect == otherConnect)
+ continue;
+ size_t const otherIndex = other->first;
+ ASSERT(otherIndex != currentIndex);
+
+ const_monomial const otherLead = mBasis.leadMonomial(otherIndex);
+ // Note that
+ // lcm(c,d) != lcmAB <=>
+ // exists i such that max(c[i], d[i]) < lcmAB[i] <=>
+ // exists i such that lcmAB[i] > c[i] && lcmAB[i] > d[i]
+ if (!eliminated(currentIndex, otherIndex) &&
+ !mRing.monomialHasStrictlyLargerExponent(lcmAB, currentLead, otherLead))
+ {
+ continue; // not an edge in G
+ }
+
+ if (otherConnect == NotConnected) {
+ other->second = currentConnect;
+ todo.push_back(other);
+ } else {
+ // At this point we have found an edge between a node connected to
+ // a and a node connected to b. So a and b are connected.
+ ASSERT(currentConnect != otherConnect);
+ applies = true;
+ break;
+ }
+ }
+ }
+
+ if (applies)
+ ++mStats.buchbergerLcmAdvancedHits;
+
+ // if (graph.size() >= 10)
+ // std::cout << "[adv size=" << graph.size() << " result= " << applies << std::endl;
+
+ ASSERT(applies == advancedBuchbergerLcmCriterionSlow(a, b));
+ return applies;
+}
+
+bool SPairs::advancedBuchbergerLcmCriterionSlow(size_t a, size_t b) const {
+ ASSERT(a != b);
+ ASSERT(mEliminated.columnCount() == mBasis.size());
+
+ monomial lcmAB = mRing.allocMonomial();
+ monomial lcm = mRing.allocMonomial();
+ mRing.monomialLeastCommonMultiple
+ (mBasis.leadMonomial(a), mBasis.leadMonomial(b), lcmAB);
+ size_t stop = mBasis.size();
+
+ // *** Build the graph vertices
+ // graph contains pairs (index, state). index is the index of a basis
+ // that is a node in G. state indicates which of a and b that the node
+ // in question is so far known to be connected to.
+ std::vector<std::pair<size_t, Connection> > graph;
+ std::vector<size_t> todo; // indexes into graph to process.
+ for (size_t i = 0; i != stop; ++i) {
+ if (mBasis.retired(i))
+ continue;
+ if (!mRing.monomialIsDivisibleBy(lcmAB, mBasis.leadMonomial(i)))
+ continue;
+ Connection con = NotConnected;
+ if (i == a) {
+ con = ConnectedA;
+ todo.push_back(graph.size());
+ } else if (i == b) {
+ con = ConnectedB;
+ todo.push_back(graph.size());
+ }
+ graph.push_back(std::make_pair(i, con));
+ }
+
+ // *** Follow edges in the graph
+ // We stop as soon as we find a node that is connected to both a and b,
+ // since then a and b are connected so that the criterion applies.
+ bool applies = false;
+ while (!applies && !todo.empty()) {
+ ASSERT(todo.size() <= graph.size());
+ std::pair<size_t, Connection> const node = graph[todo.back()];
+ todo.pop_back();
+ ASSERT(node.second != NotConnected);
+
+ // loop through all potential edges (node.first, i)
+ const_monomial leadNode = mBasis.leadMonomial(node.first);
+ for (size_t i = 0; i < graph.size(); ++i) {
+ if (node.second == graph[i].second)
+ continue;
+ ASSERT(graph[i].first != node.first);
+ size_t const other = graph[i].first;
+
+ const_monomial const leadOther = mBasis.leadMonomial(other);
+ mRing.monomialLeastCommonMultiple(leadNode, leadOther, lcm);
+ if (!eliminated(node.first, other) && mRing.monomialEQ(lcm, lcmAB))
+ continue; // not an edge in G
+
+ if (graph[i].second == NotConnected) {
+ graph[i].second = node.second;
+ todo.push_back(i);
+ } else {
+ // At this point we have found an edge between something a node to
+ // a and a node connected to b. So a and b are connected.
+ ASSERT(graph[i].second != node.second)
+ applies = true;
+ break;
+ }
+ }
+ }
+ mRing.freeMonomial(lcmAB);
+ mRing.freeMonomial(lcm);
+
+ ASSERT(applies || !simpleBuchbergerLcmCriterionSlow(a, b));
+ return applies;
+}
+
+SPairs::Stats SPairs::stats() const {
+ size_t const columnCount = mTri.columnCount();
+ mStats.sPairsConsidered = columnCount * (columnCount - 1) / 2;
+ return mStats;
+}
+
+std::string SPairs::name() const {
+ return mTri.name();
+}
+
+bool SPairs::ClassicPairTriangle::calculateOrderBy(
+ size_t a,
+ size_t b,
+ monomial orderBy
+) const {
+ ASSERT(!orderBy.isNull());
+ ASSERT(a != b);
+ ASSERT(a < mBasis.size());
+ ASSERT(b < mBasis.size());
+ if (mBasis.retired(a) || mBasis.retired(b))
+ return false;
+ const_monomial const leadA = mBasis.leadMonomial(a);
+ const_monomial const leadB = mBasis.leadMonomial(b);
+ mBasis.ring().monomialLeastCommonMultiple(leadA, leadB, orderBy);
+ return true;
+}
diff --git a/src/mathicgb/SPairs.hpp b/src/mathicgb/SPairs.hpp
new file mode 100644
index 0000000..62f906b
--- /dev/null
+++ b/src/mathicgb/SPairs.hpp
@@ -0,0 +1,156 @@
+#ifndef _s_pairs_h_
+#define _s_pairs_h_
+
+#include "PairTriangle.hpp"
+#include "BitTriangle.hpp"
+#include <utility>
+
+class PolyBasis;
+
+// Stores the set of pending S-pairs for use in the classic Buchberger
+// algorithm. Also eliminates useless S-pairs and orders the S-pairs.
+class SPairs {
+public:
+ SPairs(const PolyBasis& basis, size_t queueType);
+
+ // Returns the number of S-pairs in the data structure.
+ size_t size() const;
+
+ // Returns true if there all S-pairs have been eliminated or popped.
+ bool empty() const {return mTri.empty();}
+
+ // Removes the minimal S-pair from the data structure and return it.
+ // The S-polynomial of that pair is assumed to reduce to zero, either
+ // because it already does, or because it did not reduce to zero and then
+ // that caused the addition of another basis element. If this assumption
+ // is broken, S-pair elimination may give incorrect results.
+ //
+ // Returns the pair (invalid,invalid) if there are no S-pairs left to
+ // return, where invalid is static_cast<size_t>(-1). This can happen even
+ // if empty() returned false prior to calling pop(), since the S-pairs in
+ // the queue may have been found to be useless.
+ std::pair<size_t, size_t> pop();
+
+ // Add the pairs (index,a) to the data structure for those a such that
+ // a < index. Some of those pairs may be eliminated if they can be proven
+ // to be useless. index must be a valid index of a basis element
+ // and must only be called once per basis element and must be 1 more
+ // than the value of index for the previous call to addPairs, starting
+ // at zero for the first call.
+ void addPairs(size_t index);
+
+ // As addPairs, but assuming auto-reduction of the basis will happen.
+ // This method assumes that if lead(index) divides lead(x) for a basis
+ // element x, then x will be retired from the basis and reduced. toReduce
+ // will contain those indices x.
+ void addPairsAssumeAutoReduce(size_t index, std::vector<size_t>& toRetireAndReduce);
+
+ // Returns true if the S-pair (a,b) is known to be useless. Even if the
+ // S-pair is not useless now, it will become so later. At the latest, an
+ // S-pair becomes useless when its S-polynomial has been reduced to zero.
+ bool eliminated(size_t a, size_t b) const {
+ return mEliminated.bitUnordered(a, b);
+ }
+
+ const PolyRing& ring() const {return mRing;}
+ const PolyBasis& basis() const {return mBasis;}
+
+ size_t getMemoryUse() const;
+
+ struct Stats {
+ Stats():
+ sPairsConsidered(0),
+ relativelyPrimeHits(0),
+ buchbergerLcmSimpleHits(0),
+ buchbergerLcmAdvancedHits(0),
+ buchbergerLcmCacheHits(0),
+ late(false),
+ buchbergerLcmSimpleHitsLate(0),
+ buchbergerLcmCacheHitsLate(0)
+ {}
+
+ unsigned long long sPairsConsidered;
+ unsigned long long relativelyPrimeHits;
+ unsigned long long buchbergerLcmSimpleHits;
+ unsigned long long buchbergerLcmAdvancedHits;
+ unsigned long long buchbergerLcmCacheHits;
+ bool late; // if set to true then simpleBuchbergerLcmCriterion sets the following 2 instead:
+ unsigned long long buchbergerLcmSimpleHitsLate;
+ unsigned long long buchbergerLcmCacheHitsLate;
+ };
+ Stats stats() const;
+
+ std::string name() const;
+
+private:
+ // Returns true if Buchberger's second criterion for eliminating useless
+ // S-pairs applies to the pair (a,b). Let
+ // l(a,b) = lcm(lead(a), lead(b)).
+ // The criterion says that if there is some other basis element c such that
+ // lead(c)|l(a,b)
+ // and
+ // l(a,c) reduces to zero, and
+ // l(b,c) reduces to zero
+ // then (a,b) will reduce to zero (using classic non-signature reduction).
+ //
+ // This criterion is less straight forward to apply in case for example
+ // l(a,b) = l(a,c) = l(b,c)
+ // since then there is the potential to erroneously eliminate all the three
+ // pairs among a,b,c on the assumption that the other two pairs will reduce
+ // to zero. In such cases, we eliminate the pair with the lowest indexes.
+ // This allows removing generators that get non-minimal lead term without
+ // problems.
+ bool simpleBuchbergerLcmCriterion
+ (size_t a, size_t b, const_monomial lcmAB) const;
+
+ // As the non-slow version, but uses simpler and slower code.
+ bool simpleBuchbergerLcmCriterionSlow(size_t a, size_t b) const;
+
+ // Improves on Buchberger's second criterion by using connection in a graph
+ // to determine if an S-pair can be eliminated. This can eliminate some pairs
+ // that cannot be eliminated by looking at any one triple of generators.
+ //
+ // The algorithm is based on considering an undirected graph G.
+ // Each vertex of G represents a basis element whose lead monomial divides
+ // lcmAB. There is an edge (c,d) if lcm(c,d) != lcm(a,b) or if (c,d) has
+ // been eliminated. It is a theorem that if there is a path from a to b
+ // in G then (a,b) is a useless S-pair and so can be eliminated.
+ bool advancedBuchbergerLcmCriterion
+ (size_t a, size_t b, const_monomial lcmAB) const;
+
+ // As the non-slow version, but uses simpler and slower code.
+ bool advancedBuchbergerLcmCriterionSlow(size_t a, size_t b) const;
+
+ class ClassicPairTriangle : public PairTriangle {
+ public:
+ ClassicPairTriangle(const PolyBasis& basis, size_t queueType);
+ protected:
+ virtual bool calculateOrderBy(size_t a, size_t b, monomial orderBy) const;
+ private:
+ const PolyBasis& mBasis;
+ };
+ ClassicPairTriangle mTri;
+
+ // The bit at (i,j) is set to true if it is known that the S-pair between
+ // basis element i and j does not have to be reduced. This can be due to a
+ // useless S-pair criterion eliminating that pair, or it can be because the
+ // S-polynomial of that pair has already been reduced.
+ BitTriangle mEliminated;
+ const PolyBasis& mBasis;
+ const PolyRing& mRing;
+ mutable Stats mStats;
+
+ static const bool mUseBuchbergerLcmHitCache = true;
+ mutable std::vector<size_t> mBuchbergerLcmHitCache;
+
+ enum Connection { // used in advancedBuchbergerLcmCriterion().
+ NotConnected, // not known to be connected to a or b
+ ConnectedA, // connected to a
+ ConnectedB // connected to b
+ };
+ // Variable used only inside advancedBuchbergerLcmCriterion().
+ mutable std::vector<std::pair<size_t, Connection> >
+ mAdvancedBuchbergerLcmCriterionGraph;
+};
+
+#endif
diff --git a/src/mathicgb/SignatureGB.cpp b/src/mathicgb/SignatureGB.cpp
new file mode 100644
index 0000000..6e4282c
--- /dev/null
+++ b/src/mathicgb/SignatureGB.cpp
@@ -0,0 +1,764 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+
+#include <mathic.h>
+#include "SignatureGB.hpp"
+#include "FreeModuleOrder.hpp"
+#include "Ideal.hpp"
+#include "DivisorLookup.hpp"
+#include "SPairHandler.hpp"
+#include "PolyHeap.hpp"
+#include "MTArray.hpp"
+
+int tracingLevel = 0;
+
+void SignatureGB::computeGrobnerBasis()
+{
+ size_t counter = 0;
+
+ mTimer.reset();
+ R->resetCoefficientStats();
+ std::ostream& out = std::cout;
+
+ if (!mComputeSignatureBasis)
+ SP->setTrackEssentialPair(true);
+
+ while (true) {
+ if (SP->empty())
+ break;
+ if (!mComputeSignatureBasis && !SP->hasEssentialPair())
+ break;
+ step();
+
+ if (mBreakAfter > 0 && GB->size() > mBreakAfter) {
+ break;
+ const size_t pairs = SP->size();
+ size_t sigs = 0;
+ size_t syzygySigs = 0;
+ while (!SP->empty()) {
+ ++sigs;
+ size_t a;
+ monomial sig = SP->popSignature(mSpairTmp);
+ if (Hsyz->member(sig, a))
+ ++syzygySigs;
+ else
+ GB->minimalLeadInSig(sig);
+ R->freeMonomial(sig);
+ }
+ const double syzygyRatio = static_cast<double>(syzygySigs) / sigs;
+ std::cerr << "*** Early exit statistics ***\n"
+ << "remaining spairs: " << pairs << '\n'
+ << "remaining spair signatures: " << sigs << '\n'
+ << "spair signature syzygies: " << syzygySigs
+ << " (" << syzygyRatio * 100 << "% of sigs)\n";
+ break;
+ }
+ if (mPrintInterval == 0 || (++counter % mPrintInterval) != 0)
+ continue;
+ out << "\n-------------------------------------------------------------\n";
+ displayMemoryUse(out);
+ out << "\n";
+ displaySomeStats(out);
+ }
+ // exit(1);
+ /*displayMemoryUse(std::cout);
+ std::cout << "\n";
+ displaySomeStats(std::cout);*/
+
+ // displayMemoryUse(std::cout);
+ stats_nsecs = mTimer.getMilliseconds() / 1000.0;
+ //GB->displayBrief(out);
+}
+
+SignatureGB::SignatureGB(
+ const Ideal& ideal,
+ FreeModuleOrderType typ,
+ Reducer::ReducerType reductiontyp,
+ int divlookup_type,
+ int montable_type,
+ bool postponeKoszul,
+ bool useBaseDivisors,
+ bool preferSparseReducers,
+ bool useSingularCriterionEarly,
+ size_t queueType
+):
+ mBreakAfter(0),
+ mPrintInterval(0),
+ mComputeSignatureBasis(true),
+ R(ideal.getPolyRing()),
+ F(FreeModuleOrder::makeOrder(typ, &ideal)),
+ mPostponeKoszul(postponeKoszul),
+ mUseBaseDivisors(useBaseDivisors),
+ stats_sPairSignaturesDone(0),
+ stats_sPairsDone(0),
+ stats_koszulEliminated(0),
+ stats_SignatureCriterionLate(0),
+ stats_relativelyPrimeEliminated(0),
+ stats_pairsReduced(0),
+ stats_nsecs(0.0)
+{
+ GB = new GroebnerBasis(R, F, divlookup_type, montable_type, preferSparseReducers);
+ mKoszuls = new KoszulQueue(F, R->getMonomialPool());
+
+ const bool allowRemovals = !mPostponeKoszul;
+ Hsyz = MonomialTableArray::make(R, montable_type, ideal.size(), allowRemovals);
+
+ reducer = Reducer::makeReducer(reductiontyp, *R).release();
+ SP = new SPairHandler(R, F, GB, Hsyz, reducer, mPostponeKoszul, mUseBaseDivisors, useSingularCriterionEarly, queueType);
+
+ // Populate GB
+ for (size_t i = 0; i < ideal.size(); i++) {
+ Poly *g = new Poly(R);
+ ideal.getPoly(i)->copy(*g);
+ g->makeMonic();
+
+ monomial sig = 0;
+ sig = R->allocMonomial();
+ R->monomialEi(i, sig);
+ GB->addComponent();
+ std::auto_ptr<Poly> autoG(g);
+ GB->insert(sig, autoG);
+ }
+
+ // Populate SP
+ for (size_t i = 0; i < ideal.size(); i++)
+ SP->newPairs(i);
+
+ if (tracingLevel >= 2) {
+ SP->dump();
+ Hsyz->dump();
+ }
+}
+
+SignatureGB::~SignatureGB()
+{
+ delete reducer;
+ delete SP;
+ delete Hsyz;
+ delete mKoszuls;
+ delete GB;
+ delete F;
+}
+
+bool SignatureGB::processSPair
+ (monomial sig, const SPairHandler::PairContainer& pairs)
+{
+ ASSERT(!pairs.empty());
+
+ // the module term to reduce is multiple * GB->getSignature(gen)
+ const bool constrainTermChoice = false;
+ size_t gen = mSpairTmp.back().first;
+ if (!constrainTermChoice)
+ gen = GB->minimalLeadInSig(sig);
+ else {
+ for (size_t i = 0; i < pairs.size(); ++i)
+ if (GB->ratioCompare(pairs[i].first, gen) == GT)
+ gen = pairs[i].first;
+ }
+ ASSERT(gen != static_cast<size_t>(-1));
+ monomial multiple = R->allocMonomial();
+ R->monomialDivide(sig, GB->getSignature(gen), multiple);
+ GB->basis().usedAsStart(gen);
+
+ // reduce multiple * GB->getSignature(gen)
+ Poly* f = reducer->regularReduce(sig, multiple, gen, *GB);
+
+ R->freeMonomial(multiple);
+
+ if (constrainTermChoice && f != 0) {
+ if (GB->isSingularTopReducible(*f, sig)) {
+ delete f;
+ f = 0;
+ }
+ }
+
+ if (f == 0) { // singular reduction
+ ASSERT(f == 0);
+ if (tracingLevel >= 7)
+ std::cerr << "singular reduction" << std::endl;
+ R->freeMonomial(sig);
+ return true;
+ }
+ ASSERT(f != 0);
+
+ if (f->isZero()) { // reduction to zero
+ if (tracingLevel >= 7)
+ std::cerr << "zero reduction" << std::endl;
+ Hsyz->insert(sig, 0);
+ SP->newSyzygy(sig);
+ SP->setKnownSyzygies(mSpairTmp);
+ delete f;
+ return false;
+ }
+
+ // new basis element
+ ASSERT(!GB->isSingularTopReducible(*f, sig));
+ std::auto_ptr<Poly> autoF(f);
+ GB->insert(sig, autoF);
+ Hsyz->addComponent();
+ SP->newPairs(GB->size()-1);
+
+ if (tracingLevel >= 1) {
+ std::cerr << "New GB element." << std::endl;
+ size_t r = GB->size()-1;
+ std::cerr << "gb" << r << ": ";
+ R->monomialDisplay(std::cerr, GB->getSignature(r));
+ std::cerr << " ";
+ R->monomialDisplay(std::cerr, GB->poly(r).getLeadMonomial());
+ std::cerr << std::endl;
+ if (tracingLevel >= 7)
+ GB->dump();
+ }
+ return true;
+}
+
+void SignatureGB::step()
+{
+ ASSERT(!SP->empty());
+
+ monomial sig = SP->popSignature(mSpairTmp);
+ ++stats_sPairSignaturesDone;
+ stats_sPairsDone += mSpairTmp.size();
+
+ if (tracingLevel >= 3) {
+ std::cerr << "doing signature ";
+ R->monomialDisplay(std::cerr, sig);
+ std::cerr << std::endl;
+ }
+
+ size_t result_ignored = 0;
+ if (Hsyz->member(sig, result_ignored)) {
+ ++stats_SignatureCriterionLate;
+ SP->setKnownSyzygies(mSpairTmp);
+ if (tracingLevel >= 3)
+ std::cerr << "eliminated as in syzygy module" << std::endl;
+ R->freeMonomial(sig);
+ return;
+ }
+
+ // Not a known syzygy
+
+ while (!mKoszuls->empty()
+ && F->signatureCompare(mKoszuls->top(), sig) == LT)
+ {
+ mKoszuls->pop();
+ }
+
+ if (!mKoszuls->empty() && R->monomialEQ(mKoszuls->top(), sig))
+ {
+ ++stats_koszulEliminated;
+ // This signature is of a syzygy that is not in Hsyz, so add it
+ Hsyz->insert(sig, 0);
+ SP->newSyzygy(sig);
+ SP->setKnownSyzygies(mSpairTmp);
+ return;
+ }
+
+ typedef std::vector<std::pair<size_t, size_t> >::const_iterator iter;
+ if (mPostponeKoszul)
+ {
+ // Relatively prime check
+ for (iter it = mSpairTmp.begin(); it != mSpairTmp.end(); ++it)
+ {
+ const_monomial a = GB->getLeadMonomial(it->first);
+ const_monomial b = GB->getLeadMonomial(it->second);
+ if (R->monomialRelativelyPrime(a, b))
+ {
+ int not_used = 0;
+ ++stats_relativelyPrimeEliminated;
+ Hsyz->insert(sig, not_used);
+ SP->newSyzygy(sig);
+ SP->setKnownSyzygies(mSpairTmp);
+ return;
+ }
+ }
+ }
+#ifdef DEBUG
+ for (iter it = mSpairTmp.begin(); it != mSpairTmp.end(); ++it)
+ {
+ const_monomial a = GB->getLeadMonomial(it->first);
+ const_monomial b = GB->getLeadMonomial(it->second);
+ ASSERT(!R->monomialRelativelyPrime(a, b));
+ }
+#endif
+
+ // Reduce the pair
+ ++stats_pairsReduced;
+ if (!processSPair(sig, mSpairTmp) || !mPostponeKoszul)
+ return;
+ for (iter it = mSpairTmp.begin(); it != mSpairTmp.end(); ++it)
+ {
+ const_monomial greaterSig = GB->getSignature(it->first);
+ const_monomial smallerLead = GB->getLeadMonomial(it->second);
+ monomial koszul = R->allocMonomial();
+ R->monomialMult(greaterSig, smallerLead, koszul);
+ size_t dummy;
+ if (Hsyz->member(koszul, dummy))
+ R->freeMonomial(koszul);
+ else
+ mKoszuls->push(koszul);
+ }
+}
+
+size_t SignatureGB::getMemoryUse() const {
+ size_t sum =
+ GB->getMemoryUse() +
+ Hsyz->getMemoryUse() +
+ R->getMonomialPool().getMemoryUse() +
+ reducer->getMemoryUse() +
+ mSpairTmp.capacity() * sizeof(mSpairTmp.front());
+ sum += SP->getMemoryUse();
+
+ if (mKoszuls != 0)
+ mKoszuls->getMemoryUse();
+ return sum;
+}
+
+void SignatureGB::displayStats(std::ostream &o) const
+{
+ o << "-- stats: -- \n";
+ o << " strategy: signature"
+ << (mPostponeKoszul ? "-postpone" : "")
+ << (mUseBaseDivisors ? "-basediv" : "")
+ << (mComputeSignatureBasis ? "-full" : "-stopGB") << '\n';
+ o << " sig-order: " << F->description() << '\n';
+ o << " reduction type: " << reducer->description() << '\n';
+ o << " divisor tab type: " << GB->basis().divisorLookup().getName() << '\n';
+ o << " syzygy tab type: " << Hsyz->description() << '\n';
+ o << " S-pair queue type: " << SP->name() << '\n';
+ o << " total compute time: " << stats_nsecs << " -- seconds" << '\n';
+
+ MonomialTableArray::Stats hsyz_stats;
+ Hsyz->getStats(hsyz_stats);
+ o << " syz-n-compares: " << hsyz_stats.n_compares << '\n';
+ o << " syz-n-member-calls: " << hsyz_stats.n_calls_member << '\n';
+ o << " syz-n-inserts: " << (hsyz_stats.n_calls_insert) << '\n';
+ o << " syz-n-actual-inserts: " << (hsyz_stats.n_actual_inserts) << '\n';
+ o << " syz sig denseness: " << hsyz_stats.denseness << '\n';
+
+ const PolyRing::coefficientStats& cstats = R->getCoefficientStats();
+ o << "n-coeff-add: " << cstats.n_add << '\n';
+ o << "n-coeff-addmult: " << cstats.n_addmult << '\n';
+ o << "n-coeff-mult: " << cstats.n_mult << '\n';
+ o << "n-coeff-recip: " << cstats.n_recip << '\n';
+ o << "n-coeff-divide: " << cstats.n_divide << '\n';
+
+ displayMemoryUse(o);
+ displaySomeStats(o);
+ o << std::flush;
+}
+
+void SignatureGB::displayPaperStats(std::ostream& out) const {
+ SPairHandler::Stats stats = SP->getStats();
+ Reducer::Stats reducerStats = reducer->sigStats();
+ mic::ColumnPrinter pr;
+ pr.addColumn(true, " ");
+ pr.addColumn(false, " ");
+ pr.addColumn(true, " ");
+
+ std::ostream& name = pr[0];
+ std::ostream& value = pr[1];
+ std::ostream& extra = pr[2];
+
+ const unsigned long long considered0 = GB->size() * (GB->size() - 1) / 2;
+ const unsigned long long considered = stats.spairsConstructed;
+
+ if (considered0 != considered) {
+ name << "WARNING!!! #spairsConstructed is not correct!!\n";
+ value << '\n';
+ extra << '\n';
+ }
+
+ name << "S-pairs considered:\n";
+ value << mic::ColumnPrinter::commafy(considered) << '\n';
+ extra << '\n';
+
+ name << "Removed via non-regular criterion:\n";
+ value << mic::ColumnPrinter::commafy(stats.nonregularSPairs) << '\n';
+ extra << '\n';
+
+ name << "Removed via low base divisor:\n";
+ value << mic::ColumnPrinter::commafy(stats.lowBaseDivisorHits) << '\n';
+ extra << '\n';
+
+ name << "Removed via high base divisor:\n";
+ value << mic::ColumnPrinter::commafy(stats.highBaseDivisorHits) << '\n';
+ extra << '\n';
+
+ name << "Removed via signature criterion:\n";
+ value << mic::ColumnPrinter::commafy(stats.syzygyModuleHits) << '\n';
+ extra << '\n';
+
+ name << "Removed via relatively prime criterion:\n";
+ value << mic::ColumnPrinter::commafy(stats.earlyRelativelyPrimePairs) << '\n';
+ extra << '\n';
+
+ name << "Removed via singular criterion (early):\n";
+ value << mic::ColumnPrinter::commafy(stats.earlySingularCriterionPairs) << '\n';
+ extra << '\n';
+
+ name << "Number of queued pairs:\n";
+ value << mic::ColumnPrinter::commafy(stats.queuedPairs) << '\n';
+ extra << '\n';
+
+ name << "Removed via duplicate signature:\n";
+ value << mic::ColumnPrinter::commafy(stats.duplicateSignatures) << '\n';
+ extra << '\n';
+
+ name << "Removed via signature criterion (late):\n";
+ value << mic::ColumnPrinter::commafy(stats_SignatureCriterionLate) << '\n';
+ extra << '\n';
+
+ name << "Removed via Koszul criterion (late):\n";
+ value << mic::ColumnPrinter::commafy(stats_koszulEliminated) << '\n';
+ extra << '\n';
+
+ name << "Removed via relatively prime criterion (late):\n";
+ value << mic::ColumnPrinter::commafy(stats_relativelyPrimeEliminated) << '\n';
+ extra << '\n';
+
+ name << "Removed (singular reduction):\n";
+ value << mic::ColumnPrinter::commafy(reducerStats.singularReductions) << '\n';
+ extra << '\n';
+
+ unsigned long long nonzeroReductions = stats_pairsReduced - reducerStats.singularReductions - reducerStats.zeroReductions;
+
+ name << "Number of pairs reduced to signature GB elems:\n";
+ value << mic::ColumnPrinter::commafy(nonzeroReductions) << '\n';
+ extra << '\n';
+
+ name << "Number of pairs reduced to new syzygy signatures:\n";
+ value << mic::ColumnPrinter::commafy(reducerStats.zeroReductions) << '\n';
+ extra << '\n';
+
+ unsigned long long nleft = considered
+ - stats.nonregularSPairs
+ - stats.lowBaseDivisorHits
+ - stats.highBaseDivisorHits
+ - stats.syzygyModuleHits
+ - stats.earlyRelativelyPrimePairs
+ - stats.earlySingularCriterionPairs;
+ if (nleft != stats.queuedPairs) {
+ name << "WARNING!!! queuedPairs is not correct!!\n";
+ value << '\n';
+ extra << '\n';
+ }
+ nleft = nleft
+ - stats.duplicateSignatures
+ - stats_SignatureCriterionLate
+ - stats_koszulEliminated - stats_relativelyPrimeEliminated
+ - reducerStats.singularReductions
+ - reducerStats.zeroReductions
+ - nonzeroReductions;
+
+ name << "Number of spairs unaccounted for:\n";
+ value << mic::ColumnPrinter::commafy(nleft) << '\n';
+ extra << '\n';
+
+#ifdef MATHIC_TRACK_DIV_MASK_HIT_RATIO
+ name << "Divisor Mask Stats" << '\n';
+ value << '\n';
+ extra << '\n';
+
+ name << " DivMasks Computed: " << '\n';
+ value << mic::ColumnPrinter::commafy(mathic::DivMaskStats::maskComputes) << '\n';
+ extra << '\n';
+
+ name << " DivMask checks: " << '\n';
+ value << mic::ColumnPrinter::commafy(mathic::DivMaskStats::maskChecks) << '\n';
+ extra << '\n';
+
+ name << " DivMask hits: " << '\n';
+ value << mic::ColumnPrinter::commafy(mathic::DivMaskStats::maskHits) << '\n';
+ extra << '\n';
+
+ name << " DivMask divisor checks: " << '\n';
+ value << mic::ColumnPrinter::commafy(mathic::DivMaskStats::divChecks) << '\n';
+ extra << '\n';
+
+ name << " DivMask divisor divides: " << '\n';
+ value << mic::ColumnPrinter::commafy(mathic::DivMaskStats::divDivides) << '\n';
+ extra << '\n';
+
+ name << " DivMask divisor hits: " << '\n';
+ value << mic::ColumnPrinter::commafy(mathic::DivMaskStats::divHits) << '\n';
+ extra << '\n';
+#endif
+
+ out << "*** Statistics for ISSAC 2012 Paper ***\n" << pr << std::flush;
+}
+
+void SignatureGB::displaySomeStats(std::ostream& out) const {
+ mic::ColumnPrinter pr;
+ pr.addColumn(true, " ");
+ pr.addColumn(false, " ");
+ pr.addColumn(true, " ");
+
+ std::ostream& name = pr[0];
+ std::ostream& value = pr[1];
+ std::ostream& extra = pr[2];
+
+ const size_t basisSize = GB->size();
+ const double mseconds = mTimer.getMilliseconds();
+ const size_t pending = SP->size();
+
+ name << "Time spent:\n";
+ value << mTimer << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(mseconds / basisSize)
+ << " ms per basis element\n";
+
+ const double pendingRatio = static_cast<double>(pending) / basisSize;
+ name << "Basis elements:\n";
+ value << mic::ColumnPrinter::commafy(basisSize) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(pendingRatio)
+ << " Sp pend per basis ele\n";
+
+ const size_t basisTermCount = GB->basis().monomialCount();
+ name << "Terms for basis:\n";
+ value << mic::ColumnPrinter::commafy(basisTermCount) << '\n';
+ extra << mic::ColumnPrinter::ratio(basisTermCount, basisSize)
+ << " terms per basis ele\n";
+
+ const size_t minLeadCount = GB->basis().minimalLeadCount();
+ name << "Minimum lead terms:\n";
+ value << mic::ColumnPrinter::commafy(minLeadCount) << '\n';
+ extra << mic::ColumnPrinter::percent(minLeadCount, basisSize)
+ << " basis ele have min lead\n";
+
+ const size_t lastMinLead = GB->basis().maxIndexMinimalLead() + 1;
+ const size_t timeSinceLastMinLead = basisSize - lastMinLead;
+ name << "Index of last min lead:\n";
+ value << mic::ColumnPrinter::commafy(lastMinLead) << '\n';
+ extra << mic::ColumnPrinter::percent(timeSinceLastMinLead, basisSize)
+ << " of basis added since then\n";
+
+ const size_t minSyz = Hsyz->n_elems();
+ const double syzBasisRatio =
+ static_cast<double>(minSyz) / basisSize;
+ name << "Minimal syzygies:\n";
+ value << mic::ColumnPrinter::commafy(minSyz) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(syzBasisRatio)
+ << " syzygies per basis element\n";
+
+ const size_t queuedKoszuls = mKoszuls->size();
+ const double quedRatio = static_cast<double>(queuedKoszuls) / minSyz;
+ name << "Queued Koszul syzygies:\n";
+ value << mic::ColumnPrinter::commafy(queuedKoszuls) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(quedRatio)
+ << " queued koszuls per msyzygy\n";
+
+ const unsigned long long considered = GB->size() * (GB->size() - 1) / 2;
+ name << "S-pairs considered:\n";
+ value << mic::ColumnPrinter::commafy(considered) << '\n';
+ extra << '\n';
+
+ unsigned long long earlyNonElim;
+ SPairHandler::Stats stats = SP->getStats();
+ const unsigned long long lowElim = stats.lowBaseDivisorHits;
+ const unsigned long long highElim = stats.highBaseDivisorHits;
+ const unsigned long long syzElim = stats.syzygyModuleHits;
+ earlyNonElim = considered - lowElim - highElim - syzElim;
+
+ name << "S-pairs not early elim:\n";
+ value << mic::ColumnPrinter::commafy(earlyNonElim) << '\n';
+ extra << mic::ColumnPrinter::percent(earlyNonElim, considered)
+ << " of considered\n";
+
+ name << "Syz module S-pair elim:\n";
+ value << mic::ColumnPrinter::commafy(syzElim) << '\n';
+ extra << mic::ColumnPrinter::percent(syzElim, considered)
+ << " of considered\n";
+
+ name << "Low bdiv S-pair elim:\n";
+ value << mic::ColumnPrinter::commafy(lowElim) << '\n';
+ extra << mic::ColumnPrinter::percent(lowElim, considered)
+ << " of considered\n";
+
+ name << "High bdiv S-pair elim:\n";
+ value << mic::ColumnPrinter::commafy(highElim) << '\n';
+ extra << mic::ColumnPrinter::percent(highElim, considered)
+ << " of considered\n";
+
+ const unsigned long long hadLow = stats.hasLowBaseDivisor;
+ name << "Basis ele had low bdiv:\n";
+ value << mic::ColumnPrinter::commafy(hadLow) << '\n';
+ extra << mic::ColumnPrinter::percent(hadLow, basisSize)
+ << " of basis ele\n";
+
+ const unsigned long long hadHigh = stats.hasHighBaseDivisor;
+ name << "Basis ele had high bdiv:\n";
+ value << mic::ColumnPrinter::commafy(hadHigh) << '\n';
+ extra << mic::ColumnPrinter::percent(hadHigh, basisSize)
+ << " of basis ele\n";
+
+ name << "S-pairs pending:\n";
+ value << mic::ColumnPrinter::commafy(pending) << '\n';
+ extra << mic::ColumnPrinter::percent(pending, considered)
+ << " of considered\n";
+
+ const size_t done = stats_sPairsDone;
+ name << "S pairs done:\n";
+ value << mic::ColumnPrinter::commafy(done) << '\n';
+ extra << mic::ColumnPrinter::percent(done, earlyNonElim)
+ << " of not early elim\n";
+
+ const size_t sigsDone = stats_sPairSignaturesDone;
+ const double perSig = static_cast<double>(done) / sigsDone;
+ name << "S pair sigs done:\n";
+ value << mic::ColumnPrinter::commafy(sigsDone) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(perSig)
+ << " spairs per signature\n";
+
+ Reducer::Stats reducerStats = reducer->sigStats();
+
+ const unsigned long long reductions = reducerStats.reductions;
+ const size_t koszulElim = stats_koszulEliminated;
+ name << "Koszul sp eliminated:\n";
+ value << mic::ColumnPrinter::commafy(koszulElim) << '\n';
+ extra << mic::ColumnPrinter::percent(koszulElim, sigsDone - reductions)
+ << " of late eliminations\n";
+
+ const size_t primeElim = stats_relativelyPrimeEliminated;
+ name << "Rel.prime sp eliminated:\n";
+ value << mic::ColumnPrinter::commafy(primeElim) << '\n';
+ extra << mic::ColumnPrinter::percent(primeElim, sigsDone - reductions)
+ << " of late eliminations\n";
+
+ name << "Signature reductions:\n";
+ value << mic::ColumnPrinter::commafy(reductions) << '\n';
+ extra << mic::ColumnPrinter::percent(reductions, sigsDone)
+ << " of S-pairs are reduced\n";
+
+ const unsigned long long singularReductions =
+ reducerStats.singularReductions;
+ name << "Singular reductions:\n";
+ value << mic::ColumnPrinter::commafy(singularReductions) << '\n';
+ extra << mic::ColumnPrinter::percent(singularReductions, reductions)
+ << " of reductions\n";
+
+ const unsigned long long zeroReductions = reducerStats.zeroReductions;
+ name << "Reductions to zero:\n";
+ value << mic::ColumnPrinter::commafy(zeroReductions) << '\n';
+ extra << mic::ColumnPrinter::percent(zeroReductions, reductions)
+ << " of reductions\n";
+
+ const unsigned long long newReductions =
+ reductions - singularReductions - zeroReductions;
+ name << "Reductions to new ele:\n";
+ value << mic::ColumnPrinter::commafy(newReductions) << '\n';
+ extra << mic::ColumnPrinter::percent(newReductions, reductions)
+ << " of reductions\n";
+
+ const unsigned long long redSteps = reducerStats.steps;
+ const double stepsRatio =
+ static_cast<double>(redSteps) / (reductions - singularReductions);
+ name << "Sig reduction steps:\n";
+ value << mic::ColumnPrinter::commafy(redSteps) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(stepsRatio)
+ << " steps per non-sing reduction\n";
+
+ const unsigned long long longestReduction = reducerStats.maxSteps;
+ name << "Longest sig reduction:\n";
+ value << mic::ColumnPrinter::commafy(longestReduction) << '\n';
+ extra << '\n';
+
+ Reducer::Stats classicRedStats = reducer->classicStats();
+ const unsigned long long clReductions = classicRedStats.reductions;
+ name << "Classic reductions:\n";
+ value << mic::ColumnPrinter::commafy(clReductions) << '\n';
+ extra << '\n';
+
+ const unsigned long long clRedSteps = classicRedStats.steps;
+ const double clStepsRatio = static_cast<double>(clRedSteps) / clReductions;
+ name << "Classic reduction steps:\n";
+ value << mic::ColumnPrinter::commafy(clRedSteps) << '\n';
+ extra << mic::ColumnPrinter::oneDecimal(clStepsRatio)
+ << " steps per reduction\n";
+
+ const unsigned long long clLongestReduction = classicRedStats.maxSteps;
+ name << "Longest classic red:\n";
+ value << mic::ColumnPrinter::commafy(clLongestReduction) << '\n';
+ extra << '\n';
+
+ out << "*** Some of the statistics ***\n" << pr << std::flush;
+}
+
+void SignatureGB::displayMemoryUse(std::ostream& out) const
+{
+ // set up printer
+ mic::ColumnPrinter pr;
+ pr.addColumn();
+ pr.addColumn(false);
+ pr.addColumn(false);
+
+ std::ostream& name = pr[0];
+ std::ostream& value = pr[1];
+ std::ostream& extra = pr[2];
+
+ const size_t total = getMemoryUse();
+ { // Grobner basis
+ const size_t basisMem = GB->getMemoryUse();
+ name << "Grobner basis:\n";
+ value << mic::ColumnPrinter::bytesInUnit(basisMem) << '\n';
+ extra << mic::ColumnPrinter::percent(basisMem, total) << '\n';
+ }
+ { // Spairs
+ const size_t sPairMem = SP->getMemoryUse();
+ name << "S-pairs:\n";
+ value << mic::ColumnPrinter::bytesInUnit(sPairMem) << '\n';
+ extra << mic::ColumnPrinter::percent(sPairMem, total) << '\n';
+
+ const size_t knownSyzygyMem = SP->getKnownSyzygyBitsMemoryUse();
+ name << " Known syzygy bits:\n";
+ value << mic::ColumnPrinter::bytesInUnit(knownSyzygyMem) << '\n';
+ extra << '\n';
+ }
+ { // Syzygies
+ const size_t syzMem = Hsyz->getMemoryUse();
+ name << "Minimal syzygies:\n";
+ value << mic::ColumnPrinter::bytesInUnit(syzMem) << '\n';
+ extra << mic::ColumnPrinter::percent(syzMem, total) << '\n';
+ }
+ { // Koszul queue
+ const size_t syzQueueMem = mKoszuls->getMemoryUse();
+ name << "Koszul queue:\n";
+ value << mic::ColumnPrinter::bytesInUnit(syzQueueMem) << '\n';
+ extra << mic::ColumnPrinter::percent(syzQueueMem, total) << '\n';
+ }
+ { // Reducer
+ const size_t reducerMem = reducer->getMemoryUse();
+ name << "Reducer:\n";
+ value << mic::ColumnPrinter::bytesInUnit(reducerMem) << '\n';
+ extra << mic::ColumnPrinter::percent(reducerMem, total) << '\n';
+ }
+ { // Signatures
+ const size_t sigMem = R->getMonomialPool().getMemoryUse();
+ name << "Signatures:\n";
+ value << mic::ColumnPrinter::bytesInUnit(sigMem) << '\n';
+ extra << mic::ColumnPrinter::percent(sigMem, total) << '\n';
+ }
+ // total
+ name << "-------------\n";
+ value << '\n';
+ extra << '\n';
+
+ name << "Memory used in total:\n";
+ value << mic::ColumnPrinter::bytesInUnit(total) << "\n";
+ extra << "\n";
+
+ out << "*** Summary of memory use ***\n" << pr << std::flush;
+}
+
+unsigned long long SignatureGB::getSigReductionCount() const {
+ return reducer->sigStats().reductions;
+}
+
+unsigned long long SignatureGB::getSingularReductionCount() const {
+ return reducer->sigStats().singularReductions;
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/SignatureGB.hpp b/src/mathicgb/SignatureGB.hpp
new file mode 100644
index 0000000..dc0ff8c
--- /dev/null
+++ b/src/mathicgb/SignatureGB.hpp
@@ -0,0 +1,113 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _sig_gb_h_
+#define _sig_gb_h_
+
+#ifdef Win32
+#include "StdAfx.h"
+#endif
+
+#include "PolyRing.hpp"
+#include "MTArray.hpp"
+#include "GroebnerBasis.hpp"
+#include "FreeModuleOrder.hpp"
+#include "SPairHandler.hpp"
+#include "Reducer.hpp"
+#include "KoszulQueue.hpp"
+#include "SPairs.hpp"
+#include <map>
+
+class SPairHandler;
+class DivisorLookup;
+
+class SignatureGB {
+public:
+ SignatureGB(
+ const Ideal& ideal,
+ FreeModuleOrderType typ,
+ Reducer::ReducerType reductiontyp,
+ int divlookup_type,
+ int montable_type,
+ bool postponeKoszul,
+ bool useBaseDivisors,
+ bool preferSparseReducers,
+ bool useSingularCriterionEarly,
+ size_t queueType);
+ ~SignatureGB();
+
+ void computeGrobnerBasis();
+
+ // How many S-pairs were not eliminated before reduction.
+ unsigned long long getSigReductionCount() const;
+
+ // How many reductions were singular
+ unsigned long long getSingularReductionCount() const;
+
+ GroebnerBasis* getGB() { return GB; }
+ MonomialTableArray* getSyzTable() { return Hsyz; }
+ SPairHandler* getSPairHandler() { return SP; }
+
+ size_t getMemoryUse() const;
+ void displayStats(std::ostream& out) const;
+ void displayPaperStats(std::ostream& out) const;
+ void displayMemoryUse(std::ostream& out) const;
+ void displaySomeStats(std::ostream& out) const;
+
+ void setBreakAfter(unsigned int elements) {
+ mBreakAfter = elements;
+ }
+
+ void setPrintInterval(unsigned int reductions) {
+ mPrintInterval = reductions;
+ }
+
+ void setComputeSignatureBasis(bool value) {
+ mComputeSignatureBasis = value;
+ }
+
+private:
+ unsigned int mBreakAfter;
+ unsigned int mPrintInterval;
+ bool mComputeSignatureBasis;
+
+ bool processSPair(monomial sig, const SPairHandler::PairContainer& pairs);
+ void step();
+
+ const PolyRing *R;
+ FreeModuleOrder *F;
+
+ SPairHandler *SP;
+ MonomialTableArray *Hsyz;
+ GroebnerBasis *GB;
+ KoszulQueue *mKoszuls;
+
+ Reducer* reducer;
+
+ bool const mPostponeKoszul;
+
+ // Currently we use either both criteria (high and loow) or neither.
+ bool const mUseBaseDivisors;
+
+ SPairHandler::PairContainer mSpairTmp; // use only for getting S-pairs
+
+ // stats //////////
+ size_t stats_sPairSignaturesDone; // distinct S-pair signatures done
+ size_t stats_sPairsDone; // total S-pairs done
+ size_t stats_koszulEliminated; // S-pairs eliminated due to Koszul queue
+ size_t stats_SignatureCriterionLate; // # spairs removed due to being a syz signature
+
+ // S-pairs eliminated due to relatively prime criterion
+ size_t stats_relativelyPrimeEliminated;
+
+ size_t stats_pairsReduced; // # spairs actually sent for reduction
+
+ mic::Timer mTimer;
+ double stats_nsecs;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/TournamentReducer.cpp b/src/mathicgb/TournamentReducer.cpp
new file mode 100644
index 0000000..deb85cd
--- /dev/null
+++ b/src/mathicgb/TournamentReducer.cpp
@@ -0,0 +1,183 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#include "stdinc.h"
+#include "TournamentReducer.hpp"
+
+#include <utility>
+
+extern int tracingLevel;
+
+TournamentReducer::TournamentReducer(const PolyRing& ring):
+ Reducer(),
+ mRing(ring),
+ mLeadTerm(0, mRing.allocMonomial()),
+ mLeadTermKnown(false),
+ mQueue(Configuration(ring)),
+ mPool(sizeof(MultipleWithPos))
+{
+}
+
+class TournamentReducer::MonomialFree
+{
+public:
+ MonomialFree(const PolyRing& ring): mRing(ring) {}
+
+ bool proceed(MultipleWithPos* entry)
+ {
+ entry->destroy(mRing);
+ return true;
+ }
+private:
+ const PolyRing& mRing;
+};
+
+TournamentReducer::~TournamentReducer()
+{
+ resetReducer();
+ mRing.freeMonomial(mLeadTerm.monom);
+}
+
+///////////////////////////////////////
+// External interface routines ////////
+///////////////////////////////////////
+void TournamentReducer::insertTail(const_term multiple, const Poly* poly)
+{
+ if (poly->nTerms() <= 1)
+ return;
+ mLeadTermKnown = false;
+
+ MultipleWithPos* entry = new (mPool.alloc()) MultipleWithPos(*poly, multiple);
+ ++entry->pos;
+ entry->computeCurrent(poly->ring());
+ mQueue.push(entry);
+}
+
+void TournamentReducer::insert(monomial multiple, const Poly* poly)
+{
+ if (poly->isZero())
+ return;
+ mLeadTermKnown = false;
+
+ term termMultiple(1, multiple);
+ MultipleWithPos* entry = new (mPool.alloc()) MultipleWithPos(*poly, termMultiple);
+ entry->computeCurrent(poly->ring());
+ mQueue.push(entry);
+}
+
+namespace {
+ const_term allocTerm(const PolyRing& ring, const_term term) {
+ monomial mono = ring.allocMonomial();
+ ring.monomialCopy(term.monom, mono);
+ return const_term(term.coeff, mono);
+ }
+}
+
+TournamentReducer::MultipleWithPos::MultipleWithPos
+(const Poly& poly, const_term multiple):
+ pos(poly.begin()),
+ end(poly.end()),
+ multiple(allocTerm(poly.ring(), multiple)),
+ current(poly.ring().allocMonomial()) {}
+
+void TournamentReducer::MultipleWithPos::computeCurrent(const PolyRing& ring) {
+ ring.monomialMult(multiple.monom, pos.getMonomial(), current);
+}
+
+void TournamentReducer::MultipleWithPos::currentCoefficient
+(const PolyRing& ring, coefficient& coeff) {
+ ring.coefficientMult(multiple.coeff, pos.getCoefficient(), coeff);
+}
+
+void TournamentReducer::MultipleWithPos::destroy(const PolyRing& ring) {
+ ring.freeMonomial(current);
+ ring.freeMonomial(const_cast<ConstMonomial&>(multiple.monom).castAwayConst());
+
+ // Call the destructor to destruct the iterators into std::vector.
+ // In debug mode MSVC puts those in a linked list and the destructor
+ // has to be called since it takes an iterator off the list. We had
+ // memory corruption problems before doing this.
+ this->~MultipleWithPos();
+}
+
+bool TournamentReducer::findLeadTerm(const_term& result)
+{
+ if (mLeadTermKnown) {
+ result = mLeadTerm;
+ return true;
+ }
+
+ do {
+ if (mQueue.empty())
+ return false;
+ MultipleWithPos* entry = mQueue.top();
+ mLeadTerm.monom.swap(entry->current);
+ entry->currentCoefficient(mRing, mLeadTerm.coeff);
+
+ while (true) {
+ ++entry->pos;
+ if (entry->pos == entry->end) {
+ mQueue.pop();
+ entry->destroy(mRing);
+ mPool.free(entry);
+ } else {
+ entry->computeCurrent(mRing);
+ mQueue.decreaseTop(entry);
+ }
+
+ if (mQueue.empty())
+ break;
+
+ entry = mQueue.top();
+ if (!mRing.monomialEQ(entry->current, mLeadTerm.monom))
+ break;
+ coefficient coeff;
+ entry->currentCoefficient(mRing, coeff);
+ mRing.coefficientAddTo(mLeadTerm.coeff, const_cast<const coefficient&>(coeff));
+ }
+ } while (mRing.coefficientIsZero(mLeadTerm.coeff));
+
+ result = mLeadTerm;
+ mLeadTermKnown = true;
+ return true;
+}
+
+void TournamentReducer::removeLeadTerm()
+{
+ if (!mLeadTermKnown) {
+ const_term dummy;
+ findLeadTerm(dummy);
+ }
+ mLeadTermKnown = false;
+}
+
+void TournamentReducer::value(Poly &result)
+{
+ const_term t;
+ while (findLeadTerm(t)) {
+ result.appendTerm(t.coeff, t.monom);
+ removeLeadTerm();
+ }
+ resetReducer();
+}
+
+void TournamentReducer::resetReducer()
+{
+ MonomialFree freeer(mRing);
+ mQueue.forAll(freeer);
+ mQueue.clear();
+}
+
+size_t TournamentReducer::getMemoryUse() const
+{
+ return mQueue.getMemoryUse() + mPool.getMemoryUse();
+}
+
+void TournamentReducer::dump() const
+{
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
+
diff --git a/src/mathicgb/TournamentReducer.hpp b/src/mathicgb/TournamentReducer.hpp
new file mode 100644
index 0000000..0e39e10
--- /dev/null
+++ b/src/mathicgb/TournamentReducer.hpp
@@ -0,0 +1,82 @@
+// Copyright 2011 Bjarke Roune, Michael E. Stillman
+
+#ifndef _tournament_reducer_h_
+#define _tournament_reducer_h_
+
+#include <mathic.h>
+#include <memtailor.h>
+#include "Reducer.hpp"
+
+class TournamentReducer : public Reducer {
+public:
+ TournamentReducer(const PolyRing& R);
+ virtual ~TournamentReducer();
+
+ virtual std::string description() const { return "tournament tree reducer"; }
+
+ virtual void insertTail(const_term multiplier, const Poly *f);
+ virtual void insert(monomial multiplier, const Poly *f);
+
+ virtual bool findLeadTerm(const_term &result);
+ virtual void removeLeadTerm();
+
+ virtual void value(Poly &result); // keep extracting lead term until done
+
+ virtual size_t getMemoryUse() const;
+
+ virtual void dump() const; // Used for debugging
+
+protected:
+ virtual void resetReducer();
+
+private:
+ // Represents a term multiple of a polynomial,
+ // together with a current term of the multiple.
+ struct MultipleWithPos {
+ MultipleWithPos(const Poly& poly, const_term multiple);
+
+ Poly::const_iterator pos;
+ Poly::const_iterator const end;
+ const_term const multiple;
+ monomial current; // multiple.monom * pos.getMonomial()
+
+ void computeCurrent(const PolyRing& ring);
+ void currentCoefficient(const PolyRing& ring, coefficient& coeff);
+ void destroy(const PolyRing& ring);
+ };
+
+ class Configuration {
+ public:
+ typedef MultipleWithPos* Entry;
+
+ Configuration(const PolyRing& ring) : mRing(ring) {}
+
+ typedef bool CompareResult;
+ CompareResult compare(const Entry& a, const Entry& b) const {
+ return mRing.monomialLT(a->current, b->current);
+ }
+ bool cmpLessThan(CompareResult r) const {return r;}
+
+ static const bool fastIndex = true;
+
+ private:
+ const PolyRing& mRing;
+ };
+
+ class MonomialFree;
+
+ void insert(const_term multiplier, Poly::iterator first, Poly::iterator last);
+
+ const PolyRing& mRing;
+ term mLeadTerm;
+ bool mLeadTermKnown;
+ mic::TourTree<Configuration> mQueue;
+ memt::BufferPool mPool;
+};
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/io-util.cpp b/src/mathicgb/io-util.cpp
new file mode 100644
index 0000000..dd6313e
--- /dev/null
+++ b/src/mathicgb/io-util.cpp
@@ -0,0 +1,175 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "stdinc.h"
+#include <cstdio>
+#include <string>
+#include <iostream>
+#include <sstream>
+#include "Poly.hpp"
+#include "MTArray.hpp"
+#include "io-util.hpp"
+
+#include "PolyHeap.hpp"
+#include "PolyGeoBucket.hpp"
+#include "GroebnerBasis.hpp"
+#include "SPairHandler.hpp"
+#include "SignatureGB.hpp"
+#include "MTArray.hpp"
+
+#include "Ideal.hpp"
+#include "PolyBasis.hpp"
+
+std::auto_ptr<Poly> polyParseFromString(const PolyRing *R, const std::string &s)
+{
+ std::auto_ptr<Poly> f(new Poly(R));
+ std::istringstream in(s);
+ f->parse(in);
+ return f;
+}
+
+std::string toString(const Poly *g)
+{
+ std::ostringstream o;
+ g->display(o);
+ return o.str();
+}
+
+std::auto_ptr<Ideal> idealParseFromString(std::string str)
+{
+ std::istringstream i(str);
+ return std::auto_ptr<Ideal>(Ideal::parse(i));
+}
+
+std::auto_ptr<PolyRing> ringFromString(std::string ringinfo)
+{
+ std::stringstream ifil(ringinfo);
+ return std::auto_ptr<PolyRing>(PolyRing::read(ifil));
+}
+
+Monomial stringToMonomial(const PolyRing *R, std::string mon)
+{
+ Monomial result = R->allocMonomial1();
+ std::stringstream ifil(mon);
+ R->monomialParse(ifil, result);
+ return result;
+}
+
+std::string monomialToString(const PolyRing *R, const Monomial& mon)
+{
+ std::ostringstream o;
+ R->monomialDisplay(o,mon);
+ return o.str();
+}
+
+///////// Which of the following do we need/use? /////////////
+#ifndef NEWMONOMIALS
+monomial monomialFromString(const PolyRing *R, std::string mon)
+{
+ // This is poor code, to only be used for testing!
+ monomial result = new int[R->maxMonomialSize()];
+ std::stringstream ifil(mon);
+ R->monomialRead(ifil, result);
+ return result;
+}
+
+std::string monomialToString(const PolyRing *R, const_monomial mon)
+{
+ std::ostringstream o;
+ R->monomialWrite(o,mon);
+ return o.str();
+}
+#endif
+
+monomial monomialParseFromString(const PolyRing *R, std::string mon)
+{
+ // This is poor code, to only be used for testing!
+ monomial result = R->allocMonomial();
+ std::stringstream ifil(mon);
+ R->monomialParse(ifil, result);
+ return result;
+}
+
+std::string monomialDisplay(const PolyRing *R, const_monomial mon)
+{
+ std::ostringstream o;
+ R->monomialDisplay(o,mon);
+ return o.str();
+}
+////////////////////////////////////////////////////////////////
+
+std::string toString(GroebnerBasis *I)
+{
+ std::ostringstream o;
+ for (size_t i=0; i<I->size(); i++)
+ {
+ o << " ";
+ I->poly(i).display(o, false);
+ o << std::endl;
+ }
+ return o.str();
+}
+
+std::string toString(GroebnerBasis *I, int)
+{
+ std::ostringstream o;
+ I->display(o);
+ return o.str();
+}
+
+std::string toString(MonomialTableArray* H)
+{
+ std::ostringstream o;
+ H->display(o, 1);
+ return o.str();
+}
+
+std::string toString(Ideal *I)
+{
+ std::ostringstream o;
+ for (size_t i=0; i<I->size(); i++)
+ {
+ o << " ";
+ I->getPoly(i)->display(o,false);
+ o << std::endl;
+ }
+ return o.str();
+}
+
+std::auto_ptr<Poly> multIdealByPolyReducer(int typ, const Ideal& ideal, const Poly& g)
+{
+ const PolyRing& R = ideal.ring();
+ std::auto_ptr<Reducer> H = Reducer::makeReducer(static_cast<Reducer::ReducerType>(typ), R);
+ for (Poly::const_iterator i = g.begin(); i != g.end(); ++i) {
+ monomial mon = R.allocMonomial();
+ R.monomialCopy(i.getMonomial(), mon);
+ int x = R.monomialGetComponent(mon);
+ R.monomialChangeComponent(mon, 0);
+ std::auto_ptr<Poly> h(ideal.getPoly(x)->copy());
+ h->multByCoefficient(i.getCoefficient());
+ H->insert(mon, h.release());
+ }
+ std::auto_ptr<Poly> result(new Poly(&R));
+ const_term t;
+ while (H->findLeadTerm(t)) {
+ result->appendTerm(t.coeff, t.monom);
+ H->removeLeadTerm();
+ }
+ return result;
+}
+
+void output(std::ostream &o, const PolyBasis &I)
+{
+ for (size_t i = 0; i < I.size(); i++)
+ {
+ if (!I.retired(i))
+ {
+ I.poly(i).display(o, false);
+ o << std::endl;
+ }
+ }
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/io-util.hpp b/src/mathicgb/io-util.hpp
new file mode 100644
index 0000000..b6ce176
--- /dev/null
+++ b/src/mathicgb/io-util.hpp
@@ -0,0 +1,42 @@
+// Copyright 2011 Michael E. Stillman
+
+#ifndef _io_util_h_
+#define _io_util_h_
+
+#include "PolyRing.hpp"
+
+class Poly;
+class GroebnerBasis;
+class MonomialTableArray;
+class PolyBasis;
+class Ideal;
+
+std::auto_ptr<PolyRing> ringFromString(std::string ringinfo);
+monomial monomialFromString(const PolyRing *R, std::string mon);
+monomial monomialParseFromString(const PolyRing *R, std::string mon);
+std::string monomialToString(const PolyRing *R, const_monomial mon);
+std::string monomialDisplay(const PolyRing *R, const_monomial mon);
+
+Monomial stringToMonomial(const PolyRing *R, std::string mon);
+std::string monomialToString(const PolyRing *R, const Monomial& mon);
+
+std::string toString(GroebnerBasis *);
+std::string toString(MonomialTableArray *);
+std::string toString(GroebnerBasis *, int unused); // also displays signature
+std::string toString(Ideal *);
+std::string toString(const Poly *);
+
+std::auto_ptr<Ideal> idealParseFromString(std::string str);
+std::auto_ptr<Poly> polyParseFromString(const PolyRing *R, const std::string &s);
+
+std::auto_ptr<Poly> multIdealByPolyReducer(int typ, const Ideal& I, const Poly& g);
+
+
+void output(std::ostream &o, const PolyBasis &I);
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/mathicgb/stdinc.h b/src/mathicgb/stdinc.h
new file mode 100644
index 0000000..99fe632
--- /dev/null
+++ b/src/mathicgb/stdinc.h
@@ -0,0 +1,57 @@
+#ifdef STDINC_GUARD
+#error stdinc.h included twice
+#endif
+#define STDINC_GUARD
+
+#ifdef _MSC_VER // For Microsoft Compiler in Visual Studio C++.
+#pragma warning (push, 1) // Reduce warning level for GMP headers.
+#endif
+
+#ifdef PROFILE
+#define NO_PINLINE NO_INLINE
+#else
+#define NO_PINLINE
+#endif
+
+#ifndef _MSC_VER
+#define NO_INLINE __attribute__ ((noinline))
+#endif
+
+#ifdef _MSC_VER // For Microsoft Compiler in Visual Studio C++.
+#define NO_INLINE __declspec(noinline)
+#pragma warning (pop) // Go back to previous warning level.
+#pragma warning (disable: 4996) // std::copy is flagged as dangerous.
+#pragma warning (disable: 4290) // VC++ ignores throw () specification.
+#pragma warning (disable: 4127) // Warns about using "while (true)".
+#pragma warning (disable: 4100) // Warns about unused parameters.
+#pragma warning (disable: 4800) // Warns on int to bool conversion.
+#pragma warning (disable: 4146) // Warns on unary minus on unsigned (bit trick)
+
+// This warning warns about using the this pointer in base member
+// initializer lists. This is a pretty good warning as that can
+// obviously easily go wrong, but it is pretty useful to do as well,
+// so the warning is turned off.
+#pragma warning (disable: 4355)
+
+#if (defined _DEBUG) && (!defined DEBUG)
+#define DEBUG
+#endif
+
+#endif
+
+#include <cstddef>
+#include <memory>
+
+#ifdef DEBUG
+#include <iostream> // Useful for debugging.
+#define PRINT
+#include <cassert>
+#define ASSERT(X) assert(X);
+#define IF_DEBUG(X) X
+#else
+#define ASSERT(X)
+#define IF_DEBUG(X)
+#endif
+
+static const size_t BitsPerByte = 8;
+static const size_t MemoryAlignment = sizeof(void*);
diff --git a/src/test/FreeModuleOrderTest.cpp b/src/test/FreeModuleOrderTest.cpp
new file mode 100755
index 0000000..d087436
--- /dev/null
+++ b/src/test/FreeModuleOrderTest.cpp
@@ -0,0 +1,94 @@
+#include "mathicgb/stdinc.h"
+
+#include "mathicgb/PolyRing.hpp"
+#include "mathicgb/Ideal.hpp"
+#include "mathicgb/FreeModuleOrder.hpp"
+#include "mathicgb/io-util.hpp"
+#include <gtest/gtest.h>
+#include <algorithm>
+
+void runTest(
+ const char* idealStr,
+ const char* signatureStr,
+ const char* correctStr,
+ int orderType
+) {
+ std::string line;
+
+ std::auto_ptr<Ideal> ideal = idealParseFromString(idealStr);
+ const PolyRing* ring = ideal->getPolyRing();
+
+ std::vector<monomial> sigs;
+ std::vector<PreSPair> pairs;
+ {
+ std::istringstream in(signatureStr);
+ while (std::getline(in, line)) {
+ sigs.push_back(monomialParseFromString(ring, line));
+ PreSPair pair;
+ pair.i = static_cast<BigIndex>(pairs.size());
+ pair.signature = monomialParseFromString(ring, line);
+ pairs.push_back(pair);
+ }
+ }
+
+ std::vector<size_t> answer(pairs.size());
+ {
+ std::istringstream in(correctStr);
+ for (size_t i = 0; i < answer.size(); ++i) {
+ in >> answer[i];
+ ASSERT(in);
+ }
+ }
+
+ ASSERT(sigs.size() == pairs.size());
+ ASSERT(sigs.size() == pairs.size());
+
+ std::auto_ptr<FreeModuleOrder> order
+ (FreeModuleOrder::makeOrder(orderType, ideal.get()));
+ order->destructiveSort(pairs);
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ ring->freeMonomial(pairs[i].signature);
+ pairs[i].signature = sigs[pairs[i].i];
+ }
+ sigs.clear();
+
+ for (size_t i = 0; i < pairs.size(); ++i)
+ ASSERT_EQ(pairs[i].i, answer[i]) << i << ' ' << orderType;
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ const_monomial sigi = pairs[i].signature;
+ ASSERT_EQ(order->signatureCompare(sigi, sigi), EQ)
+ << i << ' ' << orderType;
+ for (size_t j = 0; j < i; ++j) {
+ const_monomial sigj = pairs[j].signature;
+ ASSERT_EQ(order->signatureCompare(sigj, sigi), LT)
+ << i << ' ' << j << ' ' << orderType;
+ ASSERT_EQ(order->signatureCompare(sigi, sigj), GT)
+ << i << ' ' << j << ' ' << orderType;
+ }
+ }
+}
+
+TEST(FreeModuleOrder, One) {
+ const char* ideal =
+"32003 3 "
+"1 1 1 1 "
+"3 "
+"a3b2c2 "
+"a "
+"a3c2 ";
+ const char* sigs =
+ "<1>\n"
+ "<0>\n"
+ "b10<0>\n"
+ "ac<0>\n"
+ "bc<0>\n"
+ "ac<1>\n"
+ "bc<1>\n"
+ "ab2c<2>\n";
+
+ runTest(ideal, sigs, "0 1 6 4 5 3 7 2", 1);
+ runTest(ideal, sigs, "0 6 5 1 4 3 7 2", 2);
+ runTest(ideal, sigs, "0 6 5 1 7 4 3 2", 3);
+ runTest(ideal, sigs, "0 6 5 1 4 3 7 2", 4);
+ runTest(ideal, sigs, "0 6 5 1 4 7 3 2", 5);
+}
diff --git a/src/test/gb-test.cpp b/src/test/gb-test.cpp
new file mode 100755
index 0000000..9aec9d8
--- /dev/null
+++ b/src/test/gb-test.cpp
@@ -0,0 +1,165 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "mathicgb/stdinc.h"
+
+#include "mathicgb/Poly.hpp"
+#include "mathicgb/Ideal.hpp"
+#include "mathicgb/MTArray.hpp"
+#include "mathicgb/MonTableNaive.hpp"
+#include "mathicgb/io-util.hpp"
+#include "mathicgb/PolyHeap.hpp"
+#include "mathicgb/GroebnerBasis.hpp"
+#include "mathicgb/SPairHandler.hpp"
+#include "mathicgb/SignatureGB.hpp"
+#include "mathicgb/BuchbergerAlg.hpp"
+#include "test/ideals.hpp"
+
+#include <cstdio>
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <gtest/gtest.h>
+
+extern int tracingLevel;
+
+TEST(IO, ideal) {
+ const char* idealA_fromStr_format =
+"32003 6 \
+1 1 1 1 1 1 1 \
+3 \
+-bc+ad \
+-b2+af \
+-bc2+a2e \
+";
+
+ std::auto_ptr<Ideal> I = idealParseFromString(idealA_fromStr_format);
+ EXPECT_EQ(" -bc+ad\n -b2+af\n -bc2+a2e\n", toString(I.get()));
+}
+
+void testGB(int freeModuleOrder,
+ std::string idealStr,
+ std::string sigBasisStr,
+ std::string syzygiesStr,
+ std::string initialIdealStr,
+ size_t nonSingularReductions)
+{
+ for (int spairQueue = 0; spairQueue <= 3; ++spairQueue)
+ for (int reducerType = 0; reducerType <= 30; ++reducerType)
+ for (int divLookup = 1; divLookup <= 2; ++divLookup)
+ for (int monTable = 0; monTable <= 2; ++monTable)
+ for (int signatureBasis = 0; signatureBasis <= 1; ++signatureBasis)
+ for (int buchberger = 0; buchberger <= 1; ++buchberger)
+ for (int postponeKoszul = 0; postponeKoszul <= 1; ++postponeKoszul)
+ for (int useBaseDivisors = 0; useBaseDivisors <= 1; ++useBaseDivisors)
+ for (int autoTailReduce = 0; autoTailReduce <= 1; ++autoTailReduce)
+ for (int autoTopReduce = 0; autoTopReduce <= 1; ++autoTopReduce)
+ for (int preferSparseReducers = 0; preferSparseReducers <= 1;
+ ++preferSparseReducers)
+ for (int useSingularCriterionEarly = 0; useSingularCriterionEarly <= 1;
+ ++useSingularCriterionEarly)
+ {
+ //std::cout << reducerType << ' ' << divLookup << ' ' << monTable << ' ' << signatureBasis << ' ' << buchberger << ' ' << postponeKoszul << ' ' << useBaseDivisors << ' ' << autoTailReduce << ' ' << autoTopReduce << ' ' << preferSparseReducers << std::endl;
+ if (!buchberger && (autoTopReduce || autoTailReduce))
+ continue;
+ if (buchberger && (postponeKoszul || useBaseDivisors || signatureBasis || useSingularCriterionEarly))
+ continue;
+
+ Reducer::ReducerType red = Reducer::ReducerType(reducerType);
+ if ((buchberger && signatureBasis) || static_cast<int>(red) != reducerType)
+ continue;
+ std::auto_ptr<Ideal> I(idealParseFromString(idealStr));
+ if (Reducer::makeReducerNullOnUnknown(red, I->ring()).get() == 0)
+ continue;
+
+ if (buchberger) {
+ ASSERT(!signatureBasis);
+ BuchbergerAlg alg(
+ *I, freeModuleOrder, Reducer::reducerType(reducerType), divLookup, preferSparseReducers, spairQueue);
+ alg.setUseAutoTopReduction(autoTopReduce);
+ alg.setUseAutoTailReduction(autoTailReduce);
+ alg.computeGrobnerBasis();
+ std::auto_ptr<Ideal> initialIdeal =
+ alg.basis().initialIdeal();
+ EXPECT_EQ(initialIdealStr, toString(initialIdeal.get()))
+ << reducerType << ' ' << divLookup << ' '
+ << monTable << ' ' << postponeKoszul << ' ' << useBaseDivisors;
+ } else {
+ SignatureGB basis
+ (*I, freeModuleOrder, Reducer::reducerType(reducerType),
+ divLookup, monTable, postponeKoszul, useBaseDivisors, preferSparseReducers, useSingularCriterionEarly, spairQueue);
+ basis.setComputeSignatureBasis(signatureBasis);
+ basis.computeGrobnerBasis();
+ if (!signatureBasis) {
+ std::auto_ptr<Ideal> initialIdeal =
+ basis.getGB()->basis().initialIdeal();
+ EXPECT_EQ(initialIdealStr, toString(initialIdeal.get()))
+ << reducerType << ' ' << divLookup << ' '
+ << monTable << ' ' << postponeKoszul << ' ' << useBaseDivisors;
+ } else {
+ EXPECT_EQ(sigBasisStr, toString(basis.getGB(), 1))
+ << reducerType << ' ' << divLookup << ' '
+ << monTable << ' ' << ' ' << postponeKoszul << ' '
+ << useBaseDivisors;
+ EXPECT_EQ(syzygiesStr, toString(basis.getSyzTable()))
+ << reducerType << ' ' << divLookup << ' '
+ << monTable << ' ' << ' ' << postponeKoszul << ' '
+ << useBaseDivisors;
+ EXPECT_EQ(nonSingularReductions, basis.getSigReductionCount() - basis.getSingularReductionCount())
+ << reducerType << ' ' << divLookup << ' '
+ << monTable << ' ' << ' ' << postponeKoszul << ' '
+ << useBaseDivisors;
+ }
+ }
+ }
+}
+
+extern int tracingLevel;
+TEST(GB, small) {
+ testGB(0, idealSmall, idealSmallBasis, idealSmallSyzygies, idealSmallInitial, 7);
+}
+
+TEST(GB, liu_0_1) {
+ testGB(1, liu_ideal, liu_gb_strat0_free1,
+ liu_syzygies_strat0_free1, liu_initial_strat0_free1, 13);
+}
+
+TEST(GB, weispfennig97_0_4) {
+ testGB(4, weispfennig97_ideal, weispfennig97_gb_strat0_free4,
+ weispfennig97_syzygies_strat0_free4, weispfennig97_initial_strat0_free4, 31);
+}
+
+TEST(GB, weispfennig97_0_5) {
+ testGB(5, weispfennig97_ideal, weispfennig97_gb_strat0_free5,
+ weispfennig97_syzygies_strat0_free5, weispfennig97_initial_strat0_free5, 27);
+}
+
+TEST(GB, gerdt93_0_1) {
+ testGB(1, gerdt93_ideal, gerdt93_gb_strat0_free1,
+ gerdt93_syzygies_strat0_free1, gerdt93_initial_strat0_free1, 9);
+}
+
+TEST(GB, gerdt93_0_2) {
+ testGB(2, gerdt93_ideal, gerdt93_gb_strat0_free2,
+ gerdt93_syzygies_strat0_free2, gerdt93_initial_strat0_free2, 7);
+}
+
+TEST(GB, gerdt93_0_3) {
+ testGB(3, gerdt93_ideal, gerdt93_gb_strat0_free3,
+ gerdt93_syzygies_strat0_free3, gerdt93_initial_strat0_free3, 9);
+}
+
+TEST(GB, gerdt93_0_4) {
+ testGB(4, gerdt93_ideal, gerdt93_gb_strat0_free4,
+ gerdt93_syzygies_strat0_free4, gerdt93_initial_strat0_free4, 7);
+}
+TEST(GB, gerdt93_0_5) {
+ testGB(5, gerdt93_ideal, gerdt93_gb_strat0_free5,
+ gerdt93_syzygies_strat0_free5, gerdt93_initial_strat0_free5, 7);
+}
+
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/test/gtestInclude.cpp b/src/test/gtestInclude.cpp
new file mode 100755
index 0000000..a14f48f
--- /dev/null
+++ b/src/test/gtestInclude.cpp
@@ -0,0 +1,10 @@
+// Includes a file from gtest that pulls in all of the implementation
+// of gtest. The gtest docs recommend building gtest individually for
+// each program rather than using an installed gtest and this is as
+// easy a way of doing it as any. Especially because it guarantees that
+// the compiler flags are the same, which is the whole point of the
+// recommendation to build gtest for each program.
+
+// the .. goes back from the include/ directory of gtest so we can
+// enter the src directory.
+#include <../src/gtest-all.cc>
diff --git a/src/test/ideals.cpp b/src/test/ideals.cpp
new file mode 100755
index 0000000..c12b272
--- /dev/null
+++ b/src/test/ideals.cpp
@@ -0,0 +1,266 @@
+#include "mathicgb/stdinc.h"
+#include "test/ideals.hpp"
+
+const char* idealSmall =
+"32003 6 \
+1 1 1 1 1 1 1 \
+3 \
+-bc+ad \
+-b2+af \
+-bc2+a2e";
+
+const char* idealSmallBasis = "\
+0 <0> bc-ad\n\
+1 <1> b2-af\n\
+2 <2> bc2-a2e\n\
+3 c<0> acd-a2e\n\
+4 b<0> abd-acf\n\
+5 c2<1> a2be-ac2f\n\
+6 bc<0> a2d2-ac2f\n\
+7 c3<1> a3de-ac3f\n\
+8 c4<1> a4e2-ac4f\n\
+";
+
+const char* idealSmallSyzygies =
+ " 0: b2 bc2 \n 1: c2d bc2 \n";
+
+const char* idealSmallInitial =
+ " bc\n b2\n acd\n abd\n a2be\n a2d2\n a3de\n a4e2\n";
+
+const char* liu_ideal =
+"2 6 \
+1 1 1 1 1 1 1 \
+4 \
+bc+bd+af+ef \
+ac+cd+bf+ef \
+ad+bd+cf+ef \
+ab+ac+df+ef \
+";
+
+const char* liu_gb_strat0_free1 =
+"\
+0 <0> bc+bd+af+ef\n\
+1 <1> ac+cd+bf+ef\n\
+2 <2> ad+bd+cf+ef\n\
+3 <3> ab+ac+df+ef\n\
+4 c<2> bd2+cd2+c2f+cef+cf2+ef2\n\
+5 b<2> b2d+cd2+d2f+bef+af2+ef2\n\
+6 b<1> c2d+cd2+b2f+c2f+cdf+bef+cef+def+af2+ef2\n\
+7 a<0> a2f+b2f+c2f+d2f+aef+bef+cef+def\n\
+8 ad<0> cd2f+d3f+cdef+d2ef+b2f2+c2f2+bdf2+aef2+def2+e2f2+bf3+ef3\n\
+9 c2<2> c3f+d3f+c2ef+bdef+cdef+d2ef+b2f2+c2f2+d2f2+aef2+bef2+e2f2+af3+bf3\n\
+10 b2<1> b3f+d3f+b2ef+d2ef+b2f2+c2f2+bef2+cef2+bf3+df3\n\
+11 c2d<2> b2ef2+d2ef2+be2f2+de2f2+b2f3+d2f3+aef3+cef3+af4+bf4+cf4+df4\n\
+12 c2d2<2> bde2f2+cde2f2+c2ef3+d2ef3+ae2f3+e3f3+c2f4+bdf4+cdf4+d2f4+aef4+def4+df5+ef5\n\
+13 c2d3<2> c2e2f3+d2e2f3+ce3f3+de3f3+c2ef4+d2ef4+be2f4+ce2f4+aef5+cef5+af6+bf6+cf6+df6\n\
+14 c2d4<2> cde2f4+d2e2f4+ae3f4+be3f4+ce3f4+e4f4+ce2f5+e3f5+cdf6+d2f6+aef6+bef6+cef6+e2f6+cf7+ef7\n\
+15 c2d5<2> d2e3f4+de4f4+ae3f5+be3f5+d2ef6+be2f6+aef7+bef7+bf8+df8\n\
+";
+
+const char* liu_syzygies_strat0_free1 =
+ " 0: ac ab a2d \n 1: ab b2d b2c \n 2: bc ac ab c3d \n";
+
+const char* liu_initial_strat0_free1 =
+ " ad\n bc\n ac\n ab\n a2f\n bd2\n c2d\n b2d\n cd2f\n c3f\n b3f\n b2ef2\n bde2f2\n c2e2f3\n cde2f4\n d2e3f4\n";
+
+const char* weispfennig97_ideal =
+"7583 4 1 1 1 1 1 \
+3 \
+b4+ab2c+a2d2-2abd2+b2d2+c2d2 \
+a2b2c-bc4+a3d2+ab2d2+ac2d2+3d5 \
+a3b2-abc3+a2d3+b2d3+c2d3 \
+";
+
+const char* weispfennig97_gb_strat0_free4 = "\
+0 <0> b4+ab2c+a2d2-2abd2+b2d2+c2d2\n\
+1 <1> a2b2c-bc4+a3d2+ab2d2+ac2d2+3d5\n\
+2 <2> a3b2-abc3+a2d3+b2d3+c2d3\n\
+3 c<2> a4d2+a2b2d2+a2c2d2-a2cd3-b2cd3-c3d3+3ad5\n\
+4 b2<1> b3c4+abc5-2a3bcd2-ab2c2d2-abc3d2+bc4d2-2a2bd4+a2d5-2b2d5-3acd5+c2d5-3d7\n\
+5 b2<2> ab3c3+a2bc4+2a2b3d2+2a2bc2d2-a2b2d3-2a2bcd3+ab2cd3-2b3cd3-b2c2d3-2bc3d3-2a2d5+4abd5+b2d5+c2d5\n\
+6 b3<1> ab3c2d2+ab2c3d2+a2c4d2+c6d2+2a2b2d4-a2bd5+2b3d5-2a2cd5+3abcd5-2b2cd5-bc2d5-2c3d5+3bd7\n\
+7 b2c<2> a2bc3d2-abc4d2+bc5d2+ab2c2d3-b2c3d3-bc4d3+2a3bd4-a3cd4-ab2cd4-ac3d4+3ab2d5+a2cd5-2abcd5+b2cd5+c3d5+3ad7-3cd7+3d8\n\
+8 b3<2> a3c3d2+ab2c3d2+ac5d2+a2b3d3-ab3cd3+b3c2d3-2a3d5+2a2bd5-b3d5-2ac2d5-bc2d5\n\
+9 ab2<2> a3bc4+b2c6+a3bc2d2+3ab2c3d2+a2c4d2-abc4d2+c6d2-2a3bcd3-2ab3cd3-ab2c2d3-3abc3d3+bc4d3+2a2b2d4-3a3d5+a2bd5-2a2cd5+3abcd5-2b2cd5-6bc2d5-2c3d5+a2d6+b2d6+c2d6+3bd7-3d8\n\
+10 b3c<1> a2bc4d2-ab2c4d2-a2c5d2-c7d2+2a2b3d4+2a2bc2d4-2bc4d4-a2b2d5-a2bcd5+ab2cd5-4b3cd5+2a2c2d5-3abc2d5+b2c2d5-bc3d5+2c4d5+2a3d6+2ab2d6+2ac2d6-2a2d7+4abd7+b2d7-3bcd7+c2d7+6d9\n\
+11 b2c2<2> ab2c4d2+a2c5d2-abc5d2+bc6d2+c7d2+ab2c3d3-b2c4d3-bc5d3-2a2b3d4+2a3bcd4-a3c2d4-2a2bc2d4-ab2c2d4-ac4d4+2bc4d4+a2b2d5+a2bcd5+2ab2cd5+4b3cd5-a2c2d5+abc2d5+bc3d5-c4d5-2a3d6-2ab2d6-2ac2d6+2a2d7-4abd7-b2d7+3acd7+3bcd7-4c2d7+3cd8-6d9\n\
+12 ab3<1> a3c4d2+b2c5d2+ac6d2+bc6d2-a3bcd4-ab3cd4-a3c2d4-ab2c2d4+abc3d4-ac4d4-a3bd5+2ab3d5-2a3cd5+3a2bcd5-2ab2cd5-abc2d5-2ac3d5-2a2d7+3abd7-2b2d7-3bcd7-5c2d7\n\
+13 b3c<2> a2c5d2-abc5d2+b2c5d2+2bc6d2+c7d2-b3c3d3-a2c4d3-2b2c4d3-bc5d3-c6d3-2a2b3d4+a3bcd4-ab3cd4-2a3c2d4-2a2bc2d4-2ab2c2d4+abc3d4-2ac4d4+2bc4d4-a2b2d5+3ab3d5+2a2bcd5+5b3cd5-a2c2d5+abc2d5+2bc3d5-c4d5-2a3d6+a2bd6-2ab2d6-2b3d6+2a2cd6-3abcd6+2b2cd6-2ac2d6+bc2d6+2c3d6-abd7-3b2d7+3acd7-9c2d7+3cd8-6d9\n\
+14 a2b2<1> a3bc5+b2c7+2b2c5d2-2a3bc2d3-2ab2c3d3-2b3c3d3-2abc4d3+2bc5d3+4a2b3d4-6a3bcd4-2ab3cd4+4a2bc2d4+2abc3d4-4a2b2d5+6ab3d5-3a3cd5+2a2bcd5-9ab2cd5-4b3cd5-a2c2d5+4abc2d5-2b2c2d5-7bc3d5-c4d5+a2cd6+b2cd6+c3d6-9a2d7+16abd7-3b2d7-6acd7-6bcd7-3c2d7-3ad8+6bd8-6cd8\n\
+15 ab3<2> a3bd5-ac3d5-b3d6-abcd6+2ad8\n\
+16 ab2c2<2> b2c6d2-bc7d2+abc5d3+b2c5d3-a3bc2d4+3ab2c3d4+a2c4d4+abc4d4+2b2c4d4-2bc5d4+c6d4-a2b3d5-2a3bcd5-ab3cd5-6ab2c2d5-3b3c2d5-abc3d5+2b2c3d5-2bc4d5+2a2b2d6-2ab3d6+2a3cd6+2ab2cd6-2abc2d6-4ac3d6+2a3d7-a2bd7-3ab2d7-5b3d7-7a2cd7+7abcd7-4b2cd7+2ac2d7-5bc2d7-c3d7+a2d8+b2d8-3acd8+c2d8+6ad9-3bd9+6cd9\n\
+17 a2b3<1> abc6d2+bc7d2-2b2c5d3-bc6d3-ab2c3d4+b3c3d4+a2c4d4+2abc4d4-b2c4d4+c6d4+4a2b3d5+4a3bcd5+5ab3cd5+a3c2d5+2ab2c2d5+3b3c2d5-a2c3d5-abc3d5-b2c3d5-2ac4d5+3bc4d5-c5d5+2a2b2d6-ab3d6-a2bcd6+2ab2cd6-4b3cd6-abc2d6+2ac3d6-bc3d6-2a3d7-3ab2d7+5b3d7-a2cd7+2abcd7-4b2cd7-2ac2d7-7c3d7+2a2d8-b2d8+6acd8+3bcd8+5c2d8-4ad9+6bd9-3d10\n\
+18 a2b3<2> a2b3d5+a2bc2d5+a2c3d5+ab3d6-b3cd6-bc3d6-2a2d8+3abd8\n\
+19 bc5<2> a2bc6d2-ab2c6d2+b2c7d2+2abc5d4+2b2c5d4-4bc6d4-5ab2c3d5-2b3c3d5-a2c4d5+b2c4d5+4bc5d5-c6d5-4ab3cd6+3a3c2d6+a2bc2d6+6ab2c2d6+3b3c2d6-3abc3d6+b2c3d6-4ac4d6+2bc4d6+c5d6-3a2b2d7+4ab3d7-a3cd7-3a2bcd7-8ab2cd7-5b3cd7-6a2c2d7+5abc2d7-4b2c2d7-3ac3d7-bc3d7-3c4d7-3a3d8+3a2bd8-ab2d8-5b3d8+6a2cd8-11abcd8+3b2cd8-3ac2d8+3c3d8+a2d9-2abd9-b2d9+8acd9-9bcd9+11c2d9+ad10-15cd10\n\
+20 b3c3<1> a2c7d2+b2c7d2+c9d2-4ab2c3d5+2b3c3d5-3a2c4d5+4abc4d5-2b2c4d5+3bc5d5-3c6d5-2ab3cd6-a3c2d6+a2bc2d6+ab2c2d6+3b3c2d6-abc3d6+2b2c3d6-2ac4d6+3bc4d6+c5d6-3a2b2d7+4ab3d7-a3cd7-3a2bcd7-2ab2cd7+b3cd7-2a2c2d7+3abc2d7-3b2c2d7-5ac3d7+2bc3d7-2c4d7-3a3d8+3a2bd8-4ab2d8-7b3d8+5a2cd8-11abcd8+2b2cd8-3ac2d8+2c3d8+a2d9-2abd9-b2d9+2acd9-3bcd9-c2d9+2ad10-9cd10-3d11\n\
+21 b3c3<2> abc7d2-2bc8d2-abc5d4+2b2c5d4-3bc6d4+2a3bc2d5-7ab2c3d5-4b3c3d5-2a2c4d5+abc4d5-2b2c4d5+3bc5d5-2c6d5-3ab3cd6+3a3c2d6-a2bc2d6+12ab2c2d6+b3c2d6+2a2c3d6-2b2c3d6+3ac4d6+6ab3d7+2a3cd7-6a2bcd7-4ab2cd7+2b3cd7-4a2c2d7+9abc2d7-2b2c2d7+ac3d7+2bc3d7+5c4d7+a2bd8+2b3d8+9a2cd8-14abcd8+9b2cd8+6bc2d8+6c3d8-2a2d9-2abd9+2b2d9+3acd9-9bcd9+8c2d9-ad10+3bd10-15cd10+3d11\n\
+22 a2b3c<1> bc8d2-bc7d3-2526abc5d4-b2c5d4+2531ab2c3d5+3b3c3d5+4b2c4d5-ac5d5-bc5d5+ab3cd6+a2bc2d6-6ab2c2d6-4b3c2d6-2528a2c3d6-2529abc3d6+2b2c3d6-2bc4d6-a2b2d7-2534ab3d7+a2bcd7+2530ab2cd7-2b3cd7+2529a2c2d7-6abc2d7+2527b2c2d7-5ac3d7-2bc3d7+2524c4d7+2a3d8-2ab2d8-7b3d8-5a2cd8+5abcd8-4b2cd8+4ac2d8-3bc2d8+c3d8+a2d9+abd9+2530b2d9-3acd9+6bcd9+2530c2d9+7ad10-12bd10+7cd10\n\
+23 a3b3<1> a2c4d5+abc4d5+b2c4d5-bc5d5+ab3cd6-ab2c2d6-b3c2d6+b2c3d6-ab3d7+a3cd7+ab2cd7-abc2d7-2ac3d7-3ab2d8-3b3d8-3a2cd8+2abcd8-b2cd8-c3d8+3ad10-3bd10+3cd10-3d11\n\
+24 abc5<2> abc8d2-2bc9d2+abc5d5-11b2c5d5-3bc6d5+12ab2c3d6+7b3c3d6-8abc4d6-11b2c4d6+2ac5d6+12bc5d6+7c6d6+12ab3cd7+8a3c2d7-9a2bc2d7+21ab2c2d7+19b3c2d7-8a2c3d7+3abc3d7-17b2c3d7+9ac4d7+16bc4d7+4c5d7+10a2b2d8-6ab3d8-14a3cd8-3a2bcd8-4ab2cd8+5b3cd8+7a2c2d8+9abc2d8+9b2c2d8+29ac3d8+bc3d8+4c4d8-4a3d9-20a2bd9+29ab2d9+63b3d9+23a2cd9-8abcd9-5ac2d9-4bc2d9-3c3d9+18a2d10-20abd10-3b2d10-11acd10+27bcd10+15c2d10-50ad11+48bd11-33cd11+3d12\n\
+25 ab3c3<2> ac6d5+bc6d5+b3c3d6-b2c4d6-a3c2d7-ab2c2d7+2a2c3d7+ac4d7+3a2b2d8+2ab3d8+3ab2cd8+2b3cd8-2ac3d8-a2d10-acd10-3c2d10+3ad11\n\
+26 a3b3c<1> abc5d5+3790bc6d5+3791c7d5-ab2c3d6-3790b2c4d6+3792bc5d6-3790a3c2d7-3790ab2c2d7-a2c3d7-abc3d7+3791ac4d7-bc4d7+3791a2b2d8+3789ab3d8-a2bcd8+3790ab2cd8+3788b3cd8-a2c2d8+3791b2c2d8+a3d9+ab2d9+ac2d9+2a2d10+3789abd10-3790b2d10+acd10+3790bcd10+6c2d10+3790bd11-3cd11+3d12\n\
+27 a2bc5<2> bc10d2+2540bc7d5+2521b2c5d6-2535bc6d6-846c7d6+2511ab2c3d7+2523b3c3d7+2509abc4d7-17b2c4d7+3ac5d7+852bc5d7-2525c6d7+3ab3cd8+2534a3c2d8-2536a2bc2d8+2570ab2c2d8+2569b3c2d8+1669a2c3d8-3371abc3d8-2554b2c3d8-3372ac4d8-1668bc4d8-3c5d8-3357a2b2d9-841ab3d9-2548a3cd9+856a2bcd9+1663ab2cd9+1675b3cd9+2518a2c2d9+2566abc2d9+845b2c2d9-2473ac3d9+2538bc3d9+1688c4d9-3392a3d10-2530a2bd10-792ab2d10+85b3d10+40a2cd10+2517abcd10+2536b2cd10-3395ac2d10+2528bc2d10-2559c3d10-1654a2d11+793abd11-855b2d11- [...]
+28 a2b3c3<1> ac7d5+bc7d5-b2c5d6+3790bc6d6+3791c7d6-ab2c3d7-2abc4d7+3791b2c4d7+2ac5d7-3789bc5d7-ab3cd8-3790a3c2d8-a2bc2d8-3784ab2c2d8+5b3c2d8-2a2c3d8-2b2c3d8+3791ac4d8+bc4d8+3791a2b2d9+3790ab3d9-2a3cd9-a2bcd9+3788ab2cd9+3791b3cd9-a2c2d9+4abc2d9+3791b2c2d9+4ac3d9+bc3d9-4a3d10+4a2bd10+4ab2d10+5b3d10+5a2cd10-4abcd10+2b2cd10-5ac2d10-bc2d10-c3d10+3a2d11+3786abd11-3788b2d11+3acd11+3790bcd11+5c2d11-6ad12-3787bd12-9cd12+3d13\n\
+29 a3b3c2<1> bc7d5-3033c8d5-b2c5d6+3033bc6d6-3034ab2c3d7+3034abc4d7+3032b2c4d7+3034ac5d7+3034bc5d7-1515ab3cd8-3035a2bc2d8+1518ab2c2d8-1513b3c2d8+3031a2c3d8+1516b2c3d8+1518bc4d8-1517c5d8-ab3d9-3034a3cd9-3034ab2cd9-3031b3cd9-3032abc2d9-3032ac3d9-3031bc3d9+3031a3d10-1515a2bd10+3033ab2d10+3035b3d10+3034a2cd10+3033abcd10-b2cd10+1514ac2d10+1517bc2d10-4c3d10+1521a2d11-1522abd11+3032b2d11-1516bcd11-3032c2d11-3036ad12-1513bd12-1519cd12+1516d13\n\
+30 a3b3c3<1> c9d5-3033c8d6-b2c5d7+3030bc6d7+2528c7d7-3032ab2c3d8+3b3c3d8+3040abc4d8+3036b2c4d8+3034ac5d8+507bc5d8-c6d8-1516ab3cd9+3a3c2d9-3035a2bc2d9+1520ab2c2d9-1512b3c2d9-2025a2c3d9+2527abc3d9+1517b2c3d9+2528ac4d9-1007bc4d9-1517c5d9+2523a2b2d10+2525ab3d10-3034a3cd10-2528a2bcd10-511ab2cd10-505b3cd10-2a2c2d10-3029abc2d10-2529b2c2d10-3043ac3d10-3028bc3d10+2526c4d10-2025a3d11-1516a2bd11-2031ab2d11+3025b3d11+3033a2cd11+3024abcd11-3542ac2d11+1514bc2d11-c3d11-1006a2d12+3533abd12-2023b2d12+25 [...]
+";
+
+const char* weispfennig97_syzygies_strat0_free4 =
+ " 1: b4 a4b3 \n 2: b4 a2b2c a3b3 b3c4 a3bc5 \n";
+
+const char* weispfennig97_initial_strat0_free4 =
+ " b4\n a2b2c\n a3b2\n a4d2\n b3c4\n ab3c3\n a2bc3d2\n a3c3d2\n ab3c2d2\n a3bc4\n a3bd5\n a2c5d2\n ab2c4d2\n a2b3d5\n b2c6d2\n abc6d2\n a2c4d5\n bc8d2\n ac6d5\n abc5d5\n bc7d5\n c9d5\n";
+
+const char* weispfennig97_gb_strat0_free5 = "\
+0 <0> b4+ab2c+a2d2-2abd2+b2d2+c2d2\n\
+1 <1> a2b2c-bc4+a3d2+ab2d2+ac2d2+3d5\n\
+2 <2> a3b2-abc3+a2d3+b2d3+c2d3\n\
+3 a<1> a4d2+a2b2d2+a2c2d2-a2cd3-b2cd3-c3d3+3ad5\n\
+4 a2c<0> b3c4+abc5-2a3bcd2-ab2c2d2-abc3d2+bc4d2-2a2bd4+a2d5-2b2d5-3acd5+c2d5-3d7\n\
+5 a3<0> ab3c3+a2bc4+2a2b3d2+2a2bc2d2-a2b2d3-2a2bcd3+ab2cd3-2b3cd3-b2c2d3-2bc3d3-2a2d5+4abd5+b2d5+c2d5\n\
+6 a2bc<0> ab3c2d2+ab2c3d2+a2c4d2+c6d2+2a2b2d4-a2bd5+2b3d5-2a2cd5+3abcd5-2b2cd5-bc2d5-2c3d5+3bd7\n\
+7 ab2<1> a2bc3d2-abc4d2+bc5d2+ab2c2d3-b2c3d3-bc4d3+2a3bd4-a3cd4-ab2cd4-ac3d4+3ab2d5+a2cd5-2abcd5+b2cd5+c3d5+3ad7-3cd7+3d8\n\
+8 a3b<0> a3c3d2+ab2c3d2+ac5d2+a2b3d3-ab3cd3+b3c2d3-2a3d5+2a2bd5-b3d5-2ac2d5-bc2d5\n\
+9 a4<0> a3bc4+b2c6+a3bc2d2+3ab2c3d2+a2c4d2-abc4d2+c6d2-2a3bcd3-2ab3cd3-ab2c2d3-3abc3d3+bc4d3+2a2b2d4-3a3d5+a2bd5-2a2cd5+3abcd5-2b2cd5-6bc2d5-2c3d5+a2d6+b2d6+c2d6+3bd7-3d8\n\
+10 a2bc2<0> a2bc4d2-ab2c4d2-a2c5d2-c7d2+2a2b3d4+2a2bc2d4-2bc4d4-a2b2d5-a2bcd5+ab2cd5-4b3cd5+2a2c2d5-3abc2d5+b2c2d5-bc3d5+2c4d5+2a3d6+2ab2d6+2ac2d6-2a2d7+4abd7+b2d7-3bcd7+c2d7+6d9\n\
+11 ab2c<1> ab2c4d2+a2c5d2-abc5d2+bc6d2+c7d2+ab2c3d3-b2c4d3-bc5d3-2a2b3d4+2a3bcd4-a3c2d4-2a2bc2d4-ab2c2d4-ac4d4+2bc4d4+a2b2d5+a2bcd5+2ab2cd5+4b3cd5-a2c2d5+abc2d5+bc3d5-c4d5-2a3d6-2ab2d6-2ac2d6+2a2d7-4abd7-b2d7+3acd7+3bcd7-4c2d7+3cd8-6d9\n\
+12 ab3<1> a2c5d2-abc5d2+b2c5d2+2bc6d2+c7d2-b3c3d3-a2c4d3-2b2c4d3-bc5d3-c6d3-2a2b3d4+a3bcd4-ab3cd4-2a3c2d4-2a2bc2d4-2ab2c2d4+abc3d4-2ac4d4+2bc4d4-a2b2d5+3ab3d5+2a2bcd5+5b3cd5-a2c2d5+abc2d5+2bc3d5-c4d5-2a3d6+a2bd6-2ab2d6-2b3d6+2a2cd6-3abcd6+2b2cd6-2ac2d6+bc2d6+2c3d6-abd7-3b2d7+3acd7-9c2d7+3cd8-6d9\n\
+13 a4b<0> a3bd5-ac3d5-b3d6-abcd6+2ad8\n\
+14 a2b2c<1> b2c6d2-bc7d2+abc5d3+b2c5d3-a3bc2d4+3ab2c3d4+a2c4d4+abc4d4+2b2c4d4-2bc5d4+c6d4-a2b3d5-2a3bcd5-ab3cd5-6ab2c2d5-3b3c2d5-abc3d5+2b2c3d5-2bc4d5+2a2b2d6-2ab3d6+2a3cd6+2ab2cd6-2abc2d6-4ac3d6+2a3d7-a2bd7-3ab2d7-5b3d7-7a2cd7+7abcd7-4b2cd7+2ac2d7-5bc2d7-c3d7+a2d8+b2d8-3acd8+c2d8+6ad9-3bd9+6cd9\n\
+15 a2b3<1> abc6d2+bc7d2-2b2c5d3-bc6d3-ab2c3d4+b3c3d4+a2c4d4+2abc4d4-b2c4d4+c6d4+4a2b3d5+a3bcd5+5ab3cd5+a3c2d5+2ab2c2d5+3b3c2d5-a2c3d5-abc3d5-b2c3d5+ac4d5+3bc4d5-c5d5+2a2b2d6-ab3d6-a2bcd6+2ab2cd6-b3cd6+2abc2d6+2ac3d6-bc3d6-2a3d7-3ab2d7+5b3d7-a2cd7+2abcd7-4b2cd7-2ac2d7-7c3d7+2a2d8-b2d8+3bcd8+5c2d8-4ad9+6bd9-3d10\n\
+16 a5b<0> a2b3d5+a2bc2d5+a2c3d5+ab3d6-b3cd6-bc3d6-2a2d8+3abd8\n\
+17 abc4<1> a2bc6d2-ab2c6d2+b2c7d2+2abc5d4+2b2c5d4-4bc6d4-5ab2c3d5-2b3c3d5-a2c4d5+b2c4d5+4bc5d5-c6d5-4ab3cd6+3a3c2d6+a2bc2d6+6ab2c2d6+3b3c2d6-3abc3d6+b2c3d6-4ac4d6+2bc4d6+c5d6-3a2b2d7+4ab3d7-a3cd7-3a2bcd7-8ab2cd7-5b3cd7-6a2c2d7+5abc2d7-4b2c2d7-3ac3d7-bc3d7-3c4d7-3a3d8+3a2bd8-ab2d8-5b3d8+6a2cd8-11abcd8+3b2cd8-3ac2d8+3c3d8+a2d9-2abd9-b2d9+8acd9-9bcd9+11c2d9+ad10-15cd10\n\
+18 a2bc4<0> a2c7d2+b2c7d2+c9d2-4ab2c3d5+2b3c3d5-3a2c4d5+4abc4d5-2b2c4d5+3bc5d5-3c6d5-2ab3cd6-a3c2d6+a2bc2d6+ab2c2d6+3b3c2d6-abc3d6+2b2c3d6-2ac4d6+3bc4d6+c5d6-3a2b2d7+4ab3d7-a3cd7-3a2bcd7-2ab2cd7+b3cd7-2a2c2d7+3abc2d7-3b2c2d7-5ac3d7+2bc3d7-2c4d7-3a3d8+3a2bd8-4ab2d8-7b3d8+5a2cd8-11abcd8+2b2cd8-3ac2d8+2c3d8+a2d9-2abd9-b2d9+2acd9-3bcd9-c2d9+2ad10-9cd10-3d11\n\
+19 ab3c2<1> abc7d2-2bc8d2-abc5d4+2b2c5d4-3bc6d4+2a3bc2d5-7ab2c3d5-4b3c3d5-2a2c4d5+abc4d5-2b2c4d5+3bc5d5-2c6d5-3ab3cd6+3a3c2d6-a2bc2d6+12ab2c2d6+b3c2d6+2a2c3d6-2b2c3d6+3ac4d6+6ab3d7+2a3cd7-6a2bcd7-4ab2cd7+2b3cd7-4a2c2d7+9abc2d7-2b2c2d7+ac3d7+2bc3d7+5c4d7+a2bd8+2b3d8+9a2cd8-14abcd8+9b2cd8+6bc2d8+6c3d8-2a2d9-2abd9+2b2d9+3acd9-9bcd9+8c2d9-ad10+3bd10-15cd10+3d11\n\
+20 a2b3c<1> bc8d2-bc7d3-2526abc5d4-b2c5d4-a3bc2d5+2531ab2c3d5+3b3c3d5+4b2c4d5-bc5d5+ab3cd6+a2bc2d6-6ab2c2d6-3b3c2d6-2528a2c3d6-2528abc3d6+2b2c3d6-2bc4d6-a2b2d7-2534ab3d7+a2bcd7+2530ab2cd7-2b3cd7+2529a2c2d7-6abc2d7+2527b2c2d7-5ac3d7-2bc3d7+2524c4d7+2a3d8-2ab2d8-7b3d8-5a2cd8+5abcd8-4b2cd8+2ac2d8-3bc2d8+c3d8+a2d9+abd9+2530b2d9-3acd9+6bcd9+2530c2d9+7ad10-12bd10+7cd10\n\
+21 a5bc<0> a2c4d5+abc4d5+b2c4d5-bc5d5+ab3cd6-ab2c2d6-b3c2d6+b2c3d6-ab3d7+a3cd7+ab2cd7-abc2d7-2ac3d7-3ab2d8-3b3d8-3a2cd8+2abcd8-b2cd8-c3d8+3ad10-3bd10+3cd10-3d11\n\
+22 a2bc4<1> abc8d2-2bc9d2+abc5d5-11b2c5d5-3bc6d5+12ab2c3d6+7b3c3d6-8abc4d6-11b2c4d6+2ac5d6+12bc5d6+7c6d6+12ab3cd7+8a3c2d7-9a2bc2d7+21ab2c2d7+19b3c2d7-8a2c3d7+3abc3d7-17b2c3d7+9ac4d7+16bc4d7+4c5d7+10a2b2d8-6ab3d8-14a3cd8-3a2bcd8-4ab2cd8+5b3cd8+7a2c2d8+9abc2d8+9b2c2d8+29ac3d8+bc3d8+4c4d8-4a3d9-20a2bd9+29ab2d9+63b3d9+23a2cd9-8abcd9-5ac2d9-4bc2d9-3c3d9+18a2d10-20abd10-3b2d10-11acd10+27bcd10+15c2d10-50ad11+48bd11-33cd11+3d12\n\
+23 a4bc3<0> ac6d5+bc6d5+b3c3d6-b2c4d6-a3c2d7-ab2c2d7+2a2c3d7+ac4d7+3a2b2d8+2ab3d8+3ab2cd8+2b3cd8-2ac3d8-a2d10-acd10-3c2d10+3ad11\n\
+24 a5bc2<0> abc5d5+3790bc6d5+3791c7d5-ab2c3d6-3790b2c4d6+3792bc5d6-3790a3c2d7-3790ab2c2d7-a2c3d7-abc3d7+3791ac4d7-bc4d7+3791a2b2d8+3789ab3d8-a2bcd8+3790ab2cd8+3788b3cd8-a2c2d8+3791b2c2d8+a3d9+ab2d9+ac2d9+2a2d10+3789abd10-3790b2d10+acd10+3790bcd10+6c2d10+3790bd11-3cd11+3d12\n\
+25 a3bc4<1> bc10d2+2540bc7d5+2521b2c5d6-2535bc6d6-846c7d6+2511ab2c3d7+2523b3c3d7+2509abc4d7-17b2c4d7+3ac5d7+852bc5d7-2525c6d7+3ab3cd8+2534a3c2d8-2536a2bc2d8+2570ab2c2d8+2569b3c2d8+1669a2c3d8-3371abc3d8-2554b2c3d8-3372ac4d8-1668bc4d8-3c5d8-3357a2b2d9-841ab3d9-2548a3cd9+856a2bcd9+1663ab2cd9+1675b3cd9+2518a2c2d9+2566abc2d9+845b2c2d9-2473ac3d9+2538bc3d9+1688c4d9-3392a3d10-2530a2bd10-792ab2d10+85b3d10+40a2cd10+2517abcd10+2536b2cd10-3395ac2d10+2528bc2d10-2559c3d10-1654a2d11+793abd11-855b2d11- [...]
+26 a5bc3<0> bc7d5-3033c8d5-b2c5d6+3033bc6d6-3034ab2c3d7+3034abc4d7+3032b2c4d7+3034ac5d7+3034bc5d7-1515ab3cd8-3035a2bc2d8+1518ab2c2d8-1513b3c2d8+3031a2c3d8+1516b2c3d8+1518bc4d8-1517c5d8-ab3d9-3034a3cd9-3034ab2cd9-3031b3cd9-3032abc2d9-3032ac3d9-3031bc3d9+3031a3d10-1515a2bd10+3033ab2d10+3035b3d10+3034a2cd10+3033abcd10-b2cd10+1514ac2d10+1517bc2d10-4c3d10+1521a2d11-1522abd11+3032b2d11-1516bcd11-3032c2d11-3036ad12-1513bd12-1519cd12+1516d13\n\
+27 a5bc4<0> c9d5-3033c8d6-b2c5d7+3030bc6d7+2528c7d7-3032ab2c3d8+3b3c3d8+3040abc4d8+3036b2c4d8+3034ac5d8+507bc5d8-c6d8-1516ab3cd9+3a3c2d9-3035a2bc2d9+1520ab2c2d9-1512b3c2d9-2025a2c3d9+2527abc3d9+1517b2c3d9+2528ac4d9-1007bc4d9-1517c5d9+2523a2b2d10+2525ab3d10-3034a3cd10-2528a2bcd10-511ab2cd10-505b3cd10-2a2c2d10-3029abc2d10-2529b2c2d10-3043ac3d10-3028bc3d10+2526c4d10-2025a3d11-1516a2bd11-2031ab2d11+3025b3d11+3033a2cd11+3024abcd11-3542ac2d11+1514bc2d11-c3d11-1006a2d12+3533abd12-2023b2d12+252 [...]
+";
+
+const char* weispfennig97_syzygies_strat0_free5 =
+ " 0: a2b2c a3b2 a6b \n 1: ab4 a3b2 ab3c3 a4bc4 \n";
+
+const char* weispfennig97_initial_strat0_free5 =
+ " b4\n a2b2c\n a3b2\n a4d2\n b3c4\n ab3c3\n a2bc3d2\n a3c3d2\n ab3c2d2\n a3bc4\n a3bd5\n a2c5d2\n ab2c4d2\n a2b3d5\n b2c6d2\n abc6d2\n a2c4d5\n bc8d2\n ac6d5\n abc5d5\n bc7d5\n c9d5\n";
+
+const char* gerdt93_ideal =
+ "7583 6 1 1 1 1 1 1 1\n\
+ 3\n\
+ ab-b2-4bc+ae\n\
+ a2c-6bc2+a2f\n\
+ a3+b2c-a2d\n\
+ ";
+
+const char* gerdt93_gb_strat0_free1 = "\
+0 <0> ab-b2-4bc+ae\n\
+1 <1> a2c-6bc2+a2f\n\
+2 <2> a3+b2c-a2d\n\
+3 a<1> abc2+1264b2c2-bc2d+1264b2cf\n\
+4 c2<0> b2c2+2170bc3+3249bc2d+3249ac2e+3250b2cf\n\
+5 ac<0> b3c+3259bc3+1085bc2d-b2ce+1081ac2e-10bc2e+ace2+b3f+1091b2cf+16bc2f-b2ef-4acef-4bcef+ae2f\n\
+6 a2<1> b3c2-216b2c3-12b2c2d+36bc2d2-6ab2cf+b3cf+216b2c2f-6b2cdf-36a2bf2\n\
+7 a2<0> b4-2386bc3-b3d-8b2cd-3247bc2d-2b3e-6b2ce-3195ac2e+58bc2e+b2de+4acde+4bcde+a2e2+2b2e2-ace2+8bce2-ade2-2ae3-13b3f-3357b2cf-208bc2f+4a2ef+13b2ef+52acef+52bcef-13ae2f\n\
+8 bc2<0> bc4-563bc3d-2577bc2d2-1896ac3e-469bc3e-2229ac2de+3088bc2de+2013ac2e2-1829bc3f-3362b2cdf-2179bc2df+3088b2cef+2353ac2ef+3515bc2ef-1075ace2f-785b3f2+2930b2cf2+2606bc2f2-348a2ef2+785b2ef2+3140acef2+3140bcef2-785ae2f2\n\
+9 ac2<0> bc3e-3140ac2de-1718bc2de+2814ac2e2-1718b2cef+947bc2ef-3051ace2f+3140a2ef2\n\
+10 ac3<0> ac3de+1155ac2d2e-2873bc2d2e+3790ac3e2-1570ac2de2+3362bc2de2-3088ac2e3+2814b2cdef+2370bc2def+3362b2ce2f-3790ac2e2f-1659bc2e2f-3599acde2f+1133ace3f-1896b3ef2-1898b2cef2-10bc2ef2-1155a2def2+2681a2e2f2+1896b2e2f2+ace2f2+bce2f2-1896ae3f2+a2ef3\n\
+";
+
+const char* gerdt93_syzygies_strat0_free1 = "\
+ 0: a2c a3 b2c2 abc2 \n\
+ 1: a3 \n\
+";
+
+const char* gerdt93_initial_strat0_free1 =
+ " ab\n a2c\n a3\n b2c2\n b3c\n b4\n bc3e\n bc4\n ac3de\n";
+
+const char* gerdt93_gb_strat0_free2 = "\
+0 <0> ab-b2-4bc+ae\n\
+1 <1> a2c-6bc2+a2f\n\
+2 <2> a3+b2c-a2d\n\
+3 b<1> b3c+2b2c2+16bc3-b2ce-4ac2e-10bc2e+ace2+b3f+8b2cf+16bc2f-b2ef-4acef-4bcef+ae2f\n\
+4 c<2> b2c2+2170bc3+3249bc2d+3249ac2e+3250b2cf\n\
+5 b<2> b4-2386bc3-b3d-8b2cd-3247bc2d-2b3e-6b2ce-3195ac2e+58bc2e+b2de+4acde+4bcde+a2e2+2b2e2-ace2+8bce2-ade2-2ae3-13b3f-3357b2cf-208bc2f+4a2ef+13b2ef+52acef+52bcef-13ae2f\n\
+6 bc<2> bc4-563bc3d-2577bc2d2-1896ac3e-2681bc3e-2577ac2de-3362bc2de+3088ac2e2-1829bc3f-3362b2cdf-2179bc2df-3362b2cef+2353ac2ef+1659bc2ef-1133ace2f-785b3f2+2930b2cf2+2606bc2f2+785b2ef2+3140acef2+3140bcef2-785ae2f2\n\
+7 ac<2> bc3e-3140ac2de-1718bc2de+2814ac2e2-1718b2cef+947bc2ef-3051ace2f+3140a2ef2\n\
+8 ac2<2> ac3de+1155ac2d2e-2873bc2d2e+3790ac3e2-1570ac2de2+3362bc2de2-3088ac2e3+2814b2cdef+2370bc2def+3362b2ce2f-3790ac2e2f-1659bc2e2f-3599acde2f+1133ace3f-1896b3ef2-1898b2cef2-10bc2ef2-1155a2def2+2681a2e2f2+1896b2e2f2+ace2f2+bce2f2-1896ae3f2+a2ef3\n\
+";
+
+const char* gerdt93_syzygies_strat0_free2 = "\
+ 1: ab \n\
+ 2: ab b2c a2c \n\
+";
+
+const char* gerdt93_initial_strat0_free2 =
+ " ab\n a2c\n a3\n b2c2\n b3c\n b4\n bc3e\n bc4\n ac3de\n";
+
+const char* gerdt93_gb_strat0_free3 = "\
+0 <0> ab-b2-4bc+ae\n\
+1 <1> a2c-6bc2+a2f\n\
+2 <2> a3+b2c-a2d\n\
+3 a<1> abc2+1264b2c2-bc2d+1264b2cf\n\
+4 c2<0> b2c2+2170bc3+3249bc2d+3249ac2e+3250b2cf\n\
+5 ac<0> b3c+3259bc3+1085bc2d-b2ce+1081ac2e-10bc2e+ace2+b3f+1091b2cf+16bc2f-b2ef-4acef-4bcef+ae2f\n\
+6 a2<0> b4-2386bc3-b3d-8b2cd-3247bc2d-2b3e-6b2ce-3195ac2e+58bc2e+b2de+4acde+4bcde+a2e2+2b2e2-ace2+8bce2-ade2-2ae3-13b3f-3357b2cf-208bc2f+4a2ef+13b2ef+52acef+52bcef-13ae2f\n\
+7 a2<1> b3c2-216b2c3-12b2c2d+36bc2d2-6ab2cf+b3cf+216b2c2f-6b2cdf-36a2bf2\n\
+8 bc2<0> bc4-563bc3d-2577bc2d2-1896ac3e-469bc3e-2229ac2de+3088bc2de+2013ac2e2-1829bc3f-3362b2cdf-2179bc2df+3088b2cef+2353ac2ef+3515bc2ef-1075ace2f-785b3f2+2930b2cf2+2606bc2f2-348a2ef2+785b2ef2+3140acef2+3140bcef2-785ae2f2\n\
+9 ac2<0> bc3e-3140ac2de-1718bc2de+2814ac2e2-1718b2cef+947bc2ef-3051ace2f+3140a2ef2\n\
+10 ac3<0> ac3de+1155ac2d2e-2873bc2d2e+3790ac3e2-1570ac2de2+3362bc2de2-3088ac2e3+2814b2cdef+2370bc2def+3362b2ce2f-3790ac2e2f-1659bc2e2f-3599acde2f+1133ace3f-1896b3ef2-1898b2cef2-10bc2ef2-1155a2def2+2681a2e2f2+1896b2e2f2+ace2f2+bce2f2-1896ae3f2+a2ef3\n\
+";
+
+const char* gerdt93_syzygies_strat0_free3 = "\
+ 0: a2c a3 b2c2 abc2 \n\
+ 1: a3 \n\
+";
+
+const char* gerdt93_initial_strat0_free3 =
+ " ab\n a2c\n a3\n b2c2\n b3c\n b4\n bc3e\n bc4\n ac3de\n";
+
+const char* gerdt93_gb_strat0_free4 = "\
+0 <0> ab-b2-4bc+ae\n\
+1 <1> a2c-6bc2+a2f\n\
+2 <2> a3+b2c-a2d\n\
+3 b<1> b3c+2b2c2+16bc3-b2ce-4ac2e-10bc2e+ace2+b3f+8b2cf+16bc2f-b2ef-4acef-4bcef+ae2f\n\
+4 c<2> b2c2+2170bc3+3249bc2d+3249ac2e+3250b2cf\n\
+5 b<2> b4-2386bc3-b3d-8b2cd-3247bc2d-2b3e-6b2ce-3195ac2e+58bc2e+b2de+4acde+4bcde+a2e2+2b2e2-ace2+8bce2-ade2-2ae3-13b3f-3357b2cf-208bc2f+4a2ef+13b2ef+52acef+52bcef-13ae2f\n\
+6 bc<2> bc4-563bc3d-2577bc2d2-1896ac3e-2681bc3e-2577ac2de-3362bc2de+3088ac2e2-1829bc3f-3362b2cdf-2179bc2df-3362b2cef+2353ac2ef+1659bc2ef-1133ace2f-785b3f2+2930b2cf2+2606bc2f2+785b2ef2+3140acef2+3140bcef2-785ae2f2\n\
+7 ac<2> bc3e-3140ac2de-1718bc2de+2814ac2e2-1718b2cef+947bc2ef-3051ace2f+3140a2ef2\n\
+8 ac2<2> ac3de+1155ac2d2e-2873bc2d2e+3790ac3e2-1570ac2de2+3362bc2de2-3088ac2e3+2814b2cdef+2370bc2def+3362b2ce2f-3790ac2e2f-1659bc2e2f-3599acde2f+1133ace3f-1896b3ef2-1898b2cef2-10bc2ef2-1155a2def2+2681a2e2f2+1896b2e2f2+ace2f2+bce2f2-1896ae3f2+a2ef3\n\
+";
+
+const char* gerdt93_syzygies_strat0_free4 = "\
+ 1: ab \n\
+ 2: ab b2c a2c \n\
+";
+
+const char* gerdt93_initial_strat0_free4 =
+ " ab\n a2c\n a3\n b2c2\n b3c\n b4\n bc3e\n bc4\n ac3de\n";
+
+const char* gerdt93_gb_strat0_free5 = "\
+0 <0> ab-b2-4bc+ae\n\
+1 <1> a2c-6bc2+a2f\n\
+2 <2> a3+b2c-a2d\n\
+3 ac<0> b3c+2b2c2+16bc3-b2ce-4ac2e-10bc2e+ace2+b3f+8b2cf+16bc2f-b2ef-4acef-4bcef+ae2f\n\
+4 a<1> b2c2+2170bc3+3249bc2d+3249ac2e+3250b2cf\n\
+5 a2<0> b4-2386bc3-b3d-8b2cd-3247bc2d-2b3e-6b2ce-3195ac2e+58bc2e+b2de+4acde+4bcde+a2e2+2b2e2-ace2+8bce2-ade2-2ae3-13b3f-3357b2cf-208bc2f+4a2ef+13b2ef+52acef+52bcef-13ae2f\n\
+6 ab<1> bc4-563bc3d-2577bc2d2-1896ac3e-2681bc3e-2577ac2de-3362bc2de+3088ac2e2-1829bc3f-3362b2cdf-2179bc2df-3362b2cef+2353ac2ef+1659bc2ef-1133ace2f-785b3f2+2930b2cf2+2606bc2f2+785b2ef2+3140acef2+3140bcef2-785ae2f2\n\
+7 a2<1> bc3e-3140ac2de-1718bc2de+2814ac2e2-1718b2cef+947bc2ef-3051ace2f+3140a2ef2\n\
+8 a2c<1> ac3de+1155ac2d2e-2873bc2d2e+3790ac3e2-1570ac2de2+3362bc2de2-3088ac2e3+2814b2cdef+2370bc2def+3362b2ce2f-3790ac2e2f-1659bc2e2f-3599acde2f+1133ace3f-1896b3ef2-1898b2cef2-10bc2ef2-1155a2def2+2681a2e2f2+1896b2e2f2+ace2f2+bce2f2-1896ae3f2+a2ef3\n\
+";
+
+const char* gerdt93_syzygies_strat0_free5 = "\
+ 0: a2c a3 \n\
+ 1: ab2 a2b a3 \n\
+";
+
+const char* gerdt93_initial_strat0_free5 =
+ " ab\n a2c\n a3\n b2c2\n b3c\n b4\n bc3e\n bc4\n ac3de\n";
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/test/ideals.hpp b/src/test/ideals.hpp
new file mode 100755
index 0000000..6b6212f
--- /dev/null
+++ b/src/test/ideals.hpp
@@ -0,0 +1,55 @@
+#ifndef _test_ideals_h_
+#define _test_ideals_h_
+
+// small
+extern const char* idealSmall;
+extern const char* idealSmallBasis;
+extern const char* idealSmallSyzygies;
+extern const char* idealSmallInitial;
+
+// liu
+extern const char* liu_ideal;
+extern const char* liu_gb_strat0_free1;
+extern const char* liu_syzygies_strat0_free1;
+extern const char* liu_initial_strat0_free1;
+
+// weispfennig97
+extern const char* weispfennig97_ideal;
+
+extern const char* weispfennig97_gb_strat0_free4;
+extern const char* weispfennig97_syzygies_strat0_free4;
+extern const char* weispfennig97_initial_strat0_free4;
+
+extern const char* weispfennig97_gb_strat0_free5;
+extern const char* weispfennig97_syzygies_strat0_free5;
+extern const char* weispfennig97_initial_strat0_free5;
+
+// gerdt93
+extern const char* gerdt93_ideal;
+
+extern const char* gerdt93_gb_strat0_free1;
+extern const char* gerdt93_syzygies_strat0_free1;
+extern const char* gerdt93_initial_strat0_free1;
+
+extern const char* gerdt93_gb_strat0_free2;
+extern const char* gerdt93_syzygies_strat0_free2;
+extern const char* gerdt93_initial_strat0_free2;
+
+extern const char* gerdt93_gb_strat0_free3;
+extern const char* gerdt93_syzygies_strat0_free3;
+extern const char* gerdt93_initial_strat0_free3;
+
+extern const char* gerdt93_gb_strat0_free4;
+extern const char* gerdt93_syzygies_strat0_free4;
+extern const char* gerdt93_initial_strat0_free4;
+
+extern const char* gerdt93_gb_strat0_free5;
+extern const char* gerdt93_syzygies_strat0_free5;
+extern const char* gerdt93_initial_strat0_free5;
+
+#endif
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/test/poly-test.cpp b/src/test/poly-test.cpp
new file mode 100755
index 0000000..f21fb4a
--- /dev/null
+++ b/src/test/poly-test.cpp
@@ -0,0 +1,808 @@
+// Copyright 2011 Michael E. Stillman
+
+#include "mathicgb/stdinc.h"
+#include <cstdio>
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#include "mathicgb/Poly.hpp"
+#include "mathicgb/Ideal.hpp"
+#include "mathicgb/MonTableNaive.hpp"
+#include "mathicgb/MonTableKDTree.hpp"
+#include "mathicgb/MonTableDivList.hpp"
+#include "mathicgb/MTArray.hpp"
+#include "mathicgb/io-util.hpp"
+
+#include "mathicgb/MonomialHashTable.hpp"
+#include "mathicgb/PolyHashTable.hpp"
+#include "mathicgb/PolyHeap.hpp"
+#include "mathicgb/PolyGeoBucket.hpp"
+#include "mathicgb/GroebnerBasis.hpp"
+#include "mathicgb/SPairHandler.hpp"
+#include "mathicgb/SignatureGB.hpp"
+
+#include <gtest/gtest.h>
+
+std::string ideal1 =
+"32003 4 1 1 1 1 1 \
+2 \
+ab+3d2+2d \
+c3+a-b+1 \
+";
+
+std::string ideal2 = " \
+32003 4 \
+1 1 1 1 1 \
+7 \
+bc2-ad2 \
+abc-d3 \
+b3-acd \
+a2d2-cd3 \
+ac3d-ab2d2 \
+a2c2d-b2d3 \
+c3d3-b2d4 \
+";
+
+TEST(PolyRing, read) {
+ std::stringstream o;
+ std::string ringinfo = "32003 6\n1 1 1 1 1 1";
+ std::auto_ptr<PolyRing> R(ringFromString(ringinfo));
+ R->write(o);
+
+ EXPECT_EQ("32003 6 1\n 1 1 1 1 1 1\n", o.str());
+}
+
+TEST(Poly,readwrite) {
+ std::string f1 = "14ce2<72>+13adf<16>";
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ Poly f(R.get());
+ std::stringstream ifil(f1);
+ f.parse(ifil);
+ std::ostringstream o;
+ f.display(o,true);
+ EXPECT_EQ(o.str(), f1);
+}
+
+bool testPolyParse(PolyRing* R, std::string s)
+{
+ // parse poly, then see if it matches the orig string
+ Poly f(R);
+ std::istringstream i(s);
+ f.parse(i);
+ std::ostringstream o;
+ f.display(o);
+ // std::cout << "orig = " << s << std::endl;
+ // std::cout << "f = " << o.str() << std::endl;
+ return o.str() == s;
+}
+bool testPolyParse2(PolyRing* R, std::string s, std::string answer)
+{
+ // parse poly, then see if it matches the orig string
+ Poly f(R);
+ std::istringstream i(s);
+ f.parse(i);
+ std::ostringstream o;
+ f.display(o);
+ // std::cout << "orig = " << s << std::endl;
+ // std::cout << "f = " << o.str() << std::endl;
+ return o.str() == answer;
+}
+
+TEST(Poly,parse) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ EXPECT_TRUE(testPolyParse(R.get(), "3a<1>+<0>"));
+ EXPECT_TRUE(testPolyParse(R.get(), "3a<1>+13af3<0>+14cde<0>"));
+ EXPECT_TRUE(testPolyParse(R.get(), "<1>+13af3<0>+14cde<0>"));
+}
+
+bool testMonomialParse(PolyRing* R, std::string s)
+{
+ Monomial m = stringToMonomial(R, s);
+ std::string str2 = monomialToString(R,m);
+ return s == str2;
+}
+
+TEST(Monomial, parse) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ EXPECT_TRUE(testMonomialParse(R.get(), "ab2d<2>"));
+ EXPECT_TRUE(testMonomialParse(R.get(), "ab2d<0>"));
+ EXPECT_TRUE(testMonomialParse(R.get(), "<13>"));
+ EXPECT_TRUE(testMonomialParse(R.get(), "abcdef<0>"));
+ EXPECT_TRUE(testMonomialParse(R.get(), "a10b3d4<0>"));
+}
+
+TEST(Monomial,compare) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial mone = stringToMonomial(R.get(), "<0>");
+ Monomial mone2 = stringToMonomial(R.get(), "1");
+ Monomial m1 = stringToMonomial(R.get(), "ab2<0>");
+ Monomial m2 = stringToMonomial(R.get(), "a2b<0>");
+
+ EXPECT_TRUE(R->monomialEQ(mone, mone2));
+
+ // monomial mone = monomialFromString(R, "0 0");
+ // monomial m1 = monomialFromString(R, "0 2 1 2 0 1");
+ // monomial m2 = monomialFromString(R, "0 2 1 1 0 2");
+
+ bool a = R->monomialLT(m1,m2);
+ EXPECT_TRUE(a);
+ EXPECT_FALSE(R->monomialEQ(m1,m2));
+ EXPECT_EQ(LT, R->monomialCompare(m1,m2));
+
+ a = R->monomialLT(mone,m1);
+ EXPECT_TRUE(a);
+ EXPECT_FALSE(R->monomialEQ(mone,m1));
+ EXPECT_EQ(GT, R->monomialCompare(m1,mone));
+
+ a = R->monomialLT(mone,mone);
+ EXPECT_FALSE(a);
+ EXPECT_TRUE(R->monomialEQ(mone,mone));
+ EXPECT_EQ(EQ, R->monomialCompare(mone,mone));
+
+ Monomial b = stringToMonomial(R.get(), "b<0>");
+ Monomial c = stringToMonomial(R.get(), "c<0>");
+
+ a = R->monomialLT(b,c);
+ EXPECT_FALSE(a);
+}
+
+TEST(Monomial,mult) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "ab2<0>");
+ Monomial m2 = stringToMonomial(R.get(), "a2b<0>");
+ Monomial m3ans = stringToMonomial(R.get(), "a3b3<0>");
+
+ Monomial m3 = R->allocMonomial1();
+ R->monomialMult(m1,m2,m3);
+ EXPECT_TRUE(R->monomialEQ(m3ans,m3));
+
+ R->freeMonomial(m1);
+ R->freeMonomial(m2);
+ R->freeMonomial(m3);
+ R->freeMonomial(m3ans);
+}
+
+TEST(Monomial,multTo) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "ab2<0>");
+ Monomial m2 = stringToMonomial(R.get(), "a2b<0>");
+ Monomial m3ans = stringToMonomial(R.get(), "a3b3<0>");
+ R->monomialMultTo(m1,m2);
+ EXPECT_TRUE(R->monomialEQ(m3ans,m1));
+
+ R->freeMonomial(m1);
+ R->freeMonomial(m2);
+ R->freeMonomial(m3ans);
+}
+
+TEST(Monomial, divide) {
+ // test of monomialDivide, monomialIsDivisibleBy, monomialQuotientAndMult
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "ab2<0>");
+ Monomial m2 = stringToMonomial(R.get(), "a2b<0>");
+ Monomial m3ans = stringToMonomial(R.get(), "a3b3<0>");
+ Monomial m3 = R->allocMonomial1();
+ Monomial m1a = R->allocMonomial1();
+
+ R->monomialMult(m1,m2,m3);
+ EXPECT_TRUE(R->monomialIsDivisibleBy(m3,m2));
+ EXPECT_FALSE(R->monomialIsDivisibleBy(m2,m3));
+ R->monomialDivide(m3,m2,m1a);
+ EXPECT_TRUE(R->monomialEQ(m1,m1a));
+
+ R->freeMonomial(m1);
+ R->freeMonomial(m2);
+ R->freeMonomial(m3);
+ R->freeMonomial(m3ans);
+ R->freeMonomial(m1a);
+}
+
+TEST(Monomial, monomialQuotientAndMult) {
+ // test of monomialQuotientAndMult
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "ab2f2<0>");
+ Monomial m2 = stringToMonomial(R.get(), "af<0>");
+ Monomial m3 = stringToMonomial(R.get(), "<2>");
+
+ Monomial n = R->allocMonomial1();
+ Monomial n1 = R->allocMonomial1();
+ Monomial na = R->allocMonomial1();
+
+ R->monomialQuotientAndMult(m1,m2,m3,n);
+ R->monomialDivide(m1,m2,n1); // m1//m2
+ R->monomialMult(n1,m3,na); // m1//m2 * m3 should be n
+
+ EXPECT_TRUE(R->monomialEQ(n, na));
+
+ R->freeMonomial(m1);
+ R->freeMonomial(m2);
+ R->freeMonomial(m3);
+ R->freeMonomial(n);
+ R->freeMonomial(n1);
+ R->freeMonomial(na);
+}
+
+void testMonomialOps(const PolyRing* R, std::string s1, std::string s2)
+{
+ Monomial m1 = stringToMonomial(R, s1);
+ Monomial m2 = stringToMonomial(R, s2);
+ Monomial m3 = stringToMonomial(R, "abcdef<0>");
+
+ // m1 * m2 == lcm(m1,m2) * gcd(m1,m2)
+
+ Monomial m4 = R->allocMonomial1();
+ Monomial lcm = R->allocMonomial1();
+ Monomial gcd = R->allocMonomial1();
+ Monomial m7 = R->allocMonomial1();
+ Monomial m8 = R->allocMonomial1();
+ Monomial m1a = R->allocMonomial1();
+ Monomial m2a = R->allocMonomial1();
+ Monomial m1b = R->allocMonomial1();
+ Monomial m2b = R->allocMonomial1();
+
+ R->monomialMult(m1,m2,m4);
+ R->monomialLeastCommonMultiple(m1,m2,lcm);
+ R->monomialGreatestCommonDivisor(m1,m2,gcd);
+
+ R->monomialMult(lcm,gcd,m7);
+ EXPECT_TRUE(R->monomialEQ(m4, m7));
+
+ // lcm(m1,m2)/m1, lcm(m1,m2)/m2: relatively prime
+ EXPECT_TRUE(R->monomialDivide(lcm, m1, m1a));
+ EXPECT_TRUE(R->monomialDivide(lcm, m2, m2a));
+ EXPECT_TRUE(R->monomialRelativelyPrime(m1a,m2a));
+
+ EXPECT_TRUE(R->monomialDivide(lcm, m1a, m1b));
+ EXPECT_TRUE(R->monomialDivide(lcm, m2a, m2b));
+ EXPECT_TRUE(R->monomialEQ(m1, m1b));
+ EXPECT_TRUE(R->monomialEQ(m2, m2b));
+ EXPECT_TRUE(R->monomialDivide(lcm,gcd,m8));
+
+ size_t supp1 = R->monomialSizeOfSupport(m1a);
+ size_t supp2 = R->monomialSizeOfSupport(m2a);
+ size_t supp = R->monomialSizeOfSupport(m8);
+ EXPECT_EQ(supp1+supp2, supp)
+ << monomialToString(R,m1) << " " << monomialToString(R,m2) << "\n"
+ << monomialToString(R,m1a) << " " << monomialToString(R,m2a) << " "
+ << monomialToString(R,m8);
+}
+
+TEST(Monomial, ops)
+{
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ testMonomialOps(R.get(), "ab2f2<0>", "bc2df3<0>");
+ testMonomialOps(R.get(), "ab2f2<0>", "<0>");
+ testMonomialOps(R.get(), "<0>", "<0>");
+ testMonomialOps(R.get(), "a<0>", "a<0>");
+ testMonomialOps(R.get(), "a<0>", "b<0>");
+ testMonomialOps(R.get(), "a1000b1000c1000d1000e1000f1000<0>", "b2f5<0>");
+}
+
+TEST(Monomial, ei)
+{
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "<1>");
+ Monomial m1a = R->allocMonomial1();
+ R->monomialEi(1, m1a);
+ EXPECT_TRUE(R->monomialEQ(m1,m1a));
+
+
+ m1 = stringToMonomial(R.get(), "<0>");
+ R->monomialEi(0, m1a);
+ EXPECT_TRUE(R->monomialEQ(m1,m1a));
+
+ m1 = stringToMonomial(R.get(), "<1>");
+ R->monomialEi(1, m1a);
+ EXPECT_TRUE(R->monomialEQ(m1,m1a));
+
+ m1 = stringToMonomial(R.get(), "<10000>");
+ R->monomialEi(10000, m1a);
+ EXPECT_TRUE(R->monomialEQ(m1,m1a));
+}
+
+TEST(Monomial, strict)
+{
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "ab2c3d4e");
+ Monomial m2 = stringToMonomial(R.get(), "ab2c3d4");
+ Monomial m3 = stringToMonomial(R.get(), "ab2c3d4");
+ Monomial m4 = stringToMonomial(R.get(), "ab2c3d3e");
+
+ EXPECT_TRUE(R->monomialHasStrictlyLargerExponent(m1,m2,m3));
+ EXPECT_FALSE(R->monomialHasStrictlyLargerExponent(m1,m2,m4));
+}
+
+TEST(Monomial, divideToNegative)
+{
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial m1 = stringToMonomial(R.get(), "ab100<0>");
+ Monomial m2 = stringToMonomial(R.get(), "ab2c3d4<0>");
+ Monomial m3 = R->allocMonomial1();
+ Monomial m4 = R->allocMonomial1();
+ Monomial m5 = R->allocMonomial1();
+ Monomial mone = stringToMonomial(R.get(), "<0>");
+
+ R->monomialDivideToNegative(m1,m2,m3);
+ R->monomialDivideToNegative(m2,m1,m4);
+ R->monomialMult(m3,m4,m5);
+
+ EXPECT_TRUE(R->monomialEQ(m5,mone));
+
+ m3 = stringToMonomial(R.get(), "ab2c3d4");
+ m4 = stringToMonomial(R.get(), "ab2c3d3e");
+
+ EXPECT_TRUE(R->monomialHasStrictlyLargerExponent(m1,m2,m3));
+ EXPECT_FALSE(R->monomialHasStrictlyLargerExponent(m2,m2,m4));
+}
+
+TEST(Monomial, findSignature)
+{
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+
+ Monomial v1 = stringToMonomial(R.get(), "abef");
+ Monomial v2 = stringToMonomial(R.get(), "acdf2");
+ Monomial u1 = stringToMonomial(R.get(), "f5<13>");
+ Monomial t1 = R->allocMonomial1();
+ Monomial t1ans = stringToMonomial(R.get(), "cdf6<13>");
+
+ R->monomialFindSignature(v1,v2,u1,t1);
+ EXPECT_TRUE(R->monomialEQ(t1,t1ans));
+}
+
+//#warning "remove this code"
+#if 0
+bool testMonomialOldParse(PolyRing *R, std::string s)
+{
+ monomial m = monomialParseFromString(R, s);
+ std::string str2 = monomialDisplay(R,m);
+ return s == str2;
+}
+
+TEST(OldMonomial, parse) {
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ EXPECT_TRUE(testMonomialOldParse(R, "ab2d<2>"));
+ EXPECT_TRUE(testMonomialOldParse(R, "ab2d<0>"));
+ EXPECT_TRUE(testMonomialOldParse(R, "<13>"));
+ EXPECT_TRUE(testMonomialOldParse(R, "abcdef<0>"));
+ EXPECT_TRUE(testMonomialOldParse(R, "a10b3d4<0>"));
+}
+
+TEST(OldMonomial,compare) {
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ monomial mone = monomialFromString(R, "0 0");
+ monomial m1 = monomialFromString(R, "0 2 1 2 0 1");
+ monomial m2 = monomialFromString(R, "0 2 1 1 0 2");
+
+ bool a = R->monomialLT(m1,m2);
+ EXPECT_TRUE(a);
+ EXPECT_FALSE(R->monomialEQ(m1,m2));
+ EXPECT_EQ(LT, R->monomialCompare(m1,m2));
+
+ a = R->monomialLT(mone,m1);
+ EXPECT_TRUE(a);
+ EXPECT_FALSE(R->monomialEQ(mone,m1));
+ EXPECT_EQ(GT, R->monomialCompare(m1,mone));
+
+ a = R->monomialLT(mone,mone);
+ EXPECT_FALSE(a);
+ EXPECT_TRUE(R->monomialEQ(mone,mone));
+ EXPECT_EQ(EQ, R->monomialCompare(mone,mone));
+
+ monomial b = monomialFromString(R, "0 1 1 1");
+ monomial c = monomialFromString(R, "0 1 2 1");
+ a = R->monomialLT(b,c);
+ EXPECT_FALSE(a);
+}
+
+TEST(OldMonomial,mult) {
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ monomial m1 = monomialFromString(R, "0 2 1 2 0 1"); // ab2
+ monomial m2 = monomialFromString(R, "0 2 1 1 0 2"); // a2b
+ // std::cout << "m1 is " << monomialToString(R,m1) << std::endl;
+ //std::cout << "m2 is " << monomialToString(R,m2) << std::endl;
+ monomial m3 = new int[R->maxMonomialSize()];
+ R->monomialMult(m1,m2,m3);
+ monomial m3ans = monomialFromString(R, "0 2 1 3 0 3"); // a3b3
+ //std::cout << "answer is " << monomialToString(R,m3) << std::endl;
+ EXPECT_TRUE(R->monomialEQ(m3ans,m3));
+}
+
+TEST(OldMonomial,multTo) {
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ monomial m1 = monomialFromString(R, "0 3 5 2 1 2 0 1"); // ab2
+ monomial m2 = monomialFromString(R, "0 2 1 1 0 2"); // a2b
+ R->monomialMultTo(m1,m2);
+ monomial m3ans = monomialFromString(R, "0 3 5 2 1 3 0 3"); // a3b3
+ EXPECT_TRUE(R->monomialEQ(m3ans,m1));
+}
+
+TEST(OldMonomial, divide) {
+ // test of monomialDivide, monomialIsDivisibleBy, monomialQuotientAndMult
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ monomial m1 = monomialFromString(R, "0 3 5 2 1 2 0 1"); // ab2
+ monomial m2 = monomialFromString(R, "0 2 1 1 0 2"); // a2b
+ monomial m3 = new int[R->maxMonomialSize()];
+ monomial m1a = new int[R->maxMonomialSize()];
+ R->monomialMult(m1,m2,m3);
+ EXPECT_TRUE(R->monomialIsDivisibleBy(m3,m2));
+ EXPECT_FALSE(R->monomialIsDivisibleBy(m2,m3));
+ R->monomialDivide(m3,m2,m1a);
+ EXPECT_TRUE(R->monomialEQ(m1,m1a));
+}
+
+TEST(OldMonomial, monomialQuotientAndMult) {
+ // test of monomialQuotientAndMult
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ monomial m1 = monomialFromString(R, "0 3 5 2 1 2 0 1"); // ab2f2
+ monomial m2 = monomialFromString(R, "0 2 5 1 0 1"); // af
+ monomial m3 = monomialFromString(R, "2 0"); // e_2
+ monomial n = new int[R->maxMonomialSize()];
+ monomial n1 = new int[R->maxMonomialSize()];
+ monomial na = new int[R->maxMonomialSize()];
+ R->monomialQuotientAndMult(m1,m2,m3,n);
+ R->monomialDivide(m1,m2,n1); // m1//m2
+ R->monomialMult(n1,m3,na); // m1//m2 * m3 should be n
+ // std::cout << "n is " << monomialToString(R,n) << std::endl;
+ // std::cout << "na is " << monomialToString(R,na) << std::endl;
+
+ EXPECT_TRUE(R->monomialEQ(n, na));
+}
+#endif
+
+
+TEST(Coeff, reciprocal) {
+ std::auto_ptr<PolyRing> R(ringFromString("11 6 1\n1 1 1 1 1 1"));
+ coefficient vals[10] = {1,2,3,4,5,6,7,8,9,10};
+ coefficient ans[10] = {1,6,4,3,9,2,8,7,5,10};
+ for (int i=0; i<10; i++)
+ {
+ coefficient a = vals[i];
+ R->coefficientReciprocalTo(a);
+ EXPECT_EQ(ans[i], a);
+ }
+}
+
+TEST(Coeff, reciprocal2) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ for (int i=1; i<32002; i++)
+ {
+ coefficient a1;
+ coefficient a = i;
+ R->coefficientReciprocalTo(a);
+ R->coefficientDivide(1,i,a1);
+ EXPECT_EQ(a,a1);
+ R->coefficientMultTo(a,i);
+ EXPECT_TRUE(a > 0);
+ EXPECT_TRUE(a < 32003);
+ EXPECT_TRUE(a == 1);
+ }
+}
+
+TEST(Coeff, negate) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ for (int i=1; i<32002; i++)
+ {
+ coefficient a1 = i;
+ coefficient a = i;
+ R->coefficientNegateTo(a);
+ EXPECT_TRUE(a > 0);
+ EXPECT_TRUE(a < 32003);
+ R->coefficientAddTo(a1,a);
+ EXPECT_EQ(0,a1);
+ }
+}
+
+TEST(Coeff, addone) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ for (int i=0; i<32002; i++)
+ {
+ coefficient a1 = i;
+ coefficient a = i;
+ R->coefficientAddOneTo(a);
+ EXPECT_TRUE(a > 0);
+ EXPECT_TRUE(a < 32003);
+ R->coefficientAddTo(a1,1);
+ EXPECT_EQ(a,a1);
+ }
+}
+
+TEST(MTArray,Naive1) {
+ // We create a table here
+ size_t not_used = 0;
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ std::auto_ptr<MonomialTableArray> M(MonomialTableArray::make(R.get(), 1, 6, false));
+ std::string mons[2] = {
+ "abc<1>",
+ "a2d<1>"
+ };
+ for (int i=0; i<2; i++)
+ {
+ monomial m = monomialParseFromString(R.get(), mons[i]);
+ M->insert(m,0);
+ }
+ // M.display(std::cout);
+
+ // Now we test membership
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "abc4d<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "a2d2<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "abc<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "a2d<1>"),not_used));
+ EXPECT_FALSE(M->member(monomialParseFromString(R.get(), "a2d<2>"),not_used));
+ EXPECT_FALSE(M->member(monomialParseFromString(R.get(), "ad<1>"),not_used));
+}
+
+
+TEST(MTArray,DivList1) {
+ // We create a table here
+ size_t not_used = 0;
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ MonomialTableArray* M(MonomialTableArray::make(R.get(), 1, 6, false));
+ std::string mons[2] = {
+ "abc<1>",
+ "a2d<1>"
+ };
+ for (int i=0; i<2; i++)
+ {
+ monomial m = monomialParseFromString(R.get(), mons[i]);
+ M->insert(m,0);
+ }
+ // M.display(std::cout);
+
+ // Now we test membership
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "abc4d<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "a2d2<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "abc<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "a2d<1>"),not_used));
+ EXPECT_FALSE(M->member(monomialParseFromString(R.get(), "a2d<2>"),not_used));
+ EXPECT_FALSE(M->member(monomialParseFromString(R.get(), "ad<1>"),not_used));
+}
+
+TEST(MTArray,KDTree1) {
+ // We create a table here
+ size_t not_used = 0;
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ std::auto_ptr<MonomialTableArray> M(MonomialTableArray::make(R.get(), 2, 6, false));
+ std::string mons[2] = {
+ "abc<1>",
+ "a2d<1>"
+ };
+ for (int i=0; i<2; i++)
+ {
+ monomial m = monomialParseFromString(R.get(), mons[i]);
+ M->insert(m,0);
+ }
+ // M.display(std::cout);
+
+ // Now we test membership
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "abc4d<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "a2d2<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "abc<1>"),not_used));
+ EXPECT_TRUE(M->member(monomialParseFromString(R.get(), "a2d<1>"),not_used));
+ EXPECT_FALSE(M->member(monomialParseFromString(R.get(), "a2d<2>"),not_used));
+ EXPECT_FALSE(M->member(monomialParseFromString(R.get(), "ad<1>"),not_used));
+}
+
+//#warning "remove this code"
+#if 0
+bool test_find_signatures(const PolyRing *R,
+ const_monomial u1,
+ const_monomial u2,
+ const_monomial v1,
+ const_monomial v2)
+{
+ monomial g = new int[R->maxMonomialSize()];
+ monomial t1 = new int[R->maxMonomialSize()];
+ monomial t2 = new int[R->maxMonomialSize()];
+ monomial x1 = new int[R->maxMonomialSize()];
+ monomial x2 = new int[R->maxMonomialSize()];
+ monomial y1 = new int[R->maxMonomialSize()];
+ monomial y2 = new int[R->maxMonomialSize()];
+ monomial v1v2 = new int[R->maxMonomialSize()];
+ monomial x1g = new int[R->maxMonomialSize()];
+ R->monomialFindSignature(v1,v2,u1,t1);
+ R->monomialFindSignature(v2,v1,u2,t2);
+ R->monomialGreatestCommonDivisor(v1, v2, g);
+ // check that v1*t1 == v2*t2
+ // v1*v2 == g * (v1*t1)
+ R->monomialDivide(t1,u1,y1);
+ R->monomialDivide(t2,u2,y2); // remove mult by signatures
+ R->monomialMult(v1,y1,x1);
+ R->monomialMult(v2,y2,x2);
+ R->monomialMult(v1,v2,v1v2);
+ R->monomialMult(x1,g,x1g);
+#if 0
+ std::cout << "v1 is " << monomialToString(R,v1) << std::endl;
+ std::cout << "v2 is " << monomialToString(R,v2) << std::endl;
+ std::cout << "u1 is " << monomialToString(R,u1) << std::endl;
+ std::cout << "u2 is " << monomialToString(R,u2) << std::endl;
+ std::cout << "t1 is " << monomialToString(R,t1) << std::endl;
+ std::cout << "22 is " << monomialToString(R,t2) << std::endl;
+ std::cout << "x1 is " << monomialToString(R,x1) << std::endl;
+ std::cout << "x2 is " << monomialToString(R,x2) << std::endl;
+ std::cout << "y1 is " << monomialToString(R,y1) << std::endl;
+ std::cout << "y2 is " << monomialToString(R,y2) << std::endl;
+ std::cout << "g is " << monomialToString(R,g) << std::endl;
+ std::cout << "v1v2 is " << monomialToString(R,v1v2) << std::endl;
+ std::cout << "x1g is " << monomialToString(R,x1g) << std::endl;
+#endif
+ if (!R->monomialEQ(x1,x2)) return false;
+ if (!R->monomialEQ(v1v2,x1g)) return false;
+
+ return true;
+}
+
+TEST(OldMonomial, findSignatures) {
+ PolyRing *R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ monomial v1 = monomialFromString(R, "0 3 5 2 1 2 0 1"); // ab2f2
+ monomial v2 = monomialFromString(R, "0 3 2 3 1 1 0 1"); // abc3
+ monomial u1 = monomialFromString(R, "2 3 4 1 1 2 0 1"); //
+ monomial u2 = monomialFromString(R, "3 2 1 1 0 2"); //
+ EXPECT_TRUE(test_find_signatures(R,u1,u2,v1,v2));
+}
+#endif
+
+TEST(Ideal,readwrite) {
+ // This also tests Poly::iterator
+ std::auto_ptr<Ideal> I = idealParseFromString(ideal1);
+ size_t ngens = I->viewGenerators().size();
+ EXPECT_TRUE(2 == ngens);
+
+ // now read and write each generator
+ for (size_t i=0; i<ngens; i++)
+ {
+ const Poly *f = I->getPoly(i);
+ std::ostringstream o;
+ f->display(o,false);
+ Poly g(f->getRing());
+ std::stringstream ifil(o.str());
+ g.parse(ifil);
+ EXPECT_TRUE(g == *f);
+ }
+}
+
+TEST(Poly,lead) {
+ // This also tests Poly::iterator, Poly::read, Poly::write
+ std::auto_ptr<Ideal> I = idealParseFromString(ideal1);
+ std::auto_ptr<const PolyRing> R(I->getPolyRing());
+ monomial lm = stringToMonomial(R.get(), "ab");
+ EXPECT_TRUE(R->monomialEQ(lm, I->getPoly(0)->getLeadMonomial()));
+ EXPECT_EQ(1, I->getPoly(0)->getLeadCoefficient());
+ EXPECT_EQ(0, I->getPoly(0)->getLeadComponent());
+ R->freeMonomial(lm);
+}
+
+//////////////////////////////
+// Test reducer code /////////
+//////////////////////////////
+
+void testPolyReducer(
+ Reducer::ReducerType reducerType,
+ const Ideal& ideal,
+ const std::string& f,
+ const std::string& ans
+) {
+ const PolyRing& ring = *ideal.getPolyRing();
+ std::auto_ptr<Poly> g = polyParseFromString(&ring, f);
+ std::auto_ptr<Poly> h = multIdealByPolyReducer(reducerType, ideal, *g);
+ if (!h->isZero()) {
+ Poly::iterator prev = h->begin();
+ Poly::iterator it = prev;
+ for (++it; it != h->end(); ++it, ++prev) {
+ EXPECT_TRUE(ring.monomialLT(it.getMonomial(), prev.getMonomial()))
+ << "Reduced result not in sorted order: " << toString(h.get());
+ }
+ }
+
+ EXPECT_EQ(ans, toString(h.get())) << "Reducer type " << reducerType;
+}
+
+TEST(Reducer, insert) {
+ // plan: read an ideal, and another poly
+ // use this last poly to determine what to add to the heap
+ // at the end, take the value of the heap, compare to answer
+
+ std::auto_ptr<Ideal> I = idealParseFromString(ideal2); // charac is 32003
+ for (int typ = 0; typ <= 30; ++typ) {
+ Reducer::ReducerType red = Reducer::ReducerType(typ);
+ if (static_cast<int>(red) != typ ||
+ Reducer::makeReducerNullOnUnknown(red, I->ring()).get() == 0)
+ continue;
+
+ testPolyReducer(red, *I, "c2d<0>", "bc4d<0>-ac2d3<0>");
+ testPolyReducer(red, *I, "c2d<0>-3abc<1>", "-3a2b2c2<0>+bc4d<0>+3abcd3<0>-ac2d3<0>");
+ testPolyReducer(red, *I, "c2d<0>-3abc<1>+a2<3>", "-3a2b2c2<0>+bc4d<0>+a4d2<0>-a2cd3<0>+3abcd3<0>-ac2d3<0>");
+ testPolyReducer(red, *I, "c2d<0>-3abc<1>+a2<3>+3ab<4>", "3a2bc3d<0>-3a2b3d2<0>-3a2b2c2<0>+bc4d<0>+a4d2<0>-a2cd3<0>+3abcd3<0>-ac2d3<0>");
+ testPolyReducer(red, *I, "bc4d<0>+3bc4d<0>-2bc4d<0>", "2b2c6d<0>-2abc4d3<0>");
+ testPolyReducer(red, *I, "a<0>+32002c<1>+<3>", "0");
+ }
+}
+
+std::string somePolys =
+" bc+bd+af+ef\n\
+ ac+cd+bf+ef\n\
+ ad+bd+cf+ef\n\
+ ab+ac+df+ef\n\
+ bd2+cd2+c2f+adf+bdf+cef\n\
+ b2d+acd+bcf+d2f+bef+def\n\
+ c2d+bd2+b2f+bcf+adf+cdf+bef+def\n\
+ a2f+b2f+c2f+d2f+aef+bef+cef+def\n\
+ cd2f+d3f+adef+bdef+cdef+d2ef+b2f2+acf2+c2f2+bdf2+cdf2+aef2+cef2+def2\n\
+ c3f+b2df+acdf+cd2f+c2ef+bdef\n\
+ b3f+b2cf+bc2f+b2df+acdf+bcdf+b2ef+bcef+bdef+cdef\n\
+ b2ef2+bcef2+adef2+d2ef2+be2f2+de2f2+abf3+b2f3+bcf3+adf3+cdf3+d2f3\n\
+ bde2f2+cde2f2+abdf3+b2df3+bcdf3+ad2f3+cd2f3+d3f3+b2ef3+acef3+bcef3+c2ef3+adef3+d2ef3+ae2f3+be2f3+de2f3+e3f3\n\
+ c2e2f3+ade2f3+bde2f3+d2e2f3+ce3f3+de3f3+b2df4+acdf4+bcdf4+c2df4+ad2f4+bd2f4+b2ef4+c2ef4+de2f4+e3f4\n\
+ cde2f4+d2e2f4+ae3f4+be3f4+ce3f4+e4f4+bc2f5+b2df5+c2df5+ad2f5+cd2f5+d3f5+acef5+adef5+bdef5+d2ef5+ce2f5+e3f5\n\
+ d2e3f4+de4f4+bc2df5+abd2f5+bcd2f5+c2d2f5+bc2ef5+abdef5+bd2ef5+d3ef5+b2e2f5+ace2f5+bce2f5+cde2f5+ae3f5+de3f5\n\
+";
+
+TEST(PolyHashTable,test1) {
+ std::auto_ptr<PolyRing> R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ PolyHashTable H(R.get(),3);
+ std::auto_ptr<Poly> f1 = polyParseFromString(R.get(), "3bd2+7cd2+5c2f+2adf+bdf+10cef");
+ PolyHashTable::MonomialArray M1, M2;
+ H.fromPoly(*f1, M1);
+ H.fromPoly(*f1, M2);
+ EXPECT_TRUE(M2.empty());
+ Poly g(R.get());
+ H.toPoly(M1,g);
+ // f1->display(std::cout);
+ // std::cout << std::endl;
+ // g.display(std::cout);
+ // std::cout << std::endl;
+ f1->multByCoefficient(2);
+ EXPECT_TRUE(g == *f1);
+ // H.dump();
+ H.resize(6);
+ // H.dump();
+ M1.clear();
+ H.fromPoly(*f1, M1);
+ Poly g2(R.get());
+ H.toPoly(M1,g2);
+ EXPECT_TRUE(g == g2);
+}
+
+TEST(PolyHashTable,test2) {
+ std::auto_ptr<PolyRing> R(ringFromString("32003 6 1\n1 1 1 1 1 1"));
+ PolyHashTable H(R.get(), 3);
+ std::auto_ptr<Poly> f1(polyParseFromString(R.get(), "3bd2+7cd2+5c2f+2adf+bdf+10cef"));
+ std::auto_ptr<Poly> f2(polyParseFromString(R.get(), "-3bd2+4c2f+cef+f3"));
+ PolyHashTable::MonomialArray M1, M2;
+ H.fromPoly(*f1, M1);
+ H.fromPoly(*f2, M2);
+ // H.dump(1);
+}
+
+TEST(MonomialHashTable,test1) {
+ std::auto_ptr<PolyRing> R = ringFromString("32003 6 1\n1 1 1 1 1 1");
+ MonomialHashTable H(R.get(), 3);
+ std::auto_ptr<Poly> f1 = polyParseFromString(R.get(), "3bd2+7cd2+5c2f+2adf+bdf+10cef");
+ int count = 0;
+ int was_there_count = 0;
+ for (int j = 0; j<10; j++)
+ for (Poly::iterator i = f1->begin(); i != f1->end(); ++i)
+ {
+ bool was_there = H.lookupAndInsert(i.getMonomial(), count);
+ count++;
+ if (was_there) was_there_count++;
+ }
+ MonomialHashTable::Stats stats;
+ H.getStats(stats);
+ //H.dump(1);
+}
+
+// Local Variables:
+// compile-command: "make -C .. "
+// indent-tabs-mode: nil
+// End:
diff --git a/src/test/testMain.cpp b/src/test/testMain.cpp
new file mode 100755
index 0000000..4d820af
--- /dev/null
+++ b/src/test/testMain.cpp
@@ -0,0 +1,6 @@
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mathicgb.git
More information about the debian-science-commits
mailing list