[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> &degs,
+  //       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